aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2008-01-19 00:19:29 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2008-01-19 00:19:29 +0000
commitf8247040e6231c4b3b5099ea3a526348b7941566 (patch)
tree0cc92ad6ebf6ae49a62f6e7ef8ec819121d63630
parentd88e56c61ce2042544c1a8a71c93b69ab2e6ffba (diff)
Creating tag for the release of asterisk-1.6.0-beta1v1.6.0-beta1
git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.6.0-beta1@99163 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--trunk/.cleancount1
-rw-r--r--trunk/BUGS22
-rw-r--r--trunk/CHANGES496
-rw-r--r--trunk/COPYING341
-rw-r--r--trunk/CREDITS244
-rw-r--r--trunk/LICENSE69
-rw-r--r--trunk/Makefile834
-rw-r--r--trunk/Makefile.moddir_rules166
-rw-r--r--trunk/Makefile.rules98
-rw-r--r--trunk/README262
-rw-r--r--trunk/UPGRADE.txt163
-rw-r--r--trunk/acinclude.m41106
-rw-r--r--trunk/agi/DialAnMp3.agi82
-rw-r--r--trunk/agi/Makefile52
-rw-r--r--trunk/agi/agi-test.agi79
-rw-r--r--trunk/agi/eagi-sphinx-test.c231
-rw-r--r--trunk/agi/eagi-test.c165
-rw-r--r--trunk/agi/fastagi-test94
-rwxr-xr-xtrunk/agi/jukebox.agi488
-rw-r--r--trunk/agi/numeralize44
-rw-r--r--trunk/apps/Makefile41
-rw-r--r--trunk/apps/app_adsiprog.c1581
-rw-r--r--trunk/apps/app_alarmreceiver.c813
-rw-r--r--trunk/apps/app_amd.c418
-rw-r--r--trunk/apps/app_authenticate.c212
-rw-r--r--trunk/apps/app_cdr.c63
-rw-r--r--trunk/apps/app_chanisavail.c157
-rw-r--r--trunk/apps/app_channelredirect.c93
-rw-r--r--trunk/apps/app_chanspy.c730
-rw-r--r--trunk/apps/app_controlplayback.c168
-rw-r--r--trunk/apps/app_db.c139
-rw-r--r--trunk/apps/app_dial.c2047
-rw-r--r--trunk/apps/app_dictate.c338
-rw-r--r--trunk/apps/app_directed_pickup.c172
-rw-r--r--trunk/apps/app_directory.c842
-rw-r--r--trunk/apps/app_disa.c367
-rw-r--r--trunk/apps/app_dumpchan.c160
-rw-r--r--trunk/apps/app_echo.c86
-rw-r--r--trunk/apps/app_exec.c221
-rw-r--r--trunk/apps/app_externalivr.c575
-rw-r--r--trunk/apps/app_festival.c523
-rw-r--r--trunk/apps/app_flash.c112
-rw-r--r--trunk/apps/app_followme.c1059
-rw-r--r--trunk/apps/app_forkcdr.c99
-rw-r--r--trunk/apps/app_getcpeid.c130
-rw-r--r--trunk/apps/app_ices.c205
-rw-r--r--trunk/apps/app_image.c80
-rw-r--r--trunk/apps/app_ivrdemo.c115
-rw-r--r--trunk/apps/app_jack.c971
-rw-r--r--trunk/apps/app_macro.c527
-rw-r--r--trunk/apps/app_meetme.c5592
-rw-r--r--trunk/apps/app_milliwatt.c134
-rw-r--r--trunk/apps/app_minivm.c3109
-rw-r--r--trunk/apps/app_mixmonitor.c424
-rw-r--r--trunk/apps/app_morsecode.c161
-rw-r--r--trunk/apps/app_mp3.c235
-rw-r--r--trunk/apps/app_nbscat.c218
-rw-r--r--trunk/apps/app_osplookup.c2041
-rw-r--r--trunk/apps/app_page.c193
-rw-r--r--trunk/apps/app_parkandannounce.c183
-rw-r--r--trunk/apps/app_pickupchan.c174
-rw-r--r--trunk/apps/app_playback.c524
-rw-r--r--trunk/apps/app_privacy.c173
-rw-r--r--trunk/apps/app_queue.c6153
-rw-r--r--trunk/apps/app_read.c231
-rw-r--r--trunk/apps/app_readexten.c258
-rw-r--r--trunk/apps/app_readfile.c107
-rw-r--r--trunk/apps/app_record.c365
-rw-r--r--trunk/apps/app_rpt.c7482
-rw-r--r--trunk/apps/app_sayunixtime.c112
-rw-r--r--trunk/apps/app_senddtmf.c127
-rw-r--r--trunk/apps/app_sendtext.c100
-rw-r--r--trunk/apps/app_setcallerid.c93
-rw-r--r--trunk/apps/app_skel.c125
-rw-r--r--trunk/apps/app_sms.c1932
-rw-r--r--trunk/apps/app_softhangup.c120
-rw-r--r--trunk/apps/app_speech_utils.c796
-rw-r--r--trunk/apps/app_stack.c416
-rw-r--r--trunk/apps/app_system.c129
-rw-r--r--trunk/apps/app_talkdetect.c211
-rw-r--r--trunk/apps/app_test.c467
-rw-r--r--trunk/apps/app_transfer.c125
-rw-r--r--trunk/apps/app_url.c149
-rw-r--r--trunk/apps/app_userevent.c89
-rw-r--r--trunk/apps/app_verbose.c158
-rw-r--r--trunk/apps/app_voicemail.c9831
-rw-r--r--trunk/apps/app_waitforring.c117
-rw-r--r--trunk/apps/app_waitforsilence.c189
-rw-r--r--trunk/apps/app_waituntil.c93
-rw-r--r--trunk/apps/app_while.c307
-rw-r--r--trunk/apps/app_zapateller.c113
-rw-r--r--trunk/apps/app_zapbarge.c299
-rw-r--r--trunk/apps/app_zapras.c238
-rw-r--r--trunk/apps/app_zapscan.c363
-rw-r--r--trunk/apps/enter.h287
-rw-r--r--trunk/apps/leave.h207
-rw-r--r--trunk/apps/rpt_flow.pdfbin0 -> 51935 bytes
-rwxr-xr-xtrunk/bootstrap.sh48
-rw-r--r--trunk/build_tools/cflags.xml62
-rw-r--r--trunk/build_tools/embed_modules.xml26
-rw-r--r--trunk/build_tools/get_makeopts3
-rw-r--r--trunk/build_tools/get_moduleinfo3
-rwxr-xr-xtrunk/build_tools/make_build_h20
-rwxr-xr-xtrunk/build_tools/make_buildopts_h37
-rwxr-xr-xtrunk/build_tools/make_defaults_h28
-rwxr-xr-xtrunk/build_tools/make_linker_eo_script27
-rwxr-xr-xtrunk/build_tools/make_sample_voicemail25
-rwxr-xr-xtrunk/build_tools/make_version66
-rwxr-xr-xtrunk/build_tools/make_version_c33
-rw-r--r--trunk/build_tools/menuselect-deps.in47
-rwxr-xr-xtrunk/build_tools/mkpkgconfig50
-rwxr-xr-xtrunk/build_tools/prep_tarball13
-rwxr-xr-xtrunk/build_tools/strip_nonapi29
-rw-r--r--trunk/cdr/Makefile20
-rw-r--r--trunk/cdr/cdr_adaptive_odbc.c674
-rw-r--r--trunk/cdr/cdr_csv.c346
-rw-r--r--trunk/cdr/cdr_custom.c169
-rw-r--r--trunk/cdr/cdr_manager.c204
-rw-r--r--trunk/cdr/cdr_odbc.c264
-rw-r--r--trunk/cdr/cdr_pgsql.c335
-rw-r--r--trunk/cdr/cdr_radius.c260
-rw-r--r--trunk/cdr/cdr_sqlite.c214
-rw-r--r--trunk/cdr/cdr_sqlite3_custom.c357
-rw-r--r--trunk/cdr/cdr_tds.c531
-rw-r--r--trunk/channels/DialTone.h257
-rw-r--r--trunk/channels/Makefile105
-rw-r--r--trunk/channels/chan_agent.c2399
-rw-r--r--trunk/channels/chan_alsa.c925
-rw-r--r--trunk/channels/chan_console.c1103
-rw-r--r--trunk/channels/chan_features.c570
-rw-r--r--trunk/channels/chan_gtalk.c1956
-rw-r--r--trunk/channels/chan_h323.c3353
-rw-r--r--trunk/channels/chan_iax2.c11726
-rw-r--r--trunk/channels/chan_jingle.c1862
-rw-r--r--trunk/channels/chan_local.c761
-rw-r--r--trunk/channels/chan_mgcp.c4398
-rw-r--r--trunk/channels/chan_misdn.c5747
-rw-r--r--trunk/channels/chan_nbs.c292
-rw-r--r--trunk/channels/chan_oss.c1470
-rw-r--r--trunk/channels/chan_phone.c1450
-rw-r--r--trunk/channels/chan_sip.c21227
-rw-r--r--trunk/channels/chan_skinny.c6066
-rw-r--r--trunk/channels/chan_unistim.c5668
-rw-r--r--trunk/channels/chan_usbradio.c2494
-rw-r--r--trunk/channels/chan_vpb.cc2899
-rw-r--r--trunk/channels/chan_zap.c14253
-rw-r--r--trunk/channels/console_board.c329
-rw-r--r--trunk/channels/console_gui.c1036
-rw-r--r--trunk/channels/console_video.c1035
-rw-r--r--trunk/channels/console_video.h127
-rw-r--r--trunk/channels/h323/ChangeLog43
-rw-r--r--trunk/channels/h323/INSTALL.openh32318
-rw-r--r--trunk/channels/h323/Makefile.in48
-rw-r--r--trunk/channels/h323/README144
-rw-r--r--trunk/channels/h323/TODO9
-rw-r--r--trunk/channels/h323/ast_h323.cxx2637
-rw-r--r--trunk/channels/h323/ast_h323.h189
-rw-r--r--trunk/channels/h323/caps_h323.cxx383
-rw-r--r--trunk/channels/h323/caps_h323.h172
-rw-r--r--trunk/channels/h323/chan_h323.h269
-rw-r--r--trunk/channels/h323/cisco-h225.asn74
-rw-r--r--trunk/channels/h323/cisco-h225.cxx853
-rw-r--r--trunk/channels/h323/cisco-h225.h299
-rw-r--r--trunk/channels/h323/compat_h323.cxx138
-rw-r--r--trunk/channels/h323/compat_h323.h94
-rw-r--r--trunk/channels/h323/noexport.map5
-rw-r--r--trunk/channels/iax2-parser.c1080
-rw-r--r--trunk/channels/iax2-parser.h163
-rw-r--r--trunk/channels/iax2-provision.c538
-rw-r--r--trunk/channels/iax2-provision.h53
-rw-r--r--trunk/channels/iax2.h277
-rw-r--r--trunk/channels/misdn/Makefile17
-rw-r--r--trunk/channels/misdn/chan_misdn_config.h160
-rw-r--r--trunk/channels/misdn/ie.c1422
-rw-r--r--trunk/channels/misdn/isdn_lib.c4582
-rw-r--r--trunk/channels/misdn/isdn_lib.h488
-rw-r--r--trunk/channels/misdn/isdn_lib_intern.h124
-rw-r--r--trunk/channels/misdn/isdn_msg_parser.c1353
-rw-r--r--trunk/channels/misdn/portinfo.c202
-rw-r--r--trunk/channels/misdn_config.c1158
-rw-r--r--trunk/channels/vcodecs.c1253
-rw-r--r--trunk/channels/vgrabbers.c346
-rwxr-xr-xtrunk/channels/xpmr/sinetabx.h290
-rwxr-xr-xtrunk/channels/xpmr/xpmr.c2256
-rwxr-xr-xtrunk/channels/xpmr/xpmr.h543
-rwxr-xr-xtrunk/channels/xpmr/xpmr_coef.h951
-rw-r--r--trunk/codecs/Makefile58
-rw-r--r--trunk/codecs/adpcm_slin_ex.h25
-rw-r--r--trunk/codecs/codec_a_mu.c159
-rw-r--r--trunk/codecs/codec_adpcm.c400
-rw-r--r--trunk/codecs/codec_alaw.c184
-rw-r--r--trunk/codecs/codec_g722.c301
-rw-r--r--trunk/codecs/codec_g726.c960
-rw-r--r--trunk/codecs/codec_gsm.c286
-rw-r--r--trunk/codecs/codec_ilbc.c235
-rw-r--r--trunk/codecs/codec_lpc10.c311
-rw-r--r--trunk/codecs/codec_resample.c237
-rw-r--r--trunk/codecs/codec_speex.c503
-rw-r--r--trunk/codecs/codec_ulaw.c195
-rw-r--r--trunk/codecs/codec_zap.c488
-rw-r--r--trunk/codecs/g722/Makefile18
-rw-r--r--trunk/codecs/g722/g722.h148
-rw-r--r--trunk/codecs/g722/g722_decode.c398
-rw-r--r--trunk/codecs/g722/g722_encode.c400
-rw-r--r--trunk/codecs/g722_slin_ex.h25
-rw-r--r--trunk/codecs/g726_slin_ex.h25
-rw-r--r--trunk/codecs/gsm/COPYRIGHT16
-rw-r--r--trunk/codecs/gsm/Makefile543
-rw-r--r--trunk/codecs/gsm/README37
-rw-r--r--trunk/codecs/gsm/inc/config.h51
-rw-r--r--trunk/codecs/gsm/inc/gsm.h71
-rw-r--r--trunk/codecs/gsm/inc/private.h312
-rw-r--r--trunk/codecs/gsm/inc/proto.h65
-rw-r--r--trunk/codecs/gsm/inc/unproto.h23
-rw-r--r--trunk/codecs/gsm/libgsm.vcproj253
-rw-r--r--trunk/codecs/gsm/src/add.c235
-rw-r--r--trunk/codecs/gsm/src/code.c97
-rw-r--r--trunk/codecs/gsm/src/debug.c76
-rw-r--r--trunk/codecs/gsm/src/decode.c62
-rw-r--r--trunk/codecs/gsm/src/gsm_create.c45
-rw-r--r--trunk/codecs/gsm/src/gsm_decode.c361
-rw-r--r--trunk/codecs/gsm/src/gsm_destroy.c26
-rw-r--r--trunk/codecs/gsm/src/gsm_encode.c451
-rw-r--r--trunk/codecs/gsm/src/gsm_explode.c417
-rw-r--r--trunk/codecs/gsm/src/gsm_implode.c515
-rw-r--r--trunk/codecs/gsm/src/gsm_option.c69
-rw-r--r--trunk/codecs/gsm/src/gsm_print.c167
-rw-r--r--trunk/codecs/gsm/src/k6opt.h84
-rw-r--r--trunk/codecs/gsm/src/k6opt.s739
-rw-r--r--trunk/codecs/gsm/src/long_term.c955
-rw-r--r--trunk/codecs/gsm/src/lpc.c372
-rw-r--r--trunk/codecs/gsm/src/preprocess.c127
-rw-r--r--trunk/codecs/gsm/src/rpe.c490
-rw-r--r--trunk/codecs/gsm/src/short_term.c448
-rw-r--r--trunk/codecs/gsm/src/table.c63
-rw-r--r--trunk/codecs/gsm_slin_ex.h16
-rw-r--r--trunk/codecs/ilbc/FrameClassify.c110
-rw-r--r--trunk/codecs/ilbc/FrameClassify.h26
-rw-r--r--trunk/codecs/ilbc/LPCdecode.c152
-rw-r--r--trunk/codecs/ilbc/LPCdecode.h44
-rw-r--r--trunk/codecs/ilbc/LPCencode.c228
-rw-r--r--trunk/codecs/ilbc/LPCencode.h29
-rw-r--r--trunk/codecs/ilbc/Makefile21
-rw-r--r--trunk/codecs/ilbc/StateConstructW.c76
-rw-r--r--trunk/codecs/ilbc/StateConstructW.h27
-rw-r--r--trunk/codecs/ilbc/StateSearchW.c194
-rw-r--r--trunk/codecs/ilbc/StateSearchW.h48
-rw-r--r--trunk/codecs/ilbc/anaFilter.c71
-rw-r--r--trunk/codecs/ilbc/anaFilter.h26
-rw-r--r--trunk/codecs/ilbc/constants.c729
-rw-r--r--trunk/codecs/ilbc/constants.h74
-rw-r--r--trunk/codecs/ilbc/createCB.c216
-rw-r--r--trunk/codecs/ilbc/createCB.h56
-rw-r--r--trunk/codecs/ilbc/doCPLC.c259
-rw-r--r--trunk/codecs/ilbc/doCPLC.h32
-rw-r--r--trunk/codecs/ilbc/enhancer.c665
-rw-r--r--trunk/codecs/ilbc/enhancer.h33
-rw-r--r--trunk/codecs/ilbc/filter.c168
-rw-r--r--trunk/codecs/ilbc/filter.h73
-rw-r--r--trunk/codecs/ilbc/gainquant.c107
-rw-r--r--trunk/codecs/ilbc/gainquant.h31
-rw-r--r--trunk/codecs/ilbc/getCBvec.c181
-rw-r--r--trunk/codecs/ilbc/getCBvec.h28
-rw-r--r--trunk/codecs/ilbc/helpfun.c308
-rw-r--r--trunk/codecs/ilbc/helpfun.h101
-rw-r--r--trunk/codecs/ilbc/hpInput.c60
-rw-r--r--trunk/codecs/ilbc/hpInput.h27
-rw-r--r--trunk/codecs/ilbc/hpOutput.c59
-rw-r--r--trunk/codecs/ilbc/hpOutput.h25
-rw-r--r--trunk/codecs/ilbc/iCBConstruct.c108
-rw-r--r--trunk/codecs/ilbc/iCBConstruct.h38
-rw-r--r--trunk/codecs/ilbc/iCBSearch.c480
-rw-r--r--trunk/codecs/ilbc/iCBSearch.h35
-rw-r--r--trunk/codecs/ilbc/iLBC_decode.c619
-rw-r--r--trunk/codecs/ilbc/iLBC_decode.h40
-rw-r--r--trunk/codecs/ilbc/iLBC_define.h200
-rw-r--r--trunk/codecs/ilbc/iLBC_encode.c514
-rw-r--r--trunk/codecs/ilbc/iLBC_encode.h37
-rw-r--r--trunk/codecs/ilbc/libilbc.vcproj353
-rw-r--r--trunk/codecs/ilbc/lsf.c264
-rw-r--r--trunk/codecs/ilbc/lsf.h30
-rw-r--r--trunk/codecs/ilbc/packing.c175
-rw-r--r--trunk/codecs/ilbc/packing.h67
-rw-r--r--trunk/codecs/ilbc/syntFilter.c108
-rw-r--r--trunk/codecs/ilbc/syntFilter.h27
-rw-r--r--trunk/codecs/ilbc_slin_ex.h17
-rw-r--r--trunk/codecs/log2comp.h74
-rw-r--r--trunk/codecs/lpc10/Makefile78
-rw-r--r--trunk/codecs/lpc10/README89
-rw-r--r--trunk/codecs/lpc10/analys.c649
-rw-r--r--trunk/codecs/lpc10/bsynz.c447
-rw-r--r--trunk/codecs/lpc10/chanwr.c232
-rw-r--r--trunk/codecs/lpc10/dcbias.c107
-rw-r--r--trunk/codecs/lpc10/decode.c625
-rw-r--r--trunk/codecs/lpc10/deemp.c154
-rw-r--r--trunk/codecs/lpc10/difmag.c133
-rw-r--r--trunk/codecs/lpc10/dyptrk.c405
-rw-r--r--trunk/codecs/lpc10/encode.c373
-rw-r--r--trunk/codecs/lpc10/energy.c103
-rw-r--r--trunk/codecs/lpc10/f2c.h325
-rw-r--r--trunk/codecs/lpc10/f2clib.c85
-rw-r--r--trunk/codecs/lpc10/ham84.c126
-rw-r--r--trunk/codecs/lpc10/hp100.c169
-rw-r--r--trunk/codecs/lpc10/invert.c193
-rw-r--r--trunk/codecs/lpc10/irc2pc.c151
-rw-r--r--trunk/codecs/lpc10/ivfilt.c136
-rw-r--r--trunk/codecs/lpc10/liblpc10.vcproj305
-rw-r--r--trunk/codecs/lpc10/lpc10.h256
-rw-r--r--trunk/codecs/lpc10/lpcdec.c297
-rw-r--r--trunk/codecs/lpc10/lpcenc.c181
-rw-r--r--trunk/codecs/lpc10/lpcini.c446
-rw-r--r--trunk/codecs/lpc10/lpfilt.c125
-rw-r--r--trunk/codecs/lpc10/median.c89
-rw-r--r--trunk/codecs/lpc10/mload.c163
-rw-r--r--trunk/codecs/lpc10/onset.c324
-rw-r--r--trunk/codecs/lpc10/pitsyn.c583
-rw-r--r--trunk/codecs/lpc10/placea.c242
-rw-r--r--trunk/codecs/lpc10/placev.c275
-rw-r--r--trunk/codecs/lpc10/preemp.c144
-rw-r--r--trunk/codecs/lpc10/prepro.c116
-rw-r--r--trunk/codecs/lpc10/random.c125
-rw-r--r--trunk/codecs/lpc10/rcchk.c119
-rw-r--r--trunk/codecs/lpc10/synths.c425
-rw-r--r--trunk/codecs/lpc10/tbdm.c188
-rw-r--r--trunk/codecs/lpc10/voicin.c786
-rw-r--r--trunk/codecs/lpc10/vparms.c255
-rw-r--r--trunk/codecs/lpc10_slin_ex.h13
-rw-r--r--trunk/codecs/slin_adpcm_ex.h25
-rw-r--r--trunk/codecs/slin_g722_ex.h25
-rw-r--r--trunk/codecs/slin_g726_ex.h25
-rw-r--r--trunk/codecs/slin_gsm_ex.h28
-rw-r--r--trunk/codecs/slin_ilbc_ex.h28
-rw-r--r--trunk/codecs/slin_lpc10_ex.h21
-rw-r--r--trunk/codecs/slin_resample_ex.h43
-rw-r--r--trunk/codecs/slin_speex_ex.h262
-rw-r--r--trunk/codecs/slin_ulaw_ex.h25
-rw-r--r--trunk/codecs/speex_slin_ex.h16
-rw-r--r--trunk/codecs/ulaw_slin_ex.h25
-rwxr-xr-xtrunk/config.guess1495
-rwxr-xr-xtrunk/config.sub1609
-rw-r--r--trunk/configs/adsi.conf.sample8
-rw-r--r--trunk/configs/adtranvofr.conf.sample39
-rw-r--r--trunk/configs/agents.conf.sample105
-rw-r--r--trunk/configs/alarmreceiver.conf.sample80
-rw-r--r--trunk/configs/alsa.conf.sample62
-rw-r--r--trunk/configs/amd.conf.sample18
-rw-r--r--trunk/configs/asterisk.adsi159
-rw-r--r--trunk/configs/cdr.conf.sample148
-rw-r--r--trunk/configs/cdr_adaptive_odbc.conf.sample40
-rw-r--r--trunk/configs/cdr_custom.conf.sample10
-rw-r--r--trunk/configs/cdr_manager.conf.sample15
-rw-r--r--trunk/configs/cdr_odbc.conf.sample12
-rw-r--r--trunk/configs/cdr_pgsql.conf.sample9
-rw-r--r--trunk/configs/cdr_sqlite3_custom.conf.sample7
-rw-r--r--trunk/configs/cdr_tds.conf.sample11
-rw-r--r--trunk/configs/codecs.conf.sample65
-rw-r--r--trunk/configs/console.conf.sample68
-rw-r--r--trunk/configs/dnsmgr.conf.sample5
-rw-r--r--trunk/configs/dundi.conf.sample261
-rw-r--r--trunk/configs/enum.conf.sample22
-rw-r--r--trunk/configs/extconfig.conf.sample63
-rw-r--r--trunk/configs/extensions.ael.sample448
-rw-r--r--trunk/configs/extensions.conf.sample614
-rw-r--r--trunk/configs/extensions.lua.sample208
-rw-r--r--trunk/configs/extensions_minivm.conf.sample159
-rw-r--r--trunk/configs/features.conf.sample124
-rw-r--r--trunk/configs/festival.conf.sample35
-rw-r--r--trunk/configs/followme.conf.sample86
-rw-r--r--trunk/configs/func_odbc.conf.sample77
-rw-r--r--trunk/configs/gtalk.conf.sample19
-rw-r--r--trunk/configs/h323.conf.sample207
-rw-r--r--trunk/configs/http.conf.sample71
-rw-r--r--trunk/configs/iax.conf.sample434
-rw-r--r--trunk/configs/iaxprov.conf.sample81
-rw-r--r--trunk/configs/indications.conf.sample733
-rw-r--r--trunk/configs/jabber.conf.sample21
-rw-r--r--trunk/configs/jingle.conf.sample19
-rw-r--r--trunk/configs/logger.conf.sample96
-rw-r--r--trunk/configs/manager.conf.sample97
-rw-r--r--trunk/configs/meetme.conf.sample45
-rw-r--r--trunk/configs/mgcp.conf.sample110
-rw-r--r--trunk/configs/minivm.conf.sample218
-rw-r--r--trunk/configs/misdn.conf.sample455
-rw-r--r--trunk/configs/modules.conf.sample39
-rw-r--r--trunk/configs/musiconhold.conf.sample79
-rw-r--r--trunk/configs/muted.conf.sample39
-rw-r--r--trunk/configs/osp.conf.sample90
-rw-r--r--trunk/configs/oss.conf.sample138
-rw-r--r--trunk/configs/phone.conf.sample51
-rw-r--r--trunk/configs/phoneprov.conf.sample60
-rw-r--r--trunk/configs/queuerules.conf.sample20
-rw-r--r--trunk/configs/queues.conf.sample403
-rw-r--r--trunk/configs/res_config_sqlite.conf11
-rw-r--r--trunk/configs/res_odbc.conf.sample48
-rw-r--r--trunk/configs/res_pgsql.conf.sample14
-rw-r--r--trunk/configs/res_snmp.conf.sample10
-rw-r--r--trunk/configs/rpt.conf.sample193
-rw-r--r--trunk/configs/rtp.conf.sample22
-rw-r--r--trunk/configs/say.conf.sample200
-rw-r--r--trunk/configs/sip.conf.sample896
-rw-r--r--trunk/configs/sip_notify.conf.sample22
-rw-r--r--trunk/configs/skinny.conf.sample120
-rw-r--r--trunk/configs/sla.conf.sample140
-rw-r--r--trunk/configs/smdi.conf.sample43
-rw-r--r--trunk/configs/telcordia-1.adsi83
-rw-r--r--trunk/configs/udptl.conf.sample30
-rw-r--r--trunk/configs/unistim.conf.sample76
-rw-r--r--trunk/configs/usbradio.conf.sample54
-rw-r--r--trunk/configs/users.conf.sample79
-rw-r--r--trunk/configs/voicemail.conf.sample307
-rw-r--r--trunk/configs/vpb.conf.sample108
-rw-r--r--trunk/configs/zapata.conf.sample909
-rwxr-xr-xtrunk/configure50171
-rw-r--r--trunk/configure.ac1208
-rw-r--r--trunk/contrib/README.festival47
-rw-r--r--trunk/contrib/asterisk-doxygen-header10
-rw-r--r--trunk/contrib/asterisk-ices.xml93
-rw-r--r--trunk/contrib/asterisk-ng-doxygen1274
-rw-r--r--trunk/contrib/dictionary.digium31
-rw-r--r--trunk/contrib/festival-1.4.1-diff76
-rw-r--r--trunk/contrib/festival-1.4.2.diff75
-rw-r--r--trunk/contrib/festival-1.4.3.diff93
-rw-r--r--trunk/contrib/festival-1.95.diff107
-rw-r--r--trunk/contrib/firmware/iax/iaxy.binbin0 -> 39402 bytes
-rw-r--r--trunk/contrib/i18n.testsuite.conf136
-rwxr-xr-xtrunk/contrib/init.d/rc.debian.asterisk85
-rwxr-xr-xtrunk/contrib/init.d/rc.gentoo.asterisk18
-rwxr-xr-xtrunk/contrib/init.d/rc.mandrake.asterisk185
-rwxr-xr-xtrunk/contrib/init.d/rc.mandrake.zaptel108
-rwxr-xr-xtrunk/contrib/init.d/rc.redhat.asterisk136
-rwxr-xr-xtrunk/contrib/init.d/rc.slackware.asterisk43
-rwxr-xr-xtrunk/contrib/init.d/rc.suse.asterisk127
-rw-r--r--trunk/contrib/scripts/README.messages-expire20
-rw-r--r--trunk/contrib/scripts/agents.php73
-rw-r--r--trunk/contrib/scripts/ast_grab_core70
-rw-r--r--trunk/contrib/scripts/astgenkey61
-rw-r--r--trunk/contrib/scripts/astgenkey.8129
-rw-r--r--trunk/contrib/scripts/autosupport163
-rw-r--r--trunk/contrib/scripts/autosupport.841
-rw-r--r--trunk/contrib/scripts/iax-friends.sql15
-rw-r--r--trunk/contrib/scripts/loadtest.tcl148
-rw-r--r--trunk/contrib/scripts/lookup.agi90
-rw-r--r--trunk/contrib/scripts/managerproxy.pl242
-rw-r--r--trunk/contrib/scripts/meetme.sql12
-rw-r--r--trunk/contrib/scripts/messages-expire.pl96
-rw-r--r--trunk/contrib/scripts/postgres_cdr.sql33
-rw-r--r--trunk/contrib/scripts/qview.pl100
-rw-r--r--trunk/contrib/scripts/realtime_pgsql.sql141
-rw-r--r--trunk/contrib/scripts/retrieve_extensions_from_mysql.pl113
-rw-r--r--trunk/contrib/scripts/retrieve_extensions_from_sql.pl158
-rw-r--r--trunk/contrib/scripts/retrieve_sip_conf_from_mysql.pl93
-rw-r--r--trunk/contrib/scripts/safe_asterisk184
-rw-r--r--trunk/contrib/scripts/safe_asterisk.869
-rw-r--r--trunk/contrib/scripts/safe_asterisk_restart110
-rw-r--r--trunk/contrib/scripts/sip-friends.sql14
-rw-r--r--trunk/contrib/scripts/vmail.cgi1099
-rw-r--r--trunk/contrib/scripts/vmdb.sql66
-rw-r--r--trunk/contrib/thirdparty/spexxilbcfix_xlite.regbin0 -> 452 bytes
-rw-r--r--trunk/contrib/thirdparty/spexxilbcfix_xpro.regbin0 -> 450 bytes
-rw-r--r--trunk/contrib/utils/README.rawplayer37
-rw-r--r--trunk/contrib/utils/eagi_proxy.c419
-rw-r--r--trunk/contrib/utils/rawplayer.c46
-rw-r--r--trunk/contrib/utils/zones2indications.c153
-rw-r--r--trunk/contrib/valgrind-RedHat-8.0.supp41
-rw-r--r--trunk/doc/CODING-GUIDELINES684
-rw-r--r--trunk/doc/India-CID.txt75
-rw-r--r--trunk/doc/PEERING503
-rw-r--r--trunk/doc/asterisk-mib.txt769
-rw-r--r--trunk/doc/asterisk.8199
-rw-r--r--trunk/doc/asterisk.sgml373
-rw-r--r--trunk/doc/backtrace.txt191
-rw-r--r--trunk/doc/callfiles.txt139
-rw-r--r--trunk/doc/datastores.txt63
-rw-r--r--trunk/doc/digium-mib.txt17
-rw-r--r--trunk/doc/externalivr.txt117
-rw-r--r--trunk/doc/jabber.txt15
-rw-r--r--trunk/doc/jingle.txt10
-rw-r--r--trunk/doc/macroexclusive.txt78
-rw-r--r--trunk/doc/manager_1_1.txt286
-rw-r--r--trunk/doc/modules.txt25
-rw-r--r--trunk/doc/osp.txt747
-rw-r--r--trunk/doc/queue.txt39
-rw-r--r--trunk/doc/res_config_sqlite.txt124
-rw-r--r--trunk/doc/rtp-packetization.txt75
-rw-r--r--trunk/doc/siptls.txt94
-rw-r--r--trunk/doc/smdi.txt25
-rw-r--r--trunk/doc/sms.txt147
-rw-r--r--trunk/doc/snmp.txt39
-rw-r--r--trunk/doc/speechrec.txt295
-rw-r--r--trunk/doc/ss7.txt113
-rw-r--r--trunk/doc/tex/Makefile44
-rw-r--r--trunk/doc/tex/README.txt24
-rw-r--r--trunk/doc/tex/ael.tex1305
-rw-r--r--trunk/doc/tex/ajam.tex97
-rw-r--r--trunk/doc/tex/app-sms.tex518
-rw-r--r--trunk/doc/tex/asterisk-conf.tex141
-rw-r--r--trunk/doc/tex/asterisk.tex162
-rw-r--r--trunk/doc/tex/backtrace.tex217
-rw-r--r--trunk/doc/tex/billing.tex86
-rw-r--r--trunk/doc/tex/cdrdriver.tex458
-rw-r--r--trunk/doc/tex/chaniax.tex84
-rw-r--r--trunk/doc/tex/channelvariables.tex974
-rw-r--r--trunk/doc/tex/cliprompt.tex29
-rw-r--r--trunk/doc/tex/configuration.tex225
-rw-r--r--trunk/doc/tex/dundi.tex41
-rw-r--r--trunk/doc/tex/enum.tex355
-rw-r--r--trunk/doc/tex/extensions.tex82
-rw-r--r--trunk/doc/tex/freetds.tex16
-rw-r--r--trunk/doc/tex/hardware.tex100
-rw-r--r--trunk/doc/tex/ices.tex7
-rw-r--r--trunk/doc/tex/imapstorage.tex196
-rw-r--r--trunk/doc/tex/jitterbuffer.tex98
-rw-r--r--trunk/doc/tex/localchannel.tex80
-rw-r--r--trunk/doc/tex/manager.tex258
-rw-r--r--trunk/doc/tex/misdn.tex272
-rw-r--r--trunk/doc/tex/mp3.tex11
-rw-r--r--trunk/doc/tex/odbcstorage.tex31
-rw-r--r--trunk/doc/tex/phoneprov.tex307
-rw-r--r--trunk/doc/tex/privacy.tex354
-rw-r--r--trunk/doc/tex/qos.tex135
-rw-r--r--trunk/doc/tex/queuelog.tex118
-rw-r--r--trunk/doc/tex/queues-with-callback-members.tex551
-rw-r--r--trunk/doc/tex/realtime.tex127
-rw-r--r--trunk/doc/tex/security.tex80
-rw-r--r--trunk/doc/tex/sla.tex387
-rw-r--r--trunk/doc/unistim.txt127
-rw-r--r--trunk/doc/valgrind.txt19
-rw-r--r--trunk/doc/video.txt46
-rw-r--r--trunk/doc/voicemail_odbc_postgresql.txt427
-rw-r--r--trunk/formats/Makefile20
-rw-r--r--trunk/formats/format_g723.c152
-rw-r--r--trunk/formats/format_g726.c261
-rw-r--r--trunk/formats/format_g729.c148
-rw-r--r--trunk/formats/format_gsm.c170
-rw-r--r--trunk/formats/format_h263.c186
-rw-r--r--trunk/formats/format_h264.c175
-rw-r--r--trunk/formats/format_ilbc.c146
-rw-r--r--trunk/formats/format_jpeg.c115
-rw-r--r--trunk/formats/format_ogg_vorbis.c552
-rw-r--r--trunk/formats/format_pcm.c485
-rw-r--r--trunk/formats/format_sln.c130
-rw-r--r--trunk/formats/format_sln16.c138
-rw-r--r--trunk/formats/format_vox.c135
-rw-r--r--trunk/formats/format_wav.c491
-rw-r--r--trunk/formats/format_wav_gsm.c559
-rw-r--r--trunk/formats/msgsm.h689
-rw-r--r--trunk/funcs/Makefile20
-rw-r--r--trunk/funcs/func_base64.c87
-rw-r--r--trunk/funcs/func_blacklist.c74
-rw-r--r--trunk/funcs/func_callerid.c233
-rw-r--r--trunk/funcs/func_cdr.c162
-rw-r--r--trunk/funcs/func_channel.c206
-rw-r--r--trunk/funcs/func_curl.c204
-rw-r--r--trunk/funcs/func_cut.c300
-rw-r--r--trunk/funcs/func_db.c225
-rw-r--r--trunk/funcs/func_devstate.c255
-rw-r--r--trunk/funcs/func_dialgroup.c220
-rw-r--r--trunk/funcs/func_dialplan.c106
-rw-r--r--trunk/funcs/func_enum.c391
-rw-r--r--trunk/funcs/func_env.c215
-rw-r--r--trunk/funcs/func_extstate.c133
-rw-r--r--trunk/funcs/func_global.c82
-rw-r--r--trunk/funcs/func_groupcount.c231
-rw-r--r--trunk/funcs/func_iconv.c125
-rw-r--r--trunk/funcs/func_lock.c350
-rw-r--r--trunk/funcs/func_logic.c242
-rw-r--r--trunk/funcs/func_math.c333
-rw-r--r--trunk/funcs/func_md5.c67
-rw-r--r--trunk/funcs/func_module.c68
-rw-r--r--trunk/funcs/func_odbc.c900
-rw-r--r--trunk/funcs/func_rand.c93
-rw-r--r--trunk/funcs/func_realtime.c154
-rw-r--r--trunk/funcs/func_sha1.c76
-rw-r--r--trunk/funcs/func_shell.c92
-rw-r--r--trunk/funcs/func_strings.c886
-rw-r--r--trunk/funcs/func_sysinfo.c116
-rw-r--r--trunk/funcs/func_timeout.c184
-rw-r--r--trunk/funcs/func_uri.c96
-rw-r--r--trunk/funcs/func_version.c101
-rw-r--r--trunk/funcs/func_vmcount.c93
-rw-r--r--trunk/funcs/func_volume.c160
-rw-r--r--trunk/images/animlogo.gifbin0 -> 63968 bytes
-rw-r--r--trunk/images/asterisk-intro.jpgbin0 -> 6143 bytes
-rw-r--r--trunk/images/font.pngbin0 -> 1549 bytes
-rw-r--r--trunk/images/kpad2.jpgbin0 -> 10374 bytes
-rw-r--r--trunk/images/play.gifbin0 -> 341 bytes
-rw-r--r--trunk/include/asterisk.h184
-rw-r--r--trunk/include/asterisk/_private.h53
-rw-r--r--trunk/include/asterisk/abstract_jb.h220
-rw-r--r--trunk/include/asterisk/acl.h84
-rw-r--r--trunk/include/asterisk/adsi.h353
-rw-r--r--trunk/include/asterisk/ael_structs.h123
-rw-r--r--trunk/include/asterisk/aes.h67
-rw-r--r--trunk/include/asterisk/aes_internal.h170
-rw-r--r--trunk/include/asterisk/agi.h66
-rw-r--r--trunk/include/asterisk/alaw.h86
-rw-r--r--trunk/include/asterisk/app.h463
-rw-r--r--trunk/include/asterisk/ast_expr.h40
-rw-r--r--trunk/include/asterisk/astdb.h58
-rw-r--r--trunk/include/asterisk/astmm.h82
-rw-r--r--trunk/include/asterisk/astobj.h819
-rw-r--r--trunk/include/asterisk/astobj2.h568
-rw-r--r--trunk/include/asterisk/astosp.h31
-rw-r--r--trunk/include/asterisk/audiohook.h210
-rw-r--r--trunk/include/asterisk/autoconfig.h.in1205
-rw-r--r--trunk/include/asterisk/callerid.h345
-rw-r--r--trunk/include/asterisk/causes.h149
-rw-r--r--trunk/include/asterisk/cdr.h337
-rw-r--r--trunk/include/asterisk/channel.h1577
-rw-r--r--trunk/include/asterisk/chanvars.h42
-rw-r--r--trunk/include/asterisk/cli.h285
-rw-r--r--trunk/include/asterisk/compat.h184
-rw-r--r--trunk/include/asterisk/compiler.h56
-rw-r--r--trunk/include/asterisk/config.h406
-rw-r--r--trunk/include/asterisk/crypto.h126
-rw-r--r--trunk/include/asterisk/devicestate.h203
-rw-r--r--trunk/include/asterisk/dial.h168
-rw-r--r--trunk/include/asterisk/dlfcn-compat.h88
-rw-r--r--trunk/include/asterisk/dns.h39
-rw-r--r--trunk/include/asterisk/dnsmgr.h62
-rw-r--r--trunk/include/asterisk/doxyref.h563
-rw-r--r--trunk/include/asterisk/dsp.h111
-rw-r--r--trunk/include/asterisk/dundi.h231
-rw-r--r--trunk/include/asterisk/endian.h69
-rw-r--r--trunk/include/asterisk/enum.h90
-rw-r--r--trunk/include/asterisk/event.h482
-rw-r--r--trunk/include/asterisk/event_defs.h143
-rw-r--r--trunk/include/asterisk/extconf.h248
-rw-r--r--trunk/include/asterisk/features.h112
-rw-r--r--trunk/include/asterisk/file.h322
-rw-r--r--trunk/include/asterisk/frame.h603
-rw-r--r--trunk/include/asterisk/fskmodem.h81
-rw-r--r--trunk/include/asterisk/global_datastores.h36
-rw-r--r--trunk/include/asterisk/hashtab.h324
-rw-r--r--trunk/include/asterisk/http.h93
-rw-r--r--trunk/include/asterisk/image.h90
-rw-r--r--trunk/include/asterisk/indications.h89
-rw-r--r--trunk/include/asterisk/inline_api.h66
-rw-r--r--trunk/include/asterisk/io.h150
-rw-r--r--trunk/include/asterisk/jabber.h201
-rw-r--r--trunk/include/asterisk/jingle.h61
l---------trunk/include/asterisk/libresample.h1
-rw-r--r--trunk/include/asterisk/linkedlists.h775
-rw-r--r--trunk/include/asterisk/localtime.h48
-rw-r--r--trunk/include/asterisk/lock.h1205
-rw-r--r--trunk/include/asterisk/logger.h178
-rw-r--r--trunk/include/asterisk/manager.h211
-rw-r--r--trunk/include/asterisk/md5.h38
-rw-r--r--trunk/include/asterisk/mod_format.h144
-rw-r--r--trunk/include/asterisk/module.h421
-rw-r--r--trunk/include/asterisk/monitor.h71
-rw-r--r--trunk/include/asterisk/musiconhold.h59
-rw-r--r--trunk/include/asterisk/netsock.h70
-rw-r--r--trunk/include/asterisk/network.h98
-rw-r--r--trunk/include/asterisk/options.h137
-rw-r--r--trunk/include/asterisk/paths.h39
-rw-r--r--trunk/include/asterisk/pbx.h978
-rw-r--r--trunk/include/asterisk/plc.h153
-rw-r--r--trunk/include/asterisk/poll-compat.h111
-rw-r--r--trunk/include/asterisk/privacy.h46
-rw-r--r--trunk/include/asterisk/pval.h273
-rw-r--r--trunk/include/asterisk/res_odbc.h122
-rw-r--r--trunk/include/asterisk/rtp.h293
-rw-r--r--trunk/include/asterisk/say.h170
-rw-r--r--trunk/include/asterisk/sched.h176
-rw-r--r--trunk/include/asterisk/sha1.h73
-rw-r--r--trunk/include/asterisk/slinfactory.h65
-rw-r--r--trunk/include/asterisk/smdi.h127
-rw-r--r--trunk/include/asterisk/speech.h156
-rw-r--r--trunk/include/asterisk/srv.h45
-rw-r--r--trunk/include/asterisk/stringfields.h307
-rw-r--r--trunk/include/asterisk/strings.h691
-rw-r--r--trunk/include/asterisk/tcptls.h166
-rw-r--r--trunk/include/asterisk/tdd.h82
-rw-r--r--trunk/include/asterisk/term.h85
-rw-r--r--trunk/include/asterisk/threadstorage.h212
-rw-r--r--trunk/include/asterisk/time.h178
-rw-r--r--trunk/include/asterisk/transcap.h46
-rw-r--r--trunk/include/asterisk/translate.h273
-rw-r--r--trunk/include/asterisk/udptl.h127
-rw-r--r--trunk/include/asterisk/ulaw.h87
-rw-r--r--trunk/include/asterisk/unaligned.h102
-rw-r--r--trunk/include/asterisk/utils.h657
-rw-r--r--trunk/include/asterisk/version.h44
-rw-r--r--trunk/include/asterisk/zapata.h48
-rw-r--r--trunk/include/jitterbuf.h174
-rw-r--r--trunk/include/solaris-compat/compat.h46
-rw-r--r--trunk/include/solaris-compat/sys/cdefs.h10
-rw-r--r--trunk/include/solaris-compat/sys/queue.h540
-rwxr-xr-xtrunk/install-sh269
-rw-r--r--trunk/keys/freeworlddialup.pub6
-rw-r--r--trunk/keys/iaxtel.pub6
-rw-r--r--trunk/main/Makefile174
-rw-r--r--trunk/main/abstract_jb.c774
-rw-r--r--trunk/main/acl.c357
-rw-r--r--trunk/main/adsistub.c77
-rw-r--r--trunk/main/aescrypt.c321
-rw-r--r--trunk/main/aeskey.c473
-rw-r--r--trunk/main/aesopt.h1029
-rw-r--r--trunk/main/aestab.c236
-rw-r--r--trunk/main/alaw.c210
-rw-r--r--trunk/main/app.c1739
-rw-r--r--trunk/main/ast_expr2.c3448
-rw-r--r--trunk/main/ast_expr2.fl444
-rw-r--r--trunk/main/ast_expr2.h115
-rw-r--r--trunk/main/ast_expr2.y1619
-rw-r--r--trunk/main/ast_expr2f.c2515
-rw-r--r--trunk/main/asterisk.c3267
-rw-r--r--trunk/main/astmm.c479
-rw-r--r--trunk/main/astobj2.c732
-rw-r--r--trunk/main/audiohook.c693
-rw-r--r--trunk/main/autoservice.c252
-rw-r--r--trunk/main/buildinfo.c33
-rw-r--r--trunk/main/callerid.c1115
-rw-r--r--trunk/main/cdr.c1456
-rw-r--r--trunk/main/channel.c4843
-rw-r--r--trunk/main/chanvars.c82
-rw-r--r--trunk/main/cli.c1918
-rw-r--r--trunk/main/config.c2281
-rw-r--r--trunk/main/cryptostub.c67
-rw-r--r--trunk/main/cygload.c39
-rw-r--r--trunk/main/db.c671
-rw-r--r--trunk/main/db1-ast/Makefile72
-rw-r--r--trunk/main/db1-ast/btree/bt_close.c182
-rw-r--r--trunk/main/db1-ast/btree/bt_conv.c221
-rw-r--r--trunk/main/db1-ast/btree/bt_debug.c329
-rw-r--r--trunk/main/db1-ast/btree/bt_delete.c657
-rw-r--r--trunk/main/db1-ast/btree/bt_get.c105
-rw-r--r--trunk/main/db1-ast/btree/bt_open.c458
-rw-r--r--trunk/main/db1-ast/btree/bt_overflow.c228
-rw-r--r--trunk/main/db1-ast/btree/bt_page.c100
-rw-r--r--trunk/main/db1-ast/btree/bt_put.c321
-rw-r--r--trunk/main/db1-ast/btree/bt_search.c213
-rw-r--r--trunk/main/db1-ast/btree/bt_seq.c460
-rw-r--r--trunk/main/db1-ast/btree/bt_split.c829
-rw-r--r--trunk/main/db1-ast/btree/bt_utils.c260
-rw-r--r--trunk/main/db1-ast/btree/btree.h391
-rw-r--r--trunk/main/db1-ast/btree/extern.h70
-rw-r--r--trunk/main/db1-ast/db/db.c103
-rw-r--r--trunk/main/db1-ast/hash/README72
-rw-r--r--trunk/main/db1-ast/hash/extern.h65
-rw-r--r--trunk/main/db1-ast/hash/hash.c999
-rw-r--r--trunk/main/db1-ast/hash/hash.h293
-rw-r--r--trunk/main/db1-ast/hash/hash_bigkey.c668
-rw-r--r--trunk/main/db1-ast/hash/hash_buf.c355
-rw-r--r--trunk/main/db1-ast/hash/hash_func.c225
-rw-r--r--trunk/main/db1-ast/hash/hash_log2.c56
-rw-r--r--trunk/main/db1-ast/hash/hash_page.c944
-rw-r--r--trunk/main/db1-ast/hash/hsearch.c107
-rw-r--r--trunk/main/db1-ast/hash/ndbm.c235
-rw-r--r--trunk/main/db1-ast/hash/page.h92
-rw-r--r--trunk/main/db1-ast/hash/search.h51
-rw-r--r--trunk/main/db1-ast/include/circ-queue.h131
-rw-r--r--trunk/main/db1-ast/include/compat.h49
-rw-r--r--trunk/main/db1-ast/include/db.h250
-rw-r--r--trunk/main/db1-ast/include/mpool.h115
-rw-r--r--trunk/main/db1-ast/include/ndbm.h79
-rw-r--r--trunk/main/db1-ast/libdb.map11
-rw-r--r--trunk/main/db1-ast/mpool/README7
-rw-r--r--trunk/main/db1-ast/mpool/mpool.c498
-rw-r--r--trunk/main/db1-ast/recno/extern.h54
-rw-r--r--trunk/main/db1-ast/recno/rec_close.c183
-rw-r--r--trunk/main/db1-ast/recno/rec_delete.c197
-rw-r--r--trunk/main/db1-ast/recno/rec_get.c311
-rw-r--r--trunk/main/db1-ast/recno/rec_open.c241
-rw-r--r--trunk/main/db1-ast/recno/rec_put.c280
-rw-r--r--trunk/main/db1-ast/recno/rec_search.c126
-rw-r--r--trunk/main/db1-ast/recno/rec_seq.c131
-rw-r--r--trunk/main/db1-ast/recno/rec_utils.c122
-rw-r--r--trunk/main/db1-ast/recno/recno.h39
-rw-r--r--trunk/main/devicestate.c553
-rw-r--r--trunk/main/dial.c1014
-rw-r--r--trunk/main/dlfcn.c1309
-rw-r--r--trunk/main/dns.c299
-rw-r--r--trunk/main/dnsmgr.c425
-rw-r--r--trunk/main/dsp.c1349
-rw-r--r--trunk/main/ecdisa.h15
-rw-r--r--trunk/main/editline/CHANGES42
-rw-r--r--trunk/main/editline/INSTALL64
-rw-r--r--trunk/main/editline/Makefile.in234
-rw-r--r--trunk/main/editline/PLATFORMS13
-rw-r--r--trunk/main/editline/README11
-rw-r--r--trunk/main/editline/TEST/test.c268
-rw-r--r--trunk/main/editline/chared.c695
-rw-r--r--trunk/main/editline/chared.h159
-rw-r--r--trunk/main/editline/common.c951
-rwxr-xr-xtrunk/main/editline/config.guess1449
-rw-r--r--trunk/main/editline/config.h.in21
-rwxr-xr-xtrunk/main/editline/config.sub1412
-rwxr-xr-xtrunk/main/editline/configure2309
-rw-r--r--trunk/main/editline/configure.in274
-rw-r--r--trunk/main/editline/editline.3646
-rw-r--r--trunk/main/editline/editrc.5491
-rw-r--r--trunk/main/editline/el.c509
-rw-r--r--trunk/main/editline/el.h145
-rw-r--r--trunk/main/editline/emacs.c488
-rw-r--r--trunk/main/editline/hist.c197
-rw-r--r--trunk/main/editline/hist.h80
-rw-r--r--trunk/main/editline/histedit.h197
-rw-r--r--trunk/main/editline/history.c875
-rwxr-xr-xtrunk/main/editline/install-sh250
-rw-r--r--trunk/main/editline/key.c687
-rw-r--r--trunk/main/editline/key.h79
-rw-r--r--trunk/main/editline/makelist254
-rw-r--r--trunk/main/editline/map.c1418
-rw-r--r--trunk/main/editline/map.h79
-rw-r--r--trunk/main/editline/np/fgetln.c88
-rw-r--r--trunk/main/editline/np/strlcat.c75
-rw-r--r--trunk/main/editline/np/strlcpy.c75
-rw-r--r--trunk/main/editline/np/unvis.c322
-rw-r--r--trunk/main/editline/np/vis.c347
-rw-r--r--trunk/main/editline/np/vis.h96
-rw-r--r--trunk/main/editline/parse.c259
-rw-r--r--trunk/main/editline/parse.h52
-rw-r--r--trunk/main/editline/prompt.c174
-rw-r--r--trunk/main/editline/prompt.h62
-rw-r--r--trunk/main/editline/read.c555
-rw-r--r--trunk/main/editline/read.h55
-rw-r--r--trunk/main/editline/readline.c1665
-rw-r--r--trunk/main/editline/readline/readline.h118
-rw-r--r--trunk/main/editline/refresh.c1104
-rw-r--r--trunk/main/editline/refresh.h63
-rw-r--r--trunk/main/editline/search.c649
-rw-r--r--trunk/main/editline/search.h70
-rw-r--r--trunk/main/editline/sig.c198
-rw-r--r--trunk/main/editline/sig.h72
-rw-r--r--trunk/main/editline/sys.h123
-rw-r--r--trunk/main/editline/term.c1587
-rw-r--r--trunk/main/editline/term.h124
-rw-r--r--trunk/main/editline/tokenizer.c397
-rw-r--r--trunk/main/editline/tokenizer.h54
-rw-r--r--trunk/main/editline/tty.c1182
-rw-r--r--trunk/main/editline/tty.h484
-rw-r--r--trunk/main/editline/vi.c941
-rw-r--r--trunk/main/enum.c655
-rw-r--r--trunk/main/event.c822
-rw-r--r--trunk/main/file.c1245
-rw-r--r--trunk/main/fixedjitterbuf.c347
-rw-r--r--trunk/main/fixedjitterbuf.h93
-rw-r--r--trunk/main/frame.c1513
-rw-r--r--trunk/main/fskmodem.c360
-rw-r--r--trunk/main/global_datastores.c83
-rw-r--r--trunk/main/hashtab.c805
-rw-r--r--trunk/main/http.c1118
-rw-r--r--trunk/main/image.c208
-rw-r--r--trunk/main/indications.c598
-rw-r--r--trunk/main/io.c381
-rw-r--r--trunk/main/jitterbuf.c844
-rw-r--r--trunk/main/libresample/LICENSE.txt463
-rw-r--r--trunk/main/libresample/Makefile.asterisk11
-rw-r--r--trunk/main/libresample/Makefile.in52
-rw-r--r--trunk/main/libresample/README.txt84
-rwxr-xr-xtrunk/main/libresample/config.guess1432
-rwxr-xr-xtrunk/main/libresample/config.sub1537
-rwxr-xr-xtrunk/main/libresample/configure4552
-rw-r--r--trunk/main/libresample/configure.in68
-rw-r--r--trunk/main/libresample/include/libresample.h120
-rwxr-xr-xtrunk/main/libresample/install-sh251
-rw-r--r--trunk/main/libresample/src/configtemplate.h7
-rw-r--r--trunk/main/libresample/src/filterkit.c215
-rw-r--r--trunk/main/libresample/src/filterkit.h28
-rw-r--r--trunk/main/libresample/src/resample.c347
-rw-r--r--trunk/main/libresample/src/resample_defs.h88
-rw-r--r--trunk/main/libresample/src/resamplesubs.c123
-rw-r--r--trunk/main/libresample/tests/compareresample.c183
-rw-r--r--trunk/main/libresample/tests/resample-sndfile.c213
-rw-r--r--trunk/main/libresample/tests/testresample.c182
-rw-r--r--trunk/main/libresample/win/libresample.dsp116
-rw-r--r--trunk/main/libresample/win/libresample.vcproj192
-rw-r--r--trunk/main/loader.c1006
-rw-r--r--trunk/main/logger.c1203
-rw-r--r--trunk/main/manager.c3768
-rw-r--r--trunk/main/md5.c265
-rw-r--r--trunk/main/minimime/Make.conf7
-rw-r--r--trunk/main/minimime/Makefile67
-rw-r--r--trunk/main/minimime/mimeparser.h76
-rw-r--r--trunk/main/minimime/mimeparser.l484
-rw-r--r--trunk/main/minimime/mimeparser.tab.c2339
-rw-r--r--trunk/main/minimime/mimeparser.tab.h112
-rw-r--r--trunk/main/minimime/mimeparser.y748
-rw-r--r--trunk/main/minimime/mimeparser.yy.c2627
-rw-r--r--trunk/main/minimime/minimime.c244
-rw-r--r--trunk/main/minimime/mm.h367
-rw-r--r--trunk/main/minimime/mm_base64.c210
-rw-r--r--trunk/main/minimime/mm_codecs.c250
-rw-r--r--trunk/main/minimime/mm_contenttype.c759
-rw-r--r--trunk/main/minimime/mm_context.c614
-rw-r--r--trunk/main/minimime/mm_envelope.c271
-rw-r--r--trunk/main/minimime/mm_error.c123
-rw-r--r--trunk/main/minimime/mm_header.c213
-rw-r--r--trunk/main/minimime/mm_init.c65
-rw-r--r--trunk/main/minimime/mm_internal.h65
-rw-r--r--trunk/main/minimime/mm_mem.c170
-rw-r--r--trunk/main/minimime/mm_mem.h32
-rw-r--r--trunk/main/minimime/mm_mimepart.c659
-rw-r--r--trunk/main/minimime/mm_mimeutil.c137
-rw-r--r--trunk/main/minimime/mm_param.c225
-rw-r--r--trunk/main/minimime/mm_parse.c168
-rw-r--r--trunk/main/minimime/mm_queue.h508
-rw-r--r--trunk/main/minimime/mm_util.c412
-rw-r--r--trunk/main/minimime/mm_util.h50
-rw-r--r--trunk/main/minimime/mm_warnings.c99
-rw-r--r--trunk/main/minimime/strlcat.c70
-rw-r--r--trunk/main/minimime/strlcpy.c66
-rw-r--r--trunk/main/minimime/sys/mm_queue.h503
-rwxr-xr-xtrunk/main/minimime/test.sh54
-rw-r--r--trunk/main/minimime/tests/Makefile18
-rw-r--r--trunk/main/minimime/tests/create.c105
-rw-r--r--trunk/main/minimime/tests/messages/test1.txt50
-rw-r--r--trunk/main/minimime/tests/messages/test2.txt50
-rw-r--r--trunk/main/minimime/tests/messages/test3.txt12
-rw-r--r--trunk/main/minimime/tests/messages/test4.txt168
-rw-r--r--trunk/main/minimime/tests/messages/test5.txt44
-rw-r--r--trunk/main/minimime/tests/messages/test6.txt12
-rw-r--r--trunk/main/minimime/tests/messages/test7.txt64
-rw-r--r--trunk/main/minimime/tests/parse.c230
-rw-r--r--trunk/main/netsock.c208
-rw-r--r--trunk/main/pbx.c7800
-rw-r--r--trunk/main/plc.c248
-rw-r--r--trunk/main/poll.c306
-rw-r--r--trunk/main/privacy.c112
-rw-r--r--trunk/main/rtp.c4093
-rw-r--r--trunk/main/say.c7461
-rw-r--r--trunk/main/sched.c406
-rw-r--r--trunk/main/sha1.c337
-rw-r--r--trunk/main/slinfactory.c165
-rw-r--r--trunk/main/srv.c238
-rw-r--r--trunk/main/stdtime/Makefile29
-rw-r--r--trunk/main/stdtime/localtime.c1820
-rw-r--r--trunk/main/stdtime/private.h358
-rw-r--r--trunk/main/stdtime/test.c21
-rw-r--r--trunk/main/stdtime/tzfile.h184
-rw-r--r--trunk/main/strcompat.c463
-rw-r--r--trunk/main/tcptls.c452
-rw-r--r--trunk/main/tdd.c338
-rw-r--r--trunk/main/term.c297
-rw-r--r--trunk/main/threadstorage.c239
-rw-r--r--trunk/main/translate.c904
-rw-r--r--trunk/main/udptl.c1273
-rw-r--r--trunk/main/ulaw.c246
-rw-r--r--trunk/main/utils.c1557
-rw-r--r--trunk/makeopts.in227
-rwxr-xr-xtrunk/missing198
-rwxr-xr-xtrunk/mkinstalldirs40
-rw-r--r--trunk/pbx/Makefile32
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest10/extensions.ael131
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest12/extensions.ael13
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/extensions.ael7
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/qq.ael6
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t1/a.ael4
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t1/b.ael6
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t1/c.ael13
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t2/d.ael4
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t2/e.ael6
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t2/f.ael9
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t3/g.ael4
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t3/h.ael6
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t3/i.ael4
-rw-r--r--trunk/pbx/ael/ael-test/ael-ntest22/t3/j.ael6
-rwxr-xr-xtrunk/pbx/ael/ael-test/ael-ntest9/extensions.ael12
-rw-r--r--trunk/pbx/ael/ael-test/ael-test1/extensions.ael163
-rw-r--r--trunk/pbx/ael/ael-test/ael-test11/extensions.ael57
-rw-r--r--trunk/pbx/ael/ael-test/ael-test14/extensions.ael20
-rw-r--r--trunk/pbx/ael/ael-test/ael-test15/extensions.ael17
-rw-r--r--trunk/pbx/ael/ael-test/ael-test16/extensions.ael4
-rw-r--r--trunk/pbx/ael/ael-test/ael-test18/extensions.ael40
-rw-r--r--trunk/pbx/ael/ael-test/ael-test19/extensions.ael377
-rw-r--r--trunk/pbx/ael/ael-test/ael-test2/apptest.ael2146
-rw-r--r--trunk/pbx/ael/ael-test/ael-test2/extensions.ael8
-rw-r--r--trunk/pbx/ael/ael-test/ael-test20/extensions.ael8
-rwxr-xr-xtrunk/pbx/ael/ael-test/ael-test3/extensions.ael3184
-rw-r--r--trunk/pbx/ael/ael-test/ael-test3/include1.ael23
-rw-r--r--trunk/pbx/ael/ael-test/ael-test3/include2.ael24
-rw-r--r--trunk/pbx/ael/ael-test/ael-test3/include3.ael22
-rw-r--r--trunk/pbx/ael/ael-test/ael-test3/include4.ael22
-rw-r--r--trunk/pbx/ael/ael-test/ael-test3/include5.ael21
-rwxr-xr-xtrunk/pbx/ael/ael-test/ael-test3/telemarket_torture.ael2812
-rw-r--r--trunk/pbx/ael/ael-test/ael-test4/apptest.ael2146
-rw-r--r--trunk/pbx/ael/ael-test/ael-test4/extensions.ael8
-rw-r--r--trunk/pbx/ael/ael-test/ael-test5/extensions.ael838
-rw-r--r--trunk/pbx/ael/ael-test/ael-test6/extensions.ael833
-rw-r--r--trunk/pbx/ael/ael-test/ael-test7/extensions.ael460
-rw-r--r--trunk/pbx/ael/ael-test/ael-test8/extensions.ael27
-rwxr-xr-xtrunk/pbx/ael/ael-test/ael-vtest13/extensions.ael3183
-rw-r--r--trunk/pbx/ael/ael-test/ael-vtest13/include1.ael23
-rw-r--r--trunk/pbx/ael/ael-test/ael-vtest13/include2.ael24
-rw-r--r--trunk/pbx/ael/ael-test/ael-vtest13/include3.ael22
-rw-r--r--trunk/pbx/ael/ael-test/ael-vtest13/include4.ael22
-rw-r--r--trunk/pbx/ael/ael-test/ael-vtest13/include5.ael21
-rwxr-xr-xtrunk/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2812
-rw-r--r--trunk/pbx/ael/ael-test/ael-vtest17/extensions.ael116
-rw-r--r--trunk/pbx/ael/ael-test/ael-vtest21/extensions.ael14
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-ntest10171
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-ntest1230
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-ntest2254
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-ntest923
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test118
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test1113
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test1411
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test1512
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test1612
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test1811
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test1918
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test228
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test2011
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test399
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test428
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test514
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test624
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test719
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-test811
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-vtest133030
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-vtest1769
-rw-r--r--trunk/pbx/ael/ael-test/ref.ael-vtest219
-rwxr-xr-xtrunk/pbx/ael/ael-test/runtests56
-rwxr-xr-xtrunk/pbx/ael/ael-test/setref7
-rw-r--r--trunk/pbx/dundi-parser.c846
-rw-r--r--trunk/pbx/dundi-parser.h88
-rw-r--r--trunk/pbx/pbx_ael.c313
-rw-r--r--trunk/pbx/pbx_config.c1668
-rw-r--r--trunk/pbx/pbx_dundi.c4847
-rw-r--r--trunk/pbx/pbx_gtkconsole.c502
-rw-r--r--trunk/pbx/pbx_loopback.c176
-rw-r--r--trunk/pbx/pbx_lua.c1354
-rw-r--r--trunk/pbx/pbx_realtime.c251
-rw-r--r--trunk/pbx/pbx_spool.c509
-rw-r--r--trunk/phoneprov/000000000000-directory.xml6
-rw-r--r--trunk/phoneprov/000000000000-phone.cfg1
-rw-r--r--trunk/phoneprov/000000000000.cfg2
-rw-r--r--trunk/phoneprov/polycom.xml37
-rw-r--r--trunk/redhat/asterisk.spec137
-rw-r--r--trunk/redhat/rpmmacros3
-rw-r--r--trunk/redhat/rpmrc3
-rw-r--r--trunk/res/Makefile53
-rw-r--r--trunk/res/ael/ael.flex694
-rw-r--r--trunk/res/ael/ael.tab.c3413
-rw-r--r--trunk/res/ael/ael.tab.h154
-rw-r--r--trunk/res/ael/ael.y823
-rw-r--r--trunk/res/ael/ael_lex.c3127
-rw-r--r--trunk/res/ael/pval.c5435
-rw-r--r--trunk/res/res_adsi.c1120
-rw-r--r--trunk/res/res_ael_share.c54
-rw-r--r--trunk/res/res_agi.c2992
-rw-r--r--trunk/res/res_clioriginate.c191
-rw-r--r--trunk/res/res_config_curl.c503
-rw-r--r--trunk/res/res_config_odbc.c739
-rw-r--r--trunk/res/res_config_pgsql.c1008
-rw-r--r--trunk/res/res_config_sqlite.c1534
-rw-r--r--trunk/res/res_convert.c160
-rw-r--r--trunk/res/res_crypto.c624
-rw-r--r--trunk/res/res_features.c3444
-rw-r--r--trunk/res/res_indications.c419
-rw-r--r--trunk/res/res_jabber.c3015
-rw-r--r--trunk/res/res_limit.c216
-rw-r--r--trunk/res/res_monitor.c765
-rw-r--r--trunk/res/res_musiconhold.c1566
-rw-r--r--trunk/res/res_odbc.c778
-rw-r--r--trunk/res/res_phoneprov.c1033
-rw-r--r--trunk/res/res_realtime.c129
-rw-r--r--trunk/res/res_smdi.c746
-rw-r--r--trunk/res/res_snmp.c121
-rw-r--r--trunk/res/res_speech.c344
-rw-r--r--trunk/res/snmp/agent.c842
-rw-r--r--trunk/res/snmp/agent.h40
-rw-r--r--trunk/sample.call80
-rw-r--r--trunk/sounds/Makefile157
-rw-r--r--trunk/sounds/sounds.xml68
-rw-r--r--trunk/static-http/ajamdemo.html219
-rw-r--r--trunk/static-http/astman.css34
-rw-r--r--trunk/static-http/astman.js262
-rw-r--r--trunk/static-http/prototype.js1781
-rw-r--r--trunk/tests/Makefile20
-rw-r--r--trunk/tests/test_skel.c54
-rw-r--r--trunk/utils/Makefile177
-rw-r--r--trunk/utils/ael_main.c584
-rw-r--r--trunk/utils/astcanary.c84
-rw-r--r--trunk/utils/astman.1102
-rw-r--r--trunk/utils/astman.c752
-rwxr-xr-xtrunk/utils/build-extensions-conf.lua81
-rw-r--r--trunk/utils/check_expr.c459
-rw-r--r--trunk/utils/clicompat.c16
-rw-r--r--trunk/utils/conf2ael.c692
-rw-r--r--trunk/utils/expr2.testinput126
-rw-r--r--trunk/utils/extconf.c6211
-rw-r--r--trunk/utils/frame.c1034
-rw-r--r--trunk/utils/frame.h300
-rw-r--r--trunk/utils/hashtest.c364
-rw-r--r--trunk/utils/hashtest2.c376
-rw-r--r--trunk/utils/muted.c698
-rw-r--r--trunk/utils/smsq.c771
-rw-r--r--trunk/utils/stereorize.c159
-rw-r--r--trunk/utils/streamplayer.c121
1092 files changed, 554646 insertions, 0 deletions
diff --git a/trunk/.cleancount b/trunk/.cleancount
new file mode 100644
index 000000000..bb95160cb
--- /dev/null
+++ b/trunk/.cleancount
@@ -0,0 +1 @@
+33
diff --git a/trunk/BUGS b/trunk/BUGS
new file mode 100644
index 000000000..96b55e655
--- /dev/null
+++ b/trunk/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/trunk/CHANGES b/trunk/CHANGES
new file mode 100644
index 000000000..00757c4b1
--- /dev/null
+++ b/trunk/CHANGES
@@ -0,0 +1,496 @@
+------------------------------------------------------------------------------
+--- Functionality changes since Asterisk 1.4-beta was branched ----------------
+-------------------------------------------------------------------------------
+
+AMI - The manager (TCP/TLS/HTTP)
+--------------------------------
+ * Manager has undergone a lot of changes, all of them documented
+ in doc/manager_1_1.txt
+ * Manager version has changed to 1.1
+ * Added a new action 'CoreShowChannels' to list currently defined channels
+ and some information about them.
+ * Added a new action 'SIPshowregistry' to list SIP registrations.
+ * Added TLS support for the manager interface and HTTP server
+ * Added the URI redirect option for the built-in HTTP server
+ * The output of CallerID in Manager events is now more consistent.
+ CallerIDNum is used for number and CallerIDName for name.
+ * Enable https support for builtin web server.
+ See configs/http.conf.sample for details.
+ * Added a new action, GetConfigJSON, which can return the contents of an
+ Asterisk configuration file in JSON format. This is intended to help
+ improve the performance of AJAX applications using the manager interface
+ over HTTP.
+ * SIP and IAX manager events now use "ChannelType" in all cases where we
+ indicate channel driver. Previously, we used a mixture of "Channel"
+ and "ChannelDriver" headers.
+ * Added a "Bridge" action which allows you to bridge any two channels that
+ are currently active on the system.
+ * Added a "ListAllVoicemailUsers" action that allows you to get a list of all
+ the voicemail users setup.
+ * Added 'DBDel' and 'DBDelTree' manager commands.
+ * cdr_manager now reports events via the "cdr" level, separating it from
+ the very verbose "call" level.
+ * Manager users are now stored in memory. If you change the manager account
+ list (delete or add accounts) you need to reload manager.
+ * Added Masquerade manager event for when a masquerade happens between
+ two channels.
+ * Added "manager reload" command for the CLI
+ * Lots of commands that only provided information are now allowed under the
+ Reporting privilege, instead of only under Call or System.
+ * The IAX* commands now require either System or Reporting privilege, to
+ mirror the privileges of the SIP* commands.
+
+Dialplan functions
+------------------
+ * Added the DEVICE_STATE() dialplan function which allows retrieving any device
+ state in the dialplan, as well as creating custom device states that are
+ controllable from the dialplan.
+ * Extend CALLERID() function with "pres" and "ton" parameters to
+ fetch string representation of calling number presentation indicator
+ and numeric representation of type of calling number value.
+ * MailboxExists converted to dialplan function
+ * A new option to Dial() for telling IP phones not to count the call
+ as "missed" when dial times out and cancels.
+ * Added LOCK(), TRYLOCK(), and UNLOCK(), which provide a single level dialplan
+ mutex. No deadlocks are possible, as LOCK() only allows a single lock to be
+ held for any given channel. Also, locks are automatically freed when a
+ channel is hung up.
+ * Added HINT() dialplan function that allows retrieving hint information.
+ Hints are mappings between extensions and devices for the sake of
+ determining the state of an extension. This function can retrieve the list
+ of devices or the name associated with a hint.
+ * Added EXTENSION_STATE() dialplan function which allows retrieving the state
+ of any extension.
+ * Added SYSINFO() dialplan function which allows retrieval of system information
+ * Added a new dialplan function, DIALPLAN_EXISTS(), which allows you to check for
+ the existence of a dialplan target.
+ * Added two new dialplan functions, TOUPPER and TOLOWER, which convert a string to
+ upper and lower case, respectively.
+
+CLI Changes
+-----------
+ * New CLI command "core show hint" (usage: core show hint <exten>)
+ * New CLI command "core show settings"
+ * Added 'core show channels count' CLI command.
+ * Added the ability to set the core debug and verbose values on a per-file basis.
+ * Added 'queue pause member' and 'queue unpause member' CLI commands
+ * Ability to set process limits ("ulimit") without restarting Asterisk
+ * Enhanced "agi debug" to print the channel name as a prefix to the debug
+ output to make debugging on busy systems much easier.
+ * New CLI commands "dialplan set extenpatternmatching true/false"
+ * New CLI command: "core set chanvar" to set a channel variable from the CLI.
+ * Added an easy way to execute Asterisk CLI commands at startup. Any commands
+ listed in the startup_commands file in the Asterisk configuration directory
+ will get executed.
+
+SIP changes
+-----------
+ * Improved NAT and STUN support.
+ chan_sip now can use port numbers in bindaddr, externip and externhost
+ options, as well as contact a STUN server to detect its external address
+ for the SIP socket. See sip.conf.sample, 'NAT' section.
+ * The default SIP useragent= identifier now includes the Asterisk version
+ * A new option, match_auth_username in sip.conf changes the matching of incoming requests.
+ If set, and the incoming request carries authentication info,
+ the username to match in the users list is taken from the Digest header
+ rather than from the From: field. This feature is considered experimental.
+ * The "musiconhold" and "musicclass" settings in sip.conf are now removed,
+ since they where replaced by "mohsuggest" and "mohinterpret" in version 1.4
+ * The "localmask" setting was removed in version 1.2 and the reminder about it
+ being removed is now also removed.
+ * A new option "busylevel" for setting a level of calls where asterisk reports
+ a device as busy, to separate it from call-limit. This value is also added
+ to the SIP_PEER dialplan function.
+ * A new realtime family called "sipregs" is now supported to store SIP registration
+ data. If this family is defined, "sippeers" will be used for configuration and
+ "sipregs" for registrations. If it's not defined, "sippeers" will be used for
+ registration data, as before.
+ * The SIPPEER function have new options for port address, call and pickup groups
+ * Added support for T.140 realtime text in SIP/RTP
+ * The "checkmwi" option has been removed from sip.conf, as it is no longer
+ required due to the restructuring of how MWI is handled. See the descriptions
+ in this file of the "pollmailboxes" and "pollfreq" options to voicemail.conf
+ for more information.
+ * Added rtpdest option to CHANNEL() dialplan function.
+ * Added SIPREFERRINGCONTEXT and SIPREFERREDBYHDR variables which are set when a transfer takes place.
+ * SIP now adds a header to the CANCEL if the call was answered by another phone
+ in the same dial command, or if the new c option in dial() is used.
+ * The new default is that 100 Trying is not sent on REGISTER attempts as the RFC specifically
+ states it is not needed. For phones, however, that do require it the "registertrying" option
+ has been added so it can be enabled.
+ * A new option called "callcounter" (global/peer/user level) enables call counters needed
+ for better status reports needed for queues and SIP subscriptions. (Call-Limit was previously
+ used to enable this functionality).
+ * New settings for timer T1 and timer B on a global level or per device. This makes it
+ possible to force timeout faster on non-responsive SIP servers. These settings are
+ considered advanced, so don't use them unless you have a problem.
+ * Added a dial string option to be able to set the To: header in an INVITE to any
+ SIP uri.
+ * Added a new global and per-peer option, qualifyfreq, which allows you to configure
+ the qualify frequency.
+ * Added SIP Session Timers support (RFC 4028). This prevents stuck SIP sessions that
+ were not properly torn down due to network or endpoint failures during an established
+ SIP session.
+ * Added TCP and TLS support for SIP. See doc/siptls.txt and configs/sip.conf.sample for
+ more information on how it is used.
+
+IAX2 changes
+------------
+ * Added the trunkmaxsize configuration option to chan_iax2.
+ * Added the srvlookup option to iax.conf
+ * Added support for OSP. The token is set and retrieved through the CHANNEL()
+ dialplan function.
+
+XMPP Google Talk/Jingle changes
+-------------------------------
+ * Added the bindaddr option to gtalk.conf.
+
+Skinny changes
+-------------
+ * Added skinny show device, skinny show line, and skinny show settings CLI commands.
+ * Proper codec support in chan_skinny.
+ * Added settings for IP and Ethernet QoS requests
+
+MGCP changes
+------------
+ * Added separate settings for media QoS in mgcp.conf
+
+Console Channel Driver changes
+-------------------
+ * Added experimental support for video send & receive to chan_oss.
+ This requires SDL and ffmpeg/avcodec, plus Video4Linux or X11 to act as
+ a video source.
+
+Phone channel changes (chan_phone)
+----------------------------------
+ * Added G729 passthrough support to chan_phone for Sigma Designs boards.
+
+H.323 channel Changes
+---------------------
+ * H323 remote hold notification support added (by NOTIFY message
+ and/or H.450 supplementary service)
+
+Local channel changes
+---------------------
+ * The device state functionality in the Local channel driver has been updated
+ to indicate INUSE or NOT_INUSE when a Local channel is being used as opposed
+ to just UNKNOWN if the extension exists.
+ * Added jitterbuffer support for chan_local. This allows you to use the
+ generic jitterbuffer on incoming calls going to Asterisk applications.
+ For example, this would allow you to use a jitterbuffer for an incoming
+ SIP call to Voicemail by putting a Local channel in the middle. This
+ feature is enabled by using the 'j' option in the Dial string to the Local
+ channel in conjunction with the existing 'n' option for local channels.
+
+Zaptel channel driver (chan_zap) Changes
+----------------------------------------
+ * SS7 support in chan_zap (via libss7 library)
+ * In India, some carriers transmit CID via dtmf. Some code has been added
+ that will handle some situations. The cidstart=polarity_IN choice has been added for
+ those carriers that transmit CID via dtmf after a polarity change.
+ * CID matching information is now shown when doing 'dialplan show'.
+ * Added zap show version CLI command to chan_zap.
+ * Added setvar support to zapata.conf channel entries.
+ * Added two new options: mwimonitor and mwimonitornotify. These options allow
+ you to enable MWI monitoring on FXO lines. When the MWI state changes,
+ the script specified in the mwimonitornotify option is executed. An internal
+ event indicating the new state of the mailbox is also generated, so that
+ the normal MWI facilities in Asterisk work as usual.
+ * Added signalling type 'auto', which attempts to use the same signalling type
+ for a channel as configured in Zaptel. This is primarily designed for analog
+ ports, but will also work for digital ports that are configured for FXS or FXO
+ signalling types. This mode is also the default now, so if your zapata.conf
+ does not specify signalling for a channel (which is unlikely as the sample
+ configuration file has always recommended specifying it for every channel) then
+ the 'auto' mode will be used for that channel if possible.
+ * Added a 'zap set dnd' command to allow CLI control of the Do-Not-Disturb
+ state for a channel; also ensured that the DNDState Manager event is
+ emitted no matter how the DND state is set or cleared.
+
+New Channel Drivers
+-------------------
+ * Added a new channel driver, chan_unistim. See doc/unistim.txt and
+ configs/unistim.conf.sample for details. This new channel driver allows
+ you to use Nortel i2002, i2004, and i2050 phones with Asterisk.
+ * Added a new channel driver, chan_console, which uses portaudio as a cross
+ platform audio interface. It was written as a channel driver that would
+ work with Mac CoreAudio, but portaudio supports a number of other audio
+ interfaces, as well. Note that this channel driver requires v19 or higher
+ of portaudio; older versions have a different API.
+
+DUNDi changes
+-------------
+ * Added the ability to specify arguments to the Dial application when using
+ the DUNDi switch in the dialplan.
+ * Added the ability to set weights for responses dynamically. This can be
+ done using a global variable or a dialplan function. Using the SHELL()
+ function would allow you to have an external script set the weight for
+ each response.
+ * Added two new dialplan functions, DUNDIQUERY and DUNDIRESULT. These
+ functions will allow you to initiate a DUNDi query from the dialplan,
+ find out how many results there are, and access each one.
+
+ENUM changes
+------------
+ * Added two new dialplan functions, ENUMQUERY and ENUMRESULT. These
+ functions will allow you to initiate an ENUM lookup from the dialplan,
+ and Asterisk will cache the results. ENUMRESULT can be used to access
+ the results without doing multiple DNS queries.
+
+Voicemail Changes
+-----------------
+ * Added the ability to customize which sound files are used for some of the
+ prompts within the Voicemail application by changing them in voicemail.conf
+ * Added the ability for the "voicemail show users" CLI command to show users
+ configured by the dynamic realtime configuration method.
+ * MWI (Message Waiting Indication) handling has been significantly
+ restructured internally to Asterisk. It is now totally event based
+ instead of polling based. The voicemail application will notify other
+ modules that have subscribed to MWI events when something in the mailbox
+ changes.
+ This also means that if any other entity outside of Asterisk is changing
+ the contents of mailboxes, then the voicemail application still needs to
+ poll for changes. Examples of situations that would require this option
+ are web interfaces to voicemail or an email client in the case of using
+ IMAP storage. So, two new options have been added to voicemail.conf
+ to account for this: "pollmailboxes" and "pollfreq". See the sample
+ configuration file for details.
+ * Added "tw" language support
+ * Added support for storage of greetings using an IMAP server
+ * Added ability to customize forward, reverse, stop, and pause keys for message playback
+ * SMDI is now enabled in voicemail using the smdienable option.
+ * A "lockmode" option has been added to asterisk.conf to configure the file
+ locking method used for voicemail, and potentially other things in the
+ future. The default is the old behavior, lockfile. However, there is a
+ new method, "flock", that uses a different method for situations where the
+ lockfile will not work, such as on SMB/CIFS mounts.
+ * Added the ability to backup deleted messages, to ease recovery in the case
+ that a user accidentally deletes a message, and discovers that they need it.
+
+Queue changes
+-------------
+ * Added the general option 'shared_lastcall' so that member's wrapuptime may be
+ used across multiple queues.
+ * Added QUEUE_VARIABLES function to set queue variables added setqueuevar and
+ setqueueentryvar options for each queue, see queues.conf.sample for details.
+ * Added keepstats option to queues.conf which will keep queue
+ statistics during a reload.
+ * setinterfacevar option in queues.conf also now sets a variable
+ called MEMBERNAME which contains the member's name.
+ * Added 'Strategy' field to manager event QueueParams which represents
+ the queue strategy in use.
+ * Added option to run macro when a queue member is connected to a caller,
+ see queues.conf.sample for details.
+ * app_queue now has a 'loose' option which is almost exactly like 'strict' except it
+ does not count paused queue members as unavailable.
+ * Added min-announce-frequency option to queues.conf which allows you to control the
+ minimum amount of time between queue announcements for use when the caller's queue
+ position changes frequently.
+ * Added additional information to EXITWITHTIMEOUT and EXITWITHKEY events in the
+ queue log.
+ * Added ability for non-realtime queues to have realtime members
+ * Added the "linear" strategy to queues.
+ * Added the "wrandom" strategy to queues.
+ * Added new channel variable QUEUE_MIN_PENALTY
+ * QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY may be adjusted in mid-call by defining
+ rules in queuerules.conf. See configs/queuerules.conf.sample for details
+ * Added a new parameter for member definition, called state_interface. This may be
+ used so that a member may be called via one interface but have a different interface's
+ device state reported.
+
+MeetMe Changes
+--------------
+ * The 'o' option to provide an optimization has been removed and its functionality
+ has been enabled by default.
+ * When a conference is created, the UNIQUEID of the channel that caused it to be
+ created is stored. Then, every channel that joins the conference will have the
+ MEETMEUNIQUEID channel variable set with this ID. This can be used to relate
+ callers that come and go from long standing conferences.
+ * Added a new application, MeetMeChannelAdmin, which is similar to MeetMeAdmin,
+ except it does operations on a channel by name, instead of number in a conference.
+ This is a very useful feature in combination with the 'X' option to ChanSpy.
+ * Added 'C' option to Meetme which causes a caller to continue in the dialplan
+ when kicked out.
+ * Added new RealTime functionality to provide support for scheduled conferencing.
+ This includes optional messages to the caller if they attempt to join before
+ the schedule start time, or to allow the caller to join the conference early.
+ Also included is optional support for limiting the number of callers per
+ RealTime conference.
+ * Added the S() and L() options to the MeetMe application. These are pretty
+ much identical to the S() and L() options to Dial(). They let you set
+ timeouts for the conference, as well as have warning sounds played to
+ let the caller know how much time is left, and when it is running out.
+ * Added the ability to do "meetme concise" with the "meetme" CLI command.
+ This extends the concise capabilities of this CLI command to include
+ listing all conferences, instead of an addition to the other sub commands
+ for the "meetme" command.
+ * Added the ability to specify the music on hold class used to play into the
+ conference when there is only one member and the M option is used.
+
+Other Dialplan Application Changes
+----------------------------------
+ * Argument support for Gosub application
+ * From the to-do lists: straighten out the app timeout args:
+ Wait() app now really does 0.3 seconds- was truncating arg to an int.
+ WaitExten() same as Wait().
+ Congestion() - Now takes floating pt. argument.
+ Busy() - now takes floating pt. argument.
+ Read() - timeout now can be floating pt.
+ WaitForRing() now takes floating pt timeout arg.
+ SpeechBackground() -- clarified in the docstrings that the timeout is an integer seconds.
+ * Added 's' option to Page application.
+ * Added 'E' and 'V' commands to ExternalIVR.
+ * Added 'o' and 'X' options to Chanspy.
+ * Added a new dialplan application, Bridge, which allows you to bridge the
+ calling channel to any other active channel on the system.
+ * Added the ability to specify a music on hold class to play instead of ringing
+ for the SLATrunk application.
+ * The Read application no longer exits the dialplan on error. Instead, it sets
+ READSTATUS to ERROR, which you can catch and handle separately.
+ * Added 'm' option to Directory, which lists out names, 8 at a time, instead
+ of asking for verification of each name, one at a time.
+ * Privacy() no longer uses privacy.conf, as all options are specifyable as
+ direct options to the app.
+ * AMD() has a new "maximum word length" option. "show application AMD" from the CLI
+ for more details
+
+Music On Hold Changes
+---------------------
+ * A new option, "digit", has been added for music on hold classes in
+ musiconhold.conf. If this is set for a music on hold class, a caller
+ listening to music on hold can press this digit to switch to listening
+ to this music on hold class.
+ * Support for realtime music on hold has been added.
+ * In conjunction with the realtime music on hold, a general section has
+ been added to musiconhold.conf, its sole variable is cachertclasses. If this
+ is set, then music on hold classes found in realtime will be cached in memory.
+
+AEL Changes
+-----------
+ * AEL upgraded to use the Gosub with Arguments instead
+ of Macro application, to hopefully reduce the problems
+ seen with the artificially low stack ceiling that
+ Macro bumps into. Macros can only call other Macros
+ to a depth of 7. Tests run using gosub, show depths
+ limited only by virtual memory. A small test demonstrated
+ recursive call depths of 100,000 without problems.
+ -- in addition to this, all apps that allowed a macro
+ to be called, as in Dial, queues, etc, are now allowing
+ a gosub call in similar fashion.
+ * AEL now generates LOCAL(argname) declarations when it
+ Set()'s the each arg name to the value of ${ARG1}, ${ARG2),
+ etc. That makes the arguments local in scope. The user
+ can define their own local variables in macros, now,
+ by saying "local myvar=someval;" or using Set() in this
+ fashion: Set(LOCAL(myvar)=someval); ("local" is now
+ an AEL keyword).
+ * utils/conf2ael introduced. Will convert an extensions.conf
+ file into extensions.ael. Very crude and unfinished, but
+ will be improved as time goes by. Should be useful for a
+ first pass at conversion.
+ * aelparse will now read extensions.conf to see if a referenced
+ macro or context is there before issueing a warning.
+
+Call Features (res_features) Changes
+------------------------------------
+ * Added the parkedcalltransfers option to features.conf
+ * The built-in method for doing attended transfers has been updated to
+ include some new options that allow you to have the transferee sent
+ back to the person that did the transfer if the transfer is not successful.
+ See the options "atxferdropcall", "atxferloopdelay", and "atxfercallbackretries"
+ in features.conf.sample.
+ * Added support for configuring named groups of custom call features in
+ features.conf. This means that features can be written a single time, and
+ then mapped into groups of features for different key mappings or easier
+ access control.
+ * Updated the ParkedCall application to allow you to not specify a parking
+ extension. If you don't specify a parking space to pick up, it will grab
+ the first one available.
+
+Language Support Changes
+------------------------
+ * Brazilian Portuguese (pt-BR) in VM, and say.c was added
+ * Added support for the Hungarian language for saying numbers, dates, and times.
+
+AGI Changes
+-----------
+ * Added SPEECH commands for speech recognition. A complete listing can be found
+ using agi show.
+
+Logger changes
+--------------
+ * Added rotatestrategy option to logger.conf, along with two new options:
+ "timestamp" which will use the time to name the logger files instead of
+ sequence number; and "rotate", which rotates the names of the logfiles,
+ similar to the way syslog rotates files.
+ * Added exec_after_rotate option to logger.conf, which allows a system
+ command to be run after rotation. This is primarily useful with
+ rotatestrategry=rotate, to allow a limit on the number of logfiles kept
+ and to ensure that the oldest log file gets deleted.
+ * Added realtime support for the queue log
+
+Miscellaneous New Modules
+-------------------------
+ * Added a new CDR module, cdr_sqlite3_custom.
+ * Added a new realtime configuration module, res_config_sqlite
+ * Added a new codec translation module, codec_resample, which re-samples
+ signed linear audio between 8 kHz and 16 kHz to help support wideband
+ codecs.
+ * Added a new module, res_phoneprov, which allows auto-provisioning of phones
+ based on configuration templates that use Asterisk dialplan function and
+ variable substitution. It should be possible to create phone profiles and
+ templates that work for the majority of phones provisioned over http. It
+ is currently only intended to provision a single user account per phone.
+ An example profile and set of templates for Polycom phones is provided.
+ NOTE: Polycom firmware is not included, but should be placed in
+ AST_DATA_DIR/phoneprov/configs to match up with the included templates.
+ * Added a new module, app_jack, which provides interfaces to JACK, the Jack
+ Audio Connection Kit (http://www.jackaudio.org/). Two interfaces are
+ provided; there is a JACK() application, and a JACK_HOOK() function. Both
+ interfaces create an input and output JACK port. The application makes
+ these ports the endpoint of the call. The audio coming from the channel
+ goes out the output port and whatever comes back in on the input port is
+ what gets sent to the channel. The JACK_HOOK() function turns on a JACK
+ audiohook on the channel. This lets you run the audio coming from a
+ channel through JACK, and whatever comes back in is what gets forwarded
+ on as the channel's audio. This is very useful for building custom
+ vocoders or doing recording or analysis of the channel's audio in another
+ application.
+ * Added a new module, res_config_curl, which permits using a HTTP POST url
+ to retrieve, create, update, and delete realtime information from a remote
+ web server. Note that this module requires func_curl.so to be loaded for
+ backend functionality.
+
+Miscellaneous
+-------------
+ * Ability to use libcap to set high ToS bits when non-root
+ on Linux. If configure is unable to find libcap then you
+ can use --with-cap to specify the path.
+ * Added maxfiles option to options section of asterisk.conf which allows you to specify
+ what Asterisk should set as the maximum number of open files when it loads.
+ * Added the jittertargetextra configuration option.
+ * The cdr_manager module has a [mappings] feature, like cdr_custom,
+ to add fields to the manager event from the CDR variables.
+ * Added support for setting the CoS for VLAN traffic (802.1p). See the sample
+ configuration files for the IP channel drivers. The new option is "cos".
+ This information is also documented in doc/qos.tex, or the IP Quality of Service
+ section of asterisk.pdf.
+ * When originating a call using AMI or pbx_spool that fails the reason for failure
+ will now be available in the failed extension using the REASON dialplan variable.
+ * Added support for reading the TOUCH_MONITOR_PREFIX channel variable.
+ It allows you to configure a prefix for auto-monitor recordings.
+ * Added support for writing and running your dialplan in lua. See
+ configs/extensions.lua.sample for examples of how to do this.
+ * A new extension pattern matching algorithm, based on a trie, is introduced
+ here, that could noticeably speed up mid-sized to large dialplans.
+ It is NOT used by default, as duplicating the behaviour of the old pattern
+ matcher is still under development. A config file option, in extensions.conf,
+ in the [general] section, called "extenpatternmatchingnew", is by default
+ set to false; setting that to true will force the use of the new algorithm.
+ Also, the cli commands "dialplan set extenpatternmatchingnew true/false" can
+ be used to switch the algorithms at run time.
+ * A new option when starting a remote asterisk (rasterisk, asterisk -r) for
+ specifying which socket to use to connect to the running Asterisk daemon
+ (-s)
+ * Added logging to 'make update' command. See update.log
+
diff --git a/trunk/COPYING b/trunk/COPYING
new file mode 100644
index 000000000..aa2ebac66
--- /dev/null
+++ b/trunk/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/trunk/CREDITS b/trunk/CREDITS
new file mode 100644
index 000000000..a7a432d69
--- /dev/null
+++ b/trunk/CREDITS
@@ -0,0 +1,244 @@
+
+=== 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
+
+John Todd, TalkPlus, Inc. and JR Richardson, Ntegrated Solutions. - for funding
+ the development of SIP Session Timers support.
+
+=== 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 release manager and countless enhancements and bug
+ fixes.
+ russell(AT)digium.com
+
+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(AT)yahoo.com http://www.asterlink.com
+
+James Golovich - Innumerable contributions, including SIP TCP and TLS support.
+ You can find him and asterisk-perl at http://asterisk.gnuinter.net
+
+Andre Bierwirth - Extension hints and status
+
+Jean-Denis Girard - Various contributions from the South Pacific Islands
+ jd-girard(AT)esoft.pf http://www.esoft.pf
+
+William Jordan / Vonage - MySQL enhancements to Voicemail
+ wjordan(AT)vonage.com
+
+Jac Kersing - Various fixes
+
+Steven Critchfield - Seek and Trunc functions for playback and recording
+ critch(AT)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(AT)oa.com.au
+
+James Dennis - Cisco SIP compatibility patches to work with SIP service
+ providers. Can be contacted at asterisk(AT)jdennis.net
+
+Tilghman Lesher - ast_localtime(); ast_say_date_with_format();
+ GotoIfTime, SayUnixTime, HasNewVoicemail applications;
+ CUT, SORT, EVAL, CURL, FIELDQTY, STRFTIME, some QUEUE* functions;
+ func_odbc, cdr_adaptive_odbc, and other innumerable bug fixes.
+ tilghman(AT)digium.com http://asterisk.drunkcoder.com/
+
+Jayson Vantuyl - Manager protocol changes, various other bugs.
+ jvantuyl(AT)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(AT)sigmasoft.com
+
+Josh Roberson - chan_zap reload support, Advanced Voicemail Features, & other
+ misc. patches. - josh(AT)asteriasgi.com, http://www.asteriasgi.com
+
+William Waites - syslog support, SIP NAT traversal for SIP-UA. ww(AT)styx.org
+
+Rich Murphey - Porting to FreeBSD, NetBSD, OpenBSD, and Darwin.
+ rich(AT)whiteoaklabs.com http://whiteoaklabs.com
+
+Simon Lockhart - Porting to Solaris (based on work of Logan ???)
+ simon(AT)slimey.org
+
+Olle E. Johansson - SIP RFC compliance, documentation and testing, testing,
+ testing; MiniVM - the small voicemail system, many documentation
+ updates/corrections, and many bug fixes.
+ oej(AT)edvina.net, http://edvina.net
+
+Steve Kann - new jitter buffer for IAX2
+ stevek(AT)stevek.com
+
+Constantine Filin - major contributions to the Asterisk Realtime Architecture
+
+Steve Murphy - privacy support, $[ ] parser upgrade, AEL2 parser upgrade.
+ murf(AT)digium.com
+
+Claude Patry - bug fixes, feature enhancements, and bug marshalling
+ cpatry(AT)gmail.com
+
+Miroslav Nachev, miro(AT)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(AT)securax.be
+
+Roy Sigurd Karlsbakk - providing funding for generic jitterbuffer development
+ roy(AT)karlsbakk.net, Briiz Telecom AS
+
+Voop AS, Nuvio Inc, Inotel S.A and Foniris Telecom A/S - funding for rewrite
+ of SIP transfers
+
+Philippe Sultan - RADIUS CDR module, many fixes to res_jabber and gtalk/jingle
+ channel drivers.
+ INRIA, http://www.inria.fr/
+
+John Martin, Aupix - Improved video support in the SIP channel
+ T.140 text support in RTP/SIP
+
+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(AT)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(AT)gdspartners.com
+
+Bartosz Supczinski - Support for Polish added by DIR (www.dir.pl)
+ Bartosz.Supczinski(AT)dir.pl
+
+James Rothenberger - Support for IMAP storage integration added by
+ OneBizTone LLC Work funded by University of Pennsylvania jar(AT)onebiztone.com
+
+Paul Cadach - Bringing chan_h323 up to date, bug fixes, and more!
+
+Voop AS - Financial support for a lot of work with the SIP driver and the IAX
+ trunk MTU patch
+
+Cedric Hans - Development of chan_unistim
+ cedric.hans(AT)mlkj.net
+
+Sergio Fadda - console_video: video support for chan_oss and chan_alsa
+
+Marta Carbone - console_video and the astobj2 framework
+
+Luigi Rizzo - astobj2, console_video, windows build, chan_oss cleanup,
+ and a bunch of infrastructure work (loader, new_cli, ...)
+
+Brett Bryant - digit option for musiconhold selection, ENUMQUERY and ENUMRESULT functions,
+ feature group configuration for features.conf, per-file CLI debug and verbose settings,
+ TCP and TLS support for SIP, and various bug fixes.
+ brettbryant(AT)gmail.com
+
+=== 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(AT)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/trunk/LICENSE b/trunk/LICENSE
new file mode 100644
index 000000000..39e0bb8ae
--- /dev/null
+++ b/trunk/LICENSE
@@ -0,0 +1,69 @@
+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 and
+OpenH323 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/misery.digium.com/6000 (via IAX2)
+licensing@digium.com (via email)
+
+Digium, Inc.
+445 Jan Davis Drive
+Huntsville, AL 35806
+USA
diff --git a/trunk/Makefile b/trunk/Makefile
new file mode 100644
index 000000000..5b8951809
--- /dev/null
+++ b/trunk/Makefile
@@ -0,0 +1,834 @@
+#
+# 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)
+# 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
+#
+# Initial values for ASTCFLAGS and ASTLDFLAGS can be specified in the
+# environment when running make, as follows:
+#
+# $ ASTCFLAGS="-Werror" make ...
+#
+# note that this is different from
+#
+# $ make ASTCFLAGS="-Werror" ...
+#
+# If you need to pass compiler/linker flags as 'make' variables, please use
+#
+# $ make COPTS="..." LDOPTS="..." ...
+#
+#
+# You can add the path of local module subdirs from the command line with
+# make LOCAL_MOD_SUBDIRS= ....
+
+export ASTTOPDIR # Top level dir, used in subdirs' Makefiles
+export ASTERISKVERSION
+export ASTERISKVERSIONNUM
+
+#--- values used for default paths
+
+# DESTDIR is the staging (or final) directory where files are copied
+# during the install process. Define it before 'export', otherwise
+# export will set it to the empty string making ?= fail.
+# WARNING: do not put spaces or comments after the value.
+DESTDIR?=$(INSTALL_PATH)
+export DESTDIR
+
+export INSTALL_PATH # Additional prefix for the following paths
+export ASTETCDIR # Path for config files
+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 OSARCH # Operating system
+export PROC # Processor type
+
+export NOISY_BUILD # Used in Makefile.rules
+export MENUSELECT_CFLAGS # Options selected in menuselect.
+export AST_DEVMODE # Set to "yes" for additional compiler
+ # and runtime checks
+
+export SOLINK # linker flags for shared objects
+export STATIC_BUILD # Additional cflags, set to -static
+ # for static builds. Probably
+ # should go directly to ASTLDFLAGS
+
+#--- paths to various commands
+export CC
+export CXX
+export AR
+export RANLIB
+export HOST_CC
+export INSTALL
+export STRIP
+export DOWNLOAD
+export AWK
+export GREP
+export ID
+
+# 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
+
+ASTTOPDIR:=$(CURDIR)
+
+# Overwite config files on "make samples"
+OVERWRITE=y
+
+# Include debug and macro symbols in the executables (-g) and profiling info (-pg)
+DEBUG=-g3
+
+
+# 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
+ ASTDBDIR=$(ASTVARLIBDIR)
+ ASTKEYDIR=$(ASTVARLIBDIR)
+ 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
+ ASTDBDIR=$(localstatedir)/db/asterisk
+else
+ ASTVARLIBDIR=$(localstatedir)/lib/asterisk
+ ASTDBDIR=$(ASTVARLIBDIR)
+endif
+ ASTKEYDIR=$(ASTVARLIBDIR)
+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
+
+# Create OPTIONS variable, but probably we can assign directly to ASTCFLAGS
+OPTIONS=
+
+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)
+
+ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/autoconfig.h
+
+ifeq ($(AST_DEVMODE),yes)
+ ASTCFLAGS+=-Werror -Wunused -Wundef $(AST_DECLARATION_AFTER_STATEMENT)
+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)
+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 -D_XPG4_2
+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)
+ RPMVERSION:=$(shell sed 's/[-\/:]/_/g' .version)
+else
+ RPMVERSION=unknown
+endif
+
+ifneq ($(wildcard .svn),)
+ ASTERISKVERSIONNUM:=999999
+endif
+
+# XXX MALLOC_DEBUG is probably unused, Makefile.moddir_rules adds the
+# value directly to ASTCFLAGS
+ASTCFLAGS+=$(MALLOC_DEBUG)$(OPTIONS)
+
+MOD_SUBDIRS:=channels pbx apps codecs formats cdr funcs tests main res $(LOCAL_MOD_SUBDIRS)
+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__
+ SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
+else
+# These are used for all but Darwin
+ SOLINK=-shared -Xlinker -x
+ ifneq ($(findstring BSD,$(OSARCH)),)
+ LDFLAGS+=-L/usr/local/lib
+ endif
+endif
+
+ifeq ($(OSARCH),SunOS)
+ SOLINK=-shared -fpic -L/usr/local/ssl/lib
+endif
+
+# comment to print directories during submakes
+#PRINT_DIR=yes
+
+SILENTMAKE:=$(MAKE) --quiet --no-print-directory
+ifneq ($(PRINT_DIR)$(NOISY_BUILD),)
+SUBMAKE:=$(MAKE) --quiet
+else
+SUBMAKE:=$(MAKE) --quiet --no-print-directory
+endif
+
+# This is used when generating the doxygen documentation
+ifneq ($(DOT),:)
+ HAVEDOT=yes
+else
+ HAVEDOT=no
+endif
+
+# $(MAKE) is printed in several places, and we want it to be a
+# fixed size string. Define a variable whose name has also the
+# same size, so we can easily align text.
+ifeq ($(MAKE), gmake)
+ mK="gmake"
+else
+ mK=" make"
+endif
+
+all: _all
+ @echo " +--------- Asterisk Build Complete ---------+"
+ @echo " + Asterisk has successfully been built, and +"
+ @echo " + can be installed by running: +"
+ @echo " + +"
+ @echo " + $(mK) install +"
+ @echo " +-------------------------------------------+"
+
+_all: cleantest makeopts $(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 $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts
+
+$(MOD_SUBDIRS_EMBED_LDSCRIPT):
+ @echo "EMBED_LDSCRIPTS+="`$(SILENTMAKE) -C $(@:-embed-ldscript=) SUBDIR=$(@:-embed-ldscript=) __embed_ldscript` >> makeopts.embed_rules
+
+$(MOD_SUBDIRS_EMBED_LDFLAGS):
+ @echo "EMBED_LDFLAGS+="`$(SILENTMAKE) -C $(@:-embed-ldflags=) SUBDIR=$(@:-embed-ldflags=) __embed_ldflags` >> makeopts.embed_rules
+
+$(MOD_SUBDIRS_EMBED_LIBS):
+ @echo "EMBED_LIBS+="`$(SILENTMAKE) -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) $(PRINT_DIR) $(MOD_SUBDIRS_EMBED_LDSCRIPT)
+ @$(MAKE) $(PRINT_DIR) $(MOD_SUBDIRS_EMBED_LDFLAGS)
+ @$(MAKE) $(PRINT_DIR) $(MOD_SUBDIRS_EMBED_LIBS)
+
+$(SUBDIRS): main/version.c include/asterisk/build.h include/asterisk/buildopts.h defaults.h makeopts.embed_rules
+
+ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ # Non-windows:
+ # 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))
+else
+ # Windows: we need to build main (i.e. the asterisk dll) first,
+ # followed by res, followed by the other directories, because
+ # dll symbols must be resolved during linking and not at runtime.
+D1:= $(filter-out main,$(MOD_SUBDIRS))
+D1:= $(filter-out res,$(D1))
+
+$(D1): res
+res: main
+endif
+
+$(MOD_SUBDIRS):
+ @ASTCFLAGS="$(MOD_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(MAKE) $(PRINT_DIR) --no-builtin-rules -C $@ SUBDIR=$@ all
+
+$(OTHER_SUBDIRS):
+ @ASTCFLAGS="$(OTHER_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(MAKE) $(PRINT_DIR) --no-builtin-rules -C $@ SUBDIR=$@ all
+
+defaults.h: makeopts
+ @build_tools/make_defaults_h > $@.tmp
+ @cmp -s $@.tmp $@ || mv $@.tmp $@
+ @rm -f $@.tmp
+
+main/version.c:
+ @build_tools/make_version_c > $@.tmp
+ @cmp -s $@.tmp $@ || mv $@.tmp $@
+ @rm -f $@.tmp
+
+include/asterisk/buildopts.h: menuselect.makeopts
+ @build_tools/make_buildopts_h > $@.tmp
+ @cmp -s $@.tmp $@ || mv $@.tmp $@
+ @rm -f $@.tmp
+
+include/asterisk/build.h:
+ @build_tools/make_build_h > $@.tmp
+ @cmp -s $@.tmp $@ || mv $@.tmp $@
+ @rm -f $@.tmp
+
+$(SUBDIRS_CLEAN):
+ @$(MAKE) $(PRINT_DIR) -C $(@:-clean=) clean
+
+$(SUBDIRS_DIST_CLEAN):
+ @$(MAKE) $(PRINT_DIR) -C $(@:-dist-clean=) dist-clean
+
+clean: $(SUBDIRS_CLEAN)
+ rm -f defaults.h
+ rm -f include/asterisk/build.h
+ rm -f main/version.c
+ @$(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
+ 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)/phoneprov
+ for x in phoneprov/*; do \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/phoneprov ; \
+ done
+ mkdir -p $(DESTDIR)$(ASTDATADIR)/static-http
+ for x in static-http/*; do \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/static-http ; \
+ done
+ if [ -d doc/tex/asterisk ] ; then \
+ mkdir -p $(DESTDIR)$(ASTDATADIR)/static-http/docs ; \
+ for n in doc/tex/asterisk/* ; do \
+ $(INSTALL) -m 644 $$n $(DESTDIR)$(ASTDATADIR)/static-http/docs ; \
+ done \
+ fi
+ 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..." ; \
+ fromrev="`svn info | $(AWK) '/Revision: / {print $$2}'`"; \
+ svn update | tee update.out; \
+ torev="`svn info | $(AWK) '/Revision: / {print $$2}'`"; \
+ echo "`date` Updated from revision $${fromrev} to $${torev}." >> update.log; \
+ 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)))
+
+bininstall: _all
+ 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
+ $(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) --quiet $(PRINT_DIR) -C $(@:-install=) install
+
+NEWMODS:=$(foreach d,$(MOD_SUBDIRS),$(notdir $(wildcard $(d)/*.so)))
+OLDMODS=$(filter-out $(NEWMODS),$(notdir $(wildcard $(DESTDIR)$(MODULES_DIR)/*.so)))
+
+oldmodcheck:
+ @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
+
+install: datafiles bininstall $(SUBDIRS_INSTALL)
+ @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 " + +"
+ @echo " + $(mK) samples +"
+ @echo " + +"
+ @echo " +----------------- or ---------------------+"
+ @echo " + +"
+ @echo " + You can go ahead and install the asterisk +"
+ @echo " + program documentation now or later run: +"
+ @echo " + +"
+ @echo " + $(mK) progdocs +"
+ @echo " + +"
+ @echo " + **Note** This requires that you have +"
+ @echo " + doxygen installed on your local system +"
+ @echo " +-------------------------------------------+"
+ @$(MAKE) -s oldmodcheck
+
+upgrade: bininstall
+
+# XXX why *.adsi is installed first ?
+adsi:
+ @echo Installing adsi config files...
+ @mkdir -p $(DESTDIR)$(ASTETCDIR)
+ @for x in configs/*.adsi; do \
+ dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
+ if [ -f $${dst} ] ; then \
+ echo "Overwriting $$x" ; \
+ else \
+ echo "Installing $$x" ; \
+ fi ; \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x` ; \
+ done
+
+samples: adsi
+ @echo Installing other config files...
+ @mkdir -p $(DESTDIR)$(ASTETCDIR)
+ @for x in configs/*.sample; do \
+ dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample`" ; \
+ if [ -f $${dst} ]; then \
+ if [ "$(OVERWRITE)" = "y" ]; then \
+ if cmp -s $${dst} $$x ; then \
+ echo "Config file $$x is unchanged"; \
+ continue; \
+ fi ; \
+ mv -f $${dst} $${dst}.old ; \
+ else \
+ echo "Skipping config file $$x"; \
+ continue; \
+ fi ;\
+ fi ; \
+ echo "Installing file $$x"; \
+ $(INSTALL) -m 644 $$x $${dst} ;\
+ done
+ @if [ "$(OVERWRITE)" = "y" ] || [ ! -f $(DESTDIR)$(ASTCONFPATH) ]; then \
+ echo "Creating asterisk.conf"; \
+ ( \
+ echo "[directories](!) ; remove the (!) to enable this" ; \
+ echo "astetcdir => $(ASTETCDIR)" ; \
+ echo "astmoddir => $(MODULES_DIR)" ; \
+ echo "astvarlibdir => $(ASTVARLIBDIR)" ; \
+ echo "astdbdir => $(ASTDBDIR)" ; \
+ echo "astkeydir => $(ASTKEYDIR)" ; \
+ echo "astdatadir => $(ASTDATADIR)" ; \
+ echo "astagidir => $(AGI_DIR)" ; \
+ echo "astspooldir => $(ASTSPOOLDIR)" ; \
+ echo "astrundir => $(ASTVARRUNDIR)" ; \
+ echo "astlogdir => $(ASTLOGDIR)" ; \
+ echo "" ; \
+ echo ";[options]" ; \
+ 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 ";languageprefix = yes ; Use the new sound prefix path syntax" ; \
+ echo ";internal_timing = yes" ; \
+ echo ";systemname = my_system_name ; prefix uniqueid with a system name for global uniqueness issues" ; \
+ echo ";autosystemname = yes ; automatically set systemname to hostname - uses 'localhost' on failure, or systemname if set" ; \
+ 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 ";maxfiles = 1000 ; Maximum amount of openfiles" ; \
+ echo ";minmemfree = 1 ; in MBs, Asterisk stops accepting new calls if the amount of free memory falls below this watermark" ; \
+ 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 ";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 "" ; \
+ 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 " +-------------------------------------------+"
+
+spec:
+ sed "s/^Version:.*/Version: $(RPMVERSION)/g" redhat/asterisk.spec > asterisk.spec ; \
+
+rpm: __rpm
+
+__rpm: main/version.c include/asterisk/buildopts.h spec
+ rm -rf /tmp/asterisk ; \
+ mkdir -p /tmp/asterisk/redhat/RPMS/i386 ; \
+ $(MAKE) DESTDIR=/tmp/asterisk install ; \
+ $(MAKE) DESTDIR=/tmp/asterisk samples ; \
+ mkdir -p /tmp/asterisk/etc/rc.d/init.d ; \
+ cp -f contrib/init.d/rc.redhat.asterisk /tmp/asterisk/etc/rc.d/init.d/asterisk ; \
+ rpmbuild --rcfile /usr/lib/rpm/rpmrc:redhat/rpmrc -bb asterisk.spec
+
+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) $(PRINT_DIR) -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 " + +"
+ @echo " + $(mK) uninstall-all +"
+ @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 $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.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 $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!"
+
+# options for make in menuselect/
+MAKE_MENUSELECT=CC="$(HOST_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect CONFIGURE_SILENT="--silent"
+
+menuselect/menuselect: menuselect/makeopts
+ $(MAKE_MENUSELECT)
+
+menuselect/gmenuselect: menuselect/makeopts
+ $(MAKE_MENUSELECT) gmenuselect
+
+menuselect/makeopts:
+ $(MAKE_MENUSELECT) makeopts
+
+menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc)) build_tools/cflags.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 >> $@
+ @cat build_tools/embed_modules.xml >> $@
+ @cat sounds/sounds.xml >> $@
+ @echo "</menu>" >> $@
+
+pdf: asterisk.pdf
+asterisk.pdf:
+ $(MAKE) -C doc/tex asterisk.pdf
+
+.PHONY: menuselect main sounds clean dist-clean distclean all prereqs cleantest uninstall _uninstall uninstall-all pdf dont-optimize $(SUBDIRS_INSTALL) $(SUBDIRS_DIST_CLEAN) $(SUBDIRS_CLEAN) $(SUBDIRS_UNINSTALL) $(SUBDIRS) $(MOD_SUBDIRS_EMBED_LDSCRIPT) $(MOD_SUBDIRS_EMBED_LDFLAGS) $(MOD_SUBDIRS_EMBED_LIBS) main/version.c
diff --git a/trunk/Makefile.moddir_rules b/trunk/Makefile.moddir_rules
new file mode 100644
index 000000000..9258e8d63
--- /dev/null
+++ b/trunk/Makefile.moddir_rules
@@ -0,0 +1,166 @@
+#
+# 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
+#
+
+# Makefile rules for building modules.
+
+# In most cases, we set target-specific variables for certain targets
+# (remember that they apply recursively to prerequisites).
+# Also note that we can only set one variable per rule, so we have to
+# repeat the left hand side to set multiple variables.
+
+ifneq ($(findstring MALLOC_DEBUG,$(MENUSELECT_CFLAGS)),)
+ ifeq ($(findstring astmm.h,$(ASTCFLAGS)),)
+ ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/astmm.h
+ endif
+endif
+
+ifeq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ ASTCFLAGS+=${GC_CFLAGS}
+endif
+
+ifneq ($(findstring STATIC_BUILD,$(MENUSELECT_CFLAGS)),)
+ STATIC_BUILD=-static
+endif
+
+include $(ASTTOPDIR)/Makefile.rules
+
+# If MODULE_PREFIX is defined, use it to run the standard functions to set
+# C_MODS, CC_MODS, LOADABLE_MODS and EMBEDDED_MODS.
+# Each word of MODULE_PREFIX is a prefix for filenames that we consider
+# valid C or CC modules (eg. app, func ...). Note that the underscore
+# is added here, and does not need to be in MODULE_PREFIX
+#
+# Use MODULE_EXCLUDE to specify additional modules to exclude.
+
+ifneq ($(MODULE_PREFIX),)
+ ALL_C_MODS:=
+ ALL_C_MODS+=$(foreach p,$(MODULE_PREFIX),$(patsubst %.c,%,$(wildcard $(p)_*.c)))
+ ALL_CC_MODS:=
+ ALL_CC_MODS+=$(foreach p,$(MODULE_PREFIX),$(patsubst %.cc,%,$(wildcard $(p)_*.cc)))
+
+ C_MODS:=$(filter-out $(MENUSELECT_$(MENUSELECT_CATEGORY)),$(ALL_C_MODS))
+ CC_MODS:=$(filter-out $(MENUSELECT_$(MENUSELECT_CATEGORY)),$(ALL_CC_MODS))
+
+ # and store in the list of embedded or loadable modules
+ ifneq ($(findstring $(MENUSELECT_CATEGORY),$(MENUSELECT_EMBED)),)
+ EMBEDDED_MODS:=$(C_MODS) $(CC_MODS)
+ else
+ LOADABLE_MODS:=$(C_MODS) $(CC_MODS)
+ endif
+endif
+
+# Both C++ and C++ sources need their module name in AST_MODULE
+# We also pass whatever _INCLUDE list is generated by menuselect
+# (they are stored in file 'makeopts')
+
+$(addsuffix .oo,$(CC_MODS)) $(addsuffix .o,$(C_MODS)): \
+ ASTCFLAGS+= -DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE))
+
+ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ # don't define -fPIC on mingw32 and cygwin, it is the default
+ $(LOADABLE_MODS:%=%.so): ASTCFLAGS+=-fPIC
+endif
+
+# For loadable modules, pass _LIB and _LDFLAGS from menuselect.
+$(LOADABLE_MODS:%=%.so): LIBS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LIB))
+$(LOADABLE_MODS:%=%.so): ASTLDFLAGS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LDFLAGS))
+
+$(EMBEDDED_MODS:%=%.o): ASTCFLAGS+=-DEMBEDDED_MODULE=$*
+
+$(addsuffix .so,$(filter $(LOADABLE_MODS),$(C_MODS))): %.so: %.o
+$(addsuffix .so,$(filter $(LOADABLE_MODS),$(CC_MODS))): %.so: %.oo
+
+modules.link: $(addsuffix .eo,$(filter $(EMBEDDED_MODS),$(C_MODS)))
+
+.PHONY: clean uninstall _all moduleinfo makeopts
+
+ifneq ($(LOADABLE_MODS),)
+_all: $(LOADABLE_MODS:%=%.so)
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ # linker options and extra libraries for cygwin
+ SOLINK=-Wl,--out-implib=lib$@.a -shared
+ LIBS+=-L$(ASTTOPDIR)/main -lasterisk -L$(ASTTOPDIR)/res $($@_LIBS)
+ # additional libraries in res/
+endif
+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 %.eo,$^)); do echo "INPUT (../$${file})" >> $@; done
+ @for file in $(patsubst %,$(SUBDIR)/%,$(filter-out %.eo,$^)); do echo "INPUT (../$${file})" >> $@; done
+
+clean::
+ rm -f *.so *.o *.oo *.eo
+ rm -f .*.o.d .*.oo.d
+ rm -f *.s *.i
+ rm -f modules.link
+
+install:: all
+ @echo "Installing modules from `basename $(CURDIR)`..."
+ @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/trunk/Makefile.rules b/trunk/Makefile.rules
new file mode 100644
index 000000000..ca21dcc04
--- /dev/null
+++ b/trunk/Makefile.rules
@@ -0,0 +1,98 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile rules
+#
+# 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
+#
+
+# Rules for various build phases.
+# 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
+
+# extra cflags to build dependencies. Recursively expanded.
+MAKE_DEPS= -MMD -MT $@ -MF .$(subst /,_,$@).d -MP
+
+ifeq ($(NOISY_BUILD),)
+ ECHO_PREFIX=@
+ CMD_PREFIX=@
+else
+ ECHO_PREFIX=@\#
+ CMD_PREFIX=
+endif
+
+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
+
+ OPTIMIZE?=-O6
+ ASTCFLAGS+=$(OPTIMIZE)
+endif
+
+# build rules for various targets
+%.o: %.c
+ $(ECHO_PREFIX) echo " [CC] $< -> $@"
+ $(CMD_PREFIX) $(CC) -o $@ -c $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
+
+%.o: %.i
+ $(ECHO_PREFIX) echo " [CCi] $< -> $@"
+ $(CMD_PREFIX) $(CC) -o $@ -c $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
+
+%.i: %.c
+ $(ECHO_PREFIX) echo " [CPP] $< -> $@"
+ $(CMD_PREFIX) $(CC) -o $@ -E $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
+
+%.o: %.s
+ $(ECHO_PREFIX) echo " [AS] $< -> $@"
+ $(CMD_PREFIX) $(CC) -o $@ -c $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
+
+%.oo: %.cc
+ $(ECHO_PREFIX) echo " [CXX] $< -> $@"
+ $(CMD_PREFIX) $(CXX) -o $@ -c $< $(PTHREAD_CFLAGS) $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations,$(ASTCFLAGS)) $(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 $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $^ $(PTHREAD_LIBS) $(LIBS)
+
+%.so: %.oo
+ $(ECHO_PREFIX) echo " [LDXX] $^ -> $@"
+ $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $^ $(PTHREAD_LIBS) $(LIBS)
+
+%.eo: %.o
+ $(ECHO_PREFIX) echo " [EMBED] $< -> $@"
+ $(CMD_PREFIX) $(ASTTOPDIR)/build_tools/make_linker_eo_script $* > .$@.ld
+ $(CMD_PREFIX) $(LD) -r -T .$@.ld -o $@ $<
+ $(CMD_PREFIX) rm -f .$@.ld
+
+%.eo: %.oo
+ $(ECHO_PREFIX) echo " [EMBED] $< -> $@"
+ $(CMD_PREFIX) $(ASTTOPDIR)/build_tools/make_linker_eo_script $* > .$@.ld
+ $(CMD_PREFIX) $(LD) -r -T .$@.ld -o $@ $<
+ $(CMD_PREFIX) rm -f .$@.ld
+
+%: %.o
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $^ $(PTHREAD_LIBS) $(LIBS)
+
+dist-clean::
diff --git a/trunk/README b/trunk/README
new file mode 100644
index 000000000..176bfca44
--- /dev/null
+++ b/trunk/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-2006 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 zapata.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/trunk/UPGRADE.txt b/trunk/UPGRADE.txt
new file mode 100644
index 000000000..dcde18981
--- /dev/null
+++ b/trunk/UPGRADE.txt
@@ -0,0 +1,163 @@
+Information for Upgrading From Previous Asterisk Releases
+=========================================================
+
+AEL:
+
+* Macros are now implemented underneath with the Gosub() application.
+ Heaven Help You if you wrote code depending on any aspect of this!
+ Previous to 1.6, macros were implemented with the Macro() app, which
+ provided a nice feature of auto-returning. The compiler will do its
+ best to insert a Return() app call at the end of your macro if you did
+ not include it, but really, you should make sure that all execution
+ paths within your macros end in "return;".
+
+* The conf2ael program is 'introduced' in this release; it is in a rather
+ crude state, but deemed useful for making a first pass at converting
+ extensions.conf code into AEL. More intelligence will come with time.
+
+Core:
+
+* The 'languageprefix' option in asterisk.conf is now deprecated, and
+ the default sound file layout for non-English sounds is the 'new
+ style' layout introduced in Asterisk 1.4 (and used by the automatic
+ sound file installer in the Makefile).
+
+* The ast_expr2 stuff has been modified to handle floating-point numbers.
+ Numbers of the format D.D are now acceptable input for the expr parser,
+ Where D is a string of base-10 digits. All math is now done in "long double",
+ if it is available on your compiler/architecture. This was half-way between
+ a bug-fix (because the MATH func returns fp by default), and an enhancement.
+ Also, for those counting on, or needing, integer operations, a series of
+ 'functions' were also added to the expr language, to allow several styles
+ of rounding/truncation, along with a set of common floating point operations,
+ like sin, cos, tan, log, pow, etc. The ability to call external functions
+ like CDR(), etc. was also added, without having to use the ${...} notation.
+
+* The delimiter passed to applications has been changed to the comma (','), as
+ that is what people are used to using within extensions.conf. If you are
+ using realtime extensions, you will need to translate your existing dialplan
+ to use this separator. To use a literal comma, you need merely to escape it
+ with a backslash ('\'). Another possible side effect is that you may need to
+ remove the obscene level of backslashing that was necessary for the dialplan
+ to work correctly in 1.4 and previous versions. This should make writing
+ dialplans less painful in the future, albeit with the pain of a one-time
+ conversion.
+
+* The logger.conf option 'rotatetimestamp' has been deprecated in favor of
+ 'rotatestrategy'. This new option supports a 'rotate' strategy that more
+ closely mimics the system logger in terms of file rotation.
+
+* The concise versions of various CLI commands are now deprecated. We recommend
+ using the manager interface (AMI) for application integration with Asterisk.
+
+Voicemail:
+
+* The voicemail configuration values 'maxmessage' and 'minmessage' have
+ been changed to 'maxsecs' and 'minsecs' to clarify their purpose and
+ to make them more distinguishable from 'maxmsgs', which sets folder
+ size. The old variables will continue to work in this version, albeit
+ with a deprecation warning.
+* If you use any interface for modifying voicemail aside from the built in
+ dialplan applications, then the option "pollmailboxes" *must* be set in
+ voicemail.conf for message waiting indication (MWI) to work properly. This
+ is because Voicemail notification is now event based instead of polling
+ based. The channel drivers are no longer responsible for constantly manually
+ checking mailboxes for changes so that they can send MWI information to users.
+ Examples of situations that would require this option are web interfaces to
+ voicemail or an email client in the case of using IMAP storage.
+
+Applications:
+
+* ChanIsAvail() now has a 't' option, which allows the specified device
+ to be queried for state without consulting the channel drivers. This
+ performs mostly a 'ChanExists' sort of function.
+* SetCallerPres() has been replaced with the CALLERPRES() dialplan function
+ and is now deprecated.
+* DISA()'s fifth argument is now an options argument. If you have previously
+ used 'NOANSWER' in this argument, you'll need to convert that to the new
+ option 'n'.
+* Macro() is now deprecated. If you need subroutines, you should use the
+ Gosub()/Return() applications. To replace MacroExclusive(), we have
+ introduced dialplan functions LOCK(), TRYLOCK(), and UNLOCK(). You may use
+ these functions in any location where you desire to ensure that only one
+ channel is executing that path at any one time.
+* Read() now sets a READSTATUS variable on exit. It does NOT automatically
+ return -1 (and hangup) anymore on error. If you want to hangup on error,
+ you need to do so explicitly in your dialplan.
+* Privacy() no longer uses privacy.conf, so any options must be specified
+ directly in the application arguments.
+
+Dialplan Functions:
+
+* QUEUE_MEMBER_COUNT() has been deprecated in favor of the QUEUE_MEMBER() function. For
+ more information, issue a "show function QUEUE_MEMBER" from the CLI.
+
+CDR:
+
+* The cdr_sqlite module has been marked as deprecated in favor of
+ cdr_sqlite3_custom. It will potentially be removed from the tree
+ after Asterisk 1.6 is released.
+
+* The cdr_odbc module now uses res_odbc to manage its connections. The
+ username and password parameters in cdr_odbc.conf, therefore, are no
+ longer used. The dsn parameter now points to an entry in res_odbc.conf.
+
+Formats:
+
+* format_wav: The GAIN preprocessor definition and source code that used it
+ is removed. This change was made in response to user complaints of
+ choppiness or the clipping of loud signal peaks. To increase the volume
+ of voicemail messages, use the 'volgain' option in voicemail.conf
+
+Channel Drivers:
+
+* SIP: a small upgrade to support the "Record" button on the SNOM360,
+ which sends a sip INFO message with a "Record: on" or "Record: off"
+ header. If Asterisk is set up (via features.conf) to accept "One Touch Monitor"
+ requests (by default, via '*1'), then the user-configured dialpad sequence
+ is generated, and recording can be started and stopped via this button. The
+ file names and formats are all controlled via the normal mechanisms. If the
+ user has not configured the automon feature, the normal "415 Unsupported media type"
+ is returned, and nothing is done.
+* SIP: The "call-limit" option is marked as deprecated. It still works in this version of
+ Asterisk, but will be removed in the following version. Please use the groupcount functions
+ in the dialplan to enforce call limits. The "limitonpeer" configuration option is
+ now renamed to "counteronpeer".
+* SIP: The "username" option is now renamed to "defaultuser" to match "defaultip".
+ These are used only before registration to call a peer with the uri
+ sip:defaultuser@defaultip
+ The "username" setting still work, but is deprecated and will not work in
+ the next version of Asterisk.
+
+* chan_local.c: the comma delimiter inside the channel name has been changed to a
+ semicolon, in order to make the Local channel driver compatible with the comma
+ delimiter change in applications.
+* H323: The "tos" setting has changed name to "tos_audio" and "cos" to "cos_audio"
+ to be compatible with settings in sip.conf. The "tos" and "cos" configuration
+ is deprecated and will stop working in the next release of Asterisk.
+
+* Console: A new console channel driver, chan_console, has been added to Asterisk.
+ This new module can not be loaded at the same time as chan_alsa or chan_oss. The
+ default modules.conf only loads one of them (chan_oss by default). So, unless you
+ have modified your modules.conf to not use the autoload option, then you will need
+ to modify modules.conf to add another "noload" line to ensure that only one of
+ these three modules gets loaded.
+
+Configuration:
+
+* pbx_dundi.c: tos parameter changed to use new values. Old values like lowdelay,
+ lowcost and other is not acceptable now. Look into qos.tex for description of
+ this parameter.
+
+Manager:
+
+* Manager has been upgraded to version 1.1 with a lot of changes.
+ Please check doc/manager_1_1.txt for information
+
+* The IAXpeers command output has been changed to more closely resemble the
+ output of the SIPpeers command.
+
+* cdr_manager now reports at the "cdr" level, not at "call" You may need to
+ change your manager.conf to add the level to existing AMI users, if they
+ want to see the CDR events generated.
+
diff --git a/trunk/acinclude.m4 b/trunk/acinclude.m4
new file mode 100644
index 000000000..9b84af614
--- /dev/null
+++ b/trunk/acinclude.m4
@@ -0,0 +1,1106 @@
+# Various support functions for configure.ac in asterisk
+#
+
+# Helper function to check for gcc attributes.
+# AST_GCC_ATTRIBUTE([attribute name])
+
+AC_DEFUN([AST_GCC_ATTRIBUTE],
+[
+AC_MSG_CHECKING(for compiler 'attribute $1' support)
+AC_COMPILE_IFELSE(
+ AC_LANG_PROGRAM([static int __attribute__(($1)) test(void) {}],
+ []),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE_UNQUOTED([HAVE_ATTRIBUTE_$1], 1, [Define to 1 if your GCC C compiler supports the '$1' attribute.]),
+ AC_MSG_RESULT(no))
+])
+
+# Helper function to setup variables for a package.
+# $1 -> the package name. Used in configure.ac and also as a prefix
+# for the variables ($1_DIR, $1_INCLUDE, $1_LIB) in makeopts
+# $3 -> option name, used in --with-$3 or --without-$3 when calling configure.
+# $2 and $4 are just text describing the package (short and long form)
+
+# AST_EXT_LIB_SETUP([package], [short description], [configure option name], [long description])
+
+AC_DEFUN([AST_EXT_LIB_SETUP],
+[
+ $1_DESCRIP="$2"
+ $1_OPTION="$3"
+ AC_ARG_WITH([$3], AC_HELP_STRING([--with-$3=PATH],[use $2 files in PATH $4]),
+ [
+ case ${withval} in
+ n|no)
+ USE_$1=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} $1"
+ ;;
+ *)
+ $1_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} $1"
+ ;;
+ esac
+ ])
+ PBX_$1=0
+ AC_SUBST([$1_LIB])
+ AC_SUBST([$1_INCLUDE])
+ AC_SUBST([$1_DIR])
+ AC_SUBST([PBX_$1])
+])
+
+# Check whether any of the mandatory modules are not present, and
+# print error messages in case. The mandatory list is built using
+# --with-* arguments when invoking configure.
+
+AC_DEFUN([AST_CHECK_MANDATORY],
+[
+ AC_MSG_CHECKING([for mandatory modules: ${ac_mandatory_list}])
+ err=0;
+ for i in ${ac_mandatory_list}; do
+ eval "a=\${PBX_$i}"
+ if test "x${a}" = "x1" ; then continue; fi
+ if test ${err} = "0" ; then AC_MSG_RESULT(fail) ; fi
+ AC_MSG_RESULT()
+ eval "a=\${${i}_OPTION}"
+ AC_MSG_NOTICE([***])
+ AC_MSG_NOTICE([*** The $i installation appears to be missing or broken.])
+ AC_MSG_NOTICE([*** Either correct the installation, or run configure])
+ AC_MSG_NOTICE([*** including --without-${a}.])
+ err=1
+ done
+ if test $err = 1 ; then exit 1; fi
+ AC_MSG_RESULT(ok)
+])
+
+# The next three functions check for the availability of a given package.
+# AST_C_DEFINE_CHECK looks for the presence of a #define in a header file,
+# AST_C_COMPILE_CHECK can be used for testing for various items in header files,
+# AST_EXT_LIB_CHECK looks for a symbol in a given library, or at least
+# for the presence of a header file.
+# AST_EXT_TOOL_CHECK looks for a symbol in using $1-config to determine CFLAGS and LIBS
+#
+# They are only run if PBX_$1 != 1 (where $1 is the package),
+# so you can call them multiple times and stop at the first matching one.
+# On success, they both set PBX_$1 = 1, set $1_INCLUDE and $1_LIB as applicable,
+# and also #define HAVE_$1 1 and #define HAVE_$1_VERSION ${last_argument}
+# in autoconfig.h so you can tell which test succeeded.
+# They should be called after AST_EXT_LIB_SETUP($1, ...)
+
+# Check if a given macro is defined in a certain header.
+
+# AST_C_DEFINE_CHECK([package], [macro name], [header file], [version])
+AC_DEFUN([AST_C_DEFINE_CHECK],
+[
+ if test "x${PBX_$1}" != "x1" -a "${USE_$1}" != "no"; then
+ AC_MSG_CHECKING([for $2 in $3])
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${$1_DIR}" != "x"; then
+ $1_INCLUDE="-I${$1_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${$1_INCLUDE}"
+
+ AC_COMPILE_IFELSE(
+ [ AC_LANG_PROGRAM( [#include <$3>],
+ [#if defined($2)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+ ])],
+ [ AC_MSG_RESULT(yes)
+ PBX_$1=1
+ AC_DEFINE([HAVE_$1], 1, [Define if your system has the $1 headers.])
+ AC_DEFINE([HAVE_$1_VERSION], $4, [Define $1 headers version])
+ ],
+ [ AC_MSG_RESULT(no) ]
+ )
+ CPPFLAGS="${saved_cppflags}"
+ fi
+])
+
+
+# Check if a given expression will compile using a certain header.
+
+# AST_C_COMPILE_CHECK([package], [expression], [header file], [version])
+AC_DEFUN([AST_C_COMPILE_CHECK],
+[
+ if test "x${PBX_$1}" != "x1" -a "${USE_$1}" != "no"; then
+ AC_MSG_CHECKING([if "$2" compiles using $3])
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${$1_DIR}" != "x"; then
+ $1_INCLUDE="-I${$1_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${$1_INCLUDE}"
+
+ AC_COMPILE_IFELSE(
+ [ AC_LANG_PROGRAM( [#include <$3>],
+ [ $2; ]
+ )],
+ [ AC_MSG_RESULT(yes)
+ PBX_$1=1
+ AC_DEFINE([HAVE_$1], 1, [Define if your system has the $1 headers.])
+ AC_DEFINE([HAVE_$1_VERSION], $4, [Define $1 headers version])
+ ],
+ [ AC_MSG_RESULT(no) ]
+ )
+ CPPFLAGS="${saved_cppflags}"
+ fi
+])
+
+
+# Check for existence of a given package ($1), either looking up a function
+# in a library, or, if no function is supplied, only check for the
+# existence of the header files.
+
+# AST_EXT_LIB_CHECK([package], [library], [function], [header],
+# [extra libs], [extra cflags], [version])
+AC_DEFUN([AST_EXT_LIB_CHECK],
+[
+if test "x${PBX_$1}" != "x1" -a "${USE_$1}" != "no"; then
+ pbxlibdir=""
+ # if --with-$1=DIR has been specified, use it.
+ if test "x${$1_DIR}" != "x"; then
+ if test -d ${$1_DIR}/lib; then
+ pbxlibdir="-L${$1_DIR}/lib"
+ else
+ pbxlibdir="-L${$1_DIR}"
+ fi
+ fi
+ pbxfuncname="$3"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_$1_FOUND=yes
+ else
+ AC_CHECK_LIB([$2], [${pbxfuncname}], [AST_$1_FOUND=yes], [AST_$1_FOUND=no], ${pbxlibdir} $5)
+ fi
+
+ # now check for the header.
+ if test "${AST_$1_FOUND}" = "yes"; then
+ $1_LIB="${pbxlibdir} -l$2 $5"
+ # if --with-$1=DIR has been specified, use it.
+ if test "x${$1_DIR}" != "x"; then
+ $1_INCLUDE="-I${$1_DIR}/include"
+ fi
+ $1_INCLUDE="${$1_INCLUDE} $6"
+ if test "x$4" = "x" ; then # no header, assume found
+ $1_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${$1_INCLUDE} $6"
+ AC_CHECK_HEADER([$4], [$1_HEADER_FOUND=1], [$1_HEADER_FOUND=0])
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${$1_HEADER_FOUND}" = "x0" ; then
+ $1_LIB=""
+ $1_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ $1_LIB=""
+ fi
+ PBX_$1=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+ AC_DEFINE_UNQUOTED([HAVE_$1], 1, [Define this to indicate the ${$1_DESCRIP} library])
+ AC_DEFINE_UNQUOTED([HAVE_$1_VERSION], [$7], [Define to indicate the ${$1_DESCRIP} library version])
+ fi
+ fi
+fi
+])
+
+
+# Check for a package using $2-config. Similar to AST_EXT_LIB_CHECK,
+# but use $2-config to determine cflags and libraries to use.
+# $3 and $4 can be used to replace --cflags and --libs in the request
+
+# AST_EXT_TOOL_CHECK([package], [tool name], [--cflags], [--libs], [includes], [expression])
+AC_DEFUN([AST_EXT_TOOL_CHECK],
+[
+ if test "x${PBX_$1}" != "x1" -a "${USE_$1}" != "no"; then
+ PBX_$1=0
+ AC_CHECK_TOOL(CONFIG_$1, $2-config, No)
+ if test ! "x${CONFIG_$1}" = xNo; then
+ if test x"$3" = x ; then A=--cflags ; else A="$3" ; fi
+ $1_INCLUDE=$(${CONFIG_$1} $A)
+ if test x"$4" = x ; then A=--libs ; else A="$4" ; fi
+ $1_LIB=$(${CONFIG_$1} $A)
+ if test x"$5" != x ; then
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${$1_DIR}" != "x"; then
+ $1_INCLUDE="-I${$1_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${$1_INCLUDE}"
+
+ AC_COMPILE_IFELSE(
+ [ AC_LANG_PROGRAM( [ $5 ],
+ [ $6; ]
+ )],
+ [ PBX_$1=1
+ AC_DEFINE([HAVE_$1], 1, [Define if your system has the $1 headers.])
+ ],
+ []
+ )
+ CPPFLAGS="${saved_cppflags}"
+ else
+ PBX_$1=1
+ AC_DEFINE([HAVE_$1], 1, [Define if your system has the $1 libraries.])
+ fi
+ fi
+ fi
+])
+
+AC_DEFUN(
+[AST_CHECK_GNU_MAKE], [AC_CACHE_CHECK(for GNU make, GNU_MAKE,
+ GNU_MAKE='Not Found' ;
+ GNU_MAKE_VERSION_MAJOR=0 ;
+ GNU_MAKE_VERSION_MINOR=0 ;
+ for a in make gmake gnumake ; do
+ if test -z "$a" ; then continue ; fi ;
+ if ( sh -c "$a --version" 2> /dev/null | grep GNU 2>&1 > /dev/null ) ; then
+ GNU_MAKE=$a ;
+ GNU_MAKE_VERSION_MAJOR=`$GNU_MAKE --version | grep "GNU Make" | cut -f3 -d' ' | cut -f1 -d'.'`
+ GNU_MAKE_VERSION_MINOR=`$GNU_MAKE --version | grep "GNU Make" | cut -f2 -d'.' | cut -c1-2`
+ break;
+ fi
+ done ;
+) ;
+if test "x$GNU_MAKE" = "xNot Found" ; then
+ AC_MSG_ERROR( *** Please install GNU make. It is required to build Asterisk!)
+ exit 1
+fi
+AC_SUBST([GNU_MAKE])
+])
+
+
+AC_DEFUN(
+[AST_CHECK_PWLIB], [
+PWLIB_INCDIR=
+PWLIB_LIBDIR=
+AC_LANG_PUSH([C++])
+if test "${PWLIBDIR:-unset}" != "unset" ; then
+ AC_CHECK_HEADER(${PWLIBDIR}/version.h, HAS_PWLIB=1, )
+fi
+if test "${HAS_PWLIB:-unset}" = "unset" ; then
+ if test "${OPENH323DIR:-unset}" != "unset"; then
+ AC_CHECK_HEADER(${OPENH323DIR}/../pwlib/version.h, HAS_PWLIB=1, )
+ fi
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ PWLIBDIR="${OPENH323DIR}/../pwlib"
+ else
+ AC_CHECK_HEADER(${HOME}/pwlib/include/ptlib.h, HAS_PWLIB=1, )
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ PWLIBDIR="${HOME}/pwlib"
+ else
+ AC_CHECK_HEADER(/usr/local/include/ptlib.h, HAS_PWLIB=1, )
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ AC_PATH_PROG(PTLIB_CONFIG, ptlib-config, , /usr/local/bin)
+ if test "${PTLIB_CONFIG:-unset}" = "unset" ; then
+ AC_PATH_PROG(PTLIB_CONFIG, ptlib-config, , /usr/local/share/pwlib/make)
+ fi
+ PWLIB_INCDIR="/usr/local/include"
+ PWLIB_LIBDIR=`${PTLIB_CONFIG} --pwlibdir`
+ if test "${PWLIB_LIBDIR:-unset}" = "unset"; then
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/local/lib64"
+ else
+ PWLIB_LIBDIR="/usr/local/lib"
+ fi
+ fi
+ PWLIB_LIB=`${PTLIB_CONFIG} --ldflags --libs`
+ PWLIB_LIB="-L${PWLIB_LIBDIR} `echo ${PWLIB_LIB}`"
+ else
+ AC_CHECK_HEADER(/usr/include/ptlib.h, HAS_PWLIB=1, )
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ AC_PATH_PROG(PTLIB_CONFIG, ptlib-config, , /usr/share/pwlib/make)
+ PWLIB_INCDIR="/usr/include"
+ PWLIB_LIBDIR=`${PTLIB_CONFIG} --pwlibdir`
+ if test "${PWLIB_LIBDIR:-unset}" = "unset"; then
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/lib64"
+ else
+ PWLIB_LIBDIR="/usr/lib"
+ fi
+ fi
+ PWLIB_LIB=`${PTLIB_CONFIG} --ldflags --libs`
+ PWLIB_LIB="-L${PWLIB_LIBDIR} `echo ${PWLIB_LIB}`"
+ fi
+ fi
+ fi
+ fi
+fi
+
+#if test "${HAS_PWLIB:-unset}" = "unset" ; then
+# echo "Cannot find pwlib - please install or set PWLIBDIR and try again"
+# exit
+#fi
+
+if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ if test "${PWLIBDIR:-unset}" = "unset" ; then
+ if test "${PTLIB_CONFIG:-unset}" != "unset" ; then
+ PWLIBDIR=`$PTLIB_CONFIG --prefix`
+ else
+ echo "Cannot find ptlib-config - please install and try again"
+ exit
+ fi
+ fi
+
+ if test "x$PWLIBDIR" = "x/usr" -o "x$PWLIBDIR" = "x/usr/"; then
+ PWLIBDIR="/usr/share/pwlib"
+ PWLIB_INCDIR="/usr/include"
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/lib64"
+ else
+ PWLIB_LIBDIR="/usr/lib"
+ fi
+ fi
+ if test "x$PWLIBDIR" = "x/usr/local" -o "x$PWLIBDIR" = "x/usr/"; then
+ PWLIBDIR="/usr/local/share/pwlib"
+ PWLIB_INCDIR="/usr/local/include"
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/local/lib64"
+ else
+ PWLIB_LIBDIR="/usr/local/lib"
+ fi
+ fi
+
+ if test "${PWLIB_INCDIR:-unset}" = "unset"; then
+ PWLIB_INCDIR="${PWLIBDIR}/include"
+ fi
+ if test "${PWLIB_LIBDIR:-unset}" = "unset"; then
+ PWLIB_LIBDIR="${PWLIBDIR}/lib"
+ fi
+
+ AC_SUBST([PWLIBDIR])
+ AC_SUBST([PWLIB_INCDIR])
+ AC_SUBST([PWLIB_LIBDIR])
+fi
+ AC_LANG_POP([C++])
+])
+
+
+AC_DEFUN(
+[AST_CHECK_OPENH323_PLATFORM], [
+PWLIB_OSTYPE=
+case "$host_os" in
+ linux*) PWLIB_OSTYPE=linux ;
+ ;;
+ freebsd* ) PWLIB_OSTYPE=FreeBSD ;
+ ;;
+ openbsd* ) PWLIB_OSTYPE=OpenBSD ;
+ ENDLDLIBS="-lossaudio" ;
+ ;;
+ netbsd* ) PWLIB_OSTYPE=NetBSD ;
+ ENDLDLIBS="-lossaudio" ;
+ ;;
+ solaris* | sunos* ) PWLIB_OSTYPE=solaris ;
+ ;;
+ darwin* ) PWLIB_OSTYPE=Darwin ;
+ ;;
+ beos*) PWLIB_OSTYPE=beos ;
+ STDCCFLAGS="$STDCCFLAGS -D__BEOS__"
+ ;;
+ cygwin*) PWLIB_OSTYPE=cygwin ;
+ ;;
+ mingw*) PWLIB_OSTYPE=mingw ;
+ STDCCFLAGS="$STDCCFLAGS -mms-bitfields" ;
+ ENDLDLIBS="-lwinmm -lwsock32 -lsnmpapi -lmpr -lcomdlg32 -lgdi32 -lavicap32" ;
+ ;;
+ * ) PWLIB_OSTYPE="$host_os" ;
+ AC_MSG_WARN("OS $PWLIB_OSTYPE not recognized - proceed with caution!") ;
+ ;;
+esac
+
+PWLIB_MACHTYPE=
+case "$host_cpu" in
+ x86 | i686 | i586 | i486 | i386 ) PWLIB_MACHTYPE=x86
+ ;;
+
+ x86_64) PWLIB_MACHTYPE=x86_64 ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ alpha | alphaev56 | alphaev6 | alphaev67 | alphaev7) PWLIB_MACHTYPE=alpha ;
+ P_64BIT=1 ;
+ ;;
+
+ sparc ) PWLIB_MACHTYPE=sparc ;
+ ;;
+
+ powerpc ) PWLIB_MACHTYPE=ppc ;
+ ;;
+
+ ppc ) PWLIB_MACHTYPE=ppc ;
+ ;;
+
+ powerpc64 ) PWLIB_MACHTYPE=ppc64 ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ ppc64 ) PWLIB_MACHTYPE=ppc64 ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ ia64) PWLIB_MACHTYPE=ia64 ;
+ P_64BIT=1 ;
+ ;;
+
+ s390x) PWLIB_MACHTYPE=s390x ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ s390) PWLIB_MACHTYPE=s390 ;
+ ;;
+
+ * ) PWLIB_MACHTYPE="$host_cpu";
+ AC_MSG_WARN("CPU $PWLIB_MACHTYPE not recognized - proceed with caution!") ;;
+esac
+
+PWLIB_PLATFORM="${PWLIB_OSTYPE}_${PWLIB_MACHTYPE}"
+
+AC_SUBST([PWLIB_PLATFORM])
+])
+
+
+AC_DEFUN(
+[AST_CHECK_OPENH323], [
+OPENH323_INCDIR=
+OPENH323_LIBDIR=
+AC_LANG_PUSH([C++])
+if test "${OPENH323DIR:-unset}" != "unset" ; then
+ AC_CHECK_HEADER(${OPENH323DIR}/version.h, HAS_OPENH323=1, )
+fi
+if test "${HAS_OPENH323:-unset}" = "unset" ; then
+ AC_CHECK_HEADER(${PWLIBDIR}/../openh323/version.h, OPENH323DIR="${PWLIBDIR}/../openh323"; HAS_OPENH323=1, )
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="${PWLIBDIR}/../openh323"
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I${PWLIB_INCDIR}/openh323 -I${PWLIB_INCDIR}"
+ AC_CHECK_HEADER(${OPENH323DIR}/include/h323.h, , OPENH323_INCDIR="${PWLIB_INCDIR}/openh323"; OPENH323_LIBDIR="${PWLIB_LIBDIR}", [#include <ptlib.h>])
+ CPPFLAGS="${saved_cppflags}"
+ else
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I${HOME}/openh323/include -I${PWLIB_INCDIR}"
+ AC_CHECK_HEADER(${HOME}/openh323/include/h323.h, HAS_OPENH323=1, )
+ CPPFLAGS="${saved_cppflags}"
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="${HOME}/openh323"
+ else
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I/usr/local/include/openh323 -I${PWLIB_INCDIR}"
+ AC_CHECK_HEADER(/usr/local/include/openh323/h323.h, HAS_OPENH323=1, )
+ CPPFLAGS="${saved_cppflags}"
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="/usr/local/share/openh323"
+ OPENH323_INCDIR="/usr/local/include/openh323"
+ if test "x$LIB64" != "x"; then
+ OPENH323_LIBDIR="/usr/local/lib64"
+ else
+ OPENH323_LIBDIR="/usr/local/lib"
+ fi
+ else
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I/usr/include/openh323 -I${PWLIB_INCDIR}"
+ AC_CHECK_HEADER(/usr/include/openh323/h323.h, HAS_OPENH323=1, , [#include <ptlib.h>])
+ CPPFLAGS="${saved_cppflags}"
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="/usr/share/openh323"
+ OPENH323_INCDIR="/usr/include/openh323"
+ if test "x$LIB64" != "x"; then
+ OPENH323_LIBDIR="/usr/lib64"
+ else
+ OPENH323_LIBDIR="/usr/lib"
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ if test "${OPENH323_INCDIR:-unset}" = "unset"; then
+ OPENH323_INCDIR="${OPENH323DIR}/include"
+ fi
+ if test "${OPENH323_LIBDIR:-unset}" = "unset"; then
+ OPENH323_LIBDIR="${OPENH323DIR}/lib"
+ fi
+
+ OPENH323_LIBDIR="`cd ${OPENH323_LIBDIR}; pwd`"
+ OPENH323_INCDIR="`cd ${OPENH323_INCDIR}; pwd`"
+ OPENH323DIR="`cd ${OPENH323DIR}; pwd`"
+
+ AC_SUBST([OPENH323DIR])
+ AC_SUBST([OPENH323_INCDIR])
+ AC_SUBST([OPENH323_LIBDIR])
+fi
+ AC_LANG_POP([C++])
+])
+
+
+AC_DEFUN(
+[AST_CHECK_PWLIB_VERSION], [
+ if test "${HAS_$2:-unset}" != "unset"; then
+ $2_VERSION=`grep "$2_VERSION" ${$2_INCDIR}/$3 | cut -f2 -d ' ' | sed -e 's/"//g'`
+ $2_MAJOR_VERSION=`echo ${$2_VERSION} | cut -f1 -d.`
+ $2_MINOR_VERSION=`echo ${$2_VERSION} | cut -f2 -d.`
+ $2_BUILD_NUMBER=`echo ${$2_VERSION} | cut -f3 -d.`
+ let $2_VER=${$2_MAJOR_VERSION}*10000+${$2_MINOR_VERSION}*100+${$2_BUILD_NUMBER}
+ let $2_REQ=$4*10000+$5*100+$6
+
+ AC_MSG_CHECKING(if $1 version ${$2_VERSION} is compatible with chan_h323)
+ if test ${$2_VER} -lt ${$2_REQ}; then
+ AC_MSG_RESULT(no)
+ unset HAS_$2
+ else
+ AC_MSG_RESULT(yes)
+ fi
+ fi
+])
+
+
+AC_DEFUN(
+[AST_CHECK_PWLIB_BUILD], [
+ if test "${HAS_$2:-unset}" != "unset"; then
+ AC_MSG_CHECKING($1 installation validity)
+
+ saved_cppflags="${CPPFLAGS}"
+ saved_libs="${LIBS}"
+ if test "${$2_LIB:-unset}" != "unset"; then
+ LIBS="${LIBS} ${$2_LIB} $7"
+ else
+ LIBS="${LIBS} -L${$2_LIBDIR} -l${PLATFORM_$2} $7"
+ fi
+ CPPFLAGS="${CPPFLAGS} -I${$2_INCDIR} $6"
+
+ AC_LANG_PUSH([C++])
+
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([$4],[$5])],
+ [ AC_MSG_RESULT(yes)
+ ac_cv_lib_$2="yes"
+ ],
+ [ AC_MSG_RESULT(no)
+ ac_cv_lib_$2="no"
+ ]
+ )
+
+ AC_LANG_POP([C++])
+
+ LIBS="${saved_libs}"
+ CPPFLAGS="${saved_cppflags}"
+
+ if test "${ac_cv_lib_$2}" = "yes"; then
+ if test "${$2_LIB:-undef}" = "undef"; then
+ if test "${$2_LIBDIR}" != "" -a "${$2_LIBDIR}" != "/usr/lib"; then
+ $2_LIB="-L${$2_LIBDIR} -l${PLATFORM_$2}"
+ else
+ $2_LIB="-l${PLATFORM_$2}"
+ fi
+ fi
+ if test "${$2_INCDIR}" != "" -a "${$2_INCDIR}" != "/usr/include"; then
+ $2_INCLUDE="-I${$2_INCDIR}"
+ fi
+ PBX_$2=1
+ AC_DEFINE([HAVE_$2], 1, [$3])
+ fi
+ fi
+])
+
+AC_DEFUN(
+[AST_CHECK_OPENH323_BUILD], [
+ if test "${HAS_OPENH323:-unset}" != "unset"; then
+ AC_MSG_CHECKING(OpenH323 build option)
+ OPENH323_SUFFIX=
+ prefixes="h323_${PWLIB_PLATFORM}_ h323_ openh323"
+ for pfx in $prefixes; do
+ files=`ls -l ${OPENH323_LIBDIR}/lib${pfx}*.so* 2>/dev/null`
+ libfile=
+ if test -n "$files"; then
+ for f in $files; do
+ if test -f $f -a ! -L $f; then
+ libfile=`basename $f`
+ break;
+ fi
+ done
+ fi
+ if test -n "$libfile"; then
+ OPENH323_PREFIX=$pfx
+ break;
+ fi
+ done
+ if test "${libfile:-unset}" != "unset"; then
+ OPENH323_SUFFIX=`eval "echo ${libfile} | sed -e 's/lib${OPENH323_PREFIX}\(@<:@^.@:>@*\)\..*/\1/'"`
+ fi
+ case "${OPENH323_SUFFIX}" in
+ n)
+ OPENH323_BUILD="notrace";;
+ r)
+ OPENH323_BUILD="opt";;
+ d)
+ OPENH323_BUILD="debug";;
+ *)
+ if test "${OPENH323_PREFIX:-undef}" = "openh323"; then
+ notrace=`eval "grep NOTRACE ${OPENH323DIR}/openh323u.mak | grep = | sed -e 's/@<:@A-Z0-9_@:>@*@<:@ @:>@*=@<:@ @:>@*//'"`
+ if test "x$notrace" = "x"; then
+ notrace="0"
+ fi
+ if test "$notrace" -ne 0; then
+ OPENH323_BUILD="notrace"
+ else
+ OPENH323_BUILD="opt"
+ fi
+ OPENH323_LIB="-l${OPENH323_PREFIX}"
+ else
+ OPENH323_BUILD="notrace"
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT(${OPENH323_BUILD})
+
+ AC_SUBST([OPENH323_SUFFIX])
+ AC_SUBST([OPENH323_BUILD])
+ fi
+])
+
+
+# AST_FUNC_FORK
+# -------------
+AN_FUNCTION([fork], [AST_FUNC_FORK])
+AN_FUNCTION([vfork], [AST_FUNC_FORK])
+AC_DEFUN([AST_FUNC_FORK],
+[AC_REQUIRE([AC_TYPE_PID_T])dnl
+AC_CHECK_HEADERS(vfork.h)
+AC_CHECK_FUNCS(fork vfork)
+if test "x$ac_cv_func_fork" = xyes; then
+ _AST_FUNC_FORK
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp* | *-*-uclinux* | *-*-linux-uclibc* )
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ AC_MSG_WARN([result $ac_cv_func_fork_works guessed because of cross compilation])
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ _AC_FUNC_VFORK
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ AC_MSG_WARN([result $ac_cv_func_vfork_works guessed because of cross compilation])
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+ AC_DEFINE(HAVE_WORKING_VFORK, 1, [Define to 1 if `vfork' works.])
+else
+ AC_DEFINE(vfork, fork, [Define as `fork' if `vfork' does not work.])
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+ AC_DEFINE(HAVE_WORKING_FORK, 1, [Define to 1 if `fork' works.])
+fi
+])# AST_FUNC_FORK
+
+
+# _AST_FUNC_FORK
+# -------------
+AC_DEFUN([_AST_FUNC_FORK],
+ [AC_CACHE_CHECK(for working fork, ac_cv_func_fork_works,
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+ [
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+ ])],
+ [ac_cv_func_fork_works=yes],
+ [ac_cv_func_fork_works=no],
+ [ac_cv_func_fork_works=cross])])]
+)# _AST_FUNC_FORK
+
+# AST_PROG_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([AST_PROG_LD],
+[AC_ARG_WITH([gnu-ld],
+ [AC_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test "$withval" = no || with_gnu_ld=yes],
+ [with_gnu_ld=no])
+AC_REQUIRE([AST_PROG_SED])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AST_PROG_LD_GNU
+])# AST_PROG_LD
+
+
+# AST_PROG_LD_GNU
+# --------------
+AC_DEFUN([AST_PROG_LD_GNU],
+[AC_REQUIRE([AST_PROG_EGREP])dnl
+AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# AST_PROG_LD_GNU
+
+# AST_PROG_EGREP
+# -------------
+m4_ifndef([AST_PROG_EGREP], [AC_DEFUN([AST_PROG_EGREP],
+[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep],
+ [if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+ then ac_cv_prog_egrep='grep -E'
+ else ac_cv_prog_egrep='egrep'
+ fi])
+ EGREP=$ac_cv_prog_egrep
+ AC_SUBST([EGREP])
+])]) # AST_PROG_EGREP
+
+# AST_PROG_SED
+# -----------
+# Check for a fully functional sed program that truncates
+# as few characters as possible. Prefer GNU sed if found.
+AC_DEFUN([AST_PROG_SED],
+[AC_CACHE_CHECK([for a sed that does not truncate output], ac_cv_path_SED,
+ [dnl ac_script should not contain more than 99 commands (for HP-UX sed),
+ dnl but more than about 7000 bytes, to catch a limit in Solaris 8 /usr/ucb/sed.
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" | sed 99q >conftest.sed
+ $as_unset ac_script || ac_script=
+ _AC_PATH_PROG_FEATURE_CHECK(SED, [sed gsed],
+ [_AC_FEATURE_CHECK_LENGTH([ac_path_SED], [ac_cv_path_SED],
+ ["$ac_path_SED" -f conftest.sed])])])
+ SED="$ac_cv_path_SED"
+ AC_SUBST([SED])dnl
+ rm -f conftest.sed
+])# AST_PROG_SED
+
+dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl @summary figure out how to build C programs using POSIX threads
+dnl
+dnl This macro figures out how to build C programs using POSIX threads.
+dnl It sets the PTHREAD_LIBS output variable to the threads library and
+dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
+dnl C compiler flags that are needed. (The user can also force certain
+dnl compiler flags/libs to be tested by setting these environment
+dnl variables.)
+dnl
+dnl Also sets PTHREAD_CC to any special C compiler that is needed for
+dnl multi-threaded programs (defaults to the value of CC otherwise).
+dnl (This is necessary on AIX to use the special cc_r compiler alias.)
+dnl
+dnl NOTE: You are assumed to not only compile your program with these
+dnl flags, but also link it with them as well. e.g. you should link
+dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
+dnl $LIBS
+dnl
+dnl If you are only building threads programs, you may wish to use
+dnl these variables in your default LIBS, CFLAGS, and CC:
+dnl
+dnl LIBS="$PTHREAD_LIBS $LIBS"
+dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+dnl CC="$PTHREAD_CC"
+dnl
+dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
+dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
+dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
+dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
+dnl default action will define HAVE_PTHREAD.
+dnl
+dnl Please let the authors know if this macro fails on any platform, or
+dnl if you have any other suggestions or comments. This macro was based
+dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
+dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
+dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
+dnl We are also grateful for the helpful feedback of numerous users.
+dnl
+dnl @category InstalledPackages
+dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
+dnl @version 2006-05-29
+dnl @license GPLWithACException
+
+AC_DEFUN([ACX_PTHREAD],
+[
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+ [acx_pthread_ok=yes])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
+ [attr_name=$attr; break])
+ done
+ AC_MSG_RESULT($attr_name)
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ AC_MSG_RESULT(${flag})
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+ :
+else
+ acx_pthread_ok=no
+ $2
+fi
+AC_LANG_RESTORE
+])dnl ACX_PTHREAD
diff --git a/trunk/agi/DialAnMp3.agi b/trunk/agi/DialAnMp3.agi
new file mode 100644
index 000000000..59a54265e
--- /dev/null
+++ b/trunk/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/trunk/agi/Makefile b/trunk/agi/Makefile
new file mode 100644
index 000000000..0cb6f3f02
--- /dev/null
+++ b/trunk/agi/Makefile
@@ -0,0 +1,52 @@
+#
+# 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
+
+ifeq ($(OSARCH),mingw32)
+ AGIS:=
+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 .*.o.d .*.oo.d
+ rm -f *.s *.i
+ rm -f strcompat.c
+
+ifneq ($(wildcard .*.d),)
+ include .*.d
+endif
diff --git a/trunk/agi/agi-test.agi b/trunk/agi/agi-test.agi
new file mode 100644
index 000000000..4fc36eda8
--- /dev/null
+++ b/trunk/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/trunk/agi/eagi-sphinx-test.c b/trunk/agi/eagi-sphinx-test.c
new file mode 100644
index 000000000..d2898763c
--- /dev/null
+++ b/trunk/agi/eagi-sphinx-test.c
@@ -0,0 +1,231 @@
+/*
+ * Extended AGI test application
+ *
+ * This code is released into public domain
+ * without any warranty of any kind.
+ *
+ */
+
+/*! \file
+ * Extended AGI test application
+ *
+ * This code is released into public domain
+ * without any warranty of any kind.
+ *
+ * \ingroup agi
+ */
+
+#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(;;) {
+ fgets(buf, sizeof(buf), stdin);
+ 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)) {
+ fgets(astresp, sizeof(astresp), stdin);
+ 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) {
+ if (sphinx_sock > -1)
+ write(sphinx_sock, audiobuf, res);
+ }
+ }
+ 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/trunk/agi/eagi-test.c b/trunk/agi/eagi-test.c
new file mode 100644
index 000000000..704fd7b22
--- /dev/null
+++ b/trunk/agi/eagi-test.c
@@ -0,0 +1,165 @@
+/*
+ * Extended AGI test application
+ *
+ * This code is released into the public domain
+ * with no warranty of any kind
+ */
+
+#include "asterisk.h"
+
+#define AUDIO_FILENO (STDERR_FILENO + 1)
+
+/*! \file
+ * Extended AGI test application
+ *
+ * This code is released into the public domain
+ * with no warranty of any kind
+ *
+ * \ingroup agi
+ */
+
+static int read_environment(void)
+{
+ char buf[256];
+ char *val;
+ /* Read environment */
+ for(;;) {
+ fgets(buf, sizeof(buf), stdin);
+ 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)) {
+ fgets(astresp, sizeof(astresp), stdin);
+ 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/trunk/agi/fastagi-test b/trunk/agi/fastagi-test
new file mode 100644
index 000000000..d3f13cf6b
--- /dev/null
+++ b/trunk/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/trunk/agi/jukebox.agi b/trunk/agi/jukebox.agi
new file mode 100755
index 000000000..7bd9c10f9
--- /dev/null
+++ b/trunk/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/trunk/agi/numeralize b/trunk/agi/numeralize
new file mode 100644
index 000000000..5ca51913d
--- /dev/null
+++ b/trunk/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/trunk/apps/Makefile b/trunk/apps/Makefile
new file mode 100644
index 000000000..22b8f6d7a
--- /dev/null
+++ b/trunk/apps/Makefile
@@ -0,0 +1,41 @@
+#
+# 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 $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=app
+MENUSELECT_CATEGORY=APPS
+MENUSELECT_DESCRIPTION=Applications
+
+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
+
+ifeq (SunOS,$(shell uname))
+ MENUSELECT_DEPENDS_app_chanspy+=RT
+ RT_LIB=-lrt
+endif
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ LIBS+= -lres_features.so -lres_ael_share.so -lres_monitor.so -lres_speech.so
+ LIBS+= -lres_smdi.so
+endif
+
diff --git a/trunk/apps/app_adsiprog.c b/trunk/apps/app_adsiprog.c
new file mode 100644
index 000000000..1b09ce7b5
--- /dev/null
+++ b/trunk/apps/app_adsiprog.c
@@ -0,0 +1,1581 @@
+/*
+ * 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 <netinet/in.h>
+#include <ctype.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/adsi.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, *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], *a;
+ int bytes = 0;
+
+ if (!(a = get_token(&args, script, lineno))) {
+ 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 = get_token(&args, script, lineno);
+ char *gline = get_token(&args, script, lineno);
+ int line;
+ unsigned char cmd;
+
+ 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 = get_token(&args, script, lineno);
+ char *gline = get_token(&args, script, lineno);
+ int line;
+ unsigned char cmd;
+
+ 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 = get_token(&args, script, lineno);
+ int ms;
+
+ 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 = get_token(&args, script, lineno);
+ int state;
+
+ 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 = 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 = get_token(&args, script, lineno);
+ char sname[80];
+ struct adsi_flag *flag;
+
+ 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;
+ }
+
+ if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
+ 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 = get_token(&args, script, lineno);
+ struct adsi_flag *flag;
+ char sname[80];
+
+ 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;
+ }
+
+ if (!(flag = getflagbyname(state, sname, script, lineno, 0))) {
+ 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 = get_token(&args, script, lineno);
+ int secs;
+
+ 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, newkey[80];
+ int bytes, x, flagid = 0;
+ unsigned char keyid[6];
+ struct adsi_soft_key *key;
+ struct adsi_flag *flag;
+
+ for (x = 0; x < 7; x++) {
+ /* Up to 6 key arguments */
+ if (!(tok = get_token(&args, script, lineno)))
+ break;
+ if (!strcasecmp(tok, "UNLESS")) {
+ /* Check for trailing UNLESS flag */
+ if (!(tok = get_token(&args, script, lineno))) {
+ 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;
+ }
+
+ if (!(key = getkeybyname(state, newkey, script, lineno)))
+ 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, dispname[80];
+ int line = 0, flag = 0, cmd = 3;
+ struct adsi_display *disp;
+
+ /* Get display */
+ if (!(tok = get_token(&args, script, lineno)) || 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;
+ }
+
+ if (!(disp = getdisplaybyname(state, dispname, script, lineno, 0))) {
+ ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
+ return 0;
+ }
+
+ if (!(tok = get_token(&args, script, lineno)) || strcasecmp(tok, "AT")) {
+ ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
+ return 0;
+ }
+
+ /* Get line number */
+ if (!(tok = get_token(&args, script, lineno)) || 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;
+ }
+
+ if ((tok = get_token(&args, script, lineno)) && !strcasecmp(tok, "NOUPDATE")) {
+ cmd = 1;
+ tok = get_token(&args, script, lineno);
+ }
+
+ if (tok && !strcasecmp(tok, "UNLESS")) {
+ /* Check for trailing UNLESS flag */
+ if (!(tok = get_token(&args, script, lineno))) {
+ 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 = 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 = 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 = 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 = 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 = get_token(&args, script, lineno);
+ char subscript[80];
+ struct adsi_subscript *sub;
+
+ 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;
+ }
+
+ if (!(sub = getsubbyname(state, subscript, script, lineno)))
+ 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 = get_token(&args, script, lineno);
+ char subscript[80], sname[80];
+ int sawin = 0, event, snums[8], scnt = 0, x;
+ struct adsi_subscript *sub;
+
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
+ return 0;
+ }
+
+ if ((event = geteventbyname(tok)) < 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++;
+ if (!(tok = get_token(&args, script, lineno)))
+ 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);
+ }
+ if (!(tok = get_token(&args, script, lineno))) {
+ 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;
+ }
+ if (!(sub = getsubbyname(state, subscript, script, lineno)))
+ 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, res;
+ char *unused;
+
+ 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, res, max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
+ char *unused;
+
+ 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 = get_token(&buf, script, lineno);
+ char *args, vname[256], tmp[80], tmp2[80];
+ int lrci, wi, event;
+ struct adsi_display *disp;
+ struct adsi_subscript *newsub;
+
+ if (!keyword)
+ return 0;
+
+ switch(state->state) {
+ case STATE_NORMAL:
+ if (!strcasecmp(keyword, "DESCRIPTION")) {
+ if ((args = get_token(&buf, script, lineno))) {
+ 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")) {
+ if ((args = get_token(&buf, script, lineno))) {
+ 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")) {
+ if ((args = get_token(&buf, script, lineno))) {
+ 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")) {
+ if ((args = get_token(&buf, script, lineno))) {
+ 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")) {
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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;
+ }
+ if (!(state->key = getkeybyname(state, vname, script, lineno))) {
+ 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;
+ }
+ if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
+ break;
+ }
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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;
+ }
+ if ((args = get_token(&buf, script, lineno))) {
+ if (strcasecmp(args, "OR")) {
+ ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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")) {
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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 (!(state->sub = getsubbyname(state, vname, script, lineno))) {
+ 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;
+ }
+ if (!(args = get_token(&buf, script, lineno)) || 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")) {
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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")) {
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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;
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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;
+ }
+ if (!(disp = getdisplaybyname(state, vname, script, lineno, 1)))
+ break;
+ if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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")) {
+ if (!(args = get_token(&buf, script, lineno))) {
+ 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;
+ }
+ if (!(newsub = getsubbyname(state, tmp, script, lineno)))
+ 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")) {
+ if (!(args = get_token(&buf, script, lineno))) {
+ ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if ((event = geteventbyname(args)) < 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
+ break;
+ }
+ if (!(args = get_token(&buf, script, lineno)) || 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], buf[256], *c;
+ int lineno = 0, x, err;
+ struct adsi_script *scr;
+
+ if (script[0] == '/')
+ ast_copy_string(fn, script, sizeof(fn));
+ else
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, script);
+
+ if (!(f = fopen(fn, "r"))) {
+ 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)) {
+ fgets(buf, sizeof(buf), f);
+ if (!feof(f)) {
+ lineno++;
+ /* Trim off trailing return */
+ buf[strlen(buf) - 1] = '\0';
+ /* Strip comments */
+ if ((c = strchr(buf, ';')))
+ *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);
+ ast_free(scr);
+ return NULL;
+ case STATE_INKEY:
+ ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
+ ast_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) {
+ ast_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, bytes;
+ unsigned char buf[1024];
+
+ if (!(scr = compile_script(script)))
+ 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 */
+ ast_verb(3, "User rejected download attempt\n");
+ ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
+ ast_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 */
+ ast_verb(3, "Download attempt failed\n");
+ ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
+ ast_free(scr);
+ return -1;
+ }
+ ast_free(scr);
+ ast_adsi_unload_session(chan);
+ return 0;
+}
+
+static int adsi_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (ast_strlen_zero(data))
+ data = "asterisk.adsi";
+
+ if (!ast_adsi_available(chan)) {
+ ast_verb(3, "ADSI Unavailable on CPE. Not bothering to try.\n");
+ } else {
+ ast_verb(3, "ADSI Available on CPE. Attempting Upload.\n");
+ res = adsi_prog(chan, data);
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ if (ast_register_application(app, adsi_exec, synopsis, descrip))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application");
diff --git a/trunk/apps/app_alarmreceiver.c b/trunk/apps/app_alarmreceiver.c
new file mode 100644
index 000000000..c4fa81109
--- /dev/null
+++ b/trunk/apps/app_alarmreceiver.c
@@ -0,0 +1,813 @@
+/*
+ * 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 <math.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.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){
+ ast_verb(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)){
+ ast_verb(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_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
+
+ res = 1;
+ break;
+ }
+
+ if ((r = ast_waitfor(chan, -1) < 0)) {
+ ast_debug(1, "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;
+ struct timeval t;
+ struct ast_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 */
+
+ t = ast_tvnow();
+ ast_localtime(&t, &now, NULL);
+
+ /* Format the time */
+
+ ast_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){
+ if (option_verbose >= 3 )
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n");
+
+ ast_debug(1,"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) {
+ if (option_verbose >= 3)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n");
+ ast_debug(1,"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;
+
+ ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
+ ast_debug(1, "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){
+ ast_verb(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");
+ ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
+ ast_debug(1, "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");
+ ast_verb(2, "AlarmReceiver: Wrong message type\n");
+ ast_debug(1, "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;
+ event_node_t *elp, *efree;
+ char signalling_type[64] = "";
+
+ event_node_t *event_head = NULL;
+
+ /* Set write and read formats to ULAW */
+
+ ast_verb(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);
+ 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);
+ return -1;
+ }
+
+ /* Set default values for this invocation of the application */
+
+ ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
+
+
+ /* Answer the channel if it is not already */
+
+ ast_verb(4, "AlarmReceiver: Answering channel\n");
+
+ if (chan->_state != AST_STATE_UP) {
+ if ((res = ast_answer(chan)))
+ return -1;
+ }
+
+ /* Wait for the connection to settle post-answer */
+
+ ast_verb(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_debug(1,"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;
+ ast_free(efree);
+ }
+
+ return 0;
+}
+
+/*
+* Load the configuration from the configuration file
+*/
+
+static int load_config(void)
+{
+ struct ast_config *cfg;
+ const char *p;
+ struct ast_flags config_flags = { 0 };
+
+ /* Read in the config file */
+
+ cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
+
+ 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)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ if(load_config()) {
+ if (ast_register_application(app, alarmreceiver_exec, synopsis, descrip))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+ }
+ else
+ return AST_MODULE_LOAD_DECLINE;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
diff --git a/trunk/apps/app_amd.c b/trunk/apps/app_amd.c
new file mode 100644
index 000000000..4e440d388
--- /dev/null
+++ b/trunk/apps/app_amd.c
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Answering machine detection
+ *
+ * \author Claude Klimos (claude.klimos@aheeva.com)
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/lock.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],[|maximumWordLength])\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"
+"- 'maximumWordLength' is the maximum duration of a word to accept. If exceeded then MACHINE\n"
+"This application sets the following channel variables 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"
+" MAXWORDLENGTH-<%d consecutiveVoiceDuration>\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;
+static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
+
+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;
+ int inInitialSilence = 1;
+ int inGreeting = 0;
+ int voiceDuration = 0;
+ int silenceDuration = 0;
+ int iTotalTime = 0;
+ int iWordsCount = 0;
+ int currentState = STATE_IN_SILENCE;
+ 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 maximumWordLength = dfltMaximumWordLength;
+
+ 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);
+ AST_APP_ARG(argMaximumWordLength);
+ );
+
+ ast_verb(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);
+ if (!ast_strlen_zero(args.argMaximumWordLength))
+ maximumWordLength = atoi(args.argMaximumWordLength);
+ } else {
+ ast_debug(1, "AMD using the default parameters.\n");
+ }
+
+ /* Now we're ready to roll! */
+ ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
+ "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
+ initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
+ minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
+
+ /* 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, totalAnalysisTime)) > -1) {
+ /* If we fail to read in a frame, that means they hung up */
+ if (!(f = ast_read(chan))) {
+ ast_verb(3, "AMD: Channel [%s]. HANGUP\n", chan->name);
+ ast_debug(1, "Got hangup\n");
+ strcpy(amdStatus, "HANGUP");
+ break;
+ }
+
+ if (f->frametype == AST_FRAME_VOICE) {
+ /* If the total time exceeds the analysis time then give up as we are not too sure */
+ framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
+ iTotalTime += framelength;
+ if (iTotalTime >= totalAnalysisTime) {
+ ast_verb(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 */
+ dspsilence = 0;
+ ast_dsp_silence(silenceDetector, f, &dspsilence);
+ if (dspsilence) {
+ silenceDuration = dspsilence;
+
+ if (silenceDuration >= betweenWordsSilence) {
+ if (currentState != STATE_IN_SILENCE ) {
+ previousState = currentState;
+ ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", chan->name);
+ }
+ /* Find words less than word duration */
+ if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
+ ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", chan->name, consecutiveVoiceDuration);
+ }
+ currentState = STATE_IN_SILENCE;
+ consecutiveVoiceDuration = 0;
+ }
+
+ if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
+ ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
+ chan->name, silenceDuration, initialSilence);
+ ast_frfree(f);
+ strcpy(amdStatus , "MACHINE");
+ sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
+ break;
+ }
+
+ if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
+ ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
+ chan->name, silenceDuration, afterGreetingSilence);
+ ast_frfree(f);
+ strcpy(amdStatus , "HUMAN");
+ sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
+ 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++;
+ ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", chan->name, iWordsCount);
+ previousState = currentState;
+ currentState = STATE_IN_WORD;
+ }
+ if (consecutiveVoiceDuration >= maximumWordLength){
+ ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", chan->name, consecutiveVoiceDuration);
+ ast_frfree(f);
+ strcpy(amdStatus , "MACHINE");
+ sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
+ break;
+ }
+ if (iWordsCount >= maximumNumberOfWords) {
+ ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", chan->name, iWordsCount);
+ ast_frfree(f);
+ strcpy(amdStatus , "MACHINE");
+ sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
+ break;
+ }
+
+ if (inGreeting == 1 && voiceDuration >= greeting) {
+ ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", chan->name, voiceDuration, greeting);
+ ast_frfree(f);
+ strcpy(amdStatus , "MACHINE");
+ sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
+ break;
+ }
+
+ if (voiceDuration >= minimumWordLength ) {
+ if (silenceDuration > 0)
+ ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", chan->name, silenceDuration);
+ silenceDuration = 0;
+ }
+ if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0){
+ /* Only go in here once to change the greeting flag when we detect the 1st word */
+ if (silenceDuration > 0)
+ ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", chan->name, silenceDuration, voiceDuration);
+ inInitialSilence = 0;
+ inGreeting = 1;
+ }
+
+ }
+ }
+ ast_frfree(f);
+ }
+
+ if (!res) {
+ /* It took too long to get a frame back. Giving up. */
+ ast_verb(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)
+{
+ isAnsweringMachine(chan, data);
+
+ return 0;
+}
+
+static int load_config(int reload)
+{
+ struct ast_config *cfg = NULL;
+ char *cat = NULL;
+ struct ast_variable *var = NULL;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if (!(cfg = ast_config_load("amd.conf", config_flags))) {
+ ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ 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 if (!strcasecmp(var->name, "maximum_word_length")) {
+ dfltMaximumWordLength = 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);
+
+ ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
+ "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
+ dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
+ dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ if (load_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ if (ast_register_application(app, amd_exec, synopsis, descrip))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload(void)
+{
+ if (load_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/apps/app_authenticate.c b/trunk/apps/app_authenticate.c
new file mode 100644
index 000000000..3a3e7582e
--- /dev/null
+++ b/trunk/apps/app_authenticate.c
@@ -0,0 +1,212 @@
+/*
+ * 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 "asterisk/lock.h"
+#include "asterisk/file.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"
+
+enum {
+ OPT_ACCOUNT = (1 << 0),
+ OPT_DATABASE = (1 << 1),
+ 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('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.\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"
+" 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, retries, maxdigits;
+ char passwd[256], *prompt = "agent-pass", *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;
+ }
+
+ if (chan->_state != AST_STATE_UP) {
+ if ((res = ast_answer(chan)))
+ 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 */
+ for (retries = 0; retries < 3; retries++) {
+ if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 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;
+ char buf[256] = "", md5passwd[33] = "", *md5secret = NULL;
+
+ if (!(f = fopen(arglist.password, "r"))) {
+ ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
+ continue;
+ }
+
+ while (!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!feof(f) && !ast_strlen_zero(buf)) {
+ buf[strlen(buf) - 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 {
+ /* 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);
+ if (!(res = ast_streamfile(chan, "auth-thankyou", chan->language)))
+ res = ast_waitstream(chan, "");
+ } else {
+ if (!ast_streamfile(chan, "vm-goodbye", chan->language))
+ res = ast_waitstream(chan, "");
+ res = -1;
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ if (ast_register_application(app, auth_exec, synopsis, descrip))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");
diff --git a/trunk/apps/app_cdr.c b/trunk/apps/app_cdr.c
new file mode 100644
index 000000000..7804a806b
--- /dev/null
+++ b/trunk/apps/app_cdr.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ *
+ * \author Martin Pycko <martinp@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/module.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)
+{
+ if (chan->cdr)
+ ast_set_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(nocdr_app);
+}
+
+static int load_module(void)
+{
+ if (ast_register_application(nocdr_app, nocdr_exec, nocdr_synopsis, nocdr_descrip))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Tell Asterisk to not maintain a CDR for the current call");
diff --git a/trunk/apps/app_chanisavail.c b/trunk/apps/app_chanisavail.c
new file mode 100644
index 000000000..67d29e6ee
--- /dev/null
+++ b/trunk/apps/app_chanisavail.c
@@ -0,0 +1,157 @@
+/*
+* 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 <sys/ioctl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/devicestate.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.\n"
+" Options:\n"
+" s - Consider the channel unavailable if the channel is in use at all.\n"
+" t - Simply checks if specified channels exist in the channel list\n"
+" (implies option s).\n"
+"This application sets the following channel variable upon completion:\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";
+
+
+static int chanavail_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1, inuse=-1, option_state=0, string_compare=0;
+ int status;
+ 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;
+ }
+
+ 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, 't'))
+ string_compare = 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");
+ return -1;
+ }
+ *number = '\0';
+ number++;
+
+ if (string_compare) {
+ /* ast_parse_device_state checks for "SIP/1234" as a channel name.
+ ast_device_state will ask the SIP driver for the channel state. */
+
+ snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
+ status = inuse = ast_parse_device_state(trychan);
+ } else 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", "");
+ }
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+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/trunk/apps/app_channelredirect.c b/trunk/apps/app_channelredirect.c
new file mode 100644
index 000000000..325c681ac
--- /dev/null
+++ b/trunk/apps/app_channelredirect.c
@@ -0,0 +1,93 @@
+/*
+ * 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 "asterisk/file.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"
+
+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;
+ char *info;
+ 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;
+ }
+
+ 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;
+ }
+
+ res = ast_parseable_goto(chan2, args.label);
+
+ ast_channel_unlock(chan2);
+quit:
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, asyncgoto_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Redirects a given channel to a dialplan target");
diff --git a/trunk/apps/app_chanspy.c b/trunk/apps/app_chanspy.c
new file mode 100644
index 000000000..a29973ff4
--- /dev/null
+++ b/trunk/apps/app_chanspy.c
@@ -0,0 +1,730 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
+ * Copyright (C) 2005 - 2006, 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>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/audiohook.h"
+#include "asterisk/features.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
+
+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"
+" Note: The X option supersedes the three features above in that if a valid\n"
+" single digit extension exists in the correct context ChanSpy will\n"
+" exit to it. This also disables choosing a channel based on 'chanprefix'\n"
+" and a digit sequence.\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"
+" o - Only listen to audio coming from this channel.\n"
+" X - Allow the user to exit ChanSpy to a valid single digit\n"
+" numeric extension in the current context or the context\n"
+" specified by the SPY_EXIT_CONTEXT channel variable. The\n"
+" name of the last channel that was spied on will be stored\n"
+" in the SPY_CHANNEL variable.\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"
+" Note: The X option superseeds the two features above in that if a valid\n"
+" single digit extension exists in the correct context it ChanSpy will\n"
+" exit to it.\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"
+" o - Only listen to audio coming from this channel.\n"
+" X - Allow the user to exit ChanSpy to a valid single digit\n"
+" numeric extension in the current context or the context\n"
+" specified by the SPY_EXIT_CONTEXT channel variable. The\n"
+" name of the last channel that was spied on will be stored\n"
+" in the SPY_CHANNEL variable.\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 */
+ OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
+ OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
+} 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),
+ AST_APP_OPTION('o', OPTION_READONLY),
+ AST_APP_OPTION('X', OPTION_EXIT),
+});
+
+
+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 = NULL;
+
+ ast_audiohook_lock(&csth->spy_audiohook);
+ if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ /* Channel is already gone more than likely */
+ 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)
+ write(csth->fd, f->data, f->datalen);
+
+ 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, struct ast_channel *spychan, struct ast_audiohook *audiohook)
+{
+ int res = 0;
+ struct ast_channel *peer = NULL;
+
+ 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;
+}
+
+static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
+ const struct ast_flags *flags, char *exitcontext)
+{
+ 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;
+
+ if (ast_check_hangup(chan) || ast_check_hangup(spyee))
+ return 0;
+
+ name = ast_strdupa(spyee->name);
+ ast_verb(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, chan, &csth.spy_audiohook)) {
+ ast_audiohook_destroy(&csth.spy_audiohook);
+ return 0;
+ }
+
+ if (ast_test_flag(flags, OPTION_WHISPER)) {
+ ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
+ start_spying(spyee, chan, &csth.whisper_audiohook);
+ }
+
+ 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 (ast_test_flag(flags, OPTION_EXIT)) {
+ char tmp[2];
+ tmp[0] = res;
+ tmp[1] = '\0';
+ if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
+ ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
+ pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
+ running = -2;
+ break;
+ } else {
+ ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
+ }
+ } else if (res >= '0' && res <= '9') {
+ inp[x++] = res;
+ }
+
+ if (res == '*') {
+ running = 0;
+ break;
+ } else if (res == '#') {
+ if (!ast_strlen_zero(inp)) {
+ running = atoi(inp);
+ break;
+ }
+
+ (*volfactor)++;
+ if (*volfactor > 4)
+ *volfactor = -4;
+ ast_verb(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;
+ }
+ }
+
+ 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);
+
+ ast_verb(2, "Done Spying on channel %s\n", name);
+
+ return running;
+}
+
+static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
+ const char *exten, const char *context)
+{
+ struct ast_channel *this;
+
+ 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) {
+ ast_channel_unlock(this);
+ if (!strncmp(this->name, "Zap/pseudo", 10))
+ goto redo;
+ }
+
+ return this;
+}
+
+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)
+{
+ struct ast_channel *peer, *prev, *next;
+ char nameprefix[AST_NAME_STRLEN];
+ char peer_name[AST_NAME_STRLEN + 5];
+ char exitcontext[AST_MAX_CONTEXT] = "";
+ signed char zero_volume = 0;
+ int waitms;
+ int res;
+ char *ptr;
+ int num;
+
+ if (ast_test_flag(flags, OPTION_EXIT)) {
+ const char *c;
+ if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
+ ast_copy_string(exitcontext, c, sizeof(exitcontext));
+ else if (!ast_strlen_zero(chan->macrocontext))
+ ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
+ else
+ ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
+ }
+
+ 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 (;;) {
+ if (!ast_test_flag(flags, OPTION_QUIET)) {
+ 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;
+ }
+ if (!ast_strlen_zero(exitcontext)) {
+ char tmp[2];
+ tmp[0] = res;
+ tmp[1] = '\0';
+ if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
+ goto exit;
+ else
+ ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
+ }
+ }
+
+ res = ast_waitfordigit(chan, waitms);
+ if (res < 0) {
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+ break;
+ }
+ if (!ast_strlen_zero(exitcontext)) {
+ char tmp[2];
+ tmp[0] = res;
+ tmp[1] = '\0';
+ if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
+ goto exit;
+ else
+ ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
+ }
+
+ /* reset for the next loop around, unless overridden later */
+ waitms = 100;
+ peer = prev = next = NULL;
+
+ for (peer = next_channel(peer, spec, exten, context);
+ peer;
+ prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
+ const char *group;
+ int igrp = !mygroup;
+ char *groups[25];
+ int num_groups = 0;
+ char *dup_group;
+ int x;
+ char *s;
+
+ if (peer == prev)
+ break;
+
+ if (peer == chan)
+ continue;
+
+ if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
+ continue;
+
+ if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
+ continue;
+
+ if (mygroup) {
+ if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
+ dup_group = ast_strdupa(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)
+ continue;
+
+ strcpy(peer_name, "spy-");
+ strncat(peer_name, peer->name, AST_NAME_STRLEN);
+ ptr = strchr(peer_name, '/');
+ *ptr++ = '\0';
+
+ for (s = peer_name; s < ptr; s++)
+ *s = tolower(*s);
+
+ 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)
+ break;
+ } else
+ res = ast_say_character_str(chan, peer_name, "", chan->language);
+ if ((num = atoi(ptr)))
+ ast_say_digits(chan, atoi(ptr), "", chan->language);
+ }
+
+ waitms = 5000;
+ res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
+
+ if (res == -1) {
+ goto exit;
+ } else if (res == -2) {
+ res = 0;
+ goto exit;
+ } else if (res > 1 && spec) {
+ snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
+ if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
+ ast_channel_unlock(next);
+ } else {
+ /* stay on this channel */
+ next = peer;
+ }
+ peer = NULL;
+ }
+ }
+ }
+exit:
+
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+
+ ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
+
+ return res;
+}
+
+static int chanspy_exec(struct ast_channel *chan, void *data)
+{
+ char *mygroup = NULL;
+ char *recbase = NULL;
+ int fd = 0;
+ struct ast_flags flags;
+ int oldwf = 0;
+ int volfactor = 0;
+ int res;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(spec);
+ AST_APP_ARG(options);
+ );
+ char *opts[OPT_ARG_ARRAY_SIZE];
+
+ data = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.spec && !strcmp(args.spec, "all"))
+ args.spec = NULL;
+
+ if (args.options) {
+ ast_app_parse_options(spy_opts, &flags, opts, args.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");
+ return -1;
+ }
+
+ if (recbase) {
+ char filename[512];
+
+ 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, AST_FILE_MODE)) <= 0) {
+ ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
+ fd = 0;
+ }
+ }
+
+ res = common_exec(chan, &flags, volfactor, fd, mygroup, args.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");
+
+ return res;
+}
+
+static int extenspy_exec(struct ast_channel *chan, void *data)
+{
+ char *ptr, *exten = NULL;
+ char *mygroup = NULL;
+ char *recbase = NULL;
+ int fd = 0;
+ struct ast_flags flags;
+ int oldwf = 0;
+ int volfactor = 0;
+ int res;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(context);
+ AST_APP_ARG(options);
+ );
+
+ data = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, data);
+ if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
+ exten = args.context;
+ *ptr++ = '\0';
+ args.context = ptr;
+ }
+
+ if (ast_strlen_zero(args.context))
+ args.context = ast_strdupa(chan->context);
+
+ if (args.options) {
+ char *opts[OPT_ARG_ARRAY_SIZE];
+
+ ast_app_parse_options(spy_opts, &flags, opts, args.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");
+ return -1;
+ }
+
+ if (recbase) {
+ char filename[512];
+
+ 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, AST_FILE_MODE)) <= 0) {
+ ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
+ fd = 0;
+ }
+ }
+
+ res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
+
+ if (fd)
+ close(fd);
+
+ if (oldwf && ast_set_write_format(chan, oldwf) < 0)
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_unregister_application(app_chan);
+ res |= ast_unregister_application(app_ext);
+
+ 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/trunk/apps/app_controlplayback.c b/trunk/apps/app_controlplayback.c
new file mode 100644
index 000000000..b6dfd898e
--- /dev/null
+++ b/trunk/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 "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.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"
+" o(#) - Start at # ms from the beginning of the file.\n"
+"This application sets the following channel variables upon completion:\n"
+" CPLAYBACKSTATUS - This variable contains the status of the attempt as a text\n"
+" string, one of: SUCCESS | USERSTOPPED | ERROR\n"
+" CPLAYBACKOFFSET - This contains the offset in ms into the file where\n"
+" playback was at when it stopped. -1 is end of file.\n"
+" CPLAYBACKSTOPKEY - If the playback is stopped by the user this variable contains\n"
+" the key that was pressed.\n";
+
+enum {
+ OPT_OFFSET = (1 << 1),
+};
+
+enum {
+ OPT_ARG_OFFSET = 0,
+ /* must stay as the last entry ... */
+ OPT_ARG_ARRAY_LEN,
+};
+
+AST_APP_OPTIONS(cpb_opts, BEGIN_OPTIONS
+ AST_APP_OPTION_ARG('o', OPT_OFFSET, OPT_ARG_OFFSET),
+END_OPTIONS );
+
+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;
+ int skipms = 0;
+ long offsetms = 0;
+ char offsetbuf[20];
+ char stopkeybuf[2];
+ char *tmp;
+ struct ast_flags opts = { 0, };
+ char *opt_args[OPT_ARG_ARRAY_LEN];
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(skip);
+ AST_APP_ARG(fwd);
+ AST_APP_ARG(rev);
+ AST_APP_ARG(stop);
+ AST_APP_ARG(pause);
+ AST_APP_ARG(restart);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
+ return -1;
+ }
+
+ tmp = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if (args.argc < 1) {
+ ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
+ return -1;
+ }
+
+ skipms = args.skip ? (atoi(args.skip) ? atoi(args.skip) : 3000) : 3000;
+
+ if (!args.fwd || !is_on_phonepad(*args.fwd))
+ args.fwd = "#";
+ if (!args.rev || !is_on_phonepad(*args.rev))
+ args.rev = "*";
+ if (args.stop && !is_on_phonepad(*args.stop))
+ args.stop = NULL;
+ if (args.pause && !is_on_phonepad(*args.pause))
+ args.pause = NULL;
+ if (args.restart && !is_on_phonepad(*args.restart))
+ args.restart = NULL;
+
+ if (args.options) {
+ ast_app_parse_options(cpb_opts, &opts, opt_args, args.options);
+ if (ast_test_flag(&opts, OPT_OFFSET))
+ offsetms = atol(opt_args[OPT_ARG_OFFSET]);
+ }
+
+ res = ast_control_streamfile(chan, args.filename, args.fwd, args.rev, args.stop, args.pause, args.restart, skipms, &offsetms);
+
+ /* If we stopped on one of our stop keys, return 0 */
+ if (res > 0 && args.stop && strchr(args.stop, res)) {
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
+ snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res);
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf);
+ res = 0;
+ } else {
+ if (res < 0) {
+ res = 0;
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
+ } else
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
+ }
+
+ snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms);
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf);
+
+ 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/trunk/apps/app_db.c b/trunk/apps/app_db.c
new file mode 100644
index 000000000..0cc5043ce
--- /dev/null
+++ b/trunk/apps/app_db.c
@@ -0,0 +1,139 @@
+/*
+ * 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 "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/astdb.h"
+#include "asterisk/lock.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;
+
+ argv = ast_strdupa(data);
+
+ if (strchr(argv, '/')) {
+ family = strsep(&argv, "/");
+ keytree = strsep(&argv, "\0");
+ if (!family || !keytree) {
+ ast_debug(1, "Ignoring; Syntax error in argument\n");
+ return 0;
+ }
+ if (ast_strlen_zero(keytree))
+ keytree = 0;
+ } else {
+ family = argv;
+ keytree = 0;
+ }
+
+ if (keytree)
+ ast_verb(3, "DBdeltree: family=%s, keytree=%s\n", family, keytree);
+ else
+ ast_verb(3, "DBdeltree: family=%s\n", family);
+
+ if (ast_db_deltree(family, keytree))
+ ast_verb(3, "DBdeltree: Error deleting key from database.\n");
+
+ return 0;
+}
+
+static int del_exec(struct ast_channel *chan, void *data)
+{
+ char *argv, *family, *key;
+ static int deprecation_warning = 0;
+
+ 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_debug(1, "Ignoring; Syntax error in argument\n");
+ return 0;
+ }
+ ast_verb(3, "DBdel: family=%s, key=%s\n", family, key);
+ if (ast_db_del(family, key))
+ ast_verb(3, "DBdel: Error deleting key from database.\n");
+ } else {
+ ast_debug(1, "Ignoring, no parameters\n");
+ }
+
+ 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/trunk/apps/app_dial.c b/trunk/apps/app_dial.c
new file mode 100644
index 000000000..db1f76c8d
--- /dev/null
+++ b/trunk/apps/app_dial.c
@@ -0,0 +1,2047 @@
+/*
+ * 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
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.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"
+" c - If DIAL cancels this call, always set the flag to tell the channel\n"
+" driver that the call is answered elsewhere.\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"
+" e - execute the 'h' extension for peer after the call ends\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.\n"
+" H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n"
+" i - Asterisk will ignore any forwarding requests it may receive on this\n"
+" dial attempt.\n"
+" k - Allow the called party to enable parking of the call by sending\n"
+" the DTMF sequence defined for call parking in 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 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.\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 features.conf.\n"
+" T - Allow the calling party to transfer the called party by sending the\n"
+" DTMF sequence defined in features.conf.\n"
+" U(x[^arg]) - Execute via Gosub the routine 'x' for the *called* channel before connecting\n"
+" to the calling channel. Arguments can be specified to the Gosub\n"
+" using '^' as a delimeter. The Gosub routine can set the variable\n"
+" GOSUB_RESULT to specify the following actions after the Gosub returns.\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.\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 routine.\n"
+" w - Allow the called party to enable recording of the call by sending\n"
+" the DTMF sequence defined for one-touch recording in features.conf.\n"
+" W - Allow the calling party to enable recording of the call by sending\n"
+" the DTMF sequence defined for one-touch recording in features.conf.\n"
+" x - Allow the called party to enable recording of the call by sending\n"
+" the DTMF sequence defined for one-touch automixmonitor in features.conf\n"
+" X - Allow the calling party to enable recording of the call by sending\n"
+" the DTMF sequence defined for one-touch automixmonitor in 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 retying the call. After 'retires' 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_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),
+ OPT_CALLEE_GOSUB = (1 << 28),
+ OPT_CALLEE_MIXMONITOR = (1 << 29),
+ OPT_CALLER_MIXMONITOR = (1 << 30),
+};
+
+#define DIAL_STILLGOING (1 << 31)
+#define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */
+#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33)
+#define OPT_PEER_H ((uint64_t)1 << 34)
+
+enum {
+ OPT_ARG_ANNOUNCE = 0,
+ OPT_ARG_SENDDTMF,
+ OPT_ARG_GOTO,
+ OPT_ARG_DURATION_LIMIT,
+ OPT_ARG_MUSICBACK,
+ OPT_ARG_CALLEE_MACRO,
+ OPT_ARG_CALLEE_GOSUB,
+ 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,
+};
+
+AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
+ AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
+ AST_APP_OPTION('C', OPT_RESETCDR),
+ AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
+ AST_APP_OPTION('d', OPT_DTMF_EXIT),
+ AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
+ AST_APP_OPTION('e', OPT_PEER_H),
+ 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('k', OPT_CALLEE_PARK),
+ AST_APP_OPTION('K', OPT_CALLER_PARK),
+ 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_ARG('U', OPT_CALLEE_GOSUB, OPT_ARG_CALLEE_GOSUB),
+ AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
+ AST_APP_OPTION('W', OPT_CALLER_MONITOR),
+ AST_APP_OPTION('x', OPT_CALLEE_MIXMONITOR),
+ AST_APP_OPTION('X', OPT_CALLER_MIXMONITOR),
+END_OPTIONS );
+
+#define CAN_EARLY_BRIDGE(flags) (!ast_test_flag64(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))
+
+/*
+ * The list of active channels
+ */
+struct chanlist {
+ struct chanlist *next;
+ struct ast_channel *chan;
+ uint64_t flags;
+};
+
+
+static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
+{
+ /* Hang up a tree of stuff */
+ struct chanlist *oo;
+ while (outgoing) {
+ /* Hangup any existing lines we have open */
+ if (outgoing->chan && (outgoing->chan != exception)) {
+ if (answered_elsewhere)
+ ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
+ ast_hangup(outgoing->chan);
+ }
+ oo = outgoing;
+ outgoing = outgoing->next;
+ ast_free(oo);
+ }
+}
+
+#define AST_MAX_WATCHERS 256
+
+/*
+ * argument to handle_cause() and other functions.
+ */
+struct cause_args {
+ struct ast_channel *chan;
+ int busy;
+ int congestion;
+ int nochan;
+};
+
+static void handle_cause(int cause, struct cause_args *num)
+{
+ struct ast_cdr *cdr = num->chan->cdr;
+
+ switch(cause) {
+ case AST_CAUSE_BUSY:
+ if (cdr)
+ ast_cdr_busy(cdr);
+ num->busy++;
+ break;
+
+ case AST_CAUSE_CONGESTION:
+ if (cdr)
+ ast_cdr_failed(cdr);
+ num->congestion++;
+ break;
+
+ case AST_CAUSE_UNREGISTERED:
+ if (cdr)
+ ast_cdr_failed(cdr);
+ num->nochan++;
+ break;
+
+ case AST_CAUSE_NORMAL_CLEARING:
+ break;
+
+ default:
+ num->nochan++;
+ break;
+ }
+}
+
+/* free the buffer if allocated, and set the pointer to the second arg */
+#define S_REPLACE(s, new_val) \
+ do { \
+ if (s) \
+ ast_free(s); \
+ s = (new_val); \
+ } 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, const char *dialstring)
+{
+ manager_event(EVENT_FLAG_CALL, "Dial",
+ "SubEvent: Begin\r\n"
+ "Channel: %s\r\n"
+ "Destination: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "UniqueID: %s\r\n"
+ "DestUniqueID: %s\r\n"
+ "Dialstring: %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, dialstring ? dialstring : "");
+}
+
+static void senddialendevent(const struct ast_channel *src, const char *dialstatus)
+{
+ manager_event(EVENT_FLAG_CALL, "Dial",
+ "SubEvent: End\r\n"
+ "Channel: %s\r\n"
+ "UniqueID: %s\r\n"
+ "DialStatus: %s\r\n",
+ src->name, src->uniqueid, dialstatus);
+}
+
+/*!
+ * helper function for wait_for_answer()
+ *
+ * XXX this code is highly suspicious, as it essentially overwrites
+ * the outgoing channel without properly deleting it.
+ */
+static void do_forward(struct chanlist *o,
+ struct cause_args *num, struct ast_flags64 *peerflags, int single)
+{
+ char tmpchan[256];
+ struct ast_channel *original = o->chan;
+ struct ast_channel *c = o->chan; /* the winner */
+ struct ast_channel *in = num->chan; /* the input channel */
+ 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 */
+ ast_verb(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_flag64(peerflags, OPT_IGNORE_FORWARDING)) {
+ ast_verb(3, "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff);
+ c = o->chan = NULL;
+ cause = AST_CAUSE_BUSY;
+ } else {
+ /* Setup parameters */
+ c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
+ if (c) {
+ 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_flag64(o, DIAL_STILLGOING);
+ handle_cause(cause, num);
+ } else {
+ char *new_cid_num, *new_cid_name;
+ struct ast_channel *src;
+
+ ast_rtp_make_compatible(c, in, single);
+ if (ast_test_flag64(o, OPT_FORCECLID)) {
+ new_cid_num = ast_strdup(S_OR(in->macroexten, in->exten));
+ new_cid_name = NULL; /* XXX no name ? */
+ src = c; /* XXX possible bug in previous code, which used 'winner' ? it may have changed */
+ } else {
+ new_cid_num = ast_strdup(in->cid.cid_num);
+ new_cid_name = ast_strdup(in->cid.cid_name);
+ src = in;
+ }
+ ast_string_field_set(c, accountcode, src->accountcode);
+ c->cdrflags = src->cdrflags;
+ S_REPLACE(c->cid.cid_num, new_cid_num);
+ S_REPLACE(c->cid.cid_name, new_cid_name);
+
+ if (in->cid.cid_ani) { /* XXX or maybe unconditional ? */
+ S_REPLACE(c->cid.cid_ani, ast_strdup(in->cid.cid_ani));
+ }
+ S_REPLACE(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_flag64(o, DIAL_STILLGOING);
+ ast_hangup(original);
+ c = o->chan = NULL;
+ num->nochan++;
+ } else {
+ senddialevent(in, c, stuff);
+ /* After calling, set callerid to extension */
+ if (!ast_test_flag64(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(original);
+ }
+ }
+}
+
+/* argument used for some functions. */
+struct privacy_args {
+ int sentringing;
+ int privdb_val;
+ char privcid[256];
+ char privintro[1024];
+ char status[256];
+};
+
+static struct ast_channel *wait_for_answer(struct ast_channel *in,
+ struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags,
+ struct privacy_args *pa,
+ const struct cause_args *num_in, int *result)
+{
+ struct cause_args num = *num_in;
+ int prestart = num.busy + num.congestion + num.nochan;
+ int orig = *to;
+ struct ast_channel *peer = NULL;
+ /* single is set if only one destination is enabled */
+ int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
+#ifdef HAVE_EPOLL
+ struct chanlist *epollo;
+#endif
+
+ 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);
+ }
+
+#ifdef HAVE_EPOLL
+ for (epollo = outgoing; epollo; epollo = epollo->next)
+ ast_poll_channel_add(in, epollo->chan);
+#endif
+
+ while (*to && !peer) {
+ struct chanlist *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_flag64(o, DIAL_STILLGOING) && o->chan)
+ watchers[pos++] = o->chan;
+ numlines++;
+ }
+ if (pos == 1) { /* only the input channel is available */
+ if (numlines == (num.busy + num.congestion + num.nochan)) {
+ ast_verb(2, "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
+ if (num.busy)
+ strcpy(pa->status, "BUSY");
+ else if (num.congestion)
+ strcpy(pa->status, "CONGESTION");
+ else if (num.nochan)
+ strcpy(pa->status, "CHANUNAVAIL");
+ } else {
+ ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
+ }
+ *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_flag64(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) {
+ if (!peer) {
+ ast_verb(3, "%s answered %s\n", c->name, in->name);
+ peer = c;
+ ast_copy_flags64(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 |
+ OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
+ DIAL_NOFORWARDHTML);
+ ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
+ ast_copy_string(c->exten, "", sizeof(c->exten));
+ }
+ continue;
+ }
+ if (c != winner)
+ continue;
+ /* here, o->chan == c == winner */
+ if (!ast_strlen_zero(c->call_forward)) {
+ do_forward(o, &num, peerflags, single);
+ continue;
+ }
+ f = ast_read(winner);
+ if (!f) {
+ in->hangupcause = c->hangupcause;
+#ifdef HAVE_EPOLL
+ ast_poll_channel_del(in, c);
+#endif
+ ast_hangup(c);
+ c = o->chan = NULL;
+ ast_clear_flag64(o, DIAL_STILLGOING);
+ handle_cause(in->hangupcause, &num);
+ continue;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch(f->subclass) {
+ case AST_CONTROL_ANSWER:
+ /* This is our guy if someone answered. */
+ if (!peer) {
+ ast_verb(3, "%s answered %s\n", c->name, in->name);
+ peer = c;
+ ast_copy_flags64(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 |
+ OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
+ DIAL_NOFORWARDHTML);
+ ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
+ ast_copy_string(c->exten, "", sizeof(c->exten));
+ if (CAN_EARLY_BRIDGE(peerflags))
+ /* Setup early bridge if appropriate */
+ ast_channel_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:
+ ast_verb(3, "%s is busy\n", c->name);
+ in->hangupcause = c->hangupcause;
+ ast_hangup(c);
+ c = o->chan = NULL;
+ ast_clear_flag64(o, DIAL_STILLGOING);
+ handle_cause(AST_CAUSE_BUSY, &num);
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(3, "%s is circuit-busy\n", c->name);
+ in->hangupcause = c->hangupcause;
+ ast_hangup(c);
+ c = o->chan = NULL;
+ ast_clear_flag64(o, DIAL_STILLGOING);
+ handle_cause(AST_CAUSE_CONGESTION, &num);
+ break;
+ case AST_CONTROL_RINGING:
+ ast_verb(3, "%s is ringing\n", c->name);
+ /* Setup early media if appropriate */
+ if (single && CAN_EARLY_BRIDGE(peerflags))
+ ast_channel_early_bridge(in, c);
+ if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK)) {
+ ast_indicate(in, AST_CONTROL_RINGING);
+ pa->sentringing++;
+ }
+ break;
+ case AST_CONTROL_PROGRESS:
+ ast_verb(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))
+ ast_channel_early_bridge(in, c);
+ if (!ast_test_flag64(outgoing, OPT_RINGBACK))
+ ast_indicate(in, AST_CONTROL_PROGRESS);
+ break;
+ case AST_CONTROL_VIDUPDATE:
+ ast_verb(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_PROCEEDING:
+ ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name);
+ if (single && CAN_EARLY_BRIDGE(peerflags))
+ ast_channel_early_bridge(in, c);
+ if (!ast_test_flag64(outgoing, OPT_RINGBACK))
+ ast_indicate(in, AST_CONTROL_PROCEEDING);
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verb(3, "Call on %s placed on hold\n", c->name);
+ ast_indicate(in, AST_CONTROL_HOLD);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verb(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_flag64(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
+ ast_verb(3, "%s stopped sounds\n", c->name);
+ ast_indicate(in, -1);
+ pa->sentringing = 0;
+ }
+ break;
+ default:
+ ast_debug(1, "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_flag64(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_flag64(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_flag64(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_flag64(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;
+ strcpy(pa->status, "CANCEL");
+ ast_cdr_noanswer(in->cdr);
+ if (f)
+ ast_frfree(f);
+ return NULL;
+ }
+
+ /* now f is guaranteed non-NULL */
+ if (f->frametype == AST_FRAME_DTMF) {
+ if (ast_test_flag64(peerflags, OPT_DTMF_EXIT)) {
+ const char *context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
+ if (onedigit_goto(in, context, (char) f->subclass, 1)) {
+ ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
+ *to = 0;
+ ast_cdr_noanswer(in->cdr);
+ *result = f->subclass;
+ strcpy(pa->status, "CANCEL");
+ ast_frfree(f);
+ return NULL;
+ }
+ }
+
+ if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
+ (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */
+ ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
+ *to = 0;
+ strcpy(pa->status, "CANCEL");
+ ast_cdr_noanswer(in->cdr);
+ ast_frfree(f);
+ return NULL;
+ }
+ }
+
+ /* Forward HTML stuff */
+ if (single && (f->frametype == AST_FRAME_HTML) && !ast_test_flag64(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))) {
+ ast_verb(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)
+ ast_verb(3, "Nobody picked up in %d ms\n", orig);
+ if (!*to || ast_check_hangup(in)) {
+ ast_cdr_noanswer(in->cdr);
+ }
+
+ }
+ if (peer && !ast_cdr_log_unanswered()) {
+ /* suppress the CDR's that didn't win */
+ struct chanlist *o;
+ for (o = outgoing; o; o = o->next) {
+ struct ast_channel *c = o->chan;
+ if (c && c != peer && c->cdr) {
+ ast_set_flag(c->cdr, AST_CDR_FLAG_POST_DISABLED);
+ }
+ }
+ } else if (!peer && !ast_cdr_log_unanswered()) {
+ /* suppress the CDR's that didn't win */
+ struct chanlist *o;
+ for (o = outgoing; o; o = o->next) {
+ struct ast_channel *c = o->chan;
+ if (c && c->cdr) {
+ ast_set_flag(c->cdr, AST_CDR_FLAG_POST_DISABLED);
+ }
+ }
+ }
+
+#ifdef HAVE_EPOLL
+ for (epollo = outgoing; epollo; epollo = epollo->next) {
+ if (epollo->chan)
+ ast_poll_channel_del(in, epollo->chan);
+ }
+#endif
+
+ 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_flags64 *opts, int res)
+{
+ if (res < '1')
+ return 0;
+ if (ast_test_flag64(opts, OPT_PRIVACY) && res <= '5')
+ return 1;
+ if (ast_test_flag64(opts, OPT_SCREENING) && res <= '4')
+ return 1;
+ return 0;
+}
+
+static int do_timelimit(struct ast_channel *chan, struct ast_bridge_config *config,
+ char *parse, unsigned int *calldurationlimit)
+{
+ char *stringp = ast_strdupa(parse);
+ char *limit_str, *warning_str, *warnfreq_str;
+ const char *var;
+ int play_to_caller = 0, play_to_callee = 0;
+ int delta;
+
+ limit_str = strsep(&stringp, ":");
+ warning_str = strsep(&stringp, ":");
+ warnfreq_str = strsep(&stringp, ":");
+
+ config->timelimit = atol(limit_str);
+ if (warning_str)
+ config->play_warning = atol(warning_str);
+ if (warnfreq_str)
+ config->warning_freq = atol(warnfreq_str);
+
+ if (!config->timelimit) {
+ ast_log(LOG_WARNING, "Dial does not accept L(%s), hanging up.\n", limit_str);
+ config->timelimit = config->play_warning = config->warning_freq = 0;
+ config->warning_sound = NULL;
+ return -1; /* error */
+ } else if ( (delta = config->play_warning - config->timelimit) > 0) {
+ int w = config->warning_freq;
+
+ /* If the first warning is requested _after_ the entire call would end,
+ and no warning frequency is requested, then turn off the warning. If
+ a warning frequency is requested, reduce the 'first warning' time by
+ that frequency until it falls within the call's total time limit.
+ Graphically:
+ timelim->| delta |<-playwarning
+ 0__________________|_________________|
+ | w | | | |
+
+ so the number of intervals to cut is 1+(delta-1)/w
+ */
+
+ if (w == 0) {
+ config->play_warning = 0;
+ } else {
+ config->play_warning -= w * ( 1 + (delta-1)/w );
+ if (config->play_warning < 1)
+ config->play_warning = config->warning_freq = 0;
+ }
+ }
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");
+ play_to_caller = var ? ast_true(var) : 1;
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
+ play_to_callee = var ? ast_true(var) : 0;
+
+ if (!play_to_caller && !play_to_callee)
+ play_to_caller = 1;
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
+ config->warning_sound = S_OR(var, "timeleft");
+
+ /* The code looking at config wants a NULL, not just "", to decide
+ * that the message should not be played, so we replace "" with NULL.
+ * Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
+ * not found.
+ */
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
+ config->end_sound = S_OR(var, NULL);
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
+ config->start_sound = S_OR(var, NULL);
+
+ /* undo effect of S(x) in case they are both used */
+ *calldurationlimit = 0;
+ /* more efficient to do it like S(x) does since no advanced opts */
+ if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
+ *calldurationlimit = config->timelimit / 1000;
+ ast_verb(3, "Setting call duration limit to %d seconds.\n",
+ *calldurationlimit);
+ config->timelimit = play_to_caller = play_to_callee =
+ config->play_warning = config->warning_freq = 0;
+ } else {
+ ast_verb(3, "Limit Data for this call:\n");
+ ast_verb(4, "timelimit = %ld\n", config->timelimit);
+ ast_verb(4, "play_warning = %ld\n", config->play_warning);
+ ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
+ ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
+ ast_verb(4, "warning_freq = %ld\n", config->warning_freq);
+ ast_verb(4, "start_sound = %s\n", S_OR(config->start_sound, ""));
+ ast_verb(4, "warning_sound = %s\n", config->warning_sound);
+ ast_verb(4, "end_sound = %s\n", S_OR(config->end_sound, ""));
+ }
+ if (play_to_caller)
+ ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
+ if (play_to_callee)
+ ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
+ return 0;
+}
+
+static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
+ struct ast_flags64 *opts, char **opt_args, struct privacy_args *pa)
+{
+
+ int res2;
+ int loopcount = 0;
+
+ /* Get the user's intro, store it in priv-callerintros/$CID,
+ unless it is already there-- this should be done before the
+ call is actually dialed */
+
+ /* all ring indications and moh for the caller has been halted as soon as the
+ target extension was picked up. We are going to have to kill some
+ time and make the caller believe the peer hasn't picked up yet */
+
+ if (ast_test_flag64(opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
+ char *original_moh = ast_strdupa(chan->musicclass);
+ ast_indicate(chan, -1);
+ ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
+ ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
+ ast_string_field_set(chan, musicclass, original_moh);
+ } else if (ast_test_flag64(opts, OPT_RINGBACK)) {
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ pa->sentringing++;
+ }
+
+ /* Start autoservice on the other chan ?? */
+ res2 = ast_autoservice_start(chan);
+ /* Now Stream the File */
+ for (loopcount = 0; loopcount < 3; loopcount++) {
+ if (res2 && loopcount == 0) /* error in ast_autoservice_start() */
+ break;
+ if (!res2) /* on timeout, play the message again */
+ res2 = ast_play_and_wait(peer, "priv-callpending");
+ if (!valid_priv_reply(opts, res2))
+ res2 = 0;
+ /* priv-callpending script:
+ "I have a caller waiting, who introduces themselves as:"
+ */
+ if (!res2)
+ res2 = ast_play_and_wait(peer, pa->privintro);
+ if (!valid_priv_reply(opts, res2))
+ res2 = 0;
+ /* now get input from the called party, as to their choice */
+ if (!res2) {
+ /* XXX can we have both, or they are mutually exclusive ? */
+ if (ast_test_flag64(opts, OPT_PRIVACY))
+ res2 = ast_play_and_wait(peer, "priv-callee-options");
+ if (ast_test_flag64(opts, OPT_SCREENING))
+ res2 = ast_play_and_wait(peer, "screen-callee-options");
+ }
+ /*! \page DialPrivacy Dial Privacy scripts
+ \par priv-callee-options script:
+ "Dial 1 if you wish this caller to reach you directly in the future,
+ and immediately connect to their incoming call
+ Dial 2 if you wish to send this caller to voicemail now and
+ forevermore.
+ Dial 3 to send this caller to the torture menus, now and forevermore.
+ Dial 4 to send this caller to a simple "go away" menu, now and forevermore.
+ Dial 5 to allow this caller to come straight thru to you in the future,
+ but right now, just this once, send them to voicemail."
+ \par screen-callee-options script:
+ "Dial 1 if you wish to immediately connect to the incoming call
+ Dial 2 if you wish to send this caller to voicemail.
+ Dial 3 to send this caller to the torture menus.
+ Dial 4 to send this caller to a simple "go away" menu.
+ */
+ if (valid_priv_reply(opts, res2))
+ break;
+ /* invalid option */
+ res2 = ast_play_and_wait(peer, "vm-sorry");
+ }
+
+ if (ast_test_flag64(opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+ } else if (ast_test_flag64(opts, OPT_RINGBACK)) {
+ ast_indicate(chan, -1);
+ pa->sentringing = 0;
+ }
+ ast_autoservice_stop(chan);
+ if (ast_test_flag64(opts, OPT_PRIVACY) && (res2 >= '1' && res2 <= '5')) {
+ /* map keypresses to various things, the index is res2 - '1' */
+ static const char *_val[] = { "ALLOW", "DENY", "TORTURE", "KILL", "ALLOW" };
+ static const int _flag[] = { AST_PRIVACY_ALLOW, AST_PRIVACY_DENY, AST_PRIVACY_TORTURE, AST_PRIVACY_KILL, AST_PRIVACY_ALLOW};
+ int i = res2 - '1';
+ ast_verb(3, "--Set privacy database entry %s/%s to %s\n",
+ opt_args[OPT_ARG_PRIVACY], pa->privcid, _val[i]);
+ ast_privacy_set(opt_args[OPT_ARG_PRIVACY], pa->privcid, _flag[i]);
+ }
+ switch (res2) {
+ case '1':
+ break;
+ case '2':
+ ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
+ break;
+ case '3':
+ ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
+ break;
+ case '4':
+ ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
+ break;
+ case '5':
+ /* XXX should we set status to DENY ? */
+ if (ast_test_flag64(opts, OPT_PRIVACY))
+ break;
+ /* if not privacy, then 5 is the same as "default" case */
+ default: /* bad input or -1 if failure to start autoservice */
+ /* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do? */
+ /* well, there seems basically two choices. Just patch the caller thru immediately,
+ or,... put 'em thru to voicemail. */
+ /* since the callee may have hung up, let's do the voicemail thing, no database decision */
+ ast_log(LOG_NOTICE, "privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n");
+ /* XXX should we set status to DENY ? */
+ /* XXX what about the privacy flags ? */
+ break;
+ }
+
+ if (res2 == '1') { /* the only case where we actually connect */
+ /* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll
+ just clog things up, and it's not useful information, not being tied to a CID */
+ if (strncmp(pa->privcid, "NOCALLERID", 10) == 0 || ast_test_flag64(opts, OPT_SCREEN_NOINTRO)) {
+ ast_filedelete(pa->privintro, NULL);
+ if (ast_fileexists(pa->privintro, NULL, NULL) > 0)
+ ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", pa->privintro);
+ else
+ ast_verb(3, "Successfully deleted %s intro file\n", pa->privintro);
+ }
+ return 0; /* the good exit path */
+ } else {
+ ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
+ return -1;
+ }
+}
+
+/*! \brief returns 1 if successful, 0 or <0 if the caller should 'goto out' */
+static int setup_privacy_args(struct privacy_args *pa,
+ struct ast_flags64 *opts, char *opt_args[], struct ast_channel *chan)
+{
+ char callerid[60];
+ int res;
+ char *l;
+
+ if (!ast_strlen_zero(chan->cid.cid_num)) {
+ l = ast_strdupa(chan->cid.cid_num);
+ ast_shrink_phone_number(l);
+ if (ast_test_flag64(opts, OPT_PRIVACY) ) {
+ ast_verb(3, "Privacy DB is '%s', clid is '%s'\n", opt_args[OPT_ARG_PRIVACY], l);
+ pa->privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
+ } else {
+ ast_verb(3, "Privacy Screening, clid is '%s'\n", l);
+ pa->privdb_val = AST_PRIVACY_UNKNOWN;
+ }
+ } else {
+ char *tnam, *tn2;
+
+ tnam = ast_strdupa(chan->name);
+ /* clean the channel name so slashes don't try to end up in disk file name */
+ for (tn2 = tnam; *tn2; tn2++) {
+ if (*tn2 == '/') /* any other chars to be afraid of? */
+ *tn2 = '=';
+ }
+ ast_verb(3, "Privacy-- callerid is empty\n");
+
+ snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam);
+ l = callerid;
+ pa->privdb_val = AST_PRIVACY_UNKNOWN;
+ }
+
+ ast_copy_string(pa->privcid, l, sizeof(pa->privcid));
+
+ if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCLID)) {
+ /* if callerid is set and OPT_SCREEN_NOCLID is set also */
+ ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid);
+ pa->privdb_val = AST_PRIVACY_ALLOW;
+ } else if (ast_test_flag64(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) {
+ ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val);
+ }
+
+ if (pa->privdb_val == AST_PRIVACY_DENY) {
+ ast_verb(3, "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
+ ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
+ return 0;
+ } else if (pa->privdb_val == AST_PRIVACY_KILL) {
+ ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
+ return 0; /* Is this right? */
+ } else if (pa->privdb_val == AST_PRIVACY_TORTURE) {
+ ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
+ return 0; /* is this right??? */
+ } else if (pa->privdb_val == AST_PRIVACY_UNKNOWN) {
+ /* Get the user's intro, store it in priv-callerintros/$CID,
+ unless it is already there-- this should be done before the
+ call is actually dialed */
+
+ /* make sure the priv-callerintros dir actually exists */
+ snprintf(pa->privintro, sizeof(pa->privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR);
+ if ((res = ast_mkdir(pa->privintro, 0755))) {
+ ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(res));
+ return -1;
+ }
+
+ snprintf(pa->privintro, sizeof(pa->privintro), "priv-callerintros/%s", pa->privcid);
+ if (ast_fileexists(pa->privintro, NULL, NULL ) > 0 && strncmp(pa->privcid, "NOCALLERID", 10) != 0) {
+ /* the DELUX version of this code would allow this caller the
+ option to hear and retape their previously recorded intro.
+ */
+ } else {
+ int duration; /* for feedback from play_and_wait */
+ /* the file doesn't exist yet. Let the caller submit his
+ vocal intro for posterity */
+ /* priv-recordintro script:
+
+ "At the tone, please say your name:"
+
+ */
+ ast_answer(chan);
+ res = ast_play_and_record(chan, "priv-recordintro", pa->privintro, 4, "gsm", &duration, 128, 2000, 0); /* NOTE: I've reduced the total time to 4 sec */
+ /* don't think we'll need a lock removed, we took care of
+ conflicts by naming the pa.privintro file */
+ if (res == -1) {
+ /* Delete the file regardless since they hung up during recording */
+ ast_filedelete(pa->privintro, NULL);
+ if (ast_fileexists(pa->privintro, NULL, NULL) > 0)
+ ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", pa->privintro);
+ else
+ ast_verb(3, "Successfully deleted %s intro file\n", pa->privintro);
+ return -1;
+ }
+ if (!ast_streamfile(chan, "vm-dialout", chan->language) )
+ ast_waitstream(chan, "");
+ }
+ }
+ return 1; /* success */
+}
+
+static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags64 *peerflags, int *continue_exec)
+{
+ int res = -1; /* default: error */
+ char *rest, *cur; /* scan the list of destinations */
+ struct chanlist *outgoing = NULL; /* list of destinations */
+ struct ast_channel *peer;
+ int to; /* timeout */
+ struct cause_args num = { chan, 0, 0, 0 };
+ int cause;
+ char numsubst[256];
+ char cidname[AST_MAX_EXTENSION] = "";
+
+ struct ast_bridge_config config = { { 0, } };
+ unsigned int calldurationlimit = 0;
+ char *dtmfcalled = NULL, *dtmfcalling = NULL;
+ struct privacy_args pa = {
+ .sentringing = 0,
+ .privdb_val = 0,
+ .status = "INVALIDARGS",
+ };
+ int sentringing = 0, moh = 0;
+ const char *outbound_group = NULL;
+ int result = 0;
+ time_t start_time;
+ 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_flags64 opts = { 0, };
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+ struct ast_datastore *datastore = NULL;
+ 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", pa.status);
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options) &&
+ ast_app_parse_options64(dial_exec_options, &opts, opt_args, args.options)) {
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.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", pa.status);
+ goto done;
+ }
+
+ if (ast_test_flag64(&opts, OPT_OPERMODE)) {
+ opermode = ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]) ? 1 : atoi(opt_args[OPT_ARG_OPERMODE]);
+ ast_verb(3, "Setting operator services mode to %d.\n", opermode);
+ }
+
+ if (ast_test_flag64(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) {
+ calldurationlimit = atoi(opt_args[OPT_ARG_DURATION_STOP]);
+ if (!calldurationlimit) {
+ ast_log(LOG_WARNING, "Dial does not accept S(%s), hanging up.\n", opt_args[OPT_ARG_DURATION_STOP]);
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
+ goto done;
+ }
+ ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
+ }
+
+ if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
+ dtmfcalling = opt_args[OPT_ARG_SENDDTMF];
+ dtmfcalled = strsep(&dtmfcalling, ":");
+ }
+
+ if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
+ if (do_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
+ goto done;
+ }
+
+ if (ast_test_flag64(&opts, OPT_RESETCDR) && chan->cdr)
+ ast_cdr_reset(chan->cdr, NULL);
+ if (ast_test_flag64(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
+ opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten);
+
+ if (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) {
+ res = setup_privacy_args(&pa, &opts, opt_args, chan);
+ if (res <= 0)
+ goto out;
+ res = -1; /* reset default */
+ }
+
+ if (continue_exec)
+ *continue_exec = 0;
+
+ /* If a channel group has been specified, get it for use when we create peer channels */
+ if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
+ outbound_group = ast_strdupa(outbound_group);
+ pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
+ } else {
+ outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
+ }
+
+ ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING);
+ /* loop through the list of dial destinations */
+ rest = args.peers;
+ while ((cur = strsep(&rest, "&")) ) {
+ struct chanlist *tmp;
+ struct ast_channel *tc; /* channel for this destination */
+ /* Get a technology/[device:]number pair */
+ char *number = cur;
+ char *interface = ast_strdupa(number);
+ char *tech = strsep(&number, "/");
+ /* find if we already dialed this interface */
+ struct ast_dialed_interface *di;
+ AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
+ num_dialed++;
+ if (!number) {
+ ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n");
+ goto out;
+ }
+ if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ goto out;
+ if (opts.flags) {
+ ast_copy_flags64(tmp, &opts,
+ OPT_CANCEL_ELSEWHERE |
+ OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
+ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
+ OPT_CALLEE_PARK | OPT_CALLER_PARK |
+ OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
+ OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
+ ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);
+ }
+ ast_copy_string(numsubst, number, sizeof(numsubst));
+ /* Request the peer */
+
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
+ ast_channel_unlock(chan);
+
+ if (datastore)
+ dialed_interfaces = datastore->data;
+ else {
+ if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
+ ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
+ ast_free(tmp);
+ goto out;
+ }
+
+ datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+
+ if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
+ ast_free(tmp);
+ goto out;
+ }
+
+ datastore->data = dialed_interfaces;
+ AST_LIST_HEAD_INIT(dialed_interfaces);
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+ }
+
+ AST_LIST_LOCK(dialed_interfaces);
+ AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
+ if (!strcasecmp(di->interface, interface)) {
+ ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
+ di->interface);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(dialed_interfaces);
+
+ if (di) {
+ fulldial++;
+ ast_free(tmp);
+ continue;
+ }
+
+ /* It is always ok to dial a Local interface. We only keep track of
+ * which "real" interfaces have been dialed. The Local channel will
+ * inherit this list so that if it ends up dialing a real interface,
+ * it won't call one that has already been called. */
+ if (strcasecmp(tech, "Local")) {
+ if (!(di = ast_calloc(1, sizeof(*di) + strlen(interface)))) {
+ AST_LIST_UNLOCK(dialed_interfaces);
+ ast_free(tmp);
+ goto out;
+ }
+ strcpy(di->interface, interface);
+
+ AST_LIST_LOCK(dialed_interfaces);
+ AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
+ AST_LIST_UNLOCK(dialed_interfaces);
+ }
+
+ tc = ast_request(tech, chan->nativeformats, numsubst, &cause);
+ if (!tc) {
+ /* If we can't, just go on to the next call */
+ ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
+ tech, cause, ast_cause2str(cause));
+ handle_cause(cause, &num);
+ if (!rest) /* we are on the last destination */
+ chan->hangupcause = cause;
+ ast_free(tmp);
+ continue;
+ }
+ pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);
+
+ /* Setup outgoing SDP to match incoming one */
+ ast_rtp_make_compatible(tc, chan, !outgoing && !rest);
+
+ /* Inherit specially named variables from parent channel */
+ ast_channel_inherit_variables(chan, tc);
+
+ tc->appl = "AppDial";
+ tc->data = "(Outgoing Line)";
+ tc->whentohangup = 0;
+
+ S_REPLACE(tc->cid.cid_num, ast_strdup(chan->cid.cid_num));
+ S_REPLACE(tc->cid.cid_name, ast_strdup(chan->cid.cid_name));
+ S_REPLACE(tc->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
+ S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
+
+ /* Copy language from incoming to outgoing */
+ ast_string_field_set(tc, language, chan->language);
+ ast_string_field_set(tc, accountcode, chan->accountcode);
+ tc->cdrflags = chan->cdrflags;
+ if (ast_strlen_zero(tc->musicclass))
+ ast_string_field_set(tc, musicclass, chan->musicclass);
+ /* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */
+ tc->cid.cid_pres = chan->cid.cid_pres;
+ tc->cid.cid_ton = chan->cid.cid_ton;
+ tc->cid.cid_tns = chan->cid.cid_tns;
+ tc->cid.cid_ani2 = chan->cid.cid_ani2;
+ tc->adsicpe = chan->adsicpe;
+ tc->transfercapability = chan->transfercapability;
+
+ /* If we have an outbound group, set this peer channel to it */
+ if (outbound_group)
+ ast_app_group_set_channel(tc, outbound_group);
+
+ /* Inherit context and extension */
+ if (!ast_strlen_zero(chan->macrocontext))
+ ast_copy_string(tc->dialcontext, chan->macrocontext, sizeof(tc->dialcontext));
+ else
+ ast_copy_string(tc->dialcontext, chan->context, sizeof(tc->dialcontext));
+ if (!ast_strlen_zero(chan->macroexten))
+ ast_copy_string(tc->exten, chan->macroexten, sizeof(tc->exten));
+ else
+ ast_copy_string(tc->exten, chan->exten, sizeof(tc->exten));
+
+ res = ast_call(tc, numsubst, 0); /* Place the call, but don't wait on the answer */
+
+ /* Save the info in cdr's that we called them */
+ if (chan->cdr)
+ ast_cdr_setdestchan(chan->cdr, tc->name);
+
+ /* check the results of ast_call */
+ if (res) {
+ /* Again, keep going even if there's an error */
+ ast_debug(1, "ast call on peer returned %d\n", res);
+ ast_verb(3, "Couldn't call %s\n", numsubst);
+ ast_hangup(tc);
+ tc = NULL;
+ ast_free(tmp);
+ continue;
+ } else {
+ senddialevent(chan, tc, numsubst);
+ ast_verb(3, "Called %s\n", numsubst);
+ if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID))
+ ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL);
+ }
+ /* Put them in the list of outgoing thingies... We're ready now.
+ XXX If we're forcibly removed, these outgoing calls won't get
+ hung up XXX */
+ ast_set_flag64(tmp, DIAL_STILLGOING);
+ tmp->chan = tc;
+ tmp->next = outgoing;
+ outgoing = tmp;
+ /* If this line is up, don't try anybody else */
+ if (outgoing->chan->_state == AST_STATE_UP)
+ break;
+ }
+
+ if (ast_strlen_zero(args.timeout)) {
+ to = -1;
+ } else {
+ to = atoi(args.timeout);
+ if (to > 0)
+ to *= 1000;
+ else
+ ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
+ }
+
+ if (!outgoing) {
+ strcpy(pa.status, "CHANUNAVAIL");
+ if (fulldial == num_dialed) {
+ res = -1;
+ goto out;
+ }
+ } else {
+ /* Our status will at least be NOANSWER */
+ strcpy(pa.status, "NOANSWER");
+ if (ast_test_flag64(outgoing, OPT_MUSICBACK)) {
+ moh = 1;
+ if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
+ char *original_moh = ast_strdupa(chan->musicclass);
+ ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
+ ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
+ ast_string_field_set(chan, musicclass, original_moh);
+ } else {
+ ast_moh_start(chan, NULL, NULL);
+ }
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+ } else if (ast_test_flag64(outgoing, OPT_RINGBACK)) {
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ sentringing++;
+ }
+ }
+
+ time(&start_time);
+ peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result);
+
+ ast_channel_datastore_remove(chan, datastore);
+ ast_channel_datastore_free(datastore);
+ if (!peer) {
+ if (result) {
+ res = result;
+ } else if (to) { /* Musta gotten hung up */
+ res = -1;
+ } else { /* Nobody answered, next please? */
+ res = 0;
+ }
+ /* almost done, although the 'else' block is 400 lines */
+ } else {
+ const char *number;
+ time_t end_time, answer_time = time(NULL);
+ char toast[80]; /* buffer to set variables */
+
+ strcpy(pa.status, "ANSWER");
+ /* Ah ha! Someone answered within the desired timeframe. Of course after this
+ we will always return with -1 so that it is hung up properly after the
+ conversation. */
+ hanguptree(outgoing, peer, 1);
+ outgoing = NULL;
+ /* If appropriate, log that we have a destination channel */
+ if (chan->cdr)
+ ast_cdr_setdestchan(chan->cdr, peer->name);
+ if (peer->name)
+ pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name);
+
+ number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
+ if (!number)
+ number = numsubst;
+ pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
+ if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
+ ast_debug(1, "app_dial: sendurl=%s.\n", args.url);
+ ast_channel_sendurl( peer, args.url );
+ }
+ if ( (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) && pa.privdb_val == AST_PRIVACY_UNKNOWN) {
+ if (do_privacy(chan, peer, &opts, opt_args, &pa)) {
+ res = 0;
+ goto out;
+ }
+ }
+ if (!ast_test_flag64(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
+ res = 0;
+ } else {
+ int digit = 0;
+ /* Start autoservice on the other chan */
+ res = ast_autoservice_start(chan);
+ /* Now Stream the File */
+ if (!res)
+ res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language);
+ if (!res) {
+ digit = ast_waitstream(peer, AST_DIGIT_ANY);
+ }
+ /* Ok, done. stop autoservice */
+ res = ast_autoservice_stop(chan);
+ if (digit > 0 && !res)
+ res = ast_senddigit(chan, digit, 0);
+ else
+ res = digit;
+
+ }
+
+ if (chan && peer && ast_test_flag64(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
+ replace_macro_delimiter(opt_args[OPT_ARG_GOTO]);
+ ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);
+ /* peer goes to the same context and extension as chan, so just copy info from chan*/
+ ast_copy_string(peer->context, chan->context, sizeof(peer->context));
+ ast_copy_string(peer->exten, chan->exten, sizeof(peer->exten));
+ peer->priority = chan->priority + 2;
+ ast_pbx_start(peer);
+ hanguptree(outgoing, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
+ if (continue_exec)
+ *continue_exec = 1;
+ res = 0;
+ goto done;
+ }
+
+ if (ast_test_flag64(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
+ struct ast_app *theapp;
+ const char *macro_result;
+
+ res = ast_autoservice_start(chan);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
+ res = -1;
+ }
+
+ theapp = pbx_findapp("Macro");
+
+ if (theapp && !res) { /* XXX why check res here ? */
+ replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
+ res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_MACRO]);
+ ast_debug(1, "Macro exited with status %d\n", res);
+ res = 0;
+ } else {
+ ast_log(LOG_ERROR, "Could not find application Macro\n");
+ res = -1;
+ }
+
+ if (ast_autoservice_stop(chan) < 0) {
+ ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
+ res = -1;
+ }
+
+ if (!res && (macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
+ char *macro_transfer_dest;
+
+ if (!strcasecmp(macro_result, "BUSY")) {
+ ast_copy_string(pa.status, macro_result, sizeof(pa.status));
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ res = -1;
+ } else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
+ ast_copy_string(pa.status, macro_result, sizeof(pa.status));
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ res = -1;
+ } else if (!strcasecmp(macro_result, "CONTINUE")) {
+ /* hangup peer and keep chan alive assuming the macro has changed
+ the context / exten / priority or perhaps
+ the next priority in the current exten is desired.
+ */
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ res = -1;
+ } else if (!strcasecmp(macro_result, "ABORT")) {
+ /* Hangup both ends unless the caller has the g flag */
+ res = -1;
+ } else if (!strncasecmp(macro_result, "GOTO:", 5) && (macro_transfer_dest = ast_strdupa(macro_result + 5))) {
+ res = -1;
+ /* perform a transfer to a new extension */
+ if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
+ replace_macro_delimiter(macro_transfer_dest);
+ if (!ast_parseable_goto(chan, macro_transfer_dest))
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ }
+ }
+ }
+ }
+
+ if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
+ struct ast_app *theapp;
+ const char *gosub_result;
+ char *gosub_args, *gosub_argstart;
+
+ res = ast_autoservice_start(chan);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
+ res = -1;
+ }
+
+ theapp = pbx_findapp("Gosub");
+
+ if (theapp && !res) { /* XXX why check res here ? */
+ replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
+
+ /* Set where we came from */
+ ast_copy_string(peer->context, "app_dial_gosub_virtual_context", sizeof(peer->context));
+ ast_copy_string(peer->exten, "s", sizeof(peer->exten));
+ peer->priority = 0;
+
+ gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], '|');
+ if (gosub_argstart) {
+ *gosub_argstart = 0;
+ asprintf(&gosub_args, "%s|s|1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], gosub_argstart + 1);
+ *gosub_argstart = '|';
+ } else {
+ asprintf(&gosub_args, "%s|s|1", opt_args[OPT_ARG_CALLEE_GOSUB]);
+ }
+
+ if (gosub_args) {
+ res = pbx_exec(peer, theapp, gosub_args);
+ ast_pbx_run(peer);
+ ast_free(gosub_args);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
+ } else
+ ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
+
+ res = 0;
+ } else {
+ ast_log(LOG_ERROR, "Could not find application Gosub\n");
+ res = -1;
+ }
+
+ if (ast_autoservice_stop(chan) < 0) {
+ ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
+ res = -1;
+ }
+
+ if (!res && (gosub_result = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
+ char *gosub_transfer_dest;
+
+ if (!strcasecmp(gosub_result, "BUSY")) {
+ ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ res = -1;
+ } else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
+ ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ res = -1;
+ } else if (!strcasecmp(gosub_result, "CONTINUE")) {
+ /* hangup peer and keep chan alive assuming the macro has changed
+ the context / exten / priority or perhaps
+ the next priority in the current exten is desired.
+ */
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ res = -1;
+ } else if (!strcasecmp(gosub_result, "ABORT")) {
+ /* Hangup both ends unless the caller has the g flag */
+ res = -1;
+ } else if (!strncasecmp(gosub_result, "GOTO:", 5) && (gosub_transfer_dest = ast_strdupa(gosub_result + 5))) {
+ res = -1;
+ /* perform a transfer to a new extension */
+ if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
+ replace_macro_delimiter(gosub_transfer_dest);
+ if (!ast_parseable_goto(chan, gosub_transfer_dest))
+ ast_set_flag64(peerflags, OPT_GO_ON);
+ }
+ }
+ }
+ }
+
+ if (!res) {
+ if (calldurationlimit > 0) {
+ peer->whentohangup = time(NULL) + calldurationlimit;
+ }
+ if (!ast_strlen_zero(dtmfcalled)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the called party.\n", dtmfcalled);
+ res = ast_dtmf_stream(peer, chan, dtmfcalled, 250, 0);
+ }
+ if (!ast_strlen_zero(dtmfcalling)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the calling party.\n", dtmfcalling);
+ res = ast_dtmf_stream(chan, peer, dtmfcalling, 250, 0);
+ }
+ }
+
+ if (res) { /* some error */
+ res = -1;
+ end_time = time(NULL);
+ } else {
+ if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
+ if (ast_test_flag64(peerflags, OPT_CALLER_TRANSFER))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
+ if (ast_test_flag64(peerflags, OPT_CALLEE_HANGUP))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag64(peerflags, OPT_CALLEE_MONITOR))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
+ if (ast_test_flag64(peerflags, OPT_CALLER_MONITOR))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
+ if (ast_test_flag64(peerflags, OPT_CALLEE_PARK))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
+ if (ast_test_flag64(peerflags, OPT_CALLER_PARK))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
+ if (ast_test_flag64(peerflags, OPT_CALLEE_MIXMONITOR))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMIXMON);
+ if (ast_test_flag64(peerflags, OPT_CALLER_MIXMONITOR))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMIXMON);
+
+ if (moh) {
+ moh = 0;
+ ast_moh_stop(chan);
+ } else if (sentringing) {
+ sentringing = 0;
+ ast_indicate(chan, -1);
+ }
+ /* Be sure no generators are left on it */
+ ast_deactivate_generator(chan);
+ /* Make sure channels are compatible */
+ res = ast_channel_make_compatible(chan, peer);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
+ ast_hangup(peer);
+ res = -1;
+ goto done;
+ }
+ if (opermode && !strncmp(chan->name, "Zap", 3) && !strncmp(peer->name, "Zap", 3)) {
+ /* what's this special handling for Zap <-> Zap ?
+ * A: Zap to Zap calls are natively bridged at the kernel driver
+ * level, so we need to ensure that this mode gets propagated
+ * all the way down. */
+ struct oprmode oprmode;
+
+ oprmode.peer = peer;
+ oprmode.mode = opermode;
+
+ ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
+ }
+ res = ast_bridge_call(chan, peer, &config);
+ end_time = time(NULL);
+ snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
+ pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast);
+ }
+
+ snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
+ pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast);
+
+
+ if (ast_test_flag64(&opts, OPT_PEER_H)) {
+ ast_log(LOG_NOTICE, "PEER context: %s; PEER exten: %s; PEER priority: %d\n",
+ peer->context, peer->exten, peer->priority);
+ }
+
+ strcpy(peer->context, chan->context);
+
+ if (ast_test_flag64(&opts, OPT_PEER_H) && ast_exists_extension(peer, peer->context, "h", 1, peer->cid.cid_num)) {
+ int autoloopflag;
+ int found;
+ strcpy(peer->exten, "h");
+ peer->priority = 1;
+ autoloopflag = ast_test_flag(peer, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
+ ast_set_flag(peer, AST_FLAG_IN_AUTOLOOP);
+
+ while ((res = ast_spawn_extension(peer, peer->context, peer->exten, peer->priority, peer->cid.cid_num, &found, 1))) {
+ peer->priority++;
+ }
+ if (found && res) {
+ /* Something bad happened, or a hangup has been requested. */
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
+ }
+ ast_set2_flag(peer, autoloopflag, AST_FLAG_IN_AUTOLOOP); /* set it back the way it was */
+ }
+ if (res != AST_PBX_NO_HANGUP_PEER) {
+ if (!ast_check_hangup(chan))
+ chan->hangupcause = peer->hangupcause;
+ ast_hangup(peer);
+ }
+ }
+out:
+ if (moh) {
+ moh = 0;
+ ast_moh_stop(chan);
+ } else if (sentringing) {
+ sentringing = 0;
+ ast_indicate(chan, -1);
+ }
+ ast_channel_early_bridge(chan, NULL);
+ hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
+ senddialendevent(chan, pa.status);
+ ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
+
+ if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_KEEPALIVE)) {
+ if (calldurationlimit)
+ chan->whentohangup = 0;
+ res = 0;
+ }
+
+done:
+ return res;
+}
+
+static int dial_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_flags64 peerflags;
+
+ memset(&peerflags, 0, sizeof(peerflags));
+
+ return dial_exec_full(chan, data, &peerflags, NULL);
+}
+
+static int retrydial_exec(struct ast_channel *chan, void *data)
+{
+ char *parse;
+ const char *context = NULL;
+ int sleep = 0, loops = 0, res = -1;
+ struct ast_flags64 peerflags = { 0, };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(announce);
+ AST_APP_ARG(sleep);
+ AST_APP_ARG(retries);
+ AST_APP_ARG(dialdata);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "RetryDial requires an argument!\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if ((sleep = atoi(args.sleep))) {
+ sleep *= 1000;
+ }
+
+ loops = atoi(args.retries);
+
+ if (!args.dialdata) {
+ ast_log(LOG_ERROR, "%s requires a 4th argument (dialdata)\n", rapp);
+ goto done;
+ }
+
+ if (sleep < 1000)
+ sleep = 10000;
+
+ if (!loops)
+ loops = -1; /* run forever */
+
+ context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT");
+
+ res = 0;
+ while (loops) {
+ int continue_exec;
+
+ chan->data = "Retrying";
+ if (ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_stop(chan);
+
+ res = dial_exec_full(chan, args.dialdata, &peerflags, &continue_exec);
+ if (continue_exec)
+ break;
+
+ if (res == 0) {
+ if (ast_test_flag64(&peerflags, OPT_DTMF_EXIT)) {
+ if (!ast_strlen_zero(args.announce)) {
+ if (ast_fileexists(args.announce, NULL, chan->language) > 0) {
+ if (!(res = ast_streamfile(chan, args.announce, chan->language)))
+ ast_waitstream(chan, AST_DIGIT_ANY);
+ } else
+ ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
+ }
+ if (!res && sleep) {
+ if (!ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_start(chan, NULL, NULL);
+ res = ast_waitfordigit(chan, sleep);
+ }
+ } else {
+ if (!ast_strlen_zero(args.announce)) {
+ if (ast_fileexists(args.announce, NULL, chan->language) > 0) {
+ if (!(res = ast_streamfile(chan, args.announce, chan->language)))
+ res = ast_waitstream(chan, "");
+ } else
+ ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
+ }
+ if (sleep) {
+ if (!ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_start(chan, NULL, NULL);
+ if (!res)
+ res = ast_waitfordigit(chan, sleep);
+ }
+ }
+ }
+
+ if (res < 0)
+ break;
+ else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
+ if (onedigit_goto(chan, context, (char) res, 1)) {
+ res = 0;
+ break;
+ }
+ }
+ loops--;
+ }
+ if (loops == 0)
+ res = 0;
+ else if (res == 1)
+ res = 0;
+
+ if (ast_test_flag(chan, AST_FLAG_MOH))
+ ast_moh_stop(chan);
+ done:
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+ struct ast_context *con;
+
+ res = ast_unregister_application(app);
+ res |= ast_unregister_application(rapp);
+
+ if ((con = ast_context_find("app_dial_gosub_virtual_context")))
+ {
+ ast_context_remove_extension2(con, "s", 1, NULL);
+ ast_context_destroy(con, "app_dial"); /* leave nothing behind */
+ }
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+ struct ast_context *con;
+
+ con = ast_context_find("app_dial_gosub_virtual_context");
+ if (!con)
+ con = ast_context_create(NULL, "app_dial_gosub_virtual_context", "app_dial");
+ if (!con)
+ ast_log(LOG_ERROR, "Dial virtual context 'app_dial_gosub_virtual_context' does not exist and unable to create\n");
+ else
+ ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_dial");
+
+ res = ast_register_application(app, dial_exec, synopsis, descrip);
+ res |= ast_register_application(rapp, retrydial_exec, rsynopsis, rdescrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialing Application");
diff --git a/trunk/apps/app_dictate.c b/trunk/apps/app_dictate.c
new file mode 100644
index 000000000..58d61116a
--- /dev/null
+++ b/trunk/apps/app_dictate.c
@@ -0,0 +1,338 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Anthony Minessale II
+ *
+ * Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * Donated by Sangoma Technologies <http://www.samgoma.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 Virtual Dictation Machine Application For Asterisk
+ *
+ * \author Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
+#include "asterisk/file.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/say.h"
+#include "asterisk/app.h"
+
+static char *app = "Dictate";
+static char *synopsis = "Virtual Dictation Machine";
+static char *desc = " Dictate([<base_dir>[,<filename>]])\n"
+"Start dictation machine using optional base dir for files.\n";
+
+
+typedef enum {
+ DFLAG_RECORD = (1 << 0),
+ DFLAG_PLAY = (1 << 1),
+ DFLAG_TRUNC = (1 << 2),
+ DFLAG_PAUSE = (1 << 3),
+} dflags;
+
+typedef enum {
+ DMODE_INIT,
+ DMODE_RECORD,
+ DMODE_PLAY
+} dmodes;
+
+#define ast_toggle_flag(it,flag) if(ast_test_flag(it, flag)) ast_clear_flag(it, flag); else ast_set_flag(it, flag)
+
+static int play_and_wait(struct ast_channel *chan, char *file, char *digits)
+{
+ int res = -1;
+ if (!ast_streamfile(chan, file, chan->language)) {
+ res = ast_waitstream(chan, digits);
+ }
+ return res;
+}
+
+static int dictate_exec(struct ast_channel *chan, void *data)
+{
+ char *path = NULL, filein[256], *filename = "";
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(base);
+ AST_APP_ARG(filename);
+ );
+ char dftbase[256];
+ char *base;
+ struct ast_flags flags = {0};
+ struct ast_filestream *fs;
+ struct ast_frame *f = NULL;
+ int ffactor = 320 * 80,
+ res = 0,
+ done = 0,
+ oldr = 0,
+ lastop = 0,
+ samples = 0,
+ speed = 1,
+ digit = 0,
+ len = 0,
+ maxlen = 0,
+ mode = 0;
+
+ snprintf(dftbase, sizeof(dftbase), "%s/dictate", ast_config_AST_SPOOL_DIR);
+ if (!ast_strlen_zero(data)) {
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+ } else
+ args.argc = 0;
+
+ if (args.argc && !ast_strlen_zero(args.base)) {
+ base = args.base;
+ } else {
+ base = dftbase;
+ }
+ if (args.argc > 1 && args.filename) {
+ filename = args.filename;
+ }
+ oldr = chan->readformat;
+ if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)) < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode.\n");
+ return -1;
+ }
+
+ ast_answer(chan);
+ ast_safe_sleep(chan, 200);
+ for (res = 0; !res;) {
+ if (ast_strlen_zero(filename)) {
+ if (ast_app_getdata(chan, "dictate/enter_filename", filein, sizeof(filein), 0) ||
+ ast_strlen_zero(filein)) {
+ res = -1;
+ break;
+ }
+ } else {
+ ast_copy_string(filein, filename, sizeof(filein));
+ filename = "";
+ }
+ ast_mkdir(base, 0755);
+ len = strlen(base) + strlen(filein) + 2;
+ if (!path || len > maxlen) {
+ path = alloca(len);
+ memset(path, 0, len);
+ maxlen = len;
+ } else {
+ memset(path, 0, maxlen);
+ }
+
+ snprintf(path, len, "%s/%s", base, filein);
+ fs = ast_writefile(path, "raw", NULL, O_CREAT|O_APPEND, 0, AST_FILE_MODE);
+ mode = DMODE_PLAY;
+ memset(&flags, 0, sizeof(flags));
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ digit = play_and_wait(chan, "dictate/forhelp", AST_DIGIT_ANY);
+ done = 0;
+ speed = 1;
+ res = 0;
+ lastop = 0;
+ samples = 0;
+ while (!done && ((res = ast_waitfor(chan, -1)) > -1) && fs && (f = ast_read(chan))) {
+ if (digit) {
+ struct ast_frame fr = {AST_FRAME_DTMF, digit};
+ ast_queue_frame(chan, &fr);
+ digit = 0;
+ }
+ if ((f->frametype == AST_FRAME_DTMF)) {
+ int got = 1;
+ switch(mode) {
+ case DMODE_PLAY:
+ switch(f->subclass) {
+ case '1':
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ mode = DMODE_RECORD;
+ break;
+ case '2':
+ speed++;
+ if (speed > 4) {
+ speed = 1;
+ }
+ res = ast_say_number(chan, speed, AST_DIGIT_ANY, chan->language, NULL);
+ break;
+ case '7':
+ samples -= ffactor;
+ if(samples < 0) {
+ samples = 0;
+ }
+ ast_seekstream(fs, samples, SEEK_SET);
+ break;
+ case '8':
+ samples += ffactor;
+ ast_seekstream(fs, samples, SEEK_SET);
+ break;
+
+ default:
+ got = 0;
+ }
+ break;
+ case DMODE_RECORD:
+ switch(f->subclass) {
+ case '1':
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ mode = DMODE_PLAY;
+ break;
+ case '8':
+ ast_toggle_flag(&flags, DFLAG_TRUNC);
+ lastop = 0;
+ break;
+ default:
+ got = 0;
+ }
+ break;
+ default:
+ got = 0;
+ }
+ if (!got) {
+ switch(f->subclass) {
+ case '#':
+ done = 1;
+ continue;
+ break;
+ case '*':
+ ast_toggle_flag(&flags, DFLAG_PAUSE);
+ if (ast_test_flag(&flags, DFLAG_PAUSE)) {
+ digit = play_and_wait(chan, "dictate/pause", AST_DIGIT_ANY);
+ } else {
+ digit = play_and_wait(chan, mode == DMODE_PLAY ? "dictate/playback" : "dictate/record", AST_DIGIT_ANY);
+ }
+ break;
+ case '0':
+ ast_set_flag(&flags, DFLAG_PAUSE);
+ digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
+ switch(mode) {
+ case DMODE_PLAY:
+ digit = play_and_wait(chan, "dictate/play_help", AST_DIGIT_ANY);
+ break;
+ case DMODE_RECORD:
+ digit = play_and_wait(chan, "dictate/record_help", AST_DIGIT_ANY);
+ break;
+ }
+ if (digit == 0) {
+ digit = play_and_wait(chan, "dictate/both_help", AST_DIGIT_ANY);
+ } else if (digit < 0) {
+ done = 1;
+ break;
+ }
+ break;
+ }
+ }
+
+ } else if (f->frametype == AST_FRAME_VOICE) {
+ switch(mode) {
+ struct ast_frame *fr;
+ int x;
+ case DMODE_PLAY:
+ if (lastop != DMODE_PLAY) {
+ if (ast_test_flag(&flags, DFLAG_PAUSE)) {
+ digit = play_and_wait(chan, "dictate/playback_mode", AST_DIGIT_ANY);
+ if (digit == 0) {
+ digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
+ } else if (digit < 0) {
+ break;
+ }
+ }
+ if (lastop != DFLAG_PLAY) {
+ lastop = DFLAG_PLAY;
+ ast_closestream(fs);
+ if (!(fs = ast_openstream(chan, path, chan->language)))
+ break;
+ ast_seekstream(fs, samples, SEEK_SET);
+ chan->stream = NULL;
+ }
+ lastop = DMODE_PLAY;
+ }
+
+ if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
+ for (x = 0; x < speed; x++) {
+ if ((fr = ast_readframe(fs))) {
+ ast_write(chan, fr);
+ samples += fr->samples;
+ ast_frfree(fr);
+ fr = NULL;
+ } else {
+ samples = 0;
+ ast_seekstream(fs, 0, SEEK_SET);
+ }
+ }
+ }
+ break;
+ case DMODE_RECORD:
+ if (lastop != DMODE_RECORD) {
+ int oflags = O_CREAT | O_WRONLY;
+ if (ast_test_flag(&flags, DFLAG_PAUSE)) {
+ digit = play_and_wait(chan, "dictate/record_mode", AST_DIGIT_ANY);
+ if (digit == 0) {
+ digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
+ } else if (digit < 0) {
+ break;
+ }
+ }
+ lastop = DMODE_RECORD;
+ ast_closestream(fs);
+ if ( ast_test_flag(&flags, DFLAG_TRUNC)) {
+ oflags |= O_TRUNC;
+ digit = play_and_wait(chan, "dictate/truncating_audio", AST_DIGIT_ANY);
+ } else {
+ oflags |= O_APPEND;
+ }
+ fs = ast_writefile(path, "raw", NULL, oflags, 0, AST_FILE_MODE);
+ if (ast_test_flag(&flags, DFLAG_TRUNC)) {
+ ast_seekstream(fs, 0, SEEK_SET);
+ ast_clear_flag(&flags, DFLAG_TRUNC);
+ } else {
+ ast_seekstream(fs, 0, SEEK_END);
+ }
+ }
+ if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
+ res = ast_writestream(fs, f);
+ }
+ break;
+ }
+
+ }
+
+ ast_frfree(f);
+ }
+ }
+ if (oldr) {
+ ast_set_read_format(chan, oldr);
+ }
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int res;
+ res = ast_unregister_application(app);
+ return res;
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, dictate_exec, synopsis, desc);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Virtual Dictation Machine");
diff --git a/trunk/apps/app_directed_pickup.c b/trunk/apps/app_directed_pickup.c
new file mode 100644
index 000000000..07d81c869
--- /dev/null
+++ b/trunk/apps/app_directed_pickup.c
@@ -0,0 +1,172 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Joshua Colp
+ *
+ * Joshua Colp <jcolp@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 Directed Call Pickup Support
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.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"
+
+#define PICKUPMARK "PICKUPMARK"
+
+static const char *app = "Pickup";
+static const char *synopsis = "Directed Call Pickup";
+static const char *descrip =
+" Pickup([extension[@context][&extension2@[context]...]]): This application can\n"
+"pickup any ringing channel that is calling the specified extension. If no\n"
+"context is specified, the current context will be used. If you use the special\n"
+"string \"PICKUPMARK\" for the context parameter, for example 10@PICKUPMARK,\n"
+"this application tries to find a channel which has defined a ${PICKUPMARK}\n"
+"channel variable with the same value as \"extension\" (in this example, \"10\").\n"
+"When no parameter is specified, the application will pickup a channel matching\n"
+"the pickup group of the active channel.";
+
+/* Perform actual pickup between two channels */
+static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
+{
+ int res = 0;
+
+ ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
+
+ if ((res = ast_answer(chan))) {
+ ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
+ return -1;
+ }
+
+ if ((res = ast_queue_control(chan, AST_CONTROL_ANSWER))) {
+ ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
+ return -1;
+ }
+
+ if ((res = ast_channel_masquerade(target, chan))) {
+ ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
+ return -1;
+ }
+
+ return res;
+}
+
+/* Helper function that determines whether a channel is capable of being picked up */
+static int can_pickup(struct ast_channel *chan)
+{
+ if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING))
+ return 1;
+ else
+ return 0;
+}
+
+/* Attempt to pick up specified extension with context */
+static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
+{
+ int res = -1;
+ struct ast_channel *target = NULL;
+
+ while ((target = ast_channel_walk_locked(target))) {
+ if ((!strcasecmp(target->macroexten, exten) || !strcasecmp(target->exten, exten)) &&
+ !strcasecmp(target->dialcontext, context) &&
+ can_pickup(target)) {
+ res = pickup_do(chan, target);
+ ast_channel_unlock(target);
+ break;
+ }
+ ast_channel_unlock(target);
+ }
+
+ return res;
+}
+
+/* Attempt to pick up specified mark */
+static int pickup_by_mark(struct ast_channel *chan, const char *mark)
+{
+ int res = -1;
+ const char *tmp = NULL;
+ struct ast_channel *target = NULL;
+
+ while ((target = ast_channel_walk_locked(target))) {
+ if ((tmp = pbx_builtin_getvar_helper(target, PICKUPMARK)) &&
+ !strcasecmp(tmp, mark) &&
+ can_pickup(target)) {
+ res = pickup_do(chan, target);
+ ast_channel_unlock(target);
+ break;
+ }
+ ast_channel_unlock(target);
+ }
+
+ return res;
+}
+
+/* Main application entry point */
+static int pickup_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *tmp = ast_strdupa(data);
+ char *exten = NULL, *context = NULL;
+
+ if (ast_strlen_zero(data)) {
+ res = ast_pickup_call(chan);
+ return res;
+ }
+
+ /* Parse extension (and context if there) */
+ while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
+ if ((context = strchr(exten, '@')))
+ *context++ = '\0';
+ if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
+ if (!pickup_by_mark(chan, exten))
+ break;
+ } else {
+ if (!pickup_by_exten(chan, exten, !ast_strlen_zero(context) ? context : chan->context))
+ break;
+ }
+ ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten);
+ }
+
+ 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, pickup_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");
diff --git a/trunk/apps/app_directory.c b/trunk/apps/app_directory.c
new file mode 100644
index 000000000..08d5cf842
--- /dev/null
+++ b/trunk/apps/app_directory.c
@@ -0,0 +1,842 @@
+/*
+ * 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 Provide a directory of extensions
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
+#include "asterisk/file.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/say.h"
+#include "asterisk/app.h"
+
+#ifdef ODBC_STORAGE
+#include <sys/mman.h>
+#include "asterisk/res_odbc.h"
+
+static char odbc_database[80] = "asterisk";
+static char odbc_table[80] = "voicemessages";
+static char vmfmts[80] = "wav";
+#endif
+
+static char *app = "Directory";
+
+static char *synopsis = "Provide directory of voicemail extensions";
+static char *descrip =
+" Directory(vm-context[,dial-context[,options]]): This application will present\n"
+"the calling channel with a directory of extensions from which they can search\n"
+"by name. The list of names and corresponding extensions is retrieved from the\n"
+"voicemail configuration file, voicemail.conf.\n"
+" This application will immediately exit if one of the following DTMF digits are\n"
+"received and the extension to jump to exists:\n"
+" 0 - Jump to the 'o' extension, if it exists.\n"
+" * - Jump to the 'a' extension, if it exists.\n\n"
+" Parameters:\n"
+" vm-context - This is the context within voicemail.conf to use for the\n"
+" Directory.\n"
+" dial-context - This is the dialplan context to use when looking for an\n"
+" extension that the user has selected, or when jumping to the\n"
+" 'o' or 'a' extension.\n\n"
+" Options:\n"
+" e - In addition to the name, also read the extension number to the\n"
+" caller before presenting dialing options.\n"
+" f - Allow the caller to enter the first name of a user in the directory\n"
+" instead of using the last name.\n"
+" m - Instead of reading each name sequentially and asking for confirmation,\n"
+" create a menu of up to 8 names.\n";
+
+/* For simplicity, I'm keeping the format compatible with the voicemail config,
+ but i'm open to suggestions for isolating it */
+
+#define VOICEMAIL_CONFIG "voicemail.conf"
+
+/* How many digits to read in */
+#define NUMDIGITS 3
+
+enum {
+ OPT_LISTBYFIRSTNAME = (1 << 0),
+ OPT_SAYEXTENSION = (1 << 1),
+ OPT_FROMVOICEMAIL = (1 << 2),
+ OPT_SELECTFROMMENU = (1 << 3),
+} directory_option_flags;
+
+struct directory_item {
+ char exten[AST_MAX_EXTENSION + 1];
+ char name[AST_MAX_EXTENSION + 1];
+ char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */
+
+ AST_LIST_ENTRY(directory_item) entry;
+};
+
+AST_APP_OPTIONS(directory_app_options, {
+ AST_APP_OPTION('f', OPT_LISTBYFIRSTNAME),
+ AST_APP_OPTION('e', OPT_SAYEXTENSION),
+ AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
+ AST_APP_OPTION('m', OPT_SELECTFROMMENU),
+});
+
+#ifdef ODBC_STORAGE
+struct generic_prepare_struct {
+ const char *sql;
+ const char *param;
+};
+
+static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
+{
+ struct generic_prepare_struct *gps = data;
+ SQLHSTMT stmt;
+ int res;
+
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ return NULL;
+ }
+
+ res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *)gps->sql);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+
+ if (!ast_strlen_zero(gps->param))
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->param), 0, (void *)gps->param, 0, NULL);
+
+ return stmt;
+}
+
+static void retrieve_file(char *dir)
+{
+ int x = 0;
+ int res;
+ int fd=-1;
+ size_t fdlen = 0;
+ void *fdm = MAP_FAILED;
+ SQLHSTMT stmt;
+ char sql[256];
+ char fmt[80]="", empty[10] = "";
+ char *c;
+ SQLLEN colsize;
+ char full_fn[256];
+ struct odbc_obj *obj;
+ struct generic_prepare_struct gps = { .sql = sql, .param = dir };
+
+ obj = ast_odbc_request_obj(odbc_database, 1);
+ if (obj) {
+ do {
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ c = strchr(fmt, '|');
+ if (c)
+ *c = '\0';
+ if (!strcasecmp(fmt, "wav49"))
+ strcpy(fmt, "WAV");
+ snprintf(full_fn, sizeof(full_fn), "%s.%s", dir, fmt);
+ snprintf(sql, sizeof(sql), "SELECT recording FROM %s WHERE dir=? AND msgnum=-1", odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ break;
+ }
+ res = SQLFetch(stmt);
+ if (res == SQL_NO_DATA) {
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ break;
+ } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ break;
+ }
+ fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, AST_FILE_MODE);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ break;
+ }
+
+ res = SQLGetData(stmt, 1, SQL_BINARY, empty, 0, &colsize);
+ fdlen = colsize;
+ if (fd > -1) {
+ char tmp[1]="";
+ lseek(fd, fdlen - 1, SEEK_SET);
+ if (write(fd, tmp, 1) != 1) {
+ close(fd);
+ fd = -1;
+ break;
+ }
+ if (fd > -1)
+ fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ }
+ if (fdm != MAP_FAILED) {
+ memset(fdm, 0, fdlen);
+ res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ break;
+ }
+ }
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ } while (0);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ if (fdm != MAP_FAILED)
+ munmap(fdm, fdlen);
+ if (fd > -1)
+ close(fd);
+ return;
+}
+#endif
+
+static int compare(const char *text, const char *template)
+{
+ char digit;
+
+ while (*template) {
+ digit = toupper(*text++);
+ switch (digit) {
+ case 0:
+ return -1;
+ case '1':
+ digit = '1';
+ break;
+ case '2':
+ case 'A':
+ case 'B':
+ case 'C':
+ digit = '2';
+ break;
+ case '3':
+ case 'D':
+ case 'E':
+ case 'F':
+ digit = '3';
+ break;
+ case '4':
+ case 'G':
+ case 'H':
+ case 'I':
+ digit = '4';
+ break;
+ case '5':
+ case 'J':
+ case 'K':
+ case 'L':
+ digit = '5';
+ break;
+ case '6':
+ case 'M':
+ case 'N':
+ case 'O':
+ digit = '6';
+ break;
+ case '7':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ digit = '7';
+ break;
+ case '8':
+ case 'T':
+ case 'U':
+ case 'V':
+ digit = '8';
+ break;
+ case '9':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ digit = '9';
+ break;
+
+ default:
+ if (digit > ' ')
+ return -1;
+ continue;
+ }
+
+ if (*template++ != digit)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* play name of mailbox owner.
+ * returns: -1 for bad or missing extension
+ * '1' for selected entry from directory
+ * '*' for skipped entry from directory
+ */
+static int play_mailbox_owner(struct ast_channel *chan, const char *context,
+ const char *ext, const char *name, struct ast_flags *flags)
+{
+ int res = 0;
+ char fn[256];
+
+ /* Check for the VoiceMail2 greeting first */
+ snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
+ ast_config_AST_SPOOL_DIR, context, ext);
+#ifdef ODBC_STORAGE
+ retrieve_file(fn);
+#endif
+
+ if (ast_fileexists(fn, NULL, chan->language) <= 0) {
+ /* no file, check for an old-style Voicemail greeting */
+ snprintf(fn, sizeof(fn), "%s/vm/%s/greet",
+ ast_config_AST_SPOOL_DIR, ext);
+ }
+#ifdef ODBC_STORAGE
+ retrieve_file(fn);
+#endif
+
+ if (ast_fileexists(fn, NULL, chan->language) > 0) {
+ res = ast_stream_and_wait(chan, fn, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ /* If Option 'e' was specified, also read the extension number with the name */
+ if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
+ ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
+ res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
+ }
+ } else {
+ res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
+ if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
+ ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
+ res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
+ }
+ }
+#ifdef ODBC_STORAGE
+ ast_filedelete(fn, NULL);
+#endif
+
+ return res;
+}
+
+static int select_entry(struct ast_channel *chan, const char *context, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
+{
+ ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, dialcontext);
+
+ if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
+ /* We still want to set the exten though */
+ ast_copy_string(chan->exten, item->exten, sizeof(chan->exten));
+ } else if (ast_goto_if_exists(chan, dialcontext, item->exten, 1)) {
+ ast_log(LOG_WARNING,
+ "Can't find extension '%s' in context '%s'. "
+ "Did you pass the wrong context to Directory?\n",
+ item->exten, dialcontext);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
+{
+ struct directory_item *item, **ptr;
+ int i, res, loop;
+
+ for (ptr = items, i = 0; i < count; i++, ptr++) {
+ item = *ptr;
+
+ for (loop = 3 ; loop > 0; loop--) {
+ res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
+
+ if (!res)
+ res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
+ if (!res)
+ res = ast_waitfordigit(chan, 3000);
+ ast_stopstream(chan);
+
+ if (res == '1') { /* Name selected */
+ return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
+ } else if (res == '*') {
+ /* Skip to next match in list */
+ break;
+ }
+
+ if (res < 0)
+ return -1;
+
+ res = 0;
+ }
+ }
+
+ /* Nothing was selected */
+ return 0;
+}
+
+static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
+{
+ struct directory_item **block, *item;
+ int i, limit, res = 0;
+ char buf[9];
+
+ for (block = items; count; block += limit, count -= limit) {
+ limit = count;
+ if (limit > 8)
+ limit = 8;
+
+ for (i = 0; i < limit && !res; i++) {
+ item = block[i];
+
+ snprintf(buf, sizeof(buf), "digits/%d", i + 1);
+ /* Press <num> for <name>, [ extension <ext> ] */
+ res = ast_streamfile(chan, "dir-multi1", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (!res)
+ res = ast_streamfile(chan, buf, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (!res)
+ res = ast_streamfile(chan, "dir-multi2", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (!res)
+ res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ if (!res)
+ res = ast_waitfordigit(chan, 800);
+ }
+
+ /* Press "9" for more names. */
+ if (!res && count > limit) {
+ res = ast_streamfile(chan, "dir-multi9", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ }
+
+ if (!res) {
+ res = ast_waitfordigit(chan, 3000);
+ }
+
+ if (res && res > '0' && res < '1' + limit) {
+ return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1;
+ }
+
+ if (res < 0)
+ return -1;
+
+ res = 0;
+ }
+
+ /* Nothing was selected */
+ return 0;
+}
+
+static struct ast_config *realtime_directory(char *context)
+{
+ struct ast_config *cfg;
+ struct ast_config *rtdata;
+ struct ast_category *cat;
+ struct ast_variable *var;
+ char *mailbox;
+ const char *fullname;
+ const char *hidefromdir;
+ char tmp[100];
+ struct ast_flags config_flags = { 0 };
+
+ /* Load flat file config. */
+ cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
+
+ if (!cfg) {
+ /* Loading config failed. */
+ ast_log(LOG_WARNING, "Loading config failed.\n");
+ return NULL;
+ }
+
+ /* Get realtime entries, categorized by their mailbox number
+ and present in the requested context */
+ rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
+
+ /* if there are no results, just return the entries from the config file */
+ if (!rtdata)
+ return cfg;
+
+ /* Does the context exist within the config file? If not, make one */
+ cat = ast_category_get(cfg, context);
+ if (!cat) {
+ cat = ast_category_new(context, "", 99999);
+ if (!cat) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ ast_config_destroy(cfg);
+ return NULL;
+ }
+ ast_category_append(cfg, cat);
+ }
+
+ mailbox = NULL;
+ while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
+ fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
+ hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
+ snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
+ fullname ? fullname : "",
+ hidefromdir ? hidefromdir : "no");
+ var = ast_variable_new(mailbox, tmp, "");
+ if (var)
+ ast_variable_append(cat, var);
+ else
+ ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
+ }
+ ast_config_destroy(rtdata);
+
+ return cfg;
+}
+
+static int check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
+{
+ struct directory_item *item;
+ const char *key = NULL;
+ int namelen;
+
+
+ /* Set key to last name or first name depending on search mode */
+ if (!use_first_name)
+ key = strchr(item_fullname, ' ');
+
+ if (key)
+ key++;
+ else
+ key = item_fullname;
+
+ if (compare(key, pattern_ext))
+ return 0;
+
+ /* Match */
+ item = ast_calloc(1, sizeof(*item));
+ if (!item)
+ return -1;
+ ast_copy_string(item->name, item_fullname, sizeof(item->name));
+ ast_copy_string(item->exten, item_ext, sizeof(item->exten));
+
+ ast_copy_string(item->key, key, sizeof(item->key));
+ if (key != item_fullname) {
+ /* Key is the last name. Append first name to key in order to sort Last,First */
+ namelen = key - item_fullname - 1;
+ if (namelen > sizeof(item->key) - strlen(item->key) - 1)
+ namelen = sizeof(item->key) - strlen(item->key) - 1;
+ strncat(item->key, item_fullname, namelen);
+ }
+
+ *result = item;
+ return 1;
+}
+
+typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
+
+static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, int use_first_name, itemlist *alist)
+{
+ struct ast_variable *v;
+ char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat;
+ struct directory_item *item;
+ int res;
+
+ ast_debug(2, "Pattern: %s\n", ext);
+
+ for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
+
+ /* Ignore hidden */
+ if (strcasestr(v->value, "hidefromdir=yes"))
+ continue;
+
+ ast_copy_string(buf, v->value, sizeof(buf));
+ bufptr = buf;
+
+ /* password,Full Name,email,pager,options */
+ strsep(&bufptr, ",");
+ pos = strsep(&bufptr, ",");
+
+ res = check_match(&item, pos, v->name, ext, use_first_name);
+ if (!res)
+ continue;
+ else if (res < 0)
+ return -1;
+
+ AST_LIST_INSERT_TAIL(alist, item, entry);
+ }
+
+
+ if (ucfg) {
+ for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
+ const char *pos;
+ if (!strcasecmp(cat, "general"))
+ continue;
+ if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
+ continue;
+
+ /* Find all candidate extensions */
+ pos = ast_variable_retrieve(ucfg, cat, "fullname");
+ if (!pos)
+ continue;
+
+ res = check_match(&item, pos, cat, ext, use_first_name);
+ if (!res)
+ continue;
+ else if (res < 0)
+ return -1;
+
+ AST_LIST_INSERT_TAIL(alist, item, entry);
+ }
+ }
+
+ return 0;
+}
+
+static void sort_items(struct directory_item **sorted, int count)
+{
+ int reordered, i;
+ struct directory_item **ptr, *tmp;
+
+ if (count < 2)
+ return;
+
+ /* Bubble-sort items by the key */
+ do {
+ reordered = 0;
+ for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
+ if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
+ tmp = ptr[0];
+ ptr[0] = ptr[1];
+ ptr[1] = tmp;
+ reordered++;
+ }
+ }
+ } while (reordered);
+}
+
+static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
+{
+ if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
+ (!ast_strlen_zero(chan->macrocontext) &&
+ !ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Can't find extension '%s' in current context. "
+ "Not Exiting the Directory!\n", ext);
+ return -1;
+ }
+}
+
+static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, struct ast_flags *flags)
+{
+ /* Read in the first three digits.. "digit" is the first digit, already read */
+ int res = 0;
+ itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+ struct directory_item *item, **ptr, **sorted = NULL;
+ int count, i;
+ char ext[NUMDIGITS + 1] = "";
+
+ if (ast_strlen_zero(context)) {
+ ast_log(LOG_WARNING,
+ "Directory must be called with an argument "
+ "(context in which to interpret extensions)\n");
+ return -1;
+ }
+
+ if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
+ return 0;
+ }
+
+ if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
+ return 0;
+ }
+
+ ext[0] = digit;
+ if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0)
+ return -1;
+
+ res = search_directory(context, vmcfg, ucfg, ext, ast_test_flag(flags, OPT_LISTBYFIRSTNAME), &alist);
+ if (res)
+ goto exit;
+
+ /* Count items in the list */
+ count = 0;
+ AST_LIST_TRAVERSE(&alist, item, entry) {
+ count++;
+ }
+
+ if (count < 1) {
+ res = ast_streamfile(chan, "dir-nomatch", chan->language);
+ goto exit;
+ }
+
+
+ /* Create plain array of pointers to items (for sorting) */
+ sorted = ast_calloc(count, sizeof(*sorted));
+
+ ptr = sorted;
+ AST_LIST_TRAVERSE(&alist, item, entry) {
+ *ptr++ = item;
+ }
+
+ /* Sort items */
+ sort_items(sorted, count);
+
+ if (option_debug) {
+ ast_debug(2, "Listing matching entries:\n");
+ for (ptr = sorted, i = 0; i < count; i++, ptr++) {
+ ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
+ }
+ }
+
+ if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
+ /* Offer multiple entries at the same time */
+ res = select_item_menu(chan, sorted, count, context, dialcontext, flags);
+ } else {
+ /* Offer entries one by one */
+ res = select_item_seq(chan, sorted, count, context, dialcontext, flags);
+ }
+
+ if (!res) {
+ res = ast_streamfile(chan, "dir-nomore", chan->language);
+ }
+
+exit:
+ if (sorted)
+ ast_free(sorted);
+
+ while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
+ ast_free(item);
+
+ return res;
+}
+
+static int directory_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_config *cfg, *ucfg;
+ const char *dirintro;
+ char *parse, *opts[0];
+ struct ast_flags flags = { 0 };
+ struct ast_flags config_flags = { 0 };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(vmcontext);
+ AST_APP_ARG(dialcontext);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
+ return -1;
+
+ if (ast_strlen_zero(args.dialcontext))
+ args.dialcontext = args.vmcontext;
+
+ cfg = realtime_directory(args.vmcontext);
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
+ return -1;
+ }
+
+ ucfg = ast_config_load("users.conf", config_flags);
+
+ dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
+ if (ast_strlen_zero(dirintro))
+ dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
+ if (ast_strlen_zero(dirintro))
+ dirintro = ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) ? "dir-intro-fn" : "dir-intro";
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ for (;;) {
+ if (!res)
+ res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (!res)
+ res = ast_waitfordigit(chan, 5000);
+
+ if (res <= 0)
+ break;
+
+ res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, &flags);
+ if (res)
+ break;
+
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+
+ if (res)
+ break;
+ }
+
+ if (ucfg)
+ ast_config_destroy(ucfg);
+ ast_config_destroy(cfg);
+
+ return res < 0 ? -1 : 0;
+}
+
+static int unload_module(void)
+{
+ int res;
+ res = ast_unregister_application(app);
+ return res;
+}
+
+static int load_module(void)
+{
+#ifdef ODBC_STORAGE
+ struct ast_flags config_flags = { 0 };
+ struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
+ const char *tmp;
+
+ if (cfg) {
+ if ((tmp = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
+ ast_copy_string(odbc_database, tmp, sizeof(odbc_database));
+ }
+ if ((tmp = ast_variable_retrieve(cfg, "general", "odbctable"))) {
+ ast_copy_string(odbc_table, tmp, sizeof(odbc_table));
+ }
+ if ((tmp = ast_variable_retrieve(cfg, "general", "format"))) {
+ ast_copy_string(vmfmts, tmp, sizeof(vmfmts));
+ }
+ ast_config_destroy(cfg);
+ } else
+ ast_log(LOG_WARNING, "Unable to load " VOICEMAIL_CONFIG " - ODBC defaults will be used\n");
+#endif
+
+ return ast_register_application(app, directory_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");
diff --git a/trunk/apps/app_disa.c b/trunk/apps/app_disa.c
new file mode 100644
index 000000000..ac74c78a4
--- /dev/null
+++ b/trunk/apps/app_disa.c
@@ -0,0 +1,367 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ *
+ * Made only slightly more sane by 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 DISA -- Direct Inward System Access Application
+ *
+ * \author Jim Dixon <jim@lambdatel.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+#include <sys/time.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/indications.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/callerid.h"
+#include "asterisk/stringfields.h"
+
+static char *app = "DISA";
+
+static char *synopsis = "DISA (Direct Inward System Access)";
+
+static char *descrip =
+"DISA(<numeric passcode>[,<context>[,<cid>[,mailbox[,options]]]]) or\n"
+"DISA(<filename>[,,,,options])\n"
+"The DISA, Direct Inward System Access, application allows someone from \n"
+"outside the telephone switch (PBX) to obtain an \"internal\" system \n"
+"dialtone and to place calls from it as if they were placing a call from \n"
+"within the switch.\n"
+"DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
+"the pound sign (#). If the passcode is correct, the user is then given\n"
+"system dialtone within <context> on which a call may be placed. If the user\n"
+"enters an invalid extension and extension \"i\" exists in the specified\n"
+"context, it will be used.\n"
+"\n"
+"If you need to present a DISA dialtone without entering a password, simply\n"
+"set <passcode> to \"no-password\".\n"
+"\n"
+"Be aware that using this may compromise the security of your PBX.\n"
+"\n"
+"The arguments to this application (in extensions.conf) allow either\n"
+"specification of a single global passcode (that everyone uses), or\n"
+"individual passcodes contained in a file.\n"
+"\n"
+"The file that contains the passcodes (if used) allows a complete\n"
+"specification of all of the same arguments available on the command\n"
+"line, with the sole exception of the options. The file may contain blank\n"
+"lines, or comments starting with \"#\" or \";\".\n"
+"\n"
+"<context> specifies the dialplan context in which the user-entered extension\n"
+"will be matched. If no context is specified, the DISA application defaults\n"
+"the context to \"disa\". Presumably a normal system will have a special\n"
+"context set up for DISA use with some or a lot of restrictions.\n"
+"\n"
+"<cid> specifies a new (different) callerid to be used for this call.\n"
+"\n"
+"<mailbox[@context]> will cause a stutter-dialtone (indication \"dialrecall\")\n"
+"to be used, if the specified mailbox contains any new messages.\n"
+"\n"
+"The following options are available:\n"
+" n - the DISA application will not answer initially.\n"
+" p - the extension entered will be considered complete when a '#' is entered.\n";
+
+enum {
+ NOANSWER_FLAG = (1 << 0),
+ POUND_TO_END_FLAG = (1 << 1),
+} option_flags;
+
+AST_APP_OPTIONS(app_opts, {
+ AST_APP_OPTION('n', NOANSWER_FLAG),
+ AST_APP_OPTION('p', POUND_TO_END_FLAG),
+});
+
+static void play_dialtone(struct ast_channel *chan, char *mailbox)
+{
+ const struct ind_tone_zone_sound *ts = NULL;
+ if(ast_app_has_voicemail(mailbox, NULL))
+ ts = ast_get_indication_tone(chan->zone, "dialrecall");
+ else
+ ts = ast_get_indication_tone(chan->zone, "dial");
+ if (ts)
+ ast_playtones_start(chan, 0, ts->data, 0);
+ else
+ ast_tonepair_start(chan, 350, 440, 0, 0);
+}
+
+static int disa_exec(struct ast_channel *chan, void *data)
+{
+ int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
+ int firstdigittimeout = (chan->pbx ? chan->pbx->rtimeout * 1000 : 20000);
+ int digittimeout = (chan->pbx ? chan->pbx->dtimeout * 1000 : 10000);
+ struct ast_flags flags;
+ char *tmp, exten[AST_MAX_EXTENSION] = "", acctcode[20]="";
+ char pwline[256];
+ char ourcidname[256],ourcidnum[256];
+ struct ast_frame *f;
+ struct timeval lastdigittime;
+ int res;
+ FILE *fp;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(passcode);
+ AST_APP_ARG(context);
+ AST_APP_ARG(cid);
+ AST_APP_ARG(mailbox);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
+ return -1;
+ }
+
+ ast_debug(1, "Digittimeout: %d\n", digittimeout);
+ ast_debug(1, "Responsetimeout: %d\n", firstdigittimeout);
+
+ tmp = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if (ast_strlen_zero(args.context))
+ args.context = "disa";
+ if (ast_strlen_zero(args.mailbox))
+ args.mailbox = "";
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(app_opts, &flags, NULL, args.options);
+
+ ast_debug(1, "Mailbox: %s\n",args.mailbox);
+
+ if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
+ if (chan->_state != AST_STATE_UP) {
+ /* answer */
+ ast_answer(chan);
+ }
+ } else
+ special_noanswer = 1;
+
+ ast_debug(1, "Context: %s\n",args.context);
+
+ if (!strcasecmp(args.passcode, "no-password")) {
+ k |= 1; /* We have the password */
+ ast_debug(1, "DISA no-password login success\n");
+ }
+
+ lastdigittime = ast_tvnow();
+
+ play_dialtone(chan, args.mailbox);
+
+ ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
+
+ for (;;) {
+ /* if outa time, give em reorder */
+ if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
+ ast_debug(1,"DISA %s entry timeout on chan %s\n",
+ ((k&1) ? "extension" : "password"),chan->name);
+ break;
+ }
+
+ if ((res = ast_waitfor(chan, -1) < 0)) {
+ ast_debug(1, "Waitfor returned %d\n", res);
+ continue;
+ }
+
+ if (!(f = ast_read(chan))) {
+ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ }
+
+ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
+ ast_frfree(f);
+ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ }
+
+ /* If the frame coming in is not DTMF, just drop it and continue */
+ if (f->frametype != AST_FRAME_DTMF) {
+ ast_frfree(f);
+ continue;
+ }
+
+ j = f->subclass; /* save digit */
+ ast_frfree(f);
+
+ if (!i) {
+ k |= 2; /* We have the first digit */
+ ast_playtones_stop(chan);
+ }
+
+ lastdigittime = ast_tvnow();
+
+ /* got a DTMF tone */
+ if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
+ if (!(k&1)) { /* if in password state */
+ if (j == '#') { /* end of password */
+ /* see if this is an integer */
+ if (sscanf(args.passcode,"%d",&j) < 1) { /* nope, it must be a filename */
+ fp = fopen(args.passcode,"r");
+ if (!fp) {
+ ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
+ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ }
+ pwline[0] = 0;
+ while(fgets(pwline,sizeof(pwline) - 1,fp)) {
+ if (!pwline[0])
+ continue;
+ if (pwline[strlen(pwline) - 1] == '\n')
+ pwline[strlen(pwline) - 1] = 0;
+ if (!pwline[0])
+ continue;
+ /* skip comments */
+ if (pwline[0] == '#')
+ continue;
+ if (pwline[0] == ';')
+ continue;
+
+ AST_STANDARD_APP_ARGS(args, pwline);
+
+ ast_debug(1, "Mailbox: %s\n",args.mailbox);
+
+ /* password must be in valid format (numeric) */
+ if (sscanf(args.passcode,"%d", &j) < 1)
+ continue;
+ /* if we got it */
+ if (!strcmp(exten,args.passcode)) {
+ if (ast_strlen_zero(args.context))
+ args.context = "disa";
+ if (ast_strlen_zero(args.mailbox))
+ args.mailbox = "";
+ break;
+ }
+ }
+ fclose(fp);
+ }
+ /* compare the two */
+ if (strcmp(exten,args.passcode)) {
+ ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
+ goto reorder;
+
+ }
+ /* password good, set to dial state */
+ ast_debug(1,"DISA on chan %s password is good\n",chan->name);
+ play_dialtone(chan, args.mailbox);
+
+ k|=1; /* In number mode */
+ i = 0; /* re-set buffer pointer */
+ exten[sizeof(acctcode)] = 0;
+ ast_copy_string(acctcode, exten, sizeof(acctcode));
+ exten[0] = 0;
+ ast_debug(1,"Successful DISA log-in on chan %s\n", chan->name);
+ continue;
+ }
+ } else {
+ if (j == '#') { /* end of extension */
+ break;
+ }
+ }
+
+ exten[i++] = j; /* save digit */
+ exten[i] = 0;
+ if (!(k&1))
+ continue; /* if getting password, continue doing it */
+ /* if this exists */
+
+ /* user wants end of number, remove # */
+ if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
+ exten[--i] = 0;
+ break;
+ }
+
+ if (ast_ignore_pattern(args.context, exten)) {
+ play_dialtone(chan, "");
+ did_ignore = 1;
+ } else
+ if (did_ignore) {
+ ast_playtones_stop(chan);
+ did_ignore = 0;
+ }
+
+ /* if can do some more, do it */
+ if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
+ break;
+ }
+ }
+ }
+
+ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+
+ if (k == 3) {
+ int recheck = 0;
+ struct ast_flags flags = { AST_CDR_FLAG_POSTED };
+
+ if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
+ pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
+ exten[0] = 'i';
+ exten[1] = '\0';
+ recheck = 1;
+ }
+ if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
+ ast_playtones_stop(chan);
+ /* We're authenticated and have a target extension */
+ if (!ast_strlen_zero(args.cid)) {
+ ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
+ ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
+ }
+
+ if (!ast_strlen_zero(acctcode))
+ ast_string_field_set(chan, accountcode, acctcode);
+
+ if (special_noanswer) flags.flags = 0;
+ ast_cdr_reset(chan->cdr, &flags);
+ ast_explicit_goto(chan, args.context, exten, 1);
+ return 0;
+ }
+ }
+
+ /* Received invalid, but no "i" extension exists in the given context */
+
+reorder:
+ /* Play congestion for a bit */
+ ast_indicate(chan, AST_CONTROL_CONGESTION);
+ ast_safe_sleep(chan, 10*1000);
+
+ ast_playtones_stop(chan);
+
+ return -1;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, disa_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");
diff --git a/trunk/apps/app_dumpchan.c b/trunk/apps/app_dumpchan.c
new file mode 100644
index 000000000..48fb6215d
--- /dev/null
+++ b/trunk/apps/app_dumpchan.c
@@ -0,0 +1,160 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005, Anthony Minessale II.
+ *
+ * Anthony Minessale <anthmct@yahoo.com>
+ *
+ * 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 Application to dump channel variables
+ *
+ * \author Anthony Minessale <anthmct@yahoo.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+
+static char *app = "DumpChan";
+static char *synopsis = "Dump Info About The Calling Channel";
+static char *desc =
+ " DumpChan([<min_verbose_level>])\n"
+ "Displays information on channel and listing of all channel\n"
+ "variables. If min_verbose_level is specified, output is only\n"
+ "displayed when the verbose level is currently set to that number\n"
+ "or greater. \n";
+
+
+static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
+{
+ struct timeval now;
+ long elapsed_seconds = 0;
+ int hour = 0, min = 0, sec = 0;
+ char cgrp[BUFSIZ/2];
+ char pgrp[BUFSIZ/2];
+ char formatbuf[BUFSIZ/2];
+
+ now = ast_tvnow();
+ memset(buf, 0, size);
+ if (!c)
+ return 0;
+
+ if (c->cdr) {
+ elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
+ hour = elapsed_seconds / 3600;
+ min = (elapsed_seconds % 3600) / 60;
+ sec = elapsed_seconds % 60;
+ }
+
+ snprintf(buf,size,
+ "Name= %s\n"
+ "Type= %s\n"
+ "UniqueID= %s\n"
+ "CallerIDNum= %s\n"
+ "CallerIDName= %s\n"
+ "DNIDDigits= %s\n"
+ "RDNIS= %s\n"
+ "Language= %s\n"
+ "State= %s (%d)\n"
+ "Rings= %d\n"
+ "NativeFormat= %s\n"
+ "WriteFormat= %s\n"
+ "ReadFormat= %s\n"
+ "RawWriteFormat= %s\n"
+ "RawReadFormat= %s\n"
+ "1stFileDescriptor= %d\n"
+ "Framesin= %d %s\n"
+ "Framesout= %d %s\n"
+ "TimetoHangup= %ld\n"
+ "ElapsedTime= %dh%dm%ds\n"
+ "Context= %s\n"
+ "Extension= %s\n"
+ "Priority= %d\n"
+ "CallGroup= %s\n"
+ "PickupGroup= %s\n"
+ "Application= %s\n"
+ "Data= %s\n"
+ "Blocking_in= %s\n",
+ c->name,
+ c->tech->type,
+ c->uniqueid,
+ S_OR(c->cid.cid_num, "(N/A)"),
+ S_OR(c->cid.cid_name, "(N/A)"),
+ S_OR(c->cid.cid_dnid, "(N/A)"),
+ S_OR(c->cid.cid_rdnis, "(N/A)"),
+ c->language,
+ ast_state2str(c->_state),
+ c->_state,
+ c->rings,
+ ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->nativeformats),
+ ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->writeformat),
+ ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->readformat),
+ ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->rawwriteformat),
+ ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->rawreadformat),
+ c->fds[0], c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
+ c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", (long)c->whentohangup,
+ hour,
+ min,
+ sec,
+ c->context,
+ c->exten,
+ c->priority,
+ ast_print_group(cgrp, sizeof(cgrp), c->callgroup),
+ ast_print_group(pgrp, sizeof(pgrp), c->pickupgroup),
+ ( c->appl ? c->appl : "(N/A)" ),
+ ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
+ (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
+
+ return 0;
+}
+
+static int dumpchan_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_str *vars = ast_str_alloca(BUFSIZ * 4); /* XXX very large! */
+ char info[1024];
+ int level = 0;
+ static char *line = "================================================================================";
+
+ if (!ast_strlen_zero(data))
+ level = atoi(data);
+
+ pbx_builtin_serialize_variables(chan, &vars);
+ serialize_showchan(chan, info, sizeof(info));
+ if (option_verbose >= level)
+ ast_verbose("\nDumping Info For Channel: %s:\n%s\nInfo:\n%s\nVariables:\n%s%s\n", chan->name, line, info, vars->str, line);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, dumpchan_exec, synopsis, desc);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dump Info About The Calling Channel");
diff --git a/trunk/apps/app_echo.c b/trunk/apps/app_echo.c
new file mode 100644
index 000000000..89242ffa0
--- /dev/null
+++ b/trunk/apps/app_echo.c
@@ -0,0 +1,86 @@
+/*
+ * 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 Echo application -- play back what you hear to evaluate latency
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+
+static char *app = "Echo";
+
+static char *synopsis = "Echo audio, video, or DTMF back to the calling party";
+
+static char *descrip =
+" Echo(): This application will echo any audio, video, or DTMF frames read from\n"
+"the calling channel back to itself. If the DTMF digit '#' is received, the\n"
+"application will exit.\n";
+
+
+static int echo_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ int format;
+
+ format = ast_best_codec(chan->nativeformats);
+ ast_set_write_format(chan, format);
+ ast_set_read_format(chan, format);
+
+ while (ast_waitfor(chan, -1) > -1) {
+ struct ast_frame *f = ast_read(chan);
+ if (!f)
+ break;
+ f->delivery.tv_sec = 0;
+ f->delivery.tv_usec = 0;
+ if (ast_write(chan, f)) {
+ ast_frfree(f);
+ goto end;
+ }
+ if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
+ res = 0;
+ ast_frfree(f);
+ goto end;
+ }
+ ast_frfree(f);
+ }
+end:
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, echo_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Echo Application");
diff --git a/trunk/apps/app_exec.c b/trunk/apps/app_exec.c
new file mode 100644
index 000000000..441e81458
--- /dev/null
+++ b/trunk/apps/app_exec.c
@@ -0,0 +1,221 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2005, Tilghman Lesher. All rights reserved.
+ * Portions copyright (c) 2006, Philipp Dunkel.
+ *
+ * Tilghman Lesher <app_exec__v002@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Exec application
+ *
+ * \author Tilghman Lesher <app_exec__v002@the-tilghman.com>
+ * \author Philipp Dunkel <philipp.dunkel@ebox.at>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+
+/* Maximum length of any variable */
+#define MAXRESULT 1024
+
+/*! Note
+ *
+ * The key difference between these two apps is exit status. In a
+ * nutshell, Exec tries to be transparent as possible, behaving
+ * in exactly the same way as if the application it calls was
+ * directly invoked from the dialplan.
+ *
+ * TryExec, on the other hand, provides a way to execute applications
+ * and catch any possible fatal error without actually fatally
+ * affecting the dialplan.
+ */
+
+static char *app_exec = "Exec";
+static char *exec_synopsis = "Executes dialplan application";
+static char *exec_descrip =
+" Exec(appname(arguments)):\n"
+"Allows an arbitrary application to be invoked even when not\n"
+"hardcoded into the dialplan. If the underlying application\n"
+"terminates the dialplan, or if the application cannot be found,\n"
+"Exec will terminate the dialplan.\n"
+" To invoke external applications, see the application System.\n"
+" If you would like to catch any error instead, see TryExec.\n";
+
+static char *app_tryexec = "TryExec";
+static char *tryexec_synopsis = "Executes dialplan application, always returning";
+static char *tryexec_descrip =
+" TryExec(appname(arguments)):\n"
+"Allows an arbitrary application to be invoked even when not\n"
+"hardcoded into the dialplan. To invoke external applications\n"
+"see the application System. Always returns to the dialplan.\n"
+"The channel variable TRYSTATUS will be set to one of:\n"
+" SUCCESS if the application returned zero\n"
+" FAILED if the application returned non-zero\n"
+" NOAPP if the application was not found or was not specified\n";
+
+static char *app_execif = "ExecIf";
+static char *execif_synopsis = "Executes dialplan application, conditionally";
+static char *execif_descrip =
+" ExecIF (<expr>?<appiftrue>(<args>)[:<appiffalse>(<args>)])\n"
+"If <expr> is true, execute and return the result of <appiftrue>(<args>).\n"
+"If <expr> is true, but <appiftrue> is not found, then the application\n"
+"will return a non-zero value.\n";
+
+static int exec_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *s, *appname, *endargs, args[MAXRESULT];
+ struct ast_app *app;
+
+ if (ast_strlen_zero(data))
+ return 0;
+
+ s = ast_strdupa(data);
+ args[0] = 0;
+ appname = strsep(&s, "(");
+ if (s) {
+ endargs = strrchr(s, ')');
+ if (endargs)
+ *endargs = '\0';
+ pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
+ }
+ if (appname) {
+ app = pbx_findapp(appname);
+ if (app) {
+ res = pbx_exec(chan, app, args);
+ } else {
+ ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+static int tryexec_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *s, *appname, *endargs, args[MAXRESULT];
+ struct ast_app *app;
+
+ if (ast_strlen_zero(data))
+ return 0;
+
+ s = ast_strdupa(data);
+ args[0] = 0;
+ appname = strsep(&s, "(");
+ if (s) {
+ endargs = strrchr(s, ')');
+ if (endargs)
+ *endargs = '\0';
+ pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
+ }
+ if (appname) {
+ app = pbx_findapp(appname);
+ if (app) {
+ res = pbx_exec(chan, app, args);
+ pbx_builtin_setvar_helper(chan, "TRYSTATUS", res ? "FAILED" : "SUCCESS");
+ } else {
+ ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
+ pbx_builtin_setvar_helper(chan, "TRYSTATUS", "NOAPP");
+ }
+ }
+
+ return 0;
+}
+
+static int execif_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *truedata = NULL, *falsedata = NULL, *end;
+ struct ast_app *app = NULL;
+ AST_DECLARE_APP_ARGS(expr,
+ AST_APP_ARG(expr);
+ AST_APP_ARG(remainder);
+ );
+ AST_DECLARE_APP_ARGS(apps,
+ AST_APP_ARG(t);
+ AST_APP_ARG(f);
+ );
+ char *parse = ast_strdupa(data);
+
+ AST_NONSTANDARD_APP_ARGS(expr, parse, '?');
+ if (ast_strlen_zero(expr.remainder)) {
+ ast_log(LOG_ERROR, "Usage: ExecIf(<expr>?<appiftrue>(<args>)[:<appiffalse>(<args)])\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(apps, expr.remainder, ':');
+
+ if (apps.t && (truedata = strchr(apps.t, '('))) {
+ *truedata++ = '\0';
+ if ((end = strrchr(truedata, ')')))
+ *end = '\0';
+ }
+
+ if (apps.f && (falsedata = strchr(apps.f, '('))) {
+ *falsedata++ = '\0';
+ if ((end = strrchr(falsedata, ')')))
+ *end = '\0';
+ }
+
+ if (pbx_checkcondition(expr.expr)) {
+ if (!ast_strlen_zero(apps.t) && (app = pbx_findapp(apps.t))) {
+ res = pbx_exec(chan, app, S_OR(truedata, ""));
+ } else {
+ ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.t);
+ res = -1;
+ }
+ } else if (!ast_strlen_zero(apps.f)) {
+ if ((app = pbx_findapp(apps.f))) {
+ res = pbx_exec(chan, app, S_OR(falsedata, ""));
+ } else {
+ ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.f);
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_exec);
+ res |= ast_unregister_application(app_tryexec);
+ res |= ast_unregister_application(app_execif);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = ast_register_application(app_exec, exec_exec, exec_synopsis, exec_descrip);
+ res |= ast_register_application(app_tryexec, tryexec_exec, tryexec_synopsis, tryexec_descrip);
+ res |= ast_register_application(app_execif, execif_exec, execif_synopsis, execif_descrip);
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Executes dialplan applications");
diff --git a/trunk/apps/app_externalivr.c b/trunk/apps/app_externalivr.c
new file mode 100644
index 000000000..3de290f32
--- /dev/null
+++ b/trunk/apps/app_externalivr.c
@@ -0,0 +1,575 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * Portions taken from the file-based music-on-hold work
+ * created by Anthony Minessale II in res_musiconhold.c
+ *
+ * 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 External IVR application interface
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * \note Portions taken from the file-based music-on-hold work
+ * created by Anthony Minessale II in res_musiconhold.c
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/app.h"
+#include "asterisk/utils.h"
+
+static const char *app = "ExternalIVR";
+
+static const char *synopsis = "Interfaces with an external IVR application";
+
+static const char *descrip =
+" ExternalIVR(command[,arg[,arg...]]): Forks an process to run the supplied command,\n"
+"and starts a generator on the channel. The generator's play list is\n"
+"controlled by the external application, which can add and clear entries\n"
+"via simple commands issued over its stdout. The external application\n"
+"will receive all DTMF events received on the channel, and notification\n"
+"if the channel is hung up. The application will not be forcibly terminated\n"
+"when the channel is hung up.\n"
+"See doc/externalivr.txt for a protocol specification.\n";
+
+/* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
+#define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
+
+struct playlist_entry {
+ AST_LIST_ENTRY(playlist_entry) list;
+ char filename[1];
+};
+
+struct ivr_localuser {
+ struct ast_channel *chan;
+ AST_LIST_HEAD(playlist, playlist_entry) playlist;
+ AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
+ int abort_current_sound;
+ int playing_silence;
+ int option_autoclear;
+};
+
+
+struct gen_state {
+ struct ivr_localuser *u;
+ struct ast_filestream *stream;
+ struct playlist_entry *current;
+ int sample_queue;
+};
+
+static void send_child_event(FILE *handle, const char event, const char *data,
+ const struct ast_channel *chan)
+{
+ char tmp[256];
+
+ if (!data) {
+ snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL));
+ } else {
+ snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data);
+ }
+
+ fprintf(handle, "%s\n", tmp);
+ if (option_debug)
+ ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp);
+}
+
+static void *gen_alloc(struct ast_channel *chan, void *params)
+{
+ struct ivr_localuser *u = params;
+ struct gen_state *state;
+
+ if (!(state = ast_calloc(1, sizeof(*state))))
+ return NULL;
+
+ state->u = u;
+
+ return state;
+}
+
+static void gen_closestream(struct gen_state *state)
+{
+ if (!state->stream)
+ return;
+
+ ast_closestream(state->stream);
+ state->u->chan->stream = NULL;
+ state->stream = NULL;
+}
+
+static void gen_release(struct ast_channel *chan, void *data)
+{
+ struct gen_state *state = data;
+
+ gen_closestream(state);
+ ast_free(data);
+}
+
+/* caller has the playlist locked */
+static int gen_nextfile(struct gen_state *state)
+{
+ struct ivr_localuser *u = state->u;
+ char *file_to_stream;
+
+ u->abort_current_sound = 0;
+ u->playing_silence = 0;
+ gen_closestream(state);
+
+ while (!state->stream) {
+ state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
+ if (state->current) {
+ file_to_stream = state->current->filename;
+ } else {
+ file_to_stream = "silence/10";
+ u->playing_silence = 1;
+ }
+
+ if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
+ ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
+ if (!u->playing_silence) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return (!state->stream);
+}
+
+static struct ast_frame *gen_readframe(struct gen_state *state)
+{
+ struct ast_frame *f = NULL;
+ struct ivr_localuser *u = state->u;
+
+ if (u->abort_current_sound ||
+ (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
+ gen_closestream(state);
+ AST_LIST_LOCK(&u->playlist);
+ gen_nextfile(state);
+ AST_LIST_UNLOCK(&u->playlist);
+ }
+
+ if (!(state->stream && (f = ast_readframe(state->stream)))) {
+ if (state->current) {
+ AST_LIST_LOCK(&u->finishlist);
+ AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
+ AST_LIST_UNLOCK(&u->finishlist);
+ state->current = NULL;
+ }
+ if (!gen_nextfile(state))
+ f = ast_readframe(state->stream);
+ }
+
+ return f;
+}
+
+static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct gen_state *state = data;
+ struct ast_frame *f = NULL;
+ int res = 0;
+
+ state->sample_queue += samples;
+
+ while (state->sample_queue > 0) {
+ if (!(f = gen_readframe(state)))
+ return -1;
+
+ res = ast_write(chan, f);
+ ast_frfree(f);
+ if (res < 0) {
+ ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
+ return -1;
+ }
+ state->sample_queue -= f->samples;
+ }
+
+ return res;
+}
+
+static struct ast_generator gen =
+{
+ alloc: gen_alloc,
+ release: gen_release,
+ generate: gen_generate,
+};
+
+static struct playlist_entry *make_entry(const char *filename)
+{
+ struct playlist_entry *entry;
+
+ if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
+ return NULL;
+
+ strcpy(entry->filename, filename);
+
+ return entry;
+}
+
+static int app_exec(struct ast_channel *chan, void *data)
+{
+ struct playlist_entry *entry;
+ int child_stdin[2] = { 0,0 };
+ int child_stdout[2] = { 0,0 };
+ int child_stderr[2] = { 0,0 };
+ int res = -1;
+ int gen_active = 0;
+ int pid;
+ char *buf, *command;
+ FILE *child_commands = NULL;
+ FILE *child_errors = NULL;
+ FILE *child_events = NULL;
+ struct ivr_localuser foo = {
+ .playlist = AST_LIST_HEAD_INIT_VALUE,
+ .finishlist = AST_LIST_HEAD_INIT_VALUE,
+ };
+ struct ivr_localuser *u = &foo;
+ sigset_t fullset, oldset;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(cmd)[32];
+ );
+
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
+
+ u->abort_current_sound = 0;
+ u->chan = chan;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
+ return -1;
+ }
+
+ buf = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, buf);
+
+ if (pipe(child_stdin)) {
+ ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (pipe(child_stdout)) {
+ ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (pipe(child_stderr)) {
+ ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (chan->_state != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+
+ if (ast_activate_generator(chan, &gen, u) < 0) {
+ ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
+ goto exit;
+ } else
+ gen_active = 1;
+
+ pid = fork();
+ if (pid < 0) {
+ ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
+ goto exit;
+ }
+
+ if (!pid) {
+ /* child process */
+ int i;
+
+ signal(SIGPIPE, SIG_DFL);
+ pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
+
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+
+ dup2(child_stdin[0], STDIN_FILENO);
+ dup2(child_stdout[1], STDOUT_FILENO);
+ dup2(child_stderr[1], STDERR_FILENO);
+ for (i = STDERR_FILENO + 1; i < 1024; i++)
+ close(i);
+ execv(args.cmd[0], args.cmd);
+ fprintf(stderr, "Failed to execute '%s': %s\n", args.cmd[0], strerror(errno));
+ _exit(1);
+ } else {
+ /* parent process */
+ int child_events_fd = child_stdin[1];
+ int child_commands_fd = child_stdout[0];
+ int child_errors_fd = child_stderr[0];
+ struct ast_frame *f;
+ int ms;
+ int exception;
+ int ready_fd;
+ int waitfds[2] = { child_errors_fd, child_commands_fd };
+ struct ast_channel *rchan;
+
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+
+ close(child_stdin[0]);
+ child_stdin[0] = 0;
+ close(child_stdout[1]);
+ child_stdout[1] = 0;
+ close(child_stderr[1]);
+ child_stderr[1] = 0;
+
+ if (!(child_events = fdopen(child_events_fd, "w"))) {
+ ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n");
+ goto exit;
+ }
+
+ if (!(child_commands = fdopen(child_commands_fd, "r"))) {
+ ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n");
+ goto exit;
+ }
+
+ if (!(child_errors = fdopen(child_errors_fd, "r"))) {
+ ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n");
+ goto exit;
+ }
+
+ setvbuf(child_events, NULL, _IONBF, 0);
+ setvbuf(child_commands, NULL, _IONBF, 0);
+ setvbuf(child_errors, NULL, _IONBF, 0);
+
+ res = 0;
+
+ while (1) {
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
+ ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
+ res = -1;
+ break;
+ }
+
+ if (ast_check_hangup(chan)) {
+ ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
+ send_child_event(child_events, 'H', NULL, chan);
+ res = -1;
+ break;
+ }
+
+ ready_fd = 0;
+ ms = 100;
+ errno = 0;
+ exception = 0;
+
+ rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms);
+
+ if (!AST_LIST_EMPTY(&u->finishlist)) {
+ AST_LIST_LOCK(&u->finishlist);
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
+ send_child_event(child_events, 'F', entry->filename, chan);
+ ast_free(entry);
+ }
+ AST_LIST_UNLOCK(&u->finishlist);
+ }
+
+ if (rchan) {
+ /* the channel has something */
+ f = ast_read(chan);
+ if (!f) {
+ ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
+ send_child_event(child_events, 'H', NULL, chan);
+ res = -1;
+ break;
+ }
+
+ if (f->frametype == AST_FRAME_DTMF) {
+ send_child_event(child_events, f->subclass, NULL, chan);
+ if (u->option_autoclear) {
+ if (!u->abort_current_sound && !u->playing_silence)
+ send_child_event(child_events, 'T', NULL, chan);
+ AST_LIST_LOCK(&u->playlist);
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
+ send_child_event(child_events, 'D', entry->filename, chan);
+ ast_free(entry);
+ }
+ if (!u->playing_silence)
+ u->abort_current_sound = 1;
+ AST_LIST_UNLOCK(&u->playlist);
+ }
+ } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
+ ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
+ send_child_event(child_events, 'H', NULL, chan);
+ ast_frfree(f);
+ res = -1;
+ break;
+ }
+ ast_frfree(f);
+ } else if (ready_fd == child_commands_fd) {
+ char input[1024];
+
+ if (exception || feof(child_commands)) {
+ ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
+ res = -1;
+ break;
+ }
+
+ if (!fgets(input, sizeof(input), child_commands))
+ continue;
+
+ command = ast_strip(input);
+
+ if (option_debug)
+ ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);
+
+ if (strlen(input) < 4)
+ continue;
+
+ if (input[0] == 'S') {
+ if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
+ ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
+ send_child_event(child_events, 'Z', NULL, chan);
+ strcpy(&input[2], "exception");
+ }
+ if (!u->abort_current_sound && !u->playing_silence)
+ send_child_event(child_events, 'T', NULL, chan);
+ AST_LIST_LOCK(&u->playlist);
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
+ send_child_event(child_events, 'D', entry->filename, chan);
+ ast_free(entry);
+ }
+ if (!u->playing_silence)
+ u->abort_current_sound = 1;
+ entry = make_entry(&input[2]);
+ if (entry)
+ AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
+ AST_LIST_UNLOCK(&u->playlist);
+ } else if (input[0] == 'A') {
+ if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
+ ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
+ send_child_event(child_events, 'Z', NULL, chan);
+ strcpy(&input[2], "exception");
+ }
+ entry = make_entry(&input[2]);
+ if (entry) {
+ AST_LIST_LOCK(&u->playlist);
+ AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
+ AST_LIST_UNLOCK(&u->playlist);
+ }
+ } else if (input[0] == 'E') {
+ ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
+ send_child_event(child_events, 'E', NULL, chan);
+ res = 0;
+ break;
+ } else if (input[0] == 'H') {
+ ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
+ send_child_event(child_events, 'H', NULL, chan);
+ res = -1;
+ break;
+ } else if (input[0] == 'O') {
+ if (!strcasecmp(&input[2], "autoclear"))
+ u->option_autoclear = 1;
+ else if (!strcasecmp(&input[2], "noautoclear"))
+ u->option_autoclear = 0;
+ else
+ ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
+ } else if (input[0] == 'V') {
+ char *c;
+ c = strchr(&input[2], '=');
+ if (!c) {
+ send_child_event(child_events, 'Z', NULL, chan);
+ } else {
+ *c++ = '\0';
+ pbx_builtin_setvar_helper(chan, &input[2], c);
+ }
+ }
+ } else if (ready_fd == child_errors_fd) {
+ char input[1024];
+
+ if (exception || feof(child_errors)) {
+ ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
+ res = -1;
+ break;
+ }
+
+ if (fgets(input, sizeof(input), child_errors)) {
+ command = ast_strip(input);
+ ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
+ }
+ } else if ((ready_fd < 0) && ms) {
+ if (errno == 0 || errno == EINTR)
+ continue;
+
+ ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
+ break;
+ }
+ }
+ }
+
+ exit:
+ if (gen_active)
+ ast_deactivate_generator(chan);
+
+ if (child_events)
+ fclose(child_events);
+
+ if (child_commands)
+ fclose(child_commands);
+
+ if (child_errors)
+ fclose(child_errors);
+
+ if (child_stdin[0])
+ close(child_stdin[0]);
+
+ if (child_stdin[1])
+ close(child_stdin[1]);
+
+ if (child_stdout[0])
+ close(child_stdout[0]);
+
+ if (child_stdout[1])
+ close(child_stdout[1]);
+
+ if (child_stderr[0])
+ close(child_stderr[0]);
+
+ if (child_stderr[1])
+ close(child_stderr[1]);
+
+ while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
+ ast_free(entry);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, app_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");
diff --git a/trunk/apps/app_festival.c b/trunk/apps/app_festival.c
new file mode 100644
index 000000000..926638e57
--- /dev/null
+++ b/trunk/apps/app_festival.c
@@ -0,0 +1,523 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002, Christos Ricudis
+ *
+ * Christos Ricudis <ricudis@itc.auth.gr>
+ *
+ * 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 Connect to festival
+ *
+ * \author Christos Ricudis <ricudis@itc.auth.gr>
+ *
+ * \extref The Festival Speech Synthesis System - http://www.cstr.ed.ac.uk/projects/festival/
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/md5.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+#define FESTIVAL_CONFIG "festival.conf"
+#define MAXLEN 180
+#define MAXFESTLEN 2048
+
+static char *app = "Festival";
+
+static char *synopsis = "Say text to the user";
+
+static char *descrip =
+" Festival(text[,intkeys]): Connect to Festival, send the argument, get back the waveform,\n"
+"play it to the user, allowing any given interrupt keys to immediately terminate and return\n"
+"the value, or 'any' to allow any number back (useful in dialplan)\n";
+
+
+static char *socket_receive_file_to_buff(int fd, int *size)
+{
+ /* Receive file (probably a waveform file) from socket using
+ * Festival key stuff technique, but long winded I know, sorry
+ * but will receive any file without closing the stream or
+ * using OOB data
+ */
+ static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
+ char *buff, *tmp;
+ int bufflen;
+ int n,k,i;
+ char c;
+
+ bufflen = 1024;
+ if (!(buff = ast_malloc(bufflen)))
+ return NULL;
+ *size = 0;
+
+ for (k = 0; file_stuff_key[k] != '\0';) {
+ n = read(fd, &c, 1);
+ if (n == 0)
+ break; /* hit stream eof before end of file */
+ if ((*size) + k + 1 >= bufflen) {
+ /* +1 so you can add a terminating NULL if you want */
+ bufflen += bufflen / 4;
+ if (!(tmp = ast_realloc(buff, bufflen))) {
+ ast_free(buff);
+ return NULL;
+ }
+ buff = tmp;
+ }
+ if (file_stuff_key[k] == c)
+ k++;
+ else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) {
+ /* It looked like the key but wasn't */
+ for (i = 0; i < k; i++, (*size)++)
+ buff[*size] = file_stuff_key[i];
+ k = 0;
+ /* omit the stuffed 'X' */
+ } else {
+ for (i = 0; i < k; i++, (*size)++)
+ buff[*size] = file_stuff_key[i];
+ k = 0;
+ buff[*size] = c;
+ (*size)++;
+ }
+ }
+
+ return buff;
+}
+
+static int send_waveform_to_fd(char *waveform, int length, int fd)
+{
+ int res;
+ int x;
+#ifdef __PPC__
+ char c;
+#endif
+ sigset_t fullset, oldset;
+
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
+
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res) {
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ return res;
+ }
+ for (x = 0; x < 256; x++) {
+ if (x != fd)
+ close(x);
+ }
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+ signal(SIGPIPE, SIG_DFL);
+ pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
+#ifdef __PPC__
+ for (x = 0; x < length; x += 2) {
+ c = *(waveform + x + 1);
+ *(waveform + x + 1) = *(waveform + x);
+ *(waveform + x) = c;
+ }
+#endif
+ write(fd, waveform, length);
+ close(fd);
+ exit(0);
+}
+
+static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length, char *intkeys)
+{
+ int res = 0;
+ int fds[2];
+ int pid = -1;
+ int needed = 0;
+ int owriteformat;
+ struct ast_frame *f;
+ struct myframe {
+ struct ast_frame f;
+ char offset[AST_FRIENDLY_OFFSET];
+ char frdata[2048];
+ } myf = {
+ .f = { 0, },
+ };
+
+ if (pipe(fds)) {
+ ast_log(LOG_WARNING, "Unable to create pipe\n");
+ return -1;
+ }
+
+ /* Answer if it's not already going */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+ ast_stopstream(chan);
+ ast_indicate(chan, -1);
+
+ owriteformat = chan->writeformat;
+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ return -1;
+ }
+
+ res = send_waveform_to_fd(waveform, length, fds[1]);
+ if (res >= 0) {
+ pid = res;
+ /* Order is important -- there's almost always going to be mp3... we want to prioritize the
+ user */
+ for (;;) {
+ res = ast_waitfor(chan, 1000);
+ if (res < 1) {
+ res = -1;
+ break;
+ }
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ ast_debug(1, "User pressed a key\n");
+ if (intkeys && strchr(intkeys, f->subclass)) {
+ res = f->subclass;
+ ast_frfree(f);
+ break;
+ }
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ /* Treat as a generator */
+ needed = f->samples * 2;
+ if (needed > sizeof(myf.frdata)) {
+ ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
+ (int)sizeof(myf.frdata) / 2, needed/2);
+ needed = sizeof(myf.frdata);
+ }
+ res = read(fds[0], myf.frdata, needed);
+ if (res > 0) {
+ myf.f.frametype = AST_FRAME_VOICE;
+ myf.f.subclass = AST_FORMAT_SLINEAR;
+ myf.f.datalen = res;
+ myf.f.samples = res / 2;
+ myf.f.offset = AST_FRIENDLY_OFFSET;
+ myf.f.src = __PRETTY_FUNCTION__;
+ myf.f.data = myf.frdata;
+ if (ast_write(chan, &myf.f) < 0) {
+ res = -1;
+ ast_frfree(f);
+ break;
+ }
+ if (res < needed) { /* last frame */
+ ast_debug(1, "Last frame\n");
+ res = 0;
+ ast_frfree(f);
+ break;
+ }
+ } else {
+ ast_debug(1, "No more waveform\n");
+ res = 0;
+ }
+ }
+ ast_frfree(f);
+ }
+ }
+ close(fds[0]);
+ close(fds[1]);
+
+#if 0
+ if (pid > -1)
+ kill(pid, SIGKILL);
+#endif
+ if (!res && owriteformat)
+ ast_set_write_format(chan, owriteformat);
+ return res;
+}
+
+static int festival_exec(struct ast_channel *chan, void *vdata)
+{
+ int usecache;
+ int res = 0;
+ struct sockaddr_in serv_addr;
+ struct hostent *serverhost;
+ struct ast_hostent ahp;
+ int fd;
+ FILE *fs;
+ const char *host;
+ const char *cachedir;
+ const char *temp;
+ const char *festivalcommand;
+ int port = 1314;
+ int n;
+ char ack[4];
+ char *waveform;
+ int filesize;
+ int wave;
+ char bigstring[MAXFESTLEN];
+ int i;
+ struct MD5Context md5ctx;
+ unsigned char MD5Res[16];
+ char MD5Hex[33] = "";
+ char koko[4] = "";
+ char cachefile[MAXFESTLEN]="";
+ int readcache = 0;
+ int writecache = 0;
+ int strln;
+ int fdesc = -1;
+ char buffer[16384];
+ int seekpos = 0;
+ char *data;
+ struct ast_config *cfg;
+ char *newfestivalcommand;
+ struct ast_flags config_flags = { 0 };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(text);
+ AST_APP_ARG(interrupt);
+ );
+
+ if (ast_strlen_zero(vdata)) {
+ ast_log(LOG_WARNING, "festival requires an argument (text)\n");
+ return -1;
+ }
+
+ cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
+ return -1;
+ }
+ if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
+ host = "localhost";
+ }
+ if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
+ port = 1314;
+ } else {
+ port = atoi(temp);
+ }
+ if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
+ usecache = 0;
+ } else {
+ usecache = ast_true(temp);
+ }
+ if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
+ cachedir = "/tmp/";
+ }
+ if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
+ festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n";
+ } else { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */
+ int i, j;
+ newfestivalcommand = alloca(strlen(festivalcommand) + 1);
+
+ for (i = 0, j = 0; i < strlen(festivalcommand); i++) {
+ if (festivalcommand[i] == '\\' && festivalcommand[i + 1] == 'n') {
+ newfestivalcommand[j++] = '\n';
+ i++;
+ } else if (festivalcommand[i] == '\\') {
+ newfestivalcommand[j++] = festivalcommand[i + 1];
+ i++;
+ } else
+ newfestivalcommand[j++] = festivalcommand[i];
+ }
+ newfestivalcommand[j] = '\0';
+ festivalcommand = newfestivalcommand;
+ }
+
+ data = ast_strdupa(vdata);
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.interrupt && !strcasecmp(args.interrupt, "any"))
+ args.interrupt = AST_DIGIT_ANY;
+
+ ast_debug(1, "Text passed to festival server : %s\n", args.text);
+ /* Connect to local festival server */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "festival_client: can't get socket\n");
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+
+ if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
+ /* its a name rather than an ipnum */
+ serverhost = ast_gethostbyname(host, &ahp);
+
+ if (serverhost == NULL) {
+ ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
+ }
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(port);
+
+ if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
+ ast_log(LOG_WARNING, "festival_client: connect to server failed\n");
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ /* Compute MD5 sum of string */
+ MD5Init(&md5ctx);
+ MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text));
+ MD5Final(MD5Res, &md5ctx);
+ MD5Hex[0] = '\0';
+
+ /* Convert to HEX and look if there is any matching file in the cache
+ directory */
+ for (i = 0; i < 16; i++) {
+ snprintf(koko, sizeof(koko), "%X", MD5Res[i]);
+ strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1);
+ }
+ readcache = 0;
+ writecache = 0;
+ if (strlen(cachedir) + strlen(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
+ snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
+ fdesc = open(cachefile, O_RDWR);
+ if (fdesc == -1) {
+ fdesc = open(cachefile, O_CREAT | O_RDWR, AST_FILE_MODE);
+ if (fdesc != -1) {
+ writecache = 1;
+ strln = strlen(args.text);
+ ast_debug(1, "line length : %d\n", strln);
+ write(fdesc, &strln, sizeof(strln));
+ write(fdesc, args.text, strln);
+ seekpos = lseek(fdesc, 0, SEEK_CUR);
+ ast_debug(1, "Seek position : %d\n", seekpos);
+ }
+ } else {
+ read(fdesc, &strln, sizeof(strln));
+ ast_debug(1, "Cache file exists, strln=%d, strlen=%d\n", strln, (int)strlen(args.text));
+ if (strlen(args.text) == strln) {
+ ast_debug(1, "Size OK\n");
+ read(fdesc, &bigstring, strln);
+ bigstring[strln] = 0;
+ if (strcmp(bigstring, args.text) == 0) {
+ readcache = 1;
+ } else {
+ ast_log(LOG_WARNING, "Strings do not match\n");
+ }
+ } else {
+ ast_log(LOG_WARNING, "Size mismatch\n");
+ }
+ }
+ }
+
+ if (readcache == 1) {
+ close(fd);
+ fd = fdesc;
+ ast_debug(1, "Reading from cache...\n");
+ } else {
+ ast_debug(1, "Passing text to festival...\n");
+ fs = fdopen(dup(fd), "wb");
+ fprintf(fs, festivalcommand, args.text);
+ fflush(fs);
+ fclose(fs);
+ }
+
+ /* Write to cache and then pass it down */
+ if (writecache == 1) {
+ ast_debug(1, "Writing result to cache...\n");
+ while ((strln = read(fd, buffer, 16384)) != 0) {
+ write(fdesc, buffer, strln);
+ }
+ close(fd);
+ close(fdesc);
+ fd = open(cachefile, O_RDWR);
+ lseek(fd, seekpos, SEEK_SET);
+ }
+
+ ast_debug(1, "Passing data to channel...\n");
+
+ /* Read back info from server */
+ /* This assumes only one waveform will come back, also LP is unlikely */
+ wave = 0;
+ do {
+ int read_data;
+ for (n = 0; n < 3; ) {
+ read_data = read(fd, ack + n, 3 - n);
+ /* this avoids falling in infinite loop
+ * in case that festival server goes down
+ */
+ if (read_data == -1) {
+ ast_log(LOG_WARNING, "Unable to read from cache/festival fd\n");
+ close(fd);
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ n += read_data;
+ }
+ ack[3] = '\0';
+ if (strcmp(ack, "WV\n") == 0) { /* receive a waveform */
+ ast_debug(1, "Festival WV command\n");
+ if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
+ res = send_waveform_to_channel(chan, waveform, filesize, args.interrupt);
+ ast_free(waveform);
+ }
+ break;
+ } else if (strcmp(ack, "LP\n") == 0) { /* receive an s-expr */
+ ast_debug(1, "Festival LP command\n");
+ if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
+ waveform[filesize] = '\0';
+ ast_log(LOG_WARNING, "Festival returned LP : %s\n", waveform);
+ ast_free(waveform);
+ }
+ } else if (strcmp(ack, "ER\n") == 0) { /* server got an error */
+ ast_log(LOG_WARNING, "Festival returned ER\n");
+ res = -1;
+ break;
+ }
+ } while (strcmp(ack, "OK\n") != 0);
+ close(fd);
+ ast_config_destroy(cfg);
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ struct ast_flags config_flags = { 0 };
+ struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ ast_config_destroy(cfg);
+ return ast_register_application(app, festival_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Festival Interface");
diff --git a/trunk/apps/app_flash.c b/trunk/apps/app_flash.c
new file mode 100644
index 000000000..d57feeb91
--- /dev/null
+++ b/trunk/apps/app_flash.c
@@ -0,0 +1,112 @@
+/*
+ * 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 App to flash a zap trunk
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>zaptel</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/zapata.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+
+static char *app = "Flash";
+
+static char *synopsis = "Flashes a Zap Trunk";
+
+static char *descrip =
+"Performs a flash on a zap trunk. This can be used\n"
+"to access features provided on an incoming analogue circuit\n"
+"such as conference and call waiting. Use with SendDTMF() to\n"
+"perform external transfers\n";
+
+
+static inline int zt_wait_event(int fd)
+{
+ /* Avoid the silly zt_waitevent which ignores a bunch of events */
+ int i,j=0;
+ i = ZT_IOMUX_SIGEVENT;
+ if (ioctl(fd, ZT_IOMUX, &i) == -1) return -1;
+ if (ioctl(fd, ZT_GETEVENT, &j) == -1) return -1;
+ return j;
+}
+
+static int flash_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ int x;
+ struct zt_params ztp;
+
+ if (strcasecmp(chan->tech->type, "Zap")) {
+ ast_log(LOG_WARNING, "%s is not a Zap channel\n", chan->name);
+ return -1;
+ }
+
+ memset(&ztp, 0, sizeof(ztp));
+ res = ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp);
+ if (!res) {
+ if (ztp.sigtype & __ZT_SIG_FXS) {
+ x = ZT_FLASH;
+ res = ioctl(chan->fds[0], ZT_HOOK, &x);
+ if (!res || (errno == EINPROGRESS)) {
+ if (res) {
+ /* Wait for the event to finish */
+ zt_wait_event(chan->fds[0]);
+ }
+ res = ast_safe_sleep(chan, 1000);
+ ast_verb(3, "Flashed channel %s\n", chan->name);
+ } else
+ ast_log(LOG_WARNING, "Unable to flash channel %s: %s\n", chan->name, strerror(errno));
+ } else
+ ast_log(LOG_WARNING, "%s is not an FXO Channel\n", chan->name);
+ } else
+ ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", chan->name, strerror(errno));
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, flash_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Flash channel application");
+
diff --git a/trunk/apps/app_followme.c b/trunk/apps/app_followme.c
new file mode 100644
index 000000000..f670d65c0
--- /dev/null
+++ b/trunk/apps/app_followme.c
@@ -0,0 +1,1059 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * A full-featured Find-Me/Follow-Me Application
+ *
+ * Copyright (C) 2005-2006, BJ Weschke All Rights Reserved.
+ *
+ * BJ Weschke <bweschke@btwtech.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Find-Me Follow-Me application
+ *
+ * \author BJ Weschke <bweschke@btwtech.com>
+ *
+ * \arg See \ref Config_followme
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/say.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/config.h"
+#include "asterisk/monitor.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/astdb.h"
+#include "asterisk/app.h"
+
+static char *app = "FollowMe";
+static char *synopsis = "Find-Me/Follow-Me application";
+static char *descrip =
+" FollowMe(followmeid[,options]):\n"
+"This application performs Find-Me/Follow-Me functionality for the caller\n"
+"as defined in the profile matching the <followmeid> parameter in\n"
+"followme.conf. If the specified <followmeid> profile doesn't exist in\n"
+"followme.conf, execution will be returned to the dialplan and call\n"
+"execution will continue at the next priority.\n\n"
+" Options:\n"
+" s - Playback the incoming status message prior to starting the follow-me step(s)\n"
+" a - Record the caller's name so it can be announced to the callee on each step\n"
+" n - Playback the unreachable status message if we've run out of steps to reach the\n"
+" or the callee has elected not to be reachable.\n"
+"Returns -1 on hangup\n";
+
+/*! \brief Number structure */
+struct number {
+ char number[512]; /*!< Phone Number(s) and/or Extension(s) */
+ long timeout; /*!< Dial Timeout, if used. */
+ int order; /*!< The order to dial in */
+ AST_LIST_ENTRY(number) entry; /*!< Next Number record */
+};
+
+/*! \brief Data structure for followme scripts */
+struct call_followme {
+ ast_mutex_t lock;
+ char name[AST_MAX_EXTENSION]; /*!< Name - FollowMeID */
+ char moh[AST_MAX_CONTEXT]; /*!< Music On Hold Class to be used */
+ char context[AST_MAX_CONTEXT]; /*!< Context to dial from */
+ unsigned int active; /*!< Profile is active (1), or disabled (0). */
+ char takecall[20]; /*!< Digit mapping to take a call */
+ char nextindp[20]; /*!< Digit mapping to decline a call */
+ char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
+
+ AST_LIST_HEAD_NOLOCK(numbers, number) numbers; /*!< Head of the list of follow-me numbers */
+ AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */
+ AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */
+ AST_LIST_ENTRY(call_followme) entry; /*!< Next Follow-Me record */
+};
+
+struct fm_args {
+ struct ast_channel *chan;
+ char *mohclass;
+ AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
+ int status;
+ char context[AST_MAX_CONTEXT];
+ char namerecloc[AST_MAX_CONTEXT];
+ struct ast_channel *outbound;
+ char takecall[20]; /*!< Digit mapping to take a call */
+ char nextindp[20]; /*!< Digit mapping to decline a call */
+ char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char norecordingprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char optionsprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char plsholdprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char statusprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ char sorryprompt[PATH_MAX]; /*!< Sound prompt name and path */
+ struct ast_flags followmeflags;
+};
+
+struct findme_user {
+ struct ast_channel *ochan;
+ int state;
+ char dialarg[256];
+ char yn[10];
+ int ynidx;
+ long digts;
+ int cleared;
+ AST_LIST_ENTRY(findme_user) entry;
+};
+
+enum {
+ FOLLOWMEFLAG_STATUSMSG = (1 << 0),
+ FOLLOWMEFLAG_RECORDNAME = (1 << 1),
+ FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2)
+};
+
+AST_APP_OPTIONS(followme_opts, {
+ AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ),
+ AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ),
+ AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ),
+});
+
+static int ynlongest = 0;
+static time_t start_time, answer_time, end_time;
+
+static const char *featuredigittostr;
+static int featuredigittimeout = 5000; /*!< Feature Digit Timeout */
+static const char *defaultmoh = "default"; /*!< Default Music-On-Hold Class */
+
+static char takecall[20] = "1", nextindp[20] = "2";
+static char callfromprompt[PATH_MAX] = "followme/call-from";
+static char norecordingprompt[PATH_MAX] = "followme/no-recording";
+static char optionsprompt[PATH_MAX] = "followme/options";
+static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
+static char statusprompt[PATH_MAX] = "followme/status";
+static char sorryprompt[PATH_MAX] = "followme/sorry";
+
+
+static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
+AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
+
+static void free_numbers(struct call_followme *f)
+{
+ /* Free numbers attached to the profile */
+ struct number *prev;
+
+ while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
+ /* Free the number */
+ ast_free(prev);
+ AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
+
+ while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
+ /* Free the blacklisted number */
+ ast_free(prev);
+ AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
+
+ while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
+ /* Free the whitelisted number */
+ ast_free(prev);
+ AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
+
+}
+
+
+/*! \brief Allocate and initialize followme profile */
+static struct call_followme *alloc_profile(const char *fmname)
+{
+ struct call_followme *f;
+
+ if (!(f = ast_calloc(1, sizeof(*f))))
+ return NULL;
+
+ ast_mutex_init(&f->lock);
+ ast_copy_string(f->name, fmname, sizeof(f->name));
+ f->moh[0] = '\0';
+ f->context[0] = '\0';
+ ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
+ ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
+ ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
+ ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
+ ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
+ ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
+ ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
+ ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
+ AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
+ AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
+ AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
+ return f;
+}
+
+static void init_profile(struct call_followme *f)
+{
+ f->active = 1;
+ ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
+}
+
+
+
+/*! \brief Set parameter in profile from configuration file */
+static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
+{
+
+ if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
+ ast_copy_string(f->moh, val, sizeof(f->moh));
+ else if (!strcasecmp(param, "context"))
+ ast_copy_string(f->context, val, sizeof(f->context));
+ else if (!strcasecmp(param, "takecall"))
+ ast_copy_string(f->takecall, val, sizeof(f->takecall));
+ else if (!strcasecmp(param, "declinecall"))
+ ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
+ else if (!strcasecmp(param, "call-from-prompt"))
+ ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
+ else if (!strcasecmp(param, "followme-norecording-prompt"))
+ ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
+ else if (!strcasecmp(param, "followme-options-prompt"))
+ ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
+ else if (!strcasecmp(param, "followme-pls-hold-prompt"))
+ ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
+ else if (!strcasecmp(param, "followme-status-prompt"))
+ ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
+ else if (!strcasecmp(param, "followme-sorry-prompt"))
+ ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
+ else if (failunknown) {
+ if (linenum >= 0)
+ ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
+ else
+ ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
+ }
+}
+
+/*! \brief Add a new number */
+static struct number *create_followme_number(char *number, int timeout, int numorder)
+{
+ struct number *cur;
+ char *tmp;
+
+
+ if (!(cur = ast_calloc(1, sizeof(*cur))))
+ return NULL;
+
+ cur->timeout = timeout;
+ if ((tmp = strchr(number, ',')))
+ *tmp = '\0';
+ ast_copy_string(cur->number, number, sizeof(cur->number));
+ cur->order = numorder;
+ ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
+
+ return cur;
+}
+
+/*! \brief Reload followme application module */
+static int reload_followme(int reload)
+{
+ struct call_followme *f;
+ struct ast_config *cfg;
+ char *cat = NULL, *tmp;
+ struct ast_variable *var;
+ struct number *cur, *nm;
+ char numberstr[90];
+ int timeout;
+ char *timeoutstr;
+ int numorder;
+ const char *takecallstr;
+ const char *declinecallstr;
+ const char *tmpstr;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if (!(cfg = ast_config_load("followme.conf", config_flags))) {
+ ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ AST_RWLIST_WRLOCK(&followmes);
+
+ /* Reset Global Var Values */
+ featuredigittimeout = 5000;
+
+ /* Mark all profiles as inactive for the moment */
+ AST_RWLIST_TRAVERSE(&followmes, f, entry) {
+ f->active = 0;
+ }
+
+ featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
+
+ if (!ast_strlen_zero(featuredigittostr)) {
+ if (!sscanf(featuredigittostr, "%d", &featuredigittimeout))
+ featuredigittimeout = 5000;
+ }
+
+ takecallstr = ast_variable_retrieve(cfg, "general", "takecall");
+ if (!ast_strlen_zero(takecallstr))
+ ast_copy_string(takecall, takecallstr, sizeof(takecall));
+
+ declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall");
+ if (!ast_strlen_zero(declinecallstr))
+ ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
+
+ tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt");
+ if (!ast_strlen_zero(tmpstr))
+ ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
+
+ tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt");
+ if (!ast_strlen_zero(tmpstr))
+ ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
+
+ tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt");
+ if (!ast_strlen_zero(tmpstr))
+ ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
+
+ tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt");
+ if (!ast_strlen_zero(tmpstr))
+ ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
+
+ tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt");
+ if (!ast_strlen_zero(tmpstr))
+ ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
+
+ tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt");
+ if (!ast_strlen_zero(tmpstr))
+ ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
+
+ /* Chug through config file */
+ while ((cat = ast_category_browse(cfg, cat))) {
+ int new = 0;
+
+ if (!strcasecmp(cat, "general"))
+ continue;
+
+ /* Look for an existing one */
+ AST_LIST_TRAVERSE(&followmes, f, entry) {
+ if (!strcasecmp(f->name, cat))
+ break;
+ }
+
+ ast_debug(1, "New profile %s.\n", cat);
+
+ if (!f) {
+ /* Make one then */
+ f = alloc_profile(cat);
+ new = 1;
+ }
+
+ /* Totally fail if we fail to find/create an entry */
+ if (!f)
+ continue;
+
+ if (!new)
+ ast_mutex_lock(&f->lock);
+ /* Re-initialize the profile */
+ init_profile(f);
+ free_numbers(f);
+ var = ast_variable_browse(cfg, cat);
+ while(var) {
+ if (!strcasecmp(var->name, "number")) {
+ int idx = 0;
+
+ /* Add a new number */
+ ast_copy_string(numberstr, var->value, sizeof(numberstr));
+ if ((tmp = strchr(numberstr, ','))) {
+ *tmp++ = '\0';
+ timeoutstr = ast_strdupa(tmp);
+ if ((tmp = strchr(timeoutstr, ','))) {
+ *tmp++ = '\0';
+ numorder = atoi(tmp);
+ if (numorder < 0)
+ numorder = 0;
+ } else
+ numorder = 0;
+ timeout = atoi(timeoutstr);
+ if (timeout < 0)
+ timeout = 25;
+ } else {
+ timeout = 25;
+ numorder = 0;
+ }
+
+ if (!numorder) {
+ idx = 1;
+ AST_LIST_TRAVERSE(&f->numbers, nm, entry)
+ idx++;
+ numorder = idx;
+ }
+ cur = create_followme_number(numberstr, timeout, numorder);
+ AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
+ } else {
+ profile_set_param(f, var->name, var->value, var->lineno, 1);
+ ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
+ }
+ var = var->next;
+ } /* End while(var) loop */
+
+ if (!new)
+ ast_mutex_unlock(&f->lock);
+ else
+ AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
+ }
+
+ ast_config_destroy(cfg);
+
+ AST_RWLIST_UNLOCK(&followmes);
+
+ return 1;
+}
+
+static void clear_caller(struct findme_user *tmpuser)
+{
+ struct ast_channel *outbound;
+
+ if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
+ outbound = tmpuser->ochan;
+ if (!outbound->cdr) {
+ outbound->cdr = ast_cdr_alloc();
+ if (outbound->cdr)
+ ast_cdr_init(outbound->cdr, outbound);
+ }
+ if (outbound->cdr) {
+ char tmp[256];
+
+ snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
+ ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
+ ast_cdr_update(outbound);
+ ast_cdr_start(outbound->cdr);
+ ast_cdr_end(outbound->cdr);
+ /* If the cause wasn't handled properly */
+ if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
+ ast_cdr_failed(outbound->cdr);
+ } else
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ ast_hangup(tmpuser->ochan);
+ }
+
+}
+
+static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
+{
+ struct findme_user *tmpuser;
+
+ AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
+ clear_caller(tmpuser);
+ tmpuser->cleared = 1;
+ }
+
+}
+
+
+
+static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, int *status, struct fm_args *tpargs)
+{
+ struct ast_channel *watchers[256];
+ int pos;
+ struct ast_channel *winner;
+ struct ast_frame *f;
+ int ctstatus = 0;
+ int dg;
+ struct findme_user *tmpuser;
+ int to = 0;
+ int livechannels = 0;
+ int tmpto;
+ long totalwait = 0, wtd = 0, towas = 0;
+ char *callfromname;
+ char *pressbuttonname;
+
+ /* ------------ wait_for_winner_channel start --------------- */
+
+ callfromname = ast_strdupa(tpargs->callfromprompt);
+ pressbuttonname = ast_strdupa(tpargs->optionsprompt);
+
+ if (AST_LIST_EMPTY(findme_user_list)) {
+ ast_verb(3, "couldn't reach at this number.\n");
+ return NULL;
+ }
+
+ if (!caller) {
+ ast_verb(3, "Original caller hungup. Cleanup.\n");
+ clear_calling_tree(findme_user_list);
+ return NULL;
+ }
+
+ totalwait = nm->timeout * 1000;
+
+ while (!ctstatus) {
+ to = 1000;
+ pos = 1;
+ livechannels = 0;
+ watchers[0] = caller;
+
+ dg = 0;
+ winner = NULL;
+ AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
+ if (tmpuser->state >= 0 && tmpuser->ochan) {
+ if (tmpuser->state == 3)
+ tmpuser->digts += (towas - wtd);
+ if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
+ ast_verb(3, "We've been waiting for digits longer than we should have.\n");
+ if (!ast_strlen_zero(namerecloc)) {
+ tmpuser->state = 1;
+ tmpuser->digts = 0;
+ if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
+ ast_sched_runq(tmpuser->ochan->sched);
+ } else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
+ return NULL;
+ }
+ } else {
+ tmpuser->state = 2;
+ tmpuser->digts = 0;
+ if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
+ ast_sched_runq(tmpuser->ochan->sched);
+ else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
+ return NULL;
+ }
+ }
+ }
+ if (tmpuser->ochan->stream) {
+ ast_sched_runq(tmpuser->ochan->sched);
+ tmpto = ast_sched_wait(tmpuser->ochan->sched);
+ if (tmpto > 0 && tmpto < to)
+ to = tmpto;
+ else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
+ ast_stopstream(tmpuser->ochan);
+ if (tmpuser->state == 1) {
+ ast_verb(3, "Playback of the call-from file appears to be done.\n");
+ if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
+ tmpuser->state = 2;
+ } else {
+ ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
+ memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
+ tmpuser->ynidx = 0;
+ if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
+ tmpuser->state = 3;
+ else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
+ return NULL;
+ }
+ }
+ } else if (tmpuser->state == 2) {
+ ast_verb(3, "Playback of name file appears to be done.\n");
+ memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
+ tmpuser->ynidx = 0;
+ if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
+ tmpuser->state = 3;
+
+ } else {
+ return NULL;
+ }
+ } else if (tmpuser->state == 3) {
+ ast_verb(3, "Playback of the next step file appears to be done.\n");
+ tmpuser->digts = 0;
+ }
+ }
+ }
+ watchers[pos++] = tmpuser->ochan;
+ livechannels++;
+ }
+ }
+
+ tmpto = to;
+ if (to < 0) {
+ to = 1000;
+ tmpto = 1000;
+ }
+ towas = to;
+ winner = ast_waitfor_n(watchers, pos, &to);
+ tmpto -= to;
+ totalwait -= tmpto;
+ wtd = to;
+ if (totalwait <= 0) {
+ ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
+ clear_calling_tree(findme_user_list);
+ return NULL;
+ }
+ if (winner) {
+ /* Need to find out which channel this is */
+ dg = 0;
+ while ((winner != watchers[dg]) && (dg < 256))
+ dg++;
+ AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
+ if (tmpuser->ochan == winner)
+ break;
+ f = ast_read(winner);
+ if (f) {
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch(f->subclass) {
+ case AST_CONTROL_HANGUP:
+ if (option_verbose > 2)
+ ast_verb(3, "%s received a hangup frame.\n", winner->name);
+ if (dg == 0) {
+ ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
+ clear_calling_tree(findme_user_list);
+ ctstatus = -1;
+ }
+ break;
+ case AST_CONTROL_ANSWER:
+ if (option_verbose > 2)
+ ast_verb(3, "%s answered %s\n", winner->name, caller->name);
+ /* If call has been answered, then the eventual hangup is likely to be normal hangup */
+ winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ ast_verb(3, "Starting playback of %s\n", callfromname);
+ if (dg > 0) {
+ if (!ast_strlen_zero(namerecloc)) {
+ if (!ast_streamfile(winner, callfromname, winner->language)) {
+ ast_sched_runq(winner->sched);
+ tmpuser->state = 1;
+ } else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
+ ast_frfree(f);
+ return NULL;
+ }
+ } else {
+ tmpuser->state = 2;
+ if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
+ ast_sched_runq(tmpuser->ochan->sched);
+ else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
+ ast_frfree(f);
+ return NULL;
+ }
+ }
+ }
+ break;
+ case AST_CONTROL_BUSY:
+ ast_verb(3, "%s is busy\n", winner->name);
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(3, "%s is circuit-busy\n", winner->name);
+ break;
+ case AST_CONTROL_RINGING:
+ ast_verb(3, "%s is ringing\n", winner->name);
+ break;
+ case AST_CONTROL_PROGRESS:
+ ast_verb(3, "%s is making progress passing it to %s\n", winner->name, caller->name);
+ break;
+ case AST_CONTROL_VIDUPDATE:
+ ast_verb(3, "%s requested a video update, passing it to %s\n", winner->name, caller->name);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ ast_verb(3, "%s is proceeding passing it to %s\n", winner->name,caller->name);
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verb(3, "Call on %s placed on hold\n", winner->name);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verb(3, "Call on %s left from hold\n", winner->name);
+ break;
+ case AST_CONTROL_OFFHOOK:
+ case AST_CONTROL_FLASH:
+ /* Ignore going off hook and flash */
+ break;
+ case -1:
+ ast_verb(3, "%s stopped sounds\n", winner->name);
+ break;
+ default:
+ ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
+ break;
+ }
+ }
+ if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
+ if (winner->stream)
+ ast_stopstream(winner);
+ tmpuser->digts = 0;
+ ast_debug(1, "DTMF received: %c\n",(char) f->subclass);
+ tmpuser->yn[tmpuser->ynidx] = (char) f->subclass;
+ tmpuser->ynidx++;
+ ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
+ if (tmpuser->ynidx >= ynlongest) {
+ ast_debug(1, "reached longest possible match - doing evals\n");
+ if (!strcmp(tmpuser->yn, tpargs->takecall)) {
+ ast_debug(1, "Match to take the call!\n");
+ ast_frfree(f);
+ return tmpuser->ochan;
+ }
+ if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
+ ast_debug(1, "Next in dial plan step requested.\n");
+ *status = 1;
+ ast_frfree(f);
+ return NULL;
+ }
+
+ }
+ }
+
+ ast_frfree(f);
+ } else {
+ if (winner) {
+ ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg);
+ if (!dg) {
+ clear_calling_tree(findme_user_list);
+ return NULL;
+ } else {
+ tmpuser->state = -1;
+ ast_hangup(winner);
+ livechannels--;
+ ast_debug(1, "live channels left %d\n", livechannels);
+ if (!livechannels) {
+ ast_verb(3, "no live channels left. exiting.\n");
+ return NULL;
+ }
+ }
+ }
+ }
+
+ } else
+ ast_debug(1, "timed out waiting for action\n");
+ }
+
+ /* --- WAIT FOR WINNER NUMBER END! -----------*/
+ return NULL;
+}
+
+static void findmeexec(struct fm_args *tpargs)
+{
+ struct number *nm;
+ struct ast_channel *outbound;
+ struct ast_channel *caller;
+ struct ast_channel *winner = NULL;
+ char dialarg[512];
+ int dg, idx;
+ char *rest, *number;
+ struct findme_user *tmpuser;
+ struct findme_user *fmuser;
+ struct findme_user *headuser;
+ struct findme_user_listptr *findme_user_list;
+ int status;
+
+ findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
+ AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
+
+ /* We're going to figure out what the longest possible string of digits to collect is */
+ ynlongest = 0;
+ if (strlen(tpargs->takecall) > ynlongest)
+ ynlongest = strlen(tpargs->takecall);
+ if (strlen(tpargs->nextindp) > ynlongest)
+ ynlongest = strlen(tpargs->nextindp);
+
+ idx = 1;
+ caller = tpargs->chan;
+ AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
+ if (nm->order == idx)
+ break;
+
+ while (nm) {
+
+ ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
+ time(&start_time);
+
+ number = ast_strdupa(nm->number);
+ ast_debug(3, "examining %s\n", number);
+ do {
+ rest = strchr(number, '&');
+ if (rest) {
+ *rest = 0;
+ rest++;
+ }
+
+ if (!strcmp(tpargs->context, ""))
+ sprintf(dialarg, "%s", number);
+ else
+ sprintf(dialarg, "%s@%s", number, tpargs->context);
+
+ tmpuser = ast_calloc(1, sizeof(*tmpuser));
+ if (!tmpuser) {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ ast_free(findme_user_list);
+ return;
+ }
+
+ outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
+ if (outbound) {
+ ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
+ ast_channel_inherit_variables(tpargs->chan, outbound);
+ ast_verb(3, "calling %s\n", dialarg);
+ if (!ast_call(outbound,dialarg,0)) {
+ tmpuser->ochan = outbound;
+ tmpuser->state = 0;
+ tmpuser->cleared = 0;
+ ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
+ AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
+ } else {
+ ast_verb(3, "couldn't reach at this number.\n");
+ if (outbound) {
+ if (!outbound->cdr)
+ outbound->cdr = ast_cdr_alloc();
+ if (outbound->cdr) {
+ char tmp[256];
+
+ ast_cdr_init(outbound->cdr, outbound);
+ snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
+ ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
+ ast_cdr_update(outbound);
+ ast_cdr_start(outbound->cdr);
+ ast_cdr_end(outbound->cdr);
+ /* If the cause wasn't handled properly */
+ if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
+ ast_cdr_failed(outbound->cdr);
+ } else {
+ ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
+ ast_hangup(outbound);
+ outbound = NULL;
+ }
+ }
+
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
+
+ number = rest;
+ } while (number);
+
+ status = 0;
+ if (!AST_LIST_EMPTY(findme_user_list))
+ winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
+
+
+ while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
+ if (!fmuser->cleared && fmuser->ochan != winner)
+ clear_caller(fmuser);
+ ast_free(fmuser);
+ }
+
+ fmuser = NULL;
+ tmpuser = NULL;
+ headuser = NULL;
+ if (winner)
+ break;
+
+ if (!caller) {
+ tpargs->status = 1;
+ ast_free(findme_user_list);
+ return;
+ }
+
+ idx++;
+ AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
+ if (nm->order == idx)
+ break;
+
+ }
+ ast_free(findme_user_list);
+ if (!winner)
+ tpargs->status = 1;
+ else {
+ tpargs->status = 100;
+ tpargs->outbound = winner;
+ }
+
+
+ return;
+
+}
+
+static int app_exec(struct ast_channel *chan, void *data)
+{
+ struct fm_args targs;
+ struct ast_bridge_config config;
+ struct call_followme *f;
+ struct number *nm, *newnm;
+ int res = 0;
+ char *argstr;
+ char namerecloc[255];
+ int duration = 0;
+ struct ast_channel *caller;
+ struct ast_channel *outbound;
+ static char toast[80];
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(followmeid);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
+ return -1;
+ }
+
+ if (!(argstr = ast_strdupa((char *)data))) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, argstr);
+
+ if (ast_strlen_zero(args.followmeid)) {
+ ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
+ return -1;
+ }
+
+ AST_RWLIST_RDLOCK(&followmes);
+ AST_RWLIST_TRAVERSE(&followmes, f, entry) {
+ if (!strcasecmp(f->name, args.followmeid) && (f->active))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&followmes);
+
+ ast_debug(1, "New profile %s.\n", args.followmeid);
+
+ if (!f) {
+ ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
+ return 0;
+ }
+
+ /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
+ if (args.options)
+ ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
+
+ /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
+ ast_mutex_lock(&f->lock);
+ targs.mohclass = ast_strdupa(f->moh);
+ ast_copy_string(targs.context, f->context, sizeof(targs.context));
+ ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
+ ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
+ ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
+ ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
+ ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
+ ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
+ ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
+ ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
+ /* Copy the numbers we're going to use into another list in case the master list should get modified
+ (and locked) while we're trying to do a follow-me */
+ AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
+ AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
+ newnm = create_followme_number(nm->number, nm->timeout, nm->order);
+ AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
+ }
+ ast_mutex_unlock(&f->lock);
+
+ if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
+ ast_stream_and_wait(chan, targs.statusprompt, "");
+
+ snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
+ duration = 5;
+
+ if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
+ if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, 128, 0, NULL) < 0)
+ goto outrun;
+
+ if (!ast_fileexists(namerecloc, NULL, chan->language))
+ ast_copy_string(namerecloc, "", sizeof(namerecloc));
+
+ if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
+ goto outrun;
+ if (ast_waitstream(chan, "") < 0)
+ goto outrun;
+ ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
+
+ targs.status = 0;
+ targs.chan = chan;
+ ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
+
+ findmeexec(&targs);
+
+ while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
+ ast_free(nm);
+
+ if (!ast_strlen_zero(namerecloc))
+ unlink(namerecloc);
+
+ if (targs.status != 100) {
+ ast_moh_stop(chan);
+ if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
+ ast_stream_and_wait(chan, targs.sorryprompt, "");
+ res = 0;
+ } else {
+ caller = chan;
+ outbound = targs.outbound;
+ /* Bridge the two channels. */
+
+ memset(&config,0,sizeof(struct ast_bridge_config));
+ ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
+ ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
+ ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
+
+ ast_moh_stop(caller);
+ /* Be sure no generators are left on it */
+ ast_deactivate_generator(caller);
+ /* Make sure channels are compatible */
+ res = ast_channel_make_compatible(caller, outbound);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
+ ast_hangup(outbound);
+ goto outrun;
+ }
+ time(&answer_time);
+ res = ast_bridge_call(caller,outbound,&config);
+ time(&end_time);
+ snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
+ pbx_builtin_setvar_helper(caller, "DIALEDTIME", toast);
+ snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
+ pbx_builtin_setvar_helper(caller, "ANSWEREDTIME", toast);
+ if (outbound)
+ ast_hangup(outbound);
+ }
+
+ outrun:
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ struct call_followme *f;
+
+ ast_unregister_application(app);
+
+ /* Free Memory. Yeah! I'm free! */
+ AST_RWLIST_WRLOCK(&followmes);
+ while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
+ free_numbers(f);
+ ast_free(f);
+ }
+
+ AST_RWLIST_UNLOCK(&followmes);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ if(!reload_followme(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ return ast_register_application(app, app_exec, synopsis, descrip);
+}
+
+static int reload(void)
+{
+ reload_followme(1);
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/apps/app_forkcdr.c b/trunk/apps/app_forkcdr.c
new file mode 100644
index 000000000..c74de67b7
--- /dev/null
+++ b/trunk/apps/app_forkcdr.c
@@ -0,0 +1,99 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
+ * Development of this app Sponsered/Funded by TAAN Softworks Corp
+ *
+ * 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 Fork CDR application
+ *
+ * \author Anthony Minessale anthmct@yahoo.com
+ *
+ * \note Development of this app Sponsored/Funded by TAAN Softworks Corp
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+
+static char *app = "ForkCDR";
+static char *synopsis =
+"Forks the Call Data Record";
+static char *descrip =
+" ForkCDR([options]): Causes the Call Data Record to fork an additional\n"
+"cdr record starting from the time of the fork call\n"
+" Options:\n"
+" v - If the option is passed all cdr variables will be passed along also.\n";
+
+
+static void ast_cdr_fork(struct ast_channel *chan)
+{
+ struct ast_cdr *cdr;
+ struct ast_cdr *newcdr;
+ struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
+
+ cdr = chan->cdr;
+
+ while (cdr->next)
+ cdr = cdr->next;
+
+ if (!(newcdr = ast_cdr_dup(cdr)))
+ return;
+
+ ast_cdr_append(cdr, newcdr);
+ ast_cdr_reset(newcdr, &flags);
+
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
+ ast_cdr_free_vars(cdr, 0);
+
+ ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
+}
+
+static int forkcdr_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (!chan->cdr) {
+ ast_log(LOG_WARNING, "Channel does not have a CDR\n");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(data))
+ ast_set2_flag(chan->cdr, strchr(data, 'v'), AST_CDR_FLAG_KEEP_VARS);
+
+ ast_cdr_fork(chan);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, forkcdr_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities");
diff --git a/trunk/apps/app_getcpeid.c b/trunk/apps/app_getcpeid.c
new file mode 100644
index 000000000..1bab819b7
--- /dev/null
+++ b/trunk/apps/app_getcpeid.c
@@ -0,0 +1,130 @@
+/*
+ * 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 Get ADSI CPE ID
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/adsi.h"
+
+static char *app = "GetCPEID";
+
+static char *synopsis = "Get ADSI CPE ID";
+
+static char *descrip =
+" GetCPEID(): Obtains and displays ADSI CPE ID and other information in order\n"
+"to properly setup zapata.conf for on-hook operations.\n";
+
+
+static int cpeid_setstatus(struct ast_channel *chan, char *stuff[], int voice)
+{
+ int justify[5] = { ADSI_JUST_CENT, ADSI_JUST_LEFT, ADSI_JUST_LEFT, ADSI_JUST_LEFT };
+ char *tmp[5];
+ int x;
+ for (x=0;x<4;x++)
+ tmp[x] = stuff[x];
+ tmp[4] = NULL;
+ return ast_adsi_print(chan, tmp, justify, voice);
+}
+
+static int cpeid_exec(struct ast_channel *chan, void *idata)
+{
+ int res=0;
+ unsigned char cpeid[4];
+ int gotgeometry = 0;
+ int gotcpeid = 0;
+ int width, height, buttons;
+ char *data[4];
+ unsigned int x;
+
+ for (x = 0; x < 4; x++)
+ data[x] = alloca(80);
+
+ strcpy(data[0], "** CPE Info **");
+ strcpy(data[1], "Identifying CPE...");
+ strcpy(data[2], "Please wait...");
+ res = ast_adsi_load_session(chan, NULL, 0, 1);
+ if (res > 0) {
+ cpeid_setstatus(chan, data, 0);
+ res = ast_adsi_get_cpeid(chan, cpeid, 0);
+ if (res > 0) {
+ gotcpeid = 1;
+ ast_verb(3, "Got CPEID of '%02x:%02x:%02x:%02x' on '%s'\n", cpeid[0], cpeid[1], cpeid[2], cpeid[3], chan->name);
+ }
+ if (res > -1) {
+ strcpy(data[1], "Measuring CPE...");
+ strcpy(data[2], "Please wait...");
+ cpeid_setstatus(chan, data, 0);
+ res = ast_adsi_get_cpeinfo(chan, &width, &height, &buttons, 0);
+ if (res > -1) {
+ ast_verb(3, "CPE has %d lines, %d columns, and %d buttons on '%s'\n", height, width, buttons, chan->name);
+ gotgeometry = 1;
+ }
+ }
+ if (res > -1) {
+ if (gotcpeid)
+ snprintf(data[1], 80, "CPEID: %02x:%02x:%02x:%02x", cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
+ else
+ strcpy(data[1], "CPEID Unknown");
+ if (gotgeometry)
+ snprintf(data[2], 80, "Geom: %dx%d, %d buttons", width, height, buttons);
+ else
+ strcpy(data[2], "Geometry unknown");
+ strcpy(data[3], "Press # to exit");
+ cpeid_setstatus(chan, data, 1);
+ for(;;) {
+ res = ast_waitfordigit(chan, 1000);
+ if (res < 0)
+ break;
+ if (res == '#') {
+ res = 0;
+ break;
+ }
+ }
+ ast_adsi_unload_session(chan);
+ }
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, cpeid_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get ADSI CPE ID");
diff --git a/trunk/apps/app_ices.c b/trunk/apps/app_ices.c
new file mode 100644
index 000000000..b2a4e9b91
--- /dev/null
+++ b/trunk/apps/app_ices.c
@@ -0,0 +1,205 @@
+/*
+ * 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 Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \extref ICES - http://www.icecast.org/ices.php
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/time.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/frame.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+#define ICES "/usr/bin/ices"
+#define LOCAL_ICES "/usr/local/bin/ices"
+
+static char *app = "ICES";
+
+static char *synopsis = "Encode and stream using 'ices'";
+
+static char *descrip =
+" ICES(config.xml) Streams to an icecast server using ices\n"
+"(available separately). A configuration file must be supplied\n"
+"for ices (see examples/asterisk-ices.conf). \n";
+
+
+static int icesencode(char *filename, int fd)
+{
+ int res;
+ int x;
+ sigset_t fullset, oldset;
+
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
+
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res) {
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ return res;
+ }
+
+ /* Stop ignoring PIPE */
+ signal(SIGPIPE, SIG_DFL);
+ pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
+
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+ dup2(fd, STDIN_FILENO);
+ for (x=STDERR_FILENO + 1;x<1024;x++) {
+ if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
+ close(x);
+ }
+ /* Most commonly installed in /usr/local/bin */
+ execl(ICES, "ices", filename, (char *)NULL);
+ /* But many places has it in /usr/bin */
+ execl(LOCAL_ICES, "ices", filename, (char *)NULL);
+ /* As a last-ditch effort, try to use PATH */
+ execlp("ices", "ices", filename, (char *)NULL);
+ ast_log(LOG_WARNING, "Execute of ices failed\n");
+ _exit(0);
+}
+
+static int ices_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int fds[2];
+ int ms = -1;
+ int pid = -1;
+ int flags;
+ int oreadformat;
+ struct timeval last;
+ struct ast_frame *f;
+ char filename[256]="";
+ char *c;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
+ return -1;
+ }
+
+ last = ast_tv(0, 0);
+
+ if (pipe(fds)) {
+ ast_log(LOG_WARNING, "Unable to create pipe\n");
+ return -1;
+ }
+ flags = fcntl(fds[1], F_GETFL);
+ fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
+
+ ast_stopstream(chan);
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ if (res) {
+ close(fds[0]);
+ close(fds[1]);
+ ast_log(LOG_WARNING, "Answer failed!\n");
+ return -1;
+ }
+
+ oreadformat = chan->readformat;
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ close(fds[0]);
+ close(fds[1]);
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ return -1;
+ }
+ if (((char *)data)[0] == '/')
+ ast_copy_string(filename, (char *) data, sizeof(filename));
+ else
+ snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, (char *)data);
+ /* Placeholder for options */
+ c = strchr(filename, '|');
+ if (c)
+ *c = '\0';
+ res = icesencode(filename, fds[0]);
+ close(fds[0]);
+ if (res >= 0) {
+ pid = res;
+ for (;;) {
+ /* Wait for audio, and stream */
+ ms = ast_waitfor(chan, -1);
+ if (ms < 0) {
+ ast_debug(1, "Hangup detected\n");
+ res = -1;
+ break;
+ }
+ f = ast_read(chan);
+ if (!f) {
+ ast_debug(1, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ res = write(fds[1], f->data, f->datalen);
+ if (res < 0) {
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
+ res = -1;
+ ast_frfree(f);
+ break;
+ }
+ }
+ }
+ ast_frfree(f);
+ }
+ }
+ close(fds[1]);
+
+ if (pid > -1)
+ kill(pid, SIGKILL);
+ if (!res && oreadformat)
+ ast_set_read_format(chan, oreadformat);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, ices_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices");
diff --git a/trunk/apps/app_image.c b/trunk/apps/app_image.c
new file mode 100644
index 000000000..1e5b0c857
--- /dev/null
+++ b/trunk/apps/app_image.c
@@ -0,0 +1,80 @@
+/*
+ * 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 App to transmit an image
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/image.h"
+
+static char *app = "SendImage";
+
+static char *synopsis = "Send an image file";
+
+static char *descrip =
+" SendImage(filename): Sends an image on a channel.\n"
+"If the channel supports image transport but the image send fails, the channel\n"
+"will be hung up. Otherwise, the dialplan continues execution. This\n"
+"application sets the following channel variable upon completion:\n"
+" SENDIMAGESTATUS The status is the result of the attempt, one of:\n"
+" OK | NOSUPPORT \n";
+
+
+static int sendimage_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SendImage requires an argument (filename)\n");
+ return -1;
+ }
+
+ if (!ast_supports_images(chan)) {
+ /* Does not support transport */
+ pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "NOSUPPORT");
+ return 0;
+ }
+
+ if (!(res = ast_send_image(chan, data)))
+ pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "OK");
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, sendimage_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Image Transmission Application");
diff --git a/trunk/apps/app_ivrdemo.c b/trunk/apps/app_ivrdemo.c
new file mode 100644
index 000000000..6dbbe7191
--- /dev/null
+++ b/trunk/apps/app_ivrdemo.c
@@ -0,0 +1,115 @@
+/*
+ * 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 IVR Demo application
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "IVR Demo Application";
+static char *app = "IVRDemo";
+static char *synopsis =
+" This is a skeleton application that shows you the basic structure to create your\n"
+"own asterisk applications and demonstrates the IVR demo.\n";
+
+static int ivr_demo_func(struct ast_channel *chan, void *data)
+{
+ ast_verbose("IVR Demo, data is %s!\n", (char *)data);
+ return 0;
+}
+
+AST_IVR_DECLARE_MENU(ivr_submenu, "IVR Demo Sub Menu", 0,
+{
+ { "s", AST_ACTION_BACKGROUND, "demo-abouttotry" },
+ { "s", AST_ACTION_WAITOPTION },
+ { "1", AST_ACTION_PLAYBACK, "digits/1" },
+ { "1", AST_ACTION_PLAYBACK, "digits/1" },
+ { "1", AST_ACTION_RESTART },
+ { "2", AST_ACTION_PLAYLIST, "digits/2;digits/3" },
+ { "3", AST_ACTION_CALLBACK, ivr_demo_func },
+ { "4", AST_ACTION_TRANSFER, "demo|s|1" },
+ { "*", AST_ACTION_REPEAT },
+ { "#", AST_ACTION_UPONE },
+ { NULL }
+});
+
+AST_IVR_DECLARE_MENU(ivr_demo, "IVR Demo Main Menu", 0,
+{
+ { "s", AST_ACTION_BACKGROUND, "demo-congrats" },
+ { "g", AST_ACTION_BACKGROUND, "demo-instruct" },
+ { "g", AST_ACTION_WAITOPTION },
+ { "1", AST_ACTION_PLAYBACK, "digits/1" },
+ { "1", AST_ACTION_RESTART },
+ { "2", AST_ACTION_MENU, &ivr_submenu },
+ { "2", AST_ACTION_RESTART },
+ { "i", AST_ACTION_PLAYBACK, "invalid" },
+ { "i", AST_ACTION_REPEAT, (void *)(unsigned long)2 },
+ { "#", AST_ACTION_EXIT },
+ { NULL },
+});
+
+
+static int skel_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
+ return -1;
+ }
+
+ /* Do our thing here */
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ if (!res)
+ res = ast_ivr_menu_run(chan, &ivr_demo, data);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, skel_exec, tdesc, synopsis);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "IVR Demo Application");
diff --git a/trunk/apps/app_jack.c b/trunk/apps/app_jack.c
new file mode 100644
index 000000000..8faecd1f2
--- /dev/null
+++ b/trunk/apps/app_jack.c
@@ -0,0 +1,971 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 - 2008, Russell Bryant
+ *
+ * Russell Bryant <russell@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 Jack Application
+ *
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * This is an application to connect an Asterisk channel to an input
+ * and output jack port so that the audio can be processed through
+ * another application, or to play audio from another application.
+ *
+ * \arg http://www.jackaudio.org/
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>jack</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <limits.h>
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/strings.h"
+#include "asterisk/lock.h"
+#include "asterisk/libresample.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/audiohook.h"
+
+#define RESAMPLE_QUALITY 0
+
+#define RINGBUFFER_SIZE 16384
+
+/*! \brief Common options between the Jack() app and JACK_HOOK() function */
+#define COMMON_OPTIONS \
+" s(<name>) - Connect to the specified jack server name.\n" \
+" i(<name>) - Connect the output port that gets created to the specified\n" \
+" jack input port.\n" \
+" o(<name>) - Connect the input port that gets created to the specified\n" \
+" jack output port.\n" \
+" n - Do not automatically start the JACK server if it is not already\n" \
+" running.\n"
+
+static char *jack_app = "JACK";
+static char *jack_synopsis =
+"JACK (Jack Audio Connection Kit) Application";
+static char *jack_desc =
+"JACK([options])\n"
+" When this application is executed, two jack ports will be created; one input\n"
+"and one output. Other applications can be hooked up to these ports to access\n"
+"the audio coming from, or being sent to the channel.\n"
+" Valid options:\n"
+COMMON_OPTIONS
+"";
+
+struct jack_data {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(server_name);
+ AST_STRING_FIELD(connect_input_port);
+ AST_STRING_FIELD(connect_output_port);
+ );
+ jack_client_t *client;
+ jack_port_t *input_port;
+ jack_port_t *output_port;
+ jack_ringbuffer_t *input_rb;
+ jack_ringbuffer_t *output_rb;
+ void *output_resampler;
+ double output_resample_factor;
+ void *input_resampler;
+ double input_resample_factor;
+ unsigned int stop:1;
+ unsigned int has_audiohook:1;
+ unsigned int no_start_server:1;
+ /*! Only used with JACK_HOOK */
+ struct ast_audiohook audiohook;
+};
+
+static const struct {
+ jack_status_t status;
+ const char *str;
+} jack_status_table[] = {
+ { JackFailure, "Failure" },
+ { JackInvalidOption, "Invalid Option" },
+ { JackNameNotUnique, "Name Not Unique" },
+ { JackServerStarted, "Server Started" },
+ { JackServerFailed, "Server Failed" },
+ { JackServerError, "Server Error" },
+ { JackNoSuchClient, "No Such Client" },
+ { JackLoadFailure, "Load Failure" },
+ { JackInitFailure, "Init Failure" },
+ { JackShmFailure, "Shared Memory Access Failure" },
+ { JackVersionError, "Version Mismatch" },
+};
+
+static const char *jack_status_to_str(jack_status_t status)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_LEN(jack_status_table); i++) {
+ if (jack_status_table[i].status == status)
+ return jack_status_table[i].str;
+ }
+
+ return "Unknown Error";
+}
+
+static void log_jack_status(const char *prefix, jack_status_t status)
+{
+ struct ast_str *str = ast_str_alloca(512);
+ int i, first = 0;
+
+ for (i = 0; i < (sizeof(status) * 8); i++) {
+ if (!(status & (1 << i)))
+ continue;
+
+ if (!first) {
+ ast_str_set(&str, 0, "%s", jack_status_to_str((1 << i)));
+ first = 1;
+ } else
+ ast_str_append(&str, 0, ", %s", jack_status_to_str((1 << i)));
+ }
+
+ ast_log(LOG_NOTICE, "%s: %s\n", prefix, str->str);
+}
+
+static int alloc_resampler(struct jack_data *jack_data, int input)
+{
+ double from_srate, to_srate, jack_srate;
+ void **resampler;
+ double *resample_factor;
+
+ if (input && jack_data->input_resampler)
+ return 0;
+
+ if (!input && jack_data->output_resampler)
+ return 0;
+
+ jack_srate = jack_get_sample_rate(jack_data->client);
+
+ /* XXX Hard coded 8 kHz */
+
+ to_srate = input ? 8000.0 : jack_srate;
+ from_srate = input ? jack_srate : 8000.0;
+
+ resample_factor = input ? &jack_data->input_resample_factor :
+ &jack_data->output_resample_factor;
+
+ if (from_srate == to_srate) {
+ /* Awesome! The jack sample rate is the same as ours.
+ * Resampling isn't needed. */
+ *resample_factor = 1.0;
+ return 0;
+ }
+
+ *resample_factor = to_srate / from_srate;
+
+ resampler = input ? &jack_data->input_resampler :
+ &jack_data->output_resampler;
+
+ if (!(*resampler = resample_open(RESAMPLE_QUALITY,
+ *resample_factor, *resample_factor))) {
+ ast_log(LOG_ERROR, "Failed to open %s resampler\n",
+ input ? "input" : "output");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Handle jack input port
+ *
+ * Read nframes number of samples from the input buffer, resample it
+ * if necessary, and write it into the appropriate ringbuffer.
+ */
+static void handle_input(void *buf, jack_nframes_t nframes,
+ struct jack_data *jack_data)
+{
+ short s_buf[nframes];
+ float *in_buf = buf;
+ size_t res;
+ int i;
+ size_t write_len = sizeof(s_buf);
+
+ if (jack_data->input_resampler) {
+ int total_in_buf_used = 0;
+ int total_out_buf_used = 0;
+ float f_buf[nframes + 1];
+
+ memset(f_buf, 0, sizeof(f_buf));
+
+ while (total_in_buf_used < nframes) {
+ int in_buf_used;
+ int out_buf_used;
+
+ out_buf_used = resample_process(jack_data->input_resampler,
+ jack_data->input_resample_factor,
+ &in_buf[total_in_buf_used], nframes - total_in_buf_used,
+ 0, &in_buf_used,
+ &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
+
+ if (out_buf_used < 0)
+ break;
+
+ total_out_buf_used += out_buf_used;
+ total_in_buf_used += in_buf_used;
+
+ if (total_out_buf_used == ARRAY_LEN(f_buf)) {
+ ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size, "
+ "nframes '%d', total_out_buf_used '%d'\n", nframes, total_out_buf_used);
+ break;
+ }
+ }
+
+ for (i = 0; i < total_out_buf_used; i++)
+ s_buf[i] = f_buf[i] * (SHRT_MAX / 1.0);
+
+ write_len = total_out_buf_used * sizeof(int16_t);
+ } else {
+ /* No resampling needed */
+
+ for (i = 0; i < nframes; i++)
+ s_buf[i] = in_buf[i] * (SHRT_MAX / 1.0);
+ }
+
+ res = jack_ringbuffer_write(jack_data->input_rb, (const char *) s_buf, write_len);
+ if (res != write_len) {
+ ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
+ (int) sizeof(s_buf), (int) res);
+ }
+}
+
+/*!
+ * \brief Handle jack output port
+ *
+ * Read nframes number of samples from the ringbuffer and write it out to the
+ * output port buffer.
+ */
+static void handle_output(void *buf, jack_nframes_t nframes,
+ struct jack_data *jack_data)
+{
+ size_t res, len;
+
+ len = nframes * sizeof(float);
+
+ res = jack_ringbuffer_read(jack_data->output_rb, buf, len);
+
+ if (len != res) {
+ ast_debug(2, "Wanted %d bytes to send to the output port, "
+ "but only got %d\n", (int) len, (int) res);
+ }
+}
+
+static int jack_process(jack_nframes_t nframes, void *arg)
+{
+ struct jack_data *jack_data = arg;
+ void *input_port_buf, *output_port_buf;
+
+ if (!jack_data->input_resample_factor)
+ alloc_resampler(jack_data, 1);
+
+ input_port_buf = jack_port_get_buffer(jack_data->input_port, nframes);
+ handle_input(input_port_buf, nframes, jack_data);
+
+ output_port_buf = jack_port_get_buffer(jack_data->output_port, nframes);
+ handle_output(output_port_buf, nframes, jack_data);
+
+ return 0;
+}
+
+static void jack_shutdown(void *arg)
+{
+ struct jack_data *jack_data = arg;
+
+ jack_data->stop = 1;
+}
+
+static struct jack_data *destroy_jack_data(struct jack_data *jack_data)
+{
+ if (jack_data->input_port) {
+ jack_port_unregister(jack_data->client, jack_data->input_port);
+ jack_data->input_port = NULL;
+ }
+
+ if (jack_data->output_port) {
+ jack_port_unregister(jack_data->client, jack_data->output_port);
+ jack_data->output_port = NULL;
+ }
+
+ if (jack_data->client) {
+ jack_client_close(jack_data->client);
+ jack_data->client = NULL;
+ }
+
+ if (jack_data->input_rb) {
+ jack_ringbuffer_free(jack_data->input_rb);
+ jack_data->input_rb = NULL;
+ }
+
+ if (jack_data->output_rb) {
+ jack_ringbuffer_free(jack_data->output_rb);
+ jack_data->output_rb = NULL;
+ }
+
+ if (jack_data->output_resampler) {
+ resample_close(jack_data->output_resampler);
+ jack_data->output_resampler = NULL;
+ }
+
+ if (jack_data->input_resampler) {
+ resample_close(jack_data->input_resampler);
+ jack_data->input_resampler = NULL;
+ }
+
+ if (jack_data->has_audiohook)
+ ast_audiohook_destroy(&jack_data->audiohook);
+
+ ast_string_field_free_memory(jack_data);
+
+ ast_free(jack_data);
+
+ return NULL;
+}
+
+static int init_jack_data(struct ast_channel *chan, struct jack_data *jack_data)
+{
+ const char *chan_name;
+ jack_status_t status = 0;
+ jack_options_t jack_options = JackNullOption;
+
+ ast_channel_lock(chan);
+ chan_name = ast_strdupa(chan->name);
+ ast_channel_unlock(chan);
+
+ if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
+ return -1;
+
+ if (!(jack_data->input_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
+ return -1;
+
+ if (jack_data->no_start_server)
+ jack_options |= JackNoStartServer;
+
+ if (!ast_strlen_zero(jack_data->server_name)) {
+ jack_options |= JackServerName;
+ jack_data->client = jack_client_open(chan_name, jack_options, &status,
+ jack_data->server_name);
+ } else {
+ jack_data->client = jack_client_open(chan_name, jack_options, &status);
+ }
+
+ if (status)
+ log_jack_status("Client Open Status", status);
+
+ if (!jack_data->client)
+ return -1;
+
+ jack_data->input_port = jack_port_register(jack_data->client, "input",
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
+ if (!jack_data->input_port) {
+ ast_log(LOG_ERROR, "Failed to create input port for jack port\n");
+ return -1;
+ }
+
+ jack_data->output_port = jack_port_register(jack_data->client, "output",
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
+ if (!jack_data->output_port) {
+ ast_log(LOG_ERROR, "Failed to create output port for jack port\n");
+ return -1;
+ }
+
+ if (jack_set_process_callback(jack_data->client, jack_process, jack_data)) {
+ ast_log(LOG_ERROR, "Failed to register process callback with jack client\n");
+ return -1;
+ }
+
+ jack_on_shutdown(jack_data->client, jack_shutdown, jack_data);
+
+ if (jack_activate(jack_data->client)) {
+ ast_log(LOG_ERROR, "Unable to activate jack client\n");
+ return -1;
+ }
+
+ while (!ast_strlen_zero(jack_data->connect_input_port)) {
+ const char **ports;
+ int i;
+
+ ports = jack_get_ports(jack_data->client, jack_data->connect_input_port,
+ NULL, JackPortIsInput);
+
+ if (!ports) {
+ ast_log(LOG_ERROR, "No input port matching '%s' was found\n",
+ jack_data->connect_input_port);
+ break;
+ }
+
+ for (i = 0; ports[i]; i++) {
+ ast_debug(1, "Found port '%s' that matched specified input port '%s'\n",
+ ports[i], jack_data->connect_input_port);
+ }
+
+ if (jack_connect(jack_data->client, jack_port_name(jack_data->output_port), ports[0])) {
+ ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
+ jack_port_name(jack_data->output_port));
+ } else {
+ ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
+ jack_port_name(jack_data->output_port));
+ }
+
+ free((void *) ports);
+
+ break;
+ }
+
+ while (!ast_strlen_zero(jack_data->connect_output_port)) {
+ const char **ports;
+ int i;
+
+ ports = jack_get_ports(jack_data->client, jack_data->connect_output_port,
+ NULL, JackPortIsOutput);
+
+ if (!ports) {
+ ast_log(LOG_ERROR, "No output port matching '%s' was found\n",
+ jack_data->connect_output_port);
+ break;
+ }
+
+ for (i = 0; ports[i]; i++) {
+ ast_debug(1, "Found port '%s' that matched specified output port '%s'\n",
+ ports[i], jack_data->connect_output_port);
+ }
+
+ if (jack_connect(jack_data->client, ports[0], jack_port_name(jack_data->input_port))) {
+ ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
+ jack_port_name(jack_data->input_port));
+ } else {
+ ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
+ jack_port_name(jack_data->input_port));
+ }
+
+ free((void *) ports);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int queue_voice_frame(struct jack_data *jack_data, struct ast_frame *f)
+{
+ float f_buf[f->samples * 8];
+ size_t f_buf_used = 0;
+ int i;
+ int16_t *s_buf = f->data;
+ size_t res;
+
+ memset(f_buf, 0, sizeof(f_buf));
+
+ if (!jack_data->output_resample_factor)
+ alloc_resampler(jack_data, 0);
+
+ if (jack_data->output_resampler) {
+ float in_buf[f->samples];
+ int total_in_buf_used = 0;
+ int total_out_buf_used = 0;
+
+ memset(in_buf, 0, sizeof(in_buf));
+
+ for (i = 0; i < f->samples; i++)
+ in_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
+
+ while (total_in_buf_used < ARRAY_LEN(in_buf)) {
+ int in_buf_used;
+ int out_buf_used;
+
+ out_buf_used = resample_process(jack_data->output_resampler,
+ jack_data->output_resample_factor,
+ &in_buf[total_in_buf_used], ARRAY_LEN(in_buf) - total_in_buf_used,
+ 0, &in_buf_used,
+ &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
+
+ if (out_buf_used < 0)
+ break;
+
+ total_out_buf_used += out_buf_used;
+ total_in_buf_used += in_buf_used;
+
+ if (total_out_buf_used == ARRAY_LEN(f_buf)) {
+ ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size\n");
+ break;
+ }
+ }
+
+ f_buf_used = total_out_buf_used;
+ if (f_buf_used > ARRAY_LEN(f_buf))
+ f_buf_used = ARRAY_LEN(f_buf);
+ } else {
+ /* No resampling needed */
+
+ for (i = 0; i < f->samples; i++)
+ f_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
+
+ f_buf_used = f->samples;
+ }
+
+ res = jack_ringbuffer_write(jack_data->output_rb, (const char *) f_buf, f_buf_used * sizeof(float));
+ if (res != (f_buf_used * sizeof(float))) {
+ ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
+ (int) (f_buf_used * sizeof(float)), (int) res);
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief handle jack audio
+ *
+ * \param[in] chan The Asterisk channel to write the frames to if no output frame
+ * is provided.
+ * \param[in] jack_data This is the jack_data struct that contains the input
+ * ringbuffer that audio will be read from.
+ * \param[out] out_frame If this argument is non-NULL, then assuming there is
+ * enough data avilable in the ringbuffer, the audio in this frame
+ * will get replaced with audio from the input buffer. If there is
+ * not enough data available to read at this time, then the frame
+ * data gets zeroed out.
+ *
+ * Read data from the input ringbuffer, which is the properly resampled audio
+ * that was read from the jack input port. Write it to the channel in 20 ms frames,
+ * or fill up an output frame instead if one is provided.
+ *
+ * \return Nothing.
+ */
+static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data,
+ struct ast_frame *out_frame)
+{
+ short buf[160];
+ struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .src = "JACK",
+ .data = buf,
+ .datalen = sizeof(buf),
+ .samples = ARRAY_LEN(buf),
+ };
+
+ for (;;) {
+ size_t res, read_len;
+ char *read_buf;
+
+ read_len = out_frame ? out_frame->datalen : sizeof(buf);
+ read_buf = out_frame ? out_frame->data : buf;
+
+ res = jack_ringbuffer_read_space(jack_data->input_rb);
+
+ if (res < read_len) {
+ /* Not enough data ready for another frame, move on ... */
+ if (out_frame) {
+ ast_debug(1, "Sending an empty frame for the JACK_HOOK\n");
+ memset(out_frame->data, 0, out_frame->datalen);
+ }
+ break;
+ }
+
+ res = jack_ringbuffer_read(jack_data->input_rb, (char *) read_buf, read_len);
+
+ if (res < read_len) {
+ ast_log(LOG_ERROR, "Error reading from ringbuffer, even though it said there was enough data\n");
+ break;
+ }
+
+ if (out_frame) {
+ /* If an output frame was provided, then we just want to fill up the
+ * buffer in that frame and return. */
+ break;
+ }
+
+ ast_write(chan, &f);
+ }
+}
+
+enum {
+ OPT_SERVER_NAME = (1 << 0),
+ OPT_INPUT_PORT = (1 << 1),
+ OPT_OUTPUT_PORT = (1 << 2),
+ OPT_NOSTART_SERVER = (1 << 3),
+};
+
+enum {
+ OPT_ARG_SERVER_NAME,
+ OPT_ARG_INPUT_PORT,
+ OPT_ARG_OUTPUT_PORT,
+ /* Must be the last element */
+ OPT_ARG_ARRAY_SIZE,
+};
+
+AST_APP_OPTIONS(jack_exec_options, BEGIN_OPTIONS
+ AST_APP_OPTION_ARG('s', OPT_SERVER_NAME, OPT_ARG_SERVER_NAME),
+ AST_APP_OPTION_ARG('i', OPT_INPUT_PORT, OPT_ARG_INPUT_PORT),
+ AST_APP_OPTION_ARG('o', OPT_OUTPUT_PORT, OPT_ARG_OUTPUT_PORT),
+ AST_APP_OPTION('n', OPT_NOSTART_SERVER),
+END_OPTIONS );
+
+static struct jack_data *jack_data_alloc(void)
+{
+ struct jack_data *jack_data;
+
+ if (!(jack_data = ast_calloc(1, sizeof(*jack_data))))
+ return NULL;
+
+ if (ast_string_field_init(jack_data, 32)) {
+ ast_free(jack_data);
+ return NULL;
+ }
+
+ return jack_data;
+}
+
+/*!
+ * \note This must be done before calling init_jack_data().
+ */
+static int handle_options(struct jack_data *jack_data, const char *__options_str)
+{
+ struct ast_flags options = { 0, };
+ char *option_args[OPT_ARG_ARRAY_SIZE];
+ char *options_str;
+
+ options_str = ast_strdupa(__options_str);
+
+ ast_app_parse_options(jack_exec_options, &options, option_args, options_str);
+
+ if (ast_test_flag(&options, OPT_SERVER_NAME)) {
+ if (!ast_strlen_zero(option_args[OPT_ARG_SERVER_NAME]))
+ ast_string_field_set(jack_data, server_name, option_args[OPT_ARG_SERVER_NAME]);
+ else {
+ ast_log(LOG_ERROR, "A server name must be provided with the s() option\n");
+ return -1;
+ }
+ }
+
+ if (ast_test_flag(&options, OPT_INPUT_PORT)) {
+ if (!ast_strlen_zero(option_args[OPT_ARG_INPUT_PORT]))
+ ast_string_field_set(jack_data, connect_input_port, option_args[OPT_ARG_INPUT_PORT]);
+ else {
+ ast_log(LOG_ERROR, "A name must be provided with the i() option\n");
+ return -1;
+ }
+ }
+
+ if (ast_test_flag(&options, OPT_OUTPUT_PORT)) {
+ if (!ast_strlen_zero(option_args[OPT_ARG_OUTPUT_PORT]))
+ ast_string_field_set(jack_data, connect_output_port, option_args[OPT_ARG_OUTPUT_PORT]);
+ else {
+ ast_log(LOG_ERROR, "A name must be provided with the o() option\n");
+ return -1;
+ }
+ }
+
+ jack_data->no_start_server = ast_test_flag(&options, OPT_NOSTART_SERVER) ? 1 : 0;
+
+ return 0;
+}
+
+static int jack_exec(struct ast_channel *chan, void *data)
+{
+ struct jack_data *jack_data;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(options);
+ );
+
+ if (!(jack_data = jack_data_alloc()))
+ return -1;
+
+ args.options = data;
+
+ if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options)) {
+ destroy_jack_data(jack_data);
+ return -1;
+ }
+
+ if (init_jack_data(chan, jack_data)) {
+ destroy_jack_data(jack_data);
+ return -1;
+ }
+
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+ destroy_jack_data(jack_data);
+ return -1;
+ }
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ destroy_jack_data(jack_data);
+ return -1;
+ }
+
+ while (!jack_data->stop) {
+ struct ast_frame *f;
+
+ ast_waitfor(chan, -1);
+
+ f = ast_read(chan);
+ if (!f) {
+ jack_data->stop = 1;
+ continue;
+ }
+
+ switch (f->frametype) {
+ case AST_FRAME_CONTROL:
+ if (f->subclass == AST_CONTROL_HANGUP)
+ jack_data->stop = 1;
+ break;
+ case AST_FRAME_VOICE:
+ queue_voice_frame(jack_data, f);
+ default:
+ break;
+ }
+
+ ast_frfree(f);
+
+ handle_jack_audio(chan, jack_data, NULL);
+ }
+
+ jack_data = destroy_jack_data(jack_data);
+
+ return 0;
+}
+
+static void jack_hook_ds_destroy(void *data)
+{
+ struct jack_data *jack_data = data;
+
+ destroy_jack_data(jack_data);
+}
+
+static const struct ast_datastore_info jack_hook_ds_info = {
+ .type = "JACK_HOOK",
+ .destroy = jack_hook_ds_destroy,
+};
+
+static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan,
+ struct ast_frame *frame, enum ast_audiohook_direction direction)
+{
+ struct ast_datastore *datastore;
+ struct jack_data *jack_data;
+
+ if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
+ return 0;
+
+ if (direction != AST_AUDIOHOOK_DIRECTION_READ)
+ return 0;
+
+ if (frame->frametype != AST_FRAME_VOICE)
+ return 0;
+
+ if (frame->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Expected frame in SLINEAR for the audiohook, but got format %d\n",
+ frame->subclass);
+ return 0;
+ }
+
+ ast_channel_lock(chan);
+
+ if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
+ ast_log(LOG_ERROR, "JACK_HOOK datastore not found for '%s'\n", chan->name);
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ jack_data = datastore->data;
+
+ queue_voice_frame(jack_data, frame);
+
+ handle_jack_audio(chan, jack_data, frame);
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+static int enable_jack_hook(struct ast_channel *chan, char *data)
+{
+ struct ast_datastore *datastore;
+ struct jack_data *jack_data = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(mode);
+ AST_APP_ARG(options);
+ );
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ ast_channel_lock(chan);
+
+ if ((datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
+ ast_log(LOG_ERROR, "JACK_HOOK already enabled for '%s'\n", chan->name);
+ goto return_error;
+ }
+
+ if (ast_strlen_zero(args.mode) || strcasecmp(args.mode, "manipulate")) {
+ ast_log(LOG_ERROR, "'%s' is not a supported mode. Only manipulate is supported.\n",
+ S_OR(args.mode, "<none>"));
+ goto return_error;
+ }
+
+ if (!(jack_data = jack_data_alloc()))
+ goto return_error;
+
+ if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options))
+ goto return_error;
+
+ if (init_jack_data(chan, jack_data))
+ goto return_error;
+
+ if (!(datastore = ast_channel_datastore_alloc(&jack_hook_ds_info, NULL)))
+ goto return_error;
+
+ jack_data->has_audiohook = 1;
+ ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK");
+ jack_data->audiohook.manipulate_callback = jack_hook_callback;
+
+ datastore->data = jack_data;
+
+ if (ast_audiohook_attach(chan, &jack_data->audiohook))
+ goto return_error;
+
+ if (ast_channel_datastore_add(chan, datastore))
+ goto return_error;
+
+ ast_channel_unlock(chan);
+
+ return 0;
+
+return_error:
+ ast_channel_unlock(chan);
+
+ if (jack_data)
+ destroy_jack_data(jack_data);
+
+ return -1;
+}
+
+static int disable_jack_hook(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+ struct jack_data *jack_data;
+
+ ast_channel_lock(chan);
+
+ if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
+ ast_channel_unlock(chan);
+ ast_log(LOG_WARNING, "No JACK_HOOK found to disable\n");
+ return -1;
+ }
+
+ ast_channel_datastore_remove(chan, datastore);
+
+ jack_data = datastore->data;
+ ast_audiohook_detach(&jack_data->audiohook);
+
+ /* Keep the channel locked while we destroy the datastore, so that we can
+ * ensure that all of the jack stuff is stopped just in case another frame
+ * tries to come through the audiohook callback. */
+ ast_channel_datastore_free(datastore);
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+static int jack_hook_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ int res;
+
+ if (!strcasecmp(value, "on"))
+ res = enable_jack_hook(chan, data);
+ else if (!strcasecmp(value, "off"))
+ res = disable_jack_hook(chan);
+ else {
+ ast_log(LOG_ERROR, "'%s' is not a valid value for JACK_HOOK()\n", value);
+ res = -1;
+ }
+
+ return res;
+}
+
+static struct ast_custom_function jack_hook_function = {
+ .name = "JACK_HOOK",
+ .synopsis = "Enable a jack hook on a channel",
+ .syntax = "JACK_HOOK(<mode>,[options])",
+ .desc =
+ " The JACK_HOOK allows turning on or off jack connectivity to this channel.\n"
+ "When the JACK_HOOK is turned on, jack ports will get created that allow\n"
+ "access to the audio stream for this channel. The mode specifies which mode\n"
+ "this hook should run in. A mode must be specified when turning the JACK_HOOK.\n"
+ "on. However, all arguments are optional when turning it off.\n"
+ "\n"
+ " Valid modes are:\n"
+#if 0
+ /* XXX TODO */
+ " spy - Create a read-only audio hook. Only an output jack port will\n"
+ " get created.\n"
+ " whisper - Create a write-only audio hook. Only an input jack port will\n"
+ " get created.\n"
+#endif
+ " manipulate - Create a read/write audio hook. Both an input and an output\n"
+ " jack port will get created. Audio from the channel will be\n"
+ " sent out the output port and will be replaced by the audio\n"
+ " coming in on the input port as it gets passed on.\n"
+ "\n"
+ " Valid options are:\n"
+ COMMON_OPTIONS
+ "\n"
+ " Examples:\n"
+ " To turn on the JACK_HOOK,\n"
+ " Set(JACK_HOOK(manipulate,i(pure_data_0:input0)o(pure_data_0:output0))=on)\n"
+ " To turn off the JACK_HOOK,\n"
+ " Set(JACK_HOOK()=off)\n"
+ "",
+ .write = jack_hook_write,
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(jack_app);
+ res |= ast_custom_function_unregister(&jack_hook_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ if (ast_register_application(jack_app, jack_exec, jack_synopsis, jack_desc))
+ return AST_MODULE_LOAD_DECLINE;
+
+ if (ast_custom_function_register(&jack_hook_function)) {
+ ast_unregister_application(jack_app);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "JACK Interface");
diff --git a/trunk/apps/app_macro.c b/trunk/apps/app_macro.c
new file mode 100644
index 000000000..b087ad36b
--- /dev/null
+++ b/trunk/apps/app_macro.c
@@ -0,0 +1,527 @@
+/*
+ * 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 Dial plan macro Implementation
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+#define MAX_ARGS 80
+
+/* special result value used to force macro exit */
+#define MACRO_EXIT_RESULT 1024
+
+static char *descrip =
+" Macro(macroname,arg1,arg2...): Executes a macro using the context\n"
+"'macro-<macroname>', jumping to the 's' extension of that context and\n"
+"executing each step, then returning when the steps end. \n"
+"The calling extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
+"${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively. Arguments become\n"
+"${ARG1}, ${ARG2}, etc in the macro context.\n"
+"If you Goto out of the Macro context, the Macro will terminate and control\n"
+"will be returned at the location of the Goto.\n"
+"If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
+"at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n"
+"Extensions: While a macro is being executed, it becomes the current context.\n"
+" This means that if a hangup occurs, for instance, that the macro\n"
+" will be searched for an 'h' extension, NOT the context from which\n"
+" the macro was called. So, make sure to define all appropriate\n"
+" extensions in your macro! (Note: AEL does not use macros)\n"
+"WARNING: Because of the way Macro is implemented (it executes the priorities\n"
+" contained within it via sub-engine), and a fixed per-thread\n"
+" memory stack allowance, macros are limited to 7 levels\n"
+" of nesting (macro calling macro calling macro, etc.); It\n"
+" may be possible that stack-intensive applications in deeply nested macros\n"
+" could cause asterisk to crash earlier than this limit. It is advised that\n"
+" if you need to deeply nest macro calls, that you use the Gosub application\n"
+" (now allows arguments like a Macro) with explict Return() calls instead.\n";
+
+static char *if_descrip =
+" MacroIf(<expr>?macroname_a[,arg1][:macroname_b[,arg1]])\n"
+"Executes macro defined in <macroname_a> if <expr> is true\n"
+"(otherwise <macroname_b> if provided)\n"
+"Arguments and return values as in application Macro()\n";
+
+static char *exclusive_descrip =
+" MacroExclusive(macroname,arg1,arg2...):\n"
+"Executes macro defined in the context 'macro-macroname'\n"
+"Only one call at a time may run the macro.\n"
+"(we'll wait if another call is busy executing in the Macro)\n"
+"Arguments and return values as in application Macro()\n";
+
+static char *exit_descrip =
+" MacroExit():\n"
+"Causes the currently running macro to exit as if it had\n"
+"ended normally by running out of priorities to execute.\n"
+"If used outside a macro, will likely cause unexpected\n"
+"behavior.\n";
+
+static char *app = "Macro";
+static char *if_app = "MacroIf";
+static char *exclusive_app = "MacroExclusive";
+static char *exit_app = "MacroExit";
+
+static char *synopsis = "Macro Implementation";
+static char *if_synopsis = "Conditional Macro Implementation";
+static char *exclusive_synopsis = "Exclusive Macro Implementation";
+static char *exit_synopsis = "Exit From Macro";
+
+
+static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
+{
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_context *c2;
+
+ for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
+ if (ast_extension_match(ast_get_extension_name(e), exten)) {
+ int needmatch = ast_get_extension_matchcid(e);
+ if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
+ (!needmatch)) {
+ /* This is the matching extension we want */
+ struct ast_exten *p;
+ for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
+ if (priority != ast_get_extension_priority(p))
+ continue;
+ return p;
+ }
+ }
+ }
+ }
+
+ /* No match; run through includes */
+ for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
+ for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
+ if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
+ e = find_matching_priority(c2, exten, priority, callerid);
+ if (e)
+ return e;
+ }
+ }
+ }
+ return NULL;
+}
+
+static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
+{
+ const char *s;
+ char *tmp;
+ char *cur, *rest;
+ char *macro;
+ char fullmacro[80];
+ char varname[80];
+ char runningapp[80], runningdata[1024];
+ char *oldargs[MAX_ARGS + 1] = { NULL, };
+ int argc, x;
+ int res=0;
+ char oldexten[256]="";
+ int oldpriority, gosub_level = 0;
+ char pc[80], depthc[12];
+ char oldcontext[AST_MAX_CONTEXT] = "";
+ const char *inhangupc;
+ int offset, depth = 0, maxdepth = 7;
+ int setmacrocontext=0;
+ int autoloopflag, dead = 0, inhangup = 0;
+
+ char *save_macro_exten;
+ char *save_macro_context;
+ char *save_macro_priority;
+ char *save_macro_offset;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
+ return -1;
+ }
+
+ /* does the user want a deeper rabbit hole? */
+ s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
+ if (s)
+ sscanf(s, "%d", &maxdepth);
+
+ /* Count how many levels deep the rabbit hole goes */
+ s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
+ if (s)
+ sscanf(s, "%d", &depth);
+ /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
+ if (strcmp(chan->exten, "h") == 0)
+ pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
+ inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP");
+ if (!ast_strlen_zero(inhangupc))
+ sscanf(inhangupc, "%d", &inhangup);
+
+ if (depth >= maxdepth) {
+ ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
+ return 0;
+ }
+ snprintf(depthc, sizeof(depthc), "%d", depth + 1);
+ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
+
+ tmp = ast_strdupa(data);
+ rest = tmp;
+ macro = strsep(&rest, ",");
+ if (ast_strlen_zero(macro)) {
+ ast_log(LOG_WARNING, "Invalid macro name specified\n");
+ return 0;
+ }
+
+ snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
+ if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
+ if (!ast_context_find(fullmacro))
+ ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
+ else
+ ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
+ return 0;
+ }
+
+ /* If we are to run the macro exclusively, take the mutex */
+ if (exclusive) {
+ ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
+ ast_autoservice_start(chan);
+ if (ast_context_lockmacro(fullmacro)) {
+ ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
+ ast_autoservice_stop(chan);
+ return 0;
+ }
+ ast_autoservice_stop(chan);
+ }
+
+ /* Save old info */
+ oldpriority = chan->priority;
+ ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
+ ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
+ if (ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
+ ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
+ chan->macropriority = chan->priority;
+ setmacrocontext=1;
+ }
+ argc = 1;
+ /* Save old macro variables */
+ save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
+ pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
+
+ save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
+ pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
+
+ save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
+ snprintf(pc, sizeof(pc), "%d", oldpriority);
+ pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
+
+ save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
+ pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
+
+ /* Setup environment for new run */
+ chan->exten[0] = 's';
+ chan->exten[1] = '\0';
+ ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
+ chan->priority = 1;
+
+ while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
+ const char *s;
+ /* Save copy of old arguments if we're overwriting some, otherwise
+ let them pass through to the other macro */
+ snprintf(varname, sizeof(varname), "ARG%d", argc);
+ s = pbx_builtin_getvar_helper(chan, varname);
+ if (s)
+ oldargs[argc] = ast_strdup(s);
+ pbx_builtin_setvar_helper(chan, varname, cur);
+ argc++;
+ }
+ autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
+ ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
+ while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
+ struct ast_context *c;
+ struct ast_exten *e;
+ int foundx;
+ runningapp[0] = '\0';
+ runningdata[0] = '\0';
+
+ /* What application will execute? */
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_WARNING, "Failed to lock contexts list\n");
+ } else {
+ for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
+ if (!strcmp(ast_get_context_name(c), chan->context)) {
+ if (ast_rdlock_context(c)) {
+ ast_log(LOG_WARNING, "Unable to lock context?\n");
+ } else {
+ e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
+ if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
+ ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
+ ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
+ }
+ ast_unlock_context(c);
+ }
+ break;
+ }
+ }
+ }
+ ast_unlock_contexts();
+
+ /* Reset the macro depth, if it was changed in the last iteration */
+ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
+
+ if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num, &foundx,1))) {
+ /* Something bad happened, or a hangup has been requested. */
+ if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
+ (res == '*') || (res == '#')) {
+ /* Just return result as to the previous application as if it had been dialed */
+ ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
+ break;
+ }
+ switch(res) {
+ case MACRO_EXIT_RESULT:
+ res = 0;
+ goto out;
+ case AST_PBX_KEEPALIVE:
+ ast_debug(2, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE in macro '%s' on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
+ goto out;
+ break;
+ default:
+ ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
+ dead = 1;
+ goto out;
+ }
+ }
+
+ ast_debug(1, "Executed application: %s\n", runningapp);
+
+ if (!strcasecmp(runningapp, "GOSUB")) {
+ gosub_level++;
+ ast_debug(1, "Incrementing gosub_level\n");
+ } else if (!strcasecmp(runningapp, "GOSUBIF")) {
+ char tmp2[1024], *cond, *app, *app2 = tmp2;
+ pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
+ cond = strsep(&app2, "?");
+ app = strsep(&app2, ":");
+ if (pbx_checkcondition(cond)) {
+ if (!ast_strlen_zero(app)) {
+ gosub_level++;
+ ast_debug(1, "Incrementing gosub_level\n");
+ }
+ } else {
+ if (!ast_strlen_zero(app2)) {
+ gosub_level++;
+ ast_debug(1, "Incrementing gosub_level\n");
+ }
+ }
+ } else if (!strcasecmp(runningapp, "RETURN")) {
+ gosub_level--;
+ ast_debug(1, "Decrementing gosub_level\n");
+ } else if (!strcasecmp(runningapp, "STACKPOP")) {
+ gosub_level--;
+ ast_debug(1, "Decrementing gosub_level\n");
+ } else if (!strncasecmp(runningapp, "EXEC", 4)) {
+ /* Must evaluate args to find actual app */
+ char tmp2[1024], *tmp3 = NULL;
+ pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
+ if (!strcasecmp(runningapp, "EXECIF")) {
+ tmp3 = strchr(tmp2, '|');
+ if (tmp3)
+ *tmp3++ = '\0';
+ if (!pbx_checkcondition(tmp2))
+ tmp3 = NULL;
+ } else
+ tmp3 = tmp2;
+
+ if (tmp3)
+ ast_debug(1, "Last app: %s\n", tmp3);
+
+ if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
+ gosub_level++;
+ ast_debug(1, "Incrementing gosub_level\n");
+ } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
+ gosub_level--;
+ ast_debug(1, "Decrementing gosub_level\n");
+ } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
+ gosub_level--;
+ ast_debug(1, "Decrementing gosub_level\n");
+ }
+ }
+
+ if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
+ ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
+ break;
+ }
+
+ /* don't stop executing extensions when we're in "h" */
+ if (ast_check_hangup(chan) && !inhangup) {
+ ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
+ goto out;
+ }
+ chan->priority++;
+ }
+ out:
+ /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
+ snprintf(depthc, sizeof(depthc), "%d", depth);
+ if (!dead) {
+ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
+ ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
+ }
+
+ for (x = 1; x < argc; x++) {
+ /* Restore old arguments and delete ours */
+ snprintf(varname, sizeof(varname), "ARG%d", x);
+ if (oldargs[x]) {
+ if (!dead)
+ pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
+ ast_free(oldargs[x]);
+ } else if (!dead) {
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+ }
+
+ /* Restore macro variables */
+ if (!dead) {
+ pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
+ pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
+ pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
+ }
+ if (save_macro_exten)
+ ast_free(save_macro_exten);
+ if (save_macro_context)
+ ast_free(save_macro_context);
+ if (save_macro_priority)
+ ast_free(save_macro_priority);
+
+ if (!dead && setmacrocontext) {
+ chan->macrocontext[0] = '\0';
+ chan->macroexten[0] = '\0';
+ chan->macropriority = 0;
+ }
+
+ if (!dead && !strcasecmp(chan->context, fullmacro)) {
+ /* If we're leaving the macro normally, restore original information */
+ chan->priority = oldpriority;
+ ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
+ if (!(ast_check_hangup(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
+ /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
+ const char *offsets;
+ ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
+ if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
+ /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
+ normally if there is any problem */
+ if (sscanf(offsets, "%d", &offset) == 1) {
+ if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
+ chan->priority += offset;
+ }
+ }
+ }
+ }
+ }
+
+ if (!dead)
+ pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
+ if (save_macro_offset)
+ ast_free(save_macro_offset);
+
+ /* Unlock the macro */
+ if (exclusive) {
+ ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
+ if (ast_context_unlockmacro(fullmacro)) {
+ ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
+ res = 0;
+ }
+ }
+
+ return res;
+}
+
+static int macro_exec(struct ast_channel *chan, void *data)
+{
+ return _macro_exec(chan, data, 0);
+}
+
+static int macroexclusive_exec(struct ast_channel *chan, void *data)
+{
+ return _macro_exec(chan, data, 1);
+}
+
+static int macroif_exec(struct ast_channel *chan, void *data)
+{
+ char *expr = NULL, *label_a = NULL, *label_b = NULL;
+ int res = 0;
+
+ if (!(expr = ast_strdupa(data)))
+ return -1;
+
+ if ((label_a = strchr(expr, '?'))) {
+ *label_a = '\0';
+ label_a++;
+ if ((label_b = strchr(label_a, ':'))) {
+ *label_b = '\0';
+ label_b++;
+ }
+ if (pbx_checkcondition(expr))
+ res = macro_exec(chan, label_a);
+ else if (label_b)
+ res = macro_exec(chan, label_b);
+ } else
+ ast_log(LOG_WARNING, "Invalid Syntax.\n");
+
+ return res;
+}
+
+static int macro_exit_exec(struct ast_channel *chan, void *data)
+{
+ return MACRO_EXIT_RESULT;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(if_app);
+ res |= ast_unregister_application(exit_app);
+ res |= ast_unregister_application(app);
+ res |= ast_unregister_application(exclusive_app);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
+ res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
+ res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
+ res |= ast_register_application(app, macro_exec, synopsis, descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");
diff --git a/trunk/apps/app_meetme.c b/trunk/apps/app_meetme.c
new file mode 100644
index 000000000..3cc5b551e
--- /dev/null
+++ b/trunk/apps/app_meetme.c
@@ -0,0 +1,5592 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * SLA Implementation by:
+ * Russell Bryant <russell@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 Meet me conference bridge and Shared Line Appearances
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author (SLA) Russell Bryant <russell@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>zaptel</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/zapata.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/manager.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/astobj.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/dial.h"
+#include "asterisk/causes.h"
+#include "asterisk/paths.h"
+
+#include "enter.h"
+#include "leave.h"
+
+#define CONFIG_FILE_NAME "meetme.conf"
+#define SLA_CONFIG_FILE "sla.conf"
+
+/*! each buffer is 20ms, so this is 640ms total */
+#define DEFAULT_AUDIO_BUFFERS 32
+
+/*! String format for scheduled conferences */
+#define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
+
+enum {
+ ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
+ ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
+ ADMINFLAG_KICKME = (1 << 3), /*!< User has been kicked */
+ /*! User has requested to speak */
+ ADMINFLAG_T_REQUEST = (1 << 4),
+};
+
+#define MEETME_DELAYDETECTTALK 300
+#define MEETME_DELAYDETECTENDTALK 1000
+
+#define AST_FRAME_BITS 32
+
+enum volume_action {
+ VOL_UP,
+ VOL_DOWN
+};
+
+enum entrance_sound {
+ ENTER,
+ LEAVE
+};
+
+enum recording_state {
+ MEETME_RECORD_OFF,
+ MEETME_RECORD_STARTED,
+ MEETME_RECORD_ACTIVE,
+ MEETME_RECORD_TERMINATE
+};
+
+#define CONF_SIZE 320
+
+enum {
+ /*! user has admin access on the conference */
+ CONFFLAG_ADMIN = (1 << 0),
+ /*! If set the user can only receive audio from the conference */
+ CONFFLAG_MONITOR = (1 << 1),
+ /*! If set asterisk will exit conference when key defined in p() option is pressed */
+ CONFFLAG_KEYEXIT = (1 << 2),
+ /*! If set asterisk will provide a menu to the user when '*' is pressed */
+ CONFFLAG_STARMENU = (1 << 3),
+ /*! If set the use can only send audio to the conference */
+ CONFFLAG_TALKER = (1 << 4),
+ /*! If set there will be no enter or leave sounds */
+ CONFFLAG_QUIET = (1 << 5),
+ /*! If set, when user joins the conference, they will be told the number
+ * of users that are already in */
+ CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
+ /*! Set to run AGI Script in Background */
+ CONFFLAG_AGI = (1 << 7),
+ /*! Set to have music on hold when user is alone in conference */
+ CONFFLAG_MOH = (1 << 8),
+ /*! If set the MeetMe will return if all marked with this flag left */
+ CONFFLAG_MARKEDEXIT = (1 << 9),
+ /*! If set, the MeetMe will wait until a marked user enters */
+ CONFFLAG_WAITMARKED = (1 << 10),
+ /*! If set, the MeetMe will exit to the specified context */
+ CONFFLAG_EXIT_CONTEXT = (1 << 11),
+ /*! If set, the user will be marked */
+ CONFFLAG_MARKEDUSER = (1 << 12),
+ /*! If set, user will be ask record name on entry of conference */
+ CONFFLAG_INTROUSER = (1 << 13),
+ /*! If set, the MeetMe will be recorded */
+ CONFFLAG_RECORDCONF = (1<< 14),
+ /*! If set, the user will be monitored if the user is talking or not */
+ CONFFLAG_MONITORTALKER = (1 << 15),
+ CONFFLAG_DYNAMIC = (1 << 16),
+ CONFFLAG_DYNAMICPIN = (1 << 17),
+ CONFFLAG_EMPTY = (1 << 18),
+ CONFFLAG_EMPTYNOPIN = (1 << 19),
+ CONFFLAG_ALWAYSPROMPT = (1 << 20),
+ /*! If set, won't speak the extra prompt when the first person
+ * enters the conference */
+ CONFFLAG_NOONLYPERSON = (1 << 22),
+ /*! If set, user will be asked to record name on entry of conference
+ * without review */
+ CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
+ /*! If set, the user will be initially self-muted */
+ CONFFLAG_STARTMUTED = (1 << 24),
+ /*! Pass DTMF through the conference */
+ CONFFLAG_PASS_DTMF = (1 << 25),
+ CONFFLAG_SLA_STATION = (1 << 26),
+ CONFFLAG_SLA_TRUNK = (1 << 27),
+ /*! If set, the user should continue in the dialplan if kicked out */
+ CONFFLAG_KICK_CONTINUE = (1 << 28),
+ CONFFLAG_DURATION_STOP = (1 << 29),
+ CONFFLAG_DURATION_LIMIT = (1 << 30),
+};
+
+enum {
+ OPT_ARG_WAITMARKED = 0,
+ OPT_ARG_EXITKEYS = 1,
+ OPT_ARG_DURATION_STOP = 2,
+ OPT_ARG_DURATION_LIMIT = 3,
+ OPT_ARG_MOH_CLASS = 4,
+ OPT_ARG_ARRAY_SIZE = 5,
+};
+
+AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
+ AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
+ AST_APP_OPTION('a', CONFFLAG_ADMIN ),
+ AST_APP_OPTION('b', CONFFLAG_AGI ),
+ AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
+ AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
+ AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
+ AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
+ AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
+ AST_APP_OPTION('e', CONFFLAG_EMPTY ),
+ AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
+ AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
+ AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
+ AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
+ AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
+ AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
+ AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
+ AST_APP_OPTION('q', CONFFLAG_QUIET ),
+ AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
+ AST_APP_OPTION('s', CONFFLAG_STARMENU ),
+ AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
+ AST_APP_OPTION('l', CONFFLAG_MONITOR ),
+ AST_APP_OPTION('t', CONFFLAG_TALKER ),
+ AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
+ AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
+ AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
+ AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
+ AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
+ AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
+END_OPTIONS );
+
+static const char *app = "MeetMe";
+static const char *app2 = "MeetMeCount";
+static const char *app3 = "MeetMeAdmin";
+static const char *app4 = "MeetMeChannelAdmin";
+static const char *slastation_app = "SLAStation";
+static const char *slatrunk_app = "SLATrunk";
+
+static const char *synopsis = "MeetMe conference bridge";
+static const char *synopsis2 = "MeetMe participant count";
+static const char *synopsis3 = "MeetMe conference Administration";
+static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
+static const char *slastation_synopsis = "Shared Line Appearance Station";
+static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
+
+/* Lookup RealTime conferences based on confno and current time */
+static int rt_schedule;
+static int fuzzystart;
+static int earlyalert;
+static int endalert;
+
+/* Log participant count to the RealTime backend */
+static int rt_log_members;
+
+static const char *descrip =
+" MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
+"conference. If the conference number is omitted, the user will be prompted\n"
+"to enter one. User can exit the conference by hangup, or if the 'p' option\n"
+"is specified, by pressing '#'.\n"
+"Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
+" must be present for conferencing to operate properly. In addition, the chan_zap\n"
+" channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'a' -- set admin mode\n"
+" 'A' -- set marked mode\n"
+" 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
+" Default: conf-background.agi (Note: This does not work with\n"
+" non-Zap channels in the same conference)\n"
+" 'c' -- announce user(s) count on joining a conference\n"
+" 'C' -- continue in dialplan when kicked out of conference\n"
+" 'd' -- dynamically add conference\n"
+" 'D' -- dynamically add conference, prompting for a PIN\n"
+" 'e' -- select an empty conference\n"
+" 'E' -- select an empty pinless conference\n"
+" 'F' -- Pass DTMF through the conference.\n"
+" 'i' -- announce user join/leave with review\n"
+" 'I' -- announce user join/leave without review\n"
+" 'l' -- set listen only mode (Listen only, no talking)\n"
+" 'm' -- set initially muted\n"
+" 'M[(<class>)]'\n"
+" -- enable music on hold when the conference has a single caller.\n"
+" Optionally, specify a musiconhold class to use. If one is not\n"
+" provided, it will use the channel's currently set music class,\n"
+" or \"default\".\n"
+" 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
+" being muted, meaning (a) No encode is done on transmission and\n"
+" (b) Received audio that is not registered as talking is omitted\n"
+" causing no buildup in background noise\n"
+" 'p[(<keys>)]'\n"
+" -- allow user to exit the conference by pressing '#' (default)\n"
+" or any of the defined keys. If keys contain '*' this will override\n"
+" option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
+" 'P' -- always prompt for the pin even if it is specified\n"
+" 'q' -- quiet mode (don't play enter/leave sounds)\n"
+" 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
+" using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
+" meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
+" wav.\n"
+" 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
+" 't' -- set talk only mode. (Talk only, no listening)\n"
+" 'T' -- set talker detection (sent to manager interface and meetme list)\n"
+" 'w[(<secs>)]'\n"
+" -- wait until the marked user enters the conference\n"
+" 'x' -- close the conference when last marked user exits\n"
+" 'X' -- allow user to exit the conference by entering a valid single\n"
+" digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
+" if that variable is not defined.\n"
+" '1' -- do not play message when first person enters\n"
+" 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
+" 'L(x[:y][:z])' - Limit the conference 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"
+" * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
+" * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
+" The default is to say the time remaining.\n"
+"";
+
+static const char *descrip2 =
+" MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
+"MeetMe conference. If var is specified, playback will be skipped and the value\n"
+"will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
+"the channel, unless priority n+1 exists, in which case priority progress will\n"
+"continue.\n"
+"A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
+
+static const char *descrip3 =
+" MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
+" 'e' -- Eject last user that joined\n"
+" 'k' -- Kick one user out of conference\n"
+" 'K' -- Kick all users out of conference\n"
+" 'l' -- Unlock conference\n"
+" 'L' -- Lock conference\n"
+" 'm' -- Unmute one user\n"
+" 'M' -- Mute one user\n"
+" 'n' -- Unmute all users in the conference\n"
+" 'N' -- Mute all non-admin users in the conference\n"
+" 'r' -- Reset one user's volume settings\n"
+" 'R' -- Reset all users volume settings\n"
+" 's' -- Lower entire conference speaking volume\n"
+" 'S' -- Raise entire conference speaking volume\n"
+" 't' -- Lower one user's talk volume\n"
+" 'T' -- Raise one user's talk volume\n"
+" 'u' -- Lower one user's listen volume\n"
+" 'U' -- Raise one user's listen volume\n"
+" 'v' -- Lower entire conference listening volume\n"
+" 'V' -- Raise entire conference listening volume\n"
+"";
+
+static const char *descrip4 =
+" MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
+"channel in any coference.\n"
+" 'k' -- Kick the specified user out of the conference he is in\n"
+" 'm' -- Unmute the specified user\n"
+" 'M' -- Mute the specified user\n"
+"";
+
+static const char *slastation_desc =
+" SLAStation(<station name>):\n"
+"This application should be executed by an SLA station. The argument depends\n"
+"on how the call was initiated. If the phone was just taken off hook, then\n"
+"the argument \"station\" should be just the station name. If the call was\n"
+"initiated by pressing a line key, then the station name should be preceded\n"
+"by an underscore and the trunk name associated with that line button.\n"
+"For example: \"station1_line1\"."
+" On exit, this application will set the variable SLASTATION_STATUS to\n"
+"one of the following values:\n"
+" FAILURE | CONGESTION | SUCCESS\n"
+"";
+
+static const char *slatrunk_desc =
+" SLATrunk(<trunk name>[,options]):\n"
+"This application should be executed by an SLA trunk on an inbound call.\n"
+"The channel calling this application should correspond to the SLA trunk\n"
+"with the name \"trunk\" that is being passed as an argument.\n"
+" On exit, this application will set the variable SLATRUNK_STATUS to\n"
+"one of the following values:\n"
+" FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
+" The available options are:\n"
+" M[(<class>)] - Play back the specified MOH class instead of ringing\n"
+"";
+
+#define MAX_CONFNUM 80
+#define MAX_PIN 80
+
+/*! \brief The MeetMe Conference object */
+struct ast_conference {
+ ast_mutex_t playlock; /*!< Conference specific lock (players) */
+ ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
+ char confno[MAX_CONFNUM]; /*!< Conference */
+ struct ast_channel *chan; /*!< Announcements channel */
+ struct ast_channel *lchan; /*!< Listen/Record channel */
+ int fd; /*!< Announcements fd */
+ int zapconf; /*!< Zaptel Conf # */
+ int users; /*!< Number of active users */
+ int markedusers; /*!< Number of marked users */
+ int maxusers; /*!< Participant limit if scheduled */
+ int endalert; /*!< When to play conf ending message */
+ time_t start; /*!< Start time (s) */
+ int refcount; /*!< reference count of usage */
+ enum recording_state recording:2; /*!< recording status */
+ unsigned int isdynamic:1; /*!< Created on the fly? */
+ unsigned int locked:1; /*!< Is the conference locked? */
+ pthread_t recordthread; /*!< thread for recording */
+ ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
+ pthread_attr_t attr; /*!< thread attribute */
+ const char *recordingfilename; /*!< Filename to record the Conference into */
+ const char *recordingformat; /*!< Format to record the Conference in */
+ char pin[MAX_PIN]; /*!< If protected by a PIN */
+ char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
+ char uniqueid[32];
+ long endtime; /*!< When to end the conf if scheduled */
+ struct ast_frame *transframe[32];
+ struct ast_frame *origframe;
+ struct ast_trans_pvt *transpath[32];
+ AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
+ AST_LIST_ENTRY(ast_conference) list;
+};
+
+static AST_LIST_HEAD_STATIC(confs, ast_conference);
+
+static unsigned int conf_map[1024] = {0, };
+
+struct volume {
+ int desired; /*!< Desired volume adjustment */
+ int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
+};
+
+/*! \brief The MeetMe User object */
+struct ast_conf_user {
+ int user_no; /*!< User Number */
+ int userflags; /*!< Flags as set in the conference */
+ int adminflags; /*!< Flags set by the Admin */
+ struct ast_channel *chan; /*!< Connected channel */
+ int talking; /*!< Is user talking */
+ int zapchannel; /*!< Is a Zaptel channel */
+ char usrvalue[50]; /*!< Custom User Value */
+ char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
+ time_t jointime; /*!< Time the user joined the conference */
+ time_t kicktime; /*!< Time the user will be kicked from the conference */
+ struct timeval start_time; /*!< Time the user entered into the conference */
+ long timelimit; /*!< Time limit for the user to be in the conference L(x:y:z) */
+ long play_warning; /*!< Play a warning when 'y' ms are left */
+ long warning_freq; /*!< Repeat the warning every 'z' ms */
+ const char *warning_sound; /*!< File to play as warning if 'y' is defined */
+ const char *end_sound; /*!< File to play when time is up. */
+ struct volume talk;
+ struct volume listen;
+ AST_LIST_ENTRY(ast_conf_user) list;
+};
+
+enum sla_which_trunk_refs {
+ ALL_TRUNK_REFS,
+ INACTIVE_TRUNK_REFS,
+};
+
+enum sla_trunk_state {
+ SLA_TRUNK_STATE_IDLE,
+ SLA_TRUNK_STATE_RINGING,
+ SLA_TRUNK_STATE_UP,
+ SLA_TRUNK_STATE_ONHOLD,
+ SLA_TRUNK_STATE_ONHOLD_BYME,
+};
+
+enum sla_hold_access {
+ /*! This means that any station can put it on hold, and any station
+ * can retrieve the call from hold. */
+ SLA_HOLD_OPEN,
+ /*! This means that only the station that put the call on hold may
+ * retrieve it from hold. */
+ SLA_HOLD_PRIVATE,
+};
+
+struct sla_trunk_ref;
+
+struct sla_station {
+ AST_RWLIST_ENTRY(sla_station) entry;
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name);
+ AST_STRING_FIELD(device);
+ AST_STRING_FIELD(autocontext);
+ );
+ AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
+ struct ast_dial *dial;
+ /*! Ring timeout for this station, for any trunk. If a ring timeout
+ * is set for a specific trunk on this station, that will take
+ * priority over this value. */
+ unsigned int ring_timeout;
+ /*! Ring delay for this station, for any trunk. If a ring delay
+ * is set for a specific trunk on this station, that will take
+ * priority over this value. */
+ unsigned int ring_delay;
+ /*! This option uses the values in the sla_hold_access enum and sets the
+ * access control type for hold on this station. */
+ unsigned int hold_access:1;
+ /*! Use count for inside sla_station_exec */
+ unsigned int ref_count;
+};
+
+struct sla_station_ref {
+ AST_LIST_ENTRY(sla_station_ref) entry;
+ struct sla_station *station;
+};
+
+struct sla_trunk {
+ AST_RWLIST_ENTRY(sla_trunk) entry;
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name);
+ AST_STRING_FIELD(device);
+ AST_STRING_FIELD(autocontext);
+ );
+ AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
+ /*! Number of stations that use this trunk */
+ unsigned int num_stations;
+ /*! Number of stations currently on a call with this trunk */
+ unsigned int active_stations;
+ /*! Number of stations that have this trunk on hold. */
+ unsigned int hold_stations;
+ struct ast_channel *chan;
+ unsigned int ring_timeout;
+ /*! If set to 1, no station will be able to join an active call with
+ * this trunk. */
+ unsigned int barge_disabled:1;
+ /*! This option uses the values in the sla_hold_access enum and sets the
+ * access control type for hold on this trunk. */
+ unsigned int hold_access:1;
+ /*! Whether this trunk is currently on hold, meaning that once a station
+ * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
+ unsigned int on_hold:1;
+ /*! Use count for inside sla_trunk_exec */
+ unsigned int ref_count;
+};
+
+struct sla_trunk_ref {
+ AST_LIST_ENTRY(sla_trunk_ref) entry;
+ struct sla_trunk *trunk;
+ enum sla_trunk_state state;
+ struct ast_channel *chan;
+ /*! Ring timeout to use when this trunk is ringing on this specific
+ * station. This takes higher priority than a ring timeout set at
+ * the station level. */
+ unsigned int ring_timeout;
+ /*! Ring delay to use when this trunk is ringing on this specific
+ * station. This takes higher priority than a ring delay set at
+ * the station level. */
+ unsigned int ring_delay;
+};
+
+static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
+static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
+
+static const char sla_registrar[] = "SLA";
+
+/*! \brief Event types that can be queued up for the SLA thread */
+enum sla_event_type {
+ /*! A station has put the call on hold */
+ SLA_EVENT_HOLD,
+ /*! The state of a dial has changed */
+ SLA_EVENT_DIAL_STATE,
+ /*! The state of a ringing trunk has changed */
+ SLA_EVENT_RINGING_TRUNK,
+ /*! A reload of configuration has been requested */
+ SLA_EVENT_RELOAD,
+ /*! Poke the SLA thread so it can check if it can perform a reload */
+ SLA_EVENT_CHECK_RELOAD,
+};
+
+struct sla_event {
+ enum sla_event_type type;
+ struct sla_station *station;
+ struct sla_trunk_ref *trunk_ref;
+ AST_LIST_ENTRY(sla_event) entry;
+};
+
+/*! \brief A station that failed to be dialed
+ * \note Only used by the SLA thread. */
+struct sla_failed_station {
+ struct sla_station *station;
+ struct timeval last_try;
+ AST_LIST_ENTRY(sla_failed_station) entry;
+};
+
+/*! \brief A trunk that is ringing */
+struct sla_ringing_trunk {
+ struct sla_trunk *trunk;
+ /*! The time that this trunk started ringing */
+ struct timeval ring_begin;
+ AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
+ AST_LIST_ENTRY(sla_ringing_trunk) entry;
+};
+
+enum sla_station_hangup {
+ SLA_STATION_HANGUP_NORMAL,
+ SLA_STATION_HANGUP_TIMEOUT,
+};
+
+/*! \brief A station that is ringing */
+struct sla_ringing_station {
+ struct sla_station *station;
+ /*! The time that this station started ringing */
+ struct timeval ring_begin;
+ AST_LIST_ENTRY(sla_ringing_station) entry;
+};
+
+/*!
+ * \brief A structure for data used by the sla thread
+ */
+static struct {
+ /*! The SLA thread ID */
+ pthread_t thread;
+ ast_cond_t cond;
+ ast_mutex_t lock;
+ AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
+ AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
+ AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
+ AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
+ unsigned int stop:1;
+ /*! Attempt to handle CallerID, even though it is known not to work
+ * properly in some situations. */
+ unsigned int attempt_callerid:1;
+ /*! A reload has been requested */
+ unsigned int reload:1;
+} sla = {
+ .thread = AST_PTHREADT_NULL,
+};
+
+/*! The number of audio buffers to be allocated on pseudo channels
+ * when in a conference */
+static int audio_buffers;
+
+/*! Map 'volume' levels from -5 through +5 into
+ * decibel (dB) settings for channel drivers
+ * Note: these are not a straight linear-to-dB
+ * conversion... the numbers have been modified
+ * to give the user a better level of adjustability
+ */
+static char const gain_map[] = {
+ -15,
+ -13,
+ -10,
+ -6,
+ 0,
+ 0,
+ 0,
+ 6,
+ 10,
+ 13,
+ 15,
+};
+
+
+static int admin_exec(struct ast_channel *chan, void *data);
+static void *recordthread(void *args);
+
+static char *istalking(int x)
+{
+ if (x > 0)
+ return "(talking)";
+ else if (x < 0)
+ return "(unmonitored)";
+ else
+ return "(not talking)";
+}
+
+static int careful_write(int fd, unsigned char *data, int len, int block)
+{
+ int res;
+ int x;
+
+ while (len) {
+ if (block) {
+ x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
+ res = ioctl(fd, ZT_IOMUX, &x);
+ } else
+ res = 0;
+ if (res >= 0)
+ 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 set_talk_volume(struct ast_conf_user *user, int volume)
+{
+ char gain_adjust;
+
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ gain_adjust = gain_map[volume + 5];
+
+ return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
+}
+
+static int set_listen_volume(struct ast_conf_user *user, int volume)
+{
+ char gain_adjust;
+
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ gain_adjust = gain_map[volume + 5];
+
+ return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
+}
+
+static void tweak_volume(struct volume *vol, enum volume_action action)
+{
+ switch (action) {
+ case VOL_UP:
+ switch (vol->desired) {
+ case 5:
+ break;
+ case 0:
+ vol->desired = 2;
+ break;
+ case -2:
+ vol->desired = 0;
+ break;
+ default:
+ vol->desired++;
+ break;
+ }
+ break;
+ case VOL_DOWN:
+ switch (vol->desired) {
+ case -5:
+ break;
+ case 2:
+ vol->desired = 0;
+ break;
+ case 0:
+ vol->desired = -2;
+ break;
+ default:
+ vol->desired--;
+ break;
+ }
+ }
+}
+
+static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
+{
+ tweak_volume(&user->talk, action);
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ if (!set_talk_volume(user, user->talk.desired))
+ user->talk.actual = 0;
+ else
+ user->talk.actual = user->talk.desired;
+}
+
+static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
+{
+ tweak_volume(&user->listen, action);
+ /* attempt to make the adjustment in the channel driver;
+ if successful, don't adjust in the frame reading routine
+ */
+ if (!set_listen_volume(user, user->listen.desired))
+ user->listen.actual = 0;
+ else
+ user->listen.actual = user->listen.desired;
+}
+
+static void reset_volumes(struct ast_conf_user *user)
+{
+ signed char zero_volume = 0;
+
+ ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
+ ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
+}
+
+static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
+{
+ unsigned char *data;
+ int len;
+ int res = -1;
+
+ if (!ast_check_hangup(chan))
+ res = ast_autoservice_start(chan);
+
+ AST_LIST_LOCK(&confs);
+
+ switch(sound) {
+ case ENTER:
+ data = enter;
+ len = sizeof(enter);
+ break;
+ case LEAVE:
+ data = leave;
+ len = sizeof(leave);
+ break;
+ default:
+ data = NULL;
+ len = 0;
+ }
+ if (data) {
+ careful_write(conf->fd, data, len, 1);
+ }
+
+ AST_LIST_UNLOCK(&confs);
+
+ if (!res)
+ ast_autoservice_stop(chan);
+}
+
+/*!
+ * \brief Find or create a conference
+ *
+ * \param confno The conference name/number
+ * \param pin The regular user pin
+ * \param pinadmin The admin pin
+ * \param make Make the conf if it doesn't exist
+ * \param dynamic Mark the newly created conference as dynamic
+ * \param refcount How many references to mark on the conference
+ * \param chan The asterisk channel
+ *
+ * \return A pointer to the conference struct, or NULL if it wasn't found and
+ * make or dynamic were not set.
+ */
+static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
+{
+ struct ast_conference *cnf;
+ struct zt_confinfo ztc = { 0, };
+ int confno_int = 0;
+
+ AST_LIST_LOCK(&confs);
+
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (!strcmp(confno, cnf->confno))
+ break;
+ }
+
+ if (cnf || (!make && !dynamic))
+ goto cnfout;
+
+ /* Make a new one */
+ if (!(cnf = ast_calloc(1, sizeof(*cnf))))
+ goto cnfout;
+
+ ast_mutex_init(&cnf->playlock);
+ ast_mutex_init(&cnf->listenlock);
+ cnf->recordthread = AST_PTHREADT_NULL;
+ ast_mutex_init(&cnf->recordthreadlock);
+ ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
+ ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
+ ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
+ ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
+
+ /* Setup a new zap conference */
+ ztc.confno = -1;
+ ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
+ cnf->fd = open("/dev/zap/pseudo", O_RDWR);
+ if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Unable to open pseudo device\n");
+ if (cnf->fd >= 0)
+ close(cnf->fd);
+ ast_free(cnf);
+ cnf = NULL;
+ goto cnfout;
+ }
+
+ cnf->zapconf = ztc.confno;
+
+ /* Setup a new channel for playback of audio files */
+ cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (cnf->chan) {
+ ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
+ ztc.chan = 0;
+ ztc.confno = cnf->zapconf;
+ ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
+ if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ if (cnf->chan)
+ ast_hangup(cnf->chan);
+ else
+ close(cnf->fd);
+
+ ast_free(cnf);
+ cnf = NULL;
+ goto cnfout;
+ }
+ }
+
+ /* Fill the conference struct */
+ cnf->start = time(NULL);
+ cnf->maxusers = 0x7fffffff;
+ cnf->isdynamic = dynamic ? 1 : 0;
+ ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
+ AST_LIST_INSERT_HEAD(&confs, cnf, list);
+
+ /* Reserve conference number in map */
+ if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
+ conf_map[confno_int] = 1;
+
+cnfout:
+ if (cnf)
+ ast_atomic_fetchadd_int(&cnf->refcount, refcount);
+
+ AST_LIST_UNLOCK(&confs);
+
+ return cnf;
+}
+
+
+static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
+{
+ static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
+
+ int len = strlen(word);
+ int which = 0;
+ struct ast_conference *cnf = NULL;
+ struct ast_conf_user *usr = NULL;
+ char *confno = NULL;
+ char usrno[50] = "";
+ char *myline, *ret = NULL;
+
+ if (pos == 1) { /* Command */
+ return ast_cli_complete(word, cmds, state);
+ } else if (pos == 2) { /* Conference Number */
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
+ ret = cnf->confno;
+ break;
+ }
+ }
+ ret = ast_strdup(ret); /* dup before releasing the lock */
+ AST_LIST_UNLOCK(&confs);
+ return ret;
+ } else if (pos == 3) {
+ /* User Number || Conf Command option*/
+ if (strstr(line, "mute") || strstr(line, "kick")) {
+ if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
+ return ast_strdup("all");
+ which++;
+ AST_LIST_LOCK(&confs);
+
+ /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
+ myline = ast_strdupa(line);
+ if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
+ while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
+ ;
+ }
+
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (!strcmp(confno, cnf->confno))
+ break;
+ }
+
+ if (cnf) {
+ /* Search for the user */
+ AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
+ snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
+ if (!strncasecmp(word, usrno, len) && ++which > state)
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&confs);
+ return usr ? ast_strdup(usrno) : NULL;
+ } else if (strstr(line, "list") && (state == 0))
+ return ast_strdup("concise");
+ }
+
+ return NULL;
+}
+
+static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ /* Process the command */
+ struct ast_conference *cnf;
+ struct ast_conf_user *user;
+ int hr, min, sec;
+ int i = 0, total = 0;
+ time_t now;
+ char *header_format = "%-14s %-14s %-10s %-8s %-8s %-6s\n";
+ char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n";
+ char cmdline[1024] = "";
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "meetme";
+ e->usage =
+ "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
+ " Executes a command for the conference or on a conferee\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_meetmecmd(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc > 8)
+ ast_cli(a->fd, "Invalid Arguments.\n");
+ /* Check for length so no buffer will overflow... */
+ for (i = 0; i < a->argc; i++) {
+ if (strlen(a->argv[i]) > 100)
+ ast_cli(a->fd, "Invalid Arguments.\n");
+ }
+ if (a->argc == 1 || (a->argc == 2 && !strcasecmp(a->argv[1], "concise"))) {
+ /* 'MeetMe': List all the conferences */
+ int concise = (a->argc == 2 && !strcasecmp(a->argv[1], "concise"));
+ now = time(NULL);
+ AST_LIST_LOCK(&confs);
+ if (AST_LIST_EMPTY(&confs)) {
+ if (!concise)
+ ast_cli(a->fd, "No active MeetMe conferences.\n");
+ AST_LIST_UNLOCK(&confs);
+ return CLI_SUCCESS;
+ }
+ if (!concise)
+ ast_cli(a->fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (cnf->markedusers == 0)
+ strcpy(cmdline, "N/A ");
+ else
+ snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
+ hr = (now - cnf->start) / 3600;
+ min = ((now - cnf->start) % 3600) / 60;
+ sec = (now - cnf->start) % 60;
+ if (!concise)
+ ast_cli(a->fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
+ else {
+ ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
+ cnf->confno,
+ cnf->users,
+ cnf->markedusers,
+ hr, min, sec,
+ cnf->isdynamic,
+ cnf->locked);
+ }
+
+ total += cnf->users;
+ }
+ AST_LIST_UNLOCK(&confs);
+ if (!concise)
+ ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
+ return CLI_SUCCESS;
+ }
+ if (a->argc < 3)
+ return CLI_SHOWUSAGE;
+ ast_copy_string(cmdline, a->argv[2], sizeof(cmdline)); /* Argv 2: conference number */
+ if (strstr(a->argv[1], "lock")) {
+ if (strcmp(a->argv[1], "lock") == 0) {
+ /* Lock */
+ strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ /* Unlock */
+ strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ } else if (strstr(a->argv[1], "mute")) {
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+ if (strcmp(a->argv[1], "mute") == 0) {
+ /* Mute */
+ if (strcmp(a->argv[3], "all") == 0) {
+ strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
+ strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ } else {
+ /* Unmute */
+ if (strcmp(a->argv[3], "all") == 0) {
+ strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
+ strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ }
+ } else if (strcmp(a->argv[1], "kick") == 0) {
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+ if (strcmp(a->argv[3], "all") == 0) {
+ /* Kick all */
+ strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
+ } else {
+ /* Kick a single user */
+ strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
+ strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
+ }
+ } else if (strcmp(a->argv[1], "list") == 0) {
+ int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
+ /* List all the users in a conference */
+ if (AST_LIST_EMPTY(&confs)) {
+ if (!concise)
+ ast_cli(a->fd, "No active conferences.\n");
+ return CLI_SUCCESS;
+ }
+ /* Find the right conference */
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (strcmp(cnf->confno, a->argv[2]) == 0)
+ break;
+ }
+ if (!cnf) {
+ if (!concise)
+ ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
+ AST_LIST_UNLOCK(&confs);
+ return CLI_SUCCESS;
+ }
+ /* Show all the users */
+ time(&now);
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
+ hr = (now - user->jointime) / 3600;
+ min = ((now - user->jointime) % 3600) / 60;
+ sec = (now - user->jointime) % 60;
+ if (!concise)
+ ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
+ user->user_no,
+ S_OR(user->chan->cid.cid_num, "<unknown>"),
+ S_OR(user->chan->cid.cid_name, "<no name>"),
+ user->chan->name,
+ user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
+ user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
+ user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
+ user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
+ istalking(user->talking), hr, min, sec);
+ else
+ ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
+ user->user_no,
+ S_OR(user->chan->cid.cid_num, ""),
+ S_OR(user->chan->cid.cid_name, ""),
+ user->chan->name,
+ user->userflags & CONFFLAG_ADMIN ? "1" : "",
+ user->userflags & CONFFLAG_MONITOR ? "1" : "",
+ user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
+ user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
+ user->talking, hr, min, sec);
+
+ }
+ if (!concise)
+ ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
+ AST_LIST_UNLOCK(&confs);
+ return CLI_SUCCESS;
+ } else
+ return CLI_SHOWUSAGE;
+
+ ast_debug(1, "Cmdline: %s\n", cmdline);
+
+ admin_exec(NULL, cmdline);
+
+ return CLI_SUCCESS;
+}
+
+static const char *sla_hold_str(unsigned int hold_access)
+{
+ const char *hold = "Unknown";
+
+ switch (hold_access) {
+ case SLA_HOLD_OPEN:
+ hold = "Open";
+ break;
+ case SLA_HOLD_PRIVATE:
+ hold = "Private";
+ default:
+ break;
+ }
+
+ return hold;
+}
+
+static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ const struct sla_trunk *trunk;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sla show trunks";
+ e->usage =
+ "Usage: sla show trunks\n"
+ " This will list all trunks defined in sla.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "\n"
+ "=============================================================\n"
+ "=== Configured SLA Trunks ===================================\n"
+ "=============================================================\n"
+ "===\n");
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+ struct sla_station_ref *station_ref;
+ char ring_timeout[16] = "(none)";
+ if (trunk->ring_timeout)
+ snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
+ ast_cli(a->fd, "=== ---------------------------------------------------------\n"
+ "=== Trunk Name: %s\n"
+ "=== ==> Device: %s\n"
+ "=== ==> AutoContext: %s\n"
+ "=== ==> RingTimeout: %s\n"
+ "=== ==> BargeAllowed: %s\n"
+ "=== ==> HoldAccess: %s\n"
+ "=== ==> Stations ...\n",
+ trunk->name, trunk->device,
+ S_OR(trunk->autocontext, "(none)"),
+ ring_timeout,
+ trunk->barge_disabled ? "No" : "Yes",
+ sla_hold_str(trunk->hold_access));
+ AST_RWLIST_RDLOCK(&sla_stations);
+ AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
+ ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
+ AST_RWLIST_UNLOCK(&sla_stations);
+ ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
+ }
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ ast_cli(a->fd, "=============================================================\n\n");
+
+ return CLI_SUCCESS;
+}
+
+static const char *trunkstate2str(enum sla_trunk_state state)
+{
+#define S(e) case e: return # e;
+ switch (state) {
+ S(SLA_TRUNK_STATE_IDLE)
+ S(SLA_TRUNK_STATE_RINGING)
+ S(SLA_TRUNK_STATE_UP)
+ S(SLA_TRUNK_STATE_ONHOLD)
+ S(SLA_TRUNK_STATE_ONHOLD_BYME)
+ }
+ return "Uknown State";
+#undef S
+}
+
+static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ const struct sla_station *station;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sla show stations";
+ e->usage =
+ "Usage: sla show stations\n"
+ " This will list all stations defined in sla.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "\n"
+ "=============================================================\n"
+ "=== Configured SLA Stations =================================\n"
+ "=============================================================\n"
+ "===\n");
+ AST_RWLIST_RDLOCK(&sla_stations);
+ AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+ struct sla_trunk_ref *trunk_ref;
+ char ring_timeout[16] = "(none)";
+ char ring_delay[16] = "(none)";
+ if (station->ring_timeout) {
+ snprintf(ring_timeout, sizeof(ring_timeout),
+ "%u", station->ring_timeout);
+ }
+ if (station->ring_delay) {
+ snprintf(ring_delay, sizeof(ring_delay),
+ "%u", station->ring_delay);
+ }
+ ast_cli(a->fd, "=== ---------------------------------------------------------\n"
+ "=== Station Name: %s\n"
+ "=== ==> Device: %s\n"
+ "=== ==> AutoContext: %s\n"
+ "=== ==> RingTimeout: %s\n"
+ "=== ==> RingDelay: %s\n"
+ "=== ==> HoldAccess: %s\n"
+ "=== ==> Trunks ...\n",
+ station->name, station->device,
+ S_OR(station->autocontext, "(none)"),
+ ring_timeout, ring_delay,
+ sla_hold_str(station->hold_access));
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (trunk_ref->ring_timeout) {
+ snprintf(ring_timeout, sizeof(ring_timeout),
+ "%u", trunk_ref->ring_timeout);
+ } else
+ strcpy(ring_timeout, "(none)");
+ if (trunk_ref->ring_delay) {
+ snprintf(ring_delay, sizeof(ring_delay),
+ "%u", trunk_ref->ring_delay);
+ } else
+ strcpy(ring_delay, "(none)");
+ ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
+ "=== ==> State: %s\n"
+ "=== ==> RingTimeout: %s\n"
+ "=== ==> RingDelay: %s\n",
+ trunk_ref->trunk->name,
+ trunkstate2str(trunk_ref->state),
+ ring_timeout, ring_delay);
+ }
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ ast_cli(a->fd, "=== ---------------------------------------------------------\n"
+ "===\n");
+ }
+ AST_RWLIST_UNLOCK(&sla_stations);
+ ast_cli(a->fd, "============================================================\n"
+ "\n");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_meetme[] = {
+ AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
+ AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
+ AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
+};
+
+static void conf_flush(int fd, struct ast_channel *chan)
+{
+ int x;
+
+ /* read any frames that may be waiting on the channel
+ and throw them away
+ */
+ if (chan) {
+ struct ast_frame *f;
+
+ /* when no frames are available, this will wait
+ for 1 millisecond maximum
+ */
+ while (ast_waitfor(chan, 1)) {
+ f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+ else /* channel was hung up or something else happened */
+ break;
+ }
+ }
+
+ /* flush any data sitting in the pseudo channel */
+ x = ZT_FLUSH_ALL;
+ if (ioctl(fd, ZT_FLUSH, &x))
+ ast_log(LOG_WARNING, "Error flushing channel\n");
+
+}
+
+/* Remove the conference from the list and free it.
+ We assume that this was called while holding conflock. */
+static int conf_free(struct ast_conference *conf)
+{
+ int x;
+
+ AST_LIST_REMOVE(&confs, conf, list);
+ manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
+
+ if (conf->recording == MEETME_RECORD_ACTIVE) {
+ conf->recording = MEETME_RECORD_TERMINATE;
+ AST_LIST_UNLOCK(&confs);
+ while (1) {
+ usleep(1);
+ AST_LIST_LOCK(&confs);
+ if (conf->recording == MEETME_RECORD_OFF)
+ break;
+ AST_LIST_UNLOCK(&confs);
+ }
+ }
+
+ for (x = 0; x < AST_FRAME_BITS; x++) {
+ if (conf->transframe[x])
+ ast_frfree(conf->transframe[x]);
+ if (conf->transpath[x])
+ ast_translator_free_path(conf->transpath[x]);
+ }
+ if (conf->origframe)
+ ast_frfree(conf->origframe);
+ if (conf->lchan)
+ ast_hangup(conf->lchan);
+ if (conf->chan)
+ ast_hangup(conf->chan);
+ if (conf->fd >= 0)
+ close(conf->fd);
+
+ ast_mutex_destroy(&conf->playlock);
+ ast_mutex_destroy(&conf->listenlock);
+ ast_mutex_destroy(&conf->recordthreadlock);
+ ast_free(conf);
+
+ return 0;
+}
+
+static void conf_queue_dtmf(const struct ast_conference *conf,
+ const struct ast_conf_user *sender, struct ast_frame *f)
+{
+ struct ast_conf_user *user;
+
+ AST_LIST_TRAVERSE(&conf->userlist, user, list) {
+ if (user == sender)
+ continue;
+ if (ast_write(user->chan, f) < 0)
+ ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
+ }
+}
+
+static void sla_queue_event_full(enum sla_event_type type,
+ struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
+{
+ struct sla_event *event;
+
+ if (!(event = ast_calloc(1, sizeof(*event))))
+ return;
+
+ event->type = type;
+ event->trunk_ref = trunk_ref;
+ event->station = station;
+
+ if (!lock) {
+ AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
+ return;
+ }
+
+ ast_mutex_lock(&sla.lock);
+ AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
+ ast_cond_signal(&sla.cond);
+ ast_mutex_unlock(&sla.lock);
+}
+
+static void sla_queue_event_nolock(enum sla_event_type type)
+{
+ sla_queue_event_full(type, NULL, NULL, 0);
+}
+
+static void sla_queue_event(enum sla_event_type type)
+{
+ sla_queue_event_full(type, NULL, NULL, 1);
+}
+
+/*! \brief Queue a SLA event from the conference */
+static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
+ struct ast_conference *conf)
+{
+ struct sla_station *station;
+ struct sla_trunk_ref *trunk_ref = NULL;
+ char *trunk_name;
+
+ trunk_name = ast_strdupa(conf->confno);
+ strsep(&trunk_name, "_");
+ if (ast_strlen_zero(trunk_name)) {
+ ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
+ return;
+ }
+
+ AST_RWLIST_RDLOCK(&sla_stations);
+ AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
+ break;
+ }
+ if (trunk_ref)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&sla_stations);
+
+ if (!trunk_ref) {
+ ast_debug(1, "Trunk not found for event!\n");
+ return;
+ }
+
+ sla_queue_event_full(type, trunk_ref, station, 1);
+}
+
+/* Decrement reference counts, as incremented by find_conf() */
+static int dispose_conf(struct ast_conference *conf)
+{
+ int res = 0;
+ int confno_int = 0;
+
+ AST_LIST_LOCK(&confs);
+ if (ast_atomic_dec_and_test(&conf->refcount)) {
+ /* Take the conference room number out of an inuse state */
+ if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
+ conf_map[confno_int] = 0;
+ conf_free(conf);
+ res = 1;
+ }
+ AST_LIST_UNLOCK(&confs);
+
+ return res;
+}
+
+static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
+{
+ char *original_moh;
+
+ ast_channel_lock(chan);
+ original_moh = ast_strdupa(chan->musicclass);
+ ast_string_field_set(chan, musicclass, musicclass);
+ ast_channel_unlock(chan);
+
+ ast_moh_start(chan, original_moh, NULL);
+
+ ast_channel_lock(chan);
+ ast_string_field_set(chan, musicclass, original_moh);
+ ast_channel_unlock(chan);
+}
+
+static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
+{
+ struct ast_conf_user *user = NULL;
+ struct ast_conf_user *usr = NULL;
+ int fd;
+ struct zt_confinfo ztc, ztc_empty;
+ 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 musiconhold = 0;
+ int firstpass = 0;
+ int lastmarked = 0;
+ int currentmarked = 0;
+ int ret = -1;
+ int x;
+ int menu_active = 0;
+ int talkreq_manager = 0;
+ int using_pseudo = 0;
+ int duration = 20;
+ int hr, min, sec;
+ int sent_event = 0;
+ int checked = 0;
+ int announcement_played = 0;
+ struct timeval now;
+ struct ast_dsp *dsp = NULL;
+ struct ast_app *app;
+ const char *agifile;
+ const char *agifiledefault = "conf-background.agi";
+ char meetmesecs[30] = "";
+ char exitcontext[AST_MAX_CONTEXT] = "";
+ char recordingtmp[AST_MAX_EXTENSION] = "";
+ char members[10] = "";
+ int dtmf, opt_waitmarked_timeout = 0;
+ time_t timeout = 0;
+ ZT_BUFFERINFO bi;
+ char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
+ char *buf = __buf + AST_FRIENDLY_OFFSET;
+ char *exitkeys = NULL;
+ unsigned int calldurationlimit = 0;
+ long timelimit = 0;
+ long play_warning = 0;
+ long warning_freq = 0;
+ const char *warning_sound = NULL;
+ const char *end_sound = NULL;
+ char *parse;
+ long time_left_ms = 0;
+ struct timeval nexteventts = { 0, };
+ int to;
+ int setusercount = 0;
+
+ if (!(user = ast_calloc(1, sizeof(*user))))
+ return ret;
+
+ /* Possible timeout waiting for marked user */
+ if ((confflags & CONFFLAG_WAITMARKED) &&
+ !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
+ (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
+ (opt_waitmarked_timeout > 0)) {
+ timeout = time(NULL) + opt_waitmarked_timeout;
+ }
+
+ if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
+ calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
+ ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
+ }
+
+ if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
+ char *limit_str, *warning_str, *warnfreq_str;
+ const char *var;
+
+ parse = optargs[OPT_ARG_DURATION_LIMIT];
+ limit_str = strsep(&parse, ":");
+ warning_str = strsep(&parse, ":");
+ warnfreq_str = parse;
+
+ timelimit = atol(limit_str);
+ if (warning_str)
+ play_warning = atol(warning_str);
+ if (warnfreq_str)
+ warning_freq = atol(warnfreq_str);
+
+ if (!timelimit) {
+ timelimit = play_warning = warning_freq = 0;
+ warning_sound = NULL;
+ } else if (play_warning > timelimit) {
+ if (!warning_freq) {
+ play_warning = 0;
+ } else {
+ while (play_warning > timelimit)
+ play_warning -= warning_freq;
+ if (play_warning < 1)
+ play_warning = warning_freq = 0;
+ }
+ }
+
+ var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE");
+ warning_sound = var ? var : "timeleft";
+
+ var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE");
+ end_sound = var ? var : NULL;
+
+ /* undo effect of S(x) in case they are both used */
+ calldurationlimit = 0;
+ /* more efficient do it like S(x) does since no advanced opts */
+ if (!play_warning && !end_sound && timelimit) {
+ calldurationlimit = timelimit / 1000;
+ timelimit = play_warning = warning_freq = 0;
+ } else {
+ ast_debug(2, "Limit Data for this call:\n");
+ ast_debug(2, "- timelimit = %ld\n", timelimit);
+ ast_debug(2, "- play_warning = %ld\n", play_warning);
+ ast_debug(2, "- warning_freq = %ld\n", warning_freq);
+ ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
+ ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
+ }
+ }
+
+ /* Get exit keys */
+ if ((confflags & CONFFLAG_KEYEXIT)) {
+ if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
+ exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
+ else
+ exitkeys = ast_strdupa("#"); /* Default */
+ }
+
+ if (confflags & CONFFLAG_RECORDCONF) {
+ if (!conf->recordingfilename) {
+ conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
+ if (!conf->recordingfilename) {
+ snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
+ conf->recordingfilename = ast_strdupa(recordingtmp);
+ }
+ conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
+ if (!conf->recordingformat) {
+ ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
+ conf->recordingformat = ast_strdupa(recordingtmp);
+ }
+ ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
+ conf->confno, conf->recordingfilename, conf->recordingformat);
+ }
+ }
+
+ ast_mutex_lock(&conf->recordthreadlock);
+ if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
+ ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
+ ztc.chan = 0;
+ ztc.confno = conf->zapconf;
+ ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
+ if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error starting listen channel\n");
+ ast_hangup(conf->lchan);
+ conf->lchan = NULL;
+ } else {
+ ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
+ }
+ }
+ ast_mutex_unlock(&conf->recordthreadlock);
+
+ time(&user->jointime);
+
+ user->timelimit = timelimit;
+ user->play_warning = play_warning;
+ user->warning_freq = warning_freq;
+ user->warning_sound = warning_sound;
+ user->end_sound = end_sound;
+
+ if (calldurationlimit > 0) {
+ time(&user->kicktime);
+ user->kicktime = user->kicktime + calldurationlimit;
+ }
+
+ if (ast_tvzero(user->start_time))
+ user->start_time = ast_tvnow();
+ time_left_ms = user->timelimit;
+
+ if (user->timelimit) {
+ nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
+ nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
+ }
+
+ if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
+ /* Sorry, but this conference is locked! */
+ if (!ast_streamfile(chan, "conf-locked", chan->language))
+ ast_waitstream(chan, "");
+ goto outrun;
+ }
+
+ ast_mutex_lock(&conf->playlock);
+
+ if (AST_LIST_EMPTY(&conf->userlist))
+ user->user_no = 1;
+ else
+ user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
+
+ if (rt_schedule && conf->maxusers)
+ if (user->user_no > conf->maxusers) {
+ /* Sorry, but this confernce has reached the participant limit! */
+ if (!ast_streamfile(chan, "conf-full", chan->language))
+ ast_waitstream(chan, "");
+ goto outrun;
+ }
+
+ AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
+
+ user->chan = chan;
+ user->userflags = confflags;
+ user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
+ user->talking = -1;
+
+ ast_mutex_unlock(&conf->playlock);
+
+ if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
+ snprintf(user->namerecloc, sizeof(user->namerecloc),
+ "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
+ conf->confno, user->user_no);
+ if (confflags & CONFFLAG_INTROUSERNOREVIEW)
+ res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
+ else
+ res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
+ if (res == -1)
+ goto outrun;
+ }
+
+ ast_mutex_lock(&conf->playlock);
+
+ if (confflags & CONFFLAG_MARKEDUSER)
+ conf->markedusers++;
+ conf->users++;
+ if (rt_log_members) {
+ /* Update table */
+ snprintf(members, sizeof(members), "%d", conf->users);
+ ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
+ }
+ setusercount = 1;
+
+ /* This device changed state now - if this is the first user */
+ if (conf->users == 1)
+ ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
+
+ ast_mutex_unlock(&conf->playlock);
+
+ /* return the unique ID of the conference */
+ pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
+
+ if (confflags & CONFFLAG_EXIT_CONTEXT) {
+ if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
+ ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
+ else if (!ast_strlen_zero(chan->macrocontext))
+ ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
+ else
+ ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
+ }
+
+ if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
+ if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
+ if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
+ ast_waitstream(chan, "");
+ if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
+ if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
+ ast_waitstream(chan, "");
+ }
+
+ if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
+ int keepplaying = 1;
+
+ if (conf->users == 2) {
+ if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (res > 0)
+ keepplaying = 0;
+ else if (res == -1)
+ goto outrun;
+ }
+ } else {
+ if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (res > 0)
+ keepplaying = 0;
+ else if (res == -1)
+ goto outrun;
+ }
+ if (keepplaying) {
+ res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ if (res > 0)
+ keepplaying = 0;
+ else if (res == -1)
+ goto outrun;
+ }
+ if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (res > 0)
+ keepplaying = 0;
+ else if (res == -1)
+ goto outrun;
+ }
+ }
+ }
+
+ ast_indicate(chan, -1);
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
+ goto outrun;
+ }
+
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
+ goto outrun;
+ }
+
+ retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->audiohooks || chan->monitor) ? 1 : 0);
+ user->zapchannel = !retryzap;
+
+ zapretry:
+ origfd = chan->fds[0];
+ if (retryzap) {
+ fd = open("/dev/zap/pseudo", O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+ goto outrun;
+ }
+ using_pseudo = 1;
+ /* 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 / 2;
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = audio_buffers;
+ if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
+ ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ x = 1;
+ if (ioctl(fd, ZT_SETLINEAR, &x)) {
+ ast_log(LOG_WARNING, "Unable to set linear mode: %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));
+ memset(&ztc_empty, 0, sizeof(ztc_empty));
+ /* Check to see if we're in a conference... */
+ ztc.chan = 0;
+ if (ioctl(fd, ZT_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_debug(1, "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 = conf->zapconf;
+
+ ast_mutex_lock(&conf->playlock);
+
+ if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
+ if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
+ if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
+ ast_waitstream(conf->chan, "");
+ if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
+ ast_waitstream(conf->chan, "");
+ }
+ }
+
+ if (confflags & CONFFLAG_MONITOR)
+ ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+ else if (confflags & CONFFLAG_TALKER)
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+ else
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ ast_mutex_unlock(&conf->playlock);
+ goto outrun;
+ }
+ ast_debug(1, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
+
+ if (!sent_event) {
+ manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n"
+ "CallerIDnum: %s\r\n"
+ "CallerIDname: %s\r\n",
+ chan->name, chan->uniqueid, conf->confno,
+ user->user_no,
+ S_OR(user->chan->cid.cid_num, "<unknown>"),
+ S_OR(user->chan->cid.cid_name, "<unknown>")
+ );
+ sent_event = 1;
+ }
+
+ if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
+ firstpass = 1;
+ if (!(confflags & CONFFLAG_QUIET))
+ if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
+ conf_play(chan, conf, ENTER);
+ }
+
+ ast_mutex_unlock(&conf->playlock);
+
+ conf_flush(fd, chan);
+
+ if (confflags & CONFFLAG_AGI) {
+ /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
+ or use default filename of conf-background.agi */
+
+ agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
+ if (!agifile)
+ agifile = agifiledefault;
+
+ if (user->zapchannel) {
+ /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
+ x = 1;
+ ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
+ }
+ /* Find a pointer to the agi app and execute the script */
+ app = pbx_findapp("agi");
+ if (app) {
+ char *s = ast_strdupa(agifile);
+ ret = pbx_exec(chan, app, s);
+ } else {
+ ast_log(LOG_WARNING, "Could not find application (agi)\n");
+ ret = -2;
+ }
+ if (user->zapchannel) {
+ /* Remove CONFMUTE mode on Zap channel */
+ x = 0;
+ ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
+ }
+ } else {
+ if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
+ /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
+ x = 1;
+ ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
+ }
+ if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
+ ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
+ res = -1;
+ }
+ for (;;) {
+ int menu_was_active = 0;
+
+ outfd = -1;
+ ms = -1;
+ now = ast_tvnow();
+
+ if (rt_schedule) {
+ if (now.tv_sec % 60 == 0) {
+ if (!checked) {
+ if (now.tv_sec > conf->endtime) {
+ ast_verbose("Quitting time...\n");
+ goto outrun;
+ }
+
+ if (!announcement_played && conf->endalert) {
+ if (now.tv_sec + conf->endalert > conf->endtime) {
+ if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
+ ast_waitstream(chan, "");
+ ast_say_digits(chan, (now.tv_sec + conf->endalert - conf->endtime) / 60, "", chan->language);
+ if (!ast_streamfile(chan, "minutes", chan->language))
+ ast_waitstream(chan, "");
+ announcement_played = 1;
+ }
+ }
+ checked = 1;
+
+ }
+ } else {
+ checked = 0;
+ }
+ }
+
+ if (user->kicktime && (user->kicktime <= now.tv_sec))
+ break;
+
+ to = -1;
+ if (user->timelimit) {
+ int minutes = 0, seconds = 0, remain = 0;
+
+ to = ast_tvdiff_ms(nexteventts, now);
+ if (to < 0)
+ to = 0;
+ time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
+ if (time_left_ms < to)
+ to = time_left_ms;
+
+ if (time_left_ms <= 0) {
+ if (user->end_sound) {
+ res = ast_streamfile(chan, user->end_sound, chan->language);
+ res = ast_waitstream(chan, "");
+ }
+ break;
+ }
+
+ if (!to) {
+ if (time_left_ms >= 5000) {
+
+ remain = (time_left_ms + 500) / 1000;
+ if (remain / 60 >= 1) {
+ minutes = remain / 60;
+ seconds = remain % 60;
+ } else {
+ seconds = remain;
+ }
+
+ /* force the time left to round up if appropriate */
+ if (user->warning_sound && user->play_warning) {
+ if (!strcmp(user->warning_sound, "timeleft")) {
+
+ res = ast_streamfile(chan, "vm-youhave", chan->language);
+ res = ast_waitstream(chan, "");
+ if (minutes) {
+ res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ res = ast_streamfile(chan, "queue-minutes", chan->language);
+ res = ast_waitstream(chan, "");
+ }
+ if (seconds) {
+ res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ res = ast_streamfile(chan, "queue-seconds", chan->language);
+ res = ast_waitstream(chan, "");
+ }
+ } else {
+ res = ast_streamfile(chan, user->warning_sound, chan->language);
+ res = ast_waitstream(chan, "");
+ }
+ }
+ }
+ if (user->warning_freq)
+ nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
+ else
+ nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
+ }
+ }
+
+ now = ast_tvnow();
+ if (timeout && now.tv_sec >= timeout)
+ break;
+
+ /* if we have just exited from the menu, and the user had a channel-driver
+ volume adjustment, restore it
+ */
+ if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
+ set_talk_volume(user, user->listen.desired);
+
+ menu_was_active = menu_active;
+
+ currentmarked = conf->markedusers;
+ if (!(confflags & CONFFLAG_QUIET) &&
+ (confflags & CONFFLAG_MARKEDUSER) &&
+ (confflags & CONFFLAG_WAITMARKED) &&
+ lastmarked == 0) {
+ if (currentmarked == 1 && conf->users > 1) {
+ ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ if (conf->users - 1 == 1) {
+ if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
+ ast_waitstream(chan, "");
+ }
+ }
+ if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
+ if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
+ ast_waitstream(chan, "");
+ }
+
+ c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+
+ /* Update the struct with the actual confflags */
+ user->userflags = confflags;
+
+ if (confflags & CONFFLAG_WAITMARKED) {
+ if (currentmarked == 0) {
+ if (lastmarked != 0) {
+ if (!(confflags & CONFFLAG_QUIET))
+ if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
+ ast_waitstream(chan, "");
+ if (confflags & CONFFLAG_MARKEDEXIT) {
+ if (confflags & CONFFLAG_KICK_CONTINUE)
+ ret = 0;
+ break;
+ } else {
+ ztc.confmode = ZT_CONF_CONF;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ }
+ }
+ if (!musiconhold && (confflags & CONFFLAG_MOH)) {
+ conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
+ musiconhold = 1;
+ }
+ } else if (currentmarked >= 1 && lastmarked == 0) {
+ /* Marked user entered, so cancel timeout */
+ timeout = 0;
+ if (confflags & CONFFLAG_MONITOR)
+ ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+ else if (confflags & CONFFLAG_TALKER)
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+ else
+ ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ if (musiconhold && (confflags & CONFFLAG_MOH)) {
+ ast_moh_stop(chan);
+ musiconhold = 0;
+ }
+ if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
+ if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
+ ast_waitstream(chan, "");
+ conf_play(chan, conf, ENTER);
+ }
+ }
+ }
+
+ /* trying to add moh for single person conf */
+ if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
+ if (conf->users == 1) {
+ if (!musiconhold) {
+ conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
+ musiconhold = 1;
+ }
+ } else {
+ if (musiconhold) {
+ ast_moh_stop(chan);
+ musiconhold = 0;
+ }
+ }
+ }
+
+ /* Leave if the last marked user left */
+ if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
+ if (confflags & CONFFLAG_KICK_CONTINUE)
+ ret = 0;
+ else
+ ret = -1;
+ break;
+ }
+
+ /* Check if my modes have changed */
+
+ /* If I should be muted but am still talker, mute me */
+ if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
+ ztc.confmode ^= ZT_CONF_TALKER;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+ ret = -1;
+ break;
+ }
+
+ manager_event(EVENT_FLAG_CALL, "MeetmeMute",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %i\r\n"
+ "Status: on\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+
+ /* If I should be un-muted but am not talker, un-mute me */
+ if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
+ ztc.confmode |= ZT_CONF_TALKER;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+ ret = -1;
+ break;
+ }
+
+ manager_event(EVENT_FLAG_CALL, "MeetmeMute",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %i\r\n"
+ "Status: off\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+
+ if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
+ (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
+ talkreq_manager = 1;
+
+ manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %i\r\n"
+ "Status: on\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+
+
+ if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
+ !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
+ talkreq_manager = 0;
+ manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %i\r\n"
+ "Status: off\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+
+ /* If I have been kicked, exit the conference */
+ if (user->adminflags & ADMINFLAG_KICKME) {
+ /* You have been kicked. */
+ if (!(confflags & CONFFLAG_QUIET) &&
+ !ast_streamfile(chan, "conf-kicked", chan->language)) {
+ ast_waitstream(chan, "");
+ }
+ ret = 0;
+ break;
+ }
+
+ /* Perform an extra hangup check just in case */
+ if (ast_check_hangup(chan))
+ break;
+
+ if (c) {
+ if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
+ if (using_pseudo) {
+ /* Kill old pseudo */
+ close(fd);
+ using_pseudo = 0;
+ }
+ ast_debug(1, "Ooh, something swapped out under us, starting over\n");
+ retryzap = (strcasecmp(c->tech->type, "Zap") || (c->audiohooks || c->monitor) ? 1 : 0);
+ user->zapchannel = !retryzap;
+ goto zapretry;
+ }
+ if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
+ f = ast_read_noaudio(c);
+ else
+ f = ast_read(c);
+ if (!f)
+ break;
+ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
+ if (user->talk.actual)
+ ast_frame_adjust_volume(f, user->talk.actual);
+
+ if (!(confflags & CONFFLAG_MONITOR)) {
+ int totalsilence;
+
+ if (user->talking == -1)
+ user->talking = 0;
+
+ res = ast_dsp_silence(dsp, f, &totalsilence);
+ if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
+ user->talking = 1;
+ if (confflags & CONFFLAG_MONITORTALKER)
+ manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n"
+ "Status: on\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+ if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
+ user->talking = 0;
+ if (confflags & CONFFLAG_MONITORTALKER)
+ manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n"
+ "Status: off\r\n",
+ chan->name, chan->uniqueid, conf->confno, user->user_no);
+ }
+ }
+ if (using_pseudo) {
+ /* Absolutely do _not_ use careful_write here...
+ it is important that we read data from the channel
+ as fast as it arrives, and feed it into the conference.
+ The buffering in the pseudo channel will take care of any
+ timing differences, unless they are so drastic as to lose
+ audio frames (in which case carefully writing would only
+ have delayed the audio even further).
+ */
+ /* As it turns out, we do want to use careful write. We just
+ don't want to block, but we do want to at least *try*
+ to write out all the samples.
+ */
+ if (user->talking)
+ careful_write(fd, f->data, f->datalen, 0);
+ }
+ } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
+ char tmp[2];
+
+ if (confflags & CONFFLAG_PASS_DTMF)
+ conf_queue_dtmf(conf, user, f);
+
+ tmp[0] = f->subclass;
+ tmp[1] = '\0';
+ if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
+ ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
+ ret = 0;
+ ast_frfree(f);
+ break;
+ } else {
+ ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
+ }
+ } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
+ char exitkey[2];
+
+ exitkey[0] = f->subclass;
+ exitkey[1] = '\0';
+
+ pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", exitkey);
+
+ if (confflags & CONFFLAG_PASS_DTMF)
+ conf_queue_dtmf(conf, user, f);
+ ret = 0;
+ ast_frfree(f);
+ break;
+ } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
+ if (confflags & CONFFLAG_PASS_DTMF)
+ conf_queue_dtmf(conf, user, f);
+ if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ ast_frfree(f);
+ goto outrun;
+ }
+
+ /* if we are entering the menu, and the user has a channel-driver
+ volume adjustment, clear it
+ */
+ if (!menu_active && user->talk.desired && !user->talk.actual)
+ set_talk_volume(user, 0);
+
+ if (musiconhold) {
+ ast_moh_stop(chan);
+ }
+ if ((confflags & CONFFLAG_ADMIN)) {
+ /* Admin menu */
+ if (!menu_active) {
+ menu_active = 1;
+ /* Record this sound! */
+ if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
+ dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ } else
+ dtmf = 0;
+ } else
+ dtmf = f->subclass;
+ if (dtmf) {
+ switch(dtmf) {
+ case '1': /* Un/Mute */
+ menu_active = 0;
+
+ /* for admin, change both admin and use flags */
+ if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
+ user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
+ else
+ user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
+
+ if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
+ if (!ast_streamfile(chan, "conf-muted", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ if (!ast_streamfile(chan, "conf-unmuted", chan->language))
+ ast_waitstream(chan, "");
+ }
+ break;
+ case '2': /* Un/Lock the Conference */
+ menu_active = 0;
+ if (conf->locked) {
+ conf->locked = 0;
+ if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ conf->locked = 1;
+ if (!ast_streamfile(chan, "conf-lockednow", chan->language))
+ ast_waitstream(chan, "");
+ }
+ break;
+ case '3': /* Eject last user */
+ menu_active = 0;
+ usr = AST_LIST_LAST(&conf->userlist);
+ if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
+ if (!ast_streamfile(chan, "conf-errormenu", chan->language))
+ ast_waitstream(chan, "");
+ } else
+ usr->adminflags |= ADMINFLAG_KICKME;
+ ast_stopstream(chan);
+ break;
+ case '4':
+ tweak_listen_volume(user, VOL_DOWN);
+ break;
+ case '6':
+ tweak_listen_volume(user, VOL_UP);
+ break;
+ case '7':
+ tweak_talk_volume(user, VOL_DOWN);
+ break;
+ case '8':
+ menu_active = 0;
+ break;
+ case '9':
+ tweak_talk_volume(user, VOL_UP);
+ break;
+ default:
+ menu_active = 0;
+ /* Play an error message! */
+ if (!ast_streamfile(chan, "conf-errormenu", chan->language))
+ ast_waitstream(chan, "");
+ break;
+ }
+ }
+ } else {
+ /* User menu */
+ if (!menu_active) {
+ menu_active = 1;
+ if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
+ dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ } else
+ dtmf = 0;
+ } else
+ dtmf = f->subclass;
+ if (dtmf) {
+ switch(dtmf) {
+ case '1': /* Un/Mute */
+ menu_active = 0;
+
+ /* user can only toggle the self-muted state */
+ user->adminflags ^= ADMINFLAG_SELFMUTED;
+
+ /* they can't override the admin mute state */
+ if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
+ if (!ast_streamfile(chan, "conf-muted", chan->language))
+ ast_waitstream(chan, "");
+ } else {
+ if (!ast_streamfile(chan, "conf-unmuted", chan->language))
+ ast_waitstream(chan, "");
+ }
+ break;
+ case '2':
+ menu_active = 0;
+ if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
+ user->adminflags |= ADMINFLAG_T_REQUEST;
+
+ if (user->adminflags & ADMINFLAG_T_REQUEST)
+ if (!ast_streamfile(chan, "beep", chan->language))
+ ast_waitstream(chan, "");
+ break;
+ case '4':
+ tweak_listen_volume(user, VOL_DOWN);
+ break;
+ case '6':
+ tweak_listen_volume(user, VOL_UP);
+ break;
+ case '7':
+ tweak_talk_volume(user, VOL_DOWN);
+ break;
+ case '8':
+ menu_active = 0;
+ break;
+ case '9':
+ tweak_talk_volume(user, VOL_UP);
+ break;
+ default:
+ menu_active = 0;
+ if (!ast_streamfile(chan, "conf-errormenu", chan->language))
+ ast_waitstream(chan, "");
+ break;
+ }
+ }
+ }
+ if (musiconhold)
+ conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ ast_frfree(f);
+ goto outrun;
+ }
+
+ conf_flush(fd, chan);
+ } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
+ && confflags & CONFFLAG_PASS_DTMF) {
+ conf_queue_dtmf(conf, user, f);
+ } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
+ switch (f->subclass) {
+ case AST_CONTROL_HOLD:
+ sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
+ break;
+ default:
+ break;
+ }
+ } else if (f->frametype == AST_FRAME_NULL) {
+ /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
+ } else {
+ ast_debug(1,
+ "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
+ chan->name, f->frametype, 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_SLINEAR;
+ fr.datalen = res;
+ fr.samples = res / 2;
+ fr.data = buf;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ if (!user->listen.actual &&
+ ((confflags & CONFFLAG_MONITOR) ||
+ (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
+ (!user->talking)) ) {
+ int index;
+ for (index = 0; index < AST_FRAME_BITS; index++)
+ if (chan->rawwriteformat & (1 << index))
+ break;
+ if (index >= AST_FRAME_BITS)
+ goto bailoutandtrynormal;
+ ast_mutex_lock(&conf->listenlock);
+ if (!conf->transframe[index]) {
+ if (conf->origframe) {
+ if (!conf->transpath[index])
+ conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
+ if (conf->transpath[index]) {
+ conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
+ if (!conf->transframe[index])
+ conf->transframe[index] = &ast_null_frame;
+ }
+ }
+ }
+ if (conf->transframe[index]) {
+ if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
+ if (ast_write(chan, conf->transframe[index]))
+ ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
+ }
+ } else {
+ ast_mutex_unlock(&conf->listenlock);
+ goto bailoutandtrynormal;
+ }
+ ast_mutex_unlock(&conf->listenlock);
+ } else {
+bailoutandtrynormal:
+ if (user->listen.actual)
+ ast_frame_adjust_volume(&fr, user->listen.actual);
+ if (ast_write(chan, &fr) < 0) {
+ ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
+ }
+ lastmarked = currentmarked;
+ }
+ }
+
+ if (musiconhold)
+ ast_moh_stop(chan);
+
+ if (using_pseudo)
+ close(fd);
+ else {
+ /* Take out of conference */
+ ztc.chan = 0;
+ ztc.confno = 0;
+ ztc.confmode = 0;
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ }
+ }
+
+ reset_volumes(user);
+
+ AST_LIST_LOCK(&confs);
+ if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
+ conf_play(chan, conf, LEAVE);
+
+ if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
+ if (ast_fileexists(user->namerecloc, NULL, NULL)) {
+ if ((conf->chan) && (conf->users > 1)) {
+ if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
+ ast_waitstream(conf->chan, "");
+ if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
+ ast_waitstream(conf->chan, "");
+ }
+ ast_filedelete(user->namerecloc, NULL);
+ }
+ }
+ AST_LIST_UNLOCK(&confs);
+
+ outrun:
+ AST_LIST_LOCK(&confs);
+
+ if (dsp)
+ ast_dsp_free(dsp);
+
+ if (user->user_no) { /* Only cleanup users who really joined! */
+ now = ast_tvnow();
+ hr = (now.tv_sec - user->jointime) / 3600;
+ min = ((now.tv_sec - user->jointime) % 3600) / 60;
+ sec = (now.tv_sec - user->jointime) % 60;
+
+ if (sent_event) {
+ manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Meetme: %s\r\n"
+ "Usernum: %d\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Duration: %ld\r\n",
+ chan->name, chan->uniqueid, conf->confno,
+ user->user_no,
+ S_OR(user->chan->cid.cid_num, "<unknown>"),
+ S_OR(user->chan->cid.cid_name, "<unknown>"),
+ (long)(now.tv_sec - user->jointime));
+ }
+
+ if (setusercount) {
+ conf->users--;
+ if (rt_log_members) {
+ /* Update table */
+ snprintf(members, sizeof(members), "%d", conf->users);
+ ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
+ }
+ if (confflags & CONFFLAG_MARKEDUSER)
+ conf->markedusers--;
+ }
+ /* Remove ourselves from the list */
+ AST_LIST_REMOVE(&conf->userlist, user, list);
+
+ /* Change any states */
+ if (!conf->users)
+ ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
+
+ /* Return the number of seconds the user was in the conf */
+ snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
+ pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
+ }
+ ast_free(user);
+ AST_LIST_UNLOCK(&confs);
+
+ return ret;
+}
+
+static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
+ char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
+ char *optargs[], int *too_early)
+{
+ struct ast_variable *var;
+ struct ast_conference *cnf;
+
+ *too_early = 0;
+
+ /* Check first in the conference list */
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (!strcmp(confno, cnf->confno))
+ break;
+ }
+ if (cnf) {
+ cnf->refcount += refcount;
+ }
+ AST_LIST_UNLOCK(&confs);
+
+ if (!cnf) {
+ char *pin = NULL, *pinadmin = NULL; /* For temp use */
+ int maxusers = 0;
+ struct timeval now;
+ char currenttime[19] = "";
+ char eatime[19] = "";
+ char useropts[32] = "";
+ char adminopts[32] = "";
+ struct ast_tm tm, etm;
+ struct timeval starttime = { .tv_sec = 0 }, endtime = { .tv_sec = 0 };
+
+ if (rt_schedule) {
+ now = ast_tvnow();
+
+ if (fuzzystart)
+ now.tv_sec += fuzzystart;
+
+ ast_localtime(&now, &tm, NULL);
+ ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
+
+ if (earlyalert) {
+ now.tv_sec += earlyalert;
+ ast_localtime(&now, &etm, NULL);
+ ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
+ } else {
+ ast_copy_string(eatime, currenttime, sizeof(eatime));
+ }
+
+ ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
+
+ var = ast_load_realtime("meetme", "confno",
+ confno, "starttime <= ", eatime, "endtime >= ",
+ currenttime, NULL);
+ } else
+ var = ast_load_realtime("meetme", "confno", confno, NULL);
+
+ if (!var)
+ return NULL;
+
+ while (var) {
+ if (!strcasecmp(var->name, "pin")) {
+ pin = ast_strdupa(var->value);
+ } else if (!strcasecmp(var->name, "adminpin")) {
+ pinadmin = ast_strdupa(var->value);
+ } else if (!strcasecmp(var->name, "opts")) {
+ ast_copy_string(useropts, var->value, sizeof(useropts));
+ } else if (!strcasecmp(var->name, "maxusers")) {
+ maxusers = atoi(var->value);
+ } else if (!strcasecmp(var->name, "adminopts")) {
+ ast_copy_string(adminopts, var->value, sizeof(adminopts));
+ } else if (!strcasecmp(var->name, "endtime")) {
+ union {
+ struct ast_tm atm;
+ struct tm tm;
+ } t = { { 0, }, };
+ strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
+ endtime = ast_mktime(&t.atm, NULL);
+ } else if (!strcasecmp(var->name, "starttime")) {
+ union {
+ struct ast_tm atm;
+ struct tm tm;
+ } t = { { 0, }, };
+ strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
+ starttime = ast_mktime(&t.atm, NULL);
+ }
+
+ var = var->next;
+ }
+ ast_variables_destroy(var);
+
+ if (earlyalert) {
+ now = ast_tvnow();
+
+ if (now.tv_sec + fuzzystart < starttime.tv_sec) {
+ /* Announce that the caller is early and exit */
+ if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
+ ast_waitstream(chan, "");
+ *too_early = 1;
+ return NULL;
+ }
+ }
+
+ cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
+
+ if (cnf) {
+ cnf->maxusers = maxusers;
+ cnf->endalert = endalert;
+ cnf->endtime = endtime.tv_sec;
+ }
+ }
+
+ if (cnf) {
+ if (confflags && !cnf->chan &&
+ !ast_test_flag(confflags, CONFFLAG_QUIET) &&
+ ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
+ ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
+ ast_clear_flag(confflags, CONFFLAG_INTROUSER);
+ }
+
+ if (confflags && !cnf->chan &&
+ ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
+ ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
+ ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
+ }
+ }
+
+ return cnf;
+}
+
+
+static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
+ char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ struct ast_flags config_flags = { 0 };
+ struct ast_conference *cnf;
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(confno);
+ AST_APP_ARG(pin);
+ AST_APP_ARG(pinadmin);
+ );
+
+ /* Check first in the conference list */
+ ast_debug(1, "The requested confno is '%s'?\n", confno);
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
+ if (!strcmp(confno, cnf->confno))
+ break;
+ }
+ if (cnf) {
+ cnf->refcount += refcount;
+ }
+ AST_LIST_UNLOCK(&confs);
+
+ if (!cnf) {
+ if (dynamic) {
+ /* No need to parse meetme.conf */
+ ast_debug(1, "Building dynamic conference '%s'\n", confno);
+ if (dynamic_pin) {
+ if (dynamic_pin[0] == 'q') {
+ /* Query the user to enter a PIN */
+ if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
+ return NULL;
+ }
+ cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
+ } else {
+ cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
+ }
+ } else {
+ /* Check the config */
+ cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
+ return NULL;
+ }
+ for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
+ if (strcasecmp(var->name, "conf"))
+ continue;
+
+ if (!(parse = ast_strdupa(var->value)))
+ return NULL;
+
+ AST_STANDARD_APP_ARGS(args, parse);
+ ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
+ if (!strcasecmp(args.confno, confno)) {
+ /* Bingo it's a valid conference */
+ cnf = build_conf(args.confno,
+ S_OR(args.pin, ""),
+ S_OR(args.pinadmin, ""),
+ make, dynamic, refcount, chan);
+ break;
+ }
+ }
+ if (!var) {
+ ast_debug(1, "%s isn't a valid conference\n", confno);
+ }
+ ast_config_destroy(cfg);
+ }
+ } else if (dynamic_pin) {
+ /* Correct for the user selecting 'D' instead of 'd' to have
+ someone join into a conference that has already been created
+ with a pin. */
+ if (dynamic_pin[0] == 'q')
+ dynamic_pin[0] = '\0';
+ }
+
+ if (cnf) {
+ if (confflags && !cnf->chan &&
+ !ast_test_flag(confflags, CONFFLAG_QUIET) &&
+ ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
+ ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
+ ast_clear_flag(confflags, CONFFLAG_INTROUSER);
+ }
+
+ if (confflags && !cnf->chan &&
+ ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
+ ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
+ ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
+ }
+ }
+
+ return cnf;
+}
+
+/*! \brief The MeetmeCount application */
+static int count_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_conference *conf;
+ int count;
+ char *localdata;
+ char val[80] = "0";
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(confno);
+ AST_APP_ARG(varname);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
+ return -1;
+ }
+
+ if (!(localdata = ast_strdupa(data)))
+ return -1;
+
+ AST_STANDARD_APP_ARGS(args, localdata);
+
+ conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
+
+ if (conf) {
+ count = conf->users;
+ dispose_conf(conf);
+ conf = NULL;
+ } else
+ count = 0;
+
+ if (!ast_strlen_zero(args.varname)) {
+ /* have var so load it and exit */
+ snprintf(val, sizeof(val), "%d", count);
+ pbx_builtin_setvar_helper(chan, args.varname, val);
+ } else {
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+ res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
+ }
+
+ return res;
+}
+
+/*! \brief The meetme() application */
+static int conf_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ char confno[MAX_CONFNUM] = "";
+ int allowretry = 0;
+ int retrycnt = 0;
+ struct ast_conference *cnf = NULL;
+ struct ast_flags confflags = {0}, config_flags = { 0 };
+ int dynamic = 0;
+ int empty = 0, empty_no_pin = 0;
+ int always_prompt = 0;
+ char *notdata, *info, the_pin[MAX_PIN] = "";
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(confno);
+ AST_APP_ARG(options);
+ AST_APP_ARG(pin);
+ );
+ char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
+
+ if (ast_strlen_zero(data)) {
+ allowretry = 1;
+ notdata = "";
+ } else {
+ notdata = data;
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ info = ast_strdupa(notdata);
+
+ AST_STANDARD_APP_ARGS(args, info);
+
+ if (args.confno) {
+ ast_copy_string(confno, args.confno, sizeof(confno));
+ if (ast_strlen_zero(confno)) {
+ allowretry = 1;
+ }
+ }
+
+ if (args.pin)
+ ast_copy_string(the_pin, args.pin, sizeof(the_pin));
+
+ if (args.options) {
+ ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
+ dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
+ if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
+ strcpy(the_pin, "q");
+
+ empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
+ empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
+ always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
+ }
+
+ do {
+ if (retrycnt > 3)
+ allowretry = 0;
+ if (empty) {
+ int i;
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ int confno_int;
+
+ /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
+ if ((empty_no_pin) || (!dynamic)) {
+ cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
+ if (cfg) {
+ var = ast_variable_browse(cfg, "rooms");
+ while (var) {
+ if (!strcasecmp(var->name, "conf")) {
+ char *stringp = ast_strdupa(var->value);
+ if (stringp) {
+ char *confno_tmp = strsep(&stringp, "|,");
+ int found = 0;
+ if (!dynamic) {
+ /* For static: run through the list and see if this conference is empty */
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (!strcmp(confno_tmp, cnf->confno)) {
+ /* The conference exists, therefore it's not empty */
+ found = 1;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&confs);
+ if (!found) {
+ /* At this point, we have a confno_tmp (static conference) that is empty */
+ if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
+ /* Case 1: empty_no_pin and pin is nonexistent (NULL)
+ * Case 2: empty_no_pin and pin is blank (but not NULL)
+ * Case 3: not empty_no_pin
+ */
+ ast_copy_string(confno, confno_tmp, sizeof(confno));
+ break;
+ /* XXX the map is not complete (but we do have a confno) */
+ }
+ }
+ }
+ }
+ }
+ var = var->next;
+ }
+ ast_config_destroy(cfg);
+ }
+ }
+
+ /* Select first conference number not in use */
+ if (ast_strlen_zero(confno) && dynamic) {
+ AST_LIST_LOCK(&confs);
+ for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
+ if (!conf_map[i]) {
+ snprintf(confno, sizeof(confno), "%d", i);
+ conf_map[i] = 1;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&confs);
+ }
+
+ /* Not found? */
+ if (ast_strlen_zero(confno)) {
+ res = ast_streamfile(chan, "conf-noempty", chan->language);
+ if (!res)
+ ast_waitstream(chan, "");
+ } else {
+ if (sscanf(confno, "%d", &confno_int) == 1) {
+ if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
+ res = ast_streamfile(chan, "conf-enteringno", chan->language);
+ if (!res) {
+ ast_waitstream(chan, "");
+ res = ast_say_digits(chan, confno_int, "", chan->language);
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
+ }
+ }
+ }
+
+ while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
+ /* Prompt user for conference number */
+ res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
+ if (res < 0) {
+ /* Don't try to validate when we catch an error */
+ confno[0] = '\0';
+ allowretry = 0;
+ break;
+ }
+ }
+ if (!ast_strlen_zero(confno)) {
+ /* Check the validity of the conference */
+ cnf = find_conf(chan, confno, 1, dynamic, the_pin,
+ sizeof(the_pin), 1, &confflags);
+ if (!cnf) {
+ int too_early = 0;
+ cnf = find_conf_realtime(chan, confno, 1, dynamic,
+ the_pin, sizeof(the_pin), 1, &confflags, optargs, &too_early);
+ if (rt_schedule && too_early)
+ allowretry = 0;
+ }
+
+ if (!cnf) {
+ if (allowretry) {
+ confno[0] = '\0';
+ res = ast_streamfile(chan, "conf-invalid", chan->language);
+ if (!res)
+ ast_waitstream(chan, "");
+ res = -1;
+ }
+ } else {
+ if ((!ast_strlen_zero(cnf->pin) &&
+ !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
+ (!ast_strlen_zero(cnf->pinadmin) &&
+ ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
+ char pin[MAX_PIN] = "";
+ int j;
+
+ /* Allow the pin to be retried up to 3 times */
+ for (j = 0; j < 3; j++) {
+ if (*the_pin && (always_prompt == 0)) {
+ ast_copy_string(pin, the_pin, sizeof(pin));
+ res = 0;
+ } else {
+ /* Prompt user for pin if pin is required */
+ res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
+ }
+ if (res >= 0) {
+ if (!strcasecmp(pin, cnf->pin) ||
+ (!ast_strlen_zero(cnf->pinadmin) &&
+ !strcasecmp(pin, cnf->pinadmin))) {
+ /* Pin correct */
+ allowretry = 0;
+ if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
+ ast_set_flag(&confflags, CONFFLAG_ADMIN);
+ /* Run the conference */
+ res = conf_run(chan, cnf, confflags.flags, optargs);
+ break;
+ } else {
+ /* Pin invalid */
+ if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ }
+ else {
+ ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
+ break;
+ }
+ if (res < 0)
+ break;
+ pin[0] = res;
+ pin[1] = '\0';
+ res = -1;
+ if (allowretry)
+ confno[0] = '\0';
+ }
+ } else {
+ /* failed when getting the pin */
+ res = -1;
+ allowretry = 0;
+ /* see if we need to get rid of the conference */
+ break;
+ }
+
+ /* Don't retry pin with a static pin */
+ if (*the_pin && (always_prompt == 0)) {
+ break;
+ }
+ }
+ } else {
+ /* No pin required */
+ allowretry = 0;
+
+ /* Run the conference */
+ res = conf_run(chan, cnf, confflags.flags, optargs);
+ }
+ dispose_conf(cnf);
+ cnf = NULL;
+ }
+ }
+ } while (allowretry);
+
+ if (cnf)
+ dispose_conf(cnf);
+
+ return res;
+}
+
+static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
+{
+ struct ast_conf_user *user = NULL;
+ int cid;
+
+ sscanf(callerident, "%i", &cid);
+ if (conf && callerident) {
+ AST_LIST_TRAVERSE(&conf->userlist, user, list) {
+ if (cid == user->user_no)
+ return user;
+ }
+ }
+ return NULL;
+}
+
+/*! \brief The MeetMeadmin application */
+/* MeetMeAdmin(confno, command, caller) */
+static int admin_exec(struct ast_channel *chan, void *data) {
+ char *params;
+ struct ast_conference *cnf;
+ struct ast_conf_user *user = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(confno);
+ AST_APP_ARG(command);
+ AST_APP_ARG(user);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
+ return -1;
+ }
+
+ params = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, params);
+
+ if (!args.command) {
+ ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
+ return -1;
+ }
+
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ if (!strcmp(cnf->confno, args.confno))
+ break;
+ }
+
+ if (!cnf) {
+ ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
+ AST_LIST_UNLOCK(&confs);
+ return 0;
+ }
+
+ ast_atomic_fetchadd_int(&cnf->refcount, 1);
+
+ if (args.user)
+ user = find_user(cnf, args.user);
+
+ switch (*args.command) {
+ case 76: /* L: Lock */
+ cnf->locked = 1;
+ break;
+ case 108: /* l: Unlock */
+ cnf->locked = 0;
+ break;
+ case 75: /* K: kick all users */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list)
+ user->adminflags |= ADMINFLAG_KICKME;
+ break;
+ case 101: /* e: Eject last user*/
+ user = AST_LIST_LAST(&cnf->userlist);
+ if (!(user->userflags & CONFFLAG_ADMIN))
+ user->adminflags |= ADMINFLAG_KICKME;
+ else
+ ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
+ break;
+ case 77: /* M: Mute */
+ if (user) {
+ user->adminflags |= ADMINFLAG_MUTED;
+ } else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ case 78: /* N: Mute all (non-admin) users */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
+ if (!(user->userflags & CONFFLAG_ADMIN))
+ user->adminflags |= ADMINFLAG_MUTED;
+ }
+ break;
+ case 109: /* m: Unmute */
+ if (user) {
+ user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
+ } else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ case 110: /* n: Unmute all users */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list)
+ user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
+ break;
+ case 107: /* k: Kick user */
+ if (user)
+ user->adminflags |= ADMINFLAG_KICKME;
+ else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ case 118: /* v: Lower all users listen volume */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list)
+ tweak_listen_volume(user, VOL_DOWN);
+ break;
+ case 86: /* V: Raise all users listen volume */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list)
+ tweak_listen_volume(user, VOL_UP);
+ break;
+ case 115: /* s: Lower all users speaking volume */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list)
+ tweak_talk_volume(user, VOL_DOWN);
+ break;
+ case 83: /* S: Raise all users speaking volume */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list)
+ tweak_talk_volume(user, VOL_UP);
+ break;
+ case 82: /* R: Reset all volume levels */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list)
+ reset_volumes(user);
+ break;
+ case 114: /* r: Reset user's volume level */
+ if (user)
+ reset_volumes(user);
+ else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ case 85: /* U: Raise user's listen volume */
+ if (user)
+ tweak_listen_volume(user, VOL_UP);
+ else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ case 117: /* u: Lower user's listen volume */
+ if (user)
+ tweak_listen_volume(user, VOL_DOWN);
+ else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ case 84: /* T: Raise user's talk volume */
+ if (user)
+ tweak_talk_volume(user, VOL_UP);
+ else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ case 116: /* t: Lower user's talk volume */
+ if (user)
+ tweak_talk_volume(user, VOL_DOWN);
+ else
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ break;
+ }
+
+ AST_LIST_UNLOCK(&confs);
+
+ dispose_conf(cnf);
+
+ return 0;
+}
+
+/*--- channel_admin_exec: The MeetMeChannelAdmin application */
+/* MeetMeChannelAdmin(channel, command) */
+static int channel_admin_exec(struct ast_channel *chan, void *data) {
+ char *params;
+ struct ast_conference *conf = NULL;
+ struct ast_conf_user *user = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(channel);
+ AST_APP_ARG(command);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
+ return -1;
+ }
+
+ params = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, params);
+
+ if (!args.channel) {
+ ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
+ return -1;
+ }
+
+ if (!args.command) {
+ ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
+ return -1;
+ }
+
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, conf, list) {
+ AST_LIST_TRAVERSE(&conf->userlist, user, list) {
+ if (!strcmp(user->chan->name, args.channel))
+ break;
+ }
+ }
+
+ if (!user) {
+ ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
+ AST_LIST_UNLOCK(&confs);
+ return 0;
+ }
+
+ /* perform the specified action */
+ switch (*args.command) {
+ case 77: /* M: Mute */
+ user->adminflags |= ADMINFLAG_MUTED;
+ break;
+ case 109: /* m: Unmute */
+ user->adminflags &= ~ADMINFLAG_MUTED;
+ break;
+ case 107: /* k: Kick user */
+ user->adminflags |= ADMINFLAG_KICKME;
+ break;
+ default: /* unknown command */
+ ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
+ break;
+ }
+
+ AST_LIST_UNLOCK(&confs);
+
+ return 0;
+}
+
+static int meetmemute(struct mansession *s, const struct message *m, int mute)
+{
+ struct ast_conference *conf;
+ struct ast_conf_user *user;
+ const char *confid = astman_get_header(m, "Meetme");
+ char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
+ int userno;
+
+ if (ast_strlen_zero(confid)) {
+ astman_send_error(s, m, "Meetme conference not specified");
+ return 0;
+ }
+
+ if (ast_strlen_zero(userid)) {
+ astman_send_error(s, m, "Meetme user number not specified");
+ return 0;
+ }
+
+ userno = strtoul(userid, &userid, 10);
+
+ if (*userid) {
+ astman_send_error(s, m, "Invalid user number");
+ return 0;
+ }
+
+ /* Look in the conference list */
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, conf, list) {
+ if (!strcmp(confid, conf->confno))
+ break;
+ }
+
+ if (!conf) {
+ AST_LIST_UNLOCK(&confs);
+ astman_send_error(s, m, "Meetme conference does not exist");
+ return 0;
+ }
+
+ AST_LIST_TRAVERSE(&conf->userlist, user, list)
+ if (user->user_no == userno)
+ break;
+
+ if (!user) {
+ AST_LIST_UNLOCK(&confs);
+ astman_send_error(s, m, "User number not found");
+ return 0;
+ }
+
+ if (mute)
+ user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
+ else
+ user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
+
+ AST_LIST_UNLOCK(&confs);
+
+ ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, user->chan->name, user->chan->uniqueid);
+
+ astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
+ return 0;
+}
+
+static int action_meetmemute(struct mansession *s, const struct message *m)
+{
+ return meetmemute(s, m, 1);
+}
+
+static int action_meetmeunmute(struct mansession *s, const struct message *m)
+{
+ return meetmemute(s, m, 0);
+}
+
+static char mandescr_meetmelist[] =
+"Description: Lists all users in a particular MeetMe conference.\n"
+"MeetmeList will follow as separate events, followed by a final event called\n"
+"MeetmeListComplete.\n"
+"Variables:\n"
+" *ActionId: <id>\n"
+" *Conference: <confno>\n";
+
+static int action_meetmelist(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ const char *conference = astman_get_header(m, "Conference");
+ char idText[80] = "";
+ struct ast_conference *cnf;
+ struct ast_conf_user *user;
+ int total = 0;
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
+
+ if (AST_LIST_EMPTY(&confs)) {
+ astman_send_error(s, m, "No active conferences.");
+ return 0;
+ }
+
+ astman_send_listack(s, m, "Meetme user list will follow", "start");
+
+ /* Find the right conference */
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, cnf, list) {
+ /* If we ask for one particular, and this isn't it, skip it */
+ if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
+ continue;
+
+ /* Show all the users */
+ AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
+ total++;
+ astman_append(s,
+ "Event: MeetmeList\r\n"
+ "%s"
+ "Conference: %s\r\n"
+ "UserNumber: %d\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Channel: %s\r\n"
+ "Admin: %s\r\n"
+ "Role: %s\r\n"
+ "MarkedUser: %s\r\n"
+ "Muted: %s\r\n"
+ "Talking: %s\r\n"
+ "\r\n",
+ idText,
+ cnf->confno,
+ user->user_no,
+ S_OR(user->chan->cid.cid_num, "<unknown>"),
+ S_OR(user->chan->cid.cid_name, "<no name>"),
+ user->chan->name,
+ user->userflags & CONFFLAG_ADMIN ? "Yes" : "No",
+ user->userflags & CONFFLAG_MONITOR ? "Listen only" : user->userflags & CONFFLAG_TALKER ? "Talk only" : "Talk and listen",
+ user->userflags & CONFFLAG_MARKEDUSER ? "Yes" : "No",
+ user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
+ user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
+ }
+ }
+ AST_LIST_UNLOCK(&confs);
+ /* Send final confirmation */
+ astman_append(s,
+ "Event: MeetmeListComplete\r\n"
+ "EventList: Complete\r\n"
+ "ListItems: %d\r\n"
+ "%s"
+ "\r\n", total, idText);
+ return 0;
+}
+
+static void *recordthread(void *args)
+{
+ struct ast_conference *cnf = args;
+ struct ast_frame *f = NULL;
+ int flags;
+ struct ast_filestream *s = NULL;
+ int res = 0;
+ int x;
+ const char *oldrecordingfilename = NULL;
+
+ if (!cnf || !cnf->lchan) {
+ pthread_exit(0);
+ }
+
+ ast_stopstream(cnf->lchan);
+ flags = O_CREAT | O_TRUNC | O_WRONLY;
+
+
+ cnf->recording = MEETME_RECORD_ACTIVE;
+ while (ast_waitfor(cnf->lchan, -1) > -1) {
+ if (cnf->recording == MEETME_RECORD_TERMINATE) {
+ AST_LIST_LOCK(&confs);
+ AST_LIST_UNLOCK(&confs);
+ break;
+ }
+ if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
+ s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
+ oldrecordingfilename = cnf->recordingfilename;
+ }
+
+ f = ast_read(cnf->lchan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ ast_mutex_lock(&cnf->listenlock);
+ for (x = 0; x < AST_FRAME_BITS; x++) {
+ /* Free any translations that have occured */
+ if (cnf->transframe[x]) {
+ ast_frfree(cnf->transframe[x]);
+ cnf->transframe[x] = NULL;
+ }
+ }
+ if (cnf->origframe)
+ ast_frfree(cnf->origframe);
+ cnf->origframe = ast_frdup(f);
+ ast_mutex_unlock(&cnf->listenlock);
+ if (s)
+ res = ast_writestream(s, f);
+ if (res) {
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ }
+ cnf->recording = MEETME_RECORD_OFF;
+ if (s)
+ ast_closestream(s);
+
+ pthread_exit(0);
+}
+
+/*! \brief Callback for devicestate providers */
+static enum ast_device_state meetmestate(const char *data)
+{
+ struct ast_conference *conf;
+
+ /* Find conference */
+ AST_LIST_LOCK(&confs);
+ AST_LIST_TRAVERSE(&confs, conf, list) {
+ if (!strcmp(data, conf->confno))
+ break;
+ }
+ AST_LIST_UNLOCK(&confs);
+ if (!conf)
+ return AST_DEVICE_INVALID;
+
+
+ /* SKREP to fill */
+ if (!conf->users)
+ return AST_DEVICE_NOT_INUSE;
+
+ return AST_DEVICE_INUSE;
+}
+
+static void load_config_meetme(void)
+{
+ struct ast_config *cfg;
+ struct ast_flags config_flags = { 0 };
+ const char *val;
+
+ if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags)))
+ return;
+
+ audio_buffers = DEFAULT_AUDIO_BUFFERS;
+
+ /* Scheduling support is off by default */
+ rt_schedule = 0;
+ fuzzystart = 0;
+ earlyalert = 0;
+ endalert = 0;
+
+ /* Logging of participants defaults to ON for compatibility reasons */
+ rt_log_members = 1;
+
+ if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
+ if ((sscanf(val, "%d", &audio_buffers) != 1)) {
+ ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
+ audio_buffers = DEFAULT_AUDIO_BUFFERS;
+ } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
+ ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
+ ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
+ audio_buffers = DEFAULT_AUDIO_BUFFERS;
+ }
+ if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
+ ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
+ }
+
+ if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
+ rt_schedule = ast_true(val);
+ if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
+ rt_log_members = ast_true(val);
+ if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
+ if ((sscanf(val, "%d", &fuzzystart) != 1)) {
+ ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
+ fuzzystart = 0;
+ }
+ }
+ if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
+ if ((sscanf(val, "%d", &earlyalert) != 1)) {
+ ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
+ earlyalert = 0;
+ }
+ }
+ if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
+ if ((sscanf(val, "%d", &endalert) != 1)) {
+ ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
+ endalert = 0;
+ }
+ }
+
+ ast_config_destroy(cfg);
+}
+
+/*! \brief Find an SLA trunk by name
+ * \note This must be called with the sla_trunks container locked
+ */
+static struct sla_trunk *sla_find_trunk(const char *name)
+{
+ struct sla_trunk *trunk = NULL;
+
+ AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+ if (!strcasecmp(trunk->name, name))
+ break;
+ }
+
+ return trunk;
+}
+
+/*! \brief Find an SLA station by name
+ * \note This must be called with the sla_stations container locked
+ */
+static struct sla_station *sla_find_station(const char *name)
+{
+ struct sla_station *station = NULL;
+
+ AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+ if (!strcasecmp(station->name, name))
+ break;
+ }
+
+ return station;
+}
+
+static int sla_check_station_hold_access(const struct sla_trunk *trunk,
+ const struct sla_station *station)
+{
+ struct sla_station_ref *station_ref;
+ struct sla_trunk_ref *trunk_ref;
+
+ /* For each station that has this call on hold, check for private hold. */
+ AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+ AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
+ if (trunk_ref->trunk != trunk || station_ref->station == station)
+ continue;
+ if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
+ station_ref->station->hold_access == SLA_HOLD_PRIVATE)
+ return 1;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Find a trunk reference on a station by name
+ * \param station the station
+ * \param name the trunk's name
+ * \return a pointer to the station's trunk reference. If the trunk
+ * is not found, it is not idle and barge is disabled, or if
+ * it is on hold and private hold is set, then NULL will be returned.
+ */
+static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
+ const char *name)
+{
+ struct sla_trunk_ref *trunk_ref = NULL;
+
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (strcasecmp(trunk_ref->trunk->name, name))
+ continue;
+
+ if ( (trunk_ref->trunk->barge_disabled
+ && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
+ (trunk_ref->trunk->hold_stations
+ && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
+ && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
+ sla_check_station_hold_access(trunk_ref->trunk, station) )
+ {
+ trunk_ref = NULL;
+ }
+
+ break;
+ }
+
+ return trunk_ref;
+}
+
+static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
+{
+ struct sla_station_ref *station_ref;
+
+ if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
+ return NULL;
+
+ station_ref->station = station;
+
+ return station_ref;
+}
+
+static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
+{
+ struct sla_ringing_station *ringing_station;
+
+ if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
+ return NULL;
+
+ ringing_station->station = station;
+ ringing_station->ring_begin = ast_tvnow();
+
+ return ringing_station;
+}
+
+static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
+{
+ switch (state) {
+ case SLA_TRUNK_STATE_IDLE:
+ return AST_DEVICE_NOT_INUSE;
+ case SLA_TRUNK_STATE_RINGING:
+ return AST_DEVICE_RINGING;
+ case SLA_TRUNK_STATE_UP:
+ return AST_DEVICE_INUSE;
+ case SLA_TRUNK_STATE_ONHOLD:
+ case SLA_TRUNK_STATE_ONHOLD_BYME:
+ return AST_DEVICE_ONHOLD;
+ }
+
+ return AST_DEVICE_UNKNOWN;
+}
+
+static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
+ enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
+{
+ struct sla_station *station;
+ struct sla_trunk_ref *trunk_ref;
+
+ AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
+ || trunk_ref == exclude)
+ continue;
+ trunk_ref->state = state;
+ ast_devstate_changed(sla_state_to_devstate(state),
+ "SLA:%s_%s", station->name, trunk->name);
+ break;
+ }
+ }
+}
+
+struct run_station_args {
+ struct sla_station *station;
+ struct sla_trunk_ref *trunk_ref;
+ ast_mutex_t *cond_lock;
+ ast_cond_t *cond;
+};
+
+static void *run_station(void *data)
+{
+ struct sla_station *station;
+ struct sla_trunk_ref *trunk_ref;
+ char conf_name[MAX_CONFNUM];
+ struct ast_flags conf_flags = { 0 };
+ struct ast_conference *conf;
+
+ {
+ struct run_station_args *args = data;
+ station = args->station;
+ trunk_ref = args->trunk_ref;
+ ast_mutex_lock(args->cond_lock);
+ ast_cond_signal(args->cond);
+ ast_mutex_unlock(args->cond_lock);
+ /* args is no longer valid here. */
+ }
+
+ ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
+ snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
+ ast_set_flag(&conf_flags,
+ CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
+ ast_answer(trunk_ref->chan);
+ conf = build_conf(conf_name, "", "", 0, 0, 1, trunk_ref->chan);
+ if (conf) {
+ conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
+ dispose_conf(conf);
+ conf = NULL;
+ }
+ trunk_ref->chan = NULL;
+ if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
+ trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
+ strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
+ admin_exec(NULL, conf_name);
+ trunk_ref->trunk->hold_stations = 0;
+ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
+ }
+
+ ast_dial_join(station->dial);
+ ast_dial_destroy(station->dial);
+ station->dial = NULL;
+
+ return NULL;
+}
+
+static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
+{
+ char buf[80];
+ struct sla_station_ref *station_ref;
+
+ snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
+ admin_exec(NULL, buf);
+ sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
+
+ while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
+ ast_free(station_ref);
+
+ ast_free(ringing_trunk);
+}
+
+static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
+ enum sla_station_hangup hangup)
+{
+ struct sla_ringing_trunk *ringing_trunk;
+ struct sla_trunk_ref *trunk_ref;
+ struct sla_station_ref *station_ref;
+
+ ast_dial_join(ringing_station->station->dial);
+ ast_dial_destroy(ringing_station->station->dial);
+ ringing_station->station->dial = NULL;
+
+ if (hangup == SLA_STATION_HANGUP_NORMAL)
+ goto done;
+
+ /* If the station is being hung up because of a timeout, then add it to the
+ * list of timed out stations on each of the ringing trunks. This is so
+ * that when doing further processing to figure out which stations should be
+ * ringing, which trunk to answer, determining timeouts, etc., we know which
+ * ringing trunks we should ignore. */
+ AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
+ AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
+ if (ringing_trunk->trunk == trunk_ref->trunk)
+ break;
+ }
+ if (!trunk_ref)
+ continue;
+ if (!(station_ref = sla_create_station_ref(ringing_station->station)))
+ continue;
+ AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
+ }
+
+done:
+ ast_free(ringing_station);
+}
+
+static void sla_dial_state_callback(struct ast_dial *dial)
+{
+ sla_queue_event(SLA_EVENT_DIAL_STATE);
+}
+
+/*! \brief Check to see if dialing this station already timed out for this ringing trunk
+ * \note Assumes sla.lock is locked
+ */
+static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
+ const struct sla_station *station)
+{
+ struct sla_station_ref *timed_out_station;
+
+ AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
+ if (station == timed_out_station->station)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*! \brief Choose the highest priority ringing trunk for a station
+ * \param station the station
+ * \param remove remove the ringing trunk once selected
+ * \param trunk_ref a place to store the pointer to this stations reference to
+ * the selected trunk
+ * \return a pointer to the selected ringing trunk, or NULL if none found
+ * \note Assumes that sla.lock is locked
+ */
+static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
+ struct sla_trunk_ref **trunk_ref, int remove)
+{
+ struct sla_trunk_ref *s_trunk_ref;
+ struct sla_ringing_trunk *ringing_trunk = NULL;
+
+ AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
+ /* Make sure this is the trunk we're looking for */
+ if (s_trunk_ref->trunk != ringing_trunk->trunk)
+ continue;
+
+ /* This trunk on the station is ringing. But, make sure this station
+ * didn't already time out while this trunk was ringing. */
+ if (sla_check_timed_out_station(ringing_trunk, station))
+ continue;
+
+ if (remove)
+ AST_LIST_REMOVE_CURRENT(entry);
+
+ if (trunk_ref)
+ *trunk_ref = s_trunk_ref;
+
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (ringing_trunk)
+ break;
+ }
+
+ return ringing_trunk;
+}
+
+static void sla_handle_dial_state_event(void)
+{
+ struct sla_ringing_station *ringing_station;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
+ struct sla_trunk_ref *s_trunk_ref = NULL;
+ struct sla_ringing_trunk *ringing_trunk = NULL;
+ struct run_station_args args;
+ enum ast_dial_result dial_res;
+ pthread_t dont_care;
+ ast_mutex_t cond_lock;
+ ast_cond_t cond;
+
+ switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
+ case AST_DIAL_RESULT_HANGUP:
+ case AST_DIAL_RESULT_INVALID:
+ case AST_DIAL_RESULT_FAILED:
+ case AST_DIAL_RESULT_TIMEOUT:
+ case AST_DIAL_RESULT_UNANSWERED:
+ AST_LIST_REMOVE_CURRENT(entry);
+ sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
+ break;
+ case AST_DIAL_RESULT_ANSWERED:
+ AST_LIST_REMOVE_CURRENT(entry);
+ /* Find the appropriate trunk to answer. */
+ ast_mutex_lock(&sla.lock);
+ ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
+ ast_mutex_unlock(&sla.lock);
+ if (!ringing_trunk) {
+ ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
+ break;
+ }
+ /* Track the channel that answered this trunk */
+ s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
+ /* Actually answer the trunk */
+ ast_answer(ringing_trunk->trunk->chan);
+ sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
+ /* Now, start a thread that will connect this station to the trunk. The rest of
+ * the code here sets up the thread and ensures that it is able to save the arguments
+ * before they are no longer valid since they are allocated on the stack. */
+ args.trunk_ref = s_trunk_ref;
+ args.station = ringing_station->station;
+ args.cond = &cond;
+ args.cond_lock = &cond_lock;
+ ast_free(ringing_trunk);
+ ast_free(ringing_station);
+ ast_mutex_init(&cond_lock);
+ ast_cond_init(&cond, NULL);
+ ast_mutex_lock(&cond_lock);
+ ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
+ ast_cond_wait(&cond, &cond_lock);
+ ast_mutex_unlock(&cond_lock);
+ ast_mutex_destroy(&cond_lock);
+ ast_cond_destroy(&cond);
+ break;
+ case AST_DIAL_RESULT_TRYING:
+ case AST_DIAL_RESULT_RINGING:
+ case AST_DIAL_RESULT_PROGRESS:
+ case AST_DIAL_RESULT_PROCEEDING:
+ break;
+ }
+ if (dial_res == AST_DIAL_RESULT_ANSWERED) {
+ /* Queue up reprocessing ringing trunks, and then ringing stations again */
+ sla_queue_event(SLA_EVENT_RINGING_TRUNK);
+ sla_queue_event(SLA_EVENT_DIAL_STATE);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+}
+
+/*! \brief Check to see if this station is already ringing
+ * \note Assumes sla.lock is locked
+ */
+static int sla_check_ringing_station(const struct sla_station *station)
+{
+ struct sla_ringing_station *ringing_station;
+
+ AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
+ if (station == ringing_station->station)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*! \brief Check to see if this station has failed to be dialed in the past minute
+ * \note assumes sla.lock is locked
+ */
+static int sla_check_failed_station(const struct sla_station *station)
+{
+ struct sla_failed_station *failed_station;
+ int res = 0;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
+ if (station != failed_station->station)
+ continue;
+ if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_free(failed_station);
+ break;
+ }
+ res = 1;
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ return res;
+}
+
+/*! \brief Ring a station
+ * \note Assumes sla.lock is locked
+ */
+static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
+{
+ char *tech, *tech_data;
+ struct ast_dial *dial;
+ struct sla_ringing_station *ringing_station;
+ const char *cid_name = NULL, *cid_num = NULL;
+ enum ast_dial_result res;
+
+ if (!(dial = ast_dial_create()))
+ return -1;
+
+ ast_dial_set_state_callback(dial, sla_dial_state_callback);
+ tech_data = ast_strdupa(station->device);
+ tech = strsep(&tech_data, "/");
+
+ if (ast_dial_append(dial, tech, tech_data) == -1) {
+ ast_dial_destroy(dial);
+ return -1;
+ }
+
+ if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
+ cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
+ ast_free(ringing_trunk->trunk->chan->cid.cid_name);
+ ringing_trunk->trunk->chan->cid.cid_name = NULL;
+ }
+ if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
+ cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
+ ast_free(ringing_trunk->trunk->chan->cid.cid_num);
+ ringing_trunk->trunk->chan->cid.cid_num = NULL;
+ }
+
+ res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
+
+ if (cid_name)
+ ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
+ if (cid_num)
+ ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
+
+ if (res != AST_DIAL_RESULT_TRYING) {
+ struct sla_failed_station *failed_station;
+ ast_dial_destroy(dial);
+ if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
+ return -1;
+ failed_station->station = station;
+ failed_station->last_try = ast_tvnow();
+ AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
+ return -1;
+ }
+ if (!(ringing_station = sla_create_ringing_station(station))) {
+ ast_dial_join(dial);
+ ast_dial_destroy(dial);
+ return -1;
+ }
+
+ station->dial = dial;
+
+ AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
+
+ return 0;
+}
+
+/*! \brief Check to see if a station is in use
+ */
+static int sla_check_inuse_station(const struct sla_station *station)
+{
+ struct sla_trunk_ref *trunk_ref;
+
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (trunk_ref->chan)
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
+ const struct sla_trunk *trunk)
+{
+ struct sla_trunk_ref *trunk_ref = NULL;
+
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (trunk_ref->trunk == trunk)
+ break;
+ }
+
+ return trunk_ref;
+}
+
+/*! \brief Calculate the ring delay for a given ringing trunk on a station
+ * \param station the station
+ * \param ringing_trunk the trunk. If NULL, the highest priority ringing trunk will be used
+ * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
+ */
+static int sla_check_station_delay(struct sla_station *station,
+ struct sla_ringing_trunk *ringing_trunk)
+{
+ struct sla_trunk_ref *trunk_ref;
+ unsigned int delay = UINT_MAX;
+ int time_left, time_elapsed;
+
+ if (!ringing_trunk)
+ ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
+ else
+ trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
+
+ if (!ringing_trunk || !trunk_ref)
+ return delay;
+
+ /* If this station has a ring delay specific to the highest priority
+ * ringing trunk, use that. Otherwise, use the ring delay specified
+ * globally for the station. */
+ delay = trunk_ref->ring_delay;
+ if (!delay)
+ delay = station->ring_delay;
+ if (!delay)
+ return INT_MAX;
+
+ time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
+ time_left = (delay * 1000) - time_elapsed;
+
+ return time_left;
+}
+
+/*! \brief Ring stations based on current set of ringing trunks
+ * \note Assumes that sla.lock is locked
+ */
+static void sla_ring_stations(void)
+{
+ struct sla_station_ref *station_ref;
+ struct sla_ringing_trunk *ringing_trunk;
+
+ /* Make sure that every station that uses at least one of the ringing
+ * trunks, is ringing. */
+ AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
+ AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
+ int time_left;
+
+ /* Is this station already ringing? */
+ if (sla_check_ringing_station(station_ref->station))
+ continue;
+
+ /* Is this station already in a call? */
+ if (sla_check_inuse_station(station_ref->station))
+ continue;
+
+ /* Did we fail to dial this station earlier? If so, has it been
+ * a minute since we tried? */
+ if (sla_check_failed_station(station_ref->station))
+ continue;
+
+ /* If this station already timed out while this trunk was ringing,
+ * do not dial it again for this ringing trunk. */
+ if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
+ continue;
+
+ /* Check for a ring delay in progress */
+ time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
+ if (time_left != INT_MAX && time_left > 0)
+ continue;
+
+ /* It is time to make this station begin to ring. Do it! */
+ sla_ring_station(ringing_trunk, station_ref->station);
+ }
+ }
+ /* Now, all of the stations that should be ringing, are ringing. */
+}
+
+static void sla_hangup_stations(void)
+{
+ struct sla_trunk_ref *trunk_ref;
+ struct sla_ringing_station *ringing_station;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
+ AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
+ struct sla_ringing_trunk *ringing_trunk;
+ ast_mutex_lock(&sla.lock);
+ AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
+ if (trunk_ref->trunk == ringing_trunk->trunk)
+ break;
+ }
+ ast_mutex_unlock(&sla.lock);
+ if (ringing_trunk)
+ break;
+ }
+ if (!trunk_ref) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_dial_join(ringing_station->station->dial);
+ ast_dial_destroy(ringing_station->station->dial);
+ ringing_station->station->dial = NULL;
+ ast_free(ringing_station);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+}
+
+static void sla_handle_ringing_trunk_event(void)
+{
+ ast_mutex_lock(&sla.lock);
+ sla_ring_stations();
+ ast_mutex_unlock(&sla.lock);
+
+ /* Find stations that shouldn't be ringing anymore. */
+ sla_hangup_stations();
+}
+
+static void sla_handle_hold_event(struct sla_event *event)
+{
+ ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
+ event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
+ ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s",
+ event->station->name, event->trunk_ref->trunk->name);
+ sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
+ INACTIVE_TRUNK_REFS, event->trunk_ref);
+
+ if (event->trunk_ref->trunk->active_stations == 1) {
+ /* The station putting it on hold is the only one on the call, so start
+ * Music on hold to the trunk. */
+ event->trunk_ref->trunk->on_hold = 1;
+ ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
+ }
+
+ ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
+ event->trunk_ref->chan = NULL;
+}
+
+/*! \brief Process trunk ring timeouts
+ * \note Called with sla.lock locked
+ * \return non-zero if a change to the ringing trunks was made
+ */
+static int sla_calc_trunk_timeouts(unsigned int *timeout)
+{
+ struct sla_ringing_trunk *ringing_trunk;
+ int res = 0;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
+ int time_left, time_elapsed;
+ if (!ringing_trunk->trunk->ring_timeout)
+ continue;
+ time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
+ time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
+ if (time_left <= 0) {
+ pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
+ AST_LIST_REMOVE_CURRENT(entry);
+ sla_stop_ringing_trunk(ringing_trunk);
+ res = 1;
+ continue;
+ }
+ if (time_left < *timeout)
+ *timeout = time_left;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ return res;
+}
+
+/*! \brief Process station ring timeouts
+ * \note Called with sla.lock locked
+ * \return non-zero if a change to the ringing stations was made
+ */
+static int sla_calc_station_timeouts(unsigned int *timeout)
+{
+ struct sla_ringing_trunk *ringing_trunk;
+ struct sla_ringing_station *ringing_station;
+ int res = 0;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
+ unsigned int ring_timeout = 0;
+ int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
+ struct sla_trunk_ref *trunk_ref;
+
+ /* If there are any ring timeouts specified for a specific trunk
+ * on the station, then use the highest per-trunk ring timeout.
+ * Otherwise, use the ring timeout set for the entire station. */
+ AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
+ struct sla_station_ref *station_ref;
+ int trunk_time_elapsed, trunk_time_left;
+
+ AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
+ if (ringing_trunk->trunk == trunk_ref->trunk)
+ break;
+ }
+ if (!ringing_trunk)
+ continue;
+
+ /* If there is a trunk that is ringing without a timeout, then the
+ * only timeout that could matter is a global station ring timeout. */
+ if (!trunk_ref->ring_timeout)
+ break;
+
+ /* This trunk on this station is ringing and has a timeout.
+ * However, make sure this trunk isn't still ringing from a
+ * previous timeout. If so, don't consider it. */
+ AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
+ if (station_ref->station == ringing_station->station)
+ break;
+ }
+ if (station_ref)
+ continue;
+
+ trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
+ trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
+ if (trunk_time_left > final_trunk_time_left)
+ final_trunk_time_left = trunk_time_left;
+ }
+
+ /* No timeout was found for ringing trunks, and no timeout for the entire station */
+ if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
+ continue;
+
+ /* Compute how much time is left for a global station timeout */
+ if (ringing_station->station->ring_timeout) {
+ ring_timeout = ringing_station->station->ring_timeout;
+ time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
+ time_left = (ring_timeout * 1000) - time_elapsed;
+ }
+
+ /* If the time left based on the per-trunk timeouts is smaller than the
+ * global station ring timeout, use that. */
+ if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
+ time_left = final_trunk_time_left;
+
+ /* If there is no time left, the station needs to stop ringing */
+ if (time_left <= 0) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
+ res = 1;
+ continue;
+ }
+
+ /* There is still some time left for this station to ring, so save that
+ * timeout if it is the first event scheduled to occur */
+ if (time_left < *timeout)
+ *timeout = time_left;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ return res;
+}
+
+/*! \brief Calculate the ring delay for a station
+ * \note Assumes sla.lock is locked
+ */
+static int sla_calc_station_delays(unsigned int *timeout)
+{
+ struct sla_station *station;
+ int res = 0;
+
+ AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+ struct sla_ringing_trunk *ringing_trunk;
+ int time_left;
+
+ /* Ignore stations already ringing */
+ if (sla_check_ringing_station(station))
+ continue;
+
+ /* Ignore stations already on a call */
+ if (sla_check_inuse_station(station))
+ continue;
+
+ /* Ignore stations that don't have one of their trunks ringing */
+ if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
+ continue;
+
+ if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
+ continue;
+
+ /* If there is no time left, then the station needs to start ringing.
+ * Return non-zero so that an event will be queued up an event to
+ * make that happen. */
+ if (time_left <= 0) {
+ res = 1;
+ continue;
+ }
+
+ if (time_left < *timeout)
+ *timeout = time_left;
+ }
+
+ return res;
+}
+
+/*! \brief Calculate the time until the next known event
+ * \note Called with sla.lock locked */
+static int sla_process_timers(struct timespec *ts)
+{
+ unsigned int timeout = UINT_MAX;
+ struct timeval tv;
+ unsigned int change_made = 0;
+
+ /* Check for ring timeouts on ringing trunks */
+ if (sla_calc_trunk_timeouts(&timeout))
+ change_made = 1;
+
+ /* Check for ring timeouts on ringing stations */
+ if (sla_calc_station_timeouts(&timeout))
+ change_made = 1;
+
+ /* Check for station ring delays */
+ if (sla_calc_station_delays(&timeout))
+ change_made = 1;
+
+ /* queue reprocessing of ringing trunks */
+ if (change_made)
+ sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
+
+ /* No timeout */
+ if (timeout == UINT_MAX)
+ return 0;
+
+ if (ts) {
+ tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
+ }
+
+ return 1;
+}
+
+static int sla_load_config(int reload);
+
+/*! \brief Check if we can do a reload of SLA, and do it if we can */
+static void sla_check_reload(void)
+{
+ struct sla_station *station;
+ struct sla_trunk *trunk;
+
+ ast_mutex_lock(&sla.lock);
+
+ if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
+ || !AST_LIST_EMPTY(&sla.ringing_stations)) {
+ ast_mutex_unlock(&sla.lock);
+ return;
+ }
+
+ AST_RWLIST_RDLOCK(&sla_stations);
+ AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+ if (station->ref_count)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&sla_stations);
+ if (station) {
+ ast_mutex_unlock(&sla.lock);
+ return;
+ }
+
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+ if (trunk->ref_count)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ if (trunk) {
+ ast_mutex_unlock(&sla.lock);
+ return;
+ }
+
+ /* yay */
+ sla_load_config(1);
+ sla.reload = 0;
+
+ ast_mutex_unlock(&sla.lock);
+}
+
+static void *sla_thread(void *data)
+{
+ struct sla_failed_station *failed_station;
+ struct sla_ringing_station *ringing_station;
+
+ ast_mutex_lock(&sla.lock);
+
+ while (!sla.stop) {
+ struct sla_event *event;
+ struct timespec ts = { 0, };
+ unsigned int have_timeout = 0;
+
+ if (AST_LIST_EMPTY(&sla.event_q)) {
+ if ((have_timeout = sla_process_timers(&ts)))
+ ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
+ else
+ ast_cond_wait(&sla.cond, &sla.lock);
+ if (sla.stop)
+ break;
+ }
+
+ if (have_timeout)
+ sla_process_timers(NULL);
+
+ while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
+ ast_mutex_unlock(&sla.lock);
+ switch (event->type) {
+ case SLA_EVENT_HOLD:
+ sla_handle_hold_event(event);
+ break;
+ case SLA_EVENT_DIAL_STATE:
+ sla_handle_dial_state_event();
+ break;
+ case SLA_EVENT_RINGING_TRUNK:
+ sla_handle_ringing_trunk_event();
+ break;
+ case SLA_EVENT_RELOAD:
+ sla.reload = 1;
+ case SLA_EVENT_CHECK_RELOAD:
+ break;
+ }
+ ast_free(event);
+ ast_mutex_lock(&sla.lock);
+ }
+
+ if (sla.reload)
+ sla_check_reload();
+ }
+
+ ast_mutex_unlock(&sla.lock);
+
+ while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
+ ast_free(ringing_station);
+
+ while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
+ ast_free(failed_station);
+
+ return NULL;
+}
+
+struct dial_trunk_args {
+ struct sla_trunk_ref *trunk_ref;
+ struct sla_station *station;
+ ast_mutex_t *cond_lock;
+ ast_cond_t *cond;
+};
+
+static void *dial_trunk(void *data)
+{
+ struct dial_trunk_args *args = data;
+ struct ast_dial *dial;
+ char *tech, *tech_data;
+ enum ast_dial_result dial_res;
+ char conf_name[MAX_CONFNUM];
+ struct ast_conference *conf;
+ struct ast_flags conf_flags = { 0 };
+ struct sla_trunk_ref *trunk_ref = args->trunk_ref;
+ const char *cid_name = NULL, *cid_num = NULL;
+
+ if (!(dial = ast_dial_create())) {
+ ast_mutex_lock(args->cond_lock);
+ ast_cond_signal(args->cond);
+ ast_mutex_unlock(args->cond_lock);
+ return NULL;
+ }
+
+ tech_data = ast_strdupa(trunk_ref->trunk->device);
+ tech = strsep(&tech_data, "/");
+ if (ast_dial_append(dial, tech, tech_data) == -1) {
+ ast_mutex_lock(args->cond_lock);
+ ast_cond_signal(args->cond);
+ ast_mutex_unlock(args->cond_lock);
+ ast_dial_destroy(dial);
+ return NULL;
+ }
+
+ if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
+ cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
+ ast_free(trunk_ref->chan->cid.cid_name);
+ trunk_ref->chan->cid.cid_name = NULL;
+ }
+ if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
+ cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
+ ast_free(trunk_ref->chan->cid.cid_num);
+ trunk_ref->chan->cid.cid_num = NULL;
+ }
+
+ dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
+
+ if (cid_name)
+ trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
+ if (cid_num)
+ trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
+
+ if (dial_res != AST_DIAL_RESULT_TRYING) {
+ ast_mutex_lock(args->cond_lock);
+ ast_cond_signal(args->cond);
+ ast_mutex_unlock(args->cond_lock);
+ ast_dial_destroy(dial);
+ return NULL;
+ }
+
+ for (;;) {
+ unsigned int done = 0;
+ switch ((dial_res = ast_dial_state(dial))) {
+ case AST_DIAL_RESULT_ANSWERED:
+ trunk_ref->trunk->chan = ast_dial_answered(dial);
+ case AST_DIAL_RESULT_HANGUP:
+ case AST_DIAL_RESULT_INVALID:
+ case AST_DIAL_RESULT_FAILED:
+ case AST_DIAL_RESULT_TIMEOUT:
+ case AST_DIAL_RESULT_UNANSWERED:
+ done = 1;
+ case AST_DIAL_RESULT_TRYING:
+ case AST_DIAL_RESULT_RINGING:
+ case AST_DIAL_RESULT_PROGRESS:
+ case AST_DIAL_RESULT_PROCEEDING:
+ break;
+ }
+ if (done)
+ break;
+ }
+
+ if (!trunk_ref->trunk->chan) {
+ ast_mutex_lock(args->cond_lock);
+ ast_cond_signal(args->cond);
+ ast_mutex_unlock(args->cond_lock);
+ ast_dial_join(dial);
+ ast_dial_destroy(dial);
+ return NULL;
+ }
+
+ snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
+ ast_set_flag(&conf_flags,
+ CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
+ CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
+ conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan);
+
+ ast_mutex_lock(args->cond_lock);
+ ast_cond_signal(args->cond);
+ ast_mutex_unlock(args->cond_lock);
+
+ if (conf) {
+ conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
+ dispose_conf(conf);
+ conf = NULL;
+ }
+
+ /* If the trunk is going away, it is definitely now IDLE. */
+ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
+
+ trunk_ref->trunk->chan = NULL;
+ trunk_ref->trunk->on_hold = 0;
+
+ ast_dial_join(dial);
+ ast_dial_destroy(dial);
+
+ return NULL;
+}
+
+/*! \brief For a given station, choose the highest priority idle trunk
+ */
+static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
+{
+ struct sla_trunk_ref *trunk_ref = NULL;
+
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
+ break;
+ }
+
+ return trunk_ref;
+}
+
+static int sla_station_exec(struct ast_channel *chan, void *data)
+{
+ char *station_name, *trunk_name;
+ struct sla_station *station;
+ struct sla_trunk_ref *trunk_ref = NULL;
+ char conf_name[MAX_CONFNUM];
+ struct ast_flags conf_flags = { 0 };
+ struct ast_conference *conf;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
+ pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
+ return 0;
+ }
+
+ trunk_name = ast_strdupa(data);
+ station_name = strsep(&trunk_name, "_");
+
+ if (ast_strlen_zero(station_name)) {
+ ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
+ pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
+ return 0;
+ }
+
+ AST_RWLIST_RDLOCK(&sla_stations);
+ station = sla_find_station(station_name);
+ if (station)
+ ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
+ AST_RWLIST_UNLOCK(&sla_stations);
+
+ if (!station) {
+ ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
+ pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+ return 0;
+ }
+
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ if (!ast_strlen_zero(trunk_name)) {
+ trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
+ } else
+ trunk_ref = sla_choose_idle_trunk(station);
+ AST_RWLIST_UNLOCK(&sla_trunks);
+
+ if (!trunk_ref) {
+ if (ast_strlen_zero(trunk_name))
+ ast_log(LOG_NOTICE, "No trunks available for call.\n");
+ else {
+ ast_log(LOG_NOTICE, "Can't join existing call on trunk "
+ "'%s' due to access controls.\n", trunk_name);
+ }
+ pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
+ ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+ return 0;
+ }
+
+ if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
+ if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
+ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
+ else {
+ trunk_ref->state = SLA_TRUNK_STATE_UP;
+ ast_devstate_changed(AST_DEVICE_INUSE,
+ "SLA:%s_%s", station->name, trunk_ref->trunk->name);
+ }
+ } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
+ struct sla_ringing_trunk *ringing_trunk;
+
+ ast_mutex_lock(&sla.lock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
+ if (ringing_trunk->trunk == trunk_ref->trunk) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ ast_mutex_unlock(&sla.lock);
+
+ if (ringing_trunk) {
+ ast_answer(ringing_trunk->trunk->chan);
+ sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
+
+ free(ringing_trunk);
+
+ /* Queue up reprocessing ringing trunks, and then ringing stations again */
+ sla_queue_event(SLA_EVENT_RINGING_TRUNK);
+ sla_queue_event(SLA_EVENT_DIAL_STATE);
+ }
+ }
+
+ trunk_ref->chan = chan;
+
+ if (!trunk_ref->trunk->chan) {
+ ast_mutex_t cond_lock;
+ ast_cond_t cond;
+ pthread_t dont_care;
+ struct dial_trunk_args args = {
+ .trunk_ref = trunk_ref,
+ .station = station,
+ .cond_lock = &cond_lock,
+ .cond = &cond,
+ };
+ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
+ /* Create a thread to dial the trunk and dump it into the conference.
+ * However, we want to wait until the trunk has been dialed and the
+ * conference is created before continuing on here. */
+ ast_autoservice_start(chan);
+ ast_mutex_init(&cond_lock);
+ ast_cond_init(&cond, NULL);
+ ast_mutex_lock(&cond_lock);
+ ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
+ ast_cond_wait(&cond, &cond_lock);
+ ast_mutex_unlock(&cond_lock);
+ ast_mutex_destroy(&cond_lock);
+ ast_cond_destroy(&cond);
+ ast_autoservice_stop(chan);
+ if (!trunk_ref->trunk->chan) {
+ ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
+ pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
+ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
+ trunk_ref->chan = NULL;
+ ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+ return 0;
+ }
+ }
+
+ if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
+ trunk_ref->trunk->on_hold) {
+ trunk_ref->trunk->on_hold = 0;
+ ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
+ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
+ }
+
+ snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
+ ast_set_flag(&conf_flags,
+ CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
+ ast_answer(chan);
+ conf = build_conf(conf_name, "", "", 0, 0, 1, chan);
+ if (conf) {
+ conf_run(chan, conf, conf_flags.flags, NULL);
+ dispose_conf(conf);
+ conf = NULL;
+ }
+ trunk_ref->chan = NULL;
+ if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
+ trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
+ strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
+ admin_exec(NULL, conf_name);
+ trunk_ref->trunk->hold_stations = 0;
+ sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
+ }
+
+ pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
+
+ ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+
+ return 0;
+}
+
+static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
+{
+ struct sla_trunk_ref *trunk_ref;
+
+ if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
+ return NULL;
+
+ trunk_ref->trunk = trunk;
+
+ return trunk_ref;
+}
+
+static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
+{
+ struct sla_ringing_trunk *ringing_trunk;
+
+ if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
+ return NULL;
+
+ ringing_trunk->trunk = trunk;
+ ringing_trunk->ring_begin = ast_tvnow();
+
+ sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
+
+ ast_mutex_lock(&sla.lock);
+ AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
+ ast_mutex_unlock(&sla.lock);
+
+ sla_queue_event(SLA_EVENT_RINGING_TRUNK);
+
+ return ringing_trunk;
+}
+
+enum {
+ SLA_TRUNK_OPT_MOH = (1 << 0),
+};
+
+enum {
+ SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
+ SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
+};
+
+AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
+ AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
+END_OPTIONS );
+
+static int sla_trunk_exec(struct ast_channel *chan, void *data)
+{
+ char conf_name[MAX_CONFNUM];
+ struct ast_conference *conf;
+ struct ast_flags conf_flags = { 0 };
+ struct sla_trunk *trunk;
+ struct sla_ringing_trunk *ringing_trunk;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(trunk_name);
+ AST_APP_ARG(options);
+ );
+ char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
+ char *conf_opt_args[OPT_ARG_ARRAY_SIZE] = { NULL, };
+ struct ast_flags opt_flags = { 0 };
+ char *parse;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (args.argc == 2) {
+ if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
+ ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
+ return -1;
+ }
+ }
+
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ trunk = sla_find_trunk(args.trunk_name);
+ if (trunk)
+ ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
+ AST_RWLIST_UNLOCK(&sla_trunks);
+
+ if (!trunk) {
+ ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
+ pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
+ ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+ return 0;
+ }
+
+ if (trunk->chan) {
+ ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
+ args.trunk_name);
+ pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
+ ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+ return 0;
+ }
+
+ trunk->chan = chan;
+
+ if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
+ pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
+ ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+ return 0;
+ }
+
+ snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
+ conf = build_conf(conf_name, "", "", 1, 1, 1, chan);
+ if (!conf) {
+ pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
+ ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+ return 0;
+ }
+ ast_set_flag(&conf_flags,
+ CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF);
+
+ if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
+ ast_indicate(chan, -1);
+ ast_set_flag(&conf_flags, CONFFLAG_MOH);
+ conf_opt_args[OPT_ARG_MOH_CLASS] = opts[SLA_TRUNK_OPT_ARG_MOH_CLASS];
+ } else
+ ast_indicate(chan, AST_CONTROL_RINGING);
+
+ conf_run(chan, conf, conf_flags.flags, opts);
+ dispose_conf(conf);
+ conf = NULL;
+ trunk->chan = NULL;
+ trunk->on_hold = 0;
+
+ if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
+ pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
+
+ /* Remove the entry from the list of ringing trunks if it is still there. */
+ ast_mutex_lock(&sla.lock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
+ if (ringing_trunk->trunk == trunk) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ ast_mutex_unlock(&sla.lock);
+ if (ringing_trunk) {
+ sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
+ ast_free(ringing_trunk);
+ pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
+ /* Queue reprocessing of ringing trunks to make stations stop ringing
+ * that shouldn't be ringing after this trunk stopped. */
+ sla_queue_event(SLA_EVENT_RINGING_TRUNK);
+ }
+
+ ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
+ sla_queue_event(SLA_EVENT_CHECK_RELOAD);
+
+ return 0;
+}
+
+static enum ast_device_state sla_state(const char *data)
+{
+ char *buf, *station_name, *trunk_name;
+ struct sla_station *station;
+ struct sla_trunk_ref *trunk_ref;
+ enum ast_device_state res = AST_DEVICE_INVALID;
+
+ trunk_name = buf = ast_strdupa(data);
+ station_name = strsep(&trunk_name, "_");
+
+ AST_RWLIST_RDLOCK(&sla_stations);
+ AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+ if (strcasecmp(station_name, station->name))
+ continue;
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
+ break;
+ }
+ if (!trunk_ref) {
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ break;
+ }
+ res = sla_state_to_devstate(trunk_ref->state);
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ }
+ AST_RWLIST_UNLOCK(&sla_stations);
+
+ if (res == AST_DEVICE_INVALID) {
+ ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
+ trunk_name, station_name);
+ }
+
+ return res;
+}
+
+static void destroy_trunk(struct sla_trunk *trunk)
+{
+ struct sla_station_ref *station_ref;
+
+ if (!ast_strlen_zero(trunk->autocontext))
+ ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
+
+ while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
+ ast_free(station_ref);
+
+ ast_string_field_free_memory(trunk);
+ ast_free(trunk);
+}
+
+static void destroy_station(struct sla_station *station)
+{
+ struct sla_trunk_ref *trunk_ref;
+
+ if (!ast_strlen_zero(station->autocontext)) {
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ char exten[AST_MAX_EXTENSION];
+ char hint[AST_MAX_APP];
+ snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
+ snprintf(hint, sizeof(hint), "SLA:%s", exten);
+ ast_context_remove_extension(station->autocontext, exten,
+ 1, sla_registrar);
+ ast_context_remove_extension(station->autocontext, hint,
+ PRIORITY_HINT, sla_registrar);
+ }
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ }
+
+ while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
+ ast_free(trunk_ref);
+
+ ast_string_field_free_memory(station);
+ ast_free(station);
+}
+
+static void sla_destroy(void)
+{
+ struct sla_trunk *trunk;
+ struct sla_station *station;
+
+ AST_RWLIST_WRLOCK(&sla_trunks);
+ while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
+ destroy_trunk(trunk);
+ AST_RWLIST_UNLOCK(&sla_trunks);
+
+ AST_RWLIST_WRLOCK(&sla_stations);
+ while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
+ destroy_station(station);
+ AST_RWLIST_UNLOCK(&sla_stations);
+
+ if (sla.thread != AST_PTHREADT_NULL) {
+ ast_mutex_lock(&sla.lock);
+ sla.stop = 1;
+ ast_cond_signal(&sla.cond);
+ ast_mutex_unlock(&sla.lock);
+ pthread_join(sla.thread, NULL);
+ }
+
+ /* Drop any created contexts from the dialplan */
+ ast_context_destroy(NULL, sla_registrar);
+
+ ast_mutex_destroy(&sla.lock);
+ ast_cond_destroy(&sla.cond);
+}
+
+static int sla_check_device(const char *device)
+{
+ char *tech, *tech_data;
+
+ tech_data = ast_strdupa(device);
+ tech = strsep(&tech_data, "/");
+
+ if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
+ return -1;
+
+ return 0;
+}
+
+static int sla_build_trunk(struct ast_config *cfg, const char *cat)
+{
+ struct sla_trunk *trunk;
+ struct ast_variable *var;
+ const char *dev;
+
+ if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
+ ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
+ return -1;
+ }
+
+ if (sla_check_device(dev)) {
+ ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
+ cat, dev);
+ return -1;
+ }
+
+ if (!(trunk = ast_calloc(1, sizeof(*trunk))))
+ return -1;
+ if (ast_string_field_init(trunk, 32)) {
+ ast_free(trunk);
+ return -1;
+ }
+
+ ast_string_field_set(trunk, name, cat);
+ ast_string_field_set(trunk, device, dev);
+
+ for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
+ if (!strcasecmp(var->name, "autocontext"))
+ ast_string_field_set(trunk, autocontext, var->value);
+ else if (!strcasecmp(var->name, "ringtimeout")) {
+ if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
+ ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
+ var->value, trunk->name);
+ trunk->ring_timeout = 0;
+ }
+ } else if (!strcasecmp(var->name, "barge"))
+ trunk->barge_disabled = ast_false(var->value);
+ else if (!strcasecmp(var->name, "hold")) {
+ if (!strcasecmp(var->value, "private"))
+ trunk->hold_access = SLA_HOLD_PRIVATE;
+ else if (!strcasecmp(var->value, "open"))
+ trunk->hold_access = SLA_HOLD_OPEN;
+ else {
+ ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
+ var->value, trunk->name);
+ }
+ } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
+ ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
+ var->name, var->lineno, SLA_CONFIG_FILE);
+ }
+ }
+
+ if (!ast_strlen_zero(trunk->autocontext)) {
+ struct ast_context *context;
+ context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
+ if (!context) {
+ ast_log(LOG_ERROR, "Failed to automatically find or create "
+ "context '%s' for SLA!\n", trunk->autocontext);
+ destroy_trunk(trunk);
+ return -1;
+ }
+ if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
+ NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
+ ast_log(LOG_ERROR, "Failed to automatically create extension "
+ "for trunk '%s'!\n", trunk->name);
+ destroy_trunk(trunk);
+ return -1;
+ }
+ }
+
+ AST_RWLIST_WRLOCK(&sla_trunks);
+ AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
+ AST_RWLIST_UNLOCK(&sla_trunks);
+
+ return 0;
+}
+
+static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
+{
+ struct sla_trunk *trunk;
+ struct sla_trunk_ref *trunk_ref;
+ struct sla_station_ref *station_ref;
+ char *trunk_name, *options, *cur;
+
+ options = ast_strdupa(var->value);
+ trunk_name = strsep(&options, ",");
+
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+ if (!strcasecmp(trunk->name, trunk_name))
+ break;
+ }
+
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ if (!trunk) {
+ ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
+ return;
+ }
+ if (!(trunk_ref = create_trunk_ref(trunk)))
+ return;
+ trunk_ref->state = SLA_TRUNK_STATE_IDLE;
+
+ while ((cur = strsep(&options, ","))) {
+ char *name, *value = cur;
+ name = strsep(&value, "=");
+ if (!strcasecmp(name, "ringtimeout")) {
+ if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
+ ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
+ "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
+ trunk_ref->ring_timeout = 0;
+ }
+ } else if (!strcasecmp(name, "ringdelay")) {
+ if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
+ ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
+ "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
+ trunk_ref->ring_delay = 0;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Invalid option '%s' for "
+ "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
+ }
+ }
+
+ if (!(station_ref = sla_create_station_ref(station))) {
+ ast_free(trunk_ref);
+ return;
+ }
+ ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
+ AST_RWLIST_WRLOCK(&sla_trunks);
+ AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
+}
+
+static int sla_build_station(struct ast_config *cfg, const char *cat)
+{
+ struct sla_station *station;
+ struct ast_variable *var;
+ const char *dev;
+
+ if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
+ ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
+ return -1;
+ }
+
+ if (!(station = ast_calloc(1, sizeof(*station))))
+ return -1;
+ if (ast_string_field_init(station, 32)) {
+ ast_free(station);
+ return -1;
+ }
+
+ ast_string_field_set(station, name, cat);
+ ast_string_field_set(station, device, dev);
+
+ for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
+ if (!strcasecmp(var->name, "trunk"))
+ sla_add_trunk_to_station(station, var);
+ else if (!strcasecmp(var->name, "autocontext"))
+ ast_string_field_set(station, autocontext, var->value);
+ else if (!strcasecmp(var->name, "ringtimeout")) {
+ if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
+ ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
+ var->value, station->name);
+ station->ring_timeout = 0;
+ }
+ } else if (!strcasecmp(var->name, "ringdelay")) {
+ if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
+ ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
+ var->value, station->name);
+ station->ring_delay = 0;
+ }
+ } else if (!strcasecmp(var->name, "hold")) {
+ if (!strcasecmp(var->value, "private"))
+ station->hold_access = SLA_HOLD_PRIVATE;
+ else if (!strcasecmp(var->value, "open"))
+ station->hold_access = SLA_HOLD_OPEN;
+ else {
+ ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
+ var->value, station->name);
+ }
+
+ } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
+ ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
+ var->name, var->lineno, SLA_CONFIG_FILE);
+ }
+ }
+
+ if (!ast_strlen_zero(station->autocontext)) {
+ struct ast_context *context;
+ struct sla_trunk_ref *trunk_ref;
+ context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
+ if (!context) {
+ ast_log(LOG_ERROR, "Failed to automatically find or create "
+ "context '%s' for SLA!\n", station->autocontext);
+ destroy_station(station);
+ return -1;
+ }
+ /* The extension for when the handset goes off-hook.
+ * exten => station1,1,SLAStation(station1) */
+ if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
+ NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
+ ast_log(LOG_ERROR, "Failed to automatically create extension "
+ "for trunk '%s'!\n", station->name);
+ destroy_station(station);
+ return -1;
+ }
+ AST_RWLIST_RDLOCK(&sla_trunks);
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ char exten[AST_MAX_EXTENSION];
+ char hint[AST_MAX_APP];
+ snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
+ snprintf(hint, sizeof(hint), "SLA:%s", exten);
+ /* Extension for this line button
+ * exten => station1_line1,1,SLAStation(station1_line1) */
+ if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
+ NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
+ ast_log(LOG_ERROR, "Failed to automatically create extension "
+ "for trunk '%s'!\n", station->name);
+ destroy_station(station);
+ return -1;
+ }
+ /* Hint for this line button
+ * exten => station1_line1,hint,SLA:station1_line1 */
+ if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
+ NULL, NULL, hint, NULL, NULL, sla_registrar)) {
+ ast_log(LOG_ERROR, "Failed to automatically create hint "
+ "for trunk '%s'!\n", station->name);
+ destroy_station(station);
+ return -1;
+ }
+ }
+ AST_RWLIST_UNLOCK(&sla_trunks);
+ }
+
+ AST_RWLIST_WRLOCK(&sla_stations);
+ AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
+ AST_RWLIST_UNLOCK(&sla_stations);
+
+ return 0;
+}
+
+static int sla_load_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ const char *cat = NULL;
+ int res = 0;
+ const char *val;
+
+ if (!reload) {
+ ast_mutex_init(&sla.lock);
+ ast_cond_init(&sla.cond, NULL);
+ }
+
+ if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags)))
+ return 0; /* Treat no config as normal */
+ else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
+ sla.attempt_callerid = ast_true(val);
+
+ while ((cat = ast_category_browse(cfg, cat)) && !res) {
+ const char *type;
+ if (!strcasecmp(cat, "general"))
+ continue;
+ if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
+ ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
+ SLA_CONFIG_FILE);
+ continue;
+ }
+ if (!strcasecmp(type, "trunk"))
+ res = sla_build_trunk(cfg, cat);
+ else if (!strcasecmp(type, "station"))
+ res = sla_build_station(cfg, cat);
+ else {
+ ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
+ SLA_CONFIG_FILE, type);
+ }
+ }
+
+ ast_config_destroy(cfg);
+
+ if (!reload)
+ ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
+
+ return res;
+}
+
+static int load_config(int reload)
+{
+ load_config_meetme();
+
+ if (reload) {
+ sla_queue_event(SLA_EVENT_RELOAD);
+ ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
+ "and will be completed when the system is idle.\n");
+ return 0;
+ }
+
+ return sla_load_config(0);
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
+ res = ast_manager_unregister("MeetmeMute");
+ res |= ast_manager_unregister("MeetmeUnmute");
+ res |= ast_manager_unregister("MeetmeList");
+ res |= ast_unregister_application(app4);
+ res |= ast_unregister_application(app3);
+ res |= ast_unregister_application(app2);
+ res |= ast_unregister_application(app);
+ res |= ast_unregister_application(slastation_app);
+ res |= ast_unregister_application(slatrunk_app);
+
+ ast_devstate_prov_del("Meetme");
+ ast_devstate_prov_del("SLA");
+
+ sla_destroy();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= load_config(0);
+
+ ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
+ res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL,
+ action_meetmemute, "Mute a Meetme user");
+ res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL,
+ action_meetmeunmute, "Unmute a Meetme user");
+ res |= ast_manager_register2("MeetmeList", EVENT_FLAG_REPORTING,
+ action_meetmelist, "List participants in a conference", mandescr_meetmelist);
+ res |= ast_register_application(app4, channel_admin_exec, synopsis4, descrip4);
+ res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
+ res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
+ res |= ast_register_application(app, conf_exec, synopsis, descrip);
+ res |= ast_register_application(slastation_app, sla_station_exec,
+ slastation_synopsis, slastation_desc);
+ res |= ast_register_application(slatrunk_app, sla_trunk_exec,
+ slatrunk_synopsis, slatrunk_desc);
+
+ res |= ast_devstate_prov_add("Meetme", meetmestate);
+ res |= ast_devstate_prov_add("SLA", sla_state);
+
+ return res;
+}
+
+static int reload(void)
+{
+ return load_config(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/trunk/apps/app_milliwatt.c b/trunk/apps/app_milliwatt.c
new file mode 100644
index 000000000..754faa555
--- /dev/null
+++ b/trunk/apps/app_milliwatt.c
@@ -0,0 +1,134 @@
+/*
+ * 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 Digital Milliwatt Test
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+
+static char *app = "Milliwatt";
+
+static char *synopsis = "Generate a Constant 1000Hz tone at 0dbm (mu-law)";
+
+static char *descrip =
+"Milliwatt(): Generate a Constant 1000Hz tone at 0dbm (mu-law)\n";
+
+static char digital_milliwatt[] = {0x1e,0x0b,0x0b,0x1e,0x9e,0x8b,0x8b,0x9e} ;
+
+static void *milliwatt_alloc(struct ast_channel *chan, void *params)
+{
+ return ast_calloc(1, sizeof(int));
+}
+
+static void milliwatt_release(struct ast_channel *chan, void *data)
+{
+ ast_free(data);
+ return;
+}
+
+static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ unsigned char buf[AST_FRIENDLY_OFFSET + 640];
+ const int maxsamples = sizeof (buf) / sizeof (buf[0]);
+ int i, *indexp = (int *) data;
+ struct ast_frame wf = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_ULAW,
+ .offset = AST_FRIENDLY_OFFSET,
+ .data = buf + AST_FRIENDLY_OFFSET,
+ .src = __FUNCTION__,
+ };
+
+ /* Instead of len, use samples, because channel.c generator_force
+ * generate(chan, tmp, 0, 160) ignores len. In any case, len is
+ * a multiple of samples, given by number of samples times bytes per
+ * sample. In the case of ulaw, len = samples. for signed linear
+ * len = 2 * samples */
+ if (samples > maxsamples) {
+ ast_log(LOG_WARNING, "Only doing %d samples (%d requested)\n", maxsamples, samples);
+ samples = maxsamples;
+ }
+ len = samples * sizeof (buf[0]);
+ wf.datalen = len;
+ wf.samples = samples;
+
+ /* create a buffer containing the digital milliwatt pattern */
+ for (i = 0; i < len; i++) {
+ buf[AST_FRIENDLY_OFFSET + i] = digital_milliwatt[(*indexp)++];
+ *indexp &= 7;
+ }
+
+ if (ast_write(chan,&wf) < 0) {
+ ast_log(LOG_WARNING,"Failed to write frame to '%s': %s\n",chan->name,strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_generator milliwattgen =
+{
+ alloc: milliwatt_alloc,
+ release: milliwatt_release,
+ generate: milliwatt_generate,
+};
+
+static int milliwatt_exec(struct ast_channel *chan, void *data)
+{
+
+ ast_set_write_format(chan, AST_FORMAT_ULAW);
+ ast_set_read_format(chan, AST_FORMAT_ULAW);
+
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ if (ast_activate_generator(chan,&milliwattgen,"milliwatt") < 0) {
+ ast_log(LOG_WARNING,"Failed to activate generator on '%s'\n",chan->name);
+ return -1;
+ }
+
+ while(!ast_safe_sleep(chan, 10000));
+
+ ast_deactivate_generator(chan);
+
+ return -1;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, milliwatt_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Digital Milliwatt (mu-law) Test Application");
diff --git a/trunk/apps/app_minivm.c b/trunk/apps/app_minivm.c
new file mode 100644
index 000000000..77497fb19
--- /dev/null
+++ b/trunk/apps/app_minivm.c
@@ -0,0 +1,3109 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ * and Edvina AB, Sollentuna, Sweden
+ *
+ * Mark Spencer <markster@digium.com> (Comedian Mail)
+ * and Olle E. Johansson, Edvina.net <oej@edvina.net> (Mini-Voicemail changes)
+ *
+ * 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 MiniVoiceMail - A Minimal Voicemail System for Asterisk
+ *
+ * A voicemail system in small building blocks, working together
+ * based on the Comedian Mail voicemail system (app_voicemail.c).
+ *
+ * \par See also
+ * \arg \ref Config_minivm
+ * \arg \ref Config_minivm_examples
+ * \arg \ref App_minivm
+ *
+ * \ingroup applications
+ *
+ * \page App_minivm Asterisk Mini-voicemail - A minimal voicemail system
+ *
+ * This is a minimal voicemail system, building blocks for something
+ * else. It is built for multi-language systems.
+ * The current version is focused on accounts where voicemail is
+ * forwarded to users in e-mail. It's work in progress, with loosed ends hanging
+ * around from the old voicemail system and it's configuration.
+ *
+ * Hopefully, we can expand this to be a full replacement of voicemail() and voicemailmain()
+ * in the future.
+ *
+ * Dialplan applications
+ * - minivmRecord - record voicemail and send as e-mail ( \ref minivm_record_exec() )
+ * - minivmGreet - Play user's greeting or default greeting ( \ref minivm_greet_exec() )
+ * - minivmNotify - Notify user of message ( \ref minivm_notify_exec() )
+ * - minivmDelete - Delete voicemail message ( \ref minivm_delete_exec() )
+ * - minivmAccMess - Record personal messages (busy | unavailable | temporary)
+ *
+ * Dialplan functions
+ * - MINIVMACCOUNT() - A dialplan function
+ * - MINIVMCOUNTER() - Manage voicemail-related counters for accounts or domains
+ *
+ * CLI Commands
+ * - minivm list accounts
+ * - minivm list zones
+ * - minivm list templates
+ * - minivm show stats
+ * - minivm show settings
+ *
+ * Some notes
+ * - General configuration in minivm.conf
+ * - Users in realtime or configuration file
+ * - Or configured on the command line with just the e-mail address
+ *
+ * Voicemail accounts are identified by userid and domain
+ *
+ * Language codes are like setlocale - langcode_countrycode
+ * \note Don't use language codes like the rest of Asterisk, two letter countrycode. Use
+ * language_country like setlocale().
+ *
+ * Examples:
+ * - Swedish, Sweden sv_se
+ * - Swedish, Finland sv_fi
+ * - English, USA en_us
+ * - English, GB en_gb
+ *
+ * \par See also
+ * \arg \ref Config_minivm
+ * \arg \ref Config_minivm_examples
+ * \arg \ref Minivm_directories
+ * \arg \ref app_minivm.c
+ * \arg Comedian mail: app_voicemail.c
+ * \arg \ref descrip_minivm_accmess
+ * \arg \ref descrip_minivm_greet
+ * \arg \ref descrip_minivm_record
+ * \arg \ref descrip_minivm_delete
+ * \arg \ref descrip_minivm_notify
+ *
+ * \arg \ref App_minivm_todo
+ */
+/*! \page Minivm_directories Asterisk Mini-Voicemail Directory structure
+ *
+ * The directory structure for storing voicemail
+ * - AST_SPOOL_DIR - usually /var/spool/asterisk (configurable in asterisk.conf)
+ * - MVM_SPOOL_DIR - should be configurable, usually AST_SPOOL_DIR/voicemail
+ * - Domain MVM_SPOOL_DIR/domain
+ * - Username MVM_SPOOL_DIR/domain/username
+ * - /greet : Recording of account owner's name
+ * - /busy : Busy message
+ * - /unavailable : Unavailable message
+ * - /temp : Temporary message
+ *
+ * For account anita@localdomain.xx the account directory would as a default be
+ * \b /var/spool/asterisk/voicemail/localdomain.xx/anita
+ *
+ * To avoid transcoding, these sound files should be converted into several formats
+ * They are recorded in the format closest to the incoming streams
+ *
+ *
+ * Back: \ref App_minivm
+ */
+
+/*! \page Config_minivm_examples Example dialplan for Mini-Voicemail
+ * \section Example dialplan scripts for Mini-Voicemail
+ * \verbinclude extensions_minivm.conf.sample
+ *
+ * Back: \ref App_minivm
+ */
+
+/*! \page App_minivm_todo Asterisk Mini-Voicemail - todo
+ * - configure accounts from AMI?
+ * - test, test, test, test
+ * - fix "vm-theextensionis.gsm" voiceprompt from Allison in various formats
+ * "The extension you are calling"
+ * - For trunk, consider using channel storage for information passing between small applications
+ * - Set default directory for voicemail
+ * - New app for creating directory for account if it does not exist
+ * - Re-insert code for IMAP storage at some point
+ * - Jabber integration for notifications
+ * - Figure out how to handle video in voicemail
+ * - Integration with the HTTP server
+ * - New app for moving messages between mailboxes, and optionally mark it as "new"
+ *
+ * For Asterisk 1.4/trunk
+ * - Use string fields for minivm_account
+ *
+ * Back: \ref App_minivm
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <dirent.h>
+#include <locale.h>
+
+
+#include "asterisk/paths.h" /* use various paths */
+#include "asterisk/astobj.h"
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/say.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/manager.h"
+#include "asterisk/dsp.h"
+#include "asterisk/localtime.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/callerid.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+#define MVM_REVIEW (1 << 0) /*!< Review message */
+#define MVM_OPERATOR (1 << 1) /*!< Operator exit during voicemail recording */
+#define MVM_REALTIME (1 << 2) /*!< This user is a realtime account */
+#define MVM_SVMAIL (1 << 3)
+#define MVM_ENVELOPE (1 << 4)
+#define MVM_PBXSKIP (1 << 9)
+#define MVM_ALLOCED (1 << 13)
+
+/*! \brief Default mail command to mail voicemail. Change it with the
+ mailcmd= command in voicemail.conf */
+#define SENDMAIL "/usr/sbin/sendmail -t"
+
+#define SOUND_INTRO "vm-intro"
+#define B64_BASEMAXINLINE 256 /*!< Buffer size for Base 64 attachment encoding */
+#define B64_BASELINELEN 72 /*!< Line length for Base 64 endoded messages */
+#define EOL "\r\n"
+
+#define MAX_DATETIME_FORMAT 512
+#define MAX_NUM_CID_CONTEXTS 10
+
+#define ERROR_LOCK_PATH -100
+#define VOICEMAIL_DIR_MODE 0700
+
+#define VOICEMAIL_CONFIG "minivm.conf"
+#define ASTERISK_USERNAME "asterisk" /*!< Default username for sending mail is asterisk\@localhost */
+
+/*! \brief Message types for notification */
+enum mvm_messagetype {
+ MVM_MESSAGE_EMAIL,
+ MVM_MESSAGE_PAGE
+ /* For trunk: MVM_MESSAGE_JABBER, */
+};
+
+static char MVM_SPOOL_DIR[PATH_MAX];
+
+/* Module declarations */
+static char *app_minivm_record = "MinivmRecord"; /* Leave a message */
+static char *app_minivm_greet = "MinivmGreet"; /* Play voicemail prompts */
+static char *app_minivm_notify = "MinivmNotify"; /* Notify about voicemail by using one of several methods */
+static char *app_minivm_delete = "MinivmDelete"; /* Notify about voicemail by using one of several methods */
+static char *app_minivm_accmess = "MinivmAccMess"; /* Record personal voicemail messages */
+
+static char *synopsis_minivm_record = "Receive Mini-Voicemail and forward via e-mail";
+static char *descrip_minivm_record =
+ " MinivmRecord(username@domain[,options]):\n"
+ "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
+ "MiniVM records audio file in configured format and forwards message to e-mail and pager.\n"
+ "If there's no user account for that address, a temporary account will\n"
+ "be used with default options.\n"
+ "The recorded file name and path will be stored in MINIVM_FILENAME and the \n"
+ "duration of the message will be stored in MINIVM_DURATION\n"
+ "\nNote: If the caller hangs up after the recording, the only way to send\n"
+ "the message and clean up is to execute in the \"h\" extension.\n"
+ "\nThe application will exit if any of the following DTMF digits are \n"
+ "received and the requested extension exist in the current context.\n"
+ " 0 - Jump to the 'o' extension in the current dialplan context.\n"
+ " * - Jump to the 'a' extension in the current dialplan context.\n"
+ "\n"
+ "Result is given in channel variable MINIVM_RECORD_STATUS\n"
+ " The possible values are: SUCCESS | USEREXIT | FAILED\n\n"
+ " Options:\n"
+ " g(#) - Use the specified amount of gain when recording the voicemail\n"
+ " message. The units are whole-number decibels (dB).\n"
+ "\n";
+
+static char *synopsis_minivm_greet = "Play Mini-Voicemail prompts";
+static char *descrip_minivm_greet =
+ " MinivmGreet(username@domain[,options]):\n"
+ "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
+ "MinivmGreet() plays default prompts or user specific prompts for an account.\n"
+ "Busy and unavailable messages can be choosen, but will be overridden if a temporary\n"
+ "message exists for the account.\n"
+ "\n"
+ "Result is given in channel variable MINIVM_GREET_STATUS\n"
+ " The possible values are: SUCCESS | USEREXIT | FAILED\n\n"
+ " Options:\n"
+ " b - Play the 'busy' greeting to the calling party.\n"
+ " s - Skip the playback of instructions for leaving a message to the\n"
+ " calling party.\n"
+ " u - Play the 'unavailable greeting.\n"
+ "\n";
+
+static char *synopsis_minivm_notify = "Notify voicemail owner about new messages.";
+static char *descrip_minivm_notify =
+ " MinivmNotify(username@domain[,template]):\n"
+ "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
+ "MiniVMnotify forwards messages about new voicemail to e-mail and pager.\n"
+ "If there's no user account for that address, a temporary account will\n"
+ "be used with default options (set in minivm.conf).\n"
+ "The recorded file name and path will be read from MVM_FILENAME and the \n"
+ "duration of the message will be accessed from MVM_DURATION (set by MinivmRecord() )\n"
+ "If the channel variable MVM_COUNTER is set, this will be used in the\n"
+ "message file name and available in the template for the message.\n"
+ "If not template is given, the default email template will be used to send email and\n"
+ "default pager template to send paging message (if the user account is configured with\n"
+ "a paging address.\n"
+ "\n"
+ "Result is given in channel variable MINIVM_NOTIFY_STATUS\n"
+ " The possible values are: SUCCESS | FAILED\n"
+ "\n";
+
+static char *synopsis_minivm_delete = "Delete Mini-Voicemail voicemail messages";
+static char *descrip_minivm_delete =
+ " MinivmDelete(filename):\n"
+ "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
+ "It deletes voicemail file set in MVM_FILENAME or given filename.\n"
+ "\n"
+ "Result is given in channel variable MINIVM_DELETE_STATUS\n"
+ " The possible values are: SUCCESS | FAILED\n"
+ " FAILED is set if the file does not exist or can't be deleted.\n"
+ "\n";
+
+static char *synopsis_minivm_accmess = "Record account specific messages";
+static char *descrip_minivm_accmess =
+ " MinivmAccmess(username@domain,option):\n"
+ "This application is part of the Mini-Voicemail system, configured in minivm.conf.\n"
+ "Use this application to record account specific audio/video messages for\n"
+ "busy, unavailable and temporary messages.\n"
+ "Account specific directories will be created if they do not exist.\n"
+ "\nThe option selects message to be recorded:\n"
+ " u Unavailable\n"
+ " b Busy\n"
+ " t Temporary (overrides busy and unavailable)\n"
+ " n Account name\n"
+ "\n"
+ "Result is given in channel variable MINIVM_ACCMESS_STATUS\n"
+ " The possible values are: SUCCESS | FAILED\n"
+ " FAILED is set if the file can't be created.\n"
+ "\n";
+
+enum {
+ OPT_SILENT = (1 << 0),
+ OPT_BUSY_GREETING = (1 << 1),
+ OPT_UNAVAIL_GREETING = (1 << 2),
+ OPT_TEMP_GREETING = (1 << 3),
+ OPT_NAME_GREETING = (1 << 4),
+ OPT_RECORDGAIN = (1 << 5),
+} minivm_option_flags;
+
+enum {
+ OPT_ARG_RECORDGAIN = 0,
+ OPT_ARG_ARRAY_SIZE = 1,
+} minivm_option_args;
+
+AST_APP_OPTIONS(minivm_app_options, {
+ AST_APP_OPTION('s', OPT_SILENT),
+ AST_APP_OPTION('b', OPT_BUSY_GREETING),
+ AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
+ AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
+});
+
+AST_APP_OPTIONS(minivm_accmess_options, {
+ AST_APP_OPTION('b', OPT_BUSY_GREETING),
+ AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
+ AST_APP_OPTION('t', OPT_TEMP_GREETING),
+ AST_APP_OPTION('n', OPT_NAME_GREETING),
+});
+
+/*! \brief Structure for linked list of Mini-Voicemail users: \ref minivm_accounts */
+struct minivm_account {
+ char username[AST_MAX_CONTEXT]; /*!< Mailbox username */
+ char domain[AST_MAX_CONTEXT]; /*!< Voicemail domain */
+
+ char pincode[10]; /*!< Secret pin code, numbers only */
+ char fullname[120]; /*!< Full name, for directory app */
+ char email[80]; /*!< E-mail address - override */
+ char pager[80]; /*!< E-mail address to pager (no attachment) */
+ char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Voicemail account account code */
+ char serveremail[80]; /*!< From: Mail address */
+ char externnotify[160]; /*!< Configurable notification command */
+ char language[MAX_LANGUAGE]; /*!< Config: Language setting */
+ char zonetag[80]; /*!< Time zone */
+ char uniqueid[20]; /*!< Unique integer identifier */
+ char exit[80]; /*!< Options for exiting from voicemail() */
+ char attachfmt[80]; /*!< Format for voicemail audio file attachment */
+ char etemplate[80]; /*!< Pager template */
+ char ptemplate[80]; /*!< Voicemail format */
+ unsigned int flags; /*!< MVM_ flags */
+ struct ast_variable *chanvars; /*!< Variables for e-mail template */
+ double volgain; /*!< Volume gain for voicemails sent via e-mail */
+ AST_LIST_ENTRY(minivm_account) list;
+};
+
+/*! \brief The list of e-mail accounts */
+static AST_LIST_HEAD_STATIC(minivm_accounts, minivm_account);
+
+/*! \brief Linked list of e-mail templates in various languages
+ These are used as templates for e-mails, pager messages and jabber messages
+ \ref message_templates
+*/
+struct minivm_template {
+ char name[80]; /*!< Template name */
+ char *body; /*!< Body of this template */
+ char fromaddress[100]; /*!< Who's sending the e-mail? */
+ char serveremail[80]; /*!< From: Mail address */
+ char subject[100]; /*!< Subject line */
+ char charset[32]; /*!< Default character set for this template */
+ char locale[20]; /*!< Locale for setlocale() */
+ char dateformat[80]; /*!< Date format to use in this attachment */
+ int attachment; /*!< Attachment of media yes/no - no for pager messages */
+ AST_LIST_ENTRY(minivm_template) list; /*!< List mechanics */
+};
+
+/*! \brief The list of e-mail templates */
+static AST_LIST_HEAD_STATIC(message_templates, minivm_template);
+
+/*! \brief Options for leaving voicemail with the voicemail() application */
+struct leave_vm_options {
+ unsigned int flags;
+ signed char record_gain;
+};
+
+/*! \brief Structure for base64 encoding */
+struct b64_baseio {
+ int iocp;
+ int iolen;
+ int linelength;
+ int ateof;
+ unsigned char iobuf[B64_BASEMAXINLINE];
+};
+
+/*! \brief Voicemail time zones */
+struct minivm_zone {
+ char name[80]; /*!< Name of this time zone */
+ char timezone[80]; /*!< Timezone definition */
+ char msg_format[BUFSIZ]; /*!< Not used in minivm ...yet */
+ AST_LIST_ENTRY(minivm_zone) list; /*!< List mechanics */
+};
+
+/*! \brief The list of e-mail time zones */
+static AST_LIST_HEAD_STATIC(minivm_zones, minivm_zone);
+
+/*! \brief Structure for gathering statistics */
+struct minivm_stats {
+ int voicemailaccounts; /*!< Number of static accounts */
+ int timezones; /*!< Number of time zones */
+ int templates; /*!< Number of templates */
+
+ struct timeval reset; /*!< Time for last reset */
+ int receivedmessages; /*!< Number of received messages since reset */
+ struct timeval lastreceived; /*!< Time for last voicemail sent */
+};
+
+/*! \brief Statistics for voicemail */
+static struct minivm_stats global_stats;
+
+AST_MUTEX_DEFINE_STATIC(minivmlock); /*!< Lock to protect voicemail system */
+AST_MUTEX_DEFINE_STATIC(minivmloglock); /*!< Lock to protect voicemail system log file */
+
+FILE *minivmlogfile; /*!< The minivm log file */
+
+static int global_vmminmessage; /*!< Minimum duration of messages */
+static int global_vmmaxmessage; /*!< Maximum duration of message */
+static int global_maxsilence; /*!< Maximum silence during recording */
+static int global_maxgreet; /*!< Maximum length of prompts */
+static int global_silencethreshold = 128;
+static char global_mailcmd[160]; /*!< Configurable mail cmd */
+static char global_externnotify[160]; /*!< External notification application */
+static char global_logfile[PATH_MAX]; /*!< Global log file for messages */
+static char default_vmformat[80];
+
+static struct ast_flags globalflags = {0}; /*!< Global voicemail flags */
+static int global_saydurationminfo;
+static char global_charset[32]; /*!< Global charset in messages */
+
+static double global_volgain; /*!< Volume gain for voicmemail via e-mail */
+
+/*! \brief Default dateformat, can be overridden in configuration file */
+#define DEFAULT_DATEFORMAT "%A, %B %d, %Y at %r"
+#define DEFAULT_CHARSET "ISO-8859-1"
+
+/* Forward declarations */
+static char *message_template_parse_filebody(const char *filename);
+static char *message_template_parse_emailbody(const char *body);
+static int create_vmaccount(char *name, struct ast_variable *var, int realtime);
+static struct minivm_account *find_user_realtime(const char *domain, const char *username);
+static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+/*! \brief Create message template */
+static struct minivm_template *message_template_create(const char *name)
+{
+ struct minivm_template *template;
+
+ template = ast_calloc(1, sizeof(*template));
+ if (!template)
+ return NULL;
+
+ /* Set some defaults for templates */
+ ast_copy_string(template->name, name, sizeof(template->name));
+ ast_copy_string(template->dateformat, DEFAULT_DATEFORMAT, sizeof(template->dateformat));
+ ast_copy_string(template->charset, DEFAULT_CHARSET, sizeof(template->charset));
+ ast_copy_string(template->subject, "New message in mailbox ${MVM_USERNAME}@${MVM_DOMAIN}", sizeof(template->subject));
+ template->attachment = TRUE;
+
+ return template;
+}
+
+/*! \brief Release memory allocated by message template */
+static void message_template_free(struct minivm_template *template)
+{
+ if (template->body)
+ ast_free(template->body);
+
+ ast_free (template);
+}
+
+/*! \brief Build message template from configuration */
+static int message_template_build(const char *name, struct ast_variable *var)
+{
+ struct minivm_template *template;
+ int error = 0;
+
+ template = message_template_create(name);
+ if (!template) {
+ ast_log(LOG_ERROR, "Out of memory, can't allocate message template object %s.\n", name);
+ return -1;
+ }
+
+ while (var) {
+ ast_debug(3, "-_-_- Configuring template option %s = \"%s\" for template %s\n", var->name, var->value, name);
+ if (!strcasecmp(var->name, "fromaddress")) {
+ ast_copy_string(template->fromaddress, var->value, sizeof(template->fromaddress));
+ } else if (!strcasecmp(var->name, "fromemail")) {
+ ast_copy_string(template->serveremail, var->value, sizeof(template->serveremail));
+ } else if (!strcasecmp(var->name, "subject")) {
+ ast_copy_string(template->subject, var->value, sizeof(template->subject));
+ } else if (!strcasecmp(var->name, "locale")) {
+ ast_copy_string(template->locale, var->value, sizeof(template->locale));
+ } else if (!strcasecmp(var->name, "attachmedia")) {
+ template->attachment = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "dateformat")) {
+ ast_copy_string(template->dateformat, var->value, sizeof(template->dateformat));
+ } else if (!strcasecmp(var->name, "charset")) {
+ ast_copy_string(template->charset, var->value, sizeof(template->charset));
+ } else if (!strcasecmp(var->name, "templatefile")) {
+ if (template->body)
+ ast_free(template->body);
+ template->body = message_template_parse_filebody(var->value);
+ if (!template->body) {
+ ast_log(LOG_ERROR, "Error reading message body definition file %s\n", var->value);
+ error++;
+ }
+ } else if (!strcasecmp(var->name, "messagebody")) {
+ if (template->body)
+ ast_free(template->body);
+ template->body = message_template_parse_emailbody(var->value);
+ if (!template->body) {
+ ast_log(LOG_ERROR, "Error parsing message body definition:\n %s\n", var->value);
+ error++;
+ }
+ } else {
+ ast_log(LOG_ERROR, "Unknown message template configuration option \"%s=%s\"\n", var->name, var->value);
+ error++;
+ }
+ var = var->next;
+ }
+ if (error)
+ ast_log(LOG_ERROR, "-- %d errors found parsing message template definition %s\n", error, name);
+
+ AST_LIST_LOCK(&message_templates);
+ AST_LIST_INSERT_TAIL(&message_templates, template, list);
+ AST_LIST_UNLOCK(&message_templates);
+
+ global_stats.templates++;
+
+ return error;
+}
+
+/*! \brief Find named template */
+static struct minivm_template *message_template_find(const char *name)
+{
+ struct minivm_template *this, *res = NULL;
+
+ if (ast_strlen_zero(name))
+ return NULL;
+
+ AST_LIST_LOCK(&message_templates);
+ AST_LIST_TRAVERSE(&message_templates, this, list) {
+ if (!strcasecmp(this->name, name)) {
+ res = this;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&message_templates);
+
+ return res;
+}
+
+
+/*! \brief Clear list of templates */
+static void message_destroy_list(void)
+{
+ struct minivm_template *this;
+ AST_LIST_LOCK(&message_templates);
+ while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list)))
+ message_template_free(this);
+
+ AST_LIST_UNLOCK(&message_templates);
+}
+
+/*! \brief read buffer from file (base64 conversion) */
+static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
+{
+ int l;
+
+ if (bio->ateof)
+ return 0;
+
+ if ((l = fread(bio->iobuf, 1, B64_BASEMAXINLINE,fi)) <= 0) {
+ if (ferror(fi))
+ return -1;
+
+ bio->ateof = 1;
+ return 0;
+ }
+
+ bio->iolen= l;
+ bio->iocp= 0;
+
+ return 1;
+}
+
+/*! \brief read character from file to buffer (base64 conversion) */
+static int b64_inchar(struct b64_baseio *bio, FILE *fi)
+{
+ if (bio->iocp >= bio->iolen) {
+ if (!b64_inbuf(bio, fi))
+ return EOF;
+ }
+
+ return bio->iobuf[bio->iocp++];
+}
+
+/*! \brief write buffer to file (base64 conversion) */
+static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
+{
+ if (bio->linelength >= B64_BASELINELEN) {
+ if (fputs(EOL,so) == EOF)
+ return -1;
+
+ bio->linelength= 0;
+ }
+
+ if (putc(((unsigned char) c), so) == EOF)
+ return -1;
+
+ bio->linelength++;
+
+ return 1;
+}
+
+/*! \brief Encode file to base64 encoding for email attachment (base64 conversion) */
+static int base_encode(char *filename, FILE *so)
+{
+ unsigned char dtable[B64_BASEMAXINLINE];
+ int i,hiteof= 0;
+ FILE *fi;
+ struct b64_baseio bio;
+
+ memset(&bio, 0, sizeof(bio));
+ bio.iocp = B64_BASEMAXINLINE;
+
+ if (!(fi = fopen(filename, "rb"))) {
+ ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ for (i= 0; i<9; i++) {
+ dtable[i]= 'A'+i;
+ dtable[i+9]= 'J'+i;
+ dtable[26+i]= 'a'+i;
+ dtable[26+i+9]= 'j'+i;
+ }
+ for (i= 0; i < 8; i++) {
+ dtable[i+18]= 'S'+i;
+ dtable[26+i+18]= 's'+i;
+ }
+ for (i= 0; i < 10; i++) {
+ dtable[52+i]= '0'+i;
+ }
+ dtable[62]= '+';
+ dtable[63]= '/';
+
+ while (!hiteof){
+ unsigned char igroup[3], ogroup[4];
+ int c,n;
+
+ igroup[0]= igroup[1]= igroup[2]= 0;
+
+ for (n= 0; n < 3; n++) {
+ if ((c = b64_inchar(&bio, fi)) == EOF) {
+ hiteof= 1;
+ break;
+ }
+ igroup[n]= (unsigned char)c;
+ }
+
+ if (n> 0) {
+ ogroup[0]= dtable[igroup[0]>>2];
+ ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
+ ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
+ ogroup[3]= dtable[igroup[2]&0x3F];
+
+ if (n<3) {
+ ogroup[3]= '=';
+
+ if (n<2)
+ ogroup[2]= '=';
+ }
+
+ for (i= 0;i<4;i++)
+ b64_ochar(&bio, ogroup[i], so);
+ }
+ }
+
+ /* Put end of line - line feed */
+ if (fputs(EOL, so) == EOF)
+ return 0;
+
+ fclose(fi);
+
+ return 1;
+}
+
+static int get_date(char *s, int len)
+{
+ struct ast_tm tm;
+ struct timeval tv = ast_tvnow();
+
+ ast_localtime(&tv, &tm, NULL);
+ return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
+}
+
+
+/*! \brief Free user structure - if it's allocated */
+static void free_user(struct minivm_account *vmu)
+{
+ if (vmu->chanvars)
+ ast_variables_destroy(vmu->chanvars);
+ ast_free(vmu);
+}
+
+
+
+/*! \brief Prepare for voicemail template by adding channel variables
+ to the channel
+*/
+static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm_account *vmu, const char *cidnum, const char *cidname, const char *dur, const char *date, const char *counter)
+{
+ char callerid[256];
+ struct ast_variable *var;
+
+ if (!channel) {
+ ast_log(LOG_ERROR, "No allocated channel, giving up...\n");
+ return;
+ }
+
+ for (var = vmu->chanvars ; var ; var = var->next)
+ pbx_builtin_setvar_helper(channel, var->name, var->value);
+
+ /* Prepare variables for substition in email body and subject */
+ pbx_builtin_setvar_helper(channel, "MVM_NAME", vmu->fullname);
+ pbx_builtin_setvar_helper(channel, "MVM_DUR", dur);
+ pbx_builtin_setvar_helper(channel, "MVM_DOMAIN", vmu->domain);
+ pbx_builtin_setvar_helper(channel, "MVM_USERNAME", vmu->username);
+ pbx_builtin_setvar_helper(channel, "MVM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
+ pbx_builtin_setvar_helper(channel, "MVM_CIDNAME", (cidname ? cidname : "an unknown caller"));
+ pbx_builtin_setvar_helper(channel, "MVM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
+ pbx_builtin_setvar_helper(channel, "MVM_DATE", date);
+ if (!ast_strlen_zero(counter))
+ pbx_builtin_setvar_helper(channel, "MVM_COUNTER", counter);
+}
+
+/*! \brief Set default values for Mini-Voicemail users */
+static void populate_defaults(struct minivm_account *vmu)
+{
+ ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
+ ast_copy_string(vmu->attachfmt, default_vmformat, sizeof(vmu->attachfmt));
+ vmu->volgain = global_volgain;
+}
+
+/*! \brief Fix quote of mail headers for non-ascii characters */
+static char *mailheader_quote(const char *from, char *to, size_t len)
+{
+ char *ptr = to;
+ *ptr++ = '"';
+ for (; ptr < to + len - 1; from++) {
+ if (*from == '"')
+ *ptr++ = '\\';
+ else if (*from == '\0')
+ break;
+ *ptr++ = *from;
+ }
+ if (ptr < to + len - 1)
+ *ptr++ = '"';
+ *ptr = '\0';
+ return to;
+}
+
+
+/*! \brief Allocate new vm user and set default values */
+static struct minivm_account *mvm_user_alloc(void)
+{
+ struct minivm_account *new;
+
+ new = ast_calloc(1, sizeof(*new));
+ if (!new)
+ return NULL;
+ populate_defaults(new);
+
+ return new;
+}
+
+
+/*! \brief Clear list of users */
+static void vmaccounts_destroy_list(void)
+{
+ struct minivm_account *this;
+ AST_LIST_LOCK(&minivm_accounts);
+ while ((this = AST_LIST_REMOVE_HEAD(&minivm_accounts, list)))
+ ast_free(this);
+ AST_LIST_UNLOCK(&minivm_accounts);
+}
+
+
+/*! \brief Find user from static memory object list */
+static struct minivm_account *find_account(const char *domain, const char *username, int createtemp)
+{
+ struct minivm_account *vmu = NULL, *cur;
+
+
+ if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
+ ast_log(LOG_NOTICE, "No username or domain? \n");
+ return NULL;
+ }
+ ast_debug(3, "-_-_-_- Looking for voicemail user %s in domain %s\n", username, domain);
+
+ AST_LIST_LOCK(&minivm_accounts);
+ AST_LIST_TRAVERSE(&minivm_accounts, cur, list) {
+ /* Is this the voicemail account we're looking for? */
+ if (!strcasecmp(domain, cur->domain) && !strcasecmp(username, cur->username))
+ break;
+ }
+ AST_LIST_UNLOCK(&minivm_accounts);
+
+ if (cur) {
+ ast_debug(3, "-_-_- Found account for %s@%s\n", username, domain);
+ vmu = cur;
+
+ } else
+ vmu = find_user_realtime(domain, username);
+
+ if (createtemp && !vmu) {
+ /* Create a temporary user, send e-mail and be gone */
+ vmu = mvm_user_alloc();
+ ast_set2_flag(vmu, TRUE, MVM_ALLOCED);
+ if (vmu) {
+ ast_copy_string(vmu->username, username, sizeof(vmu->username));
+ ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
+ ast_debug(1, "--- Created temporary account\n");
+ }
+
+ }
+ return vmu;
+}
+
+/*! \brief Find user in realtime storage
+ Returns pointer to minivm_account structure
+*/
+static struct minivm_account *find_user_realtime(const char *domain, const char *username)
+{
+ struct ast_variable *var;
+ struct minivm_account *retval;
+ char name[MAXHOSTNAMELEN];
+
+ retval = mvm_user_alloc();
+ if (!retval)
+ return NULL;
+
+ if (username)
+ ast_copy_string(retval->username, username, sizeof(retval->username));
+
+ populate_defaults(retval);
+ var = ast_load_realtime("minivm", "username", username, "domain", domain, NULL);
+
+ if (!var) {
+ ast_free(retval);
+ return NULL;
+ }
+
+ snprintf(name, sizeof(name), "%s@%s", username, domain);
+ create_vmaccount(name, var, TRUE);
+
+ ast_variables_destroy(var);
+ return retval;
+}
+
+/*! \brief Send voicemail with audio file as an attachment */
+static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter)
+{
+ FILE *p = NULL;
+ int pfd;
+ char email[256] = "";
+ char who[256] = "";
+ char date[256];
+ char bound[256];
+ char fname[PATH_MAX];
+ char dur[PATH_MAX];
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ char tmp2[PATH_MAX];
+ struct timeval now;
+ struct ast_tm tm;
+ struct minivm_zone *the_zone = NULL;
+ int len_passdata;
+ struct ast_channel *ast;
+ char *finalfilename;
+ char *passdata = NULL;
+ char *passdata2 = NULL;
+ char *fromaddress;
+ char *fromemail;
+
+ if (type == MVM_MESSAGE_EMAIL) {
+ if (vmu && !ast_strlen_zero(vmu->email)) {
+ ast_copy_string(email, vmu->email, sizeof(email));
+ } else if (!ast_strlen_zero(vmu->username) && !ast_strlen_zero(vmu->domain))
+ snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
+ } else if (type == MVM_MESSAGE_PAGE) {
+ ast_copy_string(email, vmu->pager, sizeof(email));
+ }
+
+ if (ast_strlen_zero(email)) {
+ ast_log(LOG_WARNING, "No address to send message to.\n");
+ return -1;
+ }
+
+ ast_debug(3, "-_-_- Sending mail to %s@%s - Using template %s\n", vmu->username, vmu->domain, template->name);
+
+ if (!strcmp(format, "wav49"))
+ format = "WAV";
+
+
+ /* If we have a gain option, process it now with sox */
+ if (type == MVM_MESSAGE_EMAIL && (vmu->volgain < -.001 || vmu->volgain > .001) ) {
+ char newtmp[PATH_MAX];
+ char tmpcmd[PATH_MAX];
+ int tmpfd;
+
+ ast_copy_string(newtmp, "/tmp/XXXXXX", sizeof(newtmp));
+ ast_debug(3, "newtmp: %s\n", newtmp);
+ tmpfd = mkstemp(newtmp);
+ snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, filename, format, newtmp, format);
+ ast_safe_system(tmpcmd);
+ finalfilename = newtmp;
+ ast_debug(3, "-- VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->volgain, vmu->username);
+ } else {
+ finalfilename = ast_strdupa(filename);
+ }
+
+ /* Create file name */
+ snprintf(fname, sizeof(fname), "%s.%s", finalfilename, format);
+
+ if (template->attachment)
+ ast_debug(1, "-- Attaching file '%s', format '%s', uservm is '%d'\n", finalfilename, format, attach_user_voicemail);
+
+ /* Make a temporary file instead of piping directly to sendmail, in case the mail
+ command hangs */
+ pfd = mkstemp(tmp);
+ if (pfd > -1) {
+ p = fdopen(pfd, "w");
+ if (!p) {
+ close(pfd);
+ pfd = -1;
+ }
+ ast_debug(1, "-_-_- Opening temp file for e-mail: %s\n", tmp);
+ }
+ if (!p) {
+ ast_log(LOG_WARNING, "Unable to open temporary file '%s'\n", tmp);
+ return -1;
+ }
+ /* Allocate channel used for chanvar substitution */
+ ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
+
+
+ snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+
+ /* Does this user have a timezone specified? */
+ if (!ast_strlen_zero(vmu->zonetag)) {
+ /* Find the zone in the list */
+ struct minivm_zone *z;
+ AST_LIST_LOCK(&minivm_zones);
+ AST_LIST_TRAVERSE(&minivm_zones, z, list) {
+ if (strcmp(z->name, vmu->zonetag))
+ continue;
+ the_zone = z;
+ }
+ AST_LIST_UNLOCK(&minivm_zones);
+ }
+
+ now = ast_tvnow();
+ ast_localtime(&now, &tm, the_zone ? the_zone->timezone : NULL);
+ ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
+
+ /* Start printing the email to the temporary file */
+ fprintf(p, "Date: %s\n", date);
+
+ /* Set date format for voicemail mail */
+ ast_strftime(date, sizeof(date), template->dateformat, &tm);
+
+
+ /* Populate channel with channel variables for substitution */
+ prep_email_sub_vars(ast, vmu, cidnum, cidname, dur, date, counter);
+
+ /* Find email address to use */
+ /* If there's a server e-mail adress in the account, user that, othterwise template */
+ fromemail = ast_strlen_zero(vmu->serveremail) ? template->serveremail : vmu->serveremail;
+
+ /* Find name to user for server e-mail */
+ fromaddress = ast_strlen_zero(template->fromaddress) ? "" : template->fromaddress;
+
+ /* If needed, add hostname as domain */
+ if (ast_strlen_zero(fromemail))
+ fromemail = "asterisk";
+
+ if (strchr(fromemail, '@'))
+ ast_copy_string(who, fromemail, sizeof(who));
+ else {
+ char host[MAXHOSTNAMELEN];
+ gethostname(host, sizeof(host)-1);
+ snprintf(who, sizeof(who), "%s@%s", fromemail, host);
+ }
+
+ if (ast_strlen_zero(fromaddress)) {
+ fprintf(p, "From: Asterisk PBX <%s>\n", who);
+ } else {
+ /* Allocate a buffer big enough for variable substitution */
+ int vmlen = strlen(fromaddress) * 3 + 200;
+
+ ast_debug(4, "-_-_- Fromaddress template: %s\n", fromaddress);
+ if ((passdata = alloca(vmlen))) {
+ pbx_substitute_variables_helper(ast, fromaddress, passdata, vmlen);
+ len_passdata = strlen(passdata) * 2 + 3;
+ passdata2 = alloca(len_passdata);
+ fprintf(p, "From: %s <%s>\n", mailheader_quote(passdata, passdata2, len_passdata), who);
+ } else {
+ ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ fclose(p);
+ return -1;
+ }
+ }
+ ast_debug(4, "-_-_- Fromstring now: %s\n", ast_strlen_zero(passdata) ? "-default-" : passdata);
+
+ fprintf(p, "Message-ID: <Asterisk-%d-%s-%d-%s>\n", (unsigned int)rand(), vmu->username, (int)getpid(), who);
+ len_passdata = strlen(vmu->fullname) * 2 + 3;
+ passdata2 = alloca(len_passdata);
+ if (!ast_strlen_zero(vmu->email))
+ fprintf(p, "To: %s <%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->email);
+ else
+ fprintf(p, "To: %s <%s@%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->username, vmu->domain);
+
+ if (!ast_strlen_zero(template->subject)) {
+ char *passdata;
+ int vmlen = strlen(template->subject) * 3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ pbx_substitute_variables_helper(ast, template->subject, passdata, vmlen);
+ fprintf(p, "Subject: %s\n", passdata);
+ } else {
+ ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ fclose(p);
+ return -1;
+ }
+
+ ast_debug(4, "-_-_- Subject now: %s\n", passdata);
+
+ } else {
+ fprintf(p, "Subject: New message in mailbox %s@%s\n", vmu->username, vmu->domain);
+ ast_debug(1, "-_-_- Using default subject for this email \n");
+ }
+
+
+ if (option_debug > 2)
+ fprintf(p, "X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->username, vmu->domain);
+ fprintf(p, "MIME-Version: 1.0\n");
+
+ /* Something unique. */
+ snprintf(bound, sizeof(bound), "voicemail_%s%d%d", vmu->username, (int)getpid(), (unsigned int)rand());
+
+ fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
+
+ fprintf(p, "--%s\n", bound);
+ fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", global_charset);
+ if (!ast_strlen_zero(template->body)) {
+ char *passdata;
+ int vmlen = strlen(template->body)*3 + 200;
+ if ((passdata = alloca(vmlen))) {
+ pbx_substitute_variables_helper(ast, template->body, passdata, vmlen);
+ ast_debug(3, "Message now: %s\n-----\n", passdata);
+ fprintf(p, "%s\n", passdata);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
+ } else {
+ fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n"
+
+ "in mailbox %s from %s, on %s so you might\n"
+ "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
+ dur, vmu->username, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
+ ast_debug(3, "Using default message body (no template)\n-----\n");
+ }
+ /* Eww. We want formats to tell us their own MIME type */
+ if (template->attachment) {
+ char *ctype = "audio/x-";
+ ast_debug(3, "-_-_- Attaching file to message: %s\n", fname);
+ if (!strcasecmp(format, "ogg"))
+ ctype = "application/";
+
+ fprintf(p, "--%s\n", bound);
+ fprintf(p, "Content-Type: %s%s; name=\"voicemailmsg.%s\"\n", ctype, format, format);
+ fprintf(p, "Content-Transfer-Encoding: base64\n");
+ fprintf(p, "Content-Description: Voicemail sound attachment.\n");
+ fprintf(p, "Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter : "", format);
+
+ base_encode(fname, p);
+ fprintf(p, "\n\n--%s--\n.\n", bound);
+ }
+ fclose(p);
+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", global_mailcmd, tmp, tmp);
+ ast_safe_system(tmp2);
+ ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
+ ast_debug(3, "-_-_- Actual command used: %s\n", tmp2);
+ if (ast)
+ ast_channel_free(ast);
+ return 0;
+}
+
+/*! \brief Create directory based on components */
+static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder)
+{
+ return snprintf(dest, len, "%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ? "" : "/", folder ? folder : "");
+}
+
+/*! \brief Checks if directory exists. Does not create directory, but builds string in dest
+ * \param dest String. base directory.
+ * \param len Int. Length base directory string.
+ * \param domain String. Ignored if is null or empty string.
+ * \param username String. Ignored if is null or empty string.
+ * \param folder String. Ignored if is null or empty string.
+ * \return 0 on failure, 1 on success.
+ */
+static int check_dirpath(char *dest, int len, char *domain, char *username, char *folder)
+{
+ struct stat filestat;
+ make_dir(dest, len, domain, username, folder ? folder : "");
+ if (stat(dest, &filestat)== -1)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/*! \brief basically mkdir -p $dest/$domain/$username/$folder
+ * \param dest String. base directory.
+ * \param len Length of directory string
+ * \param domain String. Ignored if is null or empty string.
+ * \param folder String. Ignored if is null or empty string.
+ * \param username String. Ignored if is null or empty string.
+ * \return -1 on failure, 0 on success.
+ */
+static int create_dirpath(char *dest, int len, char *domain, char *username, char *folder)
+{
+ int res;
+ make_dir(dest, len, domain, username, folder);
+ if ((res = ast_mkdir(dest, 0777))) {
+ ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
+ return -1;
+ }
+ ast_debug(2, "Creating directory for %s@%s folder %s : %s\n", username, domain, folder, dest);
+ return 0;
+}
+
+
+/*! \brief Play intro message before recording voicemail
+*/
+static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes)
+{
+ int res;
+ char fn[PATH_MAX];
+
+ ast_debug(2, "-_-_- Still preparing to play message ...\n");
+
+ snprintf(fn, sizeof(fn), "%s%s/%s/greet", MVM_SPOOL_DIR, domain, username);
+
+ if (ast_fileexists(fn, NULL, NULL) > 0) {
+ res = ast_streamfile(chan, fn, chan->language);
+ if (res)
+ return -1;
+ res = ast_waitstream(chan, ecodes);
+ if (res)
+ return res;
+ } else {
+ int numericusername = 1;
+ char *i = username;
+
+ ast_debug(2, "-_-_- No personal prompts. Using default prompt set for language\n");
+
+ while (*i) {
+ ast_debug(2, "-_-_- Numeric? Checking %c\n", *i);
+ if (!isdigit(*i)) {
+ numericusername = FALSE;
+ break;
+ }
+ i++;
+ }
+
+ if (numericusername) {
+ if(ast_streamfile(chan, "vm-theperson", chan->language))
+ return -1;
+ if ((res = ast_waitstream(chan, ecodes)))
+ return res;
+
+ res = ast_say_digit_str(chan, username, ecodes, chan->language);
+ if (res)
+ return res;
+ } else {
+ if(ast_streamfile(chan, "vm-theextensionis", chan->language))
+ return -1;
+ if ((res = ast_waitstream(chan, ecodes)))
+ return res;
+ }
+ }
+
+ res = ast_streamfile(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language);
+ if (res)
+ return -1;
+ res = ast_waitstream(chan, ecodes);
+ return res;
+}
+
+/*! \brief Delete media files and attribute file */
+static int vm_delete(char *file)
+{
+ int res;
+
+ ast_debug(1, "-_-_- Deleting voicemail file %s\n", file);
+
+ res = unlink(file); /* Remove the meta data file */
+ res |= ast_filedelete(file, NULL); /* remove the media file */
+ return res;
+}
+
+
+/*! \brief Record voicemail message & let caller review or re-record it, or set options if applicable */
+static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
+ int outsidecaller, struct minivm_account *vmu, int *duration, const char *unlockdir,
+ signed char record_gain)
+{
+ int cmd = 0;
+ int max_attempts = 3;
+ int attempts = 0;
+ int recorded = 0;
+ int message_exists = 0;
+ signed char zero_gain = 0;
+ char *acceptdtmf = "#";
+ char *canceldtmf = "";
+
+ /* Note that urgent and private are for flagging messages as such in the future */
+
+ /* barf if no pointer passed to store duration in */
+ if (duration == NULL) {
+ ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
+ return -1;
+ }
+
+ cmd = '3'; /* Want to start by recording */
+
+ while ((cmd >= 0) && (cmd != 't')) {
+ switch (cmd) {
+ case '2':
+ /* Review */
+ ast_verb(3, "Reviewing the message\n");
+ ast_streamfile(chan, recordfile, chan->language);
+ cmd = ast_waitstream(chan, AST_DIGIT_ANY);
+ break;
+ case '3':
+ message_exists = 0;
+ /* Record */
+ if (option_verbose > 2) {
+ if (recorded == 1)
+ ast_verb(3, "Re-recording the message\n");
+ else
+ ast_verb(3, "Recording the message\n");
+ }
+ if (recorded && outsidecaller)
+ cmd = ast_play_and_wait(chan, "beep");
+ recorded = 1;
+ /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
+ if (ast_test_flag(vmu, MVM_OPERATOR))
+ canceldtmf = "0";
+ cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf);
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
+ if (cmd == -1) /* User has hung up, no options to give */
+ return cmd;
+ if (cmd == '0')
+ break;
+ else if (cmd == '*')
+ break;
+ else {
+ /* If all is well, a message exists */
+ message_exists = 1;
+ cmd = 0;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '*':
+ case '#':
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ break;
+ case '0':
+ if(!ast_test_flag(vmu, MVM_OPERATOR)) {
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ break;
+ }
+ if (message_exists || recorded) {
+ cmd = ast_play_and_wait(chan, "vm-saveoper");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 3000);
+ if (cmd == '1') {
+ ast_play_and_wait(chan, "vm-msgsaved");
+ cmd = '0';
+ } else {
+ ast_play_and_wait(chan, "vm-deleted");
+ vm_delete(recordfile);
+ cmd = '0';
+ }
+ }
+ return cmd;
+ default:
+ /* If the caller is an ouside caller, and the review option is enabled,
+ allow them to review the message, but let the owner of the box review
+ their OGM's */
+ if (outsidecaller && !ast_test_flag(vmu, MVM_REVIEW))
+ return cmd;
+ if (message_exists) {
+ cmd = ast_play_and_wait(chan, "vm-review");
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-torerecord");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+
+ if (!cmd && outsidecaller && ast_test_flag(vmu, MVM_OPERATOR)) {
+ cmd = ast_play_and_wait(chan, "vm-reachoper");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 6000);
+ if (!cmd) {
+ attempts++;
+ }
+ if (attempts > max_attempts) {
+ cmd = 't';
+ }
+ }
+ }
+ if (outsidecaller)
+ ast_play_and_wait(chan, "vm-goodbye");
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+/*! \brief Run external notification for voicemail message */
+static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
+{
+ char arguments[BUFSIZ];
+
+ if (ast_strlen_zero(vmu->externnotify) && ast_strlen_zero(global_externnotify))
+ return;
+
+ snprintf(arguments, sizeof(arguments), "%s %s@%s %s %s&",
+ ast_strlen_zero(vmu->externnotify) ? global_externnotify : vmu->externnotify,
+ vmu->username, vmu->domain,
+ chan->cid.cid_name, chan->cid.cid_num);
+
+ ast_debug(1, "Executing: %s\n", arguments);
+ ast_safe_system(arguments);
+}
+
+/*! \brief Send message to voicemail account owner */
+static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname)
+{
+ char *stringp;
+ struct minivm_template *etemplate;
+ char *messageformat;
+ int res = 0;
+ char oldlocale[100];
+ const char *counter;
+
+ if (!ast_strlen_zero(vmu->attachfmt)) {
+ if (strstr(format, vmu->attachfmt)) {
+ format = vmu->attachfmt;
+ } else
+ ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, format, vmu->username, vmu->domain);
+ }
+
+ etemplate = message_template_find(vmu->etemplate);
+ if (!etemplate)
+ etemplate = message_template_find(templatename);
+ if (!etemplate)
+ etemplate = message_template_find("email-default");
+
+ /* Attach only the first format */
+ stringp = messageformat = ast_strdupa(format);
+ strsep(&stringp, "|");
+
+ if (!ast_strlen_zero(etemplate->locale)) {
+ char *newlocale;
+ ast_copy_string(oldlocale, setlocale(LC_TIME, NULL), sizeof(oldlocale));
+ ast_debug(2, "-_-_- Changing locale from %s to %s\n", oldlocale, etemplate->locale);
+ newlocale = setlocale(LC_TIME, etemplate->locale);
+ if (newlocale == NULL) {
+ ast_log(LOG_WARNING, "-_-_- Changing to new locale did not work. Locale: %s\n", etemplate->locale);
+ }
+ }
+
+
+
+ /* Read counter if available */
+ counter = pbx_builtin_getvar_helper(chan, "MVM_COUNTER");
+ if (ast_strlen_zero(counter)) {
+ ast_debug(2, "-_-_- MVM_COUNTER not found\n");
+ } else {
+ ast_debug(2, "-_-_- MVM_COUNTER found - will use it with value %s\n", counter);
+ }
+
+ res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_EMAIL, counter);
+
+ if (res == 0 && !ast_strlen_zero(vmu->pager)) {
+ /* Find template for paging */
+ etemplate = message_template_find(vmu->ptemplate);
+ if (!etemplate)
+ etemplate = message_template_find("pager-default");
+ if (etemplate->locale) {
+ ast_copy_string(oldlocale, setlocale(LC_TIME, ""), sizeof(oldlocale));
+ setlocale(LC_TIME, etemplate->locale);
+ }
+
+ res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter);
+ }
+
+ manager_event(EVENT_FLAG_CALL, "MiniVoiceMail", "Action: SentNotification\rn\nMailbox: %s@%s\r\nCounter: %s\r\n", vmu->username, vmu->domain, counter);
+
+ run_externnotify(chan, vmu); /* Run external notification */
+
+ if (etemplate->locale)
+ setlocale(LC_TIME, oldlocale); /* Rest to old locale */
+ return res;
+}
+
+
+/*! \brief Record voicemail message, store into file prepared for sending e-mail */
+static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options)
+{
+ char tmptxtfile[PATH_MAX];
+ char callerid[256];
+ FILE *txt;
+ int res = 0, txtdes;
+ int msgnum;
+ int duration = 0;
+ char date[256];
+ char tmpdir[PATH_MAX];
+ char ext_context[256] = "";
+ char fmt[80];
+ char *domain;
+ char tmp[256] = "";
+ struct minivm_account *vmu;
+ int userdir;
+
+ ast_copy_string(tmp, username, sizeof(tmp));
+ username = tmp;
+ domain = strchr(tmp, '@');
+ if (domain) {
+ *domain = '\0';
+ domain++;
+ }
+
+ if (!(vmu = find_account(domain, username, TRUE))) {
+ /* We could not find user, let's exit */
+ ast_log(LOG_ERROR, "Can't allocate temporary account for '%s@%s'\n", username, domain);
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
+ return 0;
+ }
+
+ /* Setup pre-file if appropriate */
+ if (strcmp(vmu->domain, "localhost"))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
+ else
+ ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
+
+ /* The meat of recording the message... All the announcements and beeps have been played*/
+ if (ast_strlen_zero(vmu->attachfmt))
+ ast_copy_string(fmt, default_vmformat, sizeof(fmt));
+ else
+ ast_copy_string(fmt, vmu->attachfmt, sizeof(fmt));
+
+ if (ast_strlen_zero(fmt)) {
+ ast_log(LOG_WARNING, "No format for saving voicemail? Default %s\n", default_vmformat);
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
+ return res;
+ }
+ msgnum = 0;
+
+ userdir = check_dirpath(tmpdir, sizeof(tmpdir), vmu->domain, username, "tmp");
+
+ /* If we have no user directory, use generic temporary directory */
+ if (!userdir) {
+ create_dirpath(tmpdir, sizeof(tmpdir), "0000_minivm_temp", "mediafiles", "");
+ ast_debug(3, "Creating temporary directory %s\n", tmpdir);
+ }
+
+
+ snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
+
+
+ /* XXX This file needs to be in temp directory */
+ txtdes = mkstemp(tmptxtfile);
+ if (txtdes < 0) {
+ ast_log(LOG_ERROR, "Unable to create message file %s: %s\n", tmptxtfile, strerror(errno));
+ res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
+ return res;
+ }
+
+ if (res >= 0) {
+ /* Unless we're *really* silent, try to send the beep */
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ }
+
+ /* OEJ XXX Maybe this can be turned into a log file? Hmm. */
+ /* Store information */
+ ast_debug(2, "Open file for metadata: %s\n", tmptxtfile);
+
+ res = play_record_review(chan, NULL, tmptxtfile, global_vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
+
+ txt = fdopen(txtdes, "w+");
+ if (!txt) {
+ ast_log(LOG_WARNING, "Error opening text file for output\n");
+ } else {
+ struct ast_tm tm;
+ struct timeval now = ast_tvnow();
+ char timebuf[30];
+ char logbuf[BUFSIZ];
+ get_date(date, sizeof(date));
+ ast_localtime(&now, &tm, NULL);
+ ast_strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
+
+ snprintf(logbuf, sizeof(logbuf),
+ /* "Mailbox:domain:macrocontext:exten:priority:callerchan:callerid:origdate:origtime:duration:durationstatus:accountcode" */
+ "%s:%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
+ username,
+ chan->context,
+ chan->macrocontext,
+ chan->exten,
+ chan->priority,
+ chan->name,
+ ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
+ date,
+ timebuf,
+ duration,
+ duration < global_vmminmessage ? "IGNORED" : "OK",
+ vmu->accountcode
+ );
+ fprintf(txt, logbuf);
+ if (minivmlogfile) {
+ ast_mutex_lock(&minivmloglock);
+ fprintf(minivmlogfile, logbuf);
+ ast_mutex_unlock(&minivmloglock);
+ }
+
+ if (duration < global_vmminmessage) {
+ ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, global_vmminmessage);
+ fclose(txt);
+ ast_filedelete(tmptxtfile, NULL);
+ unlink(tmptxtfile);
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
+ return 0;
+ }
+ fclose(txt); /* Close log file */
+ if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
+ ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
+ unlink(tmptxtfile);
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
+ if(ast_test_flag(vmu, MVM_ALLOCED))
+ free_user(vmu);
+ return 0;
+ }
+
+ /* Set channel variables for the notify application */
+ pbx_builtin_setvar_helper(chan, "MVM_FILENAME", tmptxtfile);
+ snprintf(timebuf, sizeof(timebuf), "%d", duration);
+ pbx_builtin_setvar_helper(chan, "MVM_DURATION", timebuf);
+ pbx_builtin_setvar_helper(chan, "MVM_FORMAT", fmt);
+
+ }
+ global_stats.lastreceived = ast_tvnow();
+ global_stats.receivedmessages++;
+// /* Go ahead and delete audio files from system, they're not needed any more */
+// if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
+// ast_filedelete(tmptxtfile, NULL);
+// /* Even not being used at the moment, it's better to convert ast_log to ast_debug anyway */
+// ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile);
+// }
+
+ if (res > 0)
+ res = 0;
+
+ if(ast_test_flag(vmu, MVM_ALLOCED))
+ free_user(vmu);
+
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "SUCCESS");
+ return res;
+}
+
+/*! \brief Notify voicemail account owners - either generic template or user specific */
+static int minivm_notify_exec(struct ast_channel *chan, void *data)
+{
+ int argc;
+ char *argv[2];
+ int res = 0;
+ char tmp[PATH_MAX];
+ char *domain;
+ char *tmpptr;
+ struct minivm_account *vmu;
+ char *username = argv[0];
+ const char *template = "";
+ const char *filename;
+ const char *format;
+ const char *duration_string;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
+ return -1;
+ }
+ tmpptr = ast_strdupa((char *)data);
+ if (!tmpptr) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+ argc = ast_app_separate_args(tmpptr, ',', argv, sizeof(argv) / sizeof(argv[0]));
+
+ if (argc == 2 && !ast_strlen_zero(argv[1]))
+ template = argv[1];
+
+ ast_copy_string(tmp, argv[0], sizeof(tmp));
+ username = tmp;
+ domain = strchr(tmp, '@');
+ if (domain) {
+ *domain = '\0';
+ domain++;
+ }
+ if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
+ ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
+ return -1;
+ }
+
+ if(!(vmu = find_account(domain, username, TRUE))) {
+ /* We could not find user, let's exit */
+ ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
+ pbx_builtin_setvar_helper(chan, "MINIVM_NOTIFY_STATUS", "FAILED");
+ return -1;
+ }
+
+ filename = pbx_builtin_getvar_helper(chan, "MVM_FILENAME");
+ format = pbx_builtin_getvar_helper(chan, "MVM_FORMAT");
+ duration_string = pbx_builtin_getvar_helper(chan, "MVM_DURATION");
+ /* Notify of new message to e-mail and pager */
+ if (!ast_strlen_zero(filename)) {
+ res = notify_new_message(chan, template, vmu, filename, atoi(duration_string), format, chan->cid.cid_num, chan->cid.cid_name);
+ };
+
+ pbx_builtin_setvar_helper(chan, "MINIVM_NOTIFY_STATUS", res == 0 ? "SUCCESS" : "FAILED");
+
+
+ if(ast_test_flag(vmu, MVM_ALLOCED))
+ free_user(vmu);
+
+ /* Ok, we're ready to rock and roll. Return to dialplan */
+
+ return res;
+
+}
+
+/*! \brief Dialplan function to record voicemail */
+static int minivm_record_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *tmp;
+ struct leave_vm_options leave_options;
+ int argc;
+ char *argv[2];
+ struct ast_flags flags = { 0 };
+ char *opts[OPT_ARG_ARRAY_SIZE];
+
+ memset(&leave_options, 0, sizeof(leave_options));
+
+ /* Answer channel if it's not already answered */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
+ return -1;
+ }
+ tmp = ast_strdupa((char *)data);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+ argc = ast_app_separate_args(tmp, ',', argv, sizeof(argv) / sizeof(argv[0]));
+ if (argc == 2) {
+ if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1])) {
+ return -1;
+ }
+ ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
+ if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
+ int gain;
+
+ if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
+ ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
+ return -1;
+ } else
+ leave_options.record_gain = (signed char) gain;
+ }
+ }
+
+ /* Now run the appliation and good luck to you! */
+ res = leave_voicemail(chan, argv[0], &leave_options);
+
+ if (res == ERROR_LOCK_PATH) {
+ ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "FAILED");
+ res = 0;
+ }
+ pbx_builtin_setvar_helper(chan, "MINIVM_RECORD_STATUS", "SUCCESS");
+
+ return res;
+}
+
+/*! \brief Play voicemail prompts - either generic or user specific */
+static int minivm_greet_exec(struct ast_channel *chan, void *data)
+{
+ struct leave_vm_options leave_options = { 0, '\0'};
+ int argc;
+ char *argv[2];
+ struct ast_flags flags = { 0 };
+ char *opts[OPT_ARG_ARRAY_SIZE];
+ int res = 0;
+ int ausemacro = 0;
+ int ousemacro = 0;
+ int ouseexten = 0;
+ char tmp[PATH_MAX];
+ char dest[PATH_MAX];
+ char prefile[PATH_MAX];
+ char tempfile[PATH_MAX] = "";
+ char ext_context[256] = "";
+ char *domain;
+ char ecodes[16] = "#";
+ char *tmpptr;
+ struct minivm_account *vmu;
+ char *username = argv[0];
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
+ return -1;
+ }
+ tmpptr = ast_strdupa((char *)data);
+ if (!tmpptr) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+ argc = ast_app_separate_args(tmpptr, ',', argv, sizeof(argv) / sizeof(argv[0]));
+
+ if (argc == 2) {
+ if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1]))
+ return -1;
+ ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
+ }
+
+ ast_copy_string(tmp, argv[0], sizeof(tmp));
+ username = tmp;
+ domain = strchr(tmp, '@');
+ if (domain) {
+ *domain = '\0';
+ domain++;
+ }
+ if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
+ ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument: %s\n", argv[0]);
+ return -1;
+ }
+ ast_debug(1, "-_-_- Trying to find configuration for user %s in domain %s\n", username, domain);
+
+ if (!(vmu = find_account(domain, username, TRUE))) {
+ ast_log(LOG_ERROR, "Could not allocate memory. \n");
+ return -1;
+ }
+
+ /* Answer channel if it's not already answered */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ /* Setup pre-file if appropriate */
+ if (strcmp(vmu->domain, "localhost"))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
+ else
+ ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
+
+ if (ast_test_flag(&leave_options, OPT_BUSY_GREETING)) {
+ res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "busy");
+ if (res)
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", MVM_SPOOL_DIR, vmu->domain, username);
+ } else if (ast_test_flag(&leave_options, OPT_UNAVAIL_GREETING)) {
+ res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "unavail");
+ if (res)
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", MVM_SPOOL_DIR, vmu->domain, username);
+ }
+ /* Check for temporary greeting - it overrides busy and unavail */
+ snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", MVM_SPOOL_DIR, vmu->domain, username);
+ if (!(res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "temp"))) {
+ ast_debug(2, "Temporary message directory does not exist, using default (%s)\n", tempfile);
+ ast_copy_string(prefile, tempfile, sizeof(prefile));
+ }
+ ast_debug(2, "-_-_- Preparing to play message ...\n");
+
+ /* Check current or macro-calling context for special extensions */
+ if (ast_test_flag(vmu, MVM_OPERATOR)) {
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ouseexten = 1;
+ }
+ } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ouseexten = 1;
+ }
+ else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ousemacro = 1;
+ }
+ }
+
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ ausemacro = 1;
+ }
+
+ res = 0; /* Reset */
+ /* Play the beginning intro if desired */
+ if (!ast_strlen_zero(prefile)) {
+ if (ast_streamfile(chan, prefile, chan->language) > -1)
+ res = ast_waitstream(chan, ecodes);
+ } else {
+ ast_debug(2, "%s doesn't exist, doing what we can\n", prefile);
+ res = invent_message(chan, vmu->domain, username, ast_test_flag(&leave_options, OPT_BUSY_GREETING), ecodes);
+ }
+ if (res < 0) {
+ ast_debug(2, "Hang up during prefile playback\n");
+ pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "FAILED");
+ if(ast_test_flag(vmu, MVM_ALLOCED))
+ free_user(vmu);
+ return -1;
+ }
+ if (res == '#') {
+ /* On a '#' we skip the instructions */
+ ast_set_flag(&leave_options, OPT_SILENT);
+ res = 0;
+ }
+ if (!res && !ast_test_flag(&leave_options, OPT_SILENT)) {
+ res = ast_streamfile(chan, SOUND_INTRO, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, ecodes);
+ if (res == '#') {
+ ast_set_flag(&leave_options, OPT_SILENT);
+ res = 0;
+ }
+ }
+ if (res > 0)
+ ast_stopstream(chan);
+ /* Check for a '*' here in case the caller wants to escape from voicemail to something
+ other than the operator -- an automated attendant or mailbox login for example */
+ if (res == '*') {
+ chan->exten[0] = 'a';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ chan->priority = 0;
+ pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "USEREXIT");
+ res = 0;
+ } else if (res == '0') { /* Check for a '0' here */
+ if(ouseexten || ousemacro) {
+ chan->exten[0] = 'o';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ ast_play_and_wait(chan, "transfer");
+ chan->priority = 0;
+ pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "USEREXIT");
+ }
+ res = 0;
+ } else if (res < 0) {
+ pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "FAILED");
+ res = -1;
+ } else
+ pbx_builtin_setvar_helper(chan, "MINIVM_GREET_STATUS", "SUCCESS");
+
+ if(ast_test_flag(vmu, MVM_ALLOCED))
+ free_user(vmu);
+
+
+ /* Ok, we're ready to rock and roll. Return to dialplan */
+ return res;
+
+}
+
+/*! \brief Dialplan application to delete voicemail */
+static int minivm_delete_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char filename[BUFSIZ];
+
+ if (!ast_strlen_zero(data))
+ ast_copy_string(filename, (char *) data, sizeof(filename));
+ else
+ ast_copy_string(filename, pbx_builtin_getvar_helper(chan, "MVM_FILENAME"), sizeof(filename));
+
+ if (ast_strlen_zero(filename)) {
+ ast_log(LOG_ERROR, "No filename given in application arguments or channel variable MVM_FILENAME\n");
+ return res;
+ }
+
+ /* Go ahead and delete audio files from system, they're not needed any more */
+ /* We should look for both audio and text files here */
+ if (ast_fileexists(filename, NULL, NULL) > 0) {
+ res = vm_delete(filename);
+ if (res) {
+ ast_debug(2, "-_-_- Can't delete file: %s\n", filename);
+ pbx_builtin_setvar_helper(chan, "MINIVM_DELETE_STATUS", "FAILED");
+ } else {
+ ast_debug(2, "-_-_- Deleted voicemail file :: %s \n", filename);
+ pbx_builtin_setvar_helper(chan, "MINIVM_DELETE_STATUS", "SUCCESS");
+ }
+ } else {
+ ast_debug(2, "-_-_- Filename does not exist: %s\n", filename);
+ pbx_builtin_setvar_helper(chan, "MINIVM_DELETE_STATUS", "FAILED");
+ }
+
+ return res;
+}
+
+/*! \brief Record specific messages for voicemail account */
+static int minivm_accmess_exec(struct ast_channel *chan, void *data)
+{
+ int argc = 0;
+ char *argv[2];
+ int res = 0;
+ char filename[PATH_MAX];
+ char tmp[PATH_MAX];
+ char *domain;
+ char *tmpptr = NULL;
+ struct minivm_account *vmu;
+ char *username = argv[0];
+ struct ast_flags flags = { 0 };
+ char *opts[OPT_ARG_ARRAY_SIZE];
+ int error = FALSE;
+ char *message = NULL;
+ char *prompt = NULL;
+ int duration;
+ int cmd;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
+ error = TRUE;
+ } else
+ tmpptr = ast_strdupa((char *)data);
+ if (!error) {
+ if (!tmpptr) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ error = TRUE;
+ } else
+ argc = ast_app_separate_args(tmpptr, ',', argv, sizeof(argv) / sizeof(argv[0]));
+ }
+
+ if (argc <=1) {
+ ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
+ error = TRUE;
+ }
+ if (!error && strlen(argv[1]) > 1) {
+ ast_log(LOG_ERROR, "MinivmAccmess can only handle one option at a time. Bad option string: %s\n", argv[1]);
+ error = TRUE;
+ }
+
+ if (!error && ast_app_parse_options(minivm_accmess_options, &flags, opts, argv[1])) {
+ ast_log(LOG_ERROR, "Can't parse option %s\n", argv[1]);
+ error = TRUE;
+ }
+
+ if (error)
+ return -1;
+
+ ast_copy_string(tmp, argv[0], sizeof(tmp));
+ username = tmp;
+ domain = strchr(tmp, '@');
+ if (domain) {
+ *domain = '\0';
+ domain++;
+ }
+ if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
+ ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
+ return -1;
+ }
+
+ if(!(vmu = find_account(domain, username, TRUE))) {
+ /* We could not find user, let's exit */
+ ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
+ pbx_builtin_setvar_helper(chan, "MINIVM_NOTIFY_STATUS", "FAILED");
+ return -1;
+ }
+
+ /* Answer channel if it's not already answered */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ /* Here's where the action is */
+ if (ast_test_flag(&flags, OPT_BUSY_GREETING)) {
+ message = "busy";
+ prompt = "vm-rec-busy";
+ } else if (ast_test_flag(&flags, OPT_UNAVAIL_GREETING)) {
+ message = "unavailable";
+ prompt = "vm-rec-unavail";
+ } else if (ast_test_flag(&flags, OPT_TEMP_GREETING)) {
+ message = "temp";
+ prompt = "vm-temp-greeting";
+ } else if (ast_test_flag(&flags, OPT_NAME_GREETING)) {
+ message = "greet";
+ prompt = "vm-rec-name";
+ }
+ snprintf(filename,sizeof(filename), "%s%s/%s/%s", MVM_SPOOL_DIR, vmu->domain, vmu->username, message);
+ /* Maybe we should check the result of play_record_review ? */
+ cmd = play_record_review(chan, prompt, filename, global_maxgreet, default_vmformat, 0, vmu, &duration, NULL, FALSE);
+
+ ast_debug(1, "Recorded new %s message in %s (duration %d)\n", message, filename, duration);
+
+ if(ast_test_flag(vmu, MVM_ALLOCED))
+ free_user(vmu);
+
+
+ /* Ok, we're ready to rock and roll. Return to dialplan */
+ return res;
+
+}
+
+/*! \brief Append new mailbox to mailbox list from configuration file */
+static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
+{
+ struct minivm_account *vmu;
+ char *domain;
+ char *username;
+ char accbuf[BUFSIZ];
+
+ ast_debug(3, "Creating %s account for [%s]\n", realtime ? "realtime" : "static", name);
+
+ ast_copy_string(accbuf, name, sizeof(accbuf));
+ username = accbuf;
+ domain = strchr(accbuf, '@');
+ if (domain) {
+ *domain = '\0';
+ domain++;
+ }
+ if (ast_strlen_zero(domain)) {
+ ast_log(LOG_ERROR, "No domain given for mini-voicemail account %s. Not configured.\n", name);
+ return 0;
+ }
+
+ ast_debug(3, "Creating static account for user %s domain %s\n", username, domain);
+
+ /* Allocate user account */
+ vmu = ast_calloc(1, sizeof(*vmu));
+ if (!vmu)
+ return 0;
+
+ ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
+ ast_copy_string(vmu->username, username, sizeof(vmu->username));
+
+ populate_defaults(vmu);
+
+ ast_debug(3, "...Configuring account %s\n", name);
+
+ while (var) {
+ ast_debug(3, "---- Configuring %s = \"%s\" for account %s\n", var->name, var->value, name);
+ if (!strcasecmp(var->name, "serveremail")) {
+ ast_copy_string(vmu->serveremail, var->value, sizeof(vmu->serveremail));
+ } else if (!strcasecmp(var->name, "email")) {
+ ast_copy_string(vmu->email, var->value, sizeof(vmu->email));
+ } else if (!strcasecmp(var->name, "accountcode")) {
+ ast_copy_string(vmu->accountcode, var->value, sizeof(vmu->accountcode));
+ } else if (!strcasecmp(var->name, "pincode")) {
+ ast_copy_string(vmu->pincode, var->value, sizeof(vmu->pincode));
+ } else if (!strcasecmp(var->name, "domain")) {
+ ast_copy_string(vmu->domain, var->value, sizeof(vmu->domain));
+ } else if (!strcasecmp(var->name, "language")) {
+ ast_copy_string(vmu->language, var->value, sizeof(vmu->language));
+ } else if (!strcasecmp(var->name, "timezone")) {
+ ast_copy_string(vmu->zonetag, var->value, sizeof(vmu->zonetag));
+ } else if (!strcasecmp(var->name, "externnotify")) {
+ ast_copy_string(vmu->externnotify, var->value, sizeof(vmu->externnotify));
+ } else if (!strcasecmp(var->name, "etemplate")) {
+ ast_copy_string(vmu->etemplate, var->value, sizeof(vmu->etemplate));
+ } else if (!strcasecmp(var->name, "ptemplate")) {
+ ast_copy_string(vmu->ptemplate, var->value, sizeof(vmu->ptemplate));
+ } else if (!strcasecmp(var->name, "fullname")) {
+ ast_copy_string(vmu->fullname, var->value, sizeof(vmu->fullname));
+ } else if (!strcasecmp(var->name, "setvar")) {
+ char *varval;
+ char *varname = ast_strdupa(var->value);
+ struct ast_variable *tmpvar;
+
+ if (varname && (varval = strchr(varname, '='))) {
+ *varval = '\0';
+ varval++;
+ if ((tmpvar = ast_variable_new(varname, varval, ""))) {
+ tmpvar->next = vmu->chanvars;
+ vmu->chanvars = tmpvar;
+ }
+ }
+ } else if (!strcasecmp(var->name, "pager")) {
+ ast_copy_string(vmu->pager, var->value, sizeof(vmu->pager));
+ } else if (!strcasecmp(var->name, "volgain")) {
+ sscanf(var->value, "%lf", &vmu->volgain);
+ } else {
+ ast_log(LOG_ERROR, "Unknown configuration option for minivm account %s : %s\n", name, var->name);
+ }
+ var = var->next;
+ }
+ ast_debug(3, "...Linking account %s\n", name);
+
+ AST_LIST_LOCK(&minivm_accounts);
+ AST_LIST_INSERT_TAIL(&minivm_accounts, vmu, list);
+ AST_LIST_UNLOCK(&minivm_accounts);
+
+ global_stats.voicemailaccounts++;
+
+ ast_debug(2, "MINIVM :: Created account %s@%s - tz %s etemplate %s %s\n", username, domain, ast_strlen_zero(vmu->zonetag) ? "" : vmu->zonetag, ast_strlen_zero(vmu->etemplate) ? "" : vmu->etemplate, realtime ? "(realtime)" : "");
+ return 0;
+}
+
+/*! \brief Free Mini Voicemail timezone */
+static void free_zone(struct minivm_zone *z)
+{
+ ast_free(z);
+}
+
+/*! \brief Clear list of timezones */
+static void timezone_destroy_list(void)
+{
+ struct minivm_zone *this;
+
+ AST_LIST_LOCK(&minivm_zones);
+ while ((this = AST_LIST_REMOVE_HEAD(&minivm_zones, list)))
+ free_zone(this);
+
+ AST_LIST_UNLOCK(&minivm_zones);
+}
+
+/*! \brief Add time zone to memory list */
+static int timezone_add(const char *zonename, const char *config)
+{
+
+ struct minivm_zone *newzone;
+ char *msg_format, *timezone;
+
+ newzone = ast_calloc(1, sizeof(*newzone));
+ if (newzone == NULL)
+ return 0;
+
+ msg_format = ast_strdupa(config);
+ if (msg_format == NULL) {
+ ast_log(LOG_WARNING, "Out of memory.\n");
+ ast_free(newzone);
+ return 0;
+ }
+
+ timezone = strsep(&msg_format, "|");
+ if (!msg_format) {
+ ast_log(LOG_WARNING, "Invalid timezone definition : %s\n", zonename);
+ ast_free(newzone);
+ return 0;
+ }
+
+ ast_copy_string(newzone->name, zonename, sizeof(newzone->name));
+ ast_copy_string(newzone->timezone, timezone, sizeof(newzone->timezone));
+ ast_copy_string(newzone->msg_format, msg_format, sizeof(newzone->msg_format));
+
+ AST_LIST_LOCK(&minivm_zones);
+ AST_LIST_INSERT_TAIL(&minivm_zones, newzone, list);
+ AST_LIST_UNLOCK(&minivm_zones);
+
+ global_stats.timezones++;
+
+ return 0;
+}
+
+/*! \brief Read message template from file */
+static char *message_template_parse_filebody(const char *filename) {
+ char buf[BUFSIZ * 6];
+ char readbuf[BUFSIZ];
+ char filenamebuf[BUFSIZ];
+ char *writepos;
+ char *messagebody;
+ FILE *fi;
+ int lines = 0;
+
+ if (ast_strlen_zero(filename))
+ return NULL;
+ if (*filename == '/')
+ ast_copy_string(filenamebuf, filename, sizeof(filenamebuf));
+ else
+ snprintf(filenamebuf, sizeof(filenamebuf), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
+
+ if (!(fi = fopen(filenamebuf, "r"))) {
+ ast_log(LOG_ERROR, "Can't read message template from file: %s\n", filenamebuf);
+ return NULL;
+ }
+ writepos = buf;
+ while (fgets(readbuf, sizeof(readbuf), fi)) {
+ lines ++;
+ if (writepos != buf) {
+ *writepos = '\n'; /* Replace EOL with new line */
+ writepos++;
+ }
+ ast_copy_string(writepos, readbuf, sizeof(buf) - (writepos - buf));
+ writepos += strlen(readbuf) - 1;
+ }
+ fclose(fi);
+ messagebody = ast_calloc(1, strlen(buf + 1));
+ ast_copy_string(messagebody, buf, strlen(buf) + 1);
+ ast_debug(4, "---> Size of allocation %d\n", (int) strlen(buf + 1) );
+ ast_debug(4, "---> Done reading message template : \n%s\n---- END message template--- \n", messagebody);
+
+ return messagebody;
+}
+
+/*! \brief Parse emailbody template from configuration file */
+static char *message_template_parse_emailbody(const char *configuration)
+{
+ char *tmpread, *tmpwrite;
+ char *emailbody = ast_strdup(configuration);
+
+ /* substitute strings \t and \n into the apropriate characters */
+ tmpread = tmpwrite = emailbody;
+ while ((tmpwrite = strchr(tmpread,'\\'))) {
+ int len = strlen("\n");
+ switch (tmpwrite[1]) {
+ case 'n':
+ memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ strncpy(tmpwrite, "\n", len);
+ break;
+ case 't':
+ memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ strncpy(tmpwrite, "\t", len);
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
+ }
+ tmpread = tmpwrite + len;
+ }
+ return emailbody;
+}
+
+/*! \brief Apply general configuration options */
+static int apply_general_options(struct ast_variable *var)
+{
+ int error = 0;
+
+ while (var) {
+ /* Mail command */
+ if (!strcmp(var->name, "mailcmd")) {
+ ast_copy_string(global_mailcmd, var->value, sizeof(global_mailcmd)); /* User setting */
+ } else if (!strcmp(var->name, "maxgreet")) {
+ global_maxgreet = atoi(var->value);
+ } else if (!strcmp(var->name, "maxsilence")) {
+ global_maxsilence = atoi(var->value);
+ if (global_maxsilence > 0)
+ global_maxsilence *= 1000;
+ } else if (!strcmp(var->name, "logfile")) {
+ if (!ast_strlen_zero(var->value) ) {
+ if(*(var->value) == '/')
+ ast_copy_string(global_logfile, var->value, sizeof(global_logfile));
+ else
+ snprintf(global_logfile, sizeof(global_logfile), "%s/%s", ast_config_AST_LOG_DIR, var->value);
+ }
+ } else if (!strcmp(var->name, "externnotify")) {
+ /* External voicemail notify application */
+ ast_copy_string(global_externnotify, var->value, sizeof(global_externnotify));
+ } else if (!strcmp(var->name, "silencetreshold")) {
+ /* Silence treshold */
+ global_silencethreshold = atoi(var->value);
+ } else if (!strcmp(var->name, "maxmessage")) {
+ int x;
+ if (sscanf(var->value, "%d", &x) == 1) {
+ global_vmmaxmessage = x;
+ } else {
+ error ++;
+ ast_log(LOG_WARNING, "Invalid max message time length\n");
+ }
+ } else if (!strcmp(var->name, "minmessage")) {
+ int x;
+ if (sscanf(var->value, "%d", &x) == 1) {
+ global_vmminmessage = x;
+ if (global_maxsilence <= global_vmminmessage)
+ ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
+ } else {
+ error ++;
+ ast_log(LOG_WARNING, "Invalid min message time length\n");
+ }
+ } else if (!strcmp(var->name, "format")) {
+ ast_copy_string(default_vmformat, var->value, sizeof(default_vmformat));
+ } else if (!strcmp(var->name, "review")) {
+ ast_set2_flag((&globalflags), ast_true(var->value), MVM_REVIEW);
+ } else if (!strcmp(var->name, "operator")) {
+ ast_set2_flag((&globalflags), ast_true(var->value), MVM_OPERATOR);
+ }
+ var = var->next;
+ }
+ return error;
+}
+
+/*! \brief Load minivoicemail configuration */
+static int load_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ char *cat;
+ const char *chanvar;
+ int error = 0;
+ struct minivm_template *template;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ ast_mutex_lock(&minivmlock);
+
+ /* Destroy lists to reconfigure */
+ message_destroy_list(); /* Destroy list of voicemail message templates */
+ timezone_destroy_list(); /* Destroy list of timezones */
+ vmaccounts_destroy_list(); /* Destroy list of voicemail accounts */
+ ast_debug(2, "Destroyed memory objects...\n");
+
+ /* First, set some default settings */
+ global_externnotify[0] = '\0';
+ global_logfile[0] = '\0';
+ global_silencethreshold = 256;
+ global_vmmaxmessage = 2000;
+ global_maxgreet = 2000;
+ global_vmminmessage = 0;
+ strcpy(global_mailcmd, SENDMAIL);
+ global_maxsilence = 0;
+ global_saydurationminfo = 2;
+ ast_copy_string(default_vmformat, "wav", sizeof(default_vmformat));
+ ast_set2_flag((&globalflags), FALSE, MVM_REVIEW);
+ ast_set2_flag((&globalflags), FALSE, MVM_OPERATOR);
+ strcpy(global_charset, "ISO-8859-1");
+ /* Reset statistics */
+ memset(&global_stats, 0, sizeof(global_stats));
+ global_stats.reset = ast_tvnow();
+
+ /* Make sure we could load configuration file */
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Failed to load configuration file. Module activated with default settings.\n");
+ ast_mutex_unlock(&minivmlock);
+ return 0;
+ }
+
+ ast_debug(2, "-_-_- Loaded configuration file, now parsing\n");
+
+ /* General settings */
+
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
+ ast_debug(3, "-_-_- Found configuration section [%s]\n", cat);
+ if (!strcasecmp(cat, "general")) {
+ /* Nothing right now */
+ error += apply_general_options(ast_variable_browse(cfg, cat));
+ } else if (!strncasecmp(cat, "template-", 9)) {
+ /* Template */
+ char *name = cat + 9;
+
+ /* Now build and link template to list */
+ error += message_template_build(name, ast_variable_browse(cfg, cat));
+ } else {
+ var = ast_variable_browse(cfg, cat);
+ if (!strcasecmp(cat, "zonemessages")) {
+ /* Timezones in this context */
+ while (var) {
+ timezone_add(var->name, var->value);
+ var = var->next;
+ }
+ } else {
+ /* Create mailbox from this */
+ error += create_vmaccount(cat, var, FALSE);
+ }
+ }
+ /* Find next section in configuration file */
+ cat = ast_category_browse(cfg, cat);
+ }
+
+ /* Configure the default email template */
+ message_template_build("email-default", NULL);
+ template = message_template_find("email-default");
+
+ /* Load date format config for voicemail mail */
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "emaildateformat")))
+ ast_copy_string(template->dateformat, chanvar, sizeof(template->dateformat));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "emailfromstring")))
+ ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "emailaaddress")))
+ ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "emailcharset")))
+ ast_copy_string(template->charset, chanvar, sizeof(template->charset));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "emailsubject")))
+ ast_copy_string(template->subject, chanvar, sizeof(template->subject));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "emailbody")))
+ template->body = message_template_parse_emailbody(chanvar);
+ template->attachment = TRUE;
+
+ message_template_build("pager-default", NULL);
+ template = message_template_find("pager-default");
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
+ ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "pageraddress")))
+ ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "pagercharset")))
+ ast_copy_string(template->charset, chanvar, sizeof(template->charset));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "pagersubject")))
+ ast_copy_string(template->subject, chanvar,sizeof(template->subject));
+ if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerbody")))
+ template->body = message_template_parse_emailbody(chanvar);
+ template->attachment = FALSE;
+
+ if (error)
+ ast_log(LOG_ERROR, "--- A total of %d errors found in mini-voicemail configuration\n", error);
+
+ ast_mutex_unlock(&minivmlock);
+ ast_config_destroy(cfg);
+
+ /* Close log file if it's open and disabled */
+ if(minivmlogfile)
+ fclose(minivmlogfile);
+
+ /* Open log file if it's enabled */
+ if(!ast_strlen_zero(global_logfile)) {
+ minivmlogfile = fopen(global_logfile, "a");
+ if(!minivmlogfile)
+ ast_log(LOG_ERROR, "Failed to open minivm log file %s : %s\n", global_logfile, strerror(errno));
+ if (minivmlogfile)
+ ast_debug(3, "-_-_- Opened log file %s \n", global_logfile);
+ }
+
+ return 0;
+}
+
+/*! \brief CLI routine for listing templates */
+static char *handle_minivm_list_templates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct minivm_template *this;
+ char *output_format = "%-15s %-10s %-10s %-15.15s %-50s\n";
+ int count = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "minivm list templates";
+ e->usage =
+ "Usage: minivm list templates\n"
+ " Lists message templates for e-mail, paging and IM\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&message_templates);
+ if (AST_LIST_EMPTY(&message_templates)) {
+ ast_cli(a->fd, "There are no message templates defined\n");
+ AST_LIST_UNLOCK(&message_templates);
+ return CLI_FAILURE;
+ }
+ ast_cli(a->fd, output_format, "Template name", "Charset", "Locale", "Attach media", "Subject");
+ ast_cli(a->fd, output_format, "-------------", "-------", "------", "------------", "-------");
+ AST_LIST_TRAVERSE(&message_templates, this, list) {
+ ast_cli(a->fd, output_format, this->name,
+ this->charset ? this->charset : "-",
+ this->locale ? this->locale : "-",
+ this->attachment ? "Yes" : "No",
+ this->subject ? this->subject : "-");
+ count++;
+ }
+ AST_LIST_UNLOCK(&message_templates);
+ ast_cli(a->fd, "\n * Total: %d minivoicemail message templates\n", count);
+ return CLI_SUCCESS;
+}
+
+static char *complete_minivm_show_users(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ int wordlen;
+ struct minivm_account *vmu;
+ const char *domain = "";
+
+ /* 0 - voicemail; 1 - list; 2 - accounts; 3 - for; 4 - <domain> */
+ if (pos > 4)
+ return NULL;
+ if (pos == 3)
+ return (state == 0) ? ast_strdup("for") : NULL;
+ wordlen = strlen(word);
+ AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
+ if (!strncasecmp(word, vmu->domain, wordlen)) {
+ if (domain && strcmp(domain, vmu->domain) && ++which > state)
+ return ast_strdup(vmu->domain);
+ /* ignore repeated domains ? */
+ domain = vmu->domain;
+ }
+ }
+ return NULL;
+}
+
+/*! \brief CLI command to list voicemail accounts */
+static char *handle_minivm_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct minivm_account *vmu;
+ char *output_format = "%-23s %-15s %-15s %-10s %-10s %-50s\n";
+ int count = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "minivm list accounts";
+ e->usage =
+ "Usage: minivm list accounts\n"
+ " Lists all mailboxes currently set up\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_minivm_show_users(a->line, a->word, a->pos, a->n);
+ }
+
+ if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
+ return CLI_SHOWUSAGE;
+ if ((a->argc == 5) && strcmp(a->argv[3],"for"))
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&minivm_accounts);
+ if (AST_LIST_EMPTY(&minivm_accounts)) {
+ ast_cli(a->fd, "There are no voicemail users currently defined\n");
+ AST_LIST_UNLOCK(&minivm_accounts);
+ return CLI_FAILURE;
+ }
+ ast_cli(a->fd, output_format, "User", "E-Template", "P-template", "Zone", "Format", "Full name");
+ ast_cli(a->fd, output_format, "----", "----------", "----------", "----", "------", "---------");
+ AST_LIST_TRAVERSE(&minivm_accounts, vmu, list) {
+ char tmp[256] = "";
+ if ((a->argc == 3) || ((a->argc == 5) && !strcmp(a->argv[4], vmu->domain))) {
+ count++;
+ snprintf(tmp, sizeof(tmp), "%s@%s", vmu->username, vmu->domain);
+ ast_cli(a->fd, output_format, tmp, vmu->etemplate ? vmu->etemplate : "-",
+ vmu->ptemplate ? vmu->ptemplate : "-",
+ vmu->zonetag ? vmu->zonetag : "-",
+ vmu->attachfmt ? vmu->attachfmt : "-",
+ vmu->fullname);
+ }
+ }
+ AST_LIST_UNLOCK(&minivm_accounts);
+ ast_cli(a->fd, "\n * Total: %d minivoicemail accounts\n", count);
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show a list of voicemail zones in the CLI */
+static char *handle_minivm_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct minivm_zone *zone;
+ char *output_format = "%-15s %-20s %-45s\n";
+ char *res = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "minivm list zones";
+ e->usage =
+ "Usage: minivm list zones\n"
+ " Lists zone message formats\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&minivm_zones);
+ if (!AST_LIST_EMPTY(&minivm_zones)) {
+ ast_cli(a->fd, output_format, "Zone", "Timezone", "Message Format");
+ ast_cli(a->fd, output_format, "----", "--------", "--------------");
+ AST_LIST_TRAVERSE(&minivm_zones, zone, list) {
+ ast_cli(a->fd, output_format, zone->name, zone->timezone, zone->msg_format);
+ }
+ } else {
+ ast_cli(a->fd, "There are no voicemail zones currently defined\n");
+ res = CLI_FAILURE;
+ }
+ AST_LIST_UNLOCK(&minivm_zones);
+
+ return res;
+}
+
+/*! \brief CLI Show settings */
+static char *handle_minivm_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "minivm show settings";
+ e->usage =
+ "Usage: minivm show settings\n"
+ " Display Mini-Voicemail general settings\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "* Mini-Voicemail general settings\n");
+ ast_cli(a->fd, " -------------------------------\n");
+ ast_cli(a->fd, "\n");
+ ast_cli(a->fd, " Mail command (shell): %s\n", global_mailcmd);
+ ast_cli(a->fd, " Max silence: %d\n", global_maxsilence);
+ ast_cli(a->fd, " Silence treshold: %d\n", global_silencethreshold);
+ ast_cli(a->fd, " Max message length (secs): %d\n", global_vmmaxmessage);
+ ast_cli(a->fd, " Min message length (secs): %d\n", global_vmminmessage);
+ ast_cli(a->fd, " Default format: %s\n", default_vmformat);
+ ast_cli(a->fd, " Extern notify (shell): %s\n", global_externnotify);
+ ast_cli(a->fd, " Logfile: %s\n", global_logfile[0] ? global_logfile : "<disabled>");
+ ast_cli(a->fd, " Operator exit: %s\n", ast_test_flag(&globalflags, MVM_OPERATOR) ? "Yes" : "No");
+ ast_cli(a->fd, " Message review: %s\n", ast_test_flag(&globalflags, MVM_REVIEW) ? "Yes" : "No");
+
+ ast_cli(a->fd, "\n");
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show stats */
+static char *handle_minivm_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_tm time;
+ char buf[BUFSIZ];
+
+ switch (cmd) {
+
+ case CLI_INIT:
+ e->command = "minivm show stats";
+ e->usage =
+ "Usage: minivm show stats\n"
+ " Display Mini-Voicemail counters\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "* Mini-Voicemail statistics\n");
+ ast_cli(a->fd, " -------------------------\n");
+ ast_cli(a->fd, "\n");
+ ast_cli(a->fd, " Voicemail accounts: %5d\n", global_stats.voicemailaccounts);
+ ast_cli(a->fd, " Templates: %5d\n", global_stats.templates);
+ ast_cli(a->fd, " Timezones: %5d\n", global_stats.timezones);
+ if (global_stats.receivedmessages == 0) {
+ ast_cli(a->fd, " Received messages since last reset: <none>\n");
+ } else {
+ ast_cli(a->fd, " Received messages since last reset: %d\n", global_stats.receivedmessages);
+ ast_localtime(&global_stats.lastreceived, &time, NULL);
+ ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &time);
+ ast_cli(a->fd, " Last received voicemail: %s\n", buf);
+ }
+ ast_localtime(&global_stats.reset, &time, NULL);
+ ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &time);
+ ast_cli(a->fd, " Last reset: %s\n", buf);
+
+ ast_cli(a->fd, "\n");
+ return CLI_SUCCESS;
+}
+
+/*! \brief ${MINIVMACCOUNT()} Dialplan function - reads account data */
+static int minivm_account_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct minivm_account *vmu;
+ char *username, *domain, *colname;
+
+ if (!(username = ast_strdupa(data))) {
+ ast_log(LOG_ERROR, "Memory Error!\n");
+ return -1;
+ }
+
+ if ((colname = strchr(username, ':'))) {
+ *colname = '\0';
+ colname++;
+ } else {
+ colname = "path";
+ }
+ if ((domain = strchr(username, '@'))) {
+ *domain = '\0';
+ domain++;
+ }
+ if (ast_strlen_zero(username) || ast_strlen_zero(domain)) {
+ ast_log(LOG_ERROR, "This function needs a username and a domain: username@domain\n");
+ return 0;
+ }
+
+ if (!(vmu = find_account(domain, username, TRUE)))
+ return 0;
+
+ if (!strcasecmp(colname, "hasaccount")) {
+ ast_copy_string(buf, (ast_test_flag(vmu, MVM_ALLOCED) ? "0" : "1"), len);
+ } else if (!strcasecmp(colname, "fullname")) {
+ ast_copy_string(buf, vmu->fullname, len);
+ } else if (!strcasecmp(colname, "email")) {
+ if (!ast_strlen_zero(vmu->email))
+ ast_copy_string(buf, vmu->email, len);
+ else
+ snprintf(buf, len, "%s@%s", vmu->username, vmu->domain);
+ } else if (!strcasecmp(colname, "pager")) {
+ ast_copy_string(buf, vmu->pager, len);
+ } else if (!strcasecmp(colname, "etemplate")) {
+ if (!ast_strlen_zero(vmu->etemplate))
+ ast_copy_string(buf, vmu->etemplate, len);
+ else
+ ast_copy_string(buf, "email-default", len);
+ } else if (!strcasecmp(colname, "language")) {
+ ast_copy_string(buf, vmu->language, len);
+ } else if (!strcasecmp(colname, "timezone")) {
+ ast_copy_string(buf, vmu->zonetag, len);
+ } else if (!strcasecmp(colname, "ptemplate")) {
+ if (!ast_strlen_zero(vmu->ptemplate))
+ ast_copy_string(buf, vmu->ptemplate, len);
+ else
+ ast_copy_string(buf, "email-default", len);
+ } else if (!strcasecmp(colname, "accountcode")) {
+ ast_copy_string(buf, vmu->accountcode, len);
+ } else if (!strcasecmp(colname, "pincode")) {
+ ast_copy_string(buf, vmu->pincode, len);
+ } else if (!strcasecmp(colname, "path")) {
+ check_dirpath(buf, len, vmu->domain, vmu->username, NULL);
+ } else { /* Look in channel variables */
+ struct ast_variable *var;
+ int found = 0;
+
+ for (var = vmu->chanvars ; var ; var = var->next)
+ if (!strcmp(var->name, colname)) {
+ ast_copy_string(buf, var->value, len);
+ found = 1;
+ break;
+ }
+ }
+
+ if(ast_test_flag(vmu, MVM_ALLOCED))
+ free_user(vmu);
+
+ return 0;
+}
+
+/*! \brief lock directory
+
+ only return failure if ast_lock_path returns 'timeout',
+ not if the path does not exist or any other reason
+*/
+static int vm_lock_path(const char *path)
+{
+ switch (ast_lock_path(path)) {
+ case AST_LOCK_TIMEOUT:
+ return -1;
+ default:
+ return 0;
+ }
+}
+
+/*! \brief Access counter file, lock directory, read and possibly write it again changed
+ \param directory Directory to crate file in
+ \param countername filename
+ \param value If set to zero, we only read the variable
+ \param operand 0 to read, 1 to set new value, 2 to change
+ \return -1 on error, otherwise counter value
+*/
+static int access_counter_file(char *directory, char *countername, int value, int operand)
+{
+ char filename[BUFSIZ];
+ char readbuf[BUFSIZ];
+ FILE *counterfile;
+ int old = 0, counter = 0;
+
+ /* Lock directory */
+ if (vm_lock_path(directory)) {
+ return -1; /* Could not lock directory */
+ }
+ snprintf(filename, sizeof(filename), "%s/%s.counter", directory, countername);
+ if (operand != 1) {
+ counterfile = fopen(filename, "r");
+ if (counterfile) {
+ if(fgets(readbuf, sizeof(readbuf), counterfile)) {
+ ast_debug(3, "Read this string from counter file: %s\n", readbuf);
+ old = counter = atoi(readbuf);
+ }
+ fclose(counterfile);
+ }
+ }
+ switch (operand) {
+ case 0: /* Read only */
+ ast_unlock_path(directory);
+ ast_debug(2, "MINIVM Counter %s/%s: Value %d\n", directory, countername, counter);
+ return counter;
+ break;
+ case 1: /* Set new value */
+ counter = value;
+ break;
+ case 2: /* Change value */
+ counter += value;
+ if (counter < 0) /* Don't allow counters to fall below zero */
+ counter = 0;
+ break;
+ }
+
+ /* Now, write the new value to the file */
+ counterfile = fopen(filename, "w");
+ if (!counterfile) {
+ ast_log(LOG_ERROR, "Could not open counter file for writing : %s - %s\n", filename, strerror(errno));
+ ast_unlock_path(directory);
+ return -1; /* Could not open file for writing */
+ }
+ fprintf(counterfile, "%d\n\n", counter);
+ fclose(counterfile);
+ ast_unlock_path(directory);
+ ast_debug(2, "MINIVM Counter %s/%s: Old value %d New value %d\n", directory, countername, old, counter);
+ return counter;
+}
+
+/*! \brief ${MINIVMCOUNTER()} Dialplan function - read counters */
+static int minivm_counter_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *username, *domain, *countername;
+ struct minivm_account *vmu = NULL;
+ char userpath[BUFSIZ];
+ int res;
+
+ *buf = '\0';
+
+ if (!(username = ast_strdupa(data))) { /* Copy indata to local buffer */
+ ast_log(LOG_WARNING, "Memory error!\n");
+ return -1;
+ }
+ if ((countername = strchr(username, ':'))) {
+ *countername = '\0';
+ countername++;
+ }
+
+ if ((domain = strchr(username, '@'))) {
+ *domain = '\0';
+ domain++;
+ }
+
+ /* If we have neither username nor domain now, let's give up */
+ if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
+ ast_log(LOG_ERROR, "No account given\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(countername)) {
+ ast_log(LOG_ERROR, "This function needs two arguments: Account:countername\n");
+ return -1;
+ }
+
+ /* We only have a domain, no username */
+ if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
+ domain = username;
+ username = NULL;
+ }
+
+ /* If we can't find account or if the account is temporary, return. */
+ if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
+ ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
+ return 0;
+ }
+
+ create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
+
+ /* We have the path, now read the counter file */
+ res = access_counter_file(userpath, countername, 0, 0);
+ if (res >= 0)
+ snprintf(buf, len, "%d", res);
+ return 0;
+}
+
+/*! \brief ${MINIVMCOUNTER()} Dialplan function - changes counter data */
+static int minivm_counter_func_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ char *username, *domain, *countername, *operand;
+ char userpath[BUFSIZ];
+ struct minivm_account *vmu;
+ int change = 0;
+ int operation = 0;
+
+ if(!value)
+ return -1;
+ change = atoi(value);
+
+ if (!(username = ast_strdupa(data))) { /* Copy indata to local buffer */
+ ast_log(LOG_WARNING, "Memory error!\n");
+ return -1;
+ }
+
+ if ((countername = strchr(username, ':'))) {
+ *countername = '\0';
+ countername++;
+ }
+ if ((operand = strchr(countername, ':'))) {
+ *operand = '\0';
+ operand++;
+ }
+
+ if ((domain = strchr(username, '@'))) {
+ *domain = '\0';
+ domain++;
+ }
+
+ /* If we have neither username nor domain now, let's give up */
+ if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
+ ast_log(LOG_ERROR, "No account given\n");
+ return -1;
+ }
+
+ /* We only have a domain, no username */
+ if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
+ domain = username;
+ username = NULL;
+ }
+
+ if (ast_strlen_zero(operand) || ast_strlen_zero(countername)) {
+ ast_log(LOG_ERROR, "Writing to this function requires three arguments: Account:countername:operand\n");
+ return -1;
+ }
+
+ /* If we can't find account or if the account is temporary, return. */
+ if (!ast_strlen_zero(username) && !(vmu = find_account(domain, username, FALSE))) {
+ ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
+ return 0;
+ }
+
+ create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
+ /* Now, find out our operator */
+ if (*operand == 'i') /* Increment */
+ operation = 2;
+ else if (*operand == 'd') {
+ change = change * -1;
+ operation = 2;
+ } else if (*operand == 's')
+ operation = 1;
+ else {
+ ast_log(LOG_ERROR, "Unknown operator: %s\n", operand);
+ return -1;
+ }
+
+ /* We have the path, now read the counter file */
+ access_counter_file(userpath, countername, change, operation);
+ return 0;
+}
+
+
+/*! \brief CLI commands for Mini-voicemail */
+static struct ast_cli_entry cli_minivm[] = {
+ AST_CLI_DEFINE(handle_minivm_show_users, "List defined mini-voicemail boxes"),
+ AST_CLI_DEFINE(handle_minivm_show_zones, "List zone message formats"),
+ AST_CLI_DEFINE(handle_minivm_list_templates, "List message templates"),
+ AST_CLI_DEFINE(handle_minivm_reload, "Reload Mini-voicemail configuration"),
+ AST_CLI_DEFINE(handle_minivm_show_stats, "Show some mini-voicemail statistics"),
+ AST_CLI_DEFINE(handle_minivm_show_settings, "Show mini-voicemail general settings"),
+};
+
+static struct ast_custom_function minivm_counter_function = {
+ .name = "MINIVMCOUNTER",
+ .synopsis = "Reads or sets counters for MiniVoicemail message",
+ .syntax = "MINIVMCOUNTER(<account>:name[:operand])",
+ .read = minivm_counter_func_read,
+ .write = minivm_counter_func_write,
+ .desc = "Valid operands for changing the value of a counter when assigning a value are:\n"
+ "- i Increment by value\n"
+ "- d Decrement by value\n"
+ "- s Set to value\n"
+ "\nThe counters never goes below zero.\n"
+ "- The name of the counter is a string, up to 10 characters\n"
+ "- If account is given and it exists, the counter is specific for the account\n"
+ "- If account is a domain and the domain directory exists, counters are specific for a domain\n"
+ "The operation is atomic and the counter is locked while changing the value\n"
+ "\nThe counters are stored as text files in the minivm account directories. It might be better to use\n"
+ "realtime functions if you are using a database to operate your Asterisk\n",
+};
+
+static struct ast_custom_function minivm_account_function = {
+ .name = "MINIVMACCOUNT",
+ .synopsis = "Gets MiniVoicemail account information",
+ .syntax = "MINIVMACCOUNT(<account>:item)",
+ .read = minivm_account_func_read,
+ .desc = "Valid items are:\n"
+ "- path Path to account mailbox (if account exists, otherwise temporary mailbox)\n"
+ "- hasaccount 1 if static Minivm account exists, 0 otherwise\n"
+ "- fullname Full name of account owner\n"
+ "- email Email address used for account\n"
+ "- etemplate E-mail template for account (default template if none is configured)\n"
+ "- ptemplate Pager template for account (default template if none is configured)\n"
+ "- accountcode Account code for voicemail account\n"
+ "- pincode Pin code for voicemail account\n"
+ "- timezone Time zone for voicemail account\n"
+ "- language Language for voicemail account\n"
+ "- <channel variable name> Channel variable value (set in configuration for account)\n"
+ "\n",
+};
+
+/*! \brief Load mini voicemail module */
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app_minivm_record, minivm_record_exec, synopsis_minivm_record, descrip_minivm_record);
+ res = ast_register_application(app_minivm_greet, minivm_greet_exec, synopsis_minivm_greet, descrip_minivm_greet);
+ res = ast_register_application(app_minivm_notify, minivm_notify_exec, synopsis_minivm_notify, descrip_minivm_notify);
+ res = ast_register_application(app_minivm_delete, minivm_delete_exec, synopsis_minivm_delete, descrip_minivm_delete);
+ res = ast_register_application(app_minivm_accmess, minivm_accmess_exec, synopsis_minivm_accmess, descrip_minivm_accmess);
+
+ ast_custom_function_register(&minivm_account_function);
+ ast_custom_function_register(&minivm_counter_function);
+ if (res)
+ return(res);
+
+ if ((res = load_config(0)))
+ return(res);
+
+ ast_cli_register_multiple(cli_minivm, sizeof(cli_minivm)/sizeof(cli_minivm[0]));
+
+ /* compute the location of the voicemail spool directory */
+ snprintf(MVM_SPOOL_DIR, sizeof(MVM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
+
+ return res;
+}
+
+/*! \brief Reload mini voicemail module */
+static int reload(void)
+{
+ return(load_config(1));
+}
+
+/*! \brief Reload cofiguration */
+static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "minivm reload";
+ e->usage =
+ "Usage: minivm reload\n"
+ " Reload mini-voicemail configuration and reset statistics\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ reload();
+ ast_cli(a->fd, "\n-- Mini voicemail re-configured \n");
+ return CLI_SUCCESS;
+}
+
+/*! \brief Unload mini voicemail module */
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_minivm_record);
+ res |= ast_unregister_application(app_minivm_greet);
+ res |= ast_unregister_application(app_minivm_notify);
+ res |= ast_unregister_application(app_minivm_delete);
+ res |= ast_unregister_application(app_minivm_accmess);
+ ast_cli_unregister_multiple(cli_minivm, sizeof(cli_minivm)/sizeof(cli_minivm[0]));
+ ast_custom_function_unregister(&minivm_account_function);
+ ast_custom_function_unregister(&minivm_counter_function);
+
+ message_destroy_list(); /* Destroy list of voicemail message templates */
+ timezone_destroy_list(); /* Destroy list of timezones */
+ vmaccounts_destroy_list(); /* Destroy list of voicemail accounts */
+
+ return res;
+}
+
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mini VoiceMail (A minimal Voicemail e-mail System)",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/apps/app_mixmonitor.c b/trunk/apps/app_mixmonitor.c
new file mode 100644
index 000000000..012af01c5
--- /dev/null
+++ b/trunk/apps/app_mixmonitor.c
@@ -0,0 +1,424 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Anthony Minessale II
+ * Copyright (C) 2005 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * Based on app_muxmon.c provided by
+ * Anthony Minessale II <anthmct@yahoo.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 MixMonitor() - Record a call and mix the audio during the recording
+ * \ingroup applications
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * \note Based on app_muxmon.c provided by
+ * Anthony Minessale II <anthmct@yahoo.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
+#include "asterisk/file.h"
+#include "asterisk/audiohook.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h"
+
+#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
+
+static const char *app = "MixMonitor";
+static const char *synopsis = "Record a call and mix the audio during the recording";
+static const char *desc = ""
+" MixMonitor(<file>.<ext>[,<options>[,<command>]]):\n"
+"Records the audio on the current channel to the specified file.\n"
+"If the filename is an absolute path, uses that path, otherwise\n"
+"creates the file in the configured monitoring directory from\n"
+"asterisk.conf.\n\n"
+"Valid options:\n"
+" a - Append to the file instead of overwriting it.\n"
+" b - Only save audio to the file while the channel is bridged.\n"
+" Note: Does not include conferences or sounds played to each bridged\n"
+" party.\n"
+" v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
+" V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
+" W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
+" (range -4 to 4)\n\n"
+"<command> will be executed when the recording is over\n"
+"Any strings matching ^{X} will be unescaped to ${X}.\n"
+"All variables will be evaluated at the time MixMonitor is called.\n"
+"The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
+"";
+
+static const char *stop_app = "StopMixMonitor";
+static const char *stop_synopsis = "Stop recording a call through MixMonitor";
+static const char *stop_desc = ""
+" StopMixMonitor():\n"
+"Stops the audio recording that was started with a call to MixMonitor()\n"
+"on the current channel.\n"
+"";
+
+struct module_symbols *me;
+
+static const char *mixmonitor_spy_type = "MixMonitor";
+
+struct mixmonitor {
+ struct ast_audiohook audiohook;
+ char *filename;
+ char *post_process;
+ char *name;
+ unsigned int flags;
+ struct ast_channel *chan;
+};
+
+enum {
+ MUXFLAG_APPEND = (1 << 1),
+ MUXFLAG_BRIDGED = (1 << 2),
+ MUXFLAG_VOLUME = (1 << 3),
+ MUXFLAG_READVOLUME = (1 << 4),
+ MUXFLAG_WRITEVOLUME = (1 << 5),
+} mixmonitor_flags;
+
+enum {
+ OPT_ARG_READVOLUME = 0,
+ OPT_ARG_WRITEVOLUME,
+ OPT_ARG_VOLUME,
+ OPT_ARG_ARRAY_SIZE,
+} mixmonitor_args;
+
+AST_APP_OPTIONS(mixmonitor_opts, {
+ AST_APP_OPTION('a', MUXFLAG_APPEND),
+ AST_APP_OPTION('b', MUXFLAG_BRIDGED),
+ AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
+ AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
+ AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
+});
+
+static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
+{
+ struct ast_channel *peer = NULL;
+ int res = 0;
+
+ if (!chan)
+ return -1;
+
+ 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;
+}
+
+#define SAMPLES_PER_FRAME 160
+
+static void *mixmonitor_thread(void *obj)
+{
+ struct mixmonitor *mixmonitor = obj;
+ struct ast_filestream *fs = NULL;
+ unsigned int oflags;
+ char *ext;
+ int errflag = 0;
+
+ ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
+
+ ast_audiohook_lock(&mixmonitor->audiohook);
+
+ while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
+ struct ast_frame *fr = NULL;
+
+ ast_audiohook_trigger_wait(&mixmonitor->audiohook);
+
+ if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
+ break;
+
+ if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
+ continue;
+
+ if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) {
+ /* Initialize the file if not already done so */
+ if (!fs && !errflag) {
+ oflags = O_CREAT | O_WRONLY;
+ oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
+
+ if ((ext = strrchr(mixmonitor->filename, '.')))
+ *(ext++) = '\0';
+ else
+ ext = "raw";
+
+ if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
+ ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
+ errflag = 1;
+ }
+ }
+
+ /* Write out frame */
+ if (fs)
+ ast_writestream(fs, fr);
+ }
+
+ /* All done! free it. */
+ ast_frame_free(fr, 0);
+
+ }
+
+ ast_audiohook_detach(&mixmonitor->audiohook);
+ ast_audiohook_unlock(&mixmonitor->audiohook);
+ ast_audiohook_destroy(&mixmonitor->audiohook);
+
+ ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
+
+ if (fs)
+ ast_closestream(fs);
+
+ if (mixmonitor->post_process) {
+ ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
+ ast_safe_system(mixmonitor->post_process);
+ }
+
+ ast_free(mixmonitor);
+
+
+ return NULL;
+}
+
+static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
+ int readvol, int writevol, const char *post_process)
+{
+ pthread_t thread;
+ struct mixmonitor *mixmonitor;
+ char postprocess2[1024] = "";
+ size_t len;
+
+ len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
+
+ postprocess2[0] = 0;
+ /* If a post process system command is given attach it to the structure */
+ if (!ast_strlen_zero(post_process)) {
+ char *p1, *p2;
+
+ p1 = ast_strdupa(post_process);
+ for (p2 = p1; *p2 ; p2++) {
+ if (*p2 == '^' && *(p2+1) == '{') {
+ *p2 = '$';
+ }
+ }
+ pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
+ if (!ast_strlen_zero(postprocess2))
+ len += strlen(postprocess2) + 1;
+ }
+
+ /* Pre-allocate mixmonitor structure and spy */
+ if (!(mixmonitor = ast_calloc(1, len))) {
+ return;
+ }
+
+ /* Copy over flags and channel name */
+ mixmonitor->flags = flags;
+ mixmonitor->chan = chan;
+ mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
+ strcpy(mixmonitor->name, chan->name);
+ if (!ast_strlen_zero(postprocess2)) {
+ mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
+ strcpy(mixmonitor->post_process, postprocess2);
+ }
+
+ mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
+ strcpy(mixmonitor->filename, filename);
+
+ /* Setup the actual spy before creating our thread */
+ if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
+ ast_free(mixmonitor);
+ return;
+ }
+
+ ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_WRITE);
+
+ if (readvol)
+ mixmonitor->audiohook.options.read_volume = readvol;
+ if (writevol)
+ mixmonitor->audiohook.options.write_volume = writevol;
+
+ if (startmon(chan, &mixmonitor->audiohook)) {
+ ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
+ mixmonitor_spy_type, chan->name);
+ ast_audiohook_destroy(&mixmonitor->audiohook);
+ ast_free(mixmonitor);
+ return;
+ }
+
+ ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
+
+}
+
+static int mixmonitor_exec(struct ast_channel *chan, void *data)
+{
+ int x, readvol = 0, writevol = 0;
+ struct ast_flags flags = {0};
+ char *parse, *tmp, *slash;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(options);
+ AST_APP_ARG(post_process);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.filename)) {
+ ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
+ return -1;
+ }
+
+ if (args.options) {
+ char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
+
+ ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
+
+ if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
+ if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
+ ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
+ } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+ ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
+ } else {
+ readvol = get_volfactor(x);
+ }
+ }
+
+ if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
+ if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
+ ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
+ } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+ ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
+ } else {
+ writevol = get_volfactor(x);
+ }
+ }
+
+ if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
+ if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
+ ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
+ } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+ ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
+ } else {
+ readvol = writevol = get_volfactor(x);
+ }
+ }
+ }
+
+ /* if not provided an absolute path, use the system-configured monitoring directory */
+ if (args.filename[0] != '/') {
+ char *build;
+
+ build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
+ sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
+ args.filename = build;
+ }
+
+ tmp = ast_strdupa(args.filename);
+ if ((slash = strrchr(tmp, '/')))
+ *slash = '\0';
+ ast_mkdir(tmp, 0777);
+
+ pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
+ launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
+
+ return 0;
+}
+
+static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
+{
+ ast_audiohook_detach_source(chan, mixmonitor_spy_type);
+ return 0;
+}
+
+static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mixmonitor [start|stop]";
+ e->usage =
+ "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
+ " The optional arguments are passed to the MixMonitor\n"
+ " application when the 'start' command is used.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
+ }
+
+ if (a->argc < 3)
+ return CLI_SHOWUSAGE;
+
+ if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
+ ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
+ /* Technically this is a failure, but we don't want 2 errors printing out */
+ return CLI_SUCCESS;
+ }
+
+ if (!strcasecmp(a->argv[1], "start")) {
+ mixmonitor_exec(chan, a->argv[3]);
+ ast_channel_unlock(chan);
+ } else {
+ ast_channel_unlock(chan);
+ ast_audiohook_detach_source(chan, mixmonitor_spy_type);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_mixmonitor[] = {
+ AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
+ res = ast_unregister_application(stop_app);
+ res |= ast_unregister_application(app);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
+ res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
+ res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");
diff --git a/trunk/apps/app_morsecode.c b/trunk/apps/app_morsecode.c
new file mode 100644
index 000000000..6c88ed32c
--- /dev/null
+++ b/trunk/apps/app_morsecode.c
@@ -0,0 +1,161 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2006, Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Morsecode application
+ *
+ * \author Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/indications.h"
+
+static char *app_morsecode = "Morsecode";
+
+static char *morsecode_synopsis = "Plays morse code";
+
+static char *morsecode_descrip =
+" Morsecode(<string>):\n"
+"Plays the Morse code equivalent of the passed string. If the variable\n"
+"MORSEDITLEN is set, it will use that value for the length (in ms) of the dit\n"
+"(defaults to 80). Additionally, if MORSETONE is set, it will use that tone\n"
+"(in Hz). The tone default is 800.\n";
+
+
+static char *morsecode[] = {
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 0-15 */
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */
+ " ", /* 32 - <space> */
+ ".-.-.-", /* 33 - ! */
+ ".-..-.", /* 34 - " */
+ "", /* 35 - # */
+ "", /* 36 - $ */
+ "", /* 37 - % */
+ "", /* 38 - & */
+ ".----.", /* 39 - ' */
+ "-.--.-", /* 40 - ( */
+ "-.--.-", /* 41 - ) */
+ "", /* 42 - * */
+ "", /* 43 - + */
+ "--..--", /* 44 - , */
+ "-....-", /* 45 - - */
+ ".-.-.-", /* 46 - . */
+ "-..-.", /* 47 - / */
+ "-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----.", /* 48-57 - 0-9 */
+ "---...", /* 58 - : */
+ "-.-.-.", /* 59 - ; */
+ "", /* 60 - < */
+ "-...-", /* 61 - = */
+ "", /* 62 - > */
+ "..--..", /* 63 - ? */
+ ".--.-.", /* 64 - @ */
+ ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
+ "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
+ "-.--.-", /* 91 - [ (really '(') */
+ "-..-.", /* 92 - \ (really '/') */
+ "-.--.-", /* 93 - ] (really ')') */
+ "", /* 94 - ^ */
+ "..--.-", /* 95 - _ */
+ ".----.", /* 96 - ` */
+ ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
+ "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
+ "-.--.-", /* 123 - { (really '(') */
+ "", /* 124 - | */
+ "-.--.-", /* 125 - } (really ')') */
+ "-..-.", /* 126 - ~ (really bar) */
+ ". . .", /* 127 - <del> (error) */
+};
+
+static void playtone(struct ast_channel *chan, int tone, int len)
+{
+ char dtmf[20];
+ snprintf(dtmf, sizeof(dtmf), "%d/%d", tone, len);
+ ast_playtones_start(chan, 0, dtmf, 0);
+ ast_safe_sleep(chan, len);
+ ast_playtones_stop(chan);
+}
+
+static int morsecode_exec(struct ast_channel *chan, void *data)
+{
+ int res=0, ditlen, tone;
+ char *digit;
+ const char *ditlenc, *tonec;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: Morsecode(<string>) - no argument found\n");
+ return 0;
+ }
+
+ /* Use variable MORESEDITLEN, if set (else 80) */
+ ditlenc = pbx_builtin_getvar_helper(chan, "MORSEDITLEN");
+ if (ast_strlen_zero(ditlenc) || (sscanf(ditlenc, "%d", &ditlen) != 1)) {
+ ditlen = 80;
+ }
+
+ /* Use variable MORSETONE, if set (else 800) */
+ tonec = pbx_builtin_getvar_helper(chan, "MORSETONE");
+ if (ast_strlen_zero(tonec) || (sscanf(tonec, "%d", &tone) != 1)) {
+ tone = 800;
+ }
+
+ for (digit = data; *digit; digit++) {
+ int digit2 = *digit;
+ char *dahdit;
+ if (digit2 < 0) {
+ continue;
+ }
+ for (dahdit = morsecode[digit2]; *dahdit; dahdit++) {
+ if (*dahdit == '-') {
+ playtone(chan, tone, 3 * ditlen);
+ } else if (*dahdit == '.') {
+ playtone(chan, tone, 1 * ditlen);
+ } else {
+ /* Account for ditlen of silence immediately following */
+ playtone(chan, 0, 2 * ditlen);
+ }
+
+ /* Pause slightly between each dit and dah */
+ playtone(chan, 0, 1 * ditlen);
+ }
+ /* Pause between characters */
+ playtone(chan, 0, 2 * ditlen);
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app_morsecode);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app_morsecode, morsecode_exec, morsecode_synopsis, morsecode_descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Morse code");
diff --git a/trunk/apps/app_mp3.c b/trunk/apps/app_mp3.c
new file mode 100644
index 000000000..d2f2f5c0e
--- /dev/null
+++ b/trunk/apps/app_mp3.c
@@ -0,0 +1,235 @@
+/*
+ * 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 Silly application to play an MP3 file -- uses mpg123
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/frame.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
+#define MPG_123 "/usr/bin/mpg123"
+
+static char *app = "MP3Player";
+
+static char *synopsis = "Play an MP3 file or stream";
+
+static char *descrip =
+" MP3Player(location): Executes mpg123 to play the given location,\n"
+"which typically would be a filename or a URL. User can exit by pressing\n"
+"any key on the dialpad, or by hanging up.";
+
+
+static int mp3play(char *filename, int fd)
+{
+ int res;
+ int x;
+ sigset_t fullset, oldset;
+
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
+
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res) {
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ return res;
+ }
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+ signal(SIGPIPE, SIG_DFL);
+ pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
+
+ dup2(fd, STDOUT_FILENO);
+ for (x=STDERR_FILENO + 1;x<256;x++) {
+ if (x != STDOUT_FILENO)
+ close(x);
+ }
+ /* Execute mpg123, but buffer if it's a net connection */
+ if (!strncasecmp(filename, "http://", 7)) {
+ /* Most commonly installed in /usr/local/bin */
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* But many places has it in /usr/bin */
+ execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* As a last-ditch effort, try to use PATH */
+ execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ }
+ else {
+ /* Most commonly installed in /usr/local/bin */
+ execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* But many places has it in /usr/bin */
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ /* As a last-ditch effort, try to use PATH */
+ execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ }
+ ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
+ _exit(0);
+}
+
+static int timed_read(int fd, void *data, int datalen, int timeout)
+{
+ int res;
+ struct pollfd fds[1];
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ res = poll(fds, 1, timeout);
+ if (res < 1) {
+ ast_log(LOG_NOTICE, "Poll timed out/errored out with %d\n", res);
+ return -1;
+ }
+ return read(fd, data, datalen);
+
+}
+
+static int mp3_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int fds[2];
+ int ms = -1;
+ int pid = -1;
+ int owriteformat;
+ int timeout = 2000;
+ struct timeval next;
+ struct ast_frame *f;
+ struct myframe {
+ struct ast_frame f;
+ char offset[AST_FRIENDLY_OFFSET];
+ short frdata[160];
+ } myf;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
+ return -1;
+ }
+
+ if (pipe(fds)) {
+ ast_log(LOG_WARNING, "Unable to create pipe\n");
+ return -1;
+ }
+
+ ast_stopstream(chan);
+
+ owriteformat = chan->writeformat;
+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ return -1;
+ }
+
+ res = mp3play((char *)data, fds[1]);
+ if (!strncasecmp((char *)data, "http://", 7)) {
+ timeout = 10000;
+ }
+ /* Wait 1000 ms first */
+ next = ast_tvnow();
+ next.tv_sec += 1;
+ if (res >= 0) {
+ pid = res;
+ /* Order is important -- there's almost always going to be mp3... we want to prioritize the
+ user */
+ for (;;) {
+ ms = ast_tvdiff_ms(next, ast_tvnow());
+ if (ms <= 0) {
+ res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout);
+ if (res > 0) {
+ myf.f.frametype = AST_FRAME_VOICE;
+ myf.f.subclass = AST_FORMAT_SLINEAR;
+ myf.f.datalen = res;
+ myf.f.samples = res / 2;
+ myf.f.mallocd = 0;
+ myf.f.offset = AST_FRIENDLY_OFFSET;
+ myf.f.src = __PRETTY_FUNCTION__;
+ myf.f.delivery.tv_sec = 0;
+ myf.f.delivery.tv_usec = 0;
+ myf.f.data = myf.frdata;
+ if (ast_write(chan, &myf.f) < 0) {
+ res = -1;
+ break;
+ }
+ } else {
+ ast_debug(1, "No more mp3\n");
+ res = 0;
+ break;
+ }
+ next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
+ } else {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ ast_debug(1, "Hangup detected\n");
+ res = -1;
+ break;
+ }
+ if (ms) {
+ f = ast_read(chan);
+ if (!f) {
+ ast_debug(1, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ ast_debug(1, "User pressed a key\n");
+ ast_frfree(f);
+ res = 0;
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+ }
+ }
+ close(fds[0]);
+ close(fds[1]);
+
+ if (pid > -1)
+ kill(pid, SIGKILL);
+ if (!res && owriteformat)
+ ast_set_write_format(chan, owriteformat);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, mp3_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly MP3 Application");
diff --git a/trunk/apps/app_nbscat.c b/trunk/apps/app_nbscat.c
new file mode 100644
index 000000000..4e7203319
--- /dev/null
+++ b/trunk/apps/app_nbscat.c
@@ -0,0 +1,218 @@
+/*
+ * 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 Silly application to play an NBScat file -- uses nbscat8k
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/frame.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+
+#define LOCAL_NBSCAT "/usr/local/bin/nbscat8k"
+#define NBSCAT "/usr/bin/nbscat8k"
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+static char *app = "NBScat";
+
+static char *synopsis = "Play an NBS local stream";
+
+static char *descrip =
+" NBScat(): Executes nbscat to listen to the local NBS stream.\n"
+"User can exit by pressing any key.\n";
+
+
+static int NBScatplay(int fd)
+{
+ int res;
+ int x;
+ sigset_t fullset, oldset;
+
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
+
+ res = fork();
+ if (res < 0)
+ ast_log(LOG_WARNING, "Fork failed\n");
+ if (res) {
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ return res;
+ }
+ signal(SIGPIPE, SIG_DFL);
+ pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
+
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+
+ dup2(fd, STDOUT_FILENO);
+ for (x = STDERR_FILENO + 1; x < 1024; x++) {
+ if (x != STDOUT_FILENO)
+ close(x);
+ }
+ /* Most commonly installed in /usr/local/bin */
+ execl(NBSCAT, "nbscat8k", "-d", (char *)NULL);
+ execl(LOCAL_NBSCAT, "nbscat8k", "-d", (char *)NULL);
+ ast_log(LOG_WARNING, "Execute of nbscat8k failed\n");
+ _exit(0);
+}
+
+static int timed_read(int fd, void *data, int datalen)
+{
+ int res;
+ struct pollfd fds[1];
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ res = poll(fds, 1, 2000);
+ if (res < 1) {
+ ast_log(LOG_NOTICE, "Selected timed out/errored out with %d\n", res);
+ return -1;
+ }
+ return read(fd, data, datalen);
+
+}
+
+static int NBScat_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int fds[2];
+ int ms = -1;
+ int pid = -1;
+ int owriteformat;
+ struct timeval next;
+ struct ast_frame *f;
+ struct myframe {
+ struct ast_frame f;
+ char offset[AST_FRIENDLY_OFFSET];
+ short frdata[160];
+ } myf;
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds)) {
+ ast_log(LOG_WARNING, "Unable to create socketpair\n");
+ return -1;
+ }
+
+ ast_stopstream(chan);
+
+ owriteformat = chan->writeformat;
+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
+ return -1;
+ }
+
+ res = NBScatplay(fds[1]);
+ /* Wait 1000 ms first */
+ next = ast_tvnow();
+ next.tv_sec += 1;
+ if (res >= 0) {
+ pid = res;
+ /* Order is important -- there's almost always going to be mp3... we want to prioritize the
+ user */
+ for (;;) {
+ ms = ast_tvdiff_ms(next, ast_tvnow());
+ if (ms <= 0) {
+ res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata));
+ if (res > 0) {
+ myf.f.frametype = AST_FRAME_VOICE;
+ myf.f.subclass = AST_FORMAT_SLINEAR;
+ myf.f.datalen = res;
+ myf.f.samples = res / 2;
+ myf.f.mallocd = 0;
+ myf.f.offset = AST_FRIENDLY_OFFSET;
+ myf.f.src = __PRETTY_FUNCTION__;
+ myf.f.delivery.tv_sec = 0;
+ myf.f.delivery.tv_usec = 0;
+ myf.f.data = myf.frdata;
+ if (ast_write(chan, &myf.f) < 0) {
+ res = -1;
+ break;
+ }
+ } else {
+ ast_debug(1, "No more mp3\n");
+ res = 0;
+ break;
+ }
+ next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
+ } else {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ ast_debug(1, "Hangup detected\n");
+ res = -1;
+ break;
+ }
+ if (ms) {
+ f = ast_read(chan);
+ if (!f) {
+ ast_debug(1, "Null frame == hangup() detected\n");
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ ast_debug(1, "User pressed a key\n");
+ ast_frfree(f);
+ res = 0;
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+ }
+ }
+ close(fds[0]);
+ close(fds[1]);
+
+ if (pid > -1)
+ kill(pid, SIGKILL);
+ if (!res && owriteformat)
+ ast_set_write_format(chan, owriteformat);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, NBScat_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly NBS Stream Application");
diff --git a/trunk/apps/app_osplookup.c b/trunk/apps/app_osplookup.c
new file mode 100644
index 000000000..bee5dae1d
--- /dev/null
+++ b/trunk/apps/app_osplookup.c
@@ -0,0 +1,2041 @@
+/*
+ * 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 Open Settlement Protocol (OSP) Applications
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \extref The OSP Toolkit: http://www.transnexus.com
+ * \extref OpenSSL http://www.openssl.org
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>osptk</depend>
+ <depend>ssl</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <osp/osp.h>
+#include <osp/osputils.h>
+
+#include "asterisk/paths.h"
+#include "asterisk/lock.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cli.h"
+#include "asterisk/astosp.h"
+
+/* OSP Buffer Sizes */
+#define OSP_INTSTR_SIZE ((unsigned int)16) /* OSP signed/unsigned int string buffer size */
+#define OSP_NORSTR_SIZE ((unsigned int)256) /* OSP normal string buffer size */
+#define OSP_TOKSTR_SIZE ((unsigned int)4096) /* OSP token string buffer size */
+#define OSP_TECHSTR_SIZE ((unsigned int)32) /* OSP signed/unsigned int string buffer size */
+#define OSP_UUID_SIZE ((unsigned int)16) /* UUID size */
+#define OSP_UUIDSTR_SIZE ((unsigned int)36) /* UUID string size */
+
+/* OSP Authentication Policy */
+enum osp_authpolicy {
+ OSP_AUTH_NO, /* Accept any call */
+ OSP_AUTH_YES, /* Accept call with valid OSP token or without OSP token */
+ OSP_AUTH_EXCLUSIVE /* Only accept call with valid OSP token */
+};
+
+/* Call ID type*/
+#define OSP_CALLID_UNDEFINED ((unsigned int)0) /* UNDEFINED */
+#define OSP_CALLID_H323 ((unsigned int)(1 << 0)) /* H.323 */
+#define OSP_CALLID_SIP ((unsigned int)(1 << 1)) /* SIP */
+#define OSP_CALLID_IAX ((unsigned int)(1 << 2)) /* IAX2 */
+#define OSP_CALLID_MAXNUM ((unsigned int)3) /* Max number of call ID type */
+
+/* OSP Supported Destination Protocols */
+#define OSP_PROT_H323 ((char*)"H323") /* H323 Q931 protocol name*/
+#define OSP_PROT_SIP ((char*)"SIP") /* SIP protocol name */
+#define OSP_PROT_IAX ((char*)"IAX") /* IAX protocol name */
+#define OSP_PROT_OTHER ((char*)"OTHER") /* Other protocol name */
+
+/* OSP supported Destination Tech */
+#if 0
+#define OSP_TECH_H323 ((char*)"OOH323") /* OOH323 tech name */
+#endif
+#define OSP_TECH_H323 ((char*)"H323") /* OH323 tech name */
+#define OSP_TECH_SIP ((char*)"SIP") /* SIP tech name */
+#define OSP_TECH_IAX ((char*)"IAX2") /* IAX2 tech name */
+
+/* SIP OSP header field name */
+#define OSP_SIP_HEADER ((char*)"P-OSP-Auth-Token: ")
+
+/* OSP Constants */
+#define OSP_INVALID_HANDLE ((int)-1) /* Invalid OSP handle, provider, transaction etc. */
+#define OSP_CONFIG_FILE ((const char*)"osp.conf") /* OSP configuration file name */
+#define OSP_GENERAL_CAT ((const char*)"general") /* OSP global configuration context name */
+#define OSP_DEF_PROVIDER ((const char*)"default") /* OSP default provider context name */
+#define OSP_MAX_CERTS ((unsigned int)10) /* OSP max number of cacerts */
+#define OSP_MAX_SRVS ((unsigned int)10) /* OSP max number of service points */
+#define OSP_DEF_MAXCONNECTIONS ((unsigned int)20) /* OSP default max_connections */
+#define OSP_MIN_MAXCONNECTIONS ((unsigned int)1) /* OSP min max_connections */
+#define OSP_MAX_MAXCONNECTIONS ((unsigned int)1000) /* OSP max max_connections */
+#define OSP_DEF_RETRYDELAY ((unsigned int)0) /* OSP default retry delay */
+#define OSP_MIN_RETRYDELAY ((unsigned int)0) /* OSP min retry delay */
+#define OSP_MAX_RETRYDELAY ((unsigned int)10) /* OSP max retry delay */
+#define OSP_DEF_RETRYLIMIT ((unsigned int)2) /* OSP default retry times */
+#define OSP_MIN_RETRYLIMIT ((unsigned int)0) /* OSP min retry times */
+#define OSP_MAX_RETRYLIMIT ((unsigned int)100) /* OSP max retry times */
+#define OSP_DEF_TIMEOUT ((unsigned int)500) /* OSP default timeout in ms */
+#define OSP_MIN_TIMEOUT ((unsigned int)200) /* OSP min timeout in ms */
+#define OSP_MAX_TIMEOUT ((unsigned int)10000) /* OSP max timeout in ms */
+#define OSP_DEF_AUTHPOLICY ((enum osp_authpolicy)OSP_AUTH_YES)
+#define OSP_AUDIT_URL ((const char*)"localhost") /* OSP default Audit URL */
+#define OSP_LOCAL_VALIDATION ((int)1) /* Validate OSP token locally */
+#define OSP_SSL_LIFETIME ((unsigned int)300) /* SSL life time, in seconds */
+#define OSP_HTTP_PERSISTENCE ((int)1) /* In seconds */
+#define OSP_CUSTOMER_ID ((const char*)"") /* OSP customer ID */
+#define OSP_DEVICE_ID ((const char*)"") /* OSP device ID */
+#define OSP_DEF_DESTINATIONS ((unsigned int)5) /* OSP default max number of destinations */
+#define OSP_DEF_TIMELIMIT ((unsigned int)0) /* OSP default duration limit, no limit */
+#define OSP_DEF_PROTOCOL OSP_PROT_SIP /* OSP default destination protocol, SIP */
+
+/* OSP Provider */
+struct osp_provider {
+ char name[OSP_NORSTR_SIZE]; /* OSP provider context name */
+ char privatekey[OSP_NORSTR_SIZE]; /* OSP private key file name */
+ char localcert[OSP_NORSTR_SIZE]; /* OSP local cert file name */
+ unsigned int cacount; /* Number of cacerts */
+ char cacerts[OSP_MAX_CERTS][OSP_NORSTR_SIZE]; /* Cacert file names */
+ unsigned int spcount; /* Number of service points */
+ char srvpoints[OSP_MAX_SRVS][OSP_NORSTR_SIZE]; /* Service point URLs */
+ int maxconnections; /* Max number of connections */
+ int retrydelay; /* Retry delay */
+ int retrylimit; /* Retry limit */
+ int timeout; /* Timeout in ms */
+ char source[OSP_NORSTR_SIZE]; /* IP of self */
+ enum osp_authpolicy authpolicy; /* OSP authentication policy */
+ char* defaultprotocol; /* OSP default destination protocol */
+ OSPTPROVHANDLE handle; /* OSP provider handle */
+ struct osp_provider* next; /* Pointer to next OSP provider */
+};
+
+/* Call ID */
+struct osp_callid {
+ unsigned char buf[OSPC_CALLID_MAXSIZE]; /* Call ID string */
+ unsigned int len; /* Call ID length */
+};
+
+/* OSP Application In/Output Results */
+struct osp_result {
+ int inhandle; /* Inbound transaction handle */
+ int outhandle; /* Outbound transaction handle */
+ unsigned int intimelimit; /* Inbound duration limit */
+ unsigned int outtimelimit; /* Outbound duration limit */
+ char tech[OSP_TECHSTR_SIZE]; /* Outbound Asterisk TECH string */
+ char dest[OSP_NORSTR_SIZE]; /* Outbound destination IP address */
+ char called[OSP_NORSTR_SIZE]; /* Outbound called number, may be translated */
+ char calling[OSP_NORSTR_SIZE]; /* Outbound calling number, may be translated */
+ char token[OSP_TOKSTR_SIZE]; /* Outbound OSP token */
+ char networkid[OSP_NORSTR_SIZE]; /* Outbound network ID */
+ unsigned int numresults; /* Number of remain outbound destinations */
+ struct osp_callid outcallid; /* Outbound call ID */
+};
+
+/* OSP Module Global Variables */
+AST_MUTEX_DEFINE_STATIC(osplock); /* Lock of OSP provider list */
+static int osp_initialized = 0; /* Init flag */
+static int osp_hardware = 0; /* Hardware accelleration flag */
+static struct osp_provider* ospproviders = NULL; /* OSP provider list */
+static unsigned int osp_tokenformat = TOKEN_ALGO_SIGNED; /* Token format supported */
+
+/* OSP Client Wrapper APIs */
+
+/*!
+ * \brief Create OSP provider handle according to configuration
+ * \param cfg OSP configuration
+ * \param provider OSP provider context name
+ * \return 1 Success, 0 Failed, -1 Error
+ */
+static int osp_create_provider(
+ struct ast_config* cfg,
+ const char* provider)
+{
+ int res;
+ unsigned int t, i, j;
+ struct osp_provider* p;
+ struct ast_variable* v;
+ OSPTPRIVATEKEY privatekey;
+ OSPTCERT localcert;
+ const char* psrvpoints[OSP_MAX_SRVS];
+ OSPTCERT cacerts[OSP_MAX_CERTS];
+ const OSPTCERT* pcacerts[OSP_MAX_CERTS];
+ int error = OSPC_ERR_NO_ERROR;
+
+ if (!(p = ast_calloc(1, sizeof(*p)))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ ast_copy_string(p->name, provider, sizeof(p->name));
+ snprintf(p->privatekey, sizeof(p->privatekey), "%s/%s-privatekey.pem", ast_config_AST_KEY_DIR, provider);
+ snprintf(p->localcert, sizeof(p->localcert), "%s/%s-localcert.pem", ast_config_AST_KEY_DIR, provider);
+ p->maxconnections = OSP_DEF_MAXCONNECTIONS;
+ p->retrydelay = OSP_DEF_RETRYDELAY;
+ p->retrylimit = OSP_DEF_RETRYLIMIT;
+ p->timeout = OSP_DEF_TIMEOUT;
+ p->authpolicy = OSP_DEF_AUTHPOLICY;
+ p->defaultprotocol = OSP_DEF_PROTOCOL;
+ p->handle = OSP_INVALID_HANDLE;
+
+ v = ast_variable_browse(cfg, provider);
+ while(v) {
+ if (!strcasecmp(v->name, "privatekey")) {
+ if (v->value[0] == '/') {
+ ast_copy_string(p->privatekey, v->value, sizeof(p->privatekey));
+ } else {
+ snprintf(p->privatekey, sizeof(p->privatekey), "%s/%s", ast_config_AST_KEY_DIR, v->value);
+ }
+ ast_debug(1, "OSP: privatekey '%s'\n", p->privatekey);
+ } else if (!strcasecmp(v->name, "localcert")) {
+ if (v->value[0] == '/') {
+ ast_copy_string(p->localcert, v->value, sizeof(p->localcert));
+ } else {
+ snprintf(p->localcert, sizeof(p->localcert), "%s/%s", ast_config_AST_KEY_DIR, v->value);
+ }
+ ast_debug(1, "OSP: localcert '%s'\n", p->localcert);
+ } else if (!strcasecmp(v->name, "cacert")) {
+ if (p->cacount < OSP_MAX_CERTS) {
+ if (v->value[0] == '/') {
+ ast_copy_string(p->cacerts[p->cacount], v->value, sizeof(p->cacerts[0]));
+ } else {
+ snprintf(p->cacerts[p->cacount], sizeof(p->cacerts[0]), "%s/%s", ast_config_AST_KEY_DIR, v->value);
+ }
+ ast_debug(1, "OSP: cacert[%d]: '%s'\n", p->cacount, p->cacerts[p->cacount]);
+ p->cacount++;
+ } else {
+ ast_log(LOG_WARNING, "OSP: Too many CA Certificates at line %d\n", v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "servicepoint")) {
+ if (p->spcount < OSP_MAX_SRVS) {
+ ast_copy_string(p->srvpoints[p->spcount], v->value, sizeof(p->srvpoints[0]));
+ ast_debug(1, "OSP: servicepoint[%d]: '%s'\n", p->spcount, p->srvpoints[p->spcount]);
+ p->spcount++;
+ } else {
+ ast_log(LOG_WARNING, "OSP: Too many Service Points at line %d\n", v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "maxconnections")) {
+ if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_MAXCONNECTIONS) && (t <= OSP_MAX_MAXCONNECTIONS)) {
+ p->maxconnections = t;
+ ast_debug(1, "OSP: maxconnections '%d'\n", t);
+ } else {
+ ast_log(LOG_WARNING, "OSP: maxconnections should be an integer from %d to %d, not '%s' at line %d\n",
+ OSP_MIN_MAXCONNECTIONS, OSP_MAX_MAXCONNECTIONS, v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "retrydelay")) {
+ if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_RETRYDELAY) && (t <= OSP_MAX_RETRYDELAY)) {
+ p->retrydelay = t;
+ ast_debug(1, "OSP: retrydelay '%d'\n", t);
+ } else {
+ ast_log(LOG_WARNING, "OSP: retrydelay should be an integer from %d to %d, not '%s' at line %d\n",
+ OSP_MIN_RETRYDELAY, OSP_MAX_RETRYDELAY, v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "retrylimit")) {
+ if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_RETRYLIMIT) && (t <= OSP_MAX_RETRYLIMIT)) {
+ p->retrylimit = t;
+ ast_debug(1, "OSP: retrylimit '%d'\n", t);
+ } else {
+ ast_log(LOG_WARNING, "OSP: retrylimit should be an integer from %d to %d, not '%s' at line %d\n",
+ OSP_MIN_RETRYLIMIT, OSP_MAX_RETRYLIMIT, v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "timeout")) {
+ if ((sscanf(v->value, "%d", &t) == 1) && (t >= OSP_MIN_TIMEOUT) && (t <= OSP_MAX_TIMEOUT)) {
+ p->timeout = t;
+ ast_debug(1, "OSP: timeout '%d'\n", t);
+ } else {
+ ast_log(LOG_WARNING, "OSP: timeout should be an integer from %d to %d, not '%s' at line %d\n",
+ OSP_MIN_TIMEOUT, OSP_MAX_TIMEOUT, v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "source")) {
+ ast_copy_string(p->source, v->value, sizeof(p->source));
+ ast_debug(1, "OSP: source '%s'\n", p->source);
+ } else if (!strcasecmp(v->name, "authpolicy")) {
+ if ((sscanf(v->value, "%d", &t) == 1) && ((t == OSP_AUTH_NO) || (t == OSP_AUTH_YES) || (t == OSP_AUTH_EXCLUSIVE))) {
+ p->authpolicy = t;
+ ast_debug(1, "OSP: authpolicy '%d'\n", t);
+ } else {
+ ast_log(LOG_WARNING, "OSP: authpolicy should be %d, %d or %d, not '%s' at line %d\n",
+ OSP_AUTH_NO, OSP_AUTH_YES, OSP_AUTH_EXCLUSIVE, v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "defaultprotocol")) {
+ if (!strcasecmp(v->value, OSP_PROT_SIP)) {
+ p->defaultprotocol = OSP_PROT_SIP;
+ ast_debug(1, "OSP: default protocol '%s'\n", p->defaultprotocol);
+ } else if (!strcasecmp(v->value, OSP_PROT_H323)) {
+ p->defaultprotocol = OSP_PROT_H323;
+ ast_debug(1, "OSP: default protocol '%s'\n", p->defaultprotocol);
+ } else if (!strcasecmp(v->value, OSP_PROT_IAX)) {
+ p->defaultprotocol = OSP_PROT_IAX;
+ ast_debug(1, "OSP: default protocol '%s'\n", p->defaultprotocol);
+ } else {
+ ast_log(LOG_WARNING, "OSP: default protocol should be %s, %s, %s, or %s not '%s' at line %d\n",
+ OSP_PROT_SIP, OSP_PROT_H323, OSP_PROT_IAX, OSP_PROT_OTHER, v->value, v->lineno);
+ }
+ }
+ v = v->next;
+ }
+
+ error = OSPPUtilLoadPEMPrivateKey((unsigned char*)p->privatekey, &privatekey);
+ if (error != OSPC_ERR_NO_ERROR) {
+ ast_log(LOG_WARNING, "OSP: Unable to load privatekey '%s', error '%d'\n", p->privatekey, error);
+ ast_free(p);
+ return 0;
+ }
+
+ error = OSPPUtilLoadPEMCert((unsigned char*)p->localcert, &localcert);
+ if (error != OSPC_ERR_NO_ERROR) {
+ ast_log(LOG_WARNING, "OSP: Unable to load localcert '%s', error '%d'\n", p->localcert, error);
+ if (privatekey.PrivateKeyData) {
+ ast_free(privatekey.PrivateKeyData);
+ }
+ ast_free(p);
+ return 0;
+ }
+
+ if (p->cacount < 1) {
+ snprintf(p->cacerts[p->cacount], sizeof(p->cacerts[0]), "%s/%s-cacert.pem", ast_config_AST_KEY_DIR, provider);
+ ast_debug(1, "OSP: cacert[%d]: '%s'\n", p->cacount, p->cacerts[p->cacount]);
+ p->cacount++;
+ }
+ for (i = 0; i < p->cacount; i++) {
+ error = OSPPUtilLoadPEMCert((unsigned char*)p->cacerts[i], &cacerts[i]);
+ if (error != OSPC_ERR_NO_ERROR) {
+ ast_log(LOG_WARNING, "OSP: Unable to load cacert '%s', error '%d'\n", p->cacerts[i], error);
+ for (j = 0; j < i; j++) {
+ if (cacerts[j].CertData) {
+ ast_free(cacerts[j].CertData);
+ }
+ }
+ if (localcert.CertData) {
+ ast_free(localcert.CertData);
+ }
+ if (privatekey.PrivateKeyData) {
+ ast_free(privatekey.PrivateKeyData);
+ }
+ ast_free(p);
+ return 0;
+ }
+ pcacerts[i] = &cacerts[i];
+ }
+
+ for (i = 0; i < p->spcount; i++) {
+ psrvpoints[i] = p->srvpoints[i];
+ }
+
+ error = OSPPProviderNew(
+ p->spcount,
+ psrvpoints,
+ NULL,
+ OSP_AUDIT_URL,
+ &privatekey,
+ &localcert,
+ p->cacount,
+ pcacerts,
+ OSP_LOCAL_VALIDATION,
+ OSP_SSL_LIFETIME,
+ p->maxconnections,
+ OSP_HTTP_PERSISTENCE,
+ p->retrydelay,
+ p->retrylimit,
+ p->timeout,
+ OSP_CUSTOMER_ID,
+ OSP_DEVICE_ID,
+ &p->handle);
+ if (error != OSPC_ERR_NO_ERROR) {
+ ast_log(LOG_WARNING, "OSP: Unable to create provider '%s', error '%d'\n", provider, error);
+ ast_free(p);
+ res = -1;
+ } else {
+ ast_debug(1, "OSP: provider '%s'\n", provider);
+ ast_mutex_lock(&osplock);
+ p->next = ospproviders;
+ ospproviders = p;
+ ast_mutex_unlock(&osplock);
+ res = 1;
+ }
+
+ for (i = 0; i < p->cacount; i++) {
+ if (cacerts[i].CertData) {
+ ast_free(cacerts[i].CertData);
+ }
+ }
+ if (localcert.CertData) {
+ ast_free(localcert.CertData);
+ }
+ if (privatekey.PrivateKeyData) {
+ ast_free(privatekey.PrivateKeyData);
+ }
+
+ return res;
+}
+
+/*!
+ * \brief Get OSP provider by name
+ * \param name OSP provider context name
+ * \param provider OSP provider structure
+ * \return 1 Success, 0 Failed, -1 Error
+ */
+static int osp_get_provider(
+ const char* name,
+ struct osp_provider** provider)
+{
+ int res = 0;
+ struct osp_provider* p;
+
+ ast_mutex_lock(&osplock);
+ p = ospproviders;
+ while(p) {
+ if (!strcasecmp(p->name, name)) {
+ *provider = p;
+ ast_debug(1, "OSP: find provider '%s'\n", name);
+ res = 1;
+ break;
+ }
+ p = p->next;
+ }
+ ast_mutex_unlock(&osplock);
+
+ return res;
+}
+
+/*!
+ * \brief Create OSP transaction handle
+ * \param provider OSP provider context name
+ * \param transaction OSP transaction handle, output
+ * \param sourcesize Size of source buffer, in/output
+ * \param source Source of provider, output
+ * \return 1 Success, 0 Failed, -1 Error
+ */
+static int osp_create_transaction(
+ const char* provider,
+ int* transaction,
+ unsigned int sourcesize,
+ char* source)
+{
+ int res = 0;
+ struct osp_provider* p;
+ int error;
+
+ ast_mutex_lock(&osplock);
+ p = ospproviders;
+ while(p) {
+ if (!strcasecmp(p->name, provider)) {
+ error = OSPPTransactionNew(p->handle, transaction);
+ if (error == OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: transaction '%d'\n", *transaction);
+ ast_copy_string(source, p->source, sourcesize);
+ ast_debug(1, "OSP: source '%s'\n", source);
+ res = 1;
+ } else {
+ *transaction = OSP_INVALID_HANDLE;
+ ast_debug(1, "OSP: Unable to create transaction handle, error '%d'\n", error);
+ res = -1;
+ }
+ break;
+ }
+ p = p->next;
+ }
+ ast_mutex_unlock(&osplock);
+
+ return res;
+}
+
+/*!
+ * \brief Convert address to "[x.x.x.x]" or "host.domain" format
+ * \param src Source address string
+ * \param dst Destination address string
+ * \param buffersize Size of dst buffer
+ */
+static void osp_convert_address(
+ const char* src,
+ char* dst,
+ int buffersize)
+{
+ struct in_addr inp;
+
+ if (inet_aton(src, &inp) != 0) {
+ snprintf(dst, buffersize, "[%s]", src);
+ } else {
+ snprintf(dst, buffersize, "%s", src);
+ }
+}
+
+/*!
+ * \brief Validate OSP token of inbound call
+ * \param transaction OSP transaction handle
+ * \param source Source of inbound call
+ * \param destination Destination of inbound call
+ * \param calling Calling number
+ * \param called Called number
+ * \param token OSP token, may be empty
+ * \param timelimit Call duration limit, output
+ * \return 1 Success, 0 Failed, -1 Error
+ */
+static int osp_validate_token(
+ int transaction,
+ const char* source,
+ const char* destination,
+ const char* calling,
+ const char* called,
+ const char* token,
+ unsigned int* timelimit)
+{
+ int res;
+ int tokenlen;
+ unsigned char tokenstr[OSP_TOKSTR_SIZE];
+ char src[OSP_NORSTR_SIZE];
+ char dst[OSP_NORSTR_SIZE];
+ unsigned int authorised;
+ unsigned int dummy = 0;
+ int error;
+
+ tokenlen = ast_base64decode(tokenstr, token, strlen(token));
+ osp_convert_address(source, src, sizeof(src));
+ osp_convert_address(destination, dst, sizeof(dst));
+ error = OSPPTransactionValidateAuthorisation(
+ transaction,
+ src,
+ dst,
+ NULL,
+ NULL,
+ calling ? calling : "",
+ OSPC_E164,
+ called,
+ OSPC_E164,
+ 0,
+ NULL,
+ tokenlen,
+ (char*)tokenstr,
+ &authorised,
+ timelimit,
+ &dummy,
+ NULL,
+ osp_tokenformat);
+ if (error != OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: Unable to validate inbound token\n");
+ res = -1;
+ } else if (authorised) {
+ ast_debug(1, "OSP: Authorised\n");
+ res = 1;
+ } else {
+ ast_debug(1, "OSP: Unauthorised\n");
+ res = 0;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief Choose min duration limit
+ * \param in Inbound duration limit
+ * \param out Outbound duration limit
+ * \return min duration limit
+ */
+static unsigned int osp_choose_timelimit(
+ unsigned int in,
+ unsigned int out)
+{
+ if (in == OSP_DEF_TIMELIMIT) {
+ return out;
+ } else if (out == OSP_DEF_TIMELIMIT) {
+ return in;
+ } else {
+ return in < out ? in : out;
+ }
+}
+
+/*!
+ * \brief Choose min duration limit
+ * \param provider OSP provider
+ * \param called Called number
+ * \param calling Calling number
+ * \param destination Destination IP in '[x.x.x.x]' format
+ * \param tokenlen OSP token length
+ * \param token OSP token
+ * \param reason Failure reason, output
+ * \param result OSP lookup results, in/output
+ * \return 1 Success, 0 Failed, -1 Error
+ */
+static int osp_check_destination(
+ struct osp_provider* provider,
+ const char* called,
+ const char* calling,
+ char* destination,
+ unsigned int tokenlen,
+ const char* token,
+ enum OSPEFAILREASON* reason,
+ struct osp_result* result)
+{
+ int res;
+ OSPE_DEST_OSP_ENABLED enabled;
+ OSPE_DEST_PROT protocol;
+ int error;
+
+ if (strlen(destination) <= 2) {
+ ast_debug(1, "OSP: Wrong destination format '%s'\n", destination);
+ *reason = OSPC_FAIL_NORMAL_UNSPECIFIED;
+ return -1;
+ }
+
+ if ((error = OSPPTransactionIsDestOSPEnabled(result->outhandle, &enabled)) != OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: Unable to get destination OSP version, error '%d'\n", error);
+ *reason = OSPC_FAIL_NORMAL_UNSPECIFIED;
+ return -1;
+ }
+
+ if (enabled == OSPE_OSP_FALSE) {
+ result->token[0] = '\0';
+ } else {
+ ast_base64encode(result->token, (const unsigned char*)token, tokenlen, sizeof(result->token) - 1);
+ }
+
+ if ((error = OSPPTransactionGetDestNetworkId(result->outhandle, result->networkid)) != OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: Unable to get destination network ID, error '%d'\n", error);
+ result->networkid[0] = '\0';
+ }
+
+ if ((error = OSPPTransactionGetDestProtocol(result->outhandle, &protocol)) != OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: Unable to get destination protocol, error '%d'\n", error);
+ *reason = OSPC_FAIL_NORMAL_UNSPECIFIED;
+ result->token[0] = '\0';
+ result->networkid[0] = '\0';
+ return -1;
+ }
+
+ res = 1;
+ /* Strip leading and trailing brackets */
+ destination[strlen(destination) - 1] = '\0';
+ switch(protocol) {
+ case OSPE_DEST_PROT_H323_SETUP:
+ ast_debug(1, "OSP: protocol '%s'\n", OSP_PROT_H323);
+ ast_copy_string(result->tech, OSP_TECH_H323, sizeof(result->tech));
+ ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
+ ast_copy_string(result->called, called, sizeof(result->called));
+ ast_copy_string(result->calling, calling, sizeof(result->calling));
+ break;
+ case OSPE_DEST_PROT_SIP:
+ ast_debug(1, "OSP: protocol '%s'\n", OSP_PROT_SIP);
+ ast_copy_string(result->tech, OSP_TECH_SIP, sizeof(result->tech));
+ ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
+ ast_copy_string(result->called, called, sizeof(result->called));
+ ast_copy_string(result->calling, calling, sizeof(result->calling));
+ break;
+ case OSPE_DEST_PROT_IAX:
+ ast_debug(1, "OSP: protocol '%s'\n", OSP_PROT_IAX);
+ ast_copy_string(result->tech, OSP_TECH_IAX, sizeof(result->tech));
+ ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
+ ast_copy_string(result->called, called, sizeof(result->called));
+ ast_copy_string(result->calling, calling, sizeof(result->calling));
+ break;
+ case OSPE_DEST_PROT_UNDEFINED:
+ case OSPE_DEST_PROT_UNKNOWN:
+ ast_debug(1, "OSP: unknown/undefined protocol '%d'\n", protocol);
+ ast_debug(1, "OSP: use default protocol '%s'\n", provider->defaultprotocol);
+
+ ast_copy_string(result->tech, provider->defaultprotocol, sizeof(result->tech));
+ ast_copy_string(result->dest, destination + 1, sizeof(result->dest));
+ ast_copy_string(result->called, called, sizeof(result->called));
+ ast_copy_string(result->calling, calling, sizeof(result->calling));
+ break;
+ case OSPE_DEST_PROT_H323_LRQ:
+ default:
+ ast_log(LOG_WARNING, "OSP: unsupported protocol '%d'\n", protocol);
+ *reason = OSPC_FAIL_PROTOCOL_ERROR;
+ result->token[0] = '\0';
+ result->networkid[0] = '\0';
+ res = 0;
+ break;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief Convert Asterisk status to TC code
+ * \param cause Asterisk hangup cause
+ * \return OSP TC code
+ */
+static enum OSPEFAILREASON asterisk2osp(
+ int cause)
+{
+ return (enum OSPEFAILREASON)cause;
+}
+
+/*!
+ * \brief OSP Authentication function
+ * \param provider OSP provider context name
+ * \param transaction OSP transaction handle, output
+ * \param source Source of inbound call
+ * \param calling Calling number
+ * \param called Called number
+ * \param token OSP token, may be empty
+ * \param timelimit Call duration limit, output
+ * \return 1 Authenricated, 0 Unauthenticated, -1 Error
+ */
+static int osp_auth(
+ const char* provider,
+ int* transaction,
+ const char* source,
+ const char* calling,
+ const char* called,
+ const char* token,
+ unsigned int* timelimit)
+{
+ int res;
+ struct osp_provider* p;
+ char dest[OSP_NORSTR_SIZE];
+
+ *transaction = OSP_INVALID_HANDLE;
+ *timelimit = OSP_DEF_TIMELIMIT;
+
+ if ((res = osp_get_provider(provider, &p)) <= 0) {
+ ast_debug(1, "OSP: Unabe to find OSP provider '%s'\n", provider);
+ return res;
+ }
+
+ switch (p->authpolicy) {
+ case OSP_AUTH_NO:
+ res = 1;
+ break;
+ case OSP_AUTH_EXCLUSIVE:
+ if (ast_strlen_zero(token)) {
+ res = 0;
+ } else if ((res = osp_create_transaction(provider, transaction, sizeof(dest), dest)) <= 0) {
+ ast_debug(1, "OSP: Unable to generate transaction handle\n");
+ *transaction = OSP_INVALID_HANDLE;
+ res = 0;
+ } else if((res = osp_validate_token(*transaction, source, dest, calling, called, token, timelimit)) <= 0) {
+ OSPPTransactionRecordFailure(*transaction, OSPC_FAIL_CALL_REJECTED);
+ }
+ break;
+ case OSP_AUTH_YES:
+ default:
+ if (ast_strlen_zero(token)) {
+ res = 1;
+ } else if ((res = osp_create_transaction(provider, transaction, sizeof(dest), dest)) <= 0) {
+ ast_debug(1, "OSP: Unable to generate transaction handle\n");
+ *transaction = OSP_INVALID_HANDLE;
+ res = 0;
+ } else if((res = osp_validate_token(*transaction, source, dest, calling, called, token, timelimit)) <= 0) {
+ OSPPTransactionRecordFailure(*transaction, OSPC_FAIL_CALL_REJECTED);
+ }
+ break;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief Create a UUID
+ * \param uuid UUID buffer
+ * \param buffersize UUID buffer size
+ * \return 1 Created, -1 Error
+ */
+static int osp_create_uuid(
+ unsigned char* uuid,
+ unsigned int* buffersize)
+{
+ int i, res;
+ long int* tmp;
+
+ if (*buffersize >= OSP_UUID_SIZE) {
+ tmp = (long int*)uuid;
+ for (i = 0; i < OSP_UUID_SIZE / sizeof(long int); i++) {
+ tmp[i] = ast_random();
+ }
+ *buffersize = OSP_UUID_SIZE;
+ res = 1;
+ } else {
+ res = -1;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief UUID to string
+ * \param uuid UUID
+ * \param buffer String buffer
+ * \param buffersize String buffer size
+ * \return 1 Successed, -1 Error
+ */
+static int osp_uuid2str(
+ unsigned char* uuid,
+ char* buffer,
+ unsigned int buffersize)
+{
+ int res;
+
+ if (buffersize > OSP_UUIDSTR_SIZE) {
+ snprintf(buffer, buffersize, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+ res = 1;
+ } else {
+ res = -1;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief Create a call ID according to the type
+ * \param type Call ID type
+ * \param callid Call ID buffer
+ * \return 1 Created, 0 Not create, -1 Error
+ */
+static int osp_create_callid(
+ unsigned int type,
+ struct osp_callid* callid)
+{
+ int res;
+
+ callid->len = sizeof(callid->buf);
+ switch (type) {
+ case OSP_CALLID_H323:
+ res = osp_create_uuid(callid->buf, &callid->len);
+ break;
+ case OSP_CALLID_SIP:
+ case OSP_CALLID_IAX:
+ res = 0;
+ default:
+ res = -1;
+ break;
+ }
+
+ if ((res != 1) && (callid->len != 0)) {
+ callid->buf[0] = '\0';
+ callid->len = 0;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief OSP Lookup function
+ * \param provider OSP provider context name
+ * \param srcdev Source device of outbound call
+ * \param calling Calling number
+ * \param called Called number
+ * \param callidtypes Call ID types
+ * \param result Lookup results
+ * \return 1 Found , 0 No route, -1 Error
+ */
+static int osp_lookup(
+ const char* provider,
+ const char* srcdev,
+ const char* calling,
+ const char* called,
+ unsigned int callidtypes,
+ struct osp_result* result)
+{
+ int res;
+ struct osp_provider* p;
+ char source[OSP_NORSTR_SIZE];
+ char callingnum[OSP_NORSTR_SIZE];
+ char callednum[OSP_NORSTR_SIZE];
+ char destination[OSP_NORSTR_SIZE];
+ unsigned int tokenlen;
+ char token[OSP_TOKSTR_SIZE];
+ char src[OSP_NORSTR_SIZE];
+ char dev[OSP_NORSTR_SIZE];
+ unsigned int i, type;
+ struct osp_callid callid;
+ unsigned int callidnum;
+ OSPTCALLID* callids[OSP_CALLID_MAXNUM];
+ unsigned int dummy = 0;
+ enum OSPEFAILREASON reason;
+ int error;
+
+ result->outhandle = OSP_INVALID_HANDLE;
+ result->tech[0] = '\0';
+ result->dest[0] = '\0';
+ result->called[0] = '\0';
+ result->calling[0] = '\0';
+ result->token[0] = '\0';
+ result->networkid[0] = '\0';
+ result->numresults = 0;
+ result->outtimelimit = OSP_DEF_TIMELIMIT;
+
+ if ((res = osp_get_provider(provider, &p)) <= 0) {
+ ast_debug(1, "OSP: Unabe to find OSP provider '%s'\n", provider);
+ return res;
+ }
+
+ if ((res = osp_create_transaction(provider, &result->outhandle, sizeof(source), source)) <= 0) {
+ ast_debug(1, "OSP: Unable to generate transaction handle\n");
+ result->outhandle = OSP_INVALID_HANDLE;
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
+ }
+ return -1;
+ }
+
+ callidnum = 0;
+ callids[0] = NULL;
+ for (i = 0; i < OSP_CALLID_MAXNUM; i++) {
+ type = 1 << i;
+ if (callidtypes & type) {
+ error = osp_create_callid(type, &callid);
+ if (error == 1) {
+ callids[callidnum] = OSPPCallIdNew(callid.len, callid.buf);
+ callidnum++;
+ }
+ }
+ }
+
+ osp_convert_address(source, src, sizeof(src));
+ osp_convert_address(srcdev, dev, sizeof(dev));
+ result->numresults = OSP_DEF_DESTINATIONS;
+ error = OSPPTransactionRequestAuthorisation(
+ result->outhandle,
+ src,
+ dev,
+ calling ? calling : "",
+ OSPC_E164,
+ called,
+ OSPC_E164,
+ NULL,
+ callidnum,
+ callids,
+ NULL,
+ &result->numresults,
+ &dummy,
+ NULL);
+
+ for (i = 0; i < callidnum; i++) {
+ OSPPCallIdDelete(&callids[i]);
+ }
+
+ if (error != OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: Unable to request authorization\n");
+ result->numresults = 0;
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
+ }
+ return -1;
+ }
+
+ if (!result->numresults) {
+ ast_debug(1, "OSP: No more destination\n");
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
+ }
+ return 0;
+ }
+
+ result->outcallid.len = sizeof(result->outcallid.buf);
+ tokenlen = sizeof(token);
+ error = OSPPTransactionGetFirstDestination(
+ result->outhandle,
+ 0,
+ NULL,
+ NULL,
+ &result->outtimelimit,
+ &result->outcallid.len,
+ result->outcallid.buf,
+ sizeof(callednum),
+ callednum,
+ sizeof(callingnum),
+ callingnum,
+ sizeof(destination),
+ destination,
+ 0,
+ NULL,
+ &tokenlen,
+ token);
+ if (error != OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: Unable to get first route\n");
+ result->numresults = 0;
+ result->outtimelimit = OSP_DEF_TIMELIMIT;
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
+ }
+ return -1;
+ }
+
+ result->numresults--;
+ result->outtimelimit = osp_choose_timelimit(result->intimelimit, result->outtimelimit);
+ ast_debug(1, "OSP: outtimelimit '%d'\n", result->outtimelimit);
+ ast_debug(1, "OSP: called '%s'\n", callednum);
+ ast_debug(1, "OSP: calling '%s'\n", callingnum);
+ ast_debug(1, "OSP: destination '%s'\n", destination);
+ ast_debug(1, "OSP: token size '%d'\n", tokenlen);
+
+ if ((res = osp_check_destination(p, callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) {
+ return 1;
+ }
+
+ if (!result->numresults) {
+ ast_debug(1, "OSP: No more destination\n");
+ result->outtimelimit = OSP_DEF_TIMELIMIT;
+ OSPPTransactionRecordFailure(result->outhandle, reason);
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
+ }
+ return 0;
+ }
+
+ while(result->numresults) {
+ result->outcallid.len = sizeof(result->outcallid.buf);
+ tokenlen = sizeof(token);
+ error = OSPPTransactionGetNextDestination(
+ result->outhandle,
+ reason,
+ 0,
+ NULL,
+ NULL,
+ &result->outtimelimit,
+ &result->outcallid.len,
+ result->outcallid.buf,
+ sizeof(callednum),
+ callednum,
+ sizeof(callingnum),
+ callingnum,
+ sizeof(destination),
+ destination,
+ 0,
+ NULL,
+ &tokenlen,
+ token);
+ if (error == OSPC_ERR_NO_ERROR) {
+ result->numresults--;
+ result->outtimelimit = osp_choose_timelimit(result->intimelimit, result->outtimelimit);
+ ast_debug(1, "OSP: outtimelimit '%d'\n", result->outtimelimit);
+ ast_debug(1, "OSP: called '%s'\n", callednum);
+ ast_debug(1, "OSP: calling '%s'\n", callingnum);
+ ast_debug(1, "OSP: destination '%s'\n", destination);
+ ast_debug(1, "OSP: token size '%d'\n", tokenlen);
+
+ if ((res = osp_check_destination(p, callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) {
+ break;
+ } else if (!result->numresults) {
+ ast_debug(1, "OSP: No more destination\n");
+ OSPPTransactionRecordFailure(result->outhandle, reason);
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
+ }
+ res = 0;
+ break;
+ }
+ } else {
+ ast_debug(1, "OSP: Unable to get route, error '%d'\n", error);
+ result->numresults = 0;
+ result->outtimelimit = OSP_DEF_TIMELIMIT;
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
+ }
+ res = -1;
+ break;
+ }
+ }
+ return res;
+}
+
+/*!
+ * \brief OSP Lookup Next function
+ * \param provider OSP provider name
+ * \param cause Asterisk hangup cuase
+ * \param result Lookup results, in/output
+ * \return 1 Found , 0 No route, -1 Error
+ */
+static int osp_next(
+ const char* provider,
+ int cause,
+ struct osp_result* result)
+{
+ int res;
+ struct osp_provider* p;
+ char callingnum[OSP_NORSTR_SIZE];
+ char callednum[OSP_NORSTR_SIZE];
+ char destination[OSP_NORSTR_SIZE];
+ unsigned int tokenlen;
+ char token[OSP_TOKSTR_SIZE];
+ enum OSPEFAILREASON reason;
+ int error;
+
+ result->tech[0] = '\0';
+ result->dest[0] = '\0';
+ result->called[0] = '\0';
+ result->calling[0] = '\0';
+ result->token[0] = '\0';
+ result->networkid[0] = '\0';
+ result->outtimelimit = OSP_DEF_TIMELIMIT;
+
+ if ((res = osp_get_provider(provider, &p)) <= 0) {
+ ast_debug(1, "OSP: Unabe to find OSP provider '%s'\n", provider);
+ return res;
+ }
+
+ if (result->outhandle == OSP_INVALID_HANDLE) {
+ ast_debug(1, "OSP: Transaction handle undefined\n");
+ result->numresults = 0;
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
+ }
+ return -1;
+ }
+
+ reason = asterisk2osp(cause);
+
+ if (!result->numresults) {
+ ast_debug(1, "OSP: No more destination\n");
+ OSPPTransactionRecordFailure(result->outhandle, reason);
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
+ }
+ return 0;
+ }
+
+ while(result->numresults) {
+ result->outcallid.len = sizeof(result->outcallid.buf);
+ tokenlen = sizeof(token);
+ error = OSPPTransactionGetNextDestination(
+ result->outhandle,
+ reason,
+ 0,
+ NULL,
+ NULL,
+ &result->outtimelimit,
+ &result->outcallid.len,
+ result->outcallid.buf,
+ sizeof(callednum),
+ callednum,
+ sizeof(callingnum),
+ callingnum,
+ sizeof(destination),
+ destination,
+ 0,
+ NULL,
+ &tokenlen,
+ token);
+ if (error == OSPC_ERR_NO_ERROR) {
+ result->numresults--;
+ result->outtimelimit = osp_choose_timelimit(result->intimelimit, result->outtimelimit);
+ ast_debug(1, "OSP: outtimelimit '%d'\n", result->outtimelimit);
+ ast_debug(1, "OSP: called '%s'\n", callednum);
+ ast_debug(1, "OSP: calling '%s'\n", callingnum);
+ ast_debug(1, "OSP: destination '%s'\n", destination);
+ ast_debug(1, "OSP: token size '%d'\n", tokenlen);
+
+ if ((res = osp_check_destination(p, callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) {
+ res = 1;
+ break;
+ } else if (!result->numresults) {
+ ast_debug(1, "OSP: No more destination\n");
+ OSPPTransactionRecordFailure(result->outhandle, reason);
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST);
+ }
+ res = 0;
+ break;
+ }
+ } else {
+ ast_debug(1, "OSP: Unable to get route, error '%d'\n", error);
+ result->token[0] = '\0';
+ result->numresults = 0;
+ result->outtimelimit = OSP_DEF_TIMELIMIT;
+ if (result->inhandle != OSP_INVALID_HANDLE) {
+ OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NORMAL_UNSPECIFIED);
+ }
+ res = -1;
+ break;
+ }
+ }
+
+ return res;
+}
+
+/*!
+ * \brief OSP Finish function
+ * \param handle OSP in/outbound transaction handle
+ * \param recorded If failure reason has been recorded
+ * \param cause Asterisk hangup cause
+ * \param start Call start time
+ * \param connect Call connect time
+ * \param end Call end time
+ * \param release Who release first, 0 source, 1 destination
+ * \return 1 Success, 0 Failed, -1 Error
+ */
+static int osp_finish(
+ int handle,
+ int recorded,
+ int cause,
+ time_t start,
+ time_t connect,
+ time_t end,
+ unsigned int release)
+{
+ int res;
+ enum OSPEFAILREASON reason;
+ time_t alert = 0;
+ unsigned isPddInfoPresent = 0;
+ unsigned pdd = 0;
+ unsigned int dummy = 0;
+ int error;
+
+ if (handle == OSP_INVALID_HANDLE) {
+ return 0;
+ }
+
+ if (!recorded) {
+ reason = asterisk2osp(cause);
+ OSPPTransactionRecordFailure(handle, reason);
+ }
+
+ error = OSPPTransactionReportUsage(
+ handle,
+ difftime(end, connect),
+ start,
+ end,
+ alert,
+ connect,
+ isPddInfoPresent,
+ pdd,
+ release,
+ (unsigned char*)"",
+ 0,
+ 0,
+ 0,
+ 0,
+ &dummy,
+ NULL);
+ if (error == OSPC_ERR_NO_ERROR) {
+ ast_debug(1, "OSP: Usage reported\n");
+ res = 1;
+ } else {
+ ast_debug(1, "OSP: Unable to report usage, error '%d'\n", error);
+ res = -1;
+ }
+ OSPPTransactionDelete(handle);
+
+ return res;
+}
+
+/* OSP Application APIs */
+
+/*!
+ * \brief OSP Application OSPAuth
+ * \param chan Channel
+ * \param data Parameter
+ * \return 0 Success, -1 Failed
+ */
+static int ospauth_exec(
+ struct ast_channel* chan,
+ void* data)
+{
+ int res;
+ const char* provider = OSP_DEF_PROVIDER;
+ struct varshead* headp;
+ struct ast_var_t* current;
+ const char* source = "";
+ const char* token = "";
+ int handle;
+ unsigned int timelimit;
+ char buffer[OSP_INTSTR_SIZE];
+ const char* status;
+ char* tmp;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(provider);
+ AST_APP_ARG(options);
+ );
+
+ if (!(tmp = ast_strdupa(data))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if (!ast_strlen_zero(args.provider)) {
+ provider = args.provider;
+ }
+ ast_debug(1, "OSPAuth: provider '%s'\n", provider);
+
+ headp = &chan->varshead;
+ AST_LIST_TRAVERSE(headp, current, entries) {
+ if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
+ source = ast_var_value(current);
+ } else if (!strcasecmp(ast_var_name(current), "OSPINTOKEN")) {
+ token = ast_var_value(current);
+ }
+ }
+
+ ast_debug(1, "OSPAuth: source '%s'\n", source);
+ ast_debug(1, "OSPAuth: token size '%zd'\n", strlen(token));
+
+ if ((res = osp_auth(provider, &handle, source, chan->cid.cid_num, chan->exten, token, &timelimit)) > 0) {
+ status = AST_OSP_SUCCESS;
+ } else {
+ timelimit = OSP_DEF_TIMELIMIT;
+ if (!res) {
+ status = AST_OSP_FAILED;
+ } else {
+ status = AST_OSP_ERROR;
+ }
+ }
+
+ snprintf(buffer, sizeof(buffer), "%d", handle);
+ pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
+ ast_debug(1, "OSPAuth: OSPINHANDLE '%s'\n", buffer);
+ snprintf(buffer, sizeof(buffer), "%d", timelimit);
+ pbx_builtin_setvar_helper(chan, "OSPINTIMELIMIT", buffer);
+ ast_debug(1, "OSPAuth: OSPINTIMELIMIT '%s'\n", buffer);
+ pbx_builtin_setvar_helper(chan, "OSPAUTHSTATUS", status);
+ ast_debug(1, "OSPAuth: %s\n", status);
+
+ if(res <= 0) {
+ res = -1;
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief OSP Application OSPLookup
+ * \param chan Channel
+ * \param data Parameter
+ * \return 0 Success, -1 Failed
+ */
+static int osplookup_exec(
+ struct ast_channel* chan,
+ void* data)
+{
+ int res, cres;
+ const char* provider = OSP_DEF_PROVIDER;
+ struct varshead* headp;
+ struct ast_var_t* current;
+ const char* srcdev = "";
+ const char* netid = "";
+ char buffer[OSP_TOKSTR_SIZE];
+ unsigned int callidtypes = OSP_CALLID_UNDEFINED;
+ struct osp_result result;
+ const char* status;
+ char* tmp;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(exten);
+ AST_APP_ARG(provider);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "OSPLookup: Arg required, OSPLookup(exten[|provider[|options]])\n");
+ return -1;
+ }
+
+ if (!(tmp = ast_strdupa(data))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ ast_debug(1, "OSPLookup: exten '%s'\n", args.exten);
+
+ if (!ast_strlen_zero(args.provider)) {
+ provider = args.provider;
+ }
+ ast_debug(1, "OSPlookup: provider '%s'\n", provider);
+
+ if (args.options) {
+ if (strchr(args.options, 'h')) {
+ callidtypes |= OSP_CALLID_H323;
+ }
+ if (strchr(args.options, 's')) {
+ callidtypes |= OSP_CALLID_SIP;
+ }
+ if (strchr(args.options, 'i')) {
+ callidtypes |= OSP_CALLID_IAX;
+ }
+ }
+ ast_debug(1, "OSPLookup: call id types '%d'\n", callidtypes);
+
+ result.inhandle = OSP_INVALID_HANDLE;
+ result.intimelimit = OSP_DEF_TIMELIMIT;
+
+ headp = &chan->varshead;
+ AST_LIST_TRAVERSE(headp, current, entries) {
+ if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
+ if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
+ result.inhandle = OSP_INVALID_HANDLE;
+ }
+ } else if (!strcasecmp(ast_var_name(current), "OSPINTIMELIMIT")) {
+ if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
+ result.intimelimit = OSP_DEF_TIMELIMIT;
+ }
+ } else if (!strcasecmp(ast_var_name(current), "OSPINNETWORKID")) {
+ netid = ast_var_value(current);
+ } else if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
+ srcdev = ast_var_value(current);
+ }
+ }
+ ast_debug(1, "OSPLookup: OSPINHANDLE '%d'\n", result.inhandle);
+ ast_debug(1, "OSPLookup: OSPINTIMELIMIT '%d'\n", result.intimelimit);
+ ast_debug(1, "OSPLookup: OSPINNETWORKID '%s'\n", netid);
+ ast_debug(1, "OSPLookup: source device '%s'\n", srcdev);
+
+ if ((cres = ast_autoservice_start(chan)) < 0) {
+ return -1;
+ }
+
+ if ((res = osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, callidtypes, &result)) > 0) {
+ status = AST_OSP_SUCCESS;
+ } else {
+ result.tech[0] = '\0';
+ result.dest[0] = '\0';
+ result.called[0] = '\0';
+ result.calling[0] = '\0';
+ result.token[0] = '\0';
+ result.networkid[0] = '\0';
+ result.numresults = 0;
+ result.outtimelimit = OSP_DEF_TIMELIMIT;
+ result.outcallid.buf[0] = '\0';
+ result.outcallid.len = 0;
+ if (!res) {
+ status = AST_OSP_FAILED;
+ } else {
+ status = AST_OSP_ERROR;
+ }
+ }
+
+ snprintf(buffer, sizeof(buffer), "%d", result.outhandle);
+ pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
+ ast_debug(1, "OSPLookup: OSPOUTHANDLE '%s'\n", buffer);
+ pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
+ ast_debug(1, "OSPLookup: OSPTECH '%s'\n", result.tech);
+ pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
+ ast_debug(1, "OSPLookup: OSPDEST '%s'\n", result.dest);
+ pbx_builtin_setvar_helper(chan, "OSPCALLED", result.called);
+ ast_debug(1, "OSPLookup: OSPCALLED '%s'\n", result.called);
+ pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
+ ast_debug(1, "OSPLookup: OSPCALLING '%s'\n", result.calling);
+ pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
+ ast_debug(1, "OSPLookup: OSPOUTTOKEN size '%zd'\n", strlen(result.token));
+ snprintf(buffer, sizeof(buffer), "%d", result.numresults);
+ pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
+ ast_debug(1, "OSPLookup: OSPRESULTS '%s'\n", buffer);
+ snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
+ pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
+ ast_debug(1, "OSPLookup: OSPOUTTIMELIMIT '%s'\n", buffer);
+ snprintf(buffer, sizeof(buffer), "%d", callidtypes);
+ pbx_builtin_setvar_helper(chan, "OSPOUTCALLIDTYPES", buffer);
+ ast_debug(1, "OSPLookup: OSPOUTCALLIDTYPES '%s'\n", buffer);
+ pbx_builtin_setvar_helper(chan, "OSPLOOKUPSTATUS", status);
+ ast_debug(1, "OSPLookup: %s\n", status);
+
+ if (!strcasecmp(result.tech, OSP_TECH_H323)) {
+ if ((callidtypes & OSP_CALLID_H323) && (result.outcallid.len != 0)) {
+ osp_uuid2str(result.outcallid.buf, buffer, sizeof(buffer));
+ } else {
+ buffer[0] = '\0';
+ }
+ pbx_builtin_setvar_helper(chan, "OSPOUTCALLID", buffer);
+ snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
+ pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
+ } else if (!strcasecmp(result.tech, OSP_TECH_SIP)) {
+ snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
+ pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
+ if (!ast_strlen_zero(result.token)) {
+ snprintf(buffer, sizeof(buffer), "%s%s", OSP_SIP_HEADER, result.token);
+ pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
+ ast_debug(1, "OSPLookup: SIPADDHEADER size '%zd'\n", strlen(buffer));
+ }
+ } else if (!strcasecmp(result.tech, OSP_TECH_IAX)) {
+ snprintf(buffer, sizeof(buffer), "%s/%s/%s", result.tech, result.dest, result.called);
+ pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
+ }
+
+ if ((cres = ast_autoservice_stop(chan)) < 0) {
+ return -1;
+ }
+
+ if(res <= 0) {
+ res = -1;
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief OSP Application OSPNext
+ * \param chan Channel
+ * \param data Parameter
+ * \return 0 Success, -1 Failed
+ */
+static int ospnext_exec(
+ struct ast_channel* chan,
+ void* data)
+{
+ int res;
+ const char* provider = OSP_DEF_PROVIDER;
+ int cause = 0;
+ struct varshead* headp;
+ struct ast_var_t* current;
+ struct osp_result result;
+ char buffer[OSP_TOKSTR_SIZE];
+ unsigned int callidtypes = OSP_CALLID_UNDEFINED;
+ const char* status;
+ char* tmp;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(cause);
+ AST_APP_ARG(provider);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "OSPNext: Arg required, OSPNext(cause[|provider[|options]])\n");
+ return -1;
+ }
+
+ if (!(tmp = ast_strdupa(data))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if (!ast_strlen_zero(args.cause) && sscanf(args.cause, "%d", &cause) != 1) {
+ cause = 0;
+ }
+ ast_debug(1, "OSPNext: cause '%d'\n", cause);
+
+ if (!ast_strlen_zero(args.provider)) {
+ provider = args.provider;
+ }
+ ast_debug(1, "OSPlookup: provider '%s'\n", provider);
+
+ result.inhandle = OSP_INVALID_HANDLE;
+ result.outhandle = OSP_INVALID_HANDLE;
+ result.intimelimit = OSP_DEF_TIMELIMIT;
+ result.numresults = 0;
+
+ headp = &chan->varshead;
+ AST_LIST_TRAVERSE(headp, current, entries) {
+ if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
+ if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
+ result.inhandle = OSP_INVALID_HANDLE;
+ }
+ } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
+ if (sscanf(ast_var_value(current), "%d", &result.outhandle) != 1) {
+ result.outhandle = OSP_INVALID_HANDLE;
+ }
+ } else if (!strcasecmp(ast_var_name(current), "OSPINTIMELIMIT")) {
+ if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
+ result.intimelimit = OSP_DEF_TIMELIMIT;
+ }
+ } else if (!strcasecmp(ast_var_name(current), "OSPOUTCALLIDTYPES")) {
+ if (sscanf(ast_var_value(current), "%d", &callidtypes) != 1) {
+ callidtypes = OSP_CALLID_UNDEFINED;
+ }
+ } else if (!strcasecmp(ast_var_name(current), "OSPRESULTS")) {
+ if (sscanf(ast_var_value(current), "%d", &result.numresults) != 1) {
+ result.numresults = 0;
+ }
+ }
+ }
+ ast_debug(1, "OSPNext: OSPINHANDLE '%d'\n", result.inhandle);
+ ast_debug(1, "OSPNext: OSPOUTHANDLE '%d'\n", result.outhandle);
+ ast_debug(1, "OSPNext: OSPINTIMELIMIT '%d'\n", result.intimelimit);
+ ast_debug(1, "OSPNext: OSPOUTCALLIDTYPES '%d'\n", callidtypes);
+ ast_debug(1, "OSPNext: OSPRESULTS '%d'\n", result.numresults);
+
+ if ((res = osp_next(provider, cause, &result)) > 0) {
+ status = AST_OSP_SUCCESS;
+ } else {
+ result.tech[0] = '\0';
+ result.dest[0] = '\0';
+ result.called[0] = '\0';
+ result.calling[0] = '\0';
+ result.token[0] = '\0';
+ result.networkid[0] = '\0';
+ result.numresults = 0;
+ result.outtimelimit = OSP_DEF_TIMELIMIT;
+ result.outcallid.buf[0] = '\0';
+ result.outcallid.len = 0;
+ if (!res) {
+ status = AST_OSP_FAILED;
+ } else {
+ status = AST_OSP_ERROR;
+ }
+ }
+
+ pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
+ ast_debug(1, "OSPNext: OSPTECH '%s'\n", result.tech);
+ pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
+ ast_debug(1, "OSPNext: OSPDEST '%s'\n", result.dest);
+ pbx_builtin_setvar_helper(chan, "OSPCALLED", result.called);
+ ast_debug(1, "OSPNext: OSPCALLED'%s'\n", result.called);
+ pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
+ ast_debug(1, "OSPNext: OSPCALLING '%s'\n", result.calling);
+ pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
+ ast_debug(1, "OSPNext: OSPOUTTOKEN size '%zd'\n", strlen(result.token));
+ snprintf(buffer, sizeof(buffer), "%d", result.numresults);
+ pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
+ ast_debug(1, "OSPNext: OSPRESULTS '%s'\n", buffer);
+ snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
+ pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
+ ast_debug(1, "OSPNext: OSPOUTTIMELIMIT '%s'\n", buffer);
+ pbx_builtin_setvar_helper(chan, "OSPNEXTSTATUS", status);
+ ast_debug(1, "OSPNext: %s\n", status);
+
+ if (!strcasecmp(result.tech, OSP_TECH_H323)) {
+ if ((callidtypes & OSP_CALLID_H323) && (result.outcallid.len != 0)) {
+ osp_uuid2str(result.outcallid.buf, buffer, sizeof(buffer));
+ } else {
+ buffer[0] = '\0';
+ }
+ pbx_builtin_setvar_helper(chan, "OSPOUTCALLID", buffer);
+ snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
+ pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
+ } else if (!strcasecmp(result.tech, OSP_TECH_SIP)) {
+ snprintf(buffer, sizeof(buffer), "%s/%s@%s", result.tech, result.called, result.dest);
+ pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
+ if (!ast_strlen_zero(result.token)) {
+ snprintf(buffer, sizeof(buffer), "%s%s", OSP_SIP_HEADER, result.token);
+ pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
+ ast_debug(1, "OSPLookup: SIPADDHEADER size '%zd'\n", strlen(buffer));
+ }
+ } else if (!strcasecmp(result.tech, OSP_TECH_IAX)) {
+ snprintf(buffer, sizeof(buffer), "%s/%s/%s", result.tech, result.dest, result.called);
+ pbx_builtin_setvar_helper(chan, "OSPDIALSTR", buffer);
+ }
+
+ if(res <= 0) {
+ res = -1;
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief OSP Application OSPFinish
+ * \param chan Channel
+ * \param data Parameter
+ * \return 0 Success, -1 Failed
+ */
+static int ospfinished_exec(
+ struct ast_channel* chan,
+ void* data)
+{
+ int res = 1;
+ int cause = 0;
+ struct varshead* headp;
+ struct ast_var_t* current;
+ int inhandle = OSP_INVALID_HANDLE;
+ int outhandle = OSP_INVALID_HANDLE;
+ int recorded = 0;
+ time_t start, connect, end;
+ unsigned int release;
+ char buffer[OSP_INTSTR_SIZE];
+ const char* status;
+ char* tmp;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(cause);
+ AST_APP_ARG(options);
+ );
+
+ if (!(tmp = ast_strdupa(data))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ headp = &chan->varshead;
+ AST_LIST_TRAVERSE(headp, current, entries) {
+ if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
+ if (sscanf(ast_var_value(current), "%d", &inhandle) != 1) {
+ inhandle = OSP_INVALID_HANDLE;
+ }
+ } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
+ if (sscanf(ast_var_value(current), "%d", &outhandle) != 1) {
+ outhandle = OSP_INVALID_HANDLE;
+ }
+ } else if (!recorded &&
+ (!strcasecmp(ast_var_name(current), "OSPAUTHSTATUS") ||
+ !strcasecmp(ast_var_name(current), "OSPLOOKUPSTATUS") ||
+ !strcasecmp(ast_var_name(current), "OSPNEXTSTATUS")))
+ {
+ if (strcasecmp(ast_var_value(current), AST_OSP_SUCCESS)) {
+ recorded = 1;
+ }
+ }
+ }
+ ast_debug(1, "OSPFinish: OSPINHANDLE '%d'\n", inhandle);
+ ast_debug(1, "OSPFinish: OSPOUTHANDLE '%d'\n", outhandle);
+ ast_debug(1, "OSPFinish: recorded '%d'\n", recorded);
+
+ if (!ast_strlen_zero(args.cause) && sscanf(args.cause, "%d", &cause) != 1) {
+ cause = 0;
+ }
+ ast_debug(1, "OSPFinish: cause '%d'\n", cause);
+
+ if (chan->cdr) {
+ start = chan->cdr->start.tv_sec;
+ connect = chan->cdr->answer.tv_sec;
+ if (connect) {
+ end = time(NULL);
+ } else {
+ end = connect;
+ }
+ } else {
+ start = 0;
+ connect = 0;
+ end = 0;
+ }
+ ast_debug(1, "OSPFinish: start '%ld'\n", start);
+ ast_debug(1, "OSPFinish: connect '%ld'\n", connect);
+ ast_debug(1, "OSPFinish: end '%ld'\n", end);
+
+ release = ast_check_hangup(chan) ? 0 : 1;
+
+ if (osp_finish(outhandle, recorded, cause, start, connect, end, release) <= 0) {
+ ast_debug(1, "OSPFinish: Unable to report usage for outbound call\n");
+ }
+ switch (cause) {
+ case AST_CAUSE_NORMAL_CLEARING:
+ break;
+ default:
+ cause = AST_CAUSE_NO_ROUTE_DESTINATION;
+ break;
+ }
+ if (osp_finish(inhandle, recorded, cause, start, connect, end, release) <= 0) {
+ ast_debug(1, "OSPFinish: Unable to report usage for inbound call\n");
+ }
+ snprintf(buffer, sizeof(buffer), "%d", OSP_INVALID_HANDLE);
+ pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
+ pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
+
+ if (res > 0) {
+ status = AST_OSP_SUCCESS;
+ } else if (!res) {
+ status = AST_OSP_FAILED;
+ } else {
+ status = AST_OSP_ERROR;
+ }
+ pbx_builtin_setvar_helper(chan, "OSPFINISHSTATUS", status);
+
+ if(!res) {
+ res = -1;
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+/* OSP Module APIs */
+
+static int osp_unload(void);
+static int osp_load(int reload)
+{
+ const char* t;
+ unsigned int v;
+ struct ast_config* cfg;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ int error = OSPC_ERR_NO_ERROR;
+
+ if ((cfg = ast_config_load(OSP_CONFIG_FILE, config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (cfg) {
+ if (reload)
+ osp_unload();
+
+ t = ast_variable_retrieve(cfg, OSP_GENERAL_CAT, "accelerate");
+ if (t && ast_true(t)) {
+ if ((error = OSPPInit(1)) != OSPC_ERR_NO_ERROR) {
+ ast_log(LOG_WARNING, "OSP: Unable to enable hardware accelleration\n");
+ OSPPInit(0);
+ } else {
+ osp_hardware = 1;
+ }
+ } else {
+ OSPPInit(0);
+ }
+ ast_debug(1, "OSP: osp_hardware '%d'\n", osp_hardware);
+
+ t = ast_variable_retrieve(cfg, OSP_GENERAL_CAT, "tokenformat");
+ if (t) {
+ if ((sscanf(t, "%d", &v) == 1) &&
+ ((v == TOKEN_ALGO_SIGNED) || (v == TOKEN_ALGO_UNSIGNED) || (v == TOKEN_ALGO_BOTH)))
+ {
+ osp_tokenformat = v;
+ } else {
+ ast_log(LOG_WARNING, "tokenformat should be an integer from %d, %d or %d, not '%s'\n",
+ TOKEN_ALGO_SIGNED, TOKEN_ALGO_UNSIGNED, TOKEN_ALGO_BOTH, t);
+ }
+ }
+ ast_debug(1, "OSP: osp_tokenformat '%d'\n", osp_tokenformat);
+
+ t = ast_category_browse(cfg, NULL);
+ while(t) {
+ if (strcasecmp(t, OSP_GENERAL_CAT)) {
+ osp_create_provider(cfg, t);
+ }
+ t = ast_category_browse(cfg, t);
+ }
+
+ osp_initialized = 1;
+
+ ast_config_destroy(cfg);
+ } else {
+ ast_log(LOG_WARNING, "OSP: Unable to find configuration. OSP support disabled\n");
+ return 0;
+ }
+ ast_debug(1, "OSP: osp_initialized '%d'\n", osp_initialized);
+
+ return 1;
+}
+
+static int osp_unload(void)
+{
+ struct osp_provider* p;
+ struct osp_provider* next;
+
+ if (osp_initialized) {
+ ast_mutex_lock(&osplock);
+ p = ospproviders;
+ while(p) {
+ next = p->next;
+ OSPPProviderDelete(p->handle, 0);
+ ast_free(p);
+ p = next;
+ }
+ ospproviders = NULL;
+ ast_mutex_unlock(&osplock);
+
+ OSPPCleanup();
+
+ osp_tokenformat = TOKEN_ALGO_SIGNED;
+ osp_hardware = 0;
+ osp_initialized = 0;
+ }
+ return 0;
+}
+
+static char *handle_cli_osp_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i;
+ int found = 0;
+ struct osp_provider* p;
+ const char* provider = NULL;
+ const char* tokenalgo;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "osp show";
+ e->usage =
+ "Usage: osp show\n"
+ " Displays information on Open Settlement Protocol support\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc < 2) || (a->argc > 3))
+ return CLI_SHOWUSAGE;
+ if (a->argc > 2)
+ provider = a->argv[2];
+ if (!provider) {
+ switch (osp_tokenformat) {
+ case TOKEN_ALGO_BOTH:
+ tokenalgo = "Both";
+ break;
+ case TOKEN_ALGO_UNSIGNED:
+ tokenalgo = "Unsigned";
+ break;
+ case TOKEN_ALGO_SIGNED:
+ default:
+ tokenalgo = "Signed";
+ break;
+ }
+ ast_cli(a->fd, "OSP: %s %s %s\n",
+ osp_initialized ? "Initialized" : "Uninitialized", osp_hardware ? "Accelerated" : "Normal", tokenalgo);
+ }
+
+ ast_mutex_lock(&osplock);
+ p = ospproviders;
+ while(p) {
+ if (!provider || !strcasecmp(p->name, provider)) {
+ if (found) {
+ ast_cli(a->fd, "\n");
+ }
+ ast_cli(a->fd, " == OSP Provider '%s' == \n", p->name);
+ ast_cli(a->fd, "Local Private Key: %s\n", p->privatekey);
+ ast_cli(a->fd, "Local Certificate: %s\n", p->localcert);
+ for (i = 0; i < p->cacount; i++) {
+ ast_cli(a->fd, "CA Certificate %d: %s\n", i + 1, p->cacerts[i]);
+ }
+ for (i = 0; i < p->spcount; i++) {
+ ast_cli(a->fd, "Service Point %d: %s\n", i + 1, p->srvpoints[i]);
+ }
+ ast_cli(a->fd, "Max Connections: %d\n", p->maxconnections);
+ ast_cli(a->fd, "Retry Delay: %d seconds\n", p->retrydelay);
+ ast_cli(a->fd, "Retry Limit: %d\n", p->retrylimit);
+ ast_cli(a->fd, "Timeout: %d milliseconds\n", p->timeout);
+ ast_cli(a->fd, "Source: %s\n", strlen(p->source) ? p->source : "<unspecified>");
+ ast_cli(a->fd, "Auth Policy %d\n", p->authpolicy);
+ ast_cli(a->fd, "Default protocol %s\n", p->defaultprotocol);
+ ast_cli(a->fd, "OSP Handle: %d\n", p->handle);
+ found++;
+ }
+ p = p->next;
+ }
+ ast_mutex_unlock(&osplock);
+
+ if (!found) {
+ if (provider) {
+ ast_cli(a->fd, "Unable to find OSP provider '%s'\n", provider);
+ } else {
+ ast_cli(a->fd, "No OSP providers configured\n");
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static const char* app1= "OSPAuth";
+static const char* synopsis1 = "OSP authentication";
+static const char* descrip1 =
+" OSPAuth([provider[,options]]): Authenticate a SIP INVITE by OSP and sets\n"
+"the variables:\n"
+" ${OSPINHANDLE}: The inbound call transaction handle\n"
+" ${OSPINTIMELIMIT}: The inbound call duration limit in seconds\n"
+"\n"
+"This application sets the following channel variable upon completion:\n"
+" OSPAUTHSTATUS The status of the OSP Auth attempt as a text string, one of\n"
+" SUCCESS | FAILED | ERROR\n";
+
+static const char* app2= "OSPLookup";
+static const char* synopsis2 = "Lookup destination by OSP";
+static const char* descrip2 =
+" OSPLookup(exten[,provider[,options]]): Looks up an extension via OSP and sets\n"
+"the variables, where 'n' is the number of the result beginning with 1:\n"
+" ${OSPOUTHANDLE}: The OSP Handle for anything remaining\n"
+" ${OSPTECH}: The technology to use for the call\n"
+" ${OSPDEST}: The destination to use for the call\n"
+" ${OSPCALLED}: The called number to use for the call\n"
+" ${OSPCALLING}: The calling number to use for the call\n"
+" ${OSPDIALSTR}: The dial command string\n"
+" ${OSPOUTTOKEN}: The actual OSP token as a string\n"
+" ${OSPOUTTIMELIMIT}: The outbound call duration limit in seconds\n"
+" ${OSPOUTCALLIDTYPES}: The outbound call id types\n"
+" ${OSPOUTCALLID}: The outbound call id\n"
+" ${OSPRESULTS}: The number of OSP results total remaining\n"
+"\n"
+"The option string may contain the following character:\n"
+" 'h' -- generate H323 call id for the outbound call\n"
+" 's' -- generate SIP call id for the outbound call. Have not been implemented\n"
+" 'i' -- generate IAX call id for the outbound call. Have not been implemented\n"
+"This application sets the following channel variable upon completion:\n"
+" OSPLOOKUPSTATUS The status of the OSP Lookup attempt as a text string, one of\n"
+" SUCCESS | FAILED | ERROR\n";
+
+static const char* app3 = "OSPNext";
+static const char* synopsis3 = "Lookup next destination by OSP";
+static const char* descrip3 =
+" OSPNext(cause[,provider[,options]]): Looks up the next OSP Destination for ${OSPOUTHANDLE}\n"
+"See OSPLookup for more information\n"
+"\n"
+"This application sets the following channel variable upon completion:\n"
+" OSPNEXTSTATUS The status of the OSP Next attempt as a text string, one of\n"
+" SUCCESS | FAILED | ERROR\n";
+
+static const char* app4 = "OSPFinish";
+static const char* synopsis4 = "Record OSP entry";
+static const char* descrip4 =
+" OSPFinish([status[,options]]): Records call state for ${OSPINHANDLE}, according to\n"
+"status, which should be one of BUSY, CONGESTION, ANSWER, NOANSWER, or CHANUNAVAIL\n"
+"or coincidentally, just what the Dial application stores in its ${DIALSTATUS}.\n"
+"\n"
+"This application sets the following channel variable upon completion:\n"
+" OSPFINISHSTATUS The status of the OSP Finish attempt as a text string, one of\n"
+" SUCCESS | FAILED | ERROR \n";
+
+static struct ast_cli_entry cli_osp[] = {
+ AST_CLI_DEFINE(handle_cli_osp_show, "Displays OSF information")
+};
+
+static int load_module(void)
+{
+ int res;
+
+ if (!osp_load(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ ast_cli_register_multiple(cli_osp, sizeof(cli_osp) / sizeof(struct ast_cli_entry));
+ res = ast_register_application(app1, ospauth_exec, synopsis1, descrip1);
+ res |= ast_register_application(app2, osplookup_exec, synopsis2, descrip2);
+ res |= ast_register_application(app3, ospnext_exec, synopsis3, descrip3);
+ res |= ast_register_application(app4, ospfinished_exec, synopsis4, descrip4);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app4);
+ res |= ast_unregister_application(app3);
+ res |= ast_unregister_application(app2);
+ res |= ast_unregister_application(app1);
+ ast_cli_unregister_multiple(cli_osp, sizeof(cli_osp) / sizeof(struct ast_cli_entry));
+ osp_unload();
+
+ return res;
+}
+
+static int reload(void)
+{
+ osp_load(1);
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Open Settlement Protocol Applications",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/apps/app_page.c b/trunk/apps/app_page.c
new file mode 100644
index 000000000..2ea367edd
--- /dev/null
+++ b/trunk/apps/app_page.c
@@ -0,0 +1,193 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2006 Digium, Inc. All rights reserved.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * This code is released under the GNU General Public License
+ * version 2.0. See LICENSE for more information.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief page() - Paging application
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>zaptel</depend>
+ <depend>app_meetme</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/utils.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/dial.h"
+
+static const char *app_page= "Page";
+
+static const char *page_synopsis = "Pages phones";
+
+static const char *page_descrip =
+"Page(Technology/Resource&Technology2/Resource2[,options])\n"
+" Places outbound calls to the given technology / resource and dumps\n"
+"them into a conference bridge as muted participants. The original\n"
+"caller is dumped into the conference as a speaker and the room is\n"
+"destroyed when the original caller leaves. Valid options are:\n"
+" d - full duplex audio\n"
+" q - quiet, do not play beep to caller\n"
+" r - record the page into a file (see 'r' for app_meetme)\n"
+" s - only dial channel if devicestate says it is not in use\n";
+
+enum {
+ PAGE_DUPLEX = (1 << 0),
+ PAGE_QUIET = (1 << 1),
+ PAGE_RECORD = (1 << 2),
+ PAGE_SKIP = (1 << 3),
+} page_opt_flags;
+
+AST_APP_OPTIONS(page_opts, {
+ AST_APP_OPTION('d', PAGE_DUPLEX),
+ AST_APP_OPTION('q', PAGE_QUIET),
+ AST_APP_OPTION('r', PAGE_RECORD),
+ AST_APP_OPTION('s', PAGE_SKIP),
+});
+
+#define MAX_DIALS 128
+
+static int page_exec(struct ast_channel *chan, void *data)
+{
+ char *options, *tech, *resource, *tmp;
+ char meetmeopts[88], originator[AST_CHANNEL_NAME], *opts[0];
+ struct ast_flags flags = { 0 };
+ unsigned int confid = ast_random();
+ struct ast_app *app;
+ int res = 0, pos = 0, i = 0;
+ struct ast_dial *dials[MAX_DIALS];
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
+ return -1;
+ }
+
+ if (!(app = pbx_findapp("MeetMe"))) {
+ ast_log(LOG_WARNING, "There is no MeetMe application available!\n");
+ return -1;
+ };
+
+ options = ast_strdupa(data);
+
+ ast_copy_string(originator, chan->name, sizeof(originator));
+ if ((tmp = strchr(originator, '-')))
+ *tmp = '\0';
+
+ tmp = strsep(&options, ",");
+ if (options)
+ ast_app_parse_options(page_opts, &flags, opts, options);
+
+ snprintf(meetmeopts, sizeof(meetmeopts), "MeetMe,%ud,%s%sqxdw(5)", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "m"),
+ (ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
+
+ /* Go through parsing/calling each device */
+ while ((tech = strsep(&tmp, "&"))) {
+ int state = 0;
+ struct ast_dial *dial = NULL;
+
+ /* don't call the originating device */
+ if (!strcasecmp(tech, originator))
+ continue;
+
+ /* If no resource is available, continue on */
+ if (!(resource = strchr(tech, '/'))) {
+ ast_log(LOG_WARNING, "Incomplete destination '%s' supplied.\n", tech);
+ continue;
+ }
+
+ /* Ensure device is not in use if skip option is enabled */
+ if (ast_test_flag(&flags, PAGE_SKIP) && (state = ast_device_state(tech)) != AST_DEVICE_NOT_INUSE) {
+ ast_log(LOG_WARNING, "Destination '%s' has device state '%s'.\n", tech, devstate2str(state));
+ continue;
+ }
+
+ *resource++ = '\0';
+
+ /* Create a dialing structure */
+ if (!(dial = ast_dial_create())) {
+ ast_log(LOG_WARNING, "Failed to create dialing structure.\n");
+ continue;
+ }
+
+ /* Append technology and resource */
+ ast_dial_append(dial, tech, resource);
+
+ /* Set ANSWER_EXEC as global option */
+ ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, meetmeopts);
+
+ /* Run this dial in async mode */
+ ast_dial_run(dial, chan, 1);
+
+ /* Put in our dialing array */
+ dials[pos++] = dial;
+ }
+
+ if (!ast_test_flag(&flags, PAGE_QUIET)) {
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ }
+
+ if (!res) {
+ snprintf(meetmeopts, sizeof(meetmeopts), "%ud,A%s%sqxd", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "t"),
+ (ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
+ pbx_exec(chan, app, meetmeopts);
+ }
+
+ /* Go through each dial attempt cancelling, joining, and destroying */
+ for (i = 0; i < pos; i++) {
+ struct ast_dial *dial = dials[i];
+
+ /* We have to wait for the async thread to exit as it's possible Meetme won't throw them out immediately */
+ ast_dial_join(dial);
+
+ /* Hangup all channels */
+ ast_dial_hangup(dial);
+
+ /* Destroy dialing structure */
+ ast_dial_destroy(dial);
+ }
+
+ return -1;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app_page);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app_page, page_exec, page_synopsis, page_descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Page Multiple Phones");
+
diff --git a/trunk/apps/app_parkandannounce.c b/trunk/apps/app_parkandannounce.c
new file mode 100644
index 000000000..7ad7a6416
--- /dev/null
+++ b/trunk/apps/app_parkandannounce.c
@@ -0,0 +1,183 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Author: Ben Miller <bgmiller@dccinc.com>
+ * With TONS of help from Mark!
+ *
+ * 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 ParkAndAnnounce application for Asterisk
+ *
+ * \author Ben Miller <bgmiller@dccinc.com>
+ * \arg With TONS of help from Mark!
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/features.h"
+#include "asterisk/say.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static char *app = "ParkAndAnnounce";
+
+static char *synopsis = "Park and Announce";
+
+static char *descrip =
+" ParkAndAnnounce(announce:template,timeout,dial[,return_context]):\n"
+"Park a call into the parkinglot and announce the call to another channel.\n"
+"\n"
+"announce template: Colon-separated list of files to announce. The word PARKED\n"
+" will be replaced by a say_digits of the extension in which\n"
+" the call is parked.\n"
+"timeout: Time in seconds before the call returns into the return\n"
+" context.\n"
+"dial: The app_dial style resource to call to make the\n"
+" announcement. Console/dsp calls the console.\n"
+"return_context: The goto-style label to jump the call back into after\n"
+" timeout. Default <priority+1>.\n"
+"\n"
+"The variable ${PARKEDAT} will contain the parking extension into which the\n"
+"call was placed. Use with the Local channel to allow the dialplan to make\n"
+"use of this information.\n";
+
+
+static int parkandannounce_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ int lot, timeout = 0, dres;
+ char *dialtech, *tmp[100], buf[13];
+ int looptemp, i;
+ char *s;
+
+ struct ast_channel *dchan;
+ struct outgoing_helper oh = { 0, };
+ int outstate;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(template);
+ AST_APP_ARG(timeout);
+ AST_APP_ARG(dial);
+ AST_APP_ARG(return_context);
+ );
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
+ return -1;
+ }
+
+ s = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, s);
+
+ if (args.timeout)
+ timeout = atoi(args.timeout) * 1000;
+
+ if (ast_strlen_zero(args.dial)) {
+ ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n");
+ return -1;
+ }
+
+ dialtech = strsep(&args.dial, "/");
+ ast_verb(3, "Dial Tech,String: (%s,%s)\n", dialtech, args.dial);
+
+ if (!ast_strlen_zero(args.return_context))
+ ast_parseable_goto(chan, args.return_context);
+
+ ast_verb(3, "Return Context: (%s,%s,%d) ID: %s\n", chan->context, chan->exten, chan->priority, chan->cid.cid_num);
+ if (!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
+ ast_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
+ }
+
+ /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
+ before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
+
+ ast_masq_park_call(chan, NULL, timeout, &lot);
+
+ ast_verb(3, "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, args.return_context);
+
+ /* Now place the call to the extension */
+
+ snprintf(buf, sizeof(buf), "%d", lot);
+ oh.parent_channel = chan;
+ oh.vars = ast_variable_new("_PARKEDAT", buf, "");
+ dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
+
+ if (dchan) {
+ if (dchan->_state == AST_STATE_UP) {
+ ast_verb(4, "Channel %s was answered.\n", dchan->name);
+ } else {
+ ast_verb(4, "Channel %s was never answered.\n", dchan->name);
+ ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
+ ast_hangup(dchan);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
+ return -1;
+ }
+
+ ast_stopstream(dchan);
+
+ /* now we have the call placed and are ready to play stuff to it */
+
+ ast_verb(4, "Announce Template:%s\n", args.template);
+
+ for (looptemp = 0, tmp[looptemp++] = strsep(&args.template, ":");
+ looptemp < sizeof(tmp) / sizeof(tmp[0]);
+ tmp[looptemp++] = strsep(&args.template, ":"));
+
+ for (i = 0; i < looptemp; i++) {
+ ast_verb(4, "Announce:%s\n", tmp[i]);
+ if (!strcmp(tmp[i], "PARKED")) {
+ ast_say_digits(dchan, lot, "", dchan->language);
+ } else {
+ dres = ast_streamfile(dchan, tmp[i], dchan->language);
+ if (!dres) {
+ dres = ast_waitstream(dchan, "");
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
+ dres = 0;
+ }
+ }
+ }
+
+ ast_stopstream(dchan);
+ ast_hangup(dchan);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ /* return ast_register_application(app, park_exec); */
+ return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");
diff --git a/trunk/apps/app_pickupchan.c b/trunk/apps/app_pickupchan.c
new file mode 100644
index 000000000..6c3c4e3ef
--- /dev/null
+++ b/trunk/apps/app_pickupchan.c
@@ -0,0 +1,174 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2008, Gary Cook
+ *
+ * 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 Pickup a ringing channel
+ *
+ * \author Gary Cook
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.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/options.h"
+
+static const char *app = "PickupChan";
+static const char *synopsis = "Pickup a ringing channel";
+static const char *descrip =
+" PickupChan(channel[&channel...]): This application can pickup any ringing channel\n";
+
+/*! \todo This application should return a result code, like PICKUPRESULT */
+
+/*! \brief Helper function that determines whether a channel is capable of being picked up */
+static int can_pickup(struct ast_channel *chan)
+{
+ ast_debug(3, "Checking Pickup '%s' state '%s ( %d )'\n", chan->name, ast_state2str(chan->_state), chan->_state);
+
+ if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
+static struct ast_channel *my_ast_get_channel_by_name_locked(char *channame)
+{
+ struct ast_channel *chan;
+ char *chkchan = alloca(strlen(channame) + 2);
+
+ /* need to append a '-' for the comparison so we check full channel name,
+ * i.e SIP/hgc- , use a temporary variable so original stays the same for
+ * debugging.
+ */
+ strcpy(chkchan, channame);
+ strcat(chkchan, "-");
+
+ for (chan = ast_walk_channel_by_name_prefix_locked(NULL, channame, strlen(channame));
+ chan;
+ chan = ast_walk_channel_by_name_prefix_locked(chan, channame, strlen(channame))) {
+ if (!strncasecmp(chan->name, chkchan, strlen(chkchan)) && can_pickup(chan))
+ return chan;
+ ast_channel_unlock(chan);
+ }
+ return NULL;
+}
+
+/*! \brief Perform actual pickup between two channels */
+static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
+{
+ int res = 0;
+
+ ast_debug(3, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
+
+ if ((res = ast_answer(chan))) {
+ ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
+ return -1;
+ }
+
+ if ((res = ast_queue_control(chan, AST_CONTROL_ANSWER))) {
+ ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
+ return -1;
+ }
+
+ if ((res = ast_channel_masquerade(target, chan))) {
+ ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
+ return -1;
+ }
+
+ return res;
+}
+
+/*! \brief Attempt to pick up specified channel named , does not use context */
+static int pickup_by_channel(struct ast_channel *chan, char *pickup)
+{
+ int res = 0;
+ struct ast_channel *target;
+
+ if (!(target = my_ast_get_channel_by_name_locked(pickup)))
+ return -1;
+
+ /* Just check that we are not picking up the SAME as target */
+ if (chan->name != target->name && chan != target) {
+ res = pickup_do(chan, target);
+ ast_channel_unlock(target);
+ }
+
+ return res;
+}
+
+/*! \brief Main application entry point */
+static int pickupchan_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_module_user *u = NULL;
+ char *tmp = ast_strdupa(data);
+ char *pickup = NULL, *context = NULL;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Pickup requires an argument (channel)!\n");
+ return -1;
+ }
+
+ u = ast_module_user_add(chan);
+
+ /* Parse channel (and ignore context if there) */
+ while (!ast_strlen_zero(tmp) && (pickup = strsep(&tmp, "&"))) {
+ if ((context = strchr(pickup , '@'))) {
+ *context++ = '\0';
+ }
+ if (!strncasecmp(chan->name, pickup , strlen(pickup))) {
+ ast_log(LOG_NOTICE, "Cannot pickup your own channel %s.\n", pickup);
+ } else {
+ if (!pickup_by_channel(chan, pickup)) {
+ break;
+ }
+ ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
+ }
+ }
+
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, pickupchan_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel Pickup Application");
diff --git a/trunk/apps/app_playback.c b/trunk/apps/app_playback.c
new file mode 100644
index 000000000..21b3ab3eb
--- /dev/null
+++ b/trunk/apps/app_playback.c
@@ -0,0 +1,524 @@
+/*
+ * 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 playback a sound file
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+/* This file provides config-file based 'say' functions, and implenents
+ * some CLI commands.
+ */
+#include "asterisk/say.h" /* provides config-file based 'say' functions */
+#include "asterisk/cli.h"
+
+static char *app = "Playback";
+
+static char *synopsis = "Play a file";
+
+static char *descrip =
+" Playback(filename[&filename2...][,option]): Plays back given filenames (do not put\n"
+"extension). Options may also be included following a comma.\n"
+"The 'skip' option causes the playback of the message to be skipped if the channel\n"
+"is not in the 'up' state (i.e. it hasn't been answered yet). If 'skip' is \n"
+"specified, the application will return immediately should the channel not be\n"
+"off hook. Otherwise, unless 'noanswer' is specified, the channel will\n"
+"be answered before the sound is played. Not all channels support playing\n"
+"messages while still on hook.\n"
+"This application sets the following channel variable upon completion:\n"
+" PLAYBACKSTATUS The status of the playback attempt as a text string, one of\n"
+" SUCCESS | FAILED\n"
+;
+
+
+static struct ast_config *say_cfg = NULL;
+/* save the say' api calls.
+ * The first entry is NULL if we have the standard source,
+ * otherwise we are sourcing from here.
+ * 'say load [new|old]' will enable the new or old method, or report status
+ */
+static const void *say_api_buf[40];
+static const char *say_old = "old";
+static const char *say_new = "new";
+
+static void save_say_mode(const void *arg)
+{
+ int i = 0;
+ say_api_buf[i++] = arg;
+
+ say_api_buf[i++] = ast_say_number_full;
+ say_api_buf[i++] = ast_say_enumeration_full;
+ say_api_buf[i++] = ast_say_digit_str_full;
+ say_api_buf[i++] = ast_say_character_str_full;
+ say_api_buf[i++] = ast_say_phonetic_str_full;
+ say_api_buf[i++] = ast_say_datetime;
+ say_api_buf[i++] = ast_say_time;
+ say_api_buf[i++] = ast_say_date;
+ say_api_buf[i++] = ast_say_datetime_from_now;
+ say_api_buf[i++] = ast_say_date_with_format;
+}
+
+static void restore_say_mode(void *arg)
+{
+ int i = 0;
+ say_api_buf[i++] = arg;
+
+ ast_say_number_full = say_api_buf[i++];
+ ast_say_enumeration_full = say_api_buf[i++];
+ ast_say_digit_str_full = say_api_buf[i++];
+ ast_say_character_str_full = say_api_buf[i++];
+ ast_say_phonetic_str_full = say_api_buf[i++];
+ ast_say_datetime = say_api_buf[i++];
+ ast_say_time = say_api_buf[i++];
+ ast_say_date = say_api_buf[i++];
+ ast_say_datetime_from_now = say_api_buf[i++];
+ ast_say_date_with_format = say_api_buf[i++];
+}
+
+/*
+ * Typical 'say' arguments in addition to the date or number or string
+ * to say. We do not include 'options' because they may be different
+ * in recursive calls, and so they are better left as an external
+ * parameter.
+ */
+typedef struct {
+ struct ast_channel *chan;
+ const char *ints;
+ const char *language;
+ int audiofd;
+ int ctrlfd;
+} say_args_t;
+
+static int s_streamwait3(const say_args_t *a, const char *fn)
+{
+ int res = ast_streamfile(a->chan, fn, a->language);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
+ return res;
+ }
+ res = (a->audiofd > -1 && a->ctrlfd > -1) ?
+ ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
+ ast_waitstream(a->chan, a->ints);
+ ast_stopstream(a->chan);
+ return res;
+}
+
+/*
+ * the string is 'prefix:data' or prefix:fmt:data'
+ * with ':' being invalid in strings.
+ */
+static int do_say(say_args_t *a, const char *s, const char *options, int depth)
+{
+ struct ast_variable *v;
+ char *lang, *x, *rule = NULL;
+ int ret = 0;
+ struct varshead head = { .first = NULL, .last = NULL };
+ struct ast_var_t *n;
+
+ ast_debug(2, "string <%s> depth <%d>\n", s, depth);
+ if (depth++ > 10) {
+ ast_log(LOG_WARNING, "recursion too deep, exiting\n");
+ return -1;
+ } else if (!say_cfg) {
+ ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
+ return -1;
+ }
+
+ /* scan languages same as in file.c */
+ if (a->language == NULL)
+ a->language = "en"; /* default */
+ ast_debug(2, "try <%s> in <%s>\n", s, a->language);
+ lang = ast_strdupa(a->language);
+ for (;;) {
+ for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
+ if (ast_extension_match(v->name, s)) {
+ rule = ast_strdupa(v->value);
+ break;
+ }
+ }
+ if (rule)
+ break;
+ if ( (x = strchr(lang, '_')) )
+ *x = '\0'; /* try without suffix */
+ else if (strcmp(lang, "en"))
+ lang = "en"; /* last resort, try 'en' if not done yet */
+ else
+ break;
+ }
+ if (!rule)
+ return 0;
+
+ /* skip up to two prefixes to get the value */
+ if ( (x = strchr(s, ':')) )
+ s = x + 1;
+ if ( (x = strchr(s, ':')) )
+ s = x + 1;
+ ast_debug(2, "value is <%s>\n", s);
+ n = ast_var_assign("SAY", s);
+ AST_LIST_INSERT_HEAD(&head, n, entries);
+
+ /* scan the body, one piece at a time */
+ while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
+ char fn[128];
+ const char *p, *fmt, *data; /* format and data pointers */
+
+ /* prepare a decent file name */
+ x = ast_skip_blanks(x);
+ ast_trim_blanks(x);
+
+ /* replace variables */
+ pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
+ ast_debug(2, "doing [%s]\n", fn);
+
+ /* locate prefix and data, if any */
+ fmt = index(fn, ':');
+ if (!fmt || fmt == fn) { /* regular filename */
+ ret = s_streamwait3(a, fn);
+ continue;
+ }
+ fmt++;
+ data = index(fmt, ':'); /* colon before data */
+ if (!data || data == fmt) { /* simple prefix-fmt */
+ ret = do_say(a, fn, options, depth);
+ continue;
+ }
+ /* prefix:fmt:data */
+ for (p = fmt; p < data && ret <= 0; p++) {
+ char fn2[sizeof(fn)];
+ if (*p == ' ' || *p == '\t') /* skip blanks */
+ continue;
+ if (*p == '\'') {/* file name - we trim them */
+ char *y;
+ strcpy(fn2, ast_skip_blanks(p+1)); /* make a full copy */
+ y = index(fn2, '\'');
+ if (!y) {
+ p = data; /* invalid. prepare to end */
+ break;
+ }
+ *y = '\0';
+ ast_trim_blanks(fn2);
+ p = index(p+1, '\'');
+ ret = s_streamwait3(a, fn2);
+ } else {
+ int l = fmt-fn;
+ strcpy(fn2, fn); /* copy everything */
+ /* after prefix, append the format */
+ fn2[l++] = *p;
+ strcpy(fn2 + l, data);
+ ret = do_say(a, fn2, options, depth);
+ }
+
+ if (ret) {
+ break;
+ }
+ }
+ }
+ ast_var_delete(n);
+ return ret;
+}
+
+static int say_full(struct ast_channel *chan, const char *string,
+ const char *ints, const char *lang, const char *options,
+ int audiofd, int ctrlfd)
+{
+ say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
+ return do_say(&a, string, options, 0);
+}
+
+static int say_number_full(struct ast_channel *chan, int num,
+ const char *ints, const char *lang, const char *options,
+ int audiofd, int ctrlfd)
+{
+ char buf[64];
+ say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
+ snprintf(buf, sizeof(buf), "num:%d", num);
+ return do_say(&a, buf, options, 0);
+}
+
+static int say_enumeration_full(struct ast_channel *chan, int num,
+ const char *ints, const char *lang, const char *options,
+ int audiofd, int ctrlfd)
+{
+ char buf[64];
+ say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
+ snprintf(buf, sizeof(buf), "enum:%d", num);
+ return do_say(&a, buf, options, 0);
+}
+
+static int say_date_generic(struct ast_channel *chan, time_t t,
+ const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix)
+{
+ char buf[128];
+ struct ast_tm tm;
+ struct timeval tv = { t, 0 };
+ say_args_t a = { chan, ints, lang, -1, -1 };
+ if (format == NULL)
+ format = "";
+
+ ast_localtime(&tv, &tm, NULL);
+ snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
+ prefix,
+ format,
+ tm.tm_year+1900,
+ tm.tm_mon+1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ tm.tm_wday,
+ tm.tm_yday);
+ return do_say(&a, buf, NULL, 0);
+}
+
+static int say_date_with_format(struct ast_channel *chan, time_t t,
+ const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ return say_date_generic(chan, t, ints, lang, format, timezone, "datetime");
+}
+
+static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ return say_date_generic(chan, t, ints, lang, "", NULL, "date");
+}
+
+static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ return say_date_generic(chan, t, ints, lang, "", NULL, "time");
+}
+
+static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
+}
+
+/*
+ * remap the 'say' functions to use those in this file
+ */
+static int say_init_mode(const char *mode) {
+ if (!strcmp(mode, say_new)) {
+ if (say_cfg == NULL) {
+ ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
+ return -1;
+ }
+ save_say_mode(say_new);
+ ast_say_number_full = say_number_full;
+
+ ast_say_enumeration_full = say_enumeration_full;
+#if 0
+ ast_say_digits_full = say_digits_full;
+ ast_say_digit_str_full = say_digit_str_full;
+ ast_say_character_str_full = say_character_str_full;
+ ast_say_phonetic_str_full = say_phonetic_str_full;
+ ast_say_datetime_from_now = say_datetime_from_now;
+#endif
+ ast_say_datetime = say_datetime;
+ ast_say_time = say_time;
+ ast_say_date = say_date;
+ ast_say_date_with_format = say_date_with_format;
+ } else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
+ restore_say_mode(NULL);
+ } else if (strcmp(mode, say_old)) {
+ ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ const char *old_mode = say_api_buf[0] ? say_new : say_old;
+ char *mode;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "say load [new|old]";
+ e->usage =
+ "Usage: say load [new|old]\n"
+ " say load\n"
+ " Report status of current say mode\n"
+ " say load new\n"
+ " Set say method, configured in say.conf\n"
+ " say load old\n"
+ " Set old say method, coded in asterisk core\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc == 2) {
+ ast_cli(a->fd, "say mode is [%s]\n", old_mode);
+ return CLI_SUCCESS;
+ } else if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ mode = a->argv[2];
+ if (!strcmp(mode, old_mode))
+ ast_cli(a->fd, "say mode is %s already\n", mode);
+ else
+ if (say_init_mode(mode) == 0)
+ ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_playback[] = {
+ AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
+};
+
+static int playback_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int mres = 0;
+ char *tmp;
+ int option_skip=0;
+ int option_say=0;
+ int option_noanswer = 0;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filenames);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
+ return -1;
+ }
+
+ tmp = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if (args.options) {
+ if (strcasestr(args.options, "skip"))
+ option_skip = 1;
+ if (strcasestr(args.options, "say"))
+ option_say = 1;
+ if (strcasestr(args.options, "noanswer"))
+ option_noanswer = 1;
+ }
+ if (chan->_state != AST_STATE_UP) {
+ if (option_skip) {
+ /* At the user's option, skip if the line is not up */
+ goto done;
+ } else if (!option_noanswer)
+ /* Otherwise answer unless we're supposed to send this while on-hook */
+ res = ast_answer(chan);
+ }
+ if (!res) {
+ char *back = args.filenames;
+ char *front;
+
+ ast_stopstream(chan);
+ while (!res && (front = strsep(&back, "&"))) {
+ if (option_say)
+ res = say_full(chan, front, "", chan->language, NULL, -1, -1);
+ else
+ res = ast_streamfile(chan, front, chan->language);
+ if (!res) {
+ res = ast_waitstream(chan, "");
+ ast_stopstream(chan);
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
+ res = 0;
+ mres = 1;
+ }
+ }
+ }
+done:
+ pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
+ return res;
+}
+
+static int reload(void)
+{
+ struct ast_variable *v;
+ struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
+ struct ast_config *newcfg;
+
+ if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (say_cfg) {
+ ast_config_destroy(say_cfg);
+ ast_log(LOG_NOTICE, "Reloading say.conf\n");
+ say_cfg = newcfg;
+ }
+
+ if (say_cfg) {
+ for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
+ if (ast_extension_match(v->name, "mode")) {
+ say_init_mode(v->value);
+ break;
+ }
+ }
+ }
+
+ /*
+ * XXX here we should sort rules according to the same order
+ * we have in pbx.c so we have the same matching behaviour.
+ */
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
+
+ if (say_cfg)
+ ast_config_destroy(say_cfg);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ struct ast_variable *v;
+ struct ast_flags config_flags = { 0 };
+
+ say_cfg = ast_config_load("say.conf", config_flags);
+ if (say_cfg) {
+ for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
+ if (ast_extension_match(v->name, "mode")) {
+ say_init_mode(v->value);
+ break;
+ }
+ }
+ }
+
+ ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
+ return ast_register_application(app, playback_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/apps/app_privacy.c b/trunk/apps/app_privacy.c
new file mode 100644
index 000000000..dacfd704c
--- /dev/null
+++ b/trunk/apps/app_privacy.c
@@ -0,0 +1,173 @@
+/*
+ * 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 Block all calls without Caller*ID, require phone # to be entered
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/utils.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+
+static char *app = "PrivacyManager";
+
+static char *synopsis = "Require phone number to be entered, if no CallerID sent";
+
+static char *descrip =
+ " PrivacyManager([maxretries][,minlength]): If no Caller*ID \n"
+ "is sent, PrivacyManager answers the channel and asks the caller to\n"
+ "enter their phone number. The caller is given 'maxretries' attempts to do so.\n"
+ "The application does nothing if Caller*ID was received on the channel.\n"
+ " maxretries default 3 -maximum number of attempts the caller is allowed \n"
+ " to input a callerid.\n"
+ " minlength default 10 -minimum allowable digits in the input callerid number.\n"
+ "The application sets the following channel variable upon completion: \n"
+ "PRIVACYMGRSTATUS The status of the privacy manager's attempt to collect \n"
+ " a phone number from the user. A text string that is either:\n"
+ " SUCCESS | FAILED \n"
+;
+
+
+static int privacy_exec (struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int retries;
+ int maxretries = 3;
+ int minlength = 10;
+ int x = 0;
+ char phone[30];
+ char *parse = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(maxretries);
+ AST_APP_ARG(minlength);
+ AST_APP_ARG(options);
+ );
+
+ if (!ast_strlen_zero(chan->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "CallerID Present: Skipping\n");
+ } else {
+ /*Answer the channel if it is not already*/
+ if (chan->_state != AST_STATE_UP) {
+ if ((res = ast_answer(chan)))
+ return -1;
+ }
+
+ if (!ast_strlen_zero(data)) {
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.maxretries) {
+ if (sscanf(args.maxretries, "%d", &x) == 1)
+ maxretries = x;
+ else
+ ast_log(LOG_WARNING, "Invalid max retries argument\n");
+ }
+ if (args.minlength) {
+ if (sscanf(args.minlength, "%d", &x) == 1)
+ minlength = x;
+ else
+ ast_log(LOG_WARNING, "Invalid min length argument\n");
+ }
+
+ }
+
+ /* Play unidentified call */
+ res = ast_safe_sleep(chan, 1000);
+ if (!res)
+ res = ast_streamfile(chan, "privacy-unident", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+
+ /* Ask for 10 digit number, give 3 attempts */
+ for (retries = 0; retries < maxretries; retries++) {
+ if (!res)
+ res = ast_streamfile(chan, "privacy-prompt", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+
+ if (!res )
+ res = ast_readstring(chan, phone, sizeof(phone) - 1, /* digit timeout ms */ 3200, /* first digit timeout */ 5000, "#");
+
+ if (res < 0)
+ break;
+
+ /* Make sure we get at least digits */
+ if (strlen(phone) >= minlength )
+ break;
+ else {
+ res = ast_streamfile(chan, "privacy-incorrect", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ }
+ }
+
+ /* Got a number, play sounds and send them on their way */
+ if ((retries < maxretries) && res >= 0 ) {
+ res = ast_streamfile(chan, "privacy-thankyou", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+
+ ast_set_callerid (chan, phone, "Privacy Manager", NULL);
+
+ /* Clear the unavailable presence bit so if it came in on PRI
+ * the caller id will now be passed out to other channels
+ */
+ chan->cid.cid_pres &= (AST_PRES_UNAVAILABLE ^ 0xFF);
+
+ if (option_verbose > 2) {
+ ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID to %s, callerpres to %d\n",phone,chan->cid.cid_pres);
+ }
+ pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "SUCCESS");
+ } else {
+ pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "FAILED");
+ }
+ }
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application (app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, privacy_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Require phone number to be entered, if no CallerID sent");
diff --git a/trunk/apps/app_queue.c b/trunk/apps/app_queue.c
new file mode 100644
index 000000000..a2db787a7
--- /dev/null
+++ b/trunk/apps/app_queue.c
@@ -0,0 +1,6153 @@
+/*
+ * 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 True call queues with optional send URL on answer
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg Config in \ref Config_qu queues.conf
+ *
+ * \par Development notes
+ * \note 2004-11-25: Persistent Dynamic Members added by:
+ * NetNation Communications (www.netnation.com)
+ * Kevin Lindsay <kevinl@netnation.com>
+ *
+ * Each dynamic agent in each queue is now stored in the astdb.
+ * When asterisk is restarted, each agent will be automatically
+ * readded into their recorded queues. This feature can be
+ * configured with the 'persistent_members=<1|0>' setting in the
+ * '[general]' category in queues.conf. The default is on.
+ *
+ * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
+ *
+ * \note These features added by David C. Troy <dave@toad.net>:
+ * - Per-queue holdtime calculation
+ * - Estimated holdtime announcement
+ * - Position announcement
+ * - Abandoned/completed call counters
+ * - Failout timer passed as optional app parameter
+ * - Optional monitoring of calls, started when call is answered
+ *
+ * Patch Version 1.07 2003-12-24 01
+ *
+ * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
+ * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
+ *
+ * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
+ * by Matthew Enger <m.enger@xi.com.au>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>res_monitor</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/say.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/config.h"
+#include "asterisk/monitor.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/astdb.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/event.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/strings.h"
+#include "asterisk/global_datastores.h"
+
+enum {
+ QUEUE_STRATEGY_RINGALL = 0,
+ QUEUE_STRATEGY_LEASTRECENT,
+ QUEUE_STRATEGY_FEWESTCALLS,
+ QUEUE_STRATEGY_RANDOM,
+ QUEUE_STRATEGY_RRMEMORY,
+ QUEUE_STRATEGY_LINEAR,
+ QUEUE_STRATEGY_WRANDOM
+};
+
+static struct strategy {
+ int strategy;
+ char *name;
+} strategies[] = {
+ { QUEUE_STRATEGY_RINGALL, "ringall" },
+ { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
+ { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
+ { QUEUE_STRATEGY_RANDOM, "random" },
+ { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
+ { QUEUE_STRATEGY_LINEAR, "linear" },
+ { QUEUE_STRATEGY_WRANDOM, "wrandom"},
+};
+
+#define DEFAULT_RETRY 5
+#define DEFAULT_TIMEOUT 15
+#define RECHECK 1 /* Recheck every second to see we we're at the top yet */
+#define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
+#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /* The minimum number of seconds between position announcements
+ The default value of 15 provides backwards compatibility */
+#define MAX_QUEUE_BUCKETS 53
+
+#define RES_OKAY 0 /* Action completed */
+#define RES_EXISTS (-1) /* Entry already exists */
+#define RES_OUTOFMEMORY (-2) /* Out of memory */
+#define RES_NOSUCHQUEUE (-3) /* No such queue */
+#define RES_NOT_DYNAMIC (-4) /* Member is not dynamic */
+
+static char *app = "Queue";
+
+static char *synopsis = "Queue a call for a call queue";
+
+static char *descrip =
+" Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
+"Queues an incoming call in a particular call queue as defined in queues.conf.\n"
+"This application will return to the dialplan if the queue does not exist, or\n"
+"any of the join options cause the caller to not enter the queue.\n"
+"The option string may contain zero or more of the following characters:\n"
+" 'c' -- continue in the dialplan if the callee hangs up.\n"
+" 'd' -- data-quality (modem) call (minimum delay).\n"
+" 'h' -- allow callee to hang up by pressing *.\n"
+" 'H' -- allow caller to hang up by pressing *.\n"
+" 'n' -- no retries on the timeout; will exit this application and \n"
+" go to the next step.\n"
+" 'i' -- ignore call forward requests from queue members and do nothing\n"
+" when they are requested.\n"
+" 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
+" 't' -- allow the called user to transfer the calling user.\n"
+" 'T' -- allow the calling user to transfer the call.\n"
+" 'w' -- allow the called user to write the conversation to disk via Monitor.\n"
+" 'W' -- allow the calling user to write the conversation to disk via Monitor.\n"
+" 'k' -- Allow the called party to enable parking of the call by sending\n"
+" the DTMF sequence defined for call parking in 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 features.conf.\n"
+" 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
+" 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
+
+" In addition to transferring the call, a call may be parked and then picked\n"
+"up by another user.\n"
+" The optional URL will be sent to the called party if the channel supports\n"
+"it.\n"
+" The optional AGI parameter will setup an AGI script to be executed on the \n"
+"calling party's channel once they are connected to a queue member.\n"
+" The optional macro parameter will run a macro on the \n"
+"calling party's channel once they are connected to a queue member.\n"
+" The optional gosub parameter will run a gosub on the \n"
+"calling party's channel once they are connected to a queue member.\n"
+" The optional rule parameter will cause the queue's defaultrule to be\n"
+"overridden by the rule specified.\n"
+" The timeout will cause the queue to fail out after a specified number of\n"
+"seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
+" This application sets the following channel variable upon completion:\n"
+" QUEUESTATUS The status of the call as a text string, one of\n"
+" TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
+
+static char *app_aqm = "AddQueueMember" ;
+static char *app_aqm_synopsis = "Dynamically adds queue members" ;
+static char *app_aqm_descrip =
+" AddQueueMember(queuename[,interface[,penalty[,options[,membername]]]]):\n"
+"Dynamically adds interface to an existing queue.\n"
+"If the interface is already in the queue it will return an error.\n"
+" This application sets the following channel variable upon completion:\n"
+" AQMSTATUS The status of the attempt to add a queue member as a \n"
+" text string, one of\n"
+" ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
+"Example: AddQueueMember(techsupport,SIP/3000)\n"
+"";
+
+static char *app_rqm = "RemoveQueueMember" ;
+static char *app_rqm_synopsis = "Dynamically removes queue members" ;
+static char *app_rqm_descrip =
+" RemoveQueueMember(queuename[,interface[,options]]):\n"
+"Dynamically removes interface to an existing queue\n"
+"If the interface is NOT in the queue it will return an error.\n"
+" This application sets the following channel variable upon completion:\n"
+" RQMSTATUS The status of the attempt to remove a queue member as a\n"
+" text string, one of\n"
+" REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
+"Example: RemoveQueueMember(techsupport,SIP/3000)\n"
+"";
+
+static char *app_pqm = "PauseQueueMember" ;
+static char *app_pqm_synopsis = "Pauses a queue member" ;
+static char *app_pqm_descrip =
+" PauseQueueMember([queuename],interface[,options[,reason]]):\n"
+"Pauses (blocks calls for) a queue member.\n"
+"The given interface will be paused in the given queue. This prevents\n"
+"any calls from being sent from the queue to the interface until it is\n"
+"unpaused with UnpauseQueueMember or the manager interface. If no\n"
+"queuename is given, the interface is paused in every queue it is a\n"
+"member of. The application will fail if the interface is not found.\n"
+"The reason string is entirely optional and is used to add extra information\n"
+"to the appropriate queue_log entries and manager events.\n"
+" This application sets the following channel variable upon completion:\n"
+" PQMSTATUS The status of the attempt to pause a queue member as a\n"
+" text string, one of\n"
+" PAUSED | NOTFOUND\n"
+"Example: PauseQueueMember(,SIP/3000)\n";
+
+static char *app_upqm = "UnpauseQueueMember" ;
+static char *app_upqm_synopsis = "Unpauses a queue member" ;
+static char *app_upqm_descrip =
+" UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
+"Unpauses (resumes calls to) a queue member.\n"
+"This is the counterpart to PauseQueueMember and operates exactly the\n"
+"same way, except it unpauses instead of pausing the given interface.\n"
+"The reason string is entirely optional and is used to add extra information\n"
+"to the appropriate queue_log entries and manager events.\n"
+" This application sets the following channel variable upon completion:\n"
+" UPQMSTATUS The status of the attempt to unpause a queue \n"
+" member as a text string, one of\n"
+" UNPAUSED | NOTFOUND\n"
+"Example: UnpauseQueueMember(,SIP/3000)\n";
+
+static char *app_ql = "QueueLog" ;
+static char *app_ql_synopsis = "Writes to the queue_log" ;
+static char *app_ql_descrip =
+" QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
+"Allows you to write your own events into the queue log\n"
+"Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
+
+/*! \brief Persistent Members astdb family */
+static const char *pm_family = "Queue/PersistentMembers";
+/* The maximum length of each persistent member queue database entry */
+#define PM_MAX_LEN 8192
+
+/*! \brief queues.conf [general] option */
+static int queue_keep_stats = 0;
+
+/*! \brief queues.conf [general] option */
+static int queue_persistent_members = 0;
+
+/*! \brief queues.conf per-queue weight option */
+static int use_weight = 0;
+
+/*! \brief queues.conf [general] option */
+static int autofill_default = 0;
+
+/*! \brief queues.conf [general] option */
+static int montype_default = 0;
+
+/*! \brief queues.conf [general] option */
+static int shared_lastcall = 0;
+
+/*! \brief Subscription to device state change events */
+static struct ast_event_sub *device_state_sub;
+
+/*! \brief queues.conf [general] option */
+static int update_cdr = 0;
+
+enum queue_result {
+ QUEUE_UNKNOWN = 0,
+ QUEUE_TIMEOUT = 1,
+ QUEUE_JOINEMPTY = 2,
+ QUEUE_LEAVEEMPTY = 3,
+ QUEUE_JOINUNAVAIL = 4,
+ QUEUE_LEAVEUNAVAIL = 5,
+ QUEUE_FULL = 6,
+ QUEUE_CONTINUE = 7,
+};
+
+const struct {
+ enum queue_result id;
+ char *text;
+} queue_results[] = {
+ { QUEUE_UNKNOWN, "UNKNOWN" },
+ { QUEUE_TIMEOUT, "TIMEOUT" },
+ { QUEUE_JOINEMPTY,"JOINEMPTY" },
+ { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
+ { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
+ { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
+ { QUEUE_FULL, "FULL" },
+ { QUEUE_CONTINUE, "CONTINUE" },
+};
+
+/*! \brief 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.
+
+ There are two "links" defined in this structure, q_next and call_next.
+ q_next links ALL defined callattempt structures into a linked list. call_next is
+ a link which allows for a subset of the callattempts to be traversed. This subset
+ is used in wait_for_answer so that irrelevant callattempts are not traversed. This
+ also is helpful so that queue logs are always accurate in the case where a call to
+ a member times out, especially if using the ringall strategy. */
+
+struct callattempt {
+ struct callattempt *q_next;
+ struct callattempt *call_next;
+ struct ast_channel *chan;
+ char interface[256];
+ int stillgoing;
+ int metric;
+ int oldstatus;
+ time_t lastcall;
+ struct call_queue *lastqueue;
+ struct member *member;
+};
+
+
+struct queue_ent {
+ struct call_queue *parent; /*!< What queue is our parent */
+ char moh[80]; /*!< Name of musiconhold to be used */
+ char announce[80]; /*!< Announcement to play for member when call is answered */
+ char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
+ char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
+ int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
+ int pos; /*!< Where we are in the queue */
+ int prio; /*!< Our priority */
+ int last_pos_said; /*!< Last position we told the user */
+ time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
+ int last_periodic_announce_sound; /*!< The last periodic announcement we made */
+ time_t last_pos; /*!< Last time we told the user their position */
+ int opos; /*!< Where we started in the queue */
+ int handled; /*!< Whether our call was handled */
+ int pending; /*!< Non-zero if we are attempting to call a member */
+ int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
+ int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
+ int linpos; /*!< If using linear strategy, what position are we at? */
+ int linwrapped; /*!< Is the linpos wrapped? */
+ time_t start; /*!< When we started holding */
+ time_t expire; /*!< When this entry should expire (time out of queue) */
+ struct ast_channel *chan; /*!< Our channel */
+ AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
+ struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
+ struct queue_ent *next; /*!< The next queue entry */
+};
+
+struct member {
+ char interface[80]; /*!< Technology/Location to dial to reach this member*/
+ char state_interface[80]; /*!< Technology/Location from which to read devicestate changes */
+ char membername[80]; /*!< Member name to use in queue logs */
+ int penalty; /*!< Are we a last resort? */
+ int calls; /*!< Number of calls serviced by this member */
+ int dynamic; /*!< Are we dynamically added? */
+ int realtime; /*!< Is this member realtime? */
+ int status; /*!< Status of queue member */
+ int paused; /*!< Are we paused (not accepting calls)? */
+ time_t lastcall; /*!< When last successful call was hungup */
+ struct call_queue *lastqueue; /*!< Last queue we received a call */
+ unsigned int dead:1; /*!< Used to detect members deleted in realtime */
+ unsigned int delme:1; /*!< Flag to delete entry on reload */
+};
+
+struct member_interface {
+ char interface[80];
+ AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
+};
+
+static AST_LIST_HEAD_STATIC(interfaces, member_interface);
+
+/* values used in multi-bit flags in call_queue */
+#define QUEUE_EMPTY_NORMAL 1
+#define QUEUE_EMPTY_STRICT 2
+#define QUEUE_EMPTY_LOOSE 3
+#define ANNOUNCEHOLDTIME_ALWAYS 1
+#define ANNOUNCEHOLDTIME_ONCE 2
+#define QUEUE_EVENT_VARIABLES 3
+
+struct penalty_rule {
+ int time; /*!< Number of seconds that need to pass before applying this rule */
+ int max_value; /*!< The amount specified in the penalty rule for max penalty */
+ int min_value; /*!< The amount specified in the penalty rule for min penalty */
+ int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
+ int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
+ AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
+};
+
+struct call_queue {
+ AST_DECLARE_STRING_FIELDS(
+ /*! Queue name */
+ AST_STRING_FIELD(name);
+ /*! Music on Hold class */
+ AST_STRING_FIELD(moh);
+ /*! Announcement to play when call is answered */
+ AST_STRING_FIELD(announce);
+ /*! Exit context */
+ AST_STRING_FIELD(context);
+ /*! Macro to run upon member connection */
+ AST_STRING_FIELD(membermacro);
+ /*! Gosub to run upon member connection */
+ AST_STRING_FIELD(membergosub);
+ /*! Default rule to use if none specified in call to Queue() */
+ AST_STRING_FIELD(defaultrule);
+ /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
+ AST_STRING_FIELD(sound_next);
+ /*! Sound file: "There are currently" (def. queue-thereare) */
+ AST_STRING_FIELD(sound_thereare);
+ /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
+ AST_STRING_FIELD(sound_calls);
+ /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
+ AST_STRING_FIELD(sound_holdtime);
+ /*! Sound file: "minutes." (def. queue-minutes) */
+ AST_STRING_FIELD(sound_minutes);
+ /*! Sound file: "less-than" (def. queue-lessthan) */
+ AST_STRING_FIELD(sound_lessthan);
+ /*! Sound file: "seconds." (def. queue-seconds) */
+ AST_STRING_FIELD(sound_seconds);
+ /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
+ AST_STRING_FIELD(sound_thanks);
+ /*! Sound file: Custom announce for caller, no default */
+ AST_STRING_FIELD(sound_callerannounce);
+ /*! Sound file: "Hold time" (def. queue-reporthold) */
+ AST_STRING_FIELD(sound_reporthold);
+ );
+ /*! Sound files: Custom announce, no default */
+ struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
+ unsigned int dead:1;
+ unsigned int joinempty:2;
+ unsigned int eventwhencalled:2;
+ unsigned int leavewhenempty:2;
+ unsigned int ringinuse:1;
+ unsigned int setinterfacevar:1;
+ unsigned int setqueuevar:1;
+ unsigned int setqueueentryvar:1;
+ unsigned int reportholdtime:1;
+ unsigned int wrapped:1;
+ unsigned int timeoutrestart:1;
+ unsigned int announceholdtime:2;
+ unsigned int announceposition:1;
+ int strategy:4;
+ unsigned int maskmemberstatus:1;
+ unsigned int realtime:1;
+ unsigned int found:1;
+ int announcefrequency; /*!< How often to announce their position */
+ int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
+ int periodicannouncefrequency; /*!< How often to play periodic announcement */
+ int roundingseconds; /*!< How many seconds do we round to? */
+ int holdtime; /*!< Current avg holdtime, based on recursive boxcar filter */
+ int callscompleted; /*!< Number of queue calls completed */
+ int callsabandoned; /*!< Number of queue calls abandoned */
+ int servicelevel; /*!< seconds setting for servicelevel*/
+ int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
+ char monfmt[8]; /*!< Format to use when recording calls */
+ int montype; /*!< Monitor type Monitor vs. MixMonitor */
+ int count; /*!< How many entries */
+ int maxlen; /*!< Max number of entries */
+ int wrapuptime; /*!< Wrapup Time */
+
+ int retry; /*!< Retry calling everyone after this amount of time */
+ int timeout; /*!< How long to wait for an answer */
+ int weight; /*!< Respective weight */
+ int autopause; /*!< Auto pause queue members if they fail to answer */
+
+ /* Queue strategy things */
+ int rrpos; /*!< Round Robin - position */
+ int memberdelay; /*!< Seconds to delay connecting member to caller */
+ int autofill; /*!< Ignore the head call status and ring an available agent */
+
+ struct ao2_container *members; /*!< Head of the list of members */
+ /*!
+ * \brief Number of members _logged in_
+ * \note There will be members in the members container that are not logged
+ * in, so this can not simply be replaced with ao2_container_count().
+ */
+ int membercount;
+ struct queue_ent *head; /*!< Head of the list of callers */
+ AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
+ AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
+};
+
+struct rule_list {
+ char name[80];
+ AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
+ AST_LIST_ENTRY(rule_list) list;
+};
+
+AST_LIST_HEAD_STATIC(rule_lists, rule_list);
+
+static struct ao2_container *queues;
+
+static void update_realtime_members(struct call_queue *q);
+static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
+
+/*! \brief sets the QUEUESTATUS channel variable */
+static void set_queue_result(struct ast_channel *chan, enum queue_result res)
+{
+ int i;
+
+ for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
+ if (queue_results[i].id == res) {
+ pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
+ return;
+ }
+ }
+}
+
+static char *int2strat(int strategy)
+{
+ int x;
+
+ for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
+ if (strategy == strategies[x].strategy)
+ return strategies[x].name;
+ }
+
+ return "<unknown>";
+}
+
+static int strat2int(const char *strategy)
+{
+ int x;
+
+ for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
+ if (!strcasecmp(strategy, strategies[x].name))
+ return strategies[x].strategy;
+ }
+
+ return -1;
+}
+
+static int queue_hash_cb(const void *obj, const int flags)
+{
+ const struct call_queue *q = obj;
+ return ast_str_hash(q->name);
+}
+
+static int queue_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct call_queue *q = obj, *q2 = arg;
+ return !strcasecmp(q->name, q2->name) ? CMP_MATCH : 0;
+}
+
+static inline struct call_queue *queue_ref(struct call_queue *q)
+{
+ ao2_ref(q, 1);
+ return q;
+}
+
+static inline struct call_queue *queue_unref(struct call_queue *q)
+{
+ ao2_ref(q, -1);
+ return q;
+}
+
+static void set_queue_variables(struct queue_ent *qe)
+{
+
+ char interfacevar[256]="";
+ float sl = 0;
+
+ if (qe->parent->setqueuevar) {
+ sl = 0;
+ if (qe->parent->callscompleted > 0)
+ sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
+
+ snprintf(interfacevar,sizeof(interfacevar),
+ "QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f",
+ qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
+ qe->parent->callsabandoned, qe->parent->servicelevel, sl);
+
+ pbx_builtin_setvar(qe->chan, interfacevar);
+ }
+}
+
+/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
+static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
+{
+ struct queue_ent *cur;
+
+ if (!q || !new)
+ return;
+ if (prev) {
+ cur = prev->next;
+ prev->next = new;
+ } else {
+ cur = q->head;
+ q->head = new;
+ }
+ new->next = cur;
+ new->parent = q;
+ new->pos = ++(*pos);
+ new->opos = *pos;
+}
+
+enum queue_member_status {
+ QUEUE_NO_MEMBERS,
+ QUEUE_NO_REACHABLE_MEMBERS,
+ QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
+ QUEUE_NORMAL
+};
+
+/*! \brief Check if members are available
+ *
+ * This function checks to see if members are available to be called. If any member
+ * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
+ * the appropriate reason why is returned
+ */
+static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
+{
+ struct member *member;
+ struct ao2_iterator mem_iter;
+ enum queue_member_status result = QUEUE_NO_MEMBERS;
+
+ ao2_lock(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
+ if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
+ continue;
+
+ switch (member->status) {
+ case AST_DEVICE_INVALID:
+ /* nothing to do */
+ break;
+ case AST_DEVICE_UNAVAILABLE:
+ if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)
+ result = QUEUE_NO_REACHABLE_MEMBERS;
+ break;
+ default:
+ if (member->paused) {
+ result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
+ } else {
+ ao2_unlock(q);
+ ao2_ref(member, -1);
+ return QUEUE_NORMAL;
+ }
+ break;
+ }
+ }
+
+ ao2_unlock(q);
+ return result;
+}
+
+struct statechange {
+ AST_LIST_ENTRY(statechange) entry;
+ int state;
+ char dev[0];
+};
+/*! \brief set a member's status based on device state of that member's state_interface*/
+static void *handle_statechange(struct statechange *sc)
+{
+ struct call_queue *q;
+ struct member *cur;
+ struct ao2_iterator mem_iter;
+ struct member_interface *curint;
+ struct ao2_iterator queue_iter;
+ char *loc;
+ char *technology;
+
+ technology = ast_strdupa(sc->dev);
+ loc = strchr(technology, '/');
+ if (loc) {
+ *loc++ = '\0';
+ } else {
+ return NULL;
+ }
+
+ AST_LIST_LOCK(&interfaces);
+ AST_LIST_TRAVERSE(&interfaces, curint, list) {
+ char *interface;
+ char *slash_pos;
+ interface = ast_strdupa(curint->interface);
+ if ((slash_pos = strchr(interface, '/')))
+ if ((slash_pos = strchr(slash_pos + 1, '/')))
+ *slash_pos = '\0';
+
+ if (!strcasecmp(interface, sc->dev))
+ break;
+ }
+ AST_LIST_UNLOCK(&interfaces);
+
+ if (!curint) {
+ ast_debug(3, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
+ return NULL;
+ }
+
+ ast_debug(1, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((cur = ao2_iterator_next(&mem_iter))) {
+ char *interface;
+ char *slash_pos;
+ interface = ast_strdupa(cur->state_interface);
+ if ((slash_pos = strchr(interface, '/')))
+ if ((slash_pos = strchr(slash_pos + 1, '/')))
+ *slash_pos = '\0';
+
+ if (strcasecmp(sc->dev, interface)) {
+ ao2_ref(cur, -1);
+ continue;
+ }
+
+ if (cur->status != sc->state) {
+ cur->status = sc->state;
+ if (q->maskmemberstatus) {
+ ao2_ref(cur, -1);
+ continue;
+ }
+
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n",
+ q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
+ cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
+ }
+ ao2_ref(cur, -1);
+ }
+ queue_unref(q);
+ ao2_unlock(q);
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Data used by the device state thread
+ */
+static struct {
+ /*! Set to 1 to stop the thread */
+ unsigned int stop:1;
+ /*! The device state monitoring thread */
+ pthread_t thread;
+ /*! Lock for the state change queue */
+ ast_mutex_t lock;
+ /*! Condition for the state change queue */
+ ast_cond_t cond;
+ /*! Queue of state changes */
+ AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
+} device_state = {
+ .thread = AST_PTHREADT_NULL,
+};
+
+/*! \brief Consumer of the statechange queue */
+static void *device_state_thread(void *data)
+{
+ struct statechange *sc = NULL;
+
+ while (!device_state.stop) {
+ ast_mutex_lock(&device_state.lock);
+ if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
+ ast_cond_wait(&device_state.cond, &device_state.lock);
+ sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
+ }
+ ast_mutex_unlock(&device_state.lock);
+
+ /* Check to see if we were woken up to see the request to stop */
+ if (device_state.stop)
+ break;
+
+ if (!sc)
+ continue;
+
+ handle_statechange(sc);
+
+ ast_free(sc);
+ sc = NULL;
+ }
+
+ if (sc)
+ ast_free(sc);
+
+ while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
+ ast_free(sc);
+
+ return NULL;
+}
+/*! \brief Producer of the statechange queue */
+static int statechange_queue(const char *dev, enum ast_device_state state)
+{
+ struct statechange *sc;
+
+ if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
+ return 0;
+
+ sc->state = state;
+ strcpy(sc->dev, dev);
+
+ ast_mutex_lock(&device_state.lock);
+ AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
+ ast_cond_signal(&device_state.cond);
+ ast_mutex_unlock(&device_state.lock);
+
+ return 0;
+}
+static void device_state_cb(const struct ast_event *event, void *unused)
+{
+ enum ast_device_state state;
+ const char *device;
+
+ state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+ device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
+
+ if (ast_strlen_zero(device)) {
+ ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
+ return;
+ }
+
+ statechange_queue(device, state);
+}
+
+/*! \brief allocate space for new queue member and set fields based on parameters passed */
+static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
+{
+ struct member *cur;
+
+ if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
+ cur->penalty = penalty;
+ cur->paused = paused;
+ ast_copy_string(cur->interface, interface, sizeof(cur->interface));
+ if (!ast_strlen_zero(state_interface))
+ ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
+ else
+ ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
+ if (!ast_strlen_zero(membername))
+ ast_copy_string(cur->membername, membername, sizeof(cur->membername));
+ else
+ ast_copy_string(cur->membername, interface, sizeof(cur->membername));
+ if (!strchr(cur->interface, '/'))
+ ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
+ cur->status = ast_device_state(cur->state_interface);
+ }
+
+ return cur;
+}
+
+
+static int compress_char(const char c)
+{
+ if (c < 32)
+ return 0;
+ else if (c > 96)
+ return c - 64;
+ else
+ return c - 32;
+}
+
+static int member_hash_fn(const void *obj, const int flags)
+{
+ const struct member *mem = obj;
+ const char *chname = strchr(mem->interface, '/');
+ int ret = 0, i;
+ if (!chname)
+ chname = mem->interface;
+ for (i = 0; i < 5 && chname[i]; i++)
+ ret += compress_char(chname[i]) << (i * 6);
+ return ret;
+}
+
+static int member_cmp_fn(void *obj1, void *obj2, int flags)
+{
+ struct member *mem1 = obj1, *mem2 = obj2;
+ return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
+}
+
+static void init_queue(struct call_queue *q)
+{
+ int i;
+ struct penalty_rule *pr_iter;
+
+ q->dead = 0;
+ q->retry = DEFAULT_RETRY;
+ q->timeout = -1;
+ q->maxlen = 0;
+ q->announcefrequency = 0;
+ q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
+ q->announceholdtime = 0;
+ q->announceholdtime = 1;
+ q->roundingseconds = 0; /* Default - don't announce seconds */
+ q->servicelevel = 0;
+ q->ringinuse = 1;
+ q->setinterfacevar = 0;
+ q->setqueuevar = 0;
+ q->setqueueentryvar = 0;
+ q->autofill = autofill_default;
+ q->montype = montype_default;
+ q->monfmt[0] = '\0';
+ q->reportholdtime = 0;
+ q->wrapuptime = 0;
+ q->autofill = 0;
+ q->joinempty = 0;
+ q->leavewhenempty = 0;
+ q->memberdelay = 0;
+ q->maskmemberstatus = 0;
+ q->eventwhencalled = 0;
+ q->weight = 0;
+ q->timeoutrestart = 0;
+ q->periodicannouncefrequency = 0;
+ if (!q->members) {
+ if (q->strategy == QUEUE_STRATEGY_LINEAR)
+ /* linear strategy depends on order, so we have to place all members in a single bucket */
+ q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
+ else
+ q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
+ }
+ q->membercount = 0;
+ q->found = 1;
+
+ ast_string_field_set(q, sound_next, "queue-youarenext");
+ ast_string_field_set(q, sound_thereare, "queue-thereare");
+ ast_string_field_set(q, sound_calls, "queue-callswaiting");
+ ast_string_field_set(q, sound_holdtime, "queue-holdtime");
+ ast_string_field_set(q, sound_minutes, "queue-minutes");
+ ast_string_field_set(q, sound_seconds, "queue-seconds");
+ ast_string_field_set(q, sound_thanks, "queue-thankyou");
+ ast_string_field_set(q, sound_lessthan, "queue-less-than");
+ ast_string_field_set(q, sound_reporthold, "queue-reporthold");
+
+ if ((q->sound_periodicannounce[0] = ast_str_create(32)))
+ ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
+
+ for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
+ if (q->sound_periodicannounce[i])
+ ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
+ }
+
+ while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
+ ast_free(pr_iter);
+}
+
+static void clear_queue(struct call_queue *q)
+{
+ q->holdtime = 0;
+ q->callscompleted = 0;
+ q->callsabandoned = 0;
+ q->callscompletedinsl = 0;
+ q->wrapuptime = 0;
+}
+
+static int add_to_interfaces(const char *interface)
+{
+ struct member_interface *curint;
+
+ AST_LIST_LOCK(&interfaces);
+ AST_LIST_TRAVERSE(&interfaces, curint, list) {
+ if (!strcasecmp(curint->interface, interface))
+ break;
+ }
+
+ if (curint) {
+ AST_LIST_UNLOCK(&interfaces);
+ return 0;
+ }
+
+ ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
+
+ if ((curint = ast_calloc(1, sizeof(*curint)))) {
+ ast_copy_string(curint->interface, interface, sizeof(curint->interface));
+ AST_LIST_INSERT_HEAD(&interfaces, curint, list);
+ }
+ AST_LIST_UNLOCK(&interfaces);
+
+ return 0;
+}
+
+static int interface_exists_global(const char *interface)
+{
+ struct call_queue *q;
+ struct member *mem, tmpmem;
+ struct ao2_iterator queue_iter, mem_iter;
+ int ret = 0;
+
+ ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((mem = ao2_iterator_next(&mem_iter))) {
+ if (!strcasecmp(mem->state_interface, interface)) {
+ ao2_ref(mem, -1);
+ ret = 1;
+ break;
+ }
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+
+ return ret;
+}
+
+static int remove_from_interfaces(const char *interface)
+{
+ struct member_interface *curint;
+
+ AST_LIST_LOCK(&interfaces);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
+ if (!strcasecmp(curint->interface, interface)) {
+ if (!interface_exists_global(interface)) {
+ ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(curint);
+ }
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&interfaces);
+
+ return 0;
+}
+
+static void clear_and_free_interfaces(void)
+{
+ struct member_interface *curint;
+
+ AST_LIST_LOCK(&interfaces);
+ while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
+ ast_free(curint);
+ AST_LIST_UNLOCK(&interfaces);
+}
+
+/*Note: call this with the rule_lists locked */
+static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
+{
+ char *timestr, *maxstr, *minstr, *contentdup;
+ struct penalty_rule *rule = NULL, *rule_iter;
+ struct rule_list *rl_iter;
+ int time, inserted = 0;
+
+ if (!(rule = ast_calloc(1, sizeof(*rule)))) {
+ ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
+ return -1;
+ }
+
+ contentdup = ast_strdupa(content);
+
+ if (!(maxstr = strchr(contentdup, ','))) {
+ ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
+ ast_free(rule);
+ return -1;
+ }
+
+ *maxstr++ = '\0';
+ timestr = contentdup;
+
+ if ((time = atoi(timestr)) < 0) {
+ ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
+ ast_free(rule);
+ return -1;
+ }
+
+ rule->time = time;
+
+ if ((minstr = strchr(maxstr,',')))
+ *minstr++ = '\0';
+
+ /* The last check will evaluate true if either no penalty change is indicated for a given rule
+ * OR if a min penalty change is indicated but no max penalty change is */
+ if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
+ rule->max_relative = 1;
+ }
+
+ rule->max_value = atoi(maxstr);
+
+ if (!ast_strlen_zero(minstr)) {
+ if (*minstr == '+' || *minstr == '-')
+ rule->min_relative = 1;
+ rule->min_value = atoi(minstr);
+ } else /*there was no minimum specified, so assume this means no change*/
+ rule->min_relative = 1;
+
+ /*We have the rule made, now we need to insert it where it belongs*/
+ AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
+ if (strcasecmp(rl_iter->name, list_name))
+ continue;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
+ if (rule->time < rule_iter->time) {
+ AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
+ inserted = 1;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!inserted) {
+ AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Configure a queue parameter.
+\par
+ For error reporting, line number is passed for .conf static configuration.
+ For Realtime queues, linenum is -1.
+ The failunknown flag is set for config files (and static realtime) to show
+ errors for unknown parameters. It is cleared for dynamic realtime to allow
+ extra fields in the tables. */
+static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
+{
+ if (!strcasecmp(param, "musicclass") ||
+ !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
+ ast_string_field_set(q, moh, val);
+ } else if (!strcasecmp(param, "announce")) {
+ ast_string_field_set(q, announce, val);
+ } else if (!strcasecmp(param, "context")) {
+ ast_string_field_set(q, context, val);
+ } else if (!strcasecmp(param, "timeout")) {
+ q->timeout = atoi(val);
+ if (q->timeout < 0)
+ q->timeout = DEFAULT_TIMEOUT;
+ } else if (!strcasecmp(param, "ringinuse")) {
+ q->ringinuse = ast_true(val);
+ } else if (!strcasecmp(param, "setinterfacevar")) {
+ q->setinterfacevar = ast_true(val);
+ } else if (!strcasecmp(param, "setqueuevar")) {
+ q->setqueuevar = ast_true(val);
+ } else if (!strcasecmp(param, "setqueueentryvar")) {
+ q->setqueueentryvar = ast_true(val);
+ } else if (!strcasecmp(param, "monitor-format")) {
+ ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
+ } else if (!strcasecmp(param, "membermacro")) {
+ ast_string_field_set(q, membermacro, val);
+ } else if (!strcasecmp(param, "membergosub")) {
+ ast_string_field_set(q, membergosub, val);
+ } else if (!strcasecmp(param, "queue-youarenext")) {
+ ast_string_field_set(q, sound_next, val);
+ } else if (!strcasecmp(param, "queue-thereare")) {
+ ast_string_field_set(q, sound_thereare, val);
+ } else if (!strcasecmp(param, "queue-callswaiting")) {
+ ast_string_field_set(q, sound_calls, val);
+ } else if (!strcasecmp(param, "queue-holdtime")) {
+ ast_string_field_set(q, sound_holdtime, val);
+ } else if (!strcasecmp(param, "queue-minutes")) {
+ ast_string_field_set(q, sound_minutes, val);
+ } else if (!strcasecmp(param, "queue-seconds")) {
+ ast_string_field_set(q, sound_seconds, val);
+ } else if (!strcasecmp(param, "queue-lessthan")) {
+ ast_string_field_set(q, sound_lessthan, val);
+ } else if (!strcasecmp(param, "queue-thankyou")) {
+ ast_string_field_set(q, sound_thanks, val);
+ } else if (!strcasecmp(param, "queue-callerannounce")) {
+ ast_string_field_set(q, sound_callerannounce, val);
+ } else if (!strcasecmp(param, "queue-reporthold")) {
+ ast_string_field_set(q, sound_reporthold, val);
+ } else if (!strcasecmp(param, "announce-frequency")) {
+ q->announcefrequency = atoi(val);
+ } else if (!strcasecmp(param, "min-announce-frequency")) {
+ q->minannouncefrequency = atoi(val);
+ ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
+ } else if (!strcasecmp(param, "announce-round-seconds")) {
+ q->roundingseconds = atoi(val);
+ /* Rounding to any other values just doesn't make sense... */
+ if (!(q->roundingseconds == 0 || q->roundingseconds == 1 || q->roundingseconds == 5 || q->roundingseconds == 10
+ || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
+ if (linenum >= 0) {
+ ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
+ "using 0 instead for queue '%s' at line %d of queues.conf\n",
+ val, param, q->name, linenum);
+ } else {
+ ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
+ "using 0 instead for queue '%s'\n", val, param, q->name);
+ }
+ q->roundingseconds=0;
+ }
+ } else if (!strcasecmp(param, "announce-holdtime")) {
+ if (!strcasecmp(val, "once"))
+ q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
+ else if (ast_true(val))
+ q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
+ else
+ q->announceholdtime = 0;
+ } else if (!strcasecmp(param, "announce-position")) {
+ q->announceposition = ast_true(val);
+ } else if (!strcasecmp(param, "periodic-announce")) {
+ if (strchr(val, ',')) {
+ char *s, *buf = ast_strdupa(val);
+ unsigned int i = 0;
+
+ while ((s = strsep(&buf, ",|"))) {
+ if (!q->sound_periodicannounce[i])
+ q->sound_periodicannounce[i] = ast_str_create(16);
+ ast_str_set(&q->sound_periodicannounce[i], 0, s);
+ i++;
+ if (i == MAX_PERIODIC_ANNOUNCEMENTS)
+ break;
+ }
+ } else {
+ ast_str_set(&q->sound_periodicannounce[0], 0, val);
+ }
+ } else if (!strcasecmp(param, "periodic-announce-frequency")) {
+ q->periodicannouncefrequency = atoi(val);
+ } else if (!strcasecmp(param, "retry")) {
+ q->retry = atoi(val);
+ if (q->retry <= 0)
+ q->retry = DEFAULT_RETRY;
+ } else if (!strcasecmp(param, "wrapuptime")) {
+ q->wrapuptime = atoi(val);
+ } else if (!strcasecmp(param, "autofill")) {
+ q->autofill = ast_true(val);
+ } else if (!strcasecmp(param, "monitor-type")) {
+ if (!strcasecmp(val, "mixmonitor"))
+ q->montype = 1;
+ } else if (!strcasecmp(param, "autopause")) {
+ q->autopause = ast_true(val);
+ } else if (!strcasecmp(param, "maxlen")) {
+ q->maxlen = atoi(val);
+ if (q->maxlen < 0)
+ q->maxlen = 0;
+ } else if (!strcasecmp(param, "servicelevel")) {
+ q->servicelevel= atoi(val);
+ } else if (!strcasecmp(param, "strategy")) {
+ /* We already have set this, no need to do it again */
+ return;
+ } else if (!strcasecmp(param, "joinempty")) {
+ if (!strcasecmp(val, "loose"))
+ q->joinempty = QUEUE_EMPTY_LOOSE;
+ else if (!strcasecmp(val, "strict"))
+ q->joinempty = QUEUE_EMPTY_STRICT;
+ else if (ast_true(val))
+ q->joinempty = QUEUE_EMPTY_NORMAL;
+ else
+ q->joinempty = 0;
+ } else if (!strcasecmp(param, "leavewhenempty")) {
+ if (!strcasecmp(val, "loose"))
+ q->leavewhenempty = QUEUE_EMPTY_LOOSE;
+ else if (!strcasecmp(val, "strict"))
+ q->leavewhenempty = QUEUE_EMPTY_STRICT;
+ else if (ast_true(val))
+ q->leavewhenempty = QUEUE_EMPTY_NORMAL;
+ else
+ q->leavewhenempty = 0;
+ } else if (!strcasecmp(param, "eventmemberstatus")) {
+ q->maskmemberstatus = !ast_true(val);
+ } else if (!strcasecmp(param, "eventwhencalled")) {
+ if (!strcasecmp(val, "vars")) {
+ q->eventwhencalled = QUEUE_EVENT_VARIABLES;
+ } else {
+ q->eventwhencalled = ast_true(val) ? 1 : 0;
+ }
+ } else if (!strcasecmp(param, "reportholdtime")) {
+ q->reportholdtime = ast_true(val);
+ } else if (!strcasecmp(param, "memberdelay")) {
+ q->memberdelay = atoi(val);
+ } else if (!strcasecmp(param, "weight")) {
+ q->weight = atoi(val);
+ if (q->weight)
+ use_weight++;
+ /* With Realtime queues, if the last queue using weights is deleted in realtime,
+ we will not see any effect on use_weight until next reload. */
+ } else if (!strcasecmp(param, "timeoutrestart")) {
+ q->timeoutrestart = ast_true(val);
+ } else if (!strcasecmp(param, "defaultrule")) {
+ ast_string_field_set(q, defaultrule, val);
+ } else if (failunknown) {
+ if (linenum >= 0) {
+ ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
+ q->name, param, linenum);
+ } else {
+ ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
+ }
+ }
+}
+
+static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
+{
+ struct member *m, tmpmem;
+ int penalty = 0;
+ int paused = 0;
+
+ if (penalty_str) {
+ penalty = atoi(penalty_str);
+ if (penalty < 0)
+ penalty = 0;
+ }
+
+ if (paused_str) {
+ paused = atoi(paused_str);
+ if (paused < 0)
+ paused = 0;
+ }
+
+ /* Find the member, or the place to put a new one. */
+ ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
+ m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
+
+ /* Create a new one if not found, else update penalty */
+ if (!m) {
+ if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
+ m->dead = 0;
+ m->realtime = 1;
+ add_to_interfaces(state_interface);
+ ao2_link(q->members, m);
+ ao2_ref(m, -1);
+ m = NULL;
+ q->membercount++;
+ }
+ } else {
+ m->dead = 0; /* Do not delete this one. */
+ if (paused_str)
+ m->paused = paused;
+ if (strcasecmp(state_interface, m->state_interface)) {
+ remove_from_interfaces(m->state_interface);
+ ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
+ add_to_interfaces(m->state_interface);
+ }
+ m->penalty = penalty;
+ ao2_ref(m, -1);
+ }
+}
+
+static void free_members(struct call_queue *q, int all)
+{
+ /* Free non-dynamic members */
+ struct member *cur;
+ struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
+
+ while ((cur = ao2_iterator_next(&mem_iter))) {
+ if (all || !cur->dynamic) {
+ ao2_unlink(q->members, cur);
+ remove_from_interfaces(cur->state_interface);
+ q->membercount--;
+ }
+ ao2_ref(cur, -1);
+ }
+}
+
+static void destroy_queue(void *obj)
+{
+ struct call_queue *q = obj;
+ int i;
+
+ ast_debug(0, "Queue destructor called for queue '%s'!\n", q->name);
+
+ free_members(q, 1);
+ ast_string_field_free_memory(q);
+ for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
+ if (q->sound_periodicannounce[i])
+ free(q->sound_periodicannounce[i]);
+ }
+ ao2_ref(q->members, -1);
+}
+
+static struct call_queue *alloc_queue(const char *queuename)
+{
+ struct call_queue *q;
+
+ if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
+ if (ast_string_field_init(q, 64)) {
+ free(q);
+ return NULL;
+ }
+ ast_string_field_set(q, name, queuename);
+ }
+ return q;
+}
+
+/*!\brief Reload a single queue via realtime.
+ \return Return the queue, or NULL if it doesn't exist.
+ \note Should be called with the global qlock locked. */
+static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
+{
+ struct ast_variable *v;
+ struct call_queue *q, tmpq = {
+ .name = queuename,
+ };
+ struct member *m;
+ struct ao2_iterator mem_iter;
+ char *interface = NULL;
+ const char *tmp_name;
+ char *tmp;
+ char tmpbuf[64]; /* Must be longer than the longest queue param name. */
+
+ /* Static queues override realtime. */
+ if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
+ ao2_lock(q);
+ if (!q->realtime) {
+ if (q->dead) {
+ ao2_unlock(q);
+ queue_unref(q);
+ return NULL;
+ } else {
+ ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
+ ao2_unlock(q);
+ return q;
+ }
+ }
+ queue_unref(q);
+ } else if (!member_config)
+ /* Not found in the list, and it's not realtime ... */
+ return NULL;
+
+ /* Check if queue is defined in realtime. */
+ if (!queue_vars) {
+ /* Delete queue from in-core list if it has been deleted in realtime. */
+ if (q) {
+ /*! \note Hmm, can't seem to distinguish a DB failure from a not
+ found condition... So we might delete an in-core queue
+ in case of DB failure. */
+ ast_debug(1, "Queue %s not found in realtime.\n", queuename);
+
+ q->dead = 1;
+ /* Delete if unused (else will be deleted when last caller leaves). */
+ ao2_unlink(queues, q);
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+ return NULL;
+ }
+
+ /* Create a new queue if an in-core entry does not exist yet. */
+ if (!q) {
+ if (!(q = alloc_queue(queuename)))
+ return NULL;
+ ao2_lock(q);
+ clear_queue(q);
+ q->realtime = 1;
+ init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
+ ao2_link(queues, q);
+ }
+
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ for (v = queue_vars; v; v = v->next) {
+ /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
+ if ((tmp = strchr(v->name, '_'))) {
+ ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
+ tmp_name = tmpbuf;
+ tmp = tmpbuf;
+ while ((tmp = strchr(tmp, '_')))
+ *tmp++ = '-';
+ } else
+ tmp_name = v->name;
+ queue_set_param(q, tmp_name, v->value, -1, 0);
+ }
+
+ /* Temporarily set realtime members dead so we can detect deleted ones.
+ * Also set the membercount correctly for realtime*/
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ q->membercount++;
+ if (m->realtime)
+ m->dead = 1;
+ ao2_ref(m, -1);
+ }
+
+ while ((interface = ast_category_browse(member_config, interface))) {
+ rt_handle_member_record(q, interface,
+ ast_variable_retrieve(member_config, interface, "membername"),
+ ast_variable_retrieve(member_config, interface, "penalty"),
+ ast_variable_retrieve(member_config, interface, "paused"),
+ ast_variable_retrieve(member_config, interface, "state_interface"));
+ }
+
+ /* Delete all realtime members that have been deleted in DB. */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (m->dead) {
+ ao2_unlink(q->members, m);
+ remove_from_interfaces(m->state_interface);
+ q->membercount--;
+ }
+ ao2_ref(m, -1);
+ }
+
+ ao2_unlock(q);
+
+ return q;
+}
+
+static struct call_queue *load_realtime_queue(const char *queuename)
+{
+ struct ast_variable *queue_vars;
+ struct ast_config *member_config = NULL;
+ struct call_queue *q = NULL, tmpq = {
+ .name = queuename,
+ };
+
+ /* Find the queue in the in-core list first. */
+ q = ao2_find(queues, &tmpq, OBJ_POINTER);
+
+ if (!q || q->realtime) {
+ /*! \note Load from realtime before taking the global qlock, to avoid blocking all
+ queue operations while waiting for the DB.
+
+ This will be two separate database transactions, so we might
+ see queue parameters as they were before another process
+ changed the queue and member list as it was after the change.
+ Thus we might see an empty member list when a queue is
+ deleted. In practise, this is unlikely to cause a problem. */
+
+ queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
+ if (queue_vars) {
+ member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
+ if (!member_config) {
+ ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
+ ast_variables_destroy(queue_vars);
+ return NULL;
+ }
+ }
+
+ ao2_lock(queues);
+ q = find_queue_by_name_rt(queuename, queue_vars, member_config);
+ if (member_config)
+ ast_config_destroy(member_config);
+ if (queue_vars)
+ ast_variables_destroy(queue_vars);
+ ao2_unlock(queues);
+
+ } else {
+ update_realtime_members(q);
+ }
+ return q;
+}
+
+static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
+{
+ struct ast_variable *var;
+ int ret = -1;
+
+ if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL)))
+ return ret;
+ while (var) {
+ if (!strcmp(var->name, "uniqueid"))
+ break;
+ var = var->next;
+ }
+ if (var && !ast_strlen_zero(var->value)) {
+ if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
+ ret = 0;
+ }
+ return ret;
+}
+
+static void update_realtime_members(struct call_queue *q)
+{
+ struct ast_config *member_config = NULL;
+ struct member *m;
+ char *interface = NULL;
+ struct ao2_iterator mem_iter;
+
+ member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL);
+ if (!member_config) {
+ /*This queue doesn't have realtime members*/
+ ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
+ return;
+ }
+
+ ao2_lock(q);
+
+ /* Temporarily set realtime members dead so we can detect deleted ones.*/
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (m->realtime)
+ m->dead = 1;
+ ao2_ref(m, -1);
+ }
+
+ while ((interface = ast_category_browse(member_config, interface))) {
+ rt_handle_member_record(q, interface,
+ S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
+ ast_variable_retrieve(member_config, interface, "penalty"),
+ ast_variable_retrieve(member_config, interface, "paused"),
+ ast_variable_retrieve(member_config, interface, "state_interface"));
+ }
+
+ /* Delete all realtime members that have been deleted in DB. */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (m->dead) {
+ ao2_unlink(q->members, m);
+ remove_from_interfaces(m->state_interface);
+ q->membercount--;
+ }
+ ao2_ref(m, -1);
+ }
+ ao2_unlock(q);
+}
+
+static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
+{
+ struct call_queue *q;
+ struct queue_ent *cur, *prev = NULL;
+ int res = -1;
+ int pos = 0;
+ int inserted = 0;
+ enum queue_member_status stat;
+
+ if (!(q = load_realtime_queue(queuename)))
+ return res;
+
+ ao2_lock(queues);
+ ao2_lock(q);
+
+ /* This is our one */
+ stat = get_member_status(q, qe->max_penalty, qe->min_penalty);
+ if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
+ *reason = QUEUE_JOINEMPTY;
+ else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS))
+ *reason = QUEUE_JOINUNAVAIL;
+ else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
+ *reason = QUEUE_JOINUNAVAIL;
+ else if (q->maxlen && (q->count >= q->maxlen))
+ *reason = QUEUE_FULL;
+ else {
+ /* There's space for us, put us at the right position inside
+ * the queue.
+ * Take into account the priority of the calling user */
+ inserted = 0;
+ prev = NULL;
+ cur = q->head;
+ while (cur) {
+ /* We have higher priority than the current user, enter
+ * before him, after all the other users with priority
+ * higher or equal to our priority. */
+ if ((!inserted) && (qe->prio > cur->prio)) {
+ insert_entry(q, prev, qe, &pos);
+ inserted = 1;
+ }
+ cur->pos = ++pos;
+ prev = cur;
+ cur = cur->next;
+ }
+ /* No luck, join at the end of the queue */
+ if (!inserted)
+ insert_entry(q, prev, qe, &pos);
+ ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
+ ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
+ ast_copy_string(qe->context, q->context, sizeof(qe->context));
+ q->count++;
+ res = 0;
+ manager_event(EVENT_FLAG_CALL, "Join",
+ "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
+ qe->chan->name,
+ S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
+ S_OR(qe->chan->cid.cid_name, "unknown"),
+ q->name, qe->pos, q->count, qe->chan->uniqueid );
+ ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
+ }
+ ao2_unlock(q);
+ ao2_unlock(queues);
+
+ return res;
+}
+
+static int play_file(struct ast_channel *chan, const char *filename)
+{
+ int res;
+
+ ast_stopstream(chan);
+
+ res = ast_streamfile(chan, filename, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+
+ ast_stopstream(chan);
+
+ return res;
+}
+
+static int valid_exit(struct queue_ent *qe, char digit)
+{
+ int digitlen = strlen(qe->digits);
+
+ /* Prevent possible buffer overflow */
+ if (digitlen < sizeof(qe->digits) - 2) {
+ qe->digits[digitlen] = digit;
+ qe->digits[digitlen + 1] = '\0';
+ } else {
+ qe->digits[0] = '\0';
+ return 0;
+ }
+
+ /* If there's no context to goto, short-circuit */
+ if (ast_strlen_zero(qe->context))
+ return 0;
+
+ /* If the extension is bad, then reset the digits to blank */
+ if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
+ qe->digits[0] = '\0';
+ return 0;
+ }
+
+ /* We have an exact match */
+ if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
+ qe->valid_digits = 1;
+ /* Return 1 on a successful goto */
+ return 1;
+ }
+
+ return 0;
+}
+
+static int say_position(struct queue_ent *qe, int ringing)
+{
+ int res = 0, avgholdmins, avgholdsecs;
+ time_t now;
+
+ /* Let minannouncefrequency seconds pass between the start of each position announcement */
+ time(&now);
+ if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
+ return 0;
+
+ /* If either our position has changed, or we are over the freq timer, say position */
+ if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
+ return 0;
+
+ if (ringing) {
+ ast_indicate(qe->chan,-1);
+ } else {
+ ast_moh_stop(qe->chan);
+ }
+ if (qe->parent->announceposition) {
+ /* Say we're next, if we are */
+ if (qe->pos == 1) {
+ res = play_file(qe->chan, qe->parent->sound_next);
+ if (res)
+ goto playout;
+ else
+ goto posout;
+ } else {
+ res = play_file(qe->chan, qe->parent->sound_thereare);
+ if (res)
+ goto playout;
+ res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
+ if (res)
+ goto playout;
+ res = play_file(qe->chan, qe->parent->sound_calls);
+ if (res)
+ goto playout;
+ }
+ }
+ /* Round hold time to nearest minute */
+ avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
+
+ /* If they have specified a rounding then round the seconds as well */
+ if (qe->parent->roundingseconds) {
+ avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
+ avgholdsecs *= qe->parent->roundingseconds;
+ } else {
+ avgholdsecs = 0;
+ }
+
+ ast_verb(3, "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
+
+ /* If the hold time is >1 min, if it's enabled, and if it's not
+ supposed to be only once and we have already said it, say it */
+ if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
+ (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
+ res = play_file(qe->chan, qe->parent->sound_holdtime);
+ if (res)
+ goto playout;
+
+ if (avgholdmins > 0) {
+ if (avgholdmins < 2) {
+ res = play_file(qe->chan, qe->parent->sound_lessthan);
+ if (res)
+ goto playout;
+
+ res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
+ if (res)
+ goto playout;
+ } else {
+ res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
+ if (res)
+ goto playout;
+ }
+
+ res = play_file(qe->chan, qe->parent->sound_minutes);
+ if (res)
+ goto playout;
+ }
+ if (avgholdsecs>0) {
+ res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
+ if (res)
+ goto playout;
+
+ res = play_file(qe->chan, qe->parent->sound_seconds);
+ if (res)
+ goto playout;
+ }
+
+ }
+
+posout:
+ if (qe->parent->announceposition) {
+ ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
+ qe->chan->name, qe->parent->name, qe->pos);
+ }
+ res = play_file(qe->chan, qe->parent->sound_thanks);
+
+playout:
+ if ((res > 0 && !valid_exit(qe, res)) || res < 0)
+ res = 0;
+
+ /* Set our last_pos indicators */
+ qe->last_pos = now;
+ qe->last_pos_said = qe->pos;
+
+ /* Don't restart music on hold if we're about to exit the caller from the queue */
+ if (!res) {
+ if (ringing)
+ ast_indicate(qe->chan, AST_CONTROL_RINGING);
+ else
+ ast_moh_start(qe->chan, qe->moh, NULL);
+ }
+ return res;
+}
+
+static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
+{
+ int oldvalue;
+
+ /* Calculate holdtime using a recursive boxcar filter */
+ /* Thanks to SRT for this contribution */
+ /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
+
+ ao2_lock(qe->parent);
+ oldvalue = qe->parent->holdtime;
+ qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
+ ao2_unlock(qe->parent);
+}
+
+
+static void leave_queue(struct queue_ent *qe)
+{
+ struct call_queue *q;
+ struct queue_ent *cur, *prev = NULL;
+ struct penalty_rule *pr_iter;
+ int pos = 0;
+
+ if (!(q = qe->parent))
+ return;
+ queue_ref(q);
+ ao2_lock(q);
+
+ prev = NULL;
+ for (cur = q->head; cur; cur = cur->next) {
+ if (cur == qe) {
+ q->count--;
+
+ /* Take us out of the queue */
+ manager_event(EVENT_FLAG_CALL, "Leave",
+ "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
+ qe->chan->name, q->name, q->count, qe->chan->uniqueid);
+ ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
+ /* Take us out of the queue */
+ if (prev)
+ prev->next = cur->next;
+ else
+ q->head = cur->next;
+ /* Free penalty rules */
+ while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
+ ast_free(pr_iter);
+ } else {
+ /* Renumber the people after us in the queue based on a new count */
+ cur->pos = ++pos;
+ prev = cur;
+ }
+ }
+ ao2_unlock(q);
+
+ /*If the queue is a realtime queue, check to see if it's still defined in real time*/
+ if (q->realtime) {
+ if (!ast_load_realtime("queues", "name", q->name, NULL))
+ q->dead = 1;
+ }
+
+ if (q->dead) {
+ /* It's dead and nobody is in it, so kill it */
+ ao2_unlink(queues, q);
+ queue_unref(q);
+ }
+ queue_unref(q);
+}
+
+/* Hang up a list of outgoing calls */
+static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
+{
+ struct callattempt *oo;
+
+ while (outgoing) {
+ /* Hangup any existing lines we have open */
+ if (outgoing->chan && (outgoing->chan != exception))
+ ast_hangup(outgoing->chan);
+ oo = outgoing;
+ outgoing = outgoing->q_next;
+ if (oo->member)
+ ao2_ref(oo->member, -1);
+ ast_free(oo);
+ }
+}
+
+static int update_status(struct call_queue *q, struct member *member, int status)
+{
+ struct member *cur;
+ struct ao2_iterator mem_iter;
+
+ /* Since a reload could have taken place, we have to traverse the list to
+ be sure it's still valid */
+ ao2_lock(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((cur = ao2_iterator_next(&mem_iter))) {
+ if (member != cur) {
+ ao2_ref(cur, -1);
+ continue;
+ }
+
+ cur->status = status;
+ if (!q->maskmemberstatus) {
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n",
+ q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static",
+ cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
+ }
+ ao2_ref(cur, -1);
+ }
+ ao2_unlock(q);
+ return 0;
+}
+
+static int update_dial_status(struct call_queue *q, struct member *member, int status)
+{
+ if (status == AST_CAUSE_BUSY)
+ status = AST_DEVICE_BUSY;
+ else if (status == AST_CAUSE_UNREGISTERED)
+ status = AST_DEVICE_UNAVAILABLE;
+ else if (status == AST_CAUSE_NOSUCHDRIVER)
+ status = AST_DEVICE_INVALID;
+ else
+ status = AST_DEVICE_UNKNOWN;
+ return update_status(q, member, status);
+}
+
+/* traverse all defined queues which have calls waiting and contain this member
+ return 0 if no other queue has precedence (higher weight) or 1 if found */
+static int compare_weight(struct call_queue *rq, struct member *member)
+{
+ struct call_queue *q;
+ struct member *mem;
+ int found = 0;
+ struct ao2_iterator queue_iter;
+
+ /* &qlock and &rq->lock already set by try_calling()
+ * to solve deadlock */
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ if (q == rq) { /* don't check myself, could deadlock */
+ queue_unref(q);
+ continue;
+ }
+ ao2_lock(q);
+ if (q->count && q->members) {
+ if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
+ ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
+ if (q->weight > rq->weight) {
+ ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
+ found = 1;
+ }
+ ao2_ref(mem, -1);
+ }
+ }
+ ao2_unlock(q);
+ if (found) {
+ queue_unref(q);
+ break;
+ }
+ queue_unref(q);
+ }
+ return found;
+}
+
+/*! \brief common hangup actions */
+static void do_hang(struct callattempt *o)
+{
+ o->stillgoing = 0;
+ ast_hangup(o->chan);
+ o->chan = NULL;
+}
+
+static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
+{
+ struct ast_str *buf = ast_str_alloca(len + 1);
+ char *tmp;
+
+ if (pbx_builtin_serialize_variables(chan, &buf)) {
+ int i, j;
+
+ /* convert "\n" to "\nVariable: " */
+ strcpy(vars, "Variable: ");
+ tmp = buf->str;
+
+ for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
+ vars[j] = tmp[i];
+
+ if (tmp[i + 1] == '\0')
+ break;
+ if (tmp[i] == '\n') {
+ vars[j++] = '\r';
+ vars[j++] = '\n';
+
+ ast_copy_string(&(vars[j]), "Variable: ", len - j);
+ j += 9;
+ }
+ }
+ if (j > len - 1)
+ j = len - 1;
+ vars[j - 2] = '\r';
+ vars[j - 1] = '\n';
+ vars[j] = '\0';
+ } else {
+ /* there are no channel variables; leave it blank */
+ *vars = '\0';
+ }
+ return vars;
+}
+
+/*! \brief Part 2 of ring_one
+ *
+ * Does error checking before attempting to request a channel and call a member. This
+ * function is only called from ring_one
+ */
+static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
+{
+ int res;
+ int status;
+ char tech[256];
+ char *location;
+
+ /* on entry here, we know that tmp->chan == NULL */
+ if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
+ (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
+ ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
+ (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ (*busies)++;
+ return 0;
+ }
+
+ if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
+ ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ return 0;
+ }
+
+ if (tmp->member->paused) {
+ ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ return 0;
+ }
+ if (use_weight && compare_weight(qe->parent,tmp->member)) {
+ ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ (*busies)++;
+ return 0;
+ }
+
+ ast_copy_string(tech, tmp->interface, sizeof(tech));
+ if ((location = strchr(tech, '/')))
+ *location++ = '\0';
+ else
+ location = "";
+
+ /* Request the peer */
+ tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
+ if (!tmp->chan) { /* If we can't, just go on to the next call */
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+ tmp->stillgoing = 0;
+ update_dial_status(qe->parent, tmp->member, status);
+
+ ao2_lock(qe->parent);
+ qe->parent->rrpos++;
+ qe->linpos++;
+ ao2_unlock(qe->parent);
+
+
+ (*busies)++;
+ return 0;
+ } else if (status != tmp->oldstatus)
+ update_dial_status(qe->parent, tmp->member, status);
+
+ tmp->chan->appl = "AppQueue";
+ tmp->chan->data = "(Outgoing Line)";
+ tmp->chan->whentohangup = 0;
+ if (tmp->chan->cid.cid_num)
+ ast_free(tmp->chan->cid.cid_num);
+ tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
+ if (tmp->chan->cid.cid_name)
+ ast_free(tmp->chan->cid.cid_name);
+ tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
+ if (tmp->chan->cid.cid_ani)
+ ast_free(tmp->chan->cid.cid_ani);
+ tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
+
+ /* Inherit specially named variables from parent channel */
+ ast_channel_inherit_variables(qe->chan, tmp->chan);
+
+ /* Presense of ADSI CPE on outgoing channel follows ours */
+ tmp->chan->adsicpe = qe->chan->adsicpe;
+
+ /* Inherit context and extension */
+ if (!ast_strlen_zero(qe->chan->macrocontext))
+ ast_copy_string(tmp->chan->dialcontext, qe->chan->macrocontext, sizeof(tmp->chan->dialcontext));
+ else
+ ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
+ if (!ast_strlen_zero(qe->chan->macroexten))
+ ast_copy_string(tmp->chan->exten, qe->chan->macroexten, sizeof(tmp->chan->exten));
+ else
+ ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
+
+ /* Place the call, but don't wait on the answer */
+ if ((res = ast_call(tmp->chan, location, 0))) {
+ /* Again, keep going even if there's an error */
+ ast_debug(1, "ast call on peer returned %d\n", res);
+ ast_verb(3, "Couldn't call %s\n", tmp->interface);
+ do_hang(tmp);
+ (*busies)++;
+ return 0;
+ } else if (qe->parent->eventwhencalled) {
+ char vars[2048];
+
+ manager_event(EVENT_FLAG_AGENT, "AgentCalled",
+ "Queue: %s\r\n"
+ "AgentCalled: %s\r\n"
+ "AgentName: %s\r\n"
+ "ChannelCalling: %s\r\n"
+ "DestinationChannel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "%s",
+ qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
+ tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
+ tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
+ qe->chan->context, qe->chan->exten, qe->chan->priority,
+ qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
+ ast_verb(3, "Called %s\n", tmp->interface);
+ }
+
+ return 1;
+}
+
+/*! \brief find the entry with the best metric, or NULL */
+static struct callattempt *find_best(struct callattempt *outgoing)
+{
+ struct callattempt *best = NULL, *cur;
+
+ for (cur = outgoing; cur; cur = cur->q_next) {
+ if (cur->stillgoing && /* Not already done */
+ !cur->chan && /* Isn't already going */
+ (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
+ best = cur;
+ }
+ }
+
+ return best;
+}
+
+/*! \brief Place a call to a queue member
+ *
+ * Once metrics have been calculated for each member, this function is used
+ * to place a call to the appropriate member (or members). The low-level
+ * channel-handling and error detection is handled in ring_entry
+ *
+ * Returns 1 if a member was called successfully, 0 otherwise
+ */
+static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
+{
+ int ret = 0;
+
+ while (ret == 0) {
+ struct callattempt *best = find_best(outgoing);
+ if (!best) {
+ ast_debug(1, "Nobody left to try ringing in queue\n");
+ break;
+ }
+ if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
+ struct callattempt *cur;
+ /* Ring everyone who shares this best metric (for ringall) */
+ for (cur = outgoing; cur; cur = cur->q_next) {
+ if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
+ ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
+ ret |= ring_entry(qe, cur, busies);
+ }
+ }
+ } else {
+ /* Ring just the best channel */
+ ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
+ ret = ring_entry(qe, best, busies);
+ }
+ }
+
+ return ret;
+}
+
+static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
+{
+ struct callattempt *best = find_best(outgoing);
+
+ if (best) {
+ /* Ring just the best channel */
+ ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
+ qe->parent->rrpos = best->metric % 1000;
+ } else {
+ /* Just increment rrpos */
+ if (qe->parent->wrapped) {
+ /* No more channels, start over */
+ qe->parent->rrpos = 0;
+ } else {
+ /* Prioritize next entry */
+ qe->parent->rrpos++;
+ }
+ }
+ qe->parent->wrapped = 0;
+
+ return 0;
+}
+
+static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
+{
+ struct callattempt *best = find_best(outgoing);
+
+ if (best) {
+ /* Ring just the best channel */
+ ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
+ qe->linpos = best->metric % 1000;
+ } else {
+ /* Just increment rrpos */
+ if (qe->linwrapped) {
+ /* No more channels, start over */
+ qe->linpos = 0;
+ } else {
+ /* Prioritize next entry */
+ qe->linpos++;
+ }
+ }
+ qe->linwrapped = 0;
+
+ return 0;
+}
+
+static int say_periodic_announcement(struct queue_ent *qe, int ringing)
+{
+ int res = 0;
+ time_t now;
+
+ /* Get the current time */
+ time(&now);
+
+ /* Check to see if it is time to announce */
+ if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
+ return 0;
+
+ /* Stop the music on hold so we can play our own file */
+ if (ringing)
+ ast_indicate(qe->chan,-1);
+ else
+ ast_moh_stop(qe->chan);
+
+ ast_verb(3, "Playing periodic announcement\n");
+
+ /* Check to make sure we have a sound file. If not, reset to the first sound file */
+ if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS ||
+ !qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound] ||
+ ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
+ qe->last_periodic_announce_sound = 0;
+ }
+
+ /* play the announcement */
+ res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
+
+ if ((res > 0 && !valid_exit(qe, res)) || res < 0)
+ res = 0;
+
+ /* Resume Music on Hold if the caller is going to stay in the queue */
+ if (!res) {
+ if (ringing)
+ ast_indicate(qe->chan, AST_CONTROL_RINGING);
+ else
+ ast_moh_start(qe->chan, qe->moh, NULL);
+ }
+
+ /* update last_periodic_announce_time */
+ qe->last_periodic_announce_time = now;
+
+ /* Update the current periodic announcement to the next announcement */
+ qe->last_periodic_announce_sound++;
+
+ return res;
+}
+
+static void record_abandoned(struct queue_ent *qe)
+{
+ ao2_lock(qe->parent);
+ set_queue_variables(qe);
+ manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Position: %d\r\n"
+ "OriginalPosition: %d\r\n"
+ "HoldTime: %d\r\n",
+ qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
+
+ qe->parent->callsabandoned++;
+ ao2_unlock(qe->parent);
+}
+
+/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
+static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
+{
+ ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
+ ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
+ if (qe->parent->autopause) {
+ if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
+ ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
+ } else {
+ ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
+ }
+ }
+ return;
+}
+
+#define AST_MAX_WATCHERS 256
+/*! \brief Wait for a member to answer the call
+ *
+ * \param[in] qe the queue_ent corresponding to the caller in the queue
+ * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
+ * \param[in] to the amount of time (in milliseconds) to wait for a response
+ * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
+ * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
+ * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
+ * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
+ */
+static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
+{
+ const char *queue = qe->parent->name;
+ struct callattempt *o, *start = NULL, *prev = NULL;
+ int status;
+ int numbusies = prebusies;
+ int numnochan = 0;
+ int stillgoing = 0;
+ int orig = *to;
+ struct ast_frame *f;
+ struct callattempt *peer = NULL;
+ struct ast_channel *winner;
+ struct ast_channel *in = qe->chan;
+ char on[80] = "";
+ char membername[80] = "";
+ long starttime = 0;
+ long endtime = 0;
+#ifdef HAVE_EPOLL
+ struct callattempt *epollo;
+#endif
+
+ starttime = (long) time(NULL);
+#ifdef HAVE_EPOLL
+ for (epollo = outgoing; epollo; epollo = epollo->q_next) {
+ if (epollo->chan)
+ ast_poll_channel_add(in, epollo->chan);
+ }
+#endif
+
+ while (*to && !peer) {
+ int numlines, retry, pos = 1;
+ struct ast_channel *watchers[AST_MAX_WATCHERS];
+ watchers[0] = in;
+ start = NULL;
+
+ for (retry = 0; retry < 2; retry++) {
+ numlines = 0;
+ for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
+ if (o->stillgoing) { /* Keep track of important channels */
+ stillgoing = 1;
+ if (o->chan) {
+ watchers[pos++] = o->chan;
+ if (!start)
+ start = o;
+ else
+ prev->call_next = o;
+ prev = o;
+ }
+ }
+ numlines++;
+ }
+ if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
+ (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
+ break;
+ /* On "ringall" strategy we only move to the next penalty level
+ when *all* ringing phones are done in the current penalty level */
+ ring_one(qe, outgoing, &numbusies);
+ /* and retry... */
+ }
+ if (pos == 1 /* not found */) {
+ if (numlines == (numbusies + numnochan)) {
+ ast_debug(1, "Everyone is busy at this time\n");
+ } else {
+ ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
+ }
+ *to = 0;
+ return NULL;
+ }
+ winner = ast_waitfor_n(watchers, pos, to);
+ for (o = start; o; o = o->call_next) {
+ if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
+ if (!peer) {
+ ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
+ peer = o;
+ }
+ } else if (o->chan && (o->chan == winner)) {
+
+ ast_copy_string(on, o->member->interface, sizeof(on));
+ ast_copy_string(membername, o->member->membername, sizeof(membername));
+
+ if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
+ ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
+ numnochan++;
+ do_hang(o);
+ winner = NULL;
+ continue;
+ } else if (!ast_strlen_zero(o->chan->call_forward)) {
+ char tmpchan[256];
+ char *stuff;
+ char *tech;
+
+ ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
+ if ((stuff = strchr(tmpchan, '/'))) {
+ *stuff++ = '\0';
+ tech = tmpchan;
+ } else {
+ snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
+ stuff = tmpchan;
+ tech = "Local";
+ }
+ /* Before processing channel, go ahead and check for forwarding */
+ ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
+ /* Setup parameters */
+ o->chan = ast_request(tech, in->nativeformats, stuff, &status);
+ if (status != o->oldstatus)
+ update_dial_status(qe->parent, o->member, status);
+ if (!o->chan) {
+ ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
+ o->stillgoing = 0;
+ numnochan++;
+ } else {
+ ast_channel_inherit_variables(in, o->chan);
+ ast_channel_datastore_inherit(in, o->chan);
+ if (o->chan->cid.cid_num)
+ ast_free(o->chan->cid.cid_num);
+ o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
+
+ if (o->chan->cid.cid_name)
+ ast_free(o->chan->cid.cid_name);
+ o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
+
+ ast_string_field_set(o->chan, accountcode, in->accountcode);
+ o->chan->cdrflags = in->cdrflags;
+
+ if (in->cid.cid_ani) {
+ if (o->chan->cid.cid_ani)
+ ast_free(o->chan->cid.cid_ani);
+ o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
+ }
+ if (o->chan->cid.cid_rdnis)
+ ast_free(o->chan->cid.cid_rdnis);
+ o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
+ if (ast_call(o->chan, tmpchan, 0)) {
+ ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
+ do_hang(o);
+ numnochan++;
+ }
+ }
+ /* Hangup the original channel now, in case we needed it */
+ ast_hangup(winner);
+ continue;
+ }
+ f = ast_read(winner);
+ if (f) {
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch (f->subclass) {
+ case AST_CONTROL_ANSWER:
+ /* This is our guy if someone answered. */
+ if (!peer) {
+ ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
+ peer = o;
+ }
+ break;
+ case AST_CONTROL_BUSY:
+ ast_verb(3, "%s is busy\n", o->chan->name);
+ if (in->cdr)
+ ast_cdr_busy(in->cdr);
+ do_hang(o);
+ endtime = (long) time(NULL);
+ endtime -= starttime;
+ rna(endtime*1000, qe, on, membername);
+ if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
+ if (qe->parent->timeoutrestart)
+ *to = orig;
+ ring_one(qe, outgoing, &numbusies);
+ }
+ numbusies++;
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(3, "%s is circuit-busy\n", o->chan->name);
+ if (in->cdr)
+ ast_cdr_busy(in->cdr);
+ endtime = (long) time(NULL);
+ endtime -= starttime;
+ rna(endtime*1000, qe, on, membername);
+ do_hang(o);
+ if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
+ if (qe->parent->timeoutrestart)
+ *to = orig;
+ ring_one(qe, outgoing, &numbusies);
+ }
+ numbusies++;
+ break;
+ case AST_CONTROL_RINGING:
+ ast_verb(3, "%s is ringing\n", o->chan->name);
+ break;
+ case AST_CONTROL_OFFHOOK:
+ /* Ignore going off hook */
+ break;
+ default:
+ ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
+ }
+ }
+ ast_frfree(f);
+ } else {
+ endtime = (long) time(NULL) - starttime;
+ rna(endtime * 1000, qe, on, membername);
+ do_hang(o);
+ if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
+ if (qe->parent->timeoutrestart)
+ *to = orig;
+ ring_one(qe, outgoing, &numbusies);
+ }
+ }
+ }
+ }
+ if (winner == in) {
+ f = ast_read(in);
+ if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
+ /* Got hung up */
+ *to = -1;
+ if (f) {
+ ast_frfree(f);
+ }
+ return NULL;
+ }
+ if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
+ ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
+ *to = 0;
+ ast_frfree(f);
+ return NULL;
+ }
+ if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
+ ast_verb(3, "User pressed digit: %c\n", f->subclass);
+ *to = 0;
+ *digit = f->subclass;
+ ast_frfree(f);
+ return NULL;
+ }
+ ast_frfree(f);
+ }
+ if (!*to) {
+ for (o = start; o; o = o->call_next)
+ rna(orig, qe, o->interface, o->member->membername);
+ }
+ }
+
+#ifdef HAVE_EPOLL
+ for (epollo = outgoing; epollo; epollo = epollo->q_next) {
+ if (epollo->chan)
+ ast_poll_channel_del(in, epollo->chan);
+ }
+#endif
+
+ return peer;
+}
+/*! \brief Check if we should start attempting to call queue members
+ *
+ * The behavior of this function is dependent first on whether autofill is enabled
+ * and second on whether the ring strategy is ringall. If autofill is not enabled,
+ * then return true if we're the head of the queue. If autofill is enabled, then
+ * we count the available members and see if the number of available members is enough
+ * that given our position in the queue, we would theoretically be able to connect to
+ * one of those available members
+ */
+static int is_our_turn(struct queue_ent *qe)
+{
+ struct queue_ent *ch;
+ struct member *cur;
+ int avl = 0;
+ int idx = 0;
+ int res;
+
+ if (!qe->parent->autofill) {
+ /* Atomically read the parent head -- does not need a lock */
+ ch = qe->parent->head;
+ /* If we are now at the top of the head, break out */
+ if (ch == qe) {
+ ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
+ res = 1;
+ } else {
+ ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
+ res = 0;
+ }
+
+ } else {
+ /* This needs a lock. How many members are available to be served? */
+ ao2_lock(qe->parent);
+
+ ch = qe->parent->head;
+
+ if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
+ ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
+ avl = 1;
+ } else {
+ struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
+ while ((cur = ao2_iterator_next(&mem_iter))) {
+ switch (cur->status) {
+ case AST_DEVICE_NOT_INUSE:
+ case AST_DEVICE_UNKNOWN:
+ if (!cur->paused)
+ avl++;
+ break;
+ }
+ ao2_ref(cur, -1);
+ }
+ }
+
+ ast_debug(1, "There are %d available members.\n", avl);
+
+ while ((idx < avl) && (ch) && !ch->pending && (ch != qe)) {
+ idx++;
+ ch = ch->next;
+ }
+
+ /* If the queue entry is within avl [the number of available members] calls from the top ... */
+ if (ch && idx < avl) {
+ ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
+ res = 1;
+ } else {
+ ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
+ res = 0;
+ }
+
+ ao2_unlock(qe->parent);
+ }
+
+ return res;
+}
+static void update_qe_rule(struct queue_ent *qe)
+{
+ int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
+ int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
+ char max_penalty_str[20], min_penalty_str[20];
+ /* a relative change to the penalty could put it below 0 */
+ if (max_penalty < 0)
+ max_penalty = 0;
+ if (min_penalty < 0)
+ min_penalty = 0;
+ if (min_penalty > max_penalty)
+ min_penalty = max_penalty;
+ snprintf(max_penalty_str, sizeof(max_penalty_str) - 1, "%d", max_penalty);
+ snprintf(min_penalty_str, sizeof(min_penalty_str) - 1, "%d", min_penalty);
+ pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
+ pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
+ qe->max_penalty = max_penalty;
+ qe->min_penalty = min_penalty;
+ ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
+ qe->pr = AST_LIST_NEXT(qe->pr, list);
+}
+
+/*! \brief The waiting areas for callers who are not actively calling members
+ *
+ * This function is one large loop. This function will return if a caller
+ * either exits the queue or it becomes that caller's turn to attempt calling
+ * queue members. Inside the loop, we service the caller with periodic announcements,
+ * holdtime announcements, etc. as configured in queues.conf
+ *
+ * \retval 0 if the caller's turn has arrived
+ * \retval -1 if the caller should exit the queue.
+ */
+static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
+{
+ int res = 0;
+
+ /* This is the holding pen for callers 2 through maxlen */
+ for (;;) {
+ enum queue_member_status stat;
+
+ if (is_our_turn(qe))
+ break;
+
+ /* If we have timed out, break out */
+ if (qe->expire && (time(NULL) > qe->expire)) {
+ *reason = QUEUE_TIMEOUT;
+ break;
+ }
+
+ stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
+
+ /* leave the queue if no agents, if enabled */
+ if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
+ *reason = QUEUE_LEAVEEMPTY;
+ ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
+ leave_queue(qe);
+ break;
+ }
+
+ /* leave the queue if no reachable agents, if enabled */
+ if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
+ *reason = QUEUE_LEAVEUNAVAIL;
+ ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
+ leave_queue(qe);
+ break;
+ }
+ if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
+ *reason = QUEUE_LEAVEUNAVAIL;
+ ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
+ leave_queue(qe);
+ break;
+ }
+
+ /* Make a position announcement, if enabled */
+ if (qe->parent->announcefrequency &&
+ (res = say_position(qe,ringing)))
+ break;
+
+ /* Make a periodic announcement, if enabled */
+ if (qe->parent->periodicannouncefrequency &&
+ (res = say_periodic_announcement(qe,ringing)))
+ break;
+
+ /* see if we need to move to the next penalty level for this queue */
+ while (qe->pr && ((time(NULL) - qe->start) > qe->pr->time)) {
+ update_qe_rule(qe);
+ }
+
+ /* Wait a second before checking again */
+ if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
+ if (res > 0 && !valid_exit(qe, res))
+ res = 0;
+ else
+ break;
+ }
+ }
+
+ return res;
+}
+
+static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
+{
+ struct member *mem;
+ struct call_queue *qtmp;
+ struct ao2_iterator queue_iter;
+
+ if (shared_lastcall) {
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((qtmp = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(qtmp);
+ if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
+ time(&mem->lastcall);
+ mem->calls++;
+ mem->lastqueue = q;
+ ao2_ref(mem, -1);
+ }
+ ao2_unlock(qtmp);
+ ao2_ref(qtmp, -1);
+ }
+ } else {
+ ao2_lock(q);
+ time(&member->lastcall);
+ member->calls++;
+ member->lastqueue = q;
+ ao2_unlock(q);
+ }
+ ao2_lock(q);
+ q->callscompleted++;
+ if (callcompletedinsl)
+ q->callscompletedinsl++;
+ ao2_unlock(q);
+ return 0;
+}
+
+/*! \brief Calculate the metric of each member in the outgoing callattempts
+ *
+ * A numeric metric is given to each member depending on the ring strategy used
+ * by the queue. Members with lower metrics will be called before members with
+ * higher metrics
+ */
+static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
+{
+ if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
+ return -1;
+
+ switch (q->strategy) {
+ case QUEUE_STRATEGY_RINGALL:
+ /* Everyone equal, except for penalty */
+ tmp->metric = mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_LINEAR:
+ if (pos < qe->linpos) {
+ tmp->metric = 1000 + pos;
+ } else {
+ if (pos > qe->linpos)
+ /* Indicate there is another priority */
+ qe->linwrapped = 1;
+ tmp->metric = pos;
+ }
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_RRMEMORY:
+ if (pos < q->rrpos) {
+ tmp->metric = 1000 + pos;
+ } else {
+ if (pos > q->rrpos)
+ /* Indicate there is another priority */
+ q->wrapped = 1;
+ tmp->metric = pos;
+ }
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_RANDOM:
+ tmp->metric = ast_random() % 1000;
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_WRANDOM:
+ tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
+ break;
+ case QUEUE_STRATEGY_FEWESTCALLS:
+ tmp->metric = mem->calls;
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ case QUEUE_STRATEGY_LEASTRECENT:
+ if (!mem->lastcall)
+ tmp->metric = 0;
+ else
+ tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
+ tmp->metric += mem->penalty * 1000000;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
+ break;
+ }
+ return 0;
+}
+
+enum agent_complete_reason {
+ CALLER,
+ AGENT,
+ TRANSFER
+};
+
+static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
+ const struct ast_channel *peer, const struct member *member, time_t callstart,
+ char *vars, size_t vars_len, enum agent_complete_reason rsn)
+{
+ const char *reason = NULL; /* silence dumb compilers */
+
+ if (!qe->parent->eventwhencalled)
+ return;
+
+ switch (rsn) {
+ case CALLER:
+ reason = "caller";
+ break;
+ case AGENT:
+ reason = "agent";
+ break;
+ case TRANSFER:
+ reason = "transfer";
+ break;
+ }
+
+ manager_event(EVENT_FLAG_AGENT, "AgentComplete",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Channel: %s\r\n"
+ "Member: %s\r\n"
+ "MemberName: %s\r\n"
+ "HoldTime: %ld\r\n"
+ "TalkTime: %ld\r\n"
+ "Reason: %s\r\n"
+ "%s",
+ queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
+ (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
+ qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
+}
+/*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
+ *
+ * Here is the process of this function
+ * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
+ * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
+ * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
+ * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
+ * during each iteration, we call calc_metric to determine which members should be rung when.
+ * 3. Call ring_one to place a call to the appropriate member(s)
+ * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
+ * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
+ * 6. Start the monitor or mixmonitor if the option is set
+ * 7. Remove the caller from the queue to allow other callers to advance
+ * 8. Bridge the call.
+ * 9. Do any post processing after the call has disconnected.
+ *
+ * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
+ * \param[in] options the options passed as the third parameter to the Queue() application
+ * \param[in] url the url passed as the fourth parameter to the Queue() application
+ * \param[in,out] tries the number of times we have tried calling queue members
+ * \param[out] noption set if the call to Queue() has the 'n' option set.
+ * \param[in] agi the agi passed as the fifth parameter to the Queue() application
+ * \param[in] macro the macro passed as the sixth parameter to the Queue() application
+ * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
+ * \param[in] ringing 1 if the 'r' option is set, otherwise 0
+ */
+static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
+{
+ struct member *cur;
+ struct callattempt *outgoing = NULL; /* the list of calls we are building */
+ int to, orig;
+ char oldexten[AST_MAX_EXTENSION]="";
+ char oldcontext[AST_MAX_CONTEXT]="";
+ char queuename[256]="";
+ char interfacevar[256]="";
+ struct ast_channel *peer;
+ struct ast_channel *which;
+ struct callattempt *lpeer;
+ struct member *member;
+ struct ast_app *app;
+ int res = 0, bridge = 0;
+ int numbusies = 0;
+ int x=0;
+ char *announce = NULL;
+ char digit = 0;
+ time_t callstart;
+ time_t now = time(NULL);
+ struct ast_bridge_config bridge_config;
+ char nondataquality = 1;
+ char *agiexec = NULL;
+ char *macroexec = NULL;
+ char *gosubexec = NULL;
+ int ret = 0;
+ const char *monitorfilename;
+ const char *monitor_exec;
+ const char *monitor_options;
+ char tmpid[256], tmpid2[256];
+ char meid[1024], meid2[1024];
+ char mixmonargs[1512];
+ struct ast_app *mixmonapp = NULL;
+ char *p;
+ char vars[2048];
+ int forwardsallowed = 1;
+ int callcompletedinsl;
+ struct ao2_iterator memi;
+ struct ast_datastore *datastore;
+
+ ast_channel_lock(qe->chan);
+ datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
+ ast_channel_unlock(qe->chan);
+
+ memset(&bridge_config, 0, sizeof(bridge_config));
+ tmpid[0] = 0;
+ meid[0] = 0;
+ time(&now);
+
+ for (; options && *options; options++)
+ switch (*options) {
+ case 't':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
+ break;
+ case 'T':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
+ break;
+ case 'w':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
+ break;
+ case 'W':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
+ break;
+ case 'd':
+ nondataquality = 0;
+ break;
+ case 'h':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
+ break;
+ case 'H':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
+ break;
+ case 'k':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
+ break;
+ case 'K':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
+ break;
+ case 'n':
+ if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
+ (*tries)++;
+ else
+ *tries = qe->parent->membercount;
+ *noption = 1;
+ break;
+ case 'i':
+ forwardsallowed = 0;
+ break;
+ case 'x':
+ ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
+ break;
+ case 'X':
+ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
+ break;
+
+ }
+
+ /* Hold the lock while we setup the outgoing calls */
+ if (use_weight)
+ ao2_lock(queues);
+ ao2_lock(qe->parent);
+ ast_debug(1, "%s is trying to call a queue member.\n",
+ qe->chan->name);
+ ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
+ if (!ast_strlen_zero(qe->announce))
+ announce = qe->announce;
+ if (!ast_strlen_zero(announceoverride))
+ announce = announceoverride;
+
+ memi = ao2_iterator_init(qe->parent->members, 0);
+ while ((cur = ao2_iterator_next(&memi))) {
+ struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
+ struct ast_dialed_interface *di;
+ AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
+ if (!tmp) {
+ ao2_ref(cur, -1);
+ ao2_unlock(qe->parent);
+ if (use_weight)
+ ao2_unlock(queues);
+ goto out;
+ }
+ if (!datastore) {
+ if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
+ ao2_ref(cur, -1);
+ ao2_unlock(qe->parent);
+ if (use_weight)
+ ao2_unlock(queues);
+ free(tmp);
+ goto out;
+ }
+ datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+ if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
+ ao2_ref(cur, -1);
+ ao2_unlock(&qe->parent);
+ if (use_weight)
+ ao2_unlock(queues);
+ free(tmp);
+ goto out;
+ }
+ datastore->data = dialed_interfaces;
+ AST_LIST_HEAD_INIT(dialed_interfaces);
+
+ ast_channel_lock(qe->chan);
+ ast_channel_datastore_add(qe->chan, datastore);
+ ast_channel_unlock(qe->chan);
+ } else
+ dialed_interfaces = datastore->data;
+
+ AST_LIST_LOCK(dialed_interfaces);
+ AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
+ if (!strcasecmp(cur->interface, di->interface)) {
+ ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
+ di->interface);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(dialed_interfaces);
+
+ if (di) {
+ free(tmp);
+ continue;
+ }
+
+ /* It is always ok to dial a Local interface. We only keep track of
+ * which "real" interfaces have been dialed. The Local channel will
+ * inherit this list so that if it ends up dialing a real interface,
+ * it won't call one that has already been called. */
+ if (strncasecmp(cur->interface, "Local/", 6)) {
+ if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
+ ao2_ref(cur, -1);
+ ao2_unlock(qe->parent);
+ if (use_weight)
+ ao2_unlock(queues);
+ free(tmp);
+ goto out;
+ }
+ strcpy(di->interface, cur->interface);
+
+ AST_LIST_LOCK(dialed_interfaces);
+ AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
+ AST_LIST_UNLOCK(dialed_interfaces);
+ }
+
+ tmp->stillgoing = -1;
+ tmp->member = cur;
+ tmp->oldstatus = cur->status;
+ tmp->lastcall = cur->lastcall;
+ tmp->lastqueue = cur->lastqueue;
+ ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
+ /* Special case: If we ring everyone, go ahead and ring them, otherwise
+ just calculate their metric for the appropriate strategy */
+ if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
+ /* Put them in the list of outgoing thingies... We're ready now.
+ XXX If we're forcibly removed, these outgoing calls won't get
+ hung up XXX */
+ tmp->q_next = outgoing;
+ outgoing = tmp;
+ /* If this line is up, don't try anybody else */
+ if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
+ break;
+ } else {
+ ao2_ref(cur, -1);
+ ast_free(tmp);
+ }
+ }
+ if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
+ to = (qe->expire - now) * 1000;
+ else
+ to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
+ orig = to;
+ ++qe->pending;
+ ring_one(qe, outgoing, &numbusies);
+ ao2_unlock(qe->parent);
+ if (use_weight)
+ ao2_unlock(queues);
+ lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
+ if (datastore) {
+ ast_channel_datastore_remove(qe->chan, datastore);
+ ast_channel_datastore_free(datastore);
+ }
+ ao2_lock(qe->parent);
+ if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
+ store_next_rr(qe, outgoing);
+ }
+ if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
+ store_next_lin(qe, outgoing);
+ }
+ ao2_unlock(qe->parent);
+ peer = lpeer ? lpeer->chan : NULL;
+ if (!peer) {
+ qe->pending = 0;
+ if (to) {
+ /* Must gotten hung up */
+ res = -1;
+ } else {
+ /* User exited by pressing a digit */
+ res = digit;
+ }
+ if (res == -1)
+ ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
+ } else { /* peer is valid */
+ /* Ah ha! Someone answered within the desired timeframe. Of course after this
+ we will always return with -1 so that it is hung up properly after the
+ conversation. */
+ qe->handled++;
+ if (!strcmp(qe->chan->tech->type, "Zap"))
+ ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
+ if (!strcmp(peer->tech->type, "Zap"))
+ ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
+ /* Update parameters for the queue */
+ time(&now);
+ recalc_holdtime(qe, (now - qe->start));
+ ao2_lock(qe->parent);
+ callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
+ ao2_unlock(qe->parent);
+ member = lpeer->member;
+ /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
+ ao2_ref(member, 1);
+ hangupcalls(outgoing, peer);
+ outgoing = NULL;
+ if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
+ int res2;
+
+ res2 = ast_autoservice_start(qe->chan);
+ if (!res2) {
+ if (qe->parent->memberdelay) {
+ ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
+ res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
+ }
+ if (!res2 && announce) {
+ play_file(peer, announce);
+ }
+ if (!res2 && qe->parent->reportholdtime) {
+ if (!play_file(peer, qe->parent->sound_reporthold)) {
+ int holdtime;
+
+ time(&now);
+ holdtime = abs((now - qe->start) / 60);
+ if (holdtime < 2) {
+ play_file(peer, qe->parent->sound_lessthan);
+ ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
+ } else
+ ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
+ play_file(peer, qe->parent->sound_minutes);
+ }
+ }
+ }
+ res2 |= ast_autoservice_stop(qe->chan);
+ if (ast_check_hangup(peer)) {
+ /* Agent must have hung up */
+ ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
+ record_abandoned(qe);
+ if (qe->parent->eventwhencalled)
+ manager_event(EVENT_FLAG_AGENT, "AgentDump",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Channel: %s\r\n"
+ "Member: %s\r\n"
+ "MemberName: %s\r\n"
+ "%s",
+ queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
+ qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
+ ast_hangup(peer);
+ ao2_ref(member, -1);
+ goto out;
+ } else if (res2) {
+ /* Caller must have hung up just before being connected*/
+ ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
+ record_abandoned(qe);
+ ast_hangup(peer);
+ ao2_ref(member, -1);
+ return -1;
+ }
+ }
+ /* Stop music on hold */
+ if (ringing)
+ ast_indicate(qe->chan,-1);
+ else
+ ast_moh_stop(qe->chan);
+ /* If appropriate, log that we have a destination channel */
+ if (qe->chan->cdr)
+ ast_cdr_setdestchan(qe->chan->cdr, peer->name);
+ /* Make sure channels are compatible */
+ res = ast_channel_make_compatible(qe->chan, peer);
+ if (res < 0) {
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
+ ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
+ record_abandoned(qe);
+ ast_hangup(peer);
+ ao2_ref(member, -1);
+ return -1;
+ }
+
+ /* Play announcement to the caller telling it's his turn if defined */
+ if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
+ if (play_file(qe->chan, qe->parent->sound_callerannounce))
+ ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
+ }
+
+ ao2_lock(qe->parent);
+ /* if setinterfacevar is defined, make member variables available to the channel */
+ /* use pbx_builtin_setvar to set a load of variables with one call */
+ if (qe->parent->setinterfacevar) {
+ snprintf(interfacevar,sizeof(interfacevar), "MEMBERINTERFACE=%s|MEMBERNAME=%s|MEMBERCALLS=%d|MEMBERLASTCALL=%ld|MEMBERPENALTY=%d|MEMBERDYNAMIC=%d|MEMBERREALTIME=%d",
+ member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
+ pbx_builtin_setvar(qe->chan, interfacevar);
+ }
+
+ /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
+ /* use pbx_builtin_setvar to set a load of variables with one call */
+ if (qe->parent->setqueueentryvar) {
+ snprintf(interfacevar,sizeof(interfacevar), "QEHOLDTIME=%ld|QEORIGINALPOS=%d",
+ (long) time(NULL) - qe->start, qe->opos);
+ pbx_builtin_setvar(qe->chan, interfacevar);
+ }
+
+ /* try to set queue variables if configured to do so*/
+ set_queue_variables(qe);
+ ao2_unlock(qe->parent);
+
+ /* Begin Monitoring */
+ if (qe->parent->monfmt && *qe->parent->monfmt) {
+ if (!qe->parent->montype) {
+ ast_debug(1, "Starting Monitor as requested.\n");
+ monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
+ if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
+ which = qe->chan;
+ else
+ which = peer;
+ if (monitorfilename)
+ ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
+ else if (qe->chan->cdr)
+ ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
+ else {
+ /* Last ditch effort -- no CDR, make up something */
+ snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
+ ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
+ }
+ } else {
+ ast_debug(1, "Starting MixMonitor as requested.\n");
+ monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
+ if (!monitorfilename) {
+ if (qe->chan->cdr)
+ ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
+ else
+ snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
+ } else {
+ const char *m = monitorfilename;
+ for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
+ switch (*m) {
+ case '^':
+ if (*(m + 1) == '{')
+ *p = '$';
+ break;
+ case ',':
+ *p++ = '\\';
+ /* Fall through */
+ default:
+ *p = *m;
+ }
+ if (*m == '\0')
+ break;
+ }
+ if (p == tmpid2 + sizeof(tmpid2))
+ tmpid2[sizeof(tmpid2) - 1] = '\0';
+
+ pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
+ }
+
+ monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
+ monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
+
+ if (monitor_exec) {
+ const char *m = monitor_exec;
+ for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
+ switch (*m) {
+ case '^':
+ if (*(m + 1) == '{')
+ *p = '$';
+ break;
+ case ',':
+ *p++ = '\\';
+ /* Fall through */
+ default:
+ *p = *m;
+ }
+ if (*m == '\0')
+ break;
+ }
+ if (p == meid2 + sizeof(meid2))
+ meid2[sizeof(meid2) - 1] = '\0';
+
+ pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
+ }
+
+ snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
+
+ mixmonapp = pbx_findapp("MixMonitor");
+
+ if (!monitor_options)
+ monitor_options = "";
+
+ if (mixmonapp) {
+ if (!ast_strlen_zero(monitor_exec))
+ snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
+ else
+ snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
+
+ ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
+ /* We purposely lock the CDR so that pbx_exec does not update the application data */
+ if (qe->chan->cdr)
+ ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
+ ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
+ if (qe->chan->cdr)
+ ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
+
+ } else
+ ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
+
+ }
+ }
+ /* Drop out of the queue at this point, to prepare for next caller */
+ leave_queue(qe);
+ if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
+ ast_debug(1, "app_queue: sendurl=%s.\n", url);
+ ast_channel_sendurl(peer, url);
+ }
+
+ /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
+ /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
+ if (!ast_strlen_zero(macro)) {
+ macroexec = ast_strdupa(macro);
+ } else {
+ if (qe->parent->membermacro)
+ macroexec = ast_strdupa(qe->parent->membermacro);
+ }
+
+ if (!ast_strlen_zero(macroexec)) {
+ ast_debug(1, "app_queue: macro=%s.\n", macroexec);
+
+ res = ast_autoservice_start(qe->chan);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
+ res = -1;
+ }
+
+ app = pbx_findapp("Macro");
+
+ if (app) {
+ res = pbx_exec(qe->chan, app, macroexec);
+ ast_debug(1, "Macro exited with status %d\n", res);
+ res = 0;
+ } else {
+ ast_log(LOG_ERROR, "Could not find application Macro\n");
+ res = -1;
+ }
+
+ if (ast_autoservice_stop(qe->chan) < 0) {
+ ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
+ res = -1;
+ }
+ }
+
+ /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
+ /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
+ if (!ast_strlen_zero(gosub)) {
+ gosubexec = ast_strdupa(gosub);
+ } else {
+ if (qe->parent->membergosub)
+ gosubexec = ast_strdupa(qe->parent->membergosub);
+ }
+
+ if (!ast_strlen_zero(gosubexec)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
+
+ res = ast_autoservice_start(qe->chan);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
+ res = -1;
+ }
+
+ app = pbx_findapp("Gosub");
+
+ if (app) {
+ char *gosub_args, *gosub_argstart;
+
+ /* Set where we came from */
+ ast_copy_string(qe->chan->context, "app_dial_gosub_virtual_context", sizeof(qe->chan->context));
+ ast_copy_string(qe->chan->exten, "s", sizeof(qe->chan->exten));
+ qe->chan->priority = 0;
+
+ gosub_argstart = strchr(gosubexec, ',');
+ if (gosub_argstart) {
+ *gosub_argstart = 0;
+ asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1);
+ *gosub_argstart = '|';
+ } else {
+ asprintf(&gosub_args, "%s,s,1", gosubexec);
+ }
+ if (gosub_args) {
+ res = pbx_exec(qe->chan, app, gosub_args);
+ ast_pbx_run(qe->chan);
+ free(gosub_args);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
+ } else
+ ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
+
+ res = 0;
+ } else {
+ ast_log(LOG_ERROR, "Could not find application Gosub\n");
+ res = -1;
+ }
+
+ if (ast_autoservice_stop(qe->chan) < 0) {
+ ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
+ res = -1;
+ }
+ }
+
+ if (!ast_strlen_zero(agi)) {
+ ast_debug(1, "app_queue: agi=%s.\n", agi);
+ app = pbx_findapp("agi");
+ if (app) {
+ agiexec = ast_strdupa(agi);
+ ret = pbx_exec(qe->chan, app, agiexec);
+ } else
+ ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
+ }
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
+ (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
+ if (update_cdr && qe->chan->cdr)
+ ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
+ if (qe->parent->eventwhencalled)
+ manager_event(EVENT_FLAG_AGENT, "AgentConnect",
+ "Queue: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Channel: %s\r\n"
+ "Member: %s\r\n"
+ "MemberName: %s\r\n"
+ "Holdtime: %ld\r\n"
+ "BridgedChannel: %s\r\n"
+ "Ringtime: %ld\r\n"
+ "%s",
+ queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
+ (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
+ qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
+ ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
+ ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
+ time(&callstart);
+
+ bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
+
+ if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
+ qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
+ (long) (time(NULL) - callstart));
+ send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
+ } else if (ast_check_hangup(qe->chan)) {
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
+ (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
+ send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
+ } else {
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
+ (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
+ send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
+ }
+
+ if (bridge != AST_PBX_NO_HANGUP_PEER)
+ ast_hangup(peer);
+ update_queue(qe->parent, member, callcompletedinsl);
+ res = bridge ? bridge : 1;
+ ao2_ref(member, -1);
+ }
+out:
+ hangupcalls(outgoing, NULL);
+
+ return res;
+}
+
+static int wait_a_bit(struct queue_ent *qe)
+{
+ /* Don't need to hold the lock while we setup the outgoing calls */
+ int retrywait = qe->parent->retry * 1000;
+
+ int res = ast_waitfordigit(qe->chan, retrywait);
+ if (res > 0 && !valid_exit(qe, res))
+ res = 0;
+
+ return res;
+}
+
+static struct member *interface_exists(struct call_queue *q, const char *interface)
+{
+ struct member *mem;
+ struct ao2_iterator mem_iter;
+
+ if (!q)
+ return NULL;
+
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((mem = ao2_iterator_next(&mem_iter))) {
+ if (!strcasecmp(interface, mem->interface))
+ return mem;
+ ao2_ref(mem, -1);
+ }
+
+ return NULL;
+}
+
+
+/* Dump all members in a specific queue to the database
+ *
+ * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
+ *
+ */
+static void dump_queue_members(struct call_queue *pm_queue)
+{
+ struct member *cur_member;
+ char value[PM_MAX_LEN];
+ int value_len = 0;
+ int res;
+ struct ao2_iterator mem_iter;
+
+ memset(value, 0, sizeof(value));
+
+ if (!pm_queue)
+ return;
+
+ mem_iter = ao2_iterator_init(pm_queue->members, 0);
+ while ((cur_member = ao2_iterator_next(&mem_iter))) {
+ if (!cur_member->dynamic) {
+ ao2_ref(cur_member, -1);
+ continue;
+ }
+
+ res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
+ value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
+
+ ao2_ref(cur_member, -1);
+
+ if (res != strlen(value + value_len)) {
+ ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
+ break;
+ }
+ value_len += res;
+ }
+
+ if (value_len && !cur_member) {
+ if (ast_db_put(pm_family, pm_queue->name, value))
+ ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
+ } else
+ /* Delete the entry if the queue is empty or there is an error */
+ ast_db_del(pm_family, pm_queue->name);
+}
+
+static int remove_from_queue(const char *queuename, const char *interface)
+{
+ struct call_queue *q, tmpq = {
+ .name = queuename,
+ };
+ struct member *mem, tmpmem;
+ int res = RES_NOSUCHQUEUE;
+
+ ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
+ if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
+ ao2_lock(q);
+ if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
+ /* XXX future changes should beware of this assumption!! */
+ if (!mem->dynamic) {
+ ao2_ref(mem, -1);
+ ao2_unlock(q);
+ return RES_NOT_DYNAMIC;
+ }
+ q->membercount--;
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n",
+ q->name, mem->interface, mem->membername);
+ ao2_unlink(q->members, mem);
+ remove_from_interfaces(mem->state_interface);
+ ao2_ref(mem, -1);
+
+ if (queue_persistent_members)
+ dump_queue_members(q);
+
+ res = RES_OKAY;
+ } else {
+ res = RES_EXISTS;
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+
+ return res;
+}
+
+
+static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
+{
+ struct call_queue *q;
+ struct member *new_member, *old_member;
+ int res = RES_NOSUCHQUEUE;
+
+ /* \note Ensure the appropriate realtime queue is loaded. Note that this
+ * short-circuits if the queue is already in memory. */
+ if (!(q = load_realtime_queue(queuename)))
+ return res;
+
+ ao2_lock(queues);
+
+ ao2_lock(q);
+ if ((old_member = interface_exists(q, interface)) == NULL) {
+ if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
+ add_to_interfaces(state_interface);
+ new_member->dynamic = 1;
+ ao2_link(q->members, new_member);
+ q->membercount++;
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n",
+ q->name, new_member->interface, new_member->membername,
+ "dynamic",
+ new_member->penalty, new_member->calls, (int) new_member->lastcall,
+ new_member->status, new_member->paused);
+
+ ao2_ref(new_member, -1);
+ new_member = NULL;
+
+ if (dump)
+ dump_queue_members(q);
+
+ res = RES_OKAY;
+ } else {
+ res = RES_OUTOFMEMORY;
+ }
+ } else {
+ ao2_ref(old_member, -1);
+ res = RES_EXISTS;
+ }
+ ao2_unlock(q);
+ ao2_unlock(queues);
+
+ return res;
+}
+
+static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
+{
+ int found = 0;
+ struct call_queue *q;
+ struct member *mem;
+ struct ao2_iterator queue_iter;
+
+ /* Special event for when all queues are paused - individual events still generated */
+ /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
+ if (ast_strlen_zero(queuename))
+ ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
+
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(q);
+ if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
+ if ((mem = interface_exists(q, interface))) {
+ found++;
+ if (mem->paused == paused) {
+ ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
+ }
+ mem->paused = paused;
+
+ if (queue_persistent_members)
+ dump_queue_members(q);
+
+ if (mem->realtime)
+ update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
+
+ ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
+
+ if (!ast_strlen_zero(reason)) {
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n"
+ "Paused: %d\r\n"
+ "Reason: %s\r\n",
+ q->name, mem->interface, mem->membername, paused, reason);
+ } else {
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "MemberName: %s\r\n"
+ "Paused: %d\r\n",
+ q->name, mem->interface, mem->membername, paused);
+ }
+ ao2_ref(mem, -1);
+ }
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+
+ return found ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+/* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
+static int set_member_penalty(char *queuename, char *interface, int penalty)
+{
+ int foundinterface = 0, foundqueue = 0;
+ struct call_queue *q;
+ struct member *mem;
+ struct ao2_iterator queue_iter;
+
+ if (penalty < 0) {
+ ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
+ return RESULT_FAILURE;
+ }
+
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(q);
+ if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
+ foundqueue++;
+ if ((mem = interface_exists(q, interface))) {
+ foundinterface++;
+ mem->penalty = penalty;
+
+ ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Penalty: %d\r\n",
+ q->name, mem->interface, penalty);
+
+ }
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+
+ if (foundinterface) {
+ return RESULT_SUCCESS;
+ } else if (!foundqueue) {
+ ast_log (LOG_ERROR, "Invalid queuename\n");
+ } else {
+ ast_log (LOG_ERROR, "Invalid interface\n");
+ }
+
+ return RESULT_FAILURE;
+}
+
+/* \brief Gets members penalty.
+ *
+ * \return Return the members penalty or RESULT_FAILURE on error. */
+static int get_member_penalty(char *queuename, char *interface)
+{
+ int foundqueue = 0, penalty;
+ struct call_queue *q, tmpq = {
+ .name = queuename,
+ };
+ struct member *mem;
+
+ if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
+ foundqueue = 1;
+ ao2_lock(q);
+ if ((mem = interface_exists(q, interface))) {
+ penalty = mem->penalty;
+ ao2_unlock(q);
+ queue_unref(q);
+ return penalty;
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+
+ /* some useful debuging */
+ if (foundqueue)
+ ast_log (LOG_ERROR, "Invalid queuename\n");
+ else
+ ast_log (LOG_ERROR, "Invalid interface\n");
+
+ return RESULT_FAILURE;
+}
+
+/* Reload dynamic queue members persisted into the astdb */
+static void reload_queue_members(void)
+{
+ char *cur_ptr;
+ const char *queue_name;
+ char *member;
+ char *interface;
+ char *membername = NULL;
+ char *state_interface;
+ char *penalty_tok;
+ int penalty = 0;
+ char *paused_tok;
+ int paused = 0;
+ struct ast_db_entry *db_tree;
+ struct ast_db_entry *entry;
+ struct call_queue *cur_queue;
+ char queue_data[PM_MAX_LEN];
+
+ ao2_lock(queues);
+
+ /* Each key in 'pm_family' is the name of a queue */
+ db_tree = ast_db_gettree(pm_family, NULL);
+ for (entry = db_tree; entry; entry = entry->next) {
+
+ queue_name = entry->key + strlen(pm_family) + 2;
+
+ {
+ struct call_queue tmpq = {
+ .name = queue_name,
+ };
+ cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
+ }
+
+ if (!cur_queue)
+ cur_queue = load_realtime_queue(queue_name);
+
+ if (!cur_queue) {
+ /* If the queue no longer exists, remove it from the
+ * database */
+ ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
+ ast_db_del(pm_family, queue_name);
+ continue;
+ }
+
+ if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
+ queue_unref(cur_queue);
+ continue;
+ }
+
+ cur_ptr = queue_data;
+ while ((member = strsep(&cur_ptr, ",|"))) {
+ if (ast_strlen_zero(member))
+ continue;
+
+ interface = strsep(&member, ";");
+ penalty_tok = strsep(&member, ";");
+ paused_tok = strsep(&member, ";");
+ membername = strsep(&member, ";");
+ state_interface = strsep(&member, ";");
+
+ if (!penalty_tok) {
+ ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
+ break;
+ }
+ penalty = strtol(penalty_tok, NULL, 10);
+ if (errno == ERANGE) {
+ ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
+ break;
+ }
+
+ if (!paused_tok) {
+ ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
+ break;
+ }
+ paused = strtol(paused_tok, NULL, 10);
+ if ((errno == ERANGE) || paused < 0 || paused > 1) {
+ ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
+ break;
+ }
+
+ ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
+
+ if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
+ ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
+ break;
+ }
+ }
+ queue_unref(cur_queue);
+ }
+
+ ao2_unlock(queues);
+ if (db_tree) {
+ ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
+ ast_db_freetree(db_tree);
+ }
+}
+
+static int pqm_exec(struct ast_channel *chan, void *data)
+{
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(options);
+ AST_APP_ARG(reason);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options][|reason])\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.interface)) {
+ ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options[|reason]])\n");
+ return -1;
+ }
+
+ if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
+ ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
+ pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
+ return -1;
+ }
+
+ pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
+
+ return 0;
+}
+
+static int upqm_exec(struct ast_channel *chan, void *data)
+{
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(options);
+ AST_APP_ARG(reason);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options[|reason]])\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.interface)) {
+ ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options[|reason]])\n");
+ return -1;
+ }
+
+ if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
+ ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
+ pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
+ return -1;
+ }
+
+ pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
+
+ return 0;
+}
+
+static int rqm_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ char *parse, *temppos = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(options);
+ );
+
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.interface)) {
+ args.interface = ast_strdupa(chan->name);
+ temppos = strrchr(args.interface, '-');
+ if (temppos)
+ *temppos = '\0';
+ }
+
+ switch (remove_from_queue(args.queuename, args.interface)) {
+ case RES_OKAY:
+ ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
+ ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
+ pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
+ res = 0;
+ break;
+ case RES_EXISTS:
+ ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
+ pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
+ res = 0;
+ break;
+ case RES_NOSUCHQUEUE:
+ ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
+ pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
+ res = 0;
+ break;
+ case RES_NOT_DYNAMIC:
+ ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
+ pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
+ res = 0;
+ break;
+ }
+
+ return res;
+}
+
+static int aqm_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ char *parse, *temppos = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ AST_APP_ARG(penalty);
+ AST_APP_ARG(options);
+ AST_APP_ARG(membername);
+ AST_APP_ARG(state_interface);
+ );
+ int penalty = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.interface)) {
+ args.interface = ast_strdupa(chan->name);
+ temppos = strrchr(args.interface, '-');
+ if (temppos)
+ *temppos = '\0';
+ }
+
+ if (!ast_strlen_zero(args.penalty)) {
+ if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
+ ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
+ penalty = 0;
+ }
+ }
+
+ switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
+ case RES_OKAY:
+ ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
+ ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
+ pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
+ res = 0;
+ break;
+ case RES_EXISTS:
+ ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
+ pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
+ res = 0;
+ break;
+ case RES_NOSUCHQUEUE:
+ ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
+ pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
+ res = 0;
+ break;
+ case RES_OUTOFMEMORY:
+ ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
+ break;
+ }
+
+ return res;
+}
+
+static int ql_exec(struct ast_channel *chan, void *data)
+{
+ char *parse;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(uniqueid);
+ AST_APP_ARG(membername);
+ AST_APP_ARG(event);
+ AST_APP_ARG(params);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
+ || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
+ ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
+ return -1;
+ }
+
+ ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
+ "%s", args.params ? args.params : "");
+
+ return 0;
+}
+
+static void copy_rules(struct queue_ent *qe, const char *rulename)
+{
+ struct penalty_rule *pr_iter;
+ struct rule_list *rl_iter;
+ const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
+ AST_LIST_LOCK(&rule_lists);
+ AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
+ if (!strcasecmp(rl_iter->name, tmp))
+ break;
+ }
+ if (rl_iter) {
+ AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
+ struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
+ if (!new_pr) {
+ ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
+ AST_LIST_UNLOCK(&rule_lists);
+ break;
+ }
+ new_pr->time = pr_iter->time;
+ new_pr->max_value = pr_iter->max_value;
+ new_pr->min_value = pr_iter->min_value;
+ new_pr->max_relative = pr_iter->max_relative;
+ new_pr->min_relative = pr_iter->min_relative;
+ AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
+ }
+ }
+ AST_LIST_UNLOCK(&rule_lists);
+}
+
+/*!\brief The starting point for all queue calls
+ *
+ * The process involved here is to
+ * 1. Parse the options specified in the call to Queue()
+ * 2. Join the queue
+ * 3. Wait in a loop until it is our turn to try calling a queue member
+ * 4. Attempt to call a queue member
+ * 5. If 4. did not result in a bridged call, then check for between
+ * call options such as periodic announcements etc.
+ * 6. Try 4 again uless some condition (such as an expiration time) causes us to
+ * exit the queue.
+ */
+static int queue_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ int ringing=0;
+ const char *user_priority;
+ const char *max_penalty_str;
+ const char *min_penalty_str;
+ int prio;
+ int qcontinue = 0;
+ int max_penalty, min_penalty;
+ enum queue_result reason = QUEUE_UNKNOWN;
+ /* whether to exit Queue application after the timeout hits */
+ int tries = 0;
+ int noption = 0;
+ char *parse;
+ int makeannouncement = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(options);
+ AST_APP_ARG(url);
+ AST_APP_ARG(announceoverride);
+ AST_APP_ARG(queuetimeoutstr);
+ AST_APP_ARG(agi);
+ AST_APP_ARG(macro);
+ AST_APP_ARG(gosub);
+ AST_APP_ARG(rule);
+ );
+ /* Our queue entry */
+ struct queue_ent qe;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ /* Setup our queue entry */
+ memset(&qe, 0, sizeof(qe));
+ qe.start = time(NULL);
+
+ /* set the expire time based on the supplied timeout; */
+ if (args.queuetimeoutstr)
+ qe.expire = qe.start + atoi(args.queuetimeoutstr);
+ else
+ qe.expire = 0;
+
+ /* Get the priority from the variable ${QUEUE_PRIO} */
+ user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
+ if (user_priority) {
+ if (sscanf(user_priority, "%d", &prio) == 1) {
+ ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
+ } else {
+ ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
+ user_priority, chan->name);
+ prio = 0;
+ }
+ } else {
+ ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
+ prio = 0;
+ }
+
+ /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
+
+ if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
+ if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
+ ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
+ } else {
+ ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
+ max_penalty_str, chan->name);
+ max_penalty = 0;
+ }
+ } else {
+ max_penalty = 0;
+ }
+
+ if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
+ if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
+ ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
+ } else {
+ ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
+ min_penalty_str, chan->name);
+ min_penalty = 0;
+ }
+ } else {
+ min_penalty = 0;
+ }
+
+ if (args.options && (strchr(args.options, 'r')))
+ ringing = 1;
+
+ if (args.options && (strchr(args.options, 'c')))
+ qcontinue = 1;
+
+ ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
+ args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
+
+ qe.chan = chan;
+ qe.prio = prio;
+ qe.max_penalty = max_penalty;
+ qe.min_penalty = min_penalty;
+ qe.last_pos_said = 0;
+ qe.last_pos = 0;
+ qe.last_periodic_announce_time = time(NULL);
+ qe.last_periodic_announce_sound = 0;
+ qe.valid_digits = 0;
+ if (join_queue(args.queuename, &qe, &reason)) {
+ ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
+ set_queue_result(chan, reason);
+ return 0;
+ }
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
+ S_OR(chan->cid.cid_num, ""));
+ copy_rules(&qe, args.rule);
+ qe.pr = AST_LIST_FIRST(&qe.qe_rules);
+check_turns:
+ if (ringing) {
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ } else {
+ ast_moh_start(chan, qe.moh, NULL);
+ }
+
+ /* This is the wait loop for callers 2 through maxlen */
+ res = wait_our_turn(&qe, ringing, &reason);
+ if (res) {
+ goto stop;
+ }
+
+ makeannouncement = 0;
+
+ for (;;) {
+ /* This is the wait loop for the head caller*/
+ /* To exit, they may get their call answered; */
+ /* they may dial a digit from the queue context; */
+ /* or, they may timeout. */
+
+ enum queue_member_status stat;
+
+ /* Leave if we have exceeded our queuetimeout */
+ if (qe.expire && (time(NULL) > qe.expire)) {
+ record_abandoned(&qe);
+ reason = QUEUE_TIMEOUT;
+ res = 0;
+ ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
+ qe.pos, qe.opos, (long) time(NULL) - qe.start);
+ break;
+ }
+
+ if (makeannouncement) {
+ /* Make a position announcement, if enabled */
+ if (qe.parent->announcefrequency)
+ if ((res = say_position(&qe,ringing)))
+ goto stop;
+ }
+ makeannouncement = 1;
+
+ /* Make a periodic announcement, if enabled */
+ if (qe.parent->periodicannouncefrequency)
+ if ((res = say_periodic_announcement(&qe,ringing)))
+ goto stop;
+
+ /* see if we need to move to the next penalty level for this queue */
+ while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
+ update_qe_rule(&qe);
+ }
+
+ /* Try calling all queue members for 'timeout' seconds */
+ res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
+ if (res) {
+ goto stop;
+ }
+
+ stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
+
+ /* exit after 'timeout' cycle if 'n' option enabled */
+ if (noption && tries >= qe.parent->membercount) {
+ ast_verb(3, "Exiting on time-out cycle\n");
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
+ record_abandoned(&qe);
+ reason = QUEUE_TIMEOUT;
+ res = 0;
+ break;
+ }
+
+ /* leave the queue if no agents, if enabled */
+ if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
+ record_abandoned(&qe);
+ reason = QUEUE_LEAVEEMPTY;
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
+ res = 0;
+ break;
+ }
+
+ /* leave the queue if no reachable agents, if enabled */
+ if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
+ record_abandoned(&qe);
+ reason = QUEUE_LEAVEUNAVAIL;
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
+ res = 0;
+ break;
+ }
+ if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
+ record_abandoned(&qe);
+ reason = QUEUE_LEAVEUNAVAIL;
+ res = 0;
+ break;
+ }
+
+ /* Leave if we have exceeded our queuetimeout */
+ if (qe.expire && (time(NULL) > qe.expire)) {
+ record_abandoned(&qe);
+ reason = QUEUE_TIMEOUT;
+ res = 0;
+ ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
+ break;
+ }
+
+ /* If using dynamic realtime members, we should regenerate the member list for this queue */
+ update_realtime_members(qe.parent);
+
+ /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
+ res = wait_a_bit(&qe);
+ if (res)
+ goto stop;
+
+ /* Since this is a priority queue and
+ * it is not sure that we are still at the head
+ * of the queue, go and check for our turn again.
+ */
+ if (!is_our_turn(&qe)) {
+ ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
+ goto check_turns;
+ }
+ }
+
+stop:
+ if (res) {
+ if (res < 0) {
+ if (!qe.handled) {
+ record_abandoned(&qe);
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
+ "%d|%d|%ld", qe.pos, qe.opos,
+ (long) time(NULL) - qe.start);
+ }
+ res = -1;
+ } else if (qe.valid_digits) {
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
+ "%s|%d", qe.digits, qe.pos);
+ }
+ }
+
+ /* Don't allow return code > 0 */
+ if (res >= 0 && res != AST_PBX_KEEPALIVE) {
+ res = 0;
+ if (ringing) {
+ ast_indicate(chan, -1);
+ } else {
+ ast_moh_stop(chan);
+ }
+ ast_stopstream(chan);
+ }
+
+ set_queue_variables(&qe);
+
+ leave_queue(&qe);
+ if (reason != QUEUE_UNKNOWN)
+ set_queue_result(chan, reason);
+
+ return res;
+}
+
+static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int res = -1;
+ struct call_queue *q, tmpq = {
+ .name = data,
+ };
+
+ char interfacevar[256]="";
+ float sl = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
+ return -1;
+ }
+
+ if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
+ ao2_lock(q);
+ if (q->setqueuevar) {
+ sl = 0;
+ res = 0;
+
+ if (q->callscompleted > 0)
+ sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
+
+ snprintf(interfacevar,sizeof(interfacevar),
+ "QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f",
+ q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
+
+ pbx_builtin_setvar(chan, interfacevar);
+ }
+
+ ao2_unlock(q);
+ queue_unref(q);
+ } else
+ ast_log(LOG_WARNING, "queue %s was not found\n", data);
+
+ snprintf(buf, len, "%d", res);
+
+ return 0;
+}
+
+static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int count = 0;
+ struct member *m;
+ struct ao2_iterator mem_iter;
+ struct call_queue *q;
+ char *option;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
+ return -1;
+ }
+
+ if ((option = strchr(data, ',')))
+ *option++ = '\0';
+ else
+ option = "logged";
+ if ((q = load_realtime_queue(data))) {
+ ao2_lock(q);
+ if (!strcasecmp(option, "logged")) {
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ /* Count the agents who are logged in and presently answering calls */
+ if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
+ count++;
+ }
+ ao2_ref(m, -1);
+ }
+ } else if (!strcasecmp(option, "free")) {
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ /* Count the agents who are logged in and presently answering calls */
+ if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
+ count++;
+ }
+ ao2_ref(m, -1);
+ }
+ } else /* must be "count" */
+ count = q->membercount;
+ ao2_unlock(q);
+ queue_unref(q);
+ } else
+ ast_log(LOG_WARNING, "queue %s was not found\n", data);
+
+ snprintf(buf, len, "%d", count);
+
+ return 0;
+}
+
+static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int count = 0;
+ struct member *m;
+ struct call_queue *q;
+ struct ao2_iterator mem_iter;
+ static int depflag = 1;
+
+ if (depflag) {
+ depflag = 0;
+ ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
+ return -1;
+ }
+
+ if ((q = load_realtime_queue(data))) {
+ ao2_lock(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ /* Count the agents who are logged in and presently answering calls */
+ if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
+ count++;
+ }
+ ao2_ref(m, -1);
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ } else
+ ast_log(LOG_WARNING, "queue %s was not found\n", data);
+
+ snprintf(buf, len, "%d", count);
+
+ return 0;
+}
+
+
+static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int count = 0;
+ struct call_queue *q, tmpq = {
+ .name = data,
+ };
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
+ return -1;
+ }
+
+ if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
+ ao2_lock(q);
+ count = q->count;
+ ao2_unlock(q);
+ queue_unref(q);
+ } else
+ ast_log(LOG_WARNING, "queue %s was not found\n", data);
+
+ snprintf(buf, len, "%d", count);
+
+ return 0;
+}
+
+static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct call_queue *q, tmpq = {
+ .name = data,
+ };
+ struct member *m;
+
+ /* Ensure an otherwise empty list doesn't return garbage */
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
+ return -1;
+ }
+
+ if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
+ int buflen = 0, count = 0;
+ struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
+
+ ao2_lock(q);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ /* strcat() is always faster than printf() */
+ if (count++) {
+ strncat(buf + buflen, ",", len - buflen - 1);
+ buflen++;
+ }
+ strncat(buf + buflen, m->membername, len - buflen - 1);
+ buflen += strlen(m->membername);
+ /* Safeguard against overflow (negative length) */
+ if (buflen >= len - 2) {
+ ao2_ref(m, -1);
+ ast_log(LOG_WARNING, "Truncating list\n");
+ break;
+ }
+ ao2_ref(m, -1);
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ } else
+ ast_log(LOG_WARNING, "queue %s was not found\n", data);
+
+ /* We should already be terminated, but let's make sure. */
+ buf[len - 1] = '\0';
+
+ return 0;
+}
+
+/*! \brief Dialplan function QUEUE_MEMBER_PENALTY()
+ * Gets the members penalty. */
+static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int penalty;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ );
+ /* Make sure the returned value on error is NULL. */
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.argc < 2) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+ return -1;
+ }
+
+ penalty = get_member_penalty (args.queuename, args.interface);
+
+ if (penalty >= 0) /* remember that buf is already '\0' */
+ snprintf (buf, len, "%d", penalty);
+
+ return 0;
+}
+
+/*! Dialplan function QUEUE_MEMBER_PENALTY()
+ * Sets the members penalty. */
+static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int penalty;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(interface);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.argc < 2) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+ return -1;
+ }
+
+ penalty = atoi(value);
+
+ if (ast_strlen_zero(args.interface)) {
+ ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
+ return -1;
+ }
+
+ /* if queuename = NULL then penalty will be set for interface in all the queues. */
+ if (set_member_penalty(args.queuename, args.interface, penalty)) {
+ ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function queuevar_function = {
+ .name = "QUEUE_VARIABLES",
+ .synopsis = "Return Queue information in variables",
+ .syntax = "QUEUE_VARIABLES(<queuename>)",
+ .desc =
+"Makes the following queue variables available.\n"
+"QUEUEMAX maxmimum number of calls allowed\n"
+"QUEUESTRATEGY the strategy of the queue\n"
+"QUEUECALLS number of calls currently in the queue\n"
+"QUEUEHOLDTIME current average hold time\n"
+"QUEUECOMPLETED number of completed calls for the queue\n"
+"QUEUEABANDONED number of abandoned calls\n"
+"QUEUESRVLEVEL queue service level\n"
+"QUEUESRVLEVELPERF current service level performance\n"
+"Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
+ .read = queue_function_var,
+};
+
+static struct ast_custom_function queuemembercount_function = {
+ .name = "QUEUE_MEMBER",
+ .synopsis = "Count number of members answering a queue",
+ .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
+ .desc =
+"Returns the number of members currently associated with the specified queue.\n"
+"One of three options may be passed to determine the count returned:\n"
+ "\"logged\" - Returns the number of logged-in members for the specified queue\n"
+ "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
+ "\"count\" - Returns the total number of members for the specified queue\n",
+ .read = queue_function_qac,
+};
+
+static struct ast_custom_function queuemembercount_dep = {
+ .name = "QUEUE_MEMBER_COUNT",
+ .synopsis = "Count number of members answering a queue",
+ .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
+ .desc =
+"Returns the number of members currently associated with the specified queue.\n\n"
+"This function has been deprecated in favor of the QUEUE_MEMBER function\n",
+ .read = queue_function_qac_dep,
+};
+
+static struct ast_custom_function queuewaitingcount_function = {
+ .name = "QUEUE_WAITING_COUNT",
+ .synopsis = "Count number of calls currently waiting in a queue",
+ .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
+ .desc =
+"Returns the number of callers currently waiting in the specified queue.\n",
+ .read = queue_function_queuewaitingcount,
+};
+
+static struct ast_custom_function queuememberlist_function = {
+ .name = "QUEUE_MEMBER_LIST",
+ .synopsis = "Returns a list of interfaces on a queue",
+ .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
+ .desc =
+"Returns a comma-separated list of members associated with the specified queue.\n",
+ .read = queue_function_queuememberlist,
+};
+
+static struct ast_custom_function queuememberpenalty_function = {
+ .name = "QUEUE_MEMBER_PENALTY",
+ .synopsis = "Gets or sets queue members penalty.",
+ .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
+ .desc =
+"Gets or sets queue members penalty\n",
+ .read = queue_function_memberpenalty_read,
+ .write = queue_function_memberpenalty_write,
+};
+
+static int reload_queue_rules(int reload)
+{
+ struct ast_config *cfg;
+ struct rule_list *rl_iter, *new_rl;
+ struct penalty_rule *pr_iter;
+ char *rulecat = NULL;
+ struct ast_variable *rulevar = NULL;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
+ ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+ ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
+ return AST_MODULE_LOAD_SUCCESS;
+ } else {
+ AST_LIST_LOCK(&rule_lists);
+ while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
+ while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
+ ast_free(pr_iter);
+ ast_free(rl_iter);
+ }
+ while ((rulecat = ast_category_browse(cfg, rulecat))) {
+ if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
+ ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
+ AST_LIST_UNLOCK(&rule_lists);
+ return AST_MODULE_LOAD_FAILURE;
+ } else {
+ ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
+ AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
+ for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
+ if(!strcasecmp(rulevar->name, "penaltychange"))
+ insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
+ else
+ ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
+ }
+ }
+ AST_LIST_UNLOCK(&rule_lists);
+ }
+
+ ast_config_destroy(cfg);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+
+static int reload_queues(int reload)
+{
+ struct call_queue *q;
+ struct ast_config *cfg;
+ char *cat, *tmp;
+ struct ast_variable *var;
+ struct member *cur, *newm;
+ struct ao2_iterator mem_iter;
+ int new;
+ const char *general_val = NULL;
+ char parse[80];
+ char *interface, *state_interface;
+ char *membername = NULL;
+ int penalty;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ao2_iterator queue_iter;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(interface);
+ AST_APP_ARG(penalty);
+ AST_APP_ARG(membername);
+ AST_APP_ARG(state_interface);
+ );
+
+ /*First things first. Let's load queuerules.conf*/
+ if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
+ return AST_MODULE_LOAD_FAILURE;
+
+ if (!(cfg = ast_config_load("queues.conf", config_flags))) {
+ ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ ao2_lock(queues);
+ use_weight=0;
+ /* Mark all queues as dead for the moment */
+ queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ if (!q->realtime) {
+ q->dead = 1;
+ q->found = 0;
+ }
+ queue_unref(q);
+ }
+
+ /* Chug through config file */
+ cat = NULL;
+ while ((cat = ast_category_browse(cfg, cat)) ) {
+ if (!strcasecmp(cat, "general")) {
+ /* Initialize global settings */
+ queue_keep_stats = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
+ queue_keep_stats = ast_true(general_val);
+ queue_persistent_members = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
+ queue_persistent_members = ast_true(general_val);
+ autofill_default = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
+ autofill_default = ast_true(general_val);
+ montype_default = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
+ if (!strcasecmp(general_val, "mixmonitor"))
+ montype_default = 1;
+ }
+ update_cdr = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
+ update_cdr = ast_true(general_val);
+ shared_lastcall = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
+ shared_lastcall = ast_true(general_val);
+ } else { /* Define queue */
+ /* Look for an existing one */
+ struct call_queue tmpq = {
+ .name = cat,
+ };
+ if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
+ /* Make one then */
+ if (!(q = alloc_queue(cat))) {
+ /* TODO: Handle memory allocation failure */
+ }
+ new = 1;
+ } else
+ new = 0;
+ if (q) {
+ const char *tmpvar;
+ if (!new)
+ ao2_lock(q);
+ /* Check if a queue with this name already exists */
+ if (q->found) {
+ ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
+ if (!new) {
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+ continue;
+ }
+ /* Due to the fact that the "linear" strategy will have a different allocation
+ * scheme for queue members, we must devise the queue's strategy before other initializations
+ */
+ if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
+ q->strategy = strat2int(tmpvar);
+ if (q->strategy < 0) {
+ ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
+ tmpvar, q->name);
+ q->strategy = QUEUE_STRATEGY_RINGALL;
+ }
+ } else
+ q->strategy = QUEUE_STRATEGY_RINGALL;
+ /* Re-initialize the queue, and clear statistics */
+ init_queue(q);
+ if (!queue_keep_stats)
+ clear_queue(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((cur = ao2_iterator_next(&mem_iter))) {
+ if (!cur->dynamic) {
+ cur->delme = 1;
+ }
+ ao2_ref(cur, -1);
+ }
+ for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
+ if (!strcasecmp(var->name, "member")) {
+ struct member tmpmem;
+ membername = NULL;
+
+ /* Add a new member */
+ ast_copy_string(parse, var->value, sizeof(parse));
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ interface = args.interface;
+ if (!ast_strlen_zero(args.penalty)) {
+ tmp = args.penalty;
+ while (*tmp && *tmp < 33) tmp++;
+ penalty = atoi(tmp);
+ if (penalty < 0) {
+ penalty = 0;
+ }
+ } else
+ penalty = 0;
+
+ if (!ast_strlen_zero(args.membername)) {
+ membername = args.membername;
+ while (*membername && *membername < 33) membername++;
+ }
+
+ if (!ast_strlen_zero(args.state_interface)) {
+ state_interface = args.state_interface;
+ while (*state_interface && *state_interface < 33) state_interface++;
+ } else
+ state_interface = interface;
+
+ /* Find the old position in the list */
+ ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
+ cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
+ /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
+ if (cur && strcasecmp(cur->state_interface, state_interface)) {
+ remove_from_interfaces(cur->state_interface);
+ }
+ newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
+ if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
+ add_to_interfaces(state_interface);
+ ao2_link(q->members, newm);
+ ao2_ref(newm, -1);
+ newm = NULL;
+
+ if (cur)
+ ao2_ref(cur, -1);
+ else {
+ q->membercount++;
+ }
+ } else {
+ queue_set_param(q, var->name, var->value, var->lineno, 1);
+ }
+ }
+
+ /* Free remaining members marked as delme */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((cur = ao2_iterator_next(&mem_iter))) {
+ if (! cur->delme) {
+ ao2_ref(cur, -1);
+ continue;
+ }
+ ast_log(LOG_DEBUG, "%s in queue marked as delme, we should be deleting...\n", cur->interface);
+ q->membercount--;
+ ao2_unlink(q->members, cur);
+ remove_from_interfaces(cur->interface);
+ ao2_ref(cur, -1);
+ }
+
+ if (new) {
+ ao2_link(queues, q);
+ } else
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ if (q->dead) {
+ ao2_unlink(queues, q);
+ } else {
+ ao2_lock(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((cur = ao2_iterator_next(&mem_iter))) {
+ if (cur->dynamic)
+ q->membercount++;
+ cur->status = ast_device_state(cur->interface);
+ ao2_ref(cur, -1);
+ }
+ ao2_unlock(q);
+ }
+ queue_unref(q);
+ }
+ ao2_unlock(queues);
+ return 1;
+}
+
+/*! \brief direct ouput to manager or cli with proper terminator */
+static void do_print(struct mansession *s, int fd, const char *str)
+{
+ if (s)
+ astman_append(s, "%s\r\n", str);
+ else
+ ast_cli(fd, "%s\n", str);
+}
+
+static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
+{
+ struct call_queue *q;
+ struct ast_str *out = ast_str_alloca(240);
+ int found = 0;
+ time_t now = time(NULL);
+ struct ao2_iterator queue_iter;
+ struct ao2_iterator mem_iter;
+
+ if (argc != 2 && argc != 3)
+ return CLI_SHOWUSAGE;
+
+ /* We only want to load realtime queues when a specific queue is asked for. */
+ if (argc == 3) /* specific queue */
+ load_realtime_queue(argv[2]);
+
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ float sl;
+
+ ao2_lock(q);
+ if (argc == 3 && strcasecmp(q->name, argv[2])) {
+ ao2_unlock(q);
+ continue;
+ }
+ found = 1;
+
+ ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
+ if (q->maxlen)
+ ast_str_append(&out, 0, "%d", q->maxlen);
+ else
+ ast_str_append(&out, 0, "unlimited");
+ sl = 0;
+ if (q->callscompleted > 0)
+ sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
+ ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
+ int2strat(q->strategy), q->holdtime, q->weight,
+ q->callscompleted, q->callsabandoned,sl,q->servicelevel);
+ do_print(s, fd, out->str);
+ if (!ao2_container_count(q->members))
+ do_print(s, fd, " No Members");
+ else {
+ struct member *mem;
+
+ do_print(s, fd, " Members: ");
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((mem = ao2_iterator_next(&mem_iter))) {
+ ast_str_set(&out, 0, " %s", mem->membername);
+ if (mem->penalty)
+ ast_str_append(&out, 0, " with penalty %d", mem->penalty);
+ ast_str_append(&out, 0, "%s%s%s (%s)",
+ mem->dynamic ? " (dynamic)" : "",
+ mem->realtime ? " (realtime)" : "",
+ mem->paused ? " (paused)" : "",
+ devstate2str(mem->status));
+ if (mem->calls)
+ ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
+ mem->calls, (long) (time(NULL) - mem->lastcall));
+ else
+ ast_str_append(&out, 0, " has taken no calls yet");
+ do_print(s, fd, out->str);
+ ao2_ref(mem, -1);
+ }
+ }
+ if (!q->head)
+ do_print(s, fd, " No Callers");
+ else {
+ struct queue_ent *qe;
+ int pos = 1;
+
+ do_print(s, fd, " Callers: ");
+ for (qe = q->head; qe; qe = qe->next) {
+ ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
+ pos++, qe->chan->name, (long) (now - qe->start) / 60,
+ (long) (now - qe->start) % 60, qe->prio);
+ do_print(s, fd, out->str);
+ }
+ }
+ do_print(s, fd, ""); /* blank line between entries */
+ ao2_unlock(q);
+ if (argc == 3) { /* print a specific entry */
+ queue_unref(q);
+ break;
+ }
+ queue_unref(q);
+ }
+ if (!found) {
+ if (argc == 3)
+ ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
+ else
+ ast_str_set(&out, 0, "No queues.");
+ do_print(s, fd, out->str);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *complete_queue(const char *line, const char *word, int pos, int state)
+{
+ struct call_queue *q;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen = strlen(word);
+ struct ao2_iterator queue_iter;
+
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
+ ret = ast_strdup(q->name);
+ queue_unref(q);
+ break;
+ }
+ queue_unref(q);
+ }
+
+ return ret;
+}
+
+static char *complete_queue_show(const char *line, const char *word, int pos, int state)
+{
+ if (pos == 2)
+ return complete_queue(line, word, pos, state);
+ return NULL;
+}
+
+static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch ( cmd ) {
+ case CLI_INIT:
+ e->command = "queue show";
+ e->usage =
+ "Usage: queue show\n"
+ " Provides summary information on a specified queue.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_queue_show(a->line, a->word, a->pos, a->n);
+ }
+
+ return __queues_show(NULL, a->fd, a->argc, a->argv);
+}
+
+/*!\brief callback to display queues status in manager
+ \addtogroup Group_AMI
+ */
+static int manager_queues_show(struct mansession *s, const struct message *m)
+{
+ char *a[] = { "queue", "show" };
+
+ __queues_show(s, -1, 2, a);
+ astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
+
+ return RESULT_SUCCESS;
+}
+
+static int manager_queue_rule_show(struct mansession *s, const struct message *m)
+{
+ const char *rule = astman_get_header(m, "Rule");
+ struct rule_list *rl_iter;
+ struct penalty_rule *pr_iter;
+
+ AST_LIST_LOCK(&rule_lists);
+ AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
+ if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
+ astman_append(s, "RuleList: %s\r\n", rl_iter->name);
+ AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
+ astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
+ }
+ if (!ast_strlen_zero(rule))
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&rule_lists);
+
+ astman_append(s, "\r\n\r\n");
+
+ return RESULT_SUCCESS;
+}
+
+/* Dump summary of queue info */
+static int manager_queues_summary(struct mansession *s, const struct message *m)
+{
+ time_t now;
+ int qmemcount = 0;
+ int qmemavail = 0;
+ int qchancount = 0;
+ int qlongestholdtime = 0;
+ const char *id = astman_get_header(m, "ActionID");
+ const char *queuefilter = astman_get_header(m, "Queue");
+ char idText[256] = "";
+ struct call_queue *q;
+ struct queue_ent *qe;
+ struct member *mem;
+ struct ao2_iterator queue_iter;
+ struct ao2_iterator mem_iter;
+
+ astman_send_ack(s, m, "Queue summary will follow");
+ time(&now);
+ if (!ast_strlen_zero(id))
+ snprintf(idText, 256, "ActionID: %s\r\n", id);
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(q);
+
+ /* List queue properties */
+ if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
+ /* Reset the necessary local variables if no queuefilter is set*/
+ qmemcount = 0;
+ qmemavail = 0;
+ qchancount = 0;
+ qlongestholdtime = 0;
+
+ /* List Queue Members */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((mem = ao2_iterator_next(&mem_iter))) {
+ if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
+ ++qmemcount;
+ if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
+ ++qmemavail;
+ }
+ }
+ ao2_ref(mem, -1);
+ }
+ for (qe = q->head; qe; qe = qe->next) {
+ if ((now - qe->start) > qlongestholdtime) {
+ qlongestholdtime = now - qe->start;
+ }
+ ++qchancount;
+ }
+ astman_append(s, "Event: QueueSummary\r\n"
+ "Queue: %s\r\n"
+ "LoggedIn: %d\r\n"
+ "Available: %d\r\n"
+ "Callers: %d\r\n"
+ "HoldTime: %d\r\n"
+ "LongestHoldTime: %d\r\n"
+ "%s"
+ "\r\n",
+ q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+ astman_append(s,
+ "Event: QueueSummaryComplete\r\n"
+ "%s"
+ "\r\n", idText);
+
+ return RESULT_SUCCESS;
+}
+
+/* Dump queue status */
+static int manager_queues_status(struct mansession *s, const struct message *m)
+{
+ time_t now;
+ int pos;
+ const char *id = astman_get_header(m,"ActionID");
+ const char *queuefilter = astman_get_header(m,"Queue");
+ const char *memberfilter = astman_get_header(m,"Member");
+ char idText[256] = "";
+ struct call_queue *q;
+ struct queue_ent *qe;
+ float sl = 0;
+ struct member *mem;
+ struct ao2_iterator queue_iter;
+ struct ao2_iterator mem_iter;
+
+ astman_send_ack(s, m, "Queue status will follow");
+ time(&now);
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(q);
+
+ /* List queue properties */
+ if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
+ sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
+ astman_append(s, "Event: QueueParams\r\n"
+ "Queue: %s\r\n"
+ "Max: %d\r\n"
+ "Strategy: %s\r\n"
+ "Calls: %d\r\n"
+ "Holdtime: %d\r\n"
+ "Completed: %d\r\n"
+ "Abandoned: %d\r\n"
+ "ServiceLevel: %d\r\n"
+ "ServicelevelPerf: %2.1f\r\n"
+ "Weight: %d\r\n"
+ "%s"
+ "\r\n",
+ q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
+ q->callsabandoned, q->servicelevel, sl, q->weight, idText);
+ /* List Queue Members */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((mem = ao2_iterator_next(&mem_iter))) {
+ if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
+ astman_append(s, "Event: QueueMember\r\n"
+ "Queue: %s\r\n"
+ "Name: %s\r\n"
+ "Location: %s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %d\r\n"
+ "Status: %d\r\n"
+ "Paused: %d\r\n"
+ "%s"
+ "\r\n",
+ q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
+ mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
+ }
+ ao2_ref(mem, -1);
+ }
+ /* List Queue Entries */
+ pos = 1;
+ for (qe = q->head; qe; qe = qe->next) {
+ astman_append(s, "Event: QueueEntry\r\n"
+ "Queue: %s\r\n"
+ "Position: %d\r\n"
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Wait: %ld\r\n"
+ "%s"
+ "\r\n",
+ q->name, pos++, qe->chan->name,
+ S_OR(qe->chan->cid.cid_num, "unknown"),
+ S_OR(qe->chan->cid.cid_name, "unknown"),
+ (long) (now - qe->start), idText);
+ }
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+
+ astman_append(s,
+ "Event: QueueStatusComplete\r\n"
+ "%s"
+ "\r\n",idText);
+
+ return RESULT_SUCCESS;
+}
+
+static int manager_add_queue_member(struct mansession *s, const struct message *m)
+{
+ const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
+ int paused, penalty = 0;
+
+ queuename = astman_get_header(m, "Queue");
+ interface = astman_get_header(m, "Interface");
+ penalty_s = astman_get_header(m, "Penalty");
+ paused_s = astman_get_header(m, "Paused");
+ membername = astman_get_header(m, "MemberName");
+ state_interface = astman_get_header(m, "StateInterface");
+
+ if (ast_strlen_zero(queuename)) {
+ astman_send_error(s, m, "'Queue' not specified.");
+ return 0;
+ }
+
+ if (ast_strlen_zero(interface)) {
+ astman_send_error(s, m, "'Interface' not specified.");
+ return 0;
+ }
+
+ if (ast_strlen_zero(penalty_s))
+ penalty = 0;
+ else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
+ penalty = 0;
+
+ if (ast_strlen_zero(paused_s))
+ paused = 0;
+ else
+ paused = abs(ast_true(paused_s));
+
+ switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
+ case RES_OKAY:
+ ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
+ astman_send_ack(s, m, "Added interface to queue");
+ break;
+ case RES_EXISTS:
+ astman_send_error(s, m, "Unable to add interface: Already there");
+ break;
+ case RES_NOSUCHQUEUE:
+ astman_send_error(s, m, "Unable to add interface to queue: No such queue");
+ break;
+ case RES_OUTOFMEMORY:
+ astman_send_error(s, m, "Out of memory");
+ break;
+ }
+
+ return 0;
+}
+
+static int manager_remove_queue_member(struct mansession *s, const struct message *m)
+{
+ const char *queuename, *interface;
+
+ queuename = astman_get_header(m, "Queue");
+ interface = astman_get_header(m, "Interface");
+
+ if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
+ astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
+ return 0;
+ }
+
+ switch (remove_from_queue(queuename, interface)) {
+ case RES_OKAY:
+ ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
+ astman_send_ack(s, m, "Removed interface from queue");
+ break;
+ case RES_EXISTS:
+ astman_send_error(s, m, "Unable to remove interface: Not there");
+ break;
+ case RES_NOSUCHQUEUE:
+ astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
+ break;
+ case RES_OUTOFMEMORY:
+ astman_send_error(s, m, "Out of memory");
+ break;
+ case RES_NOT_DYNAMIC:
+ astman_send_error(s, m, "Member not dynamic");
+ break;
+ }
+
+ return 0;
+}
+
+static int manager_pause_queue_member(struct mansession *s, const struct message *m)
+{
+ const char *queuename, *interface, *paused_s, *reason;
+ int paused;
+
+ interface = astman_get_header(m, "Interface");
+ paused_s = astman_get_header(m, "Paused");
+ queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
+ reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */
+
+ if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
+ astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
+ return 0;
+ }
+
+ paused = abs(ast_true(paused_s));
+
+ if (set_member_paused(queuename, interface, reason, paused))
+ astman_send_error(s, m, "Interface not found");
+ else
+ astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
+ return 0;
+}
+
+static int manager_queue_log_custom(struct mansession *s, const struct message *m)
+{
+ const char *queuename, *event, *message, *interface, *uniqueid;
+
+ queuename = astman_get_header(m, "Queue");
+ uniqueid = astman_get_header(m, "UniqueId");
+ interface = astman_get_header(m, "Interface");
+ event = astman_get_header(m, "Event");
+ message = astman_get_header(m, "Message");
+
+ if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
+ astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
+ return 0;
+ }
+
+ ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
+ astman_send_ack(s, m, "Event added successfully");
+
+ return 0;
+}
+
+static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
+{
+ /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
+ switch (pos) {
+ case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
+ return NULL;
+ case 4: /* only one possible match, "to" */
+ return state == 0 ? ast_strdup("to") : NULL;
+ case 5: /* <queue> */
+ return complete_queue(line, word, pos, state);
+ case 6: /* only one possible match, "penalty" */
+ return state == 0 ? ast_strdup("penalty") : NULL;
+ case 7:
+ if (state < 100) { /* 0-99 */
+ char *num;
+ if ((num = ast_malloc(3))) {
+ sprintf(num, "%d", state);
+ }
+ return num;
+ } else {
+ return NULL;
+ }
+ case 8: /* only one possible match, "as" */
+ return state == 0 ? ast_strdup("as") : NULL;
+ case 9: /* Don't attempt to complete name of member (infinite possibilities) */
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
+static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
+{
+ const char *queuename, *interface, *penalty_s;
+ int penalty;
+
+ interface = astman_get_header(m, "Interface");
+ penalty_s = astman_get_header(m, "Penalty");
+ /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
+ queuename = astman_get_header(m, "Queue");
+
+ if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
+ astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
+ return 0;
+ }
+
+ penalty = atoi(penalty_s);
+
+ if (set_member_penalty((char *)queuename, (char *)interface, penalty))
+ astman_send_error(s, m, "Invalid interface, queuename or penalty");
+ else
+ astman_send_ack(s, m, "Interface penalty set successfully");
+
+ return 0;
+}
+
+static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *queuename, *interface, *membername = NULL, *state_interface = NULL;
+ int penalty;
+
+ switch ( cmd ) {
+ case CLI_INIT:
+ e->command = "queue add member";
+ e->usage =
+ "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_queue_add_member(a->line, a->word, a->pos, a->n);
+ }
+
+ if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
+ return CLI_SHOWUSAGE;
+ } else if (strcmp(a->argv[4], "to")) {
+ return CLI_SHOWUSAGE;
+ } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
+ return CLI_SHOWUSAGE;
+ } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
+ return CLI_SHOWUSAGE;
+ } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
+ return CLI_SHOWUSAGE;
+ }
+
+ queuename = a->argv[5];
+ interface = a->argv[3];
+ if (a->argc >= 8) {
+ if (sscanf(a->argv[7], "%d", &penalty) == 1) {
+ if (penalty < 0) {
+ ast_cli(a->fd, "Penalty must be >= 0\n");
+ penalty = 0;
+ }
+ } else {
+ ast_cli(a->fd, "Penalty must be an integer >= 0\n");
+ penalty = 0;
+ }
+ } else {
+ penalty = 0;
+ }
+
+ if (a->argc >= 10) {
+ membername = a->argv[9];
+ }
+
+ if (a->argc >= 12) {
+ state_interface = a->argv[11];
+ }
+
+ switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
+ case RES_OKAY:
+ ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
+ ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
+ return CLI_SUCCESS;
+ case RES_EXISTS:
+ ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
+ return CLI_FAILURE;
+ case RES_NOSUCHQUEUE:
+ ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
+ return CLI_FAILURE;
+ case RES_OUTOFMEMORY:
+ ast_cli(a->fd, "Out of memory\n");
+ return CLI_FAILURE;
+ case RES_NOT_DYNAMIC:
+ ast_cli(a->fd, "Member not dynamic\n");
+ return CLI_FAILURE;
+ default:
+ return CLI_FAILURE;
+ }
+}
+
+static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ struct call_queue *q;
+ struct member *m;
+ struct ao2_iterator queue_iter;
+ struct ao2_iterator mem_iter;
+ int wordlen = strlen(word);
+
+ /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
+ if (pos > 5 || pos < 3)
+ return NULL;
+ if (pos == 4) /* only one possible match, 'from' */
+ return (state == 0 ? ast_strdup("from") : NULL);
+
+ if (pos == 5) /* No need to duplicate code */
+ return complete_queue(line, word, pos, state);
+
+ /* here is the case for 3, <member> */
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_iterator_next(&queue_iter))) {
+ ao2_lock(q);
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
+ char *tmp;
+ ao2_unlock(q);
+ tmp = m->membername;
+ ao2_ref(m, -1);
+ queue_unref(q);
+ return ast_strdup(tmp);
+ }
+ ao2_ref(m, -1);
+ }
+ ao2_unlock(q);
+ queue_unref(q);
+ }
+
+ return NULL;
+}
+
+static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *queuename, *interface;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "queue remove member";
+ e->usage = "Usage: queue remove member <channel> from <queue>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 6) {
+ return CLI_SHOWUSAGE;
+ } else if (strcmp(a->argv[4], "from")) {
+ return CLI_SHOWUSAGE;
+ }
+
+ queuename = a->argv[5];
+ interface = a->argv[3];
+
+ switch (remove_from_queue(queuename, interface)) {
+ case RES_OKAY:
+ ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
+ ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
+ return CLI_SUCCESS;
+ case RES_EXISTS:
+ ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
+ return CLI_FAILURE;
+ case RES_NOSUCHQUEUE:
+ ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
+ return CLI_FAILURE;
+ case RES_OUTOFMEMORY:
+ ast_cli(a->fd, "Out of memory\n");
+ return CLI_FAILURE;
+ default:
+ return CLI_FAILURE;
+ }
+}
+
+static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
+{
+ /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
+ switch (pos) {
+ case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
+ return NULL;
+ case 4: /* only one possible match, "queue" */
+ return state == 0 ? ast_strdup("queue") : NULL;
+ case 5: /* <queue> */
+ return complete_queue(line, word, pos, state);
+ case 6: /* "reason" */
+ return state == 0 ? ast_strdup("reason") : NULL;
+ case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
+static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *queuename, *interface, *reason;
+ int paused;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "queue {pause|unpause} member";
+ e->usage =
+ "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
+ " Pause or unpause a queue member. Not specifying a particular queue\n"
+ " will pause or unpause a member across all queues to which the member\n"
+ " belongs.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
+ }
+
+ if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
+ return CLI_SHOWUSAGE;
+ } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
+ return CLI_SHOWUSAGE;
+ } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
+ return CLI_SHOWUSAGE;
+ }
+
+
+ interface = a->argv[3];
+ queuename = a->argc >= 6 ? a->argv[5] : NULL;
+ reason = a->argc == 8 ? a->argv[7] : NULL;
+ paused = !strcasecmp(a->argv[1], "pause");
+
+ if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
+ ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
+ if (!ast_strlen_zero(queuename))
+ ast_cli(a->fd, " in queue '%s'", queuename);
+ if (!ast_strlen_zero(reason))
+ ast_cli(a->fd, " for reason '%s'", reason);
+ ast_cli(a->fd, "\n");
+ return CLI_SUCCESS;
+ } else {
+ ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
+ if (!ast_strlen_zero(queuename))
+ ast_cli(a->fd, " in queue '%s'", queuename);
+ if (!ast_strlen_zero(reason))
+ ast_cli(a->fd, " for reason '%s'", reason);
+ ast_cli(a->fd, "\n");
+ return CLI_FAILURE;
+ }
+}
+
+static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
+{
+ /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
+ switch (pos) {
+ case 4:
+ if (state == 0) {
+ return ast_strdup("on");
+ } else {
+ return NULL;
+ }
+ case 6:
+ if (state == 0) {
+ return ast_strdup("in");
+ } else {
+ return NULL;
+ }
+ case 7:
+ return complete_queue(line, word, pos, state);
+ default:
+ return NULL;
+ }
+}
+
+static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *queuename = NULL, *interface;
+ int penalty = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "queue set penalty";
+ e->usage =
+ "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
+ "Set a member's penalty in the queue specified. If no queue is specified\n"
+ "then that interface's penalty is set in all queues to which that interface is a member\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 6 && a->argc != 8) {
+ return CLI_SHOWUSAGE;
+ } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (a->argc == 8)
+ queuename = a->argv[7];
+ interface = a->argv[5];
+ penalty = atoi(a->argv[3]);
+
+ switch (set_member_penalty(queuename, interface, penalty)) {
+ case RESULT_SUCCESS:
+ ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
+ return CLI_SUCCESS;
+ case RESULT_FAILURE:
+ ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
+ return CLI_FAILURE;
+ default:
+ return CLI_FAILURE;
+ }
+}
+
+static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ struct rule_list *rl_iter;
+ int wordlen = strlen(word);
+ char *ret = NULL;
+ if (pos != 3) /* Wha? */ {
+ ast_log(LOG_DEBUG, "Hitting this???, pos is %d\n", pos);
+ return NULL;
+ }
+
+ AST_LIST_LOCK(&rule_lists);
+ AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
+ if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
+ ret = ast_strdup(rl_iter->name);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&rule_lists);
+
+ return ret;
+}
+
+static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *rule;
+ struct rule_list *rl_iter;
+ struct penalty_rule *pr_iter;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "queue rules show";
+ e->usage =
+ "Usage: queue rules show [rulename]\n"
+ "Show the list of rules associated with rulename. If no\n"
+ "rulename is specified, list all rules defined in queuerules.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 3 && a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ rule = a->argc == 4 ? a->argv[3] : "";
+ AST_LIST_LOCK(&rule_lists);
+ AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
+ if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
+ ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
+ AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
+ ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
+ }
+ }
+ }
+ AST_LIST_UNLOCK(&rule_lists);
+ return CLI_SUCCESS;
+}
+
+static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "queue rules reload";
+ e->usage =
+ "Usage: queue rules reload\n"
+ "Reloads rules defined in queuerules.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ reload_queue_rules(1);
+ return CLI_SUCCESS;
+}
+
+static const char qpm_cmd_usage[] =
+"Usage: queue pause member <channel> in <queue> reason <reason>\n";
+
+static const char qum_cmd_usage[] =
+"Usage: queue unpause member <channel> in <queue> reason <reason>\n";
+
+static const char qsmp_cmd_usage[] =
+"Usage: queue set member penalty <channel> from <queue> <penalty>\n";
+
+static struct ast_cli_entry cli_queue[] = {
+ AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
+ AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
+ AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
+ AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
+ AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
+ AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
+ AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
+};
+
+static int unload_module(void)
+{
+ int res;
+ struct ast_context *con;
+
+ if (device_state.thread != AST_PTHREADT_NULL) {
+ device_state.stop = 1;
+ ast_mutex_lock(&device_state.lock);
+ ast_cond_signal(&device_state.cond);
+ ast_mutex_unlock(&device_state.lock);
+ pthread_join(device_state.thread, NULL);
+ }
+
+ ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
+ res = ast_manager_unregister("QueueStatus");
+ res |= ast_manager_unregister("Queues");
+ res |= ast_manager_unregister("QueueStatus");
+ res |= ast_manager_unregister("QueueSummary");
+ res |= ast_manager_unregister("QueueAdd");
+ res |= ast_manager_unregister("QueueRemove");
+ res |= ast_manager_unregister("QueuePause");
+ res |= ast_manager_unregister("QueueLog");
+ res |= ast_manager_unregister("QueuePenalty");
+ res |= ast_unregister_application(app_aqm);
+ res |= ast_unregister_application(app_rqm);
+ res |= ast_unregister_application(app_pqm);
+ res |= ast_unregister_application(app_upqm);
+ res |= ast_unregister_application(app_ql);
+ res |= ast_unregister_application(app);
+ res |= ast_custom_function_unregister(&queuevar_function);
+ res |= ast_custom_function_unregister(&queuemembercount_function);
+ res |= ast_custom_function_unregister(&queuemembercount_dep);
+ res |= ast_custom_function_unregister(&queuememberlist_function);
+ res |= ast_custom_function_unregister(&queuewaitingcount_function);
+ res |= ast_custom_function_unregister(&queuememberpenalty_function);
+
+ if (device_state_sub)
+ ast_event_unsubscribe(device_state_sub);
+
+ if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
+ ast_context_remove_extension2(con, "s", 1, NULL);
+ ast_context_destroy(con, "app_queue"); /* leave no trace */
+ }
+
+ clear_and_free_interfaces();
+
+ ao2_ref(queues, -1);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+ struct ast_context *con;
+
+ queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
+
+ if (!reload_queues(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ con = ast_context_find("app_queue_gosub_virtual_context");
+ if (!con)
+ con = ast_context_create(NULL, "app_queue_gosub_virtual_context", "app_queue");
+ if (!con)
+ ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
+ else
+ ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_queue");
+
+ if (queue_persistent_members)
+ reload_queue_members();
+
+ ast_mutex_init(&device_state.lock);
+ ast_cond_init(&device_state.cond, NULL);
+ ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
+
+ ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
+ res = ast_register_application(app, queue_exec, synopsis, descrip);
+ res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
+ res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
+ res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
+ res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
+ res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
+ res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
+ res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
+ res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
+ res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
+ res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
+ res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
+ res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
+ res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
+ res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
+ res |= ast_custom_function_register(&queuevar_function);
+ res |= ast_custom_function_register(&queuemembercount_function);
+ res |= ast_custom_function_register(&queuemembercount_dep);
+ res |= ast_custom_function_register(&queuememberlist_function);
+ res |= ast_custom_function_register(&queuewaitingcount_function);
+ res |= ast_custom_function_register(&queuememberpenalty_function);
+ if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END)))
+ res = -1;
+
+ return res ? AST_MODULE_LOAD_DECLINE : 0;
+}
+
+static int reload(void)
+{
+ reload_queues(1);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/trunk/apps/app_read.c b/trunk/apps/app_read.c
new file mode 100644
index 000000000..a7fdf624b
--- /dev/null
+++ b/trunk/apps/app_read.c
@@ -0,0 +1,231 @@
+/*
+ * 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 read a variable
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/indications.h"
+
+enum {
+ OPT_SKIP = (1 << 0),
+ OPT_INDICATION = (1 << 1),
+ OPT_NOANSWER = (1 << 2),
+} read_option_flags;
+
+AST_APP_OPTIONS(read_app_options, {
+ AST_APP_OPTION('s', OPT_SKIP),
+ AST_APP_OPTION('i', OPT_INDICATION),
+ AST_APP_OPTION('n', OPT_NOANSWER),
+});
+
+static char *app = "Read";
+
+static char *synopsis = "Read a variable";
+
+static char *descrip =
+" Read(variable[,filename[&filename2...]][,maxdigits][,option][,attempts][,timeout])\n\n"
+"Reads a #-terminated string of digits a certain number of times from the\n"
+"user in to the given variable.\n"
+" filename -- file(s) to play before reading digits or tone with option i\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"
+" Any value below 0 means the same. Max accepted value is 255.\n"
+" option -- options are 's' , 'i', 'n'\n"
+" 's' to return immediately if the line is not up,\n"
+" 'i' to play filename as an indication tone from your indications.conf\n"
+" 'n' to read digits even if the line is not up.\n"
+" attempts -- if greater than 1, that many attempts will be made in the \n"
+" event no data is entered.\n"
+" timeout -- The number of seconds to wait for a digit response. If greater\n"
+" than 0, that value will override the default timeout. Can be floating point.\n"
+"This application sets the following channel variable upon completion:\n"
+" READSTATUS - This is the status of the read operation.\n"
+" Possible values are:\n"
+" OK | ERROR | HANGUP | INTERRUPTED | SKIPPED | TIMEOUT\n";
+
+
+#define ast_next_data(instr,ptr,delim) if((ptr=strchr(instr,delim))) { *(ptr) = '\0' ; ptr++;}
+
+static int read_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char tmp[256] = "";
+ int maxdigits = 255;
+ int tries = 1, to = 0, x = 0;
+ double tosec;
+ char *argcopy = NULL;
+ struct ind_tone_zone_sound *ts = NULL;
+ struct ast_flags flags = {0};
+ const char *status = "ERROR";
+
+ AST_DECLARE_APP_ARGS(arglist,
+ AST_APP_ARG(variable);
+ AST_APP_ARG(filename);
+ AST_APP_ARG(maxdigits);
+ AST_APP_ARG(options);
+ AST_APP_ARG(attempts);
+ AST_APP_ARG(timeout);
+ );
+
+ pbx_builtin_setvar_helper(chan, "READSTATUS", status);
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
+ return 0;
+ }
+
+ argcopy = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(arglist, argcopy);
+
+ if (!ast_strlen_zero(arglist.options)) {
+ ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
+ }
+
+ if (!ast_strlen_zero(arglist.attempts)) {
+ tries = atoi(arglist.attempts);
+ if (tries <= 0)
+ tries = 1;
+ }
+
+ if (!ast_strlen_zero(arglist.timeout)) {
+ tosec = atof(arglist.timeout);
+ if (tosec <= 0)
+ to = 0;
+ else
+ to = tosec * 1000.0;
+ }
+
+ if (ast_strlen_zero(arglist.filename)) {
+ arglist.filename = NULL;
+ }
+ if (!ast_strlen_zero(arglist.maxdigits)) {
+ maxdigits = atoi(arglist.maxdigits);
+ if ((maxdigits < 1) || (maxdigits > 255)) {
+ maxdigits = 255;
+ } else
+ ast_verb(3, "Accepting a maximum of %d digits.\n", maxdigits);
+ }
+ if (ast_strlen_zero(arglist.variable)) {
+ ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[,filename][,maxdigits][,option][,attempts][,timeout])\n\n");
+ return 0;
+ }
+ if (ast_test_flag(&flags, OPT_INDICATION)) {
+ if (! ast_strlen_zero(arglist.filename)) {
+ ts = ast_get_indication_tone(chan->zone, arglist.filename);
+ }
+ }
+ if (chan->_state != AST_STATE_UP) {
+ if (ast_test_flag(&flags, OPT_SKIP)) {
+ /* At the user's option, skip if the line is not up */
+ pbx_builtin_setvar_helper(chan, arglist.variable, "");
+ pbx_builtin_setvar_helper(chan, "READSTATUS", "SKIPPED");
+ return 0;
+ } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
+ /* Otherwise answer unless we're supposed to read while on-hook */
+ res = ast_answer(chan);
+ }
+ }
+ if (!res) {
+ while (tries && !res) {
+ ast_stopstream(chan);
+ if (ts && ts->data[0]) {
+ if (!to)
+ to = chan->pbx ? chan->pbx->rtimeout * 1000 : 6000;
+ res = ast_playtones_start(chan, 0, ts->data, 0);
+ for (x = 0; x < maxdigits; ) {
+ res = ast_waitfordigit(chan, to);
+ ast_playtones_stop(chan);
+ if (res < 1) {
+ if (res == 0)
+ status = "TIMEOUT";
+ tmp[x]='\0';
+ break;
+ }
+ tmp[x++] = res;
+ if (tmp[x-1] == '#') {
+ tmp[x-1] = '\0';
+ status = "OK";
+ break;
+ }
+ if (x >= maxdigits) {
+ status = "OK";
+ }
+ }
+ } else {
+ res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
+ if (res == 0)
+ status = "OK";
+ else if (res == 1)
+ status = "TIMEOUT";
+ else if (res == 2)
+ status = "INTERRUPTED";
+ }
+ if (res > -1) {
+ pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
+ if (!ast_strlen_zero(tmp)) {
+ ast_verb(3, "User entered '%s'\n", tmp);
+ tries = 0;
+ } else {
+ tries--;
+ if (tries)
+ ast_verb(3, "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
+ else
+ ast_verb(3, "User entered nothing.\n");
+ }
+ res = 0;
+ } else {
+ pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
+ ast_verb(3, "User disconnected\n");
+ }
+ }
+ }
+
+ if (ast_check_hangup(chan))
+ status = "HANGUP";
+ pbx_builtin_setvar_helper(chan, "READSTATUS", status);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, read_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");
diff --git a/trunk/apps/app_readexten.c b/trunk/apps/app_readexten.c
new file mode 100644
index 000000000..cb4fd8c2a
--- /dev/null
+++ b/trunk/apps/app_readexten.c
@@ -0,0 +1,258 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 Dave Chappell
+ *
+ * David Chappell <David.Chappell@trincoll.edu>
+ *
+ * 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 read an extension into a variable
+ *
+ * \author David Chappell <David.Chappell@trincoll.edu>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/indications.h"
+#include "asterisk/channel.h"
+
+enum {
+ OPT_SKIP = (1 << 0),
+ OPT_INDICATION = (1 << 1),
+ OPT_NOANSWER = (1 << 2),
+} readexten_option_flags;
+
+AST_APP_OPTIONS(readexten_app_options, {
+ AST_APP_OPTION('s', OPT_SKIP),
+ AST_APP_OPTION('i', OPT_INDICATION),
+ AST_APP_OPTION('n', OPT_NOANSWER),
+});
+
+static char *app = "ReadExten";
+
+static char *synopsis = "Read an extension into a variable";
+
+static char *descrip =
+" ReadExten(<variable>[,[<filename>][,[<context>][,[<option>][,<timeout>]]]])\n\n"
+"Reads a #-terminated string of digits from the user into the given variable.\n"
+" filename file to play before reading digits or tone with option i\n"
+" context context in which to match extensions\n"
+" option options are:\n"
+" s - Return immediately if the channel is not answered,\n"
+" i - Play filename as an indication tone from your\n"
+" indications.conf\n"
+" n - Read digits even if the channel is not answered.\n"
+" timeout An integer number of seconds to wait for a digit response. If\n"
+" greater than 0, that value will override the default timeout.\n\n"
+"ReadExten will set READEXTENSTATUS on exit with one of the following statuses:\n"
+" OK A valid extension exists in ${variable}\n"
+" TIMEOUT No extension was entered in the specified time\n"
+" INVALID An invalid extension, ${INVALID_EXTEN}, was entered\n"
+" SKIP Line was not up and the option 's' was specified\n"
+" ERROR Invalid arguments were passed\n";
+
+
+static int readexten_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char exten[256] = "";
+ int maxdigits = sizeof(exten) - 1;
+ int timeout = 0, digit_timeout = 0, x = 0;
+ char *argcopy = NULL, *status = "";
+ struct ind_tone_zone_sound *ts = NULL;
+ struct ast_flags flags = {0};
+
+ AST_DECLARE_APP_ARGS(arglist,
+ AST_APP_ARG(variable);
+ AST_APP_ARG(filename);
+ AST_APP_ARG(context);
+ AST_APP_ARG(options);
+ AST_APP_ARG(timeout);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ReadExten requires at least one argument\n");
+ pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
+ return 0;
+ }
+
+ argcopy = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(arglist, argcopy);
+
+ if (ast_strlen_zero(arglist.variable)) {
+ ast_log(LOG_WARNING, "Invalid! Usage: ReadExten(variable[|filename][|context][|options][|timeout])\n\n");
+ pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
+ return 0;
+ }
+
+ if (ast_strlen_zero(arglist.filename))
+ arglist.filename = NULL;
+
+ if (ast_strlen_zero(arglist.context))
+ arglist.context = chan->context;
+
+ if (!ast_strlen_zero(arglist.options))
+ ast_app_parse_options(readexten_app_options, &flags, NULL, arglist.options);
+
+ if (!ast_strlen_zero(arglist.timeout)) {
+ timeout = atoi(arglist.timeout);
+ if (timeout > 0)
+ timeout *= 1000;
+ }
+
+ if (timeout <= 0)
+ timeout = chan->pbx ? chan->pbx->rtimeout * 1000 : 10000;
+
+ if (digit_timeout <= 0)
+ digit_timeout = chan->pbx ? chan->pbx->dtimeout * 1000 : 5000;
+
+ if (ast_test_flag(&flags, OPT_INDICATION) && !ast_strlen_zero(arglist.filename))
+ ts = ast_get_indication_tone(chan->zone, arglist.filename);
+
+ do {
+ if (chan->_state != AST_STATE_UP) {
+ if (ast_test_flag(&flags, OPT_SKIP)) {
+ /* At the user's option, skip if the line is not up */
+ pbx_builtin_setvar_helper(chan, arglist.variable, "");
+ status = "SKIP";
+ break;
+ } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
+ /* Otherwise answer unless we're supposed to read while on-hook */
+ res = ast_answer(chan);
+ }
+ }
+
+ if (res < 0) {
+ status = "HANGUP";
+ break;
+ }
+
+ ast_playtones_stop(chan);
+ ast_stopstream(chan);
+
+ if (ts && ts->data[0])
+ res = ast_playtones_start(chan, 0, ts->data, 0);
+ else if (arglist.filename)
+ res = ast_streamfile(chan, arglist.filename, chan->language);
+
+ for (x = 0; x < maxdigits; x++) {
+ ast_debug(3, "extension so far: '%s', timeout: %d\n", exten, timeout);
+ res = ast_waitfordigit(chan, timeout);
+
+ ast_playtones_stop(chan);
+ ast_stopstream(chan);
+ timeout = digit_timeout;
+
+ if (res < 1) { /* timeout expired or hangup */
+ if (ast_check_hangup(chan))
+ status = "HANGUP";
+ else
+ status = "TIMEOUT";
+ break;
+ } else if (res == '#') {
+ break;
+ }
+
+ exten[x] = res;
+ if (!ast_matchmore_extension(chan, arglist.context, exten, 1 /* priority */, chan->cid.cid_num)) {
+ break;
+ }
+ }
+
+ if (!ast_strlen_zero(status))
+ break;
+
+ if (ast_exists_extension(chan, arglist.context, exten, 1, chan->cid.cid_num)) {
+ ast_debug(3, "User entered valid extension '%s'\n", exten);
+ pbx_builtin_setvar_helper(chan, arglist.variable, exten);
+ status = "OK";
+ } else {
+ ast_debug(3, "User dialed invalid extension '%s' in context '%s' on %s\n", exten, arglist.context, chan->name);
+ pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
+ status = "INVALID";
+ }
+ } while (0);
+
+ pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", status);
+
+ return status[0] == 'H' ? -1 : 0;
+}
+
+static int acf_isexten_exec(struct ast_channel *chan, const char *cmd, char *parse, char *buffer, size_t buflen)
+{
+ int priority_int;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(context);
+ AST_APP_ARG(extension);
+ AST_APP_ARG(priority);
+ );
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.context))
+ args.context = chan->context;
+
+ if (ast_strlen_zero(args.extension)) {
+ ast_log(LOG_WARNING, "Syntax: VALID_EXTEN([<context>],<extension>[,<priority>]) - missing argument <extension>!\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.priority))
+ priority_int = 1;
+ else
+ priority_int = atoi(args.priority);
+
+ if (ast_exists_extension(chan, args.context, args.extension, priority_int, chan->cid.cid_num))
+ ast_copy_string(buffer, "1", buflen);
+ else
+ ast_copy_string(buffer, "0", buflen);
+
+ return 0;
+}
+
+static struct ast_custom_function acf_isexten = {
+ .name = "VALID_EXTEN",
+ .synopsis = "Determine whether an extension exists or not",
+ .syntax = "VALID_EXTEN([<context>],<extension>[,<priority>])",
+ .desc =
+"Returns a true value if the indicated context, extension, and priority exist.\n"
+"Context defaults to the current context, priority defaults to 1.\n",
+ .read = acf_isexten_exec,
+};
+
+static int unload_module(void)
+{
+ int res = ast_unregister_application(app);
+ res |= ast_custom_function_unregister(&acf_isexten);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = ast_register_application(app, readexten_exec, synopsis, descrip);
+ res |= ast_custom_function_register(&acf_isexten);
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read and evaluate extension validity");
diff --git a/trunk/apps/app_readfile.c b/trunk/apps/app_readfile.c
new file mode 100644
index 000000000..8762ef860
--- /dev/null
+++ b/trunk/apps/app_readfile.c
@@ -0,0 +1,107 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matt O'Gorman <mogorman@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 ReadFile application -- Reads in a File for you.
+ *
+ * \author Matt O'Gorman <mogorman@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+
+static char *app_readfile = "ReadFile";
+
+static char *readfile_synopsis = "Read the contents of a text file into a channel variable";
+
+static char *readfile_descrip =
+"ReadFile(varname=file,length)\n"
+" varname - Result stored here.\n"
+" file - The name of the file to read.\n"
+" length - Maximum number of characters to capture.\n";
+
+
+static int readfile_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ char *s, *varname=NULL, *file=NULL, *length=NULL, *returnvar=NULL;
+ int len=0;
+ static int deprecation_warning = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ReadFile require an argument!\n");
+ return -1;
+ }
+
+ s = ast_strdupa(data);
+
+ varname = strsep(&s, "=");
+ file = strsep(&s, ",");
+ length = s;
+
+ if (deprecation_warning++ % 10 == 0)
+ ast_log(LOG_WARNING, "ReadFile has been deprecated in favor of Set(%s=${FILE(%s,0,%s)})\n", varname, file, length);
+
+ if (!varname || !file) {
+ ast_log(LOG_ERROR, "No file or variable specified!\n");
+ return -1;
+ }
+
+ if (length) {
+ if ((sscanf(length, "%d", &len) != 1) || (len < 0)) {
+ ast_log(LOG_WARNING, "%s is not a positive number, defaulting length to max\n", length);
+ len = 0;
+ }
+ }
+
+ if ((returnvar = ast_read_textfile(file))) {
+ if (len > 0) {
+ if (len < strlen(returnvar))
+ returnvar[len]='\0';
+ else
+ ast_log(LOG_WARNING, "%s is longer than %d, and %d \n", file, len, (int)strlen(returnvar));
+ }
+ pbx_builtin_setvar_helper(chan, varname, returnvar);
+ ast_free(returnvar);
+ }
+
+ return res;
+}
+
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app_readfile);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app_readfile, readfile_exec, readfile_synopsis, readfile_descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Stores output of file into a variable");
diff --git a/trunk/apps/app_record.c b/trunk/apps/app_record.c
new file mode 100644
index 000000000..7214e1f07
--- /dev/null
+++ b/trunk/apps/app_record.c
@@ -0,0 +1,365 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matthew Fredrickson <creslin@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 record a sound file
+ *
+ * \author Matthew Fredrickson <creslin@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h"
+#include "asterisk/dsp.h" /* use dsp routines for silence detection */
+
+
+static char *app = "Record";
+
+static char *synopsis = "Record to a file";
+
+static char *descrip =
+" Record(filename.format,silence[,maxduration][,options])\n\n"
+"Records from the channel into a given filename. If the file exists it will\n"
+"be overwritten.\n"
+"- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
+"- 'silence' is the number of seconds of silence to allow before returning.\n"
+"- 'maxduration' is the maximum recording duration in seconds. If missing\n"
+"or 0 there is no maximum.\n"
+"- 'options' may contain any of the following letters:\n"
+" 'a' : append to existing recording rather than replacing\n"
+" 'n' : do not answer, but record anyway if line not yet answered\n"
+" 'q' : quiet (do not play a beep tone)\n"
+" 's' : skip recording if the line is not yet answered\n"
+" 't' : use alternate '*' terminator key (DTMF) instead of default '#'\n"
+" 'x' : ignore all terminator keys (DTMF) and keep recording until hangup\n"
+"\n"
+"If filename contains '%d', these characters will be replaced with a number\n"
+"incremented by one each time the file is recorded. A channel variable\n"
+"named RECORDED_FILE will also be set, which contains the final filemname.\n\n"
+"Use 'core show file formats' to see the available formats on your system\n\n"
+"User can press '#' to terminate the recording and continue to the next priority.\n\n"
+"If the user should hangup during a recording, all data will be lost and the\n"
+"application will teminate. \n";
+
+enum {
+ OPTION_APPEND = (1 << 0),
+ OPTION_NOANSWER = (1 << 1),
+ OPTION_QUIET = (1 << 2),
+ OPTION_SKIP = (1 << 3),
+ OPTION_STAR_TERMINATE = (1 << 4),
+ OPTION_IGNORE_TERMINATE = (1 << 5),
+ FLAG_HAS_PERCENT = (1 << 6),
+};
+
+AST_APP_OPTIONS(app_opts,{
+ AST_APP_OPTION('a', OPTION_APPEND),
+ AST_APP_OPTION('n', OPTION_NOANSWER),
+ AST_APP_OPTION('q', OPTION_QUIET),
+ AST_APP_OPTION('s', OPTION_SKIP),
+ AST_APP_OPTION('t', OPTION_STAR_TERMINATE),
+ AST_APP_OPTION('x', OPTION_IGNORE_TERMINATE),
+});
+
+static int record_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int count = 0;
+ char *ext = NULL, *opts[0];
+ char *parse, *dir, *file;
+ int i = 0;
+ char tmp[256];
+
+ struct ast_filestream *s = NULL;
+ struct ast_frame *f = NULL;
+
+ struct ast_dsp *sildet = NULL; /* silence detector dsp */
+ int totalsilence = 0;
+ int dspsilence = 0;
+ int silence = 0; /* amount of silence to allow */
+ int gotsilence = 0; /* did we timeout for silence? */
+ int maxduration = 0; /* max duration of recording in milliseconds */
+ int gottimeout = 0; /* did we timeout for maxduration exceeded? */
+ int terminator = '#';
+ int rfmt = 0;
+ int ioflags;
+ int waitres;
+ struct ast_silence_generator *silgen = NULL;
+ struct ast_flags flags = { 0, };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(silence);
+ AST_APP_ARG(maxduration);
+ AST_APP_ARG(options);
+ );
+
+ /* The next few lines of code parse out the filename and header from the input string */
+ if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
+ ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (args.argc == 4)
+ ast_app_parse_options(app_opts, &flags, opts, args.options);
+
+ if (!ast_strlen_zero(args.filename)) {
+ if (strstr(args.filename, "%d"))
+ ast_set_flag(&flags, FLAG_HAS_PERCENT);
+ ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */
+ if (!ext)
+ ext = strchr(args.filename, ':');
+ if (ext) {
+ *ext = '\0';
+ ext++;
+ }
+ }
+ if (!ext) {
+ ast_log(LOG_WARNING, "No extension specified to filename!\n");
+ return -1;
+ }
+ if (args.silence) {
+ if ((sscanf(args.silence, "%d", &i) == 1) && (i > -1)) {
+ silence = i * 1000;
+ } else if (!ast_strlen_zero(args.silence)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", args.silence);
+ }
+ }
+
+ if (args.maxduration) {
+ if ((sscanf(args.maxduration, "%d", &i) == 1) && (i > -1))
+ /* Convert duration to milliseconds */
+ maxduration = i * 1000;
+ else if (!ast_strlen_zero(args.maxduration))
+ ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", args.maxduration);
+ }
+
+ if (ast_test_flag(&flags, OPTION_STAR_TERMINATE))
+ terminator = '*';
+ if (ast_test_flag(&flags, OPTION_IGNORE_TERMINATE))
+ terminator = '\0';
+
+ /* done parsing */
+
+ /* these are to allow the use of the %d in the config file for a wild card of sort to
+ create a new file with the inputed name scheme */
+ if (ast_test_flag(&flags, FLAG_HAS_PERCENT)) {
+ AST_DECLARE_APP_ARGS(fname,
+ AST_APP_ARG(piece)[100];
+ );
+ char *tmp2 = ast_strdupa(args.filename);
+ char countstring[15];
+ int i;
+
+ /* Separate each piece out by the format specifier */
+ AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
+ do {
+ int tmplen;
+ /* First piece has no leading percent, so it's copied verbatim */
+ ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
+ tmplen = strlen(tmp);
+ for (i = 1; i < fname.argc; i++) {
+ if (fname.piece[i][0] == 'd') {
+ /* Substitute the count */
+ snprintf(countstring, sizeof(countstring), "%d", count);
+ ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
+ tmplen += strlen(countstring);
+ } else if (tmplen + 2 < sizeof(tmp)) {
+ /* Unknown format specifier - just copy it verbatim */
+ tmp[tmplen++] = '%';
+ tmp[tmplen++] = fname.piece[i][0];
+ }
+ /* Copy the remaining portion of the piece */
+ ast_copy_string(tmp + tmplen, &(fname.piece[i][1]), sizeof(tmp) - tmplen);
+ }
+ count++;
+ } while (ast_fileexists(tmp, ext, chan->language) > 0);
+ pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
+ } else
+ ast_copy_string(tmp, args.filename, sizeof(tmp));
+ /* end of routine mentioned */
+
+ if (chan->_state != AST_STATE_UP) {
+ if (ast_test_flag(&flags, OPTION_SKIP)) {
+ /* At the user's option, skip if the line is not up */
+ return 0;
+ } else if (!ast_test_flag(&flags, OPTION_NOANSWER)) {
+ /* Otherwise answer unless we're supposed to record while on-hook */
+ res = ast_answer(chan);
+ }
+ }
+
+ if (res) {
+ ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
+ goto out;
+ }
+
+ if (!ast_test_flag(&flags, OPTION_QUIET)) {
+ /* Some code to play a nice little beep to signify the start of the record operation */
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res) {
+ res = ast_waitstream(chan, "");
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
+ }
+ ast_stopstream(chan);
+ }
+
+ /* The end of beep code. Now the recording starts */
+
+ if (silence > 0) {
+ rfmt = chan->readformat;
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+ return -1;
+ }
+ sildet = ast_dsp_new();
+ if (!sildet) {
+ ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+ return -1;
+ }
+ ast_dsp_set_threshold(sildet, 256);
+ }
+
+ /* Create the directory if it does not exist. */
+ dir = ast_strdupa(tmp);
+ if ((file = strrchr(dir, '/')))
+ *file++ = '\0';
+ ast_mkdir (dir, 0777);
+
+ ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
+ s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE);
+
+ if (!s) {
+ ast_log(LOG_WARNING, "Could not create file %s\n", args.filename);
+ goto out;
+ }
+
+ if (ast_opt_transmit_silence)
+ silgen = ast_channel_start_silence_generator(chan);
+
+ /* Request a video update */
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+
+ if (maxduration <= 0)
+ maxduration = -1;
+
+ while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
+ if (maxduration > 0) {
+ if (waitres == 0) {
+ gottimeout = 1;
+ break;
+ }
+ maxduration = waitres;
+ }
+
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ res = ast_writestream(s, f);
+
+ if (res) {
+ ast_log(LOG_WARNING, "Problem writing frame\n");
+ ast_frfree(f);
+ break;
+ }
+
+ if (silence > 0) {
+ dspsilence = 0;
+ ast_dsp_silence(sildet, f, &dspsilence);
+ if (dspsilence) {
+ totalsilence = dspsilence;
+ } else {
+ totalsilence = 0;
+ }
+ if (totalsilence > silence) {
+ /* Ended happily with silence */
+ ast_frfree(f);
+ gotsilence = 1;
+ break;
+ }
+ }
+ } else if (f->frametype == AST_FRAME_VIDEO) {
+ res = ast_writestream(s, f);
+
+ if (res) {
+ ast_log(LOG_WARNING, "Problem writing frame\n");
+ ast_frfree(f);
+ break;
+ }
+ } else if ((f->frametype == AST_FRAME_DTMF) &&
+ (f->subclass == terminator)) {
+ ast_frfree(f);
+ break;
+ }
+ ast_frfree(f);
+ }
+ if (!f) {
+ ast_debug(1, "Got hangup\n");
+ res = -1;
+ }
+
+ if (gotsilence) {
+ ast_stream_rewind(s, silence - 1000);
+ ast_truncstream(s);
+ } else if (!gottimeout) {
+ /* Strip off the last 1/4 second of it */
+ ast_stream_rewind(s, 250);
+ ast_truncstream(s);
+ }
+ ast_closestream(s);
+
+ if (silgen)
+ ast_channel_stop_silence_generator(chan, silgen);
+
+out:
+ if ((silence > 0) && rfmt) {
+ res = ast_set_read_format(chan, rfmt);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
+ if (sildet)
+ ast_dsp_free(sildet);
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, record_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial Record Application");
diff --git a/trunk/apps/app_rpt.c b/trunk/apps/app_rpt.c
new file mode 100644
index 000000000..ae8f4ecea
--- /dev/null
+++ b/trunk/apps/app_rpt.c
@@ -0,0 +1,7482 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002-2005, Jim Dixon, WB6NIL
+ *
+ * Jim Dixon, WB6NIL <jim@lambdatel.com>
+ * Serious contributions by Steve RoDgers, WA6ZFT <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 Radio Repeater / Remote Base program
+ * version 0.48 06/13/06
+ *
+ * \author Jim Dixon, WB6NIL <jim@lambdatel.com>
+ *
+ * \note Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
+ *
+ * See http://www.zapatatelephony.org/app_rpt.html
+ *
+ *
+ * Repeater / Remote Functions:
+ * "Simple" Mode: * - autopatch access, # - autopatch hangup
+ * Normal mode:
+ * See the function list in rpt.conf (autopatchup, autopatchdn)
+ * autopatchup can optionally take comma delimited setting=value pairs:
+ *
+ *
+ * context=string : Override default context with "string"
+ * dialtime=ms : Specify the max number of milliseconds between phone number digits (1000 milliseconds = 1 second)
+ * farenddisconnect=1 : Automatically disconnect when called party hangs up
+ * noct=1 : Don't send repeater courtesy tone during autopatch calls
+ * quiet=1 : Don't send dial tone, or connect messages. Do not send patch down message when called party hangs up
+ *
+ *
+ * Example: 123=autopatchup,dialtime=20000,noct=1,farenddisconnect=1
+ *
+ * To send an asterisk (*) while dialing or talking on phone,
+ * use the autopatch acess code.
+ *
+ *
+ * status cmds:
+ *
+ * 1 - Force ID
+ * 2 - Give Time of Day
+ * 3 - Give software Version
+ *
+ * cop (control operator) cmds:
+ *
+ * 1 - System warm boot
+ * 2 - System enable
+ * 3 - System disable
+ * 4 - Test Tone On
+ * 5 - Dump System Variables on Console (debug)
+ * 6 - PTT (phone mode only)
+ *
+ * ilink cmds:
+ *
+ * 1 - Disconnect specified link
+ * 2 - Connect specified link -- monitor only
+ * 3 - Connect specified link -- tranceive
+ * 4 - Enter command mode on specified link
+ * 5 - System status
+ * 6 - Disconnect all links
+ *
+ * remote cmds:
+ *
+ * 1 - Recall Memory MM (*000-*099) (Gets memory from rpt.conf)
+ * 2 - Set VFO MMMMM*KKK*O (Mhz digits, Khz digits, Offset)
+ * 3 - Set Rx PL Tone HHH*D*
+ * 4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
+ * 5 - Link Status (long)
+ * 6 - Set operating mode M (FM, USB, LSB, AM, etc)
+ * 100 - RX PL off (Default)
+ * 101 - RX PL On
+ * 102 - TX PL Off (Default)
+ * 103 - TX PL On
+ * 104 - Low Power
+ * 105 - Med Power
+ * 106 - Hi Power
+ * 107 - Bump Down 20 Hz
+ * 108 - Bump Down 100 Hz
+ * 109 - Bump Down 500 Hz
+ * 110 - Bump Up 20 Hz
+ * 111 - Bump Up 100 Hz
+ * 112 - Bump Up 500 Hz
+ * 113 - Scan Down Slow
+ * 114 - Scan Down Medium
+ * 115 - Scan Down Fast
+ * 116 - Scan Up Slow
+ * 117 - Scan Up Medium
+ * 118 - Scan Up Fast
+ * 119 - Transmit allowing auto-tune
+ * 140 - Link Status (brief)
+ *
+ *
+ *
+ * 'duplex' modes: (defaults to duplex=2)
+ *
+ * 0 - Only remote links key Tx and no main repeat audio.
+ * 1 - Everything other then main Rx keys Tx, no main repeat audio.
+ * 2 - Normal mode
+ * 3 - Normal except no main repeat audio.
+ * 4 - Normal except no main repeat audio during autopatch only
+ *
+*/
+
+/*** MODULEINFO
+ <depend>zaptel</depend>
+ <depend>tonezone</depend>
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+/* Un-comment the following to include support for MDC-1200 digital tone
+ signalling protocol (using KA6SQG's GPL'ed implementation) */
+/* #include "mdc_decode.c" */
+
+/* Un-comment the following to include support for notch filters in the
+ rx audio stream (using Tony Fisher's mknotch (mkfilter) implementation) */
+/* #include "rpt_notch.c" */
+
+/* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
+
+#define MAXDTMF 32
+#define MAXMACRO 2048
+#define MAXGOSUB 2048
+#define MACROTIME 100
+#define GOSUBTIME 100
+#define MACROPTIME 500
+#define GOSUBPTIME 500
+#define DTMF_TIMEOUT 3
+
+#ifdef __RPT_NOTCH
+#define MAXFILTERS 10
+#endif
+
+#define DISC_TIME 10000 /* report disc after 10 seconds of no connect */
+#define MAX_RETRIES 5
+
+#define REDUNDANT_TX_TIME 2000
+
+#define RETRY_TIMER_MS 5000
+
+#define MAXPEERSTR 31
+#define MAXREMSTR 15
+
+#define DELIMCHR ','
+#define QUOTECHR 34
+
+#define NODES "nodes"
+#define MEMORY "memory"
+#define MACRO "macro"
+#define GOSUB "gosub"
+#define FUNCTIONS "functions"
+#define TELEMETRY "telemetry"
+#define MORSE "morse"
+#define FUNCCHAR '*'
+#define ENDCHAR '#'
+
+#define DEFAULT_IOBASE 0x378
+
+#define MAXCONNECTTIME 5000
+
+#define MAXNODESTR 300
+
+#define MAXPATCHCONTEXT 100
+
+#define ACTIONSIZE 32
+
+#define TELEPARAMSIZE 256
+
+#define REM_SCANTIME 100
+
+
+enum {REM_OFF, REM_MONITOR, REM_TX};
+
+enum {ID, PROC, TERM, COMPLETE, UNKEY, REMDISC, REMALREADY, REMNOTFOUND, REMGO,
+ CONNECTED, CONNFAIL, STATUS, TIMEOUT, ID1, STATS_TIME,
+ STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
+ TAILMSG, MACRO_NOTFOUND, GOSUB_NOTFOUND, MACRO_BUSY, GOSUB_BUSY, LASTNODEKEY};
+
+enum {REM_SIMPLEX, REM_MINUS, REM_PLUS};
+
+enum {REM_LOWPWR, REM_MEDPWR, REM_HIPWR};
+
+enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY};
+
+enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
+
+enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
+
+enum {REM_MODE_FM, REM_MODE_USB, REM_MODE_LSB, REM_MODE_AM};
+
+enum {HF_SCAN_OFF, HF_SCAN_DOWN_SLOW, HF_SCAN_DOWN_QUICK,
+ HF_SCAN_DOWN_FAST, HF_SCAN_UP_SLOW, HF_SCAN_UP_QUICK, HF_SCAN_UP_FAST};
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+#include <search.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/io.h>
+#include <math.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/callerid.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/features.h"
+#include "asterisk/cli.h"
+#include "asterisk/config.h"
+#include "asterisk/say.h"
+#include "asterisk/localtime.h"
+#include "asterisk/app.h"
+
+#include "asterisk/zapata.h"
+
+static char *app = "Rpt";
+
+static char *synopsis = "Radio Repeater/Remote Base Control System";
+
+static char *descrip =
+" Rpt(nodename[,options]): Radio Remote Link or Remote Base Link Endpoint Process.\n"
+"\n"
+" Not specifying an option puts it in normal endpoint mode (where source\n"
+" IP and nodename are verified).\n"
+"\n"
+" Options are as follows:\n"
+"\n"
+" X - Normal endpoint mode WITHOUT security check. Only specify\n"
+" this if you have checked security already (like with an IAX2\n"
+" user/password or something).\n"
+"\n"
+" Rannounce-string[,timeout[,timeout-destination]] - Amateur Radio\n"
+" Reverse Autopatch. Caller is put on hold, and announcement (as\n"
+" specified by the 'announce-string') is played on radio system.\n"
+" Users of radio system can access autopatch, dial specified\n"
+" code, and pick up call. Announce-string is list of names of\n"
+" recordings, or \"PARKED\" to substitute code for un-parking,\n"
+" or \"NODE\" to substitute node number.\n"
+"\n"
+" P - Phone Control mode. This allows a regular phone user to have\n"
+" full control and audio access to the radio system. For the\n"
+" user to have DTMF control, the 'phone_functions' parameter\n"
+" must be specified for the node in 'rpt.conf'. An additional\n"
+" function (cop,6) must be listed so that PTT control is available.\n"
+"\n"
+" D - Dumb Phone Control mode. This allows a regular phone user to\n"
+" have full control and audio access to the radio system. In this\n"
+" mode, the PTT is activated for the entire length of the call.\n"
+" For the user to have DTMF control (not generally recomended in\n"
+" this mode), the 'dphone_functions' parameter must be specified\n"
+" for the node in 'rpt.conf'. Otherwise no DTMF control will be\n"
+" available to the phone user.\n"
+"\n";
+
+static unsigned int vmajor = 0;
+static unsigned int vminor = 47;
+
+static int debug = 0; /* FIXME Set this >0 for extra debug output */
+static int nrpts = 0;
+
+char *discstr = "!!DISCONNECT!!";
+static char *remote_rig_ft897 = "ft897";
+static char *remote_rig_rbi = "rbi";
+
+#define MSWAIT 200
+#define HANGTIME 5000
+#define TOTIME 180000
+#define IDTIME 300000
+#define MAXRPTS 20
+#define MAX_STAT_LINKS 32
+#define POLITEID 30000
+#define FUNCTDELAY 1500
+
+static pthread_t rpt_master_thread;
+
+struct rpt;
+
+struct rpt_link
+{
+ struct rpt_link *next;
+ struct rpt_link *prev;
+ char mode; /* 1 if in tx mode */
+ char isremote;
+ char phonemode;
+ char name[MAXNODESTR]; /* identifier (routing) string */
+ char lasttx;
+ char lastrx;
+ char connected;
+ char hasconnected;
+ char outbound;
+ char disced;
+ char killme;
+ long elaptime;
+ long disctime;
+ long retrytimer;
+ long retxtimer;
+ int retries;
+ int reconnects;
+ long long connecttime;
+ struct ast_channel *chan;
+ struct ast_channel *pchan;
+} ;
+
+struct rpt_lstat
+{
+ struct rpt_lstat *next;
+ struct rpt_lstat *prev;
+ char peer[MAXPEERSTR];
+ char name[MAXNODESTR];
+ char mode;
+ char outbound;
+ char reconnects;
+ long long connecttime;
+} ;
+
+struct rpt_tele
+{
+ struct rpt_tele *next;
+ struct rpt_tele *prev;
+ struct rpt *rpt;
+ struct ast_channel *chan;
+ int mode;
+ struct rpt_link mylink;
+ char param[TELEPARAMSIZE];
+ pthread_t threadid;
+} ;
+
+struct function_table_tag
+{
+ char action[ACTIONSIZE];
+ int (*function)(struct rpt *myrpt, char *param, char *digitbuf,
+ int command_source, struct rpt_link *mylink);
+} ;
+
+/* Used to store the morse code patterns */
+
+struct morse_bits
+{
+ int len;
+ int ddcomb;
+} ;
+
+struct telem_defaults
+{
+ char name[20];
+ char value[80];
+} ;
+
+
+static struct rpt
+{
+ ast_mutex_t lock;
+ struct ast_config *cfg;
+ char reload;
+
+ char *name;
+ char *rxchanname;
+ char *txchanname;
+ char *remote;
+
+ struct {
+ char ourcontext[80];
+ char ourcallerid[80];
+ char acctcode[21];
+ char ident[80];
+ char tonezone[80];
+ char simple;
+ char functions[80];
+ char link_functions[80];
+ char phone_functions[80];
+ char dphone_functions[80];
+ char nodes[80];
+ int hangtime;
+ int totime;
+ int idtime;
+ int tailmessagetime;
+ int tailsquashedtime;
+ int duplex;
+ int politeid;
+ char *tailmsgbuf;
+ AST_DECLARE_APP_ARGS(tailmsg,
+ AST_APP_ARG(msgs)[100];
+ );
+ char memory[80];
+ char macro[80];
+ char gosub[80];
+ char startupmacro[80];
+ char startupgosub[80];
+ int iobase;
+ char funcchar;
+ char endchar;
+ char nobusyout;
+ } p;
+ struct rpt_link links;
+ int unkeytocttimer;
+ char keyed;
+ char exttx;
+ char localtx;
+ char remoterx;
+ char remotetx;
+ char remoteon;
+ char tounkeyed;
+ char tonotify;
+ char enable;
+ char dtmfbuf[MAXDTMF];
+ char macrobuf[MAXMACRO];
+ char gosubbuf[MAXGOSUB];
+ char rem_dtmfbuf[MAXDTMF];
+ char lastdtmfcommand[MAXDTMF];
+ char cmdnode[50];
+ struct ast_channel *rxchannel, *txchannel;
+ struct ast_channel *pchannel, *txpchannel, *remchannel;
+ struct rpt_tele tele;
+ struct timeval lasttv, curtv;
+ pthread_t rpt_call_thread, rpt_thread;
+ time_t dtmf_time, rem_dtmf_time, dtmf_time_rem;
+ int tailtimer, totimer, idtimer, txconf, conf, callmode, cidx, scantimer, tmsgtimer, skedtimer;
+ int mustid, tailid;
+ int tailevent;
+ int telemrefcount;
+ int dtmfidx, rem_dtmfidx;
+ int dailytxtime, dailykerchunks, totalkerchunks, dailykeyups, totalkeyups, timeouts;
+ int totalexecdcommands, dailyexecdcommands;
+ long retxtimer;
+ long long totaltxtime;
+ char mydtmf;
+ char exten[AST_MAX_EXTENSION];
+ char freq[MAXREMSTR], rxpl[MAXREMSTR], txpl[MAXREMSTR];
+ char offset;
+ char powerlevel;
+ char txplon;
+ char rxplon;
+ char remmode;
+ char tunerequest;
+ char hfscanmode;
+ int hfscanstatus;
+ char lastlinknode[MAXNODESTR];
+ char stopgen;
+ char patchfarenddisconnect;
+ char patchnoct;
+ char patchquiet;
+ char patchcontext[MAXPATCHCONTEXT];
+ int patchdialtime;
+ int macro_longest;
+ int gosub_longest;
+ int phone_longestfunc;
+ int dphone_longestfunc;
+ int link_longestfunc;
+ int longestfunc;
+ int longestnode;
+ int threadrestarts;
+ int tailmessagen;
+ time_t disgorgetime;
+ time_t lastthreadrestarttime;
+ long macrotimer;
+ long gosubtimer;
+ char lastnodewhichkeyedusup[MAXNODESTR];
+#ifdef __RPT_NOTCH
+ struct rptfilter
+ {
+ char desc[100];
+ float x0;
+ float x1;
+ float x2;
+ float y0;
+ float y1;
+ float y2;
+ float gain;
+ float const0;
+ float const1;
+ float const2;
+ } filters[MAXFILTERS];
+#endif
+#ifdef _MDC_DECODE_H_
+ mdc_decoder_t *mdc;
+ unsigned short lastunit;
+#endif
+} rpt_vars[MAXRPTS];
+
+
+#ifdef APP_RPT_LOCK_DEBUG
+
+#warning COMPILING WITH LOCK-DEBUGGING ENABLED!!
+
+#define MAXLOCKTHREAD 100
+
+#define rpt_mutex_lock(x) _rpt_mutex_lock(x, myrpt, __LINE__)
+#define rpt_mutex_unlock(x) _rpt_mutex_unlock(x, myrpt, __LINE__)
+
+struct lockthread
+{
+ pthread_t id;
+ int lockcount;
+ int lastlock;
+ int lastunlock;
+} lockthreads[MAXLOCKTHREAD];
+
+
+struct by_lightning
+{
+ int line;
+ struct timeval tv;
+ struct rpt *rpt;
+ struct lockthread lockthread;
+} lock_ring[32];
+
+
+int lock_ring_index = 0;
+
+AST_MUTEX_DEFINE_STATIC(locklock);
+
+static struct lockthread *get_lockthread(pthread_t id)
+{
+ int i;
+
+ for (i = 0; i < MAXLOCKTHREAD; i++) {
+ if (lockthreads[i].id == id)
+ return(&lockthreads[i]);
+ }
+ return NULL;
+}
+
+static struct lockthread *put_lockthread(pthread_t id)
+{
+ int i;
+
+ for (i = 0; i < MAXLOCKTHREAD; i++) {
+ if (lockthreads[i].id == id)
+ return(&lockthreads[i]);
+ }
+ for (i = 0; i < MAXLOCKTHREAD; i++) {
+ if (!lockthreads[i].id) {
+ lockthreads[i].lockcount = 0;
+ lockthreads[i].lastlock = 0;
+ lockthreads[i].lastunlock = 0;
+ lockthreads[i].id = id;
+ return &lockthreads[i];
+ }
+ }
+ return NULL;
+}
+
+
+static void rpt_mutex_spew(void)
+{
+ struct by_lightning lock_ring_copy[32];
+ int lock_ring_index_copy;
+ int i, j;
+ long long diff;
+ char a[100] = "";
+ struct ast_tm tm;
+ struct timeval lasttv;
+
+ ast_mutex_lock(&locklock);
+ memcpy(&lock_ring_copy, &lock_ring, sizeof(lock_ring_copy));
+ lock_ring_index_copy = lock_ring_index;
+ ast_mutex_unlock(&locklock);
+
+ lasttv.tv_sec = lasttv.tv_usec = 0;
+ for (i = 0; i < 32; i++) {
+ j = (i + lock_ring_index_copy) % 32;
+ ast_strftime(a, sizeof(a) - 1, "%m/%d/%Y %H:%M:%S",
+ ast_localtime(&lock_ring_copy[j].tv, &tm, NULL));
+ diff = 0;
+ if (lasttv.tv_sec) {
+ diff = (lock_ring_copy[j].tv.tv_sec - lasttv.tv_sec) * 1000000;
+ diff += (lock_ring_copy[j].tv.tv_usec - lasttv.tv_usec);
+ }
+ lasttv.tv_sec = lock_ring_copy[j].tv.tv_sec;
+ lasttv.tv_usec = lock_ring_copy[j].tv.tv_usec;
+ if (!lock_ring_copy[j].tv.tv_sec)
+ continue;
+ if (lock_ring_copy[j].line < 0) {
+ ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] UNLOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
+ i - 31, -lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
+ (int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
+ } else {
+ ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] LOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
+ i - 31, lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
+ (int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
+ }
+ }
+}
+
+
+static void _rpt_mutex_lock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
+{
+ struct lockthread *t;
+ pthread_t id;
+
+ id = pthread_self();
+ ast_mutex_lock(&locklock);
+ t = put_lockthread(id);
+ if (!t) {
+ ast_mutex_unlock(&locklock);
+ return;
+ }
+ if (t->lockcount) {
+ int lastline = t->lastlock;
+ ast_mutex_unlock(&locklock);
+ ast_log(LOG_NOTICE, "rpt_mutex_lock: Double lock request line %d node %s pid %x, last lock was line %d\n",
+ line, myrpt->name, (int) t->id, lastline);
+ rpt_mutex_spew();
+ return;
+ }
+ t->lastlock = line;
+ t->lockcount = 1;
+ gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
+ lock_ring[lock_ring_index].rpt = myrpt;
+ memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
+ lock_ring[lock_ring_index++].line = line;
+ if (lock_ring_index == 32)
+ lock_ring_index = 0;
+ ast_mutex_unlock(&locklock);
+ ast_mutex_lock(lockp);
+}
+
+
+static void _rpt_mutex_unlock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
+{
+ struct lockthread *t;
+ pthread_t id;
+
+ id = pthread_self();
+ ast_mutex_lock(&locklock);
+ t = put_lockthread(id);
+ if (!t) {
+ ast_mutex_unlock(&locklock);
+ return;
+ }
+ if (!t->lockcount) {
+ int lastline = t->lastunlock;
+ ast_mutex_unlock(&locklock);
+ ast_log(LOG_NOTICE, "rpt_mutex_lock: Double un-lock request line %d node %s pid %x, last un-lock was line %d\n",
+ line, myrpt->name, (int) t->id, lastline);
+ rpt_mutex_spew();
+ return;
+ }
+ t->lastunlock = line;
+ t->lockcount = 0;
+ gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
+ lock_ring[lock_ring_index].rpt = myrpt;
+ memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
+ lock_ring[lock_ring_index++].line = -line;
+ if (lock_ring_index == 32)
+ lock_ring_index = 0;
+ ast_mutex_unlock(&locklock);
+ ast_mutex_unlock(lockp);
+}
+
+#else /* APP_RPT_LOCK_DEBUG */
+
+#define rpt_mutex_lock(x) ast_mutex_lock(x)
+#define rpt_mutex_unlock(x) ast_mutex_unlock(x)
+
+#endif /* APP_RPT_LOCK_DEBUG */
+
+/*
+* CLI extensions
+*/
+
+/* Debug mode */
+static char *handle_cli_rpt_debug_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_rpt_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_rpt_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_rpt_lstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_rpt_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_rpt_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry cli_rpt[] = {
+ AST_CLI_DEFINE(handle_cli_rpt_debug_level, "Enable app_rpt debuggin"),
+ AST_CLI_DEFINE(handle_cli_rpt_dump, "Dump app_rpt structs for debugging"),
+ AST_CLI_DEFINE(handle_cli_rpt_stats, "Dump node statistics"),
+ AST_CLI_DEFINE(handle_cli_rpt_lstats, "Dump link statistics"),
+ AST_CLI_DEFINE(handle_cli_rpt_reload, "Reload app_rpt config"),
+ AST_CLI_DEFINE(handle_cli_rpt_restart, "Restart app_rpt")
+};
+
+/*
+* Telemetry defaults
+*/
+
+
+static struct telem_defaults tele_defs[] = {
+ {"ct1", "|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
+ {"ct2", "|t(660,880,150,3072)"},
+ {"ct3", "|t(440,0,150,3072)"},
+ {"ct4", "|t(550,0,150,3072)"},
+ {"ct5", "|t(660,0,150,3072)"},
+ {"ct6", "|t(880,0,150,3072)"},
+ {"ct7", "|t(660,440,150,3072)"},
+ {"ct8", "|t(700,1100,150,3072)"},
+ {"remotemon", "|t(1600,0,75,2048)"},
+ {"remotetx", "|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
+ {"cmdmode", "|t(900,904,200,2048)"},
+ {"functcomplete", "|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
+} ;
+
+/*
+* Forward decl's - these suppress compiler warnings when funcs coded further down the file than their invocation
+*/
+
+static int setrbi(struct rpt *myrpt);
+
+
+
+/*
+* Define function protos for function table here
+*/
+
+static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_gosub(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+/*
+* Function table
+*/
+
+static struct function_table_tag function_table[] = {
+ {"cop", function_cop},
+ {"autopatchup", function_autopatchup},
+ {"autopatchdn", function_autopatchdn},
+ {"ilink", function_ilink},
+ {"status", function_status},
+ {"remote", function_remote},
+ {"macro", function_macro},
+ {"gosub", function_gosub},
+} ;
+
+/*
+* Match a keyword in a list, and return index of string plus 1 if there was a match,
+* else return 0. If param is passed in non-null, then it will be set to the first character past the match
+*/
+
+static int matchkeyword(char *string, char **param, char *keywords[])
+{
+ int i, ls;
+ for (i = 0; keywords[i]; i++) {
+ ls = strlen(keywords[i]);
+ if (!ls) {
+ *param = NULL;
+ return 0;
+ }
+ if (!strncmp(string, keywords[i], ls)) {
+ if (param)
+ *param = string + ls;
+ return i + 1;
+ }
+ }
+ param = NULL;
+ return 0;
+}
+
+/*
+* Skip characters in string which are in charlist, and return a pointer to the
+* first non-matching character
+*/
+
+static char *skipchars(char *string, char *charlist)
+{
+ int i;
+ while (*string) {
+ for (i = 0; charlist[i] ; i++) {
+ if (*string == charlist[i]) {
+ string++;
+ break;
+ }
+ }
+ if (!charlist[i])
+ return string;
+ }
+ return string;
+}
+
+
+
+static int myatoi(const char *str)
+{
+ int ret;
+
+ if (str == NULL)
+ return -1;
+ /* leave this %i alone, non-base-10 input is useful here */
+ if (sscanf(str, "%i", &ret) != 1)
+ return -1;
+ return ret;
+}
+
+
+#ifdef __RPT_NOTCH
+
+/* rpt filter routine */
+static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len)
+{
+ int i, j;
+ struct rptfilter *f;
+
+ for (i = 0; i < len; i++) {
+ for (j = 0; j < MAXFILTERS; j++) {
+ f = &myrpt->filters[j];
+ if (!*f->desc)
+ continue;
+ f->x0 = f->x1; f->x1 = f->x2;
+ f->x2 = ((float)buf[i]) / f->gain;
+ f->y0 = f->y1; f->y1 = f->y2;
+ f->y2 = (f->x0 + f->x2) + f->const0 * f->x1
+ + (f->const1 * f->y0) + (f->const2 * f->y1);
+ buf[i] = (short)f->y2;
+ }
+ }
+}
+
+#endif
+
+/* Retrieve an int from a config file */
+static int retrieve_astcfgint(struct rpt *myrpt, const char *category, const char *name, int min, int max, int defl)
+{
+ const char *var = ast_variable_retrieve(myrpt->cfg, category, name);
+ int ret;
+
+ if (var) {
+ ret = myatoi(var);
+ if (ret < min)
+ ret = min;
+ else if (ret > max)
+ ret = max;
+ } else
+ ret = defl;
+ return ret;
+}
+
+
+static void load_rpt_vars(int n, int init)
+{
+ int j;
+ struct ast_variable *vp, *var;
+ struct ast_config *cfg;
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+#ifdef __RPT_NOTCH
+ AST_DECLARE_APP_ARGS(strs,
+ AST_APP_ARG(str)[100];
+ );
+#endif
+
+ ast_verb(3, "%s config for repeater %s\n",
+ (init) ? "Loading initial" : "Re-Loading", rpt_vars[n].name);
+ ast_mutex_lock(&rpt_vars[n].lock);
+ if (rpt_vars[n].cfg)
+ ast_config_destroy(rpt_vars[n].cfg);
+ cfg = ast_config_load("rpt.conf", config_flags);
+ if (!cfg) {
+ ast_mutex_unlock(&rpt_vars[n].lock);
+ ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n");
+ pthread_exit(NULL);
+ }
+ rpt_vars[n].cfg = cfg;
+ /* Free previously malloc'ed buffer */
+ if (!init && rpt_vars[n].p.tailmsgbuf)
+ ast_free(rpt_vars[n].p.tailmsgbuf);
+ memset(&rpt_vars[n].p, 0, sizeof(rpt_vars[n].p));
+ if (init) {
+ char *cp;
+ int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
+
+ cp = (char *) &rpt_vars[n].p;
+ memset(cp + sizeof(rpt_vars[n].p), 0,
+ sizeof(rpt_vars[n]) - (sizeof(rpt_vars[n].p) + savearea));
+ rpt_vars[n].tele.next = &rpt_vars[n].tele;
+ rpt_vars[n].tele.prev = &rpt_vars[n].tele;
+ rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
+ rpt_vars[n].tailmessagen = 0;
+ }
+#ifdef __RPT_NOTCH
+ /* zot out filters stuff */
+ memset(&rpt_vars[n].filters, 0, sizeof(rpt_vars[n].filters));
+#endif
+
+ /* Defaults */
+ ast_copy_string(rpt_vars[n].p.ourcontext, rpt_vars[n].name, sizeof(rpt_vars[n].p.ourcontext));
+ rpt_vars[n].p.hangtime = HANGTIME;
+ rpt_vars[n].p.totime = TOTIME;
+ rpt_vars[n].p.duplex = 2;
+ rpt_vars[n].p.idtime = IDTIME;
+ rpt_vars[n].p.politeid = POLITEID;
+ ast_copy_string(rpt_vars[n].p.memory, MEMORY, sizeof(rpt_vars[n].p.memory));
+ ast_copy_string(rpt_vars[n].p.macro, MACRO, sizeof(rpt_vars[n].p.macro));
+ ast_copy_string(rpt_vars[n].p.gosub, GOSUB, sizeof(rpt_vars[n].p.gosub));
+ rpt_vars[n].p.iobase = DEFAULT_IOBASE;
+ ast_copy_string(rpt_vars[n].p.functions, FUNCTIONS, sizeof(rpt_vars[n].p.functions));
+ rpt_vars[n].p.simple = 1;
+ rpt_vars[n].p.funcchar = FUNCCHAR;
+ rpt_vars[n].p.endchar = ENDCHAR;
+ ast_copy_string(rpt_vars[n].p.nodes, NODES, sizeof(rpt_vars[n].p.nodes));
+
+ for (var = ast_variable_browse(cfg, rpt_vars[n].name); var; var = var->next) {
+ if (!strcmp(var->name, "context")) {
+ ast_copy_string(rpt_vars[n].p.ourcontext, var->value, sizeof(rpt_vars[n].p.ourcontext));
+ } else if (!strcmp(var->name, "callerid")) {
+ ast_copy_string(rpt_vars[n].p.ourcallerid, var->value, sizeof(rpt_vars[n].p.ourcallerid));
+ } else if (!strcmp(var->name, "accountcode")) {
+ ast_copy_string(rpt_vars[n].p.acctcode, var->value, sizeof(rpt_vars[n].p.acctcode));
+ } else if (!strcmp(var->name, "idrecording")) {
+ ast_copy_string(rpt_vars[n].p.ident, var->value, sizeof(rpt_vars[n].p.ident));
+ } else if (!strcmp(var->name, "hangtime")) {
+ rpt_vars[n].p.hangtime = atoi(var->value);
+ } else if (!strcmp(var->name, "totime")) {
+ rpt_vars[n].p.totime = atoi(var->value);
+ } else if (!strcmp(var->name, "tailmessagetime")) {
+ rpt_vars[n].p.tailmessagetime = atoi(var->value);
+ if (rpt_vars[n].p.tailmessagetime < 0)
+ rpt_vars[n].p.tailmessagetime = 0;
+ else if (rpt_vars[n].p.tailmessagetime > 2400000)
+ rpt_vars[n].p.tailmessagetime = 2400000;
+ } else if (!strcmp(var->name, "tailsquashedtime")) {
+ rpt_vars[n].p.tailsquashedtime = atoi(var->value);
+ if (rpt_vars[n].p.tailsquashedtime < 0)
+ rpt_vars[n].p.tailsquashedtime = 0;
+ else if (rpt_vars[n].p.tailsquashedtime > 2400000)
+ rpt_vars[n].p.tailsquashedtime = 2400000;
+ } else if (!strcmp(var->name, "duplex")) {
+ rpt_vars[n].p.duplex = atoi(var->value);
+ if (rpt_vars[n].p.duplex < 0)
+ rpt_vars[n].p.duplex = 0;
+ else if (rpt_vars[n].p.duplex > 4)
+ rpt_vars[n].p.duplex = 4;
+ } else if (!strcmp(var->name, "idtime")) {
+ rpt_vars[n].p.idtime = atoi(var->value);
+ if (rpt_vars[n].p.idtime < 60000)
+ rpt_vars[n].p.idtime = 60000;
+ else if (rpt_vars[n].p.idtime > 2400000)
+ rpt_vars[n].p.idtime = 2400000;
+ } else if (!strcmp(var->name, "politeid")) {
+ rpt_vars[n].p.politeid = atoi(var->value);
+ if (rpt_vars[n].p.politeid < 30000)
+ rpt_vars[n].p.politeid = 30000;
+ else if (rpt_vars[n].p.politeid > 300000)
+ rpt_vars[n].p.politeid = 300000;
+ } else if (!strcmp(var->name, "tonezone")) {
+ ast_copy_string(rpt_vars[n].p.tonezone, var->value, sizeof(rpt_vars[n].p.tonezone));
+ } else if (!strcmp(var->name, "tailmessagelist")) {
+ rpt_vars[n].p.tailmsgbuf = ast_strdup(var->value);
+ AST_STANDARD_APP_ARGS(rpt_vars[n].p.tailmsg, rpt_vars[n].p.tailmsgbuf);
+ } else if (!strcmp(var->name, "memory")) {
+ ast_copy_string(rpt_vars[n].p.memory, var->value, sizeof(rpt_vars[n].p.memory));
+ } else if (!strcmp(var->name, "macro")) {
+ ast_copy_string(rpt_vars[n].p.macro, var->value, sizeof(rpt_vars[n].p.macro));
+ } else if (!strcmp(var->name, "gosub")) {
+ ast_copy_string(rpt_vars[n].p.gosub, var->value, sizeof(rpt_vars[n].p.gosub));
+ } else if (!strcmp(var->name, "startup_macro")) {
+ ast_copy_string(rpt_vars[n].p.startupmacro, var->value, sizeof(rpt_vars[n].p.startupmacro));
+ } else if (!strcmp(var->name, "startup_gosub")) {
+ ast_copy_string(rpt_vars[n].p.startupgosub, var->value, sizeof(rpt_vars[n].p.startupgosub));
+ } else if (!strcmp(var->name, "iobase")) {
+ /* do not use atoi() here, we need to be able to have
+ the input specified in hex or decimal so we use
+ sscanf with a %i */
+ if (sscanf(var->value, "%i", &rpt_vars[n].p.iobase) != 1)
+ rpt_vars[n].p.iobase = DEFAULT_IOBASE;
+ } else if (!strcmp(var->name, "functions")) {
+ rpt_vars[n].p.simple = 0;
+ ast_copy_string(rpt_vars[n].p.functions, var->value, sizeof(rpt_vars[n].p.functions));
+ } else if (!strcmp(var->name, "link_functions")) {
+ ast_copy_string(rpt_vars[n].p.link_functions, var->value, sizeof(rpt_vars[n].p.link_functions));
+ } else if (!strcmp(var->name, "phone_functions")) {
+ ast_copy_string(rpt_vars[n].p.phone_functions, var->value, sizeof(rpt_vars[n].p.phone_functions));
+ } else if (!strcmp(var->name, "dphone_functions")) {
+ ast_copy_string(rpt_vars[n].p.dphone_functions, var->value, sizeof(rpt_vars[n].p.dphone_functions));
+ } else if (!strcmp(var->name, "funcchar")) {
+ rpt_vars[n].p.funcchar = *var->value;
+ } else if (!strcmp(var->name, "endchar")) {
+ rpt_vars[n].p.endchar = *var->value;
+ } else if (!strcmp(var->name, "nobusyout")) {
+ rpt_vars[n].p.nobusyout = ast_true(var->value);
+ } else if (!strcmp(var->name, "nodes")) {
+ ast_copy_string(rpt_vars[n].p.nodes, var->value, sizeof(rpt_vars[n].p.nodes));
+#ifdef __RPT_NOTCH
+ } else if (!strcmp(var->name, "rxnotch")) {
+ char *tmp = ast_strdupa(val);
+ AST_STANDARD_APP_ARGS(strs, tmp);
+ strs.argc &= ~1; /* force an even number, rounded down */
+ if (strs.argc >= 2) {
+ for (j = 0; j < strs.argc; j += 2) {
+ rpt_mknotch(atof(strs.str[j]), atof(strs.str[j + 1]),
+ &rpt_vars[n].filters[j >> 1].gain,
+ &rpt_vars[n].filters[j >> 1].const0,
+ &rpt_vars[n].filters[j >> 1].const1,
+ &rpt_vars[n].filters[j >> 1].const2);
+ sprintf(rpt_vars[n].filters[j >> 1].desc, "%s Hz, BW = %s",
+ strs.str[j], strs.str[j + 1]);
+ }
+ }
+#endif
+ }
+ }
+
+ /* If these aren't specified, copy them from the functions property. */
+ if (ast_strlen_zero(rpt_vars[n].p.link_functions))
+ ast_copy_string(rpt_vars[n].p.link_functions, rpt_vars[n].p.functions, sizeof(rpt_vars[n].p.link_functions));
+
+ rpt_vars[n].longestnode = 0;
+ for (vp = ast_variable_browse(cfg, rpt_vars[n].p.nodes); vp; vp = vp->next) {
+ if ((j = strlen(vp->name)) > rpt_vars[n].longestnode)
+ rpt_vars[n].longestnode = j;
+ }
+
+ /*
+ * For this repeater, Determine the length of the longest function
+ */
+ rpt_vars[n].longestfunc = 0;
+ for (vp = ast_variable_browse(cfg, rpt_vars[n].p.functions); vp; vp = vp->next) {
+ if ((j = strlen(vp->name)) > rpt_vars[n].longestfunc)
+ rpt_vars[n].longestfunc = j;
+ }
+
+ rpt_vars[n].link_longestfunc = 0;
+ for (vp = ast_variable_browse(cfg, rpt_vars[n].p.link_functions); vp; vp = vp->next) {
+ if ((j = strlen(vp->name)) > rpt_vars[n].link_longestfunc)
+ rpt_vars[n].link_longestfunc = j;
+ }
+
+ rpt_vars[n].phone_longestfunc = 0;
+ for (vp = ast_variable_browse(cfg, rpt_vars[n].p.phone_functions); vp; vp = vp->next) {
+ if ((j = strlen(vp->name)) > rpt_vars[n].phone_longestfunc)
+ rpt_vars[n].phone_longestfunc = j;
+ }
+
+ rpt_vars[n].dphone_longestfunc = 0;
+ for (vp = ast_variable_browse(cfg, rpt_vars[n].p.dphone_functions); vp; vp = vp->next) {
+ if ((j = strlen(vp->name)) > rpt_vars[n].dphone_longestfunc)
+ rpt_vars[n].dphone_longestfunc = j;
+ }
+
+ rpt_vars[n].macro_longest = 1;
+ for (vp = ast_variable_browse(cfg, rpt_vars[n].p.macro); vp; vp = vp->next) {
+ if ((j = strlen(vp->name)) > rpt_vars[n].macro_longest)
+ rpt_vars[n].macro_longest = j;
+ }
+
+ rpt_vars[n].gosub_longest = 1;
+ for (vp = ast_variable_browse(cfg, rpt_vars[n].p.gosub); vp; vp = vp->next) {
+ if ((j = strlen(vp->name)) > rpt_vars[n].gosub_longest)
+ rpt_vars[n].gosub_longest = j;
+ }
+ ast_mutex_unlock(&rpt_vars[n].lock);
+}
+
+/*
+* Enable or disable debug output at a given level at the console
+*/
+static char *handle_cli_rpt_debug_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int newlevel;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rpt debug level";
+ e->usage =
+ "Usage: rpt debug level {0-7}\n"
+ " Enables debug messages in app_rpt\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ newlevel = myatoi(a->argv[3]);
+ if ((newlevel < 0) || (newlevel > 7))
+ return CLI_SHOWUSAGE;
+ if (newlevel)
+ ast_cli(a->fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
+ else
+ ast_cli(a->fd, "app_rpt Debugging disabled\n");
+
+ debug = newlevel;
+
+ return CLI_SUCCESS;
+}
+
+/*
+* Dump rpt struct debugging onto console
+*/
+static char *handle_cli_rpt_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rpt dump";
+ e->usage =
+ "Usage: rpt dump <nodename>\n"
+ " Dumps struct debug info to log\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ for (i = 0; i < nrpts; i++) {
+ if (!strcmp(a->argv[2], rpt_vars[i].name)) {
+ rpt_vars[i].disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
+ ast_cli(a->fd, "app_rpt struct dump requested for node %s\n", a->argv[2]);
+ return CLI_SUCCESS;
+ }
+ }
+ return CLI_FAILURE;
+}
+
+/*
+* Dump statistics onto console
+*/
+static char *handle_cli_rpt_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, j;
+ int dailytxtime, dailykerchunks;
+ int totalkerchunks, dailykeyups, totalkeyups, timeouts;
+ int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds;
+ long long totaltxtime;
+ struct rpt_link *l;
+ char *listoflinks[MAX_STAT_LINKS];
+ char *lastnodewhichkeyedusup, *lastdtmfcommand;
+ char *tot_state, *ider_state, *patch_state;
+ char *reverse_patch_state, *enable_state, *input_signal, *called_number;
+ struct rpt *myrpt;
+
+ static char *not_applicable = "N/A";
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rpt stats";
+ e->usage =
+ "Usage: rpt stats <nodename>\n"
+ " Dumps node statistics to console\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ for (i = 0 ; i <= MAX_STAT_LINKS; i++)
+ listoflinks[i] = NULL;
+
+ tot_state = ider_state =
+ patch_state = reverse_patch_state =
+ input_signal = called_number =
+ lastdtmfcommand = not_applicable;
+
+ for (i = 0; i < nrpts; i++) {
+ if (!strcmp(a->argv[2], rpt_vars[i].name)) {
+ /* Make a copy of all stat variables while locked */
+ myrpt = &rpt_vars[i];
+ rpt_mutex_lock(&myrpt->lock); /* LOCK */
+
+ dailytxtime = myrpt->dailytxtime;
+ totaltxtime = myrpt->totaltxtime;
+ dailykeyups = myrpt->dailykeyups;
+ totalkeyups = myrpt->totalkeyups;
+ dailykerchunks = myrpt->dailykerchunks;
+ totalkerchunks = myrpt->totalkerchunks;
+ dailyexecdcommands = myrpt->dailyexecdcommands;
+ totalexecdcommands = myrpt->totalexecdcommands;
+ timeouts = myrpt->timeouts;
+
+ /* Traverse the list of connected nodes */
+ reverse_patch_state = "DOWN";
+ j = 0;
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') { /* Skip '0' nodes */
+ reverse_patch_state = "UP";
+ l = l->next;
+ continue;
+ }
+ listoflinks[j] = ast_strdupa(l->name);
+ if (listoflinks[j])
+ j++;
+ l = l->next;
+ }
+
+ lastnodewhichkeyedusup = ast_strdupa(myrpt->lastnodewhichkeyedusup);
+ if ((!lastnodewhichkeyedusup) || (ast_strlen_zero(lastnodewhichkeyedusup)))
+ lastnodewhichkeyedusup = not_applicable;
+
+ if (myrpt->keyed)
+ input_signal = "YES";
+ else
+ input_signal = "NO";
+
+ if (myrpt->enable)
+ enable_state = "YES";
+ else
+ enable_state = "NO";
+
+ if (!myrpt->totimer)
+ tot_state = "TIMED OUT!";
+ else if (myrpt->totimer != myrpt->p.totime)
+ tot_state = "ARMED";
+ else
+ tot_state = "RESET";
+
+ if (myrpt->tailid)
+ ider_state = "QUEUED IN TAIL";
+ else if (myrpt->mustid)
+ ider_state = "QUEUED FOR CLEANUP";
+ else
+ ider_state = "CLEAN";
+
+ switch (myrpt->callmode) {
+ case 1:
+ patch_state = "DIALING";
+ break;
+ case 2:
+ patch_state = "CONNECTING";
+ break;
+ case 3:
+ patch_state = "UP";
+ break;
+ case 4:
+ patch_state = "CALL FAILED";
+ break;
+ default:
+ patch_state = "DOWN";
+ }
+
+ if (!ast_strlen_zero(myrpt->exten))
+ called_number = ast_strdupa(myrpt->exten);
+
+ if (!ast_strlen_zero(myrpt->lastdtmfcommand))
+ lastdtmfcommand = ast_strdupa(myrpt->lastdtmfcommand);
+
+ rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
+
+ ast_cli(a->fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name);
+ ast_cli(a->fd, "Signal on input..................................: %s\n", input_signal);
+ ast_cli(a->fd, "Transmitter enabled..............................: %s\n", enable_state);
+ ast_cli(a->fd, "Time out timer state.............................: %s\n", tot_state);
+ ast_cli(a->fd, "Time outs since system initialization............: %d\n", timeouts);
+ ast_cli(a->fd, "Identifier state.................................: %s\n", ider_state);
+ ast_cli(a->fd, "Kerchunks today..................................: %d\n", dailykerchunks);
+ ast_cli(a->fd, "Kerchunks since system initialization............: %d\n", totalkerchunks);
+ ast_cli(a->fd, "Keyups today.....................................: %d\n", dailykeyups);
+ ast_cli(a->fd, "Keyups since system initialization...............: %d\n", totalkeyups);
+ ast_cli(a->fd, "DTMF commands today..............................: %d\n", dailyexecdcommands);
+ ast_cli(a->fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands);
+ ast_cli(a->fd, "Last DTMF command executed.......................: %s\n", lastdtmfcommand);
+
+ hours = dailytxtime / 3600000;
+ dailytxtime %= 3600000;
+ minutes = dailytxtime / 60000;
+ dailytxtime %= 60000;
+ seconds = dailytxtime / 1000;
+ dailytxtime %= 1000;
+
+ ast_cli(a->fd, "TX time today ...................................: %02d:%02d:%02d.%d\n",
+ hours, minutes, seconds, dailytxtime);
+
+ hours = (int) totaltxtime / 3600000;
+ totaltxtime %= 3600000;
+ minutes = (int) totaltxtime / 60000;
+ totaltxtime %= 60000;
+ seconds = (int) totaltxtime / 1000;
+ totaltxtime %= 1000;
+
+ ast_cli(a->fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n",
+ hours, minutes, seconds, (int) totaltxtime);
+ ast_cli(a->fd, "Nodes currently connected to us..................: ");
+ for (j = 0;; j++) {
+ if (!listoflinks[j]) {
+ if (!j) {
+ ast_cli(a->fd, "<NONE>");
+ }
+ break;
+ }
+ ast_cli(a->fd, "%s", listoflinks[j]);
+ if (j % 4 == 3) {
+ ast_cli(a->fd, "\n");
+ ast_cli(a->fd, " : ");
+ } else {
+ if (listoflinks[j + 1])
+ ast_cli(a->fd, ", ");
+ }
+ }
+ ast_cli(a->fd, "\n");
+
+ ast_cli(a->fd, "Last node which transmitted to us................: %s\n", lastnodewhichkeyedusup);
+ ast_cli(a->fd, "Autopatch state..................................: %s\n", patch_state);
+ ast_cli(a->fd, "Autopatch called number..........................: %s\n", called_number);
+ ast_cli(a->fd, "Reverse patch/IAXRPT connected...................: %s\n\n", reverse_patch_state);
+
+ return CLI_SUCCESS;
+ }
+ }
+ return CLI_FAILURE;
+}
+
+/*
+* Link stats function
+*/
+static char *handle_cli_rpt_lstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, j;
+ struct rpt *myrpt;
+ struct rpt_link *l;
+ struct rpt_lstat *s, *t;
+ struct rpt_lstat s_head;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rpt lstats";
+ e->usage =
+ "Usage: rpt lstats <nodename>\n"
+ " Dumps link statistics to console\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ s = NULL;
+ s_head.next = &s_head;
+ s_head.prev = &s_head;
+
+ for (i = 0; i < nrpts; i++) {
+ if (!strcmp(a->argv[2], rpt_vars[i].name)) {
+ /* Make a copy of all stat variables while locked */
+ myrpt = &rpt_vars[i];
+ rpt_mutex_lock(&myrpt->lock); /* LOCK */
+ /* Traverse the list of connected nodes */
+ j = 0;
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') { /* Skip '0' nodes */
+ l = l->next;
+ continue;
+ }
+ if ((s = ast_calloc(1, sizeof(*s))) == NULL) {
+ ast_log(LOG_ERROR, "Malloc failed in rpt_do_lstats\n");
+ rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
+ return CLI_FAILURE;
+ }
+ ast_copy_string(s->name, l->name, MAXREMSTR);
+ pbx_substitute_variables_helper(l->chan, "${IAXPEER(CURRENTCHANNEL)}", s->peer, MAXPEERSTR - 1);
+ s->mode = l->mode;
+ s->outbound = l->outbound;
+ s->reconnects = l->reconnects;
+ s->connecttime = l->connecttime;
+ insque((struct qelem *) s, (struct qelem *) s_head.next);
+ l = l->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
+ ast_cli(a->fd, "NODE PEER RECONNECTS DIRECTION CONNECT TIME\n");
+ ast_cli(a->fd, "---- ---- ---------- --------- ------------\n");
+
+ for (s = s_head.next; s != &s_head; s = s->next) {
+ int hours, minutes, seconds;
+ long long connecttime = s->connecttime;
+ char conntime[31];
+ hours = (int) connecttime/3600000;
+ connecttime %= 3600000;
+ minutes = (int) connecttime/60000;
+ connecttime %= 60000;
+ seconds = (int) connecttime/1000;
+ connecttime %= 1000;
+ snprintf(conntime, sizeof(conntime), "%02d:%02d:%02d.%d",
+ hours, minutes, seconds, (int) connecttime);
+ ast_cli(a->fd, "%-10s%-20s%-12d%-11s%-30s\n",
+ s->name, s->peer, s->reconnects, (s->outbound)? "OUT":"IN", conntime);
+ }
+ /* destroy our local link queue */
+ s = s_head.next;
+ while (s != &s_head) {
+ t = s;
+ s = s->next;
+ remque((struct qelem *)t);
+ ast_free(t);
+ }
+ return CLI_SUCCESS;
+ }
+ }
+
+ return CLI_FAILURE;
+}
+
+/*
+* reload vars
+*/
+static char *handle_cli_rpt_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int n;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rpt reload";
+ e->usage =
+ "Usage: rpt reload\n"
+ " Reloads app_rpt running config parameters\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ for (n = 0; n < nrpts; n++)
+ rpt_vars[n].reload = 1;
+
+ return CLI_SUCCESS;
+}
+
+/*
+* restart app_rpt
+*/
+static char *handle_cli_rpt_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rpt restart";
+ e->usage =
+ "Usage: rpt restart\n"
+ " Restarts app_rpt\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+ for (i = 0; i < nrpts; i++) {
+ if (rpt_vars[i].rxchannel)
+ ast_softhangup(rpt_vars[i].rxchannel, AST_SOFTHANGUP_DEV);
+ }
+ return CLI_SUCCESS;
+}
+
+static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
+{
+ int res;
+
+ if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
+ return res;
+
+ while (chan->generatordata) {
+ if (ast_safe_sleep(chan, 1))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
+{
+ return play_tone_pair(chan, freq, 0, duration, amplitude);
+}
+
+static int play_silence(struct ast_channel *chan, int duration)
+{
+ return play_tone_pair(chan, 0, 0, duration, 0);
+}
+
+
+static int send_morse(struct ast_channel *chan, const char *string, int speed, int freq, int amplitude)
+{
+
+ static struct morse_bits mbits[] = {
+ {0, 0}, /* SPACE */
+ {0, 0},
+ {6, 18},/* " */
+ {0, 0},
+ {7, 72},/* $ */
+ {0, 0},
+ {0, 0},
+ {6, 30},/* ' */
+ {5, 13},/* ( */
+ {6, 29},/* ) */
+ {0, 0},
+ {5, 10},/* + */
+ {6, 51},/* , */
+ {6, 33},/* - */
+ {6, 42},/* . */
+ {5, 9}, /* / */
+ {5, 31},/* 0 */
+ {5, 30},/* 1 */
+ {5, 28},/* 2 */
+ {5, 24},/* 3 */
+ {5, 16},/* 4 */
+ {5, 0}, /* 5 */
+ {5, 1}, /* 6 */
+ {5, 3}, /* 7 */
+ {5, 7}, /* 8 */
+ {5, 15},/* 9 */
+ {6, 7}, /* : */
+ {6, 21},/* ; */
+ {0, 0},
+ {5, 33},/* = */
+ {0, 0},
+ {6, 12},/* ? */
+ {0, 0},
+ {2, 2}, /* A */
+ {4, 1}, /* B */
+ {4, 5}, /* C */
+ {3, 1}, /* D */
+ {1, 0}, /* E */
+ {4, 4}, /* F */
+ {3, 3}, /* G */
+ {4, 0}, /* H */
+ {2, 0}, /* I */
+ {4, 14},/* J */
+ {3, 5}, /* K */
+ {4, 2}, /* L */
+ {2, 3}, /* M */
+ {2, 1}, /* N */
+ {3, 7}, /* O */
+ {4, 6}, /* P */
+ {4, 11},/* Q */
+ {3, 2}, /* R */
+ {3, 0}, /* S */
+ {1, 1}, /* T */
+ {3, 4}, /* U */
+ {4, 8}, /* V */
+ {3, 6}, /* W */
+ {4, 9}, /* X */
+ {4, 13},/* Y */
+ {4, 3} /* Z */
+ };
+
+ int dottime;
+ int dashtime;
+ int intralettertime;
+ int interlettertime;
+ int interwordtime;
+ int len, ddcomb;
+ int res;
+ int c;
+ int i;
+ int flags;
+
+ res = 0;
+
+ /* Approximate the dot time from the speed arg. */
+
+ dottime = 900 / speed;
+
+ /* Establish timing relationships */
+
+ dashtime = 3 * dottime;
+ intralettertime = dottime;
+ interlettertime = dottime * 4 ;
+ interwordtime = dottime * 7;
+
+ for (; (*string) && (!res); string++) {
+
+ c = *string;
+
+ /* Convert lower case to upper case */
+
+ if ((c >= 'a') && (c <= 'z'))
+ c -= 0x20;
+
+ /* Can't deal with any char code greater than Z, skip it */
+
+ if (c > 'Z')
+ continue;
+
+ /* If space char, wait the inter word time */
+
+ if (c == ' ') {
+ if (!res)
+ res = play_silence(chan, interwordtime);
+ continue;
+ }
+
+ /* Subtract out control char offset to match our table */
+
+ c -= 0x20;
+
+ /* Get the character data */
+
+ len = mbits[c].len;
+ ddcomb = mbits[c].ddcomb;
+
+ /* Send the character */
+
+ for (; len ; len--) {
+ if (!res)
+ res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
+ if (!res)
+ res = play_silence(chan, intralettertime);
+ ddcomb >>= 1;
+ }
+
+ /* Wait the interletter time */
+
+ if (!res)
+ res = play_silence(chan, interlettertime - intralettertime);
+ }
+
+ /* Wait for all the frames to be sent */
+
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_stopstream(chan);
+
+ /*
+ * Wait for the zaptel driver to physically write the tone blocks to the hardware
+ */
+
+ for (i = 0; i < 20 ; i++) {
+ flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
+ res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
+ if (flags & ZT_IOMUX_WRITEEMPTY)
+ break;
+ if ( ast_safe_sleep(chan, 50)) {
+ res = -1;
+ break;
+ }
+ }
+
+
+ return res;
+}
+
+static int send_tone_telemetry(struct ast_channel *chan, const char *tonestring)
+{
+ char *stringp;
+ char *tonesubset;
+ int f1, f2;
+ int duration;
+ int amplitude;
+ int res;
+ int i;
+ int flags;
+
+ res = 0;
+
+ stringp = ast_strdupa(tonestring);
+
+ for (;tonestring;) {
+ tonesubset = strsep(&stringp, ")");
+ if (!tonesubset)
+ break;
+ if (sscanf(tonesubset, "(%d,%d,%d,%d", &f1, &f2, &duration, &amplitude) != 4)
+ break;
+ res = play_tone_pair(chan, f1, f2, duration, amplitude);
+ if (res)
+ break;
+ }
+ if (!res)
+ res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
+
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_stopstream(chan);
+
+ /*
+ * Wait for the zaptel driver to physically write the tone blocks to the hardware
+ */
+
+ for (i = 0; i < 20 ; i++) {
+ flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
+ res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
+ if (flags & ZT_IOMUX_WRITEEMPTY)
+ break;
+ if (ast_safe_sleep(chan, 50)) {
+ res = -1;
+ break;
+ }
+ }
+
+ return res;
+}
+
+
+static int sayfile(struct ast_channel *mychannel, const char *fname)
+{
+ int res;
+
+ res = ast_streamfile(mychannel, fname, mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ return res;
+}
+
+static int saycharstr(struct ast_channel *mychannel, char *str)
+{
+ int res;
+
+ res = ast_say_character_str(mychannel, str, NULL, mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ return res;
+}
+
+static int saynum(struct ast_channel *mychannel, int num)
+{
+ int res;
+ res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ return res;
+}
+
+static int saydigits(struct ast_channel *mychannel, int num)
+{
+ int res;
+ res = ast_say_digits(mychannel, num, NULL, mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ return res;
+}
+
+
+static int telem_any(struct rpt *myrpt, struct ast_channel *chan, const char *entry)
+{
+ int res;
+ char c;
+
+ static int morsespeed;
+ static int morsefreq;
+ static int morseampl;
+ static int morseidfreq = 0;
+ static int morseidampl;
+ static char mcat[] = MORSE;
+
+ res = 0;
+
+ if (!morseidfreq) { /* Get the morse parameters if not already loaded */
+ morsespeed = retrieve_astcfgint(myrpt, mcat, "speed", 5, 20, 20);
+ morsefreq = retrieve_astcfgint(myrpt, mcat, "frequency", 300, 3000, 800);
+ morseampl = retrieve_astcfgint(myrpt, mcat, "amplitude", 200, 8192, 4096);
+ morseidampl = retrieve_astcfgint(myrpt, mcat, "idamplitude", 200, 8192, 2048);
+ morseidfreq = retrieve_astcfgint(myrpt, mcat, "idfrequency", 300, 3000, 330);
+ }
+
+ /* Is it a file, or a tone sequence? */
+
+ if (entry[0] == '|') {
+ c = entry[1];
+ if ((c >= 'a') && (c <= 'z'))
+ c -= 0x20;
+
+ switch (c) {
+ case 'I': /* Morse ID */
+ res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
+ break;
+ case 'M': /* Morse Message */
+ res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
+ break;
+ case 'T': /* Tone sequence */
+ res = send_tone_telemetry(chan, entry + 2);
+ break;
+ default:
+ res = -1;
+ }
+ } else
+ res = sayfile(chan, entry); /* File */
+ return res;
+}
+
+/*
+* This function looks up a telemetry name in the config file, and does a telemetry response as configured.
+*
+* 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
+*/
+
+static int telem_lookup(struct rpt *myrpt, struct ast_channel *chan, const char *node, const char *name)
+{
+ int res = 0;
+ int i;
+ const char *entry = NULL;
+ const char *telemetry;
+
+ /* Retrieve the section name for telemetry from the node section */
+ if ((telemetry = ast_variable_retrieve(myrpt->cfg, node, TELEMETRY)))
+ entry = ast_variable_retrieve(myrpt->cfg, telemetry, name);
+
+ /* Try to look up the telemetry name */
+
+ if (!entry) {
+ /* Telemetry name wasn't found in the config file, use the default */
+ for (i = 0; i < sizeof(tele_defs) / sizeof(struct telem_defaults); i++) {
+ if (!strcasecmp(tele_defs[i].name, name))
+ entry = tele_defs[i].value;
+ }
+ }
+ if (entry) {
+ if (!ast_strlen_zero(entry))
+ telem_any(myrpt, chan, entry);
+ } else {
+ res = -1;
+ }
+ return res;
+}
+
+/*
+* Retrieve a wait interval
+*/
+
+static int get_wait_interval(struct rpt *myrpt, int type)
+{
+ int interval = 1000;
+ const char *wait_times = ast_variable_retrieve(myrpt->cfg, myrpt->name, "wait_times");
+
+ switch (type) {
+ case DLY_TELEM:
+ if (wait_times)
+ interval = retrieve_astcfgint(myrpt, wait_times, "telemwait", 500, 5000, 1000);
+ break;
+ case DLY_ID:
+ if (wait_times)
+ interval = retrieve_astcfgint(myrpt, wait_times, "idwait", 250, 5000, 500);
+ else
+ interval = 500;
+ break;
+ case DLY_UNKEY:
+ if (wait_times)
+ interval = retrieve_astcfgint(myrpt, wait_times, "unkeywait", 500, 5000, 1000);
+ break;
+ case DLY_CALLTERM:
+ if (wait_times)
+ interval = retrieve_astcfgint(myrpt, wait_times, "calltermwait", 500, 5000, 1500);
+ break;
+ default:
+ return 0;
+ }
+ return interval;
+}
+
+
+/*
+* Wait a configurable interval of time
+*/
+
+
+static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
+{
+ int interval;
+ interval = get_wait_interval(myrpt, type);
+ if (debug)
+ ast_log(LOG_NOTICE, " Delay interval = %d\n", interval);
+ if (interval)
+ ast_safe_sleep(chan, interval);
+ if (debug)
+ ast_log(LOG_NOTICE, "Delay complete\n");
+ return;
+}
+
+
+static void *rpt_tele_thread(void *this)
+{
+ ZT_CONFINFO ci; /* conference info */
+ int res = 0, haslink, hastx, hasremote, imdone = 0, unkeys_queued, x;
+ struct rpt_tele *mytele = (struct rpt_tele *)this;
+ struct rpt_tele *tlist;
+ struct rpt *myrpt;
+ struct rpt_link *l, *m, linkbase;
+ struct ast_channel *mychannel;
+ const char *p, *ct;
+ struct timeval tv;
+ struct ast_tm localtm;
+#ifdef APP_RPT_LOCK_DEBUG
+ struct lockthread *t;
+#endif
+
+ /* get a pointer to myrpt */
+ myrpt = mytele->rpt;
+
+ /* Snag copies of a few key myrpt variables */
+ rpt_mutex_lock(&myrpt->lock);
+ rpt_mutex_unlock(&myrpt->lock);
+
+ /* allocate a pseudo-channel thru asterisk */
+ mychannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!mychannel) {
+ ast_log(LOG_WARNING, "rpt: unable to obtain pseudo channel\n");
+ rpt_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_free(mytele);
+ pthread_exit(NULL);
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
+ rpt_mutex_unlock(&myrpt->lock);
+
+ /* make a conference for the tx */
+ ci.chan = 0;
+ /* If there's an ID queued, or tail message queued, */
+ /* only connect the ID audio to the local tx conference so */
+ /* linked systems can't hear it */
+ ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY) ||
+ (mytele->mode == TAILMSG)) ?
+ myrpt->txconf : myrpt->conf);
+ ci.confmode = ZT_CONF_CONFANN;
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ rpt_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
+ ast_free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ ast_stopstream(mychannel);
+ switch (mytele->mode) {
+ case ID:
+ case ID1:
+ /* wait a bit */
+ wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM, mychannel);
+ res = telem_any(myrpt, mychannel, myrpt->p.ident);
+ imdone=1;
+ break;
+
+ case TAILMSG:
+ res = ast_streamfile(mychannel, myrpt->p.tailmsg.msgs[myrpt->tailmessagen], mychannel->language);
+ break;
+
+ case IDTALKOVER:
+ p = ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover");
+ if (p)
+ res = telem_any(myrpt, mychannel, p);
+ imdone = 1;
+ break;
+ case PROC:
+ /* wait a little bit longer */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = telem_lookup(myrpt, mychannel, myrpt->name, "patchup");
+ if (res < 0) { /* Then default message */
+ res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
+ }
+ break;
+ case TERM:
+ /* wait a little bit longer */
+ wait_interval(myrpt, DLY_CALLTERM, mychannel);
+ res = telem_lookup(myrpt, mychannel, myrpt->name, "patchdown");
+ if (res < 0) { /* Then default message */
+ res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
+ }
+ break;
+ case COMPLETE:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = telem_lookup(myrpt, mychannel, myrpt->name, "functcomplete");
+ break;
+ case MACRO_NOTFOUND:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/macro_notfound", mychannel->language);
+ break;
+ case GOSUB_NOTFOUND:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/gosub_notfound", mychannel->language);
+ break;
+ case MACRO_BUSY:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/macro_busy", mychannel->language);
+ break;
+ case GOSUB_BUSY:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/gosub_busy", mychannel->language);
+ break;
+ case UNKEY:
+ if (myrpt->patchnoct && myrpt->callmode) { /* If no CT during patch configured, then don't send one */
+ imdone = 1;
+ break;
+ }
+
+ /*
+ * Reset the Unkey to CT timer
+ */
+
+ x = get_wait_interval(myrpt, DLY_UNKEY);
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->unkeytocttimer = x; /* Must be protected as it is changed below */
+ rpt_mutex_unlock(&myrpt->lock);
+
+ /*
+ * If there's one already queued, don't do another
+ */
+
+ tlist = myrpt->tele.next;
+ unkeys_queued = 0;
+ if (tlist != &myrpt->tele) {
+ rpt_mutex_lock(&myrpt->lock);
+ while (tlist != &myrpt->tele) {
+ if (tlist->mode == UNKEY)
+ unkeys_queued++;
+ tlist = tlist->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ }
+ if (unkeys_queued > 1) {
+ imdone = 1;
+ break;
+ }
+
+ /* Wait for the telemetry timer to expire */
+ /* Periodically check the timer since it can be re-initialized above */
+ while (myrpt->unkeytocttimer) {
+ int ctint;
+ if (myrpt->unkeytocttimer > 100)
+ ctint = 100;
+ else
+ ctint = myrpt->unkeytocttimer;
+ ast_safe_sleep(mychannel, ctint);
+ rpt_mutex_lock(&myrpt->lock);
+ if (myrpt->unkeytocttimer < ctint)
+ myrpt->unkeytocttimer = 0;
+ else
+ myrpt->unkeytocttimer -= ctint;
+ rpt_mutex_unlock(&myrpt->lock);
+ }
+
+ /*
+ * Now, the carrier on the rptr rx should be gone.
+ * If it re-appeared, then forget about sending the CT
+ */
+ if (myrpt->keyed) {
+ imdone = 1;
+ break;
+ }
+
+ rpt_mutex_lock(&myrpt->lock); /* Update the kerchunk counters */
+ myrpt->dailykerchunks++;
+ myrpt->totalkerchunks++;
+ rpt_mutex_unlock(&myrpt->lock);
+
+ haslink = 0;
+ hastx = 0;
+ hasremote = 0;
+ l = myrpt->links.next;
+ if (l != &myrpt->links) {
+ rpt_mutex_lock(&myrpt->lock);
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ haslink = 1;
+ if (l->mode) {
+ hastx++;
+ if (l->isremote)
+ hasremote++;
+ }
+ l = l->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ }
+ if (haslink) {
+ res = telem_lookup(myrpt, mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
+ if (res)
+ ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name);
+
+ /* if in remote cmd mode, indicate it */
+ if (myrpt->cmdnode[0]) {
+ ast_safe_sleep(mychannel, 200);
+ res = telem_lookup(myrpt, mychannel, myrpt->name, "cmdmode");
+ if (res)
+ ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ }
+ } else if ((ct = ast_variable_retrieve(myrpt->cfg, myrpt->name, "unlinkedct"))) { /* Unlinked Courtesy Tone */
+ res = telem_lookup(myrpt, mychannel, myrpt->name, ct);
+ if (res)
+ ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
+ }
+ if (hasremote && (!myrpt->cmdnode[0])) {
+ /* set for all to hear */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONFANN;
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ rpt_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
+ ast_free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ if ((ct = ast_variable_retrieve(myrpt->cfg, myrpt->name, "remotect"))) { /* Unlinked Courtesy Tone */
+ ast_safe_sleep(mychannel, 200);
+ res = telem_lookup(myrpt, mychannel, myrpt->name, ct);
+ if (res)
+ ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
+ }
+ }
+#ifdef _MDC_DECODE_H_
+ if (myrpt->lastunit) {
+ char mystr[10];
+
+ ast_safe_sleep(mychannel, 200);
+ /* set for all to hear */
+ ci.chan = 0;
+ ci.confno = myrpt->txconf;
+ ci.confmode = ZT_CONF_CONFANN;
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ rpt_mutex_lock(&myrpt->lock);
+ remque((struct qelem *)mytele);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
+ ast_free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ snprintf(mystr, sizeof(mystr), "%04x", myrpt->lastunit);
+ myrpt->lastunit = 0;
+ ast_say_character_str(mychannel, mystr, NULL, mychannel->language);
+ break;
+ }
+#endif
+ imdone = 1;
+ break;
+ case REMDISC:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
+ res = ast_streamfile(mychannel, ((mytele->mylink.connected) ?
+ "rpt/remote_disc" : "rpt/remote_busy"), mychannel->language);
+ break;
+ case REMALREADY:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language);
+ break;
+ case REMNOTFOUND:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language);
+ break;
+ case REMGO:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language);
+ break;
+ case CONNECTED:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
+ res = ast_streamfile(mychannel, "rpt/connected", mychannel->language);
+ break;
+ case CONNFAIL:
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
+ res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language);
+ break;
+ case STATUS:
+ /* wait a little bit */
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ hastx = 0;
+ linkbase.next = &linkbase;
+ linkbase.prev = &linkbase;
+ rpt_mutex_lock(&myrpt->lock);
+ /* make our own list of links */
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ m = ast_malloc(sizeof(*m));
+ if (!m) {
+ ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name);
+ remque((struct qelem *)mytele);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
+ ast_free(mytele);
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ memcpy(m, l, sizeof(struct rpt_link));
+ m->next = m->prev = NULL;
+ insque((struct qelem *)m, (struct qelem *)linkbase.next);
+ l = l->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel, myrpt->name, NULL, mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ if (myrpt->callmode) {
+ hastx = 1;
+ res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ }
+ l = linkbase.next;
+ while (l != &linkbase) {
+ hastx = 1;
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel, l->name, NULL, mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ res = ast_streamfile(mychannel, ((l->mode) ?
+ "rpt/tranceive" : "rpt/monitor"), mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ l = l->next;
+ }
+ if (!hastx) {
+ res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ }
+ /* destroy our local link queue */
+ l = linkbase.next;
+ while (l != &linkbase) {
+ m = l;
+ l = l->next;
+ remque((struct qelem *)m);
+ ast_free(m);
+ }
+ imdone = 1;
+ break;
+
+ case LASTNODEKEY: /* Identify last node which keyed us up */
+ rpt_mutex_lock(&myrpt->lock);
+ if (myrpt->lastnodewhichkeyedusup)
+ p = ast_strdupa(myrpt->lastnodewhichkeyedusup); /* Make a local copy of the node name */
+ else
+ p = NULL;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (!p) {
+ imdone = 1; /* no node previously keyed us up, or the node which did has been disconnected */
+ break;
+ }
+ wait_interval(myrpt, DLY_TELEM, mychannel);
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel, p, NULL, mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ imdone = 1;
+ break;
+
+ case TIMEOUT:
+ res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ ast_stopstream(mychannel);
+ ast_say_character_str(mychannel, myrpt->name, NULL, mychannel->language);
+ res = ast_streamfile(mychannel, "rpt/timeout", mychannel->language);
+ break;
+
+ case STATS_TIME:
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ tv = ast_tvnow();
+ ast_localtime(&tv, &localtm, NULL);
+ /* Say the phase of the day is before the time */
+ if ((localtm.tm_hour >= 0) && (localtm.tm_hour < 12))
+ p = "rpt/goodmorning";
+ else if ((localtm.tm_hour >= 12) && (localtm.tm_hour < 18))
+ p = "rpt/goodafternoon";
+ else
+ p = "rpt/goodevening";
+ if (sayfile(mychannel, p) == -1) {
+ imdone = 1;
+ break;
+ }
+ /* Say the time is ... */
+ if (sayfile(mychannel, "rpt/thetimeis") == -1) {
+ imdone = 1;
+ break;
+ }
+ /* Say the time */
+ res = ast_say_time(mychannel, tv.tv_sec, "", mychannel->language);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ ast_stopstream(mychannel);
+ imdone = 1;
+ break;
+ case STATS_VERSION:
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ /* Say "version" */
+ if (sayfile(mychannel, "rpt/version") == -1) {
+ imdone = 1;
+ break;
+ }
+ if (!res) /* Say "X" */
+ ast_say_number(mychannel, vmajor, "", mychannel->language, (char *) NULL);
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ ast_stopstream(mychannel);
+ if (saycharstr(mychannel, ".") == -1) {
+ imdone = 1;
+ break;
+ }
+ if (!res) /* Say "Y" */
+ ast_say_number(mychannel, vminor, "", mychannel->language, (char *) NULL);
+ if (!res) {
+ res = ast_waitstream(mychannel, "");
+ ast_stopstream(mychannel);
+ } else
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ imdone = 1;
+ break;
+ case ARB_ALPHA:
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ if (mytele->param)
+ saycharstr(mychannel, mytele->param);
+ imdone = 1;
+ break;
+ case REV_PATCH:
+ wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
+ if (mytele->param) {
+ /* Parts of this section taken from app_parkandannounce */
+ char *tpl_working, *tpl_current;
+ char *tmp[100], *myparm;
+ int looptemp=0, i = 0, dres = 0;
+
+ tpl_working = ast_strdupa(mytele->param);
+ myparm = strsep(&tpl_working, ",");
+ tpl_current = strsep(&tpl_working, ":");
+
+ while (tpl_current && looptemp < sizeof(tmp)) {
+ tmp[looptemp] = tpl_current;
+ looptemp++;
+ tpl_current = strsep(&tpl_working, ":");
+ }
+
+ for (i = 0; i < looptemp; i++) {
+ if (!strcmp(tmp[i], "PARKED")) {
+ ast_say_digits(mychannel, atoi(myparm), "", mychannel->language);
+ } else if (!strcmp(tmp[i], "NODE")) {
+ ast_say_digits(mychannel, atoi(myrpt->name), "", mychannel->language);
+ } else {
+ dres = ast_streamfile(mychannel, tmp[i], mychannel->language);
+ if (!dres) {
+ dres = ast_waitstream(mychannel, "");
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], mychannel->name);
+ dres = 0;
+ }
+ }
+ }
+ }
+ imdone = 1;
+ break;
+ case TEST_TONE:
+ imdone = 1;
+ myrpt->stopgen = 0;
+ if ((res = ast_tonepair_start(mychannel, 1004.0, 0, 99999999, 7200.0)))
+ break;
+ while (mychannel->generatordata && (!myrpt->stopgen)) {
+ if (ast_safe_sleep(mychannel, 1)) break;
+ imdone = 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ myrpt->stopgen = 0;
+ if (!imdone) {
+ if (!res)
+ res = ast_waitstream(mychannel, "");
+ else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+ res = 0;
+ }
+ }
+ ast_stopstream(mychannel);
+ rpt_mutex_lock(&myrpt->lock);
+ if (mytele->mode == TAILMSG) {
+ if (!res) {
+ myrpt->tailmessagen++;
+ if (myrpt->tailmessagen >= myrpt->p.tailmsg.argc)
+ myrpt->tailmessagen = 0;
+ } else {
+ myrpt->tmsgtimer = myrpt->p.tailsquashedtime;
+ }
+ }
+ remque((struct qelem *)mytele);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_free(mytele);
+ ast_hangup(mychannel);
+#ifdef APP_RPT_LOCK_DEBUG
+ sleep(5);
+ ast_mutex_lock(&locklock);
+ t = get_lockthread(pthread_self());
+ if (t)
+ memset(t, 0, sizeof(struct lockthread));
+ ast_mutex_unlock(&locklock);
+#endif
+ pthread_exit(NULL);
+}
+
+static void rpt_telemetry(struct rpt *myrpt, int mode, void *data)
+{
+ struct rpt_tele *tele;
+ struct rpt_link *mylink = (struct rpt_link *) data;
+ int res;
+
+ tele = ast_calloc(1, sizeof(*tele));
+ if (!tele) {
+ ast_log(LOG_WARNING, "Unable to allocate memory\n");
+ pthread_exit(NULL);
+ }
+ tele->rpt = myrpt;
+ tele->mode = mode;
+ rpt_mutex_lock(&myrpt->lock);
+ if ((mode == CONNFAIL) || (mode == REMDISC) || (mode == CONNECTED)) {
+ if (mylink) {
+ memcpy(&tele->mylink, mylink, sizeof(struct rpt_link));
+ }
+ } else if ((mode == ARB_ALPHA) || (mode == REV_PATCH)) {
+ ast_copy_string(tele->param, (char *) data, sizeof(tele->param));
+ }
+ insque((struct qelem *)tele, (struct qelem *)myrpt->tele.next);
+ rpt_mutex_unlock(&myrpt->lock);
+ res = ast_pthread_create_detached(&tele->threadid, NULL, rpt_tele_thread, (void *) tele);
+ if (res != 0) {
+ rpt_mutex_lock(&myrpt->lock);
+ remque((struct qlem *) tele); /* We don't like stuck transmitters, remove it from the queue */
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_WARNING, "Could not create telemetry thread: %s\n", strerror(res));
+ }
+ return;
+}
+
+static void *rpt_call(void *this)
+{
+ ZT_CONFINFO ci; /* conference info */
+ struct rpt *myrpt = (struct rpt *)this;
+ int res;
+ struct ast_frame wf;
+ int stopped, congstarted, dialtimer, lastcidx, aborted;
+ struct ast_channel *mychannel, *genchannel;
+
+ myrpt->mydtmf = 0;
+ /* allocate a pseudo-channel thru asterisk */
+ mychannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!mychannel) {
+ ast_log(LOG_ERROR, "rpt: unable to obtain pseudo channel\n");
+ pthread_exit(NULL);
+ }
+ ci.chan = 0;
+ ci.confno = myrpt->conf; /* use the pseudo conference */
+ ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
+ | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
+ /* first put the channel on the conference */
+ if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(mychannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ /* allocate a pseudo-channel thru asterisk */
+ genchannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!genchannel) {
+ ast_log(LOG_ERROR, "rpt: unable to obtain pseudo channel\n");
+ ast_hangup(mychannel);
+ pthread_exit(NULL);
+ }
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
+ | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
+ /* first put the channel on the conference */
+ if (ioctl(genchannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ if (myrpt->p.tonezone && (tone_zone_set_zone(mychannel->fds[0], myrpt->p.tonezone) == -1)) {
+ ast_log(LOG_WARNING, "Unable to set tone zone %s\n", myrpt->p.tonezone);
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ if (myrpt->p.tonezone && (tone_zone_set_zone(genchannel->fds[0], myrpt->p.tonezone) == -1)) {
+ ast_log(LOG_WARNING, "Unable to set tone zone %s\n", myrpt->p.tonezone);
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ /* start dialtone if patchquiet is 0. Special patch modes don't send dial tone */
+ if ((!myrpt->patchquiet) && (tone_zone_play_tone(mychannel->fds[0], ZT_TONE_DIALTONE) < 0)) {
+ ast_log(LOG_WARNING, "Cannot start dialtone\n");
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ stopped = 0;
+ congstarted = 0;
+ dialtimer = 0;
+ lastcidx = 0;
+ aborted = 0;
+
+ while ((myrpt->callmode == 1) || (myrpt->callmode == 4)) {
+ if ((myrpt->patchdialtime) && (myrpt->callmode == 1) && (myrpt->cidx != lastcidx)) {
+ dialtimer = 0;
+ lastcidx = myrpt->cidx;
+ }
+
+ if ((myrpt->patchdialtime) && (dialtimer >= myrpt->patchdialtime)) {
+ rpt_mutex_lock(&myrpt->lock);
+ aborted = 1;
+ myrpt->callmode = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ break;
+ }
+
+ if ((!myrpt->patchquiet) && (!stopped) && (myrpt->callmode == 1) && (myrpt->cidx > 0)) {
+ stopped = 1;
+ /* stop dial tone */
+ tone_zone_play_tone(mychannel->fds[0], -1);
+ }
+ if (myrpt->callmode == 4) {
+ if (!congstarted) {
+ congstarted = 1;
+ /* start congestion tone */
+ tone_zone_play_tone(mychannel->fds[0], ZT_TONE_CONGESTION);
+ }
+ }
+ res = ast_safe_sleep(mychannel, MSWAIT);
+ if (res < 0) {
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ dialtimer += MSWAIT;
+ }
+ /* stop any tone generation */
+ tone_zone_play_tone(mychannel->fds[0], -1);
+ /* end if done */
+ if (!myrpt->callmode) {
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if ((!myrpt->patchquiet) && aborted)
+ rpt_telemetry(myrpt, TERM, NULL);
+ pthread_exit(NULL);
+ }
+
+ if (myrpt->p.ourcallerid && *myrpt->p.ourcallerid) {
+ char *name, *loc, *instr;
+ instr = ast_strdup(myrpt->p.ourcallerid);
+ if (instr) {
+ ast_callerid_parse(instr, &name, &loc);
+ if (loc) {
+ if (mychannel->cid.cid_num)
+ ast_free(mychannel->cid.cid_num);
+ mychannel->cid.cid_num = ast_strdup(loc);
+ }
+ if (name) {
+ if (mychannel->cid.cid_name)
+ ast_free(mychannel->cid.cid_name);
+ mychannel->cid.cid_name = ast_strdup(name);
+ }
+ ast_free(instr);
+ }
+ }
+
+ ast_copy_string(mychannel->exten, myrpt->exten, sizeof(mychannel->exten));
+ ast_copy_string(mychannel->context, myrpt->patchcontext, sizeof(mychannel->context));
+
+ if (myrpt->p.acctcode)
+ ast_copy_string((char *)mychannel->accountcode, myrpt->p.acctcode, sizeof(mychannel->accountcode));
+ mychannel->priority = 1;
+ ast_channel_undefer_dtmf(mychannel);
+ if (ast_pbx_start(mychannel) < 0) {
+ ast_log(LOG_WARNING, "Unable to start PBX!!\n");
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ usleep(10000);
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 3;
+ /* set appropriate conference for the pseudo */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = (myrpt->p.duplex == 2) ? ZT_CONF_CONFANNMON :
+ (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(mychannel);
+ ast_hangup(genchannel);
+ myrpt->callmode = 0;
+ pthread_exit(NULL);
+ }
+ while (myrpt->callmode) {
+ if ((!mychannel->pbx) && (myrpt->callmode != 4)) {
+ if (myrpt->patchfarenddisconnect) { /* If patch is setup for far end disconnect */
+ myrpt->callmode = 0;
+ if (!myrpt->patchquiet) {
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, TERM, NULL);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ } else { /* Send congestion until patch is downed by command */
+ myrpt->callmode = 4;
+ rpt_mutex_unlock(&myrpt->lock);
+ /* start congestion tone */
+ tone_zone_play_tone(genchannel->fds[0], ZT_TONE_CONGESTION);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ }
+ if (myrpt->mydtmf) {
+ wf.frametype = AST_FRAME_DTMF;
+ wf.subclass = myrpt->mydtmf;
+ wf.offset = 0;
+ wf.mallocd = 0;
+ wf.data = NULL;
+ wf.datalen = 0;
+ wf.samples = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_write(genchannel, &wf);
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->mydtmf = 0;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ usleep(MSWAIT * 1000);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ tone_zone_play_tone(genchannel->fds[0], -1);
+ if (mychannel->pbx)
+ ast_softhangup(mychannel, AST_SOFTHANGUP_DEV);
+ ast_hangup(genchannel);
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ /* set appropriate conference for the pseudo */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? ZT_CONF_CONFANNMON :
+ (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ }
+ pthread_exit(NULL);
+}
+
+static void send_link_dtmf(struct rpt *myrpt, char c)
+{
+ char str[300];
+ struct ast_frame wf;
+ struct rpt_link *l;
+
+ snprintf(str, sizeof(str), "D %s %s %d %c", myrpt->cmdnode, myrpt->name, ++(myrpt->dtmfidx), c);
+ wf.frametype = AST_FRAME_TEXT;
+ wf.subclass = 0;
+ wf.offset = 0;
+ wf.mallocd = 1;
+ wf.datalen = strlen(str) + 1;
+ wf.samples = 0;
+ l = myrpt->links.next;
+ /* first, see if our dude is there */
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ /* if we found it, write it and were done */
+ if (!strcmp(l->name, myrpt->cmdnode)) {
+ wf.data = ast_strdup(str);
+ if (l->chan)
+ ast_write(l->chan, &wf);
+ return;
+ }
+ l = l->next;
+ }
+ l = myrpt->links.next;
+ /* if not, give it to everyone */
+ while (l != &myrpt->links) {
+ wf.data = ast_strdup(str);
+ if (l->chan)
+ ast_write(l->chan, &wf);
+ l = l->next;
+ }
+ return;
+}
+
+/*
+* Internet linking function
+*/
+
+static int function_ilink(struct rpt *myrpt, char *param, char *digits, int command_source, struct rpt_link *mylink)
+{
+ const char *val;
+ char *s, *tele;
+ char deststr[300] = "", modechange = 0;
+ char digitbuf[MAXNODESTR];
+ struct rpt_link *l;
+ int reconnects = 0;
+ ZT_CONFINFO ci; /* conference info */
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(s1);
+ AST_APP_ARG(s2); /* XXX Never used. Scratch? XXX */
+ );
+
+ if (!param)
+ return DC_ERROR;
+
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ ast_copy_string(digitbuf, digits, sizeof(digitbuf));
+ ast_debug(1, "@@@@ ilink param = %s, digitbuf = %s\n", S_OR(param, "(null)"), digitbuf);
+
+ switch (myatoi(param)) {
+ case 1: /* Link off */
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf, myrpt->lastlinknode);
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
+ if (!val) {
+ if (strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name, digitbuf))
+ break;
+ l = l->next;
+ }
+ if (l != &myrpt->links) { /* if found */
+ struct ast_frame wf;
+ ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 1;
+ rpt_mutex_unlock(&myrpt->lock);
+ wf.frametype = AST_FRAME_TEXT;
+ wf.subclass = 0;
+ wf.offset = 0;
+ wf.mallocd = 1;
+ wf.datalen = strlen(discstr) + 1;
+ wf.samples = 0;
+ wf.data = ast_strdup(discstr);
+ if (l->chan) {
+ ast_write(l->chan, &wf);
+ if (ast_safe_sleep(l->chan, 250) == -1)
+ return DC_ERROR;
+ ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
+ }
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ return DC_COMPLETE;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+ case 2: /* Link Monitor */
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf, myrpt->lastlinknode);
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
+ if (!val) {
+ if (strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+ }
+ s = ast_strdupa(val);
+ AST_STANDARD_APP_ARGS(args, s);
+ rpt_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name, digitbuf))
+ break;
+ l = l->next;
+ }
+ /* if found */
+ if (l != &myrpt->links) {
+ /* if already in this mode, just ignore */
+ if ((!l->mode) || (!l->chan)) {
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, REMALREADY, NULL);
+ return DC_COMPLETE;
+ }
+ reconnects = l->reconnects;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (l->chan)
+ ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 2;
+ modechange = 1;
+ } else
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
+ /* establish call in monitor mode */
+ l = ast_calloc(1, sizeof(*l));
+ if (!l) {
+ ast_log(LOG_WARNING, "Unable to malloc\n");
+ return DC_ERROR;
+ }
+ snprintf(deststr, sizeof(deststr), "IAX2/%s", args.s1);
+ tele = strchr(deststr, '/');
+ if (!tele) {
+ ast_log(LOG_ERROR, "link2:Dial number (%s) must be in format tech/number\n", deststr);
+ return DC_ERROR;
+ }
+ *tele++ = 0;
+ l->isremote = (s && ast_true(s));
+ ast_copy_string(l->name, digitbuf, MAXNODESTR);
+ l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
+ if (modechange)
+ l->connected = 1;
+ if (l->chan) {
+ ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
+ l->chan->whentohangup = 0;
+ l->chan->appl = "Apprpt";
+ l->chan->data = "(Remote Rx)";
+ ast_verb(3, "rpt (remote) initiating call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ if (l->chan->cid.cid_num)
+ ast_free(l->chan->cid.cid_num);
+ l->chan->cid.cid_num = ast_strdup(myrpt->name);
+ ast_call(l->chan, tele, 0);
+ } else {
+ rpt_telemetry(myrpt, CONNFAIL, l);
+ ast_free(l);
+ ast_verb(3, "Unable to place call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ return DC_ERROR;
+ }
+ /* allocate a pseudo-channel thru asterisk */
+ l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!l->pchan) {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
+ ast_hangup(l->chan);
+ ast_free(l);
+ return DC_ERROR;
+ }
+ ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
+ /* make a conference for the pseudo-one */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ ast_free(l);
+ return DC_ERROR;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ l->reconnects = reconnects;
+ /* insert at end of queue */
+ insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ return DC_COMPLETE;
+ case 3: /* Link transceive */
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf, myrpt->lastlinknode);
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
+ if (!val) {
+ if (strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+ }
+ s = ast_strdupa(val);
+ AST_STANDARD_APP_ARGS(args, s);
+ rpt_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name, digitbuf))
+ break;
+ l = l->next;
+ }
+ /* if found */
+ if (l != &myrpt->links) {
+ /* if already in this mode, just ignore */
+ if ((l->mode) || (!l->chan)) {
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, REMALREADY, NULL);
+ return DC_COMPLETE;
+ }
+ reconnects = l->reconnects;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (l->chan)
+ ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 2;
+ modechange = 1;
+ } else
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
+ /* establish call in tranceive mode */
+ l = ast_calloc(1, sizeof(*l));
+ if (!l) {
+ ast_log(LOG_WARNING, "Unable to malloc\n");
+ return DC_ERROR;
+ }
+ l->mode = 1;
+ l->outbound = 1;
+ ast_copy_string(l->name, digitbuf, MAXNODESTR);
+ l->isremote = (s && ast_true(s));
+ if (modechange)
+ l->connected = 1;
+ snprintf(deststr, sizeof(deststr), "IAX2/%s", args.s1);
+ tele = strchr(deststr, '/');
+ if (!tele) {
+ ast_log(LOG_ERROR, "link3:Dial number (%s) must be in format tech/number\n", deststr);
+ ast_free(l);
+ return DC_ERROR;
+ }
+ *tele++ = 0;
+ l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
+ if (l->chan) {
+ ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
+ l->chan->whentohangup = 0;
+ l->chan->appl = "Apprpt";
+ l->chan->data = "(Remote Rx)";
+ ast_verb(3, "rpt (remote) initiating call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ if (l->chan->cid.cid_num)
+ ast_free(l->chan->cid.cid_num);
+ l->chan->cid.cid_num = ast_strdup(myrpt->name);
+ ast_call(l->chan, tele, 999);
+ } else {
+ rpt_telemetry(myrpt, CONNFAIL, l);
+ ast_free(l);
+ ast_verb(3, "Unable to place call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ return DC_ERROR;
+ }
+ /* allocate a pseudo-channel thru asterisk */
+ l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!l->pchan) {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
+ ast_hangup(l->chan);
+ ast_free(l);
+ return DC_ERROR;
+ }
+ ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ ast_free(l);
+ return DC_ERROR;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ l->reconnects = reconnects;
+ /* insert at end of queue */
+ insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ return DC_COMPLETE;
+ case 4: /* Enter Command Mode */
+ /* if doesnt allow link cmd, or no links active, return */
+ if (((command_source != SOURCE_RPT) &&
+ (command_source != SOURCE_PHONE) &&
+ (command_source != SOURCE_DPHONE)) ||
+ (myrpt->links.next == &myrpt->links))
+ return DC_COMPLETE;
+ /* if already in cmd mode, or selected self, fughetabahtit */
+ if ((myrpt->cmdnode[0]) || (!strcmp(myrpt->name, digitbuf))) {
+ rpt_telemetry(myrpt, REMALREADY, NULL);
+ return DC_COMPLETE;
+ }
+ if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
+ strcpy(digitbuf, myrpt->lastlinknode);
+ /* node must at least exist in list */
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
+ if (!val) {
+ if (strlen(digitbuf) >= myrpt->longestnode)
+ return DC_ERROR;
+ break;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ strcpy(myrpt->lastlinknode, digitbuf);
+ ast_copy_string(myrpt->cmdnode, digitbuf, sizeof(myrpt->cmdnode));
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, REMGO, NULL);
+ return DC_COMPLETE;
+ case 5: /* Status */
+ rpt_telemetry(myrpt, STATUS, NULL);
+ return DC_COMPLETE;
+ case 6: /* All Links Off */
+ l = myrpt->links.next;
+ while (l != &myrpt->links) { /* This code is broke and needs to be changed to work with the reconnect kludge */
+ if (l->chan)
+ ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); /* Hang 'em up */
+ l = l->next;
+ }
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ break;
+ case 7: /* Identify last node which keyed us up */
+ rpt_telemetry(myrpt, LASTNODEKEY, NULL);
+ break;
+ default:
+ return DC_ERROR;
+ }
+
+ return DC_INDETERMINATE;
+}
+
+/*
+* Autopatch up
+*/
+
+static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ int i, index;
+ char *value = NULL;
+ AST_DECLARE_APP_ARGS(params,
+ AST_APP_ARG(list)[20];
+ );
+
+ static char *keywords[] = {
+ "context",
+ "dialtime",
+ "farenddisconnect",
+ "noct",
+ "quiet",
+ NULL
+ };
+
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ ast_debug(1, "@@@@ Autopatch up\n");
+
+ if (!myrpt->callmode) {
+ /* Set defaults */
+ myrpt->patchnoct = 0;
+ myrpt->patchdialtime = 0;
+ myrpt->patchfarenddisconnect = 0;
+ myrpt->patchquiet = 0;
+ ast_copy_string(myrpt->patchcontext, myrpt->p.ourcontext, sizeof(myrpt->patchcontext));
+
+ if (param) {
+ /* Process parameter list */
+ char *tmp = ast_strdupa(param);
+ AST_STANDARD_APP_ARGS(params, tmp);
+ for (i = 0; i < params.argc; i++) {
+ index = matchkeyword(params.list[i], &value, keywords);
+ if (value)
+ value = skipchars(value, "= ");
+ switch (index) {
+ case 1: /* context */
+ ast_copy_string(myrpt->patchcontext, value, sizeof(myrpt->patchcontext)) ;
+ break;
+ case 2: /* dialtime */
+ myrpt->patchdialtime = atoi(value);
+ break;
+ case 3: /* farenddisconnect */
+ myrpt->patchfarenddisconnect = atoi(value);
+ break;
+ case 4: /* noct */
+ myrpt->patchnoct = atoi(value);
+ break;
+ case 5: /* quiet */
+ myrpt->patchquiet = atoi(value);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ rpt_mutex_lock(&myrpt->lock);
+
+ /* if on call, force * into current audio stream */
+
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
+ myrpt->mydtmf = myrpt->p.funcchar;
+ }
+ if (myrpt->callmode) {
+ rpt_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+ }
+ myrpt->callmode = 1;
+ myrpt->cidx = 0;
+ myrpt->exten[myrpt->cidx] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_pthread_create_detached(&myrpt->rpt_call_thread, NULL, rpt_call, (void *) myrpt);
+ return DC_COMPLETE;
+}
+
+/*
+* Autopatch down
+*/
+
+static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ ast_debug(1, "@@@@ Autopatch down\n");
+
+ rpt_mutex_lock(&myrpt->lock);
+
+ if (!myrpt->callmode) {
+ rpt_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+ }
+
+ myrpt->callmode = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, TERM, NULL);
+ return DC_COMPLETE;
+}
+
+/*
+* Status
+*/
+
+static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+
+ if (!param)
+ return DC_ERROR;
+
+ if (!myrpt->enable)
+ return DC_ERROR;
+
+ ast_debug(1, "@@@@ status param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
+
+ switch (myatoi(param)) {
+ case 1: /* System ID */
+ rpt_telemetry(myrpt, ID1, NULL);
+ return DC_COMPLETE;
+ case 2: /* System Time */
+ rpt_telemetry(myrpt, STATS_TIME, NULL);
+ return DC_COMPLETE;
+ case 3: /* app_rpt.c version */
+ rpt_telemetry(myrpt, STATS_VERSION, NULL);
+ default:
+ return DC_ERROR;
+ }
+
+ /* Never reached */
+ return DC_INDETERMINATE;
+}
+
+/*
+* Macro-oni (without Salami)
+*/
+
+static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+
+ const char *val;
+ int i;
+ struct ast_channel *mychannel;
+
+ if ((!myrpt->remote) && (!myrpt->enable))
+ return DC_ERROR;
+
+ ast_debug(1, "@@@@ macro-oni param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
+
+ mychannel = myrpt->remchannel;
+
+ if (ast_strlen_zero(digitbuf)) /* needs 1 digit */
+ return DC_INDETERMINATE;
+
+ for (i = 0; i < digitbuf[i]; i++) {
+ if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ }
+
+ if (*digitbuf == '0')
+ val = myrpt->p.startupmacro;
+ else
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, digitbuf);
+ /* param was 1 for local buf */
+ if (!val) {
+ rpt_telemetry(myrpt, MACRO_NOTFOUND, NULL);
+ return DC_COMPLETE;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ if ((sizeof(myrpt->macrobuf) - strlen(myrpt->macrobuf)) < strlen(val)) {
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, MACRO_BUSY, NULL);
+ return DC_ERROR;
+ }
+ myrpt->macrotimer = MACROTIME;
+ strncat(myrpt->macrobuf, val, sizeof(myrpt->macrobuf) - 1);
+ rpt_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+}
+
+/*
+* Gosub
+*/
+
+static int function_gosub(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+
+ const char *val;
+ int i;
+ struct ast_channel *mychannel;
+
+ if ((!myrpt->remote) && (!myrpt->enable))
+ return DC_ERROR;
+
+ if (debug)
+ ast_log(LOG_DEBUG, "@@@@ gosub param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
+
+ mychannel = myrpt->remchannel;
+
+ if (ast_strlen_zero(digitbuf)) /* needs 1 digit */
+ return DC_INDETERMINATE;
+
+ for (i = 0; i < digitbuf[i]; i++) {
+ if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ }
+
+ if (*digitbuf == '0')
+ val = myrpt->p.startupgosub;
+ else
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.gosub, digitbuf);
+ /* param was 1 for local buf */
+ if (!val) {
+ rpt_telemetry(myrpt, GOSUB_NOTFOUND, NULL);
+ return DC_COMPLETE;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ if ((sizeof(myrpt->gosubbuf) - strlen(myrpt->gosubbuf)) < strlen(val)) {
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, GOSUB_BUSY, NULL);
+ return DC_ERROR;
+ }
+ myrpt->gosubtimer = GOSUBTIME;
+ strncat(myrpt->gosubbuf, val, sizeof(myrpt->gosubbuf) - 1);
+ rpt_mutex_unlock(&myrpt->lock);
+ return DC_COMPLETE;
+}
+
+/*
+* COP - Control operator
+*/
+
+static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ if (!param)
+ return DC_ERROR;
+
+ switch(myatoi(param)) {
+ case 1: /* System reset */
+ ast_cli_command(STDERR_FILENO, "restart now"); /* A little less drastic than what was previously here. */
+ return DC_COMPLETE;
+ case 2:
+ myrpt->enable = 1;
+ rpt_telemetry(myrpt, ARB_ALPHA, (void *) "RPTENA");
+ return DC_COMPLETE;
+ case 3:
+ myrpt->enable = 0;
+ return DC_COMPLETE;
+ case 4: /* test tone on */
+ rpt_telemetry(myrpt, TEST_TONE, NULL);
+ return DC_COMPLETE;
+ case 5: /* Disgorge variables to log for debug purposes */
+ myrpt->disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
+ return DC_COMPLETE;
+ case 6: /* Simulate COR being activated (phone only) */
+ if (command_source != SOURCE_PHONE)
+ return DC_INDETERMINATE;
+ return DC_DOKEY;
+ }
+ return DC_INDETERMINATE;
+}
+
+/*
+* Collect digits one by one until something matches
+*/
+
+static int collect_function_digits(struct rpt *myrpt, char *digits, int command_source, struct rpt_link *mylink)
+{
+ int i;
+ char *stringp, *functiondigits;
+ char function_table_name[30] = "";
+ struct ast_variable *vp;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(action);
+ AST_APP_ARG(param);
+ );
+
+ ast_debug(1, "@@@@ Digits collected: %s, source: %d\n", digits, command_source);
+
+ if (command_source == SOURCE_DPHONE) {
+ if (!myrpt->p.dphone_functions)
+ return DC_INDETERMINATE;
+ ast_copy_string(function_table_name, myrpt->p.dphone_functions, sizeof(function_table_name));
+ } else if (command_source == SOURCE_PHONE) {
+ if (!myrpt->p.phone_functions)
+ return DC_INDETERMINATE;
+ ast_copy_string(function_table_name, myrpt->p.phone_functions, sizeof(function_table_name));
+ } else if (command_source == SOURCE_LNK)
+ ast_copy_string(function_table_name, myrpt->p.link_functions, sizeof(function_table_name));
+ else
+ ast_copy_string(function_table_name, myrpt->p.functions, sizeof(function_table_name));
+
+ for (vp = ast_variable_browse(myrpt->cfg, function_table_name); vp; vp = vp->next) {
+ if (!strncasecmp(vp->name, digits, strlen(vp->name)))
+ break;
+ }
+ if (!vp) {
+ int n;
+
+ n = myrpt->longestfunc;
+ if (command_source == SOURCE_LNK)
+ n = myrpt->link_longestfunc;
+ else if (command_source == SOURCE_PHONE)
+ n = myrpt->phone_longestfunc;
+ else if (command_source == SOURCE_DPHONE)
+ n = myrpt->dphone_longestfunc;
+
+ if (strlen(digits) >= n)
+ return DC_ERROR;
+ else
+ return DC_INDETERMINATE;
+ }
+
+ /* Found a match, retrieve value part and parse */
+ stringp = ast_strdupa(vp->value);
+ AST_STANDARD_APP_ARGS(args, stringp);
+
+ ast_debug(1, "@@@@ action: %s, param = %s\n", args.action, S_OR(args.param, "(null)"));
+ /* Look up the action */
+ for (i = 0; i < (sizeof(function_table) / sizeof(struct function_table_tag)); i++) {
+ if (!strncasecmp(args.action, function_table[i].action, strlen(args.action)))
+ break;
+ }
+ ast_debug(1, "@@@@ table index i = %d\n", i);
+ if (i == (sizeof(function_table) / sizeof(struct function_table_tag))) {
+ /* Error, action not in table */
+ return DC_ERROR;
+ }
+ if (function_table[i].function == NULL) {
+ /* Error, function undefined */
+ ast_debug(1, "@@@@ NULL for action: %s\n", args.action);
+ return DC_ERROR;
+ }
+ functiondigits = digits + strlen(vp->name);
+ return (*function_table[i].function)(myrpt, args.param, functiondigits, command_source, mylink);
+}
+
+
+static void handle_link_data(struct rpt *myrpt, struct rpt_link *mylink, char *str)
+{
+ char cmd[300] = "", dest[300], src[300], c;
+ int seq, res;
+ struct rpt_link *l;
+ struct ast_frame wf;
+
+ wf.frametype = AST_FRAME_TEXT;
+ wf.subclass = 0;
+ wf.offset = 0;
+ wf.mallocd = 1;
+ wf.datalen = strlen(str) + 1;
+ wf.samples = 0;
+ if (!strcmp(str, discstr)) {
+ mylink->disced = 1;
+ mylink->retries = MAX_RETRIES + 1;
+ ast_softhangup(mylink->chan, AST_SOFTHANGUP_DEV);
+ return;
+ }
+ if (sscanf(str, "%s %s %s %d %c", cmd, dest, src, &seq, &c) != 5) {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
+ return;
+ }
+ if (strcmp(cmd, "D")) {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
+ return;
+ }
+
+ if (dest[0] == '0') {
+ strcpy(dest, myrpt->name);
+ }
+
+ /* if not for me, redistribute to all links */
+ if (strcmp(dest, myrpt->name)) {
+ l = myrpt->links.next;
+ /* see if this is one in list */
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ /* dont send back from where it came */
+ if ((l == mylink) || (!strcmp(l->name, mylink->name))) {
+ l = l->next;
+ continue;
+ }
+ /* if it is, send it and we're done */
+ if (!strcmp(l->name, dest)) {
+ /* send, but not to src */
+ if (strcmp(l->name, src)) {
+ wf.data = ast_strdup(str);
+ if (l->chan)
+ ast_write(l->chan, &wf);
+ }
+ return;
+ }
+ l = l->next;
+ }
+ l = myrpt->links.next;
+ /* otherwise, send it to all of em */
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ /* dont send back from where it came */
+ if ((l == mylink) || (!strcmp(l->name, mylink->name))) {
+ l = l->next;
+ continue;
+ }
+ /* send, but not to src */
+ if (strcmp(l->name, src)) {
+ wf.data = ast_strdup(str);
+ if (l->chan)
+ ast_write(l->chan, &wf);
+ }
+ l = l->next;
+ }
+ return;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ if (c == myrpt->p.endchar)
+ myrpt->stopgen = 1;
+ if (myrpt->callmode == 1) {
+ myrpt->exten[myrpt->cidx++] = c;
+ myrpt->exten[myrpt->cidx] = 0;
+ /* if this exists */
+ if (ast_exists_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
+ myrpt->callmode = 2;
+ if (!myrpt->patchquiet) {
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, PROC, NULL);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ }
+ /* if can continue, do so */
+ if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
+ /* call has failed, inform user */
+ myrpt->callmode = 4;
+ }
+ }
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
+ myrpt->mydtmf = c;
+ }
+ if (c == myrpt->p.funcchar) {
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+ time(&myrpt->rem_dtmf_time);
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+ } else if ((c != myrpt->p.endchar) && (myrpt->rem_dtmfidx >= 0)) {
+ time(&myrpt->rem_dtmf_time);
+ if (myrpt->rem_dtmfidx < MAXDTMF) {
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_copy_string(cmd, myrpt->rem_dtmfbuf, sizeof(cmd));
+ res = collect_function_digits(myrpt, cmd, SOURCE_LNK, mylink);
+ rpt_mutex_lock(&myrpt->lock);
+
+ switch (res) {
+ case DC_INDETERMINATE:
+ break;
+ case DC_REQ_FLUSH:
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[0] = 0;
+ break;
+ case DC_COMPLETE:
+ myrpt->totalexecdcommands++;
+ myrpt->dailyexecdcommands++;
+ ast_copy_string(myrpt->lastdtmfcommand, cmd, MAXDTMF);
+ myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+ case DC_ERROR:
+ default:
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+ }
+ }
+
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+}
+
+static void handle_link_phone_dtmf(struct rpt *myrpt, struct rpt_link *mylink, char c)
+{
+ char cmd[300];
+ int res;
+
+ rpt_mutex_lock(&myrpt->lock);
+ if (c == myrpt->p.endchar) {
+ if (mylink->lastrx) {
+ mylink->lastrx = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ myrpt->stopgen = 1;
+ if (myrpt->cmdnode[0]) {
+ myrpt->cmdnode[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ return;
+ }
+ }
+ if (myrpt->cmdnode[0]) {
+ rpt_mutex_unlock(&myrpt->lock);
+ send_link_dtmf(myrpt, c);
+ return;
+ }
+ if (myrpt->callmode == 1) {
+ myrpt->exten[myrpt->cidx++] = c;
+ myrpt->exten[myrpt->cidx] = 0;
+ /* if this exists */
+ if (ast_exists_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
+ myrpt->callmode = 2;
+ if (!myrpt->patchquiet) {
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, PROC, NULL);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ }
+ /* if can continue, do so */
+ if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
+ /* call has failed, inform user */
+ myrpt->callmode = 4;
+ }
+ }
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
+ myrpt->mydtmf = c;
+ }
+ if (c == myrpt->p.funcchar) {
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+ time(&myrpt->rem_dtmf_time);
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+ } else if ((c != myrpt->p.endchar) && (myrpt->rem_dtmfidx >= 0)) {
+ time(&myrpt->rem_dtmf_time);
+ if (myrpt->rem_dtmfidx < MAXDTMF) {
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
+ myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
+
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_copy_string(cmd, myrpt->rem_dtmfbuf, sizeof(cmd));
+ switch(mylink->phonemode) {
+ case 1:
+ res = collect_function_digits(myrpt, cmd,
+ SOURCE_PHONE, mylink);
+ break;
+ case 2:
+ res = collect_function_digits(myrpt, cmd,
+ SOURCE_DPHONE, mylink);
+ break;
+ default:
+ res = collect_function_digits(myrpt, cmd,
+ SOURCE_LNK, mylink);
+ break;
+ }
+
+ rpt_mutex_lock(&myrpt->lock);
+
+ switch(res) {
+ case DC_INDETERMINATE:
+ break;
+ case DC_DOKEY:
+ mylink->lastrx = 1;
+ break;
+ case DC_REQ_FLUSH:
+ myrpt->rem_dtmfidx = 0;
+ myrpt->rem_dtmfbuf[0] = 0;
+ break;
+ case DC_COMPLETE:
+ myrpt->totalexecdcommands++;
+ myrpt->dailyexecdcommands++;
+ ast_copy_string(myrpt->lastdtmfcommand, cmd, MAXDTMF);
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+ case DC_ERROR:
+ default:
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmf_time = 0;
+ break;
+ }
+ }
+
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+}
+
+/* Doug Hall RBI-1 serial data definitions:
+ *
+ * Byte 0: Expansion external outputs
+ * Byte 1:
+ * Bits 0-3 are BAND as follows:
+ * Bits 4-5 are POWER bits as follows:
+ * 00 - Low Power
+ * 01 - Hi Power
+ * 02 - Med Power
+ * Bits 6-7 are always set
+ * Byte 2:
+ * Bits 0-3 MHZ in BCD format
+ * Bits 4-5 are offset as follows:
+ * 00 - minus
+ * 01 - plus
+ * 02 - simplex
+ * 03 - minus minus (whatever that is)
+ * Bit 6 is the 0/5 KHZ bit
+ * Bit 7 is always set
+ * Byte 3:
+ * Bits 0-3 are 10 KHZ in BCD format
+ * Bits 4-7 are 100 KHZ in BCD format
+ * Byte 4: PL Tone code and encode/decode enable bits
+ * Bits 0-5 are PL tone code (comspec binary codes)
+ * Bit 6 is encode enable/disable
+ * Bit 7 is decode enable/disable
+ */
+
+/* take the frequency from the 10 mhz digits (and up) and convert it
+ to a band number */
+
+static int rbi_mhztoband(char *str)
+{
+ int i;
+
+ i = atoi(str) / 10; /* get the 10's of mhz */
+ switch (i) {
+ case 2:
+ return 10;
+ case 5:
+ return 11;
+ case 14:
+ return 2;
+ case 22:
+ return 3;
+ case 44:
+ return 4;
+ case 124:
+ return 0;
+ case 125:
+ return 1;
+ case 126:
+ return 8;
+ case 127:
+ return 5;
+ case 128:
+ return 6;
+ case 129:
+ return 7;
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* take a PL frequency and turn it into a code */
+static int rbi_pltocode(char *str)
+{
+ int i;
+ char *s;
+
+ s = strchr(str, '.');
+ i = 0;
+ if (s)
+ i = atoi(s + 1);
+ i += atoi(str) * 10;
+ switch(i) {
+ case 670:
+ return 0;
+ case 719:
+ return 1;
+ case 744:
+ return 2;
+ case 770:
+ return 3;
+ case 797:
+ return 4;
+ case 825:
+ return 5;
+ case 854:
+ return 6;
+ case 885:
+ return 7;
+ case 915:
+ return 8;
+ case 948:
+ return 9;
+ case 974:
+ return 10;
+ case 1000:
+ return 11;
+ case 1035:
+ return 12;
+ case 1072:
+ return 13;
+ case 1109:
+ return 14;
+ case 1148:
+ return 15;
+ case 1188:
+ return 16;
+ case 1230:
+ return 17;
+ case 1273:
+ return 18;
+ case 1318:
+ return 19;
+ case 1365:
+ return 20;
+ case 1413:
+ return 21;
+ case 1462:
+ return 22;
+ case 1514:
+ return 23;
+ case 1567:
+ return 24;
+ case 1622:
+ return 25;
+ case 1679:
+ return 26;
+ case 1738:
+ return 27;
+ case 1799:
+ return 28;
+ case 1862:
+ return 29;
+ case 1928:
+ return 30;
+ case 2035:
+ return 31;
+ case 2107:
+ return 32;
+ case 2181:
+ return 33;
+ case 2257:
+ return 34;
+ case 2336:
+ return 35;
+ case 2418:
+ return 36;
+ case 2503:
+ return 37;
+ }
+ return -1;
+}
+
+/*
+* Shift out a formatted serial bit stream
+*/
+
+static void rbi_out_parallel(struct rpt *myrpt, unsigned char *data)
+{
+ int i, j;
+ unsigned char od, d;
+
+ for (i = 0; i < 5; i++) {
+ od = *data++;
+ for (j = 0; j < 8; j++) {
+ d = od & 1;
+ outb(d, myrpt->p.iobase);
+ usleep(15);
+ od >>= 1;
+ outb(d | 2, myrpt->p.iobase);
+ usleep(30);
+ outb(d, myrpt->p.iobase);
+ usleep(10);
+ }
+ }
+ /* >= 50 us */
+ usleep(50);
+}
+
+static void rbi_out(struct rpt *myrpt, unsigned char *data)
+{
+ struct zt_radio_param r = { 0, };
+
+ r.radpar = ZT_RADPAR_REMMODE;
+ r.data = ZT_RADPAR_REM_RBI1;
+ /* if setparam ioctl fails, its probably not a pciradio card */
+ if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &r) == -1) {
+ rbi_out_parallel(myrpt, data);
+ return;
+ }
+ r.radpar = ZT_RADPAR_REMCOMMAND;
+ memcpy(&r.data, data, 5);
+ if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &r) == -1) {
+ ast_log(LOG_WARNING, "Cannot send RBI command for channel %s\n", myrpt->rxchannel->name);
+ return;
+ }
+}
+
+static int serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int txbytes, char *rxbuf, int rxmaxbytes, int asciiflag)
+{
+ int i;
+ struct zt_radio_param prm;
+
+ char *buf = alloca(30 + txbytes * 3);
+ int len;
+ ast_copy_string(buf, "String output was: ", 30 + txbytes * 3);
+ len = strlen(buf);
+ for (i = 0; i < txbytes; i++)
+ len += snprintf(buf + len, 30 + txbytes * 3 - len, "%02X ", (unsigned char) txbuf[i]);
+ strcat(buf + len, "\n");
+ ast_debug(1, "%s", buf);
+
+ prm.radpar = ZT_RADPAR_REMMODE;
+ if (asciiflag)
+ prm.data = ZT_RADPAR_REM_SERIAL_ASCII;
+ else
+ prm.data = ZT_RADPAR_REM_SERIAL;
+ if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &prm) == -1)
+ return -1;
+ prm.radpar = ZT_RADPAR_REMCOMMAND;
+ prm.data = rxmaxbytes;
+ memcpy(prm.buf, txbuf, txbytes);
+ prm.index = txbytes;
+ if (ioctl(myrpt->rxchannel->fds[0], ZT_RADIO_SETPARAM, &prm) == -1)
+ return -1;
+ if (rxbuf) {
+ *rxbuf = 0;
+ memcpy(rxbuf, prm.buf, prm.index);
+ }
+ return(prm.index);
+}
+
+static int setrbi(struct rpt *myrpt)
+{
+ char tmp[MAXREMSTR] = "", *s;
+ unsigned char rbicmd[5];
+ int band, txoffset = 0, txpower = 0, txpl;
+
+ /* must be a remote system */
+ if (!myrpt->remote)
+ return(0);
+ /* must have rbi hardware */
+ if (strncmp(myrpt->remote, remote_rig_rbi, 3))
+ return(0);
+ ast_copy_string(tmp, myrpt->freq, sizeof(tmp));
+ s = strchr(tmp, '.');
+ /* if no decimal, is invalid */
+
+ if (s == NULL) {
+ ast_debug(1, "@@@@ Frequency needs a decimal\n");
+ return -1;
+ }
+
+ *s++ = 0;
+ if (strlen(tmp) < 2) {
+ ast_debug(1, "@@@@ Bad MHz digits: %s\n", tmp);
+ return -1;
+ }
+
+ if (strlen(s) < 3) {
+ ast_debug(1, "@@@@ Bad KHz digits: %s\n", s);
+ return -1;
+ }
+
+ if ((s[2] != '0') && (s[2] != '5')) {
+ ast_debug(1, "@@@@ KHz must end in 0 or 5: %c\n", s[2]);
+ return -1;
+ }
+
+ band = rbi_mhztoband(tmp);
+ if (band == -1) {
+ ast_debug(1, "@@@@ Bad Band: %s\n", tmp);
+ return -1;
+ }
+
+ txpl = rbi_pltocode(myrpt->txpl);
+
+ if (txpl == -1) {
+ ast_debug(1, "@@@@ Bad TX PL: %s\n", myrpt->txpl);
+ return -1;
+ }
+
+
+ switch (myrpt->offset) {
+ case REM_MINUS:
+ txoffset = 0;
+ break;
+ case REM_PLUS:
+ txoffset = 0x10;
+ break;
+ case REM_SIMPLEX:
+ txoffset = 0x20;
+ break;
+ }
+ switch(myrpt->powerlevel) {
+ case REM_LOWPWR:
+ txpower = 0;
+ break;
+ case REM_MEDPWR:
+ txpower = 0x20;
+ break;
+ case REM_HIPWR:
+ txpower = 0x10;
+ break;
+ }
+ rbicmd[0] = 0;
+ rbicmd[1] = band | txpower | 0xc0;
+ rbicmd[2] = (*(s - 2) - '0') | txoffset | 0x80;
+ if (s[2] == '5')
+ rbicmd[2] |= 0x40;
+ rbicmd[3] = ((*s - '0') << 4) + (s[1] - '0');
+ rbicmd[4] = txpl;
+ if (myrpt->txplon)
+ rbicmd[4] |= 0x40;
+ if (myrpt->rxplon)
+ rbicmd[4] |= 0x80;
+ rbi_out(myrpt, rbicmd);
+ return 0;
+}
+
+
+/* Check for valid rbi frequency */
+/* Hard coded limits now, configurable later, maybe? */
+
+static int check_freq_rbi(int m, int d, int *defmode)
+{
+ int dflmd = REM_MODE_FM;
+
+ if (m == 50) { /* 6 meters */
+ if (d < 10100)
+ return -1;
+ } else if ((m >= 51) && ( m < 54)) {
+ /* nada */
+ } else if (m == 144) { /* 2 meters */
+ if (d < 10100)
+ return -1;
+ } else if ((m >= 145) && (m < 148)) {
+ /* nada */
+ } else if ((m >= 222) && (m < 225)) { /* 1.25 meters */
+ /* nada */
+ } else if ((m >= 430) && (m < 450)) { /* 70 centimeters */
+ /* nada */
+ } else if ((m >= 1240) && (m < 1300)) { /* 23 centimeters */
+ /* nada */
+ } else
+ return -1;
+
+ if (defmode)
+ *defmode = dflmd;
+
+ return 0;
+}
+
+static int split_decimal(char *input, int *ints, int *decs, int places)
+{
+ double input2 = 0.0;
+ long long modifier = (long long)pow(10.0, (double)places);
+ if (sscanf(input, "%lf", &input2) == 1) {
+ long long input3 = input2 * modifier;
+ *ints = input3 / modifier;
+ *decs = input3 % modifier;
+ return 0;
+ } else
+ return -1;
+}
+
+/*
+* Split frequency into mhz and decimals
+*/
+
+#define split_freq(mhz, decimal, freq) split_decimal(freq, mhz, decimal, 5)
+
+/*
+* Split ctcss frequency into hertz and decimal
+*/
+
+#define split_ctcss_freq(hertz, decimal, freq) split_decimal(freq, hertz, decimal, 1)
+
+/*
+* FT-897 I/O handlers
+*/
+
+/* Check to see that the frequency is valid */
+/* Hard coded limits now, configurable later, maybe? */
+
+static int check_freq_ft897(int m, int d, int *defmode)
+{
+ int dflmd = REM_MODE_FM;
+
+ if (m == 1) { /* 160 meters */
+ dflmd = REM_MODE_LSB;
+ if (d < 80001)
+ return -1;
+ } else if (m == 3) { /* 80 meters */
+ dflmd = REM_MODE_LSB;
+ if (d < 75001)
+ return -1;
+ } else if (m == 7) { /* 40 meters */
+ dflmd = REM_MODE_LSB;
+ if ((d < 15001) || (d > 29999))
+ return -1;
+ } else if (m == 14) { /* 20 meters */
+ dflmd = REM_MODE_USB;
+ if ((d < 15001) || (d > 34999))
+ return -1;
+ } else if (m == 18) { /* 17 meters */
+ dflmd = REM_MODE_USB;
+ if ((d < 11001) || (d > 16797))
+ return -1;
+ } else if (m == 21) { /* 15 meters */
+ dflmd = REM_MODE_USB;
+ if ((d < 20001) || (d > 44999))
+ return -1;
+ } else if (m == 24) { /* 12 meters */
+ dflmd = REM_MODE_USB;
+ if ((d < 93001) || (d > 98999))
+ return -1;
+ } else if (m == 28) { /* 10 meters */
+ dflmd = REM_MODE_USB;
+ if (d < 30001)
+ return -1;
+ } else if (m == 29) {
+ if (d >= 51000)
+ dflmd = REM_MODE_FM;
+ else
+ dflmd = REM_MODE_USB;
+ if (d > 69999)
+ return -1;
+ } else if (m == 50) { /* 6 meters */
+ if (d < 10100)
+ return -1;
+ if (d >= 30000)
+ dflmd = REM_MODE_FM;
+ else
+ dflmd = REM_MODE_USB;
+ } else if ((m >= 51) && ( m < 54)) {
+ dflmd = REM_MODE_FM;
+ } else if (m == 144) { /* 2 meters */
+ if (d < 10100)
+ return -1;
+ if (d >= 30000)
+ dflmd = REM_MODE_FM;
+ else
+ dflmd = REM_MODE_USB;
+ } else if ((m >= 145) && (m < 148)) {
+ dflmd = REM_MODE_FM;
+ } else if ((m >= 430) && (m < 450)) { /* 70 centimeters */
+ if (m < 438)
+ dflmd = REM_MODE_USB;
+ else
+ dflmd = REM_MODE_FM;
+ ;
+ } else
+ return -1;
+
+ if (defmode)
+ *defmode = dflmd;
+
+ return 0;
+}
+
+/*
+* Set a new frequency for the FT897
+*/
+
+static int set_freq_ft897(struct rpt *myrpt, char *newfreq)
+{
+ unsigned char cmdstr[5];
+ int fd, m, d;
+
+ fd = 0;
+ ast_debug(1, "New frequency: %s\n", newfreq);
+
+ if (split_freq(&m, &d, newfreq))
+ return -1;
+
+ /* The FT-897 likes packed BCD frequencies */
+
+ cmdstr[0] = ((m / 100) << 4) + ((m % 100) / 10); /* 100MHz 10Mhz */
+ cmdstr[1] = ((m % 10) << 4) + (d / 10000); /* 1MHz 100KHz */
+ cmdstr[2] = (((d % 10000) / 1000) << 4) + ((d % 1000) / 100); /* 10KHz 1KHz */
+ cmdstr[3] = (((d % 100) / 10) << 4) + (d % 10); /* 100Hz 10Hz */
+ cmdstr[4] = 0x01; /* command */
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+/* ft-897 simple commands */
+
+static int simple_command_ft897(struct rpt *myrpt, char command)
+{
+ unsigned char cmdstr[5] = { 0, 0, 0, 0, command };
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+/* ft-897 offset */
+
+static int set_offset_ft897(struct rpt *myrpt, char offset)
+{
+ unsigned char cmdstr[5] = "";
+
+ switch (offset) {
+ case REM_SIMPLEX:
+ cmdstr[0] = 0x89;
+ break;
+ case REM_MINUS:
+ cmdstr[0] = 0x09;
+ break;
+ case REM_PLUS:
+ cmdstr[0] = 0x49;
+ break;
+ default:
+ return -1;
+ }
+
+ cmdstr[4] = 0x09;
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+/* ft-897 mode */
+
+static int set_mode_ft897(struct rpt *myrpt, char newmode)
+{
+ unsigned char cmdstr[5] = { 0, 0, 0, 0, 0x07 };
+
+ switch (newmode) {
+ case REM_MODE_FM:
+ cmdstr[0] = 0x08;
+ break;
+ case REM_MODE_USB:
+ cmdstr[0] = 0x01;
+ break;
+ case REM_MODE_LSB:
+ cmdstr[0] = 0x00;
+ break;
+ case REM_MODE_AM:
+ cmdstr[0] = 0x04;
+ break;
+ default:
+ return -1;
+ }
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+/* Set tone encode and decode modes */
+
+static int set_ctcss_mode_ft897(struct rpt *myrpt, char txplon, char rxplon)
+{
+ unsigned char cmdstr[5] = { 0, 0, 0, 0, 0x0A };
+
+ if (rxplon && txplon)
+ cmdstr[0] = 0x2A; /* Encode and Decode */
+ else if (!rxplon && txplon)
+ cmdstr[0] = 0x4A; /* Encode only */
+ else if (rxplon && !txplon)
+ cmdstr[0] = 0x3A; /* Encode only */
+ else
+ cmdstr[0] = 0x8A; /* OFF */
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+
+/* Set transmit and receive ctcss tone frequencies */
+
+static int set_ctcss_freq_ft897(struct rpt *myrpt, char *txtone, char *rxtone)
+{
+ unsigned char cmdstr[5] = { 0, 0, 0, 0, 0x0B };
+ int hertz, decimal;
+
+ if (split_ctcss_freq(&hertz, &decimal, txtone))
+ return -1;
+
+ cmdstr[0] = ((hertz / 100) << 4) + (hertz % 100) / 10;
+ cmdstr[1] = ((hertz % 10) << 4) + (decimal % 10);
+
+ if (rxtone) {
+ if (split_ctcss_freq(&hertz, &decimal, rxtone))
+ return -1;
+
+ cmdstr[2] = ((hertz / 100) << 4) + (hertz % 100)/ 10;
+ cmdstr[3] = ((hertz % 10) << 4) + (decimal % 10);
+ }
+
+ return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0);
+}
+
+
+
+static int set_ft897(struct rpt *myrpt)
+{
+ int res;
+
+ ast_debug(1, "@@@@ lock on\n");
+
+ res = simple_command_ft897(myrpt, 0x00); /* LOCK on */
+
+ ast_debug(1, "@@@@ ptt off\n");
+
+ if (!res)
+ res = simple_command_ft897(myrpt, 0x88); /* PTT off */
+
+ ast_debug(1, "Modulation mode\n");
+
+ if (!res)
+ res = set_mode_ft897(myrpt, myrpt->remmode); /* Modulation mode */
+
+ ast_debug(1, "Split off\n");
+
+ if (!res)
+ simple_command_ft897(myrpt, 0x82); /* Split off */
+
+ ast_debug(1, "Frequency\n");
+
+ if (!res)
+ res = set_freq_ft897(myrpt, myrpt->freq); /* Frequency */
+ if ((myrpt->remmode == REM_MODE_FM)) {
+ ast_debug(1, "Offset\n");
+ if (!res)
+ res = set_offset_ft897(myrpt, myrpt->offset); /* Offset if FM */
+ if ((!res)&&(myrpt->rxplon || myrpt->txplon)) {
+ ast_debug(1, "CTCSS tone freqs.\n");
+ res = set_ctcss_freq_ft897(myrpt, myrpt->txpl, myrpt->rxpl); /* CTCSS freqs if CTCSS is enabled */
+ }
+ if (!res) {
+ ast_debug(1, "CTCSS mode\n");
+ res = set_ctcss_mode_ft897(myrpt, myrpt->txplon, myrpt->rxplon); /* CTCSS mode */
+ }
+ }
+ if ((myrpt->remmode == REM_MODE_USB)||(myrpt->remmode == REM_MODE_LSB)) {
+ ast_debug(1, "Clarifier off\n");
+ simple_command_ft897(myrpt, 0x85); /* Clarifier off if LSB or USB */
+ }
+ return res;
+}
+
+static int closerem_ft897(struct rpt *myrpt)
+{
+ simple_command_ft897(myrpt, 0x88); /* PTT off */
+ return 0;
+}
+
+/*
+* Bump frequency up or down by a small amount
+* Return 0 if the new frequnecy is valid, or -1 if invalid
+* Interval is in Hz, resolution is 10Hz
+*/
+
+static int multimode_bump_freq_ft897(struct rpt *myrpt, int interval)
+{
+ int m, d;
+
+ ast_debug(1, "Before bump: %s\n", myrpt->freq);
+
+ if (split_freq(&m, &d, myrpt->freq))
+ return -1;
+
+ d += (interval / 10); /* 10Hz resolution */
+ if (d < 0) {
+ m--;
+ d += 100000;
+ } else if (d >= 100000) {
+ m++;
+ d -= 100000;
+ }
+
+ if (check_freq_ft897(m, d, NULL)) {
+ ast_debug(1, "Bump freq invalid\n");
+ return -1;
+ }
+
+ snprintf(myrpt->freq, MAXREMSTR, "%d.%05d", m, d);
+ ast_debug(1, "After bump: %s\n", myrpt->freq);
+
+ return set_freq_ft897(myrpt, myrpt->freq);
+}
+
+
+
+/*
+* Dispatch to correct I/O handler
+*/
+
+static int setrem(struct rpt *myrpt)
+{
+ return 0; /* XXX BROKEN!! */
+ if (!strcmp(myrpt->remote, remote_rig_ft897))
+ return set_ft897(myrpt);
+ else if (!strcmp(myrpt->remote, remote_rig_rbi))
+ return setrbi(myrpt);
+ else
+ return -1;
+}
+
+static int closerem(struct rpt *myrpt)
+{
+ return 0; /* XXX BROKEN!! */
+ if (!strcmp(myrpt->remote, remote_rig_ft897))
+ return closerem_ft897(myrpt);
+ else
+ return 0;
+}
+
+/*
+* Dispatch to correct frequency checker
+*/
+
+static int check_freq(struct rpt *myrpt, int m, int d, int *defmode)
+{
+ if (!strcmp(myrpt->remote, remote_rig_ft897))
+ return check_freq_ft897(m, d, defmode);
+ else if (!strcmp(myrpt->remote, remote_rig_rbi))
+ return check_freq_rbi(m, d, defmode);
+ else
+ return -1;
+}
+
+/*
+* Return 1 if rig is multimode capable
+*/
+
+static int multimode_capable(struct rpt *myrpt)
+{
+ if (!strcmp(myrpt->remote, remote_rig_ft897))
+ return 1;
+ return 0;
+}
+
+/*
+* Dispatch to correct frequency bumping function
+*/
+
+static int multimode_bump_freq(struct rpt *myrpt, int interval)
+{
+ if (!strcmp(myrpt->remote, remote_rig_ft897))
+ return multimode_bump_freq_ft897(myrpt, interval);
+ else
+ return -1;
+}
+
+
+/*
+* Queue announcment that scan has been stopped
+*/
+
+static void stop_scan(struct rpt *myrpt, int flag)
+{
+ myrpt->hfscanmode = 0;
+ myrpt->hfscanstatus = ((flag) ? -2 : -1);
+}
+
+/*
+* This is called periodically when in scan mode
+*/
+
+static int service_scan(struct rpt *myrpt)
+{
+ int res, interval, mhz, decimals;
+ char k10=0, k100=0;
+
+ switch (myrpt->hfscanmode) {
+
+ case HF_SCAN_DOWN_SLOW:
+ interval = -10; /* 100Hz /sec */
+ break;
+
+ case HF_SCAN_DOWN_QUICK:
+ interval = -50; /* 500Hz /sec */
+ break;
+
+ case HF_SCAN_DOWN_FAST:
+ interval = -200; /* 2KHz /sec */
+ break;
+
+ case HF_SCAN_UP_SLOW:
+ interval = 10; /* 100Hz /sec */
+ break;
+
+ case HF_SCAN_UP_QUICK:
+ interval = 50; /* 500 Hz/sec */
+ break;
+
+ case HF_SCAN_UP_FAST:
+ interval = 200; /* 2KHz /sec */
+ break;
+
+ default:
+ myrpt->hfscanmode = 0; /* Huh? */
+ return -1;
+ }
+
+ res = split_freq(&mhz, &decimals, myrpt->freq);
+
+ if (!res) {
+ k100 = decimals / 10000;
+ k10 = (decimals / 1000) % 10;
+ res = multimode_bump_freq(myrpt, interval);
+ }
+
+ if (!res)
+ res = split_freq(&mhz, &decimals, myrpt->freq);
+
+ if (res) {
+ stop_scan(myrpt, 1);
+ return -1;
+ }
+
+ /* Announce 10KHz boundaries */
+ if (k10 != (decimals / 1000) % 10) {
+ int myhund = (interval < 0) ? k100 : decimals / 10000;
+ int myten = (interval < 0) ? k10 : (decimals / 1000) % 10;
+ myrpt->hfscanstatus = (myten == 0) ? (myhund) * 100 : (myten) * 10;
+ }
+ return res;
+
+}
+
+
+static int rmt_telem_start(struct rpt *myrpt, struct ast_channel *chan, int delay)
+{
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ ast_indicate(chan, AST_CONTROL_RADIO_KEY);
+ if (ast_safe_sleep(chan, delay) == -1)
+ return -1;
+ return 0;
+}
+
+
+static int rmt_telem_finish(struct rpt *myrpt, struct ast_channel *chan)
+{
+ struct zt_params par;
+
+ if (ioctl(myrpt->txchannel->fds[0], ZT_GET_PARAMS, &par) == -1) {
+ return -1;
+
+ }
+ if (!par.rxisoffhook) {
+ ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_UNKEY);
+ myrpt->remoterx = 0;
+ } else {
+ myrpt->remoterx = 1;
+ }
+ return 0;
+}
+
+
+static int rmt_sayfile(struct rpt *myrpt, struct ast_channel *chan, int delay, char *filename)
+{
+ int res;
+
+ res = rmt_telem_start(myrpt, chan, delay);
+
+ if (!res)
+ res = sayfile(chan, filename);
+
+ if (!res)
+ res = rmt_telem_finish(myrpt, chan);
+ return res;
+}
+
+static int rmt_saycharstr(struct rpt *myrpt, struct ast_channel *chan, int delay, char *charstr)
+{
+ int res;
+
+ res = rmt_telem_start(myrpt, chan, delay);
+
+ if (!res)
+ res = saycharstr(chan, charstr);
+
+ if (!res)
+ res = rmt_telem_finish(myrpt, chan);
+ return res;
+}
+
+/*
+* Remote base function
+*/
+
+static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+ char *s, *modestr;
+ const char *val;
+ int i, j, ht, k, l, ls2, res, offset, offsave, modesave, defmode;
+ char multimode = 0;
+ char oc;
+ char tmp[20], freq[20] = "", savestr[20] = "";
+ int mhz = 0, decimals = 0;
+ struct ast_channel *mychannel;
+ AST_DECLARE_APP_ARGS(args1,
+ AST_APP_ARG(freq);
+ AST_APP_ARG(xpl);
+ AST_APP_ARG(mode);
+ );
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(s1);
+ AST_APP_ARG(s2);
+ );
+
+ if ((!param) || (command_source == SOURCE_RPT) || (command_source == SOURCE_LNK))
+ return DC_ERROR;
+
+ multimode = multimode_capable(myrpt);
+ mychannel = myrpt->remchannel;
+
+ switch (myatoi(param)) {
+ case 1: /* retrieve memory */
+ if (strlen(digitbuf) < 2) /* needs 2 digits */
+ break;
+
+ for (i = 0 ; i < 2 ; i++) {
+ if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ }
+
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.memory, digitbuf);
+ if (!val) {
+ if (ast_safe_sleep(mychannel, 1000) == -1)
+ return DC_ERROR;
+ sayfile(mychannel, "rpt/memory_notfound");
+ return DC_COMPLETE;
+ }
+ s = ast_strdupa(val);
+ AST_STANDARD_APP_ARGS(args1, s);
+ if (args1.argc < 3)
+ return DC_ERROR;
+ ast_copy_string(myrpt->freq, args1.freq, sizeof(myrpt->freq));
+ ast_copy_string(myrpt->rxpl, args1.xpl, sizeof(myrpt->rxpl));
+ ast_copy_string(myrpt->txpl, args1.xpl, sizeof(myrpt->rxpl));
+ myrpt->remmode = REM_MODE_FM;
+ myrpt->offset = REM_SIMPLEX;
+ myrpt->powerlevel = REM_MEDPWR;
+ myrpt->txplon = myrpt->rxplon = 0;
+ modestr = args1.mode;
+ while (*modestr) {
+ switch (*modestr++) {
+ case 'A':
+ case 'a':
+ strcpy(myrpt->rxpl, "100.0");
+ strcpy(myrpt->txpl, "100.0");
+ myrpt->remmode = REM_MODE_AM;
+ break;
+
+ case 'B':
+ case 'b':
+ strcpy(myrpt->rxpl, "100.0");
+ strcpy(myrpt->txpl, "100.0");
+ myrpt->remmode = REM_MODE_LSB;
+ break;
+
+ case 'F':
+ myrpt->remmode = REM_MODE_FM;
+ break;
+
+ case 'L':
+ case 'l':
+ myrpt->powerlevel = REM_LOWPWR;
+ break;
+ case 'H':
+ case 'h':
+ myrpt->powerlevel = REM_HIPWR;
+ break;
+
+ case 'M':
+ case 'm':
+ myrpt->powerlevel = REM_MEDPWR;
+ break;
+
+ case '-':
+ myrpt->offset = REM_MINUS;
+ break;
+
+ case '+':
+ myrpt->offset = REM_PLUS;
+ break;
+
+ case 'S':
+ case 's':
+ myrpt->offset = REM_SIMPLEX;
+ break;
+
+ case 'T':
+ case 't':
+ myrpt->txplon = 1;
+ break;
+
+ case 'R':
+ case 'r':
+ myrpt->rxplon = 1;
+ break;
+
+ case 'U':
+ case 'u':
+ strcpy(myrpt->rxpl, "100.0");
+ strcpy(myrpt->txpl, "100.0");
+ myrpt->remmode = REM_MODE_USB;
+ break;
+ }
+ }
+
+ if (setrem(myrpt) == -1)
+ return DC_ERROR;
+
+ return DC_COMPLETE;
+
+ case 2: /* set freq and offset */
+ for (i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++) { /* look for M+*K+*O or M+*H+* depending on mode */
+ if (digitbuf[i] == '*') {
+ j++;
+ continue;
+ }
+ if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ goto invalid_freq;
+ else {
+ if (j == 0)
+ l++; /* # of digits before first * */
+ if (j == 1)
+ k++; /* # of digits after first * */
+ }
+ }
+
+ i = strlen(digitbuf) - 1;
+ if (multimode) {
+ if ((j > 2) || (l > 3) || (k > 6))
+ goto invalid_freq; /* &^@#! */
+ } else {
+ if ((j > 2) || (l > 4) || (k > 3))
+ goto invalid_freq; /* &^@#! */
+ }
+
+ /* Wait for M+*K+* */
+
+ if (j < 2)
+ break; /* Not yet */
+
+ /* We have a frequency */
+
+ s = ast_strdupa(digitbuf);
+ AST_NONSTANDARD_APP_ARGS(args, s, '*');
+ ls2 = strlen(args.s2);
+
+ switch (ls2) { /* Allow partial entry of khz and hz digits for laziness support */
+ case 1:
+ ht = 0;
+ k = 100 * atoi(args.s2);
+ break;
+ case 2:
+ ht = 0;
+ k = 10 * atoi(args.s2);
+ break;
+ case 3:
+ if (!multimode) {
+ if ((args.s2[2] != '0') && (args.s2[2] != '5'))
+ goto invalid_freq;
+ }
+ ht = 0;
+ k = atoi(args.s2);
+ break;
+ case 4:
+ k = atoi(args.s2) / 10;
+ ht = 10 * (atoi(args.s2 + (ls2 - 1)));
+ break;
+ case 5:
+ k = atoi(args.s2) / 100;
+ ht = (atoi(args.s2 + (ls2 - 2)));
+ break;
+ default:
+ goto invalid_freq;
+ }
+
+ /* Check frequency for validity and establish a default mode */
+
+ snprintf(freq, sizeof(freq), "%s.%03d%02d", args.s1, k, ht);
+ ast_debug(1, "New frequency: %s\n", freq);
+
+ split_freq(&mhz, &decimals, freq);
+
+ if (check_freq(myrpt, mhz, decimals, &defmode)) /* Check to see if frequency entered is legit */
+ goto invalid_freq;
+
+ if ((defmode == REM_MODE_FM) && (digitbuf[i] == '*')) /* If FM, user must enter and additional offset digit */
+ break; /* Not yet */
+
+ offset = REM_SIMPLEX; /* Assume simplex */
+
+ if (defmode == REM_MODE_FM) {
+ oc = *s; /* Pick off offset */
+ if (oc) {
+ switch (oc) {
+ case '1':
+ offset = REM_MINUS;
+ break;
+ case '2':
+ offset = REM_SIMPLEX;
+ break;
+ case '3':
+ offset = REM_PLUS;
+ break;
+ default:
+ goto invalid_freq;
+ }
+ }
+ }
+ offsave = myrpt->offset;
+ modesave = myrpt->remmode;
+ ast_copy_string(savestr, myrpt->freq, sizeof(savestr));
+ ast_copy_string(myrpt->freq, freq, sizeof(myrpt->freq));
+ myrpt->offset = offset;
+ myrpt->remmode = defmode;
+
+ if (setrem(myrpt) == -1) {
+ myrpt->offset = offsave;
+ myrpt->remmode = modesave;
+ ast_copy_string(myrpt->freq, savestr, sizeof(myrpt->freq));
+ goto invalid_freq;
+ }
+
+ return DC_COMPLETE;
+
+invalid_freq:
+ rmt_sayfile(myrpt, mychannel, 1000, "rpt/invalid-freq");
+
+ return DC_ERROR;
+
+ case 3: /* set rx PL tone */
+
+ for (i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++) { /* look for N+*N */
+ if (digitbuf[i] == '*') {
+ j++;
+ continue;
+ }
+ if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ else {
+ if (j)
+ l++;
+ else
+ k++;
+ }
+ }
+ if ((j > 1) || (k > 3) || (l > 1))
+ return DC_ERROR; /* &$@^! */
+ i = strlen(digitbuf) - 1;
+ if ((j != 1) || (k < 2)|| (l != 1))
+ break; /* Not yet */
+ ast_debug(1, "PL digits entered %s\n", digitbuf);
+
+ ast_copy_string(tmp, digitbuf, sizeof(tmp));
+ /* see if we have at least 1 */
+ s = strchr(tmp, '*');
+ if (s)
+ *s = '.';
+ ast_copy_string(savestr, myrpt->rxpl, sizeof(savestr));
+ ast_copy_string(myrpt->rxpl, tmp, sizeof(myrpt->rxpl));
+
+ if (setrem(myrpt) == -1) {
+ ast_copy_string(myrpt->rxpl, savestr, sizeof(myrpt->rxpl));
+ return DC_ERROR;
+ }
+
+ return DC_COMPLETE;
+ case 4: /* set tx PL tone */
+ for (i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++) { /* look for N+*N */
+ if (digitbuf[i] == '*') {
+ j++;
+ continue;
+ }
+ if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+ return DC_ERROR;
+ else {
+ if (j)
+ l++;
+ else
+ k++;
+ }
+ }
+ if ((j > 1) || (k > 3) || (l > 1))
+ return DC_ERROR; /* &$@^! */
+ i = strlen(digitbuf) - 1;
+ if ((j != 1) || (k < 2)|| (l != 1))
+ break; /* Not yet */
+ ast_debug(1, "PL digits entered %s\n", digitbuf);
+
+ ast_copy_string(tmp, digitbuf, sizeof(tmp));
+ /* see if we have at least 1 */
+ s = strchr(tmp, '*');
+ if (s)
+ *s = '.';
+ ast_copy_string(savestr, myrpt->txpl, sizeof(savestr));
+ ast_copy_string(myrpt->txpl, tmp, sizeof(myrpt->txpl));
+
+ if (setrem(myrpt) == -1) {
+ ast_copy_string(myrpt->txpl, savestr, sizeof(myrpt->txpl));
+ return DC_ERROR;
+ }
+
+ return DC_COMPLETE;
+
+ case 6: /* MODE (FM,USB,LSB,AM) */
+ if (strlen(digitbuf) < 1)
+ break;
+
+ if (!multimode)
+ return DC_ERROR; /* Multimode radios only */
+
+ switch (*digitbuf) {
+ case '1':
+ split_freq(&mhz, &decimals, myrpt->freq);
+ if (mhz < 29) /* No FM allowed below 29MHz! */
+ return DC_ERROR;
+ myrpt->remmode = REM_MODE_FM;
+ res = rmt_saycharstr(myrpt, mychannel, 1000, "FM");
+ break;
+
+ case '2':
+ myrpt->remmode = REM_MODE_USB;
+ res = rmt_saycharstr(myrpt, mychannel, 1000, "USB");
+ break;
+
+ case '3':
+ myrpt->remmode = REM_MODE_LSB;
+ res = rmt_saycharstr(myrpt, mychannel, 1000, "LSB");
+ break;
+
+ case '4':
+ myrpt->remmode = REM_MODE_AM;
+ res = rmt_saycharstr(myrpt, mychannel, 1000, "AM");
+ break;
+
+ default:
+ return DC_ERROR;
+ }
+ if (res)
+ return DC_ERROR;
+
+ if (setrem(myrpt))
+ return DC_ERROR;
+ return DC_COMPLETE;
+
+ case 100: /* other stuff */
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ res = rmt_telem_start(myrpt, mychannel, 1000);
+ switch (myatoi(param)) { /* Quick commands requiring a setrem call */
+ case 100: /* RX PL Off */
+ myrpt->rxplon = 0;
+ if (!res)
+ res = sayfile(mychannel, "rpt/rxpl");
+ if (!res)
+ sayfile(mychannel, "rpt/off");
+ break;
+
+ case 101: /* RX PL On */
+ myrpt->rxplon = 1;
+ if (!res)
+ res = sayfile(mychannel, "rpt/rxpl");
+ if (!res)
+ sayfile(mychannel, "rpt/on");
+ break;
+
+ case 102: /* TX PL Off */
+ myrpt->txplon = 0;
+ if (!res)
+ res = sayfile(mychannel, "rpt/txpl");
+ if (!res)
+ sayfile(mychannel, "rpt/off");
+ break;
+
+ case 103: /* TX PL On */
+ myrpt->txplon = 1;
+ if (!res)
+ res = sayfile(mychannel, "rpt/txpl");
+ if (!res)
+ sayfile(mychannel, "rpt/on");
+ break;
+
+ case 104: /* Low Power */
+ myrpt->powerlevel = REM_LOWPWR;
+ if (!res)
+ res = sayfile(mychannel, "rpt/lopwr");
+ break;
+
+ case 105: /* Medium Power */
+ myrpt->powerlevel = REM_MEDPWR;
+ if (!res)
+ res = sayfile(mychannel, "rpt/medpwr");
+ break;
+
+ case 106: /* Hi Power */
+ myrpt->powerlevel = REM_HIPWR;
+ if (!res)
+ res = sayfile(mychannel, "rpt/hipwr");
+ break;
+
+ default:
+ if (!res)
+ rmt_telem_finish(myrpt, mychannel);
+ return DC_ERROR;
+ }
+ if (!res)
+ res = rmt_telem_finish(myrpt, mychannel);
+ if (res)
+ return DC_ERROR;
+
+ if (setrem(myrpt) == -1)
+ return DC_ERROR;
+ return DC_COMPLETE;
+
+ case 107: /* Bump down 20Hz */
+ multimode_bump_freq(myrpt, -20);
+ return DC_COMPLETE;
+
+ case 108: /* Bump down 100Hz */
+ multimode_bump_freq(myrpt, -100);
+ return DC_COMPLETE;
+
+ case 109: /* Bump down 500Hz */
+ multimode_bump_freq(myrpt, -500);
+ return DC_COMPLETE;
+
+ case 110: /* Bump up 20Hz */
+ multimode_bump_freq(myrpt, 20);
+ return DC_COMPLETE;
+
+ case 111: /* Bump up 100Hz */
+ multimode_bump_freq(myrpt, 100);
+ return DC_COMPLETE;
+
+ case 112: /* Bump up 500Hz */
+ multimode_bump_freq(myrpt, 500);
+ return DC_COMPLETE;
+
+ case 113:
+ case 114:
+ case 115:
+ case 116:
+ case 117:
+ case 118:
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ ast_indicate(mychannel, AST_CONTROL_RADIO_KEY);
+ if (ast_safe_sleep(mychannel, 1000) == -1)
+ return DC_ERROR;
+
+ switch (myatoi(param)) {
+ case 113: /* Scan down slow */
+ res = sayfile(mychannel, "rpt/down");
+ if (!res)
+ res = sayfile(mychannel, "rpt/slow");
+ if (!res) {
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_DOWN_SLOW;
+ }
+ break;
+
+ case 114: /* Scan down quick */
+ res = sayfile(mychannel, "rpt/down");
+ if (!res)
+ res = sayfile(mychannel, "rpt/quick");
+ if (!res) {
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_DOWN_QUICK;
+ }
+ break;
+
+ case 115: /* Scan down fast */
+ res = sayfile(mychannel, "rpt/down");
+ if (!res)
+ res = sayfile(mychannel, "rpt/fast");
+ if (!res) {
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_DOWN_FAST;
+ }
+ break;
+
+ case 116: /* Scan up slow */
+ res = sayfile(mychannel, "rpt/up");
+ if (!res)
+ res = sayfile(mychannel, "rpt/slow");
+ if (!res) {
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_UP_SLOW;
+ }
+ break;
+
+ case 117: /* Scan up quick */
+ res = sayfile(mychannel, "rpt/up");
+ if (!res)
+ res = sayfile(mychannel, "rpt/quick");
+ if (!res) {
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_UP_QUICK;
+ }
+ break;
+
+ case 118: /* Scan up fast */
+ res = sayfile(mychannel, "rpt/up");
+ if (!res)
+ res = sayfile(mychannel, "rpt/fast");
+ if (!res) {
+ myrpt->scantimer = REM_SCANTIME;
+ myrpt->hfscanmode = HF_SCAN_UP_FAST;
+ }
+ break;
+ }
+ rmt_telem_finish(myrpt, mychannel);
+ return DC_COMPLETE;
+
+
+ case 119: /* Tune Request */
+ myrpt->tunerequest = 1;
+ return DC_COMPLETE;
+
+ case 5: /* Long Status */
+ case 140: /* Short Status */
+ res = rmt_telem_start(myrpt, mychannel, 1000);
+
+ res = sayfile(mychannel, "rpt/node");
+ if (!res)
+ res = saycharstr(mychannel, myrpt->name);
+ if (!res)
+ res = sayfile(mychannel, "rpt/frequency");
+ if (!res)
+ res = split_freq(&mhz, &decimals, myrpt->freq);
+ if (!res) {
+ if (mhz < 100)
+ res = saynum(mychannel, mhz);
+ else
+ res = saydigits(mychannel, mhz);
+ }
+ if (!res)
+ res = sayfile(mychannel, "letters/dot");
+ if (!res)
+ res = saydigits(mychannel, decimals);
+
+ if (res) {
+ rmt_telem_finish(myrpt, mychannel);
+ return DC_ERROR;
+ }
+ if (myrpt->remmode == REM_MODE_FM) { /* Mode FM? */
+ switch (myrpt->offset) {
+ case REM_MINUS:
+ res = sayfile(mychannel, "rpt/minus");
+ break;
+
+ case REM_SIMPLEX:
+ res = sayfile(mychannel, "rpt/simplex");
+ break;
+
+ case REM_PLUS:
+ res = sayfile(mychannel, "rpt/plus");
+ break;
+
+ default:
+ return DC_ERROR;
+
+ }
+ } else { /* Must be USB, LSB, or AM */
+ switch (myrpt->remmode) {
+ case REM_MODE_USB:
+ res = saycharstr(mychannel, "USB");
+ break;
+ case REM_MODE_LSB:
+ res = saycharstr(mychannel, "LSB");
+ break;
+ case REM_MODE_AM:
+ res = saycharstr(mychannel, "AM");
+ break;
+ default:
+ return DC_ERROR;
+ }
+ }
+
+ if (res == -1) {
+ rmt_telem_finish(myrpt, mychannel);
+ return DC_ERROR;
+ }
+
+ if (myatoi(param) == 140) { /* Short status? */
+ if (!res)
+ res = rmt_telem_finish(myrpt, mychannel);
+ if (res)
+ return DC_ERROR;
+ return DC_COMPLETE;
+ }
+
+ switch (myrpt->powerlevel) {
+ case REM_LOWPWR:
+ res = sayfile(mychannel, "rpt/lopwr") ;
+ break;
+ case REM_MEDPWR:
+ res = sayfile(mychannel, "rpt/medpwr");
+ break;
+ case REM_HIPWR:
+ res = sayfile(mychannel, "rpt/hipwr");
+ break;
+ }
+ if (res || (sayfile(mychannel, "rpt/rxpl") == -1) ||
+ (sayfile(mychannel, "rpt/frequency") == -1) ||
+ (saycharstr(mychannel, myrpt->rxpl) == -1) ||
+ (sayfile(mychannel, "rpt/txpl") == -1) ||
+ (sayfile(mychannel, "rpt/frequency") == -1) ||
+ (saycharstr(mychannel, myrpt->txpl) == -1) ||
+ (sayfile(mychannel, "rpt/txpl") == -1) ||
+ (sayfile(mychannel, ((myrpt->txplon) ? "rpt/on" : "rpt/off")) == -1) ||
+ (sayfile(mychannel, "rpt/rxpl") == -1) ||
+ (sayfile(mychannel, ((myrpt->rxplon) ? "rpt/on" : "rpt/off")) == -1))
+ {
+ rmt_telem_finish(myrpt, mychannel);
+ return DC_ERROR;
+ }
+ if (!res)
+ res = rmt_telem_finish(myrpt, mychannel);
+ if (res)
+ return DC_ERROR;
+
+ return DC_COMPLETE;
+ default:
+ return DC_ERROR;
+ }
+
+ return DC_INDETERMINATE;
+}
+
+static int handle_remote_dtmf_digit(struct rpt *myrpt, char c, char *keyed, int phonemode)
+{
+ time_t now;
+ int ret, res = 0, src;
+
+ /* Stop scan mode if in scan mode */
+ if (myrpt->hfscanmode) {
+ stop_scan(myrpt, 0);
+ return 0;
+ }
+
+ time(&now);
+ /* if timed-out */
+ if ((myrpt->dtmf_time_rem + DTMF_TIMEOUT) < now) {
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = 0;
+ }
+ /* if decode not active */
+ if (myrpt->dtmfidx == -1) {
+ /* if not lead-in digit, dont worry */
+ if (c != myrpt->p.funcchar)
+ return 0;
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = now;
+ return 0;
+ }
+ /* if too many in buffer, start over */
+ if (myrpt->dtmfidx >= MAXDTMF) {
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = now;
+ }
+ if (c == myrpt->p.funcchar) {
+ /* if star at beginning, or 2 together, erase buffer */
+ if ((myrpt->dtmfidx < 1) || (myrpt->dtmfbuf[myrpt->dtmfidx - 1] == myrpt->p.funcchar)) {
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = now;
+ return 0;
+ }
+ }
+ myrpt->dtmfbuf[myrpt->dtmfidx++] = c;
+ myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
+ myrpt->dtmf_time_rem = now;
+
+
+ src = SOURCE_RMT;
+ if (phonemode > 1)
+ src = SOURCE_DPHONE;
+ else if (phonemode)
+ src = SOURCE_PHONE;
+ ret = collect_function_digits(myrpt, myrpt->dtmfbuf, src, NULL);
+
+ switch(ret) {
+ case DC_INDETERMINATE:
+ res = 0;
+ break;
+ case DC_DOKEY:
+ if (keyed)
+ *keyed = 1;
+ res = 0;
+ break;
+ case DC_REQ_FLUSH:
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ res = 0;
+ break;
+ case DC_COMPLETE:
+ myrpt->totalexecdcommands++;
+ myrpt->dailyexecdcommands++;
+ ast_copy_string(myrpt->lastdtmfcommand, myrpt->dtmfbuf, MAXDTMF);
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmf_time_rem = 0;
+ res = 1;
+ break;
+ case DC_ERROR:
+ default:
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmf_time_rem = 0;
+ res = 0;
+ }
+
+ return res;
+}
+
+static int handle_remote_data(struct rpt *myrpt, char *str)
+{
+ char cmd[300], dest[300], src[300], c;
+ int seq, res;
+
+ if (!strcmp(str, discstr))
+ return 0;
+ if (sscanf(str, "%s %s %s %d %c", cmd, dest, src, &seq, &c) != 5) {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
+ return 0;
+ }
+ if (strcmp(cmd, "D")) {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
+ return 0;
+ }
+ /* if not for me, ignore */
+ if (strcmp(dest, myrpt->name))
+ return 0;
+ res = handle_remote_dtmf_digit(myrpt, c, NULL, 0);
+ if (res != 1)
+ return res;
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx) {
+ ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_KEY);
+ }
+ if (ast_safe_sleep(myrpt->remchannel, 1000) == -1)
+ return -1;
+ res = telem_lookup(myrpt, myrpt->remchannel, myrpt->name, "functcomplete");
+ rmt_telem_finish(myrpt, myrpt->remchannel);
+ return res;
+}
+
+static int handle_remote_phone_dtmf(struct rpt *myrpt, char c, char *keyed, int phonemode)
+{
+ int res;
+
+ if (keyed && *keyed && (c == myrpt->p.endchar)) {
+ *keyed = 0;
+ return DC_INDETERMINATE;
+ }
+
+ res = handle_remote_dtmf_digit(myrpt, c, keyed, phonemode);
+ if (res != 1)
+ return res;
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx) {
+ ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_KEY);
+ }
+ if (ast_safe_sleep(myrpt->remchannel, 1000) == -1)
+ return -1;
+ res = telem_lookup(myrpt, myrpt->remchannel, myrpt->name, "functcomplete");
+ rmt_telem_finish(myrpt, myrpt->remchannel);
+ return res;
+}
+
+static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l)
+{
+ const char *val;
+ char *s, *tele;
+ char deststr[300] = "";
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(channel);
+ AST_APP_ARG(extra);
+ );
+
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, l->name);
+ if (!val) {
+ ast_log(LOG_ERROR, "attempt_reconnect: cannot find node %s\n", l->name);
+ return -1;
+ }
+
+ rpt_mutex_lock(&myrpt->lock);
+ /* remove from queue */
+ remque((struct qelem *) l);
+ rpt_mutex_unlock(&myrpt->lock);
+ s = ast_strdupa(val);
+ AST_STANDARD_APP_ARGS(args, s);
+
+ /* XXX This section doesn't make any sense. Why not just use args.channel? XXX */
+ snprintf(deststr, sizeof(deststr), "IAX2/%s", args.channel);
+ tele = strchr(deststr, '/');
+ if (!tele) {
+ ast_log(LOG_ERROR, "attempt_reconnect:Dial number (%s) must be in format tech/number\n", deststr);
+ return -1;
+ }
+ *tele++ = 0;
+ l->elaptime = 0;
+ l->connecttime = 0;
+ l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
+ if (l->chan) {
+ ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
+ l->chan->whentohangup = 0;
+ l->chan->appl = "Apprpt";
+ l->chan->data = "(Remote Rx)";
+ ast_verb(3, "rpt (attempt_reconnect) initiating call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ if (l->chan->cid.cid_num)
+ ast_free(l->chan->cid.cid_num);
+ l->chan->cid.cid_num = ast_strdup(myrpt->name);
+ ast_call(l->chan, tele, 999);
+
+ } else {
+ ast_verb(3, "Unable to place call to %s/%s on %s\n",
+ deststr, tele, l->chan->name);
+ return -1;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ /* put back in queue */
+ insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_NOTICE, "Reconnect Attempt to %s in process\n", l->name);
+ return 0;
+}
+
+/* 0 return=continue, 1 return = break, -1 return = error */
+static void local_dtmf_helper(struct rpt *myrpt, char c)
+{
+ int res;
+ char cmd[MAXDTMF+1] = "";
+
+ if (c == myrpt->p.endchar) {
+ /* if in simple mode, kill autopatch */
+ if (myrpt->p.simple && myrpt->callmode) {
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->callmode = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, TERM, NULL);
+ return;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ myrpt->stopgen = 1;
+ if (myrpt->cmdnode[0]) {
+ myrpt->cmdnode[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, COMPLETE, NULL);
+ } else
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ if (myrpt->cmdnode[0]) {
+ rpt_mutex_unlock(&myrpt->lock);
+ send_link_dtmf(myrpt, c);
+ return;
+ }
+ if (!myrpt->p.simple) {
+ if (c == myrpt->p.funcchar) {
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ time(&myrpt->dtmf_time);
+ return;
+ } else if ((c != myrpt->p.endchar) && (myrpt->dtmfidx >= 0)) {
+ time(&myrpt->dtmf_time);
+
+ if (myrpt->dtmfidx < MAXDTMF) {
+ myrpt->dtmfbuf[myrpt->dtmfidx++] = c;
+ myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
+
+ ast_copy_string(cmd, myrpt->dtmfbuf, sizeof(cmd));
+
+ rpt_mutex_unlock(&myrpt->lock);
+ res = collect_function_digits(myrpt, cmd, SOURCE_RPT, NULL);
+ rpt_mutex_lock(&myrpt->lock);
+ switch(res) {
+ case DC_INDETERMINATE:
+ break;
+ case DC_REQ_FLUSH:
+ myrpt->dtmfidx = 0;
+ myrpt->dtmfbuf[0] = 0;
+ break;
+ case DC_COMPLETE:
+ myrpt->totalexecdcommands++;
+ myrpt->dailyexecdcommands++;
+ ast_copy_string(myrpt->lastdtmfcommand, cmd, MAXDTMF);
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmf_time = 0;
+ break;
+
+ case DC_ERROR:
+ default:
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmf_time = 0;
+ break;
+ }
+ if (res != DC_INDETERMINATE) {
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ }
+ }
+ } else /* if simple */ {
+ if ((!myrpt->callmode) && (c == myrpt->p.funcchar)) {
+ myrpt->callmode = 1;
+ myrpt->patchnoct = 0;
+ myrpt->patchquiet = 0;
+ myrpt->patchfarenddisconnect = 0;
+ myrpt->patchdialtime = 0;
+ ast_copy_string(myrpt->patchcontext, myrpt->p.ourcontext, sizeof(myrpt->patchcontext));
+ myrpt->cidx = 0;
+ myrpt->exten[myrpt->cidx] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_pthread_create_detached(&myrpt->rpt_call_thread, NULL, rpt_call, (void *)myrpt);
+ return;
+ }
+ }
+ if (myrpt->callmode == 1) {
+ myrpt->exten[myrpt->cidx++] = c;
+ myrpt->exten[myrpt->cidx] = 0;
+ /* if this exists */
+ if (ast_exists_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
+ myrpt->callmode = 2;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (!myrpt->patchquiet)
+ rpt_telemetry(myrpt, PROC, NULL);
+ return;
+ }
+ /* if can continue, do so */
+ if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
+ /* call has failed, inform user */
+ myrpt->callmode = 4;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+ }
+ if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
+ myrpt->mydtmf = c;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ return;
+}
+
+
+/* place an ID event in the telemetry queue */
+
+static void queue_id(struct rpt *myrpt)
+{
+ myrpt->mustid = myrpt->tailid = 0;
+ myrpt->idtimer = myrpt->p.idtime; /* Reset our ID timer */
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, ID, NULL);
+ rpt_mutex_lock(&myrpt->lock);
+}
+
+/* Scheduler */
+
+static void do_scheduler(struct rpt *myrpt)
+{
+ int res;
+ struct ast_tm tmnow;
+
+ memcpy(&myrpt->lasttv, &myrpt->curtv, sizeof(struct timeval));
+
+ if ( (res = gettimeofday(&myrpt->curtv, NULL)) < 0)
+ ast_log(LOG_NOTICE, "Scheduler gettime of day returned: %s\n", strerror(res));
+
+ /* Try to get close to a 1 second resolution */
+
+ if (myrpt->lasttv.tv_sec == myrpt->curtv.tv_sec)
+ return;
+
+ ast_localtime(&myrpt->curtv, &tmnow, NULL);
+
+ /* If midnight, then reset all daily statistics */
+
+ if ((tmnow.tm_hour == 0) && (tmnow.tm_min == 0) && (tmnow.tm_sec == 0)) {
+ myrpt->dailykeyups = 0;
+ myrpt->dailytxtime = 0;
+ myrpt->dailykerchunks = 0;
+ myrpt->dailyexecdcommands = 0;
+ }
+}
+
+
+/* single thread with one file (request) to dial */
+static void *rpt(void *this)
+{
+ struct rpt *myrpt = (struct rpt *)this;
+ char *tele, c;
+ const char *idtalkover;
+ int ms = MSWAIT, i, lasttx=0, val, remrx=0, identqueued, othertelemqueued, tailmessagequeued, ctqueued;
+ struct ast_channel *who;
+ ZT_CONFINFO ci; /* conference info */
+ time_t t;
+ struct rpt_link *l, *m;
+ struct rpt_tele *telem;
+ char tmpstr[300];
+
+ rpt_mutex_lock(&myrpt->lock);
+
+ telem = myrpt->tele.next;
+ while (telem != &myrpt->tele) {
+ ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV);
+ telem = telem->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ /* find our index, and load the vars initially */
+ for (i = 0; i < nrpts; i++) {
+ if (&rpt_vars[i] == myrpt) {
+ load_rpt_vars(i, 0);
+ break;
+ }
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ ast_copy_string(tmpstr, myrpt->rxchanname, sizeof(tmpstr));
+ tele = strchr(tmpstr, '/');
+ if (!tele) {
+ ast_log(LOG_ERROR, "rpt:Rxchannel Dial number (%s) must be in format tech/number\n", myrpt->rxchanname);
+ rpt_mutex_unlock(&myrpt->lock);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->rxchannel = ast_request(tmpstr, AST_FORMAT_SLINEAR, tele, NULL);
+ if (myrpt->rxchannel) {
+ if (myrpt->rxchannel->_state == AST_STATE_BUSY) {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Rx channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ ast_set_read_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
+ myrpt->rxchannel->whentohangup = 0;
+ myrpt->rxchannel->appl = "Apprpt";
+ myrpt->rxchannel->data = "(Repeater Rx)";
+ ast_verb(3, "rpt (Rx) initiating call to %s/%s on %s\n",
+ tmpstr, tele, myrpt->rxchannel->name);
+ ast_call(myrpt->rxchannel, tele, 999);
+ if (myrpt->rxchannel->_state != AST_STATE_UP) {
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ } else {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Rx channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ if (myrpt->txchanname) {
+ ast_copy_string(tmpstr, myrpt->txchanname, sizeof(tmpstr));
+ tele = strchr(tmpstr, '/');
+ if (!tele) {
+ ast_log(LOG_ERROR, "rpt:Txchannel Dial number (%s) must be in format tech/number\n", myrpt->txchanname);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->txchannel = ast_request(tmpstr, AST_FORMAT_SLINEAR, tele, NULL);
+ if (myrpt->txchannel) {
+ if (myrpt->txchannel->_state == AST_STATE_BUSY) {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Tx channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ ast_set_read_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
+ myrpt->txchannel->whentohangup = 0;
+ myrpt->txchannel->appl = "Apprpt";
+ myrpt->txchannel->data = "(Repeater Tx)";
+ ast_verb(3, "rpt (Tx) initiating call to %s/%s on %s\n",
+ tmpstr, tele, myrpt->txchannel->name);
+ ast_call(myrpt->txchannel, tele, 999);
+ if (myrpt->rxchannel->_state != AST_STATE_UP) {
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ ast_hangup(myrpt->txchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ } else {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Tx channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ } else {
+ myrpt->txchannel = myrpt->rxchannel;
+ }
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_KEY);
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ /* allocate a pseudo-channel thru asterisk */
+ myrpt->pchannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!myrpt->pchannel) {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = -1; /* make a new conf */
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(myrpt->txchannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* save tx conference number */
+ myrpt->txconf = ci.confno;
+ /* make a conference for the pseudo */
+ ci.chan = 0;
+ ci.confno = -1; /* make a new conf */
+ ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? ZT_CONF_CONFANNMON :
+ (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
+ /* first put the channel on the conference in announce mode */
+ if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* save pseudo channel conference number */
+ myrpt->conf = ci.confno;
+ /* allocate a pseudo-channel thru asterisk */
+ myrpt->txpchannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!myrpt->txpchannel) {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = myrpt->txconf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER ;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(myrpt->txpchannel->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->txpchannel);
+ ast_hangup(myrpt->pchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ /* Now, the idea here is to copy from the physical rx channel buffer
+ into the pseudo tx buffer, and from the pseudo rx buffer into the
+ tx channel buffer */
+ myrpt->links.next = &myrpt->links;
+ myrpt->links.prev = &myrpt->links;
+ myrpt->tailtimer = 0;
+ myrpt->totimer = 0;
+ myrpt->tmsgtimer = myrpt->p.tailmessagetime;
+ myrpt->idtimer = myrpt->p.politeid;
+ myrpt->mustid = myrpt->tailid = 0;
+ myrpt->callmode = 0;
+ myrpt->tounkeyed = 0;
+ myrpt->tonotify = 0;
+ myrpt->retxtimer = 0;
+ myrpt->skedtimer = 0;
+ myrpt->tailevent = 0;
+ lasttx = 0;
+ myrpt->keyed = 0;
+ idtalkover = ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover");
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmfbuf[0] = 0;
+ myrpt->dtmf_time = 0;
+ myrpt->rem_dtmf_time = 0;
+ myrpt->enable = 1;
+ myrpt->disgorgetime = 0;
+ myrpt->lastnodewhichkeyedusup[0] = '\0';
+ myrpt->dailytxtime = 0;
+ myrpt->totaltxtime = 0;
+ myrpt->dailykeyups = 0;
+ myrpt->totalkeyups = 0;
+ myrpt->dailykerchunks = 0;
+ myrpt->totalkerchunks = 0;
+ myrpt->dailyexecdcommands = 0;
+ myrpt->totalexecdcommands = 0;
+ myrpt->timeouts = 0;
+ myrpt->exten[0] = '\0';
+ myrpt->lastdtmfcommand[0] = '\0';
+ if (myrpt->p.startupmacro) {
+ snprintf(myrpt->macrobuf, sizeof(myrpt->macrobuf), "PPPP%s", myrpt->p.startupmacro);
+ }
+ if (myrpt->p.startupgosub) {
+ snprintf(myrpt->gosubbuf, sizeof(myrpt->gosubbuf), "PPPP%s", myrpt->p.startupgosub);
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ val = 0;
+ ast_channel_setoption(myrpt->rxchannel, AST_OPTION_TONE_VERIFY, &val, sizeof(char), 0);
+ val = 1;
+ ast_channel_setoption(myrpt->rxchannel, AST_OPTION_RELAXDTMF, &val, sizeof(char), 0);
+ while (ms >= 0) {
+ struct ast_frame *f;
+ struct ast_channel *cs[300];
+ int totx=0, elap=0, n, toexit = 0;
+
+ /* DEBUG Dump */
+ if ((myrpt->disgorgetime) && (time(NULL) >= myrpt->disgorgetime)) {
+ struct rpt_link *zl;
+ struct rpt_tele *zt;
+
+ myrpt->disgorgetime = 0;
+ ast_log(LOG_NOTICE, "********** Variable Dump Start (app_rpt) **********\n");
+ ast_log(LOG_NOTICE, "totx = %d\n", totx);
+ ast_log(LOG_NOTICE, "remrx = %d\n", remrx);
+ ast_log(LOG_NOTICE, "lasttx = %d\n", lasttx);
+ ast_log(LOG_NOTICE, "elap = %d\n", elap);
+ ast_log(LOG_NOTICE, "toexit = %d\n", toexit);
+
+ ast_log(LOG_NOTICE, "myrpt->keyed = %d\n", myrpt->keyed);
+ ast_log(LOG_NOTICE, "myrpt->localtx = %d\n", myrpt->localtx);
+ ast_log(LOG_NOTICE, "myrpt->callmode = %d\n", myrpt->callmode);
+ ast_log(LOG_NOTICE, "myrpt->enable = %d\n", myrpt->enable);
+ ast_log(LOG_NOTICE, "myrpt->mustid = %d\n", myrpt->mustid);
+ ast_log(LOG_NOTICE, "myrpt->tounkeyed = %d\n", myrpt->tounkeyed);
+ ast_log(LOG_NOTICE, "myrpt->tonotify = %d\n", myrpt->tonotify);
+ ast_log(LOG_NOTICE, "myrpt->retxtimer = %ld\n", myrpt->retxtimer);
+ ast_log(LOG_NOTICE, "myrpt->totimer = %d\n", myrpt->totimer);
+ ast_log(LOG_NOTICE, "myrpt->tailtimer = %d\n", myrpt->tailtimer);
+ ast_log(LOG_NOTICE, "myrpt->tailevent = %d\n", myrpt->tailevent);
+
+ zl = myrpt->links.next;
+ while (zl != &myrpt->links) {
+ ast_log(LOG_NOTICE, "*** Link Name: %s ***\n", zl->name);
+ ast_log(LOG_NOTICE, " link->lasttx %d\n", zl->lasttx);
+ ast_log(LOG_NOTICE, " link->lastrx %d\n", zl->lastrx);
+ ast_log(LOG_NOTICE, " link->connected %d\n", zl->connected);
+ ast_log(LOG_NOTICE, " link->hasconnected %d\n", zl->hasconnected);
+ ast_log(LOG_NOTICE, " link->outbound %d\n", zl->outbound);
+ ast_log(LOG_NOTICE, " link->disced %d\n", zl->disced);
+ ast_log(LOG_NOTICE, " link->killme %d\n", zl->killme);
+ ast_log(LOG_NOTICE, " link->disctime %ld\n", zl->disctime);
+ ast_log(LOG_NOTICE, " link->retrytimer %ld\n", zl->retrytimer);
+ ast_log(LOG_NOTICE, " link->retries = %d\n", zl->retries);
+ ast_log(LOG_NOTICE, " link->reconnects = %d\n", zl->reconnects);
+ zl = zl->next;
+ }
+
+ zt = myrpt->tele.next;
+ if (zt != &myrpt->tele)
+ ast_log(LOG_NOTICE, "*** Telemetry Queue ***\n");
+ while (zt != &myrpt->tele) {
+ ast_log(LOG_NOTICE, " Telemetry mode: %d\n", zt->mode);
+ zt = zt->next;
+ }
+ ast_log(LOG_NOTICE, "******* Variable Dump End (app_rpt) *******\n");
+ }
+
+ if (myrpt->reload) {
+ struct rpt_tele *telem;
+
+ rpt_mutex_lock(&myrpt->lock);
+ telem = myrpt->tele.next;
+ while (telem != &myrpt->tele) {
+ ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV);
+ telem = telem->next;
+ }
+ myrpt->reload = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ usleep(10000);
+ /* find our index, and load the vars */
+ for (i = 0; i < nrpts; i++) {
+ if (&rpt_vars[i] == myrpt) {
+ load_rpt_vars(i, 0);
+ break;
+ }
+ }
+ }
+
+ rpt_mutex_lock(&myrpt->lock);
+ if (ast_check_hangup(myrpt->rxchannel)) break;
+ if (ast_check_hangup(myrpt->txchannel)) break;
+ if (ast_check_hangup(myrpt->pchannel)) break;
+ if (ast_check_hangup(myrpt->txpchannel)) break;
+
+ /* Update local tx with keyed if not parsing a command */
+ myrpt->localtx = myrpt->keyed && (myrpt->dtmfidx == -1) && (!myrpt->cmdnode[0]);
+ /* If someone's connected, and they're transmitting from their end to us, set remrx true */
+ l = myrpt->links.next;
+ remrx = 0;
+ while (l != &myrpt->links) {
+ if (l->lastrx) {
+ remrx = 1;
+ if (l->name[0] != '0') /* Ignore '0' nodes */
+ strcpy(myrpt->lastnodewhichkeyedusup, l->name); /* Note the node which is doing the key up */
+ }
+ l = l->next;
+ }
+ /* Create a "must_id" flag for the cleanup ID */
+ myrpt->mustid |= (myrpt->idtimer) && (myrpt->keyed || remrx) ;
+ /* Build a fresh totx from myrpt->keyed and autopatch activated */
+ totx = myrpt->callmode;
+ /* If full duplex, add local tx to totx */
+ if (myrpt->p.duplex > 1) totx = totx || myrpt->localtx;
+ /* Traverse the telemetry list to see what's queued */
+ identqueued = 0;
+ othertelemqueued = 0;
+ tailmessagequeued = 0;
+ ctqueued = 0;
+ telem = myrpt->tele.next;
+ while (telem != &myrpt->tele) {
+ if ((telem->mode == ID) || (telem->mode == IDTALKOVER)) {
+ identqueued = 1; /* Identification telemetry */
+ } else if (telem->mode == TAILMSG) {
+ tailmessagequeued = 1; /* Tail message telemetry */
+ } else {
+ if (telem->mode != UNKEY)
+ othertelemqueued = 1; /* Other telemetry */
+ else
+ ctqueued = 1; /* Courtesy tone telemetry */
+ }
+ telem = telem->next;
+ }
+
+ /* Add in any "other" telemetry, if 3/4 or full duplex */
+ if (myrpt->p.duplex > 0)
+ totx = totx || othertelemqueued;
+ /* Update external (to links) transmitter PTT state with everything but ID, CT, and tailmessage telemetry */
+ myrpt->exttx = totx;
+ /* If half or 3/4 duplex, add localtx to external link tx */
+ if (myrpt->p.duplex < 2)
+ myrpt->exttx = myrpt->exttx || myrpt->localtx;
+ /* Add in ID telemetry to local transmitter */
+ totx = totx || remrx;
+ /* If 3/4 or full duplex, add in ident and CT telemetry */
+ if (myrpt->p.duplex > 0)
+ totx = totx || identqueued || ctqueued;
+ /* Reset time out timer variables if there is no activity */
+ if (!totx) {
+ myrpt->totimer = myrpt->p.totime;
+ myrpt->tounkeyed = 0;
+ myrpt->tonotify = 0;
+ } else
+ myrpt->tailtimer = myrpt->p.hangtime; /* Initialize tail timer */
+ /* Disable the local transmitter if we are timed out */
+ totx = totx && myrpt->totimer;
+ /* if timed-out and not said already, say it */
+ if ((!myrpt->totimer) && (!myrpt->tonotify)) {
+ myrpt->tonotify = 1;
+ myrpt->timeouts++;
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, TIMEOUT, NULL);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+
+ /* If unkey and re-key, reset time out timer */
+ if ((!totx) && (!myrpt->totimer) && (!myrpt->tounkeyed) && (!myrpt->keyed)) {
+ myrpt->tounkeyed = 1;
+ }
+ if ((!totx) && (!myrpt->totimer) && myrpt->tounkeyed && myrpt->keyed) {
+ myrpt->totimer = myrpt->p.totime;
+ myrpt->tounkeyed = 0;
+ myrpt->tonotify = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ /* if timed-out and in circuit busy after call */
+ if ((!totx) && (!myrpt->totimer) && (myrpt->callmode == 4)) {
+ myrpt->callmode = 0;
+ }
+ /* get rid of tail if timed out */
+ if (!myrpt->totimer)
+ myrpt->tailtimer = 0;
+ /* if not timed-out, add in tail */
+ if (myrpt->totimer)
+ totx = totx || myrpt->tailtimer;
+ /* If user or links key up or are keyed up over standard ID, switch to talkover ID, if one is defined */
+ /* If tail message, kill the message if someone keys up over it */
+ if ((myrpt->keyed || remrx) && ((identqueued && idtalkover) || (tailmessagequeued))) {
+ int hasid = 0, hastalkover = 0;
+
+ telem = myrpt->tele.next;
+ while (telem != &myrpt->tele) {
+ if (telem->mode == ID) {
+ if (telem->chan)
+ ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV); /* Whoosh! */
+ hasid = 1;
+ }
+ if (telem->mode == TAILMSG) {
+ if (telem->chan)
+ ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV); /* Whoosh! */
+ }
+ if (telem->mode == IDTALKOVER)
+ hastalkover = 1;
+ telem = telem->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ if (hasid && (!hastalkover))
+ rpt_telemetry(myrpt, IDTALKOVER, NULL); /* Start Talkover ID */
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ /* Try to be polite */
+ /* If the repeater has been inactive for longer than the ID time, do an initial ID in the tail*/
+ /* If within 30 seconds of the time to ID, try do it in the tail */
+ /* else if at ID time limit, do it right over the top of them */
+ /* Lastly, if the repeater has been keyed, and the ID timer is expired, do a clean up ID */
+ if (myrpt->mustid && (!myrpt->idtimer))
+ queue_id(myrpt);
+
+ if ((totx && (!myrpt->exttx) &&
+ (myrpt->idtimer <= myrpt->p.politeid) && myrpt->tailtimer)) {
+ myrpt->tailid = 1;
+ }
+
+ /* If tail timer expires, then check for tail messages */
+
+ if (myrpt->tailevent) {
+ myrpt->tailevent = 0;
+ if (myrpt->tailid) {
+ totx = 1;
+ queue_id(myrpt);
+ }
+ else if ((myrpt->p.tailmsg.msgs[0]) &&
+ (myrpt->p.tailmessagetime) && (myrpt->tmsgtimer == 0)) {
+ totx = 1;
+ myrpt->tmsgtimer = myrpt->p.tailmessagetime;
+ rpt_mutex_unlock(&myrpt->lock);
+ rpt_telemetry(myrpt, TAILMSG, NULL);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ }
+
+ /* Main TX control */
+
+ /* let telemetry transmit anyway (regardless of timeout) */
+ if (myrpt->p.duplex > 0)
+ totx = totx || (myrpt->tele.next != &myrpt->tele);
+ if (totx && (!lasttx)) {
+ lasttx = 1;
+ myrpt->dailykeyups++;
+ myrpt->totalkeyups++;
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_KEY);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ totx = totx && myrpt->enable;
+ if ((!totx) && lasttx) {
+ lasttx = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ time(&t);
+ /* if DTMF timeout */
+ if ((!myrpt->cmdnode[0]) && (myrpt->dtmfidx >= 0) && ((myrpt->dtmf_time + DTMF_TIMEOUT) < t)) {
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ }
+ /* if remote DTMF timeout */
+ if ((myrpt->rem_dtmfidx >= 0) && ((myrpt->rem_dtmf_time + DTMF_TIMEOUT) < t)) {
+ myrpt->rem_dtmfidx = -1;
+ myrpt->rem_dtmfbuf[0] = 0;
+ }
+
+ /* Reconnect */
+
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ if (l->killme) {
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode, l->name))
+ myrpt->cmdnode[0] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ /* hang-up on call to device */
+ if (l->chan)
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ ast_free(l);
+ rpt_mutex_lock(&myrpt->lock);
+ /* re-start link traversal */
+ l = myrpt->links.next;
+ continue;
+ }
+ l = l->next;
+ }
+ n = 0;
+ cs[n++] = myrpt->rxchannel;
+ cs[n++] = myrpt->pchannel;
+ cs[n++] = myrpt->txpchannel;
+ if (myrpt->txchannel != myrpt->rxchannel)
+ cs[n++] = myrpt->txchannel;
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ if ((!l->killme) && (!l->disctime) && l->chan) {
+ cs[n++] = l->chan;
+ cs[n++] = l->pchan;
+ }
+ l = l->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ ms = MSWAIT;
+ who = ast_waitfor_n(cs, n, &ms);
+ if (who == NULL)
+ ms = 0;
+ elap = MSWAIT - ms;
+ rpt_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ if (!l->lasttx) {
+ if ((l->retxtimer += elap) >= REDUNDANT_TX_TIME) {
+ l->retxtimer = 0;
+ if (l->chan)
+ ast_indicate(l->chan, AST_CONTROL_RADIO_UNKEY);
+ }
+ } else
+ l->retxtimer = 0;
+ if (l->disctime) { /* Disconnect timer active on a channel ? */
+ l->disctime -= elap;
+ if (l->disctime <= 0) /* Disconnect timer expired on inbound channel ? */
+ l->disctime = 0; /* Yep */
+ }
+
+ if (l->retrytimer) {
+ l->retrytimer -= elap;
+ if (l->retrytimer < 0)
+ l->retrytimer = 0;
+ }
+
+ /* Tally connect time */
+ l->connecttime += elap;
+
+ /* ignore non-timing channels */
+ if (l->elaptime < 0) {
+ l = l->next;
+ continue;
+ }
+ l->elaptime += elap;
+ /* if connection has taken too long */
+ if ((l->elaptime > MAXCONNECTTIME) &&
+ ((!l->chan) || (l->chan->_state != AST_STATE_UP))) {
+ l->elaptime = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (l->chan)
+ ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if ((!l->chan) && (!l->retrytimer) && l->outbound &&
+ (l->retries++ < MAX_RETRIES) && (l->hasconnected)) {
+ if (l->chan)
+ ast_hangup(l->chan);
+ rpt_mutex_unlock(&myrpt->lock);
+ if ((l->name[0] != '0') && (!l->isremote)) {
+ l->retrytimer = MAX_RETRIES + 1;
+ } else {
+ if (attempt_reconnect(myrpt, l) == -1) {
+ l->retrytimer = RETRY_TIMER_MS;
+ }
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if ((!l->chan) && (!l->retrytimer) && l->outbound && (l->retries >= MAX_RETRIES)) {
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode, l->name))
+ myrpt->cmdnode[0] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (l->name[0] != '0') {
+ if (!l->hasconnected)
+ rpt_telemetry(myrpt, CONNFAIL, l);
+ else
+ rpt_telemetry(myrpt, REMDISC, l);
+ }
+ /* hang-up on call to device */
+ ast_hangup(l->pchan);
+ ast_free(l);
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if ((!l->chan) && (!l->disctime) && (!l->outbound)) {
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode, l->name))
+ myrpt->cmdnode[0] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (l->name[0] != '0') {
+ rpt_telemetry(myrpt, REMDISC, l);
+ }
+ /* hang-up on call to device */
+ ast_hangup(l->pchan);
+ ast_free(l);
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ l = l->next;
+ }
+ if (totx) {
+ myrpt->dailytxtime += elap;
+ myrpt->totaltxtime += elap;
+ }
+ i = myrpt->tailtimer;
+ if (myrpt->tailtimer)
+ myrpt->tailtimer -= elap;
+ if (myrpt->tailtimer < 0)
+ myrpt->tailtimer = 0;
+ if ((i) && (myrpt->tailtimer == 0))
+ myrpt->tailevent = 1;
+ if (myrpt->totimer)
+ myrpt->totimer -= elap;
+ if (myrpt->totimer < 0)
+ myrpt->totimer = 0;
+ if (myrpt->idtimer)
+ myrpt->idtimer -= elap;
+ if (myrpt->idtimer < 0)
+ myrpt->idtimer = 0;
+ if (myrpt->tmsgtimer)
+ myrpt->tmsgtimer -= elap;
+ if (myrpt->tmsgtimer < 0)
+ myrpt->tmsgtimer = 0;
+ /* do macro timers */
+ if (myrpt->macrotimer)
+ myrpt->macrotimer -= elap;
+ if (myrpt->macrotimer < 0)
+ myrpt->macrotimer = 0;
+ /* do gosub timers */
+ if (myrpt->gosubtimer)
+ myrpt->gosubtimer -= elap;
+ if (myrpt->gosubtimer < 0)
+ myrpt->gosubtimer = 0;
+ /* Execute scheduler appx. every 2 tenths of a second */
+ if (myrpt->skedtimer <= 0) {
+ myrpt->skedtimer = 200;
+ do_scheduler(myrpt);
+ } else
+ myrpt->skedtimer -= elap;
+ if (!ms) {
+ rpt_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ c = myrpt->macrobuf[0];
+ if (c && (!myrpt->macrotimer)) {
+ myrpt->macrotimer = MACROTIME;
+ memmove(myrpt->macrobuf, myrpt->macrobuf + 1, sizeof(myrpt->macrobuf) - 1);
+ if ((c == 'p') || (c == 'P'))
+ myrpt->macrotimer = MACROPTIME;
+ rpt_mutex_unlock(&myrpt->lock);
+ local_dtmf_helper(myrpt, c);
+ } else
+ rpt_mutex_unlock(&myrpt->lock);
+ c = myrpt->gosubbuf[0];
+ if (c && (!myrpt->gosubtimer)) {
+ myrpt->gosubtimer = GOSUBTIME;
+ memmove(myrpt->gosubbuf, myrpt->gosubbuf + 1, sizeof(myrpt->gosubbuf) - 1);
+ if ((c == 'p') || (c == 'P'))
+ myrpt->gosubtimer = GOSUBPTIME;
+ rpt_mutex_unlock(&myrpt->lock);
+ local_dtmf_helper(myrpt, c);
+ } else
+ rpt_mutex_unlock(&myrpt->lock);
+ if (who == myrpt->rxchannel) { /* if it was a read from rx */
+ f = ast_read(myrpt->rxchannel);
+ if (!f) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+#ifdef _MDC_DECODE_H_
+ unsigned char ubuf[2560];
+ short *sp;
+ int n;
+#endif
+
+ if (!myrpt->localtx) {
+ memset(f->data, 0, f->datalen);
+ }
+
+#ifdef _MDC_DECODE_H_
+ sp = (short *) f->data;
+ /* convert block to unsigned char */
+ for (n = 0; n < f->datalen / 2; n++) {
+ ubuf[n] = (*sp++ >> 8) + 128;
+ }
+ n = mdc_decoder_process_samples(myrpt->mdc, ubuf, f->datalen / 2);
+ if (n == 1) {
+ unsigned char op, arg;
+ unsigned short unitID;
+
+ mdc_decoder_get_packet(myrpt->mdc, &op, &arg, &unitID);
+ if (debug > 2) {
+ ast_log(LOG_NOTICE, "Got (single-length) packet:\n");
+ ast_log(LOG_NOTICE, "op: %02x, arg: %02x, UnitID: %04x\n", op & 255, arg & 255, unitID);
+ }
+ if ((op == 1) && (arg == 0)) {
+ myrpt->lastunit = unitID;
+ }
+ }
+ if ((debug > 2) && (i == 2)) {
+ unsigned char op, arg, ex1, ex2, ex3, ex4;
+ unsigned short unitID;
+
+ mdc_decoder_get_double_packet(myrpt->mdc, &op, &arg, &unitID, &ex1, &ex2, &ex3, &ex4);
+ ast_log(LOG_NOTICE, "Got (double-length) packet:\n");
+ ast_log(LOG_NOTICE, "op: %02x, arg: %02x, UnitID: %04x\n", op & 255, arg & 255, unitID);
+ ast_log(LOG_NOTICE, "ex1: %02x, ex2: %02x, ex3: %02x, ex4: %02x\n",
+ ex1 & 255, ex2 & 255, ex3 & 255, ex4 & 255);
+ }
+#endif
+#ifdef __RPT_NOTCH
+ /* apply inbound filters, if any */
+ rpt_filter(myrpt, f->data, f->datalen / 2);
+#endif
+ ast_write(myrpt->pchannel, f);
+ } else if (f->frametype == AST_FRAME_DTMF) {
+ c = (char) f->subclass; /* get DTMF char */
+ ast_frfree(f);
+ if (!myrpt->keyed)
+ continue;
+ local_dtmf_helper(myrpt, c);
+ continue;
+ } else if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY) {
+ if ((!lasttx) || (myrpt->p.duplex > 1)) {
+ ast_debug(8, "@@@@ rx key\n");
+ myrpt->keyed = 1;
+ }
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
+ if ((!lasttx) || (myrpt->p.duplex > 1)) {
+ ast_debug(8, "@@@@ rx un-key\n");
+ if (myrpt->keyed) {
+ rpt_telemetry(myrpt, UNKEY, NULL);
+ }
+ myrpt->keyed = 0;
+ }
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ if (who == myrpt->pchannel) { /* if it was a read from pseudo */
+ f = ast_read(myrpt->pchannel);
+ if (!f) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ ast_write(myrpt->txpchannel, f);
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ if (who == myrpt->txchannel) { /* if it was a read from tx */
+ f = ast_read(myrpt->txchannel);
+ if (!f) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ toexit = 0;
+ rpt_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ if (l->disctime) {
+ l = l->next;
+ continue;
+ }
+ if (who == l->chan) { /* if it was a read from rx */
+ remrx = 0;
+ /* see if any other links are receiving */
+ m = myrpt->links.next;
+ while (m != &myrpt->links) {
+ /* if not us, count it */
+ if ((m != l) && (m->lastrx))
+ remrx = 1;
+ m = m->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ totx = (((l->isremote) ? myrpt->localtx :
+ myrpt->exttx) || remrx) && l->mode;
+ if (l->chan && (l->lasttx != totx)) {
+ if (totx) {
+ ast_indicate(l->chan, AST_CONTROL_RADIO_KEY);
+ } else {
+ ast_indicate(l->chan, AST_CONTROL_RADIO_UNKEY);
+ }
+ }
+ l->lasttx = totx;
+ f = ast_read(l->chan);
+ if (!f) {
+ if ((!l->disced) && (!l->outbound)) {
+ if ((l->name[0] == '0') || l->isremote)
+ l->disctime = 1;
+ else
+ l->disctime = DISC_TIME;
+ rpt_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ break;
+ }
+
+ if (l->retrytimer) {
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected)) {
+ rpt_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (attempt_reconnect(myrpt, l) == -1) {
+ l->retrytimer = RETRY_TIMER_MS;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode, l->name))
+ myrpt->cmdnode[0] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (!l->hasconnected)
+ rpt_telemetry(myrpt, CONNFAIL, l);
+ else if (l->disced != 2)
+ rpt_telemetry(myrpt, REMDISC, l);
+ /* hang-up on call to device */
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ ast_free(l);
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (!l->lastrx) {
+ memset(f->data, 0, f->datalen);
+ }
+ ast_write(l->pchan, f);
+ }
+ if (f->frametype == AST_FRAME_TEXT) {
+ handle_link_data(myrpt, l, f->data);
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ handle_link_phone_dtmf(myrpt, l, f->subclass);
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_ANSWER) {
+ char lconnected = l->connected;
+ l->connected = 1;
+ l->hasconnected = 1;
+ l->elaptime = -1;
+ l->retries = 0;
+ if (!lconnected)
+ rpt_telemetry(myrpt, CONNECTED, l);
+ else
+ l->reconnects++;
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY) {
+ ast_debug(8, "@@@@ rx key\n");
+ l->lastrx = 1;
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
+ ast_debug(8, "@@@@ rx un-key\n");
+ l->lastrx = 0;
+ }
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_frfree(f);
+ if ((!l->outbound) && (!l->disced)) {
+ if ((l->name[0] == '0') || l->isremote)
+ l->disctime = 1;
+ else
+ l->disctime = DISC_TIME;
+ rpt_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ break;
+ }
+ if (l->retrytimer) {
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected)) {
+ rpt_mutex_lock(&myrpt->lock);
+ ast_hangup(l->chan);
+ l->chan = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (attempt_reconnect(myrpt, l) == -1) {
+ l->retrytimer = RETRY_TIMER_MS;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ /* remove from queue */
+ remque((struct qelem *) l);
+ if (!strcmp(myrpt->cmdnode, l->name))
+ myrpt->cmdnode[0] = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (!l->hasconnected)
+ rpt_telemetry(myrpt, CONNFAIL, l);
+ else if (l->disced != 2)
+ rpt_telemetry(myrpt, REMDISC, l);
+ /* hang-up on call to device */
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ ast_free(l);
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ }
+ ast_frfree(f);
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (who == l->pchan) {
+ rpt_mutex_unlock(&myrpt->lock);
+ f = ast_read(l->pchan);
+ if (!f) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ toexit = 1;
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (l->chan)
+ ast_write(l->chan, f);
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ toexit = 1;
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ }
+ ast_frfree(f);
+ rpt_mutex_lock(&myrpt->lock);
+ break;
+ }
+ l = l->next;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ if (toexit)
+ break;
+ if (who == myrpt->txpchannel) { /* if it was a read from remote tx */
+ f = ast_read(myrpt->txpchannel);
+ if (!f) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ }
+ usleep(100000);
+ ast_hangup(myrpt->pchannel);
+ ast_hangup(myrpt->txpchannel);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ rpt_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ while (l != &myrpt->links) {
+ struct rpt_link *ll = l;
+ /* remove from queue */
+ remque((struct qelem *) l);
+ /* hang-up on call to device */
+ if (l->chan)
+ ast_hangup(l->chan);
+ ast_hangup(l->pchan);
+ l = l->next;
+ ast_free(ll);
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_debug(1, "@@@@ rpt:Hung up channel\n");
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ return NULL;
+}
+
+
+static void *rpt_master(void *config)
+{
+ int i, n;
+ struct ast_config *cfg;
+ char *this;
+ const char *val;
+
+ /* go thru all the specified repeaters */
+ this = NULL;
+ n = 0;
+ rpt_vars[n].cfg = config;
+ cfg = rpt_vars[n].cfg;
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n");
+ pthread_exit(NULL);
+ }
+ while ((this = ast_category_browse(cfg, this)) != NULL) {
+ for (i = 0; i < strlen(this); i++) {
+ if ((this[i] < '0') || (this[i] > '9'))
+ break;
+ }
+ if (i != strlen(this))
+ continue; /* Not a node defn */
+ memset(&rpt_vars[n], 0, sizeof(rpt_vars[n]));
+ rpt_vars[n].name = ast_strdup(this);
+ val = ast_variable_retrieve(cfg, this, "rxchannel");
+ if (val)
+ rpt_vars[n].rxchanname = ast_strdup(val);
+ val = ast_variable_retrieve(cfg, this, "txchannel");
+ if (val)
+ rpt_vars[n].txchanname = ast_strdup(val);
+ val = ast_variable_retrieve(cfg, this, "remote");
+ if (val)
+ rpt_vars[n].remote = ast_strdup(val);
+ ast_mutex_init(&rpt_vars[n].lock);
+ rpt_vars[n].tele.next = &rpt_vars[n].tele;
+ rpt_vars[n].tele.prev = &rpt_vars[n].tele;
+ rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
+ rpt_vars[n].tailmessagen = 0;
+#ifdef _MDC_DECODE_H_
+ rpt_vars[n].mdc = mdc_decoder_new(8000);
+#endif
+ n++;
+ }
+ nrpts = n;
+ ast_config_destroy(cfg);
+
+ /* start em all */
+ for (i = 0; i < n; i++) {
+ load_rpt_vars(i, 1);
+
+ /* if is a remote, dont start one for it */
+ if (rpt_vars[i].remote) {
+ ast_copy_string(rpt_vars[i].freq, "146.580", sizeof(rpt_vars[i].freq));
+ ast_copy_string(rpt_vars[i].rxpl, "100.0", sizeof(rpt_vars[i].rxpl));
+ ast_copy_string(rpt_vars[i].txpl, "100.0", sizeof(rpt_vars[i].txpl));
+ rpt_vars[i].remmode = REM_MODE_FM;
+ rpt_vars[i].offset = REM_SIMPLEX;
+ rpt_vars[i].powerlevel = REM_MEDPWR;
+ continue;
+ }
+ if (!rpt_vars[i].p.ident) {
+ ast_log(LOG_WARNING, "Did not specify ident for node %s\n", rpt_vars[i].name);
+ ast_config_destroy(cfg);
+ pthread_exit(NULL);
+ }
+ ast_pthread_create_detached(&rpt_vars[i].rpt_thread, NULL, rpt, (void *) &rpt_vars[i]);
+ }
+ usleep(500000);
+ for (;;) {
+ /* Now monitor each thread, and restart it if necessary */
+ for (i = 0; i < n; i++) {
+ int rv;
+ if (rpt_vars[i].remote)
+ continue;
+ if (rpt_vars[i].rpt_thread == AST_PTHREADT_STOP)
+ rv = -1;
+ else
+ rv = pthread_kill(rpt_vars[i].rpt_thread, 0);
+ if (rv) {
+ if (time(NULL) - rpt_vars[i].lastthreadrestarttime <= 15) {
+ if (rpt_vars[i].threadrestarts >= 5) {
+ ast_log(LOG_ERROR, "Continual RPT thread restarts, killing Asterisk\n");
+ ast_cli_command(STDERR_FILENO, "restart now");
+ } else {
+ ast_log(LOG_NOTICE, "RPT thread restarted on %s\n", rpt_vars[i].name);
+ rpt_vars[i].threadrestarts++;
+ }
+ } else
+ rpt_vars[i].threadrestarts = 0;
+
+ rpt_vars[i].lastthreadrestarttime = time(NULL);
+ ast_pthread_create_detached(&rpt_vars[i].rpt_thread, NULL, rpt, (void *) &rpt_vars[i]);
+ ast_log(LOG_WARNING, "rpt_thread restarted on node %s\n", rpt_vars[i].name);
+ }
+
+ }
+ usleep(2000000);
+ }
+ ast_config_destroy(cfg);
+ pthread_exit(NULL);
+}
+
+static int rpt_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1, i, rem_totx, n, phone_mode = 0;
+ char *tmp, keyed = 0;
+ char *options = NULL, *tele, c;
+ struct rpt *myrpt;
+ struct ast_frame *f;
+ struct ast_channel *who;
+ struct ast_channel *cs[20];
+ struct rpt_link *l;
+ ZT_CONFINFO ci; /* conference info */
+ ZT_PARAMS par;
+ int ms, elap;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(node);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Rpt requires an argument (system node)\n");
+ return -1;
+ }
+ tmp = ast_strdupa((char *)data);
+ AST_STANDARD_APP_ARGS(args, tmp);
+ myrpt = NULL;
+ /* see if we can find our specified one */
+ for (i = 0; i < nrpts; i++) {
+ /* if name matches, assign it and exit loop */
+ if (!strcmp(args.node, rpt_vars[i].name)) {
+ myrpt = &rpt_vars[i];
+ break;
+ }
+ }
+ if (myrpt == NULL) {
+ ast_log(LOG_WARNING, "Cannot find specified system node %s\n", args.node);
+ return -1;
+ }
+
+ /* if not phone access, must be an IAX connection */
+ if (options && ((*options == 'P') || (*options == 'D') || (*options == 'R'))) {
+ phone_mode = 1;
+ if (*options == 'D')
+ phone_mode = 2;
+ ast_set_callerid(chan, "0", "app_rpt user", "0");
+ } else {
+ if (strncmp(chan->name, "IAX2", 4)) {
+ ast_log(LOG_WARNING, "We only accept links via IAX2!!\n");
+ return -1;
+ }
+ }
+ if (*args.options == 'R') {
+ /* Parts of this section taken from app_parkandannounce */
+ int m, lot, timeout = 0;
+ char tmp[256];
+ char *s;
+ AST_DECLARE_APP_ARGS(optionarg,
+ AST_APP_ARG(template);
+ AST_APP_ARG(timeout);
+ AST_APP_ARG(return_context);
+ );
+
+ rpt_mutex_lock(&myrpt->lock);
+ m = myrpt->callmode;
+ rpt_mutex_unlock(&myrpt->lock);
+
+ if ((!myrpt->p.nobusyout) && m) {
+ if (chan->_state != AST_STATE_UP) {
+ ast_indicate(chan, AST_CONTROL_BUSY);
+ }
+ while (ast_safe_sleep(chan, 10000) != -1) {
+ /* This used to be a busy loop. It's probably better to yield the processor here. */
+ usleep(1);
+ }
+ return -1;
+ }
+
+ if (chan->_state != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+
+ s = ast_strdupa(options);
+ AST_STANDARD_APP_ARGS(optionarg, s);
+ if (optionarg.argc == 0 || ast_strlen_zero(optionarg.template)) {
+ ast_log(LOG_WARNING, "An announce template must be defined\n");
+ return -1;
+ }
+
+ if (optionarg.argc >= 2) {
+ timeout = atoi(optionarg.timeout) * 1000;
+ }
+
+ if (!ast_strlen_zero(optionarg.return_context)) {
+ if (ast_parseable_goto(chan, optionarg.return_context)) {
+ ast_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
+ }
+ }
+
+ /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
+ before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
+
+ ast_masq_park_call(chan, NULL, timeout, &lot);
+ ast_verb(3, "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, optionarg.return_context);
+
+ snprintf(tmp, sizeof(tmp), "%d,%s", lot, optionarg.template + 1);
+ rpt_telemetry(myrpt, REV_PATCH, tmp);
+
+ return 0;
+ }
+
+ if (!options) {
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ struct in_addr ia;
+ char hisip[100], nodeip[100];
+ const char *val;
+ char *s, *s1, *s2;
+
+ /* look at callerid to see what node this comes from */
+ if (!chan->cid.cid_num) { /* if doesn't have caller id */
+ ast_log(LOG_WARNING, "Doesn't have callerid on %s\n", args.node);
+ return -1;
+ }
+
+ /* get his IP from IAX2 module */
+ pbx_substitute_variables_helper(chan, "${IAXPEER(CURRENTCHANNEL)}", hisip, sizeof(hisip) - 1);
+ if (ast_strlen_zero(hisip)) {
+ ast_log(LOG_WARNING, "Link IP address cannot be determined!!\n");
+ return -1;
+ }
+
+ if (!strcmp(myrpt->name, chan->cid.cid_num)) {
+ ast_log(LOG_WARNING, "Trying to link to self!!\n");
+ return -1;
+ }
+
+ if (*(chan->cid.cid_num) < '1') {
+ ast_log(LOG_WARNING, "Node %s Invalid for connection here!!\n", chan->cid.cid_num);
+ return -1;
+ }
+
+ /* look for his reported node string */
+ val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, chan->cid.cid_num);
+ if (!val) {
+ ast_log(LOG_WARNING, "Reported node %s cannot be found!!\n", chan->cid.cid_num);
+ return -1;
+ }
+ ast_copy_string(tmp, val, sizeof(tmp));
+ s = tmp;
+ s1 = strsep(&s, ",");
+ s2 = strsep(&s, ",");
+ if (!s2) {
+ ast_log(LOG_WARNING, "Reported node %s not in correct format!!\n", chan->cid.cid_num);
+ return -1;
+ }
+ if (strcmp(s2, "NONE")) {
+ hp = ast_gethostbyname(s2, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n", chan->cid.cid_num, s2);
+ return -1;
+ }
+ memcpy(&ia, hp->h_addr, sizeof(in_addr_t));
+ ast_copy_string(nodeip, ast_inet_ntoa(ia), sizeof(nodeip));
+ if (strcmp(hisip, nodeip)) {
+ char *s3 = strchr(s1, '@');
+ if (s3)
+ s1 = s3 + 1;
+ s3 = strchr(s1, '/');
+ if (s3)
+ *s3 = 0;
+ hp = ast_gethostbyname(s1, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n", chan->cid.cid_num, s1);
+ return -1;
+ }
+ memcpy(&ia, hp->h_addr, sizeof(in_addr_t));
+ ast_copy_string(nodeip, ast_inet_ntoa(ia), sizeof(nodeip));
+ if (strcmp(hisip, nodeip)) {
+ ast_log(LOG_WARNING, "Node %s IP %s does not match link IP %s!!\n", chan->cid.cid_num, nodeip, hisip);
+ return -1;
+ }
+ }
+ }
+ }
+
+ /* if is not a remote */
+ if (!myrpt->remote) {
+ int reconnects = 0;
+
+ /* look at callerid to see what node this comes from */
+ if (!chan->cid.cid_num) { /* if doesn't have caller id */
+ ast_log(LOG_WARNING, "Doesnt have callerid on %s\n", args.node);
+ return -1;
+ }
+
+ if (!strcmp(myrpt->name, chan->cid.cid_num)) {
+ ast_log(LOG_WARNING, "Trying to link to self!!\n");
+ return -1;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ l = myrpt->links.next;
+ /* try to find this one in queue */
+ while (l != &myrpt->links) {
+ if (l->name[0] == '0') {
+ l = l->next;
+ continue;
+ }
+ /* if found matching string */
+ if (!strcmp(l->name, chan->cid.cid_num))
+ break;
+ l = l->next;
+ }
+ /* if found */
+ if (l != &myrpt->links) {
+ l->killme = 1;
+ l->retries = MAX_RETRIES + 1;
+ l->disced = 2;
+ reconnects = l->reconnects;
+ reconnects++;
+ rpt_mutex_unlock(&myrpt->lock);
+ usleep(500000);
+ } else
+ rpt_mutex_unlock(&myrpt->lock);
+ /* establish call in tranceive mode */
+ l = ast_calloc(1, sizeof(*l));
+ if (!l) {
+ ast_log(LOG_WARNING, "Unable to malloc\n");
+ pthread_exit(NULL);
+ }
+ l->mode = 1;
+ ast_copy_string(l->name, chan->cid.cid_num, sizeof(l->name));
+ l->isremote = 0;
+ l->chan = chan;
+ l->connected = 1;
+ l->hasconnected = 1;
+ l->reconnects = reconnects;
+ l->phonemode = phone_mode;
+ ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
+ /* allocate a pseudo-channel thru asterisk */
+ l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
+ if (!l->pchan) {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
+ pthread_exit(NULL);
+ }
+ ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
+ /* make a conference for the tx */
+ ci.chan = 0;
+ ci.confno = myrpt->conf;
+ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
+ /* first put the channel on the conference in proper mode */
+ if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
+ ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+ pthread_exit(NULL);
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ if (phone_mode > 1)
+ l->lastrx = 1;
+ /* insert at end of queue */
+ insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
+ rpt_mutex_unlock(&myrpt->lock);
+ if (chan->_state != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+ return AST_PBX_KEEPALIVE;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ /* if remote, error if anyone else already linked */
+ if (myrpt->remoteon) {
+ rpt_mutex_unlock(&myrpt->lock);
+ usleep(500000);
+ if (myrpt->remoteon) {
+ ast_log(LOG_WARNING, "Trying to use busy link on %s\n", args.node);
+ return -1;
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ myrpt->remoteon = 1;
+ if (ioperm(myrpt->p.iobase, 1, 1) == -1) {
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n", myrpt->p.iobase);
+ return -1;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ /* find our index, and load the vars initially */
+ for (i = 0; i < nrpts; i++) {
+ if (&rpt_vars[i] == myrpt) {
+ load_rpt_vars(i, 0);
+ break;
+ }
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ tele = strchr(myrpt->rxchanname, '/');
+ if (!tele) {
+ ast_log(LOG_ERROR, "rpt:Dial number must be in format tech/number\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->rxchannel = ast_request(myrpt->rxchanname, AST_FORMAT_SLINEAR, tele, NULL);
+ if (myrpt->rxchannel) {
+ ast_set_read_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->rxchannel, AST_FORMAT_SLINEAR);
+ myrpt->rxchannel->whentohangup = 0;
+ myrpt->rxchannel->appl = "Apprpt";
+ myrpt->rxchannel->data = "(Link Rx)";
+ ast_verb(3, "rpt (Rx) initiating call to %s/%s on %s\n",
+ myrpt->rxchanname, tele, myrpt->rxchannel->name);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_call(myrpt->rxchannel, tele, 999);
+ rpt_mutex_lock(&myrpt->lock);
+ } else {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Rx channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ pthread_exit(NULL);
+ }
+ *--tele = '/';
+ if (myrpt->txchanname) {
+ tele = strchr(myrpt->txchanname, '/');
+ if (!tele) {
+ ast_log(LOG_ERROR, "rpt:Dial number must be in format tech/number\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ pthread_exit(NULL);
+ }
+ *tele++ = 0;
+ myrpt->txchannel = ast_request(myrpt->txchanname, AST_FORMAT_SLINEAR, tele, NULL);
+ if (myrpt->txchannel) {
+ ast_set_read_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->txchannel, AST_FORMAT_SLINEAR);
+ myrpt->txchannel->whentohangup = 0;
+ myrpt->txchannel->appl = "Apprpt";
+ myrpt->txchannel->data = "(Link Tx)";
+ ast_verb(3, "rpt (Tx) initiating call to %s/%s on %s\n",
+ myrpt->txchanname, tele, myrpt->txchannel->name);
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_call(myrpt->txchannel, tele, 999);
+ rpt_mutex_lock(&myrpt->lock);
+ } else {
+ ast_log(LOG_ERROR, "rpt:Sorry unable to obtain Tx channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ ast_hangup(myrpt->rxchannel);
+ pthread_exit(NULL);
+ }
+ *--tele = '/';
+ } else {
+ myrpt->txchannel = myrpt->rxchannel;
+ }
+ myrpt->remoterx = 0;
+ myrpt->remotetx = 0;
+ myrpt->retxtimer = 0;
+ myrpt->remoteon = 1;
+ myrpt->dtmfidx = -1;
+ myrpt->dtmfbuf[0] = 0;
+ myrpt->dtmf_time_rem = 0;
+ myrpt->hfscanmode = 0;
+ myrpt->hfscanstatus = 0;
+ if (myrpt->p.startupmacro) {
+ myrpt->remchannel = chan; /* Save copy of channel */
+ snprintf(myrpt->macrobuf, sizeof(myrpt->macrobuf), "PPPP%s", myrpt->p.startupmacro);
+ }
+ if (myrpt->p.startupgosub) {
+ myrpt->remchannel = chan; /* Save copy of channel */
+ snprintf(myrpt->gosubbuf, sizeof(myrpt->gosubbuf), "PPPP%s", myrpt->p.startupgosub);
+ }
+ myrpt->reload = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ setrem(myrpt);
+ ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+ ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ /* if we are on 2w loop and are a remote, turn EC on */
+ if (myrpt->remote && (myrpt->rxchannel == myrpt->txchannel)) {
+ i = 128;
+ ioctl(myrpt->rxchannel->fds[0], ZT_ECHOCANCEL, &i);
+ }
+ if (chan->_state != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+
+ if (ioctl(myrpt->txchannel->fds[0], ZT_GET_PARAMS, &par) != -1) {
+ if (par.rxisoffhook) {
+ ast_indicate(chan, AST_CONTROL_RADIO_KEY);
+ myrpt->remoterx = 1;
+ }
+ }
+ n = 0;
+ cs[n++] = chan;
+ cs[n++] = myrpt->rxchannel;
+ if (myrpt->rxchannel != myrpt->txchannel)
+ cs[n++] = myrpt->txchannel;
+ for (;;) {
+ if (ast_check_hangup(chan))
+ break;
+ if (ast_check_hangup(myrpt->rxchannel))
+ break;
+ if (myrpt->reload) {
+ myrpt->reload = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ /* find our index, and load the vars */
+ for (i = 0; i < nrpts; i++) {
+ if (&rpt_vars[i] == myrpt) {
+ load_rpt_vars(i, 0);
+ break;
+ }
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ }
+ ms = MSWAIT;
+ who = ast_waitfor_n(cs, n, &ms);
+ if (who == NULL)
+ ms = 0;
+ elap = MSWAIT - ms;
+ if (myrpt->macrotimer)
+ myrpt->macrotimer -= elap;
+ if (myrpt->macrotimer < 0)
+ myrpt->macrotimer = 0;
+ if (myrpt->gosubtimer)
+ myrpt->gosubtimer -= elap;
+ if (myrpt->gosubtimer < 0)
+ myrpt->gosubtimer = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (!ms)
+ continue;
+ rem_totx = keyed;
+
+
+ if ((!myrpt->remoterx) && (!myrpt->remotetx)) {
+ if ((myrpt->retxtimer += elap) >= REDUNDANT_TX_TIME) {
+ myrpt->retxtimer = 0;
+ ast_indicate(chan, AST_CONTROL_RADIO_UNKEY);
+ }
+ } else
+ myrpt->retxtimer = 0;
+ if (rem_totx && (!myrpt->remotetx)) { /* Remote base radio TX key */
+ myrpt->remotetx = 1;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_KEY);
+ }
+ if ((!rem_totx) && myrpt->remotetx) { /* Remote base radio TX unkey */
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ }
+
+ if (myrpt->tunerequest && (!strcmp(myrpt->remote, remote_rig_ft897))) { /* ft-897 specific for now... */
+ myrpt->tunerequest = 0;
+ set_mode_ft897(myrpt, REM_MODE_AM);
+ simple_command_ft897(myrpt, 8);
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx)
+ ast_indicate(chan, AST_CONTROL_RADIO_KEY);
+ if (play_tone(chan, 800, 6000, 8192) == -1)
+ break;
+
+ rmt_telem_finish(myrpt, chan);
+ set_mode_ft897(myrpt, 0x88);
+ setrem(myrpt);
+ }
+
+ if (myrpt->hfscanmode) {
+ myrpt->scantimer -= elap;
+ if (myrpt->scantimer <= 0) {
+ myrpt->scantimer = REM_SCANTIME;
+ service_scan(myrpt);
+ }
+ }
+ if (who == chan) { /* if it was a read from incoming */
+ f = ast_read(chan);
+ if (!f) {
+ ast_debug(1, "@@@@ link:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ /* if not transmitting, zero-out audio */
+ if (!myrpt->remotetx)
+ memset(f->data, 0, f->datalen);
+ ast_write(myrpt->txchannel, f);
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ myrpt->remchannel = chan; /* Save copy of channel */
+ if (handle_remote_phone_dtmf(myrpt, f->subclass, &keyed, phone_mode) == -1) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ if (f->frametype == AST_FRAME_TEXT) {
+ myrpt->remchannel = chan; /* Save copy of channel */
+ if (handle_remote_data(myrpt, f->data) == -1) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY) {
+ ast_debug(8, "@@@@ rx key\n");
+ keyed = 1;
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
+ ast_debug(8, "@@@@ rx un-key\n");
+ keyed = 0;
+ }
+ }
+ if (myrpt->hfscanstatus) {
+ myrpt->remchannel = chan; /* Save copy of channel */
+ myrpt->remotetx = 0;
+ ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY);
+ if (!myrpt->remoterx) {
+ ast_indicate(myrpt->remchannel, AST_CONTROL_RADIO_KEY);
+ }
+ if (myrpt->hfscanstatus < 0) {
+ if (myrpt->hfscanstatus == -1) {
+ if (ast_safe_sleep(myrpt->remchannel, 1000) == -1)
+ break;
+ }
+ sayfile(myrpt->remchannel, "rpt/stop");
+ } else {
+ saynum(myrpt->remchannel, myrpt->hfscanstatus );
+ }
+ rmt_telem_finish(myrpt, myrpt->remchannel);
+ myrpt->hfscanstatus = 0;
+ }
+ ast_frfree(f);
+ rpt_mutex_lock(&myrpt->lock);
+ c = myrpt->macrobuf[0];
+ if (c && (!myrpt->macrotimer)) {
+ myrpt->macrotimer = MACROTIME;
+ memmove(myrpt->macrobuf, myrpt->macrobuf + 1, sizeof(myrpt->macrobuf) - 1);
+ if ((c == 'p') || (c == 'P'))
+ myrpt->macrotimer = MACROPTIME;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (handle_remote_dtmf_digit(myrpt, c, &keyed, 0) == -1)
+ break;
+ continue;
+ }
+ c = myrpt->gosubbuf[0];
+ if (c && (!myrpt->gosubtimer)) {
+ myrpt->gosubtimer = GOSUBTIME;
+ memmove(myrpt->gosubbuf, myrpt->gosubbuf + 1, sizeof(myrpt->gosubbuf) - 1);
+ if ((c == 'p') || (c == 'P'))
+ myrpt->gosubtimer = GOSUBPTIME;
+ rpt_mutex_unlock(&myrpt->lock);
+ if (handle_remote_dtmf_digit(myrpt, c, &keyed, 0) == -1)
+ break;
+ continue;
+ }
+ rpt_mutex_unlock(&myrpt->lock);
+ continue;
+ }
+ if (who == myrpt->rxchannel) { /* if it was a read from radio */
+ f = ast_read(myrpt->rxchannel);
+ if (!f) {
+ ast_debug(1, "@@@@ link:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ if ((myrpt->remote) && (myrpt->remotetx))
+ memset(f->data, 0, f->datalen);
+ ast_write(chan, f);
+ } else if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ /* if RX key */
+ if (f->subclass == AST_CONTROL_RADIO_KEY) {
+ ast_debug(8, "@@@@ remote rx key\n");
+ if (!myrpt->remotetx) {
+ ast_indicate(chan, AST_CONTROL_RADIO_KEY);
+ myrpt->remoterx = 1;
+ }
+ }
+ /* if RX un-key */
+ if (f->subclass == AST_CONTROL_RADIO_UNKEY) {
+ ast_debug(8, "@@@@ remote rx un-key\n");
+ if (!myrpt->remotetx) {
+ ast_indicate(chan, AST_CONTROL_RADIO_UNKEY);
+ myrpt->remoterx = 0;
+ }
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+ if ((myrpt->rxchannel != myrpt->txchannel) && (who == myrpt->txchannel)) {
+ /* do this cuz you have to */
+ f = ast_read(myrpt->txchannel);
+ if (!f) {
+ ast_debug(1, "@@@@ link:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if (f->subclass == AST_CONTROL_HANGUP) {
+ ast_debug(1, "@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
+
+ }
+ rpt_mutex_lock(&myrpt->lock);
+ if (myrpt->rxchannel != myrpt->txchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->hfscanmode = 0;
+ myrpt->hfscanstatus = 0;
+ myrpt->remoteon = 0;
+ rpt_mutex_unlock(&myrpt->lock);
+ closerem(myrpt);
+ return res;
+}
+
+static int unload_module(void)
+{
+ int i;
+
+ for (i = 0; i < nrpts; i++) {
+ if (!strcmp(rpt_vars[i].name, rpt_vars[i].p.nodes))
+ continue;
+ ast_mutex_destroy(&rpt_vars[i].lock);
+ }
+ i = ast_unregister_application(app);
+
+ /* Unregister cli extensions */
+ ast_cli_unregister_multiple(cli_rpt, sizeof(cli_rpt) / sizeof(struct ast_cli_entry));
+
+ return i;
+}
+
+static int load_module(void)
+{
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+ struct ast_config *cfg = ast_config_load("rpt.conf", config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "No such configuration file rpt.conf\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ ast_pthread_create(&rpt_master_thread, NULL, rpt_master, cfg);
+
+ /* Register cli extensions */
+ ast_cli_register_multiple(cli_rpt, sizeof(cli_rpt) / sizeof(struct ast_cli_entry));
+
+ return ast_register_application(app, rpt_exec, synopsis, descrip);
+}
+
+static int reload(void)
+{
+ int n;
+
+ for (n = 0; n < nrpts; n++)
+ rpt_vars[n].reload = 1;
+ return(0);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Radio Repeater / Remote Base",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/apps/app_sayunixtime.c b/trunk/apps/app_sayunixtime.c
new file mode 100644
index 000000000..d0e23449e
--- /dev/null
+++ b/trunk/apps/app_sayunixtime.c
@@ -0,0 +1,112 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2003, 2006 Tilghman Lesher. All rights reserved.
+ * Copyright (c) 2006 Digium, Inc.
+ *
+ * Tilghman Lesher <app_sayunixtime__200309@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief SayUnixTime application
+ *
+ * \author Tilghman Lesher <app_sayunixtime__200309@the-tilghman.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/say.h"
+#include "asterisk/app.h"
+
+static char *app_sayunixtime = "SayUnixTime";
+static char *app_datetime = "DateTime";
+
+static char *sayunixtime_synopsis = "Says a specified time in a custom format";
+
+static char *sayunixtime_descrip =
+"SayUnixTime([unixtime][,[timezone][,format]])\n"
+" unixtime - time, in seconds since Jan 1, 1970. May be negative.\n"
+" defaults to now.\n"
+" timezone - timezone, see /usr/share/zoneinfo for a list.\n"
+" defaults to machine default.\n"
+" format - a format the time is to be said in. See voicemail.conf.\n"
+" defaults to \"ABdY 'digits/at' IMp\"\n";
+static char *datetime_descrip =
+"DateTime([unixtime][,[timezone][,format]])\n"
+" unixtime - time, in seconds since Jan 1, 1970. May be negative.\n"
+" defaults to now.\n"
+" timezone - timezone, see /usr/share/zoneinfo for a list.\n"
+" defaults to machine default.\n"
+" format: - a format the time is to be said in. See voicemail.conf.\n"
+" defaults to \"ABdY 'digits/at' IMp\"\n";
+
+
+static int sayunixtime_exec(struct ast_channel *chan, void *data)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(timeval);
+ AST_APP_ARG(timezone);
+ AST_APP_ARG(format);
+ );
+ char *parse;
+ int res = 0;
+ time_t unixtime;
+
+ if (!data)
+ return 0;
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ ast_get_time_t(args.timeval, &unixtime, time(NULL), NULL);
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ if (!res)
+ res = ast_say_date_with_format(chan, unixtime, AST_DIGIT_ANY,
+ chan->language, args.format, args.timezone);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_sayunixtime);
+ res |= ast_unregister_application(app_datetime);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app_sayunixtime, sayunixtime_exec, sayunixtime_synopsis, sayunixtime_descrip);
+ res |= ast_register_application(app_datetime, sayunixtime_exec, sayunixtime_synopsis, datetime_descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say time");
diff --git a/trunk/apps/app_senddtmf.c b/trunk/apps/app_senddtmf.c
new file mode 100644
index 000000000..67bd4feaa
--- /dev/null
+++ b/trunk/apps/app_senddtmf.c
@@ -0,0 +1,127 @@
+/*
+ * 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 App to send DTMF digits
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/manager.h"
+#include "asterisk/channel.h"
+
+static char *app = "SendDTMF";
+
+static char *synopsis = "Sends arbitrary DTMF digits";
+
+static char *descrip =
+" SendDTMF(digits[,timeout_ms]): Sends DTMF digits on a channel. \n"
+" Accepted digits: 0-9, *#abcd, w (.5s pause)\n"
+" The application will either pass the assigned digits or terminate if it\n"
+" encounters an error.\n";
+
+
+static int senddtmf_exec(struct ast_channel *chan, void *vdata)
+{
+ int res = 0;
+ char *data;
+ int timeout = 0, duration = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(digits);
+ AST_APP_ARG(timeout);
+ AST_APP_ARG(duration);
+ );
+
+ if (ast_strlen_zero(vdata)) {
+ ast_log(LOG_WARNING, "SendDTMF requires an argument (digits or *#aAbBcCdD)\n");
+ return 0;
+ }
+
+ data = ast_strdupa(vdata);
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (!ast_strlen_zero(args.timeout))
+ timeout = atoi(args.timeout);
+ if (!ast_strlen_zero(args.duration))
+ duration = atoi(args.duration);
+ res = ast_dtmf_stream(chan, NULL, args.digits, timeout <= 0 ? 250 : timeout, duration);
+
+ return res;
+}
+
+static char mandescr_playdtmf[] =
+"Description: Plays a dtmf digit on the specified channel.\n"
+"Variables: (all are required)\n"
+" Channel: Channel name to send digit to\n"
+" Digit: The dtmf digit to play\n";
+
+static int manager_play_dtmf(struct mansession *s, const struct message *m)
+{
+ const char *channel = astman_get_header(m, "Channel");
+ const char *digit = astman_get_header(m, "Digit");
+ struct ast_channel *chan = ast_get_channel_by_name_locked(channel);
+
+ if (!chan) {
+ astman_send_error(s, m, "Channel not specified");
+ return 0;
+ }
+ if (!digit) {
+ astman_send_error(s, m, "No digit specified");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ ast_senddigit(chan, *digit, 0);
+
+ ast_channel_unlock(chan);
+ astman_send_ack(s, m, "DTMF successfully queued");
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+ res |= ast_manager_unregister("PlayDTMF");
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_manager_register2( "PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf, "Play DTMF signal on a specific channel.", mandescr_playdtmf );
+ res |= ast_register_application(app, senddtmf_exec, synopsis, descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send DTMF digits Application");
diff --git a/trunk/apps/app_sendtext.c b/trunk/apps/app_sendtext.c
new file mode 100644
index 000000000..e55c59528
--- /dev/null
+++ b/trunk/apps/app_sendtext.c
@@ -0,0 +1,100 @@
+/*
+ * 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 App to transmit a text message
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note Requires support of sending text messages from channel driver
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+
+static const char *app = "SendText";
+
+static const char *synopsis = "Send a Text Message";
+
+static const char *descrip =
+" SendText(text[,options]): Sends text to current channel (callee).\n"
+"Result of transmission will be stored in the SENDTEXTSTATUS\n"
+"channel variable:\n"
+" SUCCESS Transmission succeeded\n"
+" FAILURE Transmission failed\n"
+" UNSUPPORTED Text transmission not supported by channel\n"
+"\n"
+"At this moment, text is supposed to be 7 bit ASCII in most channels.\n";
+
+static int sendtext_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *status = "UNSUPPORTED";
+ char *parse = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(text);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SendText requires an argument (text[,options])\n");
+ return -1;
+ } else
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options) {
+ }
+
+ ast_channel_lock(chan);
+ if (!chan->tech->send_text) {
+ ast_channel_unlock(chan);
+ /* Does not support transport */
+ return 0;
+ }
+ status = "FAILURE";
+ ast_channel_unlock(chan);
+ res = ast_sendtext(chan, args.text);
+ if (!res)
+ status = "SUCCESS";
+ pbx_builtin_setvar_helper(chan, "SENDTEXTSTATUS", status);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, sendtext_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send Text Applications");
diff --git a/trunk/apps/app_setcallerid.c b/trunk/apps/app_setcallerid.c
new file mode 100644
index 000000000..c76b80eef
--- /dev/null
+++ b/trunk/apps/app_setcallerid.c
@@ -0,0 +1,93 @@
+/*
+ * 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 App to set callerid presentation
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/image.h"
+#include "asterisk/callerid.h"
+
+static char *app2 = "SetCallerPres";
+
+static char *synopsis2 = "Set CallerID Presentation";
+
+
+static char *descrip2 =
+" SetCallerPres(presentation): Set Caller*ID presentation on a call.\n"
+" Valid presentations are:\n"
+"\n"
+" allowed_not_screened : Presentation Allowed, Not Screened\n"
+" allowed_passed_screen : Presentation Allowed, Passed Screen\n"
+" allowed_failed_screen : Presentation Allowed, Failed Screen\n"
+" allowed : Presentation Allowed, Network Number\n"
+" prohib_not_screened : Presentation Prohibited, Not Screened\n"
+" prohib_passed_screen : Presentation Prohibited, Passed Screen\n"
+" prohib_failed_screen : Presentation Prohibited, Failed Screen\n"
+" prohib : Presentation Prohibited, Network Number\n"
+" unavailable : Number Unavailable\n"
+"\n"
+;
+
+static int setcallerid_pres_exec(struct ast_channel *chan, void *data)
+{
+ int pres = -1;
+ static int deprecated = 0;
+
+ if (!deprecated) {
+ deprecated = 1;
+ ast_log(LOG_WARNING, "SetCallerPres is deprecated. Please use Set(CALLERPRES()=%s) instead.\n", (char *)data);
+ }
+ pres = ast_parse_caller_presentation(data);
+
+ if (pres < 0) {
+ ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show application SetCallerPres')\n",
+ (char *) data);
+ return 0;
+ }
+
+ chan->cid.cid_pres = pres;
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app2);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app2, setcallerid_pres_exec, synopsis2, descrip2);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Set CallerID Presentation Application");
diff --git a/trunk/apps/app_skel.c b/trunk/apps/app_skel.c
new file mode 100644
index 000000000..8365b506a
--- /dev/null
+++ b/trunk/apps/app_skel.c
@@ -0,0 +1,125 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) <Year>, <Your Name Here>
+ *
+ * <Your Name Here> <<Your Email Here>>
+ *
+ * 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 Skeleton application
+ *
+ * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk application
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *app = "Skel";
+static char *synopsis =
+"Skeleton application.";
+static char *descrip = "This application is a template to build other applications from.\n"
+ " It shows you the basic structure to create your own Asterisk applications.\n";
+
+enum {
+ OPTION_A = (1 << 0),
+ OPTION_B = (1 << 1),
+ OPTION_C = (1 << 2),
+} option_flags;
+
+enum {
+ OPTION_ARG_B = 0,
+ OPTION_ARG_C = 1,
+ /* This *must* be the last value in this enum! */
+ OPTION_ARG_ARRAY_SIZE = 2,
+} option_args;
+
+AST_APP_OPTIONS(app_opts,{
+ AST_APP_OPTION('a', OPTION_A),
+ AST_APP_OPTION_ARG('b', OPTION_B, OPTION_ARG_B),
+ AST_APP_OPTION_ARG('c', OPTION_C, OPTION_ARG_C),
+});
+
+
+static int app_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_flags flags;
+ char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(dummy);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s requires an argument (dummy[,options])\n", app);
+ return -1;
+ }
+
+ /* Do our thing here */
+
+ /* We need to make a copy of the input string if we are going to modify it! */
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc == 2)
+ ast_app_parse_options(app_opts, &flags, opts, args.options);
+
+ if (!ast_strlen_zero(args.dummy))
+ ast_log(LOG_NOTICE, "Dummy value is : %s\n", args.dummy);
+
+ if (ast_test_flag(&flags, OPTION_A))
+ ast_log(LOG_NOTICE, "Option A is set\n");
+
+ if (ast_test_flag(&flags, OPTION_B))
+ ast_log(LOG_NOTICE, "Option B is set with : %s\n", opts[OPTION_ARG_B] ? opts[OPTION_ARG_B] : "<unspecified>");
+
+ if (ast_test_flag(&flags, OPTION_C))
+ ast_log(LOG_NOTICE, "Option C is set with : %s\n", opts[OPTION_ARG_C] ? opts[OPTION_ARG_C] : "<unspecified>");
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+ res = ast_unregister_application(app);
+ return res;
+}
+
+static int load_module(void)
+{
+ if (ast_register_application(app, app_exec, synopsis, descrip))
+ return AST_MODULE_LOAD_DECLINE;
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Application");
diff --git a/trunk/apps/app_sms.c b/trunk/apps/app_sms.c
new file mode 100644
index 000000000..3c54d43f1
--- /dev/null
+++ b/trunk/apps/app_sms.c
@@ -0,0 +1,1932 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005, Adrian Kennard, rights assigned to Digium
+ *
+ * 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 SMS application - ETSI ES 201 912 protocol 1 implementation
+ *
+ * \par Development notes
+ * \note The ETSI standards are available free of charge from ETSI at
+ * http://pda.etsi.org/pda/queryform.asp
+ * Among the relevant documents here we have:
+ *
+ * ES 201 912 SMS for PSTN/ISDN
+ * TS 123 040 Technical realization of SMS
+ *
+ *
+ * \ingroup applications
+ *
+ * \author Adrian Kennard (for the original protocol 1 code)
+ * \author Filippo Grassilli (Hyppo) - protocol 2 support
+ * Not fully tested, under development
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR and LOG_DIR */
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/alaw.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+/* #define OUTALAW */ /* enable this to output Alaw rather than linear */
+
+/* ToDo */
+/* Add full VP support */
+/* Handle status report messages (generation and reception) */
+/* Time zones on time stamps */
+/* user ref field */
+
+static volatile unsigned char message_ref; /* arbitary message ref */
+static volatile unsigned int seq; /* arbitrary message sequence number for unqiue files */
+
+static char log_file[255];
+
+static char *app = "SMS";
+
+static char *synopsis = "Communicates with SMS service centres and SMS capable analogue phones";
+
+static char *descrip =
+ " SMS(name,[a][s][t][p(d)][r][o],addr,body):\n"
+ "SMS handles exchange of SMS data with a call to/from SMS capable\n"
+ "phone or SMS PSTN service center. Can send and/or receive SMS messages.\n"
+ "Works to ETSI ES 201 912; compatible with BT SMS PSTN service in UK\n"
+ "and Telecom Italia in Italy.\n"
+ "Typical usage is to use to handle calls from the SMS service centre CLI,\n"
+ "or to set up a call using 'outgoing' or manager interface to connect\n"
+ "service centre to SMS()\n"
+ "name is the name of the queue used in /var/spool/asterisk/sms\n"
+ "Arguments:\n"
+ " a - answer, i.e. send initial FSK packet.\n"
+ " s - act as service centre talking to a phone.\n"
+ " t - use protocol 2 (default used is protocol 1).\n"
+ " p(N) - set the initial delay to N ms (default is 300).\n"
+ " addr and body are a deprecated format to send messages out.\n"
+ " s - set the Status Report Request (SRR) bit.\n"
+ " o - the body should be coded as octets not 7-bit symbols.\n"
+ "Messages are processed as per text file message queues.\n"
+ "smsq (a separate software) is a command to generate message\n"
+ "queues and send messages.\n"
+ "NOTE: the protocol has tight delay bounds. Please use short frames\n"
+ "and disable/keep short the jitter buffer on the ATA to make sure that\n"
+ "respones (ACK etc.) are received in time.\n";
+
+/*
+ * 80 samples of a single period of the wave. At 8000 Hz, it means these
+ * are the samples of a 100 Hz signal.
+ * To pick the two carriers (1300Hz for '1' and 2100 Hz for '0') used by
+ * the modulation, we should take one every 13 and 21 samples respectively.
+ */
+static signed short wave[] = {
+ 0, 392, 782, 1167, 1545, 1913, 2270, 2612, 2939, 3247, 3536, 3802, 4045, 4263, 4455, 4619, 4755, 4862, 4938, 4985,
+ 5000, 4985, 4938, 4862, 4755, 4619, 4455, 4263, 4045, 3802, 3536, 3247, 2939, 2612, 2270, 1913, 1545, 1167, 782, 392,
+ 0, -392, -782, -1167,
+ -1545, -1913, -2270, -2612, -2939, -3247, -3536, -3802, -4045, -4263, -4455, -4619, -4755, -4862, -4938, -4985, -5000,
+ -4985, -4938, -4862,
+ -4755, -4619, -4455, -4263, -4045, -3802, -3536, -3247, -2939, -2612, -2270, -1913, -1545, -1167, -782, -392
+};
+
+#ifdef OUTALAW
+static unsigned char wavea[80];
+typedef unsigned char output_t;
+static const output_t *wave_out = wavea; /* outgoing samples */
+#define __OUT_FMT AST_FORMAT_ALAW;
+#else
+typedef signed short output_t;
+static const output_t *wave_out = wave; /* outgoing samples */
+#define __OUT_FMT AST_FORMAT_SLINEAR
+#endif
+
+#define OSYNC_BITS 80 /* initial sync bits */
+
+/*!
+ * The SMS spec ETSI ES 201 912 defines two protocols with different message types.
+ * Also note that the high bit is used to indicate whether the message
+ * is complete or not, but in two opposite ways:
+ * for Protocol 1, 0x80 means that the message is complete;
+ * for Protocol 2, 0x00 means that the message is complete;
+ */
+enum message_types {
+ DLL_SMS_MASK = 0x7f, /* mask for the valid bits */
+
+ /* Protocol 1 values */
+ DLL1_SMS_DATA = 0x11, /* data packet */
+ DLL1_SMS_ERROR = 0x12,
+ DLL1_SMS_EST = 0x13, /* start the connection */
+ DLL1_SMS_REL = 0x14, /* end the connection */
+ DLL1_SMS_ACK = 0x15,
+ DLL1_SMS_NACK = 0x16,
+
+ DLL1_SMS_COMPLETE = 0x80, /* packet is complete */
+ DLL1_SMS_MORE = 0x00, /* more data to follow */
+
+ /* Protocol 2 values */
+ DLL2_SMS_EST = 0x7f, /* magic number. No message body */
+ DLL2_SMS_INFO_MO = 0x10,
+ DLL2_SMS_INFO_MT = 0x11,
+ DLL2_SMS_INFO_STA = 0x12,
+ DLL2_SMS_NACK = 0x13,
+ DLL2_SMS_ACK0 = 0x14, /* ack even-numbered frame */
+ DLL2_SMS_ACK1 = 0x15, /* ack odd-numbered frame */
+ DLL2_SMS_ENQ = 0x16,
+ DLL2_SMS_REL = 0x17, /* end the connection */
+
+ DLL2_SMS_COMPLETE = 0x00, /* packet is complete */
+ DLL2_SMS_MORE = 0x80, /* more data to follow */
+};
+
+/* SMS 7 bit character mapping to UCS-2 */
+static const unsigned short defaultalphabet[] = {
+ 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC,
+ 0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5,
+ 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
+ 0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9,
+ ' ', '!', '"', '#', 164, '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
+ 161, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 196, 214, 209, 220, 167,
+ 191, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 228, 246, 241, 252, 224,
+};
+
+static const unsigned short escapes[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x000C, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0x005E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0x007B, 0x007D, 0, 0, 0, 0, 0, 0x005C,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x005B, 0x007E, 0x005D, 0,
+ 0x007C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x20AC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+#define SMSLEN 160 /*!< max SMS length */
+#define SMSLEN_8 140 /*!< max SMS length for 8-bit char */
+
+typedef struct sms_s {
+ unsigned char hangup; /*!< we are done... */
+ unsigned char err; /*!< set for any errors */
+ unsigned char smsc:1; /*!< we are SMSC */
+ unsigned char rx:1; /*!< this is a received message */
+ char queue[30]; /*!< queue name */
+ char oa[20]; /*!< originating address */
+ char da[20]; /*!< destination address */
+ struct timeval scts; /*!< time stamp, UTC */
+ unsigned char pid; /*!< protocol ID */
+ unsigned char dcs; /*!< data coding scheme */
+ short mr; /*!< message reference - actually a byte, but use -1 for not set */
+ int udl; /*!< user data length */
+ int udhl; /*!< user data header length */
+ unsigned char srr:1; /*!< Status Report request */
+ unsigned char udhi:1; /*!< User Data Header required, even if length 0 */
+ unsigned char rp:1; /*!< Reply Path */
+ unsigned int vp; /*!< validity period in minutes, 0 for not set */
+ unsigned short ud[SMSLEN]; /*!< user data (message), UCS-2 coded */
+ unsigned char udh[SMSLEN]; /*!< user data header */
+ char cli[20]; /*!< caller ID */
+ unsigned char ophase; /*!< phase (0-79) for 0 and 1 frequencies (1300Hz and 2100Hz) */
+ unsigned char ophasep; /*!< phase (0-79) for 1200 bps */
+ unsigned char obyte; /*!< byte being sent */
+ unsigned int opause; /*!< silent pause before sending (in sample periods) */
+ unsigned char obitp; /*!< bit in byte */
+ unsigned char osync; /*!< sync bits to send */
+ unsigned char obytep; /*!< byte in data */
+ unsigned char obyten; /*!< bytes in data */
+ unsigned char omsg[256]; /*!< data buffer (out) */
+ unsigned char imsg[250]; /*!< data buffer (in) */
+ signed long long ims0,
+ imc0,
+ ims1,
+ imc1; /*!< magnitude averages sin/cos 0/1 */
+ unsigned int idle;
+ unsigned short imag; /*!< signal level */
+ unsigned char ips0; /*!< phase sin for bit 0, start at 0 inc by 21 mod 80 */
+ unsigned char ips1; /*!< phase cos for bit 0, start at 20 inc by 21 mod 80 */
+ unsigned char ipc0; /*!< phase sin for bit 1, start at 0 inc by 13 mod 80 */
+ unsigned char ipc1; /*!< phase cos for bit 1, start at 20 inc by 13 mod 80 */
+ unsigned char ibitl; /*!< last bit */
+ unsigned char ibitc; /*!< bit run length count */
+ unsigned char iphasep; /*!< bit phase (0-79) for 1200 bps */
+ unsigned char ibitn; /*!< bit number in byte being received */
+ unsigned char ibytev; /*!< byte value being received */
+ unsigned char ibytep; /*!< byte pointer in message */
+ unsigned char ibytec; /*!< byte checksum for message */
+ unsigned char ierr; /*!< error flag */
+ unsigned char ibith; /*!< history of last bits */
+ unsigned char ibitt; /*!< total of 1's in last 3 bytes */
+ /* more to go here */
+
+ int opause_0; /*!< initial delay in ms, p() option */
+ int protocol; /*!< ETSI SMS protocol to use (passed at app call) */
+ int oseizure; /*!< protocol 2: channel seizure bits to send */
+ int framenumber; /*!< protocol 2: frame number (for sending ACK0 or ACK1) */
+ char udtxt[SMSLEN]; /*!< user data (message), PLAIN text */
+} sms_t;
+
+/* different types of encoding */
+#define is7bit(dcs) ( ((dcs) & 0xC0) ? (!((dcs)&4) ) : (((dcs) & 0xc) == 0) )
+#define is8bit(dcs) ( ((dcs) & 0xC0) ? ( ((dcs)&4) ) : (((dcs) & 0xc) == 4) )
+#define is16bit(dcs) ( ((dcs) & 0xC0) ? 0 : (((dcs) & 0xc) == 8) )
+
+static void sms_messagetx(sms_t *h);
+
+/*! \brief copy number, skipping non digits apart from leading + */
+static void numcpy(char *d, char *s)
+{
+ if (*s == '+')
+ *d++ = *s++;
+ while (*s) {
+ if (isdigit(*s))
+ *d++ = *s;
+ s++;
+ }
+ *d = 0;
+}
+
+/*! \brief static, return a date/time in ISO format */
+static char *isodate(time_t t, char *buf, int len)
+{
+ struct ast_tm tm;
+ struct timeval tv = { t, 0 };
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
+ return buf;
+}
+
+/*! \brief Reads next UCS character from NUL terminated UTF-8 string and advance pointer */
+/* for non valid UTF-8 sequences, returns character as is */
+/* Does not advance pointer for null termination */
+static long utf8decode(unsigned char **pp)
+{
+ unsigned char *p = *pp;
+ if (!*p)
+ return 0; /* null termination of string */
+ (*pp)++;
+ if (*p < 0xC0)
+ return *p; /* ascii or continuation character */
+ if (*p < 0xE0) {
+ if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp)++;
+ return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
+ }
+ if (*p < 0xF0) {
+ if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 2;
+ return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
+ }
+ if (*p < 0xF8) {
+ if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 3;
+ return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
+ }
+ if (*p < 0xFC) {
+ if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
+ || (p[4] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 4;
+ return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
+ }
+ if (*p < 0xFE) {
+ if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
+ || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 5;
+ return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) + (p[5] & 0x3F);
+ }
+ return *p; /* not sensible */
+}
+
+/*! \brief takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using SMS 7 bit character codes */
+/* The return value is the number of septets packed in to o, which is internally limited to SMSLEN */
+/* o can be null, in which case this is used to validate or count only */
+/* if the input contains invalid characters then the return value is -1 */
+static int packsms7(unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char p = 0; /* output pointer (bytes) */
+ unsigned char b = 0; /* bit position */
+ unsigned char n = 0; /* output character count */
+ unsigned char dummy[SMSLEN];
+
+ if (o == NULL) /* output to a dummy buffer if o not set */
+ o = dummy;
+
+ if (udhl) { /* header */
+ o[p++] = udhl;
+ b = 1;
+ n = 1;
+ while (udhl--) {
+ o[p++] = *udh++;
+ b += 8;
+ while (b >= 7) {
+ b -= 7;
+ n++;
+ }
+ if (n >= SMSLEN)
+ return n;
+ }
+ if (b) {
+ b = 7 - b;
+ if (++n >= SMSLEN)
+ return n;
+ }; /* filling to septet boundary */
+ }
+ o[p] = 0;
+ /* message */
+ while (udl--) {
+ long u;
+ unsigned char v;
+ u = *ud++;
+ /* XXX 0 is invalid ? */
+ /* look up in defaultalphabet[]. If found, v is the 7-bit code */
+ for (v = 0; v < 128 && defaultalphabet[v] != u; v++);
+ if (v == 128 /* not found */ && u && n + 1 < SMSLEN) {
+ /* if not found, look in the escapes table (we need 2 bytes) */
+ for (v = 0; v < 128 && escapes[v] != u; v++);
+ if (v < 128) { /* escaped sequence, esc + v */
+ /* store the low (8-b) bits in o[p], the remaining bits in o[p+1] */
+ o[p] |= (27 << b); /* the low bits go into o[p] */
+ b += 7;
+ if (b >= 8) {
+ b -= 8;
+ p++;
+ o[p] = (27 >> (7 - b));
+ }
+ n++;
+ }
+ }
+ if (v == 128)
+ return -1; /* invalid character */
+ /* store, same as above */
+ o[p] |= (v << b);
+ b += 7;
+ if (b >= 8) {
+ b -= 8;
+ p++;
+ o[p] = (v >> (7 - b));
+ }
+ if (++n >= SMSLEN)
+ return n;
+ }
+ return n;
+}
+
+/*! \brief takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud)
+ * and packs in to o using 8 bit character codes.
+ * The return value is the number of bytes packed in to o, which is internally limited to 140.
+ * o can be null, in which case this is used to validate or count only.
+ * if the input contains invalid characters then the return value is -1
+ */
+static int packsms8(unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char p = 0;
+ unsigned char dummy[SMSLEN_8];
+
+ if (o == NULL)
+ o = dummy;
+ /* header - no encoding */
+ if (udhl) {
+ o[p++] = udhl;
+ while (udhl--) {
+ o[p++] = *udh++;
+ if (p >= SMSLEN_8)
+ return p;
+ }
+ }
+ while (udl--) {
+ long u;
+ u = *ud++;
+ if (u < 0 || u > 0xFF)
+ return -1; /* not valid */
+ o[p++] = u;
+ if (p >= SMSLEN_8)
+ return p;
+ }
+ return p;
+}
+
+/*! \brief takes a binary header (udhl bytes at udh) and UCS-2
+ message (udl characters at ud) and packs in to o using 16 bit
+ UCS-2 character codes
+ The return value is the number of bytes packed in to o, which is
+ internally limited to 140
+ o can be null, in which case this is used to validate or count
+ only if the input contains invalid characters then
+ the return value is -1 */
+static int packsms16(unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char p = 0;
+ unsigned char dummy[SMSLEN_8];
+
+ if (o == NULL)
+ o = dummy;
+ /* header - no encoding */
+ if (udhl) {
+ o[p++] = udhl;
+ while (udhl--) {
+ o[p++] = *udh++;
+ if (p >= SMSLEN_8)
+ return p;
+ }
+ }
+ while (udl--) {
+ long u;
+ u = *ud++;
+ o[p++] = (u >> 8);
+ if (p >= SMSLEN_8)
+ return p - 1; /* could not fit last character */
+ o[p++] = u;
+ if (p >= SMSLEN_8)
+ return p;
+ }
+ return p;
+}
+
+/*! \brief general pack, with length and data,
+ returns number of bytes of target used */
+static int packsms(unsigned char dcs, unsigned char *base, unsigned int udhl, unsigned char *udh, int udl, unsigned short *ud)
+{
+ unsigned char *p = base;
+ if (udl == 0)
+ *p++ = 0; /* no user data */
+ else {
+
+ int l = 0;
+ if (is7bit(dcs)) { /* 7 bit */
+ l = packsms7(p + 1, udhl, udh, udl, ud);
+ if (l < 0)
+ l = 0;
+ *p++ = l;
+ p += (l * 7 + 7) / 8;
+ } else if (is8bit(dcs)) { /* 8 bit */
+ l = packsms8(p + 1, udhl, udh, udl, ud);
+ if (l < 0)
+ l = 0;
+ *p++ = l;
+ p += l;
+ } else { /* UCS-2 */
+ l = packsms16(p + 1, udhl, udh, udl, ud);
+ if (l < 0)
+ l = 0;
+ *p++ = l;
+ p += l;
+ }
+ }
+ return p - base;
+}
+
+
+/*! \brief pack a date and return */
+static void packdate(unsigned char *o, time_t w)
+{
+ struct ast_tm t;
+ struct timeval tv = { w, 0 };
+ int z;
+
+ ast_localtime(&tv, &t, NULL);
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) || defined(__CYGWIN__)
+ z = -t.tm_gmtoff / 60 / 15;
+#else
+ z = timezone / 60 / 15;
+#endif
+ *o++ = ((t.tm_year % 10) << 4) + (t.tm_year % 100) / 10;
+ *o++ = (((t.tm_mon + 1) % 10) << 4) + (t.tm_mon + 1) / 10;
+ *o++ = ((t.tm_mday % 10) << 4) + t.tm_mday / 10;
+ *o++ = ((t.tm_hour % 10) << 4) + t.tm_hour / 10;
+ *o++ = ((t.tm_min % 10) << 4) + t.tm_min / 10;
+ *o++ = ((t.tm_sec % 10) << 4) + t.tm_sec / 10;
+ if (z < 0)
+ *o++ = (((-z) % 10) << 4) + (-z) / 10 + 0x08;
+ else
+ *o++ = ((z % 10) << 4) + z / 10;
+}
+
+/*! \brief unpack a date and return */
+static time_t unpackdate(unsigned char *i)
+{
+ struct tm t;
+ t.tm_year = 100 + (i[0] & 0xF) * 10 + (i[0] >> 4);
+ t.tm_mon = (i[1] & 0xF) * 10 + (i[1] >> 4) - 1;
+ t.tm_mday = (i[2] & 0xF) * 10 + (i[2] >> 4);
+ t.tm_hour = (i[3] & 0xF) * 10 + (i[3] >> 4);
+ t.tm_min = (i[4] & 0xF) * 10 + (i[4] >> 4);
+ t.tm_sec = (i[5] & 0xF) * 10 + (i[5] >> 4);
+ t.tm_isdst = 0;
+ if (i[6] & 0x08)
+ t.tm_min += 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
+ else
+ t.tm_min -= 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
+ return mktime(&t);
+}
+
+/*! \brief unpacks bytes (7 bit encoding) at i, len l septets,
+ and places in udh and ud setting udhl and udl. udh not used
+ if udhi not set */
+static void unpacksms7(unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ unsigned char b = 0, p = 0;
+ unsigned short *o = ud;
+ *udhl = 0;
+ if (udhi && l) { /* header */
+ int h = i[p];
+ *udhl = h;
+ if (h) {
+ b = 1;
+ p++;
+ l--;
+ while (h-- && l) {
+ *udh++ = i[p++];
+ b += 8;
+ while (b >= 7) {
+ b -= 7;
+ l--;
+ if (!l)
+ break;
+ }
+ }
+ /* adjust for fill, septets */
+ if (b) {
+ b = 7 - b;
+ l--;
+ }
+ }
+ }
+ while (l--) {
+ unsigned char v;
+ if (b < 2)
+ v = ((i[p] >> b) & 0x7F); /* everything in one byte */
+ else
+ v = ((((i[p] >> b) + (i[p + 1] << (8 - b)))) & 0x7F);
+ b += 7;
+ if (b >= 8) {
+ b -= 8;
+ p++;
+ }
+ /* 0x00A0 is the encoding of ESC (27) in defaultalphabet */
+ if (o > ud && o[-1] == 0x00A0 && escapes[v])
+ o[-1] = escapes[v];
+ else
+ *o++ = defaultalphabet[v];
+ }
+ *udl = (o - ud);
+}
+
+/*! \brief unpacks bytes (8 bit encoding) at i, len l septets,
+ and places in udh and ud setting udhl and udl. udh not used
+ if udhi not set */
+static void unpacksms8(unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ unsigned short *o = ud;
+ *udhl = 0;
+ if (udhi) {
+ int n = *i;
+ *udhl = n;
+ if (n) {
+ i++;
+ l--;
+ while (l && n) {
+ l--;
+ n--;
+ *udh++ = *i++;
+ }
+ }
+ }
+ while (l--)
+ *o++ = *i++; /* not to UTF-8 as explicitly 8 bit coding in DCS */
+ *udl = (o - ud);
+}
+
+/*! \brief unpacks bytes (16 bit encoding) at i, len l septets,
+ and places in udh and ud setting udhl and udl.
+ udh not used if udhi not set */
+static void unpacksms16(unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ unsigned short *o = ud;
+ *udhl = 0;
+ if (udhi) {
+ int n = *i;
+ *udhl = n;
+ if (n) {
+ i++;
+ l--;
+ while (l && n) {
+ l--;
+ n--;
+ *udh++ = *i++;
+ }
+ }
+ }
+ while (l--) {
+ int v = *i++;
+ if (l--)
+ v = (v << 8) + *i++;
+ *o++ = v;
+ }
+ *udl = (o - ud);
+}
+
+/*! \brief general unpack - starts with length byte (octet or septet) and returns number of bytes used, inc length */
+static int unpacksms(unsigned char dcs, unsigned char *i, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
+{
+ int l = *i++;
+ if (is7bit(dcs)) {
+ unpacksms7(i, l, udh, udhl, ud, udl, udhi);
+ l = (l * 7 + 7) / 8; /* adjust length to return */
+ } else if (is8bit(dcs))
+ unpacksms8(i, l, udh, udhl, ud, udl, udhi);
+ else
+ unpacksms16(i, l, udh, udhl, ud, udl, udhi);
+ return l + 1;
+}
+
+/*! \brief unpack an address from i, return byte length, unpack to o */
+static unsigned char unpackaddress(char *o, unsigned char *i)
+{
+ unsigned char l = i[0],
+ p;
+ if (i[1] == 0x91)
+ *o++ = '+';
+ for (p = 0; p < l; p++) {
+ if (p & 1)
+ *o++ = (i[2 + p / 2] >> 4) + '0';
+ else
+ *o++ = (i[2 + p / 2] & 0xF) + '0';
+ }
+ *o = 0;
+ return (l + 5) / 2;
+}
+
+/*! \brief store an address at o, and return number of bytes used */
+static unsigned char packaddress(unsigned char *o, char *i)
+{
+ unsigned char p = 2;
+ o[0] = 0; /* number of bytes */
+ if (*i == '+') { /* record as bit 0 in byte 1 */
+ i++;
+ o[1] = 0x91;
+ } else
+ o[1] = 0x81;
+ for ( ; *i ; i++) {
+ if (!isdigit(*i)) /* ignore non-digits */
+ continue;
+ if (o[0] & 1)
+ o[p++] |= ((*i & 0xF) << 4);
+ else
+ o[p] = (*i & 0xF);
+ o[0]++;
+ }
+ if (o[0] & 1)
+ o[p++] |= 0xF0; /* pad */
+ return p;
+}
+
+/*! \brief Log the output, and remove file */
+static void sms_log(sms_t * h, char status)
+{
+ int o;
+
+ if (*h->oa == '\0' && *h->da == '\0')
+ return;
+ o = open(log_file, O_CREAT | O_APPEND | O_WRONLY, AST_FILE_MODE);
+ if (o >= 0) {
+ char line[1000], mrs[3] = "", *p;
+ char buf[30];
+ unsigned char n;
+
+ if (h->mr >= 0)
+ snprintf(mrs, sizeof(mrs), "%02X", h->mr);
+ snprintf(line, sizeof(line), "%s %c%c%c%s %s %s %s ",
+ isodate(time(NULL), buf, sizeof(buf)),
+ status, h->rx ? 'I' : 'O', h->smsc ? 'S' : 'M', mrs, h->queue,
+ S_OR(h->oa, "-"), S_OR(h->da, "-") );
+ p = line + strlen(line);
+ for (n = 0; n < h->udl; n++) {
+ if (h->ud[n] == '\\') {
+ *p++ = '\\';
+ *p++ = '\\';
+ } else if (h->ud[n] == '\n') {
+ *p++ = '\\';
+ *p++ = 'n';
+ } else if (h->ud[n] == '\r') {
+ *p++ = '\\';
+ *p++ = 'r';
+ } else if (h->ud[n] < 32 || h->ud[n] == 127)
+ *p++ = 191;
+ else
+ *p++ = h->ud[n];
+ }
+ *p++ = '\n';
+ *p = 0;
+ write(o, line, strlen(line));
+ close(o);
+ }
+ *h->oa = *h->da = h->udl = 0;
+}
+
+/*! \brief parse and delete a file */
+static void sms_readfile(sms_t * h, char *fn)
+{
+ char line[1000];
+ FILE *s;
+ char dcsset = 0; /* if DSC set */
+ ast_log(LOG_EVENT, "Sending %s\n", fn);
+ h->rx = h->udl = *h->oa = *h->da = h->pid = h->srr = h->udhi = h->rp = h->vp = h->udhl = 0;
+ h->mr = -1;
+ h->dcs = 0xF1; /* normal messages class 1 */
+ h->scts = ast_tvnow();
+ s = fopen(fn, "r");
+ if (s) {
+ if (unlink(fn)) { /* concurrent access, we lost */
+ fclose(s);
+ return;
+ }
+ while (fgets (line, sizeof(line), s)) { /* process line in file */
+ char *p;
+ void *pp = &p;
+ for (p = line; *p && *p != '\n' && *p != '\r'; p++);
+ *p = 0; /* strip eoln */
+ p = line;
+ if (!*p || *p == ';')
+ continue; /* blank line or comment, ignore */
+ while (isalnum(*p)) {
+ *p = tolower (*p);
+ p++;
+ }
+ while (isspace (*p))
+ *p++ = 0;
+ if (*p == '=') {
+ *p++ = 0;
+ if (!strcmp(line, "ud")) { /* parse message (UTF-8) */
+ unsigned char o = 0;
+ memcpy(h->udtxt, p, SMSLEN); /* for protocol 2 */
+ while (*p && o < SMSLEN)
+ h->ud[o++] = utf8decode(pp);
+ h->udl = o;
+ if (*p)
+ ast_log(LOG_WARNING, "UD too long in %s\n", fn);
+ } else {
+ while (isspace (*p))
+ p++;
+ if (!strcmp(line, "oa") && strlen(p) < sizeof(h->oa))
+ numcpy (h->oa, p);
+ else if (!strcmp(line, "da") && strlen(p) < sizeof(h->oa))
+ numcpy (h->da, p);
+ else if (!strcmp(line, "pid"))
+ h->pid = atoi(p);
+ else if (!strcmp(line, "dcs")) {
+ h->dcs = atoi(p);
+ dcsset = 1;
+ } else if (!strcmp(line, "mr"))
+ h->mr = atoi(p);
+ else if (!strcmp(line, "srr"))
+ h->srr = (atoi(p) ? 1 : 0);
+ else if (!strcmp(line, "vp"))
+ h->vp = atoi(p);
+ else if (!strcmp(line, "rp"))
+ h->rp = (atoi(p) ? 1 : 0);
+ else if (!strcmp(line, "scts")) { /* get date/time */
+ int Y,
+ m,
+ d,
+ H,
+ M,
+ S;
+ if (sscanf (p, "%d-%d-%dT%d:%d:%d", &Y, &m, &d, &H, &M, &S) == 6) {
+ struct ast_tm t = { 0, };
+ t.tm_year = Y - 1900;
+ t.tm_mon = m - 1;
+ t.tm_mday = d;
+ t.tm_hour = H;
+ t.tm_min = M;
+ t.tm_sec = S;
+ t.tm_isdst = -1;
+ h->scts = ast_mktime(&t, NULL);
+ if (h->scts.tv_sec == 0)
+ ast_log(LOG_WARNING, "Bad date/timein %s: %s", fn, p);
+ }
+ } else
+ ast_log(LOG_WARNING, "Cannot parse in %s: %s=%si\n", fn, line, p);
+ }
+ } else if (*p == '#') { /* raw hex format */
+ *p++ = 0;
+ if (*p == '#') {
+ p++;
+ if (!strcmp(line, "ud")) { /* user data */
+ int o = 0;
+ while (*p && o < SMSLEN) {
+ if (isxdigit(*p) && isxdigit(p[1]) && isxdigit(p[2]) && isxdigit(p[3])) {
+ h->ud[o++] =
+ (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 12) +
+ (((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
+ (((isalpha(p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha(p[3]) ? 9 : 0) + (p[3] & 0xF));
+ p += 4;
+ } else
+ break;
+ }
+ h->udl = o;
+ if (*p)
+ ast_log(LOG_WARNING, "UD too long / invalid UCS-2 hex in %s\n", fn);
+ } else
+ ast_log(LOG_WARNING, "Only ud can use ## format, %s\n", fn);
+ } else if (!strcmp(line, "ud")) { /* user data */
+ int o = 0;
+ while (*p && o < SMSLEN) {
+ if (isxdigit(*p) && isxdigit(p[1])) {
+ h->ud[o++] = (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF));
+ p += 2;
+ } else
+ break;
+ }
+ h->udl = o;
+ if (*p)
+ ast_log(LOG_WARNING, "UD too long / invalid UCS-1 hex in %s\n", fn);
+ } else if (!strcmp(line, "udh")) { /* user data header */
+ unsigned char o = 0;
+ h->udhi = 1;
+ while (*p && o < SMSLEN) {
+ if (isxdigit(*p) && isxdigit(p[1])) {
+ h->udh[o] = (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF));
+ o++;
+ p += 2;
+ } else
+ break;
+ }
+ h->udhl = o;
+ if (*p)
+ ast_log(LOG_WARNING, "UDH too long / invalid hex in %s\n", fn);
+ } else
+ ast_log(LOG_WARNING, "Only ud and udh can use # format, %s\n", fn);
+ } else
+ ast_log(LOG_WARNING, "Cannot parse in %s: %s\n", fn, line);
+ }
+ fclose(s);
+ if (!dcsset && packsms7(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
+ if (packsms8(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
+ if (packsms16(0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log(LOG_WARNING, "Invalid UTF-8 message even for UCS-2 (%s)\n", fn);
+ else {
+ h->dcs = 0x08; /* default to 16 bit */
+ ast_log(LOG_WARNING, "Sending in 16 bit format(%s)\n", fn);
+ }
+ } else {
+ h->dcs = 0xF5; /* default to 8 bit */
+ ast_log(LOG_WARNING, "Sending in 8 bit format(%s)\n", fn);
+ }
+ }
+ if (is7bit(h->dcs) && packsms7(0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log(LOG_WARNING, "Invalid 7 bit GSM data %s\n", fn);
+ if (is8bit(h->dcs) && packsms8(0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log(LOG_WARNING, "Invalid 8 bit data %s\n", fn);
+ if (is16bit(h->dcs) && packsms16(0, h->udhl, h->udh, h->udl, h->ud) < 0)
+ ast_log(LOG_WARNING, "Invalid 16 bit data %s\n", fn);
+ }
+}
+
+/*! \brief white a received text message to a file */
+static void sms_writefile(sms_t * h)
+{
+ char fn[200] = "", fn2[200] = "";
+ char buf[30];
+ FILE *o;
+
+ snprintf(fn, sizeof(fn), "%s/sms/%s", ast_config_AST_SPOOL_DIR, h->smsc ? h->rx ? "morx" : "mttx" : h->rx ? "mtrx" : "motx");
+ ast_mkdir(fn, 0777); /* ensure it exists */
+ ast_copy_string(fn2, fn, sizeof(fn2));
+ snprintf(fn2 + strlen(fn2), sizeof(fn2) - strlen(fn2), "/%s.%s-%d", h->queue, isodate(h->scts.tv_sec, buf, sizeof(buf)), seq++);
+ snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/.%s", fn2 + strlen(fn) + 1);
+ o = fopen(fn, "w");
+ if (o == NULL)
+ return;
+
+ if (*h->oa)
+ fprintf(o, "oa=%s\n", h->oa);
+ if (*h->da)
+ fprintf(o, "da=%s\n", h->da);
+ if (h->udhi) {
+ unsigned int p;
+ fprintf(o, "udh#");
+ for (p = 0; p < h->udhl; p++)
+ fprintf(o, "%02X", h->udh[p]);
+ fprintf(o, "\n");
+ }
+ if (h->udl) {
+ unsigned int p;
+ for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
+ if (p < h->udl)
+ fputc(';', o); /* cannot use ud=, but include as a comment for human readable */
+ fprintf(o, "ud=");
+ for (p = 0; p < h->udl; p++) {
+ unsigned short v = h->ud[p];
+ if (v < 32)
+ fputc(191, o);
+ else if (v < 0x80)
+ fputc(v, o);
+ else if (v < 0x800)
+ {
+ fputc(0xC0 + (v >> 6), o);
+ fputc(0x80 + (v & 0x3F), o);
+ } else
+ {
+ fputc(0xE0 + (v >> 12), o);
+ fputc(0x80 + ((v >> 6) & 0x3F), o);
+ fputc(0x80 + (v & 0x3F), o);
+ }
+ }
+ fprintf(o, "\n");
+ for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
+ if (p < h->udl) {
+ for (p = 0; p < h->udl && h->ud[p] < 0x100; p++);
+ if (p == h->udl) { /* can write in ucs-1 hex */
+ fprintf(o, "ud#");
+ for (p = 0; p < h->udl; p++)
+ fprintf(o, "%02X", h->ud[p]);
+ fprintf(o, "\n");
+ } else { /* write in UCS-2 */
+ fprintf(o, "ud##");
+ for (p = 0; p < h->udl; p++)
+ fprintf(o, "%04X", h->ud[p]);
+ fprintf(o, "\n");
+ }
+ }
+ }
+ if (h->scts.tv_sec) {
+ char buf[30];
+ fprintf(o, "scts=%s\n", isodate(h->scts.tv_sec, buf, sizeof(buf)));
+ }
+ if (h->pid)
+ fprintf(o, "pid=%d\n", h->pid);
+ if (h->dcs != 0xF1)
+ fprintf(o, "dcs=%d\n", h->dcs);
+ if (h->vp)
+ fprintf(o, "vp=%d\n", h->vp);
+ if (h->srr)
+ fprintf(o, "srr=1\n");
+ if (h->mr >= 0)
+ fprintf(o, "mr=%d\n", h->mr);
+ if (h->rp)
+ fprintf(o, "rp=1\n");
+ fclose(o);
+ if (rename(fn, fn2))
+ unlink(fn);
+ else
+ ast_log(LOG_EVENT, "Received to %s\n", fn2);
+}
+
+/*! \brief read dir skipping dot files... */
+static struct dirent *readdirqueue(DIR *d, char *queue)
+{
+ struct dirent *f;
+ do {
+ f = readdir(d);
+ } while (f && (*f->d_name == '.' || strncmp(f->d_name, queue, strlen(queue)) || f->d_name[strlen(queue)] != '.'));
+ return f;
+}
+
+/*! \brief handle the incoming message */
+static unsigned char sms_handleincoming (sms_t * h)
+{
+ unsigned char p = 3;
+ if (h->smsc) { /* SMSC */
+ if ((h->imsg[2] & 3) == 1) { /* SMS-SUBMIT */
+ h->udhl = h->udl = 0;
+ h->vp = 0;
+ h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
+ h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
+ h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
+ ast_copy_string(h->oa, h->cli, sizeof(h->oa));
+ h->scts = ast_tvnow();
+ h->mr = h->imsg[p++];
+ p += unpackaddress(h->da, h->imsg + p);
+ h->pid = h->imsg[p++];
+ h->dcs = h->imsg[p++];
+ if ((h->imsg[2] & 0x18) == 0x10) { /* relative VP */
+ if (h->imsg[p] < 144)
+ h->vp = (h->imsg[p] + 1) * 5;
+ else if (h->imsg[p] < 168)
+ h->vp = 720 + (h->imsg[p] - 143) * 30;
+ else if (h->imsg[p] < 197)
+ h->vp = (h->imsg[p] - 166) * 1440;
+ else
+ h->vp = (h->imsg[p] - 192) * 10080;
+ p++;
+ } else if (h->imsg[2] & 0x18)
+ p += 7; /* ignore enhanced / absolute VP */
+ p += unpacksms(h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
+ h->rx = 1; /* received message */
+ sms_writefile(h); /* write the file */
+ if (p != h->imsg[1] + 2) {
+ ast_log(LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
+ return 0xFF; /* duh! */
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unknown message type %02X\n", h->imsg[2]);
+ return 0xFF;
+ }
+ } else { /* client */
+ if (!(h->imsg[2] & 3)) { /* SMS-DELIVER */
+ *h->da = h->srr = h->rp = h->vp = h->udhi = h->udhl = h->udl = 0;
+ h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
+ h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
+ h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
+ h->mr = -1;
+ p += unpackaddress(h->oa, h->imsg + p);
+ h->pid = h->imsg[p++];
+ h->dcs = h->imsg[p++];
+ h->scts.tv_sec = unpackdate(h->imsg + p);
+ p += 7;
+ p += unpacksms(h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
+ h->rx = 1; /* received message */
+ sms_writefile(h); /* write the file */
+ if (p != h->imsg[1] + 2) {
+ ast_log(LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
+ return 0xFF; /* duh! */
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unknown message type %02X\n", h->imsg[2]);
+ return 0xFF;
+ }
+ }
+ return 0; /* no error */
+}
+
+#ifdef SOLARIS
+#define NAME_MAX 1024
+#endif
+
+/*!
+ * Add data to a protocol 2 message.
+ * Use the length field (h->omsg[1]) as a pointer to the next free position.
+ */
+static void adddata_proto2(sms_t *h, unsigned char msg, char *data, int size)
+{
+ int x = h->omsg[1]+2; /* Get current position */
+ if (x == 2)
+ x += 2; /* First: skip Payload length (set later) */
+ h->omsg[x++] = msg; /* Message code */
+ h->omsg[x++] = (unsigned char)size; /* Data size Low */
+ h->omsg[x++] = 0; /* Data size Hi */
+ for (; size > 0 ; size--)
+ h->omsg[x++] = *data++;
+ h->omsg[1] = x - 2; /* Frame size */
+ h->omsg[2] = x - 4; /* Payload length (Lo) */
+ h->omsg[3] = 0; /* Payload length (Hi) */
+}
+
+static void putdummydata_proto2(sms_t *h)
+{
+ adddata_proto2(h, 0x10, "\0", 1); /* Media Identifier > SMS */
+ adddata_proto2(h, 0x11, "\0\0\0\0\0\0", 6); /* Firmware version */
+ adddata_proto2(h, 0x12, "\2\0\4", 3); /* SMS provider ID */
+ adddata_proto2(h, 0x13, h->udtxt, h->udl); /* Body */
+}
+
+static void sms_compose2(sms_t *h, int more)
+{
+ struct ast_tm tm;
+ struct timeval tv = h->scts;
+ char stm[9];
+
+ h->omsg[0] = 0x00; /* set later... */
+ h->omsg[1] = 0;
+ putdummydata_proto2(h);
+ if (h->smsc) { /* deliver */
+ h->omsg[0] = 0x11; /* SMS_DELIVERY */
+ /* Required: 10 11 12 13 14 15 17 (seems they must be ordered!) */
+ ast_localtime(&tv, &tm, NULL);
+ sprintf(stm, "%02d%02d%02d%02d", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min); /* Date mmddHHMM */
+ adddata_proto2(h, 0x14, stm, 8); /* Date */
+ if (*h->oa == 0)
+ strcpy(h->oa, "00000000");
+ adddata_proto2(h, 0x15, h->oa, strlen(h->oa)); /* Originator */
+ adddata_proto2(h, 0x17, "\1", 1); /* Calling Terminal ID */
+ } else { /* submit */
+ h->omsg[0] = 0x10; /* SMS_SUBMIT */
+ /* Required: 10 11 12 13 17 18 1B 1C (seems they must be ordered!) */
+ adddata_proto2(h, 0x17, "\1", 1); /* Calling Terminal ID */
+ if (*h->da == 0)
+ strcpy(h->da, "00000000");
+ adddata_proto2(h, 0x18, h->da, strlen(h->da)); /* Originator */
+ adddata_proto2(h, 0x1B, "\1", 1); /* Called Terminal ID */
+ adddata_proto2(h, 0x1C, "\0\0\0", 3); /* Notification */
+ }
+}
+
+static void putdummydata_proto2(sms_t *h);
+
+#define MAX_DEBUG_LEN 300
+static char *sms_hexdump(unsigned char buf[], int size, char *s /* destination */)
+{
+ char *p;
+ int f;
+
+ for (p = s, f = 0; f < size && f < MAX_DEBUG_LEN; f++, p += 3)
+ sprintf(p, "%02X ", (unsigned char)buf[f]);
+ return(s);
+}
+
+
+/*! \brief sms_handleincoming_proto2: handle the incoming message */
+static int sms_handleincoming_proto2(sms_t *h)
+{
+ int f, i, sz = 0;
+ int msg, msgsz;
+ struct ast_tm tm;
+ struct timeval tv = { 0, 0 };
+ char debug_buf[MAX_DEBUG_LEN * 3 + 1];
+
+ sz = h->imsg[1] + 2;
+ /* ast_verb(3, "SMS-P2 Frame: %s\n", sms_hexdump(h->imsg, sz, debug_buf)); */
+
+ /* Parse message body (called payload) */
+ tv = h->scts = ast_tvnow();
+ for (f = 4; f < sz; ) {
+ msg = h->imsg[f++];
+ msgsz = h->imsg[f++];
+ msgsz += (h->imsg[f++] * 256);
+ switch (msg) {
+ case 0x13: /* Body */
+ ast_verb(3, "SMS-P2 Body#%02X=[%.*s]\n", msg, msgsz, &h->imsg[f]);
+ if (msgsz >= sizeof(h->imsg))
+ msgsz = sizeof(h->imsg) - 1;
+ for (i = 0; i < msgsz; i++)
+ h->ud[i] = h->imsg[f + i];
+ h->udl = msgsz;
+ break;
+ case 0x14: /* Date SCTS */
+ tv = h->scts = ast_tvnow();
+ ast_localtime(&tv, &tm, NULL);
+ tm.tm_mon = ( (h->imsg[f] * 10) + h->imsg[f + 1] ) - 1;
+ tm.tm_mday = ( (h->imsg[f + 2] * 10) + h->imsg[f + 3] );
+ tm.tm_hour = ( (h->imsg[f + 4] * 10) + h->imsg[f + 5] );
+ tm.tm_min = ( (h->imsg[f + 6] * 10) + h->imsg[f + 7] );
+ tm.tm_sec = 0;
+ h->scts = ast_mktime(&tm, NULL);
+ ast_verb(3, "SMS-P2 Date#%02X=%02d/%02d %02d:%02d\n", msg, tm.tm_mday, tm.tm_mon + 1, tm.tm_hour, tm.tm_min);
+ break;
+ case 0x15: /* Calling line (from SMSC) */
+ if (msgsz >= 20)
+ msgsz = 20 - 1;
+ ast_verb(3, "SMS-P2 Origin#%02X=[%.*s]\n", msg, msgsz, &h->imsg[f]);
+ ast_copy_string(h->oa, (char *)(&h->imsg[f]), msgsz + 1);
+ break;
+ case 0x18: /* Destination(from TE/phone) */
+ if (msgsz >= 20)
+ msgsz = 20 - 1;
+ ast_verb(3, "SMS-P2 Destination#%02X=[%.*s]\n", msg, msgsz, &h->imsg[f]);
+ ast_copy_string(h->da, (char *)(&h->imsg[f]), msgsz + 1);
+ break;
+ case 0x1C: /* Notify */
+ ast_verb(3, "SMS-P2 Notify#%02X=%s\n", msg, sms_hexdump(&h->imsg[f], 3, debug_buf));
+ break;
+ default:
+ ast_verb(3, "SMS-P2 Par#%02X [%d]: %s\n", msg, msgsz, sms_hexdump(&h->imsg[f], msgsz, debug_buf));
+ break;
+ }
+ f+=msgsz; /* Skip to next */
+ }
+ h->rx = 1; /* received message */
+ sms_writefile(h); /* write the file */
+ return 0; /* no error */
+}
+
+#if 0
+static void smssend(sms_t *h, char *c)
+{
+ int f, x;
+ for (f = 0; f < strlen(c); f++) {
+ sscanf(&c[f*3], "%x", &x);
+ h->omsg[f] = x;
+ }
+ sms_messagetx(h);
+}
+#endif
+
+static void sms_nextoutgoing (sms_t *h);
+
+static void sms_messagerx2(sms_t * h)
+{
+ int p = h->imsg[0] & DLL_SMS_MASK ; /* mask the high bit */
+ int cause;
+
+#define DLL2_ACK(h) ((h->framenumber & 1) ? DLL2_SMS_ACK1: DLL2_SMS_ACK1)
+ switch (p) {
+ case DLL2_SMS_EST: /* Protocol 2: Connection ready (fake): send message */
+ sms_nextoutgoing (h);
+ /* smssend(h,"11 29 27 00 10 01 00 00 11 06 00 00 00 00 00 00 00 12 03 00 02 00 04 13 01 00 41 14 08 00 30 39 31 35 30 02 30 02 15 02 00 39 30 "); */
+ break;
+
+ case DLL2_SMS_INFO_MO: /* transport SMS_SUBMIT */
+ case DLL2_SMS_INFO_MT: /* transport SMS_DELIVERY */
+ cause = sms_handleincoming_proto2(h);
+ if (!cause) /* ACK */
+ sms_log(h, 'Y');
+ h->omsg[0] = DLL2_ACK(h);
+ h->omsg[1] = 0x06; /* msg len */
+ h->omsg[2] = 0x04; /* payload len */
+ h->omsg[3] = 0x00; /* payload len */
+ h->omsg[4] = 0x1f; /* Response type */
+ h->omsg[5] = 0x01; /* parameter len */
+ h->omsg[6] = 0x00; /* parameter len */
+ h->omsg[7] = cause; /* CONFIRM or error */
+ sms_messagetx(h);
+ break;
+
+ case DLL2_SMS_NACK: /* Protocol 2: SMS_NAK */
+ h->omsg[0] = DLL2_SMS_REL; /* SMS_REL */
+ h->omsg[1] = 0x00; /* msg len */
+ sms_messagetx(h);
+ break;
+
+ case DLL2_SMS_ACK0:
+ case DLL2_SMS_ACK1:
+ /* SMS_ACK also transport SMS_SUBMIT or SMS_DELIVERY */
+ if ( (h->omsg[0] & DLL_SMS_MASK) == DLL2_SMS_REL) {
+ /* a response to our Release, just hangup */
+ h->hangup = 1; /* hangup */
+ } else {
+ /* XXX depending on what we are.. */
+ ast_log(LOG_NOTICE, "SMS_SUBMIT or SMS_DELIVERY");
+ sms_nextoutgoing (h);
+ }
+ break;
+
+ case DLL2_SMS_REL: /* Protocol 2: SMS_REL (hangup req) */
+ h->omsg[0] = DLL2_ACK(h);
+ h->omsg[1] = 0;
+ sms_messagetx(h);
+ break;
+ }
+}
+
+/*! \brief compose a message for protocol 1 */
+static void sms_compose1(sms_t *h, int more)
+{
+ unsigned int p = 2; /* next byte to write. Skip type and len */
+
+ h->omsg[0] = 0x91; /* SMS_DATA */
+ if (h->smsc) { /* deliver */
+ h->omsg[p++] = (more ? 4 : 0) + ((h->udhl > 0) ? 0x40 : 0);
+ p += packaddress(h->omsg + p, h->oa);
+ h->omsg[p++] = h->pid;
+ h->omsg[p++] = h->dcs;
+ packdate(h->omsg + p, h->scts.tv_sec);
+ p += 7;
+ p += packsms(h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
+ } else { /* submit */
+ h->omsg[p++] =
+ 0x01 + (more ? 4 : 0) + (h->srr ? 0x20 : 0) + (h->rp ? 0x80 : 0) + (h->vp ? 0x10 : 0) + (h->udhi ? 0x40 : 0);
+ if (h->mr < 0)
+ h->mr = message_ref++;
+ h->omsg[p++] = h->mr;
+ p += packaddress(h->omsg + p, h->da);
+ h->omsg[p++] = h->pid;
+ h->omsg[p++] = h->dcs;
+ if (h->vp) { /* relative VP */
+ if (h->vp < 720)
+ h->omsg[p++] = (h->vp + 4) / 5 - 1;
+ else if (h->vp < 1440)
+ h->omsg[p++] = (h->vp - 720 + 29) / 30 + 143;
+ else if (h->vp < 43200)
+ h->omsg[p++] = (h->vp + 1439) / 1440 + 166;
+ else if (h->vp < 635040)
+ h->omsg[p++] = (h->vp + 10079) / 10080 + 192;
+ else
+ h->omsg[p++] = 255; /* max */
+ }
+ p += packsms(h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
+ }
+ h->omsg[1] = p - 2;
+}
+
+/*! \brief find and fill in next message, or send a REL if none waiting */
+static void sms_nextoutgoing (sms_t * h)
+{
+ char fn[100 + NAME_MAX] = "";
+ DIR *d;
+ char more = 0;
+
+ *h->da = *h->oa = '\0'; /* clear destinations */
+ h->rx = 0; /* outgoing message */
+ snprintf(fn, sizeof(fn), "%s/sms/%s", ast_config_AST_SPOOL_DIR, h->smsc ? "mttx" : "motx");
+ ast_mkdir(fn, 0777); /* ensure it exists */
+ d = opendir(fn);
+ if (d) {
+ struct dirent *f = readdirqueue(d, h->queue);
+ if (f) {
+ snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/%s", f->d_name);
+ sms_readfile(h, fn);
+ if (readdirqueue(d, h->queue))
+ more = 1; /* more to send */
+ }
+ closedir(d);
+ }
+ if (*h->da || *h->oa) { /* message to send */
+ if (h->protocol == 2)
+ sms_compose2(h, more);
+ else
+ sms_compose1(h, more);
+ } else { /* no message */
+ if (h->protocol == 2) {
+ h->omsg[0] = 0x17; /* SMS_REL */
+ h->omsg[1] = 0;
+ } else {
+ h->omsg[0] = 0x94; /* SMS_REL */
+ h->omsg[1] = 0;
+ }
+ }
+ sms_messagetx(h);
+}
+
+#define DIR_RX 1
+#define DIR_TX 2
+static void sms_debug (int dir, sms_t *h)
+{
+ char txt[259 * 3 + 1];
+ char *p = txt; /* always long enough */
+ unsigned char *msg = (dir == DIR_RX) ? h->imsg : h->omsg;
+ int n = (dir == DIR_RX) ? h->ibytep : msg[1] + 2;
+ int q = 0;
+ while (q < n && q < 30) {
+ sprintf(p, " %02X", msg[q++]);
+ p += 3;
+ }
+ if (q < n)
+ sprintf(p, "...");
+ ast_verb(3, "SMS %s%s\n", dir == DIR_RX ? "RX" : "TX", txt);
+}
+
+
+static void sms_messagerx(sms_t * h)
+{
+ int cause;
+
+ sms_debug (DIR_RX, h);
+ if (h->protocol == 2) {
+ sms_messagerx2(h);
+ return;
+ }
+ /* parse incoming message for Protocol 1 */
+ switch (h->imsg[0]) {
+ case 0x91: /* SMS_DATA */
+ cause = sms_handleincoming (h);
+ if (!cause) {
+ sms_log(h, 'Y');
+ h->omsg[0] = 0x95; /* SMS_ACK */
+ h->omsg[1] = 0x02;
+ h->omsg[2] = 0x00; /* deliver report */
+ h->omsg[3] = 0x00; /* no parameters */
+ } else { /* NACK */
+ sms_log(h, 'N');
+ h->omsg[0] = 0x96; /* SMS_NACK */
+ h->omsg[1] = 3;
+ h->omsg[2] = 0; /* delivery report */
+ h->omsg[3] = cause; /* cause */
+ h->omsg[4] = 0; /* no parameters */
+ }
+ sms_messagetx(h);
+ break;
+
+ case 0x92: /* SMS_ERROR */
+ h->err = 1;
+ sms_messagetx(h); /* send whatever we sent again */
+ break;
+ case 0x93: /* SMS_EST */
+ sms_nextoutgoing (h);
+ break;
+ case 0x94: /* SMS_REL */
+ h->hangup = 1; /* hangup */
+ break;
+ case 0x95: /* SMS_ACK */
+ sms_log(h, 'Y');
+ sms_nextoutgoing (h);
+ break;
+ case 0x96: /* SMS_NACK */
+ h->err = 1;
+ sms_log(h, 'N');
+ sms_nextoutgoing (h);
+ break;
+ default: /* Unknown */
+ h->omsg[0] = 0x92; /* SMS_ERROR */
+ h->omsg[1] = 1;
+ h->omsg[2] = 3; /* unknown message type; */
+ sms_messagetx(h);
+ break;
+ }
+}
+
+static void sms_messagetx(sms_t * h)
+{
+ unsigned char c = 0, p;
+ int len = h->omsg[1] + 2; /* total message length excluding checksum */
+
+ for (p = 0; p < len; p++) /* compute checksum */
+ c += h->omsg[p];
+ h->omsg[len] = 0 - c; /* actually, (256 - (c & 0fxx)) & 0xff) */
+ sms_debug(DIR_TX, h);
+ h->framenumber++; /* Proto 2 */
+ h->obyte = 1; /* send mark ('1') at the beginning */
+ h->opause = 200;
+ /* Change the initial message delay. BT requires 300ms,
+ * but for others this might be way too much and the phone
+ * could time out. XXX make it configurable.
+ */
+ if (h->omsg[0] == 0x93)
+ h->opause = 8 * h->opause_0; /* initial message delay */
+ h->obytep = 0;
+ h->obitp = 0;
+ if (h->protocol == 2) {
+ h->oseizure = 300; /* Proto 2: 300bits (or more ?) */
+ h->obyte = 0; /* Seizure starts with space (0) */
+ h->opause = 400;
+ } else {
+ h->oseizure = 0; /* Proto 1: No seizure */
+ }
+ /* Note - setting osync triggers the generator */
+ h->osync = OSYNC_BITS; /* 80 sync bits */
+ h->obyten = len + 1; /* bytes to send (including checksum) */
+}
+
+/*!
+ * outgoing data are produced by this generator function, that reads from
+ * the descriptor whether it has data to send and which ones.
+ */
+static int sms_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct ast_frame f = { 0 };
+#define MAXSAMPLES (800)
+ output_t *buf;
+ sms_t *h = data;
+ int i;
+
+ if (samples > MAXSAMPLES) {
+ ast_log(LOG_WARNING, "Only doing %d samples (%d requested)\n",
+ MAXSAMPLES, samples);
+ samples = MAXSAMPLES;
+ }
+ len = samples * sizeof(*buf) + AST_FRIENDLY_OFFSET;
+ buf = alloca(len);
+
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = __OUT_FMT;
+ f.datalen = samples * sizeof(*buf);
+ f.offset = AST_FRIENDLY_OFFSET;
+ f.mallocd = 0;
+ f.data = buf;
+ f.samples = samples;
+ f.src = "app_sms";
+ /* create a buffer containing the digital sms pattern */
+ for (i = 0; i < samples; i++) {
+ buf[i] = wave_out[0]; /* default is silence */
+
+ if (h->opause)
+ h->opause--;
+ else if (h->obyten || h->osync) { /* sending data */
+ buf[i] = wave_out[h->ophase];
+ h->ophase += (h->obyte & 1) ? 13 : 21; /* compute next phase */
+ if (h->ophase >= 80)
+ h->ophase -= 80;
+ if ((h->ophasep += 12) >= 80) { /* time to send the next bit */
+ h->ophasep -= 80;
+ if (h->oseizure > 0) { /* sending channel seizure (proto 2) */
+ h->oseizure--;
+ h->obyte ^= 1; /* toggle low bit */
+ } else if (h->osync) {
+ h->obyte = 1; /* send mark as sync bit */
+ h->osync--; /* sending sync bits */
+ if (h->osync == 0 && h->protocol == 2 && h->omsg[0] == DLL2_SMS_EST) {
+ h->obytep = h->obyten = 0; /* we are done */
+ }
+ } else {
+ h->obitp++;
+ if (h->obitp == 1)
+ h->obyte = 0; /* start bit; */
+ else if (h->obitp == 2)
+ h->obyte = h->omsg[h->obytep];
+ else if (h->obitp == 10) {
+ h->obyte = 1; /* stop bit */
+ h->obitp = 0;
+ h->obytep++;
+ if (h->obytep == h->obyten) {
+ h->obytep = h->obyten = 0; /* sent */
+ h->osync = 10; /* trailing marks */
+ }
+ } else
+ h->obyte >>= 1;
+ }
+ }
+ }
+ }
+ if (ast_write(chan, &f) < 0) {
+ ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
+ return -1;
+ }
+ return 0;
+#undef MAXSAMPLES
+}
+
+/*!
+ * Just return the pointer to the descriptor that we received.
+ */
+static void *sms_alloc(struct ast_channel *chan, void *sms_t_ptr)
+{
+ return sms_t_ptr;
+}
+
+static void sms_release(struct ast_channel *chan, void *data)
+{
+ return; /* nothing to do here. */
+}
+
+static struct ast_generator smsgen = {
+ .alloc = sms_alloc,
+ .release = sms_release,
+ .generate = sms_generate,
+};
+
+/*!
+ * Process an incoming frame, trying to detect the carrier and
+ * decode the message. The two frequencies are 1300 and 2100 Hz.
+ * The decoder detects the amplitude of the signal over the last
+ * few samples, filtering the absolute values with a lowpass filter.
+ * If the magnitude (h->imag) is large enough, multiply the signal
+ * by the two carriers, and compute the amplitudes m0 and m1.
+ * Record the current sample as '0' or '1' depending on which one is greater.
+ * The last 3 bits are stored in h->ibith, with the count of '1'
+ * bits in h->ibitt.
+ * XXX the rest is to be determined.
+ */
+static void sms_process(sms_t * h, int samples, signed short *data)
+{
+ int bit;
+
+ /*
+ * Ignore incoming audio while a packet is being transmitted,
+ * the protocol is half-duplex.
+ * Unfortunately this means that if the outbound and incoming
+ * transmission overlap (which is an error condition anyways),
+ * we may miss some data and this makes debugging harder.
+ */
+ if (h->obyten || h->osync)
+ return;
+ for ( ; samples-- ; data++) {
+ unsigned long long m0, m1;
+ if (abs(*data) > h->imag)
+ h->imag = abs(*data);
+ else
+ h->imag = h->imag * 7 / 8;
+ if (h->imag <= 500) { /* below [arbitrary] threahold: lost carrier */
+ if (h->idle++ == 80000) { /* nothing happening */
+ ast_log(LOG_NOTICE, "No data, hanging up\n");
+ h->hangup = 1;
+ h->err = 1;
+ }
+ if (h->ierr) { /* error */
+ ast_log(LOG_NOTICE, "Error %d, hanging up\n", h->ierr);
+ /* Protocol 1 */
+ h->err = 1;
+ h->omsg[0] = 0x92; /* error */
+ h->omsg[1] = 1;
+ h->omsg[2] = h->ierr;
+ sms_messagetx(h); /* send error */
+ }
+ h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
+ continue;
+ }
+ h->idle = 0;
+
+ /* multiply signal by the two carriers. */
+ h->ims0 = (h->ims0 * 6 + *data * wave[h->ips0]) / 7;
+ h->imc0 = (h->imc0 * 6 + *data * wave[h->ipc0]) / 7;
+ h->ims1 = (h->ims1 * 6 + *data * wave[h->ips1]) / 7;
+ h->imc1 = (h->imc1 * 6 + *data * wave[h->ipc1]) / 7;
+ /* compute the amplitudes */
+ m0 = h->ims0 * h->ims0 + h->imc0 * h->imc0;
+ m1 = h->ims1 * h->ims1 + h->imc1 * h->imc1;
+
+ /* advance the sin/cos pointers */
+ if ((h->ips0 += 21) >= 80)
+ h->ips0 -= 80;
+ if ((h->ipc0 += 21) >= 80)
+ h->ipc0 -= 80;
+ if ((h->ips1 += 13) >= 80)
+ h->ips1 -= 80;
+ if ((h->ipc1 += 13) >= 80)
+ h->ipc1 -= 80;
+
+ /* set new bit to 1 or 0 depending on which value is stronger */
+ h->ibith <<= 1;
+ if (m1 > m0)
+ h->ibith |= 1;
+ if (h->ibith & 8)
+ h->ibitt--;
+ if (h->ibith & 1)
+ h->ibitt++;
+ bit = ((h->ibitt > 1) ? 1 : 0);
+ if (bit != h->ibitl)
+ h->ibitc = 1;
+ else
+ h->ibitc++;
+ h->ibitl = bit;
+ if (!h->ibitn && h->ibitc == 4 && !bit) {
+ h->ibitn = 1;
+ h->iphasep = 0;
+ }
+ if (bit && h->ibitc == 200) { /* sync, restart message */
+ /* Protocol 2: empty connnection ready (I am master) */
+ if (h->framenumber < 0 && h->ibytec >= 160 && !memcmp(h->imsg, "UUUUUUUUUUUUUUUUUUUU", 20)) {
+ h->framenumber = 1;
+ ast_verb(3, "SMS protocol 2 detected\n");
+ h->protocol = 2;
+ h->imsg[0] = 0xff; /* special message (fake) */
+ h->imsg[1] = h->imsg[2] = 0x00;
+ h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
+ sms_messagerx(h);
+ }
+ h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
+ }
+ if (h->ibitn) {
+ h->iphasep += 12;
+ if (h->iphasep >= 80) { /* next bit */
+ h->iphasep -= 80;
+ if (h->ibitn++ == 9) { /* end of byte */
+ if (!bit) { /* bad stop bit */
+ ast_log(LOG_NOTICE, "bad stop bit");
+ h->ierr = 0xFF; /* unknown error */
+ } else {
+ if (h->ibytep < sizeof(h->imsg)) {
+ h->imsg[h->ibytep] = h->ibytev;
+ h->ibytec += h->ibytev;
+ h->ibytep++;
+ } else if (h->ibytep == sizeof(h->imsg)) {
+ ast_log(LOG_NOTICE, "msg too large");
+ h->ierr = 2; /* bad message length */
+ }
+ if (h->ibytep > 1 && h->ibytep == 3 + h->imsg[1] && !h->ierr) {
+ if (!h->ibytec)
+ sms_messagerx(h);
+ else {
+ ast_log(LOG_NOTICE, "bad checksum");
+ h->ierr = 1; /* bad checksum */
+ }
+ }
+ }
+ h->ibitn = 0;
+ }
+ h->ibytev = (h->ibytev >> 1) + (bit ? 0x80 : 0);
+ }
+ }
+ }
+}
+
+/*
+ * Standard argument parsing:
+ * - one enum for the flags we recognise,
+ * - one enum for argument indexes
+ * - AST_APP_OPTIONS() to drive the parsing routine
+ * - in the function, AST_DECLARE_APP_ARGS(...) for the arguments.
+ */
+enum {
+ OPTION_BE_SMSC = (1 << 0), /* act as sms center */
+ OPTION_ANSWER = (1 << 1), /* answer on incoming calls */
+ OPTION_TWO = (1 << 2), /* Use Protocol Two */
+ OPTION_PAUSE = (1 << 3), /* pause before sending data, in ms */
+ OPTION_SRR = (1 << 4), /* set srr */
+ OPTION_DCS = (1 << 5), /* set dcs */
+} sms_flags;
+
+enum {
+ OPTION_ARG_PAUSE = 0,
+ OPTION_ARG_ARRAY_SIZE
+} sms_opt_args;
+
+AST_APP_OPTIONS(sms_options, {
+ AST_APP_OPTION('s', OPTION_BE_SMSC),
+ AST_APP_OPTION('a', OPTION_ANSWER),
+ AST_APP_OPTION('t', OPTION_TWO),
+ AST_APP_OPTION('r', OPTION_SRR),
+ AST_APP_OPTION('o', OPTION_DCS),
+ AST_APP_OPTION_ARG('p', OPTION_PAUSE, OPTION_ARG_PAUSE),
+ } );
+
+static int sms_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ sms_t h = { 0 };
+ /* argument parsing support */
+ struct ast_flags sms_flags;
+ char *parse, *sms_opts[OPTION_ARG_ARRAY_SIZE];
+ char *p;
+ AST_DECLARE_APP_ARGS(sms_args,
+ AST_APP_ARG(queue);
+ AST_APP_ARG(options);
+ AST_APP_ARG(addr);
+ AST_APP_ARG(body);
+ );
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Requires queue name at least\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data); /* create a local copy */
+ AST_STANDARD_APP_ARGS(sms_args, parse);
+ if (sms_args.argc > 1)
+ ast_app_parse_options(sms_options, &sms_flags, sms_opts, sms_args.options);
+
+ ast_verb(1, "sms argc %d queue <%s> opts <%s> addr <%s> body <%s>\n",
+ sms_args.argc, S_OR(sms_args.queue, ""),
+ S_OR(sms_args.options, ""),
+ S_OR(sms_args.addr, ""),
+ S_OR(sms_args.body, "") );
+
+ h.ipc0 = h.ipc1 = 20; /* phase for cosine */
+ h.dcs = 0xF1; /* default */
+
+ if (chan->cid.cid_num)
+ ast_copy_string(h.cli, chan->cid.cid_num, sizeof(h.cli));
+
+ if (ast_strlen_zero(sms_args.queue)) {
+ ast_log(LOG_ERROR, "Requires queue name\n");
+ goto done;
+ }
+ if (strlen(sms_args.queue) >= sizeof(h.queue)) {
+ ast_log(LOG_ERROR, "Queue name too long\n");
+ goto done;
+ }
+ ast_copy_string(h.queue, sms_args.queue, sizeof(h.queue));
+
+ for (p = h.queue; *p; p++)
+ if (!isalnum(*p))
+ *p = '-'; /* make very safe for filenames */
+
+ h.smsc = ast_test_flag(&sms_flags, OPTION_BE_SMSC);
+ h.protocol = ast_test_flag(&sms_flags, OPTION_TWO) ? 2 : 1;
+ if (!ast_strlen_zero(sms_opts[OPTION_ARG_PAUSE]))
+ h.opause_0 = atoi(sms_opts[OPTION_ARG_PAUSE]);
+ if (h.opause_0 < 25 || h.opause_0 > 2000)
+ h.opause_0 = 300; /* default 300ms */
+ ast_verb(1, "initial delay %dms\n", h.opause_0);
+
+
+ /* the following apply if there is an arg3/4 and apply to the created message file */
+ if (ast_test_flag(&sms_flags, OPTION_SRR))
+ h.srr = 1;
+ if (ast_test_flag(&sms_flags, OPTION_DCS))
+ h.dcs = 1;
+#if 0
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': /* set the pid for saved local message */
+ h.pid = 0x40 + (*d & 0xF);
+ break;
+ }
+#endif
+ if (sms_args.argc > 2) {
+ unsigned char *up;
+
+ /* submitting a message, not taking call. */
+ /* deprecated, use smsq instead */
+ h.scts = ast_tvnow();
+ if (ast_strlen_zero(sms_args.addr) || strlen(sms_args.addr) >= sizeof(h.oa)) {
+ ast_log(LOG_ERROR, "Address too long %s\n", sms_args.addr);
+ goto done;
+ }
+ if (h.smsc)
+ ast_copy_string(h.oa, sms_args.addr, sizeof(h.oa));
+ else {
+ ast_copy_string(h.da, sms_args.addr, sizeof(h.da));
+ ast_copy_string(h.oa, h.cli, sizeof(h.oa));
+ }
+ h.udl = 0;
+ if (ast_strlen_zero(sms_args.body)) {
+ ast_log(LOG_ERROR, "Missing body for %s\n", sms_args.addr);
+ goto done;
+ }
+ up = (unsigned char *)sms_args.body;
+ while (*up && h.udl < SMSLEN)
+ h.ud[h.udl++] = utf8decode(&up);
+ if (is7bit(h.dcs) && packsms7(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
+ ast_log(LOG_WARNING, "Invalid 7 bit GSM data\n");
+ goto done;
+ }
+ if (is8bit(h.dcs) && packsms8(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
+ ast_log(LOG_WARNING, "Invalid 8 bit data\n");
+ goto done;
+ }
+ if (is16bit(h.dcs) && packsms16(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
+ ast_log(LOG_WARNING, "Invalid 16 bit data\n");
+ goto done;
+ }
+ h.rx = 0; /* sent message */
+ h.mr = -1;
+ sms_writefile(&h);
+ res = h.err;
+ goto done;
+ }
+
+ if (ast_test_flag(&sms_flags, OPTION_ANSWER)) {
+ h.framenumber = 1; /* Proto 2 */
+ /* set up SMS_EST initial message */
+ if (h.protocol == 2) {
+ h.omsg[0] = DLL2_SMS_EST;
+ h.omsg[1] = 0;
+ } else {
+ h.omsg[0] = DLL1_SMS_EST | DLL1_SMS_COMPLETE;
+ h.omsg[1] = 0;
+ }
+ sms_messagetx(&h);
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ res = ast_set_write_format(chan, __OUT_FMT);
+ if (res >= 0)
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "Unable to set to linear mode, giving up\n");
+ goto done;
+ }
+
+ if ( (res = ast_activate_generator(chan, &smsgen, &h)) < 0) {
+ ast_log(LOG_ERROR, "Failed to activate generator on '%s'\n", chan->name);
+ goto done;
+ }
+
+ /* Do our thing here */
+ for (;;) {
+ struct ast_frame *f;
+ int i = ast_waitfor(chan, -1);
+ if (i < 0) {
+ ast_log(LOG_NOTICE, "waitfor failed\n");
+ break;
+ }
+ if (h.hangup) {
+ ast_log(LOG_NOTICE, "channel hangup\n");
+ break;
+ }
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_NOTICE, "ast_read failed\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE) {
+ sms_process(&h, f->samples, f->data);
+ }
+
+ ast_frfree(f);
+ }
+ res = h.err; /* XXX */
+
+ sms_log(&h, '?'); /* log incomplete message */
+done:
+ return (res);
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+#ifdef OUTALAW
+ int p;
+ for (p = 0; p < 80; p++)
+ wavea[p] = AST_LIN2A (wave[p]);
+#endif
+ snprintf(log_file, sizeof(log_file), "%s/sms", ast_config_AST_LOG_DIR);
+ return ast_register_application(app, sms_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SMS/PSTN handler");
diff --git a/trunk/apps/app_softhangup.c b/trunk/apps/app_softhangup.c
new file mode 100644
index 000000000..7af852560
--- /dev/null
+++ b/trunk/apps/app_softhangup.c
@@ -0,0 +1,120 @@
+/*
+ * 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 SoftHangup application
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *synopsis = "Soft Hangup Application";
+
+static char *desc = " SoftHangup(Technology/resource[,options]):\n"
+"Hangs up the requested channel. If there are no channels to hangup,\n"
+"the application will report it.\n"
+" Options:\n"
+" 'a' - hang up all channels on a specified device instead of a single resource\n";
+
+static char *app = "SoftHangup";
+
+enum {
+ OPTION_ALL = (1 << 0),
+};
+
+AST_APP_OPTIONS(app_opts,{
+ AST_APP_OPTION('a', OPTION_ALL),
+});
+
+static int softhangup_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_channel *c = NULL;
+ char *cut, *opts[0];
+ char name[AST_CHANNEL_NAME] = "", *parse;
+ struct ast_flags flags;
+ int lenmatch;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(channel);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SoftHangup requires an argument (Technology/resource)\n");
+ return 0;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc == 2)
+ ast_app_parse_options(app_opts, &flags, opts, args.options);
+ lenmatch = strlen(args.channel);
+
+ for (c = ast_walk_channel_by_name_prefix_locked(NULL, args.channel, lenmatch);
+ c;
+ c = ast_walk_channel_by_name_prefix_locked(c, args.channel, lenmatch)) {
+ ast_copy_string(name, c->name, sizeof(name));
+ if (ast_test_flag(&flags, OPTION_ALL)) {
+ /* CAPI is set up like CAPI[foo/bar]/clcnt */
+ if (!strcmp(c->tech->type, "CAPI"))
+ cut = strrchr(name, '/');
+ /* Basically everything else is Foo/Bar-Z */
+ else
+ cut = strchr(name, '-');
+ /* Get rid of what we've cut */
+ if (cut)
+ *cut = 0;
+ }
+ if (!strcasecmp(name, args.channel)) {
+ ast_log(LOG_WARNING, "Soft hanging %s up.\n", c->name);
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ if (!ast_test_flag(&flags, OPTION_ALL)) {
+ ast_channel_unlock(c);
+ break;
+ }
+ }
+ ast_channel_unlock(c);
+ }
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, softhangup_exec, synopsis, desc);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hangs up the requested channel");
diff --git a/trunk/apps/app_speech_utils.c b/trunk/apps/app_speech_utils.c
new file mode 100644
index 000000000..327e7922f
--- /dev/null
+++ b/trunk/apps/app_speech_utils.c
@@ -0,0 +1,796 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Speech Recognition Utility Applications
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/speech.h"
+
+/* Descriptions for each application */
+static char *speechcreate_descrip =
+" SpeechCreate(engine name):\n"
+"This application creates information to be used by all the other applications.\n"
+"It must be called before doing any speech recognition activities such as activating a grammar.\n"
+"It takes the engine name to use as the argument, if not specified the default engine will be used.\n";
+
+static char *speechactivategrammar_descrip =
+" SpeechActivateGrammar(Grammar Name):\n"
+"This activates the specified grammar to be recognized by the engine.\n"
+"A grammar tells the speech recognition engine what to recognize, and how to portray it back to you \n"
+"in the dialplan. The grammar name is the only argument to this application.\n";
+
+static char *speechstart_descrip =
+" SpeechStart():\n"
+"Tell the speech recognition engine that it should start trying to get results from audio being \n"
+"fed to it. This has no arguments.\n";
+
+static char *speechbackground_descrip =
+" SpeechBackground(Sound File,Timeout):\n"
+"This application plays a sound file and waits for the person to speak. Once they start speaking playback\n"
+"of the file stops, and silence is heard. Once they stop talking the processing sound is played to indicate\n"
+"the speech recognition engine is working. Once results are available the application returns and results \n"
+"(score and text) are available using dialplan functions.\n"
+"The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)}\n"
+"and ${SPEECH_SCORE(1)}.\n"
+"The first argument is the sound file and the second is the timeout integer in seconds. Note the timeout will\n"
+"only start once the sound file has stopped playing.\n";
+
+static char *speechdeactivategrammar_descrip =
+" SpeechDeactivateGrammar(Grammar Name):\n"
+"This deactivates the specified grammar so that it is no longer recognized.\n"
+"The only argument is the grammar name to deactivate.\n";
+
+static char *speechprocessingsound_descrip =
+" SpeechProcessingSound(Sound File):\n"
+"This changes the processing sound that SpeechBackground plays back when the speech recognition engine is\n"
+"processing and working to get results.\n"
+"It takes the sound file as the only argument.\n";
+
+static char *speechdestroy_descrip =
+" SpeechDestroy():\n"
+"This destroys the information used by all the other speech recognition applications.\n"
+"If you call this application but end up wanting to recognize more speech, you must call SpeechCreate\n"
+ "again before calling any other application. It takes no arguments.\n";
+
+static char *speechload_descrip =
+" SpeechLoadGrammar(Grammar Name,Path):\n"
+"Load a grammar only on the channel, not globally.\n"
+"It takes the grammar name as first argument and path as second.\n";
+
+static char *speechunload_descrip =
+" SpeechUnloadGrammar(Grammar Name):\n"
+"Unload a grammar. It takes the grammar name as the only argument.\n";
+
+/*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
+static void destroy_callback(void *data)
+{
+ struct ast_speech *speech = (struct ast_speech*)data;
+
+ if (speech == NULL) {
+ return;
+ }
+
+ /* Deallocate now */
+ ast_speech_destroy(speech);
+
+ return;
+}
+
+/*! \brief Static structure for datastore information */
+static const struct ast_datastore_info speech_datastore = {
+ .type = "speech",
+ .destroy = destroy_callback
+};
+
+/*! \brief Helper function used to find the speech structure attached to a channel */
+static struct ast_speech *find_speech(struct ast_channel *chan)
+{
+ struct ast_speech *speech = NULL;
+ struct ast_datastore *datastore = NULL;
+
+ datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
+ if (datastore == NULL) {
+ return NULL;
+ }
+ speech = datastore->data;
+
+ return speech;
+}
+
+/* Helper function to find a specific speech recognition result by number and nbest alternative */
+static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
+{
+ struct ast_speech_result *result = results;
+ char *tmp = NULL;
+ int nbest_num = 0, wanted_num = 0, i = 0;
+
+ if (!result)
+ return NULL;
+
+ if ((tmp = strchr(result_num, '/'))) {
+ *tmp++ = '\0';
+ nbest_num = atoi(result_num);
+ wanted_num = atoi(tmp);
+ } else {
+ wanted_num = atoi(result_num);
+ }
+
+ do {
+ if (result->nbest_num != nbest_num)
+ continue;
+ if (i == wanted_num)
+ break;
+ i++;
+ } while ((result = AST_LIST_NEXT(result, list)));
+
+ return result;
+}
+
+/*! \brief SPEECH_SCORE() Dialplan Function */
+static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ struct ast_speech_result *result = NULL;
+ struct ast_speech *speech = find_speech(chan);
+ char tmp[128] = "";
+
+ if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
+ return -1;
+
+ snprintf(tmp, sizeof(tmp), "%d", result->score);
+
+ ast_copy_string(buf, tmp, len);
+
+ return 0;
+}
+
+static struct ast_custom_function speech_score_function = {
+ .name = "SPEECH_SCORE",
+ .synopsis = "Gets the confidence score of a result.",
+ .syntax = "SPEECH_SCORE([nbest number/]result number)",
+ .desc =
+ "Gets the confidence score of a result.\n",
+ .read = speech_score,
+ .write = NULL,
+};
+
+/*! \brief SPEECH_TEXT() Dialplan Function */
+static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ struct ast_speech_result *result = NULL;
+ struct ast_speech *speech = find_speech(chan);
+
+ if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
+ return -1;
+
+ if (result->text != NULL)
+ ast_copy_string(buf, result->text, len);
+
+ return 0;
+}
+
+static struct ast_custom_function speech_text_function = {
+ .name = "SPEECH_TEXT",
+ .synopsis = "Gets the recognized text of a result.",
+ .syntax = "SPEECH_TEXT([nbest number/]result number)",
+ .desc =
+ "Gets the recognized text of a result.\n",
+ .read = speech_text,
+ .write = NULL,
+};
+
+/*! \brief SPEECH_GRAMMAR() Dialplan Function */
+static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ struct ast_speech_result *result = NULL;
+ struct ast_speech *speech = find_speech(chan);
+
+ if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
+ return -1;
+
+ if (result->grammar != NULL)
+ ast_copy_string(buf, result->grammar, len);
+
+ return 0;
+}
+
+static struct ast_custom_function speech_grammar_function = {
+ .name = "SPEECH_GRAMMAR",
+ .synopsis = "Gets the matched grammar of a result if available.",
+ .syntax = "SPEECH_GRAMMAR([nbest number/]result number)",
+ .desc =
+ "Gets the matched grammar of a result if available.\n",
+ .read = speech_grammar,
+ .write = NULL,
+};
+
+/*! \brief SPEECH_ENGINE() Dialplan Function */
+static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ struct ast_speech *speech = find_speech(chan);
+
+ if (data == NULL || speech == NULL)
+ return -1;
+
+ ast_speech_change(speech, data, value);
+
+ return 0;
+}
+
+static struct ast_custom_function speech_engine_function = {
+ .name = "SPEECH_ENGINE",
+ .synopsis = "Change a speech engine specific attribute.",
+ .syntax = "SPEECH_ENGINE(name)=value",
+ .desc =
+ "Changes a speech engine specific attribute.\n",
+ .read = NULL,
+ .write = speech_engine_write,
+};
+
+/*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
+static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ struct ast_speech *speech = find_speech(chan);
+
+ if (data == NULL || speech == NULL)
+ return -1;
+
+ if (!strcasecmp(value, "normal"))
+ ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
+ else if (!strcasecmp(value, "nbest"))
+ ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
+
+ return 0;
+}
+
+static struct ast_custom_function speech_results_type_function = {
+ .name = "SPEECH_RESULTS_TYPE",
+ .synopsis = "Sets the type of results that will be returned.",
+ .syntax = "SPEECH_RESULTS_TYPE()=results type",
+ .desc =
+ "Sets the type of results that will be returned. Valid options are normal or nbest.",
+ .read = NULL,
+ .write = speech_results_type_write,
+};
+
+/*! \brief SPEECH() Dialplan Function */
+static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ int results = 0;
+ struct ast_speech_result *result = NULL;
+ struct ast_speech *speech = find_speech(chan);
+ char tmp[128] = "";
+
+ /* Now go for the various options */
+ if (!strcasecmp(data, "status")) {
+ if (speech != NULL)
+ ast_copy_string(buf, "1", len);
+ else
+ ast_copy_string(buf, "0", len);
+ return 0;
+ }
+
+ /* Make sure we have a speech structure for everything else */
+ if (speech == NULL) {
+ return -1;
+ }
+
+ /* Check to see if they are checking for silence */
+ if (!strcasecmp(data, "spoke")) {
+ if (ast_test_flag(speech, AST_SPEECH_SPOKE))
+ ast_copy_string(buf, "1", len);
+ else
+ ast_copy_string(buf, "0", len);
+ } else if (!strcasecmp(data, "results")) {
+ /* Count number of results */
+ for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
+ results++;
+ snprintf(tmp, sizeof(tmp), "%d", results);
+ ast_copy_string(buf, tmp, len);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function speech_function = {
+ .name = "SPEECH",
+ .synopsis = "Gets information about speech recognition results.",
+ .syntax = "SPEECH(argument)",
+ .desc =
+ "Gets information about speech recognition results.\n"
+ "status: Returns 1 upon speech object existing, or 0 if not\n"
+ "spoke: Returns 1 if spoker spoke, or 0 if not\n"
+ "results: Returns number of results that were recognized\n",
+ .read = speech_read,
+ .write = NULL,
+};
+
+
+
+/*! \brief SpeechCreate() Dialplan Application */
+static int speech_create(struct ast_channel *chan, void *data)
+{
+ struct ast_speech *speech = NULL;
+ struct ast_datastore *datastore = NULL;
+
+ /* Request a speech object */
+ speech = ast_speech_new(data, chan->nativeformats);
+ if (speech == NULL) {
+ /* Not available */
+ pbx_builtin_setvar_helper(chan, "ERROR", "1");
+ return 0;
+ }
+
+ datastore = ast_channel_datastore_alloc(&speech_datastore, NULL);
+ if (datastore == NULL) {
+ ast_speech_destroy(speech);
+ pbx_builtin_setvar_helper(chan, "ERROR", "1");
+ return 0;
+ }
+ datastore->data = speech;
+ ast_channel_datastore_add(chan, datastore);
+
+ return 0;
+}
+
+/*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
+static int speech_load(struct ast_channel *chan, void *vdata)
+{
+ int res = 0;
+ struct ast_speech *speech = find_speech(chan);
+ char *data;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(grammar);
+ AST_APP_ARG(path);
+ );
+
+ data = ast_strdupa(vdata);
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (speech == NULL)
+ return -1;
+
+ if (args.argc != 2)
+ return -1;
+
+ /* Load the grammar locally on the object */
+ res = ast_speech_grammar_load(speech, args.grammar, args.path);
+
+ return res;
+}
+
+/*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
+static int speech_unload(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_speech *speech = find_speech(chan);
+
+ if (speech == NULL)
+ return -1;
+
+ /* Unload the grammar */
+ res = ast_speech_grammar_unload(speech, data);
+
+ return res;
+}
+
+/*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
+static int speech_deactivate(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_speech *speech = find_speech(chan);
+
+ if (speech == NULL)
+ return -1;
+
+ /* Deactivate the grammar on the speech object */
+ res = ast_speech_grammar_deactivate(speech, data);
+
+ return res;
+}
+
+/*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
+static int speech_activate(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_speech *speech = find_speech(chan);
+
+ if (speech == NULL)
+ return -1;
+
+ /* Activate the grammar on the speech object */
+ res = ast_speech_grammar_activate(speech, data);
+
+ return res;
+}
+
+/*! \brief SpeechStart() Dialplan Application */
+static int speech_start(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_speech *speech = find_speech(chan);
+
+ if (speech == NULL)
+ return -1;
+
+ ast_speech_start(speech);
+
+ return res;
+}
+
+/*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
+static int speech_processing_sound(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_speech *speech = find_speech(chan);
+
+ if (speech == NULL)
+ return -1;
+
+ if (speech->processing_sound != NULL) {
+ ast_free(speech->processing_sound);
+ speech->processing_sound = NULL;
+ }
+
+ speech->processing_sound = ast_strdup(data);
+
+ return res;
+}
+
+/*! \brief Helper function used by speech_background to playback a soundfile */
+static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
+{
+ struct ast_filestream *fs = NULL;
+
+ if (!(fs = ast_openstream(chan, filename, preflang)))
+ return -1;
+
+ if (ast_applystream(chan, fs))
+ return -1;
+
+ ast_playstream(fs);
+
+ return 0;
+}
+
+/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
+static int speech_background(struct ast_channel *chan, void *data)
+{
+ unsigned int timeout = 0;
+ int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
+ struct ast_speech *speech = find_speech(chan);
+ struct ast_frame *f = NULL;
+ int oldreadformat = AST_FORMAT_SLINEAR;
+ char dtmf[AST_MAX_EXTENSION] = "";
+ time_t start, current;
+ struct ast_datastore *datastore = NULL;
+ char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
+ const char *tmp2 = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(soundfile);
+ AST_APP_ARG(timeout);
+ );
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (speech == NULL)
+ return -1;
+
+ /* If channel is not already answered, then answer it */
+ if (chan->_state != AST_STATE_UP && ast_answer(chan))
+ return -1;
+
+ /* Record old read format */
+ oldreadformat = chan->readformat;
+
+ /* Change read format to be signed linear */
+ if (ast_set_read_format(chan, speech->format))
+ return -1;
+
+ if (!ast_strlen_zero(args.soundfile)) {
+ /* Yay sound file */
+ filename_tmp = ast_strdupa(args.soundfile);
+ if (!ast_strlen_zero(args.timeout)) {
+ if ((timeout = atoi(args.timeout)) == 0)
+ timeout = -1;
+ } else
+ timeout = 0;
+ }
+
+ /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
+ if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2))
+ max_dtmf_len = atoi(tmp2);
+
+ /* See if a terminator is specified */
+ if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
+ if (ast_strlen_zero(tmp2))
+ dtmf_terminator = '\0';
+ else
+ dtmf_terminator = tmp2[0];
+ }
+
+ /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
+ if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
+ ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
+ ast_speech_start(speech);
+ }
+
+ /* Ensure no streams are currently running */
+ ast_stopstream(chan);
+
+ /* Okay it's streaming so go into a loop grabbing frames! */
+ while (done == 0) {
+ /* If the filename is null and stream is not running, start up a new sound file */
+ if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
+ /* Discard old stream information */
+ ast_stopstream(chan);
+ /* Start new stream */
+ speech_streamfile(chan, filename, chan->language);
+ }
+
+ /* Run scheduled stuff */
+ ast_sched_runq(chan->sched);
+
+ /* Yay scheduling */
+ res = ast_sched_wait(chan->sched);
+ if (res < 0)
+ res = 1000;
+
+ /* If there is a frame waiting, get it - if not - oh well */
+ if (ast_waitfor(chan, res) > 0) {
+ f = ast_read(chan);
+ if (f == NULL) {
+ /* The channel has hung up most likely */
+ done = 3;
+ break;
+ }
+ }
+
+ /* Do timeout check (shared between audio/dtmf) */
+ if ((!quieted || strlen(dtmf)) && started == 1) {
+ time(&current);
+ if ((current-start) >= timeout) {
+ done = 1;
+ if (f)
+ ast_frfree(f);
+ break;
+ }
+ }
+
+ /* Do checks on speech structure to see if it's changed */
+ ast_mutex_lock(&speech->lock);
+ if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
+ if (chan->stream)
+ ast_stopstream(chan);
+ ast_clear_flag(speech, AST_SPEECH_QUIET);
+ quieted = 1;
+ }
+ /* Check state so we can see what to do */
+ switch (speech->state) {
+ case AST_SPEECH_STATE_READY:
+ /* If audio playback has stopped do a check for timeout purposes */
+ if (chan->streamid == -1 && chan->timingfunc == NULL)
+ ast_stopstream(chan);
+ if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
+ if (timeout == -1) {
+ done = 1;
+ if (f)
+ ast_frfree(f);
+ break;
+ }
+ time(&start);
+ started = 1;
+ }
+ /* Write audio frame out to speech engine if no DTMF has been received */
+ if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
+ ast_speech_write(speech, f->data, f->datalen);
+ }
+ break;
+ case AST_SPEECH_STATE_WAIT:
+ /* Cue up waiting sound if not already playing */
+ if (!strlen(dtmf)) {
+ if (chan->stream == NULL) {
+ if (speech->processing_sound != NULL) {
+ if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
+ speech_streamfile(chan, speech->processing_sound, chan->language);
+ }
+ }
+ } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
+ ast_stopstream(chan);
+ if (speech->processing_sound != NULL) {
+ if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
+ speech_streamfile(chan, speech->processing_sound, chan->language);
+ }
+ }
+ }
+ }
+ break;
+ case AST_SPEECH_STATE_DONE:
+ /* Now that we are done... let's switch back to not ready state */
+ ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
+ if (!strlen(dtmf)) {
+ /* Copy to speech structure the results, if available */
+ speech->results = ast_speech_results_get(speech);
+ /* Break out of our background too */
+ done = 1;
+ /* Stop audio playback */
+ if (chan->stream != NULL) {
+ ast_stopstream(chan);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ ast_mutex_unlock(&speech->lock);
+
+ /* Deal with other frame types */
+ if (f != NULL) {
+ /* Free the frame we received */
+ switch (f->frametype) {
+ case AST_FRAME_DTMF:
+ if (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) {
+ done = 1;
+ } else {
+ if (chan->stream != NULL) {
+ ast_stopstream(chan);
+ }
+ if (!started) {
+ /* Change timeout to be 5 seconds for DTMF input */
+ timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5;
+ started = 1;
+ }
+ time(&start);
+ snprintf(tmp, sizeof(tmp), "%c", f->subclass);
+ strncat(dtmf, tmp, sizeof(dtmf));
+ /* If the maximum length of the DTMF has been reached, stop now */
+ if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
+ done = 1;
+ }
+ break;
+ case AST_FRAME_CONTROL:
+ switch (f->subclass) {
+ case AST_CONTROL_HANGUP:
+ /* Since they hung up we should destroy the speech structure */
+ done = 3;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ ast_frfree(f);
+ f = NULL;
+ }
+ }
+
+ if (!ast_strlen_zero(dtmf)) {
+ /* We sort of make a results entry */
+ speech->results = ast_calloc(1, sizeof(*speech->results));
+ if (speech->results != NULL) {
+ ast_speech_dtmf(speech, dtmf);
+ speech->results->score = 1000;
+ speech->results->text = ast_strdup(dtmf);
+ speech->results->grammar = ast_strdup("dtmf");
+ }
+ }
+
+ /* See if it was because they hung up */
+ if (done == 3) {
+ /* Destroy speech structure */
+ ast_speech_destroy(speech);
+ datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
+ if (datastore != NULL)
+ ast_channel_datastore_remove(chan, datastore);
+ } else {
+ /* Channel is okay so restore read format */
+ ast_set_read_format(chan, oldreadformat);
+ }
+
+ return 0;
+}
+
+
+/*! \brief SpeechDestroy() Dialplan Application */
+static int speech_destroy(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_speech *speech = find_speech(chan);
+ struct ast_datastore *datastore = NULL;
+
+ if (speech == NULL)
+ return -1;
+
+ /* Destroy speech structure */
+ ast_speech_destroy(speech);
+
+ datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
+ if (datastore != NULL) {
+ ast_channel_datastore_remove(chan, datastore);
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res = ast_unregister_application("SpeechCreate");
+ res |= ast_unregister_application("SpeechLoadGrammar");
+ res |= ast_unregister_application("SpeechUnloadGrammar");
+ res |= ast_unregister_application("SpeechActivateGrammar");
+ res |= ast_unregister_application("SpeechDeactivateGrammar");
+ res |= ast_unregister_application("SpeechStart");
+ res |= ast_unregister_application("SpeechBackground");
+ res |= ast_unregister_application("SpeechDestroy");
+ res |= ast_unregister_application("SpeechProcessingSound");
+ res |= ast_custom_function_unregister(&speech_function);
+ res |= ast_custom_function_unregister(&speech_score_function);
+ res |= ast_custom_function_unregister(&speech_text_function);
+ res |= ast_custom_function_unregister(&speech_grammar_function);
+ res |= ast_custom_function_unregister(&speech_engine_function);
+ res |= ast_custom_function_unregister(&speech_results_type_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
+ res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
+ res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
+ res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
+ res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
+ res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
+ res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
+ res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
+ res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
+ res |= ast_custom_function_register(&speech_function);
+ res |= ast_custom_function_register(&speech_score_function);
+ res |= ast_custom_function_register(&speech_text_function);
+ res |= ast_custom_function_register(&speech_grammar_function);
+ res |= ast_custom_function_register(&speech_engine_function);
+ res |= ast_custom_function_register(&speech_results_type_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");
diff --git a/trunk/apps/app_stack.c b/trunk/apps/app_stack.c
new file mode 100644
index 000000000..7f53aff15
--- /dev/null
+++ b/trunk/apps/app_stack.c
@@ -0,0 +1,416 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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 Stack applications Gosub, Return, etc.
+ *
+ * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/manager.h"
+#include "asterisk/channel.h"
+
+static const char *app_gosub = "Gosub";
+static const char *app_gosubif = "GosubIf";
+static const char *app_return = "Return";
+static const char *app_pop = "StackPop";
+
+static const char *gosub_synopsis = "Jump to label, saving return address";
+static const char *gosubif_synopsis = "Conditionally jump to label, saving return address";
+static const char *return_synopsis = "Return from gosub routine";
+static const char *pop_synopsis = "Remove one address from gosub stack";
+
+static const char *gosub_descrip =
+" Gosub([[context,]exten,]priority[(arg1[,...][,argN])]):\n"
+"Jumps to the label specified, saving the return address.\n";
+static const char *gosubif_descrip =
+" GosubIf(condition?labeliftrue[(arg1[,...])][:labeliffalse[(arg1[,...])]]):\n"
+"If the condition is true, then jump to labeliftrue. If false, jumps to\n"
+"labeliffalse, if specified. In either case, a jump saves the return point\n"
+"in the dialplan, to be returned to with a Return.\n";
+static const char *return_descrip =
+" Return([return-value]):\n"
+"Jumps to the last label on the stack, removing it. The return value, if\n"
+"any, is saved in the channel variable GOSUB_RETVAL.\n";
+static const char *pop_descrip =
+" StackPop():\n"
+"Removes last label on the stack, discarding it.\n";
+
+
+static void gosub_free(void *data);
+
+static struct ast_datastore_info stack_info = {
+ .type = "GOSUB",
+ .destroy = gosub_free,
+};
+
+struct gosub_stack_frame {
+ AST_LIST_ENTRY(gosub_stack_frame) entries;
+ /* 100 arguments is all that we support anyway, but this will handle up to 255 */
+ unsigned char arguments;
+ struct varshead varshead;
+ int priority;
+ char *context;
+ char extension[0];
+};
+
+static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
+{
+ struct ast_var_t *variables;
+ int found = 0;
+
+ /* Does this variable already exist? */
+ AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
+ if (!strcmp(var, ast_var_name(variables))) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!ast_strlen_zero(value)) {
+ if (!found) {
+ variables = ast_var_assign(var, "");
+ AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
+ pbx_builtin_pushvar_helper(chan, var, value);
+ } else
+ pbx_builtin_setvar_helper(chan, var, value);
+
+ manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
+ "Channel: %s\r\n"
+ "Variable: LOCAL(%s)\r\n"
+ "Value: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan->name, var, value, chan->uniqueid);
+ }
+ return 0;
+}
+
+static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
+{
+ unsigned char i;
+ char argname[15];
+ struct ast_var_t *vardata;
+
+ /* If chan is not defined, then we're calling it as part of gosub_free,
+ * and the channel variables will be deallocated anyway. Otherwise, we're
+ * just releasing a single frame, so we need to clean up the arguments for
+ * that frame, so that we re-expose the variables from the previous frame
+ * that were hidden by this one.
+ */
+ if (chan) {
+ for (i = 1; i <= frame->arguments && i != 0; i++) {
+ snprintf(argname, sizeof(argname), "ARG%hhd", i);
+ pbx_builtin_setvar_helper(chan, argname, NULL);
+ }
+ }
+
+ /* Delete local variables */
+ while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
+ if (chan)
+ pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
+ ast_var_delete(vardata);
+ }
+
+ ast_free(frame);
+}
+
+static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
+{
+ struct gosub_stack_frame *new = NULL;
+ int len_extension = strlen(extension), len_context = strlen(context);
+
+ if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
+ AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
+ strcpy(new->extension, extension);
+ new->context = new->extension + len_extension + 1;
+ strcpy(new->context, context);
+ new->priority = priority;
+ new->arguments = arguments;
+ }
+ return new;
+}
+
+static void gosub_free(void *data)
+{
+ AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
+ struct gosub_stack_frame *oldframe;
+ AST_LIST_LOCK(oldlist);
+ while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
+ gosub_release_frame(NULL, oldframe);
+ }
+ AST_LIST_UNLOCK(oldlist);
+ AST_LIST_HEAD_DESTROY(oldlist);
+ ast_free(oldlist);
+}
+
+static int pop_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+ struct gosub_stack_frame *oldframe;
+ AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+
+ if (!stack_store) {
+ ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
+ return 0;
+ }
+
+ oldlist = stack_store->data;
+ AST_LIST_LOCK(oldlist);
+ oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
+ AST_LIST_UNLOCK(oldlist);
+
+ if (oldframe) {
+ gosub_release_frame(chan, oldframe);
+ } else {
+ ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
+ }
+ return 0;
+}
+
+static int return_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+ struct gosub_stack_frame *oldframe;
+ AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+ char *retval = data;
+
+ if (!stack_store) {
+ ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
+ return -1;
+ }
+
+ oldlist = stack_store->data;
+ AST_LIST_LOCK(oldlist);
+ oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
+ AST_LIST_UNLOCK(oldlist);
+
+ if (!oldframe) {
+ ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
+ return -1;
+ }
+
+ ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
+ gosub_release_frame(chan, oldframe);
+
+ /* Set a return value, if any */
+ pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
+ return 0;
+}
+
+static int gosub_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+ AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+ struct gosub_stack_frame *newframe;
+ char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
+ int i;
+ AST_DECLARE_APP_ARGS(args2,
+ AST_APP_ARG(argval)[100];
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "%s requires an argument: %s([[context|]exten|]priority[(arg1[|...][|argN])])\n", app_gosub, app_gosub);
+ return -1;
+ }
+
+ if (!stack_store) {
+ ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
+ stack_store = ast_channel_datastore_alloc(&stack_info, NULL);
+ if (!stack_store) {
+ ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
+ return -1;
+ }
+
+ oldlist = ast_calloc(1, sizeof(*oldlist));
+ if (!oldlist) {
+ ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
+ ast_channel_datastore_free(stack_store);
+ return -1;
+ }
+
+ stack_store->data = oldlist;
+ AST_LIST_HEAD_INIT(oldlist);
+ ast_channel_datastore_add(chan, stack_store);
+ }
+
+ /* Separate the arguments from the label */
+ /* NOTE: you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
+ label = strsep(&tmp, "(");
+ if (tmp) {
+ endparen = strrchr(tmp, ')');
+ if (endparen)
+ *endparen = '\0';
+ else
+ ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
+ AST_STANDARD_APP_ARGS(args2, tmp);
+ } else
+ args2.argc = 0;
+
+ /* Create the return address, but don't save it until we know that the Gosub destination exists */
+ newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
+
+ if (!newframe)
+ return -1;
+
+ if (ast_parseable_goto(chan, label)) {
+ ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
+ ast_free(newframe);
+ return -1;
+ }
+
+ /* Now that we know for certain that we're going to a new location, set our arguments */
+ for (i = 0; i < args2.argc; i++) {
+ snprintf(argname, sizeof(argname), "ARG%d", i + 1);
+ frame_set_var(chan, newframe, argname, args2.argval[i]);
+ ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
+ }
+
+ /* And finally, save our return address */
+ oldlist = stack_store->data;
+ AST_LIST_LOCK(oldlist);
+ AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
+ AST_LIST_UNLOCK(oldlist);
+
+ return 0;
+}
+
+static int gosubif_exec(struct ast_channel *chan, void *data)
+{
+ char *args;
+ int res=0;
+ AST_DECLARE_APP_ARGS(cond,
+ AST_APP_ARG(ition);
+ AST_APP_ARG(labels);
+ );
+ AST_DECLARE_APP_ARGS(label,
+ AST_APP_ARG(iftrue);
+ AST_APP_ARG(iffalse);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
+ return 0;
+ }
+
+ args = ast_strdupa(data);
+ AST_NONSTANDARD_APP_ARGS(cond, args, '?');
+ if (cond.argc != 2) {
+ ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
+ return 0;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(label, cond.labels, ':');
+
+ if (pbx_checkcondition(cond.ition)) {
+ if (!ast_strlen_zero(label.iftrue))
+ res = gosub_exec(chan, label.iftrue);
+ } else if (!ast_strlen_zero(label.iffalse)) {
+ res = gosub_exec(chan, label.iffalse);
+ }
+
+ return res;
+}
+
+static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+ AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+ struct gosub_stack_frame *frame;
+ struct ast_var_t *variables;
+
+ if (!stack_store)
+ return -1;
+
+ oldlist = stack_store->data;
+ AST_LIST_LOCK(oldlist);
+ frame = AST_LIST_FIRST(oldlist);
+ AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
+ if (!strcmp(data, ast_var_name(variables))) {
+ const char *tmp = pbx_builtin_getvar_helper(chan, data);
+ ast_copy_string(buf, S_OR(tmp, ""), len);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(oldlist);
+ return 0;
+}
+
+static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
+{
+ struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+ AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+ struct gosub_stack_frame *frame;
+
+ if (!stack_store) {
+ ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
+ return -1;
+ }
+
+ oldlist = stack_store->data;
+ AST_LIST_LOCK(oldlist);
+ frame = AST_LIST_FIRST(oldlist);
+
+ if (frame)
+ frame_set_var(chan, frame, var, value);
+
+ AST_LIST_UNLOCK(oldlist);
+
+ return 0;
+}
+
+static struct ast_custom_function local_function = {
+ .name = "LOCAL",
+ .synopsis = "Variables local to the gosub stack frame",
+ .syntax = "LOCAL(<varname>)",
+ .write = local_write,
+ .read = local_read,
+};
+
+static int unload_module(void)
+{
+ ast_unregister_application(app_return);
+ ast_unregister_application(app_pop);
+ ast_unregister_application(app_gosubif);
+ ast_unregister_application(app_gosub);
+ ast_custom_function_unregister(&local_function);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
+ ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
+ ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
+ ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
+ ast_custom_function_register(&local_function);
+
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");
diff --git a/trunk/apps/app_system.c b/trunk/apps/app_system.c
new file mode 100644
index 000000000..1f39c5a4e
--- /dev/null
+++ b/trunk/apps/app_system.c
@@ -0,0 +1,129 @@
+/*
+ * 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 system commands
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h" /* autoservice */
+
+static char *app = "System";
+
+static char *app2 = "TrySystem";
+
+static char *synopsis = "Execute a system command";
+
+static char *synopsis2 = "Try executing a system command";
+
+static char *chanvar = "SYSTEMSTATUS";
+
+static char *descrip =
+" System(command): Executes a command by using system(). If the command\n"
+"fails, the console should report a fallthrough. \n"
+"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
+" FAILURE Could not execute the specified command\n"
+" SUCCESS Specified command successfully executed\n";
+
+static char *descrip2 =
+" TrySystem(command): Executes a command by using system().\n"
+"on any situation.\n"
+"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
+" FAILURE Could not execute the specified command\n"
+" SUCCESS Specified command successfully executed\n"
+" APPERROR Specified command successfully executed, but returned error code\n";
+
+static int system_exec_helper(struct ast_channel *chan, void *data, int failmode)
+{
+ int res = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "System requires an argument(command)\n");
+ pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
+ return failmode;
+ }
+
+ ast_autoservice_start(chan);
+
+ /* Do our thing here */
+ res = ast_safe_system((char *)data);
+ if ((res < 0) && (errno != ECHILD)) {
+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
+ pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
+ res = failmode;
+ } else if (res == 127) {
+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
+ pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
+ res = failmode;
+ } else {
+ if (res < 0)
+ res = 0;
+ if (res != 0)
+ pbx_builtin_setvar_helper(chan, chanvar, "APPERROR");
+ else
+ pbx_builtin_setvar_helper(chan, chanvar, "SUCCESS");
+ res = 0;
+ }
+
+ ast_autoservice_stop(chan);
+
+ return res;
+}
+
+static int system_exec(struct ast_channel *chan, void *data)
+{
+ return system_exec_helper(chan, data, -1);
+}
+
+static int trysystem_exec(struct ast_channel *chan, void *data)
+{
+ return system_exec_helper(chan, data, 0);
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+ res |= ast_unregister_application(app2);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app2, trysystem_exec, synopsis2, descrip2);
+ res |= ast_register_application(app, system_exec, synopsis, descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Generic System() application");
diff --git a/trunk/apps/app_talkdetect.c b/trunk/apps/app_talkdetect.c
new file mode 100644
index 000000000..0aa5e818b
--- /dev/null
+++ b/trunk/apps/app_talkdetect.c
@@ -0,0 +1,211 @@
+/*
+ * 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 Playback a file with audio detect
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/dsp.h"
+#include "asterisk/app.h"
+
+static char *app = "BackgroundDetect";
+
+static char *synopsis = "Background a file with talk detect";
+
+static char *descrip =
+" BackgroundDetect(filename[,sil[,min,[max]]]): Plays back a given\n"
+"filename, waiting for interruption from a given digit (the digit must\n"
+"start the beginning of a valid extension, or it will be ignored).\n"
+"During the playback of the file, audio is monitored in the receive\n"
+"direction, and if a period of non-silence which is greater than 'min' ms\n"
+"yet less than 'max' ms is followed by silence for at least 'sil' ms then\n"
+"the audio playback is aborted and processing jumps to the 'talk' extension\n"
+"if available. If unspecified, sil, min, and max default to 1000, 100, and\n"
+"infinity respectively.\n";
+
+
+static int background_detect_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *tmp;
+ struct ast_frame *fr;
+ int notsilent = 0;
+ struct timeval start = { 0, 0};
+ int sil = 1000;
+ int min = 100;
+ int max = -1;
+ int x;
+ int origrformat=0;
+ struct ast_dsp *dsp = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(silence);
+ AST_APP_ARG(min);
+ AST_APP_ARG(max);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
+ return -1;
+ }
+
+ tmp = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, tmp);
+
+ if ((sscanf(args.silence, "%d", &x) == 1) && (x > 0))
+ sil = x;
+ if ((sscanf(args.min, "%d", &x) == 1) && (x > 0))
+ min = x;
+ if ((sscanf(args.max, "%d", &x) == 1) && (x > 0))
+ max = x;
+
+ ast_debug(1, "Preparing detect of '%s', sil=%d, min=%d, max=%d\n", args.filename, sil, min, max);
+ do {
+ if (chan->_state != AST_STATE_UP) {
+ if ((res = ast_answer(chan)))
+ break;
+ }
+
+ origrformat = chan->readformat;
+ if ((ast_set_read_format(chan, AST_FORMAT_SLINEAR))) {
+ ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
+ res = -1;
+ break;
+ }
+
+ if (!(dsp = ast_dsp_new())) {
+ ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
+ res = -1;
+ break;
+ }
+ ast_stopstream(chan);
+ if (ast_streamfile(chan, tmp, chan->language)) {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
+ break;
+ }
+
+ while (chan->stream) {
+ res = ast_sched_wait(chan->sched);
+ if ((res < 0) && !chan->timingfunc) {
+ res = 0;
+ break;
+ }
+ if (res < 0)
+ res = 1000;
+ res = ast_waitfor(chan, res);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name);
+ break;
+ } else if (res > 0) {
+ fr = ast_read(chan);
+ if (!fr) {
+ res = -1;
+ break;
+ } else if (fr->frametype == AST_FRAME_DTMF) {
+ char t[2];
+ t[0] = fr->subclass;
+ t[1] = '\0';
+ if (ast_canmatch_extension(chan, chan->context, t, 1, chan->cid.cid_num)) {
+ /* They entered a valid extension, or might be anyhow */
+ res = fr->subclass;
+ ast_frfree(fr);
+ break;
+ }
+ } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR)) {
+ int totalsilence;
+ int ms;
+ res = ast_dsp_silence(dsp, fr, &totalsilence);
+ if (res && (totalsilence > sil)) {
+ /* We've been quiet a little while */
+ if (notsilent) {
+ /* We had heard some talking */
+ ms = ast_tvdiff_ms(ast_tvnow(), start);
+ ms -= sil;
+ if (ms < 0)
+ ms = 0;
+ if ((ms > min) && ((max < 0) || (ms < max))) {
+ char ms_str[10];
+ ast_debug(1, "Found qualified token of %d ms\n", ms);
+
+ /* Save detected talk time (in milliseconds) */
+ sprintf(ms_str, "%d", ms );
+ pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
+
+ ast_goto_if_exists(chan, chan->context, "talk", 1);
+ res = 0;
+ ast_frfree(fr);
+ break;
+ } else {
+ ast_debug(1, "Found unqualified token of %d ms\n", ms);
+ }
+ notsilent = 0;
+ }
+ } else {
+ if (!notsilent) {
+ /* Heard some audio, mark the begining of the token */
+ start = ast_tvnow();
+ ast_debug(1, "Start of voice token!\n");
+ notsilent = 1;
+ }
+ }
+ }
+ ast_frfree(fr);
+ }
+ ast_sched_runq(chan->sched);
+ }
+ ast_stopstream(chan);
+ } while (0);
+
+ if (res > -1) {
+ if (origrformat && ast_set_read_format(chan, origrformat)) {
+ ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
+ chan->name, ast_getformatname(origrformat));
+ }
+ }
+ if (dsp)
+ ast_dsp_free(dsp);
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, background_detect_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Playback with Talk Detection");
diff --git a/trunk/apps/app_test.c b/trunk/apps/app_test.c
new file mode 100644
index 000000000..e13dc865f
--- /dev/null
+++ b/trunk/apps/app_test.c
@@ -0,0 +1,467 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Russell Bryant <russelb@clemson.edu>
+ *
+ * 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 to test connection and produce report in text file
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Russell Bryant <russelb@clemson.edu>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+
+static char *tests_descrip =
+ " TestServer(): Perform test server function and write call report.\n"
+ "Results stored in /var/log/asterisk/testreports/<testid>-server.txt";
+static char *tests_app = "TestServer";
+static char *tests_synopsis = "Execute Interface Test Server";
+
+static char *testc_descrip =
+ " TestClient(testid): Executes test client with given testid.\n"
+ "Results stored in /var/log/asterisk/testreports/<testid>-client.txt";
+
+static char *testc_app = "TestClient";
+static char *testc_synopsis = "Execute Interface Test Client";
+
+static int measurenoise(struct ast_channel *chan, int ms, char *who)
+{
+ int res=0;
+ int mssofar;
+ int noise=0;
+ int samples=0;
+ int x;
+ short *foo;
+ struct timeval start;
+ struct ast_frame *f;
+ int rformat;
+ rformat = chan->readformat;
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_NOTICE, "Unable to set to linear mode!\n");
+ return -1;
+ }
+ start = ast_tvnow();
+ for(;;) {
+ mssofar = ast_tvdiff_ms(ast_tvnow(), start);
+ if (mssofar > ms)
+ break;
+ res = ast_waitfor(chan, ms - mssofar);
+ if (res < 1)
+ break;
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
+ foo = (short *)f->data;
+ for (x=0;x<f->samples;x++) {
+ noise += abs(foo[x]);
+ samples++;
+ }
+ }
+ ast_frfree(f);
+ }
+
+ if (rformat) {
+ if (ast_set_read_format(chan, rformat)) {
+ ast_log(LOG_NOTICE, "Unable to restore original format!\n");
+ return -1;
+ }
+ }
+ if (res < 0)
+ return res;
+ if (!samples) {
+ ast_log(LOG_NOTICE, "No samples were received from the other side!\n");
+ return -1;
+ }
+ ast_debug(1, "%s: Noise: %d, samples: %d, avg: %d\n", who, noise, samples, noise / samples);
+ return (noise / samples);
+}
+
+static int sendnoise(struct ast_channel *chan, int ms)
+{
+ int res;
+ res = ast_tonepair_start(chan, 1537, 2195, ms, 8192);
+ if (!res) {
+ res = ast_waitfordigit(chan, ms);
+ ast_tonepair_stop(chan);
+ }
+ return res;
+}
+
+static int testclient_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *testid=data;
+ char fn[80];
+ char serverver[80];
+ FILE *f;
+
+ /* Check for test id */
+ if (ast_strlen_zero(testid)) {
+ ast_log(LOG_WARNING, "TestClient requires an argument - the test id\n");
+ return -1;
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ /* Wait a few just to be sure things get started */
+ res = ast_safe_sleep(chan, 3000);
+ /* Transmit client version */
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0);
+ ast_debug(1, "Transmit client version\n");
+
+ /* Read server version */
+ ast_debug(1, "Read server version\n");
+ if (!res)
+ res = ast_app_getdata(chan, NULL, serverver, sizeof(serverver) - 1, 0);
+ if (res > 0)
+ res = 0;
+ ast_debug(1, "server version: %s\n", serverver);
+
+ if (res > 0)
+ res = 0;
+
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ /* Send test id */
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, testid, 0, 0);
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, "#", 0, 0);
+ ast_debug(1, "send test identifier: %s\n", testid);
+
+ if ((res >=0) && (!ast_strlen_zero(testid))) {
+ /* Make the directory to hold the test results in case it's not there */
+ snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
+ ast_mkdir(fn, 0777);
+ snprintf(fn, sizeof(fn), "%s/testresults/%s-client.txt", ast_config_AST_LOG_DIR, testid);
+ if ((f = fopen(fn, "w+"))) {
+ setlinebuf(f);
+ fprintf(f, "CLIENTCHAN: %s\n", chan->name);
+ fprintf(f, "CLIENTTEST ID: %s\n", testid);
+ fprintf(f, "ANSWER: PASS\n");
+ res = 0;
+
+ if (!res) {
+ /* Step 1: Wait for "1" */
+ ast_debug(1, "TestClient: 2. Wait DTMF 1\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 1: %s\n", (res != '1') ? "FAIL" : "PASS");
+ if (res == '1')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ if (!res) {
+ /* Step 2: Send "2" */
+ ast_debug(1, "TestClient: 2. Send DTMF 2\n");
+ res = ast_dtmf_stream(chan, NULL, "2", 0, 0);
+ fprintf(f, "SEND DTMF 2: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 3: Wait one second */
+ ast_debug(1, "TestClient: 3. Wait one second\n");
+ res = ast_safe_sleep(chan, 1000);
+ fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 4: Measure noise */
+ ast_debug(1, "TestClient: 4. Measure noise\n");
+ res = measurenoise(chan, 5000, "TestClient");
+ fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 5: Wait for "4" */
+ ast_debug(1, "TestClient: 5. Wait DTMF 4\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 4: %s\n", (res != '4') ? "FAIL" : "PASS");
+ if (res == '4')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res) {
+ /* Step 6: Transmit tone noise */
+ ast_debug(1, "TestClient: 6. Transmit tone\n");
+ res = sendnoise(chan, 6000);
+ fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
+ }
+ if (!res || (res == '5')) {
+ /* Step 7: Wait for "5" */
+ ast_debug(1, "TestClient: 7. Wait DTMF 5\n");
+ if (!res)
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 5: %s\n", (res != '5') ? "FAIL" : "PASS");
+ if (res == '5')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res) {
+ /* Step 8: Wait one second */
+ ast_debug(1, "TestClient: 8. Wait one second\n");
+ res = ast_safe_sleep(chan, 1000);
+ fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 9: Measure noise */
+ ast_debug(1, "TestClient: 6. Measure tone\n");
+ res = measurenoise(chan, 4000, "TestClient");
+ fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 10: Send "7" */
+ ast_debug(1, "TestClient: 7. Send DTMF 7\n");
+ res = ast_dtmf_stream(chan, NULL, "7", 0, 0);
+ fprintf(f, "SEND DTMF 7: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res =0;
+ }
+ if (!res) {
+ /* Step 11: Wait for "8" */
+ ast_debug(1, "TestClient: 11. Wait DTMF 8\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 8: %s\n", (res != '8') ? "FAIL" : "PASS");
+ if (res == '8')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res) {
+ /* Step 12: Hangup! */
+ ast_debug(1, "TestClient: 12. Hangup\n");
+ }
+
+ ast_debug(1, "-- TEST COMPLETE--\n");
+ fprintf(f, "-- END TEST--\n");
+ fclose(f);
+ res = -1;
+ } else
+ res = -1;
+ } else {
+ ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
+ res = -1;
+ }
+ return res;
+}
+
+static int testserver_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char testid[80]="";
+ char fn[80];
+ FILE *f;
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ /* Read version */
+ ast_debug(1, "Read client version\n");
+ if (!res)
+ res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
+ if (res > 0)
+ res = 0;
+
+ ast_debug(1, "client version: %s\n", testid);
+ ast_debug(1, "Transmit server version\n");
+
+ res = ast_safe_sleep(chan, 1000);
+ if (!res)
+ res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0);
+ if (res > 0)
+ res = 0;
+
+ if (!res)
+ res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
+ ast_debug(1, "read test identifier: %s\n", testid);
+ /* Check for sneakyness */
+ if (strchr(testid, '/'))
+ res = -1;
+ if ((res >=0) && (!ast_strlen_zero(testid))) {
+ /* Got a Test ID! Whoo hoo! */
+ /* Make the directory to hold the test results in case it's not there */
+ snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
+ ast_mkdir(fn, 0777);
+ snprintf(fn, sizeof(fn), "%s/testresults/%s-server.txt", ast_config_AST_LOG_DIR, testid);
+ if ((f = fopen(fn, "w+"))) {
+ setlinebuf(f);
+ fprintf(f, "SERVERCHAN: %s\n", chan->name);
+ fprintf(f, "SERVERTEST ID: %s\n", testid);
+ fprintf(f, "ANSWER: PASS\n");
+ ast_debug(1, "Processing Test ID '%s'\n", testid);
+ res = ast_safe_sleep(chan, 1000);
+ if (!res) {
+ /* Step 1: Send "1" */
+ ast_debug(1, "TestServer: 1. Send DTMF 1\n");
+ res = ast_dtmf_stream(chan, NULL, "1", 0,0 );
+ fprintf(f, "SEND DTMF 1: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 2: Wait for "2" */
+ ast_debug(1, "TestServer: 2. Wait DTMF 2\n");
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 2: %s\n", (res != '2') ? "FAIL" : "PASS");
+ if (res == '2')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res) {
+ /* Step 3: Measure noise */
+ ast_debug(1, "TestServer: 3. Measure noise\n");
+ res = measurenoise(chan, 6000, "TestServer");
+ fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 4: Send "4" */
+ ast_debug(1, "TestServer: 4. Send DTMF 4\n");
+ res = ast_dtmf_stream(chan, NULL, "4", 0, 0);
+ fprintf(f, "SEND DTMF 4: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 5: Wait one second */
+ ast_debug(1, "TestServer: 5. Wait one second\n");
+ res = ast_safe_sleep(chan, 1000);
+ fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 6: Measure noise */
+ ast_debug(1, "TestServer: 6. Measure tone\n");
+ res = measurenoise(chan, 4000, "TestServer");
+ fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 7: Send "5" */
+ ast_debug(1, "TestServer: 7. Send DTMF 5\n");
+ res = ast_dtmf_stream(chan, NULL, "5", 0, 0);
+ fprintf(f, "SEND DTMF 5: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+
+ if (!res) {
+ /* Step 8: Transmit tone noise */
+ ast_debug(1, "TestServer: 8. Transmit tone\n");
+ res = sendnoise(chan, 6000);
+ fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
+ }
+
+ if (!res || (res == '7')) {
+ /* Step 9: Wait for "7" */
+ ast_debug(1, "TestServer: 9. Wait DTMF 7\n");
+ if (!res)
+ res = ast_waitfordigit(chan, 3000);
+ fprintf(f, "WAIT DTMF 7: %s\n", (res != '7') ? "FAIL" : "PASS");
+ if (res == '7')
+ res = 0;
+ else
+ res = -1;
+ }
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ if (!res) {
+ /* Step 10: Send "8" */
+ ast_debug(1, "TestServer: 10. Send DTMF 8\n");
+ res = ast_dtmf_stream(chan, NULL, "8", 0, 0);
+ fprintf(f, "SEND DTMF 8: %s\n", (res < 0) ? "FAIL" : "PASS");
+ if (res > 0)
+ res = 0;
+ }
+ if (!res) {
+ /* Step 11: Wait for hangup to arrive! */
+ ast_debug(1, "TestServer: 11. Waiting for hangup\n");
+ res = ast_safe_sleep(chan, 10000);
+ fprintf(f, "WAIT HANGUP: %s\n", (res < 0) ? "PASS" : "FAIL");
+ }
+
+ ast_log(LOG_NOTICE, "-- TEST COMPLETE--\n");
+ fprintf(f, "-- END TEST--\n");
+ fclose(f);
+ res = -1;
+ } else
+ res = -1;
+ } else {
+ ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
+ res = -1;
+ }
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(testc_app);
+ res |= ast_unregister_application(tests_app);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(testc_app, testclient_exec, testc_synopsis, testc_descrip);
+ res |= ast_register_application(tests_app, testserver_exec, tests_synopsis, tests_descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Interface Test Application");
diff --git a/trunk/apps/app_transfer.c b/trunk/apps/app_transfer.c
new file mode 100644
index 000000000..ee6c2c588
--- /dev/null
+++ b/trunk/apps/app_transfer.c
@@ -0,0 +1,125 @@
+/*
+ * 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 Transfer a caller
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * Requires transfer support from channel driver
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h"
+
+
+static const char *app = "Transfer";
+
+static const char *synopsis = "Transfer caller to remote extension";
+
+static const char *descrip =
+" Transfer([Tech/]dest[,options]): Requests the remote caller be transferred\n"
+"to a given destination. If TECH (SIP, IAX2, LOCAL etc) is used, only\n"
+"an incoming call with the same channel technology will be transfered.\n"
+"Note that for SIP, if you transfer before call is setup, a 302 redirect\n"
+"SIP message will be returned to the caller.\n"
+"\nThe result of the application will be reported in the TRANSFERSTATUS\n"
+"channel variable:\n"
+" SUCCESS Transfer succeeded\n"
+" FAILURE Transfer failed\n"
+" UNSUPPORTED Transfer unsupported by channel driver\n";
+
+static int transfer_exec(struct ast_channel *chan, void *data)
+{
+ int res;
+ int len;
+ char *slash;
+ char *tech = NULL;
+ char *dest = NULL;
+ char *status;
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(dest);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero((char *)data)) {
+ ast_log(LOG_WARNING, "Transfer requires an argument ([Tech/]destination[,options])\n");
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+ return 0;
+ } else
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.options) {
+ }
+
+ dest = args.dest;
+
+ if ((slash = strchr(dest, '/')) && (len = (slash - dest))) {
+ tech = dest;
+ dest = slash + 1;
+ /* Allow execution only if the Tech/destination agrees with the type of the channel */
+ if (strncasecmp(chan->tech->type, tech, len)) {
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+ return 0;
+ }
+ }
+
+ /* Check if the channel supports transfer before we try it */
+ if (!chan->tech->transfer) {
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "UNSUPPORTED");
+ return 0;
+ }
+
+ res = ast_transfer(chan, dest);
+
+ if (res < 0) {
+ status = "FAILURE";
+ res = 0;
+ } else {
+ status = "SUCCESS";
+ res = 0;
+ }
+
+ pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", status);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, transfer_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Transfers a caller to another extension");
diff --git a/trunk/apps/app_url.c b/trunk/apps/app_url.c
new file mode 100644
index 000000000..f71b32fd0
--- /dev/null
+++ b/trunk/apps/app_url.c
@@ -0,0 +1,149 @@
+/*
+ * 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 App to transmit a URL
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h"
+
+static char *app = "SendURL";
+
+static char *synopsis = "Send a URL";
+
+static char *descrip =
+" SendURL(URL[,option]): Requests client go to URL (IAX2) or sends the \n"
+"URL to the client (other channels).\n"
+"Result is returned in the SENDURLSTATUS channel variable:\n"
+" SUCCESS URL successfully sent to client\n"
+" FAILURE Failed to send URL\n"
+" NOLOAD Client failed to load URL (wait enabled)\n"
+" UNSUPPORTED Channel does not support URL transport\n"
+"\n"
+"If the option 'w' is specified, execution will wait for an\n"
+"acknowledgement that the URL has been loaded before continuing\n"
+"\n"
+"SendURL continues normally if the URL was sent correctly or if the channel\n"
+"does not support HTML transport. Otherwise, the channel is hung up.\n";
+
+enum {
+ OPTION_WAIT = (1 << 0),
+} option_flags;
+
+AST_APP_OPTIONS(app_opts,{
+ AST_APP_OPTION('w', OPTION_WAIT),
+});
+
+static int sendurl_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *tmp;
+ struct ast_frame *f;
+ char *status = "FAILURE";
+ char *opts[0];
+ struct ast_flags flags;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(url);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SendURL requires an argument (URL)\n");
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
+ return -1;
+ }
+
+ tmp = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, tmp);
+ if (args.argc == 2)
+ ast_app_parse_options(app_opts, &flags, opts, args.options);
+
+ if (!ast_channel_supports_html(chan)) {
+ /* Does not support transport */
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "UNSUPPORTED");
+ return 0;
+ }
+ res = ast_channel_sendurl(chan, args.url);
+ if (res == -1) {
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "FAILURE");
+ return res;
+ }
+ status = "SUCCESS";
+ if (ast_test_flag(&flags, OPTION_WAIT)) {
+ for(;;) {
+ /* Wait for an event */
+ res = ast_waitfor(chan, -1);
+ if (res < 0)
+ break;
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ status = "FAILURE";
+ break;
+ }
+ if (f->frametype == AST_FRAME_HTML) {
+ switch(f->subclass) {
+ case AST_HTML_LDCOMPLETE:
+ res = 0;
+ ast_frfree(f);
+ status = "NOLOAD";
+ goto out;
+ break;
+ case AST_HTML_NOSUPPORT:
+ /* Does not support transport */
+ status = "UNSUPPORTED";
+ res = 0;
+ ast_frfree(f);
+ goto out;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know what to do with HTML subclass %d\n", f->subclass);
+ };
+ }
+ ast_frfree(f);
+ }
+ }
+out:
+ pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, sendurl_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send URL Applications");
diff --git a/trunk/apps/app_userevent.c b/trunk/apps/app_userevent.c
new file mode 100644
index 000000000..91d067d54
--- /dev/null
+++ b/trunk/apps/app_userevent.c
@@ -0,0 +1,89 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * 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 UserEvent application -- send manager event
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/manager.h"
+#include "asterisk/app.h"
+
+static char *app = "UserEvent";
+
+static char *synopsis = "Send an arbitrary event to the manager interface";
+
+static char *descrip =
+" UserEvent(eventname[,body]): Sends an arbitrary event to the manager\n"
+"interface, with an optional body representing additional arguments. The\n"
+"body may be specified as a | delimeted list of headers. Each additional\n"
+"argument will be placed on a new line in the event. The format of the\n"
+"event will be:\n"
+" Event: UserEvent\n"
+" UserEvent: <specified event name>\n"
+" [body]\n"
+"If no body is specified, only Event and UserEvent headers will be present.\n";
+
+
+static int userevent_exec(struct ast_channel *chan, void *data)
+{
+ char *parse, buf[2048] = "";
+ int x, buflen = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(eventname);
+ AST_APP_ARG(extra)[100];
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "UserEvent requires an argument (eventname,optional event body)\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ for (x = 0; x < args.argc - 1; x++) {
+ ast_copy_string(buf + buflen, args.extra[x], sizeof(buf) - buflen - 2);
+ buflen += strlen(args.extra[x]);
+ ast_copy_string(buf + buflen, "\r\n", 3);
+ buflen += 2;
+ }
+
+ manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", args.eventname, buf);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, userevent_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Custom User Event Application");
diff --git a/trunk/apps/app_verbose.c b/trunk/apps/app_verbose.c
new file mode 100644
index 000000000..525cc1c55
--- /dev/null
+++ b/trunk/apps/app_verbose.c
@@ -0,0 +1,158 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2005 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_verbose_v001@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Verbose logging application
+ *
+ * \author Tilghman Lesher <app_verbose_v001@the-tilghman.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h"
+
+static char *app_verbose = "Verbose";
+static char *verbose_synopsis = "Send arbitrary text to verbose output";
+static char *verbose_descrip =
+"Verbose([<level>,]<message>)\n"
+" level must be an integer value. If not specified, defaults to 0.\n";
+
+static char *app_log = "Log";
+static char *log_synopsis = "Send arbitrary text to a selected log level";
+static char *log_descrip =
+"Log(<level>,<message>)\n"
+" level must be one of ERROR, WARNING, NOTICE, DEBUG, VERBOSE, DTMF\n";
+
+
+static int verbose_exec(struct ast_channel *chan, void *data)
+{
+ int vsize;
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(level);
+ AST_APP_ARG(msg);
+ );
+
+ if (ast_strlen_zero(data)) {
+ return 0;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (args.argc == 1) {
+ args.msg = args.level;
+ args.level = "0";
+ }
+
+ if (sscanf(args.level, "%d", &vsize) != 1) {
+ vsize = 0;
+ ast_log(LOG_WARNING, "'%s' is not a verboser number\n", args.level);
+ }
+ if (option_verbose >= vsize) {
+ switch (vsize) {
+ case 0:
+ ast_verbose("%s\n", args.msg);
+ break;
+ case 1:
+ ast_verbose(VERBOSE_PREFIX_1 "%s\n", args.msg);
+ break;
+ case 2:
+ ast_verbose(VERBOSE_PREFIX_2 "%s\n", args.msg);
+ break;
+ case 3:
+ ast_verbose(VERBOSE_PREFIX_3 "%s\n", args.msg);
+ break;
+ default:
+ ast_verbose(VERBOSE_PREFIX_4 "%s\n", args.msg);
+ }
+ }
+
+ return 0;
+}
+
+static int log_exec(struct ast_channel *chan, void *data)
+{
+ char *parse;
+ int lnum = -1;
+ char extension[AST_MAX_EXTENSION + 5], context[AST_MAX_EXTENSION + 2];
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(level);
+ AST_APP_ARG(msg);
+ );
+
+ if (ast_strlen_zero(data))
+ return 0;
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!strcasecmp(args.level, "ERROR")) {
+ lnum = __LOG_ERROR;
+ } else if (!strcasecmp(args.level, "WARNING")) {
+ lnum = __LOG_WARNING;
+ } else if (!strcasecmp(args.level, "NOTICE")) {
+ lnum = __LOG_NOTICE;
+ } else if (!strcasecmp(args.level, "DEBUG")) {
+ lnum = __LOG_DEBUG;
+ } else if (!strcasecmp(args.level, "VERBOSE")) {
+ lnum = __LOG_VERBOSE;
+ } else if (!strcasecmp(args.level, "DTMF")) {
+ lnum = __LOG_DTMF;
+ } else if (!strcasecmp(args.level, "EVENT")) {
+ lnum = __LOG_EVENT;
+ } else {
+ ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level);
+ }
+
+ if (lnum > -1) {
+ snprintf(context, sizeof(context), "@ %s", chan->context);
+ snprintf(extension, sizeof(extension), "Ext. %s", chan->exten);
+
+ ast_log(lnum, extension, chan->priority, context, "%s\n", args.msg);
+ }
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app_verbose);
+ res |= ast_unregister_application(app_log);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(app_log, log_exec, log_synopsis, log_descrip);
+ res |= ast_register_application(app_verbose, verbose_exec, verbose_synopsis, verbose_descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send verbose output");
diff --git a/trunk/apps/app_voicemail.c b/trunk/apps/app_voicemail.c
new file mode 100644
index 000000000..0ba4e36be
--- /dev/null
+++ b/trunk/apps/app_voicemail.c
@@ -0,0 +1,9831 @@
+/*
+ * 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 Comedian Mail - Voicemail System
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \extref Unixodbc - http://www.unixodbc.org
+ * \extref A source distribution of University of Washington's IMAP
+c-client (http://www.washington.edu/imap/
+ *
+ * \par See also
+ * \arg \ref Config_vm
+ * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
+ * \ingroup applications
+ * \note This module requires res_adsi to load. This needs to be optional
+ * during compilation.
+ *
+ *
+ *
+ * \note This file is now almost impossible to work with, due to all \#ifdefs.
+ * Feels like the database code before realtime. Someone - please come up
+ * with a plan to clean this up.
+ */
+
+/*** MODULEINFO
+ <depend>res_smdi</depend>
+ ***/
+
+/*** MAKEOPTS
+<category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
+ <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
+ <depend>unixodbc</depend>
+ <depend>ltdl</depend>
+ <conflict>IMAP_STORAGE</conflict>
+ <defaultenabled>no</defaultenabled>
+ </member>
+ <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
+ <depend>imap_tk</depend>
+ <conflict>ODBC_STORAGE</conflict>
+ <use>ssl</use>
+ <defaultenabled>no</defaultenabled>
+ </member>
+</category>
+ ***/
+
+/*It is important to include the IMAP_STORAGE related headers
+ * before asterisk.h since asterisk.h includes logger.h. logger.h
+ * and c-client.h have conflicting definitions for LOG_WARNING and
+ * LOG_DEBUG, so it's important that we use Asterisk's definitions
+ * here instead of the c-client's
+ */
+#ifdef IMAP_STORAGE
+#include <ctype.h>
+#include <signal.h>
+#include <pwd.h>
+#ifdef USE_SYSTEM_IMAP
+#include <imap/c-client.h>
+#include <imap/imap4r1.h>
+#include <imap/linkage.h>
+#else
+#include "c-client.h"
+#include "imap4r1.h"
+#include "linkage.h"
+#endif
+#endif
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <dirent.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/say.h"
+#include "asterisk/module.h"
+#include "asterisk/adsi.h"
+#include "asterisk/app.h"
+#include "asterisk/manager.h"
+#include "asterisk/dsp.h"
+#include "asterisk/localtime.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/smdi.h"
+#include "asterisk/event.h"
+
+#ifdef ODBC_STORAGE
+#include "asterisk/res_odbc.h"
+#endif
+
+#ifdef IMAP_STORAGE
+static char imapserver[48];
+static char imapport[8];
+static char imapflags[128];
+static char imapfolder[64];
+static char greetingfolder[64];
+static char authuser[32];
+static char authpassword[42];
+
+static int expungeonhangup = 1;
+static int imapgreetings = 0;
+static char delimiter = '\0';
+
+struct vm_state;
+struct ast_vm_user;
+
+/* Forward declarations for IMAP */
+static int init_mailstream(struct vm_state *vms, int box);
+static void write_file(char *filename, char *buffer, unsigned long len);
+static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
+static void vm_imap_delete(int msgnum, struct vm_state *vms);
+static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
+static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
+static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
+static void vmstate_insert(struct vm_state *vms);
+static void vmstate_delete(struct vm_state *vms);
+static void set_update(MAILSTREAM * stream);
+static void init_vm_state(struct vm_state *vms);
+static void check_msgArray(struct vm_state *vms);
+static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
+static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
+static void get_mailbox_delimiter(MAILSTREAM *stream);
+static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
+static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
+static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
+static void update_messages_by_imapuser(const char *user, unsigned long number);
+
+static int imap_remove_file (char *dir, int msgnum);
+static int imap_retrieve_file (char *dir, int msgnum, const char *mailbox, char *context);
+static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
+static void check_quota(struct vm_state *vms, char *mailbox);
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
+struct vmstate {
+ struct vm_state *vms;
+ AST_LIST_ENTRY(vmstate) list;
+};
+
+static AST_LIST_HEAD_STATIC(vmstates, vmstate);
+
+#endif
+
+#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
+
+#define COMMAND_TIMEOUT 5000
+/* Don't modify these here; set your umask at runtime instead */
+#define VOICEMAIL_DIR_MODE 0777
+#define VOICEMAIL_FILE_MODE 0666
+#define CHUNKSIZE 65536
+
+#define VOICEMAIL_CONFIG "voicemail.conf"
+#define ASTERISK_USERNAME "asterisk"
+
+/* Define fast-forward, pause, restart, and reverse keys
+ while listening to a voicemail message - these are
+ strings, not characters */
+#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
+#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
+#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
+#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
+#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
+#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
+
+/* Default mail command to mail voicemail. Change it with the
+ mailcmd= command in voicemail.conf */
+#define SENDMAIL "/usr/sbin/sendmail -t"
+
+#define INTRO "vm-intro"
+
+#define MAXMSG 100
+#define MAXMSGLIMIT 9999
+
+#define BASELINELEN 72
+#define BASEMAXINLINE 256
+#define eol "\r\n"
+
+#define MAX_DATETIME_FORMAT 512
+#define MAX_NUM_CID_CONTEXTS 10
+
+#define VM_REVIEW (1 << 0)
+#define VM_OPERATOR (1 << 1)
+#define VM_SAYCID (1 << 2)
+#define VM_SVMAIL (1 << 3)
+#define VM_ENVELOPE (1 << 4)
+#define VM_SAYDURATION (1 << 5)
+#define VM_SKIPAFTERCMD (1 << 6)
+#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
+#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
+#define VM_PBXSKIP (1 << 9)
+#define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
+#define VM_ATTACH (1 << 11)
+#define VM_DELETE (1 << 12)
+#define VM_ALLOCED (1 << 13)
+#define VM_SEARCH (1 << 14)
+#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
+#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
+#define ERROR_LOCK_PATH -100
+
+
+enum {
+ NEW_FOLDER,
+ OLD_FOLDER,
+ WORK_FOLDER,
+ FAMILY_FOLDER,
+ FRIENDS_FOLDER,
+ GREETINGS_FOLDER
+} vm_box;
+
+enum {
+ OPT_SILENT = (1 << 0),
+ OPT_BUSY_GREETING = (1 << 1),
+ OPT_UNAVAIL_GREETING = (1 << 2),
+ OPT_RECORDGAIN = (1 << 3),
+ OPT_PREPEND_MAILBOX = (1 << 4),
+ OPT_AUTOPLAY = (1 << 6),
+ OPT_DTMFEXIT = (1 << 7),
+} vm_option_flags;
+
+enum {
+ OPT_ARG_RECORDGAIN = 0,
+ OPT_ARG_PLAYFOLDER = 1,
+ OPT_ARG_DTMFEXIT = 2,
+ /* This *must* be the last value in this enum! */
+ OPT_ARG_ARRAY_SIZE = 3,
+} vm_option_args;
+
+AST_APP_OPTIONS(vm_app_options, {
+ AST_APP_OPTION('s', OPT_SILENT),
+ AST_APP_OPTION('b', OPT_BUSY_GREETING),
+ AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
+ AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
+ AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
+ AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
+ AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
+});
+
+static int load_config(int reload);
+
+/*! \page vmlang Voicemail Language Syntaxes Supported
+
+ \par Syntaxes supported, not really language codes.
+ \arg \b en - English
+ \arg \b de - German
+ \arg \b es - Spanish
+ \arg \b fr - French
+ \arg \b it - Italian
+ \arg \b nl - Dutch
+ \arg \b pt - Portuguese
+ \arg \b pt_BR - Portuguese (Brazil)
+ \arg \b gr - Greek
+ \arg \b no - Norwegian
+ \arg \b se - Swedish
+ \arg \b tw - Chinese (Taiwan)
+ \arg \b ua - Ukrainian
+
+German requires the following additional soundfile:
+\arg \b 1F einE (feminine)
+
+Spanish requires the following additional soundfile:
+\arg \b 1M un (masculine)
+
+Dutch, Portuguese & Spanish require the following additional soundfiles:
+\arg \b vm-INBOXs singular of 'new'
+\arg \b vm-Olds singular of 'old/heard/read'
+
+NB these are plural:
+\arg \b vm-INBOX nieuwe (nl)
+\arg \b vm-Old oude (nl)
+
+Polish uses:
+\arg \b vm-new-a 'new', feminine singular accusative
+\arg \b vm-new-e 'new', feminine plural accusative
+\arg \b vm-new-ych 'new', feminine plural genitive
+\arg \b vm-old-a 'old', feminine singular accusative
+\arg \b vm-old-e 'old', feminine plural accusative
+\arg \b vm-old-ych 'old', feminine plural genitive
+\arg \b digits/1-a 'one', not always same as 'digits/1'
+\arg \b digits/2-ie 'two', not always same as 'digits/2'
+
+Swedish uses:
+\arg \b vm-nytt singular of 'new'
+\arg \b vm-nya plural of 'new'
+\arg \b vm-gammalt singular of 'old'
+\arg \b vm-gamla plural of 'old'
+\arg \b digits/ett 'one', not always same as 'digits/1'
+
+Norwegian uses:
+\arg \b vm-ny singular of 'new'
+\arg \b vm-nye plural of 'new'
+\arg \b vm-gammel singular of 'old'
+\arg \b vm-gamle plural of 'old'
+
+Dutch also uses:
+\arg \b nl-om 'at'?
+
+Spanish also uses:
+\arg \b vm-youhaveno
+
+Ukrainian requires the following additional soundfile:
+\arg \b vm-nove 'nove'
+\arg \b vm-stare 'stare'
+\arg \b digits/ua/1e 'odne'
+
+Italian requires the following additional soundfile:
+
+For vm_intro_it:
+\arg \b vm-nuovo new
+\arg \b vm-nuovi new plural
+\arg \b vm-vecchio old
+\arg \b vm-vecchi old plural
+
+Chinese (Taiwan) requires the following additional soundfile:
+\arg \b vm-tong A class-word for call (tong1)
+\arg \b vm-ri A class-word for day (ri4)
+\arg \b vm-you You (ni3)
+\arg \b vm-haveno Have no (mei2 you3)
+\arg \b vm-have Have (you3)
+\arg \b vm-listen To listen (yao4 ting1)
+
+
+\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
+spelled among others when you have to change folder. For the above reasons, vm-INBOX
+and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
+
+*/
+
+struct baseio {
+ int iocp;
+ int iolen;
+ int linelength;
+ int ateof;
+ unsigned char iobuf[BASEMAXINLINE];
+};
+
+/*! Structure for linked list of users
+ * Use ast_vm_user_destroy() to free one of these structures. */
+struct ast_vm_user {
+ char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
+ char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
+ char password[80]; /*!< Secret pin code, numbers only */
+ char fullname[80]; /*!< Full name, for directory app */
+ char email[80]; /*!< E-mail address */
+ char pager[80]; /*!< E-mail address to pager (no attachment) */
+ char serveremail[80]; /*!< From: Mail address */
+ char mailcmd[160]; /*!< Configurable mail command */
+ char language[MAX_LANGUAGE]; /*!< Config: Language setting */
+ char zonetag[80]; /*!< Time zone */
+ char callback[80];
+ char dialout[80];
+ char uniqueid[20]; /*!< Unique integer identifier */
+ char exit[80];
+ char attachfmt[20]; /*!< Attachment format */
+ unsigned int flags; /*!< VM_ flags */
+ int saydurationm;
+ int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
+ int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
+ int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
+#ifdef IMAP_STORAGE
+ char imapuser[80]; /*!< IMAP server login */
+ char imappassword[80]; /*!< IMAP server password if authpassword not defined */
+#endif
+ double volgain; /*!< Volume gain for voicemails sent via email */
+ AST_LIST_ENTRY(ast_vm_user) list;
+};
+
+/*! Voicemail time zones */
+struct vm_zone {
+ AST_LIST_ENTRY(vm_zone) list;
+ char name[80];
+ char timezone[80];
+ char msg_format[512];
+};
+
+/*! Voicemail mailbox state */
+struct vm_state {
+ char curbox[80];
+ char username[80];
+ char curdir[PATH_MAX];
+ char vmbox[PATH_MAX];
+ char fn[PATH_MAX];
+ char fn2[PATH_MAX];
+ int *deleted;
+ int *heard;
+ int curmsg;
+ int lastmsg;
+ int newmessages;
+ int oldmessages;
+ int starting;
+ int repeats;
+#ifdef IMAP_STORAGE
+ int updated; /*!< decremented on each mail check until 1 -allows delay */
+ long msgArray[256];
+ MAILSTREAM *mailstream;
+ int vmArrayIndex;
+ char imapuser[80]; /*!< IMAP server login */
+ int interactive;
+ unsigned int quota_limit;
+ unsigned int quota_usage;
+ struct vm_state *persist_vms;
+#endif
+};
+
+
+#ifdef ODBC_STORAGE
+static char odbc_database[80];
+static char odbc_table[80];
+#define RETRIEVE(a,b,c,d) retrieve_file(a,b)
+#define DISPOSE(a,b) remove_file(a,b)
+#define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
+#define EXISTS(a,b,c,d) (message_exists(a,b))
+#define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
+#define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
+#define DELETE(a,b,c) (delete_file(a,b))
+#else
+#ifdef IMAP_STORAGE
+#define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
+#define DISPOSE(a,b) (imap_remove_file(a,b))
+#define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
+#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
+#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
+#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
+#define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
+#define DELETE(a,b,c) (vm_delete(c))
+#else
+#define RETRIEVE(a,b,c,d)
+#define DISPOSE(a,b)
+#define STORE(a,b,c,d,e,f,g,h,i)
+#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
+#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
+#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
+#define DELETE(a,b,c) (vm_delete(c))
+#endif
+#endif
+
+static char VM_SPOOL_DIR[PATH_MAX];
+
+static char ext_pass_cmd[128];
+
+int my_umask;
+
+#define PWDCHANGE_INTERNAL (1 << 1)
+#define PWDCHANGE_EXTERNAL (1 << 2)
+static int pwdchange = PWDCHANGE_INTERNAL;
+
+#ifdef ODBC_STORAGE
+#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
+#else
+# ifdef IMAP_STORAGE
+# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
+# else
+# define tdesc "Comedian Mail (Voicemail System)"
+# endif
+#endif
+
+static char userscontext[AST_MAX_EXTENSION] = "default";
+
+static char *addesc = "Comedian Mail";
+
+static char *synopsis_vm = "Leave a Voicemail message";
+
+static char *descrip_vm =
+ " VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
+ "application allows the calling party to leave a message for the specified\n"
+ "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
+ "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
+ "specified mailbox does not exist.\n"
+ " The Voicemail application will exit if any of the following DTMF digits are\n"
+ "received:\n"
+ " 0 - Jump to the 'o' extension in the current dialplan context.\n"
+ " * - Jump to the 'a' extension in the current dialplan context.\n"
+ " This application will set the following channel variable upon completion:\n"
+ " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
+ " application. The possible values are:\n"
+ " SUCCESS | USEREXIT | FAILED\n\n"
+ " Options:\n"
+ " b - Play the 'busy' greeting to the calling party.\n"
+ " d([c]) - Accept digits for a new extension in context c, if played during\n"
+ " the greeting. Context defaults to the current context.\n"
+ " g(#) - Use the specified amount of gain when recording the voicemail\n"
+ " message. The units are whole-number decibels (dB).\n"
+ " s - Skip the playback of instructions for leaving a message to the\n"
+ " calling party.\n"
+ " u - Play the 'unavailable' greeting.\n";
+
+static char *synopsis_vmain = "Check Voicemail messages";
+
+static char *descrip_vmain =
+ " VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
+ "calling party to check voicemail messages. A specific mailbox, and optional\n"
+ "corresponding context, may be specified. If a mailbox is not provided, the\n"
+ "calling party will be prompted to enter one. If a context is not specified,\n"
+ "the 'default' context will be used.\n\n"
+ " Options:\n"
+ " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
+ " is entered by the caller.\n"
+ " g(#) - Use the specified amount of gain when recording a voicemail\n"
+ " message. The units are whole-number decibels (dB).\n"
+ " s - Skip checking the passcode for the mailbox.\n"
+ " a(#) - Skip folder prompt and go directly to folder specified.\n"
+ " Defaults to INBOX\n";
+
+static char *synopsis_vm_box_exists =
+"Check to see if Voicemail mailbox exists";
+
+static char *descrip_vm_box_exists =
+ " MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
+ "mailbox exists. If no voicemail context is specified, the 'default' context\n"
+ "will be used.\n"
+ " This application will set the following channel variable upon completion:\n"
+ " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
+ " MailboxExists application. Possible values include:\n"
+ " SUCCESS | FAILED\n\n"
+ " Options: (none)\n";
+
+static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
+
+static char *descrip_vmauthenticate =
+ " VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
+ "same way as the Authenticate application, but the passwords are taken from\n"
+ "voicemail.conf.\n"
+ " If the mailbox is specified, only that mailbox's password will be considered\n"
+ "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
+ "be set with the authenticated mailbox.\n\n"
+ " Options:\n"
+ " s - Skip playing the initial prompts.\n";
+
+/* Leave a message */
+static char *app = "VoiceMail";
+
+/* Check mail, control, etc */
+static char *app2 = "VoiceMailMain";
+
+static char *app3 = "MailboxExists";
+static char *app4 = "VMAuthenticate";
+
+static AST_LIST_HEAD_STATIC(users, ast_vm_user);
+static AST_LIST_HEAD_STATIC(zones, vm_zone);
+static int maxsilence;
+static int maxmsg;
+static int maxdeletedmsg;
+static int silencethreshold = 128;
+static char serveremail[80];
+static char mailcmd[160]; /* Configurable mail cmd */
+static char externnotify[160];
+static struct ast_smdi_interface *smdi_iface = NULL;
+static char vmfmts[80];
+static double volgain;
+static int vmminsecs;
+static int vmmaxsecs;
+static int maxgreet;
+static int skipms;
+static int maxlogins;
+
+/*! Poll mailboxes for changes since there is something external to
+ * app_voicemail that may change them. */
+static unsigned int poll_mailboxes;
+
+/*! Polling frequency */
+static unsigned int poll_freq;
+/*! By default, poll every 30 seconds */
+#define DEFAULT_POLL_FREQ 30
+
+AST_MUTEX_DEFINE_STATIC(poll_lock);
+static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t poll_thread = AST_PTHREADT_NULL;
+static unsigned char poll_thread_run;
+
+/*! Subscription to ... MWI event subscriptions */
+static struct ast_event_sub *mwi_sub_sub;
+/*! Subscription to ... MWI event un-subscriptions */
+static struct ast_event_sub *mwi_unsub_sub;
+
+/*!
+ * \brief An MWI subscription
+ *
+ * This is so we can keep track of which mailboxes are subscribed to.
+ * This way, we know which mailboxes to poll when the pollmailboxes
+ * option is being used.
+ */
+struct mwi_sub {
+ AST_RWLIST_ENTRY(mwi_sub) entry;
+ int old_new;
+ int old_old;
+ uint32_t uniqueid;
+ char mailbox[1];
+};
+
+static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
+
+/* custom audio control prompts for voicemail playback */
+static char listen_control_forward_key[12];
+static char listen_control_reverse_key[12];
+static char listen_control_pause_key[12];
+static char listen_control_restart_key[12];
+static char listen_control_stop_key[12];
+
+/* custom password sounds */
+static char vm_password[80] = "vm-password";
+static char vm_newpassword[80] = "vm-newpassword";
+static char vm_passchanged[80] = "vm-passchanged";
+static char vm_reenterpassword[80] = "vm-reenterpassword";
+static char vm_mismatch[80] = "vm-mismatch";
+
+static struct ast_flags globalflags = {0};
+
+static int saydurationminfo;
+
+static char dialcontext[AST_MAX_CONTEXT] = "";
+static char callcontext[AST_MAX_CONTEXT] = "";
+static char exitcontext[AST_MAX_CONTEXT] = "";
+
+static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
+
+
+static char *emailbody = NULL;
+static char *emailsubject = NULL;
+static char *pagerbody = NULL;
+static char *pagersubject = NULL;
+static char fromstring[100];
+static char pagerfromstring[100];
+static char emailtitle[100];
+static char charset[32] = "ISO-8859-1";
+
+static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
+static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
+static int adsiver = 1;
+static char emaildateformat[32] = "%A, %B %d, %Y at %r";
+
+/* Forward declarations - generic */
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
+static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
+static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
+static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
+ char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
+ signed char record_gain, struct vm_state *vms);
+static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
+static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
+static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
+static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
+static void apply_options(struct ast_vm_user *vmu, const char *options);
+static int is_valid_dtmf(const char *key);
+
+#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
+static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
+#endif
+
+
+
+static void populate_defaults(struct ast_vm_user *vmu)
+{
+ ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
+ if (saydurationminfo)
+ vmu->saydurationm = saydurationminfo;
+ ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
+ ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
+ ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
+ if (vmmaxsecs)
+ vmu->maxsecs = vmmaxsecs;
+ if (maxmsg)
+ vmu->maxmsg = maxmsg;
+ if (maxdeletedmsg)
+ vmu->maxdeletedmsg = maxdeletedmsg;
+ vmu->volgain = volgain;
+}
+
+static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
+{
+ int x;
+ if (!strcasecmp(var, "attach")) {
+ ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
+ } else if (!strcasecmp(var, "attachfmt")) {
+ ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
+ } else if (!strcasecmp(var, "serveremail")) {
+ ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
+ } else if (!strcasecmp(var, "language")) {
+ ast_copy_string(vmu->language, value, sizeof(vmu->language));
+ } else if (!strcasecmp(var, "tz")) {
+ ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
+#ifdef IMAP_STORAGE
+ } else if (!strcasecmp(var, "imapuser")) {
+ ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
+ } else if (!strcasecmp(var, "imappassword")) {
+ ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
+#endif
+ } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
+ ast_set2_flag(vmu, ast_true(value), VM_DELETE);
+ } else if (!strcasecmp(var, "saycid")){
+ ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
+ } else if (!strcasecmp(var,"sendvoicemail")){
+ ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
+ } else if (!strcasecmp(var, "review")){
+ ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
+ } else if (!strcasecmp(var, "tempgreetwarn")){
+ ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
+ } else if (!strcasecmp(var, "operator")){
+ ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
+ } else if (!strcasecmp(var, "envelope")){
+ ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
+ } else if (!strcasecmp(var, "moveheard")){
+ ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
+ } else if (!strcasecmp(var, "sayduration")){
+ ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
+ } else if (!strcasecmp(var, "saydurationm")){
+ if (sscanf(value, "%d", &x) == 1) {
+ vmu->saydurationm = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
+ }
+ } else if (!strcasecmp(var, "forcename")){
+ ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
+ } else if (!strcasecmp(var, "forcegreetings")){
+ ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
+ } else if (!strcasecmp(var, "callback")) {
+ ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
+ } else if (!strcasecmp(var, "dialout")) {
+ ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
+ } else if (!strcasecmp(var, "exitcontext")) {
+ ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
+ } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
+ if (vmu->maxsecs <= 0) {
+ ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
+ vmu->maxsecs = vmmaxsecs;
+ } else {
+ vmu->maxsecs = atoi(value);
+ }
+ if (!strcasecmp(var, "maxmessage"))
+ ast_log(LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
+ } else if (!strcasecmp(var, "maxmsg")) {
+ vmu->maxmsg = atoi(value);
+ if (vmu->maxmsg <= 0) {
+ ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
+ vmu->maxmsg = MAXMSG;
+ } else if (vmu->maxmsg > MAXMSGLIMIT) {
+ ast_log(LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
+ vmu->maxmsg = MAXMSGLIMIT;
+ }
+ } else if (!strcasecmp(var, "backupdeleted")) {
+ if (sscanf(value, "%d", &x) == 1)
+ vmu->maxdeletedmsg = x;
+ else if (ast_true(value))
+ vmu->maxdeletedmsg = MAXMSG;
+ else
+ vmu->maxdeletedmsg = 0;
+
+ if (vmu->maxdeletedmsg < 0) {
+ ast_log(LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
+ vmu->maxdeletedmsg = MAXMSG;
+ } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
+ ast_log(LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
+ vmu->maxdeletedmsg = MAXMSGLIMIT;
+ }
+ } else if (!strcasecmp(var, "volgain")) {
+ sscanf(value, "%lf", &vmu->volgain);
+ } else if (!strcasecmp(var, "options")) {
+ apply_options(vmu, value);
+ }
+}
+
+static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
+{
+ int res;
+ if (!ast_strlen_zero(vmu->uniqueid)) {
+ res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
+ if (res > 0) {
+ ast_copy_string(vmu->password, password, sizeof(vmu->password));
+ res = 0;
+ } else if (!res) {
+ res = -1;
+ }
+ return res;
+ }
+ return -1;
+}
+
+static void apply_options(struct ast_vm_user *vmu, const char *options)
+{ /* Destructively Parse options and apply */
+ char *stringp;
+ char *s;
+ char *var, *value;
+ stringp = ast_strdupa(options);
+ while ((s = strsep(&stringp, "|"))) {
+ value = s;
+ if ((var = strsep(&value, "=")) && value) {
+ apply_option(vmu, var, value);
+ }
+ }
+}
+
+static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
+{
+ struct ast_variable *tmp;
+ tmp = var;
+ while (tmp) {
+ if (!strcasecmp(tmp->name, "vmsecret")) {
+ ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
+ } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
+ if (ast_strlen_zero(retval->password))
+ ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
+ } else if (!strcasecmp(tmp->name, "uniqueid")) {
+ ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
+ } else if (!strcasecmp(tmp->name, "pager")) {
+ ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
+ } else if (!strcasecmp(tmp->name, "email")) {
+ ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
+ } else if (!strcasecmp(tmp->name, "fullname")) {
+ ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
+ } else if (!strcasecmp(tmp->name, "context")) {
+ ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
+#ifdef IMAP_STORAGE
+ } else if (!strcasecmp(tmp->name, "imapuser")) {
+ ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
+ } else if (!strcasecmp(tmp->name, "imappassword")) {
+ ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
+#endif
+ } else
+ apply_option(retval, tmp->name, tmp->value);
+ tmp = tmp->next;
+ }
+}
+
+static int is_valid_dtmf(const char *key)
+{
+ int i;
+ char *local_key = ast_strdupa(key);
+
+ for (i = 0; i < strlen(key); ++i) {
+ if (!strchr(VALID_DTMF, *local_key)) {
+ ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
+ return 0;
+ }
+ local_key++;
+ }
+ return 1;
+}
+
+static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
+{
+ struct ast_variable *var;
+ struct ast_vm_user *retval;
+
+ if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
+ if (!ivm)
+ ast_set_flag(retval, VM_ALLOCED);
+ else
+ memset(retval, 0, sizeof(*retval));
+ if (mailbox)
+ ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
+ populate_defaults(retval);
+ if (!context && ast_test_flag((&globalflags), VM_SEARCH))
+ var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
+ else
+ var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
+ if (var) {
+ apply_options_full(retval, var);
+ ast_variables_destroy(var);
+ } else {
+ if (!ivm)
+ ast_free(retval);
+ retval = NULL;
+ }
+ }
+ return retval;
+}
+
+static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
+{
+ /* This function could be made to generate one from a database, too */
+ struct ast_vm_user *vmu=NULL, *cur;
+ AST_LIST_LOCK(&users);
+
+ if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
+ context = "default";
+
+ AST_LIST_TRAVERSE(&users, cur, list) {
+ if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
+ break;
+ if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
+ break;
+ }
+ if (cur) {
+ /* Make a copy, so that on a reload, we have no race */
+ if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
+ memcpy(vmu, cur, sizeof(*vmu));
+ ast_set2_flag(vmu, !ivm, VM_ALLOCED);
+ AST_LIST_NEXT(vmu, list) = NULL;
+ }
+ } else
+ vmu = find_user_realtime(ivm, context, mailbox);
+ AST_LIST_UNLOCK(&users);
+ return vmu;
+}
+
+static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
+{
+ /* This function could be made to generate one from a database, too */
+ struct ast_vm_user *cur;
+ int res = -1;
+ AST_LIST_LOCK(&users);
+ AST_LIST_TRAVERSE(&users, cur, list) {
+ if ((!context || !strcasecmp(context, cur->context)) &&
+ (!strcasecmp(mailbox, cur->mailbox)))
+ break;
+ }
+ if (cur) {
+ ast_copy_string(cur->password, newpass, sizeof(cur->password));
+ res = 0;
+ }
+ AST_LIST_UNLOCK(&users);
+ return res;
+}
+
+static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
+{
+ struct ast_config *cfg=NULL;
+ struct ast_variable *var=NULL;
+ struct ast_category *cat=NULL;
+ char *category=NULL, *value=NULL, *new=NULL;
+ const char *tmp=NULL;
+ struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
+ if (!change_password_realtime(vmu, newpassword))
+ return;
+
+ /* check voicemail.conf */
+ if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
+ while ((category = ast_category_browse(cfg, category))) {
+ if (!strcasecmp(category, vmu->context)) {
+ if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
+ ast_log(LOG_WARNING, "We could not find the mailbox.\n");
+ break;
+ }
+ value = strstr(tmp,",");
+ if (!value) {
+ ast_log(LOG_WARNING, "variable has bad format.\n");
+ break;
+ }
+ new = alloca((strlen(value)+strlen(newpassword)+1));
+ sprintf(new,"%s%s", newpassword, value);
+ if (!(cat = ast_category_get(cfg, category))) {
+ ast_log(LOG_WARNING, "Failed to get category structure.\n");
+ break;
+ }
+ ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
+ }
+ }
+ /* save the results */
+ reset_user_pw(vmu->context, vmu->mailbox, newpassword);
+ ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
+ config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
+ }
+ category = NULL;
+ var = NULL;
+ /* check users.conf and update the password stored for the mailbox*/
+ /* if no vmsecret entry exists create one. */
+ if ((cfg = ast_config_load("users.conf", config_flags))) {
+ ast_debug(4, "we are looking for %s\n", vmu->mailbox);
+ while ((category = ast_category_browse(cfg, category))) {
+ ast_debug(4, "users.conf: %s\n", category);
+ if (!strcasecmp(category, vmu->mailbox)) {
+ if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
+ ast_debug(3, "looks like we need to make vmsecret!\n");
+ var = ast_variable_new("vmsecret", newpassword, "");
+ }
+ new = alloca(strlen(newpassword)+1);
+ sprintf(new, "%s", newpassword);
+ if (!(cat = ast_category_get(cfg, category))) {
+ ast_debug(4, "failed to get category!\n");
+ break;
+ }
+ if (!var)
+ ast_variable_update(cat, "vmsecret", new, NULL, 0);
+ else
+ ast_variable_append(cat, var);
+ }
+ }
+ /* save the results and clean things up */
+ reset_user_pw(vmu->context, vmu->mailbox, newpassword);
+ ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
+ config_text_file_save("users.conf", cfg, "AppVoicemail");
+ }
+}
+
+static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
+{
+ char buf[255];
+ snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
+ if (!ast_safe_system(buf))
+ ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
+}
+
+static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
+{
+ return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
+}
+
+#ifdef IMAP_STORAGE
+static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
+{
+ int res;
+ if ((res = ast_mkdir(dir, 01777))) {
+ ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
+ return snprintf(dest, len, "%s/msg%04d", dir, num);
+ }
+ return snprintf(dest, len, "%s/msg%04d", dir, num);
+}
+
+static void vm_imap_delete(int msgnum, struct vm_state *vms)
+{
+ unsigned long messageNum = 0;
+ char arg[10];
+
+ /* find real message number based on msgnum */
+ /* this may be an index into vms->msgArray based on the msgnum. */
+
+ messageNum = vms->msgArray[msgnum];
+ if (messageNum == 0) {
+ ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
+ return;
+ }
+ ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
+ /* delete message */
+ snprintf (arg, sizeof(arg), "%lu",messageNum);
+ mail_setflag (vms->mailstream,arg,"\\DELETED");
+}
+
+#endif
+static int make_file(char *dest, int len, char *dir, int num)
+{
+ return snprintf(dest, len, "%s/msg%04d", dir, num);
+}
+
+/*! \brief basically mkdir -p $dest/$context/$ext/$folder
+ * \param dest String. base directory.
+ * \param len Length of dest.
+ * \param context String. Ignored if is null or empty string.
+ * \param ext String. Ignored if is null or empty string.
+ * \param folder String. Ignored if is null or empty string.
+ * \return -1 on failure, 0 on success.
+ */
+static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
+{
+ mode_t mode = VOICEMAIL_DIR_MODE;
+ int res;
+
+ make_dir(dest, len, context, ext, folder);
+ if ((res = ast_mkdir(dest, mode))) {
+ ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief Lock file path
+ only return failure if ast_lock_path returns 'timeout',
+ not if the path does not exist or any other reason
+*/
+static int vm_lock_path(const char *path)
+{
+ switch (ast_lock_path(path)) {
+ case AST_LOCK_TIMEOUT:
+ return -1;
+ default:
+ return 0;
+ }
+}
+
+
+#ifdef ODBC_STORAGE
+struct generic_prepare_struct {
+ char *sql;
+ int argc;
+ char **argv;
+};
+
+static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
+{
+ struct generic_prepare_struct *gps = data;
+ int res, i;
+ SQLHSTMT stmt;
+
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ return NULL;
+ }
+ res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+ for (i = 0; i < gps->argc; i++)
+ SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
+
+ return stmt;
+}
+
+static int retrieve_file(char *dir, int msgnum)
+{
+ int x = 0;
+ int res;
+ int fd=-1;
+ size_t fdlen = 0;
+ void *fdm = MAP_FAILED;
+ SQLSMALLINT colcount=0;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char fmt[80]="";
+ char *c;
+ char coltitle[256];
+ SQLSMALLINT collen;
+ SQLSMALLINT datatype;
+ SQLSMALLINT decimaldigits;
+ SQLSMALLINT nullable;
+ SQLULEN colsize;
+ SQLLEN colsize2;
+ FILE *f=NULL;
+ char rowdata[80];
+ char fn[PATH_MAX];
+ char full_fn[PATH_MAX];
+ char msgnums[80];
+ char *argv[] = { dir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+
+ struct odbc_obj *obj;
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ c = strchr(fmt, '|');
+ if (c)
+ *c = '\0';
+ if (!strcasecmp(fmt, "wav49"))
+ strcpy(fmt, "WAV");
+ snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string(fn, dir, sizeof(fn));
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+
+ if (!(f = fopen(full_fn, "w+"))) {
+ ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
+ goto yuck;
+ }
+
+ snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if (res == SQL_NO_DATA) {
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLNumResultCols(stmt, &colcount);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (f)
+ fprintf(f, "[message]\n");
+ for (x=0;x<colcount;x++) {
+ rowdata[0] = '\0';
+ collen = sizeof(coltitle);
+ res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
+ &datatype, &colsize, &decimaldigits, &nullable);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (!strcasecmp(coltitle, "recording")) {
+ off_t offset;
+ res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
+ fdlen = colsize2;
+ if (fd > -1) {
+ char tmp[1]="";
+ lseek(fd, fdlen - 1, SEEK_SET);
+ if (write(fd, tmp, 1) != 1) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+ /* Read out in small chunks */
+ for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
+ if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
+ ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ } else {
+ res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
+ munmap(fdm, CHUNKSIZE);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ unlink(full_fn);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ }
+ }
+ truncate(full_fn, fdlen);
+ }
+ } else {
+ res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
+ fprintf(f, "%s=%s\n", coltitle, rowdata);
+ }
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ if (f)
+ fclose(f);
+ if (fd > -1)
+ close(fd);
+ return x - 1;
+}
+
+static int remove_file(char *dir, int msgnum)
+{
+ char fn[PATH_MAX];
+ char full_fn[PATH_MAX];
+ char msgnums[80];
+
+ if (msgnum > -1) {
+ snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+ make_file(fn, sizeof(fn), dir, msgnum);
+ } else
+ ast_copy_string(fn, dir, sizeof(fn));
+ ast_filedelete(fn, NULL);
+ if (ast_check_realtime("voicemail_data")) {
+ ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
+ }
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ unlink(full_fn);
+ return 0;
+}
+
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
+{
+ int x = 0;
+ int res;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char rowdata[20];
+ char *argv[] = { dir };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
+
+ struct odbc_obj *obj;
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (sscanf(rowdata, "%d", &x) != 1)
+ ast_log(LOG_WARNING, "Failed to read message count!\n");
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ return x - 1;
+}
+
+static int message_exists(char *dir, int msgnum)
+{
+ int x = 0;
+ int res;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char rowdata[20];
+ char msgnums[20];
+ char *argv[] = { dir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+
+ struct odbc_obj *obj;
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (sscanf(rowdata, "%d", &x) != 1)
+ ast_log(LOG_WARNING, "Failed to read message count!\n");
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ return x;
+}
+
+static int count_messages(struct ast_vm_user *vmu, char *dir)
+{
+ return last_message_index(vmu, dir) + 1;
+}
+
+static void delete_file(char *sdir, int smsg)
+{
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char msgnums[20];
+ char *argv[] = { sdir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+
+ struct odbc_obj *obj;
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt)
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ else
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ return;
+}
+
+static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
+{
+ SQLHSTMT stmt;
+ char sql[512];
+ char msgnums[20];
+ char msgnumd[20];
+ struct odbc_obj *obj;
+ char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
+
+ delete_file(ddir, dmsg);
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt)
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+ else
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ return;
+}
+
+static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
+{
+ int x = 0;
+ int res;
+ int fd = -1;
+ void *fdm = MAP_FAILED;
+ size_t fdlen = -1;
+ SQLHSTMT stmt;
+ SQLLEN len;
+ char sql[PATH_MAX];
+ char msgnums[20];
+ char fn[PATH_MAX];
+ char full_fn[PATH_MAX];
+ char fmt[80]="";
+ char *c;
+ const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
+ const char *category = "";
+ struct ast_config *cfg=NULL;
+ struct odbc_obj *obj;
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+
+ delete_file(dir, msgnum);
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ c = strchr(fmt, '|');
+ if (c)
+ *c = '\0';
+ if (!strcasecmp(fmt, "wav49"))
+ strcpy(fmt, "WAV");
+ snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string(fn, dir, sizeof(fn));
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ cfg = ast_config_load(full_fn, config_flags);
+ snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+ fd = open(full_fn, O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (cfg) {
+ context = ast_variable_retrieve(cfg, "message", "context");
+ if (!context) context = "";
+ macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
+ if (!macrocontext) macrocontext = "";
+ callerid = ast_variable_retrieve(cfg, "message", "callerid");
+ if (!callerid) callerid = "";
+ origtime = ast_variable_retrieve(cfg, "message", "origtime");
+ if (!origtime) origtime = "";
+ duration = ast_variable_retrieve(cfg, "message", "duration");
+ if (!duration) duration = "";
+ category = ast_variable_retrieve(cfg, "message", "category");
+ if (!category) category = "";
+ }
+ fdlen = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ printf("Length is %zd\n", fdlen);
+ fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
+ if (fdm == MAP_FAILED) {
+ ast_log(LOG_WARNING, "Memory map failed!\n");
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (!ast_strlen_zero(category))
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
+ else
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
+ res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
+ SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
+ SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
+ SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
+ SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
+ SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
+ SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
+ if (!ast_strlen_zero(category))
+ SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
+ res = ast_odbc_smart_execute(obj, stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ if (cfg)
+ ast_config_destroy(cfg);
+ if (fdm != MAP_FAILED)
+ munmap(fdm, fdlen);
+ if (fd > -1)
+ close(fd);
+ return x;
+}
+
+static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
+{
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char msgnums[20];
+ char msgnumd[20];
+ struct odbc_obj *obj;
+ char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
+
+ delete_file(ddir, dmsg);
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+ snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt)
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ else
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ return;
+}
+
+#else
+#ifndef IMAP_STORAGE
+static int count_messages(struct ast_vm_user *vmu, char *dir)
+{
+ /* Find all .txt files - even if they are not in sequence from 0000 */
+
+ int vmcount = 0;
+ DIR *vmdir = NULL;
+ struct dirent *vment = NULL;
+
+ if (vm_lock_path(dir))
+ return ERROR_LOCK_PATH;
+
+ if ((vmdir = opendir(dir))) {
+ while ((vment = readdir(vmdir))) {
+ if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
+ vmcount++;
+ }
+ closedir(vmdir);
+ }
+ ast_unlock_path(dir);
+
+ return vmcount;
+}
+
+static void rename_file(char *sfn, char *dfn)
+{
+ char stxt[PATH_MAX];
+ char dtxt[PATH_MAX];
+ ast_filerename(sfn,dfn,NULL);
+ snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
+ snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
+ if (ast_check_realtime("voicemail_data")) {
+ ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
+ }
+ rename(stxt, dtxt);
+}
+
+static int copy(char *infile, char *outfile)
+{
+ int ifd;
+ int ofd;
+ int res;
+ int len;
+ char buf[4096];
+
+#ifdef HARDLINK_WHEN_POSSIBLE
+ /* Hard link if possible; saves disk space & is faster */
+ if (link(infile, outfile)) {
+#endif
+ if ((ifd = open(infile, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+ return -1;
+ }
+ if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+ close(ifd);
+ return -1;
+ }
+ do {
+ len = read(ifd, buf, sizeof(buf));
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+ close(ifd);
+ close(ofd);
+ unlink(outfile);
+ }
+ if (len) {
+ res = write(ofd, buf, len);
+ if (errno == ENOMEM || errno == ENOSPC || res != len) {
+ ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+ close(ifd);
+ close(ofd);
+ unlink(outfile);
+ }
+ }
+ } while (len);
+ close(ifd);
+ close(ofd);
+ return 0;
+#ifdef HARDLINK_WHEN_POSSIBLE
+ } else {
+ /* Hard link succeeded */
+ return 0;
+ }
+#endif
+}
+
+static void copy_file(char *frompath, char *topath)
+{
+ char frompath2[PATH_MAX], topath2[PATH_MAX];
+ struct ast_variable *tmp,*var = NULL;
+ const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
+ ast_filecopy(frompath, topath, NULL);
+ snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
+ snprintf(topath2, sizeof(topath2), "%s.txt", topath);
+ if (ast_check_realtime("voicemail_data")) {
+ var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
+ /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "origmailbox")) {
+ origmailbox = tmp->value;
+ } else if (!strcasecmp(tmp->name, "context")) {
+ context = tmp->value;
+ } else if (!strcasecmp(tmp->name, "macrocontext")) {
+ macrocontext = tmp->value;
+ } else if (!strcasecmp(tmp->name, "exten")) {
+ exten = tmp->value;
+ } else if (!strcasecmp(tmp->name, "priority")) {
+ priority = tmp->value;
+ } else if (!strcasecmp(tmp->name, "callerchan")) {
+ callerchan = tmp->value;
+ } else if (!strcasecmp(tmp->name, "callerid")) {
+ callerid = tmp->value;
+ } else if (!strcasecmp(tmp->name, "origdate")) {
+ origdate = tmp->value;
+ } else if (!strcasecmp(tmp->name, "origtime")) {
+ origtime = tmp->value;
+ } else if (!strcasecmp(tmp->name, "category")) {
+ category = tmp->value;
+ } else if (!strcasecmp(tmp->name, "duration")) {
+ duration = tmp->value;
+ }
+ }
+ ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
+ }
+ copy(frompath2, topath2);
+ ast_variables_destroy(var);
+}
+/*! \brief
+ * A negative return value indicates an error.
+ * \note Should always be called with a lock already set on dir.
+ */
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
+{
+ int x;
+ unsigned char map[MAXMSGLIMIT] = "";
+ DIR *msgdir;
+ struct dirent *msgdirent;
+ int msgdirint;
+
+ /* Reading the entire directory into a file map scales better than
+ * doing a stat repeatedly on a predicted sequence. I suspect this
+ * is partially due to stat(2) internally doing a readdir(2) itself to
+ * find each file. */
+ msgdir = opendir(dir);
+ while ((msgdirent = readdir(msgdir))) {
+ if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
+ map[msgdirint] = 1;
+ }
+ closedir(msgdir);
+
+ for (x = 0; x < vmu->maxmsg; x++) {
+ if (map[x] == 0)
+ break;
+ }
+
+ return x - 1;
+}
+
+#endif /*#ifndef IMAP_STORAGE*/
+#endif /*#else of #ifdef ODBC_STORAGE*/
+#ifndef ODBC_STORAGE
+static int vm_delete(char *file)
+{
+ char *txt;
+ int txtsize = 0;
+
+ txtsize = (strlen(file) + 5)*sizeof(char);
+ txt = alloca(txtsize);
+ /* Sprintf here would safe because we alloca'd exactly the right length,
+ * but trying to eliminate all sprintf's anyhow
+ */
+ if (ast_check_realtime("voicemail_data")) {
+ ast_destroy_realtime("voicemail_data", "filename", file, NULL);
+ }
+ snprintf(txt, txtsize, "%s.txt", file);
+ unlink(txt);
+ return ast_filedelete(file, NULL);
+}
+#endif
+
+static int inbuf(struct baseio *bio, FILE *fi)
+{
+ int l;
+
+ if (bio->ateof)
+ return 0;
+
+ if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
+ if (ferror(fi))
+ return -1;
+
+ bio->ateof = 1;
+ return 0;
+ }
+
+ bio->iolen= l;
+ bio->iocp= 0;
+
+ return 1;
+}
+
+static int inchar(struct baseio *bio, FILE *fi)
+{
+ if (bio->iocp>=bio->iolen) {
+ if (!inbuf(bio, fi))
+ return EOF;
+ }
+
+ return bio->iobuf[bio->iocp++];
+}
+
+static int ochar(struct baseio *bio, int c, FILE *so)
+{
+ if (bio->linelength >= BASELINELEN) {
+ if (fputs(eol,so) == EOF)
+ return -1;
+
+ bio->linelength= 0;
+ }
+
+ if (putc(((unsigned char)c),so) == EOF)
+ return -1;
+
+ bio->linelength++;
+
+ return 1;
+}
+
+static int base_encode(char *filename, FILE *so)
+{
+ static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+ int i,hiteof= 0;
+ FILE *fi;
+ struct baseio bio;
+
+ memset(&bio, 0, sizeof(bio));
+ bio.iocp = BASEMAXINLINE;
+
+ if (!(fi = fopen(filename, "rb"))) {
+ ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ while (!hiteof){
+ unsigned char igroup[3], ogroup[4];
+ int c,n;
+
+ igroup[0]= igroup[1]= igroup[2]= 0;
+
+ for (n= 0;n<3;n++) {
+ if ((c = inchar(&bio, fi)) == EOF) {
+ hiteof= 1;
+ break;
+ }
+
+ igroup[n]= (unsigned char)c;
+ }
+
+ if (n> 0) {
+ ogroup[0]= dtable[igroup[0]>>2];
+ ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
+ ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
+ ogroup[3]= dtable[igroup[2]&0x3F];
+
+ if (n<3) {
+ ogroup[3]= '=';
+
+ if (n<2)
+ ogroup[2]= '=';
+ }
+
+ for (i= 0;i<4;i++)
+ ochar(&bio, ogroup[i], so);
+ }
+ }
+
+ fclose(fi);
+
+ if (fputs(eol,so)==EOF)
+ return 0;
+
+ return 1;
+}
+
+static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
+{
+ char callerid[256];
+ /* Prepare variables for substitution in email body and subject */
+ pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
+ pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
+ snprintf(passdata, passdatasize, "%d", msgnum);
+ pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
+ pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
+ pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
+ pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
+ pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
+ pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
+ pbx_builtin_setvar_helper(ast, "VM_DATE", date);
+ pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
+}
+
+static char *quote(const char *from, char *to, size_t len)
+{
+ char *ptr = to;
+ *ptr++ = '"';
+ for (; ptr < to + len - 1; from++) {
+ if (*from == '"')
+ *ptr++ = '\\';
+ else if (*from == '\0')
+ break;
+ *ptr++ = *from;
+ }
+ if (ptr < to + len - 1)
+ *ptr++ = '"';
+ *ptr = '\0';
+ return to;
+}
+
+/*! \brief
+ * fill in *tm for current time according to the proper timezone, if any.
+ * Return tm so it can be used as a function argument.
+ */
+static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
+{
+ const struct vm_zone *z = NULL;
+ struct timeval t = ast_tvnow();
+
+ /* Does this user have a timezone specified? */
+ if (!ast_strlen_zero(vmu->zonetag)) {
+ /* Find the zone in the list */
+ AST_LIST_LOCK(&zones);
+ AST_LIST_TRAVERSE(&zones, z, list) {
+ if (!strcmp(z->name, vmu->zonetag))
+ break;
+ }
+ AST_LIST_UNLOCK(&zones);
+ }
+ ast_localtime(&t, tm, z ? z->timezone : NULL);
+ return tm;
+}
+
+/*! \brief same as mkstemp, but return a FILE * */
+static FILE *vm_mkftemp(char *template)
+{
+ FILE *p = NULL;
+ int pfd = mkstemp(template);
+ chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
+ if (pfd > -1) {
+ p = fdopen(pfd, "w+");
+ if (!p) {
+ close(pfd);
+ pfd = -1;
+ }
+ }
+ return p;
+}
+
+static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
+{
+ char date[256];
+ char host[MAXHOSTNAMELEN] = "";
+ char who[256];
+ char bound[256];
+ char fname[256];
+ char dur[256];
+ char tmpcmd[256];
+ struct ast_tm tm;
+ char *passdata2;
+ size_t len_passdata;
+ char *greeting_attachment;
+
+#ifdef IMAP_STORAGE
+#define ENDL "\r\n"
+#else
+#define ENDL "\n"
+#endif
+
+ gethostname(host, sizeof(host)-1);
+
+ if (strchr(srcemail, '@'))
+ ast_copy_string(who, srcemail, sizeof(who));
+ else
+ snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+
+ greeting_attachment = strrchr(ast_strdupa(attach), '/');
+ if (greeting_attachment)
+ *greeting_attachment++ = '\0';
+
+ snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+ ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+ fprintf(p, "Date: %s" ENDL, date);
+
+ /* Set date format for voicemail mail */
+ ast_strftime(date, sizeof(date), emaildateformat, &tm);
+
+ if (!ast_strlen_zero(fromstring)) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(fromstring)*3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
+ pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
+ len_passdata = strlen(passdata) * 2 + 3;
+ passdata2 = alloca(len_passdata);
+ fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
+ ast_channel_free(ast);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
+ len_passdata = strlen(vmu->fullname) * 2 + 3;
+ passdata2 = alloca(len_passdata);
+ fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
+ if (!ast_strlen_zero(emailsubject)) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(emailsubject) * 3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
+ pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
+ fprintf(p, "Subject: %s" ENDL, passdata);
+ ast_channel_free(ast);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else if (!ast_strlen_zero(emailtitle)) {
+ fprintf(p, emailtitle, msgnum + 1, mailbox) ;
+ fprintf(p, ENDL) ;
+ } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
+ fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
+ else
+ fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
+ fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
+ if (imap) {
+ /* additional information needed for IMAP searching */
+ fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
+ /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
+ fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
+ fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
+ fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
+ fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
+ fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
+ fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
+ fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
+ fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
+ if (!ast_strlen_zero(category))
+ fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
+ fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
+ fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
+ fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
+ }
+ if (!ast_strlen_zero(cidnum))
+ fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
+ if (!ast_strlen_zero(cidname))
+ fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
+ fprintf(p, "MIME-Version: 1.0" ENDL);
+ if (attach_user_voicemail) {
+ /* Something unique. */
+ snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
+
+ fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
+ fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
+ fprintf(p, "--%s" ENDL, bound);
+ }
+ fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
+ if (emailbody) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(emailbody)*3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
+ pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
+ fprintf(p, "%s" ENDL, passdata);
+ ast_channel_free(ast);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else if (msgnum > -1){
+ fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
+
+ "in mailbox %s from %s, on %s so you might" ENDL
+ "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
+ dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
+ } else {
+ fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
+ "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
+ }
+ if (attach_user_voicemail) {
+ /* Eww. We want formats to tell us their own MIME type */
+ char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+ char tmpdir[256], newtmp[256];
+ int tmpfd = -1;
+
+ if (vmu->volgain < -.001 || vmu->volgain > .001) {
+ create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
+ snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
+ tmpfd = mkstemp(newtmp);
+ chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
+ ast_debug(3, "newtmp: %s\n", newtmp);
+ if (tmpfd > -1) {
+ snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
+ ast_safe_system(tmpcmd);
+ attach = newtmp;
+ ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+ }
+ }
+ fprintf(p, "--%s" ENDL, bound);
+ if (msgnum > -1)
+ fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
+ else
+ fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
+ fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
+ fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
+ if (msgnum > -1)
+ fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
+ else
+ fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
+ snprintf(fname, sizeof(fname), "%s.%s", attach, format);
+ base_encode(fname, p);
+ fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
+ if (tmpfd > -1) {
+ unlink(fname);
+ close(tmpfd);
+ unlink(newtmp);
+ }
+ }
+#undef ENDL
+}
+
+static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
+{
+ FILE *p=NULL;
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ char tmp2[256];
+
+ if (vmu && ast_strlen_zero(vmu->email)) {
+ ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
+ return(0);
+ }
+ if (!strcmp(format, "wav49"))
+ format = "WAV";
+ ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
+ /* Make a temporary file instead of piping directly to sendmail, in case the mail
+ command hangs */
+ if ((p = vm_mkftemp(tmp)) == NULL) {
+ ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
+ return -1;
+ } else {
+ make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
+ fclose(p);
+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+ ast_safe_system(tmp2);
+ ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
+ }
+ return 0;
+}
+
+static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
+{
+ char date[256];
+ char host[MAXHOSTNAMELEN] = "";
+ char who[256];
+ char dur[PATH_MAX];
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ char tmp2[PATH_MAX];
+ struct ast_tm tm;
+ FILE *p;
+
+ if ((p = vm_mkftemp(tmp)) == NULL) {
+ ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
+ return -1;
+ }
+ gethostname(host, sizeof(host)-1);
+ if (strchr(srcemail, '@'))
+ ast_copy_string(who, srcemail, sizeof(who));
+ else
+ snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+ snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+ ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+ fprintf(p, "Date: %s\n", date);
+
+ if (*pagerfromstring) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(fromstring)*3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
+ pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
+ fprintf(p, "From: %s <%s>\n", passdata, who);
+ ast_channel_free(ast);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ fprintf(p, "From: Asterisk PBX <%s>\n", who);
+ fprintf(p, "To: %s\n", pager);
+ if (pagersubject) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(pagersubject) * 3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
+ pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
+ fprintf(p, "Subject: %s\n\n", passdata);
+ ast_channel_free(ast);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ fprintf(p, "Subject: New VM\n\n");
+
+ ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
+ if (pagerbody) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(pagerbody) * 3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
+ pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
+ fprintf(p, "%s\n", passdata);
+ ast_channel_free(ast);
+ } else
+ ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else {
+ fprintf(p, "New %s long msg in box %s\n"
+ "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
+ }
+ fclose(p);
+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+ ast_safe_system(tmp2);
+ ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
+ return 0;
+}
+
+static int get_date(char *s, int len)
+{
+ struct ast_tm tm;
+ struct timeval t = ast_tvnow();
+
+ ast_localtime(&t, &tm, "UTC");
+
+ return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
+}
+
+static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
+{
+ int res;
+ char fn[PATH_MAX];
+ char dest[PATH_MAX];
+
+ snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
+
+ if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
+ ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
+ return -1;
+ }
+
+ RETRIEVE(fn, -1, ext, context);
+ if (ast_fileexists(fn, NULL, NULL) > 0) {
+ res = ast_stream_and_wait(chan, fn, ecodes);
+ if (res) {
+ DISPOSE(fn, -1);
+ return res;
+ }
+ } else {
+ /* Dispose just in case */
+ DISPOSE(fn, -1);
+ res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
+ if (res)
+ return res;
+ res = ast_say_digit_str(chan, ext, ecodes, chan->language);
+ if (res)
+ return res;
+ }
+ res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
+ return res;
+}
+
+static void free_user(struct ast_vm_user *vmu)
+{
+ if (!ast_test_flag(vmu, VM_ALLOCED))
+ return;
+
+ ast_free(vmu);
+}
+
+static void free_zone(struct vm_zone *z)
+{
+ ast_free(z);
+}
+
+static const char *mbox(int id)
+{
+ static const char *msgs[] = {
+#ifdef IMAP_STORAGE
+ imapfolder,
+#else
+ "INBOX",
+#endif
+ "Old",
+ "Work",
+ "Family",
+ "Friends",
+ "Cust1",
+ "Cust2",
+ "Cust3",
+ "Cust4",
+ "Cust5",
+ "Deleted",
+ };
+ return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
+}
+#ifdef IMAP_STORAGE
+static int folder_int(const char *folder)
+{
+ /*assume a NULL folder means INBOX*/
+ if (!folder)
+ return 0;
+#ifdef IMAP_STORAGE
+ if (!strcasecmp(folder, imapfolder))
+#else
+ if (!strcasecmp(folder, "INBOX"))
+#endif
+ return 0;
+ else if (!strcasecmp(folder, "Old"))
+ return 1;
+ else if (!strcasecmp(folder, "Work"))
+ return 2;
+ else if (!strcasecmp(folder, "Family"))
+ return 3;
+ else if (!strcasecmp(folder, "Friends"))
+ return 4;
+ else if (!strcasecmp(folder, "Cust1"))
+ return 5;
+ else if (!strcasecmp(folder, "Cust2"))
+ return 6;
+ else if (!strcasecmp(folder, "Cust3"))
+ return 7;
+ else if (!strcasecmp(folder, "Cust4"))
+ return 8;
+ else if (!strcasecmp(folder, "Cust5"))
+ return 9;
+ else if (!strcasecmp(folder, "Deleted"))
+ return 10;
+ else /*assume they meant INBOX if folder is not found otherwise*/
+ return 0;
+}
+#endif
+
+#ifdef ODBC_STORAGE
+/*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
+static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
+{
+ int x = -1;
+ int res;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char rowdata[20];
+ char tmp[PATH_MAX] = "";
+ struct odbc_obj *obj;
+ char *context;
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
+
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ } else
+ context = "default";
+
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ *newmsgs = atoi(rowdata);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ *oldmsgs = atoi(rowdata);
+ x = 0;
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+
+yuck:
+ return x;
+}
+
+static int messagecount(const char *context, const char *mailbox, const char *folder)
+{
+ struct odbc_obj *obj = NULL;
+ int nummsgs = 0;
+ int res;
+ SQLHSTMT stmt = NULL;
+ char sql[PATH_MAX];
+ char rowdata[20];
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
+ if (!folder)
+ folder = "INBOX";
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ nummsgs = atoi(rowdata);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+
+yuck:
+ if (obj)
+ ast_odbc_release_obj(obj);
+ return nummsgs;
+}
+
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+ char tmp[256], *tmp2 = tmp, *mbox, *context;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ while ((context = mbox = strsep(&tmp2, ","))) {
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+ if (messagecount(context, mbox, folder))
+ return 1;
+ }
+ return 0;
+}
+
+#elif defined(IMAP_STORAGE)
+
+static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms)
+{
+ char *myserveremail = serveremail;
+ char fn[PATH_MAX];
+ char mailbox[256];
+ char *stringp;
+ FILE *p=NULL;
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ long len;
+ void *buf;
+ int tempcopy = 0;
+ STRING str;
+
+ /* Attach only the first format */
+ fmt = ast_strdupa(fmt);
+ stringp = fmt;
+ strsep(&stringp, "|");
+
+ if (!ast_strlen_zero(vmu->serveremail))
+ myserveremail = vmu->serveremail;
+
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string (fn, dir, sizeof(fn));
+
+ if (ast_strlen_zero(vmu->email)) {
+ /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
+ * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
+ * string if tempcopy is 1
+ */
+ ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
+ tempcopy = 1;
+ }
+
+ if (!strcmp(fmt, "wav49"))
+ fmt = "WAV";
+ ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
+
+ /* Make a temporary file instead of piping directly to sendmail, in case the mail
+ command hangs */
+ if (!(p = vm_mkftemp(tmp))) {
+ ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
+ if (tempcopy)
+ *(vmu->email) = '\0';
+ return -1;
+ }
+
+ if (msgnum < 0 && imapgreetings) {
+ init_mailstream(vms, GREETINGS_FOLDER);
+ imap_delete_old_greeting(fn, vms);
+ }
+
+ make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
+ /* read mail file to memory */
+ len = ftell(p);
+ rewind(p);
+ if (!(buf = ast_malloc(len+1))) {
+ ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
+ fclose(p);
+ if (tempcopy)
+ *(vmu->email) = '\0';
+ return -1;
+ }
+ fread(buf, len, 1, p);
+ ((char *)buf)[len] = '\0';
+ INIT(&str, mail_string, buf, len);
+ init_mailstream(vms, NEW_FOLDER);
+ imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
+ if (!mail_append(vms->mailstream, mailbox, &str))
+ ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
+ fclose(p);
+ unlink(tmp);
+ ast_free(buf);
+ ast_debug(3, "%s stored\n", fn);
+
+ if (tempcopy)
+ *(vmu->email) = '\0';
+
+ return 0;
+
+}
+
+static int messagecount(const char *context, const char *mailbox, const char *folder)
+{
+ SEARCHPGM *pgm;
+ SEARCHHEADER *hdr;
+
+ struct ast_vm_user *vmu, vmus;
+ struct vm_state *vms_p;
+ int ret = 0;
+ int fold = folder_int(folder);
+
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ /* We have to get the user before we can open the stream! */
+ /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
+ vmu = find_user(&vmus, context, mailbox);
+ if (!vmu) {
+ ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
+ return -1;
+ } else {
+ /* No IMAP account available */
+ if (vmu->imapuser[0] == '\0') {
+ ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
+ return -1;
+ }
+ }
+
+ /* No IMAP account available */
+ if (vmu->imapuser[0] == '\0') {
+ ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
+ free_user(vmu);
+ return -1;
+ }
+
+ /* check if someone is accessing this box right now... */
+ vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
+ if (!vms_p) {
+ vms_p = get_vm_state_by_mailbox(mailbox,1);
+ }
+ if (vms_p) {
+ ast_debug(3, "Returning before search - user is logged in\n");
+ if (fold == 0) {/*INBOX*/
+ return vms_p->newmessages;
+ }
+ if (fold == 1) {/*Old messages*/
+ return vms_p->oldmessages;
+ }
+ }
+
+ /* add one if not there... */
+ vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
+ if (!vms_p) {
+ vms_p = get_vm_state_by_mailbox(mailbox,0);
+ }
+
+ if (!vms_p) {
+ ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
+ if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
+ return -1;
+ }
+ ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
+ ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
+ vms_p->mailstream = NIL; /* save for access from interactive entry point */
+ ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
+ vms_p->updated = 1;
+ /* set mailbox to INBOX! */
+ ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
+ init_vm_state(vms_p);
+ vmstate_insert(vms_p);
+ }
+ ret = init_mailstream(vms_p, fold);
+ if (!vms_p->mailstream) {
+ ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
+ return -1;
+ }
+ if (ret == 0) {
+ pgm = mail_newsearchpgm ();
+ hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
+ pgm->header = hdr;
+ if (fold != 1) {
+ pgm->unseen = 1;
+ pgm->seen = 0;
+ }
+ /* In the special case where fold is 1 (old messages) we have to do things a bit
+ * differently. Old messages are stored in the INBOX but are marked as "seen"
+ */
+ else {
+ pgm->unseen = 0;
+ pgm->seen = 1;
+ }
+ pgm->undeleted = 1;
+ pgm->deleted = 0;
+
+ vms_p->vmArrayIndex = 0;
+ mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
+ if (fold == 0)
+ vms_p->newmessages = vms_p->vmArrayIndex;
+ if (fold == 1)
+ vms_p->oldmessages = vms_p->vmArrayIndex;
+ /*Freeing the searchpgm also frees the searchhdr*/
+ mail_free_searchpgm(&pgm);
+ vms_p->updated = 0;
+ return vms_p->vmArrayIndex;
+ } else {
+ mail_ping(vms_p->mailstream);
+ }
+ return 0;
+}
+static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
+{
+ char tmp[PATH_MAX] = "";
+ char *mailboxnc;
+ char *context;
+ char *mb;
+ char *cur;
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+
+ ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox_context))
+ return 0;
+
+ ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (strchr(mailbox_context, ',')) {
+ int tmpnew, tmpold;
+ ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+ mb = tmp;
+ while ((cur = strsep(&mb, ", "))) {
+ if (!ast_strlen_zero(cur)) {
+ if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+ return -1;
+ else {
+ if (newmsgs)
+ *newmsgs += tmpnew;
+ if (oldmsgs)
+ *oldmsgs += tmpold;
+ }
+ }
+ }
+ return 0;
+ }
+ if (context) {
+ *context = '\0';
+ mailboxnc = tmp;
+ context++;
+ } else {
+ context = "default";
+ mailboxnc = (char *)mailbox_context;
+ }
+ if (newmsgs) {
+ if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
+ return -1;
+ }
+ if (oldmsgs) {
+ if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+ char tmp[256], *tmp2, *mbox, *context;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ tmp2 = tmp;
+ if (strchr(tmp2, ',')) {
+ while ((mbox = strsep(&tmp2, ","))) {
+ if (!ast_strlen_zero(mbox)) {
+ if (has_voicemail(mbox, folder))
+ return 1;
+ }
+ }
+ }
+ if ((context= strchr(tmp, '@')))
+ *context++ = '\0';
+ else
+ context = "default";
+ return messagecount(context, tmp, folder) ? 1 : 0;
+}
+
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
+{
+ struct vm_state *sendvms = NULL, *destvms = NULL;
+ char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
+ if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
+ ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
+ return -1;
+ }
+ if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
+ ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
+ return -1;
+ }
+ snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
+ if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
+ return 0;
+ ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
+ return -1;
+}
+
+#endif
+#ifndef IMAP_STORAGE
+/* copy message only used by file storage */
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
+{
+ char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
+ const char *frombox = mbox(imbox);
+ int recipmsgnum;
+
+ ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
+
+ create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
+
+ if (!dir)
+ make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
+ else
+ ast_copy_string(fromdir, dir, sizeof(fromdir));
+
+ make_file(frompath, sizeof(frompath), fromdir, msgnum);
+ make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
+
+ if (vm_lock_path(todir))
+ return ERROR_LOCK_PATH;
+
+ recipmsgnum = last_message_index(recip, todir) + 1;
+ if (recipmsgnum < recip->maxmsg) {
+ make_file(topath, sizeof(topath), todir, recipmsgnum);
+ COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
+ } else {
+ ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
+ }
+ ast_unlock_path(todir);
+ notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
+
+ return 0;
+}
+#endif
+#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
+static int messagecount(const char *context, const char *mailbox, const char *folder)
+{
+ return __has_voicemail(context, mailbox, folder, 0);
+}
+
+
+static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
+{
+ DIR *dir;
+ struct dirent *de;
+ char fn[256];
+ int ret = 0;
+
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ if (ast_strlen_zero(folder))
+ folder = "INBOX";
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
+
+ if (!(dir = opendir(fn)))
+ return 0;
+
+ while ((de = readdir(dir))) {
+ if (!strncasecmp(de->d_name, "msg", 3)) {
+ if (shortcircuit) {
+ ret = 1;
+ break;
+ } else if (!strncasecmp(de->d_name + 8, "txt", 3))
+ ret++;
+ }
+ }
+
+ closedir(dir);
+
+ return ret;
+}
+
+
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+ char tmp[256], *tmp2 = tmp, *mbox, *context;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ while ((mbox = strsep(&tmp2, ","))) {
+ if ((context = strchr(mbox, '@')))
+ *context++ = '\0';
+ else
+ context = "default";
+ if (__has_voicemail(context, mbox, folder, 1))
+ return 1;
+ }
+ return 0;
+}
+
+
+static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
+{
+ char tmp[256];
+ char *context;
+
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+
+ if (strchr(mailbox, ',')) {
+ int tmpnew, tmpold;
+ char *mb, *cur;
+
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ mb = tmp;
+ while ((cur = strsep(&mb, ", "))) {
+ if (!ast_strlen_zero(cur)) {
+ if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+ return -1;
+ else {
+ if (newmsgs)
+ *newmsgs += tmpnew;
+ if (oldmsgs)
+ *oldmsgs += tmpold;
+ }
+ }
+ }
+ return 0;
+ }
+
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+
+ if ((context = strchr(tmp, '@')))
+ *context++ = '\0';
+ else
+ context = "default";
+
+ if (newmsgs)
+ *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
+ if (oldmsgs)
+ *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
+
+ return 0;
+}
+
+#endif
+
+static void run_externnotify(char *context, char *extension)
+{
+ char arguments[255];
+ char ext_context[256] = "";
+ int newvoicemails = 0, oldvoicemails = 0;
+ struct ast_smdi_mwi_message *mwi_msg;
+
+ if (!ast_strlen_zero(context))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
+ else
+ ast_copy_string(ext_context, extension, sizeof(ext_context));
+
+ if (smdi_iface) {
+ if (ast_app_has_voicemail(ext_context, NULL))
+ ast_smdi_mwi_set(smdi_iface, extension);
+ else
+ ast_smdi_mwi_unset(smdi_iface, extension);
+
+ if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
+ ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+ if (!strncmp(mwi_msg->cause, "INV", 3))
+ ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
+ else if (!strncmp(mwi_msg->cause, "BLK", 3))
+ ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
+ ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
+ ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+ } else {
+ ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+ }
+ }
+
+ if (!ast_strlen_zero(externnotify)) {
+ if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
+ ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
+ } else {
+ snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
+ ast_debug(1, "Executing %s\n", arguments);
+ ast_safe_system(arguments);
+ }
+ }
+}
+
+struct leave_vm_options {
+ unsigned int flags;
+ signed char record_gain;
+ char *exitcontext;
+};
+
+static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
+{
+#ifdef IMAP_STORAGE
+ int newmsgs, oldmsgs;
+ struct vm_state *vms = NULL;
+#endif
+ char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
+ char callerid[256];
+ FILE *txt;
+ char date[256];
+ int txtdes;
+ int res = 0;
+ int msgnum;
+ int duration = 0;
+ int ausemacro = 0;
+ int ousemacro = 0;
+ int ouseexten = 0;
+ int rtmsgid = 0;
+ char tmpid[16];
+ char tmpdur[16];
+ char priority[16];
+ char origtime[16];
+ char dir[PATH_MAX], tmpdir[PATH_MAX];
+ char fn[PATH_MAX];
+ char prefile[PATH_MAX] = "";
+ char tempfile[PATH_MAX] = "";
+ char ext_context[256] = "";
+ char fmt[80];
+ char *context;
+ char ecodes[17] = "#";
+ char tmp[1024] = "", *tmpptr;
+ struct ast_vm_user *vmu;
+ struct ast_vm_user svm;
+ const char *category = NULL, *code, *alldtmf = "0123456789ABCD*#";
+
+ ast_copy_string(tmp, ext, sizeof(tmp));
+ ext = tmp;
+ if ((context = strchr(tmp, '@'))) {
+ *context++ = '\0';
+ tmpptr = strchr(context, '&');
+ } else {
+ tmpptr = strchr(ext, '&');
+ }
+
+ if (tmpptr)
+ *tmpptr++ = '\0';
+
+ category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
+
+ ast_debug(3, "Before find_user\n");
+ if (!(vmu = find_user(&svm, context, ext))) {
+ ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return res;
+ }
+ /* Setup pre-file if appropriate */
+ if (strcmp(vmu->context, "default"))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
+ else
+ ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
+ if (ast_test_flag(options, OPT_BUSY_GREETING)) {
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
+ } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
+ }
+ snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
+ if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
+ ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
+ return -1;
+ }
+ RETRIEVE(tempfile, -1, ext, context);
+ if (ast_fileexists(tempfile, NULL, NULL) > 0)
+ ast_copy_string(prefile, tempfile, sizeof(prefile));
+ DISPOSE(tempfile, -1);
+ /* It's easier just to try to make it than to check for its existence */
+ create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
+
+ /* Check current or macro-calling context for special extensions */
+ if (ast_test_flag(vmu, VM_OPERATOR)) {
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ouseexten = 1;
+ }
+ } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ouseexten = 1;
+ }
+ else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ousemacro = 1;
+ }
+ }
+
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ ausemacro = 1;
+ }
+
+ if (ast_test_flag(options, OPT_DTMFEXIT)) {
+ for (code = alldtmf; *code; code++) {
+ char e[2] = "";
+ e[0] = *code;
+ if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
+ strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
+ }
+ }
+
+ /* Play the beginning intro if desired */
+ if (!ast_strlen_zero(prefile)) {
+#ifdef ODBC_STORAGE
+ int success =
+#endif
+ RETRIEVE(prefile, -1, ext, context);
+ if (ast_fileexists(prefile, NULL, NULL) > 0) {
+ if (ast_streamfile(chan, prefile, chan->language) > -1)
+ res = ast_waitstream(chan, ecodes);
+#ifdef ODBC_STORAGE
+ if (success == -1) {
+ /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
+ ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
+ store_file(prefile, vmu->mailbox, vmu->context, -1);
+ }
+#endif
+ } else {
+ ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
+ res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
+ }
+ DISPOSE(prefile, -1);
+ if (res < 0) {
+ ast_debug(1, "Hang up during prefile playback\n");
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return -1;
+ }
+ }
+ if (res == '#') {
+ /* On a '#' we skip the instructions */
+ ast_set_flag(options, OPT_SILENT);
+ res = 0;
+ }
+ if (!res && !ast_test_flag(options, OPT_SILENT)) {
+ res = ast_stream_and_wait(chan, INTRO, ecodes);
+ if (res == '#') {
+ ast_set_flag(options, OPT_SILENT);
+ res = 0;
+ }
+ }
+ if (res > 0)
+ ast_stopstream(chan);
+ /* Check for a '*' here in case the caller wants to escape from voicemail to something
+ other than the operator -- an automated attendant or mailbox login for example */
+ if (!ast_strlen_zero(vmu->exit) && (res == '*')) {
+ chan->exten[0] = 'a';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ chan->priority = 0;
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ return 0;
+ }
+
+ /* Check for a '0' here */
+ if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
+ transfer:
+ if (ouseexten || ousemacro) {
+ chan->exten[0] = 'o';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ ast_play_and_wait(chan, "transfer");
+ chan->priority = 0;
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ }
+ return 0;
+ }
+
+ /* Allow all other digits to exit Voicemail and return to the dialplan */
+ if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
+ if (!ast_strlen_zero(options->exitcontext))
+ ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ return res;
+ }
+
+ if (res < 0) {
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return -1;
+ }
+ /* The meat of recording the message... All the announcements and beeps have been played*/
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ if (!ast_strlen_zero(fmt)) {
+ msgnum = 0;
+
+#ifdef IMAP_STORAGE
+ /* Is ext a mailbox? */
+ /* must open stream for this user to get info! */
+ res = inboxcount(ext_context, &newmsgs, &oldmsgs);
+ if (res < 0) {
+ ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
+ return -1;
+ }
+ if (!(vms = get_vm_state_by_mailbox(ext,0))) {
+ /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
+ * rarely be used*/
+ if (!(vms = ast_calloc(1, sizeof(*vms)))) {
+ ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
+ return -1;
+ }
+ ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
+ ast_copy_string(vms->username, ext, sizeof(vms->username));
+ vms->mailstream = NIL;
+ ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
+ vms->updated=1;
+ ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
+ init_vm_state(vms);
+ vmstate_insert(vms);
+ vms = get_vm_state_by_mailbox(ext,0);
+ }
+ vms->newmessages++;
+
+ /* here is a big difference! We add one to it later */
+ msgnum = newmsgs + oldmsgs;
+ ast_debug(3, "Messagecount set to %d\n",msgnum);
+ snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
+ /* set variable for compatibility */
+ pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
+
+ /* Check if mailbox is full */
+ check_quota(vms, imapfolder);
+ if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
+ ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
+ ast_play_and_wait(chan, "vm-mailboxfull");
+ return -1;
+ }
+
+ /* Check if we have exceeded maxmsg */
+ if (msgnum >= vmu->maxmsg) {
+ ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
+ ast_play_and_wait(chan, "vm-mailboxfull");
+ return -1;
+ }
+
+#else
+ if (count_messages(vmu, dir) >= vmu->maxmsg) {
+ res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_log(LOG_WARNING, "No more messages possible\n");
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ goto leave_vm_out;
+ }
+
+#endif
+ snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
+ txtdes = mkstemp(tmptxtfile);
+ chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
+ if (txtdes < 0) {
+ res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ goto leave_vm_out;
+ }
+
+ /* Now play the beep once we have the message number for our next message. */
+ if (res >= 0) {
+ /* Unless we're *really* silent, try to send the beep */
+ res = ast_stream_and_wait(chan, "beep", "");
+ }
+
+ /* Store information in real-time storage */
+ if (ast_check_realtime("voicemail_data")) {
+ snprintf(priority, sizeof(priority), "%d", chan->priority);
+ snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
+ get_date(date, sizeof(date));
+ rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", category ? category : "", NULL);
+ }
+
+ /* Store information */
+ txt = fdopen(txtdes, "w+");
+ if (txt) {
+ get_date(date, sizeof(date));
+ fprintf(txt,
+ ";\n"
+ "; Message Information file\n"
+ ";\n"
+ "[message]\n"
+ "origmailbox=%s\n"
+ "context=%s\n"
+ "macrocontext=%s\n"
+ "exten=%s\n"
+ "priority=%d\n"
+ "callerchan=%s\n"
+ "callerid=%s\n"
+ "origdate=%s\n"
+ "origtime=%ld\n"
+ "category=%s\n",
+ ext,
+ chan->context,
+ chan->macrocontext,
+ chan->exten,
+ chan->priority,
+ chan->name,
+ ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
+ date, (long)time(NULL),
+ category ? category : "");
+ } else
+ ast_log(LOG_WARNING, "Error opening text file for output\n");
+#ifdef IMAP_STORAGE
+ res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
+#else
+ res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
+#endif
+
+ if (txt) {
+ if (duration < vmminsecs) {
+ fclose(txt);
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
+ ast_filedelete(tmptxtfile, NULL);
+ unlink(tmptxtfile);
+ if (ast_check_realtime("voicemail_data")) {
+ snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
+ ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
+ }
+ } else {
+ fprintf(txt, "duration=%d\n", duration);
+ fclose(txt);
+ if (vm_lock_path(dir)) {
+ ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
+ /* Delete files */
+ ast_filedelete(tmptxtfile, NULL);
+ unlink(tmptxtfile);
+ } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
+ ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
+ unlink(tmptxtfile);
+ ast_unlock_path(dir);
+ if (ast_check_realtime("voicemail_data")) {
+ snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
+ ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
+ }
+ } else {
+#ifndef IMAP_STORAGE
+ msgnum = last_message_index(vmu, dir) + 1;
+#endif
+ make_file(fn, sizeof(fn), dir, msgnum);
+
+ /* assign a variable with the name of the voicemail file */
+#ifndef IMAP_STORAGE
+ pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
+#else
+ pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
+#endif
+
+ snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
+ ast_filerename(tmptxtfile, fn, NULL);
+ rename(tmptxtfile, txtfile);
+
+ /* Properly set permissions on voicemail text descriptor file.
+ Unfortunately mkstemp() makes this file 0600 on most unix systems. */
+ if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
+ ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
+
+ ast_unlock_path(dir);
+ if (ast_check_realtime("voicemail_data")) {
+ snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
+ snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
+ ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
+ }
+ /* We must store the file first, before copying the message, because
+ * ODBC storage does the entire copy with SQL.
+ */
+ if (ast_fileexists(fn, NULL, NULL) > 0) {
+ STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
+ }
+
+ /* Are there to be more recipients of this message? */
+ while (tmpptr) {
+ struct ast_vm_user recipu, *recip;
+ char *exten, *context;
+
+ exten = strsep(&tmpptr, "&");
+ context = strchr(exten, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ }
+ if ((recip = find_user(&recipu, context, exten))) {
+ copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
+ free_user(recip);
+ }
+ }
+ /* Notification and disposal needs to happen after the copy, though. */
+ if (ast_fileexists(fn, NULL, NULL)) {
+ notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
+ DISPOSE(dir, msgnum);
+ }
+ }
+ }
+ }
+ if (res == '0') {
+ goto transfer;
+ } else if (res > 0)
+ res = 0;
+
+ if (duration < vmminsecs)
+ /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ else
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
+ } else
+ ast_log(LOG_WARNING, "No format for saving voicemail?\n");
+leave_vm_out:
+ free_user(vmu);
+
+ return res;
+}
+
+#ifndef IMAP_STORAGE
+static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
+{
+ /* we know max messages, so stop process when number is hit */
+
+ int x,dest;
+ char sfn[PATH_MAX];
+ char dfn[PATH_MAX];
+
+ if (vm_lock_path(dir))
+ return ERROR_LOCK_PATH;
+
+ for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
+ make_file(sfn, sizeof(sfn), dir, x);
+ if (EXISTS(dir, x, sfn, NULL)) {
+
+ if (x != dest) {
+ make_file(dfn, sizeof(dfn), dir, dest);
+ RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
+ }
+
+ dest++;
+ }
+ }
+ ast_unlock_path(dir);
+
+ return 0;
+}
+#endif
+
+static int say_and_wait(struct ast_channel *chan, int num, const char *language)
+{
+ int d;
+ d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
+ return d;
+}
+
+static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
+{
+#ifdef IMAP_STORAGE
+ /* we must use mbox(x) folder names, and copy the message there */
+ /* simple. huh? */
+ long res;
+ char sequence[10];
+
+ /* if save to Old folder, just leave in INBOX */
+ if (box == 1) return 10;
+ /* get the real IMAP message number for this message */
+ snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
+ ast_debug(3, "Copying sequence %s to mailbox %s\n",sequence,mbox(box));
+ res = mail_copy(vms->mailstream,sequence,(char *) mbox(box));
+ if (res == 1) return 0;
+ return 1;
+#else
+ char *dir = vms->curdir;
+ char *username = vms->username;
+ char *context = vmu->context;
+ char sfn[PATH_MAX];
+ char dfn[PATH_MAX];
+ char ddir[PATH_MAX];
+ const char *dbox = mbox(box);
+ int x, i;
+ create_dirpath(ddir, sizeof(ddir), context, username, dbox);
+
+ if (vm_lock_path(ddir))
+ return ERROR_LOCK_PATH;
+
+ x = last_message_index(vmu, ddir) + 1;
+
+ if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
+ x--;
+ for (i = 1; i <= x; i++) {
+ /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
+ make_file(sfn, sizeof(sfn), ddir, i);
+ make_file(dfn, sizeof(dfn), ddir, i - 1);
+ if (EXISTS(ddir, i, sfn, NULL)) {
+ RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
+ } else
+ break;
+ }
+ } else {
+ if (x >= vmu->maxmsg) {
+ ast_unlock_path(ddir);
+ return -1;
+ }
+ }
+ make_file(sfn, sizeof(sfn), dir, msg);
+ make_file(dfn, sizeof(dfn), ddir, x);
+ if (strcmp(sfn, dfn)) {
+ COPY(dir, msg, ddir, x, username, context, sfn, dfn);
+ }
+ ast_unlock_path(ddir);
+#endif
+ return 0;
+}
+
+static int adsi_logo(unsigned char *buf)
+{
+ int bytes = 0;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
+ return bytes;
+}
+
+static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ int x;
+ char num[5];
+
+ *useadsi = 0;
+ bytes += ast_adsi_data_mode(buf + bytes);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+ bytes = 0;
+ bytes += adsi_logo(buf);
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
+#ifdef DISPLAY
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
+#endif
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_data_mode(buf + bytes);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+ if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
+ bytes = 0;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return 0;
+ }
+
+#ifdef DISPLAY
+ /* Add a dot */
+ bytes = 0;
+ bytes += ast_adsi_logo(buf);
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+ bytes = 0;
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+#ifdef DISPLAY
+ /* Add another dot */
+ bytes = 0;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+
+ bytes = 0;
+ /* These buttons we load but don't use yet */
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+#ifdef DISPLAY
+ /* Add another dot */
+ bytes = 0;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+
+ bytes = 0;
+ for (x=0;x<5;x++) {
+ snprintf(num, sizeof(num), "%d", x);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
+ }
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+#ifdef DISPLAY
+ /* Add another dot */
+ bytes = 0;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#endif
+
+ if (ast_adsi_end_download(chan)) {
+ bytes = 0;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return 0;
+ }
+ bytes = 0;
+ bytes += ast_adsi_download_disconnect(buf + bytes);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+
+ ast_debug(1, "Done downloading scripts...\n");
+
+#ifdef DISPLAY
+ /* Add last dot */
+ bytes = 0;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+#endif
+ ast_debug(1, "Restarting session...\n");
+
+ bytes = 0;
+ /* Load the session now */
+ if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
+ *useadsi = 1;
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
+ } else
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return 0;
+}
+
+static void adsi_begin(struct ast_channel *chan, int *useadsi)
+{
+ int x;
+ if (!ast_adsi_available(chan))
+ return;
+ x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
+ if (x < 0)
+ return;
+ if (!x) {
+ if (adsi_load_vmail(chan, useadsi)) {
+ ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
+ return;
+ }
+ } else
+ *useadsi = 1;
+}
+
+static void adsi_login(struct ast_channel *chan)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+ if (!ast_adsi_available(chan))
+ return;
+
+ for (x=0;x<8;x++)
+ keys[x] = 0;
+ /* Set one key for next */
+ keys[3] = ADSI_KEY_APPS + 3;
+
+ bytes += adsi_logo(buf + bytes);
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
+ bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
+ bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
+ bytes += ast_adsi_set_keys(buf + bytes, keys);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_password(struct ast_channel *chan)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+ if (!ast_adsi_available(chan))
+ return;
+
+ for (x=0;x<8;x++)
+ keys[x] = 0;
+ /* Set one key for next */
+ keys[3] = ADSI_KEY_APPS + 3;
+
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
+ bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
+ bytes += ast_adsi_set_keys(buf + bytes, keys);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_folders(struct ast_channel *chan, int start, char *label)
+{
+ unsigned char buf[256];
+ int bytes=0;
+ unsigned char keys[8];
+ int x,y;
+
+ if (!ast_adsi_available(chan))
+ return;
+
+ for (x=0;x<5;x++) {
+ y = ADSI_KEY_APPS + 12 + start + x;
+ if (y > ADSI_KEY_APPS + 12 + 4)
+ y = 0;
+ keys[x] = ADSI_KEY_SKT | y;
+ }
+ keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
+ keys[6] = 0;
+ keys[7] = 0;
+
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_set_keys(buf + bytes, keys);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
+{
+ int bytes=0;
+ unsigned char buf[256];
+ char buf1[256], buf2[256];
+ char fn2[PATH_MAX];
+
+ char cid[256]="";
+ char *val;
+ char *name, *num;
+ char datetime[21]="";
+ FILE *f;
+
+ unsigned char keys[8];
+
+ int x;
+
+ if (!ast_adsi_available(chan))
+ return;
+
+ /* Retrieve important info */
+ snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
+ f = fopen(fn2, "r");
+ if (f) {
+ while (!feof(f)) {
+ fgets((char *)buf, sizeof(buf), f);
+ if (!feof(f)) {
+ char *stringp=NULL;
+ stringp = (char *)buf;
+ strsep(&stringp, "=");
+ val = strsep(&stringp, "=");
+ if (!ast_strlen_zero(val)) {
+ if (!strcmp((char *)buf, "callerid"))
+ ast_copy_string(cid, val, sizeof(cid));
+ if (!strcmp((char *)buf, "origdate"))
+ ast_copy_string(datetime, val, sizeof(datetime));
+ }
+ }
+ }
+ fclose(f);
+ }
+ /* New meaning for keys */
+ for (x=0;x<5;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
+ keys[6] = 0x0;
+ keys[7] = 0x0;
+
+ if (!vms->curmsg) {
+ /* No prev key, provide "Folder" instead */
+ keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ }
+ if (vms->curmsg >= vms->lastmsg) {
+ /* If last message ... */
+ if (vms->curmsg) {
+ /* but not only message, provide "Folder" instead */
+ keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ } else {
+ /* Otherwise if only message, leave blank */
+ keys[3] = 1;
+ }
+ }
+
+ if (!ast_strlen_zero(cid)) {
+ ast_callerid_parse(cid, &name, &num);
+ if (!name)
+ name = num;
+ } else
+ name = "Unknown Caller";
+
+ /* If deleted, show "undeleted" */
+
+ if (vms->deleted[vms->curmsg])
+ keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
+
+ /* Except "Exit" */
+ keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
+ snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
+ strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
+ snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
+
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_set_keys(buf + bytes, keys);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
+{
+ int bytes=0;
+ unsigned char buf[256];
+ unsigned char keys[8];
+
+ int x;
+
+ if (!ast_adsi_available(chan))
+ return;
+
+ /* New meaning for keys */
+ for (x=0;x<5;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
+
+ keys[6] = 0x0;
+ keys[7] = 0x0;
+
+ if (!vms->curmsg) {
+ /* No prev key, provide "Folder" instead */
+ keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ }
+ if (vms->curmsg >= vms->lastmsg) {
+ /* If last message ... */
+ if (vms->curmsg) {
+ /* but not only message, provide "Folder" instead */
+ keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
+ } else {
+ /* Otherwise if only message, leave blank */
+ keys[3] = 1;
+ }
+ }
+
+ /* If deleted, show "undeleted" */
+ if (vms->deleted[vms->curmsg])
+ keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
+
+ /* Except "Exit" */
+ keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
+ bytes += ast_adsi_set_keys(buf + bytes, keys);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
+{
+ unsigned char buf[256] = "";
+ char buf1[256] = "", buf2[256] = "";
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+
+ char *newm = (vms->newmessages == 1) ? "message" : "messages";
+ char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
+ if (!ast_adsi_available(chan))
+ return;
+ if (vms->newmessages) {
+ snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
+ if (vms->oldmessages) {
+ strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
+ snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
+ } else {
+ snprintf(buf2, sizeof(buf2), "%s.", newm);
+ }
+ } else if (vms->oldmessages) {
+ snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
+ snprintf(buf2, sizeof(buf2), "%s.", oldm);
+ } else {
+ strcpy(buf1, "You have no messages.");
+ buf2[0] = ' ';
+ buf2[1] = '\0';
+ }
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+
+ for (x=0;x<6;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
+ keys[6] = 0;
+ keys[7] = 0;
+
+ /* Don't let them listen if there are none */
+ if (vms->lastmsg < 0)
+ keys[0] = 1;
+ bytes += ast_adsi_set_keys(buf + bytes, keys);
+
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
+{
+ unsigned char buf[256] = "";
+ char buf1[256] = "", buf2[256] = "";
+ int bytes=0;
+ unsigned char keys[8];
+ int x;
+
+ char *mess = (vms->lastmsg == 0) ? "message" : "messages";
+
+ if (!ast_adsi_available(chan))
+ return;
+
+ /* Original command keys */
+ for (x=0;x<6;x++)
+ keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
+
+ keys[6] = 0;
+ keys[7] = 0;
+
+ if ((vms->lastmsg + 1) < 1)
+ keys[0] = 0;
+
+ snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
+ strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
+
+ if (vms->lastmsg + 1)
+ snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
+ else
+ strcpy(buf2, "no messages.");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_set_keys(buf + bytes, keys);
+
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+
+}
+
+/*
+static void adsi_clear(struct ast_channel *chan)
+{
+ char buf[256];
+ int bytes=0;
+ if (!ast_adsi_available(chan))
+ return;
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+*/
+
+static void adsi_goodbye(struct ast_channel *chan)
+{
+ unsigned char buf[256];
+ int bytes=0;
+
+ if (!ast_adsi_available(chan))
+ return;
+ bytes += adsi_logo(buf + bytes);
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+}
+
+/*--- get_folder: Folder menu ---*/
+/* Plays "press 1 for INBOX messages" etc
+ Should possibly be internationalized
+ */
+static int get_folder(struct ast_channel *chan, int start)
+{
+ int x;
+ int d;
+ char fn[PATH_MAX];
+ d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
+ if (d)
+ return d;
+ for (x = start; x< 5; x++) { /* For all folders */
+ if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
+ return d;
+ d = ast_play_and_wait(chan, "vm-for"); /* "for" */
+ if (d)
+ return d;
+ snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
+ d = vm_play_folder_name(chan, fn);
+ if (d)
+ return d;
+ d = ast_waitfordigit(chan, 500);
+ if (d)
+ return d;
+ }
+ d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
+ if (d)
+ return d;
+ d = ast_waitfordigit(chan, 4000);
+ return d;
+}
+
+static int get_folder2(struct ast_channel *chan, char *fn, int start)
+{
+ int res = 0;
+ res = ast_play_and_wait(chan, fn); /* Folder name */
+ while (((res < '0') || (res > '9')) &&
+ (res != '#') && (res >= 0)) {
+ res = get_folder(chan, 0);
+ }
+ return res;
+}
+
+static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
+ char *context, signed char record_gain, long *duration, struct vm_state *vms)
+{
+ int cmd = 0;
+ int retries = 0;
+ signed char zero_gain = 0;
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+
+ while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
+ if (cmd)
+ retries = 0;
+ switch (cmd) {
+ case '1':
+ /* prepend a message to the current message, update the metadata and return */
+ {
+ char msgfile[PATH_MAX];
+ char textfile[PATH_MAX];
+ int prepend_duration = 0;
+ struct ast_config *msg_cfg;
+ const char *duration_str;
+
+ make_file(msgfile, sizeof(msgfile), curdir, curmsg);
+ strcpy(textfile, msgfile);
+ strncat(textfile, ".txt", sizeof(textfile) - 1);
+ *duration = 0;
+
+ /* if we can't read the message metadata, stop now */
+ if (!(msg_cfg = ast_config_load(textfile, config_flags))) {
+ cmd = 0;
+ break;
+ }
+
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
+
+ cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
+
+
+ if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
+ *duration = atoi(duration_str);
+
+ if (prepend_duration) {
+ struct ast_category *msg_cat;
+ /* need enough space for a maximum-length message duration */
+ char duration_str[12];
+
+ *duration += prepend_duration;
+ msg_cat = ast_category_get(msg_cfg, "message");
+ snprintf(duration_str, 11, "%ld", *duration);
+ if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
+ config_text_file_save(textfile, msg_cfg, "app_voicemail");
+ STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, *duration, vms);
+ }
+ }
+
+ ast_config_destroy(msg_cfg);
+
+ break;
+ }
+ case '2':
+ cmd = 't';
+ break;
+ case '*':
+ cmd = '*';
+ break;
+ default:
+ cmd = ast_play_and_wait(chan,"vm-forwardoptions");
+ /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
+ if (!cmd)
+ cmd = ast_play_and_wait(chan,"vm-starmain");
+ /* "press star to return to the main menu" */
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,6000);
+ if (!cmd)
+ retries++;
+ if (retries > 3)
+ cmd = 't';
+ }
+ }
+ if (cmd == 't' || cmd == 'S')
+ cmd = 0;
+ return cmd;
+}
+
+static void queue_mwi_event(const char *mbox, int new, int old)
+{
+ struct ast_event *event;
+ char *mailbox, *context;
+
+ /* Strip off @default */
+ context = mailbox = ast_strdupa(mbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ if (!(event = ast_event_new(AST_EVENT_MWI,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, new,
+ AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
+ AST_EVENT_IE_END))) {
+ return;
+ }
+
+ ast_event_queue_and_cache(event,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
+ AST_EVENT_IE_END);
+}
+
+static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
+{
+ char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
+ int newmsgs = 0, oldmsgs = 0;
+ const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
+ char *myserveremail = serveremail;
+
+ make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
+ make_file(fn, sizeof(fn), todir, msgnum);
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
+
+ if (!ast_strlen_zero(vmu->attachfmt)) {
+ if (strstr(fmt, vmu->attachfmt))
+ fmt = vmu->attachfmt;
+ else
+ ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
+ }
+
+ /* Attach only the first format */
+ fmt = ast_strdupa(fmt);
+ stringp = fmt;
+ strsep(&stringp, "|");
+
+ if (!ast_strlen_zero(vmu->serveremail))
+ myserveremail = vmu->serveremail;
+
+ if (!ast_strlen_zero(vmu->email)) {
+ int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
+ if (!attach_user_voicemail)
+ attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
+
+ if (attach_user_voicemail)
+ RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
+
+ /*XXX possible imap issue, should category be NULL XXX*/
+ sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
+
+ if (attach_user_voicemail)
+ DISPOSE(todir, msgnum);
+ }
+
+ if (!ast_strlen_zero(vmu->pager))
+ sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
+
+ if (ast_test_flag(vmu, VM_DELETE))
+ DELETE(todir, msgnum, fn);
+
+#ifdef IMAP_STORAGE
+ DELETE(todir, msgnum, fn);
+#endif
+ /* Leave voicemail for someone */
+ if (ast_app_has_voicemail(ext_context, NULL))
+ ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
+
+ queue_mwi_event(ext_context, newmsgs, oldmsgs);
+
+ manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
+ run_externnotify(vmu->context, vmu->mailbox);
+ return 0;
+}
+
+static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int flag, signed char record_gain)
+{
+#ifdef IMAP_STORAGE
+ BODY *body;
+ char *header_content;
+ char *temp;
+ char todir[256];
+ int todircount=0;
+ struct vm_state *dstvms;
+#endif
+ char username[70]="";
+ char fn[PATH_MAX]; /* for playback of name greeting */
+ char ecodes[16] = "#";
+ int res = 0, cmd = 0;
+ struct ast_vm_user *receiver = NULL, *vmtmp;
+ AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
+ char *stringp;
+ const char *s;
+ int saved_messages = 0, found = 0;
+ int valid_extensions = 0;
+ char *dir;
+ int curmsg;
+
+ if (vms == NULL) return -1;
+ dir = vms->curdir;
+ curmsg = vms->curmsg;
+
+ while (!res && !valid_extensions) {
+ int use_directory = 0;
+ if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
+ int done = 0;
+ int retries = 0;
+ cmd=0;
+ while ((cmd >= 0) && !done ){
+ if (cmd)
+ retries = 0;
+ switch (cmd) {
+ case '1':
+ use_directory = 0;
+ done = 1;
+ break;
+ case '2':
+ use_directory = 1;
+ done=1;
+ break;
+ case '*':
+ cmd = 't';
+ done = 1;
+ break;
+ default:
+ /* Press 1 to enter an extension press 2 to use the directory */
+ cmd = ast_play_and_wait(chan,"vm-forward");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,3000);
+ if (!cmd)
+ retries++;
+ if (retries > 3)
+ {
+ cmd = 't';
+ done = 1;
+ }
+
+ }
+ }
+ if (cmd < 0 || cmd == 't')
+ break;
+ }
+
+ if (use_directory) {
+ /* use app_directory */
+
+ char old_context[sizeof(chan->context)];
+ char old_exten[sizeof(chan->exten)];
+ int old_priority;
+ struct ast_app* app;
+
+
+ app = pbx_findapp("Directory");
+ if (app) {
+ char vmcontext[256];
+ /* make backup copies */
+ memcpy(old_context, chan->context, sizeof(chan->context));
+ memcpy(old_exten, chan->exten, sizeof(chan->exten));
+ old_priority = chan->priority;
+
+ /* call the the Directory, changes the channel */
+ snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
+ res = pbx_exec(chan, app, vmcontext);
+
+ ast_copy_string(username, chan->exten, sizeof(username));
+
+ /* restore the old context, exten, and priority */
+ memcpy(chan->context, old_context, sizeof(chan->context));
+ memcpy(chan->exten, old_exten, sizeof(chan->exten));
+ chan->priority = old_priority;
+
+ } else {
+ ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
+ ast_clear_flag((&globalflags), VM_DIRECFORWARD);
+ }
+ } else {
+ /* Ask for an extension */
+ res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
+ if (res)
+ break;
+ if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
+ break;
+ }
+
+ /* start all over if no username */
+ if (ast_strlen_zero(username))
+ continue;
+ stringp = username;
+ s = strsep(&stringp, "*");
+ /* start optimistic */
+ valid_extensions = 1;
+ while (s) {
+ /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1). find_user is going to malloc since we have a NULL as first argument */
+ if ((flag == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
+ AST_LIST_INSERT_HEAD(&extensions, receiver, list);
+ found++;
+ } else {
+ valid_extensions = 0;
+ break;
+ }
+
+ /* play name if available, else play extension number */
+ snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
+ RETRIEVE(fn, -1, s, receiver->context);
+ if (ast_fileexists(fn, NULL, NULL) > 0) {
+ res = ast_stream_and_wait(chan, fn, ecodes);
+ if (res) {
+ DISPOSE(fn, -1);
+ return res;
+ }
+ } else {
+ /* Dispose just in case */
+ DISPOSE(fn, -1);
+ res = ast_say_digit_str(chan, s, ecodes, chan->language);
+ }
+
+ s = strsep(&stringp, "*");
+ }
+ /* break from the loop of reading the extensions */
+ if (valid_extensions)
+ break;
+ /* "I am sorry, that's not a valid extension. Please try again." */
+ res = ast_play_and_wait(chan, "pbx-invalid");
+ }
+ /* check if we're clear to proceed */
+ if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
+ return res;
+ if (flag==1) {
+ struct leave_vm_options leave_options;
+ char mailbox[AST_MAX_EXTENSION * 2 + 2];
+ snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
+
+ /* Send VoiceMail */
+ memset(&leave_options, 0, sizeof(leave_options));
+ leave_options.record_gain = record_gain;
+ cmd = leave_voicemail(chan, mailbox, &leave_options);
+ } else {
+
+ /* Forward VoiceMail */
+ long duration = 0;
+#ifdef IMAP_STORAGE
+ char *myserveremail = serveremail;
+ char buf[1024] = "";
+ int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
+#endif
+ RETRIEVE(dir, curmsg, sender->mailbox, context);
+ cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, vms);
+ if (!cmd) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
+#ifdef IMAP_STORAGE
+ /* Need to get message content */
+ ast_debug(3,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
+ if (!vms->msgArray[vms->curmsg]) {
+ ast_log (LOG_WARNING,"Trying to access unknown message\n");
+ return -1;
+ }
+
+ /* This will only work for new messages... */
+ header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
+ /* empty string means no valid header */
+ if (ast_strlen_zero(header_content)) {
+ ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
+ return -1;
+ }
+ /* Get header info needed by sendmail */
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))))
+ duration = atoi(temp);
+ else
+ duration = 0;
+
+ /* Attach only the first format */
+ if ((fmt = ast_strdupa(fmt))) {
+ stringp = fmt;
+ strsep(&stringp, "|");
+ } else {
+ ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
+ fmt = "WAV";
+ }
+ if (!strcasecmp(fmt, "wav49"))
+ fmt = "WAV";
+ ast_debug(3,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
+ /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
+ /* if (!ast_strlen_zero(fmt)) { */
+ snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
+ make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
+ ast_debug(3,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
+ /*mail_fetchstructure (mailstream, vmArray[0], &body); */
+ mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
+ save_body(body,vms,"3","gsm");
+ /* should not assume "fmt" here! */
+ save_body(body,vms,"2",fmt);
+
+ /* get destination mailbox */
+ dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
+ if (dstvms) {
+ init_mailstream(dstvms, 0);
+ if (!dstvms->mailstream) {
+ ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
+ } else {
+ STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
+ run_externnotify(vmtmp->context, vmtmp->mailbox);
+ }
+ } else {
+ ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
+ }
+
+ if (!ast_strlen_zero(vmtmp->serveremail))
+ myserveremail = vmtmp->serveremail;
+ attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
+ /* NULL category for IMAP storage */
+ sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
+
+#else
+ copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
+#endif
+ saved_messages++;
+ AST_LIST_REMOVE_CURRENT(list);
+ free_user(vmtmp);
+ if (res)
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ if (saved_messages > 0) {
+ /* give confirmation that the message was saved */
+ /* commented out since we can't forward batches yet
+ if (saved_messages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-saved"); */
+ res = ast_play_and_wait(chan, "vm-msgsaved");
+ }
+ }
+ }
+
+ /* If anything failed above, we still have this list to free */
+ while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
+ free_user(vmtmp);
+ return res ? res : cmd;
+}
+
+static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
+{
+ int res;
+ if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
+ ast_log(LOG_WARNING, "Unable to play message %s\n", file);
+ return res;
+}
+
+static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
+{
+ return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
+}
+
+static int play_message_category(struct ast_channel *chan, const char *category)
+{
+ int res = 0;
+
+ if (!ast_strlen_zero(category))
+ res = ast_play_and_wait(chan, category);
+
+ if (res) {
+ ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
+ res = 0;
+ }
+
+ return res;
+}
+
+static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
+{
+ int res = 0;
+ struct vm_zone *the_zone = NULL;
+ time_t t;
+
+ if (ast_get_time_t(origtime, &t, 0, NULL)) {
+ ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
+ return 0;
+ }
+
+ /* Does this user have a timezone specified? */
+ if (!ast_strlen_zero(vmu->zonetag)) {
+ /* Find the zone in the list */
+ struct vm_zone *z;
+ AST_LIST_LOCK(&zones);
+ AST_LIST_TRAVERSE(&zones, z, list) {
+ if (!strcmp(z->name, vmu->zonetag)) {
+ the_zone = z;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&zones);
+ }
+
+/* No internal variable parsing for now, so we'll comment it out for the time being */
+#if 0
+ /* Set the DIFF_* variables */
+ ast_localtime(&t, &time_now, NULL);
+ tv_now = ast_tvnow();
+ ast_localtime(&tv_now, &time_then, NULL);
+
+ /* Day difference */
+ if (time_now.tm_year == time_then.tm_year)
+ snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
+ else
+ snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
+ pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
+
+ /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
+#endif
+ if (the_zone)
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
+ else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
+ else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
+ else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
+ else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
+ else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
+ else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
+ else if (!strcasecmp(chan->language,"gr"))
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
+ else if (!strcasecmp(chan->language,"pt_BR"))
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
+ else if (!strcasecmp(chan->language,"tw")) /* CHINESE (Taiwan) syntax */
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
+ else
+ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
+#if 0
+ pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
+#endif
+ return res;
+}
+
+
+
+static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
+{
+ int res = 0;
+ int i;
+ char *callerid, *name;
+ char prefile[PATH_MAX] = "";
+
+
+ /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
+ /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
+ if ((cid == NULL)||(context == NULL))
+ return res;
+
+ /* Strip off caller ID number from name */
+ ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
+ ast_callerid_parse(cid, &name, &callerid);
+ if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
+ /* Check for internal contexts and only */
+ /* say extension when the call didn't come from an internal context in the list */
+ for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
+ ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
+ if ((strcmp(cidinternalcontexts[i], context) == 0))
+ break;
+ }
+ if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
+ if (!res) {
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
+ if (!ast_strlen_zero(prefile)) {
+ /* See if we can find a recorded name for this person instead of their extension number */
+ if (ast_fileexists(prefile, NULL, NULL) > 0) {
+ ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
+ if (!callback)
+ res = wait_file2(chan, vms, "vm-from");
+ res = ast_stream_and_wait(chan, prefile, "");
+ } else {
+ ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
+ /* BB: Say "from extension" as one saying to sound smoother */
+ if (!callback)
+ res = wait_file2(chan, vms, "vm-from-extension");
+ res = ast_say_digit_str(chan, callerid, "", chan->language);
+ }
+ }
+ }
+ }
+
+ else if (!res) {
+ ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
+ /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
+ if (!callback)
+ res = wait_file2(chan, vms, "vm-from-phonenumber");
+ res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
+ }
+ } else {
+ /* Number unknown */
+ ast_debug(1, "VM-CID: From an unknown number\n");
+ /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
+ res = wait_file2(chan, vms, "vm-unknown-caller");
+ }
+ return res;
+}
+
+static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
+{
+ int res = 0;
+ int durationm;
+ int durations;
+ /* Verify that we have a duration for the message */
+ if (duration == NULL)
+ return res;
+
+ /* Convert from seconds to minutes */
+ durations=atoi(duration);
+ durationm=(durations / 60);
+
+ ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
+
+ if ((!res) && (durationm >= minduration)) {
+ res = wait_file2(chan, vms, "vm-duration");
+
+ /* POLISH syntax */
+ if (!strcasecmp(chan->language, "pl")) {
+ div_t num = div(durationm, 10);
+
+ if (durationm == 1) {
+ res = ast_play_and_wait(chan, "digits/1z");
+ res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
+ } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
+ if (num.rem == 2) {
+ if (!num.quot) {
+ res = ast_play_and_wait(chan, "digits/2-ie");
+ } else {
+ res = say_and_wait(chan, durationm - 2 , chan->language);
+ res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
+ }
+ } else {
+ res = say_and_wait(chan, durationm, chan->language);
+ }
+ res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
+ } else {
+ res = say_and_wait(chan, durationm, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
+ }
+ /* DEFAULT syntax */
+ } else {
+ res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
+ res = wait_file2(chan, vms, "vm-minutes");
+ }
+ }
+ return res;
+}
+
+#ifdef IMAP_STORAGE
+static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
+{
+ BODY *body;
+ char *header_content;
+ char cid[256], cidN[256];
+ char context[256];
+ char origtime[32];
+ char duration[16];
+ char category[32];
+ char todir[PATH_MAX];
+ int res = 0;
+ char *attachedfilefmt;
+ char *temp;
+ char buf[1024];
+
+ vms->starting = 0;
+ ast_debug(3,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
+
+ if (!vms->msgArray[vms->curmsg]) {
+ ast_log (LOG_WARNING,"Trying to access unknown message\n");
+ return -1;
+ }
+
+ /* This will only work for new messages... */
+ header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
+ /* empty string means no valid header */
+ if (ast_strlen_zero(header_content)) {
+ ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
+ return -1;
+ }
+ snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
+ make_gsm_file(vms->fn, sizeof(vms->fn), vms->imapuser, todir, vms->curmsg);
+
+ mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
+
+ /* We have the body, now we extract the file name of the first attachment. */
+ if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
+ attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
+ } else {
+ ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
+ return -1;
+ }
+
+
+ /* Find the format of the attached file */
+
+ strsep(&attachedfilefmt, ".");
+ if (!attachedfilefmt) {
+ ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
+ return -1;
+ }
+ save_body(body, vms, "2", attachedfilefmt);
+
+ adsi_message(chan, vms);
+ if (!vms->curmsg)
+ res = wait_file2(chan, vms, "vm-first"); /* "First" */
+ else if (vms->curmsg == vms->lastmsg)
+ res = wait_file2(chan, vms, "vm-last"); /* "last" */
+
+ if (!res) {
+ res = wait_file2(chan, vms, "vm-message"); /* "message" */
+ if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
+ if (!res)
+ res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
+ }
+ }
+
+ /* Get info from headers!! */
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))))
+ ast_copy_string(cidN, temp, sizeof(cidN));
+ else
+ cidN[0] = '\0';
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))))
+ snprintf(cid, sizeof(cid), "\"%s\" <%s>", cidN, temp);
+ else
+ cid[0] = '\0';
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))))
+ ast_copy_string(context, temp, sizeof(context));
+ else
+ context[0] = '\0';
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))))
+ ast_copy_string(origtime, temp, sizeof(origtime));
+ else
+ origtime[0] = '\0';
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))))
+ ast_copy_string(duration,temp, sizeof(duration));
+ else
+ duration[0] = '\0';
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))))
+ ast_copy_string(category,temp, sizeof(category));
+ else
+ category[0] = '\0';
+
+ /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
+ /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
+ if (res == '1')
+ res = 0;
+
+ if ((!res) && !ast_strlen_zero(category)) {
+ res = play_message_category(chan, category);
+ }
+
+ if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
+ res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
+ if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
+ res = play_message_callerid(chan, vms, cid, context, 0);
+
+ if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
+ res = play_message_duration(chan, vms, duration, vmu->saydurationm);
+
+ /* Allow pressing '1' to skip envelope / callerid */
+ /* if (res == '1')
+ res = 0;
+ */
+ /*ast_config_destroy(msg_cfg);*/
+ res = 0;
+
+ if (!res) {
+ vms->heard[vms->curmsg] = 1;
+ res = wait_file(chan, vms, vms->fn);
+ }
+ DISPOSE(vms->curdir, vms->curmsg);
+ DELETE(0, 0, vms->fn);
+ return res;
+}
+#else
+static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
+{
+ int res = 0;
+ char filename[256], *cid;
+ const char *origtime, *context, *category, *duration;
+ struct ast_config *msg_cfg;
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+
+ vms->starting = 0;
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
+ adsi_message(chan, vms);
+ if (!vms->curmsg)
+ res = wait_file2(chan, vms, "vm-first"); /* "First" */
+ else if (vms->curmsg == vms->lastmsg)
+ res = wait_file2(chan, vms, "vm-last"); /* "last" */
+ if (!res) {
+ /* POLISH syntax */
+ if (!strcasecmp(chan->language, "pl")) {
+ if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
+ int ten, one;
+ char nextmsg[256];
+ ten = (vms->curmsg + 1) / 10;
+ one = (vms->curmsg + 1) % 10;
+
+ if (vms->curmsg < 20) {
+ snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
+ res = wait_file2(chan, vms, nextmsg);
+ } else {
+ snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
+ res = wait_file2(chan, vms, nextmsg);
+ if (one > 0) {
+ if (!res) {
+ snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
+ res = wait_file2(chan, vms, nextmsg);
+ }
+ }
+ }
+ }
+ if (!res)
+ res = wait_file2(chan, vms, "vm-message");
+ } else {
+ if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
+ res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
+ else /* DEFAULT syntax */
+ res = wait_file2(chan, vms, "vm-message");
+ if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
+ if (!res)
+ res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
+ }
+ }
+ }
+
+ /* Retrieve info from VM attribute file */
+ make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+ snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
+ RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
+ msg_cfg = ast_config_load(filename, config_flags);
+ if (!msg_cfg) {
+ ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
+ return 0;
+ }
+
+ if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
+ ast_log(LOG_WARNING, "No origtime?!\n");
+ DISPOSE(vms->curdir, vms->curmsg);
+ ast_config_destroy(msg_cfg);
+ return 0;
+ }
+
+ cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
+ duration = ast_variable_retrieve(msg_cfg, "message", "duration");
+ category = ast_variable_retrieve(msg_cfg, "message", "category");
+
+ context = ast_variable_retrieve(msg_cfg, "message", "context");
+ if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
+ context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
+ if (!res)
+ res = play_message_category(chan, category);
+ if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
+ res = play_message_datetime(chan, vmu, origtime, filename);
+ if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
+ res = play_message_callerid(chan, vms, cid, context, 0);
+ if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
+ res = play_message_duration(chan, vms, duration, vmu->saydurationm);
+ /* Allow pressing '1' to skip envelope / callerid */
+ if (res == '1')
+ res = 0;
+ ast_config_destroy(msg_cfg);
+
+ if (!res) {
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
+ vms->heard[vms->curmsg] = 1;
+ if ((res = wait_file(chan, vms, vms->fn)) < 0) {
+ ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
+ res = 0;
+ }
+ }
+ DISPOSE(vms->curdir, vms->curmsg);
+ return res;
+}
+#endif
+
+#ifdef IMAP_STORAGE
+static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
+{
+ char tmp[256], *t = tmp;
+ size_t left = sizeof(tmp);
+
+ if (box == OLD_FOLDER) {
+ ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
+ snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
+ } else {
+ ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
+ snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
+ }
+
+ /* Build up server information */
+ ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
+
+ /* Add authentication user if present */
+ if (!ast_strlen_zero(authuser))
+ ast_build_string(&t, &left, "/authuser=%s", authuser);
+
+ /* Add flags if present */
+ if (!ast_strlen_zero(imapflags))
+ ast_build_string(&t, &left, "/%s", imapflags);
+
+ /* End with username */
+ ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
+
+ if (box == NEW_FOLDER || box == OLD_FOLDER)
+ snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
+ else if (box == GREETINGS_FOLDER)
+ sprintf(spec, "%s%s", tmp, greetingfolder);
+ else
+ snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
+}
+
+static int init_mailstream(struct vm_state *vms, int box)
+{
+ MAILSTREAM *stream = NIL;
+ long debug;
+ char tmp[256];
+
+ if (!vms) {
+ ast_log (LOG_ERROR,"vm_state is NULL!\n");
+ return -1;
+ }
+ ast_debug(3,"vm_state user is:%s\n",vms->imapuser);
+ if (vms->mailstream == NIL || !vms->mailstream) {
+ ast_debug(1,"mailstream not set.\n");
+ } else {
+ stream = vms->mailstream;
+ }
+ /* debug = T; user wants protocol telemetry? */
+ debug = NIL; /* NO protocol telemetry? */
+
+ if (delimiter == '\0') { /* did not probe the server yet */
+ char *cp;
+#ifdef USE_SYSTEM_IMAP
+#include <imap/linkage.c>
+#else
+#include "linkage.c"
+#endif
+ /* Connect to mailbox to get mailstream so we can get delimiter */
+ imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
+ stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+ if (stream == NIL) {
+ ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
+ return -1;
+ }
+ get_mailbox_delimiter(stream);
+ /* update delimiter in imapfolder */
+ for (cp = imapfolder; *cp; cp++) {
+ if (*cp == '/')
+ *cp = delimiter;
+ }
+ }
+ /* Now connect to the target folder */
+ imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
+ ast_debug(3,"Before mail_open, server: %s, box:%d\n", tmp, box);
+ vms->mailstream = mail_open(stream, tmp, debug ? OP_DEBUG : NIL);
+ if (vms->mailstream == NIL) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
+{
+ SEARCHPGM *pgm;
+ SEARCHHEADER *hdr;
+ int ret;
+
+ ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
+ ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
+
+ if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
+ ast_log (LOG_ERROR,"Could not initialize mailstream\n");
+ return -1;
+ }
+
+ /* Check Quota */
+ if (box == 0) {
+ ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
+ check_quota(vms,(char *)mbox(box));
+ }
+
+ pgm = mail_newsearchpgm();
+
+ /* Check IMAP folder for Asterisk messages only... */
+ hdr = mail_newsearchheader("X-Asterisk-VM-Extension", vmu->mailbox);
+ pgm->header = hdr;
+ pgm->deleted = 0;
+ pgm->undeleted = 1;
+
+ /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
+ if (box == NEW_FOLDER) {
+ pgm->unseen = 1;
+ pgm->seen = 0;
+ } else if (box == OLD_FOLDER) {
+ pgm->seen = 1;
+ pgm->unseen = 0;
+ }
+
+ ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
+
+ vms->vmArrayIndex = 0;
+ mail_search_full (vms->mailstream, NULL, pgm, NIL);
+ vms->lastmsg = vms->vmArrayIndex - 1;
+ mail_free_searchpgm(&pgm);
+
+ return 0;
+}
+
+static int imap_remove_file(char *dir, int msgnum)
+{
+ char fn[PATH_MAX];
+ char full_fn[PATH_MAX];
+ char msgnums[80];
+
+ if (msgnum > -1) {
+ snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+ make_file(fn, sizeof(fn), dir, msgnum);
+ } else
+ ast_copy_string(fn, dir, sizeof(fn));
+
+ if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
+ ast_filedelete(fn, NULL);
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ unlink(full_fn);
+ }
+ return 0;
+}
+
+static int imap_retrieve_file (char *dir, int msgnum, const char *mailbox, char *context)
+{
+ struct ast_vm_user *vmu;
+ struct vm_state *vms_p;
+ char *file, *filename;
+ char *attachment;
+ /*char *mb, *cur;*/
+ int ret = 0, i;
+ BODY *body;
+
+ /* This function is only used for retrieval of IMAP greetings
+ * regular messages are not retrieved this way, nor are greetings
+ * if they are stored locally*/
+ if (msgnum > -1 || !imapgreetings) {
+ return 0;
+ } else {
+ file = strrchr(ast_strdupa(dir), '/');
+ if (file)
+ *file++ = '\0';
+ else {
+ ast_debug (1, "Failed to procure file name from directory passed.\n");
+ return -1;
+ }
+ }
+ /* We have to get the user before we can open the stream! */
+ /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
+ vmu = find_user(NULL, context, mailbox);
+ if (!vmu) {
+ ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
+ return -1;
+ } else {
+ /* No IMAP account available */
+ if (vmu->imapuser[0] == '\0') {
+ ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
+ free_user(vmu);
+ return -1;
+ }
+ }
+
+ /* check if someone is accessing this box right now... */
+ vms_p = get_vm_state_by_mailbox(mailbox,0);
+ if (!vms_p) {
+ ast_log(LOG_ERROR, "Voicemail state not found!\n");
+ return -1;
+ }
+
+ ret = init_mailstream(vms_p, GREETINGS_FOLDER);
+ if (!vms_p->mailstream) {
+ ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
+ free_user(vmu);
+ return -1;
+ }
+
+ for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
+ mail_fetchstructure(vms_p->mailstream, i + 1, &body);
+ /* We have the body, now we extract the file name of the first attachment. */
+ if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
+ attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
+ } else {
+ ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
+ return -1;
+ }
+ filename = strsep(&attachment, ".");
+ if (!strcmp(filename, file)) {
+ ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
+ vms_p->msgArray[vms_p->curmsg] = i + 1;
+ save_body(body, vms_p, "2", attachment);
+ free_user(vmu);
+ return 0;
+ }
+ }
+
+ free_user(vmu);
+ return -1;
+}
+
+static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
+{
+ char *file, *filename;
+ char *attachment;
+ char arg[10];
+ int i;
+ BODY* body;
+
+
+ file = strrchr(ast_strdupa(dir), '/');
+ if (file)
+ *file++ = '\0';
+ else {
+ ast_log (LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
+ return -1;
+ }
+
+ for (i = 0; i < vms->mailstream->nmsgs; i++) {
+ mail_fetchstructure(vms->mailstream, i + 1, &body);
+ /* We have the body, now we extract the file name of the first attachment. */
+ if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
+ attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
+ } else {
+ ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
+ return -1;
+ }
+ filename = strsep(&attachment, ".");
+ if (!strcmp(filename, file)) {
+ sprintf (arg,"%d", i+1);
+ mail_setflag (vms->mailstream,arg,"\\DELETED");
+ }
+ }
+ mail_expunge(vms->mailstream);
+ return 0;
+}
+
+#else
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
+{
+ int res = 0;
+ int count_msg, last_msg;
+
+ ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
+
+ /* Rename the member vmbox HERE so that we don't try to return before
+ * we know what's going on.
+ */
+ snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
+
+ /* Faster to make the directory than to check if it exists. */
+ create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
+
+ count_msg = count_messages(vmu, vms->curdir);
+ if (count_msg < 0)
+ return count_msg;
+ else
+ vms->lastmsg = count_msg - 1;
+
+ /*
+ The following test is needed in case sequencing gets messed up.
+ There appears to be more than one way to mess up sequence, so
+ we will not try to find all of the root causes--just fix it when
+ detected.
+ */
+
+ if (vm_lock_path(vms->curdir)) {
+ ast_log(LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
+ return -1;
+ }
+
+ last_msg = last_message_index(vmu, vms->curdir);
+ ast_unlock_path(vms->curdir);
+
+ if (last_msg < 0)
+ return last_msg;
+ else if (vms->lastmsg != last_msg)
+ {
+ ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
+ res = resequence_mailbox(vmu, vms->curdir);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+#endif
+
+static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int x = 0;
+#ifndef IMAP_STORAGE
+ int res = 0, nummsg;
+#endif
+
+ if (vms->lastmsg <= -1)
+ goto done;
+
+ vms->curmsg = -1;
+#ifndef IMAP_STORAGE
+ /* Get the deleted messages fixed */
+ if (vm_lock_path(vms->curdir))
+ return ERROR_LOCK_PATH;
+
+ for (x = 0; x < vmu->maxmsg; x++) {
+ if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
+ /* Save this message. It's not in INBOX or hasn't been heard */
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
+ if (!EXISTS(vms->curdir, x, vms->fn, NULL))
+ break;
+ vms->curmsg++;
+ make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+ if (strcmp(vms->fn, vms->fn2)) {
+ RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
+ }
+ } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
+ /* Move to old folder before deleting */
+ res = save_to_folder(vmu, vms, x, 1);
+ if (res == ERROR_LOCK_PATH) {
+ /* If save failed do not delete the message */
+ vms->deleted[x] = 0;
+ vms->heard[x] = 0;
+ --x;
+ }
+ } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
+ /* Move to deleted folder */
+ res = save_to_folder(vmu, vms, x, 10);
+ if (res == ERROR_LOCK_PATH) {
+ /* If save failed do not delete the message */
+ vms->deleted[x] = 0;
+ vms->heard[x] = 0;
+ --x;
+ }
+ } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
+ /* If realtime storage enabled - we should explicitly delete this message,
+ cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
+ if (EXISTS(vms->curdir, x, vms->fn, NULL))
+ DELETE(vms->curdir, x, vms->fn);
+ }
+ }
+
+ /* Delete ALL remaining messages */
+ nummsg = x - 1;
+ for (x = vms->curmsg + 1; x <= nummsg; x++) {
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
+ if (EXISTS(vms->curdir, x, vms->fn, NULL))
+ DELETE(vms->curdir, x, vms->fn);
+ }
+ ast_unlock_path(vms->curdir);
+#else
+ if (vms->deleted) {
+ for (x=0;x < vmu->maxmsg;x++) {
+ if (vms->deleted[x]) {
+ ast_debug(3,"IMAP delete of %d\n",x);
+ IMAP_DELETE(vms->curdir, x, vms->fn, vms);
+ }
+ }
+ }
+#endif
+
+done:
+ if (vms->deleted)
+ memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
+ if (vms->heard)
+ memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
+
+ return 0;
+}
+
+/* In Greek even though we CAN use a syntax like "friends messages"
+ * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
+ * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
+ * syntax for the above three categories which is more elegant.
+ */
+
+static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
+{
+ int cmd;
+ char *buf;
+
+ buf = alloca(strlen(mbox)+2);
+ strcpy(buf, mbox);
+ strcat(buf,"s");
+
+ if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
+ cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
+ return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
+ return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
+ }
+}
+
+static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
+{
+ int cmd;
+
+ if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
+ if (!strcasecmp(mbox, "vm-INBOX"))
+ cmd = ast_play_and_wait(chan, "vm-new-e");
+ else
+ cmd = ast_play_and_wait(chan, "vm-old-e");
+ return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ return cmd ? cmd : ast_play_and_wait(chan, mbox);
+ }
+}
+
+static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
+{
+ int cmd;
+
+ if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ return cmd ? cmd : ast_play_and_wait(chan, mbox);
+ } else {
+ cmd = ast_play_and_wait(chan, mbox);
+ return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
+ }
+}
+
+static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
+{
+ int cmd;
+
+ if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
+ cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
+ return cmd ? cmd : ast_play_and_wait(chan, mbox);
+ } else if (!strcasecmp(chan->language, "gr")){
+ return vm_play_folder_name_gr(chan, mbox);
+ } else if (!strcasecmp(chan->language, "pl")){
+ return vm_play_folder_name_pl(chan, mbox);
+ } else if (!strcasecmp(chan->language, "ua")){ /* Ukrainian syntax */
+ return vm_play_folder_name_ua(chan, mbox);
+ } else { /* Default English */
+ cmd = ast_play_and_wait(chan, mbox);
+ return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
+ }
+}
+
+/* GREEK SYNTAX
+ In greek the plural for old/new is
+ different so we need the following files
+ We also need vm-denExeteMynhmata because
+ this syntax is different.
+
+ -> vm-Olds.wav : "Palia"
+ -> vm-INBOXs.wav : "Nea"
+ -> vm-denExeteMynhmata : "den exete mynhmata"
+*/
+
+
+static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
+{
+ int res = 0;
+
+ if (vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res)
+ res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
+ if (!res) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ } else if (vms->oldmessages){
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res)
+ res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
+ if ((vms->oldmessages == 1)){
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = ast_play_and_wait(chan, "vm-Olds");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ } else if (!vms->oldmessages && !vms->newmessages)
+ res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
+ return res;
+}
+
+/* Default English syntax */
+static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
+{
+ int res;
+
+ /* Introduce messages they have */
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* ITALIAN syntax */
+static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ if (!vms->oldmessages && !vms->newmessages)
+ res = ast_play_and_wait(chan, "vm-no") ||
+ ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res && vms->newmessages) {
+ res = (vms->newmessages == 1) ?
+ ast_play_and_wait(chan, "digits/un") ||
+ ast_play_and_wait(chan, "vm-nuovo") ||
+ ast_play_and_wait(chan, "vm-message") :
+ /* 2 or more new messages */
+ say_and_wait(chan, vms->newmessages, chan->language) ||
+ ast_play_and_wait(chan, "vm-nuovi") ||
+ ast_play_and_wait(chan, "vm-messages");
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ res = (vms->oldmessages == 1) ?
+ ast_play_and_wait(chan, "digits/un") ||
+ ast_play_and_wait(chan, "vm-vecchio") ||
+ ast_play_and_wait(chan, "vm-message") :
+ /* 2 or more old messages */
+ say_and_wait(chan, vms->oldmessages, chan->language) ||
+ ast_play_and_wait(chan, "vm-vecchi") ||
+ ast_play_and_wait(chan, "vm-messages");
+ }
+ return res ? -1 : 0;
+}
+
+/* POLISH syntax */
+static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ div_t num;
+
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ return res;
+ } else {
+ res = ast_play_and_wait(chan, "vm-youhave");
+ }
+
+ if (vms->newmessages) {
+ num = div(vms->newmessages, 10);
+ if (vms->newmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/1-a");
+ res = res ? res : ast_play_and_wait(chan, "vm-new-a");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
+ if (num.rem == 2) {
+ if (!num.quot) {
+ res = ast_play_and_wait(chan, "digits/2-ie");
+ } else {
+ res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
+ res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
+ }
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ }
+ res = res ? res : ast_play_and_wait(chan, "vm-new-e");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ num = div(vms->oldmessages, 10);
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/1-a");
+ res = res ? res : ast_play_and_wait(chan, "vm-old-a");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
+ if (num.rem == 2) {
+ if (!num.quot) {
+ res = ast_play_and_wait(chan, "digits/2-ie");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
+ res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
+ }
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ }
+ res = res ? res : ast_play_and_wait(chan, "vm-old-e");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+
+ return res;
+}
+
+/* SWEDISH syntax */
+static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (res)
+ return res;
+
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ return res;
+ }
+
+ if (vms->newmessages) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "digits/ett");
+ res = res ? res : ast_play_and_wait(chan, "vm-nytt");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-nya");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/ett");
+ res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-gamla");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+
+ return res;
+}
+
+/* NORWEGIAN syntax */
+static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (res)
+ return res;
+
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ return res;
+ }
+
+ if (vms->newmessages) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "digits/1");
+ res = res ? res : ast_play_and_wait(chan, "vm-ny");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-nye");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/1");
+ res = res ? res : ast_play_and_wait(chan, "vm-gamel");
+ res = res ? res : ast_play_and_wait(chan, "vm-message");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ res = res ? res : ast_play_and_wait(chan, "vm-gamle");
+ res = res ? res : ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+
+ return res;
+}
+
+/* GERMAN syntax */
+static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "digits/1F");
+ else
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "digits/1F");
+ else
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* SPANISH syntax */
+static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-youhaveno");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ } else {
+ res = ast_play_and_wait(chan, "vm-youhave");
+ }
+ if (!res) {
+ if (vms->newmessages) {
+ if (!res) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "digits/1M");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ }
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (vms->oldmessages) {
+ if (!res) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/1M");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Olds");
+ } else {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ }
+ }
+ }
+return res;
+}
+
+/* BRAZILIAN PORTUGUESE syntax */
+static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
+ /* Introduce messages they have */
+ int res;
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-nomessages");
+ return res;
+ }
+ else {
+ res = ast_play_and_wait(chan, "vm-youhave");
+ }
+ if (vms->newmessages) {
+ if (!res)
+ res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
+ if ((vms->newmessages == 1)) {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ }
+ else {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (vms->oldmessages) {
+ if (!res)
+ res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
+ if (vms->oldmessages == 1) {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Olds");
+ }
+ else {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ }
+ return res;
+}
+
+/* FRENCH syntax */
+static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* DUTCH syntax */
+static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res) {
+ if (vms->newmessages == 1)
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ else
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-Olds");
+ else
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ if (!res) {
+ if (vms->oldmessages == 1)
+ res = ast_play_and_wait(chan, "vm-message");
+ else
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+/* PORTUGUESE syntax */
+static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
+{
+ /* Introduce messages they have */
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
+ if (!res) {
+ if ((vms->newmessages == 1)) {
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOXs");
+ } else {
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ }
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+ if (!res && vms->oldmessages) {
+ res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
+ if (!res) {
+ if (vms->oldmessages == 1) {
+ res = ast_play_and_wait(chan, "vm-message");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Olds");
+ } else {
+ res = ast_play_and_wait(chan, "vm-messages");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ }
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ }
+ }
+ return res;
+}
+
+
+/* CZECH syntax */
+/* in czech there must be declension of word new and message
+ * czech : english : czech : english
+ * --------------------------------------------------------
+ * vm-youhave : you have
+ * vm-novou : one new : vm-zpravu : message
+ * vm-nove : 2-4 new : vm-zpravy : messages
+ * vm-novych : 5-infinite new : vm-zprav : messages
+ * vm-starou : one old
+ * vm-stare : 2-4 old
+ * vm-starych : 5-infinite old
+ * jednu : one - falling 4.
+ * vm-no : no ( no messages )
+ */
+
+static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
+{
+ int res;
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res) {
+ if (vms->newmessages) {
+ if (vms->newmessages == 1) {
+ res = ast_play_and_wait(chan, "digits/jednu");
+ } else {
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ }
+ if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-novou");
+ if ((vms->newmessages) > 1 && (vms->newmessages < 5))
+ res = ast_play_and_wait(chan, "vm-nove");
+ if (vms->newmessages > 4)
+ res = ast_play_and_wait(chan, "vm-novych");
+ }
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res) {
+ if ((vms->newmessages == 1))
+ res = ast_play_and_wait(chan, "vm-zpravu");
+ if ((vms->newmessages) > 1 && (vms->newmessages < 5))
+ res = ast_play_and_wait(chan, "vm-zpravy");
+ if (vms->newmessages > 4)
+ res = ast_play_and_wait(chan, "vm-zprav");
+ }
+ }
+ if (!res && vms->oldmessages) {
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res) {
+ if ((vms->oldmessages == 1))
+ res = ast_play_and_wait(chan, "vm-starou");
+ if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
+ res = ast_play_and_wait(chan, "vm-stare");
+ if (vms->oldmessages > 4)
+ res = ast_play_and_wait(chan, "vm-starych");
+ }
+ if (!res) {
+ if ((vms->oldmessages == 1))
+ res = ast_play_and_wait(chan, "vm-zpravu");
+ if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
+ res = ast_play_and_wait(chan, "vm-zpravy");
+ if (vms->oldmessages > 4)
+ res = ast_play_and_wait(chan, "vm-zprav");
+ }
+ }
+ if (!res) {
+ if (!vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-no");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-zpravy");
+ }
+ }
+ }
+ return res;
+}
+
+static int get_lastdigits(int num)
+{
+ num %= 100;
+ return (num < 20) ? num : num % 10;
+}
+
+static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
+{
+ int res;
+ int lastnum = 0;
+ int dcnum;
+
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res && vms->newmessages) {
+ lastnum = get_lastdigits(vms->newmessages);
+ dcnum = vms->newmessages - lastnum;
+ if (dcnum)
+ res = say_and_wait(chan, dcnum, chan->language);
+ if (!res && lastnum) {
+ if (lastnum == 1)
+ res = ast_play_and_wait(chan, "digits/ru/odno");
+ else
+ res = say_and_wait(chan, lastnum, chan->language);
+ }
+
+ if (!res)
+ res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
+
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+
+ if (!res && vms->oldmessages) {
+ lastnum = get_lastdigits(vms->oldmessages);
+ dcnum = vms->oldmessages - lastnum;
+ if (dcnum)
+ res = say_and_wait(chan, dcnum, chan->language);
+ if (!res && lastnum) {
+ if (lastnum == 1)
+ res = ast_play_and_wait(chan, "digits/ru/odno");
+ else
+ res = say_and_wait(chan, lastnum, chan->language);
+ }
+
+ if (!res)
+ res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
+ }
+
+ if (!res && !vms->newmessages && !vms->oldmessages) {
+ lastnum = 0;
+ res = ast_play_and_wait(chan, "vm-no");
+ }
+
+ if (!res) {
+ switch (lastnum) {
+ case 1:
+ res = ast_play_and_wait(chan, "vm-soobshenie");
+ break;
+ case 2:
+ case 3:
+ case 4:
+ res = ast_play_and_wait(chan, "vm-soobsheniya");
+ break;
+ default:
+ res = ast_play_and_wait(chan, "vm-soobsheniy");
+ break;
+ }
+ }
+
+ return res;
+}
+
+/* CHINESE (Taiwan) syntax */
+static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms)
+{
+ int res;
+ /* Introduce messages they have */
+ res = ast_play_and_wait(chan, "vm-you");
+
+ if (!res && vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-have");
+ if (!res)
+ res = say_and_wait(chan, vms->newmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-tong");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-INBOX");
+ if (vms->oldmessages && !res)
+ res = ast_play_and_wait(chan, "vm-and");
+ else if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+
+ }
+ if (!res && vms->oldmessages) {
+ res = ast_play_and_wait(chan, "vm-have");
+ if (!res)
+ res = say_and_wait(chan, vms->oldmessages, chan->language);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-tong");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-Old");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ if (!res && !vms->oldmessages && !vms->newmessages) {
+ res = ast_play_and_wait(chan, "vm-haveno");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-messages");
+ }
+ return res;
+}
+
+/* UKRAINIAN syntax */
+/* in ukrainian the syntax is different so we need the following files
+ * --------------------------------------------------------
+ * /digits/ua/1e 'odne'
+ * vm-nove 'nove'
+ * vm-stare 'stare'
+ */
+static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
+{
+ int res;
+ int lastnum = 0;
+ int dcnum;
+
+ res = ast_play_and_wait(chan, "vm-youhave");
+ if (!res && vms->newmessages) {
+ lastnum = get_lastdigits(vms->newmessages);
+ dcnum = vms->newmessages - lastnum;
+ if (dcnum)
+ res = say_and_wait(chan, dcnum, chan->language);
+ if (!res && lastnum) {
+ if (lastnum == 1)
+ res = ast_play_and_wait(chan, "digits/ua/1e");
+ else
+ res = say_and_wait(chan, lastnum, chan->language);
+ }
+
+ if (!res)
+ res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
+
+ if (!res && vms->oldmessages)
+ res = ast_play_and_wait(chan, "vm-and");
+ }
+
+ if (!res && vms->oldmessages) {
+ lastnum = get_lastdigits(vms->oldmessages);
+ dcnum = vms->oldmessages - lastnum;
+ if (dcnum)
+ res = say_and_wait(chan, dcnum, chan->language);
+ if (!res && lastnum) {
+ if (lastnum == 1)
+ res = ast_play_and_wait(chan, "digits/ua/1e");
+ else
+ res = say_and_wait(chan, lastnum, chan->language);
+ }
+
+ if (!res)
+ res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
+ }
+
+ if (!res && !vms->newmessages && !vms->oldmessages) {
+ lastnum = 0;
+ res = ast_play_and_wait(chan, "vm-no");
+ }
+
+ if (!res) {
+ switch (lastnum) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ res = ast_play_and_wait(chan, "vm-message");
+ break;
+ default:
+ res = ast_play_and_wait(chan, "vm-messages");
+ break;
+ }
+ }
+
+ return res;
+}
+
+static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
+{
+ char prefile[256];
+
+ /* Notify the user that the temp greeting is set and give them the option to remove it */
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
+ if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
+ if (ast_fileexists(prefile, NULL, NULL) > 0)
+ ast_play_and_wait(chan, "vm-tempgreetactive");
+ }
+
+ /* Play voicemail intro - syntax is different for different languages */
+ if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
+ return vm_intro_de(chan, vms);
+ } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
+ return vm_intro_es(chan, vms);
+ } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
+ return vm_intro_it(chan, vms);
+ } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
+ return vm_intro_fr(chan, vms);
+ } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
+ return vm_intro_nl(chan, vms);
+ } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
+ return vm_intro_pt(chan, vms);
+ } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
+ return vm_intro_pt_BR(chan, vms);
+ } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
+ return vm_intro_cz(chan, vms);
+ } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
+ return vm_intro_gr(chan, vms);
+ } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
+ return vm_intro_pl(chan, vms);
+ } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
+ return vm_intro_se(chan, vms);
+ } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
+ return vm_intro_no(chan, vms);
+ } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
+ return vm_intro_ru(chan, vms);
+ } else if (!strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
+ return vm_intro_tw(chan, vms);
+ } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
+ return vm_intro_ua(chan, vms);
+ } else { /* Default to ENGLISH */
+ return vm_intro_en(chan, vms);
+ }
+}
+
+static int vm_instructions_en(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
+{
+ int res = 0;
+ /* Play instructions and wait for new command */
+ while (!res) {
+ if (vms->starting) {
+ if (vms->lastmsg > -1) {
+ res = ast_play_and_wait(chan, "vm-onefor");
+ if (!res)
+ res = vm_play_folder_name(chan, vms->vmbox);
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-opts");
+ } else {
+ if (vms->curmsg)
+ res = ast_play_and_wait(chan, "vm-prev");
+ if (!res && !skipadvanced)
+ res = ast_play_and_wait(chan, "vm-advopts");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-repeat");
+ if (!res && (vms->curmsg != vms->lastmsg))
+ res = ast_play_and_wait(chan, "vm-next");
+ if (!res) {
+ if (!vms->deleted[vms->curmsg])
+ res = ast_play_and_wait(chan, "vm-delete");
+ else
+ res = ast_play_and_wait(chan, "vm-undelete");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-toforward");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-savemessage");
+ }
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-helpexit");
+ if (!res)
+ res = ast_waitfordigit(chan, 6000);
+ if (!res) {
+ vms->repeats++;
+ if (vms->repeats > 2) {
+ res = 't';
+ }
+ }
+ }
+ return res;
+}
+
+static int vm_instructions_tw(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
+{
+ int res = 0;
+ /* Play instructions and wait for new command */
+ while (!res) {
+ if (vms->lastmsg > -1) {
+ res = ast_play_and_wait(chan, "vm-listen");
+ if (!res)
+ res = vm_play_folder_name(chan, vms->vmbox);
+ if (!res)
+ res = ast_play_and_wait(chan, "press");
+ if (!res)
+ res = ast_play_and_wait(chan, "digits/1");
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-opts");
+ if (!res) {
+ vms->starting = 0;
+ return vm_instructions_en(chan,vms,skipadvanced);
+ }
+ }
+ return res;
+}
+
+static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
+{
+ if (vms->starting && !strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
+ return vm_instructions_tw(chan, vms, skipadvanced);
+ } else { /* Default to ENGLISH */
+ return vm_instructions_en(chan, vms, skipadvanced);
+ }
+}
+
+
+static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
+{
+ int cmd = 0;
+ int duration = 0;
+ int tries = 0;
+ char newpassword[80] = "";
+ char newpassword2[80] = "";
+ char prefile[PATH_MAX] = "";
+ unsigned char buf[256];
+ int bytes=0;
+
+ if (ast_adsi_available(chan)) {
+ bytes += adsi_logo(buf + bytes);
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ }
+
+ /* First, have the user change their password
+ so they won't get here again */
+ for (;;) {
+ newpassword[1] = '\0';
+ newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
+ if (cmd == '#')
+ newpassword[0] = '\0';
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ newpassword2[1] = '\0';
+ newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
+ if (cmd == '#')
+ newpassword2[0] = '\0';
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ if (!strcmp(newpassword, newpassword2))
+ break;
+ ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
+ cmd = ast_play_and_wait(chan, vm_mismatch);
+ if (++tries == 3)
+ return -1;
+ }
+ if (pwdchange & PWDCHANGE_INTERNAL)
+ vm_change_password(vmu, newpassword);
+ if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
+ vm_change_password_shell(vmu, newpassword);
+
+ ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
+ cmd = ast_play_and_wait(chan, vm_passchanged);
+
+ /* If forcename is set, have the user record their name */
+ if (ast_test_flag(vmu, VM_FORCENAME)) {
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
+ if (ast_fileexists(prefile, NULL, NULL) < 1) {
+#ifndef IMAP_STORAGE
+ cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ }
+ }
+
+ /* If forcegreetings is set, have the user record their greetings */
+ if (ast_test_flag(vmu, VM_FORCEGREET)) {
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
+ if (ast_fileexists(prefile, NULL, NULL) < 1) {
+#ifndef IMAP_STORAGE
+ cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ }
+
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
+ if (ast_fileexists(prefile, NULL, NULL) < 1) {
+#ifndef IMAP_STORAGE
+ cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ if (cmd < 0 || cmd == 't' || cmd == '#')
+ return cmd;
+ }
+ }
+
+ return cmd;
+}
+
+static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
+{
+ int cmd = 0;
+ int retries = 0;
+ int duration = 0;
+ char newpassword[80] = "";
+ char newpassword2[80] = "";
+ char prefile[PATH_MAX] = "";
+ unsigned char buf[256];
+ int bytes=0;
+
+ if (ast_adsi_available(chan))
+ {
+ bytes += adsi_logo(buf + bytes);
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ }
+ while ((cmd >= 0) && (cmd != 't')) {
+ if (cmd)
+ retries = 0;
+ switch (cmd) {
+ case '1':
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
+#ifndef IMAP_STORAGE
+ cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ break;
+ case '2':
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
+#ifndef IMAP_STORAGE
+ cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ break;
+ case '3':
+ snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
+#ifndef IMAP_STORAGE
+ cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ break;
+ case '4':
+ cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
+ break;
+ case '5':
+ if (vmu->password[0] == '-') {
+ cmd = ast_play_and_wait(chan, "vm-no");
+ break;
+ }
+ newpassword[1] = '\0';
+ newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
+ if (cmd == '#')
+ newpassword[0] = '\0';
+ else {
+ if (cmd < 0)
+ break;
+ if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
+ break;
+ }
+ }
+ newpassword2[1] = '\0';
+ newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
+ if (cmd == '#')
+ newpassword2[0] = '\0';
+ else {
+ if (cmd < 0)
+ break;
+
+ if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
+ break;
+ }
+ }
+ if (strcmp(newpassword, newpassword2)) {
+ ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
+ cmd = ast_play_and_wait(chan, vm_mismatch);
+ break;
+ }
+ if (pwdchange & PWDCHANGE_INTERNAL)
+ vm_change_password(vmu, newpassword);
+ if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
+ vm_change_password_shell(vmu, newpassword);
+
+ ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
+ cmd = ast_play_and_wait(chan, vm_passchanged);
+ break;
+ case '*':
+ cmd = 't';
+ break;
+ default:
+ cmd = ast_play_and_wait(chan,"vm-options");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,6000);
+ if (!cmd)
+ retries++;
+ if (retries > 3)
+ cmd = 't';
+ }
+ }
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
+{
+ int cmd = 0;
+ int retries = 0;
+ int duration = 0;
+ char prefile[PATH_MAX] = "";
+ unsigned char buf[256];
+ int bytes = 0;
+
+ if (ast_adsi_available(chan)) {
+ bytes += adsi_logo(buf + bytes);
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
+ bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ }
+
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
+ while ((cmd >= 0) && (cmd != 't')) {
+ if (cmd)
+ retries = 0;
+ RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
+ if (ast_fileexists(prefile, NULL, NULL) <= 0) {
+#ifndef IMAP_STORAGE
+ play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ cmd = 't';
+ } else {
+ switch (cmd) {
+ case '1':
+#ifndef IMAP_STORAGE
+ cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
+#else
+ cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
+#endif
+ break;
+ case '2':
+ DELETE(prefile, -1, prefile);
+ ast_play_and_wait(chan, "vm-tempremoved");
+ cmd = 't';
+ break;
+ case '*':
+ cmd = 't';
+ break;
+ default:
+ cmd = ast_play_and_wait(chan,
+ ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
+ "vm-tempgreeting2" : "vm-tempgreeting");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,6000);
+ if (!cmd)
+ retries++;
+ if (retries > 3)
+ cmd = 't';
+ }
+ }
+ DISPOSE(prefile, -1);
+ }
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+/* GREEK SYNTAX */
+
+static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-youhaveno");
+ if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ } else {
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ }
+ }
+ return cmd;
+}
+
+/* Default English syntax */
+static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-youhave");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-no");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ }
+ return cmd;
+}
+
+/* ITALIAN syntax */
+static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-no");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-message");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ }
+ return cmd;
+}
+
+/* SPANISH syntax */
+static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-youhaveno");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ }
+ return cmd;
+}
+
+/* PORTUGUESE syntax */
+static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-no");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ }
+ return cmd;
+}
+
+/* Chinese (Taiwan)syntax */
+static int vm_browse_messages_tw(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ int cmd=0;
+
+ if (vms->lastmsg > -1) {
+ cmd = play_message(chan, vmu, vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-you");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-haveno");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-messages");
+ if (!cmd) {
+ snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+ cmd = ast_play_and_wait(chan, vms->fn);
+ }
+ }
+ return cmd;
+}
+
+static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+ if (!strcasecmp(chan->language, "es")) { /* SPANISH */
+ return vm_browse_messages_es(chan, vms, vmu);
+ } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
+ return vm_browse_messages_it(chan, vms, vmu);
+ } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
+ return vm_browse_messages_pt(chan, vms, vmu);
+ } else if (!strcasecmp(chan->language, "gr")){
+ return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
+ } else if (!strcasecmp(chan->language, "tw")){
+ return vm_browse_messages_tw(chan, vms, vmu); /* CHINESE (Taiwan) */
+ } else { /* Default to English syntax */
+ return vm_browse_messages_en(chan, vms, vmu);
+ }
+}
+
+static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
+ struct ast_vm_user *res_vmu, const char *context, const char *prefix,
+ int skipuser, int maxlogins, int silent)
+{
+ int useadsi=0, valid=0, logretries=0;
+ char password[AST_MAX_EXTENSION]="", *passptr;
+ struct ast_vm_user vmus, *vmu = NULL;
+
+ /* If ADSI is supported, setup login screen */
+ adsi_begin(chan, &useadsi);
+ if (!skipuser && useadsi)
+ adsi_login(chan);
+ if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
+ ast_log(LOG_WARNING, "Couldn't stream login file\n");
+ return -1;
+ }
+
+ /* Authenticate them and get their mailbox/password */
+
+ while (!valid && (logretries < maxlogins)) {
+ /* Prompt for, and read in the username */
+ if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
+ ast_log(LOG_WARNING, "Couldn't read username\n");
+ return -1;
+ }
+ if (ast_strlen_zero(mailbox)) {
+ if (chan->cid.cid_num) {
+ ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
+ } else {
+ ast_verb(3,"Username not entered\n");
+ return -1;
+ }
+ }
+ if (useadsi)
+ adsi_password(chan);
+
+ if (!ast_strlen_zero(prefix)) {
+ char fullusername[80] = "";
+ ast_copy_string(fullusername, prefix, sizeof(fullusername));
+ strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
+ ast_copy_string(mailbox, fullusername, mailbox_size);
+ }
+
+ ast_debug(1, "Before find user for mailbox %s\n",mailbox);
+ vmu = find_user(&vmus, context, mailbox);
+ if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
+ /* saved password is blank, so don't bother asking */
+ password[0] = '\0';
+ } else {
+ if (ast_streamfile(chan, vm_password, chan->language)) {
+ ast_log(LOG_WARNING, "Unable to stream password file\n");
+ return -1;
+ }
+ if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
+ ast_log(LOG_WARNING, "Unable to read password\n");
+ return -1;
+ }
+ }
+
+ if (vmu) {
+ passptr = vmu->password;
+ if (passptr[0] == '-') passptr++;
+ }
+ if (vmu && !strcmp(passptr, password))
+ valid++;
+ else {
+ ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
+ if (!ast_strlen_zero(prefix))
+ mailbox[0] = '\0';
+ }
+ logretries++;
+ if (!valid) {
+ if (skipuser || logretries >= maxlogins) {
+ if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
+ ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
+ return -1;
+ }
+ } else {
+ if (useadsi)
+ adsi_login(chan);
+ if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
+ ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
+ return -1;
+ }
+ }
+ if (ast_waitstream(chan, "")) /* Channel is hung up */
+ return -1;
+ }
+ }
+ if (!valid && (logretries >= maxlogins)) {
+ ast_stopstream(chan);
+ ast_play_and_wait(chan, "vm-goodbye");
+ return -1;
+ }
+ if (vmu && !skipuser) {
+ memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
+ }
+ return 0;
+}
+
+static int vm_execmain(struct ast_channel *chan, void *data)
+{
+ /* XXX This is, admittedly, some pretty horrendous code. For some
+ reason it just seemed a lot easier to do with GOTO's. I feel
+ like I'm back in my GWBASIC days. XXX */
+ int res=-1;
+ int cmd=0;
+ int valid = 0;
+ char prefixstr[80] ="";
+ char ext_context[256]="";
+ int box;
+ int useadsi = 0;
+ int skipuser = 0;
+ struct vm_state vms;
+ struct ast_vm_user *vmu = NULL, vmus;
+ char *context=NULL;
+ int silentexit = 0;
+ struct ast_flags flags = { 0 };
+ signed char record_gain = 0;
+ int play_auto = 0;
+ int play_folder = 0;
+#ifdef IMAP_STORAGE
+ int deleted = 0;
+#endif
+
+ /* Add the vm_state to the active list and keep it active */
+ memset(&vms, 0, sizeof(vms));
+ vms.lastmsg = -1;
+
+ memset(&vmus, 0, sizeof(vmus));
+
+ if (chan->_state != AST_STATE_UP) {
+ ast_debug(1, "Before ast_answer\n");
+ ast_answer(chan);
+ }
+
+ if (!ast_strlen_zero(data)) {
+ char *opts[OPT_ARG_ARRAY_SIZE];
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(argv0);
+ AST_APP_ARG(argv1);
+ );
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc == 2) {
+ if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
+ return -1;
+ if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
+ int gain;
+ if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
+ if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
+ ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
+ return -1;
+ } else {
+ record_gain = (signed char) gain;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
+ }
+ }
+ if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
+ play_auto = 1;
+ if (opts[OPT_ARG_PLAYFOLDER]) {
+ if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
+ ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Invalid folder set with option a\n");
+ }
+ if ( play_folder > 9 || play_folder < 0) {
+ ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
+ play_folder = 0;
+ }
+ }
+ } else {
+ /* old style options parsing */
+ while (*(args.argv0)) {
+ if (*(args.argv0) == 's')
+ ast_set_flag(&flags, OPT_SILENT);
+ else if (*(args.argv0) == 'p')
+ ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
+ else
+ break;
+ (args.argv0)++;
+ }
+
+ }
+
+ valid = ast_test_flag(&flags, OPT_SILENT);
+
+ if ((context = strchr(args.argv0, '@')))
+ *context++ = '\0';
+
+ if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
+ ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
+ else
+ ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
+
+ if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
+ skipuser++;
+ else
+ valid = 0;
+ }
+
+ if (!valid)
+ res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
+
+ ast_debug(1, "After vm_authenticate\n");
+ if (!res) {
+ valid = 1;
+ if (!skipuser)
+ vmu = &vmus;
+ } else {
+ res = 0;
+ }
+
+ /* If ADSI is supported, setup login screen */
+ adsi_begin(chan, &useadsi);
+
+#ifdef IMAP_STORAGE
+ vms.interactive = 1;
+ vms.updated = 1;
+ vmstate_insert(&vms);
+ init_vm_state(&vms);
+#endif
+ if (!valid)
+ goto out;
+
+ if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
+ cmd = ast_play_and_wait(chan, "an-error-has-occured");
+ return -1;
+ }
+ if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for heard message storage!\n");
+ cmd = ast_play_and_wait(chan, "an-error-has-occured");
+ return -1;
+ }
+
+ /* Set language from config to override channel language */
+ if (!ast_strlen_zero(vmu->language))
+ ast_string_field_set(chan, language, vmu->language);
+ create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
+ /* Retrieve old and new message counts */
+ ast_debug(1, "Before open_mailbox\n");
+ res = open_mailbox(&vms, vmu, OLD_FOLDER);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ vms.oldmessages = vms.lastmsg + 1;
+ ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
+ /* Start in INBOX */
+ res = open_mailbox(&vms, vmu, NEW_FOLDER);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ vms.newmessages = vms.lastmsg + 1;
+ ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
+
+ /* Select proper mailbox FIRST!! */
+ if (play_auto) {
+ res = open_mailbox(&vms, vmu, play_folder);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+
+ /* If there are no new messages, inform the user and hangup */
+ if (vms.lastmsg == -1) {
+ cmd = vm_browse_messages(chan, &vms, vmu);
+ res = 0;
+ goto out;
+ }
+ } else {
+ if (!vms.newmessages && vms.oldmessages) {
+ /* If we only have old messages start here */
+ res = open_mailbox(&vms, vmu, OLD_FOLDER);
+ play_folder = 1;
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ }
+ }
+
+ if (useadsi)
+ adsi_status(chan, &vms);
+ res = 0;
+
+ /* Check to see if this is a new user */
+ if (!strcasecmp(vmu->mailbox, vmu->password) &&
+ (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
+ if (ast_play_and_wait(chan, "vm-newuser") == -1)
+ ast_log(LOG_WARNING, "Couldn't stream new user file\n");
+ cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
+ if ((cmd == 't') || (cmd == '#')) {
+ /* Timeout */
+ res = 0;
+ goto out;
+ } else if (cmd < 0) {
+ /* Hangup */
+ res = -1;
+ goto out;
+ }
+ }
+#ifdef IMAP_STORAGE
+ ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
+ if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
+ ast_debug(1, "*** QUOTA EXCEEDED!!\n");
+ cmd = ast_play_and_wait(chan, "vm-mailboxfull");
+ }
+ ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
+ if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
+ ast_log(LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
+ cmd = ast_play_and_wait(chan, "vm-mailboxfull");
+ }
+#endif
+ if (play_auto) {
+ cmd = '1';
+ } else {
+ cmd = vm_intro(chan, vmu, &vms);
+ }
+
+ vms.repeats = 0;
+ vms.starting = 1;
+ while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
+ /* Run main menu */
+ switch (cmd) {
+ case '1':
+ vms.curmsg = 0;
+ /* Fall through */
+ case '5':
+ cmd = vm_browse_messages(chan, &vms, vmu);
+ break;
+ case '2': /* Change folders */
+ if (useadsi)
+ adsi_folders(chan, 0, "Change to folder...");
+ cmd = get_folder2(chan, "vm-changeto", 0);
+ if (cmd == '#') {
+ cmd = 0;
+ } else if (cmd > 0) {
+ cmd = cmd - '0';
+ res = close_mailbox(&vms, vmu);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ res = open_mailbox(&vms, vmu, cmd);
+ if (res == ERROR_LOCK_PATH)
+ goto out;
+ play_folder = cmd;
+ cmd = 0;
+ }
+ if (useadsi)
+ adsi_status2(chan, &vms);
+
+ if (!cmd)
+ cmd = vm_play_folder_name(chan, vms.vmbox);
+
+ vms.starting = 1;
+ break;
+ case '3': /* Advanced options */
+ cmd = 0;
+ vms.repeats = 0;
+ while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
+ switch (cmd) {
+ case '1': /* Reply */
+ if (vms.lastmsg > -1 && !vms.starting) {
+ cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+ case '2': /* Callback */
+ if (!vms.starting)
+ ast_verb(3, "Callback Requested\n");
+ if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
+ cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
+ if (cmd == 9) {
+ silentexit = 1;
+ goto out;
+ } else if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ }
+ else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+ case '3': /* Envelope */
+ if (vms.lastmsg > -1 && !vms.starting) {
+ cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+ case '4': /* Dialout */
+ if (!ast_strlen_zero(vmu->dialout)) {
+ cmd = dialout(chan, vmu, NULL, vmu->dialout);
+ if (cmd == 9) {
+ silentexit = 1;
+ goto out;
+ }
+ }
+ else
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ cmd = 't';
+ break;
+
+ case '5': /* Leave VoiceMail */
+ if (ast_test_flag(vmu, VM_SVMAIL)) {
+ cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan,"vm-sorry");
+ cmd='t';
+ break;
+
+ case '*': /* Return to main menu */
+ cmd = 't';
+ break;
+
+ default:
+ cmd = 0;
+ if (!vms.starting) {
+ cmd = ast_play_and_wait(chan, "vm-toreply");
+ }
+ if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
+ cmd = ast_play_and_wait(chan, "vm-tocallback");
+ }
+ if (!cmd && !vms.starting) {
+ cmd = ast_play_and_wait(chan, "vm-tohearenv");
+ }
+ if (!ast_strlen_zero(vmu->dialout) && !cmd) {
+ cmd = ast_play_and_wait(chan, "vm-tomakecall");
+ }
+ if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
+ cmd=ast_play_and_wait(chan, "vm-leavemsg");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-starmain");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan,6000);
+ if (!cmd)
+ vms.repeats++;
+ if (vms.repeats > 3)
+ cmd = 't';
+ }
+ }
+ if (cmd == 't') {
+ cmd = 0;
+ vms.repeats = 0;
+ }
+ break;
+ case '4':
+ if (vms.curmsg > 0) {
+ vms.curmsg--;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ break;
+ case '6':
+ if (vms.curmsg < vms.lastmsg) {
+ vms.curmsg++;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ break;
+ case '7':
+ if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
+ vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
+ if (useadsi)
+ adsi_delete(chan, &vms);
+ if (vms.deleted[vms.curmsg]) {
+ if (play_folder == 0)
+ vms.newmessages--;
+ else if (play_folder == 1)
+ vms.oldmessages--;
+ cmd = ast_play_and_wait(chan, "vm-deleted");
+ }
+ else {
+ if (play_folder == 0)
+ vms.newmessages++;
+ else if (play_folder == 1)
+ vms.oldmessages++;
+ cmd = ast_play_and_wait(chan, "vm-undeleted");
+ }
+ if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
+ if (vms.curmsg < vms.lastmsg) {
+ vms.curmsg++;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ }
+ } else /* Delete not valid if we haven't selected a message */
+ cmd = 0;
+#ifdef IMAP_STORAGE
+ deleted = 1;
+#endif
+ break;
+
+ case '8':
+ if (vms.lastmsg > -1) {
+ cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+ }
+ } else
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ break;
+ case '9':
+ if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
+ /* No message selected */
+ cmd = 0;
+ break;
+ }
+ if (useadsi)
+ adsi_folders(chan, 1, "Save to folder...");
+ cmd = get_folder2(chan, "vm-savefolder", 1);
+ box = 0; /* Shut up compiler */
+ if (cmd == '#') {
+ cmd = 0;
+ break;
+ } else if (cmd > 0) {
+ box = cmd = cmd - '0';
+ cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
+ if (cmd == ERROR_LOCK_PATH) {
+ res = cmd;
+ goto out;
+#ifdef IMAP_STORAGE
+ } else if (cmd == 10) {
+ goto out;
+#endif
+ } else if (!cmd) {
+ vms.deleted[vms.curmsg] = 1;
+ } else {
+ vms.deleted[vms.curmsg] = 0;
+ vms.heard[vms.curmsg] = 0;
+ }
+ }
+ make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
+ if (useadsi)
+ adsi_message(chan, &vms);
+ snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
+ if (!cmd) {
+ cmd = ast_play_and_wait(chan, "vm-message");
+ if (!cmd)
+ cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-savedto");
+ if (!cmd)
+ cmd = vm_play_folder_name(chan, vms.fn);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-mailboxfull");
+ }
+ if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
+ if (vms.curmsg < vms.lastmsg) {
+ vms.curmsg++;
+ cmd = play_message(chan, vmu, &vms);
+ } else {
+ cmd = ast_play_and_wait(chan, "vm-nomore");
+ }
+ }
+ break;
+ case '*':
+ if (!vms.starting) {
+ cmd = ast_play_and_wait(chan, "vm-onefor");
+ if (!cmd)
+ cmd = vm_play_folder_name(chan, vms.vmbox);
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-opts");
+ if (!cmd)
+ cmd = vm_instructions(chan, &vms, 1);
+ } else
+ cmd = 0;
+ break;
+ case '0':
+ cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
+ if (useadsi)
+ adsi_status(chan, &vms);
+ break;
+ default: /* Nothing */
+ cmd = vm_instructions(chan, &vms, 0);
+ break;
+ }
+ }
+ if ((cmd == 't') || (cmd == '#')) {
+ /* Timeout */
+ res = 0;
+ } else {
+ /* Hangup */
+ res = -1;
+ }
+
+out:
+ if (res > -1) {
+ ast_stopstream(chan);
+ adsi_goodbye(chan);
+ if (valid) {
+ if (silentexit)
+ res = ast_play_and_wait(chan, "vm-dialout");
+ else
+ res = ast_play_and_wait(chan, "vm-goodbye");
+ if (res > 0)
+ res = 0;
+ }
+ if (useadsi)
+ ast_adsi_unload_session(chan);
+ }
+ if (vmu)
+ close_mailbox(&vms, vmu);
+ if (valid) {
+ int new = 0, old = 0;
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
+ manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
+ run_externnotify(vmu->context, vmu->mailbox);
+ ast_app_inboxcount(ext_context, &new, &old);
+ queue_mwi_event(ext_context, new, old);
+ }
+#ifdef IMAP_STORAGE
+ /* expunge message - use UID Expunge if supported on IMAP server*/
+ ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
+ if (vmu && deleted == 1 && expungeonhangup == 1) {
+#ifdef HAVE_IMAP_TK2006
+ if (LEVELUIDPLUS (vms.mailstream)) {
+ mail_expunge_full(vms.mailstream,NIL,EX_UID);
+ } else
+#endif
+ mail_expunge(vms.mailstream);
+ }
+ /* before we delete the state, we should copy pertinent info
+ * back to the persistent model */
+ vmstate_delete(&vms);
+#endif
+ if (vmu)
+ free_user(vmu);
+ if (vms.deleted)
+ ast_free(vms.deleted);
+ if (vms.heard)
+ ast_free(vms.heard);
+
+ return res;
+}
+
+static int vm_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *tmp;
+ struct leave_vm_options leave_options;
+ struct ast_flags flags = { 0 };
+ char *opts[OPT_ARG_ARRAY_SIZE];
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(argv0);
+ AST_APP_ARG(argv1);
+ );
+
+ memset(&leave_options, 0, sizeof(leave_options));
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ if (!ast_strlen_zero(data)) {
+ tmp = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, tmp);
+ if (args.argc == 2) {
+ if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
+ return -1;
+ ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_DTMFEXIT);
+ if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
+ int gain;
+
+ if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
+ ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
+ return -1;
+ } else {
+ leave_options.record_gain = (signed char) gain;
+ }
+ }
+ if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
+ if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
+ leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
+ }
+ }
+ } else {
+ char tmp[256];
+ res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
+ if (res < 0)
+ return res;
+ if (ast_strlen_zero(tmp))
+ return 0;
+ args.argv0 = ast_strdupa(tmp);
+ }
+
+ res = leave_voicemail(chan, args.argv0, &leave_options);
+
+ if (res == ERROR_LOCK_PATH) {
+ ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ res = 0;
+ }
+
+ return res;
+}
+
+static struct ast_vm_user *find_or_create(const char *context, const char *mbox)
+{
+ struct ast_vm_user *vmu;
+
+ AST_LIST_TRAVERSE(&users, vmu, list) {
+ if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
+ break;
+ if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
+ break;
+ }
+
+ if (vmu)
+ return vmu;
+
+ if (!(vmu = ast_calloc(1, sizeof(*vmu))))
+ return NULL;
+
+ ast_copy_string(vmu->context, context, sizeof(vmu->context));
+ ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
+
+ AST_LIST_INSERT_TAIL(&users, vmu, list);
+
+ return vmu;
+}
+
+static int append_mailbox(const char *context, const char *mbox, const char *data)
+{
+ /* Assumes lock is already held */
+ char *tmp;
+ char *stringp;
+ char *s;
+ struct ast_vm_user *vmu;
+ char *mailbox_full;
+ int new = 0, old = 0;
+
+ tmp = ast_strdupa(data);
+
+ if (!(vmu = find_or_create(context, mbox)))
+ return -1;
+
+ populate_defaults(vmu);
+
+ stringp = tmp;
+ if ((s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->password, s, sizeof(vmu->password));
+ if (stringp && (s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
+ if (stringp && (s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->email, s, sizeof(vmu->email));
+ if (stringp && (s = strsep(&stringp, ",")))
+ ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
+ if (stringp && (s = strsep(&stringp, ",")))
+ apply_options(vmu, s);
+
+ mailbox_full = alloca(strlen(mbox) + strlen(context) + 1);
+ strcpy(mailbox_full, mbox);
+ strcat(mailbox_full, "@");
+ strcat(mailbox_full, context);
+
+ inboxcount(mailbox_full, &new, &old);
+ queue_mwi_event(mailbox_full, new, old);
+
+ return 0;
+}
+
+static int vm_box_exists(struct ast_channel *chan, void *data)
+{
+ struct ast_vm_user svm;
+ char *context, *box;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(mbox);
+ AST_APP_ARG(options);
+ );
+ static int dep_warning = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
+ return -1;
+ }
+
+ if (!dep_warning) {
+ dep_warning = 1;
+ ast_log(LOG_WARNING, "MailboxExists is deprecated. Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
+ }
+
+ box = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, box);
+
+ if (args.options) {
+ }
+
+ if ((context = strchr(args.mbox, '@'))) {
+ *context = '\0';
+ context++;
+ }
+
+ if (find_user(&svm, context, args.mbox)) {
+ pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
+ } else
+ pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
+
+ return 0;
+}
+
+static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
+{
+ struct ast_vm_user svm;
+ AST_DECLARE_APP_ARGS(arg,
+ AST_APP_ARG(mbox);
+ AST_APP_ARG(context);
+ );
+
+ AST_NONSTANDARD_APP_ARGS(arg, args, '@');
+
+ ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
+ return 0;
+}
+
+static struct ast_custom_function mailbox_exists_acf = {
+ .name = "MAILBOX_EXISTS",
+ .synopsis = "Tell if a mailbox is configured",
+ .desc =
+"Returns a boolean of whether the corresponding mailbox exists. If context\n"
+"is not specified, defaults to the \"default\" context.\n",
+ .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
+ .read = acf_mailbox_exists,
+};
+
+static int vmauthenticate(struct ast_channel *chan, void *data)
+{
+ char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
+ struct ast_vm_user vmus;
+ char *options = NULL;
+ int silent = 0, skipuser = 0;
+ int res = -1;
+
+ if (s) {
+ s = ast_strdupa(s);
+ user = strsep(&s, ",");
+ options = strsep(&s, ",");
+ if (user) {
+ s = user;
+ user = strsep(&s, "@");
+ context = strsep(&s, "");
+ if (!ast_strlen_zero(user))
+ skipuser++;
+ ast_copy_string(mailbox, user, sizeof(mailbox));
+ }
+ }
+
+ if (options) {
+ silent = (strchr(options, 's')) != NULL;
+ }
+
+ if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
+ pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
+ pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
+ ast_play_and_wait(chan, "auth-thankyou");
+ res = 0;
+ }
+
+ return res;
+}
+
+static char *show_users_realtime(int fd, const char *context)
+{
+ struct ast_config *cfg;
+ const char *cat = NULL;
+
+ if (!(cfg = ast_load_realtime_multientry("voicemail",
+ "context", context, NULL))) {
+ return CLI_FAILURE;
+ }
+
+ ast_cli(fd, "\n"
+ "=============================================================\n"
+ "=== Configured Voicemail Users ==============================\n"
+ "=============================================================\n"
+ "===\n");
+
+ while ((cat = ast_category_browse(cfg, cat))) {
+ struct ast_variable *var = NULL;
+ ast_cli(fd, "=== Mailbox ...\n"
+ "===\n");
+ for (var = ast_variable_browse(cfg, cat); var; var = var->next)
+ ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
+ ast_cli(fd, "===\n"
+ "=== ---------------------------------------------------------\n"
+ "===\n");
+ }
+
+ ast_cli(fd, "=============================================================\n"
+ "\n");
+
+ return CLI_SUCCESS;
+}
+
+static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ int wordlen;
+ struct ast_vm_user *vmu;
+ const char *context = "";
+
+ /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
+ if (pos > 4)
+ return NULL;
+ if (pos == 3)
+ return (state == 0) ? ast_strdup("for") : NULL;
+ wordlen = strlen(word);
+ AST_LIST_TRAVERSE(&users, vmu, list) {
+ if (!strncasecmp(word, vmu->context, wordlen)) {
+ if (context && strcmp(context, vmu->context) && ++which > state)
+ return ast_strdup(vmu->context);
+ /* ignore repeated contexts ? */
+ context = vmu->context;
+ }
+ }
+ return NULL;
+}
+
+/*! \brief Show a list of voicemail users in the CLI */
+static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_vm_user *vmu;
+ char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
+ const char *context = NULL;
+ int users_counter = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "voicemail show users";
+ e->usage =
+ "Usage: voicemail show users [for <context>]\n"
+ " Lists all mailboxes currently set up\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
+ }
+
+ if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
+ return CLI_SHOWUSAGE;
+ if (a->argc == 5) {
+ if (strcmp(a->argv[3],"for"))
+ return CLI_SHOWUSAGE;
+ context = a->argv[4];
+ }
+
+ if (ast_check_realtime("voicemail")) {
+ if (!context) {
+ ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
+ return CLI_SHOWUSAGE;
+ }
+ return show_users_realtime(a->fd, context);
+ }
+
+ AST_LIST_LOCK(&users);
+ if (AST_LIST_EMPTY(&users)) {
+ ast_cli(a->fd, "There are no voicemail users currently defined\n");
+ AST_LIST_UNLOCK(&users);
+ return CLI_FAILURE;
+ }
+ if (a->argc == 3)
+ ast_cli(a->fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
+ else {
+ int count = 0;
+ AST_LIST_TRAVERSE(&users, vmu, list) {
+ if (!strcmp(context, vmu->context))
+ count++;
+ }
+ if (count) {
+ ast_cli(a->fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
+ } else {
+ ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
+ AST_LIST_UNLOCK(&users);
+ return CLI_FAILURE;
+ }
+ }
+ AST_LIST_TRAVERSE(&users, vmu, list) {
+ int newmsgs = 0, oldmsgs = 0;
+ char count[12], tmp[256] = "";
+
+ if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
+ snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
+ inboxcount(tmp, &newmsgs, &oldmsgs);
+ snprintf(count,sizeof(count),"%d",newmsgs);
+ ast_cli(a->fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
+ users_counter++;
+ }
+ }
+ AST_LIST_UNLOCK(&users);
+ ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show a list of voicemail zones in the CLI */
+static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct vm_zone *zone;
+ char *output_format = "%-15s %-20s %-45s\n";
+ char *res = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "voicemail show zones";
+ e->usage =
+ "Usage: voicemail show zones\n"
+ " Lists zone message formats\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&zones);
+ if (!AST_LIST_EMPTY(&zones)) {
+ ast_cli(a->fd, output_format, "Zone", "Timezone", "Message Format");
+ AST_LIST_TRAVERSE(&zones, zone, list) {
+ ast_cli(a->fd, output_format, zone->name, zone->timezone, zone->msg_format);
+ }
+ } else {
+ ast_cli(a->fd, "There are no voicemail zones currently defined\n");
+ res = CLI_FAILURE;
+ }
+ AST_LIST_UNLOCK(&zones);
+
+ return res;
+}
+
+/*! \brief Reload voicemail configuration from the CLI */
+static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "voicemail reload";
+ e->usage =
+ "Usage: voicemail reload\n"
+ " Reload voicemail configuration\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "Reloading voicemail configuration...\n");
+ load_config(1);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_voicemail[] = {
+ AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
+ AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
+ AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
+};
+
+static void poll_subscribed_mailboxes(void)
+{
+ struct mwi_sub *mwi_sub;
+
+ AST_RWLIST_RDLOCK(&mwi_subs);
+ AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
+ int new = 0, old = 0;
+
+ if (ast_strlen_zero(mwi_sub->mailbox))
+ continue;
+
+ inboxcount(mwi_sub->mailbox, &new, &old);
+
+ if (new != mwi_sub->old_new || old != mwi_sub->old_old) {
+ mwi_sub->old_new = new;
+ mwi_sub->old_old = old;
+ queue_mwi_event(mwi_sub->mailbox, new, old);
+ }
+ }
+ AST_RWLIST_UNLOCK(&mwi_subs);
+}
+
+static void *mb_poll_thread(void *data)
+{
+ while (poll_thread_run) {
+ struct timespec ts = { 0, };
+ struct timeval tv;
+
+ tv = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+
+ ast_mutex_lock(&poll_lock);
+ ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
+ ast_mutex_unlock(&poll_lock);
+
+ if (!poll_thread_run)
+ break;
+
+ poll_subscribed_mailboxes();
+ }
+
+ return NULL;
+}
+
+static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
+{
+ ast_free(mwi_sub);
+}
+
+static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
+{
+ uint32_t uniqueid;
+ struct mwi_sub *mwi_sub;
+
+ if (ast_event_get_type(event) != AST_EVENT_UNSUB)
+ return;
+
+ if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
+ return;
+
+ uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
+
+ AST_RWLIST_WRLOCK(&mwi_subs);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
+ if (mwi_sub->uniqueid == uniqueid) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+ AST_RWLIST_UNLOCK(&mwi_subs);
+
+ if (mwi_sub)
+ mwi_sub_destroy(mwi_sub);
+}
+
+static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
+{
+ const char *mailbox;
+ uint32_t uniqueid;
+ unsigned int len;
+ struct mwi_sub *mwi_sub;
+
+ if (ast_event_get_type(event) != AST_EVENT_SUB)
+ return;
+
+ if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
+ return;
+
+ mailbox = ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX);
+ uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
+
+ len = sizeof(*mwi_sub);
+ if (!ast_strlen_zero(mailbox))
+ len += strlen(mailbox);
+
+ if (!(mwi_sub = ast_calloc(1, len)))
+ return;
+
+ mwi_sub->uniqueid = uniqueid;
+ if (!ast_strlen_zero(mailbox))
+ strcpy(mwi_sub->mailbox, mailbox);
+
+ AST_RWLIST_WRLOCK(&mwi_subs);
+ AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
+ AST_RWLIST_UNLOCK(&mwi_subs);
+}
+
+static void start_poll_thread(void)
+{
+ pthread_attr_t attr;
+
+ mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
+ AST_EVENT_IE_END);
+
+ mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
+ AST_EVENT_IE_END);
+
+ if (mwi_sub_sub)
+ ast_event_report_subs(mwi_sub_sub);
+
+ poll_thread_run = 1;
+
+ pthread_attr_init(&attr);
+ ast_pthread_create(&poll_thread, &attr, mb_poll_thread, NULL);
+ pthread_attr_destroy(&attr);
+}
+
+static void stop_poll_thread(void)
+{
+ poll_thread_run = 0;
+
+ if (mwi_sub_sub) {
+ ast_event_unsubscribe(mwi_sub_sub);
+ mwi_sub_sub = NULL;
+ }
+
+ if (mwi_unsub_sub) {
+ ast_event_unsubscribe(mwi_unsub_sub);
+ mwi_unsub_sub = NULL;
+ }
+
+ ast_mutex_lock(&poll_lock);
+ ast_cond_signal(&poll_cond);
+ ast_mutex_unlock(&poll_lock);
+
+ pthread_join(poll_thread, NULL);
+
+ poll_thread = AST_PTHREADT_NULL;
+}
+
+/*! \brief Manager list voicemail users command */
+static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
+{
+ struct ast_vm_user *vmu = NULL;
+ const char *id = astman_get_header(m, "ActionID");
+ char actionid[128] = "";
+
+ if (!ast_strlen_zero(id))
+ snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
+
+ AST_LIST_LOCK(&users);
+
+ if (AST_LIST_EMPTY(&users)) {
+ astman_send_ack(s, m, "There are no voicemail users currently defined.");
+ AST_LIST_UNLOCK(&users);
+ return RESULT_SUCCESS;
+ }
+
+ astman_send_ack(s, m, "Voicemail user list will follow\r\n");
+
+ AST_LIST_TRAVERSE(&users, vmu, list) {
+ char dirname[256];
+
+#ifdef IMAP_STORAGE
+ int new, old;
+ inboxcount (vmu->mailbox, &new, &old);
+#endif
+
+ make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
+ astman_append(s,
+ "%s"
+ "Event: VoicemailUserEntry\r\n"
+ "VMContext: %s\r\n"
+ "VoiceMailbox: %s\r\n"
+ "Fullname: %s\r\n"
+ "Email: %s\r\n"
+ "Pager: %s\r\n"
+ "ServerEmail: %s\r\n"
+ "MailCommand: %s\r\n"
+ "Language: %s\r\n"
+ "TimeZone: %s\r\n"
+ "Callback: %s\r\n"
+ "Dialout: %s\r\n"
+ "UniqueID: %s\r\n"
+ "ExitContext: %s\r\n"
+ "SayDurationMinimum: %d\r\n"
+ "SayEnvelope: %s\r\n"
+ "SayCID: %s\r\n"
+ "AttachMessage: %s\r\n"
+ "AttachmentFormat: %s\r\n"
+ "DeleteMessage: %s\r\n"
+ "VolumeGain: %.2f\r\n"
+ "CanReview: %s\r\n"
+ "CallOperator: %s\r\n"
+ "MaxMessageCount: %d\r\n"
+ "MaxMessageLength: %d\r\n"
+ "NewMessageCount: %d\r\n"
+#ifdef IMAP_STORAGE
+ "OldMessageCount: %d\r\n"
+ "IMAPUser: %s\r\n"
+#endif
+ "\r\n",
+ actionid,
+ vmu->context,
+ vmu->mailbox,
+ vmu->fullname,
+ vmu->email,
+ vmu->pager,
+ vmu->serveremail,
+ vmu->mailcmd,
+ vmu->language,
+ vmu->zonetag,
+ vmu->callback,
+ vmu->dialout,
+ vmu->uniqueid,
+ vmu->exit,
+ vmu->saydurationm,
+ ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
+ ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
+ ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
+ vmu->attachfmt,
+ ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
+ vmu->volgain,
+ ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
+ ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
+ vmu->maxmsg,
+ vmu->maxsecs,
+#ifdef IMAP_STORAGE
+ new, old, vmu->imapuser
+#else
+ count_messages(vmu, dirname)
+#endif
+ );
+ }
+ astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
+
+ AST_LIST_UNLOCK(&users);
+
+ return RESULT_SUCCESS;
+}
+
+static int load_config(int reload)
+{
+ struct ast_vm_user *cur;
+ struct vm_zone *zcur;
+ struct ast_config *cfg, *ucfg;
+ char *cat;
+ struct ast_variable *var;
+ const char *val;
+ const char *s;
+ const char *key;
+ char *q, *stringp;
+ int x;
+ int tmpadsi[4];
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
+ if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
+ } else {
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ ucfg = ast_config_load("users.conf", config_flags);
+ }
+
+ /* set audio control prompts */
+ strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
+ strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
+ strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
+ strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
+ strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
+
+ AST_LIST_LOCK(&users);
+ while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
+ ast_set_flag(cur, VM_ALLOCED);
+ free_user(cur);
+ }
+
+ AST_LIST_LOCK(&zones);
+ while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
+ free_zone(zcur);
+ AST_LIST_UNLOCK(&zones);
+
+ memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
+
+ if (cfg) {
+ /* General settings */
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
+ val = "default";
+ ast_copy_string(userscontext, val, sizeof(userscontext));
+ /* Attach voice message to mail message ? */
+ if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
+ val = "yes";
+ ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
+ val = "no";
+ ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
+
+ volgain = 0.0;
+ if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
+ sscanf(val, "%lf", &volgain);
+
+#ifdef ODBC_STORAGE
+ strcpy(odbc_database, "asterisk");
+ if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
+ ast_copy_string(odbc_database, val, sizeof(odbc_database));
+ }
+ strcpy(odbc_table, "voicemessages");
+ if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
+ ast_copy_string(odbc_table, val, sizeof(odbc_table));
+ }
+#endif
+ /* Mail command */
+ strcpy(mailcmd, SENDMAIL);
+ if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
+ ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
+
+ maxsilence = 0;
+ if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
+ maxsilence = atoi(val);
+ if (maxsilence > 0)
+ maxsilence *= 1000;
+ }
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
+ maxmsg = MAXMSG;
+ } else {
+ maxmsg = atoi(val);
+ if (maxmsg <= 0) {
+ ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
+ maxmsg = MAXMSG;
+ } else if (maxmsg > MAXMSGLIMIT) {
+ ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
+ maxmsg = MAXMSGLIMIT;
+ }
+ }
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
+ maxdeletedmsg = 0;
+ } else {
+ if (sscanf(val, "%d", &x) == 1)
+ maxdeletedmsg = x;
+ else if (ast_true(val))
+ maxdeletedmsg = MAXMSG;
+ else
+ maxdeletedmsg = 0;
+
+ if (maxdeletedmsg < 0) {
+ ast_log(LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
+ maxdeletedmsg = MAXMSG;
+ } else if (maxdeletedmsg > MAXMSGLIMIT) {
+ ast_log(LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
+ maxdeletedmsg = MAXMSGLIMIT;
+ }
+ }
+
+ /* Load date format config for voicemail mail */
+ if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
+ ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
+ }
+
+ /* External password changing command */
+ if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
+ ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
+ pwdchange = PWDCHANGE_EXTERNAL;
+ } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
+ ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
+ pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
+ }
+
+#ifdef IMAP_STORAGE
+ /* IMAP server address */
+ if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
+ ast_copy_string(imapserver, val, sizeof(imapserver));
+ } else {
+ ast_copy_string(imapserver,"localhost", sizeof(imapserver));
+ }
+ /* IMAP server port */
+ if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
+ ast_copy_string(imapport, val, sizeof(imapport));
+ } else {
+ ast_copy_string(imapport,"143", sizeof(imapport));
+ }
+ /* IMAP server flags */
+ if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
+ ast_copy_string(imapflags, val, sizeof(imapflags));
+ }
+ /* IMAP server master username */
+ if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
+ ast_copy_string(authuser, val, sizeof(authuser));
+ }
+ /* IMAP server master password */
+ if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
+ ast_copy_string(authpassword, val, sizeof(authpassword));
+ }
+ /* Expunge on exit */
+ if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
+ if (ast_false(val))
+ expungeonhangup = 0;
+ else
+ expungeonhangup = 1;
+ } else {
+ expungeonhangup = 1;
+ }
+ /* IMAP voicemail folder */
+ if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
+ ast_copy_string(imapfolder, val, sizeof(imapfolder));
+ } else {
+ ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
+ }
+ if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
+ imapgreetings = ast_true(val);
+ } else {
+ imapgreetings = 0;
+ }
+ if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
+ ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
+ } else {
+ ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
+ }
+
+ /* There is some very unorthodox casting done here. This is due
+ * to the way c-client handles the argument passed in. It expects a
+ * void pointer and casts the pointer directly to a long without
+ * first dereferencing it. */
+ if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
+ mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
+ } else {
+ mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
+ }
+
+ if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
+ mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
+ } else {
+ mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
+ }
+
+ if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
+ mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
+ } else {
+ mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
+ }
+
+ if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
+ mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
+ } else {
+ mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
+ }
+
+#endif
+ /* External voicemail notify application */
+ if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
+ ast_copy_string(externnotify, val, sizeof(externnotify));
+ ast_debug(1, "found externnotify: %s\n", externnotify);
+ } else {
+ externnotify[0] = '\0';
+ }
+
+ /* SMDI voicemail notification */
+ if ((s = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(s)) {
+ ast_debug(1, "Enabled SMDI voicemail notification\n");
+ if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
+ smdi_iface = ast_smdi_interface_find(val);
+ } else {
+ ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
+ smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
+ }
+ if (!smdi_iface) {
+ ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
+ } else {
+ ast_debug(1, "Using SMDI port %s\n", smdi_iface->name);
+ }
+ }
+
+ /* Silence treshold */
+ silencethreshold = 256;
+ if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
+ silencethreshold = atoi(val);
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
+ val = ASTERISK_USERNAME;
+ ast_copy_string(serveremail, val, sizeof(serveremail));
+
+ vmmaxsecs = 0;
+ if ((s = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ vmmaxsecs = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid max message time length\n");
+ }
+ } else if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
+ static int maxmessage_deprecate = 0;
+ if (maxmessage_deprecate == 0) {
+ maxmessage_deprecate = 1;
+ ast_log(LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
+ }
+ if (sscanf(s, "%d", &x) == 1) {
+ vmmaxsecs = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid max message time length\n");
+ }
+ }
+
+ vmminsecs = 0;
+ if ((s = ast_variable_retrieve(cfg, "general", "minsecs"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ vmminsecs = x;
+ if (maxsilence <= vmminsecs)
+ ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
+ } else {
+ ast_log(LOG_WARNING, "Invalid min message time length\n");
+ }
+ } else if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
+ static int maxmessage_deprecate = 0;
+ if (maxmessage_deprecate == 0) {
+ maxmessage_deprecate = 1;
+ ast_log(LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
+ }
+ if (sscanf(s, "%d", &x) == 1) {
+ vmminsecs = x;
+ if (maxsilence <= vmminsecs)
+ ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
+ } else {
+ ast_log(LOG_WARNING, "Invalid min message time length\n");
+ }
+ }
+
+ val = ast_variable_retrieve(cfg, "general", "format");
+ if (!val)
+ val = "wav";
+ ast_copy_string(vmfmts, val, sizeof(vmfmts));
+
+ skipms = 3000;
+ if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ maxgreet = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid max message greeting length\n");
+ }
+ }
+
+ if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ skipms = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid skipms value\n");
+ }
+ }
+
+ maxlogins = 3;
+ if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
+ if (sscanf(s, "%d", &x) == 1) {
+ maxlogins = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
+ }
+ }
+
+ /* Force new user to record name ? */
+ if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
+ val = "no";
+ ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
+
+ /* Force new user to record greetings ? */
+ if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
+ val = "no";
+ ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
+
+ if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
+ ast_debug(1,"VM_CID Internal context string: %s\n",s);
+ stringp = ast_strdupa(s);
+ for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
+ if (!ast_strlen_zero(stringp)) {
+ q = strsep(&stringp, ",");
+ while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
+ q++;
+ ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
+ ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
+ } else {
+ cidinternalcontexts[x][0] = '\0';
+ }
+ }
+ }
+ if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
+ ast_debug(1,"VM Review Option disabled globally\n");
+ val = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
+
+ /*Temporary greeting reminder */
+ if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
+ ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
+ val = "no";
+ } else {
+ ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
+ ast_debug(1,"VM Operator break disabled globally\n");
+ val = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
+ ast_debug(1,"VM CID Info before msg disabled globally\n");
+ val = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
+
+ if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
+ ast_debug(1,"Send Voicemail msg disabled globally\n");
+ val = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
+ ast_debug(1,"ENVELOPE before msg enabled globally\n");
+ val = "yes";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
+ ast_debug(1,"Move Heard enabled globally\n");
+ val = "yes";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
+ ast_debug(1,"Duration info before msg enabled globally\n");
+ val = "yes";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
+
+ saydurationminfo = 2;
+ if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
+ if (sscanf(val, "%d", &x) == 1) {
+ saydurationminfo = x;
+ } else {
+ ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
+ }
+ }
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
+ ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
+ val = "no";
+ }
+ ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
+
+ if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
+ ast_copy_string(dialcontext, val, sizeof(dialcontext));
+ ast_debug(1, "found dialout context: %s\n", dialcontext);
+ } else {
+ dialcontext[0] = '\0';
+ }
+
+ if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
+ ast_copy_string(callcontext, val, sizeof(callcontext));
+ ast_debug(1, "found callback context: %s\n", callcontext);
+ } else {
+ callcontext[0] = '\0';
+ }
+
+ if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
+ ast_copy_string(exitcontext, val, sizeof(exitcontext));
+ ast_debug(1, "found operator context: %s\n", exitcontext);
+ } else {
+ exitcontext[0] = '\0';
+ }
+
+ /* load password sounds configuration */
+ if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
+ ast_copy_string(vm_password, val, sizeof(vm_password));
+ if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
+ ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
+ if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
+ ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
+ if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
+ ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
+ if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
+ ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
+ /* load configurable audio prompts */
+ if ((key = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(key))
+ ast_copy_string(listen_control_forward_key, key, sizeof(listen_control_forward_key));
+ if ((key = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(key))
+ ast_copy_string(listen_control_reverse_key, key, sizeof(listen_control_reverse_key));
+ if ((key = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(key))
+ ast_copy_string(listen_control_pause_key, key, sizeof(listen_control_pause_key));
+ if ((key = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(key))
+ ast_copy_string(listen_control_restart_key, key, sizeof(listen_control_restart_key));
+ if ((key = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(key))
+ ast_copy_string(listen_control_stop_key, key, sizeof(listen_control_stop_key));
+
+ if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
+ val = "no";
+ ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
+
+ poll_freq = DEFAULT_POLL_FREQ;
+ if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
+ if (sscanf(val, "%u", &poll_freq) != 1) {
+ poll_freq = DEFAULT_POLL_FREQ;
+ ast_log(LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
+ }
+ }
+
+ poll_mailboxes = 0;
+ if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
+ poll_mailboxes = ast_true(val);
+
+ if (ucfg) {
+ for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
+ if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
+ continue;
+ if ((cur = find_or_create(userscontext, cat))) {
+ populate_defaults(cur);
+ apply_options_full(cur, ast_variable_browse(ucfg, cat));
+ ast_copy_string(cur->context, userscontext, sizeof(cur->context));
+ }
+ }
+ ast_config_destroy(ucfg);
+ }
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ var = ast_variable_browse(cfg, cat);
+ if (strcasecmp(cat, "zonemessages")) {
+ /* Process mailboxes in this context */
+ while (var) {
+ append_mailbox(cat, var->name, var->value);
+ var = var->next;
+ }
+ } else {
+ /* Timezones in this context */
+ while (var) {
+ struct vm_zone *z;
+ if ((z = ast_malloc(sizeof(*z)))) {
+ char *msg_format, *timezone;
+ msg_format = ast_strdupa(var->value);
+ timezone = strsep(&msg_format, "|");
+ if (msg_format) {
+ ast_copy_string(z->name, var->name, sizeof(z->name));
+ ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
+ ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
+ AST_LIST_LOCK(&zones);
+ AST_LIST_INSERT_HEAD(&zones, z, list);
+ AST_LIST_UNLOCK(&zones);
+ } else {
+ ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
+ ast_free(z);
+ }
+ } else {
+ AST_LIST_UNLOCK(&users);
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ var = var->next;
+ }
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ memset(fromstring, 0, sizeof(fromstring));
+ memset(pagerfromstring,0,sizeof(pagerfromstring));
+ memset(emailtitle,0,sizeof(emailtitle));
+ strcpy(charset, "ISO-8859-1");
+ if (emailbody) {
+ ast_free(emailbody);
+ emailbody = NULL;
+ }
+ if (emailsubject) {
+ ast_free(emailsubject);
+ emailsubject = NULL;
+ }
+ if (pagerbody) {
+ ast_free(pagerbody);
+ pagerbody = NULL;
+ }
+ if (pagersubject) {
+ ast_free(pagersubject);
+ pagersubject = NULL;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
+ ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
+ if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
+ ast_copy_string(fromstring,s,sizeof(fromstring));
+ if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
+ ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
+ if ((s = ast_variable_retrieve(cfg, "general", "charset")))
+ ast_copy_string(charset,s,sizeof(charset));
+ if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
+ sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
+ for (x = 0; x < 4; x++) {
+ memcpy(&adsifdn[x], &tmpadsi[x], 1);
+ }
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
+ sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
+ for (x = 0; x < 4; x++) {
+ memcpy(&adsisec[x], &tmpadsi[x], 1);
+ }
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
+ if (atoi(s)) {
+ adsiver = atoi(s);
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
+ ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
+ ast_copy_string(emailtitle,s,sizeof(emailtitle));
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
+ emailsubject = ast_strdup(s);
+ if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
+ char *tmpread, *tmpwrite;
+ emailbody = ast_strdup(s);
+
+ /* substitute strings \t and \n into the appropriate characters */
+ tmpread = tmpwrite = emailbody;
+ while ((tmpwrite = strchr(tmpread,'\\'))) {
+ switch (tmpwrite[1]) {
+ case 'r':
+ memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ *tmpwrite = '\r';
+ break;
+ case 'n':
+ memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ *tmpwrite = '\n';
+ break;
+ case 't':
+ memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ *tmpwrite = '\t';
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
+ }
+ tmpread = tmpwrite + 1;
+ }
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
+ pagersubject = ast_strdup(s);
+ if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
+ char *tmpread, *tmpwrite;
+ pagerbody = ast_strdup(s);
+
+ /* substitute strings \t and \n into the appropriate characters */
+ tmpread = tmpwrite = pagerbody;
+ while ((tmpwrite = strchr(tmpread, '\\'))) {
+ switch (tmpwrite[1]) {
+ case 'r':
+ memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ *tmpwrite = '\r';
+ break;
+ case 'n':
+ memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ *tmpwrite = '\n';
+ break;
+ case 't':
+ memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
+ *tmpwrite = '\t';
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
+ }
+ tmpread = tmpwrite + 1;
+ }
+ }
+ AST_LIST_UNLOCK(&users);
+ ast_config_destroy(cfg);
+
+ if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
+ start_poll_thread();
+ if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
+ stop_poll_thread();;
+
+ return 0;
+ } else {
+ AST_LIST_UNLOCK(&users);
+ ast_log(LOG_WARNING, "Failed to load configuration file.\n");
+ if (ucfg)
+ ast_config_destroy(ucfg);
+ return 0;
+ }
+}
+
+static int reload(void)
+{
+ return load_config(1);
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+ res |= ast_unregister_application(app2);
+ res |= ast_unregister_application(app3);
+ res |= ast_unregister_application(app4);
+ res |= ast_custom_function_unregister(&mailbox_exists_acf);
+ res |= ast_manager_unregister("VoicemailUsersList");
+ ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
+ ast_uninstall_vm_functions();
+
+ if (poll_thread != AST_PTHREADT_NULL)
+ stop_poll_thread();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+ my_umask = umask(0);
+ umask(my_umask);
+
+ /* compute the location of the voicemail spool directory */
+ snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
+
+ if ((res = load_config(0)))
+ return res;
+
+ res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
+ res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
+ res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
+ res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
+ res |= ast_custom_function_register(&mailbox_exists_acf);
+ res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
+ if (res)
+ return res;
+
+ ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
+
+ ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
+
+ return res;
+}
+
+static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
+{
+ int cmd = 0;
+ char destination[80] = "";
+ int retries = 0;
+
+ if (!num) {
+ ast_verb(3, "Destination number will be entered manually\n");
+ while (retries < 3 && cmd != 't') {
+ destination[1] = '\0';
+ destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
+ if (!cmd)
+ destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
+ if (!cmd)
+ destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
+ if (!cmd) {
+ cmd = ast_waitfordigit(chan, 6000);
+ if (cmd)
+ destination[0] = cmd;
+ }
+ if (!cmd) {
+ retries++;
+ } else {
+
+ if (cmd < 0)
+ return 0;
+ if (cmd == '*') {
+ ast_verb(3, "User hit '*' to cancel outgoing call\n");
+ return 0;
+ }
+ if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
+ retries++;
+ else
+ cmd = 't';
+ }
+ }
+ if (retries >= 3) {
+ return 0;
+ }
+
+ } else {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
+ ast_copy_string(destination, num, sizeof(destination));
+ }
+
+ if (!ast_strlen_zero(destination)) {
+ if (destination[strlen(destination) -1 ] == '*')
+ return 0;
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
+ ast_copy_string(chan->exten, destination, sizeof(chan->exten));
+ ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
+ chan->priority = 0;
+ return 9;
+ }
+ return 0;
+}
+
+static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
+{
+ int res = 0;
+#ifdef IMAP_STORAGE
+ char origtimeS[256], cidN[256], cid[256], contextS[256];
+ char *header_content, *temp;
+ char buf[1024];
+#else
+ char *cid;
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+#endif
+ char filename[PATH_MAX];
+ struct ast_config *msg_cfg = NULL;
+ const char *origtime, *context;
+ char *name, *num;
+ int retries = 0;
+
+ vms->starting = 0;
+#ifdef IMAP_STORAGE
+ /* START HERE */
+
+ /* get the message info!! */
+ ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms->curmsg, vms->msgArray[vms->curmsg]);
+
+ if (!vms->msgArray[vms->curmsg]) {
+ ast_log(LOG_WARNING, "Trying to access unknown message\n");
+ return -1;
+ }
+
+ /* This will only work for new messages... */
+ if (!(header_content = mail_fetchheader(vms->mailstream, vms->msgArray[vms->curmsg])) || ast_strlen_zero(header_content)) {
+ ast_log(LOG_ERROR,"Could not fetch header for message number %ld\n", vms->msgArray[vms->curmsg]);
+ return -1;
+ }
+
+ /* Get info from headers!! */
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))))
+ ast_copy_string(cidN, temp, sizeof(cidN));
+ else
+ cidN[0] = '\0';
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))))
+ snprintf(cid, sizeof(cid), "\"%s\" <%s>",cidN, temp);
+ else
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))))
+ ast_copy_string(contextS,temp, sizeof(contextS));
+ else
+ contextS[0] = '\0';
+
+ context = &contextS[0];
+
+ if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))))
+ ast_copy_string(origtimeS,temp, sizeof(origtimeS));
+ else
+ origtimeS[0] = '\0';
+
+ origtime = &origtimeS[0];
+
+ ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
+#else
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
+
+ /* Retrieve info from VM attribute file */
+
+ make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+ snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
+ RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
+ msg_cfg = ast_config_load(filename, config_flags);
+ DISPOSE(vms->curdir, vms->curmsg);
+ if (!msg_cfg) {
+ ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
+ return 0;
+ }
+
+ if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
+ ast_config_destroy(msg_cfg);
+ return 0;
+ }
+
+ cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
+
+ context = ast_variable_retrieve(msg_cfg, "message", "context");
+ if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
+ context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
+#endif
+ switch (option) {
+ case 3:
+ if (!res)
+ res = play_message_datetime(chan, vmu, origtime, filename);
+ if (!res)
+ res = play_message_callerid(chan, vms, cid, context, 0);
+
+ res = 't';
+ break;
+
+ case 2: /* Call back */
+
+ if (ast_strlen_zero(cid))
+ break;
+
+ ast_callerid_parse(cid, &name, &num);
+ while ((res > -1) && (res != 't')) {
+ switch (res) {
+ case '1':
+ if (num) {
+ /* Dial the CID number */
+ res = dialout(chan, vmu, num, vmu->callback);
+ if (res) {
+ ast_config_destroy(msg_cfg);
+ return 9;
+ }
+ } else {
+ res = '2';
+ }
+ break;
+
+ case '2':
+ /* Want to enter a different number, can only do this if there's a dialout context for this user */
+ if (!ast_strlen_zero(vmu->dialout)) {
+ res = dialout(chan, vmu, NULL, vmu->dialout);
+ if (res) {
+ ast_config_destroy(msg_cfg);
+ return 9;
+ }
+ } else {
+ ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
+ res = ast_play_and_wait(chan, "vm-sorry");
+ }
+ ast_config_destroy(msg_cfg);
+ return res;
+ case '*':
+ res = 't';
+ break;
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+
+ res = ast_play_and_wait(chan, "vm-sorry");
+ retries++;
+ break;
+ default:
+ if (num) {
+ ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
+ res = ast_play_and_wait(chan, "vm-num-i-have");
+ if (!res)
+ res = play_message_callerid(chan, vms, num, vmu->context, 1);
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-tocallnum");
+ /* Only prompt for a caller-specified number if there is a dialout context specified */
+ if (!ast_strlen_zero(vmu->dialout)) {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-calldiffnum");
+ }
+ } else {
+ res = ast_play_and_wait(chan, "vm-nonumber");
+ if (!ast_strlen_zero(vmu->dialout)) {
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-toenternumber");
+ }
+ }
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-star-cancel");
+ if (!res)
+ res = ast_waitfordigit(chan, 6000);
+ if (!res) {
+ retries++;
+ if (retries > 3)
+ res = 't';
+ }
+ break;
+
+ }
+ if (res == 't')
+ res = 0;
+ else if (res == '*')
+ res = -1;
+ }
+ break;
+
+ case 1: /* Reply */
+ /* Send reply directly to sender */
+ if (ast_strlen_zero(cid))
+ break;
+
+ ast_callerid_parse(cid, &name, &num);
+ if (!num) {
+ ast_verb(3, "No CID number available, no reply sent\n");
+ if (!res)
+ res = ast_play_and_wait(chan, "vm-nonumber");
+ ast_config_destroy(msg_cfg);
+ return res;
+ } else {
+ struct ast_vm_user vmu2;
+ if (find_user(&vmu2, vmu->context, num)) {
+ struct leave_vm_options leave_options;
+ char mailbox[AST_MAX_EXTENSION * 2 + 2];
+ snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
+
+ ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
+
+ memset(&leave_options, 0, sizeof(leave_options));
+ leave_options.record_gain = record_gain;
+ res = leave_voicemail(chan, mailbox, &leave_options);
+ if (!res)
+ res = 't';
+ ast_config_destroy(msg_cfg);
+ return res;
+ } else {
+ /* Sender has no mailbox, can't reply */
+ ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
+ ast_play_and_wait(chan, "vm-nobox");
+ res = 't';
+ ast_config_destroy(msg_cfg);
+ return res;
+ }
+ }
+ res = 0;
+
+ break;
+ }
+
+#ifndef IMAP_STORAGE
+ ast_config_destroy(msg_cfg);
+
+ if (!res) {
+ make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
+ vms->heard[msg] = 1;
+ res = wait_file(chan, vms, vms->fn);
+ }
+#endif
+ return res;
+}
+
+static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
+ int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
+ signed char record_gain, struct vm_state *vms)
+{
+ /* Record message & let caller review or re-record it, or set options if applicable */
+ int res = 0;
+ int cmd = 0;
+ int max_attempts = 3;
+ int attempts = 0;
+ int recorded = 0;
+ int message_exists = 0;
+ signed char zero_gain = 0;
+ char tempfile[PATH_MAX];
+ char *acceptdtmf = "#";
+ char *canceldtmf = "";
+
+ /* Note that urgent and private are for flagging messages as such in the future */
+
+ /* barf if no pointer passed to store duration in */
+ if (duration == NULL) {
+ ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
+ return -1;
+ }
+
+ if (!outsidecaller)
+ snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
+ else
+ ast_copy_string(tempfile, recordfile, sizeof(tempfile));
+
+ cmd = '3'; /* Want to start by recording */
+
+ while ((cmd >= 0) && (cmd != 't')) {
+ switch (cmd) {
+ case '1':
+ if (!message_exists) {
+ /* In this case, 1 is to record a message */
+ cmd = '3';
+ break;
+ } else {
+ /* Otherwise 1 is to save the existing message */
+ ast_verb(3, "Saving message as is\n");
+ if (!outsidecaller)
+ ast_filerename(tempfile, recordfile, NULL);
+ ast_stream_and_wait(chan, "vm-msgsaved", "");
+ if (!outsidecaller) {
+ STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
+ DISPOSE(recordfile, -1);
+ }
+ cmd = 't';
+ return res;
+ }
+ case '2':
+ /* Review */
+ ast_verb(3, "Reviewing the message\n");
+ cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
+ break;
+ case '3':
+ message_exists = 0;
+ /* Record */
+ if (recorded == 1)
+ ast_verb(3, "Re-recording the message\n");
+ else
+ ast_verb(3, "Recording the message\n");
+
+ if (recorded && outsidecaller) {
+ cmd = ast_play_and_wait(chan, INTRO);
+ cmd = ast_play_and_wait(chan, "beep");
+ }
+ recorded = 1;
+ /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
+ if (ast_test_flag(vmu, VM_OPERATOR))
+ canceldtmf = "0";
+ cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
+ if (record_gain)
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
+ if (cmd == -1) {
+ /* User has hung up, no options to give */
+ if (!outsidecaller) {
+ /* user was recording a greeting and they hung up, so let's delete the recording. */
+ ast_filedelete(tempfile, NULL);
+ }
+ return cmd;
+ }
+ if (cmd == '0') {
+ break;
+ } else if (cmd == '*') {
+ break;
+ }
+#if 0
+ else if (vmu->review && (*duration < 5)) {
+ /* Message is too short */
+ ast_verb(3, "Message too short\n");
+ cmd = ast_play_and_wait(chan, "vm-tooshort");
+ cmd = ast_filedelete(tempfile, NULL);
+ break;
+ }
+ else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
+ /* Message is all silence */
+ ast_verb(3, "Nothing recorded\n");
+ cmd = ast_filedelete(tempfile, NULL);
+ cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-speakup");
+ break;
+ }
+#endif
+ else {
+ /* If all is well, a message exists */
+ message_exists = 1;
+ cmd = 0;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '*':
+ case '#':
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ break;
+#if 0
+/* XXX Commented out for the moment because of the dangers of deleting
+ a message while recording (can put the message numbers out of sync) */
+ case '*':
+ /* Cancel recording, delete message, offer to take another message*/
+ cmd = ast_play_and_wait(chan, "vm-deleted");
+ cmd = ast_filedelete(tempfile, NULL);
+ if (outsidecaller) {
+ res = vm_exec(chan, NULL);
+ return res;
+ }
+ else
+ return 1;
+#endif
+ case '0':
+ if (!ast_test_flag(vmu, VM_OPERATOR)) {
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ break;
+ }
+ if (message_exists || recorded) {
+ cmd = ast_play_and_wait(chan, "vm-saveoper");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 3000);
+ if (cmd == '1') {
+ ast_play_and_wait(chan, "vm-msgsaved");
+ cmd = '0';
+ } else {
+ ast_play_and_wait(chan, "vm-deleted");
+ DELETE(recordfile, -1, recordfile);
+ cmd = '0';
+ }
+ }
+ return cmd;
+ default:
+ /* If the caller is an ouside caller, and the review option is enabled,
+ allow them to review the message, but let the owner of the box review
+ their OGM's */
+ if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
+ return cmd;
+ if (message_exists) {
+ cmd = ast_play_and_wait(chan, "vm-review");
+ }
+ else {
+ cmd = ast_play_and_wait(chan, "vm-torerecord");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+
+ if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
+ cmd = ast_play_and_wait(chan, "vm-reachoper");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+#if 0
+ if (!cmd)
+ cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
+#endif
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 6000);
+ if (!cmd) {
+ attempts++;
+ }
+ if (attempts > max_attempts) {
+ cmd = 't';
+ }
+ }
+ }
+ if (outsidecaller)
+ ast_play_and_wait(chan, "vm-goodbye");
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+#ifdef IMAP_STORAGE
+
+static void write_file(char *filename, char *buffer, unsigned long len)
+{
+ FILE *output;
+
+ output = fopen (filename, "w");
+ fwrite (buffer, len, 1, output);
+ fclose (output);
+}
+
+static void update_messages_by_imapuser(const char *user, unsigned long number)
+{
+ struct vmstate *vlist = NULL;
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+ if (!vlist->vms) {
+ ast_debug(3, "error: vms is NULL for %s\n", user);
+ continue;
+ }
+ if (!vlist->vms->imapuser) {
+ ast_debug(3, "error: imapuser is NULL for %s\n", user);
+ continue;
+ }
+ ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vlist->vms->vmArrayIndex, vlist->vms->interactive);
+ vlist->vms->msgArray[vlist->vms->vmArrayIndex++] = number;
+ }
+ AST_LIST_UNLOCK(&vmstates);
+}
+
+void mm_searched(MAILSTREAM *stream, unsigned long number)
+{
+ char *mailbox = stream->mailbox, buf[1024] = "", *user;
+
+ if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
+ return;
+
+ update_messages_by_imapuser(user, number);
+}
+
+static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
+{
+ struct ast_variable *var;
+ struct ast_vm_user *vmu;
+
+ vmu = ast_calloc(1, sizeof *vmu);
+ if (!vmu)
+ return NULL;
+ ast_set_flag(vmu, VM_ALLOCED);
+ populate_defaults(vmu);
+
+ var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
+ if (var) {
+ apply_options_full(vmu, var);
+ ast_variables_destroy(var);
+ return vmu;
+ } else {
+ ast_free(vmu);
+ return NULL;
+ }
+}
+
+/* Interfaces to C-client */
+
+void mm_exists(MAILSTREAM * stream, unsigned long number)
+{
+ /* mail_ping will callback here if new mail! */
+ ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
+ if (number == 0) return;
+ set_update(stream);
+}
+
+
+void mm_expunged(MAILSTREAM * stream, unsigned long number)
+{
+ /* mail_ping will callback here if expunged mail! */
+ ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
+ if (number == 0) return;
+ set_update(stream);
+}
+
+
+void mm_flags(MAILSTREAM * stream, unsigned long number)
+{
+ /* mail_ping will callback here if read mail! */
+ ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
+ if (number == 0) return;
+ set_update(stream);
+}
+
+
+void mm_notify(MAILSTREAM * stream, char *string, long errflg)
+{
+ mm_log (string, errflg);
+}
+
+
+void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
+{
+ if (delimiter == '\0') {
+ delimiter = delim;
+ }
+
+ ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
+ if (attributes & LATT_NOINFERIORS)
+ ast_debug(5, "no inferiors\n");
+ if (attributes & LATT_NOSELECT)
+ ast_debug(5, "no select\n");
+ if (attributes & LATT_MARKED)
+ ast_debug(5, "marked\n");
+ if (attributes & LATT_UNMARKED)
+ ast_debug(5, "unmarked\n");
+}
+
+
+void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
+{
+ ast_debug(5, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
+ if (attributes & LATT_NOINFERIORS)
+ ast_debug(5, "no inferiors\n");
+ if (attributes & LATT_NOSELECT)
+ ast_debug(5, "no select\n");
+ if (attributes & LATT_MARKED)
+ ast_debug(5, "marked\n");
+ if (attributes & LATT_UNMARKED)
+ ast_debug(5, "unmarked\n");
+}
+
+
+void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
+{
+ ast_log (LOG_NOTICE," Mailbox %s", mailbox);
+ if (status->flags & SA_MESSAGES)
+ ast_log (LOG_NOTICE,", %lu messages", status->messages);
+ if (status->flags & SA_RECENT)
+ ast_log (LOG_NOTICE,", %lu recent", status->recent);
+ if (status->flags & SA_UNSEEN)
+ ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
+ if (status->flags & SA_UIDVALIDITY)
+ ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
+ if (status->flags & SA_UIDNEXT)
+ ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
+ ast_log (LOG_NOTICE,"\n");
+}
+
+
+void mm_log(char *string, long errflg)
+{
+ switch ((short) errflg) {
+ case NIL:
+ ast_debug(1,"IMAP Info: %s\n", string);
+ break;
+ case PARSE:
+ case WARN:
+ ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
+ break;
+ case ERROR:
+ ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
+ break;
+ }
+}
+
+
+void mm_dlog(char *string)
+{
+ ast_log (LOG_NOTICE, "%s\n", string);
+}
+
+
+void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
+{
+ struct ast_vm_user *vmu;
+
+ ast_debug(4, "Entering callback mm_login\n");
+
+ ast_copy_string(user, mb->user, MAILTMPLEN);
+
+ /* We should only do this when necessary */
+ if (!ast_strlen_zero(authpassword)) {
+ ast_copy_string(pwd, authpassword, MAILTMPLEN);
+ } else {
+ AST_LIST_TRAVERSE(&users, vmu, list) {
+ if (!strcasecmp(mb->user, vmu->imapuser)) {
+ ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
+ break;
+ }
+ }
+ if (!vmu) {
+ if ((vmu = find_user_realtime_imapuser(mb->user))) {
+ ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
+ free_user(vmu);
+ }
+ }
+ }
+}
+
+
+void mm_critical(MAILSTREAM * stream)
+{
+}
+
+
+void mm_nocritical(MAILSTREAM * stream)
+{
+}
+
+
+long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
+{
+ kill (getpid (), SIGSTOP);
+ return NIL;
+}
+
+
+void mm_fatal(char *string)
+{
+ ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
+}
+
+/* C-client callback to handle quota */
+static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
+{
+ struct vm_state *vms;
+ char *mailbox = stream->mailbox, *user;
+ char buf[1024] = "";
+ unsigned long usage = 0, limit = 0;
+
+ while (pquota) {
+ usage = pquota->usage;
+ limit = pquota->limit;
+ pquota = pquota->next;
+ }
+
+ if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 2))) {
+ ast_log(LOG_ERROR, "No state found.\n");
+ return;
+ }
+
+ ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
+
+ vms->quota_usage = usage;
+ vms->quota_limit = limit;
+}
+
+static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
+{
+ char *start, *eol_pnt;
+ int taglen;
+
+ if (ast_strlen_zero(header) || ast_strlen_zero(tag))
+ return NULL;
+
+ taglen = strlen(tag) + 1;
+ if (taglen < 1)
+ return NULL;
+
+ if (!(start = strstr(header, tag)))
+ return NULL;
+
+ /* Since we can be called multiple times we should clear our buffer */
+ memset(buf, 0, len);
+
+ ast_copy_string(buf, start+taglen, len);
+ if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
+ *eol_pnt = '\0';
+ return buf;
+}
+
+static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
+{
+ char *start, *quote, *eol_pnt;
+
+ if (ast_strlen_zero(mailbox))
+ return NULL;
+
+ if (!(start = strstr(mailbox, "/user=")))
+ return NULL;
+
+ ast_copy_string(buf, start+6, len);
+
+ if (!(quote = strchr(buf, '\"'))) {
+ if (!(eol_pnt = strchr(buf, '/')))
+ eol_pnt = strchr(buf,'}');
+ *eol_pnt = '\0';
+ return buf;
+ } else {
+ eol_pnt = strchr(buf+1,'\"');
+ *eol_pnt = '\0';
+ return buf+1;
+ }
+}
+
+static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
+{
+ struct vmstate *vlist = NULL;
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+ if (!vlist->vms) {
+ ast_debug(3, "error: vms is NULL for %s\n", user);
+ continue;
+ }
+ if (!vlist->vms->imapuser) {
+ ast_debug(3, "error: imapuser is NULL for %s\n", user);
+ continue;
+ }
+
+ if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
+ AST_LIST_UNLOCK(&vmstates);
+ return vlist->vms;
+ }
+ }
+ AST_LIST_UNLOCK(&vmstates);
+
+ ast_debug(3, "%s not found in vmstates\n", user);
+
+ return NULL;
+}
+
+static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
+{
+
+ struct vmstate *vlist = NULL;
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+ if (!vlist->vms) {
+ ast_debug(3, "error: vms is NULL for %s\n", mailbox);
+ continue;
+ }
+ if (!vlist->vms->username) {
+ ast_debug(3, "error: username is NULL for %s\n", mailbox);
+ continue;
+ }
+
+ ast_debug(3, "comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n", mailbox, interactive, vlist->vms->username, vlist->vms->interactive);
+
+ if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
+ ast_debug(3, "Found it!\n");
+ AST_LIST_UNLOCK(&vmstates);
+ return vlist->vms;
+ }
+ }
+ AST_LIST_UNLOCK(&vmstates);
+
+ ast_debug(3, "%s not found in vmstates\n", mailbox);
+
+ return NULL;
+}
+
+static void vmstate_insert(struct vm_state *vms)
+{
+ struct vmstate *v;
+ struct vm_state *altvms;
+
+ /* If interactive, it probably already exists, and we should
+ use the one we already have since it is more up to date.
+ We can compare the username to find the duplicate */
+ if (vms->interactive == 1) {
+ altvms = get_vm_state_by_mailbox(vms->username,0);
+ if (altvms) {
+ ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
+ vms->newmessages = altvms->newmessages;
+ vms->oldmessages = altvms->oldmessages;
+ ast_debug(3, "check_msgArray before memcpy\n");
+ check_msgArray(vms);
+ /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
+ copy_msgArray(vms, altvms);
+ ast_debug(3, "check_msgArray after memcpy\n");
+ check_msgArray(vms);
+ vms->vmArrayIndex = altvms->vmArrayIndex;
+ vms->lastmsg = altvms->lastmsg;
+ vms->curmsg = altvms->curmsg;
+ /* get a pointer to the persistent store */
+ vms->persist_vms = altvms;
+ /* Reuse the mailstream? */
+ vms->mailstream = altvms->mailstream;
+ /* vms->mailstream = NIL; */
+ }
+ }
+
+ if (!(v = ast_calloc(1, sizeof(*v))))
+ return;
+
+ v->vms = vms;
+
+ ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_INSERT_TAIL(&vmstates, v, list);
+ AST_LIST_UNLOCK(&vmstates);
+}
+
+static void vmstate_delete(struct vm_state *vms)
+{
+ struct vmstate *vc = NULL;
+ struct vm_state *altvms = NULL;
+
+ /* If interactive, we should copy pertinent info
+ back to the persistent state (to make update immediate) */
+ if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
+ ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
+ altvms->newmessages = vms->newmessages;
+ altvms->oldmessages = vms->oldmessages;
+ altvms->updated = 1;
+ }
+
+ ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
+ if (vc->vms == vms) {
+ AST_LIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&vmstates);
+
+ if (vc)
+ ast_free(vc);
+ else
+ ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
+}
+
+static void set_update(MAILSTREAM * stream)
+{
+ struct vm_state *vms;
+ char *mailbox = stream->mailbox, *user;
+ char buf[1024] = "";
+
+ if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
+ if (user && option_debug > 2)
+ ast_log(LOG_WARNING, "User %s mailbox not found for update.\n", user);
+ return;
+ }
+
+ ast_debug(3, "User %s mailbox set for update.\n", user);
+
+ vms->updated = 1; /* Set updated flag since mailbox changed */
+}
+
+static void init_vm_state(struct vm_state *vms)
+{
+ int x;
+ vms->vmArrayIndex = 0;
+ for (x = 0; x < 256; x++) {
+ vms->msgArray[x] = 0;
+ }
+}
+
+static void check_msgArray(struct vm_state *vms)
+{
+ int x;
+ for (x = 0; x<256; x++) {
+ if (vms->msgArray[x]!=0) {
+ ast_debug(1, "Item %d set to %ld\n",x,vms->msgArray[x]);
+ }
+ }
+}
+
+static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
+{
+ int x;
+ for (x = 0; x<256; x++) {
+ dst->msgArray[x] = src->msgArray[x];
+ }
+}
+
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
+{
+ char *body_content;
+ char *body_decoded;
+ unsigned long len;
+ unsigned long newlen;
+ char filename[256];
+
+ if (!body || body == NIL)
+ return -1;
+ body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
+ if (body_content != NIL) {
+ snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
+ /* ast_debug(1,body_content); */
+ body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
+ write_file (filename, (char *) body_decoded, newlen);
+ }
+ return 0;
+}
+
+/* get delimiter via mm_list callback */
+static void get_mailbox_delimiter(MAILSTREAM *stream) {
+ char tmp[50];
+ snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
+ mail_list(stream, tmp, "*");
+}
+
+/* Check Quota for user */
+static void check_quota(struct vm_state *vms, char *mailbox) {
+ mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
+ ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
+ if (vms && vms->mailstream != NULL) {
+ imap_getquotaroot(vms->mailstream, mailbox);
+ } else {
+ ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
+ }
+}
+
+#endif /* IMAP_STORAGE */
+
+/* This is a workaround so that menuselect displays a proper description
+ * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
+ */
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/apps/app_waitforring.c b/trunk/apps/app_waitforring.c
new file mode 100644
index 000000000..b4248fee9
--- /dev/null
+++ b/trunk/apps/app_waitforring.c
@@ -0,0 +1,117 @@
+/*
+ * 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 Wait for Ring Application
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+
+static char *synopsis = "Wait for Ring Application";
+
+static char *desc = " WaitForRing(timeout):\n"
+"Returns 0 after waiting at least timeout seconds. and\n"
+"only after the next ring has completed. Returns 0 on\n"
+"success or -1 on hangup\n";
+
+static char *app = "WaitForRing";
+
+
+static int waitforring_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_frame *f;
+ int res = 0;
+ double s;
+ int ms;
+
+ if (!data || (sscanf(data, "%lg", &s) != 1)) {
+ ast_log(LOG_WARNING, "WaitForRing requires an argument (minimum seconds)\n");
+ return 0;
+ }
+
+ ms = s*1000.0;
+ while(ms > 0) {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ res = ms;
+ break;
+ }
+ if (ms > 0) {
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
+ ast_verb(3, "Got a ring but still waiting for timeout\n");
+ }
+ ast_frfree(f);
+ }
+ }
+ /* Now we're really ready for the ring */
+ if (!res) {
+ ms = 99999999;
+ while(ms > 0) {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ res = ms;
+ break;
+ }
+ if (ms > 0) {
+ f = ast_read(chan);
+ if (!f) {
+ res = -1;
+ break;
+ }
+ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
+ ast_verb(3, "Got a ring after the timeout\n");
+ ast_frfree(f);
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, waitforring_exec, synopsis, desc);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Waits until first ring after time");
diff --git a/trunk/apps/app_waitforsilence.c b/trunk/apps/app_waitforsilence.c
new file mode 100644
index 000000000..e45f24344
--- /dev/null
+++ b/trunk/apps/app_waitforsilence.c
@@ -0,0 +1,189 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * WaitForSilence Application by David C. Troy <dave@popvox.com>
+ * Version 1.11 2006-06-29
+ *
+ * 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 Wait for Silence
+ * - Waits for up to 'x' milliseconds of silence, 'y' times \n
+ * - WaitForSilence(500,2) will wait for 1/2 second of silence, twice \n
+ * - WaitForSilence(1000,1) will wait for 1 second of silence, once \n
+ * - WaitForSilence(300,3,10) will wait for 300ms of silence, 3 times, and return after 10sec \n
+ *
+ * \author David C. Troy <dave@popvox.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/dsp.h"
+#include "asterisk/module.h"
+
+static char *app = "WaitForSilence";
+static char *synopsis = "Waits for a specified amount of silence";
+static char *descrip =
+" WaitForSilence(silencerequired[,iterations][,timeout]):\n"
+"Wait for Silence: Waits for up to 'silencerequired' \n"
+"milliseconds of silence, 'iterations' times or once if omitted.\n"
+"An optional timeout specified the number of seconds to return\n"
+"after, even if we do not receive the specified amount of silence.\n"
+"Use 'timeout' with caution, as it may defeat the purpose of this\n"
+"application, which is to wait indefinitely until silence is detected\n"
+"on the line. This is particularly useful for reverse-911-type\n"
+"call broadcast applications where you need to wait for an answering\n"
+"machine to complete its spiel before playing a message.\n"
+"The timeout parameter is specified only to avoid an infinite loop in\n"
+"cases where silence is never achieved. Typically you will want to\n"
+"include two or more calls to WaitForSilence when dealing with an answering\n"
+"machine; first waiting for the spiel to finish, then waiting for the beep, etc.\n\n"
+ "Examples:\n"
+" - WaitForSilence(500,2) will wait for 1/2 second of silence, twice\n"
+" - WaitForSilence(1000) will wait for 1 second of silence, once\n"
+" - WaitForSilence(300,3,10) will wait for 300ms silence, 3 times,\n"
+" and returns after 10 sec, even if silence is not detected\n\n"
+"Sets the channel variable WAITSTATUS with to one of these values:\n"
+"SILENCE - if exited with silence detected\n"
+"TIMEOUT - if exited without silence detected after timeout\n";
+
+static int do_waiting(struct ast_channel *chan, int silencereqd, time_t waitstart, int timeout) {
+ struct ast_frame *f;
+ int dspsilence = 0;
+ static int silencethreshold = 128;
+ int rfmt = 0;
+ int res = 0;
+ struct ast_dsp *sildet; /* silence detector dsp */
+ time_t now;
+
+ rfmt = chan->readformat; /* Set to linear mode */
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set channel to linear mode, giving up\n");
+ return -1;
+ }
+
+ sildet = ast_dsp_new(); /* Create the silence detector */
+ if (!sildet) {
+ ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+ return -1;
+ }
+ ast_dsp_set_threshold(sildet, silencethreshold);
+
+ /* Await silence... */
+ f = NULL;
+ for(;;) {
+ /* Start with no silence received */
+ dspsilence = 0;
+
+ res = ast_waitfor(chan, silencereqd);
+
+ /* Must have gotten a hangup; let's exit */
+ if (res <= 0) {
+ f = NULL;
+ break;
+ }
+
+ /* We waited and got no frame; sounds like digital silence or a muted digital channel */
+ if (!res) {
+ dspsilence = silencereqd;
+ } else {
+ /* Looks like we did get a frame, so let's check it out */
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f && f->frametype == AST_FRAME_VOICE) {
+ ast_dsp_silence(sildet, f, &dspsilence);
+ ast_frfree(f);
+ }
+ }
+
+ ast_verb(3, "Got %dms silence< %dms required\n", dspsilence, silencereqd);
+
+ if (dspsilence >= silencereqd) {
+ ast_verb(3, "Exiting with %dms silence >= %dms required\n", dspsilence, silencereqd);
+ /* Ended happily with silence */
+ res = 1;
+ pbx_builtin_setvar_helper(chan, "WAITSTATUS", "SILENCE");
+ ast_debug(1, "WAITSTATUS was set to SILENCE\n");
+ break;
+ }
+
+ if ( timeout && (difftime(time(&now),waitstart) >= timeout) ) {
+ pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT");
+ ast_debug(1, "WAITSTATUS was set to TIMEOUT\n");
+ res = 0;
+ break;
+ }
+ }
+
+
+ if (rfmt && ast_set_read_format(chan, rfmt)) {
+ ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
+ }
+ ast_dsp_free(sildet);
+ return res;
+}
+
+static int waitforsilence_exec(struct ast_channel *chan, void *data)
+{
+ int res = 1;
+ int silencereqd = 1000;
+ int timeout = 0;
+ int iterations = 1, i;
+ time_t waitstart;
+
+ res = ast_answer(chan); /* Answer the channel */
+
+ if (!data || ( (sscanf(data, "%d,%d,%d", &silencereqd, &iterations, &timeout) != 3) &&
+ (sscanf(data, "%d|%d", &silencereqd, &iterations) != 2) &&
+ (sscanf(data, "%d", &silencereqd) != 1) ) ) {
+ ast_log(LOG_WARNING, "Using default value of 1000ms, 1 iteration, no timeout\n");
+ }
+
+ ast_verb(3, "Waiting %d time(s) for %d ms silence with %d timeout\n", iterations, silencereqd, timeout);
+
+ time(&waitstart);
+ res = 1;
+ for (i=0; (i<iterations) && (res == 1); i++) {
+ res = do_waiting(chan, silencereqd, waitstart, timeout);
+ }
+ if (res > 0)
+ res = 0;
+ return res;
+}
+
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, waitforsilence_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Wait For Silence");
+
diff --git a/trunk/apps/app_waituntil.c b/trunk/apps/app_waituntil.c
new file mode 100644
index 000000000..1334683ce
--- /dev/null
+++ b/trunk/apps/app_waituntil.c
@@ -0,0 +1,93 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Redfish Solutions
+ *
+ * Philip Prindeville <philipp@redfish-solutions.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 Sleep until the given epoch
+ *
+ * \author Philip Prindeville <philipp@redfish-solutions.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+static char *app = "WaitUntil";
+static char *synopsis = "Wait (sleep) until the current time is the given epoch";
+static char *descrip =
+" WaitUntil(<epoch>): Waits until the given time. Sets WAITUNTILSTATUS to\n"
+"one of the following values:\n"
+" OK Wait succeeded\n"
+" FAILURE Invalid argument\n"
+" HANGUP Channel hung up before time elapsed\n"
+" PAST The time specified was already past\n";
+
+static int waituntil_exec(struct ast_channel *chan, void *data)
+{
+ int res;
+ double fraction;
+ struct timeval future = { 0, };
+ struct timeval tv = ast_tvnow();
+ int msec;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "WaitUntil requires an argument(epoch)\n");
+ pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "FAILURE");
+ return 0;
+ }
+
+ if (sscanf(data, "%ld%lf", (long *)&future.tv_sec, &fraction) == 0) {
+ ast_log(LOG_WARNING, "WaitUntil called with non-numeric argument\n");
+ pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "FAILURE");
+ return 0;
+ }
+
+ future.tv_usec = fraction * 1000000;
+
+ if ((msec = ast_tvdiff_ms(future, tv)) < 0) {
+ ast_log(LOG_NOTICE, "WaitUntil called in the past (now %ld, arg %ld)\n", (long)tv.tv_sec, (long)future.tv_sec);
+ pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "PAST");
+ return 0;
+ }
+
+ if ((res = ast_safe_sleep(chan, msec)))
+ pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "HANGUP");
+ else
+ pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "OK");
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, waituntil_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Wait until specified time");
diff --git a/trunk/apps/app_while.c b/trunk/apps/app_while.c
new file mode 100644
index 000000000..1c61d966e
--- /dev/null
+++ b/trunk/apps/app_while.c
@@ -0,0 +1,307 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2004 - 2005, Anthony Minessale <anthmct@yahoo.com>
+ *
+ * Anthony Minessale <anthmct@yahoo.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 While Loop Implementation
+ *
+ * \author Anthony Minessale <anthmct@yahoo.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+
+static char *start_app = "While";
+static char *start_desc =
+" While(<expr>): Start a While Loop. Execution will return to this\n"
+"point when EndWhile() is called until expr is no longer true.\n";
+
+static char *start_synopsis = "Start a while loop";
+
+
+static char *stop_app = "EndWhile";
+static char *stop_desc =
+" EndWhile(): Return to the previous called While()\n";
+
+static char *stop_synopsis = "End a while loop";
+
+static char *exit_app = "ExitWhile";
+static char *exit_desc =
+" ExitWhile(): Exits a While() loop, whether or not the conditional has been satisfied.\n";
+static char *exit_synopsis = "End a While loop";
+
+static char *continue_app = "ContinueWhile";
+static char *continue_desc =
+" ContinueWhile(): Returns to the top of the while loop and re-evaluates the conditional.\n";
+static char *continue_synopsis = "Restart a While loop";
+
+#define VAR_SIZE 64
+
+
+static const char *get_index(struct ast_channel *chan, const char *prefix, int index) {
+ char varname[VAR_SIZE];
+
+ snprintf(varname, VAR_SIZE, "%s_%d", prefix, index);
+ return pbx_builtin_getvar_helper(chan, varname);
+}
+
+static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
+{
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_context *c2;
+
+ for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
+ if (ast_extension_match(ast_get_extension_name(e), exten)) {
+ int needmatch = ast_get_extension_matchcid(e);
+ if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
+ (!needmatch)) {
+ /* This is the matching extension we want */
+ struct ast_exten *p;
+ for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
+ if (priority != ast_get_extension_priority(p))
+ continue;
+ return p;
+ }
+ }
+ }
+ }
+
+ /* No match; run through includes */
+ for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
+ for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
+ if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
+ e = find_matching_priority(c2, exten, priority, callerid);
+ if (e)
+ return e;
+ }
+ }
+ }
+ return NULL;
+}
+
+static int find_matching_endwhile(struct ast_channel *chan)
+{
+ struct ast_context *c;
+ int res=-1;
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock contexts list\n");
+ return -1;
+ }
+
+ for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
+ struct ast_exten *e;
+
+ if (!ast_rdlock_context(c)) {
+ if (!strcmp(ast_get_context_name(c), chan->context)) {
+ /* This is the matching context we want */
+ int cur_priority = chan->priority + 1, level=1;
+
+ for (e = find_matching_priority(c, chan->exten, cur_priority, chan->cid.cid_num); e; e = find_matching_priority(c, chan->exten, ++cur_priority, chan->cid.cid_num)) {
+ if (!strcasecmp(ast_get_extension_app(e), "WHILE")) {
+ level++;
+ } else if (!strcasecmp(ast_get_extension_app(e), "ENDWHILE")) {
+ level--;
+ }
+
+ if (level == 0) {
+ res = cur_priority;
+ break;
+ }
+ }
+ }
+ ast_unlock_context(c);
+ if (res > 0) {
+ break;
+ }
+ }
+ }
+ ast_unlock_contexts();
+ return res;
+}
+
+static int _while_exec(struct ast_channel *chan, void *data, int end)
+{
+ int res=0;
+ const char *while_pri = NULL;
+ char *my_name = NULL;
+ const char *condition = NULL, *label = NULL;
+ char varname[VAR_SIZE], end_varname[VAR_SIZE];
+ const char *prefix = "WHILE";
+ size_t size=0;
+ int used_index_i = -1, x=0;
+ char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
+
+ if (!chan) {
+ /* huh ? */
+ return -1;
+ }
+
+ /* dont want run away loops if the chan isn't even up
+ this is up for debate since it slows things down a tad ......
+ */
+ if (ast_waitfordigit(chan,1) < 0)
+ return -1;
+
+ for (x=0;;x++) {
+ if (get_index(chan, prefix, x)) {
+ used_index_i = x;
+ } else
+ break;
+ }
+
+ snprintf(used_index, VAR_SIZE, "%d", used_index_i);
+ snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
+
+ if (!end)
+ condition = ast_strdupa(data);
+
+ size = strlen(chan->context) + strlen(chan->exten) + 32;
+ my_name = alloca(size);
+ memset(my_name, 0, size);
+ snprintf(my_name, size, "%s_%s_%d", chan->context, chan->exten, chan->priority);
+
+ if (ast_strlen_zero(label)) {
+ if (end)
+ label = used_index;
+ else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
+ label = new_index;
+ pbx_builtin_setvar_helper(chan, my_name, label);
+ }
+
+ }
+
+ snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
+ while_pri = pbx_builtin_getvar_helper(chan, varname);
+
+ if ((while_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
+ snprintf(end_varname,VAR_SIZE,"END_%s",varname);
+ }
+
+
+ if ((!end && !pbx_checkcondition(condition)) || (end == 2)) {
+ /* Condition Met (clean up helper vars) */
+ const char *goto_str;
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ pbx_builtin_setvar_helper(chan, my_name, NULL);
+ snprintf(end_varname,VAR_SIZE,"END_%s",varname);
+ if ((goto_str=pbx_builtin_getvar_helper(chan, end_varname))) {
+ ast_parseable_goto(chan, goto_str);
+ pbx_builtin_setvar_helper(chan, end_varname, NULL);
+ } else {
+ int pri = find_matching_endwhile(chan);
+ if (pri > 0) {
+ ast_verb(3, "Jumping to priority %d\n", pri);
+ chan->priority = pri;
+ } else {
+ ast_log(LOG_WARNING, "Couldn't find matching EndWhile? (While at %s@%s priority %d)\n", chan->context, chan->exten, chan->priority);
+ }
+ }
+ return res;
+ }
+
+ if (!end && !while_pri) {
+ char *goto_str;
+ size = strlen(chan->context) + strlen(chan->exten) + 32;
+ goto_str = alloca(size);
+ memset(goto_str, 0, size);
+ snprintf(goto_str, size, "%s,%s,%d", chan->context, chan->exten, chan->priority);
+ pbx_builtin_setvar_helper(chan, varname, goto_str);
+ }
+
+ else if (end && while_pri) {
+ /* END of loop */
+ snprintf(end_varname, VAR_SIZE, "END_%s", varname);
+ if (! pbx_builtin_getvar_helper(chan, end_varname)) {
+ char *goto_str;
+ size = strlen(chan->context) + strlen(chan->exten) + 32;
+ goto_str = alloca(size);
+ memset(goto_str, 0, size);
+ snprintf(goto_str, size, "%s,%s,%d", chan->context, chan->exten, chan->priority+1);
+ pbx_builtin_setvar_helper(chan, end_varname, goto_str);
+ }
+ ast_parseable_goto(chan, while_pri);
+ }
+
+ return res;
+}
+
+static int while_start_exec(struct ast_channel *chan, void *data) {
+ return _while_exec(chan, data, 0);
+}
+
+static int while_end_exec(struct ast_channel *chan, void *data) {
+ return _while_exec(chan, data, 1);
+}
+
+static int while_exit_exec(struct ast_channel *chan, void *data) {
+ return _while_exec(chan, data, 2);
+}
+
+static int while_continue_exec(struct ast_channel *chan, void *data)
+{
+ int x;
+ const char *prefix = "WHILE", *while_pri=NULL;
+
+ for (x = 0; ; x++) {
+ const char *tmp = get_index(chan, prefix, x);
+ if (tmp)
+ while_pri = tmp;
+ else
+ break;
+ }
+
+ if (while_pri)
+ ast_parseable_goto(chan, while_pri);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(start_app);
+ res |= ast_unregister_application(stop_app);
+ res |= ast_unregister_application(exit_app);
+ res |= ast_unregister_application(continue_app);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(start_app, while_start_exec, start_synopsis, start_desc);
+ res |= ast_register_application(stop_app, while_end_exec, stop_synopsis, stop_desc);
+ res |= ast_register_application(exit_app, while_exit_exec, exit_synopsis, exit_desc);
+ res |= ast_register_application(continue_app, while_continue_exec, continue_synopsis, continue_desc);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "While Loops and Conditional Execution");
diff --git a/trunk/apps/app_zapateller.c b/trunk/apps/app_zapateller.c
new file mode 100644
index 000000000..2641e69b4
--- /dev/null
+++ b/trunk/apps/app_zapateller.c
@@ -0,0 +1,113 @@
+/*
+ * 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 Playback the special information tone to get rid of telemarketers
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/app.h"
+
+static char *app = "Zapateller";
+
+static char *synopsis = "Block telemarketers with SIT";
+
+static char *descrip =
+" Zapateller(options): Generates special information tone to block\n"
+"telemarketers from calling you. Options is a pipe-delimited list of\n"
+"options. The following options are available:\n"
+" 'answer' - causes the line to be answered before playing the tone,\n"
+" 'nocallerid' - causes Zapateller to only play the tone if there is no\n"
+" callerid information available. Options should be\n"
+" separated by , characters\n\n"
+" This application will set the following channel variable upon completion:\n"
+" ZAPATELLERSTATUS - This will contain the last action accomplished by the\n"
+" Zapateller application. Possible values include:\n"
+" NOTHING | ANSWERED | ZAPPED\n\n";
+
+
+static int zapateller_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int i, answer = 0, nocallerid = 0;
+ char *parse = ast_strdupa((char *)data);
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(options)[2];
+ );
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ for (i = 0; i < args.argc; i++) {
+ if (!strcasecmp(args.options[i], "answer"))
+ answer = 1;
+ else if (!strcasecmp(args.options[i], "nocallerid"))
+ nocallerid = 1;
+ }
+
+ pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "NOTHING");
+ ast_stopstream(chan);
+ if (chan->_state != AST_STATE_UP) {
+ if (answer) {
+ res = ast_answer(chan);
+ pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "ANSWERED");
+ }
+ if (!res)
+ res = ast_safe_sleep(chan, 500);
+ }
+
+ if (!ast_strlen_zero(chan->cid.cid_num) && nocallerid)
+ return res;
+
+ if (!res)
+ res = ast_tonepair(chan, 950, 0, 330, 0);
+ if (!res)
+ res = ast_tonepair(chan, 1400, 0, 330, 0);
+ if (!res)
+ res = ast_tonepair(chan, 1800, 0, 330, 0);
+ if (!res)
+ res = ast_tonepair(chan, 0, 0, 1000, 0);
+
+ pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "ZAPPED");
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ((ast_register_application(app, zapateller_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Block Telemarketers with Special Information Tone");
diff --git a/trunk/apps/app_zapbarge.c b/trunk/apps/app_zapbarge.c
new file mode 100644
index 000000000..bb8c4cbb3
--- /dev/null
+++ b/trunk/apps/app_zapbarge.c
@@ -0,0 +1,299 @@
+/*
+ * 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>zaptel</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/zapata.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+
+static char *app = "ZapBarge";
+
+static char *synopsis = "Barge in (monitor) Zap channel";
+
+static char *descrip =
+" ZapBarge([channel]): Barges in on a specified zap\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 zt_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;
+
+ ZT_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("/dev/zap/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 = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ if (ioctl(fd, ZT_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, ZT_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_debug(1, "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 = ZT_CONF_MONITORBOTH;
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ ast_debug(1, "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_debug(1, "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, ZT_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;
+ int retrycnt = 0;
+ int confflags = 0;
+ int confno = 0;
+ char confstr[80] = "";
+
+ if (!ast_strlen_zero(data)) {
+ if ((sscanf(data, "Zap/%d", &confno) != 1) &&
+ (sscanf(data, "%d", &confno) != 1)) {
+ ast_log(LOG_WARNING, "ZapBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
+ 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 */
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on Zap channel application");
diff --git a/trunk/apps/app_zapras.c b/trunk/apps/app_zapras.c
new file mode 100644
index 000000000..480e5f643
--- /dev/null
+++ b/trunk/apps/app_zapras.c
@@ -0,0 +1,238 @@
+/*
+ * 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>zaptel</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 <fcntl.h>
+
+#include "asterisk/zapata.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+
+static char *app = "ZapRAS";
+
+static char *synopsis = "Executes Zaptel ISDN RAS application";
+
+static char *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 be patched to be zaptel aware. 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 zaptel 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";
+ argv[argc++] = "zaptel.so";
+ 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 zt_bufferinfo savebi;
+ int x;
+
+ res = ioctl(chan->fds[0], ZT_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 (ast_check_hangup(chan) && !signalled) {
+ ast_debug(1, "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], ZT_AUDIOMODE, &x);
+
+ /* Restore saved values */
+ res = ioctl(chan->fds[0], ZT_SET_BUFINFO, &savebi);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name);
+ }
+ break;
+ }
+ }
+}
+
+static int zapras_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ char *args;
+ ZT_PARAMS ztp;
+
+ if (!data)
+ data = "";
+
+ 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, "Zap")) {
+ /* If it's not a zap channel, we're done. Wait a couple of
+ seconds and then hangup... */
+ ast_verb(2, "Channel %s is not a Zap channel\n", chan->name);
+ sleep(2);
+ } else {
+ memset(&ztp, 0, sizeof(ztp));
+ if (ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp)) {
+ ast_log(LOG_WARNING, "Unable to get zaptel parameters\n");
+ } else if (ztp.sigtype != ZT_SIG_CLEAR) {
+ ast_verb(2, "Channel %s is not a clear channel\n", chan->name);
+ } else {
+ /* Everything should be okay. Run PPP. */
+ ast_verb(3, "Starting RAS on %s\n", chan->name);
+ /* Execute RAS */
+ run_ras(chan, args);
+ }
+ }
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ((ast_register_application(app, zapras_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Zaptel ISDN Remote Access Server");
+
diff --git a/trunk/apps/app_zapscan.c b/trunk/apps/app_zapscan.c
new file mode 100644
index 000000000..5367d99d1
--- /dev/null
+++ b/trunk/apps/app_zapscan.c
@@ -0,0 +1,363 @@
+/*
+ * 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>zaptel</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/zapata.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+
+static char *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),"Zap/%d-1",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 zt_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;
+
+ ZT_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("/dev/zap/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 = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ if (ioctl(fd, ZT_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, ZT_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_debug(1, "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 = ZT_CONF_MONITORBOTH;
+
+ if (ioctl(fd, ZT_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ ast_debug(1, "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_debug(1, "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_verb(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, ZT_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;
+ 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;
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ desired_group = ast_strdupa(data);
+ if(!ast_strlen_zero(desired_group)) {
+ ast_verb(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_verb(3, "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
+ } else {
+ ast_channel_unlock(tempchan);
+ lastchan = tempchan;
+ continue;
+ }
+ }
+ if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
+ ast_verb(3, "Zap channel %s is in-use, monitoring...\n", tempchan->name);
+ ast_copy_string(confstr, tempchan->name, sizeof(confstr));
+ ast_channel_unlock(tempchan);
+ 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_channel_unlock(tempchan);
+ lastchan = tempchan;
+ }
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");
+
diff --git a/trunk/apps/enter.h b/trunk/apps/enter.h
new file mode 100644
index 000000000..ac765984a
--- /dev/null
+++ b/trunk/apps/enter.h
@@ -0,0 +1,287 @@
+/*
+ * U-law 8-bit audio data
+ *
+ * Source: enter.raw
+ *
+ * Copyright (C) 1999, Mark Spencer and Linux Support Services
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char enter[] = {
+0xba, 0xba, 0xb0, 0xa6, 0xa9, 0xb8, 0xfe, 0x46, 0x42, 0x46,
+0x4a, 0xfe, 0xac, 0xa2, 0x9f, 0x9f, 0xa8, 0xb8, 0x3b, 0x29,
+0x35, 0x4a, 0xfe, 0xc1, 0xad, 0xa2, 0xad, 0xc5, 0x4e, 0x68,
+0x68, 0xe7, 0xb8, 0xb0, 0xb2, 0xc1, 0xc1, 0xb0, 0xae, 0xcd,
+0xfe, 0xfe, 0xcd, 0xcd, 0xfe, 0x68, 0xd3, 0xb2, 0xae, 0xab,
+0xb2, 0xfe, 0x35, 0x31, 0xdb, 0xac, 0xab, 0xaf, 0xab, 0xaa,
+0xb4, 0x68, 0x3b, 0x39, 0x3f, 0x68, 0xb4, 0xa8, 0xa8, 0xb0,
+0xbc, 0xbc, 0xc5, 0x3f, 0x31, 0x37, 0xfe, 0xc1, 0xbc, 0xb0,
+0xa5, 0xa2, 0xa8, 0xaf, 0xbe, 0x3b, 0x28, 0x26, 0x3d, 0xbc,
+0xb0, 0xae, 0xa2, 0x9f, 0xa2, 0xfe, 0x29, 0x24, 0x29, 0x4a,
+0xc5, 0xaa, 0xa8, 0xa9, 0xa8, 0xa5, 0xa7, 0xdb, 0x2c, 0x27,
+0x2d, 0x4a, 0xfe, 0xdb, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0xe7,
+0x2c, 0x22, 0x2b, 0xfe, 0xba, 0xb0, 0xaa, 0x9f, 0xa3, 0xb0,
+0x5c, 0x33, 0x33, 0x39, 0x5c, 0xdb, 0xc1, 0xb4, 0xb0, 0xaa,
+0xad, 0xba, 0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xe7, 0xaf,
+0xa6, 0xa7, 0xb0, 0xfe, 0x46, 0x39, 0x5c, 0xe7, 0xdb, 0xfe,
+0xba, 0xac, 0xa8, 0xc5, 0x46, 0x33, 0x54, 0xc5, 0xae, 0xad,
+0xb2, 0xc1, 0xcd, 0xc1, 0xbc, 0xfe, 0x3f, 0x37, 0xfe, 0xb4,
+0xb6, 0xcd, 0xdb, 0xc1, 0xb0, 0xb6, 0xcd, 0x4e, 0x39, 0x37,
+0xfe, 0xb0, 0xab, 0xa9, 0xa9, 0xa9, 0xb0, 0x5c, 0x29, 0x25,
+0x31, 0xfe, 0xc1, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xcd, 0x3b,
+0x2a, 0x2c, 0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8,
+0xfe, 0x33, 0x27, 0x2a, 0x39, 0xfe, 0xc1, 0xbe, 0xb0, 0xa2,
+0x9f, 0xb0, 0x33, 0x22, 0x25, 0x46, 0xc1, 0xb8, 0xb0, 0xab,
+0xa8, 0xa8, 0xb0, 0xbe, 0x42, 0x2c, 0x2e, 0x4a, 0xfe, 0x5c,
+0xfe, 0xb4, 0xa8, 0xa8, 0xba, 0xfe, 0x4a, 0x39, 0x39, 0x46,
+0xfe, 0xbc, 0xaf, 0xa5, 0xa5, 0xae, 0x68, 0x37, 0x4a, 0xfe,
+0xfe, 0x4a, 0x4a, 0xd3, 0xb0, 0xb0, 0xc1, 0x5c, 0x46, 0x46,
+0xd3, 0xb6, 0xbe, 0x54, 0x54, 0xc9, 0xab, 0xae, 0xc5, 0x46,
+0x4a, 0xfe, 0xcd, 0xc9, 0xcd, 0xe7, 0xe7, 0xc9, 0xb4, 0xc5,
+0x4a, 0x2c, 0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb,
+0xfe, 0x4a, 0x46, 0x3f, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6,
+0xb2, 0xcd, 0x33, 0x2e, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xaf,
+0xa7, 0xa7, 0xad, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xcd,
+0xc5, 0xac, 0xa6, 0xac, 0xfe, 0x3b, 0x2c, 0x2d, 0x3d, 0xc1,
+0xb4, 0xbe, 0xcd, 0xaf, 0xa5, 0xa8, 0xe7, 0x31, 0x2f, 0x39,
+0x46, 0x5c, 0xdb, 0xbc, 0xba, 0xaf, 0xa9, 0xad, 0xfe, 0x2f,
+0x2d, 0xba, 0xad, 0xba, 0xfe, 0x3d, 0x42, 0x5c, 0xc9, 0xc1,
+0xcd, 0xfe, 0xc1, 0xae, 0xa6, 0xcd, 0x33, 0x25, 0x3b, 0xdb,
+0xb0, 0xb6, 0xb8, 0xb6, 0xb4, 0xb8, 0xba, 0xfe, 0x3d, 0x37,
+0xfe, 0xba, 0xc1, 0x54, 0x54, 0xd3, 0xb0, 0xb4, 0xe7, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xb6, 0xa9, 0xa7, 0xba,
+0x3d, 0x35, 0xfe, 0xc1, 0xcd, 0x4a, 0x54, 0xbe, 0xb2, 0xb8,
+0xfe, 0x46, 0x3b, 0xfe, 0xba, 0xab, 0xc5, 0x46, 0x3b, 0xbc,
+0xaa, 0xab, 0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xdb, 0x54, 0x3d,
+0x4a, 0xbc, 0xac, 0xb4, 0x3f, 0x2e, 0x3d, 0xba, 0xb0, 0xb8,
+0xba, 0xb6, 0xba, 0xcd, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4,
+0xbe, 0x54, 0x54, 0xcd, 0xb6, 0xc9, 0x46, 0x54, 0xcd, 0xc5,
+0xdb, 0xfe, 0xfe, 0xc1, 0xae, 0xa9, 0xac, 0xfe, 0x35, 0x2e,
+0xfe, 0xba, 0xc1, 0x5c, 0xfe, 0xb6, 0xaa, 0xb0, 0xe7, 0x35,
+0x2e, 0x39, 0xc1, 0xac, 0xb0, 0xfe, 0xfe, 0xbc, 0xa6, 0xac,
+0xc1, 0x42, 0x46, 0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xc9, 0xae,
+0xa9, 0xb0, 0x54, 0x35, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xae,
+0xab, 0xb6, 0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x4e,
+0xfe, 0xb0, 0xac, 0xb8, 0xfe, 0xfe, 0xc1, 0xb6, 0xc5, 0x46,
+0x3d, 0xe7, 0xb4, 0xa7, 0xab, 0xbc, 0x3f, 0x37, 0x54, 0xba,
+0xcd, 0x54, 0x42, 0xc5, 0xae, 0xac, 0xc9, 0x46, 0x3d, 0x54,
+0xba, 0xb0, 0xb0, 0xfe, 0x5c, 0xcd, 0xb0, 0xb0, 0xc9, 0x54,
+0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xc1, 0xba, 0xc5,
+0xfe, 0x42, 0x46, 0xfe, 0xc5, 0xba, 0xb2, 0xa7, 0xa7, 0xb0,
+0xfe, 0x3d, 0x4a, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xb0,
+0xae, 0xc5, 0x4e, 0x39, 0xfe, 0xc5, 0xbe, 0xfe, 0x54, 0xc9,
+0xa9, 0xa2, 0xa5, 0xbc, 0x3b, 0x2f, 0x35, 0xfe, 0xc9, 0xfe,
+0xfe, 0xc5, 0xa9, 0xa6, 0xb0, 0x54, 0x31, 0x31, 0x3f, 0xd3,
+0xbc, 0xc1, 0xcd, 0xb8, 0xae, 0xa8, 0xb4, 0xd3, 0x54, 0x4e,
+0x5c, 0x54, 0xfe, 0xdb, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c,
+0x3d, 0x3f, 0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0xfe,
+0xfe, 0xe7, 0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xb8, 0xaf, 0xb0,
+0xe7, 0x42, 0x4a, 0xcd, 0xbc, 0xdb, 0x46, 0x68, 0xcd, 0xb0,
+0xab, 0xbc, 0xfe, 0x3d, 0x46, 0xfe, 0xb8, 0xbc, 0xd3, 0xd3,
+0xb6, 0xb0, 0xb6, 0x5c, 0x3b, 0x35, 0x54, 0xdb, 0xba, 0xb4,
+0xc1, 0xc9, 0xc1, 0xba, 0xc9, 0x5c, 0x3d, 0x46, 0xfe, 0xcd,
+0xc5, 0xb8, 0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3d, 0x35, 0x46,
+0xfe, 0xdb, 0xbc, 0xb2, 0xa9, 0xab, 0xba, 0x3f, 0x31, 0x39,
+0xfe, 0xe7, 0xdb, 0xcd, 0xb8, 0xae, 0xab, 0xac, 0xe7, 0x3d,
+0x2d, 0x3f, 0xfe, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa8, 0xb0,
+0xfe, 0x31, 0x2d, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xb4, 0xa8,
+0xad, 0xc5, 0x46, 0x39, 0x3f, 0x5c, 0xfe, 0xd3, 0xc5, 0xc1,
+0xb6, 0xb0, 0xbc, 0x68, 0x46, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe,
+0xc1, 0xaf, 0xb0, 0xb8, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe,
+0xfe, 0xe7, 0xb0, 0xab, 0xb2, 0x4a, 0x37, 0x3f, 0xcd, 0xbe,
+0xc1, 0xe7, 0xe7, 0xd3, 0xb6, 0xb4, 0xc9, 0x3b, 0x33, 0x4a,
+0xba, 0xb4, 0xc5, 0xfe, 0xc9, 0xb6, 0xb4, 0xcd, 0xfe, 0x3b,
+0x3b, 0xfe, 0xc1, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xba, 0x4a,
+0x31, 0x35, 0x68, 0xcd, 0xc5, 0xba, 0xb4, 0xb0, 0xb0, 0xba,
+0x5c, 0x35, 0x2f, 0x4e, 0xd3, 0xc1, 0xdb, 0xd3, 0xb4, 0xa9,
+0xab, 0xcd, 0x3b, 0x2f, 0x35, 0xfe, 0xd3, 0xd3, 0xdb, 0xbc,
+0xad, 0xa4, 0xb0, 0xfe, 0x2d, 0x2f, 0x3f, 0xe7, 0xe7, 0xe7,
+0xcd, 0xb4, 0xaf, 0xad, 0xc5, 0x3d, 0x31, 0x3d, 0xe7, 0xd3,
+0xe7, 0xe7, 0xc1, 0xaf, 0xad, 0xb6, 0xfe, 0x4a, 0x42, 0x54,
+0xfe, 0x68, 0xfe, 0xd3, 0xb2, 0xae, 0xb4, 0xfe, 0x42, 0x4e,
+0xcd, 0xc5, 0xcd, 0xdb, 0xc9, 0xb4, 0xb0, 0xb6, 0xfe, 0x3b,
+0x42, 0xe7, 0xb0, 0xb8, 0xcd, 0xfe, 0xc9, 0xb6, 0xb8, 0xfe,
+0x42, 0x3d, 0xfe, 0xc1, 0xb0, 0xba, 0xd3, 0xfe, 0xc1, 0xb0,
+0xb6, 0xfe, 0x3b, 0x3f, 0xe7, 0xba, 0xb8, 0xbc, 0xc5, 0xc1,
+0xc1, 0xcd, 0xfe, 0x3b, 0x37, 0xfe, 0xc1, 0xb4, 0xb6, 0xb8,
+0xb6, 0xb8, 0xc5, 0x5c, 0x3f, 0x46, 0xfe, 0xcd, 0xc5, 0xcd,
+0xcd, 0xc1, 0xb2, 0xb2, 0xfe, 0x3f, 0x35, 0x54, 0xdb, 0xc1,
+0xcd, 0xcd, 0xbc, 0xaf, 0xac, 0xb6, 0x54, 0x35, 0x31, 0x68,
+0xba, 0xb8, 0xcd, 0xdb, 0xc9, 0xb2, 0xb4, 0xc9, 0x46, 0x39,
+0x42, 0xdb, 0xbc, 0xbc, 0xcd, 0xcd, 0xbe, 0xb2, 0xb8, 0xe7,
+0x54, 0x46, 0xfe, 0xfe, 0xdb, 0xc9, 0xc5, 0xbe, 0xbe, 0xc9,
+0xfe, 0x5c, 0x5c, 0xfe, 0xd3, 0xcd, 0xcd, 0xc5, 0xb6, 0xb2,
+0xc5, 0x68, 0x4e, 0xfe, 0xc5, 0xc1, 0xcd, 0x68, 0x5c, 0xe7,
+0xb8, 0xb6, 0xd3, 0x4a, 0x46, 0xfe, 0xbc, 0xb8, 0xc1, 0xe7,
+0xe7, 0xc1, 0xb4, 0xbe, 0xfe, 0x3f, 0x3f, 0xfe, 0xba, 0xb2,
+0xba, 0xe7, 0xfe, 0xcd, 0xcd, 0xfe, 0x4e, 0x46, 0xfe, 0xc5,
+0xb8, 0xb2, 0xba, 0xc1, 0xcd, 0xd3, 0xe7, 0xfe, 0x5c, 0x5c,
+0xfe, 0xe7, 0xc5, 0xbe, 0xb6, 0xba, 0xc5, 0xfe, 0x3f, 0x3f,
+0x54, 0xfe, 0xd3, 0xc1, 0xbc, 0xb6, 0xb0, 0xb0, 0xd3, 0x54,
+0x39, 0x46, 0xfe, 0xc1, 0xcd, 0xe7, 0xe7, 0xc5, 0xb8, 0xb4,
+0xd3, 0x54, 0x37, 0x42, 0xdb, 0xbe, 0xc1, 0xd3, 0xcd, 0xb8,
+0xb0, 0xb0, 0xcd, 0x4a, 0x3b, 0x42, 0xe7, 0xc5, 0xbe, 0xcd,
+0xe7, 0xd3, 0xc5, 0xcd, 0xfe, 0x54, 0x54, 0x68, 0xe7, 0xc5,
+0xc1, 0xc1, 0xcd, 0xcd, 0xc9, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe,
+0xfe, 0xe7, 0xc5, 0xbe, 0xc1, 0xfe, 0x5c, 0x5c, 0xfe, 0xcd,
+0xcd, 0xcd, 0xdb, 0xd3, 0xc1, 0xbc, 0xbe, 0xfe, 0x4e, 0x54,
+0xcd, 0xb6, 0xb8, 0xd3, 0x5c, 0x5c, 0xfe, 0xc5, 0xc9, 0xfe,
+0x46, 0x4a, 0xe7, 0xb4, 0xb6, 0xc5, 0xfe, 0xe7, 0xcd, 0xc9,
+0xdb, 0xfe, 0x4e, 0x68, 0xd3, 0xb6, 0xb2, 0xbc, 0xfe, 0x68,
+0xfe, 0xfe, 0x68, 0x54, 0x68, 0xe7, 0xc5, 0xbc, 0xb8, 0xbe,
+0xcd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3, 0xcd,
+0xc1, 0xb8, 0xbc, 0xdb, 0x4e, 0x42, 0x4a, 0xfe, 0xc9, 0xc1,
+0xcd, 0xd3, 0xcd, 0xba, 0xb8, 0xcd, 0x46, 0x3b, 0xfe, 0xc9,
+0xba, 0xcd, 0xe7, 0xfe, 0xd3, 0xc1, 0xba, 0xdb, 0x54, 0x3d,
+0x68, 0xd3, 0xbc, 0xcd, 0xfe, 0xfe, 0xc5, 0xbe, 0xc1, 0xe7,
+0x54, 0x4a, 0xfe, 0xc9, 0xc1, 0xcd, 0xfe, 0xfe, 0xd3, 0xd3,
+0xd3, 0xfe, 0xe7, 0xe7, 0xe7, 0xdb, 0xd3, 0xe7, 0xe7, 0xe7,
+0xfe, 0xfe, 0xfe, 0xfe, 0xcd, 0xc9, 0xdb, 0xfe, 0xfe, 0xdb,
+0xbe, 0xc9, 0xfe, 0x5c, 0xfe, 0xc9, 0xbc, 0xbe, 0xdb, 0x68,
+0x5c, 0xdb, 0xc5, 0xd3, 0x54, 0x46, 0xfe, 0xbc, 0xb2, 0xb8,
+0xdb, 0x68, 0x68, 0xe7, 0xcd, 0xdb, 0x5c, 0x54, 0xfe, 0xc1,
+0xb8, 0xc1, 0xe7, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
+0xd3, 0xc5, 0xc1, 0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0x54, 0x4e,
+0xfe, 0xd3, 0xcd, 0xd3, 0xd3, 0xc5, 0xc1, 0xc1, 0xe7, 0x5c,
+0x4e, 0x5c, 0xd3, 0xc1, 0xcd, 0xfe, 0xfe, 0xcd, 0xba, 0xba,
+0xe7, 0x4a, 0x4a, 0x68, 0xcd, 0xc5, 0xcd, 0xfe, 0xfe, 0xcd,
+0xb8, 0xc1, 0xe7, 0x4e, 0x5c, 0xe7, 0xc1, 0xc9, 0xdb, 0xfe,
+0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0x68, 0xfe, 0xdb, 0xd3, 0xe7,
+0xfe, 0xfe, 0xcd, 0xc9, 0xcd, 0xd3, 0xd3, 0xd3, 0xcd, 0xe7,
+0xfe, 0xfe, 0xe7, 0xc5, 0xc5, 0xe7, 0x68, 0x68, 0xe7, 0xc1,
+0xc5, 0xfe, 0x5c, 0xfe, 0xd3, 0xc1, 0xd3, 0xfe, 0x68, 0xe7,
+0xc5, 0xb6, 0xc5, 0xe7, 0x68, 0xfe, 0xcd, 0xc5, 0xe7, 0xfe,
+0x54, 0xfe, 0xc9, 0xc5, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3,
+0xfe, 0xfe, 0xfe, 0xcd, 0xc1, 0xc1, 0xc9, 0xd3, 0xd3, 0xe7,
+0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xd3, 0xdb, 0xe7, 0xe7, 0xd3,
+0xcd, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xd3, 0xe7, 0xe7,
+0xc9, 0xbc, 0xbe, 0xe7, 0x68, 0x4a, 0xfe, 0xdb, 0xcd, 0xfe,
+0xfe, 0xfe, 0xcd, 0xc1, 0xc9, 0xfe, 0x54, 0x5c, 0xe7, 0xc9,
+0xc5, 0xe7, 0xfe, 0xfe, 0xcd, 0xc5, 0xc5, 0xe7, 0xfe, 0xfe,
+0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xdb, 0xd3, 0xd3, 0xdb, 0xe7,
+0xfe, 0xfe, 0xe7, 0xe7, 0xdb, 0xd3, 0xc9, 0xd3, 0xe7, 0xfe,
+0xfe, 0xd3, 0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xcd, 0xcd,
+0xfe, 0xfe, 0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd,
+0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xdb, 0xc9, 0xcd, 0xe7, 0xfe,
+0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7, 0xd3, 0xc5, 0xcd,
+0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe,
+0xe7, 0xcd, 0xcd, 0xd3, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe,
+0xe7, 0xe7, 0xdb, 0xc9, 0xc1, 0xc5, 0xfe, 0x5c, 0x68, 0xfe,
+0xd3, 0xdb, 0xe7, 0xe7, 0xe7, 0xd3, 0xc5, 0xcd, 0xe7, 0x68,
+0xfe, 0xe7, 0xcd, 0xd3, 0xe7, 0xfe, 0xe7, 0xcd, 0xc1, 0xc1,
+0xdb, 0xfe, 0x54, 0xfe, 0xe7, 0xcd, 0xe7, 0xfe, 0xe7, 0xd3,
+0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xcd, 0xfe,
+0xfe, 0xe7, 0xcd, 0xd3, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7,
+0xd3, 0xd3, 0xe7, 0xfe, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
+0xfe, 0xdb, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9,
+0xcd, 0xe7, 0xfe, 0xfe, 0xd3, 0xcd, 0xdb, 0xfe, 0x5c, 0xfe,
+0xcd, 0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9, 0xcd, 0xfe,
+0x68, 0xfe, 0xd3, 0xc1, 0xc1, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7,
+0xfe, 0xfe, 0x68, 0xfe, 0xe7, 0xc5, 0xc9, 0xdb, 0xfe, 0xfe,
+0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xdb, 0xc5, 0xc5,
+0xd3, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe,
+0xc9, 0xc1, 0xc5, 0xfe, 0x54, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd,
+0xfe, 0xfe, 0xdb, 0xc5, 0xc9, 0xfe, 0x5c, 0x68, 0xfe, 0xcd,
+0xcd, 0xfe, 0xfe, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe,
+0xdb, 0xc9, 0xc5, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe,
+0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe,
+0xe7, 0xd3, 0xcd, 0xd3, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7,
+0xfe, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xd3, 0xc5,
+0xc9, 0xfe, 0x5c, 0x54, 0xfe, 0xcd, 0xc1, 0xcd, 0xe7, 0xfe,
+0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x5c, 0xe7, 0xc1, 0xc1, 0xd3,
+0xfe, 0xfe, 0xe7, 0xd3, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd,
+0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xe7, 0xd3, 0xcd, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe, 0xfe, 0xdb,
+0xc9, 0xcd, 0xe7, 0xfe, 0xe7, 0xc9, 0xc5, 0xdb, 0xfe, 0x5c,
+0xfe, 0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3,
+0xfe, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7,
+0xd3, 0xe7, 0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe,
+0xfe, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xe7, 0xdb, 0xcd, 0xd3,
+0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe, 0xdb, 0xd3, 0xdb, 0xe7,
+0xe7, 0xdb, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xc5,
+0xcd, 0xe7, 0xfe, 0xdb, 0xd3, 0xe7, 0xfe, 0x68, 0xfe, 0xe7,
+0xcd, 0xcd, 0xd3, 0xfe, 0xfe, 0xe7, 0xdb, 0xe7, 0xfe, 0x68,
+0xfe, 0xdb, 0xfe, 0x68, 0xbe, 0xb2, 0xae, 0xab, 0xb2, 0xfe,
+0x2f, 0x31, 0xdb, 0xac, 0xad, 0xaf, 0xab, 0xab, 0xb4, 0x68,
+0x37, 0x39, 0x3f, 0xe7, 0xb4, 0xa8, 0xaa, 0xb0, 0xbc, 0xbc,
+0xc5, 0x3f, 0x31, 0x3d, 0xfe, 0xc1, 0xb8, 0xb0, 0xa5, 0xa2,
+0xa8, 0xaf, 0xdb, 0x3b, 0x28, 0x2a, 0x3d, 0xbc, 0xb0, 0xaa,
+0xa2, 0x9f, 0xab, 0xfe, 0x29, 0x24, 0x29, 0x4a, 0xb4, 0xaa,
+0xa8, 0xa9, 0xa8, 0xa5, 0xac, 0xdb, 0x2c, 0x27, 0x35, 0x4a,
+0xfe, 0xcd, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0x4e, 0x2c, 0x22,
+0x33, 0xfe, 0xba, 0xb0, 0xa6, 0x9f, 0xa3, 0xbc, 0x5c, 0x33,
+0x31, 0x39, 0x5c, 0xcd, 0xc1, 0xb4, 0xad, 0xaa, 0xad, 0xcd,
+0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xc5, 0xaf, 0xa6, 0xa9,
+0xb0, 0xfe, 0x3d, 0x39, 0x5c, 0xdb, 0xdb, 0xfe, 0xba, 0xac,
+0xa8, 0xc5, 0x39, 0x33, 0x54, 0xb8, 0xae, 0xad, 0xb8, 0xc1,
+0xcd, 0xbe, 0xbc, 0xfe, 0x39, 0x37, 0xfe, 0xb4, 0xba, 0xcd,
+0xdb, 0xb8, 0xb0, 0xb6, 0xfe, 0x4e, 0x39, 0x3d, 0xfe, 0xb0,
+0xaa, 0xa9, 0xa9, 0xaa, 0xb0, 0x5c, 0x29, 0x28, 0x31, 0xfe,
+0xba, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xfe, 0x3b, 0x2a, 0x2f,
+0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8, 0xfe, 0x2c,
+0x27, 0x2a, 0x46, 0xfe, 0xc1, 0xbc, 0xb0, 0xa2, 0xa2, 0xb0,
+0x33, 0x22, 0x2b, 0x46, 0xc1, 0xb4, 0xb0, 0xab, 0xa8, 0xa8,
+0xb0, 0xdb, 0x42, 0x2c, 0x33, 0x4a, 0xfe, 0x5c, 0xdb, 0xb4,
+0xa8, 0xad, 0xba, 0xfe, 0x46, 0x39, 0x39, 0x4a, 0xfe, 0xbc,
+0xab, 0xa5, 0xa5, 0xb8, 0x68, 0x37, 0x4a, 0xe7, 0xfe, 0x4a,
+0x5c, 0xd3, 0xb0, 0xb2, 0xc1, 0x5c, 0x42, 0x46, 0xd3, 0xb4,
+0xbe, 0x54, 0x54, 0xb6, 0xab, 0xae, 0xe7, 0x46, 0x4a, 0xfe,
+0xcd, 0xc9, 0xd3, 0xe7, 0xe7, 0xbe, 0xb4, 0xc5, 0x37, 0x2c,
+0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb, 0x54, 0x4a,
+0x46, 0x42, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6, 0xb6, 0xcd,
+0x33, 0x2f, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xac, 0xa7, 0xa7,
+0xb2, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xd3, 0xc5, 0xac,
+0xa6, 0xac, 0xfe, 0x33, 0x2c, 0x2d, 0x54, 0xc1, 0xb4, 0xcd,
+0xcd, 0xaf, 0xa4, 0xa8, 0xe7, 0x31, 0x31, 0x39, 0x46, 0xfe,
+0xdb, 0xbc, 0xb6, 0xaf, 0xa9, 0xb2, 0xfe, 0x2f, 0xfe, 0xba,
+0xad, 0xba, 0x4e, 0x3d, 0x42, 0xfe, 0xc9, 0xc1, 0xe7, 0xfe,
+0xc1, 0xa9, 0xa6, 0xcd, 0x2a, 0x25, 0x3b, 0xbc, 0xb0, 0xb6,
+0xb8, 0xb4, 0xb4, 0xb8, 0xc1, 0xfe, 0x3d, 0x3d, 0xfe, 0xba,
+0xd3, 0x54, 0x54, 0xbe, 0xb0, 0xb4, 0xe7, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xc5, 0xb6, 0xa9, 0xaa, 0xba, 0x3d, 0x39,
+0xfe, 0xc1, 0xfe, 0x4a, 0x54, 0xbe, 0xb2, 0xb8, 0xfe, 0x3d,
+0x3b, 0xfe, 0xb0, 0xab, 0xc5, 0x39, 0x3b, 0xbc, 0xa7, 0xab,
+0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x3d, 0xfe, 0xbc,
+0xac, 0xc9, 0x3f, 0x2e, 0xfe, 0xba, 0xb0, 0xba, 0xba, 0xb6,
+0xba, 0xd3, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4, 0xbe, 0x54,
+0x68, 0xcd, 0xb6, 0xfe, 0x46, 0x54, 0xcd, 0xc5, 0xdb, 0xfe,
+0xe7, 0xc1, 0xae, 0xa8, 0xac, 0xfe, 0x2e, 0x2e, 0xfe, 0xb6,
+0xc1, 0x5c, 0xe7, 0xb6, 0xaa, 0xb0, 0x54, 0x35, 0x2e, 0x4a,
+0xc1, 0xac, 0xbc, 0xfe, 0xfe, 0xaf, 0xa6, 0xac, 0xfe, 0x42,
+0x46, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xae, 0xa9, 0xb0,
+0x54, 0x31, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xac, 0xab, 0xb6,
+0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x54, 0xfe, 0xb0,
+0xae, 0xb8, 0xfe, 0xe7, 0xc1, 0xb6, 0xe7, 0x46, 0x3d, 0xe7,
+0xae, 0xa7, 0xab, 0xdb, 0x3f, 0x37, 0xfe, 0xba, 0xcd, 0x3f,
+0x42, 0xc5, 0xab, 0xac, 0xc9, 0x46, 0x3d, 0x54, 0xba, 0xad,
+0xb0, 0xfe, 0x68, 0xcd, 0xb0, 0xb0, 0xc9, 0x54, 0x54, 0xfe,
+0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xbe, 0xba, 0xc5, 0x68, 0x42,
+0x46, 0xe7, 0xc5, 0xba, 0xaf, 0xa7, 0xa7, 0xbc, 0xfe, 0x3d,
+0x4a, 0x68, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xaf, 0xae, 0xc5,
+0x3d, 0x39, 0xfe, 0xbc, 0xbe, 0xfe, 0x68, 0xc9, 0xa9, 0xa2,
+0xaa, 0xbc, 0x3b, 0x2d, 0x35, 0xfe, 0xcd, 0xfe, 0xfe, 0xb4,
+0xa9, 0xa6, 0xbc, 0x54, 0x31, 0x31, 0x54, 0xd3, 0xbc, 0xc5,
+0xcd, 0xb8, 0xab, 0xa8, 0xb4, 0xfe, 0x54, 0x4e, 0x68, 0x54,
+0xfe, 0xc9, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c, 0x3b, 0x3f,
+0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0x68, 0xfe, 0xe7,
+0xdb, 0xfe, 0xfe, 0xfe, 0xcd, 0xb8, 0xaf, 0xb6, 0xe7, 0x42,
+0x5c, 0xcd, 0xbc, 0xfe, 0x46, 0x68, 0xba, 0xb0, 0xab, 0xbc,
+0x54, 0x3d, 0x46, 0xc9, 0xb8, 0xbc, 0xdb, 0xd3, 0xb6, 0xb0,
+0xb6, 0x5c, 0x37, 0x35, 0x54, 0xc9, 0xba, 0xb4, 0xc1, 0xc9,
+0xc1, 0xba, 0xe7, 0x5c, 0x3d, 0x54, 0xfe, 0xcd, 0xc5, 0xb8,
+0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3b, 0x35, 0x46, 0xfe, 0xdb,
+0xbc, 0xaf, 0xa9, 0xab, 0xd3, 0x3f, 0x31, 0x3f, 0xfe, 0xe7,
+0xdb, 0xcd, 0xb8, 0xae, 0xaa, 0xac, 0xe7, 0x33, 0x2d, 0x3f,
+0xd3, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa9, 0xb0, 0xfe, 0x31,
+0x2f, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xae, 0xa8, 0xad, 0xfe,
+0x46, 0x39, 0x46, 0x5c, 0xfe, 0xcd, 0xc5, 0xc1, 0xb6, 0xb0,
+0xbc, 0x68, 0x42, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe, 0xb6, 0xaf,
+0xb0, 0xc5, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe, 0x68, 0xe7,
+0xb0, 0xac, 0xb2, 0x4a, 0x35, 0x3f, 0xcd, 0xbc, 0xc1, 0xe7,
+0xe7, 0xd3, 0xb6, 0xb4, 0xfe, 0x3b, 0x33, 0xfe, 0xba, 0xb4,
+0xd3, 0xfe, 0xc9, 0xb4, 0xb4, 0xcd, 0x4a, 0x3b, 0x3b, 0xfe,
+0xb8, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xcd, 0x4a, 0x31, 0x3b,
+0x68, 0xcd, 0xc1, 0xba, 0xb4, 0xb0, 0xb0, 0xba, 0x5c, 0x2f,
+0x2f, 0x4e, 0xc9, 0xc1, 0xdb, 0xc9, 0xb4 };
diff --git a/trunk/apps/leave.h b/trunk/apps/leave.h
new file mode 100644
index 000000000..238976f20
--- /dev/null
+++ b/trunk/apps/leave.h
@@ -0,0 +1,207 @@
+/*
+ * U-law 8-bit audio data
+ *
+ * Source: leave.raw
+ *
+ * Copyright (C) 1999, Mark Spencer and Linux Support Services
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char leave[] = {
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc1, 0x3d,
+0x42, 0x46, 0x3f, 0x3f, 0x46, 0x3f, 0x4e, 0xba, 0xbe, 0xbe,
+0xbc, 0xba, 0xbe, 0xc5, 0xb6, 0x2e, 0x2c, 0x33, 0x2f, 0x2e,
+0x2f, 0x33, 0x2b, 0x54, 0xac, 0xb0, 0xb0, 0xad, 0xaf, 0xb0,
+0xae, 0xcd, 0x3b, 0x2f, 0x31, 0x2e, 0x2f, 0x31, 0x2e, 0x46,
+0xc5, 0xaf, 0xb0, 0xaf, 0xae, 0xaf, 0xaf, 0xb0, 0xfe, 0x2d,
+0x31, 0x31, 0x2e, 0x31, 0x2f, 0x31, 0xfe, 0xae, 0xaf, 0xaf,
+0xae, 0xb0, 0xae, 0xaf, 0xfe, 0xdb, 0x2e, 0x2e, 0x31, 0x31,
+0x2d, 0x2e, 0xdb, 0x68, 0xaf, 0xad, 0xb0, 0xb0, 0xae, 0xaf,
+0x5c, 0xe7, 0x39, 0x2d, 0x31, 0x31, 0x31, 0x2d, 0xfe, 0xfe,
+0x68, 0xad, 0xaf, 0xb0, 0xaf, 0xac, 0xbc, 0xfe, 0xd3, 0x2f,
+0x2e, 0x33, 0x31, 0x2d, 0x4e, 0xdb, 0xfe, 0xfe, 0xac, 0xaf,
+0xb0, 0xac, 0xb6, 0x68, 0xe7, 0xdb, 0x2e, 0x2f, 0x35, 0x2f,
+0x31, 0xe7, 0xe7, 0x68, 0xad, 0xac, 0xb0, 0xae, 0xac, 0xfe,
+0xfe, 0xdb, 0xfe, 0x2d, 0x33, 0x31, 0x2e, 0xfe, 0xfe, 0xfe,
+0xfe, 0xbc, 0xaf, 0xb0, 0xad, 0xfe, 0xfe, 0xfe, 0xe7, 0x5c,
+0x2e, 0x33, 0x2e, 0x35, 0xe7, 0xfe, 0xfe, 0xfe, 0xad, 0xb0,
+0xaf, 0xc1, 0xfe, 0xe7, 0xfe, 0xe7, 0x3d, 0x31, 0x2f, 0x37,
+0xe7, 0xfe, 0xfe, 0xe7, 0xfe, 0xaf, 0xad, 0xbe, 0xfe, 0xdb,
+0xfe, 0xfe, 0xdb, 0x35, 0x2d, 0x39, 0xdb, 0xfe, 0xfe, 0xdb,
+0xfe, 0xfe, 0xad, 0xaf, 0xfe, 0xfe, 0xe7, 0x68, 0xfe, 0xd3,
+0x2e, 0x2c, 0xdb, 0xdb, 0x2c, 0x35, 0xd3, 0x68, 0xaf, 0xad,
+0xb0, 0xb0, 0xad, 0xba, 0x68, 0xe7, 0xe7, 0x2e, 0x2f, 0x33,
+0x31, 0x2d, 0xdb, 0xd3, 0x5c, 0xae, 0xaa, 0xe7, 0x68, 0xaa,
+0xe7, 0xfe, 0xdb, 0xe7, 0xfe, 0xe7, 0xd3, 0x2d, 0xfe, 0xdb,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc5, 0xfe, 0xe7, 0xe7,
+0xfe, 0xfe, 0xe7, 0xe7, 0x3b, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xc5, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0x3b,
+0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xb0, 0xfe, 0xfe,
+0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0x2e, 0x5c, 0xdb, 0xfe, 0xfe,
+0xe7, 0xe7, 0x68, 0xb0, 0xbe, 0x68, 0xe7, 0xe7, 0xfe, 0xfe,
+0xdb, 0x39, 0x2f, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xbe,
+0xaf, 0xe7, 0x68, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0x33, 0x33,
+0xdb, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xb0, 0xb0, 0xfe, 0xfe,
+0xe7, 0xfe, 0xfe, 0xfe, 0x35, 0x33, 0xe7, 0xe7, 0xfe, 0xe7,
+0xe7, 0xfe, 0xb0, 0xb2, 0xb0, 0xfe, 0xfe, 0xe7, 0xfe, 0xe7,
+0x46, 0x35, 0x35, 0x3f, 0xe7, 0xfe, 0xe7, 0xfe, 0xb2, 0xb0,
+0xb2, 0xb0, 0xfe, 0xfe, 0xfe, 0xfe, 0x42, 0x35, 0x37, 0x33,
+0xe7, 0xfe, 0xfe, 0xfe, 0xb8, 0xb0, 0xb6, 0xb0, 0xba, 0xfe,
+0xfe, 0xe7, 0xe7, 0x33, 0x39, 0x39, 0x33, 0xe7, 0xdb, 0xfe,
+0xe7, 0xb0, 0xb4, 0xb6, 0xb0, 0xcd, 0xfe, 0xe7, 0xe7, 0x33,
+0x39, 0x3b, 0x33, 0x46, 0xd3, 0xfe, 0xfe, 0xb0, 0xb2, 0xb6,
+0xb4, 0xb0, 0xfe, 0xfe, 0xdb, 0x35, 0x37, 0x39, 0x39, 0x35,
+0x37, 0xdb, 0x68, 0xcd, 0xb2, 0xb6, 0xb6, 0xb4, 0xb4, 0x68,
+0xe7, 0x42, 0x37, 0x3b, 0x3b, 0x39, 0x37, 0xdb, 0xfe, 0xcd,
+0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb4, 0xfe, 0x54, 0x37, 0x3b,
+0x39, 0x3b, 0x3b, 0x39, 0xe7, 0xfe, 0xb6, 0xb6, 0xb6, 0xb4,
+0xb6, 0xb6, 0xbc, 0xfe, 0x3f, 0x3b, 0x3b, 0x39, 0x3b, 0x3b,
+0x39, 0xe7, 0xb6, 0xb8, 0xb8, 0xb6, 0xb8, 0xb8, 0xb4, 0xfe,
+0x3b, 0x3d, 0x3d, 0x3b, 0x39, 0x3d, 0x3b, 0x39, 0xbe, 0xb8,
+0xba, 0xb8, 0xb6, 0xb8, 0xba, 0xb4, 0xfe, 0x39, 0x3f, 0x3d,
+0x3b, 0x3d, 0x3f, 0x39, 0xdb, 0xb4, 0xba, 0xb8, 0xb6, 0xb8,
+0xbc, 0xb4, 0xba, 0x39, 0x42, 0x3f, 0x3d, 0x3d, 0x3f, 0x3f,
+0x3b, 0xb8, 0xb6, 0xbc, 0xb8, 0xb8, 0xba, 0xbc, 0xb8, 0xe7,
+0x3d, 0x42, 0x3f, 0x3d, 0x3f, 0x42, 0x3d, 0xfe, 0xb8, 0xbc,
+0xbc, 0xba, 0xba, 0xbc, 0xba, 0xe7, 0x3d, 0x3f, 0x42, 0x3f,
+0x3f, 0x42, 0x42, 0xfe, 0xfe, 0xbc, 0xbc, 0xbe, 0xbc, 0xbe,
+0xbc, 0xc5, 0xe7, 0x68, 0x42, 0x46, 0x42, 0x46, 0x42, 0x46,
+0xfe, 0xfe, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xc5, 0xfe, 0xdb,
+0x46, 0x46, 0x4a, 0x4a, 0x46, 0x46, 0xe7, 0xfe, 0xd3, 0xbe,
+0xc9, 0xc9, 0xc5, 0xc5, 0xe7, 0xdb, 0xd3, 0x4a, 0x4e, 0x54,
+0x4e, 0x4e, 0xfe, 0x5c, 0x54, 0xd3, 0xcd, 0xd3, 0xd3, 0xcd,
+0xd3, 0xd3, 0xcd, 0xfe, 0x5c, 0x68, 0x5c, 0x5c, 0x5c, 0x68,
+0x5c, 0x5c, 0xcd, 0xcd, 0xd3, 0xcd, 0xdb, 0xe7, 0xe7, 0xdb,
+0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7,
+0xfe, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0xfe, 0x46, 0x35, 0x35,
+0x37, 0x39, 0x3b, 0x39, 0x35, 0x33, 0x35, 0x5c, 0xd3, 0xcd,
+0xdb, 0xfe, 0xfe, 0xd3, 0xb0, 0xb0, 0xb0, 0xb4, 0xb4, 0xb6,
+0xb2, 0xb0, 0xb0, 0xb6, 0xcd, 0x5c, 0x68, 0xfe, 0xfe, 0xfe,
+0x3b, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x39, 0x37, 0x35, 0x35,
+0x3f, 0xdb, 0xcd, 0xcd, 0xdb, 0xfe, 0xe7, 0xc5, 0xb0, 0xb0,
+0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb0, 0xb0, 0xcd, 0x5c, 0x5c,
+0xfe, 0xe7, 0xe7, 0xfe, 0x35, 0x35, 0x35, 0x39, 0x3b, 0x3b,
+0x39, 0x35, 0x33, 0x39, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe,
+0xba, 0xb0, 0xb0, 0xb2, 0xb4, 0xb6, 0xb6, 0xb4, 0xb2, 0xb0,
+0xb4, 0xc9, 0x5c, 0x68, 0xfe, 0xfe, 0x5c, 0x3b, 0x35, 0x37,
+0x39, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37, 0x3d, 0xe7, 0xcd,
+0xdb, 0xfe, 0xe7, 0xbe, 0xb2, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6,
+0xb6, 0xb4, 0xb0, 0xb0, 0xc5, 0x5c, 0x5c, 0xfe, 0xe7, 0xe7,
+0x4e, 0x35, 0x35, 0x37, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37,
+0x3b, 0xe7, 0xc9, 0xcd, 0xe7, 0xfe, 0xd3, 0xb4, 0xb2, 0xb2,
+0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2, 0xb4, 0xc1, 0x68,
+0x68, 0xfe, 0xfe, 0x42, 0x39, 0x37, 0x39, 0x3b, 0x3b, 0x3b,
+0x3b, 0x3b, 0x39, 0x37, 0x3b, 0xfe, 0xd3, 0xdb, 0xfe, 0xcd,
+0xb4, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2,
+0xb2, 0xc1, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0x3d, 0x37, 0x37,
+0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x37, 0x37, 0x39, 0xfe, 0xcd,
+0xd3, 0xfe, 0xfe, 0xc1, 0xb2, 0xb2, 0xb4, 0xb6, 0xb6, 0xb6,
+0xb6, 0xb6, 0xb6, 0xb4, 0xb4, 0xbc, 0x68, 0xfe, 0xfe, 0xfe,
+0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x39,
+0x39, 0x3b, 0xfe, 0xdb, 0xe7, 0xfe, 0xbc, 0xb6, 0xb6, 0xb6,
+0xb8, 0xb6, 0xb6, 0xb6, 0xb8, 0xb6, 0xb4, 0xb4, 0xbc, 0xfe,
+0x68, 0xfe, 0xe7, 0x5c, 0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b,
+0x3d, 0x3d, 0x3b, 0x39, 0x3b, 0x68, 0xdb, 0xdb, 0xfe, 0xe7,
+0xb8, 0xb6, 0xb6, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb6,
+0xb4, 0xb6, 0xdb, 0x68, 0xfe, 0xfe, 0x46, 0x3b, 0x3b, 0x3b,
+0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3b, 0x5c,
+0xdb, 0xdb, 0xc9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xbc, 0xcd, 0xfe, 0xfe, 0x3d,
+0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d,
+0x3b, 0x3d, 0x46, 0xfe, 0xe7, 0xe7, 0xc5, 0xb8, 0xb8, 0xb8,
+0xba, 0xba, 0xb8, 0xb8, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xcd,
+0xfe, 0xfe, 0x68, 0x3f, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+0x3d, 0x3d, 0x3f, 0x3f, 0x3d, 0x3b, 0x4a, 0xfe, 0xdb, 0xbc,
+0xb8, 0xba, 0xba, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xba, 0xba,
+0xba, 0xba, 0xba, 0xc5, 0xfe, 0x54, 0x3f, 0x3f, 0x3f, 0x3f,
+0x3f, 0x3f, 0x3d, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
+0xfe, 0xe7, 0xdb, 0xbc, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+0xba, 0xba, 0xbc, 0xba, 0xba, 0xba, 0xc5, 0xfe, 0xfe, 0x4e,
+0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
+0x42, 0x42, 0x3f, 0x46, 0xfe, 0xcd, 0xb8, 0xba, 0xbc, 0xbc,
+0xbc, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xba, 0xb8,
+0xbe, 0xfe, 0x42, 0x3d, 0x3f, 0x42, 0x42, 0x42, 0x3f, 0x3f,
+0x3f, 0x42, 0x42, 0x42, 0x42, 0x3f, 0x3f, 0x68, 0xdb, 0xc5,
+0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+0xbc, 0xbc, 0xba, 0xc1, 0xfe, 0xfe, 0x3f, 0x42, 0x46, 0x46,
+0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, 0x46, 0x42, 0x3f,
+0x42, 0x68, 0xbe, 0xba, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xbc,
+0xbc, 0xbc, 0xbe, 0xc1, 0xbe, 0xbc, 0xba, 0xbe, 0x68, 0x3f,
+0x42, 0x46, 0x4a, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x46, 0x46,
+0x46, 0x46, 0x42, 0x42, 0x68, 0xd3, 0xbc, 0xbc, 0xbe, 0xc1,
+0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc1, 0xbe, 0xbe,
+0xc1, 0xfe, 0x4e, 0x42, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46,
+0x46, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46, 0x68, 0xdb, 0xbe,
+0xbe, 0xc1, 0xc5, 0xc1, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1,
+0xc5, 0xc5, 0xbe, 0xbc, 0xc1, 0x4e, 0x46, 0x46, 0x4a, 0x4e,
+0x4e, 0x4a, 0x46, 0x46, 0x46, 0x4a, 0x4a, 0x4e, 0x4a, 0x46,
+0x46, 0xfe, 0xbe, 0xbe, 0xc1, 0xc9, 0xc5, 0xc5, 0xc1, 0xc1,
+0xc1, 0xc1, 0xc5, 0xc5, 0xc5, 0xc5, 0xbe, 0xc1, 0xfe, 0x4a,
+0x4a, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4a, 0x4a, 0x4a, 0x4e,
+0x54, 0x4e, 0x4a, 0x4a, 0x4e, 0xcd, 0xc1, 0xc5, 0xc5, 0xc9,
+0xc5, 0xc5, 0xc5, 0xc5, 0xc9, 0xcd, 0xcd, 0xcd, 0xcd, 0xc9,
+0xc9, 0xd3, 0x68, 0x54, 0x5c, 0x68, 0x68, 0x68, 0x5c, 0x5c,
+0x5c, 0x5c, 0x5c, 0x68, 0x68, 0x5c, 0x54, 0x5c, 0xdb, 0xcd,
+0xcd, 0xdb, 0xdb, 0xdb, 0xdb, 0xd3, 0xd3, 0xe7, 0xe7, 0xe7,
+0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+0xfe, 0xfe, 0xfe };
diff --git a/trunk/apps/rpt_flow.pdf b/trunk/apps/rpt_flow.pdf
new file mode 100644
index 000000000..2085af0f2
--- /dev/null
+++ b/trunk/apps/rpt_flow.pdf
Binary files differ
diff --git a/trunk/bootstrap.sh b/trunk/bootstrap.sh
new file mode 100755
index 000000000..b7eb4d83d
--- /dev/null
+++ b/trunk/bootstrap.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+check_for_app() {
+ $1 --version 2>&1 >/dev/null
+ if [ $? != 0 ]
+ then
+ echo "Please install $1 and run bootstrap.sh again!"
+ exit 1
+ fi
+}
+
+# On FreeBSD and OpenBSD, multiple autoconf/automake versions have different names.
+# On linux, envitonment variables tell which one to use.
+
+uname -s | grep -q BSD
+if [ $? = 0 ] ; then # BSD case
+ case `uname -sr` in
+ 'FreeBSD 4'*) # FreeBSD 4.x has a different naming
+ MY_AC_VER=259
+ MY_AM_VER=19
+ ;;
+ *)
+ MY_AC_VER=-2.61
+ MY_AM_VER=-1.9
+ ;;
+ esac
+else # linux case
+ MY_AC_VER=
+ MY_AM_VER=
+ AUTOCONF_VERSION=2.60
+ AUTOMAKE_VERSION=1.9
+ export AUTOCONF_VERSION
+ export AUTOMAKE_VERSION
+fi
+
+check_for_app autoconf${MY_AC_VER}
+check_for_app autoheader${MY_AC_VER}
+check_for_app automake${MY_AM_VER}
+check_for_app aclocal${MY_AM_VER}
+
+echo "Generating the configure script ..."
+
+aclocal${MY_AM_VER} 2>/dev/null
+autoconf${MY_AC_VER}
+autoheader${MY_AC_VER}
+automake${MY_AM_VER} --add-missing --copy 2>/dev/null
+
+exit 0
diff --git a/trunk/build_tools/cflags.xml b/trunk/build_tools/cflags.xml
new file mode 100644
index 000000000..fa69c1263
--- /dev/null
+++ b/trunk/build_tools/cflags.xml
@@ -0,0 +1,62 @@
+ <category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes" remove_on_change=".lastclean">
+ <member name="DONT_OPTIMIZE" displayname="Disable Optimizations by the Compiler">
+ </member>
+ <member name="DEBUG_THREADS" displayname="Enable Thread Debugging">
+ </member>
+ <member name="STATIC_BUILD" displayname="Build static binaries">
+ </member>
+ <member name="LOADABLE_MODULES" displayname="Runtime module loading">
+ <defaultenabled>yes</defaultenabled>
+ </member>
+ <member name="LOW_MEMORY" displayname="Optimize for Low Memory Usage">
+ </member>
+ <member name="LOTS_OF_SPANS" displayname="More than 32 Zaptel spans">
+ </member>
+ <member name="MTX_PROFILE" displayname="Enable Code Profiling Using TSC Counters">
+ </member>
+ <member name="RADIO_RELAX" displayname="Relax DTMF for Radio Applications">
+ </member>
+ <member name="G711_NEW_ALGORITHM" displayname="Use the NEW ulaw/alaw codecs (slower, but cleaner)">
+ <defaultenabled>no</defaultenabled>
+ </member>
+ <member name="G711_REDUCED_BRANCHING" displayname="New ulaw/alaw codec, reduced branching (might help it run faster in some architectures)">
+ <defaultenabled>yes</defaultenabled>
+ <depend>G711_NEW_ALGORITHM</depend>
+ </member>
+ <member name="TEST_CODING_TABLES" displayname="New ulaw/alaw codec, turn on table tests on init">
+ <depend>G711_NEW_ALGORITHM</depend>
+ </member>
+ <member name="TEST_TANDEM_TRANSCODING" displayname="New ulaw/alaw codec, turn on transcoding tests on init">
+ <depend>G711_NEW_ALGORITHM</depend>
+ </member>
+ <member name="DEBUG_CHANNEL_LOCKS" displayname="Debug Channel Locking">
+ </member>
+ <member name="DEBUG_SCHEDULER" displayname="Enable Scheduler Debugging Output">
+ </member>
+ <member name="DEBUG_THREADLOCALS" displayname="Enable Thread-Local-Storage Debugging">
+ </member>
+ <member name="DETECT_DEADLOCKS" displayname="Detect Deadlocks">
+ <depend>DEBUG_THREADS</depend>
+ </member>
+ <member name="DUMP_SCHEDULER" displayname="Dump Scheduler Contents for Debugging">
+ </member>
+ <member name="MALLOC_DEBUG" displayname="Keep Track of Memory Allocations">
+ </member>
+ <member name="TRACE_FRAMES" displayname="Trace Frame Allocations">
+ </member>
+ <member name="DO_CRASH" displayname="Crash on fatal errors">
+ </member>
+ <member name="THREAD_CRASH" displayname="Crash on mutex errors">
+ </member>
+ <member name="BUSYDETECT_TONEONLY" displayname="Enable additional comparision of only the tone duration not the silence part">
+ <conflict>BUSYDETECT_COMPARE_TONE_AND_SILENCE</conflict>
+ <defaultenabled>no</defaultenabled>
+ </member>
+ <member name="BUSYDETECT_COMPARE_TONE_AND_SILENCE" displayname="Assume that tone and silence have the same duration">
+ <conflict>BUSYDETECT_TONEONLY</conflict>
+ <defaultenabled>no</defaultenabled>
+ </member>
+ <member name="BUSYDETECT_DEBUG" displayname="Enable additional busy detection debugging">
+ <defaultenabled>no</defaultenabled>
+ </member>
+ </category>
diff --git a/trunk/build_tools/embed_modules.xml b/trunk/build_tools/embed_modules.xml
new file mode 100644
index 000000000..054e1dfad
--- /dev/null
+++ b/trunk/build_tools/embed_modules.xml
@@ -0,0 +1,26 @@
+ <category name="MENUSELECT_EMBED" displayname="Module Embedding" positive_output="yes" remove_on_change="main/asterisk">
+ <member name="APPS" displayname="Applications" remove_on_change="apps/*.o">
+ <depend>gnu_ld</depend>
+ </member>
+ <member name="CDR" displayname="Call Detail Recording" remove_on_change="cdr/*.o">
+ <depend>gnu_ld</depend>
+ </member>
+ <member name="CHANNELS" displayname="Channels" remove_on_change="channels/*.o channels/misdn/*.o">
+ <depend>gnu_ld</depend>
+ </member>
+ <member name="CODECS" displayname="Coders/Decoders" remove_on_change="codecs/*.o codecs/gsm/src/*.o codecs/ilbc/*.o codecs/lpc10/*.o codecs/gsm/lib/libgsm.a codecs/lpc10/liblpc10.a codecs/ilbc/libilbc.a codecs/g722/libg722.a">
+ <depend>gnu_ld</depend>
+ </member>
+ <member name="FORMATS" displayname="File Formats" remove_on_change="formats/*.o">
+ <depend>gnu_ld</depend>
+ </member>
+ <member name="FUNCS" displayname="Dialplan Functions" remove_on_change="funcs/*.o">
+ <depend>gnu_ld</depend>
+ </member>
+ <member name="PBX" displayname="PBX Functionality" remove_on_change="pbx/*.o pbx/ael/*.o">
+ <depend>gnu_ld</depend>
+ </member>
+ <member name="RES" displayname="Resource Modules" remove_on_change="res/*.o res/snmp/*.o">
+ <depend>gnu_ld</depend>
+ </member>
+ </category>
diff --git a/trunk/build_tools/get_makeopts b/trunk/build_tools/get_makeopts
new file mode 100644
index 000000000..e63622afd
--- /dev/null
+++ b/trunk/build_tools/get_makeopts
@@ -0,0 +1,3 @@
+/\/\*\*\* MAKEOPTS/ {printit=1; next}
+/\*\*\*\// {if (printit) exit}
+// {if (printit) print}
diff --git a/trunk/build_tools/get_moduleinfo b/trunk/build_tools/get_moduleinfo
new file mode 100644
index 000000000..d17c28e06
--- /dev/null
+++ b/trunk/build_tools/get_moduleinfo
@@ -0,0 +1,3 @@
+/\/\*\*\* MODULEINFO/ {printit=1; next}
+/\*\*\*\// {if (printit) exit}
+// {if (printit) print}
diff --git a/trunk/build_tools/make_build_h b/trunk/build_tools/make_build_h
new file mode 100755
index 000000000..b7dadfc33
--- /dev/null
+++ b/trunk/build_tools/make_build_h
@@ -0,0 +1,20 @@
+#!/bin/sh
+HOSTNAME=`uname -n`
+KERNEL=`uname -r`
+MACHINE=`uname -m`
+OS=`uname -s`
+USER=`${ID} -un`
+DATE=`date -u "+%Y-%m-%d %H:%M:%S"`
+cat << END
+/*
+ * build.h
+ * Automatically generated
+ */
+#define BUILD_HOSTNAME "${HOSTNAME}"
+#define BUILD_KERNEL "${KERNEL}"
+#define BUILD_MACHINE "${MACHINE}"
+#define BUILD_OS "${OS}"
+#define BUILD_DATE "${DATE} UTC"
+#define BUILD_USER "${USER}"
+
+END
diff --git a/trunk/build_tools/make_buildopts_h b/trunk/build_tools/make_buildopts_h
new file mode 100755
index 000000000..ace404555
--- /dev/null
+++ b/trunk/build_tools/make_buildopts_h
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+cat << END
+/*
+ * buildopts.h
+ * Automatically generated
+ */
+
+END
+TMP=`${GREP} MENUSELECT_CFLAGS menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
+for x in ${TMP}; do
+ echo "#define ${x} 1"
+done
+TMP=`${GREP} MENUSELECT_BUILD_DEPS menuselect.makeopts | sed 's/MENUSELECT_BUILD_DEPS\=//g'`
+for x in ${TMP}; do
+ x2=`echo ${x} | tr a-z A-Z`
+ echo "#define AST_MODULE_${x2} 1"
+done
+if ${GREP} AST_DEVMODE makeopts | ${GREP} -q yes
+then
+ echo "#define AST_DEVMODE 1"
+ TMP="${TMP} AST_DEVMODE"
+fi
+
+case ${OSARCH} in # actually we should check build_os
+*BSD|mingw|darwin*)
+ BUILDSUM=`echo ${TMP} | md5 | cut -c1-32`
+ ;;
+SunOS)
+ BUILDSUM=`echo ${TMP} | digest -a md5 | cut -c1-32`
+ ;;
+*)
+ BUILDSUM=`echo ${TMP} | md5sum | cut -c1-32`
+ ;;
+esac
+
+echo "#define AST_BUILDOPT_SUM \"${BUILDSUM}\""
diff --git a/trunk/build_tools/make_defaults_h b/trunk/build_tools/make_defaults_h
new file mode 100755
index 000000000..0aae60804
--- /dev/null
+++ b/trunk/build_tools/make_defaults_h
@@ -0,0 +1,28 @@
+#!/bin/sh
+cat << END
+/*
+ * defaults.h
+ * Automatically generated from build options,
+ * only used in main/asterisk.c
+ */
+#define DEFAULT_CONFIG_FILE "${INSTALL_PATH}${ASTCONFPATH}"
+
+#define DEFAULT_CONFIG_DIR "${INSTALL_PATH}${ASTETCDIR}"
+#define DEFAULT_MODULE_DIR "${INSTALL_PATH}${MODULES_DIR}"
+#define DEFAULT_AGI_DIR "${INSTALL_PATH}${AGI_DIR}"
+#define DEFAULT_LOG_DIR "${INSTALL_PATH}${ASTLOGDIR}"
+
+#define DEFAULT_RUN_DIR "${INSTALL_PATH}${ASTVARRUNDIR}"
+#define DEFAULT_SOCKET "${INSTALL_PATH}${ASTVARRUNDIR}/asterisk.ctl"
+#define DEFAULT_PID "${INSTALL_PATH}${ASTVARRUNDIR}/asterisk.pid"
+
+#define DEFAULT_VAR_DIR "${INSTALL_PATH}${ASTVARLIBDIR}"
+#define DEFAULT_DB "${INSTALL_PATH}${ASTVARLIBDIR}/astdb"
+
+#define DEFAULT_DATA_DIR "${INSTALL_PATH}${ASTDATADIR}"
+#define DEFAULT_KEY_DIR "${INSTALL_PATH}${ASTDATADIR}/keys"
+
+#define DEFAULT_SPOOL_DIR "${INSTALL_PATH}${ASTSPOOLDIR}"
+#define DEFAULT_TMP_DIR "${INSTALL_PATH}${ASTSPOOLDIR}/tmp"
+
+END
diff --git a/trunk/build_tools/make_linker_eo_script b/trunk/build_tools/make_linker_eo_script
new file mode 100755
index 000000000..39456c058
--- /dev/null
+++ b/trunk/build_tools/make_linker_eo_script
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+cat << EOF
+SECTIONS {
+.text : { *(.text) }
+.data : { __${1}_data_start = . ; *(.data) __${1}_data_end = . ;}
+.rodata : { *(.rodata*) }
+.bss : { __${1}_bss_start = . ; *(.bss) __${1}_bss_end = . ;}
+.debug_abbrev : { *(.debug_abbrev) }
+.debug_info : { *(.debug_info) }
+.debug_line : { *(.debug_line) }
+.debug_macinfo : { *(.debug_macinfo) }
+.dtors : { *(.dtors) }
+.ctors : { *(.ctors) }
+.data.rel.local : { *(.data.rel.local) }
+.data.rel.ro.local : { *(.data.rel.ro.local) }
+.debug_frame : { *(.debug_frame) }
+.eh_frame : { *(.eh_frame) }
+.debug_loc : { *(.debug_loc) }
+.debug_pubname : { *(.debug_pubname) }
+.debug_aranges : { *(.debug_aranges) }
+.debug_ranges : { *(.debug_ranges) }
+.debug_str : { *(.debug_str) }
+.comment : { *(.comment) }
+.note.GNU-stack : { *(.note.GNU-stack) }
+}
+EOF
diff --git a/trunk/build_tools/make_sample_voicemail b/trunk/build_tools/make_sample_voicemail
new file mode 100755
index 000000000..272cf379a
--- /dev/null
+++ b/trunk/build_tools/make_sample_voicemail
@@ -0,0 +1,25 @@
+#!/bin/sh -e
+
+for lang in /en/ /fr/ /es/
+ do
+ for format in ulaw alaw wav gsm g729 g722
+ do
+ [ ! -f ${1}/sounds${lang}vm-isunavail.${format} ] && continue
+
+ mkdir -p ${2}/voicemail/default/1234${lang}
+
+ : > ${2}/voicemail/default/1234${lang}unavail.${format}
+
+ for file in vm-theperson digits/1 digits/2 digits/3 digits/4 vm-isunavail
+ do
+ cat ${1}/sounds${lang}${file}.${format} >> ${2}/voicemail/default/1234${lang}unavail.${format}
+ done
+
+ : > ${2}/voicemail/default/1234${lang}busy.${format}
+
+ for file in vm-theperson digits/1 digits/2 digits/3 digits/4 vm-isonphone
+ do
+ cat ${1}/sounds${lang}${file}.${format} >> ${2}/voicemail/default/1234${lang}busy.${format}
+ done
+ done
+done
diff --git a/trunk/build_tools/make_version b/trunk/build_tools/make_version
new file mode 100755
index 000000000..4e9901dc5
--- /dev/null
+++ b/trunk/build_tools/make_version
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+if [ -f ${1}/.version ]; then
+ cat ${1}/.version
+elif [ -d .svn ]; then
+ PARTS=`LANG=C svn info ${1} | ${GREP} URL | ${AWK} '{print $2;}' | sed -e 's:^.*/svn/asterisk/::' | sed -e 's:/: :g'`
+ BRANCH=0
+ TEAM=0
+ TAG=0
+
+ REV=`svnversion -c ${1} | cut -d: -f2`
+
+ BASE=`LANG=C svn pg svnmerge-integrated ${1} | cut -d: -f1`
+
+ if [ "${PARTS}" = "trunk" ] ; then
+ echo SVN-trunk-r${REV}
+ exit 0
+ fi
+
+ for PART in $PARTS ; do
+ if [ ${TAG} != 0 ] ; then
+ RESULT="${PART}"
+ break
+ fi
+
+ if [ ${BRANCH} != 0 ] ; then
+ if [ -z ${RESULT} ] ; then
+ RESULT="${PART}"
+ else
+ RESULT="${RESULT}-${PART}"
+ fi
+ break
+ fi
+
+ if [ ${TEAM} != 0 ] ; then
+ if [ -z ${RESULT} ] ; then
+ RESULT="${PART}"
+ else
+ RESULT="${RESULT}-${PART}"
+ fi
+ continue
+ fi
+
+ if [ "${PART}" = "branches" ] ; then
+ BRANCH=1
+ RESULT="branch"
+ continue
+ fi
+
+ if [ "${PART}" = "tags" ] ; then
+ TAG=1
+ continue
+ fi
+
+ if [ "${PART}" = "team" ] ; then
+ TEAM=1
+ continue
+ fi
+ done
+
+ if [ ${TAG} != 0 ] ; then
+ echo ${RESULT}
+ else
+ echo SVN-${RESULT}-r${REV}${BASE:+-${BASE}}
+ fi
+fi
diff --git a/trunk/build_tools/make_version_c b/trunk/build_tools/make_version_c
new file mode 100755
index 000000000..291290ddd
--- /dev/null
+++ b/trunk/build_tools/make_version_c
@@ -0,0 +1,33 @@
+#!/bin/sh
+if [ ! -f ../.flavor ]; then
+ EXTRA=""
+else
+ aadkver=`cat ../.version`
+ aadkflavor=`cat ../.flavor`
+ EXTRA=" (${aadkflavor} ${aadkver})"
+fi
+cat << END
+/*
+ * version.c
+ * Automatically generated
+ */
+
+#include "asterisk.h"
+
+#include "asterisk/version.h"
+
+static const char asterisk_version[] = "${ASTERISKVERSION}${EXTRA}";
+
+static const char asterisk_version_num[] = "${ASTERISKVERSIONNUM}";
+
+const char *ast_get_version(void)
+{
+ return asterisk_version;
+}
+
+const char *ast_get_version_num(void)
+{
+ return asterisk_version_num;
+}
+
+END
diff --git a/trunk/build_tools/menuselect-deps.in b/trunk/build_tools/menuselect-deps.in
new file mode 100644
index 000000000..f29ab8920
--- /dev/null
+++ b/trunk/build_tools/menuselect-deps.in
@@ -0,0 +1,47 @@
+ASOUND=@PBX_ALSA@
+CRYPTO=@PBX_CRYPTO@
+CURL=@PBX_CURL@
+FREETDS=@PBX_FREETDS@
+GNU_LD=@GNU_LD@
+GSM=@PBX_GSM@
+GTK2=@PBX_GTK2@
+GTK=@PBX_GTK@
+H323=@PBX_H323@
+ICONV=@PBX_ICONV@
+IKSEMEL=@PBX_IKSEMEL@
+IMAP_TK=@PBX_IMAP_TK@
+ISDNNET=@PBX_ISDNNET@
+IXJUSER=@PBX_IXJUSER@
+JACK=@PBX_JACK@
+LTDL=@PBX_LTDL@
+LUA=@PBX_LUA@
+MISDN=@PBX_MISDN@
+NBS=@PBX_NBS@
+NETSNMP=@PBX_NETSNMP@
+NEWT=@PBX_NEWT@
+OGG=@PBX_OGG@
+OPENH323=@PBX_OPENH323@
+OSPTK=@PBX_OSPTK@
+OSSAUDIO=@PBX_OSS@
+PGSQL=@PBX_PGSQL@
+POPT=@PBX_POPT@
+PORTAUDIO=@PBX_PORTAUDIO@
+PRI=@PBX_PRI@
+RADIUS=@PBX_RADIUS@
+SPEEX=@PBX_SPEEX@
+SPEEXDSP=@PBX_SPEEXDSP@
+SQLITE3=@PBX_SQLITE3@
+SQLITE=@PBX_SQLITE@
+SS7=@PBX_SS7@
+SSL=@PBX_OPENSSL@
+SUPPSERV=@PBX_SUPPSERV@
+TONEZONE=@PBX_TONEZONE@
+UNIXODBC=@PBX_UNIXODBC@
+USB=@PBX_USB@
+VORBIS=@PBX_VORBIS@
+VPBAPI=@PBX_VPB@
+WINARCH=@WINARCH@
+ZAPTEL=@PBX_ZAPTEL@
+ZAPTEL_TRANSCODE=@PBX_ZAPTEL_TRANSCODE@
+ZAPTEL_VLDTMF=@PBX_ZAPTEL_VLDTMF@
+ZLIB=@PBX_ZLIB@
diff --git a/trunk/build_tools/mkpkgconfig b/trunk/build_tools/mkpkgconfig
new file mode 100755
index 000000000..ceea7ebc0
--- /dev/null
+++ b/trunk/build_tools/mkpkgconfig
@@ -0,0 +1,50 @@
+#!/bin/bash
+PPATH=$1
+## Make sure we were called from Makefile
+
+if [ "x$ASTERISKVERSIONNUM" = "x" ]; then
+ echo " ** Do not call this script directly"
+ exit
+fi
+
+## Create a pkgconfig spec file for 3rd party modules (pkg-config asterisk --cflags)
+
+if [ ! -d $PPATH ]; then
+ exit
+fi
+
+#Solaris (and some others) don't have sed -r. perl -p is equivalent
+if [[ `echo "xxx" | sed -r 's/x/y/g' 2>/dev/null | ${GREP} -c "yyy"` != 0 ]]; then
+ EXTREGEX="sed -r -e"
+else
+ EXTREGEX="perl -pe"
+fi
+
+## Clean out CFLAGS for the spec file.
+
+LOCAL_CFLAGS=`echo $CFLAGS | ${EXTREGEX} 's/\s*-pipe\s*//g' | ${EXTREGEX} 's/-[Wmp]\S*\s*//g' | \
+ ${EXTREGEX} 's/-I(include|\.\.\/include) //g' | \
+ ${EXTREGEX} 's/-DINSTALL_PREFIX=\S* //g' | \
+ ${EXTREGEX} 's/-DASTERISK_VERSION=\S* //g' | \
+ ${EXTREGEX} 's/-DAST(ETCDIR|LIBDIR|VARLIBDIR|VARRUNDIR|SPOOLDIR|LOGDIR|CONFPATH|MODDIR|AGIDIR)=\S* //g'`
+
+
+cat <<EOF > $PPATH/asterisk.pc
+install_prefix=$INSTALL_PREFIX
+version_number=$ASTERISKVERSIONNUM
+etcdir=$ASTETCDIR
+libdir=$ASTLIBDIR
+varlibdir=$ASTVARLIBDIR
+varrundir=$ASTVARRUNDIR
+spooldir=$ASTSPOOLDIR
+logdir=$ASTLOGDIR
+confpath=$ASTCONFPATH
+moddir=$MODULES_DIR
+agidir=$AGI_DIR
+
+Name: asterisk
+Description: Open Source PBX and telephony toolkit
+Version: $ASTERISKVERSION
+Libs: $LIBS
+Cflags: $LOCAL_CFLAGS
+EOF
diff --git a/trunk/build_tools/prep_tarball b/trunk/build_tools/prep_tarball
new file mode 100755
index 000000000..5877a8a7b
--- /dev/null
+++ b/trunk/build_tools/prep_tarball
@@ -0,0 +1,13 @@
+#!/bin/sh -e
+
+# This script will be executed by the 'mkrelease' script to do any tasks
+# necessary during tarball creation of this project.
+#
+# It will be executed from the top-level directory of the project.
+
+make -C sounds MENUSELECT_CORE_SOUNDS=CORE-SOUNDS-EN-GSM MENUSELECT_MOH=MOH-FREEPLAY-WAV WGET=wget DOWNLOAD=wget all
+make AWK=awk GREP=grep menuselect-tree
+
+VERSION=`cat .version`
+sed -i -e "s/ASTERISKVERSION/${VERSION}/" doc/tex/asterisk.tex
+cd doc/tex && rubber --pdf asterisk.tex && latex2html asterisk.tex
diff --git a/trunk/build_tools/strip_nonapi b/trunk/build_tools/strip_nonapi
new file mode 100755
index 000000000..8a8d06fcc
--- /dev/null
+++ b/trunk/build_tools/strip_nonapi
@@ -0,0 +1,29 @@
+#!/bin/sh -e
+
+# This script is designed to remove all non-API global symbols from an object
+# file. The only global symbols that should be retained are those that belong
+# to the official namespace. Unfortunately doing this is platform-specific, as
+# the object file manipulation tools are not consistent across platforms.
+#
+# On platforms where this script does not know what to do, the object file
+# will retain non-API global symbols, and this may have unpleasant side effects.
+#
+# Prefixes that belong to the official namespace are:
+# ast_
+# _ast_
+# __ast_
+# astman_
+# pbx_
+# resample_
+
+FILTER="${GREP} -v -e ^ast_ -e ^_ast_ -e ^__ast_ -e ^astman_ -e ^pbx_ -e ^resample_"
+
+case "${OSARCH}" in
+ linux-gnu|FreeBSD)
+ nm ${1} | ${GREP} -e " T " | cut -d" " -f3 | ${FILTER} > striplist
+ sed -e "s/^/-N /" striplist | xargs ${STRIP} ${1}
+ rm -f striplist
+ ;;
+ *)
+ ;;
+esac
diff --git a/trunk/cdr/Makefile b/trunk/cdr/Makefile
new file mode 100644
index 000000000..a2767b31e
--- /dev/null
+++ b/trunk/cdr/Makefile
@@ -0,0 +1,20 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for CDR backends
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=cdr
+MENUSELECT_CATEGORY=CDR
+MENUSELECT_DESCRIPTION=Call Detail Recording
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/trunk/cdr/cdr_adaptive_odbc.c b/trunk/cdr/cdr_adaptive_odbc.c
new file mode 100644
index 000000000..9daca2cf8
--- /dev/null
+++ b/trunk/cdr/cdr_adaptive_odbc.c
@@ -0,0 +1,674 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Tilghman Lesher
+ *
+ * Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.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 Adaptive ODBC CDR backend
+ *
+ * \author Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com>
+ * \ingroup cdr_drivers
+ */
+
+/*** MODULEINFO
+ <depend>unixodbc</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <time.h>
+
+#include <sql.h>
+#include <sqlext.h>
+#include <sqltypes.h>
+
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/res_odbc.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+
+#define CONFIG "cdr_adaptive_odbc.conf"
+
+static char *name = "Adaptive ODBC";
+/* Optimization to reduce number of memory allocations */
+static int maxsize = 512, maxsize2 = 512;
+
+struct columns {
+ char *name;
+ char *cdrname;
+ char *filtervalue;
+ SQLSMALLINT type;
+ SQLINTEGER size;
+ SQLSMALLINT decimals;
+ SQLSMALLINT radix;
+ SQLSMALLINT nullable;
+ SQLINTEGER octetlen;
+ AST_LIST_ENTRY(columns) list;
+};
+
+struct tables {
+ char *connection;
+ char *table;
+ AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
+ AST_RWLIST_ENTRY(tables) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);
+
+static int load_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ const char *tmp, *catg;
+ struct tables *tableptr;
+ struct columns *entry;
+ struct odbc_obj *obj;
+ char columnname[80];
+ char connection[40];
+ char table[40];
+ int lenconnection, lentable;
+ SQLLEN sqlptr;
+ int res = 0;
+ SQLHSTMT stmt = NULL;
+ struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */
+
+ cfg = ast_config_load(CONFIG, config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CDRs.\n");
+ return -1;
+ }
+
+ for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
+ var = ast_variable_browse(cfg, catg);
+ if (!var)
+ continue;
+
+ if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
+ ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg);
+ continue;
+ }
+ ast_copy_string(connection, tmp, sizeof(connection));
+ lenconnection = strlen(connection);
+
+ /* When loading, we want to be sure we can connect. */
+ obj = ast_odbc_request_obj(connection, 1);
+ if (!obj) {
+ ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
+ continue;
+ }
+
+ if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
+ ast_log(LOG_NOTICE, "No table name found. Assuming 'cdr'.\n");
+ tmp = "cdr";
+ }
+ ast_copy_string(table, tmp, sizeof(table));
+ lentable = strlen(table);
+
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
+ ast_odbc_release_obj(obj);
+ continue;
+ }
+
+ res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection);
+ ast_odbc_release_obj(obj);
+ continue;
+ }
+
+ tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
+ if (!tableptr) {
+ ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
+ ast_odbc_release_obj(obj);
+ res = -1;
+ break;
+ }
+
+ tableptr->connection = (char *)tableptr + sizeof(*tableptr);
+ tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
+ ast_copy_string(tableptr->connection, connection, lenconnection + 1);
+ ast_copy_string(tableptr->table, table, lentable + 1);
+
+ ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
+
+ /* Check for filters first */
+ for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
+ if (strncmp(var->name, "filter", 6) == 0) {
+ char *cdrvar = ast_strdupa(var->name + 6);
+ cdrvar = ast_strip(cdrvar);
+ ast_verb(3, "Found filter %s for cdr variable %s in %s@%s\n", var->value, cdrvar, tableptr->table, tableptr->connection);
+
+ entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1);
+ if (!entry) {
+ ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
+ res = -1;
+ break;
+ }
+
+ /* NULL column entry means this isn't a column in the database */
+ entry->name = NULL;
+ entry->cdrname = (char *)entry + sizeof(*entry);
+ entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1;
+ strcpy(entry->cdrname, cdrvar);
+ strcpy(entry->filtervalue, var->value);
+
+ AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
+ }
+ }
+
+ while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
+ char *cdrvar = "";
+
+ SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
+
+ /* Is there an alias for this column? */
+
+ /* NOTE: This seems like a non-optimal parse method, but I'm going
+ * for user configuration readability, rather than fast parsing. We
+ * really don't parse this file all that often, anyway.
+ */
+ for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
+ if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
+ char *tmp = ast_strdupa(var->name + 5);
+ cdrvar = ast_strip(tmp);
+ ast_verb(3, "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection);
+ break;
+ }
+ }
+
+ entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1);
+ if (!entry) {
+ ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
+ res = -1;
+ break;
+ }
+ entry->name = (char *)entry + sizeof(*entry);
+ strcpy(entry->name, columnname);
+
+ if (!ast_strlen_zero(cdrvar)) {
+ entry->cdrname = entry->name + strlen(columnname) + 1;
+ strcpy(entry->cdrname, cdrvar);
+ } else /* Point to same place as the column name */
+ entry->cdrname = (char *)entry + sizeof(*entry);
+
+ SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
+ SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
+ SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
+ SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
+ SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
+ SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
+
+ /* Specification states that the octenlen should be the maximum number of bytes
+ * returned in a char or binary column, but it seems that some drivers just set
+ * it to NULL. (Bad Postgres! No biscuit!) */
+ if (entry->octetlen == 0)
+ entry->octetlen = entry->size;
+
+ ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
+ /* Insert column info into column list */
+ AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
+ res = 0;
+ }
+
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+
+ if (AST_LIST_FIRST(&(tableptr->columns)))
+ AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
+ else
+ ast_free(tableptr);
+ }
+ return res;
+}
+
+static int free_config(void)
+{
+ struct tables *table;
+ struct columns *entry;
+ while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
+ while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
+ ast_free(entry);
+ }
+ ast_free(table);
+ }
+ return 0;
+}
+
+static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
+{
+ int res, i;
+ char *sql = data;
+ SQLHSTMT stmt;
+ SQLINTEGER nativeerror = 0, numfields = 0;
+ SQLSMALLINT diagbytes = 0;
+ unsigned char state[10], diagnostic[256];
+
+ res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ return NULL;
+ }
+
+ res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+ SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
+ for (i = 0; i < numfields; i++) {
+ SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
+ ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
+ if (i > 10) {
+ ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
+ break;
+ }
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
+#define LENGTHEN_BUF1(size) \
+ do { \
+ /* Lengthen buffer, if necessary */ \
+ if ((newsize = lensql + (size) + 3) > sizesql) { \
+ if ((tmp = ast_realloc(sql, (newsize / 512 + 1) * 512))) { \
+ sql = tmp; \
+ sizesql = (newsize / 512 + 1) * 512; \
+ } else { \
+ ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
+ ast_free(sql); \
+ ast_free(sql2); \
+ AST_RWLIST_UNLOCK(&odbc_tables); \
+ return -1; \
+ } \
+ } \
+ } while (0)
+
+#define LENGTHEN_BUF2(size) \
+ do { \
+ if ((newsize = lensql2 + (size) + 3) > sizesql2) { \
+ if ((tmp = ast_realloc(sql2, (newsize / 512 + 1) * 512))) { \
+ sql2 = tmp; \
+ sizesql2 = (newsize / 512 + 1) * 512; \
+ } else { \
+ ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
+ ast_free(sql); \
+ ast_free(sql2); \
+ AST_RWLIST_UNLOCK(&odbc_tables); \
+ return -1; \
+ } \
+ } \
+ } while (0)
+
+static int odbc_log(struct ast_cdr *cdr)
+{
+ struct tables *tableptr;
+ struct columns *entry;
+ struct odbc_obj *obj;
+ int lensql, lensql2, sizesql = maxsize, sizesql2 = maxsize2, newsize;
+ /* Allocated, so we can realloc() */
+ char *sql = ast_calloc(sizeof(char), sizesql), *sql2 = ast_calloc(sizeof(char), sizesql2), *tmp;
+ char colbuf[1024], *colptr;
+ SQLHSTMT stmt = NULL;
+ SQLLEN rows = 0;
+
+ if (!sql || !sql2) {
+ if (sql)
+ ast_free(sql);
+ if (sql2)
+ ast_free(sql2);
+ return -1;
+ }
+
+ if (AST_RWLIST_RDLOCK(&odbc_tables)) {
+ ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n");
+ ast_free(sql);
+ ast_free(sql2);
+ return -1;
+ }
+
+ AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
+ lensql = snprintf(sql, sizesql, "INSERT INTO %s (", tableptr->table);
+ lensql2 = snprintf(sql2, sizesql2, " VALUES (");
+
+ /* No need to check the connection now; we'll handle any failure in prepare_and_execute */
+ if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
+ ast_log(LOG_WARNING, "cdr_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql);
+ continue;
+ }
+
+ AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
+ /* Check if we have a similarly named variable */
+ ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0,
+ (strcasecmp(entry->cdrname, "start") == 0 ||
+ strcasecmp(entry->cdrname, "answer") == 0 ||
+ strcasecmp(entry->cdrname, "end") == 0) ? 0 : 1);
+
+ if (colptr) {
+ /* Check first if the column filters this entry. Note that this
+ * is very specifically NOT ast_strlen_zero(), because the filter
+ * could legitimately specify that the field is blank, which is
+ * different from the field being unspecified (NULL). */
+ if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
+ ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
+ " '%s'. Cancelling this CDR.\n",
+ entry->cdrname, colptr, entry->filtervalue);
+ goto early_release;
+ }
+
+ /* Only a filter? */
+ if (ast_strlen_zero(entry->name))
+ continue;
+
+ LENGTHEN_BUF1(strlen(entry->name));
+
+ switch (entry->type) {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_LONGVARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_LONGVARBINARY:
+ case SQL_GUID:
+ /* For these two field names, get the rendered form, instead of the raw
+ * form (but only when we're dealing with a character-based field).
+ */
+ if (strcasecmp(entry->name, "disposition") == 0)
+ ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
+ else if (strcasecmp(entry->name, "amaflags") == 0)
+ ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
+
+ /* Truncate too-long fields */
+ if (entry->type != SQL_GUID) {
+ if (strlen(colptr) > entry->octetlen)
+ colptr[entry->octetlen] = '\0';
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(strlen(colptr));
+
+ /* Encode value, with escaping */
+ strcpy(sql2 + lensql2, "'");
+ lensql2++;
+ for (tmp = colptr; *tmp; tmp++) {
+ if (*tmp == '\'') {
+ strcpy(sql2 + lensql2, "''");
+ lensql2 += 2;
+ } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
+ strcpy(sql2 + lensql2, "\\\\");
+ lensql2 += 2;
+ } else {
+ sql2[lensql2++] = *tmp;
+ sql2[lensql2] = '\0';
+ }
+ }
+ strcpy(sql2 + lensql2, "',");
+ lensql2 += 2;
+ break;
+ case SQL_TYPE_DATE:
+ {
+ int year = 0, month = 0, day = 0;
+ if (sscanf(colptr, "%d-%d-%d", &year, &month, &day) != 3 || year <= 0 ||
+ month <= 0 || month > 12 || day < 0 || day > 31 ||
+ ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
+ (month == 2 && year % 400 == 0 && day > 29) ||
+ (month == 2 && year % 100 == 0 && day > 28) ||
+ (month == 2 && year % 4 == 0 && day > 29) ||
+ (month == 2 && year % 4 != 0 && day > 28)) {
+ ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
+ break;
+ }
+
+ if (year > 0 && year < 100)
+ year += 2000;
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(17);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "{ d '%04d-%02d-%02d' },", year, month, day);
+ }
+ break;
+ case SQL_TYPE_TIME:
+ {
+ int hour = 0, minute = 0, second = 0;
+ int count = sscanf(colptr, "%d:%d:%d", &hour, &minute, &second);
+
+ if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
+ ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
+ break;
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(15);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "{ t '%02d:%02d:%02d' },", hour, minute, second);
+ }
+ break;
+ case SQL_TYPE_TIMESTAMP:
+ case SQL_TIMESTAMP:
+ {
+ int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
+ int count = sscanf(colptr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+ if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
+ month <= 0 || month > 12 || day < 0 || day > 31 ||
+ ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
+ (month == 2 && year % 400 == 0 && day > 29) ||
+ (month == 2 && year % 100 == 0 && day > 28) ||
+ (month == 2 && year % 4 == 0 && day > 29) ||
+ (month == 2 && year % 4 != 0 && day > 28) ||
+ hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
+ ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
+ break;
+ }
+
+ if (year > 0 && year < 100)
+ year += 2000;
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(26);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "{ ts '%04d-%02d-%02d %02d:%02d:%02d' },", year, month, day, hour, minute, second);
+ }
+ break;
+ case SQL_INTEGER:
+ {
+ int integer = 0;
+ if (sscanf(colptr, "%d", &integer) != 1) {
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+ break;
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(12);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
+ }
+ break;
+ case SQL_BIGINT:
+ {
+ long long integer = 0;
+ if (sscanf(colptr, "%lld", &integer) != 1) {
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+ break;
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(24);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%lld,", integer);
+ }
+ break;
+ case SQL_SMALLINT:
+ {
+ short integer = 0;
+ if (sscanf(colptr, "%hd", &integer) != 1) {
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+ break;
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(6);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
+ }
+ break;
+ case SQL_TINYINT:
+ {
+ char integer = 0;
+ if (sscanf(colptr, "%hhd", &integer) != 1) {
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+ break;
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(4);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
+ }
+ break;
+ case SQL_BIT:
+ {
+ char integer = 0;
+ if (sscanf(colptr, "%hhd", &integer) != 1) {
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
+ break;
+ }
+ if (integer != 0)
+ integer = 1;
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(2);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
+ }
+ break;
+ case SQL_NUMERIC:
+ case SQL_DECIMAL:
+ {
+ double number = 0.0;
+ if (sscanf(colptr, "%lf", &number) != 1) {
+ ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
+ break;
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(entry->decimals);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%*.*lf,", entry->decimals, entry->radix, number);
+ }
+ break;
+ case SQL_FLOAT:
+ case SQL_REAL:
+ case SQL_DOUBLE:
+ {
+ double number = 0.0;
+ if (sscanf(colptr, "%lf", &number) != 1) {
+ ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
+ break;
+ }
+
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
+ LENGTHEN_BUF2(entry->decimals);
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%lf,", number);
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
+ }
+ }
+ }
+
+ /* Concatenate the two constructed buffers */
+ LENGTHEN_BUF1(lensql2);
+ sql[lensql - 1] = ')';
+ sql2[lensql2 - 1] = ')';
+ strcat(sql + lensql, sql2);
+
+ ast_verb(11, "[%s]\n", sql);
+
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, sql);
+ if (stmt) {
+ SQLRowCount(stmt, &rows);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ }
+ if (rows == 0) {
+ ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql);
+ }
+early_release:
+ ast_odbc_release_obj(obj);
+ }
+ AST_RWLIST_UNLOCK(&odbc_tables);
+
+ /* Next time, just allocate buffers that are that big to start with. */
+ if (sizesql > maxsize)
+ maxsize = sizesql;
+ if (sizesql2 > maxsize2)
+ maxsize2 = sizesql2;
+
+ ast_free(sql);
+ ast_free(sql2);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ ast_cdr_unregister(name);
+ usleep(1);
+ if (AST_RWLIST_WRLOCK(&odbc_tables)) {
+ ast_cdr_register(name, ast_module_info->description, odbc_log);
+ ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
+ return -1;
+ }
+
+ free_config();
+ AST_RWLIST_UNLOCK(&odbc_tables);
+ return 0;
+}
+
+static int load_module(void)
+{
+ if (AST_RWLIST_WRLOCK(&odbc_tables)) {
+ ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
+ return 0;
+ }
+
+ load_config();
+ AST_RWLIST_UNLOCK(&odbc_tables);
+ ast_cdr_register(name, ast_module_info->description, odbc_log);
+ return 0;
+}
+
+static int reload(void)
+{
+ if (AST_RWLIST_WRLOCK(&odbc_tables)) {
+ ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
+ return -1;
+ }
+
+ free_config();
+ load_config();
+ AST_RWLIST_UNLOCK(&odbc_tables);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Adaptive ODBC CDR backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
+
diff --git a/trunk/cdr/cdr_csv.c b/trunk/cdr/cdr_csv.c
new file mode 100644
index 000000000..cca1e8714
--- /dev/null
+++ b/trunk/cdr/cdr_csv.c
@@ -0,0 +1,346 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ * 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 Comma Separated Value CDR records.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg See also \ref AstCDR
+ * \ingroup cdr_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+#define CSV_LOG_DIR "/cdr-csv"
+#define CSV_MASTER "/Master.csv"
+
+#define DATE_FORMAT "%Y-%m-%d %T"
+
+static int usegmtime = 0;
+static int loguniqueid = 0;
+static int loguserfield = 0;
+static char *config = "cdr.conf";
+
+/* #define CSV_LOGUNIQUEID 1 */
+/* #define CSV_LOGUSERFIELD 1 */
+
+/*----------------------------------------------------
+ The values are as follows:
+
+
+ "accountcode", accountcode is the account name of detail records, Master.csv contains all records *
+ Detail records are configured on a channel basis, IAX and SIP are determined by user *
+ Zap is determined by channel in zaptel.conf
+ "source",
+ "destination",
+ "destination context",
+ "callerid",
+ "channel",
+ "destination channel", (if applicable)
+ "last application", Last application run on the channel
+ "last app argument", argument to the last channel
+ "start time",
+ "answer time",
+ "end time",
+ duration, Duration is the whole length that the entire call lasted. ie. call rx'd to hangup
+ "end time" minus "start time"
+ billable seconds, the duration that a call was up after other end answered which will be <= to duration
+ "end time" minus "answer time"
+ "disposition", ANSWERED, NO ANSWER, BUSY
+ "amaflags", DOCUMENTATION, BILL, IGNORE etc, specified on a per channel basis like accountcode.
+ "uniqueid", unique call identifier
+ "userfield" user field set via SetCDRUserField
+----------------------------------------------------------*/
+
+static char *name = "csv";
+
+AST_MUTEX_DEFINE_STATIC(mf_lock);
+AST_MUTEX_DEFINE_STATIC(acf_lock);
+
+static int load_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ const char *tmp;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ usegmtime = 0;
+ loguniqueid = 0;
+ loguserfield = 0;
+
+ if (!(cfg = ast_config_load(config, config_flags))) {
+ ast_log(LOG_WARNING, "unable to load config: %s\n", config);
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (!(var = ast_variable_browse(cfg, "csv"))) {
+ ast_config_destroy(cfg);
+ return 0;
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, "csv", "usegmtime"))) {
+ usegmtime = ast_true(tmp);
+ if (usegmtime)
+ ast_debug(1, "logging time in GMT\n");
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, "csv", "loguniqueid"))) {
+ loguniqueid = ast_true(tmp);
+ if (loguniqueid)
+ ast_debug(1, "logging CDR field UNIQUEID\n");
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, "csv", "loguserfield"))) {
+ loguserfield = ast_true(tmp);
+ if (loguserfield)
+ ast_debug(1, "logging CDR user-defined field\n");
+ }
+
+ ast_config_destroy(cfg);
+ return 1;
+}
+
+static int append_string(char *buf, char *s, size_t bufsize)
+{
+ int pos = strlen(buf), spos = 0, error = -1;
+
+ if (pos >= bufsize - 4)
+ return -1;
+
+ buf[pos++] = '\"';
+
+ while(pos < bufsize - 3) {
+ if (!s[spos]) {
+ error = 0;
+ break;
+ }
+ if (s[spos] == '\"')
+ buf[pos++] = '\"';
+ buf[pos++] = s[spos];
+ spos++;
+ }
+
+ buf[pos++] = '\"';
+ buf[pos++] = ',';
+ buf[pos++] = '\0';
+
+ return error;
+}
+
+static int append_int(char *buf, int s, size_t bufsize)
+{
+ char tmp[32];
+ int pos = strlen(buf);
+
+ snprintf(tmp, sizeof(tmp), "%d", s);
+
+ if (pos + strlen(tmp) > bufsize - 3)
+ return -1;
+
+ strncat(buf, tmp, bufsize - strlen(buf) - 1);
+ pos = strlen(buf);
+ buf[pos++] = ',';
+ buf[pos++] = '\0';
+
+ return 0;
+}
+
+static int append_date(char *buf, struct timeval tv, size_t bufsize)
+{
+ char tmp[80] = "";
+ struct ast_tm tm;
+
+ if (strlen(buf) > bufsize - 3)
+ return -1;
+
+ if (ast_tvzero(tv)) {
+ strncat(buf, ",", bufsize - strlen(buf) - 1);
+ return 0;
+ }
+
+ ast_localtime(&tv, &tm, usegmtime ? "GMT" : NULL);
+ ast_strftime(tmp, sizeof(tmp), DATE_FORMAT, &tm);
+
+ return append_string(buf, tmp, bufsize);
+}
+
+static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
+{
+
+ buf[0] = '\0';
+ /* Account code */
+ append_string(buf, cdr->accountcode, bufsize);
+ /* Source */
+ append_string(buf, cdr->src, bufsize);
+ /* Destination */
+ append_string(buf, cdr->dst, bufsize);
+ /* Destination context */
+ append_string(buf, cdr->dcontext, bufsize);
+ /* Caller*ID */
+ append_string(buf, cdr->clid, bufsize);
+ /* Channel */
+ append_string(buf, cdr->channel, bufsize);
+ /* Destination Channel */
+ append_string(buf, cdr->dstchannel, bufsize);
+ /* Last Application */
+ append_string(buf, cdr->lastapp, bufsize);
+ /* Last Data */
+ append_string(buf, cdr->lastdata, bufsize);
+ /* Start Time */
+ append_date(buf, cdr->start, bufsize);
+ /* Answer Time */
+ append_date(buf, cdr->answer, bufsize);
+ /* End Time */
+ append_date(buf, cdr->end, bufsize);
+ /* Duration */
+ append_int(buf, cdr->duration, bufsize);
+ /* Billable seconds */
+ append_int(buf, cdr->billsec, bufsize);
+ /* Disposition */
+ append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
+ /* AMA Flags */
+ append_string(buf, ast_cdr_flags2str(cdr->amaflags), bufsize);
+ /* Unique ID */
+ if (loguniqueid)
+ append_string(buf, cdr->uniqueid, bufsize);
+ /* append the user field */
+ if(loguserfield)
+ append_string(buf, cdr->userfield,bufsize);
+ /* If we hit the end of our buffer, log an error */
+ if (strlen(buf) < bufsize - 5) {
+ /* Trim off trailing comma */
+ buf[strlen(buf) - 1] = '\0';
+ strncat(buf, "\n", bufsize - strlen(buf) - 1);
+ return 0;
+ }
+ return -1;
+}
+
+static int writefile(char *s, char *acc)
+{
+ char tmp[PATH_MAX];
+ FILE *f;
+
+ if (strchr(acc, '/') || (acc[0] == '.')) {
+ ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n", acc);
+ return -1;
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s/%s/%s.csv", ast_config_AST_LOG_DIR,CSV_LOG_DIR, acc);
+
+ ast_mutex_lock(&acf_lock);
+ if (!(f = fopen(tmp, "a"))) {
+ ast_mutex_unlock(&acf_lock);
+ ast_log(LOG_ERROR, "Unable to open file %s : %s\n", tmp, strerror(errno));
+ return -1;
+ }
+ fputs(s, f);
+ fflush(f);
+ fclose(f);
+ ast_mutex_unlock(&acf_lock);
+
+ return 0;
+}
+
+
+static int csv_log(struct ast_cdr *cdr)
+{
+ FILE *mf = NULL;
+ /* Make sure we have a big enough buf */
+ char buf[1024];
+ char csvmaster[PATH_MAX];
+ snprintf(csvmaster, sizeof(csvmaster),"%s/%s/%s", ast_config_AST_LOG_DIR, CSV_LOG_DIR, CSV_MASTER);
+#if 0
+ printf("[CDR] %s ('%s' -> '%s') Dur: %ds Bill: %ds Disp: %s Flags: %s Account: [%s]\n", cdr->channel, cdr->src, cdr->dst, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->accountcode);
+#endif
+ if (build_csv_record(buf, sizeof(buf), cdr)) {
+ ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes. CDR not recorded!\n", (int)sizeof(buf));
+ return 0;
+ }
+
+ /* because of the absolutely unconditional need for the
+ highest reliability possible in writing billing records,
+ we open write and close the log file each time */
+ ast_mutex_lock(&mf_lock);
+ if ((mf = fopen(csvmaster, "a"))) {
+ fputs(buf, mf);
+ fflush(mf); /* be particularly anal here */
+ fclose(mf);
+ mf = NULL;
+ ast_mutex_unlock(&mf_lock);
+ } else {
+ ast_mutex_unlock(&mf_lock);
+ ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", csvmaster, strerror(errno));
+ }
+
+ if (!ast_strlen_zero(cdr->accountcode)) {
+ if (writefile(buf, cdr->accountcode))
+ ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s' : %s\n", cdr->accountcode, strerror(errno));
+ }
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ ast_cdr_unregister(name);
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if(!load_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ if ((res = ast_cdr_register(name, ast_module_info->description, csv_log)))
+ ast_log(LOG_ERROR, "Unable to register CSV CDR handling\n");
+
+ return res;
+}
+
+static int reload(void)
+{
+ load_config(1);
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Comma Separated Values CDR Backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/cdr/cdr_custom.c b/trunk/cdr/cdr_custom.c
new file mode 100644
index 000000000..fbfb8d527
--- /dev/null
+++ b/trunk/cdr/cdr_custom.c
@@ -0,0 +1,169 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ * 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 Custom Comma Separated Value CDR records.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg See also \ref AstCDR
+ *
+ * Logs in LOG_DIR/cdr_custom
+ * \ingroup cdr_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+
+#define CUSTOM_LOG_DIR "/cdr_custom"
+
+#define DATE_FORMAT "%Y-%m-%d %T"
+
+AST_MUTEX_DEFINE_STATIC(lock);
+
+static char *name = "cdr-custom";
+
+static FILE *mf = NULL;
+
+static char master[PATH_MAX];
+static char format[1024]="";
+
+static int load_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ int res = -1;
+
+ if ((cfg = ast_config_load("cdr_custom.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ strcpy(format, "");
+ strcpy(master, "");
+ ast_mutex_lock(&lock);
+ if (cfg) {
+ var = ast_variable_browse(cfg, "mappings");
+ while(var) {
+ if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
+ if (strlen(var->value) > (sizeof(format) - 1))
+ ast_log(LOG_WARNING, "Format string too long, will be truncated, at line %d\n", var->lineno);
+ ast_copy_string(format, var->value, sizeof(format) - 1);
+ strcat(format,"\n");
+ snprintf(master, sizeof(master),"%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
+ if (var->next) {
+ ast_log(LOG_NOTICE, "Sorry, only one mapping is supported at this time, mapping '%s' will be ignored at line %d.\n", var->next->name, var->next->lineno);
+ break;
+ }
+ } else
+ ast_log(LOG_NOTICE, "Mapping must have both filename and format at line %d\n", var->lineno);
+ var = var->next;
+ }
+ ast_config_destroy(cfg);
+ res = 0;
+ } else {
+ if (reload)
+ ast_log(LOG_WARNING, "Failed to reload configuration file.\n");
+ else
+ ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
+ }
+ ast_mutex_unlock(&lock);
+
+ return res;
+}
+
+
+
+static int custom_log(struct ast_cdr *cdr)
+{
+ /* Make sure we have a big enough buf */
+ char buf[2048];
+ struct ast_channel dummy;
+
+ /* Abort if no master file is specified */
+ if (ast_strlen_zero(master))
+ return 0;
+
+ /* Quite possibly the first use of a static struct ast_channel, we need it so the var funcs will work */
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.cdr = cdr;
+ pbx_substitute_variables_helper(&dummy, format, buf, sizeof(buf) - 1);
+
+ /* because of the absolutely unconditional need for the
+ highest reliability possible in writing billing records,
+ we open write and close the log file each time */
+ mf = fopen(master, "a");
+ if (!mf) {
+ ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", master, strerror(errno));
+ }
+ if (mf) {
+ fputs(buf, mf);
+ fflush(mf); /* be particularly anal here */
+ fclose(mf);
+ mf = NULL;
+ }
+ return 0;
+}
+
+static int unload_module(void)
+{
+ if (mf)
+ fclose(mf);
+ ast_cdr_unregister(name);
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ if (!load_config(0)) {
+ res = ast_cdr_register(name, ast_module_info->description, custom_log);
+ if (res)
+ ast_log(LOG_ERROR, "Unable to register custom CDR handling\n");
+ if (mf)
+ fclose(mf);
+ return res;
+ } else
+ return AST_MODULE_LOAD_DECLINE;
+}
+
+static int reload(void)
+{
+ return load_config(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Customizable Comma Separated Values CDR Backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/trunk/cdr/cdr_manager.c b/trunk/cdr/cdr_manager.c
new file mode 100644
index 000000000..8b2ab215d
--- /dev/null
+++ b/trunk/cdr/cdr_manager.c
@@ -0,0 +1,204 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005
+ *
+ * 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 Asterisk Call Manager CDR records.
+ *
+ * See also
+ * \arg \ref AstCDR
+ * \arg \ref AstAMI
+ * \arg \ref Config_ami
+ * \ingroup cdr_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+#include "asterisk/manager.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+
+#define DATE_FORMAT "%Y-%m-%d %T"
+#define CONF_FILE "cdr_manager.conf"
+#define CUSTOM_FIELDS_BUF_SIZE 1024
+
+static char *name = "cdr_manager";
+
+static int enablecdr = 0;
+struct ast_str *customfields;
+
+static int manager_log(struct ast_cdr *cdr);
+
+static int load_config(int reload)
+{
+ char *cat = NULL;
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ int newenablecdr = 0;
+
+ cfg = ast_config_load(CONF_FILE, config_flags);
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (reload && customfields) {
+ ast_free(customfields);
+ }
+ customfields = NULL;
+
+ if (!cfg) {
+ /* Standard configuration */
+ ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
+ if (enablecdr)
+ ast_cdr_unregister(name);
+ enablecdr = 0;
+ return 0;
+ }
+
+ while ( (cat = ast_category_browse(cfg, cat)) ) {
+ if (!strcasecmp(cat, "general")) {
+ v = ast_variable_browse(cfg, cat);
+ while (v) {
+ if (!strcasecmp(v->name, "enabled"))
+ newenablecdr = ast_true(v->value);
+
+ v = v->next;
+ }
+ } else if (!strcasecmp(cat, "mappings")) {
+ customfields = ast_str_create(CUSTOM_FIELDS_BUF_SIZE);
+ v = ast_variable_browse(cfg, cat);
+ while (v) {
+ if (customfields && !ast_strlen_zero(v->name) && !ast_strlen_zero(v->value)) {
+ if( (customfields->used + strlen(v->value) + strlen(v->name) + 14) < customfields->len) {
+ ast_str_append(&customfields, -1, "%s: ${CDR(%s)}\r\n", v->value, v->name);
+ ast_log(LOG_NOTICE, "Added mapping %s: ${CDR(%s)}\n", v->value, v->name);
+ } else {
+ ast_log(LOG_WARNING, "No more buffer space to add other custom fields\n");
+ break;
+ }
+
+ }
+ v = v->next;
+ }
+ }
+ }
+
+ ast_config_destroy(cfg);
+
+ if (enablecdr && !newenablecdr)
+ ast_cdr_unregister(name);
+ else if (!enablecdr && newenablecdr)
+ ast_cdr_register(name, "Asterisk Manager Interface CDR Backend", manager_log);
+ enablecdr = newenablecdr;
+
+ return 1;
+}
+
+static int manager_log(struct ast_cdr *cdr)
+{
+ struct ast_tm timeresult;
+ char strStartTime[80] = "";
+ char strAnswerTime[80] = "";
+ char strEndTime[80] = "";
+ char buf[CUSTOM_FIELDS_BUF_SIZE];
+ struct ast_channel dummy;
+
+ if (!enablecdr)
+ return 0;
+
+ ast_localtime(&cdr->start, &timeresult, NULL);
+ ast_strftime(strStartTime, sizeof(strStartTime), DATE_FORMAT, &timeresult);
+
+ if (cdr->answer.tv_sec) {
+ ast_localtime(&cdr->answer, &timeresult, NULL);
+ ast_strftime(strAnswerTime, sizeof(strAnswerTime), DATE_FORMAT, &timeresult);
+ }
+
+ ast_localtime(&cdr->end, &timeresult, NULL);
+ ast_strftime(strEndTime, sizeof(strEndTime), DATE_FORMAT, &timeresult);
+
+ buf[0] = 0;
+ /* Custom fields handling */
+ if (customfields != NULL && customfields->used > 0) {
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.cdr = cdr;
+ pbx_substitute_variables_helper(&dummy, customfields->str, buf, sizeof(buf) - 1);
+ }
+
+ manager_event(EVENT_FLAG_CDR, "Cdr",
+ "AccountCode: %s\r\n"
+ "Source: %s\r\n"
+ "Destination: %s\r\n"
+ "DestinationContext: %s\r\n"
+ "CallerID: %s\r\n"
+ "Channel: %s\r\n"
+ "DestinationChannel: %s\r\n"
+ "LastApplication: %s\r\n"
+ "LastData: %s\r\n"
+ "StartTime: %s\r\n"
+ "AnswerTime: %s\r\n"
+ "EndTime: %s\r\n"
+ "Duration: %ld\r\n"
+ "BillableSeconds: %ld\r\n"
+ "Disposition: %s\r\n"
+ "AMAFlags: %s\r\n"
+ "UniqueID: %s\r\n"
+ "UserField: %s\r\n"
+ "%s",
+ cdr->accountcode, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->channel,
+ cdr->dstchannel, cdr->lastapp, cdr->lastdata, strStartTime, strAnswerTime, strEndTime,
+ cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition),
+ ast_cdr_flags2str(cdr->amaflags), cdr->uniqueid, cdr->userfield,buf);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ ast_cdr_unregister(name);
+ if (customfields)
+ ast_free(customfields);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ /* Configuration file */
+ if (!load_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload(void)
+{
+ return load_config(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Manager Interface CDR Backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/cdr/cdr_odbc.c b/trunk/cdr/cdr_odbc.c
new file mode 100644
index 000000000..af02d393c
--- /dev/null
+++ b/trunk/cdr/cdr_odbc.c
@@ -0,0 +1,264 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2003-2005, Digium, Inc.
+ *
+ * Brian K. West <brian@bkw.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 ODBC CDR Backend
+ *
+ * \author Brian K. West <brian@bkw.org>
+ *
+ * See also:
+ * \arg http://www.unixodbc.org
+ * \arg \ref Config_cdr
+ * \ingroup cdr_drivers
+ */
+
+/*** MODULEINFO
+ <depend>unixodbc</depend>
+ <depend>ltdl</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/res_odbc.h"
+
+#define DATE_FORMAT "%Y-%m-%d %T"
+
+static char *name = "ODBC";
+static char *config_file = "cdr_odbc.conf";
+static char *dsn = NULL, *table = NULL;
+
+enum {
+ CONFIG_LOGUNIQUEID = 1 << 0,
+ CONFIG_USEGMTIME = 1 << 1,
+ CONFIG_DISPOSITIONSTRING = 1 << 2,
+};
+
+static struct ast_flags config = { 0 };
+
+static SQLHSTMT prepare_cb(struct odbc_obj *obj, void *data)
+{
+ struct ast_cdr *cdr = data;
+ SQLRETURN ODBC_res;
+ char sqlcmd[2048] = "", timestr[128];
+ struct ast_tm tm;
+ SQLHSTMT stmt;
+
+ ast_localtime(&cdr->start, &tm, ast_test_flag(&config, CONFIG_USEGMTIME) ? "GMT" : NULL);
+ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
+
+ if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
+ snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
+ "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,"
+ "lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) "
+ "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
+ } else {
+ snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
+ "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
+ "duration,billsec,disposition,amaflags,accountcode) "
+ "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
+ }
+
+ ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+
+ if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
+ ast_verb(11, "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+
+ ODBC_res = SQLPrepare(stmt, (unsigned char *)sqlcmd, SQL_NTS);
+
+ if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
+ ast_verb(11, "cdr_odbc: Error in PREPARE %d\n", ODBC_res);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->clid), 0, cdr->clid, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->src), 0, cdr->src, 0, NULL);
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dst), 0, cdr->dst, 0, NULL);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dcontext), 0, cdr->dcontext, 0, NULL);
+ SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->channel), 0, cdr->channel, 0, NULL);
+ SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
+ SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
+ SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
+ SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
+ SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
+ if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING))
+ SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
+ else
+ SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
+ SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
+ SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);
+
+ if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
+ SQLBindParameter(stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL);
+ SQLBindParameter(stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL);
+ }
+
+ return stmt;
+}
+
+
+static int odbc_log(struct ast_cdr *cdr)
+{
+ struct odbc_obj *obj = ast_odbc_request_obj(dsn, 0);
+ SQLHSTMT stmt;
+
+ if (!obj) {
+ ast_log(LOG_ERROR, "Unable to retrieve database handle. CDR failed.\n");
+ return -1;
+ }
+
+ stmt = ast_odbc_prepare_and_execute(obj, prepare_cb, cdr);
+ if (stmt) {
+ SQLLEN rows = 0;
+
+ SQLRowCount(stmt, &rows);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+ if (rows == 0)
+ ast_log(LOG_WARNING, "CDR successfully ran, but inserted 0 rows?\n");
+ } else
+ ast_log(LOG_ERROR, "CDR prepare or execute failed\n");
+ ast_odbc_release_obj(obj);
+ return 0;
+}
+
+static int odbc_load_module(int reload)
+{
+ int res = 0;
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ const char *tmp;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ do {
+ cfg = ast_config_load(config_file, config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config_file);
+ res = AST_MODULE_LOAD_DECLINE;
+ break;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ break;
+
+ var = ast_variable_browse(cfg, "global");
+ if (!var) {
+ /* nothing configured */
+ break;
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, "global", "dsn")) == NULL) {
+ ast_log(LOG_WARNING, "cdr_odbc: dsn not specified. Assuming asteriskdb\n");
+ tmp = "asteriskdb";
+ }
+ if (dsn)
+ ast_free(dsn);
+ dsn = ast_strdup(tmp);
+ if (dsn == NULL) {
+ res = -1;
+ break;
+ }
+
+ if (((tmp = ast_variable_retrieve(cfg, "global", "dispositionstring"))) && ast_true(tmp))
+ ast_set_flag(&config, CONFIG_DISPOSITIONSTRING);
+ else
+ ast_clear_flag(&config, CONFIG_DISPOSITIONSTRING);
+
+ if (((tmp = ast_variable_retrieve(cfg, "global", "loguniqueid"))) && ast_true(tmp)) {
+ ast_set_flag(&config, CONFIG_LOGUNIQUEID);
+ ast_debug(1, "cdr_odbc: Logging uniqueid\n");
+ } else {
+ ast_clear_flag(&config, CONFIG_LOGUNIQUEID);
+ ast_debug(1, "cdr_odbc: Not logging uniqueid\n");
+ }
+
+ if (((tmp = ast_variable_retrieve(cfg, "global", "usegmtime"))) && ast_true(tmp)) {
+ ast_set_flag(&config, CONFIG_USEGMTIME);
+ ast_debug(1, "cdr_odbc: Logging in GMT\n");
+ } else {
+ ast_clear_flag(&config, CONFIG_USEGMTIME);
+ ast_debug(1, "cdr_odbc: Logging in local time\n");
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, "global", "table")) == NULL) {
+ ast_log(LOG_WARNING, "cdr_odbc: table not specified. Assuming cdr\n");
+ tmp = "cdr";
+ }
+ if (table)
+ ast_free(table);
+ table = ast_strdup(tmp);
+ if (table == NULL) {
+ res = -1;
+ break;
+ }
+
+ ast_verb(3, "cdr_odbc: dsn is %s\n", dsn);
+ ast_verb(3, "cdr_odbc: table is %s\n", table);
+
+ res = ast_cdr_register(name, ast_module_info->description, odbc_log);
+ if (res) {
+ ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n");
+ }
+ } while (0);
+
+ if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED)
+ ast_config_destroy(cfg);
+ return res;
+}
+
+static int load_module(void)
+{
+ return odbc_load_module(0);
+}
+
+static int unload_module(void)
+{
+ ast_cdr_unregister(name);
+
+ if (dsn) {
+ ast_verb(11, "cdr_odbc: free dsn\n");
+ ast_free(dsn);
+ }
+ if (table) {
+ ast_verb(11, "cdr_odbc: free table\n");
+ ast_free(table);
+ }
+
+ return 0;
+}
+
+static int reload(void)
+{
+ return odbc_load_module(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC CDR Backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/cdr/cdr_pgsql.c b/trunk/cdr/cdr_pgsql.c
new file mode 100644
index 000000000..eea4416a5
--- /dev/null
+++ b/trunk/cdr/cdr_pgsql.c
@@ -0,0 +1,335 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2003 - 2006
+ *
+ * Matthew D. Hardeman <mhardemn@papersoft.com>
+ * Adapted from the MySQL CDR logger originally by James Sharp
+ *
+ * Modified September 2003
+ * Matthew D. Hardeman <mhardemn@papersoft.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 PostgreSQL CDR logger
+ *
+ * \author Matthew D. Hardeman <mhardemn@papersoft.com>
+ * \extref PostgreSQL http://www.postgresql.org/
+ *
+ * See also
+ * \arg \ref Config_cdr
+ * \arg http://www.postgresql.org/
+ * \ingroup cdr_drivers
+ */
+
+/*** MODULEINFO
+ <depend>pgsql</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+
+#include <libpq-fe.h>
+
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+
+#define DATE_FORMAT "%Y-%m-%d %T"
+
+static char *name = "pgsql";
+static char *config = "cdr_pgsql.conf";
+static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL;
+static int connected = 0;
+
+AST_MUTEX_DEFINE_STATIC(pgsql_lock);
+
+static PGconn *conn = NULL;
+
+static int pgsql_log(struct ast_cdr *cdr)
+{
+ struct ast_tm tm;
+ char sqlcmd[2048] = "", timestr[128];
+ char *pgerror;
+ PGresult *result;
+
+ ast_mutex_lock(&pgsql_lock);
+
+ ast_localtime(&cdr->start, &tm, NULL);
+ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
+
+ if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
+ conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
+ if (PQstatus(conn) != CONNECTION_BAD) {
+ connected = 1;
+ } else {
+ pgerror = PQerrorMessage(conn);
+ ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
+ ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
+ PQfinish(conn);
+ conn = NULL;
+ }
+ }
+
+ if (connected) {
+ char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
+ char *src=NULL, *dst=NULL, *uniqueid=NULL, *userfield=NULL;
+ int pgerr;
+
+ /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */
+ if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, clid, cdr->clid, strlen(cdr->clid), &pgerr);
+ if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, dcontext, cdr->dcontext, strlen(cdr->dcontext), &pgerr);
+ if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, channel, cdr->channel, strlen(cdr->channel), &pgerr);
+ if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, dstchannel, cdr->dstchannel, strlen(cdr->dstchannel), &pgerr);
+ if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, lastapp, cdr->lastapp, strlen(cdr->lastapp), &pgerr);
+ if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, lastdata, cdr->lastdata, strlen(cdr->lastdata), &pgerr);
+ if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, uniqueid, cdr->uniqueid, strlen(cdr->uniqueid), &pgerr);
+ if ((userfield = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, userfield, cdr->userfield, strlen(cdr->userfield), &pgerr);
+ if ((src = alloca(strlen(cdr->src) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, src, cdr->src, strlen(cdr->src), &pgerr);
+ if ((dst = alloca(strlen(cdr->dst) * 2 + 1)) != NULL)
+ PQescapeStringConn(conn, dst, cdr->dst, strlen(cdr->dst), &pgerr);
+
+ /* Check for all alloca failures above at once */
+ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid) || (!userfield) || (!src) || (!dst)) {
+ ast_log(LOG_ERROR, "cdr_pgsql: Out of memory error (insert fails)\n");
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+
+ ast_debug(2, "cdr_pgsql: inserting a CDR record.\n");
+
+ snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,"
+ "lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES"
+ " ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%ld,%ld,'%s',%ld,'%s','%s','%s')",
+ table, timestr, clid, src, dst, dcontext, channel, dstchannel, lastapp, lastdata,
+ cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid, userfield);
+
+ ast_debug(3, "cdr_pgsql: SQL command executed: %s\n",sqlcmd);
+
+ /* Test to be sure we're still connected... */
+ /* If we're connected, and connection is working, good. */
+ /* Otherwise, attempt reconnect. If it fails... sorry... */
+ if (PQstatus(conn) == CONNECTION_OK) {
+ connected = 1;
+ } else {
+ ast_log(LOG_ERROR, "cdr_pgsql: Connection was lost... attempting to reconnect.\n");
+ PQreset(conn);
+ if (PQstatus(conn) == CONNECTION_OK) {
+ ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
+ connected = 1;
+ } else {
+ pgerror = PQerrorMessage(conn);
+ ast_log(LOG_ERROR, "cdr_pgsql: Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
+ ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
+ PQfinish(conn);
+ conn = NULL;
+ connected = 0;
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+ }
+ result = PQexec(conn, sqlcmd);
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ pgerror = PQresultErrorMessage(result);
+ ast_log(LOG_ERROR,"cdr_pgsql: Failed to insert call detail record into database!\n");
+ ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
+ ast_log(LOG_ERROR,"cdr_pgsql: Connection may have been lost... attempting to reconnect.\n");
+ PQreset(conn);
+ if (PQstatus(conn) == CONNECTION_OK) {
+ ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
+ connected = 1;
+ PQclear(result);
+ result = PQexec(conn, sqlcmd);
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ pgerror = PQresultErrorMessage(result);
+ ast_log(LOG_ERROR,"cdr_pgsql: HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
+ ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
+ }
+ }
+ ast_mutex_unlock(&pgsql_lock);
+ PQclear(result);
+ return -1;
+ }
+ PQclear(result);
+ }
+ ast_mutex_unlock(&pgsql_lock);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ PQfinish(conn);
+ if (pghostname)
+ ast_free(pghostname);
+ if (pgdbname)
+ ast_free(pgdbname);
+ if (pgdbuser)
+ ast_free(pgdbuser);
+ if (pgpassword)
+ ast_free(pgpassword);
+ if (pgdbport)
+ ast_free(pgdbport);
+ if (table)
+ ast_free(table);
+ ast_cdr_unregister(name);
+ return 0;
+}
+
+static int config_module(int reload)
+{
+ struct ast_variable *var;
+ char *pgerror;
+ const char *tmp;
+ struct ast_config *cfg;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load(config, config_flags)) == NULL) {
+ ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (!(var = ast_variable_browse(cfg, "global"))) {
+ ast_config_destroy(cfg);
+ return 0;
+ }
+
+ if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
+ ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
+ tmp = ""; /* connect via UNIX-socket by default */
+ }
+
+ if (pghostname)
+ ast_free(pghostname);
+ if (!(pghostname = ast_strdup(tmp))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
+ ast_log(LOG_WARNING,"PostgreSQL database not specified. Assuming asterisk\n");
+ tmp = "asteriskcdrdb";
+ }
+
+ if (pgdbname)
+ ast_free(pgdbname);
+ if (!(pgdbname = ast_strdup(tmp))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
+ ast_log(LOG_WARNING,"PostgreSQL database user not specified. Assuming asterisk\n");
+ tmp = "asterisk";
+ }
+
+ if (pgdbuser)
+ ast_free(pgdbuser);
+ if (!(pgdbuser = ast_strdup(tmp))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
+ ast_log(LOG_WARNING,"PostgreSQL database password not specified. Assuming blank\n");
+ tmp = "";
+ }
+
+ if (pgpassword)
+ ast_free(pgpassword);
+ if (!(pgpassword = ast_strdup(tmp))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) {
+ ast_log(LOG_WARNING,"PostgreSQL database port not specified. Using default 5432.\n");
+ tmp = "5432";
+ }
+
+ if (pgdbport)
+ ast_free(pgdbport);
+ if (!(pgdbport = ast_strdup(tmp))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
+ ast_log(LOG_WARNING,"CDR table not specified. Assuming cdr\n");
+ tmp = "cdr";
+ }
+
+ if (table)
+ ast_free(table);
+ if (!(table = ast_strdup(tmp))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ if (option_debug) {
+ if (ast_strlen_zero(pghostname))
+ ast_debug(1, "cdr_pgsql: using default unix socket\n");
+ else
+ ast_debug(1, "cdr_pgsql: got hostname of %s\n", pghostname);
+ ast_debug(1, "cdr_pgsql: got port of %s\n", pgdbport);
+ ast_debug(1, "cdr_pgsql: got user of %s\n", pgdbuser);
+ ast_debug(1, "cdr_pgsql: got dbname of %s\n", pgdbname);
+ ast_debug(1, "cdr_pgsql: got password of %s\n", pgpassword);
+ ast_debug(1, "cdr_pgsql: got sql table name of %s\n", table);
+ }
+
+ conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
+ if (PQstatus(conn) != CONNECTION_BAD) {
+ ast_debug(1, "Successfully connected to PostgreSQL database.\n");
+ connected = 1;
+ } else {
+ pgerror = PQerrorMessage(conn);
+ ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
+ ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
+ connected = 0;
+ }
+
+ ast_config_destroy(cfg);
+
+ return ast_cdr_register(name, ast_module_info->description, pgsql_log);
+}
+
+static int load_module(void)
+{
+ return config_module(0) ? AST_MODULE_LOAD_DECLINE : 0;
+}
+
+static int reload(void)
+{
+ return config_module(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL CDR Backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/cdr/cdr_radius.c b/trunk/cdr/cdr_radius.c
new file mode 100644
index 000000000..3f0b2c9f2
--- /dev/null
+++ b/trunk/cdr/cdr_radius.c
@@ -0,0 +1,260 @@
+/*
+ * 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 RADIUS CDR Support
+ * \author Philippe Sultan
+ * \extref The Radius Client Library - http://developer.berlios.de/projects/radiusclient-ng/
+ *
+ * \arg See also \ref AstCDR
+ * \ingroup cdr_drivers
+ */
+
+/*** MODULEINFO
+ <depend>radius</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Rev$")
+
+#include <time.h>
+#include <radiusclient-ng.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+
+/*! ISO 8601 standard format */
+#define DATE_FORMAT "%Y-%m-%d %T %z"
+
+#define VENDOR_CODE 22736
+
+enum {
+ PW_AST_ACCT_CODE = 101,
+ PW_AST_SRC = 102,
+ PW_AST_DST = 103,
+ PW_AST_DST_CTX = 104,
+ PW_AST_CLID = 105,
+ PW_AST_CHAN = 106,
+ PW_AST_DST_CHAN = 107,
+ PW_AST_LAST_APP = 108,
+ PW_AST_LAST_DATA = 109,
+ PW_AST_START_TIME = 110,
+ PW_AST_ANSWER_TIME = 111,
+ PW_AST_END_TIME = 112,
+ PW_AST_DURATION = 113,
+ PW_AST_BILL_SEC = 114,
+ PW_AST_DISPOSITION = 115,
+ PW_AST_AMA_FLAGS = 116,
+ PW_AST_UNIQUE_ID = 117,
+ PW_AST_USER_FIELD = 118
+};
+
+enum {
+ /*! Log dates and times in UTC */
+ RADIUS_FLAG_USEGMTIME = (1 << 0),
+ /*! Log Unique ID */
+ RADIUS_FLAG_LOGUNIQUEID = (1 << 1),
+ /*! Log User Field */
+ RADIUS_FLAG_LOGUSERFIELD = (1 << 2)
+};
+
+static char *desc = "RADIUS CDR Backend";
+static char *name = "radius";
+static char *cdr_config = "cdr.conf";
+
+static char radiuscfg[PATH_MAX] = "/etc/radiusclient-ng/radiusclient.conf";
+
+static struct ast_flags global_flags = { RADIUS_FLAG_USEGMTIME | RADIUS_FLAG_LOGUNIQUEID | RADIUS_FLAG_LOGUSERFIELD };
+
+static rc_handle *rh = NULL;
+
+static int build_radius_record(VALUE_PAIR **send, struct ast_cdr *cdr)
+{
+ int recordtype = PW_STATUS_STOP;
+ struct ast_tm tm;
+ char timestr[128];
+ char *tmp;
+
+ if (!rc_avpair_add(rh, send, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0))
+ return -1;
+
+ /* Account code */
+ if (!rc_avpair_add(rh, send, PW_AST_ACCT_CODE, &cdr->accountcode, strlen(cdr->accountcode), VENDOR_CODE))
+ return -1;
+
+ /* Source */
+ if (!rc_avpair_add(rh, send, PW_AST_SRC, &cdr->src, strlen(cdr->src), VENDOR_CODE))
+ return -1;
+
+ /* Destination */
+ if (!rc_avpair_add(rh, send, PW_AST_DST, &cdr->dst, strlen(cdr->dst), VENDOR_CODE))
+ return -1;
+
+ /* Destination context */
+ if (!rc_avpair_add(rh, send, PW_AST_DST_CTX, &cdr->dcontext, strlen(cdr->dcontext), VENDOR_CODE))
+ return -1;
+
+ /* Caller ID */
+ if (!rc_avpair_add(rh, send, PW_AST_CLID, &cdr->clid, strlen(cdr->clid), VENDOR_CODE))
+ return -1;
+
+ /* Channel */
+ if (!rc_avpair_add(rh, send, PW_AST_CHAN, &cdr->channel, strlen(cdr->channel), VENDOR_CODE))
+ return -1;
+
+ /* Destination Channel */
+ if (!rc_avpair_add(rh, send, PW_AST_DST_CHAN, &cdr->dstchannel, strlen(cdr->dstchannel), VENDOR_CODE))
+ return -1;
+
+ /* Last Application */
+ if (!rc_avpair_add(rh, send, PW_AST_LAST_APP, &cdr->lastapp, strlen(cdr->lastapp), VENDOR_CODE))
+ return -1;
+
+ /* Last Data */
+ if (!rc_avpair_add(rh, send, PW_AST_LAST_DATA, &cdr->lastdata, strlen(cdr->lastdata), VENDOR_CODE))
+ return -1;
+
+
+ /* Start Time */
+ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT,
+ ast_localtime(&cdr->start, &tm,
+ ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL));
+ if (!rc_avpair_add(rh, send, PW_AST_START_TIME, timestr, strlen(timestr), VENDOR_CODE))
+ return -1;
+
+ /* Answer Time */
+ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT,
+ ast_localtime(&cdr->answer, &tm,
+ ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL));
+ if (!rc_avpair_add(rh, send, PW_AST_ANSWER_TIME, timestr, strlen(timestr), VENDOR_CODE))
+ return -1;
+
+ /* End Time */
+ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT,
+ ast_localtime(&cdr->end, &tm,
+ ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL));
+ if (!rc_avpair_add(rh, send, PW_AST_END_TIME, timestr, strlen(timestr), VENDOR_CODE))
+ return -1;
+
+ /* Duration */
+ if (!rc_avpair_add(rh, send, PW_AST_DURATION, &cdr->duration, 0, VENDOR_CODE))
+ return -1;
+
+ /* Billable seconds */
+ if (!rc_avpair_add(rh, send, PW_AST_BILL_SEC, &cdr->billsec, 0, VENDOR_CODE))
+ return -1;
+
+ /* Disposition */
+ tmp = ast_cdr_disp2str(cdr->disposition);
+ if (!rc_avpair_add(rh, send, PW_AST_DISPOSITION, tmp, strlen(tmp), VENDOR_CODE))
+ return -1;
+
+ /* AMA Flags */
+ tmp = ast_cdr_flags2str(cdr->amaflags);
+ if (!rc_avpair_add(rh, send, PW_AST_AMA_FLAGS, tmp, strlen(tmp), VENDOR_CODE))
+ return -1;
+
+ if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUNIQUEID)) {
+ /* Unique ID */
+ if (!rc_avpair_add(rh, send, PW_AST_UNIQUE_ID, &cdr->uniqueid, strlen(cdr->uniqueid), VENDOR_CODE))
+ return -1;
+ }
+
+ if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUSERFIELD)) {
+ /* append the user field */
+ if (!rc_avpair_add(rh, send, PW_AST_USER_FIELD, &cdr->userfield, strlen(cdr->userfield), VENDOR_CODE))
+ return -1;
+ }
+
+ /* Setting Acct-Session-Id & User-Name attributes for proper generation
+ of Acct-Unique-Session-Id on server side */
+ /* Channel */
+ if (!rc_avpair_add(rh, send, PW_USER_NAME, &cdr->channel, strlen(cdr->channel), 0))
+ return -1;
+
+ /* Unique ID */
+ if (!rc_avpair_add(rh, send, PW_ACCT_SESSION_ID, &cdr->uniqueid, strlen(cdr->uniqueid), 0))
+ return -1;
+
+ return 0;
+}
+
+static int radius_log(struct ast_cdr *cdr)
+{
+ int result = ERROR_RC;
+ VALUE_PAIR *send = NULL;
+
+ if (build_radius_record(&send, cdr)) {
+ ast_debug(1, "Unable to create RADIUS record. CDR not recorded!\n");
+ return result;
+ }
+
+ result = rc_acct(rh, 0, send);
+ if (result != OK_RC)
+ ast_log(LOG_ERROR, "Failed to record Radius CDR record!\n");
+
+ return result;
+}
+
+static int unload_module(void)
+{
+ ast_cdr_unregister(name);
+ return 0;
+}
+
+static int load_module(void)
+{
+ struct ast_config *cfg;
+ struct ast_flags config_flags = { 0 };
+ int res;
+ const char *tmp;
+
+ if ((cfg = ast_config_load(cdr_config, config_flags))) {
+ ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(cfg, "radius", "usegmtime")), RADIUS_FLAG_USEGMTIME);
+ ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(cfg, "radius", "loguniqueid")), RADIUS_FLAG_LOGUNIQUEID);
+ ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(cfg, "radius", "loguserfield")), RADIUS_FLAG_LOGUSERFIELD);
+ if ((tmp = ast_variable_retrieve(cfg, "radius", "radiuscfg")))
+ ast_copy_string(radiuscfg, tmp, sizeof(radiuscfg));
+ ast_config_destroy(cfg);
+ } else
+ return AST_MODULE_LOAD_DECLINE;
+
+ /* start logging */
+ rc_openlog("asterisk");
+
+ /* read radiusclient-ng config file */
+ if (!(rh = rc_read_config(radiuscfg))) {
+ ast_log(LOG_NOTICE, "Cannot load radiusclient-ng configuration file %s.\n", radiuscfg);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ /* read radiusclient-ng dictionaries */
+ if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"))) {
+ ast_log(LOG_NOTICE, "Cannot load radiusclient-ng dictionary file.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ res = ast_cdr_register(name, desc, radius_log);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "RADIUS CDR Backend");
diff --git a/trunk/cdr/cdr_sqlite.c b/trunk/cdr/cdr_sqlite.c
new file mode 100644
index 000000000..601e5b245
--- /dev/null
+++ b/trunk/cdr/cdr_sqlite.c
@@ -0,0 +1,214 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005, Holger Schurig
+ *
+ *
+ * Ideas taken from other cdr_*.c files
+ *
+ * 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 Store CDR records in a SQLite database.
+ *
+ * \author Holger Schurig <hs4233@mail.mn-solutions.de>
+ * \extref SQLite http://www.sqlite.org/
+ *
+ * See also
+ * \arg \ref Config_cdr
+ * \arg http://www.sqlite.org/
+ *
+ * Creates the database and table on-the-fly
+ * \ingroup cdr_drivers
+ *
+ * \note This module has been marked deprecated in favor for cdr_sqlite3_custom
+ */
+
+/*** MODULEINFO
+ <depend>sqlite</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sqlite.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+#include "asterisk/paths.h"
+
+#define LOG_UNIQUEID 0
+#define LOG_USERFIELD 0
+
+/* When you change the DATE_FORMAT, be sure to change the CHAR(19) below to something else */
+#define DATE_FORMAT "%Y-%m-%d %T"
+
+static char *name = "sqlite";
+static sqlite* db = NULL;
+
+AST_MUTEX_DEFINE_STATIC(sqlite_lock);
+
+/*! \brief SQL table format */
+static char sql_create_table[] = "CREATE TABLE cdr ("
+" AcctId INTEGER PRIMARY KEY,"
+" clid VARCHAR(80),"
+" src VARCHAR(80),"
+" dst VARCHAR(80),"
+" dcontext VARCHAR(80),"
+" channel VARCHAR(80),"
+" dstchannel VARCHAR(80),"
+" lastapp VARCHAR(80),"
+" lastdata VARCHAR(80),"
+" start CHAR(19),"
+" answer CHAR(19),"
+" end CHAR(19),"
+" duration INTEGER,"
+" billsec INTEGER,"
+" disposition INTEGER,"
+" amaflags INTEGER,"
+" accountcode VARCHAR(20)"
+#if LOG_UNIQUEID
+" ,uniqueid VARCHAR(32)"
+#endif
+#if LOG_USERFIELD
+" ,userfield VARCHAR(255)"
+#endif
+");";
+
+static int sqlite_log(struct ast_cdr *cdr)
+{
+ int res = 0;
+ char *zErr = 0;
+ struct ast_tm tm;
+ char startstr[80], answerstr[80], endstr[80];
+ int count;
+
+ ast_mutex_lock(&sqlite_lock);
+
+ ast_localtime(&cdr->start, &tm, NULL);
+ ast_strftime(startstr, sizeof(startstr), DATE_FORMAT, &tm);
+
+ ast_localtime(&cdr->answer, &tm, NULL);
+ ast_strftime(answerstr, sizeof(answerstr), DATE_FORMAT, &tm);
+
+ ast_localtime(&cdr->end, &tm, NULL);
+ ast_strftime(endstr, sizeof(endstr), DATE_FORMAT, &tm);
+
+ for(count=0; count<5; count++) {
+ res = sqlite_exec_printf(db,
+ "INSERT INTO cdr ("
+ "clid,src,dst,dcontext,"
+ "channel,dstchannel,lastapp,lastdata, "
+ "start,answer,end,"
+ "duration,billsec,disposition,amaflags, "
+ "accountcode"
+# if LOG_UNIQUEID
+ ",uniqueid"
+# endif
+# if LOG_USERFIELD
+ ",userfield"
+# endif
+ ") VALUES ("
+ "'%q', '%q', '%q', '%q', "
+ "'%q', '%q', '%q', '%q', "
+ "'%q', '%q', '%q', "
+ "%d, %d, %d, %d, "
+ "'%q'"
+# if LOG_UNIQUEID
+ ",'%q'"
+# endif
+# if LOG_USERFIELD
+ ",'%q'"
+# endif
+ ")", NULL, NULL, &zErr,
+ cdr->clid, cdr->src, cdr->dst, cdr->dcontext,
+ cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata,
+ startstr, answerstr, endstr,
+ cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags,
+ cdr->accountcode
+# if LOG_UNIQUEID
+ ,cdr->uniqueid
+# endif
+# if LOG_USERFIELD
+ ,cdr->userfield
+# endif
+ );
+ if (res != SQLITE_BUSY && res != SQLITE_LOCKED)
+ break;
+ usleep(200);
+ }
+
+ if (zErr) {
+ ast_log(LOG_ERROR, "cdr_sqlite: %s\n", zErr);
+ ast_free(zErr);
+ }
+
+ ast_mutex_unlock(&sqlite_lock);
+ return res;
+}
+
+static int unload_module(void)
+{
+ if (db)
+ sqlite_close(db);
+ ast_cdr_unregister(name);
+ return 0;
+}
+
+static int load_module(void)
+{
+ char *zErr;
+ char fn[PATH_MAX];
+ int res;
+
+ ast_log(LOG_WARNING, "This module has been marked deprecated in favor of "
+ "using cdr_sqlite3_custom. (May be removed after Asterisk 1.6)\n");
+
+ /* is the database there? */
+ snprintf(fn, sizeof(fn), "%s/cdr.db", ast_config_AST_LOG_DIR);
+ db = sqlite_open(fn, AST_FILE_MODE, &zErr);
+ if (!db) {
+ ast_log(LOG_ERROR, "cdr_sqlite: %s\n", zErr);
+ ast_free(zErr);
+ return -1;
+ }
+
+ /* is the table there? */
+ res = sqlite_exec(db, "SELECT COUNT(AcctId) FROM cdr;", NULL, NULL, NULL);
+ if (res) {
+ res = sqlite_exec(db, sql_create_table, NULL, NULL, &zErr);
+ if (res) {
+ ast_log(LOG_ERROR, "cdr_sqlite: Unable to create table 'cdr': %s\n", zErr);
+ ast_free(zErr);
+ goto err;
+ }
+
+ /* TODO: here we should probably create an index */
+ }
+
+ res = ast_cdr_register(name, ast_module_info->description, sqlite_log);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to register SQLite CDR handling\n");
+ return -1;
+ }
+ return 0;
+
+err:
+ if (db)
+ sqlite_close(db);
+ return -1;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SQLite CDR Backend");
diff --git a/trunk/cdr/cdr_sqlite3_custom.c b/trunk/cdr/cdr_sqlite3_custom.c
new file mode 100644
index 000000000..87718db59
--- /dev/null
+++ b/trunk/cdr/cdr_sqlite3_custom.c
@@ -0,0 +1,357 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com> and others.
+ *
+ * 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 Custom SQLite3 CDR records.
+ *
+ * \author Adapted by Alejandro Rios <alejandro.rios@avatar.com.co> and
+ * Russell Bryant <russell@digium.com> from
+ * cdr_mysql_custom by Edward Eastman <ed@dm3.co.uk>,
+ * and cdr_sqlite by Holger Schurig <hs4233@mail.mn-solutions.de>
+ *
+ *
+ * \arg See also \ref AstCDR
+ *
+ *
+ * \ingroup cdr_drivers
+ */
+
+/*** MODULEINFO
+ <depend>sqlite3</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+#include <sqlite3.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+
+AST_MUTEX_DEFINE_STATIC(lock);
+
+static const char config_file[] = "cdr_sqlite3_custom.conf";
+
+static char *desc = "Customizable SQLite3 CDR Backend";
+static char *name = "cdr_sqlite3_custom";
+static sqlite3 *db = NULL;
+
+static char table[80];
+static char *columns;
+
+struct values {
+ char *expression;
+ AST_LIST_ENTRY(values) list;
+};
+
+static AST_LIST_HEAD_STATIC(sql_values, values);
+
+static int free_config(void);
+
+static int load_column_config(const char *tmp)
+{
+ char *col = NULL;
+ char *cols = NULL;
+ char *escaped = NULL;
+ struct ast_str *column_string = NULL;
+
+ if (ast_strlen_zero(tmp)) {
+ ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n");
+ return -1;
+ }
+ if (!(column_string = ast_str_create(1024))) {
+ ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
+ return -1;
+ }
+ if (!(cols = ast_strdup(tmp))) {
+ ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
+ ast_free(column_string);
+ return -1;
+ }
+ while ((col = strsep(&cols, ","))) {
+ col = ast_strip(col);
+ escaped = sqlite3_mprintf("%q", col);
+ if (!escaped) {
+ ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table);
+ ast_free(column_string);
+ ast_free(cols);
+ return -1;
+ }
+ if (!column_string->used)
+ ast_str_set(&column_string, 0, escaped);
+ else
+ ast_str_append(&column_string, 0, ",%s", escaped);
+ sqlite3_free(escaped);
+ }
+ if (!(columns = ast_strdup(column_string->str))) {
+ ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table);
+ ast_free(column_string);
+ ast_free(cols);
+ return -1;
+ }
+ ast_free(column_string);
+ ast_free(cols);
+
+ return 0;
+}
+
+static int load_values_config(const char *tmp)
+{
+ char *val = NULL;
+ char *vals = NULL;
+ struct values *value = NULL;
+
+ if (ast_strlen_zero(tmp)) {
+ ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n");
+ return -1;
+ }
+ if (!(vals = ast_strdup(tmp))) {
+ ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp);
+ return -1;
+ }
+ while ((val = strsep(&vals, ","))) {
+ /* Strip the single quotes off if they are there */
+ val = ast_strip_quoted(val, "'", "'");
+ value = ast_calloc(sizeof(char), sizeof(*value) + strlen(val) + 1);
+ if (!value) {
+ ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", val);
+ ast_free(vals);
+ return -1;
+ }
+ value->expression = (char *) value + sizeof(*value);
+ ast_copy_string(value->expression, val, strlen(val) + 1);
+ AST_LIST_INSERT_TAIL(&sql_values, value, list);
+ }
+ ast_free(vals);
+
+ return 0;
+}
+
+static int load_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_variable *mappingvar;
+ const char *tmp;
+
+ if (!(cfg = ast_config_load(config_file, config_flags))) {
+ if (reload)
+ ast_log(LOG_WARNING, "Failed to reload configuration file.\n");
+ else
+ ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (reload)
+ free_config();
+
+ ast_mutex_lock(&lock);
+
+ if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
+ /* Nothing configured */
+ ast_mutex_unlock(&lock);
+ ast_config_destroy(cfg);
+ return 0;
+ }
+
+ /* Mapping must have a table name */
+ tmp = ast_variable_retrieve(cfg, "master", "table");
+ if (!ast_strlen_zero(tmp))
+ ast_copy_string(table, tmp, sizeof(table));
+ else {
+ ast_log(LOG_WARNING, "Table name not specified. Assuming cdr.\n");
+ strcpy(table, "cdr");
+ }
+
+ /* Columns */
+ tmp = ast_variable_retrieve(cfg, "master", "columns");
+ if (load_column_config(tmp)) {
+ ast_mutex_unlock(&lock);
+ ast_config_destroy(cfg);
+ free_config();
+ return -1;
+ }
+
+ /* Values */
+ tmp = ast_variable_retrieve(cfg, "master", "values");
+ if (load_values_config(tmp)) {
+ ast_mutex_unlock(&lock);
+ ast_config_destroy(cfg);
+ free_config();
+ return -1;
+ }
+
+ ast_verb(3, "cdr_sqlite3_custom: Logging CDR records to table '%s' in 'master.db'\n", table);
+
+ ast_mutex_unlock(&lock);
+ ast_config_destroy(cfg);
+
+ return 0;
+}
+
+static int free_config(void)
+{
+ struct values *value;
+
+ ast_mutex_lock(&lock);
+
+ if (db) {
+ sqlite3_close(db);
+ db = NULL;
+ }
+
+ if (columns) {
+ ast_free(columns);
+ columns = NULL;
+ }
+
+ while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list)))
+ ast_free(value);
+
+ ast_mutex_unlock(&lock);
+
+ return 0;
+}
+
+static int sqlite3_log(struct ast_cdr *cdr)
+{
+ int res = 0;
+ char *error = NULL;
+ char *sql = NULL;
+ struct ast_channel dummy = { 0, };
+ int count = 0;
+
+ { /* Make it obvious that only sql should be used outside of this block */
+ char *escaped;
+ char subst_buf[2048];
+ struct values *value;
+ struct ast_str *value_string = ast_str_create(1024);
+ dummy.cdr = cdr;
+ AST_LIST_TRAVERSE(&sql_values, value, list) {
+ memset(subst_buf, 0, sizeof(subst_buf));
+ pbx_substitute_variables_helper(&dummy, value->expression, subst_buf, sizeof(subst_buf) - 1);
+ escaped = sqlite3_mprintf("%q", subst_buf);
+ if (!value_string->used)
+ ast_str_append(&value_string, 0, "'%s'", escaped);
+ else
+ ast_str_append(&value_string, 0, ",'%s'", escaped);
+ sqlite3_free(escaped);
+ }
+ sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, value_string->str);
+ ast_debug(1, "About to log: %s\n", sql);
+ ast_free(value_string);
+ }
+
+ ast_mutex_lock(&lock);
+
+ /* XXX This seems awful arbitrary... */
+ for (count = 0; count < 5; count++) {
+ res = sqlite3_exec(db, sql, NULL, NULL, &error);
+ if (res != SQLITE_BUSY && res != SQLITE_LOCKED)
+ break;
+ usleep(200);
+ }
+
+ if (error) {
+ ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql);
+ sqlite3_free(error);
+ }
+
+ if (sql)
+ sqlite3_free(sql);
+
+ ast_mutex_unlock(&lock);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ free_config();
+
+ ast_cdr_unregister(name);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ char *error;
+ char filename[PATH_MAX];
+ int res;
+ char *sql;
+
+ if (!load_config(0)) {
+ res = ast_cdr_register(name, desc, sqlite3_log);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to register custom SQLite3 CDR handling\n");
+ free_config();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ } else
+ return AST_MODULE_LOAD_DECLINE;
+
+ /* is the database there? */
+ snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR);
+ res = sqlite3_open(filename, &db);
+ if (res != SQLITE_OK) {
+ ast_log(LOG_ERROR, "Could not open database %s.\n", filename);
+ free_config();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ /* is the table there? */
+ sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
+ res = sqlite3_exec(db, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ /* We don't use %q for the column list here since we already escaped when building it */
+ sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns);
+ res = sqlite3_exec(db, sql, NULL, NULL, &error);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error);
+ sqlite3_free(error);
+ free_config();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ }
+
+ return 0;
+}
+
+static int reload(void)
+{
+ return load_config(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SQLite3 Custom CDR Module",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/trunk/cdr/cdr_tds.c b/trunk/cdr/cdr_tds.c
new file mode 100644
index 000000000..9d322fd27
--- /dev/null
+++ b/trunk/cdr/cdr_tds.c
@@ -0,0 +1,531 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2006, Digium, Inc.
+ *
+ * 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 FreeTDS CDR logger
+ *
+ * See also
+ * \arg \ref Config_cdr
+ * \arg http://www.freetds.org/
+ * \ingroup cdr_drivers
+ */
+
+/*! \verbatim
+ *
+ * Table Structure for `cdr`
+ *
+ * Created on: 05/20/2004 16:16
+ * Last changed on: 07/27/2004 20:01
+
+CREATE TABLE [dbo].[cdr] (
+ [accountcode] [varchar] (20) NULL ,
+ [src] [varchar] (80) NULL ,
+ [dst] [varchar] (80) NULL ,
+ [dcontext] [varchar] (80) NULL ,
+ [clid] [varchar] (80) NULL ,
+ [channel] [varchar] (80) NULL ,
+ [dstchannel] [varchar] (80) NULL ,
+ [lastapp] [varchar] (80) NULL ,
+ [lastdata] [varchar] (80) NULL ,
+ [start] [datetime] NULL ,
+ [answer] [datetime] NULL ,
+ [end] [datetime] NULL ,
+ [duration] [int] NULL ,
+ [billsec] [int] NULL ,
+ [disposition] [varchar] (20) NULL ,
+ [amaflags] [varchar] (16) NULL ,
+ [uniqueid] [varchar] (32) NULL
+) ON [PRIMARY]
+
+\endverbatim
+
+*/
+
+/*** MODULEINFO
+ <depend>freetds</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+#include <math.h>
+
+#include <tds.h>
+#include <tdsconvert.h>
+#include <ctype.h>
+
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/module.h"
+
+#ifdef FREETDS_PRE_0_62
+#warning "You have older TDS, you should upgrade!"
+#endif
+
+#define DATE_FORMAT "%Y/%m/%d %T"
+
+static char *name = "mssql";
+static char *config = "cdr_tds.conf";
+
+static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *charset = NULL, *language = NULL;
+static char *table = NULL;
+
+static int connected = 0;
+
+AST_MUTEX_DEFINE_STATIC(tds_lock);
+
+static TDSSOCKET *tds;
+static TDSLOGIN *login;
+static TDSCONTEXT *context;
+
+static char *anti_injection(const char *, int);
+static void get_date(char *, struct timeval);
+
+static int mssql_connect(void);
+static int mssql_disconnect(void);
+
+static int tds_log(struct ast_cdr *cdr)
+{
+ char sqlcmd[2048], start[80], answer[80], end[80];
+ char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid;
+ int res = 0;
+ int retried = 0;
+#ifdef FREETDS_PRE_0_62
+ TDS_INT result_type;
+#endif
+
+ ast_mutex_lock(&tds_lock);
+
+ memset(sqlcmd, 0, 2048);
+
+ accountcode = anti_injection(cdr->accountcode, 20);
+ src = anti_injection(cdr->src, 80);
+ dst = anti_injection(cdr->dst, 80);
+ dcontext = anti_injection(cdr->dcontext, 80);
+ clid = anti_injection(cdr->clid, 80);
+ channel = anti_injection(cdr->channel, 80);
+ dstchannel = anti_injection(cdr->dstchannel, 80);
+ lastapp = anti_injection(cdr->lastapp, 80);
+ lastdata = anti_injection(cdr->lastdata, 80);
+ uniqueid = anti_injection(cdr->uniqueid, 32);
+
+ get_date(start, cdr->start);
+ get_date(answer, cdr->answer);
+ get_date(end, cdr->end);
+
+ sprintf(
+ sqlcmd,
+ "INSERT INTO %s "
+ "("
+ "accountcode, "
+ "src, "
+ "dst, "
+ "dcontext, "
+ "clid, "
+ "channel, "
+ "dstchannel, "
+ "lastapp, "
+ "lastdata, "
+ "start, "
+ "answer, "
+ "[end], "
+ "duration, "
+ "billsec, "
+ "disposition, "
+ "amaflags, "
+ "uniqueid"
+ ") "
+ "VALUES "
+ "("
+ "'%s', " /* accountcode */
+ "'%s', " /* src */
+ "'%s', " /* dst */
+ "'%s', " /* dcontext */
+ "'%s', " /* clid */
+ "'%s', " /* channel */
+ "'%s', " /* dstchannel */
+ "'%s', " /* lastapp */
+ "'%s', " /* lastdata */
+ "%s, " /* start */
+ "%s, " /* answer */
+ "%s, " /* end */
+ "%ld, " /* duration */
+ "%ld, " /* billsec */
+ "'%s', " /* disposition */
+ "'%s', " /* amaflags */
+ "'%s'" /* uniqueid */
+ ")",
+ table,
+ accountcode,
+ src,
+ dst,
+ dcontext,
+ clid,
+ channel,
+ dstchannel,
+ lastapp,
+ lastdata,
+ start,
+ answer,
+ end,
+ cdr->duration,
+ cdr->billsec,
+ ast_cdr_disp2str(cdr->disposition),
+ ast_cdr_flags2str(cdr->amaflags),
+ uniqueid
+ );
+
+ do {
+ if (!connected) {
+ if (mssql_connect())
+ ast_log(LOG_ERROR, "Failed to reconnect to SQL database.\n");
+ else
+ ast_log(LOG_WARNING, "Reconnected to SQL database.\n");
+
+ retried = 1; /* note that we have now tried */
+ }
+
+#ifdef FREETDS_PRE_0_62
+ if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
+#else
+ if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
+#endif
+ {
+ ast_log(LOG_ERROR, "Failed to insert Call Data Record into SQL database.\n");
+
+ mssql_disconnect(); /* this is ok even if we are already disconnected */
+ }
+ } while (!connected && !retried);
+
+ ast_free(accountcode);
+ ast_free(src);
+ ast_free(dst);
+ ast_free(dcontext);
+ ast_free(clid);
+ ast_free(channel);
+ ast_free(dstchannel);
+ ast_free(lastapp);
+ ast_free(lastdata);
+ ast_free(uniqueid);
+
+ ast_mutex_unlock(&tds_lock);
+
+ return res;
+}
+
+static char *anti_injection(const char *str, int len)
+{
+ /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */
+
+ char *buf;
+ char *buf_ptr, *srh_ptr;
+ char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
+ int idx;
+
+ if ((buf = ast_malloc(len + 1)) == NULL)
+ {
+ ast_log(LOG_ERROR, "cdr_tds: Out of memory error\n");
+ return NULL;
+ }
+ memset(buf, 0, len);
+
+ buf_ptr = buf;
+
+ /* Escape single quotes */
+ for (; *str && strlen(buf) < len; str++)
+ {
+ if (*str == '\'')
+ *buf_ptr++ = '\'';
+ *buf_ptr++ = *str;
+ }
+ *buf_ptr = '\0';
+
+ /* Erase known bad input */
+ for (idx=0; *known_bad[idx]; idx++)
+ {
+ while((srh_ptr = strcasestr(buf, known_bad[idx])))
+ {
+ memmove(srh_ptr, srh_ptr+strlen(known_bad[idx]), strlen(srh_ptr+strlen(known_bad[idx]))+1);
+ }
+ }
+
+ return buf;
+}
+
+static void get_date(char *dateField, struct timeval tv)
+{
+ struct ast_tm tm;
+ char buf[80];
+
+ /* To make sure we have date variable if not insert null to SQL */
+ if (!ast_tvzero(tv))
+ {
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(buf, 80, DATE_FORMAT, &tm);
+ sprintf(dateField, "'%s'", buf);
+ }
+ else
+ {
+ strcpy(dateField, "null");
+ }
+}
+
+static int mssql_disconnect(void)
+{
+ if (tds) {
+ tds_free_socket(tds);
+ tds = NULL;
+ }
+
+ if (context) {
+ tds_free_context(context);
+ context = NULL;
+ }
+
+ if (login) {
+ tds_free_login(login);
+ login = NULL;
+ }
+
+ connected = 0;
+
+ return 0;
+}
+
+static int mssql_connect(void)
+{
+#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
+ TDSCONNECTION *connection = NULL;
+#else
+ TDSCONNECTINFO *connection = NULL;
+#endif
+ char query[128];
+
+ /* Connect to M$SQL Server */
+ if (!(login = tds_alloc_login()))
+ {
+ ast_log(LOG_ERROR, "tds_alloc_login() failed.\n");
+ return -1;
+ }
+
+ tds_set_server(login, hostname);
+ tds_set_user(login, dbuser);
+ tds_set_passwd(login, password);
+ tds_set_app(login, "TSQL");
+ tds_set_library(login, "TDS-Library");
+#ifndef FREETDS_PRE_0_62
+ tds_set_client_charset(login, charset);
+#endif
+ tds_set_language(login, language);
+ tds_set_packet(login, 512);
+ tds_set_version(login, 7, 0);
+
+#ifdef FREETDS_0_64
+ if (!(context = tds_alloc_context(NULL)))
+#else
+ if (!(context = tds_alloc_context()))
+#endif
+ {
+ ast_log(LOG_ERROR, "tds_alloc_context() failed.\n");
+ goto connect_fail;
+ }
+
+ if (!(tds = tds_alloc_socket(context, 512))) {
+ ast_log(LOG_ERROR, "tds_alloc_socket() failed.\n");
+ goto connect_fail;
+ }
+
+ tds_set_parent(tds, NULL);
+ connection = tds_read_config_info(tds, login, context->locale);
+ if (!connection)
+ {
+ ast_log(LOG_ERROR, "tds_read_config() failed.\n");
+ goto connect_fail;
+ }
+
+ if (tds_connect(tds, connection) == TDS_FAIL)
+ {
+ ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n");
+ tds = NULL; /* freed by tds_connect() on error */
+#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
+ tds_free_connection(connection);
+#else
+ tds_free_connect(connection);
+#endif
+ connection = NULL;
+ goto connect_fail;
+ }
+#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
+ tds_free_connection(connection);
+#else
+ tds_free_connect(connection);
+#endif
+ connection = NULL;
+
+ sprintf(query, "USE %s", dbname);
+#ifdef FREETDS_PRE_0_62
+ if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
+#else
+ if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
+#endif
+ {
+ ast_log(LOG_ERROR, "Could not change database (%s)\n", dbname);
+ goto connect_fail;
+ }
+
+ connected = 1;
+ return 0;
+
+connect_fail:
+ mssql_disconnect();
+ return -1;
+}
+
+static int tds_unload_module(void)
+{
+ mssql_disconnect();
+
+ ast_cdr_unregister(name);
+
+ if (hostname) ast_free(hostname);
+ if (dbname) ast_free(dbname);
+ if (dbuser) ast_free(dbuser);
+ if (password) ast_free(password);
+ if (charset) ast_free(charset);
+ if (language) ast_free(language);
+ if (table) ast_free(table);
+
+ return 0;
+}
+
+static int tds_load_module(int reload)
+{
+ int res = 0;
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ const char *ptr = NULL;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+#ifdef FREETDS_PRE_0_62
+ TDS_INT result_type;
+#endif
+
+ cfg = ast_config_load(config, config_flags);
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config for MSSQL CDR's: %s\n", config);
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ var = ast_variable_browse(cfg, "global");
+ if (!var) /* nothing configured */ {
+ ast_config_destroy(cfg);
+ return 0;
+ }
+
+ ptr = ast_variable_retrieve(cfg, "global", "hostname");
+ if (ptr) {
+ if (hostname)
+ ast_free(hostname);
+ hostname = ast_strdup(ptr);
+ } else
+ ast_log(LOG_ERROR, "Database server hostname not specified.\n");
+
+ ptr = ast_variable_retrieve(cfg, "global", "dbname");
+ if (ptr) {
+ if (dbname)
+ ast_free(dbname);
+ dbname = ast_strdup(ptr);
+ } else
+ ast_log(LOG_ERROR, "Database dbname not specified.\n");
+
+ ptr = ast_variable_retrieve(cfg, "global", "user");
+ if (ptr) {
+ if (dbuser)
+ ast_free(dbuser);
+ dbuser = ast_strdup(ptr);
+ } else
+ ast_log(LOG_ERROR, "Database dbuser not specified.\n");
+
+ ptr = ast_variable_retrieve(cfg, "global", "password");
+ if (ptr) {
+ if (password)
+ ast_free(password);
+ password = ast_strdup(ptr);
+ } else
+ ast_log(LOG_ERROR,"Database password not specified.\n");
+
+ ptr = ast_variable_retrieve(cfg, "global", "charset");
+ if (charset)
+ ast_free(charset);
+ if (ptr)
+ charset = ast_strdup(ptr);
+ else
+ charset = ast_strdup("iso_1");
+
+ if (language)
+ ast_free(language);
+ ptr = ast_variable_retrieve(cfg, "global", "language");
+ if (ptr)
+ language = ast_strdup(ptr);
+ else
+ language = ast_strdup("us_english");
+
+ ptr = ast_variable_retrieve(cfg, "global", "table");
+ if (ptr == NULL) {
+ ast_debug(1, "cdr_tds: table not specified. Assuming cdr\n");
+ ptr = "cdr";
+ }
+ if (table)
+ ast_free(table);
+ table = ast_strdup(ptr);
+
+ ast_config_destroy(cfg);
+
+ ast_mutex_lock(&tds_lock);
+ mssql_disconnect();
+ mssql_connect();
+ ast_mutex_unlock(&tds_lock);
+
+ return res;
+}
+
+static int reload(void)
+{
+ return tds_load_module(1);
+}
+
+static int load_module(void)
+{
+ if (!tds_load_module(0))
+ return AST_MODULE_LOAD_DECLINE;
+ ast_cdr_register(name, ast_module_info->description, tds_log);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return tds_unload_module();
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MSSQL CDR Backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/channels/DialTone.h b/trunk/channels/DialTone.h
new file mode 100644
index 000000000..e71ba0c7d
--- /dev/null
+++ b/trunk/channels/DialTone.h
@@ -0,0 +1,257 @@
+/*
+ * 8-bit raw data
+ *
+ * Source: DialTone.ulaw
+ *
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+/*! \file
+ * \brief
+ * 8-bit raw data
+ */
+
+static unsigned char DialTone[] = {
+0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa,
+0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c,
+0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3,
+0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47,
+0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49,
+0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf,
+0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24,
+0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e,
+0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b,
+0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c,
+0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f,
+0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9,
+0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41,
+0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b,
+0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5,
+0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b,
+0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96,
+0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14,
+0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95,
+0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a,
+0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5,
+0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43,
+0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32,
+0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e,
+0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16,
+0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91,
+0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10,
+0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92,
+0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19,
+0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4,
+0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a,
+0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d,
+0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c,
+0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15,
+0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90,
+0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10,
+0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93,
+0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a,
+0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8,
+0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a,
+0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c,
+0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d,
+0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16,
+0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93,
+0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13,
+0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97,
+0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e,
+0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad,
+0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9,
+0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e,
+0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0,
+0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b,
+0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a,
+0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b,
+0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e,
+0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28,
+0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9,
+0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0,
+0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36,
+0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab,
+0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27,
+0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6,
+0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29,
+0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae,
+0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a,
+0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd,
+0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7,
+0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c,
+0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7,
+0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a,
+0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5,
+0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78,
+0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d,
+0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6,
+0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e,
+0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64,
+0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7,
+0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39,
+0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf,
+0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b,
+0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa,
+0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c,
+0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3,
+0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47,
+0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49,
+0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf,
+0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24,
+0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e,
+0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b,
+0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c,
+0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f,
+0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9,
+0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41,
+0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b,
+0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5,
+0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b,
+0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96,
+0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14,
+0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95,
+0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a,
+0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5,
+0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43,
+0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32,
+0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e,
+0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16,
+0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91,
+0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10,
+0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92,
+0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19,
+0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4,
+0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a,
+0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d,
+0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c,
+0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15,
+0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90,
+0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10,
+0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93,
+0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a,
+0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8,
+0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a,
+0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c,
+0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d,
+0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16,
+0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93,
+0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13,
+0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97,
+0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e,
+0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad,
+0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9,
+0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e,
+0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0,
+0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b,
+0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a,
+0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b,
+0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e,
+0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28,
+0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9,
+0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0,
+0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36,
+0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab,
+0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27,
+0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6,
+0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29,
+0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae,
+0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a,
+0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd,
+0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7,
+0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c,
+0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7,
+0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a,
+0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5,
+0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78,
+0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d,
+0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6,
+0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e,
+0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64,
+0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7,
+0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39,
+0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf,
+0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b,
+0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa,
+0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c,
+0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3,
+0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47,
+0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49,
+0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf,
+0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24,
+0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e,
+0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b,
+0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c,
+0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f,
+0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9,
+0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41,
+0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b,
+0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5,
+0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b,
+0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96,
+0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14,
+0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95,
+0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a,
+0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5,
+0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43,
+0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32,
+0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e,
+0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16,
+0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91,
+0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10,
+0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92,
+0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19,
+0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4,
+0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a,
+0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d,
+0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c,
+0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15,
+0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90,
+0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10,
+0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93,
+0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a,
+0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8,
+0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a,
+0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c,
+0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d,
+0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16,
+0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93,
+0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13,
+0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97,
+0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e,
+0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad,
+0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9,
+0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e,
+0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0,
+0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b,
+0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a,
+0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b,
+0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e,
+0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28,
+0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9,
+0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0,
+0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36,
+0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab,
+0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27,
+0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6,
+0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29,
+0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae,
+0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a,
+0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd,
+0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7,
+0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c,
+0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7,
+0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a,
+0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5,
+0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78,
+0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d,
+0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6,
+0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e,
+0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64,
+0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7,
+0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39,
+0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf,
+0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b };
diff --git a/trunk/channels/Makefile b/trunk/channels/Makefile
new file mode 100644
index 000000000..467fc8031
--- /dev/null
+++ b/trunk/channels/Makefile
@@ -0,0 +1,105 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for channel drivers
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=chan
+MENUSELECT_CATEGORY=CHANNELS
+MENUSELECT_DESCRIPTION=Channel Drivers
+
+ifeq ($(OSARCH),OpenBSD)
+ PTLIB=-lpt_OpenBSD_x86_r
+ H323LIB=-lh323_OpenBSD_x86_r
+endif
+
+ifeq ($(OSARCH),linux-gnu)
+ PTLIB=-lpt_linux_x86_r
+ H323LIB=-lh323_linux_x86_r
+ CHANH323LIB=-ldl
+endif
+
+ifeq ($(OSARCH),FreeBSD)
+ PTLIB=-lpt_FreeBSD_x86_r
+ H323LIB=-lh323_FreeBSD_x86_r
+ CHANH323LIB=-pthread
+endif
+
+ifeq ($(OSARCH),NetBSD)
+ PTLIB=-lpt_NetBSD_x86_r
+ H323LIB=-lh323_NetBSD_x86_r
+endif
+
+ifeq ($(wildcard h323/libchanh323.a),)
+ MODULE_EXCLUDE += chan_h323
+endif
+
+ifndef OPENH323DIR
+ OPENH323DIR=$(HOME)/openh323
+endif
+
+ifndef PWLIBDIR
+ PWLIBDIR=$(HOME)/pwlib
+endif
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ LIBS+= -lres_monitor.so -lres_features.so
+endif
+
+clean::
+ $(MAKE) -C misdn clean
+
+ifneq ($(wildcard h323/Makefile.ast),)
+ include h323/Makefile.ast
+H323LDFLAGS+=-Wl,--version-script=h323/noexport.map
+clean::
+ if [ -f h323/Makefile ]; then $(MAKE) -C h323 clean; fi
+else
+h323/libchanh323.a h323/Makefile.ast:
+ $(CMD_PREFIX) $(MAKE) -C h323
+ $(CMD_PREFIX) rm -f ../main/asterisk
+ $(CMD_PREFIX) echo "***************************************************************"
+ $(CMD_PREFIX) echo
+ $(CMD_PREFIX) echo "********** Re-run 'make' to pick up H.323 parameters **********"
+ $(CMD_PREFIX) echo
+ $(CMD_PREFIX) echo "***************************************************************"
+ $(CMD_PREFIX) exit 1
+endif
+
+dist-clean::
+ rm -f h323/Makefile
+
+$(if $(filter chan_iax2,$(EMBEDDED_MODS)),modules.link,chan_iax2.so): iax2-parser.o iax2-provision.o
+
+ifeq ($(OSARCH),linux-gnu)
+chan_h323.so: chan_h323.o h323/libchanh323.a h323/Makefile.ast
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $(H323LDFLAGS) -o $@ $< h323/libchanh323.a $(H323LDLIBS)
+else
+chan_h323.so: chan_h323.o h323/libchanh323.a
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat
+endif
+
+chan_misdn.o: ASTCFLAGS+=-Imisdn
+
+misdn_config.o: ASTCFLAGS+=-Imisdn
+
+misdn/isdn_lib.o: ASTCFLAGS+=-Wno-strict-aliasing
+
+$(if $(filter chan_misdn,$(EMBEDDED_MODS)),modules.link,chan_misdn.so): misdn_config.o misdn/isdn_lib.o misdn/isdn_msg_parser.o
+
+chan_vpb.oo: ASTCFLAGS:=$(filter-out -Wdeclaration-after-statement,$(ASTCFLAGS))
+
+$(if $(filter chan_oss,$(EMBEDDED_MODS)),modules.link,chan_oss.so): console_video.o vgrabbers.o console_board.o
diff --git a/trunk/channels/chan_agent.c b/trunk/channels/chan_agent.c
new file mode 100644
index 000000000..56bb5561c
--- /dev/null
+++ b/trunk/channels/chan_agent.c
@@ -0,0 +1,2399 @@
+/*
+ * 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 Implementation of Agents (proxy channel)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * This file is the implementation of Agents modules.
+ * It is a dynamic module that is loaded by Asterisk.
+ * \par See also
+ * \arg \ref Config_agent
+ *
+ * \ingroup channel_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/manager.h"
+#include "asterisk/features.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/astdb.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/monitor.h"
+#include "asterisk/stringfields.h"
+
+static const char tdesc[] = "Call Agent Proxy Channel";
+static const char config[] = "agents.conf";
+
+static const char app[] = "AgentLogin";
+static const char app3[] = "AgentMonitorOutgoing";
+
+static const char synopsis[] = "Call agent login";
+static const char synopsis3[] = "Record agent's outgoing call";
+
+static const char descrip[] =
+" AgentLogin([AgentNo][,options]):\n"
+"Asks the agent to login to the system. Always returns -1. While\n"
+"logged in, the agent can receive calls and will hear a 'beep'\n"
+"when a new call comes in. The agent can dump the call by pressing\n"
+"the star key.\n"
+"The option string may contain zero or more of the following characters:\n"
+" 's' -- silent login - do not announce the login ok segment after agent logged on/off\n";
+
+static const char descrip3[] =
+" AgentMonitorOutgoing([options]):\n"
+"Tries to figure out the id of the agent who is placing outgoing call based on\n"
+"comparison of the callerid of the current interface and the global variable \n"
+"placed by the AgentCallbackLogin application. That's why it should be used only\n"
+"with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
+"instead of Monitor application. That has to be configured in the agents.conf file.\n"
+"\nReturn value:\n"
+"Normally the app returns 0 unless the options are passed.\n"
+"\nOptions:\n"
+" 'd' - make the app return -1 if there is an error condition\n"
+" 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
+" 'n' - don't generate the warnings when there is no callerid or the\n"
+" agentid is not known.\n"
+" It's handy if you want to have one context for agent and non-agent calls.\n";
+
+static const char mandescr_agents[] =
+"Description: Will list info about all possible agents.\n"
+"Variables: NONE\n";
+
+static const char mandescr_agent_logoff[] =
+"Description: Sets an agent as no longer logged in.\n"
+"Variables: (Names marked with * are required)\n"
+" *Agent: Agent ID of the agent to log off\n"
+" Soft: Set to 'true' to not hangup existing calls\n";
+
+static char moh[80] = "default";
+
+#define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
+#define AST_MAX_BUF 256
+#define AST_MAX_FILENAME_LEN 256
+
+static const char pa_family[] = "Agents"; /*!< Persistent Agents astdb family */
+#define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
+
+static int persistent_agents = 0; /*!< queues.conf [general] option */
+static void dump_agents(void);
+
+static ast_group_t group;
+static int autologoff;
+static int wrapuptime;
+static int ackcall;
+static int endcall;
+static int multiplelogin = 1;
+static int autologoffunavail = 0;
+
+static int maxlogintries = 3;
+static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
+
+static int recordagentcalls = 0;
+static char recordformat[AST_MAX_BUF] = "";
+static char recordformatext[AST_MAX_BUF] = "";
+static char urlprefix[AST_MAX_BUF] = "";
+static char savecallsin[AST_MAX_BUF] = "";
+static int updatecdr = 0;
+static char beep[AST_MAX_BUF] = "beep";
+
+#define GETAGENTBYCALLERID "AGENTBYCALLERID"
+
+/*! \brief Structure representing an agent. */
+struct agent_pvt {
+ ast_mutex_t lock; /*!< Channel private lock */
+ int dead; /*!< Poised for destruction? */
+ int pending; /*!< Not a real agent -- just pending a match */
+ int abouttograb; /*!< About to grab */
+ int autologoff; /*!< Auto timeout time */
+ int ackcall; /*!< ackcall */
+ int deferlogoff; /*!< Defer logoff to hangup */
+ time_t loginstart; /*!< When agent first logged in (0 when logged off) */
+ time_t start; /*!< When call started */
+ struct timeval lastdisc; /*!< When last disconnected */
+ int wrapuptime; /*!< Wrapup time in ms */
+ ast_group_t group; /*!< Group memberships */
+ int acknowledged; /*!< Acknowledged */
+ char moh[80]; /*!< Which music on hold */
+ char agent[AST_MAX_AGENT]; /*!< Agent ID */
+ char password[AST_MAX_AGENT]; /*!< Password for Agent login */
+ char name[AST_MAX_AGENT];
+ ast_mutex_t app_lock; /**< Synchronization between owning applications */
+ volatile pthread_t owning_app; /**< Owning application thread id */
+ volatile int app_sleep_cond; /**< Sleep condition for the login app */
+ struct ast_channel *owner; /**< Agent */
+ char loginchan[80]; /**< channel they logged in from */
+ char logincallerid[80]; /**< Caller ID they had when they logged in */
+ struct ast_channel *chan; /**< Channel we use */
+ AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
+};
+
+static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
+
+#define CHECK_FORMATS(ast, p) do { \
+ if (p->chan) {\
+ if (ast->nativeformats != p->chan->nativeformats) { \
+ ast_debug(1, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
+ /* Native formats changed, reset things */ \
+ ast->nativeformats = p->chan->nativeformats; \
+ ast_debug(1, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
+ ast_set_read_format(ast, ast->readformat); \
+ ast_set_write_format(ast, ast->writeformat); \
+ } \
+ if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \
+ ast_set_read_format(p->chan, ast->rawreadformat); \
+ if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
+ ast_set_write_format(p->chan, ast->rawwriteformat); \
+ } \
+} while(0)
+
+/*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
+ properly for a timingfd XXX This might need more work if agents were logged in as agents or other
+ totally impractical combinations XXX */
+
+#define CLEANUP(ast, p) do { \
+ int x; \
+ if (p->chan) { \
+ for (x=0;x<AST_MAX_FDS;x++) {\
+ if (x != AST_TIMING_FD) \
+ ast_channel_set_fd(ast, x, p->chan->fds[x]); \
+ } \
+ ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \
+ } \
+} while(0)
+
+/*--- Forward declarations */
+static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
+static int agent_devicestate(void *data);
+static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
+static int agent_digit_begin(struct ast_channel *ast, char digit);
+static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int agent_call(struct ast_channel *ast, char *dest, int timeout);
+static int agent_hangup(struct ast_channel *ast);
+static int agent_answer(struct ast_channel *ast);
+static struct ast_frame *agent_read(struct ast_channel *ast);
+static int agent_write(struct ast_channel *ast, struct ast_frame *f);
+static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
+static int agent_sendtext(struct ast_channel *ast, const char *text);
+static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
+static void set_agentbycallerid(const char *callerid, const char *agent);
+static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
+static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
+static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
+
+/*! \brief Channel interface description for PBX integration */
+static const struct ast_channel_tech agent_tech = {
+ .type = "Agent",
+ .description = tdesc,
+ .capabilities = -1,
+ .requester = agent_request,
+ .devicestate = agent_devicestate,
+ .send_digit_begin = agent_digit_begin,
+ .send_digit_end = agent_digit_end,
+ .call = agent_call,
+ .hangup = agent_hangup,
+ .answer = agent_answer,
+ .read = agent_read,
+ .write = agent_write,
+ .write_video = agent_write,
+ .send_html = agent_sendhtml,
+ .send_text = agent_sendtext,
+ .exception = agent_read,
+ .indicate = agent_indicate,
+ .fixup = agent_fixup,
+ .bridged_channel = agent_bridgedchannel,
+ .get_base_channel = agent_get_base_channel,
+ .set_base_channel = agent_set_base_channel,
+};
+
+/*!
+ * Adds an agent to the global list of agents.
+ *
+ * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
+ * \param pending If it is pending or not.
+ * @return The just created agent.
+ * \sa agent_pvt, agents.
+ */
+static struct agent_pvt *add_agent(const char *agent, int pending)
+{
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(agt);
+ AST_APP_ARG(password);
+ AST_APP_ARG(name);
+ );
+ char *password = NULL;
+ char *name = NULL;
+ char *agt = NULL;
+ struct agent_pvt *p;
+
+ parse = ast_strdupa(agent);
+
+ /* Extract username (agt), password and name from agent (args). */
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if(args.argc == 0) {
+ ast_log(LOG_WARNING, "A blank agent line!\n");
+ return NULL;
+ }
+
+ if(ast_strlen_zero(args.agt) ) {
+ ast_log(LOG_WARNING, "An agent line with no agentid!\n");
+ return NULL;
+ } else
+ agt = args.agt;
+
+ if(!ast_strlen_zero(args.password)) {
+ password = args.password;
+ while (*password && *password < 33) password++;
+ }
+ if(!ast_strlen_zero(args.name)) {
+ name = args.name;
+ while (*name && *name < 33) name++;
+ }
+
+ /* Are we searching for the agent here ? To see if it exists already ? */
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ if (!pending && !strcmp(p->agent, agt))
+ break;
+ }
+ if (!p) {
+ // Build the agent.
+ if (!(p = ast_calloc(1, sizeof(*p))))
+ return NULL;
+ ast_copy_string(p->agent, agt, sizeof(p->agent));
+ ast_mutex_init(&p->lock);
+ ast_mutex_init(&p->app_lock);
+ p->owning_app = (pthread_t) -1;
+ p->app_sleep_cond = 1;
+ p->group = group;
+ p->pending = pending;
+ AST_LIST_INSERT_TAIL(&agents, p, list);
+ }
+
+ ast_copy_string(p->password, password ? password : "", sizeof(p->password));
+ ast_copy_string(p->name, name ? name : "", sizeof(p->name));
+ ast_copy_string(p->moh, moh, sizeof(p->moh));
+ p->ackcall = ackcall;
+ p->autologoff = autologoff;
+
+ /* If someone reduces the wrapuptime and reloads, we want it
+ * to change the wrapuptime immediately on all calls */
+ if (p->wrapuptime > wrapuptime) {
+ struct timeval now = ast_tvnow();
+ /* XXX check what is this exactly */
+
+ /* We won't be pedantic and check the tv_usec val */
+ if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
+ p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
+ p->lastdisc.tv_usec = now.tv_usec;
+ }
+ }
+ p->wrapuptime = wrapuptime;
+
+ if (pending)
+ p->dead = 1;
+ else
+ p->dead = 0;
+ return p;
+}
+
+/*!
+ * Deletes an agent after doing some clean up.
+ * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
+ * \param p Agent to be deleted.
+ * \returns Always 0.
+ */
+static int agent_cleanup(struct agent_pvt *p)
+{
+ struct ast_channel *chan = p->owner;
+ p->owner = NULL;
+ chan->tech_pvt = NULL;
+ p->app_sleep_cond = 1;
+ /* Release ownership of the agent to other threads (presumably running the login app). */
+ ast_mutex_unlock(&p->app_lock);
+ if (chan)
+ ast_channel_free(chan);
+ if (p->dead) {
+ ast_mutex_destroy(&p->lock);
+ ast_mutex_destroy(&p->app_lock);
+ ast_free(p);
+ }
+ return 0;
+}
+
+static int check_availability(struct agent_pvt *newlyavailable, int needlock);
+
+static int agent_answer(struct ast_channel *ast)
+{
+ ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
+ return -1;
+}
+
+static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
+{
+ char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
+ char filename[AST_MAX_BUF];
+ int res = -1;
+ if (!p)
+ return -1;
+ if (!ast->monitor) {
+ snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
+ /* substitute . for - */
+ if ((pointer = strchr(filename, '.')))
+ *pointer = '-';
+ snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
+ ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT);
+ ast_monitor_setjoinfiles(ast, 1);
+ snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
+#if 0
+ ast_verbose("name is %s, link is %s\n",tmp, tmp2);
+#endif
+ if (!ast->cdr)
+ ast->cdr = ast_cdr_alloc();
+ ast_cdr_setuserfield(ast, tmp2);
+ res = 0;
+ } else
+ ast_log(LOG_ERROR, "Recording already started on that call.\n");
+ return res;
+}
+
+static int agent_start_monitoring(struct ast_channel *ast, int needlock)
+{
+ return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
+}
+
+static struct ast_frame *agent_read(struct ast_channel *ast)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ struct ast_frame *f = NULL;
+ static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
+ const char *status;
+ ast_mutex_lock(&p->lock);
+ CHECK_FORMATS(ast, p);
+ if (p->chan) {
+ ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
+ p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
+ f = ast_read(p->chan);
+ } else
+ f = &ast_null_frame;
+ if (!f) {
+ /* If there's a channel, hang it up (if it's on a callback) make it NULL */
+ if (p->chan) {
+ p->chan->_bridge = NULL;
+ /* Note that we don't hangup if it's not a callback because Asterisk will do it
+ for us when the PBX instance that called login finishes */
+ if (!ast_strlen_zero(p->loginchan)) {
+ if (p->chan)
+ ast_debug(1, "Bridge on '%s' being cleared (2)\n", p->chan->name);
+
+ status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
+ if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
+ long logintime = time(NULL) - p->loginstart;
+ p->loginstart = 0;
+ ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
+ agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
+ }
+ ast_hangup(p->chan);
+ if (p->wrapuptime && p->acknowledged)
+ p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
+ }
+ p->chan = NULL;
+ p->acknowledged = 0;
+ }
+ } else {
+ /* if acknowledgement is not required, and the channel is up, we may have missed
+ an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
+ if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
+ p->acknowledged = 1;
+ switch (f->frametype) {
+ case AST_FRAME_CONTROL:
+ if (f->subclass == AST_CONTROL_ANSWER) {
+ if (p->ackcall) {
+ ast_verb(3, "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
+ /* Don't pass answer along */
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ p->acknowledged = 1;
+ /* Use the builtin answer frame for the
+ recording start check below. */
+ ast_frfree(f);
+ f = &answer_frame;
+ }
+ }
+ break;
+ case AST_FRAME_DTMF_BEGIN:
+ /*ignore DTMF begin's as it can cause issues with queue announce files*/
+ if((!p->acknowledged && f->subclass == '#') || (f->subclass == '*' && endcall)){
+ ast_frfree(f);
+ f = &ast_null_frame;
+ }
+ break;
+ case AST_FRAME_DTMF_END:
+ if (!p->acknowledged && (f->subclass == '#')) {
+ ast_verb(3, "%s acknowledged\n", p->chan->name);
+ p->acknowledged = 1;
+ ast_frfree(f);
+ f = &answer_frame;
+ } else if (f->subclass == '*' && endcall) {
+ /* terminates call */
+ ast_frfree(f);
+ f = NULL;
+ }
+ break;
+ case AST_FRAME_VOICE:
+ case AST_FRAME_VIDEO:
+ /* don't pass voice or video until the call is acknowledged */
+ if (!p->acknowledged) {
+ ast_frfree(f);
+ f = &ast_null_frame;
+ }
+ default:
+ /* pass everything else on through */
+ break;
+ }
+ }
+
+ CLEANUP(ast,p);
+ if (p->chan && !p->chan->_bridge) {
+ if (strcasecmp(p->chan->tech->type, "Local")) {
+ p->chan->_bridge = ast;
+ if (p->chan)
+ ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+ if (recordagentcalls && f == &answer_frame)
+ agent_start_monitoring(ast,0);
+ return f;
+}
+
+static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ int res = -1;
+ ast_mutex_lock(&p->lock);
+ if (p->chan)
+ res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int agent_sendtext(struct ast_channel *ast, const char *text)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ int res = -1;
+ ast_mutex_lock(&p->lock);
+ if (p->chan)
+ res = ast_sendtext(p->chan, text);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int agent_write(struct ast_channel *ast, struct ast_frame *f)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ int res = -1;
+ CHECK_FORMATS(ast, p);
+ ast_mutex_lock(&p->lock);
+ if (!p->chan)
+ res = 0;
+ else {
+ if ((f->frametype != AST_FRAME_VOICE) ||
+ (f->frametype != AST_FRAME_VIDEO) ||
+ (f->subclass == p->chan->writeformat)) {
+ res = ast_write(p->chan, f);
+ } else {
+ ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
+ f->frametype == AST_FRAME_VOICE ? "audio" : "video",
+ ast->name, p->chan->name);
+ res = 0;
+ }
+ }
+ CLEANUP(ast, p);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct agent_pvt *p = newchan->tech_pvt;
+ ast_mutex_lock(&p->lock);
+ if (p->owner != oldchan) {
+ ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ p->owner = newchan;
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ int res = -1;
+ ast_mutex_lock(&p->lock);
+ if (p->chan)
+ res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
+ else
+ res = 0;
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int agent_digit_begin(struct ast_channel *ast, char digit)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ ast_mutex_lock(&p->lock);
+ ast_senddigit_begin(p->chan, digit);
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ ast_mutex_lock(&p->lock);
+ ast_senddigit_end(p->chan, digit, duration);
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int agent_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int newstate=0;
+ ast_mutex_lock(&p->lock);
+ p->acknowledged = 0;
+ if (!p->chan) {
+ if (p->pending) {
+ ast_debug(1, "Pretending to dial on pending agent\n");
+ newstate = AST_STATE_DIALING;
+ res = 0;
+ } else {
+ ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
+ res = -1;
+ }
+ ast_mutex_unlock(&p->lock);
+ if (newstate)
+ ast_setstate(ast, newstate);
+ return res;
+ } else if (!ast_strlen_zero(p->loginchan)) {
+ time(&p->start);
+ /* Call on this agent */
+ ast_verb(3, "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
+ ast_set_callerid(p->chan,
+ ast->cid.cid_num, ast->cid.cid_name, NULL);
+ ast_channel_inherit_variables(ast, p->chan);
+ res = ast_call(p->chan, p->loginchan, 0);
+ CLEANUP(ast,p);
+ ast_mutex_unlock(&p->lock);
+ return res;
+ }
+ ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
+ ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
+ res = ast_streamfile(p->chan, beep, p->chan->language);
+ ast_debug(3, "Played beep, result '%d'\n", res);
+ if (!res) {
+ res = ast_waitstream(p->chan, "");
+ ast_debug(3, "Waited for stream, result '%d'\n", res);
+ }
+ if (!res) {
+ res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
+ ast_debug(3, "Set read format, result '%d'\n", res);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
+ } else {
+ /* Agent hung-up */
+ p->chan = NULL;
+ }
+
+ if (!res) {
+ res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
+ ast_debug(3, "Set write format, result '%d'\n", res);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
+ }
+ if(!res) {
+ /* Call is immediately up, or might need ack */
+ if (p->ackcall > 1)
+ newstate = AST_STATE_RINGING;
+ else {
+ newstate = AST_STATE_UP;
+ if (recordagentcalls)
+ agent_start_monitoring(ast, 0);
+ p->acknowledged = 1;
+ }
+ res = 0;
+ }
+ CLEANUP(ast, p);
+ ast_mutex_unlock(&p->lock);
+ if (newstate)
+ ast_setstate(ast, newstate);
+ return res;
+}
+
+/*! \brief store/clear the global variable that stores agentid based on the callerid */
+static void set_agentbycallerid(const char *callerid, const char *agent)
+{
+ char buf[AST_MAX_BUF];
+
+ /* if there is no Caller ID, nothing to do */
+ if (ast_strlen_zero(callerid))
+ return;
+
+ snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
+ pbx_builtin_setvar_helper(NULL, buf, agent);
+}
+
+/*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
+struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
+{
+ struct agent_pvt *p = NULL;
+ struct ast_channel *base = chan;
+
+ /* chan is locked by the calling function */
+ if (!chan || !chan->tech_pvt) {
+ ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL);
+ return NULL;
+ }
+ p = chan->tech_pvt;
+ if (p->chan)
+ base = p->chan;
+ return base;
+}
+
+int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
+{
+ struct agent_pvt *p = NULL;
+
+ if (!chan || !base) {
+ ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
+ return -1;
+ }
+ p = chan->tech_pvt;
+ if (!p) {
+ ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
+ return -1;
+ }
+ p->chan = base;
+ return 0;
+}
+
+static int agent_hangup(struct ast_channel *ast)
+{
+ struct agent_pvt *p = ast->tech_pvt;
+ int howlong = 0;
+ const char *status;
+ ast_mutex_lock(&p->lock);
+ p->owner = NULL;
+ ast->tech_pvt = NULL;
+ p->app_sleep_cond = 1;
+ p->acknowledged = 0;
+
+ /* if they really are hung up then set start to 0 so the test
+ * later if we're called on an already downed channel
+ * doesn't cause an agent to be logged out like when
+ * agent_request() is followed immediately by agent_hangup()
+ * as in apps/app_chanisavail.c:chanavail_exec()
+ */
+
+ ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
+ if (p->start && (ast->_state != AST_STATE_UP)) {
+ howlong = time(NULL) - p->start;
+ p->start = 0;
+ } else if (ast->_state == AST_STATE_RESERVED)
+ howlong = 0;
+ else
+ p->start = 0;
+ if (p->chan) {
+ p->chan->_bridge = NULL;
+ /* If they're dead, go ahead and hang up on the agent now */
+ if (!ast_strlen_zero(p->loginchan)) {
+ /* Store last disconnect time */
+ if (p->wrapuptime)
+ p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
+ else
+ p->lastdisc = ast_tv(0,0);
+ if (p->chan) {
+ status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
+ if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
+ long logintime = time(NULL) - p->loginstart;
+ p->loginstart = 0;
+ ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
+ agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
+ }
+ /* Recognize the hangup and pass it along immediately */
+ ast_hangup(p->chan);
+ p->chan = NULL;
+ }
+ ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
+ if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
+ long logintime = time(NULL) - p->loginstart;
+ p->loginstart = 0;
+ if (!p->deferlogoff)
+ ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
+ p->deferlogoff = 0;
+ agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
+ if (persistent_agents)
+ dump_agents();
+ }
+ } else if (p->dead) {
+ ast_channel_lock(p->chan);
+ ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(p->chan);
+ } else if (p->loginstart) {
+ ast_channel_lock(p->chan);
+ ast_indicate_data(p->chan, AST_CONTROL_HOLD,
+ S_OR(p->moh, NULL),
+ !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
+ ast_channel_unlock(p->chan);
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+
+ /* Only register a device state change if the agent is still logged in */
+ if (!p->loginstart) {
+ p->loginchan[0] = '\0';
+ p->logincallerid[0] = '\0';
+ if (persistent_agents)
+ dump_agents();
+ } else {
+ ast_device_state_changed("Agent/%s", p->agent);
+ }
+
+ if (p->pending) {
+ AST_LIST_LOCK(&agents);
+ AST_LIST_REMOVE(&agents, p, list);
+ AST_LIST_UNLOCK(&agents);
+ }
+ if (p->abouttograb) {
+ /* Let the "about to grab" thread know this isn't valid anymore, and let it
+ kill it later */
+ p->abouttograb = 0;
+ } else if (p->dead) {
+ ast_mutex_destroy(&p->lock);
+ ast_mutex_destroy(&p->app_lock);
+ ast_free(p);
+ } else {
+ if (p->chan) {
+ /* Not dead -- check availability now */
+ ast_mutex_lock(&p->lock);
+ /* Store last disconnect time */
+ p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
+ ast_mutex_unlock(&p->lock);
+ }
+ /* Release ownership of the agent to other threads (presumably running the login app). */
+ if (ast_strlen_zero(p->loginchan))
+ ast_mutex_unlock(&p->app_lock);
+ }
+ return 0;
+}
+
+static int agent_cont_sleep( void *data )
+{
+ struct agent_pvt *p;
+ int res;
+
+ p = (struct agent_pvt *)data;
+
+ ast_mutex_lock(&p->lock);
+ res = p->app_sleep_cond;
+ if (p->lastdisc.tv_sec) {
+ if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0)
+ res = 1;
+ }
+ ast_mutex_unlock(&p->lock);
+
+ if (!res)
+ ast_debug(5, "agent_cont_sleep() returning %d\n", res );
+
+ return res;
+}
+
+static int agent_ack_sleep(void *data)
+{
+ struct agent_pvt *p;
+ int res=0;
+ int to = 1000;
+ struct ast_frame *f;
+
+ /* Wait a second and look for something */
+
+ p = (struct agent_pvt *) data;
+ if (!p->chan)
+ return -1;
+
+ for(;;) {
+ to = ast_waitfor(p->chan, to);
+ if (to < 0)
+ return -1;
+ if (!to)
+ return 0;
+ f = ast_read(p->chan);
+ if (!f)
+ return -1;
+ if (f->frametype == AST_FRAME_DTMF)
+ res = f->subclass;
+ else
+ res = 0;
+ ast_frfree(f);
+ ast_mutex_lock(&p->lock);
+ if (!p->app_sleep_cond) {
+ ast_mutex_unlock(&p->lock);
+ return 0;
+ } else if (res == '#') {
+ ast_mutex_unlock(&p->lock);
+ return 1;
+ }
+ ast_mutex_unlock(&p->lock);
+ res = 0;
+ }
+ return res;
+}
+
+static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
+{
+ struct agent_pvt *p = bridge->tech_pvt;
+ struct ast_channel *ret = NULL;
+
+ if (p) {
+ if (chan == p->chan)
+ ret = bridge->_bridge;
+ else if (chan == bridge->_bridge)
+ ret = p->chan;
+ }
+
+ ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
+ return ret;
+}
+
+/*! \brief Create new agent channel */
+static struct ast_channel *agent_new(struct agent_pvt *p, int state)
+{
+ struct ast_channel *tmp;
+#if 0
+ if (!p->chan) {
+ ast_log(LOG_WARNING, "No channel? :(\n");
+ return NULL;
+ }
+#endif
+ if (p->pending)
+ tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, ast_random() & 0xffff);
+ else
+ tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
+ return NULL;
+ }
+
+ tmp->tech = &agent_tech;
+ if (p->chan) {
+ tmp->nativeformats = p->chan->nativeformats;
+ tmp->writeformat = p->chan->writeformat;
+ tmp->rawwriteformat = p->chan->writeformat;
+ tmp->readformat = p->chan->readformat;
+ tmp->rawreadformat = p->chan->readformat;
+ ast_string_field_set(tmp, language, p->chan->language);
+ ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
+ /* XXX Is this really all we copy form the originating channel?? */
+ } else {
+ tmp->nativeformats = AST_FORMAT_SLINEAR;
+ tmp->writeformat = AST_FORMAT_SLINEAR;
+ tmp->rawwriteformat = AST_FORMAT_SLINEAR;
+ tmp->readformat = AST_FORMAT_SLINEAR;
+ tmp->rawreadformat = AST_FORMAT_SLINEAR;
+ }
+ /* Safe, agentlock already held */
+ tmp->tech_pvt = p;
+ p->owner = tmp;
+ tmp->priority = 1;
+ /* Wake up and wait for other applications (by definition the login app)
+ * to release this channel). Takes ownership of the agent channel
+ * to this thread only.
+ * For signalling the other thread, ast_queue_frame is used until we
+ * can safely use signals for this purpose. The pselect() needs to be
+ * implemented in the kernel for this.
+ */
+ p->app_sleep_cond = 0;
+ if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
+ if (p->chan) {
+ ast_queue_frame(p->chan, &ast_null_frame);
+ ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
+ ast_mutex_lock(&p->app_lock);
+ ast_mutex_lock(&p->lock);
+ } else {
+ ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
+ p->owner = NULL;
+ tmp->tech_pvt = NULL;
+ p->app_sleep_cond = 1;
+ ast_channel_free( tmp );
+ ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
+ ast_mutex_unlock(&p->app_lock);
+ return NULL;
+ }
+ } else if (!ast_strlen_zero(p->loginchan)) {
+ if (p->chan)
+ ast_queue_frame(p->chan, &ast_null_frame);
+ if (!p->chan) {
+ ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
+ p->owner = NULL;
+ tmp->tech_pvt = NULL;
+ p->app_sleep_cond = 1;
+ ast_channel_free( tmp );
+ ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
+ return NULL;
+ }
+ }
+ if (p->chan)
+ ast_indicate(p->chan, AST_CONTROL_UNHOLD);
+ p->owning_app = pthread_self();
+ /* After the above step, there should not be any blockers. */
+ if (p->chan) {
+ if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
+ ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
+ CRASH;
+ }
+ }
+ return tmp;
+}
+
+
+/*!
+ * Read configuration data. The file named agents.conf.
+ *
+ * \returns Always 0, or so it seems.
+ */
+static int read_agent_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_config *ucfg;
+ struct ast_variable *v;
+ struct agent_pvt *p;
+ const char *general_val;
+ const char *catname;
+ const char *hasagent;
+ int genhasagent;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ group = 0;
+ autologoff = 0;
+ wrapuptime = 0;
+ ackcall = 0;
+ endcall = 1;
+ cfg = ast_config_load(config, config_flags);
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return -1;
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ p->dead = 1;
+ }
+ strcpy(moh, "default");
+ /* set the default recording values */
+ recordagentcalls = 0;
+ strcpy(recordformat, "wav");
+ strcpy(recordformatext, "wav");
+ urlprefix[0] = '\0';
+ savecallsin[0] = '\0';
+
+ /* Read in [general] section for persistence */
+ if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
+ persistent_agents = ast_true(general_val);
+ multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
+
+ /* Read in the [agents] section */
+ v = ast_variable_browse(cfg, "agents");
+ while(v) {
+ /* Create the interface list */
+ if (!strcasecmp(v->name, "agent")) {
+ add_agent(v->value, 0);
+ } else if (!strcasecmp(v->name, "group")) {
+ group = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "autologoff")) {
+ autologoff = atoi(v->value);
+ if (autologoff < 0)
+ autologoff = 0;
+ } else if (!strcasecmp(v->name, "ackcall")) {
+ if (!strcasecmp(v->value, "always"))
+ ackcall = 2;
+ else if (ast_true(v->value))
+ ackcall = 1;
+ else
+ ackcall = 0;
+ } else if (!strcasecmp(v->name, "endcall")) {
+ endcall = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "wrapuptime")) {
+ wrapuptime = atoi(v->value);
+ if (wrapuptime < 0)
+ wrapuptime = 0;
+ } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
+ maxlogintries = atoi(v->value);
+ if (maxlogintries < 0)
+ maxlogintries = 0;
+ } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
+ strcpy(agentgoodbye,v->value);
+ } else if (!strcasecmp(v->name, "musiconhold")) {
+ ast_copy_string(moh, v->value, sizeof(moh));
+ } else if (!strcasecmp(v->name, "updatecdr")) {
+ if (ast_true(v->value))
+ updatecdr = 1;
+ else
+ updatecdr = 0;
+ } else if (!strcasecmp(v->name, "autologoffunavail")) {
+ if (ast_true(v->value))
+ autologoffunavail = 1;
+ else
+ autologoffunavail = 0;
+ } else if (!strcasecmp(v->name, "recordagentcalls")) {
+ recordagentcalls = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "recordformat")) {
+ ast_copy_string(recordformat, v->value, sizeof(recordformat));
+ if (!strcasecmp(v->value, "wav49"))
+ strcpy(recordformatext, "WAV");
+ else
+ ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
+ } else if (!strcasecmp(v->name, "urlprefix")) {
+ ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
+ if (urlprefix[strlen(urlprefix) - 1] != '/')
+ strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
+ } else if (!strcasecmp(v->name, "savecallsin")) {
+ if (v->value[0] == '/')
+ ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
+ else
+ snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
+ if (savecallsin[strlen(savecallsin) - 1] != '/')
+ strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
+ } else if (!strcasecmp(v->name, "custom_beep")) {
+ ast_copy_string(beep, v->value, sizeof(beep));
+ }
+ v = v->next;
+ }
+ if ((ucfg = ast_config_load("users.conf", config_flags)) && ucfg != CONFIG_STATUS_FILEUNCHANGED) {
+ genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
+ catname = ast_category_browse(ucfg, NULL);
+ while(catname) {
+ if (strcasecmp(catname, "general")) {
+ hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
+ if (ast_true(hasagent) || (!hasagent && genhasagent)) {
+ char tmp[256];
+ const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
+ const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
+ if (!fullname)
+ fullname = "";
+ if (!secret)
+ secret = "";
+ snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
+ add_agent(tmp, 0);
+ }
+ }
+ catname = ast_category_browse(ucfg, catname);
+ }
+ ast_config_destroy(ucfg);
+ }
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
+ if (p->dead) {
+ AST_LIST_REMOVE_CURRENT(list);
+ /* Destroy if appropriate */
+ if (!p->owner) {
+ if (!p->chan) {
+ ast_mutex_destroy(&p->lock);
+ ast_mutex_destroy(&p->app_lock);
+ ast_free(p);
+ } else {
+ /* Cause them to hang up */
+ ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
+ }
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&agents);
+ ast_config_destroy(cfg);
+ return 1;
+}
+
+static int check_availability(struct agent_pvt *newlyavailable, int needlock)
+{
+ struct ast_channel *chan=NULL, *parent=NULL;
+ struct agent_pvt *p;
+ int res;
+
+ ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
+ if (needlock)
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ if (p == newlyavailable) {
+ continue;
+ }
+ ast_mutex_lock(&p->lock);
+ if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
+ ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
+ /* We found a pending call, time to merge */
+ chan = agent_new(newlyavailable, AST_STATE_DOWN);
+ parent = p->owner;
+ p->abouttograb = 1;
+ ast_mutex_unlock(&p->lock);
+ break;
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ if (needlock)
+ AST_LIST_UNLOCK(&agents);
+ if (parent && chan) {
+ if (newlyavailable->ackcall > 1) {
+ /* Don't do beep here */
+ res = 0;
+ } else {
+ ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
+ res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
+ ast_debug(3, "Played beep, result '%d'\n", res);
+ if (!res) {
+ res = ast_waitstream(newlyavailable->chan, "");
+ ast_debug(1, "Waited for stream, result '%d'\n", res);
+ }
+ }
+ if (!res) {
+ /* Note -- parent may have disappeared */
+ if (p->abouttograb) {
+ newlyavailable->acknowledged = 1;
+ /* Safe -- agent lock already held */
+ ast_setstate(parent, AST_STATE_UP);
+ ast_setstate(chan, AST_STATE_UP);
+ ast_copy_string(parent->context, chan->context, sizeof(parent->context));
+ /* Go ahead and mark the channel as a zombie so that masquerade will
+ destroy it for us, and we need not call ast_hangup */
+ ast_set_flag(chan, AST_FLAG_ZOMBIE);
+ ast_channel_masquerade(parent, chan);
+ p->abouttograb = 0;
+ } else {
+ ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
+ agent_cleanup(newlyavailable);
+ }
+ } else {
+ ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
+ agent_cleanup(newlyavailable);
+ }
+ }
+ return 0;
+}
+
+static int check_beep(struct agent_pvt *newlyavailable, int needlock)
+{
+ struct agent_pvt *p;
+ int res=0;
+
+ ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
+ if (needlock)
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ if (p == newlyavailable) {
+ continue;
+ }
+ ast_mutex_lock(&p->lock);
+ if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
+ ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
+ ast_mutex_unlock(&p->lock);
+ break;
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ if (needlock)
+ AST_LIST_UNLOCK(&agents);
+ if (p) {
+ ast_mutex_unlock(&newlyavailable->lock);
+ ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
+ res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
+ ast_debug(1, "Played beep, result '%d'\n", res);
+ if (!res) {
+ res = ast_waitstream(newlyavailable->chan, "");
+ ast_debug(1, "Waited for stream, result '%d'\n", res);
+ }
+ ast_mutex_lock(&newlyavailable->lock);
+ }
+ return res;
+}
+
+/*! \brief Part of the Asterisk PBX interface */
+static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
+{
+ struct agent_pvt *p;
+ struct ast_channel *chan = NULL;
+ char *s;
+ ast_group_t groupmatch;
+ int groupoff;
+ int waitforagent=0;
+ int hasagent = 0;
+ struct timeval tv;
+
+ s = data;
+ if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
+ groupmatch = (1 << groupoff);
+ } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
+ groupmatch = (1 << groupoff);
+ waitforagent = 1;
+ } else
+ groupmatch = 0;
+
+ /* Check actual logged in agents first */
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ ast_mutex_lock(&p->lock);
+ if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
+ ast_strlen_zero(p->loginchan)) {
+ if (p->chan)
+ hasagent++;
+ tv = ast_tvnow();
+ if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
+ p->lastdisc = ast_tv(0, 0);
+ /* Agent must be registered, but not have any active call, and not be in a waiting state */
+ if (!p->owner && p->chan) {
+ /* Fixed agent */
+ chan = agent_new(p, AST_STATE_DOWN);
+ }
+ if (chan) {
+ ast_mutex_unlock(&p->lock);
+ break;
+ }
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ if (!p) {
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ ast_mutex_lock(&p->lock);
+ if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
+ if (p->chan || !ast_strlen_zero(p->loginchan))
+ hasagent++;
+ tv = ast_tvnow();
+#if 0
+ ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
+#endif
+ if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
+ p->lastdisc = ast_tv(0, 0);
+ /* Agent must be registered, but not have any active call, and not be in a waiting state */
+ if (!p->owner && p->chan) {
+ /* Could still get a fixed agent */
+ chan = agent_new(p, AST_STATE_DOWN);
+ } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
+ /* Adjustable agent */
+ p->chan = ast_request("Local", format, p->loginchan, cause);
+ if (p->chan)
+ chan = agent_new(p, AST_STATE_DOWN);
+ }
+ if (chan) {
+ ast_mutex_unlock(&p->lock);
+ break;
+ }
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ }
+
+ if (!chan && waitforagent) {
+ /* No agent available -- but we're requesting to wait for one.
+ Allocate a place holder */
+ if (hasagent) {
+ ast_debug(1, "Creating place holder for '%s'\n", s);
+ p = add_agent(data, 1);
+ p->group = groupmatch;
+ chan = agent_new(p, AST_STATE_DOWN);
+ if (!chan)
+ ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
+ } else {
+ ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
+ }
+ }
+ *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
+ AST_LIST_UNLOCK(&agents);
+ return chan;
+}
+
+static force_inline int powerof(unsigned int d)
+{
+ int x = ffs(d);
+
+ if (x)
+ return x - 1;
+
+ return 0;
+}
+
+/*!
+ * Lists agents and their status to the Manager API.
+ * It is registered on load_module() and it gets called by the manager backend.
+ * \param s
+ * \param m
+ * \returns
+ * \sa action_agent_logoff(), load_module().
+ */
+static int action_agents(struct mansession *s, const struct message *m)
+{
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+ char chanbuf[256];
+ struct agent_pvt *p;
+ char *username = NULL;
+ char *loginChan = NULL;
+ char *talkingto = NULL;
+ char *talkingtoChan = NULL;
+ char *status = NULL;
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
+ astman_send_ack(s, m, "Agents will follow");
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ ast_mutex_lock(&p->lock);
+
+ /* Status Values:
+ AGENT_LOGGEDOFF - Agent isn't logged in
+ AGENT_IDLE - Agent is logged in, and waiting for call
+ AGENT_ONCALL - Agent is logged in, and on a call
+ AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
+
+ username = S_OR(p->name, "None");
+
+ /* Set a default status. It 'should' get changed. */
+ status = "AGENT_UNKNOWN";
+
+ if (!ast_strlen_zero(p->loginchan) && !p->chan) {
+ loginChan = p->loginchan;
+ talkingto = "n/a";
+ talkingtoChan = "n/a";
+ status = "AGENT_IDLE";
+ if (p->acknowledged) {
+ snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
+ loginChan = chanbuf;
+ }
+ } else if (p->chan) {
+ loginChan = ast_strdupa(p->chan->name);
+ if (p->owner && p->owner->_bridge) {
+ talkingto = p->chan->cid.cid_num;
+ if (ast_bridged_channel(p->owner))
+ talkingtoChan = ast_strdupa(ast_bridged_channel(p->owner)->name);
+ else
+ talkingtoChan = "n/a";
+ status = "AGENT_ONCALL";
+ } else {
+ talkingto = "n/a";
+ talkingtoChan = "n/a";
+ status = "AGENT_IDLE";
+ }
+ } else {
+ loginChan = "n/a";
+ talkingto = "n/a";
+ talkingtoChan = "n/a";
+ status = "AGENT_LOGGEDOFF";
+ }
+
+ astman_append(s, "Event: Agents\r\n"
+ "Agent: %s\r\n"
+ "Name: %s\r\n"
+ "Status: %s\r\n"
+ "LoggedInChan: %s\r\n"
+ "LoggedInTime: %d\r\n"
+ "TalkingTo: %s\r\n"
+ "TalkingToChan: %s\r\n"
+ "%s"
+ "\r\n",
+ p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
+ ast_mutex_unlock(&p->lock);
+ }
+ AST_LIST_UNLOCK(&agents);
+ astman_append(s, "Event: AgentsComplete\r\n"
+ "%s"
+ "\r\n",idText);
+ return 0;
+}
+
+static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
+{
+ char *tmp = NULL;
+ char agent[AST_MAX_AGENT];
+
+ if (!ast_strlen_zero(logcommand))
+ tmp = logcommand;
+ else
+ tmp = ast_strdupa("");
+
+ snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
+
+ if (!ast_strlen_zero(uniqueid)) {
+ manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
+ "Agent: %s\r\n"
+ "Reason: %s\r\n"
+ "Loginchan: %s\r\n"
+ "Logintime: %ld\r\n"
+ "Uniqueid: %s\r\n",
+ p->agent, tmp, loginchan, logintime, uniqueid);
+ } else {
+ manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
+ "Agent: %s\r\n"
+ "Reason: %s\r\n"
+ "Loginchan: %s\r\n"
+ "Logintime: %ld\r\n",
+ p->agent, tmp, loginchan, logintime);
+ }
+
+ ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
+ set_agentbycallerid(p->logincallerid, NULL);
+ p->loginchan[0] ='\0';
+ p->logincallerid[0] = '\0';
+ ast_device_state_changed("Agent/%s", p->agent);
+ if (persistent_agents)
+ dump_agents();
+
+}
+
+static int agent_logoff(const char *agent, int soft)
+{
+ struct agent_pvt *p;
+ long logintime;
+ int ret = -1; /* Return -1 if no agent if found */
+
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ if (!strcasecmp(p->agent, agent)) {
+ ret = 0;
+ if (p->owner || p->chan) {
+ if (!soft) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
+ if (p->chan)
+ ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
+ } else
+ p->deferlogoff = 1;
+ } else {
+ logintime = time(NULL) - p->loginstart;
+ p->loginstart = 0;
+ agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int ret;
+ char *agent;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "agent logoff";
+ e->usage =
+ "Usage: agent logoff <channel> [soft]\n"
+ " Sets an agent as no longer logged in.\n"
+ " If 'soft' is specified, do not hangup existing calls.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
+ return CLI_SHOWUSAGE;
+
+ agent = a->argv[2] + 6;
+ ret = agent_logoff(agent, a->argc == 4);
+ if (ret == 0)
+ ast_cli(a->fd, "Logging out %s\n", agent);
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * Sets an agent as no longer logged in in the Manager API.
+ * It is registered on load_module() and it gets called by the manager backend.
+ * \param s
+ * \param m
+ * \returns
+ * \sa action_agents(), load_module().
+ */
+static int action_agent_logoff(struct mansession *s, const struct message *m)
+{
+ const char *agent = astman_get_header(m, "Agent");
+ const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
+ int soft;
+ int ret; /* return value of agent_logoff */
+
+ if (ast_strlen_zero(agent)) {
+ astman_send_error(s, m, "No agent specified");
+ return 0;
+ }
+
+ soft = ast_true(soft_s) ? 1 : 0;
+ ret = agent_logoff(agent, soft);
+ if (ret == 0)
+ astman_send_ack(s, m, "Agent logged out");
+ else
+ astman_send_error(s, m, "No such agent");
+
+ return 0;
+}
+
+static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
+{
+ if (pos == 2) {
+ struct agent_pvt *p;
+ char name[AST_MAX_AGENT];
+ int which = 0, len = strlen(word);
+
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ snprintf(name, sizeof(name), "Agent/%s", p->agent);
+ if (!strncasecmp(word, name, len) && p->loginstart && ++which > state)
+ return ast_strdup(name);
+ }
+ } else if (pos == 3 && state == 0)
+ return ast_strdup("soft");
+
+ return NULL;
+}
+
+/*!
+ * Show agents in cli.
+ */
+static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct agent_pvt *p;
+ char username[AST_MAX_BUF];
+ char location[AST_MAX_BUF] = "";
+ char talkingto[AST_MAX_BUF] = "";
+ char moh[AST_MAX_BUF];
+ int count_agents = 0; /*!< Number of agents configured */
+ int online_agents = 0; /*!< Number of online agents */
+ int offline_agents = 0; /*!< Number of offline agents */
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "agent show";
+ e->usage =
+ "Usage: agent show\n"
+ " Provides summary information on agents.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ ast_mutex_lock(&p->lock);
+ if (p->pending) {
+ if (p->group)
+ ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
+ else
+ ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
+ } else {
+ if (!ast_strlen_zero(p->name))
+ snprintf(username, sizeof(username), "(%s) ", p->name);
+ else
+ username[0] = '\0';
+ if (p->chan) {
+ snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
+ if (p->owner && ast_bridged_channel(p->owner))
+ snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
+ else
+ strcpy(talkingto, " is idle");
+ online_agents++;
+ } else if (!ast_strlen_zero(p->loginchan)) {
+ if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
+ snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
+ else
+ snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
+ talkingto[0] = '\0';
+ online_agents++;
+ if (p->acknowledged)
+ strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
+ } else {
+ strcpy(location, "not logged in");
+ talkingto[0] = '\0';
+ offline_agents++;
+ }
+ if (!ast_strlen_zero(p->moh))
+ snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
+ ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
+ username, location, talkingto, moh);
+ count_agents++;
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ AST_LIST_UNLOCK(&agents);
+ if ( !count_agents )
+ ast_cli(a->fd, "No Agents are configured in %s\n",config);
+ else
+ ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
+ ast_cli(a->fd, "\n");
+
+ return CLI_SUCCESS;
+}
+
+
+static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct agent_pvt *p;
+ char username[AST_MAX_BUF];
+ char location[AST_MAX_BUF] = "";
+ char talkingto[AST_MAX_BUF] = "";
+ char moh[AST_MAX_BUF];
+ int count_agents = 0; /* Number of agents configured */
+ int online_agents = 0; /* Number of online agents */
+ int agent_status = 0; /* 0 means offline, 1 means online */
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "agent show online";
+ e->usage =
+ "Usage: agent show online\n"
+ " Provides a list of all online agents.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ agent_status = 0; /* reset it to offline */
+ ast_mutex_lock(&p->lock);
+ if (!ast_strlen_zero(p->name))
+ snprintf(username, sizeof(username), "(%s) ", p->name);
+ else
+ username[0] = '\0';
+ if (p->chan) {
+ snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
+ if (p->owner && ast_bridged_channel(p->owner))
+ snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
+ else
+ strcpy(talkingto, " is idle");
+ agent_status = 1;
+ online_agents++;
+ } else if (!ast_strlen_zero(p->loginchan)) {
+ snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
+ talkingto[0] = '\0';
+ agent_status = 1;
+ online_agents++;
+ if (p->acknowledged)
+ strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
+ }
+ if (!ast_strlen_zero(p->moh))
+ snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
+ if (agent_status)
+ ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
+ count_agents++;
+ ast_mutex_unlock(&p->lock);
+ }
+ AST_LIST_UNLOCK(&agents);
+ if (!count_agents)
+ ast_cli(a->fd, "No Agents are configured in %s\n", config);
+ else
+ ast_cli(a->fd, "%d agents online\n", online_agents);
+ ast_cli(a->fd, "\n");
+ return CLI_SUCCESS;
+}
+
+static const char agent_logoff_usage[] =
+"Usage: agent logoff <channel> [soft]\n"
+" Sets an agent as no longer logged in.\n"
+" If 'soft' is specified, do not hangup existing calls.\n";
+
+static struct ast_cli_entry cli_agents[] = {
+ AST_CLI_DEFINE(agents_show, "Show status of agents"),
+ AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
+ AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
+};
+
+/*!
+ * Called by the AgentLogin application (from the dial plan).
+ *
+ * \brief Log in agent application.
+ *
+ * \param chan
+ * \param data
+ * \returns
+ * \sa agentmonitoroutgoing_exec(), load_module().
+ */
+static int login_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int tries = 0;
+ int max_login_tries = maxlogintries;
+ struct agent_pvt *p;
+ struct ast_module_user *u;
+ int login_state = 0;
+ char user[AST_MAX_AGENT] = "";
+ char pass[AST_MAX_AGENT];
+ char agent[AST_MAX_AGENT] = "";
+ char xpass[AST_MAX_AGENT] = "";
+ char *errmsg;
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(agent_id);
+ AST_APP_ARG(options);
+ AST_APP_ARG(extension);
+ );
+ const char *tmpoptions = NULL;
+ int play_announcement = 1;
+ char agent_goodbye[AST_MAX_FILENAME_LEN];
+ int update_cdr = updatecdr;
+ char *filename = "agent-loginok";
+
+ u = ast_module_user_add(chan);
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
+
+ /* Set Channel Specific Login Overrides */
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
+ max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
+ if (max_login_tries < 0)
+ max_login_tries = 0;
+ tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
+ ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
+ }
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
+ if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
+ update_cdr = 1;
+ else
+ update_cdr = 0;
+ tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
+ ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
+ }
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
+ strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
+ tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
+ ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
+ }
+ /* End Channel Specific Login Overrides */
+
+ if (!ast_strlen_zero(args.options)) {
+ if (strchr(args.options, 's')) {
+ play_announcement = 0;
+ }
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ if (!res) {
+ if (!ast_strlen_zero(args.agent_id))
+ ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
+ else
+ res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
+ }
+ while (!res && (max_login_tries==0 || tries < max_login_tries)) {
+ tries++;
+ /* Check for password */
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ if (!strcmp(p->agent, user) && !p->pending)
+ ast_copy_string(xpass, p->password, sizeof(xpass));
+ }
+ AST_LIST_UNLOCK(&agents);
+ if (!res) {
+ if (!ast_strlen_zero(xpass))
+ res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
+ else
+ pass[0] = '\0';
+ }
+ errmsg = "agent-incorrect";
+
+#if 0
+ ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
+#endif
+
+ /* Check again for accuracy */
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ ast_mutex_lock(&p->lock);
+ if (!strcmp(p->agent, user) &&
+ !strcmp(p->password, pass) && !p->pending) {
+ login_state = 1; /* Successful Login */
+
+ /* Ensure we can't be gotten until we're done */
+ p->lastdisc = ast_tvnow();
+ p->lastdisc.tv_sec++;
+
+ /* Set Channel Specific Agent Overrides */
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
+ if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
+ p->ackcall = 2;
+ else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
+ p->ackcall = 1;
+ else
+ p->ackcall = 0;
+ tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
+ ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
+ }
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
+ p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
+ if (p->autologoff < 0)
+ p->autologoff = 0;
+ tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
+ ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
+ }
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
+ p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
+ if (p->wrapuptime < 0)
+ p->wrapuptime = 0;
+ tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
+ ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
+ }
+ /* End Channel Specific Agent Overrides */
+ if (!p->chan) {
+ long logintime;
+ snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
+
+ p->loginchan[0] = '\0';
+ p->logincallerid[0] = '\0';
+ p->acknowledged = 0;
+
+ ast_mutex_unlock(&p->lock);
+ AST_LIST_UNLOCK(&agents);
+ if( !res && play_announcement==1 )
+ res = ast_streamfile(chan, filename, chan->language);
+ if (!res)
+ ast_waitstream(chan, "");
+ AST_LIST_LOCK(&agents);
+ ast_mutex_lock(&p->lock);
+ if (!res) {
+ res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
+ }
+ if (!res) {
+ res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
+ }
+ /* Check once more just in case */
+ if (p->chan)
+ res = -1;
+ if (!res) {
+ ast_indicate_data(chan, AST_CONTROL_HOLD,
+ S_OR(p->moh, NULL),
+ !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
+ if (p->loginstart == 0)
+ time(&p->loginstart);
+ manager_event(EVENT_FLAG_AGENT, "Agentlogin",
+ "Agent: %s\r\n"
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n",
+ p->agent, chan->name, chan->uniqueid);
+ if (update_cdr && chan->cdr)
+ snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
+ ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
+ ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
+ ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
+ /* Login this channel and wait for it to go away */
+ p->chan = chan;
+ if (p->ackcall > 1)
+ check_beep(p, 0);
+ else
+ check_availability(p, 0);
+ ast_mutex_unlock(&p->lock);
+ AST_LIST_UNLOCK(&agents);
+ ast_device_state_changed("Agent/%s", p->agent);
+ while (res >= 0) {
+ ast_mutex_lock(&p->lock);
+ if (p->deferlogoff && p->chan) {
+ ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
+ p->deferlogoff = 0;
+ }
+ if (p->chan != chan)
+ res = -1;
+ ast_mutex_unlock(&p->lock);
+ /* Yield here so other interested threads can kick in. */
+ sched_yield();
+ if (res)
+ break;
+
+ AST_LIST_LOCK(&agents);
+ ast_mutex_lock(&p->lock);
+ if (p->lastdisc.tv_sec) {
+ if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
+ ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
+ p->lastdisc = ast_tv(0, 0);
+ ast_device_state_changed("Agent/%s", p->agent);
+ if (p->ackcall > 1)
+ check_beep(p, 0);
+ else
+ check_availability(p, 0);
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+ AST_LIST_UNLOCK(&agents);
+ /* Synchronize channel ownership between call to agent and itself. */
+ ast_mutex_lock( &p->app_lock );
+ ast_mutex_lock(&p->lock);
+ p->owning_app = pthread_self();
+ ast_mutex_unlock(&p->lock);
+ if (p->ackcall > 1)
+ res = agent_ack_sleep(p);
+ else
+ res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
+ ast_mutex_unlock( &p->app_lock );
+ if ((p->ackcall > 1) && (res == 1)) {
+ AST_LIST_LOCK(&agents);
+ ast_mutex_lock(&p->lock);
+ check_availability(p, 0);
+ ast_mutex_unlock(&p->lock);
+ AST_LIST_UNLOCK(&agents);
+ res = 0;
+ }
+ sched_yield();
+ }
+ ast_mutex_lock(&p->lock);
+ if (res && p->owner)
+ ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
+ /* Log us off if appropriate */
+ if (p->chan == chan)
+ p->chan = NULL;
+ p->acknowledged = 0;
+ logintime = time(NULL) - p->loginstart;
+ p->loginstart = 0;
+ ast_mutex_unlock(&p->lock);
+ manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
+ "Agent: %s\r\n"
+ "Logintime: %ld\r\n"
+ "Uniqueid: %s\r\n",
+ p->agent, logintime, chan->uniqueid);
+ ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
+ ast_verb(2, "Agent '%s' logged out\n", p->agent);
+ /* If there is no owner, go ahead and kill it now */
+ ast_device_state_changed("Agent/%s", p->agent);
+ if (p->dead && !p->owner) {
+ ast_mutex_destroy(&p->lock);
+ ast_mutex_destroy(&p->app_lock);
+ ast_free(p);
+ }
+ }
+ else {
+ ast_mutex_unlock(&p->lock);
+ p = NULL;
+ }
+ res = -1;
+ } else {
+ ast_mutex_unlock(&p->lock);
+ errmsg = "agent-alreadyon";
+ p = NULL;
+ }
+ break;
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ if (!p)
+ AST_LIST_UNLOCK(&agents);
+
+ if (!res && (max_login_tries==0 || tries < max_login_tries))
+ res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
+ }
+
+ if (!res)
+ res = ast_safe_sleep(chan, 500);
+
+ ast_module_user_remove(u);
+
+ return -1;
+}
+
+/*!
+ * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
+ *
+ * \param chan
+ * \param data
+ * \returns
+ * \sa login_exec(), load_module().
+ */
+static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
+{
+ int exitifnoagentid = 0;
+ int nowarnings = 0;
+ int changeoutgoing = 0;
+ int res = 0;
+ char agent[AST_MAX_AGENT];
+
+ if (data) {
+ if (strchr(data, 'd'))
+ exitifnoagentid = 1;
+ if (strchr(data, 'n'))
+ nowarnings = 1;
+ if (strchr(data, 'c'))
+ changeoutgoing = 1;
+ }
+ if (chan->cid.cid_num) {
+ const char *tmp;
+ char agentvar[AST_MAX_BUF];
+ snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
+ if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
+ struct agent_pvt *p;
+ ast_copy_string(agent, tmp, sizeof(agent));
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ if (!strcasecmp(p->agent, tmp)) {
+ if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
+ __agent_start_monitoring(chan, p, 1);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&agents);
+
+ } else {
+ res = -1;
+ if (!nowarnings)
+ ast_log(LOG_WARNING, "Couldn't find the global variable %s, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n", agentvar);
+ }
+ } else {
+ res = -1;
+ if (!nowarnings)
+ ast_log(LOG_WARNING, "There is no callerid on that call, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n");
+ }
+ if (res) {
+ if (exitifnoagentid)
+ return res;
+ }
+ return 0;
+}
+
+/*!
+ * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
+ */
+static void dump_agents(void)
+{
+ struct agent_pvt *cur_agent = NULL;
+ char buf[256];
+
+ AST_LIST_TRAVERSE(&agents, cur_agent, list) {
+ if (cur_agent->chan)
+ continue;
+
+ if (!ast_strlen_zero(cur_agent->loginchan)) {
+ snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
+ if (ast_db_put(pa_family, cur_agent->agent, buf))
+ ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
+ else
+ ast_debug(1, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
+ } else {
+ /* Delete - no agent or there is an error */
+ ast_db_del(pa_family, cur_agent->agent);
+ }
+ }
+}
+
+/*!
+ * \brief Reload the persistent agents from astdb.
+ */
+static void reload_agents(void)
+{
+ char *agent_num;
+ struct ast_db_entry *db_tree;
+ struct ast_db_entry *entry;
+ struct agent_pvt *cur_agent;
+ char agent_data[256];
+ char *parse;
+ char *agent_chan;
+ char *agent_callerid;
+
+ db_tree = ast_db_gettree(pa_family, NULL);
+
+ AST_LIST_LOCK(&agents);
+ for (entry = db_tree; entry; entry = entry->next) {
+ agent_num = entry->key + strlen(pa_family) + 2;
+ AST_LIST_TRAVERSE(&agents, cur_agent, list) {
+ ast_mutex_lock(&cur_agent->lock);
+ if (strcmp(agent_num, cur_agent->agent) == 0)
+ break;
+ ast_mutex_unlock(&cur_agent->lock);
+ }
+ if (!cur_agent) {
+ ast_db_del(pa_family, agent_num);
+ continue;
+ } else
+ ast_mutex_unlock(&cur_agent->lock);
+ if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
+ ast_debug(1, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
+ parse = agent_data;
+ agent_chan = strsep(&parse, ";");
+ agent_callerid = strsep(&parse, ";");
+ ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
+ if (agent_callerid) {
+ ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
+ set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
+ } else
+ cur_agent->logincallerid[0] = '\0';
+ if (cur_agent->loginstart == 0)
+ time(&cur_agent->loginstart);
+ ast_device_state_changed("Agent/%s", cur_agent->agent);
+ }
+ }
+ AST_LIST_UNLOCK(&agents);
+ if (db_tree) {
+ ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
+ ast_db_freetree(db_tree);
+ }
+}
+
+/*! \brief Part of PBX channel interface */
+static int agent_devicestate(void *data)
+{
+ struct agent_pvt *p;
+ char *s;
+ ast_group_t groupmatch;
+ int groupoff;
+ int res = AST_DEVICE_INVALID;
+
+ s = data;
+ if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
+ groupmatch = (1 << groupoff);
+ else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
+ groupmatch = (1 << groupoff);
+ } else
+ groupmatch = 0;
+
+ /* Check actual logged in agents first */
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ ast_mutex_lock(&p->lock);
+ if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
+ if (p->owner) {
+ if (res != AST_DEVICE_INUSE)
+ res = AST_DEVICE_BUSY;
+ } else {
+ if (res == AST_DEVICE_BUSY)
+ res = AST_DEVICE_INUSE;
+ if (p->chan || !ast_strlen_zero(p->loginchan)) {
+ if (res == AST_DEVICE_INVALID)
+ res = AST_DEVICE_UNKNOWN;
+ } else if (res == AST_DEVICE_INVALID)
+ res = AST_DEVICE_UNAVAILABLE;
+ }
+ if (!strcmp(data, p->agent)) {
+ ast_mutex_unlock(&p->lock);
+ break;
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ AST_LIST_UNLOCK(&agents);
+ return res;
+}
+
+static struct agent_pvt *find_agent(char *agentid)
+{
+ struct agent_pvt *cur;
+
+ AST_LIST_TRAVERSE(&agents, cur, list) {
+ if (!strcmp(cur->agent, agentid))
+ break;
+ }
+
+ return cur;
+}
+
+static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(agentid);
+ AST_APP_ARG(item);
+ );
+ char *tmp;
+ struct agent_pvt *agent;
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, ':');
+ if (!args.item)
+ args.item = "status";
+
+ if (!(agent = find_agent(args.agentid))) {
+ ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
+ return -1;
+ }
+
+ if (!strcasecmp(args.item, "status")) {
+ char *status = "LOGGEDOUT";
+ if (agent->chan || !ast_strlen_zero(agent->loginchan))
+ status = "LOGGEDIN";
+ ast_copy_string(buf, status, len);
+ } else if (!strcasecmp(args.item, "password"))
+ ast_copy_string(buf, agent->password, len);
+ else if (!strcasecmp(args.item, "name"))
+ ast_copy_string(buf, agent->name, len);
+ else if (!strcasecmp(args.item, "mohclass"))
+ ast_copy_string(buf, agent->moh, len);
+ else if (!strcasecmp(args.item, "channel")) {
+ if (agent->chan) {
+ ast_copy_string(buf, agent->chan->name, len);
+ tmp = strrchr(buf, '-');
+ if (tmp)
+ *tmp = '\0';
+ }
+ } else if (!strcasecmp(args.item, "exten"))
+ ast_copy_string(buf, agent->loginchan, len);
+
+ return 0;
+}
+
+struct ast_custom_function agent_function = {
+ .name = "AGENT",
+ .synopsis = "Gets information about an Agent",
+ .syntax = "AGENT(<agentid>[:item])",
+ .read = function_agent,
+ .desc = "The valid items to retrieve are:\n"
+ "- status (default) The status of the agent\n"
+ " LOGGEDIN | LOGGEDOUT\n"
+ "- password The password of the agent\n"
+ "- name The name of the agent\n"
+ "- mohclass MusicOnHold class\n"
+ "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
+ "- channel The name of the active channel for the Agent (AgentLogin)\n"
+};
+
+
+/*!
+ * \brief Initialize the Agents module.
+ * This function is being called by Asterisk when loading the module.
+ * Among other things it registers applications, cli commands and reads the cofiguration file.
+ *
+ * \returns int Always 0.
+ */
+static int load_module(void)
+{
+ /* Make sure we can register our agent channel type */
+ if (ast_channel_register(&agent_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ /* Read in the config */
+ if (!read_agent_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ if (persistent_agents)
+ reload_agents();
+ /* Dialplan applications */
+ ast_register_application(app, login_exec, synopsis, descrip);
+ ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
+
+ /* Manager commands */
+ ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
+ ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
+
+ /* CLI Commands */
+ ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
+
+ /* Dialplan Functions */
+ ast_custom_function_register(&agent_function);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload(void)
+{
+ if (!read_agent_config(1)) {
+ if (persistent_agents)
+ reload_agents();
+ }
+ return 0;
+}
+
+static int unload_module(void)
+{
+ struct agent_pvt *p;
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&agent_tech);
+ /* Unregister dialplan functions */
+ ast_custom_function_unregister(&agent_function);
+ /* Unregister CLI commands */
+ ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
+ /* Unregister dialplan applications */
+ ast_unregister_application(app);
+ ast_unregister_application(app3);
+ /* Unregister manager command */
+ ast_manager_unregister("Agents");
+ ast_manager_unregister("AgentLogoff");
+ /* Unregister channel */
+ AST_LIST_LOCK(&agents);
+ /* Hangup all interfaces if they have an owner */
+ while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ ast_free(p);
+ }
+ AST_LIST_UNLOCK(&agents);
+ AST_LIST_HEAD_DESTROY(&agents);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/channels/chan_alsa.c b/trunk/channels/chan_alsa.c
new file mode 100644
index 000000000..ec1107695
--- /dev/null
+++ b/trunk/channels/chan_alsa.c
@@ -0,0 +1,925 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * By Matthew Fredrickson <creslin@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 ALSA sound card channel driver
+ *
+ * \author Matthew Fredrickson <creslin@digium.com>
+ *
+ * \par See also
+ * \arg Config_alsa
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>asound</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/endian.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/musiconhold.h"
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf = {
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+#define DEBUG 0
+/* Which device to use */
+#define ALSA_INDEV "default"
+#define ALSA_OUTDEV "default"
+#define DESIRED_RATE 8000
+
+/* Lets use 160 sample frames, just like GSM. */
+#define FRAME_SIZE 160
+#define PERIOD_FRAMES 80 /* 80 Frames, at 2 bytes each */
+
+/* When you set the frame size, you have to come up with
+ the right buffer format as well. */
+/* 5 64-byte frames = one frame */
+#define BUFFER_FMT ((buffersize * 10) << 16) | (0x0006);
+
+/* Don't switch between read/write modes faster than every 300 ms */
+#define MIN_SWITCH_TIME 600
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
+#else
+static snd_pcm_format_t format = SND_PCM_FORMAT_S16_BE;
+#endif
+
+/* static int block = O_NONBLOCK; */
+static char indevname[50] = ALSA_INDEV;
+static char outdevname[50] = ALSA_OUTDEV;
+
+static int silencesuppression = 0;
+static int silencethreshold = 1000;
+
+AST_MUTEX_DEFINE_STATIC(alsalock);
+
+static const char tdesc[] = "ALSA Console Channel Driver";
+static const char config[] = "alsa.conf";
+
+static char context[AST_MAX_CONTEXT] = "default";
+static char language[MAX_LANGUAGE] = "";
+static char exten[AST_MAX_EXTENSION] = "s";
+static char mohinterpret[MAX_MUSICCLASS];
+
+static int hookstate = 0;
+
+static struct chan_alsa_pvt {
+ /* We only have one ALSA structure -- near sighted perhaps, but it
+ keeps this driver as simple as possible -- as it should be. */
+ struct ast_channel *owner;
+ char exten[AST_MAX_EXTENSION];
+ char context[AST_MAX_CONTEXT];
+ snd_pcm_t *icard, *ocard;
+
+} alsa;
+
+/* Number of buffers... Each is FRAMESIZE/8 ms long. For example
+ with 160 sample frames, and a buffer size of 3, we have a 60ms buffer,
+ usually plenty. */
+
+#define MAX_BUFFER_SIZE 100
+
+/* File descriptors for sound device */
+static int readdev = -1;
+static int writedev = -1;
+
+static int autoanswer = 1;
+
+static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause);
+static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration);
+static int alsa_text(struct ast_channel *c, const char *text);
+static int alsa_hangup(struct ast_channel *c);
+static int alsa_answer(struct ast_channel *c);
+static struct ast_frame *alsa_read(struct ast_channel *chan);
+static int alsa_call(struct ast_channel *c, char *dest, int timeout);
+static int alsa_write(struct ast_channel *chan, struct ast_frame *f);
+static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen);
+static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+static const struct ast_channel_tech alsa_tech = {
+ .type = "Console",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_SLINEAR,
+ .requester = alsa_request,
+ .send_digit_end = alsa_digit,
+ .send_text = alsa_text,
+ .hangup = alsa_hangup,
+ .answer = alsa_answer,
+ .read = alsa_read,
+ .call = alsa_call,
+ .write = alsa_write,
+ .indicate = alsa_indicate,
+ .fixup = alsa_fixup,
+};
+
+static snd_pcm_t *alsa_card_init(char *dev, snd_pcm_stream_t stream)
+{
+ int err;
+ int direction;
+ snd_pcm_t *handle = NULL;
+ snd_pcm_hw_params_t *hwparams = NULL;
+ snd_pcm_sw_params_t *swparams = NULL;
+ struct pollfd pfd;
+ snd_pcm_uframes_t period_size = PERIOD_FRAMES * 4;
+ snd_pcm_uframes_t buffer_size = 0;
+ unsigned int rate = DESIRED_RATE;
+ snd_pcm_uframes_t start_threshold, stop_threshold;
+
+ err = snd_pcm_open(&handle, dev, stream, O_NONBLOCK);
+ if (err < 0) {
+ ast_log(LOG_ERROR, "snd_pcm_open failed: %s\n", snd_strerror(err));
+ return NULL;
+ } else {
+ ast_debug(1, "Opening device %s in %s mode\n", dev, (stream == SND_PCM_STREAM_CAPTURE) ? "read" : "write");
+ }
+
+ hwparams = alloca(snd_pcm_hw_params_sizeof());
+ memset(hwparams, 0, snd_pcm_hw_params_sizeof());
+ snd_pcm_hw_params_any(handle, hwparams);
+
+ err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (err < 0)
+ ast_log(LOG_ERROR, "set_access failed: %s\n", snd_strerror(err));
+
+ err = snd_pcm_hw_params_set_format(handle, hwparams, format);
+ if (err < 0)
+ ast_log(LOG_ERROR, "set_format failed: %s\n", snd_strerror(err));
+
+ err = snd_pcm_hw_params_set_channels(handle, hwparams, 1);
+ if (err < 0)
+ ast_log(LOG_ERROR, "set_channels failed: %s\n", snd_strerror(err));
+
+ direction = 0;
+ err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, &direction);
+ if (rate != DESIRED_RATE)
+ ast_log(LOG_WARNING, "Rate not correct, requested %d, got %d\n", DESIRED_RATE, rate);
+
+ direction = 0;
+ err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &direction);
+ if (err < 0)
+ ast_log(LOG_ERROR, "period_size(%ld frames) is bad: %s\n", period_size, snd_strerror(err));
+ else {
+ ast_debug(1, "Period size is %d\n", err);
+ }
+
+ buffer_size = 4096 * 2; /* period_size * 16; */
+ err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size);
+ if (err < 0)
+ ast_log(LOG_WARNING, "Problem setting buffer size of %ld: %s\n", buffer_size, snd_strerror(err));
+ else {
+ ast_debug(1, "Buffer size is set to %d frames\n", err);
+ }
+
+ err = snd_pcm_hw_params(handle, hwparams);
+ if (err < 0)
+ ast_log(LOG_ERROR, "Couldn't set the new hw params: %s\n", snd_strerror(err));
+
+ swparams = alloca(snd_pcm_sw_params_sizeof());
+ memset(swparams, 0, snd_pcm_sw_params_sizeof());
+ snd_pcm_sw_params_current(handle, swparams);
+
+ if (stream == SND_PCM_STREAM_PLAYBACK)
+ start_threshold = period_size;
+ else
+ start_threshold = 1;
+
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
+ if (err < 0)
+ ast_log(LOG_ERROR, "start threshold: %s\n", snd_strerror(err));
+
+ if (stream == SND_PCM_STREAM_PLAYBACK)
+ stop_threshold = buffer_size;
+ else
+ stop_threshold = buffer_size;
+
+ err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
+ if (err < 0)
+ ast_log(LOG_ERROR, "stop threshold: %s\n", snd_strerror(err));
+
+ err = snd_pcm_sw_params(handle, swparams);
+ if (err < 0)
+ ast_log(LOG_ERROR, "sw_params: %s\n", snd_strerror(err));
+
+ err = snd_pcm_poll_descriptors_count(handle);
+ if (err <= 0)
+ ast_log(LOG_ERROR, "Unable to get a poll descriptors count, error is %s\n", snd_strerror(err));
+ if (err != 1) {
+ ast_debug(1, "Can't handle more than one device\n");
+ }
+
+ snd_pcm_poll_descriptors(handle, &pfd, err);
+ ast_debug(1, "Acquired fd %d from the poll descriptor\n", pfd.fd);
+
+ if (stream == SND_PCM_STREAM_CAPTURE)
+ readdev = pfd.fd;
+ else
+ writedev = pfd.fd;
+
+ return handle;
+}
+
+static int soundcard_init(void)
+{
+ alsa.icard = alsa_card_init(indevname, SND_PCM_STREAM_CAPTURE);
+ alsa.ocard = alsa_card_init(outdevname, SND_PCM_STREAM_PLAYBACK);
+
+ if (!alsa.icard || !alsa.ocard) {
+ ast_log(LOG_ERROR, "Problem opening ALSA I/O devices\n");
+ return -1;
+ }
+
+ return readdev;
+}
+
+static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration)
+{
+ ast_mutex_lock(&alsalock);
+ ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
+ digit, duration);
+ ast_mutex_unlock(&alsalock);
+
+ return 0;
+}
+
+static int alsa_text(struct ast_channel *c, const char *text)
+{
+ ast_mutex_lock(&alsalock);
+ ast_verbose(" << Console Received text %s >> \n", text);
+ ast_mutex_unlock(&alsalock);
+
+ return 0;
+}
+
+static void grab_owner(void)
+{
+ while (alsa.owner && ast_channel_trylock(alsa.owner)) {
+ ast_mutex_unlock(&alsalock);
+ usleep(1);
+ ast_mutex_lock(&alsalock);
+ }
+}
+
+static int alsa_call(struct ast_channel *c, char *dest, int timeout)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL };
+
+ ast_mutex_lock(&alsalock);
+ ast_verbose(" << Call placed to '%s' on console >> \n", dest);
+ if (autoanswer) {
+ ast_verbose(" << Auto-answered >> \n");
+ grab_owner();
+ if (alsa.owner) {
+ f.subclass = AST_CONTROL_ANSWER;
+ ast_queue_frame(alsa.owner, &f);
+ ast_channel_unlock(alsa.owner);
+ }
+ } else {
+ ast_verbose(" << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
+ grab_owner();
+ if (alsa.owner) {
+ f.subclass = AST_CONTROL_RINGING;
+ ast_queue_frame(alsa.owner, &f);
+ ast_channel_unlock(alsa.owner);
+ ast_indicate(alsa.owner, AST_CONTROL_RINGING);
+ }
+ }
+ snd_pcm_prepare(alsa.icard);
+ snd_pcm_start(alsa.icard);
+ ast_mutex_unlock(&alsalock);
+
+ return 0;
+}
+
+static int alsa_answer(struct ast_channel *c)
+{
+ ast_mutex_lock(&alsalock);
+ ast_verbose(" << Console call has been answered >> \n");
+ ast_setstate(c, AST_STATE_UP);
+ snd_pcm_prepare(alsa.icard);
+ snd_pcm_start(alsa.icard);
+ ast_mutex_unlock(&alsalock);
+
+ return 0;
+}
+
+static int alsa_hangup(struct ast_channel *c)
+{
+ ast_mutex_lock(&alsalock);
+ c->tech_pvt = NULL;
+ alsa.owner = NULL;
+ ast_verbose(" << Hangup on console >> \n");
+ ast_module_unref(ast_module_info->self);
+ hookstate = 0;
+ snd_pcm_drop(alsa.icard);
+ ast_mutex_unlock(&alsalock);
+
+ return 0;
+}
+
+static int alsa_write(struct ast_channel *chan, struct ast_frame *f)
+{
+ static char sizbuf[8000];
+ static int sizpos = 0;
+ int len = sizpos;
+ int pos;
+ int res = 0;
+ /* size_t frames = 0; */
+ snd_pcm_state_t state;
+
+ ast_mutex_lock(&alsalock);
+
+ /* We have to digest the frame in 160-byte portions */
+ if (f->datalen > sizeof(sizbuf) - sizpos) {
+ ast_log(LOG_WARNING, "Frame too large\n");
+ res = -1;
+ } else {
+ memcpy(sizbuf + sizpos, f->data, f->datalen);
+ len += f->datalen;
+ pos = 0;
+ state = snd_pcm_state(alsa.ocard);
+ if (state == SND_PCM_STATE_XRUN)
+ snd_pcm_prepare(alsa.ocard);
+ res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2);
+ if (res == -EPIPE) {
+#if DEBUG
+ ast_debug(1, "XRUN write\n");
+#endif
+ snd_pcm_prepare(alsa.ocard);
+ res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2);
+ if (res != len / 2) {
+ ast_log(LOG_ERROR, "Write error: %s\n", snd_strerror(res));
+ res = -1;
+ } else if (res < 0) {
+ ast_log(LOG_ERROR, "Write error %s\n", snd_strerror(res));
+ res = -1;
+ }
+ } else {
+ if (res == -ESTRPIPE)
+ ast_log(LOG_ERROR, "You've got some big problems\n");
+ else if (res < 0)
+ ast_log(LOG_NOTICE, "Error %d on write\n", res);
+ }
+ }
+ ast_mutex_unlock(&alsalock);
+
+ return res >= 0 ? 0 : res;
+}
+
+
+static struct ast_frame *alsa_read(struct ast_channel *chan)
+{
+ static struct ast_frame f;
+ static short __buf[FRAME_SIZE + AST_FRIENDLY_OFFSET / 2];
+ short *buf;
+ static int readpos = 0;
+ static int left = FRAME_SIZE;
+ snd_pcm_state_t state;
+ int r = 0;
+ int off = 0;
+
+ ast_mutex_lock(&alsalock);
+ f.frametype = AST_FRAME_NULL;
+ f.subclass = 0;
+ f.samples = 0;
+ f.datalen = 0;
+ f.data = NULL;
+ f.offset = 0;
+ f.src = "Console";
+ f.mallocd = 0;
+ f.delivery.tv_sec = 0;
+ f.delivery.tv_usec = 0;
+
+ state = snd_pcm_state(alsa.icard);
+ if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING)) {
+ snd_pcm_prepare(alsa.icard);
+ }
+
+ buf = __buf + AST_FRIENDLY_OFFSET / 2;
+
+ r = snd_pcm_readi(alsa.icard, buf + readpos, left);
+ if (r == -EPIPE) {
+#if DEBUG
+ ast_log(LOG_ERROR, "XRUN read\n");
+#endif
+ snd_pcm_prepare(alsa.icard);
+ } else if (r == -ESTRPIPE) {
+ ast_log(LOG_ERROR, "-ESTRPIPE\n");
+ snd_pcm_prepare(alsa.icard);
+ } else if (r < 0) {
+ ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r));
+ } else if (r >= 0) {
+ off -= r;
+ }
+ /* Update positions */
+ readpos += r;
+ left -= r;
+
+ if (readpos >= FRAME_SIZE) {
+ /* A real frame */
+ readpos = 0;
+ left = FRAME_SIZE;
+ if (chan->_state != AST_STATE_UP) {
+ /* Don't transmit unless it's up */
+ ast_mutex_unlock(&alsalock);
+ return &f;
+ }
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.samples = FRAME_SIZE;
+ f.datalen = FRAME_SIZE * 2;
+ f.data = buf;
+ f.offset = AST_FRIENDLY_OFFSET;
+ f.src = "Console";
+ f.mallocd = 0;
+
+ }
+ ast_mutex_unlock(&alsalock);
+
+ return &f;
+}
+
+static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct chan_alsa_pvt *p = newchan->tech_pvt;
+
+ ast_mutex_lock(&alsalock);
+ p->owner = newchan;
+ ast_mutex_unlock(&alsalock);
+
+ return 0;
+}
+
+static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
+{
+ int res = 0;
+
+ ast_mutex_lock(&alsalock);
+
+ switch (cond) {
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_RINGING:
+ case -1:
+ res = -1; /* Ask for inband indications */
+ break;
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_VIDUPDATE:
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verbose(" << Console Has Been Placed on Hold >> \n");
+ ast_moh_start(chan, data, mohinterpret);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verbose(" << Console Has Been Retrieved from Hold >> \n");
+ ast_moh_stop(chan);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, chan->name);
+ res = -1;
+ }
+
+ ast_mutex_unlock(&alsalock);
+
+ return res;
+}
+
+static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
+{
+ struct ast_channel *tmp = NULL;
+
+ if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "ALSA/%s", indevname)))
+ return NULL;
+
+ tmp->tech = &alsa_tech;
+ ast_channel_set_fd(tmp, 0, readdev);
+ tmp->nativeformats = AST_FORMAT_SLINEAR;
+ tmp->readformat = AST_FORMAT_SLINEAR;
+ tmp->writeformat = AST_FORMAT_SLINEAR;
+ tmp->tech_pvt = p;
+ if (!ast_strlen_zero(p->context))
+ ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
+ if (!ast_strlen_zero(p->exten))
+ ast_copy_string(tmp->exten, p->exten, sizeof(tmp->exten));
+ if (!ast_strlen_zero(language))
+ ast_string_field_set(tmp, language, language);
+ p->owner = tmp;
+ ast_module_ref(ast_module_info->self);
+ ast_jb_configure(tmp, &global_jbconf);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ tmp = NULL;
+ }
+ }
+
+ return tmp;
+}
+
+static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat = format;
+ struct ast_channel *tmp = NULL;
+
+ if (!(format &= AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat);
+ return NULL;
+ }
+
+ ast_mutex_lock(&alsalock);
+
+ if (alsa.owner) {
+ ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n");
+ *cause = AST_CAUSE_BUSY;
+ } else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN))) {
+ ast_log(LOG_WARNING, "Unable to create new ALSA channel\n");
+ }
+
+ ast_mutex_unlock(&alsalock);
+
+ return tmp;
+}
+
+static char *autoanswer_complete(const char *line, const char *word, int pos, int state)
+{
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+ switch (state) {
+ case 0:
+ if (!ast_strlen_zero(word) && !strncasecmp(word, "on", MIN(strlen(word), 2)))
+ return ast_strdup("on");
+ case 1:
+ if (!ast_strlen_zero(word) && !strncasecmp(word, "off", MIN(strlen(word), 3)))
+ return ast_strdup("off");
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static char *console_autoanswer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console autoanswer";
+ e->usage =
+ "Usage: console autoanswer [on|off]\n"
+ " Enables or disables autoanswer feature. If used without\n"
+ " argument, displays the current on/off status of autoanswer.\n"
+ " The default value of autoanswer is in 'alsa.conf'.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return autoanswer_complete(a->line, a->word, a->pos, a->n);
+ }
+
+ if ((a->argc != 2) && (a->argc != 3))
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&alsalock);
+ if (a->argc == 2) {
+ ast_cli(a->fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
+ } else {
+ if (!strcasecmp(a->argv[2], "on"))
+ autoanswer = -1;
+ else if (!strcasecmp(a->argv[2], "off"))
+ autoanswer = 0;
+ else
+ res = CLI_SHOWUSAGE;
+ }
+ ast_mutex_unlock(&alsalock);
+
+ return res;
+}
+
+static char *console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console answer";
+ e->usage =
+ "Usage: console answer\n"
+ " Answers an incoming call on the console (ALSA) channel.\n";
+
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&alsalock);
+
+ if (!alsa.owner) {
+ ast_cli(a->fd, "No one is calling us\n");
+ res = CLI_FAILURE;
+ } else {
+ hookstate = 1;
+ grab_owner();
+ if (alsa.owner) {
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
+
+ ast_queue_frame(alsa.owner, &f);
+ ast_channel_unlock(alsa.owner);
+ }
+ }
+
+ snd_pcm_prepare(alsa.icard);
+ snd_pcm_start(alsa.icard);
+
+ ast_mutex_unlock(&alsalock);
+
+ return res;
+}
+
+static char *console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int tmparg = 3;
+ char *res = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console send text";
+ e->usage =
+ "Usage: console send text <message>\n"
+ " Sends a text message for display on the remote terminal.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 3)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&alsalock);
+
+ if (!alsa.owner) {
+ ast_cli(a->fd, "No channel active\n");
+ res = CLI_FAILURE;
+ } else {
+ struct ast_frame f = { AST_FRAME_TEXT, 0 };
+ char text2send[256] = "";
+
+ while (tmparg < a->argc) {
+ strncat(text2send, a->argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1);
+ strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1);
+ }
+
+ text2send[strlen(text2send) - 1] = '\n';
+ f.data = text2send;
+ f.datalen = strlen(text2send) + 1;
+ grab_owner();
+ if (alsa.owner) {
+ ast_queue_frame(alsa.owner, &f);
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_ANSWER;
+ f.data = NULL;
+ f.datalen = 0;
+ ast_queue_frame(alsa.owner, &f);
+ ast_channel_unlock(alsa.owner);
+ }
+ }
+
+ ast_mutex_unlock(&alsalock);
+
+ return res;
+}
+
+static char *console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console hangup";
+ e->usage =
+ "Usage: console hangup\n"
+ " Hangs up any call currently placed on the console.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&alsalock);
+
+ if (!alsa.owner && !hookstate) {
+ ast_cli(a->fd, "No call to hangup\n");
+ res = CLI_FAILURE;
+ } else {
+ hookstate = 0;
+ grab_owner();
+ if (alsa.owner) {
+ ast_queue_hangup(alsa.owner);
+ ast_channel_unlock(alsa.owner);
+ }
+ }
+
+ ast_mutex_unlock(&alsalock);
+
+ return res;
+}
+
+static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char tmp[256], *tmp2;
+ char *mye, *myc;
+ char *d;
+ char *res = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console dial";
+ e->usage =
+ "Usage: console dial [extension[@context]]\n"
+ " Dials a given extension (and context if specified)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc != 2) && (a->argc != 3))
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&alsalock);
+
+ if (alsa.owner) {
+ if (a->argc == 3) {
+ if (alsa.owner) {
+ for (d = a->argv[2]; *d; d++) {
+ struct ast_frame f = { .frametype = AST_FRAME_DTMF, .subclass = *d };
+
+ ast_queue_frame(alsa.owner, &f);
+ }
+ }
+ } else {
+ ast_cli(a->fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
+ res = CLI_FAILURE;
+ }
+ } else {
+ mye = exten;
+ myc = context;
+ if (a->argc == 3) {
+ char *stringp = NULL;
+
+ ast_copy_string(tmp, a->argv[2], sizeof(tmp));
+ stringp = tmp;
+ strsep(&stringp, "@");
+ tmp2 = strsep(&stringp, "@");
+ if (!ast_strlen_zero(tmp))
+ mye = tmp;
+ if (!ast_strlen_zero(tmp2))
+ myc = tmp2;
+ }
+ if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
+ ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
+ ast_copy_string(alsa.context, myc, sizeof(alsa.context));
+ hookstate = 1;
+ alsa_new(&alsa, AST_STATE_RINGING);
+ } else
+ ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
+ }
+
+ ast_mutex_unlock(&alsalock);
+
+ return res;
+}
+
+static struct ast_cli_entry cli_alsa[] = {
+ AST_CLI_DEFINE(console_answer, "Answer an incoming console call"),
+ AST_CLI_DEFINE(console_hangup, "Hangup a call on the console"),
+ AST_CLI_DEFINE(console_dial, "Dial an extension on the console"),
+ AST_CLI_DEFINE(console_sendtext, "Send text to the remote device"),
+ AST_CLI_DEFINE(console_autoanswer, "Sets/displays autoanswer"),
+};
+
+static int load_module(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { 0 };
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ strcpy(mohinterpret, "default");
+
+ if (!(cfg = ast_config_load(config, config_flags)))
+ return AST_MODULE_LOAD_DECLINE;
+
+ v = ast_variable_browse(cfg, "general");
+ for (; v; v = v->next) {
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+ continue;
+
+ if (!strcasecmp(v->name, "autoanswer"))
+ autoanswer = ast_true(v->value);
+ else if (!strcasecmp(v->name, "silencesuppression"))
+ silencesuppression = ast_true(v->value);
+ else if (!strcasecmp(v->name, "silencethreshold"))
+ silencethreshold = atoi(v->value);
+ else if (!strcasecmp(v->name, "context"))
+ ast_copy_string(context, v->value, sizeof(context));
+ else if (!strcasecmp(v->name, "language"))
+ ast_copy_string(language, v->value, sizeof(language));
+ else if (!strcasecmp(v->name, "extension"))
+ ast_copy_string(exten, v->value, sizeof(exten));
+ else if (!strcasecmp(v->name, "input_device"))
+ ast_copy_string(indevname, v->value, sizeof(indevname));
+ else if (!strcasecmp(v->name, "output_device"))
+ ast_copy_string(outdevname, v->value, sizeof(outdevname));
+ else if (!strcasecmp(v->name, "mohinterpret"))
+ ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
+ }
+ ast_config_destroy(cfg);
+
+ if (soundcard_init() < 0) {
+ ast_verb(2, "No sound card detected -- console channel will be unavailable\n");
+ ast_verb(2, "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (ast_channel_register(&alsa_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Console'\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_channel_unregister(&alsa_tech);
+ ast_cli_unregister_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
+
+ if (alsa.icard)
+ snd_pcm_close(alsa.icard);
+ if (alsa.ocard)
+ snd_pcm_close(alsa.ocard);
+ if (alsa.owner)
+ ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD);
+ if (alsa.owner)
+ return -1;
+
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ALSA Console Channel Driver");
diff --git a/trunk/channels/chan_console.c b/trunk/channels/chan_console.c
new file mode 100644
index 000000000..58f456c39
--- /dev/null
+++ b/trunk/channels/chan_console.c
@@ -0,0 +1,1103 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006 - 2007, Digium, Inc.
+ *
+ * Russell Bryant <russell@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 Cross-platform console channel driver
+ *
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \note Some of the code in this file came from chan_oss and chan_alsa.
+ * chan_oss, Mark Spencer <markster@digium.com>
+ * chan_oss, Luigi Rizzo
+ * chan_alsa, Matthew Fredrickson <creslin@digium.com>
+ *
+ * \ingroup channel_drivers
+ *
+ * \extref Portaudio http://www.portaudio.com/
+ *
+ * To install portaudio v19 from svn, check it out using the following command:
+ * - svn co https://www.portaudio.com/repos/portaudio/branches/v19-devel
+ *
+ * \note Since this works with any audio system that libportaudio supports,
+ * including ALSA and OSS, this may someday deprecate chan_alsa and chan_oss.
+ * However, before that can be done, it needs to *at least* have all of the
+ * features that these other channel drivers have. The features implemented
+ * in at least one of the other console channel drivers that are not yet
+ * implemented here are:
+ *
+ * - Multiple device support
+ * - with "active" CLI command
+ * - Set Auto-answer from the dialplan
+ * - transfer CLI command
+ * - boost CLI command and .conf option
+ * - console_video support
+ */
+
+/*** MODULEINFO
+ <depend>portaudio</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/signal.h> /* SIGURG */
+
+#include <portaudio.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/causes.h"
+#include "asterisk/cli.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/callerid.h"
+
+/*!
+ * \brief The sample rate to request from PortAudio
+ *
+ * \todo Make this optional. If this is only going to talk to 8 kHz endpoints,
+ * then it makes sense to use 8 kHz natively.
+ */
+#define SAMPLE_RATE 16000
+
+/*!
+ * \brief The number of samples to configure the portaudio stream for
+ *
+ * 320 samples (20 ms) is the most common frame size in Asterisk. So, the code
+ * in this module reads 320 sample frames from the portaudio stream and queues
+ * them up on the Asterisk channel. Frames of any size can be written to a
+ * portaudio stream, but the portaudio documentation does say that for high
+ * performance applications, the data should be written to Pa_WriteStream in
+ * the same size as what is used to initialize the stream.
+ */
+#define NUM_SAMPLES 320
+
+/*! \brief Mono Input */
+#define INPUT_CHANNELS 1
+
+/*! \brief Mono Output */
+#define OUTPUT_CHANNELS 1
+
+/*!
+ * \brief Maximum text message length
+ * \note This should be changed if there is a common definition somewhere
+ * that defines the maximum length of a text message.
+ */
+#define TEXT_SIZE 256
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/*! \brief Dance, Kirby, Dance! @{ */
+#define V_BEGIN " --- <(\"<) --- "
+#define V_END " --- (>\")> ---\n"
+/*! @} */
+
+static const char config_file[] = "console.conf";
+
+/*!
+ * \brief Console pvt structure
+ *
+ * Currently, this is a singleton object. However, multiple instances will be
+ * needed when this module is updated for multiple device support.
+ */
+static struct console_pvt {
+ AST_DECLARE_STRING_FIELDS(
+ /*! Name of the device */
+ AST_STRING_FIELD(name);
+ /*! Default context for outgoing calls */
+ AST_STRING_FIELD(context);
+ /*! Default extension for outgoing calls */
+ AST_STRING_FIELD(exten);
+ /*! Default CallerID number */
+ AST_STRING_FIELD(cid_num);
+ /*! Default CallerID name */
+ AST_STRING_FIELD(cid_name);
+ /*! Default MOH class to listen to, if:
+ * - No MOH class set on the channel
+ * - Peer channel putting this device on hold did not suggest a class */
+ AST_STRING_FIELD(mohinterpret);
+ /*! Default language */
+ AST_STRING_FIELD(language);
+ );
+ /*! Current channel for this device */
+ struct ast_channel *owner;
+ /*! Current PortAudio stream for this device */
+ PaStream *stream;
+ /*! A frame for preparing to queue on to the channel */
+ struct ast_frame fr;
+ /*! Running = 1, Not running = 0 */
+ unsigned int streamstate:1;
+ /*! On-hook = 0, Off-hook = 1 */
+ unsigned int hookstate:1;
+ /*! Unmuted = 0, Muted = 1 */
+ unsigned int muted:1;
+ /*! Automatically answer incoming calls */
+ unsigned int autoanswer:1;
+ /*! Ignore context in the console dial CLI command */
+ unsigned int overridecontext:1;
+ /*! Lock to protect data in this struct */
+ ast_mutex_t __lock;
+ /*! ID for the stream monitor thread */
+ pthread_t thread;
+} console_pvt = {
+ .__lock = AST_MUTEX_INIT_VALUE,
+ .thread = AST_PTHREADT_NULL,
+};
+
+/*!
+ * \brief Global jitterbuffer configuration
+ *
+ * \note Disabled by default.
+ */
+static struct ast_jb_conf default_jbconf = {
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+/*! Channel Technology Callbacks @{ */
+static struct ast_channel *console_request(const char *type, int format,
+ void *data, int *cause);
+static int console_digit_begin(struct ast_channel *c, char digit);
+static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration);
+static int console_text(struct ast_channel *c, const char *text);
+static int console_hangup(struct ast_channel *c);
+static int console_answer(struct ast_channel *c);
+static struct ast_frame *console_read(struct ast_channel *chan);
+static int console_call(struct ast_channel *c, char *dest, int timeout);
+static int console_write(struct ast_channel *chan, struct ast_frame *f);
+static int console_indicate(struct ast_channel *chan, int cond,
+ const void *data, size_t datalen);
+static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+/*! @} */
+
+/*!
+ * \brief Formats natively supported by this module.
+ */
+#define SUPPORTED_FORMATS ( AST_FORMAT_SLINEAR16 )
+
+static const struct ast_channel_tech console_tech = {
+ .type = "Console",
+ .description = "Console Channel Driver",
+ .capabilities = SUPPORTED_FORMATS,
+ .requester = console_request,
+ .send_digit_begin = console_digit_begin,
+ .send_digit_end = console_digit_end,
+ .send_text = console_text,
+ .hangup = console_hangup,
+ .answer = console_answer,
+ .read = console_read,
+ .call = console_call,
+ .write = console_write,
+ .indicate = console_indicate,
+ .fixup = console_fixup,
+};
+
+/*! \brief lock a console_pvt struct */
+#define console_pvt_lock(pvt) ast_mutex_lock(&(pvt)->__lock)
+
+/*! \brief unlock a console_pvt struct */
+#define console_pvt_unlock(pvt) ast_mutex_unlock(&(pvt)->__lock)
+
+/*!
+ * \brief Stream monitor thread
+ *
+ * \arg data A pointer to the console_pvt structure that contains the portaudio
+ * stream that needs to be monitored.
+ *
+ * This function runs in its own thread to monitor data coming in from a
+ * portaudio stream. When enough data is available, it is queued up to
+ * be read from the Asterisk channel.
+ */
+static void *stream_monitor(void *data)
+{
+ struct console_pvt *pvt = data;
+ char buf[NUM_SAMPLES * sizeof(int16_t)];
+ PaError res;
+ struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR16,
+ .src = "console_stream_monitor",
+ .data = buf,
+ .datalen = sizeof(buf),
+ .samples = sizeof(buf) / sizeof(int16_t),
+ };
+
+ for (;;) {
+ pthread_testcancel();
+ res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t));
+ pthread_testcancel();
+
+ if (res == paNoError)
+ ast_queue_frame(pvt->owner, &f);
+ }
+
+ return NULL;
+}
+
+static int start_stream(struct console_pvt *pvt)
+{
+ PaError res;
+ int ret_val = 0;
+
+ console_pvt_lock(pvt);
+
+ if (pvt->streamstate)
+ goto return_unlock;
+
+ pvt->streamstate = 1;
+ ast_debug(1, "Starting stream\n");
+
+ res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS,
+ paInt16, SAMPLE_RATE, NUM_SAMPLES, NULL, NULL);
+ if (res != paNoError) {
+ ast_log(LOG_WARNING, "Failed to open default audio device - (%d) %s\n",
+ res, Pa_GetErrorText(res));
+ ret_val = -1;
+ goto return_unlock;
+ }
+
+ res = Pa_StartStream(pvt->stream);
+ if (res != paNoError) {
+ ast_log(LOG_WARNING, "Failed to start stream - (%d) %s\n",
+ res, Pa_GetErrorText(res));
+ ret_val = -1;
+ goto return_unlock;
+ }
+
+ if (ast_pthread_create_background(&pvt->thread, NULL, stream_monitor, pvt)) {
+ ast_log(LOG_ERROR, "Failed to start stream monitor thread\n");
+ ret_val = -1;
+ }
+
+return_unlock:
+ console_pvt_unlock(pvt);
+
+ return ret_val;
+}
+
+static int stop_stream(struct console_pvt *pvt)
+{
+ if (!pvt->streamstate)
+ return 0;
+
+ pthread_cancel(pvt->thread);
+ pthread_kill(pvt->thread, SIGURG);
+ pthread_join(pvt->thread, NULL);
+
+ console_pvt_lock(pvt);
+ Pa_AbortStream(pvt->stream);
+ Pa_CloseStream(pvt->stream);
+ pvt->stream = NULL;
+ pvt->streamstate = 0;
+ console_pvt_unlock(pvt);
+
+ return 0;
+}
+
+/*!
+ * \note Called with the pvt struct locked
+ */
+static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state)
+{
+ struct ast_channel *chan;
+
+ if (!(chan = ast_channel_alloc(1, state, pvt->cid_num, pvt->cid_name, NULL,
+ ext, ctx, 0, "Console/%s", pvt->name))) {
+ return NULL;
+ }
+
+ chan->tech = &console_tech;
+ chan->nativeformats = AST_FORMAT_SLINEAR16;
+ chan->readformat = AST_FORMAT_SLINEAR16;
+ chan->writeformat = AST_FORMAT_SLINEAR16;
+ chan->tech_pvt = pvt;
+
+ pvt->owner = chan;
+
+ if (!ast_strlen_zero(pvt->language))
+ ast_string_field_set(chan, language, pvt->language);
+
+ ast_jb_configure(chan, &global_jbconf);
+
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(chan)) {
+ chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(chan);
+ chan = NULL;
+ } else
+ start_stream(pvt);
+ }
+
+ return chan;
+}
+
+static struct ast_channel *console_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat = format;
+ struct ast_channel *chan;
+ struct console_pvt *pvt = &console_pvt;
+
+ format &= SUPPORTED_FORMATS;
+ if (!format) {
+ ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%d'\n", oldformat);
+ return NULL;
+ }
+
+ if (pvt->owner) {
+ ast_log(LOG_NOTICE, "Console channel already active!\n");
+ *cause = AST_CAUSE_BUSY;
+ return NULL;
+ }
+
+ console_pvt_lock(pvt);
+ chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN);
+ console_pvt_unlock(pvt);
+
+ if (!chan)
+ ast_log(LOG_WARNING, "Unable to create new Console channel!\n");
+
+ return chan;
+}
+
+static int console_digit_begin(struct ast_channel *c, char digit)
+{
+ ast_verb(1, V_BEGIN "Console Received Beginning of Digit %c" V_END, digit);
+
+ return -1; /* non-zero to request inband audio */
+}
+
+static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+ ast_verb(1, V_BEGIN "Console Received End of Digit %c (duration %u)" V_END,
+ digit, duration);
+
+ return -1; /* non-zero to request inband audio */
+}
+
+static int console_text(struct ast_channel *c, const char *text)
+{
+ ast_verb(1, V_BEGIN "Console Received Text '%s'" V_END, text);
+
+ return 0;
+}
+
+static int console_hangup(struct ast_channel *c)
+{
+ struct console_pvt *pvt = &console_pvt;
+
+ ast_verb(1, V_BEGIN "Hangup on Console" V_END);
+
+ pvt->hookstate = 0;
+ c->tech_pvt = NULL;
+ pvt->owner = NULL;
+
+ stop_stream(pvt);
+
+ return 0;
+}
+
+static int console_answer(struct ast_channel *c)
+{
+ struct console_pvt *pvt = &console_pvt;
+
+ ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END);
+
+ ast_setstate(c, AST_STATE_UP);
+
+ return start_stream(pvt);
+}
+
+/*
+ * \brief Implementation of the ast_channel_tech read() callback
+ *
+ * Calling this function is harmless. However, if it does get called, it
+ * is an indication that something weird happened that really shouldn't
+ * have and is worth looking into.
+ *
+ * Why should this function not get called? Well, let me explain. There are
+ * a couple of ways to pass on audio that has come from this channel. The way
+ * that this channel driver uses is that once the audio is available, it is
+ * wrapped in an ast_frame and queued onto the channel using ast_queue_frame().
+ *
+ * The other method would be signalling to the core that there is audio waiting,
+ * and that it needs to call the channel's read() callback to get it. The way
+ * the channel gets signalled is that one or more file descriptors are placed
+ * in the fds array on the ast_channel which the core will poll() on. When the
+ * fd indicates that input is available, the read() callback is called. This
+ * is especially useful when there is a dedicated file descriptor where the
+ * audio is read from. An example would be the socket for an RTP stream.
+ */
+static struct ast_frame *console_read(struct ast_channel *chan)
+{
+ ast_debug(1, "I should not be called ...\n");
+
+ return &ast_null_frame;
+}
+
+static int console_call(struct ast_channel *c, char *dest, int timeout)
+{
+ struct ast_frame f = { 0, };
+ struct console_pvt *pvt = &console_pvt;
+
+ ast_verb(1, V_BEGIN "Call to device '%s' on console from '%s' <%s>" V_END,
+ dest, c->cid.cid_name, c->cid.cid_num);
+
+ console_pvt_lock(pvt);
+
+ if (pvt->autoanswer) {
+ pvt->hookstate = 1;
+ console_pvt_unlock(pvt);
+ ast_verb(1, V_BEGIN "Auto-answered" V_END);
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_ANSWER;
+ } else {
+ console_pvt_unlock(pvt);
+ ast_verb(1, V_BEGIN "Type 'console answer' to answer, or use the 'autoanswer' option "
+ "for future calls" V_END);
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_RINGING;
+ ast_indicate(c, AST_CONTROL_RINGING);
+ }
+
+ ast_queue_frame(c, &f);
+
+ return start_stream(pvt);
+}
+
+static int console_write(struct ast_channel *chan, struct ast_frame *f)
+{
+ struct console_pvt *pvt = &console_pvt;
+
+ Pa_WriteStream(pvt->stream, f->data, f->samples);
+
+ return 0;
+}
+
+static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
+{
+ struct console_pvt *pvt = chan->tech_pvt;
+ int res = 0;
+
+ switch (cond) {
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_RINGING:
+ case -1:
+ res = -1; /* Ask for inband indications */
+ break;
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_VIDUPDATE:
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verb(1, V_BEGIN "Console Has Been Placed on Hold" V_END);
+ ast_moh_start(chan, data, pvt->mohinterpret);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verb(1, V_BEGIN "Console Has Been Retrieved from Hold" V_END);
+ ast_moh_stop(chan);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n",
+ cond, chan->name);
+ /* The core will play inband indications for us if appropriate */
+ res = -1;
+ }
+
+ return res;
+}
+
+static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct console_pvt *pvt = &console_pvt;
+
+ pvt->owner = newchan;
+
+ return 0;
+}
+
+/*!
+ * split a string in extension-context, returns pointers to malloc'ed
+ * strings.
+ * If we do not have 'overridecontext' then the last @ is considered as
+ * a context separator, and the context is overridden.
+ * This is usually not very necessary as you can play with the dialplan,
+ * and it is nice not to need it because you have '@' in SIP addresses.
+ * Return value is the buffer address.
+ *
+ * \note came from chan_oss
+ */
+static char *ast_ext_ctx(struct console_pvt *pvt, const char *src, char **ext, char **ctx)
+{
+ if (ext == NULL || ctx == NULL)
+ return NULL; /* error */
+
+ *ext = *ctx = NULL;
+
+ if (src && *src != '\0')
+ *ext = ast_strdup(src);
+
+ if (*ext == NULL)
+ return NULL;
+
+ if (!pvt->overridecontext) {
+ /* parse from the right */
+ *ctx = strrchr(*ext, '@');
+ if (*ctx)
+ *(*ctx)++ = '\0';
+ }
+
+ return *ext;
+}
+
+static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd,
+ struct ast_cli_args *a)
+{
+ struct console_pvt *pvt = &console_pvt;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console set autoanswer [on|off]";
+ e->usage =
+ "Usage: console set autoanswer [on|off]\n"
+ " Enables or disables autoanswer feature. If used without\n"
+ " argument, displays the current on/off status of autoanswer.\n"
+ " The default value of autoanswer is in 'oss.conf'.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == e->args - 1) {
+ ast_cli(a->fd, "Auto answer is %s.\n", pvt->autoanswer ? "on" : "off");
+ return CLI_SUCCESS;
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ if (!pvt) {
+ ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n",
+ pvt->name);
+ return CLI_FAILURE;
+ }
+
+ if (!strcasecmp(a->argv[e->args-1], "on"))
+ pvt->autoanswer = 1;
+ else if (!strcasecmp(a->argv[e->args - 1], "off"))
+ pvt->autoanswer = 0;
+ else
+ return CLI_SHOWUSAGE;
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH };
+ struct console_pvt *pvt = &console_pvt;
+
+ if (cmd == CLI_INIT) {
+ e->command = "console flash";
+ e->usage =
+ "Usage: console flash\n"
+ " Flashes the call currently placed on the console.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ if (!pvt->owner) {
+ ast_cli(a->fd, "No call to flash\n");
+ return CLI_FAILURE;
+ }
+
+ pvt->hookstate = 0;
+
+ ast_queue_frame(pvt->owner, &f);
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *s = NULL;
+ const char *mye = NULL, *myc = NULL;
+ struct console_pvt *pvt = &console_pvt;
+
+ if (cmd == CLI_INIT) {
+ e->command = "console dial";
+ e->usage =
+ "Usage: console dial [extension[@context]]\n"
+ " Dials a given extension (and context if specified)\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc > e->args + 1)
+ return CLI_SHOWUSAGE;
+
+ if (pvt->owner) { /* already in a call */
+ int i;
+ struct ast_frame f = { AST_FRAME_DTMF, 0 };
+
+ if (a->argc == e->args) { /* argument is mandatory here */
+ ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n");
+ return CLI_FAILURE;
+ }
+ s = a->argv[e->args];
+ /* send the string one char at a time */
+ for (i = 0; i < strlen(s); i++) {
+ f.subclass = s[i];
+ ast_queue_frame(pvt->owner, &f);
+ }
+ return CLI_SUCCESS;
+ }
+
+ /* if we have an argument split it into extension and context */
+ if (a->argc == e->args + 1) {
+ char *ext = NULL, *con = NULL;
+ s = ast_ext_ctx(pvt, a->argv[e->args], &ext, &con);
+ ast_debug(1, "provided '%s', exten '%s' context '%s'\n",
+ a->argv[e->args], mye, myc);
+ mye = ext;
+ myc = con;
+ }
+
+ /* supply default values if needed */
+ if (ast_strlen_zero(mye))
+ mye = pvt->exten;
+ if (ast_strlen_zero(myc))
+ myc = pvt->context;
+
+ if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
+ console_pvt_lock(pvt);
+ pvt->hookstate = 1;
+ console_new(pvt, mye, myc, AST_STATE_RINGING);
+ console_pvt_unlock(pvt);
+ } else
+ ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
+
+ if (s)
+ free(s);
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct console_pvt *pvt = &console_pvt;
+
+ if (cmd == CLI_INIT) {
+ e->command = "console hangup";
+ e->usage =
+ "Usage: console hangup\n"
+ " Hangs up any call currently placed on the console.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ if (!pvt->owner && !pvt->hookstate) {
+ ast_cli(a->fd, "No call to hang up\n");
+ return CLI_FAILURE;
+ }
+
+ pvt->hookstate = 0;
+ if (pvt->owner)
+ ast_queue_hangup(pvt->owner);
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *s;
+ struct console_pvt *pvt = &console_pvt;
+
+ if (cmd == CLI_INIT) {
+ e->command = "console {mute|unmute}";
+ e->usage =
+ "Usage: console {mute|unmute}\n"
+ " Mute/unmute the microphone.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ s = a->argv[e->args-1];
+ if (!strcasecmp(s, "mute"))
+ pvt->muted = 1;
+ else if (!strcasecmp(s, "unmute"))
+ pvt->muted = 0;
+ else
+ return CLI_SHOWUSAGE;
+
+ ast_verb(1, V_BEGIN "The Console is now %s" V_END,
+ pvt->muted ? "Muted" : "Unmuted");
+
+ return CLI_SUCCESS;
+}
+
+static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ PaDeviceIndex index, num, def_input, def_output;
+
+ if (cmd == CLI_INIT) {
+ e->command = "console list devices";
+ e->usage =
+ "Usage: console list devices\n"
+ " List all available devices.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "\n"
+ "=============================================================\n"
+ "=== Available Devices =======================================\n"
+ "=============================================================\n"
+ "===\n");
+
+ num = Pa_GetDeviceCount();
+ if (!num) {
+ ast_cli(a->fd, "(None)\n");
+ return CLI_SUCCESS;
+ }
+
+ def_input = Pa_GetDefaultInputDevice();
+ def_output = Pa_GetDefaultOutputDevice();
+ for (index = 0; index < num; index++) {
+ const PaDeviceInfo *dev = Pa_GetDeviceInfo(index);
+ if (!dev)
+ continue;
+ ast_cli(a->fd, "=== ---------------------------------------------------------\n"
+ "=== Device Name: %s\n", dev->name);
+ if (dev->maxInputChannels)
+ ast_cli(a->fd, "=== ---> %sInput Device\n", (index == def_input) ? "Default " : "");
+ if (dev->maxOutputChannels)
+ ast_cli(a->fd, "=== ---> %sOutput Device\n", (index == def_output) ? "Default " : "");
+ ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
+ }
+
+ ast_cli(a->fd, "=============================================================\n\n");
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief answer command from the console
+ */
+static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
+ struct console_pvt *pvt = &console_pvt;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console answer";
+ e->usage =
+ "Usage: console answer\n"
+ " Answers an incoming call on the console channel.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL; /* no completion */
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ if (!pvt->owner) {
+ ast_cli(a->fd, "No one is calling us\n");
+ return CLI_FAILURE;
+ }
+
+ pvt->hookstate = 1;
+
+ ast_indicate(pvt->owner, -1);
+
+ ast_queue_frame(pvt->owner, &f);
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Console send text CLI command
+ *
+ * \note concatenate all arguments into a single string. argv is NULL-terminated
+ * so we can use it right away
+ */
+static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char buf[TEXT_SIZE];
+ struct console_pvt *pvt = &console_pvt;
+ struct ast_frame f = {
+ .frametype = AST_FRAME_TEXT,
+ .data = buf,
+ .src = "console_send_text",
+ };
+ int len;
+
+ if (cmd == CLI_INIT) {
+ e->command = "console send text";
+ e->usage =
+ "Usage: console send text <message>\n"
+ " Sends a text message for display on the remote terminal.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc < e->args + 1)
+ return CLI_SHOWUSAGE;
+
+ if (!pvt->owner) {
+ ast_cli(a->fd, "Not in a call\n");
+ return CLI_FAILURE;
+ }
+
+ ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
+ if (ast_strlen_zero(buf))
+ return CLI_SHOWUSAGE;
+
+ len = strlen(buf);
+ buf[len] = '\n';
+ f.datalen = len + 1;
+
+ ast_queue_frame(pvt->owner, &f);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_console[] = {
+ AST_CLI_DEFINE(cli_console_dial, "Dial an extension from the console"),
+ AST_CLI_DEFINE(cli_console_hangup, "Hangup a call on the console"),
+ AST_CLI_DEFINE(cli_console_mute, "Disable/Enable mic input"),
+ AST_CLI_DEFINE(cli_console_answer, "Answer an incoming console call"),
+ AST_CLI_DEFINE(cli_console_sendtext, "Send text to a connected party"),
+ AST_CLI_DEFINE(cli_console_flash, "Send a flash to the connected party"),
+ AST_CLI_DEFINE(cli_console_autoanswer, "Turn autoanswer on or off"),
+ AST_CLI_DEFINE(cli_list_devices, "List available devices"),
+};
+
+/*!
+ * \brief Set default values for a pvt struct
+ *
+ * \note This function expects the pvt lock to be held.
+ */
+static void set_pvt_defaults(struct console_pvt *pvt, int reload)
+{
+ if (!reload) {
+ /* This should be changed for multiple device support. Right now,
+ * there is no way to change the name of a device. The default
+ * input and output sound devices are the only ones supported. */
+ ast_string_field_set(pvt, name, "default");
+ }
+
+ ast_string_field_set(pvt, mohinterpret, "default");
+ ast_string_field_set(pvt, context, "default");
+ ast_string_field_set(pvt, exten, "s");
+ ast_string_field_set(pvt, language, "");
+ ast_string_field_set(pvt, cid_num, "");
+ ast_string_field_set(pvt, cid_name, "");
+
+ pvt->overridecontext = 0;
+ pvt->autoanswer = 0;
+}
+
+static void store_callerid(struct console_pvt *pvt, const char *value)
+{
+ char cid_name[256];
+ char cid_num[256];
+
+ ast_callerid_split(value, cid_name, sizeof(cid_name),
+ cid_num, sizeof(cid_num));
+
+ ast_string_field_set(pvt, cid_name, cid_name);
+ ast_string_field_set(pvt, cid_num, cid_num);
+}
+
+/*!
+ * \brief Store a configuration parameter in a pvt struct
+ *
+ * \note This function expects the pvt lock to be held.
+ */
+static void store_config_core(struct console_pvt *pvt, const char *var, const char *value)
+{
+ if (!ast_jb_read_conf(&global_jbconf, var, value))
+ return;
+
+ CV_START(var, value);
+
+ CV_STRFIELD("context", pvt, context);
+ CV_STRFIELD("extension", pvt, exten);
+ CV_STRFIELD("mohinterpret", pvt, mohinterpret);
+ CV_STRFIELD("language", pvt, language);
+ CV_F("callerid", store_callerid(pvt, value));
+ CV_BOOL("overridecontext", pvt->overridecontext);
+ CV_BOOL("autoanswer", pvt->autoanswer);
+
+ ast_log(LOG_WARNING, "Unknown option '%s'\n", var);
+
+ CV_END;
+}
+
+/*!
+ * \brief Load the configuration
+ * \param reload if this was called due to a reload
+ * \retval 0 succcess
+ * \retval -1 failure
+ */
+static int load_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct console_pvt *pvt = &console_pvt;
+ struct ast_flags config_flags = { 0 };
+ int res = -1;
+
+ /* default values */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf));
+
+ console_pvt_lock(pvt);
+
+ set_pvt_defaults(pvt, reload);
+
+ if (!(cfg = ast_config_load(config_file, config_flags))) {
+ ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
+ goto return_unlock;
+ }
+
+ for (v = ast_variable_browse(cfg, "general"); v; v = v->next)
+ store_config_core(pvt, v->name, v->value);
+
+ ast_config_destroy(cfg);
+
+ res = 0;
+
+return_unlock:
+ console_pvt_unlock(pvt);
+ return res;
+}
+
+static int init_pvt(struct console_pvt *pvt)
+{
+ if (ast_string_field_init(pvt, 32))
+ return -1;
+
+ if (ast_mutex_init(&pvt->__lock)) {
+ ast_log(LOG_ERROR, "Failed to initialize mutex\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void destroy_pvt(struct console_pvt *pvt)
+{
+ ast_string_field_free_memory(pvt);
+
+ ast_mutex_destroy(&pvt->__lock);
+}
+
+static int unload_module(void)
+{
+ struct console_pvt *pvt = &console_pvt;
+
+ if (pvt->hookstate)
+ stop_stream(pvt);
+
+ Pa_Terminate();
+
+ ast_channel_unregister(&console_tech);
+ ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
+
+ destroy_pvt(pvt);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ PaError res;
+ struct console_pvt *pvt = &console_pvt;
+
+ if (init_pvt(pvt))
+ goto return_error;
+
+ if (load_config(0))
+ goto return_error;
+
+ res = Pa_Initialize();
+ if (res != paNoError) {
+ ast_log(LOG_WARNING, "Failed to initialize audio system - (%d) %s\n",
+ res, Pa_GetErrorText(res));
+ goto return_error_pa_init;
+ }
+
+ if (ast_channel_register(&console_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel type 'Console'\n");
+ goto return_error_chan_reg;
+ }
+
+ if (ast_cli_register_multiple(cli_console, ARRAY_LEN(cli_console)))
+ goto return_error_cli_reg;
+
+ return AST_MODULE_LOAD_SUCCESS;
+
+return_error_cli_reg:
+ ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
+return_error_chan_reg:
+ ast_channel_unregister(&console_tech);
+return_error_pa_init:
+ Pa_Terminate();
+return_error:
+ destroy_pvt(pvt);
+
+ return AST_MODULE_LOAD_DECLINE;
+}
+
+static int reload(void)
+{
+ return load_config(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Console Channel Driver",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/trunk/channels/chan_features.c b/trunk/channels/chan_features.c
new file mode 100644
index 000000000..65146f463
--- /dev/null
+++ b/trunk/channels/chan_features.c
@@ -0,0 +1,570 @@
+/*
+ * 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 feature Proxy Channel
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note *** Experimental code ****
+ *
+ * \ingroup channel_drivers
+ */
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <fcntl.h>
+#include <sys/signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/manager.h"
+#include "asterisk/stringfields.h"
+
+static const char tdesc[] = "Feature Proxy Channel Driver";
+
+#define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
+
+struct feature_sub {
+ struct ast_channel *owner;
+ int inthreeway;
+ int pfd;
+ int timingfdbackup;
+ int alertpipebackup[2];
+};
+
+struct feature_pvt {
+ ast_mutex_t lock; /* Channel private lock */
+ char tech[AST_MAX_EXTENSION]; /* Technology to abstract */
+ char dest[AST_MAX_EXTENSION]; /* Destination to abstract */
+ struct ast_channel *subchan;
+ struct feature_sub subs[3]; /* Subs */
+ struct ast_channel *owner; /* Current Master Channel */
+ AST_LIST_ENTRY(feature_pvt) list; /* Next entity */
+};
+
+static AST_LIST_HEAD_STATIC(features, feature_pvt);
+
+#define SUB_REAL 0 /* Active call */
+#define SUB_CALLWAIT 1 /* Call-Waiting call on hold */
+#define SUB_THREEWAY 2 /* Three-way call */
+
+static struct ast_channel *features_request(const char *type, int format, void *data, int *cause);
+static int features_digit_begin(struct ast_channel *ast, char digit);
+static int features_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int features_call(struct ast_channel *ast, char *dest, int timeout);
+static int features_hangup(struct ast_channel *ast);
+static int features_answer(struct ast_channel *ast);
+static struct ast_frame *features_read(struct ast_channel *ast);
+static int features_write(struct ast_channel *ast, struct ast_frame *f);
+static int features_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int features_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+static const struct ast_channel_tech features_tech = {
+ .type = "Feature",
+ .description = tdesc,
+ .capabilities = -1,
+ .requester = features_request,
+ .send_digit_begin = features_digit_begin,
+ .send_digit_end = features_digit_end,
+ .call = features_call,
+ .hangup = features_hangup,
+ .answer = features_answer,
+ .read = features_read,
+ .write = features_write,
+ .exception = features_read,
+ .indicate = features_indicate,
+ .fixup = features_fixup,
+};
+
+static inline void init_sub(struct feature_sub *sub)
+{
+ sub->inthreeway = 0;
+ sub->pfd = -1;
+ sub->timingfdbackup = -1;
+ sub->alertpipebackup[0] = sub->alertpipebackup[1] = -1;
+}
+
+static inline int indexof(struct feature_pvt *p, struct ast_channel *owner, int nullok)
+{
+ int x;
+ if (!owner) {
+ ast_log(LOG_WARNING, "indexof called on NULL owner??\n");
+ return -1;
+ }
+ for (x=0; x<3; x++) {
+ if (owner == p->subs[x].owner)
+ return x;
+ }
+ return -1;
+}
+
+#if 0
+static void wakeup_sub(struct feature_pvt *p, int a)
+{
+ struct ast_frame null = { AST_FRAME_NULL, };
+ for (;;) {
+ if (p->subs[a].owner) {
+ if (ast_mutex_trylock(&p->subs[a].owner->lock)) {
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ } else {
+ ast_queue_frame(p->subs[a].owner, &null);
+ ast_mutex_unlock(&p->subs[a].owner->lock);
+ break;
+ }
+ } else
+ break;
+ }
+}
+#endif
+
+static void restore_channel(struct feature_pvt *p, int index)
+{
+ /* Restore timing/alertpipe */
+ p->subs[index].owner->timingfd = p->subs[index].timingfdbackup;
+ p->subs[index].owner->alertpipe[0] = p->subs[index].alertpipebackup[0];
+ p->subs[index].owner->alertpipe[1] = p->subs[index].alertpipebackup[1];
+ ast_channel_set_fd(p->subs[index].owner, AST_ALERT_FD, p->subs[index].alertpipebackup[0]);
+ ast_channel_set_fd(p->subs[index].owner, AST_TIMING_FD, p->subs[index].timingfdbackup);
+}
+
+static void update_features(struct feature_pvt *p, int index)
+{
+ int x;
+ if (p->subs[index].owner) {
+ for (x=0; x<AST_MAX_FDS; x++) {
+ if (index)
+ ast_channel_set_fd(p->subs[index].owner, x, -1);
+ else
+ ast_channel_set_fd(p->subs[index].owner, x, p->subchan->fds[x]);
+ }
+ if (!index) {
+ /* Copy timings from master channel */
+ p->subs[index].owner->timingfd = p->subchan->timingfd;
+ p->subs[index].owner->alertpipe[0] = p->subchan->alertpipe[0];
+ p->subs[index].owner->alertpipe[1] = p->subchan->alertpipe[1];
+ if (p->subs[index].owner->nativeformats != p->subchan->readformat) {
+ p->subs[index].owner->nativeformats = p->subchan->readformat;
+ if (p->subs[index].owner->readformat)
+ ast_set_read_format(p->subs[index].owner, p->subs[index].owner->readformat);
+ if (p->subs[index].owner->writeformat)
+ ast_set_write_format(p->subs[index].owner, p->subs[index].owner->writeformat);
+ }
+ } else{
+ restore_channel(p, index);
+ }
+ }
+}
+
+#if 0
+static void swap_subs(struct feature_pvt *p, int a, int b)
+{
+ int tinthreeway;
+ struct ast_channel *towner;
+
+ ast_debug(1, "Swapping %d and %d\n", a, b);
+
+ towner = p->subs[a].owner;
+ tinthreeway = p->subs[a].inthreeway;
+
+ p->subs[a].owner = p->subs[b].owner;
+ p->subs[a].inthreeway = p->subs[b].inthreeway;
+
+ p->subs[b].owner = towner;
+ p->subs[b].inthreeway = tinthreeway;
+ update_features(p,a);
+ update_features(p,b);
+ wakeup_sub(p, a);
+ wakeup_sub(p, b);
+}
+#endif
+
+static int features_answer(struct ast_channel *ast)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int x;
+
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (!x && p->subchan)
+ res = ast_answer(p->subchan);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static struct ast_frame *features_read(struct ast_channel *ast)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ struct ast_frame *f;
+ int x;
+
+ f = &ast_null_frame;
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (!x && p->subchan) {
+ update_features(p, x);
+ f = ast_read(p->subchan);
+ }
+ ast_mutex_unlock(&p->lock);
+ return f;
+}
+
+static int features_write(struct ast_channel *ast, struct ast_frame *f)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int x;
+
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (!x && p->subchan)
+ res = ast_write(p->subchan, f);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int features_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct feature_pvt *p = newchan->tech_pvt;
+ int x;
+
+ ast_mutex_lock(&p->lock);
+ if (p->owner == oldchan)
+ p->owner = newchan;
+ for (x = 0; x < 3; x++) {
+ if (p->subs[x].owner == oldchan)
+ p->subs[x].owner = newchan;
+ }
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int features_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int x;
+
+ /* Queue up a frame representing the indication as a control frame */
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (!x && p->subchan)
+ res = ast_indicate(p->subchan, condition);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int features_digit_begin(struct ast_channel *ast, char digit)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int x;
+
+ /* Queue up a frame representing the indication as a control frame */
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (!x && p->subchan)
+ res = ast_senddigit_begin(p->subchan, digit);
+ ast_mutex_unlock(&p->lock);
+
+ return res;
+}
+
+static int features_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int x;
+
+ /* Queue up a frame representing the indication as a control frame */
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (!x && p->subchan)
+ res = ast_senddigit_end(p->subchan, digit, duration);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int features_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int x;
+ char *dest2;
+
+ dest2 = strchr(dest, '/');
+ if (dest2) {
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (!x && p->subchan) {
+ p->subchan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
+ p->subchan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
+ p->subchan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
+ p->subchan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
+
+ p->subchan->cid.cid_pres = p->owner->cid.cid_pres;
+ ast_string_field_set(p->subchan, language, p->owner->language);
+ ast_string_field_set(p->subchan, accountcode, p->owner->accountcode);
+ p->subchan->cdrflags = p->owner->cdrflags;
+ res = ast_call(p->subchan, dest2, timeout);
+ update_features(p, x);
+ } else
+ ast_log(LOG_NOTICE, "Uhm yah, not quite there with the call waiting...\n");
+ ast_mutex_unlock(&p->lock);
+ }
+ return res;
+}
+
+static int features_hangup(struct ast_channel *ast)
+{
+ struct feature_pvt *p = ast->tech_pvt;
+ int x;
+
+ ast_mutex_lock(&p->lock);
+ x = indexof(p, ast, 0);
+ if (x > -1) {
+ restore_channel(p, x);
+ p->subs[x].owner = NULL;
+ /* XXX Re-arrange, unconference, etc XXX */
+ }
+ ast->tech_pvt = NULL;
+
+ if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) {
+ ast_mutex_unlock(&p->lock);
+ /* Remove from list */
+ AST_LIST_LOCK(&features);
+ AST_LIST_REMOVE(&features, p, list);
+ AST_LIST_UNLOCK(&features);
+ ast_mutex_lock(&p->lock);
+ /* And destroy */
+ if (p->subchan)
+ ast_hangup(p->subchan);
+ ast_mutex_unlock(&p->lock);
+ ast_mutex_destroy(&p->lock);
+ ast_free(p);
+ return 0;
+ }
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static struct feature_pvt *features_alloc(char *data, int format)
+{
+ struct feature_pvt *tmp;
+ char *dest=NULL;
+ char *tech;
+ int x;
+ int status;
+ struct ast_channel *chan;
+
+ tech = ast_strdupa(data);
+ if (tech) {
+ dest = strchr(tech, '/');
+ if (dest) {
+ *dest = '\0';
+ dest++;
+ }
+ }
+ if (!tech || !dest) {
+ ast_log(LOG_NOTICE, "Format for feature channel is Feature/Tech/Dest ('%s' not valid)!\n",
+ data);
+ return NULL;
+ }
+ AST_LIST_LOCK(&features);
+ AST_LIST_TRAVERSE(&features, tmp, list) {
+ if (!strcasecmp(tmp->tech, tech) && !strcmp(tmp->dest, dest))
+ break;
+ }
+ AST_LIST_UNLOCK(&features);
+ if (!tmp) {
+ chan = ast_request(tech, format, dest, &status);
+ if (!chan) {
+ ast_log(LOG_NOTICE, "Unable to allocate subchannel '%s/%s'\n", tech, dest);
+ return NULL;
+ }
+ tmp = ast_calloc(1, sizeof(*tmp));
+ if (tmp) {
+ for (x=0;x<3;x++)
+ init_sub(tmp->subs + x);
+ ast_mutex_init(&tmp->lock);
+ ast_copy_string(tmp->tech, tech, sizeof(tmp->tech));
+ ast_copy_string(tmp->dest, dest, sizeof(tmp->dest));
+ tmp->subchan = chan;
+ AST_LIST_LOCK(&features);
+ AST_LIST_INSERT_HEAD(&features, tmp, list);
+ AST_LIST_UNLOCK(&features);
+ }
+ }
+ return tmp;
+}
+
+static struct ast_channel *features_new(struct feature_pvt *p, int state, int index)
+{
+ struct ast_channel *tmp;
+ int x,y;
+ char *b2 = 0;
+ if (!p->subchan) {
+ ast_log(LOG_WARNING, "Called upon channel with no subchan:(\n");
+ return NULL;
+ }
+ if (p->subs[index].owner) {
+ ast_log(LOG_WARNING, "Called to put index %d already there!\n", index);
+ return NULL;
+ }
+ /* figure out what you want the name to be */
+ for (x=1;x<4;x++) {
+ if (b2)
+ ast_free(b2);
+ asprintf(&b2, "%s/%s-%d", p->tech, p->dest, x);
+ for (y=0;y<3;y++) {
+ if (y == index)
+ continue;
+ if (p->subs[y].owner && !strcasecmp(p->subs[y].owner->name, b2))
+ break;
+ }
+ if (y >= 3)
+ break;
+ }
+ tmp = ast_channel_alloc(0, state, 0,0, "", "", "", 0, "Feature/%s", b2);
+ /* free up the name, it was copied into the channel name */
+ if (b2)
+ ast_free(b2);
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ return NULL;
+ }
+ tmp->tech = &features_tech;
+ tmp->writeformat = p->subchan->writeformat;
+ tmp->rawwriteformat = p->subchan->rawwriteformat;
+ tmp->readformat = p->subchan->readformat;
+ tmp->rawreadformat = p->subchan->rawreadformat;
+ tmp->nativeformats = p->subchan->readformat;
+ tmp->tech_pvt = p;
+ p->subs[index].owner = tmp;
+ if (!p->owner)
+ p->owner = tmp;
+ ast_module_ref(ast_module_info->self);
+ return tmp;
+}
+
+
+static struct ast_channel *features_request(const char *type, int format, void *data, int *cause)
+{
+ struct feature_pvt *p;
+ struct ast_channel *chan = NULL;
+
+ p = features_alloc(data, format);
+ if (p && !p->subs[SUB_REAL].owner)
+ chan = features_new(p, AST_STATE_DOWN, SUB_REAL);
+ if (chan)
+ update_features(p,SUB_REAL);
+ return chan;
+}
+
+static char *features_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct feature_pvt *p;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "feature show channels";
+ e->usage =
+ "Usage: feature show channels\n"
+ " Provides summary information on feature channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ if (AST_LIST_EMPTY(&features)) {
+ ast_cli(a->fd, "No feature channels in use\n");
+ return CLI_SUCCESS;
+ }
+
+ AST_LIST_LOCK(&features);
+ AST_LIST_TRAVERSE(&features, p, list) {
+ ast_mutex_lock(&p->lock);
+ ast_cli(a->fd, "%s -- %s/%s\n", p->owner ? p->owner->name : "<unowned>", p->tech, p->dest);
+ ast_mutex_unlock(&p->lock);
+ }
+ AST_LIST_UNLOCK(&features);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_features[] = {
+ AST_CLI_DEFINE(features_show, "List status of feature channels"),
+};
+
+static int load_module(void)
+{
+ /* Make sure we can register our sip channel type */
+ if (ast_channel_register(&features_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Feature'\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ struct feature_pvt *p;
+
+ /* First, take us out of the channel loop */
+ ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
+ ast_channel_unregister(&features_tech);
+
+ if (!AST_LIST_LOCK(&features))
+ return -1;
+ /* Hangup all interfaces if they have an owner */
+ while ((p = AST_LIST_REMOVE_HEAD(&features, list))) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ ast_free(p);
+ }
+ AST_LIST_UNLOCK(&features);
+
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Feature Proxy Channel");
+
diff --git a/trunk/channels/chan_gtalk.c b/trunk/channels/chan_gtalk.c
new file mode 100644
index 000000000..05e7de704
--- /dev/null
+++ b/trunk/channels/chan_gtalk.c
@@ -0,0 +1,1956 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matt O'Gorman <mogorman@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
+ *
+ * \author Matt O'Gorman <mogorman@digium.com>
+ *
+ * \brief Gtalk Channel Driver, until google/libjingle works with jingle spec
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>iksemel</depend>
+ <depend>res_jabber</depend>
+ <use>openssl</use>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/signal.h>
+#include <iksemel.h>
+#include <pthread.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/manager.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/astobj.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/jabber.h"
+
+#define GOOGLE_CONFIG "gtalk.conf"
+
+#define GOOGLE_NS "http://www.google.com/session"
+
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+enum gtalk_protocol {
+ AJI_PROTOCOL_UDP = 1,
+ AJI_PROTOCOL_SSLTCP = 2,
+};
+
+enum gtalk_connect_type {
+ AJI_CONNECT_STUN = 1,
+ AJI_CONNECT_LOCAL = 2,
+ AJI_CONNECT_RELAY = 3,
+};
+
+struct gtalk_pvt {
+ ast_mutex_t lock; /*!< Channel private lock */
+ time_t laststun;
+ struct gtalk *parent; /*!< Parent client */
+ char sid[100];
+ char us[AJI_MAX_JIDLEN];
+ char them[AJI_MAX_JIDLEN];
+ char ring[10]; /*!< Message ID of ring */
+ iksrule *ringrule; /*!< Rule for matching RING request */
+ int initiator; /*!< If we're the initiator */
+ int alreadygone;
+ int capability;
+ struct ast_codec_pref prefs;
+ struct gtalk_candidate *theircandidates;
+ struct gtalk_candidate *ourcandidates;
+ char cid_num[80]; /*!< Caller ID num */
+ char cid_name[80]; /*!< Caller ID name */
+ char exten[80]; /*!< Called extension */
+ struct ast_channel *owner; /*!< Master Channel */
+ struct ast_rtp *rtp; /*!< RTP audio session */
+ struct ast_rtp *vrtp; /*!< RTP video session */
+ int jointcapability; /*!< Supported capability at both ends (codecs ) */
+ int peercapability;
+ struct gtalk_pvt *next; /* Next entity */
+};
+
+struct gtalk_candidate {
+ char name[100];
+ enum gtalk_protocol protocol;
+ double preference;
+ char username[100];
+ char password[100];
+ enum gtalk_connect_type type;
+ char network[6];
+ int generation;
+ char ip[16];
+ int port;
+ int receipt;
+ struct gtalk_candidate *next;
+};
+
+struct gtalk {
+ ASTOBJ_COMPONENTS(struct gtalk);
+ struct aji_client *connection;
+ struct aji_buddy *buddy;
+ struct gtalk_pvt *p;
+ struct ast_codec_pref prefs;
+ int amaflags; /*!< AMA Flags */
+ char user[AJI_MAX_JIDLEN];
+ char context[AST_MAX_CONTEXT];
+ char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
+ int capability;
+ ast_group_t callgroup; /*!< Call group */
+ ast_group_t pickupgroup; /*!< Pickup group */
+ int callingpres; /*!< Calling presentation */
+ int allowguest;
+ char language[MAX_LANGUAGE]; /*!< Default language for prompts */
+ char musicclass[MAX_MUSICCLASS]; /*!< Music on Hold class */
+};
+
+struct gtalk_container {
+ ASTOBJ_CONTAINER_COMPONENTS(struct gtalk);
+};
+
+static const char desc[] = "Gtalk Channel";
+
+static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263;
+
+AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pvt's) */
+
+/* Forward declarations */
+static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause);
+static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration);
+static int gtalk_digit_begin(struct ast_channel *ast, char digit);
+static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int gtalk_call(struct ast_channel *ast, char *dest, int timeout);
+static int gtalk_hangup(struct ast_channel *ast);
+static int gtalk_answer(struct ast_channel *ast);
+static int gtalk_newcall(struct gtalk *client, ikspak *pak);
+static struct ast_frame *gtalk_read(struct ast_channel *ast);
+static int gtalk_write(struct ast_channel *ast, struct ast_frame *f);
+static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
+static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid);
+static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+/*----- RTP interface functions */
+static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp,
+ struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active);
+static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static int gtalk_get_codec(struct ast_channel *chan);
+
+/*! \brief PBX interface structure for channel registration */
+static const struct ast_channel_tech gtalk_tech = {
+ .type = "Gtalk",
+ .description = "Gtalk Channel Driver",
+ .capabilities = AST_FORMAT_AUDIO_MASK,
+ .requester = gtalk_request,
+ .send_digit_begin = gtalk_digit_begin,
+ .send_digit_end = gtalk_digit_end,
+ .bridge = ast_rtp_bridge,
+ .call = gtalk_call,
+ .hangup = gtalk_hangup,
+ .answer = gtalk_answer,
+ .read = gtalk_read,
+ .write = gtalk_write,
+ .exception = gtalk_read,
+ .indicate = gtalk_indicate,
+ .fixup = gtalk_fixup,
+ .send_html = gtalk_sendhtml,
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
+};
+
+static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */
+
+static struct sched_context *sched; /*!< The scheduling context */
+static struct io_context *io; /*!< The IO context */
+static struct in_addr __ourip;
+
+/*! \brief RTP driver interface */
+static struct ast_rtp_protocol gtalk_rtp = {
+ type: "Gtalk",
+ get_rtp_info: gtalk_get_rtp_peer,
+ set_rtp_peer: gtalk_set_rtp_peer,
+ get_codec: gtalk_get_codec,
+};
+
+static struct ast_cli_entry gtalk_cli[] = {
+ AST_CLI_DEFINE(gtalk_do_reload, "Reload GoogleTalk configuration"),
+ AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"),
+};
+
+static char externip[16];
+
+static struct gtalk_container gtalk_list;
+
+static void gtalk_member_destroy(struct gtalk *obj)
+{
+ ast_free(obj);
+}
+
+static struct gtalk *find_gtalk(char *name, char *connection)
+{
+ struct gtalk *gtalk = NULL;
+ char *domain = NULL , *s = NULL;
+
+ if (strchr(connection, '@')) {
+ s = ast_strdupa(connection);
+ domain = strsep(&s, "@");
+ ast_verbose("OOOOH domain = %s\n", domain);
+ }
+ gtalk = ASTOBJ_CONTAINER_FIND(&gtalk_list, name);
+ if (!gtalk && strchr(name, '@'))
+ gtalk = ASTOBJ_CONTAINER_FIND_FULL(&gtalk_list, name, user,,, strcasecmp);
+
+ if (!gtalk) { /* guest call */
+ ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ if (!strcasecmp(iterator->name, "guest")) {
+ if (!strcasecmp(iterator->connection->jid->partial, connection)) {
+ gtalk = iterator;
+ } else if (!strcasecmp(iterator->connection->name, connection)) {
+ gtalk = iterator;
+ } else if (iterator->connection->component && !strcasecmp(iterator->connection->user,domain)) {
+ gtalk = iterator;
+ }
+ }
+ ASTOBJ_UNLOCK(iterator);
+
+ if (gtalk)
+ break;
+ });
+
+ }
+ return gtalk;
+}
+
+
+static int add_codec_to_answer(const struct gtalk_pvt *p, int codec, iks *dcodecs)
+{
+ char *format = ast_getformatname(codec);
+
+ if (!strcasecmp("ulaw", format)) {
+ iks *payload_eg711u, *payload_pcmu;
+ payload_pcmu = iks_new("payload-type");
+ payload_eg711u = iks_new("payload-type");
+
+ if(!payload_eg711u || !payload_pcmu) {
+ if(payload_pcmu)
+ iks_delete(payload_pcmu);
+ if(payload_eg711u)
+ iks_delete(payload_eg711u);
+ ast_log(LOG_WARNING,"Failed to allocate iks node");
+ return -1;
+ }
+ iks_insert_attrib(payload_pcmu, "id", "0");
+ iks_insert_attrib(payload_pcmu, "name", "PCMU");
+ iks_insert_attrib(payload_pcmu, "clockrate","8000");
+ iks_insert_attrib(payload_pcmu, "bitrate","64000");
+ iks_insert_attrib(payload_eg711u, "id", "100");
+ iks_insert_attrib(payload_eg711u, "name", "EG711U");
+ iks_insert_attrib(payload_eg711u, "clockrate","8000");
+ iks_insert_attrib(payload_eg711u, "bitrate","64000");
+ iks_insert_node(dcodecs, payload_pcmu);
+ iks_insert_node(dcodecs, payload_eg711u);
+ }
+ if (!strcasecmp("alaw", format)) {
+ iks *payload_eg711a, *payload_pcma;
+ payload_pcma = iks_new("payload-type");
+ payload_eg711a = iks_new("payload-type");
+ if(!payload_eg711a || !payload_pcma) {
+ if(payload_eg711a)
+ iks_delete(payload_eg711a);
+ if(payload_pcma)
+ iks_delete(payload_pcma);
+ ast_log(LOG_WARNING,"Failed to allocate iks node");
+ return -1;
+ }
+ iks_insert_attrib(payload_pcma, "id", "8");
+ iks_insert_attrib(payload_pcma, "name", "PCMA");
+ iks_insert_attrib(payload_pcma, "clockrate","8000");
+ iks_insert_attrib(payload_pcma, "bitrate","64000");
+ payload_eg711a = iks_new("payload-type");
+ iks_insert_attrib(payload_eg711a, "id", "101");
+ iks_insert_attrib(payload_eg711a, "name", "EG711A");
+ iks_insert_attrib(payload_eg711a, "clockrate","8000");
+ iks_insert_attrib(payload_eg711a, "bitrate","64000");
+ iks_insert_node(dcodecs, payload_pcma);
+ iks_insert_node(dcodecs, payload_eg711a);
+ }
+ if (!strcasecmp("ilbc", format)) {
+ iks *payload_ilbc = iks_new("payload-type");
+ if(!payload_ilbc) {
+ ast_log(LOG_WARNING,"Failed to allocate iks node");
+ return -1;
+ }
+ iks_insert_attrib(payload_ilbc, "id", "97");
+ iks_insert_attrib(payload_ilbc, "name", "iLBC");
+ iks_insert_attrib(payload_ilbc, "clockrate","8000");
+ iks_insert_attrib(payload_ilbc, "bitrate","13300");
+ iks_insert_node(dcodecs, payload_ilbc);
+ }
+ if (!strcasecmp("g723", format)) {
+ iks *payload_g723 = iks_new("payload-type");
+ if(!payload_g723) {
+ ast_log(LOG_WARNING,"Failed to allocate iks node");
+ return -1;
+ }
+ iks_insert_attrib(payload_g723, "id", "4");
+ iks_insert_attrib(payload_g723, "name", "G723");
+ iks_insert_attrib(payload_g723, "clockrate","8000");
+ iks_insert_attrib(payload_g723, "bitrate","6300");
+ iks_insert_node(dcodecs, payload_g723);
+ }
+ if (!strcasecmp("speex", format)) {
+ iks *payload_speex = iks_new("payload-type");
+ if(!payload_speex) {
+ ast_log(LOG_WARNING,"Failed to allocate iks node");
+ return -1;
+ }
+ iks_insert_attrib(payload_speex, "id", "110");
+ iks_insert_attrib(payload_speex, "name", "speex");
+ iks_insert_attrib(payload_speex, "clockrate","8000");
+ iks_insert_attrib(payload_speex, "bitrate","11000");
+ iks_insert_node(dcodecs, payload_speex);
+ }
+ ast_rtp_lookup_code(p->rtp, 1, codec);
+ return 0;
+}
+
+static int gtalk_invite(struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator)
+{
+ struct gtalk *client = p->parent;
+ iks *iq, *gtalk, *dcodecs, *payload_telephone, *transport;
+ int x;
+ int pref_codec = 0;
+ int alreadysent = 0;
+
+
+ iq = iks_new("iq");
+ gtalk = iks_new("session");
+ dcodecs = iks_new("description");
+ transport = iks_new("transport");
+ payload_telephone = iks_new("payload-type");
+ if (!(iq && gtalk && dcodecs && transport && payload_telephone)){
+ if(iq)
+ iks_delete(iq);
+ if(gtalk)
+ iks_delete(gtalk);
+ if(dcodecs)
+ iks_delete(dcodecs);
+ if(transport)
+ iks_delete(transport);
+ if(payload_telephone)
+ iks_delete(payload_telephone);
+
+ ast_log(LOG_ERROR, "Could not allocate iksemel nodes\n");
+ return 0;
+ }
+ iks_insert_attrib(dcodecs, "xmlns", "http://www.google.com/session/phone");
+ iks_insert_attrib(dcodecs, "xml:lang", "en");
+
+ for (x = 0; x < 32; x++) {
+ if (!(pref_codec = ast_codec_pref_index(&client->prefs, x)))
+ break;
+ if (!(client->capability & pref_codec))
+ continue;
+ if (alreadysent & pref_codec)
+ continue;
+ add_codec_to_answer(p, pref_codec, dcodecs);
+ alreadysent |= pref_codec;
+ }
+
+ iks_insert_attrib(payload_telephone, "id", "106");
+ iks_insert_attrib(payload_telephone, "name", "telephone-event");
+ iks_insert_attrib(payload_telephone, "clockrate", "8000");
+
+ iks_insert_attrib(transport,"xmlns","http://www.google.com/transport/p2p");
+
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "to", to);
+ iks_insert_attrib(iq, "from", from);
+ iks_insert_attrib(iq, "id", client->connection->mid);
+ ast_aji_increment_mid(client->connection->mid);
+
+ iks_insert_attrib(gtalk, "xmlns", "http://www.google.com/session");
+ iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept");
+ iks_insert_attrib(gtalk, "initiator", initiator ? from : to);
+ iks_insert_attrib(gtalk, "id", sid);
+ iks_insert_node(iq, gtalk);
+ iks_insert_node(gtalk, dcodecs);
+ iks_insert_node(gtalk, transport);
+ iks_insert_node(dcodecs, payload_telephone);
+
+ ast_aji_send(client->connection, iq);
+ iks_delete(payload_telephone);
+ iks_delete(transport);
+ iks_delete(dcodecs);
+ iks_delete(gtalk);
+ iks_delete(iq);
+ return 1;
+}
+
+static int gtalk_invite_response(struct gtalk_pvt *p, char *to , char *from, char *sid, int initiator)
+{
+ iks *iq, *session, *transport;
+ iq = iks_new("iq");
+ session = iks_new("session");
+ transport = iks_new("transport");
+ if(!(iq && session && transport)) {
+ if(iq)
+ iks_delete(iq);
+ if(session)
+ iks_delete(session);
+ if(transport)
+ iks_delete(transport);
+ ast_log(LOG_ERROR, " Unable to allocate IKS node\n");
+ return -1;
+ }
+ iks_insert_attrib(iq, "from", from);
+ iks_insert_attrib(iq, "to", to);
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "id",p->parent->connection->mid);
+ ast_aji_increment_mid(p->parent->connection->mid);
+ iks_insert_attrib(session, "type", "transport-accept");
+ iks_insert_attrib(session, "id", sid);
+ iks_insert_attrib(session, "initiator", initiator ? from : to);
+ iks_insert_attrib(session, "xmlns", "http://www.google.com/session");
+ iks_insert_attrib(transport, "xmlns", "http://www.google.com/transport/p2p");
+ iks_insert_node(iq,session);
+ iks_insert_node(session,transport);
+ ast_aji_send(p->parent->connection, iq);
+ iks_delete(transport);
+ iks_delete(session);
+ iks_delete(iq);
+ return 1;
+
+}
+
+static int gtalk_ringing_ack(void *data, ikspak *pak)
+{
+ struct gtalk_pvt *p = data;
+
+ if (p->ringrule)
+ iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
+ p->ringrule = NULL;
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_RINGING);
+ return IKS_FILTER_EAT;
+}
+
+static int gtalk_answer(struct ast_channel *ast)
+{
+ struct gtalk_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ ast_debug(1, "Answer!\n");
+ ast_mutex_lock(&p->lock);
+ gtalk_invite(p, p->them, p->us,p->sid, 0);
+ manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
+ ast->name, "GTALK", p->sid);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct gtalk_pvt *p = chan->tech_pvt;
+ enum ast_rtp_get_result res = AST_RTP_GET_FAILED;
+
+ if (!p)
+ return res;
+
+ ast_mutex_lock(&p->lock);
+ if (p->rtp){
+ *rtp = p->rtp;
+ res = AST_RTP_TRY_PARTIAL;
+ }
+ ast_mutex_unlock(&p->lock);
+
+ return res;
+}
+
+static int gtalk_get_codec(struct ast_channel *chan)
+{
+ struct gtalk_pvt *p = chan->tech_pvt;
+ return p->peercapability;
+}
+
+static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+{
+ struct gtalk_pvt *p;
+
+ p = chan->tech_pvt;
+ if (!p)
+ return -1;
+ ast_mutex_lock(&p->lock);
+
+/* if (rtp)
+ ast_rtp_get_peer(rtp, &p->redirip);
+ else
+ memset(&p->redirip, 0, sizeof(p->redirip));
+ p->redircodecs = codecs; */
+
+ /* Reset lastrtprx timer */
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2)
+{
+ iks *response = NULL, *error = NULL, *reason = NULL;
+ int res = -1;
+
+ response = iks_new("iq");
+ if (response) {
+ iks_insert_attrib(response, "type", "result");
+ iks_insert_attrib(response, "from", from);
+ iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
+ iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));
+ if (reasonstr) {
+ error = iks_new("error");
+ if (error) {
+ iks_insert_attrib(error, "type", "cancel");
+ reason = iks_new(reasonstr);
+ if (reason)
+ iks_insert_node(error, reason);
+ iks_insert_node(response, error);
+ }
+ }
+ ast_aji_send(client->connection, response);
+ if (reason)
+ iks_delete(reason);
+ if (error)
+ iks_delete(error);
+ iks_delete(response);
+ res = 0;
+ }
+ return res;
+}
+
+static int gtalk_is_answered(struct gtalk *client, ikspak *pak)
+{
+ struct gtalk_pvt *tmp;
+ char *from;
+ ast_debug(1, "The client is %s\n", client->name);
+ /* Make sure our new call doesn't exist yet */
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid))
+ break;
+ }
+
+ from = iks_find_attrib(pak->x, "to");
+ if(!from)
+ from = client->connection->jid->full;
+
+ if (tmp) {
+ if (tmp->owner)
+ ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
+ } else
+ ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
+ gtalk_response(client, from, pak, NULL, NULL);
+ return 1;
+}
+
+static int gtalk_is_accepted(struct gtalk *client, ikspak *pak)
+{
+ struct gtalk_pvt *tmp;
+ char *from;
+
+ ast_log(LOG_DEBUG, "The client is %s\n", client->name);
+ /* find corresponding call */
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid))
+ break;
+ }
+
+ from = iks_find_attrib(pak->x, "to");
+ if(!from)
+ from = client->connection->jid->full;
+
+ if (!tmp)
+ ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
+
+ /* answer 'iq' packet to let the remote peer know that we're alive */
+ gtalk_response(client, from, pak, NULL, NULL);
+ return 1;
+}
+
+static int gtalk_handle_dtmf(struct gtalk *client, ikspak *pak)
+{
+ struct gtalk_pvt *tmp;
+ iks *dtmfnode = NULL, *dtmfchild = NULL;
+ char *dtmf;
+ char *from;
+ /* Make sure our new call doesn't exist yet */
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid))
+ break;
+ }
+ from = iks_find_attrib(pak->x, "to");
+ if(!from)
+ from = client->connection->jid->full;
+
+
+ if (tmp) {
+ if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) {
+ gtalk_response(client, from, pak,
+ "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
+ "unsupported-dtmf-method xmlns='http://jabber.org/protocol/gtalk/info/dtmf#errors'");
+ return -1;
+ }
+ if ((dtmfnode = iks_find(pak->x, "dtmf"))) {
+ if((dtmf = iks_find_attrib(dtmfnode, "code"))) {
+ if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) {
+ struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass);
+ } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
+ struct ast_frame f = {AST_FRAME_DTMF_END, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass);
+ } else if(iks_find_attrib(pak->x, "dtmf")) { /* 250 millasecond default */
+ struct ast_frame f = {AST_FRAME_DTMF, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass);
+ }
+ }
+ } else if ((dtmfnode = iks_find_with_attrib(pak->x, "gtalk", "action", "session-info"))) {
+ if((dtmfchild = iks_find(dtmfnode, "dtmf"))) {
+ if((dtmf = iks_find_attrib(dtmfchild, "code"))) {
+ if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) {
+ struct ast_frame f = {AST_FRAME_DTMF_END, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass);
+ } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
+ struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass);
+ }
+ }
+ }
+ }
+ gtalk_response(client, from, pak, NULL, NULL);
+ return 1;
+ } else
+ ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
+
+ gtalk_response(client, from, pak, NULL, NULL);
+ return 1;
+}
+
+static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak)
+{
+ struct gtalk_pvt *tmp;
+ char *from;
+
+ ast_debug(1, "The client is %s\n", client->name);
+ /* Make sure our new call doesn't exist yet */
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid))
+ break;
+ }
+ from = iks_find_attrib(pak->x, "to");
+ if(!from)
+ from = client->connection->jid->full;
+
+ if (tmp) {
+ tmp->alreadygone = 1;
+ if (tmp->owner)
+ ast_queue_hangup(tmp->owner);
+ } else
+ ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
+ gtalk_response(client, from, pak, NULL, NULL);
+ return 1;
+}
+
+static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to)
+{
+ struct gtalk_candidate *tmp;
+ struct aji_client *c = client->connection;
+ struct gtalk_candidate *ours1 = NULL, *ours2 = NULL;
+ struct sockaddr_in sin;
+ struct sockaddr_in dest;
+ struct in_addr us;
+ iks *iq, *gtalk, *candidate, *transport;
+ char user[17], pass[17], preference[5], port[7];
+
+
+ iq = iks_new("iq");
+ gtalk = iks_new("session");
+ candidate = iks_new("candidate");
+ transport = iks_new("transport");
+ if (!iq || !gtalk || !candidate || !transport) {
+ ast_log(LOG_ERROR, "Memory allocation error\n");
+ goto safeout;
+ }
+ ours1 = ast_calloc(1, sizeof(*ours1));
+ ours2 = ast_calloc(1, sizeof(*ours2));
+ if (!ours1 || !ours2)
+ goto safeout;
+
+ iks_insert_attrib(transport, "xmlns","http://www.google.com/transport/p2p");
+ iks_insert_node(iq, gtalk);
+ iks_insert_node(gtalk,transport);
+ iks_insert_node(transport, candidate);
+
+ for (; p; p = p->next) {
+ if (!strcasecmp(p->sid, sid))
+ break;
+ }
+
+ if (!p) {
+ ast_log(LOG_NOTICE, "No matching gtalk session - SID %s!\n", sid);
+ goto safeout;
+ }
+
+ ast_rtp_get_us(p->rtp, &sin);
+ ast_find_ourip(&us, bindaddr);
+
+ /* Setup our gtalk candidates */
+ ast_copy_string(ours1->name, "rtp", sizeof(ours1->name));
+ ours1->port = ntohs(sin.sin_port);
+ ours1->preference = 1;
+ snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
+ snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(ours1->username, user, sizeof(ours1->username));
+ ast_copy_string(ours1->password, pass, sizeof(ours1->password));
+ ast_copy_string(ours1->ip, ast_inet_ntoa(us), sizeof(ours1->ip));
+ ours1->protocol = AJI_PROTOCOL_UDP;
+ ours1->type = AJI_CONNECT_LOCAL;
+ ours1->generation = 0;
+ p->ourcandidates = ours1;
+
+ if (!ast_strlen_zero(externip)) {
+ /* XXX We should really stun for this one not just go with externip XXX */
+ snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
+ snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(ours2->username, user, sizeof(ours2->username));
+ ast_copy_string(ours2->password, pass, sizeof(ours2->password));
+ ast_copy_string(ours2->ip, externip, sizeof(ours2->ip));
+ ast_copy_string(ours2->name, "rtp", sizeof(ours1->name));
+ ours2->port = ntohs(sin.sin_port);
+ ours2->preference = 0.9;
+ ours2->protocol = AJI_PROTOCOL_UDP;
+ ours2->type = AJI_CONNECT_STUN;
+ ours2->generation = 0;
+ ours1->next = ours2;
+ ours2 = NULL;
+ }
+ ours1 = NULL;
+ dest.sin_addr = __ourip;
+ dest.sin_port = sin.sin_port;
+
+
+ for (tmp = p->ourcandidates; tmp; tmp = tmp->next) {
+ snprintf(port, sizeof(port), "%d", tmp->port);
+ snprintf(preference, sizeof(preference), "%.2f", tmp->preference);
+ iks_insert_attrib(iq, "from", to);
+ iks_insert_attrib(iq, "to", from);
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "id", c->mid);
+ ast_aji_increment_mid(c->mid);
+ iks_insert_attrib(gtalk, "type", "transport-info");
+ iks_insert_attrib(gtalk, "id", sid);
+ iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : from);
+ iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
+ iks_insert_attrib(candidate, "name", tmp->name);
+ iks_insert_attrib(candidate, "address", tmp->ip);
+ iks_insert_attrib(candidate, "port", port);
+ iks_insert_attrib(candidate, "username", tmp->username);
+ iks_insert_attrib(candidate, "password", tmp->password);
+ iks_insert_attrib(candidate, "preference", preference);
+ if (tmp->protocol == AJI_PROTOCOL_UDP)
+ iks_insert_attrib(candidate, "protocol", "udp");
+ if (tmp->protocol == AJI_PROTOCOL_SSLTCP)
+ iks_insert_attrib(candidate, "protocol", "ssltcp");
+ if (tmp->type == AJI_CONNECT_STUN)
+ iks_insert_attrib(candidate, "type", "stun");
+ if (tmp->type == AJI_CONNECT_LOCAL)
+ iks_insert_attrib(candidate, "type", "local");
+ if (tmp->type == AJI_CONNECT_RELAY)
+ iks_insert_attrib(candidate, "type", "relay");
+ iks_insert_attrib(candidate, "network", "0");
+ iks_insert_attrib(candidate, "generation", "0");
+ ast_aji_send(c, iq);
+ }
+ p->laststun = 0;
+
+safeout:
+ if (ours1)
+ ast_free(ours1);
+ if (ours2)
+ ast_free(ours2);
+ if (iq)
+ iks_delete(iq);
+ if (gtalk)
+ iks_delete(gtalk);
+ if (candidate)
+ iks_delete(candidate);
+ if(transport)
+ iks_delete(transport);
+ return 1;
+}
+
+static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid)
+{
+ struct gtalk_pvt *tmp = NULL;
+ struct aji_resource *resources = NULL;
+ struct aji_buddy *buddy;
+ char idroster[200];
+ char *data, *exten = NULL;
+
+ ast_debug(1, "The client is %s for alloc\n", client->name);
+ if (!sid && !strchr(them, '/')) { /* I started call! */
+ if (!strcasecmp(client->name, "guest")) {
+ buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them);
+ if (buddy)
+ resources = buddy->resources;
+ } else if (client->buddy)
+ resources = client->buddy->resources;
+ while (resources) {
+ if (resources->cap->jingle) {
+ break;
+ }
+ resources = resources->next;
+ }
+ if (resources)
+ snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource);
+ else {
+ ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n");
+ return NULL;
+ }
+ }
+ if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
+ return NULL;
+ }
+
+ memcpy(&tmp->prefs, &client->prefs, sizeof(struct ast_codec_pref));
+
+ if (sid) {
+ ast_copy_string(tmp->sid, sid, sizeof(tmp->sid));
+ ast_copy_string(tmp->them, them, sizeof(tmp->them));
+ ast_copy_string(tmp->us, us, sizeof(tmp->us));
+ } else {
+ snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(tmp->them, idroster, sizeof(tmp->them));
+ ast_copy_string(tmp->us, us, sizeof(tmp->us));
+ tmp->initiator = 1;
+ }
+ tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ tmp->parent = client;
+ if (!tmp->rtp) {
+ ast_log(LOG_WARNING, "Out of RTP sessions?\n");
+ ast_free(tmp);
+ return NULL;
+ }
+
+ /* Set CALLERID(name) to the full JID of the remote peer */
+ ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name));
+
+ if(strchr(tmp->us, '/')) {
+ data = ast_strdupa(tmp->us);
+ exten = strsep(&data, "/");
+ } else
+ exten = tmp->us;
+ ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
+ ast_mutex_init(&tmp->lock);
+ ast_mutex_lock(&gtalklock);
+ tmp->next = client->p;
+ client->p = tmp;
+ ast_mutex_unlock(&gtalklock);
+ return tmp;
+}
+
+/*! \brief Start new gtalk channel */
+static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title)
+{
+ struct ast_channel *tmp;
+ int fmt;
+ int what;
+ const char *n2;
+
+ if (title)
+ n2 = title;
+ else
+ n2 = i->us;
+ tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff);
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
+ return NULL;
+ }
+ tmp->tech = &gtalk_tech;
+
+ /* Select our native format based on codec preference until we receive
+ something from another device to the contrary. */
+/* ast_verbose("XXXXXXXXXXXXX\nXXX i->jointcapability = %X\nXXX i->capability = %X\nXXX global_capability %X\n XXXXXXXXXXXX\n",i->jointcapability,i->capability,global_capability); */
+ if (i->jointcapability)
+ what = i->jointcapability;
+ else if (i->capability)
+ what = i->capability;
+ else
+ what = global_capability;
+
+ /* Set Frame packetization */
+ if (i->rtp)
+ ast_rtp_codec_setpref(i->rtp, &i->prefs);
+
+ tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
+ fmt = ast_best_codec(tmp->nativeformats);
+
+ if (i->rtp) {
+ ast_rtp_setstun(i->rtp, 1);
+ ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp));
+ ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp));
+ }
+ if (i->vrtp) {
+ ast_rtp_setstun(i->rtp, 1);
+ ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp));
+ ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp));
+ }
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->adsicpe = AST_ADSI_UNAVAILABLE;
+ tmp->writeformat = fmt;
+ tmp->rawwriteformat = fmt;
+ tmp->readformat = fmt;
+ tmp->rawreadformat = fmt;
+ tmp->tech_pvt = i;
+
+ tmp->callgroup = client->callgroup;
+ tmp->pickupgroup = client->pickupgroup;
+ tmp->cid.cid_pres = client->callingpres;
+ if (!ast_strlen_zero(client->accountcode))
+ ast_string_field_set(tmp, accountcode, client->accountcode);
+ if (client->amaflags)
+ tmp->amaflags = client->amaflags;
+ if (!ast_strlen_zero(client->language))
+ ast_string_field_set(tmp, language, client->language);
+ if (!ast_strlen_zero(client->musicclass))
+ ast_string_field_set(tmp, musicclass, client->musicclass);
+ i->owner = tmp;
+ ast_module_ref(ast_module_info->self);
+ ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
+
+ if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s"))
+ tmp->cid.cid_dnid = ast_strdup(i->exten);
+ tmp->priority = 1;
+ if (i->rtp)
+ ast_jb_configure(tmp, &global_jbconf);
+ if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(tmp);
+ tmp = NULL;
+ } else {
+ manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
+ "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
+ i->owner ? i->owner->name : "", "Gtalk", i->sid);
+ }
+ return tmp;
+}
+
+static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
+{
+ iks *request, *session = NULL;
+ int res = -1;
+
+ request = iks_new("iq");
+ if (request) {
+ iks_insert_attrib(request, "type", "set");
+ iks_insert_attrib(request, "from", p->us);
+ iks_insert_attrib(request, "to", p->them);
+ iks_insert_attrib(request, "id", client->connection->mid);
+ ast_aji_increment_mid(client->connection->mid);
+ session = iks_new("session");
+ if (session) {
+ iks_insert_attrib(session, "type", action);
+ iks_insert_attrib(session, "id", p->sid);
+ iks_insert_attrib(session, "initiator", p->initiator ? p->us : p->them);
+ iks_insert_attrib(session, "xmlns", "http://www.google.com/session");
+ iks_insert_node(request, session);
+ ast_aji_send(client->connection, request);
+ iks_delete(session);
+ res = 0;
+ }
+ iks_delete(request);
+ }
+ return res;
+}
+
+static void gtalk_free_candidates(struct gtalk_candidate *candidate)
+{
+ struct gtalk_candidate *last;
+ while (candidate) {
+ last = candidate;
+ candidate = candidate->next;
+ ast_free(last);
+ }
+}
+
+static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
+{
+ struct gtalk_pvt *cur, *prev = NULL;
+ cur = client->p;
+ while (cur) {
+ if (cur == p) {
+ if (prev)
+ prev->next = p->next;
+ else
+ client->p = p->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ if (p->ringrule)
+ iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
+ if (p->owner)
+ ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
+ if (p->rtp)
+ ast_rtp_destroy(p->rtp);
+ if (p->vrtp)
+ ast_rtp_destroy(p->vrtp);
+ gtalk_free_candidates(p->theircandidates);
+ ast_free(p);
+}
+
+
+static int gtalk_newcall(struct gtalk *client, ikspak *pak)
+{
+ struct gtalk_pvt *p, *tmp = client->p;
+ struct ast_channel *chan;
+ int res;
+ iks *codec;
+ char *from = NULL;
+ /* Make sure our new call doesn't exist yet */
+ from = iks_find_attrib(pak->x,"to");
+ if(!from)
+ from = client->connection->jid->full;
+
+ while (tmp) {
+ if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
+ ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
+ gtalk_response(client, from, pak, "out-of-order", NULL);
+ return -1;
+ }
+ tmp = tmp->next;
+ }
+
+ p = gtalk_alloc(client, from, pak->from->full, iks_find_attrib(pak->query, "id"));
+ if (!p) {
+ ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
+ return -1;
+ }
+ chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user);
+ if (chan) {
+ ast_mutex_lock(&p->lock);
+ ast_copy_string(p->them, pak->from->full, sizeof(p->them));
+ if (iks_find_attrib(pak->query, "id")) {
+ ast_copy_string(p->sid, iks_find_attrib(pak->query, "id"),
+ sizeof(p->sid));
+ }
+
+ codec = iks_child(iks_child(iks_child(pak->x)));
+ while (codec) {
+ ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id")));
+ ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio",
+ iks_find_attrib(codec, "name"), 0);
+ codec = iks_next(codec);
+ }
+
+ ast_mutex_unlock(&p->lock);
+ ast_setstate(chan, AST_STATE_RING);
+ res = ast_pbx_start(chan);
+
+ switch (res) {
+ case AST_PBX_FAILED:
+ ast_log(LOG_WARNING, "Failed to start PBX :(\n");
+ gtalk_response(client, from, pak, "service-unavailable", NULL);
+ break;
+ case AST_PBX_CALL_LIMIT:
+ ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
+ gtalk_response(client, from, pak, "service-unavailable", NULL);
+ break;
+ case AST_PBX_SUCCESS:
+ gtalk_response(client, from, pak, NULL, NULL);
+ gtalk_invite_response(p, p->them, p->us,p->sid, 0);
+ gtalk_create_candidates(client, p, p->sid, p->them, p->us);
+ /* nothing to do */
+ break;
+ }
+ } else {
+ gtalk_free_pvt(client, p);
+ }
+ return 1;
+}
+
+static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
+{
+ struct gtalk_candidate *tmp;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct sockaddr_in sin;
+ struct sockaddr_in aux;
+
+ if (time(NULL) == p->laststun)
+ return 0;
+
+ tmp = p->theircandidates;
+ p->laststun = time(NULL);
+ while (tmp) {
+ char username[256];
+
+ /* Find the IP address of the host */
+ hp = ast_gethostbyname(tmp->ip, &ahp);
+ sin.sin_family = AF_INET;
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ sin.sin_port = htons(tmp->port);
+ snprintf(username, sizeof(username), "%s%s", tmp->username,
+ p->ourcandidates->username);
+
+ /* Find out the result of the STUN */
+ ast_rtp_get_peer(p->rtp, &aux);
+
+ /* If the STUN result is different from the IP of the hostname,
+ lock on the stun IP of the hostname advertised by the
+ remote client */
+ if (aux.sin_addr.s_addr &&
+ aux.sin_addr.s_addr != sin.sin_addr.s_addr)
+ ast_rtp_stun_request(p->rtp, &aux, username);
+ else
+ ast_rtp_stun_request(p->rtp, &sin, username);
+
+ if (aux.sin_addr.s_addr) {
+ ast_debug(4, "Receiving RTP traffic from IP %s, matches with remote candidate's IP %s\n", ast_inet_ntoa(aux.sin_addr), tmp->ip);
+ ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
+ }
+
+ tmp = tmp->next;
+ }
+ return 1;
+}
+
+static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
+{
+ struct gtalk_pvt *p = NULL, *tmp = NULL;
+ struct aji_client *c = client->connection;
+ struct gtalk_candidate *newcandidate = NULL;
+ iks *traversenodes = NULL, *receipt = NULL;
+ char *from;
+
+ from = iks_find_attrib(pak->x,"to");
+ if(!from)
+ from = c->jid->full;
+
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
+ p = tmp;
+ break;
+ }
+ }
+
+ if (!p)
+ return -1;
+
+ traversenodes = pak->query;
+ while(traversenodes) {
+ if(!strcasecmp(iks_name(traversenodes), "session")) {
+ traversenodes = iks_child(traversenodes);
+ continue;
+ }
+ if(!strcasecmp(iks_name(traversenodes), "transport")) {
+ traversenodes = iks_child(traversenodes);
+ continue;
+ }
+ if(!strcasecmp(iks_name(traversenodes), "candidate")) {
+ newcandidate = ast_calloc(1, sizeof(*newcandidate));
+ if (!newcandidate)
+ return 0;
+ ast_copy_string(newcandidate->name, iks_find_attrib(traversenodes, "name"),
+ sizeof(newcandidate->name));
+ ast_copy_string(newcandidate->ip, iks_find_attrib(traversenodes, "address"),
+ sizeof(newcandidate->ip));
+ newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
+ ast_copy_string(newcandidate->username, iks_find_attrib(traversenodes, "username"),
+ sizeof(newcandidate->username));
+ ast_copy_string(newcandidate->password, iks_find_attrib(traversenodes, "password"),
+ sizeof(newcandidate->password));
+ newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
+ if (!strcasecmp(iks_find_attrib(traversenodes, "protocol"), "udp"))
+ newcandidate->protocol = AJI_PROTOCOL_UDP;
+ if (!strcasecmp(iks_find_attrib(traversenodes, "protocol"), "ssltcp"))
+ newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
+
+ if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "stun"))
+ newcandidate->type = AJI_CONNECT_STUN;
+ if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "local"))
+ newcandidate->type = AJI_CONNECT_LOCAL;
+ if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "relay"))
+ newcandidate->type = AJI_CONNECT_RELAY;
+ ast_copy_string(newcandidate->network, iks_find_attrib(traversenodes, "network"),
+ sizeof(newcandidate->network));
+ newcandidate->generation = atoi(iks_find_attrib(traversenodes, "generation"));
+ newcandidate->next = NULL;
+
+ newcandidate->next = p->theircandidates;
+ p->theircandidates = newcandidate;
+ p->laststun = 0;
+ gtalk_update_stun(p->parent, p);
+ newcandidate = NULL;
+ }
+ traversenodes = iks_next(traversenodes);
+ }
+
+ receipt = iks_new("iq");
+ iks_insert_attrib(receipt, "type", "result");
+ iks_insert_attrib(receipt, "from", from);
+ iks_insert_attrib(receipt, "to", iks_find_attrib(pak->x, "from"));
+ iks_insert_attrib(receipt, "id", iks_find_attrib(pak->x, "id"));
+ ast_aji_send(c, receipt);
+ iks_delete(receipt);
+
+ return 1;
+}
+
+static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
+{
+ struct ast_frame *f;
+
+ if (!p->rtp)
+ return &ast_null_frame;
+ f = ast_rtp_read(p->rtp);
+ gtalk_update_stun(p->parent, p);
+ if (p->owner) {
+ /* We already hold the channel lock */
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
+ ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
+ p->owner->nativeformats =
+ (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass;
+ ast_set_read_format(p->owner, p->owner->readformat);
+ ast_set_write_format(p->owner, p->owner->writeformat);
+ }
+/* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
+ f = ast_dsp_process(p->owner, p->vad, f);
+ if (option_debug && f && (f->frametype == AST_FRAME_DTMF))
+ ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
+ } */
+ }
+ }
+ return f;
+}
+
+static struct ast_frame *gtalk_read(struct ast_channel *ast)
+{
+ struct ast_frame *fr;
+ struct gtalk_pvt *p = ast->tech_pvt;
+
+ ast_mutex_lock(&p->lock);
+ fr = gtalk_rtp_read(ast, p);
+ ast_mutex_unlock(&p->lock);
+ return fr;
+}
+
+/*! \brief Send frame to media channel (rtp) */
+static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct gtalk_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ switch (frame->frametype) {
+ case AST_FRAME_VOICE:
+ if (!(frame->subclass & ast->nativeformats)) {
+ ast_log(LOG_WARNING,
+ "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
+ frame->subclass, ast->nativeformats, ast->readformat,
+ ast->writeformat);
+ return 0;
+ }
+ if (p) {
+ ast_mutex_lock(&p->lock);
+ if (p->rtp) {
+ res = ast_rtp_write(p->rtp, frame);
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ case AST_FRAME_VIDEO:
+ if (p) {
+ ast_mutex_lock(&p->lock);
+ if (p->vrtp) {
+ res = ast_rtp_write(p->vrtp, frame);
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ case AST_FRAME_IMAGE:
+ return 0;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't send %d type frames with Gtalk write\n",
+ frame->frametype);
+ return 0;
+ }
+
+ return res;
+}
+
+static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct gtalk_pvt *p = newchan->tech_pvt;
+ ast_mutex_lock(&p->lock);
+
+ if ((p->owner != oldchan)) {
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ if (p->owner == oldchan)
+ p->owner = newchan;
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+ int res = 0;
+
+ switch (condition) {
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, data, NULL);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Don't know how to indicate condition '%d'\n", condition);
+ res = -1;
+ }
+
+ return res;
+}
+
+static int gtalk_digit_begin(struct ast_channel *chan, char digit)
+{
+ return gtalk_digit(chan, digit, 0);
+}
+
+static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
+{
+ return gtalk_digit(chan, digit, duration);
+}
+
+static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct gtalk_pvt *p = ast->tech_pvt;
+ struct gtalk *client = p->parent;
+ iks *iq, *gtalk, *dtmf;
+ char buffer[2] = {digit, '\0'};
+ iq = iks_new("iq");
+ gtalk = iks_new("gtalk");
+ dtmf = iks_new("dtmf");
+ if(!iq || !gtalk || !dtmf) {
+ if(iq)
+ iks_delete(iq);
+ if(gtalk)
+ iks_delete(gtalk);
+ if(dtmf)
+ iks_delete(dtmf);
+ ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
+ return -1;
+ }
+
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "to", p->them);
+ iks_insert_attrib(iq, "from", p->us);
+ iks_insert_attrib(iq, "id", client->connection->mid);
+ ast_aji_increment_mid(client->connection->mid);
+ iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
+ iks_insert_attrib(gtalk, "action", "session-info");
+ iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: p->them);
+ iks_insert_attrib(gtalk, "sid", p->sid);
+ iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
+ iks_insert_attrib(dtmf, "code", buffer);
+ iks_insert_node(iq, gtalk);
+ iks_insert_node(gtalk, dtmf);
+
+ ast_mutex_lock(&p->lock);
+ if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
+ iks_insert_attrib(dtmf, "action", "button-down");
+ } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
+ iks_insert_attrib(dtmf, "action", "button-up");
+ }
+ ast_aji_send(client->connection, iq);
+ iks_delete(iq);
+ iks_delete(gtalk);
+ iks_delete(dtmf);
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
+{
+ ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
+
+ return -1;
+}
+
+/* Not in use right now.
+static int gtalk_auto_congest(void *nothing)
+{
+ struct gtalk_pvt *p = nothing;
+
+ ast_mutex_lock(&p->lock);
+ if (p->owner) {
+ if (!ast_channel_trylock(p->owner)) {
+ ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ ast_channel_unlock(p->owner);
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+*/
+
+/*! \brief Initiate new call, part of PBX interface
+ * dest is the dial string */
+static int gtalk_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct gtalk_pvt *p = ast->tech_pvt;
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+
+ ast_setstate(ast, AST_STATE_RING);
+ p->jointcapability = p->capability;
+ if (!p->ringrule) {
+ ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
+ p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
+ IKS_RULE_ID, p->ring, IKS_RULE_DONE);
+ } else
+ ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
+
+ gtalk_invite(p, p->them, p->us, p->sid, 1);
+ gtalk_create_candidates(p->parent, p, p->sid, p->them, p->us);
+
+ return 0;
+}
+
+/*! \brief Hangup a call through the gtalk proxy channel */
+static int gtalk_hangup(struct ast_channel *ast)
+{
+ struct gtalk_pvt *p = ast->tech_pvt;
+ struct gtalk *client;
+
+ ast_mutex_lock(&p->lock);
+ client = p->parent;
+ p->owner = NULL;
+ ast->tech_pvt = NULL;
+ if (!p->alreadygone)
+ gtalk_action(client, p, "terminate");
+ ast_mutex_unlock(&p->lock);
+
+ gtalk_free_pvt(client, p);
+ ast_module_unref(ast_module_info->self);
+
+ return 0;
+}
+
+/*! \brief Part of PBX interface */
+static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause)
+{
+ struct gtalk_pvt *p = NULL;
+ struct gtalk *client = NULL;
+ char *sender = NULL, *to = NULL, *s = NULL;
+ struct ast_channel *chan = NULL;
+
+ if (data) {
+ s = ast_strdupa(data);
+ if (s) {
+ sender = strsep(&s, "/");
+ if (sender && (sender[0] != '\0'))
+ to = strsep(&s, "/");
+ if (!to) {
+ ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
+ return NULL;
+ }
+ }
+ }
+ client = find_gtalk(to, sender);
+ if (!client) {
+ ast_log(LOG_WARNING, "Could not find recipient.\n");
+ return NULL;
+ }
+ ASTOBJ_WRLOCK(client);
+ p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
+ if (p)
+ chan = gtalk_new(client, p, AST_STATE_DOWN, to);
+
+ ASTOBJ_UNLOCK(client);
+ return chan;
+}
+
+/*! \brief CLI command "gtalk show channels" */
+static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
+ struct gtalk_pvt *p;
+ struct ast_channel *chan;
+ int numchans = 0;
+ char them[AJI_MAX_JIDLEN];
+ char *jid = NULL;
+ char *resource = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "gtalk show channels";
+ e->usage =
+ "Usage: gtalk show channels\n"
+ " Shows current state of the Gtalk channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&gtalklock);
+ ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
+ ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
+ ASTOBJ_WRLOCK(iterator);
+ p = iterator->p;
+ while(p) {
+ chan = p->owner;
+ ast_copy_string(them, p->them, sizeof(them));
+ jid = them;
+ resource = strchr(them, '/');
+ if (!resource)
+ resource = "None";
+ else {
+ *resource = '\0';
+ resource ++;
+ }
+ if (chan)
+ ast_cli(a->fd, FORMAT,
+ chan->name,
+ jid,
+ resource,
+ ast_getformatname(chan->readformat),
+ ast_getformatname(chan->writeformat)
+ );
+ else
+ ast_log(LOG_WARNING, "No available channel\n");
+ numchans ++;
+ p = p->next;
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ ast_mutex_unlock(&gtalklock);
+
+ ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
+ return CLI_SUCCESS;
+#undef FORMAT
+}
+
+/*! \brief CLI command "gtalk reload" */
+static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "gtalk reload";
+ e->usage =
+ "Usage: gtalk reload\n"
+ " Reload gtalk channel driver.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_verbose("IT DOES WORK!\n");
+ return CLI_SUCCESS;
+}
+
+static int gtalk_parser(void *data, ikspak *pak)
+{
+ struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
+
+ if (iks_find_with_attrib(pak->x, "session", "type", "initiate")) {
+ /* New call */
+ gtalk_newcall(client, pak);
+ } else if (iks_find_with_attrib(pak->x, "session", "type", "candidates") || iks_find_with_attrib(pak->x, "session", "type", "transport-info")) {
+ ast_debug(3, "About to add candidate!\n");
+ gtalk_add_candidate(client, pak);
+ ast_debug(3, "Candidate Added!\n");
+ } else if (iks_find_with_attrib(pak->x, "session", "type", "accept")) {
+ gtalk_is_answered(client, pak);
+ } else if (iks_find_with_attrib(pak->x, "session", "type", "transport-accept")) {
+ gtalk_is_accepted(client, pak);
+ } else if (iks_find_with_attrib(pak->x, "session", "type", "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
+ gtalk_handle_dtmf(client, pak);
+ } else if (iks_find_with_attrib(pak->x, "session", "type", "terminate")) {
+ gtalk_hangup_farend(client, pak);
+ } else if (iks_find_with_attrib(pak->x, "session", "type", "reject")) {
+ gtalk_hangup_farend(client, pak);
+ }
+ ASTOBJ_UNREF(client, gtalk_member_destroy);
+ return IKS_FILTER_EAT;
+}
+
+/* Not using this anymore probably take out soon
+static struct gtalk_candidate *gtalk_create_candidate(char *args)
+{
+ char *name, *type, *preference, *protocol;
+ struct gtalk_candidate *res;
+ res = ast_calloc(1, sizeof(*res));
+ if (args)
+ name = args;
+ if ((args = strchr(args, ','))) {
+ *args = '\0';
+ args++;
+ preference = args;
+ }
+ if ((args = strchr(args, ','))) {
+ *args = '\0';
+ args++;
+ protocol = args;
+ }
+ if ((args = strchr(args, ','))) {
+ *args = '\0';
+ args++;
+ type = args;
+ }
+ if (name)
+ ast_copy_string(res->name, name, sizeof(res->name));
+ if (preference) {
+ res->preference = atof(preference);
+ }
+ if (protocol) {
+ if (!strcasecmp("udp", protocol))
+ res->protocol = AJI_PROTOCOL_UDP;
+ if (!strcasecmp("ssltcp", protocol))
+ res->protocol = AJI_PROTOCOL_SSLTCP;
+ }
+ if (type) {
+ if (!strcasecmp("stun", type))
+ res->type = AJI_CONNECT_STUN;
+ if (!strcasecmp("local", type))
+ res->type = AJI_CONNECT_LOCAL;
+ if (!strcasecmp("relay", type))
+ res->type = AJI_CONNECT_RELAY;
+ }
+
+ return res;
+}
+*/
+
+static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
+ struct ast_codec_pref prefs, char *context,
+ struct gtalk *member)
+{
+ struct aji_client *client;
+
+ if (!member)
+ ast_log(LOG_WARNING, "Out of memory.\n");
+
+ ast_copy_string(member->name, label, sizeof(member->name));
+ ast_copy_string(member->user, label, sizeof(member->user));
+ ast_copy_string(member->context, context, sizeof(member->context));
+ member->allowguest = allowguest;
+ member->prefs = prefs;
+ while (var) {
+#if 0
+ struct gtalk_candidate *candidate = NULL;
+#endif
+ if (!strcasecmp(var->name, "username"))
+ ast_copy_string(member->user, var->value, sizeof(member->user));
+ else if (!strcasecmp(var->name, "disallow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0);
+ else if (!strcasecmp(var->name, "allow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
+ else if (!strcasecmp(var->name, "context"))
+ ast_copy_string(member->context, var->value, sizeof(member->context));
+#if 0
+ else if (!strcasecmp(var->name, "candidate")) {
+ candidate = gtalk_create_candidate(var->value);
+ if (candidate) {
+ candidate->next = member->ourcandidates;
+ member->ourcandidates = candidate;
+ }
+ }
+#endif
+ else if (!strcasecmp(var->name, "connection")) {
+ if ((client = ast_aji_get_client(var->value))) {
+ member->connection = client;
+ iks_filter_add_rule(client->f, gtalk_parser, member,
+ IKS_RULE_TYPE, IKS_PAK_IQ,
+ IKS_RULE_FROM_PARTIAL, member->user,
+ IKS_RULE_NS, "http://www.google.com/session",
+ IKS_RULE_DONE);
+
+ } else {
+ ast_log(LOG_ERROR, "connection referenced not found!\n");
+ return 0;
+ }
+ }
+ var = var->next;
+ }
+ if (member->connection && member->user)
+ member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
+ else {
+ ast_log(LOG_ERROR, "No Connection or Username!\n");
+ }
+ return 1;
+}
+
+static int gtalk_load_config(void)
+{
+ char *cat = NULL;
+ struct ast_config *cfg = NULL;
+ char context[AST_MAX_CONTEXT];
+ int allowguest = 1;
+ struct ast_variable *var;
+ struct gtalk *member;
+ struct ast_codec_pref prefs;
+ struct aji_client_container *clients;
+ struct gtalk_candidate *global_candidates = NULL;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct ast_flags config_flags = { 0 };
+
+ cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
+ if (!cfg)
+ return 0;
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ cat = ast_category_browse(cfg, NULL);
+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, var->name, var->value))
+ continue;
+
+ if (!strcasecmp(var->name, "allowguest"))
+ allowguest =
+ (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
+ else if (!strcasecmp(var->name, "disallow"))
+ ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0);
+ else if (!strcasecmp(var->name, "allow"))
+ ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
+ else if (!strcasecmp(var->name, "context"))
+ ast_copy_string(context, var->value, sizeof(context));
+ else if (!strcasecmp(var->name, "bindaddr")) {
+ if (!(hp = ast_gethostbyname(var->value, &ahp))) {
+ ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
+ } else {
+ memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
+ }
+ }
+/* Idea to allow for custom candidates */
+/*
+ else if (!strcasecmp(var->name, "candidate")) {
+ candidate = gtalk_create_candidate(var->value);
+ if (candidate) {
+ candidate->next = global_candidates;
+ global_candidates = candidate;
+ }
+ }
+*/
+ }
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ var = ast_variable_browse(cfg, cat);
+ member = ast_calloc(1, sizeof(*member));
+ ASTOBJ_INIT(member);
+ ASTOBJ_WRLOCK(member);
+ if (!strcasecmp(cat, "guest")) {
+ ast_copy_string(member->name, "guest", sizeof(member->name));
+ ast_copy_string(member->user, "guest", sizeof(member->user));
+ ast_copy_string(member->context, context, sizeof(member->context));
+ member->allowguest = allowguest;
+ member->prefs = prefs;
+ while (var) {
+ if (!strcasecmp(var->name, "disallow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability,
+ var->value, 0);
+ else if (!strcasecmp(var->name, "allow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability,
+ var->value, 1);
+ else if (!strcasecmp(var->name, "context"))
+ ast_copy_string(member->context, var->value,
+ sizeof(member->context));
+/* Idea to allow for custom candidates */
+/*
+ else if (!strcasecmp(var->name, "candidate")) {
+ candidate = gtalk_create_candidate(var->value);
+ if (candidate) {
+ candidate->next = member->ourcandidates;
+ member->ourcandidates = candidate;
+ }
+ }
+*/
+ var = var->next;
+ }
+ ASTOBJ_UNLOCK(member);
+ clients = ast_aji_get_clients();
+ if (clients) {
+ ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
+ ASTOBJ_WRLOCK(iterator);
+ ASTOBJ_WRLOCK(member);
+ member->connection = iterator;
+ iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "http://www.google.com/session", IKS_RULE_DONE);
+ iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "http://jabber.org/protocol/gtalk", IKS_RULE_DONE);
+ ASTOBJ_UNLOCK(member);
+ ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
+ ASTOBJ_UNLOCK(iterator);
+ });
+ } else {
+ ASTOBJ_UNLOCK(member);
+ ASTOBJ_UNREF(member, gtalk_member_destroy);
+ }
+ } else {
+ ASTOBJ_UNLOCK(member);
+ if (gtalk_create_member(cat, var, allowguest, prefs, context, member))
+ ASTOBJ_CONTAINER_LINK(&gtalk_list, member);
+ ASTOBJ_UNREF(member, gtalk_member_destroy);
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ gtalk_free_candidates(global_candidates);
+ return 1;
+}
+
+/*! \brief Load module into PBX, register channel */
+static int load_module(void)
+{
+ ASTOBJ_CONTAINER_INIT(&gtalk_list);
+ if (!gtalk_load_config()) {
+ ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
+ return 0;
+ }
+
+ sched = sched_context_create();
+ if (!sched)
+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
+
+ io = io_context_create();
+ if (!io)
+ ast_log(LOG_WARNING, "Unable to create I/O context\n");
+
+ if (ast_find_ourip(&__ourip, bindaddr)) {
+ ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
+ return 0;
+ }
+
+ ast_rtp_proto_register(&gtalk_rtp);
+ ast_cli_register_multiple(gtalk_cli, sizeof(gtalk_cli) / sizeof(gtalk_cli[0]));
+
+ /* Make sure we can register our channel type */
+ if (ast_channel_register(&gtalk_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief Reload module */
+static int reload(void)
+{
+ return 0;
+}
+
+/*! \brief Unload the gtalk channel from Asterisk */
+static int unload_module(void)
+{
+ struct gtalk_pvt *privates = NULL;
+ ast_cli_unregister_multiple(gtalk_cli, sizeof(gtalk_cli) / sizeof(gtalk_cli[0]));
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&gtalk_tech);
+ ast_rtp_proto_unregister(&gtalk_rtp);
+
+ if (!ast_mutex_lock(&gtalklock)) {
+ /* Hangup all interfaces if they have an owner */
+ ASTOBJ_CONTAINER_TRAVERSE(&gtalk_list, 1, {
+ ASTOBJ_WRLOCK(iterator);
+ privates = iterator->p;
+ while(privates) {
+ if (privates->owner)
+ ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
+ privates = privates->next;
+ }
+ iterator->p = NULL;
+ ASTOBJ_UNLOCK(iterator);
+ });
+ ast_mutex_unlock(&gtalklock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+ ASTOBJ_CONTAINER_DESTROYALL(&gtalk_list, gtalk_member_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&gtalk_list);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Gtalk Channel Driver",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/channels/chan_h323.c b/trunk/channels/chan_h323.c
new file mode 100644
index 000000000..8761026d0
--- /dev/null
+++ b/trunk/channels/chan_h323.c
@@ -0,0 +1,3353 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005
+ *
+ * OpenH323 Channel Driver for ASTERISK PBX.
+ * By Jeremy McNamara
+ * For The NuFone Network
+ *
+ * chan_h323 has been derived from code created by
+ * Michael Manousos and Mark Spencer
+ *
+ * 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 This file is part of the chan_h323 driver for Asterisk
+ *
+ * \author Jeremy McNamara
+ *
+ * \par See also
+ * \arg Config_h323
+ * \extref OpenH323 http://www.voxgratia.org/
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>openh323</depend>
+ <defaultenabled>yes</defaultenabled>
+ ***/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/param.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cli.h"
+#include "asterisk/dsp.h"
+#include "asterisk/causes.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/astobj.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "h323/chan_h323.h"
+
+receive_digit_cb on_receive_digit;
+on_rtp_cb on_external_rtp_create;
+start_rtp_cb on_start_rtp_channel;
+setup_incoming_cb on_incoming_call;
+setup_outbound_cb on_outgoing_call;
+chan_ringing_cb on_chan_ringing;
+con_established_cb on_connection_established;
+clear_con_cb on_connection_cleared;
+answer_call_cb on_answer_call;
+progress_cb on_progress;
+rfc2833_cb on_set_rfc2833_payload;
+hangup_cb on_hangup;
+setcapabilities_cb on_setcapabilities;
+setpeercapabilities_cb on_setpeercapabilities;
+onhold_cb on_hold;
+
+int h323debug; /*!< global debug flag */
+
+/*! \brief Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+/** Variables required by Asterisk */
+static const char tdesc[] = "The NuFone Network's Open H.323 Channel Driver";
+static const char config[] = "h323.conf";
+static char default_context[AST_MAX_CONTEXT] = "default";
+static struct sockaddr_in bindaddr;
+
+#define GLOBAL_CAPABILITY (AST_FORMAT_G723_1 | AST_FORMAT_GSM | AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_G729A | AST_FORMAT_G726_AAL2 | AST_FORMAT_H261)
+
+/** H.323 configuration values */
+static int h323_signalling_port = 1720;
+static char gatekeeper[100];
+static int gatekeeper_disable = 1;
+static int gatekeeper_discover = 0;
+static int gkroute = 0;
+/* Find user by alias (h.323 id) is default, alternative is the incomming call's source IP address*/
+static int userbyalias = 1;
+static int acceptAnonymous = 1;
+static unsigned int tos = 0;
+static unsigned int cos = 0;
+static char secret[50];
+static unsigned int unique = 0;
+
+static call_options_t global_options;
+
+/*! \brief Private structure of a OpenH323 channel */
+struct oh323_pvt {
+ ast_mutex_t lock; /*!< Channel private lock */
+ call_options_t options; /*!<!< Options to be used during call setup */
+ int alreadygone; /*!< Whether or not we've already been destroyed by our peer */
+ int needdestroy; /*!< if we need to be destroyed */
+ call_details_t cd; /*!< Call details */
+ struct ast_channel *owner; /*!< Who owns us */
+ struct sockaddr_in sa; /*!< Our peer */
+ struct sockaddr_in redirip; /*!< Where our RTP should be going if not to us */
+ int nonCodecCapability; /*!< non-audio capability */
+ int outgoing; /*!< Outgoing or incoming call? */
+ char exten[AST_MAX_EXTENSION]; /*!< Requested extension */
+ char context[AST_MAX_CONTEXT]; /*!< Context where to start */
+ char accountcode[256]; /*!< Account code */
+ char rdnis[80]; /*!< Referring DNIS, if available */
+ int amaflags; /*!< AMA Flags */
+ struct ast_rtp *rtp; /*!< RTP Session */
+ struct ast_dsp *vad; /*!< Used for in-band DTMF detection */
+ int nativeformats; /*!< Codec formats supported by a channel */
+ int needhangup; /*!< Send hangup when Asterisk is ready */
+ int hangupcause; /*!< Hangup cause from OpenH323 layer */
+ int newstate; /*!< Pending state change */
+ int newcontrol; /*!< Pending control to send */
+ int newdigit; /*!< Pending DTMF digit to send */
+ int newduration; /*!< Pending DTMF digit duration to send */
+ int pref_codec; /*!< Preferred codec */
+ int peercapability; /*!< Capabilities learned from peer */
+ int jointcapability; /*!< Common capabilities for local and remote side */
+ struct ast_codec_pref peer_prefs; /*!< Preferenced list of codecs which remote side supports */
+ int dtmf_pt[2]; /*!< Payload code used for RFC2833/CISCO messages */
+ int curDTMF; /*!< DTMF tone being generated to Asterisk side */
+ int DTMFsched; /*!< Scheduler descriptor for DTMF */
+ int update_rtp_info; /*!< Configuration of fd's array is pending */
+ int recvonly; /*!< Peer isn't wish to receive our voice stream */
+ int txDtmfDigit; /*!< DTMF digit being to send to H.323 side */
+ int noInbandDtmf; /*!< Inband DTMF processing by DSP isn't available */
+ int connection_established; /*!< Call got CONNECT message */
+ int got_progress; /*!< Call got PROGRESS message, pass inband audio */
+ struct oh323_pvt *next; /*!< Next channel in list */
+} *iflist = NULL;
+
+/*! \brief H323 User list */
+static struct h323_user_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct oh323_user);
+} userl;
+
+/*! \brief H323 peer list */
+static struct h323_peer_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct oh323_peer);
+} peerl;
+
+/*! \brief H323 alias list */
+static struct h323_alias_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct oh323_alias);
+} aliasl;
+
+/* Asterisk RTP stuff */
+static struct sched_context *sched;
+static struct io_context *io;
+
+AST_MUTEX_DEFINE_STATIC(iflock); /*!< Protect the interface list (oh323_pvt) */
+
+/*! \brief Protect the H.323 monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+/*! \brief Protect the H.323 capabilities list, to avoid more than one channel to set the capabilities simultaneaously in the h323 stack. */
+AST_MUTEX_DEFINE_STATIC(caplock);
+
+/*! \brief Protect the reload process */
+AST_MUTEX_DEFINE_STATIC(h323_reload_lock);
+static int h323_reloading = 0;
+
+/*! \brief This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+static int restart_monitor(void);
+static int h323_do_reload(void);
+
+static void delete_users(void);
+static void delete_aliases(void);
+static void prune_peers(void);
+
+static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause);
+static int oh323_digit_begin(struct ast_channel *c, char digit);
+static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration);
+static int oh323_call(struct ast_channel *c, char *dest, int timeout);
+static int oh323_hangup(struct ast_channel *c);
+static int oh323_answer(struct ast_channel *c);
+static struct ast_frame *oh323_read(struct ast_channel *c);
+static int oh323_write(struct ast_channel *c, struct ast_frame *frame);
+static int oh323_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen);
+static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+static const struct ast_channel_tech oh323_tech = {
+ .type = "H323",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_AUDIO_MASK,
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
+ .requester = oh323_request,
+ .send_digit_begin = oh323_digit_begin,
+ .send_digit_end = oh323_digit_end,
+ .call = oh323_call,
+ .hangup = oh323_hangup,
+ .answer = oh323_answer,
+ .read = oh323_read,
+ .write = oh323_write,
+ .indicate = oh323_indicate,
+ .fixup = oh323_fixup,
+ /* disable, for now */
+#if 0
+ .bridge = ast_rtp_bridge,
+#endif
+};
+
+static const char* redirectingreason2str(int redirectingreason)
+{
+ switch (redirectingreason) {
+ case 0:
+ return "UNKNOWN";
+ case 1:
+ return "BUSY";
+ case 2:
+ return "NO_REPLY";
+ case 0xF:
+ return "UNCONDITIONAL";
+ default:
+ return "NOREDIRECT";
+ }
+}
+
+static void oh323_destroy_alias(struct oh323_alias *alias)
+{
+ if (h323debug)
+ ast_debug(1, "Destroying alias '%s'\n", alias->name);
+ ast_free(alias);
+}
+
+static void oh323_destroy_user(struct oh323_user *user)
+{
+ if (h323debug)
+ ast_debug(1, "Destroying user '%s'\n", user->name);
+ ast_free_ha(user->ha);
+ ast_free(user);
+}
+
+static void oh323_destroy_peer(struct oh323_peer *peer)
+{
+ if (h323debug)
+ ast_debug(1, "Destroying peer '%s'\n", peer->name);
+ ast_free_ha(peer->ha);
+ ast_free(peer);
+}
+
+static int oh323_simulate_dtmf_end(const void *data)
+{
+ struct oh323_pvt *pvt = (struct oh323_pvt *)data;
+
+ if (pvt) {
+ ast_mutex_lock(&pvt->lock);
+ /* Don't hold pvt lock while trying to lock the channel */
+ while(pvt->owner && ast_channel_trylock(pvt->owner)) {
+ ast_mutex_unlock(&pvt->lock);
+ usleep(1);
+ ast_mutex_lock(&pvt->lock);
+ }
+
+ if (pvt->owner) {
+ struct ast_frame f = {
+ .frametype = AST_FRAME_DTMF_END,
+ .subclass = pvt->curDTMF,
+ .samples = 0,
+ .src = "SIMULATE_DTMF_END",
+ };
+ ast_queue_frame(pvt->owner, &f);
+ ast_channel_unlock(pvt->owner);
+ }
+
+ pvt->DTMFsched = -1;
+ ast_mutex_unlock(&pvt->lock);
+ }
+
+ return 0;
+}
+
+/*! \brief Channel and private structures should be already locked */
+static void __oh323_update_info(struct ast_channel *c, struct oh323_pvt *pvt)
+{
+ if (c->nativeformats != pvt->nativeformats) {
+ if (h323debug)
+ ast_debug(1, "Preparing %s for new native format\n", c->name);
+ c->nativeformats = pvt->nativeformats;
+ ast_set_read_format(c, c->readformat);
+ ast_set_write_format(c, c->writeformat);
+ }
+ if (pvt->needhangup) {
+ if (h323debug)
+ ast_debug(1, "Process pending hangup for %s\n", c->name);
+ c->_softhangup |= AST_SOFTHANGUP_DEV;
+ c->hangupcause = pvt->hangupcause;
+ ast_queue_hangup(c);
+ pvt->needhangup = 0;
+ pvt->newstate = pvt->newcontrol = pvt->newdigit = pvt->DTMFsched = -1;
+ }
+ if (pvt->newstate >= 0) {
+ ast_setstate(c, pvt->newstate);
+ pvt->newstate = -1;
+ }
+ if (pvt->newcontrol >= 0) {
+ ast_queue_control(c, pvt->newcontrol);
+ pvt->newcontrol = -1;
+ }
+ if (pvt->newdigit >= 0) {
+ struct ast_frame f = {
+ .frametype = AST_FRAME_DTMF_END,
+ .subclass = pvt->newdigit,
+ .samples = pvt->newduration * 8,
+ .len = pvt->newduration,
+ .src = "UPDATE_INFO",
+ };
+ if (pvt->newdigit == ' ') { /* signalUpdate message */
+ f.subclass = pvt->curDTMF;
+ if (pvt->DTMFsched >= 0) {
+ ast_sched_del(sched, pvt->DTMFsched);
+ pvt->DTMFsched = -1;
+ }
+ } else { /* Regular input or signal message */
+ if (pvt->newduration) { /* This is a signal, signalUpdate follows */
+ f.frametype = AST_FRAME_DTMF_BEGIN;
+ if (pvt->DTMFsched >= 0)
+ ast_sched_del(sched, pvt->DTMFsched);
+ pvt->DTMFsched = ast_sched_add(sched, pvt->newduration, oh323_simulate_dtmf_end, pvt);
+ if (h323debug)
+ ast_log(LOG_DTMF, "Scheduled DTMF END simulation for %d ms, id=%d\n", pvt->newduration, pvt->DTMFsched);
+ }
+ pvt->curDTMF = pvt->newdigit;
+ }
+ ast_queue_frame(c, &f);
+ pvt->newdigit = -1;
+ }
+ if (pvt->update_rtp_info > 0) {
+ if (pvt->rtp) {
+ ast_jb_configure(c, &global_jbconf);
+ ast_channel_set_fd(c, 0, ast_rtp_fd(pvt->rtp));
+ ast_channel_set_fd(c, 1, ast_rtcp_fd(pvt->rtp));
+ ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */
+ }
+ pvt->update_rtp_info = -1;
+ }
+}
+
+/*! \brief Only channel structure should be locked */
+static void oh323_update_info(struct ast_channel *c)
+{
+ struct oh323_pvt *pvt = c->tech_pvt;
+
+ if (pvt) {
+ ast_mutex_lock(&pvt->lock);
+ __oh323_update_info(c, pvt);
+ ast_mutex_unlock(&pvt->lock);
+ }
+}
+
+static void cleanup_call_details(call_details_t *cd)
+{
+ if (cd->call_token) {
+ ast_free(cd->call_token);
+ cd->call_token = NULL;
+ }
+ if (cd->call_source_aliases) {
+ ast_free(cd->call_source_aliases);
+ cd->call_source_aliases = NULL;
+ }
+ if (cd->call_dest_alias) {
+ ast_free(cd->call_dest_alias);
+ cd->call_dest_alias = NULL;
+ }
+ if (cd->call_source_name) {
+ ast_free(cd->call_source_name);
+ cd->call_source_name = NULL;
+ }
+ if (cd->call_source_e164) {
+ ast_free(cd->call_source_e164);
+ cd->call_source_e164 = NULL;
+ }
+ if (cd->call_dest_e164) {
+ ast_free(cd->call_dest_e164);
+ cd->call_dest_e164 = NULL;
+ }
+ if (cd->sourceIp) {
+ ast_free(cd->sourceIp);
+ cd->sourceIp = NULL;
+ }
+ if (cd->redirect_number) {
+ ast_free(cd->redirect_number);
+ cd->redirect_number = NULL;
+ }
+}
+
+static void __oh323_destroy(struct oh323_pvt *pvt)
+{
+ struct oh323_pvt *cur, *prev = NULL;
+
+ if (pvt->DTMFsched >= 0) {
+ ast_sched_del(sched, pvt->DTMFsched);
+ pvt->DTMFsched = -1;
+ }
+
+ if (pvt->rtp) {
+ ast_rtp_destroy(pvt->rtp);
+ }
+
+ /* Free dsp used for in-band DTMF detection */
+ if (pvt->vad) {
+ ast_dsp_free(pvt->vad);
+ }
+ cleanup_call_details(&pvt->cd);
+
+ /* Unlink us from the owner if we have one */
+ if (pvt->owner) {
+ ast_channel_lock(pvt->owner);
+ if (h323debug)
+ ast_debug(1, "Detaching from %s\n", pvt->owner->name);
+ pvt->owner->tech_pvt = NULL;
+ ast_channel_unlock(pvt->owner);
+ }
+ cur = iflist;
+ while(cur) {
+ if (cur == pvt) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ iflist = cur->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ if (!cur) {
+ ast_log(LOG_WARNING, "%p is not in list?!?! \n", cur);
+ } else {
+ ast_mutex_unlock(&pvt->lock);
+ ast_mutex_destroy(&pvt->lock);
+ ast_free(pvt);
+ }
+}
+
+static void oh323_destroy(struct oh323_pvt *pvt)
+{
+ if (h323debug) {
+ ast_debug(1, "Destroying channel %s\n", (pvt->owner ? pvt->owner->name : "<unknown>"));
+ }
+ ast_mutex_lock(&iflock);
+ ast_mutex_lock(&pvt->lock);
+ __oh323_destroy(pvt);
+ ast_mutex_unlock(&iflock);
+}
+
+static int oh323_digit_begin(struct ast_channel *c, char digit)
+{
+ struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
+ char *token;
+
+ if (!pvt) {
+ ast_log(LOG_ERROR, "No private structure?! This is bad\n");
+ return -1;
+ }
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->rtp &&
+ (((pvt->options.dtmfmode & H323_DTMF_RFC2833) && pvt->dtmf_pt[0])
+ /*|| ((pvt->options.dtmfmode & H323_DTMF_CISCO) && pvt->dtmf_pt[1]))*/)) {
+ /* out-of-band DTMF */
+ if (h323debug) {
+ ast_log(LOG_DTMF, "Begin sending out-of-band digit %c on %s\n", digit, c->name);
+ }
+ ast_rtp_senddigit_begin(pvt->rtp, digit);
+ ast_mutex_unlock(&pvt->lock);
+ } else if (pvt->txDtmfDigit != digit) {
+ /* in-band DTMF */
+ if (h323debug) {
+ ast_log(LOG_DTMF, "Begin sending inband digit %c on %s\n", digit, c->name);
+ }
+ pvt->txDtmfDigit = digit;
+ token = pvt->cd.call_token ? ast_strdup(pvt->cd.call_token) : NULL;
+ ast_mutex_unlock(&pvt->lock);
+ h323_send_tone(token, digit);
+ if (token) {
+ ast_free(token);
+ }
+ } else
+ ast_mutex_unlock(&pvt->lock);
+ oh323_update_info(c);
+ return 0;
+}
+
+/*! \brief
+ * Send (play) the specified digit to the channel.
+ *
+ */
+static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+ struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
+ char *token;
+
+ if (!pvt) {
+ ast_log(LOG_ERROR, "No private structure?! This is bad\n");
+ return -1;
+ }
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->rtp && (pvt->options.dtmfmode & H323_DTMF_RFC2833) && ((pvt->dtmf_pt[0] > 0) || (pvt->dtmf_pt[0] > 0))) {
+ /* out-of-band DTMF */
+ if (h323debug) {
+ ast_log(LOG_DTMF, "End sending out-of-band digit %c on %s, duration %d\n", digit, c->name, duration);
+ }
+ ast_rtp_senddigit_end(pvt->rtp, digit);
+ ast_mutex_unlock(&pvt->lock);
+ } else {
+ /* in-band DTMF */
+ if (h323debug) {
+ ast_log(LOG_DTMF, "End sending inband digit %c on %s, duration %d\n", digit, c->name, duration);
+ }
+ pvt->txDtmfDigit = ' ';
+ token = pvt->cd.call_token ? ast_strdup(pvt->cd.call_token) : NULL;
+ ast_mutex_unlock(&pvt->lock);
+ h323_send_tone(token, ' ');
+ if (token) {
+ ast_free(token);
+ }
+ }
+ oh323_update_info(c);
+ return 0;
+}
+
+/*! \brief
+ * Make a call over the specified channel to the specified
+ * destination.
+ * Returns -1 on error, 0 on success.
+ */
+static int oh323_call(struct ast_channel *c, char *dest, int timeout)
+{
+ int res = 0;
+ struct oh323_pvt *pvt = (struct oh323_pvt *)c->tech_pvt;
+ const char *addr;
+ char called_addr[1024];
+
+ if (h323debug) {
+ ast_debug(1, "Calling to %s on %s\n", dest, c->name);
+ }
+ if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "Line is already in use (%s)\n", c->name);
+ return -1;
+ }
+ ast_mutex_lock(&pvt->lock);
+ if (!gatekeeper_disable) {
+ if (ast_strlen_zero(pvt->exten)) {
+ ast_copy_string(called_addr, dest, sizeof(called_addr));
+ } else {
+ snprintf(called_addr, sizeof(called_addr), "%s@%s", pvt->exten, dest);
+ }
+ } else {
+ res = htons(pvt->sa.sin_port);
+ addr = ast_inet_ntoa(pvt->sa.sin_addr);
+ if (ast_strlen_zero(pvt->exten)) {
+ snprintf(called_addr, sizeof(called_addr), "%s:%d", addr, res);
+ } else {
+ snprintf(called_addr, sizeof(called_addr), "%s@%s:%d", pvt->exten, addr, res);
+ }
+ }
+ /* make sure null terminated */
+ called_addr[sizeof(called_addr) - 1] = '\0';
+
+ if (c->cid.cid_num)
+ ast_copy_string(pvt->options.cid_num, c->cid.cid_num, sizeof(pvt->options.cid_num));
+
+ if (c->cid.cid_name)
+ ast_copy_string(pvt->options.cid_name, c->cid.cid_name, sizeof(pvt->options.cid_name));
+
+ if (c->cid.cid_rdnis) {
+ ast_copy_string(pvt->options.cid_rdnis, c->cid.cid_rdnis, sizeof(pvt->options.cid_rdnis));
+ }
+
+ pvt->options.presentation = c->cid.cid_pres;
+ pvt->options.type_of_number = c->cid.cid_ton;
+
+ if ((addr = pbx_builtin_getvar_helper(c, "PRIREDIRECTREASON"))) {
+ if (!strcasecmp(addr, "UNKNOWN"))
+ pvt->options.redirect_reason = 0;
+ else if (!strcasecmp(addr, "BUSY"))
+ pvt->options.redirect_reason = 1;
+ else if (!strcasecmp(addr, "NO_REPLY"))
+ pvt->options.redirect_reason = 2;
+ else if (!strcasecmp(addr, "UNCONDITIONAL"))
+ pvt->options.redirect_reason = 15;
+ else
+ pvt->options.redirect_reason = -1;
+ } else
+ pvt->options.redirect_reason = -1;
+
+ pvt->options.transfer_capability = c->transfercapability;
+
+ /* indicate that this is an outgoing call */
+ pvt->outgoing = 1;
+
+ ast_verb(3, "Requested transfer capability: 0x%.2x - %s\n", c->transfercapability, ast_transfercapability2str(c->transfercapability));
+ if (h323debug)
+ ast_debug(1, "Placing outgoing call to %s, %d/%d\n", called_addr, pvt->options.dtmfcodec[0], pvt->options.dtmfcodec[1]);
+ ast_mutex_unlock(&pvt->lock);
+ res = h323_make_call(called_addr, &(pvt->cd), &pvt->options);
+ if (res) {
+ ast_log(LOG_NOTICE, "h323_make_call failed(%s)\n", c->name);
+ return -1;
+ }
+ oh323_update_info(c);
+ return 0;
+}
+
+static int oh323_answer(struct ast_channel *c)
+{
+ int res;
+ struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
+ char *token;
+
+ if (h323debug)
+ ast_debug(1, "Answering on %s\n", c->name);
+
+ ast_mutex_lock(&pvt->lock);
+ token = pvt->cd.call_token ? ast_strdup(pvt->cd.call_token) : NULL;
+ ast_mutex_unlock(&pvt->lock);
+ res = h323_answering_call(token, 0);
+ if (token)
+ ast_free(token);
+
+ oh323_update_info(c);
+ if (c->_state != AST_STATE_UP) {
+ ast_setstate(c, AST_STATE_UP);
+ }
+ return res;
+}
+
+static int oh323_hangup(struct ast_channel *c)
+{
+ struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
+ int q931cause = AST_CAUSE_NORMAL_CLEARING;
+ char *call_token;
+
+
+ if (h323debug)
+ ast_debug(1, "Hanging up and scheduling destroy of call %s\n", c->name);
+
+ if (!c->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ ast_mutex_lock(&pvt->lock);
+ /* Determine how to disconnect */
+ if (pvt->owner != c) {
+ ast_log(LOG_WARNING, "Huh? We aren't the owner?\n");
+ ast_mutex_unlock(&pvt->lock);
+ return 0;
+ }
+
+ pvt->owner = NULL;
+ c->tech_pvt = NULL;
+
+ if (c->hangupcause) {
+ q931cause = c->hangupcause;
+ } else {
+ const char *cause = pbx_builtin_getvar_helper(c, "DIALSTATUS");
+ if (cause) {
+ if (!strcmp(cause, "CONGESTION")) {
+ q931cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
+ } else if (!strcmp(cause, "BUSY")) {
+ q931cause = AST_CAUSE_USER_BUSY;
+ } else if (!strcmp(cause, "CHANISUNVAIL")) {
+ q931cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+ } else if (!strcmp(cause, "NOANSWER")) {
+ q931cause = AST_CAUSE_NO_ANSWER;
+ } else if (!strcmp(cause, "CANCEL")) {
+ q931cause = AST_CAUSE_CALL_REJECTED;
+ }
+ }
+ }
+
+ /* Start the process if it's not already started */
+ if (!pvt->alreadygone && !pvt->hangupcause) {
+ call_token = pvt->cd.call_token ? ast_strdup(pvt->cd.call_token) : NULL;
+ if (call_token) {
+ /* Release lock to eliminate deadlock */
+ ast_mutex_unlock(&pvt->lock);
+ if (h323_clear_call(call_token, q931cause)) {
+ ast_log(LOG_WARNING, "ClearCall failed.\n");
+ }
+ ast_free(call_token);
+ ast_mutex_lock(&pvt->lock);
+ }
+ }
+ pvt->needdestroy = 1;
+ ast_mutex_unlock(&pvt->lock);
+
+ /* Update usage counter */
+ ast_module_unref(ast_module_info->self);
+
+ return 0;
+}
+
+/*! \brief Retrieve audio/etc from channel. Assumes pvt->lock is already held. */
+static struct ast_frame *oh323_rtp_read(struct oh323_pvt *pvt)
+{
+ struct ast_frame *f;
+
+ /* Only apply it for the first packet, we just need the correct ip/port */
+ if (pvt->options.nat) {
+ ast_rtp_setnat(pvt->rtp, pvt->options.nat);
+ pvt->options.nat = 0;
+ }
+
+ f = ast_rtp_read(pvt->rtp);
+ /* Don't send RFC2833 if we're not supposed to */
+ if (f && (f->frametype == AST_FRAME_DTMF) && !(pvt->options.dtmfmode & (H323_DTMF_RFC2833 | H323_DTMF_CISCO))) {
+ return &ast_null_frame;
+ }
+ if (pvt->owner) {
+ /* We already hold the channel lock */
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass != pvt->owner->nativeformats) {
+ /* Try to avoid deadlock */
+ if (ast_channel_trylock(pvt->owner)) {
+ ast_log(LOG_NOTICE, "Format changed but channel is locked. Ignoring frame...\n");
+ return &ast_null_frame;
+ }
+ if (h323debug)
+ ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
+ pvt->owner->nativeformats = f->subclass;
+ pvt->nativeformats = f->subclass;
+ ast_set_read_format(pvt->owner, pvt->owner->readformat);
+ ast_set_write_format(pvt->owner, pvt->owner->writeformat);
+ ast_channel_unlock(pvt->owner);
+ }
+ /* Do in-band DTMF detection */
+ if ((pvt->options.dtmfmode & H323_DTMF_INBAND) && pvt->vad) {
+ if ((pvt->nativeformats & (AST_FORMAT_SLINEAR | AST_FORMAT_ALAW | AST_FORMAT_ULAW))) {
+ if (!ast_channel_trylock(pvt->owner)) {
+ f = ast_dsp_process(pvt->owner, pvt->vad, f);
+ ast_channel_unlock(pvt->owner);
+ }
+ else
+ ast_log(LOG_NOTICE, "Unable to process inband DTMF while channel is locked\n");
+ } else if (pvt->nativeformats && !pvt->noInbandDtmf) {
+ ast_log(LOG_NOTICE, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(f->subclass));
+ pvt->noInbandDtmf = 1;
+ }
+ if (f &&(f->frametype == AST_FRAME_DTMF)) {
+ if (h323debug)
+ ast_log(LOG_DTMF, "Received in-band digit %c.\n", f->subclass);
+ }
+ }
+ }
+ }
+ return f;
+}
+
+static struct ast_frame *oh323_read(struct ast_channel *c)
+{
+ struct ast_frame *fr;
+ struct oh323_pvt *pvt = (struct oh323_pvt *)c->tech_pvt;
+ ast_mutex_lock(&pvt->lock);
+ __oh323_update_info(c, pvt);
+ switch(c->fdno) {
+ case 0:
+ fr = oh323_rtp_read(pvt);
+ break;
+ case 1:
+ if (pvt->rtp)
+ fr = ast_rtcp_read(pvt->rtp);
+ else
+ fr = &ast_null_frame;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unable to handle fd %d on channel %s\n", c->fdno, c->name);
+ fr = &ast_null_frame;
+ break;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ return fr;
+}
+
+static int oh323_write(struct ast_channel *c, struct ast_frame *frame)
+{
+ struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
+ int res = 0;
+ if (frame->frametype != AST_FRAME_VOICE) {
+ if (frame->frametype == AST_FRAME_IMAGE) {
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Can't send %d type frames with H323 write\n", frame->frametype);
+ return 0;
+ }
+ } else {
+ if (!(frame->subclass & c->nativeformats)) {
+ ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
+ frame->subclass, c->nativeformats, c->readformat, c->writeformat);
+ return 0;
+ }
+ }
+ if (pvt) {
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->rtp && !pvt->recvonly)
+ res = ast_rtp_write(pvt->rtp, frame);
+ __oh323_update_info(c, pvt);
+ ast_mutex_unlock(&pvt->lock);
+ }
+ return res;
+}
+
+static int oh323_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen)
+{
+
+ struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
+ char *token = (char *)NULL;
+ int res = -1;
+ int got_progress;
+
+ ast_mutex_lock(&pvt->lock);
+ token = (pvt->cd.call_token ? ast_strdup(pvt->cd.call_token) : NULL);
+ got_progress = pvt->got_progress;
+ if (condition == AST_CONTROL_PROGRESS)
+ pvt->got_progress = 1;
+ else if ((condition == AST_CONTROL_BUSY) || (condition == AST_CONTROL_CONGESTION))
+ pvt->alreadygone = 1;
+ ast_mutex_unlock(&pvt->lock);
+
+ if (h323debug)
+ ast_debug(1, "OH323: Indicating %d on %s (%s)\n", condition, token, c->name);
+
+ switch(condition) {
+ case AST_CONTROL_RINGING:
+ if (c->_state == AST_STATE_RING || c->_state == AST_STATE_RINGING) {
+ h323_send_alerting(token);
+ res = (got_progress ? 0 : -1); /* Do not simulate any audio tones if we got PROGRESS message */
+ }
+ break;
+ case AST_CONTROL_PROGRESS:
+ if (c->_state != AST_STATE_UP) {
+ /* Do not send PROGRESS message more than once */
+ if (!got_progress)
+ h323_send_progress(token);
+ res = 0;
+ }
+ break;
+ case AST_CONTROL_BUSY:
+ if (c->_state != AST_STATE_UP) {
+ h323_answering_call(token, 1);
+ ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV);
+ res = 0;
+ }
+ break;
+ case AST_CONTROL_CONGESTION:
+ if (c->_state != AST_STATE_UP) {
+ h323_answering_call(token, 1);
+ ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV);
+ res = 0;
+ }
+ break;
+ case AST_CONTROL_HOLD:
+ h323_hold_call(token, 1);
+ /* We should start MOH only if remote party isn't provide audio for us */
+ ast_moh_start(c, data, NULL);
+ res = 0;
+ break;
+ case AST_CONTROL_UNHOLD:
+ h323_hold_call(token, 0);
+ ast_moh_stop(c);
+ res = 0;
+ break;
+ case AST_CONTROL_PROCEEDING:
+ case -1:
+ break;
+ default:
+ ast_log(LOG_WARNING, "OH323: Don't know how to indicate condition %d on %s\n", condition, token);
+ break;
+ }
+
+ if (h323debug)
+ ast_debug(1, "OH323: Indicated %d on %s, res=%d\n", condition, token, res);
+ if (token)
+ ast_free(token);
+ oh323_update_info(c);
+
+ return res;
+}
+
+static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct oh323_pvt *pvt = (struct oh323_pvt *) newchan->tech_pvt;
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->owner != oldchan) {
+ ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, pvt->owner);
+ return -1;
+ }
+ pvt->owner = newchan;
+ ast_mutex_unlock(&pvt->lock);
+ return 0;
+}
+
+static int __oh323_rtp_create(struct oh323_pvt *pvt)
+{
+ struct in_addr our_addr;
+
+ if (pvt->rtp)
+ return 0;
+
+ if (ast_find_ourip(&our_addr, bindaddr)) {
+ ast_mutex_unlock(&pvt->lock);
+ ast_log(LOG_ERROR, "Unable to locate local IP address for RTP stream\n");
+ return -1;
+ }
+ pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, our_addr);
+ if (!pvt->rtp) {
+ ast_mutex_unlock(&pvt->lock);
+ ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno));
+ return -1;
+ }
+ if (h323debug)
+ ast_debug(1, "Created RTP channel\n");
+
+ ast_rtp_setqos(pvt->rtp, tos, cos, "H323 RTP");
+
+ if (h323debug)
+ ast_debug(1, "Setting NAT on RTP to %d\n", pvt->options.nat);
+ ast_rtp_setnat(pvt->rtp, pvt->options.nat);
+
+ if (pvt->dtmf_pt[0] > 0)
+ ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[0], "audio", "telephone-event", 0);
+ if (pvt->dtmf_pt[1] > 0)
+ ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[1], "audio", "cisco-telephone-event", 0);
+
+ if (pvt->peercapability)
+ ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs);
+
+ if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
+ ast_jb_configure(pvt->owner, &global_jbconf);
+ ast_channel_set_fd(pvt->owner, 0, ast_rtp_fd(pvt->rtp));
+ ast_channel_set_fd(pvt->owner, 1, ast_rtcp_fd(pvt->rtp));
+ ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */
+ ast_channel_unlock(pvt->owner);
+ } else
+ pvt->update_rtp_info = 1;
+
+ return 0;
+}
+
+/*! \brief Private structure should be locked on a call */
+static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const char *host)
+{
+ struct ast_channel *ch;
+ char *cid_num, *cid_name;
+ int fmt;
+
+ if (!ast_strlen_zero(pvt->options.cid_num))
+ cid_num = pvt->options.cid_num;
+ else
+ cid_num = pvt->cd.call_source_e164;
+
+ if (!ast_strlen_zero(pvt->options.cid_name))
+ cid_name = pvt->options.cid_name;
+ else
+ cid_name = pvt->cd.call_source_name;
+
+ /* Don't hold a oh323_pvt lock while we allocate a chanel */
+ ast_mutex_unlock(&pvt->lock);
+ ch = ast_channel_alloc(1, state, cid_num, cid_name, pvt->accountcode, pvt->exten, pvt->context, pvt->amaflags, "H323/%s", host);
+ /* Update usage counter */
+ ast_module_ref(ast_module_info->self);
+ ast_mutex_lock(&pvt->lock);
+ if (ch) {
+ ch->tech = &oh323_tech;
+ if (!(fmt = pvt->jointcapability) && !(fmt = pvt->options.capability))
+ fmt = global_options.capability;
+ ch->nativeformats = ast_codec_choose(&pvt->options.prefs, fmt, 1)/* | (pvt->jointcapability & AST_FORMAT_VIDEO_MASK)*/;
+ pvt->nativeformats = ch->nativeformats;
+ fmt = ast_best_codec(ch->nativeformats);
+ ch->writeformat = fmt;
+ ch->rawwriteformat = fmt;
+ ch->readformat = fmt;
+ ch->rawreadformat = fmt;
+#if 0
+ ast_channel_set_fd(ch, 0, ast_rtp_fd(pvt->rtp));
+ ast_channel_set_fd(ch, 1, ast_rtcp_fd(pvt->rtp));
+#endif
+#ifdef VIDEO_SUPPORT
+ if (pvt->vrtp) {
+ ast_channel_set_fd(ch, 2, ast_rtp_fd(pvt->vrtp));
+ ast_channel_set_fd(ch, 3, ast_rtcp_fd(pvt->vrtp));
+ }
+#endif
+#ifdef T38_SUPPORT
+ if (pvt->udptl) {
+ ast_channel_set_fd(ch, 4, ast_udptl_fd(pvt->udptl));
+ }
+#endif
+ if (state == AST_STATE_RING) {
+ ch->rings = 1;
+ }
+ /* Allocate dsp for in-band DTMF support */
+ if (pvt->options.dtmfmode & H323_DTMF_INBAND) {
+ pvt->vad = ast_dsp_new();
+ ast_dsp_set_features(pvt->vad, DSP_FEATURE_DTMF_DETECT);
+ }
+ /* Register channel functions. */
+ ch->tech_pvt = pvt;
+ /* Set the owner of this channel */
+ pvt->owner = ch;
+
+ ast_copy_string(ch->context, pvt->context, sizeof(ch->context));
+ ast_copy_string(ch->exten, pvt->exten, sizeof(ch->exten));
+ ch->priority = 1;
+ if (!ast_strlen_zero(pvt->accountcode)) {
+ ast_string_field_set(ch, accountcode, pvt->accountcode);
+ }
+ if (pvt->amaflags) {
+ ch->amaflags = pvt->amaflags;
+ }
+
+ /* Don't use ast_set_callerid() here because it will
+ * generate a needless NewCallerID event */
+ ch->cid.cid_ani = ast_strdup(cid_num);
+
+ if (pvt->cd.redirect_reason >= 0) {
+ ch->cid.cid_rdnis = ast_strdup(pvt->cd.redirect_number);
+ pbx_builtin_setvar_helper(ch, "PRIREDIRECTREASON", redirectingreason2str(pvt->cd.redirect_reason));
+ }
+ ch->cid.cid_pres = pvt->cd.presentation;
+ ch->cid.cid_ton = pvt->cd.type_of_number;
+
+ if (!ast_strlen_zero(pvt->exten) && strcmp(pvt->exten, "s")) {
+ ch->cid.cid_dnid = ast_strdup(pvt->exten);
+ }
+ if (pvt->cd.transfer_capability >= 0)
+ ch->transfercapability = pvt->cd.transfer_capability;
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(ch)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ch->name);
+ ast_hangup(ch);
+ ch = NULL;
+ }
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ }
+ return ch;
+}
+
+static struct oh323_pvt *oh323_alloc(int callid)
+{
+ struct oh323_pvt *pvt;
+
+ pvt = ast_calloc(1, sizeof(*pvt));
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Couldn't allocate private structure. This is bad\n");
+ return NULL;
+ }
+ pvt->cd.redirect_reason = -1;
+ pvt->cd.transfer_capability = -1;
+ /* Ensure the call token is allocated for outgoing call */
+ if (!callid) {
+ if ((pvt->cd).call_token == NULL) {
+ (pvt->cd).call_token = ast_calloc(1, 128);
+ }
+ if (!pvt->cd.call_token) {
+ ast_log(LOG_ERROR, "Not enough memory to alocate call token\n");
+ ast_rtp_destroy(pvt->rtp);
+ ast_free(pvt);
+ return NULL;
+ }
+ memset((char *)(pvt->cd).call_token, 0, 128);
+ pvt->cd.call_reference = callid;
+ }
+ memcpy(&pvt->options, &global_options, sizeof(pvt->options));
+ pvt->jointcapability = pvt->options.capability;
+ if (pvt->options.dtmfmode & (H323_DTMF_RFC2833 | H323_DTMF_CISCO)) {
+ pvt->nonCodecCapability |= AST_RTP_DTMF;
+ } else {
+ pvt->nonCodecCapability &= ~AST_RTP_DTMF;
+ }
+ ast_copy_string(pvt->context, default_context, sizeof(pvt->context));
+ pvt->newstate = pvt->newcontrol = pvt->newdigit = pvt->update_rtp_info = pvt->DTMFsched = -1;
+ ast_mutex_init(&pvt->lock);
+ /* Add to interface list */
+ ast_mutex_lock(&iflock);
+ pvt->next = iflist;
+ iflist = pvt;
+ ast_mutex_unlock(&iflock);
+ return pvt;
+}
+
+static struct oh323_pvt *find_call_locked(int call_reference, const char *token)
+{
+ struct oh323_pvt *pvt;
+
+ ast_mutex_lock(&iflock);
+ pvt = iflist;
+ while(pvt) {
+ if (!pvt->needdestroy && ((signed int)pvt->cd.call_reference == call_reference)) {
+ /* Found the call */
+ if ((token != NULL) && (!strcmp(pvt->cd.call_token, token))) {
+ ast_mutex_lock(&pvt->lock);
+ ast_mutex_unlock(&iflock);
+ return pvt;
+ } else if (token == NULL) {
+ ast_log(LOG_WARNING, "Call Token is NULL\n");
+ ast_mutex_lock(&pvt->lock);
+ ast_mutex_unlock(&iflock);
+ return pvt;
+ }
+ }
+ pvt = pvt->next;
+ }
+ ast_mutex_unlock(&iflock);
+ return NULL;
+}
+
+static int update_state(struct oh323_pvt *pvt, int state, int signal)
+{
+ if (!pvt)
+ return 0;
+ if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
+ if (state >= 0)
+ ast_setstate(pvt->owner, state);
+ if (signal >= 0)
+ ast_queue_control(pvt->owner, signal);
+ ast_channel_unlock(pvt->owner);
+ return 1;
+ }
+ else {
+ if (state >= 0)
+ pvt->newstate = state;
+ if (signal >= 0)
+ pvt->newcontrol = signal;
+ return 0;
+ }
+}
+
+static struct oh323_alias *build_alias(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime)
+{
+ struct oh323_alias *alias;
+ int found = 0;
+
+ alias = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&aliasl, name, name, 0, 0, strcasecmp);
+
+ if (alias)
+ found++;
+ else {
+ if (!(alias = ast_calloc(1, sizeof(*alias))))
+ return NULL;
+ ASTOBJ_INIT(alias);
+ }
+ if (!found && name)
+ ast_copy_string(alias->name, name, sizeof(alias->name));
+ for (; v || ((v = alt) && !(alt = NULL)); v = v->next) {
+ if (!strcasecmp(v->name, "e164")) {
+ ast_copy_string(alias->e164, v->value, sizeof(alias->e164));
+ } else if (!strcasecmp(v->name, "prefix")) {
+ ast_copy_string(alias->prefix, v->value, sizeof(alias->prefix));
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(alias->context, v->value, sizeof(alias->context));
+ } else if (!strcasecmp(v->name, "secret")) {
+ ast_copy_string(alias->secret, v->value, sizeof(alias->secret));
+ } else {
+ if (strcasecmp(v->value, "h323")) {
+ ast_log(LOG_WARNING, "Keyword %s does not make sense in type=h323\n", v->name);
+ }
+ }
+ }
+ ASTOBJ_UNMARK(alias);
+ return alias;
+}
+
+static struct oh323_alias *realtime_alias(const char *alias)
+{
+ struct ast_variable *var, *tmp;
+ struct oh323_alias *a;
+
+ var = ast_load_realtime("h323", "name", alias, NULL);
+
+ if (!var)
+ return NULL;
+
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "type") &&
+ !(!strcasecmp(tmp->value, "alias") || !strcasecmp(tmp->value, "h323"))) {
+ ast_variables_destroy(var);
+ return NULL;
+ }
+ }
+
+ a = build_alias(alias, var, NULL, 1);
+
+ ast_variables_destroy(var);
+
+ return a;
+}
+
+static int update_common_options(struct ast_variable *v, struct call_options *options)
+{
+ int tmp;
+ char *val, *opt;
+
+ if (!strcasecmp(v->name, "allow")) {
+ ast_parse_allow_disallow(&options->prefs, &options->capability, v->value, 1);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&options->prefs, &options->capability, v->value, 0);
+ } else if (!strcasecmp(v->name, "dtmfmode")) {
+ val = ast_strdupa(v->value);
+ if ((opt = strchr(val, ':')) != (char *)NULL) {
+ *opt++ = '\0';
+ tmp = atoi(opt);
+ }
+ if (!strcasecmp(v->value, "inband")) {
+ options->dtmfmode |= H323_DTMF_INBAND;
+ } else if (!strcasecmp(val, "rfc2833")) {
+ options->dtmfmode |= H323_DTMF_RFC2833;
+ if (!opt) {
+ options->dtmfcodec[0] = H323_DTMF_RFC2833_PT;
+ } else if ((tmp >= 96) && (tmp < 128)) {
+ options->dtmfcodec[0] = tmp;
+ } else {
+ options->dtmfcodec[0] = H323_DTMF_RFC2833_PT;
+ ast_log(LOG_WARNING, "Unknown rfc2833 payload %s specified at line %d, using default %d\n", opt, v->lineno, options->dtmfcodec[0]);
+ }
+ } else if (!strcasecmp(val, "cisco")) {
+ options->dtmfmode |= H323_DTMF_CISCO;
+ if (!opt) {
+ options->dtmfcodec[1] = H323_DTMF_CISCO_PT;
+ } else if ((tmp >= 96) && (tmp < 128)) {
+ options->dtmfcodec[1] = tmp;
+ } else {
+ options->dtmfcodec[1] = H323_DTMF_CISCO_PT;
+ ast_log(LOG_WARNING, "Unknown Cisco DTMF payload %s specified at line %d, using default %d\n", opt, v->lineno, options->dtmfcodec[1]);
+ }
+ } else if (!strcasecmp(v->value, "h245-signal")) {
+ options->dtmfmode |= H323_DTMF_SIGNAL;
+ } else {
+ ast_log(LOG_WARNING, "Unknown dtmf mode '%s' at line %d\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "dtmfcodec")) {
+ ast_log(LOG_NOTICE, "Option %s at line %d is deprecated. Use dtmfmode=rfc2833[:<payload>] instead.\n", v->name, v->lineno);
+ tmp = atoi(v->value);
+ if (tmp < 96)
+ ast_log(LOG_WARNING, "Invalid %s value %s at line %d\n", v->name, v->value, v->lineno);
+ else
+ options->dtmfcodec[0] = tmp;
+ } else if (!strcasecmp(v->name, "bridge")) {
+ options->bridge = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "nat")) {
+ options->nat = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "fastStart")) {
+ options->fastStart = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "h245Tunneling")) {
+ options->h245Tunneling = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "silenceSuppression")) {
+ options->silenceSuppression = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "progress_setup")) {
+ tmp = atoi(v->value);
+ if ((tmp != 0) && (tmp != 1) && (tmp != 3) && (tmp != 8)) {
+ ast_log(LOG_WARNING, "Invalid value %s for %s at line %d, assuming 0\n", v->value, v->name, v->lineno);
+ tmp = 0;
+ }
+ options->progress_setup = tmp;
+ } else if (!strcasecmp(v->name, "progress_alert")) {
+ tmp = atoi(v->value);
+ if ((tmp != 0) && (tmp != 1) && (tmp != 8)) {
+ ast_log(LOG_WARNING, "Invalid value %s for %s at line %d, assuming 0\n", v->value, v->name, v->lineno);
+ tmp = 0;
+ }
+ options->progress_alert = tmp;
+ } else if (!strcasecmp(v->name, "progress_audio")) {
+ options->progress_audio = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callerid")) {
+ ast_callerid_split(v->value, options->cid_name, sizeof(options->cid_name), options->cid_num, sizeof(options->cid_num));
+ } else if (!strcasecmp(v->name, "fullname")) {
+ ast_copy_string(options->cid_name, v->value, sizeof(options->cid_name));
+ } else if (!strcasecmp(v->name, "cid_number")) {
+ ast_copy_string(options->cid_num, v->value, sizeof(options->cid_num));
+ } else if (!strcasecmp(v->name, "tunneling")) {
+ if (!strcasecmp(v->value, "none"))
+ options->tunnelOptions = 0;
+ else if (!strcasecmp(v->value, "cisco"))
+ options->tunnelOptions |= H323_TUNNEL_CISCO;
+ else if (!strcasecmp(v->value, "qsig"))
+ options->tunnelOptions |= H323_TUNNEL_QSIG;
+ else
+ ast_log(LOG_WARNING, "Invalid value %s for %s at line %d\n", v->value, v->name, v->lineno);
+ } else if (!strcasecmp(v->name, "hold")) {
+ if (!strcasecmp(v->value, "none"))
+ options->holdHandling = ~0;
+ else if (!strcasecmp(v->value, "notify"))
+ options->holdHandling |= H323_HOLD_NOTIFY;
+ else if (!strcasecmp(v->value, "q931only"))
+ options->holdHandling |= H323_HOLD_NOTIFY | H323_HOLD_Q931ONLY;
+ else if (!strcasecmp(v->value, "h450"))
+ options->holdHandling |= H323_HOLD_H450;
+ else
+ ast_log(LOG_WARNING, "Invalid value %s for %s at line %d\n", v->value, v->name, v->lineno);
+ } else
+ return 1;
+
+ return 0;
+}
+
+static struct oh323_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime)
+{
+ struct oh323_user *user;
+ struct ast_ha *oldha;
+ int found = 0;
+ int format;
+
+ user = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&userl, name, name, 0, 0, strcmp);
+
+ if (user)
+ found++;
+ else {
+ if (!(user = ast_calloc(1, sizeof(*user))))
+ return NULL;
+ ASTOBJ_INIT(user);
+ }
+ oldha = user->ha;
+ user->ha = (struct ast_ha *)NULL;
+ memcpy(&user->options, &global_options, sizeof(user->options));
+ user->options.dtmfmode = 0;
+ user->options.holdHandling = 0;
+ /* Set default context */
+ ast_copy_string(user->context, default_context, sizeof(user->context));
+ if (user && !found)
+ ast_copy_string(user->name, name, sizeof(user->name));
+
+#if 0 /* XXX Port channel variables functionality from chan_sip XXX */
+ if (user->chanvars) {
+ ast_variables_destroy(user->chanvars);
+ user->chanvars = NULL;
+ }
+#endif
+
+ for (; v || ((v = alt) && !(alt = NULL)); v = v->next) {
+ if (!update_common_options(v, &user->options))
+ continue;
+ if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(user->context, v->value, sizeof(user->context));
+ } else if (!strcasecmp(v->name, "secret")) {
+ ast_copy_string(user->secret, v->value, sizeof(user->secret));
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode));
+ } else if (!strcasecmp(v->name, "host")) {
+ if (!strcasecmp(v->value, "dynamic")) {
+ ast_log(LOG_ERROR, "A dynamic host on a type=user does not make any sense\n");
+ ASTOBJ_UNREF(user, oh323_destroy_user);
+ return NULL;
+ } else if (ast_get_ip(&user->addr, v->value)) {
+ ASTOBJ_UNREF(user, oh323_destroy_user);
+ return NULL;
+ }
+ /* Let us know we need to use ip authentication */
+ user->host = 1;
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ format = ast_cdr_amaflags2int(v->value);
+ if (format < 0) {
+ ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
+ } else {
+ user->amaflags = format;
+ }
+ } else if (!strcasecmp(v->name, "permit") ||
+ !strcasecmp(v->name, "deny")) {
+ int ha_error = 0;
+
+ user->ha = ast_append_ha(v->name, v->value, user->ha, &ha_error);
+ if (ha_error)
+ ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
+ }
+ }
+ if (!user->options.dtmfmode)
+ user->options.dtmfmode = global_options.dtmfmode;
+ if (user->options.holdHandling == ~0)
+ user->options.holdHandling = 0;
+ else if (!user->options.holdHandling)
+ user->options.holdHandling = global_options.holdHandling;
+ ASTOBJ_UNMARK(user);
+ ast_free_ha(oldha);
+ return user;
+}
+
+static struct oh323_user *realtime_user(const call_details_t *cd)
+{
+ struct ast_variable *var, *tmp;
+ struct oh323_user *user;
+ const char *username;
+
+ if (userbyalias)
+ var = ast_load_realtime("h323", "name", username = cd->call_source_aliases, NULL);
+ else {
+ username = (char *)NULL;
+ var = ast_load_realtime("h323", "host", cd->sourceIp, NULL);
+ }
+
+ if (!var)
+ return NULL;
+
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "type") &&
+ !(!strcasecmp(tmp->value, "user") || !strcasecmp(tmp->value, "friend"))) {
+ ast_variables_destroy(var);
+ return NULL;
+ } else if (!username && !strcasecmp(tmp->name, "name"))
+ username = tmp->value;
+ }
+
+ if (!username) {
+ ast_log(LOG_WARNING, "Cannot determine user name for IP address %s\n", cd->sourceIp);
+ ast_variables_destroy(var);
+ return NULL;
+ }
+
+ user = build_user(username, var, NULL, 1);
+
+ ast_variables_destroy(var);
+
+ return user;
+}
+
+static struct oh323_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime)
+{
+ struct oh323_peer *peer;
+ struct ast_ha *oldha;
+ int found = 0;
+
+ peer = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&peerl, name, name, 0, 0, strcmp);
+
+ if (peer)
+ found++;
+ else {
+ if (!(peer = ast_calloc(1, sizeof(*peer))))
+ return NULL;
+ ASTOBJ_INIT(peer);
+ }
+ oldha = peer->ha;
+ peer->ha = NULL;
+ memcpy(&peer->options, &global_options, sizeof(peer->options));
+ peer->options.dtmfmode = 0;
+ peer->options.holdHandling = 0;
+ peer->addr.sin_port = htons(h323_signalling_port);
+ peer->addr.sin_family = AF_INET;
+ if (!found && name)
+ ast_copy_string(peer->name, name, sizeof(peer->name));
+
+#if 0 /* XXX Port channel variables functionality from chan_sip XXX */
+ if (peer->chanvars) {
+ ast_variables_destroy(peer->chanvars);
+ peer->chanvars = NULL;
+ }
+#endif
+ /* Default settings for mailbox */
+ peer->mailbox[0] = '\0';
+
+ for (; v || ((v = alt) && !(alt = NULL)); v = v->next) {
+ if (!update_common_options(v, &peer->options))
+ continue;
+ if (!strcasecmp(v->name, "host")) {
+ if (!strcasecmp(v->value, "dynamic")) {
+ ast_log(LOG_ERROR, "Dynamic host configuration not implemented.\n");
+ ASTOBJ_UNREF(peer, oh323_destroy_peer);
+ return NULL;
+ }
+ if (ast_get_ip(&peer->addr, v->value)) {
+ ast_log(LOG_ERROR, "Could not determine IP for %s\n", v->value);
+ ASTOBJ_UNREF(peer, oh323_destroy_peer);
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "port")) {
+ peer->addr.sin_port = htons(atoi(v->value));
+ } else if (!strcasecmp(v->name, "permit") ||
+ !strcasecmp(v->name, "deny")) {
+ int ha_error = 0;
+
+ peer->ha = ast_append_ha(v->name, v->value, peer->ha, &ha_error);
+ if (ha_error)
+ ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
+ } else if (!strcasecmp(v->name, "mailbox")) {
+ ast_copy_string(peer->mailbox, v->value, sizeof(peer->mailbox));
+ }
+ }
+ if (!peer->options.dtmfmode)
+ peer->options.dtmfmode = global_options.dtmfmode;
+ if (peer->options.holdHandling == ~0)
+ peer->options.holdHandling = 0;
+ else if (!peer->options.holdHandling)
+ peer->options.holdHandling = global_options.holdHandling;
+ ASTOBJ_UNMARK(peer);
+ ast_free_ha(oldha);
+ return peer;
+}
+
+static struct oh323_peer *realtime_peer(const char *peername, struct sockaddr_in *sin)
+{
+ struct oh323_peer *peer;
+ struct ast_variable *var;
+ struct ast_variable *tmp;
+ const char *addr;
+
+ /* First check on peer name */
+ if (peername)
+ var = ast_load_realtime("h323", "name", peername, addr = NULL);
+ else if (sin) /* Then check on IP address for dynamic peers */
+ var = ast_load_realtime("h323", "host", addr = ast_inet_ntoa(sin->sin_addr), NULL);
+ else
+ return NULL;
+
+ if (!var)
+ return NULL;
+
+ for (tmp = var; tmp; tmp = tmp->next) {
+ /* If this is type=user, then skip this object. */
+ if (!strcasecmp(tmp->name, "type") &&
+ !(!strcasecmp(tmp->value, "peer") || !strcasecmp(tmp->value, "friend"))) {
+ ast_variables_destroy(var);
+ return NULL;
+ } else if (!peername && !strcasecmp(tmp->name, "name")) {
+ peername = tmp->value;
+ }
+ }
+
+ if (!peername) { /* Did not find peer in realtime */
+ ast_log(LOG_WARNING, "Cannot determine peer name for IP address %s\n", addr);
+ ast_variables_destroy(var);
+ return NULL;
+ }
+
+ /* Peer found in realtime, now build it in memory */
+ peer = build_peer(peername, var, NULL, 1);
+
+ ast_variables_destroy(var);
+
+ return peer;
+}
+
+static int oh323_addrcmp_str(struct in_addr inaddr, char *addr)
+{
+ return strcmp(ast_inet_ntoa(inaddr), addr);
+}
+
+static struct oh323_user *find_user(const call_details_t *cd, int realtime)
+{
+ struct oh323_user *u;
+
+ if (userbyalias)
+ u = ASTOBJ_CONTAINER_FIND(&userl, cd->call_source_aliases);
+ else
+ u = ASTOBJ_CONTAINER_FIND_FULL(&userl, cd->sourceIp, addr.sin_addr, 0, 0, oh323_addrcmp_str);
+
+ if (!u && realtime)
+ u = realtime_user(cd);
+
+ if (!u && h323debug)
+ ast_debug(1, "Could not find user by name %s or address %s\n", cd->call_source_aliases, cd->sourceIp);
+
+ return u;
+}
+
+static int oh323_addrcmp(struct sockaddr_in addr, struct sockaddr_in *sin)
+{
+ int res;
+
+ if (!sin)
+ res = -1;
+ else
+ res = inaddrcmp(&addr , sin);
+
+ return res;
+}
+
+static struct oh323_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime)
+{
+ struct oh323_peer *p;
+
+ if (peer)
+ p = ASTOBJ_CONTAINER_FIND(&peerl, peer);
+ else
+ p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, addr, 0, 0, oh323_addrcmp);
+
+ if (!p && realtime)
+ p = realtime_peer(peer, sin);
+
+ if (!p && h323debug)
+ ast_debug(1, "Could not find peer by name %s or address %s\n", (peer ? peer : "<NONE>"), (sin ? ast_inet_ntoa(sin->sin_addr) : "<NONE>"));
+
+ return p;
+}
+
+static int create_addr(struct oh323_pvt *pvt, char *opeer)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct oh323_peer *p;
+ int portno;
+ int found = 0;
+ char *port;
+ char *hostn;
+ char peer[256] = "";
+
+ ast_copy_string(peer, opeer, sizeof(peer));
+ port = strchr(peer, ':');
+ if (port) {
+ *port = '\0';
+ port++;
+ }
+ pvt->sa.sin_family = AF_INET;
+ p = find_peer(peer, NULL, 1);
+ if (p) {
+ found++;
+ memcpy(&pvt->options, &p->options, sizeof(pvt->options));
+ pvt->jointcapability = pvt->options.capability;
+ if (pvt->options.dtmfmode) {
+ if (pvt->options.dtmfmode & H323_DTMF_RFC2833) {
+ pvt->nonCodecCapability |= AST_RTP_DTMF;
+ } else {
+ pvt->nonCodecCapability &= ~AST_RTP_DTMF;
+ }
+ }
+ if (p->addr.sin_addr.s_addr) {
+ pvt->sa.sin_addr = p->addr.sin_addr;
+ pvt->sa.sin_port = p->addr.sin_port;
+ }
+ ASTOBJ_UNREF(p, oh323_destroy_peer);
+ }
+ if (!p && !found) {
+ hostn = peer;
+ if (port) {
+ portno = atoi(port);
+ } else {
+ portno = h323_signalling_port;
+ }
+ hp = ast_gethostbyname(hostn, &ahp);
+ if (hp) {
+ memcpy(&pvt->sa.sin_addr, hp->h_addr, sizeof(pvt->sa.sin_addr));
+ pvt->sa.sin_port = htons(portno);
+ /* Look peer by address */
+ p = find_peer(NULL, &pvt->sa, 1);
+ memcpy(&pvt->options, (p ? &p->options : &global_options), sizeof(pvt->options));
+ pvt->jointcapability = pvt->options.capability;
+ if (p) {
+ ASTOBJ_UNREF(p, oh323_destroy_peer);
+ }
+ if (pvt->options.dtmfmode) {
+ if (pvt->options.dtmfmode & H323_DTMF_RFC2833) {
+ pvt->nonCodecCapability |= AST_RTP_DTMF;
+ } else {
+ pvt->nonCodecCapability &= ~AST_RTP_DTMF;
+ }
+ }
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "No such host: %s\n", peer);
+ return -1;
+ }
+ } else if (!found) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+ struct oh323_pvt *pvt;
+ struct ast_channel *tmpc = NULL;
+ char *dest = (char *)data;
+ char *ext, *host;
+ char *h323id = NULL;
+ char tmp[256], tmp1[256];
+
+ if (h323debug)
+ ast_debug(1, "type=%s, format=%d, data=%s.\n", type, format, (char *)data);
+
+ pvt = oh323_alloc(0);
+ if (!pvt) {
+ ast_log(LOG_WARNING, "Unable to build pvt data for '%s'\n", (char *)data);
+ return NULL;
+ }
+ oldformat = format;
+ format &= AST_FORMAT_AUDIO_MASK;
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
+ oh323_destroy(pvt);
+ if (cause)
+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+ return NULL;
+ }
+ ast_copy_string(tmp, dest, sizeof(tmp));
+ host = strchr(tmp, '@');
+ if (host) {
+ *host = '\0';
+ host++;
+ ext = tmp;
+ } else {
+ ext = strrchr(tmp, '/');
+ if (ext)
+ *ext++ = '\0';
+ host = tmp;
+ }
+ strtok_r(host, "/", &(h323id));
+ if (!ast_strlen_zero(h323id)) {
+ h323_set_id(h323id);
+ }
+ if (ext) {
+ ast_copy_string(pvt->exten, ext, sizeof(pvt->exten));
+ }
+ if (h323debug)
+ ast_debug(1, "Extension: %s Host: %s\n", pvt->exten, host);
+
+ if (gatekeeper_disable) {
+ if (create_addr(pvt, host)) {
+ oh323_destroy(pvt);
+ if (cause)
+ *cause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
+ return NULL;
+ }
+ }
+ else {
+ memcpy(&pvt->options, &global_options, sizeof(pvt->options));
+ pvt->jointcapability = pvt->options.capability;
+ if (pvt->options.dtmfmode) {
+ if (pvt->options.dtmfmode & H323_DTMF_RFC2833) {
+ pvt->nonCodecCapability |= AST_RTP_DTMF;
+ } else {
+ pvt->nonCodecCapability &= ~AST_RTP_DTMF;
+ }
+ }
+ }
+
+ ast_mutex_lock(&caplock);
+ /* Generate unique channel identifier */
+ snprintf(tmp1, sizeof(tmp1)-1, "%s-%u", host, ++unique);
+ tmp1[sizeof(tmp1)-1] = '\0';
+ ast_mutex_unlock(&caplock);
+
+ ast_mutex_lock(&pvt->lock);
+ tmpc = __oh323_new(pvt, AST_STATE_DOWN, tmp1);
+ ast_mutex_unlock(&pvt->lock);
+ if (!tmpc) {
+ oh323_destroy(pvt);
+ if (cause)
+ *cause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
+ }
+ ast_update_use_count();
+ restart_monitor();
+ return tmpc;
+}
+
+/*! \brief Find a call by alias */
+static struct oh323_alias *find_alias(const char *source_aliases, int realtime)
+{
+ struct oh323_alias *a;
+
+ a = ASTOBJ_CONTAINER_FIND(&aliasl, source_aliases);
+
+ if (!a && realtime)
+ a = realtime_alias(source_aliases);
+
+ return a;
+}
+
+/*! \brief
+ * Callback for sending digits from H.323 up to asterisk
+ *
+ */
+static int receive_digit(unsigned call_reference, char digit, const char *token, int duration)
+{
+ struct oh323_pvt *pvt;
+ int res;
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Received digit '%c' (%u ms) for call %s without private structure\n", digit, duration, token);
+ return -1;
+ }
+ if (h323debug)
+ ast_log(LOG_DTMF, "Received %s digit '%c' (%u ms) for call %s\n", (digit == ' ' ? "update for" : "new"), (digit == ' ' ? pvt->curDTMF : digit), duration, token);
+
+ if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
+ if (digit == '!')
+ res = ast_queue_control(pvt->owner, AST_CONTROL_FLASH);
+ else {
+ struct ast_frame f = {
+ .frametype = AST_FRAME_DTMF_END,
+ .subclass = digit,
+ .samples = duration * 8,
+ .len = duration,
+ .src = "SEND_DIGIT",
+ };
+ if (digit == ' ') { /* signalUpdate message */
+ f.subclass = pvt->curDTMF;
+ if (pvt->DTMFsched >= 0) {
+ ast_sched_del(sched, pvt->DTMFsched);
+ pvt->DTMFsched = -1;
+ }
+ } else { /* Regular input or signal message */
+ if (pvt->DTMFsched >= 0) {
+ /* We still don't send DTMF END from previous event, send it now */
+ ast_sched_del(sched, pvt->DTMFsched);
+ pvt->DTMFsched = -1;
+ f.subclass = pvt->curDTMF;
+ f.samples = f.len = 0;
+ ast_queue_frame(pvt->owner, &f);
+ /* Restore values */
+ f.subclass = digit;
+ f.samples = duration * 8;
+ f.len = duration;
+ }
+ if (duration) { /* This is a signal, signalUpdate follows */
+ f.frametype = AST_FRAME_DTMF_BEGIN;
+ pvt->DTMFsched = ast_sched_add(sched, duration, oh323_simulate_dtmf_end, pvt);
+ if (h323debug)
+ ast_log(LOG_DTMF, "Scheduled DTMF END simulation for %d ms, id=%d\n", duration, pvt->DTMFsched);
+ }
+ pvt->curDTMF = digit;
+ }
+ res = ast_queue_frame(pvt->owner, &f);
+ }
+ ast_channel_unlock(pvt->owner);
+ } else {
+ if (digit == '!')
+ pvt->newcontrol = AST_CONTROL_FLASH;
+ else {
+ pvt->newduration = duration;
+ pvt->newdigit = digit;
+ }
+ res = 0;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ return res;
+}
+
+/*! \brief
+ * Callback function used to inform the H.323 stack of the local rtp ip/port details
+ *
+ * \return Returns the local RTP information
+ */
+static struct rtp_info *external_rtp_create(unsigned call_reference, const char * token)
+{
+ struct oh323_pvt *pvt;
+ struct sockaddr_in us;
+ struct rtp_info *info;
+
+ info = ast_calloc(1, sizeof(*info));
+ if (!info) {
+ ast_log(LOG_ERROR, "Unable to allocated info structure, this is very bad\n");
+ return NULL;
+ }
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ ast_free(info);
+ ast_log(LOG_ERROR, "Unable to find call %s(%d)\n", token, call_reference);
+ return NULL;
+ }
+ if (!pvt->rtp)
+ __oh323_rtp_create(pvt);
+ if (!pvt->rtp) {
+ ast_mutex_unlock(&pvt->lock);
+ ast_free(info);
+ ast_log(LOG_ERROR, "No RTP stream is available for call %s (%d)", token, call_reference);
+ return NULL;
+ }
+ /* figure out our local RTP port and tell the H.323 stack about it */
+ ast_rtp_get_us(pvt->rtp, &us);
+ ast_mutex_unlock(&pvt->lock);
+
+ ast_copy_string(info->addr, ast_inet_ntoa(us.sin_addr), sizeof(info->addr));
+ info->port = ntohs(us.sin_port);
+ if (h323debug)
+ ast_debug(1, "Sending RTP 'US' %s:%d\n", info->addr, info->port);
+ return info;
+}
+
+/*
+ * Definition taken from rtp.c for rtpPayloadType because we need it here.
+ */
+
+struct rtpPayloadType {
+ int isAstFormat; /* whether the following code is an AST_FORMAT */
+ int code;
+};
+
+/*! \brief
+ * Call-back function passing remote ip/port information from H.323 to asterisk
+ *
+ * Returns nothing
+ */
+static void setup_rtp_connection(unsigned call_reference, const char *remoteIp, int remotePort, const char *token, int pt)
+{
+ struct oh323_pvt *pvt;
+ struct sockaddr_in them;
+ struct rtpPayloadType rtptype;
+ int nativeformats_changed;
+ enum { NEED_NONE, NEED_HOLD, NEED_UNHOLD } rtp_change = NEED_NONE;
+
+ if (h323debug)
+ ast_debug(1, "Setting up RTP connection for %s\n", token);
+
+ /* Find the call or allocate a private structure if call not found */
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Something is wrong: rtp\n");
+ return;
+ }
+ if (pvt->alreadygone) {
+ ast_mutex_unlock(&pvt->lock);
+ return;
+ }
+
+ if (!pvt->rtp)
+ __oh323_rtp_create(pvt);
+
+ if ((pt == 2) && (pvt->jointcapability & AST_FORMAT_G726_AAL2)) {
+ ast_rtp_set_rtpmap_type(pvt->rtp, pt, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD);
+ }
+
+ them.sin_family = AF_INET;
+ /* only works for IPv4 */
+ them.sin_addr.s_addr = inet_addr(remoteIp);
+ them.sin_port = htons(remotePort);
+
+ if (them.sin_addr.s_addr) {
+ ast_rtp_set_peer(pvt->rtp, &them);
+ if (pvt->recvonly) {
+ pvt->recvonly = 0;
+ rtp_change = NEED_UNHOLD;
+ }
+ } else {
+ ast_rtp_stop(pvt->rtp);
+ if (!pvt->recvonly) {
+ pvt->recvonly = 1;
+ rtp_change = NEED_HOLD;
+ }
+ }
+
+ /* Change native format to reflect information taken from OLC/OLCAck */
+ nativeformats_changed = 0;
+ if (pt != 128 && pvt->rtp) { /* Payload type is invalid, so try to use previously decided */
+ rtptype = ast_rtp_lookup_pt(pvt->rtp, pt);
+ if (h323debug)
+ ast_debug(1, "Native format is set to %d from %d by RTP payload type %d\n", rtptype.code, pvt->nativeformats, pt);
+ if (pvt->nativeformats != rtptype.code) {
+ pvt->nativeformats = rtptype.code;
+ nativeformats_changed = 1;
+ }
+ } else if (h323debug)
+ ast_log(LOG_NOTICE, "Payload type is unknown, formats isn't changed\n");
+
+ /* Don't try to lock the channel if nothing changed */
+ if (nativeformats_changed || pvt->options.progress_audio || (rtp_change != NEED_NONE)) {
+ if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
+ /* Re-build translation path only if native format(s) has been changed */
+ if (pvt->owner->nativeformats != pvt->nativeformats) {
+ if (h323debug)
+ ast_debug(1, "Native format changed to %d from %d, read format is %d, write format is %d\n", pvt->nativeformats, pvt->owner->nativeformats, pvt->owner->readformat, pvt->owner->writeformat);
+ pvt->owner->nativeformats = pvt->nativeformats;
+ ast_set_read_format(pvt->owner, pvt->owner->readformat);
+ ast_set_write_format(pvt->owner, pvt->owner->writeformat);
+ }
+ if (pvt->options.progress_audio)
+ ast_queue_control(pvt->owner, AST_CONTROL_PROGRESS);
+ switch (rtp_change) {
+ case NEED_HOLD:
+ ast_queue_control(pvt->owner, AST_CONTROL_HOLD);
+ break;
+ case NEED_UNHOLD:
+ ast_queue_control(pvt->owner, AST_CONTROL_UNHOLD);
+ break;
+ default:
+ break;
+ }
+ ast_channel_unlock(pvt->owner);
+ }
+ else {
+ if (pvt->options.progress_audio)
+ pvt->newcontrol = AST_CONTROL_PROGRESS;
+ else if (rtp_change == NEED_HOLD)
+ pvt->newcontrol = AST_CONTROL_HOLD;
+ else if (rtp_change == NEED_UNHOLD)
+ pvt->newcontrol = AST_CONTROL_UNHOLD;
+ if (h323debug)
+ ast_debug(1, "RTP connection preparation for %s is pending...\n", token);
+ }
+ }
+ ast_mutex_unlock(&pvt->lock);
+
+ if (h323debug)
+ ast_debug(1, "RTP connection prepared for %s\n", token);
+
+ return;
+}
+
+/*! \brief
+ * Call-back function to signal asterisk that the channel has been answered
+ * Returns nothing
+ */
+static void connection_made(unsigned call_reference, const char *token)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Call %s answered\n", token);
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Something is wrong: connection\n");
+ return;
+ }
+
+ /* Inform asterisk about remote party connected only on outgoing calls */
+ if (!pvt->outgoing) {
+ ast_mutex_unlock(&pvt->lock);
+ return;
+ }
+ /* Do not send ANSWER message more than once */
+ if (!pvt->connection_established) {
+ pvt->connection_established = 1;
+ update_state(pvt, -1, AST_CONTROL_ANSWER);
+ }
+ ast_mutex_unlock(&pvt->lock);
+ return;
+}
+
+static int progress(unsigned call_reference, const char *token, int inband)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Received ALERT/PROGRESS message for %s tones\n", (inband ? "inband" : "self-generated"));
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Private structure not found in progress.\n");
+ return -1;
+ }
+ if (!pvt->owner) {
+ ast_mutex_unlock(&pvt->lock);
+ ast_log(LOG_ERROR, "No Asterisk channel associated with private structure.\n");
+ return -1;
+ }
+ update_state(pvt, -1, (inband ? AST_CONTROL_PROGRESS : AST_CONTROL_RINGING));
+ ast_mutex_unlock(&pvt->lock);
+
+ return 0;
+}
+
+/*! \brief
+ * Call-back function for incoming calls
+ *
+ * Returns 1 on success
+ */
+static call_options_t *setup_incoming_call(call_details_t *cd)
+{
+ struct oh323_pvt *pvt;
+ struct oh323_user *user = NULL;
+ struct oh323_alias *alias = NULL;
+
+ if (h323debug)
+ ast_debug(1, "Setting up incoming call for %s\n", cd->call_token);
+
+ /* allocate the call*/
+ pvt = oh323_alloc(cd->call_reference);
+
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Unable to allocate private structure, this is bad.\n");
+ cleanup_call_details(cd);
+ return NULL;
+ }
+
+ /* Populate the call details in the private structure */
+ memcpy(&pvt->cd, cd, sizeof(pvt->cd));
+ memcpy(&pvt->options, &global_options, sizeof(pvt->options));
+ pvt->jointcapability = pvt->options.capability;
+
+ if (h323debug) {
+ ast_verb(3, "Setting up Call\n");
+ ast_verb(3, " \tCall token: [%s]\n", pvt->cd.call_token);
+ ast_verb(3, " \tCalling party name: [%s]\n", pvt->cd.call_source_name);
+ ast_verb(3, " \tCalling party number: [%s]\n", pvt->cd.call_source_e164);
+ ast_verb(3, " \tCalled party name: [%s]\n", pvt->cd.call_dest_alias);
+ ast_verb(3, " \tCalled party number: [%s]\n", pvt->cd.call_dest_e164);
+ if (pvt->cd.redirect_reason >= 0)
+ ast_verb(3, " \tRedirecting party number: [%s] (reason %d)\n", pvt->cd.redirect_number, pvt->cd.redirect_reason);
+ ast_verb(3, " \tCalling party IP: [%s]\n", pvt->cd.sourceIp);
+ }
+
+ /* Decide if we are allowing Gatekeeper routed calls*/
+ if ((!strcasecmp(cd->sourceIp, gatekeeper)) && (gkroute == -1) && !gatekeeper_disable) {
+ if (!ast_strlen_zero(cd->call_dest_e164)) {
+ ast_copy_string(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten));
+ ast_copy_string(pvt->context, default_context, sizeof(pvt->context));
+ } else {
+ alias = find_alias(cd->call_dest_alias, 1);
+ if (!alias) {
+ ast_log(LOG_ERROR, "Call for %s rejected, alias not found\n", cd->call_dest_alias);
+ oh323_destroy(pvt);
+ return NULL;
+ }
+ ast_copy_string(pvt->exten, alias->name, sizeof(pvt->exten));
+ ast_copy_string(pvt->context, alias->context, sizeof(pvt->context));
+ }
+ } else {
+ /* Either this call is not from the Gatekeeper
+ or we are not allowing gk routed calls */
+ user = find_user(cd, 1);
+ if (!user) {
+ if (!acceptAnonymous) {
+ ast_log(LOG_NOTICE, "Anonymous call from '%s@%s' rejected\n", pvt->cd.call_source_aliases, pvt->cd.sourceIp);
+ oh323_destroy(pvt);
+ return NULL;
+ }
+ if (ast_strlen_zero(default_context)) {
+ ast_log(LOG_ERROR, "Call from '%s@%s' rejected due to no default context\n", pvt->cd.call_source_aliases, pvt->cd.sourceIp);
+ oh323_destroy(pvt);
+ return NULL;
+ }
+ ast_copy_string(pvt->context, default_context, sizeof(pvt->context));
+ if (!ast_strlen_zero(pvt->cd.call_dest_e164)) {
+ ast_copy_string(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten));
+ } else {
+ ast_copy_string(pvt->exten, cd->call_dest_alias, sizeof(pvt->exten));
+ }
+ if (h323debug)
+ ast_debug(1, "Sending %s@%s to context [%s] extension %s\n", cd->call_source_aliases, cd->sourceIp, pvt->context, pvt->exten);
+ } else {
+ if (user->host) {
+ if (strcasecmp(cd->sourceIp, ast_inet_ntoa(user->addr.sin_addr))) {
+ if (ast_strlen_zero(user->context)) {
+ if (ast_strlen_zero(default_context)) {
+ ast_log(LOG_ERROR, "Call from '%s' rejected due to non-matching IP address (%s) and no default context\n", user->name, cd->sourceIp);
+ oh323_destroy(pvt);
+ ASTOBJ_UNREF(user, oh323_destroy_user);
+ return NULL;
+ }
+ ast_copy_string(pvt->context, default_context, sizeof(pvt->context));
+ } else {
+ ast_copy_string(pvt->context, user->context, sizeof(pvt->context));
+ }
+ pvt->exten[0] = 'i';
+ pvt->exten[1] = '\0';
+ ast_log(LOG_ERROR, "Call from '%s' rejected due to non-matching IP address (%s)s\n", user->name, cd->sourceIp);
+ oh323_destroy(pvt);
+ ASTOBJ_UNREF(user, oh323_destroy_user);
+ return NULL; /* XXX: Hmmm... Why to setup context if we drop connection immediately??? */
+ }
+ }
+ ast_copy_string(pvt->context, user->context, sizeof(pvt->context));
+ memcpy(&pvt->options, &user->options, sizeof(pvt->options));
+ pvt->jointcapability = pvt->options.capability;
+ if (!ast_strlen_zero(pvt->cd.call_dest_e164)) {
+ ast_copy_string(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten));
+ } else {
+ ast_copy_string(pvt->exten, cd->call_dest_alias, sizeof(pvt->exten));
+ }
+ if (!ast_strlen_zero(user->accountcode)) {
+ ast_copy_string(pvt->accountcode, user->accountcode, sizeof(pvt->accountcode));
+ }
+ if (user->amaflags) {
+ pvt->amaflags = user->amaflags;
+ }
+ ASTOBJ_UNREF(user, oh323_destroy_user);
+ }
+ }
+ return &pvt->options;
+}
+
+/*! \brief
+ * Call-back function to start PBX when OpenH323 ready to serve incoming call
+ *
+ * Returns 1 on success
+ */
+static int answer_call(unsigned call_reference, const char *token)
+{
+ struct oh323_pvt *pvt;
+ struct ast_channel *c = NULL;
+ enum {ext_original, ext_s, ext_i, ext_notexists} try_exten;
+ char tmp_exten[sizeof(pvt->exten)];
+
+ if (h323debug)
+ ast_debug(1, "Preparing Asterisk to answer for %s\n", token);
+
+ /* Find the call or allocate a private structure if call not found */
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Something is wrong: answer_call\n");
+ return 0;
+ }
+ /* Check if requested extension@context pair exists in the dialplan */
+ ast_copy_string(tmp_exten, pvt->exten, sizeof(tmp_exten));
+
+ /* Try to find best extension in specified context */
+ if ((tmp_exten[0] != '\0') && (tmp_exten[1] == '\0')) {
+ if (tmp_exten[0] == 's')
+ try_exten = ext_s;
+ else if (tmp_exten[0] == 'i')
+ try_exten = ext_i;
+ else
+ try_exten = ext_original;
+ } else
+ try_exten = ext_original;
+ do {
+ if (ast_exists_extension(NULL, pvt->context, tmp_exten, 1, NULL))
+ break;
+ switch (try_exten) {
+ case ext_original:
+ tmp_exten[0] = 's';
+ tmp_exten[1] = '\0';
+ try_exten = ext_s;
+ break;
+ case ext_s:
+ tmp_exten[0] = 'i';
+ try_exten = ext_i;
+ break;
+ case ext_i:
+ try_exten = ext_notexists;
+ break;
+ default:
+ break;
+ }
+ } while (try_exten != ext_notexists);
+
+ /* Drop the call if we don't have <exten>, s and i extensions */
+ if (try_exten == ext_notexists) {
+ ast_log(LOG_NOTICE, "Dropping call because extensions '%s', 's' and 'i' doesn't exists in context [%s]\n", pvt->exten, pvt->context);
+ ast_mutex_unlock(&pvt->lock);
+ h323_clear_call(token, AST_CAUSE_UNALLOCATED);
+ return 0;
+ } else if ((try_exten != ext_original) && (strcmp(pvt->exten, tmp_exten) != 0)) {
+ if (h323debug)
+ ast_debug(1, "Going to extension %s@%s because %s@%s isn't exists\n", tmp_exten, pvt->context, pvt->exten, pvt->context);
+ ast_copy_string(pvt->exten, tmp_exten, sizeof(pvt->exten));
+ }
+
+ /* allocate a channel and tell asterisk about it */
+ c = __oh323_new(pvt, AST_STATE_RINGING, pvt->cd.call_token);
+
+ /* And release when done */
+ ast_mutex_unlock(&pvt->lock);
+ if (!c) {
+ ast_log(LOG_ERROR, "Couldn't create channel. This is bad\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*! \brief
+ * Call-back function to establish an outgoing H.323 call
+ *
+ * Returns 1 on success
+ */
+static int setup_outgoing_call(call_details_t *cd)
+{
+ /* Use argument here or free it immediately */
+ cleanup_call_details(cd);
+
+ return 1;
+}
+
+/*! \brief
+ * Call-back function to signal asterisk that the channel is ringing
+ * Returns nothing
+ */
+static void chan_ringing(unsigned call_reference, const char *token)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Ringing on %s\n", token);
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ ast_log(LOG_ERROR, "Something is wrong: ringing\n");
+ return;
+ }
+ if (!pvt->owner) {
+ ast_mutex_unlock(&pvt->lock);
+ ast_log(LOG_ERROR, "Channel has no owner\n");
+ return;
+ }
+ update_state(pvt, AST_STATE_RINGING, AST_CONTROL_RINGING);
+ ast_mutex_unlock(&pvt->lock);
+ return;
+}
+
+/*! \brief
+ * Call-back function to cleanup communication
+ * Returns nothing,
+ */
+static void cleanup_connection(unsigned call_reference, const char *call_token)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Cleaning connection to %s\n", call_token);
+
+ while (1) {
+ pvt = find_call_locked(call_reference, call_token);
+ if (!pvt) {
+ if (h323debug)
+ ast_debug(1, "No connection for %s\n", call_token);
+ return;
+ }
+ if (!pvt->owner || !ast_channel_trylock(pvt->owner))
+ break;
+#if 1
+ ast_log(LOG_NOTICE, "Avoiding H.323 destory deadlock on %s\n", call_token);
+#ifdef DEBUG_THREADS
+ /* XXX to be completed
+ * If we want to print more info on who is holding the lock,
+ * implement the relevant code in lock.h and use the routines
+ * supplied there.
+ */
+#endif
+#endif
+ ast_mutex_unlock(&pvt->lock);
+ usleep(1);
+ }
+ if (pvt->rtp) {
+ /* Immediately stop RTP */
+ ast_rtp_destroy(pvt->rtp);
+ pvt->rtp = NULL;
+ }
+ /* Free dsp used for in-band DTMF detection */
+ if (pvt->vad) {
+ ast_dsp_free(pvt->vad);
+ pvt->vad = NULL;
+ }
+ cleanup_call_details(&pvt->cd);
+ pvt->alreadygone = 1;
+ /* Send hangup */
+ if (pvt->owner) {
+ pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_queue_hangup(pvt->owner);
+ ast_channel_unlock(pvt->owner);
+ }
+ ast_mutex_unlock(&pvt->lock);
+ if (h323debug)
+ ast_debug(1, "Connection to %s cleaned\n", call_token);
+ return;
+}
+
+static void hangup_connection(unsigned int call_reference, const char *token, int cause)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Hanging up connection to %s with cause %d\n", token, cause);
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ if (h323debug)
+ ast_debug(1, "Connection to %s already cleared\n", token);
+ return;
+ }
+ if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
+ pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ pvt->owner->hangupcause = pvt->hangupcause = cause;
+ ast_queue_hangup(pvt->owner);
+ ast_channel_unlock(pvt->owner);
+ }
+ else {
+ pvt->needhangup = 1;
+ pvt->hangupcause = cause;
+ if (h323debug)
+ ast_debug(1, "Hangup for %s is pending\n", token);
+ }
+ ast_mutex_unlock(&pvt->lock);
+}
+
+static void set_dtmf_payload(unsigned call_reference, const char *token, int payload, int is_cisco)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Setting %s DTMF payload to %d on %s\n", (is_cisco ? "Cisco" : "RFC2833"), payload, token);
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt) {
+ return;
+ }
+ if (pvt->rtp) {
+ ast_rtp_set_rtpmap_type(pvt->rtp, payload, "audio", (is_cisco ? "cisco-telephone-event" : "telephone-event"), 0);
+ }
+ pvt->dtmf_pt[is_cisco ? 1 : 0] = payload;
+ ast_mutex_unlock(&pvt->lock);
+ if (h323debug)
+ ast_debug(1, "DTMF payload on %s set to %d\n", token, payload);
+}
+
+static void set_peer_capabilities(unsigned call_reference, const char *token, int capabilities, struct ast_codec_pref *prefs)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Got remote capabilities from connection %s\n", token);
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt)
+ return;
+ pvt->peercapability = capabilities;
+ pvt->jointcapability = pvt->options.capability & capabilities;
+ if (prefs) {
+ memcpy(&pvt->peer_prefs, prefs, sizeof(pvt->peer_prefs));
+ if (h323debug) {
+ int i;
+ for (i = 0; i < 32; ++i) {
+ if (!prefs->order[i])
+ break;
+ ast_debug(1, "prefs[%d]=%s:%d\n", i, (prefs->order[i] ? ast_getformatname(1 << (prefs->order[i]-1)) : "<none>"), prefs->framing[i]);
+ }
+ }
+ if (pvt->rtp)
+ ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs);
+ }
+ ast_mutex_unlock(&pvt->lock);
+}
+
+static void set_local_capabilities(unsigned call_reference, const char *token)
+{
+ struct oh323_pvt *pvt;
+ int capability, dtmfmode, pref_codec;
+ struct ast_codec_pref prefs;
+
+ if (h323debug)
+ ast_debug(1, "Setting capabilities for connection %s\n", token);
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt)
+ return;
+ capability = (pvt->jointcapability) ? pvt->jointcapability : pvt->options.capability;
+ dtmfmode = pvt->options.dtmfmode;
+ prefs = pvt->options.prefs;
+ pref_codec = pvt->pref_codec;
+ ast_mutex_unlock(&pvt->lock);
+ h323_set_capabilities(token, capability, dtmfmode, &prefs, pref_codec);
+
+ if (h323debug)
+ ast_debug(1, "Capabilities for connection %s is set\n", token);
+}
+
+static void remote_hold(unsigned call_reference, const char *token, int is_hold)
+{
+ struct oh323_pvt *pvt;
+
+ if (h323debug)
+ ast_debug(1, "Setting %shold status for connection %s\n", (is_hold ? "" : "un"), token);
+
+ pvt = find_call_locked(call_reference, token);
+ if (!pvt)
+ return;
+ if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
+ if (is_hold)
+ ast_queue_control(pvt->owner, AST_CONTROL_HOLD);
+ else
+ ast_queue_control(pvt->owner, AST_CONTROL_UNHOLD);
+ ast_channel_unlock(pvt->owner);
+ }
+ else {
+ if (is_hold)
+ pvt->newcontrol = AST_CONTROL_HOLD;
+ else
+ pvt->newcontrol = AST_CONTROL_UNHOLD;
+ }
+ ast_mutex_unlock(&pvt->lock);
+}
+
+static void *do_monitor(void *data)
+{
+ int res;
+ int reloading;
+ struct oh323_pvt *oh323 = NULL;
+
+ for(;;) {
+ /* Check for a reload request */
+ ast_mutex_lock(&h323_reload_lock);
+ reloading = h323_reloading;
+ h323_reloading = 0;
+ ast_mutex_unlock(&h323_reload_lock);
+ if (reloading) {
+ ast_verb(1, "Reloading H.323\n");
+ h323_do_reload();
+ }
+ /* Check for interfaces needing to be killed */
+ if (!ast_mutex_trylock(&iflock)) {
+#if 1
+ do {
+ for (oh323 = iflist; oh323; oh323 = oh323->next) {
+ if (!ast_mutex_trylock(&oh323->lock)) {
+ if (oh323->needdestroy) {
+ __oh323_destroy(oh323);
+ break;
+ }
+ ast_mutex_unlock(&oh323->lock);
+ }
+ }
+ } while (/*oh323*/ 0);
+#else
+restartsearch:
+ oh323 = iflist;
+ while(oh323) {
+ if (!ast_mutex_trylock(&oh323->lock)) {
+ if (oh323->needdestroy) {
+ __oh323_destroy(oh323);
+ goto restartsearch;
+ }
+ ast_mutex_unlock(&oh323->lock);
+ oh323 = oh323->next;
+ }
+ }
+#endif
+ ast_mutex_unlock(&iflock);
+ } else
+ oh323 = (struct oh323_pvt *)1; /* Force fast loop */
+ pthread_testcancel();
+ /* Wait for sched or io */
+ res = ast_sched_wait(sched);
+ if ((res < 0) || (res > 1000)) {
+ res = 1000;
+ }
+ /* Do not wait if some channel(s) is destroyed, probably, more available too */
+ if (oh323)
+ res = 1;
+ res = ast_io_wait(io, res);
+ pthread_testcancel();
+ ast_mutex_lock(&monlock);
+ if (res >= 0) {
+ ast_sched_runq(sched);
+ }
+ ast_mutex_unlock(&monlock);
+ }
+ /* Never reached */
+ return NULL;
+}
+
+static int restart_monitor(void)
+{
+ /* If we're supposed to be stopped -- stay stopped */
+ if (ast_mutex_lock(&monlock)) {
+ ast_log(LOG_WARNING, "Unable to lock monitor\n");
+ return -1;
+ }
+ if (monitor_thread == AST_PTHREADT_STOP) {
+ ast_mutex_unlock(&monlock);
+ return 0;
+ }
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread && (monitor_thread != AST_PTHREADT_NULL)) {
+ /* Wake up the thread */
+ pthread_kill(monitor_thread, SIGURG);
+ } else {
+ /* Start a new monitor */
+ if (ast_pthread_create_detached_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
+ monitor_thread = AST_PTHREADT_NULL;
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+static char *handle_cli_h323_set_trace(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "h323 set trace [off]";
+ e->usage =
+ "Usage: h323 set trace (off|<trace level>)\n"
+ " Enable/Disable H.323 stack tracing for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ if (!strcasecmp(a->argv[3], "off")) {
+ h323_debug(0, 0);
+ ast_cli(a->fd, "H.323 Trace Disabled\n");
+ } else {
+ int tracelevel = atoi(a->argv[3]);
+ h323_debug(1, tracelevel);
+ ast_cli(a->fd, "H.323 Trace Enabled (Trace Level: %d)\n", tracelevel);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_h323_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "h323 set debug [off]";
+ e->usage =
+ "Usage: h323 set debug [off]\n"
+ " Enable/Disable H.323 debugging output\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 4 && strcasecmp(a->argv[3], "off"))
+ return CLI_SHOWUSAGE;
+
+ h323debug = (a->argc == 3) ? 1 : 0;
+ ast_cli(a->fd, "H.323 Debugging %s\n", h323debug ? "Enabled" : "Disabled");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_h323_cycle_gk(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "h323 cycle gk";
+ e->usage =
+ "Usage: h323 cycle gk\n"
+ " Manually re-register with the Gatekeper (Currently Disabled)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ h323_gk_urq();
+
+ /* Possibly register with a GK */
+ if (!gatekeeper_disable) {
+ if (h323_set_gk(gatekeeper_discover, gatekeeper, secret)) {
+ ast_log(LOG_ERROR, "Gatekeeper registration failed.\n");
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_h323_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "h323 hangup";
+ e->usage =
+ "Usage: h323 hangup <token>\n"
+ " Manually try to hang up the call identified by <token>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ if (h323_soft_hangup(a->argv[2])) {
+ ast_verb(3, "Hangup succeeded on %s\n", a->argv[2]);
+ } else {
+ ast_verb(3, "Hangup failed for %s\n", a->argv[2]);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_h323_show_tokens(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "h323 show tokens";
+ e->usage =
+ "Usage: h323 show tokens\n"
+ " Print out all active call tokens\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ h323_show_tokens();
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_h323[] = {
+ AST_CLI_DEFINE(handle_cli_h323_set_trace, "Enable/Disable H.323 Stack Tracing"),
+ AST_CLI_DEFINE(handle_cli_h323_set_debug, "Enable/Disable H.323 Debugging"),
+ AST_CLI_DEFINE(handle_cli_h323_cycle_gk, "Manually re-register with the Gatekeper"),
+ AST_CLI_DEFINE(handle_cli_h323_hangup, "Manually try to hang up a call"),
+ AST_CLI_DEFINE(handle_cli_h323_show_tokens, "Show all active call tokens"),
+};
+
+static void delete_users(void)
+{
+ int pruned = 0;
+
+ /* Delete all users */
+ ASTOBJ_CONTAINER_WRLOCK(&userl);
+ ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ ASTOBJ_MARK(iterator);
+ ++pruned;
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+ if (pruned) {
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&userl, oh323_destroy_user);
+ }
+ ASTOBJ_CONTAINER_UNLOCK(&userl);
+
+ ASTOBJ_CONTAINER_WRLOCK(&peerl);
+ ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ ASTOBJ_MARK(iterator);
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+ ASTOBJ_CONTAINER_UNLOCK(&peerl);
+}
+
+static void delete_aliases(void)
+{
+ int pruned = 0;
+
+ /* Delete all aliases */
+ ASTOBJ_CONTAINER_WRLOCK(&aliasl);
+ ASTOBJ_CONTAINER_TRAVERSE(&aliasl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ ASTOBJ_MARK(iterator);
+ ++pruned;
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+ if (pruned) {
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&aliasl, oh323_destroy_alias);
+ }
+ ASTOBJ_CONTAINER_UNLOCK(&aliasl);
+}
+
+static void prune_peers(void)
+{
+ /* Prune peers who still are supposed to be deleted */
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, oh323_destroy_peer);
+}
+
+static int reload_config(int is_reload)
+{
+ struct ast_config *cfg, *ucfg;
+ struct ast_variable *v;
+ struct oh323_peer *peer = NULL;
+ struct oh323_user *user = NULL;
+ struct oh323_alias *alias = NULL;
+ struct ast_hostent ahp; struct hostent *hp;
+ char *cat;
+ const char *utype;
+ int is_user, is_peer, is_alias;
+ char _gatekeeper[100];
+ int gk_discover, gk_disable, gk_changed;
+ struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ cfg = ast_config_load(config, config_flags);
+
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config %s, H.323 disabled\n", config);
+ return 1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+ ucfg = ast_config_load("users.conf", config_flags);
+ if (ucfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ cfg = ast_config_load(config, config_flags);
+ } else {
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ ucfg = ast_config_load("users.conf", config_flags);
+ }
+
+ if (is_reload) {
+ delete_users();
+ delete_aliases();
+ prune_peers();
+ }
+
+ /* fire up the H.323 Endpoint */
+ if (!h323_end_point_exist()) {
+ h323_end_point_create();
+ }
+ ast_copy_string(_gatekeeper, gatekeeper, sizeof(_gatekeeper));
+ gk_discover = gatekeeper_discover;
+ gk_disable = gatekeeper_disable;
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ memset(&global_options, 0, sizeof(global_options));
+ global_options.fastStart = 1;
+ global_options.h245Tunneling = 1;
+ global_options.dtmfcodec[0] = H323_DTMF_RFC2833_PT;
+ global_options.dtmfcodec[1] = H323_DTMF_CISCO_PT;
+ global_options.dtmfmode = 0;
+ global_options.holdHandling = 0;
+ global_options.capability = GLOBAL_CAPABILITY;
+ global_options.bridge = 1; /* Do native bridging by default */
+ strcpy(default_context, "default");
+ h323_signalling_port = 1720;
+ gatekeeper_disable = 1;
+ gatekeeper_discover = 0;
+ gkroute = 0;
+ userbyalias = 1;
+ acceptAnonymous = 1;
+ tos = 0;
+ cos = 0;
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ if (ucfg) {
+ struct ast_variable *gen;
+ int genhas_h323;
+ const char *has_h323;
+
+ genhas_h323 = ast_true(ast_variable_retrieve(ucfg, "general", "hash323"));
+ gen = ast_variable_browse(ucfg, "general");
+ for (cat = ast_category_browse(ucfg, NULL); cat; cat = ast_category_browse(ucfg, cat)) {
+ if (strcasecmp(cat, "general")) {
+ has_h323 = ast_variable_retrieve(ucfg, cat, "hash323");
+ if (ast_true(has_h323) || (!has_h323 && genhas_h323)) {
+ user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0);
+ if (user) {
+ ASTOBJ_CONTAINER_LINK(&userl, user);
+ ASTOBJ_UNREF(user, oh323_destroy_user);
+ }
+ peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
+ if (peer) {
+ ASTOBJ_CONTAINER_LINK(&peerl, peer);
+ ASTOBJ_UNREF(peer, oh323_destroy_peer);
+ }
+ }
+ }
+ }
+ ast_config_destroy(ucfg);
+ }
+
+ for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+ continue;
+ /* Create the interface list */
+ if (!strcasecmp(v->name, "port")) {
+ h323_signalling_port = (int)strtol(v->value, NULL, 10);
+ } else if (!strcasecmp(v->name, "bindaddr")) {
+ if (!(hp = ast_gethostbyname(v->value, &ahp))) {
+ ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
+ } else {
+ memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
+ }
+ } else if (!strcasecmp(v->name, "tos")) { /* Needs to be removed in next release */
+ ast_log(LOG_WARNING, "The \"tos\" setting is deprecated in this version of Asterisk. Please change to \"tos_audio\".\n");
+ if (ast_str2tos(v->value, &tos)) {
+ ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "tos_audio")) {
+ if (ast_str2tos(v->value, &tos)) {
+ ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "cos")) {
+ ast_log(LOG_WARNING, "The \"cos\" setting is deprecated in this version of Asterisk. Please change to \"cos_audio\".\n");
+ if (ast_str2cos(v->value, &cos)) {
+ ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "cos_audio")) {
+ if (ast_str2cos(v->value, &cos)) {
+ ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "gatekeeper")) {
+ if (!strcasecmp(v->value, "DISABLE")) {
+ gatekeeper_disable = 1;
+ } else if (!strcasecmp(v->value, "DISCOVER")) {
+ gatekeeper_disable = 0;
+ gatekeeper_discover = 1;
+ } else {
+ gatekeeper_disable = 0;
+ ast_copy_string(gatekeeper, v->value, sizeof(gatekeeper));
+ }
+ } else if (!strcasecmp(v->name, "secret")) {
+ ast_copy_string(secret, v->value, sizeof(secret));
+ } else if (!strcasecmp(v->name, "AllowGKRouted")) {
+ gkroute = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(default_context, v->value, sizeof(default_context));
+ ast_verb(2, "Setting default context to %s\n", default_context);
+ } else if (!strcasecmp(v->name, "UserByAlias")) {
+ userbyalias = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "AcceptAnonymous")) {
+ acceptAnonymous = ast_true(v->value);
+ } else if (!update_common_options(v, &global_options)) {
+ /* dummy */
+ }
+ }
+ if (!global_options.dtmfmode)
+ global_options.dtmfmode = H323_DTMF_RFC2833;
+ if (global_options.holdHandling == ~0)
+ global_options.holdHandling = 0;
+ else if (!global_options.holdHandling)
+ global_options.holdHandling = H323_HOLD_H450;
+
+ for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
+ if (strcasecmp(cat, "general")) {
+ utype = ast_variable_retrieve(cfg, cat, "type");
+ if (utype) {
+ is_user = is_peer = is_alias = 0;
+ if (!strcasecmp(utype, "user"))
+ is_user = 1;
+ else if (!strcasecmp(utype, "peer"))
+ is_peer = 1;
+ else if (!strcasecmp(utype, "friend"))
+ is_user = is_peer = 1;
+ else if (!strcasecmp(utype, "h323") || !strcasecmp(utype, "alias"))
+ is_alias = 1;
+ else {
+ ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config);
+ continue;
+ }
+ if (is_user) {
+ user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ if (user) {
+ ASTOBJ_CONTAINER_LINK(&userl, user);
+ ASTOBJ_UNREF(user, oh323_destroy_user);
+ }
+ }
+ if (is_peer) {
+ peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ if (peer) {
+ ASTOBJ_CONTAINER_LINK(&peerl, peer);
+ ASTOBJ_UNREF(peer, oh323_destroy_peer);
+ }
+ }
+ if (is_alias) {
+ alias = build_alias(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ if (alias) {
+ ASTOBJ_CONTAINER_LINK(&aliasl, alias);
+ ASTOBJ_UNREF(alias, oh323_destroy_alias);
+ }
+ }
+ } else {
+ ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+
+ /* Register our H.323 aliases if any*/
+ ASTOBJ_CONTAINER_WRLOCK(&aliasl);
+ ASTOBJ_CONTAINER_TRAVERSE(&aliasl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ if (h323_set_alias(iterator)) {
+ ast_log(LOG_ERROR, "Alias %s rejected by endpoint\n", alias->name);
+ ASTOBJ_UNLOCK(iterator);
+ continue;
+ }
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+ ASTOBJ_CONTAINER_UNLOCK(&aliasl);
+
+ /* Don't touch GK if nothing changed because URQ will drop all existing calls */
+ gk_changed = 0;
+ if (gatekeeper_disable != gk_disable)
+ gk_changed = is_reload;
+ else if(!gatekeeper_disable && (gatekeeper_discover != gk_discover))
+ gk_changed = is_reload;
+ else if(!gatekeeper_disable && (strncmp(_gatekeeper, gatekeeper, sizeof(_gatekeeper)) != 0))
+ gk_changed = is_reload;
+ if (gk_changed) {
+ if(!gk_disable)
+ h323_gk_urq();
+ if (!gatekeeper_disable) {
+ if (h323_set_gk(gatekeeper_discover, gatekeeper, secret)) {
+ ast_log(LOG_ERROR, "Gatekeeper registration failed.\n");
+ gatekeeper_disable = 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int h323_reload(void)
+{
+ ast_mutex_lock(&h323_reload_lock);
+ if (h323_reloading) {
+ ast_verbose("Previous H.323 reload not yet done\n");
+ } else {
+ h323_reloading = 1;
+ }
+ ast_mutex_unlock(&h323_reload_lock);
+ restart_monitor();
+ return 0;
+}
+
+static char *handle_cli_h323_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "h323 reload";
+ e->usage =
+ "Usage: h323 reload\n"
+ " Reloads H.323 configuration from h323.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ h323_reload();
+
+ return CLI_SUCCESS;
+}
+
+static int h323_do_reload(void)
+{
+ reload_config(1);
+ return 0;
+}
+
+static int reload(void)
+{
+ if (!sched || !io) {
+ ast_log(LOG_NOTICE, "Unload and load chan_h323.so again in order to receive configuration changes.\n");
+ return 0;
+ }
+ return h323_reload();
+}
+
+static struct ast_cli_entry cli_h323_reload =
+ AST_CLI_DEFINE(handle_cli_h323_reload, "Reload H.323 configuration");
+
+static enum ast_rtp_get_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct oh323_pvt *pvt;
+ enum ast_rtp_get_result res = AST_RTP_GET_FAILED;
+
+ if (!(pvt = (struct oh323_pvt *)chan->tech_pvt))
+ return res;
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->rtp && pvt->options.bridge) {
+ *rtp = pvt->rtp;
+ res = AST_RTP_TRY_NATIVE;
+ }
+ ast_mutex_unlock(&pvt->lock);
+
+ return res;
+}
+
+static enum ast_rtp_get_result oh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ return AST_RTP_GET_FAILED;
+}
+
+static char *convertcap(int cap)
+{
+ switch (cap) {
+ case AST_FORMAT_G723_1:
+ return "G.723";
+ case AST_FORMAT_GSM:
+ return "GSM";
+ case AST_FORMAT_ULAW:
+ return "ULAW";
+ case AST_FORMAT_ALAW:
+ return "ALAW";
+ case AST_FORMAT_G722:
+ return "G.722";
+ case AST_FORMAT_ADPCM:
+ return "G.728";
+ case AST_FORMAT_G729A:
+ return "G.729";
+ case AST_FORMAT_SPEEX:
+ return "SPEEX";
+ case AST_FORMAT_ILBC:
+ return "ILBC";
+ default:
+ ast_log(LOG_NOTICE, "Don't know how to deal with mode %d\n", cap);
+ return NULL;
+ }
+}
+
+static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+{
+ /* XXX Deal with Video */
+ struct oh323_pvt *pvt;
+ struct sockaddr_in them;
+ struct sockaddr_in us;
+ char *mode;
+
+ if (!rtp) {
+ return 0;
+ }
+
+ mode = convertcap(chan->writeformat);
+ pvt = (struct oh323_pvt *) chan->tech_pvt;
+ if (!pvt) {
+ ast_log(LOG_ERROR, "No Private Structure, this is bad\n");
+ return -1;
+ }
+ ast_rtp_get_peer(rtp, &them);
+ ast_rtp_get_us(rtp, &us);
+#if 0 /* Native bridge still isn't ready */
+ h323_native_bridge(pvt->cd.call_token, ast_inet_ntoa(them.sin_addr), mode);
+#endif
+ return 0;
+}
+
+static struct ast_rtp_protocol oh323_rtp = {
+ .type = "H323",
+ .get_rtp_info = oh323_get_rtp_peer,
+ .get_vrtp_info = oh323_get_vrtp_peer,
+ .set_rtp_peer = oh323_set_rtp_peer,
+};
+
+static enum ast_module_load_result load_module(void)
+{
+ int res;
+
+ h323debug = 0;
+ sched = sched_context_create();
+ if (!sched) {
+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ io = io_context_create();
+ if (!io) {
+ ast_log(LOG_WARNING, "Unable to create I/O context\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ast_cli_register(&cli_h323_reload);
+ ASTOBJ_CONTAINER_INIT(&userl);
+ ASTOBJ_CONTAINER_INIT(&peerl);
+ ASTOBJ_CONTAINER_INIT(&aliasl);
+ res = reload_config(0);
+ if (res) {
+ /* No config entry */
+ ast_log(LOG_NOTICE, "Unload and load chan_h323.so again in order to receive configuration changes.\n");
+ ast_cli_unregister(&cli_h323_reload);
+ io_context_destroy(io);
+ io = NULL;
+ sched_context_destroy(sched);
+ sched = NULL;
+ ASTOBJ_CONTAINER_DESTROY(&userl);
+ ASTOBJ_CONTAINER_DESTROY(&peerl);
+ ASTOBJ_CONTAINER_DESTROY(&aliasl);
+ return AST_MODULE_LOAD_DECLINE;
+ } else {
+ /* Make sure we can register our channel type */
+ if (ast_channel_register(&oh323_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'H323'\n");
+ ast_cli_unregister(&cli_h323_reload);
+ h323_end_process();
+ io_context_destroy(io);
+ sched_context_destroy(sched);
+
+ ASTOBJ_CONTAINER_DESTROYALL(&userl, oh323_destroy_user);
+ ASTOBJ_CONTAINER_DESTROY(&userl);
+ ASTOBJ_CONTAINER_DESTROYALL(&peerl, oh323_destroy_peer);
+ ASTOBJ_CONTAINER_DESTROY(&peerl);
+ ASTOBJ_CONTAINER_DESTROYALL(&aliasl, oh323_destroy_alias);
+ ASTOBJ_CONTAINER_DESTROY(&aliasl);
+
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ast_cli_register_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry));
+
+ ast_rtp_proto_register(&oh323_rtp);
+
+ /* Register our callback functions */
+ h323_callback_register(setup_incoming_call,
+ setup_outgoing_call,
+ external_rtp_create,
+ setup_rtp_connection,
+ cleanup_connection,
+ chan_ringing,
+ connection_made,
+ receive_digit,
+ answer_call,
+ progress,
+ set_dtmf_payload,
+ hangup_connection,
+ set_local_capabilities,
+ set_peer_capabilities,
+ remote_hold);
+ /* start the h.323 listener */
+ if (h323_start_listener(h323_signalling_port, bindaddr)) {
+ ast_log(LOG_ERROR, "Unable to create H323 listener.\n");
+ ast_rtp_proto_unregister(&oh323_rtp);
+ ast_cli_unregister_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry));
+ ast_cli_unregister(&cli_h323_reload);
+ h323_end_process();
+ io_context_destroy(io);
+ sched_context_destroy(sched);
+
+ ASTOBJ_CONTAINER_DESTROYALL(&userl, oh323_destroy_user);
+ ASTOBJ_CONTAINER_DESTROY(&userl);
+ ASTOBJ_CONTAINER_DESTROYALL(&peerl, oh323_destroy_peer);
+ ASTOBJ_CONTAINER_DESTROY(&peerl);
+ ASTOBJ_CONTAINER_DESTROYALL(&aliasl, oh323_destroy_alias);
+ ASTOBJ_CONTAINER_DESTROY(&aliasl);
+
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ /* Possibly register with a GK */
+ if (!gatekeeper_disable) {
+ if (h323_set_gk(gatekeeper_discover, gatekeeper, secret)) {
+ ast_log(LOG_ERROR, "Gatekeeper registration failed.\n");
+ gatekeeper_disable = 1;
+ res = AST_MODULE_LOAD_SUCCESS;
+ }
+ }
+ /* And start the monitor for the first time */
+ restart_monitor();
+ }
+ return res;
+}
+
+static int unload_module(void)
+{
+ struct oh323_pvt *p, *pl;
+
+ /* unregister commands */
+ ast_cli_unregister_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry));
+ ast_cli_unregister(&cli_h323_reload);
+
+ ast_channel_unregister(&oh323_tech);
+ ast_rtp_proto_unregister(&oh323_rtp);
+
+ if (!ast_mutex_lock(&iflock)) {
+ /* hangup all interfaces if they have an owner */
+ p = iflist;
+ while(p) {
+ if (p->owner) {
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ }
+ p = p->next;
+ }
+ iflist = NULL;
+ ast_mutex_unlock(&iflock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the interface list\n");
+ return -1;
+ }
+ if (!ast_mutex_lock(&monlock)) {
+ if ((monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
+ /* this causes a seg, anyone know why? */
+ if (monitor_thread != pthread_self())
+ pthread_cancel(monitor_thread);
+ pthread_kill(monitor_thread, SIGURG);
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+ if (!ast_mutex_lock(&iflock)) {
+ /* destroy all the interfaces and free their memory */
+ p = iflist;
+ while(p) {
+ pl = p;
+ p = p->next;
+ /* free associated memory */
+ ast_mutex_destroy(&pl->lock);
+ ast_free(pl);
+ }
+ iflist = NULL;
+ ast_mutex_unlock(&iflock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the interface list\n");
+ return -1;
+ }
+ if (!gatekeeper_disable)
+ h323_gk_urq();
+ h323_end_process();
+ if (io)
+ io_context_destroy(io);
+ if (sched)
+ sched_context_destroy(sched);
+
+ ASTOBJ_CONTAINER_DESTROYALL(&userl, oh323_destroy_user);
+ ASTOBJ_CONTAINER_DESTROY(&userl);
+ ASTOBJ_CONTAINER_DESTROYALL(&peerl, oh323_destroy_peer);
+ ASTOBJ_CONTAINER_DESTROY(&peerl);
+ ASTOBJ_CONTAINER_DESTROYALL(&aliasl, oh323_destroy_alias);
+ ASTOBJ_CONTAINER_DESTROY(&aliasl);
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "The NuFone Network's OpenH323 Channel Driver",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/trunk/channels/chan_iax2.c b/trunk/channels/chan_iax2.c
new file mode 100644
index 000000000..c5774669a
--- /dev/null
+++ b/trunk/channels/chan_iax2.c
@@ -0,0 +1,11726 @@
+/*
+ * 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 Implementation of Inter-Asterisk eXchange Version 2
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \par See also
+ * \arg \ref Config_iax
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <use>zaptel</use>
+ <use>crypto</use>
+ <depend>res_features</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/mman.h>
+#include <dirent.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <signal.h>
+#include <strings.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <regex.h>
+
+#include "asterisk/zapata.h"
+#include "asterisk/paths.h" /* need ast_config_AST_DATA_DIR for firmware */
+
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/translate.h"
+#include "asterisk/md5.h"
+#include "asterisk/cdr.h"
+#include "asterisk/crypto.h"
+#include "asterisk/acl.h"
+#include "asterisk/manager.h"
+#include "asterisk/callerid.h"
+#include "asterisk/app.h"
+#include "asterisk/astdb.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/features.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/localtime.h"
+#include "asterisk/aes.h"
+#include "asterisk/dnsmgr.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/netsock.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/event.h"
+#include "asterisk/astobj2.h"
+
+#include "iax2.h"
+#include "iax2-parser.h"
+#include "iax2-provision.h"
+#include "jitterbuf.h"
+
+/* Define SCHED_MULTITHREADED to run the scheduler in a special
+ multithreaded mode. */
+#define SCHED_MULTITHREADED
+
+/* Define DEBUG_SCHED_MULTITHREADED to keep track of where each
+ thread is actually doing. */
+#define DEBUG_SCHED_MULTITHREAD
+
+
+#ifdef SO_NO_CHECK
+static int nochecksums = 0;
+#endif
+
+
+#define PTR_TO_CALLNO(a) ((unsigned short)(unsigned long)(a))
+#define CALLNO_TO_PTR(a) ((void *)(unsigned long)(a))
+
+#define DEFAULT_THREAD_COUNT 10
+#define DEFAULT_MAX_THREAD_COUNT 100
+#define DEFAULT_RETRY_TIME 1000
+#define MEMORY_SIZE 100
+#define DEFAULT_DROP 3
+/* Flag to use with trunk calls, keeping these calls high up. It halves our effective use
+ but keeps the division between trunked and non-trunked better. */
+#define TRUNK_CALL_START 0x4000
+
+#define DEBUG_SUPPORT
+
+#define MIN_REUSE_TIME 60 /* Don't reuse a call number within 60 seconds */
+
+/* Sample over last 100 units to determine historic jitter */
+#define GAMMA (0.01)
+
+static struct ast_codec_pref prefs;
+
+static const char tdesc[] = "Inter Asterisk eXchange Driver (Ver 2)";
+
+
+/*! \brief Maximum transmission unit for the UDP packet in the trunk not to be
+ fragmented. This is based on 1516 - ethernet - ip - udp - iax minus one g711 frame = 1240 */
+#define MAX_TRUNK_MTU 1240
+
+static int global_max_trunk_mtu; /*!< Maximum MTU, 0 if not used */
+static int trunk_timed, trunk_untimed, trunk_maxmtu, trunk_nmaxmtu ; /*!< Trunk MTU statistics */
+
+
+static char context[80] = "default";
+
+static char language[MAX_LANGUAGE] = "";
+static char regcontext[AST_MAX_CONTEXT] = "";
+
+static int maxauthreq = 3;
+static int max_retries = 4;
+static int ping_time = 21;
+static int lagrq_time = 10;
+static int maxtrunkcall = TRUNK_CALL_START;
+static int maxnontrunkcall = 1;
+static int maxjitterbuffer=1000;
+static int resyncthreshold=1000;
+static int maxjitterinterps=10;
+static int jittertargetextra = 40; /* number of milliseconds the new jitter buffer adds on to its size */
+
+#define MAX_TRUNKDATA 640 * 200 /*!< 40ms, uncompressed linear * 200 channels */
+
+static int trunkfreq = 20;
+static int trunkmaxsize = MAX_TRUNKDATA;
+
+static int authdebug = 1;
+static int autokill = 0;
+static int iaxcompat = 0;
+
+static int iaxdefaultdpcache=10 * 60; /* Cache dialplan entries for 10 minutes by default */
+
+static int iaxdefaulttimeout = 5; /* Default to wait no more than 5 seconds for a reply to come back */
+
+static unsigned int tos = 0;
+
+static unsigned int cos = 0;
+
+static int min_reg_expire;
+static int max_reg_expire;
+
+static int srvlookup = 0;
+
+static int timingfd = -1; /* Timing file descriptor */
+
+static struct ast_netsock_list *netsock;
+static struct ast_netsock_list *outsock; /*!< used if sourceaddress specified and bindaddr == INADDR_ANY */
+static int defaultsockfd = -1;
+
+int (*iax2_regfunk)(const char *username, int onoff) = NULL;
+
+/* Ethernet, etc */
+#define IAX_CAPABILITY_FULLBANDWIDTH 0xFFFF
+/* T1, maybe ISDN */
+#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \
+ ~AST_FORMAT_SLINEAR & \
+ ~AST_FORMAT_ULAW & \
+ ~AST_FORMAT_ALAW & \
+ ~AST_FORMAT_G722)
+/* A modem */
+#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \
+ ~AST_FORMAT_G726 & \
+ ~AST_FORMAT_G726_AAL2 & \
+ ~AST_FORMAT_ADPCM)
+
+#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \
+ ~AST_FORMAT_G723_1)
+
+
+#define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */
+#define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */
+#define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */
+
+static struct io_context *io;
+static struct sched_context *sched;
+
+static int iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
+
+static int iaxdebug = 0;
+
+static int iaxtrunkdebug = 0;
+
+static int test_losspct = 0;
+#ifdef IAXTESTS
+static int test_late = 0;
+static int test_resync = 0;
+static int test_jit = 0;
+static int test_jitpct = 0;
+#endif /* IAXTESTS */
+
+static char accountcode[AST_MAX_ACCOUNT_CODE];
+static char mohinterpret[MAX_MUSICCLASS];
+static char mohsuggest[MAX_MUSICCLASS];
+static int amaflags = 0;
+static int adsi = 0;
+static int delayreject = 0;
+static int iax2_encryption = 0;
+
+static struct ast_flags globalflags = { 0 };
+
+static pthread_t netthreadid = AST_PTHREADT_NULL;
+static pthread_t schedthreadid = AST_PTHREADT_NULL;
+AST_MUTEX_DEFINE_STATIC(sched_lock);
+static ast_cond_t sched_cond;
+
+enum iax2_state {
+ IAX_STATE_STARTED = (1 << 0),
+ IAX_STATE_AUTHENTICATED = (1 << 1),
+ IAX_STATE_TBD = (1 << 2),
+ IAX_STATE_UNCHANGED = (1 << 3),
+};
+
+struct iax2_context {
+ char context[AST_MAX_CONTEXT];
+ struct iax2_context *next;
+};
+
+enum iax2_flags {
+ IAX_HASCALLERID = (1 << 0), /*!< CallerID has been specified */
+ IAX_DELME = (1 << 1), /*!< Needs to be deleted */
+ IAX_TEMPONLY = (1 << 2), /*!< Temporary (realtime) */
+ IAX_TRUNK = (1 << 3), /*!< Treat as a trunk */
+ IAX_NOTRANSFER = (1 << 4), /*!< Don't native bridge */
+ IAX_USEJITTERBUF = (1 << 5), /*!< Use jitter buffer */
+ IAX_DYNAMIC = (1 << 6), /*!< dynamic peer */
+ IAX_SENDANI = (1 << 7), /*!< Send ANI along with CallerID */
+ /* (1 << 8) is currently unused due to the deprecation of an old option. Go ahead, take it! */
+ IAX_ALREADYGONE = (1 << 9), /*!< Already disconnected */
+ IAX_PROVISION = (1 << 10), /*!< This is a provisioning request */
+ IAX_QUELCH = (1 << 11), /*!< Whether or not we quelch audio */
+ IAX_ENCRYPTED = (1 << 12), /*!< Whether we should assume encrypted tx/rx */
+ IAX_KEYPOPULATED = (1 << 13), /*!< Whether we have a key populated */
+ IAX_CODEC_USER_FIRST = (1 << 14), /*!< are we willing to let the other guy choose the codec? */
+ IAX_CODEC_NOPREFS = (1 << 15), /*!< Force old behaviour by turning off prefs */
+ IAX_CODEC_NOCAP = (1 << 16), /*!< only consider requested format and ignore capabilities*/
+ IAX_RTCACHEFRIENDS = (1 << 17), /*!< let realtime stay till your reload */
+ IAX_RTUPDATE = (1 << 18), /*!< Send a realtime update */
+ IAX_RTAUTOCLEAR = (1 << 19), /*!< erase me on expire */
+ IAX_FORCEJITTERBUF = (1 << 20), /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */
+ IAX_RTIGNOREREGEXPIRE = (1 << 21), /*!< When using realtime, ignore registration expiration */
+ IAX_TRUNKTIMESTAMPS = (1 << 22), /*!< Send trunk timestamps */
+ IAX_TRANSFERMEDIA = (1 << 23), /*!< When doing IAX2 transfers, transfer media only */
+ IAX_MAXAUTHREQ = (1 << 24), /*!< Maximum outstanding AUTHREQ restriction is in place */
+ IAX_DELAYPBXSTART = (1 << 25), /*!< Don't start a PBX on the channel until the peer sends us a
+ response, so that we've achieved a three-way handshake with
+ them before sending voice or anything else*/
+};
+
+static int global_rtautoclear = 120;
+
+static int reload_config(void);
+
+struct iax2_user {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name);
+ AST_STRING_FIELD(secret);
+ AST_STRING_FIELD(dbsecret);
+ AST_STRING_FIELD(accountcode);
+ AST_STRING_FIELD(mohinterpret);
+ AST_STRING_FIELD(mohsuggest);
+ AST_STRING_FIELD(inkeys); /*!< Key(s) this user can use to authenticate to us */
+ AST_STRING_FIELD(language);
+ AST_STRING_FIELD(cid_num);
+ AST_STRING_FIELD(cid_name);
+ );
+
+ int authmethods;
+ int encmethods;
+ int amaflags;
+ int adsi;
+ unsigned int flags;
+ int capability;
+ int maxauthreq; /*!< Maximum allowed outstanding AUTHREQs */
+ int curauthreq; /*!< Current number of outstanding AUTHREQs */
+ struct ast_codec_pref prefs;
+ struct ast_ha *ha;
+ struct iax2_context *contexts;
+ struct ast_variable *vars;
+};
+
+struct iax2_peer {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name);
+ AST_STRING_FIELD(username);
+ AST_STRING_FIELD(secret);
+ AST_STRING_FIELD(dbsecret);
+ AST_STRING_FIELD(outkey); /*!< What key we use to talk to this peer */
+
+ AST_STRING_FIELD(regexten); /*!< Extension to register (if regcontext is used) */
+ AST_STRING_FIELD(context); /*!< For transfers only */
+ AST_STRING_FIELD(peercontext); /*!< Context to pass to peer */
+ AST_STRING_FIELD(mailbox); /*!< Mailbox */
+ AST_STRING_FIELD(mohinterpret);
+ AST_STRING_FIELD(mohsuggest);
+ AST_STRING_FIELD(inkeys); /*!< Key(s) this peer can use to authenticate to us */
+ /* Suggested caller id if registering */
+ AST_STRING_FIELD(cid_num); /*!< Default context (for transfer really) */
+ AST_STRING_FIELD(cid_name); /*!< Default context (for transfer really) */
+ AST_STRING_FIELD(zonetag); /*!< Time Zone */
+ );
+ struct ast_codec_pref prefs;
+ struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */
+ struct sockaddr_in addr;
+ int formats;
+ int sockfd; /*!< Socket to use for transmission */
+ struct in_addr mask;
+ int adsi;
+ unsigned int flags;
+
+ /* Dynamic Registration fields */
+ struct sockaddr_in defaddr; /*!< Default address if there is one */
+ int authmethods; /*!< Authentication methods (IAX_AUTH_*) */
+ int encmethods; /*!< Encryption methods (IAX_ENCRYPT_*) */
+
+ int expire; /*!< Schedule entry for expiry */
+ int expiry; /*!< How soon to expire */
+ int capability; /*!< Capability */
+
+ /* Qualification */
+ int callno; /*!< Call number of POKE request */
+ int pokeexpire; /*!< Scheduled qualification-related task (ie iax2_poke_peer_s or iax2_poke_noanswer) */
+ int lastms; /*!< How long last response took (in ms), or -1 for no response */
+ int maxms; /*!< Max ms we will accept for the host to be up, 0 to not monitor */
+
+ int pokefreqok; /*!< How often to check if the host is up */
+ int pokefreqnotok; /*!< How often to check when the host has been determined to be down */
+ int historicms; /*!< How long recent average responses took */
+ int smoothing; /*!< Sample over how many units to determine historic ms */
+
+ struct ast_event_sub *mwi_event_sub;
+
+ struct ast_ha *ha;
+};
+
+#define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
+
+struct iax2_trunk_peer {
+ ast_mutex_t lock;
+ int sockfd;
+ struct sockaddr_in addr;
+ struct timeval txtrunktime; /*!< Transmit trunktime */
+ struct timeval rxtrunktime; /*!< Receive trunktime */
+ struct timeval lasttxtime; /*!< Last transmitted trunktime */
+ struct timeval trunkact; /*!< Last trunk activity */
+ unsigned int lastsent; /*!< Last sent time */
+ /* Trunk data and length */
+ unsigned char *trunkdata;
+ unsigned int trunkdatalen;
+ unsigned int trunkdataalloc;
+ int trunkmaxmtu;
+ int trunkerror;
+ int calls;
+ AST_LIST_ENTRY(iax2_trunk_peer) list;
+};
+
+static AST_LIST_HEAD_STATIC(tpeers, iax2_trunk_peer);
+
+struct iax_firmware {
+ AST_LIST_ENTRY(iax_firmware) list;
+ int fd;
+ int mmaplen;
+ int dead;
+ struct ast_iax2_firmware_header *fwh;
+ unsigned char *buf;
+};
+
+enum iax_reg_state {
+ REG_STATE_UNREGISTERED = 0,
+ REG_STATE_REGSENT,
+ REG_STATE_AUTHSENT,
+ REG_STATE_REGISTERED,
+ REG_STATE_REJECTED,
+ REG_STATE_TIMEOUT,
+ REG_STATE_NOAUTH
+};
+
+enum iax_transfer_state {
+ TRANSFER_NONE = 0,
+ TRANSFER_BEGIN,
+ TRANSFER_READY,
+ TRANSFER_RELEASED,
+ TRANSFER_PASSTHROUGH,
+ TRANSFER_MBEGIN,
+ TRANSFER_MREADY,
+ TRANSFER_MRELEASED,
+ TRANSFER_MPASSTHROUGH,
+ TRANSFER_MEDIA,
+ TRANSFER_MEDIAPASS
+};
+
+struct iax2_registry {
+ struct sockaddr_in addr; /*!< Who we connect to for registration purposes */
+ char username[80];
+ char secret[80]; /*!< Password or key name in []'s */
+ int expire; /*!< Sched ID of expiration */
+ int refresh; /*!< How often to refresh */
+ enum iax_reg_state regstate;
+ int messages; /*!< Message count, low 8 bits = new, high 8 bits = old */
+ int callno; /*!< Associated call number if applicable */
+ struct sockaddr_in us; /*!< Who the server thinks we are */
+ struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */
+ AST_LIST_ENTRY(iax2_registry) entry;
+};
+
+static AST_LIST_HEAD_STATIC(registrations, iax2_registry);
+
+/* Don't retry more frequently than every 10 ms, or less frequently than every 5 seconds */
+#define MIN_RETRY_TIME 100
+#define MAX_RETRY_TIME 10000
+
+#define MAX_JITTER_BUFFER 50
+#define MIN_JITTER_BUFFER 10
+
+#define DEFAULT_TRUNKDATA 640 * 10 /*!< 40ms, uncompressed linear * 10 channels */
+
+#define MAX_TIMESTAMP_SKEW 160 /*!< maximum difference between actual and predicted ts for sending */
+
+/* If consecutive voice frame timestamps jump by more than this many milliseconds, then jitter buffer will resync */
+#define TS_GAP_FOR_JB_RESYNC 5000
+
+static int iaxthreadcount = DEFAULT_THREAD_COUNT;
+static int iaxmaxthreadcount = DEFAULT_MAX_THREAD_COUNT;
+static int iaxdynamicthreadcount = 0;
+static int iaxactivethreadcount = 0;
+
+struct iax_rr {
+ int jitter;
+ int losspct;
+ int losscnt;
+ int packets;
+ int delay;
+ int dropped;
+ int ooo;
+};
+
+struct iax2_pvt_ref;
+
+struct chan_iax2_pvt {
+ /*! Socket to send/receive on for this call */
+ int sockfd;
+ /*! Last received voice format */
+ int voiceformat;
+ /*! Last received video format */
+ int videoformat;
+ /*! Last sent voice format */
+ int svoiceformat;
+ /*! Last sent video format */
+ int svideoformat;
+ /*! What we are capable of sending */
+ int capability;
+ /*! Last received timestamp */
+ unsigned int last;
+ /*! Last sent timestamp - never send the same timestamp twice in a single call */
+ unsigned int lastsent;
+ /*! Next outgoing timestamp if everything is good */
+ unsigned int nextpred;
+ /*! True if the last voice we transmitted was not silence/CNG */
+ unsigned int notsilenttx:1;
+ /*! Ping time */
+ unsigned int pingtime;
+ /*! Max time for initial response */
+ int maxtime;
+ /*! Peer Address */
+ struct sockaddr_in addr;
+ /*! Actual used codec preferences */
+ struct ast_codec_pref prefs;
+ /*! Requested codec preferences */
+ struct ast_codec_pref rprefs;
+ /*! Our call number */
+ unsigned short callno;
+ /*! Peer callno */
+ unsigned short peercallno;
+ /*! Negotiated format, this is only used to remember what format was
+ chosen for an unauthenticated call so that the channel can get
+ created later using the right format */
+ int chosenformat;
+ /*! Peer selected format */
+ int peerformat;
+ /*! Peer capability */
+ int peercapability;
+ /*! timeval that we base our transmission on */
+ struct timeval offset;
+ /*! timeval that we base our delivery on */
+ struct timeval rxcore;
+ /*! The jitterbuffer */
+ jitterbuf *jb;
+ /*! active jb read scheduler id */
+ int jbid;
+ /*! LAG */
+ int lag;
+ /*! Error, as discovered by the manager */
+ int error;
+ /*! Owner if we have one */
+ struct ast_channel *owner;
+ /*! What's our state? */
+ struct ast_flags state;
+ /*! Expiry (optional) */
+ int expiry;
+ /*! Next outgoing sequence number */
+ unsigned char oseqno;
+ /*! Next sequence number they have not yet acknowledged */
+ unsigned char rseqno;
+ /*! Next incoming sequence number */
+ unsigned char iseqno;
+ /*! Last incoming sequence number we have acknowledged */
+ unsigned char aseqno;
+
+ AST_DECLARE_STRING_FIELDS(
+ /*! Peer name */
+ AST_STRING_FIELD(peer);
+ /*! Default Context */
+ AST_STRING_FIELD(context);
+ /*! Caller ID if available */
+ AST_STRING_FIELD(cid_num);
+ AST_STRING_FIELD(cid_name);
+ /*! Hidden Caller ID (i.e. ANI) if appropriate */
+ AST_STRING_FIELD(ani);
+ /*! DNID */
+ AST_STRING_FIELD(dnid);
+ /*! RDNIS */
+ AST_STRING_FIELD(rdnis);
+ /*! Requested Extension */
+ AST_STRING_FIELD(exten);
+ /*! Expected Username */
+ AST_STRING_FIELD(username);
+ /*! Expected Secret */
+ AST_STRING_FIELD(secret);
+ /*! MD5 challenge */
+ AST_STRING_FIELD(challenge);
+ /*! Public keys permitted keys for incoming authentication */
+ AST_STRING_FIELD(inkeys);
+ /*! Private key for outgoing authentication */
+ AST_STRING_FIELD(outkey);
+ /*! Preferred language */
+ AST_STRING_FIELD(language);
+ /*! Hostname/peername for naming purposes */
+ AST_STRING_FIELD(host);
+
+ AST_STRING_FIELD(dproot);
+ AST_STRING_FIELD(accountcode);
+ AST_STRING_FIELD(mohinterpret);
+ AST_STRING_FIELD(mohsuggest);
+ /*! received OSP token */
+ AST_STRING_FIELD(osptoken);
+ );
+
+ /*! permitted authentication methods */
+ int authmethods;
+ /*! permitted encryption methods */
+ int encmethods;
+ /*! Encryption AES-128 Key */
+ ast_aes_encrypt_key ecx;
+ /*! Decryption AES-128 Key */
+ ast_aes_decrypt_key dcx;
+ /*! 32 bytes of semi-random data */
+ unsigned char semirand[32];
+ /*! Associated registry */
+ struct iax2_registry *reg;
+ /*! Associated peer for poking */
+ struct iax2_peer *peerpoke;
+ /*! IAX_ flags */
+ unsigned int flags;
+ int adsi;
+
+ /*! Transferring status */
+ enum iax_transfer_state transferring;
+ /*! Transfer identifier */
+ int transferid;
+ /*! Who we are IAX transferring to */
+ struct sockaddr_in transfer;
+ /*! What's the new call number for the transfer */
+ unsigned short transfercallno;
+ /*! Transfer encrypt AES-128 Key */
+ ast_aes_encrypt_key tdcx;
+
+ /*! Status of knowledge of peer ADSI capability */
+ int peeradsicpe;
+
+ /*! Who we are bridged to */
+ unsigned short bridgecallno;
+
+ int pingid; /*!< Transmit PING request */
+ int lagid; /*!< Retransmit lag request */
+ int autoid; /*!< Auto hangup for Dialplan requestor */
+ int authid; /*!< Authentication rejection ID */
+ int authfail; /*!< Reason to report failure */
+ int initid; /*!< Initial peer auto-congest ID (based on qualified peers) */
+ int calling_ton;
+ int calling_tns;
+ int calling_pres;
+ int amaflags;
+ AST_LIST_HEAD_NOLOCK(, iax2_dpcache) dpentries;
+ struct ast_variable *vars;
+ /*! last received remote rr */
+ struct iax_rr remote_rr;
+ /*! Current base time: (just for stats) */
+ int min;
+ /*! Dropped frame count: (just for stats) */
+ int frames_dropped;
+ /*! received frame count: (just for stats) */
+ int frames_received;
+};
+
+/*!
+ * \brief a list of frames that may need to be retransmitted
+ *
+ * \note The contents of this list do not need to be explicitly destroyed
+ * on module unload. This is because all active calls are destroyed, and
+ * all frames in this queue will get destroyed as a part of that process.
+ */
+static AST_LIST_HEAD_STATIC(frame_queue, iax_frame);
+
+/*!
+ * This module will get much higher performance when doing a lot of
+ * user and peer lookups if the number of buckets is increased from 1.
+ * However, to maintain old behavior for Asterisk 1.4, these are set to
+ * 1 by default. When using multiple buckets, search order through these
+ * containers is considered random, so you will not be able to depend on
+ * the order the entires are specified in iax.conf for matching order. */
+#ifdef LOW_MEMORY
+#define MAX_PEER_BUCKETS 17
+#else
+#define MAX_PEER_BUCKETS 563
+#endif
+static struct ao2_container *peers;
+
+#define MAX_USER_BUCKETS MAX_PEER_BUCKETS
+static struct ao2_container *users;
+
+static AST_LIST_HEAD_STATIC(firmwares, iax_firmware);
+
+enum {
+ /*! Extension exists */
+ CACHE_FLAG_EXISTS = (1 << 0),
+ /*! Extension is nonexistent */
+ CACHE_FLAG_NONEXISTENT = (1 << 1),
+ /*! Extension can exist */
+ CACHE_FLAG_CANEXIST = (1 << 2),
+ /*! Waiting to hear back response */
+ CACHE_FLAG_PENDING = (1 << 3),
+ /*! Timed out */
+ CACHE_FLAG_TIMEOUT = (1 << 4),
+ /*! Request transmitted */
+ CACHE_FLAG_TRANSMITTED = (1 << 5),
+ /*! Timeout */
+ CACHE_FLAG_UNKNOWN = (1 << 6),
+ /*! Matchmore */
+ CACHE_FLAG_MATCHMORE = (1 << 7),
+};
+
+struct iax2_dpcache {
+ char peercontext[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ struct timeval orig;
+ struct timeval expiry;
+ int flags;
+ unsigned short callno;
+ int waiters[256];
+ AST_LIST_ENTRY(iax2_dpcache) cache_list;
+ AST_LIST_ENTRY(iax2_dpcache) peer_list;
+};
+
+static AST_LIST_HEAD_STATIC(dpcache, iax2_dpcache);
+
+static void reg_source_db(struct iax2_peer *p);
+static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
+
+static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt);
+static char *complete_iax2_show_peer(const char *line, const char *word, int pos, int state);
+static char *complete_iax2_unregister(const char *line, const char *word, int pos, int state);
+
+enum iax2_thread_iostate {
+ IAX_IOSTATE_IDLE,
+ IAX_IOSTATE_READY,
+ IAX_IOSTATE_PROCESSING,
+ IAX_IOSTATE_SCHEDREADY,
+};
+
+enum iax2_thread_type {
+ IAX_THREAD_TYPE_POOL,
+ IAX_THREAD_TYPE_DYNAMIC,
+};
+
+struct iax2_pkt_buf {
+ AST_LIST_ENTRY(iax2_pkt_buf) entry;
+ size_t len;
+ unsigned char buf[1];
+};
+
+struct iax2_thread {
+ AST_LIST_ENTRY(iax2_thread) list;
+ enum iax2_thread_type type;
+ enum iax2_thread_iostate iostate;
+#ifdef SCHED_MULTITHREADED
+ void (*schedfunc)(const void *);
+ const void *scheddata;
+#endif
+#ifdef DEBUG_SCHED_MULTITHREAD
+ char curfunc[80];
+#endif
+ int actions;
+ pthread_t threadid;
+ int threadnum;
+ struct sockaddr_in iosin;
+ unsigned char readbuf[4096];
+ unsigned char *buf;
+ ssize_t buf_len;
+ size_t buf_size;
+ int iofd;
+ time_t checktime;
+ ast_mutex_t lock;
+ ast_cond_t cond;
+ unsigned int ready_for_signal:1;
+ /*! if this thread is processing a full frame,
+ some information about that frame will be stored
+ here, so we can avoid dispatching any more full
+ frames for that callno to other threads */
+ struct {
+ unsigned short callno;
+ struct sockaddr_in sin;
+ unsigned char type;
+ unsigned char csub;
+ } ffinfo;
+ /*! Queued up full frames for processing. If more full frames arrive for
+ * a call which this thread is already processing a full frame for, they
+ * are queued up here. */
+ AST_LIST_HEAD_NOLOCK(, iax2_pkt_buf) full_frames;
+};
+
+/* Thread lists */
+static AST_LIST_HEAD_STATIC(idle_list, iax2_thread);
+static AST_LIST_HEAD_STATIC(active_list, iax2_thread);
+static AST_LIST_HEAD_STATIC(dynamic_list, iax2_thread);
+
+static void *iax2_process_thread(void *data);
+
+static void signal_condition(ast_mutex_t *lock, ast_cond_t *cond)
+{
+ ast_mutex_lock(lock);
+ ast_cond_signal(cond);
+ ast_mutex_unlock(lock);
+}
+
+static void iax_debug_output(const char *data)
+{
+ if (iaxdebug)
+ ast_verbose("%s", data);
+}
+
+static void iax_error_output(const char *data)
+{
+ ast_log(LOG_WARNING, "%s", data);
+}
+
+static void jb_error_output(const char *fmt, ...)
+{
+ va_list args;
+ char buf[1024];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ ast_log(LOG_ERROR, buf);
+}
+
+static void jb_warning_output(const char *fmt, ...)
+{
+ va_list args;
+ char buf[1024];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ ast_log(LOG_WARNING, buf);
+}
+
+static void jb_debug_output(const char *fmt, ...)
+{
+ va_list args;
+ char buf[1024];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ ast_verbose(buf);
+}
+
+/*!
+ * \brief an array of iax2 pvt structures
+ *
+ * The container for active chan_iax2_pvt structures is implemented as an
+ * array for extremely quick direct access to the correct pvt structure
+ * based on the local call number. The local call number is used as the
+ * index into the array where the associated pvt structure is stored.
+ */
+static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
+/*!
+ * \brief chan_iax2_pvt structure locks
+ *
+ * These locks are used when accessing a pvt structure in the iaxs array.
+ * The index used here is the same as used in the iaxs array. It is the
+ * local call number for the associated pvt struct.
+ */
+static ast_mutex_t iaxsl[IAX_MAX_CALLS];
+/*!
+ * \brief The last time a call number was used
+ *
+ * It is important to know the last time that a call number was used locally so
+ * that it is not used again too soon. The reason for this is the same as the
+ * reason that the TCP protocol state machine requires a "TIME WAIT" state.
+ *
+ * For example, say that a call is up. Then, the remote side sends a HANGUP,
+ * which we respond to with an ACK. However, there is no way to know whether
+ * the ACK made it there successfully. If it were to get lost, the remote
+ * side may retransmit the HANGUP. If in the meantime, this call number has
+ * been reused locally, given the right set of circumstances, this retransmitted
+ * HANGUP could potentially improperly hang up the new session. So, to avoid
+ * this potential issue, we must wait a specified timeout period before reusing
+ * a local call number.
+ *
+ * The specified time that we must wait before reusing a local call number is
+ * defined as MIN_REUSE_TIME, with a default of 60 seconds.
+ */
+static struct timeval lastused[IAX_MAX_CALLS];
+
+static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
+static int expire_registry(const void *data);
+static int iax2_answer(struct ast_channel *c);
+static int iax2_call(struct ast_channel *c, char *dest, int timeout);
+static int iax2_devicestate(void *data);
+static int iax2_digit_begin(struct ast_channel *c, char digit);
+static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration);
+static int iax2_do_register(struct iax2_registry *reg);
+static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan);
+static int iax2_hangup(struct ast_channel *c);
+static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen);
+static int iax2_poke_peer(struct iax2_peer *peer, int heldcall);
+static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force);
+static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final);
+static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen);
+static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img);
+static int iax2_sendtext(struct ast_channel *c, const char *text);
+static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen);
+static int iax2_transfer(struct ast_channel *c, const char *dest);
+static int iax2_write(struct ast_channel *c, struct ast_frame *f);
+static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now);
+static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
+static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
+static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
+static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int);
+static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int);
+static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause);
+static struct ast_frame *iax2_read(struct ast_channel *c);
+static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
+static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
+static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
+static void prune_peers(void);
+static void *iax2_dup_variable_datastore(void *);
+static void iax2_free_variable_datastore(void *);
+
+static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
+static int acf_channel_write(struct ast_channel *chan, const char *function, char *data, const char *value);
+
+static const struct ast_channel_tech iax2_tech = {
+ .type = "IAX2",
+ .description = tdesc,
+ .capabilities = IAX_CAPABILITY_FULLBANDWIDTH,
+ .properties = AST_CHAN_TP_WANTSJITTER,
+ .requester = iax2_request,
+ .devicestate = iax2_devicestate,
+ .send_digit_begin = iax2_digit_begin,
+ .send_digit_end = iax2_digit_end,
+ .send_text = iax2_sendtext,
+ .send_image = iax2_sendimage,
+ .send_html = iax2_sendhtml,
+ .call = iax2_call,
+ .hangup = iax2_hangup,
+ .answer = iax2_answer,
+ .read = iax2_read,
+ .write = iax2_write,
+ .write_video = iax2_write,
+ .indicate = iax2_indicate,
+ .setoption = iax2_setoption,
+ .bridge = iax2_bridge,
+ .transfer = iax2_transfer,
+ .fixup = iax2_fixup,
+ .func_channel_read = acf_channel_read,
+ .func_channel_write = acf_channel_write,
+};
+
+static void mwi_event_cb(const struct ast_event *event, void *userdata)
+{
+ /* The MWI subscriptions exist just so the core knows we care about those
+ * mailboxes. However, we just grab the events out of the cache when it
+ * is time to send MWI, since it is only sent with a REGACK. */
+}
+
+/*! \brief Send manager event at call setup to link between Asterisk channel name
+ and IAX2 call identifiers */
+static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
+{
+ manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
+ "Channel: %s\r\nChanneltype: IAX2\r\nIAX2-callno-local: %d\r\nIAX2-callno-remote: %d\r\nIAX2-peer: %s\r\n",
+ pvt->owner ? pvt->owner->name : "",
+ pvt->callno, pvt->peercallno, pvt->peer ? pvt->peer : "");
+}
+
+
+static struct ast_datastore_info iax2_variable_datastore_info = {
+ .type = "IAX2_VARIABLE",
+ .duplicate = iax2_dup_variable_datastore,
+ .destroy = iax2_free_variable_datastore,
+};
+
+static void *iax2_dup_variable_datastore(void *old)
+{
+ AST_LIST_HEAD(, ast_var_t) *oldlist = old, *newlist;
+ struct ast_var_t *oldvar, *newvar;
+
+ newlist = ast_calloc(sizeof(*newlist), 1);
+ if (!newlist) {
+ ast_log(LOG_ERROR, "Unable to duplicate iax2 variables\n");
+ return NULL;
+ }
+
+ AST_LIST_HEAD_INIT(newlist);
+ AST_LIST_LOCK(oldlist);
+ AST_LIST_TRAVERSE(oldlist, oldvar, entries) {
+ newvar = ast_var_assign(ast_var_name(oldvar), ast_var_value(oldvar));
+ if (newvar)
+ AST_LIST_INSERT_TAIL(newlist, newvar, entries);
+ else
+ ast_log(LOG_ERROR, "Unable to duplicate iax2 variable '%s'\n", ast_var_name(oldvar));
+ }
+ AST_LIST_UNLOCK(oldlist);
+ return newlist;
+}
+
+static void iax2_free_variable_datastore(void *old)
+{
+ AST_LIST_HEAD(, ast_var_t) *oldlist = old;
+ struct ast_var_t *oldvar;
+
+ AST_LIST_LOCK(oldlist);
+ while ((oldvar = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
+ ast_free(oldvar);
+ }
+ AST_LIST_UNLOCK(oldlist);
+ AST_LIST_HEAD_DESTROY(oldlist);
+ ast_free(oldlist);
+}
+
+
+/* WARNING: insert_idle_thread should only ever be called within the
+ * context of an iax2_process_thread() thread.
+ */
+static void insert_idle_thread(struct iax2_thread *thread)
+{
+ if (thread->type == IAX_THREAD_TYPE_DYNAMIC) {
+ AST_LIST_LOCK(&dynamic_list);
+ AST_LIST_INSERT_TAIL(&dynamic_list, thread, list);
+ AST_LIST_UNLOCK(&dynamic_list);
+ } else {
+ AST_LIST_LOCK(&idle_list);
+ AST_LIST_INSERT_TAIL(&idle_list, thread, list);
+ AST_LIST_UNLOCK(&idle_list);
+ }
+
+ return;
+}
+
+static struct iax2_thread *find_idle_thread(void)
+{
+ struct iax2_thread *thread = NULL;
+
+ /* Pop the head of the idle list off */
+ AST_LIST_LOCK(&idle_list);
+ thread = AST_LIST_REMOVE_HEAD(&idle_list, list);
+ AST_LIST_UNLOCK(&idle_list);
+
+ /* If we popped a thread off the idle list, just return it */
+ if (thread) {
+ memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
+ return thread;
+ }
+
+ /* Pop the head of the dynamic list off */
+ AST_LIST_LOCK(&dynamic_list);
+ thread = AST_LIST_REMOVE_HEAD(&dynamic_list, list);
+ AST_LIST_UNLOCK(&dynamic_list);
+
+ /* If we popped a thread off the dynamic list, just return it */
+ if (thread) {
+ memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
+ return thread;
+ }
+
+ /* If we can't create a new dynamic thread for any reason, return no thread at all */
+ if (iaxdynamicthreadcount >= iaxmaxthreadcount || !(thread = ast_calloc(1, sizeof(*thread))))
+ return NULL;
+
+ /* Set default values */
+ thread->threadnum = ast_atomic_fetchadd_int(&iaxdynamicthreadcount, 1);
+ thread->type = IAX_THREAD_TYPE_DYNAMIC;
+
+ /* Initialize lock and condition */
+ ast_mutex_init(&thread->lock);
+ ast_cond_init(&thread->cond, NULL);
+
+ /* Create thread and send it on it's way */
+ if (ast_pthread_create_detached_background(&thread->threadid, NULL, iax2_process_thread, thread)) {
+ ast_cond_destroy(&thread->cond);
+ ast_mutex_destroy(&thread->lock);
+ ast_free(thread);
+ return NULL;
+ }
+
+ /* this thread is not processing a full frame (since it is idle),
+ so ensure that the field for the full frame call number is empty */
+ memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
+
+ /* Wait for the thread to be ready before returning it to the caller */
+ while (!thread->ready_for_signal)
+ usleep(1);
+
+ return thread;
+}
+
+#ifdef SCHED_MULTITHREADED
+static int __schedule_action(void (*func)(const void *data), const void *data, const char *funcname)
+{
+ struct iax2_thread *thread = NULL;
+ static time_t lasterror;
+ static time_t t;
+
+ thread = find_idle_thread();
+
+ if (thread != NULL) {
+ thread->schedfunc = func;
+ thread->scheddata = data;
+ thread->iostate = IAX_IOSTATE_SCHEDREADY;
+#ifdef DEBUG_SCHED_MULTITHREAD
+ ast_copy_string(thread->curfunc, funcname, sizeof(thread->curfunc));
+#endif
+ signal_condition(&thread->lock, &thread->cond);
+ return 0;
+ }
+ time(&t);
+ if (t != lasterror)
+ ast_log(LOG_NOTICE, "Out of idle IAX2 threads for scheduling!\n");
+ lasterror = t;
+
+ return -1;
+}
+#define schedule_action(func, data) __schedule_action(func, data, __PRETTY_FUNCTION__)
+#endif
+
+static int iax2_sched_replace(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data)
+{
+ int res;
+
+ res = ast_sched_replace(old_id, con, when, callback, data);
+ signal_condition(&sched_lock, &sched_cond);
+
+ return res;
+}
+
+static int iax2_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data)
+{
+ int res;
+
+ res = ast_sched_add(con, when, callback, data);
+ signal_condition(&sched_lock, &sched_cond);
+
+ return res;
+}
+
+static int send_ping(const void *data);
+
+static void __send_ping(const void *data)
+{
+ int callno = (long)data;
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno] && iaxs[callno]->pingid != -1) {
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
+ iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+}
+
+static int send_ping(const void *data)
+{
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__send_ping, data))
+#endif
+ __send_ping(data);
+ return 0;
+}
+
+static int get_encrypt_methods(const char *s)
+{
+ int e;
+ if (!strcasecmp(s, "aes128"))
+ e = IAX_ENCRYPT_AES128;
+ else if (ast_true(s))
+ e = IAX_ENCRYPT_AES128;
+ else
+ e = 0;
+ return e;
+}
+
+static int send_lagrq(const void *data);
+
+static void __send_lagrq(const void *data)
+{
+ int callno = (long)data;
+ /* Ping only if it's real not if it's bridged */
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno] && iaxs[callno]->lagid != -1) {
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
+ iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+}
+
+static int send_lagrq(const void *data)
+{
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__send_lagrq, data))
+#endif
+ __send_lagrq(data);
+ return 0;
+}
+
+static unsigned char compress_subclass(int subclass)
+{
+ int x;
+ int power=-1;
+ /* If it's 128 or smaller, just return it */
+ if (subclass < IAX_FLAG_SC_LOG)
+ return subclass;
+ /* Otherwise find its power */
+ for (x = 0; x < IAX_MAX_SHIFT; x++) {
+ if (subclass & (1 << x)) {
+ if (power > -1) {
+ ast_log(LOG_WARNING, "Can't compress subclass %d\n", subclass);
+ return 0;
+ } else
+ power = x;
+ }
+ }
+ return power | IAX_FLAG_SC_LOG;
+}
+
+static int uncompress_subclass(unsigned char csub)
+{
+ /* If the SC_LOG flag is set, return 2^csub otherwise csub */
+ if (csub & IAX_FLAG_SC_LOG) {
+ /* special case for 'compressed' -1 */
+ if (csub == 0xff)
+ return -1;
+ else
+ return 1 << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT);
+ }
+ else
+ return csub;
+}
+
+/*!
+ * \note The only member of the peer passed here guaranteed to be set is the name field
+ */
+static int peer_hash_cb(const void *obj, const int flags)
+{
+ const struct iax2_peer *peer = obj;
+
+ return ast_str_hash(peer->name);
+}
+
+/*!
+ * \note The only member of the peer passed here guaranteed to be set is the name field
+ */
+static int peer_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj, *peer2 = arg;
+
+ return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_hash_cb(const void *obj, const int flags)
+{
+ const struct iax2_user *user = obj;
+
+ return ast_str_hash(user->name);
+}
+
+/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj, *user2 = arg;
+
+ return !strcasecmp(user->name, user2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note This funtion calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
+ * so do not call it with a pvt lock held.
+ */
+static struct iax2_peer *find_peer(const char *name, int realtime)
+{
+ struct iax2_peer *peer = NULL;
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
+
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+
+ /* Now go for realtime if applicable */
+ if(!peer && realtime)
+ peer = realtime_peer(name, NULL);
+
+ return peer;
+}
+
+static struct iax2_peer *peer_ref(struct iax2_peer *peer)
+{
+ ao2_ref(peer, +1);
+ return peer;
+}
+
+static inline struct iax2_peer *peer_unref(struct iax2_peer *peer)
+{
+ ao2_ref(peer, -1);
+ return NULL;
+}
+
+static inline struct iax2_user *user_ref(struct iax2_user *user)
+{
+ ao2_ref(user, +1);
+ return user;
+}
+
+static inline struct iax2_user *user_unref(struct iax2_user *user)
+{
+ ao2_ref(user, -1);
+ return NULL;
+}
+
+static int iax2_getpeername(struct sockaddr_in sin, char *host, int len)
+{
+ struct iax2_peer *peer = NULL;
+ int res = 0;
+ struct ao2_iterator i;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
+ (peer->addr.sin_port == sin.sin_port)) {
+ ast_copy_string(host, peer->name, len);
+ peer_unref(peer);
+ res = 1;
+ break;
+ }
+ peer_unref(peer);
+ }
+
+ if (!peer) {
+ peer = realtime_peer(NULL, &sin);
+ if (peer) {
+ ast_copy_string(host, peer->name, len);
+ peer_unref(peer);
+ res = 1;
+ }
+ }
+
+ return res;
+}
+
+static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host)
+{
+ struct chan_iax2_pvt *tmp;
+ jb_conf jbconf;
+
+ if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ return NULL;
+
+ if (ast_string_field_init(tmp, 32)) {
+ ast_free(tmp);
+ tmp = NULL;
+ return NULL;
+ }
+
+ tmp->prefs = prefs;
+ tmp->pingid = -1;
+ tmp->lagid = -1;
+ tmp->autoid = -1;
+ tmp->authid = -1;
+ tmp->initid = -1;
+
+ ast_string_field_set(tmp,exten, "s");
+ ast_string_field_set(tmp,host, host);
+
+ tmp->jb = jb_new();
+ tmp->jbid = -1;
+ jbconf.max_jitterbuf = maxjitterbuffer;
+ jbconf.resync_threshold = resyncthreshold;
+ jbconf.max_contig_interp = maxjitterinterps;
+ jbconf.target_extra = jittertargetextra;
+ jb_setconf(tmp->jb,&jbconf);
+
+ AST_LIST_HEAD_INIT_NOLOCK(&tmp->dpentries);
+
+ return tmp;
+}
+
+static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
+{
+ struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen, fr->cacheable);
+ if (new) {
+ size_t afdatalen = new->afdatalen;
+ memcpy(new, fr, sizeof(*new));
+ iax_frame_wrap(new, &fr->af);
+ new->afdatalen = afdatalen;
+ new->data = NULL;
+ new->datalen = 0;
+ new->direction = DIRECTION_INGRESS;
+ new->retrans = -1;
+ }
+ return new;
+}
+
+#define NEW_PREVENT 0
+#define NEW_ALLOW 1
+#define NEW_FORCE 2
+
+static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur)
+{
+ if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
+ (cur->addr.sin_port == sin->sin_port)) {
+ /* This is the main host */
+ if ((cur->peercallno == callno) ||
+ ((dcallno == cur->callno) && !cur->peercallno)) {
+ /* That's us. Be sure we keep track of the peer call number */
+ return 1;
+ }
+ }
+ if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
+ (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
+ /* We're transferring */
+ if ((dcallno == cur->callno) || (cur->transferring == TRANSFER_MEDIAPASS && cur->transfercallno == callno))
+ return 1;
+ }
+ return 0;
+}
+
+static void update_max_trunk(void)
+{
+ int max = TRUNK_CALL_START;
+ int x;
+ /* XXX Prolly don't need locks here XXX */
+ for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
+ if (iaxs[x])
+ max = x + 1;
+ }
+ maxtrunkcall = max;
+ if (iaxdebug)
+ ast_debug(1, "New max trunk callno is %d\n", max);
+}
+
+static void update_max_nontrunk(void)
+{
+ int max = 1;
+ int x;
+ /* XXX Prolly don't need locks here XXX */
+ for (x=1;x<TRUNK_CALL_START - 1; x++) {
+ if (iaxs[x])
+ max = x + 1;
+ }
+ maxnontrunkcall = max;
+ if (iaxdebug)
+ ast_debug(1, "New max nontrunk callno is %d\n", max);
+}
+
+static int make_trunk(unsigned short callno, int locked)
+{
+ int x;
+ int res= 0;
+ struct timeval now = ast_tvnow();
+ if (iaxs[callno]->oseqno) {
+ ast_log(LOG_WARNING, "Can't make trunk once a call has started!\n");
+ return -1;
+ }
+ if (callno & TRUNK_CALL_START) {
+ ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno);
+ return -1;
+ }
+ for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
+ ast_mutex_lock(&iaxsl[x]);
+ if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
+ iaxs[x] = iaxs[callno];
+ iaxs[x]->callno = x;
+ iaxs[callno] = NULL;
+ /* Update the two timers that should have been started */
+ iaxs[x]->pingid = iax2_sched_replace(iaxs[x]->pingid, sched,
+ ping_time * 1000, send_ping, (void *)(long)x);
+ iaxs[x]->lagid = iax2_sched_replace(iaxs[x]->lagid, sched,
+ lagrq_time * 1000, send_lagrq, (void *)(long)x);
+ if (locked)
+ ast_mutex_unlock(&iaxsl[callno]);
+ res = x;
+ if (!locked)
+ ast_mutex_unlock(&iaxsl[x]);
+ break;
+ }
+ ast_mutex_unlock(&iaxsl[x]);
+ }
+ if (x >= IAX_MAX_CALLS - 1) {
+ ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n");
+ return -1;
+ }
+ ast_debug(1, "Made call %d into trunk call %d\n", callno, x);
+ /* We move this call from a non-trunked to a trunked call */
+ update_max_trunk();
+ update_max_nontrunk();
+ return res;
+}
+
+/*!
+ * \todo XXX Note that this function contains a very expensive operation that
+ * happens for *every* incoming media frame. It iterates through every
+ * possible call number, locking and unlocking each one, to try to match the
+ * incoming frame to an active call. Call numbers can be up to 2^15, 32768.
+ * So, for a call with a local call number of 20000, every incoming audio
+ * frame would require 20000 mutex lock and unlock operations. Ouch.
+ *
+ * It's a shame that IAX2 media frames carry the source call number instead of
+ * the destination call number. If they did, this lookup wouldn't be needed.
+ * However, it's too late to change that now. Instead, we need to come up with
+ * a better way of indexing active calls so that these frequent lookups are not
+ * so expensive.
+ *
+ * \note Calling this function while holding another pvt lock can cause a deadlock.
+ */
+static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd)
+{
+ int res = 0;
+ int x;
+ struct timeval now;
+ char host[80];
+
+ if (new <= NEW_ALLOW) {
+ for (x=1;(res < 1) && (x<maxnontrunkcall);x++) {
+ ast_mutex_lock(&iaxsl[x]);
+ if (iaxs[x]) {
+ /* Look for an exact match */
+ if (match(sin, callno, dcallno, iaxs[x])) {
+ res = x;
+ }
+ }
+ ast_mutex_unlock(&iaxsl[x]);
+ }
+ for (x=TRUNK_CALL_START;(res < 1) && (x<maxtrunkcall);x++) {
+ ast_mutex_lock(&iaxsl[x]);
+ if (iaxs[x]) {
+ /* Look for an exact match */
+ if (match(sin, callno, dcallno, iaxs[x])) {
+ res = x;
+ }
+ }
+ ast_mutex_unlock(&iaxsl[x]);
+ }
+ }
+ if ((res < 1) && (new >= NEW_ALLOW)) {
+ /* It may seem odd that we look through the peer list for a name for
+ * this *incoming* call. Well, it is weird. However, users don't
+ * have an IP address/port number that we can match against. So,
+ * this is just checking for a peer that has that IP/port and
+ * assuming that we have a user of the same name. This isn't always
+ * correct, but it will be changed if needed after authentication. */
+ if (!iax2_getpeername(*sin, host, sizeof(host)))
+ snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+ now = ast_tvnow();
+ for (x=1;x<TRUNK_CALL_START;x++) {
+ /* Find first unused call number that hasn't been used in a while */
+ ast_mutex_lock(&iaxsl[x]);
+ if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) break;
+ ast_mutex_unlock(&iaxsl[x]);
+ }
+ /* We've still got lock held if we found a spot */
+ if (x >= TRUNK_CALL_START) {
+ ast_log(LOG_WARNING, "No more space\n");
+ return 0;
+ }
+ iaxs[x] = new_iax(sin, host);
+ update_max_nontrunk();
+ if (iaxs[x]) {
+ if (iaxdebug)
+ ast_debug(1, "Creating new call structure %d\n", x);
+ iaxs[x]->sockfd = sockfd;
+ iaxs[x]->addr.sin_port = sin->sin_port;
+ iaxs[x]->addr.sin_family = sin->sin_family;
+ iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr;
+ iaxs[x]->peercallno = callno;
+ iaxs[x]->callno = x;
+ iaxs[x]->pingtime = DEFAULT_RETRY_TIME;
+ iaxs[x]->expiry = min_reg_expire;
+ iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
+ iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
+ iaxs[x]->amaflags = amaflags;
+ ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+
+ ast_string_field_set(iaxs[x], accountcode, accountcode);
+ ast_string_field_set(iaxs[x], mohinterpret, mohinterpret);
+ ast_string_field_set(iaxs[x], mohsuggest, mohsuggest);
+ } else {
+ ast_log(LOG_WARNING, "Out of resources\n");
+ ast_mutex_unlock(&iaxsl[x]);
+ return 0;
+ }
+ ast_mutex_unlock(&iaxsl[x]);
+ res = x;
+ }
+ return res;
+}
+
+static void iax2_frame_free(struct iax_frame *fr)
+{
+ if (fr->retrans > -1)
+ ast_sched_del(sched, fr->retrans);
+ iax_frame_free(fr);
+}
+
+/*!
+ * \brief Queue a frame to a call's owning asterisk channel
+ *
+ * \pre This function assumes that iaxsl[callno] is locked when called.
+ *
+ * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
+ * was valid before calling it, it may no longer be valid after calling it.
+ * This function may unlock and lock the mutex associated with this callno,
+ * meaning that another thread may grab it and destroy the call.
+ */
+static int iax2_queue_frame(int callno, struct ast_frame *f)
+{
+ for (;;) {
+ if (iaxs[callno] && iaxs[callno]->owner) {
+ if (ast_channel_trylock(iaxs[callno]->owner)) {
+ /* Avoid deadlock by pausing and trying again */
+ ast_mutex_unlock(&iaxsl[callno]);
+ usleep(1);
+ ast_mutex_lock(&iaxsl[callno]);
+ } else {
+ ast_queue_frame(iaxs[callno]->owner, f);
+ ast_channel_unlock(iaxs[callno]->owner);
+ break;
+ }
+ } else
+ break;
+ }
+ return 0;
+}
+
+/*!
+ * \brief Queue a hangup frame on the ast_channel owner
+ *
+ * This function queues a hangup frame on the owner of the IAX2 pvt struct that
+ * is active for the given call number.
+ *
+ * \pre Assumes lock for callno is already held.
+ *
+ * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
+ * was valid before calling it, it may no longer be valid after calling it.
+ * This function may unlock and lock the mutex associated with this callno,
+ * meaning that another thread may grab it and destroy the call.
+ */
+static int iax2_queue_hangup(int callno)
+{
+ for (;;) {
+ if (iaxs[callno] && iaxs[callno]->owner) {
+ if (ast_channel_trylock(iaxs[callno]->owner)) {
+ /* Avoid deadlock by pausing and trying again */
+ ast_mutex_unlock(&iaxsl[callno]);
+ usleep(1);
+ ast_mutex_lock(&iaxsl[callno]);
+ } else {
+ ast_queue_hangup(iaxs[callno]->owner);
+ ast_channel_unlock(iaxs[callno]->owner);
+ break;
+ }
+ } else
+ break;
+ }
+ return 0;
+}
+
+/*!
+ * \brief Queue a control frame on the ast_channel owner
+ *
+ * This function queues a control frame on the owner of the IAX2 pvt struct that
+ * is active for the given call number.
+ *
+ * \pre Assumes lock for callno is already held.
+ *
+ * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
+ * was valid before calling it, it may no longer be valid after calling it.
+ * This function may unlock and lock the mutex associated with this callno,
+ * meaning that another thread may grab it and destroy the call.
+ */
+static int iax2_queue_control_data(int callno,
+ enum ast_control_frame_type control, const void *data, size_t datalen)
+{
+ for (;;) {
+ if (iaxs[callno] && iaxs[callno]->owner) {
+ if (ast_channel_trylock(iaxs[callno]->owner)) {
+ /* Avoid deadlock by pausing and trying again */
+ ast_mutex_unlock(&iaxsl[callno]);
+ usleep(1);
+ ast_mutex_lock(&iaxsl[callno]);
+ } else {
+ ast_queue_control_data(iaxs[callno]->owner, control, data, datalen);
+ ast_channel_unlock(iaxs[callno]->owner);
+ break;
+ }
+ } else
+ break;
+ }
+ return 0;
+}
+static void destroy_firmware(struct iax_firmware *cur)
+{
+ /* Close firmware */
+ if (cur->fwh) {
+ munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
+ }
+ close(cur->fd);
+ ast_free(cur);
+}
+
+static int try_firmware(char *s)
+{
+ struct stat stbuf;
+ struct iax_firmware *cur = NULL;
+ int ifd, fd, res, len, chunk;
+ struct ast_iax2_firmware_header *fwh, fwh2;
+ struct MD5Context md5;
+ unsigned char sum[16], buf[1024];
+ char *s2, *last;
+
+ if (!(s2 = alloca(strlen(s) + 100))) {
+ ast_log(LOG_WARNING, "Alloca failed!\n");
+ return -1;
+ }
+
+ last = strrchr(s, '/');
+ if (last)
+ last++;
+ else
+ last = s;
+
+ snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, (unsigned long)ast_random());
+
+ if ((res = stat(s, &stbuf) < 0)) {
+ ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
+ return -1;
+ }
+
+ /* Make sure it's not a directory */
+ if (S_ISDIR(stbuf.st_mode))
+ return -1;
+ ifd = open(s, O_RDONLY);
+ if (ifd < 0) {
+ ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
+ return -1;
+ }
+ fd = open(s2, O_RDWR | O_CREAT | O_EXCL, AST_FILE_MODE);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
+ close(ifd);
+ return -1;
+ }
+ /* Unlink our newly created file */
+ unlink(s2);
+
+ /* Now copy the firmware into it */
+ len = stbuf.st_size;
+ while(len) {
+ chunk = len;
+ if (chunk > sizeof(buf))
+ chunk = sizeof(buf);
+ res = read(ifd, buf, chunk);
+ if (res != chunk) {
+ ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
+ close(ifd);
+ close(fd);
+ return -1;
+ }
+ res = write(fd, buf, chunk);
+ if (res != chunk) {
+ ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
+ close(ifd);
+ close(fd);
+ return -1;
+ }
+ len -= chunk;
+ }
+ close(ifd);
+ /* Return to the beginning */
+ lseek(fd, 0, SEEK_SET);
+ if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
+ ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
+ close(fd);
+ return -1;
+ }
+ if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
+ ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
+ close(fd);
+ return -1;
+ }
+ if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
+ ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
+ close(fd);
+ return -1;
+ }
+ if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
+ ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
+ close(fd);
+ return -1;
+ }
+ fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (fwh == (void *) -1) {
+ ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ MD5Init(&md5);
+ MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
+ MD5Final(sum, &md5);
+ if (memcmp(sum, fwh->chksum, sizeof(sum))) {
+ ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
+ munmap((void*)fwh, stbuf.st_size);
+ close(fd);
+ return -1;
+ }
+
+ AST_LIST_TRAVERSE(&firmwares, cur, list) {
+ if (!strcmp((char *)cur->fwh->devname, (char *)fwh->devname)) {
+ /* Found a candidate */
+ if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
+ /* The version we have on loaded is older, load this one instead */
+ break;
+ /* This version is no newer than what we have. Don't worry about it.
+ We'll consider it a proper load anyhow though */
+ munmap((void*)fwh, stbuf.st_size);
+ close(fd);
+ return 0;
+ }
+ }
+
+ if (!cur && ((cur = ast_calloc(1, sizeof(*cur))))) {
+ cur->fd = -1;
+ AST_LIST_INSERT_TAIL(&firmwares, cur, list);
+ }
+
+ if (cur) {
+ if (cur->fwh)
+ munmap((void*)cur->fwh, cur->mmaplen);
+ if (cur->fd > -1)
+ close(cur->fd);
+ cur->fwh = fwh;
+ cur->fd = fd;
+ cur->mmaplen = stbuf.st_size;
+ cur->dead = 0;
+ }
+
+ return 0;
+}
+
+static int iax_check_version(char *dev)
+{
+ int res = 0;
+ struct iax_firmware *cur = NULL;
+
+ if (ast_strlen_zero(dev))
+ return 0;
+
+ AST_LIST_LOCK(&firmwares);
+ AST_LIST_TRAVERSE(&firmwares, cur, list) {
+ if (!strcmp(dev, (char *)cur->fwh->devname)) {
+ res = ntohs(cur->fwh->version);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&firmwares);
+
+ return res;
+}
+
+static int iax_firmware_append(struct iax_ie_data *ied, const unsigned char *dev, unsigned int desc)
+{
+ int res = -1;
+ unsigned int bs = desc & 0xff;
+ unsigned int start = (desc >> 8) & 0xffffff;
+ unsigned int bytes;
+ struct iax_firmware *cur;
+
+ if (ast_strlen_zero((char *)dev) || !bs)
+ return -1;
+
+ start *= bs;
+
+ AST_LIST_LOCK(&firmwares);
+ AST_LIST_TRAVERSE(&firmwares, cur, list) {
+ if (strcmp((char *)dev, (char *)cur->fwh->devname))
+ continue;
+ iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc);
+ if (start < ntohl(cur->fwh->datalen)) {
+ bytes = ntohl(cur->fwh->datalen) - start;
+ if (bytes > bs)
+ bytes = bs;
+ iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
+ } else {
+ bytes = 0;
+ iax_ie_append(ied, IAX_IE_FWBLOCKDATA);
+ }
+ if (bytes == bs)
+ res = 0;
+ else
+ res = 1;
+ break;
+ }
+ AST_LIST_UNLOCK(&firmwares);
+
+ return res;
+}
+
+
+static void reload_firmware(int unload)
+{
+ struct iax_firmware *cur = NULL;
+ DIR *fwd;
+ struct dirent *de;
+ char dir[256], fn[256];
+
+ AST_LIST_LOCK(&firmwares);
+
+ /* Mark all as dead */
+ AST_LIST_TRAVERSE(&firmwares, cur, list)
+ cur->dead = 1;
+
+ /* Now that we have marked them dead... load new ones */
+ if (!unload) {
+ snprintf(dir, sizeof(dir), "%s/firmware/iax", ast_config_AST_DATA_DIR);
+ fwd = opendir(dir);
+ if (fwd) {
+ while((de = readdir(fwd))) {
+ if (de->d_name[0] != '.') {
+ snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name);
+ if (!try_firmware(fn)) {
+ ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
+ }
+ }
+ }
+ closedir(fwd);
+ } else
+ ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", dir, strerror(errno));
+ }
+
+ /* Clean up leftovers */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&firmwares, cur, list) {
+ if (!cur->dead)
+ continue;
+ AST_LIST_REMOVE_CURRENT(list);
+ destroy_firmware(cur);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ AST_LIST_UNLOCK(&firmwares);
+}
+
+/*!
+ * \note This function assumes that iaxsl[callno] is locked when called.
+ *
+ * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
+ * was valid before calling it, it may no longer be valid after calling it.
+ * This function calls iax2_queue_frame(), which may unlock and lock the mutex
+ * associated with this callno, meaning that another thread may grab it and destroy the call.
+ */
+static int __do_deliver(void *data)
+{
+ /* Just deliver the packet by using queueing. This is called by
+ the IAX thread with the iaxsl lock held. */
+ struct iax_frame *fr = data;
+ fr->retrans = -1;
+ ast_clear_flag(&fr->af, AST_FRFLAG_HAS_TIMING_INFO);
+ if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE))
+ iax2_queue_frame(fr->callno, &fr->af);
+ /* Free our iax frame */
+ iax2_frame_free(fr);
+ /* And don't run again */
+ return 0;
+}
+
+static int handle_error(void)
+{
+ /* XXX Ideally we should figure out why an error occurred and then abort those
+ rather than continuing to try. Unfortunately, the published interface does
+ not seem to work XXX */
+#if 0
+ struct sockaddr_in *sin;
+ int res;
+ struct msghdr m;
+ struct sock_extended_err e;
+ m.msg_name = NULL;
+ m.msg_namelen = 0;
+ m.msg_iov = NULL;
+ m.msg_control = &e;
+ m.msg_controllen = sizeof(e);
+ m.msg_flags = 0;
+ res = recvmsg(netsocket, &m, MSG_ERRQUEUE);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Error detected, but unable to read error: %s\n", strerror(errno));
+ else {
+ if (m.msg_controllen) {
+ sin = (struct sockaddr_in *)SO_EE_OFFENDER(&e);
+ if (sin)
+ ast_log(LOG_WARNING, "Receive error from %s\n", ast_inet_ntoa(sin->sin_addr));
+ else
+ ast_log(LOG_WARNING, "No address detected??\n");
+ } else {
+ ast_log(LOG_WARNING, "Local error: %s\n", strerror(e.ee_errno));
+ }
+ }
+#endif
+ return 0;
+}
+
+static int transmit_trunk(struct iax_frame *f, struct sockaddr_in *sin, int sockfd)
+{
+ int res;
+ res = sendto(sockfd, f->data, f->datalen, 0,(struct sockaddr *)sin,
+ sizeof(*sin));
+ if (res < 0) {
+ ast_debug(1, "Received error: %s\n", strerror(errno));
+ handle_error();
+ } else
+ res = 0;
+ return res;
+}
+
+static int send_packet(struct iax_frame *f)
+{
+ int res;
+ int callno = f->callno;
+
+ /* Don't send if there was an error, but return error instead */
+ if (!callno || !iaxs[callno] || iaxs[callno]->error)
+ return -1;
+
+ /* Called with iaxsl held */
+ if (iaxdebug)
+ ast_debug(3, "Sending %d on %d/%d to %s:%d\n", f->ts, callno, iaxs[callno]->peercallno, ast_inet_ntoa(iaxs[callno]->addr.sin_addr), ntohs(iaxs[callno]->addr.sin_port));
+ if (f->transfer) {
+ if (iaxdebug)
+ iax_showframe(f, NULL, 0, &iaxs[callno]->transfer, f->datalen - sizeof(struct ast_iax2_full_hdr));
+ res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->transfer,
+ sizeof(iaxs[callno]->transfer));
+ } else {
+ if (iaxdebug)
+ iax_showframe(f, NULL, 0, &iaxs[callno]->addr, f->datalen - sizeof(struct ast_iax2_full_hdr));
+ res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->addr,
+ sizeof(iaxs[callno]->addr));
+ }
+ if (res < 0) {
+ if (iaxdebug)
+ ast_debug(1, "Received error: %s\n", strerror(errno));
+ handle_error();
+ } else
+ res = 0;
+ return res;
+}
+
+static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
+{
+ /* Decrement AUTHREQ count if needed */
+ if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
+ struct iax2_user *user;
+ struct iax2_user tmp_user = {
+ .name = pvt->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
+ user_unref(user);
+ }
+
+ ast_clear_flag(pvt, IAX_MAXAUTHREQ);
+ }
+ /* No more pings or lagrq's */
+ if (pvt->pingid > -1)
+ ast_sched_del(sched, pvt->pingid);
+ pvt->pingid = -1;
+ if (pvt->lagid > -1)
+ ast_sched_del(sched, pvt->lagid);
+ pvt->lagid = -1;
+ if (pvt->autoid > -1)
+ ast_sched_del(sched, pvt->autoid);
+ pvt->autoid = -1;
+ if (pvt->authid > -1)
+ ast_sched_del(sched, pvt->authid);
+ pvt->authid = -1;
+ if (pvt->initid > -1)
+ ast_sched_del(sched, pvt->initid);
+ pvt->initid = -1;
+ if (pvt->jbid > -1)
+ ast_sched_del(sched, pvt->jbid);
+ pvt->jbid = -1;
+}
+
+/*!
+ * \note Since this function calls iax2_queue_hangup(), the pvt struct
+ * for the given call number may disappear during its execution.
+ */
+static int iax2_predestroy(int callno)
+{
+ struct ast_channel *c = NULL;
+ struct chan_iax2_pvt *pvt = iaxs[callno];
+
+ if (!pvt)
+ return -1;
+
+ if (!ast_test_flag(pvt, IAX_ALREADYGONE)) {
+ iax2_destroy_helper(pvt);
+ ast_set_flag(pvt, IAX_ALREADYGONE);
+ }
+
+ if ((c = pvt->owner)) {
+ c->tech_pvt = NULL;
+ iax2_queue_hangup(callno);
+ pvt->owner = NULL;
+ ast_module_unref(ast_module_info->self);
+ }
+
+ return 0;
+}
+
+static void iax2_destroy(int callno)
+{
+ struct chan_iax2_pvt *pvt = NULL;
+ struct iax_frame *cur = NULL;
+ struct ast_channel *owner = NULL;
+
+retry:
+ pvt = iaxs[callno];
+ lastused[callno] = ast_tvnow();
+
+ owner = pvt ? pvt->owner : NULL;
+
+ if (owner) {
+ if (ast_channel_trylock(owner)) {
+ ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
+ ast_mutex_unlock(&iaxsl[callno]);
+ usleep(1);
+ ast_mutex_lock(&iaxsl[callno]);
+ goto retry;
+ }
+ }
+ if (!owner)
+ iaxs[callno] = NULL;
+ if (pvt) {
+ if (!owner)
+ pvt->owner = NULL;
+ iax2_destroy_helper(pvt);
+
+ /* Already gone */
+ ast_set_flag(pvt, IAX_ALREADYGONE);
+
+ if (owner) {
+ /* If there's an owner, prod it to give up */
+ /* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup()
+ * because we already hold the owner channel lock. */
+ ast_queue_hangup(owner);
+ }
+
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_TRAVERSE(&frame_queue, cur, list) {
+ /* Cancel any pending transmissions */
+ if (cur->callno == pvt->callno)
+ cur->retries = -1;
+ }
+ AST_LIST_UNLOCK(&frame_queue);
+
+ if (pvt->reg)
+ pvt->reg->callno = 0;
+ if (!owner) {
+ jb_frame frame;
+ if (pvt->vars) {
+ ast_variables_destroy(pvt->vars);
+ pvt->vars = NULL;
+ }
+
+ while (jb_getall(pvt->jb, &frame) == JB_OK)
+ iax2_frame_free(frame.data);
+ jb_destroy(pvt->jb);
+ /* gotta free up the stringfields */
+ ast_string_field_free_memory(pvt);
+ ast_free(pvt);
+ }
+ }
+ if (owner) {
+ ast_channel_unlock(owner);
+ }
+ if (callno & 0x4000)
+ update_max_trunk();
+}
+
+static int update_packet(struct iax_frame *f)
+{
+ /* Called with iaxsl lock held, and iaxs[callno] non-NULL */
+ struct ast_iax2_full_hdr *fh = f->data;
+ /* Mark this as a retransmission */
+ fh->dcallno = ntohs(IAX_FLAG_RETRANS | f->dcallno);
+ /* Update iseqno */
+ f->iseqno = iaxs[f->callno]->iseqno;
+ fh->iseqno = f->iseqno;
+ return 0;
+}
+
+static int attempt_transmit(const void *data);
+static void __attempt_transmit(const void *data)
+{
+ /* Attempt to transmit the frame to the remote peer...
+ Called without iaxsl held. */
+ struct iax_frame *f = (struct iax_frame *)data;
+ int freeme = 0;
+ int callno = f->callno;
+ /* Make sure this call is still active */
+ if (callno)
+ ast_mutex_lock(&iaxsl[callno]);
+ if (callno && iaxs[callno]) {
+ if ((f->retries < 0) /* Already ACK'd */ ||
+ (f->retries >= max_retries) /* Too many attempts */) {
+ /* Record an error if we've transmitted too many times */
+ if (f->retries >= max_retries) {
+ if (f->transfer) {
+ /* Transfer timeout */
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
+ } else if (f->final) {
+ if (f->final)
+ iax2_destroy(callno);
+ } else {
+ if (iaxs[callno]->owner)
+ ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", ast_inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass, f->ts, f->oseqno);
+ iaxs[callno]->error = ETIMEDOUT;
+ if (iaxs[callno]->owner) {
+ struct ast_frame fr = { 0, };
+ /* Hangup the fd */
+ fr.frametype = AST_FRAME_CONTROL;
+ fr.subclass = AST_CONTROL_HANGUP;
+ iax2_queue_frame(callno, &fr); /* XXX */
+ /* Remember, owner could disappear */
+ if (iaxs[callno] && iaxs[callno]->owner)
+ iaxs[callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
+ } else {
+ if (iaxs[callno]->reg) {
+ memset(&iaxs[callno]->reg->us, 0, sizeof(iaxs[callno]->reg->us));
+ iaxs[callno]->reg->regstate = REG_STATE_TIMEOUT;
+ iaxs[callno]->reg->refresh = IAX_DEFAULT_REG_EXPIRE;
+ }
+ iax2_destroy(callno);
+ }
+ }
+
+ }
+ freeme = 1;
+ } else {
+ /* Update it if it needs it */
+ update_packet(f);
+ /* Attempt transmission */
+ send_packet(f);
+ f->retries++;
+ /* Try again later after 10 times as long */
+ f->retrytime *= 10;
+ if (f->retrytime > MAX_RETRY_TIME)
+ f->retrytime = MAX_RETRY_TIME;
+ /* Transfer messages max out at one second */
+ if (f->transfer && (f->retrytime > 1000))
+ f->retrytime = 1000;
+ f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f);
+ }
+ } else {
+ /* Make sure it gets freed */
+ f->retries = -1;
+ freeme = 1;
+ }
+ if (callno)
+ ast_mutex_unlock(&iaxsl[callno]);
+ /* Do not try again */
+ if (freeme) {
+ /* Don't attempt delivery, just remove it from the queue */
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_REMOVE(&frame_queue, f, list);
+ AST_LIST_UNLOCK(&frame_queue);
+ f->retrans = -1;
+ /* Free the IAX frame */
+ iax2_frame_free(f);
+ }
+}
+
+static int attempt_transmit(const void *data)
+{
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__attempt_transmit, data))
+#endif
+ __attempt_transmit(data);
+ return 0;
+}
+
+static char *handle_cli_iax2_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct iax2_peer *peer;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 prune realtime";
+ e->usage =
+ "Usage: iax2 prune realtime [<peername>|all]\n"
+ " Prunes object(s) from the cache\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_iax2_show_peer(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ if (!strcmp(a->argv[3], "all")) {
+ reload_config();
+ ast_cli(a->fd, "Cache flushed successfully.\n");
+ } else if ((peer = find_peer(a->argv[3], 0))) {
+ if(ast_test_flag(peer, IAX_RTCACHEFRIENDS)) {
+ ast_set_flag(peer, IAX_RTAUTOCLEAR);
+ expire_registry(peer_ref(peer));
+ ast_cli(a->fd, "Peer %s was removed from the cache.\n", a->argv[3]);
+ } else {
+ ast_cli(a->fd, "Peer %s is not eligible for this operation.\n", a->argv[3]);
+ }
+ peer_unref(peer);
+ } else {
+ ast_cli(a->fd, "Peer %s was not found in the cache.\n", a->argv[3]);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_test_losspct(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 test losspct";
+ e->usage =
+ "Usage: iax2 test losspct <percentage>\n"
+ " For testing, throws away <percentage> percent of incoming packets\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ test_losspct = atoi(a->argv[3]);
+
+ return CLI_SUCCESS;
+}
+
+#ifdef IAXTESTS
+static char *handle_cli_iax2_test_late(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 test late";
+ e->usage =
+ "Usage: iax2 test late <ms>\n"
+ " For testing, count the next frame as <ms> ms late\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ test_late = atoi(a->argv[3]);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_test_resync(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 test resync";
+ e->usage =
+ "Usage: iax2 test resync <ms>\n"
+ " For testing, adjust all future frames by <ms> ms\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ test_resync = atoi(a->argv[3]);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_test_jitter(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 test jitter";
+ e->usage =
+ "Usage: iax2 test jitter <ms> <pct>\n"
+ " For testing, simulate maximum jitter of +/- <ms> on <pct>\n"
+ " percentage of packets. If <pct> is not specified, adds\n"
+ " jitter to all packets.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 4 || a->argc > 5)
+ return CLI_SHOWUSAGE;
+
+ test_jit = atoi(a->argv[3]);
+ if (a->argc == 5)
+ test_jitpct = atoi(a->argv[4]);
+
+ return CLI_SUCCESS;
+}
+#endif /* IAXTESTS */
+
+/*! \brief peer_status: Report Peer status in character string */
+/* returns 1 if peer is online, -1 if unmonitored */
+static int peer_status(struct iax2_peer *peer, char *status, int statuslen)
+{
+ int res = 0;
+ if (peer->maxms) {
+ if (peer->lastms < 0) {
+ ast_copy_string(status, "UNREACHABLE", statuslen);
+ } else if (peer->lastms > peer->maxms) {
+ snprintf(status, statuslen, "LAGGED (%d ms)", peer->lastms);
+ res = 1;
+ } else if (peer->lastms) {
+ snprintf(status, statuslen, "OK (%d ms)", peer->lastms);
+ res = 1;
+ } else {
+ ast_copy_string(status, "UNKNOWN", statuslen);
+ }
+ } else {
+ ast_copy_string(status, "Unmonitored", statuslen);
+ res = -1;
+ }
+ return res;
+}
+
+/*! \brief Show one peer in detail */
+static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char status[30];
+ char cbuf[256];
+ struct iax2_peer *peer;
+ char codec_buf[512];
+ int x = 0, codec = 0, load_realtime = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show peer";
+ e->usage =
+ "Usage: iax2 show peer <name>\n"
+ " Display details on specific IAX peer\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_iax2_show_peer(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? 1 : 0;
+
+ peer = find_peer(a->argv[3], load_realtime);
+ if (peer) {
+ ast_cli(a->fd, "\n\n");
+ ast_cli(a->fd, " * Name : %s\n", peer->name);
+ ast_cli(a->fd, " Secret : %s\n", ast_strlen_zero(peer->secret) ? "<Not set>" : "<Set>");
+ ast_cli(a->fd, " Context : %s\n", peer->context);
+ ast_cli(a->fd, " Mailbox : %s\n", peer->mailbox);
+ ast_cli(a->fd, " Dynamic : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No");
+ ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
+ ast_cli(a->fd, " Expire : %d\n", peer->expire);
+ ast_cli(a->fd, " ACL : %s\n", (peer->ha ? "Yes" : "No"));
+ ast_cli(a->fd, " Addr->IP : %s Port %d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port));
+ ast_cli(a->fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port));
+ ast_cli(a->fd, " Username : %s\n", peer->username);
+ ast_cli(a->fd, " Codecs : ");
+ ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability);
+ ast_cli(a->fd, "%s\n", codec_buf);
+
+ ast_cli(a->fd, " Codec Order : (");
+ for(x = 0; x < 32 ; x++) {
+ codec = ast_codec_pref_index(&peer->prefs,x);
+ if(!codec)
+ break;
+ ast_cli(a->fd, "%s", ast_getformatname(codec));
+ if(x < 31 && ast_codec_pref_index(&peer->prefs,x+1))
+ ast_cli(a->fd, "|");
+ }
+
+ if (!x)
+ ast_cli(a->fd, "none");
+ ast_cli(a->fd, ")\n");
+
+ ast_cli(a->fd, " Status : ");
+ peer_status(peer, status, sizeof(status));
+ ast_cli(a->fd, "%s\n",status);
+ ast_cli(a->fd, " Qualify : every %dms when OK, every %dms when UNREACHABLE (sample smoothing %s)\n", peer->pokefreqok, peer->pokefreqnotok, peer->smoothing ? "On" : "Off");
+ ast_cli(a->fd, "\n");
+ peer_unref(peer);
+ } else {
+ ast_cli(a->fd, "Peer %s not found.\n", a->argv[3]);
+ ast_cli(a->fd, "\n");
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *complete_iax2_show_peer(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ struct iax2_peer *peer;
+ char *res = NULL;
+ int wordlen = strlen(word);
+ struct ao2_iterator i;
+
+ /* 0 - iax2; 1 - show; 2 - peer; 3 - <peername> */
+ if (pos != 3)
+ return NULL;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if (!strncasecmp(peer->name, word, wordlen) && ++which > state) {
+ res = ast_strdup(peer->name);
+ peer_unref(peer);
+ break;
+ }
+ peer_unref(peer);
+ }
+
+ return res;
+}
+
+static char *handle_cli_iax2_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct iax_frame *cur;
+ int cnt = 0, dead = 0, final = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show stats";
+ e->usage =
+ "Usage: iax2 show stats\n"
+ " Display statistics on IAX channel driver.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_TRAVERSE(&frame_queue, cur, list) {
+ if (cur->retries < 0)
+ dead++;
+ if (cur->final)
+ final++;
+ cnt++;
+ }
+ AST_LIST_UNLOCK(&frame_queue);
+
+ ast_cli(a->fd, " IAX Statistics\n");
+ ast_cli(a->fd, "---------------------\n");
+ ast_cli(a->fd, "Outstanding frames: %d (%d ingress, %d egress)\n", iax_get_frames(), iax_get_iframes(), iax_get_oframes());
+ ast_cli(a->fd, "%d timed and %d untimed transmits; MTU %d/%d/%d\n", trunk_timed, trunk_untimed,
+ trunk_maxmtu, trunk_nmaxmtu, global_max_trunk_mtu);
+ ast_cli(a->fd, "Packets in transmit queue: %d dead, %d final, %d total\n\n", dead, final, cnt);
+
+ trunk_timed = trunk_untimed = 0;
+ if (trunk_maxmtu > trunk_nmaxmtu)
+ trunk_nmaxmtu = trunk_maxmtu;
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Set trunk MTU from CLI */
+static char *handle_cli_iax2_set_mtu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int mtuv;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 set mtu";
+ e->usage =
+ "Usage: iax2 set mtu <value>\n"
+ " Set the system-wide IAX IP mtu to <value> bytes net or\n"
+ " zero to disable. Disabling means that the operating system\n"
+ " must handle fragmentation of UDP packets when the IAX2 trunk\n"
+ " packet exceeds the UDP payload size. This is substantially\n"
+ " below the IP mtu. Try 1240 on ethernets. Must be 172 or\n"
+ " greater for G.711 samples.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ if (strncasecmp(a->argv[3], "default", strlen(a->argv[3])) == 0)
+ mtuv = MAX_TRUNK_MTU;
+ else
+ mtuv = atoi(a->argv[3]);
+
+ if (mtuv == 0) {
+ ast_cli(a->fd, "Trunk MTU control disabled (mtu was %d)\n", global_max_trunk_mtu);
+ global_max_trunk_mtu = 0;
+ return CLI_SUCCESS;
+ }
+ if (mtuv < 172 || mtuv > 4000) {
+ ast_cli(a->fd, "Trunk MTU must be between 172 and 4000\n");
+ return CLI_SHOWUSAGE;
+ }
+ ast_cli(a->fd, "Trunk MTU changed from %d to %d\n", global_max_trunk_mtu, mtuv);
+ global_max_trunk_mtu = mtuv;
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct iax2_dpcache *dp = NULL;
+ char tmp[1024], *pc = NULL;
+ int s, x, y;
+ struct timeval tv = ast_tvnow();
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show cache";
+ e->usage =
+ "Usage: iax2 show cache\n"
+ " Display currently cached IAX Dialplan results.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_LIST_LOCK(&dpcache);
+
+ ast_cli(a->fd, "%-20.20s %-12.12s %-9.9s %-8.8s %s\n", "Peer/Context", "Exten", "Exp.", "Wait.", "Flags");
+
+ AST_LIST_TRAVERSE(&dpcache, dp, cache_list) {
+ s = dp->expiry.tv_sec - tv.tv_sec;
+ tmp[0] = '\0';
+ if (dp->flags & CACHE_FLAG_EXISTS)
+ strncat(tmp, "EXISTS|", sizeof(tmp) - strlen(tmp) - 1);
+ if (dp->flags & CACHE_FLAG_NONEXISTENT)
+ strncat(tmp, "NONEXISTENT|", sizeof(tmp) - strlen(tmp) - 1);
+ if (dp->flags & CACHE_FLAG_CANEXIST)
+ strncat(tmp, "CANEXIST|", sizeof(tmp) - strlen(tmp) - 1);
+ if (dp->flags & CACHE_FLAG_PENDING)
+ strncat(tmp, "PENDING|", sizeof(tmp) - strlen(tmp) - 1);
+ if (dp->flags & CACHE_FLAG_TIMEOUT)
+ strncat(tmp, "TIMEOUT|", sizeof(tmp) - strlen(tmp) - 1);
+ if (dp->flags & CACHE_FLAG_TRANSMITTED)
+ strncat(tmp, "TRANSMITTED|", sizeof(tmp) - strlen(tmp) - 1);
+ if (dp->flags & CACHE_FLAG_MATCHMORE)
+ strncat(tmp, "MATCHMORE|", sizeof(tmp) - strlen(tmp) - 1);
+ if (dp->flags & CACHE_FLAG_UNKNOWN)
+ strncat(tmp, "UNKNOWN|", sizeof(tmp) - strlen(tmp) - 1);
+ /* Trim trailing pipe */
+ if (!ast_strlen_zero(tmp))
+ tmp[strlen(tmp) - 1] = '\0';
+ else
+ ast_copy_string(tmp, "(none)", sizeof(tmp));
+ y = 0;
+ pc = strchr(dp->peercontext, '@');
+ if (!pc)
+ pc = dp->peercontext;
+ else
+ pc++;
+ for (x = 0; x < sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+ if (dp->waiters[x] > -1)
+ y++;
+ if (s > 0)
+ ast_cli(a->fd, "%-20.20s %-12.12s %-9d %-8d %s\n", pc, dp->exten, s, y, tmp);
+ else
+ ast_cli(a->fd, "%-20.20s %-12.12s %-9.9s %-8d %s\n", pc, dp->exten, "(expired)", y, tmp);
+ }
+
+ AST_LIST_LOCK(&dpcache);
+
+ return CLI_SUCCESS;
+}
+
+static unsigned int calc_rxstamp(struct chan_iax2_pvt *p, unsigned int offset);
+
+static void unwrap_timestamp(struct iax_frame *fr)
+{
+ int x;
+
+ if ( (fr->ts & 0xFFFF0000) == (iaxs[fr->callno]->last & 0xFFFF0000) ) {
+ x = fr->ts - iaxs[fr->callno]->last;
+ if (x < -50000) {
+ /* Sudden big jump backwards in timestamp:
+ What likely happened here is that miniframe timestamp has circled but we haven't
+ gotten the update from the main packet. We'll just pretend that we did, and
+ update the timestamp appropriately. */
+ fr->ts = ( (iaxs[fr->callno]->last & 0xFFFF0000) + 0x10000) | (fr->ts & 0xFFFF);
+ if (iaxdebug)
+ ast_debug(1, "schedule_delivery: pushed forward timestamp\n");
+ }
+ if (x > 50000) {
+ /* Sudden apparent big jump forwards in timestamp:
+ What's likely happened is this is an old miniframe belonging to the previous
+ top-16-bit timestamp that has turned up out of order.
+ Adjust the timestamp appropriately. */
+ fr->ts = ( (iaxs[fr->callno]->last & 0xFFFF0000) - 0x10000) | (fr->ts & 0xFFFF);
+ if (iaxdebug)
+ ast_debug(1, "schedule_delivery: pushed back timestamp\n");
+ }
+ }
+}
+
+static int get_from_jb(const void *p);
+
+static void update_jbsched(struct chan_iax2_pvt *pvt)
+{
+ int when;
+
+ when = ast_tvdiff_ms(ast_tvnow(), pvt->rxcore);
+
+ when = jb_next(pvt->jb) - when;
+
+ if(when <= 0) {
+ /* XXX should really just empty until when > 0.. */
+ when = 1;
+ }
+
+ pvt->jbid = iax2_sched_replace(pvt->jbid, sched, when, get_from_jb,
+ CALLNO_TO_PTR(pvt->callno));
+}
+
+static void __get_from_jb(const void *p)
+{
+ int callno = PTR_TO_CALLNO(p);
+ struct chan_iax2_pvt *pvt = NULL;
+ struct iax_frame *fr;
+ jb_frame frame;
+ int ret;
+ long now;
+ long next;
+ struct timeval tv = ast_tvnow();
+
+ /* Make sure we have a valid private structure before going on */
+ ast_mutex_lock(&iaxsl[callno]);
+ pvt = iaxs[callno];
+ if (!pvt) {
+ /* No go! */
+ ast_mutex_unlock(&iaxsl[callno]);
+ return;
+ }
+
+ pvt->jbid = -1;
+
+ /* round up a millisecond since ast_sched_runq does; */
+ /* prevents us from spinning while waiting for our now */
+ /* to catch up with runq's now */
+ tv.tv_usec += 1000;
+
+ now = ast_tvdiff_ms(tv, pvt->rxcore);
+
+ if(now >= (next = jb_next(pvt->jb))) {
+ ret = jb_get(pvt->jb,&frame,now,ast_codec_interp_len(pvt->voiceformat));
+ switch(ret) {
+ case JB_OK:
+ fr = frame.data;
+ __do_deliver(fr);
+ /* __do_deliver() can cause the call to disappear */
+ pvt = iaxs[callno];
+ break;
+ case JB_INTERP:
+ {
+ struct ast_frame af = { 0, };
+
+ /* create an interpolation frame */
+ af.frametype = AST_FRAME_VOICE;
+ af.subclass = pvt->voiceformat;
+ af.samples = frame.ms * 8;
+ af.src = "IAX2 JB interpolation";
+ af.delivery = ast_tvadd(pvt->rxcore, ast_samp2tv(next, 1000));
+ af.offset = AST_FRIENDLY_OFFSET;
+
+ /* queue the frame: For consistency, we would call __do_deliver here, but __do_deliver wants an iax_frame,
+ * which we'd need to malloc, and then it would free it. That seems like a drag */
+ if (!ast_test_flag(iaxs[callno], IAX_ALREADYGONE)) {
+ iax2_queue_frame(callno, &af);
+ /* iax2_queue_frame() could cause the call to disappear */
+ pvt = iaxs[callno];
+ }
+ }
+ break;
+ case JB_DROP:
+ iax2_frame_free(frame.data);
+ break;
+ case JB_NOFRAME:
+ case JB_EMPTY:
+ /* do nothing */
+ break;
+ default:
+ /* shouldn't happen */
+ break;
+ }
+ }
+ if (pvt)
+ update_jbsched(pvt);
+ ast_mutex_unlock(&iaxsl[callno]);
+}
+
+static int get_from_jb(const void *data)
+{
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__get_from_jb, data))
+#endif
+ __get_from_jb(data);
+ return 0;
+}
+
+/*!
+ * \note This function assumes fr->callno is locked
+ *
+ * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
+ * was valid before calling it, it may no longer be valid after calling it.
+ */
+static int schedule_delivery(struct iax_frame *fr, int updatehistory, int fromtrunk, unsigned int *tsout)
+{
+ int type, len;
+ int ret;
+ int needfree = 0;
+
+ /* Attempt to recover wrapped timestamps */
+ unwrap_timestamp(fr);
+
+ /* delivery time is sender's sent timestamp converted back into absolute time according to our clock */
+ if ( !fromtrunk && !ast_tvzero(iaxs[fr->callno]->rxcore))
+ fr->af.delivery = ast_tvadd(iaxs[fr->callno]->rxcore, ast_samp2tv(fr->ts, 1000));
+ else {
+#if 0
+ ast_debug(1, "schedule_delivery: set delivery to 0 as we don't have an rxcore yet, or frame is from trunk.\n");
+#endif
+ fr->af.delivery = ast_tv(0,0);
+ }
+
+ type = JB_TYPE_CONTROL;
+ len = 0;
+
+ if(fr->af.frametype == AST_FRAME_VOICE) {
+ type = JB_TYPE_VOICE;
+ len = ast_codec_get_samples(&fr->af) / 8;
+ } else if(fr->af.frametype == AST_FRAME_CNG) {
+ type = JB_TYPE_SILENCE;
+ }
+
+ if ( (!ast_test_flag(iaxs[fr->callno], IAX_USEJITTERBUF)) ) {
+ if (tsout)
+ *tsout = fr->ts;
+ __do_deliver(fr);
+ return -1;
+ }
+
+ /* if the user hasn't requested we force the use of the jitterbuffer, and we're bridged to
+ * a channel that can accept jitter, then flush and suspend the jb, and send this frame straight through */
+ if( (!ast_test_flag(iaxs[fr->callno], IAX_FORCEJITTERBUF)) &&
+ iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner) &&
+ (ast_bridged_channel(iaxs[fr->callno]->owner)->tech->properties & AST_CHAN_TP_WANTSJITTER)) {
+ jb_frame frame;
+
+ /* deliver any frames in the jb */
+ while (jb_getall(iaxs[fr->callno]->jb, &frame) == JB_OK) {
+ __do_deliver(frame.data);
+ /* __do_deliver() can make the call disappear */
+ if (!iaxs[fr->callno])
+ return -1;
+ }
+
+ jb_reset(iaxs[fr->callno]->jb);
+
+ if (iaxs[fr->callno]->jbid > -1)
+ ast_sched_del(sched, iaxs[fr->callno]->jbid);
+
+ iaxs[fr->callno]->jbid = -1;
+
+ /* deliver this frame now */
+ if (tsout)
+ *tsout = fr->ts;
+ __do_deliver(fr);
+ return -1;
+ }
+
+ /* insert into jitterbuffer */
+ /* TODO: Perhaps we could act immediately if it's not droppable and late */
+ ret = jb_put(iaxs[fr->callno]->jb, fr, type, len, fr->ts,
+ calc_rxstamp(iaxs[fr->callno],fr->ts));
+ if (ret == JB_DROP) {
+ needfree++;
+ } else if (ret == JB_SCHED) {
+ update_jbsched(iaxs[fr->callno]);
+ }
+ if (tsout)
+ *tsout = fr->ts;
+ if (needfree) {
+ /* Free our iax frame */
+ iax2_frame_free(fr);
+ return -1;
+ }
+ return 0;
+}
+
+static int iax2_transmit(struct iax_frame *fr)
+{
+ /* Lock the queue and place this packet at the end */
+ /* By setting this to 0, the network thread will send it for us, and
+ queue retransmission if necessary */
+ fr->sentyet = 0;
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_INSERT_TAIL(&frame_queue, fr, list);
+ AST_LIST_UNLOCK(&frame_queue);
+ /* Wake up the network and scheduler thread */
+ if (netthreadid != AST_PTHREADT_NULL)
+ pthread_kill(netthreadid, SIGURG);
+ signal_condition(&sched_lock, &sched_cond);
+ return 0;
+}
+
+
+
+static int iax2_digit_begin(struct ast_channel *c, char digit)
+{
+ return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_BEGIN, digit, 0, NULL, 0, -1);
+}
+
+static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+ return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1);
+}
+
+static int iax2_sendtext(struct ast_channel *c, const char *text)
+{
+
+ return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_TEXT,
+ 0, 0, (unsigned char *)text, strlen(text) + 1, -1);
+}
+
+static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img)
+{
+ return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_IMAGE, img->subclass, 0, img->data, img->datalen, -1);
+}
+
+static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen)
+{
+ return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_HTML, subclass, 0, (unsigned char *)data, datalen, -1);
+}
+
+static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan)
+{
+ unsigned short callno = PTR_TO_CALLNO(newchan->tech_pvt);
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno])
+ iaxs[callno]->owner = newchan;
+ else
+ ast_log(LOG_WARNING, "Uh, this isn't a good sign...\n");
+ ast_mutex_unlock(&iaxsl[callno]);
+ return 0;
+}
+
+/*!
+ * \note This function calls reg_source_db -> iax2_poke_peer -> find_callno,
+ * so do not call this with a pvt lock held.
+ */
+static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin)
+{
+ struct ast_variable *var = NULL;
+ struct ast_variable *tmp;
+ struct iax2_peer *peer=NULL;
+ time_t regseconds = 0, nowtime;
+ int dynamic=0;
+
+ if (peername) {
+ var = ast_load_realtime("iaxpeers", "name", peername, "host", "dynamic", NULL);
+ if (!var && sin)
+ var = ast_load_realtime("iaxpeers", "name", peername, "host", ast_inet_ntoa(sin->sin_addr), NULL);
+ } else if (sin) {
+ char porta[25];
+ sprintf(porta, "%d", ntohs(sin->sin_port));
+ var = ast_load_realtime("iaxpeers", "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL);
+ if (var) {
+ /* We'll need the peer name in order to build the structure! */
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "name"))
+ peername = tmp->value;
+ }
+ }
+ }
+ if (!var && peername) { /* Last ditch effort */
+ var = ast_load_realtime("iaxpeers", "name", peername, NULL);
+ /*!\note
+ * If this one loaded something, then we need to ensure that the host
+ * field matched. The only reason why we can't have this as a criteria
+ * is because we only have the IP address and the host field might be
+ * set as a name (and the reverse PTR might not match).
+ */
+ if (var && sin) {
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "host")) {
+ struct in_addr sin2 = { 0, };
+ struct ast_dnsmgr_entry *dnsmgr = NULL;
+ if ((ast_dnsmgr_lookup(tmp->value, &sin2, &dnsmgr) < 0) || (memcmp(&sin2, &sin->sin_addr, sizeof(sin2)) != 0)) {
+ /* No match */
+ ast_variables_destroy(var);
+ var = NULL;
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (!var)
+ return NULL;
+
+ peer = build_peer(peername, var, NULL, ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS) ? 0 : 1);
+
+ if (!peer) {
+ ast_variables_destroy(var);
+ return NULL;
+ }
+
+ for (tmp = var; tmp; tmp = tmp->next) {
+ /* Make sure it's not a user only... */
+ if (!strcasecmp(tmp->name, "type")) {
+ if (strcasecmp(tmp->value, "friend") &&
+ strcasecmp(tmp->value, "peer")) {
+ /* Whoops, we weren't supposed to exist! */
+ peer = peer_unref(peer);
+ break;
+ }
+ } else if (!strcasecmp(tmp->name, "regseconds")) {
+ ast_get_time_t(tmp->value, &regseconds, 0, NULL);
+ } else if (!strcasecmp(tmp->name, "ipaddr")) {
+ inet_aton(tmp->value, &(peer->addr.sin_addr));
+ } else if (!strcasecmp(tmp->name, "port")) {
+ peer->addr.sin_port = htons(atoi(tmp->value));
+ } else if (!strcasecmp(tmp->name, "host")) {
+ if (!strcasecmp(tmp->value, "dynamic"))
+ dynamic = 1;
+ }
+ }
+
+ ast_variables_destroy(var);
+
+ if (!peer)
+ return NULL;
+
+ if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) {
+ ast_copy_flags(peer, &globalflags, IAX_RTAUTOCLEAR|IAX_RTCACHEFRIENDS);
+ if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) {
+ if (peer->expire > -1) {
+ if (!ast_sched_del(sched, peer->expire)) {
+ peer->expire = -1;
+ peer_unref(peer);
+ }
+ }
+ peer->expire = iax2_sched_add(sched, (global_rtautoclear) * 1000, expire_registry, peer_ref(peer));
+ if (peer->expire == -1)
+ peer_unref(peer);
+ }
+ ao2_link(peers, peer);
+ if (ast_test_flag(peer, IAX_DYNAMIC))
+ reg_source_db(peer);
+ } else {
+ ast_set_flag(peer, IAX_TEMPONLY);
+ }
+
+ if (!ast_test_flag(&globalflags, IAX_RTIGNOREREGEXPIRE) && dynamic) {
+ time(&nowtime);
+ if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) {
+ memset(&peer->addr, 0, sizeof(peer->addr));
+ realtime_update_peer(peer->name, &peer->addr, 0);
+ ast_debug(1, "realtime_peer: Bah, '%s' is expired (%d/%d/%d)!\n",
+ peername, (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
+ }
+ else {
+ ast_debug(1, "realtime_peer: Registration for '%s' still active (%d/%d/%d)!\n",
+ peername, (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
+ }
+ }
+
+ return peer;
+}
+
+static struct iax2_user *realtime_user(const char *username, struct sockaddr_in *sin)
+{
+ struct ast_variable *var;
+ struct ast_variable *tmp;
+ struct iax2_user *user=NULL;
+
+ var = ast_load_realtime("iaxusers", "name", username, "host", "dynamic", NULL);
+ if (!var)
+ var = ast_load_realtime("iaxusers", "name", username, "host", ast_inet_ntoa(sin->sin_addr), NULL);
+ if (!var && sin) {
+ char porta[6];
+ snprintf(porta, sizeof(porta), "%d", ntohs(sin->sin_port));
+ var = ast_load_realtime("iaxusers", "name", username, "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL);
+ if (!var)
+ var = ast_load_realtime("iaxusers", "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL);
+ }
+ if (!var) { /* Last ditch effort */
+ var = ast_load_realtime("iaxusers", "name", username, NULL);
+ /*!\note
+ * If this one loaded something, then we need to ensure that the host
+ * field matched. The only reason why we can't have this as a criteria
+ * is because we only have the IP address and the host field might be
+ * set as a name (and the reverse PTR might not match).
+ */
+ if (var) {
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "host")) {
+ struct in_addr sin2 = { 0, };
+ struct ast_dnsmgr_entry *dnsmgr = NULL;
+ if ((ast_dnsmgr_lookup(tmp->value, &sin2, &dnsmgr) < 0) || (memcmp(&sin2, &sin->sin_addr, sizeof(sin2)) != 0)) {
+ /* No match */
+ ast_variables_destroy(var);
+ var = NULL;
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (!var)
+ return NULL;
+
+ tmp = var;
+ while(tmp) {
+ /* Make sure it's not a peer only... */
+ if (!strcasecmp(tmp->name, "type")) {
+ if (strcasecmp(tmp->value, "friend") &&
+ strcasecmp(tmp->value, "user")) {
+ return NULL;
+ }
+ }
+ tmp = tmp->next;
+ }
+
+ user = build_user(username, var, NULL, !ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS));
+
+ ast_variables_destroy(var);
+
+ if (!user)
+ return NULL;
+
+ if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) {
+ ast_set_flag(user, IAX_RTCACHEFRIENDS);
+ ao2_link(users, user);
+ } else {
+ ast_set_flag(user, IAX_TEMPONLY);
+ }
+
+ return user;
+}
+
+static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime)
+{
+ char port[10];
+ char regseconds[20];
+
+ snprintf(regseconds, sizeof(regseconds), "%d", (int)regtime);
+ snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
+ ast_update_realtime("iaxpeers", "name", peername,
+ "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", port,
+ "regseconds", regseconds, NULL);
+}
+
+struct create_addr_info {
+ int capability;
+ unsigned int flags;
+ int maxtime;
+ int encmethods;
+ int found;
+ int sockfd;
+ int adsi;
+ char username[80];
+ char secret[80];
+ char outkey[80];
+ char timezone[80];
+ char prefs[32];
+ char context[AST_MAX_CONTEXT];
+ char peercontext[AST_MAX_CONTEXT];
+ char mohinterpret[MAX_MUSICCLASS];
+ char mohsuggest[MAX_MUSICCLASS];
+};
+
+static int create_addr(const char *peername, struct ast_channel *c, struct sockaddr_in *sin, struct create_addr_info *cai)
+{
+ struct iax2_peer *peer;
+ int res = -1;
+ struct ast_codec_pref ourprefs;
+
+ ast_clear_flag(cai, IAX_SENDANI | IAX_TRUNK);
+ cai->sockfd = defaultsockfd;
+ cai->maxtime = 0;
+ sin->sin_family = AF_INET;
+
+ if (!(peer = find_peer(peername, 1))) {
+ cai->found = 0;
+ if (ast_get_ip_or_srv(sin, peername, srvlookup ? "_iax._udp" : NULL)) {
+ ast_log(LOG_WARNING, "No such host: %s\n", peername);
+ return -1;
+ }
+ sin->sin_port = htons(IAX_DEFAULT_PORTNO);
+ /* use global iax prefs for unknown peer/user */
+ /* But move the calling channel's native codec to the top of the preference list */
+ memcpy(&ourprefs, &prefs, sizeof(ourprefs));
+ if (c)
+ ast_codec_pref_prepend(&ourprefs, c->nativeformats, 1);
+ ast_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1);
+ return 0;
+ }
+
+ cai->found = 1;
+
+ /* if the peer has no address (current or default), return failure */
+ if (!(peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr))
+ goto return_unref;
+
+ /* if the peer is being monitored and is currently unreachable, return failure */
+ if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0)))
+ goto return_unref;
+
+ ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+ cai->maxtime = peer->maxms;
+ cai->capability = peer->capability;
+ cai->encmethods = peer->encmethods;
+ cai->sockfd = peer->sockfd;
+ cai->adsi = peer->adsi;
+ memcpy(&ourprefs, &peer->prefs, sizeof(ourprefs));
+ /* Move the calling channel's native codec to the top of the preference list */
+ if (c) {
+ ast_log(LOG_DEBUG, "prepending %x to prefs\n", c->nativeformats);
+ ast_codec_pref_prepend(&ourprefs, c->nativeformats, 1);
+ }
+ ast_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1);
+ ast_copy_string(cai->context, peer->context, sizeof(cai->context));
+ ast_copy_string(cai->peercontext, peer->peercontext, sizeof(cai->peercontext));
+ ast_copy_string(cai->username, peer->username, sizeof(cai->username));
+ ast_copy_string(cai->timezone, peer->zonetag, sizeof(cai->timezone));
+ ast_copy_string(cai->outkey, peer->outkey, sizeof(cai->outkey));
+ ast_copy_string(cai->mohinterpret, peer->mohinterpret, sizeof(cai->mohinterpret));
+ ast_copy_string(cai->mohsuggest, peer->mohsuggest, sizeof(cai->mohsuggest));
+ if (ast_strlen_zero(peer->dbsecret)) {
+ ast_copy_string(cai->secret, peer->secret, sizeof(cai->secret));
+ } else {
+ char *family;
+ char *key = NULL;
+
+ family = ast_strdupa(peer->dbsecret);
+ key = strchr(family, '/');
+ if (key)
+ *key++ = '\0';
+ if (!key || ast_db_get(family, key, cai->secret, sizeof(cai->secret))) {
+ ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", peer->dbsecret);
+ goto return_unref;
+ }
+ }
+
+ if (peer->addr.sin_addr.s_addr) {
+ sin->sin_addr = peer->addr.sin_addr;
+ sin->sin_port = peer->addr.sin_port;
+ } else {
+ sin->sin_addr = peer->defaddr.sin_addr;
+ sin->sin_port = peer->defaddr.sin_port;
+ }
+
+ res = 0;
+
+return_unref:
+ peer_unref(peer);
+
+ return res;
+}
+
+static void __auto_congest(const void *nothing)
+{
+ int callno = PTR_TO_CALLNO(nothing);
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_CONGESTION };
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+ iaxs[callno]->initid = -1;
+ iax2_queue_frame(callno, &f);
+ ast_log(LOG_NOTICE, "Auto-congesting call due to slow response\n");
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+}
+
+static int auto_congest(const void *data)
+{
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__auto_congest, data))
+#endif
+ __auto_congest(data);
+ return 0;
+}
+
+static unsigned int iax2_datetime(const char *tz)
+{
+ struct timeval t = ast_tvnow();
+ struct ast_tm tm;
+ unsigned int tmp;
+ ast_localtime(&t, &tm, ast_strlen_zero(tz) ? NULL : tz);
+ tmp = (tm.tm_sec >> 1) & 0x1f; /* 5 bits of seconds */
+ tmp |= (tm.tm_min & 0x3f) << 5; /* 6 bits of minutes */
+ tmp |= (tm.tm_hour & 0x1f) << 11; /* 5 bits of hours */
+ tmp |= (tm.tm_mday & 0x1f) << 16; /* 5 bits of day of month */
+ tmp |= ((tm.tm_mon + 1) & 0xf) << 21; /* 4 bits of month */
+ tmp |= ((tm.tm_year - 100) & 0x7f) << 25; /* 7 bits of year */
+ return tmp;
+}
+
+struct parsed_dial_string {
+ char *username;
+ char *password;
+ char *key;
+ char *peer;
+ char *port;
+ char *exten;
+ char *context;
+ char *options;
+};
+
+/*!
+ * \brief Parses an IAX dial string into its component parts.
+ * \param data the string to be parsed
+ * \param pds pointer to a \c struct \c parsed_dial_string to be filled in
+ * \return nothing
+ *
+ * This function parses the string and fills the structure
+ * with pointers to its component parts. The input string
+ * will be modified.
+ *
+ * \note This function supports both plaintext passwords and RSA
+ * key names; if the password string is formatted as '[keyname]',
+ * then the keyname will be placed into the key field, and the
+ * password field will be set to NULL.
+ *
+ * \note The dial string format is:
+ * [username[:password]@]peer[:port][/exten[@@context]][/options]
+ */
+static void parse_dial_string(char *data, struct parsed_dial_string *pds)
+{
+ if (ast_strlen_zero(data))
+ return;
+
+ pds->peer = strsep(&data, "/");
+ pds->exten = strsep(&data, "/");
+ pds->options = data;
+
+ if (pds->exten) {
+ data = pds->exten;
+ pds->exten = strsep(&data, "@");
+ pds->context = data;
+ }
+
+ if (strchr(pds->peer, '@')) {
+ data = pds->peer;
+ pds->username = strsep(&data, "@");
+ pds->peer = data;
+ }
+
+ if (pds->username) {
+ data = pds->username;
+ pds->username = strsep(&data, ":");
+ pds->password = data;
+ }
+
+ data = pds->peer;
+ pds->peer = strsep(&data, ":");
+ pds->port = data;
+
+ /* check for a key name wrapped in [] in the secret position, if found,
+ move it to the key field instead
+ */
+ if (pds->password && (pds->password[0] == '[')) {
+ pds->key = ast_strip_quoted(pds->password, "[", "]");
+ pds->password = NULL;
+ }
+}
+
+static int iax2_call(struct ast_channel *c, char *dest, int timeout)
+{
+ struct sockaddr_in sin;
+ char *l=NULL, *n=NULL, *tmpstr;
+ struct iax_ie_data ied;
+ char *defaultrdest = "s";
+ unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
+ struct parsed_dial_string pds;
+ struct create_addr_info cai;
+ struct ast_var_t *var;
+ struct ast_datastore *variablestore = ast_channel_datastore_find(c, &iax2_variable_datastore_info, NULL);
+ const char* osp_token_ptr;
+ unsigned int osp_token_length;
+ unsigned char osp_block_index;
+ unsigned int osp_block_length;
+ unsigned char osp_buffer[256];
+
+ if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "Channel is already in use (%s)?\n", c->name);
+ return -1;
+ }
+
+ memset(&cai, 0, sizeof(cai));
+ cai.encmethods = iax2_encryption;
+
+ memset(&pds, 0, sizeof(pds));
+ tmpstr = ast_strdupa(dest);
+ parse_dial_string(tmpstr, &pds);
+
+ if (!pds.exten)
+ pds.exten = defaultrdest;
+
+ if (create_addr(pds.peer, c, &sin, &cai)) {
+ ast_log(LOG_WARNING, "No address associated with '%s'\n", pds.peer);
+ return -1;
+ }
+
+ if (!pds.username && !ast_strlen_zero(cai.username))
+ pds.username = cai.username;
+ if (!pds.password && !ast_strlen_zero(cai.secret))
+ pds.password = cai.secret;
+ if (!pds.key && !ast_strlen_zero(cai.outkey))
+ pds.key = cai.outkey;
+ if (!pds.context && !ast_strlen_zero(cai.peercontext))
+ pds.context = cai.peercontext;
+
+ /* Keep track of the context for outgoing calls too */
+ ast_copy_string(c->context, cai.context, sizeof(c->context));
+
+ if (pds.port)
+ sin.sin_port = htons(atoi(pds.port));
+
+ l = c->cid.cid_num;
+ n = c->cid.cid_name;
+
+ /* Now build request */
+ memset(&ied, 0, sizeof(ied));
+
+ /* On new call, first IE MUST be IAX version of caller */
+ iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION);
+ iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, pds.exten);
+ if (pds.options && strchr(pds.options, 'a')) {
+ /* Request auto answer */
+ iax_ie_append(&ied, IAX_IE_AUTOANSWER);
+ }
+
+ iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, cai.prefs);
+
+ if (l) {
+ iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, l);
+ iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres);
+ } else {
+ if (n)
+ iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres);
+ else
+ iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, AST_PRES_NUMBER_NOT_AVAILABLE);
+ }
+
+ iax_ie_append_byte(&ied, IAX_IE_CALLINGTON, c->cid.cid_ton);
+ iax_ie_append_short(&ied, IAX_IE_CALLINGTNS, c->cid.cid_tns);
+
+ if (n)
+ iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, n);
+ if (ast_test_flag(iaxs[callno], IAX_SENDANI) && c->cid.cid_ani)
+ iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, c->cid.cid_ani);
+
+ if (!ast_strlen_zero(c->language))
+ iax_ie_append_str(&ied, IAX_IE_LANGUAGE, c->language);
+ if (!ast_strlen_zero(c->cid.cid_dnid))
+ iax_ie_append_str(&ied, IAX_IE_DNID, c->cid.cid_dnid);
+ if (!ast_strlen_zero(c->cid.cid_rdnis))
+ iax_ie_append_str(&ied, IAX_IE_RDNIS, c->cid.cid_rdnis);
+
+ if (pds.context)
+ iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, pds.context);
+
+ if (pds.username)
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, pds.username);
+
+ if (cai.encmethods)
+ iax_ie_append_short(&ied, IAX_IE_ENCRYPTION, cai.encmethods);
+
+ ast_mutex_lock(&iaxsl[callno]);
+
+ if (!ast_strlen_zero(c->context))
+ ast_string_field_set(iaxs[callno], context, c->context);
+
+ if (pds.username)
+ ast_string_field_set(iaxs[callno], username, pds.username);
+
+ iaxs[callno]->encmethods = cai.encmethods;
+
+ iaxs[callno]->adsi = cai.adsi;
+
+ ast_string_field_set(iaxs[callno], mohinterpret, cai.mohinterpret);
+ ast_string_field_set(iaxs[callno], mohsuggest, cai.mohsuggest);
+
+ if (pds.key)
+ ast_string_field_set(iaxs[callno], outkey, pds.key);
+ if (pds.password)
+ ast_string_field_set(iaxs[callno], secret, pds.password);
+
+ iax_ie_append_int(&ied, IAX_IE_FORMAT, c->nativeformats);
+ iax_ie_append_int(&ied, IAX_IE_CAPABILITY, iaxs[callno]->capability);
+ iax_ie_append_short(&ied, IAX_IE_ADSICPE, c->adsicpe);
+ iax_ie_append_int(&ied, IAX_IE_DATETIME, iax2_datetime(cai.timezone));
+
+ if (iaxs[callno]->maxtime) {
+ /* Initialize pingtime and auto-congest time */
+ iaxs[callno]->pingtime = iaxs[callno]->maxtime / 2;
+ iaxs[callno]->initid = iax2_sched_add(sched, iaxs[callno]->maxtime * 2, auto_congest, CALLNO_TO_PTR(callno));
+ } else if (autokill) {
+ iaxs[callno]->pingtime = autokill / 2;
+ iaxs[callno]->initid = iax2_sched_add(sched, autokill * 2, auto_congest, CALLNO_TO_PTR(callno));
+ }
+
+ /* Check if there is an OSP token set by IAXCHANINFO function */
+ osp_token_ptr = iaxs[callno]->osptoken;
+ if (!ast_strlen_zero(osp_token_ptr)) {
+ if ((osp_token_length = strlen(osp_token_ptr)) <= IAX_MAX_OSPTOKEN_SIZE) {
+ osp_block_index = 0;
+ while (osp_token_length > 0) {
+ osp_block_length = IAX_MAX_OSPBLOCK_SIZE < osp_token_length ? IAX_MAX_OSPBLOCK_SIZE : osp_token_length;
+ osp_buffer[0] = osp_block_index;
+ memcpy(osp_buffer + 1, osp_token_ptr, osp_block_length);
+ iax_ie_append_raw(&ied, IAX_IE_OSPTOKEN, osp_buffer, osp_block_length + 1);
+ osp_block_index++;
+ osp_token_ptr += osp_block_length;
+ osp_token_length -= osp_block_length;
+ }
+ } else
+ ast_log(LOG_WARNING, "OSP token is too long\n");
+ } else if (iaxdebug)
+ ast_debug(1, "OSP token is undefined\n");
+
+ /* send the command using the appropriate socket for this peer */
+ iaxs[callno]->sockfd = cai.sockfd;
+
+ /* Add remote vars */
+ if (variablestore) {
+ AST_LIST_HEAD(, ast_var_t) *variablelist = variablestore->data;
+ AST_LIST_LOCK(variablelist);
+ AST_LIST_TRAVERSE(variablelist, var, entries) {
+ char tmp[256];
+ int i;
+ /* Automatically divide the value up into sized chunks */
+ for (i = 0; i < strlen(ast_var_value(var)); i += 255 - (strlen(ast_var_name(var)) + 1)) {
+ snprintf(tmp, sizeof(tmp), "%s=%s", ast_var_name(var), ast_var_value(var) + i);
+ iax_ie_append_str(&ied, IAX_IE_VARIABLE, tmp);
+ }
+ }
+ AST_LIST_UNLOCK(variablelist);
+ }
+
+ /* Transmit the string in a "NEW" request */
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+
+ ast_mutex_unlock(&iaxsl[callno]);
+ ast_setstate(c, AST_STATE_RINGING);
+
+ return 0;
+}
+
+static int iax2_hangup(struct ast_channel *c)
+{
+ unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
+ int alreadygone;
+ struct iax_ie_data ied;
+ memset(&ied, 0, sizeof(ied));
+ ast_mutex_lock(&iaxsl[callno]);
+ if (callno && iaxs[callno]) {
+ ast_debug(1, "We're hanging up %s now...\n", c->name);
+ alreadygone = ast_test_flag(iaxs[callno], IAX_ALREADYGONE);
+ /* Send the hangup unless we have had a transmission error or are already gone */
+ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, (unsigned char)c->hangupcause);
+ if (!iaxs[callno]->error && !alreadygone) {
+ send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1);
+ if (!iaxs[callno]) {
+ ast_mutex_unlock(&iaxsl[callno]);
+ return 0;
+ }
+ }
+ /* Explicitly predestroy it */
+ iax2_predestroy(callno);
+ /* If we were already gone to begin with, destroy us now */
+ if (alreadygone && iaxs[callno]) {
+ ast_debug(1, "Really destroying %s now...\n", c->name);
+ iax2_destroy(callno);
+ }
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+ ast_verb(3, "Hungup '%s'\n", c->name);
+ return 0;
+}
+
+static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen)
+{
+ struct ast_option_header *h;
+ int res;
+
+ switch (option) {
+ case AST_OPTION_TXGAIN:
+ case AST_OPTION_RXGAIN:
+ /* these two cannot be sent, because they require a result */
+ errno = ENOSYS;
+ return -1;
+ default:
+ if (!(h = ast_malloc(datalen + sizeof(*h))))
+ return -1;
+
+ h->flag = AST_OPTION_FLAG_REQUEST;
+ h->option = htons(option);
+ memcpy(h->data, data, datalen);
+ res = send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_CONTROL,
+ AST_CONTROL_OPTION, 0, (unsigned char *) h,
+ datalen + sizeof(*h), -1);
+ ast_free(h);
+ return res;
+ }
+}
+
+static struct ast_frame *iax2_read(struct ast_channel *c)
+{
+ ast_log(LOG_NOTICE, "I should never be called!\n");
+ return &ast_null_frame;
+}
+
+static int iax2_start_transfer(unsigned short callno0, unsigned short callno1, int mediaonly)
+{
+ int res;
+ struct iax_ie_data ied0;
+ struct iax_ie_data ied1;
+ unsigned int transferid = (unsigned int)ast_random();
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_addr(&ied0, IAX_IE_APPARENT_ADDR, &iaxs[callno1]->addr);
+ iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[callno1]->peercallno);
+ iax_ie_append_int(&ied0, IAX_IE_TRANSFERID, transferid);
+
+ memset(&ied1, 0, sizeof(ied1));
+ iax_ie_append_addr(&ied1, IAX_IE_APPARENT_ADDR, &iaxs[callno0]->addr);
+ iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[callno0]->peercallno);
+ iax_ie_append_int(&ied1, IAX_IE_TRANSFERID, transferid);
+
+ res = send_command(iaxs[callno0], AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied0.buf, ied0.pos, -1);
+ if (res)
+ return -1;
+ res = send_command(iaxs[callno1], AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied1.buf, ied1.pos, -1);
+ if (res)
+ return -1;
+ iaxs[callno0]->transferring = mediaonly ? TRANSFER_MBEGIN : TRANSFER_BEGIN;
+ iaxs[callno1]->transferring = mediaonly ? TRANSFER_MBEGIN : TRANSFER_BEGIN;
+ return 0;
+}
+
+static void lock_both(unsigned short callno0, unsigned short callno1)
+{
+ ast_mutex_lock(&iaxsl[callno0]);
+ while (ast_mutex_trylock(&iaxsl[callno1])) {
+ ast_mutex_unlock(&iaxsl[callno0]);
+ usleep(10);
+ ast_mutex_lock(&iaxsl[callno0]);
+ }
+}
+
+static void unlock_both(unsigned short callno0, unsigned short callno1)
+{
+ ast_mutex_unlock(&iaxsl[callno1]);
+ ast_mutex_unlock(&iaxsl[callno0]);
+}
+
+static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+{
+ struct ast_channel *cs[3];
+ struct ast_channel *who, *other;
+ int to = -1;
+ int res = -1;
+ int transferstarted=0;
+ struct ast_frame *f;
+ unsigned short callno0 = PTR_TO_CALLNO(c0->tech_pvt);
+ unsigned short callno1 = PTR_TO_CALLNO(c1->tech_pvt);
+ struct timeval waittimer = {0, 0}, tv;
+
+ lock_both(callno0, callno1);
+ if (!iaxs[callno0] || !iaxs[callno1]) {
+ unlock_both(callno0, callno1);
+ return AST_BRIDGE_FAILED;
+ }
+ /* Put them in native bridge mode */
+ if (!flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) {
+ iaxs[callno0]->bridgecallno = callno1;
+ iaxs[callno1]->bridgecallno = callno0;
+ }
+ unlock_both(callno0, callno1);
+
+ /* If not, try to bridge until we can execute a transfer, if we can */
+ cs[0] = c0;
+ cs[1] = c1;
+ for (/* ever */;;) {
+ /* Check in case we got masqueraded into */
+ if ((c0->tech != &iax2_tech) || (c1->tech != &iax2_tech)) {
+ ast_verb(3, "Can't masquerade, we're different...\n");
+ /* Remove from native mode */
+ if (c0->tech == &iax2_tech) {
+ ast_mutex_lock(&iaxsl[callno0]);
+ iaxs[callno0]->bridgecallno = 0;
+ ast_mutex_unlock(&iaxsl[callno0]);
+ }
+ if (c1->tech == &iax2_tech) {
+ ast_mutex_lock(&iaxsl[callno1]);
+ iaxs[callno1]->bridgecallno = 0;
+ ast_mutex_unlock(&iaxsl[callno1]);
+ }
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+ if (c0->nativeformats != c1->nativeformats) {
+ char buf0[255];
+ char buf1[255];
+ ast_getformatname_multiple(buf0, sizeof(buf0) -1, c0->nativeformats);
+ ast_getformatname_multiple(buf1, sizeof(buf1) -1, c1->nativeformats);
+ ast_verb(3, "Operating with different codecs %d[%s] %d[%s] , can't native bridge...\n", c0->nativeformats, buf0, c1->nativeformats, buf1);
+ /* Remove from native mode */
+ lock_both(callno0, callno1);
+ if (iaxs[callno0])
+ iaxs[callno0]->bridgecallno = 0;
+ if (iaxs[callno1])
+ iaxs[callno1]->bridgecallno = 0;
+ unlock_both(callno0, callno1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+ /* check if transfered and if we really want native bridging */
+ if (!transferstarted && !ast_test_flag(iaxs[callno0], IAX_NOTRANSFER) && !ast_test_flag(iaxs[callno1], IAX_NOTRANSFER)) {
+ /* Try the transfer */
+ if (iax2_start_transfer(callno0, callno1, (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) ||
+ ast_test_flag(iaxs[callno0], IAX_TRANSFERMEDIA) | ast_test_flag(iaxs[callno1], IAX_TRANSFERMEDIA)))
+ ast_log(LOG_WARNING, "Unable to start the transfer\n");
+ transferstarted = 1;
+ }
+ if ((iaxs[callno0]->transferring == TRANSFER_RELEASED) && (iaxs[callno1]->transferring == TRANSFER_RELEASED)) {
+ /* Call has been transferred. We're no longer involved */
+ tv = ast_tvnow();
+ if (ast_tvzero(waittimer)) {
+ waittimer = tv;
+ } else if (tv.tv_sec - waittimer.tv_sec > IAX_LINGER_TIMEOUT) {
+ c0->_softhangup |= AST_SOFTHANGUP_DEV;
+ c1->_softhangup |= AST_SOFTHANGUP_DEV;
+ *fo = NULL;
+ *rc = c0;
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ }
+ to = 1000;
+ who = ast_waitfor_n(cs, 2, &to);
+ if (timeoutms > -1) {
+ timeoutms -= (1000 - to);
+ if (timeoutms < 0)
+ timeoutms = 0;
+ }
+ if (!who) {
+ if (!timeoutms) {
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ if (ast_check_hangup(c0) || ast_check_hangup(c1)) {
+ res = AST_BRIDGE_FAILED;
+ break;
+ }
+ continue;
+ }
+ f = ast_read(who);
+ if (!f) {
+ *fo = NULL;
+ *rc = who;
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+ *fo = f;
+ *rc = who;
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ other = (who == c0) ? c1 : c0; /* the 'other' channel */
+ if ((f->frametype == AST_FRAME_VOICE) ||
+ (f->frametype == AST_FRAME_TEXT) ||
+ (f->frametype == AST_FRAME_VIDEO) ||
+ (f->frametype == AST_FRAME_IMAGE) ||
+ (f->frametype == AST_FRAME_DTMF)) {
+ /* monitored dtmf take out of the bridge.
+ * check if we monitor the specific source.
+ */
+ int monitored_source = (who == c0) ? AST_BRIDGE_DTMF_CHANNEL_0 : AST_BRIDGE_DTMF_CHANNEL_1;
+ if (f->frametype == AST_FRAME_DTMF && (flags & monitored_source)) {
+ *rc = who;
+ *fo = f;
+ res = AST_BRIDGE_COMPLETE;
+ /* Remove from native mode */
+ break;
+ }
+ /* everything else goes to the other side */
+ ast_write(other, f);
+ }
+ ast_frfree(f);
+ /* Swap who gets priority */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ }
+ lock_both(callno0, callno1);
+ if(iaxs[callno0])
+ iaxs[callno0]->bridgecallno = 0;
+ if(iaxs[callno1])
+ iaxs[callno1]->bridgecallno = 0;
+ unlock_both(callno0, callno1);
+ return res;
+}
+
+static int iax2_answer(struct ast_channel *c)
+{
+ unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
+ ast_debug(1, "Answering IAX2 call\n");
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno])
+ iax2_ami_channelupdate(iaxs[callno]);
+ ast_mutex_unlock(&iaxsl[callno]);
+ return send_command_locked(callno, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1);
+}
+
+static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen)
+{
+ unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
+ struct chan_iax2_pvt *pvt;
+ int res = 0;
+
+ if (iaxdebug)
+ ast_debug(1, "Indicating condition %d\n", condition);
+
+ ast_mutex_lock(&iaxsl[callno]);
+ pvt = iaxs[callno];
+
+ switch (condition) {
+ case AST_CONTROL_HOLD:
+ if (strcasecmp(pvt->mohinterpret, "passthrough")) {
+ ast_moh_start(c, data, pvt->mohinterpret);
+ goto done;
+ }
+ break;
+ case AST_CONTROL_UNHOLD:
+ if (strcasecmp(pvt->mohinterpret, "passthrough")) {
+ ast_moh_stop(c);
+ goto done;
+ }
+ }
+
+ res = send_command(pvt, AST_FRAME_CONTROL, condition, 0, data, datalen, -1);
+
+done:
+ ast_mutex_unlock(&iaxsl[callno]);
+
+ return res;
+}
+
+static int iax2_transfer(struct ast_channel *c, const char *dest)
+{
+ unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
+ struct iax_ie_data ied = { "", };
+ char tmp[256], *context;
+ ast_copy_string(tmp, dest, sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ }
+ iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, tmp);
+ if (context)
+ iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context);
+ ast_debug(1, "Transferring '%s' to '%s'\n", c->name, dest);
+ return send_command_locked(callno, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1);
+}
+
+static int iax2_getpeertrunk(struct sockaddr_in sin)
+{
+ struct iax2_peer *peer;
+ int res = 0;
+ struct ao2_iterator i;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
+ (peer->addr.sin_port == sin.sin_port)) {
+ res = ast_test_flag(peer, IAX_TRUNK);
+ peer_unref(peer);
+ break;
+ }
+ peer_unref(peer);
+ }
+
+ return res;
+}
+
+/*! \brief Create new call, interface with the PBX core */
+static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
+{
+ struct ast_channel *tmp;
+ struct chan_iax2_pvt *i;
+ struct ast_variable *v = NULL;
+
+ if (!(i = iaxs[callno])) {
+ ast_log(LOG_WARNING, "No IAX2 pvt found for callno '%d' !\n", callno);
+ return NULL;
+ }
+
+ /* Don't hold call lock */
+ ast_mutex_unlock(&iaxsl[callno]);
+ tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "IAX2/%s-%d", i->host, i->callno);
+ ast_mutex_lock(&iaxsl[callno]);
+ iax2_ami_channelupdate(i);
+ if (!tmp)
+ return NULL;
+ tmp->tech = &iax2_tech;
+ /* We can support any format by default, until we get restricted */
+ tmp->nativeformats = capability;
+ tmp->readformat = ast_best_codec(capability);
+ tmp->writeformat = ast_best_codec(capability);
+ tmp->tech_pvt = CALLNO_TO_PTR(i->callno);
+
+ /* Don't use ast_set_callerid() here because it will
+ * generate a NewCallerID event before the NewChannel event */
+ if (!ast_strlen_zero(i->ani))
+ tmp->cid.cid_ani = ast_strdup(i->ani);
+ else
+ tmp->cid.cid_ani = ast_strdup(i->cid_num);
+ tmp->cid.cid_dnid = ast_strdup(i->dnid);
+ tmp->cid.cid_rdnis = ast_strdup(i->rdnis);
+ tmp->cid.cid_pres = i->calling_pres;
+ tmp->cid.cid_ton = i->calling_ton;
+ tmp->cid.cid_tns = i->calling_tns;
+ if (!ast_strlen_zero(i->language))
+ ast_string_field_set(tmp, language, i->language);
+ if (!ast_strlen_zero(i->accountcode))
+ ast_string_field_set(tmp, accountcode, i->accountcode);
+ if (i->amaflags)
+ tmp->amaflags = i->amaflags;
+ ast_copy_string(tmp->context, i->context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
+ if (i->adsi)
+ tmp->adsicpe = i->peeradsicpe;
+ else
+ tmp->adsicpe = AST_ADSI_UNAVAILABLE;
+ i->owner = tmp;
+ i->capability = capability;
+
+ /* Set inherited variables */
+ if (i->vars) {
+ for (v = i->vars ; v ; v = v->next)
+ pbx_builtin_setvar_helper(tmp, v->name, v->value);
+ }
+
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ i->owner = NULL;
+ return NULL;
+ }
+ }
+
+ ast_module_ref(ast_module_info->self);
+ return tmp;
+}
+
+static unsigned int calc_txpeerstamp(struct iax2_trunk_peer *tpeer, int sampms, struct timeval *tv)
+{
+ unsigned long int mssincetx; /* unsigned to handle overflows */
+ long int ms, pred;
+
+ tpeer->trunkact = *tv;
+ mssincetx = ast_tvdiff_ms(*tv, tpeer->lasttxtime);
+ if (mssincetx > 5000 || ast_tvzero(tpeer->txtrunktime)) {
+ /* If it's been at least 5 seconds since the last time we transmitted on this trunk, reset our timers */
+ tpeer->txtrunktime = *tv;
+ tpeer->lastsent = 999999;
+ }
+ /* Update last transmit time now */
+ tpeer->lasttxtime = *tv;
+
+ /* Calculate ms offset */
+ ms = ast_tvdiff_ms(*tv, tpeer->txtrunktime);
+ /* Predict from last value */
+ pred = tpeer->lastsent + sampms;
+ if (abs(ms - pred) < MAX_TIMESTAMP_SKEW)
+ ms = pred;
+
+ /* We never send the same timestamp twice, so fudge a little if we must */
+ if (ms == tpeer->lastsent)
+ ms = tpeer->lastsent + 1;
+ tpeer->lastsent = ms;
+ return ms;
+}
+
+static unsigned int fix_peerts(struct timeval *tv, int callno, unsigned int ts)
+{
+ long ms; /* NOT unsigned */
+ if (ast_tvzero(iaxs[callno]->rxcore)) {
+ /* Initialize rxcore time if appropriate */
+ iaxs[callno]->rxcore = ast_tvnow();
+ /* Round to nearest 20ms so traces look pretty */
+ iaxs[callno]->rxcore.tv_usec -= iaxs[callno]->rxcore.tv_usec % 20000;
+ }
+ /* Calculate difference between trunk and channel */
+ ms = ast_tvdiff_ms(*tv, iaxs[callno]->rxcore);
+ /* Return as the sum of trunk time and the difference between trunk and real time */
+ return ms + ts;
+}
+
+static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, struct ast_frame *f)
+{
+ int ms;
+ int voice = 0;
+ int genuine = 0;
+ int adjust;
+ struct timeval *delivery = NULL;
+
+
+ /* What sort of frame do we have?: voice is self-explanatory
+ "genuine" means an IAX frame - things like LAGRQ/RP, PING/PONG, ACK
+ non-genuine frames are CONTROL frames [ringing etc], DTMF
+ The "genuine" distinction is needed because genuine frames must get a clock-based timestamp,
+ the others need a timestamp slaved to the voice frames so that they go in sequence
+ */
+ if (f) {
+ if (f->frametype == AST_FRAME_VOICE) {
+ voice = 1;
+ delivery = &f->delivery;
+ } else if (f->frametype == AST_FRAME_IAX) {
+ genuine = 1;
+ } else if (f->frametype == AST_FRAME_CNG) {
+ p->notsilenttx = 0;
+ }
+ }
+ if (ast_tvzero(p->offset)) {
+ p->offset = ast_tvnow();
+ /* Round to nearest 20ms for nice looking traces */
+ p->offset.tv_usec -= p->offset.tv_usec % 20000;
+ }
+ /* If the timestamp is specified, just send it as is */
+ if (ts)
+ return ts;
+ /* If we have a time that the frame arrived, always use it to make our timestamp */
+ if (delivery && !ast_tvzero(*delivery)) {
+ ms = ast_tvdiff_ms(*delivery, p->offset);
+ if (iaxdebug)
+ ast_debug(3, "calc_timestamp: call %d/%d: Timestamp slaved to delivery time\n", p->callno, iaxs[p->callno]->peercallno);
+ } else {
+ ms = ast_tvdiff_ms(ast_tvnow(), p->offset);
+ if (ms < 0)
+ ms = 0;
+ if (voice) {
+ /* On a voice frame, use predicted values if appropriate */
+ if (p->notsilenttx && abs(ms - p->nextpred) <= MAX_TIMESTAMP_SKEW) {
+ /* Adjust our txcore, keeping voice and non-voice synchronized */
+ /* AN EXPLANATION:
+ When we send voice, we usually send "calculated" timestamps worked out
+ on the basis of the number of samples sent. When we send other frames,
+ we usually send timestamps worked out from the real clock.
+ The problem is that they can tend to drift out of step because the
+ source channel's clock and our clock may not be exactly at the same rate.
+ We fix this by continuously "tweaking" p->offset. p->offset is "time zero"
+ for this call. Moving it adjusts timestamps for non-voice frames.
+ We make the adjustment in the style of a moving average. Each time we
+ adjust p->offset by 10% of the difference between our clock-derived
+ timestamp and the predicted timestamp. That's why you see "10000"
+ below even though IAX2 timestamps are in milliseconds.
+ The use of a moving average avoids offset moving too radically.
+ Generally, "adjust" roams back and forth around 0, with offset hardly
+ changing at all. But if a consistent different starts to develop it
+ will be eliminated over the course of 10 frames (200-300msecs)
+ */
+ adjust = (ms - p->nextpred);
+ if (adjust < 0)
+ p->offset = ast_tvsub(p->offset, ast_samp2tv(abs(adjust), 10000));
+ else if (adjust > 0)
+ p->offset = ast_tvadd(p->offset, ast_samp2tv(adjust, 10000));
+
+ if (!p->nextpred) {
+ p->nextpred = ms; /*f->samples / 8;*/
+ if (p->nextpred <= p->lastsent)
+ p->nextpred = p->lastsent + 3;
+ }
+ ms = p->nextpred;
+ } else {
+ /* in this case, just use the actual
+ * time, since we're either way off
+ * (shouldn't happen), or we're ending a
+ * silent period -- and seed the next
+ * predicted time. Also, round ms to the
+ * next multiple of frame size (so our
+ * silent periods are multiples of
+ * frame size too) */
+
+ if (iaxdebug && abs(ms - p->nextpred) > MAX_TIMESTAMP_SKEW )
+ ast_debug(1, "predicted timestamp skew (%u) > max (%u), using real ts instead.\n",
+ abs(ms - p->nextpred), MAX_TIMESTAMP_SKEW);
+
+ if (f->samples >= 8) /* check to make sure we dont core dump */
+ {
+ int diff = ms % (f->samples / 8);
+ if (diff)
+ ms += f->samples/8 - diff;
+ }
+
+ p->nextpred = ms;
+ p->notsilenttx = 1;
+ }
+ } else {
+ /* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) if appropriate unless
+ it's a genuine frame */
+ if (genuine) {
+ /* genuine (IAX LAGRQ etc) must keep their clock-based stamps */
+ if (ms <= p->lastsent)
+ ms = p->lastsent + 3;
+ } else if (abs(ms - p->lastsent) <= MAX_TIMESTAMP_SKEW) {
+ /* non-genuine frames (!?) (DTMF, CONTROL) should be pulled into the predicted stream stamps */
+ ms = p->lastsent + 3;
+ }
+ }
+ }
+ p->lastsent = ms;
+ if (voice)
+ p->nextpred = p->nextpred + f->samples / 8;
+ return ms;
+}
+
+static unsigned int calc_rxstamp(struct chan_iax2_pvt *p, unsigned int offset)
+{
+ /* Returns where in "receive time" we are. That is, how many ms
+ since we received (or would have received) the frame with timestamp 0 */
+ int ms;
+#ifdef IAXTESTS
+ int jit;
+#endif /* IAXTESTS */
+ /* Setup rxcore if necessary */
+ if (ast_tvzero(p->rxcore)) {
+ p->rxcore = ast_tvnow();
+ if (iaxdebug)
+ ast_debug(1, "calc_rxstamp: call=%d: rxcore set to %d.%6.6d - %dms\n",
+ p->callno, (int)(p->rxcore.tv_sec), (int)(p->rxcore.tv_usec), offset);
+ p->rxcore = ast_tvsub(p->rxcore, ast_samp2tv(offset, 1000));
+#if 1
+ if (iaxdebug)
+ ast_debug(1, "calc_rxstamp: call=%d: works out as %d.%6.6d\n",
+ p->callno, (int)(p->rxcore.tv_sec),(int)( p->rxcore.tv_usec));
+#endif
+ }
+
+ ms = ast_tvdiff_ms(ast_tvnow(), p->rxcore);
+#ifdef IAXTESTS
+ if (test_jit) {
+ if (!test_jitpct || ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_jitpct)) {
+ jit = (int)((float)test_jit * ast_random() / (RAND_MAX + 1.0));
+ if ((int)(2.0 * ast_random() / (RAND_MAX + 1.0)))
+ jit = -jit;
+ ms += jit;
+ }
+ }
+ if (test_late) {
+ ms += test_late;
+ test_late = 0;
+ }
+#endif /* IAXTESTS */
+ return ms;
+}
+
+static struct iax2_trunk_peer *find_tpeer(struct sockaddr_in *sin, int fd)
+{
+ struct iax2_trunk_peer *tpeer = NULL;
+
+ /* Finds and locks trunk peer */
+ AST_LIST_LOCK(&tpeers);
+
+ AST_LIST_TRAVERSE(&tpeers, tpeer, list) {
+ if (!inaddrcmp(&tpeer->addr, sin)) {
+ ast_mutex_lock(&tpeer->lock);
+ break;
+ }
+ }
+
+ if (!tpeer) {
+ if ((tpeer = ast_calloc(1, sizeof(*tpeer)))) {
+ ast_mutex_init(&tpeer->lock);
+ tpeer->lastsent = 9999;
+ memcpy(&tpeer->addr, sin, sizeof(tpeer->addr));
+ tpeer->trunkact = ast_tvnow();
+ ast_mutex_lock(&tpeer->lock);
+ tpeer->sockfd = fd;
+#ifdef SO_NO_CHECK
+ setsockopt(tpeer->sockfd, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
+#endif
+ ast_debug(1, "Created trunk peer for '%s:%d'\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port));
+ AST_LIST_INSERT_TAIL(&tpeers, tpeer, list);
+ }
+ }
+
+ AST_LIST_UNLOCK(&tpeers);
+
+ return tpeer;
+}
+
+static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr)
+{
+ struct ast_frame *f;
+ struct iax2_trunk_peer *tpeer;
+ void *tmp, *ptr;
+ struct timeval now;
+ int res;
+ struct ast_iax2_meta_trunk_entry *met;
+ struct ast_iax2_meta_trunk_mini *mtm;
+
+ f = &fr->af;
+ tpeer = find_tpeer(&pvt->addr, pvt->sockfd);
+ if (tpeer) {
+ if (tpeer->trunkdatalen + f->datalen + 4 >= tpeer->trunkdataalloc) {
+ /* Need to reallocate space */
+ if (tpeer->trunkdataalloc < trunkmaxsize) {
+ if (!(tmp = ast_realloc(tpeer->trunkdata, tpeer->trunkdataalloc + DEFAULT_TRUNKDATA + IAX2_TRUNK_PREFACE))) {
+ ast_mutex_unlock(&tpeer->lock);
+ return -1;
+ }
+
+ tpeer->trunkdataalloc += DEFAULT_TRUNKDATA;
+ tpeer->trunkdata = tmp;
+ ast_debug(1, "Expanded trunk '%s:%d' to %d bytes\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), tpeer->trunkdataalloc);
+ } else {
+ ast_log(LOG_WARNING, "Maximum trunk data space exceeded to %s:%d\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port));
+ ast_mutex_unlock(&tpeer->lock);
+ return -1;
+ }
+ }
+
+ /* Append to meta frame */
+ ptr = tpeer->trunkdata + IAX2_TRUNK_PREFACE + tpeer->trunkdatalen;
+ if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS)) {
+ mtm = (struct ast_iax2_meta_trunk_mini *)ptr;
+ mtm->len = htons(f->datalen);
+ mtm->mini.callno = htons(pvt->callno);
+ mtm->mini.ts = htons(0xffff & fr->ts);
+ ptr += sizeof(struct ast_iax2_meta_trunk_mini);
+ tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_mini);
+ } else {
+ met = (struct ast_iax2_meta_trunk_entry *)ptr;
+ /* Store call number and length in meta header */
+ met->callno = htons(pvt->callno);
+ met->len = htons(f->datalen);
+ /* Advance pointers/decrease length past trunk entry header */
+ ptr += sizeof(struct ast_iax2_meta_trunk_entry);
+ tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_entry);
+ }
+ /* Copy actual trunk data */
+ memcpy(ptr, f->data, f->datalen);
+ tpeer->trunkdatalen += f->datalen;
+
+ tpeer->calls++;
+
+ /* track the largest mtu we actually have sent */
+ if (tpeer->trunkdatalen + f->datalen + 4 > trunk_maxmtu)
+ trunk_maxmtu = tpeer->trunkdatalen + f->datalen + 4 ;
+
+ /* if we have enough for a full MTU, ship it now without waiting */
+ if (global_max_trunk_mtu > 0 && tpeer->trunkdatalen + f->datalen + 4 >= global_max_trunk_mtu) {
+ now = ast_tvnow();
+ res = send_trunk(tpeer, &now);
+ trunk_untimed ++;
+ }
+
+ ast_mutex_unlock(&tpeer->lock);
+ }
+ return 0;
+}
+
+static void build_enc_keys(const unsigned char *digest, ast_aes_encrypt_key *ecx, ast_aes_decrypt_key *dcx)
+{
+ ast_aes_encrypt_key(digest, ecx);
+ ast_aes_decrypt_key(digest, dcx);
+}
+
+static void memcpy_decrypt(unsigned char *dst, const unsigned char *src, int len, ast_aes_decrypt_key *dcx)
+{
+#if 0
+ /* Debug with "fake encryption" */
+ int x;
+ if (len % 16)
+ ast_log(LOG_WARNING, "len should be multiple of 16, not %d!\n", len);
+ for (x=0;x<len;x++)
+ dst[x] = src[x] ^ 0xff;
+#else
+ unsigned char lastblock[16] = { 0 };
+ int x;
+ while(len > 0) {
+ ast_aes_decrypt(src, dst, dcx);
+ for (x=0;x<16;x++)
+ dst[x] ^= lastblock[x];
+ memcpy(lastblock, src, sizeof(lastblock));
+ dst += 16;
+ src += 16;
+ len -= 16;
+ }
+#endif
+}
+
+static void memcpy_encrypt(unsigned char *dst, const unsigned char *src, int len, ast_aes_encrypt_key *ecx)
+{
+#if 0
+ /* Debug with "fake encryption" */
+ int x;
+ if (len % 16)
+ ast_log(LOG_WARNING, "len should be multiple of 16, not %d!\n", len);
+ for (x=0;x<len;x++)
+ dst[x] = src[x] ^ 0xff;
+#else
+ unsigned char curblock[16] = { 0 };
+ int x;
+ while(len > 0) {
+ for (x=0;x<16;x++)
+ curblock[x] ^= src[x];
+ ast_aes_encrypt(curblock, dst, ecx);
+ memcpy(curblock, dst, sizeof(curblock));
+ dst += 16;
+ src += 16;
+ len -= 16;
+ }
+#endif
+}
+
+static int decode_frame(ast_aes_decrypt_key *dcx, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen)
+{
+ int padding;
+ unsigned char *workspace;
+
+ workspace = alloca(*datalen);
+ memset(f, 0, sizeof(*f));
+ if (ntohs(fh->scallno) & IAX_FLAG_FULL) {
+ struct ast_iax2_full_enc_hdr *efh = (struct ast_iax2_full_enc_hdr *)fh;
+ if (*datalen < 16 + sizeof(struct ast_iax2_full_hdr))
+ return -1;
+ /* Decrypt */
+ memcpy_decrypt(workspace, efh->encdata, *datalen - sizeof(struct ast_iax2_full_enc_hdr), dcx);
+
+ padding = 16 + (workspace[15] & 0xf);
+ if (iaxdebug)
+ ast_debug(1, "Decoding full frame with length %d (padding = %d) (15=%02x)\n", *datalen, padding, workspace[15]);
+ if (*datalen < padding + sizeof(struct ast_iax2_full_hdr))
+ return -1;
+
+ *datalen -= padding;
+ memcpy(efh->encdata, workspace + padding, *datalen - sizeof(struct ast_iax2_full_enc_hdr));
+ f->frametype = fh->type;
+ if (f->frametype == AST_FRAME_VIDEO) {
+ f->subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1);
+ } else {
+ f->subclass = uncompress_subclass(fh->csub);
+ }
+ } else {
+ struct ast_iax2_mini_enc_hdr *efh = (struct ast_iax2_mini_enc_hdr *)fh;
+ if (iaxdebug)
+ ast_debug(1, "Decoding mini with length %d\n", *datalen);
+ if (*datalen < 16 + sizeof(struct ast_iax2_mini_hdr))
+ return -1;
+ /* Decrypt */
+ memcpy_decrypt(workspace, efh->encdata, *datalen - sizeof(struct ast_iax2_mini_enc_hdr), dcx);
+ padding = 16 + (workspace[15] & 0x0f);
+ if (*datalen < padding + sizeof(struct ast_iax2_mini_hdr))
+ return -1;
+ *datalen -= padding;
+ memcpy(efh->encdata, workspace + padding, *datalen - sizeof(struct ast_iax2_mini_enc_hdr));
+ }
+ return 0;
+}
+
+static int encrypt_frame(ast_aes_encrypt_key *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen)
+{
+ int padding;
+ unsigned char *workspace;
+ workspace = alloca(*datalen + 32);
+ if (!workspace)
+ return -1;
+ if (ntohs(fh->scallno) & IAX_FLAG_FULL) {
+ struct ast_iax2_full_enc_hdr *efh = (struct ast_iax2_full_enc_hdr *)fh;
+ if (iaxdebug)
+ ast_debug(1, "Encoding full frame %d/%d with length %d\n", fh->type, fh->csub, *datalen);
+ padding = 16 - ((*datalen - sizeof(struct ast_iax2_full_enc_hdr)) % 16);
+ padding = 16 + (padding & 0xf);
+ memcpy(workspace, poo, padding);
+ memcpy(workspace + padding, efh->encdata, *datalen - sizeof(struct ast_iax2_full_enc_hdr));
+ workspace[15] &= 0xf0;
+ workspace[15] |= (padding & 0xf);
+ if (iaxdebug)
+ ast_debug(1, "Encoding full frame %d/%d with length %d + %d padding (15=%02x)\n", fh->type, fh->csub, *datalen, padding, workspace[15]);
+ *datalen += padding;
+ memcpy_encrypt(efh->encdata, workspace, *datalen - sizeof(struct ast_iax2_full_enc_hdr), ecx);
+ if (*datalen >= 32 + sizeof(struct ast_iax2_full_enc_hdr))
+ memcpy(poo, workspace + *datalen - 32, 32);
+ } else {
+ struct ast_iax2_mini_enc_hdr *efh = (struct ast_iax2_mini_enc_hdr *)fh;
+ if (iaxdebug)
+ ast_debug(1, "Encoding mini frame with length %d\n", *datalen);
+ padding = 16 - ((*datalen - sizeof(struct ast_iax2_mini_enc_hdr)) % 16);
+ padding = 16 + (padding & 0xf);
+ memcpy(workspace, poo, padding);
+ memcpy(workspace + padding, efh->encdata, *datalen - sizeof(struct ast_iax2_mini_enc_hdr));
+ workspace[15] &= 0xf0;
+ workspace[15] |= (padding & 0x0f);
+ *datalen += padding;
+ memcpy_encrypt(efh->encdata, workspace, *datalen - sizeof(struct ast_iax2_mini_enc_hdr), ecx);
+ if (*datalen >= 32 + sizeof(struct ast_iax2_mini_enc_hdr))
+ memcpy(poo, workspace + *datalen - 32, 32);
+ }
+ return 0;
+}
+
+static int decrypt_frame(int callno, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen)
+{
+ int res=-1;
+ if (!ast_test_flag(iaxs[callno], IAX_KEYPOPULATED)) {
+ /* Search for possible keys, given secrets */
+ struct MD5Context md5;
+ unsigned char digest[16];
+ char *tmppw, *stringp;
+
+ tmppw = ast_strdupa(iaxs[callno]->secret);
+ stringp = tmppw;
+ while ((tmppw = strsep(&stringp, ";"))) {
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)iaxs[callno]->challenge, strlen(iaxs[callno]->challenge));
+ MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
+ MD5Final(digest, &md5);
+ build_enc_keys(digest, &iaxs[callno]->ecx, &iaxs[callno]->dcx);
+ res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen);
+ if (!res) {
+ ast_set_flag(iaxs[callno], IAX_KEYPOPULATED);
+ break;
+ }
+ }
+ } else
+ res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen);
+ return res;
+}
+
+static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final)
+{
+ /* Queue a packet for delivery on a given private structure. Use "ts" for
+ timestamp, or calculate if ts is 0. Send immediately without retransmission
+ or delayed, with retransmission */
+ struct ast_iax2_full_hdr *fh;
+ struct ast_iax2_mini_hdr *mh;
+ struct ast_iax2_video_hdr *vh;
+ struct {
+ struct iax_frame fr2;
+ unsigned char buffer[4096];
+ } frb;
+ struct iax_frame *fr;
+ int res;
+ int sendmini=0;
+ unsigned int lastsent;
+ unsigned int fts;
+
+ frb.fr2.afdatalen = sizeof(frb.buffer);
+
+ if (!pvt) {
+ ast_log(LOG_WARNING, "No private structure for packet?\n");
+ return -1;
+ }
+
+ lastsent = pvt->lastsent;
+
+ /* Calculate actual timestamp */
+ fts = calc_timestamp(pvt, ts, f);
+
+ /* Bail here if this is an "interp" frame; we don't want or need to send these placeholders out
+ * (the endpoint should detect the lost packet itself). But, we want to do this here, so that we
+ * increment the "predicted timestamps" for voice, if we're predicting */
+ if(f->frametype == AST_FRAME_VOICE && f->datalen == 0)
+ return 0;
+
+
+ if ((ast_test_flag(pvt, IAX_TRUNK) ||
+ (((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) ||
+ ((fts & 0xFFFF0000L) == ((lastsent + 0x10000) & 0xFFFF0000L))))
+ /* High two bytes are the same on timestamp, or sending on a trunk */ &&
+ (f->frametype == AST_FRAME_VOICE)
+ /* is a voice frame */ &&
+ (f->subclass == pvt->svoiceformat)
+ /* is the same type */ ) {
+ /* Force immediate rather than delayed transmission */
+ now = 1;
+ /* Mark that mini-style frame is appropriate */
+ sendmini = 1;
+ }
+ if (((fts & 0xFFFF8000L) == (lastsent & 0xFFFF8000L)) &&
+ (f->frametype == AST_FRAME_VIDEO) &&
+ ((f->subclass & ~0x1) == pvt->svideoformat)) {
+ now = 1;
+ sendmini = 1;
+ }
+ /* Allocate an iax_frame */
+ if (now) {
+ fr = &frb.fr2;
+ } else
+ fr = iax_frame_new(DIRECTION_OUTGRESS, ast_test_flag(pvt, IAX_ENCRYPTED) ? f->datalen + 32 : f->datalen, (f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO));
+ if (!fr) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return -1;
+ }
+ /* Copy our prospective frame into our immediate or retransmitted wrapper */
+ iax_frame_wrap(fr, f);
+
+ fr->ts = fts;
+ fr->callno = pvt->callno;
+ fr->transfer = transfer;
+ fr->final = final;
+ if (!sendmini) {
+ /* We need a full frame */
+ if (seqno > -1)
+ fr->oseqno = seqno;
+ else
+ fr->oseqno = pvt->oseqno++;
+ fr->iseqno = pvt->iseqno;
+ fh = (struct ast_iax2_full_hdr *)(fr->af.data - sizeof(struct ast_iax2_full_hdr));
+ fh->scallno = htons(fr->callno | IAX_FLAG_FULL);
+ fh->ts = htonl(fr->ts);
+ fh->oseqno = fr->oseqno;
+ if (transfer) {
+ fh->iseqno = 0;
+ } else
+ fh->iseqno = fr->iseqno;
+ /* Keep track of the last thing we've acknowledged */
+ if (!transfer)
+ pvt->aseqno = fr->iseqno;
+ fh->type = fr->af.frametype & 0xFF;
+ if (fr->af.frametype == AST_FRAME_VIDEO)
+ fh->csub = compress_subclass(fr->af.subclass & ~0x1) | ((fr->af.subclass & 0x1) << 6);
+ else
+ fh->csub = compress_subclass(fr->af.subclass);
+ if (transfer) {
+ fr->dcallno = pvt->transfercallno;
+ } else
+ fr->dcallno = pvt->peercallno;
+ fh->dcallno = htons(fr->dcallno);
+ fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_full_hdr);
+ fr->data = fh;
+ fr->retries = 0;
+ /* Retry after 2x the ping time has passed */
+ fr->retrytime = pvt->pingtime * 2;
+ if (fr->retrytime < MIN_RETRY_TIME)
+ fr->retrytime = MIN_RETRY_TIME;
+ if (fr->retrytime > MAX_RETRY_TIME)
+ fr->retrytime = MAX_RETRY_TIME;
+ /* Acks' don't get retried */
+ if ((f->frametype == AST_FRAME_IAX) && (f->subclass == IAX_COMMAND_ACK))
+ fr->retries = -1;
+ else if (f->frametype == AST_FRAME_VOICE)
+ pvt->svoiceformat = f->subclass;
+ else if (f->frametype == AST_FRAME_VIDEO)
+ pvt->svideoformat = f->subclass & ~0x1;
+ if (ast_test_flag(pvt, IAX_ENCRYPTED)) {
+ if (ast_test_flag(pvt, IAX_KEYPOPULATED)) {
+ if (iaxdebug) {
+ if (fr->transfer)
+ iax_showframe(fr, NULL, 2, &pvt->transfer, fr->datalen - sizeof(struct ast_iax2_full_hdr));
+ else
+ iax_showframe(fr, NULL, 2, &pvt->addr, fr->datalen - sizeof(struct ast_iax2_full_hdr));
+ }
+ encrypt_frame(&pvt->ecx, fh, pvt->semirand, &fr->datalen);
+ } else
+ ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n");
+ }
+
+ if (now) {
+ res = send_packet(fr);
+ } else
+ res = iax2_transmit(fr);
+ } else {
+ if (ast_test_flag(pvt, IAX_TRUNK)) {
+ iax2_trunk_queue(pvt, fr);
+ res = 0;
+ } else if (fr->af.frametype == AST_FRAME_VIDEO) {
+ /* Video frame have no sequence number */
+ fr->oseqno = -1;
+ fr->iseqno = -1;
+ vh = (struct ast_iax2_video_hdr *)(fr->af.data - sizeof(struct ast_iax2_video_hdr));
+ vh->zeros = 0;
+ vh->callno = htons(0x8000 | fr->callno);
+ vh->ts = htons((fr->ts & 0x7FFF) | (fr->af.subclass & 0x1 ? 0x8000 : 0));
+ fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_video_hdr);
+ fr->data = vh;
+ fr->retries = -1;
+ res = send_packet(fr);
+ } else {
+ /* Mini-frames have no sequence number */
+ fr->oseqno = -1;
+ fr->iseqno = -1;
+ /* Mini frame will do */
+ mh = (struct ast_iax2_mini_hdr *)(fr->af.data - sizeof(struct ast_iax2_mini_hdr));
+ mh->callno = htons(fr->callno);
+ mh->ts = htons(fr->ts & 0xFFFF);
+ fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_mini_hdr);
+ fr->data = mh;
+ fr->retries = -1;
+ if (pvt->transferring == TRANSFER_MEDIAPASS)
+ fr->transfer = 1;
+ if (ast_test_flag(pvt, IAX_ENCRYPTED)) {
+ if (ast_test_flag(pvt, IAX_KEYPOPULATED)) {
+ encrypt_frame(&pvt->ecx, (struct ast_iax2_full_hdr *)mh, pvt->semirand, &fr->datalen);
+ } else
+ ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n");
+ }
+ res = send_packet(fr);
+ }
+ }
+ return res;
+}
+
+static char *handle_cli_iax2_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ regex_t regexbuf;
+ int havepattern = 0;
+
+#define FORMAT "%-15.15s %-20.20s %-15.15s %-15.15s %-5.5s %-5.10s\n"
+#define FORMAT2 "%-15.15s %-20.20s %-15.15d %-15.15s %-5.5s %-5.10s\n"
+
+ struct iax2_user *user = NULL;
+ char auth[90];
+ char *pstr = "";
+ struct ao2_iterator i;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show users [like]";
+ e->usage =
+ "Usage: iax2 show users [like <pattern>]\n"
+ " Lists all known IAX2 users.\n"
+ " Optional regular expression pattern is used to filter the user list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ switch (a->argc) {
+ case 5:
+ if (!strcasecmp(a->argv[3], "like")) {
+ if (regcomp(&regexbuf, a->argv[4], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = 1;
+ } else
+ return CLI_SHOWUSAGE;
+ case 3:
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C","Codec Pref");
+ i = ao2_iterator_init(users, 0);
+ for (user = ao2_iterator_next(&i); user;
+ user_unref(user), user = ao2_iterator_next(&i)) {
+ if (havepattern && regexec(&regexbuf, user->name, 0, NULL, 0))
+ continue;
+
+ if (!ast_strlen_zero(user->secret)) {
+ ast_copy_string(auth,user->secret, sizeof(auth));
+ } else if (!ast_strlen_zero(user->inkeys)) {
+ snprintf(auth, sizeof(auth), "Key: %-15.15s ", user->inkeys);
+ } else
+ ast_copy_string(auth, "-no secret-", sizeof(auth));
+
+ if(ast_test_flag(user,IAX_CODEC_NOCAP))
+ pstr = "REQ Only";
+ else if(ast_test_flag(user,IAX_CODEC_NOPREFS))
+ pstr = "Disabled";
+ else
+ pstr = ast_test_flag(user,IAX_CODEC_USER_FIRST) ? "Caller" : "Host";
+
+ ast_cli(a->fd, FORMAT2, user->name, auth, user->authmethods,
+ user->contexts ? user->contexts->context : context,
+ user->ha ? "Yes" : "No", pstr);
+ }
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc, char *argv[])
+{
+ regex_t regexbuf;
+ int havepattern = 0;
+ int total_peers = 0;
+ int online_peers = 0;
+ int offline_peers = 0;
+ int unmonitored_peers = 0;
+ struct ao2_iterator i;
+
+#define FORMAT2 "%-15.15s %-15.15s %s %-15.15s %-8s %s %-10s%s"
+#define FORMAT "%-15.15s %-15.15s %s %-15.15s %-5d%s %s %-10s%s"
+
+ struct iax2_peer *peer = NULL;
+ char name[256];
+ int registeredonly=0;
+ char *term = manager ? "\r\n" : "\n";
+ char idtext[256] = "";
+ switch (argc) {
+ case 6:
+ if (!strcasecmp(argv[3], "registered"))
+ registeredonly = 1;
+ else
+ return RESULT_SHOWUSAGE;
+ if (!strcasecmp(argv[4], "like")) {
+ if (regcomp(&regexbuf, argv[5], REG_EXTENDED | REG_NOSUB))
+ return RESULT_SHOWUSAGE;
+ havepattern = 1;
+ } else
+ return RESULT_SHOWUSAGE;
+ break;
+ case 5:
+ if (!strcasecmp(argv[3], "like")) {
+ if (regcomp(&regexbuf, argv[4], REG_EXTENDED | REG_NOSUB))
+ return RESULT_SHOWUSAGE;
+ havepattern = 1;
+ } else
+ return RESULT_SHOWUSAGE;
+ break;
+ case 4:
+ if (!strcasecmp(argv[3], "registered"))
+ registeredonly = 1;
+ else
+ return RESULT_SHOWUSAGE;
+ break;
+ case 3:
+ break;
+ default:
+ return RESULT_SHOWUSAGE;
+ }
+
+
+ if (!s)
+ ast_cli(fd, FORMAT2, "Name/Username", "Host", " ", "Mask", "Port", " ", "Status", term);
+
+ i = ao2_iterator_init(peers, 0);
+ for (peer = ao2_iterator_next(&i); peer;
+ peer_unref(peer), peer = ao2_iterator_next(&i)) {
+ char nm[20];
+ char status[20];
+ char srch[2000];
+ int retstatus;
+
+ if (registeredonly && !peer->addr.sin_addr.s_addr)
+ continue;
+ if (havepattern && regexec(&regexbuf, peer->name, 0, NULL, 0))
+ continue;
+
+ if (!ast_strlen_zero(peer->username))
+ snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
+ else
+ ast_copy_string(name, peer->name, sizeof(name));
+
+ retstatus = peer_status(peer, status, sizeof(status));
+ if (retstatus > 0)
+ online_peers++;
+ else if (!retstatus)
+ offline_peers++;
+ else
+ unmonitored_peers++;
+
+ ast_copy_string(nm, ast_inet_ntoa(peer->mask), sizeof(nm));
+
+ snprintf(srch, sizeof(srch), FORMAT, name,
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+ ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)",
+ nm,
+ ntohs(peer->addr.sin_port), ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ",
+ peer->encmethods ? "(E)" : " ", status, term);
+
+ if (s)
+ astman_append(s,
+ "Event: PeerEntry\r\n%s"
+ "Channeltype: IAX2\r\n"
+ "ChanObjectType: peer\r\n"
+ "ObjectName: %s\r\n"
+ "IPaddress: %s\r\n"
+ "IPport: %d\r\n"
+ "Dynamic: %s\r\n"
+ "Status: %s\r\n\r\n",
+ idtext,
+ name,
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "-none-",
+ ntohs(peer->addr.sin_port),
+ ast_test_flag(peer, IAX_DYNAMIC) ? "yes" : "no",
+ status);
+
+ else
+ ast_cli(fd, FORMAT, name,
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+ ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)",
+ nm,
+ ntohs(peer->addr.sin_port), ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ",
+ peer->encmethods ? "(E)" : " ", status, term);
+ total_peers++;
+ }
+
+ if (!s)
+ ast_cli(fd,"%d iax2 peers [%d online, %d offline, %d unmonitored]%s", total_peers, online_peers, offline_peers, unmonitored_peers, term);
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *handle_cli_iax2_show_threads(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct iax2_thread *thread = NULL;
+ time_t t;
+ int threadcount = 0, dynamiccount = 0;
+ char type;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show threads";
+ e->usage =
+ "Usage: iax2 show threads\n"
+ " Lists status of IAX helper threads\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "IAX2 Thread Information\n");
+ time(&t);
+ ast_cli(a->fd, "Idle Threads:\n");
+ AST_LIST_LOCK(&idle_list);
+ AST_LIST_TRAVERSE(&idle_list, thread, list) {
+#ifdef DEBUG_SCHED_MULTITHREAD
+ ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d, func='%s'\n",
+ thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc);
+#else
+ ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d\n",
+ thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions);
+#endif
+ threadcount++;
+ }
+ AST_LIST_UNLOCK(&idle_list);
+ ast_cli(a->fd, "Active Threads:\n");
+ AST_LIST_LOCK(&active_list);
+ AST_LIST_TRAVERSE(&active_list, thread, list) {
+ if (thread->type == IAX_THREAD_TYPE_DYNAMIC)
+ type = 'D';
+ else
+ type = 'P';
+#ifdef DEBUG_SCHED_MULTITHREAD
+ ast_cli(a->fd, "Thread %c%d: state=%d, update=%d, actions=%d, func='%s'\n",
+ type, thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc);
+#else
+ ast_cli(a->fd, "Thread %c%d: state=%d, update=%d, actions=%d\n",
+ type, thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions);
+#endif
+ threadcount++;
+ }
+ AST_LIST_UNLOCK(&active_list);
+ ast_cli(a->fd, "Dynamic Threads:\n");
+ AST_LIST_LOCK(&dynamic_list);
+ AST_LIST_TRAVERSE(&dynamic_list, thread, list) {
+#ifdef DEBUG_SCHED_MULTITHREAD
+ ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d, func='%s'\n",
+ thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc);
+#else
+ ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d\n",
+ thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions);
+#endif
+ dynamiccount++;
+ }
+ AST_LIST_UNLOCK(&dynamic_list);
+ ast_cli(a->fd, "%d of %d threads accounted for with %d dynamic threads\n", threadcount, iaxthreadcount, dynamiccount);
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct iax2_peer *p;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 unregister";
+ e->usage =
+ "Usage: iax2 unregister <peername>\n"
+ " Unregister (force expiration) an IAX2 peer from the registry.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_iax2_unregister(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ p = find_peer(a->argv[2], 1);
+ if (p) {
+ if (p->expire > 0) {
+ struct iax2_peer tmp_peer = {
+ .name = a->argv[2],
+ };
+ struct iax2_peer *peer;
+
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+ if (peer) {
+ expire_registry(peer_ref(peer)); /* will release its own reference when done */
+ peer_unref(peer); /* ref from ao2_find() */
+ ast_cli(a->fd, "Peer %s unregistered\n", a->argv[2]);
+ } else {
+ ast_cli(a->fd, "Peer %s not found\n", a->argv[2]);
+ }
+ } else {
+ ast_cli(a->fd, "Peer %s not registered\n", a->argv[2]);
+ }
+ } else {
+ ast_cli(a->fd, "Peer unknown: %s. Not unregistered\n", a->argv[2]);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *complete_iax2_unregister(const char *line, const char *word, int pos, int state)
+{
+ int which = 0;
+ struct iax2_peer *p = NULL;
+ char *res = NULL;
+ int wordlen = strlen(word);
+
+ /* 0 - iax2; 1 - unregister; 2 - <peername> */
+ if (pos == 2) {
+ struct ao2_iterator i = ao2_iterator_init(peers, 0);
+ while ((p = ao2_iterator_next(&i))) {
+ if (!strncasecmp(p->name, word, wordlen) &&
+ ++which > state && p->expire > 0) {
+ res = ast_strdup(p->name);
+ peer_unref(p);
+ break;
+ }
+ peer_unref(p);
+ }
+ }
+
+ return res;
+}
+
+static char *handle_cli_iax2_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show peers";
+ e->usage =
+ "Usage: iax2 show peers [registered] [like <pattern>]\n"
+ " Lists all known IAX2 peers.\n"
+ " Optional 'registered' argument lists only peers with known addresses.\n"
+ " Optional regular expression pattern is used to filter the peer list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ switch (__iax2_show_peers(0, a->fd, NULL, a->argc, a->argv)) {
+ case RESULT_SHOWUSAGE:
+ return CLI_SHOWUSAGE;
+ case RESULT_FAILURE:
+ return CLI_FAILURE;
+ default:
+ return CLI_SUCCESS;
+ }
+}
+
+static int manager_iax2_show_netstats(struct mansession *s, const struct message *m)
+{
+ ast_cli_netstats(s, -1, 0);
+ astman_append(s, "\r\n");
+ return RESULT_SUCCESS;
+}
+
+static char *handle_cli_iax2_show_firmware(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct iax_firmware *cur = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show firmware";
+ e->usage =
+ "Usage: iax2 show firmware\n"
+ " Lists all known IAX firmware images.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3 && a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "%-15.15s %-15.15s %-15.15s\n", "Device", "Version", "Size");
+ AST_LIST_LOCK(&firmwares);
+ AST_LIST_TRAVERSE(&firmwares, cur, list) {
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3], (char *) cur->fwh->devname))) {
+ ast_cli(a->fd, "%-15.15s %-15d %-15d\n", cur->fwh->devname,
+ ntohs(cur->fwh->version), (int)ntohl(cur->fwh->datalen));
+ }
+ }
+ AST_LIST_UNLOCK(&firmwares);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief callback to display iax peers in manager */
+static int manager_iax2_show_peers(struct mansession *s, const struct message *m)
+{
+ char *a[] = { "iax2", "show", "users" };
+ const char *id = astman_get_header(m,"ActionID");
+ char idtext[256] = "";
+
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+ astman_send_ack(s, m, "Peer status list will follow");
+ return __iax2_show_peers(1, -1, s, 3, a );
+}
+
+/*! \brief callback to display iax peers in manager format */
+static int manager_iax2_show_peer_list(struct mansession *s, const struct message *m)
+{
+ struct iax2_peer *peer = NULL;
+ int peer_count = 0;
+ char nm[20];
+ char status[20];
+ const char *id = astman_get_header(m,"ActionID");
+ char idtext[256] = "";
+ struct ao2_iterator i;
+
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+ astman_append(s, "Response: Success\r\n%sMessage: IAX Peer status list will follow\r\n\r\n", idtext);
+
+
+ i = ao2_iterator_init(peers, 0);
+ for (peer = ao2_iterator_next(&i); peer; peer_unref(peer), peer = ao2_iterator_next(&i)) {
+
+ astman_append(s, "Event: PeerEntry\r\n%sChanneltype: IAX\r\n", idtext);
+ if (!ast_strlen_zero(peer->username)) {
+ astman_append(s, "ObjectName: %s\r\nObjectUsername: %s\r\n", peer->name, peer->username);
+ } else {
+ astman_append(s, "ObjectName: %s\r\n", peer->name);
+ }
+ astman_append(s, "ChanObjectType: peer\r\n");
+ astman_append(s, "IPaddress: %s\r\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "-none-");
+ ast_copy_string(nm, ast_inet_ntoa(peer->mask), sizeof(nm));
+ astman_append(s, "Mask: %s\r\n", nm);
+ astman_append(s, "Port: %d\r\n", ntohs(peer->addr.sin_port));
+ astman_append(s, "Dynamic: %s\r\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No");
+ peer_status(peer, status, sizeof(status));
+ astman_append(s, "Status: %s\r\n\r\n", status);
+ peer_count++;
+ }
+
+ astman_append(s, "Event: PeerlistComplete\r\n%sListItems: %d\r\n\r\n", idtext, peer_count);
+ return RESULT_SUCCESS;
+}
+
+
+static char *regstate2str(int regstate)
+{
+ switch(regstate) {
+ case REG_STATE_UNREGISTERED:
+ return "Unregistered";
+ case REG_STATE_REGSENT:
+ return "Request Sent";
+ case REG_STATE_AUTHSENT:
+ return "Auth. Sent";
+ case REG_STATE_REGISTERED:
+ return "Registered";
+ case REG_STATE_REJECTED:
+ return "Rejected";
+ case REG_STATE_TIMEOUT:
+ return "Timeout";
+ case REG_STATE_NOAUTH:
+ return "No Authentication";
+ default:
+ return "Unknown";
+ }
+}
+
+static char *handle_cli_iax2_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-20.20s %-6.6s %-10.10s %-20.20s %8.8s %s\n"
+#define FORMAT "%-20.20s %-6.6s %-10.10s %-20.20s %8d %s\n"
+ struct iax2_registry *reg = NULL;
+ char host[80];
+ char perceived[80];
+ int counter = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show registry";
+ e->usage =
+ "Usage: iax2 show registry\n"
+ " Lists all registration requests and status.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, FORMAT2, "Host", "dnsmgr", "Username", "Perceived", "Refresh", "State");
+ AST_LIST_LOCK(&registrations);
+ AST_LIST_TRAVERSE(&registrations, reg, entry) {
+ snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(reg->addr.sin_addr), ntohs(reg->addr.sin_port));
+ if (reg->us.sin_addr.s_addr)
+ snprintf(perceived, sizeof(perceived), "%s:%d", ast_inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port));
+ else
+ ast_copy_string(perceived, "<Unregistered>", sizeof(perceived));
+ ast_cli(a->fd, FORMAT, host,
+ (reg->dnsmgr) ? "Y" : "N",
+ reg->username, perceived, reg->refresh, regstate2str(reg->regstate));
+ counter++;
+ }
+ AST_LIST_UNLOCK(&registrations);
+ ast_cli(a->fd, "%d IAX2 registrations.\n", counter);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *handle_cli_iax2_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-20.20s %-15.15s %-10.10s %-11.11s %-11.11s %-7.7s %-6.6s %-6.6s %s\n"
+#define FORMAT "%-20.20s %-15.15s %-10.10s %5.5d/%5.5d %5.5d/%5.5d %-5.5dms %-4.4dms %-4.4dms %-6.6s\n"
+#define FORMATB "%-20.20s %-15.15s %-10.10s %5.5d/%5.5d %5.5d/%5.5d [Native Bridged to ID=%5.5d]\n"
+ int x;
+ int numchans = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show channels";
+ e->usage =
+ "Usage: iax2 show channels\n"
+ " Lists all currently active IAX channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, FORMAT2, "Channel", "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "JitBuf", "Format");
+ for (x = 0; x < IAX_MAX_CALLS; x++) {
+ ast_mutex_lock(&iaxsl[x]);
+ if (iaxs[x]) {
+ int lag, jitter, localdelay;
+ jb_info jbinfo;
+
+ if (ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) {
+ jb_getinfo(iaxs[x]->jb, &jbinfo);
+ jitter = jbinfo.jitter;
+ localdelay = jbinfo.current - jbinfo.min;
+ } else {
+ jitter = -1;
+ localdelay = 0;
+ }
+ lag = iaxs[x]->remote_rr.delay;
+ ast_cli(a->fd, FORMAT,
+ iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
+ ast_inet_ntoa(iaxs[x]->addr.sin_addr),
+ S_OR(iaxs[x]->username, "(None)"),
+ iaxs[x]->callno, iaxs[x]->peercallno,
+ iaxs[x]->oseqno, iaxs[x]->iseqno,
+ lag,
+ jitter,
+ localdelay,
+ ast_getformatname(iaxs[x]->voiceformat) );
+ numchans++;
+ }
+ ast_mutex_unlock(&iaxsl[x]);
+ }
+ ast_cli(a->fd, "%d active IAX channel%s\n", numchans, (numchans != 1) ? "s" : "");
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+#undef FORMATB
+}
+
+static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt)
+{
+ int x;
+ int numchans = 0;
+ for (x=0;x<IAX_MAX_CALLS;x++) {
+ ast_mutex_lock(&iaxsl[x]);
+ if (iaxs[x]) {
+ int localjitter, localdelay, locallost, locallosspct, localdropped, localooo;
+ char *fmt;
+ jb_info jbinfo;
+
+ if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) {
+ jb_getinfo(iaxs[x]->jb, &jbinfo);
+ localjitter = jbinfo.jitter;
+ localdelay = jbinfo.current - jbinfo.min;
+ locallost = jbinfo.frames_lost;
+ locallosspct = jbinfo.losspct/1000;
+ localdropped = jbinfo.frames_dropped;
+ localooo = jbinfo.frames_ooo;
+ } else {
+ localjitter = -1;
+ localdelay = 0;
+ locallost = -1;
+ locallosspct = -1;
+ localdropped = 0;
+ localooo = -1;
+ }
+ if (limit_fmt)
+ fmt = "%-25.25s %4d %4d %4d %5d %3d %5d %4d %6d %4d %4d %5d %3d %5d %4d %6d\n";
+ else
+ fmt = "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n";
+ if (s)
+
+ astman_append(s, fmt,
+ iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
+ iaxs[x]->pingtime,
+ localjitter,
+ localdelay,
+ locallost,
+ locallosspct,
+ localdropped,
+ localooo,
+ iaxs[x]->frames_received/1000,
+ iaxs[x]->remote_rr.jitter,
+ iaxs[x]->remote_rr.delay,
+ iaxs[x]->remote_rr.losscnt,
+ iaxs[x]->remote_rr.losspct,
+ iaxs[x]->remote_rr.dropped,
+ iaxs[x]->remote_rr.ooo,
+ iaxs[x]->remote_rr.packets/1000);
+ else
+ ast_cli(fd, fmt,
+ iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
+ iaxs[x]->pingtime,
+ localjitter,
+ localdelay,
+ locallost,
+ locallosspct,
+ localdropped,
+ localooo,
+ iaxs[x]->frames_received/1000,
+ iaxs[x]->remote_rr.jitter,
+ iaxs[x]->remote_rr.delay,
+ iaxs[x]->remote_rr.losscnt,
+ iaxs[x]->remote_rr.losspct,
+ iaxs[x]->remote_rr.dropped,
+ iaxs[x]->remote_rr.ooo,
+ iaxs[x]->remote_rr.packets/1000
+ );
+ numchans++;
+ }
+ ast_mutex_unlock(&iaxsl[x]);
+ }
+
+ return numchans;
+}
+
+static char *handle_cli_iax2_show_netstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int numchans = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show netstats";
+ e->usage =
+ "Usage: iax2 show netstats\n"
+ " Lists network status for all currently active IAX channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, " -------- LOCAL --------------------- -------- REMOTE --------------------\n");
+ ast_cli(a->fd, "Channel RTT Jit Del Lost %% Drop OOO Kpkts Jit Del Lost %% Drop OOO Kpkts\n");
+ numchans = ast_cli_netstats(NULL, a->fd, 1);
+ ast_cli(a->fd, "%d active IAX channel%s\n", numchans, (numchans != 1) ? "s" : "");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 set debug";
+ e->usage =
+ "Usage: iax2 set debug\n"
+ " Enables dumping of IAX packets for debugging purposes.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 2 || a->argc > 3)
+ return CLI_SHOWUSAGE;
+ iaxdebug = 1;
+ ast_cli(a->fd, "IAX2 Debugging Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_set_debug_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 set debug off";
+ e->usage =
+ "Usage: iax2 set debug off\n"
+ " Disables dumping of IAX packets for debugging purposes.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ iaxdebug = 0;
+ ast_cli(a->fd, "IAX2 Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_set_debug_trunk(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 set debug trunk";
+ e->usage =
+ "Usage: iax2 set debug trunk\n"
+ " Requests current status of IAX trunking\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ iaxtrunkdebug = 1;
+ ast_cli(a->fd, "IAX2 Trunk Debugging Requested\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_set_debug_trunk_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 set debug trunk off";
+ e->usage =
+ "Usage: iax2 set debug trunk off\n"
+ " Disables debugging of IAX trunking\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 4 || a->argc > 5)
+ return CLI_SHOWUSAGE;
+ iaxtrunkdebug = 0;
+ ast_cli(a->fd, "IAX2 Trunk Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_set_debug_jb(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 set debug jb";
+ e->usage =
+ "Usage: iax2 set debug jb\n"
+ " Enables jitterbuffer debugging information\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ jb_setoutput(jb_error_output, jb_warning_output, jb_debug_output);
+ ast_cli(a->fd, "IAX2 Jitterbuffer Debugging Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_iax2_set_debug_jb_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 set debug jb off";
+ e->usage =
+ "Usage: iax2 set debug jb off\n"
+ " Disables jitterbuffer debugging information\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 4 || a->argc > 5)
+ return CLI_SHOWUSAGE;
+ jb_setoutput(jb_error_output, jb_warning_output, NULL);
+ ast_cli(a->fd, "IAX2 Jitterbuffer Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static int iax2_write(struct ast_channel *c, struct ast_frame *f)
+{
+ unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
+ int res = -1;
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+ /* If there's an outstanding error, return failure now */
+ if (!iaxs[callno]->error) {
+ if (ast_test_flag(iaxs[callno], IAX_ALREADYGONE))
+ res = 0;
+ /* Don't waste bandwidth sending null frames */
+ else if (f->frametype == AST_FRAME_NULL)
+ res = 0;
+ else if ((f->frametype == AST_FRAME_VOICE) && ast_test_flag(iaxs[callno], IAX_QUELCH))
+ res = 0;
+ else if (!ast_test_flag(&iaxs[callno]->state, IAX_STATE_STARTED))
+ res = 0;
+ else
+ /* Simple, just queue for transmission */
+ res = iax2_send(iaxs[callno], f, 0, -1, 0, 0, 0);
+ } else {
+ ast_debug(1, "Write error: %s\n", strerror(errno));
+ }
+ }
+ /* If it's already gone, just return */
+ ast_mutex_unlock(&iaxsl[callno]);
+ return res;
+}
+
+static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno,
+ int now, int transfer, int final)
+{
+ struct ast_frame f = { 0, };
+
+ f.frametype = type;
+ f.subclass = command;
+ f.datalen = datalen;
+ f.src = __FUNCTION__;
+ f.data = (void *) data;
+
+ return iax2_send(i, &f, ts, seqno, now, transfer, final);
+}
+
+static int send_command(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
+{
+ return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0);
+}
+
+static int send_command_locked(unsigned short callno, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
+{
+ int res;
+ ast_mutex_lock(&iaxsl[callno]);
+ res = send_command(iaxs[callno], type, command, ts, data, datalen, seqno);
+ ast_mutex_unlock(&iaxsl[callno]);
+ return res;
+}
+
+/*!
+ * \note Since this function calls iax2_predestroy() -> iax2_queue_hangup(),
+ * the pvt struct for the given call number may disappear during its
+ * execution.
+ */
+static int send_command_final(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
+{
+ int call_num = i->callno;
+ /* It is assumed that the callno has already been locked */
+ iax2_predestroy(i->callno);
+ if (!iaxs[call_num])
+ return -1;
+ return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1);
+}
+
+static int send_command_immediate(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
+{
+ return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0);
+}
+
+static int send_command_transfer(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen)
+{
+ return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0);
+}
+
+static int apply_context(struct iax2_context *con, const char *context)
+{
+ while(con) {
+ if (!strcmp(con->context, context) || !strcmp(con->context, "*"))
+ return -1;
+ con = con->next;
+ }
+ return 0;
+}
+
+
+static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
+{
+ /* Start pessimistic */
+ int res = -1;
+ int version = 2;
+ struct iax2_user *user = NULL, *best = NULL;
+ int bestscore = 0;
+ int gotcapability = 0;
+ struct ast_variable *v = NULL, *tmpvar = NULL;
+ struct ao2_iterator i;
+
+ if (!iaxs[callno])
+ return res;
+ if (ies->called_number)
+ ast_string_field_set(iaxs[callno], exten, ies->called_number);
+ if (ies->calling_number) {
+ ast_shrink_phone_number(ies->calling_number);
+ ast_string_field_set(iaxs[callno], cid_num, ies->calling_number);
+ }
+ if (ies->calling_name)
+ ast_string_field_set(iaxs[callno], cid_name, ies->calling_name);
+ if (ies->calling_ani)
+ ast_string_field_set(iaxs[callno], ani, ies->calling_ani);
+ if (ies->dnid)
+ ast_string_field_set(iaxs[callno], dnid, ies->dnid);
+ if (ies->rdnis)
+ ast_string_field_set(iaxs[callno], rdnis, ies->rdnis);
+ if (ies->called_context)
+ ast_string_field_set(iaxs[callno], context, ies->called_context);
+ if (ies->language)
+ ast_string_field_set(iaxs[callno], language, ies->language);
+ if (ies->username)
+ ast_string_field_set(iaxs[callno], username, ies->username);
+ if (ies->calling_ton > -1)
+ iaxs[callno]->calling_ton = ies->calling_ton;
+ if (ies->calling_tns > -1)
+ iaxs[callno]->calling_tns = ies->calling_tns;
+ if (ies->calling_pres > -1)
+ iaxs[callno]->calling_pres = ies->calling_pres;
+ if (ies->format)
+ iaxs[callno]->peerformat = ies->format;
+ if (ies->adsicpe)
+ iaxs[callno]->peeradsicpe = ies->adsicpe;
+ if (ies->capability) {
+ gotcapability = 1;
+ iaxs[callno]->peercapability = ies->capability;
+ }
+ if (ies->version)
+ version = ies->version;
+
+ /* Use provided preferences until told otherwise for actual preferences */
+ if(ies->codec_prefs) {
+ ast_codec_pref_convert(&iaxs[callno]->rprefs, ies->codec_prefs, 32, 0);
+ ast_codec_pref_convert(&iaxs[callno]->prefs, ies->codec_prefs, 32, 0);
+ }
+
+ if (!gotcapability)
+ iaxs[callno]->peercapability = iaxs[callno]->peerformat;
+ if (version > IAX_PROTO_VERSION) {
+ ast_log(LOG_WARNING, "Peer '%s' has too new a protocol version (%d) for me\n",
+ ast_inet_ntoa(sin->sin_addr), version);
+ return res;
+ }
+ /* Search the userlist for a compatible entry, and fill in the rest */
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
+ if ((ast_strlen_zero(iaxs[callno]->username) || /* No username specified */
+ !strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */
+ && ast_apply_ha(user->ha, sin) /* Access is permitted from this IP */
+ && (ast_strlen_zero(iaxs[callno]->context) || /* No context specified */
+ apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */
+ if (!ast_strlen_zero(iaxs[callno]->username)) {
+ /* Exact match, stop right now. */
+ if (best)
+ user_unref(best);
+ best = user;
+ break;
+ } else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->inkeys)) {
+ /* No required authentication */
+ if (user->ha) {
+ /* There was host authentication and we passed, bonus! */
+ if (bestscore < 4) {
+ bestscore = 4;
+ if (best)
+ user_unref(best);
+ best = user;
+ continue;
+ }
+ } else {
+ /* No host access, but no secret, either, not bad */
+ if (bestscore < 3) {
+ bestscore = 3;
+ if (best)
+ user_unref(best);
+ best = user;
+ continue;
+ }
+ }
+ } else {
+ if (user->ha) {
+ /* Authentication, but host access too, eh, it's something.. */
+ if (bestscore < 2) {
+ bestscore = 2;
+ if (best)
+ user_unref(best);
+ best = user;
+ continue;
+ }
+ } else {
+ /* Authentication and no host access... This is our baseline */
+ if (bestscore < 1) {
+ bestscore = 1;
+ if (best)
+ user_unref(best);
+ best = user;
+ continue;
+ }
+ }
+ }
+ }
+ user_unref(user);
+ }
+ user = best;
+ if (!user && !ast_strlen_zero(iaxs[callno]->username)) {
+ user = realtime_user(iaxs[callno]->username, sin);
+ if (user && !ast_strlen_zero(iaxs[callno]->context) && /* No context specified */
+ !apply_context(user->contexts, iaxs[callno]->context)) { /* Context is permitted */
+ user = user_unref(user);
+ }
+ }
+ if (user) {
+ /* We found our match (use the first) */
+ /* copy vars */
+ for (v = user->vars ; v ; v = v->next) {
+ if((tmpvar = ast_variable_new(v->name, v->value, v->file))) {
+ tmpvar->next = iaxs[callno]->vars;
+ iaxs[callno]->vars = tmpvar;
+ }
+ }
+ /* If a max AUTHREQ restriction is in place, activate it */
+ if (user->maxauthreq > 0)
+ ast_set_flag(iaxs[callno], IAX_MAXAUTHREQ);
+ iaxs[callno]->prefs = user->prefs;
+ ast_copy_flags(iaxs[callno], user, IAX_CODEC_USER_FIRST);
+ ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOPREFS);
+ ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOCAP);
+ iaxs[callno]->encmethods = user->encmethods;
+ /* Store the requested username if not specified */
+ if (ast_strlen_zero(iaxs[callno]->username))
+ ast_string_field_set(iaxs[callno], username, user->name);
+ /* Store whether this is a trunked call, too, of course, and move if appropriate */
+ ast_copy_flags(iaxs[callno], user, IAX_TRUNK);
+ iaxs[callno]->capability = user->capability;
+ /* And use the default context */
+ if (ast_strlen_zero(iaxs[callno]->context)) {
+ if (user->contexts)
+ ast_string_field_set(iaxs[callno], context, user->contexts->context);
+ else
+ ast_string_field_set(iaxs[callno], context, context);
+ }
+ /* And any input keys */
+ ast_string_field_set(iaxs[callno], inkeys, user->inkeys);
+ /* And the permitted authentication methods */
+ iaxs[callno]->authmethods = user->authmethods;
+ iaxs[callno]->adsi = user->adsi;
+ /* If they have callerid, override the given caller id. Always store the ANI */
+ if (!ast_strlen_zero(iaxs[callno]->cid_num) || !ast_strlen_zero(iaxs[callno]->cid_name)) {
+ if (ast_test_flag(user, IAX_HASCALLERID)) {
+ iaxs[callno]->calling_tns = 0;
+ iaxs[callno]->calling_ton = 0;
+ ast_string_field_set(iaxs[callno], cid_num, user->cid_num);
+ ast_string_field_set(iaxs[callno], cid_name, user->cid_name);
+ iaxs[callno]->calling_pres = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
+ }
+ if (ast_strlen_zero(iaxs[callno]->ani))
+ ast_string_field_set(iaxs[callno], ani, user->cid_num);
+ } else {
+ iaxs[callno]->calling_pres = AST_PRES_NUMBER_NOT_AVAILABLE;
+ }
+ if (!ast_strlen_zero(user->accountcode))
+ ast_string_field_set(iaxs[callno], accountcode, user->accountcode);
+ if (!ast_strlen_zero(user->mohinterpret))
+ ast_string_field_set(iaxs[callno], mohinterpret, user->mohinterpret);
+ if (!ast_strlen_zero(user->mohsuggest))
+ ast_string_field_set(iaxs[callno], mohsuggest, user->mohsuggest);
+ if (user->amaflags)
+ iaxs[callno]->amaflags = user->amaflags;
+ if (!ast_strlen_zero(user->language))
+ ast_string_field_set(iaxs[callno], language, user->language);
+ ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+ /* Keep this check last */
+ if (!ast_strlen_zero(user->dbsecret)) {
+ char *family, *key=NULL;
+ char buf[80];
+ family = ast_strdupa(user->dbsecret);
+ key = strchr(family, '/');
+ if (key) {
+ *key = '\0';
+ key++;
+ }
+ if (!key || ast_db_get(family, key, buf, sizeof(buf)))
+ ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", user->dbsecret);
+ else
+ ast_string_field_set(iaxs[callno], secret, buf);
+ } else
+ ast_string_field_set(iaxs[callno], secret, user->secret);
+ res = 0;
+ user = user_unref(user);
+ }
+ ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK);
+ return res;
+}
+
+static int raw_hangup(struct sockaddr_in *sin, unsigned short src, unsigned short dst, int sockfd)
+{
+ struct ast_iax2_full_hdr fh;
+ fh.scallno = htons(src | IAX_FLAG_FULL);
+ fh.dcallno = htons(dst);
+ fh.ts = 0;
+ fh.oseqno = 0;
+ fh.iseqno = 0;
+ fh.type = AST_FRAME_IAX;
+ fh.csub = compress_subclass(IAX_COMMAND_INVAL);
+ if (iaxdebug)
+ iax_showframe(NULL, &fh, 0, sin, 0);
+#if 0
+ if (option_debug)
+#endif
+ ast_debug(1, "Raw Hangup %s:%d, src=%d, dst=%d\n",
+ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), src, dst);
+ return sendto(sockfd, &fh, sizeof(fh), 0, (struct sockaddr *)sin, sizeof(*sin));
+}
+
+static void merge_encryption(struct chan_iax2_pvt *p, unsigned int enc)
+{
+ /* Select exactly one common encryption if there are any */
+ p->encmethods &= enc;
+ if (p->encmethods) {
+ if (p->encmethods & IAX_ENCRYPT_AES128)
+ p->encmethods = IAX_ENCRYPT_AES128;
+ else
+ p->encmethods = 0;
+ }
+}
+
+/*!
+ * \pre iaxsl[call_num] is locked
+ *
+ * \note Since this function calls send_command_final(), the pvt struct for the given
+ * call number may disappear while executing this function.
+ */
+static int authenticate_request(int call_num)
+{
+ struct iax_ie_data ied;
+ int res = -1, authreq_restrict = 0;
+ char challenge[10];
+ struct chan_iax2_pvt *p = iaxs[call_num];
+
+ memset(&ied, 0, sizeof(ied));
+
+ /* If an AUTHREQ restriction is in place, make sure we can send an AUTHREQ back */
+ if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ if (user->curauthreq == user->maxauthreq)
+ authreq_restrict = 1;
+ else
+ user->curauthreq++;
+ user = user_unref(user);
+ }
+ }
+
+ /* If the AUTHREQ limit test failed, send back an error */
+ if (authreq_restrict) {
+ iax_ie_append_str(&ied, IAX_IE_CAUSE, "Unauthenticated call limit reached");
+ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_CALL_REJECTED);
+ send_command_final(p, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1);
+ return 0;
+ }
+
+ iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
+ if (p->authmethods & (IAX_AUTH_MD5 | IAX_AUTH_RSA)) {
+ snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
+ ast_string_field_set(p, challenge, challenge);
+ /* snprintf(p->challenge, sizeof(p->challenge), "%d", (int)ast_random()); */
+ iax_ie_append_str(&ied, IAX_IE_CHALLENGE, p->challenge);
+ }
+ if (p->encmethods)
+ iax_ie_append_short(&ied, IAX_IE_ENCRYPTION, p->encmethods);
+
+ iax_ie_append_str(&ied,IAX_IE_USERNAME, p->username);
+
+ res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREQ, 0, ied.buf, ied.pos, -1);
+
+ if (p->encmethods)
+ ast_set_flag(p, IAX_ENCRYPTED);
+
+ return res;
+}
+
+static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
+{
+ char requeststr[256];
+ char md5secret[256] = "";
+ char secret[256] = "";
+ char rsasecret[256] = "";
+ int res = -1;
+ int x;
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
+ ast_clear_flag(p, IAX_MAXAUTHREQ);
+ }
+ ast_string_field_set(p, host, user->name);
+ user = user_unref(user);
+ }
+
+ if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
+ return res;
+ if (ies->password)
+ ast_copy_string(secret, ies->password, sizeof(secret));
+ if (ies->md5_result)
+ ast_copy_string(md5secret, ies->md5_result, sizeof(md5secret));
+ if (ies->rsa_result)
+ ast_copy_string(rsasecret, ies->rsa_result, sizeof(rsasecret));
+ if ((p->authmethods & IAX_AUTH_RSA) && !ast_strlen_zero(rsasecret) && !ast_strlen_zero(p->inkeys)) {
+ struct ast_key *key;
+ char *keyn;
+ char tmpkey[256];
+ char *stringp=NULL;
+ ast_copy_string(tmpkey, p->inkeys, sizeof(tmpkey));
+ stringp=tmpkey;
+ keyn = strsep(&stringp, ":");
+ while(keyn) {
+ key = ast_key_get(keyn, AST_KEY_PUBLIC);
+ if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
+ res = 0;
+ break;
+ } else if (!key)
+ ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn);
+ keyn = strsep(&stringp, ":");
+ }
+ } else if (p->authmethods & IAX_AUTH_MD5) {
+ struct MD5Context md5;
+ unsigned char digest[16];
+ char *tmppw, *stringp;
+
+ tmppw = ast_strdupa(p->secret);
+ stringp = tmppw;
+ while((tmppw = strsep(&stringp, ";"))) {
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)p->challenge, strlen(p->challenge));
+ MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
+ MD5Final(digest, &md5);
+ /* If they support md5, authenticate with it. */
+ for (x=0;x<16;x++)
+ sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
+ if (!strcasecmp(requeststr, md5secret)) {
+ res = 0;
+ break;
+ }
+ }
+ } else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
+ if (!strcmp(secret, p->secret))
+ res = 0;
+ }
+ return res;
+}
+
+/*! \brief Verify inbound registration */
+static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
+{
+ char requeststr[256] = "";
+ char peer[256] = "";
+ char md5secret[256] = "";
+ char rsasecret[256] = "";
+ char secret[256] = "";
+ struct iax2_peer *p = NULL;
+ struct ast_key *key;
+ char *keyn;
+ int x;
+ int expire = 0;
+ int res = -1;
+
+ ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED);
+ /* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */
+ if (ies->username)
+ ast_copy_string(peer, ies->username, sizeof(peer));
+ if (ies->password)
+ ast_copy_string(secret, ies->password, sizeof(secret));
+ if (ies->md5_result)
+ ast_copy_string(md5secret, ies->md5_result, sizeof(md5secret));
+ if (ies->rsa_result)
+ ast_copy_string(rsasecret, ies->rsa_result, sizeof(rsasecret));
+ if (ies->refresh)
+ expire = ies->refresh;
+
+ if (ast_strlen_zero(peer)) {
+ ast_log(LOG_NOTICE, "Empty registration from %s\n", ast_inet_ntoa(sin->sin_addr));
+ return -1;
+ }
+
+ /* SLD: first call to lookup peer during registration */
+ ast_mutex_unlock(&iaxsl[callno]);
+ p = find_peer(peer, 1);
+ ast_mutex_lock(&iaxsl[callno]);
+ if (!p || !iaxs[callno]) {
+ if (authdebug && !p)
+ ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
+ goto return_unref;
+ }
+
+ if (!ast_test_flag(p, IAX_DYNAMIC)) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
+ goto return_unref;
+ }
+
+ if (!ast_apply_ha(p->ha, sin)) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
+ goto return_unref;
+ }
+ if (!inaddrcmp(&p->addr, sin))
+ ast_set_flag(&iaxs[callno]->state, IAX_STATE_UNCHANGED);
+ ast_string_field_set(iaxs[callno], secret, p->secret);
+ ast_string_field_set(iaxs[callno], inkeys, p->inkeys);
+ /* Check secret against what we have on file */
+ if (!ast_strlen_zero(rsasecret) && (p->authmethods & IAX_AUTH_RSA) && !ast_strlen_zero(iaxs[callno]->challenge)) {
+ if (!ast_strlen_zero(p->inkeys)) {
+ char tmpkeys[256];
+ char *stringp=NULL;
+ ast_copy_string(tmpkeys, p->inkeys, sizeof(tmpkeys));
+ stringp=tmpkeys;
+ keyn = strsep(&stringp, ":");
+ while(keyn) {
+ key = ast_key_get(keyn, AST_KEY_PUBLIC);
+ if (key && !ast_check_signature(key, iaxs[callno]->challenge, rsasecret)) {
+ ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
+ break;
+ } else if (!key)
+ ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn);
+ keyn = strsep(&stringp, ":");
+ }
+ if (!keyn) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys);
+ goto return_unref;
+ }
+ } else {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer);
+ goto return_unref;
+ }
+ } else if (!ast_strlen_zero(md5secret) && (p->authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(iaxs[callno]->challenge)) {
+ struct MD5Context md5;
+ unsigned char digest[16];
+ char *tmppw, *stringp;
+
+ tmppw = ast_strdupa(p->secret);
+ stringp = tmppw;
+ while((tmppw = strsep(&stringp, ";"))) {
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)iaxs[callno]->challenge, strlen(iaxs[callno]->challenge));
+ MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
+ MD5Final(digest, &md5);
+ for (x=0;x<16;x++)
+ sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
+ if (!strcasecmp(requeststr, md5secret))
+ break;
+ }
+ if (tmppw) {
+ ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
+ } else {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Host %s failed MD5 authentication for '%s' (%s != %s)\n", ast_inet_ntoa(sin->sin_addr), p->name, requeststr, md5secret);
+ goto return_unref;
+ }
+ } else if (!ast_strlen_zero(secret) && (p->authmethods & IAX_AUTH_PLAINTEXT)) {
+ /* They've provided a plain text password and we support that */
+ if (strcmp(secret, p->secret)) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
+ goto return_unref;
+ } else
+ ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
+ } else if (!ast_strlen_zero(md5secret) || !ast_strlen_zero(secret)) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Inappropriate authentication received\n");
+ goto return_unref;
+ }
+ ast_string_field_set(iaxs[callno], peer, peer);
+ /* Choose lowest expiry number */
+ if (expire && (expire < iaxs[callno]->expiry))
+ iaxs[callno]->expiry = expire;
+
+ ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
+
+ res = 0;
+
+return_unref:
+ if (p)
+ peer_unref(p);
+
+ return res;
+}
+
+static int authenticate(const char *challenge, const char *secret, const char *keyn, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin, ast_aes_encrypt_key *ecx, ast_aes_decrypt_key *dcx)
+{
+ int res = -1;
+ int x;
+ if (!ast_strlen_zero(keyn)) {
+ if (!(authmethods & IAX_AUTH_RSA)) {
+ if (ast_strlen_zero(secret))
+ ast_log(LOG_NOTICE, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", ast_inet_ntoa(sin->sin_addr));
+ } else if (ast_strlen_zero(challenge)) {
+ ast_log(LOG_NOTICE, "No challenge provided for RSA authentication to %s\n", ast_inet_ntoa(sin->sin_addr));
+ } else {
+ char sig[256];
+ struct ast_key *key;
+ key = ast_key_get(keyn, AST_KEY_PRIVATE);
+ if (!key) {
+ ast_log(LOG_NOTICE, "Unable to find private key '%s'\n", keyn);
+ } else {
+ if (ast_sign(key, (char*)challenge, sig)) {
+ ast_log(LOG_NOTICE, "Unable to sign challenge with key\n");
+ res = -1;
+ } else {
+ iax_ie_append_str(ied, IAX_IE_RSA_RESULT, sig);
+ res = 0;
+ }
+ }
+ }
+ }
+ /* Fall back */
+ if (res && !ast_strlen_zero(secret)) {
+ if ((authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(challenge)) {
+ struct MD5Context md5;
+ unsigned char digest[16];
+ char digres[128];
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
+ MD5Update(&md5, (unsigned char *)secret, strlen(secret));
+ MD5Final(digest, &md5);
+ /* If they support md5, authenticate with it. */
+ for (x=0;x<16;x++)
+ sprintf(digres + (x << 1), "%2.2x", digest[x]); /* safe */
+ if (ecx && dcx)
+ build_enc_keys(digest, ecx, dcx);
+ iax_ie_append_str(ied, IAX_IE_MD5_RESULT, digres);
+ res = 0;
+ } else if (authmethods & IAX_AUTH_PLAINTEXT) {
+ iax_ie_append_str(ied, IAX_IE_PASSWORD, secret);
+ res = 0;
+ } else
+ ast_log(LOG_NOTICE, "No way to send secret to peer '%s' (their methods: %d)\n", ast_inet_ntoa(sin->sin_addr), authmethods);
+ }
+ return res;
+}
+
+/*!
+ * \note This function calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
+ * so do not call this function with a pvt lock held.
+ */
+static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin, struct iax_ies *ies, const char *override, const char *okey)
+{
+ struct iax2_peer *peer = NULL;
+ /* Start pessimistic */
+ int res = -1;
+ int authmethods = 0;
+ struct iax_ie_data ied;
+ uint16_t callno = p->callno;
+
+ memset(&ied, 0, sizeof(ied));
+
+ if (ies->username)
+ ast_string_field_set(p, username, ies->username);
+ if (ies->challenge)
+ ast_string_field_set(p, challenge, ies->challenge);
+ if (ies->authmethods)
+ authmethods = ies->authmethods;
+ if (authmethods & IAX_AUTH_MD5)
+ merge_encryption(p, ies->encmethods);
+ else
+ p->encmethods = 0;
+
+ /* Check for override RSA authentication first */
+ if (!ast_strlen_zero(override) || !ast_strlen_zero(okey)) {
+ /* Normal password authentication */
+ res = authenticate(p->challenge, override, okey, authmethods, &ied, sin, &p->ecx, &p->dcx);
+ } else {
+ struct ao2_iterator i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if ((ast_strlen_zero(p->peer) || !strcmp(p->peer, peer->name))
+ /* No peer specified at our end, or this is the peer */
+ && (ast_strlen_zero(peer->username) || (!strcmp(peer->username, p->username)))
+ /* No username specified in peer rule, or this is the right username */
+ && (!peer->addr.sin_addr.s_addr || ((sin->sin_addr.s_addr & peer->mask.s_addr) == (peer->addr.sin_addr.s_addr & peer->mask.s_addr)))
+ /* No specified host, or this is our host */
+ ) {
+ res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
+ if (!res) {
+ peer_unref(peer);
+ break;
+ }
+ }
+ peer_unref(peer);
+ }
+ if (!peer) {
+ /* We checked our list and didn't find one. It's unlikely, but possible,
+ that we're trying to authenticate *to* a realtime peer */
+ const char *peer_name = ast_strdupa(p->peer);
+ ast_mutex_unlock(&iaxsl[callno]);
+ if ((peer = realtime_peer(peer_name, NULL))) {
+ ast_mutex_lock(&iaxsl[callno]);
+ if (!(p = iaxs[callno])) {
+ peer_unref(peer);
+ return -1;
+ }
+ res = authenticate(p->challenge, peer->secret,peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
+ peer_unref(peer);
+ }
+ if (!peer) {
+ ast_mutex_lock(&iaxsl[callno]);
+ if (!(p = iaxs[callno]))
+ return -1;
+ }
+ }
+ }
+ if (ies->encmethods)
+ ast_set_flag(p, IAX_ENCRYPTED | IAX_KEYPOPULATED);
+ if (!res) {
+ struct ast_datastore *variablestore;
+ struct ast_variable *var, *prev = NULL;
+ AST_LIST_HEAD(, ast_var_t) *varlist;
+ varlist = ast_calloc(1, sizeof(*varlist));
+ variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+ if (variablestore && varlist && p->owner) {
+ variablestore->data = varlist;
+ variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+ AST_LIST_HEAD_INIT(varlist);
+ for (var = ies->vars; var; var = var->next) {
+ struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
+ if (prev)
+ ast_free(prev);
+ prev = var;
+ if (!newvar) {
+ /* Don't abort list traversal, as this would leave ies->vars in an inconsistent state. */
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ } else {
+ AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+ }
+ }
+ if (prev)
+ ast_free(prev);
+ ies->vars = NULL;
+ ast_channel_datastore_add(p->owner, variablestore);
+ } else {
+ if (p->owner)
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ if (variablestore)
+ ast_channel_datastore_free(variablestore);
+ if (varlist)
+ ast_free(varlist);
+ }
+ }
+
+ if (!res)
+ res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1);
+ return res;
+}
+
+static int iax2_do_register(struct iax2_registry *reg);
+
+static void __iax2_do_register_s(const void *data)
+{
+ struct iax2_registry *reg = (struct iax2_registry *)data;
+ reg->expire = -1;
+ iax2_do_register(reg);
+}
+
+static int iax2_do_register_s(const void *data)
+{
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__iax2_do_register_s, data))
+#endif
+ __iax2_do_register_s(data);
+ return 0;
+}
+
+static int try_transfer(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
+{
+ int newcall = 0;
+ char newip[256];
+ struct iax_ie_data ied;
+ struct sockaddr_in new;
+
+
+ memset(&ied, 0, sizeof(ied));
+ if (ies->apparent_addr)
+ bcopy(ies->apparent_addr, &new, sizeof(new));
+ if (ies->callno)
+ newcall = ies->callno;
+ if (!newcall || !new.sin_addr.s_addr || !new.sin_port) {
+ ast_log(LOG_WARNING, "Invalid transfer request\n");
+ return -1;
+ }
+ pvt->transfercallno = newcall;
+ memcpy(&pvt->transfer, &new, sizeof(pvt->transfer));
+ inet_aton(newip, &pvt->transfer.sin_addr);
+ pvt->transfer.sin_family = AF_INET;
+ pvt->transferring = TRANSFER_BEGIN;
+ pvt->transferid = ies->transferid;
+ if (ies->transferid)
+ iax_ie_append_int(&ied, IAX_IE_TRANSFERID, ies->transferid);
+ send_command_transfer(pvt, AST_FRAME_IAX, IAX_COMMAND_TXCNT, 0, ied.buf, ied.pos);
+ return 0;
+}
+
+static int complete_dpreply(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
+{
+ char exten[256] = "";
+ int status = CACHE_FLAG_UNKNOWN, expiry = iaxdefaultdpcache, x, matchmore = 0;
+ struct iax2_dpcache *dp = NULL;
+
+ if (ies->called_number)
+ ast_copy_string(exten, ies->called_number, sizeof(exten));
+
+ if (ies->dpstatus & IAX_DPSTATUS_EXISTS)
+ status = CACHE_FLAG_EXISTS;
+ else if (ies->dpstatus & IAX_DPSTATUS_CANEXIST)
+ status = CACHE_FLAG_CANEXIST;
+ else if (ies->dpstatus & IAX_DPSTATUS_NONEXISTENT)
+ status = CACHE_FLAG_NONEXISTENT;
+
+ if (ies->refresh)
+ expiry = ies->refresh;
+ if (ies->dpstatus & IAX_DPSTATUS_MATCHMORE)
+ matchmore = CACHE_FLAG_MATCHMORE;
+
+ AST_LIST_LOCK(&dpcache);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&dpcache, dp, peer_list) {
+ if (strcmp(dp->exten, exten))
+ continue;
+ AST_LIST_REMOVE_CURRENT(peer_list);
+ dp->callno = 0;
+ dp->expiry.tv_sec = dp->orig.tv_sec + expiry;
+ if (dp->flags & CACHE_FLAG_PENDING) {
+ dp->flags &= ~CACHE_FLAG_PENDING;
+ dp->flags |= status;
+ dp->flags |= matchmore;
+ }
+ /* Wake up waiters */
+ for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+ if (dp->waiters[x] > -1)
+ write(dp->waiters[x], "asdf", 4);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&dpcache);
+
+ return 0;
+}
+
+static int complete_transfer(int callno, struct iax_ies *ies)
+{
+ int peercallno = 0;
+ struct chan_iax2_pvt *pvt = iaxs[callno];
+ struct iax_frame *cur;
+ jb_frame frame;
+
+ if (ies->callno)
+ peercallno = ies->callno;
+
+ if (peercallno < 1) {
+ ast_log(LOG_WARNING, "Invalid transfer request\n");
+ return -1;
+ }
+ memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr));
+ memset(&pvt->transfer, 0, sizeof(pvt->transfer));
+ /* Reset sequence numbers */
+ pvt->oseqno = 0;
+ pvt->rseqno = 0;
+ pvt->iseqno = 0;
+ pvt->aseqno = 0;
+ pvt->peercallno = peercallno;
+ pvt->transferring = TRANSFER_NONE;
+ pvt->svoiceformat = -1;
+ pvt->voiceformat = 0;
+ pvt->svideoformat = -1;
+ pvt->videoformat = 0;
+ pvt->transfercallno = -1;
+ memset(&pvt->rxcore, 0, sizeof(pvt->rxcore));
+ memset(&pvt->offset, 0, sizeof(pvt->offset));
+ /* reset jitterbuffer */
+ while(jb_getall(pvt->jb,&frame) == JB_OK)
+ iax2_frame_free(frame.data);
+ jb_reset(pvt->jb);
+ pvt->lag = 0;
+ pvt->last = 0;
+ pvt->lastsent = 0;
+ pvt->nextpred = 0;
+ pvt->pingtime = DEFAULT_RETRY_TIME;
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_TRAVERSE(&frame_queue, cur, list) {
+ /* We must cancel any packets that would have been transmitted
+ because now we're talking to someone new. It's okay, they
+ were transmitted to someone that didn't care anyway. */
+ if (callno == cur->callno)
+ cur->retries = -1;
+ }
+ AST_LIST_UNLOCK(&frame_queue);
+ return 0;
+}
+
+/*! \brief Acknowledgment received for OUR registration */
+static int iax2_ack_registry(struct iax_ies *ies, struct sockaddr_in *sin, int callno)
+{
+ struct iax2_registry *reg;
+ /* Start pessimistic */
+ char peer[256] = "";
+ char msgstatus[60];
+ int refresh = 60;
+ char ourip[256] = "<Unspecified>";
+ struct sockaddr_in oldus;
+ struct sockaddr_in us;
+ int oldmsgs;
+
+ memset(&us, 0, sizeof(us));
+ if (ies->apparent_addr)
+ bcopy(ies->apparent_addr, &us, sizeof(us));
+ if (ies->username)
+ ast_copy_string(peer, ies->username, sizeof(peer));
+ if (ies->refresh)
+ refresh = ies->refresh;
+ if (ies->calling_number) {
+ /* We don't do anything with it really, but maybe we should */
+ }
+ reg = iaxs[callno]->reg;
+ if (!reg) {
+ ast_log(LOG_WARNING, "Registry acknowledge on unknown registry '%s'\n", peer);
+ return -1;
+ }
+ memcpy(&oldus, &reg->us, sizeof(oldus));
+ oldmsgs = reg->messages;
+ if (inaddrcmp(&reg->addr, sin)) {
+ ast_log(LOG_WARNING, "Received unsolicited registry ack from '%s'\n", ast_inet_ntoa(sin->sin_addr));
+ return -1;
+ }
+ memcpy(&reg->us, &us, sizeof(reg->us));
+ if (ies->msgcount >= 0)
+ reg->messages = ies->msgcount & 0xffff; /* only low 16 bits are used in the transmission of the IE */
+ /* always refresh the registration at the interval requested by the server
+ we are registering to
+ */
+ reg->refresh = refresh;
+ reg->expire = iax2_sched_replace(reg->expire, sched,
+ (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg);
+ if (inaddrcmp(&oldus, &reg->us) || (reg->messages != oldmsgs)) {
+ if (reg->messages > 255)
+ snprintf(msgstatus, sizeof(msgstatus), " with %d new and %d old messages waiting", reg->messages & 0xff, reg->messages >> 8);
+ else if (reg->messages > 1)
+ snprintf(msgstatus, sizeof(msgstatus), " with %d new messages waiting\n", reg->messages);
+ else if (reg->messages > 0)
+ ast_copy_string(msgstatus, " with 1 new message waiting\n", sizeof(msgstatus));
+ else
+ ast_copy_string(msgstatus, " with no messages waiting\n", sizeof(msgstatus));
+ snprintf(ourip, sizeof(ourip), "%s:%d", ast_inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port));
+ ast_verb(3, "Registered IAX2 to '%s', who sees us as %s%s\n", ast_inet_ntoa(sin->sin_addr), ourip, msgstatus);
+ manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: IAX2\r\nDomain: %s\r\nStatus: Registered\r\n", ast_inet_ntoa(sin->sin_addr));
+ }
+ reg->regstate = REG_STATE_REGISTERED;
+ return 0;
+}
+
+static int iax2_append_register(const char *hostname, const char *username,
+ const char *secret, const char *porta)
+{
+ struct iax2_registry *reg;
+
+ if (!(reg = ast_calloc(1, sizeof(*reg))))
+ return -1;
+
+ if (ast_dnsmgr_lookup(hostname, &reg->addr.sin_addr, &reg->dnsmgr) < 0) {
+ ast_free(reg);
+ return -1;
+ }
+
+ ast_copy_string(reg->username, username, sizeof(reg->username));
+
+ if (secret)
+ ast_copy_string(reg->secret, secret, sizeof(reg->secret));
+
+ reg->expire = -1;
+ reg->refresh = IAX_DEFAULT_REG_EXPIRE;
+ reg->addr.sin_family = AF_INET;
+ reg->addr.sin_port = porta ? htons(atoi(porta)) : htons(IAX_DEFAULT_PORTNO);
+
+ AST_LIST_LOCK(&registrations);
+ AST_LIST_INSERT_HEAD(&registrations, reg, entry);
+ AST_LIST_UNLOCK(&registrations);
+
+ return 0;
+}
+
+static int iax2_register(const char *value, int lineno)
+{
+ char copy[256];
+ char *username, *hostname, *secret;
+ char *porta;
+ char *stringp=NULL;
+
+ if (!value)
+ return -1;
+
+ ast_copy_string(copy, value, sizeof(copy));
+ stringp = copy;
+ username = strsep(&stringp, "@");
+ hostname = strsep(&stringp, "@");
+
+ if (!hostname) {
+ ast_log(LOG_WARNING, "Format for registration is user[:secret]@host[:port] at line %d\n", lineno);
+ return -1;
+ }
+
+ stringp = username;
+ username = strsep(&stringp, ":");
+ secret = strsep(&stringp, ":");
+ stringp = hostname;
+ hostname = strsep(&stringp, ":");
+ porta = strsep(&stringp, ":");
+
+ if (porta && !atoi(porta)) {
+ ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
+ return -1;
+ }
+
+ return iax2_append_register(hostname, username, secret, porta);
+}
+
+
+static void register_peer_exten(struct iax2_peer *peer, int onoff)
+{
+ char multi[256];
+ char *stringp, *ext;
+ if (!ast_strlen_zero(regcontext)) {
+ ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
+ stringp = multi;
+ while((ext = strsep(&stringp, "&"))) {
+ if (onoff) {
+ if (!ast_exists_extension(NULL, regcontext, ext, 1, NULL))
+ ast_add_extension(regcontext, 1, ext, 1, NULL, NULL,
+ "Noop", ast_strdup(peer->name), ast_free_ptr, "IAX2");
+ } else
+ ast_context_remove_extension(regcontext, ext, 1, NULL);
+ }
+ }
+}
+static void prune_peers(void);
+
+static void unlink_peer(struct iax2_peer *peer)
+{
+ if (peer->expire > -1) {
+ if (!ast_sched_del(sched, peer->expire)) {
+ peer->expire = -1;
+ peer_unref(peer);
+ }
+ }
+
+ if (peer->pokeexpire > -1) {
+ if (!ast_sched_del(sched, peer->pokeexpire)) {
+ peer->pokeexpire = -1;
+ peer_unref(peer);
+ }
+ }
+
+ ao2_unlink(peers, peer);
+}
+
+static void __expire_registry(const void *data)
+{
+ struct iax2_peer *peer = (struct iax2_peer *) data;
+
+ if (!peer)
+ return;
+
+ peer->expire = -1;
+
+ ast_debug(1, "Expiring registration for peer '%s'\n", peer->name);
+ if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
+ realtime_update_peer(peer->name, &peer->addr, 0);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
+ /* Reset the address */
+ memset(&peer->addr, 0, sizeof(peer->addr));
+ /* Reset expiry value */
+ peer->expiry = min_reg_expire;
+ if (!ast_test_flag(peer, IAX_TEMPONLY))
+ ast_db_del("IAX/Registry", peer->name);
+ register_peer_exten(peer, 0);
+ ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
+ if (iax2_regfunk)
+ iax2_regfunk(peer->name, 0);
+
+ if (ast_test_flag(peer, IAX_RTAUTOCLEAR))
+ unlink_peer(peer);
+
+ peer_unref(peer);
+}
+
+static int expire_registry(const void *data)
+{
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__expire_registry, data))
+#endif
+ __expire_registry(data);
+ return 0;
+}
+
+static int iax2_poke_peer(struct iax2_peer *peer, int heldcall);
+
+static void reg_source_db(struct iax2_peer *p)
+{
+ char data[80];
+ struct in_addr in;
+ char *c, *d;
+ if (!ast_test_flag(p, IAX_TEMPONLY) && (!ast_db_get("IAX/Registry", p->name, data, sizeof(data)))) {
+ c = strchr(data, ':');
+ if (c) {
+ *c = '\0';
+ c++;
+ if (inet_aton(data, &in)) {
+ d = strchr(c, ':');
+ if (d) {
+ *d = '\0';
+ d++;
+ ast_verb(3, "Seeding '%s' at %s:%d for %d\n", p->name,
+ ast_inet_ntoa(in), atoi(c), atoi(d));
+ iax2_poke_peer(p, 0);
+ p->expiry = atoi(d);
+ memset(&p->addr, 0, sizeof(p->addr));
+ p->addr.sin_family = AF_INET;
+ p->addr.sin_addr = in;
+ p->addr.sin_port = htons(atoi(c));
+ if (p->expire > -1) {
+ if (!ast_sched_del(sched, p->expire)) {
+ p->expire = -1;
+ peer_unref(p);
+ }
+ }
+ ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
+ p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p));
+ if (p->expire == -1)
+ peer_unref(p);
+ if (iax2_regfunk)
+ iax2_regfunk(p->name, 1);
+ register_peer_exten(p, 1);
+ }
+
+ }
+ }
+ }
+}
+
+/*!
+ * \pre iaxsl[callno] is locked
+ *
+ * \note Since this function calls send_command_final(), the pvt struct for
+ * the given call number may disappear while executing this function.
+ */
+static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, int fd, unsigned short refresh)
+{
+ /* Called from IAX thread only, with proper iaxsl lock */
+ struct iax_ie_data ied;
+ struct iax2_peer *p;
+ int msgcount;
+ char data[80];
+ int version;
+ const char *peer_name;
+ int res = -1;
+
+ memset(&ied, 0, sizeof(ied));
+
+ peer_name = ast_strdupa(iaxs[callno]->peer);
+
+ /* SLD: Another find_peer call during registration - this time when we are really updating our registration */
+ ast_mutex_unlock(&iaxsl[callno]);
+ if (!(p = find_peer(peer_name, 1))) {
+ ast_mutex_lock(&iaxsl[callno]);
+ ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
+ return -1;
+ }
+ ast_mutex_lock(&iaxsl[callno]);
+ if (!iaxs[callno])
+ goto return_unref;
+
+ if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) {
+ if (sin->sin_addr.s_addr) {
+ time_t nowtime;
+ time(&nowtime);
+ realtime_update_peer(peer_name, sin, nowtime);
+ } else {
+ realtime_update_peer(peer_name, sin, 0);
+ }
+ }
+ if (inaddrcmp(&p->addr, sin)) {
+ if (iax2_regfunk)
+ iax2_regfunk(p->name, 1);
+ /* Stash the IP address from which they registered */
+ memcpy(&p->addr, sin, sizeof(p->addr));
+ snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expiry);
+ if (!ast_test_flag(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) {
+ ast_db_put("IAX/Registry", p->name, data);
+ ast_verb(3, "Registered IAX2 '%s' (%s) at %s:%d\n", p->name,
+ ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Registered\r\n", p->name);
+ register_peer_exten(p, 1);
+ ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
+ } else if (!ast_test_flag(p, IAX_TEMPONLY)) {
+ ast_verb(3, "Unregistered IAX2 '%s' (%s)\n", p->name,
+ ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED");
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name);
+ register_peer_exten(p, 0);
+ ast_db_del("IAX/Registry", p->name);
+ ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
+ }
+ /* Update the host */
+ /* Verify that the host is really there */
+ iax2_poke_peer(p, callno);
+ }
+
+ /* Make sure our call still exists, an INVAL at the right point may make it go away */
+ if (!iaxs[callno]) {
+ res = 0;
+ goto return_unref;
+ }
+
+ /* Store socket fd */
+ p->sockfd = fd;
+ /* Setup the expiry */
+ if (p->expire > -1) {
+ if (!ast_sched_del(sched, p->expire)) {
+ p->expire = -1;
+ peer_unref(p);
+ }
+ }
+ /* treat an unspecified refresh interval as the minimum */
+ if (!refresh)
+ refresh = min_reg_expire;
+ if (refresh > max_reg_expire) {
+ ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
+ p->name, max_reg_expire, refresh);
+ p->expiry = max_reg_expire;
+ } else if (refresh < min_reg_expire) {
+ ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
+ p->name, min_reg_expire, refresh);
+ p->expiry = min_reg_expire;
+ } else {
+ p->expiry = refresh;
+ }
+ if (p->expiry && sin->sin_addr.s_addr) {
+ p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p));
+ if (p->expire == -1)
+ peer_unref(p);
+ }
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, p->name);
+ iax_ie_append_int(&ied, IAX_IE_DATETIME, iax2_datetime(p->zonetag));
+ if (sin->sin_addr.s_addr) {
+ iax_ie_append_short(&ied, IAX_IE_REFRESH, p->expiry);
+ iax_ie_append_addr(&ied, IAX_IE_APPARENT_ADDR, &p->addr);
+ if (!ast_strlen_zero(p->mailbox)) {
+ struct ast_event *event;
+ int new, old;
+ char *mailbox, *context;
+
+ context = mailbox = ast_strdupa(p->mailbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ event = ast_event_get_cached(AST_EVENT_MWI,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_END);
+ if (event) {
+ new = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+ old = ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
+ ast_event_destroy(event);
+ } else /* Fall back on checking the mailbox directly */
+ ast_app_inboxcount(p->mailbox, &new, &old);
+
+ if (new > 255)
+ new = 255;
+ if (old > 255)
+ old = 255;
+ msgcount = (old << 8) | new;
+
+ iax_ie_append_short(&ied, IAX_IE_MSGCOUNT, msgcount);
+ }
+ if (ast_test_flag(p, IAX_HASCALLERID)) {
+ iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, p->cid_num);
+ iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->cid_name);
+ }
+ }
+ version = iax_check_version(devtype);
+ if (version)
+ iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
+
+ res = 0;
+
+return_unref:
+ peer_unref(p);
+
+ return res ? res : send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
+}
+
+static int registry_authrequest(int callno)
+{
+ struct iax_ie_data ied;
+ struct iax2_peer *p;
+ char challenge[10];
+ const char *peer_name;
+ int res = -1;
+
+ peer_name = ast_strdupa(iaxs[callno]->peer);
+
+ /* SLD: third call to find_peer in registration */
+ ast_mutex_unlock(&iaxsl[callno]);
+ p = find_peer(peer_name, 1);
+ ast_mutex_lock(&iaxsl[callno]);
+ if (!iaxs[callno])
+ goto return_unref;
+ if (!p) {
+ ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
+ goto return_unref;
+ }
+
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
+ if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
+ /* Build the challenge */
+ snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
+ ast_string_field_set(iaxs[callno], challenge, challenge);
+ iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
+ }
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name);
+
+ res = 0;
+
+return_unref:
+ peer_unref(p);
+
+ return res ? res : send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
+}
+
+static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin)
+{
+ struct iax2_registry *reg;
+ /* Start pessimistic */
+ struct iax_ie_data ied;
+ char peer[256] = "";
+ char challenge[256] = "";
+ int res;
+ int authmethods = 0;
+ if (ies->authmethods)
+ authmethods = ies->authmethods;
+ if (ies->username)
+ ast_copy_string(peer, ies->username, sizeof(peer));
+ if (ies->challenge)
+ ast_copy_string(challenge, ies->challenge, sizeof(challenge));
+ memset(&ied, 0, sizeof(ied));
+ reg = iaxs[callno]->reg;
+ if (reg) {
+ if (inaddrcmp(&reg->addr, sin)) {
+ ast_log(LOG_WARNING, "Received unsolicited registry authenticate request from '%s'\n", ast_inet_ntoa(sin->sin_addr));
+ return -1;
+ }
+ if (ast_strlen_zero(reg->secret)) {
+ ast_log(LOG_NOTICE, "No secret associated with peer '%s'\n", reg->username);
+ reg->regstate = REG_STATE_NOAUTH;
+ return -1;
+ }
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
+ iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh);
+ if (reg->secret[0] == '[') {
+ char tmpkey[256];
+ ast_copy_string(tmpkey, reg->secret + 1, sizeof(tmpkey));
+ tmpkey[strlen(tmpkey) - 1] = '\0';
+ res = authenticate(challenge, NULL, tmpkey, authmethods, &ied, sin, NULL, NULL);
+ } else
+ res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL, NULL);
+ if (!res) {
+ reg->regstate = REG_STATE_AUTHSENT;
+ return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+ } else
+ return -1;
+ ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer);
+ } else
+ ast_log(LOG_NOTICE, "Can't reregister without a reg\n");
+ return -1;
+}
+
+static void stop_stuff(int callno)
+{
+ iax2_destroy_helper(iaxs[callno]);
+}
+
+static void __auth_reject(const void *nothing)
+{
+ /* Called from IAX thread only, without iaxs lock */
+ int callno = (int)(long)(nothing);
+ struct iax_ie_data ied;
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+ memset(&ied, 0, sizeof(ied));
+ if (iaxs[callno]->authfail == IAX_COMMAND_REGREJ) {
+ iax_ie_append_str(&ied, IAX_IE_CAUSE, "Registration Refused");
+ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_REJECTED);
+ } else if (iaxs[callno]->authfail == IAX_COMMAND_REJECT) {
+ iax_ie_append_str(&ied, IAX_IE_CAUSE, "No authority found");
+ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED);
+ }
+ send_command_final(iaxs[callno], AST_FRAME_IAX, iaxs[callno]->authfail, 0, ied.buf, ied.pos, -1);
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+}
+
+static int auth_reject(const void *data)
+{
+ int callno = (int)(long)(data);
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno])
+ iaxs[callno]->authid = -1;
+ ast_mutex_unlock(&iaxsl[callno]);
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__auth_reject, data))
+#endif
+ __auth_reject(data);
+ return 0;
+}
+
+static int auth_fail(int callno, int failcode)
+{
+ /* Schedule sending the authentication failure in one second, to prevent
+ guessing */
+ if (iaxs[callno]) {
+ iaxs[callno]->authfail = failcode;
+ if (delayreject) {
+ iaxs[callno]->authid = iax2_sched_replace(iaxs[callno]->authid,
+ sched, 1000, auth_reject, (void *)(long)callno);
+ } else
+ auth_reject((void *)(long)callno);
+ }
+ return 0;
+}
+
+static void __auto_hangup(const void *nothing)
+{
+ /* Called from IAX thread only, without iaxs lock */
+ int callno = (int)(long)(nothing);
+ struct iax_ie_data ied;
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_str(&ied, IAX_IE_CAUSE, "Timeout");
+ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_NO_USER_RESPONSE);
+ send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1);
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+}
+
+static int auto_hangup(const void *data)
+{
+ int callno = (int)(long)(data);
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+ iaxs[callno]->autoid = -1;
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__auto_hangup, data))
+#endif
+ __auto_hangup(data);
+ return 0;
+}
+
+static void iax2_dprequest(struct iax2_dpcache *dp, int callno)
+{
+ struct iax_ie_data ied;
+ /* Auto-hangup with 30 seconds of inactivity */
+ iaxs[callno]->autoid = iax2_sched_replace(iaxs[callno]->autoid,
+ sched, 30000, auto_hangup, (void *)(long)callno);
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, dp->exten);
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_DPREQ, 0, ied.buf, ied.pos, -1);
+ dp->flags |= CACHE_FLAG_TRANSMITTED;
+}
+
+static int iax2_vnak(int callno)
+{
+ return send_command_immediate(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_VNAK, 0, NULL, 0, iaxs[callno]->iseqno);
+}
+
+static void vnak_retransmit(int callno, int last)
+{
+ struct iax_frame *f;
+
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_TRAVERSE(&frame_queue, f, list) {
+ /* Send a copy immediately */
+ if ((f->callno == callno) && iaxs[f->callno] &&
+ ((unsigned char ) (f->oseqno - last) < 128) &&
+ (f->retries >= 0)) {
+ send_packet(f);
+ }
+ }
+ AST_LIST_UNLOCK(&frame_queue);
+}
+
+static void __iax2_poke_peer_s(const void *data)
+{
+ struct iax2_peer *peer = (struct iax2_peer *)data;
+ iax2_poke_peer(peer, 0);
+ peer_unref(peer);
+}
+
+static int iax2_poke_peer_s(const void *data)
+{
+ struct iax2_peer *peer = (struct iax2_peer *)data;
+ peer->pokeexpire = -1;
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__iax2_poke_peer_s, data))
+#endif
+ __iax2_poke_peer_s(data);
+ return 0;
+}
+
+static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now)
+{
+ int res = 0;
+ struct iax_frame *fr;
+ struct ast_iax2_meta_hdr *meta;
+ struct ast_iax2_meta_trunk_hdr *mth;
+ int calls = 0;
+
+ /* Point to frame */
+ fr = (struct iax_frame *)tpeer->trunkdata;
+ /* Point to meta data */
+ meta = (struct ast_iax2_meta_hdr *)fr->afdata;
+ mth = (struct ast_iax2_meta_trunk_hdr *)meta->data;
+ if (tpeer->trunkdatalen) {
+ /* We're actually sending a frame, so fill the meta trunk header and meta header */
+ meta->zeros = 0;
+ meta->metacmd = IAX_META_TRUNK;
+ if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS))
+ meta->cmddata = IAX_META_TRUNK_MINI;
+ else
+ meta->cmddata = IAX_META_TRUNK_SUPERMINI;
+ mth->ts = htonl(calc_txpeerstamp(tpeer, trunkfreq, now));
+ /* And the rest of the ast_iax2 header */
+ fr->direction = DIRECTION_OUTGRESS;
+ fr->retrans = -1;
+ fr->transfer = 0;
+ /* Any appropriate call will do */
+ fr->data = fr->afdata;
+ fr->datalen = tpeer->trunkdatalen + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr);
+ res = transmit_trunk(fr, &tpeer->addr, tpeer->sockfd);
+ calls = tpeer->calls;
+#if 0
+ ast_debug(1, "Trunking %d call chunks in %d bytes to %s:%d, ts=%d\n", calls, fr->datalen, ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), ntohl(mth->ts));
+#endif
+ /* Reset transmit trunk side data */
+ tpeer->trunkdatalen = 0;
+ tpeer->calls = 0;
+ }
+ if (res < 0)
+ return res;
+ return calls;
+}
+
+static inline int iax2_trunk_expired(struct iax2_trunk_peer *tpeer, struct timeval *now)
+{
+ /* Drop when trunk is about 5 seconds idle */
+ if (now->tv_sec > tpeer->trunkact.tv_sec + 5)
+ return 1;
+ return 0;
+}
+
+static int timing_read(int *id, int fd, short events, void *cbdata)
+{
+ char buf[1024];
+ int res, processed = 0, totalcalls = 0;
+ struct iax2_trunk_peer *tpeer = NULL, *drop = NULL;
+#ifdef ZT_TIMERACK
+ int x = 1;
+#endif
+ struct timeval now = ast_tvnow();
+ if (iaxtrunkdebug)
+ ast_verbose("Beginning trunk processing. Trunk queue ceiling is %d bytes per host\n", trunkmaxsize);
+ if (events & AST_IO_PRI) {
+#ifdef ZT_TIMERACK
+ /* Great, this is a timing interface, just call the ioctl */
+ if (ioctl(fd, ZT_TIMERACK, &x))
+ ast_log(LOG_WARNING, "Unable to acknowledge zap timer\n");
+ res = 0;
+#endif
+ } else {
+ /* Read and ignore from the pseudo channel for timing */
+ res = read(fd, buf, sizeof(buf));
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Unable to read from timing fd\n");
+ return 1;
+ }
+ }
+ /* For each peer that supports trunking... */
+ AST_LIST_LOCK(&tpeers);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&tpeers, tpeer, list) {
+ processed++;
+ res = 0;
+ ast_mutex_lock(&tpeer->lock);
+ /* We can drop a single tpeer per pass. That makes all this logic
+ substantially easier */
+ if (!drop && iax2_trunk_expired(tpeer, &now)) {
+ /* Take it out of the list, but don't free it yet, because it
+ could be in use */
+ AST_LIST_REMOVE_CURRENT(list);
+ drop = tpeer;
+ } else {
+ res = send_trunk(tpeer, &now);
+ trunk_timed++;
+ if (iaxtrunkdebug)
+ ast_verbose(" - Trunk peer (%s:%d) has %d call chunk%s in transit, %d bytes backloged and has hit a high water mark of %d bytes\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), res, (res != 1) ? "s" : "", tpeer->trunkdatalen, tpeer->trunkdataalloc);
+ }
+ totalcalls += res;
+ res = 0;
+ ast_mutex_unlock(&tpeer->lock);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&tpeers);
+
+ if (drop) {
+ ast_mutex_lock(&drop->lock);
+ /* Once we have this lock, we're sure nobody else is using it or could use it once we release it,
+ because by the time they could get tpeerlock, we've already grabbed it */
+ ast_debug(1, "Dropping unused iax2 trunk peer '%s:%d'\n", ast_inet_ntoa(drop->addr.sin_addr), ntohs(drop->addr.sin_port));
+ ast_free(drop->trunkdata);
+ ast_mutex_unlock(&drop->lock);
+ ast_mutex_destroy(&drop->lock);
+ ast_free(drop);
+
+ }
+
+ if (iaxtrunkdebug)
+ ast_verbose("Ending trunk processing with %d peers and %d call chunks processed\n", processed, totalcalls);
+ iaxtrunkdebug = 0;
+
+ return 1;
+}
+
+struct dpreq_data {
+ int callno;
+ char context[AST_MAX_EXTENSION];
+ char callednum[AST_MAX_EXTENSION];
+ char *callerid;
+};
+
+static void dp_lookup(int callno, const char *context, const char *callednum, const char *callerid, int skiplock)
+{
+ unsigned short dpstatus = 0;
+ struct iax_ie_data ied1;
+ int mm;
+
+ memset(&ied1, 0, sizeof(ied1));
+ mm = ast_matchmore_extension(NULL, context, callednum, 1, callerid);
+ /* Must be started */
+ if (!strcmp(callednum, ast_parking_ext()) || ast_exists_extension(NULL, context, callednum, 1, callerid)) {
+ dpstatus = IAX_DPSTATUS_EXISTS;
+ } else if (ast_canmatch_extension(NULL, context, callednum, 1, callerid)) {
+ dpstatus = IAX_DPSTATUS_CANEXIST;
+ } else {
+ dpstatus = IAX_DPSTATUS_NONEXISTENT;
+ }
+ if (ast_ignore_pattern(context, callednum))
+ dpstatus |= IAX_DPSTATUS_IGNOREPAT;
+ if (mm)
+ dpstatus |= IAX_DPSTATUS_MATCHMORE;
+ if (!skiplock)
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+ iax_ie_append_str(&ied1, IAX_IE_CALLED_NUMBER, callednum);
+ iax_ie_append_short(&ied1, IAX_IE_DPSTATUS, dpstatus);
+ iax_ie_append_short(&ied1, IAX_IE_REFRESH, iaxdefaultdpcache);
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_DPREP, 0, ied1.buf, ied1.pos, -1);
+ }
+ if (!skiplock)
+ ast_mutex_unlock(&iaxsl[callno]);
+}
+
+static void *dp_lookup_thread(void *data)
+{
+ /* Look up for dpreq */
+ struct dpreq_data *dpr = data;
+ dp_lookup(dpr->callno, dpr->context, dpr->callednum, dpr->callerid, 0);
+ if (dpr->callerid)
+ ast_free(dpr->callerid);
+ ast_free(dpr);
+ return NULL;
+}
+
+static void spawn_dp_lookup(int callno, const char *context, const char *callednum, const char *callerid)
+{
+ pthread_t newthread;
+ struct dpreq_data *dpr;
+
+ if (!(dpr = ast_calloc(1, sizeof(*dpr))))
+ return;
+
+ dpr->callno = callno;
+ ast_copy_string(dpr->context, context, sizeof(dpr->context));
+ ast_copy_string(dpr->callednum, callednum, sizeof(dpr->callednum));
+ if (callerid)
+ dpr->callerid = ast_strdup(callerid);
+ if (ast_pthread_create_detached(&newthread, NULL, dp_lookup_thread, dpr)) {
+ ast_log(LOG_WARNING, "Unable to start lookup thread!\n");
+ }
+}
+
+struct iax_dual {
+ struct ast_channel *chan1;
+ struct ast_channel *chan2;
+};
+
+static void *iax_park_thread(void *stuff)
+{
+ struct ast_channel *chan1, *chan2;
+ struct iax_dual *d;
+ struct ast_frame *f;
+ int ext;
+ int res;
+ d = stuff;
+ chan1 = d->chan1;
+ chan2 = d->chan2;
+ ast_free(d);
+ f = ast_read(chan1);
+ if (f)
+ ast_frfree(f);
+ res = ast_park_call(chan1, chan2, 0, &ext);
+ ast_hangup(chan2);
+ ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext);
+ return NULL;
+}
+
+static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2)
+{
+ struct iax_dual *d;
+ struct ast_channel *chan1m, *chan2m;
+ pthread_t th;
+ chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name);
+ chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "IAXPeer/%s",chan2->name);
+ if (chan2m && chan1m) {
+ /* Make formats okay */
+ chan1m->readformat = chan1->readformat;
+ chan1m->writeformat = chan1->writeformat;
+ ast_channel_masquerade(chan1m, chan1);
+ /* Setup the extensions and such */
+ ast_copy_string(chan1m->context, chan1->context, sizeof(chan1m->context));
+ ast_copy_string(chan1m->exten, chan1->exten, sizeof(chan1m->exten));
+ chan1m->priority = chan1->priority;
+
+ /* We make a clone of the peer channel too, so we can play
+ back the announcement */
+ /* Make formats okay */
+ chan2m->readformat = chan2->readformat;
+ chan2m->writeformat = chan2->writeformat;
+ ast_channel_masquerade(chan2m, chan2);
+ /* Setup the extensions and such */
+ ast_copy_string(chan2m->context, chan2->context, sizeof(chan2m->context));
+ ast_copy_string(chan2m->exten, chan2->exten, sizeof(chan2m->exten));
+ chan2m->priority = chan2->priority;
+ if (ast_do_masquerade(chan2m)) {
+ ast_log(LOG_WARNING, "Masquerade failed :(\n");
+ ast_hangup(chan2m);
+ return -1;
+ }
+ } else {
+ if (chan1m)
+ ast_hangup(chan1m);
+ if (chan2m)
+ ast_hangup(chan2m);
+ return -1;
+ }
+ if ((d = ast_calloc(1, sizeof(*d)))) {
+ d->chan1 = chan1m;
+ d->chan2 = chan2m;
+ if (!ast_pthread_create_detached_background(&th, NULL, iax_park_thread, d)) {
+ return 0;
+ }
+ ast_free(d);
+ }
+ return -1;
+}
+
+
+static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force);
+
+static int check_provisioning(struct sockaddr_in *sin, int sockfd, char *si, unsigned int ver)
+{
+ unsigned int ourver;
+ char rsi[80];
+ snprintf(rsi, sizeof(rsi), "si-%s", si);
+ if (iax_provision_version(&ourver, rsi, 1))
+ return 0;
+ ast_debug(1, "Service identifier '%s', we think '%08x', they think '%08x'\n", si, ourver, ver);
+ if (ourver != ver)
+ iax2_provision(sin, sockfd, NULL, rsi, 1);
+ return 0;
+}
+
+static void construct_rr(struct chan_iax2_pvt *pvt, struct iax_ie_data *iep)
+{
+ jb_info stats;
+ jb_getinfo(pvt->jb, &stats);
+
+ memset(iep, 0, sizeof(*iep));
+
+ iax_ie_append_int(iep,IAX_IE_RR_JITTER, stats.jitter);
+ if(stats.frames_in == 0) stats.frames_in = 1;
+ iax_ie_append_int(iep,IAX_IE_RR_LOSS, ((0xff & (stats.losspct/1000)) << 24 | (stats.frames_lost & 0x00ffffff)));
+ iax_ie_append_int(iep,IAX_IE_RR_PKTS, stats.frames_in);
+ iax_ie_append_short(iep,IAX_IE_RR_DELAY, stats.current - stats.min);
+ iax_ie_append_int(iep,IAX_IE_RR_DROPPED, stats.frames_dropped);
+ iax_ie_append_int(iep,IAX_IE_RR_OOO, stats.frames_ooo);
+}
+
+static void save_rr(struct iax_frame *fr, struct iax_ies *ies)
+{
+ iaxs[fr->callno]->remote_rr.jitter = ies->rr_jitter;
+ iaxs[fr->callno]->remote_rr.losspct = ies->rr_loss >> 24;
+ iaxs[fr->callno]->remote_rr.losscnt = ies->rr_loss & 0xffffff;
+ iaxs[fr->callno]->remote_rr.packets = ies->rr_pkts;
+ iaxs[fr->callno]->remote_rr.delay = ies->rr_delay;
+ iaxs[fr->callno]->remote_rr.dropped = ies->rr_dropped;
+ iaxs[fr->callno]->remote_rr.ooo = ies->rr_ooo;
+}
+
+static void save_osptoken(struct iax_frame *fr, struct iax_ies *ies)
+{
+ int i;
+ unsigned int length, offset = 0;
+ char full_osptoken[IAX_MAX_OSPBUFF_SIZE];
+
+ for (i = 0; i < IAX_MAX_OSPBLOCK_NUM; i++) {
+ length = ies->ospblocklength[i];
+ if (length != 0) {
+ if (length > IAX_MAX_OSPBLOCK_SIZE) {
+ /* OSP token block length wrong, clear buffer */
+ offset = 0;
+ break;
+ } else {
+ memcpy(full_osptoken + offset, ies->osptokenblock[i], length);
+ offset += length;
+ }
+ } else {
+ break;
+ }
+ }
+ *(full_osptoken + offset) = '\0';
+ if (strlen(full_osptoken) != offset) {
+ /* OSP token length wrong, clear buffer */
+ *full_osptoken = '\0';
+ }
+
+ ast_string_field_set(iaxs[fr->callno], osptoken, full_osptoken);
+}
+
+static int socket_process(struct iax2_thread *thread);
+
+/*!
+ * \brief Handle any deferred full frames for this thread
+ */
+static void handle_deferred_full_frames(struct iax2_thread *thread)
+{
+ struct iax2_pkt_buf *pkt_buf;
+
+ ast_mutex_lock(&thread->lock);
+
+ while ((pkt_buf = AST_LIST_REMOVE_HEAD(&thread->full_frames, entry))) {
+ ast_mutex_unlock(&thread->lock);
+
+ thread->buf = pkt_buf->buf;
+ thread->buf_len = pkt_buf->len;
+ thread->buf_size = pkt_buf->len + 1;
+
+ socket_process(thread);
+
+ thread->buf = NULL;
+ ast_free(pkt_buf);
+
+ ast_mutex_lock(&thread->lock);
+ }
+
+ ast_mutex_unlock(&thread->lock);
+}
+
+/*!
+ * \brief Queue the last read full frame for processing by a certain thread
+ *
+ * If there are already any full frames queued, they are sorted
+ * by sequence number.
+ */
+static void defer_full_frame(struct iax2_thread *from_here, struct iax2_thread *to_here)
+{
+ struct iax2_pkt_buf *pkt_buf, *cur_pkt_buf;
+ struct ast_iax2_full_hdr *fh, *cur_fh;
+
+ if (!(pkt_buf = ast_calloc(1, sizeof(*pkt_buf) + from_here->buf_len)))
+ return;
+
+ pkt_buf->len = from_here->buf_len;
+ memcpy(pkt_buf->buf, from_here->buf, pkt_buf->len);
+
+ fh = (struct ast_iax2_full_hdr *) pkt_buf->buf;
+ ast_mutex_lock(&to_here->lock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&to_here->full_frames, cur_pkt_buf, entry) {
+ cur_fh = (struct ast_iax2_full_hdr *) cur_pkt_buf->buf;
+ if (fh->oseqno < cur_fh->oseqno) {
+ AST_LIST_INSERT_BEFORE_CURRENT(pkt_buf, entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ if (!cur_pkt_buf)
+ AST_LIST_INSERT_TAIL(&to_here->full_frames, pkt_buf, entry);
+
+ ast_mutex_unlock(&to_here->lock);
+}
+
+static int socket_read(int *id, int fd, short events, void *cbdata)
+{
+ struct iax2_thread *thread;
+ socklen_t len;
+ time_t t;
+ static time_t last_errtime = 0;
+ struct ast_iax2_full_hdr *fh;
+
+ if (!(thread = find_idle_thread())) {
+ time(&t);
+ if (t != last_errtime)
+ ast_log(LOG_NOTICE, "Out of idle IAX2 threads for I/O, pausing!\n");
+ last_errtime = t;
+ usleep(1);
+ return 1;
+ }
+
+ len = sizeof(thread->iosin);
+ thread->iofd = fd;
+ thread->buf_len = recvfrom(fd, thread->readbuf, sizeof(thread->readbuf), 0, (struct sockaddr *) &thread->iosin, &len);
+ thread->buf_size = sizeof(thread->readbuf);
+ thread->buf = thread->readbuf;
+ if (thread->buf_len < 0) {
+ if (errno != ECONNREFUSED && errno != EAGAIN)
+ ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
+ handle_error();
+ thread->iostate = IAX_IOSTATE_IDLE;
+ signal_condition(&thread->lock, &thread->cond);
+ return 1;
+ }
+ if (test_losspct && ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_losspct)) { /* simulate random loss condition */
+ thread->iostate = IAX_IOSTATE_IDLE;
+ signal_condition(&thread->lock, &thread->cond);
+ return 1;
+ }
+
+ /* Determine if this frame is a full frame; if so, and any thread is currently
+ processing a full frame for the same callno from this peer, then drop this
+ frame (and the peer will retransmit it) */
+ fh = (struct ast_iax2_full_hdr *) thread->buf;
+ if (ntohs(fh->scallno) & IAX_FLAG_FULL) {
+ struct iax2_thread *cur = NULL;
+ uint16_t callno = ntohs(fh->scallno) & ~IAX_FLAG_FULL;
+
+ AST_LIST_LOCK(&active_list);
+ AST_LIST_TRAVERSE(&active_list, cur, list) {
+ if ((cur->ffinfo.callno == callno) &&
+ !inaddrcmp(&cur->ffinfo.sin, &thread->iosin))
+ break;
+ }
+ if (cur) {
+ /* we found another thread processing a full frame for this call,
+ so queue it up for processing later. */
+ defer_full_frame(thread, cur);
+ AST_LIST_UNLOCK(&active_list);
+ thread->iostate = IAX_IOSTATE_IDLE;
+ signal_condition(&thread->lock, &thread->cond);
+ return 1;
+ } else {
+ /* this thread is going to process this frame, so mark it */
+ thread->ffinfo.callno = callno;
+ memcpy(&thread->ffinfo.sin, &thread->iosin, sizeof(thread->ffinfo.sin));
+ thread->ffinfo.type = fh->type;
+ thread->ffinfo.csub = fh->csub;
+ }
+ AST_LIST_UNLOCK(&active_list);
+ }
+
+ /* Mark as ready and send on its way */
+ thread->iostate = IAX_IOSTATE_READY;
+#ifdef DEBUG_SCHED_MULTITHREAD
+ ast_copy_string(thread->curfunc, "socket_process", sizeof(thread->curfunc));
+#endif
+ signal_condition(&thread->lock, &thread->cond);
+
+ return 1;
+}
+
+static int socket_process_meta(int packet_len, struct ast_iax2_meta_hdr *meta, struct sockaddr_in *sin, int sockfd,
+ struct iax_frame *fr)
+{
+ unsigned char metatype;
+ struct ast_iax2_meta_trunk_mini *mtm;
+ struct ast_iax2_meta_trunk_hdr *mth;
+ struct ast_iax2_meta_trunk_entry *mte;
+ struct iax2_trunk_peer *tpeer;
+ unsigned int ts;
+ void *ptr;
+ struct timeval rxtrunktime;
+ struct ast_frame f = { 0, };
+
+ if (packet_len < sizeof(*meta)) {
+ ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a meta frame but is too short\n",
+ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+ return 1;
+ }
+
+ if (meta->metacmd != IAX_META_TRUNK)
+ return 1;
+
+ if (packet_len < (sizeof(*meta) + sizeof(*mth))) {
+ ast_log(LOG_WARNING, "midget meta trunk packet received (%d of %d min)\n", packet_len,
+ (int) (sizeof(*meta) + sizeof(*mth)));
+ return 1;
+ }
+ mth = (struct ast_iax2_meta_trunk_hdr *)(meta->data);
+ ts = ntohl(mth->ts);
+ metatype = meta->cmddata;
+ packet_len -= (sizeof(*meta) + sizeof(*mth));
+ ptr = mth->data;
+ tpeer = find_tpeer(sin, sockfd);
+ if (!tpeer) {
+ ast_log(LOG_WARNING, "Unable to accept trunked packet from '%s:%d': No matching peer\n",
+ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+ return 1;
+ }
+ tpeer->trunkact = ast_tvnow();
+ if (!ts || ast_tvzero(tpeer->rxtrunktime))
+ tpeer->rxtrunktime = tpeer->trunkact;
+ rxtrunktime = tpeer->rxtrunktime;
+ ast_mutex_unlock(&tpeer->lock);
+ while (packet_len >= sizeof(*mte)) {
+ /* Process channels */
+ unsigned short callno, trunked_ts, len;
+
+ if (metatype == IAX_META_TRUNK_MINI) {
+ mtm = (struct ast_iax2_meta_trunk_mini *) ptr;
+ ptr += sizeof(*mtm);
+ packet_len -= sizeof(*mtm);
+ len = ntohs(mtm->len);
+ callno = ntohs(mtm->mini.callno);
+ trunked_ts = ntohs(mtm->mini.ts);
+ } else if (metatype == IAX_META_TRUNK_SUPERMINI) {
+ mte = (struct ast_iax2_meta_trunk_entry *)ptr;
+ ptr += sizeof(*mte);
+ packet_len -= sizeof(*mte);
+ len = ntohs(mte->len);
+ callno = ntohs(mte->callno);
+ trunked_ts = 0;
+ } else {
+ ast_log(LOG_WARNING, "Unknown meta trunk cmd from '%s:%d': dropping\n", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+ break;
+ }
+ /* Stop if we don't have enough data */
+ if (len > packet_len)
+ break;
+ fr->callno = find_callno(callno & ~IAX_FLAG_FULL, 0, sin, NEW_PREVENT, sockfd);
+ if (!fr->callno)
+ continue;
+
+ ast_mutex_lock(&iaxsl[fr->callno]);
+
+ /* If it's a valid call, deliver the contents. If not, we
+ drop it, since we don't have a scallno to use for an INVAL */
+ /* Process as a mini frame */
+ memset(&f, 0, sizeof(f));
+ f.frametype = AST_FRAME_VOICE;
+ if (!iaxs[fr->callno]) {
+ /* drop it */
+ } else if (iaxs[fr->callno]->voiceformat == 0) {
+ ast_log(LOG_WARNING, "Received trunked frame before first full voice frame\n ");
+ iax2_vnak(fr->callno);
+ } else {
+ f.subclass = iaxs[fr->callno]->voiceformat;
+ f.datalen = len;
+ if (f.datalen >= 0) {
+ if (f.datalen)
+ f.data = ptr;
+ else
+ f.data = NULL;
+ if (trunked_ts)
+ fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | (trunked_ts & 0xffff);
+ else
+ fr->ts = fix_peerts(&rxtrunktime, fr->callno, ts);
+ /* Don't pass any packets until we're started */
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
+ struct iax_frame *duped_fr;
+
+ /* Common things */
+ f.src = "IAX2";
+ f.mallocd = 0;
+ f.offset = 0;
+ if (f.datalen && (f.frametype == AST_FRAME_VOICE))
+ f.samples = ast_codec_get_samples(&f);
+ else
+ f.samples = 0;
+ fr->outoforder = 0;
+ iax_frame_wrap(fr, &f);
+ duped_fr = iaxfrdup2(fr);
+ if (duped_fr)
+ schedule_delivery(duped_fr, 1, 1, &fr->ts);
+ if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts)
+ iaxs[fr->callno]->last = fr->ts;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Datalen < 0?\n");
+ }
+ }
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ ptr += len;
+ packet_len -= len;
+ }
+
+ return 1;
+}
+
+static int acf_iaxvar_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_datastore *variablestore = ast_channel_datastore_find(chan, &iax2_variable_datastore_info, NULL);
+ AST_LIST_HEAD(, ast_var_t) *varlist;
+ struct ast_var_t *var;
+
+ if (!variablestore) {
+ *buf = '\0';
+ return 0;
+ }
+ varlist = variablestore->data;
+
+ AST_LIST_LOCK(varlist);
+ AST_LIST_TRAVERSE(varlist, var, entries) {
+ if (strcmp(var->name, data) == 0) {
+ ast_copy_string(buf, var->value, len);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(varlist);
+ return 0;
+}
+
+static int acf_iaxvar_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ struct ast_datastore *variablestore = ast_channel_datastore_find(chan, &iax2_variable_datastore_info, NULL);
+ AST_LIST_HEAD(, ast_var_t) *varlist;
+ struct ast_var_t *var;
+
+ if (!variablestore) {
+ variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+ if (!variablestore) {
+ ast_log(LOG_ERROR, "Memory allocation error\n");
+ return -1;
+ }
+ varlist = ast_calloc(1, sizeof(*varlist));
+ if (!varlist) {
+ ast_log(LOG_ERROR, "Unable to assign new variable '%s'\n", data);
+ return -1;
+ }
+
+ AST_LIST_HEAD_INIT(varlist);
+ variablestore->data = varlist;
+ variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+ ast_channel_datastore_add(chan, variablestore);
+ } else
+ varlist = variablestore->data;
+
+ AST_LIST_LOCK(varlist);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(varlist, var, entries) {
+ if (strcmp(var->name, data) == 0) {
+ AST_LIST_REMOVE_CURRENT(entries);
+ ast_var_delete(var);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ var = ast_var_assign(data, value);
+ if (var)
+ AST_LIST_INSERT_TAIL(varlist, var, entries);
+ else
+ ast_log(LOG_ERROR, "Unable to assign new variable '%s'\n", data);
+ AST_LIST_UNLOCK(varlist);
+ return 0;
+}
+
+static struct ast_custom_function iaxvar_function = {
+ .name = "IAXVAR",
+ .synopsis = "Sets or retrieves a remote variable",
+ .syntax = "IAXVAR(<varname>)",
+ .read = acf_iaxvar_read,
+ .write = acf_iaxvar_write,
+};
+
+static int socket_process(struct iax2_thread *thread)
+{
+ struct sockaddr_in sin;
+ int res;
+ int updatehistory=1;
+ int new = NEW_PREVENT;
+ int dcallno = 0;
+ struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)thread->buf;
+ struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)thread->buf;
+ struct ast_iax2_meta_hdr *meta = (struct ast_iax2_meta_hdr *)thread->buf;
+ struct ast_iax2_video_hdr *vh = (struct ast_iax2_video_hdr *)thread->buf;
+ struct iax_frame *fr;
+ struct iax_frame *cur;
+ struct ast_frame f = { 0, };
+ struct ast_channel *c = NULL;
+ struct iax2_dpcache *dp;
+ struct iax2_peer *peer;
+ struct iax_ies ies;
+ struct iax_ie_data ied0, ied1;
+ int format;
+ int fd;
+ int exists;
+ int minivid = 0;
+ char empty[32]=""; /* Safety measure */
+ struct iax_frame *duped_fr;
+ char host_pref_buf[128];
+ char caller_pref_buf[128];
+ struct ast_codec_pref pref;
+ char *using_prefs = "mine";
+
+ /* allocate an iax_frame with 4096 bytes of data buffer */
+ fr = alloca(sizeof(*fr) + 4096);
+ fr->callno = 0;
+ fr->afdatalen = 4096; /* From alloca() above */
+
+ /* Copy frequently used parameters to the stack */
+ res = thread->buf_len;
+ fd = thread->iofd;
+ memcpy(&sin, &thread->iosin, sizeof(sin));
+
+ if (res < sizeof(*mh)) {
+ ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int) sizeof(*mh));
+ return 1;
+ }
+ if ((vh->zeros == 0) && (ntohs(vh->callno) & 0x8000)) {
+ if (res < sizeof(*vh)) {
+ ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a video frame but is too short\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ return 1;
+ }
+
+ /* This is a video frame, get call number */
+ fr->callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, fd);
+ minivid = 1;
+ } else if ((meta->zeros == 0) && !(ntohs(meta->metacmd) & 0x8000))
+ return socket_process_meta(res, meta, &sin, fd, fr);
+
+#ifdef DEBUG_SUPPORT
+ if (iaxdebug && (res >= sizeof(*fh)))
+ iax_showframe(NULL, fh, 1, &sin, res - sizeof(*fh));
+#endif
+ if (ntohs(mh->callno) & IAX_FLAG_FULL) {
+ if (res < sizeof(*fh)) {
+ ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a full frame but is too short\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ return 1;
+ }
+
+ /* Get the destination call number */
+ dcallno = ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS;
+ /* Retrieve the type and subclass */
+ f.frametype = fh->type;
+ if (f.frametype == AST_FRAME_VIDEO) {
+ f.subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1);
+ } else {
+ f.subclass = uncompress_subclass(fh->csub);
+ }
+ if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ) ||
+ (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL) ||
+ (f.subclass == IAX_COMMAND_REGREL)))
+ new = NEW_ALLOW;
+ } else {
+ /* Don't know anything about it yet */
+ f.frametype = AST_FRAME_NULL;
+ f.subclass = 0;
+ }
+
+ if (!fr->callno)
+ fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd);
+
+ if (fr->callno > 0)
+ ast_mutex_lock(&iaxsl[fr->callno]);
+
+ if (!fr->callno || !iaxs[fr->callno]) {
+ /* A call arrived for a nonexistent destination. Unless it's an "inval"
+ frame, reply with an inval */
+ if (ntohs(mh->callno) & IAX_FLAG_FULL) {
+ /* We can only raw hangup control frames */
+ if (((f.subclass != IAX_COMMAND_INVAL) &&
+ (f.subclass != IAX_COMMAND_TXCNT) &&
+ (f.subclass != IAX_COMMAND_TXACC) &&
+ (f.subclass != IAX_COMMAND_FWDOWNL))||
+ (f.frametype != AST_FRAME_IAX))
+ raw_hangup(&sin, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, ntohs(mh->callno) & ~IAX_FLAG_FULL,
+ fd);
+ }
+ if (fr->callno > 0)
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) {
+ if (decrypt_frame(fr->callno, fh, &f, &res)) {
+ ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n");
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+#ifdef DEBUG_SUPPORT
+ else if (iaxdebug)
+ iax_showframe(NULL, fh, 3, &sin, res - sizeof(*fh));
+#endif
+ }
+
+ /* count this frame */
+ iaxs[fr->callno]->frames_received++;
+
+ if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid &&
+ f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */
+ f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */
+ iaxs[fr->callno]->peercallno = (unsigned short)(ntohs(mh->callno) & ~IAX_FLAG_FULL);
+ }
+ if (ntohs(mh->callno) & IAX_FLAG_FULL) {
+ if (iaxdebug)
+ ast_debug(1, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass);
+ /* Check if it's out of order (and not an ACK or INVAL) */
+ fr->oseqno = fh->oseqno;
+ fr->iseqno = fh->iseqno;
+ fr->ts = ntohl(fh->ts);
+#ifdef IAXTESTS
+ if (test_resync) {
+ ast_debug(1, "Simulating frame ts resync, was %u now %u\n", fr->ts, fr->ts + test_resync);
+ fr->ts += test_resync;
+ }
+#endif /* IAXTESTS */
+#if 0
+ if ( (ntohs(fh->dcallno) & IAX_FLAG_RETRANS) ||
+ ( (f.frametype != AST_FRAME_VOICE) && ! (f.frametype == AST_FRAME_IAX &&
+ (f.subclass == IAX_COMMAND_NEW ||
+ f.subclass == IAX_COMMAND_AUTHREQ ||
+ f.subclass == IAX_COMMAND_ACCEPT ||
+ f.subclass == IAX_COMMAND_REJECT)) ) )
+#endif
+ if ((ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || (f.frametype != AST_FRAME_VOICE))
+ updatehistory = 0;
+ if ((iaxs[fr->callno]->iseqno != fr->oseqno) &&
+ (iaxs[fr->callno]->iseqno ||
+ ((f.subclass != IAX_COMMAND_TXCNT) &&
+ (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */
+ (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */
+ (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */
+ (f.subclass != IAX_COMMAND_TXACC)) ||
+ (f.frametype != AST_FRAME_IAX))) {
+ if (
+ ((f.subclass != IAX_COMMAND_ACK) &&
+ (f.subclass != IAX_COMMAND_INVAL) &&
+ (f.subclass != IAX_COMMAND_TXCNT) &&
+ (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */
+ (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */
+ (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */
+ (f.subclass != IAX_COMMAND_TXACC) &&
+ (f.subclass != IAX_COMMAND_VNAK)) ||
+ (f.frametype != AST_FRAME_IAX)) {
+ /* If it's not an ACK packet, it's out of order. */
+ ast_debug(1, "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n",
+ iaxs[fr->callno]->iseqno, fr->oseqno, f.frametype, f.subclass);
+ /* Check to see if we need to request retransmission,
+ * and take sequence number wraparound into account */
+ if ((unsigned char) (iaxs[fr->callno]->iseqno - fr->oseqno) < 128) {
+ /* If we've already seen it, ack it XXX There's a border condition here XXX */
+ if ((f.frametype != AST_FRAME_IAX) ||
+ ((f.subclass != IAX_COMMAND_ACK) && (f.subclass != IAX_COMMAND_INVAL))) {
+ ast_debug(1, "Acking anyway\n");
+ /* XXX Maybe we should handle its ack to us, but then again, it's probably outdated anyway, and if
+ we have anything to send, we'll retransmit and get an ACK back anyway XXX */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ }
+ } else {
+ /* Send a VNAK requesting retransmission */
+ iax2_vnak(fr->callno);
+ }
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ } else {
+ /* Increment unless it's an ACK or VNAK */
+ if (((f.subclass != IAX_COMMAND_ACK) &&
+ (f.subclass != IAX_COMMAND_INVAL) &&
+ (f.subclass != IAX_COMMAND_TXCNT) &&
+ (f.subclass != IAX_COMMAND_TXACC) &&
+ (f.subclass != IAX_COMMAND_VNAK)) ||
+ (f.frametype != AST_FRAME_IAX))
+ iaxs[fr->callno]->iseqno++;
+ }
+ /* A full frame */
+ if (res < sizeof(*fh)) {
+ ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int) sizeof(*fh));
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ /* Ensure text frames are NULL-terminated */
+ if (f.frametype == AST_FRAME_TEXT && thread->buf[res - 1] != '\0') {
+ if (res < thread->buf_size)
+ thread->buf[res++] = '\0';
+ else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */
+ thread->buf[res - 1] = '\0';
+ }
+ f.datalen = res - sizeof(*fh);
+
+ /* Handle implicit ACKing unless this is an INVAL, and only if this is
+ from the real peer, not the transfer peer */
+ if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) &&
+ ((f.subclass != IAX_COMMAND_INVAL) ||
+ (f.frametype != AST_FRAME_IAX))) {
+ unsigned char x;
+ int call_to_destroy;
+ /* XXX This code is not very efficient. Surely there is a better way which still
+ properly handles boundary conditions? XXX */
+ /* First we have to qualify that the ACKed value is within our window */
+ for (x=iaxs[fr->callno]->rseqno; x != iaxs[fr->callno]->oseqno; x++)
+ if (fr->iseqno == x)
+ break;
+ if ((x != iaxs[fr->callno]->oseqno) || (iaxs[fr->callno]->oseqno == fr->iseqno)) {
+ /* The acknowledgement is within our window. Time to acknowledge everything
+ that it says to */
+ for (x=iaxs[fr->callno]->rseqno; x != fr->iseqno; x++) {
+ /* Ack the packet with the given timestamp */
+ if (iaxdebug)
+ ast_debug(1, "Cancelling transmission of packet %d\n", x);
+ call_to_destroy = 0;
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_TRAVERSE(&frame_queue, cur, list) {
+ /* If it's our call, and our timestamp, mark -1 retries */
+ if ((fr->callno == cur->callno) && (x == cur->oseqno)) {
+ cur->retries = -1;
+ /* Destroy call if this is the end */
+ if (cur->final)
+ call_to_destroy = fr->callno;
+ }
+ }
+ AST_LIST_UNLOCK(&frame_queue);
+ if (call_to_destroy) {
+ if (iaxdebug)
+ ast_debug(1, "Really destroying %d, having been acked on final message\n", call_to_destroy);
+ iax2_destroy(call_to_destroy);
+ }
+ }
+ /* Note how much we've received acknowledgement for */
+ if (iaxs[fr->callno])
+ iaxs[fr->callno]->rseqno = fr->iseqno;
+ else {
+ /* Stop processing now */
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ } else {
+ ast_debug(1, "Received iseqno %d not within window %d->%d\n", fr->iseqno, iaxs[fr->callno]->rseqno, iaxs[fr->callno]->oseqno);
+ }
+ }
+ if (inaddrcmp(&sin, &iaxs[fr->callno]->addr) &&
+ ((f.frametype != AST_FRAME_IAX) ||
+ ((f.subclass != IAX_COMMAND_TXACC) &&
+ (f.subclass != IAX_COMMAND_TXCNT)))) {
+ /* Only messages we accept from a transfer host are TXACC and TXCNT */
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+
+ if (f.datalen) {
+ if (f.frametype == AST_FRAME_IAX) {
+ if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) {
+ ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ f.data = NULL;
+ f.datalen = 0;
+ } else
+ f.data = thread->buf + sizeof(*fh);
+ } else {
+ if (f.frametype == AST_FRAME_IAX)
+ f.data = NULL;
+ else
+ f.data = empty;
+ memset(&ies, 0, sizeof(ies));
+ }
+
+ /* when we receive the first full frame for a new incoming channel,
+ it is safe to start the PBX on the channel because we have now
+ completed a 3-way handshake with the peer */
+ if ((f.frametype == AST_FRAME_VOICE) ||
+ (f.frametype == AST_FRAME_VIDEO) ||
+ (f.frametype == AST_FRAME_IAX)) {
+ if (ast_test_flag(iaxs[fr->callno], IAX_DELAYPBXSTART)) {
+ ast_clear_flag(iaxs[fr->callno], IAX_DELAYPBXSTART);
+ if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat)) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ } else if (ies.vars) {
+ struct ast_datastore *variablestore;
+ struct ast_variable *var, *prev = NULL;
+ AST_LIST_HEAD(, ast_var_t) *varlist;
+ varlist = ast_calloc(1, sizeof(*varlist));
+ variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+ if (variablestore && varlist) {
+ variablestore->data = varlist;
+ variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+ AST_LIST_HEAD_INIT(varlist);
+ for (var = ies.vars; var; var = var->next) {
+ struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
+ if (prev)
+ ast_free(prev);
+ prev = var;
+ if (!newvar) {
+ /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ } else {
+ AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+ }
+ }
+ if (prev)
+ ast_free(prev);
+ ies.vars = NULL;
+ ast_channel_datastore_add(c, variablestore);
+ } else {
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ if (variablestore)
+ ast_channel_datastore_free(variablestore);
+ if (varlist)
+ ast_free(varlist);
+ }
+ }
+ }
+ }
+
+ if (f.frametype == AST_FRAME_VOICE) {
+ if (f.subclass != iaxs[fr->callno]->voiceformat) {
+ iaxs[fr->callno]->voiceformat = f.subclass;
+ ast_debug(1, "Ooh, voice format changed to %d\n", f.subclass);
+ if (iaxs[fr->callno]->owner) {
+ int orignative;
+retryowner:
+ if (ast_channel_trylock(iaxs[fr->callno]->owner)) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ usleep(1);
+ ast_mutex_lock(&iaxsl[fr->callno]);
+ if (iaxs[fr->callno] && iaxs[fr->callno]->owner) goto retryowner;
+ }
+ if (iaxs[fr->callno]) {
+ if (iaxs[fr->callno]->owner) {
+ orignative = iaxs[fr->callno]->owner->nativeformats;
+ iaxs[fr->callno]->owner->nativeformats = f.subclass;
+ if (iaxs[fr->callno]->owner->readformat)
+ ast_set_read_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->readformat);
+ iaxs[fr->callno]->owner->nativeformats = orignative;
+ ast_channel_unlock(iaxs[fr->callno]->owner);
+ }
+ } else {
+ ast_debug(1, "Neat, somebody took away the channel at a magical time but i found it!\n");
+ /* Free remote variables (if any) */
+ if (ies.vars)
+ ast_variables_destroy(ies.vars);
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ }
+ }
+ }
+ if (f.frametype == AST_FRAME_VIDEO) {
+ if (f.subclass != iaxs[fr->callno]->videoformat) {
+ ast_debug(1, "Ooh, video format changed to %d\n", f.subclass & ~0x1);
+ iaxs[fr->callno]->videoformat = f.subclass & ~0x1;
+ }
+ }
+ if (f.frametype == AST_FRAME_IAX) {
+ if (iaxs[fr->callno]->initid > -1) {
+ /* Don't auto congest anymore since we've gotten something usefulb ack */
+ ast_sched_del(sched, iaxs[fr->callno]->initid);
+ iaxs[fr->callno]->initid = -1;
+ }
+ /* Handle the IAX pseudo frame itself */
+ if (iaxdebug)
+ ast_debug(1, "IAX subclass %d received\n", f.subclass);
+
+ /* Update last ts unless the frame's timestamp originated with us. */
+ if (iaxs[fr->callno]->last < fr->ts &&
+ f.subclass != IAX_COMMAND_ACK &&
+ f.subclass != IAX_COMMAND_PONG &&
+ f.subclass != IAX_COMMAND_LAGRP) {
+ iaxs[fr->callno]->last = fr->ts;
+ if (iaxdebug)
+ ast_debug(1, "For call=%d, set last=%d\n", fr->callno, fr->ts);
+ }
+
+ switch(f.subclass) {
+ case IAX_COMMAND_ACK:
+ /* Do nothing */
+ break;
+ case IAX_COMMAND_QUELCH:
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
+ /* Generate Manager Hold event, if necessary*/
+ if (iaxs[fr->callno]->owner) {
+ manager_event(EVENT_FLAG_CALL, "Hold",
+ "Status: On\r\n"
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n",
+ iaxs[fr->callno]->owner->name,
+ iaxs[fr->callno]->owner->uniqueid);
+ }
+
+ ast_set_flag(iaxs[fr->callno], IAX_QUELCH);
+ if (ies.musiconhold) {
+ if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) {
+ const char *mohsuggest = iaxs[fr->callno]->mohsuggest;
+ iax2_queue_control_data(fr->callno, AST_CONTROL_HOLD,
+ S_OR(mohsuggest, NULL),
+ !ast_strlen_zero(mohsuggest) ? strlen(mohsuggest) + 1 : 0);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ }
+ }
+ }
+ break;
+ case IAX_COMMAND_UNQUELCH:
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
+ /* Generate Manager Unhold event, if necessary*/
+ if (iaxs[fr->callno]->owner && ast_test_flag(iaxs[fr->callno], IAX_QUELCH)) {
+ manager_event(EVENT_FLAG_CALL, "Hold",
+ "Status: Off\r\n"
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n",
+ iaxs[fr->callno]->owner->name,
+ iaxs[fr->callno]->owner->uniqueid);
+ }
+
+ ast_clear_flag(iaxs[fr->callno], IAX_QUELCH);
+ if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) {
+ iax2_queue_control_data(fr->callno, AST_CONTROL_UNHOLD, NULL, 0);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ }
+ }
+ break;
+ case IAX_COMMAND_TXACC:
+ if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) {
+ /* Ack the packet with the given timestamp */
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_TRAVERSE(&frame_queue, cur, list) {
+ /* Cancel any outstanding txcnt's */
+ if ((fr->callno == cur->callno) && (cur->transfer))
+ cur->retries = -1;
+ }
+ AST_LIST_UNLOCK(&frame_queue);
+ memset(&ied1, 0, sizeof(ied1));
+ iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->callno);
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1);
+ iaxs[fr->callno]->transferring = TRANSFER_READY;
+ }
+ break;
+ case IAX_COMMAND_NEW:
+ /* Ignore if it's already up */
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD))
+ break;
+ if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ check_provisioning(&sin, fd, ies.serviceident, ies.provver);
+ ast_mutex_lock(&iaxsl[fr->callno]);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ }
+ /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */
+ if (ast_test_flag(iaxs[fr->callno], IAX_TRUNK)) {
+ int new_callno;
+ if ((new_callno = make_trunk(fr->callno, 1)) != -1)
+ fr->callno = new_callno;
+ }
+ /* For security, always ack immediately */
+ if (delayreject)
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ if (check_access(fr->callno, &sin, &ies)) {
+ /* They're not allowed on */
+ auth_fail(fr->callno, IAX_COMMAND_REJECT);
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
+ break;
+ }
+ if (strcasecmp(iaxs[fr->callno]->exten, "TBD")) {
+ const char *context, *exten, *cid_num;
+
+ context = ast_strdupa(iaxs[fr->callno]->context);
+ exten = ast_strdupa(iaxs[fr->callno]->exten);
+ cid_num = ast_strdupa(iaxs[fr->callno]->cid_num);
+
+ /* This might re-enter the IAX code and need the lock */
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ exists = ast_exists_extension(NULL, context, exten, 1, cid_num);
+ ast_mutex_lock(&iaxsl[fr->callno]);
+
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ } else
+ exists = 0;
+ /* Get OSP token if it does exist */
+ save_osptoken(fr, &ies);
+ if (ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) {
+ if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) {
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
+ } else {
+ /* Select an appropriate format */
+
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
+ using_prefs = "reqonly";
+ } else {
+ using_prefs = "disabled";
+ }
+ format = iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability;
+ memset(&pref, 0, sizeof(pref));
+ strcpy(caller_pref_buf, "disabled");
+ strcpy(host_pref_buf, "disabled");
+ } else {
+ using_prefs = "mine";
+ /* If the information elements are in here... use them */
+ if (ies.codec_prefs)
+ ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0);
+ if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
+ /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
+ if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
+ pref = iaxs[fr->callno]->rprefs;
+ using_prefs = "caller";
+ } else {
+ pref = iaxs[fr->callno]->prefs;
+ }
+ } else
+ pref = iaxs[fr->callno]->prefs;
+
+ format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0);
+ ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1);
+ ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
+ }
+ if (!format) {
+ if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
+ format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability;
+ if (!format) {
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if (authdebug) {
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
+ else
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
+ }
+ } else {
+ /* Pick one... */
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
+ if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability))
+ format = 0;
+ } else {
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
+ using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
+ memset(&pref, 0, sizeof(pref));
+ format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ strcpy(caller_pref_buf,"disabled");
+ strcpy(host_pref_buf,"disabled");
+ } else {
+ using_prefs = "mine";
+ if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
+ /* Do the opposite of what we tried above. */
+ if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
+ pref = iaxs[fr->callno]->prefs;
+ } else {
+ pref = iaxs[fr->callno]->rprefs;
+ using_prefs = "caller";
+ }
+ format = ast_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, 1);
+
+ } else /* if no codec_prefs IE do it the old way */
+ format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ }
+ }
+
+ if (!format) {
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
+ ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE);
+ break;
+ }
+ }
+ }
+ if (format) {
+ /* No authentication required, let them in */
+ memset(&ied1, 0, sizeof(ied1));
+ iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
+ if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
+ ast_verb(3, "Accepting UNAUTHENTICATED call from %s:\n"
+ "%srequested format = %s,\n"
+ "%srequested prefs = %s,\n"
+ "%sactual format = %s,\n"
+ "%shost prefs = %s,\n"
+ "%spriority = %s\n",
+ ast_inet_ntoa(sin.sin_addr),
+ VERBOSE_PREFIX_4,
+ ast_getformatname(iaxs[fr->callno]->peerformat),
+ VERBOSE_PREFIX_4,
+ caller_pref_buf,
+ VERBOSE_PREFIX_4,
+ ast_getformatname(format),
+ VERBOSE_PREFIX_4,
+ host_pref_buf,
+ VERBOSE_PREFIX_4,
+ using_prefs);
+
+ iaxs[fr->callno]->chosenformat = format;
+ ast_set_flag(iaxs[fr->callno], IAX_DELAYPBXSTART);
+ } else {
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
+ /* If this is a TBD call, we're ready but now what... */
+ ast_verb(3, "Accepted unauthenticated TBD call from %s\n", ast_inet_ntoa(sin.sin_addr));
+ }
+ }
+ }
+ break;
+ }
+ if (iaxs[fr->callno]->authmethods & IAX_AUTH_MD5)
+ merge_encryption(iaxs[fr->callno],ies.encmethods);
+ else
+ iaxs[fr->callno]->encmethods = 0;
+ if (!authenticate_request(fr->callno) && iaxs[fr->callno])
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ break;
+ case IAX_COMMAND_DPREQ:
+ /* Request status in the dialplan */
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD) &&
+ !ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED) && ies.called_number) {
+ if (iaxcompat) {
+ /* Spawn a thread for the lookup */
+ spawn_dp_lookup(fr->callno, iaxs[fr->callno]->context, ies.called_number, iaxs[fr->callno]->cid_num);
+ } else {
+ /* Just look it up */
+ dp_lookup(fr->callno, iaxs[fr->callno]->context, ies.called_number, iaxs[fr->callno]->cid_num, 1);
+ }
+ }
+ break;
+ case IAX_COMMAND_HANGUP:
+ ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE);
+ ast_debug(1, "Immediately destroying %d, having received hangup\n", fr->callno);
+ /* Set hangup cause according to remote */
+ if (ies.causecode && iaxs[fr->callno]->owner)
+ iaxs[fr->callno]->owner->hangupcause = ies.causecode;
+ /* Send ack immediately, before we destroy */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ iax2_destroy(fr->callno);
+ break;
+ case IAX_COMMAND_REJECT:
+ /* Set hangup cause according to remote */
+ if (ies.causecode && iaxs[fr->callno]->owner)
+ iaxs[fr->callno]->owner->hangupcause = ies.causecode;
+
+ if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) {
+ if (iaxs[fr->callno]->owner && authdebug)
+ ast_log(LOG_WARNING, "Call rejected by %s: %s\n",
+ ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr),
+ ies.cause ? ies.cause : "<Unknown>");
+ ast_debug(1, "Immediately destroying %d, having received reject\n",
+ fr->callno);
+ }
+ /* Send ack immediately, before we destroy */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK,
+ fr->ts, NULL, 0, fr->iseqno);
+ if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION))
+ iaxs[fr->callno]->error = EPERM;
+ iax2_destroy(fr->callno);
+ break;
+ case IAX_COMMAND_TRANSFER:
+ if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner) && ies.called_number) {
+ /* Set BLINDTRANSFER channel variables */
+ pbx_builtin_setvar_helper(iaxs[fr->callno]->owner, "BLINDTRANSFER", ast_bridged_channel(iaxs[fr->callno]->owner)->name);
+ pbx_builtin_setvar_helper(ast_bridged_channel(iaxs[fr->callno]->owner), "BLINDTRANSFER", iaxs[fr->callno]->owner->name);
+ if (!strcmp(ies.called_number, ast_parking_ext())) {
+ if (iax_park(ast_bridged_channel(iaxs[fr->callno]->owner), iaxs[fr->callno]->owner)) {
+ ast_log(LOG_WARNING, "Failed to park call on '%s'\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name);
+ } else if (ast_bridged_channel(iaxs[fr->callno]->owner)) {
+ ast_debug(1, "Parked call on '%s'\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name);
+ }
+ } else {
+ if (ast_async_goto(ast_bridged_channel(iaxs[fr->callno]->owner), iaxs[fr->callno]->context, ies.called_number, 1))
+ ast_log(LOG_WARNING, "Async goto of '%s' to '%s@%s' failed\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name,
+ ies.called_number, iaxs[fr->callno]->context);
+ else {
+ ast_debug(1, "Async goto of '%s' to '%s@%s' started\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name,
+ ies.called_number, iaxs[fr->callno]->context);
+ }
+ }
+ } else {
+ ast_debug(1, "Async goto not applicable on call %d\n", fr->callno);
+ }
+ break;
+ case IAX_COMMAND_ACCEPT:
+ /* Ignore if call is already up or needs authentication or is a TBD */
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD | IAX_STATE_AUTHENTICATED))
+ break;
+ if (ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) {
+ /* Send ack immediately, before we destroy */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ iax2_destroy(fr->callno);
+ break;
+ }
+ if (ies.format) {
+ iaxs[fr->callno]->peerformat = ies.format;
+ } else {
+ if (iaxs[fr->callno]->owner)
+ iaxs[fr->callno]->peerformat = iaxs[fr->callno]->owner->nativeformats;
+ else
+ iaxs[fr->callno]->peerformat = iaxs[fr->callno]->capability;
+ }
+ ast_verb(3, "Call accepted by %s (format %s)\n", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), ast_getformatname(iaxs[fr->callno]->peerformat));
+ if (!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability)) {
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Rejected call to %s, format 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
+ } else {
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
+ if (iaxs[fr->callno]->owner) {
+ /* Switch us to use a compatible format */
+ iaxs[fr->callno]->owner->nativeformats = iaxs[fr->callno]->peerformat;
+ ast_verb(3, "Format for call is %s\n", ast_getformatname(iaxs[fr->callno]->owner->nativeformats));
+retryowner2:
+ if (ast_channel_trylock(iaxs[fr->callno]->owner)) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ usleep(1);
+ ast_mutex_lock(&iaxsl[fr->callno]);
+ if (iaxs[fr->callno] && iaxs[fr->callno]->owner) goto retryowner2;
+ }
+
+ if (iaxs[fr->callno] && iaxs[fr->callno]->owner) {
+ /* Setup read/write formats properly. */
+ if (iaxs[fr->callno]->owner->writeformat)
+ ast_set_write_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->writeformat);
+ if (iaxs[fr->callno]->owner->readformat)
+ ast_set_read_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->readformat);
+ ast_channel_unlock(iaxs[fr->callno]->owner);
+ }
+ }
+ }
+ if (iaxs[fr->callno]) {
+ AST_LIST_LOCK(&dpcache);
+ AST_LIST_TRAVERSE(&iaxs[fr->callno]->dpentries, dp, peer_list)
+ if (!(dp->flags & CACHE_FLAG_TRANSMITTED))
+ iax2_dprequest(dp, fr->callno);
+ AST_LIST_UNLOCK(&dpcache);
+ }
+ break;
+ case IAX_COMMAND_POKE:
+ /* Send back a pong packet with the original timestamp */
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr->ts, NULL, 0, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ break;
+ case IAX_COMMAND_PING:
+ {
+ struct iax_ie_data pingied;
+ construct_rr(iaxs[fr->callno], &pingied);
+ /* Send back a pong packet with the original timestamp */
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr->ts, pingied.buf, pingied.pos, -1);
+ }
+ break;
+ case IAX_COMMAND_PONG:
+ /* Calculate ping time */
+ iaxs[fr->callno]->pingtime = calc_timestamp(iaxs[fr->callno], 0, &f) - fr->ts;
+ /* save RR info */
+ save_rr(fr, &ies);
+
+ if (iaxs[fr->callno]->peerpoke) {
+ peer = iaxs[fr->callno]->peerpoke;
+ if ((peer->lastms < 0) || (peer->historicms > peer->maxms)) {
+ if (iaxs[fr->callno]->pingtime <= peer->maxms) {
+ ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE! Time: %d\n", peer->name, iaxs[fr->callno]->pingtime);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Reachable\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime);
+ ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
+ }
+ } else if ((peer->historicms > 0) && (peer->historicms <= peer->maxms)) {
+ if (iaxs[fr->callno]->pingtime > peer->maxms) {
+ ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr->callno]->pingtime);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime);
+ ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
+ }
+ }
+ peer->lastms = iaxs[fr->callno]->pingtime;
+ if (peer->smoothing && (peer->lastms > -1))
+ peer->historicms = (iaxs[fr->callno]->pingtime + peer->historicms) / 2;
+ else if (peer->smoothing && peer->lastms < 0)
+ peer->historicms = (0 + peer->historicms) / 2;
+ else
+ peer->historicms = iaxs[fr->callno]->pingtime;
+
+ /* Remove scheduled iax2_poke_noanswer */
+ if (peer->pokeexpire > -1) {
+ if (!ast_sched_del(sched, peer->pokeexpire)) {
+ peer_unref(peer);
+ peer->pokeexpire = -1;
+ }
+ }
+ /* Schedule the next cycle */
+ if ((peer->lastms < 0) || (peer->historicms > peer->maxms))
+ peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer));
+ else
+ peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqok, iax2_poke_peer_s, peer_ref(peer));
+ if (peer->pokeexpire == -1)
+ peer_unref(peer);
+ /* and finally send the ack */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ /* And wrap up the qualify call */
+ iax2_destroy(fr->callno);
+ peer->callno = 0;
+ ast_debug(1, "Peer %s: got pong, lastms %d, historicms %d, maxms %d\n", peer->name, peer->lastms, peer->historicms, peer->maxms);
+ }
+ break;
+ case IAX_COMMAND_LAGRQ:
+ case IAX_COMMAND_LAGRP:
+ f.src = "LAGRQ";
+ f.mallocd = 0;
+ f.offset = 0;
+ f.samples = 0;
+ iax_frame_wrap(fr, &f);
+ if(f.subclass == IAX_COMMAND_LAGRQ) {
+ /* Received a LAGRQ - echo back a LAGRP */
+ fr->af.subclass = IAX_COMMAND_LAGRP;
+ iax2_send(iaxs[fr->callno], &fr->af, fr->ts, -1, 0, 0, 0);
+ } else {
+ /* Received LAGRP in response to our LAGRQ */
+ unsigned int ts;
+ /* This is a reply we've been given, actually measure the difference */
+ ts = calc_timestamp(iaxs[fr->callno], 0, &fr->af);
+ iaxs[fr->callno]->lag = ts - fr->ts;
+ if (iaxdebug)
+ ast_debug(1, "Peer %s lag measured as %dms\n",
+ ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), iaxs[fr->callno]->lag);
+ }
+ break;
+ case IAX_COMMAND_AUTHREQ:
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
+ ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
+ break;
+ }
+ if (authenticate_reply(iaxs[fr->callno], &iaxs[fr->callno]->addr, &ies, iaxs[fr->callno]->secret, iaxs[fr->callno]->outkey)) {
+ ast_log(LOG_WARNING,
+ "I don't know how to authenticate %s to %s\n",
+ ies.username ? ies.username : "<unknown>", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr));
+ }
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ break;
+ case IAX_COMMAND_AUTHREP:
+ /* For security, always ack immediately */
+ if (delayreject)
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ /* Ignore once we've started */
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
+ ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
+ break;
+ }
+ if (authenticate_verify(iaxs[fr->callno], &ies)) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), iaxs[fr->callno]->username);
+ memset(&ied0, 0, sizeof(ied0));
+ auth_fail(fr->callno, IAX_COMMAND_REJECT);
+ break;
+ }
+ if (strcasecmp(iaxs[fr->callno]->exten, "TBD")) {
+ /* This might re-enter the IAX code and need the lock */
+ exists = ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num);
+ } else
+ exists = 0;
+ if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ } else {
+ /* Select an appropriate format */
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
+ using_prefs = "reqonly";
+ } else {
+ using_prefs = "disabled";
+ }
+ format = iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability;
+ memset(&pref, 0, sizeof(pref));
+ strcpy(caller_pref_buf, "disabled");
+ strcpy(host_pref_buf, "disabled");
+ } else {
+ using_prefs = "mine";
+ if (ies.codec_prefs)
+ ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0);
+ if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
+ if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
+ pref = iaxs[fr->callno]->rprefs;
+ using_prefs = "caller";
+ } else {
+ pref = iaxs[fr->callno]->prefs;
+ }
+ } else /* if no codec_prefs IE do it the old way */
+ pref = iaxs[fr->callno]->prefs;
+
+ format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0);
+ ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1);
+ ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
+ }
+ if (!format) {
+ if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
+ ast_debug(1, "We don't do requested format %s, falling back to peer capability %d\n", ast_getformatname(iaxs[fr->callno]->peerformat), iaxs[fr->callno]->peercapability);
+ format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability;
+ }
+ if (!format) {
+ if (authdebug) {
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
+ else
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
+ }
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ } else {
+ /* Pick one... */
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
+ if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability))
+ format = 0;
+ } else {
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
+ using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
+ memset(&pref, 0, sizeof(pref));
+ format = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ?
+ iaxs[fr->callno]->peerformat : ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ strcpy(caller_pref_buf,"disabled");
+ strcpy(host_pref_buf,"disabled");
+ } else {
+ using_prefs = "mine";
+ if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
+ /* Do the opposite of what we tried above. */
+ if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
+ pref = iaxs[fr->callno]->prefs;
+ } else {
+ pref = iaxs[fr->callno]->rprefs;
+ using_prefs = "caller";
+ }
+ format = ast_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, 1);
+ } else /* if no codec_prefs IE do it the old way */
+ format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ }
+ }
+ if (!format) {
+ ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ if (authdebug) {
+ if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
+ else
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
+ }
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ }
+ }
+ }
+ if (format) {
+ /* Authentication received */
+ memset(&ied1, 0, sizeof(ied1));
+ iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
+ if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
+ ast_verb(3, "Accepting AUTHENTICATED call from %s:\n"
+ "%srequested format = %s,\n"
+ "%srequested prefs = %s,\n"
+ "%sactual format = %s,\n"
+ "%shost prefs = %s,\n"
+ "%spriority = %s\n",
+ ast_inet_ntoa(sin.sin_addr),
+ VERBOSE_PREFIX_4,
+ ast_getformatname(iaxs[fr->callno]->peerformat),
+ VERBOSE_PREFIX_4,
+ caller_pref_buf,
+ VERBOSE_PREFIX_4,
+ ast_getformatname(format),
+ VERBOSE_PREFIX_4,
+ host_pref_buf,
+ VERBOSE_PREFIX_4,
+ using_prefs);
+
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
+ if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format)))
+ iax2_destroy(fr->callno);
+ else if (ies.vars) {
+ struct ast_datastore *variablestore;
+ struct ast_variable *var, *prev = NULL;
+ AST_LIST_HEAD(, ast_var_t) *varlist;
+ varlist = ast_calloc(1, sizeof(*varlist));
+ variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+ if (variablestore && varlist) {
+ variablestore->data = varlist;
+ variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+ AST_LIST_HEAD_INIT(varlist);
+ for (var = ies.vars; var; var = var->next) {
+ struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
+ if (prev)
+ ast_free(prev);
+ prev = var;
+ if (!newvar) {
+ /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ } else {
+ AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+ }
+ }
+ if (prev)
+ ast_free(prev);
+ ies.vars = NULL;
+ ast_channel_datastore_add(c, variablestore);
+ } else {
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ if (variablestore)
+ ast_channel_datastore_free(variablestore);
+ if (varlist)
+ ast_free(varlist);
+ }
+ }
+ } else {
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
+ /* If this is a TBD call, we're ready but now what... */
+ ast_verb(3, "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(sin.sin_addr));
+ }
+ }
+ }
+ break;
+ case IAX_COMMAND_DIAL:
+ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD)) {
+ ast_clear_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
+ ast_string_field_set(iaxs[fr->callno], exten, ies.called_number ? ies.called_number : "s");
+ if (!ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num)) {
+ if (authdebug)
+ ast_log(LOG_NOTICE, "Rejected dial attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ } else {
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
+ ast_verb(3, "Accepting DIAL from %s, formats = 0x%x\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat);
+ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
+ send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1);
+ if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat)))
+ iax2_destroy(fr->callno);
+ else if (ies.vars) {
+ struct ast_datastore *variablestore;
+ struct ast_variable *var, *prev = NULL;
+ AST_LIST_HEAD(, ast_var_t) *varlist;
+ varlist = ast_calloc(1, sizeof(*varlist));
+ variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
+ if (variablestore && varlist) {
+ variablestore->data = varlist;
+ variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
+ AST_LIST_HEAD_INIT(varlist);
+ for (var = ies.vars; var; var = var->next) {
+ struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
+ if (prev)
+ ast_free(prev);
+ prev = var;
+ if (!newvar) {
+ /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ } else {
+ AST_LIST_INSERT_TAIL(varlist, newvar, entries);
+ }
+ }
+ if (prev)
+ ast_free(prev);
+ ies.vars = NULL;
+ ast_channel_datastore_add(c, variablestore);
+ } else {
+ ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
+ if (variablestore)
+ ast_channel_datastore_free(variablestore);
+ if (varlist)
+ ast_free(varlist);
+ }
+ }
+ }
+ }
+ break;
+ case IAX_COMMAND_INVAL:
+ iaxs[fr->callno]->error = ENOTCONN;
+ ast_debug(1, "Immediately destroying %d, having received INVAL\n", fr->callno);
+ iax2_destroy(fr->callno);
+ ast_debug(1, "Destroying call %d\n", fr->callno);
+ break;
+ case IAX_COMMAND_VNAK:
+ ast_debug(1, "Received VNAK: resending outstanding frames\n");
+ /* Force retransmission */
+ vnak_retransmit(fr->callno, fr->iseqno);
+ break;
+ case IAX_COMMAND_REGREQ:
+ case IAX_COMMAND_REGREL:
+ /* For security, always ack immediately */
+ if (delayreject)
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ if (register_verify(fr->callno, &sin, &ies)) {
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ /* Send delayed failure */
+ auth_fail(fr->callno, IAX_COMMAND_REGREJ);
+ break;
+ }
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if ((ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) ||
+ ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED)) {
+ if (f.subclass == IAX_COMMAND_REGREL)
+ memset(&sin, 0, sizeof(sin));
+ if (update_registry(&sin, fr->callno, ies.devicetype, fd, ies.refresh))
+ ast_log(LOG_WARNING, "Registry error\n");
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ check_provisioning(&sin, fd, ies.serviceident, ies.provver);
+ ast_mutex_lock(&iaxsl[fr->callno]);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ }
+ break;
+ }
+ registry_authrequest(fr->callno);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ break;
+ case IAX_COMMAND_REGACK:
+ if (iax2_ack_registry(&ies, &sin, fr->callno))
+ ast_log(LOG_WARNING, "Registration failure\n");
+ /* Send ack immediately, before we destroy */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ iax2_destroy(fr->callno);
+ break;
+ case IAX_COMMAND_REGREJ:
+ if (iaxs[fr->callno]->reg) {
+ if (authdebug) {
+ ast_log(LOG_NOTICE, "Registration of '%s' rejected: '%s' from: '%s'\n", iaxs[fr->callno]->reg->username, ies.cause ? ies.cause : "<unknown>", ast_inet_ntoa(sin.sin_addr));
+ manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: IAX2\r\nUsername: %s\r\nStatus: Rejected\r\nCause: %s\r\n", iaxs[fr->callno]->reg->username, ies.cause ? ies.cause : "<unknown>");
+ }
+ iaxs[fr->callno]->reg->regstate = REG_STATE_REJECTED;
+ }
+ /* Send ack immediately, before we destroy */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ iax2_destroy(fr->callno);
+ break;
+ case IAX_COMMAND_REGAUTH:
+ /* Authentication request */
+ if (registry_rerequest(&ies, fr->callno, &sin)) {
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No authority found");
+ iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED);
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ }
+ break;
+ case IAX_COMMAND_TXREJ:
+ iaxs[fr->callno]->transferring = 0;
+ ast_verb(3, "Channel '%s' unable to transfer\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
+ memset(&iaxs[fr->callno]->transfer, 0, sizeof(iaxs[fr->callno]->transfer));
+ if (iaxs[fr->callno]->bridgecallno) {
+ if (iaxs[iaxs[fr->callno]->bridgecallno]->transferring) {
+ iaxs[iaxs[fr->callno]->bridgecallno]->transferring = 0;
+ send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
+ }
+ }
+ break;
+ case IAX_COMMAND_TXREADY:
+ if ((iaxs[fr->callno]->transferring == TRANSFER_BEGIN) ||
+ (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN)) {
+ if (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN)
+ iaxs[fr->callno]->transferring = TRANSFER_MREADY;
+ else
+ iaxs[fr->callno]->transferring = TRANSFER_READY;
+ ast_verb(3, "Channel '%s' ready to transfer\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
+ if (iaxs[fr->callno]->bridgecallno) {
+ if ((iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_READY) ||
+ (iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_MREADY)) {
+ /* They're both ready, now release them. */
+ if (iaxs[fr->callno]->transferring == TRANSFER_MREADY) {
+ ast_verb(3, "Attempting media bridge of %s and %s\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>",
+ iaxs[iaxs[fr->callno]->bridgecallno]->owner ? iaxs[iaxs[fr->callno]->bridgecallno]->owner->name : "<Unknown>");
+
+ iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_MEDIA;
+ iaxs[fr->callno]->transferring = TRANSFER_MEDIA;
+
+ memset(&ied0, 0, sizeof(ied0));
+ memset(&ied1, 0, sizeof(ied1));
+ iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
+ iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied0.buf, ied0.pos, -1);
+ send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied1.buf, ied1.pos, -1);
+ } else {
+ ast_verb(3, "Releasing %s and %s\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>",
+ iaxs[iaxs[fr->callno]->bridgecallno]->owner ? iaxs[iaxs[fr->callno]->bridgecallno]->owner->name : "<Unknown>");
+
+ iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_RELEASED;
+ iaxs[fr->callno]->transferring = TRANSFER_RELEASED;
+ ast_set_flag(iaxs[iaxs[fr->callno]->bridgecallno], IAX_ALREADYGONE);
+ ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE);
+
+ /* Stop doing lag & ping requests */
+ stop_stuff(fr->callno);
+ stop_stuff(iaxs[fr->callno]->bridgecallno);
+
+ memset(&ied0, 0, sizeof(ied0));
+ memset(&ied1, 0, sizeof(ied1));
+ iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
+ iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied0.buf, ied0.pos, -1);
+ send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied1.buf, ied1.pos, -1);
+ }
+
+ }
+ }
+ }
+ break;
+ case IAX_COMMAND_TXREQ:
+ try_transfer(iaxs[fr->callno], &ies);
+ break;
+ case IAX_COMMAND_TXCNT:
+ if (iaxs[fr->callno]->transferring)
+ send_command_transfer(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXACC, 0, NULL, 0);
+ break;
+ case IAX_COMMAND_TXREL:
+ /* Send ack immediately, rather than waiting until we've changed addresses */
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ complete_transfer(fr->callno, &ies);
+ stop_stuff(fr->callno); /* for attended transfer to work with libiax */
+ break;
+ case IAX_COMMAND_TXMEDIA:
+ if (iaxs[fr->callno]->transferring == TRANSFER_READY) {
+ AST_LIST_LOCK(&frame_queue);
+ AST_LIST_TRAVERSE(&frame_queue, cur, list) {
+ /* Cancel any outstanding frames and start anew */
+ if ((fr->callno == cur->callno) && (cur->transfer))
+ cur->retries = -1;
+ }
+ AST_LIST_UNLOCK(&frame_queue);
+ /* Start sending our media to the transfer address, but otherwise leave the call as-is */
+ iaxs[fr->callno]->transferring = TRANSFER_MEDIAPASS;
+ }
+ break;
+ case IAX_COMMAND_DPREP:
+ complete_dpreply(iaxs[fr->callno], &ies);
+ break;
+ case IAX_COMMAND_UNSUPPORT:
+ ast_log(LOG_NOTICE, "Peer did not understand our iax command '%d'\n", ies.iax_unknown);
+ break;
+ case IAX_COMMAND_FWDOWNL:
+ /* Firmware download */
+ memset(&ied0, 0, sizeof(ied0));
+ res = iax_firmware_append(&ied0, (unsigned char *)ies.devicetype, ies.fwdesc);
+ if (res < 0)
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+ else if (res > 0)
+ send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1);
+ else
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1);
+ if (!iaxs[fr->callno]) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ break;
+ default:
+ ast_debug(1, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno);
+ memset(&ied0, 0, sizeof(ied0));
+ iax_ie_append_byte(&ied0, IAX_IE_IAX_UNKNOWN, f.subclass);
+ send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, ied0.buf, ied0.pos, -1);
+ }
+ /* Free remote variables (if any) */
+ if (ies.vars)
+ ast_variables_destroy(ies.vars);
+
+ /* Don't actually pass these frames along */
+ if ((f.subclass != IAX_COMMAND_ACK) &&
+ (f.subclass != IAX_COMMAND_TXCNT) &&
+ (f.subclass != IAX_COMMAND_TXACC) &&
+ (f.subclass != IAX_COMMAND_INVAL) &&
+ (f.subclass != IAX_COMMAND_VNAK)) {
+ if (iaxs[fr->callno] && iaxs[fr->callno]->aseqno != iaxs[fr->callno]->iseqno)
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ }
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ /* Unless this is an ACK or INVAL frame, ack it */
+ if (iaxs[fr->callno] && iaxs[fr->callno]->aseqno != iaxs[fr->callno]->iseqno)
+ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
+ } else if (minivid) {
+ f.frametype = AST_FRAME_VIDEO;
+ if (iaxs[fr->callno]->videoformat > 0)
+ f.subclass = iaxs[fr->callno]->videoformat | (ntohs(vh->ts) & 0x8000 ? 1 : 0);
+ else {
+ ast_log(LOG_WARNING, "Received mini frame before first full video frame\n ");
+ iax2_vnak(fr->callno);
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ f.datalen = res - sizeof(*vh);
+ if (f.datalen)
+ f.data = thread->buf + sizeof(*vh);
+ else
+ f.data = NULL;
+#ifdef IAXTESTS
+ if (test_resync) {
+ fr->ts = (iaxs[fr->callno]->last & 0xFFFF8000L) | ((ntohs(vh->ts) + test_resync) & 0x7fff);
+ } else
+#endif /* IAXTESTS */
+ fr->ts = (iaxs[fr->callno]->last & 0xFFFF8000L) | (ntohs(vh->ts) & 0x7fff);
+ } else {
+ /* A mini frame */
+ f.frametype = AST_FRAME_VOICE;
+ if (iaxs[fr->callno]->voiceformat > 0)
+ f.subclass = iaxs[fr->callno]->voiceformat;
+ else {
+ ast_debug(1, "Received mini frame before first full voice frame\n");
+ iax2_vnak(fr->callno);
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ f.datalen = res - sizeof(struct ast_iax2_mini_hdr);
+ if (f.datalen < 0) {
+ ast_log(LOG_WARNING, "Datalen < 0?\n");
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ if (f.datalen)
+ f.data = thread->buf + sizeof(*mh);
+ else
+ f.data = NULL;
+#ifdef IAXTESTS
+ if (test_resync) {
+ fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | ((ntohs(mh->ts) + test_resync) & 0xffff);
+ } else
+#endif /* IAXTESTS */
+ fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | ntohs(mh->ts);
+ /* FIXME? Surely right here would be the right place to undo timestamp wraparound? */
+ }
+ /* Don't pass any packets until we're started */
+ if (!ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ /* Common things */
+ f.src = "IAX2";
+ f.mallocd = 0;
+ f.offset = 0;
+ f.len = 0;
+ if (f.datalen && (f.frametype == AST_FRAME_VOICE)) {
+ f.samples = ast_codec_get_samples(&f);
+ /* We need to byteswap incoming slinear samples from network byte order */
+ if (f.subclass == AST_FORMAT_SLINEAR)
+ ast_frame_byteswap_be(&f);
+ } else
+ f.samples = 0;
+ iax_frame_wrap(fr, &f);
+
+ /* If this is our most recent packet, use it as our basis for timestamping */
+ if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts) {
+ /*iaxs[fr->callno]->last = fr->ts; (do it afterwards cos schedule/forward_delivery needs the last ts too)*/
+ fr->outoforder = 0;
+ } else {
+ if (iaxdebug && iaxs[fr->callno])
+ ast_debug(1, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr->ts, iaxs[fr->callno]->last);
+ fr->outoforder = -1;
+ }
+ duped_fr = iaxfrdup2(fr);
+ if (duped_fr) {
+ schedule_delivery(duped_fr, updatehistory, 0, &fr->ts);
+ }
+ if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts) {
+ iaxs[fr->callno]->last = fr->ts;
+#if 1
+ if (iaxdebug)
+ ast_debug(1, "For call=%d, set last=%d\n", fr->callno, fr->ts);
+#endif
+ }
+
+ /* Always run again */
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+}
+
+/* Function to clean up process thread if it is cancelled */
+static void iax2_process_thread_cleanup(void *data)
+{
+ struct iax2_thread *thread = data;
+ ast_mutex_destroy(&thread->lock);
+ ast_cond_destroy(&thread->cond);
+ ast_free(thread);
+ ast_atomic_dec_and_test(&iaxactivethreadcount);
+}
+
+static void *iax2_process_thread(void *data)
+{
+ struct iax2_thread *thread = data;
+ struct timeval tv;
+ struct timespec ts;
+ int put_into_idle = 0;
+
+ ast_atomic_fetchadd_int(&iaxactivethreadcount,1);
+ pthread_cleanup_push(iax2_process_thread_cleanup, data);
+ for(;;) {
+ /* Wait for something to signal us to be awake */
+ ast_mutex_lock(&thread->lock);
+
+ /* Flag that we're ready to accept signals */
+ thread->ready_for_signal = 1;
+
+ /* Put into idle list if applicable */
+ if (put_into_idle)
+ insert_idle_thread(thread);
+
+ if (thread->type == IAX_THREAD_TYPE_DYNAMIC) {
+ struct iax2_thread *t = NULL;
+ /* Wait to be signalled or time out */
+ tv = ast_tvadd(ast_tvnow(), ast_samp2tv(30000, 1000));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ if (ast_cond_timedwait(&thread->cond, &thread->lock, &ts) == ETIMEDOUT) {
+ /* This thread was never put back into the available dynamic
+ * thread list, so just go away. */
+ if (!put_into_idle) {
+ ast_mutex_unlock(&thread->lock);
+ break;
+ }
+ AST_LIST_LOCK(&dynamic_list);
+ /* Account for the case where this thread is acquired *right* after a timeout */
+ if ((t = AST_LIST_REMOVE(&dynamic_list, thread, list)))
+ ast_atomic_fetchadd_int(&iaxdynamicthreadcount, -1);
+ AST_LIST_UNLOCK(&dynamic_list);
+ if (t) {
+ /* This dynamic thread timed out waiting for a task and was
+ * not acquired immediately after the timeout,
+ * so it's time to go away. */
+ ast_mutex_unlock(&thread->lock);
+ break;
+ }
+ /* Someone grabbed our thread *right* after we timed out.
+ * Wait for them to set us up with something to do and signal
+ * us to continue. */
+ tv = ast_tvadd(ast_tvnow(), ast_samp2tv(30000, 1000));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ if (ast_cond_timedwait(&thread->cond, &thread->lock, &ts) == ETIMEDOUT)
+ {
+ ast_mutex_unlock(&thread->lock);
+ break;
+ }
+ }
+ } else {
+ ast_cond_wait(&thread->cond, &thread->lock);
+ }
+
+ /* Go back into our respective list */
+ put_into_idle = 1;
+
+ ast_mutex_unlock(&thread->lock);
+
+ if (thread->iostate == IAX_IOSTATE_IDLE)
+ continue;
+
+ /* Add ourselves to the active list now */
+ AST_LIST_LOCK(&active_list);
+ AST_LIST_INSERT_HEAD(&active_list, thread, list);
+ AST_LIST_UNLOCK(&active_list);
+
+ /* See what we need to do */
+ switch(thread->iostate) {
+ case IAX_IOSTATE_READY:
+ thread->actions++;
+ thread->iostate = IAX_IOSTATE_PROCESSING;
+ socket_process(thread);
+ handle_deferred_full_frames(thread);
+ break;
+ case IAX_IOSTATE_SCHEDREADY:
+ thread->actions++;
+ thread->iostate = IAX_IOSTATE_PROCESSING;
+#ifdef SCHED_MULTITHREADED
+ thread->schedfunc(thread->scheddata);
+#endif
+ default:
+ break;
+ }
+ time(&thread->checktime);
+ thread->iostate = IAX_IOSTATE_IDLE;
+#ifdef DEBUG_SCHED_MULTITHREAD
+ thread->curfunc[0]='\0';
+#endif
+
+ /* Now... remove ourselves from the active list, and return to the idle list */
+ AST_LIST_LOCK(&active_list);
+ AST_LIST_REMOVE(&active_list, thread, list);
+ AST_LIST_UNLOCK(&active_list);
+
+ /* Make sure another frame didn't sneak in there after we thought we were done. */
+ handle_deferred_full_frames(thread);
+ }
+
+ /*!\note For some reason, idle threads are exiting without being removed
+ * from an idle list, which is causing memory corruption. Forcibly remove
+ * it from the list, if it's there.
+ */
+ AST_LIST_LOCK(&idle_list);
+ AST_LIST_REMOVE(&idle_list, thread, list);
+ AST_LIST_UNLOCK(&idle_list);
+
+ AST_LIST_LOCK(&dynamic_list);
+ AST_LIST_REMOVE(&dynamic_list, thread, list);
+ AST_LIST_UNLOCK(&dynamic_list);
+
+ /* I am exiting here on my own volition, I need to clean up my own data structures
+ * Assume that I am no longer in any of the lists (idle, active, or dynamic)
+ */
+ pthread_cleanup_pop(1);
+ return NULL;
+}
+
+static int iax2_do_register(struct iax2_registry *reg)
+{
+ struct iax_ie_data ied;
+ if (iaxdebug)
+ ast_debug(1, "Sending registration request for '%s'\n", reg->username);
+
+ if (reg->dnsmgr &&
+ ((reg->regstate == REG_STATE_TIMEOUT) || !reg->addr.sin_addr.s_addr)) {
+ /* Maybe the IP has changed, force DNS refresh */
+ ast_dnsmgr_refresh(reg->dnsmgr);
+ }
+
+ /*
+ * if IP has Changed, free allocated call to create a new one with new IP
+ * call has the pointer to IP and must be updated to the new one
+ */
+ if (reg->dnsmgr && ast_dnsmgr_changed(reg->dnsmgr) && (reg->callno > 0)) {
+ ast_mutex_lock(&iaxsl[reg->callno]);
+ iax2_destroy(reg->callno);
+ ast_mutex_unlock(&iaxsl[reg->callno]);
+ reg->callno = 0;
+ }
+ if (!reg->addr.sin_addr.s_addr) {
+ if (iaxdebug)
+ ast_debug(1, "Unable to send registration request for '%s' without IP address\n", reg->username);
+ /* Setup the next registration attempt */
+ reg->expire = iax2_sched_replace(reg->expire, sched,
+ (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg);
+ return -1;
+ }
+
+ if (!reg->callno) {
+ ast_debug(1, "Allocate call number\n");
+ reg->callno = find_callno(0, 0, &reg->addr, NEW_FORCE, defaultsockfd);
+ if (reg->callno < 1) {
+ ast_log(LOG_WARNING, "Unable to create call for registration\n");
+ return -1;
+ } else
+ ast_debug(1, "Registration created on call %d\n", reg->callno);
+ iaxs[reg->callno]->reg = reg;
+ }
+ /* Setup the next registration a little early */
+ reg->expire = iax2_sched_replace(reg->expire, sched,
+ (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg);
+ /* Send the request */
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
+ iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh);
+ send_command(iaxs[reg->callno],AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+ reg->regstate = REG_STATE_REGSENT;
+ return 0;
+}
+
+static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force)
+{
+ /* Returns 1 if provisioned, -1 if not able to find destination, or 0 if no provisioning
+ is found for template */
+ struct iax_ie_data provdata;
+ struct iax_ie_data ied;
+ unsigned int sig;
+ struct sockaddr_in sin;
+ int callno;
+ struct create_addr_info cai;
+
+ memset(&cai, 0, sizeof(cai));
+
+ ast_debug(1, "Provisioning '%s' from template '%s'\n", dest, template);
+
+ if (iax_provision_build(&provdata, &sig, template, force)) {
+ ast_debug(1, "No provisioning found for template '%s'\n", template);
+ return 0;
+ }
+
+ if (end) {
+ memcpy(&sin, end, sizeof(sin));
+ cai.sockfd = sockfd;
+ } else if (create_addr(dest, NULL, &sin, &cai))
+ return -1;
+
+ /* Build the rest of the message */
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_raw(&ied, IAX_IE_PROVISIONING, provdata.buf, provdata.pos);
+
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
+ if (!callno)
+ return -1;
+
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+ /* Schedule autodestruct in case they don't ever give us anything back */
+ iaxs[callno]->autoid = iax2_sched_replace(iaxs[callno]->autoid,
+ sched, 15000, auto_hangup, (void *)(long)callno);
+ ast_set_flag(iaxs[callno], IAX_PROVISION);
+ /* Got a call number now, so go ahead and send the provisioning information */
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PROVISION, 0, ied.buf, ied.pos, -1);
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+
+ return 1;
+}
+
+static char *papp = "IAX2Provision";
+static char *psyn = "Provision a calling IAXy with a given template";
+static char *pdescrip =
+" IAX2Provision([template]): Provisions the calling IAXy (assuming\n"
+"the calling entity is in fact an IAXy) with the given template or\n"
+"default if one is not specified. Returns -1 on error or 0 on success.\n";
+
+/*! iax2provision
+\ingroup applications
+*/
+static int iax2_prov_app(struct ast_channel *chan, void *data)
+{
+ int res;
+ char *sdata;
+ char *opts;
+ int force =0;
+ unsigned short callno = PTR_TO_CALLNO(chan->tech_pvt);
+ if (ast_strlen_zero(data))
+ data = "default";
+ sdata = ast_strdupa(data);
+ opts = strchr(sdata, '|');
+ if (opts)
+ *opts='\0';
+
+ if (chan->tech != &iax2_tech) {
+ ast_log(LOG_NOTICE, "Can't provision a non-IAX device!\n");
+ return -1;
+ }
+ if (!callno || !iaxs[callno] || !iaxs[callno]->addr.sin_addr.s_addr) {
+ ast_log(LOG_NOTICE, "Can't provision something with no IP?\n");
+ return -1;
+ }
+ res = iax2_provision(&iaxs[callno]->addr, iaxs[callno]->sockfd, NULL, sdata, force);
+ ast_verb(3, "Provisioned IAXY at '%s' with '%s'= %d\n",
+ ast_inet_ntoa(iaxs[callno]->addr.sin_addr),
+ sdata, res);
+ return res;
+}
+
+static char *handle_cli_iax2_provision(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int force = 0;
+ int res;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 provision";
+ e->usage =
+ "Usage: iax2 provision <host> <template> [forced]\n"
+ " Provisions the given peer or IP address using a template\n"
+ " matching either 'template' or '*' if the template is not\n"
+ " found. If 'forced' is specified, even empty provisioning\n"
+ " fields will be provisioned as empty fields.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos == 3)
+ return iax_prov_complete_template(a->line, a->word, a->pos, a->n);
+ return NULL;
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc > 4) {
+ if (!strcasecmp(a->argv[4], "forced"))
+ force = 1;
+ else
+ return CLI_SHOWUSAGE;
+ }
+ res = iax2_provision(NULL, -1, a->argv[2], a->argv[3], force);
+ if (res < 0)
+ ast_cli(a->fd, "Unable to find peer/address '%s'\n", a->argv[2]);
+ else if (res < 1)
+ ast_cli(a->fd, "No template (including wildcard) matching '%s'\n", a->argv[3]);
+ else
+ ast_cli(a->fd, "Provisioning '%s' with template '%s'%s\n", a->argv[2], a->argv[3], force ? ", forced" : "");
+ return CLI_SUCCESS;
+}
+
+static void __iax2_poke_noanswer(const void *data)
+{
+ struct iax2_peer *peer = (struct iax2_peer *)data;
+ if (peer->lastms > -1) {
+ ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Time: %d\n", peer->name, peer->lastms);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, peer->lastms);
+ ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
+ }
+ if (peer->callno > 0) {
+ ast_mutex_lock(&iaxsl[peer->callno]);
+ iax2_destroy(peer->callno);
+ ast_mutex_unlock(&iaxsl[peer->callno]);
+ }
+ peer->callno = 0;
+ peer->lastms = -1;
+ /* Try again quickly */
+ peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer));
+ if (peer->pokeexpire == -1)
+ peer_unref(peer);
+}
+
+static int iax2_poke_noanswer(const void *data)
+{
+ struct iax2_peer *peer = (struct iax2_peer *)data;
+ peer->pokeexpire = -1;
+#ifdef SCHED_MULTITHREADED
+ if (schedule_action(__iax2_poke_noanswer, data))
+#endif
+ __iax2_poke_noanswer(data);
+ peer_unref(peer);
+ return 0;
+}
+
+static int iax2_poke_peer_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ iax2_poke_peer(peer, 0);
+
+ return 0;
+}
+
+static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
+{
+ if (!peer->maxms || (!peer->addr.sin_addr.s_addr && !peer->dnsmgr)) {
+ /* IF we have no IP without dnsmgr, or this isn't to be monitored, return
+ immediately after clearing things out */
+ peer->lastms = 0;
+ peer->historicms = 0;
+ peer->pokeexpire = -1;
+ peer->callno = 0;
+ return 0;
+ }
+ if (peer->callno > 0) {
+ ast_log(LOG_NOTICE, "Still have a callno...\n");
+ ast_mutex_lock(&iaxsl[peer->callno]);
+ iax2_destroy(peer->callno);
+ ast_mutex_unlock(&iaxsl[peer->callno]);
+ }
+ if (heldcall)
+ ast_mutex_unlock(&iaxsl[heldcall]);
+ peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, peer->sockfd);
+ if (heldcall)
+ ast_mutex_lock(&iaxsl[heldcall]);
+ if (peer->callno < 1) {
+ ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name);
+ return -1;
+ }
+
+ /* Speed up retransmission times for this qualify call */
+ iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1;
+ iaxs[peer->callno]->peerpoke = peer;
+
+ if (peer->pokeexpire > -1) {
+ if (!ast_sched_del(sched, peer->pokeexpire)) {
+ peer->pokeexpire = -1;
+ peer_unref(peer);
+ }
+ }
+
+ /* Queue up a new task to handle no reply */
+ /* If the host is already unreachable then use the unreachable interval instead */
+ if (peer->lastms < 0)
+ peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_noanswer, peer_ref(peer));
+ else
+ peer->pokeexpire = iax2_sched_add(sched, DEFAULT_MAXMS * 2, iax2_poke_noanswer, peer_ref(peer));
+
+ if (peer->pokeexpire == -1)
+ peer_unref(peer);
+
+ /* And send the poke */
+ send_command(iaxs[peer->callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1);
+
+ return 0;
+}
+
+static void free_context(struct iax2_context *con)
+{
+ struct iax2_context *conl;
+ while(con) {
+ conl = con;
+ con = con->next;
+ ast_free(conl);
+ }
+}
+
+static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause)
+{
+ int callno;
+ int res;
+ int fmt, native;
+ struct sockaddr_in sin;
+ struct ast_channel *c;
+ struct parsed_dial_string pds;
+ struct create_addr_info cai;
+ char *tmpstr;
+
+ memset(&pds, 0, sizeof(pds));
+ tmpstr = ast_strdupa(data);
+ parse_dial_string(tmpstr, &pds);
+
+ memset(&cai, 0, sizeof(cai));
+ cai.capability = iax2_capability;
+
+ ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+
+ if (!pds.peer) {
+ ast_log(LOG_WARNING, "No peer given\n");
+ return NULL;
+ }
+
+
+ /* Populate our address from the given */
+ if (create_addr(pds.peer, NULL, &sin, &cai)) {
+ *cause = AST_CAUSE_UNREGISTERED;
+ return NULL;
+ }
+
+ if (pds.port)
+ sin.sin_port = htons(atoi(pds.port));
+
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
+ if (callno < 1) {
+ ast_log(LOG_WARNING, "Unable to create call\n");
+ *cause = AST_CAUSE_CONGESTION;
+ return NULL;
+ }
+
+ ast_mutex_lock(&iaxsl[callno]);
+
+ /* If this is a trunk, update it now */
+ ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+ if (ast_test_flag(&cai, IAX_TRUNK)) {
+ int new_callno;
+ if ((new_callno = make_trunk(callno, 1)) != -1)
+ callno = new_callno;
+ }
+ iaxs[callno]->maxtime = cai.maxtime;
+ if (cai.found)
+ ast_string_field_set(iaxs[callno], host, pds.peer);
+
+ c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability);
+
+ ast_mutex_unlock(&iaxsl[callno]);
+
+ if (c) {
+ /* Choose a format we can live with */
+ if (c->nativeformats & format)
+ c->nativeformats &= format;
+ else {
+ native = c->nativeformats;
+ fmt = format;
+ res = ast_translator_best_choice(&fmt, &native);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to create translator path for %s to %s on %s\n",
+ ast_getformatname(c->nativeformats), ast_getformatname(fmt), c->name);
+ ast_hangup(c);
+ return NULL;
+ }
+ c->nativeformats = native;
+ }
+ c->readformat = ast_best_codec(c->nativeformats);
+ c->writeformat = c->readformat;
+ }
+
+ return c;
+}
+
+static void *sched_thread(void *ignore)
+{
+ int count;
+ int res;
+ struct timeval tv;
+ struct timespec ts;
+
+ for (;;) {
+ res = ast_sched_wait(sched);
+ if ((res > 1000) || (res < 0))
+ res = 1000;
+ tv = ast_tvadd(ast_tvnow(), ast_samp2tv(res, 1000));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+
+ pthread_testcancel();
+ ast_mutex_lock(&sched_lock);
+ ast_cond_timedwait(&sched_cond, &sched_lock, &ts);
+ ast_mutex_unlock(&sched_lock);
+ pthread_testcancel();
+
+ count = ast_sched_runq(sched);
+ if (count >= 20)
+ ast_debug(1, "chan_iax2: ast_sched_runq ran %d scheduled tasks all at once\n", count);
+ }
+
+ return NULL;
+}
+
+static void *network_thread(void *ignore)
+{
+ /* Our job is simple: Send queued messages, retrying if necessary. Read frames
+ from the network, and queue them for delivery to the channels */
+ int res, count, wakeup;
+ struct iax_frame *f;
+
+ if (timingfd > -1)
+ ast_io_add(io, timingfd, timing_read, AST_IO_IN | AST_IO_PRI, NULL);
+
+ for(;;) {
+ pthread_testcancel();
+
+ /* Go through the queue, sending messages which have not yet been
+ sent, and scheduling retransmissions if appropriate */
+ AST_LIST_LOCK(&frame_queue);
+ count = 0;
+ wakeup = -1;
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&frame_queue, f, list) {
+ if (f->sentyet)
+ continue;
+
+ /* Try to lock the pvt, if we can't... don't fret - defer it till later */
+ if (ast_mutex_trylock(&iaxsl[f->callno])) {
+ wakeup = 1;
+ continue;
+ }
+
+ f->sentyet = 1;
+
+ if (iaxs[f->callno]) {
+ send_packet(f);
+ count++;
+ }
+
+ ast_mutex_unlock(&iaxsl[f->callno]);
+
+ if (f->retries < 0) {
+ /* This is not supposed to be retransmitted */
+ AST_LIST_REMOVE_CURRENT(list);
+ /* Free the iax frame */
+ iax_frame_free(f);
+ } else {
+ /* We need reliable delivery. Schedule a retransmission */
+ f->retries++;
+ f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&frame_queue);
+
+ pthread_testcancel();
+ if (count >= 20)
+ ast_debug(1, "chan_iax2: Sent %d queued outbound frames all at once\n", count);
+
+ /* Now do the IO, and run scheduled tasks */
+ res = ast_io_wait(io, wakeup);
+ if (res >= 0) {
+ if (res >= 20)
+ ast_debug(1, "chan_iax2: ast_io_wait ran %d I/Os all at once\n", res);
+ }
+ }
+ return NULL;
+}
+
+static int start_network_thread(void)
+{
+ struct iax2_thread *thread;
+ int threadcount = 0;
+ int x;
+ for (x = 0; x < iaxthreadcount; x++) {
+ thread = ast_calloc(1, sizeof(*thread));
+ if (thread) {
+ thread->type = IAX_THREAD_TYPE_POOL;
+ thread->threadnum = ++threadcount;
+ ast_mutex_init(&thread->lock);
+ ast_cond_init(&thread->cond, NULL);
+ if (ast_pthread_create_detached(&thread->threadid, NULL, iax2_process_thread, thread)) {
+ ast_log(LOG_WARNING, "Failed to create new thread!\n");
+ ast_free(thread);
+ thread = NULL;
+ }
+ AST_LIST_LOCK(&idle_list);
+ AST_LIST_INSERT_TAIL(&idle_list, thread, list);
+ AST_LIST_UNLOCK(&idle_list);
+ }
+ }
+ ast_pthread_create_background(&schedthreadid, NULL, sched_thread, NULL);
+ ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
+ ast_verb(2, "%d helper threads started\n", threadcount);
+ return 0;
+}
+
+static struct iax2_context *build_context(const char *context)
+{
+ struct iax2_context *con;
+
+ if ((con = ast_calloc(1, sizeof(*con))))
+ ast_copy_string(con->context, context, sizeof(con->context));
+
+ return con;
+}
+
+static int get_auth_methods(const char *value)
+{
+ int methods = 0;
+ if (strstr(value, "rsa"))
+ methods |= IAX_AUTH_RSA;
+ if (strstr(value, "md5"))
+ methods |= IAX_AUTH_MD5;
+ if (strstr(value, "plaintext"))
+ methods |= IAX_AUTH_PLAINTEXT;
+ return methods;
+}
+
+
+/*! \brief Check if address can be used as packet source.
+ \return 0 address available, 1 address unavailable, -1 error
+*/
+static int check_srcaddr(struct sockaddr *sa, socklen_t salen)
+{
+ int sd;
+ int res;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ ast_log(LOG_ERROR, "Socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ res = bind(sd, sa, salen);
+ if (res < 0) {
+ ast_debug(1, "Can't bind: %s\n", strerror(errno));
+ close(sd);
+ return 1;
+ }
+
+ close(sd);
+ return 0;
+}
+
+/*! \brief Parse the "sourceaddress" value,
+ lookup in netsock list and set peer's sockfd. Defaults to defaultsockfd if
+ not found. */
+static int peer_set_srcaddr(struct iax2_peer *peer, const char *srcaddr)
+{
+ struct sockaddr_in sin;
+ int nonlocal = 1;
+ int port = IAX_DEFAULT_PORTNO;
+ int sockfd = defaultsockfd;
+ char *tmp;
+ char *addr;
+ char *portstr;
+
+ if (!(tmp = ast_strdupa(srcaddr)))
+ return -1;
+
+ addr = strsep(&tmp, ":");
+ portstr = tmp;
+
+ if (portstr) {
+ port = atoi(portstr);
+ if (port < 1)
+ port = IAX_DEFAULT_PORTNO;
+ }
+
+ if (!ast_get_ip(&sin, addr)) {
+ struct ast_netsock *sock;
+ int res;
+
+ sin.sin_port = 0;
+ sin.sin_family = AF_INET;
+ res = check_srcaddr((struct sockaddr *) &sin, sizeof(sin));
+ if (res == 0) {
+ /* ip address valid. */
+ sin.sin_port = htons(port);
+ if (!(sock = ast_netsock_find(netsock, &sin)))
+ sock = ast_netsock_find(outsock, &sin);
+ if (sock) {
+ sockfd = ast_netsock_sockfd(sock);
+ nonlocal = 0;
+ } else {
+ unsigned int orig_saddr = sin.sin_addr.s_addr;
+ /* INADDR_ANY matches anyway! */
+ sin.sin_addr.s_addr = INADDR_ANY;
+ if (ast_netsock_find(netsock, &sin)) {
+ sin.sin_addr.s_addr = orig_saddr;
+ sock = ast_netsock_bind(outsock, io, srcaddr, port, tos, cos, socket_read, NULL);
+ if (sock) {
+ sockfd = ast_netsock_sockfd(sock);
+ ast_netsock_unref(sock);
+ nonlocal = 0;
+ } else {
+ nonlocal = 2;
+ }
+ }
+ }
+ }
+ }
+
+ peer->sockfd = sockfd;
+
+ if (nonlocal == 1) {
+ ast_log(LOG_WARNING, "Non-local or unbound address specified (%s) in sourceaddress for '%s', reverting to default\n",
+ srcaddr, peer->name);
+ return -1;
+ } else if (nonlocal == 2) {
+ ast_log(LOG_WARNING, "Unable to bind to sourceaddress '%s' for '%s', reverting to default\n",
+ srcaddr, peer->name);
+ return -1;
+ } else {
+ ast_debug(1, "Using sourceaddress %s for '%s'\n", srcaddr, peer->name);
+ return 0;
+ }
+}
+
+static void peer_destructor(void *obj)
+{
+ struct iax2_peer *peer = obj;
+
+ ast_free_ha(peer->ha);
+
+ if (peer->callno > 0) {
+ ast_mutex_lock(&iaxsl[peer->callno]);
+ iax2_destroy(peer->callno);
+ ast_mutex_unlock(&iaxsl[peer->callno]);
+ }
+
+ register_peer_exten(peer, 0);
+
+ if (peer->dnsmgr)
+ ast_dnsmgr_release(peer->dnsmgr);
+
+ if (peer->mwi_event_sub)
+ ast_event_unsubscribe(peer->mwi_event_sub);
+
+ ast_string_field_free_memory(peer);
+}
+
+/*! \brief Create peer structure based on configuration */
+static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
+{
+ struct iax2_peer *peer = NULL;
+ struct ast_ha *oldha = NULL;
+ int maskfound = 0;
+ int found = 0;
+ int firstpass = 1;
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
+
+ if (!temponly) {
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+ if (peer && !ast_test_flag(peer, IAX_DELME))
+ firstpass = 0;
+ }
+
+ if (peer) {
+ found++;
+ if (firstpass) {
+ oldha = peer->ha;
+ peer->ha = NULL;
+ }
+ unlink_peer(peer);
+ } else if ((peer = ao2_alloc(sizeof(*peer), peer_destructor))) {
+ peer->expire = -1;
+ peer->pokeexpire = -1;
+ peer->sockfd = defaultsockfd;
+ if (ast_string_field_init(peer, 32))
+ peer = peer_unref(peer);
+ }
+
+ if (peer) {
+ if (firstpass) {
+ ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+ peer->encmethods = iax2_encryption;
+ peer->adsi = adsi;
+ ast_string_field_set(peer,secret,"");
+ if (!found) {
+ ast_string_field_set(peer, name, name);
+ peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO);
+ peer->expiry = min_reg_expire;
+ }
+ peer->prefs = prefs;
+ peer->capability = iax2_capability;
+ peer->smoothing = 0;
+ peer->pokefreqok = DEFAULT_FREQ_OK;
+ peer->pokefreqnotok = DEFAULT_FREQ_NOTOK;
+ ast_string_field_set(peer,context,"");
+ ast_string_field_set(peer,peercontext,"");
+ ast_clear_flag(peer, IAX_HASCALLERID);
+ ast_string_field_set(peer, cid_name, "");
+ ast_string_field_set(peer, cid_num, "");
+ }
+
+ if (!v) {
+ v = alt;
+ alt = NULL;
+ }
+ while(v) {
+ if (!strcasecmp(v->name, "secret")) {
+ ast_string_field_set(peer, secret, v->value);
+ } else if (!strcasecmp(v->name, "mailbox")) {
+ ast_string_field_set(peer, mailbox, v->value);
+ } else if (!strcasecmp(v->name, "mohinterpret")) {
+ ast_string_field_set(peer, mohinterpret, v->value);
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_string_field_set(peer, mohsuggest, v->value);
+ } else if (!strcasecmp(v->name, "dbsecret")) {
+ ast_string_field_set(peer, dbsecret, v->value);
+ } else if (!strcasecmp(v->name, "trunk")) {
+ ast_set2_flag(peer, ast_true(v->value), IAX_TRUNK);
+ if (ast_test_flag(peer, IAX_TRUNK) && (timingfd < 0)) {
+ ast_log(LOG_WARNING, "Unable to support trunking on peer '%s' without zaptel timing\n", peer->name);
+ ast_clear_flag(peer, IAX_TRUNK);
+ }
+ } else if (!strcasecmp(v->name, "auth")) {
+ peer->authmethods = get_auth_methods(v->value);
+ } else if (!strcasecmp(v->name, "encryption")) {
+ peer->encmethods = get_encrypt_methods(v->value);
+ } else if (!strcasecmp(v->name, "transfer")) {
+ if (!strcasecmp(v->value, "mediaonly")) {
+ ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);
+ } else if (ast_true(v->value)) {
+ ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0);
+ } else
+ ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER);
+ } else if (!strcasecmp(v->name, "jitterbuffer")) {
+ ast_set2_flag(peer, ast_true(v->value), IAX_USEJITTERBUF);
+ } else if (!strcasecmp(v->name, "forcejitterbuffer")) {
+ ast_set2_flag(peer, ast_true(v->value), IAX_FORCEJITTERBUF);
+ } else if (!strcasecmp(v->name, "host")) {
+ if (!strcasecmp(v->value, "dynamic")) {
+ /* They'll register with us */
+ ast_set_flag(peer, IAX_DYNAMIC);
+ if (!found) {
+ /* Initialize stuff iff we're not found, otherwise
+ we keep going with what we had */
+ memset(&peer->addr.sin_addr, 0, 4);
+ if (peer->addr.sin_port) {
+ /* If we've already got a port, make it the default rather than absolute */
+ peer->defaddr.sin_port = peer->addr.sin_port;
+ peer->addr.sin_port = 0;
+ }
+ }
+ } else {
+ /* Non-dynamic. Make sure we become that way if we're not */
+ if (peer->expire > -1)
+ ast_sched_del(sched, peer->expire);
+ peer->expire = -1;
+ ast_clear_flag(peer, IAX_DYNAMIC);
+ if (ast_dnsmgr_lookup(v->value, &peer->addr.sin_addr, &peer->dnsmgr))
+ return peer_unref(peer);
+ if (!peer->addr.sin_port)
+ peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO);
+ }
+ if (!maskfound)
+ inet_aton("255.255.255.255", &peer->mask);
+ } else if (!strcasecmp(v->name, "defaultip")) {
+ if (ast_get_ip(&peer->defaddr, v->value))
+ return peer_unref(peer);
+ } else if (!strcasecmp(v->name, "sourceaddress")) {
+ peer_set_srcaddr(peer, v->value);
+ } else if (!strcasecmp(v->name, "permit") ||
+ !strcasecmp(v->name, "deny")) {
+ peer->ha = ast_append_ha(v->name, v->value, peer->ha, NULL);
+ } else if (!strcasecmp(v->name, "mask")) {
+ maskfound++;
+ inet_aton(v->value, &peer->mask);
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_string_field_set(peer, context, v->value);
+ } else if (!strcasecmp(v->name, "regexten")) {
+ ast_string_field_set(peer, regexten, v->value);
+ } else if (!strcasecmp(v->name, "peercontext")) {
+ ast_string_field_set(peer, peercontext, v->value);
+ } else if (!strcasecmp(v->name, "port")) {
+ if (ast_test_flag(peer, IAX_DYNAMIC))
+ peer->defaddr.sin_port = htons(atoi(v->value));
+ else
+ peer->addr.sin_port = htons(atoi(v->value));
+ } else if (!strcasecmp(v->name, "username")) {
+ ast_string_field_set(peer, username, v->value);
+ } else if (!strcasecmp(v->name, "allow")) {
+ ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 1);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0);
+ } else if (!strcasecmp(v->name, "callerid")) {
+ if (!ast_strlen_zero(v->value)) {
+ char name2[80];
+ char num2[80];
+ ast_callerid_split(v->value, name2, 80, num2, 80);
+ ast_string_field_set(peer, cid_name, name2);
+ ast_string_field_set(peer, cid_num, num2);
+ ast_set_flag(peer, IAX_HASCALLERID);
+ } else {
+ ast_clear_flag(peer, IAX_HASCALLERID);
+ ast_string_field_set(peer, cid_name, "");
+ ast_string_field_set(peer, cid_num, "");
+ }
+ } else if (!strcasecmp(v->name, "fullname")) {
+ if (!ast_strlen_zero(v->value)) {
+ ast_string_field_set(peer, cid_name, v->value);
+ ast_set_flag(peer, IAX_HASCALLERID);
+ } else {
+ ast_string_field_set(peer, cid_name, "");
+ if (ast_strlen_zero(peer->cid_num))
+ ast_clear_flag(peer, IAX_HASCALLERID);
+ }
+ } else if (!strcasecmp(v->name, "cid_number")) {
+ if (!ast_strlen_zero(v->value)) {
+ ast_string_field_set(peer, cid_num, v->value);
+ ast_set_flag(peer, IAX_HASCALLERID);
+ } else {
+ ast_string_field_set(peer, cid_num, "");
+ if (ast_strlen_zero(peer->cid_name))
+ ast_clear_flag(peer, IAX_HASCALLERID);
+ }
+ } else if (!strcasecmp(v->name, "sendani")) {
+ ast_set2_flag(peer, ast_true(v->value), IAX_SENDANI);
+ } else if (!strcasecmp(v->name, "inkeys")) {
+ ast_string_field_set(peer, inkeys, v->value);
+ } else if (!strcasecmp(v->name, "outkey")) {
+ ast_string_field_set(peer, outkey, v->value);
+ } else if (!strcasecmp(v->name, "qualify")) {
+ if (!strcasecmp(v->value, "no")) {
+ peer->maxms = 0;
+ } else if (!strcasecmp(v->value, "yes")) {
+ peer->maxms = DEFAULT_MAXMS;
+ } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
+ ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
+ peer->maxms = 0;
+ }
+ } else if (!strcasecmp(v->name, "qualifysmoothing")) {
+ peer->smoothing = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "qualifyfreqok")) {
+ if (sscanf(v->value, "%d", &peer->pokefreqok) != 1) {
+ ast_log(LOG_WARNING, "Qualification testing frequency of peer '%s' when OK should a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "qualifyfreqnotok")) {
+ if (sscanf(v->value, "%d", &peer->pokefreqnotok) != 1) {
+ ast_log(LOG_WARNING, "Qualification testing frequency of peer '%s' when NOT OK should be a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
+ } else ast_log(LOG_WARNING, "Set peer->pokefreqnotok to %d\n", peer->pokefreqnotok);
+ } else if (!strcasecmp(v->name, "timezone")) {
+ ast_string_field_set(peer, zonetag, v->value);
+ } else if (!strcasecmp(v->name, "adsi")) {
+ peer->adsi = ast_true(v->value);
+ }/* else if (strcasecmp(v->name,"type")) */
+ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ v = v->next;
+ if (!v) {
+ v = alt;
+ alt = NULL;
+ }
+ }
+ if (!peer->authmethods)
+ peer->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+ ast_clear_flag(peer, IAX_DELME);
+ /* Make sure these are IPv4 addresses */
+ peer->addr.sin_family = AF_INET;
+ }
+
+ if (oldha)
+ ast_free_ha(oldha);
+
+ if (!ast_strlen_zero(peer->mailbox)) {
+ char *mailbox, *context;
+ context = mailbox = ast_strdupa(peer->mailbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+ peer->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_END);
+ }
+
+ return peer;
+}
+
+static void user_destructor(void *obj)
+{
+ struct iax2_user *user = obj;
+
+ ast_free_ha(user->ha);
+ free_context(user->contexts);
+ if(user->vars) {
+ ast_variables_destroy(user->vars);
+ user->vars = NULL;
+ }
+ ast_string_field_free_memory(user);
+}
+
+/*! \brief Create in-memory user structure from configuration */
+static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
+{
+ struct iax2_user *user = NULL;
+ struct iax2_context *con, *conl = NULL;
+ struct ast_ha *oldha = NULL;
+ struct iax2_context *oldcon = NULL;
+ int format;
+ int firstpass=1;
+ int oldcurauthreq = 0;
+ char *varname = NULL, *varval = NULL;
+ struct ast_variable *tmpvar = NULL;
+ struct iax2_user tmp_user = {
+ .name = name,
+ };
+
+ if (!temponly) {
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user && !ast_test_flag(user, IAX_DELME))
+ firstpass = 0;
+ }
+
+ if (user) {
+ if (firstpass) {
+ oldcurauthreq = user->curauthreq;
+ oldha = user->ha;
+ oldcon = user->contexts;
+ user->ha = NULL;
+ user->contexts = NULL;
+ }
+ /* Already in the list, remove it and it will be added back (or FREE'd) */
+ ao2_unlink(users, user);
+ } else {
+ user = ao2_alloc(sizeof(*user), user_destructor);
+ }
+
+ if (user) {
+ if (firstpass) {
+ ast_string_field_free_memory(user);
+ memset(user, 0, sizeof(struct iax2_user));
+ if (ast_string_field_init(user, 32)) {
+ user = user_unref(user);
+ goto cleanup;
+ }
+ user->maxauthreq = maxauthreq;
+ user->curauthreq = oldcurauthreq;
+ user->prefs = prefs;
+ user->capability = iax2_capability;
+ user->encmethods = iax2_encryption;
+ user->adsi = adsi;
+ ast_string_field_set(user, name, name);
+ ast_string_field_set(user, language, language);
+ ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP);
+ ast_clear_flag(user, IAX_HASCALLERID);
+ ast_string_field_set(user, cid_name, "");
+ ast_string_field_set(user, cid_num, "");
+ }
+ if (!v) {
+ v = alt;
+ alt = NULL;
+ }
+ while(v) {
+ if (!strcasecmp(v->name, "context")) {
+ con = build_context(v->value);
+ if (con) {
+ if (conl)
+ conl->next = con;
+ else
+ user->contexts = con;
+ conl = con;
+ }
+ } else if (!strcasecmp(v->name, "permit") ||
+ !strcasecmp(v->name, "deny")) {
+ user->ha = ast_append_ha(v->name, v->value, user->ha, NULL);
+ } else if (!strcasecmp(v->name, "setvar")) {
+ varname = ast_strdupa(v->value);
+ if (varname && (varval = strchr(varname,'='))) {
+ *varval = '\0';
+ varval++;
+ if((tmpvar = ast_variable_new(varname, varval, ""))) {
+ tmpvar->next = user->vars;
+ user->vars = tmpvar;
+ }
+ }
+ } else if (!strcasecmp(v->name, "allow")) {
+ ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&user->prefs, &user->capability,v->value, 0);
+ } else if (!strcasecmp(v->name, "trunk")) {
+ ast_set2_flag(user, ast_true(v->value), IAX_TRUNK);
+ if (ast_test_flag(user, IAX_TRUNK) && (timingfd < 0)) {
+ ast_log(LOG_WARNING, "Unable to support trunking on user '%s' without zaptel timing\n", user->name);
+ ast_clear_flag(user, IAX_TRUNK);
+ }
+ } else if (!strcasecmp(v->name, "auth")) {
+ user->authmethods = get_auth_methods(v->value);
+ } else if (!strcasecmp(v->name, "encryption")) {
+ user->encmethods = get_encrypt_methods(v->value);
+ } else if (!strcasecmp(v->name, "transfer")) {
+ if (!strcasecmp(v->value, "mediaonly")) {
+ ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);
+ } else if (ast_true(v->value)) {
+ ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0);
+ } else
+ ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER);
+ } else if (!strcasecmp(v->name, "codecpriority")) {
+ if(!strcasecmp(v->value, "caller"))
+ ast_set_flag(user, IAX_CODEC_USER_FIRST);
+ else if(!strcasecmp(v->value, "disabled"))
+ ast_set_flag(user, IAX_CODEC_NOPREFS);
+ else if(!strcasecmp(v->value, "reqonly")) {
+ ast_set_flag(user, IAX_CODEC_NOCAP);
+ ast_set_flag(user, IAX_CODEC_NOPREFS);
+ }
+ } else if (!strcasecmp(v->name, "jitterbuffer")) {
+ ast_set2_flag(user, ast_true(v->value), IAX_USEJITTERBUF);
+ } else if (!strcasecmp(v->name, "forcejitterbuffer")) {
+ ast_set2_flag(user, ast_true(v->value), IAX_FORCEJITTERBUF);
+ } else if (!strcasecmp(v->name, "dbsecret")) {
+ ast_string_field_set(user, dbsecret, v->value);
+ } else if (!strcasecmp(v->name, "secret")) {
+ if (!ast_strlen_zero(user->secret)) {
+ char *old = ast_strdupa(user->secret);
+
+ ast_string_field_build(user, secret, "%s;%s", old, v->value);
+ } else
+ ast_string_field_set(user, secret, v->value);
+ } else if (!strcasecmp(v->name, "callerid")) {
+ if (!ast_strlen_zero(v->value) && strcasecmp(v->value, "asreceived")) {
+ char name2[80];
+ char num2[80];
+ ast_callerid_split(v->value, name2, sizeof(name2), num2, sizeof(num2));
+ ast_string_field_set(user, cid_name, name2);
+ ast_string_field_set(user, cid_num, num2);
+ ast_set_flag(user, IAX_HASCALLERID);
+ } else {
+ ast_clear_flag(user, IAX_HASCALLERID);
+ ast_string_field_set(user, cid_name, "");
+ ast_string_field_set(user, cid_num, "");
+ }
+ } else if (!strcasecmp(v->name, "fullname")) {
+ if (!ast_strlen_zero(v->value)) {
+ ast_string_field_set(user, cid_name, v->value);
+ ast_set_flag(user, IAX_HASCALLERID);
+ } else {
+ ast_string_field_set(user, cid_name, "");
+ if (ast_strlen_zero(user->cid_num))
+ ast_clear_flag(user, IAX_HASCALLERID);
+ }
+ } else if (!strcasecmp(v->name, "cid_number")) {
+ if (!ast_strlen_zero(v->value)) {
+ ast_string_field_set(user, cid_num, v->value);
+ ast_set_flag(user, IAX_HASCALLERID);
+ } else {
+ ast_string_field_set(user, cid_num, "");
+ if (ast_strlen_zero(user->cid_name))
+ ast_clear_flag(user, IAX_HASCALLERID);
+ }
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_string_field_set(user, accountcode, v->value);
+ } else if (!strcasecmp(v->name, "mohinterpret")) {
+ ast_string_field_set(user, mohinterpret, v->value);
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_string_field_set(user, mohsuggest, v->value);
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_string_field_set(user, language, v->value);
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ format = ast_cdr_amaflags2int(v->value);
+ if (format < 0) {
+ ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
+ } else {
+ user->amaflags = format;
+ }
+ } else if (!strcasecmp(v->name, "inkeys")) {
+ ast_string_field_set(user, inkeys, v->value);
+ } else if (!strcasecmp(v->name, "maxauthreq")) {
+ user->maxauthreq = atoi(v->value);
+ if (user->maxauthreq < 0)
+ user->maxauthreq = 0;
+ } else if (!strcasecmp(v->name, "adsi")) {
+ user->adsi = ast_true(v->value);
+ }/* else if (strcasecmp(v->name,"type")) */
+ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ v = v->next;
+ if (!v) {
+ v = alt;
+ alt = NULL;
+ }
+ }
+ if (!user->authmethods) {
+ if (!ast_strlen_zero(user->secret)) {
+ user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+ if (!ast_strlen_zero(user->inkeys))
+ user->authmethods |= IAX_AUTH_RSA;
+ } else if (!ast_strlen_zero(user->inkeys)) {
+ user->authmethods = IAX_AUTH_RSA;
+ } else {
+ user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+ }
+ }
+ ast_clear_flag(user, IAX_DELME);
+ }
+cleanup:
+ if (oldha)
+ ast_free_ha(oldha);
+ if (oldcon)
+ free_context(oldcon);
+ return user;
+}
+
+static int peer_delme_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ ast_set_flag(peer, IAX_DELME);
+
+ return 0;
+}
+
+static int user_delme_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj;
+
+ ast_set_flag(user, IAX_DELME);
+
+ return 0;
+}
+
+static void delete_users(void)
+{
+ struct iax2_registry *reg;
+
+ ao2_callback(users, 0, user_delme_cb, NULL);
+
+ AST_LIST_LOCK(&registrations);
+ while ((reg = AST_LIST_REMOVE_HEAD(&registrations, entry))) {
+ if (reg->expire > -1)
+ ast_sched_del(sched, reg->expire);
+ if (reg->callno) {
+ ast_mutex_lock(&iaxsl[reg->callno]);
+ if (iaxs[reg->callno]) {
+ iaxs[reg->callno]->reg = NULL;
+ iax2_destroy(reg->callno);
+ }
+ ast_mutex_unlock(&iaxsl[reg->callno]);
+ }
+ if (reg->dnsmgr)
+ ast_dnsmgr_release(reg->dnsmgr);
+ ast_free(reg);
+ }
+ AST_LIST_UNLOCK(&registrations);
+
+ ao2_callback(peers, 0, peer_delme_cb, NULL);
+}
+
+static void prune_users(void)
+{
+ struct iax2_user *user;
+ struct ao2_iterator i;
+
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
+ if (ast_test_flag(user, IAX_DELME))
+ ao2_unlink(users, user);
+ user_unref(user);
+ }
+}
+
+/* Prune peers who still are supposed to be deleted */
+static void prune_peers(void)
+{
+ struct iax2_peer *peer;
+ struct ao2_iterator i;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if (ast_test_flag(peer, IAX_DELME))
+ unlink_peer(peer);
+ peer_unref(peer);
+ }
+}
+
+static void set_timing(void)
+{
+#ifdef HAVE_ZAPTEL
+ int bs = trunkfreq * 8;
+ if (timingfd > -1) {
+ if (
+#ifdef ZT_TIMERACK
+ ioctl(timingfd, ZT_TIMERCONFIG, &bs) &&
+#endif
+ ioctl(timingfd, ZT_SET_BLOCKSIZE, &bs))
+ ast_log(LOG_WARNING, "Unable to set blocksize on timing source\n");
+ }
+#endif
+}
+
+static void set_config_destroy(void)
+{
+ strcpy(accountcode, "");
+ strcpy(language, "");
+ strcpy(mohinterpret, "default");
+ strcpy(mohsuggest, "");
+ global_max_trunk_mtu = MAX_TRUNK_MTU;
+ trunkmaxsize = MAX_TRUNKDATA;
+ amaflags = 0;
+ delayreject = 0;
+ ast_clear_flag((&globalflags), IAX_NOTRANSFER);
+ ast_clear_flag((&globalflags), IAX_TRANSFERMEDIA);
+ ast_clear_flag((&globalflags), IAX_USEJITTERBUF);
+ ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF);
+ delete_users();
+}
+
+/*! \brief Load configuration */
+static int set_config(char *config_file, int reload)
+{
+ struct ast_config *cfg, *ucfg;
+ int capability=iax2_capability;
+ struct ast_variable *v;
+ char *cat;
+ const char *utype;
+ const char *tosval;
+ int format;
+ int portno = IAX_DEFAULT_PORTNO;
+ int x;
+ int mtuv;
+ struct iax2_user *user;
+ struct iax2_peer *peer;
+ struct ast_netsock *ns;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+#if 0
+ static unsigned short int last_port=0;
+#endif
+
+ cfg = ast_config_load(config_file, config_flags);
+
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+ ucfg = ast_config_load("users.conf", config_flags);
+ if (ucfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ /* Otherwise we need to reread both files */
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ cfg = ast_config_load(config_file, config_flags);
+ } else { /* iax.conf changed, gotta reread users.conf, too */
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ ucfg = ast_config_load("users.conf", config_flags);
+ }
+
+ if (reload) {
+ set_config_destroy();
+ }
+
+ /* Reset global codec prefs */
+ memset(&prefs, 0 , sizeof(struct ast_codec_pref));
+
+ /* Reset Global Flags */
+ memset(&globalflags, 0, sizeof(globalflags));
+ ast_set_flag(&globalflags, IAX_RTUPDATE);
+
+#ifdef SO_NO_CHECK
+ nochecksums = 0;
+#endif
+
+ min_reg_expire = IAX_DEFAULT_REG_EXPIRE;
+ max_reg_expire = IAX_DEFAULT_REG_EXPIRE;
+
+ maxauthreq = 3;
+
+ srvlookup = 0;
+
+ v = ast_variable_browse(cfg, "general");
+
+ /* Seed initial tos value */
+ tosval = ast_variable_retrieve(cfg, "general", "tos");
+ if (tosval) {
+ if (ast_str2tos(tosval, &tos))
+ ast_log(LOG_WARNING, "Invalid tos value, refer to QoS documentation\n");
+ }
+ /* Seed initial cos value */
+ tosval = ast_variable_retrieve(cfg, "general", "cos");
+ if (tosval) {
+ if (ast_str2cos(tosval, &cos))
+ ast_log(LOG_WARNING, "Invalid cos value, refer to QoS documentation\n");
+ }
+ while(v) {
+ if (!strcasecmp(v->name, "bindport")){
+ if (reload)
+ ast_log(LOG_NOTICE, "Ignoring bindport on reload\n");
+ else
+ portno = atoi(v->value);
+ } else if (!strcasecmp(v->name, "pingtime"))
+ ping_time = atoi(v->value);
+ else if (!strcasecmp(v->name, "iaxthreadcount")) {
+ if (reload) {
+ if (atoi(v->value) != iaxthreadcount)
+ ast_log(LOG_NOTICE, "Ignoring any changes to iaxthreadcount during reload\n");
+ } else {
+ iaxthreadcount = atoi(v->value);
+ if (iaxthreadcount < 1) {
+ ast_log(LOG_NOTICE, "iaxthreadcount must be at least 1.\n");
+ iaxthreadcount = 1;
+ } else if (iaxthreadcount > 256) {
+ ast_log(LOG_NOTICE, "limiting iaxthreadcount to 256\n");
+ iaxthreadcount = 256;
+ }
+ }
+ } else if (!strcasecmp(v->name, "iaxmaxthreadcount")) {
+ if (reload) {
+ AST_LIST_LOCK(&dynamic_list);
+ iaxmaxthreadcount = atoi(v->value);
+ AST_LIST_UNLOCK(&dynamic_list);
+ } else {
+ iaxmaxthreadcount = atoi(v->value);
+ if (iaxmaxthreadcount < 0) {
+ ast_log(LOG_NOTICE, "iaxmaxthreadcount must be at least 0.\n");
+ iaxmaxthreadcount = 0;
+ } else if (iaxmaxthreadcount > 256) {
+ ast_log(LOG_NOTICE, "Limiting iaxmaxthreadcount to 256\n");
+ iaxmaxthreadcount = 256;
+ }
+ }
+ } else if (!strcasecmp(v->name, "nochecksums")) {
+#ifdef SO_NO_CHECK
+ if (ast_true(v->value))
+ nochecksums = 1;
+ else
+ nochecksums = 0;
+#else
+ if (ast_true(v->value))
+ ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n");
+#endif
+ }
+ else if (!strcasecmp(v->name, "maxjitterbuffer"))
+ maxjitterbuffer = atoi(v->value);
+ else if (!strcasecmp(v->name, "resyncthreshold"))
+ resyncthreshold = atoi(v->value);
+ else if (!strcasecmp(v->name, "maxjitterinterps"))
+ maxjitterinterps = atoi(v->value);
+ else if (!strcasecmp(v->name, "jittertargetextra"))
+ jittertargetextra = atoi(v->value);
+ else if (!strcasecmp(v->name, "lagrqtime"))
+ lagrq_time = atoi(v->value);
+ else if (!strcasecmp(v->name, "maxregexpire"))
+ max_reg_expire = atoi(v->value);
+ else if (!strcasecmp(v->name, "minregexpire"))
+ min_reg_expire = atoi(v->value);
+ else if (!strcasecmp(v->name, "bindaddr")) {
+ if (reload) {
+ ast_log(LOG_NOTICE, "Ignoring bindaddr on reload\n");
+ } else {
+ if (!(ns = ast_netsock_bind(netsock, io, v->value, portno, tos, cos, socket_read, NULL))) {
+ ast_log(LOG_WARNING, "Unable apply binding to '%s' at line %d\n", v->value, v->lineno);
+ } else {
+ if (strchr(v->value, ':'))
+ ast_verb(2, "Binding IAX2 to '%s'\n", v->value);
+ else
+ ast_verb(2, "Binding IAX2 to '%s:%d'\n", v->value, portno);
+ if (defaultsockfd < 0)
+ defaultsockfd = ast_netsock_sockfd(ns);
+ ast_netsock_unref(ns);
+ }
+ }
+ } else if (!strcasecmp(v->name, "authdebug"))
+ authdebug = ast_true(v->value);
+ else if (!strcasecmp(v->name, "encryption"))
+ iax2_encryption = get_encrypt_methods(v->value);
+ else if (!strcasecmp(v->name, "transfer")) {
+ if (!strcasecmp(v->value, "mediaonly")) {
+ ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);
+ } else if (ast_true(v->value)) {
+ ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0);
+ } else
+ ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER);
+ } else if (!strcasecmp(v->name, "codecpriority")) {
+ if(!strcasecmp(v->value, "caller"))
+ ast_set_flag((&globalflags), IAX_CODEC_USER_FIRST);
+ else if(!strcasecmp(v->value, "disabled"))
+ ast_set_flag((&globalflags), IAX_CODEC_NOPREFS);
+ else if(!strcasecmp(v->value, "reqonly")) {
+ ast_set_flag((&globalflags), IAX_CODEC_NOCAP);
+ ast_set_flag((&globalflags), IAX_CODEC_NOPREFS);
+ }
+ } else if (!strcasecmp(v->name, "jitterbuffer"))
+ ast_set2_flag((&globalflags), ast_true(v->value), IAX_USEJITTERBUF);
+ else if (!strcasecmp(v->name, "forcejitterbuffer"))
+ ast_set2_flag((&globalflags), ast_true(v->value), IAX_FORCEJITTERBUF);
+ else if (!strcasecmp(v->name, "delayreject"))
+ delayreject = ast_true(v->value);
+ else if (!strcasecmp(v->name, "rtcachefriends"))
+ ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTCACHEFRIENDS);
+ else if (!strcasecmp(v->name, "rtignoreregexpire"))
+ ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTIGNOREREGEXPIRE);
+ else if (!strcasecmp(v->name, "rtupdate"))
+ ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTUPDATE);
+ else if (!strcasecmp(v->name, "trunktimestamps"))
+ ast_set2_flag(&globalflags, ast_true(v->value), IAX_TRUNKTIMESTAMPS);
+ else if (!strcasecmp(v->name, "rtautoclear")) {
+ int i = atoi(v->value);
+ if(i > 0)
+ global_rtautoclear = i;
+ else
+ i = 0;
+ ast_set2_flag((&globalflags), i || ast_true(v->value), IAX_RTAUTOCLEAR);
+ } else if (!strcasecmp(v->name, "trunkfreq")) {
+ trunkfreq = atoi(v->value);
+ if (trunkfreq < 10)
+ trunkfreq = 10;
+ } else if (!strcasecmp(v->name, "trunkmtu")) {
+ mtuv = atoi(v->value);
+ if (mtuv == 0 )
+ global_max_trunk_mtu = 0;
+ else if (mtuv >= 172 && mtuv < 4000)
+ global_max_trunk_mtu = mtuv;
+ else
+ ast_log(LOG_NOTICE, "trunkmtu value out of bounds (%d) at line %d\n",
+ mtuv, v->lineno);
+ } else if (!strcasecmp(v->name, "trunkmaxsize")) {
+ trunkmaxsize = atoi(v->value);
+ if (trunkmaxsize == 0)
+ trunkmaxsize = MAX_TRUNKDATA;
+ } else if (!strcasecmp(v->name, "autokill")) {
+ if (sscanf(v->value, "%d", &x) == 1) {
+ if (x >= 0)
+ autokill = x;
+ else
+ ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
+ } else if (ast_true(v->value)) {
+ autokill = DEFAULT_MAXMS;
+ } else {
+ autokill = 0;
+ }
+ } else if (!strcasecmp(v->name, "bandwidth")) {
+ if (!strcasecmp(v->value, "low")) {
+ capability = IAX_CAPABILITY_LOWBANDWIDTH;
+ } else if (!strcasecmp(v->value, "medium")) {
+ capability = IAX_CAPABILITY_MEDBANDWIDTH;
+ } else if (!strcasecmp(v->value, "high")) {
+ capability = IAX_CAPABILITY_FULLBANDWIDTH;
+ } else
+ ast_log(LOG_WARNING, "bandwidth must be either low, medium, or high\n");
+ } else if (!strcasecmp(v->name, "allow")) {
+ ast_parse_allow_disallow(&prefs, &capability, v->value, 1);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&prefs, &capability, v->value, 0);
+ } else if (!strcasecmp(v->name, "register")) {
+ iax2_register(v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "iaxcompat")) {
+ iaxcompat = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "regcontext")) {
+ ast_copy_string(regcontext, v->value, sizeof(regcontext));
+ /* Create context if it doesn't exist already */
+ if (!ast_context_find(regcontext))
+ ast_context_create(NULL, regcontext, "IAX2");
+ } else if (!strcasecmp(v->name, "tos")) {
+ if (ast_str2tos(v->value, &tos))
+ ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos")) {
+ if (ast_str2cos(v->value, &cos))
+ ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(accountcode, v->value, sizeof(accountcode));
+ } else if (!strcasecmp(v->name, "mohinterpret")) {
+ ast_copy_string(mohinterpret, v->value, sizeof(user->mohinterpret));
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_copy_string(mohsuggest, v->value, sizeof(user->mohsuggest));
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ format = ast_cdr_amaflags2int(v->value);
+ if (format < 0) {
+ ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
+ } else {
+ amaflags = format;
+ }
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(language, v->value, sizeof(language));
+ } else if (!strcasecmp(v->name, "maxauthreq")) {
+ maxauthreq = atoi(v->value);
+ if (maxauthreq < 0)
+ maxauthreq = 0;
+ } else if (!strcasecmp(v->name, "adsi")) {
+ adsi = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "srvlookup")) {
+ srvlookup = ast_true(v->value);
+ } /*else if (strcasecmp(v->name,"type")) */
+ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ v = v->next;
+ }
+
+ if (defaultsockfd < 0) {
+ if (!(ns = ast_netsock_bind(netsock, io, "0.0.0.0", portno, tos, cos, socket_read, NULL))) {
+ ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+ } else {
+ ast_verb(2, "Binding IAX2 to default address 0.0.0.0:%d\n", portno);
+ defaultsockfd = ast_netsock_sockfd(ns);
+ ast_netsock_unref(ns);
+ }
+ }
+ if (reload) {
+ ast_netsock_release(outsock);
+ outsock = ast_netsock_list_alloc();
+ if (!outsock) {
+ ast_log(LOG_ERROR, "Could not allocate outsock list.\n");
+ return -1;
+ }
+ ast_netsock_init(outsock);
+ }
+
+ if (min_reg_expire > max_reg_expire) {
+ ast_log(LOG_WARNING, "Minimum registration interval of %d is more than maximum of %d, resetting minimum to %d\n",
+ min_reg_expire, max_reg_expire, max_reg_expire);
+ min_reg_expire = max_reg_expire;
+ }
+ iax2_capability = capability;
+
+ if (ucfg) {
+ struct ast_variable *gen;
+ int genhasiax;
+ int genregisteriax;
+ const char *hasiax, *registeriax;
+
+ genhasiax = ast_true(ast_variable_retrieve(ucfg, "general", "hasiax"));
+ genregisteriax = ast_true(ast_variable_retrieve(ucfg, "general", "registeriax"));
+ gen = ast_variable_browse(ucfg, "general");
+ cat = ast_category_browse(ucfg, NULL);
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ hasiax = ast_variable_retrieve(ucfg, cat, "hasiax");
+ registeriax = ast_variable_retrieve(ucfg, cat, "registeriax");
+ if (ast_true(hasiax) || (!hasiax && genhasiax)) {
+ /* Start with general parameters, then specific parameters, user and peer */
+ user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0);
+ if (user) {
+ ao2_link(users, user);
+ user = user_unref(user);
+ }
+ peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
+ if (peer) {
+ if (ast_test_flag(peer, IAX_DYNAMIC))
+ reg_source_db(peer);
+ ao2_link(peers, peer);
+ peer = peer_unref(peer);
+ }
+ }
+ if (ast_true(registeriax) || (!registeriax && genregisteriax)) {
+ char tmp[256];
+ const char *host = ast_variable_retrieve(ucfg, cat, "host");
+ const char *username = ast_variable_retrieve(ucfg, cat, "username");
+ const char *secret = ast_variable_retrieve(ucfg, cat, "secret");
+ if (!host)
+ host = ast_variable_retrieve(ucfg, "general", "host");
+ if (!username)
+ username = ast_variable_retrieve(ucfg, "general", "username");
+ if (!secret)
+ secret = ast_variable_retrieve(ucfg, "general", "secret");
+ if (!ast_strlen_zero(username) && !ast_strlen_zero(host)) {
+ if (!ast_strlen_zero(secret))
+ snprintf(tmp, sizeof(tmp), "%s:%s@%s", username, secret, host);
+ else
+ snprintf(tmp, sizeof(tmp), "%s@%s", username, host);
+ iax2_register(tmp, 0);
+ }
+ }
+ }
+ cat = ast_category_browse(ucfg, cat);
+ }
+ ast_config_destroy(ucfg);
+ }
+
+ cat = ast_category_browse(cfg, NULL);
+ while(cat) {
+ if (strcasecmp(cat, "general")) {
+ utype = ast_variable_retrieve(cfg, cat, "type");
+ if (utype) {
+ if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
+ user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ if (user) {
+ ao2_link(users, user);
+ user = user_unref(user);
+ }
+ }
+ if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) {
+ peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ if (peer) {
+ if (ast_test_flag(peer, IAX_DYNAMIC))
+ reg_source_db(peer);
+ ao2_link(peers, peer);
+ peer = peer_unref(peer);
+ }
+ } else if (strcasecmp(utype, "user")) {
+ ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config_file);
+ }
+ } else
+ ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_config_destroy(cfg);
+ set_timing();
+ return 1;
+}
+
+static void poke_all_peers(void)
+{
+ struct ao2_iterator i;
+ struct iax2_peer *peer;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ iax2_poke_peer(peer, 0);
+ peer_unref(peer);
+ }
+}
+static int reload_config(void)
+{
+ char *config = "iax.conf";
+ struct iax2_registry *reg;
+
+ if (set_config(config, 1) > 0) {
+ prune_peers();
+ prune_users();
+ trunk_timed = trunk_untimed = 0;
+ trunk_nmaxmtu = trunk_maxmtu = 0;
+
+ AST_LIST_LOCK(&registrations);
+ AST_LIST_TRAVERSE(&registrations, reg, entry)
+ iax2_do_register(reg);
+ AST_LIST_UNLOCK(&registrations);
+
+ /* Qualify hosts, too */
+ poke_all_peers();
+ }
+
+ reload_firmware(0);
+ iax_provision_reload(1);
+
+ return 0;
+}
+
+static char *handle_cli_iax2_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 reload";
+ e->usage =
+ "Usage: iax2 reload\n"
+ " Reloads IAX configuration from iax.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ reload_config();
+
+ return CLI_SUCCESS;
+}
+
+static int reload(void)
+{
+ return reload_config();
+}
+
+static int cache_get_callno_locked(const char *data)
+{
+ struct sockaddr_in sin;
+ int x;
+ int callno;
+ struct iax_ie_data ied;
+ struct create_addr_info cai;
+ struct parsed_dial_string pds;
+ char *tmpstr;
+
+ for (x=0; x<IAX_MAX_CALLS; x++) {
+ /* Look for an *exact match* call. Once a call is negotiated, it can only
+ look up entries for a single context */
+ if (!ast_mutex_trylock(&iaxsl[x])) {
+ if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot))
+ return x;
+ ast_mutex_unlock(&iaxsl[x]);
+ }
+ }
+
+ /* No match found, we need to create a new one */
+
+ memset(&cai, 0, sizeof(cai));
+ memset(&ied, 0, sizeof(ied));
+ memset(&pds, 0, sizeof(pds));
+
+ tmpstr = ast_strdupa(data);
+ parse_dial_string(tmpstr, &pds);
+
+ /* Populate our address from the given */
+ if (create_addr(pds.peer, NULL, &sin, &cai))
+ return -1;
+
+ ast_debug(1, "peer: %s, username: %s, password: %s, context: %s\n",
+ pds.peer, pds.username, pds.password, pds.context);
+
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
+ if (callno < 1) {
+ ast_log(LOG_WARNING, "Unable to create call\n");
+ return -1;
+ }
+
+ ast_mutex_lock(&iaxsl[callno]);
+ ast_string_field_set(iaxs[callno], dproot, data);
+ iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH;
+
+ iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION);
+ iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, "TBD");
+ /* the string format is slightly different from a standard dial string,
+ because the context appears in the 'exten' position
+ */
+ if (pds.exten)
+ iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, pds.exten);
+ if (pds.username)
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, pds.username);
+ iax_ie_append_int(&ied, IAX_IE_FORMAT, IAX_CAPABILITY_FULLBANDWIDTH);
+ iax_ie_append_int(&ied, IAX_IE_CAPABILITY, IAX_CAPABILITY_FULLBANDWIDTH);
+ /* Keep password handy */
+ if (pds.password)
+ ast_string_field_set(iaxs[callno], secret, pds.password);
+ if (pds.key)
+ ast_string_field_set(iaxs[callno], outkey, pds.key);
+ /* Start the call going */
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+
+ return callno;
+}
+
+static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *data, const char *context, const char *exten, int priority)
+{
+ struct iax2_dpcache *dp = NULL;
+ struct timeval tv = ast_tvnow();
+ int x, com[2], timeout, old = 0, outfd, abort, callno;
+ struct ast_channel *c = NULL;
+ struct ast_frame *f = NULL;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&dpcache, dp, cache_list) {
+ if (ast_tvcmp(tv, dp->expiry) > 0) {
+ AST_LIST_REMOVE_CURRENT(cache_list);
+ if ((dp->flags & CACHE_FLAG_PENDING) || dp->callno)
+ ast_log(LOG_WARNING, "DP still has peer field or pending or callno (flags = %d, peer = blah, callno = %d)\n", dp->flags, dp->callno);
+ else
+ ast_free(dp);
+ continue;
+ }
+ if (!strcmp(dp->peercontext, data) && !strcmp(dp->exten, exten))
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (!dp) {
+ /* No matching entry. Create a new one. */
+ /* First, can we make a callno? */
+ if ((callno = cache_get_callno_locked(data)) < 0) {
+ ast_log(LOG_WARNING, "Unable to generate call for '%s'\n", data);
+ return NULL;
+ }
+ if (!(dp = ast_calloc(1, sizeof(*dp)))) {
+ ast_mutex_unlock(&iaxsl[callno]);
+ return NULL;
+ }
+ ast_copy_string(dp->peercontext, data, sizeof(dp->peercontext));
+ ast_copy_string(dp->exten, exten, sizeof(dp->exten));
+ dp->expiry = ast_tvnow();
+ dp->orig = dp->expiry;
+ /* Expires in 30 mins by default */
+ dp->expiry.tv_sec += iaxdefaultdpcache;
+ dp->flags = CACHE_FLAG_PENDING;
+ for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+ dp->waiters[x] = -1;
+ /* Insert into the lists */
+ AST_LIST_INSERT_TAIL(&dpcache, dp, cache_list);
+ AST_LIST_INSERT_TAIL(&iaxs[callno]->dpentries, dp, peer_list);
+ /* Send the request if we're already up */
+ if (ast_test_flag(&iaxs[callno]->state, IAX_STATE_STARTED))
+ iax2_dprequest(dp, callno);
+ ast_mutex_unlock(&iaxsl[callno]);
+ }
+
+ /* By here we must have a dp */
+ if (dp->flags & CACHE_FLAG_PENDING) {
+ /* Okay, here it starts to get nasty. We need a pipe now to wait
+ for a reply to come back so long as it's pending */
+ for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++) {
+ /* Find an empty slot */
+ if (dp->waiters[x] < 0)
+ break;
+ }
+ if (x >= sizeof(dp->waiters) / sizeof(dp->waiters[0])) {
+ ast_log(LOG_WARNING, "No more waiter positions available\n");
+ return NULL;
+ }
+ if (pipe(com)) {
+ ast_log(LOG_WARNING, "Unable to create pipe for comm\n");
+ return NULL;
+ }
+ dp->waiters[x] = com[1];
+ /* Okay, now we wait */
+ timeout = iaxdefaulttimeout * 1000;
+ /* Temporarily unlock */
+ AST_LIST_UNLOCK(&dpcache);
+ /* Defer any dtmf */
+ if (chan)
+ old = ast_channel_defer_dtmf(chan);
+ abort = 0;
+ while(timeout) {
+ c = ast_waitfor_nandfds(&chan, chan ? 1 : 0, &com[0], 1, NULL, &outfd, &timeout);
+ if (outfd > -1)
+ break;
+ if (!c)
+ continue;
+ if (!(f = ast_read(c))) {
+ abort = 1;
+ break;
+ }
+ ast_frfree(f);
+ }
+ if (!timeout) {
+ ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten);
+ }
+ AST_LIST_LOCK(&dpcache);
+ dp->waiters[x] = -1;
+ close(com[1]);
+ close(com[0]);
+ if (abort) {
+ /* Don't interpret anything, just abort. Not sure what th epoint
+ of undeferring dtmf on a hung up channel is but hey whatever */
+ if (!old && chan)
+ ast_channel_undefer_dtmf(chan);
+ return NULL;
+ }
+ if (!(dp->flags & CACHE_FLAG_TIMEOUT)) {
+ /* Now to do non-independent analysis the results of our wait */
+ if (dp->flags & CACHE_FLAG_PENDING) {
+ /* Still pending... It's a timeout. Wake everybody up. Consider it no longer
+ pending. Don't let it take as long to timeout. */
+ dp->flags &= ~CACHE_FLAG_PENDING;
+ dp->flags |= CACHE_FLAG_TIMEOUT;
+ /* Expire after only 60 seconds now. This is designed to help reduce backlog in heavily loaded
+ systems without leaving it unavailable once the server comes back online */
+ dp->expiry.tv_sec = dp->orig.tv_sec + 60;
+ for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+ if (dp->waiters[x] > -1)
+ write(dp->waiters[x], "asdf", 4);
+ }
+ }
+ /* Our caller will obtain the rest */
+ if (!old && chan)
+ ast_channel_undefer_dtmf(chan);
+ }
+ return dp;
+}
+
+/*! \brief Part of the IAX2 switch interface */
+static int iax2_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res = 0;
+ struct iax2_dpcache *dp = NULL;
+#if 0
+ ast_log(LOG_NOTICE, "iax2_exists: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
+#endif
+ if ((priority != 1) && (priority != 2))
+ return 0;
+
+ AST_LIST_LOCK(&dpcache);
+ if ((dp = find_cache(chan, data, context, exten, priority))) {
+ if (dp->flags & CACHE_FLAG_EXISTS)
+ res = 1;
+ } else {
+ ast_log(LOG_WARNING, "Unable to make DP cache\n");
+ }
+ AST_LIST_UNLOCK(&dpcache);
+
+ return res;
+}
+
+/*! \brief part of the IAX2 dial plan switch interface */
+static int iax2_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res = 0;
+ struct iax2_dpcache *dp = NULL;
+#if 0
+ ast_log(LOG_NOTICE, "iax2_canmatch: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
+#endif
+ if ((priority != 1) && (priority != 2))
+ return 0;
+
+ AST_LIST_LOCK(&dpcache);
+ if ((dp = find_cache(chan, data, context, exten, priority))) {
+ if (dp->flags & CACHE_FLAG_CANEXIST)
+ res = 1;
+ } else {
+ ast_log(LOG_WARNING, "Unable to make DP cache\n");
+ }
+ AST_LIST_UNLOCK(&dpcache);
+
+ return res;
+}
+
+/*! \brief Part of the IAX2 Switch interface */
+static int iax2_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res = 0;
+ struct iax2_dpcache *dp = NULL;
+#if 0
+ ast_log(LOG_NOTICE, "iax2_matchmore: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
+#endif
+ if ((priority != 1) && (priority != 2))
+ return 0;
+
+ AST_LIST_LOCK(&dpcache);
+ if ((dp = find_cache(chan, data, context, exten, priority))) {
+ if (dp->flags & CACHE_FLAG_MATCHMORE)
+ res = 1;
+ } else {
+ ast_log(LOG_WARNING, "Unable to make DP cache\n");
+ }
+ AST_LIST_UNLOCK(&dpcache);
+
+ return res;
+}
+
+/*! \brief Execute IAX2 dialplan switch */
+static int iax2_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ char odata[256];
+ char req[256];
+ char *ncontext;
+ struct iax2_dpcache *dp = NULL;
+ struct ast_app *dial = NULL;
+#if 0
+ ast_log(LOG_NOTICE, "iax2_exec: con: %s, exten: %s, pri: %d, cid: %s, data: %s, newstack: %d\n", context, exten, priority, callerid ? callerid : "<unknown>", data, newstack);
+#endif
+ if (priority == 2) {
+ /* Indicate status, can be overridden in dialplan */
+ const char *dialstatus = pbx_builtin_getvar_helper(chan, "DIALSTATUS");
+ if (dialstatus) {
+ dial = pbx_findapp(dialstatus);
+ if (dial)
+ pbx_exec(chan, dial, "");
+ }
+ return -1;
+ } else if (priority != 1)
+ return -1;
+
+ AST_LIST_LOCK(&dpcache);
+ if ((dp = find_cache(chan, data, context, exten, priority))) {
+ if (dp->flags & CACHE_FLAG_EXISTS) {
+ ast_copy_string(odata, data, sizeof(odata));
+ ncontext = strchr(odata, '/');
+ if (ncontext) {
+ *ncontext = '\0';
+ ncontext++;
+ snprintf(req, sizeof(req), "IAX2/%s/%s@%s", odata, exten, ncontext);
+ } else {
+ snprintf(req, sizeof(req), "IAX2/%s/%s", odata, exten);
+ }
+ ast_verb(3, "Executing Dial('%s')\n", req);
+ } else {
+ AST_LIST_UNLOCK(&dpcache);
+ ast_log(LOG_WARNING, "Can't execute nonexistent extension '%s[@%s]' in data '%s'\n", exten, context, data);
+ return -1;
+ }
+ }
+ AST_LIST_UNLOCK(&dpcache);
+
+ if ((dial = pbx_findapp("Dial")))
+ return pbx_exec(chan, dial, req);
+ else
+ ast_log(LOG_WARNING, "No dial application registered\n");
+
+ return -1;
+}
+
+static int function_iaxpeer(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct iax2_peer *peer;
+ char *peername, *colname;
+
+ peername = ast_strdupa(data);
+
+ /* if our channel, return the IP address of the endpoint of current channel */
+ if (!strcmp(peername,"CURRENTCHANNEL")) {
+ unsigned short callno;
+ if (chan->tech != &iax2_tech)
+ return -1;
+ callno = PTR_TO_CALLNO(chan->tech_pvt);
+ ast_copy_string(buf, iaxs[callno]->addr.sin_addr.s_addr ? ast_inet_ntoa(iaxs[callno]->addr.sin_addr) : "", len);
+ return 0;
+ }
+
+ if ((colname = strchr(peername, ':'))) /*! \todo : will be removed after the 1.4 relese */
+ *colname++ = '\0';
+ else if ((colname = strchr(peername, '|')))
+ *colname++ = '\0';
+ else
+ colname = "ip";
+
+ if (!(peer = find_peer(peername, 1)))
+ return -1;
+
+ if (!strcasecmp(colname, "ip")) {
+ ast_copy_string(buf, peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "", len);
+ } else if (!strcasecmp(colname, "status")) {
+ peer_status(peer, buf, len);
+ } else if (!strcasecmp(colname, "mailbox")) {
+ ast_copy_string(buf, peer->mailbox, len);
+ } else if (!strcasecmp(colname, "context")) {
+ ast_copy_string(buf, peer->context, len);
+ } else if (!strcasecmp(colname, "expire")) {
+ snprintf(buf, len, "%d", peer->expire);
+ } else if (!strcasecmp(colname, "dynamic")) {
+ ast_copy_string(buf, (ast_test_flag(peer, IAX_DYNAMIC) ? "yes" : "no"), len);
+ } else if (!strcasecmp(colname, "callerid_name")) {
+ ast_copy_string(buf, peer->cid_name, len);
+ } else if (!strcasecmp(colname, "callerid_num")) {
+ ast_copy_string(buf, peer->cid_num, len);
+ } else if (!strcasecmp(colname, "codecs")) {
+ ast_getformatname_multiple(buf, len -1, peer->capability);
+ } else if (!strncasecmp(colname, "codec[", 6)) {
+ char *codecnum, *ptr;
+ int index = 0, codec = 0;
+
+ codecnum = strchr(colname, '[');
+ *codecnum = '\0';
+ codecnum++;
+ if ((ptr = strchr(codecnum, ']'))) {
+ *ptr = '\0';
+ }
+ index = atoi(codecnum);
+ if((codec = ast_codec_pref_index(&peer->prefs, index))) {
+ ast_copy_string(buf, ast_getformatname(codec), len);
+ }
+ }
+
+ peer_unref(peer);
+
+ return 0;
+}
+
+struct ast_custom_function iaxpeer_function = {
+ .name = "IAXPEER",
+ .synopsis = "Gets IAX peer information",
+ .syntax = "IAXPEER(<peername|CURRENTCHANNEL>[|item])",
+ .read = function_iaxpeer,
+ .desc = "If peername specified, valid items are:\n"
+ "- ip (default) The IP address.\n"
+ "- status The peer's status (if qualify=yes)\n"
+ "- mailbox The configured mailbox.\n"
+ "- context The configured context.\n"
+ "- expire The epoch time of the next expire.\n"
+ "- dynamic Is it dynamic? (yes/no).\n"
+ "- callerid_name The configured Caller ID name.\n"
+ "- callerid_num The configured Caller ID number.\n"
+ "- codecs The configured codecs.\n"
+ "- codec[x] Preferred codec index number 'x' (beginning with zero).\n"
+ "\n"
+ "If CURRENTCHANNEL specified, returns IP address of current channel\n"
+ "\n"
+};
+
+static int acf_channel_write(struct ast_channel *chan, const char *function, char *args, const char *value)
+{
+ struct chan_iax2_pvt *pvt;
+ unsigned int callno;
+ int res = 0;
+
+ if (!chan || chan->tech != &iax2_tech) {
+ ast_log(LOG_ERROR, "This function requires a valid IAX2 channel\n");
+ return -1;
+ }
+
+ callno = PTR_TO_CALLNO(chan->tech_pvt);
+ ast_mutex_lock(&iaxsl[callno]);
+ if (!(pvt = iaxs[callno])) {
+ ast_mutex_unlock(&iaxsl[callno]);
+ return -1;
+ }
+
+ if (!strcasecmp(args, "osptoken"))
+ ast_string_field_set(pvt, osptoken, value);
+ else
+ res = -1;
+
+ ast_mutex_unlock(&iaxsl[callno]);
+
+ return res;
+}
+
+static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *args, char *buf, size_t buflen)
+{
+ struct chan_iax2_pvt *pvt;
+ unsigned int callno;
+ int res = 0;
+
+ if (!chan || chan->tech != &iax2_tech) {
+ ast_log(LOG_ERROR, "This function requires a valid IAX2 channel\n");
+ return -1;
+ }
+
+ callno = PTR_TO_CALLNO(chan->tech_pvt);
+ ast_mutex_lock(&iaxsl[callno]);
+ if (!(pvt = iaxs[callno])) {
+ ast_mutex_unlock(&iaxsl[callno]);
+ return -1;
+ }
+
+ if (!strcasecmp(args, "osptoken"))
+ ast_copy_string(buf, pvt->osptoken, buflen);
+ else
+ res = -1;
+
+ ast_mutex_unlock(&iaxsl[callno]);
+
+ return res;
+}
+
+/*! \brief Part of the device state notification system ---*/
+static int iax2_devicestate(void *data)
+{
+ struct parsed_dial_string pds;
+ char *tmp = ast_strdupa(data);
+ struct iax2_peer *p;
+ int res = AST_DEVICE_INVALID;
+
+ memset(&pds, 0, sizeof(pds));
+ parse_dial_string(tmp, &pds);
+ if (ast_strlen_zero(pds.peer))
+ return res;
+
+ ast_debug(3, "Checking device state for device %s\n", pds.peer);
+
+ /* SLD: FIXME: second call to find_peer during registration */
+ if (!(p = find_peer(pds.peer, 1)))
+ return res;
+
+ res = AST_DEVICE_UNAVAILABLE;
+ ast_debug(3, "iax2_devicestate: Found peer. What's device state of %s? addr=%d, defaddr=%d maxms=%d, lastms=%d\n",
+ pds.peer, p->addr.sin_addr.s_addr, p->defaddr.sin_addr.s_addr, p->maxms, p->lastms);
+
+ if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
+ (!p->maxms || ((p->lastms > -1) && (p->historicms <= p->maxms)))) {
+ /* Peer is registered, or have default IP address
+ and a valid registration */
+ if (p->historicms == 0 || p->historicms <= p->maxms)
+ /* let the core figure out whether it is in use or not */
+ res = AST_DEVICE_UNKNOWN;
+ }
+
+ peer_unref(p);
+
+ return res;
+}
+
+static struct ast_switch iax2_switch =
+{
+ name: "IAX2",
+ description: "IAX Remote Dialplan Switch",
+ exists: iax2_exists,
+ canmatch: iax2_canmatch,
+ exec: iax2_exec,
+ matchmore: iax2_matchmore,
+};
+
+/*
+ { { "iax2", "show", "cache", NULL },
+ iax2_show_cache, "Display IAX cached dialplan",
+ show_cache_usage },
+
+ { { "iax2", "show", "channels", NULL },
+ iax2_show_channels, "List active IAX channels",
+ show_channels_usage },
+
+ { { "iax2", "show", "firmware", NULL },
+ iax2_show_firmware, "List available IAX firmwares",
+ show_firmware_usage },
+
+ { { "iax2", "show", "netstats", NULL },
+ iax2_show_netstats, "List active IAX channel netstats",
+ show_netstats_usage },
+
+ { { "iax2", "show", "peers", NULL },
+ iax2_show_peers, "List defined IAX peers",
+ show_peers_usage },
+
+ { { "iax2", "show", "registry", NULL },
+ iax2_show_registry, "Display IAX registration status",
+ show_reg_usage },
+
+ { { "iax2", "show", "stats", NULL },
+ iax2_show_stats, "Display IAX statistics",
+ show_stats_usage },
+
+ { { "iax2", "show", "threads", NULL },
+ iax2_show_threads, "Display IAX helper thread info",
+ show_threads_usage },
+
+ { { "iax2", "unregister", NULL },
+ iax2_unregister, "Unregister (force expiration) an IAX2 peer from the registry",
+ unregister_usage, complete_iax2_unregister },
+
+ { { "iax2", "set", "mtu", NULL },
+ iax2_set_mtu, "Set the IAX systemwide trunking MTU",
+ set_mtu_usage, NULL, NULL },
+
+ { { "iax2", "show", "users", NULL },
+ iax2_show_users, "List defined IAX users",
+ show_users_usage },
+
+ { { "iax2", "prune", "realtime", NULL },
+ iax2_prune_realtime, "Prune a cached realtime lookup",
+ prune_realtime_usage, complete_iax2_show_peer },
+
+ { { "iax2", "reload", NULL },
+ iax2_reload, "Reload IAX configuration",
+ iax2_reload_usage },
+
+ { { "iax2", "show", "peer", NULL },
+ iax2_show_peer, "Show details on specific IAX peer",
+ show_peer_usage, complete_iax2_show_peer },
+
+ { { "iax2", "set", "debug", NULL },
+ iax2_do_debug, "Enable IAX debugging",
+ debug_usage },
+
+ { { "iax2", "set", "debug", "trunk", NULL },
+ iax2_do_trunk_debug, "Enable IAX trunk debugging",
+ debug_trunk_usage },
+
+ { { "iax2", "set", "debug", "jb", NULL },
+ iax2_do_jb_debug, "Enable IAX jitterbuffer debugging",
+ debug_jb_usage },
+
+ { { "iax2", "set", "debug", "off", NULL },
+ iax2_no_debug, "Disable IAX debugging",
+ no_debug_usage },
+
+ { { "iax2", "set", "debug", "trunk", "off", NULL },
+ iax2_no_trunk_debug, "Disable IAX trunk debugging",
+ no_debug_trunk_usage },
+
+ { { "iax2", "set", "debug", "jb", "off", NULL },
+ iax2_no_jb_debug, "Disable IAX jitterbuffer debugging",
+ no_debug_jb_usage },
+
+ { { "iax2", "test", "losspct", NULL },
+ iax2_test_losspct, "Set IAX2 incoming frame loss percentage",
+ iax2_test_losspct_usage },
+
+ { { "iax2", "provision", NULL },
+ iax2_prov_cmd, "Provision an IAX device",
+ show_prov_usage, iax2_prov_complete_template_3rd },
+
+#ifdef IAXTESTS
+ { { "iax2", "test", "late", NULL },
+ iax2_test_late, "Test the receipt of a late frame",
+ iax2_test_late_usage },
+
+ { { "iax2", "test", "resync", NULL },
+ iax2_test_resync, "Test a resync in received timestamps",
+ iax2_test_resync_usage },
+
+ { { "iax2", "test", "jitter", NULL },
+ iax2_test_jitter, "Simulates jitter for testing",
+ iax2_test_jitter_usage },
+#endif
+*/
+
+static struct ast_cli_entry cli_iax2[] = {
+ AST_CLI_DEFINE(handle_cli_iax2_provision, "Provision an IAX device"),
+ AST_CLI_DEFINE(handle_cli_iax2_prune_realtime, "Prune a cached realtime lookup"),
+ AST_CLI_DEFINE(handle_cli_iax2_reload, "Reload IAX configuration"),
+ AST_CLI_DEFINE(handle_cli_iax2_set_mtu, "Set the IAX systemwide trunking MTU"),
+ AST_CLI_DEFINE(handle_cli_iax2_set_debug, "Enable IAX debugging"),
+ AST_CLI_DEFINE(handle_cli_iax2_set_debug_trunk, "Enable IAX trunk debugging"),
+ AST_CLI_DEFINE(handle_cli_iax2_set_debug_jb, "Enable IAX jitterbuffer debugging"),
+ AST_CLI_DEFINE(handle_cli_iax2_set_debug_off, "Disable IAX debugging"),
+ AST_CLI_DEFINE(handle_cli_iax2_set_debug_trunk_off, "Disable IAX trunk debugging"),
+ AST_CLI_DEFINE(handle_cli_iax2_set_debug_jb_off, "Disable IAX jitterbuffer debugging"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_cache, "Display IAX cached dialplan"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_channels, "List active IAX channels"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_firmware, "List available IAX firmware"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_netstats, "List active IAX channel netstats"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_peer, "Show details on specific IAX peer"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_peers, "List defined IAX peers"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_registry, "Display IAX registration status"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_stats, "Display IAX statistics"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_threads, "Display IAX helper thread info"),
+ AST_CLI_DEFINE(handle_cli_iax2_show_users, "List defined IAX users"),
+ AST_CLI_DEFINE(handle_cli_iax2_test_losspct, "Set IAX2 incoming frame loss percentage"),
+ AST_CLI_DEFINE(handle_cli_iax2_unregister, "Unregister (force expiration) an IAX2 peer from the registry"),
+#ifdef IAXTESTS
+ AST_CLI_DEFINE(handle_cli_iax2_test_jitter, "Simulates jitter for testing"),
+ AST_CLI_DEFINE(handle_cli_iax2_test_late, "Test the receipt of a late frame"),
+ AST_CLI_DEFINE(handle_cli_iax2_test_resync, "Test a resync in received timestamps"),
+#endif /* IAXTESTS */
+};
+
+static int __unload_module(void)
+{
+ struct iax2_thread *thread = NULL;
+ struct ast_context *con;
+ int x;
+
+ /* Make sure threads do not hold shared resources when they are canceled */
+
+ /* Grab the sched lock resource to keep it away from threads about to die */
+ /* Cancel the network thread, close the net socket */
+ if (netthreadid != AST_PTHREADT_NULL) {
+ AST_LIST_LOCK(&frame_queue);
+ ast_mutex_lock(&sched_lock);
+ pthread_cancel(netthreadid);
+ ast_cond_signal(&sched_cond);
+ ast_mutex_unlock(&sched_lock); /* Release the schedule lock resource */
+ AST_LIST_UNLOCK(&frame_queue);
+ pthread_join(netthreadid, NULL);
+ }
+ if (schedthreadid != AST_PTHREADT_NULL) {
+ ast_mutex_lock(&sched_lock);
+ pthread_cancel(schedthreadid);
+ ast_cond_signal(&sched_cond);
+ ast_mutex_unlock(&sched_lock);
+ pthread_join(schedthreadid, NULL);
+ }
+
+ /* Call for all threads to halt */
+ AST_LIST_LOCK(&idle_list);
+ while ((thread = AST_LIST_REMOVE_HEAD(&idle_list, list)))
+ pthread_cancel(thread->threadid);
+ AST_LIST_UNLOCK(&idle_list);
+
+ AST_LIST_LOCK(&active_list);
+ while ((thread = AST_LIST_REMOVE_HEAD(&active_list, list)))
+ pthread_cancel(thread->threadid);
+ AST_LIST_UNLOCK(&active_list);
+
+ AST_LIST_LOCK(&dynamic_list);
+ while ((thread = AST_LIST_REMOVE_HEAD(&dynamic_list, list)))
+ pthread_cancel(thread->threadid);
+ AST_LIST_UNLOCK(&dynamic_list);
+
+ /* Wait for threads to exit */
+ while(0 < iaxactivethreadcount)
+ usleep(10000);
+
+ ast_netsock_release(netsock);
+ ast_netsock_release(outsock);
+ for (x = 0; x < IAX_MAX_CALLS; x++) {
+ if (iaxs[x])
+ iax2_destroy(x);
+ }
+ ast_manager_unregister( "IAXpeers" );
+ ast_manager_unregister( "IAXpeerlist" );
+ ast_manager_unregister( "IAXnetstats" );
+ ast_unregister_application(papp);
+ ast_cli_unregister_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry));
+ ast_unregister_switch(&iax2_switch);
+ ast_channel_unregister(&iax2_tech);
+ delete_users();
+ iax_provision_unload();
+ sched_context_destroy(sched);
+ reload_firmware(1);
+
+ for (x = 0; x < IAX_MAX_CALLS; x++)
+ ast_mutex_destroy(&iaxsl[x]);
+
+ ao2_ref(peers, -1);
+ ao2_ref(users, -1);
+
+ con = ast_context_find(regcontext);
+ if (con)
+ ast_context_destroy(con, "IAX2");
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ ast_custom_function_unregister(&iaxpeer_function);
+ ast_custom_function_unregister(&iaxvar_function);
+ return __unload_module();
+}
+
+static int peer_set_sock_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ if (peer->sockfd < 0)
+ peer->sockfd = defaultsockfd;
+
+ return 0;
+}
+
+/*! \brief Load IAX2 module, load configuraiton ---*/
+static int load_module(void)
+{
+ char *config = "iax.conf";
+ int x = 0;
+ struct iax2_registry *reg = NULL;
+
+ peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb);
+ if (!peers)
+ return AST_MODULE_LOAD_FAILURE;
+ users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb);
+ if (!users) {
+ ao2_ref(peers, -1);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ ast_custom_function_register(&iaxpeer_function);
+ ast_custom_function_register(&iaxvar_function);
+
+ iax_set_output(iax_debug_output);
+ iax_set_error(iax_error_output);
+ jb_setoutput(jb_error_output, jb_warning_output, NULL);
+
+#ifdef HAVE_ZAPTEL
+#ifdef ZT_TIMERACK
+ timingfd = open("/dev/zap/timer", O_RDWR);
+ if (timingfd < 0)
+#endif
+ timingfd = open("/dev/zap/pseudo", O_RDWR);
+ if (timingfd < 0)
+ ast_log(LOG_WARNING, "Unable to open IAX timing interface: %s\n", strerror(errno));
+#endif
+
+ memset(iaxs, 0, sizeof(iaxs));
+
+ for (x=0;x<IAX_MAX_CALLS;x++)
+ ast_mutex_init(&iaxsl[x]);
+
+ ast_cond_init(&sched_cond, NULL);
+
+ if (!(sched = sched_context_create())) {
+ ast_log(LOG_ERROR, "Failed to create scheduler context\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (!(io = io_context_create())) {
+ ast_log(LOG_ERROR, "Failed to create I/O context\n");
+ sched_context_destroy(sched);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (!(netsock = ast_netsock_list_alloc())) {
+ ast_log(LOG_ERROR, "Failed to create netsock list\n");
+ io_context_destroy(io);
+ sched_context_destroy(sched);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ast_netsock_init(netsock);
+
+ outsock = ast_netsock_list_alloc();
+ if (!outsock) {
+ ast_log(LOG_ERROR, "Could not allocate outsock list.\n");
+ io_context_destroy(io);
+ sched_context_destroy(sched);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ast_netsock_init(outsock);
+
+ ast_cli_register_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry));
+
+ ast_register_application(papp, iax2_prov_app, psyn, pdescrip);
+
+ ast_manager_register( "IAXpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peers, "List IAX Peers" );
+ ast_manager_register( "IAXpeerlist", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peer_list, "List IAX Peers" );
+ ast_manager_register( "IAXnetstats", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_netstats, "Show IAX Netstats" );
+
+ if(set_config(config, 0) == -1)
+ return AST_MODULE_LOAD_DECLINE;
+
+ if (ast_channel_register(&iax2_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", "IAX2");
+ __unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (ast_register_switch(&iax2_switch))
+ ast_log(LOG_ERROR, "Unable to register IAX switch\n");
+
+ if (start_network_thread()) {
+ ast_log(LOG_ERROR, "Unable to start network thread\n");
+ __unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ } else
+ ast_verb(2, "IAX Ready and Listening\n");
+
+ AST_LIST_LOCK(&registrations);
+ AST_LIST_TRAVERSE(&registrations, reg, entry)
+ iax2_do_register(reg);
+ AST_LIST_UNLOCK(&registrations);
+
+ ao2_callback(peers, 0, peer_set_sock_cb, NULL);
+ ao2_callback(peers, 0, iax2_poke_peer_cb, NULL);
+
+
+ reload_firmware(0);
+ iax_provision_reload(0);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Inter Asterisk eXchange (Ver 2)",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/channels/chan_jingle.c b/trunk/channels/chan_jingle.c
new file mode 100644
index 000000000..87633978b
--- /dev/null
+++ b/trunk/channels/chan_jingle.c
@@ -0,0 +1,1862 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matt O'Gorman <mogorman@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
+ *
+ * \author Matt O'Gorman <mogorman@digium.com>
+ *
+ * \brief Jingle Channel Driver
+ *
+ * \extref Iksemel http://iksemel.jabberstudio.org/
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>iksemel</depend>
+ <depend>res_jabber</depend>
+ <use>openssl</use>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/signal.h>
+#include <iksemel.h>
+#include <pthread.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/manager.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/astobj.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/jabber.h"
+#include "asterisk/jingle.h"
+
+#define JINGLE_CONFIG "jingle.conf"
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+enum jingle_protocol {
+ AJI_PROTOCOL_UDP,
+ AJI_PROTOCOL_SSLTCP,
+};
+
+enum jingle_connect_type {
+ AJI_CONNECT_HOST,
+ AJI_CONNECT_PRFLX,
+ AJI_CONNECT_RELAY,
+ AJI_CONNECT_SRFLX,
+};
+
+struct jingle_pvt {
+ ast_mutex_t lock; /*!< Channel private lock */
+ time_t laststun;
+ struct jingle *parent; /*!< Parent client */
+ char sid[100];
+ char them[AJI_MAX_JIDLEN];
+ char ring[10]; /*!< Message ID of ring */
+ iksrule *ringrule; /*!< Rule for matching RING request */
+ int initiator; /*!< If we're the initiator */
+ int alreadygone;
+ int capability;
+ struct ast_codec_pref prefs;
+ struct jingle_candidate *theircandidates;
+ struct jingle_candidate *ourcandidates;
+ char cid_num[80]; /*!< Caller ID num */
+ char cid_name[80]; /*!< Caller ID name */
+ char exten[80]; /*!< Called extension */
+ struct ast_channel *owner; /*!< Master Channel */
+ struct ast_rtp *rtp; /*!< RTP audio session */
+ struct ast_rtp *vrtp; /*!< RTP video session */
+ int jointcapability; /*!< Supported capability at both ends (codecs ) */
+ int peercapability;
+ struct jingle_pvt *next; /* Next entity */
+};
+
+struct jingle_candidate {
+ unsigned int component; /*!< ex. : 1 for RTP, 2 for RTCP */
+ unsigned int foundation; /*!< Function of IP, protocol, type */
+ unsigned int generation;
+ char ip[16];
+ unsigned int network;
+ unsigned int port;
+ unsigned int priority;
+ enum jingle_protocol protocol;
+ char password[100];
+ enum jingle_connect_type type;
+ char ufrag[100];
+ unsigned int preference;
+ struct jingle_candidate *next;
+};
+
+struct jingle {
+ ASTOBJ_COMPONENTS(struct jingle);
+ struct aji_client *connection;
+ struct aji_buddy *buddy;
+ struct jingle_pvt *p;
+ struct ast_codec_pref prefs;
+ int amaflags; /*!< AMA Flags */
+ char user[100];
+ char context[100];
+ char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
+ int capability;
+ ast_group_t callgroup; /*!< Call group */
+ ast_group_t pickupgroup; /*!< Pickup group */
+ int callingpres; /*!< Calling presentation */
+ int allowguest;
+ char language[MAX_LANGUAGE]; /*!< Default language for prompts */
+ char musicclass[MAX_MUSICCLASS]; /*!< Music on Hold class */
+};
+
+struct jingle_container {
+ ASTOBJ_CONTAINER_COMPONENTS(struct jingle);
+};
+
+static const char desc[] = "Jingle Channel";
+static const char type[] = "Jingle";
+
+static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263;
+
+AST_MUTEX_DEFINE_STATIC(jinglelock); /*!< Protect the interface list (of jingle_pvt's) */
+
+/* Forward declarations */
+static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause);
+static int jingle_digit_begin(struct ast_channel *ast, char digit);
+static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int jingle_call(struct ast_channel *ast, char *dest, int timeout);
+static int jingle_hangup(struct ast_channel *ast);
+static int jingle_answer(struct ast_channel *ast);
+static int jingle_newcall(struct jingle *client, ikspak *pak);
+static struct ast_frame *jingle_read(struct ast_channel *ast);
+static int jingle_write(struct ast_channel *ast, struct ast_frame *f);
+static int jingle_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int jingle_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
+static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, const char *sid);
+static char *jingle_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *jingle_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+/*----- RTP interface functions */
+static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp,
+ struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active);
+static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static int jingle_get_codec(struct ast_channel *chan);
+
+/*! \brief PBX interface structure for channel registration */
+static const struct ast_channel_tech jingle_tech = {
+ .type = "Jingle",
+ .description = "Jingle Channel Driver",
+ .capabilities = AST_FORMAT_AUDIO_MASK,
+ .requester = jingle_request,
+ .send_digit_begin = jingle_digit_begin,
+ .send_digit_end = jingle_digit_end,
+ .bridge = ast_rtp_bridge,
+ .call = jingle_call,
+ .hangup = jingle_hangup,
+ .answer = jingle_answer,
+ .read = jingle_read,
+ .write = jingle_write,
+ .exception = jingle_read,
+ .indicate = jingle_indicate,
+ .fixup = jingle_fixup,
+ .send_html = jingle_sendhtml,
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
+};
+
+static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */
+
+static struct sched_context *sched; /*!< The scheduling context */
+static struct io_context *io; /*!< The IO context */
+static struct in_addr __ourip;
+
+
+/*! \brief RTP driver interface */
+static struct ast_rtp_protocol jingle_rtp = {
+ type: "Jingle",
+ get_rtp_info: jingle_get_rtp_peer,
+ set_rtp_peer: jingle_set_rtp_peer,
+ get_codec: jingle_get_codec,
+};
+
+static struct ast_cli_entry jingle_cli[] = {
+ AST_CLI_DEFINE(jingle_do_reload, "Reload Jingle configuration"),
+ AST_CLI_DEFINE(jingle_show_channels, "Show Jingle channels"),
+};
+
+
+static char externip[16];
+
+static struct jingle_container jingle_list;
+
+static void jingle_member_destroy(struct jingle *obj)
+{
+ ast_free(obj);
+}
+
+static struct jingle *find_jingle(char *name, char *connection)
+{
+ struct jingle *jingle = NULL;
+
+ jingle = ASTOBJ_CONTAINER_FIND(&jingle_list, name);
+ if (!jingle && strchr(name, '@'))
+ jingle = ASTOBJ_CONTAINER_FIND_FULL(&jingle_list, name, user,,, strcasecmp);
+
+ if (!jingle) { /* guest call */
+ ASTOBJ_CONTAINER_TRAVERSE(&jingle_list, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ if (!strcasecmp(iterator->name, "guest")) {
+ if (!strcasecmp(iterator->connection->jid->partial, connection)) {
+ jingle = iterator;
+ } else if (!strcasecmp(iterator->connection->name, connection)) {
+ jingle = iterator;
+ }
+ }
+ ASTOBJ_UNLOCK(iterator);
+
+ if (jingle)
+ break;
+ });
+
+ }
+ return jingle;
+}
+
+
+static void add_codec_to_answer(const struct jingle_pvt *p, int codec, iks *dcodecs)
+{
+ char *format = ast_getformatname(codec);
+
+ if (!strcasecmp("ulaw", format)) {
+ iks *payload_eg711u, *payload_pcmu;
+ payload_pcmu = iks_new("payload-type");
+ iks_insert_attrib(payload_pcmu, "id", "0");
+ iks_insert_attrib(payload_pcmu, "name", "PCMU");
+ payload_eg711u = iks_new("payload-type");
+ iks_insert_attrib(payload_eg711u, "id", "100");
+ iks_insert_attrib(payload_eg711u, "name", "EG711U");
+ iks_insert_node(dcodecs, payload_pcmu);
+ iks_insert_node(dcodecs, payload_eg711u);
+ }
+ if (!strcasecmp("alaw", format)) {
+ iks *payload_eg711a;
+ iks *payload_pcma = iks_new("payload-type");
+ iks_insert_attrib(payload_pcma, "id", "8");
+ iks_insert_attrib(payload_pcma, "name", "PCMA");
+ payload_eg711a = iks_new("payload-type");
+ iks_insert_attrib(payload_eg711a, "id", "101");
+ iks_insert_attrib(payload_eg711a, "name", "EG711A");
+ iks_insert_node(dcodecs, payload_pcma);
+ iks_insert_node(dcodecs, payload_eg711a);
+ }
+ if (!strcasecmp("ilbc", format)) {
+ iks *payload_ilbc = iks_new("payload-type");
+ iks_insert_attrib(payload_ilbc, "id", "97");
+ iks_insert_attrib(payload_ilbc, "name", "iLBC");
+ iks_insert_node(dcodecs, payload_ilbc);
+ }
+ if (!strcasecmp("g723", format)) {
+ iks *payload_g723 = iks_new("payload-type");
+ iks_insert_attrib(payload_g723, "id", "4");
+ iks_insert_attrib(payload_g723, "name", "G723");
+ iks_insert_node(dcodecs, payload_g723);
+ }
+ ast_rtp_lookup_code(p->rtp, 1, codec);
+}
+
+static int jingle_accept_call(struct jingle *client, struct jingle_pvt *p)
+{
+ struct jingle_pvt *tmp = client->p;
+ struct aji_client *c = client->connection;
+ iks *iq, *jingle, *dcodecs, *payload_red, *payload_audio, *payload_cn;
+ int x;
+ int pref_codec = 0;
+ int alreadysent = 0;
+
+ if (p->initiator)
+ return 1;
+
+ iq = iks_new("iq");
+ jingle = iks_new(JINGLE_NODE);
+ dcodecs = iks_new("description");
+ if (iq && jingle && dcodecs) {
+ iks_insert_attrib(dcodecs, "xmlns", JINGLE_AUDIO_RTP_NS);
+
+ for (x = 0; x < 32; x++) {
+ if (!(pref_codec = ast_codec_pref_index(&client->prefs, x)))
+ break;
+ if (!(client->capability & pref_codec))
+ continue;
+ if (alreadysent & pref_codec)
+ continue;
+ add_codec_to_answer(p, pref_codec, dcodecs);
+ alreadysent |= pref_codec;
+ }
+ payload_red = iks_new("payload-type");
+ iks_insert_attrib(payload_red, "id", "117");
+ iks_insert_attrib(payload_red, "name", "red");
+ payload_audio = iks_new("payload-type");
+ iks_insert_attrib(payload_audio, "id", "106");
+ iks_insert_attrib(payload_audio, "name", "audio/telephone-event");
+ payload_cn = iks_new("payload-type");
+ iks_insert_attrib(payload_cn, "id", "13");
+ iks_insert_attrib(payload_cn, "name", "CN");
+
+
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "to", (p->them) ? p->them : client->user);
+ iks_insert_attrib(iq, "id", client->connection->mid);
+ ast_aji_increment_mid(client->connection->mid);
+
+ iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
+ iks_insert_attrib(jingle, "action", JINGLE_ACCEPT);
+ iks_insert_attrib(jingle, "initiator", p->initiator ? client->connection->jid->full : p->them);
+ iks_insert_attrib(jingle, JINGLE_SID, tmp->sid);
+ iks_insert_node(iq, jingle);
+ iks_insert_node(jingle, dcodecs);
+ iks_insert_node(dcodecs, payload_red);
+ iks_insert_node(dcodecs, payload_audio);
+ iks_insert_node(dcodecs, payload_cn);
+
+ ast_aji_send(c, iq);
+ iks_delete(payload_red);
+ iks_delete(payload_audio);
+ iks_delete(payload_cn);
+ iks_delete(dcodecs);
+ iks_delete(jingle);
+ iks_delete(iq);
+ }
+ return 1;
+}
+
+static int jingle_ringing_ack(void *data, ikspak *pak)
+{
+ struct jingle_pvt *p = data;
+
+ if (p->ringrule)
+ iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
+ p->ringrule = NULL;
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_RINGING);
+ return IKS_FILTER_EAT;
+}
+
+static int jingle_answer(struct ast_channel *ast)
+{
+ struct jingle_pvt *p = ast->tech_pvt;
+ struct jingle *client = p->parent;
+ int res = 0;
+
+ ast_debug(1, "Answer!\n");
+ ast_mutex_lock(&p->lock);
+ jingle_accept_call(client, p);
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct jingle_pvt *p = chan->tech_pvt;
+ enum ast_rtp_get_result res = AST_RTP_GET_FAILED;
+
+ if (!p)
+ return res;
+
+ ast_mutex_lock(&p->lock);
+ if (p->rtp) {
+ *rtp = p->rtp;
+ res = AST_RTP_TRY_PARTIAL;
+ }
+ ast_mutex_unlock(&p->lock);
+
+ return res;
+}
+
+static int jingle_get_codec(struct ast_channel *chan)
+{
+ struct jingle_pvt *p = chan->tech_pvt;
+ return p->peercapability;
+}
+
+static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active)
+{
+ struct jingle_pvt *p;
+
+ p = chan->tech_pvt;
+ if (!p)
+ return -1;
+ ast_mutex_lock(&p->lock);
+
+/* if (rtp)
+ ast_rtp_get_peer(rtp, &p->redirip);
+ else
+ memset(&p->redirip, 0, sizeof(p->redirip));
+ p->redircodecs = codecs; */
+
+ /* Reset lastrtprx timer */
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int jingle_response(struct jingle *client, ikspak *pak, const char *reasonstr, const char *reasonstr2)
+{
+ iks *response = NULL, *error = NULL, *reason = NULL;
+ int res = -1;
+
+ response = iks_new("iq");
+ if (response) {
+ iks_insert_attrib(response, "type", "result");
+ iks_insert_attrib(response, "from", client->connection->jid->full);
+ iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
+ iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));
+ if (reasonstr) {
+ error = iks_new("error");
+ if (error) {
+ iks_insert_attrib(error, "type", "cancel");
+ reason = iks_new(reasonstr);
+ if (reason)
+ iks_insert_node(error, reason);
+ iks_insert_node(response, error);
+ }
+ }
+ ast_aji_send(client->connection, response);
+ if (reason)
+ iks_delete(reason);
+ if (error)
+ iks_delete(error);
+ iks_delete(response);
+ res = 0;
+ }
+ return res;
+}
+
+static int jingle_is_answered(struct jingle *client, ikspak *pak)
+{
+ struct jingle_pvt *tmp;
+
+ ast_debug(1, "The client is %s\n", client->name);
+ /* Make sure our new call doesn't exist yet */
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, JINGLE_NODE, JINGLE_SID, tmp->sid))
+ break;
+ }
+
+ if (tmp) {
+ if (tmp->owner)
+ ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
+ } else
+ ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
+ jingle_response(client, pak, NULL, NULL);
+ return 1;
+}
+
+static int jingle_handle_dtmf(struct jingle *client, ikspak *pak)
+{
+ struct jingle_pvt *tmp;
+ iks *dtmfnode = NULL, *dtmfchild = NULL;
+ char *dtmf;
+ /* Make sure our new call doesn't exist yet */
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, JINGLE_NODE, JINGLE_SID, tmp->sid))
+ break;
+ }
+
+ if (tmp) {
+ if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) {
+ jingle_response(client,pak,
+ "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
+ "unsupported-dtmf-method xmlns='http://www.xmpp.org/extensions/xep-0181.html#ns-errors'");
+ return -1;
+ }
+ if ((dtmfnode = iks_find(pak->x, "dtmf"))) {
+ if((dtmf = iks_find_attrib(dtmfnode, "code"))) {
+ if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) {
+ struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("JINGLE! DTMF-relay event received: %c\n", f.subclass);
+ } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
+ struct ast_frame f = {AST_FRAME_DTMF_END, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("JINGLE! DTMF-relay event received: %c\n", f.subclass);
+ } else if(iks_find_attrib(pak->x, "dtmf")) { /* 250 millasecond default */
+ struct ast_frame f = {AST_FRAME_DTMF, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("JINGLE! DTMF-relay event received: %c\n", f.subclass);
+ }
+ }
+ } else if ((dtmfnode = iks_find_with_attrib(pak->x, JINGLE_NODE, "action", "session-info"))) {
+ if((dtmfchild = iks_find(dtmfnode, "dtmf"))) {
+ if((dtmf = iks_find_attrib(dtmfchild, "code"))) {
+ if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) {
+ struct ast_frame f = {AST_FRAME_DTMF_END, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("JINGLE! DTMF-relay event received: %c\n", f.subclass);
+ } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
+ struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
+ f.subclass = dtmf[0];
+ ast_queue_frame(tmp->owner, &f);
+ ast_verbose("JINGLE! DTMF-relay event received: %c\n", f.subclass);
+ }
+ }
+ }
+ }
+ jingle_response(client, pak, NULL, NULL);
+ return 1;
+ } else
+ ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
+
+ jingle_response(client, pak, NULL, NULL);
+ return 1;
+}
+
+
+static int jingle_hangup_farend(struct jingle *client, ikspak *pak)
+{
+ struct jingle_pvt *tmp;
+
+ ast_debug(1, "The client is %s\n", client->name);
+ /* Make sure our new call doesn't exist yet */
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, JINGLE_NODE, JINGLE_SID, tmp->sid))
+ break;
+ }
+
+ if (tmp) {
+ tmp->alreadygone = 1;
+ if (tmp->owner)
+ ast_queue_hangup(tmp->owner);
+ } else
+ ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
+ jingle_response(client, pak, NULL, NULL);
+ return 1;
+}
+
+static int jingle_create_candidates(struct jingle *client, struct jingle_pvt *p, char *sid, char *from)
+{
+ struct jingle_candidate *tmp;
+ struct aji_client *c = client->connection;
+ struct jingle_candidate *ours1 = NULL, *ours2 = NULL;
+ struct sockaddr_in sin;
+ struct sockaddr_in dest;
+ struct in_addr us;
+ struct in_addr externaddr;
+ iks *iq, *jingle, *content, *transport, *candidate;
+ char component[16], foundation[16], generation[16], network[16], pass[16], port[7], priority[16], user[16];
+
+
+ iq = iks_new("iq");
+ jingle = iks_new(JINGLE_NODE);
+ content = iks_new("content");
+ transport = iks_new("transport");
+ candidate = iks_new("candidate");
+ if (!iq || !jingle || !content || !transport || !candidate) {
+ ast_log(LOG_ERROR, "Memory allocation error\n");
+ goto safeout;
+ }
+ ours1 = ast_calloc(1, sizeof(*ours1));
+ ours2 = ast_calloc(1, sizeof(*ours2));
+ if (!ours1 || !ours2)
+ goto safeout;
+
+ iks_insert_node(iq, jingle);
+ iks_insert_node(jingle, content);
+ iks_insert_node(content, transport);
+ iks_insert_node(transport, candidate);
+
+ for (; p; p = p->next) {
+ if (!strcasecmp(p->sid, sid))
+ break;
+ }
+
+ if (!p) {
+ ast_log(LOG_NOTICE, "No matching jingle session - SID %s!\n", sid);
+ goto safeout;
+ }
+
+ ast_rtp_get_us(p->rtp, &sin);
+ ast_find_ourip(&us, bindaddr);
+
+ /* Setup our first jingle candidate */
+ ours1->component = 1;
+ ours1->foundation = (unsigned int)bindaddr.sin_addr.s_addr | AJI_CONNECT_HOST | AJI_PROTOCOL_UDP;
+ ours1->generation = 0;
+ ast_copy_string(ours1->ip, ast_inet_ntoa(us), sizeof(ours1->ip));
+ ours1->network = 0;
+ ours1->port = ntohs(sin.sin_port);
+ ours1->priority = 1678246398;
+ ours1->protocol = AJI_PROTOCOL_UDP;
+ snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(ours1->password, pass, sizeof(ours1->password));
+ ours1->type = AJI_CONNECT_HOST;
+ snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(ours1->ufrag, user, sizeof(ours1->ufrag));
+ p->ourcandidates = ours1;
+
+ if (!ast_strlen_zero(externip)) {
+ /* XXX We should really stun for this one not just go with externip XXX */
+ if (inet_aton(externip, &externaddr))
+ ast_log(LOG_WARNING, "Invalid extern IP : %s\n", externip);
+
+ ours2->component = 1;
+ ours2->foundation = (unsigned int)externaddr.s_addr | AJI_CONNECT_PRFLX | AJI_PROTOCOL_UDP;
+ ours2->generation = 0;
+ ast_copy_string(ours2->ip, externip, sizeof(ours2->ip));
+ ours2->network = 0;
+ ours2->port = ntohs(sin.sin_port);
+ ours2->priority = 1678246397;
+ ours2->protocol = AJI_PROTOCOL_UDP;
+ snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(ours2->password, pass, sizeof(ours2->password));
+ ours2->type = AJI_CONNECT_PRFLX;
+
+ snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(ours2->ufrag, user, sizeof(ours2->ufrag));
+ ours1->next = ours2;
+ ours2 = NULL;
+ }
+ ours1 = NULL;
+ dest.sin_addr = __ourip;
+ dest.sin_port = sin.sin_port;
+
+
+ for (tmp = p->ourcandidates; tmp; tmp = tmp->next) {
+ snprintf(component, sizeof(component), "%u", tmp->component);
+ snprintf(foundation, sizeof(foundation), "%u", tmp->foundation);
+ snprintf(generation, sizeof(generation), "%u", tmp->generation);
+ snprintf(network, sizeof(network), "%u", tmp->network);
+ snprintf(port, sizeof(port), "%u", tmp->port);
+ snprintf(priority, sizeof(priority), "%u", tmp->priority);
+
+ iks_insert_attrib(iq, "from", c->jid->full);
+ iks_insert_attrib(iq, "to", from);
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "id", c->mid);
+ ast_aji_increment_mid(c->mid);
+ iks_insert_attrib(jingle, "action", JINGLE_NEGOTIATE);
+ iks_insert_attrib(jingle, JINGLE_SID, sid);
+ iks_insert_attrib(jingle, "initiator", (p->initiator) ? c->jid->full : from);
+ iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
+ iks_insert_attrib(content, "creator", p->initiator ? "initiator" : "responder");
+ iks_insert_attrib(content, "name", "asterisk-audio-content");
+ iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
+ iks_insert_attrib(candidate, "component", component);
+ iks_insert_attrib(candidate, "foundation", foundation);
+ iks_insert_attrib(candidate, "generation", generation);
+ iks_insert_attrib(candidate, "ip", tmp->ip);
+ iks_insert_attrib(candidate, "network", network);
+ iks_insert_attrib(candidate, "port", port);
+ iks_insert_attrib(candidate, "priority", priority);
+ switch (tmp->protocol) {
+ case AJI_PROTOCOL_UDP:
+ iks_insert_attrib(candidate, "protocol", "udp");
+ break;
+ case AJI_PROTOCOL_SSLTCP:
+ iks_insert_attrib(candidate, "protocol", "ssltcp");
+ break;
+ }
+ iks_insert_attrib(candidate, "pwd", tmp->password);
+ switch (tmp->type) {
+ case AJI_CONNECT_HOST:
+ iks_insert_attrib(candidate, "type", "host");
+ break;
+ case AJI_CONNECT_PRFLX:
+ iks_insert_attrib(candidate, "type", "prflx");
+ break;
+ case AJI_CONNECT_RELAY:
+ iks_insert_attrib(candidate, "type", "relay");
+ break;
+ case AJI_CONNECT_SRFLX:
+ iks_insert_attrib(candidate, "type", "srflx");
+ break;
+ }
+ iks_insert_attrib(candidate, "ufrag", tmp->ufrag);
+
+ ast_aji_send(c, iq);
+ }
+ p->laststun = 0;
+
+safeout:
+ if (ours1)
+ ast_free(ours1);
+ if (ours2)
+ ast_free(ours2);
+ if (iq)
+ iks_delete(iq);
+ if (jingle)
+ iks_delete(jingle);
+ if (content)
+ iks_delete(content);
+ if (transport)
+ iks_delete(transport);
+ if (candidate)
+ iks_delete(candidate);
+ return 1;
+}
+
+static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, const char *sid)
+{
+ struct jingle_pvt *tmp = NULL;
+ struct aji_resource *resources = NULL;
+ struct aji_buddy *buddy;
+ char idroster[200];
+
+ ast_debug(1, "The client is %s for alloc\n", client->name);
+ if (!sid && !strchr(from, '/')) { /* I started call! */
+ if (!strcasecmp(client->name, "guest")) {
+ buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, from);
+ if (buddy)
+ resources = buddy->resources;
+ } else if (client->buddy)
+ resources = client->buddy->resources;
+ while (resources) {
+ if (resources->cap->jingle) {
+ break;
+ }
+ resources = resources->next;
+ }
+ if (resources)
+ snprintf(idroster, sizeof(idroster), "%s/%s", from, resources->resource);
+ else {
+ ast_log(LOG_ERROR, "no jingle capable clients to talk to.\n");
+ return NULL;
+ }
+ }
+ if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
+ return NULL;
+ }
+
+ memcpy(&tmp->prefs, &client->prefs, sizeof(tmp->prefs));
+
+ if (sid) {
+ ast_copy_string(tmp->sid, sid, sizeof(tmp->sid));
+ ast_copy_string(tmp->them, from, sizeof(tmp->them));
+ } else {
+ snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random());
+ ast_copy_string(tmp->them, idroster, sizeof(tmp->them));
+ tmp->initiator = 1;
+ }
+ tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ tmp->parent = client;
+ if (!tmp->rtp) {
+ ast_log(LOG_WARNING, "Out of RTP sessions?\n");
+ ast_free(tmp);
+ return NULL;
+ }
+ ast_copy_string(tmp->exten, "s", sizeof(tmp->exten));
+ ast_mutex_init(&tmp->lock);
+ ast_mutex_lock(&jinglelock);
+ tmp->next = client->p;
+ client->p = tmp;
+ ast_mutex_unlock(&jinglelock);
+ return tmp;
+}
+
+/*! \brief Start new jingle channel */
+static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *i, int state, const char *title)
+{
+ struct ast_channel *tmp;
+ int fmt;
+ int what;
+ const char *str;
+
+ if (title)
+ str = title;
+ else
+ str = i->them;
+ tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", "", "", 0, "Jingle/%s-%04lx", str, ast_random() & 0xffff);
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Unable to allocate Jingle channel structure!\n");
+ return NULL;
+ }
+ tmp->tech = &jingle_tech;
+
+ /* Select our native format based on codec preference until we receive
+ something from another device to the contrary. */
+ if (i->jointcapability)
+ what = i->jointcapability;
+ else if (i->capability)
+ what = i->capability;
+ else
+ what = global_capability;
+
+ /* Set Frame packetization */
+ if (i->rtp)
+ ast_rtp_codec_setpref(i->rtp, &i->prefs);
+
+ tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
+ fmt = ast_best_codec(tmp->nativeformats);
+
+ if (i->rtp) {
+ ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp));
+ ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp));
+ }
+ if (i->vrtp) {
+ ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp));
+ ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp));
+ }
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->adsicpe = AST_ADSI_UNAVAILABLE;
+ tmp->writeformat = fmt;
+ tmp->rawwriteformat = fmt;
+ tmp->readformat = fmt;
+ tmp->rawreadformat = fmt;
+ tmp->tech_pvt = i;
+
+ tmp->callgroup = client->callgroup;
+ tmp->pickupgroup = client->pickupgroup;
+ tmp->cid.cid_pres = client->callingpres;
+ if (!ast_strlen_zero(client->accountcode))
+ ast_string_field_set(tmp, accountcode, client->accountcode);
+ if (client->amaflags)
+ tmp->amaflags = client->amaflags;
+ if (!ast_strlen_zero(client->language))
+ ast_string_field_set(tmp, language, client->language);
+ if (!ast_strlen_zero(client->musicclass))
+ ast_string_field_set(tmp, musicclass, client->musicclass);
+ i->owner = tmp;
+ ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
+ /* Don't use ast_set_callerid() here because it will
+ * generate an unnecessary NewCallerID event */
+ tmp->cid.cid_ani = ast_strdup(i->cid_num);
+ if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s"))
+ tmp->cid.cid_dnid = ast_strdup(i->exten);
+ tmp->priority = 1;
+ if (i->rtp)
+ ast_jb_configure(tmp, &global_jbconf);
+ if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(tmp);
+ tmp = NULL;
+ }
+
+ return tmp;
+}
+
+static int jingle_action(struct jingle *client, struct jingle_pvt *p, const char *action)
+{
+ iks *iq, *jingle = NULL;
+ int res = -1;
+
+ iq = iks_new("iq");
+ jingle = iks_new("jingle");
+
+ if (iq) {
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "from", client->connection->jid->full);
+ iks_insert_attrib(iq, "to", p->them);
+ iks_insert_attrib(iq, "id", client->connection->mid);
+ ast_aji_increment_mid(client->connection->mid);
+ if (jingle) {
+ iks_insert_attrib(jingle, "action", action);
+ iks_insert_attrib(jingle, JINGLE_SID, p->sid);
+ iks_insert_attrib(jingle, "initiator", p->initiator ? client->connection->jid->full : p->them);
+ iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
+
+ iks_insert_node(iq, jingle);
+
+ ast_aji_send(client->connection, iq);
+ iks_delete(jingle);
+ res = 0;
+ }
+ iks_delete(iq);
+ }
+ return res;
+}
+
+static void jingle_free_candidates(struct jingle_candidate *candidate)
+{
+ struct jingle_candidate *last;
+ while (candidate) {
+ last = candidate;
+ candidate = candidate->next;
+ ast_free(last);
+ }
+}
+
+static void jingle_free_pvt(struct jingle *client, struct jingle_pvt *p)
+{
+ struct jingle_pvt *cur, *prev = NULL;
+ cur = client->p;
+ while (cur) {
+ if (cur == p) {
+ if (prev)
+ prev->next = p->next;
+ else
+ client->p = p->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ if (p->ringrule)
+ iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
+ if (p->owner)
+ ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
+ if (p->rtp)
+ ast_rtp_destroy(p->rtp);
+ if (p->vrtp)
+ ast_rtp_destroy(p->vrtp);
+ jingle_free_candidates(p->theircandidates);
+ ast_free(p);
+}
+
+
+static int jingle_newcall(struct jingle *client, ikspak *pak)
+{
+ struct jingle_pvt *p, *tmp = client->p;
+ struct ast_channel *chan;
+ int res;
+ iks *payload_type;
+
+ /* Make sure our new call doesn't exist yet */
+ while (tmp) {
+ if (iks_find_with_attrib(pak->x, JINGLE_NODE, JINGLE_SID, tmp->sid)) {
+ ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
+ jingle_response(client, pak, "out-of-order", NULL);
+ return -1;
+ }
+ tmp = tmp->next;
+ }
+
+ p = jingle_alloc(client, pak->from->partial, iks_find_attrib(pak->query, JINGLE_SID));
+ if (!p) {
+ ast_log(LOG_WARNING, "Unable to allocate jingle structure!\n");
+ return -1;
+ }
+ chan = jingle_new(client, p, AST_STATE_DOWN, pak->from->user);
+ if (chan) {
+ ast_mutex_lock(&p->lock);
+ ast_copy_string(p->them, pak->from->full, sizeof(p->them));
+ if (iks_find_attrib(pak->query, JINGLE_SID)) {
+ ast_copy_string(p->sid, iks_find_attrib(pak->query, JINGLE_SID),
+ sizeof(p->sid));
+ }
+
+ payload_type = iks_child(iks_child(iks_child(iks_child(pak->x))));
+ while (payload_type) {
+ ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(payload_type, "id")));
+ ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(payload_type, "id")), "audio", iks_find_attrib(payload_type, "name"), 0);
+ payload_type = iks_next(payload_type);
+ }
+
+ ast_mutex_unlock(&p->lock);
+ ast_setstate(chan, AST_STATE_RING);
+ res = ast_pbx_start(chan);
+
+ switch (res) {
+ case AST_PBX_FAILED:
+ ast_log(LOG_WARNING, "Failed to start PBX :(\n");
+ jingle_response(client, pak, "service-unavailable", NULL);
+ break;
+ case AST_PBX_CALL_LIMIT:
+ ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
+ jingle_response(client, pak, "service-unavailable", NULL);
+ break;
+ case AST_PBX_SUCCESS:
+ jingle_response(client, pak, NULL, NULL);
+ jingle_create_candidates(client, p,
+ iks_find_attrib(pak->query, JINGLE_SID),
+ iks_find_attrib(pak->x, "from"));
+ /* nothing to do */
+ break;
+ }
+ } else {
+ jingle_free_pvt(client, p);
+ }
+ return 1;
+}
+
+static int jingle_update_stun(struct jingle *client, struct jingle_pvt *p)
+{
+ struct jingle_candidate *tmp;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct sockaddr_in sin;
+
+ if (time(NULL) == p->laststun)
+ return 0;
+
+ tmp = p->theircandidates;
+ p->laststun = time(NULL);
+ while (tmp) {
+ char username[256];
+ hp = ast_gethostbyname(tmp->ip, &ahp);
+ sin.sin_family = AF_INET;
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ sin.sin_port = htons(tmp->port);
+ snprintf(username, sizeof(username), "%s:%s", tmp->ufrag, p->ourcandidates->ufrag);
+
+ ast_rtp_stun_request(p->rtp, &sin, username);
+ tmp = tmp->next;
+ }
+ return 1;
+}
+
+static int jingle_add_candidate(struct jingle *client, ikspak *pak)
+{
+ struct jingle_pvt *p = NULL, *tmp = NULL;
+ struct aji_client *c = client->connection;
+ struct jingle_candidate *newcandidate = NULL;
+ iks *traversenodes = NULL, *receipt = NULL;
+
+ for (tmp = client->p; tmp; tmp = tmp->next) {
+ if (iks_find_with_attrib(pak->x, JINGLE_NODE, JINGLE_SID, tmp->sid)) {
+ p = tmp;
+ break;
+ }
+ }
+
+ if (!p)
+ return -1;
+
+ traversenodes = pak->query;
+ while(traversenodes) {
+ if(!strcasecmp(iks_name(traversenodes), "jingle")) {
+ traversenodes = iks_child(traversenodes);
+ continue;
+ }
+ if(!strcasecmp(iks_name(traversenodes), "content")) {
+ traversenodes = iks_child(traversenodes);
+ continue;
+ }
+ if(!strcasecmp(iks_name(traversenodes), "transport")) {
+ traversenodes = iks_child(traversenodes);
+ continue;
+ }
+
+ if(!strcasecmp(iks_name(traversenodes), "candidate")) {
+ newcandidate = ast_calloc(1, sizeof(*newcandidate));
+ if (!newcandidate)
+ return 0;
+ ast_copy_string(newcandidate->ip, iks_find_attrib(traversenodes, "ip"), sizeof(newcandidate->ip));
+ newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
+ ast_copy_string(newcandidate->password, iks_find_attrib(traversenodes, "pwd"), sizeof(newcandidate->password));
+ if (!strcasecmp(iks_find_attrib(traversenodes, "protocol"), "udp"))
+ newcandidate->protocol = AJI_PROTOCOL_UDP;
+ else if (!strcasecmp(iks_find_attrib(traversenodes, "protocol"), "ssltcp"))
+ newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
+
+ if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "host"))
+ newcandidate->type = AJI_CONNECT_HOST;
+ else if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "prflx"))
+ newcandidate->type = AJI_CONNECT_PRFLX;
+ else if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "relay"))
+ newcandidate->type = AJI_CONNECT_RELAY;
+ else if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "srflx"))
+ newcandidate->type = AJI_CONNECT_SRFLX;
+
+ newcandidate->network = atoi(iks_find_attrib(traversenodes, "network"));
+ newcandidate->generation = atoi(iks_find_attrib(traversenodes, "generation"));
+ newcandidate->next = NULL;
+
+ newcandidate->next = p->theircandidates;
+ p->theircandidates = newcandidate;
+ p->laststun = 0;
+ jingle_update_stun(p->parent, p);
+ newcandidate = NULL;
+ }
+ traversenodes = iks_next(traversenodes);
+ }
+
+ receipt = iks_new("iq");
+ iks_insert_attrib(receipt, "type", "result");
+ iks_insert_attrib(receipt, "from", c->jid->full);
+ iks_insert_attrib(receipt, "to", iks_find_attrib(pak->x, "from"));
+ iks_insert_attrib(receipt, "id", iks_find_attrib(pak->x, "id"));
+ ast_aji_send(c, receipt);
+ iks_delete(receipt);
+
+ return 1;
+}
+
+static struct ast_frame *jingle_rtp_read(struct ast_channel *ast, struct jingle_pvt *p)
+{
+ struct ast_frame *f;
+
+ if (!p->rtp)
+ return &ast_null_frame;
+ f = ast_rtp_read(p->rtp);
+ jingle_update_stun(p->parent, p);
+ if (p->owner) {
+ /* We already hold the channel lock */
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
+ ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
+ p->owner->nativeformats =
+ (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass;
+ ast_set_read_format(p->owner, p->owner->readformat);
+ ast_set_write_format(p->owner, p->owner->writeformat);
+ }
+/* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
+ f = ast_dsp_process(p->owner, p->vad, f);
+ if (f && (f->frametype == AST_FRAME_DTMF))
+ ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
+ } */
+ }
+ }
+ return f;
+}
+
+static struct ast_frame *jingle_read(struct ast_channel *ast)
+{
+ struct ast_frame *fr;
+ struct jingle_pvt *p = ast->tech_pvt;
+
+ ast_mutex_lock(&p->lock);
+ fr = jingle_rtp_read(ast, p);
+ ast_mutex_unlock(&p->lock);
+ return fr;
+}
+
+/*! \brief Send frame to media channel (rtp) */
+static int jingle_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct jingle_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ switch (frame->frametype) {
+ case AST_FRAME_VOICE:
+ if (!(frame->subclass & ast->nativeformats)) {
+ ast_log(LOG_WARNING,
+ "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
+ frame->subclass, ast->nativeformats, ast->readformat,
+ ast->writeformat);
+ return 0;
+ }
+ if (p) {
+ ast_mutex_lock(&p->lock);
+ if (p->rtp) {
+ res = ast_rtp_write(p->rtp, frame);
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ case AST_FRAME_VIDEO:
+ if (p) {
+ ast_mutex_lock(&p->lock);
+ if (p->vrtp) {
+ res = ast_rtp_write(p->vrtp, frame);
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ case AST_FRAME_IMAGE:
+ return 0;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't send %d type frames with Jingle write\n",
+ frame->frametype);
+ return 0;
+ }
+
+ return res;
+}
+
+static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct jingle_pvt *p = newchan->tech_pvt;
+ ast_mutex_lock(&p->lock);
+
+ if ((p->owner != oldchan)) {
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ if (p->owner == oldchan)
+ p->owner = newchan;
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int jingle_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+ int res = 0;
+
+ switch (condition) {
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, data, NULL);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Don't know how to indicate condition '%d'\n", condition);
+ res = -1;
+ }
+
+ return res;
+}
+
+static int jingle_digit(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct jingle_pvt *p = ast->tech_pvt;
+ struct jingle *client = p->parent;
+ iks *iq, *jingle, *dtmf;
+ char buffer[2] = {digit, '\0'};
+ iq = iks_new("iq");
+ jingle = iks_new("jingle");
+ dtmf = iks_new("dtmf");
+ if(!iq || !jingle || !dtmf) {
+ if(iq)
+ iks_delete(iq);
+ if(jingle)
+ iks_delete(jingle);
+ if(dtmf)
+ iks_delete(dtmf);
+ ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
+ return -1;
+ }
+
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "to", p->them);
+ iks_insert_attrib(iq, "from", client->connection->jid->full);
+ iks_insert_attrib(iq, "id", client->connection->mid);
+ ast_aji_increment_mid(client->connection->mid);
+ iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
+ iks_insert_attrib(jingle, "action", "session-info");
+ iks_insert_attrib(jingle, "initiator", p->initiator ? client->connection->jid->full : p->them);
+ iks_insert_attrib(jingle, "sid", p->sid);
+ iks_insert_attrib(dtmf, "xmlns", JINGLE_DTMF_NS);
+ iks_insert_attrib(dtmf, "code", buffer);
+ iks_insert_node(iq, jingle);
+ iks_insert_node(jingle, dtmf);
+
+ ast_mutex_lock(&p->lock);
+ if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
+ iks_insert_attrib(dtmf, "action", "button-down");
+ } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
+ iks_insert_attrib(dtmf, "action", "button-up");
+ }
+ ast_aji_send(client->connection, iq);
+ iks_delete(iq);
+ iks_delete(jingle);
+ iks_delete(dtmf);
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int jingle_digit_begin(struct ast_channel *chan, char digit)
+{
+ return jingle_digit(chan, digit, 0);
+}
+
+static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ return jingle_digit(ast, digit, duration);
+}
+
+static int jingle_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
+{
+ ast_log(LOG_NOTICE, "XXX Implement jingle sendhtml XXX\n");
+
+ return -1;
+}
+static int jingle_transmit_invite(struct jingle_pvt *p)
+{
+ struct jingle *aux = NULL;
+ struct aji_client *client = NULL;
+ iks *iq, *jingle, *content, *description, *transport;
+ iks *payload_eg711u, *payload_pcmu;
+
+ aux = p->parent;
+ client = aux->connection;
+ iq = iks_new("iq");
+ jingle = iks_new(JINGLE_NODE);
+ content = iks_new("content");
+ description = iks_new("description");
+ transport = iks_new("transport");
+ payload_pcmu = iks_new("payload-type");
+ payload_eg711u = iks_new("payload-type");
+
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib(iq, "to", p->them);
+ iks_insert_attrib(iq, "from", client->jid->full);
+ iks_insert_attrib(iq, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ iks_insert_attrib(jingle, "action", JINGLE_INITIATE);
+ iks_insert_attrib(jingle, JINGLE_SID, p->sid);
+ iks_insert_attrib(jingle, "initiator", client->jid->full);
+ iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
+ iks_insert_attrib(content, "creator", "initiator");
+ iks_insert_attrib(content, "name", "asterisk-audio-content");
+ iks_insert_attrib(content, "profile", "RTP/AVP");
+ iks_insert_attrib(description, "xmlns", JINGLE_AUDIO_RTP_NS);
+ iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
+ iks_insert_attrib(payload_pcmu, "id", "0");
+ iks_insert_attrib(payload_pcmu, "name", "PCMU");
+ iks_insert_attrib(payload_eg711u, "id", "100");
+ iks_insert_attrib(payload_eg711u, "name", "EG711U");
+
+ iks_insert_node(description, payload_pcmu);
+ iks_insert_node(description, payload_eg711u);
+ iks_insert_node(content, description);
+ iks_insert_node(content, transport);
+ iks_insert_node(jingle, content);
+ iks_insert_node(iq, jingle);
+
+ ast_aji_send(client, iq);
+
+ iks_delete(iq);
+ iks_delete(jingle);
+ iks_delete(content);
+ iks_delete(description);
+ iks_delete(transport);
+ iks_delete(payload_eg711u);
+ iks_delete(payload_pcmu);
+ return 0;
+}
+
+/* Not in use right now.
+static int jingle_auto_congest(void *nothing)
+{
+ struct jingle_pvt *p = nothing;
+
+ ast_mutex_lock(&p->lock);
+ if (p->owner) {
+ if (!ast_channel_trylock(p->owner)) {
+ ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ ast_channel_unlock(p->owner);
+ }
+ }
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+*/
+
+/*! \brief Initiate new call, part of PBX interface
+ * dest is the dial string */
+static int jingle_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct jingle_pvt *p = ast->tech_pvt;
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "jingle_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+
+ ast_setstate(ast, AST_STATE_RING);
+ p->jointcapability = p->capability;
+ if (!p->ringrule) {
+ ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
+ p->ringrule = iks_filter_add_rule(p->parent->connection->f, jingle_ringing_ack, p,
+ IKS_RULE_ID, p->ring, IKS_RULE_DONE);
+ } else
+ ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
+
+ jingle_transmit_invite(p);
+ jingle_create_candidates(p->parent, p, p->sid, p->them);
+
+ return 0;
+}
+
+/*! \brief Hangup a call through the jingle proxy channel */
+static int jingle_hangup(struct ast_channel *ast)
+{
+ struct jingle_pvt *p = ast->tech_pvt;
+ struct jingle *client;
+
+ ast_mutex_lock(&p->lock);
+ client = p->parent;
+ p->owner = NULL;
+ ast->tech_pvt = NULL;
+ if (!p->alreadygone)
+ jingle_action(client, p, JINGLE_TERMINATE);
+ ast_mutex_unlock(&p->lock);
+
+ jingle_free_pvt(client, p);
+
+ return 0;
+}
+
+/*! \brief Part of PBX interface */
+static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause)
+{
+ struct jingle_pvt *p = NULL;
+ struct jingle *client = NULL;
+ char *sender = NULL, *to = NULL, *s = NULL;
+ struct ast_channel *chan = NULL;
+
+ if (data) {
+ s = ast_strdupa(data);
+ if (s) {
+ sender = strsep(&s, "/");
+ if (sender && (sender[0] != '\0'))
+ to = strsep(&s, "/");
+ if (!to) {
+ ast_log(LOG_ERROR, "Bad arguments in Jingle Dialstring: %s\n", (char*) data);
+ return NULL;
+ }
+ }
+ }
+ client = find_jingle(to, sender);
+ if (!client) {
+ ast_log(LOG_WARNING, "Could not find recipient.\n");
+ return NULL;
+ }
+ ASTOBJ_WRLOCK(client);
+ p = jingle_alloc(client, to, NULL);
+ if (p)
+ chan = jingle_new(client, p, AST_STATE_DOWN, to);
+ ASTOBJ_UNLOCK(client);
+
+ return chan;
+}
+
+/*! \brief CLI command "jingle show channels" */
+static char *jingle_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
+ struct jingle_pvt *p;
+ struct ast_channel *chan;
+ int numchans = 0;
+ char them[AJI_MAX_JIDLEN];
+ char *jid = NULL;
+ char *resource = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jingle show channels";
+ e->usage =
+ "Usage: jingle show channels\n"
+ " Shows current state of the Jingle channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&jinglelock);
+ ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
+ ASTOBJ_CONTAINER_TRAVERSE(&jingle_list, 1, {
+ ASTOBJ_WRLOCK(iterator);
+ p = iterator->p;
+ while(p) {
+ chan = p->owner;
+ ast_copy_string(them, p->them, sizeof(them));
+ jid = them;
+ resource = strchr(them, '/');
+ if (!resource)
+ resource = "None";
+ else {
+ *resource = '\0';
+ resource ++;
+ }
+ if (chan)
+ ast_cli(a->fd, FORMAT,
+ chan->name,
+ jid,
+ resource,
+ ast_getformatname(chan->readformat),
+ ast_getformatname(chan->writeformat)
+ );
+ else
+ ast_log(LOG_WARNING, "No available channel\n");
+ numchans ++;
+ p = p->next;
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ ast_mutex_unlock(&jinglelock);
+
+ ast_cli(a->fd, "%d active jingle channel%s\n", numchans, (numchans != 1) ? "s" : "");
+ return CLI_SUCCESS;
+#undef FORMAT
+}
+
+/*! \brief CLI command "jingle reload" */
+static char *jingle_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jingle reload";
+ e->usage =
+ "Usage: jingle reload\n"
+ " Reload jingle channel driver.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ return CLI_SUCCESS;
+}
+
+static int jingle_parser(void *data, ikspak *pak)
+{
+ struct jingle *client = ASTOBJ_REF((struct jingle *) data);
+ ast_log(LOG_NOTICE, "Filter matched\n");
+
+ if (iks_find_with_attrib(pak->x, JINGLE_NODE, "action", JINGLE_INITIATE)) {
+ /* New call */
+ jingle_newcall(client, pak);
+ } else if (iks_find_with_attrib(pak->x, JINGLE_NODE, "action", JINGLE_NEGOTIATE)) {
+ ast_debug(3, "About to add candidate!\n");
+ jingle_add_candidate(client, pak);
+ ast_debug(3, "Candidate Added!\n");
+ } else if (iks_find_with_attrib(pak->x, JINGLE_NODE, "action", JINGLE_ACCEPT)) {
+ jingle_is_answered(client, pak);
+ } else if (iks_find_with_attrib(pak->x, JINGLE_NODE, "action", JINGLE_INFO)) {
+ jingle_handle_dtmf(client, pak);
+ } else if (iks_find_with_attrib(pak->x, JINGLE_NODE, "action", JINGLE_TERMINATE)) {
+ jingle_hangup_farend(client, pak);
+ } else if (iks_find_with_attrib(pak->x, JINGLE_NODE, "action", "reject")) {
+ jingle_hangup_farend(client, pak);
+ }
+ ASTOBJ_UNREF(client, jingle_member_destroy);
+ return IKS_FILTER_EAT;
+}
+/* Not using this anymore probably take out soon
+static struct jingle_candidate *jingle_create_candidate(char *args)
+{
+ char *name, *type, *preference, *protocol;
+ struct jingle_candidate *res;
+ res = ast_calloc(1, sizeof(*res));
+ if (args)
+ name = args;
+ if ((args = strchr(args, ','))) {
+ *args = '\0';
+ args++;
+ preference = args;
+ }
+ if ((args = strchr(args, ','))) {
+ *args = '\0';
+ args++;
+ protocol = args;
+ }
+ if ((args = strchr(args, ','))) {
+ *args = '\0';
+ args++;
+ type = args;
+ }
+ if (name)
+ ast_copy_string(res->name, name, sizeof(res->name));
+ if (preference) {
+ res->preference = atof(preference);
+ }
+ if (protocol) {
+ if (!strcasecmp("udp", protocol))
+ res->protocol = AJI_PROTOCOL_UDP;
+ if (!strcasecmp("ssltcp", protocol))
+ res->protocol = AJI_PROTOCOL_SSLTCP;
+ }
+ if (type) {
+ if (!strcasecmp("host", type))
+ res->type = AJI_CONNECT_HOST;
+ if (!strcasecmp("prflx", type))
+ res->type = AJI_CONNECT_PRFLX;
+ if (!strcasecmp("relay", type))
+ res->type = AJI_CONNECT_RELAY;
+ if (!strcasecmp("srflx", type))
+ res->type = AJI_CONNECT_SRFLX;
+ }
+
+ return res;
+}
+*/
+
+static int jingle_create_member(char *label, struct ast_variable *var, int allowguest,
+ struct ast_codec_pref prefs, char *context,
+ struct jingle *member)
+{
+ struct aji_client *client;
+
+ if (!member)
+ ast_log(LOG_WARNING, "Out of memory.\n");
+
+ ast_copy_string(member->name, label, sizeof(member->name));
+ ast_copy_string(member->user, label, sizeof(member->user));
+ ast_copy_string(member->context, context, sizeof(member->context));
+ member->allowguest = allowguest;
+ member->prefs = prefs;
+ while (var) {
+#if 0
+ struct jingle_candidate *candidate = NULL;
+#endif
+ if (!strcasecmp(var->name, "username"))
+ ast_copy_string(member->user, var->value, sizeof(member->user));
+ else if (!strcasecmp(var->name, "disallow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0);
+ else if (!strcasecmp(var->name, "allow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
+ else if (!strcasecmp(var->name, "context"))
+ ast_copy_string(member->context, var->value, sizeof(member->context));
+#if 0
+ else if (!strcasecmp(var->name, "candidate")) {
+ candidate = jingle_create_candidate(var->value);
+ if (candidate) {
+ candidate->next = member->ourcandidates;
+ member->ourcandidates = candidate;
+ }
+ }
+#endif
+ else if (!strcasecmp(var->name, "connection")) {
+ if ((client = ast_aji_get_client(var->value))) {
+ member->connection = client;
+ iks_filter_add_rule(client->f, jingle_parser, member,
+ IKS_RULE_TYPE, IKS_PAK_IQ,
+ IKS_RULE_FROM_PARTIAL, member->user,
+ IKS_RULE_NS, JINGLE_NS,
+ IKS_RULE_DONE);
+ } else {
+ ast_log(LOG_ERROR, "connection referenced not found!\n");
+ return 0;
+ }
+ }
+ var = var->next;
+ }
+ if (member->connection && member->user)
+ member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
+ else {
+ ast_log(LOG_ERROR, "No Connection or Username!\n");
+ }
+ return 1;
+}
+
+static int jingle_load_config(void)
+{
+ char *cat = NULL;
+ struct ast_config *cfg = NULL;
+ char context[100];
+ int allowguest = 1;
+ struct ast_variable *var;
+ struct jingle *member;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct ast_codec_pref prefs;
+ struct aji_client_container *clients;
+ struct jingle_candidate *global_candidates = NULL;
+ struct ast_flags config_flags = { 0 };
+
+ cfg = ast_config_load(JINGLE_CONFIG, config_flags);
+ if (!cfg)
+ return 0;
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ cat = ast_category_browse(cfg, NULL);
+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, var->name, var->value))
+ continue;
+
+ if (!strcasecmp(var->name, "allowguest"))
+ allowguest =
+ (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
+ else if (!strcasecmp(var->name, "disallow"))
+ ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0);
+ else if (!strcasecmp(var->name, "allow"))
+ ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
+ else if (!strcasecmp(var->name, "context"))
+ ast_copy_string(context, var->value, sizeof(context));
+ else if (!strcasecmp(var->name, "externip"))
+ ast_copy_string(externip, var->value, sizeof(externip));
+ else if (!strcasecmp(var->name, "bindaddr")) {
+ if (!(hp = ast_gethostbyname(var->value, &ahp))) {
+ ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
+ } else {
+ memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
+ }
+ }
+/* Idea to allow for custom candidates */
+/*
+ else if (!strcasecmp(var->name, "candidate")) {
+ candidate = jingle_create_candidate(var->value);
+ if (candidate) {
+ candidate->next = global_candidates;
+ global_candidates = candidate;
+ }
+ }
+*/
+ }
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ var = ast_variable_browse(cfg, cat);
+ member = ast_calloc(1, sizeof(*member));
+ ASTOBJ_INIT(member);
+ ASTOBJ_WRLOCK(member);
+ if (!strcasecmp(cat, "guest")) {
+ ast_copy_string(member->name, "guest", sizeof(member->name));
+ ast_copy_string(member->user, "guest", sizeof(member->user));
+ ast_copy_string(member->context, context, sizeof(member->context));
+ member->allowguest = allowguest;
+ member->prefs = prefs;
+ while (var) {
+ if (!strcasecmp(var->name, "disallow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability,
+ var->value, 0);
+ else if (!strcasecmp(var->name, "allow"))
+ ast_parse_allow_disallow(&member->prefs, &member->capability,
+ var->value, 1);
+ else if (!strcasecmp(var->name, "context"))
+ ast_copy_string(member->context, var->value,
+ sizeof(member->context));
+/* Idea to allow for custom candidates */
+/*
+ else if (!strcasecmp(var->name, "candidate")) {
+ candidate = jingle_create_candidate(var->value);
+ if (candidate) {
+ candidate->next = member->ourcandidates;
+ member->ourcandidates = candidate;
+ }
+ }
+*/
+ var = var->next;
+ }
+ ASTOBJ_UNLOCK(member);
+ clients = ast_aji_get_clients();
+ if (clients) {
+ ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
+ ASTOBJ_WRLOCK(iterator);
+ ASTOBJ_WRLOCK(member);
+ member->connection = iterator;
+ iks_filter_add_rule(iterator->f, jingle_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, JINGLE_NS, IKS_RULE_DONE);
+ iks_filter_add_rule(iterator->f, jingle_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, JINGLE_DTMF_NS, IKS_RULE_DONE);
+ ASTOBJ_UNLOCK(member);
+ ASTOBJ_CONTAINER_LINK(&jingle_list, member);
+ ASTOBJ_UNLOCK(iterator);
+ });
+ } else {
+ ASTOBJ_UNLOCK(member);
+ ASTOBJ_UNREF(member, jingle_member_destroy);
+ }
+ } else {
+ ASTOBJ_UNLOCK(member);
+ if (jingle_create_member(cat, var, allowguest, prefs, context, member))
+ ASTOBJ_CONTAINER_LINK(&jingle_list, member);
+ ASTOBJ_UNREF(member, jingle_member_destroy);
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ jingle_free_candidates(global_candidates);
+ return 1;
+}
+
+/*! \brief Load module into PBX, register channel */
+static int load_module(void)
+{
+ ASTOBJ_CONTAINER_INIT(&jingle_list);
+ if (!jingle_load_config()) {
+ ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", JINGLE_CONFIG);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ sched = sched_context_create();
+ if (!sched)
+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
+
+ io = io_context_create();
+ if (!io)
+ ast_log(LOG_WARNING, "Unable to create I/O context\n");
+
+ if (ast_find_ourip(&__ourip, bindaddr)) {
+ ast_log(LOG_WARNING, "Unable to get own IP address, Jingle disabled\n");
+ return 0;
+ }
+
+ ast_rtp_proto_register(&jingle_rtp);
+ ast_cli_register_multiple(jingle_cli, sizeof(jingle_cli) / sizeof(jingle_cli[0]));
+ /* Make sure we can register our channel type */
+ if (ast_channel_register(&jingle_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief Reload module */
+static int reload(void)
+{
+ return 0;
+}
+
+/*! \brief Unload the jingle channel from Asterisk */
+static int unload_module(void)
+{
+ struct jingle_pvt *privates = NULL;
+ ast_cli_unregister_multiple(jingle_cli, sizeof(jingle_cli) / sizeof(jingle_cli[0]));
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&jingle_tech);
+ ast_rtp_proto_unregister(&jingle_rtp);
+
+ if (!ast_mutex_lock(&jinglelock)) {
+ /* Hangup all interfaces if they have an owner */
+ ASTOBJ_CONTAINER_TRAVERSE(&jingle_list, 1, {
+ ASTOBJ_WRLOCK(iterator);
+ privates = iterator->p;
+ while(privates) {
+ if (privates->owner)
+ ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
+ privates = privates->next;
+ }
+ iterator->p = NULL;
+ ASTOBJ_UNLOCK(iterator);
+ });
+ ast_mutex_unlock(&jinglelock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+ ASTOBJ_CONTAINER_DESTROYALL(&jingle_list, jingle_member_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&jingle_list);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Jingle Channel Driver",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/channels/chan_local.c b/trunk/channels/chan_local.c
new file mode 100644
index 000000000..e42368e32
--- /dev/null
+++ b/trunk/channels/chan_local.c
@@ -0,0 +1,761 @@
+/*
+ * 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
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \brief Local Proxy Channel
+ *
+ * \ingroup channel_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <fcntl.h>
+#include <sys/signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/manager.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/devicestate.h"
+
+static const char tdesc[] = "Local Proxy Channel Driver";
+
+#define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
+
+static struct ast_jb_conf g_jb_conf = {
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = "",
+};
+
+static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
+static int local_digit_begin(struct ast_channel *ast, char digit);
+static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int local_call(struct ast_channel *ast, char *dest, int timeout);
+static int local_hangup(struct ast_channel *ast);
+static int local_answer(struct ast_channel *ast);
+static struct ast_frame *local_read(struct ast_channel *ast);
+static int local_write(struct ast_channel *ast, struct ast_frame *f);
+static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
+static int local_sendtext(struct ast_channel *ast, const char *text);
+static int local_devicestate(void *data);
+
+/* PBX interface structure for channel registration */
+static const struct ast_channel_tech local_tech = {
+ .type = "Local",
+ .description = tdesc,
+ .capabilities = -1,
+ .requester = local_request,
+ .send_digit_begin = local_digit_begin,
+ .send_digit_end = local_digit_end,
+ .call = local_call,
+ .hangup = local_hangup,
+ .answer = local_answer,
+ .read = local_read,
+ .write = local_write,
+ .write_video = local_write,
+ .exception = local_read,
+ .indicate = local_indicate,
+ .fixup = local_fixup,
+ .send_html = local_sendhtml,
+ .send_text = local_sendtext,
+ .devicestate = local_devicestate,
+};
+
+struct local_pvt {
+ ast_mutex_t lock; /* Channel private lock */
+ unsigned int flags; /* Private flags */
+ char context[AST_MAX_CONTEXT]; /* Context to call */
+ char exten[AST_MAX_EXTENSION]; /* Extension to call */
+ int reqformat; /* Requested format */
+ struct ast_jb_conf jb_conf; /*!< jitterbuffer configuration for this local channel */
+ struct ast_channel *owner; /* Master Channel */
+ struct ast_channel *chan; /* Outbound channel */
+ struct ast_module_user *u_owner; /*! reference to keep the module loaded while in use */
+ struct ast_module_user *u_chan; /*! reference to keep the module loaded while in use */
+ AST_LIST_ENTRY(local_pvt) list; /* Next entity */
+};
+
+#define LOCAL_GLARE_DETECT (1 << 0) /*!< Detect glare on hangup */
+#define LOCAL_CANCEL_QUEUE (1 << 1) /*!< Cancel queue */
+#define LOCAL_ALREADY_MASQED (1 << 2) /*!< Already masqueraded */
+#define LOCAL_LAUNCHED_PBX (1 << 3) /*!< PBX was launched */
+#define LOCAL_NO_OPTIMIZATION (1 << 4) /*!< Do not optimize using masquerading */
+
+static AST_LIST_HEAD_STATIC(locals, local_pvt);
+
+/*! \brief Adds devicestate to local channels */
+static int local_devicestate(void *data)
+{
+ char *exten = ast_strdupa(data);
+ char *context = NULL, *opts = NULL;
+ int res;
+ struct local_pvt *lp;
+
+ if (!(context = strchr(exten, '@'))) {
+ ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
+ return AST_DEVICE_INVALID;
+ }
+
+ *context++ = '\0';
+
+ /* Strip options if they exist */
+ if ((opts = strchr(context, '/')))
+ *opts = '\0';
+
+ ast_debug(3, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
+
+ res = ast_exists_extension(NULL, context, exten, 1, NULL);
+ if (!res)
+ return AST_DEVICE_INVALID;
+
+ res = AST_DEVICE_NOT_INUSE;
+ AST_LIST_LOCK(&locals);
+ AST_LIST_TRAVERSE(&locals, lp, list) {
+ if (!strcmp(exten, lp->exten) && !strcmp(context, lp->context) && lp->owner) {
+ res = AST_DEVICE_INUSE;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&locals);
+
+ return res;
+}
+
+static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us)
+{
+ struct ast_channel *other = NULL;
+
+retrylock:
+
+ /* Recalculate outbound channel */
+ other = isoutbound ? p->owner : p->chan;
+
+ /* Set glare detection */
+ ast_set_flag(p, LOCAL_GLARE_DETECT);
+ if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
+ /* We had a glare on the hangup. Forget all this business,
+ return and destroy p. */
+ ast_mutex_unlock(&p->lock);
+ ast_mutex_destroy(&p->lock);
+ ast_free(p);
+ return -1;
+ }
+ if (!other) {
+ ast_clear_flag(p, LOCAL_GLARE_DETECT);
+ return 0;
+ }
+ if (ast_channel_trylock(other)) {
+ /* Failed to lock. Release main lock and try again */
+ ast_mutex_unlock(&p->lock);
+ if (us) {
+ if (ast_channel_unlock(us)) {
+ ast_log(LOG_WARNING, "%s wasn't locked while sending %d/%d\n",
+ us->name, f->frametype, f->subclass);
+ us = NULL;
+ }
+ }
+ /* Wait just a bit */
+ usleep(1);
+ /* Only we can destroy ourselves, so we can't disappear here */
+ if (us)
+ ast_channel_lock(us);
+ ast_mutex_lock(&p->lock);
+ goto retrylock;
+ }
+ ast_queue_frame(other, f);
+ ast_channel_unlock(other);
+ ast_clear_flag(p, LOCAL_GLARE_DETECT);
+ return 0;
+}
+
+static int local_answer(struct ast_channel *ast)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int isoutbound;
+ int res = -1;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+ isoutbound = IS_OUTBOUND(ast, p);
+ if (isoutbound) {
+ /* Pass along answer since somebody answered us */
+ struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
+ res = local_queue_frame(p, isoutbound, &answer, ast);
+ } else
+ ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
+ if (!res)
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static void check_bridge(struct local_pvt *p, int isoutbound)
+{
+ if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner || (p->chan->_bridge != ast_bridged_channel(p->chan)))
+ return;
+
+ /* only do the masquerade if we are being called on the outbound channel,
+ if it has been bridged to another channel and if there are no pending
+ frames on the owner channel (because they would be transferred to the
+ outbound channel during the masquerade)
+ */
+ if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel! Only go one step! */ && AST_LIST_EMPTY(&p->owner->readq)) {
+ /* Masquerade bridged channel into owner */
+ /* Lock everything we need, one by one, and give up if
+ we can't get everything. Remember, we'll get another
+ chance in just a little bit */
+ if (!ast_channel_trylock(p->chan->_bridge)) {
+ if (!ast_check_hangup(p->chan->_bridge)) {
+ if (!ast_channel_trylock(p->owner)) {
+ if (!ast_check_hangup(p->owner)) {
+ ast_channel_masquerade(p->owner, p->chan->_bridge);
+ ast_set_flag(p, LOCAL_ALREADY_MASQED);
+ }
+ ast_channel_unlock(p->owner);
+ }
+ ast_channel_unlock(p->chan->_bridge);
+ }
+ }
+ /* We only allow masquerading in one 'direction'... it's important to preserve the state
+ (group variables, etc.) that live on p->chan->_bridge (and were put there by the dialplan)
+ when the local channels go away.
+ */
+#if 0
+ } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
+ /* Masquerade bridged channel into chan */
+ if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
+ if (!ast_check_hangup(p->owner->_bridge)) {
+ if (!ast_mutex_trylock(&p->chan->lock)) {
+ if (!ast_check_hangup(p->chan)) {
+ ast_channel_masquerade(p->chan, p->owner->_bridge);
+ ast_set_flag(p, LOCAL_ALREADY_MASQED);
+ }
+ ast_mutex_unlock(&p->chan->lock);
+ }
+ }
+ ast_mutex_unlock(&(p->owner->_bridge)->lock);
+ }
+#endif
+ }
+}
+
+static struct ast_frame *local_read(struct ast_channel *ast)
+{
+ return &ast_null_frame;
+}
+
+static int local_write(struct ast_channel *ast, struct ast_frame *f)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int res = -1;
+ int isoutbound;
+
+ if (!p)
+ return -1;
+
+ /* Just queue for delivery to the other side */
+ ast_mutex_lock(&p->lock);
+ isoutbound = IS_OUTBOUND(ast, p);
+ if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
+ check_bridge(p, isoutbound);
+ if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
+ res = local_queue_frame(p, isoutbound, f, ast);
+ else {
+ ast_debug(1, "Not posting to queue since already masked on '%s'\n", ast->name);
+ res = 0;
+ }
+ if (!res)
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct local_pvt *p = newchan->tech_pvt;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+
+ if ((p->owner != oldchan) && (p->chan != oldchan)) {
+ ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ if (p->owner == oldchan)
+ p->owner = newchan;
+ else
+ p->chan = newchan;
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int res = 0;
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+ int isoutbound;
+
+ if (!p)
+ return -1;
+
+ /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
+ if (condition == AST_CONTROL_HOLD) {
+ ast_moh_start(ast, data, NULL);
+ } else if (condition == AST_CONTROL_UNHOLD) {
+ ast_moh_stop(ast);
+ } else {
+ /* Queue up a frame representing the indication as a control frame */
+ ast_mutex_lock(&p->lock);
+ isoutbound = IS_OUTBOUND(ast, p);
+ f.subclass = condition;
+ f.data = (void*)data;
+ f.datalen = datalen;
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ ast_mutex_unlock(&p->lock);
+ }
+
+ return res;
+}
+
+static int local_digit_begin(struct ast_channel *ast, char digit)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int res = -1;
+ struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
+ int isoutbound;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+ isoutbound = IS_OUTBOUND(ast, p);
+ f.subclass = digit;
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ ast_mutex_unlock(&p->lock);
+
+ return res;
+}
+
+static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int res = -1;
+ struct ast_frame f = { AST_FRAME_DTMF_END, };
+ int isoutbound;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+ isoutbound = IS_OUTBOUND(ast, p);
+ f.subclass = digit;
+ f.len = duration;
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ ast_mutex_unlock(&p->lock);
+
+ return res;
+}
+
+static int local_sendtext(struct ast_channel *ast, const char *text)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int res = -1;
+ struct ast_frame f = { AST_FRAME_TEXT, };
+ int isoutbound;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+ isoutbound = IS_OUTBOUND(ast, p);
+ f.data = (char *) text;
+ f.datalen = strlen(text) + 1;
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int res = -1;
+ struct ast_frame f = { AST_FRAME_HTML, };
+ int isoutbound;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+ isoutbound = IS_OUTBOUND(ast, p);
+ f.subclass = subclass;
+ f.data = (char *)data;
+ f.datalen = datalen;
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+/*! \brief Initiate new call, part of PBX interface
+ * dest is the dial string */
+static int local_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int res;
+ struct ast_var_t *varptr = NULL, *new;
+ size_t len, namelen;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+
+ /*
+ * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
+ * call, so it's done here instead.
+ */
+ p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
+ p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
+ p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
+ p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
+ p->chan->cid.cid_pres = p->owner->cid.cid_pres;
+ ast_string_field_set(p->chan, language, p->owner->language);
+ ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
+ p->chan->cdrflags = p->owner->cdrflags;
+
+ /* copy the channel variables from the incoming channel to the outgoing channel */
+ /* Note that due to certain assumptions, they MUST be in the same order */
+ AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
+ namelen = strlen(varptr->name);
+ len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
+ if ((new = ast_calloc(1, len))) {
+ memcpy(new, varptr, len);
+ new->value = &(new->name[0]) + namelen + 1;
+ AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
+ }
+ }
+ ast_channel_datastore_inherit(p->owner, p->chan);
+
+ /* Start switch on sub channel */
+ if (!(res = ast_pbx_start(p->chan)))
+ ast_set_flag(p, LOCAL_LAUNCHED_PBX);
+
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+/*! \brief Hangup a call through the local proxy channel */
+static int local_hangup(struct ast_channel *ast)
+{
+ struct local_pvt *p = ast->tech_pvt;
+ int isoutbound;
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
+ struct ast_channel *ochan = NULL;
+ int glaredetect = 0, res = 0;
+
+ if (!p)
+ return -1;
+
+ ast_mutex_lock(&p->lock);
+ if (p->chan && ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE))
+ ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE);
+ isoutbound = IS_OUTBOUND(ast, p);
+ if (isoutbound) {
+ const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
+ if ((status) && (p->owner)) {
+ /* Deadlock avoidance */
+ while (ast_channel_trylock(p->owner)) {
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ }
+ pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
+ ast_channel_unlock(p->owner);
+ }
+ p->chan = NULL;
+ ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
+ ast_module_user_remove(p->u_chan);
+ } else {
+ p->owner = NULL;
+ ast_module_user_remove(p->u_owner);
+ }
+
+ ast->tech_pvt = NULL;
+
+ if (!p->owner && !p->chan) {
+ /* Okay, done with the private part now, too. */
+ glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
+ /* If we have a queue holding, don't actually destroy p yet, but
+ let local_queue do it. */
+ if (glaredetect)
+ ast_set_flag(p, LOCAL_CANCEL_QUEUE);
+ ast_mutex_unlock(&p->lock);
+ /* Remove from list */
+ AST_LIST_LOCK(&locals);
+ AST_LIST_REMOVE(&locals, p, list);
+ AST_LIST_UNLOCK(&locals);
+ /* Grab / release lock just in case */
+ ast_mutex_lock(&p->lock);
+ ast_mutex_unlock(&p->lock);
+ /* And destroy */
+ if (!glaredetect) {
+ ast_mutex_destroy(&p->lock);
+ ast_free(p);
+ }
+ return 0;
+ }
+ if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
+ /* Need to actually hangup since there is no PBX */
+ ochan = p->chan;
+ else
+ res = local_queue_frame(p, isoutbound, &f, NULL);
+ if (!res)
+ ast_mutex_unlock(&p->lock);
+ if (ochan)
+ ast_hangup(ochan);
+ return 0;
+}
+
+/*! \brief Create a call structure */
+static struct local_pvt *local_alloc(const char *data, int format)
+{
+ struct local_pvt *tmp = NULL;
+ char *c = NULL, *opts = NULL;
+
+ if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ return NULL;
+
+ /* Initialize private structure information */
+ ast_mutex_init(&tmp->lock);
+ ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
+
+ memcpy(&tmp->jb_conf, &g_jb_conf, sizeof(tmp->jb_conf));
+
+ /* Look for options */
+ if ((opts = strchr(tmp->exten, '/'))) {
+ *opts++ = '\0';
+ if (strchr(opts, 'n'))
+ ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
+ if (strchr(opts, 'j')) {
+ if (ast_test_flag(tmp, LOCAL_NO_OPTIMIZATION))
+ ast_set_flag(&tmp->jb_conf, AST_JB_ENABLED);
+ else {
+ ast_log(LOG_ERROR, "You must use the 'n' option for chan_local "
+ "to use the 'j' option to enable the jitterbuffer\n");
+ }
+ }
+ }
+
+ /* Look for a context */
+ if ((c = strchr(tmp->exten, '@')))
+ *c++ = '\0';
+
+ ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
+
+ tmp->reqformat = format;
+
+ if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
+ ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
+ ast_mutex_destroy(&tmp->lock);
+ ast_free(tmp);
+ tmp = NULL;
+ } else {
+ /* Add to list */
+ AST_LIST_LOCK(&locals);
+ AST_LIST_INSERT_HEAD(&locals, tmp, list);
+ AST_LIST_UNLOCK(&locals);
+ }
+
+ return tmp;
+}
+
+/*! \brief Start new local channel */
+static struct ast_channel *local_new(struct local_pvt *p, int state)
+{
+ struct ast_channel *tmp = NULL, *tmp2 = NULL;
+ int randnum = ast_random() & 0xffff, fmt = 0;
+ const char *t;
+ int ama;
+
+ /* Allocate two new Asterisk channels */
+ /* safe accountcode */
+ if (p->owner && p->owner->accountcode)
+ t = p->owner->accountcode;
+ else
+ t = "";
+
+ if (p->owner)
+ ama = p->owner->amaflags;
+ else
+ ama = 0;
+ if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum))
+ || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) {
+ if (tmp)
+ ast_channel_free(tmp);
+ if (tmp2)
+ ast_channel_free(tmp2);
+ ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
+ return NULL;
+ }
+
+ tmp2->tech = tmp->tech = &local_tech;
+
+ tmp->nativeformats = p->reqformat;
+ tmp2->nativeformats = p->reqformat;
+
+ /* Determine our read/write format and set it on each channel */
+ fmt = ast_best_codec(p->reqformat);
+ tmp->writeformat = fmt;
+ tmp2->writeformat = fmt;
+ tmp->rawwriteformat = fmt;
+ tmp2->rawwriteformat = fmt;
+ tmp->readformat = fmt;
+ tmp2->readformat = fmt;
+ tmp->rawreadformat = fmt;
+ tmp2->rawreadformat = fmt;
+
+ tmp->tech_pvt = p;
+ tmp2->tech_pvt = p;
+
+ p->owner = tmp;
+ p->chan = tmp2;
+ p->u_owner = ast_module_user_add(p->owner);
+ p->u_chan = ast_module_user_add(p->chan);
+
+ ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
+ ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
+ ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
+ tmp->priority = 1;
+ tmp2->priority = 1;
+
+ ast_jb_configure(tmp, &p->jb_conf);
+
+ return tmp;
+}
+
+
+/*! \brief Part of PBX interface */
+static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
+{
+ struct local_pvt *p = NULL;
+ struct ast_channel *chan = NULL;
+
+ /* Allocate a new private structure and then Asterisk channel */
+ if ((p = local_alloc(data, format)))
+ chan = local_new(p, AST_STATE_DOWN);
+
+ return chan;
+}
+
+/*! \brief CLI command "local show channels" */
+static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct local_pvt *p = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "local show channels";
+ e->usage =
+ "Usage: local show channels\n"
+ " Provides summary information on active local proxy channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ AST_LIST_LOCK(&locals);
+ if (!AST_LIST_EMPTY(&locals)) {
+ AST_LIST_TRAVERSE(&locals, p, list) {
+ ast_mutex_lock(&p->lock);
+ ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
+ ast_mutex_unlock(&p->lock);
+ }
+ } else
+ ast_cli(a->fd, "No local channels in use\n");
+ AST_LIST_UNLOCK(&locals);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_local[] = {
+ AST_CLI_DEFINE(locals_show, "List status of local channels"),
+};
+
+/*! \brief Load module into PBX, register channel */
+static int load_module(void)
+{
+ /* Make sure we can register our channel type */
+ if (ast_channel_register(&local_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+/*! \brief Unload the local proxy channel from Asterisk */
+static int unload_module(void)
+{
+ struct local_pvt *p = NULL;
+
+ /* First, take us out of the channel loop */
+ ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
+ ast_channel_unregister(&local_tech);
+ if (!AST_LIST_LOCK(&locals)) {
+ /* Hangup all interfaces if they have an owner */
+ AST_LIST_TRAVERSE(&locals, p, list) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ }
+ AST_LIST_UNLOCK(&locals);
+ AST_LIST_HEAD_DESTROY(&locals);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel");
diff --git a/trunk/channels/chan_mgcp.c b/trunk/channels/chan_mgcp.c
new file mode 100644
index 000000000..624013283
--- /dev/null
+++ b/trunk/channels/chan_mgcp.c
@@ -0,0 +1,4398 @@
+/*
+ * 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 Implementation of Media Gateway Control Protocol
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \par See also
+ * \arg \ref Config_mgcp
+ *
+ * \ingroup channel_drivers
+ */
+/*** MODULEINFO
+ <depend>res_features</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <sys/signal.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+#include "asterisk/cdr.h"
+#include "asterisk/astdb.h"
+#include "asterisk/features.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/utils.h"
+#include "asterisk/netsock.h"
+#include "asterisk/causes.h"
+#include "asterisk/dsp.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/event.h"
+
+/*
+ * Define to work around buggy dlink MGCP phone firmware which
+ * appears not to know that "rt" is part of the "G" package.
+ */
+/* #define DLINK_BUGGY_FIRMWARE */
+
+#define MGCPDUMPER
+#define DEFAULT_EXPIRY 120
+#define MAX_EXPIRY 3600
+#define CANREINVITE 1
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (in_addr_t)(-1)
+#endif
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+static const char tdesc[] = "Media Gateway Control Protocol (MGCP)";
+static const char config[] = "mgcp.conf";
+
+#define MGCP_DTMF_RFC2833 (1 << 0)
+#define MGCP_DTMF_INBAND (1 << 1)
+#define MGCP_DTMF_HYBRID (1 << 2)
+
+#define DEFAULT_MGCP_GW_PORT 2427 /*!< From RFC 2705 */
+#define DEFAULT_MGCP_CA_PORT 2727 /*!< From RFC 2705 */
+#define MGCP_MAX_PACKET 1500 /*!< Also from RFC 2543, should sub headers tho */
+#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit */
+#define MAX_RETRANS 5 /*!< Try only 5 times for retransmissions */
+
+/*! MGCP rtp stream modes { */
+#define MGCP_CX_SENDONLY 0
+#define MGCP_CX_RECVONLY 1
+#define MGCP_CX_SENDRECV 2
+#define MGCP_CX_CONF 3
+#define MGCP_CX_CONFERENCE 3
+#define MGCP_CX_MUTE 4
+#define MGCP_CX_INACTIVE 4
+/*! } */
+
+static char *mgcp_cxmodes[] = {
+ "sendonly",
+ "recvonly",
+ "sendrecv",
+ "confrnce",
+ "inactive"
+};
+
+enum {
+ MGCP_CMD_EPCF,
+ MGCP_CMD_CRCX,
+ MGCP_CMD_MDCX,
+ MGCP_CMD_DLCX,
+ MGCP_CMD_RQNT,
+ MGCP_CMD_NTFY,
+ MGCP_CMD_AUEP,
+ MGCP_CMD_AUCX,
+ MGCP_CMD_RSIP
+};
+
+static char context[AST_MAX_EXTENSION] = "default";
+
+static char language[MAX_LANGUAGE] = "";
+static char musicclass[MAX_MUSICCLASS] = "";
+static char cid_num[AST_MAX_EXTENSION] = "";
+static char cid_name[AST_MAX_EXTENSION] = "";
+
+static int dtmfmode = 0;
+static int nat = 0;
+
+static ast_group_t cur_callergroup = 0;
+static ast_group_t cur_pickupgroup = 0;
+
+static unsigned int tos = 0;
+static unsigned int tos_audio = 0;
+static unsigned int cos = 0;
+static unsigned int cos_audio = 0;
+
+static int immediate = 0;
+
+static int callwaiting = 0;
+
+static int callreturn = 0;
+
+static int slowsequence = 0;
+
+static int threewaycalling = 0;
+
+/*! This is for flashhook transfers */
+static int transfer = 0;
+
+static int cancallforward = 0;
+
+static int singlepath = 0;
+
+static int canreinvite = CANREINVITE;
+
+static char accountcode[AST_MAX_ACCOUNT_CODE] = "";
+
+static char mailbox[AST_MAX_EXTENSION];
+
+static int amaflags = 0;
+
+static int adsi = 0;
+
+static unsigned int oseq;
+
+/*! Wait up to 16 seconds for first digit (FXO logic) */
+static int firstdigittimeout = 16000;
+
+/*! How long to wait for following digits (FXO logic) */
+static int gendigittimeout = 8000;
+
+/*! How long to wait for an extra digit, if there is an ambiguous match */
+static int matchdigittimeout = 3000;
+
+/*! Protect the monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(netlock);
+
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+/*! This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+
+static int restart_monitor(void);
+
+static int capability = AST_FORMAT_ULAW;
+static int nonCodecCapability = AST_RTP_DTMF;
+
+static char ourhost[MAXHOSTNAMELEN];
+static struct in_addr __ourip;
+static int ourport;
+
+static int mgcpdebug = 0;
+
+static struct sched_context *sched;
+static struct io_context *io;
+/*! The private structures of the mgcp channels are linked for
+ ! selecting outgoing channels */
+
+#define MGCP_MAX_HEADERS 64
+#define MGCP_MAX_LINES 64
+
+struct mgcp_request {
+ int len;
+ char *verb;
+ char *identifier;
+ char *endpoint;
+ char *version;
+ int headers; /*!< MGCP Headers */
+ char *header[MGCP_MAX_HEADERS];
+ int lines; /*!< SDP Content */
+ char *line[MGCP_MAX_LINES];
+ char data[MGCP_MAX_PACKET];
+ int cmd; /*!< int version of verb = command */
+ unsigned int trid; /*!< int version of identifier = transaction id */
+ struct mgcp_request *next; /*!< next in the queue */
+};
+
+/*! \brief mgcp_message: MGCP message for queuing up */
+struct mgcp_message {
+ struct mgcp_endpoint *owner_ep;
+ struct mgcp_subchannel *owner_sub;
+ int retrans;
+ unsigned long expire;
+ unsigned int seqno;
+ int len;
+ struct mgcp_message *next;
+ char buf[0];
+};
+
+#define RESPONSE_TIMEOUT 30 /*!< in seconds */
+
+struct mgcp_response {
+ time_t whensent;
+ int len;
+ int seqno;
+ struct mgcp_response *next;
+ char buf[0];
+};
+
+#define MAX_SUBS 2
+
+#define SUB_REAL 0
+#define SUB_ALT 1
+
+struct mgcp_subchannel {
+ /*! subchannel magic string.
+ Needed to prove that any subchannel pointer passed by asterisk
+ really points to a valid subchannel memory area.
+ Ugly.. But serves the purpose for the time being.
+ */
+#define MGCP_SUBCHANNEL_MAGIC "!978!"
+ char magic[6];
+ ast_mutex_t lock;
+ int id;
+ struct ast_channel *owner;
+ struct mgcp_endpoint *parent;
+ struct ast_rtp *rtp;
+ struct sockaddr_in tmpdest;
+ char txident[80]; /*! \todo FIXME txident is replaced by rqnt_ident in endpoint.
+ This should be obsoleted */
+ char cxident[80];
+ char callid[80];
+ int cxmode;
+ struct mgcp_request *cx_queue; /*!< pending CX commands */
+ ast_mutex_t cx_queue_lock; /*!< CX queue lock */
+ int nat;
+ int iseq; /*!< Not used? RTP? */
+ int outgoing;
+ int alreadygone;
+ struct mgcp_subchannel *next; /*!< for out circular linked list */
+};
+
+#define MGCP_ONHOOK 1
+#define MGCP_OFFHOOK 2
+
+#define TYPE_TRUNK 1
+#define TYPE_LINE 2
+
+struct mgcp_endpoint {
+ ast_mutex_t lock;
+ char name[80];
+ struct mgcp_subchannel *sub; /*!< Pointer to our current connection, channel and stuff */
+ char accountcode[AST_MAX_ACCOUNT_CODE];
+ char exten[AST_MAX_EXTENSION]; /*!< Extention where to start */
+ char context[AST_MAX_EXTENSION];
+ char language[MAX_LANGUAGE];
+ char cid_num[AST_MAX_EXTENSION]; /*!< Caller*ID number */
+ char cid_name[AST_MAX_EXTENSION]; /*!< Caller*ID name */
+ char lastcallerid[AST_MAX_EXTENSION]; /*!< Last Caller*ID */
+ char dtmf_buf[AST_MAX_EXTENSION]; /*!< place to collect digits be */
+ char call_forward[AST_MAX_EXTENSION]; /*!< Last Caller*ID */
+ char musicclass[MAX_MUSICCLASS];
+ char curtone[80]; /*!< Current tone */
+ char mailbox[AST_MAX_EXTENSION];
+ struct ast_event_sub *mwi_event_sub;
+ ast_group_t callgroup;
+ ast_group_t pickupgroup;
+ int callwaiting;
+ int hascallwaiting;
+ int transfer;
+ int threewaycalling;
+ int singlepath;
+ int cancallforward;
+ int canreinvite;
+ int callreturn;
+ int dnd; /* How does this affect callwait? Do we just deny a mgcp_request if we're dnd? */
+ int hascallerid;
+ int hidecallerid;
+ int dtmfmode;
+ int amaflags;
+ int type;
+ int slowsequence; /*!< MS: Sequence the endpoint as a whole */
+ int group;
+ int iseq; /*!< Not used? */
+ int lastout; /*!< tracking this on the subchannels. Is it needed here? */
+ int needdestroy; /*!< Not used? */
+ int capability;
+ int nonCodecCapability;
+ int onhooktime;
+ int msgstate; /*!< voicemail message state */
+ int immediate;
+ int hookstate;
+ int adsi;
+ char rqnt_ident[80]; /*!< request identifier */
+ struct mgcp_request *rqnt_queue; /*!< pending RQNT commands */
+ ast_mutex_t rqnt_queue_lock;
+ struct mgcp_request *cmd_queue; /*!< pending commands other than RQNT */
+ ast_mutex_t cmd_queue_lock;
+ int delme; /*!< needed for reload */
+ int needaudit; /*!< needed for reload */
+ struct ast_dsp *dsp; /*!< XXX Should there be a dsp/subchannel? XXX */
+ /* owner is tracked on the subchannels, and the *sub indicates whos in charge */
+ /* struct ast_channel *owner; */
+ /* struct ast_rtp *rtp; */
+ /* struct sockaddr_in tmpdest; */
+ /* message go the the endpoint and not the channel so they stay here */
+ struct mgcp_endpoint *next;
+ struct mgcp_gateway *parent;
+};
+
+static struct mgcp_gateway {
+ /* A gateway containing one or more endpoints */
+ char name[80];
+ int isnamedottedip; /*!< is the name FQDN or dotted ip */
+ struct sockaddr_in addr;
+ struct sockaddr_in defaddr;
+ struct in_addr ourip;
+ int dynamic;
+ int expire; /*!< XXX Should we ever expire dynamic registrations? XXX */
+ struct mgcp_endpoint *endpoints;
+ struct ast_ha *ha;
+/* obsolete
+ time_t lastouttime;
+ int lastout;
+ int messagepending;
+*/
+/* Wildcard endpoint name */
+ char wcardep[30];
+ struct mgcp_message *msgs; /*!< gw msg queue */
+ ast_mutex_t msgs_lock; /*!< queue lock */
+ int retransid; /*!< retrans timer id */
+ int delme; /*!< needed for reload */
+ struct mgcp_response *responses;
+ struct mgcp_gateway *next;
+} *gateways;
+
+AST_MUTEX_DEFINE_STATIC(mgcp_reload_lock);
+static int mgcp_reloading = 0;
+
+/*! \brief gatelock: mutex for gateway/endpoint lists */
+AST_MUTEX_DEFINE_STATIC(gatelock);
+
+static int mgcpsock = -1;
+
+static struct sockaddr_in bindaddr;
+
+static struct ast_frame *mgcp_read(struct ast_channel *ast);
+static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp_request *req, char *msgrest);
+static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone);
+static int transmit_modify_request(struct mgcp_subchannel *sub);
+static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callernum, char *callername);
+static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs);
+static int transmit_connection_del(struct mgcp_subchannel *sub);
+static int transmit_audit_endpoint(struct mgcp_endpoint *p);
+static void start_rtp(struct mgcp_subchannel *sub);
+static void handle_response(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ int result, unsigned int ident, struct mgcp_request *resp);
+static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub);
+static char *mgcp_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static int reload_config(int reload);
+
+static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause);
+static int mgcp_call(struct ast_channel *ast, char *dest, int timeout);
+static int mgcp_hangup(struct ast_channel *ast);
+static int mgcp_answer(struct ast_channel *ast);
+static struct ast_frame *mgcp_read(struct ast_channel *ast);
+static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame);
+static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
+static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int mgcp_senddigit_begin(struct ast_channel *ast, char digit);
+static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int mgcp_devicestate(void *data);
+static void add_header_offhook(struct mgcp_subchannel *sub, struct mgcp_request *resp);
+
+static const struct ast_channel_tech mgcp_tech = {
+ .type = "MGCP",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_ULAW,
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
+ .requester = mgcp_request,
+ .devicestate = mgcp_devicestate,
+ .call = mgcp_call,
+ .hangup = mgcp_hangup,
+ .answer = mgcp_answer,
+ .read = mgcp_read,
+ .write = mgcp_write,
+ .indicate = mgcp_indicate,
+ .fixup = mgcp_fixup,
+ .send_digit_begin = mgcp_senddigit_begin,
+ .send_digit_end = mgcp_senddigit_end,
+ .bridge = ast_rtp_bridge,
+};
+
+static void mwi_event_cb(const struct ast_event *event, void *userdata)
+{
+ /* This module does not handle MWI in an event-based manner. However, it
+ * subscribes to MWI for each mailbox that is configured so that the core
+ * knows that we care about it. Then, chan_mgcp will get the MWI from the
+ * event cache instead of checking the mailbox directly. */
+}
+
+static int has_voicemail(struct mgcp_endpoint *p)
+{
+ int new_msgs;
+ struct ast_event *event;
+ char *mailbox, *context;
+
+ context = mailbox = ast_strdupa(p->mailbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ event = ast_event_get_cached(AST_EVENT_MWI,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_END);
+
+ if (event) {
+ new_msgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+ ast_event_destroy(event);
+ } else
+ new_msgs = ast_app_has_voicemail(p->mailbox, NULL);
+
+ return new_msgs;
+}
+
+static int unalloc_sub(struct mgcp_subchannel *sub)
+{
+ struct mgcp_endpoint *p = sub->parent;
+ if (p->sub == sub) {
+ ast_log(LOG_WARNING, "Trying to unalloc the real channel %s@%s?!?\n", p->name, p->parent->name);
+ return -1;
+ }
+ ast_debug(1, "Released sub %d of channel %s@%s\n", sub->id, p->name, p->parent->name);
+
+ sub->owner = NULL;
+ if (!ast_strlen_zero(sub->cxident)) {
+ transmit_connection_del(sub);
+ }
+ sub->cxident[0] = '\0';
+ sub->callid[0] = '\0';
+ sub->cxmode = MGCP_CX_INACTIVE;
+ sub->outgoing = 0;
+ sub->alreadygone = 0;
+ memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
+ if (sub->rtp) {
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+ dump_cmd_queues(NULL, sub); /* SC */
+ return 0;
+}
+
+/* modified for new transport mechanism */
+static int __mgcp_xmit(struct mgcp_gateway *gw, char *data, int len)
+{
+ int res;
+ if (gw->addr.sin_addr.s_addr)
+ res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->addr, sizeof(struct sockaddr_in));
+ else
+ res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->defaddr, sizeof(struct sockaddr_in));
+ if (res != len) {
+ ast_log(LOG_WARNING, "mgcp_xmit returned %d: %s\n", res, strerror(errno));
+ }
+ return res;
+}
+
+static int resend_response(struct mgcp_subchannel *sub, struct mgcp_response *resp)
+{
+ struct mgcp_endpoint *p = sub->parent;
+ int res;
+ if (mgcpdebug) {
+ ast_verbose("Retransmitting:\n%s\n to %s:%d\n", resp->buf, ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+ res = __mgcp_xmit(p->parent, resp->buf, resp->len);
+ if (res > 0)
+ res = 0;
+ return res;
+}
+
+static int send_response(struct mgcp_subchannel *sub, struct mgcp_request *req)
+{
+ struct mgcp_endpoint *p = sub->parent;
+ int res;
+ if (mgcpdebug) {
+ ast_verbose("Transmitting:\n%s\n to %s:%d\n", req->data, ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+ res = __mgcp_xmit(p->parent, req->data, req->len);
+ if (res > 0)
+ res = 0;
+ return res;
+}
+
+/* modified for new transport framework */
+static void dump_queue(struct mgcp_gateway *gw, struct mgcp_endpoint *p)
+{
+ struct mgcp_message *cur, *q = NULL, *w, *prev;
+
+ ast_mutex_lock(&gw->msgs_lock);
+ prev = NULL, cur = gw->msgs;
+ while (cur) {
+ if (!p || cur->owner_ep == p) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ gw->msgs = cur->next;
+
+ ast_log(LOG_NOTICE, "Removing message from %s transaction %u\n",
+ gw->name, cur->seqno);
+
+ w = cur;
+ cur = cur->next;
+ if (q) {
+ w->next = q;
+ } else {
+ w->next = NULL;
+ }
+ q = w;
+ } else {
+ prev = cur, cur=cur->next;
+ }
+ }
+ ast_mutex_unlock(&gw->msgs_lock);
+
+ while (q) {
+ cur = q;
+ q = q->next;
+ ast_free(cur);
+ }
+}
+
+static void mgcp_queue_frame(struct mgcp_subchannel *sub, struct ast_frame *f)
+{
+ for(;;) {
+ if (sub->owner) {
+ if (!ast_channel_trylock(sub->owner)) {
+ ast_queue_frame(sub->owner, f);
+ ast_channel_unlock(sub->owner);
+ break;
+ } else {
+ ast_mutex_unlock(&sub->lock);
+ usleep(1);
+ ast_mutex_lock(&sub->lock);
+ }
+ } else
+ break;
+ }
+}
+
+static void mgcp_queue_hangup(struct mgcp_subchannel *sub)
+{
+ for(;;) {
+ if (sub->owner) {
+ if (!ast_channel_trylock(sub->owner)) {
+ ast_queue_hangup(sub->owner);
+ ast_channel_unlock(sub->owner);
+ break;
+ } else {
+ ast_mutex_unlock(&sub->lock);
+ usleep(1);
+ ast_mutex_lock(&sub->lock);
+ }
+ } else
+ break;
+ }
+}
+
+static void mgcp_queue_control(struct mgcp_subchannel *sub, int control)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+ f.subclass = control;
+ return mgcp_queue_frame(sub, &f);
+}
+
+static int retrans_pkt(const void *data)
+{
+ struct mgcp_gateway *gw = (struct mgcp_gateway *)data;
+ struct mgcp_message *cur, *exq = NULL, *w, *prev;
+ int res = 0;
+
+ /* find out expired msgs */
+ ast_mutex_lock(&gw->msgs_lock);
+
+ prev = NULL, cur = gw->msgs;
+ while (cur) {
+ if (cur->retrans < MAX_RETRANS) {
+ cur->retrans++;
+ if (mgcpdebug) {
+ ast_verbose("Retransmitting #%d transaction %u on [%s]\n",
+ cur->retrans, cur->seqno, gw->name);
+ }
+ __mgcp_xmit(gw, cur->buf, cur->len);
+
+ prev = cur;
+ cur = cur->next;
+ } else {
+ if (prev)
+ prev->next = cur->next;
+ else
+ gw->msgs = cur->next;
+
+ ast_log(LOG_WARNING, "Maximum retries exceeded for transaction %u on [%s]\n",
+ cur->seqno, gw->name);
+
+ w = cur;
+ cur = cur->next;
+
+ if (exq) {
+ w->next = exq;
+ } else {
+ w->next = NULL;
+ }
+ exq = w;
+ }
+ }
+
+ if (!gw->msgs) {
+ gw->retransid = -1;
+ res = 0;
+ } else {
+ res = 1;
+ }
+ ast_mutex_unlock(&gw->msgs_lock);
+
+ while (exq) {
+ cur = exq;
+ /* time-out transaction */
+ handle_response(cur->owner_ep, cur->owner_sub, 406, cur->seqno, NULL);
+ exq = exq->next;
+ ast_free(cur);
+ }
+
+ return res;
+}
+
+/* modified for the new transaction mechanism */
+static int mgcp_postrequest(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ char *data, int len, unsigned int seqno)
+{
+ struct mgcp_message *msg;
+ struct mgcp_message *cur;
+ struct mgcp_gateway *gw;
+ struct timeval tv;
+
+ msg = ast_malloc(sizeof(*msg) + len);
+ if (!msg) {
+ return -1;
+ }
+ gw = ((p && p->parent) ? p->parent : NULL);
+ if (!gw) {
+ ast_free(msg);
+ return -1;
+ }
+/* SC
+ time(&t);
+ if (gw->messagepending && (gw->lastouttime + 20 < t)) {
+ ast_log(LOG_NOTICE, "Timeout waiting for response to message:%d, lastouttime: %ld, now: %ld. Dumping pending queue\n",
+ gw->msgs ? gw->msgs->seqno : -1, (long) gw->lastouttime, (long) t);
+ dump_queue(sub->parent);
+ }
+*/
+ msg->owner_sub = sub;
+ msg->owner_ep = p;
+ msg->seqno = seqno;
+ msg->next = NULL;
+ msg->len = len;
+ msg->retrans = 0;
+ memcpy(msg->buf, data, msg->len);
+
+ ast_mutex_lock(&gw->msgs_lock);
+ cur = gw->msgs;
+ if (cur) {
+ while(cur->next)
+ cur = cur->next;
+ cur->next = msg;
+ } else {
+ gw->msgs = msg;
+ }
+
+ tv = ast_tvnow();
+ msg->expire = tv.tv_sec * 1000 + tv.tv_usec / 1000 + DEFAULT_RETRANS;
+
+ if (gw->retransid == -1)
+ gw->retransid = ast_sched_add(sched, DEFAULT_RETRANS, retrans_pkt, (void *)gw);
+ ast_mutex_unlock(&gw->msgs_lock);
+/* SC
+ if (!gw->messagepending) {
+ gw->messagepending = 1;
+ gw->lastout = seqno;
+ gw->lastouttime = t;
+*/
+ __mgcp_xmit(gw, msg->buf, msg->len);
+ /* XXX Should schedule retransmission XXX */
+/* SC
+ } else
+ ast_debug(1, "Deferring transmission of transaction %d\n", seqno);
+*/
+ return 0;
+}
+
+/* modified for new transport */
+static int send_request(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ struct mgcp_request *req, unsigned int seqno)
+{
+ int res = 0;
+ struct mgcp_request **queue, *q, *r, *t;
+ ast_mutex_t *l;
+
+ ast_debug(1, "Slow sequence is %d\n", p->slowsequence);
+ if (p->slowsequence) {
+ queue = &p->cmd_queue;
+ l = &p->cmd_queue_lock;
+ ast_mutex_lock(l);
+ } else {
+ switch (req->cmd) {
+ case MGCP_CMD_DLCX:
+ queue = &sub->cx_queue;
+ l = &sub->cx_queue_lock;
+ ast_mutex_lock(l);
+ q = sub->cx_queue;
+ /* delete pending cx cmds */
+ while (q) {
+ r = q->next;
+ ast_free(q);
+ q = r;
+ }
+ *queue = NULL;
+ break;
+
+ case MGCP_CMD_CRCX:
+ case MGCP_CMD_MDCX:
+ queue = &sub->cx_queue;
+ l = &sub->cx_queue_lock;
+ ast_mutex_lock(l);
+ break;
+
+ case MGCP_CMD_RQNT:
+ queue = &p->rqnt_queue;
+ l = &p->rqnt_queue_lock;
+ ast_mutex_lock(l);
+ break;
+
+ default:
+ queue = &p->cmd_queue;
+ l = &p->cmd_queue_lock;
+ ast_mutex_lock(l);
+ break;
+ }
+ }
+
+ r = ast_malloc(sizeof(*r));
+ if (!r) {
+ ast_log(LOG_WARNING, "Cannot post MGCP request: insufficient memory\n");
+ ast_mutex_unlock(l);
+ return -1;
+ }
+ memcpy(r, req, sizeof(*r));
+
+ if (!(*queue)) {
+ if (mgcpdebug) {
+ ast_verbose("Posting Request:\n%s to %s:%d\n", req->data,
+ ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+
+ res = mgcp_postrequest(p, sub, req->data, req->len, seqno);
+ } else {
+ if (mgcpdebug) {
+ ast_verbose("Queueing Request:\n%s to %s:%d\n", req->data,
+ ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+ }
+
+ /* XXX find tail. We could also keep tail in the data struct for faster access */
+ for (t = *queue; t && t->next; t = t->next);
+
+ r->next = NULL;
+ if (t)
+ t->next = r;
+ else
+ *queue = r;
+
+ ast_mutex_unlock(l);
+
+ return res;
+}
+
+static int mgcp_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ int res;
+ struct mgcp_endpoint *p;
+ struct mgcp_subchannel *sub;
+ char tone[50] = "";
+ const char *distinctive_ring = NULL;
+ struct varshead *headp;
+ struct ast_var_t *current;
+
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP mgcp_call(%s)\n", ast->name);
+ }
+ sub = ast->tech_pvt;
+ p = sub->parent;
+ headp = &ast->varshead;
+ AST_LIST_TRAVERSE(headp,current,entries) {
+ /* Check whether there is an ALERT_INFO variable */
+ if (strcasecmp(ast_var_name(current),"ALERT_INFO") == 0) {
+ distinctive_ring = ast_var_value(current);
+ }
+ }
+
+ ast_mutex_lock(&sub->lock);
+ switch (p->hookstate) {
+ case MGCP_OFFHOOK:
+ if (!ast_strlen_zero(distinctive_ring)) {
+ snprintf(tone, sizeof(tone), "L/wt%s", distinctive_ring);
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP distinctive callwait %s\n", tone);
+ }
+ } else {
+ ast_copy_string(tone, "L/wt", sizeof(tone));
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP normal callwait %s\n", tone);
+ }
+ }
+ break;
+ case MGCP_ONHOOK:
+ default:
+ if (!ast_strlen_zero(distinctive_ring)) {
+ snprintf(tone, sizeof(tone), "L/r%s", distinctive_ring);
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP distinctive ring %s\n", tone);
+ }
+ } else {
+ ast_copy_string(tone, "L/rg", sizeof(tone));
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP default ring\n");
+ }
+ }
+ break;
+ }
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "mgcp_call called on %s, neither down nor reserved\n", ast->name);
+ ast_mutex_unlock(&sub->lock);
+ return -1;
+ }
+
+ res = 0;
+ sub->outgoing = 1;
+ sub->cxmode = MGCP_CX_RECVONLY;
+ if (p->type == TYPE_LINE) {
+ if (!sub->rtp) {
+ start_rtp(sub);
+ } else {
+ transmit_modify_request(sub);
+ }
+
+ if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(sub->next->callid)) {
+ /* try to prevent a callwait from disturbing the other connection */
+ sub->next->cxmode = MGCP_CX_RECVONLY;
+ transmit_modify_request(sub->next);
+ }
+
+ transmit_notify_request_with_callerid(sub, tone, ast->cid.cid_num, ast->cid.cid_name);
+ ast_setstate(ast, AST_STATE_RINGING);
+
+ if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(sub->next->callid)) {
+ /* Put the connection back in sendrecv */
+ sub->next->cxmode = MGCP_CX_SENDRECV;
+ transmit_modify_request(sub->next);
+ }
+ } else {
+ ast_log(LOG_NOTICE, "Don't know how to dial on trunks yet\n");
+ res = -1;
+ }
+ ast_mutex_unlock(&sub->lock);
+ ast_queue_control(ast, AST_CONTROL_RINGING);
+ return res;
+}
+
+static int mgcp_hangup(struct ast_channel *ast)
+{
+ struct mgcp_subchannel *sub = ast->tech_pvt;
+ struct mgcp_endpoint *p = sub->parent;
+
+ ast_debug(1, "mgcp_hangup(%s)\n", ast->name);
+ if (!ast->tech_pvt) {
+ ast_debug(1, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ if (strcmp(sub->magic, MGCP_SUBCHANNEL_MAGIC)) {
+ ast_debug(1, "Invalid magic. MGCP subchannel freed up already.\n");
+ return 0;
+ }
+ ast_mutex_lock(&sub->lock);
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name);
+ }
+
+ if ((p->dtmfmode & MGCP_DTMF_INBAND) && p->dsp) {
+ /* check whether other channel is active. */
+ if (!sub->next->owner) {
+ if (p->dtmfmode & MGCP_DTMF_HYBRID)
+ p->dtmfmode &= ~MGCP_DTMF_INBAND;
+ if (mgcpdebug) {
+ ast_verb(2, "MGCP free dsp on %s@%s\n", p->name, p->parent->name);
+ }
+ ast_dsp_free(p->dsp);
+ p->dsp = NULL;
+ }
+ }
+
+ sub->owner = NULL;
+ if (!ast_strlen_zero(sub->cxident)) {
+ transmit_connection_del(sub);
+ }
+ sub->cxident[0] = '\0';
+ if ((sub == p->sub) && sub->next->owner) {
+ if (p->hookstate == MGCP_OFFHOOK) {
+ if (sub->next->owner && ast_bridged_channel(sub->next->owner)) {
+ transmit_notify_request_with_callerid(p->sub, "L/wt", ast_bridged_channel(sub->next->owner)->cid.cid_num, ast_bridged_channel(sub->next->owner)->cid.cid_name);
+ }
+ } else {
+ /* set our other connection as the primary and swith over to it */
+ p->sub = sub->next;
+ p->sub->cxmode = MGCP_CX_RECVONLY;
+ transmit_modify_request(p->sub);
+ if (sub->next->owner && ast_bridged_channel(sub->next->owner)) {
+ transmit_notify_request_with_callerid(p->sub, "L/rg", ast_bridged_channel(sub->next->owner)->cid.cid_num, ast_bridged_channel(sub->next->owner)->cid.cid_name);
+ }
+ }
+
+ } else if ((sub == p->sub->next) && p->hookstate == MGCP_OFFHOOK) {
+ transmit_notify_request(sub, "L/v");
+ } else if (p->hookstate == MGCP_OFFHOOK) {
+ transmit_notify_request(sub, "L/ro");
+ } else {
+ transmit_notify_request(sub, "");
+ }
+
+ ast->tech_pvt = NULL;
+ sub->alreadygone = 0;
+ sub->outgoing = 0;
+ sub->cxmode = MGCP_CX_INACTIVE;
+ sub->callid[0] = '\0';
+ /* Reset temporary destination */
+ memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
+ if (sub->rtp) {
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+
+ ast_module_unref(ast_module_info->self);
+
+ if ((p->hookstate == MGCP_ONHOOK) && (!sub->next->rtp)) {
+ p->hidecallerid = 0;
+ if (p->hascallwaiting && !p->callwaiting) {
+ ast_verb(3, "Enabling call waiting on %s\n", ast->name);
+ p->callwaiting = -1;
+ }
+ if (has_voicemail(p)) {
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP mgcp_hangup(%s) on %s@%s set vmwi(+)\n",
+ ast->name, p->name, p->parent->name);
+ }
+ transmit_notify_request(sub, "L/vmwi(+)");
+ } else {
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP mgcp_hangup(%s) on %s@%s set vmwi(-)\n",
+ ast->name, p->name, p->parent->name);
+ }
+ transmit_notify_request(sub, "L/vmwi(-)");
+ }
+ }
+ ast_mutex_unlock(&sub->lock);
+ return 0;
+}
+
+static char *handle_mgcp_show_endpoints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct mgcp_gateway *mg;
+ struct mgcp_endpoint *me;
+ int hasendpoints = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mgcp show endpoints";
+ e->usage =
+ "Usage: mgcp show endpoints\n"
+ " Lists all endpoints known to the MGCP (Media Gateway Control Protocol) subsystem.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_mutex_lock(&gatelock);
+ mg = gateways;
+ while(mg) {
+ me = mg->endpoints;
+ ast_cli(a->fd, "Gateway '%s' at %s (%s)\n", mg->name, mg->addr.sin_addr.s_addr ? ast_inet_ntoa(mg->addr.sin_addr) : ast_inet_ntoa(mg->defaddr.sin_addr), mg->dynamic ? "Dynamic" : "Static");
+ while(me) {
+ /* Don't show wilcard endpoint */
+ if (strcmp(me->name, mg->wcardep) != 0)
+ ast_cli(a->fd, " -- '%s@%s in '%s' is %s\n", me->name, mg->name, me->context, me->sub->owner ? "active" : "idle");
+ hasendpoints = 1;
+ me = me->next;
+ }
+ if (!hasendpoints) {
+ ast_cli(a->fd, " << No Endpoints Defined >> ");
+ }
+ mg = mg->next;
+ }
+ ast_mutex_unlock(&gatelock);
+ return CLI_SUCCESS;
+}
+
+static char *handle_mgcp_audit_endpoint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct mgcp_gateway *mg;
+ struct mgcp_endpoint *me;
+ int found = 0;
+ char *ename,*gname, *c;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mgcp audit endpoint";
+ e->usage =
+ "Usage: mgcp audit endpoint <endpointid>\n"
+ " Lists the capabilities of an endpoint in the MGCP (Media Gateway Control Protocol) subsystem.\n"
+ " mgcp debug MUST be on to see the results of this command.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (!mgcpdebug) {
+ return CLI_SHOWUSAGE;
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ /* split the name into parts by null */
+ ename = a->argv[3];
+ gname = ename;
+ while (*gname) {
+ if (*gname == '@') {
+ *gname = 0;
+ gname++;
+ break;
+ }
+ gname++;
+ }
+ if (gname[0] == '[')
+ gname++;
+ if ((c = strrchr(gname, ']')))
+ *c = '\0';
+ ast_mutex_lock(&gatelock);
+ mg = gateways;
+ while(mg) {
+ if (!strcasecmp(mg->name, gname)) {
+ me = mg->endpoints;
+ while(me) {
+ if (!strcasecmp(me->name, ename)) {
+ found = 1;
+ transmit_audit_endpoint(me);
+ break;
+ }
+ me = me->next;
+ }
+ if (found) {
+ break;
+ }
+ }
+ mg = mg->next;
+ }
+ if (!found) {
+ ast_cli(a->fd, " << Could not find endpoint >> ");
+ }
+ ast_mutex_unlock(&gatelock);
+ return CLI_SUCCESS;
+}
+
+static char *handle_mgcp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mgcp set debug";
+ e->usage =
+ "Usage: mgcp set debug\n"
+ " Enables dumping of MGCP packets for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ mgcpdebug = 1;
+ ast_cli(a->fd, "MGCP Debugging Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_mgcp_set_debug_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mgcp set debug off";
+ e->usage =
+ "Usage: mgcp set debug off\n"
+ " Disables dumping of MGCP packets for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ mgcpdebug = 0;
+ ast_cli(a->fd, "MGCP Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_mgcp[] = {
+ AST_CLI_DEFINE(handle_mgcp_audit_endpoint, "Audit specified MGCP endpoint"),
+ AST_CLI_DEFINE(handle_mgcp_show_endpoints, "List defined MGCP endpoints"),
+ AST_CLI_DEFINE(handle_mgcp_set_debug, "Enable MGCP debugging"),
+ AST_CLI_DEFINE(handle_mgcp_set_debug_off, "Disable MGCP debugging"),
+ AST_CLI_DEFINE(mgcp_reload, "Reload MGCP configuration"),
+};
+
+static int mgcp_answer(struct ast_channel *ast)
+{
+ int res = 0;
+ struct mgcp_subchannel *sub = ast->tech_pvt;
+ struct mgcp_endpoint *p = sub->parent;
+
+ ast_mutex_lock(&sub->lock);
+ sub->cxmode = MGCP_CX_SENDRECV;
+ if (!sub->rtp) {
+ start_rtp(sub);
+ } else {
+ transmit_modify_request(sub);
+ }
+ ast_verb(3, "MGCP mgcp_answer(%s) on %s@%s-%d\n",
+ ast->name, p->name, p->parent->name, sub->id);
+ if (ast->_state != AST_STATE_UP) {
+ ast_setstate(ast, AST_STATE_UP);
+ ast_debug(1, "mgcp_answer(%s)\n", ast->name);
+ transmit_notify_request(sub, "");
+ transmit_modify_request(sub);
+ }
+ ast_mutex_unlock(&sub->lock);
+ return res;
+}
+
+static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub)
+{
+ /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */
+ struct ast_frame *f;
+
+ f = ast_rtp_read(sub->rtp);
+ /* Don't send RFC2833 if we're not supposed to */
+ if (f && (f->frametype == AST_FRAME_DTMF) && !(sub->parent->dtmfmode & MGCP_DTMF_RFC2833))
+ return &ast_null_frame;
+ if (sub->owner) {
+ /* We already hold the channel lock */
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass != sub->owner->nativeformats) {
+ ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
+ sub->owner->nativeformats = f->subclass;
+ ast_set_read_format(sub->owner, sub->owner->readformat);
+ ast_set_write_format(sub->owner, sub->owner->writeformat);
+ }
+ /* Courtesy fearnor aka alex@pilosoft.com */
+ if ((sub->parent->dtmfmode & MGCP_DTMF_INBAND) && (sub->parent->dsp)) {
+#if 0
+ ast_log(LOG_NOTICE, "MGCP ast_dsp_process\n");
+#endif
+ f = ast_dsp_process(sub->owner, sub->parent->dsp, f);
+ }
+ }
+ }
+ return f;
+}
+
+
+static struct ast_frame *mgcp_read(struct ast_channel *ast)
+{
+ struct ast_frame *f;
+ struct mgcp_subchannel *sub = ast->tech_pvt;
+ ast_mutex_lock(&sub->lock);
+ f = mgcp_rtp_read(sub);
+ ast_mutex_unlock(&sub->lock);
+ return f;
+}
+
+static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct mgcp_subchannel *sub = ast->tech_pvt;
+ int res = 0;
+ if (frame->frametype != AST_FRAME_VOICE) {
+ if (frame->frametype == AST_FRAME_IMAGE)
+ return 0;
+ else {
+ ast_log(LOG_WARNING, "Can't send %d type frames with MGCP write\n", frame->frametype);
+ return 0;
+ }
+ } else {
+ if (!(frame->subclass & ast->nativeformats)) {
+ ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
+ frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
+ return -1;
+ }
+ }
+ if (sub) {
+ ast_mutex_lock(&sub->lock);
+ if ((sub->parent->sub == sub) || !sub->parent->singlepath) {
+ if (sub->rtp) {
+ res = ast_rtp_write(sub->rtp, frame);
+ }
+ }
+ ast_mutex_unlock(&sub->lock);
+ }
+ return res;
+}
+
+static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct mgcp_subchannel *sub = newchan->tech_pvt;
+
+ ast_mutex_lock(&sub->lock);
+ ast_log(LOG_NOTICE, "mgcp_fixup(%s, %s)\n", oldchan->name, newchan->name);
+ if (sub->owner != oldchan) {
+ ast_mutex_unlock(&sub->lock);
+ ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
+ return -1;
+ }
+ sub->owner = newchan;
+ ast_mutex_unlock(&sub->lock);
+ return 0;
+}
+
+static int mgcp_senddigit_begin(struct ast_channel *ast, char digit)
+{
+ struct mgcp_subchannel *sub = ast->tech_pvt;
+ struct mgcp_endpoint *p = sub->parent;
+ int res = 0;
+
+ ast_mutex_lock(&sub->lock);
+ if (p->dtmfmode & MGCP_DTMF_INBAND || p->dtmfmode & MGCP_DTMF_HYBRID) {
+ ast_log(LOG_DEBUG, "Sending DTMF using inband/hybrid\n");
+ res = -1; /* Let asterisk play inband indications */
+ } else if (p->dtmfmode & MGCP_DTMF_RFC2833) {
+ ast_log(LOG_DEBUG, "Sending DTMF using RFC2833");
+ ast_rtp_senddigit_begin(sub->rtp, digit);
+ } else {
+ ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode);
+ }
+ ast_mutex_unlock(&sub->lock);
+
+ return res;
+}
+
+static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct mgcp_subchannel *sub = ast->tech_pvt;
+ struct mgcp_endpoint *p = sub->parent;
+ int res = 0;
+ char tmp[4];
+
+ ast_mutex_lock(&sub->lock);
+ if (p->dtmfmode & MGCP_DTMF_INBAND || p->dtmfmode & MGCP_DTMF_HYBRID) {
+ ast_log(LOG_DEBUG, "Stopping DTMF using inband/hybrid\n");
+ res = -1; /* Tell Asterisk to stop inband indications */
+ } else if (p->dtmfmode & MGCP_DTMF_RFC2833) {
+ ast_log(LOG_DEBUG, "Stopping DTMF using RFC2833\n");
+ tmp[0] = 'D';
+ tmp[1] = '/';
+ tmp[2] = digit;
+ tmp[3] = '\0';
+ transmit_notify_request(sub, tmp);
+ ast_rtp_senddigit_end(sub->rtp, digit);
+ } else {
+ ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode);
+ }
+ ast_mutex_unlock(&sub->lock);
+
+ return res;
+}
+
+/*!
+ * \brief mgcp_devicestate: channel callback for device status monitoring
+ * \param data tech/resource name of MGCP device to query
+ *
+ * Callback for device state management in channel subsystem
+ * to obtain device status (up/down) of a specific MGCP endpoint
+ *
+ * \return device status result (from devicestate.h) AST_DEVICE_INVALID (not available) or AST_DEVICE_UNKNOWN (available but unknown state)
+ */
+static int mgcp_devicestate(void *data)
+{
+ struct mgcp_gateway *g;
+ struct mgcp_endpoint *e = NULL;
+ char *tmp, *endpt, *gw;
+ int ret = AST_DEVICE_INVALID;
+
+ endpt = ast_strdupa(data);
+ if ((tmp = strchr(endpt, '@'))) {
+ *tmp++ = '\0';
+ gw = tmp;
+ } else
+ goto error;
+
+ ast_mutex_lock(&gatelock);
+ g = gateways;
+ while (g) {
+ if (strcasecmp(g->name, gw) == 0) {
+ e = g->endpoints;
+ break;
+ }
+ g = g->next;
+ }
+
+ if (!e)
+ goto error;
+
+ while (e) {
+ if (strcasecmp(e->name, endpt) == 0)
+ break;
+ e = e->next;
+ }
+
+ if (!e)
+ goto error;
+
+ /*
+ * As long as the gateway/endpoint is valid, we'll
+ * assume that the device is available and its state
+ * can be tracked.
+ */
+ ret = AST_DEVICE_UNKNOWN;
+
+error:
+ ast_mutex_unlock(&gatelock);
+ return ret;
+}
+
+static char *control2str(int ind) {
+ switch (ind) {
+ case AST_CONTROL_HANGUP:
+ return "Other end has hungup";
+ case AST_CONTROL_RING:
+ return "Local ring";
+ case AST_CONTROL_RINGING:
+ return "Remote end is ringing";
+ case AST_CONTROL_ANSWER:
+ return "Remote end has answered";
+ case AST_CONTROL_BUSY:
+ return "Remote end is busy";
+ case AST_CONTROL_TAKEOFFHOOK:
+ return "Make it go off hook";
+ case AST_CONTROL_OFFHOOK:
+ return "Line is off hook";
+ case AST_CONTROL_CONGESTION:
+ return "Congestion (circuits busy)";
+ case AST_CONTROL_FLASH:
+ return "Flash hook";
+ case AST_CONTROL_WINK:
+ return "Wink";
+ case AST_CONTROL_OPTION:
+ return "Set a low-level option";
+ case AST_CONTROL_RADIO_KEY:
+ return "Key Radio";
+ case AST_CONTROL_RADIO_UNKEY:
+ return "Un-Key Radio";
+ }
+ return "UNKNOWN";
+}
+
+static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
+{
+ struct mgcp_subchannel *sub = ast->tech_pvt;
+ int res = 0;
+
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP asked to indicate %d '%s' condition on channel %s\n",
+ ind, control2str(ind), ast->name);
+ }
+ ast_mutex_lock(&sub->lock);
+ switch(ind) {
+ case AST_CONTROL_RINGING:
+#ifdef DLINK_BUGGY_FIRMWARE
+ transmit_notify_request(sub, "rt");
+#else
+ transmit_notify_request(sub, "G/rt");
+#endif
+ break;
+ case AST_CONTROL_BUSY:
+ transmit_notify_request(sub, "L/bz");
+ break;
+ case AST_CONTROL_CONGESTION:
+ transmit_notify_request(sub, "G/cg");
+ break;
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, data, NULL);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ case -1:
+ transmit_notify_request(sub, "");
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
+ res = -1;
+ }
+ ast_mutex_unlock(&sub->lock);
+ return res;
+}
+
+static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state)
+{
+ struct ast_channel *tmp;
+ struct mgcp_endpoint *i = sub->parent;
+ int fmt;
+
+ tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id);
+ if (tmp) {
+ tmp->tech = &mgcp_tech;
+ tmp->nativeformats = i->capability;
+ if (!tmp->nativeformats)
+ tmp->nativeformats = capability;
+ fmt = ast_best_codec(tmp->nativeformats);
+ ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id);
+ if (sub->rtp)
+ ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp));
+ if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) {
+ i->dsp = ast_dsp_new();
+ ast_dsp_set_features(i->dsp,DSP_FEATURE_DTMF_DETECT);
+ /* this is to prevent clipping of dtmf tones during dsp processing */
+ ast_dsp_digitmode(i->dsp, DSP_DIGITMODE_NOQUELCH);
+ } else {
+ i->dsp = NULL;
+ }
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->writeformat = fmt;
+ tmp->rawwriteformat = fmt;
+ tmp->readformat = fmt;
+ tmp->rawreadformat = fmt;
+ tmp->tech_pvt = sub;
+ if (!ast_strlen_zero(i->language))
+ ast_string_field_set(tmp, language, i->language);
+ if (!ast_strlen_zero(i->accountcode))
+ ast_string_field_set(tmp, accountcode, i->accountcode);
+ if (i->amaflags)
+ tmp->amaflags = i->amaflags;
+ sub->owner = tmp;
+ ast_module_ref(ast_module_info->self);
+ tmp->callgroup = i->callgroup;
+ tmp->pickupgroup = i->pickupgroup;
+ ast_string_field_set(tmp, call_forward, i->call_forward);
+ ast_copy_string(tmp->context, i->context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
+
+ /* Don't use ast_set_callerid() here because it will
+ * generate a needless NewCallerID event */
+ tmp->cid.cid_ani = ast_strdup(i->cid_num);
+
+ if (!i->adsi)
+ tmp->adsicpe = AST_ADSI_UNAVAILABLE;
+ tmp->priority = 1;
+ if (sub->rtp)
+ ast_jb_configure(tmp, &global_jbconf);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ tmp = NULL;
+ }
+ }
+ ast_verb(3, "MGCP mgcp_new(%s) created in state: %s\n",
+ tmp->name, ast_state2str(state));
+ } else {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ }
+ return tmp;
+}
+
+static char* get_sdp_by_line(char* line, char *name, int nameLen)
+{
+ if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') {
+ char* r = line + nameLen + 1;
+ while (*r && (*r < 33)) ++r;
+ return r;
+ }
+ return "";
+}
+
+static char *get_sdp(struct mgcp_request *req, char *name)
+{
+ int x;
+ int len = strlen(name);
+ char *r;
+
+ for (x=0; x<req->lines; x++) {
+ r = get_sdp_by_line(req->line[x], name, len);
+ if (r[0] != '\0') return r;
+ }
+ return "";
+}
+
+static void sdpLineNum_iterator_init(int* iterator)
+{
+ *iterator = 0;
+}
+
+static char* get_sdp_iterate(int* iterator, struct mgcp_request *req, char *name)
+{
+ int len = strlen(name);
+ char *r;
+ while (*iterator < req->lines) {
+ r = get_sdp_by_line(req->line[(*iterator)++], name, len);
+ if (r[0] != '\0') return r;
+ }
+ return "";
+}
+
+static char *__get_header(struct mgcp_request *req, char *name, int *start)
+{
+ int x;
+ int len = strlen(name);
+ char *r;
+ for (x=*start;x<req->headers;x++) {
+ if (!strncasecmp(req->header[x], name, len) &&
+ (req->header[x][len] == ':')) {
+ r = req->header[x] + len + 1;
+ while(*r && (*r < 33))
+ r++;
+ *start = x+1;
+ return r;
+ }
+ }
+ /* Don't return NULL, so get_header is always a valid pointer */
+ return "";
+}
+
+static char *get_header(struct mgcp_request *req, char *name)
+{
+ int start = 0;
+ return __get_header(req, name, &start);
+}
+
+/*! \brief get_csv: (SC:) get comma separated value */
+static char *get_csv(char *c, int *len, char **next)
+{
+ char *s;
+
+ *next = NULL, *len = 0;
+ if (!c) return NULL;
+
+ while (*c && (*c < 33 || *c == ','))
+ c++;
+
+ s = c;
+ while (*c && (*c >= 33 && *c != ','))
+ c++, (*len)++;
+ *next = c;
+
+ if (*len == 0)
+ s = NULL, *next = NULL;
+
+ return s;
+}
+
+static struct mgcp_subchannel *find_subchannel_and_lock(char *name, int msgid, struct sockaddr_in *sin)
+{
+ struct mgcp_endpoint *p = NULL;
+ struct mgcp_subchannel *sub = NULL;
+ struct mgcp_gateway *g;
+ char tmp[256] = "";
+ char *at = NULL, *c;
+ int found = 0;
+ if (name) {
+ ast_copy_string(tmp, name, sizeof(tmp));
+ at = strchr(tmp, '@');
+ if (!at) {
+ ast_log(LOG_NOTICE, "Endpoint '%s' has no at sign!\n", name);
+ return NULL;
+ }
+ *at++ = '\0';
+ }
+ ast_mutex_lock(&gatelock);
+ if (at && (at[0] == '[')) {
+ at++;
+ c = strrchr(at, ']');
+ if (c)
+ *c = '\0';
+ }
+ g = gateways;
+ while(g) {
+ if ((!name || !strcasecmp(g->name, at)) &&
+ (sin || g->addr.sin_addr.s_addr || g->defaddr.sin_addr.s_addr)) {
+ /* Found the gateway. If it's dynamic, save it's address -- now for the endpoint */
+ if (sin && g->dynamic && name) {
+ if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) ||
+ (g->addr.sin_port != sin->sin_port)) {
+ memcpy(&g->addr, sin, sizeof(g->addr));
+ if (ast_ouraddrfor(&g->addr.sin_addr, &g->ourip))
+ memcpy(&g->ourip, &__ourip, sizeof(g->ourip));
+ ast_verb(3, "Registered MGCP gateway '%s' at %s port %d\n", g->name, ast_inet_ntoa(g->addr.sin_addr), ntohs(g->addr.sin_port));
+ }
+ }
+ /* not dynamic, check if the name matches */
+ else if (name) {
+ if (strcasecmp(g->name, at)) {
+ g = g->next;
+ continue;
+ }
+ }
+ /* not dynamic, no name, check if the addr matches */
+ else if (!name && sin) {
+ if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) ||
+ (g->addr.sin_port != sin->sin_port)) {
+ g = g->next;
+ continue;
+ }
+ } else {
+ g = g->next;
+ continue;
+ }
+ /* SC */
+ p = g->endpoints;
+ while(p) {
+ ast_debug(1, "Searching on %s@%s for subchannel\n",
+ p->name, g->name);
+ if (msgid) {
+#if 0 /* new transport mech */
+ sub = p->sub;
+ do {
+ ast_debug(1, "Searching on %s@%s-%d for subchannel with lastout: %d\n",
+ p->name, g->name, sub->id, msgid);
+ if (sub->lastout == msgid) {
+ ast_debug(1, "Found subchannel sub%d to handle request %d sub->lastout: %d\n",
+ sub->id, msgid, sub->lastout);
+ found = 1;
+ break;
+ }
+ sub = sub->next;
+ } while (sub != p->sub);
+ if (found) {
+ break;
+ }
+#endif
+ /* SC */
+ sub = p->sub;
+ found = 1;
+ /* SC */
+ break;
+ } else if (name && !strcasecmp(p->name, tmp)) {
+ ast_debug(1, "Coundn't determine subchannel, assuming current master %s@%s-%d\n",
+ p->name, g->name, p->sub->id);
+ sub = p->sub;
+ found = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if (sub && found) {
+ ast_mutex_lock(&sub->lock);
+ break;
+ }
+ }
+ g = g->next;
+ }
+ ast_mutex_unlock(&gatelock);
+ if (!sub) {
+ if (name) {
+ if (g)
+ ast_log(LOG_NOTICE, "Endpoint '%s' not found on gateway '%s'\n", tmp, at);
+ else
+ ast_log(LOG_NOTICE, "Gateway '%s' (and thus its endpoint '%s') does not exist\n", at, tmp);
+ }
+ }
+ return sub;
+}
+
+static void parse(struct mgcp_request *req)
+{
+ /* Divide fields by NULL's */
+ char *c;
+ int f = 0;
+ c = req->data;
+
+ /* First header starts immediately */
+ req->header[f] = c;
+ while(*c) {
+ if (*c == '\n') {
+ /* We've got a new header */
+ *c = 0;
+#if 0
+ printf("Header: %s (%d)\n", req->header[f], strlen(req->header[f]));
+#endif
+ if (ast_strlen_zero(req->header[f])) {
+ /* Line by itself means we're now in content */
+ c++;
+ break;
+ }
+ if (f >= MGCP_MAX_HEADERS - 1) {
+ ast_log(LOG_WARNING, "Too many MGCP headers...\n");
+ } else
+ f++;
+ req->header[f] = c + 1;
+ } else if (*c == '\r') {
+ /* Ignore but eliminate \r's */
+ *c = 0;
+ }
+ c++;
+ }
+ /* Check for last header */
+ if (!ast_strlen_zero(req->header[f]))
+ f++;
+ req->headers = f;
+ /* Now we process any mime content */
+ f = 0;
+ req->line[f] = c;
+ while(*c) {
+ if (*c == '\n') {
+ /* We've got a new line */
+ *c = 0;
+#if 0
+ printf("Line: %s (%d)\n", req->line[f], strlen(req->line[f]));
+#endif
+ if (f >= MGCP_MAX_LINES - 1) {
+ ast_log(LOG_WARNING, "Too many SDP lines...\n");
+ } else
+ f++;
+ req->line[f] = c + 1;
+ } else if (*c == '\r') {
+ /* Ignore and eliminate \r's */
+ *c = 0;
+ }
+ c++;
+ }
+ /* Check for last line */
+ if (!ast_strlen_zero(req->line[f]))
+ f++;
+ req->lines = f;
+ /* Parse up the initial header */
+ c = req->header[0];
+ while(*c && *c < 33) c++;
+ /* First the verb */
+ req->verb = c;
+ while(*c && (*c > 32)) c++;
+ if (*c) {
+ *c = '\0';
+ c++;
+ while(*c && (*c < 33)) c++;
+ req->identifier = c;
+ while(*c && (*c > 32)) c++;
+ if (*c) {
+ *c = '\0';
+ c++;
+ while(*c && (*c < 33)) c++;
+ req->endpoint = c;
+ while(*c && (*c > 32)) c++;
+ if (*c) {
+ *c = '\0';
+ c++;
+ while(*c && (*c < 33)) c++;
+ req->version = c;
+ while(*c && (*c > 32)) c++;
+ while(*c && (*c < 33)) c++;
+ while(*c && (*c > 32)) c++;
+ *c = '\0';
+ }
+ }
+ }
+
+ if (mgcpdebug) {
+ ast_verbose("Verb: '%s', Identifier: '%s', Endpoint: '%s', Version: '%s'\n",
+ req->verb, req->identifier, req->endpoint, req->version);
+ ast_verbose("%d headers, %d lines\n", req->headers, req->lines);
+ }
+ if (*c)
+ ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c);
+}
+
+static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req)
+{
+ char *m;
+ char *c;
+ char *a;
+ char host[258];
+ int len;
+ int portno;
+ int peercapability, peerNonCodecCapability;
+ struct sockaddr_in sin;
+ char *codecs;
+ struct ast_hostent ahp; struct hostent *hp;
+ int codec, codec_count=0;
+ int iterator;
+ struct mgcp_endpoint *p = sub->parent;
+
+ /* Get codec and RTP info from SDP */
+ m = get_sdp(req, "m");
+ c = get_sdp(req, "c");
+ if (ast_strlen_zero(m) || ast_strlen_zero(c)) {
+ ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
+ return -1;
+ }
+ if (sscanf(c, "IN IP4 %256s", host) != 1) {
+ ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
+ return -1;
+ }
+ /* XXX This could block for a long time, and block the main thread! XXX */
+ hp = ast_gethostbyname(host, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
+ return -1;
+ }
+ if (sscanf(m, "audio %d RTP/AVP %n", &portno, &len) != 1) {
+ ast_log(LOG_WARNING, "Unable to determine port number for RTP in '%s'\n", m);
+ return -1;
+ }
+ sin.sin_family = AF_INET;
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ sin.sin_port = htons(portno);
+ ast_rtp_set_peer(sub->rtp, &sin);
+#if 0
+ printf("Peer RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+#endif
+ /* Scan through the RTP payload types specified in a "m=" line: */
+ ast_rtp_pt_clear(sub->rtp);
+ codecs = ast_strdupa(m + len);
+ while (!ast_strlen_zero(codecs)) {
+ if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
+ if (codec_count)
+ break;
+ ast_log(LOG_WARNING, "Error in codec string '%s' at '%s'\n", m, codecs);
+ return -1;
+ }
+ ast_rtp_set_m_type(sub->rtp, codec);
+ codec_count++;
+ codecs += len;
+ }
+
+ /* Next, scan through each "a=rtpmap:" line, noting each */
+ /* specified RTP payload type (with corresponding MIME subtype): */
+ sdpLineNum_iterator_init(&iterator);
+ while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
+ char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
+ if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2)
+ continue;
+ /* Note: should really look at the 'freq' and '#chans' params too */
+ ast_rtp_set_rtpmap_type(sub->rtp, codec, "audio", mimeSubtype, 0);
+ }
+
+ /* Now gather all of the codecs that were asked for: */
+ ast_rtp_get_current_formats(sub->rtp, &peercapability, &peerNonCodecCapability);
+ p->capability = capability & peercapability;
+ if (mgcpdebug) {
+ ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n",
+ capability, peercapability, p->capability);
+ ast_verbose("Non-codec capabilities: us - %d, them - %d, combined - %d\n",
+ nonCodecCapability, peerNonCodecCapability, p->nonCodecCapability);
+ }
+ if (!p->capability) {
+ ast_log(LOG_WARNING, "No compatible codecs!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int add_header(struct mgcp_request *req, char *var, char *value)
+{
+ if (req->len >= sizeof(req->data) - 4) {
+ ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
+ return -1;
+ }
+ if (req->lines) {
+ ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
+ return -1;
+ }
+ req->header[req->headers] = req->data + req->len;
+ snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s: %s\r\n", var, value);
+ req->len += strlen(req->header[req->headers]);
+ if (req->headers < MGCP_MAX_HEADERS)
+ req->headers++;
+ else {
+ ast_log(LOG_WARNING, "Out of header space\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int add_line(struct mgcp_request *req, char *line)
+{
+ if (req->len >= sizeof(req->data) - 4) {
+ ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
+ return -1;
+ }
+ if (!req->lines) {
+ /* Add extra empty return */
+ ast_copy_string(req->data + req->len, "\r\n", sizeof(req->data) - req->len);
+ req->len += strlen(req->data + req->len);
+ }
+ req->line[req->lines] = req->data + req->len;
+ snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
+ req->len += strlen(req->line[req->lines]);
+ if (req->lines < MGCP_MAX_LINES)
+ req->lines++;
+ else {
+ ast_log(LOG_WARNING, "Out of line space\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int init_resp(struct mgcp_request *req, char *resp, struct mgcp_request *orig, char *resprest)
+{
+ /* Initialize a response */
+ if (req->headers || req->len) {
+ ast_log(LOG_WARNING, "Request already initialized?!?\n");
+ return -1;
+ }
+ req->header[req->headers] = req->data + req->len;
+ snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %s %s\r\n", resp, orig->identifier, resprest);
+ req->len += strlen(req->header[req->headers]);
+ if (req->headers < MGCP_MAX_HEADERS)
+ req->headers++;
+ else
+ ast_log(LOG_WARNING, "Out of header space\n");
+ return 0;
+}
+
+static int init_req(struct mgcp_endpoint *p, struct mgcp_request *req, char *verb)
+{
+ /* Initialize a response */
+ if (req->headers || req->len) {
+ ast_log(LOG_WARNING, "Request already initialized?!?\n");
+ return -1;
+ }
+ req->header[req->headers] = req->data + req->len;
+ /* check if we need brackets around the gw name */
+ if (p->parent->isnamedottedip)
+ snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@[%s] MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+ else
+ snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+ req->len += strlen(req->header[req->headers]);
+ if (req->headers < MGCP_MAX_HEADERS)
+ req->headers++;
+ else
+ ast_log(LOG_WARNING, "Out of header space\n");
+ return 0;
+}
+
+
+static int respprep(struct mgcp_request *resp, struct mgcp_endpoint *p, char *msg, struct mgcp_request *req, char *msgrest)
+{
+ memset(resp, 0, sizeof(*resp));
+ init_resp(resp, msg, req, msgrest);
+ return 0;
+}
+
+static int reqprep(struct mgcp_request *req, struct mgcp_endpoint *p, char *verb)
+{
+ memset(req, 0, sizeof(struct mgcp_request));
+ oseq++;
+ if (oseq > 999999999)
+ oseq = 1;
+ init_req(p, req, verb);
+ return 0;
+}
+
+static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp_request *req, char *msgrest)
+{
+ struct mgcp_request resp;
+ struct mgcp_endpoint *p = sub->parent;
+ struct mgcp_response *mgr;
+
+ respprep(&resp, p, msg, req, msgrest);
+ mgr = ast_calloc(1, sizeof(*mgr) + resp.len + 1);
+ if (mgr) {
+ /* Store MGCP response in case we have to retransmit */
+ sscanf(req->identifier, "%d", &mgr->seqno);
+ time(&mgr->whensent);
+ mgr->len = resp.len;
+ memcpy(mgr->buf, resp.data, resp.len);
+ mgr->buf[resp.len] = '\0';
+ mgr->next = p->parent->responses;
+ p->parent->responses = mgr;
+ }
+ return send_response(sub, &resp);
+}
+
+
+static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp *rtp)
+{
+ int len;
+ int codec;
+ char costr[80];
+ struct sockaddr_in sin;
+ char v[256];
+ char s[256];
+ char o[256];
+ char c[256];
+ char t[256];
+ char m[256] = "";
+ char a[1024] = "";
+ int x;
+ struct sockaddr_in dest;
+ struct mgcp_endpoint *p = sub->parent;
+ /* XXX We break with the "recommendation" and send our IP, in order that our
+ peer doesn't have to ast_gethostbyname() us XXX */
+ len = 0;
+ if (!sub->rtp) {
+ ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
+ return -1;
+ }
+ ast_rtp_get_us(sub->rtp, &sin);
+ if (rtp) {
+ ast_rtp_get_peer(rtp, &dest);
+ } else {
+ if (sub->tmpdest.sin_addr.s_addr) {
+ dest.sin_addr = sub->tmpdest.sin_addr;
+ dest.sin_port = sub->tmpdest.sin_port;
+ /* Reset temporary destination */
+ memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
+ } else {
+ dest.sin_addr = p->parent->ourip;
+ dest.sin_port = sin.sin_port;
+ }
+ }
+ if (mgcpdebug) {
+ ast_verbose("We're at %s port %d\n", ast_inet_ntoa(p->parent->ourip), ntohs(sin.sin_port));
+ }
+ ast_copy_string(v, "v=0\r\n", sizeof(v));
+ snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", (int)getpid(), (int)getpid(), ast_inet_ntoa(dest.sin_addr));
+ ast_copy_string(s, "s=session\r\n", sizeof(s));
+ snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
+ ast_copy_string(t, "t=0 0\r\n", sizeof(t));
+ snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port));
+ for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) {
+ if (p->capability & x) {
+ if (mgcpdebug) {
+ ast_verbose("Answering with capability %d\n", x);
+ }
+ codec = ast_rtp_lookup_code(sub->rtp, 1, x);
+ if (codec > -1) {
+ snprintf(costr, sizeof(costr), " %d", codec);
+ strncat(m, costr, sizeof(m) - strlen(m) - 1);
+ snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x, 0));
+ strncat(a, costr, sizeof(a) - strlen(a) - 1);
+ }
+ }
+ }
+ for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
+ if (p->nonCodecCapability & x) {
+ if (mgcpdebug) {
+ ast_verbose("Answering with non-codec capability %d\n", x);
+ }
+ codec = ast_rtp_lookup_code(sub->rtp, 0, x);
+ if (codec > -1) {
+ snprintf(costr, sizeof(costr), " %d", codec);
+ strncat(m, costr, sizeof(m) - strlen(m) - 1);
+ snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x, 0));
+ strncat(a, costr, sizeof(a) - strlen(a) - 1);
+ if (x == AST_RTP_DTMF) {
+ /* Indicate we support DTMF... Not sure about 16,
+ but MSN supports it so dang it, we will too... */
+ snprintf(costr, sizeof costr, "a=fmtp:%d 0-16\r\n", codec);
+ strncat(a, costr, sizeof(a) - strlen(a) - 1);
+ }
+ }
+ }
+ }
+ strncat(m, "\r\n", sizeof(m) - strlen(m) - 1);
+ len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a);
+ snprintf(costr, sizeof(costr), "%d", len);
+ add_line(resp, v);
+ add_line(resp, o);
+ add_line(resp, s);
+ add_line(resp, c);
+ add_line(resp, t);
+ add_line(resp, m);
+ add_line(resp, a);
+ return 0;
+}
+
+static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs)
+{
+ struct mgcp_request resp;
+ char local[256];
+ char tmp[80];
+ int x;
+ int capability;
+ struct mgcp_endpoint *p = sub->parent;
+
+ capability = p->capability;
+ if (codecs)
+ capability = codecs;
+ if (ast_strlen_zero(sub->cxident) && rtp) {
+ /* We don't have a CXident yet, store the destination and
+ wait a bit */
+ ast_rtp_get_peer(rtp, &sub->tmpdest);
+ return 0;
+ }
+ ast_copy_string(local, "p:20", sizeof(local));
+ for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) {
+ if (p->capability & x) {
+ snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0));
+ strncat(local, tmp, sizeof(local) - strlen(local) - 1);
+ }
+ }
+ reqprep(&resp, p, "MDCX");
+ add_header(&resp, "C", sub->callid);
+ add_header(&resp, "L", local);
+ add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
+ /* X header should not be sent. kept for compatibility */
+ add_header(&resp, "X", sub->txident);
+ add_header(&resp, "I", sub->cxident);
+ /*add_header(&resp, "S", "");*/
+ add_sdp(&resp, sub, rtp);
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_MDCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
+}
+
+static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp)
+{
+ struct mgcp_request resp;
+ char local[256];
+ char tmp[80];
+ int x;
+ struct mgcp_endpoint *p = sub->parent;
+
+ ast_copy_string(local, "p:20", sizeof(local));
+ for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) {
+ if (p->capability & x) {
+ snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0));
+ strncat(local, tmp, sizeof(local) - strlen(local) - 1);
+ }
+ }
+ if (mgcpdebug) {
+ ast_verb(3, "Creating connection for %s@%s-%d in cxmode: %s callid: %s\n",
+ p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid);
+ }
+ reqprep(&resp, p, "CRCX");
+ add_header(&resp, "C", sub->callid);
+ add_header(&resp, "L", local);
+ add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
+ /* X header should not be sent. kept for compatibility */
+ add_header(&resp, "X", sub->txident);
+ /*add_header(&resp, "S", "");*/
+ add_sdp(&resp, sub, rtp);
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_CRCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
+}
+
+static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone)
+{
+ struct mgcp_request resp;
+ struct mgcp_endpoint *p = sub->parent;
+
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP Asked to indicate tone: %s on %s@%s-%d in cxmode: %s\n",
+ tone, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode]);
+ }
+ ast_copy_string(p->curtone, tone, sizeof(p->curtone));
+ reqprep(&resp, p, "RQNT");
+ add_header(&resp, "X", p->rqnt_ident); /* SC */
+ switch (p->hookstate) {
+ case MGCP_ONHOOK:
+ add_header(&resp, "R", "L/hd(N)");
+ break;
+ case MGCP_OFFHOOK:
+ add_header_offhook(sub, &resp);
+ break;
+ }
+ if (!ast_strlen_zero(tone)) {
+ add_header(&resp, "S", tone);
+ }
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_RQNT;
+ resp.trid = oseq;
+ return send_request(p, NULL, &resp, oseq); /* SC */
+}
+
+static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callernum, char *callername)
+{
+ struct mgcp_request resp;
+ char tone2[256];
+ char *l, *n;
+ struct timeval t = ast_tvnow();
+ struct ast_tm tm;
+ struct mgcp_endpoint *p = sub->parent;
+
+ ast_localtime(&t, &tm, NULL);
+ n = callername;
+ l = callernum;
+ if (!n)
+ n = "";
+ if (!l)
+ l = "";
+
+ /* Keep track of last callerid for blacklist and callreturn */
+ ast_copy_string(p->lastcallerid, l, sizeof(p->lastcallerid));
+
+ snprintf(tone2, sizeof(tone2), "%s,L/ci(%02d/%02d/%02d/%02d,%s,%s)", tone,
+ tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, l, n);
+ ast_copy_string(p->curtone, tone, sizeof(p->curtone));
+ reqprep(&resp, p, "RQNT");
+ add_header(&resp, "X", p->rqnt_ident); /* SC */
+ switch (p->hookstate) {
+ case MGCP_ONHOOK:
+ add_header(&resp, "R", "L/hd(N)");
+ break;
+ case MGCP_OFFHOOK:
+ add_header_offhook(sub, &resp);
+ break;
+ }
+ if (!ast_strlen_zero(tone2)) {
+ add_header(&resp, "S", tone2);
+ }
+ if (mgcpdebug) {
+ ast_verb(3, "MGCP Asked to indicate tone: %s on %s@%s-%d in cxmode: %s\n",
+ tone2, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode]);
+ }
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_RQNT;
+ resp.trid = oseq;
+ return send_request(p, NULL, &resp, oseq); /* SC */
+}
+
+static int transmit_modify_request(struct mgcp_subchannel *sub)
+{
+ struct mgcp_request resp;
+ struct mgcp_endpoint *p = sub->parent;
+
+ if (ast_strlen_zero(sub->cxident)) {
+ /* We don't have a CXident yet, store the destination and
+ wait a bit */
+ return 0;
+ }
+ if (mgcpdebug) {
+ ast_verb(3, "Modified %s@%s-%d with new mode: %s on callid: %s\n",
+ p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid);
+ }
+ reqprep(&resp, p, "MDCX");
+ add_header(&resp, "C", sub->callid);
+ add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
+ /* X header should not be sent. kept for compatibility */
+ add_header(&resp, "X", sub->txident);
+ add_header(&resp, "I", sub->cxident);
+ switch (sub->parent->hookstate) {
+ case MGCP_ONHOOK:
+ add_header(&resp, "R", "L/hd(N)");
+ break;
+ case MGCP_OFFHOOK:
+ add_header_offhook(sub, &resp);
+ break;
+ }
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_MDCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
+}
+
+
+static void add_header_offhook(struct mgcp_subchannel *sub, struct mgcp_request *resp)
+{
+ struct mgcp_endpoint *p = sub->parent;
+
+ if (p && p->sub && p->sub->owner && p->sub->owner->_state >= AST_STATE_RINGING && (p->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)))
+ add_header(resp, "R", "L/hu(N),L/hf(N)");
+ else
+ add_header(resp, "R", "L/hu(N),L/hf(N),D/[0-9#*](N)");
+}
+
+static int transmit_audit_endpoint(struct mgcp_endpoint *p)
+{
+ struct mgcp_request resp;
+ reqprep(&resp, p, "AUEP");
+ /* removed unknown param VS */
+ /*add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,E,MD,M");*/
+ add_header(&resp, "F", "A");
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_AUEP;
+ resp.trid = oseq;
+ return send_request(p, NULL, &resp, oseq); /* SC */
+}
+
+static int transmit_connection_del(struct mgcp_subchannel *sub)
+{
+ struct mgcp_endpoint *p = sub->parent;
+ struct mgcp_request resp;
+
+ if (mgcpdebug) {
+ ast_verb(3, "Delete connection %s %s@%s-%d with new mode: %s on callid: %s\n",
+ sub->cxident, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid);
+ }
+ reqprep(&resp, p, "DLCX");
+ /* check if call id is avail */
+ if (sub->callid[0])
+ add_header(&resp, "C", sub->callid);
+ /* X header should not be sent. kept for compatibility */
+ add_header(&resp, "X", sub->txident);
+ /* check if cxident is avail */
+ if (sub->cxident[0])
+ add_header(&resp, "I", sub->cxident);
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_DLCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
+}
+
+static int transmit_connection_del_w_params(struct mgcp_endpoint *p, char *callid, char *cxident)
+{
+ struct mgcp_request resp;
+
+ if (mgcpdebug) {
+ ast_verb(3, "Delete connection %s %s@%s on callid: %s\n",
+ cxident ? cxident : "", p->name, p->parent->name, callid ? callid : "");
+ }
+ reqprep(&resp, p, "DLCX");
+ /* check if call id is avail */
+ if (callid && *callid)
+ add_header(&resp, "C", callid);
+ /* check if cxident is avail */
+ if (cxident && *cxident)
+ add_header(&resp, "I", cxident);
+ /* fill in new fields */
+ resp.cmd = MGCP_CMD_DLCX;
+ resp.trid = oseq;
+ return send_request(p, p->sub, &resp, oseq);
+}
+
+/*! \brief dump_cmd_queues: (SC:) cleanup pending commands */
+static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub)
+{
+ struct mgcp_request *t, *q;
+
+ if (p) {
+ ast_mutex_lock(&p->rqnt_queue_lock);
+ for (q = p->rqnt_queue; q; t = q->next, ast_free(q), q=t);
+ p->rqnt_queue = NULL;
+ ast_mutex_unlock(&p->rqnt_queue_lock);
+
+ ast_mutex_lock(&p->cmd_queue_lock);
+ for (q = p->cmd_queue; q; t = q->next, ast_free(q), q=t);
+ p->cmd_queue = NULL;
+ ast_mutex_unlock(&p->cmd_queue_lock);
+
+ ast_mutex_lock(&p->sub->cx_queue_lock);
+ for (q = p->sub->cx_queue; q; t = q->next, ast_free(q), q=t);
+ p->sub->cx_queue = NULL;
+ ast_mutex_unlock(&p->sub->cx_queue_lock);
+
+ ast_mutex_lock(&p->sub->next->cx_queue_lock);
+ for (q = p->sub->next->cx_queue; q; t = q->next, ast_free(q), q=t);
+ p->sub->next->cx_queue = NULL;
+ ast_mutex_unlock(&p->sub->next->cx_queue_lock);
+ } else if (sub) {
+ ast_mutex_lock(&sub->cx_queue_lock);
+ for (q = sub->cx_queue; q; t = q->next, ast_free(q), q=t);
+ sub->cx_queue = NULL;
+ ast_mutex_unlock(&sub->cx_queue_lock);
+ }
+}
+
+
+/*! \brief find_command: (SC:) remove command transaction from queue */
+static struct mgcp_request *find_command(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ struct mgcp_request **queue, ast_mutex_t *l, int ident)
+{
+ struct mgcp_request *prev, *req;
+
+ ast_mutex_lock(l);
+ for (prev = NULL, req = *queue; req; prev = req, req = req->next) {
+ if (req->trid == ident) {
+ /* remove from queue */
+ if (!prev)
+ *queue = req->next;
+ else
+ prev->next = req->next;
+
+ /* send next pending command */
+ if (*queue) {
+ if (mgcpdebug) {
+ ast_verbose("Posting Queued Request:\n%s to %s:%d\n", (*queue)->data,
+ ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+
+ mgcp_postrequest(p, sub, (*queue)->data, (*queue)->len, (*queue)->trid);
+ }
+ break;
+ }
+ }
+ ast_mutex_unlock(l);
+ return req;
+}
+
+/* modified for new transport mechanism */
+static void handle_response(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ int result, unsigned int ident, struct mgcp_request *resp)
+{
+ char *c;
+ struct mgcp_request *req;
+ struct mgcp_gateway *gw = p->parent;
+
+ if (result < 200) {
+ /* provisional response */
+ return;
+ }
+
+ if (p->slowsequence)
+ req = find_command(p, sub, &p->cmd_queue, &p->cmd_queue_lock, ident);
+ else if (sub)
+ req = find_command(p, sub, &sub->cx_queue, &sub->cx_queue_lock, ident);
+ else if (!(req = find_command(p, sub, &p->rqnt_queue, &p->rqnt_queue_lock, ident)))
+ req = find_command(p, sub, &p->cmd_queue, &p->cmd_queue_lock, ident);
+
+ if (!req) {
+ ast_verb(3, "No command found on [%s] for transaction %d. Ignoring...\n",
+ gw->name, ident);
+ return;
+ }
+
+ if (p && (result >= 400) && (result <= 599)) {
+ switch (result) {
+ case 401:
+ p->hookstate = MGCP_OFFHOOK;
+ break;
+ case 402:
+ p->hookstate = MGCP_ONHOOK;
+ break;
+ case 406:
+ ast_log(LOG_NOTICE, "Transaction %d timed out\n", ident);
+ break;
+ case 407:
+ ast_log(LOG_NOTICE, "Transaction %d aborted\n", ident);
+ break;
+ }
+ if (sub) {
+ if (sub->owner) {
+ ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n",
+ result, p->name, p->parent->name, sub ? sub->id:-1);
+ mgcp_queue_hangup(sub);
+ }
+ } else {
+ if (p->sub->next->owner) {
+ ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n",
+ result, p->name, p->parent->name, sub ? sub->id:-1);
+ mgcp_queue_hangup(p->sub);
+ }
+
+ if (p->sub->owner) {
+ ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n",
+ result, p->name, p->parent->name, sub ? sub->id:-1);
+ mgcp_queue_hangup(p->sub);
+ }
+
+ dump_cmd_queues(p, NULL);
+ }
+ }
+
+ if (resp) {
+ if (req->cmd == MGCP_CMD_CRCX) {
+ if ((c = get_header(resp, "I"))) {
+ if (!ast_strlen_zero(c) && sub) {
+ /* if we are hanging up do not process this conn. */
+ if (sub->owner) {
+ if (!ast_strlen_zero(sub->cxident)) {
+ if (strcasecmp(c, sub->cxident)) {
+ ast_log(LOG_WARNING, "Subchannel already has a cxident. sub->cxident: %s requested %s\n", sub->cxident, c);
+ }
+ }
+ ast_copy_string(sub->cxident, c, sizeof(sub->cxident));
+ if (sub->tmpdest.sin_addr.s_addr) {
+ transmit_modify_with_sdp(sub, NULL, 0);
+ }
+ } else {
+ /* XXX delete this one
+ callid and conn id may already be lost.
+ so the following del conn may have a side effect of
+ cleaning up the next subchannel */
+ transmit_connection_del(sub);
+ }
+ }
+ }
+ }
+
+ if (req->cmd == MGCP_CMD_AUEP) {
+ /* check stale connection ids */
+ if ((c = get_header(resp, "I"))) {
+ char *v, *n;
+ int len;
+ while ((v = get_csv(c, &len, &n))) {
+ if (len) {
+ if (strncasecmp(v, p->sub->cxident, len) &&
+ strncasecmp(v, p->sub->next->cxident, len)) {
+ /* connection id not found. delete it */
+ char cxident[80] = "";
+
+ if (len > (sizeof(cxident) - 1))
+ len = sizeof(cxident) - 1;
+ ast_copy_string(cxident, v, len);
+ ast_verb(3, "Non existing connection id %s on %s@%s \n",
+ cxident, p->name, gw->name);
+ transmit_connection_del_w_params(p, NULL, cxident);
+ }
+ }
+ c = n;
+ }
+ }
+
+ /* Try to determine the hookstate returned from an audit endpoint command */
+ if ((c = get_header(resp, "ES"))) {
+ if (!ast_strlen_zero(c)) {
+ if (strstr(c, "hu")) {
+ if (p->hookstate != MGCP_ONHOOK) {
+ /* XXX cleanup if we think we are offhook XXX */
+ if ((p->sub->owner || p->sub->next->owner ) &&
+ p->hookstate == MGCP_OFFHOOK)
+ mgcp_queue_hangup(sub);
+ p->hookstate = MGCP_ONHOOK;
+
+ /* update the requested events according to the new hookstate */
+ transmit_notify_request(p->sub, "");
+
+ ast_verb(3, "Setting hookstate of %s@%s to ONHOOK\n", p->name, gw->name);
+ }
+ } else if (strstr(c, "hd")) {
+ if (p->hookstate != MGCP_OFFHOOK) {
+ p->hookstate = MGCP_OFFHOOK;
+
+ /* update the requested events according to the new hookstate */
+ transmit_notify_request(p->sub, "");
+
+ ast_verb(3, "Setting hookstate of %s@%s to OFFHOOK\n", p->name, gw->name);
+ }
+ }
+ }
+ }
+ }
+
+ if (resp && resp->lines) {
+ /* do not process sdp if we are hanging up. this may be a late response */
+ if (sub && sub->owner) {
+ if (!sub->rtp)
+ start_rtp(sub);
+ if (sub->rtp)
+ process_sdp(sub, resp);
+ }
+ }
+ }
+
+ ast_free(req);
+}
+
+static void start_rtp(struct mgcp_subchannel *sub)
+{
+ ast_mutex_lock(&sub->lock);
+ /* check again to be on the safe side */
+ if (sub->rtp) {
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+ /* Allocate the RTP now */
+ sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ if (sub->rtp && sub->owner)
+ ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp));
+ if (sub->rtp) {
+ ast_rtp_setqos(sub->rtp, tos_audio, cos_audio, "MGCP RTP");
+ ast_rtp_setnat(sub->rtp, sub->nat);
+ }
+#if 0
+ ast_rtp_set_callback(p->rtp, rtpready);
+ ast_rtp_set_data(p->rtp, p);
+#endif
+ /* Make a call*ID */
+ snprintf(sub->callid, sizeof(sub->callid), "%08lx%s", ast_random(), sub->txident);
+ /* Transmit the connection create */
+ transmit_connect_with_sdp(sub, NULL);
+ ast_mutex_unlock(&sub->lock);
+}
+
+static void *mgcp_ss(void *data)
+{
+ struct ast_channel *chan = data;
+ struct mgcp_subchannel *sub = chan->tech_pvt;
+ struct mgcp_endpoint *p = sub->parent;
+ /* char exten[AST_MAX_EXTENSION] = ""; */
+ int len = 0;
+ int timeout = firstdigittimeout;
+ int res= 0;
+ int getforward = 0;
+ int loop_pause = 100;
+
+ len = strlen(p->dtmf_buf);
+
+ while(len < AST_MAX_EXTENSION-1) {
+ res = 1; /* Assume that we will get a digit */
+ while (strlen(p->dtmf_buf) == len){
+ ast_safe_sleep(chan, loop_pause);
+ timeout -= loop_pause;
+ if (timeout <= 0){
+ res = 0;
+ break;
+ }
+ res = 1;
+ }
+
+ timeout = 0;
+ len = strlen(p->dtmf_buf);
+
+ if (!ast_ignore_pattern(chan->context, p->dtmf_buf)) {
+ /*res = tone_zone_play_tone(p->subs[index].zfd, -1);*/
+ ast_indicate(chan, -1);
+ } else {
+ /* XXX Redundant? We should already be playing dialtone */
+ /*tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);*/
+ transmit_notify_request(sub, "L/dl");
+ }
+ if (ast_exists_extension(chan, chan->context, p->dtmf_buf, 1, p->cid_num)) {
+ if (!res || !ast_matchmore_extension(chan, chan->context, p->dtmf_buf, 1, p->cid_num)) {
+ if (getforward) {
+ /* Record this as the forwarding extension */
+ ast_copy_string(p->call_forward, p->dtmf_buf, sizeof(p->call_forward));
+ ast_verb(3, "Setting call forward to '%s' on channel %s\n",
+ p->call_forward, chan->name);
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ if (res)
+ break;
+ usleep(500000);
+ /*res = tone_zone_play_tone(p->subs[index].zfd, -1);*/
+ ast_indicate(chan, -1);
+ sleep(1);
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);*/
+ transmit_notify_request(sub, "L/dl");
+ len = 0;
+ getforward = 0;
+ } else {
+ /*res = tone_zone_play_tone(p->subs[index].zfd, -1);*/
+ ast_indicate(chan, -1);
+ ast_copy_string(chan->exten, p->dtmf_buf, sizeof(chan->exten));
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ ast_set_callerid(chan,
+ p->hidecallerid ? "" : p->cid_num,
+ p->hidecallerid ? "" : p->cid_name,
+ chan->cid.cid_ani ? NULL : p->cid_num);
+ ast_setstate(chan, AST_STATE_RING);
+ /*zt_enable_ec(p);*/
+ if (p->dtmfmode & MGCP_DTMF_HYBRID) {
+ p->dtmfmode |= MGCP_DTMF_INBAND;
+ ast_indicate(chan, -1);
+ }
+ res = ast_pbx_run(chan);
+ if (res) {
+ ast_log(LOG_WARNING, "PBX exited non-zero\n");
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);*/
+ /*transmit_notify_request(p, "nbz", 1);*/
+ transmit_notify_request(sub, "G/cg");
+ }
+ return NULL;
+ }
+ } else {
+ /* It's a match, but they just typed a digit, and there is an ambiguous match,
+ so just set the timeout to matchdigittimeout and wait some more */
+ timeout = matchdigittimeout;
+ }
+ } else if (res == 0) {
+ ast_debug(1, "not enough digits (and no ambiguous match)...\n");
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);*/
+ transmit_notify_request(sub, "G/cg");
+ /*zt_wait_event(p->subs[index].zfd);*/
+ ast_hangup(chan);
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ return NULL;
+ } else if (p->hascallwaiting && p->callwaiting && !strcmp(p->dtmf_buf, "*70")) {
+ ast_verb(3, "Disabling call waiting on %s\n", chan->name);
+ /* Disable call waiting if enabled */
+ p->callwaiting = 0;
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ len = 0;
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ timeout = firstdigittimeout;
+ } else if (!strcmp(p->dtmf_buf,ast_pickup_ext())) {
+ /* Scan all channels and see if any there
+ * ringing channqels with that have call groups
+ * that equal this channels pickup group
+ */
+ if (ast_pickup_call(chan)) {
+ ast_log(LOG_WARNING, "No call pickup possible...\n");
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);*/
+ transmit_notify_request(sub, "G/cg");
+ }
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ ast_hangup(chan);
+ return NULL;
+ } else if (!p->hidecallerid && !strcmp(p->dtmf_buf, "*67")) {
+ ast_verb(3, "Disabling Caller*ID on %s\n", chan->name);
+ /* Disable Caller*ID if enabled */
+ p->hidecallerid = 1;
+ ast_set_callerid(chan, "", "", NULL);
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ len = 0;
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ timeout = firstdigittimeout;
+ } else if (p->callreturn && !strcmp(p->dtmf_buf, "*69")) {
+ res = 0;
+ if (!ast_strlen_zero(p->lastcallerid)) {
+ res = ast_say_digit_str(chan, p->lastcallerid, "", chan->language);
+ }
+ if (!res)
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ break;
+ } else if (!strcmp(p->dtmf_buf, "*78")) {
+ /* Do not disturb */
+ ast_verb(3, "Enabled DND on channel %s\n", chan->name);
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ p->dnd = 1;
+ getforward = 0;
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ len = 0;
+ } else if (!strcmp(p->dtmf_buf, "*79")) {
+ /* Do not disturb */
+ ast_verb(3, "Disabled DND on channel %s\n", chan->name);
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ p->dnd = 0;
+ getforward = 0;
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ len = 0;
+ } else if (p->cancallforward && !strcmp(p->dtmf_buf, "*72")) {
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ getforward = 1;
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ len = 0;
+ } else if (p->cancallforward && !strcmp(p->dtmf_buf, "*73")) {
+ ast_verb(3, "Cancelling call forwarding on channel %s\n", chan->name);
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ memset(p->call_forward, 0, sizeof(p->call_forward));
+ getforward = 0;
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ len = 0;
+ } else if (!strcmp(p->dtmf_buf, ast_parking_ext()) &&
+ sub->next->owner && ast_bridged_channel(sub->next->owner)) {
+ /* This is a three way call, the main call being a real channel,
+ and we're parking the first call. */
+ ast_masq_park_call(ast_bridged_channel(sub->next->owner), chan, 0, NULL);
+ ast_verb(3, "Parking call to '%s'\n", chan->name);
+ break;
+ } else if (!ast_strlen_zero(p->lastcallerid) && !strcmp(p->dtmf_buf, "*60")) {
+ ast_verb(3, "Blacklisting number %s\n", p->lastcallerid);
+ res = ast_db_put("blacklist", p->lastcallerid, "1");
+ if (!res) {
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ len = 0;
+ }
+ } else if (p->hidecallerid && !strcmp(p->dtmf_buf, "*82")) {
+ ast_verb(3, "Enabling Caller*ID on %s\n", chan->name);
+ /* Enable Caller*ID if enabled */
+ p->hidecallerid = 0;
+ ast_set_callerid(chan, p->cid_num, p->cid_name, NULL);
+ /*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);*/
+ transmit_notify_request(sub, "L/sl");
+ len = 0;
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ timeout = firstdigittimeout;
+ } else if (!ast_canmatch_extension(chan, chan->context, p->dtmf_buf, 1, chan->cid.cid_num) &&
+ ((p->dtmf_buf[0] != '*') || (strlen(p->dtmf_buf) > 2))) {
+ ast_debug(1, "Can't match %s from '%s' in context %s\n", p->dtmf_buf, chan->cid.cid_num ? chan->cid.cid_num : "<Unknown Caller>", chan->context);
+ break;
+ }
+ if (!timeout)
+ timeout = gendigittimeout;
+ if (len && !ast_ignore_pattern(chan->context, p->dtmf_buf))
+ /*tone_zone_play_tone(p->subs[index].zfd, -1);*/
+ ast_indicate(chan, -1);
+ }
+#if 0
+ for (;;) {
+ res = ast_waitfordigit(chan, to);
+ if (!res) {
+ ast_debug(1, "Timeout...\n");
+ break;
+ }
+ if (res < 0) {
+ ast_debug(1, "Got hangup...\n");
+ ast_hangup(chan);
+ break;
+ }
+ exten[pos++] = res;
+ if (!ast_ignore_pattern(chan->context, exten))
+ ast_indicate(chan, -1);
+ if (ast_matchmore_extension(chan, chan->context, exten, 1, chan->callerid)) {
+ if (ast_exists_extension(chan, chan->context, exten, 1, chan->callerid))
+ to = 3000;
+ else
+ to = 8000;
+ } else
+ break;
+ }
+ if (ast_exists_extension(chan, chan->context, exten, 1, chan->callerid)) {
+ ast_copy_string(chan->exten, exten, sizeof(chan->exten)1);
+ if (!p->rtp) {
+ start_rtp(p);
+ }
+ ast_setstate(chan, AST_STATE_RING);
+ chan->rings = 1;
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_WARNING, "Unable to launch PBX on %s\n", chan->name);
+ } else {
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ return NULL;
+ }
+ }
+#endif
+ ast_hangup(chan);
+ memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
+ return NULL;
+}
+
+static int attempt_transfer(struct mgcp_endpoint *p)
+{
+ /* *************************
+ * I hope this works.
+ * Copied out of chan_zap
+ * Cross your fingers
+ * *************************/
+
+ /* In order to transfer, we need at least one of the channels to
+ actually be in a call bridge. We can't conference two applications
+ together (but then, why would we want to?) */
+ if (ast_bridged_channel(p->sub->owner)) {
+ /* The three-way person we're about to transfer to could still be in MOH, so
+ stop if now if appropriate */
+ if (ast_bridged_channel(p->sub->next->owner))
+ ast_queue_control(p->sub->next->owner, AST_CONTROL_UNHOLD);
+ if (p->sub->owner->_state == AST_STATE_RINGING) {
+ ast_indicate(ast_bridged_channel(p->sub->next->owner), AST_CONTROL_RINGING);
+ }
+ if (ast_channel_masquerade(p->sub->next->owner, ast_bridged_channel(p->sub->owner))) {
+ ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+ ast_bridged_channel(p->sub->owner)->name, p->sub->next->owner->name);
+ return -1;
+ }
+ /* Orphan the channel */
+ unalloc_sub(p->sub->next);
+ } else if (ast_bridged_channel(p->sub->next->owner)) {
+ if (p->sub->owner->_state == AST_STATE_RINGING) {
+ ast_indicate(ast_bridged_channel(p->sub->next->owner), AST_CONTROL_RINGING);
+ }
+ ast_queue_control(p->sub->next->owner, AST_CONTROL_UNHOLD);
+ if (ast_channel_masquerade(p->sub->owner, ast_bridged_channel(p->sub->next->owner))) {
+ ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+ ast_bridged_channel(p->sub->next->owner)->name, p->sub->owner->name);
+ return -1;
+ }
+ /*swap_subs(p, SUB_THREEWAY, SUB_REAL);*/
+ ast_verb(3, "Swapping %d for %d on %s@%s\n", p->sub->id, p->sub->next->id, p->name, p->parent->name);
+ p->sub = p->sub->next;
+ unalloc_sub(p->sub->next);
+ /* Tell the caller not to hangup */
+ return 1;
+ } else {
+ ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
+ p->sub->owner->name, p->sub->next->owner->name);
+ p->sub->next->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ if (p->sub->next->owner) {
+ p->sub->next->alreadygone = 1;
+ mgcp_queue_hangup(p->sub->next);
+ }
+ }
+ return 0;
+}
+
+static void handle_hd_hf(struct mgcp_subchannel *sub, char *ev)
+{
+ struct mgcp_endpoint *p = sub->parent;
+ struct ast_channel *c;
+ pthread_t t;
+
+ /* Off hook / answer */
+ if (sub->outgoing) {
+ /* Answered */
+ if (sub->owner) {
+ if (ast_bridged_channel(sub->owner))
+ ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
+ sub->cxmode = MGCP_CX_SENDRECV;
+ if (!sub->rtp) {
+ start_rtp(sub);
+ } else {
+ transmit_modify_request(sub);
+ }
+ /*transmit_notify_request(sub, "aw");*/
+ transmit_notify_request(sub, "");
+ mgcp_queue_control(sub, AST_CONTROL_ANSWER);
+ }
+ } else {
+ /* Start switch */
+ /*sub->cxmode = MGCP_CX_SENDRECV;*/
+ if (!sub->owner) {
+ if (!sub->rtp) {
+ start_rtp(sub);
+ } else {
+ transmit_modify_request(sub);
+ }
+ if (p->immediate) {
+ /* The channel is immediately up. Start right away */
+#ifdef DLINK_BUGGY_FIRMWARE
+ transmit_notify_request(sub, "rt");
+#else
+ transmit_notify_request(sub, "G/rt");
+#endif
+ c = mgcp_new(sub, AST_STATE_RING);
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to start PBX on channel %s@%s\n", p->name, p->parent->name);
+ transmit_notify_request(sub, "G/cg");
+ ast_hangup(c);
+ }
+ } else {
+ if (has_voicemail(p)) {
+ transmit_notify_request(sub, "L/sl");
+ } else {
+ transmit_notify_request(sub, "L/dl");
+ }
+ c = mgcp_new(sub, AST_STATE_DOWN);
+ if (c) {
+ if (ast_pthread_create_detached(&t, NULL, mgcp_ss, c)) {
+ ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", p->name, p->parent->name);
+ }
+ }
+ } else {
+ if (p->hookstate == MGCP_OFFHOOK) {
+ ast_log(LOG_WARNING, "Off hook, but already have owner on %s@%s\n", p->name, p->parent->name);
+ } else {
+ ast_log(LOG_WARNING, "On hook, but already have owner on %s@%s\n", p->name, p->parent->name);
+ ast_log(LOG_WARNING, "If we're onhook why are we here trying to handle a hd or hf?\n");
+ }
+ if (ast_bridged_channel(sub->owner))
+ ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
+ sub->cxmode = MGCP_CX_SENDRECV;
+ if (!sub->rtp) {
+ start_rtp(sub);
+ } else {
+ transmit_modify_request(sub);
+ }
+ /*transmit_notify_request(sub, "aw");*/
+ transmit_notify_request(sub, "");
+ /*ast_queue_control(sub->owner, AST_CONTROL_ANSWER);*/
+ }
+ }
+}
+
+static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, struct sockaddr_in *sin)
+{
+ char *ev, *s;
+ struct ast_frame f = { 0, };
+ struct mgcp_endpoint *p = sub->parent;
+ struct mgcp_gateway *g = NULL;
+ int res;
+
+ if (mgcpdebug) {
+ ast_verbose("Handling request '%s' on %s@%s\n", req->verb, p->name, p->parent->name);
+ }
+ /* Clear out potential response */
+ if (!strcasecmp(req->verb, "RSIP")) {
+ /* Test if this RSIP request is just a keepalive */
+ if(!strcasecmp( get_header(req, "RM"), "X-keepalive")) {
+ ast_verb(3, "Received keepalive request from %s@%s\n", p->name, p->parent->name);
+ transmit_response(sub, "200", req, "OK");
+ } else {
+ dump_queue(p->parent, p);
+ dump_cmd_queues(p, NULL);
+
+ if ((strcmp(p->name, p->parent->wcardep) != 0)) {
+ ast_verb(3, "Resetting interface %s@%s\n", p->name, p->parent->name);
+ }
+ /* For RSIP on wildcard we reset all endpoints */
+ if (!strcmp(p->name, p->parent->wcardep)) {
+ /* Reset all endpoints */
+ struct mgcp_endpoint *tmp_ep;
+
+ g = p->parent;
+ tmp_ep = g->endpoints;
+ while (tmp_ep) {
+ /*if ((strcmp(tmp_ep->name, "*") != 0) && (strcmp(tmp_ep->name, "aaln/" "*") != 0)) {*/
+ if (strcmp(tmp_ep->name, g->wcardep) != 0) {
+ struct mgcp_subchannel *tmp_sub, *first_sub;
+ ast_verb(3, "Resetting interface %s@%s\n", tmp_ep->name, p->parent->name);
+
+ first_sub = tmp_ep->sub;
+ tmp_sub = tmp_ep->sub;
+ while (tmp_sub) {
+ mgcp_queue_hangup(tmp_sub);
+ tmp_sub = tmp_sub->next;
+ if (tmp_sub == first_sub)
+ break;
+ }
+ }
+ tmp_ep = tmp_ep->next;
+ }
+ } else if (sub->owner) {
+ mgcp_queue_hangup(sub);
+ }
+ transmit_response(sub, "200", req, "OK");
+ /* We dont send NTFY or AUEP to wildcard ep */
+ if (strcmp(p->name, p->parent->wcardep) != 0) {
+ transmit_notify_request(sub, "");
+ /* Audit endpoint.
+ Idea is to prevent lost lines due to race conditions
+ */
+ transmit_audit_endpoint(p);
+ }
+ }
+ } else if (!strcasecmp(req->verb, "NTFY")) {
+ /* Acknowledge and be sure we keep looking for the same things */
+ transmit_response(sub, "200", req, "OK");
+ /* Notified of an event */
+ ev = get_header(req, "O");
+ s = strchr(ev, '/');
+ if (s) ev = s + 1;
+ ast_debug(1, "Endpoint '%s@%s-%d' observed '%s'\n", p->name, p->parent->name, sub->id, ev);
+ /* Keep looking for events unless this was a hangup */
+ if (strcasecmp(ev, "hu") && strcasecmp(ev, "hd") && strcasecmp(ev, "ping")) {
+ transmit_notify_request(sub, p->curtone);
+ }
+ if (!strcasecmp(ev, "hd")) {
+ p->hookstate = MGCP_OFFHOOK;
+ sub->cxmode = MGCP_CX_SENDRECV;
+ handle_hd_hf(sub, ev);
+ } else if (!strcasecmp(ev, "hf")) {
+ /* We can assume we are offhook if we received a hookflash */
+ /* First let's just do call wait and ignore threeway */
+ /* We're currently in charge */
+ if (p->hookstate != MGCP_OFFHOOK) {
+ /* Cisco c7940 sends hf even if the phone is onhook */
+ /* Thanks to point on IRC for pointing this out */
+ return -1;
+ }
+ /* do not let * conference two down channels */
+ if (sub->owner && sub->owner->_state == AST_STATE_DOWN && !sub->next->owner)
+ return -1;
+
+ if (p->callwaiting || p->transfer || p->threewaycalling) {
+ ast_verb(3, "Swapping %d for %d on %s@%s\n", p->sub->id, p->sub->next->id, p->name, p->parent->name);
+ p->sub = p->sub->next;
+
+ /* transfer control to our next subchannel */
+ if (!sub->next->owner) {
+ /* plave the first call on hold and start up a new call */
+ sub->cxmode = MGCP_CX_MUTE;
+ ast_verb(3, "MGCP Muting %d on %s@%s\n", sub->id, p->name, p->parent->name);
+ transmit_modify_request(sub);
+ if (sub->owner && ast_bridged_channel(sub->owner))
+ ast_queue_control(sub->owner, AST_CONTROL_HOLD);
+ sub->next->cxmode = MGCP_CX_RECVONLY;
+ handle_hd_hf(sub->next, ev);
+ } else if (sub->owner && sub->next->owner) {
+ /* We've got two active calls lets decide whether or not to conference or just flip flop */
+ if ((!sub->outgoing) && (!sub->next->outgoing)) {
+ /* We made both calls lets conferenct */
+ ast_verb(3, "MGCP Conferencing %d and %d on %s@%s\n",
+ sub->id, sub->next->id, p->name, p->parent->name);
+ sub->cxmode = MGCP_CX_CONF;
+ sub->next->cxmode = MGCP_CX_CONF;
+ if (ast_bridged_channel(sub->next->owner))
+ ast_queue_control(sub->next->owner, AST_CONTROL_UNHOLD);
+ transmit_modify_request(sub);
+ transmit_modify_request(sub->next);
+ } else {
+ /* Let's flipflop between calls */
+ /* XXX Need to check for state up ??? */
+ /* XXX Need a way to indicate the current call, or maybe the call that's waiting */
+ ast_verb(3, "We didn't make one of the calls FLIPFLOP %d and %d on %s@%s\n",
+ sub->id, sub->next->id, p->name, p->parent->name);
+ sub->cxmode = MGCP_CX_MUTE;
+ ast_verb(3, "MGCP Muting %d on %s@%s\n", sub->id, p->name, p->parent->name);
+ transmit_modify_request(sub);
+ if (ast_bridged_channel(sub->owner))
+ ast_queue_control(sub->owner, AST_CONTROL_HOLD);
+
+ if (ast_bridged_channel(sub->next->owner))
+ ast_queue_control(sub->next->owner, AST_CONTROL_HOLD);
+
+ handle_hd_hf(sub->next, ev);
+ }
+ } else {
+ /* We've most likely lost one of our calls find an active call and bring it up */
+ if (sub->owner) {
+ p->sub = sub;
+ } else if (sub->next->owner) {
+ p->sub = sub->next;
+ } else {
+ /* We seem to have lost both our calls */
+ /* XXX - What do we do now? */
+ return -1;
+ }
+ if (ast_bridged_channel(p->sub->owner))
+ ast_queue_control(p->sub->owner, AST_CONTROL_UNHOLD);
+ p->sub->cxmode = MGCP_CX_SENDRECV;
+ transmit_modify_request(p->sub);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Callwaiting, call transfer or threeway calling not enabled on endpoint %s@%s\n",
+ p->name, p->parent->name);
+ }
+ } else if (!strcasecmp(ev, "hu")) {
+ p->hookstate = MGCP_ONHOOK;
+ sub->cxmode = MGCP_CX_RECVONLY;
+ ast_debug(1, "MGCP %s@%s Went on hook\n", p->name, p->parent->name);
+ /* Do we need to send MDCX before a DLCX ?
+ if (sub->rtp) {
+ transmit_modify_request(sub);
+ }
+ */
+ if (p->transfer && (sub->owner && sub->next->owner) && ((!sub->outgoing) || (!sub->next->outgoing))) {
+ /* We're allowed to transfer, we have two avtive calls and */
+ /* we made at least one of the calls. Let's try and transfer */
+ ast_mutex_lock(&p->sub->next->lock);
+ res = attempt_transfer(p);
+ if (res < 0) {
+ if (p->sub->next->owner) {
+ sub->next->alreadygone = 1;
+ mgcp_queue_hangup(sub->next);
+ }
+ } else if (res) {
+ ast_log(LOG_WARNING, "Transfer attempt failed\n");
+ ast_mutex_unlock(&p->sub->next->lock);
+ return -1;
+ }
+ ast_mutex_unlock(&p->sub->next->lock);
+ } else {
+ /* Hangup the current call */
+ /* If there is another active call, mgcp_hangup will ring the phone with the other call */
+ if (sub->owner) {
+ sub->alreadygone = 1;
+ mgcp_queue_hangup(sub);
+ } else {
+ ast_verb(3, "MGCP handle_request(%s@%s-%d) ast_channel already destroyed, resending DLCX.\n",
+ p->name, p->parent->name, sub->id);
+ /* Instruct the other side to remove the connection since it apparently *
+ * still thinks the channel is active. *
+ * For Cisco IAD2421 /BAK/ */
+ transmit_connection_del(sub);
+ }
+ }
+ if ((p->hookstate == MGCP_ONHOOK) && (!sub->rtp) && (!sub->next->rtp)) {
+ p->hidecallerid = 0;
+ if (p->hascallwaiting && !p->callwaiting) {
+ ast_verb(3, "Enabling call waiting on MGCP/%s@%s-%d\n", p->name, p->parent->name, sub->id);
+ p->callwaiting = -1;
+ }
+ if (has_voicemail(p)) {
+ ast_verb(3, "MGCP handle_request(%s@%s) set vmwi(+)\n", p->name, p->parent->name);
+ transmit_notify_request(sub, "L/vmwi(+)");
+ } else {
+ ast_verb(3, "MGCP handle_request(%s@%s) set vmwi(-)\n", p->name, p->parent->name);
+ transmit_notify_request(sub, "L/vmwi(-)");
+ }
+ }
+ } else if ((strlen(ev) == 1) &&
+ (((ev[0] >= '0') && (ev[0] <= '9')) ||
+ ((ev[0] >= 'A') && (ev[0] <= 'D')) ||
+ (ev[0] == '*') || (ev[0] == '#'))) {
+ if (sub && sub->owner && (sub->owner->_state >= AST_STATE_UP)) {
+ f.frametype = AST_FRAME_DTMF;
+ f.subclass = ev[0];
+ f.src = "mgcp";
+ /* XXX MUST queue this frame to all subs in threeway call if threeway call is active */
+ mgcp_queue_frame(sub, &f);
+ ast_mutex_lock(&sub->next->lock);
+ if (sub->next->owner)
+ mgcp_queue_frame(sub->next, &f);
+ ast_mutex_unlock(&sub->next->lock);
+ if (strstr(p->curtone, "wt") && (ev[0] == 'A')) {
+ memset(p->curtone, 0, sizeof(p->curtone));
+ }
+ } else {
+ p->dtmf_buf[strlen(p->dtmf_buf)] = ev[0];
+ p->dtmf_buf[strlen(p->dtmf_buf)] = '\0';
+ }
+ } else if (!strcasecmp(ev, "T")) {
+ /* Digit timeout -- unimportant */
+ } else if (!strcasecmp(ev, "ping")) {
+ /* ping -- unimportant */
+ } else {
+ ast_log(LOG_NOTICE, "Received unknown event '%s' from %s@%s\n", ev, p->name, p->parent->name);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unknown verb '%s' received from %s\n", req->verb, ast_inet_ntoa(sin->sin_addr));
+ transmit_response(sub, "510", req, "Unknown verb");
+ }
+ return 0;
+}
+
+static int find_and_retrans(struct mgcp_subchannel *sub, struct mgcp_request *req)
+{
+ int seqno=0;
+ time_t now;
+ struct mgcp_response *prev = NULL, *cur, *next, *answer=NULL;
+ time(&now);
+ if (sscanf(req->identifier, "%d", &seqno) != 1)
+ seqno = 0;
+ cur = sub->parent->parent->responses;
+ while(cur) {
+ next = cur->next;
+ if (now - cur->whensent > RESPONSE_TIMEOUT) {
+ /* Delete this entry */
+ if (prev)
+ prev->next = next;
+ else
+ sub->parent->parent->responses = next;
+ ast_free(cur);
+ } else {
+ if (seqno == cur->seqno)
+ answer = cur;
+ prev = cur;
+ }
+ cur = next;
+ }
+ if (answer) {
+ resend_response(sub, answer);
+ return 1;
+ }
+ return 0;
+}
+
+static int mgcpsock_read(int *id, int fd, short events, void *ignore)
+{
+ struct mgcp_request req;
+ struct sockaddr_in sin;
+ struct mgcp_subchannel *sub;
+ int res;
+ socklen_t len;
+ int result;
+ int ident;
+ len = sizeof(sin);
+ memset(&req, 0, sizeof(req));
+ res = recvfrom(mgcpsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
+ if (res < 0) {
+ if (errno != ECONNREFUSED)
+ ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
+ return 1;
+ }
+ req.data[res] = '\0';
+ req.len = res;
+ if (mgcpdebug) {
+ ast_verbose("MGCP read: \n%s\nfrom %s:%d\n", req.data, ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ }
+ parse(&req);
+ if (req.headers < 1) {
+ /* Must have at least one header */
+ return 1;
+ }
+ if (ast_strlen_zero(req.identifier)) {
+ ast_log(LOG_NOTICE, "Message from %s missing identifier\n", ast_inet_ntoa(sin.sin_addr));
+ return 1;
+ }
+
+ if (sscanf(req.verb, "%d", &result) && sscanf(req.identifier, "%d", &ident)) {
+ /* Try to find who this message is for, if it's important */
+ sub = find_subchannel_and_lock(NULL, ident, &sin);
+ if (sub) {
+ struct mgcp_gateway *gw = sub->parent->parent;
+ struct mgcp_message *cur, *prev;
+
+ ast_mutex_unlock(&sub->lock);
+ ast_mutex_lock(&gw->msgs_lock);
+ for (prev = NULL, cur = gw->msgs; cur; prev = cur, cur = cur->next) {
+ if (cur->seqno == ident) {
+ ast_debug(1, "Got response back on transaction %d\n", ident);
+ if (prev)
+ prev->next = cur->next;
+ else
+ gw->msgs = cur->next;
+ break;
+ }
+ }
+
+ /* stop retrans timer if the queue is empty */
+ if (!gw->msgs && (gw->retransid != -1)) {
+ ast_sched_del(sched, gw->retransid);
+ gw->retransid = -1;
+ }
+
+ ast_mutex_unlock(&gw->msgs_lock);
+ if (cur) {
+ handle_response(cur->owner_ep, cur->owner_sub, result, ident, &req);
+ ast_free(cur);
+ return 1;
+ }
+
+ ast_log(LOG_NOTICE, "Got response back on [%s] for transaction %d we aren't sending?\n",
+ gw->name, ident);
+ }
+ } else {
+ if (ast_strlen_zero(req.endpoint) ||
+ ast_strlen_zero(req.version) ||
+ ast_strlen_zero(req.verb)) {
+ ast_log(LOG_NOTICE, "Message must have a verb, an idenitifier, version, and endpoint\n");
+ return 1;
+ }
+ /* Process request, with iflock held */
+ sub = find_subchannel_and_lock(req.endpoint, 0, &sin);
+ if (sub) {
+ /* look first to find a matching response in the queue */
+ if (!find_and_retrans(sub, &req))
+ /* pass the request off to the currently mastering subchannel */
+ handle_request(sub, &req, &sin);
+ ast_mutex_unlock(&sub->lock);
+ }
+ }
+ return 1;
+}
+
+static int *mgcpsock_read_id = NULL;
+
+static void *do_monitor(void *data)
+{
+ int res;
+ int reloading;
+ /*struct mgcp_gateway *g;*/
+ /*struct mgcp_endpoint *e;*/
+ /*time_t thispass = 0, lastpass = 0;*/
+
+ /* Add an I/O event to our UDP socket */
+ if (mgcpsock > -1)
+ mgcpsock_read_id = ast_io_add(io, mgcpsock, mgcpsock_read, AST_IO_IN, NULL);
+
+ /* This thread monitors all the frame relay interfaces which are not yet in use
+ (and thus do not have a separate thread) indefinitely */
+ /* From here on out, we die whenever asked */
+ for(;;) {
+ /* Check for a reload request */
+ ast_mutex_lock(&mgcp_reload_lock);
+ reloading = mgcp_reloading;
+ mgcp_reloading = 0;
+ ast_mutex_unlock(&mgcp_reload_lock);
+ if (reloading) {
+ ast_verb(1, "Reloading MGCP\n");
+ reload_config(1);
+ /* Add an I/O event to our UDP socket */
+ if (mgcpsock > -1)
+ mgcpsock_read_id = ast_io_add(io, mgcpsock, mgcpsock_read, AST_IO_IN, NULL);
+ }
+
+ /* Check for interfaces needing to be killed */
+ /* Don't let anybody kill us right away. Nobody should lock the interface list
+ and wait for the monitor list, but the other way around is okay. */
+ ast_mutex_lock(&monlock);
+ /* Lock the network interface */
+ ast_mutex_lock(&netlock);
+
+#if 0
+ /* XXX THIS IS COMPLETELY HOSED */
+ /* The gateway goes into a state of panic */
+ /* If the vmwi indicator is sent while it is reseting interfaces */
+ lastpass = thispass;
+ thispass = time(NULL);
+ g = gateways;
+ while(g) {
+ if (thispass != lastpass) {
+ e = g->endpoints;
+ while(e) {
+ if (e->type == TYPE_LINE) {
+ res = has_voicemail(e);
+ if ((e->msgstate != res) && (e->hookstate == MGCP_ONHOOK) && (!e->rtp)){
+ if (res) {
+ transmit_notify_request(e, "L/vmwi(+)");
+ } else {
+ transmit_notify_request(e, "L/vmwi(-)");
+ }
+ e->msgstate = res;
+ e->onhooktime = thispass;
+ }
+ }
+ e = e->next;
+ }
+ }
+ g = g->next;
+ }
+#endif
+ /* Okay, now that we know what to do, release the network lock */
+ ast_mutex_unlock(&netlock);
+ /* And from now on, we're okay to be killed, so release the monitor lock as well */
+ ast_mutex_unlock(&monlock);
+ pthread_testcancel();
+ /* Wait for sched or io */
+ res = ast_sched_wait(sched);
+ /* copied from chan_sip.c */
+ if ((res < 0) || (res > 1000))
+ res = 1000;
+ res = ast_io_wait(io, res);
+ ast_mutex_lock(&monlock);
+ if (res >= 0)
+ ast_sched_runq(sched);
+ ast_mutex_unlock(&monlock);
+ }
+ /* Never reached */
+ return NULL;
+}
+
+static int restart_monitor(void)
+{
+ /* If we're supposed to be stopped -- stay stopped */
+ if (monitor_thread == AST_PTHREADT_STOP)
+ return 0;
+ if (ast_mutex_lock(&monlock)) {
+ ast_log(LOG_WARNING, "Unable to lock monitor\n");
+ return -1;
+ }
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ /* Wake up the thread */
+ pthread_kill(monitor_thread, SIGURG);
+ } else {
+ /* Start a new monitor */
+ if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+ struct mgcp_subchannel *sub;
+ struct ast_channel *tmpc = NULL;
+ char tmp[256];
+ char *dest = data;
+
+ oldformat = format;
+ format &= capability;
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
+ return NULL;
+ }
+ ast_copy_string(tmp, dest, sizeof(tmp));
+ if (ast_strlen_zero(tmp)) {
+ ast_log(LOG_NOTICE, "MGCP Channels require an endpoint\n");
+ return NULL;
+ }
+ sub = find_subchannel_and_lock(tmp, 0, NULL);
+ if (!sub) {
+ ast_log(LOG_WARNING, "Unable to find MGCP endpoint '%s'\n", tmp);
+ *cause = AST_CAUSE_UNREGISTERED;
+ return NULL;
+ }
+
+ ast_verb(3, "MGCP mgcp_request(%s)\n", tmp);
+ ast_verb(3, "MGCP cw: %d, dnd: %d, so: %d, sno: %d\n",
+ sub->parent->callwaiting, sub->parent->dnd, sub->owner ? 1 : 0, sub->next->owner ? 1: 0);
+ /* Must be busy */
+ if (((sub->parent->callwaiting) && ((sub->owner) && (sub->next->owner))) ||
+ ((!sub->parent->callwaiting) && (sub->owner)) ||
+ (sub->parent->dnd && (ast_strlen_zero(sub->parent->call_forward)))) {
+ if (sub->parent->hookstate == MGCP_ONHOOK) {
+ if (has_voicemail(sub->parent)) {
+ transmit_notify_request(sub,"L/vmwi(+)");
+ } else {
+ transmit_notify_request(sub,"L/vmwi(-)");
+ }
+ }
+ *cause = AST_CAUSE_BUSY;
+ ast_mutex_unlock(&sub->lock);
+ return NULL;
+ }
+ tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN);
+ ast_mutex_unlock(&sub->lock);
+ if (!tmpc)
+ ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
+ restart_monitor();
+ return tmpc;
+}
+
+/* modified for reload support */
+/*! \brief build_gateway: parse mgcp.conf and create gateway/endpoint structures */
+static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
+{
+ struct mgcp_gateway *gw;
+ struct mgcp_endpoint *e;
+ struct mgcp_subchannel *sub;
+ /*char txident[80];*/
+ int i=0, y=0;
+ int gw_reload = 0;
+ int ep_reload = 0;
+ canreinvite = CANREINVITE;
+
+ /* locate existing gateway */
+ gw = gateways;
+ while (gw) {
+ if (!strcasecmp(cat, gw->name)) {
+ /* gateway already exists */
+ gw->delme = 0;
+ gw_reload = 1;
+ break;
+ }
+ gw = gw->next;
+ }
+
+ if (!gw)
+ gw = ast_calloc(1, sizeof(*gw));
+
+ if (gw) {
+ if (!gw_reload) {
+ gw->expire = -1;
+ gw->retransid = -1; /* SC */
+ ast_mutex_init(&gw->msgs_lock);
+ ast_copy_string(gw->name, cat, sizeof(gw->name));
+ /* check if the name is numeric ip */
+ if ((strchr(gw->name, '.')) && inet_addr(gw->name) != INADDR_NONE)
+ gw->isnamedottedip = 1;
+ }
+ while(v) {
+ if (!strcasecmp(v->name, "host")) {
+ if (!strcasecmp(v->value, "dynamic")) {
+ /* They'll register with us */
+ gw->dynamic = 1;
+ memset(&gw->addr.sin_addr, 0, 4);
+ if (gw->addr.sin_port) {
+ /* If we've already got a port, make it the default rather than absolute */
+ gw->defaddr.sin_port = gw->addr.sin_port;
+ gw->addr.sin_port = 0;
+ }
+ } else {
+ /* Non-dynamic. Make sure we become that way if we're not */
+ if (gw->expire > -1)
+ ast_sched_del(sched, gw->expire);
+ gw->expire = -1;
+ gw->dynamic = 0;
+ if (ast_get_ip(&gw->addr, v->value)) {
+ if (!gw_reload) {
+ ast_mutex_destroy(&gw->msgs_lock);
+ ast_free(gw);
+ }
+ return NULL;
+ }
+ }
+ } else if (!strcasecmp(v->name, "defaultip")) {
+ if (ast_get_ip(&gw->defaddr, v->value)) {
+ if (!gw_reload) {
+ ast_mutex_destroy(&gw->msgs_lock);
+ ast_free(gw);
+ }
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "permit") ||
+ !strcasecmp(v->name, "deny")) {
+ gw->ha = ast_append_ha(v->name, v->value, gw->ha, NULL);
+ } else if (!strcasecmp(v->name, "port")) {
+ gw->addr.sin_port = htons(atoi(v->value));
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(context, v->value, sizeof(context));
+ } else if (!strcasecmp(v->name, "dtmfmode")) {
+ if (!strcasecmp(v->value, "inband"))
+ dtmfmode = MGCP_DTMF_INBAND;
+ else if (!strcasecmp(v->value, "rfc2833"))
+ dtmfmode = MGCP_DTMF_RFC2833;
+ else if (!strcasecmp(v->value, "hybrid"))
+ dtmfmode = MGCP_DTMF_HYBRID;
+ else if (!strcasecmp(v->value, "none"))
+ dtmfmode = 0;
+ else
+ ast_log(LOG_WARNING, "'%s' is not a valid DTMF mode at line %d\n", v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "nat")) {
+ nat = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callerid")) {
+ if (!strcasecmp(v->value, "asreceived")) {
+ cid_num[0] = '\0';
+ cid_name[0] = '\0';
+ } else {
+ ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
+ }
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(language, v->value, sizeof(language));
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(accountcode, v->value, sizeof(accountcode));
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ y = ast_cdr_amaflags2int(v->value);
+ if (y < 0) {
+ ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
+ } else {
+ amaflags = y;
+ }
+ } else if (!strcasecmp(v->name, "musiconhold")) {
+ ast_copy_string(musicclass, v->value, sizeof(musicclass));
+ } else if (!strcasecmp(v->name, "callgroup")) {
+ cur_callergroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "pickupgroup")) {
+ cur_pickupgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "immediate")) {
+ immediate = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "cancallforward")) {
+ cancallforward = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "singlepath")) {
+ singlepath = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "canreinvite")) {
+ canreinvite = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "mailbox")) {
+ ast_copy_string(mailbox, v->value, sizeof(mailbox));
+ } else if (!strcasecmp(v->name, "adsi")) {
+ adsi = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callreturn")) {
+ callreturn = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callwaiting")) {
+ callwaiting = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "slowsequence")) {
+ slowsequence = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "transfer")) {
+ transfer = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "threewaycalling")) {
+ threewaycalling = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "wcardep")) {
+ /* locate existing endpoint */
+ e = gw->endpoints;
+ while (e) {
+ if (!strcasecmp(v->value, e->name)) {
+ /* endpoint already exists */
+ e->delme = 0;
+ ep_reload = 1;
+ break;
+ }
+ e = e->next;
+ }
+
+ if (!e) {
+ /* Allocate wildcard endpoint */
+ e = ast_calloc(1, sizeof(*e));
+ ep_reload = 0;
+ }
+
+ if (e) {
+ if (!ep_reload) {
+ memset(e, 0, sizeof(struct mgcp_endpoint));
+ ast_mutex_init(&e->lock);
+ ast_mutex_init(&e->rqnt_queue_lock);
+ ast_mutex_init(&e->cmd_queue_lock);
+ ast_copy_string(e->name, v->value, sizeof(e->name));
+ e->needaudit = 1;
+ }
+ ast_copy_string(gw->wcardep, v->value, sizeof(gw->wcardep));
+ /* XXX Should we really check for uniqueness?? XXX */
+ ast_copy_string(e->accountcode, accountcode, sizeof(e->accountcode));
+ ast_copy_string(e->context, context, sizeof(e->context));
+ ast_copy_string(e->cid_num, cid_num, sizeof(e->cid_num));
+ ast_copy_string(e->cid_name, cid_name, sizeof(e->cid_name));
+ ast_copy_string(e->language, language, sizeof(e->language));
+ ast_copy_string(e->musicclass, musicclass, sizeof(e->musicclass));
+ ast_copy_string(e->mailbox, mailbox, sizeof(e->mailbox));
+ if (!ast_strlen_zero(e->mailbox)) {
+ char *mailbox, *context;
+ context = mailbox = ast_strdupa(e->mailbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+ e->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_END);
+ }
+ snprintf(e->rqnt_ident, sizeof(e->rqnt_ident), "%08lx", ast_random());
+ e->msgstate = -1;
+ e->amaflags = amaflags;
+ e->capability = capability;
+ e->parent = gw;
+ e->dtmfmode = dtmfmode;
+ if (!ep_reload && e->sub && e->sub->rtp)
+ e->dtmfmode |= MGCP_DTMF_INBAND;
+ e->adsi = adsi;
+ e->type = TYPE_LINE;
+ e->immediate = immediate;
+ e->callgroup=cur_callergroup;
+ e->pickupgroup=cur_pickupgroup;
+ e->callreturn = callreturn;
+ e->cancallforward = cancallforward;
+ e->singlepath = singlepath;
+ e->canreinvite = canreinvite;
+ e->callwaiting = callwaiting;
+ e->hascallwaiting = callwaiting;
+ e->slowsequence = slowsequence;
+ e->transfer = transfer;
+ e->threewaycalling = threewaycalling;
+ e->onhooktime = time(NULL);
+ /* ASSUME we're onhook */
+ e->hookstate = MGCP_ONHOOK;
+ if (!ep_reload) {
+ /*snprintf(txident, sizeof(txident), "%08lx", ast_random());*/
+ for (i = 0; i < MAX_SUBS; i++) {
+ sub = ast_calloc(1, sizeof(*sub));
+ if (sub) {
+ ast_verb(3, "Allocating subchannel '%d' on %s@%s\n", i, e->name, gw->name);
+ ast_mutex_init(&sub->lock);
+ ast_mutex_init(&sub->cx_queue_lock);
+ sub->parent = e;
+ sub->id = i;
+ snprintf(sub->txident, sizeof(sub->txident), "%08lx", ast_random());
+ /*stnrcpy(sub->txident, txident, sizeof(sub->txident) - 1);*/
+ sub->cxmode = MGCP_CX_INACTIVE;
+ sub->nat = nat;
+ sub->next = e->sub;
+ e->sub = sub;
+ } else {
+ /* XXX Should find a way to clean up our memory */
+ ast_log(LOG_WARNING, "Out of memory allocating subchannel\n");
+ return NULL;
+ }
+ }
+ /* Make out subs a circular linked list so we can always sping through the whole bunch */
+ sub = e->sub;
+ /* find the end of the list */
+ while(sub->next){
+ sub = sub->next;
+ }
+ /* set the last sub->next to the first sub */
+ sub->next = e->sub;
+
+ e->next = gw->endpoints;
+ gw->endpoints = e;
+ }
+ }
+ } else if (!strcasecmp(v->name, "trunk") ||
+ !strcasecmp(v->name, "line")) {
+
+ /* locate existing endpoint */
+ e = gw->endpoints;
+ while (e) {
+ if (!strcasecmp(v->value, e->name)) {
+ /* endpoint already exists */
+ e->delme = 0;
+ ep_reload = 1;
+ break;
+ }
+ e = e->next;
+ }
+
+ if (!e) {
+ e = ast_calloc(1, sizeof(*e));
+ ep_reload = 0;
+ }
+
+ if (e) {
+ if (!ep_reload) {
+ ast_mutex_init(&e->lock);
+ ast_mutex_init(&e->rqnt_queue_lock);
+ ast_mutex_init(&e->cmd_queue_lock);
+ ast_copy_string(e->name, v->value, sizeof(e->name));
+ e->needaudit = 1;
+ }
+ /* XXX Should we really check for uniqueness?? XXX */
+ ast_copy_string(e->accountcode, accountcode, sizeof(e->accountcode));
+ ast_copy_string(e->context, context, sizeof(e->context));
+ ast_copy_string(e->cid_num, cid_num, sizeof(e->cid_num));
+ ast_copy_string(e->cid_name, cid_name, sizeof(e->cid_name));
+ ast_copy_string(e->language, language, sizeof(e->language));
+ ast_copy_string(e->musicclass, musicclass, sizeof(e->musicclass));
+ ast_copy_string(e->mailbox, mailbox, sizeof(e->mailbox));
+ if (!ast_strlen_zero(mailbox)) {
+ ast_verb(3, "Setting mailbox '%s' on %s@%s\n", mailbox, gw->name, e->name);
+ }
+ if (!ep_reload) {
+ /* XXX potential issue due to reload */
+ e->msgstate = -1;
+ e->parent = gw;
+ }
+ e->amaflags = amaflags;
+ e->capability = capability;
+ e->dtmfmode = dtmfmode;
+ e->adsi = adsi;
+ if (!strcasecmp(v->name, "trunk"))
+ e->type = TYPE_TRUNK;
+ else
+ e->type = TYPE_LINE;
+
+ e->immediate = immediate;
+ e->callgroup=cur_callergroup;
+ e->pickupgroup=cur_pickupgroup;
+ e->callreturn = callreturn;
+ e->cancallforward = cancallforward;
+ e->canreinvite = canreinvite;
+ e->singlepath = singlepath;
+ e->callwaiting = callwaiting;
+ e->hascallwaiting = callwaiting;
+ e->slowsequence = slowsequence;
+ e->transfer = transfer;
+ e->threewaycalling = threewaycalling;
+ if (!ep_reload) {
+ e->onhooktime = time(NULL);
+ /* ASSUME we're onhook */
+ e->hookstate = MGCP_ONHOOK;
+ snprintf(e->rqnt_ident, sizeof(e->rqnt_ident), "%08lx", ast_random());
+ }
+
+ for (i = 0, sub = NULL; i < MAX_SUBS; i++) {
+ if (!ep_reload) {
+ sub = ast_calloc(1, sizeof(*sub));
+ } else {
+ if (!sub)
+ sub = e->sub;
+ else
+ sub = sub->next;
+ }
+
+ if (sub) {
+ if (!ep_reload) {
+ ast_verb(3, "Allocating subchannel '%d' on %s@%s\n", i, e->name, gw->name);
+ ast_mutex_init(&sub->lock);
+ ast_mutex_init(&sub->cx_queue_lock);
+ ast_copy_string(sub->magic, MGCP_SUBCHANNEL_MAGIC, sizeof(sub->magic));
+ sub->parent = e;
+ sub->id = i;
+ snprintf(sub->txident, sizeof(sub->txident), "%08lx", ast_random());
+ sub->cxmode = MGCP_CX_INACTIVE;
+ sub->next = e->sub;
+ e->sub = sub;
+ }
+ sub->nat = nat;
+ } else {
+ /* XXX Should find a way to clean up our memory */
+ ast_log(LOG_WARNING, "Out of memory allocating subchannel\n");
+ return NULL;
+ }
+ }
+ if (!ep_reload) {
+ /* Make out subs a circular linked list so we can always sping through the whole bunch */
+ sub = e->sub;
+ /* find the end of the list */
+ while (sub->next) {
+ sub = sub->next;
+ }
+ /* set the last sub->next to the first sub */
+ sub->next = e->sub;
+
+ e->next = gw->endpoints;
+ gw->endpoints = e;
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
+ v = v->next;
+ }
+ }
+ if (!ntohl(gw->addr.sin_addr.s_addr) && !gw->dynamic) {
+ ast_log(LOG_WARNING, "Gateway '%s' lacks IP address and isn't dynamic\n", gw->name);
+ if (!gw_reload) {
+ ast_mutex_destroy(&gw->msgs_lock);
+ ast_free(gw);
+ }
+ return NULL;
+ }
+ gw->defaddr.sin_family = AF_INET;
+ gw->addr.sin_family = AF_INET;
+ if (gw->defaddr.sin_addr.s_addr && !ntohs(gw->defaddr.sin_port))
+ gw->defaddr.sin_port = htons(DEFAULT_MGCP_GW_PORT);
+ if (gw->addr.sin_addr.s_addr && !ntohs(gw->addr.sin_port))
+ gw->addr.sin_port = htons(DEFAULT_MGCP_GW_PORT);
+ if (gw->addr.sin_addr.s_addr)
+ if (ast_ouraddrfor(&gw->addr.sin_addr, &gw->ourip))
+ memcpy(&gw->ourip, &__ourip, sizeof(gw->ourip));
+
+ return (gw_reload ? NULL : gw);
+}
+
+static enum ast_rtp_get_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct mgcp_subchannel *sub = NULL;
+
+ if (!(sub = chan->tech_pvt) || !(sub->rtp))
+ return AST_RTP_GET_FAILED;
+
+ *rtp = sub->rtp;
+
+ if (sub->parent->canreinvite)
+ return AST_RTP_TRY_NATIVE;
+ else
+ return AST_RTP_TRY_PARTIAL;
+}
+
+static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+{
+ /* XXX Is there such thing as video support with MGCP? XXX */
+ struct mgcp_subchannel *sub;
+ sub = chan->tech_pvt;
+ if (sub && !sub->alreadygone) {
+ transmit_modify_with_sdp(sub, rtp, codecs);
+ return 0;
+ }
+ return -1;
+}
+
+static struct ast_rtp_protocol mgcp_rtp = {
+ .type = "MGCP",
+ .get_rtp_info = mgcp_get_rtp_peer,
+ .set_rtp_peer = mgcp_set_rtp_peer,
+};
+
+static void destroy_endpoint(struct mgcp_endpoint *e)
+{
+ struct mgcp_subchannel *sub = e->sub->next, *s;
+ int i;
+
+ for (i = 0; i < MAX_SUBS; i++) {
+ ast_mutex_lock(&sub->lock);
+ if (!ast_strlen_zero(sub->cxident)) {
+ transmit_connection_del(sub);
+ }
+ if (sub->rtp) {
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+ memset(sub->magic, 0, sizeof(sub->magic));
+ mgcp_queue_hangup(sub);
+ dump_cmd_queues(NULL, sub);
+ ast_mutex_unlock(&sub->lock);
+ sub = sub->next;
+ }
+
+ if (e->dsp) {
+ ast_dsp_free(e->dsp);
+ }
+
+ dump_queue(e->parent, e);
+ dump_cmd_queues(e, NULL);
+
+ sub = e->sub;
+ for (i = 0; (i < MAX_SUBS) && sub; i++) {
+ s = sub;
+ sub = sub->next;
+ ast_mutex_destroy(&s->lock);
+ ast_mutex_destroy(&s->cx_queue_lock);
+ ast_free(s);
+ }
+
+ if (e->mwi_event_sub)
+ ast_event_unsubscribe(e->mwi_event_sub);
+
+ ast_mutex_destroy(&e->lock);
+ ast_mutex_destroy(&e->rqnt_queue_lock);
+ ast_mutex_destroy(&e->cmd_queue_lock);
+ ast_free(e);
+}
+
+static void destroy_gateway(struct mgcp_gateway *g)
+{
+ if (g->ha)
+ ast_free_ha(g->ha);
+
+ dump_queue(g, NULL);
+
+ ast_free(g);
+}
+
+static void prune_gateways(void)
+{
+ struct mgcp_gateway *g, *z, *r;
+ struct mgcp_endpoint *e, *p, *t;
+
+ ast_mutex_lock(&gatelock);
+
+ /* prune gateways */
+ for (z = NULL, g = gateways; g;) {
+ /* prune endpoints */
+ for (p = NULL, e = g->endpoints; e; ) {
+ if (e->delme || g->delme) {
+ t = e;
+ e = e->next;
+ if (!p)
+ g->endpoints = e;
+ else
+ p->next = e;
+ destroy_endpoint(t);
+ } else {
+ p = e;
+ e = e->next;
+ }
+ }
+
+ if (g->delme) {
+ r = g;
+ g = g->next;
+ if (!z)
+ gateways = g;
+ else
+ z->next = g;
+
+ destroy_gateway(r);
+ } else {
+ z = g;
+ g = g->next;
+ }
+ }
+
+ ast_mutex_unlock(&gatelock);
+}
+
+static int reload_config(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct mgcp_gateway *g;
+ struct mgcp_endpoint *e;
+ char *cat;
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ int format;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if (gethostname(ourhost, sizeof(ourhost)-1)) {
+ ast_log(LOG_WARNING, "Unable to get hostname, MGCP disabled\n");
+ return 0;
+ }
+ cfg = ast_config_load(config, config_flags);
+
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config %s, MGCP disabled\n", config);
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ dtmfmode = 0;
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ v = ast_variable_browse(cfg, "general");
+ while (v) {
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
+ v = v->next;
+ continue;
+ }
+
+ /* Create the interface list */
+ if (!strcasecmp(v->name, "bindaddr")) {
+ if (!(hp = ast_gethostbyname(v->value, &ahp))) {
+ ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
+ } else {
+ memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
+ }
+ } else if (!strcasecmp(v->name, "allow")) {
+ format = ast_getformatbyname(v->value);
+ if (format < 1)
+ ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
+ else
+ capability |= format;
+ } else if (!strcasecmp(v->name, "disallow")) {
+ format = ast_getformatbyname(v->value);
+ if (format < 1)
+ ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
+ else
+ capability &= ~format;
+ } else if (!strcasecmp(v->name, "tos")) {
+ if (ast_str2tos(v->value, &tos))
+ ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "tos_audio")) {
+ if (ast_str2tos(v->value, &tos_audio))
+ ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos")) {
+ if (ast_str2cos(v->value, &cos))
+ ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_audio")) {
+ if (ast_str2cos(v->value, &cos_audio))
+ ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "port")) {
+ if (sscanf(v->value, "%d", &ourport) == 1) {
+ bindaddr.sin_port = htons(ourport);
+ } else {
+ ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
+ }
+ }
+ v = v->next;
+ }
+
+ /* mark existing entries for deletion */
+ ast_mutex_lock(&gatelock);
+ g = gateways;
+ while (g) {
+ g->delme = 1;
+ e = g->endpoints;
+ while (e) {
+ e->delme = 1;
+ e = e->next;
+ }
+ g = g->next;
+ }
+ ast_mutex_unlock(&gatelock);
+
+ cat = ast_category_browse(cfg, NULL);
+ while(cat) {
+ if (strcasecmp(cat, "general")) {
+ ast_mutex_lock(&gatelock);
+ g = build_gateway(cat, ast_variable_browse(cfg, cat));
+ if (g) {
+ ast_verb(3, "Added gateway '%s'\n", g->name);
+ g->next = gateways;
+ gateways = g;
+ }
+ ast_mutex_unlock(&gatelock);
+
+ /* FS: process queue and IO */
+ if (monitor_thread == pthread_self()) {
+ if (sched) ast_sched_runq(sched);
+ if (io) ast_io_wait(io, 10);
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+
+ /* prune deleted entries etc. */
+ prune_gateways();
+
+ if (ntohl(bindaddr.sin_addr.s_addr)) {
+ memcpy(&__ourip, &bindaddr.sin_addr, sizeof(__ourip));
+ } else {
+ hp = ast_gethostbyname(ourhost, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Unable to get our IP address, MGCP disabled\n");
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
+ }
+ if (!ntohs(bindaddr.sin_port))
+ bindaddr.sin_port = ntohs(DEFAULT_MGCP_CA_PORT);
+ bindaddr.sin_family = AF_INET;
+ ast_mutex_lock(&netlock);
+ if (mgcpsock > -1)
+ close(mgcpsock);
+
+ if (mgcpsock_read_id != NULL)
+ ast_io_remove(io, mgcpsock_read_id);
+ mgcpsock_read_id = NULL;
+
+ mgcpsock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (mgcpsock < 0) {
+ ast_log(LOG_WARNING, "Unable to create MGCP socket: %s\n", strerror(errno));
+ } else {
+ if (bind(mgcpsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+ ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
+ ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
+ strerror(errno));
+ close(mgcpsock);
+ mgcpsock = -1;
+ } else {
+ ast_verb(2, "MGCP Listening on %s:%d\n",
+ ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
+ ast_netsock_set_qos(mgcpsock, tos, cos, "MGCP");
+ }
+ }
+ ast_mutex_unlock(&netlock);
+ ast_config_destroy(cfg);
+
+ /* send audit only to the new endpoints */
+ g = gateways;
+ while (g) {
+ e = g->endpoints;
+ while (e && e->needaudit) {
+ e->needaudit = 0;
+ transmit_audit_endpoint(e);
+ ast_verb(3, "MGCP Auditing endpoint %s@%s for hookstate\n", e->name, g->name);
+ e = e->next;
+ }
+ g = g->next;
+ }
+
+ return 0;
+}
+
+/*! \brief load_module: PBX load module - initialization ---*/
+static int load_module(void)
+{
+ if (!(sched = sched_context_create())) {
+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (!(io = io_context_create())) {
+ ast_log(LOG_WARNING, "Unable to create I/O context\n");
+ sched_context_destroy(sched);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (reload_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ /* Make sure we can register our mgcp channel type */
+ if (ast_channel_register(&mgcp_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'MGCP'\n");
+ io_context_destroy(io);
+ sched_context_destroy(sched);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ ast_rtp_proto_register(&mgcp_rtp);
+ ast_cli_register_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry));
+
+ /* And start the monitor for the first time */
+ restart_monitor();
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static char *mgcp_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ static int deprecated = 0;
+
+ if (e) {
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mgcp reload";
+ e->usage =
+ "Usage: mgcp reload\n"
+ " 'mgcp reload' is deprecated. Please use 'reload chan_mgcp.so' instead.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ }
+
+ if (!deprecated && a && a->argc > 0) {
+ ast_log(LOG_WARNING, "'mgcp reload' is deprecated. Please use 'reload chan_mgcp.so' instead.\n");
+ deprecated = 1;
+ }
+
+ ast_mutex_lock(&mgcp_reload_lock);
+ if (mgcp_reloading) {
+ ast_verbose("Previous mgcp reload not yet done\n");
+ } else
+ mgcp_reloading = 1;
+ ast_mutex_unlock(&mgcp_reload_lock);
+ restart_monitor();
+ return CLI_SUCCESS;
+}
+
+static int reload(void)
+{
+ mgcp_reload(NULL, 0, NULL);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ struct mgcp_endpoint *e;
+ struct mgcp_gateway *g;
+
+ /* Check to see if we're reloading */
+ if (ast_mutex_trylock(&mgcp_reload_lock)) {
+ ast_log(LOG_WARNING, "MGCP is currently reloading. Unable to remove module.\n");
+ return -1;
+ } else {
+ mgcp_reloading = 1;
+ ast_mutex_unlock(&mgcp_reload_lock);
+ }
+
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&mgcp_tech);
+
+ /* Shut down the monitoring thread */
+ if (!ast_mutex_lock(&monlock)) {
+ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) {
+ pthread_cancel(monitor_thread);
+ pthread_kill(monitor_thread, SIGURG);
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ /* We always want to leave this in a consistent state */
+ ast_channel_register(&mgcp_tech);
+ mgcp_reloading = 0;
+ mgcp_reload(NULL, 0, NULL);
+ return -1;
+ }
+
+ if (!ast_mutex_lock(&gatelock)) {
+ for (g = gateways; g; g = g->next) {
+ g->delme = 1;
+ for (e = g->endpoints; e; e = e->next)
+ e->delme = 1;
+ }
+
+ prune_gateways();
+ ast_mutex_unlock(&gatelock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the gateways list.\n");
+ /* We always want to leave this in a consistent state */
+ ast_channel_register(&mgcp_tech);
+ /* Allow the monitor to restart */
+ monitor_thread = AST_PTHREADT_NULL;
+ mgcp_reloading = 0;
+ mgcp_reload(NULL, 0, NULL);
+ return -1;
+ }
+
+ close(mgcpsock);
+ ast_rtp_proto_unregister(&mgcp_rtp);
+ ast_cli_unregister_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry));
+ sched_context_destroy(sched);
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Media Gateway Control Protocol (MGCP)",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/channels/chan_misdn.c b/trunk/channels/chan_misdn.c
new file mode 100644
index 000000000..a0153ef4d
--- /dev/null
+++ b/trunk/channels/chan_misdn.c
@@ -0,0 +1,5747 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2006, Christian Richter
+ *
+ * Christian Richter <crich@beronet.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 the chan_misdn channel driver for Asterisk
+ *
+ * \author Christian Richter <crich@beronet.com>
+ *
+ * \extref MISDN http://www.misdn.org/
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>isdnnet</depend>
+ <depend>misdn</depend>
+ <depend>suppserv</depend>
+ ***/
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <sys/file.h>
+#include <semaphore.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/io.h"
+#include "asterisk/frame.h"
+#include "asterisk/translate.h"
+#include "asterisk/cli.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/dsp.h"
+#include "asterisk/file.h"
+#include "asterisk/callerid.h"
+#include "asterisk/indications.h"
+#include "asterisk/app.h"
+#include "asterisk/features.h"
+#include "asterisk/term.h"
+#include "asterisk/sched.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/causes.h"
+
+#include "chan_misdn_config.h"
+#include "isdn_lib.h"
+
+char global_tracefile[BUFFERSIZE + 1];
+
+static int g_config_initialized = 0;
+
+struct misdn_jb{
+ int size;
+ int upper_threshold;
+ char *samples, *ok;
+ int wp,rp;
+ int state_empty;
+ int state_full;
+ int state_buffer;
+ int bytes_wrote;
+ ast_mutex_t mutexjb;
+};
+
+
+
+/*! \brief allocates the jb-structure and initialise the elements*/
+struct misdn_jb *misdn_jb_init(int size, int upper_threshold);
+
+/*! \brief frees the data and destroys the given jitterbuffer struct */
+void misdn_jb_destroy(struct misdn_jb *jb);
+
+/*! \brief fills the jitterbuffer with len data returns < 0 if there was an
+error (bufferoverun). */
+int misdn_jb_fill(struct misdn_jb *jb, const char *data, int len);
+
+/*! \brief gets len bytes out of the jitterbuffer if available, else only the
+available data is returned and the return value indicates the number
+of data. */
+int misdn_jb_empty(struct misdn_jb *jb, char *data, int len);
+
+static char *complete_ch(struct ast_cli_args *a);
+static char *complete_debug_port(struct ast_cli_args *a);
+static char *complete_show_config(struct ast_cli_args *a);
+
+/* BEGIN: chan_misdn.h */
+
+ast_mutex_t release_lock;
+
+enum misdn_chan_state {
+ MISDN_NOTHING=0, /*!< at beginning */
+ MISDN_WAITING4DIGS, /*!< when waiting for infos */
+ MISDN_EXTCANTMATCH, /*!< when asterisk couldnt match our ext */
+ MISDN_INCOMING_SETUP, /*!< for incoming setups*/
+ MISDN_DIALING, /*!< when pbx_start */
+ MISDN_PROGRESS, /*!< we got a progress */
+ MISDN_PROCEEDING, /*!< we got a progress */
+ MISDN_CALLING, /*!< when misdn_call is called */
+ MISDN_CALLING_ACKNOWLEDGE, /*!< when we get SETUP_ACK */
+ MISDN_ALERTING, /*!< when Alerting */
+ MISDN_BUSY, /*!< when BUSY */
+ MISDN_CONNECTED, /*!< when connected */
+ MISDN_PRECONNECTED, /*!< when connected */
+ MISDN_DISCONNECTED, /*!< when connected */
+ MISDN_RELEASED, /*!< when connected */
+ MISDN_BRIDGED, /*!< when bridged */
+ MISDN_CLEANING, /*!< when hangup from * but we were connected before */
+ MISDN_HUNGUP_FROM_MISDN, /*!< when DISCONNECT/RELEASE/REL_COMP cam from misdn */
+ MISDN_HUNGUP_FROM_AST, /*!< when DISCONNECT/RELEASE/REL_COMP came out of */
+ /* misdn_hangup */
+ MISDN_HOLDED, /*!< if this chan is holded */
+ MISDN_HOLD_DISCONNECT, /*!< if this chan is holded */
+
+};
+
+#define ORG_AST 1
+#define ORG_MISDN 2
+
+struct hold_info {
+ int port;
+ int channel;
+};
+
+struct chan_list {
+
+ char allowed_bearers[BUFFERSIZE + 1];
+
+ enum misdn_chan_state state;
+ int need_queue_hangup;
+ int need_hangup;
+ int need_busy;
+
+ int originator;
+ int noautorespond_on_setup;
+
+ int norxtone;
+ int notxtone;
+
+ int toggle_ec;
+
+ int incoming_early_audio;
+
+ int ignore_dtmf;
+
+ int pipe[2];
+ char ast_rd_buf[4096];
+ struct ast_frame frame;
+
+ int faxdetect; /*!< 0:no 1:yes 2:yes+nojump */
+ int faxdetect_timeout;
+ struct timeval faxdetect_tv;
+ int faxhandled;
+
+ int ast_dsp;
+
+ int jb_len;
+ int jb_upper_threshold;
+ struct misdn_jb *jb;
+
+ struct ast_dsp *dsp;
+ struct ast_trans_pvt *trans;
+
+ struct ast_channel * ast;
+
+ int dummy;
+
+ struct misdn_bchannel *bc;
+
+ struct hold_info hold_info;
+
+ unsigned int l3id;
+ int addr;
+
+ char context[BUFFERSIZE];
+
+ int zero_read_cnt;
+ int dropped_frame_cnt;
+
+ int far_alerting;
+
+ int nttimeout;
+
+ int other_pid;
+ struct chan_list *other_ch;
+
+ const struct ind_tone_zone_sound *ts;
+
+ int overlap_dial;
+ int overlap_dial_task;
+ ast_mutex_t overlap_tv_lock;
+ struct timeval overlap_tv;
+
+ struct chan_list *peer;
+ struct chan_list *next;
+ struct chan_list *prev;
+ struct chan_list *first;
+};
+
+
+
+void export_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch);
+void import_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch);
+
+struct robin_list {
+ char *group;
+ int port;
+ int channel;
+ struct robin_list *next;
+ struct robin_list *prev;
+};
+static struct robin_list *robin = NULL;
+
+
+
+static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame);
+
+
+
+static inline void free_robin_list_r (struct robin_list *r)
+{
+ if (r) {
+ if (r->next)
+ free_robin_list_r(r->next);
+ if (r->group)
+ ast_free(r->group);
+ ast_free(r);
+ }
+}
+
+static void free_robin_list ( void )
+{
+ free_robin_list_r(robin);
+ robin = NULL;
+}
+
+static struct robin_list* get_robin_position (char *group)
+{
+ struct robin_list *new;
+ struct robin_list *iter = robin;
+ for (; iter; iter = iter->next) {
+ if (!strcasecmp(iter->group, group))
+ return iter;
+ }
+ new = ast_calloc(1, sizeof(*new));
+ new->group = strndup(group, strlen(group));
+ new->channel = 1;
+ if (robin) {
+ new->next = robin;
+ robin->prev = new;
+ }
+ robin = new;
+ return robin;
+}
+
+
+/*! \brief the main schedule context for stuff like l1 watcher, overlap dial, ... */
+static struct sched_context *misdn_tasks = NULL;
+static pthread_t misdn_tasks_thread;
+
+static int *misdn_ports;
+
+static void chan_misdn_log(int level, int port, char *tmpl, ...);
+
+static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, int port, int c);
+static void send_digit_to_chan(struct chan_list *cl, char digit );
+
+static void hangup_chan(struct chan_list *ch);
+static int pbx_start_chan(struct chan_list *ch);
+
+#define MISDN_ASTERISK_TECH_PVT(ast) ast->tech_pvt
+#define MISDN_ASTERISK_PVT(ast) 1
+
+#include "asterisk/strings.h"
+
+/* #define MISDN_DEBUG 1 */
+
+static const char misdn_type[] = "mISDN";
+
+static int tracing = 0 ;
+
+/*! \brief Only alaw and mulaw is allowed for now */
+static int prefformat = AST_FORMAT_ALAW ; /* AST_FORMAT_SLINEAR ; AST_FORMAT_ULAW | */
+
+static int *misdn_debug;
+static int *misdn_debug_only;
+static int max_ports;
+
+static int *misdn_in_calls;
+static int *misdn_out_calls;
+
+
+struct chan_list dummy_cl;
+
+struct chan_list *cl_te=NULL;
+ast_mutex_t cl_te_lock;
+
+static enum event_response_e
+cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data);
+
+static void send_cause2ast(struct ast_channel *ast, struct misdn_bchannel*bc, struct chan_list *ch);
+
+static void cl_queue_chan(struct chan_list **list, struct chan_list *chan);
+static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan);
+static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc);
+static struct chan_list *find_chan_by_pid(struct chan_list *list, int pid);
+
+
+
+static int dialtone_indicate(struct chan_list *cl);
+static int hanguptone_indicate(struct chan_list *cl);
+static int stop_indicate(struct chan_list *cl);
+
+static int start_bc_tones(struct chan_list *cl);
+static int stop_bc_tones(struct chan_list *cl);
+static void release_chan(struct misdn_bchannel *bc);
+
+static int misdn_check_l2l1(struct ast_channel *chan, void *data);
+static int misdn_set_opt_exec(struct ast_channel *chan, void *data);
+static int misdn_facility_exec(struct ast_channel *chan, void *data);
+
+int chan_misdn_jb_empty(struct misdn_bchannel *bc, char *buf, int len);
+
+
+void debug_numplan(int port, int numplan, char *type);
+
+
+int add_out_calls(int port);
+int add_in_calls(int port);
+
+
+#ifdef MISDN_1_2
+static int update_pipeline_config(struct misdn_bchannel *bc);
+#else
+static int update_ec_config(struct misdn_bchannel *bc);
+#endif
+
+
+
+
+/*protos*/
+
+int chan_misdn_jb_empty ( struct misdn_bchannel *bc, char *buf, int len);
+
+/*************** Helpers *****************/
+
+static struct chan_list * get_chan_by_ast(struct ast_channel *ast)
+{
+ struct chan_list *tmp;
+
+ for (tmp=cl_te; tmp; tmp = tmp->next) {
+ if ( tmp->ast == ast ) return tmp;
+ }
+
+ return NULL;
+}
+
+static struct chan_list * get_chan_by_ast_name(char *name)
+{
+ struct chan_list *tmp;
+
+ for (tmp=cl_te; tmp; tmp = tmp->next) {
+ if ( tmp->ast && strcmp(tmp->ast->name,name) == 0) return tmp;
+ }
+
+ return NULL;
+}
+
+
+
+struct allowed_bearers {
+ int cap;
+ int val;
+ char *name;
+};
+
+struct allowed_bearers allowed_bearers_array[]={
+ {INFO_CAPABILITY_SPEECH,1,"speech"},
+ {INFO_CAPABILITY_AUDIO_3_1K,2,"3_1khz"},
+ {INFO_CAPABILITY_DIGITAL_UNRESTRICTED,4,"digital_unrestricted"},
+ {INFO_CAPABILITY_DIGITAL_RESTRICTED,8,"digital_restriced"},
+ {INFO_CAPABILITY_VIDEO,16,"video"}
+};
+
+static char *bearer2str(int cap) {
+ static char *bearers[]={
+ "Speech",
+ "Audio 3.1k",
+ "Unres Digital",
+ "Res Digital",
+ "Video",
+ "Unknown Bearer"
+ };
+
+ switch (cap) {
+ case INFO_CAPABILITY_SPEECH:
+ return bearers[0];
+ break;
+ case INFO_CAPABILITY_AUDIO_3_1K:
+ return bearers[1];
+ break;
+ case INFO_CAPABILITY_DIGITAL_UNRESTRICTED:
+ return bearers[2];
+ break;
+ case INFO_CAPABILITY_DIGITAL_RESTRICTED:
+ return bearers[3];
+ break;
+ case INFO_CAPABILITY_VIDEO:
+ return bearers[4];
+ break;
+ default:
+ return bearers[5];
+ break;
+ }
+}
+
+
+static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc)
+{
+ switch (fac->Function) {
+#ifdef HAVE_MISDN_FAC_RESULT
+ case Fac_RESULT:
+ chan_misdn_log(0, bc->port," --> Received RESULT Operation\n");
+ break;
+#endif
+#ifdef HAVE_MISDN_FAC_ERROR
+ case Fac_ERROR:
+ chan_misdn_log(0, bc->port," --> Received Error Operation\n");
+ chan_misdn_log(0, bc->port," --> Value:%d Error:%s\n",fac->u.ERROR.errorValue, fac->u.ERROR.error);
+ break;
+#endif
+ case Fac_CD:
+ chan_misdn_log(1,bc->port," --> calldeflect to: %s, screened: %s\n", fac->u.CDeflection.DeflectedToNumber,
+ fac->u.CDeflection.PresentationAllowed ? "yes" : "no");
+ break;
+ case Fac_AOCDCurrency:
+ if (fac->u.AOCDcur.chargeNotAvailable)
+ chan_misdn_log(1,bc->port," --> AOCD currency: charge not available\n");
+ else if (fac->u.AOCDcur.freeOfCharge)
+ chan_misdn_log(1,bc->port," --> AOCD currency: free of charge\n");
+ else if (fac->u.AOCDchu.billingId >= 0)
+ chan_misdn_log(1,bc->port," --> AOCD currency: currency:%s amount:%d multiplier:%d typeOfChargingInfo:%d billingId:%d\n",
+ fac->u.AOCDcur.currency, fac->u.AOCDcur.currencyAmount, fac->u.AOCDcur.multiplier,
+ (fac->u.AOCDcur.typeOfChargingInfo == 0) ? "subTotal" : "total", fac->u.AOCDcur.billingId);
+ else
+ chan_misdn_log(1,bc->port," --> AOCD currency: currency:%s amount:%d multiplier:%d typeOfChargingInfo:%d\n",
+ fac->u.AOCDcur.currency, fac->u.AOCDcur.currencyAmount, fac->u.AOCDcur.multiplier,
+ (fac->u.AOCDcur.typeOfChargingInfo == 0) ? "subTotal" : "total");
+ break;
+ case Fac_AOCDChargingUnit:
+ if (fac->u.AOCDchu.chargeNotAvailable)
+ chan_misdn_log(1,bc->port," --> AOCD charging unit: charge not available\n");
+ else if (fac->u.AOCDchu.freeOfCharge)
+ chan_misdn_log(1,bc->port," --> AOCD charging unit: free of charge\n");
+ else if (fac->u.AOCDchu.billingId >= 0)
+ chan_misdn_log(1,bc->port," --> AOCD charging unit: recordedUnits:%d typeOfChargingInfo:%s billingId:%d\n",
+ fac->u.AOCDchu.recordedUnits, (fac->u.AOCDchu.typeOfChargingInfo == 0) ? "subTotal" : "total", fac->u.AOCDchu.billingId);
+ else
+ chan_misdn_log(1,bc->port," --> AOCD charging unit: recordedUnits:%d typeOfChargingInfo:%s\n",
+ fac->u.AOCDchu.recordedUnits, (fac->u.AOCDchu.typeOfChargingInfo == 0) ? "subTotal" : "total");
+ break;
+ case Fac_None:
+ default:
+ chan_misdn_log(1,bc->port," --> unknown facility\n");
+ }
+}
+
+static void print_bearer(struct misdn_bchannel *bc)
+{
+
+ chan_misdn_log(2, bc->port, " --> Bearer: %s\n",bearer2str(bc->capability));
+
+ switch(bc->law) {
+ case INFO_CODEC_ALAW:
+ chan_misdn_log(2, bc->port, " --> Codec: Alaw\n");
+ break;
+ case INFO_CODEC_ULAW:
+ chan_misdn_log(2, bc->port, " --> Codec: Ulaw\n");
+ break;
+ }
+}
+
+static void export_aoc_vars(int originator, struct ast_channel *ast, struct misdn_bchannel *bc)
+{
+ char buf[128];
+
+ if (!bc->AOCD_need_export || !ast)
+ return;
+
+ if (originator == ORG_AST) {
+ ast = ast_bridged_channel(ast);
+ if (!ast)
+ return;
+ }
+
+ switch (bc->AOCDtype) {
+ case Fac_AOCDCurrency:
+ pbx_builtin_setvar_helper(ast, "AOCD_Type", "currency");
+ if (bc->AOCD.currency.chargeNotAvailable)
+ pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "no");
+ else {
+ pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "yes");
+ if (bc->AOCD.currency.freeOfCharge)
+ pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "yes");
+ else {
+ pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "no");
+ if (snprintf(buf, sizeof(buf), "%d %s", bc->AOCD.currency.currencyAmount * bc->AOCD.currency.multiplier, bc->AOCD.currency.currency) < sizeof(buf)) {
+ pbx_builtin_setvar_helper(ast, "AOCD_Amount", buf);
+ if (bc->AOCD.currency.billingId >= 0 && snprintf(buf, sizeof(buf), "%d", bc->AOCD.currency.billingId) < sizeof(buf))
+ pbx_builtin_setvar_helper(ast, "AOCD_BillingId", buf);
+ }
+ }
+ }
+ break;
+ case Fac_AOCDChargingUnit:
+ pbx_builtin_setvar_helper(ast, "AOCD_Type", "charging_unit");
+ if (bc->AOCD.chargingUnit.chargeNotAvailable)
+ pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "no");
+ else {
+ pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "yes");
+ if (bc->AOCD.chargingUnit.freeOfCharge)
+ pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "yes");
+ else {
+ pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "no");
+ if (snprintf(buf, sizeof(buf), "%d", bc->AOCD.chargingUnit.recordedUnits) < sizeof(buf)) {
+ pbx_builtin_setvar_helper(ast, "AOCD_RecordedUnits", buf);
+ if (bc->AOCD.chargingUnit.billingId >= 0 && snprintf(buf, sizeof(buf), "%d", bc->AOCD.chargingUnit.billingId) < sizeof(buf))
+ pbx_builtin_setvar_helper(ast, "AOCD_BillingId", buf);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ bc->AOCD_need_export = 0;
+}
+
+/*************** Helpers END *************/
+
+static void sighandler(int sig)
+{}
+
+static void* misdn_tasks_thread_func (void *data)
+{
+ int wait;
+ struct sigaction sa;
+
+ sa.sa_handler = sighandler;
+ sa.sa_flags = SA_NODEFER;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGUSR1);
+ sigaction(SIGUSR1, &sa, NULL);
+
+ sem_post((sem_t *)data);
+
+ while (1) {
+ wait = ast_sched_wait(misdn_tasks);
+ if (wait < 0)
+ wait = 8000;
+ if (poll(NULL, 0, wait) < 0)
+ chan_misdn_log(4, 0, "Waking up misdn_tasks thread\n");
+ ast_sched_runq(misdn_tasks);
+ }
+ return NULL;
+}
+
+static void misdn_tasks_init (void)
+{
+ sem_t blocker;
+ int i = 5;
+
+ if (sem_init(&blocker, 0, 0)) {
+ perror("chan_misdn: Failed to initialize semaphore!");
+ exit(1);
+ }
+
+ chan_misdn_log(4, 0, "Starting misdn_tasks thread\n");
+
+ misdn_tasks = sched_context_create();
+ pthread_create(&misdn_tasks_thread, NULL, misdn_tasks_thread_func, &blocker);
+
+ while (sem_wait(&blocker) && --i);
+ sem_destroy(&blocker);
+}
+
+static void misdn_tasks_destroy (void)
+{
+ if (misdn_tasks) {
+ chan_misdn_log(4, 0, "Killing misdn_tasks thread\n");
+ if ( pthread_cancel(misdn_tasks_thread) == 0 ) {
+ cb_log(4, 0, "Joining misdn_tasks thread\n");
+ pthread_join(misdn_tasks_thread, NULL);
+ }
+ sched_context_destroy(misdn_tasks);
+ }
+}
+
+static inline void misdn_tasks_wakeup (void)
+{
+ pthread_kill(misdn_tasks_thread, SIGUSR1);
+}
+
+static inline int _misdn_tasks_add_variable (int timeout, ast_sched_cb callback, const void *data, int variable)
+{
+ int task_id;
+
+ if (!misdn_tasks) {
+ misdn_tasks_init();
+ }
+ task_id = ast_sched_add_variable(misdn_tasks, timeout, callback, data, variable);
+ misdn_tasks_wakeup();
+
+ return task_id;
+}
+
+static int misdn_tasks_add (int timeout, ast_sched_cb callback, const void *data)
+{
+ return _misdn_tasks_add_variable(timeout, callback, data, 0);
+}
+
+static int misdn_tasks_add_variable (int timeout, ast_sched_cb callback, const void *data)
+{
+ return _misdn_tasks_add_variable(timeout, callback, data, 1);
+}
+
+static void misdn_tasks_remove (int task_id)
+{
+ ast_sched_del(misdn_tasks, task_id);
+}
+
+static int misdn_l1_task (const void *data)
+{
+ misdn_lib_isdn_l1watcher(*(int *)data);
+ chan_misdn_log(5, *(int *)data, "L1watcher timeout\n");
+ return 1;
+}
+
+static int misdn_overlap_dial_task (const void *data)
+{
+ struct timeval tv_end, tv_now;
+ int diff;
+ struct chan_list *ch = (struct chan_list *)data;
+
+ chan_misdn_log(4, ch->bc->port, "overlap dial task, chan_state: %d\n", ch->state);
+
+ if (ch->state != MISDN_WAITING4DIGS) {
+ ch->overlap_dial_task = -1;
+ return 0;
+ }
+
+ ast_mutex_lock(&ch->overlap_tv_lock);
+ tv_end = ch->overlap_tv;
+ ast_mutex_unlock(&ch->overlap_tv_lock);
+
+ tv_end.tv_sec += ch->overlap_dial;
+ tv_now = ast_tvnow();
+
+ diff = ast_tvdiff_ms(tv_end, tv_now);
+
+ if (diff <= 100) {
+ char *dad=ch->bc->dad, sexten[]="s";
+ /* if we are 100ms near the timeout, we are satisfied.. */
+ stop_indicate(ch);
+
+ if (ast_strlen_zero(ch->bc->dad)) {
+ dad=sexten;
+ strcpy(ch->ast->exten, sexten);
+ }
+
+ if (ast_exists_extension(ch->ast, ch->context, dad, 1, ch->bc->oad)) {
+ ch->state=MISDN_DIALING;
+ if (pbx_start_chan(ch) < 0) {
+ chan_misdn_log(-1, ch->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n");
+ goto misdn_overlap_dial_task_disconnect;
+ }
+ } else {
+misdn_overlap_dial_task_disconnect:
+ hanguptone_indicate(ch);
+ ch->bc->out_cause=1;
+ ch->state=MISDN_CLEANING;
+ misdn_lib_send_event(ch->bc, EVENT_DISCONNECT);
+ }
+ ch->overlap_dial_task = -1;
+ return 0;
+ } else
+ return diff;
+}
+
+static void send_digit_to_chan(struct chan_list *cl, char digit )
+{
+ static const char* dtmf_tones[] = {
+ "!941+1336/100,!0/100", /* 0 */
+ "!697+1209/100,!0/100", /* 1 */
+ "!697+1336/100,!0/100", /* 2 */
+ "!697+1477/100,!0/100", /* 3 */
+ "!770+1209/100,!0/100", /* 4 */
+ "!770+1336/100,!0/100", /* 5 */
+ "!770+1477/100,!0/100", /* 6 */
+ "!852+1209/100,!0/100", /* 7 */
+ "!852+1336/100,!0/100", /* 8 */
+ "!852+1477/100,!0/100", /* 9 */
+ "!697+1633/100,!0/100", /* A */
+ "!770+1633/100,!0/100", /* B */
+ "!852+1633/100,!0/100", /* C */
+ "!941+1633/100,!0/100", /* D */
+ "!941+1209/100,!0/100", /* * */
+ "!941+1477/100,!0/100" }; /* # */
+ struct ast_channel *chan=cl->ast;
+
+ if (digit >= '0' && digit <='9')
+ ast_playtones_start(chan,0,dtmf_tones[digit-'0'], 0);
+ else if (digit >= 'A' && digit <= 'D')
+ ast_playtones_start(chan,0,dtmf_tones[digit-'A'+10], 0);
+ else if (digit == '*')
+ ast_playtones_start(chan,0,dtmf_tones[14], 0);
+ else if (digit == '#')
+ ast_playtones_start(chan,0,dtmf_tones[15], 0);
+ else {
+ /* not handled */
+ ast_debug(1, "Unable to handle DTMF tone '%c' for '%s'\n", digit, chan->name);
+ }
+}
+
+/*** CLI HANDLING ***/
+static char *handle_cli_misdn_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int level;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn set debug";
+ e->usage =
+ "Usage: misdn set debug <level> [only] | [port <port> [only]]\n"
+ " Set the debug level of the mISDN channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_debug_port(a);
+ }
+
+ if (a->argc < 4 || a->argc > 7)
+ return CLI_SHOWUSAGE;
+
+ level = atoi(a->argv[3]);
+
+ switch (a->argc) {
+ case 4:
+ case 5:
+ {
+ int only = 0, i;
+ if (a->argc == 5) {
+ if (strncasecmp(a->argv[4], "only", strlen(a->argv[4])))
+ return CLI_SHOWUSAGE;
+ else
+ only = 1;
+ }
+
+ for (i = 0; i <= max_ports; i++) {
+ misdn_debug[i] = level;
+ misdn_debug_only[i] = only;
+ }
+ ast_cli(a->fd, "changing debug level for all ports to %d%s\n",misdn_debug[0], only?" (only)":"");
+ }
+ break;
+ case 6:
+ case 7:
+ {
+ int port;
+ if (strncasecmp(a->argv[4], "port", strlen(a->argv[4])))
+ return CLI_SHOWUSAGE;
+ port = atoi(a->argv[5]);
+ if (port <= 0 || port > max_ports) {
+ switch (max_ports) {
+ case 0:
+ ast_cli(a->fd, "port number not valid! no ports available so you won't get lucky with any number here...\n");
+ break;
+ case 1:
+ ast_cli(a->fd, "port number not valid! only port 1 is availble.\n");
+ break;
+ default:
+ ast_cli(a->fd, "port number not valid! only ports 1 to %d are available.\n", max_ports);
+ }
+ return 0;
+ }
+ if (a->argc == 7) {
+ if (strncasecmp(a->argv[6], "only", strlen(a->argv[6])))
+ return CLI_SHOWUSAGE;
+ else
+ misdn_debug_only[port] = 1;
+ } else
+ misdn_debug_only[port] = 0;
+ misdn_debug[port] = level;
+ ast_cli(a->fd, "changing debug level to %d%s for port %d\n", misdn_debug[port], misdn_debug_only[port]?" (only)":"", port);
+ }
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_set_crypt_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn set crypt debug";
+ e->usage =
+ "Usage: misdn set crypt debug <level>\n"
+ " Set the crypt debug level of the mISDN channel. Level\n"
+ " must be 1 or 2.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+
+ /* Is this supposed to not do anything? */
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_port_block(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn port block";
+ e->usage =
+ "Usage: misdn port block <port>\n"
+ " Block the specified port by <port>.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ misdn_lib_port_block(atoi(a->argv[3]));
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_port_unblock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn port unblock";
+ e->usage =
+ "Usage: misdn port unblock <port>\n"
+ " Unblock the port specified by <port>.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ misdn_lib_port_unblock(atoi(a->argv[3]));
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_restart_port(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn restart port";
+ e->usage =
+ "Usage: misdn restart port <port>\n"
+ " Restart the given port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ misdn_lib_port_restart(atoi(a->argv[3]));
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_restart_pid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn restart pid";
+ e->usage =
+ "Usage: misdn restart pid <pid>\n"
+ " Restart the given pid\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ misdn_lib_pid_restart(atoi(a->argv[3]));
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_port_up(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn port up";
+ e->usage =
+ "Usage: misdn port up <port>\n"
+ " Try to establish L1 on the given port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ misdn_lib_get_port_up(atoi(a->argv[3]));
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_port_down(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn port down";
+ e->usage =
+ "Usage: misdn port down <port>\n"
+ " Try to deacivate the L1 on the given port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ misdn_lib_get_port_down(atoi(a->argv[3]));
+
+ return CLI_SUCCESS;
+}
+
+static inline void show_config_description(int fd, enum misdn_cfg_elements elem)
+{
+ char section[BUFFERSIZE];
+ char name[BUFFERSIZE];
+ char desc[BUFFERSIZE];
+ char def[BUFFERSIZE];
+ char tmp[BUFFERSIZE];
+
+ misdn_cfg_get_name(elem, tmp, sizeof(tmp));
+ term_color(name, tmp, COLOR_BRWHITE, 0, sizeof(tmp));
+ misdn_cfg_get_desc(elem, desc, sizeof(desc), def, sizeof(def));
+
+ if (elem < MISDN_CFG_LAST)
+ term_color(section, "PORTS SECTION", COLOR_YELLOW, 0, sizeof(section));
+ else
+ term_color(section, "GENERAL SECTION", COLOR_YELLOW, 0, sizeof(section));
+
+ if (*def)
+ ast_cli(fd, "[%s] %s (Default: %s)\n\t%s\n", section, name, def, desc);
+ else
+ ast_cli(fd, "[%s] %s\n\t%s\n", section, name, desc);
+
+ return;
+}
+
+static char *handle_cli_misdn_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char buffer[BUFFERSIZE];
+ enum misdn_cfg_elements elem;
+ int linebreak;
+ int onlyport = -1;
+ int ok = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn show config";
+ e->usage =
+ "Usage: misdn show config [<port> | description <config element> | descriptions [general|ports]]\n"
+ " Use 0 for <port> to only print the general config.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_show_config(a);
+ }
+
+ if (a->argc >= 4) {
+ if (!strcmp(a->argv[3], "description")) {
+ if (a->argc == 5) {
+ enum misdn_cfg_elements elem = misdn_cfg_get_elem(a->argv[4]);
+ if (elem == MISDN_CFG_FIRST)
+ ast_cli(a->fd, "Unknown element: %s\n", a->argv[4]);
+ else
+ show_config_description(a->fd, elem);
+ return CLI_SUCCESS;
+ }
+ return CLI_SHOWUSAGE;
+ } else if (!strcmp(a->argv[3], "descriptions")) {
+ if ((a->argc == 4) || ((a->argc == 5) && !strcmp(a->argv[4], "general"))) {
+ for (elem = MISDN_GEN_FIRST + 1; elem < MISDN_GEN_LAST; ++elem) {
+ show_config_description(a->fd, elem);
+ ast_cli(a->fd, "\n");
+ }
+ ok = 1;
+ }
+ if ((a->argc == 4) || ((a->argc == 5) && !strcmp(a->argv[4], "ports"))) {
+ for (elem = MISDN_CFG_FIRST + 1; elem < MISDN_CFG_LAST - 1 /* the ptp hack, remove the -1 when ptp is gone */; ++elem) {
+ show_config_description(a->fd, elem);
+ ast_cli(a->fd, "\n");
+ }
+ ok = 1;
+ }
+ return ok ? CLI_SUCCESS : CLI_SHOWUSAGE;
+ } else if (!sscanf(a->argv[3], "%d", &onlyport) || onlyport < 0) {
+ ast_cli(a->fd, "Unknown option: %s\n", a->argv[3]);
+ return CLI_SHOWUSAGE;
+ }
+ } else if (a->argc == 3 || onlyport == 0) {
+ ast_cli(a->fd, "mISDN General-Config:\n");
+ for (elem = MISDN_GEN_FIRST + 1, linebreak = 1; elem < MISDN_GEN_LAST; elem++, linebreak++) {
+ misdn_cfg_get_config_string(0, elem, buffer, sizeof(buffer));
+ ast_cli(a->fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : "");
+ }
+ ast_cli(a->fd, "\n");
+ }
+
+ if (onlyport < 0) {
+ int port = misdn_cfg_get_next_port(0);
+ for (; port > 0; port = misdn_cfg_get_next_port(port)) {
+ ast_cli(a->fd, "\n[PORT %d]\n", port);
+ for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) {
+ misdn_cfg_get_config_string(port, elem, buffer, sizeof(buffer));
+ ast_cli(a->fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : "");
+ }
+ ast_cli(a->fd, "\n");
+ }
+ }
+
+ if (onlyport > 0) {
+ if (misdn_cfg_is_port_valid(onlyport)) {
+ ast_cli(a->fd, "[PORT %d]\n", onlyport);
+ for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) {
+ misdn_cfg_get_config_string(onlyport, elem, buffer, sizeof(buffer));
+ ast_cli(a->fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : "");
+ }
+ ast_cli(a->fd, "\n");
+ } else {
+ ast_cli(a->fd, "Port %d is not active!\n", onlyport);
+ }
+ }
+
+ return CLI_SUCCESS;
+}
+
+struct state_struct {
+ enum misdn_chan_state state;
+ char txt[255];
+};
+
+static struct state_struct state_array[] = {
+ {MISDN_NOTHING,"NOTHING"}, /* at beginning */
+ {MISDN_WAITING4DIGS,"WAITING4DIGS"}, /* when waiting for infos */
+ {MISDN_EXTCANTMATCH,"EXTCANTMATCH"}, /* when asterisk couldnt match our ext */
+ {MISDN_INCOMING_SETUP,"INCOMING SETUP"}, /* when pbx_start */
+ {MISDN_DIALING,"DIALING"}, /* when pbx_start */
+ {MISDN_PROGRESS,"PROGRESS"}, /* when pbx_start */
+ {MISDN_PROCEEDING,"PROCEEDING"}, /* when pbx_start */
+ {MISDN_CALLING,"CALLING"}, /* when misdn_call is called */
+ {MISDN_CALLING_ACKNOWLEDGE,"CALLING_ACKNOWLEDGE"}, /* when misdn_call is called */
+ {MISDN_ALERTING,"ALERTING"}, /* when Alerting */
+ {MISDN_BUSY,"BUSY"}, /* when BUSY */
+ {MISDN_CONNECTED,"CONNECTED"}, /* when connected */
+ {MISDN_PRECONNECTED,"PRECONNECTED"}, /* when connected */
+ {MISDN_DISCONNECTED,"DISCONNECTED"}, /* when connected */
+ {MISDN_RELEASED,"RELEASED"}, /* when connected */
+ {MISDN_BRIDGED,"BRIDGED"}, /* when bridged */
+ {MISDN_CLEANING,"CLEANING"}, /* when hangup from * but we were connected before */
+ {MISDN_HUNGUP_FROM_MISDN,"HUNGUP_FROM_MISDN"}, /* when DISCONNECT/RELEASE/REL_COMP cam from misdn */
+ {MISDN_HOLDED,"HOLDED"}, /* when DISCONNECT/RELEASE/REL_COMP cam from misdn */
+ {MISDN_HOLD_DISCONNECT,"HOLD_DISCONNECT"}, /* when DISCONNECT/RELEASE/REL_COMP cam from misdn */
+ {MISDN_HUNGUP_FROM_AST,"HUNGUP_FROM_AST"} /* when DISCONNECT/RELEASE/REL_COMP came out of */
+ /* misdn_hangup */
+};
+
+static const char *misdn_get_ch_state(struct chan_list *p)
+{
+ int i;
+ static char state[8];
+
+ if( !p) return NULL;
+
+ for (i = 0; i < sizeof(state_array) / sizeof(struct state_struct); i++) {
+ if (state_array[i].state == p->state)
+ return state_array[i].txt;
+ }
+
+ snprintf(state, sizeof(state), "%d", p->state) ;
+
+ return state;
+}
+
+
+
+static void reload_config(void)
+{
+ int i, cfg_debug;
+
+ if (!g_config_initialized) {
+ ast_log(LOG_WARNING, "chan_misdn is not initialized properly, still reloading ?\n");
+ return ;
+ }
+
+ free_robin_list();
+ misdn_cfg_reload();
+ misdn_cfg_update_ptp();
+ misdn_cfg_get(0, MISDN_GEN_TRACEFILE, global_tracefile, sizeof(global_tracefile));
+ misdn_cfg_get(0, MISDN_GEN_DEBUG, &cfg_debug, sizeof(cfg_debug));
+
+ for (i = 0; i <= max_ports; i++) {
+ misdn_debug[i] = cfg_debug;
+ misdn_debug_only[i] = 0;
+ }
+}
+
+static char *handle_cli_misdn_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn reload";
+ e->usage =
+ "Usage: misdn reload\n"
+ " Reload internal mISDN config, read from the config\n"
+ " file.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "Reloading mISDN configuration\n");
+ reload_config();
+ return CLI_SUCCESS;
+}
+
+static void print_bc_info (int fd, struct chan_list *help, struct misdn_bchannel *bc)
+{
+ struct ast_channel *ast = help->ast;
+ ast_cli(fd,
+ "* Pid:%d Prt:%d Ch:%d Mode:%s Org:%s dad:%s oad:%s rad:%s ctx:%s state:%s\n",
+
+ bc->pid, bc->port, bc->channel,
+ bc->nt ? "NT" : "TE",
+ help->originator == ORG_AST ? "*" : "I",
+ ast ? ast->exten : NULL,
+ ast ? ast->cid.cid_num : NULL,
+ bc->rad,
+ ast ? ast->context : NULL,
+ misdn_get_ch_state(help)
+ );
+ if (misdn_debug[bc->port] > 0)
+ ast_cli(fd,
+ " --> astname: %s\n"
+ " --> ch_l3id: %x\n"
+ " --> ch_addr: %x\n"
+ " --> bc_addr: %x\n"
+ " --> bc_l3id: %x\n"
+ " --> display: %s\n"
+ " --> activated: %d\n"
+ " --> state: %s\n"
+ " --> capability: %s\n"
+#ifdef MISDN_1_2
+ " --> pipeline: %s\n"
+#else
+ " --> echo_cancel: %d\n"
+#endif
+ " --> notone : rx %d tx:%d\n"
+ " --> bc_hold: %d\n",
+ help->ast->name,
+ help->l3id,
+ help->addr,
+ bc->addr,
+ bc ? bc->l3_id : -1,
+ bc->display,
+
+ bc->active,
+ bc_state2str(bc->bc_state),
+ bearer2str(bc->capability),
+#ifdef MISDN_1_2
+ bc->pipeline,
+#else
+ bc->ec_enable,
+#endif
+
+ help->norxtone, help->notxtone,
+ bc->holded
+ );
+
+}
+
+static char *handle_cli_misdn_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_list *help = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn show channels";
+ e->usage =
+ "Usage: misdn show channels\n"
+ " Show the internal mISDN channel list\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ help = cl_te;
+
+ ast_cli(a->fd, "Channel List: %p\n", cl_te);
+
+ for (; help; help = help->next) {
+ struct misdn_bchannel *bc = help->bc;
+ struct ast_channel *ast = help->ast;
+ if (misdn_debug[0] > 2)
+ ast_cli(a->fd, "Bc:%p Ast:%p\n", bc, ast);
+ if (bc) {
+ print_bc_info(a->fd, help, bc);
+ } else {
+ if (help->state == MISDN_HOLDED) {
+ ast_cli(a->fd, "ITS A HOLDED BC:\n");
+ ast_cli(a->fd, " --> l3_id: %x\n"
+ " --> dad:%s oad:%s\n"
+ " --> hold_port: %d\n"
+ " --> hold_channel: %d\n",
+ help->l3id,
+ ast->exten,
+ ast->cid.cid_num,
+ help->hold_info.port,
+ help->hold_info.channel
+ );
+ } else {
+ ast_cli(a->fd, "* Channel in unknown STATE !!! Exten:%s, Callerid:%s\n", ast->exten, ast->cid.cid_num);
+ }
+ }
+ }
+
+ misdn_dump_chanlist();
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_list *help = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn show channel";
+ e->usage =
+ "Usage: misdn show channel <channel>\n"
+ " Show an internal mISDN channel\n.";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_ch(a);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ help = cl_te;
+
+ for (; help; help = help->next) {
+ struct misdn_bchannel *bc = help->bc;
+ struct ast_channel *ast = help->ast;
+
+ if (bc && ast) {
+ if (!strcasecmp(ast->name, a->argv[3])) {
+ print_bc_info(a->fd, help, bc);
+ break;
+ }
+ }
+ }
+
+ return CLI_SUCCESS;
+}
+
+ast_mutex_t lock;
+int MAXTICS = 8;
+
+static char *handle_cli_misdn_set_tics(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn set tics";
+ e->usage =
+ "Usage: misdn set tics <value>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ MAXTICS = atoi(a->argv[3]);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_show_stacks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int port;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn show stacks";
+ e->usage =
+ "Usage: misdn show stacks\n"
+ " Show internal mISDN stack_list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "BEGIN STACK_LIST:\n");
+ for (port = misdn_cfg_get_next_port(0); port > 0;
+ port = misdn_cfg_get_next_port(port)) {
+ char buf[128];
+ get_show_stack_details(port, buf);
+ ast_cli(a->fd," %s Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port] ? "(only)" : "");
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_show_ports_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int port;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn show ports stats";
+ e->usage =
+ "Usage: misdn show ports stats\n"
+ " Show mISDNs channel's call statistics per port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "Port\tin_calls\tout_calls\n");
+ for (port = misdn_cfg_get_next_port(0); port > 0;
+ port = misdn_cfg_get_next_port(port)) {
+ ast_cli(a->fd, "%d\t%d\t\t%d\n", port, misdn_in_calls[port], misdn_out_calls[port]);
+ }
+ ast_cli(a->fd, "\n");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_show_port(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int port;
+ char buf[128];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn show port";
+ e->usage =
+ "Usage: misdn show port <port>\n"
+ " Show detailed information for given port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ port = atoi(a->argv[3]);
+
+ ast_cli(a->fd, "BEGIN STACK_LIST:\n");
+ get_show_stack_details(port, buf);
+ ast_cli(a->fd, " %s Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port] ? "(only)" : "");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *channame;
+ char *nr;
+ struct chan_list *tmp;
+ int port;
+ char *served_nr;
+ struct misdn_bchannel dummy, *bc=&dummy;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn send facility";
+ e->usage = "Usage: misdn send facility <type> <channel|port> \"<args>\" \n"
+ "\t type is one of:\n"
+ "\t - calldeflect\n"
+ "\t - CFActivate\n"
+ "\t - CFDeactivate\n";
+
+ return NULL;
+ case CLI_GENERATE:
+ return complete_ch(a);
+ }
+
+ if (a->argc < 5)
+ return CLI_SHOWUSAGE;
+
+ if (strstr(a->argv[3], "calldeflect")) {
+ if (a->argc < 6) {
+ ast_verbose("calldeflect requires 1 arg: ToNumber\n\n");
+ return 0;
+ }
+ channame = a->argv[4];
+ nr = a->argv[5];
+
+ ast_verbose("Sending Calldeflection (%s) to %s\n", nr, channame);
+ tmp = get_chan_by_ast_name(channame);
+ if (!tmp) {
+ ast_verbose("Sending CD with nr %s to %s failed: Channel does not exist.\n",nr, channame);
+ return 0;
+ }
+
+ if (strlen(nr) >= 15) {
+ ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to 15 digits are allowed).\n",nr, channame);
+ return 0;
+ }
+ tmp->bc->fac_out.Function = Fac_CD;
+ ast_copy_string((char *)tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr, sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber));
+ misdn_lib_send_event(tmp->bc, EVENT_FACILITY);
+ } else if (strstr(a->argv[3],"CFActivate")) {
+ if (a->argc < 7) {
+ ast_verbose("CFActivate requires 2 args: 1.FromNumber, 2.ToNumber\n\n");
+ return 0;
+ }
+ port = atoi(a->argv[4]);
+ served_nr = a->argv[5];
+ nr = a->argv[6];
+
+ misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0);
+
+ ast_verbose("Sending CFActivate Port:(%d) FromNr. (%s) to Nr. (%s)\n", port, served_nr, nr);
+
+ bc->fac_out.Function = Fac_CFActivate;
+ bc->fac_out.u.CFActivate.BasicService = 0; //All Services
+ bc->fac_out.u.CFActivate.Procedure = 0; //Unconditional
+ ast_copy_string((char *)bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber));
+ ast_copy_string((char *)bc->fac_out.u.CFActivate.ForwardedToNumber, nr, sizeof(bc->fac_out.u.CFActivate.ForwardedToNumber));
+
+ misdn_lib_send_event(bc, EVENT_FACILITY);
+ } else if (strstr(a->argv[3],"CFDeactivate")) {
+
+ if (a->argc < 6) {
+ ast_verbose("CFActivate requires 1 arg: FromNumber\n\n");
+ return 0;
+ }
+ port = atoi(a->argv[4]);
+ served_nr = a->argv[5];
+
+ misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0);
+ ast_verbose("Sending CFDeactivate Port:(%d) FromNr. (%s)\n", port, served_nr);
+
+ bc->fac_out.Function = Fac_CFDeactivate;
+ bc->fac_out.u.CFDeactivate.BasicService = 0; //All Services
+ bc->fac_out.u.CFDeactivate.Procedure = 0; //Unconditional
+
+ ast_copy_string((char *)bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber));
+ misdn_lib_send_event(bc, EVENT_FACILITY);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_send_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn send restart";
+ e->usage =
+ "Usage: misdn send restart [port [channel]]\n"
+ " Send a restart for every bchannel on the given port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 4 || a->argc > 5)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 5)
+ misdn_lib_send_restart(atoi(a->argv[3]), atoi(a->argv[4]));
+ else
+ misdn_lib_send_restart(atoi(a->argv[3]), -1);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_send_digit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *channame;
+ char *msg;
+ struct chan_list *tmp;
+ int i, msglen;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn send digit";
+ e->usage =
+ "Usage: misdn send digit <channel> \"<msg>\" \n"
+ " Send <digit> to <channel> as DTMF Tone\n"
+ " when channel is a mISDN channel\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_ch(a);
+ }
+
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+
+ channame = a->argv[3];
+ msg = a->argv[4];
+ msglen = strlen(msg);
+
+ ast_cli(a->fd, "Sending %s to %s\n", msg, channame);
+
+ tmp = get_chan_by_ast_name(channame);
+ if (!tmp) {
+ ast_cli(a->fd, "Sending %s to %s failed Channel does not exist\n", msg, channame);
+ return CLI_SUCCESS;
+ }
+#if 1
+ for (i = 0; i < msglen; i++) {
+ ast_cli(a->fd, "Sending: %c\n", msg[i]);
+ send_digit_to_chan(tmp, msg[i]);
+ /* res = ast_safe_sleep(tmp->ast, 250); */
+ usleep(250000);
+ /* res = ast_waitfor(tmp->ast,100); */
+ }
+#else
+ ast_dtmf_stream(tmp->ast, NULL, msg, 250);
+#endif
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_toggle_echocancel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *channame;
+ struct chan_list *tmp;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn toggle echocancel";
+ e->usage =
+ "Usage: misdn toggle echocancel <channel>\n"
+ " Toggle EchoCancel on mISDN Channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_ch(a);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ channame = a->argv[3];
+
+ ast_cli(a->fd, "Toggling EchoCancel on %s\n", channame);
+
+ tmp = get_chan_by_ast_name(channame);
+ if (!tmp) {
+ ast_cli(a->fd, "Toggling EchoCancel %s failed Channel does not exist\n", channame);
+ return CLI_SUCCESS;
+ }
+
+ tmp->toggle_ec = tmp->toggle_ec?0:1;
+
+ if (tmp->toggle_ec) {
+#ifdef MISDN_1_2
+ update_pipeline_config(tmp->bc);
+#else
+ update_ec_config(tmp->bc);
+#endif
+ manager_ec_enable(tmp->bc);
+ } else {
+ manager_ec_disable(tmp->bc);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_misdn_send_display(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *channame;
+ char *msg;
+ struct chan_list *tmp;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "misdn send display";
+ e->usage =
+ "Usage: misdn send display <channel> \"<msg>\" \n"
+ " Send <msg> to <channel> as Display Message\n"
+ " when channel is a mISDN channel\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_ch(a);
+ }
+
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+
+ channame = a->argv[3];
+ msg = a->argv[4];
+
+ ast_cli(a->fd, "Sending %s to %s\n", msg, channame);
+ tmp = get_chan_by_ast_name(channame);
+
+ if (tmp && tmp->bc) {
+ ast_copy_string(tmp->bc->display, msg, sizeof(tmp->bc->display));
+ misdn_lib_send_event(tmp->bc, EVENT_INFORMATION);
+ } else {
+ ast_cli(a->fd, "No such channel %s\n", channame);
+ return CLI_SUCCESS;
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *complete_ch(struct ast_cli_args *a)
+{
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+}
+
+static char *complete_debug_port (struct ast_cli_args *a)
+{
+ if (a->n)
+ return NULL;
+
+ switch (a->pos) {
+ case 4:
+ if (a->word[0] == 'p')
+ return ast_strdup("port");
+ else if (a->word[0] == 'o')
+ return ast_strdup("only");
+ break;
+ case 6:
+ if (a->word[0] == 'o')
+ return ast_strdup("only");
+ break;
+ }
+ return NULL;
+}
+
+static char *complete_show_config(struct ast_cli_args *a)
+{
+ char buffer[BUFFERSIZE];
+ enum misdn_cfg_elements elem;
+ int wordlen = strlen(a->word);
+ int which = 0;
+ int port = 0;
+
+ switch (a->pos) {
+ case 3:
+ if ((!strncmp(a->word, "description", wordlen)) && (++which > a->n))
+ return ast_strdup("description");
+ if ((!strncmp(a->word, "descriptions", wordlen)) && (++which > a->n))
+ return ast_strdup("descriptions");
+ if ((!strncmp(a->word, "0", wordlen)) && (++which > a->n))
+ return ast_strdup("0");
+ while ((port = misdn_cfg_get_next_port(port)) != -1) {
+ snprintf(buffer, sizeof(buffer), "%d", port);
+ if ((!strncmp(a->word, buffer, wordlen)) && (++which > a->n)) {
+ return ast_strdup(buffer);
+ }
+ }
+ break;
+ case 4:
+ if (strstr(a->line, "description ")) {
+ for (elem = MISDN_CFG_FIRST + 1; elem < MISDN_GEN_LAST; ++elem) {
+ if ((elem == MISDN_CFG_LAST) || (elem == MISDN_GEN_FIRST))
+ continue;
+ misdn_cfg_get_name(elem, buffer, sizeof(buffer));
+ if (!wordlen || !strncmp(a->word, buffer, wordlen)) {
+ if (++which > a->n)
+ return ast_strdup(buffer);
+ }
+ }
+ } else if (strstr(a->line, "descriptions ")) {
+ if ((!wordlen || !strncmp(a->word, "general", wordlen)) && (++which > a->n))
+ return ast_strdup("general");
+ if ((!wordlen || !strncmp(a->word, "ports", wordlen)) && (++which > a->n))
+ return ast_strdup("ports");
+ }
+ break;
+ }
+ return NULL;
+}
+
+static struct ast_cli_entry chan_misdn_clis[] = {
+ AST_CLI_DEFINE(handle_cli_misdn_port_block, "Block the given port"),
+ AST_CLI_DEFINE(handle_cli_misdn_port_down, "Try to deacivate the L1 on the given port"),
+ AST_CLI_DEFINE(handle_cli_misdn_port_unblock, "Unblock the given port"),
+ AST_CLI_DEFINE(handle_cli_misdn_port_up, "Try to establish L1 on the given port"),
+ AST_CLI_DEFINE(handle_cli_misdn_reload, "Reload internal mISDN config, read from the config file"),
+ AST_CLI_DEFINE(handle_cli_misdn_restart_pid, "Restart the given pid"),
+ AST_CLI_DEFINE(handle_cli_misdn_restart_port, "Restart the given port"),
+ AST_CLI_DEFINE(handle_cli_misdn_show_channel, "Show an internal mISDN channel"),
+ AST_CLI_DEFINE(handle_cli_misdn_show_channels, "Show the internal mISDN channel list"),
+ AST_CLI_DEFINE(handle_cli_misdn_show_config, "Show internal mISDN config, read from the config file"),
+ AST_CLI_DEFINE(handle_cli_misdn_show_port, "Show detailed information for given port"),
+ AST_CLI_DEFINE(handle_cli_misdn_show_ports_stats, "Show mISDNs channel's call statistics per port"),
+ AST_CLI_DEFINE(handle_cli_misdn_show_stacks, "Show internal mISDN stack_list"),
+ AST_CLI_DEFINE(handle_cli_misdn_send_facility, "Sends a Facility Message to the mISDN Channel"),
+ AST_CLI_DEFINE(handle_cli_misdn_send_digit, "Send DTMF digit to mISDN Channel"),
+ AST_CLI_DEFINE(handle_cli_misdn_send_display, "Send Text to mISDN Channel"),
+ AST_CLI_DEFINE(handle_cli_misdn_send_restart, "Send a restart for every bchannel on the given port"),
+ AST_CLI_DEFINE(handle_cli_misdn_set_crypt_debug, "Set CryptDebuglevel of chan_misdn, at the moment, level={1,2}"),
+ AST_CLI_DEFINE(handle_cli_misdn_set_debug, "Set Debuglevel of chan_misdn"),
+ AST_CLI_DEFINE(handle_cli_misdn_set_tics, "???"),
+ AST_CLI_DEFINE(handle_cli_misdn_toggle_echocancel, "Toggle EchoCancel on mISDN Channel"),
+};
+
+static int update_config(struct chan_list *ch, int orig)
+{
+ struct ast_channel *ast;
+ struct misdn_bchannel *bc;
+ int port, hdlc = 0;
+ int pres, screen;
+
+ if (!ch) {
+ ast_log(LOG_WARNING, "Cannot configure without chanlist\n");
+ return -1;
+ }
+
+ ast = ch->ast;
+ bc = ch->bc;
+ if (! ast || ! bc) {
+ ast_log(LOG_WARNING, "Cannot configure without ast || bc\n");
+ return -1;
+ }
+
+ port = bc->port;
+
+ chan_misdn_log(7, port, "update_config: Getting Config\n");
+
+ misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(int));
+
+ if (hdlc) {
+ switch (bc->capability) {
+ case INFO_CAPABILITY_DIGITAL_UNRESTRICTED:
+ case INFO_CAPABILITY_DIGITAL_RESTRICTED:
+ chan_misdn_log(1, bc->port, " --> CONF HDLC\n");
+ bc->hdlc = 1;
+ break;
+ }
+ }
+
+
+ misdn_cfg_get(port, MISDN_CFG_PRES, &pres, sizeof(pres));
+ misdn_cfg_get(port, MISDN_CFG_SCREEN, &screen, sizeof(screen));
+ chan_misdn_log(2, port, " --> pres: %d screen: %d\n", pres, screen);
+
+ if ( (pres + screen) < 0 ) {
+
+ chan_misdn_log(2, port, " --> pres: %x\n", ast->cid.cid_pres);
+
+ switch (ast->cid.cid_pres & 0x60) {
+
+ case AST_PRES_RESTRICTED:
+ bc->pres = 1;
+ chan_misdn_log(2, port, " --> PRES: Restricted (0x1)\n");
+ break;
+ case AST_PRES_UNAVAILABLE:
+ bc->pres = 2;
+ chan_misdn_log(2, port, " --> PRES: Unavailable (0x2)\n");
+ break;
+ default:
+ bc->pres = 0;
+ chan_misdn_log(2, port, " --> PRES: Allowed (0x0)\n");
+ }
+
+ switch (ast->cid.cid_pres & 0x3) {
+
+ case AST_PRES_USER_NUMBER_UNSCREENED:
+ bc->screen = 0;
+ chan_misdn_log(2, port, " --> SCREEN: Unscreened (0x0)\n");
+ break;
+ case AST_PRES_USER_NUMBER_PASSED_SCREEN:
+ bc->screen = 1;
+ chan_misdn_log(2, port, " --> SCREEN: Passed Screen (0x1)\n");
+ break;
+ case AST_PRES_USER_NUMBER_FAILED_SCREEN:
+ bc->screen = 2;
+ chan_misdn_log(2, port, " --> SCREEN: Failed Screen (0x2)\n");
+ break;
+ case AST_PRES_NETWORK_NUMBER:
+ bc->screen = 3;
+ chan_misdn_log(2, port, " --> SCREEN: Network Nr. (0x3)\n");
+ break;
+ default:
+ bc->screen = 0;
+ chan_misdn_log(2, port, " --> SCREEN: Unscreened (0x0)\n");
+ }
+ } else {
+ bc->screen = screen;
+ bc->pres = pres;
+ }
+
+ return 0;
+}
+
+
+static void config_jitterbuffer(struct chan_list *ch)
+{
+ struct misdn_bchannel *bc = ch->bc;
+ int len = ch->jb_len, threshold = ch->jb_upper_threshold;
+
+ chan_misdn_log(5, bc->port, "config_jb: Called\n");
+
+ if (! len) {
+ chan_misdn_log(1, bc->port, "config_jb: Deactivating Jitterbuffer\n");
+ bc->nojitter=1;
+ } else {
+ if (len <= 100 || len > 8000) {
+ chan_misdn_log(0, bc->port, "config_jb: Jitterbuffer out of Bounds, setting to 1000\n");
+ len = 1000;
+ }
+
+ if ( threshold > len ) {
+ chan_misdn_log(0, bc->port, "config_jb: Jitterbuffer Threshold > Jitterbuffer setting to Jitterbuffer -1\n");
+ }
+
+ if ( ch->jb) {
+ cb_log(0, bc->port, "config_jb: We've got a Jitterbuffer Already on this port.\n");
+ misdn_jb_destroy(ch->jb);
+ ch->jb = NULL;
+ }
+
+ ch->jb=misdn_jb_init(len, threshold);
+
+ if (!ch->jb )
+ bc->nojitter = 1;
+ }
+}
+
+
+void debug_numplan(int port, int numplan, char *type)
+{
+ switch (numplan) {
+ case NUMPLAN_INTERNATIONAL:
+ chan_misdn_log(2, port, " --> %s: International\n", type);
+ break;
+ case NUMPLAN_NATIONAL:
+ chan_misdn_log(2, port, " --> %s: National\n", type);
+ break;
+ case NUMPLAN_SUBSCRIBER:
+ chan_misdn_log(2, port, " --> %s: Subscriber\n", type);
+ break;
+ case NUMPLAN_UNKNOWN:
+ chan_misdn_log(2, port, " --> %s: Unknown\n", type);
+ break;
+ /* Maybe we should cut off the prefix if present ? */
+ default:
+ chan_misdn_log(0, port, " --> !!!! Wrong dialplan setting, please see the misdn.conf sample file\n ");
+ break;
+ }
+}
+
+
+#ifdef MISDN_1_2
+static int update_pipeline_config(struct misdn_bchannel *bc)
+{
+ int ec;
+
+ misdn_cfg_get(bc->port, MISDN_CFG_PIPELINE, bc->pipeline, sizeof(bc->pipeline));
+
+ if (*bc->pipeline)
+ return 0;
+
+ misdn_cfg_get(bc->port, MISDN_CFG_ECHOCANCEL, &ec, sizeof(ec));
+ if (ec == 1)
+ ast_copy_string(bc->pipeline, "mg2ec", sizeof(bc->pipeline));
+ else if (ec > 1)
+ snprintf(bc->pipeline, sizeof(bc->pipeline), "mg2ec(deftaps=%d)", ec);
+
+ return 0;
+}
+#else
+static int update_ec_config(struct misdn_bchannel *bc)
+{
+ int ec;
+ int port = bc->port;
+
+ misdn_cfg_get(port, MISDN_CFG_ECHOCANCEL, &ec, sizeof(ec));
+
+ if (ec == 1) {
+ bc->ec_enable = 1;
+ } else if (ec > 1) {
+ bc->ec_enable = 1;
+ bc->ec_deftaps = ec;
+ }
+
+ return 0;
+}
+#endif
+
+
+static int read_config(struct chan_list *ch, int orig)
+{
+ struct ast_channel *ast;
+ struct misdn_bchannel *bc;
+ int port, hdlc = 0;
+ char lang[BUFFERSIZE + 1], localmusicclass[BUFFERSIZE + 1], faxdetect[BUFFERSIZE + 1];
+ char buf[256], buf2[256];
+ ast_group_t pg, cg;
+
+ if (!ch) {
+ ast_log(LOG_WARNING, "Cannot configure without chanlist\n");
+ return -1;
+ }
+
+ ast = ch->ast;
+ bc = ch->bc;
+ if (! ast || ! bc) {
+ ast_log(LOG_WARNING, "Cannot configure without ast || bc\n");
+ return -1;
+ }
+
+ port = bc->port;
+ chan_misdn_log(1, port, "read_config: Getting Config\n");
+
+ misdn_cfg_get(port, MISDN_CFG_LANGUAGE, lang, sizeof(lang));
+ ast_string_field_set(ast, language, lang);
+
+ misdn_cfg_get(port, MISDN_CFG_MUSICCLASS, localmusicclass, sizeof(localmusicclass));
+ ast_string_field_set(ast, musicclass, localmusicclass);
+
+ misdn_cfg_get(port, MISDN_CFG_TXGAIN, &bc->txgain, sizeof(bc->txgain));
+ misdn_cfg_get(port, MISDN_CFG_RXGAIN, &bc->rxgain, sizeof(bc->rxgain));
+
+ misdn_cfg_get(port, MISDN_CFG_INCOMING_EARLY_AUDIO, &ch->incoming_early_audio, sizeof(ch->incoming_early_audio));
+
+ misdn_cfg_get(port, MISDN_CFG_SENDDTMF, &bc->send_dtmf, sizeof(bc->send_dtmf));
+
+ misdn_cfg_get( port, MISDN_CFG_ASTDTMF, &ch->ast_dsp, sizeof(int));
+
+ if (ch->ast_dsp) {
+ ch->ignore_dtmf=1;
+ }
+
+ misdn_cfg_get(port, MISDN_CFG_NEED_MORE_INFOS, &bc->need_more_infos, sizeof(bc->need_more_infos));
+ misdn_cfg_get(port, MISDN_CFG_NTTIMEOUT, &ch->nttimeout, sizeof(ch->nttimeout));
+
+ misdn_cfg_get(port, MISDN_CFG_NOAUTORESPOND_ON_SETUP, &ch->noautorespond_on_setup, sizeof(ch->noautorespond_on_setup));
+
+ misdn_cfg_get(port, MISDN_CFG_FAR_ALERTING, &ch->far_alerting, sizeof(ch->far_alerting));
+
+ misdn_cfg_get(port, MISDN_CFG_ALLOWED_BEARERS, &ch->allowed_bearers, sizeof(ch->allowed_bearers));
+
+ misdn_cfg_get(port, MISDN_CFG_FAXDETECT, faxdetect, sizeof(faxdetect));
+
+ misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(hdlc));
+
+ if (hdlc) {
+ switch (bc->capability) {
+ case INFO_CAPABILITY_DIGITAL_UNRESTRICTED:
+ case INFO_CAPABILITY_DIGITAL_RESTRICTED:
+ chan_misdn_log(1, bc->port, " --> CONF HDLC\n");
+ bc->hdlc = 1;
+ break;
+ }
+
+ }
+ /*Initialize new Jitterbuffer*/
+ misdn_cfg_get(port, MISDN_CFG_JITTERBUFFER, &ch->jb_len, sizeof(ch->jb_len));
+ misdn_cfg_get(port, MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, &ch->jb_upper_threshold, sizeof(ch->jb_upper_threshold));
+
+ config_jitterbuffer(ch);
+
+ misdn_cfg_get(bc->port, MISDN_CFG_CONTEXT, ch->context, sizeof(ch->context));
+
+ ast_copy_string(ast->context, ch->context, sizeof(ast->context));
+
+#ifdef MISDN_1_2
+ update_pipeline_config(bc);
+#else
+ update_ec_config(bc);
+#endif
+
+ misdn_cfg_get(bc->port, MISDN_CFG_EARLY_BCONNECT, &bc->early_bconnect, sizeof(bc->early_bconnect));
+
+ misdn_cfg_get(port, MISDN_CFG_PICKUPGROUP, &pg, sizeof(pg));
+ misdn_cfg_get(port, MISDN_CFG_CALLGROUP, &cg, sizeof(cg));
+
+ chan_misdn_log(5, port, " --> * CallGrp:%s PickupGrp:%s\n", ast_print_group(buf, sizeof(buf), cg), ast_print_group(buf2, sizeof(buf2), pg));
+ ast->pickupgroup = pg;
+ ast->callgroup = cg;
+
+ if (orig == ORG_AST) {
+ char callerid[BUFFERSIZE + 1];
+
+ misdn_cfg_get(port, MISDN_CFG_TE_CHOOSE_CHANNEL, &(bc->te_choose_channel), sizeof(bc->te_choose_channel));
+
+ if (strstr(faxdetect, "outgoing") || strstr(faxdetect, "both")) {
+ if (strstr(faxdetect, "nojump"))
+ ch->faxdetect = 2;
+ else
+ ch->faxdetect = 1;
+ }
+
+ misdn_cfg_get(port, MISDN_CFG_CALLERID, callerid, sizeof(callerid));
+ if ( ! ast_strlen_zero(callerid) ) {
+ chan_misdn_log(1, port, " --> * Setting Cid to %s\n", callerid);
+ ast_copy_string(bc->oad, callerid, sizeof(bc->oad));
+ }
+
+ misdn_cfg_get(port, MISDN_CFG_DIALPLAN, &bc->dnumplan, sizeof(bc->dnumplan));
+ misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &bc->onumplan, sizeof(bc->onumplan));
+ misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(bc->cpnnumplan));
+ debug_numplan(port, bc->dnumplan, "TON");
+ debug_numplan(port, bc->onumplan, "LTON");
+ debug_numplan(port, bc->cpnnumplan, "CTON");
+
+ ch->overlap_dial = 0;
+ } else { /** ORIGINATOR MISDN **/
+ char prefix[BUFFERSIZE + 1] = "";
+
+ if (strstr(faxdetect, "incoming") || strstr(faxdetect, "both")) {
+ if (strstr(faxdetect, "nojump"))
+ ch->faxdetect = 2;
+ else
+ ch->faxdetect = 1;
+ }
+
+ misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(bc->cpnnumplan));
+ debug_numplan(port, bc->cpnnumplan, "CTON");
+
+ switch (bc->onumplan) {
+ case NUMPLAN_INTERNATIONAL:
+ misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, sizeof(prefix));
+ break;
+
+ case NUMPLAN_NATIONAL:
+ misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, sizeof(prefix));
+ break;
+ default:
+ break;
+ }
+
+ ast_copy_string(buf, bc->oad, sizeof(buf));
+ snprintf(bc->oad, sizeof(bc->oad), "%s%s", prefix, buf);
+
+ if (!ast_strlen_zero(bc->dad)) {
+ ast_copy_string(bc->orig_dad, bc->dad, sizeof(bc->orig_dad));
+ }
+
+ if ( ast_strlen_zero(bc->dad) && !ast_strlen_zero(bc->keypad)) {
+ ast_copy_string(bc->dad, bc->keypad, sizeof(bc->dad));
+ }
+
+ prefix[0] = 0;
+
+ switch (bc->dnumplan) {
+ case NUMPLAN_INTERNATIONAL:
+ misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, sizeof(prefix));
+ break;
+ case NUMPLAN_NATIONAL:
+ misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, sizeof(prefix));
+ break;
+ default:
+ break;
+ }
+
+ ast_copy_string(buf, bc->dad, sizeof(buf));
+ snprintf(bc->dad, sizeof(bc->dad), "%s%s", prefix, buf);
+
+ if (strcmp(bc->dad, ast->exten)) {
+ ast_copy_string(ast->exten, bc->dad, sizeof(ast->exten));
+ }
+
+ ast_set_callerid(ast, bc->oad, NULL, bc->oad);
+
+ if ( !ast_strlen_zero(bc->rad) ) {
+ if (ast->cid.cid_rdnis)
+ ast_free(ast->cid.cid_rdnis);
+ ast->cid.cid_rdnis = ast_strdup(bc->rad);
+ }
+
+ misdn_cfg_get(bc->port, MISDN_CFG_OVERLAP_DIAL, &ch->overlap_dial, sizeof(ch->overlap_dial));
+ ast_mutex_init(&ch->overlap_tv_lock);
+ } /* ORIG MISDN END */
+
+ ch->overlap_dial_task = -1;
+
+ if (ch->faxdetect || ch->ast_dsp) {
+ misdn_cfg_get(port, MISDN_CFG_FAXDETECT_TIMEOUT, &ch->faxdetect_timeout, sizeof(ch->faxdetect_timeout));
+ if (!ch->dsp)
+ ch->dsp = ast_dsp_new();
+ if (ch->dsp) {
+ if (ch->faxdetect)
+ ast_dsp_set_features(ch->dsp, DSP_FEATURE_DTMF_DETECT | DSP_FEATURE_FAX_DETECT);
+ else
+ ast_dsp_set_features(ch->dsp, DSP_FEATURE_DTMF_DETECT );
+ }
+ if (!ch->trans)
+ ch->trans = ast_translator_build_path(AST_FORMAT_SLINEAR, AST_FORMAT_ALAW);
+ }
+
+ /* AOCD initialization */
+ bc->AOCDtype = Fac_None;
+
+ return 0;
+}
+
+
+/*****************************/
+/*** AST Indications Start ***/
+/*****************************/
+
+static int misdn_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ int port = 0;
+ int r;
+ int exceed;
+ int bridging;
+ struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(ast);
+ struct misdn_bchannel *newbc;
+ char *opts = NULL, *ext, *tokb;
+ char *dest_cp = ast_strdupa(dest);
+
+ ext = strtok_r(dest_cp, "/", &tokb);
+
+ if (ext) {
+ ext = strtok_r(NULL, "/", &tokb);
+ if (ext) {
+ opts = strtok_r(NULL, "/", &tokb);
+ } else {
+ chan_misdn_log(0, 0, "misdn_call: No Extension given!\n");
+ return -1;
+ }
+ }
+
+ if (!ast) {
+ ast_log(LOG_WARNING, " --> ! misdn_call called on ast_channel *ast where ast == NULL\n");
+ return -1;
+ }
+
+ if (((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) || !dest ) {
+ ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name);
+ ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
+ ast_setstate(ast, AST_STATE_DOWN);
+ return -1;
+ }
+
+ if (!ch) {
+ ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name);
+ ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
+ ast_setstate(ast, AST_STATE_DOWN);
+ return -1;
+ }
+
+ newbc = ch->bc;
+
+ if (!newbc) {
+ ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name);
+ ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
+ ast_setstate(ast, AST_STATE_DOWN);
+ return -1;
+ }
+
+ port = newbc->port;
+
+ if ((exceed = add_out_calls(port))) {
+ char tmp[16];
+ snprintf(tmp, sizeof(tmp), "%d", exceed);
+ pbx_builtin_setvar_helper(ast, "MAX_OVERFLOW", tmp);
+ return -1;
+ }
+
+ chan_misdn_log(1, port, "* CALL: %s\n", dest);
+
+ chan_misdn_log(2, port, " --> * dad:%s tech:%s ctx:%s\n", ast->exten, ast->name, ast->context);
+
+ chan_misdn_log(3, port, " --> * adding2newbc ext %s\n", ast->exten);
+ if (ast->exten) {
+ ast_copy_string(ast->exten, ext, sizeof(ast->exten));
+ ast_copy_string(newbc->dad, ext, sizeof(newbc->dad));
+ }
+
+ ast_copy_string(newbc->rad, S_OR(ast->cid.cid_rdnis, ""), sizeof(newbc->rad));
+
+ chan_misdn_log(3, port, " --> * adding2newbc callerid %s\n", ast->cid.cid_num);
+ if (ast_strlen_zero(newbc->oad) && !ast_strlen_zero(ast->cid.cid_num)) {
+ ast_copy_string(newbc->oad, ast->cid.cid_num, sizeof(newbc->oad));
+ }
+
+ newbc->capability = ast->transfercapability;
+ pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(newbc->capability));
+ if ( ast->transfercapability == INFO_CAPABILITY_DIGITAL_UNRESTRICTED) {
+ chan_misdn_log(2, port, " --> * Call with flag Digital\n");
+ }
+
+ /* update screening and presentation */
+ update_config(ch, ORG_AST);
+
+ /* fill in some ies from channel vary*/
+ import_ch(ast, newbc, ch);
+
+ /* Finally The Options Override Everything */
+ if (opts)
+ misdn_set_opt_exec(ast, opts);
+ else
+ chan_misdn_log(2, port, "NO OPTS GIVEN\n");
+
+ /*check for bridging*/
+ misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging));
+ if (bridging && ch->other_ch) {
+#ifdef MISDN_1_2
+ chan_misdn_log(1, port, "Disabling EC (aka Pipeline) on both Sides\n");
+ *ch->bc->pipeline = 0;
+ *ch->other_ch->bc->pipeline = 0;
+#else
+ chan_misdn_log(1, port, "Disabling EC on both Sides\n");
+ ch->bc->ec_enable = 0;
+ ch->other_ch->bc->ec_enable = 0;
+#endif
+ }
+
+ r = misdn_lib_send_event( newbc, EVENT_SETUP );
+
+ /** we should have l3id after sending setup **/
+ ch->l3id = newbc->l3_id;
+
+ if ( r == -ENOCHAN ) {
+ chan_misdn_log(0, port, " --> * Theres no Channel at the moment .. !\n");
+ chan_misdn_log(1, port, " --> * SEND: State Down pid:%d\n", newbc ? newbc->pid : -1);
+ ast->hangupcause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
+ ast_setstate(ast, AST_STATE_DOWN);
+ return -1;
+ }
+
+ chan_misdn_log(2, port, " --> * SEND: State Dialing pid:%d\n", newbc ? newbc->pid : 1);
+
+ ast_setstate(ast, AST_STATE_DIALING);
+ ast->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+
+ if (newbc->nt)
+ stop_bc_tones(ch);
+
+ ch->state = MISDN_CALLING;
+
+ return 0;
+}
+
+
+static int misdn_answer(struct ast_channel *ast)
+{
+ struct chan_list *p;
+ const char *tmp;
+
+ if (!ast || ! (p = MISDN_ASTERISK_TECH_PVT(ast)) ) return -1;
+
+ chan_misdn_log(1, p ? (p->bc ? p->bc->port : 0) : 0, "* ANSWER:\n");
+
+ if (!p) {
+ ast_log(LOG_WARNING, " --> Channel not connected ??\n");
+ ast_queue_hangup(ast);
+ }
+
+ if (!p->bc) {
+ chan_misdn_log(1, 0, " --> Got Answer, but theres no bc obj ??\n");
+
+ ast_queue_hangup(ast);
+ }
+
+ tmp = pbx_builtin_getvar_helper(p->ast, "CRYPT_KEY");
+
+ if (!ast_strlen_zero(tmp)) {
+ chan_misdn_log(1, p->bc->port, " --> Connection will be BF crypted\n");
+ ast_copy_string(p->bc->crypt_key, tmp, sizeof(p->bc->crypt_key));
+ } else {
+ chan_misdn_log(3, p->bc->port, " --> Connection is without BF encryption\n");
+ }
+
+ tmp = pbx_builtin_getvar_helper(ast, "MISDN_DIGITAL_TRANS");
+ if (!ast_strlen_zero(tmp) && ast_true(tmp)) {
+ chan_misdn_log(1, p->bc->port, " --> Connection is transparent digital\n");
+ p->bc->nodsp = 1;
+ p->bc->hdlc = 0;
+ p->bc->nojitter = 1;
+ }
+
+ p->state = MISDN_CONNECTED;
+ stop_indicate(p);
+
+ if ( ast_strlen_zero(p->bc->cad) ) {
+ chan_misdn_log(2,p->bc->port," --> empty cad using dad\n");
+ ast_copy_string(p->bc->cad, p->bc->dad, sizeof(p->bc->cad));
+ }
+
+ misdn_lib_send_event( p->bc, EVENT_CONNECT);
+ start_bc_tones(p);
+
+ return 0;
+}
+
+static int misdn_digit_begin(struct ast_channel *chan, char digit)
+{
+ /* XXX Modify this callback to support Asterisk controlling the length of DTMF */
+ return 0;
+}
+
+static int misdn_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct chan_list *p;
+ struct misdn_bchannel *bc;
+ char buf[2] = { digit, 0 };
+
+ if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast))) return -1;
+
+ bc = p->bc;
+ chan_misdn_log(1, bc ? bc->port : 0, "* IND : Digit %c\n", digit);
+
+ if (!bc) {
+ ast_log(LOG_WARNING, " --> !! Got Digit Event withut having bchannel Object\n");
+ return -1;
+ }
+
+ switch (p->state ) {
+ case MISDN_CALLING:
+ if (strlen(bc->infos_pending) < sizeof(bc->infos_pending) - 1)
+ strncat(bc->infos_pending, buf, sizeof(bc->infos_pending) - 1);
+ break;
+ case MISDN_CALLING_ACKNOWLEDGE:
+ ast_copy_string(bc->info_dad, buf, sizeof(bc->info_dad));
+ if (strlen(bc->dad) < sizeof(bc->dad) - 1)
+ strncat(bc->dad, buf, sizeof(bc->dad) - 1);
+ ast_copy_string(p->ast->exten, bc->dad, sizeof(p->ast->exten));
+ misdn_lib_send_event( bc, EVENT_INFORMATION);
+ break;
+ default:
+ /* Do not send Digits in CONNECTED State, when
+ * the other side is too mISDN. */
+ if (p->other_ch )
+ return 0;
+
+ if ( bc->send_dtmf )
+ send_digit_to_chan(p,digit);
+ break;
+}
+
+ return 0;
+}
+
+
+static int misdn_fixup(struct ast_channel *oldast, struct ast_channel *ast)
+{
+ struct chan_list *p;
+
+ if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast) )) return -1;
+
+ chan_misdn_log(1, p->bc ? p->bc->port : 0, "* IND: Got Fixup State:%s L3id:%x\n", misdn_get_ch_state(p), p->l3id);
+
+ p->ast = ast;
+
+ return 0;
+}
+
+
+
+static int misdn_indication(struct ast_channel *ast, int cond, const void *data, size_t datalen)
+{
+ struct chan_list *p;
+
+ if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast))) {
+ ast_log(LOG_WARNING, "Returned -1 in misdn_indication\n");
+ return -1;
+ }
+
+ if (!p->bc ) {
+ chan_misdn_log(1, 0, "* IND : Indication from %s\n", ast->exten);
+ ast_log(LOG_WARNING, "Private Pointer but no bc ?\n");
+ return -1;
+ }
+
+ chan_misdn_log(5, p->bc->port, "* IND : Indication [%d] from %s\n", cond, ast->exten);
+
+ switch (cond) {
+ case AST_CONTROL_BUSY:
+ chan_misdn_log(1, p->bc->port, "* IND :\tbusy pid:%d\n", p->bc ? p->bc->pid : -1);
+ ast_setstate(ast, AST_STATE_BUSY);
+
+ p->bc->out_cause = AST_CAUSE_USER_BUSY;
+ if (p->state != MISDN_CONNECTED) {
+ start_bc_tones(p);
+ misdn_lib_send_event( p->bc, EVENT_DISCONNECT);
+ } else {
+ chan_misdn_log(-1, p->bc->port, " --> !! Got Busy in Connected State !?! ast:%s\n", ast->name);
+ }
+ return -1;
+ case AST_CONTROL_RING:
+ chan_misdn_log(1, p->bc->port, "* IND :\tring pid:%d\n", p->bc ? p->bc->pid : -1);
+ return -1;
+ case AST_CONTROL_RINGING:
+ chan_misdn_log(1, p->bc->port, "* IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1);
+ switch (p->state) {
+ case MISDN_ALERTING:
+ chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but I was Ringing before, so ignoreing it\n", p->bc ? p->bc->pid : -1);
+ break;
+ case MISDN_CONNECTED:
+ chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but Connected, so just send TONE_ALERTING without state changes \n", p->bc ? p->bc->pid : -1);
+ return -1;
+ default:
+ p->state = MISDN_ALERTING;
+ chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1);
+ misdn_lib_send_event( p->bc, EVENT_ALERTING);
+
+ if (p->other_ch && p->other_ch->bc) {
+ if (misdn_inband_avail(p->other_ch->bc)) {
+ chan_misdn_log(2, p->bc->port, " --> other End is mISDN and has inband info available\n");
+ break;
+ }
+
+ if (!p->other_ch->bc->nt) {
+ chan_misdn_log(2, p->bc->port, " --> other End is mISDN TE so it has inband info for sure (?)\n");
+ break;
+ }
+ }
+
+ chan_misdn_log(3, p->bc->port, " --> * SEND: State Ring pid:%d\n", p->bc ? p->bc->pid : -1);
+ ast_setstate(ast, AST_STATE_RINGING);
+
+ if ( !p->bc->nt && (p->originator == ORG_MISDN) && !p->incoming_early_audio )
+ chan_misdn_log(2, p->bc->port, " --> incoming_early_audio off\n");
+ else
+ return -1;
+ }
+ break;
+ case AST_CONTROL_ANSWER:
+ chan_misdn_log(1, p->bc->port, " --> * IND :\tanswer pid:%d\n", p->bc ? p->bc->pid : -1);
+ start_bc_tones(p);
+ break;
+ case AST_CONTROL_TAKEOFFHOOK:
+ chan_misdn_log(1, p->bc->port, " --> *\ttakeoffhook pid:%d\n", p->bc ? p->bc->pid : -1);
+ return -1;
+ case AST_CONTROL_OFFHOOK:
+ chan_misdn_log(1, p->bc->port, " --> *\toffhook pid:%d\n", p->bc ? p->bc->pid : -1);
+ return -1;
+ case AST_CONTROL_FLASH:
+ chan_misdn_log(1, p->bc->port, " --> *\tflash pid:%d\n", p->bc ? p->bc->pid : -1);
+ break;
+ case AST_CONTROL_PROGRESS:
+ chan_misdn_log(1, p->bc->port, " --> * IND :\tprogress pid:%d\n", p->bc ? p->bc->pid : -1);
+ misdn_lib_send_event( p->bc, EVENT_PROGRESS);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ chan_misdn_log(1, p->bc->port, " --> * IND :\tproceeding pid:%d\n", p->bc ? p->bc->pid : -1);
+ misdn_lib_send_event( p->bc, EVENT_PROCEEDING);
+ break;
+ case AST_CONTROL_CONGESTION:
+ chan_misdn_log(1, p->bc->port, " --> * IND :\tcongestion pid:%d\n", p->bc ? p->bc->pid : -1);
+
+ p->bc->out_cause = AST_CAUSE_SWITCH_CONGESTION;
+ start_bc_tones(p);
+ misdn_lib_send_event( p->bc, EVENT_DISCONNECT);
+
+ if (p->bc->nt) {
+ hanguptone_indicate(p);
+ }
+ break;
+ case -1 :
+ chan_misdn_log(1, p->bc->port, " --> * IND :\t-1! (stop indication) pid:%d\n", p->bc ? p->bc->pid : -1);
+
+ stop_indicate(p);
+
+ if (p->state == MISDN_CONNECTED)
+ start_bc_tones(p);
+ break;
+ case AST_CONTROL_HOLD:
+ chan_misdn_log(1, p->bc->port, " --> *\tHOLD pid:%d\n", p->bc ? p->bc->pid : -1);
+ break;
+ case AST_CONTROL_UNHOLD:
+ chan_misdn_log(1, p->bc->port, " --> *\tUNHOLD pid:%d\n", p->bc ? p->bc->pid : -1);
+ break;
+ default:
+ chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc ? p->bc->pid : -1);
+ }
+
+ return 0;
+}
+
+static int misdn_hangup(struct ast_channel *ast)
+{
+ struct chan_list *p;
+ struct misdn_bchannel *bc = NULL;
+ const char *varcause = NULL;
+
+ ast_debug(1, "misdn_hangup(%s)\n", ast->name);
+
+ if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast) ) ) return -1;
+
+ if (!p) {
+ chan_misdn_log(3, 0, "misdn_hangup called, without chan_list obj.\n");
+ return 0 ;
+ }
+
+ bc = p->bc;
+
+ if (bc) {
+ const char *tmp=pbx_builtin_getvar_helper(ast,"MISDN_USERUSER");
+ if (tmp) {
+ ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", tmp);
+ strcpy(bc->uu, tmp);
+ bc->uulen=strlen(bc->uu);
+ }
+ }
+
+ MISDN_ASTERISK_TECH_PVT(ast) = NULL;
+ p->ast = NULL;
+
+ if (ast->_state == AST_STATE_RESERVED ||
+ p->state == MISDN_NOTHING ||
+ p->state == MISDN_HOLDED ||
+ p->state == MISDN_HOLD_DISCONNECT ) {
+
+ CLEAN_CH:
+ /* between request and call */
+ ast_debug(1, "State Reserved (or nothing) => chanIsAvail\n");
+ MISDN_ASTERISK_TECH_PVT(ast) = NULL;
+
+ ast_mutex_lock(&release_lock);
+ cl_dequeue_chan(&cl_te, p);
+ close(p->pipe[0]);
+ close(p->pipe[1]);
+ ast_free(p);
+ ast_mutex_unlock(&release_lock);
+
+ if (bc)
+ misdn_lib_release(bc);
+
+ return 0;
+ }
+
+ if (!bc) {
+ ast_log(LOG_WARNING, "Hangup with private but no bc ? state:%s l3id:%x\n", misdn_get_ch_state(p), p->l3id);
+ goto CLEAN_CH;
+ }
+
+
+ p->need_hangup = 0;
+ p->need_queue_hangup = 0;
+ p->need_busy = 0;
+
+
+ if (!p->bc->nt)
+ stop_bc_tones(p);
+
+ bc->out_cause = ast->hangupcause ? ast->hangupcause : AST_CAUSE_NORMAL_CLEARING;
+
+ if ( (varcause = pbx_builtin_getvar_helper(ast, "HANGUPCAUSE")) ||
+ (varcause = pbx_builtin_getvar_helper(ast, "PRI_CAUSE"))) {
+ int tmpcause = atoi(varcause);
+ bc->out_cause = tmpcause ? tmpcause : AST_CAUSE_NORMAL_CLEARING;
+ }
+
+ chan_misdn_log(1, bc->port, "* IND : HANGUP\tpid:%d ctx:%s dad:%s oad:%s State:%s\n", p->bc ? p->bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(p));
+ chan_misdn_log(3, bc->port, " --> l3id:%x\n", p->l3id);
+ chan_misdn_log(3, bc->port, " --> cause:%d\n", bc->cause);
+ chan_misdn_log(2, bc->port, " --> out_cause:%d\n", bc->out_cause);
+ chan_misdn_log(2, bc->port, " --> state:%s\n", misdn_get_ch_state(p));
+
+ switch (p->state) {
+ case MISDN_INCOMING_SETUP:
+ case MISDN_CALLING:
+ p->state = MISDN_CLEANING;
+ /* This is the only place in misdn_hangup, where we
+ * can call release_chan, else it might create lot's of trouble
+ * */
+ ast_log(LOG_NOTICE, "release channel, in CALLING/INCOMING_SETUP state.. no other events happened\n");
+ release_chan(bc);
+ misdn_lib_send_event( bc, EVENT_RELEASE_COMPLETE);
+ break;
+ case MISDN_HOLDED:
+ case MISDN_DIALING:
+ start_bc_tones(p);
+ hanguptone_indicate(p);
+
+ p->state=MISDN_CLEANING;
+ if (bc->need_disconnect)
+ misdn_lib_send_event( bc, EVENT_DISCONNECT);
+ break;
+ case MISDN_CALLING_ACKNOWLEDGE:
+ start_bc_tones(p);
+ hanguptone_indicate(p);
+
+ if (bc->need_disconnect)
+ misdn_lib_send_event( bc, EVENT_DISCONNECT);
+ break;
+
+ case MISDN_ALERTING:
+ case MISDN_PROGRESS:
+ case MISDN_PROCEEDING:
+ if (p->originator != ORG_AST)
+ hanguptone_indicate(p);
+
+ /*p->state=MISDN_CLEANING;*/
+ if (bc->need_disconnect)
+ misdn_lib_send_event( bc, EVENT_DISCONNECT);
+ break;
+ case MISDN_CONNECTED:
+ case MISDN_PRECONNECTED:
+ /* Alerting or Disconect */
+ if (p->bc->nt) {
+ start_bc_tones(p);
+ hanguptone_indicate(p);
+ p->bc->progress_indicator = 8;
+ }
+ if (bc->need_disconnect)
+ misdn_lib_send_event( bc, EVENT_DISCONNECT);
+
+ /*p->state=MISDN_CLEANING;*/
+ break;
+ case MISDN_DISCONNECTED:
+ if (bc->need_release)
+ misdn_lib_send_event( bc, EVENT_RELEASE);
+ p->state = MISDN_CLEANING; /* MISDN_HUNGUP_FROM_AST; */
+ break;
+
+ case MISDN_RELEASED:
+ case MISDN_CLEANING:
+ p->state = MISDN_CLEANING;
+ break;
+
+ case MISDN_BUSY:
+ break;
+
+ case MISDN_HOLD_DISCONNECT:
+ /* need to send release here */
+ chan_misdn_log(1, bc->port, " --> cause %d\n", bc->cause);
+ chan_misdn_log(1, bc->port, " --> out_cause %d\n", bc->out_cause);
+
+ bc->out_cause = -1;
+ if (bc->need_release)
+ misdn_lib_send_event(bc, EVENT_RELEASE);
+ p->state = MISDN_CLEANING;
+ break;
+ default:
+ if (bc->nt) {
+ bc->out_cause = -1;
+ if (bc->need_release)
+ misdn_lib_send_event(bc, EVENT_RELEASE);
+ p->state = MISDN_CLEANING;
+ } else {
+ if (bc->need_disconnect)
+ misdn_lib_send_event(bc, EVENT_DISCONNECT);
+ }
+ }
+
+ p->state = MISDN_CLEANING;
+
+ chan_misdn_log(3, bc->port, " --> Channel: %s hanguped new state:%s\n", ast->name, misdn_get_ch_state(p));
+
+ return 0;
+}
+
+
+static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame)
+{
+ struct ast_frame *f,*f2;
+
+ if (tmp->trans) {
+ f2 = ast_translate(tmp->trans, frame, 0);
+ f = ast_dsp_process(tmp->ast, tmp->dsp, f2);
+ } else {
+ chan_misdn_log(0, tmp->bc->port, "No T-Path found\n");
+ return NULL;
+ }
+
+ if (!f || (f->frametype != AST_FRAME_DTMF))
+ return frame;
+
+ ast_debug(1, "Detected inband DTMF digit: %c\n", f->subclass);
+
+ if (tmp->faxdetect && (f->subclass == 'f')) {
+ /* Fax tone -- Handle and return NULL */
+ if (!tmp->faxhandled) {
+ struct ast_channel *ast = tmp->ast;
+ tmp->faxhandled++;
+ chan_misdn_log(0, tmp->bc->port, "Fax detected, preparing %s for fax transfer.\n", ast->name);
+ tmp->bc->rxgain = 0;
+ isdn_lib_update_rxgain(tmp->bc);
+ tmp->bc->txgain = 0;
+ isdn_lib_update_txgain(tmp->bc);
+#ifdef MISDN_1_2
+ *tmp->bc->pipeline = 0;
+#else
+ tmp->bc->ec_enable = 0;
+#endif
+ isdn_lib_update_ec(tmp->bc);
+ isdn_lib_stop_dtmf(tmp->bc);
+ switch (tmp->faxdetect) {
+ case 1:
+ if (strcmp(ast->exten, "fax")) {
+ char *context;
+ char context_tmp[BUFFERSIZE];
+ misdn_cfg_get(tmp->bc->port, MISDN_CFG_FAXDETECT_CONTEXT, &context_tmp, sizeof(context_tmp));
+ context = ast_strlen_zero(context_tmp) ? (ast_strlen_zero(ast->macrocontext) ? ast->context : ast->macrocontext) : context_tmp;
+ if (ast_exists_extension(ast, context, "fax", 1, ast->cid.cid_num)) {
+ ast_verb(3, "Redirecting %s to fax extension (context:%s)\n", ast->name, context);
+ /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
+ pbx_builtin_setvar_helper(ast,"FAXEXTEN",ast->exten);
+ if (ast_async_goto(ast, context, "fax", 1))
+ ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, context);
+ } else
+ ast_log(LOG_NOTICE, "Fax detected, but no fax extension ctx:%s exten:%s\n", context, ast->exten);
+ } else {
+ ast_debug(1, "Already in a fax extension, not redirecting\n");
+ }
+ break;
+ case 2:
+ ast_verb(3, "Not redirecting %s to fax extension, nojump is set.\n", ast->name);
+ break;
+ }
+ } else {
+ ast_debug(1, "Fax already handled\n");
+ }
+ }
+
+ if (tmp->ast_dsp && (f->subclass != 'f')) {
+ chan_misdn_log(2, tmp->bc->port, " --> * SEND: DTMF (AST_DSP) :%c\n", f->subclass);
+ }
+
+ return f;
+}
+
+
+static struct ast_frame *misdn_read(struct ast_channel *ast)
+{
+ struct chan_list *tmp;
+ fd_set rrfs;
+ struct timeval tv;
+ int len, t;
+
+ if (!ast) {
+ chan_misdn_log(1, 0, "misdn_read called without ast\n");
+ return NULL;
+ }
+ if (!(tmp = MISDN_ASTERISK_TECH_PVT(ast))) {
+ chan_misdn_log(1, 0, "misdn_read called without ast->pvt\n");
+ return NULL;
+ }
+
+ if (!tmp->bc && !(tmp->state == MISDN_HOLDED)) {
+ chan_misdn_log(1, 0, "misdn_read called without bc\n");
+ return NULL;
+ }
+
+ tv.tv_sec=0;
+ tv.tv_usec=20000;
+
+ FD_ZERO(&rrfs);
+ FD_SET(tmp->pipe[0],&rrfs);
+
+ t=select(FD_SETSIZE,&rrfs,NULL, NULL,&tv);
+
+ if (!t) {
+ chan_misdn_log(3, tmp->bc->port, "read Select Timed out\n");
+ len=160;
+ }
+
+ if (t<0) {
+ chan_misdn_log(-1, tmp->bc->port, "Select Error (err=%s)\n",strerror(errno));
+ return NULL;
+ }
+
+ if (FD_ISSET(tmp->pipe[0],&rrfs)) {
+ len=read(tmp->pipe[0],tmp->ast_rd_buf,sizeof(tmp->ast_rd_buf));
+
+ if (len<=0) {
+ /* we hangup here, since our pipe is closed */
+ chan_misdn_log(2,tmp->bc->port,"misdn_read: Pipe closed, hanging up\n");
+ return NULL;
+ }
+
+ } else {
+ return NULL;
+ }
+
+ tmp->frame.frametype = AST_FRAME_VOICE;
+ tmp->frame.subclass = AST_FORMAT_ALAW;
+ tmp->frame.datalen = len;
+ tmp->frame.samples = len;
+ tmp->frame.mallocd = 0;
+ tmp->frame.offset = 0;
+ tmp->frame.delivery = ast_tv(0,0);
+ tmp->frame.src = NULL;
+ tmp->frame.data = tmp->ast_rd_buf;
+
+ if (tmp->faxdetect && !tmp->faxhandled) {
+ if (tmp->faxdetect_timeout) {
+ if (ast_tvzero(tmp->faxdetect_tv)) {
+ tmp->faxdetect_tv = ast_tvnow();
+ chan_misdn_log(2, tmp->bc->port, "faxdetect: starting detection with timeout: %ds ...\n", tmp->faxdetect_timeout);
+ return process_ast_dsp(tmp, &tmp->frame);
+ } else {
+ struct timeval tv_now = ast_tvnow();
+ int diff = ast_tvdiff_ms(tv_now, tmp->faxdetect_tv);
+ if (diff <= (tmp->faxdetect_timeout * 1000)) {
+ chan_misdn_log(5, tmp->bc->port, "faxdetect: detecting ...\n");
+ return process_ast_dsp(tmp, &tmp->frame);
+ } else {
+ chan_misdn_log(2, tmp->bc->port, "faxdetect: stopping detection (time ran out) ...\n");
+ tmp->faxdetect = 0;
+ return &tmp->frame;
+ }
+ }
+ } else {
+ chan_misdn_log(5, tmp->bc->port, "faxdetect: detecting ... (no timeout)\n");
+ return process_ast_dsp(tmp, &tmp->frame);
+ }
+ } else {
+ if (tmp->ast_dsp)
+ return process_ast_dsp(tmp, &tmp->frame);
+ else
+ return &tmp->frame;
+ }
+}
+
+
+static int misdn_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct chan_list *ch;
+ int i = 0;
+
+ if (!ast || ! (ch = MISDN_ASTERISK_TECH_PVT(ast)) ) return -1;
+
+ if (ch->state == MISDN_HOLDED) {
+ chan_misdn_log(7, 0, "misdn_write: Returning because holded\n");
+ return 0;
+ }
+
+ if (!ch->bc ) {
+ ast_log(LOG_WARNING, "private but no bc\n");
+ return -1;
+ }
+
+ if (ch->notxtone) {
+ chan_misdn_log(7, ch->bc->port, "misdn_write: Returning because notxone\n");
+ return 0;
+ }
+
+
+ if (!frame->subclass) {
+ chan_misdn_log(4, ch->bc->port, "misdn_write: * prods us\n");
+ return 0;
+ }
+
+ if (!(frame->subclass & prefformat)) {
+
+ chan_misdn_log(-1, ch->bc->port, "Got Unsupported Frame with Format:%d\n", frame->subclass);
+ return 0;
+ }
+
+
+ if (!frame->samples ) {
+ chan_misdn_log(4, ch->bc->port, "misdn_write: zero write\n");
+
+ if (!strcmp(frame->src,"ast_prod")) {
+ chan_misdn_log(1, ch->bc->port, "misdn_write: state (%s) prodded.\n", misdn_get_ch_state(ch));
+
+ if (ch->ts) {
+ chan_misdn_log(4, ch->bc->port, "Starting Playtones\n");
+ misdn_lib_tone_generator_start(ch->bc);
+ }
+ return 0;
+ }
+
+ return -1;
+ }
+
+ if ( ! ch->bc->addr ) {
+ chan_misdn_log(8, ch->bc->port, "misdn_write: no addr for bc dropping:%d\n", frame->samples);
+ return 0;
+ }
+
+#ifdef MISDN_DEBUG
+ {
+ int i, max = 5 > frame->samples ? frame->samples : 5;
+
+ ast_debug(1, "write2mISDN %p %d bytes: ", p, frame->samples);
+
+ for (i = 0; i < max ; i++)
+ ast_debug(1, "%2.2x ", ((char*) frame->data)[i]);
+ }
+#endif
+
+ switch (ch->bc->bc_state) {
+ case BCHAN_ACTIVATED:
+ case BCHAN_BRIDGED:
+ break;
+ default:
+ if (!ch->dropped_frame_cnt)
+ chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) droping: %d frames addr:%x exten:%s cid:%s ch->state:%s bc_state:%d l3id:%x\n", frame->samples, ch->bc->addr, ast->exten, ast->cid.cid_num, misdn_get_ch_state( ch), ch->bc->bc_state, ch->bc->l3_id);
+
+ ch->dropped_frame_cnt++;
+ if (ch->dropped_frame_cnt > 100) {
+ ch->dropped_frame_cnt = 0;
+ chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) droping: %d frames addr:%x dropped > 100 frames!\n", frame->samples, ch->bc->addr);
+
+ }
+
+ return 0;
+ }
+
+ chan_misdn_log(9, ch->bc->port, "Sending :%d bytes 2 MISDN\n", frame->samples);
+ if ( !ch->bc->nojitter && misdn_cap_is_speech(ch->bc->capability) ) {
+ /* Buffered Transmit (triggert by read from isdn side)*/
+ if (misdn_jb_fill(ch->jb, frame->data, frame->samples) < 0) {
+ if (ch->bc->active)
+ cb_log(0, ch->bc->port, "Misdn Jitterbuffer Overflow.\n");
+ }
+
+ } else {
+ /*transmit without jitterbuffer*/
+ i=misdn_lib_tx2misdn_frm(ch->bc, frame->data, frame->samples);
+ }
+
+ return 0;
+}
+
+
+
+
+static enum ast_bridge_result misdn_bridge (struct ast_channel *c0,
+ struct ast_channel *c1, int flags,
+ struct ast_frame **fo,
+ struct ast_channel **rc,
+ int timeoutms)
+
+{
+ struct chan_list *ch1, *ch2;
+ struct ast_channel *carr[2], *who;
+ int to = -1;
+ struct ast_frame *f;
+ int p1_b, p2_b;
+ int bridging;
+
+ ch1 = get_chan_by_ast(c0);
+ ch2 = get_chan_by_ast(c1);
+
+ carr[0] = c0;
+ carr[1] = c1;
+
+ if (!(ch1 && ch2))
+ return -1;
+
+ misdn_cfg_get(ch1->bc->port, MISDN_CFG_BRIDGING, &p1_b, sizeof(p1_b));
+ misdn_cfg_get(ch2->bc->port, MISDN_CFG_BRIDGING, &p2_b, sizeof(p2_b));
+
+ if (! p1_b || ! p2_b) {
+ ast_log(LOG_NOTICE, "Falling back to Asterisk bridging\n");
+ return AST_BRIDGE_FAILED;
+ }
+
+ misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging));
+ if (bridging) {
+ /* trying to make a mISDN_dsp conference */
+ chan_misdn_log(1, ch1->bc->port, "I SEND: Making conference with Number:%d\n", ch1->bc->pid + 1);
+ misdn_lib_bridge(ch1->bc, ch2->bc);
+ }
+
+ ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name);
+
+ chan_misdn_log(1, ch1->bc->port, "* Making Native Bridge between %s and %s\n", ch1->bc->oad, ch2->bc->oad);
+
+ if (! (flags & AST_BRIDGE_DTMF_CHANNEL_0) )
+ ch1->ignore_dtmf = 1;
+
+ if (! (flags & AST_BRIDGE_DTMF_CHANNEL_1) )
+ ch2->ignore_dtmf = 1;
+
+ for (;/*ever*/;) {
+ to = -1;
+ who = ast_waitfor_n(carr, 2, &to);
+
+ if (!who) {
+ ast_log(LOG_NOTICE, "misdn_bridge: empty read, breaking out\n");
+ break;
+ }
+ f = ast_read(who);
+
+ if (!f || f->frametype == AST_FRAME_CONTROL) {
+ /* got hangup .. */
+
+ if (!f)
+ chan_misdn_log(4, ch1->bc->port, "Read Null Frame\n");
+ else
+ chan_misdn_log(4, ch1->bc->port, "Read Frame Controll class:%d\n", f->subclass);
+
+ *fo = f;
+ *rc = who;
+
+ break;
+ }
+
+ if ( f->frametype == AST_FRAME_DTMF ) {
+ chan_misdn_log(1, 0, "Read DTMF %d from %s\n", f->subclass, who->exten);
+
+ *fo = f;
+ *rc = who;
+ break;
+ }
+
+#if 0
+ if (f->frametype == AST_FRAME_VOICE) {
+ chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid +1);
+
+ continue;
+ }
+#endif
+
+ if (who == c0) {
+ ast_write(c1, f);
+ }
+ else {
+ ast_write(c0, f);
+ }
+ }
+
+ chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid + 1);
+
+ misdn_lib_split_bridge(ch1->bc, ch2->bc);
+
+ return AST_BRIDGE_COMPLETE;
+}
+
+/** AST INDICATIONS END **/
+
+static int dialtone_indicate(struct chan_list *cl)
+{
+ const struct ind_tone_zone_sound *ts = NULL;
+ struct ast_channel *ast = cl->ast;
+ int nd = 0;
+
+ if (!ast) {
+ chan_misdn_log(0, cl->bc->port, "No Ast in dialtone_indicate\n");
+ return -1;
+ }
+
+ misdn_cfg_get(cl->bc->port, MISDN_CFG_NODIALTONE, &nd, sizeof(nd));
+
+ if (nd) {
+ chan_misdn_log(1, cl->bc->port, "Not sending Dialtone, because config wants it\n");
+ return 0;
+ }
+
+ chan_misdn_log(3, cl->bc->port, " --> Dial\n");
+ ts = ast_get_indication_tone(ast->zone, "dial");
+ cl->ts = ts;
+
+ if (ts) {
+ cl->notxtone = 0;
+ cl->norxtone = 0;
+ /* This prods us in misdn_write */
+ ast_playtones_start(ast, 0, ts->data, 0);
+ }
+
+ return 0;
+}
+
+static int hanguptone_indicate(struct chan_list *cl)
+{
+ misdn_lib_send_tone(cl->bc, TONE_HANGUP);
+ return 0;
+}
+
+static int stop_indicate(struct chan_list *cl)
+{
+ struct ast_channel *ast = cl->ast;
+
+ if (!ast) {
+ chan_misdn_log(0, cl->bc->port, "No Ast in stop_indicate\n");
+ return -1;
+ }
+
+ chan_misdn_log(3, cl->bc->port, " --> None\n");
+ misdn_lib_tone_generator_stop(cl->bc);
+ ast_playtones_stop(ast);
+
+ cl->ts = NULL;
+ /*ast_deactivate_generator(ast);*/
+
+ return 0;
+}
+
+
+static int start_bc_tones(struct chan_list* cl)
+{
+ misdn_lib_tone_generator_stop(cl->bc);
+ cl->notxtone = 0;
+ cl->norxtone = 0;
+ return 0;
+}
+
+static int stop_bc_tones(struct chan_list *cl)
+{
+ if (!cl) return -1;
+
+ cl->notxtone = 1;
+ cl->norxtone = 1;
+
+ return 0;
+}
+
+
+static struct chan_list *init_chan_list(int orig)
+{
+ struct chan_list *cl;
+
+ cl = ast_calloc(1, sizeof(*cl));
+
+ if (!cl) {
+ chan_misdn_log(-1, 0, "misdn_request: malloc failed!");
+ return NULL;
+ }
+
+ cl->originator = orig;
+ cl->need_queue_hangup = 1;
+ cl->need_hangup = 1;
+ cl->need_busy = 1;
+ cl->overlap_dial_task = -1;
+
+ return cl;
+}
+
+static struct ast_channel *misdn_request(const char *type, int format, void *data, int *cause)
+{
+ struct ast_channel *tmp = NULL;
+ char group[BUFFERSIZE + 1] = "";
+ char buf[128];
+ char *buf2 = ast_strdupa(data), *ext = NULL, *port_str;
+ char *tokb = NULL, *p = NULL;
+ int channel = 0, port = 0;
+ struct misdn_bchannel *newbc = NULL;
+ int dec = 0;
+
+ struct chan_list *cl = init_chan_list(ORG_AST);
+
+ snprintf(buf, sizeof(buf), "%s/%s", misdn_type, (char*)data);
+
+ port_str = strtok_r(buf2, "/", &tokb);
+
+ ext = strtok_r(NULL, "/", &tokb);
+
+ if (port_str) {
+ if (port_str[0] == 'g' && port_str[1] == ':' ) {
+ /* We make a group call lets checkout which ports are in my group */
+ port_str += 2;
+ ast_copy_string(group, port_str, sizeof(group));
+ chan_misdn_log(2, 0, " --> Group Call group: %s\n", group);
+ } else if ((p = strchr(port_str, ':'))) {
+ /* we have a preselected channel */
+ *p = 0;
+ channel = atoi(++p);
+ port = atoi(port_str);
+ chan_misdn_log(2, port, " --> Call on preselected Channel (%d).\n", channel);
+ } else {
+ port = atoi(port_str);
+ }
+ } else {
+ ast_log(LOG_WARNING, " --> ! IND : CALL dad:%s WITHOUT PORT/Group, check extensions.conf\n", ext);
+ return NULL;
+ }
+
+ if (misdn_cfg_is_group_method(group, METHOD_STANDARD_DEC)) {
+ chan_misdn_log(4, port, " --> STARTING STANDARDDEC...\n");
+ dec = 1;
+ }
+
+ if (!ast_strlen_zero(group)) {
+ char cfg_group[BUFFERSIZE + 1];
+ struct robin_list *rr = NULL;
+
+ if (misdn_cfg_is_group_method(group, METHOD_ROUND_ROBIN)) {
+ chan_misdn_log(4, port, " --> STARTING ROUND ROBIN...\n");
+ rr = get_robin_position(group);
+ }
+
+ if (rr) {
+ int robin_channel = rr->channel;
+ int port_start;
+ int next_chan = 1;
+
+ do {
+ port_start = 0;
+ for (port = misdn_cfg_get_next_port_spin(rr->port); port > 0 && port != port_start;
+ port = misdn_cfg_get_next_port_spin(port)) {
+
+ if (!port_start)
+ port_start = port;
+
+ if (port >= port_start)
+ next_chan = 1;
+
+ if (port <= port_start && next_chan) {
+ int maxbchans=misdn_lib_get_maxchans(port);
+ if (++robin_channel >= maxbchans) {
+ robin_channel = 1;
+ }
+ next_chan = 0;
+ }
+
+ misdn_cfg_get(port, MISDN_CFG_GROUPNAME, cfg_group, sizeof(cfg_group));
+
+ if (!strcasecmp(cfg_group, group)) {
+ int port_up;
+ int check;
+ misdn_cfg_get(port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(check));
+ port_up = misdn_lib_port_up(port, check);
+
+ if (check && !port_up)
+ chan_misdn_log(1, port, "L1 is not Up on this Port\n");
+
+ if (check && port_up < 0) {
+ ast_log(LOG_WARNING, "This port (%d) is blocked\n", port);
+ }
+
+ if (port_up > 0) {
+ newbc = misdn_lib_get_free_bc(port, robin_channel, 0, 0);
+ if (newbc) {
+ chan_misdn_log(4, port, " Success! Found port:%d channel:%d\n", newbc->port, newbc->channel);
+ if (port_up)
+ chan_misdn_log(4, port, "portup:%d\n", port_up);
+ rr->port = newbc->port;
+ rr->channel = newbc->channel;
+ break;
+ }
+ }
+ }
+ }
+ } while (!newbc && robin_channel != rr->channel);
+
+ } else {
+ for (port = misdn_cfg_get_next_port(0); port > 0;
+ port = misdn_cfg_get_next_port(port)) {
+
+ misdn_cfg_get(port, MISDN_CFG_GROUPNAME, cfg_group, sizeof(cfg_group));
+
+ chan_misdn_log(3, port, "Group [%s] Port [%d]\n", group, port);
+ if (!strcasecmp(cfg_group, group)) {
+ int port_up;
+ int check;
+ misdn_cfg_get(port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(check));
+ port_up = misdn_lib_port_up(port, check);
+
+ chan_misdn_log(4, port, "portup:%d\n", port_up);
+
+ if (port_up > 0) {
+ newbc = misdn_lib_get_free_bc(port, 0, 0, dec);
+ if (newbc)
+ break;
+ }
+ }
+ }
+ }
+
+ /* Group dial failed ?*/
+ if (!newbc) {
+ ast_log(LOG_WARNING,
+ "Could not Dial out on group '%s'.\n"
+ "\tEither the L2 and L1 on all of these ports where DOWN (see 'show application misdn_check_l2l1')\n"
+ "\tOr there was no free channel on none of the ports\n\n"
+ , group);
+ return NULL;
+ }
+ } else { /* 'Normal' Port dial * Port dial */
+ if (channel)
+ chan_misdn_log(1, port, " --> preselected_channel: %d\n", channel);
+ newbc = misdn_lib_get_free_bc(port, channel, 0, dec);
+
+ if (!newbc) {
+ ast_log(LOG_WARNING, "Could not create channel on port:%d with extensions:%s\n", port, ext);
+ return NULL;
+ }
+ }
+
+
+ /* create ast_channel and link all the objects together */
+ cl->bc = newbc;
+
+ tmp = misdn_new(cl, AST_STATE_RESERVED, ext, NULL, format, port, channel);
+ if (!tmp) {
+ ast_log(LOG_ERROR,"Could not create Asterisk object\n");
+ return NULL;
+ }
+
+ cl->ast=tmp;
+
+ /* register chan in local list */
+ cl_queue_chan(&cl_te, cl) ;
+
+ /* fill in the config into the objects */
+ read_config(cl, ORG_AST);
+
+ /* important */
+ cl->need_hangup = 0;
+
+ return tmp;
+}
+
+
+static int misdn_send_text(struct ast_channel *chan, const char *text)
+{
+ struct chan_list *tmp = chan->tech_pvt;
+
+ if (tmp && tmp->bc) {
+ ast_copy_string(tmp->bc->display, text, sizeof(tmp->bc->display));
+ misdn_lib_send_event(tmp->bc, EVENT_INFORMATION);
+ } else {
+ ast_log(LOG_WARNING, "No chan_list but send_text request?\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_channel_tech misdn_tech = {
+ .type = "mISDN",
+ .description = "Channel driver for mISDN Support (Bri/Pri)",
+ .capabilities = AST_FORMAT_ALAW ,
+ .requester = misdn_request,
+ .send_digit_begin = misdn_digit_begin,
+ .send_digit_end = misdn_digit_end,
+ .call = misdn_call,
+ .bridge = misdn_bridge,
+ .hangup = misdn_hangup,
+ .answer = misdn_answer,
+ .read = misdn_read,
+ .write = misdn_write,
+ .indicate = misdn_indication,
+ .fixup = misdn_fixup,
+ .send_text = misdn_send_text,
+ .properties = 0
+};
+
+static struct ast_channel_tech misdn_tech_wo_bridge = {
+ .type = "mISDN",
+ .description = "Channel driver for mISDN Support (Bri/Pri)",
+ .capabilities = AST_FORMAT_ALAW ,
+ .requester = misdn_request,
+ .send_digit_begin = misdn_digit_begin,
+ .send_digit_end = misdn_digit_end,
+ .call = misdn_call,
+ .hangup = misdn_hangup,
+ .answer = misdn_answer,
+ .read = misdn_read,
+ .write = misdn_write,
+ .indicate = misdn_indication,
+ .fixup = misdn_fixup,
+ .send_text = misdn_send_text,
+ .properties = 0
+};
+
+
+static int glob_channel = 0;
+
+static void update_name(struct ast_channel *tmp, int port, int c)
+{
+ int chan_offset = 0;
+ int tmp_port = misdn_cfg_get_next_port(0);
+ for (; tmp_port > 0; tmp_port = misdn_cfg_get_next_port(tmp_port)) {
+ if (tmp_port == port)
+ break;
+ chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2;
+ }
+ if (c < 0)
+ c = 0;
+
+ ast_string_field_build(tmp, name, "%s/%d-u%d",
+ misdn_type, chan_offset+c, glob_channel++);
+
+ chan_misdn_log(3 , port, " --> updating channel name to [%s]\n", tmp->name);
+}
+
+static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char *exten, char *callerid, int format, int port, int c)
+{
+ struct ast_channel *tmp;
+ char *cid_name = 0, *cid_num = 0;
+ int chan_offset = 0;
+ int tmp_port = misdn_cfg_get_next_port(0);
+ int bridging;
+
+ for (; tmp_port > 0; tmp_port = misdn_cfg_get_next_port(tmp_port)) {
+ if (tmp_port == port)
+ break;
+ chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2;
+ }
+ if (c < 0)
+ c = 0;
+
+ if (callerid)
+ ast_callerid_parse(callerid, &cid_name, &cid_num);
+
+ tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", 0, "%s/%d-u%d", misdn_type, chan_offset + c, glob_channel++);
+
+ if (tmp) {
+ chan_misdn_log(2, 0, " --> * NEW CHANNEL dad:%s oad:%s\n", exten, callerid);
+
+ tmp->nativeformats = prefformat;
+
+ tmp->readformat = format;
+ tmp->rawreadformat = format;
+ tmp->writeformat = format;
+ tmp->rawwriteformat = format;
+
+ tmp->tech_pvt = chlist;
+
+ misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging));
+
+ if (bridging)
+ tmp->tech = &misdn_tech;
+ else
+ tmp->tech = &misdn_tech_wo_bridge;
+
+ tmp->writeformat = format;
+ tmp->readformat = format;
+ tmp->priority=1;
+
+ if (exten)
+ ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
+ else
+ chan_misdn_log(1, 0, "misdn_new: no exten given.\n");
+
+ if (callerid)
+ /* Don't use ast_set_callerid() here because it will
+ * generate a needless NewCallerID event */
+ tmp->cid.cid_ani = ast_strdup(cid_num);
+
+ if (pipe(chlist->pipe) < 0)
+ ast_log(LOG_ERROR, "Pipe failed\n");
+
+ ast_channel_set_fd(tmp, 0, chlist->pipe[0]);
+
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ else
+ tmp->rings = 0;
+
+ ast_jb_configure(tmp, misdn_get_global_jbconf());
+ } else {
+ chan_misdn_log(-1, 0, "Unable to allocate channel structure\n");
+ }
+
+ return tmp;
+}
+
+static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc)
+{
+ struct chan_list *help = list;
+ for (; help; help = help->next) {
+ if (help->bc == bc) return help;
+ }
+
+ chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad);
+
+ return NULL;
+}
+
+static struct chan_list *find_chan_by_pid(struct chan_list *list, int pid)
+{
+ struct chan_list *help = list;
+ for (; help; help = help->next) {
+ if ( help->bc && (help->bc->pid == pid) ) return help;
+ }
+
+ chan_misdn_log(6, 0, "$$$ find_chan: No channel found for pid:%d\n", pid);
+
+ return NULL;
+}
+
+static struct chan_list *find_holded(struct chan_list *list, struct misdn_bchannel *bc)
+{
+ struct chan_list *help = list;
+
+ if (bc->pri) return NULL;
+
+ chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d oad:%s dad:%s\n", bc->channel, bc->oad, bc->dad);
+ for (;help; help = help->next) {
+ chan_misdn_log(4, bc->port, "$$$ find_holded: --> holded:%d channel:%d\n", help->state==MISDN_HOLDED, help->hold_info.channel);
+ if ( (help->state == MISDN_HOLDED) &&
+ (help->hold_info.port == bc->port) )
+ return help;
+ }
+ chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad);
+
+ return NULL;
+}
+
+
+static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3_id, int w)
+{
+ struct chan_list *help = list;
+
+ for (; help; help = help->next) {
+ if ( (help->state == MISDN_HOLDED) &&
+ (help->l3id == l3_id)
+ )
+ return help;
+ }
+
+ return NULL;
+}
+
+static void cl_queue_chan(struct chan_list **list, struct chan_list *chan)
+{
+ chan_misdn_log(4, chan->bc ? chan->bc->port : 0, "* Queuing chan %p\n", chan);
+
+ ast_mutex_lock(&cl_te_lock);
+ if (!*list) {
+ *list = chan;
+ } else {
+ struct chan_list *help = *list;
+ for (; help->next; help = help->next);
+ help->next = chan;
+ }
+ chan->next = NULL;
+ ast_mutex_unlock(&cl_te_lock);
+}
+
+static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan)
+{
+ struct chan_list *help;
+
+ if (chan->dsp)
+ ast_dsp_free(chan->dsp);
+ if (chan->trans)
+ ast_translator_free_path(chan->trans);
+
+ ast_mutex_lock(&cl_te_lock);
+ if (!*list) {
+ ast_mutex_unlock(&cl_te_lock);
+ return;
+ }
+
+ if (*list == chan) {
+ *list = (*list)->next;
+ ast_mutex_unlock(&cl_te_lock);
+ return;
+ }
+
+ for (help = *list; help->next; help = help->next) {
+ if (help->next == chan) {
+ help->next = help->next->next;
+ ast_mutex_unlock(&cl_te_lock);
+ return;
+ }
+ }
+
+ ast_mutex_unlock(&cl_te_lock);
+}
+
+/** Channel Queue End **/
+
+
+int pbx_start_chan(struct chan_list *ch)
+{
+ int ret = ast_pbx_start(ch->ast);
+
+ if (ret >= 0)
+ ch->need_hangup = 0;
+ else
+ ch->need_hangup = 1;
+
+ return ret;
+}
+
+static void hangup_chan(struct chan_list *ch)
+{
+ int port = ch ? (ch->bc ? ch->bc->port : 0) : 0;
+ if (!ch) {
+ cb_log(1, 0, "Cannot hangup chan, no ch\n");
+ return;
+ }
+
+ cb_log(5, port, "hangup_chan called\n");
+
+ if (ch->need_hangup) {
+ cb_log(2, port, " --> hangup\n");
+ send_cause2ast(ch->ast, ch->bc, ch);
+ ch->need_hangup = 0;
+ ch->need_queue_hangup = 0;
+ if (ch->ast)
+ ast_hangup(ch->ast);
+ return;
+ }
+
+ if (!ch->need_queue_hangup) {
+ cb_log(2, port, " --> No need to queue hangup\n");
+ }
+
+ ch->need_queue_hangup = 0;
+ if (ch->ast) {
+ send_cause2ast(ch->ast, ch->bc, ch);
+
+ if (ch->ast)
+ ast_queue_hangup(ch->ast);
+ cb_log(2, port, " --> queue_hangup\n");
+ } else {
+ cb_log(1, port, "Cannot hangup chan, no ast\n");
+ }
+}
+
+/** Isdn asks us to release channel, pendant to misdn_hangup **/
+static void release_chan(struct misdn_bchannel *bc) {
+ struct ast_channel *ast=NULL;
+
+ ast_mutex_lock(&release_lock);
+ {
+ struct chan_list *ch=find_chan_by_bc(cl_te, bc);
+ if (!ch) {
+ chan_misdn_log(1, bc->port, "release_chan: Ch not found!\n");
+ ast_mutex_unlock(&release_lock);
+ return;
+ }
+
+ if (ch->ast) {
+ ast = ch->ast;
+ }
+
+ chan_misdn_log(5, bc->port, "release_chan: bc with l3id: %x\n", bc->l3_id);
+
+ /*releaseing jitterbuffer*/
+ if (ch->jb ) {
+ misdn_jb_destroy(ch->jb);
+ ch->jb = NULL;
+ } else {
+ if (!bc->nojitter)
+ chan_misdn_log(5, bc->port, "Jitterbuffer already destroyed.\n");
+ }
+
+ if (ch->overlap_dial) {
+ if (ch->overlap_dial_task != -1) {
+ misdn_tasks_remove(ch->overlap_dial_task);
+ ch->overlap_dial_task = -1;
+ }
+ ast_mutex_destroy(&ch->overlap_tv_lock);
+ }
+
+ if (ch->originator == ORG_AST) {
+ misdn_out_calls[bc->port]--;
+ } else {
+ misdn_in_calls[bc->port]--;
+ }
+
+ if (ch) {
+ close(ch->pipe[0]);
+ close(ch->pipe[1]);
+
+ if (ast && MISDN_ASTERISK_TECH_PVT(ast)) {
+ chan_misdn_log(1, bc->port, "* RELEASING CHANNEL pid:%d ctx:%s dad:%s oad:%s state: %s\n", bc ? bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(ch));
+ chan_misdn_log(3, bc->port, " --> * State Down\n");
+ MISDN_ASTERISK_TECH_PVT(ast) = NULL;
+
+ if (ast->_state != AST_STATE_RESERVED) {
+ chan_misdn_log(3, bc->port, " --> Setting AST State to down\n");
+ ast_setstate(ast, AST_STATE_DOWN);
+ }
+ }
+
+ ch->state = MISDN_CLEANING;
+ cl_dequeue_chan(&cl_te, ch);
+
+ ast_free(ch);
+ } else {
+ /* chan is already cleaned, so exiting */
+ }
+
+ ast_mutex_unlock(&release_lock);
+ }
+/*** release end **/
+}
+
+static void misdn_transfer_bc(struct chan_list *tmp_ch, struct chan_list *holded_chan)
+{
+ chan_misdn_log(4, 0, "TRANSFERING %s to %s\n", holded_chan->ast->name, tmp_ch->ast->name);
+
+ tmp_ch->state = MISDN_HOLD_DISCONNECT;
+
+ ast_moh_stop(ast_bridged_channel(holded_chan->ast));
+
+ holded_chan->state=MISDN_CONNECTED;
+ /* misdn_lib_transfer(holded_chan->bc); */
+ ast_channel_masquerade(holded_chan->ast, ast_bridged_channel(tmp_ch->ast));
+}
+
+
+static void do_immediate_setup(struct misdn_bchannel *bc, struct chan_list *ch, struct ast_channel *ast)
+{
+ char *predial;
+ struct ast_frame fr;
+
+ predial = ast_strdupa(ast->exten);
+
+ ch->state = MISDN_DIALING;
+
+ if (!ch->noautorespond_on_setup) {
+ if (bc->nt) {
+ int ret;
+ ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE );
+ } else {
+ int ret;
+ if ( misdn_lib_is_ptp(bc->port)) {
+ ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE );
+ } else {
+ ret = misdn_lib_send_event(bc, EVENT_PROCEEDING );
+ }
+ }
+ } else {
+ ch->state = MISDN_INCOMING_SETUP;
+ }
+
+ chan_misdn_log(1, bc->port, "* Starting Ast ctx:%s dad:%s oad:%s with 's' extension\n", ast->context, ast->exten, ast->cid.cid_num);
+
+ strncpy(ast->exten, "s", 2);
+
+ if (pbx_start_chan(ch) < 0) {
+ ast = NULL;
+ hangup_chan(ch);
+ hanguptone_indicate(ch);
+
+ if (bc->nt)
+ misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE );
+ else
+ misdn_lib_send_event(bc, EVENT_DISCONNECT );
+ }
+
+
+ while (!ast_strlen_zero(predial) ) {
+ fr.frametype = AST_FRAME_DTMF;
+ fr.subclass = *predial;
+ fr.src = NULL;
+ fr.data = NULL;
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.mallocd = 0;
+ fr.offset = 0;
+ fr.delivery = ast_tv(0,0);
+
+ if (ch->ast && MISDN_ASTERISK_PVT(ch->ast) && MISDN_ASTERISK_TECH_PVT(ch->ast)) {
+ ast_queue_frame(ch->ast, &fr);
+ }
+ predial++;
+ }
+}
+
+
+
+static void send_cause2ast(struct ast_channel *ast, struct misdn_bchannel *bc, struct chan_list *ch) {
+ if (!ast) {
+ chan_misdn_log(1, 0, "send_cause2ast: No Ast\n");
+ return;
+ }
+ if (!bc) {
+ chan_misdn_log(1, 0, "send_cause2ast: No BC\n");
+ return;
+ }
+ if (!ch) {
+ chan_misdn_log(1, 0, "send_cause2ast: No Ch\n");
+ return;
+ }
+
+ ast->hangupcause = bc->cause;
+
+ switch (bc->cause) {
+
+ case 1: /** Congestion Cases **/
+ case 2:
+ case 3:
+ case 4:
+ case 22:
+ case 27:
+ /*
+ * Not Queueing the Congestion anymore, since we want to hear
+ * the inband message
+ *
+ chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Congestion pid:%d\n", bc ? bc->pid : -1);
+ ch->state = MISDN_BUSY;
+
+ ast_queue_control(ast, AST_CONTROL_CONGESTION);
+ */
+ break;
+
+ case 21:
+ case 17: /* user busy */
+
+ ch->state = MISDN_BUSY;
+
+ if (!ch->need_busy) {
+ chan_misdn_log(1, bc ? bc->port : 0, "Queued busy already\n");
+ break;
+ }
+
+ chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Busy pid:%d\n", bc ? bc->pid : -1);
+
+ ast_queue_control(ast, AST_CONTROL_BUSY);
+
+ ch->need_busy = 0;
+
+ break;
+ }
+}
+
+
+void import_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch)
+{
+ const char *tmp = pbx_builtin_getvar_helper(chan, "MISDN_PID");
+ if (tmp) {
+ ch->other_pid = atoi(tmp);
+ chan_misdn_log(3, bc->port, " --> IMPORT_PID: importing pid:%s\n", tmp);
+ if (ch->other_pid > 0) {
+ ch->other_ch = find_chan_by_pid(cl_te, ch->other_pid);
+ if (ch->other_ch)
+ ch->other_ch->other_ch = ch;
+ }
+ }
+
+ tmp = pbx_builtin_getvar_helper(chan, "MISDN_ADDRESS_COMPLETE");
+ if (tmp && (atoi(tmp) == 1)) {
+ bc->sending_complete = 1;
+ }
+
+ tmp = pbx_builtin_getvar_helper(chan, "MISDN_USERUSER");
+ if (tmp) {
+ ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", tmp);
+ ast_copy_string(bc->uu, tmp, sizeof(bc->uu));
+ bc->uulen = strlen(bc->uu);
+ }
+
+ tmp = pbx_builtin_getvar_helper(chan, "MISDN_KEYPAD");
+ if (tmp)
+ ast_copy_string(bc->keypad, tmp, sizeof(bc->keypad));
+}
+
+void export_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch)
+{
+ char tmp[32];
+ chan_misdn_log(3, bc->port, " --> EXPORT_PID: pid:%d\n", bc->pid);
+ snprintf(tmp, sizeof(tmp), "%d", bc->pid);
+ pbx_builtin_setvar_helper(chan, "_MISDN_PID", tmp);
+
+ if (bc->sending_complete) {
+ snprintf(tmp, sizeof(tmp), "%d", bc->sending_complete);
+ pbx_builtin_setvar_helper(chan, "MISDN_ADDRESS_COMPLETE", tmp);
+ }
+
+ if (bc->urate) {
+ snprintf(tmp, sizeof(tmp), "%d", bc->urate);
+ pbx_builtin_setvar_helper(chan, "MISDN_URATE", tmp);
+ }
+
+ if (bc->uulen)
+ pbx_builtin_setvar_helper(chan, "MISDN_USERUSER", bc->uu);
+
+ if (!ast_strlen_zero(bc->keypad))
+ pbx_builtin_setvar_helper(chan, "MISDN_KEYPAD", bc->keypad);
+}
+
+int add_in_calls(int port)
+{
+ int max_in_calls;
+
+ misdn_cfg_get(port, MISDN_CFG_MAX_IN, &max_in_calls, sizeof(max_in_calls));
+ misdn_in_calls[port]++;
+
+ if (max_in_calls >= 0 && max_in_calls < misdn_in_calls[port]) {
+ ast_log(LOG_NOTICE, "Marking Incoming Call on port[%d]\n", port);
+ return misdn_in_calls[port] - max_in_calls;
+ }
+
+ return 0;
+}
+
+int add_out_calls(int port)
+{
+ int max_out_calls;
+
+ misdn_cfg_get(port, MISDN_CFG_MAX_OUT, &max_out_calls, sizeof(max_out_calls));
+
+ if (max_out_calls >= 0 && max_out_calls <= misdn_out_calls[port]) {
+ ast_log(LOG_NOTICE, "Rejecting Outgoing Call on port[%d]\n", port);
+ return (misdn_out_calls[port] + 1) - max_out_calls;
+ }
+
+ misdn_out_calls[port]++;
+
+ return 0;
+}
+
+static void start_pbx(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) {
+ if (pbx_start_chan(ch) < 0) {
+ hangup_chan(ch);
+ chan_misdn_log(-1, bc->port, "ast_pbx_start returned <0 in SETUP\n");
+ if (bc->nt) {
+ hanguptone_indicate(ch);
+ misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE);
+ } else
+ misdn_lib_send_event(bc, EVENT_RELEASE);
+ }
+}
+
+static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) {
+ ch->state=MISDN_WAITING4DIGS;
+ misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE );
+ if (bc->nt && !bc->dad[0])
+ dialtone_indicate(ch);
+}
+
+
+/************************************************************/
+/* Receive Events from isdn_lib here */
+/************************************************************/
+static enum event_response_e
+cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
+{
+ struct chan_list *ch = find_chan_by_bc(cl_te, bc);
+
+ if (event != EVENT_BCHAN_DATA && event != EVENT_TONE_GENERATE) { /* Debug Only Non-Bchan */
+ int debuglevel = 1;
+ if ( event == EVENT_CLEANUP && !user_data)
+ debuglevel = 5;
+
+ chan_misdn_log(debuglevel, bc->port, "I IND :%s oad:%s dad:%s pid:%d state:%s\n", manager_isdn_get_info(event), bc->oad, bc->dad, bc->pid, ch ? misdn_get_ch_state(ch) : "none");
+ if (debuglevel == 1) {
+ misdn_lib_log_ies(bc);
+ chan_misdn_log(4, bc->port, " --> bc_state:%s\n", bc_state2str(bc->bc_state));
+ }
+ }
+
+ if (!ch) {
+ switch(event) {
+ case EVENT_SETUP:
+ case EVENT_DISCONNECT:
+ case EVENT_PORT_ALARM:
+ case EVENT_RETRIEVE:
+ case EVENT_NEW_BC:
+ case EVENT_FACILITY:
+ break;
+ case EVENT_RELEASE_COMPLETE:
+ chan_misdn_log(1, bc->port, " --> no Ch, so we've already released.\n");
+ break;
+ case EVENT_CLEANUP:
+ case EVENT_TONE_GENERATE:
+ case EVENT_BCHAN_DATA:
+ return -1;
+ default:
+ chan_misdn_log(1, bc->port, "Chan not existing at the moment bc->l3id:%x bc:%p event:%s port:%d channel:%d\n", bc->l3_id, bc, manager_isdn_get_info(event), bc->port, bc->channel);
+ return -1;
+ }
+ }
+
+ if (ch) {
+ switch (event) {
+ case EVENT_TONE_GENERATE:
+ break;
+ case EVENT_DISCONNECT:
+ case EVENT_RELEASE:
+ case EVENT_RELEASE_COMPLETE:
+ case EVENT_CLEANUP:
+ case EVENT_TIMEOUT:
+ if (!ch->ast)
+ chan_misdn_log(3, bc->port, "ast_hangup already called, so we have no ast ptr anymore in event(%s)\n", manager_isdn_get_info(event));
+ break;
+ default:
+ if (!ch->ast || !MISDN_ASTERISK_PVT(ch->ast) || !MISDN_ASTERISK_TECH_PVT(ch->ast)) {
+ if (event != EVENT_BCHAN_DATA)
+ ast_log(LOG_NOTICE, "No Ast or No private Pointer in Event (%d:%s)\n", event, manager_isdn_get_info(event));
+ return -1;
+ }
+ }
+ }
+
+
+ switch (event) {
+ case EVENT_PORT_ALARM:
+ {
+ int boa = 0;
+ misdn_cfg_get(bc->port, MISDN_CFG_ALARM_BLOCK, &boa, sizeof(boa));
+ if (boa) {
+ cb_log(1, bc->port, " --> blocking\n");
+ misdn_lib_port_block(bc->port);
+ }
+ }
+ break;
+ case EVENT_BCHAN_ACTIVATED:
+ break;
+
+ case EVENT_NEW_CHANNEL:
+ update_name(ch->ast,bc->port,bc->channel);
+ break;
+
+ case EVENT_NEW_L3ID:
+ ch->l3id=bc->l3_id;
+ ch->addr=bc->addr;
+ break;
+
+ case EVENT_NEW_BC:
+ if (!ch) {
+ ch = find_holded(cl_te,bc);
+ }
+
+ if (!ch) {
+ ast_log(LOG_WARNING, "NEW_BC without chan_list?\n");
+ break;
+ }
+
+ if (bc)
+ ch->bc = (struct misdn_bchannel *)user_data;
+ break;
+
+ case EVENT_DTMF_TONE:
+ {
+ /* sending INFOS as DTMF-Frames :) */
+ struct ast_frame fr = { 0, };
+ fr.frametype = AST_FRAME_DTMF;
+ fr.subclass = bc->dtmf ;
+ fr.src = NULL;
+ fr.data = NULL;
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.mallocd = 0;
+ fr.offset = 0;
+ fr.delivery = ast_tv(0,0);
+
+ if (!ch->ignore_dtmf) {
+ chan_misdn_log(2, bc->port, " --> DTMF:%c\n", bc->dtmf);
+ ast_queue_frame(ch->ast, &fr);
+ } else {
+ chan_misdn_log(2, bc->port, " --> Ingoring DTMF:%c due to bridge flags\n", bc->dtmf);
+ }
+ }
+ break;
+ case EVENT_STATUS:
+ break;
+
+ case EVENT_INFORMATION:
+ {
+ if ( ch->state != MISDN_CONNECTED )
+ stop_indicate(ch);
+
+ if (!ch->ast)
+ break;
+
+ if (ch->state == MISDN_WAITING4DIGS ) {
+ /* Ok, incomplete Setup, waiting till extension exists */
+ if (ast_strlen_zero(bc->info_dad) && ! ast_strlen_zero(bc->keypad)) {
+ chan_misdn_log(1, bc->port, " --> using keypad as info\n");
+ ast_copy_string(bc->info_dad, bc->keypad, sizeof(bc->info_dad));
+ }
+
+ strncat(bc->dad,bc->info_dad, sizeof(bc->dad) - 1);
+ ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten));
+
+ /* Check for Pickup Request first */
+ if (!strcmp(ch->ast->exten, ast_pickup_ext())) {
+ if (ast_pickup_call(ch->ast)) {
+ hangup_chan(ch);
+ } else {
+ struct ast_channel *chan = ch->ast;
+ ch->state = MISDN_CALLING_ACKNOWLEDGE;
+ ast_setstate(chan, AST_STATE_DOWN);
+ hangup_chan(ch);
+ ch->ast = NULL;
+ break;
+ }
+ }
+
+ if (!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) {
+ if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) {
+ ast_log(LOG_WARNING, "Extension can never match, So jumping to 'i' extension. port(%d)\n", bc->port);
+ strcpy(ch->ast->exten, "i");
+
+ ch->state = MISDN_DIALING;
+ start_pbx(ch, bc, ch->ast);
+ break;
+ }
+
+ ast_log(LOG_WARNING, "Extension can never match, so disconnecting on port(%d)."
+ "maybe you want to add an 'i' extension to catch this case.\n",
+ bc->port);
+
+ if (bc->nt)
+ hanguptone_indicate(ch);
+ ch->state = MISDN_EXTCANTMATCH;
+ bc->out_cause = 1;
+
+ misdn_lib_send_event(bc, EVENT_DISCONNECT);
+ break;
+ }
+
+ if (ch->overlap_dial) {
+ ast_mutex_lock(&ch->overlap_tv_lock);
+ ch->overlap_tv = ast_tvnow();
+ ast_mutex_unlock(&ch->overlap_tv_lock);
+ if (ch->overlap_dial_task == -1) {
+ ch->overlap_dial_task =
+ misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch);
+ }
+ break;
+ }
+
+ if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) {
+
+ ch->state = MISDN_DIALING;
+ start_pbx(ch, bc, ch->ast);
+ }
+ } else {
+ /* sending INFOS as DTMF-Frames :) */
+ struct ast_frame fr;
+ int digits;
+ fr.frametype = AST_FRAME_DTMF;
+ fr.subclass = bc->info_dad[0] ;
+ fr.src = NULL;
+ fr.data = NULL;
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.mallocd = 0;
+ fr.offset = 0;
+ fr.delivery = ast_tv(0,0);
+
+ misdn_cfg_get(0, MISDN_GEN_APPEND_DIGITS2EXTEN, &digits, sizeof(digits));
+ if (ch->state != MISDN_CONNECTED ) {
+ if (digits) {
+ strncat(bc->dad, bc->info_dad, sizeof(bc->dad) - 1);
+ ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten));
+ ast_cdr_update(ch->ast);
+ }
+
+ ast_queue_frame(ch->ast, &fr);
+ }
+ }
+ }
+ break;
+ case EVENT_SETUP:
+ {
+ struct chan_list *ch = find_chan_by_bc(cl_te, bc);
+ int msn_valid = misdn_cfg_is_msn_valid(bc->port, bc->dad);
+ struct ast_channel *chan;
+ int exceed;
+ int pres,screen;
+ int ai;
+ int im;
+
+ if (ch) {
+ switch (ch->state) {
+ case MISDN_NOTHING:
+ ch = NULL;
+ break;
+ default:
+ chan_misdn_log(1, bc->port, " --> Ignoring Call we have already one\n");
+ return RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE; /* Ignore MSNs which are not in our List */
+ }
+ }
+
+ if (!bc->nt && ! msn_valid) {
+ chan_misdn_log(1, bc->port, " --> Ignoring Call, its not in our MSN List\n");
+ return RESPONSE_IGNORE_SETUP; /* Ignore MSNs which are not in our List */
+ }
+
+ if (bc->cw) {
+ int cause;
+ chan_misdn_log(0, bc->port, " --> Call Waiting on PMP sending RELEASE_COMPLETE\n");
+ misdn_cfg_get(bc->port, MISDN_CFG_REJECT_CAUSE, &cause, sizeof(cause));
+ bc->out_cause = cause ? cause : 16;
+ return RESPONSE_RELEASE_SETUP;
+ }
+
+ print_bearer(bc);
+
+ if (!bc->nt && ! msn_valid) {
+ chan_misdn_log(1, bc->port, " --> Ignoring Call, its not in our MSN List\n");
+ return RESPONSE_IGNORE_SETUP; /* Ignore MSNs which are not in our List */
+ }
+
+ if (bc->cw) {
+ int cause;
+ chan_misdn_log(0, bc->port, " --> Call Waiting on PMP sending RELEASE_COMPLETE\n");
+ misdn_cfg_get(bc->port, MISDN_CFG_REJECT_CAUSE, &cause, sizeof(cause));
+ bc->out_cause = cause ? cause : 16;
+ return RESPONSE_RELEASE_SETUP;
+ }
+
+ print_bearer(bc);
+
+ ch = init_chan_list(ORG_MISDN);
+
+ if (!ch) {
+ chan_misdn_log(-1, bc->port, "cb_events: malloc for chan_list failed!\n");
+ return 0;
+ }
+
+ ch->bc = bc;
+ ch->l3id = bc->l3_id;
+ ch->addr = bc->addr;
+ ch->originator = ORG_MISDN;
+
+ chan = misdn_new(ch, AST_STATE_RESERVED, bc->dad, bc->oad, AST_FORMAT_ALAW, bc->port, bc->channel);
+
+ if (!chan) {
+ misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE);
+ ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n");
+ return 0;
+ }
+
+ ch->ast = chan;
+
+ if ((exceed = add_in_calls(bc->port))) {
+ char tmp[16];
+ snprintf(tmp, sizeof(tmp), "%d", exceed);
+ pbx_builtin_setvar_helper(chan, "MAX_OVERFLOW", tmp);
+ }
+
+ read_config(ch, ORG_MISDN);
+
+ export_ch(chan, bc, ch);
+
+ ch->ast->rings = 1;
+ ast_setstate(ch->ast, AST_STATE_RINGING);
+
+ switch (bc->pres) {
+ case 1:
+ pres = AST_PRES_RESTRICTED;
+ chan_misdn_log(2, bc->port, " --> PRES: Restricted (1)\n");
+ break;
+ case 2:
+ pres = AST_PRES_UNAVAILABLE;
+ chan_misdn_log(2, bc->port, " --> PRES: Restricted (2)\n");
+ break;
+ default:
+ pres = AST_PRES_ALLOWED;
+ chan_misdn_log(2, bc->port, " --> PRES: Restricted (%d)\n", bc->pres);
+ }
+
+ switch (bc->screen) {
+ case 0:
+ screen = AST_PRES_USER_NUMBER_UNSCREENED;
+ chan_misdn_log(2, bc->port, " --> SCREEN: Unscreened (0)\n");
+ break;
+ case 1:
+ screen = AST_PRES_USER_NUMBER_PASSED_SCREEN;
+ chan_misdn_log(2, bc->port, " --> SCREEN: Passed screen (1)\n");
+ break;
+ case 2:
+ screen = AST_PRES_USER_NUMBER_FAILED_SCREEN;
+ chan_misdn_log(2, bc->port, " --> SCREEN: failed screen (2)\n");
+ break;
+ case 3:
+ screen = AST_PRES_NETWORK_NUMBER;
+ chan_misdn_log(2, bc->port, " --> SCREEN: Network Number (3)\n");
+ break;
+ default:
+ screen = AST_PRES_USER_NUMBER_UNSCREENED;
+ chan_misdn_log(2, bc->port, " --> SCREEN: Unscreened (%d)\n", bc->screen);
+ }
+
+ chan->cid.cid_pres = pres + screen;
+
+ pbx_builtin_setvar_helper(chan, "TRANSFERCAPABILITY", ast_transfercapability2str(bc->capability));
+ chan->transfercapability = bc->capability;
+
+ switch (bc->capability) {
+ case INFO_CAPABILITY_DIGITAL_UNRESTRICTED:
+ pbx_builtin_setvar_helper(chan, "CALLTYPE", "DIGITAL");
+ break;
+ default:
+ pbx_builtin_setvar_helper(chan, "CALLTYPE", "SPEECH");
+ }
+
+ /** queue new chan **/
+ cl_queue_chan(&cl_te, ch);
+
+ if (!strstr(ch->allowed_bearers, "all")) {
+ int i;
+ for (i = 0; i < sizeof(allowed_bearers_array) / sizeof(struct allowed_bearers); i++) {
+ if (allowed_bearers_array[i].cap == bc->capability) {
+ if (!strstr(ch->allowed_bearers, allowed_bearers_array[i].name)) {
+ chan_misdn_log(0, bc->port, "Bearer Not allowed\b");
+ bc->out_cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+
+ ch->state = MISDN_EXTCANTMATCH;
+ misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE );
+ return RESPONSE_OK;
+ }
+ }
+
+ }
+ }
+
+ /* Check for Pickup Request first */
+ if (!strcmp(chan->exten, ast_pickup_ext())) {
+ if (!ch->noautorespond_on_setup) {
+ int ret;/** Sending SETUP_ACK**/
+ ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE );
+ } else {
+ ch->state = MISDN_INCOMING_SETUP;
+ }
+ if (ast_pickup_call(chan)) {
+ hangup_chan(ch);
+ } else {
+ ch->state = MISDN_CALLING_ACKNOWLEDGE;
+ ast_setstate(chan, AST_STATE_DOWN);
+ hangup_chan(ch);
+ ch->ast = NULL;
+ break;
+ }
+ }
+
+ /*
+ added support for s extension hope it will help those poor cretains
+ which haven't overlap dial.
+ */
+ misdn_cfg_get(bc->port, MISDN_CFG_ALWAYS_IMMEDIATE, &ai, sizeof(ai));
+ if (ai) {
+ do_immediate_setup(bc, ch, chan);
+ break;
+ }
+
+ /* check if we should jump into s when we have no dad */
+ misdn_cfg_get(bc->port, MISDN_CFG_IMMEDIATE, &im, sizeof(im));
+ if (im && ast_strlen_zero(bc->dad)) {
+ do_immediate_setup(bc, ch, chan);
+ break;
+ }
+
+ chan_misdn_log(5, bc->port, "CONTEXT:%s\n", ch->context);
+ if(!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) {
+ if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) {
+ ast_log(LOG_WARNING, "Extension can never match, So jumping to 'i' extension. port(%d)\n", bc->port);
+ strcpy(ch->ast->exten, "i");
+ misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE);
+ ch->state = MISDN_DIALING;
+ start_pbx(ch, bc, chan);
+ break;
+ }
+
+ ast_log(LOG_WARNING, "Extension can never match, so disconnecting on port(%d)."
+ "maybe you want to add an 'i' extension to catch this case.\n",
+ bc->port);
+ if (bc->nt)
+ hanguptone_indicate(ch);
+
+ ch->state = MISDN_EXTCANTMATCH;
+ bc->out_cause = AST_CAUSE_UNALLOCATED;
+
+ if (bc->nt)
+ misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE );
+ else
+ misdn_lib_send_event(bc, EVENT_RELEASE );
+
+ break;
+ }
+
+ /* Whatever happens, when sending_complete is set or we are PTMP TE, we will definitely
+ * jump into the dialplan, when the dialed extension does not exist, the 's' extension
+ * will be used by Asterisk automatically. */
+ if (bc->sending_complete || (!bc->nt && !misdn_lib_is_ptp(bc->port))) {
+ if (!ch->noautorespond_on_setup) {
+ ch->state=MISDN_DIALING;
+ misdn_lib_send_event(bc, EVENT_PROCEEDING );
+ } else {
+ ch->state = MISDN_INCOMING_SETUP;
+ }
+ start_pbx(ch, bc, chan);
+ break;
+ }
+
+
+ /*
+ * When we are NT and overlapdial is set and if
+ * the number is empty, we wait for the ISDN timeout
+ * instead of our own timer.
+ */
+ if (ch->overlap_dial && bc->nt && !bc->dad[0] ) {
+ wait_for_digits(ch, bc, chan);
+ break;
+ }
+
+ /*
+ * If overlapdial we will definitely send a SETUP_ACKNOWLEDGE and wait for more
+ * Infos with a Interdigit Timeout.
+ * */
+ if (ch->overlap_dial) {
+ ast_mutex_lock(&ch->overlap_tv_lock);
+ ch->overlap_tv = ast_tvnow();
+ ast_mutex_unlock(&ch->overlap_tv_lock);
+
+ wait_for_digits(ch, bc, chan);
+ if (ch->overlap_dial_task == -1)
+ ch->overlap_dial_task =
+ misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch);
+
+ break;
+ }
+
+ /* If the extension does not exist and we're not TE_PTMP we wait for more digis
+ * without interdigit timeout.
+ * */
+ if (!ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) {
+ wait_for_digits(ch, bc, chan);
+ break;
+ }
+
+ /*
+ * If the extension exists let's just jump into it.
+ * */
+ if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) {
+ if (bc->need_more_infos)
+ misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE );
+ else
+ misdn_lib_send_event(bc, EVENT_PROCEEDING);
+
+ ch->state = MISDN_DIALING;
+ start_pbx(ch, bc, chan);
+ break;
+ }
+ }
+ break;
+
+ case EVENT_SETUP_ACKNOWLEDGE:
+ {
+ ch->state = MISDN_CALLING_ACKNOWLEDGE;
+
+ if (bc->channel)
+ update_name(ch->ast,bc->port,bc->channel);
+
+ if (!ast_strlen_zero(bc->infos_pending)) {
+ /* TX Pending Infos */
+ strncat(bc->dad, bc->infos_pending, sizeof(bc->dad) - strlen(bc->dad) - 1);
+
+ if (!ch->ast)
+ break;
+ ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten));
+ ast_copy_string(bc->info_dad, bc->infos_pending, sizeof(bc->info_dad));
+ ast_copy_string(bc->infos_pending, "", sizeof(bc->infos_pending));
+
+ misdn_lib_send_event(bc, EVENT_INFORMATION);
+ }
+ }
+ break;
+ case EVENT_PROCEEDING:
+ {
+
+ if (misdn_cap_is_speech(bc->capability) &&
+ misdn_inband_avail(bc) ) {
+ start_bc_tones(ch);
+ }
+
+ ch->state = MISDN_PROCEEDING;
+
+ if (!ch->ast)
+ break;
+
+ ast_queue_control(ch->ast, AST_CONTROL_PROCEEDING);
+ }
+ break;
+ case EVENT_PROGRESS:
+
+ if (bc->channel)
+ update_name(ch->ast, bc->port, bc->channel);
+
+ if (!bc->nt ) {
+ if ( misdn_cap_is_speech(bc->capability) &&
+ misdn_inband_avail(bc)
+ ) {
+ start_bc_tones(ch);
+ }
+
+ ch->state = MISDN_PROGRESS;
+
+ if (!ch->ast)
+ break;
+ ast_queue_control(ch->ast, AST_CONTROL_PROGRESS);
+ }
+ break;
+
+
+ case EVENT_ALERTING:
+ {
+ ch->state = MISDN_ALERTING;
+
+ if (!ch->ast)
+ break;
+
+ ast_queue_control(ch->ast, AST_CONTROL_RINGING);
+ ast_setstate(ch->ast, AST_STATE_RINGING);
+
+ cb_log(7, bc->port, " --> Set State Ringing\n");
+
+ if (misdn_cap_is_speech(bc->capability) && misdn_inband_avail(bc)) {
+ cb_log(1, bc->port, "Starting Tones, we have inband Data\n");
+ start_bc_tones(ch);
+ } else {
+ cb_log(3, bc->port, " --> We have no inband Data, the other end must create ringing\n");
+ if (ch->far_alerting) {
+ cb_log(1, bc->port, " --> The other end can not do ringing eh ?.. we must do all ourself..");
+ start_bc_tones(ch);
+ /*tone_indicate(ch, TONE_FAR_ALERTING);*/
+ }
+ }
+ }
+ break;
+ case EVENT_CONNECT:
+ {
+ struct ast_channel *bridged;
+
+ /*we answer when we've got our very new L3 ID from the NT stack */
+ misdn_lib_send_event(bc, EVENT_CONNECT_ACKNOWLEDGE);
+
+ if (!ch->ast)
+ break;
+
+ bridged = ast_bridged_channel(ch->ast);
+ stop_indicate(ch);
+
+ if (bridged && !strcasecmp(bridged->tech->type, "mISDN")) {
+ struct chan_list *bridged_ch = MISDN_ASTERISK_TECH_PVT(bridged);
+
+ chan_misdn_log(1, bc->port, " --> copying cpndialplan:%d and cad:%s to the A-Channel\n", bc->cpnnumplan, bc->cad);
+ if (bridged_ch) {
+ bridged_ch->bc->cpnnumplan = bc->cpnnumplan;
+ ast_copy_string(bridged_ch->bc->cad, bc->cad, sizeof(bridged_ch->bc->cad));
+ }
+ }
+ }
+ ch->l3id=bc->l3_id;
+ ch->addr=bc->addr;
+
+ start_bc_tones(ch);
+
+ ch->state = MISDN_CONNECTED;
+
+ ast_queue_control(ch->ast, AST_CONTROL_ANSWER);
+ break;
+ case EVENT_CONNECT_ACKNOWLEDGE:
+ {
+ ch->l3id = bc->l3_id;
+ ch->addr = bc->addr;
+
+ start_bc_tones(ch);
+
+ ch->state = MISDN_CONNECTED;
+ }
+ break;
+ case EVENT_DISCONNECT:
+ /*we might not have an ch->ast ptr here anymore*/
+ if (ch) {
+ struct chan_list *holded_ch = find_holded(cl_te, bc);
+
+ chan_misdn_log(3, bc->port, " --> org:%d nt:%d, inbandavail:%d state:%d\n", ch->originator, bc->nt, misdn_inband_avail(bc), ch->state);
+ if (ch->originator == ORG_AST && !bc->nt && misdn_inband_avail(bc) && ch->state != MISDN_CONNECTED) {
+ /* If there's inband information available (e.g. a
+ recorded message saying what was wrong with the
+ dialled number, or perhaps even giving an
+ alternative number, then play it instead of
+ immediately releasing the call */
+ chan_misdn_log(1, bc->port, " --> Inband Info Avail, not sending RELEASE\n");
+
+ ch->state = MISDN_DISCONNECTED;
+ start_bc_tones(ch);
+
+ if (ch->ast) {
+ ch->ast->hangupcause = bc->cause;
+ if (bc->cause == AST_CAUSE_USER_BUSY)
+ ast_queue_control(ch->ast, AST_CONTROL_BUSY);
+ }
+ ch->need_busy = 0;
+ break;
+ }
+
+ /*Check for holded channel, to implement transfer*/
+ if (holded_ch && holded_ch != ch && ch->ast && ch->state == MISDN_CONNECTED) {
+ cb_log(1, bc->port, " --> found holded ch\n");
+ misdn_transfer_bc(ch, holded_ch) ;
+ }
+
+ bc->need_disconnect = 0;
+
+ stop_bc_tones(ch);
+ hangup_chan(ch);
+#if 0
+ } else {
+ ch = find_holded_l3(cl_te, bc->l3_id,1);
+ if (ch) {
+ hangup_chan(ch);
+ }
+#endif
+ }
+ bc->out_cause = -1;
+ if (bc->need_release)
+ misdn_lib_send_event(bc, EVENT_RELEASE);
+ break;
+
+ case EVENT_RELEASE:
+ {
+ bc->need_disconnect = 0;
+ bc->need_release = 0;
+
+ hangup_chan(ch);
+ release_chan(bc);
+ }
+ break;
+ case EVENT_RELEASE_COMPLETE:
+ {
+ bc->need_disconnect = 0;
+ bc->need_release = 0;
+ bc->need_release_complete = 0;
+
+ stop_bc_tones(ch);
+ hangup_chan(ch);
+
+ if (ch)
+ ch->state = MISDN_CLEANING;
+
+ release_chan(bc);
+ }
+ break;
+ case EVENT_BCHAN_ERROR:
+ case EVENT_CLEANUP:
+ {
+ stop_bc_tones(ch);
+
+ switch (ch->state) {
+ case MISDN_CALLING:
+ bc->cause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
+ break;
+ default:
+ break;
+ }
+
+ hangup_chan(ch);
+ release_chan(bc);
+ }
+ break;
+
+ case EVENT_TONE_GENERATE:
+ {
+ int tone_len = bc->tone_cnt;
+ struct ast_channel *ast = ch->ast;
+ void *tmp;
+ int res;
+ int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
+
+ chan_misdn_log(9, bc->port, "TONE_GEN: len:%d\n");
+
+ if (!ast)
+ break;
+
+ if (!ast->generator)
+ break;
+
+ tmp = ast->generatordata;
+ ast->generatordata = NULL;
+ generate = ast->generator->generate;
+
+ if (tone_len < 0 || tone_len > 512 ) {
+ ast_log(LOG_NOTICE, "TONE_GEN: len was %d, set to 128\n", tone_len);
+ tone_len = 128;
+ }
+
+ res = generate(ast, tmp, tone_len, tone_len);
+ ast->generatordata = tmp;
+
+ if (res) {
+ ast_log(LOG_WARNING, "Auto-deactivating generator\n");
+ ast_deactivate_generator(ast);
+ } else {
+ bc->tone_cnt = 0;
+ }
+ }
+ break;
+
+ case EVENT_BCHAN_DATA:
+ {
+ if (ch->bc->AOCD_need_export)
+ export_aoc_vars(ch->originator, ch->ast, ch->bc);
+ if (!misdn_cap_is_speech(ch->bc->capability) ) {
+ struct ast_frame frame;
+ /*In Data Modes we queue frames*/
+ frame.frametype = AST_FRAME_VOICE; /*we have no data frames yet*/
+ frame.subclass = AST_FORMAT_ALAW;
+ frame.datalen = bc->bframe_len;
+ frame.samples = bc->bframe_len;
+ frame.mallocd = 0;
+ frame.offset = 0;
+ frame.delivery = ast_tv(0,0);
+ frame.src = NULL;
+ frame.data = bc->bframe;
+
+ if (ch->ast)
+ ast_queue_frame(ch->ast, &frame);
+ } else {
+ fd_set wrfs;
+ struct timeval tv = { 0, 0 };
+ int t;
+
+ FD_ZERO(&wrfs);
+ FD_SET(ch->pipe[1], &wrfs);
+
+ t = select(FD_SETSIZE, NULL, &wrfs, NULL, &tv);
+
+ if (!t) {
+ chan_misdn_log(9, bc->port, "Select Timed out\n");
+ break;
+ }
+
+ if (t < 0) {
+ chan_misdn_log(-1, bc->port, "Select Error (err=%s)\n", strerror(errno));
+ break;
+ }
+
+ if (FD_ISSET(ch->pipe[1], &wrfs)) {
+ chan_misdn_log(9, bc->port, "writing %d bytes 2 asterisk\n", bc->bframe_len);
+ if (write(ch->pipe[1], bc->bframe, bc->bframe_len) <= 0) {
+ chan_misdn_log(0, bc->port, "Write returned <=0 (err=%s) --> hanging up channel\n", strerror(errno));
+
+ stop_bc_tones(ch);
+ hangup_chan(ch);
+ release_chan(bc);
+ }
+ } else {
+ chan_misdn_log(1, bc->port, "Write Pipe full!\n");
+ }
+ }
+ }
+ break;
+ case EVENT_TIMEOUT:
+ {
+ if (ch && bc)
+ chan_misdn_log(1, bc->port, "--> state: %s\n", misdn_get_ch_state(ch));
+
+ switch (ch->state) {
+ case MISDN_DIALING:
+ case MISDN_PROGRESS:
+ if (bc->nt && !ch->nttimeout)
+ break;
+
+ case MISDN_CALLING:
+ case MISDN_ALERTING:
+ case MISDN_PROCEEDING:
+ case MISDN_CALLING_ACKNOWLEDGE:
+ if (bc->nt) {
+ bc->progress_indicator = 8;
+ hanguptone_indicate(ch);
+ }
+
+ bc->out_cause = AST_CAUSE_UNALLOCATED;
+ misdn_lib_send_event(bc, EVENT_DISCONNECT);
+ break;
+
+ case MISDN_WAITING4DIGS:
+ if (bc->nt) {
+ bc->progress_indicator = 8;
+ bc->out_cause = AST_CAUSE_UNALLOCATED;
+ hanguptone_indicate(ch);
+ misdn_lib_send_event(bc, EVENT_DISCONNECT);
+ } else {
+ bc->out_cause = AST_CAUSE_NORMAL_CLEARING;
+ misdn_lib_send_event(bc, EVENT_RELEASE);
+ }
+
+ break;
+
+ case MISDN_CLEANING:
+ chan_misdn_log(1,bc->port," --> in state cleaning .. so ingoring, the stack should clean it for us\n");
+ break;
+
+ default:
+ misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE);
+ }
+ }
+ break;
+
+
+ /***************************/
+ /** Suplementary Services **/
+ /***************************/
+ case EVENT_RETRIEVE:
+ {
+ struct ast_channel *hold_ast;
+
+ if (!ch) {
+ chan_misdn_log(4, bc->port, " --> no CH, searching in holded\n");
+ ch = find_holded_l3(cl_te, bc->l3_id, 1);
+ }
+
+ if (!ch) {
+ ast_log(LOG_WARNING, "Found no Holded channel, cannot Retrieve\n");
+ misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT);
+ break;
+ }
+
+ /*remember the channel again*/
+ ch->bc = bc;
+ ch->state = MISDN_CONNECTED;
+
+ ch->hold_info.port = 0;
+ ch->hold_info.channel = 0;
+
+ hold_ast = ast_bridged_channel(ch->ast);
+
+ if (hold_ast) {
+ ast_moh_stop(hold_ast);
+ }
+
+ if (misdn_lib_send_event(bc, EVENT_RETRIEVE_ACKNOWLEDGE) < 0) {
+ chan_misdn_log(4, bc->port, " --> RETRIEVE_ACK failed\n");
+ misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT);
+ }
+ }
+ break;
+
+ case EVENT_HOLD:
+ {
+ int hold_allowed;
+ struct ast_channel *bridged = ast_bridged_channel(ch->ast);
+
+ misdn_cfg_get(bc->port, MISDN_CFG_HOLD_ALLOWED, &hold_allowed, sizeof(hold_allowed));
+
+ if (!hold_allowed) {
+
+ chan_misdn_log(-1, bc->port, "Hold not allowed this port.\n");
+ misdn_lib_send_event(bc, EVENT_HOLD_REJECT);
+ break;
+ }
+
+ if (bridged) {
+ chan_misdn_log(2, bc->port, "Bridge Partner is of type: %s\n", bridged->tech->type);
+ ch->state = MISDN_HOLDED;
+ ch->l3id = bc->l3_id;
+
+ misdn_lib_send_event(bc, EVENT_HOLD_ACKNOWLEDGE);
+
+ /* XXX This should queue an AST_CONTROL_HOLD frame on this channel
+ * instead of starting moh on the bridged channel directly */
+ ast_moh_start(bridged, NULL, NULL);
+
+ /*forget the channel now*/
+ ch->bc = NULL;
+ ch->hold_info.port = bc->port;
+ ch->hold_info.channel = bc->channel;
+
+ } else {
+ misdn_lib_send_event(bc, EVENT_HOLD_REJECT);
+ chan_misdn_log(0, bc->port, "We aren't bridged to anybody\n");
+ }
+ }
+ break;
+
+ case EVENT_FACILITY:
+ print_facility(&(bc->fac_in), bc);
+
+ switch (bc->fac_in.Function) {
+#ifdef HAVE_MISDN_FAC_RESULT
+ case Fac_RESULT:
+ break;
+#endif
+ case Fac_CD:
+ if (ch) {
+ struct ast_channel *bridged = ast_bridged_channel(ch->ast);
+ struct chan_list *ch_br;
+ if (bridged && MISDN_ASTERISK_TECH_PVT(bridged)) {
+ ch_br = MISDN_ASTERISK_TECH_PVT(bridged);
+ /*ch->state = MISDN_FACILITY_DEFLECTED;*/
+ if (ch_br->bc) {
+ if (ast_exists_extension(bridged, ch->context, (char *)bc->fac_in.u.CDeflection.DeflectedToNumber, 1, bc->oad)) {
+ ch_br->state = MISDN_DIALING;
+ if (pbx_start_chan(ch_br) < 0) {
+ chan_misdn_log(-1, ch_br->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n");
+ }
+ }
+ }
+ }
+ misdn_lib_send_event(bc, EVENT_DISCONNECT);
+ }
+ break;
+ case Fac_AOCDCurrency:
+ if (ch) {
+ bc->AOCDtype = Fac_AOCDCurrency;
+ memcpy(&(bc->AOCD.currency), &(bc->fac_in.u.AOCDcur), sizeof(bc->AOCD.currency));
+ bc->AOCD_need_export = 1;
+ export_aoc_vars(ch->originator, ch->ast, bc);
+ }
+ break;
+ case Fac_AOCDChargingUnit:
+ if (ch) {
+ bc->AOCDtype = Fac_AOCDChargingUnit;
+ memcpy(&(bc->AOCD.chargingUnit), &(bc->fac_in.u.AOCDchu), sizeof(bc->AOCD.chargingUnit));
+ bc->AOCD_need_export = 1;
+ export_aoc_vars(ch->originator, ch->ast, bc);
+ }
+ break;
+ case Fac_None:
+#ifdef HAVE_MISDN_FAC_ERROR
+ case Fac_ERROR:
+#endif
+ break;
+ default:
+ chan_misdn_log(0, bc->port," --> not yet handled: facility type:%p\n", bc->fac_in.Function);
+ }
+
+ break;
+
+ case EVENT_RESTART:
+
+ if (!bc->dummy) {
+ stop_bc_tones(ch);
+ release_chan(bc);
+ }
+ break;
+
+ default:
+ chan_misdn_log(1, 0, "Got Unknown Event\n");
+ break;
+ }
+
+ return RESPONSE_OK;
+}
+
+/** TE STUFF END **/
+
+/******************************************
+ *
+ * Asterisk Channel Endpoint END
+ *
+ *
+ *******************************************/
+
+
+
+static int unload_module(void)
+{
+ /* First, take us out of the channel loop */
+ ast_log(LOG_VERBOSE, "-- Unregistering mISDN Channel Driver --\n");
+
+ misdn_tasks_destroy();
+
+ if (!g_config_initialized)
+ return 0;
+
+ ast_cli_unregister_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry));
+
+ /* ast_unregister_application("misdn_crypt"); */
+ ast_unregister_application("misdn_set_opt");
+ ast_unregister_application("misdn_facility");
+ ast_unregister_application("misdn_check_l2l1");
+
+ ast_channel_unregister(&misdn_tech);
+
+ free_robin_list();
+ misdn_cfg_destroy();
+ misdn_lib_destroy();
+
+ if (misdn_debug)
+ ast_free(misdn_debug);
+ if (misdn_debug_only)
+ ast_free(misdn_debug_only);
+ ast_free(misdn_ports);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ int i, port;
+ int ntflags = 0, ntkc = 0;
+ char ports[256] = "";
+ char tempbuf[BUFFERSIZE + 1];
+ char ntfile[BUFFERSIZE + 1];
+ struct misdn_lib_iface iface = {
+ .cb_event = cb_events,
+ .cb_log = chan_misdn_log,
+ .cb_jb_empty = chan_misdn_jb_empty,
+ };
+
+ max_ports = misdn_lib_maxports_get();
+
+ if (max_ports <= 0) {
+ ast_log(LOG_ERROR, "Unable to initialize mISDN\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (misdn_cfg_init(max_ports, 0)) {
+ ast_log(LOG_ERROR, "Unable to initialize misdn_config.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ g_config_initialized = 1;
+
+ misdn_debug = ast_malloc(sizeof(int) * (max_ports + 1));
+ if (!misdn_debug) {
+ ast_log(LOG_ERROR, "Out of memory for misdn_debug\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ misdn_ports = ast_malloc(sizeof(int) * (max_ports + 1));
+ if (!misdn_ports) {
+ ast_log(LOG_ERROR, "Out of memory for misdn_ports\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ misdn_cfg_get(0, MISDN_GEN_DEBUG, &misdn_debug[0], sizeof(misdn_debug[0]));
+ for (i = 1; i <= max_ports; i++) {
+ misdn_debug[i] = misdn_debug[0];
+ misdn_ports[i] = i;
+ }
+ *misdn_ports = 0;
+ misdn_debug_only = ast_calloc(max_ports + 1, sizeof(int));
+
+ misdn_cfg_get(0, MISDN_GEN_TRACEFILE, tempbuf, sizeof(tempbuf));
+ if (!ast_strlen_zero(tempbuf))
+ tracing = 1;
+
+ misdn_in_calls = ast_malloc(sizeof(int) * (max_ports + 1));
+ misdn_out_calls = ast_malloc(sizeof(int) * (max_ports + 1));
+
+ for (i = 1; i <= max_ports; i++) {
+ misdn_in_calls[i] = 0;
+ misdn_out_calls[i] = 0;
+ }
+
+ ast_mutex_init(&cl_te_lock);
+ ast_mutex_init(&release_lock);
+
+ misdn_cfg_update_ptp();
+ misdn_cfg_get_ports_string(ports);
+
+ if (!ast_strlen_zero(ports))
+ chan_misdn_log(0, 0, "Got: %s from get_ports\n", ports);
+ if (misdn_lib_init(ports, &iface, NULL))
+ chan_misdn_log(0, 0, "No te ports initialized\n");
+
+ misdn_cfg_get(0, MISDN_GEN_NTDEBUGFLAGS, &ntflags, sizeof(ntflags));
+ misdn_cfg_get(0, MISDN_GEN_NTDEBUGFILE, &ntfile, sizeof(ntfile));
+ misdn_cfg_get( 0, MISDN_GEN_NTKEEPCALLS, &ntkc, sizeof(ntkc));
+
+ misdn_lib_nt_keepcalls(ntkc);
+ misdn_lib_nt_debug_init(ntflags, ntfile);
+
+ if (ast_channel_register(&misdn_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", misdn_type);
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ ast_cli_register_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry));
+
+ ast_register_application("misdn_set_opt", misdn_set_opt_exec, "misdn_set_opt",
+ "misdn_set_opt(:<opt><optarg>:<opt><optarg>..):\n"
+ "Sets mISDN opts. and optargs\n"
+ "\n"
+ "The available options are:\n"
+ " d - Send display text on called phone, text is the optparam\n"
+ " n - don't detect dtmf tones on called channel\n"
+ " h - make digital outgoing call\n"
+ " c - make crypted outgoing call, param is keyindex\n"
+ " e - perform echo cancelation on this channel,\n"
+ " takes taps as arguments (32,64,128,256)\n"
+ " s - send Non Inband DTMF as inband\n"
+ " vr - rxgain control\n"
+ " vt - txgain control\n"
+ " i - Ignore detected dtmf tones, don't signal them to asterisk, they will be transported inband.\n"
+ );
+
+
+ ast_register_application("misdn_facility", misdn_facility_exec, "misdn_facility",
+ "misdn_facility(<FACILITY_TYPE>|<ARG1>|..)\n"
+ "Sends the Facility Message FACILITY_TYPE with \n"
+ "the given Arguments to the current ISDN Channel\n"
+ "Supported Facilities are:\n"
+ "\n"
+ "type=calldeflect args=Nr where to deflect\n"
+ );
+
+
+ ast_register_application("misdn_check_l2l1", misdn_check_l2l1, "misdn_check_l2l1",
+ "misdn_check_l2l1(<port>||g:<groupname>,timeout)"
+ "Checks if the L2 and L1 are up on either the given <port> or\n"
+ "on the ports in the group with <groupname>\n"
+ "If the L1/L2 are down, check_l2l1 gets up the L1/L2 and waits\n"
+ "for <timeout> seconds that this happens. Otherwise, nothing happens\n"
+ "\n"
+ "This application, ensures the L1/L2 state of the Ports in a group\n"
+ "it is intended to make the pmp_l1_check option redundant and to\n"
+ "fix a buggy switch config from your provider\n"
+ "\n"
+ "a sample dialplan would look like:\n\n"
+ "exten => _X.,1,misdn_check_l2l1(g:out|2)\n"
+ "exten => _X.,n,dial(mISDN/g:out/${EXTEN})\n"
+ "\n"
+ );
+
+
+ misdn_cfg_get(0, MISDN_GEN_TRACEFILE, global_tracefile, sizeof(global_tracefile));
+
+ /* start the l1 watchers */
+
+ for (port = misdn_cfg_get_next_port(0); port >= 0; port = misdn_cfg_get_next_port(port)) {
+ int l1timeout;
+ misdn_cfg_get(port, MISDN_CFG_L1_TIMEOUT, &l1timeout, sizeof(l1timeout));
+ if (l1timeout) {
+ chan_misdn_log(4, 0, "Adding L1watcher task: port:%d timeout:%ds\n", port, l1timeout);
+ misdn_tasks_add(l1timeout * 1000, misdn_l1_task, &misdn_ports[port]);
+ }
+ }
+
+ chan_misdn_log(0, 0, "-- mISDN Channel Driver Registered --\n");
+
+ return 0;
+}
+
+
+
+static int reload(void)
+{
+ reload_config();
+
+ return 0;
+}
+
+/*** SOME APPS ;)***/
+
+static int misdn_facility_exec(struct ast_channel *chan, void *data)
+{
+ struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan);
+ char *parse, *tok, *tokb;
+
+ chan_misdn_log(0, 0, "TYPE: %s\n", chan->tech->type);
+
+ if (strcasecmp(chan->tech->type, "mISDN")) {
+ ast_log(LOG_WARNING, "misdn_facility makes only sense with chan_misdn channels!\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero((char *)data)) {
+ ast_log(LOG_WARNING, "misdn_facility Requires arguments\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ tok = strtok_r(parse, "|", &tokb) ;
+
+ if (!tok) {
+ ast_log(LOG_WARNING, "misdn_facility Requires arguments\n");
+ return -1;
+ }
+
+ if (!strcasecmp(tok, "calldeflect")) {
+ tok = strtok_r(NULL, "|", &tokb) ;
+
+ if (!tok) {
+ ast_log(LOG_WARNING, "Facility: Call Defl Requires arguments\n");
+ }
+
+ if (strlen(tok) >= sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)) {
+ ast_log(LOG_WARNING, "Facility: Number argument too long (up to 15 digits are allowed). Ignoring.\n");
+ return 0;
+ }
+ ch->bc->fac_out.Function = Fac_CD;
+ ast_copy_string((char *)ch->bc->fac_out.u.CDeflection.DeflectedToNumber, tok, sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber));
+ misdn_lib_send_event(ch->bc, EVENT_FACILITY);
+ } else {
+ chan_misdn_log(1, ch->bc->port, "Unknown Facility: %s\n", tok);
+ }
+
+ return 0;
+}
+
+static int misdn_check_l2l1(struct ast_channel *chan, void *data)
+{
+ char *parse;
+ char group[BUFFERSIZE + 1];
+ char *port_str;
+ int port = 0;
+ int timeout;
+ int dowait = 0;
+ int port_up;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(grouppar);
+ AST_APP_ARG(timeout);
+ );
+
+ if (ast_strlen_zero((char *)data)) {
+ ast_log(LOG_WARNING, "misdn_check_l2l1 Requires arguments\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc != 2) {
+ ast_log(LOG_WARNING, "Wrong argument count\n");
+ return 0;
+ }
+
+ /*ast_log(LOG_NOTICE, "Arguments: group/port '%s' timeout '%s'\n", args.grouppar, args.timeout);*/
+ timeout = atoi(args.timeout);
+ port_str = args.grouppar;
+
+ if (port_str[0] == 'g' && port_str[1] == ':' ) {
+ /* We make a group call lets checkout which ports are in my group */
+ port_str += 2;
+ ast_copy_string(group, port_str, sizeof(group));
+ chan_misdn_log(2, 0, "Checking Ports in group: %s\n", group);
+
+ for ( port = misdn_cfg_get_next_port(port);
+ port > 0;
+ port = misdn_cfg_get_next_port(port)) {
+ char cfg_group[BUFFERSIZE + 1];
+
+ chan_misdn_log(2, 0, "trying port %d\n", port);
+
+ misdn_cfg_get(port, MISDN_CFG_GROUPNAME, cfg_group, sizeof(cfg_group));
+
+ if (!strcasecmp(cfg_group, group)) {
+ port_up = misdn_lib_port_up(port, 1);
+
+ if (!port_up) {
+ chan_misdn_log(2, 0, " --> port '%d'\n", port);
+ misdn_lib_get_port_up(port);
+ dowait = 1;
+ }
+ }
+ }
+
+ } else {
+ port = atoi(port_str);
+ chan_misdn_log(2, 0, "Checking Port: %d\n",port);
+ port_up = misdn_lib_port_up(port, 1);
+ if (!port_up) {
+ misdn_lib_get_port_up(port);
+ dowait = 1;
+ }
+ }
+
+ if (dowait) {
+ chan_misdn_log(2, 0, "Waiting for '%d' seconds\n", timeout);
+ ast_safe_sleep(chan, timeout * 1000);
+ }
+
+ return 0;
+}
+
+static int misdn_set_opt_exec(struct ast_channel *chan, void *data)
+{
+ struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan);
+ char *tok, *tokb, *parse;
+ int keyidx = 0;
+ int rxgain = 0;
+ int txgain = 0;
+ int change_jitter = 0;
+
+ if (strcasecmp(chan->tech->type, "mISDN")) {
+ ast_log(LOG_WARNING, "misdn_set_opt makes only sense with chan_misdn channels!\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero((char *)data)) {
+ ast_log(LOG_WARNING, "misdn_set_opt Requires arguments\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ for (tok = strtok_r(parse, ":", &tokb);
+ tok;
+ tok = strtok_r(NULL, ":", &tokb) ) {
+ int neglect = 0;
+
+ if (tok[0] == '!' ) {
+ neglect = 1;
+ tok++;
+ }
+
+ switch(tok[0]) {
+
+ case 'd' :
+ ast_copy_string(ch->bc->display, ++tok, sizeof(ch->bc->display));
+ chan_misdn_log(1, ch->bc->port, "SETOPT: Display:%s\n", ch->bc->display);
+ break;
+
+ case 'n':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: No DSP\n");
+ ch->bc->nodsp = 1;
+ break;
+
+ case 'j':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: jitter\n");
+ tok++;
+ change_jitter = 1;
+
+ switch ( tok[0] ) {
+ case 'b':
+ ch->jb_len = atoi(++tok);
+ chan_misdn_log(1, ch->bc->port, " --> buffer_len:%d\n", ch->jb_len);
+ break;
+ case 't' :
+ ch->jb_upper_threshold = atoi(++tok);
+ chan_misdn_log(1, ch->bc->port, " --> upper_threshold:%d\n", ch->jb_upper_threshold);
+ break;
+ case 'n':
+ ch->bc->nojitter = 1;
+ chan_misdn_log(1, ch->bc->port, " --> nojitter\n");
+ break;
+ default:
+ ch->jb_len = 4000;
+ ch->jb_upper_threshold = 0;
+ chan_misdn_log(1, ch->bc->port, " --> buffer_len:%d (default)\n", ch->jb_len);
+ chan_misdn_log(1, ch->bc->port, " --> upper_threshold:%d (default)\n", ch->jb_upper_threshold);
+ }
+ break;
+ case 'v':
+ tok++;
+
+ switch (tok[0]) {
+ case 'r' :
+ rxgain = atoi(++tok);
+ if (rxgain < -8)
+ rxgain = -8;
+ if (rxgain > 8)
+ rxgain = 8;
+ ch->bc->rxgain = rxgain;
+ chan_misdn_log(1, ch->bc->port, "SETOPT: Volume:%d\n", rxgain);
+ break;
+ case 't':
+ txgain = atoi(++tok);
+ if (txgain < -8)
+ txgain = -8;
+ if (txgain > 8)
+ txgain = 8;
+ ch->bc->txgain = txgain;
+ chan_misdn_log(1, ch->bc->port, "SETOPT: Volume:%d\n", txgain);
+ break;
+ }
+ break;
+
+ case 'c':
+ keyidx = atoi(++tok);
+ {
+ char keys[4096];
+ char *key = NULL, *tmp = keys;
+ int i;
+ misdn_cfg_get(0, MISDN_GEN_CRYPT_KEYS, keys, sizeof(keys));
+
+ for (i = 0; i < keyidx; i++) {
+ key = strsep(&tmp, ",");
+ }
+
+ if (key) {
+ ast_copy_string(ch->bc->crypt_key, key, sizeof(ch->bc->crypt_key));
+ }
+
+ chan_misdn_log(0, ch->bc->port, "SETOPT: crypt with key:%s\n", ch->bc->crypt_key);
+ break;
+ }
+ case 'e':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: EchoCancel\n");
+
+ if (neglect) {
+ chan_misdn_log(1, ch->bc->port, " --> disabled\n");
+#ifdef MISDN_1_2
+ *ch->bc->pipeline = 0;
+#else
+ ch->bc->ec_enable = 0;
+#endif
+ } else {
+#ifdef MISDN_1_2
+ update_pipeline_config(ch->bc);
+#else
+ ch->bc->ec_enable = 1;
+ ch->bc->orig = ch->originator;
+ tok++;
+ if (*tok) {
+ ch->bc->ec_deftaps = atoi(tok);
+ }
+#endif
+ }
+
+ break;
+ case 'h':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: Digital\n");
+
+ if (strlen(tok) > 1 && tok[1] == '1') {
+ chan_misdn_log(1, ch->bc->port, "SETOPT: HDLC \n");
+ if (!ch->bc->hdlc) {
+ ch->bc->hdlc = 1;
+ }
+ }
+ ch->bc->capability = INFO_CAPABILITY_DIGITAL_UNRESTRICTED;
+ break;
+
+ case 's':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: Send DTMF\n");
+ ch->bc->send_dtmf = 1;
+ break;
+
+ case 'f':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: Faxdetect\n");
+ ch->faxdetect = 1;
+ misdn_cfg_get(ch->bc->port, MISDN_CFG_FAXDETECT_TIMEOUT, &ch->faxdetect_timeout, sizeof(ch->faxdetect_timeout));
+ break;
+
+ case 'a':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: AST_DSP (for DTMF)\n");
+ ch->ast_dsp = 1;
+ break;
+
+ case 'p':
+ chan_misdn_log(1, ch->bc->port, "SETOPT: callerpres: %s\n", &tok[1]);
+ /* CRICH: callingpres!!! */
+ if (strstr(tok,"allowed")) {
+ ch->bc->pres = 0;
+ } else if (strstr(tok, "not_screened")) {
+ ch->bc->pres = 1;
+ }
+ break;
+ case 'i' :
+ chan_misdn_log(1, ch->bc->port, "Ignoring dtmf tones, just use them inband\n");
+ ch->ignore_dtmf=1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (change_jitter)
+ config_jitterbuffer(ch);
+
+ if (ch->faxdetect || ch->ast_dsp) {
+ if (!ch->dsp)
+ ch->dsp = ast_dsp_new();
+ if (ch->dsp)
+ ast_dsp_set_features(ch->dsp, DSP_FEATURE_DTMF_DETECT | DSP_FEATURE_FAX_DETECT);
+ if (!ch->trans)
+ ch->trans = ast_translator_build_path(AST_FORMAT_SLINEAR, AST_FORMAT_ALAW);
+ }
+
+ if (ch->ast_dsp) {
+ chan_misdn_log(1, ch->bc->port, "SETOPT: with AST_DSP we deactivate mISDN_dsp\n");
+ ch->bc->nodsp = 1;
+ ch->bc->nojitter = 1;
+ }
+
+ return 0;
+}
+
+
+int chan_misdn_jb_empty ( struct misdn_bchannel *bc, char *buf, int len)
+{
+ struct chan_list *ch = find_chan_by_bc(cl_te, bc);
+
+ if (ch && ch->jb) {
+ return misdn_jb_empty(ch->jb, buf, len);
+ }
+
+ return -1;
+}
+
+
+
+/*******************************************************/
+/***************** JITTERBUFFER ************************/
+/*******************************************************/
+
+
+/* allocates the jb-structure and initialise the elements*/
+struct misdn_jb *misdn_jb_init(int size, int upper_threshold)
+{
+ int i;
+ struct misdn_jb *jb;
+
+ jb = ast_malloc(sizeof(*jb));
+ if (!jb) {
+ chan_misdn_log(-1, 0, "No free Mem for jb\n");
+ return NULL;
+ }
+ jb->size = size;
+ jb->upper_threshold = upper_threshold;
+ jb->wp = 0;
+ jb->rp = 0;
+ jb->state_full = 0;
+ jb->state_empty = 0;
+ jb->bytes_wrote = 0;
+ jb->samples = ast_malloc(size * sizeof(char));
+
+ if (!jb->samples) {
+ ast_free(jb);
+ chan_misdn_log(-1, 0, "No free Mem for jb->samples\n");
+ return NULL;
+ }
+
+ jb->ok = ast_malloc(size * sizeof(char));
+
+ if (!jb->ok) {
+ ast_free(jb->samples);
+ ast_free(jb);
+ chan_misdn_log(-1, 0, "No free Mem for jb->ok\n");
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++)
+ jb->ok[i] = 0;
+
+ ast_mutex_init(&jb->mutexjb);
+
+ return jb;
+}
+
+/* frees the data and destroys the given jitterbuffer struct */
+void misdn_jb_destroy(struct misdn_jb *jb)
+{
+ ast_mutex_destroy(&jb->mutexjb);
+
+ ast_free(jb->samples);
+ ast_free(jb);
+}
+
+/* fills the jitterbuffer with len data returns < 0 if there was an
+ error (bufferoverflow). */
+int misdn_jb_fill(struct misdn_jb *jb, const char *data, int len)
+{
+ int i, j, rp, wp;
+
+ if (!jb || ! data)
+ return 0;
+
+ ast_mutex_lock(&jb->mutexjb);
+
+ wp = jb->wp;
+ rp = jb->rp;
+
+ for (i = 0; i < len; i++) {
+ jb->samples[wp] = data[i];
+ jb->ok[wp] = 1;
+ wp = (wp != jb->size - 1) ? wp + 1 : 0;
+
+ if (wp == jb->rp)
+ jb->state_full = 1;
+ }
+
+ if (wp >= rp)
+ jb->state_buffer = wp - rp;
+ else
+ jb->state_buffer = jb->size - rp + wp;
+ chan_misdn_log(9, 0, "misdn_jb_fill: written:%d | Bufferstatus:%d p:%x\n", len, jb->state_buffer, jb);
+
+ if (jb->state_full) {
+ jb->wp = wp;
+
+ rp = wp;
+ for (j = 0; j < jb->upper_threshold; j++)
+ rp = rp != 0 ? rp - 1 : jb->size - 1;
+ jb->rp = rp;
+ jb->state_full = 0;
+ jb->state_empty = 1;
+
+ ast_mutex_unlock(&jb->mutexjb);
+
+ return -1;
+ }
+
+ if (!jb->state_empty) {
+ jb->bytes_wrote += len;
+ if (jb->bytes_wrote >= jb->upper_threshold) {
+ jb->state_empty = 1;
+ jb->bytes_wrote = 0;
+ }
+ }
+ jb->wp = wp;
+
+ ast_mutex_unlock(&jb->mutexjb);
+
+ return 0;
+}
+
+/* gets len bytes out of the jitterbuffer if available, else only the
+available data is returned and the return value indicates the number
+of data. */
+int misdn_jb_empty(struct misdn_jb *jb, char *data, int len)
+{
+ int i, wp, rp, read = 0;
+
+ ast_mutex_lock(&jb->mutexjb);
+
+ rp = jb->rp;
+ wp = jb->wp;
+
+ if (jb->state_empty) {
+ for (i = 0; i < len; i++) {
+ if (wp == rp) {
+ jb->rp = rp;
+ jb->state_empty = 0;
+
+ ast_mutex_unlock(&jb->mutexjb);
+
+ return read;
+ } else {
+ if (jb->ok[rp] == 1) {
+ data[i] = jb->samples[rp];
+ jb->ok[rp] = 0;
+ rp = (rp != jb->size - 1) ? rp + 1 : 0;
+ read += 1;
+ }
+ }
+ }
+
+ if (wp >= rp)
+ jb->state_buffer = wp - rp;
+ else
+ jb->state_buffer = jb->size - rp + wp;
+ chan_misdn_log(9, 0, "misdn_jb_empty: read:%d | Bufferstatus:%d p:%x\n", len, jb->state_buffer, jb);
+
+ jb->rp = rp;
+ } else
+ chan_misdn_log(9, 0, "misdn_jb_empty: Wait...requested:%d p:%x\n", len, jb);
+
+ ast_mutex_unlock(&jb->mutexjb);
+
+ return read;
+}
+
+
+
+
+/*******************************************************/
+/*************** JITTERBUFFER END *********************/
+/*******************************************************/
+
+
+
+
+void chan_misdn_log(int level, int port, char *tmpl, ...)
+{
+ va_list ap;
+ char buf[1024];
+ char port_buf[8];
+
+ if (! ((0 <= port) && (port <= max_ports))) {
+ ast_log(LOG_WARNING, "cb_log called with out-of-range port number! (%d)\n", port);
+ port = 0;
+ level = -1;
+ }
+
+ snprintf(port_buf, sizeof(port_buf), "P[%2d] ", port);
+
+ va_start(ap, tmpl);
+ vsnprintf(buf, sizeof(buf), tmpl, ap);
+ va_end(ap);
+
+ if (level == -1)
+ ast_log(LOG_WARNING, buf);
+
+ else if (misdn_debug_only[port] ?
+ (level == 1 && misdn_debug[port]) || (level == misdn_debug[port])
+ : level <= misdn_debug[port]) {
+
+ ast_console_puts(port_buf);
+ ast_console_puts(buf);
+ }
+
+ if ((level <= misdn_debug[0]) && !ast_strlen_zero(global_tracefile) ) {
+ char ctimebuf[30];
+ time_t tm = time(NULL);
+ char *tmp = ctime_r(&tm, ctimebuf), *p;
+
+ FILE *fp = fopen(global_tracefile, "a+");
+
+ p = strchr(tmp, '\n');
+ if (p)
+ *p = ':';
+
+ if (!fp) {
+ ast_console_puts("Error opening Tracefile: [ ");
+ ast_console_puts(global_tracefile);
+ ast_console_puts(" ] ");
+
+ ast_console_puts(strerror(errno));
+ ast_console_puts("\n");
+ return ;
+ }
+
+ fputs(tmp, fp);
+ fputs(" ", fp);
+ fputs(port_buf, fp);
+ fputs(" ", fp);
+ fputs(buf, fp);
+
+ fclose(fp);
+ }
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Channel driver for mISDN Support (BRI/PRI)",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/trunk/channels/chan_nbs.c b/trunk/channels/chan_nbs.c
new file mode 100644
index 000000000..e190a436c
--- /dev/null
+++ b/trunk/channels/chan_nbs.c
@@ -0,0 +1,292 @@
+/*
+ * 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 Network broadcast sound support channel driver
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>nbs</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <nbs.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+
+static const char tdesc[] = "Network Broadcast Sound Driver";
+
+/* Only linear is allowed */
+static int prefformat = AST_FORMAT_SLINEAR;
+
+static char context[AST_MAX_EXTENSION] = "default";
+static char type[] = "NBS";
+
+/* NBS creates private structures on demand */
+
+struct nbs_pvt {
+ NBS *nbs;
+ struct ast_channel *owner; /* Channel we belong to, possibly NULL */
+ char app[16]; /* Our app */
+ char stream[80]; /* Our stream */
+ struct ast_frame fr; /* "null" frame */
+ struct ast_module_user *u; /*! for holding a reference to this module */
+};
+
+static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause);
+static int nbs_call(struct ast_channel *ast, char *dest, int timeout);
+static int nbs_hangup(struct ast_channel *ast);
+static struct ast_frame *nbs_xread(struct ast_channel *ast);
+static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame);
+
+static const struct ast_channel_tech nbs_tech = {
+ .type = type,
+ .description = tdesc,
+ .capabilities = AST_FORMAT_SLINEAR,
+ .requester = nbs_request,
+ .call = nbs_call,
+ .hangup = nbs_hangup,
+ .read = nbs_xread,
+ .write = nbs_xwrite,
+};
+
+static int nbs_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct nbs_pvt *p;
+
+ p = ast->tech_pvt;
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "nbs_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+ /* When we call, it just works, really, there's no destination... Just
+ ring the phone and wait for someone to answer */
+ ast_debug(1, "Calling %s on %s\n", dest, ast->name);
+
+ /* If we can't connect, return congestion */
+ if (nbs_connect(p->nbs)) {
+ ast_log(LOG_WARNING, "NBS Connection failed on %s\n", ast->name);
+ ast_queue_control(ast, AST_CONTROL_CONGESTION);
+ } else {
+ ast_setstate(ast, AST_STATE_RINGING);
+ ast_queue_control(ast, AST_CONTROL_ANSWER);
+ }
+
+ return 0;
+}
+
+static void nbs_destroy(struct nbs_pvt *p)
+{
+ if (p->nbs)
+ nbs_delstream(p->nbs);
+ ast_module_user_remove(p->u);
+ ast_free(p);
+}
+
+static struct nbs_pvt *nbs_alloc(void *data)
+{
+ struct nbs_pvt *p;
+ int flags = 0;
+ char stream[256];
+ char *opts;
+
+ ast_copy_string(stream, data, sizeof(stream));
+ if ((opts = strchr(stream, ':'))) {
+ *opts = '\0';
+ opts++;
+ } else
+ opts = "";
+ p = ast_calloc(1, sizeof(*p));
+ if (p) {
+ if (!ast_strlen_zero(opts)) {
+ if (strchr(opts, 'm'))
+ flags |= NBS_FLAG_MUTE;
+ if (strchr(opts, 'o'))
+ flags |= NBS_FLAG_OVERSPEAK;
+ if (strchr(opts, 'e'))
+ flags |= NBS_FLAG_EMERGENCY;
+ if (strchr(opts, 'O'))
+ flags |= NBS_FLAG_OVERRIDE;
+ } else
+ flags = NBS_FLAG_OVERSPEAK;
+
+ ast_copy_string(p->stream, stream, sizeof(p->stream));
+ p->nbs = nbs_newstream("asterisk", stream, flags);
+ if (!p->nbs) {
+ ast_log(LOG_WARNING, "Unable to allocate new NBS stream '%s' with flags %d\n", stream, flags);
+ ast_free(p);
+ p = NULL;
+ } else {
+ /* Set for 8000 hz mono, 640 samples */
+ nbs_setbitrate(p->nbs, 8000);
+ nbs_setchannels(p->nbs, 1);
+ nbs_setblocksize(p->nbs, 640);
+ nbs_setblocking(p->nbs, 0);
+ }
+ }
+ return p;
+}
+
+static int nbs_hangup(struct ast_channel *ast)
+{
+ struct nbs_pvt *p;
+ p = ast->tech_pvt;
+ ast_debug(1, "nbs_hangup(%s)\n", ast->name);
+ if (!ast->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ nbs_destroy(p);
+ ast->tech_pvt = NULL;
+ ast_setstate(ast, AST_STATE_DOWN);
+ return 0;
+}
+
+static struct ast_frame *nbs_xread(struct ast_channel *ast)
+{
+ struct nbs_pvt *p = ast->tech_pvt;
+
+
+ /* Some nice norms */
+ p->fr.datalen = 0;
+ p->fr.samples = 0;
+ p->fr.data = NULL;
+ p->fr.src = type;
+ p->fr.offset = 0;
+ p->fr.mallocd=0;
+ p->fr.delivery.tv_sec = 0;
+ p->fr.delivery.tv_usec = 0;
+
+ ast_debug(1, "Returning null frame on %s\n", ast->name);
+
+ return &p->fr;
+}
+
+static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct nbs_pvt *p = ast->tech_pvt;
+ /* Write a frame of (presumably voice) data */
+ if (frame->frametype != AST_FRAME_VOICE) {
+ if (frame->frametype != AST_FRAME_IMAGE)
+ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
+ return 0;
+ }
+ if (!(frame->subclass &
+ (AST_FORMAT_SLINEAR))) {
+ ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
+ return 0;
+ }
+ if (ast->_state != AST_STATE_UP) {
+ /* Don't try tos end audio on-hook */
+ return 0;
+ }
+ if (nbs_write(p->nbs, frame->data, frame->datalen / 2) < 0)
+ return -1;
+ return 0;
+}
+
+static struct ast_channel *nbs_new(struct nbs_pvt *i, int state)
+{
+ struct ast_channel *tmp;
+ tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "NBS/%s", i->stream);
+ if (tmp) {
+ tmp->tech = &nbs_tech;
+ ast_channel_set_fd(tmp, 0, nbs_fd(i->nbs));
+ tmp->nativeformats = prefformat;
+ tmp->rawreadformat = prefformat;
+ tmp->rawwriteformat = prefformat;
+ tmp->writeformat = prefformat;
+ tmp->readformat = prefformat;
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->tech_pvt = i;
+ ast_copy_string(tmp->context, context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, "s", sizeof(tmp->exten));
+ ast_string_field_set(tmp, language, "");
+ i->owner = tmp;
+ i->u = ast_module_user_add(tmp);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ return tmp;
+}
+
+
+static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+ struct nbs_pvt *p;
+ struct ast_channel *tmp = NULL;
+
+ oldformat = format;
+ format &= (AST_FORMAT_SLINEAR);
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+ return NULL;
+ }
+ p = nbs_alloc(data);
+ if (p) {
+ tmp = nbs_new(p, AST_STATE_DOWN);
+ if (!tmp)
+ nbs_destroy(p);
+ }
+ return tmp;
+}
+
+static int unload_module(void)
+{
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&nbs_tech);
+ return 0;
+}
+
+static int load_module(void)
+{
+ /* Make sure we can register our channel type */
+ if (ast_channel_register(&nbs_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ return -1;
+ }
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Network Broadcast Sound Support");
diff --git a/trunk/channels/chan_oss.c b/trunk/channels/chan_oss.c
new file mode 100644
index 000000000..4f40085fa
--- /dev/null
+++ b/trunk/channels/chan_oss.c
@@ -0,0 +1,1470 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * FreeBSD changes and multiple device support by Luigi Rizzo, 2005.05.25
+ * note-this code best seen with ts=8 (8-spaces tabs) in the editor
+ *
+ * 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.
+ */
+
+// #define HAVE_VIDEO_CONSOLE // uncomment to enable video
+/*! \file
+ *
+ * \brief Channel driver for OSS sound cards
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Luigi Rizzo
+ *
+ * \par See also
+ * \arg \ref Config_oss
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>ossaudio</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h> /* isalnum() used here */
+#include <math.h>
+#include <sys/ioctl.h>
+
+#ifdef __linux
+#include <linux/soundcard.h>
+#elif defined(__FreeBSD__) || defined(__CYGWIN__)
+#include <sys/soundcard.h>
+#else
+#include <soundcard.h>
+#endif
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/callerid.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cli.h"
+#include "asterisk/causes.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/app.h"
+
+#include "console_video.h"
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = "",
+};
+static struct ast_jb_conf global_jbconf;
+
+/*
+ * Basic mode of operation:
+ *
+ * we have one keyboard (which receives commands from the keyboard)
+ * and multiple headset's connected to audio cards.
+ * Cards/Headsets are named as the sections of oss.conf.
+ * The section called [general] contains the default parameters.
+ *
+ * At any time, the keyboard is attached to one card, and you
+ * can switch among them using the command 'console foo'
+ * where 'foo' is the name of the card you want.
+ *
+ * oss.conf parameters are
+START_CONFIG
+
+[general]
+ ; General config options, with default values shown.
+ ; You should use one section per device, with [general] being used
+ ; for the first device and also as a template for other devices.
+ ;
+ ; All but 'debug' can go also in the device-specific sections.
+ ;
+ ; debug = 0x0 ; misc debug flags, default is 0
+
+ ; Set the device to use for I/O
+ ; device = /dev/dsp
+
+ ; Optional mixer command to run upon startup (e.g. to set
+ ; volume levels, mutes, etc.
+ ; mixer =
+
+ ; Software mic volume booster (or attenuator), useful for sound
+ ; cards or microphones with poor sensitivity. The volume level
+ ; is in dB, ranging from -20.0 to +20.0
+ ; boost = n ; mic volume boost in dB
+
+ ; Set the callerid for outgoing calls
+ ; callerid = John Doe <555-1234>
+
+ ; autoanswer = no ; no autoanswer on call
+ ; autohangup = yes ; hangup when other party closes
+ ; extension = s ; default extension to call
+ ; context = default ; default context for outgoing calls
+ ; language = "" ; default language
+
+ ; Default Music on Hold class to use when this channel is placed on hold in
+ ; the case that the music class is not set on the channel with
+ ; Set(CHANNEL(musicclass)=whatever) in the dialplan and the peer channel
+ ; putting this one on hold did not suggest a class to use.
+ ;
+ ; mohinterpret=default
+
+ ; If you set overridecontext to 'yes', then the whole dial string
+ ; will be interpreted as an extension, which is extremely useful
+ ; to dial SIP, IAX and other extensions which use the '@' character.
+ ; The default is 'no' just for backward compatibility, but the
+ ; suggestion is to change it.
+ ; overridecontext = no ; if 'no', the last @ will start the context
+ ; if 'yes' the whole string is an extension.
+
+ ; low level device parameters in case you have problems with the
+ ; device driver on your operating system. You should not touch these
+ ; unless you know what you are doing.
+ ; queuesize = 10 ; frames in device driver
+ ; frags = 8 ; argument to SETFRAGMENT
+
+ ;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+ ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
+ ; OSS channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The OSS channel can't accept jitter,
+ ; thus an enabled jitterbuffer on the receive OSS side will always
+ ; be used if the sending side can create jitter.
+
+ ; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+ ; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usualy sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+ ; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of an OSS
+ ; channel. Two implementations are currenlty available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+ ; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+ ;-----------------------------------------------------------------------------------
+
+[card1]
+ ; device = /dev/dsp1 ; alternate device
+
+END_CONFIG
+
+.. and so on for the other cards.
+
+ */
+
+/*
+ * The following parameters are used in the driver:
+ *
+ * FRAME_SIZE the size of an audio frame, in samples.
+ * 160 is used almost universally, so you should not change it.
+ *
+ * FRAGS the argument for the SETFRAGMENT ioctl.
+ * Overridden by the 'frags' parameter in oss.conf
+ *
+ * Bits 0-7 are the base-2 log of the device's block size,
+ * bits 16-31 are the number of blocks in the driver's queue.
+ * There are a lot of differences in the way this parameter
+ * is supported by different drivers, so you may need to
+ * experiment a bit with the value.
+ * A good default for linux is 30 blocks of 64 bytes, which
+ * results in 6 frames of 320 bytes (160 samples).
+ * FreeBSD works decently with blocks of 256 or 512 bytes,
+ * leaving the number unspecified.
+ * Note that this only refers to the device buffer size,
+ * this module will then try to keep the lenght of audio
+ * buffered within small constraints.
+ *
+ * QUEUE_SIZE The max number of blocks actually allowed in the device
+ * driver's buffer, irrespective of the available number.
+ * Overridden by the 'queuesize' parameter in oss.conf
+ *
+ * Should be >=2, and at most as large as the hw queue above
+ * (otherwise it will never be full).
+ */
+
+#define FRAME_SIZE 160
+#define QUEUE_SIZE 10
+
+#if defined(__FreeBSD__)
+#define FRAGS 0x8
+#else
+#define FRAGS ( ( (6 * 5) << 16 ) | 0x6 )
+#endif
+
+/*
+ * XXX text message sizes are probably 256 chars, but i am
+ * not sure if there is a suitable definition anywhere.
+ */
+#define TEXT_SIZE 256
+
+#if 0
+#define TRYOPEN 1 /* try to open on startup */
+#endif
+#define O_CLOSE 0x444 /* special 'close' mode for device */
+/* Which device to use */
+#if defined( __OpenBSD__ ) || defined( __NetBSD__ )
+#define DEV_DSP "/dev/audio"
+#else
+#define DEV_DSP "/dev/dsp"
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+static char *config = "oss.conf"; /* default config file */
+
+static int oss_debug;
+
+/*!
+ * \brief descriptor for one of our channels.
+ *
+ * There is one used for 'default' values (from the [general] entry in
+ * the configuration file), and then one instance for each device
+ * (the default is cloned from [general], others are only created
+ * if the relevant section exists).
+ */
+struct chan_oss_pvt {
+ struct chan_oss_pvt *next;
+
+ char *name;
+ int total_blocks; /*!< total blocks in the output device */
+ int sounddev;
+ enum { M_UNSET, M_FULL, M_READ, M_WRITE } duplex;
+ int autoanswer;
+ int autohangup;
+ int hookstate;
+ char *mixer_cmd; /*!< initial command to issue to the mixer */
+ unsigned int queuesize; /*!< max fragments in queue */
+ unsigned int frags; /*!< parameter for SETFRAGMENT */
+
+ int warned; /*!< various flags used for warnings */
+#define WARN_used_blocks 1
+#define WARN_speed 2
+#define WARN_frag 4
+ int w_errors; /*!< overfull in the write path */
+ struct timeval lastopen;
+
+ int overridecontext;
+ int mute;
+
+ /*! boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must
+ * be representable in 16 bits to avoid overflows.
+ */
+#define BOOST_SCALE (1<<9)
+#define BOOST_MAX 40 /*!< slightly less than 7 bits */
+ int boost; /*!< input boost, scaled by BOOST_SCALE */
+ char device[64]; /*!< device to open */
+
+ pthread_t sthread;
+
+ struct ast_channel *owner;
+
+ struct video_desc *env; /*!< parameters for video support */
+
+ char ext[AST_MAX_EXTENSION];
+ char ctx[AST_MAX_CONTEXT];
+ char language[MAX_LANGUAGE];
+ char cid_name[256]; /*XXX */
+ char cid_num[256]; /*XXX */
+ char mohinterpret[MAX_MUSICCLASS];
+
+ /*! buffers used in oss_write */
+ char oss_write_buf[FRAME_SIZE * 2];
+ int oss_write_dst;
+ /*! buffers used in oss_read - AST_FRIENDLY_OFFSET space for headers
+ * plus enough room for a full frame
+ */
+ char oss_read_buf[FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET];
+ int readpos; /*!< read position above */
+ struct ast_frame read_f; /*!< returned by oss_read */
+};
+
+/*! forward declaration */
+static struct chan_oss_pvt *find_desc(char *dev);
+
+static char *oss_active; /*!< the active device */
+
+/*! \brief return the pointer to the video descriptor */
+struct video_desc *get_video_desc(struct ast_channel *c)
+{
+ struct chan_oss_pvt *o = c ? c->tech_pvt : find_desc(oss_active);
+ return o ? o->env : NULL;
+}
+static struct chan_oss_pvt oss_default = {
+ .sounddev = -1,
+ .duplex = M_UNSET, /* XXX check this */
+ .autoanswer = 1,
+ .autohangup = 1,
+ .queuesize = QUEUE_SIZE,
+ .frags = FRAGS,
+ .ext = "s",
+ .ctx = "default",
+ .readpos = AST_FRIENDLY_OFFSET, /* start here on reads */
+ .lastopen = { 0, 0 },
+ .boost = BOOST_SCALE,
+};
+
+
+static int setformat(struct chan_oss_pvt *o, int mode);
+
+static struct ast_channel *oss_request(const char *type, int format, void *data
+, int *cause);
+static int oss_digit_begin(struct ast_channel *c, char digit);
+static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration);
+static int oss_text(struct ast_channel *c, const char *text);
+static int oss_hangup(struct ast_channel *c);
+static int oss_answer(struct ast_channel *c);
+static struct ast_frame *oss_read(struct ast_channel *chan);
+static int oss_call(struct ast_channel *c, char *dest, int timeout);
+static int oss_write(struct ast_channel *chan, struct ast_frame *f);
+static int oss_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen);
+static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static char tdesc[] = "OSS Console Channel Driver";
+
+/* cannot do const because need to update some fields at runtime */
+static struct ast_channel_tech oss_tech = {
+ .type = "Console",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_SLINEAR, /* overwritten later */
+ .requester = oss_request,
+ .send_digit_begin = oss_digit_begin,
+ .send_digit_end = oss_digit_end,
+ .send_text = oss_text,
+ .hangup = oss_hangup,
+ .answer = oss_answer,
+ .read = oss_read,
+ .call = oss_call,
+ .write = oss_write,
+ .write_video = console_write_video,
+ .indicate = oss_indicate,
+ .fixup = oss_fixup,
+};
+
+/*!
+ * \brief returns a pointer to the descriptor with the given name
+ */
+static struct chan_oss_pvt *find_desc(char *dev)
+{
+ struct chan_oss_pvt *o = NULL;
+
+ if (!dev)
+ ast_log(LOG_WARNING, "null dev\n");
+
+ for (o = oss_default.next; o && o->name && dev && strcmp(o->name, dev) != 0; o = o->next);
+
+ if (!o)
+ ast_log(LOG_WARNING, "could not find <%s>\n", dev ? dev : "--no-device--");
+
+ return o;
+}
+
+/* !
+ * \brief split a string in extension-context, returns pointers to malloc'ed
+ * strings.
+ *
+ * If we do not have 'overridecontext' then the last @ is considered as
+ * a context separator, and the context is overridden.
+ * This is usually not very necessary as you can play with the dialplan,
+ * and it is nice not to need it because you have '@' in SIP addresses.
+ *
+ * \return the buffer address.
+ */
+static char *ast_ext_ctx(const char *src, char **ext, char **ctx)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+
+ if (ext == NULL || ctx == NULL)
+ return NULL; /* error */
+
+ *ext = *ctx = NULL;
+
+ if (src && *src != '\0')
+ *ext = ast_strdup(src);
+
+ if (*ext == NULL)
+ return NULL;
+
+ if (!o->overridecontext) {
+ /* parse from the right */
+ *ctx = strrchr(*ext, '@');
+ if (*ctx)
+ *(*ctx)++ = '\0';
+ }
+
+ return *ext;
+}
+
+/*!
+ * \brief Returns the number of blocks used in the audio output channel
+ */
+static int used_blocks(struct chan_oss_pvt *o)
+{
+ struct audio_buf_info info;
+
+ if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) {
+ if (!(o->warned & WARN_used_blocks)) {
+ ast_log(LOG_WARNING, "Error reading output space\n");
+ o->warned |= WARN_used_blocks;
+ }
+ return 1;
+ }
+
+ if (o->total_blocks == 0) {
+ if (0) /* debugging */
+ ast_log(LOG_WARNING, "fragtotal %d size %d avail %d\n", info.fragstotal, info.fragsize, info.fragments);
+ o->total_blocks = info.fragments;
+ }
+
+ return o->total_blocks - info.fragments;
+}
+
+/*! Write an exactly FRAME_SIZE sized frame */
+static int soundcard_writeframe(struct chan_oss_pvt *o, short *data)
+{
+ int res;
+
+ if (o->sounddev < 0)
+ setformat(o, O_RDWR);
+ if (o->sounddev < 0)
+ return 0; /* not fatal */
+ /*
+ * Nothing complex to manage the audio device queue.
+ * If the buffer is full just drop the extra, otherwise write.
+ * XXX in some cases it might be useful to write anyways after
+ * a number of failures, to restart the output chain.
+ */
+ res = used_blocks(o);
+ if (res > o->queuesize) { /* no room to write a block */
+ if (o->w_errors++ == 0 && (oss_debug & 0x4))
+ ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, o->w_errors);
+ return 0;
+ }
+ o->w_errors = 0;
+ return write(o->sounddev, (void *)data, FRAME_SIZE * 2);
+}
+
+/*!
+ * reset and close the device if opened,
+ * then open and initialize it in the desired mode,
+ * trigger reads and writes so we can start using it.
+ */
+static int setformat(struct chan_oss_pvt *o, int mode)
+{
+ int fmt, desired, res, fd;
+
+ if (o->sounddev >= 0) {
+ ioctl(o->sounddev, SNDCTL_DSP_RESET, 0);
+ close(o->sounddev);
+ o->duplex = M_UNSET;
+ o->sounddev = -1;
+ }
+ if (mode == O_CLOSE) /* we are done */
+ return 0;
+ if (ast_tvdiff_ms(ast_tvnow(), o->lastopen) < 1000)
+ return -1; /* don't open too often */
+ o->lastopen = ast_tvnow();
+ fd = o->sounddev = open(o->device, mode | O_NONBLOCK);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to re-open DSP device %s: %s\n", o->device, strerror(errno));
+ return -1;
+ }
+ if (o->owner)
+ ast_channel_set_fd(o->owner, 0, fd);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ fmt = AFMT_S16_LE;
+#else
+ fmt = AFMT_S16_BE;
+#endif
+ res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
+ return -1;
+ }
+ switch (mode) {
+ case O_RDWR:
+ res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+ /* Check to see if duplex set (FreeBSD Bug) */
+ res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt);
+ if (res == 0 && (fmt & DSP_CAP_DUPLEX)) {
+ ast_verb(2, "Console is full duplex\n");
+ o->duplex = M_FULL;
+ };
+ break;
+
+ case O_WRONLY:
+ o->duplex = M_WRITE;
+ break;
+
+ case O_RDONLY:
+ o->duplex = M_READ;
+ break;
+ }
+
+ fmt = 0;
+ res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ return -1;
+ }
+ fmt = desired = DEFAULT_SAMPLE_RATE; /* 8000 Hz desired */
+ res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ return -1;
+ }
+ if (fmt != desired) {
+ if (!(o->warned & WARN_speed)) {
+ ast_log(LOG_WARNING,
+ "Requested %d Hz, got %d Hz -- sound may be choppy\n",
+ desired, fmt);
+ o->warned |= WARN_speed;
+ }
+ }
+ /*
+ * on Freebsd, SETFRAGMENT does not work very well on some cards.
+ * Default to use 256 bytes, let the user override
+ */
+ if (o->frags) {
+ fmt = o->frags;
+ res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
+ if (res < 0) {
+ if (!(o->warned & WARN_frag)) {
+ ast_log(LOG_WARNING,
+ "Unable to set fragment size -- sound may be choppy\n");
+ o->warned |= WARN_frag;
+ }
+ }
+ }
+ /* on some cards, we need SNDCTL_DSP_SETTRIGGER to start outputting */
+ res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+ res = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &res);
+ /* it may fail if we are in half duplex, never mind */
+ return 0;
+}
+
+/*
+ * some of the standard methods supported by channels.
+ */
+static int oss_digit_begin(struct ast_channel *c, char digit)
+{
+ return 0;
+}
+
+static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+ /* no better use for received digits than print them */
+ ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
+ digit, duration);
+ return 0;
+}
+
+static int oss_text(struct ast_channel *c, const char *text)
+{
+ /* print received messages */
+ ast_verbose(" << Console Received text %s >> \n", text);
+ return 0;
+}
+
+/*!
+ * \brief handler for incoming calls. Either autoanswer, or start ringing
+ */
+static int oss_call(struct ast_channel *c, char *dest, int timeout)
+{
+ struct chan_oss_pvt *o = c->tech_pvt;
+ struct ast_frame f = { 0, };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(name);
+ AST_APP_ARG(flags);
+ );
+ char *parse = ast_strdupa(dest);
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ ast_verbose(" << Call to device '%s' dnid '%s' rdnis '%s' on console from '%s' <%s> >>\n", dest, c->cid.cid_dnid, c->cid.cid_rdnis, c->cid.cid_name, c->cid.cid_num);
+ if (!ast_strlen_zero(args.flags) && strcasecmp(args.flags, "answer") == 0) {
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_ANSWER;
+ ast_queue_frame(c, &f);
+ } else if (!ast_strlen_zero(args.flags) && strcasecmp(args.flags, "noanswer") == 0) {
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_RINGING;
+ ast_queue_frame(c, &f);
+ ast_indicate(c, AST_CONTROL_RINGING);
+ } else if (o->autoanswer) {
+ ast_verbose(" << Auto-answered >> \n");
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_ANSWER;
+ ast_queue_frame(c, &f);
+ } else {
+ ast_verbose("<< Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_RINGING;
+ ast_queue_frame(c, &f);
+ ast_indicate(c, AST_CONTROL_RINGING);
+ }
+ return 0;
+}
+
+/*!
+ * \brief remote side answered the phone
+ */
+static int oss_answer(struct ast_channel *c)
+{
+ ast_verbose(" << Console call has been answered >> \n");
+ ast_setstate(c, AST_STATE_UP);
+ return 0;
+}
+
+static int oss_hangup(struct ast_channel *c)
+{
+ struct chan_oss_pvt *o = c->tech_pvt;
+
+ c->tech_pvt = NULL;
+ o->owner = NULL;
+ ast_verbose(" << Hangup on console >> \n");
+ console_video_uninit(o->env);
+ ast_module_unref(ast_module_info->self);
+ if (o->hookstate) {
+ if (o->autoanswer || o->autohangup) {
+ /* Assume auto-hangup too */
+ o->hookstate = 0;
+ setformat(o, O_CLOSE);
+ }
+ }
+ return 0;
+}
+
+/*! \brief used for data coming from the network */
+static int oss_write(struct ast_channel *c, struct ast_frame *f)
+{
+ int src;
+ struct chan_oss_pvt *o = c->tech_pvt;
+
+ /*
+ * we could receive a block which is not a multiple of our
+ * FRAME_SIZE, so buffer it locally and write to the device
+ * in FRAME_SIZE chunks.
+ * Keep the residue stored for future use.
+ */
+ src = 0; /* read position into f->data */
+ while (src < f->datalen) {
+ /* Compute spare room in the buffer */
+ int l = sizeof(o->oss_write_buf) - o->oss_write_dst;
+
+ if (f->datalen - src >= l) { /* enough to fill a frame */
+ memcpy(o->oss_write_buf + o->oss_write_dst, f->data + src, l);
+ soundcard_writeframe(o, (short *) o->oss_write_buf);
+ src += l;
+ o->oss_write_dst = 0;
+ } else { /* copy residue */
+ l = f->datalen - src;
+ memcpy(o->oss_write_buf + o->oss_write_dst, f->data + src, l);
+ src += l; /* but really, we are done */
+ o->oss_write_dst += l;
+ }
+ }
+ return 0;
+}
+
+static struct ast_frame *oss_read(struct ast_channel *c)
+{
+ int res;
+ struct chan_oss_pvt *o = c->tech_pvt;
+ struct ast_frame *f = &o->read_f;
+
+ /* XXX can be simplified returning &ast_null_frame */
+ /* prepare a NULL frame in case we don't have enough data to return */
+ bzero(f, sizeof(struct ast_frame));
+ f->frametype = AST_FRAME_NULL;
+ f->src = oss_tech.type;
+
+ res = read(o->sounddev, o->oss_read_buf + o->readpos, sizeof(o->oss_read_buf) - o->readpos);
+ if (res < 0) /* audio data not ready, return a NULL frame */
+ return f;
+
+ o->readpos += res;
+ if (o->readpos < sizeof(o->oss_read_buf)) /* not enough samples */
+ return f;
+
+ if (o->mute)
+ return f;
+
+ o->readpos = AST_FRIENDLY_OFFSET; /* reset read pointer for next frame */
+ if (c->_state != AST_STATE_UP) /* drop data if frame is not up */
+ return f;
+ /* ok we can build and deliver the frame to the caller */
+ f->frametype = AST_FRAME_VOICE;
+ f->subclass = AST_FORMAT_SLINEAR;
+ f->samples = FRAME_SIZE;
+ f->datalen = FRAME_SIZE * 2;
+ f->data = o->oss_read_buf + AST_FRIENDLY_OFFSET;
+ if (o->boost != BOOST_SCALE) { /* scale and clip values */
+ int i, x;
+ int16_t *p = (int16_t *) f->data;
+ for (i = 0; i < f->samples; i++) {
+ x = (p[i] * o->boost) / BOOST_SCALE;
+ if (x > 32767)
+ x = 32767;
+ else if (x < -32768)
+ x = -32768;
+ p[i] = x;
+ }
+ }
+
+ f->offset = AST_FRIENDLY_OFFSET;
+ return f;
+}
+
+static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct chan_oss_pvt *o = newchan->tech_pvt;
+ o->owner = newchan;
+ return 0;
+}
+
+static int oss_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen)
+{
+ struct chan_oss_pvt *o = c->tech_pvt;
+ int res = 0;
+
+ switch (cond) {
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_RINGING:
+ case -1:
+ res = -1;
+ break;
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_VIDUPDATE:
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verbose(" << Console Has Been Placed on Hold >> \n");
+ ast_moh_start(c, data, o->mohinterpret);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verbose(" << Console Has Been Retrieved from Hold >> \n");
+ ast_moh_stop(c);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, c->name);
+ return -1;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief allocate a new channel.
+ */
+static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, int state)
+{
+ struct ast_channel *c;
+
+ c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "OSS/%s", o->device + 5);
+ if (c == NULL)
+ return NULL;
+ c->tech = &oss_tech;
+ if (o->sounddev < 0)
+ setformat(o, O_RDWR);
+ ast_channel_set_fd(c, 0, o->sounddev); /* -1 if device closed, override later */
+ c->nativeformats = AST_FORMAT_SLINEAR;
+ /* if the console makes the call, add video to the offer */
+ if (state == AST_STATE_RINGING)
+ c->nativeformats |= console_video_formats;
+
+ c->readformat = AST_FORMAT_SLINEAR;
+ c->writeformat = AST_FORMAT_SLINEAR;
+ c->tech_pvt = o;
+
+ if (!ast_strlen_zero(o->language))
+ ast_string_field_set(c, language, o->language);
+ /* Don't use ast_set_callerid() here because it will
+ * generate a needless NewCallerID event */
+ c->cid.cid_ani = ast_strdup(o->cid_num);
+ if (!ast_strlen_zero(ext))
+ c->cid.cid_dnid = ast_strdup(ext);
+
+ o->owner = c;
+ ast_module_ref(ast_module_info->self);
+ ast_jb_configure(c, &global_jbconf);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(c)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", c->name);
+ ast_hangup(c);
+ o->owner = c = NULL;
+ /* XXX what about the channel itself ? */
+ }
+ }
+ console_video_start(get_video_desc(c), c); /* XXX cleanup */
+
+ return c;
+}
+
+static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause)
+{
+ struct ast_channel *c;
+ struct chan_oss_pvt *o;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(name);
+ AST_APP_ARG(flags);
+ );
+ char *parse = ast_strdupa(data);
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+ o = find_desc(args.name);
+
+ ast_log(LOG_WARNING, "oss_request ty <%s> data 0x%p <%s>\n", type, data, (char *) data);
+ if (o == NULL) {
+ ast_log(LOG_NOTICE, "Device %s not found\n", args.name);
+ /* XXX we could default to 'dsp' perhaps ? */
+ return NULL;
+ }
+ if ((format & AST_FORMAT_SLINEAR) == 0) {
+ ast_log(LOG_NOTICE, "Format 0x%x unsupported\n", format);
+ return NULL;
+ }
+ if (o->owner) {
+ ast_log(LOG_NOTICE, "Already have a call (chan %p) on the OSS channel\n", o->owner);
+ *cause = AST_CAUSE_BUSY;
+ return NULL;
+ }
+ c = oss_new(o, NULL, NULL, AST_STATE_DOWN);
+ if (c == NULL) {
+ ast_log(LOG_WARNING, "Unable to create new OSS channel\n");
+ return NULL;
+ }
+ return c;
+}
+
+static void store_config_core(struct chan_oss_pvt *o, const char *var, const char *value);
+
+/*! Generic console command handler. Basically a wrapper for a subset
+ * of config file options which are also available from the CLI
+ */
+static char *console_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ const char *var, *value;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = CONSOLE_VIDEO_CMDS;
+ e->usage = "Usage: " CONSOLE_VIDEO_CMDS "...\n"
+ " Generic handler for console commands.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < e->args)
+ return CLI_SHOWUSAGE;
+ if (o == NULL) {
+ ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n",
+ oss_active);
+ return CLI_FAILURE;
+ }
+ var = a->argv[e->args-1];
+ value = a->argc > e->args ? a->argv[e->args] : NULL;
+ if (value) /* handle setting */
+ store_config_core(o, var, value);
+ if (!console_video_cli(o->env, var, a->fd)) /* print video-related values */
+ return CLI_SUCCESS;
+ /* handle other values */
+ if (!strcasecmp(var, "device")) {
+ ast_cli(a->fd, "device is [%s]\n", o->device);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *console_autoanswer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console autoanswer [on|off]";
+ e->usage =
+ "Usage: console autoanswer [on|off]\n"
+ " Enables or disables autoanswer feature. If used without\n"
+ " argument, displays the current on/off status of autoanswer.\n"
+ " The default value of autoanswer is in 'oss.conf'.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == e->args - 1) {
+ ast_cli(a->fd, "Auto answer is %s.\n", o->autoanswer ? "on" : "off");
+ return CLI_SUCCESS;
+ }
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+ if (o == NULL) {
+ ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n",
+ oss_active);
+ return CLI_FAILURE;
+ }
+ if (!strcasecmp(a->argv[e->args-1], "on"))
+ o->autoanswer = 1;
+ else if (!strcasecmp(a->argv[e->args - 1], "off"))
+ o->autoanswer = 0;
+ else
+ return CLI_SHOWUSAGE;
+ return CLI_SUCCESS;
+}
+
+/*! \brief helper function for the answer key/cli command */
+static char *console_do_answer(int fd)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ if (!o->owner) {
+ if (fd > -1)
+ ast_cli(fd, "No one is calling us\n");
+ return CLI_FAILURE;
+ }
+ o->hookstate = 1;
+ ast_queue_frame(o->owner, &f);
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief answer command from the console
+ */
+static char *console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console answer";
+ e->usage =
+ "Usage: console answer\n"
+ " Answers an incoming call on the console (OSS) channel.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL; /* no completion */
+ }
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+ return console_do_answer(a->fd);
+}
+
+/*!
+ * \brief Console send text CLI command
+ *
+ * \note concatenate all arguments into a single string. argv is NULL-terminated
+ * so we can use it right away
+ */
+static char *console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ char buf[TEXT_SIZE];
+
+ if (cmd == CLI_INIT) {
+ e->command = "console send text";
+ e->usage =
+ "Usage: console send text <message>\n"
+ " Sends a text message for display on the remote terminal.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc < e->args + 1)
+ return CLI_SHOWUSAGE;
+ if (!o->owner) {
+ ast_cli(a->fd, "Not in a call\n");
+ return CLI_FAILURE;
+ }
+ ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
+ if (!ast_strlen_zero(buf)) {
+ struct ast_frame f = { 0, };
+ int i = strlen(buf);
+ buf[i] = '\n';
+ f.frametype = AST_FRAME_TEXT;
+ f.subclass = 0;
+ f.data = buf;
+ f.datalen = i + 1;
+ ast_queue_frame(o->owner, &f);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+
+ if (cmd == CLI_INIT) {
+ e->command = "console hangup";
+ e->usage =
+ "Usage: console hangup\n"
+ " Hangs up any call currently placed on the console.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+ if (!o->owner && !o->hookstate) { /* XXX maybe only one ? */
+ ast_cli(a->fd, "No call to hang up\n");
+ return CLI_FAILURE;
+ }
+ o->hookstate = 0;
+ if (o->owner)
+ ast_queue_hangup(o->owner);
+ setformat(o, O_CLOSE);
+ return CLI_SUCCESS;
+}
+
+static char *console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH };
+ struct chan_oss_pvt *o = find_desc(oss_active);
+
+ if (cmd == CLI_INIT) {
+ e->command = "console flash";
+ e->usage =
+ "Usage: console flash\n"
+ " Flashes the call currently placed on the console.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+ if (!o->owner) { /* XXX maybe !o->hookstate too ? */
+ ast_cli(a->fd, "No call to flash\n");
+ return CLI_FAILURE;
+ }
+ o->hookstate = 0;
+ if (o->owner) /* XXX must be true, right ? */
+ ast_queue_frame(o->owner, &f);
+ return CLI_SUCCESS;
+}
+
+static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *s = NULL, *mye = NULL, *myc = NULL;
+ struct chan_oss_pvt *o = find_desc(oss_active);
+
+ if (cmd == CLI_INIT) {
+ e->command = "console dial";
+ e->usage =
+ "Usage: console dial [extension[@context]]\n"
+ " Dials a given extension (and context if specified)\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc > e->args + 1)
+ return CLI_SHOWUSAGE;
+ if (o->owner) { /* already in a call */
+ int i;
+ struct ast_frame f = { AST_FRAME_DTMF, 0 };
+
+ if (a->argc == e->args) { /* argument is mandatory here */
+ ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n");
+ return CLI_FAILURE;
+ }
+ s = a->argv[e->args];
+ /* send the string one char at a time */
+ for (i = 0; i < strlen(s); i++) {
+ f.subclass = s[i];
+ ast_queue_frame(o->owner, &f);
+ }
+ return CLI_SUCCESS;
+ }
+ /* if we have an argument split it into extension and context */
+ if (a->argc == e->args + 1)
+ s = ast_ext_ctx(a->argv[e->args], &mye, &myc);
+ /* supply default values if needed */
+ if (mye == NULL)
+ mye = o->ext;
+ if (myc == NULL)
+ myc = o->ctx;
+ if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
+ o->hookstate = 1;
+ oss_new(o, mye, myc, AST_STATE_RINGING);
+ } else
+ ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
+ if (s)
+ ast_free(s);
+ return CLI_SUCCESS;
+}
+
+static char *console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ char *s;
+
+ if (cmd == CLI_INIT) {
+ e->command = "console {mute|unmute}";
+ e->usage =
+ "Usage: console {mute|unmute}\n"
+ " Mute/unmute the microphone.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+ s = a->argv[e->args-1];
+ if (!strcasecmp(s, "mute"))
+ o->mute = 1;
+ else if (!strcasecmp(s, "unmute"))
+ o->mute = 0;
+ else
+ return CLI_SHOWUSAGE;
+ return CLI_SUCCESS;
+}
+
+static char *console_transfer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ struct ast_channel *b = NULL;
+ char *tmp, *ext, *ctx;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console transfer";
+ e->usage =
+ "Usage: console transfer <extension>[@context]\n"
+ " Transfers the currently connected call to the given extension (and\n"
+ " context if specified)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ if (o == NULL)
+ return CLI_FAILURE;
+ if (o->owner == NULL || (b = ast_bridged_channel(o->owner)) == NULL) {
+ ast_cli(a->fd, "There is no call to transfer\n");
+ return CLI_SUCCESS;
+ }
+
+ tmp = ast_ext_ctx(a->argv[2], &ext, &ctx);
+ if (ctx == NULL) /* supply default context if needed */
+ ctx = o->owner->context;
+ if (!ast_exists_extension(b, ctx, ext, 1, b->cid.cid_num))
+ ast_cli(a->fd, "No such extension exists\n");
+ else {
+ ast_cli(a->fd, "Whee, transferring %s to %s@%s.\n", b->name, ext, ctx);
+ if (ast_async_goto(b, ctx, ext, 1))
+ ast_cli(a->fd, "Failed to transfer :(\n");
+ }
+ if (tmp)
+ ast_free(tmp);
+ return CLI_SUCCESS;
+}
+
+static char *console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console active";
+ e->usage =
+ "Usage: console active [device]\n"
+ " If used without a parameter, displays which device is the current\n"
+ " console. If a device is specified, the console sound device is changed to\n"
+ " the device specified.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 2)
+ ast_cli(a->fd, "active console is [%s]\n", oss_active);
+ else if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ else {
+ struct chan_oss_pvt *o;
+ if (strcmp(a->argv[2], "show") == 0) {
+ for (o = oss_default.next; o; o = o->next)
+ ast_cli(a->fd, "device [%s] exists\n", o->name);
+ return CLI_SUCCESS;
+ }
+ o = find_desc(a->argv[2]);
+ if (o == NULL)
+ ast_cli(a->fd, "No device [%s] exists\n", a->argv[2]);
+ else
+ oss_active = o->name;
+ }
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief store the boost factor
+ */
+static void store_boost(struct chan_oss_pvt *o, const char *s)
+{
+ double boost = 0;
+ if (sscanf(s, "%lf", &boost) != 1) {
+ ast_log(LOG_WARNING, "invalid boost <%s>\n", s);
+ return;
+ }
+ if (boost < -BOOST_MAX) {
+ ast_log(LOG_WARNING, "boost %s too small, using %d\n", s, -BOOST_MAX);
+ boost = -BOOST_MAX;
+ } else if (boost > BOOST_MAX) {
+ ast_log(LOG_WARNING, "boost %s too large, using %d\n", s, BOOST_MAX);
+ boost = BOOST_MAX;
+ }
+ boost = exp(log(10) * boost / 20) * BOOST_SCALE;
+ o->boost = boost;
+ ast_log(LOG_WARNING, "setting boost %s to %d\n", s, o->boost);
+}
+
+static char *console_boost(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_oss_pvt *o = find_desc(oss_active);
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console boost";
+ e->usage =
+ "Usage: console boost [boost in dB]\n"
+ " Sets or display mic boost in dB\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 2)
+ ast_cli(a->fd, "boost currently %5.1f\n", 20 * log10(((double) o->boost / (double) BOOST_SCALE)));
+ else if (a->argc == 3)
+ store_boost(o, a->argv[2]);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_oss[] = {
+ AST_CLI_DEFINE(console_answer, "Answer an incoming console call"),
+ AST_CLI_DEFINE(console_hangup, "Hangup a call on the console"),
+ AST_CLI_DEFINE(console_flash, "Flash a call on the console"),
+ AST_CLI_DEFINE(console_dial, "Dial an extension on the console"),
+ AST_CLI_DEFINE(console_mute, "Disable/Enable mic input"),
+ AST_CLI_DEFINE(console_transfer, "Transfer a call to a different extension"),
+ AST_CLI_DEFINE(console_cmd, "Generic console command"),
+ AST_CLI_DEFINE(console_sendtext, "Send text to the remote device"),
+ AST_CLI_DEFINE(console_autoanswer, "Sets/displays autoanswer"),
+ AST_CLI_DEFINE(console_boost, "Sets/displays mic boost in dB"),
+ AST_CLI_DEFINE(console_active, "Sets/displays active console"),
+};
+
+/*!
+ * store the mixer argument from the config file, filtering possibly
+ * invalid or dangerous values (the string is used as argument for
+ * system("mixer %s")
+ */
+static void store_mixer(struct chan_oss_pvt *o, const char *s)
+{
+ int i;
+
+ for (i = 0; i < strlen(s); i++) {
+ if (!isalnum(s[i]) && index(" \t-/", s[i]) == NULL) {
+ ast_log(LOG_WARNING, "Suspect char %c in mixer cmd, ignoring:\n\t%s\n", s[i], s);
+ return;
+ }
+ }
+ if (o->mixer_cmd)
+ ast_free(o->mixer_cmd);
+ o->mixer_cmd = ast_strdup(s);
+ ast_log(LOG_WARNING, "setting mixer %s\n", s);
+}
+
+/*!
+ * store the callerid components
+ */
+static void store_callerid(struct chan_oss_pvt *o, const char *s)
+{
+ ast_callerid_split(s, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
+}
+
+static void store_config_core(struct chan_oss_pvt *o, const char *var, const char *value)
+{
+ CV_START(var, value);
+
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, var, value))
+ return;
+
+ if (!console_video_config(&o->env, var, value))
+ return; /* matched there */
+ CV_BOOL("autoanswer", o->autoanswer);
+ CV_BOOL("autohangup", o->autohangup);
+ CV_BOOL("overridecontext", o->overridecontext);
+ CV_STR("device", o->device);
+ CV_UINT("frags", o->frags);
+ CV_UINT("debug", oss_debug);
+ CV_UINT("queuesize", o->queuesize);
+ CV_STR("context", o->ctx);
+ CV_STR("language", o->language);
+ CV_STR("mohinterpret", o->mohinterpret);
+ CV_STR("extension", o->ext);
+ CV_F("mixer", store_mixer(o, value));
+ CV_F("callerid", store_callerid(o, value)) ;
+ CV_F("boost", store_boost(o, value));
+
+ CV_END;
+}
+
+/*!
+ * grab fields from the config file, init the descriptor and open the device.
+ */
+static struct chan_oss_pvt *store_config(struct ast_config *cfg, char *ctg)
+{
+ struct ast_variable *v;
+ struct chan_oss_pvt *o;
+
+ if (ctg == NULL) {
+ o = &oss_default;
+ ctg = "general";
+ } else {
+ if (!(o = ast_calloc(1, sizeof(*o))))
+ return NULL;
+ *o = oss_default;
+ /* "general" is also the default thing */
+ if (strcmp(ctg, "general") == 0) {
+ o->name = ast_strdup("dsp");
+ oss_active = o->name;
+ goto openit;
+ }
+ o->name = ast_strdup(ctg);
+ }
+
+ strcpy(o->mohinterpret, "default");
+
+ o->lastopen = ast_tvnow(); /* don't leave it 0 or tvdiff may wrap */
+ /* fill other fields from configuration */
+ for (v = ast_variable_browse(cfg, ctg); v; v = v->next) {
+ store_config_core(o, v->name, v->value);
+ }
+ if (ast_strlen_zero(o->device))
+ ast_copy_string(o->device, DEV_DSP, sizeof(o->device));
+ if (o->mixer_cmd) {
+ char *cmd;
+
+ asprintf(&cmd, "mixer %s", o->mixer_cmd);
+ ast_log(LOG_WARNING, "running [%s]\n", cmd);
+ system(cmd);
+ ast_free(cmd);
+ }
+ if (o == &oss_default) /* we are done with the default */
+ return NULL;
+
+openit:
+#ifdef TRYOPEN
+ if (setformat(o, O_RDWR) < 0) { /* open device */
+ ast_verb(1, "Device %s not detected\n", ctg);
+ ast_verb(1, "Turn off OSS support by adding " "'noload=chan_oss.so' in /etc/asterisk/modules.conf\n");
+ goto error;
+ }
+ if (o->duplex != M_FULL)
+ ast_log(LOG_WARNING, "XXX I don't work right with non " "full-duplex sound cards XXX\n");
+#endif /* TRYOPEN */
+
+ /* link into list of devices */
+ if (o != &oss_default) {
+ o->next = oss_default.next;
+ oss_default.next = o;
+ }
+ return o;
+
+#ifdef TRYOPEN
+error:
+ if (o != &oss_default)
+ ast_free(o);
+ return NULL;
+#endif
+}
+
+static int load_module(void)
+{
+ struct ast_config *cfg = NULL;
+ char *ctg = NULL;
+ struct ast_flags config_flags = { 0 };
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ /* load config file */
+ if (!(cfg = ast_config_load(config, config_flags))) {
+ ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ do {
+ store_config(cfg, ctg);
+ } while ( (ctg = ast_category_browse(cfg, ctg)) != NULL);
+
+ ast_config_destroy(cfg);
+
+ if (find_desc(oss_active) == NULL) {
+ ast_log(LOG_NOTICE, "Device %s not found\n", oss_active);
+ /* XXX we could default to 'dsp' perhaps ? */
+ /* XXX should cleanup allocated memory etc. */
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ oss_tech.capabilities |= console_video_formats;
+
+ if (ast_channel_register(&oss_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel type 'OSS'\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ ast_cli_register_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+
+static int unload_module(void)
+{
+ struct chan_oss_pvt *o;
+
+ ast_channel_unregister(&oss_tech);
+ ast_cli_unregister_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry));
+
+ for (o = oss_default.next; o; o = o->next) {
+ close(o->sounddev);
+ if (o->owner)
+ ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD);
+ if (o->owner) /* XXX how ??? */
+ return -1;
+ /* XXX what about the memory allocated ? */
+ }
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OSS Console Channel Driver");
diff --git a/trunk/channels/chan_phone.c b/trunk/channels/chan_phone.c
new file mode 100644
index 000000000..233e5e829
--- /dev/null
+++ b/trunk/channels/chan_phone.c
@@ -0,0 +1,1450 @@
+/*
+ * 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 Generic Linux Telephony Interface driver
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>ixjuser</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#ifdef HAVE_LINUX_COMPILER_H
+#include <linux/compiler.h>
+#endif
+#include <linux/telephony.h>
+/* Still use some IXJ specific stuff */
+#include <linux/version.h>
+#include <linux/ixjuser.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/callerid.h"
+#include "asterisk/causes.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/musiconhold.h"
+
+#include "DialTone.h"
+
+#ifdef QTI_PHONEJACK_TJ_PCI /* check for the newer quicknet driver v.3.1.0 which has this symbol */
+#define QNDRV_VER 310
+#else
+#define QNDRV_VER 100
+#endif
+
+#if QNDRV_VER > 100
+#ifdef __linux__
+#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, &x);
+#else /* FreeBSD and others */
+#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, x);
+#endif /* __linux__ */
+#else /* older driver */
+#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, &x);
+#endif
+
+#define DEFAULT_CALLER_ID "Unknown"
+#define PHONE_MAX_BUF 480
+#define DEFAULT_GAIN 0x100
+
+static const char tdesc[] = "Standard Linux Telephony API Driver";
+static const char config[] = "phone.conf";
+
+/* Default context for dialtone mode */
+static char context[AST_MAX_EXTENSION] = "default";
+
+/* Default language */
+static char language[MAX_LANGUAGE] = "";
+
+static int echocancel = AEC_OFF;
+
+static int silencesupression = 0;
+
+static int prefformat = AST_FORMAT_G729A | AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW;
+
+/* Protect the interface list (of phone_pvt's) */
+AST_MUTEX_DEFINE_STATIC(iflock);
+
+/* Protect the monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+/* Boolean value whether the monitoring thread shall continue. */
+static unsigned int monitor;
+
+/* This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+
+static int restart_monitor(void);
+
+/* The private structures of the Phone Jack channels are linked for
+ selecting outgoing channels */
+
+#define MODE_DIALTONE 1
+#define MODE_IMMEDIATE 2
+#define MODE_FXO 3
+#define MODE_FXS 4
+#define MODE_SIGMA 5
+
+static struct phone_pvt {
+ int fd; /* Raw file descriptor for this device */
+ struct ast_channel *owner; /* Channel we belong to, possibly NULL */
+ int mode; /* Is this in the */
+ int lastformat; /* Last output format */
+ int lastinput; /* Last input format */
+ int ministate; /* Miniature state, for dialtone mode */
+ char dev[256]; /* Device name */
+ struct phone_pvt *next; /* Next channel in list */
+ struct ast_frame fr; /* Frame */
+ char offset[AST_FRIENDLY_OFFSET];
+ char buf[PHONE_MAX_BUF]; /* Static buffer for reading frames */
+ int obuflen;
+ int dialtone;
+ int txgain, rxgain; /* gain control for playing, recording */
+ /* 0x100 - 1.0, 0x200 - 2.0, 0x80 - 0.5 */
+ int cpt; /* Call Progress Tone playing? */
+ int silencesupression;
+ char context[AST_MAX_EXTENSION];
+ char obuf[PHONE_MAX_BUF * 2];
+ char ext[AST_MAX_EXTENSION];
+ char language[MAX_LANGUAGE];
+ char cid_num[AST_MAX_EXTENSION];
+ char cid_name[AST_MAX_EXTENSION];
+} *iflist = NULL;
+
+static char cid_num[AST_MAX_EXTENSION];
+static char cid_name[AST_MAX_EXTENSION];
+
+static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause);
+static int phone_digit_begin(struct ast_channel *ast, char digit);
+static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int phone_call(struct ast_channel *ast, char *dest, int timeout);
+static int phone_hangup(struct ast_channel *ast);
+static int phone_answer(struct ast_channel *ast);
+static struct ast_frame *phone_read(struct ast_channel *ast);
+static int phone_write(struct ast_channel *ast, struct ast_frame *frame);
+static struct ast_frame *phone_exception(struct ast_channel *ast);
+static int phone_send_text(struct ast_channel *ast, const char *text);
+static int phone_fixup(struct ast_channel *old, struct ast_channel *new);
+static int phone_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen);
+
+static const struct ast_channel_tech phone_tech = {
+ .type = "Phone",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_G729A,
+ .requester = phone_request,
+ .send_digit_begin = phone_digit_begin,
+ .send_digit_end = phone_digit_end,
+ .call = phone_call,
+ .hangup = phone_hangup,
+ .answer = phone_answer,
+ .read = phone_read,
+ .write = phone_write,
+ .exception = phone_exception,
+ .indicate = phone_indicate,
+ .fixup = phone_fixup
+};
+
+static struct ast_channel_tech phone_tech_fxs = {
+ .type = "Phone",
+ .description = tdesc,
+ .requester = phone_request,
+ .send_digit_begin = phone_digit_begin,
+ .send_digit_end = phone_digit_end,
+ .call = phone_call,
+ .hangup = phone_hangup,
+ .answer = phone_answer,
+ .read = phone_read,
+ .write = phone_write,
+ .exception = phone_exception,
+ .write_video = phone_write,
+ .send_text = phone_send_text,
+ .indicate = phone_indicate,
+ .fixup = phone_fixup
+};
+
+static struct ast_channel_tech *cur_tech;
+
+static int phone_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen)
+{
+ struct phone_pvt *p = chan->tech_pvt;
+ int res=-1;
+ ast_debug(1, "Requested indication %d on channel %s\n", condition, chan->name);
+ switch(condition) {
+ case AST_CONTROL_FLASH:
+ ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK);
+ usleep(320000);
+ ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK);
+ p->lastformat = -1;
+ res = 0;
+ break;
+ case AST_CONTROL_HOLD:
+ ast_moh_start(chan, data, NULL);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(chan);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Condition %d is not supported on channel %s\n", condition, chan->name);
+ }
+ return res;
+}
+
+static int phone_fixup(struct ast_channel *old, struct ast_channel *new)
+{
+ struct phone_pvt *pvt = old->tech_pvt;
+ if (pvt && pvt->owner == old)
+ pvt->owner = new;
+ return 0;
+}
+
+static int phone_digit_begin(struct ast_channel *chan, char digit)
+{
+ /* XXX Modify this callback to let Asterisk support controlling the length of DTMF */
+ return 0;
+}
+
+static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct phone_pvt *p;
+ int outdigit;
+ p = ast->tech_pvt;
+ ast_debug(1, "Dialed %c\n", digit);
+ switch(digit) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ outdigit = digit - '0';
+ break;
+ case '*':
+ outdigit = 11;
+ break;
+ case '#':
+ outdigit = 12;
+ break;
+ case 'f': /*flash*/
+ case 'F':
+ ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK);
+ usleep(320000);
+ ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK);
+ p->lastformat = -1;
+ return 0;
+ default:
+ ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
+ return -1;
+ }
+ ast_debug(1, "Dialed %d\n", outdigit);
+ ioctl(p->fd, PHONE_PLAY_TONE, outdigit);
+ p->lastformat = -1;
+ return 0;
+}
+
+static int phone_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct phone_pvt *p;
+
+ PHONE_CID cid;
+ struct timeval UtcTime = ast_tvnow();
+ struct ast_tm tm;
+ int start;
+
+ ast_localtime(&UtcTime, &tm, NULL);
+
+ memset(&cid, 0, sizeof(PHONE_CID));
+ if(&tm != NULL) {
+ snprintf(cid.month, sizeof(cid.month), "%02d",(tm.tm_mon + 1));
+ snprintf(cid.day, sizeof(cid.day), "%02d", tm.tm_mday);
+ snprintf(cid.hour, sizeof(cid.hour), "%02d", tm.tm_hour);
+ snprintf(cid.min, sizeof(cid.min), "%02d", tm.tm_min);
+ }
+ /* the standard format of ast->callerid is: "name" <number>, but not always complete */
+ if (ast_strlen_zero(ast->cid.cid_name))
+ strcpy(cid.name, DEFAULT_CALLER_ID);
+ else
+ ast_copy_string(cid.name, ast->cid.cid_name, sizeof(cid.name));
+
+ if (ast->cid.cid_num)
+ ast_copy_string(cid.number, ast->cid.cid_num, sizeof(cid.number));
+
+ p = ast->tech_pvt;
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "phone_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+ ast_debug(1, "Ringing %s on %s (%d)\n", dest, ast->name, ast->fds[0]);
+
+ start = IXJ_PHONE_RING_START(cid);
+ if (start == -1)
+ return -1;
+
+ if (p->mode == MODE_FXS) {
+ char *digit = strchr(dest, '/');
+ if (digit)
+ {
+ digit++;
+ while (*digit)
+ phone_digit_end(ast, *digit++, 0);
+ }
+ }
+
+ ast_setstate(ast, AST_STATE_RINGING);
+ ast_queue_control(ast, AST_CONTROL_RINGING);
+ return 0;
+}
+
+static int phone_hangup(struct ast_channel *ast)
+{
+ struct phone_pvt *p;
+ p = ast->tech_pvt;
+ ast_debug(1, "phone_hangup(%s)\n", ast->name);
+ if (!ast->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ /* XXX Is there anything we can do to really hang up except stop recording? */
+ ast_setstate(ast, AST_STATE_DOWN);
+ if (ioctl(p->fd, PHONE_REC_STOP))
+ ast_log(LOG_WARNING, "Failed to stop recording\n");
+ if (ioctl(p->fd, PHONE_PLAY_STOP))
+ ast_log(LOG_WARNING, "Failed to stop playing\n");
+ if (ioctl(p->fd, PHONE_RING_STOP))
+ ast_log(LOG_WARNING, "Failed to stop ringing\n");
+ if (ioctl(p->fd, PHONE_CPT_STOP))
+ ast_log(LOG_WARNING, "Failed to stop sounds\n");
+
+ /* If it's an FXO, hang them up */
+ if (p->mode == MODE_FXO) {
+ if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK))
+ ast_debug(1, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",ast->name, strerror(errno));
+ }
+
+ /* If they're off hook, give a busy signal */
+ if (ioctl(p->fd, PHONE_HOOKSTATE)) {
+ ast_debug(1, "Got hunghup, giving busy signal\n");
+ ioctl(p->fd, PHONE_BUSY);
+ p->cpt = 1;
+ }
+ p->lastformat = -1;
+ p->lastinput = -1;
+ p->ministate = 0;
+ p->obuflen = 0;
+ p->dialtone = 0;
+ memset(p->ext, 0, sizeof(p->ext));
+ ((struct phone_pvt *)(ast->tech_pvt))->owner = NULL;
+ ast_module_unref(ast_module_info->self);
+ ast_verb(3, "Hungup '%s'\n", ast->name);
+ ast->tech_pvt = NULL;
+ ast_setstate(ast, AST_STATE_DOWN);
+ restart_monitor();
+ return 0;
+}
+
+static int phone_setup(struct ast_channel *ast)
+{
+ struct phone_pvt *p;
+ p = ast->tech_pvt;
+ ioctl(p->fd, PHONE_CPT_STOP);
+ /* Nothing to answering really, just start recording */
+ if (ast->rawreadformat == AST_FORMAT_G729A) {
+ /* Prefer g729 */
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (p->lastinput != AST_FORMAT_G729A) {
+ p->lastinput = AST_FORMAT_G729A;
+ if (ioctl(p->fd, PHONE_REC_CODEC, G729)) {
+ ast_log(LOG_WARNING, "Failed to set codec to g729\n");
+ return -1;
+ }
+ }
+ } else if (ast->rawreadformat == AST_FORMAT_G723_1) {
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (p->lastinput != AST_FORMAT_G723_1) {
+ p->lastinput = AST_FORMAT_G723_1;
+ if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) {
+ ast_log(LOG_WARNING, "Failed to set codec to g723.1\n");
+ return -1;
+ }
+ }
+ } else if (ast->rawreadformat == AST_FORMAT_SLINEAR) {
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (p->lastinput != AST_FORMAT_SLINEAR) {
+ p->lastinput = AST_FORMAT_SLINEAR;
+ if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) {
+ ast_log(LOG_WARNING, "Failed to set codec to signed linear 16\n");
+ return -1;
+ }
+ }
+ } else if (ast->rawreadformat == AST_FORMAT_ULAW) {
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (p->lastinput != AST_FORMAT_ULAW) {
+ p->lastinput = AST_FORMAT_ULAW;
+ if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) {
+ ast_log(LOG_WARNING, "Failed to set codec to uLaw\n");
+ return -1;
+ }
+ }
+ } else if (p->mode == MODE_FXS) {
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (p->lastinput != ast->rawreadformat) {
+ p->lastinput = ast->rawreadformat;
+ if (ioctl(p->fd, PHONE_REC_CODEC, ast->rawreadformat)) {
+ ast_log(LOG_WARNING, "Failed to set codec to %d\n",
+ ast->rawreadformat);
+ return -1;
+ }
+ }
+ } else {
+ ast_log(LOG_WARNING, "Can't do format %s\n", ast_getformatname(ast->rawreadformat));
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_START)) {
+ ast_log(LOG_WARNING, "Failed to start recording\n");
+ return -1;
+ }
+ /* set the DTMF times (the default is too short) */
+ ioctl(p->fd, PHONE_SET_TONE_ON_TIME, 300);
+ ioctl(p->fd, PHONE_SET_TONE_OFF_TIME, 200);
+ return 0;
+}
+
+static int phone_answer(struct ast_channel *ast)
+{
+ struct phone_pvt *p;
+ p = ast->tech_pvt;
+ /* In case it's a LineJack, take it off hook */
+ if (p->mode == MODE_FXO) {
+ if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_OFF_HOOK))
+ ast_debug(1, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n", ast->name, strerror(errno));
+ else
+ ast_debug(1, "Took linejack off hook\n");
+ }
+ phone_setup(ast);
+ ast_debug(1, "phone_answer(%s)\n", ast->name);
+ ast->rings = 0;
+ ast_setstate(ast, AST_STATE_UP);
+ return 0;
+}
+
+#if 0
+static char phone_2digit(char c)
+{
+ if (c == 12)
+ return '#';
+ else if (c == 11)
+ return '*';
+ else if ((c < 10) && (c >= 0))
+ return '0' + c - 1;
+ else
+ return '?';
+}
+#endif
+
+static struct ast_frame *phone_exception(struct ast_channel *ast)
+{
+ int res;
+ union telephony_exception phonee;
+ struct phone_pvt *p = ast->tech_pvt;
+ char digit;
+
+ /* Some nice norms */
+ p->fr.datalen = 0;
+ p->fr.samples = 0;
+ p->fr.data = NULL;
+ p->fr.src = "Phone";
+ p->fr.offset = 0;
+ p->fr.mallocd=0;
+ p->fr.delivery = ast_tv(0,0);
+
+ phonee.bytes = ioctl(p->fd, PHONE_EXCEPTION);
+ if (phonee.bits.dtmf_ready) {
+ ast_debug(1, "phone_exception(): DTMF\n");
+
+ /* We've got a digit -- Just handle this nicely and easily */
+ digit = ioctl(p->fd, PHONE_GET_DTMF_ASCII);
+ p->fr.subclass = digit;
+ p->fr.frametype = AST_FRAME_DTMF;
+ return &p->fr;
+ }
+ if (phonee.bits.hookstate) {
+ ast_debug(1, "Hookstate changed\n");
+ res = ioctl(p->fd, PHONE_HOOKSTATE);
+ /* See if we've gone on hook, if so, notify by returning NULL */
+ ast_debug(1, "New hookstate: %d\n", res);
+ if (!res && (p->mode != MODE_FXO))
+ return NULL;
+ else {
+ if (ast->_state == AST_STATE_RINGING) {
+ /* They've picked up the phone */
+ p->fr.frametype = AST_FRAME_CONTROL;
+ p->fr.subclass = AST_CONTROL_ANSWER;
+ phone_setup(ast);
+ ast_setstate(ast, AST_STATE_UP);
+ return &p->fr;
+ } else
+ ast_log(LOG_WARNING, "Got off hook in weird state %d\n", ast->_state);
+ }
+ }
+#if 1
+ if (phonee.bits.pstn_ring)
+ ast_verbose("Unit is ringing\n");
+ if (phonee.bits.caller_id) {
+ ast_verbose("We have caller ID\n");
+ }
+ if (phonee.bits.pstn_wink)
+ ast_verbose("Detected Wink\n");
+#endif
+ /* Strange -- nothing there.. */
+ p->fr.frametype = AST_FRAME_NULL;
+ p->fr.subclass = 0;
+ return &p->fr;
+}
+
+static struct ast_frame *phone_read(struct ast_channel *ast)
+{
+ int res;
+ struct phone_pvt *p = ast->tech_pvt;
+
+
+ /* Some nice norms */
+ p->fr.datalen = 0;
+ p->fr.samples = 0;
+ p->fr.data = NULL;
+ p->fr.src = "Phone";
+ p->fr.offset = 0;
+ p->fr.mallocd=0;
+ p->fr.delivery = ast_tv(0,0);
+
+ /* Try to read some data... */
+ CHECK_BLOCKING(ast);
+ res = read(p->fd, p->buf, PHONE_MAX_BUF);
+ ast_clear_flag(ast, AST_FLAG_BLOCKING);
+ if (res < 0) {
+#if 0
+ if (errno == EAGAIN) {
+ ast_log(LOG_WARNING, "Null frame received\n");
+ p->fr.frametype = AST_FRAME_NULL;
+ p->fr.subclass = 0;
+ return &p->fr;
+ }
+#endif
+ ast_log(LOG_WARNING, "Error reading: %s\n", strerror(errno));
+ return NULL;
+ }
+ p->fr.data = p->buf;
+ if (p->mode != MODE_FXS)
+ switch(p->buf[0] & 0x3) {
+ case '0':
+ case '1':
+ /* Normal */
+ break;
+ case '2':
+ case '3':
+ /* VAD/CNG, only send two words */
+ res = 4;
+ break;
+ }
+ p->fr.samples = 240;
+ p->fr.datalen = res;
+ p->fr.frametype = p->lastinput <= AST_FORMAT_AUDIO_MASK ?
+ AST_FRAME_VOICE :
+ p->lastinput <= AST_FORMAT_PNG ? AST_FRAME_IMAGE
+ : AST_FRAME_VIDEO;
+ p->fr.subclass = p->lastinput;
+ p->fr.offset = AST_FRIENDLY_OFFSET;
+ /* Byteswap from little-endian to native-endian */
+ if (p->fr.subclass == AST_FORMAT_SLINEAR)
+ ast_frame_byteswap_le(&p->fr);
+ return &p->fr;
+}
+
+static int phone_write_buf(struct phone_pvt *p, const char *buf, int len, int frlen, int swap)
+{
+ int res;
+ /* Store as much of the buffer as we can, then write fixed frames */
+ int space = sizeof(p->obuf) - p->obuflen;
+ /* Make sure we have enough buffer space to store the frame */
+ if (space < len)
+ len = space;
+ if (swap)
+ ast_swapcopy_samples(p->obuf+p->obuflen, buf, len/2);
+ else
+ memcpy(p->obuf + p->obuflen, buf, len);
+ p->obuflen += len;
+ while(p->obuflen > frlen) {
+ res = write(p->fd, p->obuf, frlen);
+ if (res != frlen) {
+ if (res < 1) {
+/*
+ * Card is in non-blocking mode now and it works well now, but there are
+ * lot of messages like this. So, this message is temporarily disabled.
+ */
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frlen);
+ }
+ }
+ p->obuflen -= frlen;
+ /* Move memory if necessary */
+ if (p->obuflen)
+ memmove(p->obuf, p->obuf + frlen, p->obuflen);
+ }
+ return len;
+}
+
+static int phone_send_text(struct ast_channel *ast, const char *text)
+{
+ int length = strlen(text);
+ return phone_write_buf(ast->tech_pvt, text, length, length, 0) ==
+ length ? 0 : -1;
+}
+
+static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct phone_pvt *p = ast->tech_pvt;
+ int res;
+ int maxfr=0;
+ char *pos;
+ int sofar;
+ int expected;
+ int codecset = 0;
+ char tmpbuf[4];
+ /* Write a frame of (presumably voice) data */
+ if (frame->frametype != AST_FRAME_VOICE && p->mode != MODE_FXS) {
+ if (frame->frametype != AST_FRAME_IMAGE)
+ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
+ return 0;
+ }
+ if (!(frame->subclass &
+ (AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_G729A)) &&
+ p->mode != MODE_FXS) {
+ ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
+ return -1;
+ }
+#if 0
+ /* If we're not in up mode, go into up mode now */
+ if (ast->_state != AST_STATE_UP) {
+ ast_setstate(ast, AST_STATE_UP);
+ phone_setup(ast);
+ }
+#else
+ if (ast->_state != AST_STATE_UP) {
+ /* Don't try tos end audio on-hook */
+ return 0;
+ }
+#endif
+ if (frame->subclass == AST_FORMAT_G729A) {
+ if (p->lastformat != AST_FORMAT_G729A) {
+ ioctl(p->fd, PHONE_PLAY_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (ioctl(p->fd, PHONE_PLAY_CODEC, G729)) {
+ ast_log(LOG_WARNING, "Unable to set G729 mode\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_CODEC, G729)) {
+ ast_log(LOG_WARNING, "Unable to set G729 mode\n");
+ return -1;
+ }
+ p->lastformat = AST_FORMAT_G729A;
+ p->lastinput = AST_FORMAT_G729A;
+ /* Reset output buffer */
+ p->obuflen = 0;
+ codecset = 1;
+ }
+ if (frame->datalen > 80) {
+ ast_log(LOG_WARNING, "Frame size too large for G.729 (%d bytes)\n", frame->datalen);
+ return -1;
+ }
+ maxfr = 80;
+ } else if (frame->subclass == AST_FORMAT_G723_1) {
+ if (p->lastformat != AST_FORMAT_G723_1) {
+ ioctl(p->fd, PHONE_PLAY_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (ioctl(p->fd, PHONE_PLAY_CODEC, G723_63)) {
+ ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) {
+ ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
+ return -1;
+ }
+ p->lastformat = AST_FORMAT_G723_1;
+ p->lastinput = AST_FORMAT_G723_1;
+ /* Reset output buffer */
+ p->obuflen = 0;
+ codecset = 1;
+ }
+ if (frame->datalen > 24) {
+ ast_log(LOG_WARNING, "Frame size too large for G.723.1 (%d bytes)\n", frame->datalen);
+ return -1;
+ }
+ maxfr = 24;
+ } else if (frame->subclass == AST_FORMAT_SLINEAR) {
+ if (p->lastformat != AST_FORMAT_SLINEAR) {
+ ioctl(p->fd, PHONE_PLAY_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (ioctl(p->fd, PHONE_PLAY_CODEC, LINEAR16)) {
+ ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) {
+ ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
+ return -1;
+ }
+ p->lastformat = AST_FORMAT_SLINEAR;
+ p->lastinput = AST_FORMAT_SLINEAR;
+ codecset = 1;
+ /* Reset output buffer */
+ p->obuflen = 0;
+ }
+ maxfr = 480;
+ } else if (frame->subclass == AST_FORMAT_ULAW) {
+ if (p->lastformat != AST_FORMAT_ULAW) {
+ ioctl(p->fd, PHONE_PLAY_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (ioctl(p->fd, PHONE_PLAY_CODEC, ULAW)) {
+ ast_log(LOG_WARNING, "Unable to set uLaw mode\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) {
+ ast_log(LOG_WARNING, "Unable to set uLaw mode\n");
+ return -1;
+ }
+ p->lastformat = AST_FORMAT_ULAW;
+ p->lastinput = AST_FORMAT_ULAW;
+ codecset = 1;
+ /* Reset output buffer */
+ p->obuflen = 0;
+ }
+ maxfr = 240;
+ } else {
+ if (p->lastformat != frame->subclass) {
+ ioctl(p->fd, PHONE_PLAY_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (ioctl(p->fd, PHONE_PLAY_CODEC, frame->subclass)) {
+ ast_log(LOG_WARNING, "Unable to set %d mode\n",
+ frame->subclass);
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_CODEC, frame->subclass)) {
+ ast_log(LOG_WARNING, "Unable to set %d mode\n",
+ frame->subclass);
+ return -1;
+ }
+ p->lastformat = frame->subclass;
+ p->lastinput = frame->subclass;
+ codecset = 1;
+ /* Reset output buffer */
+ p->obuflen = 0;
+ }
+ maxfr = 480;
+ }
+ if (codecset) {
+ ioctl(p->fd, PHONE_REC_DEPTH, 3);
+ ioctl(p->fd, PHONE_PLAY_DEPTH, 3);
+ if (ioctl(p->fd, PHONE_PLAY_START)) {
+ ast_log(LOG_WARNING, "Failed to start playback\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_START)) {
+ ast_log(LOG_WARNING, "Failed to start recording\n");
+ return -1;
+ }
+ }
+ /* If we get here, we have a frame of Appropriate data */
+ sofar = 0;
+ pos = frame->data;
+ while(sofar < frame->datalen) {
+ /* Write in no more than maxfr sized frames */
+ expected = frame->datalen - sofar;
+ if (maxfr < expected)
+ expected = maxfr;
+ /* XXX Internet Phone Jack does not handle the 4-byte VAD frame properly! XXX
+ we have to pad it to 24 bytes still. */
+ if (frame->datalen == 4) {
+ if (p->silencesupression) {
+ memset(tmpbuf + 4, 0, sizeof(tmpbuf) - 4);
+ memcpy(tmpbuf, frame->data, 4);
+ expected = 24;
+ res = phone_write_buf(p, tmpbuf, expected, maxfr, 0);
+ }
+ res = 4;
+ expected=4;
+ } else {
+ int swap = 0;
+#if __BYTE_ORDER == __BIG_ENDIAN
+ if (frame->subclass == AST_FORMAT_SLINEAR)
+ swap = 1; /* Swap big-endian samples to little-endian as we copy */
+#endif
+ res = phone_write_buf(p, pos, expected, maxfr, swap);
+ }
+ if (res != expected) {
+ if ((errno != EAGAIN) && (errno != EINTR)) {
+ if (res < 0)
+ ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno));
+ /*
+ * Card is in non-blocking mode now and it works well now, but there are
+ * lot of messages like this. So, this message is temporarily disabled.
+ */
+#if 0
+ else
+ ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);
+#endif
+ return -1;
+ } else /* Pretend it worked */
+ res = expected;
+ }
+ sofar += res;
+ pos += res;
+ }
+ return 0;
+}
+
+static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *context)
+{
+ struct ast_channel *tmp;
+ struct phone_codec_data codec;
+ tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, 0, "Phone/%s", i->dev + 5);
+ if (tmp) {
+ tmp->tech = cur_tech;
+ ast_channel_set_fd(tmp, 0, i->fd);
+ /* XXX Switching formats silently causes kernel panics XXX */
+ if (i->mode == MODE_FXS &&
+ ioctl(i->fd, PHONE_QUERY_CODEC, &codec) == 0) {
+ if (codec.type == LINEAR16)
+ tmp->nativeformats =
+ tmp->rawreadformat =
+ tmp->rawwriteformat =
+ AST_FORMAT_SLINEAR;
+ else {
+ tmp->nativeformats =
+ tmp->rawreadformat =
+ tmp->rawwriteformat =
+ prefformat & ~AST_FORMAT_SLINEAR;
+ }
+ }
+ else {
+ tmp->nativeformats = prefformat;
+ tmp->rawreadformat = prefformat;
+ tmp->rawwriteformat = prefformat;
+ }
+ /* no need to call ast_setstate: the channel_alloc already did its job */
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->tech_pvt = i;
+ ast_copy_string(tmp->context, context, sizeof(tmp->context));
+ if (!ast_strlen_zero(i->ext))
+ ast_copy_string(tmp->exten, i->ext, sizeof(tmp->exten));
+ else
+ strcpy(tmp->exten, "s");
+ if (!ast_strlen_zero(i->language))
+ ast_string_field_set(tmp, language, i->language);
+
+ /* Don't use ast_set_callerid() here because it will
+ * generate a NewCallerID event before the NewChannel event */
+ tmp->cid.cid_ani = ast_strdup(i->cid_num);
+
+ i->owner = tmp;
+ ast_module_ref(ast_module_info->self);
+ if (state != AST_STATE_DOWN) {
+ if (state == AST_STATE_RING) {
+ ioctl(tmp->fds[0], PHONE_RINGBACK);
+ i->cpt = 1;
+ }
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ return tmp;
+}
+
+static void phone_mini_packet(struct phone_pvt *i)
+{
+ int res;
+ char buf[1024];
+ /* Ignore stuff we read... */
+ res = read(i->fd, buf, sizeof(buf));
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Read returned %d: %s\n", res, strerror(errno));
+ return;
+ }
+}
+
+static void phone_check_exception(struct phone_pvt *i)
+{
+ int offhook=0;
+ char digit[2] = {0 , 0};
+ union telephony_exception phonee;
+ /* XXX Do something XXX */
+#if 0
+ ast_debug(1, "Exception!\n");
+#endif
+ phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION);
+ if (phonee.bits.dtmf_ready) {
+ digit[0] = ioctl(i->fd, PHONE_GET_DTMF_ASCII);
+ if (i->mode == MODE_DIALTONE || i->mode == MODE_FXS || i->mode == MODE_SIGMA) {
+ ioctl(i->fd, PHONE_PLAY_STOP);
+ ioctl(i->fd, PHONE_REC_STOP);
+ ioctl(i->fd, PHONE_CPT_STOP);
+ i->dialtone = 0;
+ if (strlen(i->ext) < AST_MAX_EXTENSION - 1)
+ strncat(i->ext, digit, sizeof(i->ext) - strlen(i->ext) - 1);
+ if ((i->mode != MODE_FXS ||
+ !(phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION)) ||
+ !phonee.bits.dtmf_ready) &&
+ ast_exists_extension(NULL, i->context, i->ext, 1, i->cid_num)) {
+ /* It's a valid extension in its context, get moving! */
+ phone_new(i, AST_STATE_RING, i->context);
+ /* No need to restart monitor, we are the monitor */
+ } else if (!ast_canmatch_extension(NULL, i->context, i->ext, 1, i->cid_num)) {
+ /* There is nothing in the specified extension that can match anymore.
+ Try the default */
+ if (ast_exists_extension(NULL, "default", i->ext, 1, i->cid_num)) {
+ /* Check the default, too... */
+ phone_new(i, AST_STATE_RING, "default");
+ /* XXX This should probably be justified better XXX */
+ } else if (!ast_canmatch_extension(NULL, "default", i->ext, 1, i->cid_num)) {
+ /* It's not a valid extension, give a busy signal */
+ ast_debug(1, "%s can't match anything in %s or default\n", i->ext, i->context);
+ ioctl(i->fd, PHONE_BUSY);
+ i->cpt = 1;
+ }
+ }
+#if 0
+ ast_verbose("Extension is %s\n", i->ext);
+#endif
+ }
+ }
+ if (phonee.bits.hookstate) {
+ offhook = ioctl(i->fd, PHONE_HOOKSTATE);
+ if (offhook) {
+ if (i->mode == MODE_IMMEDIATE) {
+ phone_new(i, AST_STATE_RING, i->context);
+ } else if (i->mode == MODE_DIALTONE) {
+ ast_module_ref(ast_module_info->self);
+ /* Reset the extension */
+ i->ext[0] = '\0';
+ /* Play the dialtone */
+ i->dialtone++;
+ ioctl(i->fd, PHONE_PLAY_STOP);
+ ioctl(i->fd, PHONE_PLAY_CODEC, ULAW);
+ ioctl(i->fd, PHONE_PLAY_START);
+ i->lastformat = -1;
+ } else if (i->mode == MODE_SIGMA) {
+ ast_module_ref(ast_module_info->self);
+ /* Reset the extension */
+ i->ext[0] = '\0';
+ /* Play the dialtone */
+ i->dialtone++;
+ ioctl(i->fd, PHONE_DIALTONE);
+ }
+ } else {
+ if (i->dialtone)
+ ast_module_unref(ast_module_info->self);
+ memset(i->ext, 0, sizeof(i->ext));
+ if (i->cpt)
+ {
+ ioctl(i->fd, PHONE_CPT_STOP);
+ i->cpt = 0;
+ }
+ ioctl(i->fd, PHONE_PLAY_STOP);
+ ioctl(i->fd, PHONE_REC_STOP);
+ i->dialtone = 0;
+ i->lastformat = -1;
+ }
+ }
+ if (phonee.bits.pstn_ring) {
+ ast_verbose("Unit is ringing\n");
+ phone_new(i, AST_STATE_RING, i->context);
+ }
+ if (phonee.bits.caller_id)
+ ast_verbose("We have caller ID\n");
+
+
+}
+
+static void *do_monitor(void *data)
+{
+ fd_set rfds, efds;
+ int n, res;
+ struct phone_pvt *i;
+ int tonepos = 0;
+ /* The tone we're playing this round */
+ struct timeval tv = {0,0};
+ int dotone;
+ /* This thread monitors all the frame relay interfaces which are not yet in use
+ (and thus do not have a separate thread) indefinitely */
+ while (monitor) {
+ /* Don't let anybody kill us right away. Nobody should lock the interface list
+ and wait for the monitor list, but the other way around is okay. */
+ /* Lock the interface list */
+ if (ast_mutex_lock(&iflock)) {
+ ast_log(LOG_ERROR, "Unable to grab interface lock\n");
+ return NULL;
+ }
+ /* Build the stuff we're going to select on, that is the socket of every
+ phone_pvt that does not have an associated owner channel */
+ n = -1;
+ FD_ZERO(&rfds);
+ FD_ZERO(&efds);
+ i = iflist;
+ dotone = 0;
+ while (i) {
+ if (FD_ISSET(i->fd, &rfds))
+ ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->fd, i->dev);
+ if (!i->owner) {
+ /* This needs to be watched, as it lacks an owner */
+ FD_SET(i->fd, &rfds);
+ FD_SET(i->fd, &efds);
+ if (i->fd > n)
+ n = i->fd;
+ if (i->dialtone && i->mode != MODE_SIGMA) {
+ /* Remember we're going to have to come back and play
+ more dialtones */
+ if (ast_tvzero(tv)) {
+ /* If we're due for a dialtone, play one */
+ if (write(i->fd, DialTone + tonepos, 240) != 240)
+ ast_log(LOG_WARNING, "Dial tone write error\n");
+ }
+ dotone++;
+ }
+ }
+
+ i = i->next;
+ }
+ /* Okay, now that we know what to do, release the interface lock */
+ ast_mutex_unlock(&iflock);
+
+ /* Wait indefinitely for something to happen */
+ if (dotone && i && i->mode != MODE_SIGMA) {
+ /* If we're ready to recycle the time, set it to 30 ms */
+ tonepos += 240;
+ if (tonepos >= sizeof(DialTone))
+ tonepos = 0;
+ if (ast_tvzero(tv)) {
+ tv = ast_tv(30000, 0);
+ }
+ res = ast_select(n + 1, &rfds, NULL, &efds, &tv);
+ } else {
+ res = ast_select(n + 1, &rfds, NULL, &efds, NULL);
+ tv = ast_tv(0,0);
+ tonepos = 0;
+ }
+ /* Okay, select has finished. Let's see what happened. */
+ if (res < 0) {
+ ast_debug(1, "select return %d: %s\n", res, strerror(errno));
+ continue;
+ }
+ /* If there are no fd's changed, just continue, it's probably time
+ to play some more dialtones */
+ if (!res)
+ continue;
+ /* Alright, lock the interface list again, and let's look and see what has
+ happened */
+ if (ast_mutex_lock(&iflock)) {
+ ast_log(LOG_WARNING, "Unable to lock the interface list\n");
+ continue;
+ }
+
+ i = iflist;
+ for(; i; i=i->next) {
+ if (FD_ISSET(i->fd, &rfds)) {
+ if (i->owner) {
+ continue;
+ }
+ phone_mini_packet(i);
+ }
+ if (FD_ISSET(i->fd, &efds)) {
+ if (i->owner) {
+ continue;
+ }
+ phone_check_exception(i);
+ }
+ }
+ ast_mutex_unlock(&iflock);
+ }
+ return NULL;
+
+}
+
+static int restart_monitor()
+{
+ /* If we're supposed to be stopped -- stay stopped */
+ if (monitor_thread == AST_PTHREADT_STOP)
+ return 0;
+ if (ast_mutex_lock(&monlock)) {
+ ast_log(LOG_WARNING, "Unable to lock monitor\n");
+ return -1;
+ }
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ if (ast_mutex_lock(&iflock)) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Unable to lock the interface list\n");
+ return -1;
+ }
+ monitor = 0;
+ while (pthread_kill(monitor_thread, SIGURG) == 0)
+ sched_yield();
+ pthread_join(monitor_thread, NULL);
+ ast_mutex_unlock(&iflock);
+ }
+ monitor = 1;
+ /* Start a new monitor */
+ if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+static struct phone_pvt *mkif(const char *iface, int mode, int txgain, int rxgain)
+{
+ /* Make a phone_pvt structure for this interface */
+ struct phone_pvt *tmp;
+ int flags;
+
+ tmp = ast_calloc(1, sizeof(*tmp));
+ if (tmp) {
+ tmp->fd = open(iface, O_RDWR);
+ if (tmp->fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open '%s'\n", iface);
+ ast_free(tmp);
+ return NULL;
+ }
+ if (mode == MODE_FXO) {
+ if (ioctl(tmp->fd, IXJCTL_PORT, PORT_PSTN)) {
+ ast_debug(1, "Unable to set port to PSTN\n");
+ }
+ } else {
+ if (ioctl(tmp->fd, IXJCTL_PORT, PORT_POTS))
+ if (mode != MODE_FXS)
+ ast_debug(1, "Unable to set port to POTS\n");
+ }
+ ioctl(tmp->fd, PHONE_PLAY_STOP);
+ ioctl(tmp->fd, PHONE_REC_STOP);
+ ioctl(tmp->fd, PHONE_RING_STOP);
+ ioctl(tmp->fd, PHONE_CPT_STOP);
+ if (ioctl(tmp->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK))
+ ast_debug(1, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",iface, strerror(errno));
+ if (echocancel != AEC_OFF)
+ ioctl(tmp->fd, IXJCTL_AEC_START, echocancel);
+ if (silencesupression)
+ tmp->silencesupression = 1;
+#ifdef PHONE_VAD
+ ioctl(tmp->fd, PHONE_VAD, tmp->silencesupression);
+#endif
+ tmp->mode = mode;
+ flags = fcntl(tmp->fd, F_GETFL);
+ fcntl(tmp->fd, F_SETFL, flags | O_NONBLOCK);
+ tmp->owner = NULL;
+ tmp->lastformat = -1;
+ tmp->lastinput = -1;
+ tmp->ministate = 0;
+ memset(tmp->ext, 0, sizeof(tmp->ext));
+ ast_copy_string(tmp->language, language, sizeof(tmp->language));
+ ast_copy_string(tmp->dev, iface, sizeof(tmp->dev));
+ ast_copy_string(tmp->context, context, sizeof(tmp->context));
+ tmp->next = NULL;
+ tmp->obuflen = 0;
+ tmp->dialtone = 0;
+ tmp->cpt = 0;
+ ast_copy_string(tmp->cid_num, cid_num, sizeof(tmp->cid_num));
+ ast_copy_string(tmp->cid_name, cid_name, sizeof(tmp->cid_name));
+ tmp->txgain = txgain;
+ ioctl(tmp->fd, PHONE_PLAY_VOLUME, tmp->txgain);
+ tmp->rxgain = rxgain;
+ ioctl(tmp->fd, PHONE_REC_VOLUME, tmp->rxgain);
+ }
+ return tmp;
+}
+
+static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+ struct phone_pvt *p;
+ struct ast_channel *tmp = NULL;
+ char *name = data;
+
+ /* Search for an unowned channel */
+ if (ast_mutex_lock(&iflock)) {
+ ast_log(LOG_ERROR, "Unable to lock interface list???\n");
+ return NULL;
+ }
+ p = iflist;
+ while(p) {
+ if (p->mode == MODE_FXS ||
+ format & (AST_FORMAT_G729A | AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW)) {
+ size_t length = strlen(p->dev + 5);
+ if (strncmp(name, p->dev + 5, length) == 0 &&
+ !isalnum(name[length])) {
+ if (!p->owner) {
+ tmp = phone_new(p, AST_STATE_DOWN, p->context);
+ break;
+ } else
+ *cause = AST_CAUSE_BUSY;
+ }
+ }
+ p = p->next;
+ }
+ ast_mutex_unlock(&iflock);
+ restart_monitor();
+ if (tmp == NULL) {
+ oldformat = format;
+ format &= (AST_FORMAT_G729A | AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW);
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+ return NULL;
+ }
+ }
+ return tmp;
+}
+
+/* parse gain value from config file */
+static int parse_gain_value(const char *gain_type, const char *value)
+{
+ float gain;
+
+ /* try to scan number */
+ if (sscanf(value, "%f", &gain) != 1)
+ {
+ ast_log(LOG_ERROR, "Invalid %s value '%s' in '%s' config\n",
+ value, gain_type, config);
+ return DEFAULT_GAIN;
+ }
+
+ /* multiplicate gain by 1.0 gain value */
+ gain = gain * (float)DEFAULT_GAIN;
+
+ /* percentage? */
+ if (value[strlen(value) - 1] == '%')
+ return (int)(gain / (float)100);
+
+ return (int)gain;
+}
+
+static int __unload_module(void)
+{
+ struct phone_pvt *p, *pl;
+ /* First, take us out of the channel loop */
+ if (cur_tech)
+ ast_channel_unregister(cur_tech);
+ if (!ast_mutex_lock(&iflock)) {
+ /* Hangup all interfaces if they have an owner */
+ p = iflist;
+ while(p) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ p = p->next;
+ }
+ iflist = NULL;
+ ast_mutex_unlock(&iflock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+ if (!ast_mutex_lock(&monlock)) {
+ if (monitor_thread > AST_PTHREADT_NULL) {
+ monitor = 0;
+ while (pthread_kill(monitor_thread, SIGURG) == 0)
+ sched_yield();
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+
+ if (!ast_mutex_lock(&iflock)) {
+ /* Destroy all the interfaces and free their memory */
+ p = iflist;
+ while(p) {
+ /* Close the socket, assuming it's real */
+ if (p->fd > -1)
+ close(p->fd);
+ pl = p;
+ p = p->next;
+ /* Free associated memory */
+ ast_free(pl);
+ }
+ iflist = NULL;
+ ast_mutex_unlock(&iflock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return __unload_module();
+}
+
+static int load_module(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct phone_pvt *tmp;
+ int mode = MODE_IMMEDIATE;
+ int txgain = DEFAULT_GAIN, rxgain = DEFAULT_GAIN; /* default gain 1.0 */
+ struct ast_flags config_flags = { 0 };
+
+ cfg = ast_config_load(config, config_flags);
+
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ if (ast_mutex_lock(&iflock)) {
+ /* It's a little silly to lock it, but we mind as well just to be sure */
+ ast_log(LOG_ERROR, "Unable to lock interface list???\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ v = ast_variable_browse(cfg, "interfaces");
+ while(v) {
+ /* Create the interface list */
+ if (!strcasecmp(v->name, "device")) {
+ tmp = mkif(v->value, mode, txgain, rxgain);
+ if (tmp) {
+ tmp->next = iflist;
+ iflist = tmp;
+
+ } else {
+ ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value);
+ ast_config_destroy(cfg);
+ ast_mutex_unlock(&iflock);
+ __unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ } else if (!strcasecmp(v->name, "silencesupression")) {
+ silencesupression = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(language, v->value, sizeof(language));
+ } else if (!strcasecmp(v->name, "callerid")) {
+ ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
+ } else if (!strcasecmp(v->name, "mode")) {
+ if (!strncasecmp(v->value, "di", 2))
+ mode = MODE_DIALTONE;
+ else if (!strncasecmp(v->value, "sig", 3))
+ mode = MODE_SIGMA;
+ else if (!strncasecmp(v->value, "im", 2))
+ mode = MODE_IMMEDIATE;
+ else if (!strncasecmp(v->value, "fxs", 3)) {
+ mode = MODE_FXS;
+ prefformat = 0x01ff0000; /* All non-voice */
+ }
+ else if (!strncasecmp(v->value, "fx", 2))
+ mode = MODE_FXO;
+ else
+ ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value);
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(context, v->value, sizeof(context));
+ } else if (!strcasecmp(v->name, "format")) {
+ if (!strcasecmp(v->value, "g729")) {
+ prefformat = AST_FORMAT_G729A;
+ } else if (!strcasecmp(v->value, "g723.1")) {
+ prefformat = AST_FORMAT_G723_1;
+ } else if (!strcasecmp(v->value, "slinear")) {
+ if (mode == MODE_FXS)
+ prefformat |= AST_FORMAT_SLINEAR;
+ else prefformat = AST_FORMAT_SLINEAR;
+ } else if (!strcasecmp(v->value, "ulaw")) {
+ prefformat = AST_FORMAT_ULAW;
+ } else
+ ast_log(LOG_WARNING, "Unknown format '%s'\n", v->value);
+ } else if (!strcasecmp(v->name, "echocancel")) {
+ if (!strcasecmp(v->value, "off")) {
+ echocancel = AEC_OFF;
+ } else if (!strcasecmp(v->value, "low")) {
+ echocancel = AEC_LOW;
+ } else if (!strcasecmp(v->value, "medium")) {
+ echocancel = AEC_MED;
+ } else if (!strcasecmp(v->value, "high")) {
+ echocancel = AEC_HIGH;
+ } else
+ ast_log(LOG_WARNING, "Unknown echo cancellation '%s'\n", v->value);
+ } else if (!strcasecmp(v->name, "txgain")) {
+ txgain = parse_gain_value(v->name, v->value);
+ } else if (!strcasecmp(v->name, "rxgain")) {
+ rxgain = parse_gain_value(v->name, v->value);
+ }
+ v = v->next;
+ }
+ ast_mutex_unlock(&iflock);
+
+ if (mode == MODE_FXS) {
+ phone_tech_fxs.capabilities = prefformat;
+ cur_tech = &phone_tech_fxs;
+ } else
+ cur_tech = (struct ast_channel_tech *) &phone_tech;
+
+ /* Make sure we can register our Adtranphone channel type */
+
+ if (ast_channel_register(cur_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Phone'\n");
+ ast_config_destroy(cfg);
+ __unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ast_config_destroy(cfg);
+ /* And start the monitor for the first time */
+ restart_monitor();
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Linux Telephony API Support");
diff --git a/trunk/channels/chan_sip.c b/trunk/channels/chan_sip.c
new file mode 100644
index 000000000..1d2f38feb
--- /dev/null
+++ b/trunk/channels/chan_sip.c
@@ -0,0 +1,21227 @@
+/*
+ * 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 Implementation of Session Initiation Protocol
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ *
+ * Implementation of RFC 3261 - without S/MIME, TCP and TLS support
+ * Configuration file \link Config_sip sip.conf \endlink
+ *
+ *
+ * \todo SIP over TCP
+ * \todo SIP over TLS
+ * \todo Better support of forking
+ * \todo VIA branch tag transaction checking
+ * \todo Transaction support
+ *
+ * \ingroup channel_drivers
+ *
+ * \par Overview of the handling of SIP sessions
+ * The SIP channel handles several types of SIP sessions, or dialogs,
+ * not all of them being "telephone calls".
+ * - Incoming calls that will be sent to the PBX core
+ * - Outgoing calls, generated by the PBX
+ * - SIP subscriptions and notifications of states and voicemail messages
+ * - SIP registrations, both inbound and outbound
+ * - SIP peer management (peerpoke, OPTIONS)
+ * - SIP text messages
+ *
+ * In the SIP channel, there's a list of active SIP dialogs, which includes
+ * all of these when they are active. "sip show channels" in the CLI will
+ * show most of these, excluding subscriptions which are shown by
+ * "sip show subscriptions"
+ *
+ * \par incoming packets
+ * Incoming packets are received in the monitoring thread, then handled by
+ * sipsock_read(). This function parses the packet and matches an existing
+ * dialog or starts a new SIP dialog.
+ *
+ * sipsock_read sends the packet to handle_incoming(), that parses a bit more.
+ * If it is a response to an outbound request, the packet is sent to handle_response().
+ * If it is a request, handle_incoming() sends it to one of a list of functions
+ * depending on the request type - INVITE, OPTIONS, REFER, BYE, CANCEL etc
+ * sipsock_read locks the ast_channel if it exists (an active call) and
+ * unlocks it after we have processed the SIP message.
+ *
+ * A new INVITE is sent to handle_request_invite(), that will end up
+ * starting a new channel in the PBX, the new channel after that executing
+ * in a separate channel thread. This is an incoming "call".
+ * When the call is answered, either by a bridged channel or the PBX itself
+ * the sip_answer() function is called.
+ *
+ * The actual media - Video or Audio - is mostly handled by the RTP subsystem
+ * in rtp.c
+ *
+ * \par Outbound calls
+ * Outbound calls are set up by the PBX through the sip_request_call()
+ * function. After that, they are activated by sip_call().
+ *
+ * \par Hanging up
+ * The PBX issues a hangup on both incoming and outgoing calls through
+ * the sip_hangup() function
+ */
+
+/*** MODULEINFO
+ <depend>res_features</depend>
+ ***/
+
+/*! \page sip_session_timers SIP Session Timers in Asterisk Chan_sip
+
+ The SIP Session-Timers is an extension of the SIP protocol that allows end-points and proxies to
+ refresh a session periodically. The sessions are kept alive by sending a RE-INVITE or UPDATE
+ request at a negotiated interval. If a session refresh fails then all the entities that support Session-
+ Timers clear their internal session state. In addition, UAs generate a BYE request in order to clear
+ the state in the proxies and the remote UA (this is done for the benefit of SIP entities in the path
+ that do not support Session-Timers).
+
+ The Session-Timers can be configured on a system-wide, per-user, or per-peer basis. The peruser/
+ per-peer settings override the global settings. The following new parameters have been
+ added to the sip.conf file.
+ session-timers=["accept", "originate", "refuse"]
+ session-expires=[integer]
+ session-minse=[integer]
+ session-refresher=["uas", "uac"]
+
+ The session-timers parameter in sip.conf defines the mode of operation of SIP session-timers feature in
+ Asterisk. The Asterisk can be configured in one of the following three modes:
+
+ 1. Accept :: In the "accept" mode, the Asterisk server honors session-timers requests
+ made by remote end-points. A remote end-point can request Asterisk to engage
+ session-timers by either sending it an INVITE request with a "Supported: timer"
+ header in it or by responding to Asterisk's INVITE with a 200 OK that contains
+ Session-Expires: header in it. In this mode, the Asterisk server does not
+ request session-timers from remote end-points. This is the default mode.
+ 2. Originate :: In the "originate" mode, the Asterisk server requests the remote
+ end-points to activate session-timers in addition to honoring such requests
+ made by the remote end-pints. In order to get as much protection as possible
+ against hanging SIP channels due to network or end-point failures, Asterisk
+ resends periodic re-INVITEs even if a remote end-point does not support
+ the session-timers feature.
+ 3. Refuse :: In the "refuse" mode, Asterisk acts as if it does not support session-
+ timers for inbound or outbound requests. If a remote end-point requests
+ session-timers in a dialog, then Asterisk ignores that request unless it's
+ noted as a requirement (Require: header), in which case the INVITE is
+ rejected with a 420 Bad Extension response.
+
+*/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/signal.h>
+#include <regex.h>
+
+#include "asterisk/network.h"
+#include "asterisk/paths.h" /* need ast_config_AST_SYSTEM_NAME */
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/udptl.h"
+#include "asterisk/acl.h"
+#include "asterisk/manager.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/dsp.h"
+#include "asterisk/features.h"
+#include "asterisk/srv.h"
+#include "asterisk/astdb.h"
+#include "asterisk/causes.h"
+#include "asterisk/utils.h"
+#include "asterisk/file.h"
+#include "asterisk/astobj.h"
+#include "asterisk/dnsmgr.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/monitor.h"
+#include "asterisk/netsock.h"
+#include "asterisk/localtime.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/translate.h"
+#include "asterisk/version.h"
+#include "asterisk/event.h"
+#include "asterisk/tcptls.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define XMIT_ERROR -2
+
+/* #define VOCAL_DATA_HACK */
+
+#define DEFAULT_DEFAULT_EXPIRY 120
+#define DEFAULT_MIN_EXPIRY 60
+#define DEFAULT_MAX_EXPIRY 3600
+#define DEFAULT_REGISTRATION_TIMEOUT 20
+#define DEFAULT_MAX_FORWARDS "70"
+
+/* guard limit must be larger than guard secs */
+/* guard min must be < 1000, and should be >= 250 */
+#define EXPIRY_GUARD_SECS 15 /*!< How long before expiry do we reregister */
+#define EXPIRY_GUARD_LIMIT 30 /*!< Below here, we use EXPIRY_GUARD_PCT instead of
+ EXPIRY_GUARD_SECS */
+#define EXPIRY_GUARD_MIN 500 /*!< This is the minimum guard time applied. If
+ GUARD_PCT turns out to be lower than this, it
+ will use this time instead.
+ This is in milliseconds. */
+#define EXPIRY_GUARD_PCT 0.20 /*!< Percentage of expires timeout to use when
+ below EXPIRY_GUARD_LIMIT */
+#define DEFAULT_EXPIRY 900 /*!< Expire slowly */
+
+static int min_expiry = DEFAULT_MIN_EXPIRY; /*!< Minimum accepted registration time */
+static int max_expiry = DEFAULT_MAX_EXPIRY; /*!< Maximum accepted registration time */
+static int default_expiry = DEFAULT_DEFAULT_EXPIRY;
+static int expiry = DEFAULT_EXPIRY;
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define CALLERID_UNKNOWN "Unknown"
+
+#define DEFAULT_MAXMS 2000 /*!< Qualification: Must be faster than 2 seconds by default */
+#define DEFAULT_QUALIFYFREQ 60 * 1000 /*!< Qualification: How often to check for the host to be up */
+#define DEFAULT_FREQ_NOTOK 10 * 1000 /*!< Qualification: How often to check, if the host is down... */
+
+#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit Default: 2 * 500 ms in RFC 3261 */
+#define MAX_RETRANS 6 /*!< Try only 6 times for retransmissions, a total of 7 transmissions */
+#define SIP_TIMER_T1 500 /* SIP timer T1 (according to RFC 3261) */
+#define SIP_TRANS_TIMEOUT 64 * SIP_TIMER_T1/*!< SIP request timeout (rfc 3261) 64*T1
+ \todo Use known T1 for timeout (peerpoke)
+ */
+#define DEFAULT_TRANS_TIMEOUT -1 /* Use default SIP transaction timeout */
+#define MAX_AUTHTRIES 3 /*!< Try authentication three times, then fail */
+
+#define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */
+#define SIP_MAX_LINES 64 /*!< Max amount of lines in SIP attachment (like SDP) */
+#define SIP_MAX_PACKET 4096 /*!< Also from RFC 3261 (2543), should sub headers tho */
+
+#define INITIAL_CSEQ 101 /*!< our initial sip sequence number */
+
+#define DEFAULT_MAX_SE 1800 /*!< Session-Timer Default Session-Expires period (RFC 4028) */
+#define DEFAULT_MIN_SE 90 /*!< Session-Timer Default Min-SE period (RFC 4028) */
+
+/*! \brief Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf; /*!< Global jitterbuffer configuration */
+
+static const char config[] = "sip.conf"; /*!< Main configuration file */
+static const char notify_config[] = "sip_notify.conf"; /*!< Configuration file for sending Notify with CLI commands to reconfigure or reboot phones */
+
+#define RTP 1
+#define NO_RTP 0
+
+/*! \brief Authorization scheme for call transfers
+\note Not a bitfield flag, since there are plans for other modes,
+ like "only allow transfers for authenticated devices" */
+enum transfermodes {
+ TRANSFER_OPENFORALL, /*!< Allow all SIP transfers */
+ TRANSFER_CLOSED, /*!< Allow no SIP transfers */
+};
+
+
+enum sip_result {
+ AST_SUCCESS = 0,
+ AST_FAILURE = -1,
+};
+
+/*! \brief States for the INVITE transaction, not the dialog
+ \note this is for the INVITE that sets up the dialog
+*/
+enum invitestates {
+ INV_NONE = 0, /*!< No state at all, maybe not an INVITE dialog */
+ INV_CALLING = 1, /*!< Invite sent, no answer */
+ INV_PROCEEDING = 2, /*!< We got/sent 1xx message */
+ INV_EARLY_MEDIA = 3, /*!< We got 18x message with to-tag back */
+ INV_COMPLETED = 4, /*!< Got final response with error. Wait for ACK, then CONFIRMED */
+ INV_CONFIRMED = 5, /*!< Confirmed response - we've got an ack (Incoming calls only) */
+ INV_TERMINATED = 6, /*!< Transaction done - either successful (AST_STATE_UP) or failed, but done
+ The only way out of this is a BYE from one side */
+ INV_CANCELLED = 7, /*!< Transaction cancelled by client or server in non-terminated state */
+};
+
+enum xmittype {
+ XMIT_CRITICAL = 2, /*!< Transmit critical SIP message reliably, with re-transmits.
+ If it fails, it's critical and will cause a teardown of the session */
+ XMIT_RELIABLE = 1, /*!< Transmit SIP message reliably, with re-transmits */
+ XMIT_UNRELIABLE = 0, /*!< Transmit SIP message without bothering with re-transmits */
+};
+
+enum parse_register_result {
+ PARSE_REGISTER_FAILED,
+ PARSE_REGISTER_UPDATE,
+ PARSE_REGISTER_QUERY,
+};
+
+enum subscriptiontype {
+ NONE = 0,
+ XPIDF_XML,
+ DIALOG_INFO_XML,
+ CPIM_PIDF_XML,
+ PIDF_XML,
+ MWI_NOTIFICATION
+};
+
+/*! \brief Subscription types that we support. We support
+ - dialoginfo updates (really device status, not dialog info as was the original intent of the standard)
+ - SIMPLE presence used for device status
+ - Voicemail notification subscriptions
+*/
+static const struct cfsubscription_types {
+ enum subscriptiontype type;
+ const char * const event;
+ const char * const mediatype;
+ const char * const text;
+} subscription_types[] = {
+ { NONE, "-", "unknown", "unknown" },
+ /* RFC 4235: SIP Dialog event package */
+ { DIALOG_INFO_XML, "dialog", "application/dialog-info+xml", "dialog-info+xml" },
+ { CPIM_PIDF_XML, "presence", "application/cpim-pidf+xml", "cpim-pidf+xml" }, /* RFC 3863 */
+ { PIDF_XML, "presence", "application/pidf+xml", "pidf+xml" }, /* RFC 3863 */
+ { XPIDF_XML, "presence", "application/xpidf+xml", "xpidf+xml" }, /* Pre-RFC 3863 with MS additions */
+ { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" } /* RFC 3842: Mailbox notification */
+};
+
+
+/*! \brief Authentication types - proxy or www authentication
+ \note Endpoints, like Asterisk, should always use WWW authentication to
+ allow multiple authentications in the same call - to the proxy and
+ to the end point.
+*/
+enum sip_auth_type {
+ PROXY_AUTH = 407,
+ WWW_AUTH = 401,
+};
+
+/*! \brief Authentication result from check_auth* functions */
+enum check_auth_result {
+ AUTH_DONT_KNOW = -100, /*!< no result, need to check further */
+ /* XXX maybe this is the same as AUTH_NOT_FOUND */
+
+ AUTH_SUCCESSFUL = 0,
+ AUTH_CHALLENGE_SENT = 1,
+ AUTH_SECRET_FAILED = -1,
+ AUTH_USERNAME_MISMATCH = -2,
+ AUTH_NOT_FOUND = -3, /*!< returned by register_verify */
+ AUTH_FAKE_AUTH = -4,
+ AUTH_UNKNOWN_DOMAIN = -5,
+ AUTH_PEER_NOT_DYNAMIC = -6,
+ AUTH_ACL_FAILED = -7,
+};
+
+/*! \brief States for outbound registrations (with register= lines in sip.conf */
+enum sipregistrystate {
+ REG_STATE_UNREGISTERED = 0, /*!< We are not registred */
+ /* Initial state. We should have a timeout scheduled for the initial
+ * (or next) registration transmission, calling sip_reregister
+ */
+
+ REG_STATE_REGSENT, /*!< Registration request sent */
+ /* sent initial request, waiting for an ack or a timeout to
+ * retransmit the initial request.
+ */
+
+ REG_STATE_AUTHSENT, /*!< We have tried to authenticate */
+ /* entered after transmit_register with auth info,
+ * waiting for an ack.
+ */
+
+ REG_STATE_REGISTERED, /*!< Registered and done */
+
+ REG_STATE_REJECTED, /*!< Registration rejected */
+ /* only used when the remote party has an expire larger than
+ * our max-expire. This is a final state from which we do not
+ * recover (not sure how correctly).
+ */
+
+ REG_STATE_TIMEOUT, /*!< Registration timed out */
+ /* XXX unused */
+
+ REG_STATE_NOAUTH, /*!< We have no accepted credentials */
+ /* fatal - no chance to proceed */
+
+ REG_STATE_FAILED, /*!< Registration failed after several tries */
+ /* fatal - no chance to proceed */
+};
+
+/*! \brief Modes in which Asterisk can be configured to run SIP Session-Timers */
+enum st_mode {
+ SESSION_TIMER_MODE_INVALID = 0, /*!< Invalid value */
+ SESSION_TIMER_MODE_ACCEPT, /*!< Honor inbound Session-Timer requests */
+ SESSION_TIMER_MODE_ORIGINATE, /*!< Originate outbound and honor inbound requests */
+ SESSION_TIMER_MODE_REFUSE /*!< Ignore inbound Session-Timers requests */
+};
+
+/*! \brief The entity playing the refresher role for Session-Timers */
+enum st_refresher {
+ SESSION_TIMER_REFRESHER_AUTO, /*!< Negotiated */
+ SESSION_TIMER_REFRESHER_UAC, /*!< Session is refreshed by the UAC */
+ SESSION_TIMER_REFRESHER_UAS /*!< Session is refreshed by the UAS */
+};
+
+
+/*! \brief definition of a sip proxy server
+ *
+ * For outbound proxies, this is allocated in the SIP peer dynamically or
+ * statically as the global_outboundproxy. The pointer in a SIP message is just
+ * a pointer and should *not* be de-allocated.
+ */
+struct sip_proxy {
+ char name[MAXHOSTNAMELEN]; /*!< DNS name of domain/host or IP */
+ struct sockaddr_in ip; /*!< Currently used IP address and port */
+ time_t last_dnsupdate; /*!< When this was resolved */
+ int force; /*!< If it's an outbound proxy, Force use of this outbound proxy for all outbound requests */
+ /* Room for a SRV record chain based on the name */
+};
+
+/*! \brief States whether a SIP message can create a dialog in Asterisk. */
+enum can_create_dialog {
+ CAN_NOT_CREATE_DIALOG,
+ CAN_CREATE_DIALOG,
+ CAN_CREATE_DIALOG_UNSUPPORTED_METHOD,
+};
+
+/*! \brief SIP Request methods known by Asterisk
+
+ \note Do _NOT_ make any changes to this enum, or the array following it;
+ if you think you are doing the right thing, you are probably
+ not doing the right thing. If you think there are changes
+ needed, get someone else to review them first _before_
+ submitting a patch. If these two lists do not match properly
+ bad things will happen.
+*/
+
+enum sipmethod {
+ SIP_UNKNOWN, /*!< Unknown response */
+ SIP_RESPONSE, /*!< Not request, response to outbound request */
+ SIP_REGISTER, /*!< Registration to the mothership, tell us where you are located */
+ SIP_OPTIONS, /*!< Check capabilities of a device, used for "ping" too */
+ SIP_NOTIFY, /*!< Status update, Part of the event package standard, result of a SUBSCRIBE or a REFER */
+ SIP_INVITE, /*!< Set up a session */
+ SIP_ACK, /*!< End of a three-way handshake started with INVITE. */
+ SIP_PRACK, /*!< Reliable pre-call signalling. Not supported in Asterisk. */
+ SIP_BYE, /*!< End of a session */
+ SIP_REFER, /*!< Refer to another URI (transfer) */
+ SIP_SUBSCRIBE, /*!< Subscribe for updates (voicemail, session status, device status, presence) */
+ SIP_MESSAGE, /*!< Text messaging */
+ SIP_UPDATE, /*!< Update a dialog. We can send UPDATE; but not accept it */
+ SIP_INFO, /*!< Information updates during a session */
+ SIP_CANCEL, /*!< Cancel an INVITE */
+ SIP_PUBLISH, /*!< Not supported in Asterisk */
+ SIP_PING, /*!< Not supported at all, no standard but still implemented out there */
+};
+
+/*! \brief The core structure to setup dialogs. We parse incoming messages by using
+ structure and then route the messages according to the type.
+
+ \note Note that sip_methods[i].id == i must hold or the code breaks */
+static const struct cfsip_methods {
+ enum sipmethod id;
+ int need_rtp; /*!< when this is the 'primary' use for a pvt structure, does it need RTP? */
+ char * const text;
+ enum can_create_dialog can_create;
+} sip_methods[] = {
+ { SIP_UNKNOWN, RTP, "-UNKNOWN-", CAN_CREATE_DIALOG },
+ { SIP_RESPONSE, NO_RTP, "SIP/2.0", CAN_NOT_CREATE_DIALOG },
+ { SIP_REGISTER, NO_RTP, "REGISTER", CAN_CREATE_DIALOG },
+ { SIP_OPTIONS, NO_RTP, "OPTIONS", CAN_CREATE_DIALOG },
+ { SIP_NOTIFY, NO_RTP, "NOTIFY", CAN_CREATE_DIALOG },
+ { SIP_INVITE, RTP, "INVITE", CAN_CREATE_DIALOG },
+ { SIP_ACK, NO_RTP, "ACK", CAN_NOT_CREATE_DIALOG },
+ { SIP_PRACK, NO_RTP, "PRACK", CAN_NOT_CREATE_DIALOG },
+ { SIP_BYE, NO_RTP, "BYE", CAN_NOT_CREATE_DIALOG },
+ { SIP_REFER, NO_RTP, "REFER", CAN_CREATE_DIALOG },
+ { SIP_SUBSCRIBE, NO_RTP, "SUBSCRIBE", CAN_CREATE_DIALOG },
+ { SIP_MESSAGE, NO_RTP, "MESSAGE", CAN_CREATE_DIALOG },
+ { SIP_UPDATE, NO_RTP, "UPDATE", CAN_NOT_CREATE_DIALOG },
+ { SIP_INFO, NO_RTP, "INFO", CAN_NOT_CREATE_DIALOG },
+ { SIP_CANCEL, NO_RTP, "CANCEL", CAN_NOT_CREATE_DIALOG },
+ { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD },
+ { SIP_PING, NO_RTP, "PING", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }
+};
+
+/*! Define SIP option tags, used in Require: and Supported: headers
+ We need to be aware of these properties in the phones to use
+ the replace: header. We should not do that without knowing
+ that the other end supports it...
+ This is nothing we can configure, we learn by the dialog
+ Supported: header on the REGISTER (peer) or the INVITE
+ (other devices)
+ We are not using many of these today, but will in the future.
+ This is documented in RFC 3261
+*/
+#define SUPPORTED 1
+#define NOT_SUPPORTED 0
+
+/* SIP options */
+#define SIP_OPT_REPLACES (1 << 0)
+#define SIP_OPT_100REL (1 << 1)
+#define SIP_OPT_TIMER (1 << 2)
+#define SIP_OPT_EARLY_SESSION (1 << 3)
+#define SIP_OPT_JOIN (1 << 4)
+#define SIP_OPT_PATH (1 << 5)
+#define SIP_OPT_PREF (1 << 6)
+#define SIP_OPT_PRECONDITION (1 << 7)
+#define SIP_OPT_PRIVACY (1 << 8)
+#define SIP_OPT_SDP_ANAT (1 << 9)
+#define SIP_OPT_SEC_AGREE (1 << 10)
+#define SIP_OPT_EVENTLIST (1 << 11)
+#define SIP_OPT_GRUU (1 << 12)
+#define SIP_OPT_TARGET_DIALOG (1 << 13)
+#define SIP_OPT_NOREFERSUB (1 << 14)
+#define SIP_OPT_HISTINFO (1 << 15)
+#define SIP_OPT_RESPRIORITY (1 << 16)
+#define SIP_OPT_UNKNOWN (1 << 17)
+
+
+/*! \brief List of well-known SIP options. If we get this in a require,
+ we should check the list and answer accordingly. */
+static const struct cfsip_options {
+ int id; /*!< Bitmap ID */
+ int supported; /*!< Supported by Asterisk ? */
+ char * const text; /*!< Text id, as in standard */
+} sip_options[] = { /* XXX used in 3 places */
+ /* RFC3891: Replaces: header for transfer */
+ { SIP_OPT_REPLACES, SUPPORTED, "replaces" },
+ /* One version of Polycom firmware has the wrong label */
+ { SIP_OPT_REPLACES, SUPPORTED, "replace" },
+ /* RFC3262: PRACK 100% reliability */
+ { SIP_OPT_100REL, NOT_SUPPORTED, "100rel" },
+ /* RFC4028: SIP Session-Timers */
+ { SIP_OPT_TIMER, SUPPORTED, "timer" },
+ /* RFC3959: SIP Early session support */
+ { SIP_OPT_EARLY_SESSION, NOT_SUPPORTED, "early-session" },
+ /* RFC3911: SIP Join header support */
+ { SIP_OPT_JOIN, NOT_SUPPORTED, "join" },
+ /* RFC3327: Path support */
+ { SIP_OPT_PATH, NOT_SUPPORTED, "path" },
+ /* RFC3840: Callee preferences */
+ { SIP_OPT_PREF, NOT_SUPPORTED, "pref" },
+ /* RFC3312: Precondition support */
+ { SIP_OPT_PRECONDITION, NOT_SUPPORTED, "precondition" },
+ /* RFC3323: Privacy with proxies*/
+ { SIP_OPT_PRIVACY, NOT_SUPPORTED, "privacy" },
+ /* RFC4092: Usage of the SDP ANAT Semantics in the SIP */
+ { SIP_OPT_SDP_ANAT, NOT_SUPPORTED, "sdp-anat" },
+ /* RFC3329: Security agreement mechanism */
+ { SIP_OPT_SEC_AGREE, NOT_SUPPORTED, "sec_agree" },
+ /* SIMPLE events: RFC4662 */
+ { SIP_OPT_EVENTLIST, NOT_SUPPORTED, "eventlist" },
+ /* GRUU: Globally Routable User Agent URI's */
+ { SIP_OPT_GRUU, NOT_SUPPORTED, "gruu" },
+ /* RFC4538: Target-dialog */
+ { SIP_OPT_TARGET_DIALOG,NOT_SUPPORTED, "tdialog" },
+ /* Disable the REFER subscription, RFC 4488 */
+ { SIP_OPT_NOREFERSUB, NOT_SUPPORTED, "norefersub" },
+ /* ietf-sip-history-info-06.txt */
+ { SIP_OPT_HISTINFO, NOT_SUPPORTED, "histinfo" },
+ /* ietf-sip-resource-priority-10.txt */
+ { SIP_OPT_RESPRIORITY, NOT_SUPPORTED, "resource-priority" },
+};
+
+
+/*! \brief SIP Methods we support
+ \todo This string should be set dynamically. We only support REFER and SUBSCRIBE is we have
+ allowsubscribe and allowrefer on in sip.conf.
+*/
+#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY"
+
+/*! \brief SIP Extensions we support */
+#define SUPPORTED_EXTENSIONS "replaces, timer"
+
+/*! \brief Standard SIP and TLS port from RFC 3261. DO NOT CHANGE THIS */
+#define STANDARD_SIP_PORT 5060
+#define STANDARD_TLS_PORT 5061
+/* Note: in many SIP headers, absence of a port number implies port 5060,
+ * and this is why we cannot change the above constant.
+ * There is a limited number of places in asterisk where we could,
+ * in principle, use a different "default" port number, but
+ * we do not support this feature at the moment.
+ * You can run Asterisk with SIP on a different port with a configuration
+ * option. If you change this value, the signalling will be incorrect.
+ */
+
+/*! \name DefaultValues Default values, set and reset in reload_config before reading configuration
+
+ These are default values in the source. There are other recommended values in the
+ sip.conf.sample for new installations. These may differ to keep backwards compatibility,
+ yet encouraging new behaviour on new installations
+ */
+/*@{*/
+#define DEFAULT_CONTEXT "default"
+#define DEFAULT_MOHINTERPRET "default"
+#define DEFAULT_MOHSUGGEST ""
+#define DEFAULT_VMEXTEN "asterisk"
+#define DEFAULT_CALLERID "asterisk"
+#define DEFAULT_NOTIFYMIME "application/simple-message-summary"
+#define DEFAULT_ALLOWGUEST TRUE
+#define DEFAULT_CALLCOUNTER FALSE
+#define DEFAULT_SRVLOOKUP TRUE /*!< Recommended setting is ON */
+#define DEFAULT_COMPACTHEADERS FALSE
+#define DEFAULT_TOS_SIP 0 /*!< Call signalling packets should be marked as DSCP CS3, but the default is 0 to be compatible with previous versions. */
+#define DEFAULT_TOS_AUDIO 0 /*!< Audio packets should be marked as DSCP EF (Expedited Forwarding), but the default is 0 to be compatible with previous versions. */
+#define DEFAULT_TOS_VIDEO 0 /*!< Video packets should be marked as DSCP AF41, but the default is 0 to be compatible with previous versions. */
+#define DEFAULT_TOS_TEXT 0 /*!< Text packets should be marked as XXXX XXXX, but the default is 0 to be compatible with previous versions. */
+#define DEFAULT_COS_SIP 4
+#define DEFAULT_COS_AUDIO 5
+#define DEFAULT_COS_VIDEO 6
+#define DEFAULT_COS_TEXT 5
+#define DEFAULT_ALLOW_EXT_DOM TRUE
+#define DEFAULT_REALM "asterisk"
+#define DEFAULT_NOTIFYRINGING TRUE
+#define DEFAULT_PEDANTIC FALSE
+#define DEFAULT_AUTOCREATEPEER FALSE
+#define DEFAULT_QUALIFY FALSE
+#define DEFAULT_REGEXTENONQUALIFY FALSE
+#define DEFAULT_T1MIN 100 /*!< 100 MS for minimal roundtrip time */
+#define DEFAULT_MAX_CALL_BITRATE (384) /*!< Max bitrate for video */
+#ifndef DEFAULT_USERAGENT
+#define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */
+#define DEFAULT_SDPSESSION "Asterisk PBX" /*!< Default SDP session name, (s=) header unless re-defined in sip.conf */
+#define DEFAULT_SDPOWNER "root" /*!< Default SDP username field in (o=) header unless re-defined in sip.conf */
+#endif
+/*@}*/
+
+/*! \name DefaultSettings
+ Default setttings are used as a channel setting and as a default when
+ configuring devices
+*/
+/*@{*/
+static char default_context[AST_MAX_CONTEXT];
+static char default_subscribecontext[AST_MAX_CONTEXT];
+static char default_language[MAX_LANGUAGE];
+static char default_callerid[AST_MAX_EXTENSION];
+static char default_fromdomain[AST_MAX_EXTENSION];
+static char default_notifymime[AST_MAX_EXTENSION];
+static int default_qualify; /*!< Default Qualify= setting */
+static char default_vmexten[AST_MAX_EXTENSION];
+static char default_mohinterpret[MAX_MUSICCLASS]; /*!< Global setting for moh class to use when put on hold */
+static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting
+ * a bridged channel on hold */
+static int default_maxcallbitrate; /*!< Maximum bitrate for call */
+static struct ast_codec_pref default_prefs; /*!< Default codec prefs */
+
+/*! \brief a place to store all global settings for the sip channel driver */
+struct sip_settings {
+ int peer_rtupdate; /*!< G: Update database with registration data for peer? */
+ int rtsave_sysname; /*!< G: Save system name at registration? */
+ int ignore_regexpire; /*!< G: Ignore expiration of peer */
+};
+
+static struct sip_settings sip_cfg;
+/*@}*/
+
+/*! \name GlobalSettings
+ Global settings apply to the channel (often settings you can change in the general section
+ of sip.conf
+*/
+/*@{*/
+static int global_directrtpsetup; /*!< Enable support for Direct RTP setup (no re-invites) */
+static int global_limitonpeers; /*!< Match call limit on peers only */
+static int global_rtautoclear; /*!< Realtime ?? */
+static int global_notifyringing; /*!< Send notifications on ringing */
+static int global_notifyhold; /*!< Send notifications on hold */
+static int global_alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */
+static int global_srvlookup; /*!< SRV Lookup on or off. Default is on */
+static int pedanticsipchecking; /*!< Extra checking ? Default off */
+static int autocreatepeer; /*!< Auto creation of peers at registration? Default off. */
+static int global_match_auth_username; /*!< Match auth username if available instead of From: Default off. */
+static int global_relaxdtmf; /*!< Relax DTMF */
+static int global_rtptimeout; /*!< Time out call if no RTP */
+static int global_rtpholdtimeout; /*!< Time out call if no RTP during hold */
+static int global_rtpkeepalive; /*!< Send RTP keepalives */
+static int global_reg_timeout;
+static int global_regattempts_max; /*!< Registration attempts before giving up */
+static int global_allowguest; /*!< allow unauthenticated users/peers to connect? */
+static int global_callcounter; /*!< Enable call counters for all devices. This is currently enabled by setting the peer
+ call-limit to 999. When we remove the call-limit from the code, we can make it
+ with just a boolean flag in the device structure */
+static int global_allowsubscribe; /*!< Flag for disabling ALL subscriptions, this is FALSE only if all peers are FALSE
+ the global setting is in globals_flags[1] */
+static unsigned int global_tos_sip; /*!< IP type of service for SIP packets */
+static unsigned int global_tos_audio; /*!< IP type of service for audio RTP packets */
+static unsigned int global_tos_video; /*!< IP type of service for video RTP packets */
+static unsigned int global_tos_text; /*!< IP type of service for text RTP packets */
+static unsigned int global_cos_sip; /*!< 802.1p class of service for SIP packets */
+static unsigned int global_cos_audio; /*!< 802.1p class of service for audio RTP packets */
+static unsigned int global_cos_video; /*!< 802.1p class of service for video RTP packets */
+static unsigned int global_cos_text; /*!< 802.1p class of service for text RTP packets */
+static int compactheaders; /*!< send compact sip headers */
+static int recordhistory; /*!< Record SIP history. Off by default */
+static int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */
+static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
+static char global_regcontext[AST_MAX_CONTEXT]; /*!< Context for auto-extensions */
+static char global_useragent[AST_MAX_EXTENSION]; /*!< Useragent for the SIP channel */
+static char global_sdpsession[AST_MAX_EXTENSION]; /*!< SDP session name for the SIP channel */
+static char global_sdpowner[AST_MAX_EXTENSION]; /*!< SDP owner name for the SIP channel */
+static int allow_external_domains; /*!< Accept calls to external SIP domains? */
+static int global_callevents; /*!< Whether we send manager events or not */
+static int global_t1; /*!< T1 time */
+static int global_t1min; /*!< T1 roundtrip time minimum */
+static int global_timer_b; /*!< Timer B - RFC 3261 Section 17.1.1.2 */
+static int global_regextenonqualify; /*!< Whether to add/remove regexten when qualifying peers */
+static int global_autoframing; /*!< Turn autoframing on or off. */
+static enum transfermodes global_allowtransfer; /*!< SIP Refer restriction scheme */
+static struct sip_proxy global_outboundproxy; /*!< Outbound proxy */
+static int global_matchexterniplocally; /*!< Match externip/externhost setting against localnet setting */
+static int global_qualifyfreq; /*!< Qualify frequency */
+
+
+/*! \brief Codecs that we support by default: */
+static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263;
+/*@}*/
+
+/* Object counters */
+static int suserobjs = 0; /*!< Static users */
+static int ruserobjs = 0; /*!< Realtime users */
+static int speerobjs = 0; /*!< Statis peers */
+static int rpeerobjs = 0; /*!< Realtime peers */
+static int apeerobjs = 0; /*!< Autocreated peer objects */
+static int regobjs = 0; /*!< Registry objects */
+
+static struct ast_flags global_flags[2] = {{0}}; /*!< global SIP_ flags */
+static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */
+
+static enum st_mode global_st_mode; /*!< Mode of operation for Session-Timers */
+static enum st_refresher global_st_refresher; /*!< Session-Timer refresher */
+static int global_min_se; /*!< Lowest threshold for session refresh interval */
+static int global_max_se; /*!< Highest threshold for session refresh interval */
+
+
+AST_MUTEX_DEFINE_STATIC(netlock);
+
+/*! \brief Protect the monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+AST_MUTEX_DEFINE_STATIC(sip_reload_lock);
+
+/*! \brief This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+
+static int sip_reloading = FALSE; /*!< Flag for avoiding multiple reloads at the same time */
+static enum channelreloadreason sip_reloadreason; /*!< Reason for last reload/load of configuration */
+
+static struct sched_context *sched; /*!< The scheduling context */
+static struct io_context *io; /*!< The IO context */
+static int *sipsock_read_id; /*!< ID of IO entry for sipsock FD */
+
+#define DEC_CALL_LIMIT 0
+#define INC_CALL_LIMIT 1
+#define DEC_CALL_RINGING 2
+#define INC_CALL_RINGING 3
+
+/*!< Define some SIP transports */
+enum sip_transport {
+ SIP_TRANSPORT_UDP = 1,
+ SIP_TRANSPORT_TCP = 1 << 1,
+ SIP_TRANSPORT_TLS = 1 << 2,
+};
+
+struct sip_socket {
+ ast_mutex_t *lock;
+ enum sip_transport type;
+ int fd;
+ uint16_t port;
+ struct server_instance *ser;
+};
+
+/*! \brief sip_request: The data grabbed from the UDP socket
+ *
+ * \verbatim
+ * Incoming messages: we first store the data from the socket in data[],
+ * adding a trailing \0 to make string parsing routines happy.
+ * Then call parse_request() and req.method = find_sip_method();
+ * to initialize the other fields. The \r\n at the end of each line is
+ * replaced by \0, so that data[] is not a conforming SIP message anymore.
+ * After this processing, rlPart1 is set to non-NULL to remember
+ * that we can run get_header() on this kind of packet.
+ *
+ * parse_request() splits the first line as follows:
+ * Requests have in the first line method uri SIP/2.0
+ * rlPart1 = method; rlPart2 = uri;
+ * Responses have in the first line SIP/2.0 NNN description
+ * rlPart1 = SIP/2.0; rlPart2 = NNN + description;
+ *
+ * For outgoing packets, we initialize the fields with init_req() or init_resp()
+ * (which fills the first line to "METHOD uri SIP/2.0" or "SIP/2.0 code text"),
+ * and then fill the rest with add_header() and add_line().
+ * The \r\n at the end of the line are still there, so the get_header()
+ * and similar functions don't work on these packets.
+ * \endverbatim
+ */
+struct sip_request {
+ char *rlPart1; /*!< SIP Method Name or "SIP/2.0" protocol version */
+ char *rlPart2; /*!< The Request URI or Response Status */
+ int len; /*!< bytes used in data[], excluding trailing null terminator. Rarely used. */
+ int headers; /*!< # of SIP Headers */
+ int method; /*!< Method of this request */
+ int lines; /*!< Body Content */
+ unsigned int sdp_start; /*!< the line number where the SDP begins */
+ unsigned int sdp_end; /*!< the line number where the SDP ends */
+ char debug; /*!< print extra debugging if non zero */
+ char has_to_tag; /*!< non-zero if packet has To: tag */
+ char ignore; /*!< if non-zero This is a re-transmit, ignore it */
+ char *header[SIP_MAX_HEADERS];
+ char *line[SIP_MAX_LINES];
+ char data[SIP_MAX_PACKET];
+ struct sip_socket socket;
+};
+
+/*! \brief structure used in transfers */
+struct sip_dual {
+ struct ast_channel *chan1; /*!< First channel involved */
+ struct ast_channel *chan2; /*!< Second channel involved */
+ struct sip_request req; /*!< Request that caused the transfer (REFER) */
+ int seqno; /*!< Sequence number */
+};
+
+struct sip_pkt;
+
+/*! \brief Parameters to the transmit_invite function */
+struct sip_invite_param {
+ int addsipheaders; /*!< Add extra SIP headers */
+ const char *uri_options; /*!< URI options to add to the URI */
+ const char *vxml_url; /*!< VXML url for Cisco phones */
+ char *auth; /*!< Authentication */
+ char *authheader; /*!< Auth header */
+ enum sip_auth_type auth_type; /*!< Authentication type */
+ const char *replaces; /*!< Replaces header for call transfers */
+ int transfer; /*!< Flag - is this Invite part of a SIP transfer? (invite/replaces) */
+};
+
+/*! \brief Structure to save routing information for a SIP session */
+struct sip_route {
+ struct sip_route *next;
+ char hop[0];
+};
+
+/*! \brief Modes for SIP domain handling in the PBX */
+enum domain_mode {
+ SIP_DOMAIN_AUTO, /*!< This domain is auto-configured */
+ SIP_DOMAIN_CONFIG, /*!< This domain is from configuration */
+};
+
+/*! \brief Domain data structure.
+ \note In the future, we will connect this to a configuration tree specific
+ for this domain
+*/
+struct domain {
+ char domain[MAXHOSTNAMELEN]; /*!< SIP domain we are responsible for */
+ char context[AST_MAX_EXTENSION]; /*!< Incoming context for this domain */
+ enum domain_mode mode; /*!< How did we find this domain? */
+ AST_LIST_ENTRY(domain) list; /*!< List mechanics */
+};
+
+static AST_LIST_HEAD_STATIC(domain_list, domain); /*!< The SIP domain list */
+
+
+/*! \brief sip_history: Structure for saving transactions within a SIP dialog */
+struct sip_history {
+ AST_LIST_ENTRY(sip_history) list;
+ char event[0]; /* actually more, depending on needs */
+};
+
+AST_LIST_HEAD_NOLOCK(sip_history_head, sip_history); /*!< history list, entry in sip_pvt */
+
+/*! \brief sip_auth: Credentials for authentication to other SIP services */
+struct sip_auth {
+ char realm[AST_MAX_EXTENSION]; /*!< Realm in which these credentials are valid */
+ char username[256]; /*!< Username */
+ char secret[256]; /*!< Secret */
+ char md5secret[256]; /*!< MD5Secret */
+ struct sip_auth *next; /*!< Next auth structure in list */
+};
+
+/*! \name SIPflags
+ Various flags for the flags field in the pvt structure
+ Trying to sort these up (one or more of the following):
+ D: Dialog
+ P: Peer/user
+ G: Global flag
+ When flags are used by multiple structures, it is important that
+ they have a common layout so it is easy to copy them.
+*/
+/*@{*/
+#define SIP_OUTGOING (1 << 0) /*!< D: Direction of the last transaction in this dialog */
+#define SIP_RINGING (1 << 2) /*!< D: Have sent 180 ringing */
+#define SIP_PROGRESS_SENT (1 << 3) /*!< D: Have sent 183 message progress */
+#define SIP_NEEDREINVITE (1 << 4) /*!< D: Do we need to send another reinvite? */
+#define SIP_PENDINGBYE (1 << 5) /*!< D: Need to send bye after we ack? */
+#define SIP_GOTREFER (1 << 6) /*!< D: Got a refer? */
+#define SIP_CALL_LIMIT (1 << 7) /*!< D: Call limit enforced for this call */
+#define SIP_INC_COUNT (1 << 8) /*!< D: Did this dialog increment the counter of in-use calls? */
+#define SIP_INC_RINGING (1 << 9) /*!< D: Did this connection increment the counter of in-use calls? */
+#define SIP_DEFER_BYE_ON_TRANSFER (1 << 11) /*!< D: Do not hangup at first ast_hangup */
+
+#define SIP_PROMISCREDIR (1 << 12) /*!< DP: Promiscuous redirection */
+#define SIP_TRUSTRPID (1 << 13) /*!< DP: Trust RPID headers? */
+#define SIP_USEREQPHONE (1 << 14) /*!< DP: Add user=phone to numeric URI. Default off */
+#define SIP_USECLIENTCODE (1 << 15) /*!< DP: Trust X-ClientCode info message */
+
+/* DTMF flags - see str2dtmfmode() and dtmfmode2str() */
+#define SIP_DTMF (3 << 16) /*!< DP: DTMF Support: four settings, uses two bits */
+#define SIP_DTMF_RFC2833 (0 << 16) /*!< DP: DTMF Support: RTP DTMF - "rfc2833" */
+#define SIP_DTMF_INBAND (1 << 16) /*!< DP: DTMF Support: Inband audio, only for ULAW/ALAW - "inband" */
+#define SIP_DTMF_INFO (2 << 16) /*!< DP: DTMF Support: SIP Info messages - "info" */
+#define SIP_DTMF_AUTO (3 << 16) /*!< DP: DTMF Support: AUTO switch between rfc2833 and in-band DTMF */
+#define SIP_DTMF_SHORTINFO (4 << 16) /*!< DP: DTMF Support: SIP Info messages - "info" - short variant */
+
+/* NAT settings - see nat2str() */
+#define SIP_NAT (3 << 18) /*!< DP: four settings, uses two bits */
+#define SIP_NAT_NEVER (0 << 18) /*!< DP: No nat support */
+#define SIP_NAT_RFC3581 (1 << 18) /*!< DP: NAT RFC3581 */
+#define SIP_NAT_ROUTE (2 << 18) /*!< DP: NAT Only ROUTE */
+#define SIP_NAT_ALWAYS (3 << 18) /*!< DP: NAT Both ROUTE and RFC3581 */
+
+/* re-INVITE related settings */
+#define SIP_REINVITE (7 << 20) /*!< DP: three bits used */
+#define SIP_CAN_REINVITE (1 << 20) /*!< DP: allow peers to be reinvited to send media directly p2p */
+#define SIP_CAN_REINVITE_NAT (2 << 20) /*!< DP: allow media reinvite when new peer is behind NAT */
+#define SIP_REINVITE_UPDATE (4 << 20) /*!< DP: use UPDATE (RFC3311) when reinviting this peer */
+
+/* "insecure" settings - see insecure2str() */
+#define SIP_INSECURE (3 << 23) /*!< DP: two bits used */
+#define SIP_INSECURE_PORT (1 << 23) /*!< DP: don't require matching port for incoming requests */
+#define SIP_INSECURE_INVITE (1 << 24) /*!< DP: don't require authentication for incoming INVITEs */
+
+/* Sending PROGRESS in-band settings */
+#define SIP_PROG_INBAND (3 << 25) /*!< DP: three settings, uses two bits */
+#define SIP_PROG_INBAND_NEVER (0 << 25)
+#define SIP_PROG_INBAND_NO (1 << 25)
+#define SIP_PROG_INBAND_YES (2 << 25)
+
+#define SIP_SENDRPID (1 << 29) /*!< DP: Remote Party-ID Support */
+#define SIP_G726_NONSTANDARD (1 << 31) /*!< DP: Use non-standard packing for G726-32 data */
+
+/*! \brief Flags to copy from peer/user to dialog */
+#define SIP_FLAGS_TO_COPY \
+ (SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_SENDRPID | SIP_DTMF | SIP_REINVITE | \
+ SIP_PROG_INBAND | SIP_USECLIENTCODE | SIP_NAT | SIP_G726_NONSTANDARD | \
+ SIP_USEREQPHONE | SIP_INSECURE)
+/*@}*/
+
+/*! \name SIPflags2
+ a second page of flags (for flags[1] */
+/*@{*/
+/* realtime flags */
+#define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) /*!< GP: Should we keep RT objects in memory for extended time? */
+#define SIP_PAGE2_RTAUTOCLEAR (1 << 2) /*!< GP: Should we clean memory from peers after expiry? */
+/* Space for addition of other realtime flags in the future */
+
+#define SIP_PAGE2_VIDEOSUPPORT (1 << 14) /*!< DP: Video supported if offered? */
+#define SIP_PAGE2_TEXTSUPPORT (1 << 15) /*!< GDP: Global text enable */
+#define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 16) /*!< GP: Allow subscriptions from this peer? */
+#define SIP_PAGE2_ALLOWOVERLAP (1 << 17) /*!< DP: Allow overlap dialing ? */
+#define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 18) /*!< GP: Only issue MWI notification if subscribed to */
+
+#define SIP_PAGE2_T38SUPPORT (7 << 20) /*!< GDP: T38 Fax Passthrough Support */
+#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T38 Fax Passthrough Support */
+#define SIP_PAGE2_T38SUPPORT_RTP (2 << 20) /*!< GDP: T38 Fax Passthrough Support (not implemented) */
+#define SIP_PAGE2_T38SUPPORT_TCP (4 << 20) /*!< GDP: T38 Fax Passthrough Support (not implemented) */
+
+#define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */
+#define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */
+#define SIP_PAGE2_CALL_ONHOLD_ONEDIR (2 << 23) /*!< D: One directional hold */
+#define SIP_PAGE2_CALL_ONHOLD_INACTIVE (3 << 23) /*!< D: Inactive hold */
+
+#define SIP_PAGE2_RFC2833_COMPENSATE (1 << 25) /*!< DP: Compensate for buggy RFC2833 implementations */
+#define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< DP: Buggy CISCO MWI fix */
+#define SIP_PAGE2_REGISTERTRYING (1 << 29) /*!< DP: Send 100 Trying on REGISTER attempts */
+
+#define SIP_PAGE2_FLAGS_TO_COPY \
+ (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | \
+ SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | SIP_PAGE2_BUGGY_MWI | \
+ SIP_PAGE2_TEXTSUPPORT )
+
+/*@}*/
+
+/*! \name SIPflagsT38
+ T.38 set of flags */
+
+/*@{*/
+#define T38FAX_FILL_BIT_REMOVAL (1 << 0) /*!< Default: 0 (unset)*/
+#define T38FAX_TRANSCODING_MMR (1 << 1) /*!< Default: 0 (unset)*/
+#define T38FAX_TRANSCODING_JBIG (1 << 2) /*!< Default: 0 (unset)*/
+/* Rate management */
+#define T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF (0 << 3)
+#define T38FAX_RATE_MANAGEMENT_LOCAL_TCF (1 << 3) /*!< Unset for transferredTCF (UDPTL), set for localTCF (TPKT) */
+/* UDP Error correction */
+#define T38FAX_UDP_EC_NONE (0 << 4) /*!< two bits, if unset NO t38UDPEC field in T38 SDP*/
+#define T38FAX_UDP_EC_FEC (1 << 4) /*!< Set for t38UDPFEC */
+#define T38FAX_UDP_EC_REDUNDANCY (2 << 4) /*!< Set for t38UDPRedundancy */
+/* T38 Spec version */
+#define T38FAX_VERSION (3 << 6) /*!< two bits, 2 values so far, up to 4 values max */
+#define T38FAX_VERSION_0 (0 << 6) /*!< Version 0 */
+#define T38FAX_VERSION_1 (1 << 6) /*!< Version 1 */
+/* Maximum Fax Rate */
+#define T38FAX_RATE_2400 (1 << 8) /*!< 2400 bps t38FaxRate */
+#define T38FAX_RATE_4800 (1 << 9) /*!< 4800 bps t38FaxRate */
+#define T38FAX_RATE_7200 (1 << 10) /*!< 7200 bps t38FaxRate */
+#define T38FAX_RATE_9600 (1 << 11) /*!< 9600 bps t38FaxRate */
+#define T38FAX_RATE_12000 (1 << 12) /*!< 12000 bps t38FaxRate */
+#define T38FAX_RATE_14400 (1 << 13) /*!< 14400 bps t38FaxRate */
+
+/*!< This is default: NO MMR and JBIG transcoding, NO fill bit removal, transferredTCF TCF, UDP FEC, Version 0 and 9600 max fax rate */
+static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600;
+/*@}*/
+
+/*! \brief debugging state
+ * We store separately the debugging requests from the config file
+ * and requests from the CLI. Debugging is enabled if either is set
+ * (which means that if sipdebug is set in the config file, we can
+ * only turn it off by reloading the config).
+ */
+enum sip_debug_e {
+ sip_debug_none = 0,
+ sip_debug_config = 1,
+ sip_debug_console = 2,
+};
+
+static enum sip_debug_e sipdebug;
+
+/*! \brief extra debugging for 'text' related events.
+ * At thie moment this is set together with sip_debug_console.
+ * It should either go away or be implemented properly.
+ */
+static int sipdebug_text;
+
+/*! \brief T38 States for a call */
+enum t38state {
+ T38_DISABLED = 0, /*!< Not enabled */
+ T38_LOCAL_DIRECT, /*!< Offered from local */
+ T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */
+ T38_PEER_DIRECT, /*!< Offered from peer */
+ T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */
+ T38_ENABLED /*!< Negotiated (enabled) */
+};
+
+/*! \brief T.38 channel settings (at some point we need to make this alloc'ed */
+struct t38properties {
+ struct ast_flags t38support; /*!< Flag for udptl, rtp or tcp support for this session */
+ int capability; /*!< Our T38 capability */
+ int peercapability; /*!< Peers T38 capability */
+ int jointcapability; /*!< Supported T38 capability at both ends */
+ enum t38state state; /*!< T.38 state */
+};
+
+/*! \brief Parameters to know status of transfer */
+enum referstatus {
+ REFER_IDLE, /*!< No REFER is in progress */
+ REFER_SENT, /*!< Sent REFER to transferee */
+ REFER_RECEIVED, /*!< Received REFER from transferrer */
+ REFER_CONFIRMED, /*!< Refer confirmed with a 100 TRYING (unused) */
+ REFER_ACCEPTED, /*!< Accepted by transferee */
+ REFER_RINGING, /*!< Target Ringing */
+ REFER_200OK, /*!< Answered by transfer target */
+ REFER_FAILED, /*!< REFER declined - go on */
+ REFER_NOAUTH /*!< We had no auth for REFER */
+};
+
+/*! \brief generic struct to map between strings and integers.
+ * Fill it with x-s pairs, terminate with an entry with s = NULL;
+ * Then you can call map_x_s(...) to map an integer to a string,
+ * and map_s_x() for the string -> integer mapping.
+ */
+struct _map_x_s {
+ int x;
+ const char *s;
+};
+
+static const struct _map_x_s referstatusstrings[] = {
+ { REFER_IDLE, "<none>" },
+ { REFER_SENT, "Request sent" },
+ { REFER_RECEIVED, "Request received" },
+ { REFER_CONFIRMED, "Confirmed" },
+ { REFER_ACCEPTED, "Accepted" },
+ { REFER_RINGING, "Target ringing" },
+ { REFER_200OK, "Done" },
+ { REFER_FAILED, "Failed" },
+ { REFER_NOAUTH, "Failed - auth failure" },
+ { -1, NULL} /* terminator */
+};
+
+/*! \brief Structure to handle SIP transfers. Dynamically allocated when needed
+ \note OEJ: Should be moved to string fields */
+struct sip_refer {
+ char refer_to[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO extension */
+ char refer_to_domain[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO domain */
+ char refer_to_urioption[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO uri options */
+ char refer_to_context[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO context */
+ char referred_by[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */
+ char referred_by_name[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */
+ char refer_contact[AST_MAX_EXTENSION]; /*!< Place to store Contact info from a REFER extension */
+ char replaces_callid[BUFSIZ]; /*!< Replace info: callid */
+ char replaces_callid_totag[BUFSIZ/2]; /*!< Replace info: to-tag */
+ char replaces_callid_fromtag[BUFSIZ/2]; /*!< Replace info: from-tag */
+ struct sip_pvt *refer_call; /*!< Call we are referring. This is just a reference to a
+ * dialog owned by someone else, so we should not destroy
+ * it when the sip_refer object goes.
+ */
+ int attendedtransfer; /*!< Attended or blind transfer? */
+ int localtransfer; /*!< Transfer to local domain? */
+ enum referstatus status; /*!< REFER status */
+};
+
+
+/*! \brief Structure that encapsulates all attributes related to running
+ * SIP Session-Timers feature on a per dialog basis.
+ */
+struct sip_st_dlg {
+ int st_active; /*!< Session-Timers on/off */
+ int st_interval; /*!< Session-Timers negotiated session refresh interval */
+ int st_schedid; /*!< Session-Timers ast_sched scheduler id */
+ enum st_refresher st_ref; /*!< Session-Timers session refresher */
+ int st_expirys; /*!< Session-Timers number of expirys */
+ int st_active_peer_ua; /*!< Session-Timers on/off in peer UA */
+ int st_cached_min_se; /*!< Session-Timers cached Min-SE */
+ int st_cached_max_se; /*!< Session-Timers cached Session-Expires */
+ enum st_mode st_cached_mode; /*!< Session-Timers cached M.O. */
+ enum st_refresher st_cached_ref; /*!< Session-Timers cached refresher */
+};
+
+
+/*! \brief Structure that encapsulates all attributes related to configuration
+ * of SIP Session-Timers feature on a per user/peer basis.
+ */
+struct sip_st_cfg {
+ enum st_mode st_mode_oper; /*!< Mode of operation for Session-Timers */
+ enum st_refresher st_ref; /*!< Session-Timer refresher */
+ int st_min_se; /*!< Lowest threshold for session refresh interval */
+ int st_max_se; /*!< Highest threshold for session refresh interval */
+};
+
+
+
+
+/*! \brief sip_pvt: structures used for each SIP dialog, ie. a call, a registration, a subscribe.
+ * Created and initialized by sip_alloc(), the descriptor goes into the list of
+ * descriptors (dialoglist).
+ */
+struct sip_pvt {
+ struct sip_pvt *next; /*!< Next dialog in chain */
+ ast_mutex_t pvt_lock; /*!< Dialog private lock */
+ enum invitestates invitestate; /*!< Track state of SIP_INVITEs */
+ int method; /*!< SIP method that opened this dialog */
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(callid); /*!< Global CallID */
+ AST_STRING_FIELD(randdata); /*!< Random data */
+ AST_STRING_FIELD(accountcode); /*!< Account code */
+ AST_STRING_FIELD(realm); /*!< Authorization realm */
+ AST_STRING_FIELD(nonce); /*!< Authorization nonce */
+ AST_STRING_FIELD(opaque); /*!< Opaque nonsense */
+ AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */
+ AST_STRING_FIELD(domain); /*!< Authorization domain */
+ AST_STRING_FIELD(from); /*!< The From: header */
+ AST_STRING_FIELD(useragent); /*!< User agent in SIP request */
+ AST_STRING_FIELD(exten); /*!< Extension where to start */
+ AST_STRING_FIELD(context); /*!< Context for this call */
+ AST_STRING_FIELD(subscribecontext); /*!< Subscribecontext */
+ AST_STRING_FIELD(subscribeuri); /*!< Subscribecontext */
+ AST_STRING_FIELD(fromdomain); /*!< Domain to show in the from field */
+ AST_STRING_FIELD(fromuser); /*!< User to show in the user field */
+ AST_STRING_FIELD(fromname); /*!< Name to show in the user field */
+ AST_STRING_FIELD(tohost); /*!< Host we should put in the "to" field */
+ AST_STRING_FIELD(todnid); /*!< DNID of this call (overrides host) */
+ AST_STRING_FIELD(language); /*!< Default language for this call */
+ AST_STRING_FIELD(mohinterpret); /*!< MOH class to use when put on hold */
+ AST_STRING_FIELD(mohsuggest); /*!< MOH class to suggest when putting a peer on hold */
+ AST_STRING_FIELD(rdnis); /*!< Referring DNIS */
+ AST_STRING_FIELD(redircause); /*!< Referring cause */
+ AST_STRING_FIELD(theirtag); /*!< Their tag */
+ AST_STRING_FIELD(username); /*!< [user] name */
+ AST_STRING_FIELD(peername); /*!< [peer] name, not set if [user] */
+ AST_STRING_FIELD(authname); /*!< Who we use for authentication */
+ AST_STRING_FIELD(uri); /*!< Original requested URI */
+ AST_STRING_FIELD(okcontacturi); /*!< URI from the 200 OK on INVITE */
+ AST_STRING_FIELD(peersecret); /*!< Password */
+ AST_STRING_FIELD(peermd5secret);
+ AST_STRING_FIELD(cid_num); /*!< Caller*ID number */
+ AST_STRING_FIELD(cid_name); /*!< Caller*ID name */
+ AST_STRING_FIELD(via); /*!< Via: header */
+ AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */
+ /* we only store the part in <brackets> in this field. */
+ AST_STRING_FIELD(our_contact); /*!< Our contact header */
+ AST_STRING_FIELD(rpid); /*!< Our RPID header */
+ AST_STRING_FIELD(rpid_from); /*!< Our RPID From header */
+ AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */
+ );
+ struct sip_socket socket;
+ unsigned int ocseq; /*!< Current outgoing seqno */
+ unsigned int icseq; /*!< Current incoming seqno */
+ ast_group_t callgroup; /*!< Call group */
+ ast_group_t pickupgroup; /*!< Pickup group */
+ int lastinvite; /*!< Last Cseq of invite */
+ int lastnoninvite; /*!< Last Cseq of non-invite */
+ struct ast_flags flags[2]; /*!< SIP_ flags */
+
+ /* boolean or small integers that don't belong in flags */
+ char do_history; /*!< Set if we want to record history */
+ char alreadygone; /*!< already destroyed by our peer */
+ char needdestroy; /*!< need to be destroyed by the monitor thread */
+ char outgoing_call; /*!< this is an outgoing call */
+ char answered_elsewhere; /*!< This call is cancelled due to answer on another channel */
+ char novideo; /*!< Didn't get video in invite, don't offer */
+ char notext; /*!< Text not supported (?) */
+
+ int timer_t1; /*!< SIP timer T1, ms rtt */
+ int timer_b; /*!< SIP timer B, ms */
+ unsigned int sipoptions; /*!< Supported SIP options on the other end */
+ unsigned int reqsipoptions; /*!< Required SIP options on the other end */
+ struct ast_codec_pref prefs; /*!< codec prefs */
+ int capability; /*!< Special capability (codec) */
+ int jointcapability; /*!< Supported capability at both ends (codecs) */
+ int peercapability; /*!< Supported peer capability */
+ int prefcodec; /*!< Preferred codec (outbound only) */
+ int noncodeccapability; /*!< DTMF RFC2833 telephony-event */
+ int jointnoncodeccapability; /*!< Joint Non codec capability */
+ int redircodecs; /*!< Redirect codecs */
+ int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */
+ struct sip_proxy *outboundproxy; /*!< Outbound proxy for this dialog */
+ struct t38properties t38; /*!< T38 settings */
+ struct sockaddr_in udptlredirip; /*!< Where our T.38 UDPTL should be going if not to us */
+ struct ast_udptl *udptl; /*!< T.38 UDPTL session */
+ int callingpres; /*!< Calling presentation */
+ int authtries; /*!< Times we've tried to authenticate */
+ int expiry; /*!< How long we take to expire */
+ long branch; /*!< The branch identifier of this session */
+ char tag[11]; /*!< Our tag for this session */
+ int sessionid; /*!< SDP Session ID */
+ int sessionversion; /*!< SDP Session Version */
+ int sessionversion_remote; /*!< Remote UA's SDP Session Version */
+ int session_modify; /*!< Session modification request true/false */
+ struct sockaddr_in sa; /*!< Our peer */
+ struct sockaddr_in redirip; /*!< Where our RTP should be going if not to us */
+ struct sockaddr_in vredirip; /*!< Where our Video RTP should be going if not to us */
+ struct sockaddr_in tredirip; /*!< Where our Text RTP should be going if not to us */
+ time_t lastrtprx; /*!< Last RTP received */
+ time_t lastrtptx; /*!< Last RTP sent */
+ int rtptimeout; /*!< RTP timeout time */
+ struct sockaddr_in recv; /*!< Received as */
+ struct sockaddr_in ourip; /*!< Our IP (as seen from the outside) */
+ struct ast_channel *owner; /*!< Who owns us (if we have an owner) */
+ struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */
+ int route_persistant; /*!< Is this the "real" route? */
+ struct sip_auth *peerauth; /*!< Realm authentication */
+ int noncecount; /*!< Nonce-count */
+ char lastmsg[256]; /*!< Last Message sent/received */
+ int amaflags; /*!< AMA Flags */
+ int pendinginvite; /*!< Any pending invite ? (seqno of this) */
+ struct sip_request initreq; /*!< Latest request that opened a new transaction
+ within this dialog.
+ NOT the request that opened the dialog
+ */
+
+ int initid; /*!< Auto-congest ID if appropriate (scheduler) */
+ int waitid; /*!< Wait ID for scheduler after 491 or other delays */
+ int autokillid; /*!< Auto-kill ID (scheduler) */
+ enum transfermodes allowtransfer; /*!< REFER: restriction scheme */
+ struct sip_refer *refer; /*!< REFER: SIP transfer data structure */
+ enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */
+ int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */
+ int laststate; /*!< SUBSCRIBE: Last known extension state */
+ int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */
+
+ struct ast_dsp *vad; /*!< Inband DTMF Detection dsp */
+
+ struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one
+ Used in peerpoke, mwi subscriptions */
+ struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */
+ struct ast_rtp *rtp; /*!< RTP Session */
+ struct ast_rtp *vrtp; /*!< Video RTP session */
+ struct ast_rtp *trtp; /*!< Text RTP session */
+ struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */
+ struct sip_history_head *history; /*!< History of this SIP dialog */
+ size_t history_entries; /*!< Number of entires in the history */
+ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */
+ struct sip_invite_param *options; /*!< Options for INVITE */
+ int autoframing; /*!< The number of Asters we group in a Pyroflax
+ before strolling to the Grokyzpå
+ (A bit unsure of this, please correct if
+ you know more) */
+ struct sip_st_dlg *stimer; /*!< SIP Session-Timers */
+};
+
+
+/*! Max entires in the history list for a sip_pvt */
+#define MAX_HISTORY_ENTRIES 50
+
+/*!
+ * Here we implement the container for dialogs (sip_pvt), defining
+ * generic wrapper functions to ease the transition from the current
+ * implementation (a single linked list) to a different container.
+ * In addition to a reference to the container, we need functions to lock/unlock
+ * the container and individual items, and functions to add/remove
+ * references to the individual items.
+ */
+static struct sip_pvt *dialoglist = NULL;
+
+/*! \brief Protect the SIP dialog list (of sip_pvt's) */
+AST_MUTEX_DEFINE_STATIC(dialoglock);
+
+#ifndef DETECT_DEADLOCKS
+/*! \brief hide the way the list is locked/unlocked */
+static void dialoglist_lock(void)
+{
+ ast_mutex_lock(&dialoglock);
+}
+
+static void dialoglist_unlock(void)
+{
+ ast_mutex_unlock(&dialoglock);
+}
+#else
+/* we don't want to HIDE the information about where the lock was requested if trying to debug
+ * deadlocks! So, just make these macros! */
+#define dialoglist_lock(x) ast_mutex_lock(&dialoglock)
+#define dialoglist_unlock(x) ast_mutex_unlock(&dialoglock)
+#endif
+
+/*!
+ * when we create or delete references, make sure to use these
+ * functions so we keep track of the refcounts.
+ * To simplify the code, we allow a NULL to be passed to dialog_unref().
+ */
+static struct sip_pvt *dialog_ref(struct sip_pvt *p)
+{
+ return p;
+}
+
+static struct sip_pvt *dialog_unref(struct sip_pvt *p)
+{
+ return NULL;
+}
+
+/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission
+ * Packets are linked in a list, whose head is in the struct sip_pvt they belong to.
+ * Each packet holds a reference to the parent struct sip_pvt.
+ * This structure is allocated in __sip_reliable_xmit() and only for packets that
+ * require retransmissions.
+ */
+struct sip_pkt {
+ struct sip_pkt *next; /*!< Next packet in linked list */
+ int retrans; /*!< Retransmission number */
+ int method; /*!< SIP method for this packet */
+ int seqno; /*!< Sequence number */
+ char is_resp; /*!< 1 if this is a response packet (e.g. 200 OK), 0 if it is a request */
+ char is_fatal; /*!< non-zero if there is a fatal error */
+ struct sip_pvt *owner; /*!< Owner AST call */
+ int retransid; /*!< Retransmission ID */
+ int timer_a; /*!< SIP timer A, retransmission timer */
+ int timer_t1; /*!< SIP Timer T1, estimated RTT or 500 ms */
+ int packetlen; /*!< Length of packet */
+ char data[0];
+};
+
+/*! \brief Structure for SIP user data. User's place calls to us */
+struct sip_user {
+ /* Users who can access various contexts */
+ ASTOBJ_COMPONENTS(struct sip_user);
+ char secret[80]; /*!< Password */
+ char md5secret[80]; /*!< Password in md5 */
+ char context[AST_MAX_CONTEXT]; /*!< Default context for incoming calls */
+ char subscribecontext[AST_MAX_CONTEXT]; /* Default context for subscriptions */
+ char cid_num[80]; /*!< Caller ID num */
+ char cid_name[80]; /*!< Caller ID name */
+ char accountcode[AST_MAX_ACCOUNT_CODE]; /* Account code */
+ char language[MAX_LANGUAGE]; /*!< Default language for this user */
+ char mohinterpret[MAX_MUSICCLASS];/*!< Music on Hold class */
+ char mohsuggest[MAX_MUSICCLASS];/*!< Music on Hold class */
+ char useragent[256]; /*!< User agent in SIP request */
+ struct ast_codec_pref prefs; /*!< codec prefs */
+ ast_group_t callgroup; /*!< Call group */
+ ast_group_t pickupgroup; /*!< Pickup Group */
+ unsigned int sipoptions; /*!< Supported SIP options */
+ struct ast_flags flags[2]; /*!< SIP_ flags */
+
+ /* things that don't belong in flags */
+ char is_realtime; /*!< this is a 'realtime' user */
+
+ int amaflags; /*!< AMA flags for billing */
+ int callingpres; /*!< Calling id presentation */
+ int capability; /*!< Codec capability */
+ int inUse; /*!< Number of calls in use */
+ int call_limit; /*!< Limit of concurrent calls */
+ enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */
+ struct ast_ha *ha; /*!< ACL setting */
+ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */
+ int maxcallbitrate; /*!< Maximum Bitrate for a video call */
+ int autoframing;
+ struct sip_st_cfg stimer; /*!< SIP Session-Timers */
+};
+
+/*!
+ * \brief A peer's mailbox
+ *
+ * We could use STRINGFIELDS here, but for only two strings, it seems like
+ * too much effort ...
+ */
+struct sip_mailbox {
+ char *mailbox;
+ char *context;
+ /*! Associated MWI subscription */
+ struct ast_event_sub *event_sub;
+ AST_LIST_ENTRY(sip_mailbox) entry;
+};
+
+/*! \brief Structure for SIP peer data, we place calls to peers if registered or fixed IP address (host) */
+/* XXX field 'name' must be first otherwise sip_addrcmp() will fail */
+struct sip_peer {
+ ASTOBJ_COMPONENTS(struct sip_peer); /*!< name, refcount, objflags, object pointers */
+ /*!< peer->name is the unique name of this object */
+ struct sip_socket socket;
+ char secret[80]; /*!< Password */
+ char md5secret[80]; /*!< Password in MD5 */
+ struct sip_auth *auth; /*!< Realm authentication list */
+ char context[AST_MAX_CONTEXT]; /*!< Default context for incoming calls */
+ char subscribecontext[AST_MAX_CONTEXT]; /*!< Default context for subscriptions */
+ char username[80]; /*!< Temporary username until registration */
+ char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
+ int amaflags; /*!< AMA Flags (for billing) */
+ char tohost[MAXHOSTNAMELEN]; /*!< If not dynamic, IP address */
+ char regexten[AST_MAX_EXTENSION]; /*!< Extension to register (if regcontext is used) */
+ char fromuser[80]; /*!< From: user when calling this peer */
+ char fromdomain[MAXHOSTNAMELEN]; /*!< From: domain when calling this peer */
+ char fullcontact[256]; /*!< Contact registered with us (not in sip.conf) */
+ char cid_num[80]; /*!< Caller ID num */
+ char cid_name[80]; /*!< Caller ID name */
+ int callingpres; /*!< Calling id presentation */
+ int inUse; /*!< Number of calls in use */
+ int inRinging; /*!< Number of calls ringing */
+ int onHold; /*!< Peer has someone on hold */
+ int call_limit; /*!< Limit of concurrent calls */
+ int busy_level; /*!< Level of active channels where we signal busy */
+ enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */
+ char vmexten[AST_MAX_EXTENSION]; /*!< Dialplan extension for MWI notify message*/
+ char language[MAX_LANGUAGE]; /*!< Default language for prompts */
+ char mohinterpret[MAX_MUSICCLASS];/*!< Music on Hold class */
+ char mohsuggest[MAX_MUSICCLASS];/*!< Music on Hold class */
+ char useragent[256]; /*!< User agent in SIP request (saved from registration) */
+ struct ast_codec_pref prefs; /*!< codec prefs */
+ int lastmsgssent;
+ unsigned int sipoptions; /*!< Supported SIP options */
+ struct ast_flags flags[2]; /*!< SIP_ flags */
+
+ /*! Mailboxes that this peer cares about */
+ AST_LIST_HEAD_NOLOCK(, sip_mailbox) mailboxes;
+
+ /* things that don't belong in flags */
+ char is_realtime; /*!< this is a 'realtime' peer */
+ char rt_fromcontact; /*!< P: copy fromcontact from realtime */
+ char host_dynamic; /*!< P: Dynamic Peers register with Asterisk */
+ char selfdestruct; /*!< P: Automatic peers need to destruct themselves */
+
+ int expire; /*!< When to expire this peer registration */
+ int capability; /*!< Codec capability */
+ int rtptimeout; /*!< RTP timeout */
+ int rtpholdtimeout; /*!< RTP Hold Timeout */
+ int rtpkeepalive; /*!< Send RTP packets for keepalive */
+ ast_group_t callgroup; /*!< Call group */
+ ast_group_t pickupgroup; /*!< Pickup group */
+ struct sip_proxy *outboundproxy; /*!< Outbound proxy for this peer */
+ struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */
+ struct sockaddr_in addr; /*!< IP address of peer */
+ int maxcallbitrate; /*!< Maximum Bitrate for a video call */
+
+ /* Qualification */
+ struct sip_pvt *call; /*!< Call pointer */
+ int pokeexpire; /*!< When to expire poke (qualify= checking) */
+ int lastms; /*!< How long last response took (in ms), or -1 for no response */
+ int maxms; /*!< Max ms we will accept for the host to be up, 0 to not monitor */
+ int qualifyfreq; /*!< Qualification: How often to check for the host to be up */
+ struct timeval ps; /*!< Time for sending SIP OPTION in sip_pke_peer() */
+ struct sockaddr_in defaddr; /*!< Default IP address, used until registration */
+ struct ast_ha *ha; /*!< Access control list */
+ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */
+ struct sip_pvt *mwipvt; /*!< Subscription for MWI */
+ int autoframing;
+ struct sip_st_cfg stimer; /*!< SIP Session-Timers */
+ int timer_t1; /*!< The maximum T1 value for the peer */
+ int timer_b; /*!< The maximum timer B (transaction timeouts) */
+};
+
+
+/*! \brief Registrations with other SIP proxies
+ * Created by sip_register(), the entry is linked in the 'regl' list,
+ * and never deleted (other than at 'sip reload' or module unload times).
+ * The entry always has a pending timeout, either waiting for an ACK to
+ * the REGISTER message (in which case we have to retransmit the request),
+ * or waiting for the next REGISTER message to be sent (either the initial one,
+ * or once the previously completed registration one expires).
+ * The registration can be in one of many states, though at the moment
+ * the handling is a bit mixed.
+ * Note that the entire evolution of sip_registry (transmissions,
+ * incoming packets and timeouts) is driven by one single thread,
+ * do_monitor(), so there is almost no synchronization issue.
+ * The only exception is the sip_pvt creation/lookup,
+ * as the dialoglist is also manipulated by other threads.
+ */
+struct sip_registry {
+ ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1);
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(callid); /*!< Global Call-ID */
+ AST_STRING_FIELD(realm); /*!< Authorization realm */
+ AST_STRING_FIELD(nonce); /*!< Authorization nonce */
+ AST_STRING_FIELD(opaque); /*!< Opaque nonsense */
+ AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */
+ AST_STRING_FIELD(domain); /*!< Authorization domain */
+ AST_STRING_FIELD(username); /*!< Who we are registering as */
+ AST_STRING_FIELD(authuser); /*!< Who we *authenticate* as */
+ AST_STRING_FIELD(hostname); /*!< Domain or host we register to */
+ AST_STRING_FIELD(secret); /*!< Password in clear text */
+ AST_STRING_FIELD(md5secret); /*!< Password in md5 */
+ AST_STRING_FIELD(callback); /*!< Contact extension */
+ AST_STRING_FIELD(random);
+ );
+ enum sip_transport transport;
+ int portno; /*!< Optional port override */
+ int expire; /*!< Sched ID of expiration */
+ int expiry; /*!< Value to use for the Expires header */
+ int regattempts; /*!< Number of attempts (since the last success) */
+ int timeout; /*!< sched id of sip_reg_timeout */
+ int refresh; /*!< How often to refresh */
+ struct sip_pvt *call; /*!< create a sip_pvt structure for each outbound "registration dialog" in progress */
+ enum sipregistrystate regstate; /*!< Registration state (see above) */
+ struct timeval regtime; /*!< Last successful registration time */
+ int callid_valid; /*!< 0 means we haven't chosen callid for this registry yet. */
+ unsigned int ocseq; /*!< Sequence number we got to for REGISTERs for this registry */
+ struct sockaddr_in us; /*!< Who the server thinks we are */
+ int noncecount; /*!< Nonce-count */
+ char lastmsg[256]; /*!< Last Message sent/received */
+};
+
+struct sip_threadinfo {
+ int stop;
+ pthread_t threadid;
+ struct server_instance *ser;
+ enum sip_transport type; /* We keep a copy of the type here so we can display it in the connection list */
+ AST_LIST_ENTRY(sip_threadinfo) list;
+};
+
+/* --- Linked lists of various objects --------*/
+
+/*! \brief The thread list of TCP threads */
+static AST_LIST_HEAD_STATIC(threadl, sip_threadinfo);
+
+/*! \brief The user list: Users and friends */
+static struct ast_user_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct sip_user);
+} userl;
+
+/*! \brief The peer list: Peers and Friends */
+static struct ast_peer_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct sip_peer);
+} peerl;
+
+/*! \brief The register list: Other SIP proxies we register with and place calls to */
+static struct ast_register_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry);
+ int recheck;
+} regl;
+
+static int temp_pvt_init(void *);
+static void temp_pvt_cleanup(void *);
+
+/*! \brief A per-thread temporary pvt structure */
+AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
+
+/*! \brief Authentication list for realm authentication
+ * \todo Move the sip_auth list to AST_LIST */
+static struct sip_auth *authl = NULL;
+
+
+/* --- Sockets and networking --------------*/
+
+/*! \brief Main socket for SIP communication.
+ * sipsock is shared between the manager thread (which handles reload
+ * requests), the io handler (sipsock_read()) and the user routines that
+ * issue writes (using __sip_xmit()).
+ * The socket is -1 only when opening fails (this is a permanent condition),
+ * or when we are handling a reload() that changes its address (this is
+ * a transient situation during which we might have a harmless race, see
+ * below). Because the conditions for the race to be possible are extremely
+ * rare, we don't want to pay the cost of locking on every I/O.
+ * Rather, we remember that when the race may occur, communication is
+ * bound to fail anyways, so we just live with this event and let
+ * the protocol handle this above us.
+ */
+static int sipsock = -1;
+
+static struct sockaddr_in bindaddr; /*!< The address we bind to */
+
+/*! \brief our (internal) default address/port to put in SIP/SDP messages
+ * internip is initialized picking a suitable address from one of the
+ * interfaces, and the same port number we bind to. It is used as the
+ * default address/port in SIP messages, and as the default address
+ * (but not port) in SDP messages.
+ */
+static struct sockaddr_in internip;
+
+/*! \brief our external IP address/port for SIP sessions.
+ * externip.sin_addr is only set when we know we might be behind
+ * a NAT, and this is done using a variety of (mutually exclusive)
+ * ways from the config file:
+ *
+ * + with "externip = host[:port]" we specify the address/port explicitly.
+ * The address is looked up only once when (re)loading the config file;
+ *
+ * + with "externhost = host[:port]" we do a similar thing, but the
+ * hostname is stored in externhost, and the hostname->IP mapping
+ * is refreshed every 'externrefresh' seconds;
+ *
+ * + with "stunaddr = host[:port]" we run queries every externrefresh seconds
+ * to the specified server, and store the result in externip.
+ *
+ * Other variables (externhost, externexpire, externrefresh) are used
+ * to support the above functions.
+ */
+static struct sockaddr_in externip; /*!< External IP address if we are behind NAT */
+
+static char externhost[MAXHOSTNAMELEN]; /*!< External host name */
+static time_t externexpire; /*!< Expiration counter for re-resolving external host name in dynamic DNS */
+static int externrefresh = 10;
+static struct sockaddr_in stunaddr; /*!< stun server address */
+
+/*! \brief List of local networks
+ * We store "localnet" addresses from the config file into an access list,
+ * marked as 'DENY', so the call to ast_apply_ha() will return
+ * AST_SENSE_DENY for 'local' addresses, and AST_SENSE_ALLOW for 'non local'
+ * (i.e. presumably public) addresses.
+ */
+static struct ast_ha *localaddr; /*!< List of local networks, on the same side of NAT as this Asterisk */
+
+static int ourport_tcp;
+static int ourport_tls;
+static struct sockaddr_in debugaddr;
+
+static struct ast_config *notify_types; /*!< The list of manual NOTIFY types we know how to send */
+
+/*! some list management macros. */
+
+#define UNLINK(element, head, prev) do { \
+ if (prev) \
+ (prev)->next = (element)->next; \
+ else \
+ (head) = (element)->next; \
+ } while (0)
+
+/*---------------------------- Forward declarations of functions in chan_sip.c */
+/*! \note This is added to help splitting up chan_sip.c into several files
+ in coming releases */
+
+/*--- PBX interface functions */
+static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause);
+static int sip_devicestate(void *data);
+static int sip_sendtext(struct ast_channel *ast, const char *text);
+static int sip_call(struct ast_channel *ast, char *dest, int timeout);
+static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen);
+static int sip_hangup(struct ast_channel *ast);
+static int sip_answer(struct ast_channel *ast);
+static struct ast_frame *sip_read(struct ast_channel *ast);
+static int sip_write(struct ast_channel *ast, struct ast_frame *frame);
+static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int sip_transfer(struct ast_channel *ast, const char *dest);
+static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int sip_senddigit_begin(struct ast_channel *ast, char digit);
+static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
+
+static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin);
+static int sip_standard_port(struct sip_socket s);
+static int sip_prepare_socket(struct sip_pvt *p);
+
+/*--- Transmitting responses and requests */
+static int sipsock_read(int *id, int fd, short events, void *ignore);
+static int __sip_xmit(struct sip_pvt *p, char *data, int len);
+static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len, int fatal, int sipmethod);
+static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
+static int retrans_pkt(const void *data);
+static int transmit_sip_request(struct sip_pvt *p, struct sip_request *req);
+static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg);
+static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req);
+static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req);
+static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req);
+static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp);
+static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
+static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
+static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
+static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable);
+static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
+static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch);
+static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init);
+static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp);
+static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
+static int transmit_info_with_vidupdate(struct sip_pvt *p);
+static int transmit_message_with_text(struct sip_pvt *p, const char *text);
+static int transmit_refer(struct sip_pvt *p, const char *dest);
+static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, char *vmexten);
+static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
+static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader);
+static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
+static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
+static void copy_request(struct sip_request *dst, const struct sip_request *src);
+static void receive_message(struct sip_pvt *p, struct sip_request *req);
+static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req);
+static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only);
+
+/*--- Dialog management */
+static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
+ int useglobal_nat, const int intended_method);
+static int __sip_autodestruct(const void *data);
+static void sip_scheddestroy(struct sip_pvt *p, int ms);
+static void sip_cancel_destroy(struct sip_pvt *p);
+static struct sip_pvt *sip_destroy(struct sip_pvt *p);
+static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist);
+static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
+static void __sip_pretend_ack(struct sip_pvt *p);
+static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
+static int auto_congest(const void *arg);
+static int update_call_counter(struct sip_pvt *fup, int event);
+static int hangup_sip2cause(int cause);
+static const char *hangup_cause2sip(int cause);
+static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method);
+static void free_old_route(struct sip_route *route);
+static void list_route(struct sip_route *route);
+static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards);
+static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin,
+ struct sip_request *req, char *uri);
+static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
+static void check_pendings(struct sip_pvt *p);
+static void *sip_park_thread(void *stuff);
+static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno);
+static int sip_sipredirect(struct sip_pvt *p, const char *dest);
+
+/*--- Codec handling / SDP */
+static void try_suggested_sip_codec(struct sip_pvt *p);
+static const char* get_sdp_iterate(int* start, struct sip_request *req, const char *name);
+static const char *get_sdp(struct sip_request *req, const char *name);
+static int find_sdp(struct sip_request *req);
+static int process_sdp(struct sip_pvt *p, struct sip_request *req);
+static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
+ struct ast_str **m_buf, struct ast_str **a_buf,
+ int debug, int *min_packet_size);
+static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
+ struct ast_str **m_buf, struct ast_str **a_buf,
+ int debug);
+static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp);
+static void do_setnat(struct sip_pvt *p, int natflags);
+static void stop_media_flows(struct sip_pvt *p);
+
+/*--- Authentication stuff */
+static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len);
+static int build_reply_digest(struct sip_pvt *p, int method, char *digest, int digest_len);
+static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
+ const char *secret, const char *md5secret, int sipmethod,
+ char *uri, enum xmittype reliable, int ignore);
+static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
+ int sipmethod, char *uri, enum xmittype reliable,
+ struct sockaddr_in *sin, struct sip_peer **authpeer);
+static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin);
+
+/*--- Domain handling */
+static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */
+static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context);
+static void clear_sip_domains(void);
+
+/*--- SIP realm authentication */
+static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, const char *configuration, int lineno);
+static int clear_realm_authentication(struct sip_auth *authlist); /* Clear realm authentication list (at reload) */
+static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm);
+
+/*--- Misc functions */
+static int sip_do_reload(enum channelreloadreason reason);
+static int reload_config(enum channelreloadreason reason);
+static int expire_register(const void *data);
+static void *do_monitor(void *data);
+static int restart_monitor(void);
+static int sip_addrcmp(char *name, struct sockaddr_in *sin); /* Support for peer matching */
+static int sip_refer_allocate(struct sip_pvt *p);
+static void ast_quiet_chan(struct ast_channel *chan);
+static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
+
+/*--- Device monitoring and Device/extension state/event handling */
+static int cb_extensionstate(char *context, char* exten, int state, void *data);
+static int sip_devicestate(void *data);
+static int sip_poke_noanswer(const void *data);
+static int sip_poke_peer(struct sip_peer *peer);
+static void sip_poke_all_peers(void);
+static void sip_peer_hold(struct sip_pvt *p, int hold);
+static void mwi_event_cb(const struct ast_event *, void *);
+
+/*--- Applications, functions, CLI and manager command helpers */
+static const char *sip_nat_mode(const struct sip_pvt *p);
+static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *transfermode2str(enum transfermodes mode) attribute_const;
+static const char *nat2str(int nat) attribute_const;
+static int peer_status(struct sip_peer *peer, char *status, int statuslen);
+static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
+static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static void print_group(int fd, ast_group_t group, int crlf);
+static const char *dtmfmode2str(int mode) attribute_const;
+static int str2dtmfmode(const char *str) attribute_unused;
+static const char *insecure2str(int mode) attribute_const;
+static void cleanup_stale_contexts(char *new, char *old);
+static void print_codec_to_cli(int fd, struct ast_codec_pref *pref);
+static const char *domain_mode_to_text(const enum domain_mode mode);
+static char *sip_show_domains(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]);
+static char *sip_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static const char *subscription_type2str(enum subscriptiontype subtype) attribute_pure;
+static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
+static char *complete_sip_peer(const char *word, int state, int flags2);
+static char *complete_sip_registered_peer(const char *word, int state, int flags2);
+static char *complete_sip_show_history(const char *line, const char *word, int pos, int state);
+static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state);
+static char *complete_sip_unregister(const char *line, const char *word, int pos, int state);
+static char *complete_sip_user(const char *word, int state, int flags2);
+static char *complete_sip_show_user(const char *line, const char *word, int pos, int state);
+static char *complete_sipnotify(const char *line, const char *word, int pos, int state);
+static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_do_debug_ip(int fd, char *arg);
+static char *sip_do_debug_peer(int fd, char *arg);
+static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_do_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_no_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static int sip_dtmfmode(struct ast_channel *chan, void *data);
+static int sip_addheader(struct ast_channel *chan, void *data);
+static int sip_do_reload(enum channelreloadreason reason);
+static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
+
+/*--- Debugging
+ Functions for enabling debug per IP or fully, or enabling history logging for
+ a SIP dialog
+*/
+static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to debuglog at end of dialog, before destroying data */
+static inline int sip_debug_test_addr(const struct sockaddr_in *addr);
+static inline int sip_debug_test_pvt(struct sip_pvt *p);
+
+
+/*! \brief Append to SIP dialog history
+ \return Always returns 0 */
+#define append_history(p, event, fmt , args... ) append_history_full(p, "%-15s " fmt, event, ## args)
+static void append_history_full(struct sip_pvt *p, const char *fmt, ...);
+static void sip_dump_history(struct sip_pvt *dialog);
+
+/*--- Device object handling */
+static struct sip_peer *temp_peer(const char *name);
+static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime);
+static struct sip_user *build_user(const char *name, struct ast_variable *v, int realtime);
+static int update_call_counter(struct sip_pvt *fup, int event);
+static void sip_destroy_peer(struct sip_peer *peer);
+static void sip_destroy_user(struct sip_user *user);
+static int sip_poke_peer(struct sip_peer *peer);
+static void set_peer_defaults(struct sip_peer *peer);
+static struct sip_peer *temp_peer(const char *name);
+static void register_peer_exten(struct sip_peer *peer, int onoff);
+static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime);
+static struct sip_user *find_user(const char *name, int realtime);
+static int sip_poke_peer_s(const void *data);
+static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req);
+static void reg_source_db(struct sip_peer *peer);
+static void destroy_association(struct sip_peer *peer);
+static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno);
+static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v);
+
+/* Realtime device support */
+static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, int expirey);
+static struct sip_user *realtime_user(const char *username);
+static void update_peer(struct sip_peer *p, int expiry);
+static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config);
+static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername);
+static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
+static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+/*--- Internal UA client handling (outbound registrations) */
+static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us);
+static void sip_registry_destroy(struct sip_registry *reg);
+static int sip_register(const char *value, int lineno);
+static const char *regstate2str(enum sipregistrystate regstate) attribute_const;
+static int sip_reregister(const void *data);
+static int __sip_do_register(struct sip_registry *r);
+static int sip_reg_timeout(const void *data);
+static void sip_send_all_registers(void);
+
+/*--- Parsing SIP requests and responses */
+static void append_date(struct sip_request *req); /* Append date to SIP packet */
+static int determine_firstline_parts(struct sip_request *req);
+static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
+static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize);
+static int find_sip_method(const char *msg);
+static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported);
+static void parse_request(struct sip_request *req);
+static const char *get_header(const struct sip_request *req, const char *name);
+static const char *referstatus2str(enum referstatus rstatus) attribute_pure;
+static int method_match(enum sipmethod id, const char *name);
+static void parse_copy(struct sip_request *dst, const struct sip_request *src);
+static char *get_in_brackets(char *tmp);
+static const char *find_alias(const char *name, const char *_default);
+static const char *__get_header(const struct sip_request *req, const char *name, int *start);
+static int lws2sws(char *msgbuf, int len);
+static void extract_uri(struct sip_pvt *p, struct sip_request *req);
+static char *remove_uri_parameters(char *uri);
+static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req);
+static int get_also_info(struct sip_pvt *p, struct sip_request *oreq);
+static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
+static int set_address_from_contact(struct sip_pvt *pvt);
+static void check_via(struct sip_pvt *p, struct sip_request *req);
+static char *get_calleridname(const char *input, char *output, size_t outputsize);
+static int get_rpid_num(const char *input, char *output, int maxlen);
+static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq);
+static int get_destination(struct sip_pvt *p, struct sip_request *oreq);
+static int get_msg_text(char *buf, int len, struct sip_request *req);
+static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout);
+
+/*--- Constructing requests and responses */
+static void initialize_initreq(struct sip_pvt *p, struct sip_request *req);
+static int init_req(struct sip_request *req, int sipmethod, const char *recip);
+static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch);
+static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod);
+static int init_resp(struct sip_request *resp, const char *msg);
+static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req);
+static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p);
+static void build_via(struct sip_pvt *p);
+static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer);
+static int create_addr(struct sip_pvt *dialog, const char *opeer);
+static char *generate_random_string(char *buf, size_t size);
+static void build_callid_pvt(struct sip_pvt *pvt);
+static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain);
+static void make_our_tag(char *tagbuf, size_t len);
+static int add_header(struct sip_request *req, const char *var, const char *value);
+static int add_header_contentLength(struct sip_request *req, int len);
+static int add_line(struct sip_request *req, const char *line);
+static int add_text(struct sip_request *req, const char *text);
+static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode);
+static int add_vidupdate(struct sip_request *req);
+static void add_route(struct sip_request *req, struct sip_route *route);
+static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field);
+static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field);
+static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field);
+static void set_destination(struct sip_pvt *p, char *uri);
+static void append_date(struct sip_request *req);
+static void build_contact(struct sip_pvt *p);
+static void build_rpid(struct sip_pvt *p);
+
+/*------Request handling functions */
+static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock);
+static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock);
+static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock);
+static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
+static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e);
+static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
+static int handle_request_message(struct sip_pvt *p, struct sip_request *req);
+static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e);
+static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
+static int handle_request_options(struct sip_pvt *p, struct sip_request *req);
+static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin);
+static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e);
+static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno);
+
+/*------Response handling functions */
+static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
+static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
+static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
+static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
+
+/*----- RTP interface functions */
+static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active);
+static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static int sip_get_codec(struct ast_channel *chan);
+static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect);
+
+/*------ T38 Support --------- */
+static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite);
+static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
+static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan);
+static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl);
+
+/*------ Session-Timers functions --------- */
+static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp);
+static int proc_session_timer(const void *vp);
+static void stop_session_timer(struct sip_pvt *p);
+static void start_session_timer(struct sip_pvt *p);
+static void restart_session_timer(struct sip_pvt *p);
+static const char *strefresher2str(enum st_refresher r);
+static int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher *const p_ref);
+static int parse_minse(const char *p_hdrval, int *const p_interval);
+static int st_get_se(struct sip_pvt *, int max);
+static enum st_refresher st_get_refresher(struct sip_pvt *);
+static enum st_mode st_get_mode(struct sip_pvt *);
+static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p);
+
+
+/*! \brief Definition of this channel for PBX channel registration */
+static const struct ast_channel_tech sip_tech = {
+ .type = "SIP",
+ .description = "Session Initiation Protocol (SIP)",
+ .capabilities = AST_FORMAT_AUDIO_MASK, /* all audio formats */
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
+ .requester = sip_request_call, /* called with chan unlocked */
+ .devicestate = sip_devicestate, /* called with chan unlocked (not chan-specific) */
+ .call = sip_call, /* called with chan locked */
+ .send_html = sip_sendhtml,
+ .hangup = sip_hangup, /* called with chan locked */
+ .answer = sip_answer, /* called with chan locked */
+ .read = sip_read, /* called with chan locked */
+ .write = sip_write, /* called with chan locked */
+ .write_video = sip_write, /* called with chan locked */
+ .write_text = sip_write,
+ .indicate = sip_indicate, /* called with chan locked */
+ .transfer = sip_transfer, /* called with chan locked */
+ .fixup = sip_fixup, /* called with chan locked */
+ .send_digit_begin = sip_senddigit_begin, /* called with chan unlocked */
+ .send_digit_end = sip_senddigit_end,
+ .bridge = ast_rtp_bridge, /* XXX chan unlocked ? */
+ .early_bridge = ast_rtp_early_bridge,
+ .send_text = sip_sendtext, /* called with chan locked */
+ .func_channel_read = acf_channel_read,
+};
+
+/*! \brief This version of the sip channel tech has no send_digit_begin
+ * callback so that the core knows that the channel does not want
+ * DTMF BEGIN frames.
+ * The struct is initialized just before registering the channel driver,
+ * and is for use with channels using SIP INFO DTMF.
+ */
+static struct ast_channel_tech sip_tech_info;
+
+static void *sip_tcp_worker_fn(void *);
+
+static struct ast_tls_config sip_tls_cfg;
+static struct ast_tls_config default_tls_cfg;
+
+static struct server_args sip_tcp_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = NULL,
+ .poll_timeout = -1,
+ .name = "sip tcp server",
+ .accept_fn = server_root,
+ .worker_fn = sip_tcp_worker_fn,
+};
+
+static struct server_args sip_tls_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = &sip_tls_cfg,
+ .poll_timeout = -1,
+ .name = "sip tls server",
+ .accept_fn = server_root,
+ .worker_fn = sip_tcp_worker_fn,
+};
+
+/* wrapper macro to tell whether t points to one of the sip_tech descriptors */
+#define IS_SIP_TECH(t) ((t) == &sip_tech || (t) == &sip_tech_info)
+
+/*! \brief map from an integer value to a string.
+ * If no match is found, return errorstring
+ */
+static const char *map_x_s(const struct _map_x_s *table, int x, const char *errorstring)
+{
+ const struct _map_x_s *cur;
+
+ for (cur = table; cur->s; cur++)
+ if (cur->x == x)
+ return cur->s;
+ return errorstring;
+}
+
+/*! \brief map from a string to an integer value, case insensitive.
+ * If no match is found, return errorvalue.
+ */
+static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
+{
+ const struct _map_x_s *cur;
+
+ for (cur = table; cur->s; cur++)
+ if (!strcasecmp(cur->s, s))
+ return cur->x;
+ return errorvalue;
+}
+
+
+/*! \brief Interface structure with callbacks used to connect to RTP module */
+static struct ast_rtp_protocol sip_rtp = {
+ .type = "SIP",
+ .get_rtp_info = sip_get_rtp_peer,
+ .get_vrtp_info = sip_get_vrtp_peer,
+ .get_trtp_info = sip_get_trtp_peer,
+ .set_rtp_peer = sip_set_rtp_peer,
+ .get_codec = sip_get_codec,
+};
+
+static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct server_instance *ser);
+
+static void *sip_tcp_helper_thread(void *data)
+{
+ struct sip_pvt *pvt = data;
+ struct server_instance *ser = pvt->socket.ser;
+
+ return _sip_tcp_helper_thread(pvt, ser);
+}
+
+static void *sip_tcp_worker_fn(void *data)
+{
+ struct server_instance *ser = data;
+
+ return _sip_tcp_helper_thread(NULL, ser);
+}
+
+/*! \brief SIP TCP helper function */
+static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct server_instance *ser)
+{
+ int res, cl;
+ struct sip_request req = { 0, } , reqcpy = { 0, };
+ struct sip_threadinfo *me;
+ char buf[1024];
+
+ me = ast_calloc(1, sizeof(*me));
+
+ if (!me)
+ goto cleanup2;
+
+ me->threadid = pthread_self();
+ me->ser = ser;
+ if (ser->ssl)
+ me->type = SIP_TRANSPORT_TLS;
+ else
+ me->type = SIP_TRANSPORT_TCP;
+
+ AST_LIST_LOCK(&threadl);
+ AST_LIST_INSERT_TAIL(&threadl, me, list);
+ AST_LIST_UNLOCK(&threadl);
+
+ req.socket.lock = ast_calloc(1, sizeof(*req.socket.lock));
+
+ if (!req.socket.lock)
+ goto cleanup;
+
+ ast_mutex_init(req.socket.lock);
+
+ for (;;) {
+ memset(req.data, 0, sizeof(req.data));
+ req.len = 0;
+ req.ignore = 0;
+
+ req.socket.fd = ser->fd;
+ if (ser->ssl) {
+ req.socket.type = SIP_TRANSPORT_TLS;
+ req.socket.port = htons(ourport_tls);
+ } else {
+ req.socket.type = SIP_TRANSPORT_TCP;
+ req.socket.port = htons(ourport_tcp);
+ }
+ res = ast_wait_for_input(ser->fd, -1);
+ if (res < 0) {
+ ast_log(LOG_DEBUG, "ast_wait_for_input returned %d\n", res);
+ goto cleanup;
+ }
+
+ /* Read in headers one line at a time */
+ while (req.len < 4 || strncmp((char *)&req.data + req.len - 4, "\r\n\r\n", 4)) {
+ if (req.socket.lock)
+ ast_mutex_lock(req.socket.lock);
+ if (!fgets(buf, sizeof(buf), ser->f))
+ goto cleanup;
+ if (req.socket.lock)
+ ast_mutex_unlock(req.socket.lock);
+ if (me->stop)
+ goto cleanup;
+ strncat(req.data, buf, sizeof(req.data) - req.len);
+ req.len = strlen(req.data);
+ }
+ parse_copy(&reqcpy, &req);
+ if (sscanf(get_header(&reqcpy, "Content-Length"), "%d", &cl)) {
+ while (cl > 0) {
+ if (req.socket.lock)
+ ast_mutex_lock(req.socket.lock);
+ if (!fread(buf, (cl < sizeof(buf)) ? cl : sizeof(buf), 1, ser->f))
+ goto cleanup;
+ if (req.socket.lock)
+ ast_mutex_unlock(req.socket.lock);
+ if (me->stop)
+ goto cleanup;
+ cl -= strlen(buf);
+ strncat(req.data, buf, sizeof(req.data) - req.len);
+ req.len = strlen(req.data);
+ }
+ }
+ req.socket.ser = ser;
+ handle_request_do(&req, &ser->requestor);
+ }
+
+cleanup:
+ AST_LIST_LOCK(&threadl);
+ AST_LIST_REMOVE(&threadl, me, list);
+ AST_LIST_UNLOCK(&threadl);
+ ast_free(me);
+cleanup2:
+ fclose(ser->f);
+ ast_free(ser);
+ ast_free(req.socket.lock);
+
+ return NULL;
+}
+
+#define sip_pvt_lock(x) ast_mutex_lock(&x->pvt_lock)
+#define sip_pvt_unlock(x) ast_mutex_unlock(&x->pvt_lock)
+
+/*!
+ * helper functions to unreference various types of objects.
+ * By handling them this way, we don't have to declare the
+ * destructor on each call, which removes the chance of errors.
+ */
+static void unref_peer(struct sip_peer *peer)
+{
+ ASTOBJ_UNREF(peer, sip_destroy_peer);
+}
+
+static void unref_user(struct sip_user *user)
+{
+ ASTOBJ_UNREF(user, sip_destroy_user);
+}
+
+static void *registry_unref(struct sip_registry *reg)
+{
+ ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount - 1);
+ ASTOBJ_UNREF(reg, sip_registry_destroy);
+ return NULL;
+}
+
+/*! \brief Add object reference to SIP registry */
+static struct sip_registry *registry_addref(struct sip_registry *reg)
+{
+ ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount + 1);
+ return ASTOBJ_REF(reg); /* Add pointer to registry in packet */
+}
+
+/*! \brief Interface structure with callbacks used to connect to UDPTL module*/
+static struct ast_udptl_protocol sip_udptl = {
+ type: "SIP",
+ get_udptl_info: sip_get_udptl_peer,
+ set_udptl_peer: sip_set_udptl_peer,
+};
+
+static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+
+/*! \brief Convert transfer status to string */
+static const char *referstatus2str(enum referstatus rstatus)
+{
+ return map_x_s(referstatusstrings, rstatus, "");
+}
+
+/*! \brief Initialize the initital request packet in the pvt structure.
+ This packet is used for creating replies and future requests in
+ a dialog */
+static void initialize_initreq(struct sip_pvt *p, struct sip_request *req)
+{
+ if (p->initreq.headers)
+ ast_debug(1, "Initializing already initialized SIP dialog %s (presumably reinvite)\n", p->callid);
+ else
+ ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
+ /* Use this as the basis */
+ copy_request(&p->initreq, req);
+ parse_request(&p->initreq);
+ if (req->debug)
+ ast_verbose("Initreq: %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
+}
+
+/*! \brief Encapsulate setting of SIP_ALREADYGONE to be able to trace it with debugging */
+static void sip_alreadygone(struct sip_pvt *dialog)
+{
+ ast_debug(3, "Setting SIP_ALREADYGONE on dialog %s\n", dialog->callid);
+ dialog->alreadygone = 1;
+}
+
+/*! Resolve DNS srv name or host name in a sip_proxy structure */
+static int proxy_update(struct sip_proxy *proxy)
+{
+ /* if it's actually an IP address and not a name,
+ there's no need for a managed lookup */
+ if (!inet_aton(proxy->name, &proxy->ip.sin_addr)) {
+ /* Ok, not an IP address, then let's check if it's a domain or host */
+ /* XXX Todo - if we have proxy port, don't do SRV */
+ if (ast_get_ip_or_srv(&proxy->ip, proxy->name, global_srvlookup ? "_sip._udp" : NULL) < 0) {
+ ast_log(LOG_WARNING, "Unable to locate host '%s'\n", proxy->name);
+ return FALSE;
+ }
+ }
+ proxy->last_dnsupdate = time(NULL);
+ return TRUE;
+}
+
+/*! \brief Allocate and initialize sip proxy */
+static struct sip_proxy *proxy_allocate(char *name, char *port, int force)
+{
+ struct sip_proxy *proxy;
+ proxy = ast_calloc(1, sizeof(*proxy));
+ if (!proxy)
+ return NULL;
+ proxy->force = force;
+ ast_copy_string(proxy->name, name, sizeof(proxy->name));
+ proxy->ip.sin_port = htons((!ast_strlen_zero(port) ? atoi(port) : STANDARD_SIP_PORT));
+ proxy_update(proxy);
+ return proxy;
+}
+
+/*! \brief Get default outbound proxy or global proxy */
+static struct sip_proxy *obproxy_get(struct sip_pvt *dialog, struct sip_peer *peer)
+{
+ if (peer && peer->outboundproxy) {
+ if (sipdebug)
+ ast_debug(1, "OBPROXY: Applying peer OBproxy to this call\n");
+ append_history(dialog, "OBproxy", "Using peer obproxy %s", peer->outboundproxy->name);
+ return peer->outboundproxy;
+ }
+ if (global_outboundproxy.name[0]) {
+ if (sipdebug)
+ ast_debug(1, "OBPROXY: Applying global OBproxy to this call\n");
+ append_history(dialog, "OBproxy", "Using global obproxy %s", global_outboundproxy.name);
+ return &global_outboundproxy;
+ }
+ if (sipdebug)
+ ast_debug(1, "OBPROXY: Not applying OBproxy to this call\n");
+ return NULL;
+}
+
+/*! \brief returns true if 'name' (with optional trailing whitespace)
+ * matches the sip method 'id'.
+ * Strictly speaking, SIP methods are case SENSITIVE, but we do
+ * a case-insensitive comparison to be more tolerant.
+ * following Jon Postel's rule: Be gentle in what you accept, strict with what you send
+ */
+static int method_match(enum sipmethod id, const char *name)
+{
+ int len = strlen(sip_methods[id].text);
+ int l_name = name ? strlen(name) : 0;
+ /* true if the string is long enough, and ends with whitespace, and matches */
+ return (l_name >= len && name[len] < 33 &&
+ !strncasecmp(sip_methods[id].text, name, len));
+}
+
+/*! \brief find_sip_method: Find SIP method from header */
+static int find_sip_method(const char *msg)
+{
+ int i, res = 0;
+
+ if (ast_strlen_zero(msg))
+ return 0;
+ for (i = 1; i < (sizeof(sip_methods) / sizeof(sip_methods[0])) && !res; i++) {
+ if (method_match(i, msg))
+ res = sip_methods[i].id;
+ }
+ return res;
+}
+
+/*! \brief Parse supported header in incoming packet */
+static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported)
+{
+ char *next, *sep;
+ char *temp;
+ unsigned int profile = 0;
+ int i, found;
+
+ if (ast_strlen_zero(supported) )
+ return 0;
+ temp = ast_strdupa(supported);
+
+ if (sipdebug)
+ ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", supported);
+
+ for (next = temp; next; next = sep) {
+ found = FALSE;
+ if ( (sep = strchr(next, ',')) != NULL)
+ *sep++ = '\0';
+ next = ast_skip_blanks(next);
+ if (sipdebug)
+ ast_debug(3, "Found SIP option: -%s-\n", next);
+ for (i=0; i < (sizeof(sip_options) / sizeof(sip_options[0])); i++) {
+ if (!strcasecmp(next, sip_options[i].text)) {
+ profile |= sip_options[i].id;
+ found = TRUE;
+ if (sipdebug)
+ ast_debug(3, "Matched SIP option: %s\n", next);
+ break;
+ }
+ }
+
+ /* This function is used to parse both Suported: and Require: headers.
+ Let the caller of this function know that an unknown option tag was
+ encountered, so that if the UAC requires it then the request can be
+ rejected with a 420 response. */
+ if (!found)
+ profile |= SIP_OPT_UNKNOWN;
+
+ if (!found && sipdebug) {
+ if (!strncasecmp(next, "x-", 2))
+ ast_debug(3, "Found private SIP option, not supported: %s\n", next);
+ else
+ ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
+ }
+ }
+
+ if (pvt)
+ pvt->sipoptions = profile;
+ return profile;
+}
+
+/*! \brief See if we pass debug IP filter */
+static inline int sip_debug_test_addr(const struct sockaddr_in *addr)
+{
+ if (!sipdebug)
+ return 0;
+ if (debugaddr.sin_addr.s_addr) {
+ if (((ntohs(debugaddr.sin_port) != 0)
+ && (debugaddr.sin_port != addr->sin_port))
+ || (debugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+ return 0;
+ }
+ return 1;
+}
+
+/*! \brief The real destination address for a write */
+static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
+{
+ if (p->outboundproxy)
+ return &p->outboundproxy->ip;
+
+ return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? &p->recv : &p->sa;
+}
+
+/*! \brief Display SIP nat mode */
+static const char *sip_nat_mode(const struct sip_pvt *p)
+{
+ return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? "NAT" : "no NAT";
+}
+
+/*! \brief Test PVT for debugging output */
+static inline int sip_debug_test_pvt(struct sip_pvt *p)
+{
+ if (!sipdebug)
+ return 0;
+ return sip_debug_test_addr(sip_real_dst(p));
+}
+
+static inline const char *get_transport(enum sip_transport t)
+{
+ switch (t) {
+ case SIP_TRANSPORT_UDP:
+ return "UDP";
+ case SIP_TRANSPORT_TCP:
+ return "TCP";
+ case SIP_TRANSPORT_TLS:
+ return "TLS";
+ }
+
+ return "UNKNOWN";
+}
+
+/*! \brief Transmit SIP message */
+static int __sip_xmit(struct sip_pvt *p, char *data, int len)
+{
+ int res = 0;
+ const struct sockaddr_in *dst = sip_real_dst(p);
+
+ ast_log(LOG_DEBUG, "Trying to put '%.10s' onto %s socket...\n", data, get_transport(p->socket.type));
+
+ if (sip_prepare_socket(p) < 0)
+ return XMIT_ERROR;
+
+ if (p->socket.lock)
+ ast_mutex_lock(p->socket.lock);
+
+ if (p->socket.type & SIP_TRANSPORT_UDP)
+ res = sendto(p->socket.fd, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in));
+ else {
+ if (p->socket.ser->f)
+ res = server_write(p->socket.ser, data, len);
+ else
+ ast_log(LOG_DEBUG, "No p->socket.ser->f len=%d\n", len);
+ }
+
+ if (p->socket.lock)
+ ast_mutex_unlock(p->socket.lock);
+
+ if (res == -1) {
+ switch (errno) {
+ case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
+ case EHOSTUNREACH: /* Host can't be reached */
+ case ENETDOWN: /* Interface down */
+ case ENETUNREACH: /* Network failure */
+ res = XMIT_ERROR; /* Don't bother with trying to transmit again */
+ }
+ }
+ if (res != len)
+ ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno));
+
+ return res;
+}
+
+/*! \brief Build a Via header for a request */
+static void build_via(struct sip_pvt *p)
+{
+ /* Work around buggy UNIDEN UIP200 firmware */
+ const char *rport = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_RFC3581 ? ";rport" : "";
+
+ /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */
+ ast_string_field_build(p, via, "SIP/2.0/%s %s:%d;branch=z9hG4bK%08x%s",
+ get_transport(p->socket.type),
+ ast_inet_ntoa(p->ourip.sin_addr),
+ ntohs(p->ourip.sin_port), p->branch, rport);
+}
+
+/*! \brief NAT fix - decide which IP address to use for Asterisk server?
+ *
+ * Using the localaddr structure built up with localnet statements in sip.conf
+ * apply it to their address to see if we need to substitute our
+ * externip or can get away with our internal bindaddr
+ * 'us' is always overwritten.
+ */
+static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us)
+{
+ struct sockaddr_in theirs;
+ /* Set want_remap to non-zero if we want to remap 'us' to an externally
+ * reachable IP address and port. This is done if:
+ * 1. we have a localaddr list (containing 'internal' addresses marked
+ * as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
+ * and AST_SENSE_ALLOW on 'external' ones);
+ * 2. either stunaddr or externip is set, so we know what to use as the
+ * externally visible address;
+ * 3. the remote address, 'them', is external;
+ * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
+ * when passed to ast_apply_ha() so it does need to be remapped.
+ * This fourth condition is checked later.
+ */
+ int want_remap;
+
+ *us = internip; /* starting guess for the internal address */
+ /* now ask the system what would it use to talk to 'them' */
+ ast_ouraddrfor(them, &us->sin_addr);
+ theirs.sin_addr = *them;
+
+ want_remap = localaddr &&
+ (externip.sin_addr.s_addr || stunaddr.sin_addr.s_addr) &&
+ ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
+
+ if (want_remap &&
+ (!global_matchexterniplocally || !ast_apply_ha(localaddr, us)) ) {
+ /* if we used externhost or stun, see if it is time to refresh the info */
+ if (externexpire && time(NULL) >= externexpire) {
+ if (stunaddr.sin_addr.s_addr) {
+ ast_stun_request(sipsock, &stunaddr, NULL, &externip);
+ } else {
+ if (ast_parse_arg(externhost, PARSE_INADDR, &externip))
+ ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
+ }
+ externexpire = time(NULL) + externrefresh;
+ }
+ if (externip.sin_addr.s_addr)
+ *us = externip;
+ else
+ ast_log(LOG_WARNING, "stun failed\n");
+ ast_debug(1, "Target address %s is not local, substituting externip\n",
+ ast_inet_ntoa(*(struct in_addr *)&them->s_addr));
+ } else if (bindaddr.sin_addr.s_addr) {
+ /* no remapping, but we bind to a specific address, so use it. */
+ *us = bindaddr;
+ }
+}
+
+/*! \brief Append to SIP dialog history with arg list */
+static void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap)
+{
+ char buf[80], *c = buf; /* max history length */
+ struct sip_history *hist;
+ int l;
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ strsep(&c, "\r\n"); /* Trim up everything after \r or \n */
+ l = strlen(buf) + 1;
+ if (!(hist = ast_calloc(1, sizeof(*hist) + l)))
+ return;
+ if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) {
+ ast_free(hist);
+ return;
+ }
+ memcpy(hist->event, buf, l);
+ if (p->history_entries == MAX_HISTORY_ENTRIES) {
+ struct sip_history *oldest;
+ oldest = AST_LIST_REMOVE_HEAD(p->history, list);
+ p->history_entries--;
+ ast_free(oldest);
+ }
+ AST_LIST_INSERT_TAIL(p->history, hist, list);
+ p->history_entries++;
+}
+
+/*! \brief Append to SIP dialog history with arg list */
+static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!p)
+ return;
+
+ if (!p->do_history && !recordhistory && !dumphistory)
+ return;
+
+ va_start(ap, fmt);
+ append_history_va(p, fmt, ap);
+ va_end(ap);
+
+ return;
+}
+
+/*! \brief Retransmit SIP message if no answer (Called from scheduler) */
+static int retrans_pkt(const void *data)
+{
+ struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
+ int reschedule = DEFAULT_RETRANS;
+ int xmitres = 0;
+
+ /* Lock channel PVT */
+ sip_pvt_lock(pkt->owner);
+
+ if (pkt->retrans < MAX_RETRANS) {
+ pkt->retrans++;
+ if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */
+ if (sipdebug)
+ ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method);
+ } else {
+ int siptimer_a;
+
+ if (sipdebug)
+ ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method);
+ if (!pkt->timer_a)
+ pkt->timer_a = 2 ;
+ else
+ pkt->timer_a = 2 * pkt->timer_a;
+
+ /* For non-invites, a maximum of 4 secs */
+ siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */
+ if (pkt->method != SIP_INVITE && siptimer_a > 4000)
+ siptimer_a = 4000;
+
+ /* Reschedule re-transmit */
+ reschedule = siptimer_a;
+ ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid);
+ }
+
+ if (sip_debug_test_pvt(pkt->owner)) {
+ const struct sockaddr_in *dst = sip_real_dst(pkt->owner);
+ ast_verbose("Retransmitting #%d (%s) to %s:%d:\n%s\n---\n",
+ pkt->retrans, sip_nat_mode(pkt->owner),
+ ast_inet_ntoa(dst->sin_addr),
+ ntohs(dst->sin_port), pkt->data);
+ }
+
+ append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data);
+ xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
+ sip_pvt_unlock(pkt->owner);
+ if (xmitres == XMIT_ERROR)
+ ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid);
+ else
+ return reschedule;
+ }
+ /* Too many retries */
+ if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) {
+ if (pkt->is_fatal || sipdebug) /* Tell us if it's critical or if we're debugging */
+ ast_log(LOG_WARNING, "Maximum retries exceeded on transmission %s for seqno %d (%s %s)\n",
+ pkt->owner->callid, pkt->seqno,
+ pkt->is_fatal ? "Critical" : "Non-critical", pkt->is_resp ? "Response" : "Request");
+ } else if (pkt->method == SIP_OPTIONS && sipdebug) {
+ ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid);
+
+ }
+ if (xmitres == XMIT_ERROR) {
+ ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid);
+ append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+ } else
+ append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+
+ pkt->retransid = -1;
+
+ if (pkt->is_fatal) {
+ while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) {
+ sip_pvt_unlock(pkt->owner); /* SIP_PVT, not channel */
+ usleep(1);
+ sip_pvt_lock(pkt->owner);
+ }
+
+ if (pkt->owner->owner && !pkt->owner->owner->hangupcause)
+ pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE;
+
+ if (pkt->owner->owner) {
+ sip_alreadygone(pkt->owner);
+ ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet.\n", pkt->owner->callid);
+ ast_queue_hangup(pkt->owner->owner);
+ ast_channel_unlock(pkt->owner->owner);
+ } else {
+ /* If no channel owner, destroy now */
+
+ /* Let the peerpoke system expire packets when the timer expires for poke_noanswer */
+ if (pkt->method != SIP_OPTIONS && pkt->method != SIP_REGISTER) {
+ pkt->owner->needdestroy = 1;
+ sip_alreadygone(pkt->owner);
+ append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately");
+ }
+ }
+ }
+
+ if (pkt->method == SIP_BYE) {
+ /* We're not getting answers on SIP BYE's. Tear down the call anyway. */
+ if (pkt->owner->owner)
+ ast_channel_unlock(pkt->owner->owner);
+ append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway.");
+ pkt->owner->needdestroy = 1;
+ }
+
+ /* Remove the packet */
+ for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
+ if (cur == pkt) {
+ UNLINK(cur, pkt->owner->packets, prev);
+ sip_pvt_unlock(pkt->owner);
+ ast_free(pkt);
+ return 0;
+ }
+ }
+ /* error case */
+ ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
+ sip_pvt_unlock(pkt->owner);
+ return 0;
+}
+
+/*! \brief Transmit packet with retransmits
+ \return 0 on success, -1 on failure to allocate packet
+*/
+static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len, int fatal, int sipmethod)
+{
+ struct sip_pkt *pkt = NULL;
+ int siptimer_a = DEFAULT_RETRANS;
+ int xmitres = 0;
+
+ /* If the transport is something reliable (TCP or TLS) then don't really send this reliably */
+ /* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
+ /* According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
+ if (!(p->socket.type & SIP_TRANSPORT_UDP)) {
+ xmitres = __sip_xmit(dialog_ref(p), data, len); /* Send packet */
+ if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
+ append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
+ return AST_FAILURE;
+ } else
+ return AST_SUCCESS;
+ }
+
+ if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1)))
+ return AST_FAILURE;
+ /* copy data, add a terminator and save length */
+ memcpy(pkt->data, data, len);
+ pkt->data[len] = '\0';
+ pkt->packetlen = len;
+ /* copy other parameters from the caller */
+ pkt->method = sipmethod;
+ pkt->seqno = seqno;
+ pkt->is_resp = resp;
+ pkt->is_fatal = fatal;
+ pkt->owner = dialog_ref(p);
+ pkt->next = p->packets;
+ p->packets = pkt;
+ pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */
+ if (pkt->timer_t1)
+ siptimer_a = pkt->timer_t1 * 2;
+
+ /* Schedule retransmission */
+ pkt->retransid = ast_sched_replace_variable(pkt->retransid, sched,
+ siptimer_a, retrans_pkt, pkt, 1);
+ if (sipdebug)
+ ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id #%d\n", pkt->retransid);
+ if (sipmethod == SIP_INVITE) {
+ /* Note this is a pending invite */
+ p->pendinginvite = seqno;
+ }
+
+ xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); /* Send packet */
+
+ if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
+ append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+ ast_sched_del(sched, pkt->retransid); /* No more retransmission */
+ pkt->retransid = -1;
+ return AST_FAILURE;
+ } else
+ return AST_SUCCESS;
+}
+
+/*! \brief Kill a SIP dialog (called only by the scheduler)
+ * The scheduler has a reference to this dialog when p->autokillid != -1,
+ * and we are called using that reference. So if the event is not
+ * rescheduled, we need to call dialog_unref().
+ */
+static int __sip_autodestruct(const void *data)
+{
+ struct sip_pvt *p = (struct sip_pvt *)data;
+
+ /* If this is a subscription, tell the phone that we got a timeout */
+ if (p->subscribed) {
+ transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE); /* Send last notification */
+ p->subscribed = NONE;
+ append_history(p, "Subscribestatus", "timeout");
+ ast_debug(3, "Re-scheduled destruction of SIP subscription %s\n", p->callid ? p->callid : "<unknown>");
+ return 10000; /* Reschedule this destruction so that we know that it's gone */
+ }
+
+ /* If there are packets still waiting for delivery, delay the destruction */
+ if (p->packets) {
+ ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
+ append_history(p, "ReliableXmit", "timeout");
+ return 10000;
+ }
+
+ if (p->subscribed == MWI_NOTIFICATION)
+ if (p->relatedpeer)
+ unref_peer(p->relatedpeer); /* Remove link to peer. If it's realtime, make sure it's gone from memory) */
+
+ /* Reset schedule ID */
+ p->autokillid = -1;
+
+ if (p->owner) {
+ ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
+ ast_queue_hangup(p->owner);
+ dialog_unref(p);
+ } else if (p->refer) {
+ ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
+ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
+ append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ dialog_unref(p);
+ } else {
+ append_history(p, "AutoDestroy", "%s", p->callid);
+ ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
+ sip_destroy(p); /* Go ahead and destroy dialog. All attempts to recover is done */
+ /* sip_destroy also absorbs the reference */
+ }
+ return 0;
+}
+
+/*! \brief Schedule destruction of SIP dialog */
+static void sip_scheddestroy(struct sip_pvt *p, int ms)
+{
+ if (ms < 0) {
+ if (p->timer_t1 == 0) {
+ p->timer_t1 = global_t1; /* Set timer T1 if not set (RFC 3261) */
+ p->timer_b = global_timer_b; /* Set timer B if not set (RFC 3261) */
+ }
+ ms = p->timer_t1 * 64;
+ }
+ if (sip_debug_test_pvt(p))
+ ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
+ sip_cancel_destroy(p);
+ if (p->do_history)
+ append_history(p, "SchedDestroy", "%d ms", ms);
+ p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p));
+
+ if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0)
+ stop_session_timer(p);
+}
+
+/*! \brief Cancel destruction of SIP dialog.
+ * Be careful as this also absorbs the reference - if you call it
+ * from within the scheduler, this might be the last reference.
+ */
+static void sip_cancel_destroy(struct sip_pvt *p)
+{
+ if (p->autokillid > -1) {
+ ast_sched_del(sched, p->autokillid);
+ append_history(p, "CancelDestroy", "");
+ p->autokillid = -1;
+ dialog_unref(p);
+ }
+}
+
+/*! \brief Acknowledges receipt of a packet and stops retransmission */
+static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
+{
+ struct sip_pkt *cur, *prev = NULL;
+ const char *msg = "Not Found"; /* used only for debugging */
+
+ sip_pvt_lock(p);
+
+ /* If we have an outbound proxy for this dialog, then delete it now since
+ the rest of the requests in this dialog needs to follow the routing.
+ If obforcing is set, we will keep the outbound proxy during the whole
+ dialog, regardless of what the SIP rfc says
+ */
+ if (p->outboundproxy && !p->outboundproxy->force)
+ p->outboundproxy = NULL;
+
+ for (cur = p->packets; cur; prev = cur, cur = cur->next) {
+ if (cur->seqno != seqno || cur->is_resp != resp)
+ continue;
+ if (cur->is_resp || cur->method == sipmethod) {
+ msg = "Found";
+ if (!resp && (seqno == p->pendinginvite)) {
+ ast_debug(1, "Acked pending invite %d\n", p->pendinginvite);
+ p->pendinginvite = 0;
+ }
+ if (cur->retransid > -1) {
+ if (sipdebug)
+ ast_debug(4, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
+ ast_sched_del(sched, cur->retransid);
+ cur->retransid = -1;
+ }
+ UNLINK(cur, p->packets, prev);
+ dialog_unref(cur->owner);
+ ast_free(cur);
+ break;
+ }
+ }
+ sip_pvt_unlock(p);
+ ast_debug(1, "Stopping retransmission on '%s' of %s %d: Match %s\n",
+ p->callid, resp ? "Response" : "Request", seqno, msg);
+}
+
+/*! \brief Pretend to ack all packets
+ * maybe the lock on p is not strictly necessary but there might be a race */
+static void __sip_pretend_ack(struct sip_pvt *p)
+{
+ struct sip_pkt *cur = NULL;
+
+ while (p->packets) {
+ int method;
+ if (cur == p->packets) {
+ ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text);
+ return;
+ }
+ cur = p->packets;
+ method = (cur->method) ? cur->method : find_sip_method(cur->data);
+ __sip_ack(p, cur->seqno, cur->is_resp, method);
+ }
+}
+
+/*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
+static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
+{
+ struct sip_pkt *cur;
+ int res = -1;
+
+ for (cur = p->packets; cur; cur = cur->next) {
+ if (cur->seqno == seqno && cur->is_resp == resp &&
+ (cur->is_resp || method_match(sipmethod, cur->data))) {
+ /* this is our baby */
+ if (cur->retransid > -1) {
+ if (sipdebug)
+ ast_debug(4, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text);
+ ast_sched_del(sched, cur->retransid);
+ cur->retransid = -1;
+ }
+ res = 0;
+ break;
+ }
+ }
+ ast_debug(1, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found");
+ return res;
+}
+
+
+/*! \brief Copy SIP request, parse it */
+static void parse_copy(struct sip_request *dst, const struct sip_request *src)
+{
+ memset(dst, 0, sizeof(*dst));
+ memcpy(dst->data, src->data, sizeof(dst->data));
+ dst->len = src->len;
+ parse_request(dst);
+}
+
+/*! \brief add a blank line if no body */
+static void add_blank(struct sip_request *req)
+{
+ if (!req->lines) {
+ /* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
+ ast_copy_string(req->data + req->len, "\r\n", sizeof(req->data) - req->len);
+ req->len += strlen(req->data + req->len);
+ }
+}
+
+/*! \brief Transmit response on SIP request*/
+static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
+{
+ int res;
+
+ add_blank(req);
+ if (sip_debug_test_pvt(p)) {
+ const struct sockaddr_in *dst = sip_real_dst(p);
+
+ ast_verbose("\n<--- %sTransmitting (%s) to %s:%d --->\n%s\n<------------>\n",
+ reliable ? "Reliably " : "", sip_nat_mode(p),
+ ast_inet_ntoa(dst->sin_addr),
+ ntohs(dst->sin_port), req->data);
+ }
+ if (p->do_history) {
+ struct sip_request tmp;
+ parse_copy(&tmp, req);
+ append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"),
+ (tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? tmp.rlPart2 : sip_methods[tmp.method].text);
+ }
+ res = (reliable) ?
+ __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
+ __sip_xmit(p, req->data, req->len);
+ if (res > 0)
+ return 0;
+ return res;
+}
+
+/*! \brief Send SIP Request to the other part of the dialogue */
+static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
+{
+ int res;
+
+ /* If we have an outbound proxy, reset peer address
+ Only do this once.
+ */
+ if (p->outboundproxy) {
+ p->sa = p->outboundproxy->ip;
+ }
+
+ add_blank(req);
+ if (sip_debug_test_pvt(p)) {
+ if (ast_test_flag(&p->flags[0], SIP_NAT_ROUTE))
+ ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port), req->data);
+ else
+ ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), req->data);
+ }
+ if (p->do_history) {
+ struct sip_request tmp;
+ parse_copy(&tmp, req);
+ append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
+ }
+ res = (reliable) ?
+ __sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
+ __sip_xmit(p, req->data, req->len);
+ return res;
+}
+
+/*! \brief Locate closing quote in a string, skipping escaped quotes.
+ * optionally with a limit on the search.
+ * start must be past the first quote.
+ */
+static const char *find_closing_quote(const char *start, const char *lim)
+{
+ char last_char = '\0';
+ const char *s;
+ for (s = start; *s && s != lim; last_char = *s++) {
+ if (*s == '"' && last_char != '\\')
+ break;
+ }
+ return s;
+}
+
+/*! \brief Pick out text in brackets from character string
+ \return pointer to terminated stripped string
+ \param tmp input string that will be modified
+ Examples:
+\verbatim
+ "foo" <bar> valid input, returns bar
+ foo returns the whole string
+ < "foo ... > returns the string between brackets
+ < "foo... bogus (missing closing bracket), returns the whole string
+ XXX maybe should still skip the opening bracket
+\endverbatim
+ */
+static char *get_in_brackets(char *tmp)
+{
+ const char *parse = tmp;
+ char *first_bracket;
+
+ /*
+ * Skip any quoted text until we find the part in brackets.
+ * On any error give up and return the full string.
+ */
+ while ( (first_bracket = strchr(parse, '<')) ) {
+ char *first_quote = strchr(parse, '"');
+
+ if (!first_quote || first_quote > first_bracket)
+ break; /* no need to look at quoted part */
+ /* the bracket is within quotes, so ignore it */
+ parse = find_closing_quote(first_quote + 1, NULL);
+ if (!*parse) { /* not found, return full string ? */
+ /* XXX or be robust and return in-bracket part ? */
+ ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
+ break;
+ }
+ parse++;
+ }
+ if (first_bracket) {
+ char *second_bracket = strchr(first_bracket + 1, '>');
+ if (second_bracket) {
+ *second_bracket = '\0';
+ tmp = first_bracket + 1;
+ } else {
+ ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
+ }
+ }
+
+ return tmp;
+}
+
+/*! \brief * parses a URI in its components.
+ *
+ * \note
+ * - If scheme is specified, drop it from the top.
+ * - If a component is not requested, do not split around it.
+ *
+ * This means that if we don't have domain, we cannot split
+ * name:pass and domain:port.
+ * It is safe to call with ret_name, pass, domain, port
+ * pointing all to the same place.
+ * Init pointers to empty string so we never get NULL dereferencing.
+ * Overwrites the string.
+ * return 0 on success, other values on error.
+ * \verbatim
+ * general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...]
+ * \endverbatim
+ */
+static int parse_uri(char *uri, char *scheme,
+ char **ret_name, char **pass, char **domain, char **port, char **options)
+{
+ char *name = NULL;
+ int error = 0;
+
+ /* init field as required */
+ if (pass)
+ *pass = "";
+ if (port)
+ *port = "";
+ if (scheme) {
+ int l = strlen(scheme);
+ if (!strncasecmp(uri, scheme, l))
+ uri += l;
+ else {
+ ast_debug(1, "Missing scheme '%s' in '%s'\n", scheme, uri);
+ error = -1;
+ }
+ }
+ if (!domain) {
+ /* if we don't want to split around domain, keep everything as a name,
+ * so we need to do nothing here, except remember why.
+ */
+ } else {
+ /* store the result in a temp. variable to avoid it being
+ * overwritten if arguments point to the same place.
+ */
+ char *c, *dom = "";
+
+ if ((c = strchr(uri, '@')) == NULL) {
+ /* domain-only URI, according to the SIP RFC. */
+ dom = uri;
+ name = "";
+ } else {
+ *c++ = '\0';
+ dom = c;
+ name = uri;
+ }
+
+ /* Remove options in domain and name */
+ dom = strsep(&dom, ";");
+ name = strsep(&name, ";");
+
+ if (port && (c = strchr(dom, ':'))) { /* Remove :port */
+ *c++ = '\0';
+ *port = c;
+ }
+ if (pass && (c = strchr(name, ':'))) { /* user:password */
+ *c++ = '\0';
+ *pass = c;
+ }
+ *domain = dom;
+ }
+ if (ret_name) /* same as for domain, store the result only at the end */
+ *ret_name = name;
+ if (options)
+ *options = uri ? uri : "";
+
+ return error;
+}
+
+/*! \brief Send message with Access-URL header, if this is an HTML URL only! */
+static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
+{
+ struct sip_pvt *p = chan->tech_pvt;
+
+ if (subclass != AST_HTML_URL)
+ return -1;
+
+ ast_string_field_build(p, url, "<%s>;mode=active", data);
+
+ if (sip_debug_test_pvt(p))
+ ast_debug(1, "Send URL %s, state = %d!\n", data, chan->_state);
+
+ switch (chan->_state) {
+ case AST_STATE_RING:
+ transmit_response(p, "100 Trying", &p->initreq);
+ break;
+ case AST_STATE_RINGING:
+ transmit_response(p, "180 Ringing", &p->initreq);
+ break;
+ case AST_STATE_UP:
+ if (!p->pendinginvite) { /* We are up, and have no outstanding invite */
+ transmit_reinvite_with_sdp(p, FALSE, FALSE);
+ } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to send URI when state is %d!\n", chan->_state);
+ }
+
+ return 0;
+}
+
+/*! \brief Send SIP MESSAGE text within a call
+ Called from PBX core sendtext() application */
+static int sip_sendtext(struct ast_channel *ast, const char *text)
+{
+ struct sip_pvt *p = ast->tech_pvt;
+ int debug = sip_debug_test_pvt(p);
+
+ if (debug)
+ ast_verbose("Sending text %s on %s\n", text, ast->name);
+ if (!p)
+ return -1;
+ if (ast_strlen_zero(text))
+ return 0;
+ if (debug)
+ ast_verbose("Really sending text %s on %s\n", text, ast->name);
+ transmit_message_with_text(p, text);
+ return 0;
+}
+
+/*! \brief Update peer object in realtime storage
+ If the Asterisk system name is set in asterisk.conf, we will use
+ that name and store that in the "regserver" field in the sippeers
+ table to facilitate multi-server setups.
+*/
+static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *defaultuser, const char *fullcontact, int expirey)
+{
+ char port[10];
+ char ipaddr[INET_ADDRSTRLEN];
+ char regseconds[20];
+ char *tablename = NULL;
+
+ const char *sysname = ast_config_AST_SYSTEM_NAME;
+ char *syslabel = NULL;
+
+ time_t nowtime = time(NULL) + expirey;
+ const char *fc = fullcontact ? "fullcontact" : NULL;
+
+ int realtimeregs = ast_check_realtime("sipregs");
+
+ tablename = realtimeregs ? "sipregs" : "sippeers";
+
+ snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime); /* Expiration time */
+ ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
+ snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
+
+ if (ast_strlen_zero(sysname)) /* No system name, disable this */
+ sysname = NULL;
+ else if (sip_cfg.rtsave_sysname)
+ syslabel = "regserver";
+
+ if (fc)
+ ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
+ "port", port, "regseconds", regseconds,
+ "defaultuser", defaultuser, fc, fullcontact, syslabel, sysname, NULL); /* note fc and syslabel _can_ be NULL */
+ else
+ ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
+ "port", port, "regseconds", regseconds,
+ "defaultuser", defaultuser, syslabel, sysname, NULL); /* note syslabel _can_ be NULL */
+}
+
+/*! \brief Automatically add peer extension to dial plan */
+static void register_peer_exten(struct sip_peer *peer, int onoff)
+{
+ char multi[256];
+ char *stringp, *ext, *context;
+
+ /* XXX note that global_regcontext is both a global 'enable' flag and
+ * the name of the global regexten context, if not specified
+ * individually.
+ */
+ if (ast_strlen_zero(global_regcontext))
+ return;
+
+ ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
+ stringp = multi;
+ while ((ext = strsep(&stringp, "&"))) {
+ if ((context = strchr(ext, '@'))) {
+ *context++ = '\0'; /* split ext@context */
+ if (!ast_context_find(context)) {
+ ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context);
+ continue;
+ }
+ } else {
+ context = global_regcontext;
+ }
+ if (onoff)
+ ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
+ ast_strdup(peer->name), ast_free_ptr, "SIP");
+ else
+ ast_context_remove_extension(context, ext, 1, NULL);
+ }
+}
+
+/*! Destroy mailbox subscriptions */
+static void destroy_mailbox(struct sip_mailbox *mailbox)
+{
+ if (mailbox->mailbox)
+ ast_free(mailbox->mailbox);
+ if (mailbox->context)
+ ast_free(mailbox->context);
+ if (mailbox->event_sub)
+ ast_event_unsubscribe(mailbox->event_sub);
+ ast_free(mailbox);
+}
+
+/*! Destroy all peer-related mailbox subscriptions */
+static void clear_peer_mailboxes(struct sip_peer *peer)
+{
+ struct sip_mailbox *mailbox;
+
+ while ((mailbox = AST_LIST_REMOVE_HEAD(&peer->mailboxes, entry)))
+ destroy_mailbox(mailbox);
+}
+
+/*! \brief Destroy peer object from memory */
+static void sip_destroy_peer(struct sip_peer *peer)
+{
+ ast_debug(3, "Destroying SIP peer %s\n", peer->name);
+
+ if (peer->outboundproxy)
+ ast_free(peer->outboundproxy);
+ peer->outboundproxy = NULL;
+
+ /* Delete it, it needs to disappear */
+ if (peer->call)
+ peer->call = sip_destroy(peer->call);
+
+ if (peer->mwipvt) /* We have an active subscription, delete it */
+ peer->mwipvt = sip_destroy(peer->mwipvt);
+
+ if (peer->chanvars) {
+ ast_variables_destroy(peer->chanvars);
+ peer->chanvars = NULL;
+ }
+
+ /* If the schedule delete fails, that means the schedule is currently
+ * running, which means we should wait for that thread to complete.
+ * Otherwise, there's a crashable race condition.
+ *
+ * NOTE: once peer is refcounted, this probably is no longer necessary.
+ */
+ while (peer->expire > -1 && ast_sched_del(sched, peer->expire))
+ usleep(1);
+ while (peer->pokeexpire > -1 && ast_sched_del(sched, peer->pokeexpire))
+ usleep(1);
+
+ register_peer_exten(peer, FALSE);
+ ast_free_ha(peer->ha);
+ if (peer->selfdestruct)
+ apeerobjs--;
+ else if (peer->is_realtime) {
+ rpeerobjs--;
+ ast_debug(3,"-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
+ } else
+ speerobjs--;
+ clear_realm_authentication(peer->auth);
+ peer->auth = NULL;
+ if (peer->dnsmgr)
+ ast_dnsmgr_release(peer->dnsmgr);
+ clear_peer_mailboxes(peer);
+ ast_free(peer);
+}
+
+/*! \brief Update peer data in database (if used) */
+static void update_peer(struct sip_peer *p, int expiry)
+{
+ int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
+ if (sip_cfg.peer_rtupdate &&
+ (p->is_realtime || rtcachefriends)) {
+ realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, expiry);
+ }
+}
+
+static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config)
+{
+ struct ast_variable *var = NULL;
+ struct ast_flags flags = {0};
+ char *cat = NULL;
+ const char *insecure;
+ while ((cat = ast_category_browse(config, cat))) {
+ insecure = ast_variable_retrieve(config, cat, "insecure");
+ set_insecure_flags(&flags, insecure, -1);
+ if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
+ var = ast_category_root(config, cat);
+ break;
+ }
+ }
+ return var;
+}
+
+static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername)
+{
+ struct ast_variable *tmp;
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!newpeername && !strcasecmp(tmp->name, "name"))
+ newpeername = tmp->value;
+ }
+ return newpeername;
+}
+
+/*! \brief realtime_peer: Get peer from realtime storage
+ * Checks the "sippeers" realtime family from extconfig.conf
+ * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
+*/
+static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin)
+{
+ struct sip_peer *peer;
+ struct ast_variable *var = NULL;
+ struct ast_variable *varregs = NULL;
+ struct ast_variable *tmp;
+ struct ast_config *peerlist = NULL;
+ char ipaddr[INET_ADDRSTRLEN];
+ char portstring[6]; /*up to 5 digits plus null terminator*/
+ char *cat = NULL;
+ unsigned short portnum;
+ int realtimeregs = ast_check_realtime("sipregs");
+
+ /* First check on peer name */
+ if (newpeername) {
+ if (realtimeregs)
+ varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
+
+ var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", NULL);
+ if (!var && sin)
+ var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), NULL);
+ if (!var) {
+ var = ast_load_realtime("sippeers", "name", newpeername, NULL);
+ /*!\note
+ * If this one loaded something, then we need to ensure that the host
+ * field matched. The only reason why we can't have this as a criteria
+ * is because we only have the IP address and the host field might be
+ * set as a name (and the reverse PTR might not match).
+ */
+ if (var) {
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(var->name, "host")) {
+ struct in_addr sin2 = { 0, };
+ struct ast_dnsmgr_entry *dnsmgr = NULL;
+ if ((ast_dnsmgr_lookup(tmp->value, &sin2, &dnsmgr) < 0) || (memcmp(&sin2, &sin->sin_addr, sizeof(sin2)) != 0)) {
+ /* No match */
+ ast_variables_destroy(var);
+ var = NULL;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!var && sin) { /* Then check on IP address for dynamic peers */
+ ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
+ portnum = ntohs(sin->sin_port);
+ sprintf(portstring, "%u", portnum);
+ var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, NULL); /* First check for fixed IP hosts */
+ if (var) {
+ if (realtimeregs) {
+ newpeername = get_name_from_variable(var, newpeername);
+ varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
+ }
+ } else {
+ if (realtimeregs)
+ varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, NULL); /* Then check for registered hosts */
+ else
+ var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, NULL); /* Then check for registered hosts */
+ if (varregs) {
+ newpeername = get_name_from_variable(varregs, newpeername);
+ var = ast_load_realtime("sippeers", "name", newpeername, NULL);
+ }
+ }
+ if (!var) { /*We couldn't match on ipaddress and port, so we need to check if port is insecure*/
+ peerlist = ast_load_realtime_multientry("sippeers", "host", ipaddr, NULL);
+ if (peerlist) {
+ var = get_insecure_variable_from_config(peerlist);
+ if(var) {
+ if (realtimeregs) {
+ newpeername = get_name_from_variable(var, newpeername);
+ varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
+ }
+ } else { /*var wasn't found in the list of "hosts", so try "ipaddr"*/
+ peerlist = NULL;
+ cat = NULL;
+ peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, NULL);
+ if(peerlist) {
+ var = get_insecure_variable_from_config(peerlist);
+ if(var) {
+ if (realtimeregs) {
+ newpeername = get_name_from_variable(var, newpeername);
+ varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
+ }
+ }
+ }
+ }
+ } else {
+ if (realtimeregs) {
+ peerlist = ast_load_realtime_multientry("sipregs", "ipaddr", ipaddr, NULL);
+ if (peerlist) {
+ varregs = get_insecure_variable_from_config(peerlist);
+ if (varregs) {
+ newpeername = get_name_from_variable(varregs, newpeername);
+ var = ast_load_realtime("sippeers", "name", newpeername, NULL);
+ }
+ }
+ } else {
+ peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, NULL);
+ if (peerlist) {
+ var = get_insecure_variable_from_config(peerlist);
+ if (var) {
+ newpeername = get_name_from_variable(var, newpeername);
+ varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!var) {
+ if (peerlist)
+ ast_config_destroy(peerlist);
+ return NULL;
+ }
+
+ for (tmp = var; tmp; tmp = tmp->next) {
+ /* If this is type=user, then skip this object. */
+ if (!strcasecmp(tmp->name, "type") &&
+ !strcasecmp(tmp->value, "user")) {
+ if(peerlist)
+ ast_config_destroy(peerlist);
+ else {
+ ast_variables_destroy(var);
+ ast_variables_destroy(varregs);
+ }
+ return NULL;
+ } else if (!newpeername && !strcasecmp(tmp->name, "name")) {
+ newpeername = tmp->value;
+ }
+ }
+
+ if (!newpeername) { /* Did not find peer in realtime */
+ ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", ipaddr);
+ if(peerlist)
+ ast_config_destroy(peerlist);
+ else
+ ast_variables_destroy(var);
+ return NULL;
+ }
+
+
+ /* Peer found in realtime, now build it in memory */
+ peer = build_peer(newpeername, var, varregs, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
+ if (!peer) {
+ if(peerlist)
+ ast_config_destroy(peerlist);
+ else {
+ ast_variables_destroy(var);
+ ast_variables_destroy(varregs);
+ }
+ return NULL;
+ }
+
+ ast_debug(3,"-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
+
+ if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ /* Cache peer */
+ ast_copy_flags(&peer->flags[1],&global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
+ if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
+ peer->expire = ast_sched_replace(peer->expire, sched,
+ global_rtautoclear * 1000, expire_register, (void *) peer);
+ }
+ ASTOBJ_CONTAINER_LINK(&peerl,peer);
+ } else {
+ peer->is_realtime = 1;
+ }
+ if (peerlist)
+ ast_config_destroy(peerlist);
+ else {
+ ast_variables_destroy(var);
+ ast_variables_destroy(varregs);
+ }
+
+ return peer;
+}
+
+/*! \brief Support routine for find_peer */
+static int sip_addrcmp(char *name, struct sockaddr_in *sin)
+{
+ /* We know name is the first field, so we can cast */
+ struct sip_peer *p = (struct sip_peer *) name;
+ return !(!inaddrcmp(&p->addr, sin) ||
+ (ast_test_flag(&p->flags[0], SIP_INSECURE_PORT) &&
+ (p->addr.sin_addr.s_addr == sin->sin_addr.s_addr)));
+}
+
+/*! \brief Locate peer by name or ip address
+ * This is used on incoming SIP message to find matching peer on ip
+ or outgoing message to find matching peer on name
+ \note Avoid using this function in new functions if there's a way to avoid it, i
+ since it causes a database lookup or a traversal of the in-memory peer list.
+*/
+static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime)
+{
+ struct sip_peer *p = NULL;
+
+ if (peer)
+ p = ASTOBJ_CONTAINER_FIND(&peerl, peer);
+ else
+ p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp);
+
+ if (!p && realtime)
+ p = realtime_peer(peer, sin);
+
+ return p;
+}
+
+/*! \brief Remove user object from in-memory storage */
+static void sip_destroy_user(struct sip_user *user)
+{
+ ast_debug(3, "Destroying user object from memory: %s\n", user->name);
+
+ ast_free_ha(user->ha);
+ if (user->chanvars) {
+ ast_variables_destroy(user->chanvars);
+ user->chanvars = NULL;
+ }
+ if (user->is_realtime)
+ ruserobjs--;
+ else
+ suserobjs--;
+ ast_free(user);
+}
+
+/*! \brief Load user from realtime storage
+ * Loads user from "sipusers" category in realtime (extconfig.conf)
+ * Users are matched on From: user name (the domain in skipped) */
+static struct sip_user *realtime_user(const char *username)
+{
+ struct ast_variable *var;
+ struct ast_variable *tmp;
+ struct sip_user *user = NULL;
+
+ var = ast_load_realtime("sipusers", "name", username, NULL);
+
+ if (!var)
+ return NULL;
+
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "type") &&
+ !strcasecmp(tmp->value, "peer")) {
+ ast_variables_destroy(var);
+ return NULL;
+ }
+ }
+
+ user = build_user(username, var, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
+
+ if (!user) { /* No user found */
+ ast_variables_destroy(var);
+ return NULL;
+ }
+
+ if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ ast_set_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
+ suserobjs++;
+ ASTOBJ_CONTAINER_LINK(&userl,user);
+ } else {
+ /* Move counter from s to r... */
+ suserobjs--;
+ ruserobjs++;
+ user->is_realtime = 1;
+ }
+ ast_variables_destroy(var);
+ return user;
+}
+
+/*! \brief Locate user by name
+ * Locates user by name (From: sip uri user name part) first
+ * from in-memory list (static configuration) then from
+ * realtime storage (defined in extconfig.conf) */
+static struct sip_user *find_user(const char *name, int realtime)
+{
+ struct sip_user *u = ASTOBJ_CONTAINER_FIND(&userl, name);
+ if (!u && realtime)
+ u = realtime_user(name);
+ return u;
+}
+
+/*! \brief Set nat mode on the various data sockets */
+static void do_setnat(struct sip_pvt *p, int natflags)
+{
+ const char *mode = natflags ? "On" : "Off";
+
+ if (p->rtp) {
+ ast_debug(1, "Setting NAT on RTP to %s\n", mode);
+ ast_rtp_setnat(p->rtp, natflags);
+ }
+ if (p->vrtp) {
+ ast_debug(1, "Setting NAT on VRTP to %s\n", mode);
+ ast_rtp_setnat(p->vrtp, natflags);
+ }
+ if (p->udptl) {
+ ast_debug(1, "Setting NAT on UDPTL to %s\n", mode);
+ ast_udptl_setnat(p->udptl, natflags);
+ }
+ if (p->trtp) {
+ ast_debug(1, "Setting NAT on TRTP to %s\n", mode);
+ ast_rtp_setnat(p->trtp, natflags);
+ }
+}
+
+/*! \brief Set the global T38 capabilities on a SIP dialog structure */
+static void set_t38_capabilities(struct sip_pvt *p)
+{
+ p->t38.capability = global_t38_capability;
+ if (p->udptl) {
+ if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC )
+ p->t38.capability |= T38FAX_UDP_EC_FEC;
+ else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
+ p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
+ else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE )
+ p->t38.capability |= T38FAX_UDP_EC_NONE;
+ p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
+ }
+}
+
+/*! \brief Create address structure from peer reference.
+ * This function copies data from peer to the dialog, so we don't have to look up the peer
+ * again from memory or database during the life time of the dialog.
+ *
+ * \return -1 on error, 0 on success.
+ */
+static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
+{
+ dialog->socket = peer->socket;
+
+ if ((peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr) &&
+ (!peer->maxms || ((peer->lastms >= 0) && (peer->lastms <= peer->maxms)))) {
+ dialog->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr;
+ dialog->recv = dialog->sa;
+ } else
+ return -1;
+
+ ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+ dialog->capability = peer->capability;
+ if ((!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && dialog->vrtp) {
+ ast_rtp_destroy(dialog->vrtp);
+ dialog->vrtp = NULL;
+ }
+ if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT) && dialog->trtp) {
+ ast_rtp_destroy(dialog->trtp);
+ dialog->trtp = NULL;
+ }
+ dialog->prefs = peer->prefs;
+ if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) {
+ ast_copy_flags(&dialog->t38.t38support, &peer->flags[1], SIP_PAGE2_T38SUPPORT);
+ set_t38_capabilities(dialog);
+ dialog->t38.jointcapability = dialog->t38.capability;
+ } else if (dialog->udptl) {
+ ast_udptl_destroy(dialog->udptl);
+ dialog->udptl = NULL;
+ }
+ do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
+
+ if (dialog->rtp) { /* Audio */
+ ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
+ ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+ ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout);
+ ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout);
+ ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive);
+ /* Set Frame packetization */
+ ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs);
+ dialog->autoframing = peer->autoframing;
+ }
+ if (dialog->vrtp) { /* Video */
+ ast_rtp_setdtmf(dialog->vrtp, 0);
+ ast_rtp_setdtmfcompensate(dialog->vrtp, 0);
+ ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout);
+ ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout);
+ ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive);
+ }
+ if (dialog->trtp) { /* Realtime text */
+ ast_rtp_setdtmf(dialog->trtp, 0);
+ ast_rtp_setdtmfcompensate(dialog->trtp, 0);
+ ast_rtp_set_rtptimeout(dialog->trtp, peer->rtptimeout);
+ ast_rtp_set_rtpholdtimeout(dialog->trtp, peer->rtpholdtimeout);
+ ast_rtp_set_rtpkeepalive(dialog->trtp, peer->rtpkeepalive);
+ }
+
+ ast_string_field_set(dialog, peername, peer->name);
+ ast_string_field_set(dialog, authname, peer->username);
+ ast_string_field_set(dialog, username, peer->username);
+ ast_string_field_set(dialog, peersecret, peer->secret);
+ ast_string_field_set(dialog, peermd5secret, peer->md5secret);
+ ast_string_field_set(dialog, mohsuggest, peer->mohsuggest);
+ ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
+ ast_string_field_set(dialog, tohost, peer->tohost);
+ ast_string_field_set(dialog, fullcontact, peer->fullcontact);
+ ast_string_field_set(dialog, context, peer->context);
+ dialog->outboundproxy = obproxy_get(dialog, peer);
+ dialog->callgroup = peer->callgroup;
+ dialog->pickupgroup = peer->pickupgroup;
+ dialog->allowtransfer = peer->allowtransfer;
+ dialog->jointnoncodeccapability = dialog->noncodeccapability;
+ dialog->rtptimeout = peer->rtptimeout;
+ dialog->maxcallbitrate = peer->maxcallbitrate;
+ if (ast_strlen_zero(dialog->tohost))
+ ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr));
+ if (!ast_strlen_zero(peer->fromdomain)) {
+ ast_string_field_set(dialog, fromdomain, peer->fromdomain);
+ if (!dialog->initreq.headers) {
+ char *c;
+ char *tmpcall = ast_strdupa(dialog->callid);
+
+ c = strchr(tmpcall, '@');
+ if (c) {
+ *c = '\0';
+ ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
+ }
+ }
+ }
+ if (!ast_strlen_zero(peer->fromuser))
+ ast_string_field_set(dialog, fromuser, peer->fromuser);
+ if (!ast_strlen_zero(peer->language))
+ ast_string_field_set(dialog, language, peer->language);
+
+ /* Set timer T1 to RTT for this peer (if known by qualify=) */
+ /* Minimum is settable or default to 100 ms */
+ /* If there is a maxms and lastms from a qualify use that over a manual T1
+ value. Otherwise, use the peer's T1 value. */
+ if (peer->maxms && peer->lastms)
+ dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
+ else
+ dialog->timer_t1 = peer->timer_t1;
+
+ /* Set timer B to control transaction timeouts, the peer setting is the default and overrides
+ the known timer */
+ if (peer->timer_b)
+ dialog->timer_b = peer->timer_b;
+ else
+ dialog->timer_b = 64 * dialog->timer_t1;
+
+ if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
+ (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
+ dialog->noncodeccapability |= AST_RTP_DTMF;
+ else
+ dialog->noncodeccapability &= ~AST_RTP_DTMF;
+ if (peer->call_limit)
+ ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
+
+ return 0;
+}
+
+/*! \brief create address structure from peer name
+ * Or, if peer not found, find it in the global DNS
+ * returns TRUE (-1) on failure, FALSE on success */
+static int create_addr(struct sip_pvt *dialog, const char *opeer)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct sip_peer *peer;
+ char *port;
+ int portno;
+ char host[MAXHOSTNAMELEN], *hostn;
+ char peername[256];
+
+ ast_copy_string(peername, opeer, sizeof(peername));
+ port = strchr(peername, ':');
+ if (port)
+ *port++ = '\0';
+ dialog->sa.sin_family = AF_INET;
+ dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
+ dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
+ peer = find_peer(peername, NULL, 1);
+
+ if (peer) {
+ int res = create_addr_from_peer(dialog, peer);
+ unref_peer(peer);
+ return res;
+ }
+
+ ast_string_field_set(dialog, tohost, peername);
+
+ /* Get the outbound proxy information */
+ dialog->outboundproxy = obproxy_get(dialog, NULL);
+
+ /* If we have an outbound proxy, don't bother with DNS resolution at all */
+ if (dialog->outboundproxy)
+ return 0;
+
+ /* Let's see if we can find the host in DNS. First try DNS SRV records,
+ then hostname lookup */
+
+ hostn = peername;
+ portno = port ? atoi(port) : (dialog->socket.type & SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT;
+ if (global_srvlookup) {
+ char service[MAXHOSTNAMELEN];
+ int tportno;
+ int ret;
+
+ snprintf(service, sizeof(service), "_sip._%s.%s", get_transport(dialog->socket.type), peername);
+ ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service);
+ if (ret > 0) {
+ hostn = host;
+ portno = tportno;
+ }
+ }
+ hp = ast_gethostbyname(hostn, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "No such host: %s\n", peername);
+ return -1;
+ }
+ memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr));
+ dialog->sa.sin_port = htons(portno);
+ dialog->recv = dialog->sa;
+ return 0;
+}
+
+/*! \brief Scheduled congestion on a call.
+ * Only called by the scheduler, must return the reference when done.
+ */
+static int auto_congest(const void *arg)
+{
+ struct sip_pvt *p = (struct sip_pvt *)arg;
+
+ sip_pvt_lock(p);
+ p->initid = -1; /* event gone, will not be rescheduled */
+ if (p->owner) {
+ /* XXX fails on possible deadlock */
+ if (!ast_channel_trylock(p->owner)) {
+ ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
+ append_history(p, "Cong", "Auto-congesting (timer)");
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ ast_channel_unlock(p->owner);
+ }
+ }
+ sip_pvt_unlock(p);
+ dialog_unref(p);
+ return 0;
+}
+
+
+/*! \brief Initiate SIP call from PBX
+ * used from the dial() application */
+static int sip_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ int res;
+ struct sip_pvt *p = ast->tech_pvt; /* chan is locked, so the reference cannot go away */
+ struct varshead *headp;
+ struct ast_var_t *current;
+ const char *referer = NULL; /* SIP referrer */
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+
+ /* Check whether there is vxml_url, distinctive ring variables */
+ headp=&ast->varshead;
+ AST_LIST_TRAVERSE(headp,current,entries) {
+ /* Check whether there is a VXML_URL variable */
+ if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) {
+ p->options->vxml_url = ast_var_value(current);
+ } else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
+ p->options->uri_options = ast_var_value(current);
+ } else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
+ /* Check whether there is a variable with a name starting with SIPADDHEADER */
+ p->options->addsipheaders = 1;
+ } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) {
+ /* This is a transfered call */
+ p->options->transfer = 1;
+ } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) {
+ /* This is the referrer */
+ referer = ast_var_value(current);
+ } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
+ /* We're replacing a call. */
+ p->options->replaces = ast_var_value(current);
+ } else if (!strcasecmp(ast_var_name(current), "T38CALL")) {
+ p->t38.state = T38_LOCAL_DIRECT;
+ ast_debug(1,"T38State change to %d on channel %s\n", p->t38.state, ast->name);
+ }
+
+ }
+
+ res = 0;
+ ast_set_flag(&p->flags[0], SIP_OUTGOING);
+
+ if (p->options->transfer) {
+ char buf[BUFSIZ/2];
+
+ if (referer) {
+ if (sipdebug)
+ ast_debug(3, "Call for %s transfered by %s\n", p->username, referer);
+ snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
+ } else
+ snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
+ ast_string_field_set(p, cid_name, buf);
+ }
+ ast_debug(1, "Outgoing Call for %s\n", p->username);
+
+ res = update_call_counter(p, INC_CALL_RINGING);
+
+ if (res == -1)
+ return res;
+
+ p->callingpres = ast->cid.cid_pres;
+ p->jointcapability = ast_translate_available_formats(p->capability, p->prefcodec);
+ p->jointnoncodeccapability = p->noncodeccapability;
+
+ /* If there are no audio formats left to offer, punt */
+ if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
+ ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username);
+ res = -1;
+ } else {
+ int xmitres;
+
+ p->t38.jointcapability = p->t38.capability;
+ ast_debug(2,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability);
+
+ xmitres = transmit_invite(p, SIP_INVITE, 1, 2);
+ if (xmitres == XMIT_ERROR)
+ return -1;
+ p->invitestate = INV_CALLING;
+
+ /* Initialize auto-congest time */
+ p->initid = ast_sched_replace(p->initid, sched, p->timer_b,
+ auto_congest, dialog_ref(p));
+ }
+
+ return res;
+}
+
+/*! \brief Destroy registry object
+ Objects created with the register= statement in static configuration */
+static void sip_registry_destroy(struct sip_registry *reg)
+{
+ /* Really delete */
+ ast_debug(3, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname);
+
+ if (reg->call) {
+ /* Clear registry before destroying to ensure
+ we don't get reentered trying to grab the registry lock */
+ reg->call->registry = NULL;
+ ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
+ reg->call = sip_destroy(reg->call);
+ }
+ if (reg->expire > -1)
+ ast_sched_del(sched, reg->expire);
+ if (reg->timeout > -1)
+ ast_sched_del(sched, reg->timeout);
+ ast_string_field_free_memory(reg);
+ regobjs--;
+ ast_free(reg);
+
+}
+
+/*! \brief Execute destruction of SIP dialog structure, release memory */
+static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
+{
+ struct sip_pvt *cur, *prev = NULL;
+ struct sip_pkt *cp;
+
+ if (sip_debug_test_pvt(p))
+ ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
+
+ if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
+ update_call_counter(p, DEC_CALL_LIMIT);
+ ast_debug(2, "This call did not properly clean up call limits. Call ID %s\n", p->callid);
+ }
+
+ /* Remove link from peer to subscription of MWI */
+ if (p->relatedpeer && p->relatedpeer->mwipvt)
+ p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt);
+
+ if (dumphistory)
+ sip_dump_history(p);
+
+ if (p->options)
+ ast_free(p->options);
+
+ if (p->stateid > -1)
+ ast_extension_state_del(p->stateid, NULL);
+ if (p->initid > -1)
+ ast_sched_del(sched, p->initid);
+ if (p->waitid > -1)
+ ast_sched_del(sched, p->waitid);
+ if (p->autokillid > -1)
+ ast_sched_del(sched, p->autokillid);
+
+ if (p->rtp)
+ ast_rtp_destroy(p->rtp);
+ if (p->vrtp)
+ ast_rtp_destroy(p->vrtp);
+ if (p->trtp)
+ ast_rtp_destroy(p->trtp);
+ if (p->udptl)
+ ast_udptl_destroy(p->udptl);
+ if (p->refer)
+ ast_free(p->refer);
+ if (p->route) {
+ free_old_route(p->route);
+ p->route = NULL;
+ }
+ if (p->registry) {
+ if (p->registry->call == p)
+ p->registry->call = NULL;
+ p->registry = registry_unref(p->registry);
+ }
+
+ /* Destroy Session-Timers if allocated */
+ if (p->stimer) {
+ if (p->stimer->st_active == TRUE && p->stimer->st_schedid > -1)
+ ast_sched_del(sched, p->stimer->st_schedid);
+ ast_free(p->stimer);
+ }
+
+ /* Unlink us from the owner if we have one */
+ if (p->owner) {
+ if (lockowner)
+ ast_channel_lock(p->owner);
+ ast_debug(1, "Detaching from %s\n", p->owner->name);
+ p->owner->tech_pvt = NULL;
+ if (lockowner)
+ ast_channel_unlock(p->owner);
+ }
+ /* Clear history */
+ if (p->history) {
+ struct sip_history *hist;
+ while ( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) ) {
+ ast_free(hist);
+ p->history_entries--;
+ }
+ ast_free(p->history);
+ p->history = NULL;
+ }
+
+ /* Lock dialog list before removing ourselves from the list */
+ if (lockdialoglist)
+ dialoglist_lock();
+ for (prev = NULL, cur = dialoglist; cur; prev = cur, cur = cur->next) {
+ if (cur == p) {
+ UNLINK(cur, dialoglist, prev);
+ break;
+ }
+ }
+ if (lockdialoglist)
+ dialoglist_unlock();
+ if (!cur) {
+ ast_log(LOG_WARNING, "Trying to destroy \"%s\", not found in dialog list?!?! \n", p->callid);
+ return;
+ }
+
+ /* remove all current packets in this dialog */
+ while((cp = p->packets)) {
+ p->packets = p->packets->next;
+ if (cp->retransid > -1)
+ ast_sched_del(sched, cp->retransid);
+ dialog_unref(cp->owner);
+ ast_free(cp);
+ }
+ if (p->chanvars) {
+ ast_variables_destroy(p->chanvars);
+ p->chanvars = NULL;
+ }
+ ast_mutex_destroy(&p->pvt_lock);
+
+ ast_string_field_free_memory(p);
+
+ ast_free(p);
+}
+
+/*! \brief update_call_counter: Handle call_limit for SIP users
+ * Setting a call-limit will cause calls above the limit not to be accepted.
+ *
+ * Remember that for a type=friend, there's one limit for the user and
+ * another for the peer, not a combined call limit.
+ * This will cause unexpected behaviour in subscriptions, since a "friend"
+ * is *two* devices in Asterisk, not one.
+ *
+ * Thought: For realtime, we should probably update storage with inuse counter...
+ *
+ * \return 0 if call is ok (no call limit, below threshold)
+ * -1 on rejection of call
+ *
+ */
+static int update_call_counter(struct sip_pvt *fup, int event)
+{
+ char name[256];
+ int *inuse = NULL, *call_limit = NULL, *inringing = NULL;
+ int outgoing = fup->outgoing_call;
+ struct sip_user *u = NULL;
+ struct sip_peer *p = NULL;
+
+ ast_debug(3, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming");
+
+
+ /* Test if we need to check call limits, in order to avoid
+ realtime lookups if we do not need it */
+ if (!ast_test_flag(&fup->flags[0], SIP_CALL_LIMIT) && !ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD))
+ return 0;
+
+ ast_copy_string(name, fup->username, sizeof(name));
+
+ /* Check the list of users only for incoming calls */
+ if (global_limitonpeers == FALSE && !outgoing && (u = find_user(name, 1))) {
+ inuse = &u->inUse;
+ call_limit = &u->call_limit;
+ inringing = NULL;
+ } else if ( (p = find_peer(ast_strlen_zero(fup->peername) ? name : fup->peername, NULL, 1) ) ) { /* Try to find peer */
+ inuse = &p->inUse;
+ call_limit = &p->call_limit;
+ inringing = &p->inRinging;
+ ast_copy_string(name, fup->peername, sizeof(name));
+ }
+ if (!p && !u) {
+ ast_debug(2, "%s is not a local device, no call limit\n", name);
+ return 0;
+ }
+
+ switch(event) {
+ /* incoming and outgoing affects the inUse counter */
+ case DEC_CALL_LIMIT:
+ /* Decrement inuse count if applicable */
+ if (inuse && ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
+ ast_atomic_fetchadd_int(inuse, -1);
+ ast_clear_flag(&fup->flags[0], SIP_INC_COUNT);
+ } else
+ *inuse = 0;
+ /* Decrement ringing count if applicable */
+ if (inringing && ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
+ ast_atomic_fetchadd_int(inringing, -1);
+ ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
+ }
+ /* Decrement onhold count if applicable */
+ if (ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD) && global_notifyhold) {
+ ast_clear_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD);
+ sip_peer_hold(fup, FALSE);
+ }
+ if (sipdebug)
+ ast_debug(2, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
+ break;
+
+ case INC_CALL_RINGING:
+ case INC_CALL_LIMIT:
+ /* If call limit is active and we have reached the limit, reject the call */
+ if (*call_limit > 0 ) {
+ if (*inuse >= *call_limit) {
+ ast_log(LOG_ERROR, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
+ if (u)
+ unref_user(u);
+ else
+ unref_peer(p);
+ return -1;
+ }
+ }
+ if (inringing && (event == INC_CALL_RINGING)) {
+ if (!ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
+ ast_atomic_fetchadd_int(inringing, +1);
+ ast_set_flag(&fup->flags[0], SIP_INC_RINGING);
+ }
+ }
+ /* Continue */
+ ast_atomic_fetchadd_int(inuse, +1);
+ ast_set_flag(&fup->flags[0], SIP_INC_COUNT);
+ if (sipdebug) {
+ ast_debug(2, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *inuse, *call_limit);
+ }
+ break;
+
+ case DEC_CALL_RINGING:
+ if (inringing && ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
+ ast_atomic_fetchadd_int(inringing, -1);
+ ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
+ }
+ break;
+
+ default:
+ ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
+ }
+ if (p) {
+ ast_device_state_changed("SIP/%s", p->name);
+ unref_peer(p);
+ } else /* u must be set */
+ unref_user(u);
+ return 0;
+}
+
+/*! \brief Destroy SIP call structure.
+ * Make it return NULL so the caller can do things like
+ * foo = sip_destroy(foo);
+ * and reduce the chance of bugs due to dangling pointers.
+ */
+static struct sip_pvt * sip_destroy(struct sip_pvt *p)
+{
+ ast_debug(3, "Destroying SIP dialog %s\n", p->callid);
+ __sip_destroy(p, TRUE, TRUE);
+ return NULL;
+}
+
+/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
+static int hangup_sip2cause(int cause)
+{
+ /* Possible values taken from causes.h */
+
+ switch(cause) {
+ case 401: /* Unauthorized */
+ return AST_CAUSE_CALL_REJECTED;
+ case 403: /* Not found */
+ return AST_CAUSE_CALL_REJECTED;
+ case 404: /* Not found */
+ return AST_CAUSE_UNALLOCATED;
+ case 405: /* Method not allowed */
+ return AST_CAUSE_INTERWORKING;
+ case 407: /* Proxy authentication required */
+ return AST_CAUSE_CALL_REJECTED;
+ case 408: /* No reaction */
+ return AST_CAUSE_NO_USER_RESPONSE;
+ case 409: /* Conflict */
+ return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
+ case 410: /* Gone */
+ return AST_CAUSE_UNALLOCATED;
+ case 411: /* Length required */
+ return AST_CAUSE_INTERWORKING;
+ case 413: /* Request entity too large */
+ return AST_CAUSE_INTERWORKING;
+ case 414: /* Request URI too large */
+ return AST_CAUSE_INTERWORKING;
+ case 415: /* Unsupported media type */
+ return AST_CAUSE_INTERWORKING;
+ case 420: /* Bad extension */
+ return AST_CAUSE_NO_ROUTE_DESTINATION;
+ case 480: /* No answer */
+ return AST_CAUSE_NO_ANSWER;
+ case 481: /* No answer */
+ return AST_CAUSE_INTERWORKING;
+ case 482: /* Loop detected */
+ return AST_CAUSE_INTERWORKING;
+ case 483: /* Too many hops */
+ return AST_CAUSE_NO_ANSWER;
+ case 484: /* Address incomplete */
+ return AST_CAUSE_INVALID_NUMBER_FORMAT;
+ case 485: /* Ambiguous */
+ return AST_CAUSE_UNALLOCATED;
+ case 486: /* Busy everywhere */
+ return AST_CAUSE_BUSY;
+ case 487: /* Request terminated */
+ return AST_CAUSE_INTERWORKING;
+ case 488: /* No codecs approved */
+ return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+ case 491: /* Request pending */
+ return AST_CAUSE_INTERWORKING;
+ case 493: /* Undecipherable */
+ return AST_CAUSE_INTERWORKING;
+ case 500: /* Server internal failure */
+ return AST_CAUSE_FAILURE;
+ case 501: /* Call rejected */
+ return AST_CAUSE_FACILITY_REJECTED;
+ case 502:
+ return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
+ case 503: /* Service unavailable */
+ return AST_CAUSE_CONGESTION;
+ case 504: /* Gateway timeout */
+ return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
+ case 505: /* SIP version not supported */
+ return AST_CAUSE_INTERWORKING;
+ case 600: /* Busy everywhere */
+ return AST_CAUSE_USER_BUSY;
+ case 603: /* Decline */
+ return AST_CAUSE_CALL_REJECTED;
+ case 604: /* Does not exist anywhere */
+ return AST_CAUSE_UNALLOCATED;
+ case 606: /* Not acceptable */
+ return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+ default:
+ return AST_CAUSE_NORMAL;
+ }
+ /* Never reached */
+ return 0;
+}
+
+/*! \brief Convert Asterisk hangup causes to SIP codes
+\verbatim
+ Possible values from causes.h
+ AST_CAUSE_NOTDEFINED AST_CAUSE_NORMAL AST_CAUSE_BUSY
+ AST_CAUSE_FAILURE AST_CAUSE_CONGESTION AST_CAUSE_UNALLOCATED
+
+ In addition to these, a lot of PRI codes is defined in causes.h
+ ...should we take care of them too ?
+
+ Quote RFC 3398
+
+ ISUP Cause value SIP response
+ ---------------- ------------
+ 1 unallocated number 404 Not Found
+ 2 no route to network 404 Not found
+ 3 no route to destination 404 Not found
+ 16 normal call clearing --- (*)
+ 17 user busy 486 Busy here
+ 18 no user responding 408 Request Timeout
+ 19 no answer from the user 480 Temporarily unavailable
+ 20 subscriber absent 480 Temporarily unavailable
+ 21 call rejected 403 Forbidden (+)
+ 22 number changed (w/o diagnostic) 410 Gone
+ 22 number changed (w/ diagnostic) 301 Moved Permanently
+ 23 redirection to new destination 410 Gone
+ 26 non-selected user clearing 404 Not Found (=)
+ 27 destination out of order 502 Bad Gateway
+ 28 address incomplete 484 Address incomplete
+ 29 facility rejected 501 Not implemented
+ 31 normal unspecified 480 Temporarily unavailable
+\endverbatim
+*/
+static const char *hangup_cause2sip(int cause)
+{
+ switch (cause) {
+ case AST_CAUSE_UNALLOCATED: /* 1 */
+ case AST_CAUSE_NO_ROUTE_DESTINATION: /* 3 IAX2: Can't find extension in context */
+ case AST_CAUSE_NO_ROUTE_TRANSIT_NET: /* 2 */
+ return "404 Not Found";
+ case AST_CAUSE_CONGESTION: /* 34 */
+ case AST_CAUSE_SWITCH_CONGESTION: /* 42 */
+ return "503 Service Unavailable";
+ case AST_CAUSE_NO_USER_RESPONSE: /* 18 */
+ return "408 Request Timeout";
+ case AST_CAUSE_NO_ANSWER: /* 19 */
+ return "480 Temporarily unavailable";
+ case AST_CAUSE_CALL_REJECTED: /* 21 */
+ return "403 Forbidden";
+ case AST_CAUSE_NUMBER_CHANGED: /* 22 */
+ return "410 Gone";
+ case AST_CAUSE_NORMAL_UNSPECIFIED: /* 31 */
+ return "480 Temporarily unavailable";
+ case AST_CAUSE_INVALID_NUMBER_FORMAT:
+ return "484 Address incomplete";
+ case AST_CAUSE_USER_BUSY:
+ return "486 Busy here";
+ case AST_CAUSE_FAILURE:
+ return "500 Server internal failure";
+ case AST_CAUSE_FACILITY_REJECTED: /* 29 */
+ return "501 Not Implemented";
+ case AST_CAUSE_CHAN_NOT_IMPLEMENTED:
+ return "503 Service Unavailable";
+ /* Used in chan_iax2 */
+ case AST_CAUSE_DESTINATION_OUT_OF_ORDER:
+ return "502 Bad Gateway";
+ case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL: /* Can't find codec to connect to host */
+ return "488 Not Acceptable Here";
+
+ case AST_CAUSE_NOTDEFINED:
+ default:
+ ast_debug(1, "AST hangup cause %d (no match found in SIP)\n", cause);
+ return NULL;
+ }
+
+ /* Never reached */
+ return 0;
+}
+
+
+/*! \brief sip_hangup: Hangup SIP call
+ * Part of PBX interface, called from ast_hangup */
+static int sip_hangup(struct ast_channel *ast)
+{
+ struct sip_pvt *p = ast->tech_pvt;
+ int needcancel = FALSE;
+ int needdestroy = 0;
+ struct ast_channel *oldowner = ast;
+
+ if (!p) {
+ ast_debug(1, "Asked to hangup channel that was not connected\n");
+ return 0;
+ }
+ if (ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) {
+ ast_debug(1, "This call was answered elsewhere");
+ append_history(p, "Cancel", "Call answered elsewhere");
+ p->answered_elsewhere = TRUE;
+ }
+
+ if (ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) {
+ if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
+ if (sipdebug)
+ ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username);
+ update_call_counter(p, DEC_CALL_LIMIT);
+ }
+ ast_debug(4, "SIP Transfer: Not hanging up right now... Rescheduling hangup for %s.\n", p->callid);
+ if (p->autokillid > -1)
+ sip_cancel_destroy(p);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Really hang up next time */
+ p->needdestroy = 0;
+ p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt);
+ p->owner = NULL; /* Owner will be gone after we return, so take it away */
+ return 0;
+ }
+
+ if (ast_test_flag(ast, AST_FLAG_ZOMBIE)) {
+ if (p->refer)
+ ast_debug(1, "SIP Transfer: Hanging up Zombie channel %s after transfer ... Call-ID: %s\n", ast->name, p->callid);
+ else
+ ast_debug(1, "Hanging up zombie call. Be scared.\n");
+ } else
+ ast_debug(1, "Hangup call %s, SIP callid %s\n", ast->name, p->callid);
+
+ sip_pvt_lock(p);
+ if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
+ if (sipdebug)
+ ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username);
+ update_call_counter(p, DEC_CALL_LIMIT);
+ }
+
+ /* Determine how to disconnect */
+ if (p->owner != ast) {
+ ast_log(LOG_WARNING, "Huh? We aren't the owner? Can't hangup call.\n");
+ sip_pvt_unlock(p);
+ return 0;
+ }
+ /* If the call is not UP, we need to send CANCEL instead of BYE */
+ /* In case of re-invites, the call might be UP even though we have an incomplete invite transaction */
+ if (p->invitestate < INV_COMPLETED && p->owner->_state != AST_STATE_UP) {
+ needcancel = TRUE;
+ ast_debug(4, "Hanging up channel in state %s (not UP)\n", ast_state2str(ast->_state));
+ }
+
+ stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
+
+ append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", p->owner ? ast_cause2str(p->owner->hangupcause) : "Unknown");
+
+ /* Disconnect */
+ if (p->vad)
+ ast_dsp_free(p->vad);
+
+ p->owner = NULL;
+ ast->tech_pvt = dialog_unref(ast->tech_pvt);
+
+ ast_module_unref(ast_module_info->self);
+ /* Do not destroy this pvt until we have timeout or
+ get an answer to the BYE or INVITE/CANCEL
+ If we get no answer during retransmit period, drop the call anyway.
+ (Sorry, mother-in-law, you can't deny a hangup by sending
+ 603 declined to BYE...)
+ */
+ if (p->alreadygone)
+ needdestroy = 1; /* Set destroy flag at end of this function */
+ else if (p->invitestate != INV_CALLING)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+
+ /* Start the process if it's not already started */
+ if (!p->alreadygone && !ast_strlen_zero(p->initreq.data)) {
+ if (needcancel) { /* Outgoing call, not up */
+ if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ /* stop retransmitting an INVITE that has not received a response */
+ __sip_pretend_ack(p);
+
+ /* if we can't send right now, mark it pending */
+ if (p->invitestate == INV_CALLING) {
+ /* We can't send anything in CALLING state */
+ ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+ /* Do we need a timer here if we don't hear from them at all? */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ append_history(p, "DELAY", "Not sending cancel, waiting for timeout");
+ } else {
+ /* Send a new request: CANCEL */
+ transmit_request(p, SIP_CANCEL, p->ocseq, XMIT_RELIABLE, FALSE);
+ /* Actually don't destroy us yet, wait for the 487 on our original
+ INVITE, but do set an autodestruct just in case we never get it. */
+ needdestroy = 0;
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ p->invitestate = INV_CANCELLED;
+ }
+ if ( p->initid != -1 ) {
+ /* channel still up - reverse dec of inUse counter
+ only if the channel is not auto-congested */
+ update_call_counter(p, INC_CALL_LIMIT);
+ }
+ } else { /* Incoming call, not up */
+ const char *res;
+ if (ast->hangupcause && (res = hangup_cause2sip(ast->hangupcause)))
+ transmit_response_reliable(p, res, &p->initreq);
+ else
+ transmit_response_reliable(p, "603 Declined", &p->initreq);
+ p->invitestate = INV_TERMINATED;
+ }
+ } else { /* Call is in UP state, send BYE */
+ if (!p->pendinginvite) {
+ char *audioqos = "";
+ char *videoqos = "";
+ char *textqos = "";
+ if (p->rtp)
+ audioqos = ast_rtp_get_quality(p->rtp, NULL);
+ if (p->vrtp)
+ videoqos = ast_rtp_get_quality(p->vrtp, NULL);
+ if (p->trtp)
+ textqos = ast_rtp_get_quality(p->trtp, NULL);
+ /* Send a hangup */
+ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
+
+ /* Get RTCP quality before end of call */
+ if (p->do_history) {
+ if (p->rtp)
+ append_history(p, "RTCPaudio", "Quality:%s", audioqos);
+ if (p->vrtp)
+ append_history(p, "RTCPvideo", "Quality:%s", videoqos);
+ if (p->trtp)
+ append_history(p, "RTCPtext", "Quality:%s", textqos);
+ }
+ if (p->rtp && oldowner)
+ pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos);
+ if (p->vrtp && oldowner)
+ pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos);
+ if (p->trtp && oldowner)
+ pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", textqos);
+ } else {
+ /* Note we will need a BYE when this all settles out
+ but we can't send one while we have "INVITE" outstanding. */
+ ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+ ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
+ if (p->waitid)
+ ast_sched_del(sched, p->waitid);
+ p->waitid = -1;
+ sip_cancel_destroy(p);
+ }
+ }
+ }
+ if (needdestroy)
+ p->needdestroy = 1;
+ sip_pvt_unlock(p);
+ return 0;
+}
+
+/*! \brief Try setting codec suggested by the SIP_CODEC channel variable */
+static void try_suggested_sip_codec(struct sip_pvt *p)
+{
+ int fmt;
+ const char *codec;
+
+ codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC");
+ if (!codec)
+ return;
+
+ fmt = ast_getformatbyname(codec);
+ if (fmt) {
+ ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC} variable\n", codec);
+ if (p->jointcapability & fmt) {
+ p->jointcapability &= fmt;
+ p->capability &= fmt;
+ } else
+ ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because it is not shared by both ends.\n");
+ } else
+ ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because of unrecognized/not configured codec (check allow/disallow in sip.conf): %s\n", codec);
+ return;
+}
+
+/*! \brief sip_answer: Answer SIP call , send 200 OK on Invite
+ * Part of PBX interface */
+static int sip_answer(struct ast_channel *ast)
+{
+ int res = 0;
+ struct sip_pvt *p = ast->tech_pvt;
+
+ sip_pvt_lock(p);
+ if (ast->_state != AST_STATE_UP) {
+ try_suggested_sip_codec(p);
+
+ ast_setstate(ast, AST_STATE_UP);
+ ast_debug(1, "SIP answering channel: %s\n", ast->name);
+ if (p->t38.state == T38_PEER_DIRECT) {
+ p->t38.state = T38_ENABLED;
+ ast_debug(2,"T38State change to %d on channel %s\n", p->t38.state, ast->name);
+ res = transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
+ } else
+ res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE);
+ }
+ sip_pvt_unlock(p);
+ return res;
+}
+
+/*! \brief Send frame to media channel (rtp) */
+static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct sip_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ switch (frame->frametype) {
+ case AST_FRAME_VOICE:
+ if (!(frame->subclass & ast->nativeformats)) {
+ char s1[512], s2[512], s3[512];
+ ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %s(%d) read/write = %s(%d)/%s(%d)\n",
+ frame->subclass,
+ ast_getformatname_multiple(s1, sizeof(s1) - 1, ast->nativeformats & AST_FORMAT_AUDIO_MASK),
+ ast->nativeformats & AST_FORMAT_AUDIO_MASK,
+ ast_getformatname_multiple(s2, sizeof(s2) - 1, ast->readformat),
+ ast->readformat,
+ ast_getformatname_multiple(s3, sizeof(s3) - 1, ast->writeformat),
+ ast->writeformat);
+ return 0;
+ }
+ if (p) {
+ sip_pvt_lock(p);
+ if (p->rtp) {
+ /* If channel is not up, activate early media session */
+ if ((ast->_state != AST_STATE_UP) &&
+ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
+ !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
+ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
+ }
+ p->lastrtptx = time(NULL);
+ res = ast_rtp_write(p->rtp, frame);
+ }
+ sip_pvt_unlock(p);
+ }
+ break;
+ case AST_FRAME_VIDEO:
+ if (p) {
+ sip_pvt_lock(p);
+ if (p->vrtp) {
+ /* Activate video early media */
+ if ((ast->_state != AST_STATE_UP) &&
+ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
+ !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
+ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
+ }
+ p->lastrtptx = time(NULL);
+ res = ast_rtp_write(p->vrtp, frame);
+ }
+ sip_pvt_unlock(p);
+ }
+ break;
+ case AST_FRAME_TEXT:
+ if (p) {
+ sip_pvt_lock(p);
+ if (p->trtp) {
+ /* Activate text early media */
+ if ((ast->_state != AST_STATE_UP) &&
+ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
+ !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
+ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
+ }
+ p->lastrtptx = time(NULL);
+ res = ast_rtp_write(p->trtp, frame);
+ }
+ sip_pvt_unlock(p);
+ }
+ break;
+ case AST_FRAME_IMAGE:
+ return 0;
+ break;
+ case AST_FRAME_MODEM:
+ if (p) {
+ sip_pvt_lock(p);
+ /* UDPTL requires two-way communication, so early media is not needed here.
+ we simply forget the frames if we get modem frames before the bridge is up.
+ Fax will re-transmit.
+ */
+ if (p->udptl && ast->_state == AST_STATE_UP)
+ res = ast_udptl_write(p->udptl, frame);
+ sip_pvt_unlock(p);
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype);
+ return 0;
+ }
+
+ return res;
+}
+
+/*! \brief sip_fixup: Fix up a channel: If a channel is consumed, this is called.
+ Basically update any ->owner links */
+static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ int ret = -1;
+ struct sip_pvt *p;
+
+ if (newchan && ast_test_flag(newchan, AST_FLAG_ZOMBIE))
+ ast_debug(1, "New channel is zombie\n");
+ if (oldchan && ast_test_flag(oldchan, AST_FLAG_ZOMBIE))
+ ast_debug(1, "Old channel is zombie\n");
+
+ if (!newchan || !newchan->tech_pvt) {
+ if (!newchan)
+ ast_log(LOG_WARNING, "No new channel! Fixup of %s failed.\n", oldchan->name);
+ else
+ ast_log(LOG_WARNING, "No SIP tech_pvt! Fixup of %s failed.\n", oldchan->name);
+ return -1;
+ }
+ p = newchan->tech_pvt;
+
+ sip_pvt_lock(p);
+ append_history(p, "Masq", "Old channel: %s\n", oldchan->name);
+ append_history(p, "Masq (cont)", "...new owner: %s\n", newchan->name);
+ if (p->owner != oldchan)
+ ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
+ else {
+ p->owner = newchan;
+ ret = 0;
+ }
+ ast_debug(3, "SIP Fixup: New owner for dialogue %s: %s (Old parent: %s)\n", p->callid, p->owner->name, oldchan->name);
+
+ sip_pvt_unlock(p);
+ return ret;
+}
+
+static int sip_senddigit_begin(struct ast_channel *ast, char digit)
+{
+ struct sip_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ sip_pvt_lock(p);
+ switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
+ case SIP_DTMF_INBAND:
+ res = -1; /* Tell Asterisk to generate inband indications */
+ break;
+ case SIP_DTMF_RFC2833:
+ if (p->rtp)
+ ast_rtp_senddigit_begin(p->rtp, digit);
+ break;
+ default:
+ break;
+ }
+ sip_pvt_unlock(p);
+
+ return res;
+}
+
+/*! \brief Send DTMF character on SIP channel
+ within one call, we're able to transmit in many methods simultaneously */
+static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct sip_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ sip_pvt_lock(p);
+ switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
+ case SIP_DTMF_INFO:
+ case SIP_DTMF_SHORTINFO:
+ transmit_info_with_digit(p, digit, duration);
+ break;
+ case SIP_DTMF_RFC2833:
+ if (p->rtp)
+ ast_rtp_senddigit_end(p->rtp, digit);
+ break;
+ case SIP_DTMF_INBAND:
+ res = -1; /* Tell Asterisk to stop inband indications */
+ break;
+ }
+ sip_pvt_unlock(p);
+
+ return res;
+}
+
+/*! \brief Transfer SIP call */
+static int sip_transfer(struct ast_channel *ast, const char *dest)
+{
+ struct sip_pvt *p = ast->tech_pvt;
+ int res;
+
+ if (dest == NULL) /* functions below do not take a NULL */
+ dest = "";
+ sip_pvt_lock(p);
+ if (ast->_state == AST_STATE_RING)
+ res = sip_sipredirect(p, dest);
+ else
+ res = transmit_refer(p, dest);
+ sip_pvt_unlock(p);
+ return res;
+}
+
+/*! \brief Play indication to user
+ * With SIP a lot of indications is sent as messages, letting the device play
+ the indication - busy signal, congestion etc
+ \return -1 to force ast_indicate to send indication in audio, 0 if SIP can handle the indication by sending a message
+*/
+static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+ struct sip_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ sip_pvt_lock(p);
+ switch(condition) {
+ case AST_CONTROL_RINGING:
+ if (ast->_state == AST_STATE_RING) {
+ p->invitestate = INV_EARLY_MEDIA;
+ if (!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) ||
+ (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER)) {
+ /* Send 180 ringing if out-of-band seems reasonable */
+ transmit_response(p, "180 Ringing", &p->initreq);
+ ast_set_flag(&p->flags[0], SIP_RINGING);
+ if (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_YES)
+ break;
+ } else {
+ /* Well, if it's not reasonable, just send in-band */
+ }
+ }
+ res = -1;
+ break;
+ case AST_CONTROL_BUSY:
+ if (ast->_state != AST_STATE_UP) {
+ transmit_response(p, "486 Busy Here", &p->initreq);
+ p->invitestate = INV_COMPLETED;
+ sip_alreadygone(p);
+ ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+ break;
+ }
+ res = -1;
+ break;
+ case AST_CONTROL_CONGESTION:
+ if (ast->_state != AST_STATE_UP) {
+ transmit_response(p, "503 Service Unavailable", &p->initreq);
+ p->invitestate = INV_COMPLETED;
+ sip_alreadygone(p);
+ ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+ break;
+ }
+ res = -1;
+ break;
+ case AST_CONTROL_PROCEEDING:
+ if ((ast->_state != AST_STATE_UP) &&
+ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
+ !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ transmit_response(p, "100 Trying", &p->initreq);
+ p->invitestate = INV_PROCEEDING;
+ break;
+ }
+ res = -1;
+ break;
+ case AST_CONTROL_PROGRESS:
+ if ((ast->_state != AST_STATE_UP) &&
+ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
+ !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ p->invitestate = INV_EARLY_MEDIA;
+ transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
+ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
+ break;
+ }
+ res = -1;
+ break;
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, data, p->mohinterpret);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ case AST_CONTROL_VIDUPDATE: /* Request a video frame update */
+ if (p->vrtp && !p->novideo) {
+ transmit_info_with_vidupdate(p);
+ /* ast_rtcp_send_h261fur(p->vrtp); */
+ } else
+ res = -1;
+ break;
+ case -1:
+ res = -1;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition);
+ res = -1;
+ break;
+ }
+ sip_pvt_unlock(p);
+ return res;
+}
+
+
+/*! \brief Initiate a call in the SIP channel
+ called from sip_request_call (calls from the pbx ) for outbound channels
+ and from handle_request_invite for inbound channels
+
+*/
+static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title)
+{
+ struct ast_channel *tmp;
+ struct ast_variable *v = NULL;
+ int fmt;
+ int what;
+ int video;
+ int text;
+ int needvideo = 0;
+ int needtext = 0;
+ char buf[BUFSIZ];
+ char *decoded_exten;
+
+ {
+ const char *my_name; /* pick a good name */
+
+ if (title)
+ my_name = title;
+ else if ( (my_name = strchr(i->fromdomain,':')) )
+ my_name++; /* skip ':' */
+ else
+ my_name = i->fromdomain;
+
+ sip_pvt_unlock(i);
+ /* Don't hold a sip pvt lock while we allocate a channel */
+ tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "SIP/%s-%08x", my_name, (int)(long) i);
+
+ }
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Unable to allocate AST channel structure for SIP channel\n");
+ return NULL;
+ }
+ sip_pvt_lock(i);
+
+ tmp->tech = ( ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INFO || ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO) ? &sip_tech_info : &sip_tech;
+
+ /* Select our native format based on codec preference until we receive
+ something from another device to the contrary. */
+ if (i->jointcapability) { /* The joint capabilities of us and peer */
+ what = i->jointcapability;
+ video = i->jointcapability & AST_FORMAT_VIDEO_MASK;
+ text = i->jointcapability & AST_FORMAT_TEXT_MASK;
+ } else if (i->capability) { /* Our configured capability for this peer */
+ what = i->capability;
+ video = i->capability & AST_FORMAT_VIDEO_MASK;
+ text = i->capability & AST_FORMAT_TEXT_MASK;
+ } else {
+ what = global_capability; /* Global codec support */
+ video = global_capability & AST_FORMAT_VIDEO_MASK;
+ text = global_capability & AST_FORMAT_TEXT_MASK;
+ }
+
+ /* Set the native formats for audio and merge in video */
+ tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | video | text;
+ ast_debug(3, "*** Our native formats are %s \n", ast_getformatname_multiple(buf, BUFSIZ, tmp->nativeformats));
+ ast_debug(3, "*** Joint capabilities are %s \n", ast_getformatname_multiple(buf, BUFSIZ, i->jointcapability));
+ ast_debug(3, "*** Our capabilities are %s \n", ast_getformatname_multiple(buf, BUFSIZ, i->capability));
+ ast_debug(3, "*** AST_CODEC_CHOOSE formats are %s \n", ast_getformatname_multiple(buf, BUFSIZ, ast_codec_choose(&i->prefs, what, 1)));
+ if (i->prefcodec)
+ ast_debug(3, "*** Our preferred formats from the incoming channel are %s \n", ast_getformatname_multiple(buf, BUFSIZ, i->prefcodec));
+
+ /* XXX Why are we choosing a codec from the native formats?? */
+ fmt = ast_best_codec(tmp->nativeformats);
+
+ /* If we have a prefcodec setting, we have an inbound channel that set a
+ preferred format for this call. Otherwise, we check the jointcapability
+ We also check for vrtp. If it's not there, we are not allowed do any video anyway.
+ */
+ if (i->vrtp) {
+ if (i->prefcodec)
+ needvideo = i->prefcodec & AST_FORMAT_VIDEO_MASK; /* Outbound call */
+ else
+ needvideo = i->jointcapability & AST_FORMAT_VIDEO_MASK; /* Inbound call */
+ }
+
+ if (i->trtp) {
+ if (i->prefcodec)
+ needtext = i->prefcodec & AST_FORMAT_TEXT_MASK; /* Outbound call */
+ else
+ needtext = i->jointcapability & AST_FORMAT_TEXT_MASK; /* Inbound call */
+ }
+
+ if (needvideo)
+ ast_debug(3, "This channel can handle video! HOLLYWOOD next!\n");
+ else
+ ast_debug(3, "This channel will not be able to handle video.\n");
+
+
+
+ if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) {
+ i->vad = ast_dsp_new();
+ ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT);
+ if (global_relaxdtmf)
+ ast_dsp_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
+ }
+
+ /* Set file descriptors for audio, video, realtime text and UDPTL as needed */
+ if (i->rtp) {
+ ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp));
+ ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp));
+ }
+ if (needvideo && i->vrtp) {
+ ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp));
+ ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp));
+ }
+ if (needtext && i->trtp)
+ ast_channel_set_fd(tmp, 4, ast_rtp_fd(i->trtp));
+ if (i->udptl)
+ ast_channel_set_fd(tmp, 5, ast_udptl_fd(i->udptl));
+
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->adsicpe = AST_ADSI_UNAVAILABLE;
+ tmp->writeformat = fmt;
+ tmp->rawwriteformat = fmt;
+ tmp->readformat = fmt;
+ tmp->rawreadformat = fmt;
+ tmp->tech_pvt = dialog_ref(i);
+
+ tmp->callgroup = i->callgroup;
+ tmp->pickupgroup = i->pickupgroup;
+ tmp->cid.cid_pres = i->callingpres;
+ if (!ast_strlen_zero(i->accountcode))
+ ast_string_field_set(tmp, accountcode, i->accountcode);
+ if (i->amaflags)
+ tmp->amaflags = i->amaflags;
+ if (!ast_strlen_zero(i->language))
+ ast_string_field_set(tmp, language, i->language);
+ i->owner = tmp;
+ ast_module_ref(ast_module_info->self);
+ ast_copy_string(tmp->context, i->context, sizeof(tmp->context));
+ /*Since it is valid to have extensions in the dialplan that have unescaped characters in them
+ * we should decode the uri before storing it in the channel, but leave it encoded in the sip_pvt
+ * structure so that there aren't issues when forming URI's
+ */
+ decoded_exten = ast_strdupa(i->exten);
+ ast_uri_decode(decoded_exten);
+ ast_copy_string(tmp->exten, decoded_exten, sizeof(tmp->exten));
+
+ /* Don't use ast_set_callerid() here because it will
+ * generate an unnecessary NewCallerID event */
+ tmp->cid.cid_ani = ast_strdup(i->cid_num);
+ if (!ast_strlen_zero(i->rdnis))
+ tmp->cid.cid_rdnis = ast_strdup(i->rdnis);
+
+ if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s"))
+ tmp->cid.cid_dnid = ast_strdup(i->exten);
+
+ tmp->priority = 1;
+ if (!ast_strlen_zero(i->uri))
+ pbx_builtin_setvar_helper(tmp, "SIPURI", i->uri);
+ if (!ast_strlen_zero(i->domain))
+ pbx_builtin_setvar_helper(tmp, "SIPDOMAIN", i->domain);
+ if (!ast_strlen_zero(i->callid))
+ pbx_builtin_setvar_helper(tmp, "SIPCALLID", i->callid);
+ if (i->rtp)
+ ast_jb_configure(tmp, &global_jbconf);
+
+ /* If the INVITE contains T.38 SDP information set the proper channel variable so a created outgoing call will also have T.38 */
+ if (i->udptl && i->t38.state == T38_PEER_DIRECT)
+ pbx_builtin_setvar_helper(tmp, "_T38CALL", "1");
+
+ /* Set channel variables for this call from configuration */
+ for (v = i->chanvars ; v ; v = v->next)
+ pbx_builtin_setvar_helper(tmp, v->name, v->value);
+
+ if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(tmp);
+ tmp = NULL;
+ }
+
+ if (i->do_history)
+ append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid);
+
+ /* Inform manager user about new channel and their SIP call ID */
+ if (global_callevents)
+ manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
+ "Channel: %s\r\nUniqueid: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\n",
+ tmp->name, tmp->uniqueid, "SIP", i->callid, i->fullcontact);
+
+ return tmp;
+}
+
+/*! \brief Reads one line of SIP message body */
+static char *get_body_by_line(const char *line, const char *name, int nameLen)
+{
+ if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=')
+ return ast_skip_blanks(line + nameLen + 1);
+
+ return "";
+}
+
+/*! \brief Lookup 'name' in the SDP starting
+ * at the 'start' line. Returns the matching line, and 'start'
+ * is updated with the next line number.
+ */
+static const char *get_sdp_iterate(int *start, struct sip_request *req, const char *name)
+{
+ int len = strlen(name);
+
+ while (*start < req->sdp_end) {
+ const char *r = get_body_by_line(req->line[(*start)++], name, len);
+ if (r[0] != '\0')
+ return r;
+ }
+
+ return "";
+}
+
+/*! \brief Get a line from an SDP message body */
+static const char *get_sdp(struct sip_request *req, const char *name)
+{
+ int dummy = 0;
+
+ return get_sdp_iterate(&dummy, req, name);
+}
+
+/*! \brief Get a specific line from the message body */
+static char *get_body(struct sip_request *req, char *name)
+{
+ int x;
+ int len = strlen(name);
+ char *r;
+
+ for (x = 0; x < req->lines; x++) {
+ r = get_body_by_line(req->line[x], name, len);
+ if (r[0] != '\0')
+ return r;
+ }
+
+ return "";
+}
+
+/*! \brief Find compressed SIP alias */
+static const char *find_alias(const char *name, const char *_default)
+{
+ /*! \brief Structure for conversion between compressed SIP and "normal" SIP */
+ static const struct cfalias {
+ char * const fullname;
+ char * const shortname;
+ } aliases[] = {
+ { "Content-Type", "c" },
+ { "Content-Encoding", "e" },
+ { "From", "f" },
+ { "Call-ID", "i" },
+ { "Contact", "m" },
+ { "Content-Length", "l" },
+ { "Subject", "s" },
+ { "To", "t" },
+ { "Supported", "k" },
+ { "Refer-To", "r" },
+ { "Referred-By", "b" },
+ { "Allow-Events", "u" },
+ { "Event", "o" },
+ { "Via", "v" },
+ { "Accept-Contact", "a" },
+ { "Reject-Contact", "j" },
+ { "Request-Disposition", "d" },
+ { "Session-Expires", "x" },
+ { "Identity", "y" },
+ { "Identity-Info", "n" },
+ };
+ int x;
+
+ for (x=0; x<sizeof(aliases) / sizeof(aliases[0]); x++)
+ if (!strcasecmp(aliases[x].fullname, name))
+ return aliases[x].shortname;
+
+ return _default;
+}
+
+static const char *__get_header(const struct sip_request *req, const char *name, int *start)
+{
+ int pass;
+
+ /*
+ * Technically you can place arbitrary whitespace both before and after the ':' in
+ * a header, although RFC3261 clearly says you shouldn't before, and place just
+ * one afterwards. If you shouldn't do it, what absolute idiot decided it was
+ * a good idea to say you can do it, and if you can do it, why in the hell would.
+ * you say you shouldn't.
+ * Anyways, pedanticsipchecking controls whether we allow spaces before ':',
+ * and we always allow spaces after that for compatibility.
+ */
+ for (pass = 0; name && pass < 2;pass++) {
+ int x, len = strlen(name);
+ for (x=*start; x<req->headers; x++) {
+ if (!strncasecmp(req->header[x], name, len)) {
+ char *r = req->header[x] + len; /* skip name */
+ if (pedanticsipchecking)
+ r = ast_skip_blanks(r);
+
+ if (*r == ':') {
+ *start = x+1;
+ return ast_skip_blanks(r+1);
+ }
+ }
+ }
+ if (pass == 0) /* Try aliases */
+ name = find_alias(name, NULL);
+ }
+
+ /* Don't return NULL, so get_header is always a valid pointer */
+ return "";
+}
+
+/*! \brief Get header from SIP request
+ \return Always return something, so don't check for NULL because it won't happen :-)
+*/
+static const char *get_header(const struct sip_request *req, const char *name)
+{
+ int start = 0;
+ return __get_header(req, name, &start);
+}
+
+/*! \brief Read RTP from network */
+static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect)
+{
+ /* Retrieve audio/etc from channel. Assumes p->lock is already held. */
+ struct ast_frame *f;
+
+ if (!p->rtp) {
+ /* We have no RTP allocated for this channel */
+ return &ast_null_frame;
+ }
+
+ switch(ast->fdno) {
+ case 0:
+ f = ast_rtp_read(p->rtp); /* RTP Audio */
+ break;
+ case 1:
+ f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */
+ break;
+ case 2:
+ f = ast_rtp_read(p->vrtp); /* RTP Video */
+ break;
+ case 3:
+ f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */
+ break;
+ case 4:
+ f = ast_rtp_read(p->trtp); /* RTP Text */
+ if (sipdebug_text) {
+ int i;
+ unsigned char* arr = f->data;
+ for (i=0; i < f->datalen; i++)
+ ast_verbose("%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.');
+ ast_verbose(" -> ");
+ for (i=0; i < f->datalen; i++)
+ ast_verbose("%02X ", arr[i]);
+ ast_verbose("\n");
+ }
+ break;
+ case 5:
+ f = ast_udptl_read(p->udptl); /* UDPTL for T.38 */
+ break;
+ default:
+ f = &ast_null_frame;
+ }
+ /* Don't forward RFC2833 if we're not supposed to */
+ if (f && (f->frametype == AST_FRAME_DTMF) &&
+ (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833))
+ return &ast_null_frame;
+
+ /* We already hold the channel lock */
+ if (!p->owner || (f && f->frametype != AST_FRAME_VOICE))
+ return f;
+
+ if (f && f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
+ if (!(f->subclass & p->jointcapability)) {
+ ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
+ ast_getformatname(f->subclass), p->owner->name);
+ return &ast_null_frame;
+ }
+ ast_debug(1, "Oooh, format changed to %d %s\n",
+ f->subclass, ast_getformatname(f->subclass));
+ p->owner->nativeformats = (p->owner->nativeformats & (AST_FORMAT_VIDEO_MASK | AST_FORMAT_TEXT_MASK)) | f->subclass;
+ ast_set_read_format(p->owner, p->owner->readformat);
+ ast_set_write_format(p->owner, p->owner->writeformat);
+ }
+
+ if (f && (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
+ f = ast_dsp_process(p->owner, p->vad, f);
+ if (f && f->frametype == AST_FRAME_DTMF) {
+ if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') {
+ ast_debug(1, "Fax CNG detected on %s\n", ast->name);
+ *faxdetect = 1;
+ } else {
+ ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
+ }
+ }
+ }
+
+ return f;
+}
+
+/*! \brief Read SIP RTP from channel */
+static struct ast_frame *sip_read(struct ast_channel *ast)
+{
+ struct ast_frame *fr;
+ struct sip_pvt *p = ast->tech_pvt;
+ int faxdetected = FALSE;
+
+ sip_pvt_lock(p);
+ fr = sip_rtp_read(ast, p, &faxdetected);
+ p->lastrtprx = time(NULL);
+
+ /* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */
+ /* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */
+ if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
+ if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
+ if (!p->pendinginvite) {
+ ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n",ast->name);
+ p->t38.state = T38_LOCAL_REINVITE;
+ transmit_reinvite_with_sdp(p, TRUE, FALSE);
+ ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, ast->name);
+ }
+ } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+ ast_debug(3, "Deferring reinvite on SIP (%s) - it will be re-negotiated for T.38\n", ast->name);
+ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
+ }
+ }
+
+ sip_pvt_unlock(p);
+ return fr;
+}
+
+
+/*! \brief Generate 32 byte random string for callid's etc */
+static char *generate_random_string(char *buf, size_t size)
+{
+ long val[4];
+ int x;
+
+ for (x=0; x<4; x++)
+ val[x] = ast_random();
+ snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]);
+
+ return buf;
+}
+
+/*! \brief Build SIP Call-ID value for a non-REGISTER transaction */
+static void build_callid_pvt(struct sip_pvt *pvt)
+{
+ char buf[33];
+
+ const char *host = S_OR(pvt->fromdomain, ast_inet_ntoa(pvt->ourip.sin_addr));
+
+ ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
+
+}
+
+/*! \brief Build SIP Call-ID value for a REGISTER transaction */
+static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain)
+{
+ char buf[33];
+
+ const char *host = S_OR(fromdomain, ast_inet_ntoa(ourip));
+
+ ast_string_field_build(reg, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
+}
+
+/*! \brief Make our SIP dialog tag */
+static void make_our_tag(char *tagbuf, size_t len)
+{
+ snprintf(tagbuf, len, "as%08lx", ast_random());
+}
+
+/*! \brief Allocate Session-Timers struct w/in dialog */
+static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p)
+{
+ struct sip_st_dlg *stp;
+
+ if (p->stimer) {
+ ast_log(LOG_ERROR, "Session-Timer struct already allocated\n");
+ return p->stimer;
+ }
+
+ if (!(stp = ast_calloc(1, sizeof(struct sip_st_dlg))))
+ return NULL;
+
+ p->stimer = stp;
+
+ stp->st_schedid = -1; /* Session-Timers ast_sched scheduler id */
+
+ return p->stimer;
+}
+
+/*! \brief Allocate sip_pvt structure, set defaults and link in the container.
+ * Returns a reference to the object so whoever uses it later must
+ * remember to release the reference.
+ */
+static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
+ int useglobal_nat, const int intended_method)
+{
+ struct sip_pvt *p;
+
+ if (!(p = ast_calloc(1, sizeof(*p))))
+ return NULL;
+
+ if (ast_string_field_init(p, 512)) {
+ ast_free(p);
+ return NULL;
+ }
+
+ ast_mutex_init(&p->pvt_lock);
+
+ p->socket.fd = -1;
+ p->socket.type = SIP_TRANSPORT_UDP;
+ p->method = intended_method;
+ p->initid = -1;
+ p->waitid = -1;
+ p->autokillid = -1;
+ p->subscribed = NONE;
+ p->stateid = -1;
+ p->sessionversion_remote = -1;
+ p->session_modify = TRUE;
+ p->stimer = NULL;
+ p->prefs = default_prefs; /* Set default codecs for this call */
+
+ if (intended_method != SIP_OPTIONS) { /* Peerpoke has it's own system */
+ p->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
+ p->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
+ }
+
+ if (!sin)
+ p->ourip = internip;
+ else {
+ p->sa = *sin;
+ ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
+ }
+
+ /* Copy global flags to this PVT at setup. */
+ ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&p->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+
+ p->do_history = recordhistory;
+
+ p->branch = ast_random();
+ make_our_tag(p->tag, sizeof(p->tag));
+ p->ocseq = INITIAL_CSEQ;
+
+ if (sip_methods[intended_method].need_rtp) {
+ p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ /* If the global videosupport flag is on, we always create a RTP interface for video */
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT))
+ p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT))
+ p->trtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT))
+ p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr);
+ if (!p->rtp|| (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp)
+ || (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && !p->trtp)) {
+ ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n",
+ ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video " : "",
+ ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "and text " : "", strerror(errno));
+ ast_mutex_destroy(&p->pvt_lock);
+ if (p->chanvars) {
+ ast_variables_destroy(p->chanvars);
+ p->chanvars = NULL;
+ }
+ ast_free(p);
+ return NULL;
+ }
+ ast_rtp_setqos(p->rtp, global_tos_audio, global_cos_audio, "SIP RTP");
+ ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
+ ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+ ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout);
+ ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout);
+ ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive);
+ if (p->vrtp) {
+ ast_rtp_setqos(p->vrtp, global_tos_video, global_cos_video, "SIP VRTP");
+ ast_rtp_setdtmf(p->vrtp, 0);
+ ast_rtp_setdtmfcompensate(p->vrtp, 0);
+ ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout);
+ ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout);
+ ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive);
+ }
+ if (p->trtp) {
+ ast_rtp_setqos(p->trtp, global_tos_text, global_cos_text, "SIP TRTP");
+ ast_rtp_setdtmf(p->trtp, 0);
+ ast_rtp_setdtmfcompensate(p->trtp, 0);
+ }
+ if (p->udptl)
+ ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio);
+ p->maxcallbitrate = default_maxcallbitrate;
+ }
+
+ if (useglobal_nat && sin) {
+ /* Setup NAT structure according to global settings if we have an address */
+ ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT);
+ p->recv = *sin;
+ do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
+ }
+
+ if (p->method != SIP_REGISTER)
+ ast_string_field_set(p, fromdomain, default_fromdomain);
+ build_via(p);
+ if (!callid)
+ build_callid_pvt(p);
+ else
+ ast_string_field_set(p, callid, callid);
+ /* Assign default music on hold class */
+ ast_string_field_set(p, mohinterpret, default_mohinterpret);
+ ast_string_field_set(p, mohsuggest, default_mohsuggest);
+ p->capability = global_capability;
+ p->allowtransfer = global_allowtransfer;
+ if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
+ (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
+ p->noncodeccapability |= AST_RTP_DTMF;
+ if (p->udptl) {
+ ast_copy_flags(&p->t38.t38support, &p->flags[1], SIP_PAGE2_T38SUPPORT);
+ set_t38_capabilities(p);
+ p->t38.jointcapability = p->t38.capability;
+ }
+ ast_string_field_set(p, context, default_context);
+
+
+ /* Add to active dialog list */
+ dialoglist_lock();
+ p->next = dialoglist;
+ dialoglist = dialog_ref(p);
+ dialoglist_unlock();
+ ast_debug(1, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : "(No Call-ID)", sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
+ return p;
+}
+
+/*! \brief find or create a dialog structure for an incoming SIP message.
+ * Connect incoming SIP message to current dialog or create new dialog structure
+ * Returns a reference to the sip_pvt object, remember to give it back once done.
+ * Called by handle_incoming(), sipsock_read
+ */
+static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method)
+{
+ struct sip_pvt *p = NULL;
+ char *tag = ""; /* note, tag is never NULL */
+ char totag[128];
+ char fromtag[128];
+ const char *callid = get_header(req, "Call-ID");
+ const char *from = get_header(req, "From");
+ const char *to = get_header(req, "To");
+ const char *cseq = get_header(req, "Cseq");
+
+ /* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
+ /* get_header always returns non-NULL so we must use ast_strlen_zero() */
+ if (ast_strlen_zero(callid) || ast_strlen_zero(to) ||
+ ast_strlen_zero(from) || ast_strlen_zero(cseq))
+ return NULL; /* Invalid packet */
+
+ if (pedanticsipchecking) {
+ /* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
+ we need more to identify a branch - so we have to check branch, from
+ and to tags to identify a call leg.
+ For Asterisk to behave correctly, you need to turn on pedanticsipchecking
+ in sip.conf
+ */
+ if (gettag(req, "To", totag, sizeof(totag)))
+ req->has_to_tag = 1; /* Used in handle_request/response */
+ gettag(req, "From", fromtag, sizeof(fromtag));
+
+ tag = (req->method == SIP_RESPONSE) ? totag : fromtag;
+
+ ast_debug(5, "= Looking for Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag);
+
+ /* All messages must always have From: tag */
+ if (ast_strlen_zero(fromtag)) {
+ ast_debug(5, "%s request has no from tag, dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
+ return NULL;
+ }
+ /* reject requests that must always have a To: tag */
+ if (ast_strlen_zero(totag) && (req->method == SIP_ACK || req->method == SIP_BYE || req->method == SIP_INFO )) {
+ ast_debug(5, "%s must have a to tag. dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
+ return NULL;
+ }
+ }
+
+ dialoglist_lock();
+ for (p = dialoglist; p; p = p->next) {
+ /* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
+ int found = FALSE;
+ if (ast_strlen_zero(p->callid))
+ continue;
+ if (req->method == SIP_REGISTER)
+ found = (!strcmp(p->callid, callid));
+ else
+ found = (!strcmp(p->callid, callid) &&
+ (!pedanticsipchecking || !tag || ast_strlen_zero(p->theirtag) || !strcmp(p->theirtag, tag))) ;
+
+ ast_debug(5, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag);
+
+ /* If we get a new request within an existing to-tag - check the to tag as well */
+ if (pedanticsipchecking && found && req->method != SIP_RESPONSE) { /* SIP Request */
+ if (p->tag[0] == '\0' && totag[0]) {
+ /* We have no to tag, but they have. Wrong dialog */
+ found = FALSE;
+ } else if (totag[0]) { /* Both have tags, compare them */
+ if (strcmp(totag, p->tag)) {
+ found = FALSE; /* This is not our packet */
+ }
+ }
+ if (!found)
+ ast_debug(5, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, totag, sip_methods[req->method].text);
+ }
+
+
+ if (found) {
+ /* Found the call */
+ sip_pvt_lock(p);
+ dialoglist_unlock();
+ return p;
+ }
+ }
+ dialoglist_unlock();
+
+ /* See if the method is capable of creating a dialog */
+ if (sip_methods[intended_method].can_create == CAN_CREATE_DIALOG) {
+ if (intended_method == SIP_REFER) {
+ /* We do support REFER, but not outside of a dialog yet */
+ transmit_response_using_temp(callid, sin, 1, intended_method, req, "603 Declined (no dialog)");
+ } else if (intended_method == SIP_NOTIFY) {
+ /* We do not support out-of-dialog NOTIFY either,
+ like voicemail notification, so cancel that early */
+ transmit_response_using_temp(callid, sin, 1, intended_method, req, "489 Bad event");
+ } else {
+ /* Ok, time to create a new SIP dialog object, a pvt */
+ if ((p = sip_alloc(callid, sin, 1, intended_method))) {
+ /* Ok, we've created a dialog, let's go and process it */
+ sip_pvt_lock(p);
+ } else {
+ /* We have a memory or file/socket error (can't allocate RTP sockets or something) so we're not
+ getting a dialog from sip_alloc.
+
+ Without a dialog we can't retransmit and handle ACKs and all that, but at least
+ send an error message.
+
+ Sorry, we apologize for the inconvienience
+ */
+ transmit_response_using_temp(callid, sin, 1, intended_method, req, "500 Server internal error");
+ ast_debug(4, "Failed allocating SIP dialog, sending 500 Server internal error and giving up\n");
+ }
+ }
+ return p; /* can be NULL */
+ } else if( sip_methods[intended_method].can_create == CAN_CREATE_DIALOG_UNSUPPORTED_METHOD) {
+ /* A method we do not support, let's take it on the volley */
+ transmit_response_using_temp(callid, sin, 1, intended_method, req, "501 Method Not Implemented");
+ ast_debug(2, "Got a request with unsupported SIP method.\n");
+ } else if (intended_method != SIP_RESPONSE && intended_method != SIP_ACK) {
+ /* This is a request outside of a dialog that we don't know about */
+ transmit_response_using_temp(callid, sin, 1, intended_method, req, "481 Call leg/transaction does not exist");
+ ast_debug(2, "That's odd... Got a request in unknown dialog. Callid %s\n", callid ? callid : "<unknown>");
+ }
+ /* We do not respond to responses for dialogs that we don't know about, we just drop
+ the session quickly */
+ if (intended_method == SIP_RESPONSE)
+ ast_debug(2, "That's odd... Got a response on a call we dont know about. Callid %s\n", callid ? callid : "<unknown>");
+
+ return p;
+}
+
+/*! \brief Parse register=> line in sip.conf and add to registry */
+static int sip_register(const char *value, int lineno)
+{
+ struct sip_registry *reg;
+ int portnum = 0;
+ enum sip_transport transport = SIP_TRANSPORT_UDP;
+ char buf[256] = "";
+ char *username = NULL;
+ char *hostname=NULL, *secret=NULL, *authuser=NULL;
+ char *porta=NULL;
+ char *callback=NULL;
+ char *trans=NULL;
+
+ if (!value)
+ return -1;
+
+ ast_copy_string(buf, value, sizeof(buf));
+
+ username = strstr(buf, "://");
+
+ if (username) {
+ *username = '\0';
+ username += 3;
+
+ trans = buf;
+
+ if (!strcasecmp(trans, "udp"))
+ transport = SIP_TRANSPORT_UDP;
+ else if (!strcasecmp(trans, "tcp"))
+ transport = SIP_TRANSPORT_TCP;
+ else if (!strcasecmp(trans, "tls"))
+ transport = SIP_TRANSPORT_TLS;
+ else
+ ast_log(LOG_WARNING, "'%s' is not a valid transport value for registration '%s' at line '%d'\n", trans, value, lineno);
+ } else {
+ username = buf;
+ ast_log(LOG_DEBUG, "no trans\n");
+ }
+
+ /* First split around the last '@' then parse the two components. */
+ hostname = strrchr(username, '@'); /* allow @ in the first part */
+ if (hostname)
+ *hostname++ = '\0';
+ if (ast_strlen_zero(username) || ast_strlen_zero(hostname)) {
+ ast_log(LOG_WARNING, "Format for registration is user[:secret[:authuser]]@host[:port][/contact] at line %d\n", lineno);
+ return -1;
+ }
+ /* split user[:secret[:authuser]] */
+ secret = strchr(username, ':');
+ if (secret) {
+ *secret++ = '\0';
+ authuser = strchr(secret, ':');
+ if (authuser)
+ *authuser++ = '\0';
+ }
+ /* split host[:port][/contact] */
+ callback = strchr(hostname, '/');
+ if (callback)
+ *callback++ = '\0';
+ if (ast_strlen_zero(callback))
+ callback = "s";
+ porta = strchr(hostname, ':');
+ if (porta) {
+ *porta++ = '\0';
+ portnum = atoi(porta);
+ if (portnum == 0) {
+ ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
+ return -1;
+ }
+ }
+ if (!(reg = ast_calloc(1, sizeof(*reg)))) {
+ ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n");
+ return -1;
+ }
+
+ if (ast_string_field_init(reg, 256)) {
+ ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry strings\n");
+ ast_free(reg);
+ return -1;
+ }
+
+ regobjs++;
+ ASTOBJ_INIT(reg);
+ ast_string_field_set(reg, callback, callback);
+ if (!ast_strlen_zero(username))
+ ast_string_field_set(reg, username, username);
+ if (hostname)
+ ast_string_field_set(reg, hostname, hostname);
+ if (authuser)
+ ast_string_field_set(reg, authuser, authuser);
+ if (secret)
+ ast_string_field_set(reg, secret, secret);
+ reg->transport = transport;
+ reg->expire = -1;
+ reg->expiry = default_expiry;
+ reg->timeout = -1;
+ reg->refresh = default_expiry;
+ reg->portno = portnum;
+ reg->callid_valid = FALSE;
+ reg->ocseq = INITIAL_CSEQ;
+ ASTOBJ_CONTAINER_LINK(&regl, reg); /* Add the new registry entry to the list */
+ registry_unref(reg); /* release the reference given by ASTOBJ_INIT. The container has another reference */
+ return 0;
+}
+
+/*! \brief Parse multiline SIP headers into one header
+ This is enabled if pedanticsipchecking is enabled */
+static int lws2sws(char *msgbuf, int len)
+{
+ int h = 0, t = 0;
+ int lws = 0;
+
+ for (; h < len;) {
+ /* Eliminate all CRs */
+ if (msgbuf[h] == '\r') {
+ h++;
+ continue;
+ }
+ /* Check for end-of-line */
+ if (msgbuf[h] == '\n') {
+ /* Check for end-of-message */
+ if (h + 1 == len)
+ break;
+ /* Check for a continuation line */
+ if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') {
+ /* Merge continuation line */
+ h++;
+ continue;
+ }
+ /* Propagate LF and start new line */
+ msgbuf[t++] = msgbuf[h++];
+ lws = 0;
+ continue;
+ }
+ if (msgbuf[h] == ' ' || msgbuf[h] == '\t') {
+ if (lws) {
+ h++;
+ continue;
+ }
+ msgbuf[t++] = msgbuf[h++];
+ lws = 1;
+ continue;
+ }
+ msgbuf[t++] = msgbuf[h++];
+ if (lws)
+ lws = 0;
+ }
+ msgbuf[t] = '\0';
+ return t;
+}
+
+/*! \brief Parse a SIP message
+ \note this function is used both on incoming and outgoing packets
+*/
+static void parse_request(struct sip_request *req)
+{
+ char *c = req->data, **dst = req->header;
+ int i = 0, lim = SIP_MAX_HEADERS - 1;
+
+ req->header[0] = c;
+ req->headers = -1; /* mark that we are working on the header */
+ for (; *c; c++) {
+ if (*c == '\r') /* remove \r */
+ *c = '\0';
+ else if (*c == '\n') { /* end of this line */
+ *c = '\0';
+ if (sipdebug)
+ ast_debug(4, "%7s %2d [%3d]: %s\n",
+ req->headers < 0 ? "Header" : "Body",
+ i, (int)strlen(dst[i]), dst[i]);
+ if (ast_strlen_zero(dst[i]) && req->headers < 0) {
+ req->headers = i; /* record number of header lines */
+ dst = req->line; /* start working on the body */
+ i = 0;
+ lim = SIP_MAX_LINES - 1;
+ } else { /* move to next line, check for overflows */
+ if (i++ >= lim)
+ break;
+ }
+ dst[i] = c + 1; /* record start of next line */
+ }
+ }
+ /* Check for last header without CRLF. The RFC for SDP requires CRLF,
+ but since some devices send without, we'll be generous in what we accept.
+ */
+ if (!ast_strlen_zero(dst[i])) {
+ if (sipdebug)
+ ast_debug(4, "%7s %2d [%3d]: %s\n",
+ req->headers < 0 ? "Header" : "Body",
+ i, (int)strlen(dst[i]), dst[i]);
+ i++;
+ }
+ /* update count of header or body lines */
+ if (req->headers >= 0) /* we are in the body */
+ req->lines = i;
+ else { /* no body */
+ req->headers = i;
+ req->lines = 0;
+ req->line[0] = "";
+ }
+
+ if (*c)
+ ast_log(LOG_WARNING, "Too many lines, skipping <%s>\n", c);
+ /* Split up the first line parts */
+ determine_firstline_parts(req);
+}
+
+/*!
+ \brief Determine whether a SIP message contains an SDP in its body
+ \param req the SIP request to process
+ \return 1 if SDP found, 0 if not found
+
+ Also updates req->sdp_start and req->sdp_end to indicate where the SDP
+ lives in the message body.
+*/
+static int find_sdp(struct sip_request *req)
+{
+ const char *content_type;
+ const char *search;
+ char *boundary;
+ unsigned int x;
+ int boundaryisquoted = FALSE;
+ int found_application_sdp = FALSE;
+ int found_end_of_headers = FALSE;
+
+ content_type = get_header(req, "Content-Type");
+
+ /* if the body contains only SDP, this is easy */
+ if (!strcasecmp(content_type, "application/sdp")) {
+ req->sdp_start = 0;
+ req->sdp_end = req->lines;
+ return req->lines ? 1 : 0;
+ }
+
+ /* if it's not multipart/mixed, there cannot be an SDP */
+ if (strncasecmp(content_type, "multipart/mixed", 15))
+ return 0;
+
+ /* if there is no boundary marker, it's invalid */
+ if ((search = strcasestr(content_type, ";boundary=")))
+ search += 10;
+ else if ((search = strcasestr(content_type, "; boundary=")))
+ search += 11;
+ else
+ return 0;
+
+ if (ast_strlen_zero(search))
+ return 0;
+
+ /* If the boundary is quoted with ", remove quote */
+ if (*search == '\"') {
+ search++;
+ boundaryisquoted = TRUE;
+ }
+
+ /* make a duplicate of the string, with two extra characters
+ at the beginning */
+ boundary = ast_strdupa(search - 2);
+ boundary[0] = boundary[1] = '-';
+ /* Remove final quote */
+ if (boundaryisquoted)
+ boundary[strlen(boundary) - 1] = '\0';
+
+ /* search for the boundary marker, the empty line delimiting headers from
+ sdp part and the end boundry if it exists */
+
+ for (x = 0; x < (req->lines ); x++) {
+ if(!strncasecmp(req->line[x], boundary, strlen(boundary))){
+ if(found_application_sdp && found_end_of_headers){
+ req->sdp_end = x-1;
+ return 1;
+ }
+ found_application_sdp = FALSE;
+ }
+ if(!strcasecmp(req->line[x], "Content-Type: application/sdp"))
+ found_application_sdp = TRUE;
+
+ if(strlen(req->line[x]) == 0 ){
+ if(found_application_sdp && !found_end_of_headers){
+ req->sdp_start = x;
+ found_end_of_headers = TRUE;
+ }
+ }
+ }
+ if(found_application_sdp && found_end_of_headers) {
+ req->sdp_end = x;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*! \brief Process SIP SDP offer, select formats and activate RTP channels
+ If offer is rejected, we will not change any properties of the call
+ Return 0 on success, a negative value on errors.
+ Must be called after find_sdp().
+*/
+static int process_sdp(struct sip_pvt *p, struct sip_request *req)
+{
+ const char *m; /* SDP media offer */
+ const char *c;
+ const char *a;
+ const char *o; /* Pointer to o= line */
+ char *o_copy; /* Copy of o= line */
+ char *token;
+ char host[258];
+ int len = -1;
+ int portno = -1; /*!< RTP Audio port number */
+ int vportno = -1; /*!< RTP Video port number */
+ int tportno = -1; /*!< RTP Text port number */
+ int udptlportno = -1;
+ int peert38capability = 0;
+ char s[256];
+ int old = 0;
+
+ /* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */
+ int peercapability = 0, peernoncodeccapability = 0;
+ int vpeercapability = 0, vpeernoncodeccapability = 0;
+ int tpeercapability = 0, tpeernoncodeccapability = 0;
+ struct sockaddr_in sin; /*!< media socket address */
+ struct sockaddr_in vsin; /*!< Video socket address */
+ struct sockaddr_in tsin; /*!< Text socket address */
+
+ const char *codecs;
+ struct hostent *hp; /*!< RTP Audio host IP */
+ struct hostent *vhp = NULL; /*!< RTP video host IP */
+ struct hostent *thp = NULL; /*!< RTP text host IP */
+ struct ast_hostent audiohp;
+ struct ast_hostent videohp;
+ struct ast_hostent texthp;
+ int codec;
+ int destiterator = 0;
+ int iterator;
+ int sendonly = -1;
+ int numberofports;
+ struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp; /* Buffers for codec handling */
+ int newjointcapability; /* Negotiated capability */
+ int newpeercapability;
+ int newnoncodeccapability;
+ int numberofmediastreams = 0;
+ int debug = sip_debug_test_pvt(p);
+
+ int found_rtpmap_codecs[32];
+ int last_rtpmap_codec=0;
+
+ char buf[BUFSIZ];
+ int rua_version;
+
+ if (!p->rtp) {
+ ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
+ return -1;
+ }
+
+ /* Initialize the temporary RTP structures we use to evaluate the offer from the peer */
+ newaudiortp = alloca(ast_rtp_alloc_size());
+ memset(newaudiortp, 0, ast_rtp_alloc_size());
+ ast_rtp_new_init(newaudiortp);
+ ast_rtp_pt_clear(newaudiortp);
+
+ newvideortp = alloca(ast_rtp_alloc_size());
+ memset(newvideortp, 0, ast_rtp_alloc_size());
+ ast_rtp_new_init(newvideortp);
+ ast_rtp_pt_clear(newvideortp);
+
+ newtextrtp = alloca(ast_rtp_alloc_size());
+ memset(newtextrtp, 0, ast_rtp_alloc_size());
+ ast_rtp_new_init(newtextrtp);
+ ast_rtp_pt_clear(newtextrtp);
+
+ /* Update our last rtprx when we receive an SDP, too */
+ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
+
+ /* Store the SDP version number of remote UA. This will allow us to
+ distinguish between session modifications and session refreshes. If
+ the remote UA does not send an incremented SDP version number in a
+ subsequent RE-INVITE then that means its not changing media session.
+ The RE-INVITE may have been sent to update connected party, remote
+ target or to refresh the session (Session-Timers). Asterisk must not
+ change media session and increment its own version number in answer
+ SDP in this case. */
+
+ o = get_sdp(req, "o");
+ if (ast_strlen_zero(o)) {
+ ast_log(LOG_WARNING, "SDP sytax error. SDP without an o= line\n");
+ return -1;
+ }
+
+ o_copy = ast_strdupa(o);
+ token = strsep(&o_copy, " "); /* Skip username */
+ if (!o_copy) {
+ ast_log(LOG_WARNING, "SDP sytax error in o= line username\n");
+ return -1;
+ }
+ token = strsep(&o_copy, " "); /* Skip session-id */
+ if (!o_copy) {
+ ast_log(LOG_WARNING, "SDP sytax error in o= line session-id\n");
+ return -1;
+ }
+ token = strsep(&o_copy, " "); /* Version */
+ if (!o_copy) {
+ ast_log(LOG_WARNING, "SDP sytax error in o= line\n");
+ return -1;
+ }
+ if (!sscanf(token, "%d", &rua_version)) {
+ ast_log(LOG_WARNING, "SDP sytax error in o= line version\n");
+ return -1;
+ }
+
+ if (p->sessionversion_remote < 0 || p->sessionversion_remote != rua_version) {
+ p->sessionversion_remote = rua_version;
+ p->session_modify = TRUE;
+ } else if (p->sessionversion_remote == rua_version) {
+ p->session_modify = FALSE;
+ ast_debug(2, "SDP version number same as previous SDP\n");
+ return 0;
+ }
+
+ /* Try to find first media stream */
+ m = get_sdp(req, "m");
+ destiterator = req->sdp_start;
+ c = get_sdp_iterate(&destiterator, req, "c");
+ if (ast_strlen_zero(m) || ast_strlen_zero(c)) {
+ ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
+ return -1;
+ }
+
+ /* Check for IPv4 address (not IPv6 yet) */
+ if (sscanf(c, "IN IP4 %256s", host) != 1) {
+ ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
+ return -1;
+ }
+
+ /* XXX This could block for a long time, and block the main thread! XXX */
+ hp = ast_gethostbyname(host, &audiohp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
+ return -1;
+ }
+ vhp = hp; /* Copy to video address as default too */
+ thp = hp; /* Copy to text address as default too */
+
+ iterator = req->sdp_start;
+ /* default: novideo and notext set */
+ p->novideo = TRUE;
+ p->notext = TRUE;
+
+ if (p->vrtp)
+ ast_rtp_pt_clear(newvideortp); /* Must be cleared in case no m=video line exists */
+
+ if (p->trtp)
+ ast_rtp_pt_clear(newtextrtp); /* Must be cleared in case no m=text line exists */
+
+ /* Find media streams in this SDP offer */
+ while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') {
+ int x;
+ int audio = FALSE;
+ int video = FALSE;
+ int text = FALSE;
+
+ numberofports = 1;
+ if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
+ (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) {
+ audio = TRUE;
+ numberofmediastreams++;
+ /* Found audio stream in this media definition */
+ portno = x;
+ /* Scan through the RTP payload types specified in a "m=" line: */
+ for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
+ if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
+ ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
+ return -1;
+ }
+ if (debug)
+ ast_verbose("Found RTP audio format %d\n", codec);
+ ast_rtp_set_m_type(newaudiortp, codec);
+ }
+ } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
+ (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) {
+ video = TRUE;
+ p->novideo = FALSE;
+ numberofmediastreams++;
+ vportno = x;
+ /* Scan through the RTP payload types specified in a "m=" line: */
+ for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
+ if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
+ ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
+ return -1;
+ }
+ if (debug)
+ ast_verbose("Found RTP video format %d\n", codec);
+ ast_rtp_set_m_type(newvideortp, codec);
+ }
+ } else if ((sscanf(m, "text %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
+ (sscanf(m, "text %d RTP/AVP %n", &x, &len) == 1)) {
+ text = TRUE;
+ p->notext = FALSE;
+ numberofmediastreams++;
+ tportno = x;
+ /* Scan through the RTP payload types specified in a "m=" line: */
+ for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
+ if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
+ ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
+ return -1;
+ }
+ if (debug)
+ ast_verbose("Found RTP text format %d\n", codec);
+ ast_rtp_set_m_type(newtextrtp, codec);
+ }
+ } else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1) ||
+ (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1) )) {
+ if (debug)
+ ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
+ udptlportno = x;
+ numberofmediastreams++;
+
+ if (p->owner && p->lastinvite) {
+ p->t38.state = T38_PEER_REINVITE; /* T38 Offered in re-invite from remote party */
+ ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>" );
+ } else {
+ p->t38.state = T38_PEER_DIRECT; /* T38 Offered directly from peer in first invite */
+ ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ }
+ } else
+ ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m);
+ if (numberofports > 1)
+ ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports);
+
+
+ /* Check for Media-description-level-address for audio */
+ c = get_sdp_iterate(&destiterator, req, "c");
+ if (!ast_strlen_zero(c)) {
+ if (sscanf(c, "IN IP4 %256s", host) != 1) {
+ ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c);
+ } else {
+ /* XXX This could block for a long time, and block the main thread! XXX */
+ if (audio) {
+ if ( !(hp = ast_gethostbyname(host, &audiohp))) {
+ ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in secondary c= line, '%s'\n", c);
+ return -2;
+ }
+ } else if (video) {
+ if (!(vhp = ast_gethostbyname(host, &videohp))) {
+ ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c);
+ return -2;
+ }
+ } else if (text) {
+ if (!(thp = ast_gethostbyname(host, &texthp))) {
+ ast_log(LOG_WARNING, "Unable to lookup RTP text host in secondary c= line, '%s'\n", c);
+ return -2;
+ }
+ }
+ }
+
+ }
+ }
+ if (portno == -1 && vportno == -1 && udptlportno == -1 && tportno == -1)
+ /* No acceptable offer found in SDP - we have no ports */
+ /* Do not change RTP or VRTP if this is a re-invite */
+ return -2;
+
+ if (numberofmediastreams > 3)
+ /* We have too many fax, audio and/or video and/or text media streams, fail this offer */
+ return -3;
+
+ /* RTP addresses and ports for audio and video */
+ sin.sin_family = AF_INET;
+ vsin.sin_family = AF_INET;
+ tsin.sin_family = AF_INET;
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ if (vhp)
+ memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr));
+ if (thp)
+ memcpy(&tsin.sin_addr, thp->h_addr, sizeof(tsin.sin_addr));
+
+ /* Setup UDPTL port number */
+ if (p->udptl) {
+ if (udptlportno > 0) {
+ sin.sin_port = htons(udptlportno);
+ ast_udptl_set_peer(p->udptl, &sin);
+ if (debug)
+ ast_debug(1,"Peer T.38 UDPTL is at port %s:%d\n",ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ } else {
+ ast_udptl_stop(p->udptl);
+ if (debug)
+ ast_debug(1, "Peer doesn't provide T.38 UDPTL\n");
+ }
+ }
+
+
+ if (p->rtp) {
+ if (portno > 0) {
+ sin.sin_port = htons(portno);
+ ast_rtp_set_peer(p->rtp, &sin);
+ if (debug)
+ ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ } else {
+ if (udptlportno > 0) {
+ if (debug)
+ ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid);
+ } else {
+ ast_rtp_stop(p->rtp);
+ if (debug)
+ ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid);
+ }
+ }
+ }
+ /* Setup video port number, assumes we have audio */
+ if (vportno != -1)
+ vsin.sin_port = htons(vportno);
+
+ /* Setup text port number, assumes we have audio */
+ if (tportno != -1)
+ tsin.sin_port = htons(tportno);
+
+ /* Next, scan through each "a=xxxx:" line, noting each
+ * specified RTP payload type (with corresponding MIME subtype):
+ */
+ /* XXX This needs to be done per media stream, since it's media stream specific */
+ iterator = req->sdp_start;
+ while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
+ char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
+ if (option_debug > 1) {
+ int breakout = FALSE;
+
+ /* If we're debugging, check for unsupported sdp options */
+ if (!strncasecmp(a, "rtcp:", (size_t) 5)) {
+ if (debug)
+ ast_verbose("Got unsupported a:rtcp in SDP offer \n");
+ breakout = TRUE;
+ } else if (!strncasecmp(a, "fmtp:", (size_t) 5)) {
+ /* Format parameters: Not supported */
+ /* Note: This is used for codec parameters, like bitrate for
+ G722 and video formats for H263 and H264
+ See RFC2327 for an example */
+ if (debug)
+ ast_verbose("Got unsupported a:fmtp in SDP offer \n");
+ breakout = TRUE;
+ } else if (!strncasecmp(a, "framerate:", (size_t) 10)) {
+ /* Video stuff: Not supported */
+ if (debug)
+ ast_verbose("Got unsupported a:framerate in SDP offer \n");
+ breakout = TRUE;
+ } else if (!strncasecmp(a, "maxprate:", (size_t) 9)) {
+ /* Video stuff: Not supported */
+ if (debug)
+ ast_verbose("Got unsupported a:maxprate in SDP offer \n");
+ breakout = TRUE;
+ } else if (!strncasecmp(a, "crypto:", (size_t) 7)) {
+ /* SRTP stuff, not yet supported */
+ if (debug)
+ ast_verbose("Got unsupported a:crypto in SDP offer \n");
+ breakout = TRUE;
+ }
+ if (breakout) /* We have a match, skip to next header */
+ continue;
+ }
+ if (!strcasecmp(a, "sendonly")) {
+ if (sendonly == -1)
+ sendonly = 1;
+ continue;
+ } else if (!strcasecmp(a, "inactive")) {
+ if (sendonly == -1)
+ sendonly = 2;
+ continue;
+ } else if (!strcasecmp(a, "sendrecv")) {
+ if (sendonly == -1)
+ sendonly = 0;
+ continue;
+ } else if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) {
+ char *tmp = strrchr(a, ':');
+ long int framing = 0;
+ if (tmp) {
+ tmp++;
+ framing = strtol(tmp, NULL, 10);
+ if (framing == LONG_MIN || framing == LONG_MAX) {
+ framing = 0;
+ ast_debug(1, "Can't read framing from SDP: %s\n", a);
+ }
+ }
+ if (framing && last_rtpmap_codec) {
+ if (p->autoframing) {
+ struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
+ int codec_n;
+ int format = 0;
+ for (codec_n = 0; codec_n < last_rtpmap_codec; codec_n++) {
+ format = ast_rtp_codec_getformat(found_rtpmap_codecs[codec_n]);
+ if (!format) /* non-codec or not found */
+ continue;
+ ast_debug(1, "Setting framing for %d to %ld\n", format, framing);
+ ast_codec_pref_setsize(pref, format, framing);
+ }
+ ast_rtp_codec_setpref(p->rtp, pref);
+ }
+ }
+ memset(&found_rtpmap_codecs, 0, sizeof(found_rtpmap_codecs));
+ last_rtpmap_codec = 0;
+ continue;
+ } else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) == 2) {
+ /* We have a rtpmap to handle */
+
+ /* Note: should really look at the 'freq' and '#chans' params too */
+ /* Note: This should all be done in the context of the m= above */
+ if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { /* Video */
+ if(ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0) != -1) {
+ if (debug)
+ ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec);
+ found_rtpmap_codecs[last_rtpmap_codec] = codec;
+ last_rtpmap_codec++;
+ } else {
+ ast_rtp_unset_m_type(newvideortp, codec);
+ if (debug)
+ ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
+ }
+ } else if (!strncasecmp(mimeSubtype, "T140",4)) { /* Text */
+ if (p->trtp) {
+ /* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */
+ ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0);
+ }
+ } else { /* Must be audio?? */
+ if(ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype,
+ ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0) != -1) {
+ if (debug)
+ ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec);
+ found_rtpmap_codecs[last_rtpmap_codec] = codec;
+ last_rtpmap_codec++;
+ } else {
+ ast_rtp_unset_m_type(newaudiortp, codec);
+ if (debug)
+ ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
+ }
+ }
+
+ }
+ }
+
+ if (udptlportno != -1) {
+ int found = 0, x;
+
+ old = 0;
+
+ /* Scan trough the a= lines for T38 attributes and set apropriate fileds */
+ iterator = req->sdp_start;
+ while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
+ if ((sscanf(a, "T38FaxMaxBuffer:%d", &x) == 1)) {
+ found = 1;
+ ast_debug(3, "MaxBufferSize:%d\n",x);
+ } else if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) {
+ found = 1;
+ ast_debug(3,"T38MaxBitRate: %d\n",x);
+ switch (x) {
+ case 14400:
+ peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 12000:
+ peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 9600:
+ peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 7200:
+ peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 4800:
+ peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 2400:
+ peert38capability |= T38FAX_RATE_2400;
+ break;
+ }
+ } else if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) {
+ found = 1;
+ ast_debug(3, "FaxVersion: %d\n",x);
+ if (x == 0)
+ peert38capability |= T38FAX_VERSION_0;
+ else if (x == 1)
+ peert38capability |= T38FAX_VERSION_1;
+ } else if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1)) {
+ found = 1;
+ ast_debug(3, "FaxMaxDatagram: %d\n",x);
+ ast_udptl_set_far_max_datagram(p->udptl, x);
+ ast_udptl_set_local_max_datagram(p->udptl, x);
+ } else if ((sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1)) {
+ found = 1;
+ ast_debug(3, "FillBitRemoval: %d\n",x);
+ if (x == 1)
+ peert38capability |= T38FAX_FILL_BIT_REMOVAL;
+ } else if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) {
+ found = 1;
+ ast_debug(3, "Transcoding MMR: %d\n",x);
+ if (x == 1)
+ peert38capability |= T38FAX_TRANSCODING_MMR;
+ }
+ if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) {
+ found = 1;
+ ast_debug(3, "Transcoding JBIG: %d\n",x);
+ if (x == 1)
+ peert38capability |= T38FAX_TRANSCODING_JBIG;
+ } else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) {
+ found = 1;
+ ast_debug(3, "RateManagement: %s\n", s);
+ if (!strcasecmp(s, "localTCF"))
+ peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
+ else if (!strcasecmp(s, "transferredTCF"))
+ peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
+ } else if ((sscanf(a, "T38FaxUdpEC:%255s", s) == 1)) {
+ found = 1;
+ ast_debug(3, "UDP EC: %s\n", s);
+ if (!strcasecmp(s, "t38UDPRedundancy")) {
+ peert38capability |= T38FAX_UDP_EC_REDUNDANCY;
+ ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
+ } else if (!strcasecmp(s, "t38UDPFEC")) {
+ peert38capability |= T38FAX_UDP_EC_FEC;
+ ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
+ } else {
+ peert38capability |= T38FAX_UDP_EC_NONE;
+ ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
+ }
+ }
+ }
+ if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */
+ p->t38.peercapability = peert38capability;
+ p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */
+ peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400);
+ p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */
+ }
+ if (debug)
+ ast_debug(1, "Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n",
+ p->t38.capability,
+ p->t38.peercapability,
+ p->t38.jointcapability);
+ } else {
+ p->t38.state = T38_DISABLED;
+ ast_debug(3, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ }
+
+ /* Now gather all of the codecs that we are asked for: */
+ ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability);
+ ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability);
+ ast_rtp_get_current_formats(newtextrtp, &tpeercapability, &tpeernoncodeccapability);
+
+ newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability);
+ newpeercapability = (peercapability | vpeercapability | tpeercapability);
+ newnoncodeccapability = p->noncodeccapability & peernoncodeccapability;
+
+
+ if (debug) {
+ /* shame on whoever coded this.... */
+ char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ], s5[BUFSIZ];
+
+ ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
+ ast_getformatname_multiple(s1, BUFSIZ, p->capability),
+ ast_getformatname_multiple(s2, BUFSIZ, peercapability),
+ ast_getformatname_multiple(s3, BUFSIZ, vpeercapability),
+ ast_getformatname_multiple(s4, BUFSIZ, tpeercapability),
+ ast_getformatname_multiple(s5, BUFSIZ, newjointcapability));
+
+ ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
+ ast_rtp_lookup_mime_multiple(s1, BUFSIZ, p->noncodeccapability, 0, 0),
+ ast_rtp_lookup_mime_multiple(s2, BUFSIZ, peernoncodeccapability, 0, 0),
+ ast_rtp_lookup_mime_multiple(s3, BUFSIZ, newnoncodeccapability, 0, 0));
+ }
+ if (!newjointcapability) {
+ /* If T.38 was not negotiated either, totally bail out... */
+ if (!p->t38.jointcapability) {
+ ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
+ /* Do NOT Change current setting */
+ return -1;
+ } else {
+ ast_debug(3, "Have T.38 but no audio codecs, accepting offer anyway\n");
+ return 0;
+ }
+ }
+
+ /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
+ they are acceptable */
+ p->jointcapability = newjointcapability; /* Our joint codec profile for this call */
+ p->peercapability = newpeercapability; /* The other sides capability in latest offer */
+ p->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */
+
+ ast_rtp_pt_copy(p->rtp, newaudiortp);
+ if (p->vrtp)
+ ast_rtp_pt_copy(p->vrtp, newvideortp);
+ if (p->trtp)
+ ast_rtp_pt_copy(p->trtp, newtextrtp);
+
+ if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
+ ast_clear_flag(&p->flags[0], SIP_DTMF);
+ if (newnoncodeccapability & AST_RTP_DTMF) {
+ /* XXX Would it be reasonable to drop the DSP at this point? XXX */
+ ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
+ /* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */
+ ast_rtp_setdtmf(p->rtp, 1);
+ ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+ } else {
+ ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
+ }
+ }
+
+ /* Setup audio port number */
+ if (p->rtp && sin.sin_port) {
+ ast_rtp_set_peer(p->rtp, &sin);
+ if (debug)
+ ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ }
+
+ /* Setup video port number */
+ if (p->vrtp && vsin.sin_port) {
+ ast_rtp_set_peer(p->vrtp, &vsin);
+ if (debug)
+ ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port));
+ }
+
+ /* Setup text port number */
+ if (p->trtp && tsin.sin_port) {
+ ast_rtp_set_peer(p->trtp, &tsin);
+ if (debug)
+ ast_verbose("Peer text RTP is at port %s:%d\n", ast_inet_ntoa(tsin.sin_addr), ntohs(tsin.sin_port));
+ }
+
+ /* Ok, we're going with this offer */
+ ast_debug(2, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, BUFSIZ, p->jointcapability));
+
+ if (!p->owner) /* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */
+ return 0;
+
+ ast_debug(4, "We have an owner, now see if we need to change this call\n");
+
+ if (!(p->owner->nativeformats & p->jointcapability) && (p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
+ if (debug) {
+ char s1[BUFSIZ], s2[BUFSIZ];
+ ast_debug(1, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n",
+ ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability),
+ ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats));
+ }
+ p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability) | (p->capability & tpeercapability);
+ ast_set_read_format(p->owner, p->owner->readformat);
+ ast_set_write_format(p->owner, p->owner->writeformat);
+ }
+
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin.sin_addr.s_addr && (!sendonly || sendonly == -1)) {
+ ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+ /* Activate a re-invite */
+ ast_queue_frame(p->owner, &ast_null_frame);
+ /* Queue Manager Unhold event */
+ append_history(p, "Unhold", "%s", req->data);
+ if (global_callevents)
+ manager_event(EVENT_FLAG_CALL, "Hold",
+ "Status: Off\r\n"
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n",
+ p->owner->name,
+ p->owner->uniqueid);
+ if (global_notifyhold)
+ sip_peer_hold(p, FALSE);
+ ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
+ } else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1)) {
+ int already_on_hold = ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD);
+ ast_queue_control_data(p->owner, AST_CONTROL_HOLD,
+ S_OR(p->mohsuggest, NULL),
+ !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+ if (sendonly)
+ ast_rtp_stop(p->rtp);
+ /* RTCP needs to go ahead, even if we're on hold!!! */
+ /* Activate a re-invite */
+ ast_queue_frame(p->owner, &ast_null_frame);
+ /* Queue Manager Hold event */
+ append_history(p, "Hold", "%s", req->data);
+ if (global_callevents && !ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
+ manager_event(EVENT_FLAG_CALL, "Hold",
+ "Status: On\r\n"
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n",
+ p->owner->name,
+ p->owner->uniqueid);
+ }
+ if (sendonly == 1) /* One directional hold (sendonly/recvonly) */
+ ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR);
+ else if (sendonly == 2) /* Inactive stream */
+ ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE);
+ else
+ ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ACTIVE);
+ if (global_notifyhold && !already_on_hold)
+ sip_peer_hold(p, TRUE);
+ }
+
+ return 0;
+}
+
+
+/*! \brief Add header to SIP message */
+static int add_header(struct sip_request *req, const char *var, const char *value)
+{
+ int maxlen = sizeof(req->data) - 4 - req->len; /* 4 bytes are for two \r\n ? */
+
+ if (req->headers == SIP_MAX_HEADERS) {
+ ast_log(LOG_WARNING, "Out of SIP header space\n");
+ return -1;
+ }
+
+ if (req->lines) {
+ ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
+ return -1;
+ }
+
+ if (maxlen <= 0) {
+ ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value);
+ return -1;
+ }
+
+ req->header[req->headers] = req->data + req->len;
+
+ if (compactheaders)
+ var = find_alias(var, var);
+
+ snprintf(req->header[req->headers], maxlen, "%s: %s\r\n", var, value);
+ req->len += strlen(req->header[req->headers]);
+ req->headers++;
+
+ return 0;
+}
+
+/*! \brief Add 'Content-Length' header to SIP message */
+static int add_header_contentLength(struct sip_request *req, int len)
+{
+ char clen[10];
+
+ snprintf(clen, sizeof(clen), "%d", len);
+ return add_header(req, "Content-Length", clen);
+}
+
+/*! \brief Add content (not header) to SIP message */
+static int add_line(struct sip_request *req, const char *line)
+{
+ if (req->lines == SIP_MAX_LINES) {
+ ast_log(LOG_WARNING, "Out of SIP line space\n");
+ return -1;
+ }
+ if (!req->lines) {
+ /* Add extra empty return */
+ ast_copy_string(req->data + req->len, "\r\n", sizeof(req->data) - req->len);
+ req->len += strlen(req->data + req->len);
+ }
+ if (req->len >= sizeof(req->data) - 4) {
+ ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
+ return -1;
+ }
+ req->line[req->lines] = req->data + req->len;
+ snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
+ req->len += strlen(req->line[req->lines]);
+ req->lines++;
+ return 0;
+}
+
+/*! \brief Copy one header field from one request to another */
+static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field)
+{
+ const char *tmp = get_header(orig, field);
+
+ if (!ast_strlen_zero(tmp)) /* Add what we're responding to */
+ return add_header(req, field, tmp);
+ ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
+ return -1;
+}
+
+/*! \brief Copy all headers from one request to another */
+static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field)
+{
+ int start = 0;
+ int copied = 0;
+ for (;;) {
+ const char *tmp = __get_header(orig, field, &start);
+
+ if (ast_strlen_zero(tmp))
+ break;
+ /* Add what we're responding to */
+ add_header(req, field, tmp);
+ copied++;
+ }
+ return copied ? 0 : -1;
+}
+
+/*! \brief Copy SIP VIA Headers from the request to the response
+\note If the client indicates that it wishes to know the port we received from,
+ it adds ;rport without an argument to the topmost via header. We need to
+ add the port number (from our point of view) to that parameter.
+\verbatim
+ We always add ;received=<ip address> to the topmost via header.
+\endverbatim
+ Received: RFC 3261, rport RFC 3581 */
+static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field)
+{
+ int copied = 0;
+ int start = 0;
+
+ for (;;) {
+ char new[256];
+ const char *oh = __get_header(orig, field, &start);
+
+ if (ast_strlen_zero(oh))
+ break;
+
+ if (!copied) { /* Only check for empty rport in topmost via header */
+ char leftmost[256], *others, *rport;
+
+ /* Only work on leftmost value */
+ ast_copy_string(leftmost, oh, sizeof(leftmost));
+ others = strchr(leftmost, ',');
+ if (others)
+ *others++ = '\0';
+
+ /* Find ;rport; (empty request) */
+ rport = strstr(leftmost, ";rport");
+ if (rport && *(rport+6) == '=')
+ rport = NULL; /* We already have a parameter to rport */
+
+ /* Check rport if NAT=yes or NAT=rfc3581 (which is the default setting) */
+ if (rport && ((ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_ALWAYS) || (ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_RFC3581))) {
+ /* We need to add received port - rport */
+ char *end;
+
+ rport = strstr(leftmost, ";rport");
+
+ if (rport) {
+ end = strchr(rport + 1, ';');
+ if (end)
+ memmove(rport, end, strlen(end) + 1);
+ else
+ *rport = '\0';
+ }
+
+ /* Add rport to first VIA header if requested */
+ snprintf(new, sizeof(new), "%s;received=%s;rport=%d%s%s",
+ leftmost, ast_inet_ntoa(p->recv.sin_addr),
+ ntohs(p->recv.sin_port),
+ others ? "," : "", others ? others : "");
+ } else {
+ /* We should *always* add a received to the topmost via */
+ snprintf(new, sizeof(new), "%s;received=%s%s%s",
+ leftmost, ast_inet_ntoa(p->recv.sin_addr),
+ others ? "," : "", others ? others : "");
+ }
+ oh = new; /* the header to copy */
+ } /* else add the following via headers untouched */
+ add_header(req, field, oh);
+ copied++;
+ }
+ if (!copied) {
+ ast_log(LOG_NOTICE, "No header field '%s' present to copy\n", field);
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief Add route header into request per learned route */
+static void add_route(struct sip_request *req, struct sip_route *route)
+{
+ char r[BUFSIZ*2], *p;
+ int n, rem = sizeof(r);
+
+ if (!route)
+ return;
+
+ p = r;
+ for (;route ; route = route->next) {
+ n = strlen(route->hop);
+ if (rem < n+3) /* we need room for ",<route>" */
+ break;
+ if (p != r) { /* add a separator after fist route */
+ *p++ = ',';
+ --rem;
+ }
+ *p++ = '<';
+ ast_copy_string(p, route->hop, rem); /* cannot fail */
+ p += n;
+ *p++ = '>';
+ rem -= (n+2);
+ }
+ *p = '\0';
+ add_header(req, "Route", r);
+}
+
+/*! \brief Set destination from SIP URI */
+static void set_destination(struct sip_pvt *p, char *uri)
+{
+ char *h, *maddr, hostname[256];
+ int port, hn;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int debug=sip_debug_test_pvt(p);
+
+ /* Parse uri to h (host) and port - uri is already just the part inside the <> */
+ /* general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...] */
+
+ if (debug)
+ ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
+
+ /* Find and parse hostname */
+ h = strchr(uri, '@');
+ if (h)
+ ++h;
+ else {
+ h = uri;
+ if (strncasecmp(h, "sip:", 4) == 0)
+ h += 4;
+ else if (strncasecmp(h, "sips:", 5) == 0)
+ h += 5;
+ }
+ hn = strcspn(h, ":;>") + 1;
+ if (hn > sizeof(hostname))
+ hn = sizeof(hostname);
+ ast_copy_string(hostname, h, hn);
+ /* XXX bug here if string has been trimmed to sizeof(hostname) */
+ h += hn - 1;
+
+ /* Is "port" present? if not default to STANDARD_SIP_PORT */
+ if (*h == ':') {
+ /* Parse port */
+ ++h;
+ port = strtol(h, &h, 10);
+ }
+ else
+ port = STANDARD_SIP_PORT;
+
+ /* Got the hostname:port - but maybe there's a "maddr=" to override address? */
+ maddr = strstr(h, "maddr=");
+ if (maddr) {
+ maddr += 6;
+ hn = strspn(maddr, "0123456789.") + 1;
+ if (hn > sizeof(hostname))
+ hn = sizeof(hostname);
+ ast_copy_string(hostname, maddr, hn);
+ }
+
+ hp = ast_gethostbyname(hostname, &ahp);
+ if (hp == NULL) {
+ ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname);
+ return;
+ }
+ p->sa.sin_family = AF_INET;
+ memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
+ p->sa.sin_port = htons(port);
+ if (debug)
+ ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(p->sa.sin_addr), port);
+}
+
+/*! \brief Initialize SIP response, based on SIP request */
+static int init_resp(struct sip_request *resp, const char *msg)
+{
+ /* Initialize a response */
+ memset(resp, 0, sizeof(*resp));
+ resp->method = SIP_RESPONSE;
+ resp->header[0] = resp->data;
+ snprintf(resp->header[0], sizeof(resp->data), "SIP/2.0 %s\r\n", msg);
+ resp->len = strlen(resp->header[0]);
+ resp->headers++;
+ return 0;
+}
+
+/*! \brief Initialize SIP request */
+static int init_req(struct sip_request *req, int sipmethod, const char *recip)
+{
+ /* Initialize a request */
+ memset(req, 0, sizeof(*req));
+ req->method = sipmethod;
+ req->header[0] = req->data;
+ snprintf(req->header[0], sizeof(req->data), "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip);
+ req->len = strlen(req->header[0]);
+ req->headers++;
+ return 0;
+}
+
+
+/*! \brief Prepare SIP response packet */
+static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req)
+{
+ char newto[256];
+ const char *ot;
+
+ init_resp(resp, msg);
+ copy_via_headers(p, resp, req, "Via");
+ if (msg[0] == '1' || msg[0] == '2')
+ copy_all_header(resp, req, "Record-Route");
+ copy_header(resp, req, "From");
+ ot = get_header(req, "To");
+ if (!strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) {
+ /* Add the proper tag if we don't have it already. If they have specified
+ their tag, use it. Otherwise, use our own tag */
+ if (!ast_strlen_zero(p->theirtag) && ast_test_flag(&p->flags[0], SIP_OUTGOING))
+ snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
+ else if (p->tag && !ast_test_flag(&p->flags[0], SIP_OUTGOING))
+ snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
+ else
+ ast_copy_string(newto, ot, sizeof(newto));
+ ot = newto;
+ }
+ add_header(resp, "To", ot);
+ copy_header(resp, req, "Call-ID");
+ copy_header(resp, req, "CSeq");
+ if (!ast_strlen_zero(global_useragent))
+ add_header(resp, "User-Agent", global_useragent);
+ add_header(resp, "Allow", ALLOWED_METHODS);
+ add_header(resp, "Supported", SUPPORTED_EXTENSIONS);
+
+ /* Add Session-Timers related headers if the feature is active for this session */
+ if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_active_peer_ua == TRUE) {
+ char se_hdr[256];
+ snprintf(se_hdr, sizeof(se_hdr), "%d;refresher=%s", p->stimer->st_interval,
+ strefresher2str(p->stimer->st_ref));
+ add_header(resp, "Require", "timer");
+ add_header(resp, "Session-Expires", se_hdr);
+ }
+
+ if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) {
+ /* For registration responses, we also need expiry and
+ contact info */
+ char tmp[256];
+
+ snprintf(tmp, sizeof(tmp), "%d", p->expiry);
+ add_header(resp, "Expires", tmp);
+ if (p->expiry) { /* Only add contact if we have an expiry time */
+ char contact[BUFSIZ];
+ snprintf(contact, sizeof(contact), "%s;expires=%d", p->our_contact, p->expiry);
+ add_header(resp, "Contact", contact); /* Not when we unregister */
+ }
+ } else if (msg[0] != '4' && !ast_strlen_zero(p->our_contact)) {
+ add_header(resp, "Contact", p->our_contact);
+ }
+
+ if (!ast_strlen_zero(p->url)) {
+ add_header(resp, "Access-URL", p->url);
+ ast_string_field_set(p, url, NULL);
+ }
+
+ return 0;
+}
+
+/*! \brief Initialize a SIP request message (not the initial one in a dialog) */
+static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch)
+{
+ struct sip_request *orig = &p->initreq;
+ char stripped[80];
+ char tmp[80];
+ char newto[256];
+ const char *c;
+ const char *ot, *of;
+ int is_strict = FALSE; /*!< Strict routing flag */
+ int is_outbound = ast_test_flag(&p->flags[0], SIP_OUTGOING); /* Session direction */
+
+ memset(req, 0, sizeof(struct sip_request));
+
+ snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text);
+
+ if (!seqno) {
+ p->ocseq++;
+ seqno = p->ocseq;
+ }
+
+ if (newbranch) {
+ p->branch ^= ast_random();
+ build_via(p);
+ }
+
+ /* Check for strict or loose router */
+ if (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop,";lr") == NULL) {
+ is_strict = TRUE;
+ if (sipdebug)
+ ast_debug(1, "Strict routing enforced for session %s\n", p->callid);
+ }
+
+ if (sipmethod == SIP_CANCEL)
+ c = p->initreq.rlPart2; /* Use original URI */
+ else if (sipmethod == SIP_ACK) {
+ /* Use URI from Contact: in 200 OK (if INVITE)
+ (we only have the contacturi on INVITEs) */
+ if (!ast_strlen_zero(p->okcontacturi))
+ c = is_strict ? p->route->hop : p->okcontacturi;
+ else
+ c = p->initreq.rlPart2;
+ } else if (!ast_strlen_zero(p->okcontacturi))
+ c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */
+ else if (!ast_strlen_zero(p->uri))
+ c = p->uri;
+ else {
+ char *n;
+ /* We have no URI, use To: or From: header as URI (depending on direction) */
+ ast_copy_string(stripped, get_header(orig, is_outbound ? "To" : "From"),
+ sizeof(stripped));
+ n = get_in_brackets(stripped);
+ c = remove_uri_parameters(n);
+ }
+ init_req(req, sipmethod, c);
+
+ snprintf(tmp, sizeof(tmp), "%d %s", seqno, sip_methods[sipmethod].text);
+
+ add_header(req, "Via", p->via);
+ if (p->route) {
+ set_destination(p, p->route->hop);
+ add_route(req, is_strict ? p->route->next : p->route);
+ }
+ add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
+
+ ot = get_header(orig, "To");
+ of = get_header(orig, "From");
+
+ /* Add tag *unless* this is a CANCEL, in which case we need to send it exactly
+ as our original request, including tag (or presumably lack thereof) */
+ if (!strcasestr(ot, "tag=") && sipmethod != SIP_CANCEL) {
+ /* Add the proper tag if we don't have it already. If they have specified
+ their tag, use it. Otherwise, use our own tag */
+ if (is_outbound && !ast_strlen_zero(p->theirtag))
+ snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
+ else if (!is_outbound)
+ snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
+ else
+ snprintf(newto, sizeof(newto), "%s", ot);
+ ot = newto;
+ }
+
+ if (is_outbound) {
+ add_header(req, "From", of);
+ add_header(req, "To", ot);
+ } else {
+ add_header(req, "From", ot);
+ add_header(req, "To", of);
+ }
+ /* Do not add Contact for MESSAGE, BYE and Cancel requests */
+ if (sipmethod != SIP_BYE && sipmethod != SIP_CANCEL && sipmethod != SIP_MESSAGE)
+ add_header(req, "Contact", p->our_contact);
+
+ copy_header(req, orig, "Call-ID");
+ add_header(req, "CSeq", tmp);
+
+ if (!ast_strlen_zero(global_useragent))
+ add_header(req, "User-Agent", global_useragent);
+
+ if (!ast_strlen_zero(p->rpid))
+ add_header(req, "Remote-Party-ID", p->rpid);
+
+ if (!ast_strlen_zero(p->url)) {
+ add_header(req, "Access-URL", p->url);
+ ast_string_field_set(p, url, NULL);
+ }
+
+ /* Add Session-Timers related headers if the feature is active for this session */
+ if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_active_peer_ua == TRUE) {
+ char se_hdr[256];
+ snprintf(se_hdr, sizeof(se_hdr), "%d;refresher=%s", p->stimer->st_interval,
+ strefresher2str(p->stimer->st_ref));
+ add_header(req, "Require", "timer");
+ add_header(req, "Session-Expires", se_hdr);
+ snprintf(se_hdr, sizeof(se_hdr), "%d", st_get_se(p, FALSE));
+ add_header(req, "Min-SE", se_hdr);
+ }
+
+ return 0;
+}
+
+/*! \brief Base transmit response function */
+static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
+{
+ struct sip_request resp;
+ int seqno = 0;
+
+ if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
+ ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
+ return -1;
+ }
+ respprep(&resp, p, msg, req);
+ add_header_contentLength(&resp, 0);
+ /* If we are cancelling an incoming invite for some reason, add information
+ about the reason why we are doing this in clear text */
+ if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) {
+ char buf[10];
+
+ add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
+ snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
+ add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
+ }
+ return send_response(p, &resp, reliable, seqno);
+}
+
+static int temp_pvt_init(void *data)
+{
+ struct sip_pvt *p = data;
+
+ p->do_history = 0; /* XXX do we need it ? isn't already all 0 ? */
+ return ast_string_field_init(p, 512);
+}
+
+static void temp_pvt_cleanup(void *data)
+{
+ struct sip_pvt *p = data;
+
+ ast_string_field_free_memory(p);
+
+ ast_free(data);
+}
+
+/*! \brief Transmit response, no retransmits, using a temporary pvt structure */
+static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg)
+{
+ struct sip_pvt *p = NULL;
+
+ if (!(p = ast_threadstorage_get(&ts_temp_pvt, sizeof(*p)))) {
+ ast_log(LOG_NOTICE, "Failed to get temporary pvt\n");
+ return -1;
+ }
+
+ /* XXX the structure may be dirty from previous usage.
+ * Here we should state clearly how we should reinitialize it
+ * before using it.
+ * E.g. certainly the threadstorage should be left alone,
+ * but other thihngs such as flags etc. maybe need cleanup ?
+ */
+
+ /* Initialize the bare minimum */
+ p->method = intended_method;
+
+ if (!sin)
+ p->ourip = internip;
+ else {
+ p->sa = *sin;
+ ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
+ }
+
+ p->branch = ast_random();
+ make_our_tag(p->tag, sizeof(p->tag));
+ p->ocseq = INITIAL_CSEQ;
+
+ if (useglobal_nat && sin) {
+ ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT);
+ p->recv = *sin;
+ do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
+ }
+
+ ast_string_field_set(p, fromdomain, default_fromdomain);
+ build_via(p);
+ ast_string_field_set(p, callid, callid);
+
+ p->socket.lock = req->socket.lock;
+ p->socket.type = req->socket.type;
+ p->socket.fd = req->socket.fd;
+ p->socket.port = req->socket.port;
+ p->socket.ser = req->socket.ser;
+
+ /* Use this temporary pvt structure to send the message */
+ __transmit_response(p, msg, req, XMIT_UNRELIABLE);
+
+ /* Free the string fields, but not the pool space */
+ ast_string_field_init(p, 0);
+
+ return 0;
+}
+
+/*! \brief Transmit response, no retransmits */
+static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req)
+{
+ return __transmit_response(p, msg, req, XMIT_UNRELIABLE);
+}
+
+/*! \brief Transmit response, no retransmits */
+static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported)
+{
+ struct sip_request resp;
+ respprep(&resp, p, msg, req);
+ append_date(&resp);
+ add_header(&resp, "Unsupported", unsupported);
+ add_header_contentLength(&resp, 0);
+ return send_response(p, &resp, XMIT_UNRELIABLE, 0);
+}
+
+/*! \brief Transmit 422 response with Min-SE header (Session-Timers) */
+static int transmit_response_with_minse(struct sip_pvt *p, const char *msg, const struct sip_request *req, int minse_int)
+{
+ struct sip_request resp;
+ char minse_str[20];
+
+ respprep(&resp, p, msg, req);
+ append_date(&resp);
+
+ snprintf(minse_str, sizeof(minse_str), "%d", minse_int);
+ add_header(&resp, "Min-SE", minse_str);
+
+ add_header_contentLength(&resp, 0);
+ return send_response(p, &resp, XMIT_UNRELIABLE, 0);
+}
+
+
+/*! \brief Transmit response, Make sure you get an ACK
+ This is only used for responses to INVITEs, where we need to make sure we get an ACK
+*/
+static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req)
+{
+ return __transmit_response(p, msg, req, XMIT_CRITICAL);
+}
+
+/*! \brief Append date to SIP message */
+static void append_date(struct sip_request *req)
+{
+ char tmpdat[256];
+ struct tm tm;
+ time_t t = time(NULL);
+
+ gmtime_r(&t, &tm);
+ strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T GMT", &tm);
+ add_header(req, "Date", tmpdat);
+}
+
+/*! \brief Append date and content length before transmitting response */
+static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req)
+{
+ struct sip_request resp;
+ respprep(&resp, p, msg, req);
+ append_date(&resp);
+ add_header_contentLength(&resp, 0);
+ return send_response(p, &resp, XMIT_UNRELIABLE, 0);
+}
+
+/*! \brief Append Accept header, content length before transmitting response */
+static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
+{
+ struct sip_request resp;
+ respprep(&resp, p, msg, req);
+ add_header(&resp, "Accept", "application/sdp");
+ add_header_contentLength(&resp, 0);
+ return send_response(p, &resp, reliable, 0);
+}
+
+/*! \brief Respond with authorization request */
+static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale)
+{
+ struct sip_request resp;
+ char tmp[512];
+ int seqno = 0;
+
+ if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
+ ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
+ return -1;
+ }
+ /* Stale means that they sent us correct authentication, but
+ based it on an old challenge (nonce) */
+ snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%s\"%s", global_realm, randdata, stale ? ", stale=true" : "");
+ respprep(&resp, p, msg, req);
+ add_header(&resp, header, tmp);
+ add_header_contentLength(&resp, 0);
+ append_history(p, "AuthChal", "Auth challenge sent for %s - nc %d", p->username, p->noncecount);
+ return send_response(p, &resp, reliable, seqno);
+}
+
+/*! \brief Add text body to SIP message */
+static int add_text(struct sip_request *req, const char *text)
+{
+ /* XXX Convert \n's to \r\n's XXX */
+ add_header(req, "Content-Type", "text/plain");
+ add_header_contentLength(req, strlen(text));
+ add_line(req, text);
+ return 0;
+}
+
+/*! \brief Add DTMF INFO tone to sip message
+ Mode = 0 for application/dtmf-relay (Cisco)
+ 1 for application/dtmf
+*/
+static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode)
+{
+ char tmp[256];
+ int event;
+ if (mode) {
+ /* Application/dtmf short version used by some implementations */
+ if (digit == '*')
+ event = 10;
+ else if (digit == '#')
+ event = 11;
+ else if ((digit >= 'A') && (digit <= 'D'))
+ event = 12 + digit - 'A';
+ else
+ event = atoi(&digit);
+ snprintf(tmp, sizeof(tmp), "%d\r\n", event);
+ add_header(req, "Content-Type", "application/dtmf");
+ add_header_contentLength(req, strlen(tmp));
+ add_line(req, tmp);
+ } else {
+ /* Application/dtmf-relay as documented by Cisco */
+ snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=%u\r\n", digit, duration);
+ add_header(req, "Content-Type", "application/dtmf-relay");
+ add_header_contentLength(req, strlen(tmp));
+ add_line(req, tmp);
+ }
+ return 0;
+}
+
+/*! \brief add XML encoded media control with update
+ \note XML: The only way to turn 0 bits of information into a few hundred. (markster) */
+static int add_vidupdate(struct sip_request *req)
+{
+ const char *xml_is_a_huge_waste_of_space =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n"
+ " <media_control>\r\n"
+ " <vc_primitive>\r\n"
+ " <to_encoder>\r\n"
+ " <picture_fast_update>\r\n"
+ " </picture_fast_update>\r\n"
+ " </to_encoder>\r\n"
+ " </vc_primitive>\r\n"
+ " </media_control>\r\n";
+ add_header(req, "Content-Type", "application/media_control+xml");
+ add_header_contentLength(req, strlen(xml_is_a_huge_waste_of_space));
+ add_line(req, xml_is_a_huge_waste_of_space);
+ return 0;
+}
+
+/*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
+static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
+ struct ast_str **m_buf, struct ast_str **a_buf,
+ int debug, int *min_packet_size)
+{
+ int rtp_code;
+ struct ast_format_list fmt;
+
+
+ if (debug)
+ ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
+ if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1)
+ return;
+
+ if (p->rtp) {
+ struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
+ fmt = ast_codec_pref_getsize(pref, codec);
+ } else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */
+ return;
+ ast_str_append(m_buf, 0, " %d", rtp_code);
+ ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
+ ast_rtp_lookup_mime_subtype(1, codec,
+ ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0),
+ sample_rate);
+ if (codec == AST_FORMAT_G729A) {
+ /* Indicate that we don't support VAD (G.729 annex B) */
+ ast_str_append(a_buf, 0, "a=fmtp:%d annexb=no\r\n", rtp_code);
+ } else if (codec == AST_FORMAT_G723_1) {
+ /* Indicate that we don't support VAD (G.723.1 annex A) */
+ ast_str_append(a_buf, 0, "a=fmtp:%d annexa=no\r\n", rtp_code);
+ } else if (codec == AST_FORMAT_ILBC) {
+ /* Add information about us using only 20/30 ms packetization */
+ ast_str_append(a_buf, 0, "a=fmtp:%d mode=%d\r\n", rtp_code, fmt.cur_ms);
+ }
+
+ if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size))
+ *min_packet_size = fmt.cur_ms;
+
+ /* Our first codec packetization processed cannot be zero */
+ if ((*min_packet_size)==0 && fmt.cur_ms)
+ *min_packet_size = fmt.cur_ms;
+}
+
+/*! \brief Add video codec offer to SDP offer/answer body in INVITE or 200 OK */
+/* This is different to the audio one now so we can add more caps later */
+static void add_vcodec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
+ struct ast_str **m_buf, struct ast_str **a_buf,
+ int debug, int *min_packet_size)
+{
+ int rtp_code;
+
+ if (!p->vrtp)
+ return;
+
+ if (debug)
+ ast_verbose("Adding video codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
+
+ if ((rtp_code = ast_rtp_lookup_code(p->vrtp, 1, codec)) == -1)
+ return;
+
+ ast_str_append(m_buf, 0, " %d", rtp_code);
+ ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
+ ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate);
+ /* Add fmtp code here */
+}
+
+/*! \brief Add text codec offer to SDP offer/answer body in INVITE or 200 OK */
+static void add_tcodec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
+ struct ast_str **m_buf, struct ast_str **a_buf,
+ int debug, int *min_packet_size)
+{
+ int rtp_code;
+
+ if (!p->trtp)
+ return;
+
+ if (debug)
+ ast_verbose("Adding text codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
+
+ if ((rtp_code = ast_rtp_lookup_code(p->trtp, 1, codec)) == -1)
+ return;
+
+ ast_str_append(m_buf, 0, " %d", rtp_code);
+ ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
+ ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate);
+ /* Add fmtp code here */
+}
+
+
+/*! \brief Get Max T.38 Transmission rate from T38 capabilities */
+static int t38_get_rate(int t38cap)
+{
+ int maxrate = (t38cap & (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400));
+
+ if (maxrate & T38FAX_RATE_14400) {
+ ast_debug(2, "T38MaxFaxRate 14400 found\n");
+ return 14400;
+ } else if (maxrate & T38FAX_RATE_12000) {
+ ast_debug(2, "T38MaxFaxRate 12000 found\n");
+ return 12000;
+ } else if (maxrate & T38FAX_RATE_9600) {
+ ast_debug(2, "T38MaxFaxRate 9600 found\n");
+ return 9600;
+ } else if (maxrate & T38FAX_RATE_7200) {
+ ast_debug(2, "T38MaxFaxRate 7200 found\n");
+ return 7200;
+ } else if (maxrate & T38FAX_RATE_4800) {
+ ast_debug(2, "T38MaxFaxRate 4800 found\n");
+ return 4800;
+ } else if (maxrate & T38FAX_RATE_2400) {
+ ast_debug(2, "T38MaxFaxRate 2400 found\n");
+ return 2400;
+ } else {
+ ast_debug(2, "Strange, T38MaxFaxRate NOT found in peers T38 SDP.\n");
+ return 0;
+ }
+}
+
+/*! \brief Add T.38 Session Description Protocol message */
+static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p)
+{
+ int len = 0;
+ int x = 0;
+ struct sockaddr_in udptlsin;
+ struct ast_str *m_modem = ast_str_alloca(1024);
+ struct ast_str *a_modem = ast_str_alloca(1024);
+ struct sockaddr_in udptldest = { 0, };
+ int debug;
+
+ debug = sip_debug_test_pvt(p);
+ len = 0;
+ if (!p->udptl) {
+ ast_log(LOG_WARNING, "No way to add SDP without an UDPTL structure\n");
+ return -1;
+ }
+
+ if (!p->sessionid) {
+ p->sessionid = (int)ast_random();
+ p->sessionversion = p->sessionid;
+ } else
+ p->sessionversion++;
+
+ /* Our T.38 end is */
+ ast_udptl_get_us(p->udptl, &udptlsin);
+
+ /* Determine T.38 UDPTL destination */
+ if (p->udptlredirip.sin_addr.s_addr) {
+ udptldest.sin_port = p->udptlredirip.sin_port;
+ udptldest.sin_addr = p->udptlredirip.sin_addr;
+ } else {
+ udptldest.sin_addr = p->ourip.sin_addr;
+ udptldest.sin_port = udptlsin.sin_port;
+ }
+
+ if (debug)
+ ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(udptlsin.sin_port));
+
+ /* We break with the "recommendation" and send our IP, in order that our
+ peer doesn't have to ast_gethostbyname() us */
+
+ if (debug) {
+ ast_debug(1, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
+ p->t38.capability,
+ p->t38.peercapability,
+ p->t38.jointcapability);
+ }
+ ast_str_append(&m_modem, 0, "v=0\r\n");
+ ast_str_append(&m_modem, 0, "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner , p->sessionid, p->sessionversion, ast_inet_ntoa(udptldest.sin_addr));
+ ast_str_append(&m_modem, 0, "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
+ ast_str_append(&m_modem, 0, "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr));
+ ast_str_append(&m_modem, 0, "t=0 0\r\n");
+ ast_str_append(&m_modem, 0, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port));
+
+ if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
+ ast_str_append(&a_modem, 0, "a=T38FaxVersion:0\r\n");
+ if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
+ ast_str_append(&a_modem, 0, "a=T38FaxVersion:1\r\n");
+ if ((x = t38_get_rate(p->t38.jointcapability)))
+ ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n",x);
+ ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval:%d\r\n", (p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) ? 1 : 0);
+ ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR:%d\r\n", (p->t38.jointcapability & T38FAX_TRANSCODING_MMR) ? 1 : 0);
+ ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG:%d\r\n", (p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) ? 1 : 0);
+ ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
+ x = ast_udptl_get_local_max_datagram(p->udptl);
+ ast_str_append(&a_modem, 0, "a=T38FaxMaxBuffer:%d\r\n",x);
+ ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n",x);
+ if (p->t38.jointcapability != T38FAX_UDP_EC_NONE)
+ ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
+ len = m_modem->used + a_modem->used;
+ add_header(resp, "Content-Type", "application/sdp");
+ add_header_contentLength(resp, len);
+ add_line(resp, m_modem->str);
+ add_line(resp, a_modem->str);
+
+ /* Update lastrtprx when we send our SDP */
+ p->lastrtprx = p->lastrtptx = time(NULL);
+
+ return 0;
+}
+
+
+/*! \brief Add RFC 2833 DTMF offer to SDP */
+static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
+ struct ast_str **m_buf, struct ast_str **a_buf,
+ int debug)
+{
+ int rtp_code;
+
+ if (debug)
+ ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0));
+ if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1)
+ return;
+
+ ast_str_append(m_buf, 0, " %d", rtp_code);
+ ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
+ ast_rtp_lookup_mime_subtype(0, format, 0),
+ sample_rate);
+ if (format == AST_RTP_DTMF) /* Indicate we support DTMF and FLASH... */
+ ast_str_append(a_buf, 0, "a=fmtp:%d 0-16\r\n", rtp_code);
+}
+
+/*! \brief Set all IP media addresses for this call
+ \note called from add_sdp()
+*/
+static void get_our_media_address(struct sip_pvt *p, int needvideo,
+ struct sockaddr_in *sin, struct sockaddr_in *vsin, struct sockaddr_in *tsin,
+ struct sockaddr_in *dest, struct sockaddr_in *vdest)
+{
+ /* First, get our address */
+ ast_rtp_get_us(p->rtp, sin);
+ if (p->vrtp)
+ ast_rtp_get_us(p->vrtp, vsin);
+ if (p->trtp)
+ ast_rtp_get_us(p->trtp, tsin);
+
+ /* Now, try to figure out where we want them to send data */
+ /* Is this a re-invite to move the media out, then use the original offer from caller */
+ if (p->redirip.sin_addr.s_addr) { /* If we have a redirection IP, use it */
+ dest->sin_port = p->redirip.sin_port;
+ dest->sin_addr = p->redirip.sin_addr;
+ } else {
+ dest->sin_addr = p->ourip.sin_addr;
+ dest->sin_port = sin->sin_port;
+ }
+ if (needvideo) {
+ /* Determine video destination */
+ if (p->vredirip.sin_addr.s_addr) {
+ vdest->sin_addr = p->vredirip.sin_addr;
+ vdest->sin_port = p->vredirip.sin_port;
+ } else {
+ vdest->sin_addr = p->ourip.sin_addr;
+ vdest->sin_port = vsin->sin_port;
+ }
+ }
+
+}
+
+#define SDP_SAMPLE_RATE(x) (x == AST_FORMAT_G722) ? 16000 : 8000
+
+/*! \brief Add Session Description Protocol message
+
+ If oldsdp is TRUE, then the SDP version number is not incremented. This mechanism
+ is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions
+ without modifying the media session in any way.
+*/
+static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp)
+{
+ int len = 0;
+ int alreadysent = 0;
+
+ struct sockaddr_in sin;
+ struct sockaddr_in vsin;
+ struct sockaddr_in tsin;
+ struct sockaddr_in dest;
+ struct sockaddr_in vdest = { 0, };
+ struct sockaddr_in tdest = { 0, };
+
+ /* SDP fields */
+ char *version = "v=0\r\n"; /* Protocol version */
+ char subject[256]; /* Subject of the session */
+ char owner[256]; /* Session owner/creator */
+ char connection[256]; /* Connection data */
+ char *stime = "t=0 0\r\n"; /* Time the session is active */
+ char bandwidth[256] = ""; /* Max bitrate */
+ char *hold;
+ struct ast_str *m_audio = ast_str_alloca(256); /* Media declaration line for audio */
+ struct ast_str *m_video = ast_str_alloca(256); /* Media declaration line for video */
+ struct ast_str *m_text = ast_str_alloca(256); /* Media declaration line for text */
+ struct ast_str *a_audio = ast_str_alloca(1024); /* Attributes for audio */
+ struct ast_str *a_video = ast_str_alloca(1024); /* Attributes for video */
+ struct ast_str *a_text = ast_str_alloca(1024); /* Attributes for text */
+
+ int x;
+ int capability;
+ int needvideo = FALSE;
+ int needtext = FALSE;
+ int debug = sip_debug_test_pvt(p);
+ int min_audio_packet_size = 0;
+ int min_video_packet_size = 0;
+ int min_text_packet_size = 0;
+
+ char codecbuf[BUFSIZ];
+ char buf[BUFSIZ];
+
+ /* Set the SDP session name */
+ snprintf(subject, sizeof(subject), "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
+
+ if (!p->rtp) {
+ ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
+ return AST_FAILURE;
+ }
+ /* XXX We should not change properties in the SIP dialog until
+ we have acceptance of the offer if this is a re-invite */
+
+ /* Set RTP Session ID and version */
+ if (!p->sessionid) {
+ p->sessionid = (int)ast_random();
+ p->sessionversion = p->sessionid;
+ } else {
+ if (oldsdp == FALSE)
+ p->sessionversion++;
+ }
+
+ capability = p->jointcapability;
+
+ /* XXX note, Video and Text are negated - 'true' means 'no' */
+ ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability),
+ p->novideo ? "True" : "False", p->notext ? "True" : "False");
+ ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
+
+#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
+ if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) {
+ ast_str_append(&m_audio, 0, " %d", 191);
+ ast_str_append(&a_audio, 0, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000);
+ }
+#endif
+
+ /* Check if we need video in this call */
+ if ((capability & AST_FORMAT_VIDEO_MASK) && !p->novideo) {
+ if (p->vrtp) {
+ needvideo = TRUE;
+ ast_debug(2, "This call needs video offers!\n");
+ } else
+ ast_debug(2, "This call needs video offers, but there's no video support enabled!\n");
+ }
+
+ /* Get our media addresses */
+ get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest);
+
+ if (debug)
+ ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port));
+
+ /* Ok, we need video. Let's add what we need for video and set codecs.
+ Video is handled differently than audio since we can not transcode. */
+ if (needvideo) {
+ ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
+
+ /* Build max bitrate string */
+ if (p->maxcallbitrate)
+ snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
+ if (debug)
+ ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(vsin.sin_port));
+ }
+
+ /* Check if we need text in this call */
+ if((capability & AST_FORMAT_TEXT_MASK) && !p->notext) {
+ if (sipdebug_text)
+ ast_verbose("We think we can do text\n");
+ if (p->trtp) {
+ if (sipdebug_text)
+ ast_verbose("And we have a text rtp object\n");
+ needtext = TRUE;
+ ast_debug(2, "This call needs text offers! \n");
+ } else
+ ast_debug(2, "This call needs text offers, but there's no text support enabled ! \n");
+ }
+
+ /* Ok, we need text. Let's add what we need for text and set codecs.
+ Text is handled differently than audio since we can not transcode. */
+ if (needtext) {
+ if (sipdebug_text)
+ ast_verbose("Lets set up the text sdp\n");
+ /* Determine text destination */
+ if (p->tredirip.sin_addr.s_addr) {
+ tdest.sin_addr = p->tredirip.sin_addr;
+ tdest.sin_port = p->tredirip.sin_port;
+ } else {
+ tdest.sin_addr = p->ourip.sin_addr;
+ tdest.sin_port = tsin.sin_port;
+ }
+ ast_str_append(&m_text, 0, "m=text %d RTP/AVP", ntohs(tdest.sin_port));
+
+ if (debug) /* XXX should I use tdest below ? */
+ ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port));
+
+ }
+
+ /* Start building generic SDP headers */
+
+ /* We break with the "recommendation" and send our IP, in order that our
+ peer doesn't have to ast_gethostbyname() us */
+
+ snprintf(owner, sizeof(owner), "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner, p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr));
+ snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
+ ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
+
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR)
+ hold = "a=recvonly\r\n";
+ else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE)
+ hold = "a=inactive\r\n";
+ else
+ hold = "a=sendrecv\r\n";
+
+ /* Now, start adding audio codecs. These are added in this order:
+ - First what was requested by the calling channel
+ - Then preferences in order from sip.conf device config for this peer/user
+ - Then other codecs in capabilities, including video
+ */
+
+ /* Prefer the audio codec we were requested to use, first, no matter what
+ Note that p->prefcodec can include video codecs, so mask them out
+ */
+ if (capability & p->prefcodec) {
+ int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK;
+
+ add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
+ &m_audio, &a_audio,
+ debug, &min_audio_packet_size);
+ alreadysent |= codec;
+ }
+
+ /* Start by sending our preferred audio codecs */
+ for (x = 0; x < 32; x++) {
+ int codec;
+
+ if (!(codec = ast_codec_pref_index(&p->prefs, x)))
+ break;
+
+ if (!(capability & codec))
+ continue;
+
+ if (alreadysent & codec)
+ continue;
+
+ add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
+ &m_audio, &a_audio,
+ debug, &min_audio_packet_size);
+ alreadysent |= codec;
+ }
+
+ /* Now send any other common audio and video codecs, and non-codec formats: */
+ for (x = 1; x <= (needtext ? AST_FORMAT_TEXT_MASK : (needvideo ? AST_FORMAT_VIDEO_MASK : AST_FORMAT_AUDIO_MASK)); x <<= 1) {
+ if (!(capability & x)) /* Codec not requested */
+ continue;
+
+ if (alreadysent & x) /* Already added to SDP */
+ continue;
+
+ if (x & AST_FORMAT_AUDIO_MASK)
+ add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x),
+ &m_audio, &a_audio, debug, &min_audio_packet_size);
+ else if (x & AST_FORMAT_VIDEO_MASK)
+ add_vcodec_to_sdp(p, x, 90000,
+ &m_video, &a_video, debug, &min_video_packet_size);
+ else if (x & AST_FORMAT_TEXT_MASK)
+ add_tcodec_to_sdp(p, x, 1000,
+ &m_text, &a_text, debug, &min_text_packet_size);
+ }
+
+ /* Now add DTMF RFC2833 telephony-event as a codec */
+ for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
+ if (!(p->jointnoncodeccapability & x))
+ continue;
+
+ add_noncodec_to_sdp(p, x, 8000, &m_audio, &a_audio, debug);
+ }
+
+ ast_debug(3, "-- Done with adding codecs to SDP\n");
+
+ if (!p->owner || !ast_internal_timing_enabled(p->owner))
+ ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n");
+
+ if (min_audio_packet_size)
+ ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size);
+
+ /* XXX don't think you can have ptime for video */
+ if (min_video_packet_size)
+ ast_str_append(&a_video, 0, "a=ptime:%d\r\n", min_video_packet_size);
+
+ /* XXX don't think you can have ptime for text */
+ if (min_text_packet_size)
+ ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
+
+ if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 ||
+ m_text->len - m_text->used < 2 || a_text->len - a_text->used < 2 ||
+ a_audio->len - a_audio->used < 2 || a_video->len - a_video->used < 2)
+ ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
+
+ ast_str_append(&m_audio, 0, "\r\n");
+ if (needvideo)
+ ast_str_append(&m_video, 0, "\r\n");
+ if (needtext)
+ ast_str_append(&m_text, 0, "\r\n");
+
+ len = strlen(version) + strlen(subject) + strlen(owner) +
+ strlen(connection) + strlen(stime) + m_audio->used + a_audio->used + strlen(hold);
+ if (needvideo) /* only if video response is appropriate */
+ len += m_video->used + a_video->used + strlen(bandwidth) + strlen(hold);
+ if (needtext) /* only if text response is appropriate */
+ len += m_text->used + a_text->used + strlen(hold);
+
+ add_header(resp, "Content-Type", "application/sdp");
+ add_header_contentLength(resp, len);
+ add_line(resp, version);
+ add_line(resp, owner);
+ add_line(resp, subject);
+ add_line(resp, connection);
+ if (needvideo) /* only if video response is appropriate */
+ add_line(resp, bandwidth);
+ add_line(resp, stime);
+ add_line(resp, m_audio->str);
+ add_line(resp, a_audio->str);
+ add_line(resp, hold);
+ if (needvideo) { /* only if video response is appropriate */
+ add_line(resp, m_video->str);
+ add_line(resp, a_video->str);
+ add_line(resp, hold); /* Repeat hold for the video stream */
+ }
+ if (needtext) { /* only if text response is appropriate */
+ add_line(resp, m_text->str);
+ add_line(resp, a_text->str);
+ add_line(resp, hold); /* Repeat hold for the text stream */
+ }
+
+ /* Update lastrtprx when we send our SDP */
+ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
+
+ ast_debug(3, "Done building SDP. Settling with this capability: %s\n", ast_getformatname_multiple(buf, BUFSIZ, capability));
+
+ return AST_SUCCESS;
+}
+
+/*! \brief Used for 200 OK and 183 early media */
+static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans)
+{
+ struct sip_request resp;
+ int seqno;
+
+ if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
+ ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
+ return -1;
+ }
+ respprep(&resp, p, msg, req);
+ if (p->udptl) {
+ ast_udptl_offered_from_local(p->udptl, 0);
+ add_t38_sdp(&resp, p);
+ } else
+ ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
+ if (retrans && !p->pendinginvite)
+ p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */
+ return send_response(p, &resp, retrans, seqno);
+}
+
+/*! \brief copy SIP request (mostly used to save request for responses) */
+static void copy_request(struct sip_request *dst, const struct sip_request *src)
+{
+ long offset;
+ int x;
+ offset = ((void *)dst) - ((void *)src);
+ /* First copy stuff */
+ memcpy(dst, src, sizeof(*dst));
+ /* Now fix pointer arithmetic */
+ for (x=0; x < src->headers; x++)
+ dst->header[x] += offset;
+ for (x=0; x < src->lines; x++)
+ dst->line[x] += offset;
+ dst->rlPart1 += offset;
+ dst->rlPart2 += offset;
+}
+
+/*! \brief Used for 200 OK and 183 early media
+ \return Will return XMIT_ERROR for network errors.
+*/
+static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp)
+{
+ struct sip_request resp;
+ int seqno;
+ if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
+ ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
+ return -1;
+ }
+ respprep(&resp, p, msg, req);
+ if (p->rtp) {
+ if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ ast_debug(1, "Setting framing from config on incoming call\n");
+ ast_rtp_codec_setpref(p->rtp, &p->prefs);
+ }
+ try_suggested_sip_codec(p);
+ add_sdp(&resp, p, oldsdp);
+ } else
+ ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
+ if (reliable && !p->pendinginvite)
+ p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */
+ return send_response(p, &resp, reliable, seqno);
+}
+
+/*! \brief Parse first line of incoming SIP request */
+static int determine_firstline_parts(struct sip_request *req)
+{
+ char *e = ast_skip_blanks(req->header[0]); /* there shouldn't be any */
+
+ if (!*e)
+ return -1;
+ req->rlPart1 = e; /* method or protocol */
+ e = ast_skip_nonblanks(e);
+ if (*e)
+ *e++ = '\0';
+ /* Get URI or status code */
+ e = ast_skip_blanks(e);
+ if ( !*e )
+ return -1;
+ ast_trim_blanks(e);
+
+ if (!strcasecmp(req->rlPart1, "SIP/2.0") ) { /* We have a response */
+ if (strlen(e) < 3) /* status code is 3 digits */
+ return -1;
+ req->rlPart2 = e;
+ } else { /* We have a request */
+ if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */
+ ast_debug(3, "Oops. Bogus uri in <> %s\n", e);
+ e++;
+ if (!*e)
+ return -1;
+ }
+ req->rlPart2 = e; /* URI */
+ e = ast_skip_nonblanks(e);
+ if (*e)
+ *e++ = '\0';
+ e = ast_skip_blanks(e);
+ if (strcasecmp(e, "SIP/2.0") ) {
+ ast_debug(3, "Skipping packet - Bad request protocol %s\n", e);
+ return -1;
+ }
+ }
+ return 1;
+}
+
+/*! \brief Transmit reinvite with SDP
+\note A re-invite is basically a new INVITE with the same CALL-ID and TAG as the
+ INVITE that opened the SIP dialogue
+ We reinvite so that the audio stream (RTP) go directly between
+ the SIP UAs. SIP Signalling stays with * in the path.
+
+ If t38version is TRUE, we send T38 SDP for re-invite from audio/video to
+ T38 UDPTL transmission on the channel
+
+ If oldsdp is TRUE then the SDP version number is not incremented. This
+ is needed for Session-Timers so we can send a re-invite to refresh the
+ SIP session without modifying the media session.
+*/
+static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp)
+{
+ struct sip_request req;
+
+ reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
+
+ add_header(&req, "Allow", ALLOWED_METHODS);
+ add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
+ if (sipdebug) {
+ if (oldsdp == TRUE)
+ add_header(&req, "X-asterisk-Info", "SIP re-invite (Session-Timers)");
+ else
+ add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
+ }
+
+ if (p->do_history)
+ append_history(p, "ReInv", "Re-invite sent");
+ if (t38version)
+ add_t38_sdp(&req, p);
+ else
+ add_sdp(&req, p, oldsdp);
+
+ /* Use this as the basis */
+ initialize_initreq(p, &req);
+ p->lastinvite = p->ocseq;
+ ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */
+
+ return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
+}
+
+/* \brief Remove URI parameters at end of URI, not in username part though */
+static char *remove_uri_parameters(char *uri)
+{
+ char *atsign;
+ atsign = strchr(uri, '@'); /* First, locate the at sign */
+ if (!atsign)
+ atsign = uri; /* Ok hostname only, let's stick with the rest */
+ atsign = strchr(atsign, ';'); /* Locate semi colon */
+ if (atsign)
+ *atsign = '\0'; /* Kill at the semi colon */
+ return uri;
+}
+
+/*! \brief Check Contact: URI of SIP message */
+static void extract_uri(struct sip_pvt *p, struct sip_request *req)
+{
+ char stripped[BUFSIZ];
+ char *c;
+
+ ast_copy_string(stripped, get_header(req, "Contact"), sizeof(stripped));
+ c = get_in_brackets(stripped);
+ /* Cut the URI at the at sign after the @, not in the username part */
+ c = remove_uri_parameters(c);
+ if (!ast_strlen_zero(c))
+ ast_string_field_set(p, uri, c);
+
+}
+
+/*! \brief Build contact header - the contact header we send out */
+static void build_contact(struct sip_pvt *p)
+{
+ /* Construct Contact: header */
+ if (p->socket.type & SIP_TRANSPORT_UDP) {
+ if (!sip_standard_port(p->socket))
+ ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ntohs(p->socket.port));
+ else
+ ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr));
+ } else
+ ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d;transport=%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ntohs(p->socket.port), get_transport(p->socket.type));
+}
+
+/*! \brief Build the Remote Party-ID & From using callingpres options */
+static void build_rpid(struct sip_pvt *p)
+{
+ int send_pres_tags = TRUE;
+ const char *privacy=NULL;
+ const char *screen=NULL;
+ char buf[256];
+ const char *clid = default_callerid;
+ const char *clin = NULL;
+ const char *fromdomain;
+
+ if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from))
+ return;
+
+ if (p->owner && p->owner->cid.cid_num)
+ clid = p->owner->cid.cid_num;
+ if (p->owner && p->owner->cid.cid_name)
+ clin = p->owner->cid.cid_name;
+ if (ast_strlen_zero(clin))
+ clin = clid;
+
+ switch (p->callingpres) {
+ case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
+ privacy = "off";
+ screen = "no";
+ break;
+ case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
+ privacy = "off";
+ screen = "yes";
+ break;
+ case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
+ privacy = "off";
+ screen = "no";
+ break;
+ case AST_PRES_ALLOWED_NETWORK_NUMBER:
+ privacy = "off";
+ screen = "yes";
+ break;
+ case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
+ privacy = "full";
+ screen = "no";
+ break;
+ case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
+ privacy = "full";
+ screen = "yes";
+ break;
+ case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
+ privacy = "full";
+ screen = "no";
+ break;
+ case AST_PRES_PROHIB_NETWORK_NUMBER:
+ privacy = "full";
+ screen = "yes";
+ break;
+ case AST_PRES_NUMBER_NOT_AVAILABLE:
+ send_pres_tags = FALSE;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unsupported callingpres (%d)\n", p->callingpres);
+ if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)
+ privacy = "full";
+ else
+ privacy = "off";
+ screen = "no";
+ break;
+ }
+
+ fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr));
+
+ snprintf(buf, sizeof(buf), "\"%s\" <sip:%s@%s>", clin, clid, fromdomain);
+ if (send_pres_tags)
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ";privacy=%s;screen=%s", privacy, screen);
+ ast_string_field_set(p, rpid, buf);
+
+ ast_string_field_build(p, rpid_from, "\"%s\" <sip:%s@%s>;tag=%s", clin,
+ S_OR(p->fromuser, clid),
+ fromdomain, p->tag);
+}
+
+/*! \brief Initiate new SIP request to peer/user */
+static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod)
+{
+ struct ast_str *invite = ast_str_alloca(256);
+ char from[256];
+ char to[256];
+ char tmp_n[BUFSIZ/2]; /* build a local copy of 'n' if needed */
+ char tmp_l[BUFSIZ/2]; /* build a local copy of 'l' if needed */
+ const char *l = NULL; /* XXX what is this, exactly ? */
+ const char *n = NULL; /* XXX what is this, exactly ? */
+ const char *urioptions = "";
+
+ if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
+ const char *s = p->username; /* being a string field, cannot be NULL */
+
+ /* Test p->username against allowed characters in AST_DIGIT_ANY
+ If it matches the allowed characters list, then sipuser = ";user=phone"
+ If not, then sipuser = ""
+ */
+ /* + is allowed in first position in a tel: uri */
+ if (*s == '+')
+ s++;
+ for (; *s; s++) {
+ if (!strchr(AST_DIGIT_ANYNUM, *s) )
+ break;
+ }
+ /* If we have only digits, add ;user=phone to the uri */
+ if (*s)
+ urioptions = ";user=phone";
+ }
+
+
+ snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text);
+
+ if (p->owner) {
+ l = p->owner->cid.cid_num;
+ n = p->owner->cid.cid_name;
+ }
+ /* if we are not sending RPID and user wants his callerid restricted */
+ if (!ast_test_flag(&p->flags[0], SIP_SENDRPID) &&
+ ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) {
+ l = CALLERID_UNKNOWN;
+ n = l;
+ }
+ if (ast_strlen_zero(l))
+ l = default_callerid;
+ if (ast_strlen_zero(n))
+ n = l;
+ /* Allow user to be overridden */
+ if (!ast_strlen_zero(p->fromuser))
+ l = p->fromuser;
+ else /* Save for any further attempts */
+ ast_string_field_set(p, fromuser, l);
+
+ /* Allow user to be overridden */
+ if (!ast_strlen_zero(p->fromname))
+ n = p->fromname;
+ else /* Save for any further attempts */
+ ast_string_field_set(p, fromname, n);
+
+ if (pedanticsipchecking) {
+ ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0);
+ n = tmp_n;
+ ast_uri_encode(l, tmp_l, sizeof(tmp_l), 0);
+ l = tmp_l;
+ }
+
+ if (!sip_standard_port(p->socket) && ast_strlen_zero(p->fromdomain))
+ snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)), ntohs(p->socket.port), p->tag);
+ else
+ snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)), p->tag);
+
+ /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
+ if (!ast_strlen_zero(p->fullcontact)) {
+ /* If we have full contact, trust it */
+ ast_str_append(&invite, 0, "%s", p->fullcontact);
+ } else {
+ /* Otherwise, use the username while waiting for registration */
+ ast_str_append(&invite, 0, "sip:");
+ if (!ast_strlen_zero(p->username)) {
+ n = p->username;
+ if (pedanticsipchecking) {
+ ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0);
+ n = tmp_n;
+ }
+ ast_str_append(&invite, 0, "%s@", n);
+ }
+ ast_str_append(&invite, 0, "%s", p->tohost);
+ if (ntohs(p->sa.sin_port) != STANDARD_SIP_PORT)
+ ast_str_append(&invite, 0, ":%d", ntohs(p->sa.sin_port));
+ ast_str_append(&invite, 0, "%s", urioptions);
+ }
+
+ /* If custom URI options have been provided, append them */
+ if (p->options && p->options->uri_options)
+ ast_str_append(&invite, 0, ";%s", p->options->uri_options);
+
+ /* This is the request URI, which is the next hop of the call
+ which may or may not be the destination of the call
+ */
+ ast_string_field_set(p, uri, invite->str);
+
+ if (!ast_strlen_zero(p->todnid)) {
+ /*! \todo Need to add back the VXML URL here at some point, possibly use build_string for all this junk */
+ if (!strchr(p->todnid, '@')) {
+ /* We have no domain in the dnid */
+ snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+ } else {
+ snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+ }
+ } else {
+ if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
+ /* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
+ snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "" : "sip:"), p->uri, p->theirtag);
+ } else if (p->options && p->options->vxml_url) {
+ /* If there is a VXML URL append it to the SIP URL */
+ snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
+ } else
+ snprintf(to, sizeof(to), "<%s>", p->uri);
+ }
+
+ init_req(req, sipmethod, p->uri);
+ /* now tmp_n is available so reuse it to build the CSeq */
+ snprintf(tmp_n, sizeof(tmp_n), "%d %s", ++p->ocseq, sip_methods[sipmethod].text);
+
+ add_header(req, "Via", p->via);
+ add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
+ /* SLD: FIXME?: do Route: here too? I think not cos this is the first request.
+ * OTOH, then we won't have anything in p->route anyway */
+
+ /* Build Remote Party-ID and From */
+ if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) {
+ build_rpid(p);
+ add_header(req, "From", p->rpid_from);
+ } else
+ add_header(req, "From", from);
+ add_header(req, "To", to);
+ ast_string_field_set(p, exten, l);
+ build_contact(p);
+ add_header(req, "Contact", p->our_contact);
+ add_header(req, "Call-ID", p->callid);
+ add_header(req, "CSeq", tmp_n);
+ if (!ast_strlen_zero(global_useragent))
+ add_header(req, "User-Agent", global_useragent);
+ if (!ast_strlen_zero(p->rpid))
+ add_header(req, "Remote-Party-ID", p->rpid);
+}
+
+/*! \brief Build REFER/INVITE/OPTIONS message and transmit it
+ \param init 0 = Prepare request within dialog, 1= prepare request, new branch, 2= prepare new request and new dialog. do_proxy_auth calls this with init!=2
+ \param p sip_pvt structure
+ \param sdp unknown
+ \param sipmethod unknown
+
+*/
+static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
+{
+ struct sip_request req;
+
+ req.method = sipmethod;
+ if (init) {/* Bump branch even on initial requests */
+ p->branch ^= ast_random();
+ build_via(p);
+ }
+ if (init > 1)
+ initreqprep(&req, p, sipmethod);
+ else
+ reqprep(&req, p, sipmethod, 0, 1);
+
+ if (p->options && p->options->auth)
+ add_header(&req, p->options->authheader, p->options->auth);
+ append_date(&req);
+ if (sipmethod == SIP_REFER) { /* Call transfer */
+ if (p->refer) {
+ char buf[BUFSIZ];
+ if (!ast_strlen_zero(p->refer->refer_to))
+ add_header(&req, "Refer-To", p->refer->refer_to);
+ if (!ast_strlen_zero(p->refer->referred_by)) {
+ snprintf(buf, sizeof(buf), "%s <%s>", p->refer->referred_by_name, p->refer->referred_by);
+ add_header(&req, "Referred-By", buf);
+ }
+ }
+ }
+ /* This new INVITE is part of an attended transfer. Make sure that the
+ other end knows and replace the current call with this new call */
+ if (p->options && !ast_strlen_zero(p->options->replaces)) {
+ add_header(&req, "Replaces", p->options->replaces);
+ add_header(&req, "Require", "replaces");
+ }
+
+ /* Add Session-Timers related headers */
+ if (st_get_mode(p) == SESSION_TIMER_MODE_ORIGINATE) {
+ char i2astr[10];
+
+ if (!p->stimer->st_interval)
+ p->stimer->st_interval = st_get_se(p, TRUE);
+
+ p->stimer->st_active = TRUE;
+
+ snprintf(i2astr, sizeof(i2astr), "%d", p->stimer->st_interval);
+ add_header(&req, "Session-Expires", i2astr);
+ add_header(&req, "Min-SE", i2astr);
+ }
+
+ add_header(&req, "Allow", ALLOWED_METHODS);
+ add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
+ if (p->options && p->options->addsipheaders && p->owner) {
+ struct ast_channel *chan = p->owner; /* The owner channel */
+ struct varshead *headp;
+
+ ast_channel_lock(chan);
+
+ headp = &chan->varshead;
+
+ if (!headp)
+ ast_log(LOG_WARNING,"No Headp for the channel...ooops!\n");
+ else {
+ const struct ast_var_t *current;
+ AST_LIST_TRAVERSE(headp, current, entries) {
+ /* SIPADDHEADER: Add SIP header to outgoing call */
+ if (!strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
+ char *content, *end;
+ const char *header = ast_var_value(current);
+ char *headdup = ast_strdupa(header);
+
+ /* Strip of the starting " (if it's there) */
+ if (*headdup == '"')
+ headdup++;
+ if ((content = strchr(headdup, ':'))) {
+ *content++ = '\0';
+ content = ast_skip_blanks(content); /* Skip white space */
+ /* Strip the ending " (if it's there) */
+ end = content + strlen(content) -1;
+ if (*end == '"')
+ *end = '\0';
+
+ add_header(&req, headdup, content);
+ if (sipdebug)
+ ast_debug(1, "Adding SIP Header \"%s\" with content :%s: \n", headdup, content);
+ }
+ }
+ }
+ }
+
+ ast_channel_unlock(chan);
+ }
+ if (sdp) {
+ if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) {
+ ast_udptl_offered_from_local(p->udptl, 1);
+ ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ add_t38_sdp(&req, p);
+ } else if (p->rtp)
+ add_sdp(&req, p, FALSE);
+ } else {
+ add_header_contentLength(&req, 0);
+ }
+
+ if (!p->initreq.headers)
+ initialize_initreq(p, &req);
+ p->lastinvite = p->ocseq;
+ return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq);
+}
+
+/*! \brief Used in the SUBSCRIBE notification subsystem */
+static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout)
+{
+ struct ast_str *tmp = ast_str_alloca(4000);
+ char from[256], to[256];
+ char *c, *mfrom, *mto;
+ struct sip_request req;
+ char hint[AST_MAX_EXTENSION];
+ char *statestring = "terminated";
+ const struct cfsubscription_types *subscriptiontype;
+ enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN;
+ char *pidfstate = "--";
+ char *pidfnote= "Ready";
+
+ memset(from, 0, sizeof(from));
+ memset(to, 0, sizeof(to));
+
+ switch (state) {
+ case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE):
+ statestring = (global_notifyringing) ? "early" : "confirmed";
+ local_state = NOTIFY_INUSE;
+ pidfstate = "busy";
+ pidfnote = "Ringing";
+ break;
+ case AST_EXTENSION_RINGING:
+ statestring = "early";
+ local_state = NOTIFY_INUSE;
+ pidfstate = "busy";
+ pidfnote = "Ringing";
+ break;
+ case AST_EXTENSION_INUSE:
+ statestring = "confirmed";
+ local_state = NOTIFY_INUSE;
+ pidfstate = "busy";
+ pidfnote = "On the phone";
+ break;
+ case AST_EXTENSION_BUSY:
+ statestring = "confirmed";
+ local_state = NOTIFY_CLOSED;
+ pidfstate = "busy";
+ pidfnote = "On the phone";
+ break;
+ case AST_EXTENSION_UNAVAILABLE:
+ statestring = "terminated";
+ local_state = NOTIFY_CLOSED;
+ pidfstate = "away";
+ pidfnote = "Unavailable";
+ break;
+ case AST_EXTENSION_ONHOLD:
+ statestring = "confirmed";
+ local_state = NOTIFY_INUSE;
+ pidfstate = "busy";
+ pidfnote = "On hold";
+ break;
+ case AST_EXTENSION_NOT_INUSE:
+ default:
+ /* Default setting */
+ break;
+ }
+
+ subscriptiontype = find_subscription_type(p->subscribed);
+
+ /* Check which device/devices we are watching and if they are registered */
+ if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) {
+ char *hint2 = hint, *individual_hint = NULL;
+ int hint_count = 0, unavailable_count = 0;
+
+ while ((individual_hint = strsep(&hint2, "&"))) {
+ hint_count++;
+
+ if (ast_device_state(individual_hint) == AST_DEVICE_UNAVAILABLE)
+ unavailable_count++;
+ }
+
+ /* If none of the hinted devices are registered, we will
+ * override notification and show no availability.
+ */
+ if (hint_count > 0 && hint_count == unavailable_count) {
+ local_state = NOTIFY_CLOSED;
+ pidfstate = "away";
+ pidfnote = "Not online";
+ }
+ }
+
+ ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from));
+ c = get_in_brackets(from);
+ if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) {
+ ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c);
+ return -1;
+ }
+
+ mfrom = remove_uri_parameters(c);
+
+ ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to));
+ c = get_in_brackets(to);
+ if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) {
+ ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c);
+ return -1;
+ }
+ mto = remove_uri_parameters(c);
+
+ reqprep(&req, p, SIP_NOTIFY, 0, 1);
+
+
+ add_header(&req, "Event", subscriptiontype->event);
+ add_header(&req, "Content-Type", subscriptiontype->mediatype);
+ switch(state) {
+ case AST_EXTENSION_DEACTIVATED:
+ if (timeout)
+ add_header(&req, "Subscription-State", "terminated;reason=timeout");
+ else {
+ add_header(&req, "Subscription-State", "terminated;reason=probation");
+ add_header(&req, "Retry-After", "60");
+ }
+ break;
+ case AST_EXTENSION_REMOVED:
+ add_header(&req, "Subscription-State", "terminated;reason=noresource");
+ break;
+ default:
+ if (p->expiry)
+ add_header(&req, "Subscription-State", "active");
+ else /* Expired */
+ add_header(&req, "Subscription-State", "terminated;reason=timeout");
+ }
+ switch (p->subscribed) {
+ case XPIDF_XML:
+ case CPIM_PIDF_XML:
+ ast_str_append(&tmp, 0,
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n"
+ "<presence>\n");
+ ast_str_append(&tmp, 0, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom);
+ ast_str_append(&tmp, 0, "<atom id=\"%s\">\n", p->exten);
+ ast_str_append(&tmp, 0, "<address uri=\"%s;user=ip\" priority=\"0.800000\">\n", mto);
+ ast_str_append(&tmp, 0, "<status status=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
+ ast_str_append(&tmp, 0, "<msnsubstatus substatus=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
+ ast_str_append(&tmp, 0, "</address>\n</atom>\n</presence>\n");
+ break;
+ case PIDF_XML: /* Eyebeam supports this format */
+ ast_str_append(&tmp, 0,
+ "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
+ "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" \nxmlns:pp=\"urn:ietf:params:xml:ns:pidf:person\"\nxmlns:es=\"urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status\"\nxmlns:ep=\"urn:ietf:params:xml:ns:pidf:rpid:rpid-person\"\nentity=\"%s\">\n", mfrom);
+ ast_str_append(&tmp, 0, "<pp:person><status>\n");
+ if (pidfstate[0] != '-')
+ ast_str_append(&tmp, 0, "<ep:activities><ep:%s/></ep:activities>\n", pidfstate);
+ ast_str_append(&tmp, 0, "</status></pp:person>\n");
+ ast_str_append(&tmp, 0, "<note>%s</note>\n", pidfnote); /* Note */
+ ast_str_append(&tmp, 0, "<tuple id=\"%s\">\n", p->exten); /* Tuple start */
+ ast_str_append(&tmp, 0, "<contact priority=\"1\">%s</contact>\n", mto);
+ if (pidfstate[0] == 'b') /* Busy? Still open ... */
+ ast_str_append(&tmp, 0, "<status><basic>open</basic></status>\n");
+ else
+ ast_str_append(&tmp, 0, "<status><basic>%s</basic></status>\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed");
+ ast_str_append(&tmp, 0, "</tuple>\n</presence>\n");
+ break;
+ case DIALOG_INFO_XML: /* SNOM subscribes in this format */
+ ast_str_append(&tmp, 0, "<?xml version=\"1.0\"?>\n");
+ ast_str_append(&tmp, 0, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mto);
+ if ((state & AST_EXTENSION_RINGING) && global_notifyringing)
+ ast_str_append(&tmp, 0, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten);
+ else
+ ast_str_append(&tmp, 0, "<dialog id=\"%s\">\n", p->exten);
+ ast_str_append(&tmp, 0, "<state>%s</state>\n", statestring);
+ if (state == AST_EXTENSION_ONHOLD) {
+ ast_str_append(&tmp, 0, "<local>\n<target uri=\"%s\">\n"
+ "<param pname=\"+sip.rendering\" pvalue=\"no\">\n"
+ "</target>\n</local>\n", mto);
+ }
+ ast_str_append(&tmp, 0, "</dialog>\n</dialog-info>\n");
+ break;
+ case NONE:
+ default:
+ break;
+ }
+
+ add_header_contentLength(&req, tmp->used);
+ add_line(&req, tmp->str);
+
+ return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
+/*! \brief Notify user of messages waiting in voicemail
+\note - Notification only works for registered peers with mailbox= definitions
+ in sip.conf
+ - We use the SIP Event package message-summary
+ MIME type defaults to "application/simple-message-summary";
+ */
+static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, char *vmexten)
+{
+ struct sip_request req;
+ struct ast_str *out = ast_str_alloca(500);
+
+ initreqprep(&req, p, SIP_NOTIFY);
+ add_header(&req, "Event", "message-summary");
+ add_header(&req, "Content-Type", default_notifymime);
+
+ ast_str_append(&out, 0, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no");
+ ast_str_append(&out, 0, "Message-Account: sip:%s@%s\r\n",
+ S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)));
+ /* Cisco has a bug in the SIP stack where it can't accept the
+ (0/0) notification. This can temporarily be disabled in
+ sip.conf with the "buggymwi" option */
+ ast_str_append(&out, 0, "Voice-Message: %d/%d%s\r\n",
+ newmsgs, oldmsgs, (ast_test_flag(&p->flags[1], SIP_PAGE2_BUGGY_MWI) ? "" : " (0/0)"));
+
+ if (p->subscribed) {
+ if (p->expiry)
+ add_header(&req, "Subscription-State", "active");
+ else /* Expired */
+ add_header(&req, "Subscription-State", "terminated;reason=timeout");
+ }
+
+ add_header_contentLength(&req, out->used);
+ add_line(&req, out->str);
+
+ if (!p->initreq.headers)
+ initialize_initreq(p, &req);
+ return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
+/*! \brief Transmit SIP request unreliably (only used in sip_notify subsystem) */
+static int transmit_sip_request(struct sip_pvt *p, struct sip_request *req)
+{
+ if (!p->initreq.headers) /* Initialize first request before sending */
+ initialize_initreq(p, req);
+ return send_request(p, req, XMIT_UNRELIABLE, p->ocseq);
+}
+
+/*! \brief Notify a transferring party of the status of transfer */
+static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate)
+{
+ struct sip_request req;
+ char tmp[BUFSIZ/2];
+
+ reqprep(&req, p, SIP_NOTIFY, 0, 1);
+ snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq);
+ add_header(&req, "Event", tmp);
+ add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active");
+ add_header(&req, "Content-Type", "message/sipfrag;version=2.0");
+ add_header(&req, "Allow", ALLOWED_METHODS);
+ add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
+
+ snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message);
+ add_header_contentLength(&req, strlen(tmp));
+ add_line(&req, tmp);
+
+ if (!p->initreq.headers)
+ initialize_initreq(p, &req);
+
+ p->lastnoninvite = p->ocseq;
+
+ return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
+static const struct _map_x_s regstatestrings[] = {
+ { REG_STATE_FAILED, "Failed" },
+ { REG_STATE_UNREGISTERED, "Unregistered"},
+ { REG_STATE_REGSENT, "Request Sent"},
+ { REG_STATE_AUTHSENT, "Auth. Sent"},
+ { REG_STATE_REGISTERED, "Registered"},
+ { REG_STATE_REJECTED, "Rejected"},
+ { REG_STATE_TIMEOUT, "Timeout"},
+ { REG_STATE_NOAUTH, "No Authentication"},
+ { -1, NULL } /* terminator */
+};
+
+/*! \brief Convert registration state status to string */
+static const char *regstate2str(enum sipregistrystate regstate)
+{
+ return map_x_s(regstatestrings, regstate, "Unknown");
+}
+
+/*! \brief Update registration with SIP Proxy.
+ * Called from the scheduler when the previous registration expires,
+ * so we don't have to cancel the pending event.
+ * We assume the reference so the sip_registry is valid, since it
+ * is stored in the scheduled event anyways.
+ */
+static int sip_reregister(const void *data)
+{
+ /* if we are here, we know that we need to reregister. */
+ struct sip_registry *r= registry_addref((struct sip_registry *) data);
+
+ /* if we couldn't get a reference to the registry object, punt */
+ if (!r)
+ return 0;
+
+ if (r->call && r->call->do_history)
+ append_history(r->call, "RegistryRenew", "Account: %s@%s", r->username, r->hostname);
+ /* Since registry's are only added/removed by the the monitor thread, this
+ may be overkill to reference/dereference at all here */
+ if (sipdebug)
+ ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname);
+
+ r->expire = -1;
+ __sip_do_register(r);
+ registry_unref(r);
+ return 0;
+}
+
+/*! \brief Register with SIP proxy */
+static int __sip_do_register(struct sip_registry *r)
+{
+ int res;
+
+ res = transmit_register(r, SIP_REGISTER, NULL, NULL);
+ return res;
+}
+
+/*! \brief Registration timeout, register again
+ * Registered as a timeout handler during transmit_register(),
+ * to retransmit the packet if a reply does not come back.
+ * This is called by the scheduler so the event is not pending anymore when
+ * we are called.
+ */
+static int sip_reg_timeout(const void *data)
+{
+
+ /* if we are here, our registration timed out, so we'll just do it over */
+ struct sip_registry *r = registry_addref((struct sip_registry *) data);
+ struct sip_pvt *p;
+ int res;
+
+ /* if we couldn't get a reference to the registry object, punt */
+ if (!r)
+ return 0;
+
+ ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again (Attempt #%d)\n", r->username, r->hostname, r->regattempts);
+ /* If the initial tranmission failed, we may not have an existing dialog,
+ * so it is possible that r->call == NULL.
+ * Otherwise destroy it, as we have a timeout so we don't want it.
+ */
+ if (r->call) {
+ /* Unlink us, destroy old call. Locking is not relevant here because all this happens
+ in the single SIP manager thread. */
+ p = r->call;
+ p->needdestroy = 1;
+ /* Pretend to ACK anything just in case */
+ __sip_pretend_ack(p); /* XXX we need p locked, not sure we have */
+
+ /* decouple the two objects */
+ /* p->registry == r, so r has 2 refs, and the unref won't take the object away */
+ if (p->registry)
+ p->registry = registry_unref(p->registry);
+ r->call = dialog_unref(r->call);
+ }
+ /* If we have a limit, stop registration and give up */
+ if (global_regattempts_max && r->regattempts > global_regattempts_max) {
+ /* Ok, enough is enough. Don't try any more */
+ /* We could add an external notification here...
+ steal it from app_voicemail :-) */
+ ast_log(LOG_NOTICE, " -- Giving up forever trying to register '%s@%s'\n", r->username, r->hostname);
+ r->regstate = REG_STATE_FAILED;
+ } else {
+ r->regstate = REG_STATE_UNREGISTERED;
+ r->timeout = -1;
+ res=transmit_register(r, SIP_REGISTER, NULL, NULL);
+ }
+ manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
+ registry_unref(r);
+ return 0;
+}
+
+/*! \brief Transmit register to SIP proxy or UA
+ * auth = NULL on the initial registration (from sip_reregister())
+ */
+static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader)
+{
+ struct sip_request req;
+ char from[256];
+ char to[256];
+ char tmp[80];
+ char addr[80];
+ struct sip_pvt *p;
+
+ /* exit if we are already in process with this registrar ?*/
+ if ( r == NULL || ((auth==NULL) && (r->regstate==REG_STATE_REGSENT || r->regstate==REG_STATE_AUTHSENT))) {
+ ast_log(LOG_NOTICE, "Strange, trying to register %s@%s when registration already pending\n", r->username, r->hostname);
+ return 0;
+ }
+
+ if (r->call) { /* We have a registration */
+ if (!auth) {
+ ast_log(LOG_WARNING, "Already have a REGISTER going on to %s@%s?? \n", r->username, r->hostname);
+ return 0;
+ } else {
+ p = r->call;
+ make_our_tag(p->tag, sizeof(p->tag)); /* create a new local tag for every register attempt */
+ ast_string_field_set(p, theirtag, NULL); /* forget their old tag, so we don't match tags when getting response */
+ }
+ } else {
+ /* Build callid for registration if we haven't registered before */
+ if (!r->callid_valid) {
+ build_callid_registry(r, internip.sin_addr, default_fromdomain);
+ r->callid_valid = TRUE;
+ }
+ /* Allocate SIP dialog for registration */
+ if (!(p = sip_alloc( r->callid, NULL, 0, SIP_REGISTER))) {
+ ast_log(LOG_WARNING, "Unable to allocate registration transaction (memory or socket error)\n");
+ return 0;
+ }
+
+ if (p->do_history)
+ append_history(p, "RegistryInit", "Account: %s@%s", r->username, r->hostname);
+
+ p->outboundproxy = obproxy_get(p, NULL);
+
+ /* Find address to hostname */
+ if (create_addr(p, r->hostname)) {
+ /* we have what we hope is a temporary network error,
+ * probably DNS. We need to reschedule a registration try */
+ sip_destroy(p);
+ if (r->timeout > -1) {
+ r->timeout = ast_sched_replace(r->timeout, sched,
+ global_reg_timeout * 1000, sip_reg_timeout, r);
+ ast_log(LOG_WARNING, "Still have a registration timeout for %s@%s (create_addr() error), %d\n", r->username, r->hostname, r->timeout);
+ } else {
+ r->timeout = ast_sched_add(sched, global_reg_timeout*1000, sip_reg_timeout, r);
+ ast_log(LOG_WARNING, "Probably a DNS error for registration to %s@%s, trying REGISTER again (after %d seconds)\n", r->username, r->hostname, global_reg_timeout);
+ }
+ r->regattempts++;
+ return 0;
+ }
+ /* Copy back Call-ID in case create_addr changed it */
+ ast_string_field_set(r, callid, p->callid);
+ if (r->portno) {
+ p->sa.sin_port = htons(r->portno);
+ p->recv.sin_port = htons(r->portno);
+ } else /* Set registry port to the port set from the peer definition/srv or default */
+ r->portno = ntohs(p->sa.sin_port);
+ ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Registration is outgoing call */
+ r->call = dialog_ref(p); /* Save pointer to SIP dialog */
+ p->registry = registry_addref(r); /* Add pointer to registry in packet */
+ if (!ast_strlen_zero(r->secret)) /* Secret (password) */
+ ast_string_field_set(p, peersecret, r->secret);
+ if (!ast_strlen_zero(r->md5secret))
+ ast_string_field_set(p, peermd5secret, r->md5secret);
+ /* User name in this realm
+ - if authuser is set, use that, otherwise use username */
+ if (!ast_strlen_zero(r->authuser)) {
+ ast_string_field_set(p, peername, r->authuser);
+ ast_string_field_set(p, authname, r->authuser);
+ } else if (!ast_strlen_zero(r->username)) {
+ ast_string_field_set(p, peername, r->username);
+ ast_string_field_set(p, authname, r->username);
+ ast_string_field_set(p, fromuser, r->username);
+ }
+ if (!ast_strlen_zero(r->username))
+ ast_string_field_set(p, username, r->username);
+ /* Save extension in packet */
+ if (!ast_strlen_zero(r->callback))
+ ast_string_field_set(p, exten, r->callback);
+
+ /* Set transport and port so the correct contact is built */
+ p->socket.type = r->transport;
+ p->socket.port = htons(r->portno);
+ /*
+ check which address we should use in our contact header
+ based on whether the remote host is on the external or
+ internal network so we can register through nat
+ */
+ ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
+ build_contact(p);
+ }
+
+ /* set up a timeout */
+ if (auth == NULL) {
+ if (r->timeout > -1)
+ ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout);
+ r->timeout = ast_sched_replace(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r);
+ ast_debug(1, "Scheduled a registration timeout for %s id #%d \n", r->hostname, r->timeout);
+ }
+
+ if (strchr(r->username, '@')) {
+ snprintf(from, sizeof(from), "<sip:%s>;tag=%s", r->username, p->tag);
+ if (!ast_strlen_zero(p->theirtag))
+ snprintf(to, sizeof(to), "<sip:%s>;tag=%s", r->username, p->theirtag);
+ else
+ snprintf(to, sizeof(to), "<sip:%s>", r->username);
+ } else {
+ snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->tag);
+ if (!ast_strlen_zero(p->theirtag))
+ snprintf(to, sizeof(to), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->theirtag);
+ else
+ snprintf(to, sizeof(to), "<sip:%s@%s>", r->username, p->tohost);
+ }
+
+ /* Fromdomain is what we are registering to, regardless of actual
+ host name from SRV */
+ if (!ast_strlen_zero(p->fromdomain)) {
+ if (r->portno && r->portno != STANDARD_SIP_PORT)
+ snprintf(addr, sizeof(addr), "sip:%s:%d", p->fromdomain, r->portno);
+ else
+ snprintf(addr, sizeof(addr), "sip:%s", p->fromdomain);
+ } else {
+ if (r->portno && r->portno != STANDARD_SIP_PORT)
+ snprintf(addr, sizeof(addr), "sip:%s:%d", r->hostname, r->portno);
+ else
+ snprintf(addr, sizeof(addr), "sip:%s", r->hostname);
+ }
+ ast_string_field_set(p, uri, addr);
+
+ p->branch ^= ast_random();
+
+ init_req(&req, sipmethod, addr);
+
+ /* Add to CSEQ */
+ snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, sip_methods[sipmethod].text);
+ p->ocseq = r->ocseq;
+
+ build_via(p);
+ add_header(&req, "Via", p->via);
+ add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
+ add_header(&req, "From", from);
+ add_header(&req, "To", to);
+ add_header(&req, "Call-ID", p->callid);
+ add_header(&req, "CSeq", tmp);
+ if (!ast_strlen_zero(global_useragent))
+ add_header(&req, "User-Agent", global_useragent);
+
+
+ if (auth) /* Add auth header */
+ add_header(&req, authheader, auth);
+ else if (!ast_strlen_zero(r->nonce)) {
+ char digest[1024];
+
+ /* We have auth data to reuse, build a digest header.
+ * Note, this is not always useful because some parties do not
+ * like nonces to be reused (for good reasons!) so they will
+ * challenge us anyways.
+ */
+ if (sipdebug)
+ ast_debug(1, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname);
+ ast_string_field_set(p, realm, r->realm);
+ ast_string_field_set(p, nonce, r->nonce);
+ ast_string_field_set(p, domain, r->domain);
+ ast_string_field_set(p, opaque, r->opaque);
+ ast_string_field_set(p, qop, r->qop);
+ p->noncecount = ++r->noncecount;
+
+ memset(digest,0,sizeof(digest));
+ if(!build_reply_digest(p, sipmethod, digest, sizeof(digest)))
+ add_header(&req, "Authorization", digest);
+ else
+ ast_log(LOG_NOTICE, "No authorization available for authentication of registration to %s@%s\n", r->username, r->hostname);
+
+ }
+
+ snprintf(tmp, sizeof(tmp), "%d", r->expiry);
+ add_header(&req, "Expires", tmp);
+ add_header(&req, "Contact", p->our_contact);
+ add_header(&req, "Event", "registration");
+ add_header_contentLength(&req, 0);
+
+ initialize_initreq(p, &req);
+ if (sip_debug_test_pvt(p)) {
+ ast_verbose("REGISTER %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
+ }
+ r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT;
+ r->regattempts++; /* Another attempt */
+ ast_debug(4, "REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname);
+
+ return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
+}
+
+/*! \brief Transmit text with SIP MESSAGE method */
+static int transmit_message_with_text(struct sip_pvt *p, const char *text)
+{
+ struct sip_request req;
+
+ reqprep(&req, p, SIP_MESSAGE, 0, 1);
+ add_text(&req, text);
+ return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
+/*! \brief Allocate SIP refer structure */
+static int sip_refer_allocate(struct sip_pvt *p)
+{
+ p->refer = ast_calloc(1, sizeof(struct sip_refer));
+ return p->refer ? 1 : 0;
+}
+
+/*! \brief Transmit SIP REFER message (initiated by the transfer() dialplan application
+ \note this is currently broken as we have no way of telling the dialplan
+ engine whether a transfer succeeds or fails.
+ \todo Fix the transfer() dialplan function so that a transfer may fail
+*/
+static int transmit_refer(struct sip_pvt *p, const char *dest)
+{
+ struct sip_request req = {
+ .headers = 0,
+ };
+ char from[256];
+ const char *of;
+ char *c;
+ char referto[256];
+ char *ttag, *ftag;
+ char *theirtag = ast_strdupa(p->theirtag);
+
+ if (sipdebug)
+ ast_debug(1, "SIP transfer of %s to %s\n", p->callid, dest);
+
+ /* Are we transfering an inbound or outbound call ? */
+ if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ of = get_header(&p->initreq, "To");
+ ttag = theirtag;
+ ftag = p->tag;
+ } else {
+ of = get_header(&p->initreq, "From");
+ ftag = theirtag;
+ ttag = p->tag;
+ }
+
+ ast_copy_string(from, of, sizeof(from));
+ of = get_in_brackets(from);
+ ast_string_field_set(p, from, of);
+ if (!strncasecmp(of, "sip:", 4))
+ of += 4;
+ else if (!strncasecmp(of, "sips:", 5))
+ of += 5;
+ else
+ ast_log(LOG_NOTICE, "From address missing 'sip(s):', using it anyway\n");
+ /* Get just the username part */
+ if ((c = strchr(dest, '@')))
+ c = NULL;
+ else if ((c = strchr(of, '@')))
+ *c++ = '\0';
+ if (c)
+ snprintf(referto, sizeof(referto), "<sip:%s@%s>", dest, c);
+ else
+ snprintf(referto, sizeof(referto), "<sip:%s>", dest);
+
+ /* save in case we get 407 challenge */
+ sip_refer_allocate(p);
+ ast_copy_string(p->refer->refer_to, referto, sizeof(p->refer->refer_to));
+ ast_copy_string(p->refer->referred_by, p->our_contact, sizeof(p->refer->referred_by));
+ p->refer->status = REFER_SENT; /* Set refer status */
+
+ reqprep(&req, p, SIP_REFER, 0, 1);
+ add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
+
+ add_header(&req, "Refer-To", referto);
+ add_header(&req, "Allow", ALLOWED_METHODS);
+ add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
+ if (!ast_strlen_zero(p->our_contact))
+ add_header(&req, "Referred-By", p->our_contact);
+
+ return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+ /* We should propably wait for a NOTIFY here until we ack the transfer */
+ /* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
+
+ /*! \todo In theory, we should hang around and wait for a reply, before
+ returning to the dial plan here. Don't know really how that would
+ affect the transfer() app or the pbx, but, well, to make this
+ useful we should have a STATUS code on transfer().
+ */
+}
+
+
+/*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
+static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration)
+{
+ struct sip_request req;
+
+ reqprep(&req, p, SIP_INFO, 0, 1);
+ add_digit(&req, digit, duration, (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO));
+ return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
+/*! \brief Send SIP INFO with video update request */
+static int transmit_info_with_vidupdate(struct sip_pvt *p)
+{
+ struct sip_request req;
+
+ reqprep(&req, p, SIP_INFO, 0, 1);
+ add_vidupdate(&req);
+ return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
+/*! \brief Transmit generic SIP request
+ returns XMIT_ERROR if transmit failed with a critical error (don't retry)
+*/
+static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
+{
+ struct sip_request resp;
+
+ if (sipmethod == SIP_ACK)
+ p->invitestate = INV_CONFIRMED;
+
+ reqprep(&resp, p, sipmethod, seqno, newbranch);
+ if (sipmethod == SIP_CANCEL && p->answered_elsewhere)
+ add_header(&resp, "Reason:", "SIP;cause=200;text=\"Call completed elsewhere\"");
+
+ add_header_contentLength(&resp, 0);
+ return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
+}
+
+/*! \brief return the request and response heade for a 401 or 407 code */
+static void auth_headers(enum sip_auth_type code, char **header, char **respheader)
+{
+ if (code == WWW_AUTH) { /* 401 */
+ *header = "WWW-Authenticate";
+ *respheader = "Authorization";
+ } else if (code == PROXY_AUTH) { /* 407 */
+ *header = "Proxy-Authenticate";
+ *respheader = "Proxy-Authorization";
+ } else {
+ ast_verbose("-- wrong response code %d\n", code);
+ *header = *respheader = "Invalid";
+ }
+}
+
+/*! \brief Transmit SIP request, auth added */
+static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
+{
+ struct sip_request resp;
+
+ reqprep(&resp, p, sipmethod, seqno, newbranch);
+ if (!ast_strlen_zero(p->realm)) {
+ char digest[1024];
+
+ memset(digest, 0, sizeof(digest));
+ if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
+ char *dummy, *response;
+ enum sip_auth_type code = p->options ? p->options->auth_type : PROXY_AUTH; /* XXX force 407 if unknown */
+ auth_headers(code, &dummy, &response);
+ add_header(&resp, response, digest);
+ } else
+ ast_log(LOG_WARNING, "No authentication available for call %s\n", p->callid);
+ }
+ /* If we are hanging up and know a cause for that, send it in clear text to make
+ debugging easier. */
+ if (sipmethod == SIP_BYE && p->owner && p->owner->hangupcause) {
+ char buf[10];
+
+ add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
+ snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
+ add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
+ }
+
+ add_header_contentLength(&resp, 0);
+ return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
+}
+
+/*! \brief Remove registration data from realtime database or AST/DB when registration expires */
+static void destroy_association(struct sip_peer *peer)
+{
+ int realtimeregs = ast_check_realtime("sipregs");
+ char *tablename = (realtimeregs) ? "sipregs" : "sippeers";
+
+ if (!sip_cfg.ignore_regexpire) {
+ if (peer->rt_fromcontact)
+ ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "defaultuser", "", "regserver", "", NULL);
+ else
+ ast_db_del("SIP/Registry", peer->name);
+ }
+}
+
+/*! \brief Expire registration of SIP peer */
+static int expire_register(const void *data)
+{
+ struct sip_peer *peer = (struct sip_peer *)data;
+
+ if (!peer) /* Hmmm. We have no peer. Weird. */
+ return 0;
+
+ memset(&peer->addr, 0, sizeof(peer->addr));
+
+ destroy_association(peer); /* remove registration data from storage */
+
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
+ register_peer_exten(peer, FALSE); /* Remove regexten */
+ peer->expire = -1;
+ ast_device_state_changed("SIP/%s", peer->name);
+
+ /* Do we need to release this peer from memory?
+ Only for realtime peers and autocreated peers
+ */
+ if (peer->is_realtime)
+ ast_debug(3,"-REALTIME- peer expired registration. Name: %s. Realtime peer objects now %d\n", peer->name, rpeerobjs);
+
+ if (peer->selfdestruct ||
+ ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
+ peer = ASTOBJ_CONTAINER_UNLINK(&peerl, peer); /* Remove from peer list */
+ unref_peer(peer); /* Remove from memory */
+ }
+
+ return 0;
+}
+
+/*! \brief Poke peer (send qualify to check if peer is alive and well) */
+static int sip_poke_peer_s(const void *data)
+{
+ struct sip_peer *peer = (struct sip_peer *)data;
+
+ peer->pokeexpire = -1;
+ sip_poke_peer(peer);
+ return 0;
+}
+
+/*! \brief Get registration details from Asterisk DB */
+static void reg_source_db(struct sip_peer *peer)
+{
+ char data[256];
+ struct in_addr in;
+ int expiry;
+ int port;
+ char *scan, *addr, *port_str, *expiry_str, *username, *contact;
+
+ if (peer->rt_fromcontact)
+ return;
+ if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data)))
+ return;
+
+ scan = data;
+ addr = strsep(&scan, ":");
+ port_str = strsep(&scan, ":");
+ expiry_str = strsep(&scan, ":");
+ username = strsep(&scan, ":");
+ contact = scan; /* Contact include sip: and has to be the last part of the database entry as long as we use : as a separator */
+
+ if (!inet_aton(addr, &in))
+ return;
+
+ if (port_str)
+ port = atoi(port_str);
+ else
+ return;
+
+ if (expiry_str)
+ expiry = atoi(expiry_str);
+ else
+ return;
+
+ if (username)
+ ast_copy_string(peer->username, username, sizeof(peer->username));
+ if (contact)
+ ast_copy_string(peer->fullcontact, contact, sizeof(peer->fullcontact));
+
+ ast_debug(2, "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n",
+ peer->name, peer->username, ast_inet_ntoa(in), port, expiry);
+
+ memset(&peer->addr, 0, sizeof(peer->addr));
+ peer->addr.sin_family = AF_INET;
+ peer->addr.sin_addr = in;
+ peer->addr.sin_port = htons(port);
+ if (sipsock < 0) {
+ /* SIP isn't up yet, so schedule a poke only, pretty soon */
+ peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched,
+ ast_random() % 5000 + 1, sip_poke_peer_s, peer);
+ } else
+ sip_poke_peer(peer);
+ peer->expire = ast_sched_replace(peer->expire, sched,
+ (expiry + 10) * 1000, expire_register, peer);
+ register_peer_exten(peer, TRUE);
+}
+
+/*! \brief Save contact header for 200 OK on INVITE */
+static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
+{
+ char contact[BUFSIZ];
+ char *c;
+
+ /* Look for brackets */
+ ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
+ c = get_in_brackets(contact);
+
+ /* Save full contact to call pvt for later bye or re-invite */
+ ast_string_field_set(pvt, fullcontact, c);
+
+ /* Save URI for later ACKs, BYE or RE-invites */
+ ast_string_field_set(pvt, okcontacturi, c);
+
+ /* We should return false for URI:s we can't handle,
+ like tel:, mailto:,ldap: etc */
+ return TRUE;
+}
+
+/*! \brief Change the other partys IP address based on given contact */
+static int set_address_from_contact(struct sip_pvt *pvt)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port;
+ char *host, *pt;
+ char *contact, *contact2;
+
+
+ if (ast_test_flag(&pvt->flags[0], SIP_NAT_ROUTE)) {
+ /* NAT: Don't trust the contact field. Just use what they came to us
+ with. */
+ pvt->sa = pvt->recv;
+ return 0;
+ }
+
+
+ /* Work on a copy */
+ contact = ast_strdupa(pvt->fullcontact);
+ contact2 = ast_strdupa(pvt->fullcontact);
+ /* We have only the part in <brackets> here so we just need to parse a SIP URI.*/
+
+ if (pvt->socket.type == SIP_TRANSPORT_TLS) {
+ if (parse_uri(contact, "sips:", &contact, NULL, &host, &pt, NULL)) {
+ if (parse_uri(contact2, "sip:", &contact, NULL, &host, &pt, NULL))
+ ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact);
+ }
+ port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_TLS_PORT;
+ } else {
+ if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL))
+ ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact);
+ port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT;
+ }
+
+ ast_verbose("--- set_address_from_contact host '%s'\n", host);
+
+ /* XXX This could block for a long time XXX */
+ /* We should only do this if it's a name, not an IP */
+ hp = ast_gethostbyname(host, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Invalid host name in Contact: (can't resolve in DNS) : '%s'\n", host);
+ return -1;
+ }
+ pvt->sa.sin_family = AF_INET;
+ memcpy(&pvt->sa.sin_addr, hp->h_addr, sizeof(pvt->sa.sin_addr));
+ pvt->sa.sin_port = htons(port);
+
+ return 0;
+}
+
+
+/*! \brief Parse contact header and save registration (peer registration) */
+static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req)
+{
+ char contact[BUFSIZ];
+ char data[BUFSIZ];
+ const char *expires = get_header(req, "Expires");
+ int expiry = atoi(expires);
+ char *curi, *host, *pt, *curi2;
+ int port;
+ const char *useragent;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct sockaddr_in oldsin;
+
+ ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
+
+ if (ast_strlen_zero(expires)) { /* No expires header, try look in Contact: */
+ char *s = strcasestr(contact, ";expires=");
+ if (s) {
+ expires = strsep(&s, ";"); /* trim ; and beyond */
+ if (sscanf(expires + 9, "%d", &expiry) != 1)
+ expiry = default_expiry;
+ } else {
+ /* Nothing has been specified */
+ expiry = default_expiry;
+ }
+ }
+
+ pvt->socket = peer->socket = req->socket;
+
+ /* Look for brackets */
+ curi = contact;
+ if (strchr(contact, '<') == NULL) /* No <, check for ; and strip it */
+ strsep(&curi, ";"); /* This is Header options, not URI options */
+ curi = get_in_brackets(contact);
+ curi2 = ast_strdupa(curi);
+
+ /* if they did not specify Contact: or Expires:, they are querying
+ what we currently have stored as their contact address, so return
+ it
+ */
+ if (ast_strlen_zero(curi) && ast_strlen_zero(expires)) {
+ /* If we have an active registration, tell them when the registration is going to expire */
+ if (peer->expire > -1 && !ast_strlen_zero(peer->fullcontact))
+ pvt->expiry = ast_sched_when(sched, peer->expire);
+ return PARSE_REGISTER_QUERY;
+ } else if (!strcasecmp(curi, "*") || !expiry) { /* Unregister this peer */
+ /* This means remove all registrations and return OK */
+ memset(&peer->addr, 0, sizeof(peer->addr));
+ if (peer->expire > -1)
+ ast_sched_del(sched, peer->expire);
+ peer->expire = -1;
+
+ destroy_association(peer);
+
+ register_peer_exten(peer, FALSE); /* Remove extension from regexten= setting in sip.conf */
+ peer->fullcontact[0] = '\0';
+ peer->useragent[0] = '\0';
+ peer->sipoptions = 0;
+ peer->lastms = 0;
+
+ ast_verb(3, "Unregistered SIP '%s'\n", peer->name);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", peer->name);
+ return PARSE_REGISTER_UPDATE;
+ }
+
+ /* Store whatever we got as a contact from the client */
+ ast_copy_string(peer->fullcontact, curi, sizeof(peer->fullcontact));
+
+ /* For the 200 OK, we should use the received contact */
+ ast_string_field_build(pvt, our_contact, "<%s>", curi);
+
+ /* Make sure it's a SIP URL */
+ if (pvt->socket.type == SIP_TRANSPORT_TLS) {
+ if (parse_uri(curi, "sips:", &curi, NULL, &host, &pt, NULL)) {
+ if (parse_uri(curi2, "sip:", &curi, NULL, &host, &pt, NULL))
+ ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n");
+ }
+ port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_TLS_PORT;
+ } else {
+ if (parse_uri(curi, "sip:", &curi, NULL, &host, &pt, NULL))
+ ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n");
+ port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT;
+ }
+
+ oldsin = peer->addr;
+ if (!ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE)) {
+ /* XXX This could block for a long time XXX */
+ hp = ast_gethostbyname(host, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Invalid host '%s'\n", host);
+ return PARSE_REGISTER_FAILED;
+ }
+ peer->addr.sin_family = AF_INET;
+ memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
+ peer->addr.sin_port = htons(port);
+ } else {
+ /* Don't trust the contact field. Just use what they came to us
+ with */
+ peer->addr = pvt->recv;
+ }
+
+ /* Save SIP options profile */
+ peer->sipoptions = pvt->sipoptions;
+
+ if (!ast_strlen_zero(curi) && ast_strlen_zero(peer->username))
+ ast_copy_string(peer->username, curi, sizeof(peer->username));
+
+ if (peer->expire > -1) {
+ ast_sched_del(sched, peer->expire);
+ peer->expire = -1;
+ }
+ if (expiry > max_expiry)
+ expiry = max_expiry;
+ if (expiry < min_expiry)
+ expiry = min_expiry;
+ peer->expire = peer->is_realtime ? -1 :
+ ast_sched_add(sched, (expiry + 10) * 1000, expire_register, peer);
+ pvt->expiry = expiry;
+ snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expiry, peer->username, peer->fullcontact);
+ /* Saving TCP connections is useless, we won't be able to reconnect */
+ if (!peer->rt_fromcontact && (peer->socket.type & SIP_TRANSPORT_UDP))
+ ast_db_put("SIP/Registry", peer->name, data);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name);
+
+ /* Is this a new IP address for us? */
+ if (inaddrcmp(&peer->addr, &oldsin)) {
+ sip_poke_peer(peer);
+ ast_verb(3, "Registered SIP '%s' at %s port %d expires %d\n", peer->name, ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expiry);
+ register_peer_exten(peer, TRUE);
+ }
+
+ /* Save User agent */
+ useragent = get_header(req, "User-Agent");
+ if (strcasecmp(useragent, peer->useragent)) { /* XXX copy if they are different ? */
+ ast_copy_string(peer->useragent, useragent, sizeof(peer->useragent));
+ ast_verb(4, "Saved useragent \"%s\" for peer %s\n", peer->useragent, peer->name);
+ }
+ return PARSE_REGISTER_UPDATE;
+}
+
+/*! \brief Remove route from route list */
+static void free_old_route(struct sip_route *route)
+{
+ struct sip_route *next;
+
+ while (route) {
+ next = route->next;
+ ast_free(route);
+ route = next;
+ }
+}
+
+/*! \brief List all routes - mostly for debugging */
+static void list_route(struct sip_route *route)
+{
+ if (!route)
+ ast_verbose("list_route: no route\n");
+ else {
+ for (;route; route = route->next)
+ ast_verbose("list_route: hop: <%s>\n", route->hop);
+ }
+}
+
+/*! \brief Build route list from Record-Route header */
+static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards)
+{
+ struct sip_route *thishop, *head, *tail;
+ int start = 0;
+ int len;
+ const char *rr, *contact, *c;
+
+ /* Once a persistant route is set, don't fool with it */
+ if (p->route && p->route_persistant) {
+ ast_debug(1, "build_route: Retaining previous route: <%s>\n", p->route->hop);
+ return;
+ }
+
+ if (p->route) {
+ free_old_route(p->route);
+ p->route = NULL;
+ }
+
+ p->route_persistant = backwards;
+
+ /* Build a tailq, then assign it to p->route when done.
+ * If backwards, we add entries from the head so they end up
+ * in reverse order. However, we do need to maintain a correct
+ * tail pointer because the contact is always at the end.
+ */
+ head = NULL;
+ tail = head;
+ /* 1st we pass through all the hops in any Record-Route headers */
+ for (;;) {
+ /* Each Record-Route header */
+ rr = __get_header(req, "Record-Route", &start);
+ if (*rr == '\0')
+ break;
+ for (; (rr = strchr(rr, '<')) ; rr += len) { /* Each route entry */
+ ++rr;
+ len = strcspn(rr, ">") + 1;
+ /* Make a struct route */
+ if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
+ /* ast_calloc is not needed because all fields are initialized in this block */
+ ast_copy_string(thishop->hop, rr, len);
+ ast_debug(2, "build_route: Record-Route hop: <%s>\n", thishop->hop);
+ /* Link in */
+ if (backwards) {
+ /* Link in at head so they end up in reverse order */
+ thishop->next = head;
+ head = thishop;
+ /* If this was the first then it'll be the tail */
+ if (!tail)
+ tail = thishop;
+ } else {
+ thishop->next = NULL;
+ /* Link in at the end */
+ if (tail)
+ tail->next = thishop;
+ else
+ head = thishop;
+ tail = thishop;
+ }
+ }
+ }
+ }
+
+ /* Only append the contact if we are dealing with a strict router */
+ if (!head || (!ast_strlen_zero(head->hop) && strstr(head->hop,";lr") == NULL) ) {
+ /* 2nd append the Contact: if there is one */
+ /* Can be multiple Contact headers, comma separated values - we just take the first */
+ contact = get_header(req, "Contact");
+ if (!ast_strlen_zero(contact)) {
+ ast_debug(2, "build_route: Contact hop: %s\n", contact);
+ /* Look for <: delimited address */
+ c = strchr(contact, '<');
+ if (c) {
+ /* Take to > */
+ ++c;
+ len = strcspn(c, ">") + 1;
+ } else {
+ /* No <> - just take the lot */
+ c = contact;
+ len = strlen(contact) + 1;
+ }
+ if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
+ /* ast_calloc is not needed because all fields are initialized in this block */
+ ast_copy_string(thishop->hop, c, len);
+ thishop->next = NULL;
+ /* Goes at the end */
+ if (tail)
+ tail->next = thishop;
+ else
+ head = thishop;
+ }
+ }
+ }
+
+ /* Store as new route */
+ p->route = head;
+
+ /* For debugging dump what we ended up with */
+ if (sip_debug_test_pvt(p))
+ list_route(p->route);
+}
+
+AST_THREADSTORAGE(check_auth_buf);
+#define CHECK_AUTH_BUF_INITLEN 256
+
+/*! \brief Check user authorization from peer definition
+ Some actions, like REGISTER and INVITEs from peers require
+ authentication (if peer have secret set)
+ \return 0 on success, non-zero on error
+*/
+static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
+ const char *secret, const char *md5secret, int sipmethod,
+ char *uri, enum xmittype reliable, int ignore)
+{
+ const char *response;
+ char *reqheader, *respheader;
+ const char *authtoken;
+ char a1_hash[256];
+ char resp_hash[256]="";
+ char *c;
+ int wrongnonce = FALSE;
+ int good_response;
+ const char *usednonce = p->randdata;
+ struct ast_str *buf;
+ int res;
+
+ /* table of recognised keywords, and their value in the digest */
+ enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST };
+ struct x {
+ const char *key;
+ const char *s;
+ } *i, keys[] = {
+ [K_RESP] = { "response=", "" },
+ [K_URI] = { "uri=", "" },
+ [K_USER] = { "username=", "" },
+ [K_NONCE] = { "nonce=", "" },
+ [K_LAST] = { NULL, NULL}
+ };
+
+ /* Always OK if no secret */
+ if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret))
+ return AUTH_SUCCESSFUL;
+
+ /* Always auth with WWW-auth since we're NOT a proxy */
+ /* Using proxy-auth in a B2BUA may block proxy authorization in the same transaction */
+ response = "401 Unauthorized";
+
+ /*
+ * Note the apparent swap of arguments below, compared to other
+ * usages of auth_headers().
+ */
+ auth_headers(WWW_AUTH, &respheader, &reqheader);
+
+ authtoken = get_header(req, reqheader);
+ if (ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) {
+ /* This is a retransmitted invite/register/etc, don't reconstruct authentication
+ information */
+ if (!reliable) {
+ /* Resend message if this was NOT a reliable delivery. Otherwise the
+ retransmission should get it */
+ transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
+ /* Schedule auto destroy in 32 seconds (according to RFC 3261) */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ return AUTH_CHALLENGE_SENT;
+ } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
+ /* We have no auth, so issue challenge and request authentication */
+ ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
+ transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
+ /* Schedule auto destroy in 32 seconds */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return AUTH_CHALLENGE_SENT;
+ }
+
+ /* --- We have auth, so check it */
+
+ /* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
+ an example in the spec of just what it is you're doing a hash on. */
+
+ if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN)))
+ return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
+
+ /* Make a copy of the response and parse it */
+ res = ast_str_set(&buf, 0, "%s", authtoken);
+
+ if (res == AST_DYNSTR_BUILD_FAILED)
+ return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
+
+ c = buf->str;
+
+ while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */
+ for (i = keys; i->key != NULL; i++) {
+ const char *separator = ","; /* default */
+
+ if (strncasecmp(c, i->key, strlen(i->key)) != 0)
+ continue;
+ /* Found. Skip keyword, take text in quotes or up to the separator. */
+ c += strlen(i->key);
+ if (*c == '"') { /* in quotes. Skip first and look for last */
+ c++;
+ separator = "\"";
+ }
+ i->s = c;
+ strsep(&c, separator);
+ break;
+ }
+ if (i->key == NULL) /* not found, jump after space or comma */
+ strsep(&c, " ,");
+ }
+
+ /* Verify that digest username matches the username we auth as */
+ if (strcmp(username, keys[K_USER].s)) {
+ ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
+ username, keys[K_USER].s);
+ /* Oops, we're trying something here */
+ return AUTH_USERNAME_MISMATCH;
+ }
+
+ /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */
+ if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */
+ wrongnonce = TRUE;
+ usednonce = keys[K_NONCE].s;
+ }
+
+ if (!ast_strlen_zero(md5secret))
+ ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
+ else {
+ char a1[256];
+ snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret);
+ ast_md5_hash(a1_hash, a1);
+ }
+
+ /* compute the expected response to compare with what we received */
+ {
+ char a2[256];
+ char a2_hash[256];
+ char resp[256];
+
+ snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text,
+ S_OR(keys[K_URI].s, uri));
+ ast_md5_hash(a2_hash, a2);
+ snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash);
+ ast_md5_hash(resp_hash, resp);
+ }
+
+ good_response = keys[K_RESP].s &&
+ !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash));
+ if (wrongnonce) {
+ ast_string_field_build(p, randdata, "%08lx", ast_random());
+ if (good_response) {
+ if (sipdebug)
+ ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", get_header(req, "To"));
+ /* We got working auth token, based on stale nonce . */
+ transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, TRUE);
+ } else {
+ /* Everything was wrong, so give the device one more try with a new challenge */
+ if (sipdebug)
+ ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To"));
+ transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, FALSE);
+ }
+
+ /* Schedule auto destroy in 32 seconds */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return AUTH_CHALLENGE_SENT;
+ }
+ if (good_response) {
+ append_history(p, "AuthOK", "Auth challenge succesful for %s", username);
+ return AUTH_SUCCESSFUL;
+ }
+
+ /* Ok, we have a bad username/secret pair */
+ /* Tell the UAS not to re-send this authentication data, because
+ it will continue to fail
+ */
+
+ return AUTH_SECRET_FAILED;
+}
+
+/*! \brief Change onhold state of a peer using a pvt structure */
+static void sip_peer_hold(struct sip_pvt *p, int hold)
+{
+ struct sip_peer *peer = find_peer(p->peername, NULL, 1);
+
+ if (!peer)
+ return;
+
+ /* If they put someone on hold, increment the value... otherwise decrement it */
+ ast_atomic_fetchadd_int(&peer->onHold, (hold ? +1 : -1));
+
+ /* Request device state update */
+ ast_device_state_changed("SIP/%s", peer->name);
+
+ return;
+}
+
+/*! \brief Receive MWI events that we have subscribed to */
+static void mwi_event_cb(const struct ast_event *event, void *userdata)
+{
+ struct sip_peer *peer = userdata;
+
+ ASTOBJ_RDLOCK(peer);
+ sip_send_mwi_to_peer(peer, event, 0);
+ ASTOBJ_UNLOCK(peer);
+}
+
+/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
+\note If you add an "hint" priority to the extension in the dial plan,
+ you will get notifications on device state changes */
+static int cb_extensionstate(char *context, char* exten, int state, void *data)
+{
+ struct sip_pvt *p = data;
+
+ sip_pvt_lock(p);
+
+ switch(state) {
+ case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
+ case AST_EXTENSION_REMOVED: /* Extension is gone */
+ if (p->autokillid > -1)
+ sip_cancel_destroy(p); /* Remove subscription expiry for renewals */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
+ ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
+ p->stateid = -1;
+ p->subscribed = NONE;
+ append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
+ break;
+ default: /* Tell user */
+ p->laststate = state;
+ break;
+ }
+ if (p->subscribed != NONE) /* Only send state NOTIFY if we know the format */
+ transmit_state_notify(p, state, 1, FALSE);
+
+ ast_verb(2, "Extension Changed %s new state %s for Notify User %s\n", exten, ast_extension_state2str(state), p->username);
+
+ sip_pvt_unlock(p);
+
+ return 0;
+}
+
+/*! \brief Send a fake 401 Unauthorized response when the administrator
+ wants to hide the names of local users/peers from fishers
+ */
+static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable)
+{
+ ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
+ transmit_response_with_auth(p, "401 Unauthorized", req, p->randdata, reliable, "WWW-Authenticate", 0);
+}
+
+/*!
+ * Terminate the uri at the first ';' or space.
+ * Technically we should ignore escaped space per RFC3261 (19.1.1 etc)
+ * but don't do it for the time being. Remember the uri format is:
+ *\verbatim
+ *
+ * sip:user:password@host:port;uri-parameters?headers
+ * sips:user:password@host:port;uri-parameters?headers
+ *
+ *\endverbatim
+ */
+static char *terminate_uri(char *uri)
+{
+ char *t = uri;
+ while (*t && *t > ' ' && *t != ';')
+ t++;
+ *t = '\0';
+ return uri;
+}
+
+/*! \brief Verify registration of user
+ - Registration is done in several steps, first a REGISTER without auth
+ to get a challenge (nonce) then a second one with auth
+ - Registration requests are only matched with peers that are marked as "dynamic"
+ */
+static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin,
+ struct sip_request *req, char *uri)
+{
+ enum check_auth_result res = AUTH_NOT_FOUND;
+ struct sip_peer *peer;
+ char tmp[256];
+ char *name, *c;
+ char *domain;
+
+ terminate_uri(uri); /* warning, overwrite the string */
+
+ ast_copy_string(tmp, get_header(req, "To"), sizeof(tmp));
+ if (pedanticsipchecking)
+ ast_uri_decode(tmp);
+
+ c = get_in_brackets(tmp);
+ c = remove_uri_parameters(c);
+
+ if (!strncasecmp(c, "sip:", 4)) {
+ name = c + 4;
+ } else if (!strncasecmp(c, "sips:", 5)) {
+ name = c + 5;
+ } else {
+ name = c;
+ ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_inet_ntoa(sin->sin_addr));
+ }
+
+ /* XXX here too we interpret a missing @domain as a name-only
+ * URI, whereas the RFC says this is a domain-only uri.
+ */
+ /* Strip off the domain name */
+ if ((c = strchr(name, '@'))) {
+ *c++ = '\0';
+ domain = c;
+ if ((c = strchr(domain, ':'))) /* Remove :port */
+ *c = '\0';
+ if (!AST_LIST_EMPTY(&domain_list)) {
+ if (!check_sip_domain(domain, NULL, 0)) {
+ transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
+ return AUTH_UNKNOWN_DOMAIN;
+ }
+ }
+ }
+ c = strchr(name, ';'); /* Remove any Username parameters */
+ if (c)
+ *c = '\0';
+
+ ast_string_field_set(p, exten, name);
+ build_contact(p);
+ peer = find_peer(name, NULL, 1);
+ if (!(peer && ast_apply_ha(peer->ha, sin))) {
+ /* Peer fails ACL check */
+ if (peer) {
+ unref_peer(peer);
+ peer = NULL;
+ res = AUTH_ACL_FAILED;
+ } else
+ res = AUTH_NOT_FOUND;
+ }
+
+ if (peer) {
+ /* Set Frame packetization */
+ if (p->rtp) {
+ ast_rtp_codec_setpref(p->rtp, &peer->prefs);
+ p->autoframing = peer->autoframing;
+ }
+ if (!peer->host_dynamic) {
+ ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name);
+ res = AUTH_PEER_NOT_DYNAMIC;
+ } else {
+ ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT);
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_REGISTERTRYING))
+ transmit_response(p, "100 Trying", req);
+ if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri, XMIT_UNRELIABLE, req->ignore))) {
+ sip_cancel_destroy(p);
+
+ /* We have a successful registration attempt with proper authentication,
+ now, update the peer */
+ switch (parse_register_contact(p, peer, req)) {
+ case PARSE_REGISTER_FAILED:
+ ast_log(LOG_WARNING, "Failed to parse contact info\n");
+ transmit_response_with_date(p, "400 Bad Request", req);
+ peer->lastmsgssent = -1;
+ res = 0;
+ break;
+ case PARSE_REGISTER_QUERY:
+ transmit_response_with_date(p, "200 OK", req);
+ peer->lastmsgssent = -1;
+ res = 0;
+ break;
+ case PARSE_REGISTER_UPDATE:
+ update_peer(peer, p->expiry);
+ /* Say OK and ask subsystem to retransmit msg counter */
+ transmit_response_with_date(p, "200 OK", req);
+ if (!ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY))
+ peer->lastmsgssent = -1;
+ res = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (!peer && autocreatepeer) {
+ /* Create peer if we have autocreate mode enabled */
+ peer = temp_peer(name);
+ if (peer) {
+ ASTOBJ_CONTAINER_LINK(&peerl, peer);
+ sip_cancel_destroy(p);
+ switch (parse_register_contact(p, peer, req)) {
+ case PARSE_REGISTER_FAILED:
+ ast_log(LOG_WARNING, "Failed to parse contact info\n");
+ transmit_response_with_date(p, "400 Bad Request", req);
+ peer->lastmsgssent = -1;
+ res = 0;
+ break;
+ case PARSE_REGISTER_QUERY:
+ transmit_response_with_date(p, "200 OK", req);
+ peer->lastmsgssent = -1;
+ res = 0;
+ break;
+ case PARSE_REGISTER_UPDATE:
+ /* Say OK and ask subsystem to retransmit msg counter */
+ transmit_response_with_date(p, "200 OK", req);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name);
+ peer->lastmsgssent = -1;
+ res = 0;
+ break;
+ }
+ }
+ }
+ if (!res) {
+ ast_device_state_changed("SIP/%s", peer->name);
+ }
+ if (res < 0) {
+ switch (res) {
+ case AUTH_SECRET_FAILED:
+ /* Wrong password in authentication. Go away, don't try again until you fixed it */
+ transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
+ break;
+ case AUTH_USERNAME_MISMATCH:
+ /* Username and digest username does not match.
+ Asterisk uses the From: username for authentication. We need the
+ users to use the same authentication user name until we support
+ proper authentication by digest auth name */
+ transmit_response(p, "403 Authentication user name does not match account name", &p->initreq);
+ break;
+ case AUTH_NOT_FOUND:
+ case AUTH_PEER_NOT_DYNAMIC:
+ case AUTH_ACL_FAILED:
+ if (global_alwaysauthreject) {
+ transmit_fake_auth_response(p, &p->initreq, 1);
+ } else {
+ /* URI not found */
+ if (res == AUTH_PEER_NOT_DYNAMIC)
+ transmit_response(p, "403 Forbidden", &p->initreq);
+ else
+ transmit_response(p, "404 Not found", &p->initreq);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (peer)
+ unref_peer(peer);
+
+ return res;
+}
+
+/*! \brief Translate referring cause */
+static void sip_set_redirstr(struct sip_pvt *p, char *reason) {
+
+ if (strcmp(reason, "unknown")==0) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (strcmp(reason, "user-busy")==0) {
+ ast_string_field_set(p, redircause, "BUSY");
+ } else if (strcmp(reason, "no-answer")==0) {
+ ast_string_field_set(p, redircause, "NOANSWER");
+ } else if (strcmp(reason, "unavailable")==0) {
+ ast_string_field_set(p, redircause, "UNREACHABLE");
+ } else if (strcmp(reason, "unconditional")==0) {
+ ast_string_field_set(p, redircause, "UNCONDITIONAL");
+ } else if (strcmp(reason, "time-of-day")==0) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (strcmp(reason, "do-not-disturb")==0) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (strcmp(reason, "deflection")==0) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (strcmp(reason, "follow-me")==0) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (strcmp(reason, "out-of-service")==0) {
+ ast_string_field_set(p, redircause, "UNREACHABLE");
+ } else if (strcmp(reason, "away")==0) {
+ ast_string_field_set(p, redircause, "UNREACHABLE");
+ } else {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ }
+}
+
+/*! \brief Get referring dnis */
+static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq)
+{
+ char tmp[256], *exten, *rexten, *rdomain;
+ char *params, *reason = NULL;
+ struct sip_request *req;
+
+ req = oreq ? oreq : &p->initreq;
+
+ ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp));
+ if (ast_strlen_zero(tmp))
+ return 0;
+
+ exten = get_in_brackets(tmp);
+ if (!strncasecmp(exten, "sip:", 4)) {
+ exten += 4;
+ } else if (!strncasecmp(exten, "sips:", 5)) {
+ exten += 5;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", exten);
+ return -1;
+ }
+
+ /* Get diversion-reason param if present */
+ if ((params = strchr(tmp, ';'))) {
+ *params = '\0'; /* Cut off parameters */
+ params++;
+ while (*params == ';' || *params == ' ')
+ params++;
+ /* Check if we have a reason parameter */
+ if ((reason = strcasestr(params, "reason="))) {
+ reason+=7;
+ /* Remove enclosing double-quotes */
+ if (*reason == '"')
+ ast_strip_quoted(reason, "\"", "\"");
+ if (!ast_strlen_zero(reason)) {
+ sip_set_redirstr(p, reason);
+ if (p->owner) {
+ pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause);
+ pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason);
+ }
+ }
+ }
+ }
+
+ rdomain = exten;
+ rexten = strsep(&rdomain, "@"); /* trim anything after @ */
+ if (p->owner)
+ pbx_builtin_setvar_helper(p->owner, "__SIPRDNISDOMAIN", rdomain);
+
+ if (sip_debug_test_pvt(p))
+ ast_verbose("RDNIS for this call is is %s (reason %s)\n", exten, reason ? reason : "");
+
+ ast_string_field_set(p, rdnis, rexten);
+
+ return 0;
+}
+
+/*! \brief Find out who the call is for.
+ We use the request uri as a destination.
+ This code assumes authentication has been done, so that the
+ device (peer/user) context is already set.
+ \return 0 on success (found a matching extension),
+ 1 for pickup extension or overlap dialling support (if we support it),
+ -1 on error.
+*/
+static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
+{
+ char tmp[256] = "", *uri, *a;
+ char tmpf[256] = "", *from = NULL;
+ struct sip_request *req;
+ char *colon;
+
+ req = oreq;
+ if (!req)
+ req = &p->initreq;
+
+ /* Find the request URI */
+ if (req->rlPart2)
+ ast_copy_string(tmp, req->rlPart2, sizeof(tmp));
+
+ if (pedanticsipchecking)
+ ast_uri_decode(tmp);
+
+ uri = get_in_brackets(tmp);
+
+ if (!strncasecmp(uri, "sip:", 4)) {
+ uri += 4;
+ } else if (!strncasecmp(uri, "sips:", 5)) {
+ uri += 5;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", uri);
+ return -1;
+ }
+
+ /* Now find the From: caller ID and name */
+ /* XXX Why is this done in get_destination? Isn't it already done?
+ Needs to be checked
+ */
+ ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf));
+ if (!ast_strlen_zero(tmpf)) {
+ if (pedanticsipchecking)
+ ast_uri_decode(tmpf);
+ from = get_in_brackets(tmpf);
+ }
+
+ if (!ast_strlen_zero(from)) {
+ if (!strncasecmp(from, "sip:", 4)) {
+ from += 4;
+ } else if (!strncasecmp(from, "sips:", 5)) {
+ from += 5;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", from);
+ return -1;
+ }
+ if ((a = strchr(from, '@')))
+ *a++ = '\0';
+ else
+ a = from; /* just a domain */
+ from = strsep(&from, ";"); /* Remove userinfo options */
+ a = strsep(&a, ";"); /* Remove URI options */
+ ast_string_field_set(p, fromdomain, a);
+ }
+
+ /* Skip any options and find the domain */
+
+ /* Get the target domain */
+ if ((a = strchr(uri, '@'))) {
+ *a++ = '\0';
+ } else { /* No username part */
+ a = uri;
+ uri = "s"; /* Set extension to "s" */
+ }
+ colon = strchr(a, ':'); /* Remove :port */
+ if (colon)
+ *colon = '\0';
+
+ uri = strsep(&uri, ";"); /* Remove userinfo options */
+ a = strsep(&a, ";"); /* Remove URI options */
+
+ ast_string_field_set(p, domain, a);
+
+ if (!AST_LIST_EMPTY(&domain_list)) {
+ char domain_context[AST_MAX_EXTENSION];
+
+ domain_context[0] = '\0';
+ if (!check_sip_domain(p->domain, domain_context, sizeof(domain_context))) {
+ if (!allow_external_domains && (req->method == SIP_INVITE || req->method == SIP_REFER)) {
+ ast_debug(1, "Got SIP %s to non-local domain '%s'; refusing request.\n", sip_methods[req->method].text, p->domain);
+ return -2;
+ }
+ }
+ /* If we have a context defined, overwrite the original context */
+ if (!ast_strlen_zero(domain_context))
+ ast_string_field_set(p, context, domain_context);
+ }
+
+ /* If the request coming in is a subscription and subscribecontext has been specified use it */
+ if (req->method == SIP_SUBSCRIBE && !ast_strlen_zero(p->subscribecontext))
+ ast_string_field_set(p, context, p->subscribecontext);
+
+ if (sip_debug_test_pvt(p))
+ ast_verbose("Looking for %s in %s (domain %s)\n", uri, p->context, p->domain);
+
+ /* If this is a subscription we actually just need to see if a hint exists for the extension */
+ if (req->method == SIP_SUBSCRIBE) {
+ char hint[AST_MAX_EXTENSION];
+ return (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten) ? 0 : -1);
+ } else {
+ /* Check the dialplan for the username part of the request URI,
+ the domain will be stored in the SIPDOMAIN variable
+ Since extensions.conf can have unescaped characters, try matching a decoded
+ uri in addition to the non-decoded uri
+ Return 0 if we have a matching extension */
+ char *decoded_uri = ast_strdupa(uri);
+ ast_uri_decode(decoded_uri);
+ if (ast_exists_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from)) || ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from)) ||
+ !strcmp(uri, ast_pickup_ext())) {
+ if (!oreq)
+ ast_string_field_set(p, exten, uri);
+ return 0;
+ }
+ }
+
+ /* Return 1 for pickup extension or overlap dialling support (if we support it) */
+ if((ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP) &&
+ ast_canmatch_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))) ||
+ !strncmp(uri, ast_pickup_ext(), strlen(uri))) {
+ return 1;
+ }
+
+ return -1;
+}
+
+/*! \brief Lock dialog lock and find matching pvt lock
+ - Their tag is fromtag, our tag is to-tag
+ - This means that in some transactions, totag needs to be their tag :-)
+ depending upon the direction
+ Returns a reference, remember to release it when done XXX
+*/
+static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag)
+{
+ struct sip_pvt *sip_pvt_ptr;
+
+
+ if (totag)
+ ast_debug(4, "Looking for callid %s (fromtag %s totag %s)\n", callid, fromtag ? fromtag : "<no fromtag>", totag ? totag : "<no totag>");
+
+ /* Search dialogs and find the match */
+ dialoglist_lock();
+ for (sip_pvt_ptr = dialoglist; sip_pvt_ptr; sip_pvt_ptr = sip_pvt_ptr->next) {
+ if (!strcmp(sip_pvt_ptr->callid, callid)) {
+ int match = 1;
+ char *ourtag = sip_pvt_ptr->tag;
+
+ /* Go ahead and lock it (and its owner) before returning */
+ sip_pvt_lock(sip_pvt_ptr);
+
+ /* Check if tags match. If not, this is not the call we want
+ (With a forking SIP proxy, several call legs share the
+ call id, but have different tags)
+ */
+ if (pedanticsipchecking && (strcmp(fromtag, sip_pvt_ptr->theirtag) || (!ast_strlen_zero(totag) && strcmp(totag, ourtag))))
+ match = 0;
+
+ if (!match) {
+ sip_pvt_unlock(sip_pvt_ptr);
+ continue;
+ }
+
+ if (totag)
+ ast_debug(4, "Matched %s call - their tag is %s Our tag is %s\n",
+ ast_test_flag(&sip_pvt_ptr->flags[0], SIP_OUTGOING) ? "OUTGOING": "INCOMING",
+ sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
+
+ /* deadlock avoidance... */
+ while (sip_pvt_ptr->owner && ast_channel_trylock(sip_pvt_ptr->owner)) {
+ sip_pvt_unlock(sip_pvt_ptr);
+ usleep(1);
+ sip_pvt_lock(sip_pvt_ptr);
+ }
+ break;
+ }
+ }
+ dialoglist_unlock();
+ if (!sip_pvt_ptr)
+ ast_debug(4, "Found no match for callid %s to-tag %s from-tag %s\n", callid, totag, fromtag);
+ return sip_pvt_ptr;
+}
+
+/*! \brief Call transfer support (the REFER method)
+ * Extracts Refer headers into pvt dialog structure */
+static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req)
+{
+
+ const char *p_referred_by = NULL;
+ char *h_refer_to = NULL;
+ char *h_referred_by = NULL;
+ char *refer_to;
+ const char *p_refer_to;
+ char *referred_by_uri = NULL;
+ char *ptr;
+ struct sip_request *req = NULL;
+ const char *transfer_context = NULL;
+ struct sip_refer *referdata;
+
+
+ req = outgoing_req;
+ referdata = transferer->refer;
+
+ if (!req)
+ req = &transferer->initreq;
+
+ p_refer_to = get_header(req, "Refer-To");
+ if (ast_strlen_zero(p_refer_to)) {
+ ast_log(LOG_WARNING, "Refer-To Header missing. Skipping transfer.\n");
+ return -2; /* Syntax error */
+ }
+ h_refer_to = ast_strdupa(p_refer_to);
+ refer_to = get_in_brackets(h_refer_to);
+ if (pedanticsipchecking)
+ ast_uri_decode(refer_to);
+
+ if (!strncasecmp(refer_to, "sip:", 4)) {
+ refer_to += 4; /* Skip sip: */
+ } else if (!strncasecmp(refer_to, "sips:", 5)) {
+ refer_to += 5;
+ } else {
+ ast_log(LOG_WARNING, "Can't transfer to non-sip: URI. (Refer-to: %s)?\n", refer_to);
+ return -3;
+ }
+
+ /* Get referred by header if it exists */
+ p_referred_by = get_header(req, "Referred-By");
+
+ /* Give useful transfer information to the dialplan */
+ if (transferer->owner) {
+ struct ast_channel *peer = ast_bridged_channel(transferer->owner);
+ if (peer) {
+ pbx_builtin_setvar_helper(peer, "SIPREFERRINGCONTEXT", transferer->context);
+ pbx_builtin_setvar_helper(peer, "SIPREFERREDBYHDR", p_referred_by);
+ }
+ }
+
+ if (!ast_strlen_zero(p_referred_by)) {
+ char *lessthan;
+ h_referred_by = ast_strdupa(p_referred_by);
+ if (pedanticsipchecking)
+ ast_uri_decode(h_referred_by);
+
+ /* Store referrer's caller ID name */
+ ast_copy_string(referdata->referred_by_name, h_referred_by, sizeof(referdata->referred_by_name));
+ if ((lessthan = strchr(referdata->referred_by_name, '<'))) {
+ *(lessthan - 1) = '\0'; /* Space */
+ }
+
+ referred_by_uri = get_in_brackets(h_referred_by);
+ if (!strncasecmp(referred_by_uri, "sip:", 4)) {
+ referred_by_uri += 4; /* Skip sip: */
+ } else if (!strncasecmp(referred_by_uri, "sips:", 5)) {
+ referred_by_uri += 5; /* Skip sips: */
+ } else {
+ ast_log(LOG_WARNING, "Huh? Not a sip: header (Referred-by: %s). Skipping.\n", referred_by_uri);
+ referred_by_uri = NULL;
+ }
+ }
+
+ /* Check for arguments in the refer_to header */
+ if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */
+ *ptr++ = '\0';
+ if (!strncasecmp(ptr, "REPLACES=", 9)) {
+ char *to = NULL, *from = NULL;
+
+ /* This is an attended transfer */
+ referdata->attendedtransfer = 1;
+ ast_copy_string(referdata->replaces_callid, ptr+9, sizeof(referdata->replaces_callid));
+ ast_uri_decode(referdata->replaces_callid);
+ if ((ptr = strchr(referdata->replaces_callid, ';'))) /* Find options */ {
+ *ptr++ = '\0';
+ }
+
+ if (ptr) {
+ /* Find the different tags before we destroy the string */
+ to = strcasestr(ptr, "to-tag=");
+ from = strcasestr(ptr, "from-tag=");
+ }
+
+ /* Grab the to header */
+ if (to) {
+ ptr = to + 7;
+ if ((to = strchr(ptr, '&')))
+ *to = '\0';
+ if ((to = strchr(ptr, ';')))
+ *to = '\0';
+ ast_copy_string(referdata->replaces_callid_totag, ptr, sizeof(referdata->replaces_callid_totag));
+ }
+
+ if (from) {
+ ptr = from + 9;
+ if ((to = strchr(ptr, '&')))
+ *to = '\0';
+ if ((to = strchr(ptr, ';')))
+ *to = '\0';
+ ast_copy_string(referdata->replaces_callid_fromtag, ptr, sizeof(referdata->replaces_callid_fromtag));
+ }
+
+ if (!pedanticsipchecking)
+ ast_debug(2,"Attended transfer: Will use Replace-Call-ID : %s (No check of from/to tags)\n", referdata->replaces_callid );
+ else
+ ast_debug(2,"Attended transfer: Will use Replace-Call-ID : %s F-tag: %s T-tag: %s\n", referdata->replaces_callid, referdata->replaces_callid_fromtag ? referdata->replaces_callid_fromtag : "<none>", referdata->replaces_callid_totag ? referdata->replaces_callid_totag : "<none>" );
+ }
+ }
+
+ if ((ptr = strchr(refer_to, '@'))) { /* Separate domain */
+ char *urioption = NULL, *domain;
+ *ptr++ = '\0';
+
+ if ((urioption = strchr(ptr, ';'))) /* Separate urioptions */
+ *urioption++ = '\0';
+
+ domain = ptr;
+ if ((ptr = strchr(domain, ':'))) /* Remove :port */
+ *ptr = '\0';
+
+ /* Save the domain for the dial plan */
+ ast_copy_string(referdata->refer_to_domain, domain, sizeof(referdata->refer_to_domain));
+ if (urioption)
+ ast_copy_string(referdata->refer_to_urioption, urioption, sizeof(referdata->refer_to_urioption));
+ }
+
+ if ((ptr = strchr(refer_to, ';'))) /* Remove options */
+ *ptr = '\0';
+ ast_copy_string(referdata->refer_to, refer_to, sizeof(referdata->refer_to));
+
+ if (referred_by_uri) {
+ if ((ptr = strchr(referred_by_uri, ';'))) /* Remove options */
+ *ptr = '\0';
+ ast_copy_string(referdata->referred_by, referred_by_uri, sizeof(referdata->referred_by));
+ } else {
+ referdata->referred_by[0] = '\0';
+ }
+
+ /* Determine transfer context */
+ if (transferer->owner) /* Mimic behaviour in res_features.c */
+ transfer_context = pbx_builtin_getvar_helper(transferer->owner, "TRANSFER_CONTEXT");
+
+ /* By default, use the context in the channel sending the REFER */
+ if (ast_strlen_zero(transfer_context)) {
+ transfer_context = S_OR(transferer->owner->macrocontext,
+ S_OR(transferer->context, default_context));
+ }
+
+ ast_copy_string(referdata->refer_to_context, transfer_context, sizeof(referdata->refer_to_context));
+
+ /* Either an existing extension or the parking extension */
+ if (ast_exists_extension(NULL, transfer_context, refer_to, 1, NULL) ) {
+ if (sip_debug_test_pvt(transferer)) {
+ ast_verbose("SIP transfer to extension %s@%s by %s\n", refer_to, transfer_context, referred_by_uri);
+ }
+ /* We are ready to transfer to the extension */
+ return 0;
+ }
+ if (sip_debug_test_pvt(transferer))
+ ast_verbose("Failed SIP Transfer to non-existing extension %s in context %s\n n", refer_to, transfer_context);
+
+ /* Failure, we can't find this extension */
+ return -1;
+}
+
+
+/*! \brief Call transfer support (old way, deprecated by the IETF)--*/
+static int get_also_info(struct sip_pvt *p, struct sip_request *oreq)
+{
+ char tmp[256] = "", *c, *a;
+ struct sip_request *req = oreq ? oreq : &p->initreq;
+ struct sip_refer *referdata = NULL;
+ const char *transfer_context = NULL;
+
+ if (!p->refer && !sip_refer_allocate(p))
+ return -1;
+
+ referdata = p->refer;
+
+ ast_copy_string(tmp, get_header(req, "Also"), sizeof(tmp));
+ c = get_in_brackets(tmp);
+
+ if (pedanticsipchecking)
+ ast_uri_decode(c);
+
+ if (!strncasecmp(c, "sip:", 4)) {
+ c += 4;
+ } else if (!strncasecmp(c, "sips:", 5)) {
+ c += 5;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Not a SIP header in Also: transfer (%s)?\n", c);
+ return -1;
+ }
+
+ if ((a = strchr(c, ';'))) /* Remove arguments */
+ *a = '\0';
+
+ if ((a = strchr(c, '@'))) { /* Separate Domain */
+ *a++ = '\0';
+ ast_copy_string(referdata->refer_to_domain, a, sizeof(referdata->refer_to_domain));
+ }
+
+ if (sip_debug_test_pvt(p))
+ ast_verbose("Looking for %s in %s\n", c, p->context);
+
+ if (p->owner) /* Mimic behaviour in res_features.c */
+ transfer_context = pbx_builtin_getvar_helper(p->owner, "TRANSFER_CONTEXT");
+
+ /* By default, use the context in the channel sending the REFER */
+ if (ast_strlen_zero(transfer_context)) {
+ transfer_context = S_OR(p->owner->macrocontext,
+ S_OR(p->context, default_context));
+ }
+ if (ast_exists_extension(NULL, transfer_context, c, 1, NULL)) {
+ /* This is a blind transfer */
+ ast_debug(1,"SIP Bye-also transfer to Extension %s@%s \n", c, transfer_context);
+ ast_copy_string(referdata->refer_to, c, sizeof(referdata->refer_to));
+ ast_copy_string(referdata->referred_by, "", sizeof(referdata->referred_by));
+ ast_copy_string(referdata->refer_contact, "", sizeof(referdata->refer_contact));
+ referdata->refer_call = dialog_unref(referdata->refer_call);
+ /* Set new context */
+ ast_string_field_set(p, context, transfer_context);
+ return 0;
+ } else if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
+ return 1;
+ }
+
+ return -1;
+}
+
+/*! \brief check received= and rport= in a SIP response.
+ * If we get a response with received= and/or rport= in the Via:
+ * line, use them as 'p->ourip' (see RFC 3581 for rport,
+ * and RFC 3261 for received).
+ * Using these two fields SIP can produce the correct
+ * address and port in the SIP headers without the need for STUN.
+ * The address part is also reused for the media sessions.
+ * Note that ast_sip_ouraddrfor() still rewrites p->ourip
+ * if you specify externip/seternaddr/stunaddr.
+ */
+static attribute_unused void check_via_response(struct sip_pvt *p, struct sip_request *req)
+{
+ char via[256];
+ char *cur, *opts;
+
+ ast_copy_string(via, get_header(req, "Via"), sizeof(via));
+
+ /* Work on the leftmost value of the topmost Via header */
+ opts = strchr(via, ',');
+ if (opts)
+ *opts = '\0';
+
+ /* parse all relevant options */
+ opts = strchr(via, ';');
+ if (!opts)
+ return; /* no options to parse */
+ *opts++ = '\0';
+ while ( (cur = strsep(&opts, ";")) ) {
+ if (!strncmp(cur, "rport=", 6)) {
+ int port = strtol(cur+6, NULL, 10);
+ /* XXX add error checking */
+ p->ourip.sin_port = ntohs(port);
+ } else if (!strncmp(cur, "received=", 9)) {
+ if (ast_parse_arg(cur+9, PARSE_INADDR, &p->ourip))
+ ; /* XXX add error checking */
+ }
+ }
+}
+
+/*! \brief check Via: header for hostname, port and rport request/answer */
+static void check_via(struct sip_pvt *p, struct sip_request *req)
+{
+ char via[256];
+ char *c, *pt;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+
+ ast_copy_string(via, get_header(req, "Via"), sizeof(via));
+
+ /* Work on the leftmost value of the topmost Via header */
+ c = strchr(via, ',');
+ if (c)
+ *c = '\0';
+
+ /* Check for rport */
+ c = strstr(via, ";rport");
+ if (c && (c[6] != '=')) /* rport query, not answer */
+ ast_set_flag(&p->flags[0], SIP_NAT_ROUTE);
+
+ c = strchr(via, ';');
+ if (c)
+ *c = '\0';
+
+ c = strchr(via, ' ');
+ if (c) {
+ *c = '\0';
+ c = ast_skip_blanks(c+1);
+ if (strcasecmp(via, "SIP/2.0/UDP") && strcasecmp(via, "SIP/2.0/TCP") && strcasecmp(via, "SIP/2.0/TLS")) {
+ ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via);
+ return;
+ }
+ pt = strchr(c, ':');
+ if (pt)
+ *pt++ = '\0'; /* remember port pointer */
+ hp = ast_gethostbyname(c, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "'%s' is not a valid host\n", c);
+ return;
+ }
+ memset(&p->sa, 0, sizeof(p->sa));
+ p->sa.sin_family = AF_INET;
+ memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
+ p->sa.sin_port = htons(pt ? atoi(pt) : STANDARD_SIP_PORT);
+
+ if (sip_debug_test_pvt(p)) {
+ const struct sockaddr_in *dst = sip_real_dst(p);
+ ast_verbose("Sending to %s : %d (%s)\n", ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), sip_nat_mode(p));
+ }
+ }
+}
+
+/*! \brief Get caller id name from SIP headers */
+static char *get_calleridname(const char *input, char *output, size_t outputsize)
+{
+ const char *end = strchr(input,'<'); /* first_bracket */
+ const char *tmp = strchr(input,'"'); /* first quote */
+ int bytes = 0;
+ int maxbytes = outputsize - 1;
+
+ if (!end || end == input) /* we require a part in brackets */
+ return NULL;
+
+ end--; /* move just before "<" */
+
+ if (tmp && tmp <= end) {
+ /* The quote (tmp) precedes the bracket (end+1).
+ * Find the matching quote and return the content.
+ */
+ end = strchr(tmp+1, '"');
+ if (!end)
+ return NULL;
+ bytes = (int) (end - tmp);
+ /* protect the output buffer */
+ if (bytes > maxbytes)
+ bytes = maxbytes;
+ ast_copy_string(output, tmp + 1, bytes);
+ } else {
+ /* No quoted string, or it is inside brackets. */
+ /* clear the empty characters in the begining*/
+ input = ast_skip_blanks(input);
+ /* clear the empty characters in the end */
+ while(*end && *end < 33 && end > input)
+ end--;
+ if (end >= input) {
+ bytes = (int) (end - input) + 2;
+ /* protect the output buffer */
+ if (bytes > maxbytes)
+ bytes = maxbytes;
+ ast_copy_string(output, input, bytes);
+ } else
+ return NULL;
+ }
+ return output;
+}
+
+/*! \brief Get caller id number from Remote-Party-ID header field
+ * Returns true if number should be restricted (privacy setting found)
+ * output is set to NULL if no number found
+ */
+static int get_rpid_num(const char *input, char *output, int maxlen)
+{
+ char *start;
+ char *end;
+
+ start = strchr(input,':');
+ if (!start) {
+ output[0] = '\0';
+ return 0;
+ }
+ start++;
+
+ /* we found "number" */
+ ast_copy_string(output,start,maxlen);
+ output[maxlen-1] = '\0';
+
+ end = strchr(output,'@');
+ if (end)
+ *end = '\0';
+ else
+ output[0] = '\0';
+ if (strstr(input,"privacy=full") || strstr(input,"privacy=uri"))
+ return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
+
+ return 0;
+}
+
+/*!
+ * duplicate a list of channel variables, \return the copy.
+ */
+static struct ast_variable *copy_vars(struct ast_variable *src)
+{
+ struct ast_variable *res = NULL, *tmp, *v = NULL;
+
+ for (v = src ; v ; v = v->next) {
+ if ((tmp = ast_variable_new(v->name, v->value, v->file))) {
+ tmp->next = res;
+ res = tmp;
+ }
+ }
+ return res;
+}
+
+/*! \brief helper function for check_{user|peer}_ok() */
+static void replace_cid(struct sip_pvt *p, const char *rpid_num, const char *calleridname)
+{
+ /* replace callerid if rpid found, and not restricted */
+ if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) {
+ char *tmp = ast_strdupa(rpid_num); /* XXX the copy can be done later */
+ if (!ast_strlen_zero(calleridname))
+ ast_string_field_set(p, cid_name, calleridname);
+ if (ast_is_shrinkable_phonenumber(tmp))
+ ast_shrink_phone_number(tmp);
+ ast_string_field_set(p, cid_num, tmp);
+ }
+}
+
+/*! \brief Validate user authentication */
+static enum check_auth_result check_user_ok(struct sip_pvt *p, char *of,
+ struct sip_request *req, int sipmethod, struct sockaddr_in *sin,
+ enum xmittype reliable,
+ char *rpid_num, char *calleridname, char *uri2)
+{
+ enum check_auth_result res;
+ struct sip_user *user = find_user(of, 1);
+ int debug=sip_debug_test_addr(sin);
+
+ /* Find user based on user name in the from header */
+ if (!user) {
+ if (debug)
+ ast_verbose("No user '%s' in SIP users list\n", of);
+ return AUTH_DONT_KNOW;
+ }
+ if (!ast_apply_ha(user->ha, sin)) {
+ if (debug)
+ ast_verbose("Found user '%s' for '%s', but fails host access\n",
+ user->name, of);
+ unref_user(user);
+ return AUTH_DONT_KNOW;
+ }
+ if (debug)
+ ast_verbose("Found user '%s' for '%s'\n", user->name, of);
+
+ ast_copy_flags(&p->flags[0], &user->flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&p->flags[1], &user->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+ /* copy channel vars */
+ p->chanvars = copy_vars(user->chanvars);
+ p->prefs = user->prefs;
+ /* Set Frame packetization */
+ if (p->rtp) {
+ ast_rtp_codec_setpref(p->rtp, &p->prefs);
+ p->autoframing = user->autoframing;
+ }
+
+ replace_cid(p, rpid_num, calleridname);
+ do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT_ROUTE) );
+
+ if (!(res = check_auth(p, req, user->name, user->secret, user->md5secret, sipmethod, uri2, reliable, req->ignore))) {
+ sip_cancel_destroy(p);
+ ast_copy_flags(&p->flags[0], &user->flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&p->flags[1], &user->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+ /* Copy SIP extensions profile from INVITE */
+ if (p->sipoptions)
+ user->sipoptions = p->sipoptions;
+
+ /* If we have a call limit, set flag */
+ if (user->call_limit)
+ ast_set_flag(&p->flags[0], SIP_CALL_LIMIT);
+ if (!ast_strlen_zero(user->context))
+ ast_string_field_set(p, context, user->context);
+ if (!ast_strlen_zero(user->cid_num) && !ast_strlen_zero(p->cid_num)) {
+ char *tmp = ast_strdupa(user->cid_num);
+ if (ast_is_shrinkable_phonenumber(tmp))
+ ast_shrink_phone_number(tmp);
+ ast_string_field_set(p, cid_num, tmp);
+ }
+ if (!ast_strlen_zero(user->cid_name) && !ast_strlen_zero(p->cid_num))
+ ast_string_field_set(p, cid_name, user->cid_name);
+ ast_string_field_set(p, username, user->name);
+ ast_string_field_set(p, peername, user->name);
+ ast_string_field_set(p, peersecret, user->secret);
+ ast_string_field_set(p, peermd5secret, user->md5secret);
+ ast_string_field_set(p, subscribecontext, user->subscribecontext);
+ ast_string_field_set(p, accountcode, user->accountcode);
+ ast_string_field_set(p, language, user->language);
+ ast_string_field_set(p, mohsuggest, user->mohsuggest);
+ ast_string_field_set(p, mohinterpret, user->mohinterpret);
+ p->allowtransfer = user->allowtransfer;
+ p->amaflags = user->amaflags;
+ p->callgroup = user->callgroup;
+ p->pickupgroup = user->pickupgroup;
+ if (user->callingpres) /* User callingpres setting will override RPID header */
+ p->callingpres = user->callingpres;
+
+ /* Set default codec settings for this call */
+ p->capability = user->capability; /* User codec choice */
+ p->jointcapability = user->capability; /* Our codecs */
+ if (p->peercapability) /* AND with peer's codecs */
+ p->jointcapability &= p->peercapability;
+ if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
+ (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
+ p->noncodeccapability |= AST_RTP_DTMF;
+ else
+ p->noncodeccapability &= ~AST_RTP_DTMF;
+ p->jointnoncodeccapability = p->noncodeccapability;
+ if (p->t38.peercapability)
+ p->t38.jointcapability &= p->t38.peercapability;
+ p->maxcallbitrate = user->maxcallbitrate;
+ /* If we do not support video, remove video from call structure */
+ if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(p->capability & AST_FORMAT_VIDEO_MASK)) && p->vrtp) {
+ ast_rtp_destroy(p->vrtp);
+ p->vrtp = NULL;
+ }
+ /* If we do not support text, remove text from call structure */
+ if (!ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && p->trtp) {
+ ast_rtp_destroy(p->trtp);
+ p->trtp = NULL;
+ }
+ }
+ unref_user(user);
+ return res;
+}
+
+/*! \brief Validate peer authentication */
+static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
+ struct sip_request *req, int sipmethod, struct sockaddr_in *sin,
+ struct sip_peer **authpeer,
+ enum xmittype reliable,
+ char *rpid_num, char *calleridname, char *uri2)
+{
+ enum check_auth_result res;
+ int debug=sip_debug_test_addr(sin);
+ struct sip_peer *peer;
+
+ /* For subscribes, match on peer name only; for other methods,
+ * match on IP address-port of the incoming request.
+ */
+ peer = (sipmethod == SIP_SUBSCRIBE) ? find_peer(of, NULL, 1) : find_peer(NULL, &p->recv, 1);
+
+ if (!peer) {
+ if (debug)
+ ast_verbose("No matching peer for '%s' from '%s:%d'\n",
+ of, ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
+ return AUTH_DONT_KNOW;
+ }
+
+ if (debug)
+ ast_verbose("Found peer '%s' for '%s' from %s:%d\n",
+ peer->name, of, ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
+
+ /* XXX what about p->prefs = peer->prefs; ? */
+ /* Set Frame packetization */
+ if (p->rtp) {
+ ast_rtp_codec_setpref(p->rtp, &peer->prefs);
+ p->autoframing = peer->autoframing;
+ }
+
+ /* Take the peer */
+ ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+
+ /* Copy SIP extensions profile to peer */
+ /* XXX is this correct before a successful auth ? */
+ if (p->sipoptions)
+ peer->sipoptions = p->sipoptions;
+
+ replace_cid(p, rpid_num, calleridname);
+ do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT_ROUTE));
+
+ ast_string_field_set(p, peersecret, peer->secret);
+ ast_string_field_set(p, peermd5secret, peer->md5secret);
+ ast_string_field_set(p, subscribecontext, peer->subscribecontext);
+ ast_string_field_set(p, mohinterpret, peer->mohinterpret);
+ ast_string_field_set(p, mohsuggest, peer->mohsuggest);
+ if (peer->callingpres) /* Peer calling pres setting will override RPID */
+ p->callingpres = peer->callingpres;
+ if (peer->maxms && peer->lastms)
+ p->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
+ else
+ p->timer_t1 = peer->timer_t1;
+
+ /* Set timer B to control transaction timeouts */
+ if (peer->timer_b)
+ p->timer_b = peer->timer_b;
+ else
+ p->timer_b = 64 * p->timer_t1;
+
+ if (ast_test_flag(&peer->flags[0], SIP_INSECURE_INVITE)) {
+ /* Pretend there is no required authentication */
+ ast_string_field_set(p, peersecret, NULL);
+ ast_string_field_set(p, peermd5secret, NULL);
+ }
+ if (!(res = check_auth(p, req, peer->name, p->peersecret, p->peermd5secret, sipmethod, uri2, reliable, req->ignore))) {
+ ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+ /* If we have a call limit, set flag */
+ if (peer->call_limit)
+ ast_set_flag(&p->flags[0], SIP_CALL_LIMIT);
+ ast_string_field_set(p, peername, peer->name);
+ ast_string_field_set(p, authname, peer->name);
+
+ /* copy channel vars */
+ p->chanvars = copy_vars(peer->chanvars);
+ if (authpeer) {
+ (*authpeer) = ASTOBJ_REF(peer); /* Add a ref to the object here, to keep it in memory a bit longer if it is realtime */
+ }
+
+ if (!ast_strlen_zero(peer->username)) {
+ ast_string_field_set(p, username, peer->username);
+ /* Use the default username for authentication on outbound calls */
+ /* XXX this takes the name from the caller... can we override ? */
+ ast_string_field_set(p, authname, peer->username);
+ }
+ if (!ast_strlen_zero(peer->cid_num) && !ast_strlen_zero(p->cid_num)) {
+ char *tmp = ast_strdupa(peer->cid_num);
+ if (ast_is_shrinkable_phonenumber(tmp))
+ ast_shrink_phone_number(tmp);
+ ast_string_field_set(p, cid_num, tmp);
+ }
+ if (!ast_strlen_zero(peer->cid_name) && !ast_strlen_zero(p->cid_name))
+ ast_string_field_set(p, cid_name, peer->cid_name);
+ ast_string_field_set(p, fullcontact, peer->fullcontact);
+ if (!ast_strlen_zero(peer->context))
+ ast_string_field_set(p, context, peer->context);
+ ast_string_field_set(p, peersecret, peer->secret);
+ ast_string_field_set(p, peermd5secret, peer->md5secret);
+ ast_string_field_set(p, language, peer->language);
+ ast_string_field_set(p, accountcode, peer->accountcode);
+ p->amaflags = peer->amaflags;
+ p->callgroup = peer->callgroup;
+ p->pickupgroup = peer->pickupgroup;
+ p->capability = peer->capability;
+ p->prefs = peer->prefs;
+ p->jointcapability = peer->capability;
+ if (p->peercapability)
+ p->jointcapability &= p->peercapability;
+ p->maxcallbitrate = peer->maxcallbitrate;
+ if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(p->capability & AST_FORMAT_VIDEO_MASK)) && p->vrtp) {
+ ast_rtp_destroy(p->vrtp);
+ p->vrtp = NULL;
+ }
+ if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) || !(p->capability & AST_FORMAT_TEXT_MASK)) && p->trtp) {
+ ast_rtp_destroy(p->trtp);
+ p->trtp = NULL;
+ }
+ if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
+ (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
+ p->noncodeccapability |= AST_RTP_DTMF;
+ else
+ p->noncodeccapability &= ~AST_RTP_DTMF;
+ p->jointnoncodeccapability = p->noncodeccapability;
+ if (p->t38.peercapability)
+ p->t38.jointcapability &= p->t38.peercapability;
+ }
+ unref_peer(peer);
+ return res;
+}
+
+
+/*! \brief Check if matching user or peer is defined
+ Match user on From: user name and peer on IP/port
+ This is used on first invite (not re-invites) and subscribe requests
+ \return 0 on success, non-zero on failure
+*/
+static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
+ int sipmethod, char *uri, enum xmittype reliable,
+ struct sockaddr_in *sin, struct sip_peer **authpeer)
+{
+ char from[256];
+ char *dummy; /* dummy return value for parse_uri */
+ char *domain; /* dummy return value for parse_uri */
+ char *of, *of2;
+ char rpid_num[50];
+ const char *rpid;
+ enum check_auth_result res;
+ char calleridname[50];
+ char *uri2 = ast_strdupa(uri);
+
+ terminate_uri(uri2); /* trim extra stuff */
+
+ ast_copy_string(from, get_header(req, "From"), sizeof(from));
+ if (pedanticsipchecking)
+ ast_uri_decode(from);
+ /* XXX here tries to map the username for invite things */
+ memset(calleridname, 0, sizeof(calleridname));
+ get_calleridname(from, calleridname, sizeof(calleridname));
+ if (calleridname[0])
+ ast_string_field_set(p, cid_name, calleridname);
+
+ rpid = get_header(req, "Remote-Party-ID");
+ memset(rpid_num, 0, sizeof(rpid_num));
+ if (!ast_strlen_zero(rpid))
+ p->callingpres = get_rpid_num(rpid, rpid_num, sizeof(rpid_num));
+
+ of = get_in_brackets(from);
+ if (ast_strlen_zero(p->exten)) {
+ char *t = uri2;
+ if (!strncasecmp(t, "sip:", 4))
+ t+= 4;
+ else if (!strncasecmp(t, "sips:", 5))
+ t += 5;
+ ast_string_field_set(p, exten, t);
+ t = strchr(p->exten, '@');
+ if (t)
+ *t = '\0';
+ if (ast_strlen_zero(p->our_contact))
+ build_contact(p);
+ }
+ /* save the URI part of the From header */
+ ast_string_field_set(p, from, of);
+
+ of2 = ast_strdupa(of);
+
+ /* ignore all fields but name */
+ if (p->socket.type == SIP_TRANSPORT_TLS) {
+ if (parse_uri(of, "sips:", &of, &dummy, &domain, &dummy, &dummy)) {
+ if (parse_uri(of2, "sip:", &of, &dummy, &domain, &dummy, &dummy))
+ ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
+ }
+ } else {
+ if (parse_uri(of, "sip:", &of, &dummy, &domain, &dummy, &dummy))
+ ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
+ }
+
+ if (ast_strlen_zero(of)) {
+ /* XXX note: the original code considered a missing @host
+ * as a username-only URI. The SIP RFC (19.1.1) says that
+ * this is wrong, and it should be considered as a domain-only URI.
+ * For backward compatibility, we keep this block, but it is
+ * really a mistake and should go away.
+ */
+ of = domain;
+ }
+ {
+ char *tmp = ast_strdupa(of);
+ /* We need to be able to handle auth-headers looking like
+ <sip:8164444422;phone-context=+1@1.2.3.4:5060;user=phone;tag=SDadkoa01-gK0c3bdb43>
+ */
+ tmp = strsep(&tmp, ";");
+ if (ast_is_shrinkable_phonenumber(tmp))
+ ast_shrink_phone_number(tmp);
+ ast_string_field_set(p, cid_num, tmp);
+ }
+ if (ast_strlen_zero(of))
+ return AUTH_SUCCESSFUL;
+
+ if (global_match_auth_username) {
+ /*
+ * XXX This is experimental code to grab the search key from the
+ * Auth header's username instead of the 'From' name, if available.
+ * Do not enable this block unless you understand the side effects (if any!)
+ * Note, the search for "username" should be done in a more robust way.
+ * Note2, at the moment we check both fields, though maybe we should
+ * pick one or another depending on the request ? XXX
+ */
+ const char *hdr = get_header(req, "Authorization");
+ if (ast_strlen_zero(hdr))
+ hdr = get_header(req, "Proxy-Authorization");
+
+ if ( !ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\"")) ) {
+ ast_copy_string(from, hdr + strlen("username=\""), sizeof(from));
+ of = from;
+ of = strsep(&of, "\"");
+ }
+ }
+
+ if (!authpeer) {
+ /* If we are looking for a peer, don't check the
+ user objects (or realtime) */
+ res = check_user_ok(p, of, req, sipmethod, sin,
+ reliable, rpid_num, calleridname, uri2);
+ if (res != AUTH_DONT_KNOW)
+ return res;
+ }
+
+ res = check_peer_ok(p, of, req, sipmethod, sin,
+ authpeer, reliable, rpid_num, calleridname, uri2);
+ if (res != AUTH_DONT_KNOW)
+ return res;
+
+ /* Finally, apply the guest policy */
+ if (global_allowguest) {
+ replace_cid(p, rpid_num, calleridname);
+ res = AUTH_SUCCESSFUL;
+ } else if (global_alwaysauthreject)
+ res = AUTH_FAKE_AUTH; /* reject with fake authorization request */
+ else
+ res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */
+
+ return res;
+}
+
+/*! \brief Find user
+ If we get a match, this will add a reference pointer to the user object in ASTOBJ, that needs to be unreferenced
+*/
+static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin)
+{
+ return check_user_full(p, req, sipmethod, uri, reliable, sin, NULL);
+}
+
+/*! \brief Get text out of a SIP MESSAGE packet */
+static int get_msg_text(char *buf, int len, struct sip_request *req)
+{
+ int x;
+ int y;
+
+ buf[0] = '\0';
+ y = len - strlen(buf) - 5;
+ if (y < 0)
+ y = 0;
+ for (x=0;x<req->lines;x++) {
+ strncat(buf, req->line[x], y); /* safe */
+ y -= strlen(req->line[x]) + 1;
+ if (y < 0)
+ y = 0;
+ if (y != 0)
+ strcat(buf, "\n"); /* safe */
+ }
+ return 0;
+}
+
+
+/*! \brief Receive SIP MESSAGE method messages
+\note We only handle messages within current calls currently
+ Reference: RFC 3428 */
+static void receive_message(struct sip_pvt *p, struct sip_request *req)
+{
+ char buf[1024];
+ struct ast_frame f;
+ const char *content_type = get_header(req, "Content-Type");
+
+ if (strcmp(content_type, "text/plain")) { /* No text/plain attachment */
+ transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */
+ if (!p->owner)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return;
+ }
+
+ if (get_msg_text(buf, sizeof(buf), req)) {
+ ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
+ transmit_response(p, "202 Accepted", req);
+ if (!p->owner)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return;
+ }
+
+ if (p->owner) {
+ if (sip_debug_test_pvt(p))
+ ast_verbose("Message received: '%s'\n", buf);
+ memset(&f, 0, sizeof(f));
+ f.frametype = AST_FRAME_TEXT;
+ f.subclass = 0;
+ f.offset = 0;
+ f.data = buf;
+ f.datalen = strlen(buf);
+ ast_queue_frame(p->owner, &f);
+ transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */
+ } else { /* Message outside of a call, we do not support that */
+ ast_log(LOG_WARNING,"Received message to %s from %s, dropped it...\n Content-Type:%s\n Message: %s\n", get_header(req,"To"), get_header(req,"From"), content_type, buf);
+ transmit_response(p, "405 Method Not Allowed", req); /* Good enough, or? */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ return;
+}
+
+/*! \brief CLI Command to show calls within limits set by call_limit */
+static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-25.25s %-15.15s %-15.15s \n"
+#define FORMAT2 "%-25.25s %-15.15s %-15.15s \n"
+ char ilimits[40];
+ char iused[40];
+ int showall = FALSE;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show inuse";
+ e->usage =
+ "Usage: sip show inuse [all]\n"
+ " List all SIP users and peers usage counters and limits.\n"
+ " Add option \"all\" to show all devices, not only those with a limit.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 3)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 4 && !strcmp(a->argv[3],"all"))
+ showall = TRUE;
+
+ ast_cli(a->fd, FORMAT, "* User name", "In use", "Limit");
+ ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->call_limit)
+ snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit);
+ else
+ ast_copy_string(ilimits, "N/A", sizeof(ilimits));
+ snprintf(iused, sizeof(iused), "%d", iterator->inUse);
+ if (showall || iterator->call_limit)
+ ast_cli(a->fd, FORMAT2, iterator->name, iused, ilimits);
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+
+ ast_cli(a->fd, FORMAT, "* Peer name", "In use", "Limit");
+
+ ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->call_limit)
+ snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit);
+ else
+ ast_copy_string(ilimits, "N/A", sizeof(ilimits));
+ snprintf(iused, sizeof(iused), "%d/%d/%d", iterator->inUse, iterator->inRinging, iterator->onHold);
+ if (showall || iterator->call_limit)
+ ast_cli(a->fd, FORMAT2, iterator->name, iused, ilimits);
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+
+/*! \brief Convert transfer mode to text string */
+static char *transfermode2str(enum transfermodes mode)
+{
+ if (mode == TRANSFER_OPENFORALL)
+ return "open";
+ else if (mode == TRANSFER_CLOSED)
+ return "closed";
+ return "strict";
+}
+
+static struct _map_x_s natmodes[] = {
+ { SIP_NAT_NEVER, "No"},
+ { SIP_NAT_ROUTE, "Route"},
+ { SIP_NAT_ALWAYS, "Always"},
+ { SIP_NAT_RFC3581, "RFC3581"},
+ { -1, NULL}, /* terminator */
+};
+
+/*! \brief Convert NAT setting to text string */
+static const char *nat2str(int nat)
+{
+ return map_x_s(natmodes, nat, "Unknown");
+}
+
+/*! \brief Report Peer status in character string
+ * \return 0 if peer is unreachable, 1 if peer is online, -1 if unmonitored
+ */
+
+
+/* Session-Timer Modes */
+static struct _map_x_s stmodes[] = {
+ { SESSION_TIMER_MODE_ACCEPT, "Accept"},
+ { SESSION_TIMER_MODE_ORIGINATE, "Originate"},
+ { SESSION_TIMER_MODE_REFUSE, "Refuse"},
+ { -1, NULL},
+};
+
+static const char *stmode2str(enum st_mode m)
+{
+ return map_x_s(stmodes, m, "Unknown");
+}
+
+static enum st_mode str2stmode(const char *s)
+{
+ return map_s_x(stmodes, s, -1);
+}
+
+/* Session-Timer Refreshers */
+static struct _map_x_s strefreshers[] = {
+ { SESSION_TIMER_REFRESHER_AUTO, "auto"},
+ { SESSION_TIMER_REFRESHER_UAC, "uac"},
+ { SESSION_TIMER_REFRESHER_UAS, "uas"},
+ { -1, NULL},
+};
+
+static const char *strefresher2str(enum st_refresher r)
+{
+ return map_x_s(strefreshers, r, "Unknown");
+}
+
+static enum st_refresher str2strefresher(const char *s)
+{
+ return map_s_x(strefreshers, s, -1);
+}
+
+
+static int peer_status(struct sip_peer *peer, char *status, int statuslen)
+{
+ int res = 0;
+ if (peer->maxms) {
+ if (peer->lastms < 0) {
+ ast_copy_string(status, "UNREACHABLE", statuslen);
+ } else if (peer->lastms > peer->maxms) {
+ snprintf(status, statuslen, "LAGGED (%d ms)", peer->lastms);
+ res = 1;
+ } else if (peer->lastms) {
+ snprintf(status, statuslen, "OK (%d ms)", peer->lastms);
+ res = 1;
+ } else {
+ ast_copy_string(status, "UNKNOWN", statuslen);
+ }
+ } else {
+ ast_copy_string(status, "Unmonitored", statuslen);
+ /* Checking if port is 0 */
+ res = -1;
+ }
+ return res;
+}
+
+/*! \brief return Yes or No depending on the argument.
+ * This is used in many places in CLI command, having a function to generate
+ * this helps maintaining a consistent output (and possibly emitting the
+ * output in other languages, at some point).
+ */
+static const char *cli_yesno(int x)
+{
+ return x ? "Yes" : "No";
+}
+
+/*! \brief Show active TCP connections */
+static char *sip_show_tcp(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sip_threadinfo *th;
+
+#define FORMAT2 "%-30.30s %3.6s %9.9s %6.6s\n"
+#define FORMAT "%-30.30s %-6d %-9.9s %-6.6s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show tcp";
+ e->usage =
+ "Usage: sip show tcp\n"
+ " Lists all active TCP/TLS sessions.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT2, "Host", "Port", "Transport", "Type");
+ AST_LIST_LOCK(&threadl);
+ AST_LIST_TRAVERSE(&threadl, th, list) {
+ ast_cli(a->fd, FORMAT, ast_inet_ntoa(th->ser->requestor.sin_addr),
+ ntohs(th->ser->requestor.sin_port),
+ get_transport(th->type),
+ (th->ser->client ? "Client" : "Server"));
+
+ }
+ AST_LIST_UNLOCK(&threadl);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+/*! \brief CLI Command 'SIP Show Users' */
+static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ regex_t regexbuf;
+ int havepattern = FALSE;
+
+#define FORMAT "%-25.25s %-15.15s %-15.15s %-15.15s %-5.5s%-10.10s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show users";
+ e->usage =
+ "Usage: sip show users [like <pattern>]\n"
+ " Lists all known SIP users.\n"
+ " Optional regular expression pattern is used to filter the user list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ switch (a->argc) {
+ case 5:
+ if (!strcasecmp(a->argv[3], "like")) {
+ if (regcomp(&regexbuf, a->argv[4], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = TRUE;
+ } else
+ return CLI_SHOWUSAGE;
+ case 3:
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "NAT");
+ ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+
+ if (havepattern && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
+ ASTOBJ_UNLOCK(iterator);
+ continue;
+ }
+
+ ast_cli(a->fd, FORMAT, iterator->name,
+ iterator->secret,
+ iterator->accountcode,
+ iterator->context,
+ cli_yesno(iterator->ha != NULL),
+ nat2str(ast_test_flag(&iterator->flags[0], SIP_NAT)));
+ ASTOBJ_UNLOCK(iterator);
+ } while (0)
+ );
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+}
+
+/*! \brief Manager Action SIPShowRegistry description */
+static char mandescr_show_registry[] =
+"Description: Lists all registration requests and status\n"
+"Registrations will follow as separate events. followed by a final event called\n"
+"RegistrationsComplete.\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this transaction. Will be returned.\n";
+
+/*! \brief Show SIP registrations in the manager API */
+static int manager_show_registry(struct mansession *s, const struct message *m)
+{
+ const char *id = astman_get_header(m, "ActionID");
+ char idtext[256] = "";
+ char tmpdat[256] = "";
+ int total = 0;
+ struct ast_tm tm;
+
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+ astman_send_listack(s, m, "Registrations will follow", "start");
+
+ ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->regtime.tv_sec) {
+ ast_localtime(&iterator->regtime, &tm, NULL);
+ ast_strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T", &tm);
+ } else
+ tmpdat[0] = '\0';
+ astman_append(s,
+ "Event: RegistryEntry\r\n"
+ "Host: %s\r\n"
+ "Port: %d\r\n"
+ "Username: %s\r\n"
+ "Refresh: %d\r\n"
+ "State: %s\r\n"
+ "Reg.Time: %s\r\n"
+ "\r\n", iterator->hostname, iterator->portno ? iterator->portno : STANDARD_SIP_PORT,
+ iterator->username, iterator->refresh, regstate2str(iterator->regstate), tmpdat);
+ ASTOBJ_UNLOCK(iterator);
+ total++;
+ } while(0));
+
+ astman_append(s,
+ "Event: RegistrationsComplete\r\n"
+ "EventList: Complete\r\n"
+ "ListItems: %d\r\n"
+ "%s"
+ "\r\n", total, idtext);
+
+ return 0;
+}
+
+static char mandescr_show_peers[] =
+"Description: Lists SIP peers in text format with details on current status.\n"
+"Peerlist will follow as separate events, followed by a final event called\n"
+"PeerlistComplete.\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this transaction. Will be returned.\n";
+
+/*! \brief Show SIP peers in the manager API */
+/* Inspired from chan_iax2 */
+static int manager_sip_show_peers(struct mansession *s, const struct message *m)
+{
+ const char *id = astman_get_header(m,"ActionID");
+ const char *a[] = {"sip", "show", "peers"};
+ char idtext[256] = "";
+ int total = 0;
+
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+ astman_send_listack(s, m, "Peer status list will follow", "start");
+ /* List the peers in separate manager events */
+ _sip_show_peers(-1, &total, s, m, 3, a);
+ /* Send final confirmation */
+ astman_append(s,
+ "Event: PeerlistComplete\r\n"
+ "EventList: Complete\r\n"
+ "ListItems: %d\r\n"
+ "%s"
+ "\r\n", total, idtext);
+ return 0;
+}
+
+/*! \brief CLI Show Peers command */
+static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show peers";
+ e->usage =
+ "Usage: sip show peers [like <pattern>]\n"
+ " Lists all known SIP peers.\n"
+ " Optional regular expression pattern is used to filter the peer list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ return _sip_show_peers(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+/*! \brief _sip_show_peers: Execute sip show peers command */
+static char *_sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
+{
+ regex_t regexbuf;
+ int havepattern = FALSE;
+
+/* the last argument is left-aligned, so we don't need a size anyways */
+#define FORMAT2 "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-8s %-10s %s\n"
+#define FORMAT "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-8d %-10s %s\n"
+
+ char name[256];
+ int total_peers = 0;
+ int peers_mon_online = 0;
+ int peers_mon_offline = 0;
+ int peers_unmon_offline = 0;
+ int peers_unmon_online = 0;
+ const char *id;
+ char idtext[256] = "";
+ int realtimepeers;
+
+ realtimepeers = ast_check_realtime("sippeers");
+
+ if (s) { /* Manager - get ActionID */
+ id = astman_get_header(m,"ActionID");
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+ }
+
+ switch (argc) {
+ case 5:
+ if (!strcasecmp(argv[3], "like")) {
+ if (regcomp(&regexbuf, argv[4], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = TRUE;
+ } else
+ return CLI_SHOWUSAGE;
+ case 3:
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ if (!s) /* Normal list */
+ ast_cli(fd, FORMAT2, "Name/username", "Host", "Dyn", "Nat", "ACL", "Port", "Status", (realtimepeers ? "Realtime" : ""));
+
+ ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
+ char status[20] = "";
+ char srch[2000];
+ char pstatus;
+
+ ASTOBJ_RDLOCK(iterator);
+
+ if (havepattern && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
+ ASTOBJ_UNLOCK(iterator);
+ continue;
+ }
+
+ if (!ast_strlen_zero(iterator->username) && !s)
+ snprintf(name, sizeof(name), "%s/%s", iterator->name, iterator->username);
+ else
+ ast_copy_string(name, iterator->name, sizeof(name));
+
+ pstatus = peer_status(iterator, status, sizeof(status));
+ if (pstatus == 1)
+ peers_mon_online++;
+ else if (pstatus == 0)
+ peers_mon_offline++;
+ else {
+ if (iterator->addr.sin_port == 0)
+ peers_unmon_offline++;
+ else
+ peers_unmon_online++;
+ }
+
+ snprintf(srch, sizeof(srch), FORMAT, name,
+ iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "(Unspecified)",
+ iterator->host_dynamic ? " D " : " ", /* Dynamic or not? */
+ ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */
+ iterator->ha ? " A " : " ", /* permit/deny */
+ ntohs(iterator->addr.sin_port), status,
+ realtimepeers ? (iterator->is_realtime ? "Cached RT":"") : "");
+
+ if (!s) {/* Normal CLI list */
+ ast_cli(fd, FORMAT, name,
+ iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "(Unspecified)",
+ iterator->host_dynamic ? " D " : " ", /* Dynamic or not? */
+ ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */
+ iterator->ha ? " A " : " ", /* permit/deny */
+
+ ntohs(iterator->addr.sin_port), status,
+ realtimepeers ? (iterator->is_realtime ? "Cached RT":"") : "");
+ } else { /* Manager format */
+ /* The names here need to be the same as other channels */
+ astman_append(s,
+ "Event: PeerEntry\r\n%s"
+ "Channeltype: SIP\r\n"
+ "ObjectName: %s\r\n"
+ "ChanObjectType: peer\r\n" /* "peer" or "user" */
+ "IPaddress: %s\r\n"
+ "IPport: %d\r\n"
+ "Dynamic: %s\r\n"
+ "Natsupport: %s\r\n"
+ "VideoSupport: %s\r\n"
+ "TextSupport: %s\r\n"
+ "ACL: %s\r\n"
+ "Status: %s\r\n"
+ "RealtimeDevice: %s\r\n\r\n",
+ idtext,
+ iterator->name,
+ iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "-none-",
+ ntohs(iterator->addr.sin_port),
+ iterator->host_dynamic ? "yes" : "no", /* Dynamic or not? */
+ ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? "yes" : "no", /* NAT=yes? */
+ ast_test_flag(&iterator->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */
+ ast_test_flag(&iterator->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */
+ iterator->ha ? "yes" : "no", /* permit/deny */
+ status,
+ realtimepeers ? (iterator->is_realtime ? "yes":"no") : "no");
+ }
+
+ ASTOBJ_UNLOCK(iterator);
+
+ total_peers++;
+ } while(0) );
+
+ if (!s)
+ ast_cli(fd, "%d sip peers [Monitored: %d online, %d offline Unmonitored: %d online, %d offline]\n",
+ total_peers, peers_mon_online, peers_mon_offline, peers_unmon_online, peers_unmon_offline);
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ if (total)
+ *total = total_peers;
+
+
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+/*! \brief List all allocated SIP Objects (realtime or static) */
+static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char tmp[256];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show objects";
+ e->usage =
+ "Usage: sip show objects\n"
+ " Lists status of known SIP objects\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "-= User objects: %d static, %d realtime =-\n\n", suserobjs, ruserobjs);
+ ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), &userl);
+ ast_cli(a->fd, "-= Peer objects: %d static, %d realtime, %d autocreate =-\n\n", speerobjs, rpeerobjs, apeerobjs);
+ ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), &peerl);
+ ast_cli(a->fd, "-= Registry objects: %d =-\n\n", regobjs);
+ ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), &regl);
+ return CLI_SUCCESS;
+}
+/*! \brief Print call group and pickup group */
+static void print_group(int fd, ast_group_t group, int crlf)
+{
+ char buf[256];
+ ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_group(buf, sizeof(buf), group) );
+}
+
+/*! \brief mapping between dtmf flags and strings */
+static struct _map_x_s dtmfstr[] = {
+ { SIP_DTMF_RFC2833, "rfc2833" },
+ { SIP_DTMF_INFO, "info" },
+ { SIP_DTMF_SHORTINFO, "shortinfo" },
+ { SIP_DTMF_INBAND, "inband" },
+ { SIP_DTMF_AUTO, "auto" },
+ { -1, NULL }, /* terminator */
+};
+
+/*! \brief Convert DTMF mode to printable string */
+static const char *dtmfmode2str(int mode)
+{
+ return map_x_s(dtmfstr, mode, "<error>");
+}
+
+/*! \brief maps a string to dtmfmode, returns -1 on error */
+static int str2dtmfmode(const char *str)
+{
+ return map_s_x(dtmfstr, str, -1);
+}
+
+static struct _map_x_s insecurestr[] = {
+ { SIP_INSECURE_PORT, "port" },
+ { SIP_INSECURE_INVITE, "invite" },
+ { SIP_INSECURE_PORT | SIP_INSECURE_INVITE, "port,invite" },
+ { 0, "no" },
+ { -1, NULL }, /* terminator */
+};
+
+/*! \brief Convert Insecure setting to printable string */
+static const char *insecure2str(int mode)
+{
+ return map_x_s(insecurestr, mode, "<error>");
+}
+
+/*! \brief Destroy disused contexts between reloads
+ Only used in reload_config so the code for regcontext doesn't get ugly
+*/
+static void cleanup_stale_contexts(char *new, char *old)
+{
+ char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
+
+ while ((oldcontext = strsep(&old, "&"))) {
+ stalecontext = '\0';
+ ast_copy_string(newlist, new, sizeof(newlist));
+ stringp = newlist;
+ while ((newcontext = strsep(&stringp, "&"))) {
+ if (strcmp(newcontext, oldcontext) == 0) {
+ /* This is not the context you're looking for */
+ stalecontext = '\0';
+ break;
+ } else if (strcmp(newcontext, oldcontext)) {
+ stalecontext = oldcontext;
+ }
+
+ }
+ if (stalecontext)
+ ast_context_destroy(ast_context_find(stalecontext), "SIP");
+ }
+}
+
+/*! \brief Remove temporary realtime objects from memory (CLI) */
+static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sip_peer *peer;
+ struct sip_user *user;
+ int pruneuser = FALSE;
+ int prunepeer = FALSE;
+ int multi = FALSE;
+ char *name = NULL;
+ regex_t regexbuf;
+
+ if (cmd == CLI_INIT) {
+ e->command = "sip prune realtime [peer|user|all] [all|like]";
+ e->usage =
+ "Usage: sip prune realtime [peer|user] [<name>|all|like <pattern>]\n"
+ " Prunes object(s) from the cache.\n"
+ " Optional regular expression pattern is used to filter the objects.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE) {
+ if (a->pos == 4) {
+ if (strcasestr(a->line, "realtime peer"))
+ return complete_sip_peer(a->word, a->n, SIP_PAGE2_RTCACHEFRIENDS);
+ else if (strcasestr(a->line, "realtime user"))
+ return complete_sip_user(a->word, a->n, SIP_PAGE2_RTCACHEFRIENDS);
+ }
+ return NULL;
+ }
+ switch (a->argc) {
+ case 4:
+ name = a->argv[3];
+ /* we accept a name in position 3, but keywords are not good. */
+ if (!strcasecmp(name, "user") || !strcasecmp(name, "peer") ||
+ !strcasecmp(name, "like"))
+ return CLI_SHOWUSAGE;
+ pruneuser = prunepeer = TRUE;
+ if (!strcasecmp(name, "all")) {
+ multi = TRUE;
+ name = NULL;
+ }
+ /* else a single name, already set */
+ break;
+ case 5:
+ /* sip prune realtime {user|peer|like} name */
+ name = a->argv[4];
+ if (!strcasecmp(a->argv[3], "user"))
+ pruneuser = TRUE;
+ else if (!strcasecmp(a->argv[3], "peer"))
+ prunepeer = TRUE;
+ else if (!strcasecmp(a->argv[3], "like")) {
+ pruneuser = prunepeer = TRUE;
+ multi = TRUE;
+ } else
+ return CLI_SHOWUSAGE;
+ if (!strcasecmp(a->argv[4], "like"))
+ return CLI_SHOWUSAGE;
+ if (!multi && !strcasecmp(a->argv[4], "all")) {
+ multi = TRUE;
+ name = NULL;
+ }
+ break;
+ case 6:
+ name = a->argv[5];
+ multi = TRUE;
+ /* sip prune realtime {user|peer} like name */
+ if (strcasecmp(a->argv[4], "like"))
+ return CLI_SHOWUSAGE;
+ if (!strcasecmp(a->argv[3], "user")) {
+ pruneuser = TRUE;
+ } else if (!strcasecmp(a->argv[3], "peer")) {
+ prunepeer = TRUE;
+ } else
+ return CLI_SHOWUSAGE;
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ if (multi && name) {
+ if (regcomp(&regexbuf, name, REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ }
+
+ if (multi) {
+ if (prunepeer) {
+ int pruned = 0;
+
+ ASTOBJ_CONTAINER_WRLOCK(&peerl);
+ ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ if (name && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
+ ASTOBJ_UNLOCK(iterator);
+ continue;
+ };
+ if (ast_test_flag(&iterator->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ ASTOBJ_MARK(iterator);
+ pruned++;
+ }
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+ if (pruned) {
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer);
+ ast_cli(a->fd, "%d peers pruned.\n", pruned);
+ } else
+ ast_cli(a->fd, "No peers found to prune.\n");
+ ASTOBJ_CONTAINER_UNLOCK(&peerl);
+ }
+ if (pruneuser) {
+ int pruned = 0;
+
+ ASTOBJ_CONTAINER_WRLOCK(&userl);
+ ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ if (name && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
+ ASTOBJ_UNLOCK(iterator);
+ continue;
+ };
+ if (ast_test_flag(&iterator->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ ASTOBJ_MARK(iterator);
+ pruned++;
+ }
+ ASTOBJ_UNLOCK(iterator);
+ } while (0) );
+ if (pruned) {
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&userl, sip_destroy_user);
+ ast_cli(a->fd, "%d users pruned.\n", pruned);
+ } else
+ ast_cli(a->fd, "No users found to prune.\n");
+ ASTOBJ_CONTAINER_UNLOCK(&userl);
+ }
+ } else {
+ if (prunepeer) {
+ if ((peer = ASTOBJ_CONTAINER_FIND_UNLINK(&peerl, name))) {
+ if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ ast_cli(a->fd, "Peer '%s' is not a Realtime peer, cannot be pruned.\n", name);
+ ASTOBJ_CONTAINER_LINK(&peerl, peer);
+ } else
+ ast_cli(a->fd, "Peer '%s' pruned.\n", name);
+ unref_peer(peer);
+ } else
+ ast_cli(a->fd, "Peer '%s' not found.\n", name);
+ }
+ if (pruneuser) {
+ if ((user = ASTOBJ_CONTAINER_FIND_UNLINK(&userl, name))) {
+ if (!ast_test_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ ast_cli(a->fd, "User '%s' is not a Realtime user, cannot be pruned.\n", name);
+ ASTOBJ_CONTAINER_LINK(&userl, user);
+ } else
+ ast_cli(a->fd, "User '%s' pruned.\n", name);
+ unref_user(user);
+ } else
+ ast_cli(a->fd, "User '%s' not found.\n", name);
+ }
+ }
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Print codec list from preference to CLI/manager */
+static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
+{
+ int x, codec;
+
+ for(x = 0; x < 32 ; x++) {
+ codec = ast_codec_pref_index(pref, x);
+ if (!codec)
+ break;
+ ast_cli(fd, "%s", ast_getformatname(codec));
+ ast_cli(fd, ":%d", pref->framing[x]);
+ if (x < 31 && ast_codec_pref_index(pref, x + 1))
+ ast_cli(fd, ",");
+ }
+ if (!x)
+ ast_cli(fd, "none");
+}
+
+/*! \brief Print domain mode to cli */
+static const char *domain_mode_to_text(const enum domain_mode mode)
+{
+ switch (mode) {
+ case SIP_DOMAIN_AUTO:
+ return "[Automatic]";
+ case SIP_DOMAIN_CONFIG:
+ return "[Configured]";
+ }
+
+ return "";
+}
+
+/*! \brief CLI command to list local domains */
+static char *sip_show_domains(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct domain *d;
+#define FORMAT "%-40.40s %-20.20s %-16.16s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show domains";
+ e->usage =
+ "Usage: sip show domains\n"
+ " Lists all configured SIP local domains.\n"
+ " Asterisk only responds to SIP messages to local domains.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (AST_LIST_EMPTY(&domain_list)) {
+ ast_cli(a->fd, "SIP Domain support not enabled.\n\n");
+ return CLI_SUCCESS;
+ } else {
+ ast_cli(a->fd, FORMAT, "Our local SIP domains:", "Context", "Set by");
+ AST_LIST_LOCK(&domain_list);
+ AST_LIST_TRAVERSE(&domain_list, d, list)
+ ast_cli(a->fd, FORMAT, d->domain, S_OR(d->context, "(default)"),
+ domain_mode_to_text(d->mode));
+ AST_LIST_UNLOCK(&domain_list);
+ ast_cli(a->fd, "\n");
+ return CLI_SUCCESS;
+ }
+}
+#undef FORMAT
+
+static char mandescr_show_peer[] =
+"Description: Show one SIP peer with details on current status.\n"
+"Variables: \n"
+" Peer: <name> The peer name you want to check.\n"
+" ActionID: <id> Optional action ID for this AMI transaction.\n";
+
+/*! \brief Show SIP peers in the manager API */
+static int manager_sip_show_peer(struct mansession *s, const struct message *m)
+{
+ const char *a[4];
+ const char *peer;
+
+ peer = astman_get_header(m,"Peer");
+ if (ast_strlen_zero(peer)) {
+ astman_send_error(s, m, "Peer: <name> missing.\n");
+ return 0;
+ }
+ a[0] = "sip";
+ a[1] = "show";
+ a[2] = "peer";
+ a[3] = peer;
+
+ _sip_show_peer(1, -1, s, m, 4, a);
+ astman_append(s, "\r\n\r\n" );
+ return 0;
+}
+
+/*! \brief Show one peer in detail */
+static char *sip_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show peer";
+ e->usage =
+ "Usage: sip show peer <name> [load]\n"
+ " Shows all details on one SIP peer and the current status.\n"
+ " Option \"load\" forces lookup of peer in realtime storage.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_sip_show_peer(a->line, a->word, a->pos, a->n);
+ }
+ return _sip_show_peer(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer)
+{
+ struct sip_mailbox *mailbox;
+
+ AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
+ ast_str_append(mailbox_str, 0, "%s%s%s%s",
+ mailbox->mailbox,
+ ast_strlen_zero(mailbox->context) ? "" : "@",
+ S_OR(mailbox->context, ""),
+ AST_LIST_NEXT(mailbox, entry) ? "," : "");
+ }
+}
+
+/*! \brief Show one peer in detail (main function) */
+static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
+{
+ char status[30] = "";
+ char cbuf[256];
+ struct sip_peer *peer;
+ char codec_buf[512];
+ struct ast_codec_pref *pref;
+ struct ast_variable *v;
+ struct sip_auth *auth;
+ int x = 0, codec = 0, load_realtime;
+ int realtimepeers;
+
+ realtimepeers = ast_check_realtime("sippeers");
+
+ if (argc < 4)
+ return CLI_SHOWUSAGE;
+
+ load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? TRUE : FALSE;
+ peer = find_peer(argv[3], NULL, load_realtime);
+ if (s) { /* Manager */
+ if (peer) {
+ const char *id = astman_get_header(m,"ActionID");
+
+ astman_append(s, "Response: Success\r\n");
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n",id);
+ } else {
+ snprintf (cbuf, sizeof(cbuf), "Peer %s not found.\n", argv[3]);
+ astman_send_error(s, m, cbuf);
+ return CLI_SUCCESS;
+ }
+ }
+ if (peer && type==0 ) { /* Normal listing */
+ struct ast_str *mailbox_str = ast_str_alloca(512);
+ ast_cli(fd,"\n\n");
+ ast_cli(fd, " * Name : %s\n", peer->name);
+ if (realtimepeers) { /* Realtime is enabled */
+ ast_cli(fd, " Realtime peer: %s\n", peer->is_realtime ? "Yes, cached" : "No");
+ }
+ ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"<Not set>":"<Set>");
+ ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(peer->md5secret)?"<Not set>":"<Set>");
+ for (auth = peer->auth; auth; auth = auth->next) {
+ ast_cli(fd, " Realm-auth : Realm %-15.15s User %-10.20s ", auth->realm, auth->username);
+ ast_cli(fd, "%s\n", !ast_strlen_zero(auth->secret)?"<Secret set>":(!ast_strlen_zero(auth->md5secret)?"<MD5secret set>" : "<Not set>"));
+ }
+ ast_cli(fd, " Context : %s\n", peer->context);
+ ast_cli(fd, " Subscr.Cont. : %s\n", S_OR(peer->subscribecontext, "<Not set>") );
+ ast_cli(fd, " Language : %s\n", peer->language);
+ if (!ast_strlen_zero(peer->accountcode))
+ ast_cli(fd, " Accountcode : %s\n", peer->accountcode);
+ ast_cli(fd, " AMA flags : %s\n", ast_cdr_flags2str(peer->amaflags));
+ ast_cli(fd, " Transfer mode: %s\n", transfermode2str(peer->allowtransfer));
+ ast_cli(fd, " CallingPres : %s\n", ast_describe_caller_presentation(peer->callingpres));
+ if (!ast_strlen_zero(peer->fromuser))
+ ast_cli(fd, " FromUser : %s\n", peer->fromuser);
+ if (!ast_strlen_zero(peer->fromdomain))
+ ast_cli(fd, " FromDomain : %s\n", peer->fromdomain);
+ ast_cli(fd, " Callgroup : ");
+ print_group(fd, peer->callgroup, 0);
+ ast_cli(fd, " Pickupgroup : ");
+ print_group(fd, peer->pickupgroup, 0);
+ peer_mailboxes_to_str(&mailbox_str, peer);
+ ast_cli(fd, " Mailbox : %s\n", mailbox_str->str);
+ ast_cli(fd, " VM Extension : %s\n", peer->vmexten);
+ ast_cli(fd, " LastMsgsSent : %d/%d\n", (peer->lastmsgssent & 0x7fff0000) >> 16, peer->lastmsgssent & 0xffff);
+ ast_cli(fd, " Call limit : %d\n", peer->call_limit);
+ if (peer->busy_level)
+ ast_cli(fd, " Busy level : %d\n", peer->busy_level);
+ ast_cli(fd, " Dynamic : %s\n", cli_yesno(peer->host_dynamic));
+ ast_cli(fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
+ ast_cli(fd, " MaxCallBR : %d kbps\n", peer->maxcallbitrate);
+ ast_cli(fd, " Expire : %ld\n", ast_sched_when(sched, peer->expire));
+ ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
+ ast_cli(fd, " Nat : %s\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT)));
+ ast_cli(fd, " ACL : %s\n", cli_yesno(peer->ha != NULL));
+ ast_cli(fd, " T38 pt UDPTL : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL)));
+#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
+ ast_cli(fd, " T38 pt RTP : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_RTP)));
+ ast_cli(fd, " T38 pt TCP : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_TCP)));
+#endif
+ ast_cli(fd, " CanReinvite : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE)));
+ ast_cli(fd, " PromiscRedir : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)));
+ ast_cli(fd, " User=Phone : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)));
+ ast_cli(fd, " Video Support: %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)));
+ ast_cli(fd, " Text Support : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)));
+ ast_cli(fd, " Trust RPID : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_TRUSTRPID)));
+ ast_cli(fd, " Send RPID : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_SENDRPID)));
+ ast_cli(fd, " Subscriptions: %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
+ ast_cli(fd, " Overlap dial : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP)));
+ if (peer->outboundproxy)
+ ast_cli(fd, " Outb. proxy : %s %s\n", ast_strlen_zero(peer->outboundproxy->name) ? "<not set>" : peer->outboundproxy->name,
+ peer->outboundproxy->force ? "(forced)" : "");
+
+ /* - is enumerated */
+ ast_cli(fd, " DTMFmode : %s\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF)));
+ ast_cli(fd, " Timer T1 : %d\n", peer->timer_t1);
+ ast_cli(fd, " Timer B : %d\n", peer->timer_b);
+ ast_cli(fd, " ToHost : %s\n", peer->tohost);
+ ast_cli(fd, " Addr->IP : %s Port %d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port));
+ ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port));
+ ast_cli(fd, " Transport : %s\n", get_transport(peer->socket.type));
+ if (!ast_strlen_zero(global_regcontext))
+ ast_cli(fd, " Reg. exten : %s\n", peer->regexten);
+ ast_cli(fd, " Def. Username: %s\n", peer->username);
+ ast_cli(fd, " SIP Options : ");
+ if (peer->sipoptions) {
+ int lastoption = -1;
+ for (x=0 ; (x < (sizeof(sip_options) / sizeof(sip_options[0]))); x++) {
+ if (sip_options[x].id != lastoption) {
+ if (peer->sipoptions & sip_options[x].id)
+ ast_cli(fd, "%s ", sip_options[x].text);
+ lastoption = x;
+ }
+ }
+ } else
+ ast_cli(fd, "(none)");
+
+ ast_cli(fd, "\n");
+ ast_cli(fd, " Codecs : ");
+ ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability);
+ ast_cli(fd, "%s\n", codec_buf);
+ ast_cli(fd, " Codec Order : (");
+ print_codec_to_cli(fd, &peer->prefs);
+ ast_cli(fd, ")\n");
+
+ ast_cli(fd, " Auto-Framing : %s \n", cli_yesno(peer->autoframing));
+ ast_cli(fd, " 100 on REG : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_REGISTERTRYING) ? "Yes" : "No");
+ ast_cli(fd, " Status : ");
+ peer_status(peer, status, sizeof(status));
+ ast_cli(fd, "%s\n",status);
+ ast_cli(fd, " Useragent : %s\n", peer->useragent);
+ ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact);
+ ast_cli(fd, " Qualify Freq : %d ms\n", peer->qualifyfreq);
+ if (peer->chanvars) {
+ ast_cli(fd, " Variables :\n");
+ for (v = peer->chanvars ; v ; v = v->next)
+ ast_cli(fd, " %s = %s\n", v->name, v->value);
+ }
+
+ ast_cli(fd, " Sess-Timers : %s\n", stmode2str(peer->stimer.st_mode_oper));
+ ast_cli(fd, " Sess-Refresh : %s\n", strefresher2str(peer->stimer.st_ref));
+ ast_cli(fd, " Sess-Expires : %d secs\n", peer->stimer.st_max_se);
+ ast_cli(fd, " Min-Sess : %d secs\n", peer->stimer.st_min_se);
+ ast_cli(fd,"\n");
+ unref_peer(peer);
+ } else if (peer && type == 1) { /* manager listing */
+ char buf[256];
+ struct ast_str *mailbox_str = ast_str_alloca(512);
+ astman_append(s, "Channeltype: SIP\r\n");
+ astman_append(s, "ObjectName: %s\r\n", peer->name);
+ astman_append(s, "ChanObjectType: peer\r\n");
+ astman_append(s, "SecretExist: %s\r\n", ast_strlen_zero(peer->secret)?"N":"Y");
+ astman_append(s, "MD5SecretExist: %s\r\n", ast_strlen_zero(peer->md5secret)?"N":"Y");
+ astman_append(s, "Context: %s\r\n", peer->context);
+ astman_append(s, "Language: %s\r\n", peer->language);
+ if (!ast_strlen_zero(peer->accountcode))
+ astman_append(s, "Accountcode: %s\r\n", peer->accountcode);
+ astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(peer->amaflags));
+ astman_append(s, "CID-CallingPres: %s\r\n", ast_describe_caller_presentation(peer->callingpres));
+ if (!ast_strlen_zero(peer->fromuser))
+ astman_append(s, "SIP-FromUser: %s\r\n", peer->fromuser);
+ if (!ast_strlen_zero(peer->fromdomain))
+ astman_append(s, "SIP-FromDomain: %s\r\n", peer->fromdomain);
+ astman_append(s, "Callgroup: ");
+ astman_append(s, "%s\r\n", ast_print_group(buf, sizeof(buf), peer->callgroup));
+ astman_append(s, "Pickupgroup: ");
+ astman_append(s, "%s\r\n", ast_print_group(buf, sizeof(buf), peer->pickupgroup));
+ peer_mailboxes_to_str(&mailbox_str, peer);
+ astman_append(s, "VoiceMailbox: %s\r\n", mailbox_str->str);
+ astman_append(s, "TransferMode: %s\r\n", transfermode2str(peer->allowtransfer));
+ astman_append(s, "LastMsgsSent: %d\r\n", peer->lastmsgssent);
+ astman_append(s, "Call-limit: %d\r\n", peer->call_limit);
+ astman_append(s, "Busy-level: %d\r\n", peer->busy_level);
+ astman_append(s, "MaxCallBR: %d kbps\r\n", peer->maxcallbitrate);
+ astman_append(s, "Dynamic: %s\r\n", peer->host_dynamic?"Y":"N");
+ astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, ""));
+ astman_append(s, "RegExpire: %ld seconds\r\n", ast_sched_when(sched,peer->expire));
+ astman_append(s, "SIP-AuthInsecure: %s\r\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
+ astman_append(s, "SIP-NatSupport: %s\r\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT)));
+ astman_append(s, "ACL: %s\r\n", (peer->ha?"Y":"N"));
+ astman_append(s, "SIP-CanReinvite: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE)?"Y":"N"));
+ astman_append(s, "SIP-PromiscRedir: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Y":"N"));
+ astman_append(s, "SIP-UserPhone: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Y":"N"));
+ astman_append(s, "SIP-VideoSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Y":"N"));
+ astman_append(s, "SIP-TextSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)?"Y":"N"));
+ astman_append(s, "SIP-Sess-Timers: %s\r\n", stmode2str(peer->stimer.st_mode_oper));
+ astman_append(s, "SIP-Sess-Refresh: %s\r\n", strefresher2str(peer->stimer.st_ref));
+ astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se);
+ astman_append(s, "SIP-Sess-Min: %d\r\n", peer->stimer.st_min_se);
+
+ /* - is enumerated */
+ astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF)));
+ astman_append(s, "ToHost: %s\r\n", peer->tohost);
+ astman_append(s, "Address-IP: %s\r\nAddress-Port: %d\r\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "", ntohs(peer->addr.sin_port));
+ astman_append(s, "Default-addr-IP: %s\r\nDefault-addr-port: %d\r\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port));
+ astman_append(s, "Default-Username: %s\r\n", peer->username);
+ if (!ast_strlen_zero(global_regcontext))
+ astman_append(s, "RegExtension: %s\r\n", peer->regexten);
+ astman_append(s, "Codecs: ");
+ ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability);
+ astman_append(s, "%s\r\n", codec_buf);
+ astman_append(s, "CodecOrder: ");
+ pref = &peer->prefs;
+ for(x = 0; x < 32 ; x++) {
+ codec = ast_codec_pref_index(pref,x);
+ if (!codec)
+ break;
+ astman_append(s, "%s", ast_getformatname(codec));
+ if (x < 31 && ast_codec_pref_index(pref,x+1))
+ astman_append(s, ",");
+ }
+
+ astman_append(s, "\r\n");
+ astman_append(s, "Status: ");
+ peer_status(peer, status, sizeof(status));
+ astman_append(s, "%s\r\n", status);
+ astman_append(s, "SIP-Useragent: %s\r\n", peer->useragent);
+ astman_append(s, "Reg-Contact : %s\r\n", peer->fullcontact);
+ astman_append(s, "Qualify Freq : %d ms\n", peer->qualifyfreq);
+ if (peer->chanvars) {
+ for (v = peer->chanvars ; v ; v = v->next) {
+ astman_append(s, "ChanVariable:\n");
+ astman_append(s, " %s,%s\r\n", v->name, v->value);
+ }
+ }
+
+ unref_peer(peer);
+
+ } else {
+ ast_cli(fd,"Peer %s not found.\n", argv[3]);
+ ast_cli(fd,"\n");
+ }
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show one user in detail */
+static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char cbuf[256];
+ struct sip_user *user;
+ struct ast_variable *v;
+ int load_realtime;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show user";
+ e->usage =
+ "Usage: sip show user <name> [load]\n"
+ " Shows all details on one SIP user and the current status.\n"
+ " Option \"load\" forces lookup of peer in realtime storage.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_sip_show_user(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ /* Load from realtime storage? */
+ load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? TRUE : FALSE;
+
+ user = find_user(a->argv[3], load_realtime);
+ if (user) {
+ ast_cli(a->fd,"\n\n");
+ ast_cli(a->fd, " * Name : %s\n", user->name);
+ ast_cli(a->fd, " Secret : %s\n", ast_strlen_zero(user->secret)?"<Not set>":"<Set>");
+ ast_cli(a->fd, " MD5Secret : %s\n", ast_strlen_zero(user->md5secret)?"<Not set>":"<Set>");
+ ast_cli(a->fd, " Context : %s\n", user->context);
+ ast_cli(a->fd, " Language : %s\n", user->language);
+ if (!ast_strlen_zero(user->accountcode))
+ ast_cli(a->fd, " Accountcode : %s\n", user->accountcode);
+ ast_cli(a->fd, " AMA flags : %s\n", ast_cdr_flags2str(user->amaflags));
+ ast_cli(a->fd, " Transfer mode: %s\n", transfermode2str(user->allowtransfer));
+ ast_cli(a->fd, " MaxCallBR : %d kbps\n", user->maxcallbitrate);
+ ast_cli(a->fd, " CallingPres : %s\n", ast_describe_caller_presentation(user->callingpres));
+ ast_cli(a->fd, " Call limit : %d\n", user->call_limit);
+ ast_cli(a->fd, " Callgroup : ");
+ print_group(a->fd, user->callgroup, 0);
+ ast_cli(a->fd, " Pickupgroup : ");
+ print_group(a->fd, user->pickupgroup, 0);
+ ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
+ ast_cli(a->fd, " ACL : %s\n", cli_yesno(user->ha != NULL));
+ ast_cli(a->fd, " Sess-Timers : %s\n", stmode2str(user->stimer.st_mode_oper));
+ ast_cli(a->fd, " Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref));
+ ast_cli(a->fd, " Sess-Expires : %d secs\n", user->stimer.st_max_se);
+ ast_cli(a->fd, " Sess-Min-SE : %d secs\n", user->stimer.st_min_se);
+
+ ast_cli(a->fd, " Codec Order : (");
+ print_codec_to_cli(a->fd, &user->prefs);
+ ast_cli(a->fd, ")\n");
+
+ ast_cli(a->fd, " Auto-Framing: %s \n", cli_yesno(user->autoframing));
+ if (user->chanvars) {
+ ast_cli(a->fd, " Variables :\n");
+ for (v = user->chanvars ; v ; v = v->next)
+ ast_cli(a->fd, " %s = %s\n", v->name, v->value);
+ }
+
+ ast_cli(a->fd,"\n");
+
+ unref_user(user);
+ } else {
+ ast_cli(a->fd,"User %s not found.\n", a->argv[3]);
+ ast_cli(a->fd,"\n");
+ }
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show SIP Registry (registrations with other SIP proxies */
+static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-30.30s %-12.12s %8.8s %-20.20s %-25.25s\n"
+#define FORMAT "%-30.30s %-12.12s %8d %-20.20s %-25.25s\n"
+ char host[80];
+ char tmpdat[256];
+ struct ast_tm tm;
+ int counter = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show registry";
+ e->usage =
+ "Usage: sip show registry\n"
+ " Lists all registration requests and status.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, FORMAT2, "Host", "Username", "Refresh", "State", "Reg.Time");
+ ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ snprintf(host, sizeof(host), "%s:%d", iterator->hostname, iterator->portno ? iterator->portno : STANDARD_SIP_PORT);
+ if (iterator->regtime.tv_sec) {
+ ast_localtime(&iterator->regtime, &tm, NULL);
+ ast_strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T", &tm);
+ } else
+ tmpdat[0] = '\0';
+ ast_cli(a->fd, FORMAT, host, iterator->username, iterator->refresh, regstate2str(iterator->regstate), tmpdat);
+ ASTOBJ_UNLOCK(iterator);
+ counter++;
+ } while(0));
+ ast_cli(a->fd, "%d SIP registrations.\n", counter);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+/*! \brief Unregister (force expiration) a SIP peer in the registry via CLI
+ \note This function does not tell the SIP device what's going on,
+ so use it with great care.
+*/
+static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sip_peer *peer;
+ int load_realtime = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip unregister";
+ e->usage =
+ "Usage: sip unregister <peer>\n"
+ " Unregister (force expiration) a SIP peer from the registry\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_sip_unregister(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ if ((peer = find_peer(a->argv[2], NULL, load_realtime))) {
+ if (peer->expire > 0) {
+ expire_register(peer);
+ ast_cli(a->fd, "Unregistered peer \'%s\'\n\n", a->argv[2]);
+ } else {
+ ast_cli(a->fd, "Peer %s not registered\n", a->argv[2]);
+ }
+ } else {
+ ast_cli(a->fd, "Peer unknown: \'%s\'. Not unregistered.\n", a->argv[2]);
+ }
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief List global settings for the SIP channel */
+static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int realtimepeers;
+ int realtimeusers;
+ int realtimeregs;
+ char codec_buf[BUFSIZ];
+ const char *msg; /* temporary msg pointer */
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show settings";
+ e->usage =
+ "Usage: sip show settings\n"
+ " Provides detailed list of the configuration of the SIP channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+
+ realtimepeers = ast_check_realtime("sippeers");
+ realtimeusers = ast_check_realtime("sipusers");
+ realtimeregs = ast_check_realtime("sipregs");
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "\n\nGlobal Settings:\n");
+ ast_cli(a->fd, "----------------\n");
+ ast_cli(a->fd, " SIP Port: %d\n", ntohs(bindaddr.sin_port));
+ ast_cli(a->fd, " Bindaddress: %s\n", ast_inet_ntoa(bindaddr.sin_addr));
+ ast_cli(a->fd, " Videosupport: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT)));
+ ast_cli(a->fd, " Textsupport: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT)));
+ ast_cli(a->fd, " AutoCreatePeer: %s\n", cli_yesno(autocreatepeer));
+ ast_cli(a->fd, " MatchAuthUsername: %s\n", cli_yesno(global_match_auth_username));
+ ast_cli(a->fd, " Allow unknown access: %s\n", cli_yesno(global_allowguest));
+ ast_cli(a->fd, " Allow subscriptions: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
+ ast_cli(a->fd, " Enable call counters: %s\n", cli_yesno(global_callcounter));
+ ast_cli(a->fd, " Allow overlap dialing: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)));
+ ast_cli(a->fd, " Promsic. redir: %s\n", cli_yesno(ast_test_flag(&global_flags[0], SIP_PROMISCREDIR)));
+ ast_cli(a->fd, " SIP domain support: %s\n", cli_yesno(!AST_LIST_EMPTY(&domain_list)));
+ ast_cli(a->fd, " Call to non-local dom.: %s\n", cli_yesno(allow_external_domains));
+ ast_cli(a->fd, " URI user is phone no: %s\n", cli_yesno(ast_test_flag(&global_flags[0], SIP_USEREQPHONE)));
+ ast_cli(a->fd, " Our auth realm %s\n", global_realm);
+ ast_cli(a->fd, " Realm. auth: %s\n", cli_yesno(authl != NULL));
+ ast_cli(a->fd, " Always auth rejects: %s\n", cli_yesno(global_alwaysauthreject));
+ ast_cli(a->fd, " Call limit peers only: %s\n", cli_yesno(global_limitonpeers));
+ ast_cli(a->fd, " Direct RTP setup: %s\n", cli_yesno(global_directrtpsetup));
+ ast_cli(a->fd, " User Agent: %s\n", global_useragent);
+ ast_cli(a->fd, " SDP Session Name: %s\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
+ ast_cli(a->fd, " SDP Owner Name: %s\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner);
+ ast_cli(a->fd, " Reg. context: %s\n", S_OR(global_regcontext, "(not set)"));
+ ast_cli(a->fd, " Regexten on Qualify: %s\n", cli_yesno(global_regextenonqualify));
+ ast_cli(a->fd, " Caller ID: %s\n", default_callerid);
+ ast_cli(a->fd, " From: Domain: %s\n", default_fromdomain);
+ ast_cli(a->fd, " Record SIP history: %s\n", recordhistory ? "On" : "Off");
+ ast_cli(a->fd, " Call Events: %s\n", global_callevents ? "On" : "Off");
+ ast_cli(a->fd, " IP ToS SIP: %s\n", ast_tos2str(global_tos_sip));
+ ast_cli(a->fd, " IP ToS RTP audio: %s\n", ast_tos2str(global_tos_audio));
+ ast_cli(a->fd, " IP ToS RTP video: %s\n", ast_tos2str(global_tos_video));
+ ast_cli(a->fd, " IP ToS RTP text: %s\n", ast_tos2str(global_tos_text));
+ ast_cli(a->fd, " 802.1p CoS SIP: %d\n", global_cos_sip);
+ ast_cli(a->fd, " 802.1p CoS RTP audio: %d\n", global_cos_audio);
+ ast_cli(a->fd, " 802.1p CoS RTP video: %d\n", global_cos_video);
+ ast_cli(a->fd, " 802.1p CoS RTP text: %d\n", global_cos_text);
+
+ ast_cli(a->fd, " T38 fax pt UDPTL: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_UDPTL)));
+#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
+ ast_cli(a->fd, " T38 fax pt RTP: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_RTP)));
+ ast_cli(a->fd, " T38 fax pt TCP: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_TCP)));
+#endif
+ ast_cli(a->fd, " RFC2833 Compensation: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_RFC2833_COMPENSATE)));
+ ast_cli(a->fd, " Jitterbuffer enabled: %s\n", cli_yesno(ast_test_flag(&global_jbconf, AST_JB_ENABLED)));
+ ast_cli(a->fd, " Jitterbuffer forced: %s\n", cli_yesno(ast_test_flag(&global_jbconf, AST_JB_FORCED)));
+ ast_cli(a->fd, " Jitterbuffer max size: %ld\n", global_jbconf.max_size);
+ ast_cli(a->fd, " Jitterbuffer resync: %ld\n", global_jbconf.resync_threshold);
+ ast_cli(a->fd, " Jitterbuffer impl: %s\n", global_jbconf.impl);
+ ast_cli(a->fd, " Jitterbuffer log: %s\n", cli_yesno(ast_test_flag(&global_jbconf, AST_JB_LOG)));
+ if (!realtimepeers && !realtimeusers && !realtimeregs)
+ ast_cli(a->fd, " SIP realtime: Disabled\n" );
+ else
+ ast_cli(a->fd, " SIP realtime: Enabled\n" );
+ ast_cli(a->fd, " Qualify Freq : %d ms\n", global_qualifyfreq);
+
+ ast_cli(a->fd, "\nNetwork Settings:\n");
+ ast_cli(a->fd, "---------------------------\n");
+ /* determine if/how SIP address can be remapped */
+ if (localaddr == NULL)
+ msg = "Disabled, no localnet list";
+ else if (externip.sin_addr.s_addr == 0)
+ msg = "Disabled, externip is 0.0.0.0";
+ else if (stunaddr.sin_addr.s_addr != 0)
+ msg = "Enabled using STUN";
+ else if (!ast_strlen_zero(externhost))
+ msg = "Enabled using externhost";
+ else
+ msg = "Enabled using externip";
+ ast_cli(a->fd, " SIP address remapping: %s\n", msg);
+ ast_cli(a->fd, " Externhost: %s\n", S_OR(externhost, "<none>"));
+ ast_cli(a->fd, " Externip: %s:%d\n", ast_inet_ntoa(externip.sin_addr), ntohs(externip.sin_port));
+ ast_cli(a->fd, " Externrefresh: %d\n", externrefresh);
+ ast_cli(a->fd, " Internal IP: %s:%d\n", ast_inet_ntoa(internip.sin_addr), ntohs(internip.sin_port));
+ {
+ struct ast_ha *d;
+ const char *prefix = "Localnet:";
+ char buf[INET_ADDRSTRLEN]; /* need to print two addresses */
+
+ for (d = localaddr; d ; prefix = "", d = d->next) {
+ ast_cli(a->fd, " %-24s%s/%s\n",
+ prefix, ast_inet_ntoa(d->netaddr),
+ inet_ntop(AF_INET, &d->netmask, buf, sizeof(buf)) );
+ }
+ }
+ ast_cli(a->fd, " STUN server: %s:%d\n", ast_inet_ntoa(stunaddr.sin_addr), ntohs(stunaddr.sin_port));
+
+ ast_cli(a->fd, "\nGlobal Signalling Settings:\n");
+ ast_cli(a->fd, "---------------------------\n");
+ ast_cli(a->fd, " Codecs: ");
+ ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, global_capability);
+ ast_cli(a->fd, "%s\n", codec_buf);
+ ast_cli(a->fd, " Codec Order: ");
+ print_codec_to_cli(a->fd, &default_prefs);
+ ast_cli(a->fd, "\n");
+ ast_cli(a->fd, " Relax DTMF: %s\n", cli_yesno(global_relaxdtmf));
+ ast_cli(a->fd, " Compact SIP headers: %s\n", cli_yesno(compactheaders));
+ ast_cli(a->fd, " RTP Keepalive: %d %s\n", global_rtpkeepalive, global_rtpkeepalive ? "" : "(Disabled)" );
+ ast_cli(a->fd, " RTP Timeout: %d %s\n", global_rtptimeout, global_rtptimeout ? "" : "(Disabled)" );
+ ast_cli(a->fd, " RTP Hold Timeout: %d %s\n", global_rtpholdtimeout, global_rtpholdtimeout ? "" : "(Disabled)");
+ ast_cli(a->fd, " MWI NOTIFY mime type: %s\n", default_notifymime);
+ ast_cli(a->fd, " DNS SRV lookup: %s\n", cli_yesno(global_srvlookup));
+ ast_cli(a->fd, " Pedantic SIP support: %s\n", cli_yesno(pedanticsipchecking));
+ ast_cli(a->fd, " Reg. min duration %d secs\n", min_expiry);
+ ast_cli(a->fd, " Reg. max duration: %d secs\n", max_expiry);
+ ast_cli(a->fd, " Reg. default duration: %d secs\n", default_expiry);
+ ast_cli(a->fd, " Outbound reg. timeout: %d secs\n", global_reg_timeout);
+ ast_cli(a->fd, " Outbound reg. attempts: %d\n", global_regattempts_max);
+ ast_cli(a->fd, " Notify ringing state: %s\n", cli_yesno(global_notifyringing));
+ ast_cli(a->fd, " Notify hold state: %s\n", cli_yesno(global_notifyhold));
+ ast_cli(a->fd, " SIP Transfer mode: %s\n", transfermode2str(global_allowtransfer));
+ ast_cli(a->fd, " Max Call Bitrate: %d kbps\n", default_maxcallbitrate);
+ ast_cli(a->fd, " Auto-Framing: %s\n", cli_yesno(global_autoframing));
+ ast_cli(a->fd, " Outb. proxy: %s %s\n", ast_strlen_zero(global_outboundproxy.name) ? "<not set>" : global_outboundproxy.name,
+ global_outboundproxy.force ? "(forced)" : "");
+ ast_cli(a->fd, " Session Timers: %s\n", stmode2str(global_st_mode));
+ ast_cli(a->fd, " Session Refresher: %s\n", strefresher2str (global_st_refresher));
+ ast_cli(a->fd, " Session Expires: %d secs\n", global_max_se);
+ ast_cli(a->fd, " Session Min-SE: %d secs\n", global_min_se);
+ ast_cli(a->fd, " Timer T1: %d\n", global_t1);
+ ast_cli(a->fd, " Timer T1 minimum: %d\n", global_t1min);
+ ast_cli(a->fd, " Timer B: %d\n", global_timer_b);
+
+ ast_cli(a->fd, "\nDefault Settings:\n");
+ ast_cli(a->fd, "-----------------\n");
+ ast_cli(a->fd, " Context: %s\n", default_context);
+ ast_cli(a->fd, " Nat: %s\n", nat2str(ast_test_flag(&global_flags[0], SIP_NAT)));
+ ast_cli(a->fd, " DTMF: %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF)));
+ ast_cli(a->fd, " Qualify: %d\n", default_qualify);
+ ast_cli(a->fd, " Use ClientCode: %s\n", cli_yesno(ast_test_flag(&global_flags[0], SIP_USECLIENTCODE)));
+ ast_cli(a->fd, " Progress inband: %s\n", (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER) ? "Never" : (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NO) ? "No" : "Yes" );
+ ast_cli(a->fd, " Language: %s\n", default_language);
+ ast_cli(a->fd, " MOH Interpret: %s\n", default_mohinterpret);
+ ast_cli(a->fd, " MOH Suggest: %s\n", default_mohsuggest);
+ ast_cli(a->fd, " Voice Mail Extension: %s\n", default_vmexten);
+
+
+ if (realtimepeers || realtimeusers || realtimeregs) {
+ ast_cli(a->fd, "\nRealtime SIP Settings:\n");
+ ast_cli(a->fd, "----------------------\n");
+ ast_cli(a->fd, " Realtime Peers: %s\n", cli_yesno(realtimepeers));
+ ast_cli(a->fd, " Realtime Users: %s\n", cli_yesno(realtimeusers));
+ ast_cli(a->fd, " Realtime Regs: %s\n", cli_yesno(realtimeregs));
+ ast_cli(a->fd, " Cache Friends: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)));
+ ast_cli(a->fd, " Update: %s\n", cli_yesno(sip_cfg.peer_rtupdate));
+ ast_cli(a->fd, " Ignore Reg. Expire: %s\n", cli_yesno(sip_cfg.ignore_regexpire));
+ ast_cli(a->fd, " Save sys. name: %s\n", cli_yesno(sip_cfg.rtsave_sysname));
+ ast_cli(a->fd, " Auto Clear: %d\n", global_rtautoclear);
+ }
+ ast_cli(a->fd, "\n----\n");
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show subscription type in string format */
+static const char *subscription_type2str(enum subscriptiontype subtype)
+{
+ int i;
+
+ for (i = 1; (i < (sizeof(subscription_types) / sizeof(subscription_types[0]))); i++) {
+ if (subscription_types[i].type == subtype) {
+ return subscription_types[i].text;
+ }
+ }
+ return subscription_types[0].text;
+}
+
+/*! \brief Find subscription type in array */
+static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype)
+{
+ int i;
+
+ for (i = 1; (i < (sizeof(subscription_types) / sizeof(subscription_types[0]))); i++) {
+ if (subscription_types[i].type == subtype) {
+ return &subscription_types[i];
+ }
+ }
+ return &subscription_types[0];
+}
+
+/*
+ * We try to structure all functions that loop on data structures as
+ * a handler for individual entries, and a mainloop that iterates
+ * on the main data structure. This way, moving the code to containers
+ * that support iteration through callbacks will be a lot easier.
+ */
+
+/*! \brief argument for the 'show channels|subscriptions' callback. */
+struct __show_chan_arg {
+ int fd;
+ int subscriptions;
+ int numchans; /* return value */
+};
+
+#define FORMAT3 "%-15.15s %-10.10s %-15.15s %-15.15s %-13.13s %-15.15s %-10.10s\n"
+#define FORMAT2 "%-15.15s %-10.10s %-15.15s %-15.15s %-7.7s %-15.15s\n"
+#define FORMAT "%-15.15s %-10.10s %-15.15s %-15.15s %-3.3s %-3.3s %-15.15s %-10.10s\n"
+
+/*! \brief callback for show channel|subscription */
+static int show_channels_cb(void *__cur, void *__arg, int flags)
+{
+ struct sip_pvt *cur = __cur;
+ struct __show_chan_arg *arg = __arg;
+ const struct sockaddr_in *dst = sip_real_dst(cur);
+
+ /* XXX indentation preserved to reduce diff. Will be fixed later */
+ if (cur->subscribed == NONE && !arg->subscriptions) {
+ /* set if SIP transfer in progress */
+ const char *referstatus = cur->refer ? referstatus2str(cur->refer->status) : "";
+ char formatbuf[BUFSIZ/2];
+
+ ast_cli(arg->fd, FORMAT, ast_inet_ntoa(dst->sin_addr),
+ S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
+ cur->callid,
+ ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->owner ? cur->owner->nativeformats : 0),
+ cli_yesno(ast_test_flag(&cur->flags[1], SIP_PAGE2_CALL_ONHOLD)),
+ cur->needdestroy ? "(d)" : "",
+ cur->lastmsg ,
+ referstatus
+ );
+ arg->numchans++;
+ }
+ if (cur->subscribed != NONE && arg->subscriptions) {
+ struct ast_str *mailbox_str = ast_str_alloca(512);
+ if (cur->subscribed == MWI_NOTIFICATION && cur->relatedpeer)
+ peer_mailboxes_to_str(&mailbox_str, cur->relatedpeer);
+ ast_cli(arg->fd, FORMAT3, ast_inet_ntoa(dst->sin_addr),
+ S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
+ cur->callid,
+ /* the 'complete' exten/context is hidden in the refer_to field for subscriptions */
+ cur->subscribed == MWI_NOTIFICATION ? "--" : cur->subscribeuri,
+ cur->subscribed == MWI_NOTIFICATION ? "<none>" : ast_extension_state2str(cur->laststate),
+ subscription_type2str(cur->subscribed),
+ cur->subscribed == MWI_NOTIFICATION ? S_OR(mailbox_str->str, "<none>") : "<none>"
+);
+ arg->numchans++;
+ }
+
+ return 0; /* don't care, we scan all channels */
+}
+
+/*! \brief CLI for show channels or subscriptions.
+ * This is a new-style CLI handler so a single function contains
+ * the prototype for the function, the 'generator' to produce multiple
+ * entries in case it is required, and the actual handler for the command.
+ */
+static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sip_pvt *cur;
+ struct __show_chan_arg arg = { .fd = a->fd, .numchans = 0 };
+
+ if (cmd == CLI_INIT) {
+ e->command = "sip show {channels|subscriptions}";
+ e->usage =
+ "Usage: sip show channels\n"
+ " Lists all currently active SIP calls (dialogs).\n"
+ "Usage: sip show subscriptions\n"
+ " Lists active SIP subscriptions.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE)
+ return NULL;
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+ arg.subscriptions = !strcasecmp(a->argv[e->args - 1], "subscriptions");
+ if (!arg.subscriptions)
+ ast_cli(arg.fd, FORMAT2, "Peer", "User/ANR", "Call ID", "Format", "Hold", "Last Message");
+ else
+ ast_cli(arg.fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state", "Type", "Mailbox");
+
+ /* iterate on the container and invoke the callback on each item */
+ dialoglist_lock();
+ for (cur = dialoglist; cur; cur = cur->next) {
+ show_channels_cb(cur, &arg, 0);
+ }
+ dialoglist_unlock();
+
+ /* print summary information */
+ ast_cli(arg.fd, "%d active SIP %s%s\n", arg.numchans,
+ (arg.subscriptions ? "subscription" : "dialog"),
+ ESS(arg.numchans)); /* ESS(n) returns an "s" if n>1 */
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+#undef FORMAT3
+}
+
+/*! \brief Support routine for 'sip show channel' and 'sip show history' CLI
+ * This is in charge of generating all strings that match a prefix in the
+ * given position. As many functions of this kind, each invokation has
+ * O(state) time complexity so be careful in using it.
+ */
+static char *complete_sipch(const char *line, const char *word, int pos, int state)
+{
+ int which=0;
+ struct sip_pvt *cur;
+ char *c = NULL;
+ int wordlen = strlen(word);
+
+ dialoglist_lock();
+ for (cur = dialoglist; cur; cur = cur->next) {
+ if (!strncasecmp(word, cur->callid, wordlen) && ++which > state) {
+ c = ast_strdup(cur->callid);
+ break;
+ }
+ }
+ dialoglist_unlock();
+ return c;
+}
+
+/*! \brief Do completion on peer name */
+static char *complete_sip_peer(const char *word, int state, int flags2)
+{
+ char *result = NULL;
+ int wordlen = strlen(word);
+ int which = 0;
+
+ ASTOBJ_CONTAINER_TRAVERSE(&peerl, !result, do {
+ /* locking of the object is not required because only the name and flags are being compared */
+ if (!strncasecmp(word, iterator->name, wordlen) &&
+ (!flags2 || ast_test_flag(&iterator->flags[1], flags2)) &&
+ ++which > state)
+ result = ast_strdup(iterator->name);
+ } while(0) );
+ return result;
+}
+
+/*! \brief Do completion on registered peer name */
+static char *complete_sip_registered_peer(const char *word, int state, int flags2)
+{
+ char *result = NULL;
+ int wordlen = strlen(word);
+ int which = 0;
+
+ ASTOBJ_CONTAINER_TRAVERSE(&peerl, !result, do {
+ ASTOBJ_WRLOCK(iterator);
+ if (!strncasecmp(word, iterator->name, wordlen) &&
+ (!flags2 || ast_test_flag(&iterator->flags[1], flags2)) &&
+ ++which > state && iterator->expire > 0)
+ result = ast_strdup(iterator->name);
+ ASTOBJ_UNLOCK(iterator);
+ } while(0) );
+ return result;
+}
+
+/*! \brief Support routine for 'sip show history' CLI */
+static char *complete_sip_show_history(const char *line, const char *word, int pos, int state)
+{
+ if (pos == 3)
+ return complete_sipch(line, word, pos, state);
+
+ return NULL;
+}
+
+/*! \brief Support routine for 'sip show peer' CLI */
+static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state)
+{
+ if (pos == 3)
+ return complete_sip_peer(word, state, 0);
+
+ return NULL;
+}
+
+/*! \brief Support routine for 'sip unregister' CLI */
+static char *complete_sip_unregister(const char *line, const char *word, int pos, int state)
+{
+ if (pos == 2)
+ return complete_sip_registered_peer(word, state, 0);
+
+ return NULL;
+}
+
+/*! \brief Do completion on user name */
+static char *complete_sip_user(const char *word, int state, int flags2)
+{
+ char *result = NULL;
+ int wordlen = strlen(word);
+ int which = 0;
+
+ ASTOBJ_CONTAINER_TRAVERSE(&userl, !result, do {
+ /* locking of the object is not required because only the name and flags are being compared */
+ if (!strncasecmp(word, iterator->name, wordlen)) {
+ if (flags2 && !ast_test_flag(&iterator->flags[1], flags2))
+ continue;
+ if (++which > state) {
+ result = ast_strdup(iterator->name);
+ }
+ }
+ } while(0) );
+ return result;
+}
+
+/*! \brief Support routine for 'sip show user' CLI */
+static char *complete_sip_show_user(const char *line, const char *word, int pos, int state)
+{
+ if (pos == 3)
+ return complete_sip_user(word, state, 0);
+
+ return NULL;
+}
+
+/*! \brief Support routine for 'sip notify' CLI */
+static char *complete_sipnotify(const char *line, const char *word, int pos, int state)
+{
+ char *c = NULL;
+
+ if (pos == 2) {
+ int which = 0;
+ char *cat = NULL;
+ int wordlen = strlen(word);
+
+ /* do completion for notify type */
+
+ if (!notify_types)
+ return NULL;
+
+ while ( (cat = ast_category_browse(notify_types, cat)) ) {
+ if (!strncasecmp(word, cat, wordlen) && ++which > state) {
+ c = ast_strdup(cat);
+ break;
+ }
+ }
+ return c;
+ }
+
+ if (pos > 2)
+ return complete_sip_peer(word, state, 0);
+
+ return NULL;
+}
+
+/*! \brief Show details of one active dialog */
+static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sip_pvt *cur;
+ size_t len;
+ int found = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show channel";
+ e->usage =
+ "Usage: sip show channel <call-id>\n"
+ " Provides detailed status on a given SIP dialog (identified by SIP call-id).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_sipch(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ len = strlen(a->argv[3]);
+ dialoglist_lock();
+ for (cur = dialoglist; cur; cur = cur->next) {
+ if (!strncasecmp(cur->callid, a->argv[3], len)) {
+ char formatbuf[BUFSIZ/2];
+ ast_cli(a->fd,"\n");
+ if (cur->subscribed != NONE)
+ ast_cli(a->fd, " * Subscription (type: %s)\n", subscription_type2str(cur->subscribed));
+ else
+ ast_cli(a->fd, " * SIP Call\n");
+ ast_cli(a->fd, " Curr. trans. direction: %s\n", ast_test_flag(&cur->flags[0], SIP_OUTGOING) ? "Outgoing" : "Incoming");
+ ast_cli(a->fd, " Call-ID: %s\n", cur->callid);
+ ast_cli(a->fd, " Owner channel ID: %s\n", cur->owner ? cur->owner->name : "<none>");
+ ast_cli(a->fd, " Our Codec Capability: %d\n", cur->capability);
+ ast_cli(a->fd, " Non-Codec Capability (DTMF): %d\n", cur->noncodeccapability);
+ ast_cli(a->fd, " Their Codec Capability: %d\n", cur->peercapability);
+ ast_cli(a->fd, " Joint Codec Capability: %d\n", cur->jointcapability);
+ ast_cli(a->fd, " Format: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->owner ? cur->owner->nativeformats : 0) );
+ ast_cli(a->fd, " T.38 support %s\n", cli_yesno(cur->udptl != NULL));
+ ast_cli(a->fd, " Video support %s\n", cli_yesno(cur->vrtp != NULL));
+ ast_cli(a->fd, " MaxCallBR: %d kbps\n", cur->maxcallbitrate);
+ ast_cli(a->fd, " Theoretical Address: %s:%d\n", ast_inet_ntoa(cur->sa.sin_addr), ntohs(cur->sa.sin_port));
+ ast_cli(a->fd, " Received Address: %s:%d\n", ast_inet_ntoa(cur->recv.sin_addr), ntohs(cur->recv.sin_port));
+ ast_cli(a->fd, " SIP Transfer mode: %s\n", transfermode2str(cur->allowtransfer));
+ ast_cli(a->fd, " NAT Support: %s\n", nat2str(ast_test_flag(&cur->flags[0], SIP_NAT)));
+ ast_cli(a->fd, " Audio IP: %s %s\n", ast_inet_ntoa(cur->redirip.sin_addr.s_addr ? cur->redirip.sin_addr : cur->ourip.sin_addr), cur->redirip.sin_addr.s_addr ? "(Outside bridge)" : "(local)" );
+ ast_cli(a->fd, " Our Tag: %s\n", cur->tag);
+ ast_cli(a->fd, " Their Tag: %s\n", cur->theirtag);
+ ast_cli(a->fd, " SIP User agent: %s\n", cur->useragent);
+ if (!ast_strlen_zero(cur->username))
+ ast_cli(a->fd, " Username: %s\n", cur->username);
+ if (!ast_strlen_zero(cur->peername))
+ ast_cli(a->fd, " Peername: %s\n", cur->peername);
+ if (!ast_strlen_zero(cur->uri))
+ ast_cli(a->fd, " Original uri: %s\n", cur->uri);
+ if (!ast_strlen_zero(cur->cid_num))
+ ast_cli(a->fd, " Caller-ID: %s\n", cur->cid_num);
+ ast_cli(a->fd, " Need Destroy: %s\n", cli_yesno(cur->needdestroy));
+ ast_cli(a->fd, " Last Message: %s\n", cur->lastmsg);
+ ast_cli(a->fd, " Promiscuous Redir: %s\n", cli_yesno(ast_test_flag(&cur->flags[0], SIP_PROMISCREDIR)));
+ ast_cli(a->fd, " Route: %s\n", cur->route ? cur->route->hop : "N/A");
+ ast_cli(a->fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(&cur->flags[0], SIP_DTMF)));
+ ast_cli(a->fd, " SIP Options: ");
+ if (cur->sipoptions) {
+ int x;
+ for (x=0 ; (x < (sizeof(sip_options) / sizeof(sip_options[0]))); x++) {
+ if (cur->sipoptions & sip_options[x].id)
+ ast_cli(a->fd, "%s ", sip_options[x].text);
+ }
+ ast_cli(a->fd, "\n");
+ } else
+ ast_cli(a->fd, "(none)\n");
+
+ if (!cur->stimer)
+ ast_cli(a->fd, " Session-Timer: Uninitiallized\n");
+ else {
+ ast_cli(a->fd, " Session-Timer: %s\n", cur->stimer->st_active ? "Active" : "Inactive");
+ if (cur->stimer->st_active == TRUE) {
+ ast_cli(a->fd, " S-Timer Interval: %d\n", cur->stimer->st_interval);
+ ast_cli(a->fd, " S-Timer Refresher: %s\n", strefresher2str(cur->stimer->st_ref));
+ ast_cli(a->fd, " S-Timer Expirys: %d\n", cur->stimer->st_expirys);
+ ast_cli(a->fd, " S-Timer Sched Id: %d\n", cur->stimer->st_schedid);
+ ast_cli(a->fd, " S-Timer Peer Sts: %s\n", cur->stimer->st_active_peer_ua ? "Active" : "Inactive");
+ ast_cli(a->fd, " S-Timer Cached Min-SE: %d\n", cur->stimer->st_cached_min_se);
+ ast_cli(a->fd, " S-Timer Cached SE: %d\n", cur->stimer->st_cached_max_se);
+ ast_cli(a->fd, " S-Timer Cached Ref: %s\n", strefresher2str(cur->stimer->st_cached_ref));
+ ast_cli(a->fd, " S-Timer Cached Mode: %s\n", stmode2str(cur->stimer->st_cached_mode));
+ }
+ }
+
+ ast_cli(a->fd, "\n\n");
+
+ found++;
+ }
+ }
+ dialoglist_unlock();
+ if (!found)
+ ast_cli(a->fd, "No such SIP Call ID starting with '%s'\n", a->argv[3]);
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show history details of one dialog */
+static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sip_pvt *cur;
+ size_t len;
+ int found = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show history";
+ e->usage =
+ "Usage: sip show history <call-id>\n"
+ " Provides detailed dialog history on a given SIP call (specified by call-id).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_sip_show_history(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ if (!recordhistory)
+ ast_cli(a->fd, "\n***Note: History recording is currently DISABLED. Use 'sip history' to ENABLE.\n");
+ len = strlen(a->argv[3]);
+ dialoglist_lock();
+ for (cur = dialoglist; cur; cur = cur->next) {
+ if (!strncasecmp(cur->callid, a->argv[3], len)) {
+ struct sip_history *hist;
+ int x = 0;
+
+ ast_cli(a->fd,"\n");
+ if (cur->subscribed != NONE)
+ ast_cli(a->fd, " * Subscription\n");
+ else
+ ast_cli(a->fd, " * SIP Call\n");
+ if (cur->history)
+ AST_LIST_TRAVERSE(cur->history, hist, list)
+ ast_cli(a->fd, "%d. %s\n", ++x, hist->event);
+ if (x == 0)
+ ast_cli(a->fd, "Call '%s' has no history\n", cur->callid);
+ found++;
+ }
+ }
+ dialoglist_unlock();
+ if (!found)
+ ast_cli(a->fd, "No such SIP Call ID starting with '%s'\n", a->argv[3]);
+ return CLI_SUCCESS;
+}
+
+/*! \brief Dump SIP history to debug log file at end of lifespan for SIP dialog */
+static void sip_dump_history(struct sip_pvt *dialog)
+{
+ int x = 0;
+ struct sip_history *hist;
+ static int errmsg = 0;
+
+ if (!dialog)
+ return;
+
+ if (!option_debug && !sipdebug) {
+ if (!errmsg) {
+ ast_log(LOG_NOTICE, "You must have debugging enabled (SIP or Asterisk) in order to dump SIP history.\n");
+ errmsg = 1;
+ }
+ return;
+ }
+
+ ast_debug(1, "\n---------- SIP HISTORY for '%s' \n", dialog->callid);
+ if (dialog->subscribed)
+ ast_debug(1, " * Subscription\n");
+ else
+ ast_debug(1, " * SIP Call\n");
+ if (dialog->history)
+ AST_LIST_TRAVERSE(dialog->history, hist, list)
+ ast_debug(1, " %-3.3d. %s\n", ++x, hist->event);
+ if (!x)
+ ast_debug(1, "Call '%s' has no history\n", dialog->callid);
+ ast_debug(1, "\n---------- END SIP HISTORY for '%s' \n", dialog->callid);
+}
+
+
+/*! \brief Receive SIP INFO Message
+\note Doesn't read the duration of the DTMF signal */
+static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
+{
+ char buf[1024];
+ unsigned int event;
+ const char *c = get_header(req, "Content-Type");
+
+ /* Need to check the media/type */
+ if (!strcasecmp(c, "application/dtmf-relay") ||
+ !strcasecmp(c, "application/vnd.nortelnetworks.digits")) {
+ unsigned int duration = 0;
+
+ if (!p->owner) { /* not a PBX call */
+ transmit_response(p, "481 Call leg/transaction does not exist", req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return;
+ }
+
+ /* Try getting the "signal=" part */
+ if (ast_strlen_zero(c = get_body(req, "Signal")) && ast_strlen_zero(c = get_body(req, "d"))) {
+ ast_log(LOG_WARNING, "Unable to retrieve DTMF signal from INFO message from %s\n", p->callid);
+ transmit_response(p, "200 OK", req); /* Should return error */
+ return;
+ } else {
+ ast_copy_string(buf, c, sizeof(buf));
+ }
+
+ if (!ast_strlen_zero((c = get_body(req, "Duration"))))
+ duration = atoi(c);
+ if (!duration)
+ duration = 100; /* 100 ms */
+
+
+ if (ast_strlen_zero(buf)) {
+ transmit_response(p, "200 OK", req);
+ return;
+ }
+
+ if (buf[0] == '*')
+ event = 10;
+ else if (buf[0] == '#')
+ event = 11;
+ else if ((buf[0] >= 'A') && (buf[0] <= 'D'))
+ event = 12 + buf[0] - 'A';
+ else if (buf[0] == '!')
+ event = 16;
+ else
+ event = atoi(buf);
+ if (event == 16) {
+ /* send a FLASH event */
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH, };
+ ast_queue_frame(p->owner, &f);
+ if (sipdebug)
+ ast_verbose("* DTMF-relay event received: FLASH\n");
+ } else {
+ /* send a DTMF event */
+ struct ast_frame f = { AST_FRAME_DTMF, };
+ if (event < 10) {
+ f.subclass = '0' + event;
+ } else if (event < 11) {
+ f.subclass = '*';
+ } else if (event < 12) {
+ f.subclass = '#';
+ } else if (event < 16) {
+ f.subclass = 'A' + (event - 12);
+ }
+ f.len = duration;
+ ast_queue_frame(p->owner, &f);
+ if (sipdebug)
+ ast_verbose("* DTMF-relay event received: %c\n", f.subclass);
+ }
+ transmit_response(p, "200 OK", req);
+ return;
+ } else if (!strcasecmp(c, "application/dtmf")) {
+ unsigned int duration = 0;
+
+ if (!p->owner) { /* not a PBX call */
+ transmit_response(p, "481 Call leg/transaction does not exist", req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return;
+ }
+
+
+
+ get_msg_text(buf, sizeof(buf), req);
+ duration = 100; /* 100 ms */
+
+ if (ast_strlen_zero(buf)) {
+ transmit_response(p, "200 OK", req);
+ return;
+ }
+ event = atoi(buf);
+ if (event == 16) {
+ /* send a FLASH event */
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH, };
+ ast_queue_frame(p->owner, &f);
+ if (sipdebug)
+ ast_verbose("* DTMF-relay event received: FLASH\n");
+ } else {
+ /* send a DTMF event */
+ struct ast_frame f = { AST_FRAME_DTMF, };
+ if (event < 10) {
+ f.subclass = '0' + event;
+ } else if (event < 11) {
+ f.subclass = '*';
+ } else if (event < 12) {
+ f.subclass = '#';
+ } else if (event < 16) {
+ f.subclass = 'A' + (event - 12);
+ }
+ f.len = duration;
+ ast_queue_frame(p->owner, &f);
+ if (sipdebug)
+ ast_verbose("* DTMF-relay event received: %c\n", f.subclass);
+ }
+ transmit_response(p, "200 OK", req);
+ return;
+
+ } else if (!strcasecmp(c, "application/media_control+xml")) {
+ /* Eh, we'll just assume it's a fast picture update for now */
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_VIDUPDATE);
+ transmit_response(p, "200 OK", req);
+ return;
+ } else if (!ast_strlen_zero(c = get_header(req, "X-ClientCode"))) {
+ /* Client code (from SNOM phone) */
+ if (ast_test_flag(&p->flags[0], SIP_USECLIENTCODE)) {
+ if (p->owner && p->owner->cdr)
+ ast_cdr_setuserfield(p->owner, c);
+ if (p->owner && ast_bridged_channel(p->owner) && ast_bridged_channel(p->owner)->cdr)
+ ast_cdr_setuserfield(ast_bridged_channel(p->owner), c);
+ transmit_response(p, "200 OK", req);
+ } else {
+ transmit_response(p, "403 Forbidden", req);
+ }
+ return;
+ } else if (!ast_strlen_zero(c = get_header(req, "Record"))) {
+ /* INFO messages generated by some phones to start/stop recording
+ on phone calls.
+ OEJ: I think this should be something that is enabled/disabled
+ per device. I don't want incoming callers to record calls in my
+ pbx.
+ */
+ /* first, get the feature string, if it exists */
+ struct ast_call_feature *feat;
+ int j;
+ struct ast_frame f = { AST_FRAME_DTMF, };
+
+ ast_rdlock_call_features();
+ feat = ast_find_call_feature("automon");
+ if (!feat || ast_strlen_zero(feat->exten)) {
+ ast_log(LOG_WARNING,"Recording requested, but no One Touch Monitor registered. (See features.conf)\n");
+ /* 403 means that we don't support this feature, so don't request it again */
+ transmit_response(p, "403 Forbidden", req);
+ ast_unlock_call_features();
+ return;
+ }
+ /* Send the feature code to the PBX as DTMF, just like the handset had sent it */
+ f.len = 100;
+ for (j=0; j < strlen(feat->exten); j++) {
+ f.subclass = feat->exten[j];
+ ast_queue_frame(p->owner, &f);
+ if (sipdebug)
+ ast_verbose("* DTMF-relay event faked: %c\n", f.subclass);
+ }
+ ast_unlock_call_features();
+
+ ast_debug(1, "Got a Request to Record the channel, state %s\n", c);
+ transmit_response(p, "200 OK", req);
+ return;
+ } else if (ast_strlen_zero(c = get_header(req, "Content-Length")) || !strcasecmp(c, "0")) {
+ /* This is probably just a packet making sure the signalling is still up, just send back a 200 OK */
+ transmit_response(p, "200 OK", req);
+ return;
+ }
+
+ /* Other type of INFO message, not really understood by Asterisk */
+ /* if (get_msg_text(buf, sizeof(buf), req)) { */
+
+ ast_log(LOG_WARNING, "Unable to parse INFO message from %s. Content %s\n", p->callid, buf);
+ transmit_response(p, "415 Unsupported media type", req);
+ return;
+}
+
+/*! \brief Enable SIP Debugging for a single IP */
+static char *sip_do_debug_ip(int fd, char *arg)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p;
+
+ p = arg;
+ strsep(&p, ":");
+ if (p)
+ port = atoi(p);
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL)
+ return CLI_SHOWUSAGE;
+
+ debugaddr.sin_family = AF_INET;
+ memcpy(&debugaddr.sin_addr, hp->h_addr, sizeof(debugaddr.sin_addr));
+ debugaddr.sin_port = htons(port);
+ if (port == 0)
+ ast_cli(fd, "SIP Debugging Enabled for IP: %s\n", ast_inet_ntoa(debugaddr.sin_addr));
+ else
+ ast_cli(fd, "SIP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(debugaddr.sin_addr), port);
+
+ sipdebug |= sip_debug_console;
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief sip_do_debug_peer: Turn on SIP debugging for a given peer */
+static char *sip_do_debug_peer(int fd, char *arg)
+{
+ struct sip_peer *peer = find_peer(arg, NULL, 1);
+ if (!peer)
+ ast_cli(fd, "No such peer '%s'\n", arg);
+ else if (peer->addr.sin_addr.s_addr == 0)
+ ast_cli(fd, "Unable to get IP address of peer '%s'\n", arg);
+ else {
+ debugaddr.sin_family = AF_INET;
+ debugaddr.sin_addr = peer->addr.sin_addr;
+ debugaddr.sin_port = peer->addr.sin_port;
+ ast_cli(fd, "SIP Debugging Enabled for IP: %s:%d\n",
+ ast_inet_ntoa(debugaddr.sin_addr), ntohs(debugaddr.sin_port));
+ sipdebug |= sip_debug_console;
+ }
+ if (peer)
+ unref_peer(peer);
+ return CLI_SUCCESS;
+}
+
+/*! \brief Turn on SIP debugging (CLI command) */
+static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldsipdebug = sipdebug & sip_debug_console;
+ char *what;
+
+ if (cmd == CLI_INIT) {
+ e->command = "sip set debug {on|off|ip|peer}";
+ e->usage =
+ "Usage: sip set debug {off|on|ip addr[:port]|peer peername}\n"
+ " Globally disables dumping of SIP packets,\n"
+ " or enables it either globally or for a (single)\n"
+ " IP address or registered peer.\n";
+ return NULL;
+ } else if (cmd == CLI_GENERATE) {
+ if (a->pos == 4 && strcasestr(a->line, " peer")) /* XXX should check on argv too */
+ return complete_sip_peer(a->word, a->n, 0);
+ return NULL;
+ }
+
+ what = a->argv[e->args-1]; /* guaranteed to exist */
+ if (a->argc == e->args) { /* on/off */
+ if (!strcasecmp(what, "on")) {
+ sipdebug |= sip_debug_console;
+ sipdebug_text = 1; /*! \note this can be a special debug command - "sip debug text" or something */
+ memset(&debugaddr, 0, sizeof(debugaddr));
+ ast_cli(a->fd, "SIP Debugging %senabled\n", oldsipdebug ? "re-" : "");
+ return CLI_SUCCESS;
+ } else if (!strcasecmp(what, "off")) {
+ sipdebug &= ~sip_debug_console;
+ sipdebug_text = 0;
+ ast_cli(a->fd, "SIP Debugging Disabled\n");
+ return CLI_SUCCESS;
+ }
+ } else if (a->argc == e->args +1) {/* ip/peer */
+ if (!strcasecmp(what, "ip"))
+ return sip_do_debug_ip(a->fd, a->argv[e->args]);
+ else if (!strcasecmp(what, "peer"))
+ return sip_do_debug_peer(a->fd, a->argv[e->args]);
+ }
+ return CLI_SHOWUSAGE; /* default, failure */
+}
+
+/*! \brief Cli command to send SIP notify to peer */
+static char *sip_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_variable *varlist;
+ int i;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip notify";
+ e->usage =
+ "Usage: sip notify <type> <peer> [<peer>...]\n"
+ " Send a NOTIFY message to a SIP peer or peers\n"
+ " Message types are defined in sip_notify.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_sipnotify(a->line, a->word, a->pos, a->n);
+ }
+
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ if (!notify_types) {
+ ast_cli(a->fd, "No %s file found, or no types listed there\n", notify_config);
+ return CLI_FAILURE;
+ }
+
+ varlist = ast_variable_browse(notify_types, a->argv[2]);
+
+ if (!varlist) {
+ ast_cli(a->fd, "Unable to find notify type '%s'\n", a->argv[2]);
+ return CLI_FAILURE;
+ }
+
+ for (i = 3; i < a->argc; i++) {
+ struct sip_pvt *p;
+ struct sip_request req;
+ struct ast_variable *var;
+
+ if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) {
+ ast_log(LOG_WARNING, "Unable to build sip pvt data for notify (memory/socket error)\n");
+ return CLI_FAILURE;
+ }
+
+ if (create_addr(p, a->argv[i])) {
+ /* Maybe they're not registered, etc. */
+ sip_destroy(p);
+ ast_cli(a->fd, "Could not create address for '%s'\n", a->argv[i]);
+ continue;
+ }
+
+ initreqprep(&req, p, SIP_NOTIFY);
+
+ for (var = varlist; var; var = var->next) {
+ char buf[512];
+ ast_copy_string(buf, var->value, sizeof(buf));
+ add_header(&req, var->name, ast_unescape_semicolon(buf));
+ }
+
+ /* Recalculate our side, and recalculate Call ID */
+ ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
+ build_via(p);
+ build_callid_pvt(p);
+ ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n", a->argv[2], a->argv[i]);
+ transmit_sip_request(p, &req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ dialog_unref(p);
+ }
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Enable SIP History logging (CLI) */
+static char *sip_do_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip history";
+ e->usage =
+ "Usage: sip history\n"
+ " Enables recording of SIP dialog history for debugging purposes.\n"
+ " Use 'sip show history' to view the history of a call number.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2) {
+ return CLI_SHOWUSAGE;
+ }
+ recordhistory = TRUE;
+ ast_cli(a->fd, "SIP History Recording Enabled (use 'sip show history')\n");
+ return CLI_SUCCESS;
+}
+
+/*! \brief Disable SIP History logging (CLI) */
+static char *sip_no_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip history off";
+ e->usage =
+ "Usage: sip history off\n"
+ " Disables recording of SIP dialog history for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+ recordhistory = FALSE;
+ ast_cli(a->fd, "SIP History Recording Disabled\n");
+ return CLI_SUCCESS;
+}
+
+/*! \brief Authenticate for outbound registration */
+static int do_register_auth(struct sip_pvt *p, struct sip_request *req, enum sip_auth_type code)
+{
+ char *header, *respheader;
+ char digest[1024];
+
+ p->authtries++;
+ auth_headers(code, &header, &respheader);
+ memset(digest,0,sizeof(digest));
+ if (reply_digest(p, req, header, SIP_REGISTER, digest, sizeof(digest))) {
+ /* There's nothing to use for authentication */
+ /* No digest challenge in request */
+ if (sip_debug_test_pvt(p) && p->registry)
+ ast_verbose("No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname);
+ /* No old challenge */
+ return -1;
+ }
+ if (p->do_history)
+ append_history(p, "RegistryAuth", "Try: %d", p->authtries);
+ if (sip_debug_test_pvt(p) && p->registry)
+ ast_verbose("Responding to challenge, registration to domain/host name %s\n", p->registry->hostname);
+ return transmit_register(p->registry, SIP_REGISTER, digest, respheader);
+}
+
+/*! \brief Add authentication on outbound SIP packet */
+static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, enum sip_auth_type code, int sipmethod, int init)
+{
+ char *header, *respheader;
+ char digest[1024];
+
+ if (!p->options && !(p->options = ast_calloc(1, sizeof(*p->options))))
+ return -2;
+
+ p->authtries++;
+ auth_headers(code, &header, &respheader);
+ ast_debug(2, "Auth attempt %d on %s\n", p->authtries, sip_methods[sipmethod].text);
+ memset(digest, 0, sizeof(digest));
+ if (reply_digest(p, req, header, sipmethod, digest, sizeof(digest) )) {
+ /* No way to authenticate */
+ return -1;
+ }
+ /* Now we have a reply digest */
+ p->options->auth = digest;
+ p->options->authheader = respheader;
+ return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init);
+}
+
+/*! \brief reply to authentication for outbound registrations
+\return Returns -1 if we have no auth
+\note This is used for register= servers in sip.conf, SIP proxies we register
+ with for receiving calls from. */
+static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len)
+{
+ char tmp[512];
+ char *c;
+ char oldnonce[256];
+
+ /* table of recognised keywords, and places where they should be copied */
+ const struct x {
+ const char *key;
+ const ast_string_field *field;
+ } *i, keys[] = {
+ { "realm=", &p->realm },
+ { "nonce=", &p->nonce },
+ { "opaque=", &p->opaque },
+ { "qop=", &p->qop },
+ { "domain=", &p->domain },
+ { NULL, 0 },
+ };
+
+ ast_copy_string(tmp, get_header(req, header), sizeof(tmp));
+ if (ast_strlen_zero(tmp))
+ return -1;
+ if (strncasecmp(tmp, "Digest ", strlen("Digest "))) {
+ ast_log(LOG_WARNING, "missing Digest.\n");
+ return -1;
+ }
+ c = tmp + strlen("Digest ");
+ ast_copy_string(oldnonce, p->nonce, sizeof(oldnonce));
+ while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */
+ for (i = keys; i->key != NULL; i++) {
+ char *src, *separator;
+ if (strncasecmp(c, i->key, strlen(i->key)) != 0)
+ continue;
+ /* Found. Skip keyword, take text in quotes or up to the separator. */
+ c += strlen(i->key);
+ if (*c == '"') {
+ src = ++c;
+ separator = "\"";
+ } else {
+ src = c;
+ separator = ",";
+ }
+ strsep(&c, separator); /* clear separator and move ptr */
+ ast_string_field_ptr_set(p, i->field, src);
+ break;
+ }
+ if (i->key == NULL) /* not found, try ',' */
+ strsep(&c, ",");
+ }
+ /* Reset nonce count */
+ if (strcmp(p->nonce, oldnonce))
+ p->noncecount = 0;
+
+ /* Save auth data for following registrations */
+ if (p->registry) {
+ struct sip_registry *r = p->registry;
+
+ if (strcmp(r->nonce, p->nonce)) {
+ ast_string_field_set(r, realm, p->realm);
+ ast_string_field_set(r, nonce, p->nonce);
+ ast_string_field_set(r, domain, p->domain);
+ ast_string_field_set(r, opaque, p->opaque);
+ ast_string_field_set(r, qop, p->qop);
+ r->noncecount = 0;
+ }
+ }
+ return build_reply_digest(p, sipmethod, digest, digest_len);
+}
+
+/*! \brief Build reply digest
+\return Returns -1 if we have no auth
+\note Build digest challenge for authentication of peers (for registration)
+ and users (for calls). Also used for authentication of CANCEL and BYE
+*/
+static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int digest_len)
+{
+ char a1[256];
+ char a2[256];
+ char a1_hash[256];
+ char a2_hash[256];
+ char resp[256];
+ char resp_hash[256];
+ char uri[256];
+ char cnonce[80];
+ const char *username;
+ const char *secret;
+ const char *md5secret;
+ struct sip_auth *auth = NULL; /* Realm authentication */
+
+ if (!ast_strlen_zero(p->domain))
+ ast_copy_string(uri, p->domain, sizeof(uri));
+ else if (!ast_strlen_zero(p->uri))
+ ast_copy_string(uri, p->uri, sizeof(uri));
+ else
+ snprintf(uri, sizeof(uri), "sip:%s@%s",p->username, ast_inet_ntoa(p->sa.sin_addr));
+
+ snprintf(cnonce, sizeof(cnonce), "%08lx", ast_random());
+
+ /* Check if we have separate auth credentials */
+ if ((auth = find_realm_authentication(authl, p->realm))) {
+ ast_log(LOG_WARNING, "use realm [%s] from peer [%s][%s]\n",
+ auth->username, p->peername, p->username);
+ username = auth->username;
+ secret = auth->secret;
+ md5secret = auth->md5secret;
+ if (sipdebug)
+ ast_debug(1,"Using realm %s authentication for call %s\n", p->realm, p->callid);
+ } else {
+ /* No authentication, use peer or register= config */
+ username = p->authname;
+ secret = p->peersecret;
+ md5secret = p->peermd5secret;
+ }
+ if (ast_strlen_zero(username)) /* We have no authentication */
+ return -1;
+
+ /* Calculate SIP digest response */
+ snprintf(a1,sizeof(a1),"%s:%s:%s", username, p->realm, secret);
+ snprintf(a2,sizeof(a2),"%s:%s", sip_methods[method].text, uri);
+ if (!ast_strlen_zero(md5secret))
+ ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
+ else
+ ast_md5_hash(a1_hash,a1);
+ ast_md5_hash(a2_hash,a2);
+
+ p->noncecount++;
+ if (!ast_strlen_zero(p->qop))
+ snprintf(resp,sizeof(resp),"%s:%s:%08x:%s:%s:%s", a1_hash, p->nonce, p->noncecount, cnonce, "auth", a2_hash);
+ else
+ snprintf(resp,sizeof(resp),"%s:%s:%s", a1_hash, p->nonce, a2_hash);
+ ast_md5_hash(resp_hash, resp);
+ /* XXX We hard code our qop to "auth" for now. XXX */
+ if (!ast_strlen_zero(p->qop))
+ snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\", qop=auth, cnonce=\"%s\", nc=%08x", username, p->realm, uri, p->nonce, resp_hash, p->opaque, cnonce, p->noncecount);
+ else
+ snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\"", username, p->realm, uri, p->nonce, resp_hash, p->opaque);
+
+ append_history(p, "AuthResp", "Auth response sent for %s in realm %s - nc %d", username, p->realm, p->noncecount);
+
+ return 0;
+}
+
+/*! \brief Read SIP header (dialplan function) */
+static int func_header_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
+{
+ struct sip_pvt *p;
+ const char *content = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(header);
+ AST_APP_ARG(number);
+ );
+ int i, number, start = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "This function requires a header name.\n");
+ return -1;
+ }
+
+ ast_channel_lock(chan);
+ if (!IS_SIP_TECH(chan->tech)) {
+ ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+ if (!args.number) {
+ number = 1;
+ } else {
+ sscanf(args.number, "%d", &number);
+ if (number < 1)
+ number = 1;
+ }
+
+ p = chan->tech_pvt;
+
+ /* If there is no private structure, this channel is no longer alive */
+ if (!p) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ for (i = 0; i < number; i++)
+ content = __get_header(&p->initreq, args.header, &start);
+
+ if (ast_strlen_zero(content)) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ ast_copy_string(buf, content, len);
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function sip_header_function = {
+ .name = "SIP_HEADER",
+ .synopsis = "Gets the specified SIP header",
+ .syntax = "SIP_HEADER(<name>[,<number>])",
+ .desc = "Since there are several headers (such as Via) which can occur multiple\n"
+ "times, SIP_HEADER takes an optional second argument to specify which header with\n"
+ "that name to retrieve. Headers start at offset 1.\n",
+ .read = func_header_read,
+};
+
+/*! \brief Dial plan function to check if domain is local */
+static int func_check_sipdomain(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "CHECKSIPDOMAIN requires an argument - A domain name\n");
+ return -1;
+ }
+ if (check_sip_domain(data, NULL, 0))
+ ast_copy_string(buf, data, len);
+ else
+ buf[0] = '\0';
+ return 0;
+}
+
+static struct ast_custom_function checksipdomain_function = {
+ .name = "CHECKSIPDOMAIN",
+ .synopsis = "Checks if domain is a local domain",
+ .syntax = "CHECKSIPDOMAIN(<domain|IP>)",
+ .read = func_check_sipdomain,
+ .desc = "This function checks if the domain in the argument is configured\n"
+ "as a local SIP domain that this Asterisk server is configured to handle.\n"
+ "Returns the domain name if it is locally handled, otherwise an empty string.\n"
+ "Check the domain= configuration in sip.conf\n",
+};
+
+/*! \brief ${SIPPEER()} Dialplan function - reads peer data */
+static int function_sippeer(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct sip_peer *peer;
+ char *colname;
+
+ if ((colname = strchr(data, ':'))) { /*! \todo Will be deprecated after 1.4 */
+ static int deprecation_warning = 0;
+ *colname++ = '\0';
+ if (deprecation_warning++ % 10 == 0)
+ ast_log(LOG_WARNING, "SIPPEER(): usage of ':' to separate arguments is deprecated. Please use ',' instead.\n");
+ } else if ((colname = strchr(data, ',')))
+ *colname++ = '\0';
+ else
+ colname = "ip";
+
+ if (!(peer = find_peer(data, NULL, 1)))
+ return -1;
+
+ if (!strcasecmp(colname, "ip")) {
+ ast_copy_string(buf, peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "", len);
+ } else if (!strcasecmp(colname, "port")) {
+ snprintf(buf, len, "%d", ntohs(peer->addr.sin_port));
+ } else if (!strcasecmp(colname, "status")) {
+ peer_status(peer, buf, len);
+ } else if (!strcasecmp(colname, "language")) {
+ ast_copy_string(buf, peer->language, len);
+ } else if (!strcasecmp(colname, "regexten")) {
+ ast_copy_string(buf, peer->regexten, len);
+ } else if (!strcasecmp(colname, "limit")) {
+ snprintf(buf, len, "%d", peer->call_limit);
+ } else if (!strcasecmp(colname, "busylevel")) {
+ snprintf(buf, len, "%d", peer->busy_level);
+ } else if (!strcasecmp(colname, "curcalls")) {
+ snprintf(buf, len, "%d", peer->inUse);
+ } else if (!strcasecmp(colname, "accountcode")) {
+ ast_copy_string(buf, peer->accountcode, len);
+ } else if (!strcasecmp(colname, "callgroup")) {
+ ast_print_group(buf, len, peer->callgroup);
+ } else if (!strcasecmp(colname, "pickupgroup")) {
+ ast_print_group(buf, len, peer->pickupgroup);
+ } else if (!strcasecmp(colname, "useragent")) {
+ ast_copy_string(buf, peer->useragent, len);
+ } else if (!strcasecmp(colname, "mailbox")) {
+ struct ast_str *mailbox_str = ast_str_alloca(512);
+ peer_mailboxes_to_str(&mailbox_str, peer);
+ ast_copy_string(buf, mailbox_str->str, len);
+ } else if (!strcasecmp(colname, "context")) {
+ ast_copy_string(buf, peer->context, len);
+ } else if (!strcasecmp(colname, "expire")) {
+ snprintf(buf, len, "%d", peer->expire);
+ } else if (!strcasecmp(colname, "dynamic")) {
+ ast_copy_string(buf, peer->host_dynamic ? "yes" : "no", len);
+ } else if (!strcasecmp(colname, "callerid_name")) {
+ ast_copy_string(buf, peer->cid_name, len);
+ } else if (!strcasecmp(colname, "callerid_num")) {
+ ast_copy_string(buf, peer->cid_num, len);
+ } else if (!strcasecmp(colname, "codecs")) {
+ ast_getformatname_multiple(buf, len -1, peer->capability);
+ } else if (!strncasecmp(colname, "codec[", 6)) {
+ char *codecnum;
+ int index = 0, codec = 0;
+
+ codecnum = colname + 6; /* move past the '[' */
+ codecnum = strsep(&codecnum, "]"); /* trim trailing ']' if any */
+ index = atoi(codecnum);
+ if((codec = ast_codec_pref_index(&peer->prefs, index))) {
+ ast_copy_string(buf, ast_getformatname(codec), len);
+ }
+ }
+
+ unref_peer(peer);
+
+ return 0;
+}
+
+/*! \brief Structure to declare a dialplan function: SIPPEER */
+struct ast_custom_function sippeer_function = {
+ .name = "SIPPEER",
+ .synopsis = "Gets SIP peer information",
+ .syntax = "SIPPEER(<peername>[,item])",
+ .read = function_sippeer,
+ .desc = "Valid items are:\n"
+ "- ip (default) The IP address.\n"
+ "- port The port number\n"
+ "- mailbox The configured mailbox.\n"
+ "- context The configured context.\n"
+ "- expire The epoch time of the next expire.\n"
+ "- dynamic Is it dynamic? (yes/no).\n"
+ "- callerid_name The configured Caller ID name.\n"
+ "- callerid_num The configured Caller ID number.\n"
+ "- callgroup The configured Callgroup.\n"
+ "- pickupgroup The configured Pickupgroup.\n"
+ "- codecs The configured codecs.\n"
+ "- status Status (if qualify=yes).\n"
+ "- regexten Registration extension\n"
+ "- limit Call limit (call-limit)\n"
+ "- busylevel Configured call level for signalling busy\n"
+ "- curcalls Current amount of calls \n"
+ " Only available if call-limit is set\n"
+ "- language Default language for peer\n"
+ "- accountcode Account code for this peer\n"
+ "- useragent Current user agent id for peer\n"
+ "- codec[x] Preferred codec index number 'x' (beginning with zero).\n"
+ "\n"
+};
+
+/*! \brief ${SIPCHANINFO()} Dialplan function - reads sip channel data */
+static int function_sipchaninfo_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct sip_pvt *p;
+
+ *buf = 0;
+
+ if (!data) {
+ ast_log(LOG_WARNING, "This function requires a parameter name.\n");
+ return -1;
+ }
+
+ ast_channel_lock(chan);
+ if (!IS_SIP_TECH(chan->tech)) {
+ ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ p = chan->tech_pvt;
+
+ /* If there is no private structure, this channel is no longer alive */
+ if (!p) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ if (!strcasecmp(data, "peerip")) {
+ ast_copy_string(buf, p->sa.sin_addr.s_addr ? ast_inet_ntoa(p->sa.sin_addr) : "", len);
+ } else if (!strcasecmp(data, "recvip")) {
+ ast_copy_string(buf, p->recv.sin_addr.s_addr ? ast_inet_ntoa(p->recv.sin_addr) : "", len);
+ } else if (!strcasecmp(data, "from")) {
+ ast_copy_string(buf, p->from, len);
+ } else if (!strcasecmp(data, "uri")) {
+ ast_copy_string(buf, p->uri, len);
+ } else if (!strcasecmp(data, "useragent")) {
+ ast_copy_string(buf, p->useragent, len);
+ } else if (!strcasecmp(data, "peername")) {
+ ast_copy_string(buf, p->peername, len);
+ } else if (!strcasecmp(data, "t38passthrough")) {
+ if (p->t38.state == T38_DISABLED)
+ ast_copy_string(buf, "0", sizeof("0"));
+ else /* T38 is offered or enabled in this call */
+ ast_copy_string(buf, "1", sizeof("1"));
+ } else {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+/*! \brief Structure to declare a dialplan function: SIPCHANINFO */
+static struct ast_custom_function sipchaninfo_function = {
+ .name = "SIPCHANINFO",
+ .synopsis = "Gets the specified SIP parameter from the current channel",
+ .syntax = "SIPCHANINFO(item)",
+ .read = function_sipchaninfo_read,
+ .desc = "Valid items are:\n"
+ "- peerip The IP address of the peer.\n"
+ "- recvip The source IP address of the peer.\n"
+ "- from The URI from the From: header.\n"
+ "- uri The URI from the Contact: header.\n"
+ "- useragent The useragent.\n"
+ "- peername The name of the peer.\n"
+ "- t38passthrough 1 if T38 is offered or enabled in this channel, otherwise 0\n"
+};
+
+/*! \brief Parse 302 Moved temporalily response */
+static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
+{
+ char tmp[BUFSIZ];
+ char *s, *e, *t;
+ char *domain;
+
+ ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp));
+ if ((t = strchr(tmp, ',')))
+ *t = '\0';
+ s = remove_uri_parameters(get_in_brackets(tmp));
+ if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) {
+ if (!strncasecmp(s, "sip:", 4))
+ s += 4;
+ else if (!strncasecmp(s, "sips:", 5))
+ s += 5;
+ e = strchr(s, '/');
+ if (e)
+ *e = '\0';
+ ast_debug(2, "Found promiscuous redirection to 'SIP/%s'\n", s);
+ if (p->owner)
+ ast_string_field_build(p->owner, call_forward, "SIP/%s", s);
+ } else {
+ e = strchr(tmp, '@');
+ if (e) {
+ *e++ = '\0';
+ domain = e;
+ } else {
+ /* No username part */
+ domain = tmp;
+ }
+ e = strchr(tmp, '/'); /* WHEN do we hae a forward slash in the URI? */
+ if (e)
+ *e = '\0';
+
+ if (!strncasecmp(s, "sip:", 4))
+ s += 4;
+ else if (!strncasecmp(s, "sips:", 5))
+ s += 5;
+ e = strchr(s, ';'); /* And username ; parameters? */
+ if (e)
+ *e = '\0';
+ ast_debug(2, "Received 302 Redirect to extension '%s' (domain %s)\n", s, domain);
+ if (p->owner) {
+ pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain);
+ ast_string_field_set(p->owner, call_forward, s);
+ }
+ }
+}
+
+/*! \brief Check pending actions on SIP call */
+static void check_pendings(struct sip_pvt *p)
+{
+ if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+ /* if we can't BYE, then this is really a pending CANCEL */
+ if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA)
+ transmit_request(p, SIP_CANCEL, p->ocseq, XMIT_RELIABLE, FALSE);
+ /* Actually don't destroy us yet, wait for the 487 on our original
+ INVITE, but do set an autodestruct just in case we never get it. */
+ else {
+ /* We have a pending outbound invite, don't send someting
+ new in-transaction */
+ if (p->pendinginvite)
+ return;
+
+ /* Perhaps there is an SD change INVITE outstanding */
+ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE);
+ }
+ ast_clear_flag(&p->flags[0], SIP_PENDINGBYE);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ } else if (ast_test_flag(&p->flags[0], SIP_NEEDREINVITE)) {
+ /* if we can't REINVITE, hold it for later */
+ if (p->pendinginvite || p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA || p->waitid > 0) {
+ ast_debug(2, "NOT Sending pending reinvite (yet) on '%s'\n", p->callid);
+ } else {
+ ast_debug(2, "Sending pending reinvite on '%s'\n", p->callid);
+ /* Didn't get to reinvite yet, so do it now */
+ transmit_reinvite_with_sdp(p, FALSE, FALSE);
+ ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
+ }
+ }
+}
+
+/*! \brief Reset the NEEDREINVITE flag after waiting when we get 491 on a Re-invite
+ to avoid race conditions between asterisk servers.
+ Called from the scheduler.
+*/
+static int sip_reinvite_retry(const void *data)
+{
+ struct sip_pvt *p = (struct sip_pvt *) data;
+
+ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
+ p->waitid = -1;
+ return 0;
+}
+
+
+/*! \brief Handle SIP response to INVITE dialogue */
+static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
+{
+ int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING);
+ int res = 0;
+ int xmitres = 0;
+ int reinvite = (p->owner && p->owner->_state == AST_STATE_UP);
+ struct ast_channel *bridgepeer = NULL;
+ char *p_hdrval;
+ int rtn;
+
+ if (reinvite)
+ ast_debug(4, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid);
+ else
+ ast_debug(4, "SIP response %d to standard invite\n", resp);
+
+ if (p->alreadygone) { /* This call is already gone */
+ ast_debug(1, "Got response on call that is already terminated: %s (ignoring)\n", p->callid);
+ return;
+ }
+
+ /* Acknowledge sequence number - This only happens on INVITE from SIP-call */
+ if (p->initid > -1) {
+ /* Don't auto congest anymore since we've gotten something useful back */
+ ast_sched_del(sched, p->initid);
+ p->initid = -1;
+ }
+
+ /* RFC3261 says we must treat every 1xx response (but not 100)
+ that we don't recognize as if it was 183.
+ */
+ if (resp > 100 && resp < 200 && resp!=101 && resp != 180 && resp != 182 && resp != 183)
+ resp = 183;
+
+ /* Any response between 100 and 199 is PROCEEDING */
+ if (resp >= 100 && resp < 200 && p->invitestate == INV_CALLING)
+ p->invitestate = INV_PROCEEDING;
+
+ /* Final response, not 200 ? */
+ if (resp >= 300 && (p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA ))
+ p->invitestate = INV_COMPLETED;
+
+
+ switch (resp) {
+ case 100: /* Trying */
+ case 101: /* Dialog establishment */
+ if (!req->ignore)
+ sip_cancel_destroy(p);
+ check_pendings(p);
+ break;
+
+ case 180: /* 180 Ringing */
+ case 182: /* 182 Queued */
+ if (!req->ignore)
+ sip_cancel_destroy(p);
+ if (!req->ignore && p->owner) {
+ ast_queue_control(p->owner, AST_CONTROL_RINGING);
+ if (p->owner->_state != AST_STATE_UP) {
+ ast_setstate(p->owner, AST_STATE_RINGING);
+ }
+ }
+ if (find_sdp(req)) {
+ p->invitestate = INV_EARLY_MEDIA;
+ res = process_sdp(p, req);
+ if (!req->ignore && p->owner) {
+ /* Queue a progress frame only if we have SDP in 180 or 182 */
+ ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
+ }
+ }
+ check_pendings(p);
+ break;
+
+ case 183: /* Session progress */
+ if (!req->ignore)
+ sip_cancel_destroy(p);
+ /* Ignore 183 Session progress without SDP */
+ if (find_sdp(req)) {
+ p->invitestate = INV_EARLY_MEDIA;
+ res = process_sdp(p, req);
+ if (!req->ignore && p->owner) {
+ /* Queue a progress frame */
+ ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
+ }
+ }
+ check_pendings(p);
+ break;
+
+ case 200: /* 200 OK on invite - someone's answering our call */
+ if (!req->ignore)
+ sip_cancel_destroy(p);
+ p->authtries = 0;
+ if (find_sdp(req)) {
+ if ((res = process_sdp(p, req)) && !req->ignore)
+ if (!reinvite)
+ /* This 200 OK's SDP is not acceptable, so we need to ack, then hangup */
+ /* For re-invites, we try to recover */
+ ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+ }
+
+ /* Parse contact header for continued conversation */
+ /* When we get 200 OK, we know which device (and IP) to contact for this call */
+ /* This is important when we have a SIP proxy between us and the phone */
+ if (outgoing) {
+ update_call_counter(p, DEC_CALL_RINGING);
+ parse_ok_contact(p, req);
+ if(set_address_from_contact(p)) {
+ /* Bad contact - we don't know how to reach this device */
+ /* We need to ACK, but then send a bye */
+ /* OEJ: Possible issue that may need a check:
+ If we have a proxy route between us and the device,
+ should we care about resolving the contact
+ or should we just send it?
+ */
+ if (!req->ignore)
+ ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+ }
+
+ /* Save Record-Route for any later requests we make on this dialogue */
+ if (!reinvite)
+ build_route(p, req, 1);
+ }
+
+ if (p->owner && (p->owner->_state == AST_STATE_UP) && (bridgepeer = ast_bridged_channel(p->owner))) { /* if this is a re-invite */
+ struct sip_pvt *bridgepvt = NULL;
+
+ if (!bridgepeer->tech) {
+ ast_log(LOG_WARNING, "Ooooh.. no tech! That's REALLY bad\n");
+ break;
+ }
+ if (IS_SIP_TECH(bridgepeer->tech)) {
+ bridgepvt = (struct sip_pvt*)(bridgepeer->tech_pvt);
+ if (bridgepvt->udptl) {
+ if (p->t38.state == T38_PEER_REINVITE) {
+ sip_handle_t38_reinvite(bridgepeer, p, 0);
+ ast_rtp_set_rtptimers_onhold(p->rtp);
+ if (p->vrtp)
+ ast_rtp_set_rtptimers_onhold(p->vrtp); /* Turn off RTP timers while we send fax */
+ } else if (p->t38.state == T38_DISABLED && bridgepeer && (bridgepvt->t38.state == T38_ENABLED)) {
+ ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n");
+ /* Insted of this we should somehow re-invite the other side of the bridge to RTP */
+ /* XXXX Should we really destroy this session here, without any response at all??? */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ } else {
+ ast_debug(2, "Strange... The other side of the bridge does not have a udptl struct\n");
+ sip_pvt_lock(bridgepvt);
+ bridgepvt->t38.state = T38_DISABLED;
+ sip_pvt_unlock(bridgepvt);
+ ast_debug(1,"T38 state changed to %d on channel %s\n", bridgepvt->t38.state, bridgepeer->tech->type);
+ p->t38.state = T38_DISABLED;
+ ast_debug(2,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ }
+ } else {
+ /* Other side is not a SIP channel */
+ ast_debug(2, "Strange... The other side of the bridge is not a SIP channel\n");
+ p->t38.state = T38_DISABLED;
+ ast_debug(2,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ }
+ }
+ if ((p->t38.state == T38_LOCAL_REINVITE) || (p->t38.state == T38_LOCAL_DIRECT)) {
+ /* If there was T38 reinvite and we are supposed to answer with 200 OK than this should set us to T38 negotiated mode */
+ p->t38.state = T38_ENABLED;
+ ast_debug(1, "T38 changed state to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ }
+
+ if (!req->ignore && p->owner) {
+ if (!reinvite) {
+ ast_queue_control(p->owner, AST_CONTROL_ANSWER);
+ if (global_callevents)
+ manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
+ "Channel: %s\r\nChanneltype: %s\r\nUniqueid: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",
+ p->owner->name, p->owner->uniqueid, "SIP", p->callid, p->fullcontact, p->peername);
+ } else { /* RE-invite */
+ ast_queue_frame(p->owner, &ast_null_frame);
+ }
+ } else {
+ /* It's possible we're getting an 200 OK after we've tried to disconnect
+ by sending CANCEL */
+ /* First send ACK, then send bye */
+ if (!req->ignore)
+ ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+ }
+
+ /* Check for Session-Timers related headers */
+ if (st_get_mode(p) != SESSION_TIMER_MODE_REFUSE && p->outgoing_call == TRUE && !reinvite) {
+ p_hdrval = (char*)get_header(req, "Session-Expires");
+ if (!ast_strlen_zero(p_hdrval)) {
+ /* UAS supports Session-Timers */
+ enum st_refresher tmp_st_ref = SESSION_TIMER_REFRESHER_AUTO;
+ int tmp_st_interval = 0;
+ rtn = parse_session_expires(p_hdrval, &tmp_st_interval, &tmp_st_ref);
+ if (rtn != 0) {
+ ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+ }
+ if (tmp_st_ref == SESSION_TIMER_REFRESHER_UAC ||
+ tmp_st_ref == SESSION_TIMER_REFRESHER_UAS) {
+ p->stimer->st_ref = tmp_st_ref;
+ }
+ if (tmp_st_interval) {
+ p->stimer->st_interval = tmp_st_interval;
+ }
+ p->stimer->st_active = TRUE;
+ p->stimer->st_active_peer_ua = TRUE;
+ start_session_timer(p);
+ } else {
+ /* UAS doesn't support Session-Timers */
+ if (st_get_mode(p) == SESSION_TIMER_MODE_ORIGINATE) {
+ p->stimer->st_ref = SESSION_TIMER_REFRESHER_UAC;
+ p->stimer->st_active_peer_ua = FALSE;
+ start_session_timer(p);
+ }
+ }
+ }
+
+
+ /* If I understand this right, the branch is different for a non-200 ACK only */
+ p->invitestate = INV_TERMINATED;
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE);
+ check_pendings(p);
+ break;
+
+ case 407: /* Proxy authentication */
+ case 401: /* Www auth */
+ /* First we ACK */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (p->options)
+ p->options->auth_type = resp;
+
+ /* Then we AUTH */
+ ast_string_field_set(p, theirtag, NULL); /* forget their old tag, so we don't match tags when getting response */
+ if (!req->ignore) {
+ if (p->authtries < MAX_AUTHTRIES)
+ p->invitestate = INV_CALLING;
+ if (p->authtries == MAX_AUTHTRIES || do_proxy_auth(p, req, resp, SIP_INVITE, 1)) {
+ ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", get_header(&p->initreq, "From"));
+ p->needdestroy = 1;
+ sip_alreadygone(p);
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ }
+ }
+ break;
+
+ case 403: /* Forbidden */
+ /* First we ACK */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From"));
+ if (!req->ignore && p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ p->needdestroy = 1;
+ sip_alreadygone(p);
+ break;
+
+ case 404: /* Not found */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (p->owner && !req->ignore)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ sip_alreadygone(p);
+ break;
+
+ case 408: /* Request timeout */
+ case 481: /* Call leg does not exist */
+ /* Could be REFER caused INVITE with replaces */
+ ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid);
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ break;
+
+ case 422: /* Session-Timers: Session interval too small */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ ast_string_field_set(p, theirtag, NULL);
+ proc_422_rsp(p, req);
+ break;
+
+ case 487: /* Cancelled transaction */
+ /* We have sent CANCEL on an outbound INVITE
+ This transaction is already scheduled to be killed by sip_hangup().
+ */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (p->owner && !req->ignore) {
+ ast_queue_hangup(p->owner);
+ append_history(p, "Hangup", "Got 487 on CANCEL request from us. Queued AST hangup request");
+ } else if (!req->ignore) {
+ update_call_counter(p, DEC_CALL_LIMIT);
+ append_history(p, "Hangup", "Got 487 on CANCEL request from us on call without owner. Killing this dialog.");
+ p->needdestroy = 1;
+ sip_alreadygone(p);
+ }
+ break;
+ case 488: /* Not acceptable here */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (reinvite && p->udptl) {
+ /* If this is a T.38 call, we should go back to
+ audio. If this is an audio call - something went
+ terribly wrong since we don't renegotiate codecs,
+ only IP/port .
+ */
+ p->t38.state = T38_DISABLED;
+ /* Try to reset RTP timers */
+ ast_rtp_set_rtptimers_onhold(p->rtp);
+ ast_log(LOG_ERROR, "Got error on T.38 re-invite. Bad configuration. Peer needs to have T.38 disabled.\n");
+
+ /*! \bug Is there any way we can go back to the audio call on both
+ sides here?
+ */
+ /* While figuring that out, hangup the call */
+ if (p->owner && !req->ignore)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ p->needdestroy = 1;
+ } else if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) {
+ /* We tried to send T.38 out in an initial INVITE and the remote side rejected it,
+ right now we can't fall back to audio so totally abort.
+ */
+ p->t38.state = T38_DISABLED;
+ /* Try to reset RTP timers */
+ ast_rtp_set_rtptimers_onhold(p->rtp);
+ ast_log(LOG_ERROR, "Got error on T.38 initial invite. Bailing out.\n");
+
+ /* The dialog is now terminated */
+ if (p->owner && !req->ignore)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ p->needdestroy = 1;
+ sip_alreadygone(p);
+ } else {
+ /* We can't set up this call, so give up */
+ if (p->owner && !req->ignore)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ p->needdestroy = 1;
+ }
+ break;
+ case 491: /* Pending */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (p->owner && !req->ignore) {
+ if (p->owner->_state != AST_STATE_UP) {
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ p->needdestroy = 1;
+ } else {
+ /* This is a re-invite that failed. */
+ /* Reset the flag after a while
+ */
+ int wait = 3 + ast_random() % 5;
+ p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, p);
+ ast_debug(2, "Reinvite race. Waiting %d secs before retry\n", wait);
+ }
+ }
+ break;
+
+ case 501: /* Not implemented */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ break;
+ }
+ if (xmitres == XMIT_ERROR)
+ ast_log(LOG_WARNING, "Could not transmit message in dialog %s\n", p->callid);
+}
+
+/* \brief Handle SIP response in REFER transaction
+ We've sent a REFER, now handle responses to it
+ */
+static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
+{
+ /* If no refer structure exists, then do nothing */
+ if (!p->refer)
+ return;
+
+ switch (resp) {
+ case 202: /* Transfer accepted */
+ /* We need to do something here */
+ /* The transferee is now sending INVITE to target */
+ p->refer->status = REFER_ACCEPTED;
+ /* Now wait for next message */
+ ast_debug(3, "Got 202 accepted on transfer\n");
+ /* We should hang along, waiting for NOTIFY's here */
+ break;
+
+ case 401: /* Not www-authorized on SIP method */
+ case 407: /* Proxy auth */
+ if (ast_strlen_zero(p->authname)) {
+ ast_log(LOG_WARNING, "Asked to authenticate REFER to %s:%d but we have no matching peer or realm auth!\n",
+ ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
+ p->needdestroy = 1;
+ }
+ if (p->authtries > 1 || do_proxy_auth(p, req, resp, SIP_REFER, 0)) {
+ ast_log(LOG_NOTICE, "Failed to authenticate on REFER to '%s'\n", get_header(&p->initreq, "From"));
+ p->refer->status = REFER_NOAUTH;
+ p->needdestroy = 1;
+ }
+ break;
+ case 481: /* Call leg does not exist */
+
+ /* A transfer with Replaces did not work */
+ /* OEJ: We should Set flag, cancel the REFER, go back
+ to original call - but right now we can't */
+ ast_log(LOG_WARNING, "Remote host can't match REFER request to call '%s'. Giving up.\n", p->callid);
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ p->needdestroy = 1;
+ break;
+
+ case 500: /* Server error */
+ case 501: /* Method not implemented */
+ /* Return to the current call onhold */
+ /* Status flag needed to be reset */
+ ast_log(LOG_NOTICE, "SIP transfer to %s failed, call miserably fails. \n", p->refer->refer_to);
+ p->needdestroy = 1;
+ p->refer->status = REFER_FAILED;
+ break;
+ case 603: /* Transfer declined */
+ ast_log(LOG_NOTICE, "SIP transfer to %s declined, call miserably fails. \n", p->refer->refer_to);
+ p->refer->status = REFER_FAILED;
+ p->needdestroy = 1;
+ break;
+ }
+}
+
+/*! \brief Handle responses on REGISTER to services */
+static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
+{
+ int expires, expires_ms;
+ struct sip_registry *r;
+ r=p->registry;
+
+ switch (resp) {
+ case 401: /* Unauthorized */
+ if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
+ ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s@%s' (Tries %d)\n", p->registry->username, p->registry->hostname, p->authtries);
+ p->needdestroy = 1;
+ }
+ break;
+ case 403: /* Forbidden */
+ ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname);
+ ast_sched_del(sched, r->timeout);
+ r->timeout = -1;
+ r->regstate = REG_STATE_NOAUTH;
+ p->needdestroy = 1;
+ break;
+ case 404: /* Not found */
+ ast_log(LOG_WARNING, "Got 404 Not found on SIP register to service %s@%s, giving up\n", p->registry->username,p->registry->hostname);
+ p->needdestroy = 1;
+ r->call = NULL;
+ r->regstate = REG_STATE_REJECTED;
+ ast_sched_del(sched, r->timeout);
+ r->timeout = -1;
+ break;
+ case 407: /* Proxy auth */
+ if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
+ ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s' (tries '%d')\n", get_header(&p->initreq, "From"), p->authtries);
+ p->needdestroy = 1;
+ }
+ break;
+ case 408: /* Request timeout */
+ if (global_regattempts_max)
+ p->registry->regattempts = global_regattempts_max+1;
+ p->needdestroy = 1;
+ r->call = NULL;
+ ast_sched_del(sched, r->timeout);
+ r->timeout = -1;
+ break;
+ case 423: /* Interval too brief */
+ r->expiry = atoi(get_header(req, "Min-Expires"));
+ ast_log(LOG_WARNING, "Got 423 Interval too brief for service %s@%s, minimum is %d seconds\n", p->registry->username, p->registry->hostname, r->expiry);
+ ast_sched_del(sched, r->timeout);
+ r->timeout = -1;
+ if (r->call) {
+ r->call = NULL;
+ p->needdestroy = 1;
+ }
+ if (r->expiry > max_expiry) {
+ ast_log(LOG_WARNING, "Required expiration time from %s@%s is too high, giving up\n", p->registry->username, p->registry->hostname);
+ r->expiry = default_expiry;
+ r->regstate = REG_STATE_REJECTED;
+ } else {
+ r->regstate = REG_STATE_UNREGISTERED;
+ transmit_register(r, SIP_REGISTER, NULL, NULL);
+ }
+ manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
+ break;
+ case 479: /* SER: Not able to process the URI - address is wrong in register*/
+ ast_log(LOG_WARNING, "Got error 479 on register to %s@%s, giving up (check config)\n", p->registry->username,p->registry->hostname);
+ p->needdestroy = 1;
+ r->call = NULL;
+ r->regstate = REG_STATE_REJECTED;
+ ast_sched_del(sched, r->timeout);
+ r->timeout = -1;
+ break;
+ case 200: /* 200 OK */
+ if (!r) {
+ ast_log(LOG_WARNING, "Got 200 OK on REGISTER that isn't a register\n");
+ p->needdestroy = 1;
+ return 0;
+ }
+
+ r->regstate = REG_STATE_REGISTERED;
+ r->regtime = ast_tvnow(); /* Reset time of last succesful registration */
+ manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nDomain: %s\r\nStatus: %s\r\n", r->hostname, regstate2str(r->regstate));
+ r->regattempts = 0;
+ ast_debug(1, "Registration successful\n");
+ if (r->timeout > -1) {
+ ast_debug(1, "Cancelling timeout %d\n", r->timeout);
+ ast_sched_del(sched, r->timeout);
+ }
+ r->timeout=-1;
+ r->call = NULL;
+ p->registry = NULL;
+ /* Let this one hang around until we have all the responses */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ /* p->needdestroy = 1; */
+
+ /* set us up for re-registering */
+ /* figure out how long we got registered for */
+ if (r->expire > -1)
+ ast_sched_del(sched, r->expire);
+ /* according to section 6.13 of RFC, contact headers override
+ expires headers, so check those first */
+ expires = 0;
+
+ /* XXX todo: try to save the extra call */
+ if (!ast_strlen_zero(get_header(req, "Contact"))) {
+ const char *contact = NULL;
+ const char *tmptmp = NULL;
+ int start = 0;
+ for(;;) {
+ contact = __get_header(req, "Contact", &start);
+ /* this loop ensures we get a contact header about our register request */
+ if(!ast_strlen_zero(contact)) {
+ if( (tmptmp=strstr(contact, p->our_contact))) {
+ contact=tmptmp;
+ break;
+ }
+ } else
+ break;
+ }
+ tmptmp = strcasestr(contact, "expires=");
+ if (tmptmp) {
+ if (sscanf(tmptmp + 8, "%d;", &expires) != 1)
+ expires = 0;
+ }
+
+ }
+ if (!expires)
+ expires=atoi(get_header(req, "expires"));
+ if (!expires)
+ expires=default_expiry;
+
+ expires_ms = expires * 1000;
+ if (expires <= EXPIRY_GUARD_LIMIT)
+ expires_ms -= MAX((expires_ms * EXPIRY_GUARD_PCT),EXPIRY_GUARD_MIN);
+ else
+ expires_ms -= EXPIRY_GUARD_SECS * 1000;
+ if (sipdebug)
+ ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d s)\n", r->hostname, expires, expires_ms/1000);
+
+ r->refresh= (int) expires_ms / 1000;
+
+ /* Schedule re-registration before we expire */
+ r->expire = ast_sched_replace(r->expire, sched, expires_ms, sip_reregister, r);
+ registry_unref(r);
+ }
+ return 1;
+}
+
+/*! \brief Handle qualification responses (OPTIONS) */
+static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_request *req)
+{
+ struct sip_peer *peer = p->relatedpeer;
+ int statechanged, is_reachable, was_reachable;
+ int pingtime = ast_tvdiff_ms(ast_tvnow(), peer->ps);
+
+ /*
+ * Compute the response time to a ping (goes in peer->lastms.)
+ * -1 means did not respond, 0 means unknown,
+ * 1..maxms is a valid response, >maxms means late response.
+ */
+ if (pingtime < 1) /* zero = unknown, so round up to 1 */
+ pingtime = 1;
+
+ /* Now determine new state and whether it has changed.
+ * Use some helper variables to simplify the writing
+ * of the expressions.
+ */
+ was_reachable = peer->lastms > 0 && peer->lastms <= peer->maxms;
+ is_reachable = pingtime <= peer->maxms;
+ statechanged = peer->lastms == 0 /* yes, unknown before */
+ || was_reachable != is_reachable;
+
+ peer->lastms = pingtime;
+ peer->call = dialog_unref(peer->call);
+ if (statechanged) {
+ const char *s = is_reachable ? "Reachable" : "Lagged";
+
+ ast_log(LOG_NOTICE, "Peer '%s' is now %s. (%dms / %dms)\n",
+ peer->name, s, pingtime, peer->maxms);
+ ast_device_state_changed("SIP/%s", peer->name);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
+ "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: %s\r\nTime: %d\r\n",
+ peer->name, s, pingtime);
+ if (is_reachable && global_regextenonqualify)
+ register_peer_exten(peer, TRUE);
+ }
+
+ p->needdestroy = 1;
+
+ /* Try again eventually */
+ peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched,
+ is_reachable ? peer->qualifyfreq : DEFAULT_FREQ_NOTOK,
+ sip_poke_peer_s, peer);
+}
+
+/*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */
+static void stop_media_flows(struct sip_pvt *p)
+{
+ /* Immediately stop RTP, VRTP and UDPTL as applicable */
+ if (p->rtp)
+ ast_rtp_stop(p->rtp);
+ if (p->vrtp)
+ ast_rtp_stop(p->vrtp);
+ if (p->trtp)
+ ast_rtp_stop(p->trtp);
+ if (p->udptl)
+ ast_udptl_stop(p->udptl);
+}
+
+/*! \brief Handle SIP response in dialogue */
+/* XXX only called by handle_incoming */
+static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
+{
+ struct ast_channel *owner;
+ int sipmethod;
+ int res = 1;
+ const char *c = get_header(req, "Cseq");
+ const char *msg = strchr(c, ' ');
+
+ if (!msg)
+ msg = "";
+ else
+ msg++;
+ sipmethod = find_sip_method(msg);
+
+ owner = p->owner;
+ if (owner)
+ owner->hangupcause = hangup_sip2cause(resp);
+
+ /* Acknowledge whatever it is destined for */
+ if ((resp >= 100) && (resp <= 199))
+ __sip_semi_ack(p, seqno, 0, sipmethod);
+ else
+ __sip_ack(p, seqno, 0, sipmethod);
+
+ /* Get their tag if we haven't already */
+ if (ast_strlen_zero(p->theirtag) || (resp >= 200)) {
+ char tag[128];
+
+ gettag(req, "To", tag, sizeof(tag));
+ ast_string_field_set(p, theirtag, tag);
+ }
+ /* This needs to be configurable on a channel/peer/user level,
+ not mandatory for all communication. Sadly enough, NAT implementations
+ are not so stable so we can always rely on these headers.
+ Temporarily disabled, while waiting for fix.
+ Fix assigned to Rizzo :-)
+ */
+ /* check_via_response(p, req); */
+ if (p->relatedpeer && p->method == SIP_OPTIONS) {
+ /* We don't really care what the response is, just that it replied back.
+ Well, as long as it's not a 100 response... since we might
+ need to hang around for something more "definitive" */
+ if (resp != 100)
+ handle_response_peerpoke(p, resp, req);
+ } else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ switch(resp) {
+ case 100: /* 100 Trying */
+ case 101: /* 101 Dialog establishment */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ break;
+ case 183: /* 183 Session Progress */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ break;
+ case 180: /* 180 Ringing */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ break;
+ case 182: /* 182 Queued */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ break;
+ case 200: /* 200 OK */
+ p->authtries = 0; /* Reset authentication counter */
+ if (sipmethod == SIP_MESSAGE || sipmethod == SIP_INFO) {
+ /* We successfully transmitted a message
+ or a video update request in INFO */
+ /* Nothing happens here - the message is inside a dialog */
+ } else if (sipmethod == SIP_INVITE) {
+ handle_response_invite(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_NOTIFY) {
+ /* They got the notify, this is the end */
+ if (p->owner) {
+ if (!p->refer) {
+ ast_log(LOG_WARNING, "Notify answer on an owned channel? - %s\n", p->owner->name);
+ ast_queue_hangup(p->owner);
+ } else
+ ast_debug(4, "Got OK on REFER Notify message\n");
+ } else {
+ if (p->subscribed == NONE)
+ p->needdestroy = 1;
+ }
+ } else if (sipmethod == SIP_REGISTER)
+ res = handle_response_register(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_BYE) /* Ok, we're ready to go */
+ p->needdestroy = 1;
+ break;
+ case 202: /* Transfer accepted */
+ if (sipmethod == SIP_REFER)
+ handle_response_refer(p, resp, rest, req, seqno);
+ break;
+ case 401: /* Not www-authorized on SIP method */
+ case 407: /* Proxy auth required */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_REFER)
+ handle_response_refer(p, resp, rest, req, seqno);
+ else if (p->registry && sipmethod == SIP_REGISTER)
+ res = handle_response_register(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_BYE) {
+ if (p->options)
+ p->options->auth_type = resp;
+ if (ast_strlen_zero(p->authname)) {
+ ast_log(LOG_WARNING, "Asked to authenticate %s, to %s:%d but we have no matching peer!\n",
+ msg, ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
+ p->needdestroy = 1;
+ } else if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, sipmethod, 0)) {
+ ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, get_header(&p->initreq, "From"));
+ p->needdestroy = 1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Got authentication request (%d) on %s to '%s'\n", resp, sip_methods[sipmethod].text, get_header(req, "To"));
+ p->needdestroy = 1;
+ }
+ break;
+ case 403: /* Forbidden - we failed authentication */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else if (p->registry && sipmethod == SIP_REGISTER)
+ res = handle_response_register(p, resp, rest, req, seqno);
+ else {
+ ast_log(LOG_WARNING, "Forbidden - maybe wrong password on authentication for %s\n", msg);
+ p->needdestroy = 1;
+ }
+ break;
+ case 404: /* Not found */
+ if (p->registry && sipmethod == SIP_REGISTER)
+ res = handle_response_register(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else if (owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ break;
+ case 423: /* Interval too brief */
+ if (sipmethod == SIP_REGISTER)
+ res = handle_response_register(p, resp, rest, req, seqno);
+ break;
+ case 408: /* Request timeout - terminate dialog */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_REGISTER)
+ res = handle_response_register(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_BYE) {
+ p->needdestroy = 1;
+ ast_debug(4, "Got timeout on bye. Thanks for the answer. Now, kill this call\n");
+ } else {
+ if (owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ p->needdestroy = 1;
+ }
+ break;
+
+ case 422: /* Session-Timers: Session Interval Too Small */
+ if (sipmethod == SIP_INVITE) {
+ handle_response_invite(p, resp, rest, req, seqno);
+ }
+ break;
+
+ case 481: /* Call leg does not exist */
+ if (sipmethod == SIP_INVITE) {
+ handle_response_invite(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_REFER) {
+ handle_response_refer(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_BYE) {
+ /* The other side has no transaction to bye,
+ just assume it's all right then */
+ ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid);
+ } else if (sipmethod == SIP_CANCEL) {
+ /* The other side has no transaction to cancel,
+ just assume it's all right then */
+ ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid);
+ } else {
+ ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid);
+ /* Guessing that this is not an important request */
+ }
+ break;
+ case 487:
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ break;
+ case 488: /* Not acceptable here - codec error */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ break;
+ case 491: /* Pending */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else {
+ ast_debug(1, "Got 491 on %s, unspported. Call ID %s\n", sip_methods[sipmethod].text, p->callid);
+ p->needdestroy = 1;
+ }
+ break;
+ case 501: /* Not Implemented */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_REFER)
+ handle_response_refer(p, resp, rest, req, seqno);
+ else
+ ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n", ast_inet_ntoa(p->sa.sin_addr), msg);
+ break;
+ case 603: /* Declined transfer */
+ if (sipmethod == SIP_REFER) {
+ handle_response_refer(p, resp, rest, req, seqno);
+ break;
+ }
+ /* Fallthrough */
+ default:
+ if ((resp >= 300) && (resp < 700)) {
+ /* Fatal response */
+ if ((resp != 487))
+ ast_verb(3, "Got SIP response %d \"%s\" back from %s\n", resp, rest, ast_inet_ntoa(p->sa.sin_addr));
+
+ if (sipmethod == SIP_INVITE)
+ stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
+
+ /* XXX Locking issues?? XXX */
+ switch(resp) {
+ case 300: /* Multiple Choices */
+ case 301: /* Moved permanently */
+ case 302: /* Moved temporarily */
+ case 305: /* Use Proxy */
+ parse_moved_contact(p, req);
+ /* Fall through */
+ case 486: /* Busy here */
+ case 600: /* Busy everywhere */
+ case 603: /* Decline */
+ if (p->owner)
+ ast_queue_control(p->owner, AST_CONTROL_BUSY);
+ break;
+ case 482: /*!
+ \note SIP is incapable of performing a hairpin call, which
+ is yet another failure of not having a layer 2 (again, YAY
+ IETF for thinking ahead). So we treat this as a call
+ forward and hope we end up at the right place... */
+ ast_debug(1, "Hairpin detected, setting up call forward for what it's worth\n");
+ if (p->owner)
+ ast_string_field_build(p->owner, call_forward,
+ "Local/%s@%s", p->username, p->context);
+ /* Fall through */
+ case 480: /* Temporarily Unavailable */
+ case 404: /* Not Found */
+ case 410: /* Gone */
+ case 400: /* Bad Request */
+ case 500: /* Server error */
+ if (sipmethod == SIP_REFER) {
+ handle_response_refer(p, resp, rest, req, seqno);
+ break;
+ }
+ /* Fall through */
+ case 503: /* Service Unavailable */
+ case 504: /* Server Timeout */
+ if (owner)
+ ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+ break;
+ default:
+ /* Send hangup */
+ if (owner && sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO && sipmethod != SIP_BYE)
+ ast_queue_hangup(p->owner);
+ break;
+ }
+ /* ACK on invite */
+ if (sipmethod == SIP_INVITE)
+ transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO)
+ sip_alreadygone(p);
+ if (!p->owner)
+ p->needdestroy = 1;
+ } else if ((resp >= 100) && (resp < 200)) {
+ if (sipmethod == SIP_INVITE) {
+ if (!req->ignore)
+ sip_cancel_destroy(p);
+ if (find_sdp(req))
+ process_sdp(p, req);
+ if (p->owner) {
+ /* Queue a progress frame */
+ ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
+ }
+ }
+ } else
+ ast_log(LOG_NOTICE, "Dont know how to handle a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(p->sa.sin_addr));
+ }
+ } else {
+ /* Responses to OUTGOING SIP requests on INCOMING calls
+ get handled here. As well as out-of-call message responses */
+ if (req->debug)
+ ast_verbose("SIP Response message for INCOMING dialog %s arrived\n", msg);
+
+ if (sipmethod == SIP_INVITE && resp == 200) {
+ /* Tags in early session is replaced by the tag in 200 OK, which is
+ the final reply to our INVITE */
+ char tag[128];
+
+ gettag(req, "To", tag, sizeof(tag));
+ ast_string_field_set(p, theirtag, tag);
+ }
+
+ switch(resp) {
+ case 200:
+ if (sipmethod == SIP_INVITE) {
+ handle_response_invite(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_CANCEL) {
+ ast_debug(1, "Got 200 OK on CANCEL\n");
+
+ /* Wait for 487, then destroy */
+ } else if (sipmethod == SIP_NOTIFY) {
+ /* They got the notify, this is the end */
+ if (p->owner) {
+ if (p->refer) {
+ ast_debug(1, "Got 200 OK on NOTIFY for transfer\n");
+ } else
+ ast_log(LOG_WARNING, "Notify answer on an owned channel?\n");
+ /* ast_queue_hangup(p->owner); Disabled */
+ } else {
+ if (!p->subscribed && !p->refer)
+ p->needdestroy = 1;
+ }
+ } else if (sipmethod == SIP_BYE)
+ p->needdestroy = 1;
+ else if (sipmethod == SIP_MESSAGE || sipmethod == SIP_INFO)
+ /* We successfully transmitted a message or
+ a video update request in INFO */
+ ;
+ else if (sipmethod == SIP_BYE)
+ /* Ok, we're ready to go */
+ p->needdestroy = 1;
+ break;
+ case 202: /* Transfer accepted */
+ if (sipmethod == SIP_REFER)
+ handle_response_refer(p, resp, rest, req, seqno);
+ break;
+ case 401: /* www-auth */
+ case 407:
+ if (sipmethod == SIP_REFER)
+ handle_response_refer(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_BYE) {
+ if (p->authtries == MAX_AUTHTRIES || do_proxy_auth(p, req, resp, sipmethod, 0)) {
+ ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, get_header(&p->initreq, "From"));
+ p->needdestroy = 1;
+ }
+ }
+ break;
+ case 481: /* Call leg does not exist */
+ if (sipmethod == SIP_INVITE) {
+ /* Re-invite failed */
+ handle_response_invite(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_BYE) {
+ p->needdestroy = 1;
+ } else if (sipdebug) {
+ ast_debug(1, "Remote host can't match request %s to call '%s'. Giving up\n", sip_methods[sipmethod].text, p->callid);
+ }
+ break;
+ case 501: /* Not Implemented */
+ if (sipmethod == SIP_INVITE)
+ handle_response_invite(p, resp, rest, req, seqno);
+ else if (sipmethod == SIP_REFER)
+ handle_response_refer(p, resp, rest, req, seqno);
+ break;
+ case 603: /* Declined transfer */
+ if (sipmethod == SIP_REFER) {
+ handle_response_refer(p, resp, rest, req, seqno);
+ break;
+ }
+ /* Fallthrough */
+ default: /* Errors without handlers */
+ if ((resp >= 100) && (resp < 200)) {
+ if (sipmethod == SIP_INVITE) { /* re-invite */
+ if (!req->ignore)
+ sip_cancel_destroy(p);
+ }
+ }
+ if ((resp >= 300) && (resp < 700)) {
+ if ((resp != 487))
+ ast_verb(3, "Incoming call: Got SIP response %d \"%s\" back from %s\n", resp, rest, ast_inet_ntoa(p->sa.sin_addr));
+ switch(resp) {
+ case 488: /* Not acceptable here - codec error */
+ case 603: /* Decline */
+ case 500: /* Server error */
+ case 503: /* Service Unavailable */
+ case 504: /* Server timeout */
+
+ if (sipmethod == SIP_INVITE) { /* re-invite failed */
+ sip_cancel_destroy(p);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+/*! \brief Park SIP call support function
+ Starts in a new thread, then parks the call
+ XXX Should we add a wait period after streaming audio and before hangup?? Sometimes the
+ audio can't be heard before hangup
+*/
+static void *sip_park_thread(void *stuff)
+{
+ struct ast_channel *transferee, *transferer; /* Chan1: The transferee, Chan2: The transferer */
+ struct sip_dual *d;
+ struct sip_request req;
+ int ext;
+ int res;
+
+ d = stuff;
+ transferee = d->chan1;
+ transferer = d->chan2;
+ copy_request(&req, &d->req);
+ ast_free(d);
+
+ if (!transferee || !transferer) {
+ ast_log(LOG_ERROR, "Missing channels for parking! Transferer %s Transferee %s\n", transferer ? "<available>" : "<missing>", transferee ? "<available>" : "<missing>" );
+ return NULL;
+ }
+ ast_debug(4, "SIP Park: Transferer channel %s, Transferee %s\n", transferer->name, transferee->name);
+
+ ast_channel_lock(transferee);
+ if (ast_do_masquerade(transferee)) {
+ ast_log(LOG_WARNING, "Masquerade failed.\n");
+ transmit_response(transferer->tech_pvt, "503 Internal error", &req);
+ ast_channel_unlock(transferee);
+ return NULL;
+ }
+ ast_channel_unlock(transferee);
+
+ res = ast_park_call(transferee, transferer, 0, &ext);
+
+
+#ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
+ if (!res) {
+ transmit_message_with_text(transferer->tech_pvt, "Unable to park call.\n");
+ } else {
+ /* Then tell the transferer what happened */
+ sprintf(buf, "Call parked on extension '%d'", ext);
+ transmit_message_with_text(transferer->tech_pvt, buf);
+ }
+#endif
+
+ /* Any way back to the current call??? */
+ /* Transmit response to the REFER request */
+ transmit_response(transferer->tech_pvt, "202 Accepted", &req);
+ if (!res) {
+ /* Transfer succeeded */
+ append_history(transferer->tech_pvt, "SIPpark","Parked call on %d", ext);
+ transmit_notify_with_sipfrag(transferer->tech_pvt, d->seqno, "200 OK", TRUE);
+ transferer->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ ast_hangup(transferer); /* This will cause a BYE */
+ ast_debug(1, "SIP Call parked on extension '%d'\n", ext);
+ } else {
+ transmit_notify_with_sipfrag(transferer->tech_pvt, d->seqno, "503 Service Unavailable", TRUE);
+ append_history(transferer->tech_pvt, "SIPpark","Parking failed\n");
+ ast_debug(1, "SIP Call parked failed \n");
+ /* Do not hangup call */
+ }
+ return NULL;
+}
+
+/*! \brief Park a call using the subsystem in res_features.c
+ This is executed in a separate thread
+*/
+static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno)
+{
+ struct sip_dual *d;
+ struct ast_channel *transferee, *transferer;
+ /* Chan2m: The transferer, chan1m: The transferee */
+ pthread_t th;
+
+ transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name);
+ transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "SIPPeer/%s", chan2->name);
+ if ((!transferer) || (!transferee)) {
+ if (transferee) {
+ transferee->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(transferee);
+ }
+ if (transferer) {
+ transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(transferer);
+ }
+ return -1;
+ }
+
+ /* Make formats okay */
+ transferee->readformat = chan1->readformat;
+ transferee->writeformat = chan1->writeformat;
+
+ /* Prepare for taking over the channel */
+ ast_channel_masquerade(transferee, chan1);
+
+ /* Setup the extensions and such */
+ ast_copy_string(transferee->context, chan1->context, sizeof(transferee->context));
+ ast_copy_string(transferee->exten, chan1->exten, sizeof(transferee->exten));
+ transferee->priority = chan1->priority;
+
+ /* We make a clone of the peer channel too, so we can play
+ back the announcement */
+
+ /* Make formats okay */
+ transferer->readformat = chan2->readformat;
+ transferer->writeformat = chan2->writeformat;
+
+ /* Prepare for taking over the channel. Go ahead and grab this channel
+ * lock here to avoid a deadlock with callbacks into the channel driver
+ * that hold the channel lock and want the pvt lock. */
+ while (ast_channel_trylock(chan2)) {
+ struct sip_pvt *pvt = chan2->tech_pvt;
+ sip_pvt_unlock(pvt);
+ usleep(1);
+ sip_pvt_lock(pvt);
+ }
+ ast_channel_masquerade(transferer, chan2);
+ ast_channel_unlock(chan2);
+
+ /* Setup the extensions and such */
+ ast_copy_string(transferer->context, chan2->context, sizeof(transferer->context));
+ ast_copy_string(transferer->exten, chan2->exten, sizeof(transferer->exten));
+ transferer->priority = chan2->priority;
+
+ ast_channel_lock(transferer);
+ if (ast_do_masquerade(transferer)) {
+ ast_log(LOG_WARNING, "Masquerade failed :(\n");
+ ast_channel_unlock(transferer);
+ transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ ast_hangup(transferer);
+ return -1;
+ }
+ ast_channel_unlock(transferer);
+ if (!transferer || !transferee) {
+ if (!transferer) {
+ ast_debug(1, "No transferer channel, giving up parking\n");
+ }
+ if (!transferee) {
+ ast_debug(1, "No transferee channel, giving up parking\n");
+ }
+ return -1;
+ }
+ if ((d = ast_calloc(1, sizeof(*d)))) {
+
+ /* Save original request for followup */
+ copy_request(&d->req, req);
+ d->chan1 = transferee; /* Transferee */
+ d->chan2 = transferer; /* Transferer */
+ d->seqno = seqno;
+ if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) {
+ /* Could not start thread */
+ ast_free(d); /* We don't need it anymore. If thread is created, d will be free'd
+ by sip_park_thread() */
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/*! \brief Turn off generator data
+ XXX Does this function belong in the SIP channel?
+*/
+static void ast_quiet_chan(struct ast_channel *chan)
+{
+ if (chan && chan->_state == AST_STATE_UP) {
+ if (chan->generatordata)
+ ast_deactivate_generator(chan);
+ }
+}
+
+/*! \brief Attempt transfer of SIP call
+ This fix for attended transfers on a local PBX */
+static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target)
+{
+ int res = 0;
+ struct ast_channel *peera = NULL,
+ *peerb = NULL,
+ *peerc = NULL,
+ *peerd = NULL;
+
+
+ /* We will try to connect the transferee with the target and hangup
+ all channels to the transferer */
+ ast_debug(4, "Sip transfer:--------------------\n");
+ if (transferer->chan1)
+ ast_debug(4, "-- Transferer to PBX channel: %s State %s\n", transferer->chan1->name, ast_state2str(transferer->chan1->_state));
+ else
+ ast_debug(4, "-- No transferer first channel - odd??? \n");
+ if (target->chan1)
+ ast_debug(4, "-- Transferer to PBX second channel (target): %s State %s\n", target->chan1->name, ast_state2str(target->chan1->_state));
+ else
+ ast_debug(4, "-- No target first channel ---\n");
+ if (transferer->chan2)
+ ast_debug(4, "-- Bridged call to transferee: %s State %s\n", transferer->chan2->name, ast_state2str(transferer->chan2->_state));
+ else
+ ast_debug(4, "-- No bridged call to transferee\n");
+ if (target->chan2)
+ ast_debug(4, "-- Bridged call to transfer target: %s State %s\n", target->chan2 ? target->chan2->name : "<none>", target->chan2 ? ast_state2str(target->chan2->_state) : "(none)");
+ else
+ ast_debug(4, "-- No target second channel ---\n");
+ ast_debug(4, "-- END Sip transfer:--------------------\n");
+ if (transferer->chan2) { /* We have a bridge on the transferer's channel */
+ peera = transferer->chan1; /* Transferer - PBX -> transferee channel * the one we hangup */
+ peerb = target->chan1; /* Transferer - PBX -> target channel - This will get lost in masq */
+ peerc = transferer->chan2; /* Asterisk to Transferee */
+ peerd = target->chan2; /* Asterisk to Target */
+ ast_debug(3, "SIP transfer: Four channels to handle\n");
+ } else if (target->chan2) { /* Transferer has no bridge (IVR), but transferee */
+ peera = target->chan1; /* Transferer to PBX -> target channel */
+ peerb = transferer->chan1; /* Transferer to IVR*/
+ peerc = target->chan2; /* Asterisk to Target */
+ peerd = transferer->chan2; /* Nothing */
+ ast_debug(3, "SIP transfer: Three channels to handle\n");
+ }
+
+ if (peera && peerb && peerc && (peerb != peerc)) {
+ ast_quiet_chan(peera); /* Stop generators */
+ ast_quiet_chan(peerb);
+ ast_quiet_chan(peerc);
+ if (peerd)
+ ast_quiet_chan(peerd);
+
+ /* Fix CDRs so they're attached to the remaining channel */
+ if (peera->cdr && peerb->cdr)
+ peerb->cdr = ast_cdr_append(peerb->cdr, peera->cdr);
+ else if (peera->cdr)
+ peerb->cdr = peera->cdr;
+ peera->cdr = NULL;
+
+ if (peerb->cdr && peerc->cdr)
+ peerb->cdr = ast_cdr_append(peerb->cdr, peerc->cdr);
+ else if (peerc->cdr)
+ peerb->cdr = peerc->cdr;
+ peerc->cdr = NULL;
+
+ ast_debug(4, "SIP transfer: trying to masquerade %s into %s\n", peerc->name, peerb->name);
+ if (ast_channel_masquerade(peerb, peerc)) {
+ ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name);
+ res = -1;
+ } else
+ ast_debug(4, "SIP transfer: Succeeded to masquerade channels.\n");
+ return res;
+ } else {
+ ast_log(LOG_NOTICE, "SIP Transfer attempted with no appropriate bridged calls to transfer\n");
+ if (transferer->chan1)
+ ast_softhangup_nolock(transferer->chan1, AST_SOFTHANGUP_DEV);
+ if (target->chan1)
+ ast_softhangup_nolock(target->chan1, AST_SOFTHANGUP_DEV);
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief Get tag from packet
+ *
+ * \return Returns the pointer to the provided tag buffer,
+ * or NULL if the tag was not found.
+ */
+static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize)
+{
+ const char *thetag;
+
+ if (!tagbuf)
+ return NULL;
+ tagbuf[0] = '\0'; /* reset the buffer */
+ thetag = get_header(req, header);
+ thetag = strcasestr(thetag, ";tag=");
+ if (thetag) {
+ thetag += 5;
+ ast_copy_string(tagbuf, thetag, tagbufsize);
+ return strsep(&tagbuf, ";");
+ }
+ return NULL;
+}
+
+/*! \brief Handle incoming notifications */
+static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e)
+{
+ /* This is mostly a skeleton for future improvements */
+ /* Mostly created to return proper answers on notifications on outbound REFER's */
+ int res = 0;
+ const char *event = get_header(req, "Event");
+ char *eventid = NULL;
+ char *sep;
+
+ if( (sep = strchr(event, ';')) ) { /* XXX bug here - overwriting string ? */
+ *sep++ = '\0';
+ eventid = sep;
+ }
+
+ if (sipdebug)
+ ast_debug(2, "Got NOTIFY Event: %s\n", event);
+
+ if (strcmp(event, "refer")) {
+ /* We don't understand this event. */
+ /* Here's room to implement incoming voicemail notifications :-) */
+ transmit_response(p, "489 Bad event", req);
+ res = -1;
+ } else {
+ /* Save nesting depth for now, since there might be other events we will
+ support in the future */
+
+ /* Handle REFER notifications */
+
+ char buf[1024];
+ char *cmd, *code;
+ int respcode;
+ int success = TRUE;
+
+ /* EventID for each transfer... EventID is basically the REFER cseq
+
+ We are getting notifications on a call that we transfered
+ We should hangup when we are getting a 200 OK in a sipfrag
+ Check if we have an owner of this event */
+
+ /* Check the content type */
+ if (strncasecmp(get_header(req, "Content-Type"), "message/sipfrag", strlen("message/sipfrag"))) {
+ /* We need a sipfrag */
+ transmit_response(p, "400 Bad request", req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return -1;
+ }
+
+ /* Get the text of the attachment */
+ if (get_msg_text(buf, sizeof(buf), req)) {
+ ast_log(LOG_WARNING, "Unable to retrieve attachment from NOTIFY %s\n", p->callid);
+ transmit_response(p, "400 Bad request", req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return -1;
+ }
+
+ /*
+ From the RFC...
+ A minimal, but complete, implementation can respond with a single
+ NOTIFY containing either the body:
+ SIP/2.0 100 Trying
+
+ if the subscription is pending, the body:
+ SIP/2.0 200 OK
+ if the reference was successful, the body:
+ SIP/2.0 503 Service Unavailable
+ if the reference failed, or the body:
+ SIP/2.0 603 Declined
+
+ if the REFER request was accepted before approval to follow the
+ reference could be obtained and that approval was subsequently denied
+ (see Section 2.4.7).
+
+ If there are several REFERs in the same dialog, we need to
+ match the ID of the event header...
+ */
+ ast_debug(3, "* SIP Transfer NOTIFY Attachment: \n---%s\n---\n", buf);
+ cmd = ast_skip_blanks(buf);
+ code = cmd;
+ /* We are at SIP/2.0 */
+ while(*code && (*code > 32)) { /* Search white space */
+ code++;
+ }
+ *code++ = '\0';
+ code = ast_skip_blanks(code);
+ sep = code;
+ sep++;
+ while(*sep && (*sep > 32)) { /* Search white space */
+ sep++;
+ }
+ *sep++ = '\0'; /* Response string */
+ respcode = atoi(code);
+ switch (respcode) {
+ case 100: /* Trying: */
+ case 101: /* dialog establishment */
+ /* Don't do anything yet */
+ break;
+ case 183: /* Ringing: */
+ /* Don't do anything yet */
+ break;
+ case 200: /* OK: The new call is up, hangup this call */
+ /* Hangup the call that we are replacing */
+ break;
+ case 301: /* Moved permenantly */
+ case 302: /* Moved temporarily */
+ /* Do we get the header in the packet in this case? */
+ success = FALSE;
+ break;
+ case 503: /* Service Unavailable: The new call failed */
+ /* Cancel transfer, continue the call */
+ success = FALSE;
+ break;
+ case 603: /* Declined: Not accepted */
+ /* Cancel transfer, continue the current call */
+ success = FALSE;
+ break;
+ }
+ if (!success) {
+ ast_log(LOG_NOTICE, "Transfer failed. Sorry. Nothing further to do with this call\n");
+ }
+
+ /* Confirm that we received this packet */
+ transmit_response(p, "200 OK", req);
+ };
+
+ if (!p->lastinvite)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+
+ return res;
+}
+
+/*! \brief Handle incoming OPTIONS request
+ An OPTIONS request should be answered like an INVITE from the same UA, including SDP
+*/
+static int handle_request_options(struct sip_pvt *p, struct sip_request *req)
+{
+ int res;
+
+ /*! XXX get_destination assumes we're already authenticated. This means that a request from
+ a known device (peer/user) will end up in the wrong context if this is out-of-dialog.
+ However, we want to handle OPTIONS as light as possible, so we might want to have
+ a configuration option whether we care or not. Some devices use this for testing
+ capabilities, which means that we need to match device to answer with proper
+ capabilities (including SDP).
+ \todo Fix handle_request_options device handling with optional authentication
+ (this needs to be fixed in 1.4 as well)
+ */
+ res = get_destination(p, req);
+ build_contact(p);
+
+ if (ast_strlen_zero(p->context))
+ ast_string_field_set(p, context, default_context);
+
+ if (ast_shutting_down())
+ transmit_response_with_allow(p, "503 Unavailable", req, 0);
+ else if (res < 0)
+ transmit_response_with_allow(p, "404 Not Found", req, 0);
+ else
+ transmit_response_with_allow(p, "200 OK", req, 0);
+
+ /* Destroy if this OPTIONS was the opening request, but not if
+ it's in the middle of a normal call flow. */
+ if (!p->lastinvite)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+
+ return res;
+}
+
+/*! \brief Handle the transfer part of INVITE with a replaces: header,
+ meaning a target pickup or an attended transfer.
+ Used only once.
+ XXX 'ignore' is unused.
+ */
+static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin)
+{
+ struct ast_frame *f;
+ int earlyreplace = 0;
+ int oneleggedreplace = 0; /* Call with no bridge, propably IVR or voice message */
+ struct ast_channel *c = p->owner; /* Our incoming call */
+ struct ast_channel *replacecall = p->refer->refer_call->owner; /* The channel we're about to take over */
+ struct ast_channel *targetcall; /* The bridge to the take-over target */
+
+ struct ast_channel *test;
+
+ /* Check if we're in ring state */
+ if (replacecall->_state == AST_STATE_RING)
+ earlyreplace = 1;
+
+ /* Check if we have a bridge */
+ if (!(targetcall = ast_bridged_channel(replacecall))) {
+ /* We have no bridge */
+ if (!earlyreplace) {
+ ast_debug(2, " Attended transfer attempted to replace call with no bridge (maybe ringing). Channel %s!\n", replacecall->name);
+ oneleggedreplace = 1;
+ }
+ }
+ if (targetcall && targetcall->_state == AST_STATE_RINGING)
+ ast_debug(4, "SIP transfer: Target channel is in ringing state\n");
+
+ if (targetcall)
+ ast_debug(4, "SIP transfer: Invite Replace incoming channel should bridge to channel %s while hanging up channel %s\n", targetcall->name, replacecall->name);
+ else
+ ast_debug(4, "SIP transfer: Invite Replace incoming channel should replace and hang up channel %s (one call leg)\n", replacecall->name);
+
+ if (req->ignore) {
+ ast_log(LOG_NOTICE, "Ignoring this INVITE with replaces in a stupid way.\n");
+ /* We should answer something here. If we are here, the
+ call we are replacing exists, so an accepted
+ can't harm */
+ transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE);
+ /* Do something more clever here */
+ ast_channel_unlock(c);
+ sip_pvt_unlock(p->refer->refer_call);
+ return 1;
+ }
+ if (!c) {
+ /* What to do if no channel ??? */
+ ast_log(LOG_ERROR, "Unable to create new channel. Invite/replace failed.\n");
+ transmit_response_reliable(p, "503 Service Unavailable", req);
+ append_history(p, "Xfer", "INVITE/Replace Failed. No new channel.");
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ sip_pvt_unlock(p->refer->refer_call);
+ return 1;
+ }
+ append_history(p, "Xfer", "INVITE/Replace received");
+ /* We have three channels to play with
+ channel c: New incoming call
+ targetcall: Call from PBX to target
+ p->refer->refer_call: SIP pvt dialog from transferer to pbx.
+ replacecall: The owner of the previous
+ We need to masq C into refer_call to connect to
+ targetcall;
+ If we are talking to internal audio stream, target call is null.
+ */
+
+ /* Fake call progress */
+ transmit_response(p, "100 Trying", req);
+ ast_setstate(c, AST_STATE_RING);
+
+ /* Masquerade the new call into the referred call to connect to target call
+ Targetcall is not touched by the masq */
+
+ /* Answer the incoming call and set channel to UP state */
+ transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE);
+
+ ast_setstate(c, AST_STATE_UP);
+
+ /* Stop music on hold and other generators */
+ ast_quiet_chan(replacecall);
+ ast_quiet_chan(targetcall);
+ ast_debug(4, "Invite/Replaces: preparing to masquerade %s into %s\n", c->name, replacecall->name);
+ /* Unlock clone, but not original (replacecall) */
+ if (!oneleggedreplace)
+ ast_channel_unlock(c);
+
+ /* Unlock PVT */
+ sip_pvt_unlock(p->refer->refer_call);
+
+ /* Make sure that the masq does not free our PVT for the old call */
+ if (! earlyreplace && ! oneleggedreplace )
+ ast_set_flag(&p->refer->refer_call->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
+
+ /* Prepare the masquerade - if this does not happen, we will be gone */
+ if(ast_channel_masquerade(replacecall, c))
+ ast_log(LOG_ERROR, "Failed to masquerade C into Replacecall\n");
+ else
+ ast_debug(4, "Invite/Replaces: Going to masquerade %s into %s\n", c->name, replacecall->name);
+
+ /* The masquerade will happen as soon as someone reads a frame from the channel */
+
+ /* C should now be in place of replacecall */
+ /* ast_read needs to lock channel */
+ ast_channel_unlock(c);
+
+ if (earlyreplace || oneleggedreplace ) {
+ /* Force the masq to happen */
+ if ((f = ast_read(replacecall))) { /* Force the masq to happen */
+ ast_frfree(f);
+ f = NULL;
+ ast_debug(4, "Invite/Replace: Could successfully read frame from RING channel!\n");
+ } else {
+ ast_log(LOG_WARNING, "Invite/Replace: Could not read frame from RING channel \n");
+ }
+ c->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ if (!oneleggedreplace)
+ ast_channel_unlock(replacecall);
+ } else { /* Bridged call, UP channel */
+ if ((f = ast_read(replacecall))) { /* Force the masq to happen */
+ /* Masq ok */
+ ast_frfree(f);
+ f = NULL;
+ ast_debug(3, "Invite/Replace: Could successfully read frame from channel! Masq done.\n");
+ } else {
+ ast_log(LOG_WARNING, "Invite/Replace: Could not read frame from channel. Transfer failed\n");
+ }
+ ast_channel_unlock(replacecall);
+ }
+ sip_pvt_unlock(p->refer->refer_call);
+
+ ast_setstate(c, AST_STATE_DOWN);
+ ast_debug(4, "After transfer:----------------------------\n");
+ ast_debug(4, " -- C: %s State %s\n", c->name, ast_state2str(c->_state));
+ if (replacecall)
+ ast_debug(4, " -- replacecall: %s State %s\n", replacecall->name, ast_state2str(replacecall->_state));
+ if (p->owner) {
+ ast_debug(4, " -- P->owner: %s State %s\n", p->owner->name, ast_state2str(p->owner->_state));
+ test = ast_bridged_channel(p->owner);
+ if (test)
+ ast_debug(4, " -- Call bridged to P->owner: %s State %s\n", test->name, ast_state2str(test->_state));
+ else
+ ast_debug(4, " -- No call bridged to C->owner \n");
+ } else
+ ast_debug(4, " -- No channel yet \n");
+ ast_debug(4, "End After transfer:----------------------------\n");
+
+ ast_channel_unlock(p->owner); /* Unlock new owner */
+ if (!oneleggedreplace)
+ sip_pvt_unlock(p); /* Unlock SIP structure */
+
+ /* The call should be down with no ast_channel, so hang it up */
+ c->tech_pvt = dialog_unref(c->tech_pvt);
+ ast_hangup(c);
+ return 0;
+}
+
+
+/*! \brief Handle incoming INVITE request
+\note If the INVITE has a Replaces header, it is part of an
+ * attended transfer. If so, we do not go through the dial
+ * plan but tries to find the active call and masquerade
+ * into it
+ */
+static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock)
+{
+ int res = 1;
+ int gotdest;
+ const char *p_replaces;
+ char *replace_id = NULL;
+ const char *required;
+ unsigned int required_profile = 0;
+ struct ast_channel *c = NULL; /* New channel */
+ int reinvite = 0;
+ int rtn;
+
+ const char *p_uac_se_hdr; /* UAC's Session-Expires header string */
+ const char *p_uac_min_se; /* UAC's requested Min-SE interval (char string) */
+ int uac_max_se = -1; /* UAC's Session-Expires in integer format */
+ int uac_min_se = -1; /* UAC's Min-SE in integer format */
+ int st_active = FALSE; /* Session-Timer on/off boolean */
+ int st_interval = 0; /* Session-Timer negotiated refresh interval */
+ enum st_refresher st_ref; /* Session-Timer session refresher */
+ int dlg_min_se = -1;
+ st_ref = SESSION_TIMER_REFRESHER_AUTO;
+
+ /* Find out what they support */
+ if (!p->sipoptions) {
+ const char *supported = get_header(req, "Supported");
+ if (!ast_strlen_zero(supported))
+ parse_sip_options(p, supported);
+ }
+
+ /* Find out what they require */
+ required = get_header(req, "Require");
+ if (!ast_strlen_zero(required)) {
+ required_profile = parse_sip_options(NULL, required);
+ if (required_profile && required_profile != SIP_OPT_REPLACES && required_profile != SIP_OPT_TIMER) {
+ /* At this point we only support REPLACES and Session-Timer */
+ transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, required);
+ ast_log(LOG_WARNING,"Received SIP INVITE with unsupported required extension: %s\n", required);
+ p->invitestate = INV_COMPLETED;
+ if (!p->lastinvite)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return -1;
+ }
+ }
+
+ /* The option tags may be present in Supported: or Require: headers.
+ Include the Require: option tags for further processing as well */
+ p->sipoptions |= required_profile;
+ p->reqsipoptions = required_profile;
+
+ /* Check if this is a loop */
+ if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->owner->_state != AST_STATE_UP)) {
+ /* This is a call to ourself. Send ourselves an error code and stop
+ processing immediately, as SIP really has no good mechanism for
+ being able to call yourself */
+ /* If pedantic is on, we need to check the tags. If they're different, this is
+ in fact a forked call through a SIP proxy somewhere. */
+ transmit_response(p, "482 Loop Detected", req);
+ p->invitestate = INV_COMPLETED;
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return 0;
+ }
+
+ if (!req->ignore && p->pendinginvite) {
+ /* We already have a pending invite. Sorry. You are on hold. */
+ transmit_response(p, "491 Request Pending", req);
+ ast_debug(1, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
+ /* Don't destroy dialog here */
+ return 0;
+ }
+
+ p_replaces = get_header(req, "Replaces");
+ if (!ast_strlen_zero(p_replaces)) {
+ /* We have a replaces header */
+ char *ptr;
+ char *fromtag = NULL;
+ char *totag = NULL;
+ char *start, *to;
+ int error = 0;
+
+ if (p->owner) {
+ ast_debug(3, "INVITE w Replaces on existing call? Refusing action. [%s]\n", p->callid);
+ transmit_response(p, "400 Bad request", req); /* The best way to not not accept the transfer */
+ /* Do not destroy existing call */
+ return -1;
+ }
+
+ if (sipdebug)
+ ast_debug(3, "INVITE part of call transfer. Replaces [%s]\n", p_replaces);
+ /* Create a buffer we can manipulate */
+ replace_id = ast_strdupa(p_replaces);
+ ast_uri_decode(replace_id);
+
+ if (!p->refer && !sip_refer_allocate(p)) {
+ transmit_response(p, "500 Server Internal Error", req);
+ append_history(p, "Xfer", "INVITE/Replace Failed. Out of memory.");
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ p->invitestate = INV_COMPLETED;
+ return -1;
+ }
+
+ /* Todo: (When we find phones that support this)
+ if the replaces header contains ";early-only"
+ we can only replace the call in early
+ stage, not after it's up.
+
+ If it's not in early mode, 486 Busy.
+ */
+
+ /* Skip leading whitespace */
+ replace_id = ast_skip_blanks(replace_id);
+
+ start = replace_id;
+ while ( (ptr = strsep(&start, ";")) ) {
+ ptr = ast_skip_blanks(ptr); /* XXX maybe unnecessary ? */
+ if ( (to = strcasestr(ptr, "to-tag=") ) )
+ totag = to + 7; /* skip the keyword */
+ else if ( (to = strcasestr(ptr, "from-tag=") ) ) {
+ fromtag = to + 9; /* skip the keyword */
+ fromtag = strsep(&fromtag, "&"); /* trim what ? */
+ }
+ }
+
+ if (sipdebug)
+ ast_debug(4,"Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n", replace_id, fromtag ? fromtag : "<no from tag>", totag ? totag : "<no to tag>");
+
+
+ /* Try to find call that we are replacing
+ If we have a Replaces header, we need to cancel that call if we succeed with this call
+ */
+ if ((p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
+ ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
+ transmit_response(p, "481 Call Leg Does Not Exist (Replaces)", req);
+ error = 1;
+ }
+
+ /* At this point, bot the pvt and the owner of the call to be replaced is locked */
+
+ /* The matched call is the call from the transferer to Asterisk .
+ We want to bridge the bridged part of the call to the
+ incoming invite, thus taking over the refered call */
+
+ if (p->refer->refer_call == p) {
+ ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid);
+ p->refer->refer_call = dialog_unref(p->refer->refer_call);
+ transmit_response(p, "400 Bad request", req); /* The best way to not not accept the transfer */
+ error = 1;
+ }
+
+ if (!error && !p->refer->refer_call->owner) {
+ /* Oops, someting wrong anyway, no owner, no call */
+ ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
+ /* Check for better return code */
+ transmit_response(p, "481 Call Leg Does Not Exist (Replace)", req);
+ error = 1;
+ }
+
+ if (!error && p->refer->refer_call->owner->_state != AST_STATE_RINGING && p->refer->refer_call->owner->_state != AST_STATE_RING && p->refer->refer_call->owner->_state != AST_STATE_UP ) {
+ ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
+ transmit_response(p, "603 Declined (Replaces)", req);
+ error = 1;
+ }
+
+ if (error) { /* Give up this dialog */
+ append_history(p, "Xfer", "INVITE/Replace Failed.");
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ sip_pvt_unlock(p);
+ if (p->refer->refer_call) {
+ sip_pvt_unlock(p->refer->refer_call);
+ ast_channel_unlock(p->refer->refer_call->owner);
+ }
+ p->invitestate = INV_COMPLETED;
+ return -1;
+ }
+ }
+
+ /* Check if this is an INVITE that sets up a new dialog or
+ a re-invite in an existing dialog */
+
+ if (!req->ignore) {
+ int newcall = (p->initreq.headers ? TRUE : FALSE);
+
+ sip_cancel_destroy(p);
+ /* This also counts as a pending invite */
+ p->pendinginvite = seqno;
+ check_via(p, req);
+
+ copy_request(&p->initreq, req); /* Save this INVITE as the transaction basis */
+ if (sipdebug)
+ ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
+ if (!p->owner) { /* Not a re-invite */
+ if (debug)
+ ast_verbose("Using INVITE request as basis request - %s\n", p->callid);
+ if (newcall)
+ append_history(p, "Invite", "New call: %s", p->callid);
+ parse_ok_contact(p, req);
+ } else { /* Re-invite on existing call */
+ ast_clear_flag(&p->flags[0], SIP_OUTGOING); /* This is now an inbound dialog */
+ /* Handle SDP here if we already have an owner */
+ if (find_sdp(req)) {
+ if (process_sdp(p, req)) {
+ transmit_response(p, "488 Not acceptable here", req);
+ if (!p->lastinvite)
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return -1;
+ }
+ } else {
+ p->jointcapability = p->capability;
+ ast_debug(1, "Hm.... No sdp for the moment\n");
+ }
+ if (p->do_history) /* This is a response, note what it was for */
+ append_history(p, "ReInv", "Re-invite received");
+ }
+ } else if (debug)
+ ast_verbose("Ignoring this INVITE request\n");
+
+
+ if (!p->lastinvite && !req->ignore && !p->owner) {
+ /* This is a new invite */
+ /* Handle authentication if this is our first invite */
+ res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);
+ if (res == AUTH_CHALLENGE_SENT) {
+ p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */
+ return 0;
+ }
+ if (res < 0) { /* Something failed in authentication */
+ if (res == AUTH_FAKE_AUTH) {
+ ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
+ transmit_fake_auth_response(p, req, 1);
+ } else {
+ ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From"));
+ transmit_response_reliable(p, "403 Forbidden", req);
+ }
+ p->invitestate = INV_COMPLETED;
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ ast_string_field_set(p, theirtag, NULL);
+ return 0;
+ }
+
+ /* We have a succesful authentication, process the SDP portion if there is one */
+ if (find_sdp(req)) {
+ if (process_sdp(p, req)) {
+ /* Unacceptable codecs */
+ transmit_response_reliable(p, "488 Not acceptable here", req);
+ p->invitestate = INV_COMPLETED;
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ ast_debug(1, "No compatible codecs for this SIP call.\n");
+ return -1;
+ }
+ } else { /* No SDP in invite, call control session */
+ p->jointcapability = p->capability;
+ ast_debug(2, "No SDP in Invite, third party call control\n");
+ }
+
+ /* Queue NULL frame to prod ast_rtp_bridge if appropriate */
+ /* This seems redundant ... see !p-owner above */
+ if (p->owner)
+ ast_queue_frame(p->owner, &ast_null_frame);
+
+
+ /* Initialize the context if it hasn't been already */
+ if (ast_strlen_zero(p->context))
+ ast_string_field_set(p, context, default_context);
+
+
+ /* Check number of concurrent calls -vs- incoming limit HERE */
+ ast_debug(1, "Checking SIP call limits for device %s\n", p->username);
+ if ((res = update_call_counter(p, INC_CALL_LIMIT))) {
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "Failed to place call for user %s, too many calls\n", p->username);
+ transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ p->invitestate = INV_COMPLETED;
+ }
+ return 0;
+ }
+ gotdest = get_destination(p, NULL); /* Get destination right away */
+ get_rdnis(p, NULL); /* Get redirect information */
+ extract_uri(p, req); /* Get the Contact URI */
+ build_contact(p); /* Build our contact header */
+
+ if (p->rtp) {
+ ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
+ ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
+ }
+
+ if (!replace_id && gotdest) { /* No matching extension found */
+ if (gotdest == 1 && ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP))
+ transmit_response_reliable(p, "484 Address Incomplete", req);
+ else {
+ transmit_response_reliable(p, "404 Not Found", req);
+ ast_log(LOG_NOTICE, "Call from '%s' to extension"
+ " '%s' rejected because extension not found.\n",
+ S_OR(p->username, p->peername), p->exten);
+ }
+ p->invitestate = INV_COMPLETED;
+ update_call_counter(p, DEC_CALL_LIMIT);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return 0;
+ } else {
+
+ /* If no extension was specified, use the s one */
+ /* Basically for calling to IP/Host name only */
+ if (ast_strlen_zero(p->exten))
+ ast_string_field_set(p, exten, "s");
+ /* Initialize our tag */
+
+ make_our_tag(p->tag, sizeof(p->tag));
+ /* First invitation - create the channel */
+ c = sip_new(p, AST_STATE_DOWN, S_OR(p->username, NULL));
+ *recount = 1;
+
+ /* Save Record-Route for any later requests we make on this dialogue */
+ build_route(p, req, 0);
+
+ if (c) {
+ /* Pre-lock the call */
+ ast_channel_lock(c);
+ }
+ }
+ } else {
+ if (sipdebug) {
+ if (!req->ignore)
+ ast_debug(2, "Got a SIP re-invite for call %s\n", p->callid);
+ else
+ ast_debug(2, "Got a SIP re-transmit of INVITE for call %s\n", p->callid);
+ }
+
+ reinvite = 1;
+ c = p->owner;
+ }
+
+ /* Session-Timers */
+ if (p->sipoptions == SIP_OPT_TIMER) {
+ /* The UAC has requested session-timers for this session. Negotiate
+ the session refresh interval and who will be the refresher */
+ ast_debug(2, "Incoming INVITE with 'timer' option enabled\n");
+
+ /* Allocate Session-Timers struct w/in the dialog */
+ if (!p->stimer)
+ sip_st_alloc(p);
+
+ /* Parse the Session-Expires header */
+ p_uac_se_hdr = get_header(req, "Session-Expires");
+ if (!ast_strlen_zero(p_uac_se_hdr)) {
+ rtn = parse_session_expires(p_uac_se_hdr, &uac_max_se, &st_ref);
+ if (rtn != 0) {
+ transmit_response(p, "400 Session-Expires Invalid Syntax", req);
+ p->invitestate = INV_COMPLETED;
+ if (!p->lastinvite) {
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ return -1;
+ }
+ }
+
+ /* Parse the Min-SE header */
+ p_uac_min_se = get_header(req, "Min-SE");
+ if (!ast_strlen_zero(p_uac_min_se)) {
+ rtn = parse_minse(p_uac_min_se, &uac_min_se);
+ if (rtn != 0) {
+ transmit_response(p, "400 Min-SE Invalid Syntax", req);
+ p->invitestate = INV_COMPLETED;
+ if (!p->lastinvite) {
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ return -1;
+ }
+ }
+
+ dlg_min_se = st_get_se(p, FALSE);
+ switch (st_get_mode(p)) {
+ case SESSION_TIMER_MODE_ACCEPT:
+ case SESSION_TIMER_MODE_ORIGINATE:
+ if (uac_max_se > 0 && uac_max_se < dlg_min_se) {
+ transmit_response_with_minse(p, "422 Session Interval Too Small", req, dlg_min_se);
+ p->invitestate = INV_COMPLETED;
+ if (!p->lastinvite) {
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ return -1;
+ }
+
+ p->stimer->st_active_peer_ua = TRUE;
+ st_active = TRUE;
+ if (st_ref == SESSION_TIMER_REFRESHER_AUTO) {
+ st_ref = st_get_refresher(p);
+ }
+
+ if (uac_max_se > 0) {
+ int dlg_max_se = st_get_se(p, TRUE);
+ if (dlg_max_se >= uac_min_se) {
+ st_interval = (uac_max_se < dlg_max_se) ? uac_max_se : dlg_max_se;
+ } else {
+ st_interval = uac_max_se;
+ }
+ } else {
+ st_interval = uac_min_se;
+ }
+ break;
+
+ case SESSION_TIMER_MODE_REFUSE:
+ if (p->reqsipoptions == SIP_OPT_TIMER) {
+ transmit_response_with_unsupported(p, "420 Option Disabled", req, required);
+ ast_log(LOG_WARNING,"Received SIP INVITE with supported but disabled option: %s\n", required);
+ p->invitestate = INV_COMPLETED;
+ if (!p->lastinvite) {
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ return -1;
+ }
+ break;
+
+ default:
+ ast_log(LOG_ERROR, "Internal Error %d at %s:%d\n", st_get_mode(p), __FILE__, __LINE__);
+ break;
+ }
+ } else {
+ /* The UAC did not request session-timers. Asterisk (UAS), will now decide
+ (based on session-timer-mode in sip.conf) whether to run session-timers for
+ this session or not. */
+ switch (st_get_mode(p)) {
+ case SESSION_TIMER_MODE_ORIGINATE:
+ st_active = TRUE;
+ st_interval = st_get_se(p, TRUE);
+ st_ref = SESSION_TIMER_REFRESHER_UAS;
+ p->stimer->st_active_peer_ua = FALSE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (reinvite == 0) {
+ /* Session-Timers: Start session refresh timer based on negotiation/config */
+ if (st_active == TRUE) {
+ p->stimer->st_active = TRUE;
+ p->stimer->st_interval = st_interval;
+ p->stimer->st_ref = st_ref;
+ start_session_timer(p);
+ }
+ } else {
+ if (p->stimer->st_active == TRUE) {
+ /* Session-Timers: A re-invite request sent within a dialog will serve as
+ a refresh request, no matter whether the re-invite was sent for refreshing
+ the session or modifying it.*/
+ ast_debug (2, "Restarting session-timers on a refresh - %s\n", p->callid);
+
+ /* The UAC may be adjusting the session-timers mid-session */
+ if (st_interval > 0) {
+ p->stimer->st_interval = st_interval;
+ p->stimer->st_ref = st_ref;
+ }
+
+ restart_session_timer(p);
+ if (p->stimer->st_expirys > 0) {
+ p->stimer->st_expirys--;
+ }
+ }
+ }
+
+ if (!req->ignore && p)
+ p->lastinvite = seqno;
+
+ if (replace_id) { /* Attended transfer or call pickup - we're the target */
+ /* Go and take over the target call */
+ if (sipdebug)
+ ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid);
+ return handle_invite_replaces(p, req, debug, seqno, sin);
+ }
+
+
+ if (c) { /* We have a call -either a new call or an old one (RE-INVITE) */
+ switch(c->_state) {
+ case AST_STATE_DOWN:
+ ast_debug(2, "%s: New call is still down.... Trying... \n", c->name);
+ transmit_response(p, "100 Trying", req);
+ p->invitestate = INV_PROCEEDING;
+ ast_setstate(c, AST_STATE_RING);
+ if (strcmp(p->exten, ast_pickup_ext())) { /* Call to extension -start pbx on this call */
+ enum ast_pbx_result res;
+
+ res = ast_pbx_start(c);
+
+ switch(res) {
+ case AST_PBX_FAILED:
+ ast_log(LOG_WARNING, "Failed to start PBX :(\n");
+ p->invitestate = INV_COMPLETED;
+ if (req->ignore)
+ transmit_response(p, "503 Unavailable", req);
+ else
+ transmit_response_reliable(p, "503 Unavailable", req);
+ break;
+ case AST_PBX_CALL_LIMIT:
+ ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
+ p->invitestate = INV_COMPLETED;
+ if (req->ignore)
+ transmit_response(p, "480 Temporarily Unavailable", req);
+ else
+ transmit_response_reliable(p, "480 Temporarily Unavailable", req);
+ break;
+ case AST_PBX_SUCCESS:
+ /* nothing to do */
+ break;
+ }
+
+ if (res) {
+
+ /* Unlock locks so ast_hangup can do its magic */
+ ast_channel_unlock(c);
+ sip_pvt_unlock(p);
+ ast_hangup(c);
+ sip_pvt_lock(p);
+ c = NULL;
+ }
+ } else { /* Pickup call in call group */
+ ast_channel_unlock(c);
+ *nounlock = 1;
+ if (ast_pickup_call(c)) {
+ ast_log(LOG_NOTICE, "Nothing to pick up for %s\n", p->callid);
+ if (req->ignore)
+ transmit_response(p, "503 Unavailable", req); /* OEJ - Right answer? */
+ else
+ transmit_response_reliable(p, "503 Unavailable", req);
+ sip_alreadygone(p);
+ /* Unlock locks so ast_hangup can do its magic */
+ sip_pvt_unlock(p);
+ c->hangupcause = AST_CAUSE_CALL_REJECTED;
+ } else {
+ sip_pvt_unlock(p);
+ ast_setstate(c, AST_STATE_DOWN);
+ c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ }
+ p->invitestate = INV_COMPLETED;
+ ast_hangup(c);
+ sip_pvt_lock(p);
+ c = NULL;
+ }
+ break;
+ case AST_STATE_RING:
+ transmit_response(p, "100 Trying", req);
+ p->invitestate = INV_PROCEEDING;
+ break;
+ case AST_STATE_RINGING:
+ transmit_response(p, "180 Ringing", req);
+ p->invitestate = INV_PROCEEDING;
+ break;
+ case AST_STATE_UP:
+ ast_debug(2, "%s: This call is UP.... \n", c->name);
+
+ transmit_response(p, "100 Trying", req);
+
+ if (p->t38.state == T38_PEER_REINVITE) {
+ struct ast_channel *bridgepeer = NULL;
+ struct sip_pvt *bridgepvt = NULL;
+
+ if ((bridgepeer = ast_bridged_channel(p->owner))) {
+ /* We have a bridge, and this is re-invite to switchover to T38 so we send re-invite with T38 SDP, to other side of bridge*/
+ /*! XXX: we should also check here does the other side supports t38 at all !!! XXX */
+ if (IS_SIP_TECH(bridgepeer->tech)) {
+ bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
+ if (bridgepvt->t38.state == T38_DISABLED) {
+ if (bridgepvt->udptl) { /* If everything is OK with other side's udptl struct */
+ /* Send re-invite to the bridged channel */
+ sip_handle_t38_reinvite(bridgepeer, p, 1);
+ } else { /* Something is wrong with peers udptl struct */
+ ast_log(LOG_WARNING, "Strange... The other side of the bridge don't have udptl struct\n");
+ sip_pvt_lock(bridgepvt);
+ bridgepvt->t38.state = T38_DISABLED;
+ sip_pvt_unlock(bridgepvt);
+ ast_debug(2,"T38 state changed to %d on channel %s\n", bridgepvt->t38.state, bridgepeer->name);
+ if (req->ignore)
+ transmit_response(p, "488 Not acceptable here", req);
+ else
+ transmit_response_reliable(p, "488 Not acceptable here", req);
+
+ }
+ } else {
+ /* The other side is already setup for T.38 most likely so we need to acknowledge this too */
+ transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL);
+ p->t38.state = T38_ENABLED;
+ ast_debug(1, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ }
+ } else {
+ /* Other side is not a SIP channel */
+ if (req->ignore)
+ transmit_response(p, "488 Not acceptable here", req);
+ else
+ transmit_response_reliable(p, "488 Not acceptable here", req);
+ p->t38.state = T38_DISABLED;
+ ast_debug(2,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+
+ if (!p->lastinvite) /* Only destroy if this is *not* a re-invite */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ } else {
+ /* we are not bridged in a call */
+ transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL);
+ p->t38.state = T38_ENABLED;
+ ast_debug(1,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ }
+ } else if (p->t38.state == T38_DISABLED) { /* Channel doesn't have T38 offered or enabled */
+ int sendok = TRUE;
+
+ /* If we are bridged to a channel that has T38 enabled than this is a case of RTP re-invite after T38 session */
+ /* so handle it here (re-invite other party to RTP) */
+ struct ast_channel *bridgepeer = NULL;
+ struct sip_pvt *bridgepvt = NULL;
+ if ((bridgepeer = ast_bridged_channel(p->owner))) {
+ if (IS_SIP_TECH(bridgepeer->tech)) {
+ bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
+ /* Does the bridged peer have T38 ? */
+ if (bridgepvt->t38.state == T38_ENABLED) {
+ ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n");
+ /* Insted of this we should somehow re-invite the other side of the bridge to RTP */
+ if (req->ignore)
+ transmit_response(p, "488 Not Acceptable Here (unsupported)", req);
+ else
+ transmit_response_reliable(p, "488 Not Acceptable Here (unsupported)", req);
+ sendok = FALSE;
+ }
+ /* No bridged peer with T38 enabled*/
+ }
+ }
+ /* Respond to normal re-invite */
+ if (sendok) {
+ /* If this is not a re-invite or something to ignore - it's critical */
+ transmit_response_with_sdp(p, "200 OK", req, (reinvite || req->ignore) ? XMIT_UNRELIABLE : XMIT_CRITICAL, p->session_modify == TRUE ? FALSE:TRUE);
+ }
+ }
+ p->invitestate = INV_TERMINATED;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->_state);
+ transmit_response(p, "100 Trying", req);
+ break;
+ }
+ } else {
+ if (p && (p->autokillid == -1)) {
+ const char *msg;
+
+ if (!p->jointcapability)
+ msg = "488 Not Acceptable Here (codec error)";
+ else {
+ ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n");
+ msg = "503 Unavailable";
+ }
+ if (req->ignore)
+ transmit_response(p, msg, req);
+ else
+ transmit_response_reliable(p, msg, req);
+ p->invitestate = INV_COMPLETED;
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ }
+ return res;
+}
+
+/*! \brief Find all call legs and bridge transferee with target
+ * called from handle_request_refer */
+static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno)
+{
+ struct sip_dual target; /* Chan 1: Call from tranferer to Asterisk */
+ /* Chan 2: Call from Asterisk to target */
+ int res = 0;
+ struct sip_pvt *targetcall_pvt;
+
+ /* Check if the call ID of the replaces header does exist locally */
+ if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag,
+ transferer->refer->replaces_callid_fromtag))) {
+ if (transferer->refer->localtransfer) {
+ /* We did not find the refered call. Sorry, can't accept then */
+ transmit_response(transferer, "202 Accepted", req);
+ /* Let's fake a response from someone else in order
+ to follow the standard */
+ transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
+ append_history(transferer, "Xfer", "Refer failed");
+ ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
+ transferer->refer->status = REFER_FAILED;
+ return -1;
+ }
+ /* Fall through for remote transfers that we did not find locally */
+ ast_debug(3, "SIP attended transfer: Not our call - generating INVITE with replaces\n");
+ return 0;
+ }
+
+ /* Ok, we can accept this transfer */
+ transmit_response(transferer, "202 Accepted", req);
+ append_history(transferer, "Xfer", "Refer accepted");
+ if (!targetcall_pvt->owner) { /* No active channel */
+ ast_debug(4, "SIP attended transfer: Error: No owner of target call\n");
+ /* Cancel transfer */
+ transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
+ append_history(transferer, "Xfer", "Refer failed");
+ ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
+ transferer->refer->status = REFER_FAILED;
+ sip_pvt_unlock(targetcall_pvt);
+ ast_channel_unlock(current->chan1);
+ return -1;
+ }
+
+ /* We have a channel, find the bridge */
+ target.chan1 = targetcall_pvt->owner; /* Transferer to Asterisk */
+ target.chan2 = ast_bridged_channel(targetcall_pvt->owner); /* Asterisk to target */
+
+ if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) {
+ /* Wrong state of new channel */
+ if (target.chan2)
+ ast_debug(4, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(target.chan2->_state));
+ else if (target.chan1->_state != AST_STATE_RING)
+ ast_debug(4, "SIP attended transfer: Error: No target channel\n");
+ else
+ ast_debug(4, "SIP attended transfer: Attempting transfer in ringing state\n");
+ }
+
+ /* Transfer */
+ if (sipdebug) {
+ if (current->chan2) /* We have two bridges */
+ ast_debug(4, "SIP attended transfer: trying to bridge %s and %s\n", target.chan1->name, current->chan2->name);
+ else /* One bridge, propably transfer of IVR/voicemail etc */
+ ast_debug(4, "SIP attended transfer: trying to make %s take over (masq) %s\n", target.chan1->name, current->chan1->name);
+ }
+
+ ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
+
+ /* Perform the transfer */
+ manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Attended\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\n",
+ transferer->owner->name,
+ transferer->owner->uniqueid,
+ transferer->callid,
+ target.chan1->name,
+ target.chan1->uniqueid);
+ res = attempt_transfer(current, &target);
+ sip_pvt_unlock(targetcall_pvt);
+ if (res) {
+ /* Failed transfer */
+ transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE);
+ append_history(transferer, "Xfer", "Refer failed");
+ if (targetcall_pvt->owner)
+ ast_channel_unlock(targetcall_pvt->owner);
+ /* Right now, we have to hangup, sorry. Bridge is destroyed */
+ if (res != -2)
+ ast_hangup(transferer->owner);
+ else
+ ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
+ } else {
+ /* Transfer succeeded! */
+
+ /* Tell transferer that we're done. */
+ transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
+ append_history(transferer, "Xfer", "Refer succeeded");
+ transferer->refer->status = REFER_200OK;
+ if (targetcall_pvt->owner) {
+ ast_debug(1, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name);
+ ast_channel_unlock(targetcall_pvt->owner);
+ }
+ }
+ return 1;
+}
+
+
+/*! \brief Handle incoming REFER request */
+/*! \page SIP_REFER SIP transfer Support (REFER)
+
+ REFER is used for call transfer in SIP. We get a REFER
+ to place a new call with an INVITE somwhere and then
+ keep the transferor up-to-date of the transfer. If the
+ transfer fails, get back on line with the orginal call.
+
+ - REFER can be sent outside or inside of a dialog.
+ Asterisk only accepts REFER inside of a dialog.
+
+ - If we get a replaces header, it is an attended transfer
+
+ \par Blind transfers
+ The transferor provides the transferee
+ with the transfer targets contact. The signalling between
+ transferer or transferee should not be cancelled, so the
+ call is recoverable if the transfer target can not be reached
+ by the transferee.
+
+ In this case, Asterisk receives a TRANSFER from
+ the transferor, thus is the transferee. We should
+ try to set up a call to the contact provided
+ and if that fails, re-connect the current session.
+ If the new call is set up, we issue a hangup.
+ In this scenario, we are following section 5.2
+ in the SIP CC Transfer draft. (Transfer without
+ a GRUU)
+
+ \par Transfer with consultation hold
+ In this case, the transferor
+ talks to the transfer target before the transfer takes place.
+ This is implemented with SIP hold and transfer.
+ Note: The invite From: string could indicate a transfer.
+ (Section 6. Transfer with consultation hold)
+ The transferor places the transferee on hold, starts a call
+ with the transfer target to alert them to the impending
+ transfer, terminates the connection with the target, then
+ proceeds with the transfer (as in Blind transfer above)
+
+ \par Attended transfer
+ The transferor places the transferee
+ on hold, calls the transfer target to alert them,
+ places the target on hold, then proceeds with the transfer
+ using a Replaces header field in the Refer-to header. This
+ will force the transfee to send an Invite to the target,
+ with a replaces header that instructs the target to
+ hangup the call between the transferor and the target.
+ In this case, the Refer/to: uses the AOR address. (The same
+ URI that the transferee used to establish the session with
+ the transfer target (To: ). The Require: replaces header should
+ be in the INVITE to avoid the wrong UA in a forked SIP proxy
+ scenario to answer and have no call to replace with.
+
+ The referred-by header is *NOT* required, but if we get it,
+ can be copied into the INVITE to the transfer target to
+ inform the target about the transferor
+
+ "Any REFER request has to be appropriately authenticated.".
+
+ We can't destroy dialogs, since we want the call to continue.
+
+ */
+static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock)
+{
+ struct sip_dual current; /* Chan1: Call between asterisk and transferer */
+ /* Chan2: Call between asterisk and transferee */
+
+ int res = 0;
+
+ if (req->debug)
+ ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n", p->callid, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller");
+
+ if (!p->owner) {
+ /* This is a REFER outside of an existing SIP dialog */
+ /* We can't handle that, so decline it */
+ ast_debug(3, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
+ transmit_response(p, "603 Declined (No dialog)", req);
+ if (!req->ignore) {
+ append_history(p, "Xfer", "Refer failed. Outside of dialog.");
+ sip_alreadygone(p);
+ p->needdestroy = 1;
+ }
+ return 0;
+ }
+
+
+ /* Check if transfer is allowed from this device */
+ if (p->allowtransfer == TRANSFER_CLOSED ) {
+ /* Transfer not allowed, decline */
+ transmit_response(p, "603 Declined (policy)", req);
+ append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
+ /* Do not destroy SIP session */
+ return 0;
+ }
+
+ if (!req->ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
+ /* Already have a pending REFER */
+ transmit_response(p, "491 Request pending", req);
+ append_history(p, "Xfer", "Refer failed. Request pending.");
+ return 0;
+ }
+
+ /* Allocate memory for call transfer data */
+ if (!p->refer && !sip_refer_allocate(p)) {
+ transmit_response(p, "500 Internal Server Error", req);
+ append_history(p, "Xfer", "Refer failed. Memory allocation error.");
+ return -3;
+ }
+
+ res = get_refer_info(p, req); /* Extract headers */
+
+ p->refer->status = REFER_SENT;
+
+ if (res != 0) {
+ switch (res) {
+ case -2: /* Syntax error */
+ transmit_response(p, "400 Bad Request (Refer-to missing)", req);
+ append_history(p, "Xfer", "Refer failed. Refer-to missing.");
+ if (req->debug)
+ ast_debug(1, "SIP transfer to black hole can't be handled (no refer-to: )\n");
+ break;
+ case -3:
+ transmit_response(p, "603 Declined (Non sip: uri)", req);
+ append_history(p, "Xfer", "Refer failed. Non SIP uri");
+ if (req->debug)
+ ast_debug(1, "SIP transfer to non-SIP uri denied\n");
+ break;
+ default:
+ /* Refer-to extension not found, fake a failed transfer */
+ transmit_response(p, "202 Accepted", req);
+ append_history(p, "Xfer", "Refer failed. Bad extension.");
+ transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE);
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ if (req->debug)
+ ast_debug(1, "SIP transfer to bad extension: %s\n", p->refer->refer_to);
+ break;
+ }
+ return 0;
+ }
+ if (ast_strlen_zero(p->context))
+ ast_string_field_set(p, context, default_context);
+
+ /* If we do not support SIP domains, all transfers are local */
+ if (allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
+ p->refer->localtransfer = 1;
+ if (sipdebug)
+ ast_debug(3, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
+ } else if (AST_LIST_EMPTY(&domain_list) || check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
+ /* This PBX doesn't bother with SIP domains or domain is local, so this transfer is local */
+ p->refer->localtransfer = 1;
+ } else if (sipdebug)
+ ast_debug(3, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain);
+
+ /* Is this a repeat of a current request? Ignore it */
+ /* Don't know what else to do right now. */
+ if (req->ignore)
+ return res;
+
+ /* If this is a blind transfer, we have the following
+ channels to work with:
+ - chan1, chan2: The current call between transferer and transferee (2 channels)
+ - target_channel: A new call from the transferee to the target (1 channel)
+ We need to stay tuned to what happens in order to be able
+ to bring back the call to the transferer */
+
+ /* If this is a attended transfer, we should have all call legs within reach:
+ - chan1, chan2: The call between the transferer and transferee (2 channels)
+ - target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
+ We want to bridge chan2 with targetcall_pvt!
+
+ The replaces call id in the refer message points
+ to the call leg between Asterisk and the transferer.
+ So we need to connect the target and the transferee channel
+ and hangup the two other channels silently
+
+ If the target is non-local, the call ID could be on a remote
+ machine and we need to send an INVITE with replaces to the
+ target. We basically handle this as a blind transfer
+ and let the sip_call function catch that we need replaces
+ header in the INVITE.
+ */
+
+
+ /* Get the transferer's channel */
+ current.chan1 = p->owner;
+
+ /* Find the other part of the bridge (2) - transferee */
+ current.chan2 = ast_bridged_channel(current.chan1);
+
+ if (sipdebug)
+ ast_debug(3, "SIP %s transfer: Transferer channel %s, transferee channel %s\n", p->refer->attendedtransfer ? "attended" : "blind", current.chan1->name, current.chan2 ? current.chan2->name : "<none>");
+
+ if (!current.chan2 && !p->refer->attendedtransfer) {
+ /* No bridged channel, propably IVR or echo or similar... */
+ /* Guess we should masquerade or something here */
+ /* Until we figure it out, refuse transfer of such calls */
+ if (sipdebug)
+ ast_debug(3,"Refused SIP transfer on non-bridged channel.\n");
+ p->refer->status = REFER_FAILED;
+ append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
+ transmit_response(p, "603 Declined", req);
+ return -1;
+ }
+
+ if (current.chan2) {
+ if (sipdebug)
+ ast_debug(4, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name);
+
+ ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
+ }
+
+ ast_set_flag(&p->flags[0], SIP_GOTREFER);
+
+ /* Attended transfer: Find all call legs and bridge transferee with target*/
+ if (p->refer->attendedtransfer) {
+ if ((res = local_attended_transfer(p, &current, req, seqno)))
+ return res; /* We're done with the transfer */
+ /* Fall through for remote transfers that we did not find locally */
+ if (sipdebug)
+ ast_debug(4, "SIP attended transfer: Still not our call - generating INVITE with replaces\n");
+ /* Fallthrough if we can't find the call leg internally */
+ }
+
+
+ /* Parking a call */
+ if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) {
+ /* Must release c's lock now, because it will not longer be accessible after the transfer! */
+ *nounlock = 1;
+ ast_channel_unlock(current.chan1);
+ copy_request(&current.req, req);
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ p->refer->status = REFER_200OK;
+ append_history(p, "Xfer", "REFER to call parking.");
+ manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransfer2Parking: Yes\r\n",
+ current.chan1->name,
+ current.chan1->uniqueid,
+ p->callid,
+ current.chan2->name,
+ current.chan2->uniqueid,
+ p->refer->refer_to);
+ if (sipdebug)
+ ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name);
+ sip_park(current.chan2, current.chan1, req, seqno);
+ return res;
+ }
+
+ /* Blind transfers and remote attended xfers */
+ transmit_response(p, "202 Accepted", req);
+
+ if (current.chan1 && current.chan2) {
+ ast_debug(3, "chan1->name: %s\n", current.chan1->name);
+ pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name);
+ }
+ if (current.chan2) {
+ pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", current.chan1->name);
+ pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", p->refer->refer_to_domain);
+ pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
+ /* One for the new channel */
+ pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
+ /* Attended transfer to remote host, prepare headers for the INVITE */
+ if (p->refer->referred_by)
+ pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
+ }
+ /* Generate a Replaces string to be used in the INVITE during attended transfer */
+ if (!ast_strlen_zero(p->refer->replaces_callid)) {
+ char tempheader[BUFSIZ];
+ snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid,
+ p->refer->replaces_callid_totag ? ";to-tag=" : "",
+ p->refer->replaces_callid_totag,
+ p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
+ p->refer->replaces_callid_fromtag);
+ if (current.chan2)
+ pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader);
+ }
+ /* Must release lock now, because it will not longer
+ be accessible after the transfer! */
+ *nounlock = 1;
+ ast_channel_unlock(current.chan1);
+
+ /* Connect the call */
+
+ /* FAKE ringing if not attended transfer */
+ if (!p->refer->attendedtransfer)
+ transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE);
+
+ /* For blind transfer, this will lead to a new call */
+ /* For attended transfer to remote host, this will lead to
+ a new SIP call with a replaces header, if the dial plan allows it
+ */
+ if (!current.chan2) {
+ /* We have no bridge, so we're talking with Asterisk somehow */
+ /* We need to masquerade this call */
+ /* What to do to fix this situation:
+ * Set up the new call in a new channel
+ * Let the new channel masq into this channel
+ Please add that code here :-)
+ */
+ p->refer->status = REFER_FAILED;
+ transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ append_history(p, "Xfer", "Refer failed (only bridged calls).");
+ return -1;
+ }
+ ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
+
+
+ /* For blind transfers, move the call to the new extensions. For attended transfers on multiple
+ servers - generate an INVITE with Replaces. Either way, let the dial plan decided */
+ res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
+
+ if (!res) {
+ manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransferContext: %s\r\n",
+ current.chan1->name,
+ current.chan1->uniqueid,
+ p->callid,
+ current.chan2->name,
+ current.chan2->uniqueid,
+ p->refer->refer_to, p->refer->refer_to_context);
+ /* Success - we have a new channel */
+ ast_debug(3, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind");
+ transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
+ if (p->refer->localtransfer)
+ p->refer->status = REFER_200OK;
+ if (p->owner)
+ p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ append_history(p, "Xfer", "Refer succeeded.");
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ /* Do not hangup call, the other side do that when we say 200 OK */
+ /* We could possibly implement a timer here, auto congestion */
+ res = 0;
+ } else {
+ ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Don't delay hangup */
+ ast_debug(3, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
+ append_history(p, "Xfer", "Refer failed.");
+ /* Failure of some kind */
+ p->refer->status = REFER_FAILED;
+ transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ res = -1;
+ }
+ return res;
+}
+
+/*! \brief Handle incoming CANCEL request */
+static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
+{
+
+ check_via(p, req);
+ sip_alreadygone(p);
+
+ /* At this point, we could have cancelled the invite at the same time
+ as the other side sends a CANCEL. Our final reply with error code
+ might not have been received by the other side before the CANCEL
+ was sent, so let's just give up retransmissions and waiting for
+ ACK on our error code. The call is hanging up any way. */
+ if (p->invitestate == INV_TERMINATED)
+ __sip_pretend_ack(p);
+ else
+ p->invitestate = INV_CANCELLED;
+
+ if (p->owner && p->owner->_state == AST_STATE_UP) {
+ /* This call is up, cancel is ignored, we need a bye */
+ transmit_response(p, "200 OK", req);
+ ast_debug(1, "Got CANCEL on an answered call. Ignoring... \n");
+ return 0;
+ }
+
+ if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD))
+ update_call_counter(p, DEC_CALL_LIMIT);
+
+ stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
+
+ if (p->owner)
+ ast_queue_hangup(p->owner);
+ else
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ if (p->initreq.len > 0) {
+ transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
+ transmit_response(p, "200 OK", req);
+ return 1;
+ } else {
+ transmit_response(p, "481 Call Leg Does Not Exist", req);
+ return 0;
+ }
+}
+
+static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen)
+{
+ struct sip_pvt *p = chan->tech_pvt;
+ char *all = "", *parse = ast_strdupa(preparse);
+ int res = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(param);
+ AST_APP_ARG(type);
+ AST_APP_ARG(field);
+ );
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ /* Sanity check */
+ if (!IS_SIP_TECH(chan->tech)) {
+ ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname);
+ return 0;
+ }
+
+ memset(buf, 0, buflen);
+
+ if (!strcasecmp(args.param, "rtpdest")) {
+ struct sockaddr_in sin;
+
+ if (ast_strlen_zero(args.type))
+ args.type = "audio";
+
+ if (!strcasecmp(args.type, "audio"))
+ ast_rtp_get_peer(p->rtp, &sin);
+ else if (!strcasecmp(args.type, "video"))
+ ast_rtp_get_peer(p->vrtp, &sin);
+ else if (!strcasecmp(args.type, "text"))
+ ast_rtp_get_peer(p->trtp, &sin);
+
+ snprintf(buf, buflen, "%s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ } else if (!strcasecmp(args.param, "rtpqos")) {
+ struct ast_rtp_quality qos;
+
+ memset(&qos, 0, sizeof(qos));
+
+ if (ast_strlen_zero(args.type))
+ args.type = "audio";
+ if (ast_strlen_zero(args.field))
+ args.field = "all";
+
+ if (strcasecmp(args.type, "AUDIO") == 0) {
+ all = ast_rtp_get_quality(p->rtp, &qos);
+ } else if (strcasecmp(args.type, "VIDEO") == 0) {
+ all = ast_rtp_get_quality(p->vrtp, &qos);
+ } else if (strcasecmp(args.type, "TEXT") == 0) {
+ all = ast_rtp_get_quality(p->trtp, &qos);
+ }
+
+ if (strcasecmp(args.field, "local_ssrc") == 0)
+ snprintf(buf, buflen, "%u", qos.local_ssrc);
+ else if (strcasecmp(args.field, "local_lostpackets") == 0)
+ snprintf(buf, buflen, "%u", qos.local_lostpackets);
+ else if (strcasecmp(args.field, "local_jitter") == 0)
+ snprintf(buf, buflen, "%.0f", qos.local_jitter * 1000.0);
+ else if (strcasecmp(args.field, "local_count") == 0)
+ snprintf(buf, buflen, "%u", qos.local_count);
+ else if (strcasecmp(args.field, "remote_ssrc") == 0)
+ snprintf(buf, buflen, "%u", qos.remote_ssrc);
+ else if (strcasecmp(args.field, "remote_lostpackets") == 0)
+ snprintf(buf, buflen, "%u", qos.remote_lostpackets);
+ else if (strcasecmp(args.field, "remote_jitter") == 0)
+ snprintf(buf, buflen, "%.0f", qos.remote_jitter * 1000.0);
+ else if (strcasecmp(args.field, "remote_count") == 0)
+ snprintf(buf, buflen, "%u", qos.remote_count);
+ else if (strcasecmp(args.field, "rtt") == 0)
+ snprintf(buf, buflen, "%.0f", qos.rtt * 1000.0);
+ else if (strcasecmp(args.field, "all") == 0)
+ ast_copy_string(buf, all, buflen);
+ else {
+ ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
+ return -1;
+ }
+ } else {
+ res = -1;
+ }
+ return res;
+}
+
+/*! \brief Handle incoming BYE request */
+static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
+{
+ struct ast_channel *c=NULL;
+ int res;
+ struct ast_channel *bridged_to;
+
+ /* If we have an INCOMING invite that we haven't answered, terminate that transaction */
+ if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore && !p->owner)
+ transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
+
+ p->invitestate = INV_TERMINATED;
+
+ copy_request(&p->initreq, req);
+ if (sipdebug)
+ ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
+ check_via(p, req);
+ sip_alreadygone(p);
+
+ /* Get RTCP quality before end of call */
+ if (p->do_history || p->owner) {
+ char *audioqos, *videoqos, *textqos;
+ if (p->rtp) {
+ audioqos = ast_rtp_get_quality(p->rtp, NULL);
+ if (p->do_history)
+ append_history(p, "RTCPaudio", "Quality:%s", audioqos);
+ if (p->owner)
+ pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
+ }
+ if (p->vrtp) {
+ videoqos = ast_rtp_get_quality(p->vrtp, NULL);
+ if (p->do_history)
+ append_history(p, "RTCPvideo", "Quality:%s", videoqos);
+ if (p->owner)
+ pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
+ }
+ if (p->trtp) {
+ textqos = ast_rtp_get_quality(p->trtp, NULL);
+ if (p->do_history)
+ append_history(p, "RTCPtext", "Quality:%s", textqos);
+ if (p->owner)
+ pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", textqos);
+ }
+ }
+
+ stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
+ stop_session_timer(p); /* Stop Session-Timer */
+
+ if (!ast_strlen_zero(get_header(req, "Also"))) {
+ ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method. Ask vendor to support REFER instead\n",
+ ast_inet_ntoa(p->recv.sin_addr));
+ if (ast_strlen_zero(p->context))
+ ast_string_field_set(p, context, default_context);
+ res = get_also_info(p, req);
+ if (!res) {
+ c = p->owner;
+ if (c) {
+ bridged_to = ast_bridged_channel(c);
+ if (bridged_to) {
+ /* Don't actually hangup here... */
+ ast_queue_control(c, AST_CONTROL_UNHOLD);
+ ast_async_goto(bridged_to, p->context, p->refer->refer_to,1);
+ } else
+ ast_queue_hangup(p->owner);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr));
+ if (p->owner)
+ ast_queue_hangup(p->owner);
+ }
+ } else if (p->owner) {
+ ast_queue_hangup(p->owner);
+ ast_debug(3, "Received bye, issuing owner hangup\n");
+ } else {
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
+ }
+ transmit_response(p, "200 OK", req);
+
+ return 1;
+}
+
+/*! \brief Handle incoming MESSAGE request */
+static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
+{
+ if (!req->ignore) {
+ if (req->debug)
+ ast_verbose("Receiving message!\n");
+ receive_message(p, req);
+ } else
+ transmit_response(p, "202 Accepted", req);
+ return 1;
+}
+
+static void add_peer_mwi_subs(struct sip_peer *peer)
+{
+ struct sip_mailbox *mailbox;
+
+ AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
+ mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, peer,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
+ AST_EVENT_IE_END);
+ }
+}
+
+/*! \brief Handle incoming SUBSCRIBE request */
+static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e)
+{
+ int gotdest;
+ int res = 0;
+ int firststate = AST_EXTENSION_REMOVED;
+ struct sip_peer *authpeer = NULL;
+ const char *eventheader = get_header(req, "Event"); /* Get Event package name */
+ const char *accept = get_header(req, "Accept");
+ int resubscribe = (p->subscribed != NONE);
+ char *temp, *event;
+
+ if (p->initreq.headers) {
+ /* We already have a dialog */
+ if (p->initreq.method != SIP_SUBSCRIBE) {
+ /* This is a SUBSCRIBE within another SIP dialog, which we do not support */
+ /* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */
+ transmit_response(p, "403 Forbidden (within dialog)", req);
+ /* Do not destroy session, since we will break the call if we do */
+ ast_debug(1, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text);
+ return 0;
+ } else if (req->debug) {
+ if (resubscribe)
+ ast_debug(1, "Got a re-subscribe on existing subscription %s\n", p->callid);
+ else
+ ast_debug(1, "Got a new subscription %s (possibly with auth)\n", p->callid);
+ }
+ }
+
+ /* Check if we have a global disallow setting on subscriptions.
+ if so, we don't have to check peer/user settings after auth, which saves a lot of processing
+ */
+ if (!global_allowsubscribe) {
+ transmit_response(p, "403 Forbidden (policy)", req);
+ p->needdestroy = 1;
+ return 0;
+ }
+
+ if (!req->ignore && !resubscribe) { /* Set up dialog, new subscription */
+ /* Use this as the basis */
+ if (req->debug)
+ ast_verbose("Creating new subscription\n");
+
+ copy_request(&p->initreq, req);
+ if (sipdebug)
+ ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
+ check_via(p, req);
+ } else if (req->debug && req->ignore)
+ ast_verbose("Ignoring this SUBSCRIBE request\n");
+
+ /* Find parameters to Event: header value and remove them for now */
+ if (ast_strlen_zero(eventheader)) {
+ transmit_response(p, "489 Bad Event", req);
+ ast_debug(2, "Received SIP subscribe for unknown event package: <none>\n");
+ p->needdestroy = 1;
+ return 0;
+ }
+
+ if ( (strchr(eventheader, ';'))) {
+ event = ast_strdupa(eventheader); /* Since eventheader is a const, we can't change it */
+ temp = strchr(event, ';');
+ *temp = '\0'; /* Remove any options for now */
+ /* We might need to use them later :-) */
+ } else
+ event = (char *) eventheader; /* XXX is this legal ? */
+
+ /* Handle authentication */
+ res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, sin, &authpeer);
+ /* if an authentication response was sent, we are done here */
+ if (res == AUTH_CHALLENGE_SENT) /* authpeer = NULL here */
+ return 0;
+ if (res < 0) {
+ if (res == AUTH_FAKE_AUTH) {
+ ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
+ transmit_fake_auth_response(p, req, 1);
+ } else {
+ ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From"));
+ transmit_response_reliable(p, "403 Forbidden", req);
+ }
+ p->needdestroy = 1;
+ return 0;
+ }
+
+ /* At this point, authpeer cannot be NULL. Remember we hold a reference,
+ * so we must release it when done.
+ * XXX must remove all the checks for authpeer == NULL.
+ */
+
+ /* Check if this user/peer is allowed to subscribe at all */
+ if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
+ transmit_response(p, "403 Forbidden (policy)", req);
+ p->needdestroy = 1;
+ if (authpeer)
+ unref_peer(authpeer);
+ return 0;
+ }
+
+ /* Get destination right away */
+ gotdest = get_destination(p, NULL);
+
+ /* Get full contact header - this needs to be used as a request URI in NOTIFY's */
+ parse_ok_contact(p, req);
+
+ build_contact(p);
+ if (gotdest) {
+ transmit_response(p, "404 Not Found", req);
+ p->needdestroy = 1;
+ if (authpeer)
+ unref_peer(authpeer);
+ return 0;
+ }
+
+ /* Initialize tag for new subscriptions */
+ if (ast_strlen_zero(p->tag))
+ make_our_tag(p->tag, sizeof(p->tag));
+
+ if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
+ if (authpeer) /* We do not need the authpeer any more */
+ unref_peer(authpeer);
+
+ /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
+ /* Polycom phones only handle xpidf+xml, even if they say they can
+ handle pidf+xml as well
+ */
+ if (strstr(p->useragent, "Polycom")) {
+ p->subscribed = XPIDF_XML;
+ } else if (strstr(accept, "application/pidf+xml")) {
+ p->subscribed = PIDF_XML; /* RFC 3863 format */
+ } else if (strstr(accept, "application/dialog-info+xml")) {
+ p->subscribed = DIALOG_INFO_XML;
+ /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */
+ } else if (strstr(accept, "application/cpim-pidf+xml")) {
+ p->subscribed = CPIM_PIDF_XML; /* RFC 3863 format */
+ } else if (strstr(accept, "application/xpidf+xml")) {
+ p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */
+ } else if (ast_strlen_zero(accept)) {
+ if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */
+ transmit_response(p, "489 Bad Event", req);
+
+ ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n",
+ p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri);
+ p->needdestroy = 1;
+ return 0;
+ }
+ /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least.
+ so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */
+ } else {
+ /* Can't find a format for events that we know about */
+ char mybuf[200];
+ snprintf(mybuf,sizeof(mybuf),"489 Bad Event (format %s)", accept);
+ transmit_response(p, mybuf, req);
+
+ ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format: '%s' pvt: subscribed: %d, stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n",
+ accept, (int)p->subscribed, p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri);
+ p->needdestroy = 1;
+ return 0;
+ }
+ } else if (!strcmp(event, "message-summary")) {
+ if (!ast_strlen_zero(accept) && strcmp(accept, "application/simple-message-summary")) {
+ /* Format requested that we do not support */
+ transmit_response(p, "406 Not Acceptable", req);
+ ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", accept);
+ p->needdestroy = 1;
+ if (authpeer)
+ unref_peer(authpeer);
+ return 0;
+ }
+ /* Looks like they actually want a mailbox status
+ This version of Asterisk supports mailbox subscriptions
+ The subscribed URI needs to exist in the dial plan
+ In most devices, this is configurable to the voicemailmain extension you use
+ */
+ if (!authpeer || AST_LIST_EMPTY(&authpeer->mailboxes)) {
+ transmit_response(p, "404 Not found (no mailbox)", req);
+ p->needdestroy = 1;
+ ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", authpeer->name);
+ if (authpeer)
+ unref_peer(authpeer);
+ return 0;
+ }
+
+ p->subscribed = MWI_NOTIFICATION;
+ if (ast_test_flag(&authpeer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY)) {
+ add_peer_mwi_subs(authpeer);
+ }
+ if (authpeer->mwipvt && authpeer->mwipvt != p) /* Destroy old PVT if this is a new one */
+ /* We only allow one subscription per peer */
+ sip_destroy(authpeer->mwipvt);
+ authpeer->mwipvt = p; /* Link from peer to pvt */
+ p->relatedpeer = authpeer; /* Link from pvt to peer */
+ /* Do not release authpeer here */
+ } else { /* At this point, Asterisk does not understand the specified event */
+ transmit_response(p, "489 Bad Event", req);
+ ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
+ p->needdestroy = 1;
+ if (authpeer)
+ unref_peer(authpeer);
+ return 0;
+ }
+
+ /* Add subscription for extension state from the PBX core */
+ if (p->subscribed != MWI_NOTIFICATION && !resubscribe) {
+ if (p->stateid > -1)
+ ast_extension_state_del(p->stateid, cb_extensionstate);
+ p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
+ }
+
+ if (!req->ignore && p)
+ p->lastinvite = seqno;
+ if (p && !p->needdestroy) {
+ p->expiry = atoi(get_header(req, "Expires"));
+
+ /* check if the requested expiry-time is within the approved limits from sip.conf */
+ if (p->expiry > max_expiry)
+ p->expiry = max_expiry;
+ if (p->expiry < min_expiry && p->expiry > 0)
+ p->expiry = min_expiry;
+
+ if (sipdebug) {
+ if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer)
+ ast_debug(2, "Adding subscription for mailbox notification - peer %s\n", p->relatedpeer->name);
+ else
+ ast_debug(2, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username);
+ }
+ if (p->autokillid > -1)
+ sip_cancel_destroy(p); /* Remove subscription expiry for renewals */
+ if (p->expiry > 0)
+ sip_scheddestroy(p, (p->expiry + 10) * 1000); /* Set timer for destruction of call at expiration */
+
+ if (p->subscribed == MWI_NOTIFICATION) {
+ transmit_response(p, "200 OK", req);
+ if (p->relatedpeer) { /* Send first notification */
+ ASTOBJ_WRLOCK(p->relatedpeer);
+ sip_send_mwi_to_peer(p->relatedpeer, NULL, 0);
+ ASTOBJ_UNLOCK(p->relatedpeer);
+ }
+ } else {
+ struct sip_pvt *p_old;
+
+ if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) {
+
+ ast_log(LOG_NOTICE, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension.\n", p->exten, p->context, ast_inet_ntoa(p->sa.sin_addr));
+ transmit_response(p, "404 Not found", req);
+ p->needdestroy = 1;
+ return 0;
+ }
+
+ transmit_response(p, "200 OK", req);
+ transmit_state_notify(p, firststate, 1, FALSE); /* Send first notification */
+ append_history(p, "Subscribestatus", "%s", ast_extension_state2str(firststate));
+ /* hide the 'complete' exten/context in the refer_to field for later display */
+ ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context);
+
+ /* remove any old subscription from this peer for the same exten/context,
+ as the peer has obviously forgotten about it and it's wasteful to wait
+ for it to expire and send NOTIFY messages to the peer only to have them
+ ignored (or generate errors)
+ */
+ dialoglist_lock();
+ for (p_old = dialoglist; p_old; p_old = p_old->next) {
+ if (p_old == p)
+ continue;
+ if (p_old->initreq.method != SIP_SUBSCRIBE)
+ continue;
+ if (p_old->subscribed == NONE)
+ continue;
+ sip_pvt_lock(p_old);
+ if (!strcmp(p_old->username, p->username)) {
+ if (!strcmp(p_old->exten, p->exten) &&
+ !strcmp(p_old->context, p->context)) {
+ p_old->needdestroy = 1;
+ sip_pvt_unlock(p_old);
+ break;
+ }
+ }
+ sip_pvt_unlock(p_old);
+ }
+ dialoglist_unlock();
+ }
+ if (!p->expiry)
+ p->needdestroy = 1;
+ }
+ return 1;
+}
+
+/*! \brief Handle incoming REGISTER request */
+static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e)
+{
+ enum check_auth_result res;
+
+ /* Use this as the basis */
+ copy_request(&p->initreq, req);
+ if (sipdebug)
+ ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
+ check_via(p, req);
+ if ((res = register_verify(p, sin, req, e)) < 0) {
+ const char *reason;
+
+ switch (res) {
+ case AUTH_SECRET_FAILED:
+ reason = "Wrong password";
+ break;
+ case AUTH_USERNAME_MISMATCH:
+ reason = "Username/auth name mismatch";
+ break;
+ case AUTH_NOT_FOUND:
+ reason = "No matching peer found";
+ break;
+ case AUTH_UNKNOWN_DOMAIN:
+ reason = "Not a local domain";
+ break;
+ case AUTH_PEER_NOT_DYNAMIC:
+ reason = "Peer is not supposed to register";
+ break;
+ case AUTH_ACL_FAILED:
+ reason = "Device does not match ACL";
+ break;
+ default:
+ reason = "Unknown failure";
+ break;
+ }
+ ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n",
+ get_header(req, "To"), ast_inet_ntoa(sin->sin_addr),
+ reason);
+ append_history(p, "RegRequest", "Failed : Account %s : %s", get_header(req, "To"), reason);
+ } else
+ append_history(p, "RegRequest", "Succeeded : Account %s", get_header(req, "To"));
+
+ if (res < 1) {
+ /* Destroy the session, but keep us around for just a bit in case they don't
+ get our 200 OK */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+ return res;
+}
+
+/*! \brief Handle incoming SIP requests (methods)
+\note This is where all incoming requests go first */
+/* called with p and p->owner locked */
+static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock)
+{
+ /* Called with p->lock held, as well as p->owner->lock if appropriate, keeping things
+ relatively static */
+ const char *cmd;
+ const char *cseq;
+ const char *useragent;
+ int seqno;
+ int len;
+ int respid;
+ int res = 0;
+ int debug = sip_debug_test_pvt(p);
+ char *e;
+ int error = 0;
+
+ /* Get Method and Cseq */
+ cseq = get_header(req, "Cseq");
+ cmd = req->header[0];
+
+ /* Must have Cseq */
+ if (ast_strlen_zero(cmd) || ast_strlen_zero(cseq)) {
+ ast_log(LOG_ERROR, "Missing Cseq. Dropping this SIP message, it's incomplete.\n");
+ error = 1;
+ }
+ if (!error && sscanf(cseq, "%d%n", &seqno, &len) != 1) {
+ ast_log(LOG_ERROR, "No seqno in '%s'. Dropping incomplete message.\n", cmd);
+ error = 1;
+ }
+ if (error) {
+ if (!p->initreq.headers) /* New call */
+ p->needdestroy = 1; /* Make sure we destroy this dialog */
+ return -1;
+ }
+ /* Get the command XXX */
+
+ cmd = req->rlPart1;
+ e = req->rlPart2;
+
+ /* Save useragent of the client */
+ useragent = get_header(req, "User-Agent");
+ if (!ast_strlen_zero(useragent))
+ ast_string_field_set(p, useragent, useragent);
+
+ /* Find out SIP method for incoming request */
+ if (req->method == SIP_RESPONSE) { /* Response to our request */
+ /* When we get here, we know this is a SIP dialog where we've sent
+ * a request and have a response, or at least get a response
+ * within an existing dialog. Do some sanity checks, then
+ * possibly process the request. In all cases, there function
+ * terminates at the end of this block
+ */
+ int ret = 0;
+
+ if (p->ocseq < seqno && seqno != p->lastnoninvite) {
+ ast_debug(1, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq);
+ ret = -1;
+ } else if (p->ocseq != seqno && seqno != p->lastnoninvite) {
+ /* ignore means "don't do anything with it" but still have to
+ * respond appropriately.
+ * But in this case this is a response already, so we really
+ * have nothing to do with this message, and even setting the
+ * ignore flag is pointless.
+ */
+ req->ignore = 1;
+ append_history(p, "Ignore", "Ignoring this retransmit\n");
+ } else if (e) {
+ e = ast_skip_blanks(e);
+ if (sscanf(e, "%d %n", &respid, &len) != 1) {
+ ast_log(LOG_WARNING, "Invalid response: '%s'\n", e);
+ /* XXX maybe should do ret = -1; */
+ } else if (respid <= 0) {
+ ast_log(LOG_WARNING, "Invalid SIP response code: '%d'\n", respid);
+ /* XXX maybe should do ret = -1; */
+ } else { /* finally, something worth processing */
+ /* More SIP ridiculousness, we have to ignore bogus contacts in 100 etc responses */
+ if ((respid == 200) || ((respid >= 300) && (respid <= 399)))
+ extract_uri(p, req);
+ handle_response(p, respid, e + len, req, seqno);
+ }
+ }
+ return 0;
+ }
+
+ /* New SIP request coming in
+ (could be new request in existing SIP dialog as well...)
+ */
+
+ p->method = req->method; /* Find out which SIP method they are using */
+ ast_debug(4, "**** Received %s (%d) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd);
+
+ if (p->icseq && (p->icseq > seqno)) {
+ ast_debug(1, "Ignoring too old SIP packet packet %d (expecting >= %d)\n", seqno, p->icseq);
+ if (req->method != SIP_ACK)
+ transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */
+ return -1;
+ } else if (p->icseq &&
+ p->icseq == seqno &&
+ req->method != SIP_ACK &&
+ (p->method != SIP_CANCEL || p->alreadygone)) {
+ /* ignore means "don't do anything with it" but still have to
+ respond appropriately. We do this if we receive a repeat of
+ the last sequence number */
+ req->ignore = 1;
+ ast_debug(3, "Ignoring SIP message because of retransmit (%s Seqno %d, ours %d)\n", sip_methods[p->method].text, p->icseq, seqno);
+ }
+
+ if (seqno >= p->icseq)
+ /* Next should follow monotonically (but not necessarily
+ incrementally -- thanks again to the genius authors of SIP --
+ increasing */
+ p->icseq = seqno;
+
+ /* Find their tag if we haven't got it */
+ if (ast_strlen_zero(p->theirtag)) {
+ char tag[128];
+
+ gettag(req, "From", tag, sizeof(tag));
+ ast_string_field_set(p, theirtag, tag);
+ }
+ snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd);
+
+ if (pedanticsipchecking) {
+ /* If this is a request packet without a from tag, it's not
+ correct according to RFC 3261 */
+ /* Check if this a new request in a new dialog with a totag already attached to it,
+ RFC 3261 - section 12.2 - and we don't want to mess with recovery */
+ if (!p->initreq.headers && req->has_to_tag) {
+ /* If this is a first request and it got a to-tag, it is not for us */
+ if (!req->ignore && req->method == SIP_INVITE) {
+ transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req);
+ /* Will cease to exist after ACK */
+ } else if (req->method != SIP_ACK) {
+ transmit_response(p, "481 Call/Transaction Does Not Exist", req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ } else {
+ ast_debug(1, "Got ACK for unknown dialog... strange.\n");
+ }
+ return res;
+ }
+ }
+
+ if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY)) {
+ transmit_response(p, "400 Bad request", req);
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ return -1;
+ }
+
+ /* Handle various incoming SIP methods in requests */
+ switch (p->method) {
+ case SIP_OPTIONS:
+ res = handle_request_options(p, req);
+ break;
+ case SIP_INVITE:
+ res = handle_request_invite(p, req, debug, seqno, sin, recount, e, nounlock);
+ break;
+ case SIP_REFER:
+ res = handle_request_refer(p, req, debug, seqno, nounlock);
+ break;
+ case SIP_CANCEL:
+ res = handle_request_cancel(p, req);
+ break;
+ case SIP_BYE:
+ res = handle_request_bye(p, req);
+ break;
+ case SIP_MESSAGE:
+ res = handle_request_message(p, req);
+ break;
+ case SIP_SUBSCRIBE:
+ res = handle_request_subscribe(p, req, sin, seqno, e);
+ break;
+ case SIP_REGISTER:
+ res = handle_request_register(p, req, sin, e);
+ break;
+ case SIP_INFO:
+ if (req->debug)
+ ast_verbose("Receiving INFO!\n");
+ if (!req->ignore)
+ handle_request_info(p, req);
+ else /* if ignoring, transmit response */
+ transmit_response(p, "200 OK", req);
+ break;
+ case SIP_NOTIFY:
+ res = handle_request_notify(p, req, sin, seqno, e);
+ break;
+ case SIP_ACK:
+ /* Make sure we don't ignore this */
+ if (seqno == p->pendinginvite) {
+ p->invitestate = INV_TERMINATED;
+ p->pendinginvite = 0;
+ __sip_ack(p, seqno, 1 /* response */, 0);
+ if (find_sdp(req)) {
+ if (process_sdp(p, req))
+ return -1;
+ }
+ check_pendings(p);
+ }
+ /* Got an ACK that we did not match. Ignore silently */
+ if (!p->lastinvite && ast_strlen_zero(p->randdata))
+ p->needdestroy = 1;
+ break;
+ default:
+ transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
+ ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n",
+ cmd, ast_inet_ntoa(p->sa.sin_addr));
+ /* If this is some new method, and we don't have a call, destroy it now */
+ if (!p->initreq.headers)
+ p->needdestroy = 1;
+ break;
+ }
+ return res;
+}
+
+/*! \brief Read data from SIP socket
+\note sipsock_read locks the owner channel while we are processing the SIP message
+\return 1 on error, 0 on success
+\note Successful messages is connected to SIP call and forwarded to handle_incoming()
+*/
+static int sipsock_read(int *id, int fd, short events, void *ignore)
+{
+ struct sip_request req;
+ struct sockaddr_in sin = { 0, };
+ int res;
+ socklen_t len = sizeof(sin);
+
+ memset(&req, 0, sizeof(req));
+ res = recvfrom(fd, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
+ if (res < 0) {
+#if !defined(__FreeBSD__)
+ if (errno == EAGAIN)
+ ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n");
+ else
+#endif
+ if (errno != ECONNREFUSED)
+ ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
+ return 1;
+ }
+ if (res == sizeof(req.data)) {
+ ast_debug(1, "Received packet exceeds buffer. Data is possibly lost\n");
+ req.data[sizeof(req.data) - 1] = '\0';
+ } else
+ req.data[res] = '\0';
+ req.len = res;
+
+ req.socket.fd = sipsock;
+ req.socket.type = SIP_TRANSPORT_UDP;
+ req.socket.ser = NULL;
+ req.socket.port = htons(bindaddr.sin_port);
+ req.socket.lock = NULL;
+
+ handle_request_do(&req, &sin);
+
+ return 1;
+}
+
+static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
+{
+ struct sip_pvt *p;
+ int recount = 0;
+ int nounlock = 0;
+ int lockretry;
+
+ if (sip_debug_test_addr(sin)) /* Set the debug flag early on packet level */
+ req->debug = 1;
+ if (pedanticsipchecking)
+ req->len = lws2sws(req->data, req->len); /* Fix multiline headers */
+ if (req->debug) {
+ ast_verbose("\n<--- SIP read from %s://%s:%d --->\n%s\n<------------->\n",
+ get_transport(req->socket.type), ast_inet_ntoa(sin->sin_addr),
+ ntohs(sin->sin_port), req->data);
+ }
+
+ parse_request(req);
+ req->method = find_sip_method(req->rlPart1);
+
+ if (req->debug)
+ ast_verbose("--- (%d headers %d lines)%s ---\n", req->headers, req->lines, (req->headers + req->lines == 0) ? " Nat keepalive" : "");
+
+ if (req->headers < 2) /* Must have at least two headers */
+ return 1;
+
+ /* Process request, with netlock held, and with usual deadlock avoidance */
+ for (lockretry = 100; lockretry > 0; lockretry--) {
+ ast_mutex_lock(&netlock);
+
+ /* Find the active SIP dialog or create a new one */
+ p = find_call(req, sin, req->method); /* returns p locked */
+ if (p == NULL) {
+ ast_debug(1, "Invalid SIP message - rejected , no callid, len %d\n", req->len);
+ ast_mutex_unlock(&netlock);
+ return 1;
+ }
+
+ p->socket = req->socket;
+
+ /* Go ahead and lock the owner if it has one -- we may need it */
+ /* becaues this is deadlock-prone, we need to try and unlock if failed */
+ if (!p->owner || !ast_channel_trylock(p->owner))
+ break; /* locking succeeded */
+ ast_debug(1, "Failed to grab owner channel lock, trying again. (SIP call %s)\n", p->callid);
+ sip_pvt_unlock(p);
+ ast_mutex_unlock(&netlock);
+ /* Sleep for a very short amount of time */
+ usleep(1);
+ }
+ p->recv = *sin;
+
+ if (p->do_history) /* This is a request or response, note what it was for */
+ append_history(p, "Rx", "%s / %s / %s", req->data, get_header(req, "CSeq"), req->rlPart2);
+
+ if (!lockretry) {
+ if (p->owner)
+ ast_log(LOG_ERROR, "We could NOT get the channel lock for %s! \n", S_OR(p->owner->name, "- no channel name ??? - "));
+ ast_log(LOG_ERROR, "SIP transaction failed: %s \n", p->callid);
+ if (req->method != SIP_ACK)
+ transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */
+ /* XXX We could add retry-after to make sure they come back */
+ append_history(p, "LockFail", "Owner lock failed, transaction failed.");
+ return 1;
+ }
+
+ nounlock = 0;
+ if (handle_incoming(p, req, sin, &recount, &nounlock) == -1) {
+ /* Request failed */
+ ast_debug(1, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
+ }
+
+ if (recount)
+ ast_update_use_count();
+
+ if (p->owner && !nounlock)
+ ast_channel_unlock(p->owner);
+ sip_pvt_unlock(p);
+ ast_mutex_unlock(&netlock);
+
+ return 1;
+}
+
+static int sip_standard_port(struct sip_socket s)
+{
+ if (s.type & SIP_TRANSPORT_TLS)
+ return s.port == STANDARD_TLS_PORT;
+ else
+ return s.port == STANDARD_SIP_PORT;
+}
+
+static struct server_instance *sip_tcp_locate(struct sockaddr_in *s)
+{
+ struct sip_threadinfo *th;
+
+ AST_LIST_LOCK(&threadl);
+ AST_LIST_TRAVERSE(&threadl, th, list) {
+ if ((s->sin_family == th->ser->requestor.sin_family) &&
+ (s->sin_addr.s_addr == th->ser->requestor.sin_addr.s_addr) &&
+ (s->sin_port == th->ser->requestor.sin_port))
+ return th->ser;
+ }
+ AST_LIST_UNLOCK(&threadl);
+ return NULL;
+}
+
+static int sip_prepare_socket(struct sip_pvt *p)
+{
+ struct sip_socket *s = &p->socket;
+ static const char name[] = "SIP socket";
+ struct server_instance *ser;
+ struct server_args ca = {
+ .name = name,
+ .accept_fd = -1,
+ };
+
+ if (s->fd != -1)
+ return s->fd;
+
+ if (s->type & SIP_TRANSPORT_UDP) {
+ s->fd = sipsock;
+ return s->fd;
+ }
+
+ ca.sin = *(sip_real_dst(p));
+
+ if ((ser = sip_tcp_locate(&ca.sin))) {
+ s->fd = ser->fd;
+ s->ser = ser;
+ return s->fd;
+ }
+
+ if (s->ser && s->ser->parent->tls_cfg)
+ ca.tls_cfg = s->ser->parent->tls_cfg;
+ else {
+ if (s->type & SIP_TRANSPORT_TLS) {
+ ca.tls_cfg = ast_calloc(1, sizeof(*ca.tls_cfg));
+ if (!ca.tls_cfg)
+ return -1;
+ memcpy(ca.tls_cfg, &default_tls_cfg, sizeof(*ca.tls_cfg));
+ if (!ast_strlen_zero(p->tohost))
+ ast_copy_string(ca.hostname, p->tohost, sizeof(ca.hostname));
+ }
+ }
+ s->ser = (!s->ser) ? client_start(&ca) : s->ser;
+
+ if (!s->ser) {
+ if (ca.tls_cfg)
+ ast_free(ca.tls_cfg);
+ return -1;
+ }
+
+ s->fd = ca.accept_fd;
+
+ if (ast_pthread_create_background(&ca.master, NULL, sip_tcp_helper_thread, p)) {
+ ast_log(LOG_DEBUG, "Unable to launch '%s'.", ca.name);
+ close(ca.accept_fd);
+ s->fd = ca.accept_fd = -1;
+ }
+
+ return s->fd;
+}
+
+/*!
+ * \brief Get cached MWI info
+ * \retval 0 At least one message is waiting
+ * \retval 1 no messages waiting
+ */
+static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
+{
+ struct sip_mailbox *mailbox;
+
+ AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
+ struct ast_event *event;
+ event = ast_event_get_cached(AST_EVENT_MWI,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_END);
+ if (!event)
+ continue;
+ *new += ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+ *old += ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
+ ast_event_destroy(event);
+ }
+
+ return (*new || *old) ? 0 : 1;
+}
+
+/*! \brief Send message waiting indication to alert peer that they've got voicemail */
+static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only)
+{
+ /* Called with peerl lock, but releases it */
+ struct sip_pvt *p;
+ int newmsgs = 0, oldmsgs = 0;
+
+ if (ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY) && !peer->mwipvt)
+ return 0;
+
+ /* Do we have an IP address? If not, skip this peer */
+ if (!peer->addr.sin_addr.s_addr && !peer->defaddr.sin_addr.s_addr)
+ return 0;
+
+ if (event) {
+ newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+ oldmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
+ } else if (!get_cached_mwi(peer, &newmsgs, &oldmsgs)) {
+ /* got it! Don't keep looking. */
+ } else if (cache_only) {
+ return 0;
+ } else { /* Fall back to manually checking the mailbox */
+ struct ast_str *mailbox_str = ast_str_alloca(512);
+ peer_mailboxes_to_str(&mailbox_str, peer);
+ ast_app_inboxcount(mailbox_str->str, &newmsgs, &oldmsgs);
+ }
+
+ if (peer->mwipvt) {
+ /* Base message on subscription */
+ p = dialog_ref(peer->mwipvt);
+ } else {
+ /* Build temporary dialog for this message */
+ if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY)))
+ return -1;
+ if (create_addr_from_peer(p, peer)) {
+ /* Maybe they're not registered, etc. */
+ sip_destroy(p);
+ return 0;
+ }
+ /* Recalculate our side, and recalculate Call ID */
+ ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
+ build_via(p);
+ build_callid_pvt(p);
+ /* Destroy this session after 32 secs */
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+ }
+
+ /* Send MWI */
+ ast_set_flag(&p->flags[0], SIP_OUTGOING);
+ transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
+
+ return 0;
+}
+
+/*! \brief helper function for the monitoring thread */
+static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
+{
+ /* If we have no RTP or no active owner, no need to check timers */
+ if (!dialog->rtp || !dialog->owner)
+ return;
+ /* If the call is not in UP state or redirected outside Asterisk, no need to check timers */
+ if (dialog->owner->_state != AST_STATE_UP || dialog->redirip.sin_addr.s_addr)
+ return;
+
+ /* If the call is involved in a T38 fax session do not check RTP timeout */
+ if (dialog->t38.state == T38_ENABLED)
+ return;
+
+ /* If we have no timers set, return now */
+ if ((ast_rtp_get_rtpkeepalive(dialog->rtp) == 0) && (ast_rtp_get_rtptimeout(dialog->rtp) == 0) && (ast_rtp_get_rtpholdtimeout(dialog->rtp) == 0))
+ return;
+
+ /* Check AUDIO RTP keepalives */
+ if (dialog->lastrtptx && ast_rtp_get_rtpkeepalive(dialog->rtp) &&
+ (t > dialog->lastrtptx + ast_rtp_get_rtpkeepalive(dialog->rtp))) {
+ /* Need to send an empty RTP packet */
+ dialog->lastrtptx = time(NULL);
+ ast_rtp_sendcng(dialog->rtp, 0);
+ }
+
+ /*! \todo Check video RTP keepalives
+
+ Do we need to move the lastrtptx to the RTP structure to have one for audio and one
+ for video? It really does belong to the RTP structure.
+ */
+
+ /* Check AUDIO RTP timers */
+ if (dialog->lastrtprx && (ast_rtp_get_rtptimeout(dialog->rtp) || ast_rtp_get_rtpholdtimeout(dialog->rtp)) &&
+ (t > dialog->lastrtprx + ast_rtp_get_rtptimeout(dialog->rtp))) {
+
+ /* Might be a timeout now -- see if we're on hold */
+ struct sockaddr_in sin;
+ ast_rtp_get_peer(dialog->rtp, &sin);
+ if (sin.sin_addr.s_addr || (ast_rtp_get_rtpholdtimeout(dialog->rtp) &&
+ (t > dialog->lastrtprx + ast_rtp_get_rtpholdtimeout(dialog->rtp)))) {
+ /* Needs a hangup */
+ if (ast_rtp_get_rtptimeout(dialog->rtp)) {
+ while (dialog->owner && ast_channel_trylock(dialog->owner)) {
+ sip_pvt_unlock(dialog);
+ usleep(1);
+ sip_pvt_lock(dialog);
+ }
+ ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
+ dialog->owner->name, (long) (t - dialog->lastrtprx));
+ /* Issue a softhangup */
+ ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV);
+ ast_channel_unlock(dialog->owner);
+ /* forget the timeouts for this call, since a hangup
+ has already been requested and we don't want to
+ repeatedly request hangups
+ */
+ ast_rtp_set_rtptimeout(dialog->rtp, 0);
+ ast_rtp_set_rtpholdtimeout(dialog->rtp, 0);
+ if (dialog->vrtp) {
+ ast_rtp_set_rtptimeout(dialog->vrtp, 0);
+ ast_rtp_set_rtpholdtimeout(dialog->vrtp, 0);
+ }
+ }
+ }
+ }
+}
+
+/*! \brief The SIP monitoring thread
+\note This thread monitors all the SIP sessions and peers that needs notification of mwi
+ (and thus do not have a separate thread) indefinitely
+*/
+static void *do_monitor(void *data)
+{
+ int res;
+ struct sip_pvt *dialog;
+ time_t t;
+ int reloading;
+
+ /* Add an I/O event to our SIP UDP socket */
+ if (sipsock > -1)
+ sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
+
+ /* From here on out, we die whenever asked */
+ for(;;) {
+ /* Check for a reload request */
+ ast_mutex_lock(&sip_reload_lock);
+ reloading = sip_reloading;
+ sip_reloading = FALSE;
+ ast_mutex_unlock(&sip_reload_lock);
+ if (reloading) {
+ ast_verb(1, "Reloading SIP\n");
+ sip_do_reload(sip_reloadreason);
+
+ /* Change the I/O fd of our UDP socket */
+ if (sipsock > -1) {
+ if (sipsock_read_id)
+ sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL);
+ else
+ sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
+ } else if (sipsock_read_id) {
+ ast_io_remove(io, sipsock_read_id);
+ sipsock_read_id = NULL;
+ }
+ }
+
+ /* Check for dialogs needing to be killed */
+ dialoglist_lock();
+restartsearch:
+ t = time(NULL);
+ /* don't scan the dialogs list if it hasn't been a reasonable period
+ of time since the last time we did it (when MWI is being sent, we can
+ get back to this point every millisecond or less)
+ */
+ for (dialog = dialoglist; dialog; dialog = dialog->next) {
+ sip_pvt_lock(dialog);
+ /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
+ check_rtp_timeout(dialog, t);
+ /* If we have sessions that needs to be destroyed, do it now */
+ /* Check if we have outstanding requests not responsed to or an active call
+ - if that's the case, wait with destruction */
+ if (dialog->needdestroy && !dialog->packets && !dialog->owner) {
+ sip_pvt_unlock(dialog);
+ __sip_destroy(dialog, TRUE, FALSE);
+ goto restartsearch;
+ }
+ sip_pvt_unlock(dialog);
+ }
+ dialoglist_unlock();
+
+ pthread_testcancel();
+ /* Wait for sched or io */
+ res = ast_sched_wait(sched);
+ if ((res < 0) || (res > 1000))
+ res = 1000;
+ res = ast_io_wait(io, res);
+ if (res > 20)
+ ast_debug(1, "chan_sip: ast_io_wait ran %d all at once\n", res);
+ ast_mutex_lock(&monlock);
+ if (res >= 0) {
+ res = ast_sched_runq(sched);
+ if (res >= 20)
+ ast_debug(1, "chan_sip: ast_sched_runq ran %d all at once\n", res);
+ }
+ ast_mutex_unlock(&monlock);
+ }
+
+ /* Never reached */
+ return NULL;
+}
+
+/*! \brief Start the channel monitor thread */
+static int restart_monitor(void)
+{
+ /* If we're supposed to be stopped -- stay stopped */
+ if (monitor_thread == AST_PTHREADT_STOP)
+ return 0;
+ ast_mutex_lock(&monlock);
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ /* Wake up the thread */
+ pthread_kill(monitor_thread, SIGURG);
+ } else {
+ /* Start a new monitor */
+ if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+
+/*! \brief Session-Timers: Restart session timer */
+static void restart_session_timer(struct sip_pvt *p)
+{
+ if (!p->stimer) {
+ ast_log(LOG_WARNING, "Null stimer in restart_session_timer - %s\n", p->callid);
+ return;
+ }
+
+ if (p->stimer->st_active == TRUE) {
+ if (ast_sched_del(sched, p->stimer->st_schedid) != 0) {
+ ast_log(LOG_WARNING, "ast_sched_del failed: %d - %s\n", p->stimer->st_schedid, p->callid);
+ }
+
+ ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
+ start_session_timer(p);
+ }
+}
+
+
+/*! \brief Session-Timers: Stop session timer */
+static void stop_session_timer(struct sip_pvt *p)
+{
+ if (!p->stimer) {
+ ast_log(LOG_WARNING, "Null stimer in stop_session_timer - %s\n", p->callid);
+ return;
+ }
+
+ if (p->stimer->st_active == TRUE) {
+ p->stimer->st_active = FALSE;
+ ast_sched_del(sched, p->stimer->st_schedid);
+ ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
+ }
+}
+
+
+/*! \brief Session-Timers: Start session timer */
+static void start_session_timer(struct sip_pvt *p)
+{
+ if (!p->stimer) {
+ ast_log(LOG_WARNING, "Null stimer in start_session_timer - %s\n", p->callid);
+ return;
+ }
+
+ p->stimer->st_schedid = ast_sched_add(sched, p->stimer->st_interval * 1000 / 2, proc_session_timer, p);
+ if (p->stimer->st_schedid < 0) {
+ ast_log(LOG_ERROR, "ast_sched_add failed.\n");
+ }
+ ast_debug(2, "Session timer started: %d - %s\n", p->stimer->st_schedid, p->callid);
+}
+
+
+/*! \brief Session-Timers: Process session refresh timeout event */
+static int proc_session_timer(const void *vp)
+{
+ struct sip_pvt *p = (struct sip_pvt *) vp;
+ int sendreinv = FALSE;
+
+ if (!p->stimer) {
+ ast_log(LOG_WARNING, "Null stimer in proc_session_timer - %s\n", p->callid);
+ return 0;
+ }
+
+ ast_debug(2, "Session timer expired: %d - %s\n", p->stimer->st_schedid, p->callid);
+
+ if (!p->owner) {
+ if (p->stimer->st_active == TRUE) {
+ stop_session_timer(p);
+ }
+ return 0;
+ }
+
+ if ((p->stimer->st_active != TRUE) || (p->owner->_state != AST_STATE_UP)) {
+ return 0;
+ }
+
+ switch (p->stimer->st_ref) {
+ case SESSION_TIMER_REFRESHER_UAC:
+ if (p->outgoing_call == TRUE) {
+ sendreinv = TRUE;
+ }
+ break;
+ case SESSION_TIMER_REFRESHER_UAS:
+ if (p->outgoing_call != TRUE) {
+ sendreinv = TRUE;
+ }
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown session refresher %d\n", p->stimer->st_ref);
+ return -1;
+ }
+
+ if (sendreinv == TRUE) {
+ transmit_reinvite_with_sdp(p, FALSE, TRUE);
+ } else {
+ p->stimer->st_expirys++;
+ if (p->stimer->st_expirys >= 2) {
+ ast_log(LOG_WARNING, "Session-Timer expired - %s\n", p->callid);
+ stop_session_timer(p);
+
+ while (p->owner && ast_channel_trylock(p->owner)) {
+ sip_pvt_unlock(p);
+ usleep(1);
+ sip_pvt_lock(p);
+ }
+
+ ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+ ast_channel_unlock(p->owner);
+ }
+ }
+ return 1;
+}
+
+
+/* Session-Timers: Function for parsing Min-SE header */
+int parse_minse (const char *p_hdrval, int *const p_interval)
+{
+ if (ast_strlen_zero(p_hdrval)) {
+ ast_log(LOG_WARNING, "Null Min-SE header\n");
+ return -1;
+ }
+
+ *p_interval = 0;
+ p_hdrval = ast_skip_blanks(p_hdrval);
+ if (!sscanf(p_hdrval, "%d", p_interval)) {
+ ast_log(LOG_WARNING, "Parsing of Min-SE header failed %s\n", p_hdrval);
+ return -1;
+ }
+
+ ast_debug(2, "Received Min-SE: %d\n", *p_interval);
+ return 0;
+}
+
+
+/* Session-Timers: Function for parsing Session-Expires header */
+int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher *const p_ref)
+{
+ char *p_token;
+ int ref_idx;
+ char *p_se_hdr;
+
+ if (ast_strlen_zero(p_hdrval)) {
+ ast_log(LOG_WARNING, "Null Session-Expires header\n");
+ return -1;
+ }
+
+ *p_ref = SESSION_TIMER_REFRESHER_AUTO;
+ *p_interval = 0;
+
+ p_se_hdr = ast_strdupa(p_hdrval);
+ p_se_hdr = ast_skip_blanks(p_se_hdr);
+
+ while ((p_token = strsep(&p_se_hdr, ";"))) {
+ p_token = ast_skip_blanks(p_token);
+ if (!sscanf(p_token, "%d", p_interval)) {
+ ast_log(LOG_WARNING, "Parsing of Session-Expires failed\n");
+ return -1;
+ }
+
+ ast_debug(2, "Session-Expires: %d\n", *p_interval);
+
+ if (!p_se_hdr)
+ continue;
+
+ ref_idx = strlen("refresher=");
+ if (!strncasecmp(p_se_hdr, "refresher=", ref_idx)) {
+ p_se_hdr += ref_idx;
+ p_se_hdr = ast_skip_blanks(p_se_hdr);
+
+ if (!strncasecmp(p_se_hdr, "uac", strlen("uac"))) {
+ *p_ref = SESSION_TIMER_REFRESHER_UAC;
+ ast_debug(2, "Refresher: UAC\n");
+ } else if (!strncasecmp(p_se_hdr, "uas", strlen("uas"))) {
+ *p_ref = SESSION_TIMER_REFRESHER_UAS;
+ ast_debug(2, "Refresher: UAS\n");
+ } else {
+ ast_log(LOG_WARNING, "Invalid refresher value %s\n", p_se_hdr);
+ return -1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/*! \brief Handle 422 response to INVITE with session-timer requested
+
+ Session-Timers: An INVITE originated by Asterisk that asks for session-timers support
+ from the UAS can result into a 422 response. This is how a UAS or an intermediary proxy
+ server tells Asterisk that the session refresh interval offered by Asterisk is too low
+ for them. The proc_422_rsp() function handles a 422 response. It extracts the Min-SE
+ header that comes back in 422 and sends a new INVITE accordingly. */
+static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp)
+{
+ int rtn;
+ const char *p_hdrval;
+ int minse;
+
+ p_hdrval = get_header(rsp, "Min-SE");
+ if (ast_strlen_zero(p_hdrval)) {
+ ast_log(LOG_WARNING, "422 response without a Min-SE header %s\n", p_hdrval);
+ return;
+ }
+ rtn = parse_minse(p_hdrval, &minse);
+ if (rtn != 0) {
+ ast_log(LOG_WARNING, "Parsing of Min-SE header failed %s\n", p_hdrval);
+ return;
+ }
+ p->stimer->st_interval = minse;
+ transmit_invite(p, SIP_INVITE, 1, 2);
+}
+
+
+/*! \brief Get Max or Min SE (session timer expiry)
+ \param max if true, get max se, otherwise min se
+*/
+int st_get_se(struct sip_pvt *p, int max)
+{
+ if (max == TRUE) {
+ if (p->stimer->st_cached_max_se) {
+ return p->stimer->st_cached_max_se;
+ } else {
+ if (p->username) {
+ struct sip_user *up = find_user(p->username, 1);
+ if (up) {
+ p->stimer->st_cached_max_se = up->stimer.st_max_se;
+ return (p->stimer->st_cached_max_se);
+ }
+ }
+ if (p->peername) {
+ struct sip_peer *pp = find_peer(p->peername, NULL, 1);
+ if (pp) {
+ p->stimer->st_cached_max_se = pp->stimer.st_max_se;
+ return (p->stimer->st_cached_max_se);
+ }
+ }
+ }
+ p->stimer->st_cached_max_se = global_max_se;
+ return (p->stimer->st_cached_max_se);
+ } else {
+ if (p->stimer->st_cached_min_se) {
+ return p->stimer->st_cached_min_se;
+ } else {
+ if (p->username) {
+ struct sip_user *up = find_user(p->username, 1);
+ if (up) {
+ p->stimer->st_cached_min_se = up->stimer.st_min_se;
+ return (p->stimer->st_cached_min_se);
+ }
+ }
+ if (p->peername) {
+ struct sip_peer *pp = find_peer(p->peername, NULL, 1);
+ if (pp) {
+ p->stimer->st_cached_min_se = pp->stimer.st_min_se;
+ return (p->stimer->st_cached_min_se);
+ }
+ }
+ }
+ p->stimer->st_cached_min_se = global_min_se;
+ return (p->stimer->st_cached_min_se);
+ }
+}
+
+
+/*! \brief Get the entity (UAC or UAS) that's acting as the session-timer refresher
+ \param sip_pvt pointer to the SIP dialog
+*/
+enum st_refresher st_get_refresher(struct sip_pvt *p)
+{
+ if (p->stimer->st_cached_ref != SESSION_TIMER_REFRESHER_AUTO)
+ return p->stimer->st_cached_ref;
+
+ if (p->username) {
+ struct sip_user *up = find_user(p->username, 1);
+ if (up) {
+ p->stimer->st_cached_ref = up->stimer.st_ref;
+ return up->stimer.st_ref;
+ }
+ }
+
+ if (p->peername) {
+ struct sip_peer *pp = find_peer(p->peername, NULL, 1);
+ if (pp) {
+ p->stimer->st_cached_ref = pp->stimer.st_ref;
+ return pp->stimer.st_ref;
+ }
+ }
+
+ p->stimer->st_cached_ref = global_st_refresher;
+ return global_st_refresher;
+}
+
+
+/*! \brief Get the session-timer mode
+ \param sip_pvt pointer to the SIP dialog
+*/
+enum st_mode st_get_mode(struct sip_pvt *p)
+{
+ if (!p->stimer)
+ sip_st_alloc(p);
+
+ if (p->stimer->st_cached_mode != SESSION_TIMER_MODE_INVALID)
+ return p->stimer->st_cached_mode;
+
+ if (p->username) {
+ struct sip_user *up = find_user(p->username, 1);
+ if (up) {
+ p->stimer->st_cached_mode = up->stimer.st_mode_oper;
+ return up->stimer.st_mode_oper;
+ }
+ }
+ if (p->peername) {
+ struct sip_peer *pp = find_peer(p->peername, NULL, 1);
+ if (pp) {
+ p->stimer->st_cached_mode = pp->stimer.st_mode_oper;
+ return pp->stimer.st_mode_oper;
+ }
+ }
+
+ p->stimer->st_cached_mode = global_st_mode;
+ return global_st_mode;
+}
+
+
+/*! \brief React to lack of answer to Qualify poke */
+static int sip_poke_noanswer(const void *data)
+{
+ struct sip_peer *peer = (struct sip_peer *)data;
+
+ peer->pokeexpire = -1;
+ if (peer->lastms > -1) {
+ ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Last qualify: %d\n", peer->name, peer->lastms);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1);
+ if (global_regextenonqualify)
+ register_peer_exten(peer, FALSE);
+ }
+ if (peer->call)
+ peer->call = sip_destroy(peer->call);
+ peer->lastms = -1;
+ ast_device_state_changed("SIP/%s", peer->name);
+ /* Try again quickly */
+ peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched,
+ DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
+ return 0;
+}
+
+/*! \brief Check availability of peer, also keep NAT open
+\note This is done with the interval in qualify= configuration option
+ Default is 2 seconds */
+static int sip_poke_peer(struct sip_peer *peer)
+{
+ struct sip_pvt *p;
+ int xmitres = 0;
+
+ if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
+ /* IF we have no IP, or this isn't to be monitored, return
+ immediately after clearing things out */
+ if (peer->pokeexpire > -1)
+ ast_sched_del(sched, peer->pokeexpire);
+ peer->lastms = 0;
+ peer->pokeexpire = -1;
+ peer->call = NULL;
+ return 0;
+ }
+ if (peer->call) {
+ if (sipdebug)
+ ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
+ peer->call = sip_destroy(peer->call);
+ }
+ if (!(p = peer->call = sip_alloc(NULL, NULL, 0, SIP_OPTIONS)))
+ return -1;
+
+ p->sa = peer->addr;
+ p->recv = peer->addr;
+ p->socket = peer->socket;
+ ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+
+ /* Send OPTIONs to peer's fullcontact */
+ if (!ast_strlen_zero(peer->fullcontact))
+ ast_string_field_set(p, fullcontact, peer->fullcontact);
+
+ if (!ast_strlen_zero(peer->tohost))
+ ast_string_field_set(p, tohost, peer->tohost);
+ else
+ ast_string_field_set(p, tohost, ast_inet_ntoa(peer->addr.sin_addr));
+
+ /* Recalculate our side, and recalculate Call ID */
+ ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
+ build_via(p);
+ build_callid_pvt(p);
+
+ if (peer->pokeexpire > -1)
+ ast_sched_del(sched, peer->pokeexpire);
+ p->relatedpeer = peer;
+ ast_set_flag(&p->flags[0], SIP_OUTGOING);
+#ifdef VOCAL_DATA_HACK
+ ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
+ xmitres = transmit_invite(p, SIP_INVITE, 0, 2);
+#else
+ xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2);
+#endif
+ peer->ps = ast_tvnow();
+ if (xmitres == XMIT_ERROR)
+ sip_poke_noanswer(peer); /* Immediately unreachable, network problems */
+ else {
+ peer->pokeexpire = ast_sched_replace(peer->pokeexpire, sched,
+ peer->maxms * 2, sip_poke_noanswer, peer);
+ }
+
+ return 0;
+}
+
+/*! \brief Part of PBX channel interface
+\note
+\par Return values:---
+
+ If we have qualify on and the device is not reachable, regardless of registration
+ state we return AST_DEVICE_UNAVAILABLE
+
+ For peers with call limit:
+ - not registered AST_DEVICE_UNAVAILABLE
+ - registered, no call AST_DEVICE_NOT_INUSE
+ - registered, active calls AST_DEVICE_INUSE
+ - registered, call limit reached AST_DEVICE_BUSY
+ - registered, onhold AST_DEVICE_ONHOLD
+ - registered, ringing AST_DEVICE_RINGING
+
+ For peers without call limit:
+ - not registered AST_DEVICE_UNAVAILABLE
+ - registered AST_DEVICE_NOT_INUSE
+ - fixed IP (!dynamic) AST_DEVICE_NOT_INUSE
+
+ Peers that does not have a known call and can't be reached by OPTIONS
+ - unreachable AST_DEVICE_UNAVAILABLE
+
+ If we return AST_DEVICE_UNKNOWN, the device state engine will try to find
+ out a state by walking the channel list.
+
+ The queue system (\ref app_queue.c) treats a member as "active"
+ if devicestate is != AST_DEVICE_UNAVAILBALE && != AST_DEVICE_INVALID
+
+ When placing a call to the queue member, queue system sets a member to busy if
+ != AST_DEVICE_NOT_INUSE and != AST_DEVICE_UNKNOWN
+
+*/
+static int sip_devicestate(void *data)
+{
+ char *host;
+ char *tmp;
+ struct sip_peer *p;
+
+ int res = AST_DEVICE_INVALID;
+
+ /* make sure data is not null. Maybe unnecessary, but better be safe */
+ host = ast_strdupa(data ? data : "");
+ if ((tmp = strchr(host, '@')))
+ host = tmp + 1;
+
+ ast_debug(3, "Checking device state for peer %s\n", host);
+
+ if ((p = find_peer(host, NULL, 1))) {
+ if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) {
+ /* we have an address for the peer */
+
+ /* Check status in this order
+ - Hold
+ - Ringing
+ - Busy (enforced only by call limit)
+ - Inuse (we have a call)
+ - Unreachable (qualify)
+ If we don't find any of these state, report AST_DEVICE_NOT_INUSE
+ for registered devices */
+
+ if (p->onHold)
+ /* First check for hold or ring states */
+ res = AST_DEVICE_ONHOLD;
+ else if (p->inRinging) {
+ if (p->inRinging == p->inUse)
+ res = AST_DEVICE_RINGING;
+ else
+ res = AST_DEVICE_RINGINUSE;
+ } else if (p->call_limit && (p->inUse == p->call_limit))
+ /* check call limit */
+ res = AST_DEVICE_BUSY;
+ else if (p->call_limit && p->busy_level && p->inUse >= p->busy_level)
+ /* We're forcing busy before we've reached the call limit */
+ res = AST_DEVICE_BUSY;
+ else if (p->call_limit && p->inUse)
+ /* Not busy, but we do have a call */
+ res = AST_DEVICE_INUSE;
+ else if (p->maxms && ((p->lastms > p->maxms) || (p->lastms < 0)))
+ /* We don't have a call. Are we reachable at all? Requires qualify= */
+ res = AST_DEVICE_UNAVAILABLE;
+ else /* Default reply if we're registered and have no other data */
+ res = AST_DEVICE_NOT_INUSE;
+ } else {
+ /* there is no address, it's unavailable */
+ res = AST_DEVICE_UNAVAILABLE;
+ }
+ unref_peer(p);
+ } else {
+ res = AST_DEVICE_UNKNOWN;
+ }
+
+ return res;
+}
+
+/*! \brief PBX interface function -build SIP pvt structure
+ SIP calls initiated by the PBX arrive here
+
+ SIP Dial string syntax
+ SIP/exten@host!dnid
+ or SIP/host/exten!dnid
+ or SIP/host!dnid
+*/
+static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause)
+{
+ struct sip_pvt *p;
+ struct ast_channel *tmpc = NULL;
+ char *ext, *host;
+ char tmp[256];
+ char *dest = data;
+ char *dnid;
+ int oldformat = format;
+
+ /* mask request with some set of allowed formats.
+ * XXX this needs to be fixed.
+ * The original code uses AST_FORMAT_AUDIO_MASK, but it is
+ * unclear what to use here. We have global_capabilities, which is
+ * configured from sip.conf, and sip_tech.capabilities, which is
+ * hardwired to all audio formats.
+ */
+ format &= AST_FORMAT_AUDIO_MASK;
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n", ast_getformatname(oldformat), ast_getformatname(global_capability));
+ *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; /* Can't find codec to connect to host */
+ return NULL;
+ }
+ ast_debug(1, "Asked to create a SIP channel with formats: %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), oldformat));
+
+ if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) {
+ ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", dest);
+ *cause = AST_CAUSE_SWITCH_CONGESTION;
+ return NULL;
+ }
+
+ p->outgoing_call = TRUE;
+
+ if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
+ sip_destroy(p);
+ ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n");
+ *cause = AST_CAUSE_SWITCH_CONGESTION;
+ return NULL;
+ }
+
+ /* Save the destination, the SIP dial string */
+ ast_copy_string(tmp, dest, sizeof(tmp));
+
+
+ /* Find DNID and take it away */
+ dnid = strchr(tmp, '!');
+ if (dnid != NULL) {
+ *dnid++ = '\0';
+ ast_string_field_set(p, todnid, dnid);
+ }
+
+ /* Find at sign - @ */
+ host = strchr(tmp, '@');
+ if (host) {
+ *host++ = '\0';
+ ext = tmp;
+ } else {
+ ext = strchr(tmp, '/');
+ if (ext)
+ *ext++ = '\0';
+ host = tmp;
+ }
+
+ /* We now have
+ host = peer name, DNS host name or DNS domain (for SRV)
+ ext = extension (user part of URI)
+ dnid = destination of the call (applies to the To: header)
+ */
+ if (create_addr(p, host)) {
+ *cause = AST_CAUSE_UNREGISTERED;
+ ast_debug(3, "Cant create SIP call - target device not registred\n");
+ sip_destroy(p);
+ return NULL;
+ }
+ if (ast_strlen_zero(p->peername) && ext)
+ ast_string_field_set(p, peername, ext);
+ /* Recalculate our side, and recalculate Call ID */
+ ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
+ build_via(p);
+ build_callid_pvt(p);
+
+ /* We have an extension to call, don't use the full contact here */
+ /* This to enable dialing registered peers with extension dialling,
+ like SIP/peername/extension
+ SIP/peername will still use the full contact
+ */
+ if (ext) {
+ ast_string_field_set(p, username, ext);
+ ast_string_field_set(p, fullcontact, NULL);
+ }
+#if 0
+ printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host);
+#endif
+ p->prefcodec = oldformat; /* Format for this call */
+ p->jointcapability = oldformat;
+ sip_pvt_lock(p);
+ tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */
+ if (global_callevents)
+ manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
+ "Channel: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",
+ p->owner? p->owner->name : "", "SIP", p->callid, p->fullcontact, p->peername);
+ sip_pvt_unlock(p);
+ if (!tmpc)
+ sip_destroy(p);
+ ast_update_use_count();
+ restart_monitor();
+ return tmpc;
+}
+
+/*! Parse insecure= setting in sip.conf and set flags according to setting */
+static void set_insecure_flags (struct ast_flags *flags, const char *value, int lineno)
+{
+ if (ast_strlen_zero(value))
+ return;
+
+ if (!ast_false(value)) {
+ char buf[64];
+ char *word, *next;
+
+ ast_copy_string(buf, value, sizeof(buf));
+ next = buf;
+ while ((word = strsep(&next, ","))) {
+ if (!strcasecmp(word, "port"))
+ ast_set_flag(&flags[0], SIP_INSECURE_PORT);
+ else if (!strcasecmp(word, "invite"))
+ ast_set_flag(&flags[0], SIP_INSECURE_INVITE);
+ else
+ ast_log(LOG_WARNING, "Unknown insecure mode '%s' on line %d\n", value, lineno);
+ }
+ }
+}
+
+/*!
+ \brief Handle flag-type options common to configuration of devices - users and peers
+ \param flags array of two struct ast_flags
+ \param mask array of two struct ast_flags
+ \param v linked list of config variables to process
+ \returns non-zero if any config options were handled, zero otherwise
+*/
+static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v)
+{
+ int res = 1;
+
+ if (!strcasecmp(v->name, "trustrpid")) {
+ ast_set_flag(&mask[0], SIP_TRUSTRPID);
+ ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID);
+ } else if (!strcasecmp(v->name, "sendrpid")) {
+ ast_set_flag(&mask[0], SIP_SENDRPID);
+ ast_set2_flag(&flags[0], ast_true(v->value), SIP_SENDRPID);
+ } else if (!strcasecmp(v->name, "g726nonstandard")) {
+ ast_set_flag(&mask[0], SIP_G726_NONSTANDARD);
+ ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD);
+ } else if (!strcasecmp(v->name, "useclientcode")) {
+ ast_set_flag(&mask[0], SIP_USECLIENTCODE);
+ ast_set2_flag(&flags[0], ast_true(v->value), SIP_USECLIENTCODE);
+ } else if (!strcasecmp(v->name, "dtmfmode")) {
+ ast_set_flag(&mask[0], SIP_DTMF);
+ ast_clear_flag(&flags[0], SIP_DTMF);
+ if (!strcasecmp(v->value, "inband"))
+ ast_set_flag(&flags[0], SIP_DTMF_INBAND);
+ else if (!strcasecmp(v->value, "rfc2833"))
+ ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
+ else if (!strcasecmp(v->value, "info"))
+ ast_set_flag(&flags[0], SIP_DTMF_INFO);
+ else if (!strcasecmp(v->value, "shortinfo"))
+ ast_set_flag(&flags[0], SIP_DTMF_SHORTINFO);
+ else if (!strcasecmp(v->value, "auto"))
+ ast_set_flag(&flags[0], SIP_DTMF_AUTO);
+ else {
+ ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using rfc2833\n", v->value, v->lineno);
+ ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
+ }
+ } else if (!strcasecmp(v->name, "nat")) {
+ ast_set_flag(&mask[0], SIP_NAT);
+ ast_clear_flag(&flags[0], SIP_NAT);
+ if (!strcasecmp(v->value, "never"))
+ ast_set_flag(&flags[0], SIP_NAT_NEVER);
+ else if (!strcasecmp(v->value, "route"))
+ ast_set_flag(&flags[0], SIP_NAT_ROUTE);
+ else if (ast_true(v->value))
+ ast_set_flag(&flags[0], SIP_NAT_ALWAYS);
+ else
+ ast_set_flag(&flags[0], SIP_NAT_RFC3581);
+ } else if (!strcasecmp(v->name, "canreinvite")) {
+ ast_set_flag(&mask[0], SIP_REINVITE);
+ ast_clear_flag(&flags[0], SIP_REINVITE);
+ if (ast_true(v->value)) {
+ ast_set_flag(&flags[0], SIP_CAN_REINVITE | SIP_CAN_REINVITE_NAT);
+ } else if (!ast_false(v->value)) {
+ char buf[64];
+ char *word, *next = buf;
+
+ ast_copy_string(buf, v->value, sizeof(buf));
+ while ((word = strsep(&next, ","))) {
+ if (!strcasecmp(word, "update")) {
+ ast_set_flag(&flags[0], SIP_REINVITE_UPDATE | SIP_CAN_REINVITE);
+ } else if (!strcasecmp(word, "nonat")) {
+ ast_set_flag(&flags[0], SIP_CAN_REINVITE);
+ ast_clear_flag(&flags[0], SIP_CAN_REINVITE_NAT);
+ } else {
+ ast_log(LOG_WARNING, "Unknown canreinvite mode '%s' on line %d\n", v->value, v->lineno);
+ }
+ }
+ }
+ } else if (!strcasecmp(v->name, "insecure")) {
+ ast_set_flag(&mask[0], SIP_INSECURE);
+ ast_clear_flag(&flags[0], SIP_INSECURE);
+ set_insecure_flags(&flags[0], v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "progressinband")) {
+ ast_set_flag(&mask[0], SIP_PROG_INBAND);
+ ast_clear_flag(&flags[0], SIP_PROG_INBAND);
+ if (ast_true(v->value))
+ ast_set_flag(&flags[0], SIP_PROG_INBAND_YES);
+ else if (strcasecmp(v->value, "never"))
+ ast_set_flag(&flags[0], SIP_PROG_INBAND_NO);
+ } else if (!strcasecmp(v->name, "promiscredir")) {
+ ast_set_flag(&mask[0], SIP_PROMISCREDIR);
+ ast_set2_flag(&flags[0], ast_true(v->value), SIP_PROMISCREDIR);
+ } else if (!strcasecmp(v->name, "videosupport")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_VIDEOSUPPORT);
+ } else if (!strcasecmp(v->name, "textsupport")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_TEXTSUPPORT);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_TEXTSUPPORT);
+ res = 1;
+ } else if (!strcasecmp(v->name, "allowoverlap")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP);
+ } else if (!strcasecmp(v->name, "allowsubscribe")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE);
+ } else if (!strcasecmp(v->name, "t38pt_udptl")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_UDPTL);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_UDPTL);
+#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
+ } else if (!strcasecmp(v->name, "t38pt_rtp")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_RTP);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_RTP);
+ } else if (!strcasecmp(v->name, "t38pt_tcp")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP);
+#endif
+ } else if (!strcasecmp(v->name, "rfc2833compensate")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);
+ } else if (!strcasecmp(v->name, "buggymwi")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI);
+ } else
+ res = 0;
+
+ return res;
+}
+
+/*! \brief Add SIP domain to list of domains we are responsible for */
+static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context)
+{
+ struct domain *d;
+
+ if (ast_strlen_zero(domain)) {
+ ast_log(LOG_WARNING, "Zero length domain.\n");
+ return 1;
+ }
+
+ if (!(d = ast_calloc(1, sizeof(*d))))
+ return 0;
+
+ ast_copy_string(d->domain, domain, sizeof(d->domain));
+
+ if (!ast_strlen_zero(context))
+ ast_copy_string(d->context, context, sizeof(d->context));
+
+ d->mode = mode;
+
+ AST_LIST_LOCK(&domain_list);
+ AST_LIST_INSERT_TAIL(&domain_list, d, list);
+ AST_LIST_UNLOCK(&domain_list);
+
+ if (sipdebug)
+ ast_debug(1, "Added local SIP domain '%s'\n", domain);
+
+ return 1;
+}
+
+/*! \brief check_sip_domain: Check if domain part of uri is local to our server */
+static int check_sip_domain(const char *domain, char *context, size_t len)
+{
+ struct domain *d;
+ int result = 0;
+
+ AST_LIST_LOCK(&domain_list);
+ AST_LIST_TRAVERSE(&domain_list, d, list) {
+ if (strcasecmp(d->domain, domain))
+ continue;
+
+ if (len && !ast_strlen_zero(d->context))
+ ast_copy_string(context, d->context, len);
+
+ result = 1;
+ break;
+ }
+ AST_LIST_UNLOCK(&domain_list);
+
+ return result;
+}
+
+/*! \brief Clear our domain list (at reload) */
+static void clear_sip_domains(void)
+{
+ struct domain *d;
+
+ AST_LIST_LOCK(&domain_list);
+ while ((d = AST_LIST_REMOVE_HEAD(&domain_list, list)))
+ ast_free(d);
+ AST_LIST_UNLOCK(&domain_list);
+}
+
+
+/*! \brief Add realm authentication in list */
+static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, const char *configuration, int lineno)
+{
+ char authcopy[256];
+ char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
+ char *stringp;
+ struct sip_auth *a, *b, *auth;
+
+ if (ast_strlen_zero(configuration))
+ return authlist;
+
+ ast_debug(1, "Auth config :: %s\n", configuration);
+
+ ast_copy_string(authcopy, configuration, sizeof(authcopy));
+ stringp = authcopy;
+
+ username = stringp;
+ realm = strrchr(stringp, '@');
+ if (realm)
+ *realm++ = '\0';
+ if (ast_strlen_zero(username) || ast_strlen_zero(realm)) {
+ ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno);
+ return authlist;
+ }
+ stringp = username;
+ username = strsep(&stringp, ":");
+ if (username) {
+ secret = strsep(&stringp, ":");
+ if (!secret) {
+ stringp = username;
+ md5secret = strsep(&stringp,"#");
+ }
+ }
+ if (!(auth = ast_calloc(1, sizeof(*auth))))
+ return authlist;
+
+ ast_copy_string(auth->realm, realm, sizeof(auth->realm));
+ ast_copy_string(auth->username, username, sizeof(auth->username));
+ if (secret)
+ ast_copy_string(auth->secret, secret, sizeof(auth->secret));
+ if (md5secret)
+ ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret));
+
+ /* find the end of the list */
+ for (b = NULL, a = authlist; a ; b = a, a = a->next)
+ ;
+ if (b)
+ b->next = auth; /* Add structure add end of list */
+ else
+ authlist = auth;
+
+ ast_verb(3, "Added authentication for realm %s\n", realm);
+
+ return authlist;
+
+}
+
+/*! \brief Clear realm authentication list (at reload) */
+static int clear_realm_authentication(struct sip_auth *authlist)
+{
+ struct sip_auth *a = authlist;
+ struct sip_auth *b;
+
+ while (a) {
+ b = a;
+ a = a->next;
+ ast_free(b);
+ }
+
+ return 1;
+}
+
+/*! \brief Find authentication for a specific realm */
+static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm)
+{
+ struct sip_auth *a;
+
+ for (a = authlist; a; a = a->next) {
+ if (!strcasecmp(a->realm, realm))
+ break;
+ }
+
+ return a;
+}
+
+/*!
+ * implement the servar config line
+ */
+static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
+{
+ struct ast_variable *tmpvar = NULL;
+ char *varname = ast_strdupa(buf), *varval = NULL;
+
+ if ((varval = strchr(varname,'='))) {
+ *varval++ = '\0';
+ if ((tmpvar = ast_variable_new(varname, varval, ""))) {
+ tmpvar->next = list;
+ list = tmpvar;
+ }
+ }
+ return list;
+}
+
+/*! \brief Initiate a SIP user structure from configuration (configuration or realtime) */
+static struct sip_user *build_user(const char *name, struct ast_variable *v, int realtime)
+{
+ struct sip_user *user;
+ int format;
+ struct ast_ha *oldha = NULL;
+ struct ast_flags userflags[2] = {{(0)}};
+ struct ast_flags mask[2] = {{(0)}};
+
+
+ if (!(user = ast_calloc(1, sizeof(*user))))
+ return NULL;
+
+ suserobjs++;
+ ASTOBJ_INIT(user);
+ ast_copy_string(user->name, name, sizeof(user->name));
+ oldha = user->ha;
+ user->ha = NULL;
+ ast_copy_flags(&user->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&user->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+ user->capability = global_capability;
+ user->allowtransfer = global_allowtransfer;
+ user->maxcallbitrate = default_maxcallbitrate;
+ user->autoframing = global_autoframing;
+ if (global_callcounter)
+ user->call_limit=999;
+ user->prefs = default_prefs;
+ user->stimer.st_mode_oper = global_st_mode; /* Session-Timers */
+ user->stimer.st_ref = global_st_refresher;
+ user->stimer.st_min_se = global_min_se;
+ user->stimer.st_max_se = global_max_se;
+
+ /* set default context */
+ strcpy(user->context, default_context);
+ strcpy(user->language, default_language);
+ strcpy(user->mohinterpret, default_mohinterpret);
+ strcpy(user->mohsuggest, default_mohsuggest);
+ for (; v; v = v->next) {
+ if (handle_common_options(&userflags[0], &mask[0], v))
+ continue;
+ if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(user->context, v->value, sizeof(user->context));
+ } else if (!strcasecmp(v->name, "subscribecontext")) {
+ ast_copy_string(user->subscribecontext, v->value, sizeof(user->subscribecontext));
+ } else if (!strcasecmp(v->name, "setvar")) {
+ user->chanvars = add_var(v->value, user->chanvars);
+ } else if (!strcasecmp(v->name, "permit") ||
+ !strcasecmp(v->name, "deny")) {
+ int ha_error = 0;
+
+ user->ha = ast_append_ha(v->name, v->value, user->ha, &ha_error);
+ if (ha_error)
+ ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
+ } else if (!strcasecmp(v->name, "allowtransfer")) {
+ user->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
+ } else if (!strcasecmp(v->name, "secret")) {
+ ast_copy_string(user->secret, v->value, sizeof(user->secret));
+ } else if (!strcasecmp(v->name, "md5secret")) {
+ ast_copy_string(user->md5secret, v->value, sizeof(user->md5secret));
+ } else if (!strcasecmp(v->name, "callerid")) {
+ ast_callerid_split(v->value, user->cid_name, sizeof(user->cid_name), user->cid_num, sizeof(user->cid_num));
+ } else if (!strcasecmp(v->name, "fullname")) {
+ ast_copy_string(user->cid_name, v->value, sizeof(user->cid_name));
+ } else if (!strcasecmp(v->name, "cid_number")) {
+ ast_copy_string(user->cid_num, v->value, sizeof(user->cid_num));
+ } else if (!strcasecmp(v->name, "callgroup")) {
+ user->callgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "pickupgroup")) {
+ user->pickupgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(user->language, v->value, sizeof(user->language));
+ } else if (!strcasecmp(v->name, "mohinterpret")) {
+ ast_copy_string(user->mohinterpret, v->value, sizeof(user->mohinterpret));
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_copy_string(user->mohsuggest, v->value, sizeof(user->mohsuggest));
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode));
+ } else if (!strcasecmp(v->name, "callcounter")) {
+ user->call_limit = ast_true(v->value) ? 999 : 0;
+ } else if (!strcasecmp(v->name, "call-limit")) {
+ user->call_limit = atoi(v->value);
+ if (user->call_limit < 0)
+ user->call_limit = 0;
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ format = ast_cdr_amaflags2int(v->value);
+ if (format < 0) {
+ ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
+ } else {
+ user->amaflags = format;
+ }
+ } else if (!strcasecmp(v->name, "allow")) {
+ int error = ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, TRUE);
+ if (error)
+ ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ int error = ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, FALSE);
+ if (error)
+ ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
+ } else if (!strcasecmp(v->name, "autoframing")) {
+ user->autoframing = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callingpres")) {
+ user->callingpres = ast_parse_caller_presentation(v->value);
+ if (user->callingpres == -1)
+ user->callingpres = atoi(v->value);
+ } else if (!strcasecmp(v->name, "maxcallbitrate")) {
+ user->maxcallbitrate = atoi(v->value);
+ if (user->maxcallbitrate < 0)
+ user->maxcallbitrate = default_maxcallbitrate;
+ } else if (!strcasecmp(v->name, "session-timers")) {
+ int i = (int) str2stmode(v->value);
+ if (i < 0) {
+ ast_log(LOG_WARNING, "Invalid session-timers '%s' at line %d of %s\n", v->value, v->lineno, config);
+ user->stimer.st_mode_oper = global_st_mode;
+ } else {
+ user->stimer.st_mode_oper = i;
+ }
+ } else if (!strcasecmp(v->name, "session-expires")) {
+ if (sscanf(v->value, "%d", &user->stimer.st_max_se) != 1) {
+ ast_log(LOG_WARNING, "Invalid session-expires '%s' at line %d of %s\n", v->value, v->lineno, config);
+ user->stimer.st_max_se = global_max_se;
+ }
+ } else if (!strcasecmp(v->name, "session-minse")) {
+ if (sscanf(v->value, "%d", &user->stimer.st_min_se) != 1) {
+ ast_log(LOG_WARNING, "Invalid session-minse '%s' at line %d of %s\n", v->value, v->lineno, config);
+ user->stimer.st_min_se = global_min_se;
+ }
+ if (user->stimer.st_min_se < 90) {
+ ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < 90 secs\n", v->value, v->lineno, config);
+ user->stimer.st_min_se = global_min_se;
+ }
+ } else if (!strcasecmp(v->name, "session-refresher")) {
+ int i = (int) str2strefresher(v->value);
+ if (i < 0) {
+ ast_log(LOG_WARNING, "Invalid session-refresher '%s' at line %d of %s\n", v->value, v->lineno, config);
+ user->stimer.st_ref = global_st_refresher;
+ } else {
+ user->stimer.st_ref = i;
+ }
+ }
+
+ /* We can't just report unknown options here because this may be a
+ * type=friend entry. All user options are valid for a peer, but not
+ * the other way around. */
+ }
+ ast_copy_flags(&user->flags[0], &userflags[0], mask[0].flags);
+ ast_copy_flags(&user->flags[1], &userflags[1], mask[1].flags);
+ if (ast_test_flag(&user->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))
+ global_allowsubscribe = TRUE; /* No global ban any more */
+ ast_free_ha(oldha);
+ return user;
+}
+
+/*! \brief Set peer defaults before configuring specific configurations */
+static void set_peer_defaults(struct sip_peer *peer)
+{
+ if (peer->expire == 0) {
+ /* Don't reset expire or port time during reload
+ if we have an active registration
+ */
+ peer->expire = -1;
+ peer->pokeexpire = -1;
+ peer->addr.sin_port = htons(STANDARD_SIP_PORT);
+ }
+ ast_copy_flags(&peer->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
+ ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
+ strcpy(peer->context, default_context);
+ strcpy(peer->subscribecontext, default_subscribecontext);
+ strcpy(peer->language, default_language);
+ strcpy(peer->mohinterpret, default_mohinterpret);
+ strcpy(peer->mohsuggest, default_mohsuggest);
+ peer->addr.sin_family = AF_INET;
+ peer->defaddr.sin_family = AF_INET;
+ peer->capability = global_capability;
+ peer->maxcallbitrate = default_maxcallbitrate;
+ peer->rtptimeout = global_rtptimeout;
+ peer->rtpholdtimeout = global_rtpholdtimeout;
+ peer->rtpkeepalive = global_rtpkeepalive;
+ peer->allowtransfer = global_allowtransfer;
+ peer->autoframing = global_autoframing;
+ peer->qualifyfreq = global_qualifyfreq;
+ if (global_callcounter)
+ peer->call_limit=999;
+ strcpy(peer->vmexten, default_vmexten);
+ peer->secret[0] = '\0';
+ peer->md5secret[0] = '\0';
+ peer->cid_num[0] = '\0';
+ peer->cid_name[0] = '\0';
+ peer->fromdomain[0] = '\0';
+ peer->fromuser[0] = '\0';
+ peer->regexten[0] = '\0';
+ peer->callgroup = 0;
+ peer->pickupgroup = 0;
+ peer->maxms = default_qualify;
+ peer->prefs = default_prefs;
+ peer->socket.type = SIP_TRANSPORT_UDP;
+ peer->socket.fd = -1;
+ peer->stimer.st_mode_oper = global_st_mode; /* Session-Timers */
+ peer->stimer.st_ref = global_st_refresher;
+ peer->stimer.st_min_se = global_min_se;
+ peer->stimer.st_max_se = global_max_se;
+ peer->timer_t1 = global_t1;
+ peer->timer_b = global_timer_b;
+ clear_peer_mailboxes(peer);
+}
+
+/*! \brief Create temporary peer (used in autocreatepeer mode) */
+static struct sip_peer *temp_peer(const char *name)
+{
+ struct sip_peer *peer;
+
+ if (!(peer = ast_calloc(1, sizeof(*peer))))
+ return NULL;
+
+ apeerobjs++;
+ ASTOBJ_INIT(peer);
+ set_peer_defaults(peer);
+
+ ast_copy_string(peer->name, name, sizeof(peer->name));
+
+ peer->selfdestruct = TRUE;
+ peer->host_dynamic = TRUE;
+ peer->prefs = default_prefs;
+ reg_source_db(peer);
+
+ return peer;
+}
+
+static void add_peer_mailboxes(struct sip_peer *peer, const char *value)
+{
+ char *next, *mbox, *context;
+
+ next = ast_strdupa(value);
+
+ while ((mbox = context = strsep(&next, ","))) {
+ struct sip_mailbox *mailbox;
+
+ if (!(mailbox = ast_calloc(1, sizeof(*mailbox))))
+ continue;
+
+ strsep(&context, "@");
+ if (ast_strlen_zero(mbox)) {
+ ast_free(mailbox);
+ continue;
+ }
+ mailbox->mailbox = ast_strdup(mbox);
+ mailbox->context = ast_strdup(context);
+
+ AST_LIST_INSERT_TAIL(&peer->mailboxes, mailbox, entry);
+ }
+}
+
+/*! \brief Build peer from configuration (file or realtime static/dynamic) */
+static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime)
+{
+ struct sip_peer *peer = NULL;
+ struct ast_ha *oldha = NULL;
+ int found=0;
+ int firstpass=1;
+ int format=0; /* Ama flags */
+ time_t regseconds = 0;
+ struct ast_flags peerflags[2] = {{(0)}};
+ struct ast_flags mask[2] = {{(0)}};
+ char callback[256] = "";
+ const char *srvlookup = NULL;
+
+ if (!realtime)
+ /* Note we do NOT use find_peer here, to avoid realtime recursion */
+ /* We also use a case-sensitive comparison (unlike find_peer) so
+ that case changes made to the peer name will be properly handled
+ during reload
+ */
+ peer = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&peerl, name, name, 0, 0, strcmp);
+
+ if (peer) {
+ /* Already in the list, remove it and it will be added back (or FREE'd) */
+ found++;
+ if (!(peer->objflags & ASTOBJ_FLAG_MARKED))
+ firstpass = 0;
+ } else {
+ if (!(peer = ast_calloc(1, sizeof(*peer))))
+ return NULL;
+
+ if (realtime) {
+ rpeerobjs++;
+ ast_debug(3,"-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs);
+ } else
+ speerobjs++;
+ ASTOBJ_INIT(peer);
+ }
+ /* Note that our peer HAS had its reference count incrased */
+ if (firstpass) {
+ peer->lastmsgssent = -1;
+ oldha = peer->ha;
+ peer->ha = NULL;
+ set_peer_defaults(peer); /* Set peer defaults */
+ }
+ if (!found && name)
+ ast_copy_string(peer->name, name, sizeof(peer->name));
+
+ /* If we have channel variables, remove them (reload) */
+ if (peer->chanvars) {
+ ast_variables_destroy(peer->chanvars);
+ peer->chanvars = NULL;
+ /* XXX should unregister ? */
+ }
+
+ /* If we have realm authentication information, remove them (reload) */
+ clear_realm_authentication(peer->auth);
+ peer->auth = NULL;
+
+ for (; v || ((v = alt) && !(alt=NULL)); v = v->next) {
+ if (handle_common_options(&peerflags[0], &mask[0], v))
+ continue;
+ if (!strcasecmp(v->name, "transport")) {
+ if (!strcasecmp(v->value, "udp"))
+ peer->socket.type = SIP_TRANSPORT_UDP;
+ else if (!strcasecmp(v->value, "tcp"))
+ peer->socket.type = SIP_TRANSPORT_TCP;
+ else if (!strcasecmp(v->value, "tls"))
+ peer->socket.type = SIP_TRANSPORT_TLS;
+ } else if (realtime && !strcasecmp(v->name, "regseconds")) {
+ ast_get_time_t(v->value, &regseconds, 0, NULL);
+ } else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) {
+ inet_aton(v->value, &(peer->addr.sin_addr));
+ } else if (realtime && !strcasecmp(v->name, "name"))
+ ast_copy_string(peer->name, v->value, sizeof(peer->name));
+ else if (realtime && !strcasecmp(v->name, "fullcontact")) {
+ ast_copy_string(peer->fullcontact, v->value, sizeof(peer->fullcontact));
+ peer->rt_fromcontact = TRUE;
+ } else if (!strcasecmp(v->name, "secret"))
+ ast_copy_string(peer->secret, v->value, sizeof(peer->secret));
+ else if (!strcasecmp(v->name, "md5secret"))
+ ast_copy_string(peer->md5secret, v->value, sizeof(peer->md5secret));
+ else if (!strcasecmp(v->name, "auth"))
+ peer->auth = add_realm_authentication(peer->auth, v->value, v->lineno);
+ else if (!strcasecmp(v->name, "callerid")) {
+ ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num));
+ } else if (!strcasecmp(v->name, "fullname")) {
+ ast_copy_string(peer->cid_name, v->value, sizeof(peer->cid_name));
+ } else if (!strcasecmp(v->name, "cid_number")) {
+ ast_copy_string(peer->cid_num, v->value, sizeof(peer->cid_num));
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(peer->context, v->value, sizeof(peer->context));
+ } else if (!strcasecmp(v->name, "subscribecontext")) {
+ ast_copy_string(peer->subscribecontext, v->value, sizeof(peer->subscribecontext));
+ } else if (!strcasecmp(v->name, "fromdomain")) {
+ ast_copy_string(peer->fromdomain, v->value, sizeof(peer->fromdomain));
+ } else if (!strcasecmp(v->name, "usereqphone")) {
+ ast_set2_flag(&peer->flags[0], ast_true(v->value), SIP_USEREQPHONE);
+ } else if (!strcasecmp(v->name, "fromuser")) {
+ ast_copy_string(peer->fromuser, v->value, sizeof(peer->fromuser));
+ } else if (!strcasecmp(v->name, "outboundproxy")) {
+ char *port, *next, *force, *proxyname;
+ int forceopt = FALSE;
+ /* Set peer channel variable */
+ next = proxyname = ast_strdupa(v->value);
+ if ((port = strchr(proxyname, ':'))) {
+ *port++ = '\0';
+ next = port;
+ }
+ if ((force = strchr(next, ','))) {
+ *force++ = '\0';
+ forceopt = strcmp(force, "force");
+ }
+ /* Allocate proxy object */
+ peer->outboundproxy = proxy_allocate(proxyname, port, forceopt);
+ } else if (!strcasecmp(v->name, "host")) {
+ if (!strcasecmp(v->value, "dynamic")) {
+ /* They'll register with us */
+ if (!found || !peer->host_dynamic) {
+ /* Initialize stuff if this is a new peer, or if it used to
+ * not be dynamic before the reload. */
+ memset(&peer->addr.sin_addr, 0, 4);
+ if (peer->addr.sin_port) {
+ /* If we've already got a port, make it the default rather than absolute */
+ peer->defaddr.sin_port = peer->addr.sin_port;
+ peer->addr.sin_port = 0;
+ }
+ }
+ peer->host_dynamic = TRUE;
+ } else {
+ /* Non-dynamic. Make sure we become that way if we're not */
+ if (peer->expire > -1)
+ ast_sched_del(sched, peer->expire);
+ peer->expire = -1;
+ peer->host_dynamic = FALSE;
+ srvlookup = v->value;
+ }
+ } else if (!strcasecmp(v->name, "defaultip")) {
+ if (ast_get_ip(&peer->defaddr, v->value)) {
+ unref_peer(peer);
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
+ int ha_error = 0;
+
+ peer->ha = ast_append_ha(v->name, v->value, peer->ha, &ha_error);
+ if (ha_error)
+ ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
+ } else if (!strcasecmp(v->name, "port")) {
+ if (!realtime && peer->host_dynamic)
+ peer->defaddr.sin_port = htons(atoi(v->value));
+ else
+ peer->addr.sin_port = htons(atoi(v->value));
+ } else if (!strcasecmp(v->name, "callingpres")) {
+ peer->callingpres = ast_parse_caller_presentation(v->value);
+ if (peer->callingpres == -1)
+ peer->callingpres = atoi(v->value);
+ } else if (!strcasecmp(v->name, "username") | !strcmp(v->name, "defaultuser")) { /* "username" is deprecated */
+ ast_copy_string(peer->username, v->value, sizeof(peer->username));
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(peer->language, v->value, sizeof(peer->language));
+ } else if (!strcasecmp(v->name, "regexten")) {
+ ast_copy_string(peer->regexten, v->value, sizeof(peer->regexten));
+ } else if (!strcasecmp(v->name, "callbackextension")) {
+ ast_copy_string(callback, v->value, sizeof(callback));
+ } else if (!strcasecmp(v->name, "callcounter")) {
+ peer->call_limit = ast_true(v->value) ? 999 : 0;
+ } else if (!strcasecmp(v->name, "call-limit")) {
+ peer->call_limit = atoi(v->value);
+ if (peer->call_limit < 0)
+ peer->call_limit = 0;
+ } else if (!strcasecmp(v->name, "busylevel")) {
+ peer->busy_level = atoi(v->value);
+ if (peer->busy_level < 0)
+ peer->busy_level = 0;
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ format = ast_cdr_amaflags2int(v->value);
+ if (format < 0) {
+ ast_log(LOG_WARNING, "Invalid AMA Flags for peer: %s at line %d\n", v->value, v->lineno);
+ } else {
+ peer->amaflags = format;
+ }
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(peer->accountcode, v->value, sizeof(peer->accountcode));
+ } else if (!strcasecmp(v->name, "mohinterpret")) {
+ ast_copy_string(peer->mohinterpret, v->value, sizeof(peer->mohinterpret));
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_copy_string(peer->mohsuggest, v->value, sizeof(peer->mohsuggest));
+ } else if (!strcasecmp(v->name, "mailbox")) {
+ add_peer_mailboxes(peer, v->value);
+ } else if (!strcasecmp(v->name, "subscribemwi")) {
+ ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_SUBSCRIBEMWIONLY);
+ } else if (!strcasecmp(v->name, "vmexten")) {
+ ast_copy_string(peer->vmexten, v->value, sizeof(peer->vmexten));
+ } else if (!strcasecmp(v->name, "callgroup")) {
+ peer->callgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "allowtransfer")) {
+ peer->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
+ } else if (!strcasecmp(v->name, "pickupgroup")) {
+ peer->pickupgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "allow")) {
+ int error = ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, TRUE);
+ if (error)
+ ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ int error = ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, FALSE);
+ if (error)
+ ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
+ } else if (!strcasecmp(v->name, "registertrying")) {
+ ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_REGISTERTRYING);
+ } else if (!strcasecmp(v->name, "autoframing")) {
+ peer->autoframing = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "rtptimeout")) {
+ if ((sscanf(v->value, "%d", &peer->rtptimeout) != 1) || (peer->rtptimeout < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
+ peer->rtptimeout = global_rtptimeout;
+ }
+ } else if (!strcasecmp(v->name, "rtpholdtimeout")) {
+ if ((sscanf(v->value, "%d", &peer->rtpholdtimeout) != 1) || (peer->rtpholdtimeout < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
+ peer->rtpholdtimeout = global_rtpholdtimeout;
+ }
+ } else if (!strcasecmp(v->name, "rtpkeepalive")) {
+ if ((sscanf(v->value, "%d", &peer->rtpkeepalive) != 1) || (peer->rtpkeepalive < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d. Using default.\n", v->value, v->lineno);
+ peer->rtpkeepalive = global_rtpkeepalive;
+ }
+ } else if (!strcasecmp(v->name, "timert1")) {
+ if ((sscanf(v->value, "%d", &peer->timer_t1) != 1) || (peer->timer_t1 < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid T1 time at line %d. Using default.\n", v->value, v->lineno);
+ peer->timer_t1 = global_t1;
+ }
+ } else if (!strcasecmp(v->name, "timerb")) {
+ if ((sscanf(v->value, "%d", &peer->timer_b) != 1) || (peer->timer_b < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid Timer B time at line %d. Using default.\n", v->value, v->lineno);
+ peer->timer_b = global_timer_b;
+ }
+ } else if (!strcasecmp(v->name, "setvar")) {
+ peer->chanvars = add_var(v->value, peer->chanvars);
+ } else if (!strcasecmp(v->name, "qualify")) {
+ if (!strcasecmp(v->value, "no")) {
+ peer->maxms = 0;
+ } else if (!strcasecmp(v->value, "yes")) {
+ peer->maxms = default_qualify ? default_qualify : DEFAULT_MAXMS;
+ } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
+ ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno);
+ peer->maxms = 0;
+ }
+ } else if (!strcasecmp(v->name, "qualifyfreq")) {
+ int i;
+ if (sscanf(v->value, "%d", &i) == 1)
+ peer->qualifyfreq = i * 1000;
+ else {
+ ast_log(LOG_WARNING, "Invalid qualifyfreq number '%s' at line %d of %s\n",v->value, v->lineno, config);
+ peer->qualifyfreq = global_qualifyfreq;
+ }
+ } else if (!strcasecmp(v->name, "maxcallbitrate")) {
+ peer->maxcallbitrate = atoi(v->value);
+ if (peer->maxcallbitrate < 0)
+ peer->maxcallbitrate = default_maxcallbitrate;
+ } else if (!strcasecmp(v->name, "session-timers")) {
+ int i = (int) str2stmode(v->value);
+ if (i < 0) {
+ ast_log(LOG_WARNING, "Invalid session-timers '%s' at line %d of %s\n", v->value, v->lineno, config);
+ peer->stimer.st_mode_oper = global_st_mode;
+ } else {
+ peer->stimer.st_mode_oper = i;
+ }
+ } else if (!strcasecmp(v->name, "session-expires")) {
+ if (sscanf(v->value, "%d", &peer->stimer.st_max_se) != 1) {
+ ast_log(LOG_WARNING, "Invalid session-expires '%s' at line %d of %s\n", v->value, v->lineno, config);
+ peer->stimer.st_max_se = global_max_se;
+ }
+ } else if (!strcasecmp(v->name, "session-minse")) {
+ if (sscanf(v->value, "%d", &peer->stimer.st_min_se) != 1) {
+ ast_log(LOG_WARNING, "Invalid session-minse '%s' at line %d of %s\n", v->value, v->lineno, config);
+ peer->stimer.st_min_se = global_min_se;
+ }
+ if (peer->stimer.st_min_se < 90) {
+ ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < 90 secs\n", v->value, v->lineno, config);
+ peer->stimer.st_min_se = global_min_se;
+ }
+ } else if (!strcasecmp(v->name, "session-refresher")) {
+ int i = (int) str2strefresher(v->value);
+ if (i < 0) {
+ ast_log(LOG_WARNING, "Invalid session-refresher '%s' at line %d of %s\n", v->value, v->lineno, config);
+ peer->stimer.st_ref = global_st_refresher;
+ } else {
+ peer->stimer.st_ref = i;
+ }
+ }
+ }
+
+ if (srvlookup) {
+ if (ast_get_ip_or_srv(&peer->addr, srvlookup,
+ global_srvlookup ?
+ ((peer->socket.type & SIP_TRANSPORT_UDP) ? "_sip._udp" :
+ (peer->socket.type & SIP_TRANSPORT_TCP) ? "_sip._tcp" :
+ "_sip._tls")
+ : NULL)) {
+ unref_peer(peer);
+ return NULL;
+ }
+
+ ast_copy_string(peer->tohost, srvlookup, sizeof(peer->tohost));
+ }
+
+ if (!peer->addr.sin_port)
+ peer->addr.sin_port = htons(((peer->socket.type & SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT));
+
+ if (!peer->socket.port)
+ peer->socket.port = htons(((peer->socket.type & SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT));
+
+ if (!sip_cfg.ignore_regexpire && peer->host_dynamic && realtime) {
+ time_t nowtime = time(NULL);
+
+ if ((nowtime - regseconds) > 0) {
+ destroy_association(peer);
+ memset(&peer->addr, 0, sizeof(peer->addr));
+ ast_debug(1, "Bah, we're expired (%d/%d/%d)!\n", (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
+ }
+ }
+ ast_copy_flags(&peer->flags[0], &peerflags[0], mask[0].flags);
+ ast_copy_flags(&peer->flags[1], &peerflags[1], mask[1].flags);
+ if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))
+ global_allowsubscribe = TRUE; /* No global ban any more */
+ if (!found && peer->host_dynamic && !peer->is_realtime)
+ reg_source_db(peer);
+
+ /* If they didn't request that MWI is sent *only* on subscribe, go ahead and
+ * subscribe to it now. */
+ if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY) &&
+ !AST_LIST_EMPTY(&peer->mailboxes)) {
+ add_peer_mwi_subs(peer);
+ /* Send MWI from the event cache only. This is so we can send initial
+ * MWI if app_voicemail got loaded before chan_sip. If it is the other
+ * way, then we will get events when app_voicemail gets loaded. */
+ sip_send_mwi_to_peer(peer, NULL, 1);
+ }
+
+ ASTOBJ_UNMARK(peer);
+
+ ast_free_ha(oldha);
+ if (!ast_strlen_zero(callback)) { /* build string from peer info */
+ char *reg_string;
+
+ asprintf(&reg_string, "%s:%s@%s/%s", peer->username, peer->secret, peer->tohost, callback);
+ if (reg_string) {
+ sip_register(reg_string, 0); /* XXX TODO: count in registry_count */
+ ast_free(reg_string);
+ }
+ }
+ return peer;
+}
+
+/*! \brief Re-read SIP.conf config file
+\note This function reloads all config data, except for
+ active peers (with registrations). They will only
+ change configuration data at restart, not at reload.
+ SIP debug and recordhistory state will not change
+ */
+static int reload_config(enum channelreloadreason reason)
+{
+ struct ast_config *cfg, *ucfg;
+ struct ast_variable *v;
+ struct sip_peer *peer;
+ struct sip_user *user;
+ char *cat, *stringp, *context, *oldregcontext;
+ char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
+ struct ast_flags dummy[2];
+ struct ast_flags config_flags = { reason == CHANNEL_MODULE_LOAD ? 0 : CONFIG_FLAG_FILEUNCHANGED };
+ int auto_sip_domains = FALSE;
+ struct sockaddr_in old_bindaddr = bindaddr;
+ int registry_count = 0, peer_count = 0, user_count = 0;
+
+ cfg = ast_config_load(config, config_flags);
+
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+ ucfg = ast_config_load("users.conf", config_flags);
+ if (ucfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 1;
+ /* Must reread both files, because one changed */
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ cfg = ast_config_load(config, config_flags);
+ } else {
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ ucfg = ast_config_load("users.conf", config_flags);
+ }
+
+ /* Initialize tcp sockets */
+ memset(&sip_tcp_desc.sin, 0, sizeof(sip_tcp_desc.sin));
+ memset(&sip_tls_desc.sin, 0, sizeof(sip_tls_desc.sin));
+
+ sip_tcp_desc.sin.sin_family = AF_INET;
+
+ sip_tcp_desc.sin.sin_port = htons(STANDARD_SIP_PORT);
+ sip_tls_desc.sin.sin_port = htons(STANDARD_TLS_PORT);
+
+ if (reason != CHANNEL_MODULE_LOAD) {
+ ast_debug(4, "--------------- SIP reload started\n");
+
+ clear_realm_authentication(authl);
+ clear_sip_domains();
+ authl = NULL;
+
+ /* First, destroy all outstanding registry calls */
+ /* This is needed, since otherwise active registry entries will not be destroyed */
+ ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->call) {
+ ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
+ /* This will also remove references to the registry */
+ iterator->call = sip_destroy(iterator->call);
+ }
+ ASTOBJ_UNLOCK(iterator);
+
+ } while(0));
+
+ /* Then, actually destroy users and registry */
+ ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
+ ast_debug(4, "--------------- Done destroying user list\n");
+ ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
+ ast_debug(4, "--------------- Done destroying registry list\n");
+ ASTOBJ_CONTAINER_MARKALL(&peerl);
+ }
+
+ default_tls_cfg.certfile = ast_strdup(AST_CERTFILE); /*XXX Not sure if this is useful */
+ default_tls_cfg.cipher = ast_strdup("");
+ default_tls_cfg.cafile = ast_strdup("");
+ default_tls_cfg.capath = ast_strdup("");
+
+ /* Initialize copy of current global_regcontext for later use in removing stale contexts */
+ ast_copy_string(oldcontexts, global_regcontext, sizeof(oldcontexts));
+ oldregcontext = oldcontexts;
+
+ /* Clear all flags before setting default values */
+ /* Preserve debugging settings for console */
+ sipdebug &= sip_debug_console;
+ ast_clear_flag(&global_flags[0], AST_FLAGS_ALL);
+ ast_clear_flag(&global_flags[1], AST_FLAGS_ALL);
+
+ /* Reset IP addresses */
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ memset(&stunaddr, 0, sizeof(stunaddr));
+ memset(&internip, 0, sizeof(internip));
+ /* Free memory for local network address mask */
+ ast_free_ha(localaddr);
+ memset(&localaddr, 0, sizeof(localaddr));
+ memset(&externip, 0, sizeof(externip));
+ memset(&default_prefs, 0 , sizeof(default_prefs));
+ memset(&global_outboundproxy, 0, sizeof(struct sip_proxy));
+ global_outboundproxy.ip.sin_port = htons(STANDARD_SIP_PORT);
+ global_outboundproxy.ip.sin_family = AF_INET; /* Type of address: IPv4 */
+ ourport_tcp = STANDARD_SIP_PORT;
+ ourport_tls = STANDARD_TLS_PORT;
+ bindaddr.sin_port = htons(STANDARD_SIP_PORT);
+ externip.sin_port = htons(STANDARD_SIP_PORT);
+ global_srvlookup = DEFAULT_SRVLOOKUP;
+ global_tos_sip = DEFAULT_TOS_SIP;
+ global_tos_audio = DEFAULT_TOS_AUDIO;
+ global_tos_video = DEFAULT_TOS_VIDEO;
+ global_tos_text = DEFAULT_TOS_TEXT;
+ global_cos_sip = DEFAULT_COS_SIP;
+ global_cos_audio = DEFAULT_COS_AUDIO;
+ global_cos_video = DEFAULT_COS_VIDEO;
+ global_cos_text = DEFAULT_COS_TEXT;
+
+ externhost[0] = '\0'; /* External host name (for behind NAT DynDNS support) */
+ externexpire = 0; /* Expiration for DNS re-issuing */
+ externrefresh = 10;
+
+ /* Reset channel settings to default before re-configuring */
+ allow_external_domains = DEFAULT_ALLOW_EXT_DOM; /* Allow external invites */
+ global_regcontext[0] = '\0';
+ global_regextenonqualify = DEFAULT_REGEXTENONQUALIFY;
+ expiry = DEFAULT_EXPIRY;
+ global_notifyringing = DEFAULT_NOTIFYRINGING;
+ global_limitonpeers = FALSE; /*!< Match call limit on peers only */
+ global_notifyhold = FALSE; /*!< Keep track of hold status for a peer */
+ global_directrtpsetup = FALSE; /* Experimental feature, disabled by default */
+ global_alwaysauthreject = 0;
+ global_allowsubscribe = FALSE;
+ snprintf(global_useragent, sizeof(global_useragent), "%s %s", DEFAULT_USERAGENT, ast_get_version());
+ snprintf(global_sdpsession, sizeof(global_sdpsession), "%s %s", DEFAULT_SDPSESSION, ast_get_version());
+ snprintf(global_sdpowner, sizeof(global_sdpowner), "%s", DEFAULT_SDPOWNER);
+ ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime));
+ ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
+ ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid));
+ compactheaders = DEFAULT_COMPACTHEADERS;
+ global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
+ global_regattempts_max = 0;
+ pedanticsipchecking = DEFAULT_PEDANTIC;
+ autocreatepeer = DEFAULT_AUTOCREATEPEER;
+ global_autoframing = 0;
+ global_allowguest = DEFAULT_ALLOWGUEST;
+ global_callcounter = DEFAULT_CALLCOUNTER;
+ global_match_auth_username = FALSE; /*!< Match auth username if available instead of From: Default off. */
+ global_rtptimeout = 0;
+ global_rtpholdtimeout = 0;
+ global_rtpkeepalive = 0;
+ global_allowtransfer = TRANSFER_OPENFORALL; /* Merrily accept all transfers by default */
+ global_rtautoclear = 120;
+ ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE); /* Default for peers, users: TRUE */
+ ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP); /* Default for peers, users: TRUE */
+ sip_cfg.peer_rtupdate = TRUE;
+
+ global_st_mode = SESSION_TIMER_MODE_ACCEPT; /* Session-Timers */
+ global_st_refresher = SESSION_TIMER_REFRESHER_UAS;
+ global_min_se = DEFAULT_MIN_SE;
+ global_max_se = DEFAULT_MAX_SE;
+
+ /* Initialize some reasonable defaults at SIP reload (used both for channel and as default for peers and users */
+ ast_copy_string(default_context, DEFAULT_CONTEXT, sizeof(default_context));
+ default_subscribecontext[0] = '\0';
+ default_language[0] = '\0';
+ default_fromdomain[0] = '\0';
+ default_qualify = DEFAULT_QUALIFY;
+ default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
+ ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
+ ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest));
+ ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
+ ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */
+ ast_set_flag(&global_flags[0], SIP_NAT_RFC3581); /*!< NAT support if requested by device with rport */
+ ast_set_flag(&global_flags[0], SIP_CAN_REINVITE); /*!< Allow re-invites */
+
+ /* Debugging settings, always default to off */
+ dumphistory = FALSE;
+ recordhistory = FALSE;
+ sipdebug &= ~sip_debug_config;
+
+ /* Misc settings for the channel */
+ global_relaxdtmf = FALSE;
+ global_callevents = FALSE;
+ global_t1 = SIP_TIMER_T1;
+ global_timer_b = 64 * SIP_TIMER_T1;
+ global_t1min = DEFAULT_T1MIN;
+ global_qualifyfreq = DEFAULT_QUALIFYFREQ;
+
+ global_matchexterniplocally = FALSE;
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT);
+ ast_clear_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT);
+
+ /* Read the [general] config section of sip.conf (or from realtime config) */
+ for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
+ if (handle_common_options(&global_flags[0], &dummy[0], v))
+ continue;
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+ continue;
+
+ /* Create the dialogs list */
+ if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(default_context, v->value, sizeof(default_context));
+ } else if (!strcasecmp(v->name, "subscribecontext")) {
+ ast_copy_string(default_subscribecontext, v->value, sizeof(default_subscribecontext));
+ } else if (!strcasecmp(v->name, "callcounter")) {
+ global_callcounter = ast_true(v->value) ? 1 : 0;
+ } else if (!strcasecmp(v->name, "allowguest")) {
+ global_allowguest = ast_true(v->value) ? 1 : 0;
+ } else if (!strcasecmp(v->name, "realm")) {
+ ast_copy_string(global_realm, v->value, sizeof(global_realm));
+ } else if (!strcasecmp(v->name, "useragent")) {
+ ast_copy_string(global_useragent, v->value, sizeof(global_useragent));
+ ast_debug(1, "Setting SIP channel User-Agent Name to %s\n", global_useragent);
+ } else if (!strcasecmp(v->name, "sdpsession")) {
+ ast_copy_string(global_sdpsession, v->value, sizeof(global_sdpsession));
+ } else if (!strcasecmp(v->name, "sdpowner")) {
+ /* Field cannot contain spaces */
+ if (!strstr(v->value, " "))
+ ast_copy_string(global_sdpowner, v->value, sizeof(global_sdpowner));
+ else
+ ast_log(LOG_WARNING, "'%s' must not contain spaces at line %d. Using default.\n", v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "allowtransfer")) {
+ global_allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
+ } else if (!strcasecmp(v->name, "rtcachefriends")) {
+ ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTCACHEFRIENDS);
+ } else if (!strcasecmp(v->name, "rtsavesysname")) {
+ sip_cfg.rtsave_sysname = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "rtupdate")) {
+ sip_cfg.peer_rtupdate = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "ignoreregexpire")) {
+ sip_cfg.ignore_regexpire = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "t1min")) {
+ global_t1min = atoi(v->value);
+ } else if (!strcasecmp(v->name, "tcpenable")) {
+ sip_tcp_desc.sin.sin_family = ast_false(v->value) ? 0 : AF_INET;
+ } else if (!strcasecmp(v->name, "tcpbindaddr")) {
+ if (ast_parse_arg(v->value, PARSE_INADDR, &sip_tcp_desc.sin))
+ ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n", v->name, v->value, v->lineno, config);
+ } else if (!strcasecmp(v->name, "tlsenable")) {
+ default_tls_cfg.enabled = ast_true(v->value) ? TRUE : FALSE;
+ sip_tls_desc.sin.sin_family = AF_INET;
+ } else if (!strcasecmp(v->name, "tlscertfile")) {
+ ast_free(default_tls_cfg.certfile);
+ default_tls_cfg.certfile = ast_strdup(v->value);
+ } else if (!strcasecmp(v->name, "tlscipher")) {
+ ast_free(default_tls_cfg.cipher);
+ default_tls_cfg.cipher = ast_strdup(v->value);
+ } else if (!strcasecmp(v->name, "tlscafile")) {
+ ast_free(default_tls_cfg.cafile);
+ default_tls_cfg.cafile = ast_strdup(v->value);
+ } else if (!strcasecmp(v->name, "tlscapath")) {
+ ast_free(default_tls_cfg.capath);
+ default_tls_cfg.capath = ast_strdup(v->value);
+ } else if (!strcasecmp(v->name, "tlsverifyclient")) {
+ ast_set2_flag(&default_tls_cfg.flags, ast_true(v->value), AST_SSL_VERIFY_CLIENT);
+ } else if (!strcasecmp(v->name, "tlsdontverifyserver")) {
+ ast_set2_flag(&default_tls_cfg.flags, ast_true(v->value), AST_SSL_DONT_VERIFY_SERVER);
+ } else if (!strcasecmp(v->name, "tlsbindaddr")) {
+ if (ast_parse_arg(v->value, PARSE_INADDR, &sip_tls_desc.sin))
+ ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n", v->name, v->value, v->lineno, config);
+ } else if (!strcasecmp(v->name, "rtautoclear")) {
+ int i = atoi(v->value);
+ if (i > 0)
+ global_rtautoclear = i;
+ else
+ i = 0;
+ ast_set2_flag(&global_flags[1], i || ast_true(v->value), SIP_PAGE2_RTAUTOCLEAR);
+ } else if (!strcasecmp(v->name, "usereqphone")) {
+ ast_set2_flag(&global_flags[0], ast_true(v->value), SIP_USEREQPHONE);
+ } else if (!strcasecmp(v->name, "relaxdtmf")) {
+ global_relaxdtmf = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "vmexten")) {
+ ast_copy_string(default_vmexten, v->value, sizeof(default_vmexten));
+ } else if (!strcasecmp(v->name, "rtptimeout")) {
+ if ((sscanf(v->value, "%d", &global_rtptimeout) != 1) || (global_rtptimeout < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
+ global_rtptimeout = 0;
+ }
+ } else if (!strcasecmp(v->name, "rtpholdtimeout")) {
+ if ((sscanf(v->value, "%d", &global_rtpholdtimeout) != 1) || (global_rtpholdtimeout < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
+ global_rtpholdtimeout = 0;
+ }
+ } else if (!strcasecmp(v->name, "rtpkeepalive")) {
+ if ((sscanf(v->value, "%d", &global_rtpkeepalive) != 1) || (global_rtpkeepalive < 0)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d. Using default.\n", v->value, v->lineno);
+ global_rtpkeepalive = 0;
+ }
+ } else if (!strcasecmp(v->name, "compactheaders")) {
+ compactheaders = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "notifymimetype")) {
+ ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime));
+ } else if (!strncasecmp(v->name, "limitonpeer", 11) || !strcasecmp(v->name, "counteronpeer")) {
+ global_limitonpeers = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "directrtpsetup")) {
+ global_directrtpsetup = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "notifyringing")) {
+ global_notifyringing = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "notifyhold")) {
+ global_notifyhold = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "alwaysauthreject")) {
+ global_alwaysauthreject = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "mohinterpret")) {
+ ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret));
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_copy_string(default_mohsuggest, v->value, sizeof(default_mohsuggest));
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(default_language, v->value, sizeof(default_language));
+ } else if (!strcasecmp(v->name, "regcontext")) {
+ ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
+ stringp = newcontexts;
+ /* Let's remove any contexts that are no longer defined in regcontext */
+ cleanup_stale_contexts(stringp, oldregcontext);
+ /* Create contexts if they don't exist already */
+ while ((context = strsep(&stringp, "&"))) {
+ ast_copy_string(used_context, context, sizeof(used_context));
+ if (!ast_context_find(context))
+ ast_context_create(NULL, context,"SIP");
+ }
+ ast_copy_string(global_regcontext, v->value, sizeof(global_regcontext));
+ } else if (!strcasecmp(v->name, "regextenonqualify")) {
+ global_regextenonqualify = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callerid")) {
+ ast_copy_string(default_callerid, v->value, sizeof(default_callerid));
+ } else if (!strcasecmp(v->name, "fromdomain")) {
+ ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain));
+ } else if (!strcasecmp(v->name, "outboundproxy")) {
+ char *name, *port = NULL, *force;
+
+ name = ast_strdupa(v->value);
+ if ((port = strchr(name, ':'))) {
+ *port++ = '\0';
+ global_outboundproxy.ip.sin_port = htons(atoi(port));
+ }
+
+ if ((force = strchr(port ? port : name, ','))) {
+ *force++ = '\0';
+ global_outboundproxy.force = (!strcasecmp(force, "force"));
+ }
+ ast_copy_string(global_outboundproxy.name, name, sizeof(global_outboundproxy.name));
+ proxy_update(&global_outboundproxy);
+
+ } else if (!strcasecmp(v->name, "autocreatepeer")) {
+ autocreatepeer = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "match_auth_username")) {
+ global_match_auth_username = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "srvlookup")) {
+ global_srvlookup = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "pedantic")) {
+ pedanticsipchecking = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "maxexpirey") || !strcasecmp(v->name, "maxexpiry")) {
+ max_expiry = atoi(v->value);
+ if (max_expiry < 1)
+ max_expiry = DEFAULT_MAX_EXPIRY;
+ } else if (!strcasecmp(v->name, "minexpirey") || !strcasecmp(v->name, "minexpiry")) {
+ min_expiry = atoi(v->value);
+ if (min_expiry < 1)
+ min_expiry = DEFAULT_MIN_EXPIRY;
+ } else if (!strcasecmp(v->name, "defaultexpiry") || !strcasecmp(v->name, "defaultexpirey")) {
+ default_expiry = atoi(v->value);
+ if (default_expiry < 1)
+ default_expiry = DEFAULT_DEFAULT_EXPIRY;
+ } else if (!strcasecmp(v->name, "sipdebug")) {
+ if (ast_true(v->value))
+ sipdebug |= sip_debug_config;
+ } else if (!strcasecmp(v->name, "dumphistory")) {
+ dumphistory = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "recordhistory")) {
+ recordhistory = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "registertimeout")) {
+ global_reg_timeout = atoi(v->value);
+ if (global_reg_timeout < 1)
+ global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
+ } else if (!strcasecmp(v->name, "registerattempts")) {
+ global_regattempts_max = atoi(v->value);
+ } else if (!strcasecmp(v->name, "stunaddr")) {
+ stunaddr.sin_port = htons(3478);
+ if (ast_parse_arg(v->value, PARSE_INADDR, &stunaddr))
+ ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value);
+ externexpire = time(NULL);
+ } else if (!strcasecmp(v->name, "bindaddr")) {
+ if (ast_parse_arg(v->value, PARSE_INADDR, &bindaddr))
+ ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
+ } else if (!strcasecmp(v->name, "localnet")) {
+ struct ast_ha *na;
+ int ha_error = 0;
+
+ if (!(na = ast_append_ha("d", v->value, localaddr, &ha_error)))
+ ast_log(LOG_WARNING, "Invalid localnet value: %s\n", v->value);
+ else
+ localaddr = na;
+ if (ha_error)
+ ast_log(LOG_ERROR, "Bad localnet configuration value line %d : %s\n", v->lineno, v->value);
+ } else if (!strcasecmp(v->name, "externip")) {
+ if (ast_parse_arg(v->value, PARSE_INADDR, &externip))
+ ast_log(LOG_WARNING, "Invalid address for externip keyword: %s\n", v->value);
+ externexpire = 0;
+ } else if (!strcasecmp(v->name, "externhost")) {
+ ast_copy_string(externhost, v->value, sizeof(externhost));
+ if (ast_parse_arg(externhost, PARSE_INADDR, &externip))
+ ast_log(LOG_WARNING, "Invalid address for externhost keyword: %s\n", externhost);
+ externexpire = time(NULL);
+ } else if (!strcasecmp(v->name, "externrefresh")) {
+ if (sscanf(v->value, "%d", &externrefresh) != 1) {
+ ast_log(LOG_WARNING, "Invalid externrefresh value '%s', must be an integer >0 at line %d\n", v->value, v->lineno);
+ externrefresh = 10;
+ }
+ } else if (!strcasecmp(v->name, "allow")) {
+ int error = ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, TRUE);
+ if (error)
+ ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ int error = ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, FALSE);
+ if (error)
+ ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
+ } else if (!strcasecmp(v->name, "autoframing")) {
+ global_autoframing = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "allowexternaldomains")) {
+ allow_external_domains = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "autodomain")) {
+ auto_sip_domains = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "domain")) {
+ char *domain = ast_strdupa(v->value);
+ char *context = strchr(domain, ',');
+
+ if (context)
+ *context++ = '\0';
+
+ if (ast_strlen_zero(context))
+ ast_debug(1, "No context specified at line %d for domain '%s'\n", v->lineno, domain);
+ if (ast_strlen_zero(domain))
+ ast_log(LOG_WARNING, "Empty domain specified at line %d\n", v->lineno);
+ else
+ add_sip_domain(ast_strip(domain), SIP_DOMAIN_CONFIG, context ? ast_strip(context) : "");
+ } else if (!strcasecmp(v->name, "register")) {
+ if (sip_register(v->value, v->lineno) == 0)
+ registry_count++;
+ } else if (!strcasecmp(v->name, "tos_sip")) {
+ if (ast_str2tos(v->value, &global_tos_sip))
+ ast_log(LOG_WARNING, "Invalid tos_sip value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "tos_audio")) {
+ if (ast_str2tos(v->value, &global_tos_audio))
+ ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "tos_video")) {
+ if (ast_str2tos(v->value, &global_tos_video))
+ ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "tos_text")) {
+ if (ast_str2tos(v->value, &global_tos_text))
+ ast_log(LOG_WARNING, "Invalid tos_text value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_sip")) {
+ if (ast_str2cos(v->value, &global_cos_sip))
+ ast_log(LOG_WARNING, "Invalid cos_sip value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_audio")) {
+ if (ast_str2cos(v->value, &global_cos_audio))
+ ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_video")) {
+ if (ast_str2cos(v->value, &global_cos_video))
+ ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_text")) {
+ if (ast_str2cos(v->value, &global_cos_text))
+ ast_log(LOG_WARNING, "Invalid cos_text value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "bindport")) {
+ int i;
+ if (sscanf(v->value, "%d", &i) == 1) {
+ bindaddr.sin_port = htons(i);
+ } else {
+ ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
+ }
+ } else if (!strcasecmp(v->name, "qualify")) {
+ if (!strcasecmp(v->value, "no")) {
+ default_qualify = 0;
+ } else if (!strcasecmp(v->value, "yes")) {
+ default_qualify = DEFAULT_MAXMS;
+ } else if (sscanf(v->value, "%d", &default_qualify) != 1) {
+ ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
+ default_qualify = 0;
+ }
+ } else if (!strcasecmp(v->name, "qualifyfreq")) {
+ int i;
+ if (sscanf(v->value, "%d", &i) == 1)
+ global_qualifyfreq = i * 1000;
+ else {
+ ast_log(LOG_WARNING, "Invalid qualifyfreq number '%s' at line %d of %s\n", v->value, v->lineno, config);
+ global_qualifyfreq = DEFAULT_QUALIFYFREQ;
+ }
+ } else if (!strcasecmp(v->name, "callevents")) {
+ global_callevents = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "maxcallbitrate")) {
+ default_maxcallbitrate = atoi(v->value);
+ if (default_maxcallbitrate < 0)
+ default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
+ } else if (!strcasecmp(v->name, "matchexterniplocally")) {
+ global_matchexterniplocally = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "session-timers")) {
+ int i = (int) str2stmode(v->value);
+ if (i < 0) {
+ ast_log(LOG_WARNING, "Invalid session-timers '%s' at line %d of %s\n", v->value, v->lineno, config);
+ global_st_mode = SESSION_TIMER_MODE_ACCEPT;
+ } else {
+ global_st_mode = i;
+ }
+ } else if (!strcasecmp(v->name, "session-expires")) {
+ if (sscanf(v->value, "%d", &global_max_se) != 1) {
+ ast_log(LOG_WARNING, "Invalid session-expires '%s' at line %d of %s\n", v->value, v->lineno, config);
+ global_max_se = DEFAULT_MAX_SE;
+ }
+ } else if (!strcasecmp(v->name, "session-minse")) {
+ if (sscanf(v->value, "%d", &global_min_se) != 1) {
+ ast_log(LOG_WARNING, "Invalid session-minse '%s' at line %d of %s\n", v->value, v->lineno, config);
+ global_min_se = DEFAULT_MIN_SE;
+ }
+ if (global_min_se < 90) {
+ ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < 90 secs\n", v->value, v->lineno, config);
+ global_min_se = DEFAULT_MIN_SE;
+ }
+ } else if (!strcasecmp(v->name, "session-refresher")) {
+ int i = (int) str2strefresher(v->value);
+ if (i < 0) {
+ ast_log(LOG_WARNING, "Invalid session-refresher '%s' at line %d of %s\n", v->value, v->lineno, config);
+ global_st_refresher = SESSION_TIMER_REFRESHER_UAS;
+ } else {
+ global_st_refresher = i;
+ }
+ }
+ }
+
+ if (!allow_external_domains && AST_LIST_EMPTY(&domain_list)) {
+ ast_log(LOG_WARNING, "To disallow external domains, you need to configure local SIP domains.\n");
+ allow_external_domains = 1;
+ }
+
+ /* Build list of authentication to various SIP realms, i.e. service providers */
+ for (v = ast_variable_browse(cfg, "authentication"); v ; v = v->next) {
+ /* Format for authentication is auth = username:password@realm */
+ if (!strcasecmp(v->name, "auth"))
+ authl = add_realm_authentication(authl, v->value, v->lineno);
+ }
+
+ if (ucfg) {
+ struct ast_variable *gen;
+ int genhassip, genregistersip;
+ const char *hassip, *registersip;
+
+ genhassip = ast_true(ast_variable_retrieve(ucfg, "general", "hassip"));
+ genregistersip = ast_true(ast_variable_retrieve(ucfg, "general", "registersip"));
+ gen = ast_variable_browse(ucfg, "general");
+ cat = ast_category_browse(ucfg, NULL);
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ hassip = ast_variable_retrieve(ucfg, cat, "hassip");
+ registersip = ast_variable_retrieve(ucfg, cat, "registersip");
+ if (ast_true(hassip) || (!hassip && genhassip)) {
+ peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
+ if (peer) {
+ ast_device_state_changed("SIP/%s", peer->name);
+ ASTOBJ_CONTAINER_LINK(&peerl,peer);
+ unref_peer(peer);
+ peer_count++;
+ }
+ }
+ if (ast_true(registersip) || (!registersip && genregistersip)) {
+ char tmp[256];
+ const char *host = ast_variable_retrieve(ucfg, cat, "host");
+ const char *username = ast_variable_retrieve(ucfg, cat, "username");
+ const char *secret = ast_variable_retrieve(ucfg, cat, "secret");
+ const char *contact = ast_variable_retrieve(ucfg, cat, "contact");
+ if (!host)
+ host = ast_variable_retrieve(ucfg, "general", "host");
+ if (!username)
+ username = ast_variable_retrieve(ucfg, "general", "username");
+ if (!secret)
+ secret = ast_variable_retrieve(ucfg, "general", "secret");
+ if (!contact)
+ contact = "s";
+ if (!ast_strlen_zero(username) && !ast_strlen_zero(host)) {
+ if (!ast_strlen_zero(secret))
+ snprintf(tmp, sizeof(tmp), "%s:%s@%s/%s", username, secret, host, contact);
+ else
+ snprintf(tmp, sizeof(tmp), "%s@%s/%s", username, host, contact);
+ if (sip_register(tmp, 0) == 0)
+ registry_count++;
+ }
+ }
+ }
+ cat = ast_category_browse(ucfg, cat);
+ }
+ ast_config_destroy(ucfg);
+ }
+
+
+ /* Load peers, users and friends */
+ cat = NULL;
+ while ( (cat = ast_category_browse(cfg, cat)) ) {
+ const char *utype;
+ if (!strcasecmp(cat, "general") || !strcasecmp(cat, "authentication"))
+ continue;
+ utype = ast_variable_retrieve(cfg, cat, "type");
+ if (!utype) {
+ ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
+ continue;
+ } else {
+ int is_user = 0, is_peer = 0;
+ if (!strcasecmp(utype, "user"))
+ is_user = 1;
+ else if (!strcasecmp(utype, "friend"))
+ is_user = is_peer = 1;
+ else if (!strcasecmp(utype, "peer"))
+ is_peer = 1;
+ else {
+ ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf");
+ continue;
+ }
+ if (is_user) {
+ user = build_user(cat, ast_variable_browse(cfg, cat), 0);
+ if (user) {
+ ASTOBJ_CONTAINER_LINK(&userl,user);
+ unref_user(user);
+ user_count++;
+ }
+ }
+ if (is_peer) {
+ peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ if (peer) {
+ ASTOBJ_CONTAINER_LINK(&peerl,peer);
+ unref_peer(peer);
+ peer_count++;
+ }
+ }
+ }
+ }
+ bindaddr.sin_family = AF_INET;
+ internip = bindaddr;
+ if (ast_find_ourip(&internip.sin_addr, bindaddr)) {
+ ast_log(LOG_WARNING, "Unable to get own IP address, SIP disabled\n");
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ ast_mutex_lock(&netlock);
+ if ((sipsock > -1) && (memcmp(&old_bindaddr, &bindaddr, sizeof(struct sockaddr_in)))) {
+ close(sipsock);
+ sipsock = -1;
+ }
+ if (sipsock < 0) {
+ sipsock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sipsock < 0) {
+ ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno));
+ ast_config_destroy(cfg);
+ return -1;
+ } else {
+ /* Allow SIP clients on the same host to access us: */
+ const int reuseFlag = 1;
+
+ setsockopt(sipsock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&reuseFlag,
+ sizeof reuseFlag);
+
+ ast_enable_packet_fragmentation(sipsock);
+
+ if (bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+ ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
+ ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
+ strerror(errno));
+ close(sipsock);
+ sipsock = -1;
+ } else {
+ ast_verb(2, "SIP Listening on %s:%d\n",
+ ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
+ ast_netsock_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP");
+ }
+ }
+ }
+ if (stunaddr.sin_addr.s_addr != 0) {
+ ast_debug(1, "stun to %s:%d\n",
+ ast_inet_ntoa(stunaddr.sin_addr) , ntohs(stunaddr.sin_port));
+ ast_stun_request(sipsock, &stunaddr,
+ NULL, &externip);
+ ast_debug(1, "STUN sees us at %s:%d\n",
+ ast_inet_ntoa(externip.sin_addr) , ntohs(externip.sin_port));
+ }
+ ast_mutex_unlock(&netlock);
+
+ /* Add default domains - host name, IP address and IP:port */
+ /* Only do this if user added any sip domain with "localdomains" */
+ /* In order to *not* break backwards compatibility */
+ /* Some phones address us at IP only, some with additional port number */
+ if (auto_sip_domains) {
+ char temp[MAXHOSTNAMELEN];
+
+ /* First our default IP address */
+ if (bindaddr.sin_addr.s_addr)
+ add_sip_domain(ast_inet_ntoa(bindaddr.sin_addr), SIP_DOMAIN_AUTO, NULL);
+ else
+ ast_log(LOG_NOTICE, "Can't add wildcard IP address to domain list, please add IP address to domain manually.\n");
+
+ /* Our extern IP address, if configured */
+ if (externip.sin_addr.s_addr)
+ add_sip_domain(ast_inet_ntoa(externip.sin_addr), SIP_DOMAIN_AUTO, NULL);
+
+ /* Extern host name (NAT traversal support) */
+ if (!ast_strlen_zero(externhost))
+ add_sip_domain(externhost, SIP_DOMAIN_AUTO, NULL);
+
+ /* Our host name */
+ if (!gethostname(temp, sizeof(temp)))
+ add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL);
+ }
+
+ /* Release configuration from memory */
+ ast_config_destroy(cfg);
+
+ /* Load the list of manual NOTIFY types to support */
+ if (notify_types)
+ ast_config_destroy(notify_types);
+ notify_types = ast_config_load(notify_config, config_flags);
+
+ memcpy(sip_tls_desc.tls_cfg, &default_tls_cfg, sizeof(default_tls_cfg));
+ server_start(&sip_tcp_desc);
+
+ if (ssl_setup(sip_tls_desc.tls_cfg))
+ server_start(&sip_tls_desc);
+ else if (sip_tls_desc.tls_cfg->enabled) {
+ sip_tls_desc.tls_cfg = NULL;
+ ast_log(LOG_WARNING, "SIP TLS server did not load because of errors.\n");
+ }
+
+ /* Done, tell the manager */
+ manager_event(EVENT_FLAG_SYSTEM, "ChannelReload", "ChannelType: SIP\r\nReloadReason: %s\r\nRegistry_Count: %d\r\nPeer_Count: %d\r\nUser_Count: %d\r\n", channelreloadreason2txt(reason), registry_count, peer_count, user_count);
+
+ return 0;
+}
+
+static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan)
+{
+ struct sip_pvt *p;
+ struct ast_udptl *udptl = NULL;
+
+ p = chan->tech_pvt;
+ if (!p)
+ return NULL;
+
+ sip_pvt_lock(p);
+ if (p->udptl && ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
+ udptl = p->udptl;
+ sip_pvt_unlock(p);
+ return udptl;
+}
+
+static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
+{
+ struct sip_pvt *p;
+
+ p = chan->tech_pvt;
+ if (!p)
+ return -1;
+ sip_pvt_lock(p);
+ if (udptl)
+ ast_udptl_get_peer(udptl, &p->udptlredirip);
+ else
+ memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
+ if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
+ if (!p->pendinginvite) {
+ ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip.sin_addr), udptl ? ntohs(p->udptlredirip.sin_port) : 0);
+ transmit_reinvite_with_sdp(p, TRUE, FALSE);
+ } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+ ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip.sin_addr), udptl ? ntohs(p->udptlredirip.sin_port) : 0);
+ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
+ }
+ }
+ /* Reset lastrtprx timer */
+ p->lastrtprx = p->lastrtptx = time(NULL);
+ sip_pvt_unlock(p);
+ return 0;
+}
+
+/*! \brief Handle T38 reinvite
+ \todo Make sure we don't destroy the call if we can't handle the re-invite.
+ Nothing should be changed until we have processed the SDP and know that we
+ can handle it.
+*/
+static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite)
+{
+ struct sip_pvt *p;
+ int flag = 0;
+
+ p = chan->tech_pvt;
+ if (!p || !pvt->udptl)
+ return -1;
+
+ /* Setup everything on the other side like offered/responded from first side */
+ sip_pvt_lock(p);
+
+ /*! \todo check if this is not set earlier when setting up the PVT. If not
+ maybe it should move there. */
+ p->t38.jointcapability = p->t38.peercapability = pvt->t38.jointcapability;
+
+ ast_udptl_set_far_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl));
+ ast_udptl_set_local_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl));
+ ast_udptl_set_error_correction_scheme(p->udptl, ast_udptl_get_error_correction_scheme(pvt->udptl));
+
+ if (reinvite) { /* If we are handling sending re-invite to the other side of the bridge */
+ /*! \note The SIP_CAN_REINVITE flag is for RTP media redirects,
+ not really T38 re-invites which are different. In this
+ case it's used properly, to see if we can reinvite over
+ NAT
+ */
+ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE) && ast_test_flag(&pvt->flags[0], SIP_CAN_REINVITE)) {
+ ast_udptl_get_peer(pvt->udptl, &p->udptlredirip);
+ flag =1;
+ } else {
+ memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
+ }
+ if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
+ if (!p->pendinginvite) {
+ if (flag)
+ ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port));
+ else
+ ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr));
+ transmit_reinvite_with_sdp(p, TRUE, FALSE);
+ } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+ if (flag)
+ ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port));
+ else
+ ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr));
+ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
+ }
+ }
+ /* Reset lastrtprx timer */
+ p->lastrtprx = p->lastrtptx = time(NULL);
+ sip_pvt_unlock(p);
+ return 0;
+ } else { /* If we are handling sending 200 OK to the other side of the bridge */
+ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE) && ast_test_flag(&pvt->flags[0], SIP_CAN_REINVITE)) {
+ ast_udptl_get_peer(pvt->udptl, &p->udptlredirip);
+ flag = 1;
+ } else {
+ memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
+ }
+ if (flag)
+ ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port));
+ else
+ ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr));
+ pvt->t38.state = T38_ENABLED;
+ p->t38.state = T38_ENABLED;
+ ast_debug(2, "T38 changed state to %d on channel %s\n", pvt->t38.state, pvt->owner ? pvt->owner->name : "<none>");
+ ast_debug(2, "T38 changed state to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>");
+ transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
+ p->lastrtprx = p->lastrtptx = time(NULL);
+ sip_pvt_unlock(p);
+ return 0;
+ }
+}
+
+
+/*! \brief Returns null if we can't reinvite audio (part of RTP interface) */
+static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct sip_pvt *p = NULL;
+ enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
+
+ if (!(p = chan->tech_pvt))
+ return AST_RTP_GET_FAILED;
+
+ sip_pvt_lock(p);
+ if (!(p->rtp)) {
+ sip_pvt_unlock(p);
+ return AST_RTP_GET_FAILED;
+ }
+
+ *rtp = p->rtp;
+
+ if (ast_rtp_getnat(*rtp) && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT))
+ res = AST_RTP_TRY_PARTIAL;
+ else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
+ res = AST_RTP_TRY_NATIVE;
+ else if (ast_test_flag(&global_jbconf, AST_JB_FORCED))
+ res = AST_RTP_GET_FAILED;
+
+ sip_pvt_unlock(p);
+
+ return res;
+}
+
+/*! \brief Returns null if we can't reinvite video (part of RTP interface) */
+static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct sip_pvt *p = NULL;
+ enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
+
+ if (!(p = chan->tech_pvt))
+ return AST_RTP_GET_FAILED;
+
+ sip_pvt_lock(p);
+ if (!(p->vrtp)) {
+ sip_pvt_unlock(p);
+ return AST_RTP_GET_FAILED;
+ }
+
+ *rtp = p->vrtp;
+
+ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
+ res = AST_RTP_TRY_NATIVE;
+
+ sip_pvt_unlock(p);
+
+ return res;
+}
+
+/*! \brief Returns null if we can't reinvite text (part of RTP interface) */
+static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct sip_pvt *p = NULL;
+ enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
+
+ if (!(p = chan->tech_pvt))
+ return AST_RTP_GET_FAILED;
+
+ sip_pvt_lock(p);
+ if (!(p->trtp)) {
+ sip_pvt_unlock(p);
+ return AST_RTP_GET_FAILED;
+ }
+
+ *rtp = p->trtp;
+
+ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
+ res = AST_RTP_TRY_NATIVE;
+
+ sip_pvt_unlock(p);
+
+ return res;
+}
+
+/*! \brief Set the RTP peer for this call */
+static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+{
+ struct sip_pvt *p;
+ int changed = 0;
+
+ p = chan->tech_pvt;
+ if (!p)
+ return -1;
+
+ /* Disable early RTP bridge */
+ if (chan->_state != AST_STATE_UP && !global_directrtpsetup) /* We are in early state */
+ return 0;
+
+ sip_pvt_lock(p);
+ if (p->alreadygone) {
+ /* If we're destroyed, don't bother */
+ sip_pvt_unlock(p);
+ return 0;
+ }
+
+ /* if this peer cannot handle reinvites of the media stream to devices
+ that are known to be behind a NAT, then stop the process now
+ */
+ if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) {
+ sip_pvt_unlock(p);
+ return 0;
+ }
+
+ if (rtp) {
+ changed |= ast_rtp_get_peer(rtp, &p->redirip);
+ } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) {
+ memset(&p->redirip, 0, sizeof(p->redirip));
+ changed = 1;
+ }
+ if (vrtp) {
+ changed |= ast_rtp_get_peer(vrtp, &p->vredirip);
+ } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) {
+ memset(&p->vredirip, 0, sizeof(p->vredirip));
+ changed = 1;
+ }
+ if (trtp) {
+ changed |= ast_rtp_get_peer(trtp, &p->tredirip);
+ } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) {
+ memset(&p->tredirip, 0, sizeof(p->tredirip));
+ changed = 1;
+ }
+ if (codecs && (p->redircodecs != codecs)) {
+ p->redircodecs = codecs;
+ changed = 1;
+ }
+ if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) {
+ if (chan->_state != AST_STATE_UP) { /* We are in early state */
+ if (p->do_history)
+ append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal.");
+ ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr));
+ } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */
+ ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr));
+ transmit_reinvite_with_sdp(p, FALSE, FALSE);
+ } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+ ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr));
+ /* We have a pending Invite. Send re-invite when we're done with the invite */
+ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
+ }
+ }
+ /* Reset lastrtprx timer */
+ p->lastrtprx = p->lastrtptx = time(NULL);
+ sip_pvt_unlock(p);
+ return 0;
+}
+
+static char *synopsis_dtmfmode = "Change the dtmfmode for a SIP call";
+static char *descrip_dtmfmode = " SIPDtmfMode(inband|info|rfc2833): Changes the dtmfmode for a SIP call\n";
+static char *app_dtmfmode = "SIPDtmfMode";
+
+static char *app_sipaddheader = "SIPAddHeader";
+static char *synopsis_sipaddheader = "Add a SIP header to the outbound call";
+
+static char *descrip_sipaddheader = ""
+" SIPAddHeader(Header: Content):\n"
+"Adds a header to a SIP call placed with DIAL.\n"
+"Remember to user the X-header if you are adding non-standard SIP\n"
+"headers, like \"X-Asterisk-Accountcode:\". Use this with care.\n"
+"Adding the wrong headers may jeopardize the SIP dialog.\n"
+"Always returns 0\n";
+
+
+/*! \brief Set the DTMFmode for an outbound SIP call (application) */
+static int sip_dtmfmode(struct ast_channel *chan, void *data)
+{
+ struct sip_pvt *p;
+ char *mode = data;
+
+ if (!data) {
+ ast_log(LOG_WARNING, "This application requires the argument: info, inband, rfc2833\n");
+ return 0;
+ }
+ ast_channel_lock(chan);
+ if (!IS_SIP_TECH(chan->tech)) {
+ ast_log(LOG_WARNING, "Call this application only on SIP incoming calls\n");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ p = chan->tech_pvt;
+ if (!p) {
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ sip_pvt_lock(p);
+ if (!strcasecmp(mode,"info")) {
+ ast_clear_flag(&p->flags[0], SIP_DTMF);
+ ast_set_flag(&p->flags[0], SIP_DTMF_INFO);
+ p->jointnoncodeccapability &= ~AST_RTP_DTMF;
+ } else if (!strcasecmp(mode,"shortinfo")) {
+ ast_clear_flag(&p->flags[0], SIP_DTMF);
+ ast_set_flag(&p->flags[0], SIP_DTMF_SHORTINFO);
+ p->jointnoncodeccapability &= ~AST_RTP_DTMF;
+ } else if (!strcasecmp(mode,"rfc2833")) {
+ ast_clear_flag(&p->flags[0], SIP_DTMF);
+ ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
+ p->jointnoncodeccapability |= AST_RTP_DTMF;
+ } else if (!strcasecmp(mode,"inband")) {
+ ast_clear_flag(&p->flags[0], SIP_DTMF);
+ ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
+ p->jointnoncodeccapability &= ~AST_RTP_DTMF;
+ } else
+ ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n",mode);
+ if (p->rtp)
+ ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
+ if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) {
+ if (!p->vad) {
+ p->vad = ast_dsp_new();
+ ast_dsp_set_features(p->vad, DSP_FEATURE_DTMF_DETECT);
+ }
+ } else {
+ if (p->vad) {
+ ast_dsp_free(p->vad);
+ p->vad = NULL;
+ }
+ }
+ sip_pvt_unlock(p);
+ ast_channel_unlock(chan);
+ return 0;
+}
+
+/*! \brief Add a SIP header to an outbound INVITE */
+static int sip_addheader(struct ast_channel *chan, void *data)
+{
+ int no = 0;
+ int ok = FALSE;
+ char varbuf[30];
+ char *inbuf = data;
+
+ if (ast_strlen_zero(inbuf)) {
+ ast_log(LOG_WARNING, "This application requires the argument: Header\n");
+ return 0;
+ }
+ ast_channel_lock(chan);
+
+ /* Check for headers */
+ while (!ok && no <= 50) {
+ no++;
+ snprintf(varbuf, sizeof(varbuf), "_SIPADDHEADER%.2d", no);
+
+ /* Compare without the leading underscore */
+ if( (pbx_builtin_getvar_helper(chan, (const char *) varbuf + 1) == (const char *) NULL) )
+ ok = TRUE;
+ }
+ if (ok) {
+ pbx_builtin_setvar_helper (chan, varbuf, inbuf);
+ if (sipdebug)
+ ast_debug(1,"SIP Header added \"%s\" as %s\n", inbuf, varbuf);
+ } else {
+ ast_log(LOG_WARNING, "Too many SIP headers added, max 50\n");
+ }
+ ast_channel_unlock(chan);
+ return 0;
+}
+
+/*! \brief Transfer call before connect with a 302 redirect
+\note Called by the transfer() dialplan application through the sip_transfer()
+ pbx interface function if the call is in ringing state
+\todo Fix this function so that we wait for reply to the REFER and
+ react to errors, denials or other issues the other end might have.
+ */
+static int sip_sipredirect(struct sip_pvt *p, const char *dest)
+{
+ char *cdest;
+ char *extension, *host, *port;
+ char tmp[80];
+
+ cdest = ast_strdupa(dest);
+
+ extension = strsep(&cdest, "@");
+ host = strsep(&cdest, ":");
+ port = strsep(&cdest, ":");
+ if (ast_strlen_zero(extension)) {
+ ast_log(LOG_ERROR, "Missing mandatory argument: extension\n");
+ return 0;
+ }
+
+ /* we'll issue the redirect message here */
+ if (!host) {
+ char *localtmp;
+
+ ast_copy_string(tmp, get_header(&p->initreq, "To"), sizeof(tmp));
+ if (ast_strlen_zero(tmp)) {
+ ast_log(LOG_ERROR, "Cannot retrieve the 'To' header from the original SIP request!\n");
+ return 0;
+ }
+ if ( ( (localtmp = strcasestr(tmp, "sip:")) || (localtmp = strcasestr(tmp, "sips:")) )
+ && (localtmp = strchr(localtmp, '@'))) {
+ char lhost[80], lport[80];
+
+ memset(lhost, 0, sizeof(lhost));
+ memset(lport, 0, sizeof(lport));
+ localtmp++;
+ /* This is okey because lhost and lport are as big as tmp */
+ sscanf(localtmp, "%[^<>:; ]:%[^<>:; ]", lhost, lport);
+ if (ast_strlen_zero(lhost)) {
+ ast_log(LOG_ERROR, "Can't find the host address\n");
+ return 0;
+ }
+ host = ast_strdupa(lhost);
+ if (!ast_strlen_zero(lport)) {
+ port = ast_strdupa(lport);
+ }
+ }
+ }
+
+ ast_string_field_build(p, our_contact, "Transfer <sip:%s@%s%s%s>", extension, host, port ? ":" : "", port ? port : "");
+ transmit_response_reliable(p, "302 Moved Temporarily", &p->initreq);
+
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Make sure we stop send this reply. */
+ sip_alreadygone(p);
+ /* hangup here */
+ return 0;
+}
+
+/*! \brief Return SIP UA's codec (part of the RTP interface) */
+static int sip_get_codec(struct ast_channel *chan)
+{
+ struct sip_pvt *p = chan->tech_pvt;
+ return p->peercapability ? p->peercapability : p->capability;
+}
+
+/*! \brief Send a poke to all known peers
+ Space them out 100 ms apart
+ XXX We might have a cool algorithm for this or use random - any suggestions?
+*/
+static void sip_poke_all_peers(void)
+{
+ int ms = 0;
+
+ if (!speerobjs) /* No peers, just give up */
+ return;
+
+ ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
+ ASTOBJ_WRLOCK(iterator);
+ ms += 100;
+ iterator->pokeexpire = ast_sched_replace(iterator->pokeexpire,
+ sched, ms, sip_poke_peer_s, iterator);
+ ASTOBJ_UNLOCK(iterator);
+ } while (0)
+ );
+}
+
+/*! \brief Send all known registrations */
+static void sip_send_all_registers(void)
+{
+ int ms;
+ int regspacing;
+ if (!regobjs)
+ return;
+ regspacing = default_expiry * 1000/regobjs;
+ if (regspacing > 100)
+ regspacing = 100;
+ ms = regspacing;
+ ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
+ ASTOBJ_WRLOCK(iterator);
+ ms += regspacing;
+ iterator->expire = ast_sched_replace(iterator->expire,
+ sched, ms, sip_reregister, iterator);
+ ASTOBJ_UNLOCK(iterator);
+ } while (0)
+ );
+}
+
+/*! \brief Reload module */
+static int sip_do_reload(enum channelreloadreason reason)
+{
+ reload_config(reason);
+
+ /* Prune peers who still are supposed to be deleted */
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer);
+ ast_debug(4, "--------------- Done destroying pruned peers\n");
+
+ /* Send qualify (OPTIONS) to all peers */
+ sip_poke_all_peers();
+
+ /* Register with all services */
+ sip_send_all_registers();
+
+ ast_debug(4, "--------------- SIP reload done\n");
+
+ return 0;
+}
+
+/*! \brief Force reload of module from cli */
+static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip reload";
+ e->usage =
+ "Usage: sip reload\n"
+ " Reloads SIP configuration from sip.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_mutex_lock(&sip_reload_lock);
+ if (sip_reloading)
+ ast_verbose("Previous SIP reload not yet done\n");
+ else {
+ sip_reloading = TRUE;
+ sip_reloadreason = (a && a->fd) ? CHANNEL_CLI_RELOAD : CHANNEL_MODULE_RELOAD;
+ }
+ ast_mutex_unlock(&sip_reload_lock);
+ restart_monitor();
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Part of Asterisk module interface */
+static int reload(void)
+{
+ if (sip_reload(0, 0, NULL))
+ return 0;
+ return 1;
+}
+
+/*! \brief SIP Cli commands definition */
+static struct ast_cli_entry cli_sip[] = {
+ AST_CLI_DEFINE(sip_show_channels, "List active SIP channels/subscriptions"),
+ AST_CLI_DEFINE(sip_show_domains, "List our local SIP domains."),
+ AST_CLI_DEFINE(sip_show_inuse, "List all inuse/limits"),
+ AST_CLI_DEFINE(sip_show_objects, "List all SIP object allocations"),
+ AST_CLI_DEFINE(sip_show_peers, "List defined SIP peers"),
+ AST_CLI_DEFINE(sip_show_registry, "List SIP registration status"),
+ AST_CLI_DEFINE(sip_unregister, "Unregister (force expiration) a SIP peer from the registery\n"),
+ AST_CLI_DEFINE(sip_show_settings, "Show SIP global settings"),
+ AST_CLI_DEFINE(sip_notify, "Send a notify packet to a SIP peer"),
+ AST_CLI_DEFINE(sip_show_channel, "Show detailed SIP channel info"),
+ AST_CLI_DEFINE(sip_show_history, "Show SIP dialog history"),
+ AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
+ AST_CLI_DEFINE(sip_show_users, "List defined SIP users"),
+ AST_CLI_DEFINE(sip_show_user, "Show details on specific SIP user"),
+ AST_CLI_DEFINE(sip_prune_realtime, "Prune cached Realtime users/peers"),
+ AST_CLI_DEFINE(sip_do_debug, "Enable/Disable SIP debugging"),
+ AST_CLI_DEFINE(sip_do_history, "Enable SIP history"),
+ AST_CLI_DEFINE(sip_no_history, "Disable SIP history"),
+ AST_CLI_DEFINE(sip_reload, "Reload SIP configuration"),
+ AST_CLI_DEFINE(sip_show_tcp, "List TCP Connections")
+};
+
+/*! \brief PBX load module - initialization */
+static int load_module(void)
+{
+ ast_verbose("SIP channel loading...\n");
+ ASTOBJ_CONTAINER_INIT(&userl); /* User object list */
+ ASTOBJ_CONTAINER_INIT(&peerl); /* Peer object list */
+ ASTOBJ_CONTAINER_INIT(&regl); /* Registry object list */
+
+ if (!(sched = sched_context_create())) {
+ ast_log(LOG_ERROR, "Unable to create scheduler context\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (!(io = io_context_create())) {
+ ast_log(LOG_ERROR, "Unable to create I/O context\n");
+ sched_context_destroy(sched);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ sip_reloadreason = CHANNEL_MODULE_LOAD;
+
+ if(reload_config(sip_reloadreason)) /* Load the configuration from sip.conf */
+ return AST_MODULE_LOAD_DECLINE;
+
+ /* Prepare the version that does not require DTMF BEGIN frames.
+ * We need to use tricks such as memcpy and casts because the variable
+ * has const fields.
+ */
+ memcpy(&sip_tech_info, &sip_tech, sizeof(sip_tech));
+ memset((void *) &sip_tech_info.send_digit_begin, 0, sizeof(sip_tech_info.send_digit_begin));
+
+ /* Make sure we can register our sip channel type */
+ if (ast_channel_register(&sip_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel type 'SIP'\n");
+ io_context_destroy(io);
+ sched_context_destroy(sched);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ /* Register all CLI functions for SIP */
+ ast_cli_register_multiple(cli_sip, sizeof(cli_sip)/ sizeof(struct ast_cli_entry));
+
+ /* Tell the RTP subdriver that we're here */
+ ast_rtp_proto_register(&sip_rtp);
+
+ /* Tell the UDPTL subdriver that we're here */
+ ast_udptl_proto_register(&sip_udptl);
+
+ /* Register dialplan applications */
+ ast_register_application(app_dtmfmode, sip_dtmfmode, synopsis_dtmfmode, descrip_dtmfmode);
+ ast_register_application(app_sipaddheader, sip_addheader, synopsis_sipaddheader, descrip_sipaddheader);
+
+ /* Register dialplan functions */
+ ast_custom_function_register(&sip_header_function);
+ ast_custom_function_register(&sippeer_function);
+ ast_custom_function_register(&sipchaninfo_function);
+ ast_custom_function_register(&checksipdomain_function);
+
+ /* Register manager commands */
+ ast_manager_register2("SIPpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peers,
+ "List SIP peers (text format)", mandescr_show_peers);
+ ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peer,
+ "Show SIP peer (text format)", mandescr_show_peer);
+ ast_manager_register2("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry,
+ "Show SIP registrations (text format)", mandescr_show_registry);
+ sip_poke_all_peers();
+ sip_send_all_registers();
+
+ /* And start the monitor for the first time */
+ restart_monitor();
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+/*! \brief PBX unload module API */
+static int unload_module(void)
+{
+ struct sip_pvt *p, *pl;
+ struct sip_threadinfo *th;
+ struct ast_context *con;
+
+ /* First, take us out of the channel type list */
+ ast_channel_unregister(&sip_tech);
+
+ /* Unregister dial plan functions */
+ ast_custom_function_unregister(&sipchaninfo_function);
+ ast_custom_function_unregister(&sippeer_function);
+ ast_custom_function_unregister(&sip_header_function);
+ ast_custom_function_unregister(&checksipdomain_function);
+
+ /* Unregister dial plan applications */
+ ast_unregister_application(app_dtmfmode);
+ ast_unregister_application(app_sipaddheader);
+
+ /* Unregister CLI commands */
+ ast_cli_unregister_multiple(cli_sip, sizeof(cli_sip) / sizeof(struct ast_cli_entry));
+
+ /* Disconnect from the RTP subsystem */
+ ast_rtp_proto_unregister(&sip_rtp);
+
+ /* Disconnect from UDPTL */
+ ast_udptl_proto_unregister(&sip_udptl);
+
+ /* Unregister AMI actions */
+ ast_manager_unregister("SIPpeers");
+ ast_manager_unregister("SIPshowpeer");
+ ast_manager_unregister("SIPshowregistry");
+
+ /* Kill TCP/TLS server threads */
+ if (sip_tcp_desc.master)
+ server_stop(&sip_tcp_desc);
+ if (sip_tls_desc.master)
+ server_stop(&sip_tls_desc);
+
+ /* Kill all existing TCP/TLS threads */
+ AST_LIST_LOCK(&threadl);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&threadl, th, list) {
+ pthread_t thread = th->threadid;
+ th->stop = 1;
+ AST_LIST_UNLOCK(&threadl);
+ pthread_kill(thread, SIGURG);
+ pthread_join(thread, NULL);
+ AST_LIST_LOCK(&threadl);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&threadl);
+
+ dialoglist_lock();
+ /* Hangup all dialogs if they have an owner */
+ for (p = dialoglist; p ; p = p->next) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ }
+ dialoglist_unlock();
+
+ ast_mutex_lock(&monlock);
+ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
+ pthread_cancel(monitor_thread);
+ pthread_kill(monitor_thread, SIGURG);
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+
+ dialoglist_lock();
+ /* Destroy all the dialogs and free their memory */
+ p = dialoglist;
+ while (p) {
+ pl = p;
+ p = p->next;
+ __sip_destroy(pl, TRUE, TRUE);
+ }
+ dialoglist = NULL;
+ dialoglist_unlock();
+
+ /* Free memory for local network address mask */
+ ast_free_ha(localaddr);
+
+ if (default_tls_cfg.certfile)
+ ast_free(default_tls_cfg.certfile);
+ if (default_tls_cfg.cipher)
+ ast_free(default_tls_cfg.cipher);
+ if (default_tls_cfg.cafile)
+ ast_free(default_tls_cfg.cafile);
+ if (default_tls_cfg.capath)
+ ast_free(default_tls_cfg.capath);
+
+ ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
+ ASTOBJ_CONTAINER_DESTROY(&userl);
+ ASTOBJ_CONTAINER_DESTROYALL(&peerl, sip_destroy_peer);
+ ASTOBJ_CONTAINER_DESTROY(&peerl);
+ ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&regl);
+
+ clear_realm_authentication(authl);
+ clear_sip_domains();
+ close(sipsock);
+ sched_context_destroy(sched);
+ con = ast_context_find(used_context);
+ if (con)
+ ast_context_destroy(con, "SIP");
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Session Initiation Protocol (SIP)",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/channels/chan_skinny.c b/trunk/channels/chan_skinny.c
new file mode 100644
index 000000000..719258178
--- /dev/null
+++ b/trunk/channels/chan_skinny.c
@@ -0,0 +1,6066 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * chan_skinny was developed by Jeremy McNamara & Florian Overkamp
+ * chan_skinny was heavily modified/fixed by North Antara
+ *
+ * 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 Implementation of the Skinny protocol
+ *
+ * \author Jeremy McNamara & Florian Overkamp & North Antara
+ * \ingroup channel_drivers
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <sys/signal.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/netsock.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+#include "asterisk/cdr.h"
+#include "asterisk/astdb.h"
+#include "asterisk/features.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/utils.h"
+#include "asterisk/dsp.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/astobj.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/devicestate.h"
+
+/*************************************
+ * Skinny/Asterisk Protocol Settings *
+ *************************************/
+static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
+static const char config[] = "skinny.conf";
+
+static int default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW;
+static struct ast_codec_pref default_prefs;
+
+enum skinny_codecs {
+ SKINNY_CODEC_ALAW = 2,
+ SKINNY_CODEC_ULAW = 4,
+ SKINNY_CODEC_G723_1 = 9,
+ SKINNY_CODEC_G729A = 12,
+ SKINNY_CODEC_G726_32 = 82, /* XXX Which packing order does this translate to? */
+ SKINNY_CODEC_H261 = 100,
+ SKINNY_CODEC_H263 = 101
+};
+
+#define DEFAULT_SKINNY_PORT 2000
+#define DEFAULT_SKINNY_BACKLOG 2
+#define SKINNY_MAX_PACKET 1000
+
+static unsigned int tos = 0;
+static unsigned int tos_audio = 0;
+static unsigned int tos_video = 0;
+static unsigned int cos = 0;
+static unsigned int cos_audio = 0;
+static unsigned int cos_video = 0;
+
+static int keep_alive = 120;
+static char vmexten[AST_MAX_EXTENSION]; /* Voicemail pilot number */
+static char used_context[AST_MAX_EXTENSION]; /* Voicemail pilot number */
+static char regcontext[AST_MAX_CONTEXT]; /* Context for auto-extension */
+static char date_format[6] = "D-M-Y";
+static char version_id[16] = "P002F202";
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define letohl(x) (x)
+#define letohs(x) (x)
+#define htolel(x) (x)
+#define htoles(x) (x)
+#else
+#if defined(HAVE_BYTESWAP_H)
+#include <byteswap.h>
+#define letohl(x) bswap_32(x)
+#define letohs(x) bswap_16(x)
+#define htolel(x) bswap_32(x)
+#define htoles(x) bswap_16(x)
+#elif defined(HAVE_SYS_ENDIAN_SWAP16)
+#include <sys/endian.h>
+#define letohl(x) __swap32(x)
+#define letohs(x) __swap16(x)
+#define htolel(x) __swap32(x)
+#define htoles(x) __swap16(x)
+#elif defined(HAVE_SYS_ENDIAN_BSWAP16)
+#include <sys/endian.h>
+#define letohl(x) bswap32(x)
+#define letohs(x) bswap16(x)
+#define htolel(x) bswap32(x)
+#define htoles(x) bswap16(x)
+#else
+#define __bswap_16(x) \
+ ((((x) & 0xff00) >> 8) | \
+ (((x) & 0x00ff) << 8))
+#define __bswap_32(x) \
+ ((((x) & 0xff000000) >> 24) | \
+ (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | \
+ (((x) & 0x000000ff) << 24))
+#define letohl(x) __bswap_32(x)
+#define letohs(x) __bswap_16(x)
+#define htolel(x) __bswap_32(x)
+#define htoles(x) __bswap_16(x)
+#endif
+#endif
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+AST_THREADSTORAGE(device2str_threadbuf);
+#define DEVICE2STR_BUFSIZE 15
+
+AST_THREADSTORAGE(control2str_threadbuf);
+#define CONTROL2STR_BUFSIZE 100
+
+/*********************
+ * Protocol Messages *
+ *********************/
+/* message types */
+#define KEEP_ALIVE_MESSAGE 0x0000
+/* no additional struct */
+
+#define REGISTER_MESSAGE 0x0001
+struct register_message {
+ char name[16];
+ uint32_t userId;
+ uint32_t instance;
+ uint32_t ip;
+ uint32_t type;
+ uint32_t maxStreams;
+};
+
+#define IP_PORT_MESSAGE 0x0002
+
+#define KEYPAD_BUTTON_MESSAGE 0x0003
+struct keypad_button_message {
+ uint32_t button;
+ uint32_t lineInstance;
+ uint32_t callReference;
+};
+
+
+#define ENBLOC_CALL_MESSAGE 0x0004
+struct enbloc_call_message {
+ char calledParty[24];
+};
+
+#define STIMULUS_MESSAGE 0x0005
+struct stimulus_message {
+ uint32_t stimulus;
+ uint32_t stimulusInstance;
+ uint32_t callreference;
+};
+
+#define OFFHOOK_MESSAGE 0x0006
+struct offhook_message {
+ uint32_t unknown1;
+ uint32_t unknown2;
+};
+
+#define ONHOOK_MESSAGE 0x0007
+struct onhook_message {
+ uint32_t unknown1;
+ uint32_t unknown2;
+};
+
+#define CAPABILITIES_RES_MESSAGE 0x0010
+struct station_capabilities {
+ uint32_t codec;
+ uint32_t frames;
+ union {
+ char res[8];
+ uint32_t rate;
+ } payloads;
+};
+
+#define SKINNY_MAX_CAPABILITIES 18
+
+struct capabilities_res_message {
+ uint32_t count;
+ struct station_capabilities caps[SKINNY_MAX_CAPABILITIES];
+};
+
+#define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
+struct speed_dial_stat_req_message {
+ uint32_t speedDialNumber;
+};
+
+#define LINE_STATE_REQ_MESSAGE 0x000B
+struct line_state_req_message {
+ uint32_t lineNumber;
+};
+
+#define TIME_DATE_REQ_MESSAGE 0x000D
+#define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
+#define VERSION_REQ_MESSAGE 0x000F
+#define SERVER_REQUEST_MESSAGE 0x0012
+
+#define ALARM_MESSAGE 0x0020
+struct alarm_message {
+ uint32_t alarmSeverity;
+ char displayMessage[80];
+ uint32_t alarmParam1;
+ uint32_t alarmParam2;
+};
+
+#define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022
+struct open_receive_channel_ack_message {
+ uint32_t status;
+ uint32_t ipAddr;
+ uint32_t port;
+ uint32_t passThruId;
+};
+
+#define SOFT_KEY_SET_REQ_MESSAGE 0x0025
+
+#define SOFT_KEY_EVENT_MESSAGE 0x0026
+struct soft_key_event_message {
+ uint32_t softKeyEvent;
+ uint32_t instance;
+ uint32_t callreference;
+};
+
+#define UNREGISTER_MESSAGE 0x0027
+#define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
+#define HEADSET_STATUS_MESSAGE 0x002B
+#define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D
+
+#define REGISTER_ACK_MESSAGE 0x0081
+struct register_ack_message {
+ uint32_t keepAlive;
+ char dateTemplate[6];
+ char res[2];
+ uint32_t secondaryKeepAlive;
+ char res2[4];
+};
+
+#define START_TONE_MESSAGE 0x0082
+struct start_tone_message {
+ uint32_t tone;
+ uint32_t space;
+ uint32_t instance;
+ uint32_t reference;
+};
+
+#define STOP_TONE_MESSAGE 0x0083
+struct stop_tone_message {
+ uint32_t instance;
+ uint32_t reference;
+};
+
+#define SET_RINGER_MESSAGE 0x0085
+struct set_ringer_message {
+ uint32_t ringerMode;
+ uint32_t unknown1; /* See notes in transmit_ringer_mode */
+ uint32_t unknown2;
+ uint32_t space[2];
+};
+
+#define SET_LAMP_MESSAGE 0x0086
+struct set_lamp_message {
+ uint32_t stimulus;
+ uint32_t stimulusInstance;
+ uint32_t deviceStimulus;
+};
+
+#define SET_SPEAKER_MESSAGE 0x0088
+struct set_speaker_message {
+ uint32_t mode;
+};
+
+/* XXX When do we need to use this? */
+#define SET_MICROPHONE_MESSAGE 0x0089
+struct set_microphone_message {
+ uint32_t mode;
+};
+
+#define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
+struct media_qualifier {
+ uint32_t precedence;
+ uint32_t vad;
+ uint16_t packets;
+ uint32_t bitRate;
+};
+
+struct start_media_transmission_message {
+ uint32_t conferenceId;
+ uint32_t passThruPartyId;
+ uint32_t remoteIp;
+ uint32_t remotePort;
+ uint32_t packetSize;
+ uint32_t payloadType;
+ struct media_qualifier qualifier;
+ uint32_t space[16];
+};
+
+#define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
+struct stop_media_transmission_message {
+ uint32_t conferenceId;
+ uint32_t passThruPartyId;
+ uint32_t space[3];
+};
+
+#define CALL_INFO_MESSAGE 0x008F
+struct call_info_message {
+ char callingPartyName[40];
+ char callingParty[24];
+ char calledPartyName[40];
+ char calledParty[24];
+ uint32_t instance;
+ uint32_t reference;
+ uint32_t type;
+ char originalCalledPartyName[40];
+ char originalCalledParty[24];
+ char lastRedirectingPartyName[40];
+ char lastRedirectingParty[24];
+ uint32_t originalCalledPartyRedirectReason;
+ uint32_t lastRedirectingReason;
+ char callingPartyVoiceMailbox[24];
+ char calledPartyVoiceMailbox[24];
+ char originalCalledPartyVoiceMailbox[24];
+ char lastRedirectingVoiceMailbox[24];
+ uint32_t space[3];
+};
+
+#define FORWARD_STAT_MESSAGE 0x0090
+struct forward_stat_message {
+ uint32_t activeforward;
+ uint32_t lineNumber;
+ uint32_t fwdall;
+ char fwdallnum[24];
+ uint32_t fwdbusy;
+ char fwdbusynum[24];
+ uint32_t fwdnoanswer;
+ char fwdnoanswernum[24];
+};
+
+#define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
+struct speed_dial_stat_res_message {
+ uint32_t speedDialNumber;
+ char speedDialDirNumber[24];
+ char speedDialDisplayName[40];
+};
+
+#define LINE_STAT_RES_MESSAGE 0x0092
+struct line_stat_res_message {
+ uint32_t lineNumber;
+ char lineDirNumber[24];
+ char lineDisplayName[24];
+ uint32_t space[15];
+};
+
+#define DEFINETIMEDATE_MESSAGE 0x0094
+struct definetimedate_message {
+ uint32_t year; /* since 1900 */
+ uint32_t month;
+ uint32_t dayofweek; /* monday = 1 */
+ uint32_t day;
+ uint32_t hour;
+ uint32_t minute;
+ uint32_t seconds;
+ uint32_t milliseconds;
+ uint32_t timestamp;
+};
+
+#define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
+struct button_definition {
+ uint8_t instanceNumber;
+ uint8_t buttonDefinition;
+};
+
+struct button_definition_template {
+ uint8_t buttonDefinition;
+ /* for now, anything between 0xB0 and 0xCF is custom */
+ /*int custom;*/
+};
+
+#define STIMULUS_REDIAL 0x01
+#define STIMULUS_SPEEDDIAL 0x02
+#define STIMULUS_HOLD 0x03
+#define STIMULUS_TRANSFER 0x04
+#define STIMULUS_FORWARDALL 0x05
+#define STIMULUS_FORWARDBUSY 0x06
+#define STIMULUS_FORWARDNOANSWER 0x07
+#define STIMULUS_DISPLAY 0x08
+#define STIMULUS_LINE 0x09
+#define STIMULUS_VOICEMAIL 0x0F
+#define STIMULUS_AUTOANSWER 0x11
+#define STIMULUS_DND 0x3F
+#define STIMULUS_CONFERENCE 0x7D
+#define STIMULUS_CALLPARK 0x7E
+#define STIMULUS_CALLPICKUP 0x7F
+#define STIMULUS_NONE 0xFF
+
+/* Button types */
+#define BT_REDIAL STIMULUS_REDIAL
+#define BT_SPEEDDIAL STIMULUS_SPEEDDIAL
+#define BT_HOLD STIMULUS_HOLD
+#define BT_TRANSFER STIMULUS_TRANSFER
+#define BT_FORWARDALL STIMULUS_FORWARDALL
+#define BT_FORWARDBUSY STIMULUS_FORWARDBUSY
+#define BT_FORWARDNOANSWER STIMULUS_FORWARDNOANSWER
+#define BT_DISPLAY STIMULUS_DISPLAY
+#define BT_LINE STIMULUS_LINE
+#define BT_VOICEMAIL STIMULUS_VOICEMAIL
+#define BT_AUTOANSWER STIMULUS_AUTOANSWER
+#define BT_DND STIMULUS_DND
+#define BT_CONFERENCE STIMULUS_CONFERENCE
+#define BT_CALLPARK STIMULUS_CALLPARK
+#define BT_CALLPICKUP STIMULUS_CALLPICKUP
+#define BT_NONE 0x00
+
+/* Custom button types - add our own between 0xB0 and 0xCF.
+ This may need to be revised in the future,
+ if stimuluses are ever added in this range. */
+#define BT_CUST_LINESPEEDDIAL 0xB0 /* line or speeddial with/without hint */
+#define BT_CUST_LINE 0xB1 /* line or speeddial with hint only */
+
+struct button_template_res_message {
+ uint32_t buttonOffset;
+ uint32_t buttonCount;
+ uint32_t totalButtonCount;
+ struct button_definition definition[42];
+};
+
+#define VERSION_RES_MESSAGE 0x0098
+struct version_res_message {
+ char version[16];
+};
+
+#define DISPLAYTEXT_MESSAGE 0x0099
+struct displaytext_message {
+ char text[40];
+};
+
+#define CLEAR_NOTIFY_MESSAGE 0x0115
+#define CLEAR_DISPLAY_MESSAGE 0x009A
+
+#define CAPABILITIES_REQ_MESSAGE 0x009B
+
+#define REGISTER_REJ_MESSAGE 0x009D
+struct register_rej_message {
+ char errMsg[33];
+};
+
+#define SERVER_RES_MESSAGE 0x009E
+struct server_identifier {
+ char serverName[48];
+};
+
+struct server_res_message {
+ struct server_identifier server[5];
+ uint32_t serverListenPort[5];
+ uint32_t serverIpAddr[5];
+};
+
+#define RESET_MESSAGE 0x009F
+struct reset_message {
+ uint32_t resetType;
+};
+
+#define KEEP_ALIVE_ACK_MESSAGE 0x0100
+
+#define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105
+struct open_receive_channel_message {
+ uint32_t conferenceId;
+ uint32_t partyId;
+ uint32_t packets;
+ uint32_t capability;
+ uint32_t echo;
+ uint32_t bitrate;
+ uint32_t space[16];
+};
+
+#define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106
+struct close_receive_channel_message {
+ uint32_t conferenceId;
+ uint32_t partyId;
+ uint32_t space[2];
+};
+
+#define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
+
+struct soft_key_template_definition {
+ char softKeyLabel[16];
+ uint32_t softKeyEvent;
+};
+
+#define KEYDEF_ONHOOK 0
+#define KEYDEF_CONNECTED 1
+#define KEYDEF_ONHOLD 2
+#define KEYDEF_RINGIN 3
+#define KEYDEF_OFFHOOK 4
+#define KEYDEF_CONNWITHTRANS 5
+#define KEYDEF_DADFD 6 /* Digits After Dialing First Digit */
+#define KEYDEF_CONNWITHCONF 7
+#define KEYDEF_RINGOUT 8
+#define KEYDEF_OFFHOOKWITHFEAT 9
+#define KEYDEF_UNKNOWN 10
+
+#define SOFTKEY_NONE 0x00
+#define SOFTKEY_REDIAL 0x01
+#define SOFTKEY_NEWCALL 0x02
+#define SOFTKEY_HOLD 0x03
+#define SOFTKEY_TRNSFER 0x04
+#define SOFTKEY_CFWDALL 0x05
+#define SOFTKEY_CFWDBUSY 0x06
+#define SOFTKEY_CFWDNOANSWER 0x07
+#define SOFTKEY_BKSPC 0x08
+#define SOFTKEY_ENDCALL 0x09
+#define SOFTKEY_RESUME 0x0A
+#define SOFTKEY_ANSWER 0x0B
+#define SOFTKEY_INFO 0x0C
+#define SOFTKEY_CONFRN 0x0D
+#define SOFTKEY_PARK 0x0E
+#define SOFTKEY_JOIN 0x0F
+#define SOFTKEY_MEETME 0x10
+#define SOFTKEY_PICKUP 0x11
+#define SOFTKEY_GPICKUP 0x12
+#define SOFTKEY_DND 0x13
+#define SOFTKEY_IDIVERT 0x14
+
+struct soft_key_template_definition soft_key_template_default[] = {
+ { "\200\001", SOFTKEY_REDIAL },
+ { "\200\002", SOFTKEY_NEWCALL },
+ { "\200\003", SOFTKEY_HOLD },
+ { "\200\004", SOFTKEY_TRNSFER },
+ { "\200\005", SOFTKEY_CFWDALL },
+ { "\200\006", SOFTKEY_CFWDBUSY },
+ { "\200\007", SOFTKEY_CFWDNOANSWER },
+ { "\200\010", SOFTKEY_BKSPC },
+ { "\200\011", SOFTKEY_ENDCALL },
+ { "\200\012", SOFTKEY_RESUME },
+ { "\200\013", SOFTKEY_ANSWER },
+ { "\200\014", SOFTKEY_INFO },
+ { "\200\015", SOFTKEY_CONFRN },
+ { "\200\016", SOFTKEY_PARK },
+ { "\200\017", SOFTKEY_JOIN },
+ { "\200\020", SOFTKEY_MEETME },
+ { "\200\021", SOFTKEY_PICKUP },
+ { "\200\022", SOFTKEY_GPICKUP },
+ { "\200\077", SOFTKEY_DND },
+ { "\200\120", SOFTKEY_IDIVERT },
+};
+
+/* Localized message "codes" (in octal)
+ Below is en_US (taken from a 7970)
+
+ \200\xxx
+ \000: ???
+ \001: Redial
+ \002: New Call
+ \003: Hold
+ \004: Transfer
+ \005: CFwdALL
+ \006: CFwdBusy
+ \007: CFwdNoAnswer
+ \010: <<
+ \011: EndCall
+ \012: Resume
+ \013: Answer
+ \014: Info
+ \015: Confrn
+ \016: Park
+ \017: Join
+ \020: MeetMe
+ \021: PickUp
+ \022: GPickUp
+ \023: Your current options
+ \024: Off Hook
+ \025: On Hook
+ \026: Ring out
+ \027: From
+ \030: Connected
+ \031: Busy
+ \032: Line In Use
+ \033: Call Waiting
+ \034: Call Transfer
+ \035: Call Park
+ \036: Call Proceed
+ \037: In Use Remote
+ \040: Enter number
+ \041: Call park At
+ \042: Primary Only
+ \043: Temp Fail
+ \044: You Have VoiceMail
+ \045: Forwarded to
+ \046: Can Not Complete Conference
+ \047: No Conference Bridge
+ \050: Can Not Hold Primary Control
+ \051: Invalid Conference Participant
+ \052: In Conference Already
+ \053: No Participant Info
+ \054: Exceed Maximum Parties
+ \055: Key Is Not Active
+ \056: Error No License
+ \057: Error DBConfig
+ \060: Error Database
+ \061: Error Pass Limit
+ \062: Error Unknown
+ \063: Error Mismatch
+ \064: Conference
+ \065: Park Number
+ \066: Private
+ \067: Not Enough Bandwidth
+ \070: Unknown Number
+ \071: RmLstC
+ \072: Voicemail
+ \073: ImmDiv
+ \074: Intrcpt
+ \075: SetWtch
+ \076: TrnsfVM
+ \077: DND
+ \100: DivAll
+ \101: CallBack
+ \102: Network congestion,rerouting
+ \103: Barge
+ \104: Failed to setup Barge
+ \105: Another Barge exists
+ \106: Incompatible device type
+ \107: No Park Number Available
+ \110: CallPark Reversion
+ \111: Service is not Active
+ \112: High Traffic Try Again Later
+ \113: QRT
+ \114: MCID
+ \115: DirTrfr
+ \116: Select
+ \117: ConfList
+ \120: iDivert
+ \121: cBarge
+ \122: Can Not Complete Transfer
+ \123: Can Not Join Calls
+ \124: Mcid Successful
+ \125: Number Not Configured
+ \126: Security Error
+ \127: Video Bandwidth Unavailable
+ \130: VidMode
+ \131: Max Call Duration Timeout
+ \132: Max Hold Duration Timeout
+ \133: OPickUp
+ \134: ???
+ \135: ???
+ \136: ???
+ \137: ???
+ \140: ???
+ \141: External Transfer Restricted
+ \142: ???
+ \143: ???
+ \144: ???
+ \145: Mac Address
+ \146: Host Name
+ \147: Domain Name
+ \150: IP Address
+ \151: Subnet Mask
+ \152: TFTP Server 1
+ \153: Default Router 1
+ \154: Default Router 2
+ \155: Default Router 3
+ \156: Default Router 4
+ \157: Default Router 5
+ \160: DNS Server 1
+ \161: DNS Server 2
+ \162: DNS Server 3
+ \163: DNS Server 4
+ \164: DNS Server 5
+ \165: Operational VLAN Id
+ \166: Admin. VLAN Id
+ \167: CallManager 1
+ \170: CallManager 2
+ \171: CallManager 3
+ \172: CallManager 4
+ \173: CallManager 5
+ \174: Information URL
+ \175: Directories URL
+ \176: Messages URL
+ \177: Services URL
+ */
+
+struct soft_key_definitions {
+ const uint8_t mode;
+ const uint8_t *defaults;
+ const int count;
+};
+
+static const uint8_t soft_key_default_onhook[] = {
+ SOFTKEY_REDIAL,
+ SOFTKEY_NEWCALL,
+ SOFTKEY_CFWDALL,
+ SOFTKEY_CFWDBUSY,
+ SOFTKEY_DND,
+ SOFTKEY_GPICKUP,
+ SOFTKEY_CONFRN,
+};
+
+static const uint8_t soft_key_default_connected[] = {
+ SOFTKEY_HOLD,
+ SOFTKEY_ENDCALL,
+ SOFTKEY_TRNSFER,
+ SOFTKEY_PARK,
+ SOFTKEY_CFWDALL,
+ SOFTKEY_CFWDBUSY,
+};
+
+static const uint8_t soft_key_default_onhold[] = {
+ SOFTKEY_RESUME,
+ SOFTKEY_NEWCALL,
+ SOFTKEY_ENDCALL,
+ SOFTKEY_TRNSFER,
+};
+
+static const uint8_t soft_key_default_ringin[] = {
+ SOFTKEY_ANSWER,
+ SOFTKEY_ENDCALL,
+ SOFTKEY_TRNSFER,
+};
+
+static const uint8_t soft_key_default_offhook[] = {
+ SOFTKEY_REDIAL,
+ SOFTKEY_ENDCALL,
+ SOFTKEY_CFWDALL,
+ SOFTKEY_CFWDBUSY,
+ SOFTKEY_GPICKUP,
+};
+
+static const uint8_t soft_key_default_connwithtrans[] = {
+ SOFTKEY_HOLD,
+ SOFTKEY_ENDCALL,
+ SOFTKEY_TRNSFER,
+ SOFTKEY_PARK,
+ SOFTKEY_CFWDALL,
+ SOFTKEY_CFWDBUSY,
+};
+
+static const uint8_t soft_key_default_dadfd[] = {
+ SOFTKEY_BKSPC,
+ SOFTKEY_ENDCALL,
+};
+
+static const uint8_t soft_key_default_connwithconf[] = {
+ SOFTKEY_NONE,
+};
+
+static const uint8_t soft_key_default_ringout[] = {
+ SOFTKEY_NONE,
+ SOFTKEY_ENDCALL,
+};
+
+static const uint8_t soft_key_default_offhookwithfeat[] = {
+ SOFTKEY_REDIAL,
+ SOFTKEY_ENDCALL,
+};
+
+static const uint8_t soft_key_default_unknown[] = {
+ SOFTKEY_NONE,
+};
+
+static const struct soft_key_definitions soft_key_default_definitions[] = {
+ {KEYDEF_ONHOOK, soft_key_default_onhook, sizeof(soft_key_default_onhook) / sizeof(uint8_t)},
+ {KEYDEF_CONNECTED, soft_key_default_connected, sizeof(soft_key_default_connected) / sizeof(uint8_t)},
+ {KEYDEF_ONHOLD, soft_key_default_onhold, sizeof(soft_key_default_onhold) / sizeof(uint8_t)},
+ {KEYDEF_RINGIN, soft_key_default_ringin, sizeof(soft_key_default_ringin) / sizeof(uint8_t)},
+ {KEYDEF_OFFHOOK, soft_key_default_offhook, sizeof(soft_key_default_offhook) / sizeof(uint8_t)},
+ {KEYDEF_CONNWITHTRANS, soft_key_default_connwithtrans, sizeof(soft_key_default_connwithtrans) / sizeof(uint8_t)},
+ {KEYDEF_DADFD, soft_key_default_dadfd, sizeof(soft_key_default_dadfd) / sizeof(uint8_t)},
+ {KEYDEF_CONNWITHCONF, soft_key_default_connwithconf, sizeof(soft_key_default_connwithconf) / sizeof(uint8_t)},
+ {KEYDEF_RINGOUT, soft_key_default_ringout, sizeof(soft_key_default_ringout) / sizeof(uint8_t)},
+ {KEYDEF_OFFHOOKWITHFEAT, soft_key_default_offhookwithfeat, sizeof(soft_key_default_offhookwithfeat) / sizeof(uint8_t)},
+ {KEYDEF_UNKNOWN, soft_key_default_unknown, sizeof(soft_key_default_unknown) / sizeof(uint8_t)}
+};
+
+struct soft_key_template_res_message {
+ uint32_t softKeyOffset;
+ uint32_t softKeyCount;
+ uint32_t totalSoftKeyCount;
+ struct soft_key_template_definition softKeyTemplateDefinition[32];
+};
+
+#define SOFT_KEY_SET_RES_MESSAGE 0x0109
+
+struct soft_key_set_definition {
+ uint8_t softKeyTemplateIndex[16];
+ uint16_t softKeyInfoIndex[16];
+};
+
+struct soft_key_set_res_message {
+ uint32_t softKeySetOffset;
+ uint32_t softKeySetCount;
+ uint32_t totalSoftKeySetCount;
+ struct soft_key_set_definition softKeySetDefinition[16];
+ uint32_t res;
+};
+
+#define SELECT_SOFT_KEYS_MESSAGE 0x0110
+struct select_soft_keys_message {
+ uint32_t instance;
+ uint32_t reference;
+ uint32_t softKeySetIndex;
+ uint32_t validKeyMask;
+};
+
+#define CALL_STATE_MESSAGE 0x0111
+struct call_state_message {
+ uint32_t callState;
+ uint32_t lineInstance;
+ uint32_t callReference;
+ uint32_t space[3];
+};
+
+#define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112
+struct display_prompt_status_message {
+ uint32_t messageTimeout;
+ char promptMessage[32];
+ uint32_t lineInstance;
+ uint32_t callReference;
+};
+
+#define CLEAR_PROMPT_MESSAGE 0x0113
+struct clear_prompt_message {
+ uint32_t lineInstance;
+ uint32_t callReference;
+};
+
+#define DISPLAY_NOTIFY_MESSAGE 0x0114
+struct display_notify_message {
+ uint32_t displayTimeout;
+ char displayMessage[100];
+};
+
+#define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
+struct activate_call_plane_message {
+ uint32_t lineInstance;
+};
+
+#define DIALED_NUMBER_MESSAGE 0x011D
+struct dialed_number_message {
+ char dialedNumber[24];
+ uint32_t lineInstance;
+ uint32_t callReference;
+};
+
+union skinny_data {
+ struct alarm_message alarm;
+ struct speed_dial_stat_req_message speeddialreq;
+ struct register_message reg;
+ struct register_ack_message regack;
+ struct register_rej_message regrej;
+ struct capabilities_res_message caps;
+ struct version_res_message version;
+ struct button_template_res_message buttontemplate;
+ struct displaytext_message displaytext;
+ struct display_prompt_status_message displaypromptstatus;
+ struct clear_prompt_message clearpromptstatus;
+ struct definetimedate_message definetimedate;
+ struct start_tone_message starttone;
+ struct stop_tone_message stoptone;
+ struct speed_dial_stat_res_message speeddial;
+ struct line_state_req_message line;
+ struct line_stat_res_message linestat;
+ struct soft_key_set_res_message softkeysets;
+ struct soft_key_template_res_message softkeytemplate;
+ struct server_res_message serverres;
+ struct reset_message reset;
+ struct set_lamp_message setlamp;
+ struct set_ringer_message setringer;
+ struct call_state_message callstate;
+ struct keypad_button_message keypad;
+ struct select_soft_keys_message selectsoftkey;
+ struct activate_call_plane_message activatecallplane;
+ struct stimulus_message stimulus;
+ struct offhook_message offhook;
+ struct onhook_message onhook;
+ struct set_speaker_message setspeaker;
+ struct set_microphone_message setmicrophone;
+ struct call_info_message callinfo;
+ struct start_media_transmission_message startmedia;
+ struct stop_media_transmission_message stopmedia;
+ struct open_receive_channel_message openreceivechannel;
+ struct open_receive_channel_ack_message openreceivechannelack;
+ struct close_receive_channel_message closereceivechannel;
+ struct display_notify_message displaynotify;
+ struct dialed_number_message dialednumber;
+ struct soft_key_event_message softkeyeventmessage;
+ struct enbloc_call_message enbloccallmessage;
+ struct forward_stat_message forwardstat;
+};
+
+/* packet composition */
+struct skinny_req {
+ int len;
+ int res;
+ int e;
+ union skinny_data data;
+};
+
+/* XXX This is the combined size of the variables above. (len, res, e)
+ If more are added, this MUST change.
+ (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */
+int skinny_header_size = 12;
+
+/*****************************
+ * Asterisk specific globals *
+ *****************************/
+
+static int skinnydebug = 0;
+
+/* a hostname, portnumber, socket and such is usefull for VoIP protocols */
+static struct sockaddr_in bindaddr;
+static char ourhost[256];
+static int ourport;
+static struct in_addr __ourip;
+struct ast_hostent ahp;
+struct hostent *hp;
+static int skinnysock = -1;
+static pthread_t accept_t;
+static char context[AST_MAX_CONTEXT] = "default";
+static char language[MAX_LANGUAGE] = "";
+static char mohinterpret[MAX_MUSICCLASS] = "default";
+static char mohsuggest[MAX_MUSICCLASS] = "";
+static char cid_num[AST_MAX_EXTENSION] = "";
+static char cid_name[AST_MAX_EXTENSION] = "";
+static char linelabel[AST_MAX_EXTENSION] ="";
+static int nat = 0;
+static ast_group_t cur_callergroup = 0;
+static ast_group_t cur_pickupgroup = 0;
+static int immediate = 0;
+static int callwaiting = 0;
+static int callreturn = 0;
+static int threewaycalling = 0;
+static int mwiblink = 0;
+/* This is for flashhook transfers */
+static int transfer = 0;
+static int cancallforward = 0;
+/* static int busycount = 3;*/
+static char accountcode[AST_MAX_ACCOUNT_CODE] = "";
+static char mailbox[AST_MAX_EXTENSION];
+static char regexten[AST_MAX_EXTENSION];
+static int amaflags = 0;
+static int callnums = 1;
+static int canreinvite = 0;
+
+#define SKINNY_DEVICE_UNKNOWN -1
+#define SKINNY_DEVICE_NONE 0
+#define SKINNY_DEVICE_30SPPLUS 1
+#define SKINNY_DEVICE_12SPPLUS 2
+#define SKINNY_DEVICE_12SP 3
+#define SKINNY_DEVICE_12 4
+#define SKINNY_DEVICE_30VIP 5
+#define SKINNY_DEVICE_7910 6
+#define SKINNY_DEVICE_7960 7
+#define SKINNY_DEVICE_7940 8
+#define SKINNY_DEVICE_7935 9
+#define SKINNY_DEVICE_ATA186 12 /* Cisco ATA-186 */
+#define SKINNY_DEVICE_7941 115
+#define SKINNY_DEVICE_7971 119
+#define SKINNY_DEVICE_7985 302
+#define SKINNY_DEVICE_7911 307
+#define SKINNY_DEVICE_7961GE 308
+#define SKINNY_DEVICE_7941GE 309
+#define SKINNY_DEVICE_7921 365
+#define SKINNY_DEVICE_7905 20000
+#define SKINNY_DEVICE_7920 30002
+#define SKINNY_DEVICE_7970 30006
+#define SKINNY_DEVICE_7912 30007
+#define SKINNY_DEVICE_7902 30008
+#define SKINNY_DEVICE_CIPC 30016 /* Cisco IP Communicator */
+#define SKINNY_DEVICE_7961 30018
+#define SKINNY_DEVICE_7936 30019
+#define SKINNY_DEVICE_SCCPGATEWAY_AN 30027 /* ??? */
+#define SKINNY_DEVICE_SCCPGATEWAY_BRI 30028 /* ??? */
+
+#define SKINNY_SPEAKERON 1
+#define SKINNY_SPEAKEROFF 2
+
+#define SKINNY_MICON 1
+#define SKINNY_MICOFF 2
+
+#define SKINNY_OFFHOOK 1
+#define SKINNY_ONHOOK 2
+#define SKINNY_RINGOUT 3
+#define SKINNY_RINGIN 4
+#define SKINNY_CONNECTED 5
+#define SKINNY_BUSY 6
+#define SKINNY_CONGESTION 7
+#define SKINNY_HOLD 8
+#define SKINNY_CALLWAIT 9
+#define SKINNY_TRANSFER 10
+#define SKINNY_PARK 11
+#define SKINNY_PROGRESS 12
+#define SKINNY_CALLREMOTEMULTILINE 13
+#define SKINNY_INVALID 14
+
+#define SKINNY_SILENCE 0x00
+#define SKINNY_DIALTONE 0x21
+#define SKINNY_BUSYTONE 0x23
+#define SKINNY_ALERT 0x24
+#define SKINNY_REORDER 0x25
+#define SKINNY_CALLWAITTONE 0x2D
+#define SKINNY_NOTONE 0x7F
+
+#define SKINNY_LAMP_OFF 1
+#define SKINNY_LAMP_ON 2
+#define SKINNY_LAMP_WINK 3
+#define SKINNY_LAMP_FLASH 4
+#define SKINNY_LAMP_BLINK 5
+
+#define SKINNY_RING_OFF 1
+#define SKINNY_RING_INSIDE 2
+#define SKINNY_RING_OUTSIDE 3
+#define SKINNY_RING_FEATURE 4
+
+#define SKINNY_CFWD_ALL (1 << 0)
+#define SKINNY_CFWD_BUSY (1 << 1)
+#define SKINNY_CFWD_NOANSWER (1 << 2)
+
+#define TYPE_TRUNK 1
+#define TYPE_LINE 2
+
+/* Skinny rtp stream modes. Do we really need this? */
+#define SKINNY_CX_SENDONLY 0
+#define SKINNY_CX_RECVONLY 1
+#define SKINNY_CX_SENDRECV 2
+#define SKINNY_CX_CONF 3
+#define SKINNY_CX_CONFERENCE 3
+#define SKINNY_CX_MUTE 4
+#define SKINNY_CX_INACTIVE 4
+
+#if 0
+static char *skinny_cxmodes[] = {
+ "sendonly",
+ "recvonly",
+ "sendrecv",
+ "confrnce",
+ "inactive"
+};
+#endif
+
+/* driver scheduler */
+static struct sched_context *sched = NULL;
+static struct io_context *io;
+
+/* Protect the monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+/* Protect the network socket */
+AST_MUTEX_DEFINE_STATIC(netlock);
+/* Protect the session list */
+AST_MUTEX_DEFINE_STATIC(sessionlock);
+/* Protect the device list */
+AST_MUTEX_DEFINE_STATIC(devicelock);
+#if 0
+/* Protect the paging device list */
+AST_MUTEX_DEFINE_STATIC(pagingdevicelock);
+#endif
+
+/* This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+
+/* Wait up to 16 seconds for first digit */
+static int firstdigittimeout = 16000;
+
+/* How long to wait for following digits */
+static int gendigittimeout = 8000;
+
+/* How long to wait for an extra digit, if there is an ambiguous match */
+static int matchdigittimeout = 3000;
+
+struct skinny_subchannel {
+ ast_mutex_t lock;
+ struct ast_channel *owner;
+ struct ast_rtp *rtp;
+ struct ast_rtp *vrtp;
+ unsigned int callid;
+ /* time_t lastouttime; */ /* Unused */
+ int progress;
+ int ringing;
+ int onhold;
+ /* int lastout; */ /* Unused */
+ int cxmode;
+ int nat;
+ int outgoing;
+ int alreadygone;
+
+ struct skinny_subchannel *next;
+ struct skinny_line *parent;
+};
+
+struct skinny_line {
+ ast_mutex_t lock;
+ char name[80];
+ char label[24]; /* Label that shows next to the line buttons */
+ char accountcode[AST_MAX_ACCOUNT_CODE];
+ char exten[AST_MAX_EXTENSION]; /* Extension where to start */
+ char context[AST_MAX_CONTEXT];
+ char language[MAX_LANGUAGE];
+ char cid_num[AST_MAX_EXTENSION]; /* Caller*ID */
+ char cid_name[AST_MAX_EXTENSION]; /* Caller*ID */
+ char lastcallerid[AST_MAX_EXTENSION]; /* Last Caller*ID */
+ int cfwdtype;
+ char call_forward_all[AST_MAX_EXTENSION];
+ char call_forward_busy[AST_MAX_EXTENSION];
+ char call_forward_noanswer[AST_MAX_EXTENSION];
+ char mailbox[AST_MAX_EXTENSION];
+ char vmexten[AST_MAX_EXTENSION];
+ char regexten[AST_MAX_EXTENSION]; /* Extension for auto-extensions */
+ char regcontext[AST_MAX_CONTEXT]; /* Context for auto-extensions */
+ char mohinterpret[MAX_MUSICCLASS];
+ char mohsuggest[MAX_MUSICCLASS];
+ char lastnumberdialed[AST_MAX_EXTENSION]; /* Last number that was dialed - used for redial */
+ int curtone; /* Current tone being played */
+ ast_group_t callgroup;
+ ast_group_t pickupgroup;
+ int callwaiting;
+ int transfer;
+ int threewaycalling;
+ int mwiblink;
+ int cancallforward;
+ int getforward;
+ int callreturn;
+ int dnd; /* How does this affect callwait? Do we just deny a skinny_request if we're dnd? */
+ int hascallerid;
+ int hidecallerid;
+ int amaflags;
+ int type;
+ int instance;
+ int group;
+ int needdestroy;
+ int capability;
+ int nonCodecCapability;
+ int onhooktime;
+ int msgstate; /* voicemail message state */
+ int immediate;
+ int hookstate;
+ int nat;
+ int canreinvite;
+
+ struct ast_codec_pref prefs;
+ struct skinny_subchannel *sub;
+ struct skinny_line *next;
+ struct skinny_device *parent;
+ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */
+};
+
+struct skinny_speeddial {
+ ast_mutex_t lock;
+ char label[42];
+ char context[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ int instance;
+ int stateid;
+ int laststate;
+ int isHint;
+
+ struct skinny_speeddial *next;
+ struct skinny_device *parent;
+};
+
+struct skinny_addon {
+ ast_mutex_t lock;
+ char type[10];
+
+ struct skinny_addon *next;
+ struct skinny_device *parent;
+};
+
+static struct skinny_device {
+ /* A device containing one or more lines */
+ char name[80];
+ char id[16];
+ char version_id[16];
+ char exten[AST_MAX_EXTENSION]; /* Cruddy variable name, pick a better one */
+ int type;
+ int registered;
+ int lastlineinstance;
+ int lastcallreference;
+ int capability;
+ struct sockaddr_in addr;
+ struct in_addr ourip;
+ struct skinny_line *lines;
+ struct skinny_speeddial *speeddials;
+ struct skinny_addon *addons;
+ struct ast_codec_pref prefs;
+ struct ast_ha *ha;
+ struct skinnysession *session;
+ struct skinny_device *next;
+} *devices = NULL;
+
+struct skinny_paging_device {
+ char name[80];
+ char id[16];
+ struct skinny_device ** devices;
+ struct skinny_paging_device *next;
+};
+
+static struct skinnysession {
+ pthread_t t;
+ ast_mutex_t lock;
+ struct sockaddr_in sin;
+ int fd;
+ char inbuf[SKINNY_MAX_PACKET];
+ char outbuf[SKINNY_MAX_PACKET];
+ struct skinny_device *device;
+ struct skinnysession *next;
+} *sessions = NULL;
+
+static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
+static int skinny_devicestate(void *data);
+static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
+static int skinny_hangup(struct ast_channel *ast);
+static int skinny_answer(struct ast_channel *ast);
+static struct ast_frame *skinny_read(struct ast_channel *ast);
+static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
+static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
+static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
+static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s);
+
+static const struct ast_channel_tech skinny_tech = {
+ .type = "Skinny",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_AUDIO_MASK,
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
+ .requester = skinny_request,
+ .devicestate = skinny_devicestate,
+ .call = skinny_call,
+ .hangup = skinny_hangup,
+ .answer = skinny_answer,
+ .read = skinny_read,
+ .write = skinny_write,
+ .indicate = skinny_indicate,
+ .fixup = skinny_fixup,
+ .send_digit_begin = skinny_senddigit_begin,
+ .send_digit_end = skinny_senddigit_end,
+ .bridge = ast_rtp_bridge,
+};
+
+static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data);
+
+static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_addon *a = d->addons;
+ int i;
+
+ switch (d->type) {
+ case SKINNY_DEVICE_30SPPLUS:
+ case SKINNY_DEVICE_30VIP:
+ /* 13 rows, 2 columns */
+ for (i = 0; i < 4; i++)
+ (btn++)->buttonDefinition = BT_CUST_LINE;
+ (btn++)->buttonDefinition = BT_REDIAL;
+ (btn++)->buttonDefinition = BT_VOICEMAIL;
+ (btn++)->buttonDefinition = BT_CALLPARK;
+ (btn++)->buttonDefinition = BT_FORWARDALL;
+ (btn++)->buttonDefinition = BT_CONFERENCE;
+ for (i = 0; i < 4; i++)
+ (btn++)->buttonDefinition = BT_NONE;
+ for (i = 0; i < 13; i++)
+ (btn++)->buttonDefinition = BT_SPEEDDIAL;
+
+ break;
+ case SKINNY_DEVICE_12SPPLUS:
+ case SKINNY_DEVICE_12SP:
+ case SKINNY_DEVICE_12:
+ /* 6 rows, 2 columns */
+ for (i = 0; i < 2; i++)
+ (btn++)->buttonDefinition = BT_CUST_LINE;
+ for (i = 0; i < 4; i++)
+ (btn++)->buttonDefinition = BT_SPEEDDIAL;
+ (btn++)->buttonDefinition = BT_HOLD;
+ (btn++)->buttonDefinition = BT_REDIAL;
+ (btn++)->buttonDefinition = BT_TRANSFER;
+ (btn++)->buttonDefinition = BT_FORWARDALL;
+ (btn++)->buttonDefinition = BT_CALLPARK;
+ (btn++)->buttonDefinition = BT_VOICEMAIL;
+ break;
+ case SKINNY_DEVICE_7910:
+ (btn++)->buttonDefinition = BT_LINE;
+ (btn++)->buttonDefinition = BT_HOLD;
+ (btn++)->buttonDefinition = BT_TRANSFER;
+ (btn++)->buttonDefinition = BT_DISPLAY;
+ (btn++)->buttonDefinition = BT_VOICEMAIL;
+ (btn++)->buttonDefinition = BT_CONFERENCE;
+ (btn++)->buttonDefinition = BT_FORWARDALL;
+ for (i = 0; i < 2; i++)
+ (btn++)->buttonDefinition = BT_SPEEDDIAL;
+ (btn++)->buttonDefinition = BT_REDIAL;
+ break;
+ case SKINNY_DEVICE_7960:
+ case SKINNY_DEVICE_7961:
+ case SKINNY_DEVICE_7961GE:
+ for (i = 0; i < 6; i++)
+ (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
+ break;
+ case SKINNY_DEVICE_7940:
+ case SKINNY_DEVICE_7941:
+ case SKINNY_DEVICE_7941GE:
+ for (i = 0; i < 2; i++)
+ (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
+ break;
+ case SKINNY_DEVICE_7935:
+ case SKINNY_DEVICE_7936:
+ for (i = 0; i < 2; i++)
+ (btn++)->buttonDefinition = BT_LINE;
+ break;
+ case SKINNY_DEVICE_ATA186:
+ (btn++)->buttonDefinition = BT_LINE;
+ break;
+ case SKINNY_DEVICE_7970:
+ case SKINNY_DEVICE_7971:
+ case SKINNY_DEVICE_CIPC:
+ for (i = 0; i < 8; i++)
+ (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
+ break;
+ case SKINNY_DEVICE_7985:
+ /* XXX I have no idea what the buttons look like on these. */
+ ast_log(LOG_WARNING, "Unsupported device type '%d (7985)' found.\n", d->type);
+ break;
+ case SKINNY_DEVICE_7912:
+ case SKINNY_DEVICE_7911:
+ case SKINNY_DEVICE_7905:
+ (btn++)->buttonDefinition = BT_LINE;
+ (btn++)->buttonDefinition = BT_HOLD;
+ break;
+ case SKINNY_DEVICE_7920:
+ case SKINNY_DEVICE_7921:
+ /* XXX I don't know if this is right. */
+ for (i = 0; i < 4; i++)
+ (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
+ break;
+ case SKINNY_DEVICE_7902:
+ ast_log(LOG_WARNING, "Unsupported device type '%d (7902)' found.\n", d->type);
+ break;
+ case SKINNY_DEVICE_SCCPGATEWAY_AN:
+ case SKINNY_DEVICE_SCCPGATEWAY_BRI:
+ ast_log(LOG_WARNING, "Unsupported device type '%d (SCCP gateway)' found.\n", d->type);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unknown device type '%d' found.\n", d->type);
+ break;
+ }
+
+ for (a = d->addons; a; a = a->next) {
+ if (!strcasecmp(a->type, "7914")) {
+ for (i = 0; i < 14; i++)
+ (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
+ } else {
+ ast_log(LOG_WARNING, "Unknown addon type '%s' found. Skipping.\n", a->type);
+ }
+ }
+
+ return btn;
+}
+
+static struct skinny_req *req_alloc(size_t size, int response_message)
+{
+ struct skinny_req *req;
+
+ if (!(req = ast_calloc(1, skinny_header_size + size + 4)))
+ return NULL;
+
+ req->len = htolel(size+4);
+ req->e = htolel(response_message);
+
+ return req;
+}
+
+static struct skinny_line *find_line_by_instance(struct skinny_device *d, int instance)
+{
+ struct skinny_line *l;
+
+ /*Dialing from on hook or on a 7920 uses instance 0 in requests
+ but we need to start looking at instance 1 */
+
+ if (!instance)
+ instance = 1;
+
+ for (l = d->lines; l; l = l->next) {
+ if (l->instance == instance)
+ break;
+ }
+
+ if (!l) {
+ ast_log(LOG_WARNING, "Could not find line with instance '%d' on device '%s'\n", instance, d->name);
+ }
+ return l;
+}
+
+static struct skinny_line *find_line_by_name(const char *dest)
+{
+ struct skinny_line *l;
+ struct skinny_line *tmpl = NULL;
+ struct skinny_device *d;
+ char line[256];
+ char *at;
+ char *device;
+ int checkdevice = 0;
+
+ ast_copy_string(line, dest, sizeof(line));
+ at = strchr(line, '@');
+ if (at)
+ *at++ = '\0';
+ device = at;
+
+ if (!ast_strlen_zero(device))
+ checkdevice = 1;
+
+ ast_mutex_lock(&devicelock);
+ for (d = devices; d; d = d->next) {
+ if (checkdevice && tmpl)
+ break;
+ else if (!checkdevice) {
+ /* This is a match, since we're checking for line on every device. */
+ } else if (!strcasecmp(d->name, device)) {
+ if (skinnydebug)
+ ast_verb(2, "Found device: %s\n", d->name);
+ } else
+ continue;
+
+ /* Found the device (or we don't care which device) */
+ for (l = d->lines; l; l = l->next) {
+ /* Search for the right line */
+ if (!strcasecmp(l->name, line)) {
+ if (tmpl) {
+ ast_verb(2, "Ambiguous line name: %s\n", line);
+ ast_mutex_unlock(&devicelock);
+ return NULL;
+ } else
+ tmpl = l;
+ }
+ }
+ }
+ ast_mutex_unlock(&devicelock);
+ return tmpl;
+}
+
+/*!
+ * implement the setvar config line
+ */
+static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
+{
+ struct ast_variable *tmpvar = NULL;
+ char *varname = ast_strdupa(buf), *varval = NULL;
+
+ if ((varval = strchr(varname,'='))) {
+ *varval++ = '\0';
+ if ((tmpvar = ast_variable_new(varname, varval, ""))) {
+ tmpvar->next = list;
+ list = tmpvar;
+ }
+ }
+ return list;
+}
+
+/* It's quicker/easier to find the subchannel when we know the instance number too */
+static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference)
+{
+ struct skinny_line *l = find_line_by_instance(d, instance);
+ struct skinny_subchannel *sub;
+
+ if (!l) {
+ return NULL;
+ }
+
+ /* 7920 phones set call reference to 0, so use the first
+ sub-channel on the list.
+ This MIGHT need more love to be right */
+ if (!reference)
+ sub = l->sub;
+ else {
+ for (sub = l->sub; sub; sub = sub->next) {
+ if (sub->callid == reference)
+ break;
+ }
+ }
+ if (!sub) {
+ ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s'\n", reference, d->name);
+ }
+ return sub;
+}
+
+/* Find the subchannel when we only have the callid - this shouldn't happen often */
+static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_device *d, int reference)
+{
+ struct skinny_line *l;
+ struct skinny_subchannel *sub = NULL;
+
+ for (l = d->lines; l; l = l->next) {
+ for (sub = l->sub; sub; sub = sub->next) {
+ if (sub->callid == reference)
+ break;
+ }
+ if (sub)
+ break;
+ }
+
+ if (!l) {
+ ast_log(LOG_WARNING, "Could not find any lines that contained a subchannel with reference '%d' on device '%s'\n", reference, d->name);
+ } else {
+ if (!sub) {
+ ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s@%s'\n", reference, l->name, d->name);
+ }
+ }
+ return sub;
+}
+
+static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance, int isHint)
+{
+ struct skinny_speeddial *sd;
+
+ for (sd = d->speeddials; sd; sd = sd->next) {
+ if (sd->isHint == isHint && sd->instance == instance)
+ break;
+ }
+
+ if (!sd) {
+ ast_log(LOG_WARNING, "Could not find speeddial with instance '%d' on device '%s'\n", instance, d->name);
+ }
+ return sd;
+}
+
+static int codec_skinny2ast(enum skinny_codecs skinnycodec)
+{
+ switch (skinnycodec) {
+ case SKINNY_CODEC_ALAW:
+ return AST_FORMAT_ALAW;
+ case SKINNY_CODEC_ULAW:
+ return AST_FORMAT_ULAW;
+ case SKINNY_CODEC_G723_1:
+ return AST_FORMAT_G723_1;
+ case SKINNY_CODEC_G729A:
+ return AST_FORMAT_G729A;
+ case SKINNY_CODEC_G726_32:
+ return AST_FORMAT_G726_AAL2; /* XXX Is this right? */
+ case SKINNY_CODEC_H261:
+ return AST_FORMAT_H261;
+ case SKINNY_CODEC_H263:
+ return AST_FORMAT_H263;
+ default:
+ return 0;
+ }
+}
+
+static int codec_ast2skinny(int astcodec)
+{
+ switch (astcodec) {
+ case AST_FORMAT_ALAW:
+ return SKINNY_CODEC_ALAW;
+ case AST_FORMAT_ULAW:
+ return SKINNY_CODEC_ULAW;
+ case AST_FORMAT_G723_1:
+ return SKINNY_CODEC_G723_1;
+ case AST_FORMAT_G729A:
+ return SKINNY_CODEC_G729A;
+ case AST_FORMAT_G726_AAL2: /* XXX Is this right? */
+ return SKINNY_CODEC_G726_32;
+ case AST_FORMAT_H261:
+ return SKINNY_CODEC_H261;
+ case AST_FORMAT_H263:
+ return SKINNY_CODEC_H263;
+ default:
+ return 0;
+ }
+}
+
+static int set_callforwards(struct skinny_line *l, const char *cfwd, int cfwdtype)
+{
+ if (!l)
+ return 0;
+
+ if (!ast_strlen_zero(cfwd)) {
+ if (cfwdtype & SKINNY_CFWD_ALL) {
+ l->cfwdtype |= SKINNY_CFWD_ALL;
+ ast_copy_string(l->call_forward_all, cfwd, sizeof(l->call_forward_all));
+ }
+ if (cfwdtype & SKINNY_CFWD_BUSY) {
+ l->cfwdtype |= SKINNY_CFWD_BUSY;
+ ast_copy_string(l->call_forward_busy, cfwd, sizeof(l->call_forward_busy));
+ }
+ if (cfwdtype & SKINNY_CFWD_NOANSWER) {
+ l->cfwdtype |= SKINNY_CFWD_NOANSWER;
+ ast_copy_string(l->call_forward_noanswer, cfwd, sizeof(l->call_forward_noanswer));
+ }
+ } else {
+ if (cfwdtype & SKINNY_CFWD_ALL) {
+ l->cfwdtype &= ~SKINNY_CFWD_ALL;
+ memset(l->call_forward_all, 0, sizeof(l->call_forward_all));
+ }
+ if (cfwdtype & SKINNY_CFWD_BUSY) {
+ l->cfwdtype &= ~SKINNY_CFWD_BUSY;
+ memset(l->call_forward_busy, 0, sizeof(l->call_forward_busy));
+ }
+ if (cfwdtype & SKINNY_CFWD_NOANSWER) {
+ l->cfwdtype &= ~SKINNY_CFWD_NOANSWER;
+ memset(l->call_forward_noanswer, 0, sizeof(l->call_forward_noanswer));
+ }
+ }
+ return l->cfwdtype;
+}
+
+static void cleanup_stale_contexts(char *new, char *old)
+{
+ char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
+
+ while ((oldcontext = strsep(&old, "&"))) {
+ stalecontext = '\0';
+ ast_copy_string(newlist, new, sizeof(newlist));
+ stringp = newlist;
+ while ((newcontext = strsep(&stringp, "&"))) {
+ if (strcmp(newcontext, oldcontext) == 0) {
+ /* This is not the context you're looking for */
+ stalecontext = '\0';
+ break;
+ } else if (strcmp(newcontext, oldcontext)) {
+ stalecontext = oldcontext;
+ }
+
+ }
+ if (stalecontext)
+ ast_context_destroy(ast_context_find(stalecontext), "Skinny");
+ }
+}
+
+static void register_exten(struct skinny_line *l)
+{
+ char multi[256];
+ char *stringp, *ext, *context;
+
+ if (ast_strlen_zero(regcontext))
+ return;
+
+ ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi));
+ stringp = multi;
+ while ((ext = strsep(&stringp, "&"))) {
+ if ((context = strchr(ext, '@'))) {
+ *context++ = '\0'; /* split ext@context */
+ if (!ast_context_find(context)) {
+ ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
+ continue;
+ }
+ } else {
+ context = regcontext;
+ }
+ ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
+ ast_strdup(l->name), ast_free_ptr, "Skinny");
+ }
+}
+
+static void unregister_exten(struct skinny_line *l)
+{
+ char multi[256];
+ char *stringp, *ext, *context;
+
+ if (ast_strlen_zero(regcontext))
+ return;
+
+ ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi));
+ stringp = multi;
+ while ((ext = strsep(&stringp, "&"))) {
+ if ((context = strchr(ext, '@'))) {
+ *context++ = '\0'; /* split ext@context */
+ if (!ast_context_find(context)) {
+ ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
+ continue;
+ }
+ } else {
+ context = regcontext;
+ }
+ ast_context_remove_extension(context, ext, 1, NULL);
+ }
+}
+
+static int skinny_register(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+ struct skinny_speeddial *sd;
+ struct sockaddr_in sin;
+ socklen_t slen;
+
+ ast_mutex_lock(&devicelock);
+ for (d = devices; d; d = d->next) {
+ if (!strcasecmp(req->data.reg.name, d->id)
+ && ast_apply_ha(d->ha, &(s->sin))) {
+ s->device = d;
+ d->type = letohl(req->data.reg.type);
+ if (ast_strlen_zero(d->version_id)) {
+ ast_copy_string(d->version_id, version_id, sizeof(d->version_id));
+ }
+ d->registered = 1;
+ d->session = s;
+
+ slen = sizeof(sin);
+ if (getsockname(s->fd, (struct sockaddr *)&sin, &slen)) {
+ ast_log(LOG_WARNING, "Cannot get socket name\n");
+ sin.sin_addr = __ourip;
+ }
+ d->ourip = sin.sin_addr;
+
+ for (sd = d->speeddials; sd; sd = sd->next) {
+ sd->stateid = ast_extension_state_add(sd->context, sd->exten, skinny_extensionstate_cb, sd);
+ }
+ for (l = d->lines; l; l = l->next) {
+ register_exten(l);
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+ }
+ break;
+ }
+ }
+ ast_mutex_unlock(&devicelock);
+ if (!d) {
+ return 0;
+ }
+ return 1;
+}
+
+static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+ struct skinny_speeddial *sd;
+
+ d = s->device;
+
+ if (d) {
+ d->session = NULL;
+ d->registered = 0;
+
+ for (sd = d->speeddials; sd; sd = sd->next) {
+ if (sd->stateid > -1)
+ ast_extension_state_del(sd->stateid, NULL);
+ }
+ for (l = d->lines; l; l = l->next) {
+ unregister_exten(l);
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+ }
+ }
+
+ return -1; /* main loop will destroy the session */
+}
+
+static int transmit_response(struct skinnysession *s, struct skinny_req *req)
+{
+ int res = 0;
+
+ if (!s) {
+ ast_log(LOG_WARNING, "Asked to transmit to a non-existant session!\n");
+ return -1;
+ }
+
+ ast_mutex_lock(&s->lock);
+
+ if (skinnydebug)
+ ast_log(LOG_VERBOSE, "writing packet type %04X (%d bytes) to socket %d\n", letohl(req->e), letohl(req->len)+8, s->fd);
+
+ if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
+ ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
+ return -1;
+ }
+
+ memset(s->outbuf,0,sizeof(s->outbuf));
+ memcpy(s->outbuf, req, skinny_header_size);
+ memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
+
+ res = write(s->fd, s->outbuf, letohl(req->len)+8);
+
+ if (res != letohl(req->len)+8) {
+ ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
+ if (res == -1) {
+ if (skinnydebug)
+ ast_log(LOG_WARNING, "Transmit: Skinny Client was lost, unregistering\n");
+ skinny_unregister(NULL, s);
+ }
+
+ }
+
+ ast_mutex_unlock(&s->lock);
+ return 1;
+}
+
+static void transmit_speaker_mode(struct skinnysession *s, int mode)
+{
+ struct skinny_req *req;
+
+ if (!(req = req_alloc(sizeof(struct set_speaker_message), SET_SPEAKER_MESSAGE)))
+ return;
+
+ req->data.setspeaker.mode = htolel(mode);
+ transmit_response(s, req);
+}
+/*
+static void transmit_microphone_mode(struct skinnysession *s, int mode)
+{
+ struct skinny_req *req;
+
+ if (!(req = req_alloc(sizeof(struct set_microphone_message), SET_MICROPHONE_MESSAGE)))
+ return;
+
+ req->data.setmicrophone.mode = htolel(mode);
+ transmit_response(s, req);
+}
+*/
+
+static void transmit_callinfo(struct skinnysession *s, const char *fromname, const char *fromnum, const char *toname, const char *tonum, int instance, int callid, int calltype)
+{
+ struct skinny_req *req;
+
+ if (!(req = req_alloc(sizeof(struct call_info_message), CALL_INFO_MESSAGE)))
+ return;
+
+ if (skinnydebug)
+ ast_verbose("Setting Callinfo to %s(%s) from %s(%s) on %s(%d)\n", fromname, fromnum, toname, tonum, s->device->name, instance);
+
+ if (fromname) {
+ ast_copy_string(req->data.callinfo.callingPartyName, fromname, sizeof(req->data.callinfo.callingPartyName));
+ }
+ if (fromnum) {
+ ast_copy_string(req->data.callinfo.callingParty, fromnum, sizeof(req->data.callinfo.callingParty));
+ }
+ if (toname) {
+ ast_copy_string(req->data.callinfo.calledPartyName, toname, sizeof(req->data.callinfo.calledPartyName));
+ }
+ if (tonum) {
+ ast_copy_string(req->data.callinfo.calledParty, tonum, sizeof(req->data.callinfo.calledParty));
+ }
+ req->data.callinfo.instance = htolel(instance);
+ req->data.callinfo.reference = htolel(callid);
+ req->data.callinfo.type = htolel(calltype);
+ transmit_response(s, req);
+}
+
+static void transmit_connect(struct skinnysession *s, struct skinny_subchannel *sub)
+{
+ struct skinny_req *req;
+ struct skinny_line *l = sub->parent;
+ struct ast_format_list fmt;
+
+ if (!(req = req_alloc(sizeof(struct open_receive_channel_message), OPEN_RECEIVE_CHANNEL_MESSAGE)))
+ return;
+
+ fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
+
+ req->data.openreceivechannel.conferenceId = htolel(sub->callid);
+ req->data.openreceivechannel.partyId = htolel(sub->callid);
+ req->data.openreceivechannel.packets = htolel(fmt.cur_ms);
+ req->data.openreceivechannel.capability = htolel(codec_ast2skinny(fmt.bits));
+ req->data.openreceivechannel.echo = htolel(0);
+ req->data.openreceivechannel.bitrate = htolel(0);
+ transmit_response(s, req);
+}
+
+static void transmit_tone(struct skinnysession *s, int tone, int instance, int reference)
+{
+ struct skinny_req *req;
+
+ if (tone == SKINNY_NOTONE) {
+ /* This is bad, mmm'kay? */
+ return;
+ }
+
+ if (tone > 0) {
+ if (!(req = req_alloc(sizeof(struct start_tone_message), START_TONE_MESSAGE)))
+ return;
+ req->data.starttone.tone = htolel(tone);
+ req->data.starttone.instance = htolel(instance);
+ req->data.starttone.reference = htolel(reference);
+ } else {
+ if (!(req = req_alloc(sizeof(struct stop_tone_message), STOP_TONE_MESSAGE)))
+ return;
+ req->data.stoptone.instance = htolel(instance);
+ req->data.stoptone.reference = htolel(reference);
+ }
+
+ if (tone > 0) {
+ req->data.starttone.tone = htolel(tone);
+ }
+ transmit_response(s, req);
+}
+
+static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int callid, int softkey)
+{
+ struct skinny_req *req;
+
+ if (!(req = req_alloc(sizeof(struct select_soft_keys_message), SELECT_SOFT_KEYS_MESSAGE)))
+ return;
+
+ req->data.selectsoftkey.instance = htolel(instance);
+ req->data.selectsoftkey.reference = htolel(callid);
+ req->data.selectsoftkey.softKeySetIndex = htolel(softkey);
+ req->data.selectsoftkey.validKeyMask = htolel(0xFFFFFFFF);
+ transmit_response(s, req);
+}
+
+static void transmit_lamp_indication(struct skinnysession *s, int stimulus, int instance, int indication)
+{
+ struct skinny_req *req;
+
+ if (!(req = req_alloc(sizeof(struct set_lamp_message), SET_LAMP_MESSAGE)))
+ return;
+
+ req->data.setlamp.stimulus = htolel(stimulus);
+ req->data.setlamp.stimulusInstance = htolel(instance);
+ req->data.setlamp.deviceStimulus = htolel(indication);
+ transmit_response(s, req);
+}
+
+static void transmit_ringer_mode(struct skinnysession *s, int mode)
+{
+ struct skinny_req *req;
+
+ if (skinnydebug)
+ ast_verbose("Setting ringer mode to '%d'.\n", mode);
+
+ if (!(req = req_alloc(sizeof(struct set_ringer_message), SET_RINGER_MESSAGE)))
+ return;
+
+ req->data.setringer.ringerMode = htolel(mode);
+ /* XXX okay, I don't quite know what this is, but here's what happens (on a 7960).
+ Note: The phone will always show as ringing on the display.
+
+ 1: phone will audibly ring over and over
+ 2: phone will audibly ring only once
+ any other value, will NOT cause the phone to audibly ring
+ */
+ req->data.setringer.unknown1 = htolel(1);
+ /* XXX the value here doesn't seem to change anything. Must be higher than 0.
+ Perhaps a packet capture can shed some light on this. */
+ req->data.setringer.unknown2 = htolel(1);
+ transmit_response(s, req);
+}
+
+static void transmit_displaymessage(struct skinnysession *s, const char *text, int instance, int reference)
+{
+ struct skinny_req *req;
+
+ if (text == 0) {
+ if (!(req = req_alloc(0, CLEAR_DISPLAY_MESSAGE)))
+ return;
+
+ req->data.clearpromptstatus.lineInstance = instance;
+ req->data.clearpromptstatus.callReference = reference;
+
+ if (skinnydebug)
+ ast_verbose("Clearing Display\n");
+ } else {
+ if (!(req = req_alloc(sizeof(struct displaytext_message), DISPLAYTEXT_MESSAGE)))
+ return;
+
+ ast_copy_string(req->data.displaytext.text, text, sizeof(req->data.displaytext.text));
+ if (skinnydebug)
+ ast_verbose("Displaying message '%s'\n", req->data.displaytext.text);
+ }
+
+ transmit_response(s, req);
+}
+
+static void transmit_displaynotify(struct skinnysession *s, const char *text, int t)
+{
+ struct skinny_req *req;
+
+ if (!(req = req_alloc(sizeof(struct display_notify_message), DISPLAY_NOTIFY_MESSAGE)))
+ return;
+
+ ast_copy_string(req->data.displaynotify.displayMessage, text, sizeof(req->data.displaynotify.displayMessage));
+ req->data.displaynotify.displayTimeout = htolel(t);
+
+ if (skinnydebug)
+ ast_verbose("Displaying notify '%s'\n", text);
+
+ transmit_response(s, req);
+}
+
+static void transmit_displaypromptstatus(struct skinnysession *s, const char *text, int t, int instance, int callid)
+{
+ struct skinny_req *req;
+
+ if (text == 0) {
+ if (!(req = req_alloc(sizeof(struct clear_prompt_message), CLEAR_PROMPT_MESSAGE)))
+ return;
+
+ req->data.clearpromptstatus.lineInstance = htolel(instance);
+ req->data.clearpromptstatus.callReference = htolel(callid);
+
+ if (skinnydebug)
+ ast_verbose("Clearing Prompt\n");
+ } else {
+ if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE)))
+ return;
+
+ ast_copy_string(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage));
+ req->data.displaypromptstatus.messageTimeout = htolel(t);
+ req->data.displaypromptstatus.lineInstance = htolel(instance);
+ req->data.displaypromptstatus.callReference = htolel(callid);
+
+ if (skinnydebug)
+ ast_verbose("Displaying Prompt Status '%s'\n", text);
+ }
+
+ transmit_response(s, req);
+}
+
+static void transmit_dialednumber(struct skinnysession *s, const char *text, int instance, int callid)
+{
+ struct skinny_req *req;
+
+ if (!(req = req_alloc(sizeof(struct dialed_number_message), DIALED_NUMBER_MESSAGE)))
+ return;
+
+ ast_copy_string(req->data.dialednumber.dialedNumber, text, sizeof(req->data.dialednumber.dialedNumber));
+ req->data.dialednumber.lineInstance = htolel(instance);
+ req->data.dialednumber.callReference = htolel(callid);
+
+ transmit_response(s, req);
+}
+
+static void transmit_callstate(struct skinnysession *s, int instance, int state, unsigned callid)
+{
+ struct skinny_req *req;
+
+ if (state == SKINNY_ONHOOK) {
+ if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
+ return;
+
+ req->data.closereceivechannel.conferenceId = htolel(callid);
+ req->data.closereceivechannel.partyId = htolel(callid);
+ transmit_response(s, req);
+
+ if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
+ return;
+
+ req->data.stopmedia.conferenceId = htolel(callid);
+ req->data.stopmedia.passThruPartyId = htolel(callid);
+ transmit_response(s, req);
+
+ transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
+
+ transmit_displaypromptstatus(s, NULL, 0, instance, callid);
+ }
+
+ if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
+ return;
+
+ req->data.callstate.callState = htolel(state);
+ req->data.callstate.lineInstance = htolel(instance);
+ req->data.callstate.callReference = htolel(callid);
+ transmit_response(s, req);
+
+ if (state == SKINNY_ONHOOK) {
+ transmit_selectsoftkeys(s, 0, 0, KEYDEF_ONHOOK);
+ }
+
+ if (state == SKINNY_OFFHOOK || state == SKINNY_ONHOOK) {
+ if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
+ return;
+
+ req->data.activatecallplane.lineInstance = htolel(instance);
+ transmit_response(s, req);
+ }
+}
+
+
+static void transmit_cfwdstate(struct skinnysession *s, struct skinny_line *l)
+{
+ struct skinny_req *req;
+ int anyon = 0;
+
+ if (!(req = req_alloc(sizeof(struct forward_stat_message), FORWARD_STAT_MESSAGE)))
+ return;
+
+ if (l->cfwdtype & SKINNY_CFWD_ALL) {
+ if (!ast_strlen_zero(l->call_forward_all)) {
+ ast_copy_string(req->data.forwardstat.fwdallnum, l->call_forward_all, sizeof(req->data.forwardstat.fwdallnum));
+ req->data.forwardstat.fwdall = htolel(1);
+ anyon++;
+ } else {
+ req->data.forwardstat.fwdall = htolel(0);
+ }
+ }
+ if (l->cfwdtype & SKINNY_CFWD_BUSY) {
+ if (!ast_strlen_zero(l->call_forward_busy)) {
+ ast_copy_string(req->data.forwardstat.fwdbusynum, l->call_forward_busy, sizeof(req->data.forwardstat.fwdbusynum));
+ req->data.forwardstat.fwdbusy = htolel(1);
+ anyon++;
+ } else {
+ req->data.forwardstat.fwdbusy = htolel(0);
+ }
+ }
+ if (l->cfwdtype & SKINNY_CFWD_NOANSWER) {
+ if (!ast_strlen_zero(l->call_forward_noanswer)) {
+ ast_copy_string(req->data.forwardstat.fwdnoanswernum, l->call_forward_noanswer, sizeof(req->data.forwardstat.fwdnoanswernum));
+ req->data.forwardstat.fwdnoanswer = htolel(1);
+ anyon++;
+ } else {
+ req->data.forwardstat.fwdnoanswer = htolel(0);
+ }
+ }
+ req->data.forwardstat.lineNumber = htolel(l->instance);
+ if (anyon)
+ req->data.forwardstat.activeforward = htolel(7);
+ else
+ req->data.forwardstat.activeforward = htolel(0);
+
+ transmit_response(s, req);
+}
+
+static int skinny_extensionstate_cb(char *context, char *exten, int state, void *data)
+{
+ struct skinny_speeddial *sd = data;
+ struct skinny_device *d = sd->parent;
+ struct skinnysession *s = d->session;
+ char hint[AST_MAX_EXTENSION];
+ int callstate = SKINNY_CALLREMOTEMULTILINE;
+ int lamp = SKINNY_LAMP_OFF;
+
+ switch (state) {
+ case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
+ case AST_EXTENSION_REMOVED: /* Extension is gone */
+ ast_verb(2, "Extension state: Watcher for hint %s %s. Notify Device %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", d->name);
+ sd->stateid = -1;
+ callstate = SKINNY_ONHOOK;
+ lamp = SKINNY_LAMP_OFF;
+ break;
+ case AST_EXTENSION_RINGING:
+ case AST_EXTENSION_UNAVAILABLE:
+ callstate = SKINNY_RINGIN;
+ lamp = SKINNY_LAMP_BLINK;
+ break;
+ case AST_EXTENSION_BUSY: /* callstate = SKINNY_BUSY wasn't wanting to work - I'll settle for this */
+ case AST_EXTENSION_INUSE:
+ callstate = SKINNY_CALLREMOTEMULTILINE;
+ lamp = SKINNY_LAMP_ON;
+ break;
+ case AST_EXTENSION_ONHOLD:
+ callstate = SKINNY_HOLD;
+ lamp = SKINNY_LAMP_WINK;
+ break;
+ case AST_EXTENSION_NOT_INUSE:
+ default:
+ callstate = SKINNY_ONHOOK;
+ lamp = SKINNY_LAMP_OFF;
+ break;
+ }
+
+ if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, sd->context, sd->exten)) {
+ /* If they are not registered, we will override notification and show no availability */
+ if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) {
+ callstate = SKINNY_ONHOOK;
+ lamp = SKINNY_LAMP_FLASH;
+ }
+ }
+
+ transmit_lamp_indication(s, STIMULUS_LINE, sd->instance, lamp);
+ transmit_callstate(s, sd->instance, callstate, 0);
+ sd->laststate = state;
+
+ return 0;
+}
+
+static int has_voicemail(struct skinny_line *l)
+{
+ return ast_app_has_voicemail(l->mailbox, NULL);
+}
+
+static void do_housekeeping(struct skinnysession *s)
+{
+ int new;
+ int old;
+ int device_lamp = 0;
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+
+ /* Update time on device */
+ handle_time_date_req_message(NULL, s);
+
+ /* Set MWI on individual lines */
+ for (l = d->lines; l; l = l->next) {
+ if (has_voicemail(l)) {
+ if (skinnydebug)
+ ast_verbose("Checking for voicemail Skinny %s@%s\n", l->name, d->name);
+ ast_app_inboxcount(l->mailbox, &new, &old);
+ if (skinnydebug)
+ ast_verbose("Skinny %s@%s has voicemail!\n", l->name, d->name);
+ transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
+ device_lamp++;
+ } else {
+ transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
+ }
+ }
+ /* If at least one line has VM, turn the device level lamp on */
+ if (device_lamp)
+ transmit_lamp_indication(s, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_ON);
+ else
+ transmit_lamp_indication(s, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
+}
+
+/* I do not believe skinny can deal with video.
+ Anyone know differently? */
+/* Yes, it can. Currently 7985 and Cisco VT Advantage do video. */
+static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
+{
+ struct skinny_subchannel *sub = NULL;
+
+ if (!(sub = c->tech_pvt) || !(sub->vrtp))
+ return AST_RTP_GET_FAILED;
+
+ *rtp = sub->vrtp;
+
+ return AST_RTP_TRY_NATIVE;
+}
+
+static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
+{
+ struct skinny_subchannel *sub = NULL;
+ struct skinny_line *l;
+ enum ast_rtp_get_result res = AST_RTP_TRY_NATIVE;
+
+ if (skinnydebug)
+ ast_verbose("skinny_get_rtp_peer() Channel = %s\n", c->name);
+
+
+ if (!(sub = c->tech_pvt))
+ return AST_RTP_GET_FAILED;
+
+ ast_mutex_lock(&sub->lock);
+
+ if (!(sub->rtp)){
+ ast_mutex_unlock(&sub->lock);
+ return AST_RTP_GET_FAILED;
+ }
+
+ *rtp = sub->rtp;
+
+ l = sub->parent;
+
+ if (!l->canreinvite || l->nat){
+ res = AST_RTP_TRY_PARTIAL;
+ if (skinnydebug)
+ ast_verbose("skinny_get_rtp_peer() Using AST_RTP_TRY_PARTIAL \n");
+ }
+
+ ast_mutex_unlock(&sub->lock);
+
+ return res;
+
+}
+
+static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+{
+ struct skinny_subchannel *sub;
+ struct skinny_line *l;
+ struct skinny_device *d;
+ struct skinnysession *s;
+ struct ast_format_list fmt;
+ struct sockaddr_in us;
+ struct sockaddr_in them;
+ struct skinny_req *req;
+
+ sub = c->tech_pvt;
+
+ if (c->_state != AST_STATE_UP)
+ return 0;
+
+ if (!sub) {
+ return -1;
+ }
+
+ l = sub->parent;
+ d = l->parent;
+ s = d->session;
+
+ if (rtp){
+ ast_rtp_get_peer(rtp, &them);
+
+ /* Shutdown any early-media or previous media on re-invite */
+ if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
+ return -1;
+
+ req->data.stopmedia.conferenceId = htolel(sub->callid);
+ req->data.stopmedia.passThruPartyId = htolel(sub->callid);
+ transmit_response(s, req);
+
+ if (skinnydebug)
+ ast_verbose("Peerip = %s:%d\n", ast_inet_ntoa(them.sin_addr), ntohs(them.sin_port));
+
+ if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
+ return -1;
+
+ fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
+
+ if (skinnydebug)
+ ast_verbose("Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
+
+ req->data.startmedia.conferenceId = htolel(sub->callid);
+ req->data.startmedia.passThruPartyId = htolel(sub->callid);
+ if (!(l->canreinvite) || (l->nat)){
+ ast_rtp_get_us(rtp, &us);
+ req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
+ req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
+ } else {
+ req->data.startmedia.remoteIp = htolel(them.sin_addr.s_addr);
+ req->data.startmedia.remotePort = htolel(ntohs(them.sin_port));
+ }
+ req->data.startmedia.packetSize = htolel(fmt.cur_ms);
+ req->data.startmedia.payloadType = htolel(codec_ast2skinny(fmt.bits));
+ req->data.startmedia.qualifier.precedence = htolel(127);
+ req->data.startmedia.qualifier.vad = htolel(0);
+ req->data.startmedia.qualifier.packets = htolel(0);
+ req->data.startmedia.qualifier.bitRate = htolel(0);
+ transmit_response(s, req);
+
+ return 0;
+ }
+ /* Need a return here to break the bridge */
+ return 0;
+}
+
+static struct ast_rtp_protocol skinny_rtp = {
+ .type = "Skinny",
+ .get_rtp_info = skinny_get_rtp_peer,
+ .get_vrtp_info = skinny_get_vrtp_peer,
+ .set_rtp_peer = skinny_set_rtp_peer,
+};
+
+static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny set debug";
+ e->usage =
+ "Usage: skinny set debug\n"
+ " Enables dumping of Skinny packets for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ skinnydebug = 1;
+ ast_cli(a->fd, "Skinny Debugging Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_skinny_set_debug_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny set debug off";
+ e->usage =
+ "Usage: skinny set debug off\n"
+ " Disables dumping of Skinny packets for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ skinnydebug = 0;
+ ast_cli(a->fd, "Skinny Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *complete_skinny_devices(const char *word, int state)
+{
+ struct skinny_device *d;
+ char *result = NULL;
+ int wordlen = strlen(word), which = 0;
+
+ for (d = devices; d && !result; d = d->next) {
+ if (!strncasecmp(word, d->id, wordlen) && ++which > state)
+ result = ast_strdup(d->id);
+ }
+
+ return result;
+}
+
+static char *complete_skinny_show_device(const char *line, const char *word, int pos, int state)
+{
+ return (pos == 3 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
+}
+
+static char *complete_skinny_reset(const char *line, const char *word, int pos, int state)
+{
+ return (pos == 2 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
+}
+
+static char *complete_skinny_show_line(const char *line, const char *word, int pos, int state)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+ char *result = NULL;
+ int wordlen = strlen(word), which = 0;
+
+ if (pos != 3)
+ return NULL;
+
+ for (d = devices; d && !result; d = d->next) {
+ for (l = d->lines; l && !result; l = l->next) {
+ if (!strncasecmp(word, l->name, wordlen) && ++which > state)
+ result = ast_strdup(l->name);
+ }
+ }
+
+ return result;
+}
+
+static char *handle_skinny_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct skinny_device *d;
+ struct skinny_req *req;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny reset";
+ e->usage =
+ "Usage: skinny reset <DeviceId|DeviceName|all> [restart]\n"
+ " Causes a Skinny device to reset itself, optionally with a full restart\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_skinny_reset(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&devicelock);
+
+ for (d = devices; d; d = d->next) {
+ int fullrestart = 0;
+ if (!strcasecmp(a->argv[2], d->id) || !strcasecmp(a->argv[2], d->name) || !strcasecmp(a->argv[2], "all")) {
+ if (!(d->session))
+ continue;
+
+ if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
+ continue;
+
+ if (a->argc == 4 && !strcasecmp(a->argv[3], "restart"))
+ fullrestart = 1;
+
+ if (fullrestart)
+ req->data.reset.resetType = 2;
+ else
+ req->data.reset.resetType = 1;
+
+ ast_verb(3, "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id);
+ transmit_response(d->session, req);
+ }
+ }
+ ast_mutex_unlock(&devicelock);
+ return CLI_SUCCESS;
+}
+
+static char *device2str(int type)
+{
+ char *tmp;
+
+ switch (type) {
+ case SKINNY_DEVICE_NONE:
+ return "No Device";
+ case SKINNY_DEVICE_30SPPLUS:
+ return "30SP Plus";
+ case SKINNY_DEVICE_12SPPLUS:
+ return "12SP Plus";
+ case SKINNY_DEVICE_12SP:
+ return "12SP";
+ case SKINNY_DEVICE_12:
+ return "12";
+ case SKINNY_DEVICE_30VIP:
+ return "30VIP";
+ case SKINNY_DEVICE_7910:
+ return "7910";
+ case SKINNY_DEVICE_7960:
+ return "7960";
+ case SKINNY_DEVICE_7940:
+ return "7940";
+ case SKINNY_DEVICE_7935:
+ return "7935";
+ case SKINNY_DEVICE_ATA186:
+ return "ATA186";
+ case SKINNY_DEVICE_7941:
+ return "7941";
+ case SKINNY_DEVICE_7971:
+ return "7971";
+ case SKINNY_DEVICE_7985:
+ return "7985";
+ case SKINNY_DEVICE_7911:
+ return "7911";
+ case SKINNY_DEVICE_7961GE:
+ return "7961GE";
+ case SKINNY_DEVICE_7941GE:
+ return "7941GE";
+ case SKINNY_DEVICE_7921:
+ return "7921";
+ case SKINNY_DEVICE_7905:
+ return "7905";
+ case SKINNY_DEVICE_7920:
+ return "7920";
+ case SKINNY_DEVICE_7970:
+ return "7970";
+ case SKINNY_DEVICE_7912:
+ return "7912";
+ case SKINNY_DEVICE_7902:
+ return "7902";
+ case SKINNY_DEVICE_CIPC:
+ return "IP Communicator";
+ case SKINNY_DEVICE_7961:
+ return "7961";
+ case SKINNY_DEVICE_7936:
+ return "7936";
+ case SKINNY_DEVICE_SCCPGATEWAY_AN:
+ return "SCCPGATEWAY_AN";
+ case SKINNY_DEVICE_SCCPGATEWAY_BRI:
+ return "SCCPGATEWAY_BRI";
+ case SKINNY_DEVICE_UNKNOWN:
+ return "Unknown";
+ default:
+ if (!(tmp = ast_threadstorage_get(&device2str_threadbuf, DEVICE2STR_BUFSIZE)))
+ return "Unknown";
+ snprintf(tmp, DEVICE2STR_BUFSIZE, "UNKNOWN-%d", type);
+ return tmp;
+ }
+}
+
+/*! \brief Print codec list from preference to CLI/manager */
+static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
+{
+ int x, codec;
+
+ for(x = 0; x < 32 ; x++) {
+ codec = ast_codec_pref_index(pref, x);
+ if (!codec)
+ break;
+ ast_cli(fd, "%s", ast_getformatname(codec));
+ ast_cli(fd, ":%d", pref->framing[x]);
+ if (x < 31 && ast_codec_pref_index(pref, x + 1))
+ ast_cli(fd, ",");
+ }
+ if (!x)
+ ast_cli(fd, "none");
+}
+
+static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny show devices";
+ e->usage =
+ "Usage: skinny show devices\n"
+ " Lists all devices known to the Skinny subsystem.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&devicelock);
+
+ ast_cli(a->fd, "Name DeviceId IP Type R NL\n");
+ ast_cli(a->fd, "-------------------- ---------------- --------------- --------------- - --\n");
+
+ for (d = devices; d; d = d->next) {
+ int numlines = 0;
+
+ for (l = d->lines; l; l = l->next)
+ numlines++;
+
+ ast_cli(a->fd, "%-20s %-16s %-15s %-15s %c %2d\n",
+ d->name,
+ d->id,
+ d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
+ device2str(d->type),
+ d->registered?'Y':'N',
+ numlines);
+ }
+
+ ast_mutex_unlock(&devicelock);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Show device information */
+static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+ struct skinny_speeddial *sd;
+ struct skinny_addon *sa;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny show device";
+ e->usage =
+ "Usage: skinny show device <DeviceId|DeviceName>\n"
+ " Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&devicelock);
+ for (d = devices; d; d = d->next) {
+ if (!strcasecmp(a->argv[3], d->id) || !strcasecmp(a->argv[3], d->name)) {
+ int numlines = 0, numaddons = 0, numspeeddials = 0;
+
+ for (l = d->lines; l; l = l->next)
+ numlines++;
+
+ ast_cli(a->fd, "Name: %s\n", d->name);
+ ast_cli(a->fd, "Id: %s\n", d->id);
+ ast_cli(a->fd, "version: %s\n", S_OR(d->version_id, "Unknown"));
+ ast_cli(a->fd, "Ip address: %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
+ ast_cli(a->fd, "Port: %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
+ ast_cli(a->fd, "Device Type: %s\n", device2str(d->type));
+ ast_cli(a->fd, "Registered: %s\n", (d->registered ? "Yes" : "No"));
+ ast_cli(a->fd, "Lines: %d\n", numlines);
+ for (l = d->lines; l; l = l->next)
+ ast_cli(a->fd, " %s (%s)\n", l->name, l->label);
+ for (sa = d->addons; sa; sa = sa->next)
+ numaddons++;
+ ast_cli(a->fd, "Addons: %d\n", numaddons);
+ for (sa = d->addons; sa; sa = sa->next)
+ ast_cli(a->fd, " %s\n", sa->type);
+ for (sd = d->speeddials; sd; sd = sd->next)
+ numspeeddials++;
+ ast_cli(a->fd, "Speeddials: %d\n", numspeeddials);
+ for (sd = d->speeddials; sd; sd = sd->next)
+ ast_cli(a->fd, " %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
+ }
+ }
+ ast_mutex_unlock(&devicelock);
+ return CLI_SUCCESS;
+}
+
+static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny show lines";
+ e->usage =
+ "Usage: skinny show lines\n"
+ " Lists all lines known to the Skinny subsystem.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&devicelock);
+
+ ast_cli(a->fd, "Device Name Instance Name Label \n");
+ ast_cli(a->fd, "-------------------- -------- -------------------- --------------------\n");
+ for (d = devices; d; d = d->next) {
+ for (l = d->lines; l; l = l->next) {
+ ast_cli(a->fd, "%-20s %8d %-20s %-20s\n",
+ d->name,
+ l->instance,
+ l->name,
+ l->label);
+ }
+ }
+
+ ast_mutex_unlock(&devicelock);
+ return CLI_SUCCESS;
+}
+
+/*! \brief List line information. */
+static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+ char codec_buf[512];
+ char group_buf[256];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny show line";
+ e->usage =
+ "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
+ " List all lineinformation of a specific line known to the Skinny subsystem.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&devicelock);
+
+ /* Show all lines matching the one supplied */
+ for (d = devices; d; d = d->next) {
+ if (a->argc == 6 && (strcasecmp(a->argv[5], d->id) && strcasecmp(a->argv[5], d->name)))
+ continue;
+ for (l = d->lines; l; l = l->next) {
+ if (strcasecmp(a->argv[3], l->name))
+ continue;
+ ast_cli(a->fd, "Line: %s\n", l->name);
+ ast_cli(a->fd, "On Device: %s\n", d->name);
+ ast_cli(a->fd, "Line Label: %s\n", l->label);
+ ast_cli(a->fd, "Extension: %s\n", S_OR(l->exten, "<not set>"));
+ ast_cli(a->fd, "Context: %s\n", l->context);
+ ast_cli(a->fd, "CallGroup: %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
+ ast_cli(a->fd, "PickupGroup: %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
+ ast_cli(a->fd, "Language: %s\n", S_OR(l->language, "<not set>"));
+ ast_cli(a->fd, "Accountcode: %s\n", S_OR(l->accountcode, "<not set>"));
+ ast_cli(a->fd, "AmaFlag: %s\n", ast_cdr_flags2str(l->amaflags));
+ ast_cli(a->fd, "CallerId Number: %s\n", S_OR(l->cid_num, "<not set>"));
+ ast_cli(a->fd, "CallerId Name: %s\n", S_OR(l->cid_name, "<not set>"));
+ ast_cli(a->fd, "Hide CallerId: %s\n", (l->hidecallerid ? "Yes" : "No"));
+ ast_cli(a->fd, "CFwdAll: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
+ ast_cli(a->fd, "CFwdBusy: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
+ ast_cli(a->fd, "CFwdNoAnswer: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
+ ast_cli(a->fd, "VoicemailBox: %s\n", S_OR(l->mailbox, "<not set>"));
+ ast_cli(a->fd, "VoicemailNumber: %s\n", S_OR(l->vmexten, "<not set>"));
+ ast_cli(a->fd, "MWIblink: %d\n", l->mwiblink);
+ ast_cli(a->fd, "Regextension: %s\n", S_OR(l->regexten, "<not set>"));
+ ast_cli(a->fd, "Regcontext: %s\n", S_OR(l->regcontext, "<not set>"));
+ ast_cli(a->fd, "MoHInterpret: %s\n", S_OR(l->mohinterpret, "<not set>"));
+ ast_cli(a->fd, "MoHSuggest: %s\n", S_OR(l->mohsuggest, "<not set>"));
+ ast_cli(a->fd, "Last dialed nr: %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
+ ast_cli(a->fd, "Last CallerID: %s\n", S_OR(l->lastcallerid, "<not set>"));
+ ast_cli(a->fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
+ ast_cli(a->fd, "Callwaiting: %s\n", (l->callwaiting ? "Yes" : "No"));
+ ast_cli(a->fd, "3Way Calling: %s\n", (l->threewaycalling ? "Yes" : "No"));
+ ast_cli(a->fd, "Can forward: %s\n", (l->cancallforward ? "Yes" : "No"));
+ ast_cli(a->fd, "Do Not Disturb: %s\n", (l->dnd ? "Yes" : "No"));
+ ast_cli(a->fd, "NAT: %s\n", (l->nat ? "Yes" : "No"));
+ ast_cli(a->fd, "immediate: %s\n", (l->immediate ? "Yes" : "No"));
+ ast_cli(a->fd, "Group: %d\n", l->group);
+ ast_cli(a->fd, "Codecs: ");
+ ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->capability);
+ ast_cli(a->fd, "%s\n", codec_buf);
+ ast_cli(a->fd, "Codec Order: (");
+ print_codec_to_cli(a->fd, &l->prefs);
+ ast_cli(a->fd, ")\n");
+ ast_cli(a->fd, "\n");
+ }
+ }
+
+ ast_mutex_unlock(&devicelock);
+ return CLI_SUCCESS;
+}
+
+/*! \brief List global settings for the Skinny subsystem. */
+static char *handle_skinny_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "skinny show settings";
+ e->usage =
+ "Usage: skinny show settings\n"
+ " Lists all global configuration settings of the Skinny subsystem.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "\nGlobal Settings:\n");
+ ast_cli(a->fd, " Skinny Port: %d\n", ntohs(bindaddr.sin_port));
+ ast_cli(a->fd, " Bindaddress: %s\n", ast_inet_ntoa(bindaddr.sin_addr));
+ ast_cli(a->fd, " KeepAlive: %d\n", keep_alive);
+ ast_cli(a->fd, " Date Format: %s\n", date_format);
+ ast_cli(a->fd, " Voice Mail Extension: %s\n", S_OR(vmexten, "(not set)"));
+ ast_cli(a->fd, " Reg. context: %s\n", S_OR(regcontext, "(not set)"));
+ ast_cli(a->fd, " Jitterbuffer enabled: %s\n", (ast_test_flag(&global_jbconf, AST_JB_ENABLED) ? "Yes" : "No"));
+ ast_cli(a->fd, " Jitterbuffer forced: %s\n", (ast_test_flag(&global_jbconf, AST_JB_FORCED) ? "Yes" : "No"));
+ ast_cli(a->fd, " Jitterbuffer max size: %ld\n", global_jbconf.max_size);
+ ast_cli(a->fd, " Jitterbuffer resync: %ld\n", global_jbconf.resync_threshold);
+ ast_cli(a->fd, " Jitterbuffer impl: %s\n", global_jbconf.impl);
+ ast_cli(a->fd, " Jitterbuffer log: %s\n", (ast_test_flag(&global_jbconf, AST_JB_LOG) ? "Yes" : "No"));
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_skinny[] = {
+ AST_CLI_DEFINE(handle_skinny_show_devices, "List defined Skinny devices"),
+ AST_CLI_DEFINE(handle_skinny_show_device, "List Skinny device information"),
+ AST_CLI_DEFINE(handle_skinny_show_lines, "List defined Skinny lines per device"),
+ AST_CLI_DEFINE(handle_skinny_show_line, "List Skinny line information"),
+ AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"),
+ AST_CLI_DEFINE(handle_skinny_set_debug, "Enable Skinny debugging"),
+ AST_CLI_DEFINE(handle_skinny_set_debug_off, "Disable Skinny debugging"),
+ AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"),
+};
+
+#if 0
+static struct skinny_paging_device *build_paging_device(const char *cat, struct ast_variable *v)
+{
+ return NULL;
+}
+#endif
+
+static struct skinny_device *build_device(const char *cat, struct ast_variable *v)
+{
+ struct skinny_device *d;
+ struct skinny_line *l;
+ struct skinny_speeddial *sd;
+ struct skinny_addon *a;
+ char device_vmexten[AST_MAX_EXTENSION];
+ struct ast_variable *chanvars = NULL;
+ int lineInstance = 1;
+ int speeddialInstance = 1;
+ int y = 0;
+
+ if (!(d = ast_calloc(1, sizeof(*d)))) {
+ return NULL;
+ } else {
+ ast_copy_string(d->name, cat, sizeof(d->name));
+ d->lastlineinstance = 1;
+ d->capability = default_capability;
+ d->prefs = default_prefs;
+ if (!ast_strlen_zero(vmexten))
+ ast_copy_string(device_vmexten, vmexten, sizeof(device_vmexten));
+ else
+ memset(device_vmexten, 0, sizeof(device_vmexten));
+
+ while(v) {
+ if (!strcasecmp(v->name, "host")) {
+ if (ast_get_ip(&d->addr, v->value)) {
+ ast_free(d);
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "port")) {
+ d->addr.sin_port = htons(atoi(v->value));
+ } else if (!strcasecmp(v->name, "device")) {
+ ast_copy_string(d->id, v->value, sizeof(d->id));
+ } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
+ d->ha = ast_append_ha(v->name, v->value, d->ha, NULL);
+ } else if (!strcasecmp(v->name, "vmexten")) {
+ ast_copy_string(device_vmexten, v->value, sizeof(device_vmexten));
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(context, v->value, sizeof(context));
+ } else if (!strcasecmp(v->name, "regexten")) {
+ ast_copy_string(regexten, v->value, sizeof(regexten));
+ } else if (!strcasecmp(v->name, "allow")) {
+ ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 1);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 0);
+ } else if (!strcasecmp(v->name, "version")) {
+ ast_copy_string(d->version_id, v->value, sizeof(d->version_id));
+ } else if (!strcasecmp(v->name, "canreinvite")) {
+ canreinvite = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "nat")) {
+ nat = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callerid")) {
+ if (!strcasecmp(v->value, "asreceived")) {
+ cid_num[0] = '\0';
+ cid_name[0] = '\0';
+ } else {
+ ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
+ }
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(language, v->value, sizeof(language));
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(accountcode, v->value, sizeof(accountcode));
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ y = ast_cdr_amaflags2int(v->value);
+ if (y < 0) {
+ ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
+ } else {
+ amaflags = y;
+ }
+ } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
+ ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_copy_string(mohsuggest, v->value, sizeof(mohsuggest));
+ } else if (!strcasecmp(v->name, "callgroup")) {
+ cur_callergroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "pickupgroup")) {
+ cur_pickupgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "immediate")) {
+ immediate = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "cancallforward")) {
+ cancallforward = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "mailbox")) {
+ ast_copy_string(mailbox, v->value, sizeof(mailbox));
+ } else if (!strcasecmp(v->name, "callreturn")) {
+ callreturn = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callwaiting")) {
+ callwaiting = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "transfer")) {
+ transfer = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "threewaycalling")) {
+ threewaycalling = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "mwiblink")) {
+ mwiblink = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "linelabel")) {
+ ast_copy_string(linelabel, v->value, sizeof(linelabel));
+ } else if (!strcasecmp(v->name, "setvar")) {
+ chanvars = add_var(v->value, chanvars);
+ } else if (!strcasecmp(v->name, "speeddial")) {
+ if (!(sd = ast_calloc(1, sizeof(*sd)))) {
+ return NULL;
+ } else {
+ char buf[256];
+ char *stringp = buf, *exten, *context, *label;
+
+ ast_copy_string(buf, v->value, sizeof(buf));
+ exten = strsep(&stringp, ",");
+ if ((context = strchr(exten, '@'))) {
+ *context++ = '\0';
+ }
+ label = stringp;
+ ast_mutex_init(&sd->lock);
+ ast_copy_string(sd->exten, exten, sizeof(sd->exten));
+ if (!ast_strlen_zero(context)) {
+ sd->isHint = 1;
+ sd->instance = lineInstance++;
+ ast_copy_string(sd->context, context, sizeof(sd->context));
+ } else {
+ sd->isHint = 0;
+ sd->instance = speeddialInstance++;
+ sd->context[0] = '\0';
+ }
+ ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
+
+ sd->parent = d;
+
+ sd->next = d->speeddials;
+ d->speeddials = sd;
+ }
+ } else if (!strcasecmp(v->name, "addon")) {
+ if (!(a = ast_calloc(1, sizeof(*a)))) {
+ return NULL;
+ } else {
+ ast_mutex_init(&a->lock);
+ ast_copy_string(a->type, v->value, sizeof(a->type));
+
+ a->next = d->addons;
+ d->addons = a;
+ }
+ } else if (!strcasecmp(v->name, "trunk") || !strcasecmp(v->name, "line")) {
+ if (!(l = ast_calloc(1, sizeof(*l)))) {
+ return NULL;
+ } else {
+ ast_mutex_init(&l->lock);
+ ast_copy_string(l->name, v->value, sizeof(l->name));
+
+ /* XXX Should we check for uniqueness?? XXX */
+ ast_copy_string(l->context, context, sizeof(l->context));
+ ast_copy_string(l->cid_num, cid_num, sizeof(l->cid_num));
+ ast_copy_string(l->cid_name, cid_name, sizeof(l->cid_name));
+ ast_copy_string(l->label, linelabel, sizeof(l->label));
+ ast_copy_string(l->language, language, sizeof(l->language));
+ ast_copy_string(l->mohinterpret, mohinterpret, sizeof(l->mohinterpret));
+ ast_copy_string(l->mohsuggest, mohsuggest, sizeof(l->mohsuggest));
+ ast_copy_string(l->regexten, regexten, sizeof(l->regexten));
+ ast_copy_string(l->mailbox, mailbox, sizeof(l->mailbox));
+ if (!ast_strlen_zero(mailbox))
+ ast_verb(3, "Setting mailbox '%s' on %s@%s\n", mailbox, d->name, l->name);
+ ast_copy_string(l->vmexten, device_vmexten, sizeof(vmexten));
+ l->chanvars = chanvars;
+ l->msgstate = -1;
+ l->capability = d->capability;
+ l->prefs = d->prefs;
+ l->parent = d;
+ if (!strcasecmp(v->name, "trunk")) {
+ l->type = TYPE_TRUNK;
+ } else {
+ l->type = TYPE_LINE;
+ }
+ l->immediate = immediate;
+ l->callgroup = cur_callergroup;
+ l->pickupgroup = cur_pickupgroup;
+ l->callreturn = callreturn;
+ l->cancallforward = cancallforward;
+ l->getforward = 0;
+ set_callforwards(l, NULL, 0);
+ l->callwaiting = callwaiting;
+ l->transfer = transfer;
+ l->threewaycalling = threewaycalling;
+ l->mwiblink = mwiblink;
+ l->onhooktime = time(NULL);
+ l->instance = lineInstance++;
+ /* ASSUME we're onhook at this point */
+ l->hookstate = SKINNY_ONHOOK;
+ l->nat = nat;
+ l->canreinvite = canreinvite;
+
+ l->next = d->lines;
+ d->lines = l;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
+ }
+ v = v->next;
+ }
+
+ if (!d->lines) {
+ ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
+ return NULL;
+ }
+ if (/*d->addr.sin_addr.s_addr && */!ntohs(d->addr.sin_port)) {
+ d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
+ }
+#if 0
+ /* I don't think we need this anymore at all, since d->ourip is set in skinny_register now */
+ if (d->addr.sin_addr.s_addr) {
+ /* XXX See note above, in 'host' option. */
+ if (ast_ouraddrfor(&d->addr.sin_addr, &d->ourip)) {
+ d->ourip = __ourip;
+ }
+ } else {
+ d->ourip = __ourip;
+ }
+#endif
+ }
+ return d;
+}
+
+static void start_rtp(struct skinny_subchannel *sub)
+{
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ int hasvideo = 0;
+
+ ast_mutex_lock(&sub->lock);
+ /* Allocate the RTP */
+ sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ if (hasvideo)
+ sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+
+ if (sub->rtp && sub->owner) {
+ ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp));
+ ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp));
+ }
+ if (hasvideo && sub->vrtp && sub->owner) {
+ ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp));
+ ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp));
+ }
+ if (sub->rtp) {
+ ast_rtp_setqos(sub->rtp, tos_audio, cos_audio, "Skinny RTP");
+ ast_rtp_setnat(sub->rtp, l->nat);
+ }
+ if (sub->vrtp) {
+ ast_rtp_setqos(sub->vrtp, tos_video, cos_video, "Skinny VRTP");
+ ast_rtp_setnat(sub->vrtp, l->nat);
+ }
+ /* Set Frame packetization */
+ if (sub->rtp)
+ ast_rtp_codec_setpref(sub->rtp, &l->prefs);
+
+ /* Create the RTP connection */
+ transmit_connect(d->session, sub);
+ ast_mutex_unlock(&sub->lock);
+}
+
+static void *skinny_newcall(void *data)
+{
+ struct ast_channel *c = data;
+ struct skinny_subchannel *sub = c->tech_pvt;
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+ int res = 0;
+
+ ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed));
+ ast_set_callerid(c,
+ l->hidecallerid ? "" : l->cid_num,
+ l->hidecallerid ? "" : l->cid_name,
+ c->cid.cid_ani ? NULL : l->cid_num);
+ ast_setstate(c, AST_STATE_RING);
+ res = ast_pbx_run(c);
+ if (res) {
+ ast_log(LOG_WARNING, "PBX exited non-zero\n");
+ transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+ }
+ return NULL;
+}
+
+static void *skinny_ss(void *data)
+{
+ struct ast_channel *c = data;
+ struct skinny_subchannel *sub = c->tech_pvt;
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+ int len = 0;
+ int timeout = firstdigittimeout;
+ int res = 0;
+ int loop_pause = 100;
+
+ ast_verb(3, "Starting simple switch on '%s@%s'\n", l->name, d->name);
+
+ len = strlen(d->exten);
+
+ while (len < AST_MAX_EXTENSION-1) {
+ res = 1; /* Assume that we will get a digit */
+ while (strlen(d->exten) == len){
+ ast_safe_sleep(c, loop_pause);
+ timeout -= loop_pause;
+ if ( (timeout -= loop_pause) <= 0){
+ res = 0;
+ break;
+ }
+ res = 1;
+ }
+
+ timeout = 0;
+ len = strlen(d->exten);
+
+ if (!ast_ignore_pattern(c->context, d->exten)) {
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ }
+ if (ast_exists_extension(c, c->context, d->exten, 1, l->cid_num)) {
+ if (!res || !ast_matchmore_extension(c, c->context, d->exten, 1, l->cid_num)) {
+ if (l->getforward) {
+ /* Record this as the forwarding extension */
+ set_callforwards(l, d->exten, l->getforward);
+ ast_verb(3, "Setting call forward (%d) to '%s' on channel %s\n",
+ l->cfwdtype, d->exten, c->name);
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
+ transmit_displaynotify(s, "CFwd enabled", 10);
+ transmit_cfwdstate(s, l);
+ ast_safe_sleep(c, 500);
+ ast_indicate(c, -1);
+ ast_safe_sleep(c, 1000);
+ memset(d->exten, 0, sizeof(d->exten));
+ len = 0;
+ l->getforward = 0;
+ if (sub->owner && sub->owner->_state != AST_STATE_UP) {
+ ast_indicate(c, -1);
+ ast_hangup(c);
+ }
+ return NULL;
+ } else {
+ ast_copy_string(c->exten, d->exten, sizeof(c->exten));
+ ast_copy_string(l->lastnumberdialed, d->exten, sizeof(l->lastnumberdialed));
+ memset(d->exten, 0, sizeof(d->exten));
+ skinny_newcall(c);
+ return NULL;
+ }
+ } else {
+ /* It's a match, but they just typed a digit, and there is an ambiguous match,
+ so just set the timeout to matchdigittimeout and wait some more */
+ timeout = matchdigittimeout;
+ }
+ } else if (res == 0) {
+ ast_debug(1, "Not enough digits (%s) (and no ambiguous match)...\n", d->exten);
+ memset(d->exten, 0, sizeof(d->exten));
+ transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+ if (sub->owner && sub->owner->_state != AST_STATE_UP) {
+ ast_indicate(c, -1);
+ ast_hangup(c);
+ }
+ return NULL;
+ } else if (!ast_canmatch_extension(c, c->context, d->exten, 1, c->cid.cid_num) &&
+ ((d->exten[0] != '*') || (!ast_strlen_zero(d->exten) > 2))) {
+ ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", d->exten, c->cid.cid_num ? c->cid.cid_num : "<Unknown Caller>", c->context);
+ memset(d->exten, 0, sizeof(d->exten));
+ transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+ /* hang out for 3 seconds to let congestion play */
+ ast_safe_sleep(c, 3000);
+ break;
+ }
+ if (!timeout) {
+ timeout = gendigittimeout;
+ }
+ if (len && !ast_ignore_pattern(c->context, d->exten)) {
+ ast_indicate(c, -1);
+ }
+ }
+ if (c)
+ ast_hangup(c);
+ memset(d->exten, 0, sizeof(d->exten));
+ return NULL;
+}
+
+
+
+static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ int res = 0;
+ int tone = 0;
+ struct skinny_subchannel *sub = ast->tech_pvt;
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+
+ if (!d->registered) {
+ ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
+ return -1;
+ }
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+
+ if (skinnydebug)
+ ast_verb(3, "skinny_call(%s)\n", ast->name);
+
+ if (l->dnd) {
+ ast_queue_control(ast, AST_CONTROL_BUSY);
+ return -1;
+ }
+
+ switch (l->hookstate) {
+ case SKINNY_OFFHOOK:
+ tone = SKINNY_CALLWAITTONE;
+ break;
+ case SKINNY_ONHOOK:
+ tone = SKINNY_ALERT;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
+ break;
+ }
+
+ transmit_callstate(s, l->instance, SKINNY_RINGIN, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGIN);
+ transmit_displaypromptstatus(s, "Ring-In", 0, l->instance, sub->callid);
+ transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
+ transmit_ringer_mode(s, SKINNY_RING_INSIDE);
+
+ ast_setstate(ast, AST_STATE_RINGING);
+ ast_queue_control(ast, AST_CONTROL_RINGING);
+ sub->outgoing = 1;
+ return res;
+}
+
+static int skinny_hangup(struct ast_channel *ast)
+{
+ struct skinny_subchannel *sub = ast->tech_pvt;
+ struct skinny_line *l;
+ struct skinny_device *d;
+ struct skinnysession *s;
+
+ if (!sub) {
+ ast_debug(1, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ l = sub->parent;
+ d = l->parent;
+ s = d->session;
+ if (skinnydebug)
+ ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, d->name);
+
+ if (d->registered) {
+ if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) {
+ l->hookstate = SKINNY_ONHOOK;
+ transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
+ transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
+ } else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) {
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
+ transmit_ringer_mode(s, SKINNY_RING_OFF);
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
+ do_housekeeping(s);
+ }
+ }
+ ast_mutex_lock(&sub->lock);
+ sub->owner = NULL;
+ ast->tech_pvt = NULL;
+ sub->alreadygone = 0;
+ sub->outgoing = 0;
+ if (sub->rtp) {
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+ ast_mutex_unlock(&sub->lock);
+ return 0;
+}
+
+static int skinny_answer(struct ast_channel *ast)
+{
+ int res = 0;
+ struct skinny_subchannel *sub = ast->tech_pvt;
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+ char exten[AST_MAX_EXTENSION] = "";
+
+ ast_copy_string(exten, S_OR(ast->macroexten, ast->exten), sizeof(exten));
+
+ sub->cxmode = SKINNY_CX_SENDRECV;
+ if (!sub->rtp) {
+ start_rtp(sub);
+ }
+ if (skinnydebug)
+ ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid);
+ if (ast->_state != AST_STATE_UP) {
+ ast_setstate(ast, AST_STATE_UP);
+ }
+
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ /* order matters here...
+ for some reason, transmit_callinfo must be before transmit_callstate,
+ or you won't get keypad messages in some situations. */
+ transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2);
+ transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
+ transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
+ return res;
+}
+
+/* Retrieve audio/etc from channel. Assumes sub->lock is already held. */
+static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
+{
+ struct ast_channel *ast = sub->owner;
+ struct ast_frame *f;
+
+ if (!sub->rtp) {
+ /* We have no RTP allocated for this channel */
+ return &ast_null_frame;
+ }
+
+ switch(ast->fdno) {
+ case 0:
+ f = ast_rtp_read(sub->rtp); /* RTP Audio */
+ break;
+ case 1:
+ f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */
+ break;
+ case 2:
+ f = ast_rtp_read(sub->vrtp); /* RTP Video */
+ break;
+ case 3:
+ f = ast_rtcp_read(sub->vrtp); /* RTCP Control Channel for video */
+ break;
+#if 0
+ case 5:
+ /* Not yet supported */
+ f = ast_udptl_read(sub->udptl); /* UDPTL for T.38 */
+ break;
+#endif
+ default:
+ f = &ast_null_frame;
+ }
+
+ if (ast) {
+ /* We already hold the channel lock */
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass != ast->nativeformats) {
+ ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
+ ast->nativeformats = f->subclass;
+ ast_set_read_format(ast, ast->readformat);
+ ast_set_write_format(ast, ast->writeformat);
+ }
+ }
+ }
+ return f;
+}
+
+static struct ast_frame *skinny_read(struct ast_channel *ast)
+{
+ struct ast_frame *fr;
+ struct skinny_subchannel *sub = ast->tech_pvt;
+ ast_mutex_lock(&sub->lock);
+ fr = skinny_rtp_read(sub);
+ ast_mutex_unlock(&sub->lock);
+ return fr;
+}
+
+static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct skinny_subchannel *sub = ast->tech_pvt;
+ int res = 0;
+ if (frame->frametype != AST_FRAME_VOICE) {
+ if (frame->frametype == AST_FRAME_IMAGE) {
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
+ return 0;
+ }
+ } else {
+ if (!(frame->subclass & ast->nativeformats)) {
+ ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
+ frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
+ return -1;
+ }
+ }
+ if (sub) {
+ ast_mutex_lock(&sub->lock);
+ if (sub->rtp) {
+ res = ast_rtp_write(sub->rtp, frame);
+ }
+ ast_mutex_unlock(&sub->lock);
+ }
+ return res;
+}
+
+static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct skinny_subchannel *sub = newchan->tech_pvt;
+ ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
+ if (sub->owner != oldchan) {
+ ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
+ return -1;
+ }
+ sub->owner = newchan;
+ return 0;
+}
+
+static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
+{
+ return -1; /* Start inband indications */
+}
+
+static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+#if 0
+ struct skinny_subchannel *sub = ast->tech_pvt;
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ int tmp;
+ /* not right */
+ sprintf(tmp, "%d", digit);
+ transmit_tone(d->session, digit, l->instance, sub->callid);
+#endif
+ return -1; /* Stop inband indications */
+}
+
+static int get_devicestate(struct skinny_line *l)
+{
+ struct skinny_subchannel *sub;
+ int res = AST_DEVICE_UNKNOWN;
+
+ if (!l)
+ res = AST_DEVICE_INVALID;
+ else if (!l->parent)
+ res = AST_DEVICE_UNAVAILABLE;
+ else if (l->dnd)
+ res = AST_DEVICE_BUSY;
+ else {
+ if (l->hookstate == SKINNY_ONHOOK) {
+ res = AST_DEVICE_NOT_INUSE;
+ } else {
+ res = AST_DEVICE_INUSE;
+ }
+
+ for (sub = l->sub; sub; sub = sub->next) {
+ if (sub->onhold) {
+ res = AST_DEVICE_ONHOLD;
+ break;
+ }
+ }
+ }
+
+ return res;
+}
+
+static char *control2str(int ind) {
+ char *tmp;
+
+ switch (ind) {
+ case AST_CONTROL_HANGUP:
+ return "Other end has hungup";
+ case AST_CONTROL_RING:
+ return "Local ring";
+ case AST_CONTROL_RINGING:
+ return "Remote end is ringing";
+ case AST_CONTROL_ANSWER:
+ return "Remote end has answered";
+ case AST_CONTROL_BUSY:
+ return "Remote end is busy";
+ case AST_CONTROL_TAKEOFFHOOK:
+ return "Make it go off hook";
+ case AST_CONTROL_OFFHOOK:
+ return "Line is off hook";
+ case AST_CONTROL_CONGESTION:
+ return "Congestion (circuits busy)";
+ case AST_CONTROL_FLASH:
+ return "Flash hook";
+ case AST_CONTROL_WINK:
+ return "Wink";
+ case AST_CONTROL_OPTION:
+ return "Set a low-level option";
+ case AST_CONTROL_RADIO_KEY:
+ return "Key Radio";
+ case AST_CONTROL_RADIO_UNKEY:
+ return "Un-Key Radio";
+ case AST_CONTROL_PROGRESS:
+ return "Remote end is making Progress";
+ case AST_CONTROL_PROCEEDING:
+ return "Remote end is proceeding";
+ case AST_CONTROL_HOLD:
+ return "Hold";
+ case AST_CONTROL_UNHOLD:
+ return "Unhold";
+ case -1:
+ return "Stop tone";
+ default:
+ if (!(tmp = ast_threadstorage_get(&control2str_threadbuf, CONTROL2STR_BUFSIZE)))
+ return "Unknown";
+ snprintf(tmp, CONTROL2STR_BUFSIZE, "UNKNOWN-%d", ind);
+ return tmp;
+ }
+}
+
+
+static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
+{
+ struct skinny_subchannel *sub = ast->tech_pvt;
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+ char exten[AST_MAX_EXTENSION] = "";
+
+ if (!s) {
+ ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast->name);
+ return -1;
+ }
+
+ ast_copy_string(exten, S_OR(ast->macroexten, ast->exten), sizeof(exten));
+
+ if (skinnydebug)
+ ast_verb(3, "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
+ switch(ind) {
+ case AST_CONTROL_RINGING:
+ if (ast->_state != AST_STATE_UP) {
+ if (!sub->progress) {
+ transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
+ transmit_dialednumber(s, exten, l->instance, sub->callid);
+ transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid);
+ transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
+ sub->ringing = 1;
+ break;
+ }
+ }
+ return -1;
+ case AST_CONTROL_BUSY:
+ if (ast->_state != AST_STATE_UP) {
+ transmit_tone(s, SKINNY_BUSYTONE, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
+ sub->alreadygone = 1;
+ ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+ break;
+ }
+ return -1;
+ case AST_CONTROL_CONGESTION:
+ if (ast->_state != AST_STATE_UP) {
+ transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
+ sub->alreadygone = 1;
+ ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+ break;
+ }
+ return -1;
+ case AST_CONTROL_PROGRESS:
+ if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
+ transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
+ transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid);
+ transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
+ sub->progress = 1;
+ break;
+ }
+ return -1;
+ case -1:
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ break;
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, data, l->mohinterpret);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
+ return -1;
+ }
+ return 0;
+}
+
+static struct ast_channel *skinny_new(struct skinny_line *l, int state)
+{
+ struct ast_channel *tmp;
+ struct skinny_subchannel *sub;
+ struct skinny_device *d = l->parent;
+ struct ast_variable *v = NULL;
+ int fmt;
+
+ tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ return NULL;
+ } else {
+ sub = ast_calloc(1, sizeof(*sub));
+ if (!sub) {
+ ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
+ return NULL;
+ } else {
+ ast_mutex_init(&sub->lock);
+
+ sub->owner = tmp;
+ sub->callid = callnums++;
+ d->lastlineinstance = l->instance;
+ d->lastcallreference = sub->callid;
+ sub->cxmode = SKINNY_CX_INACTIVE;
+ sub->nat = l->nat;
+ sub->parent = l;
+ sub->onhold = 0;
+
+ sub->next = l->sub;
+ l->sub = sub;
+ }
+ tmp->tech = &skinny_tech;
+ tmp->tech_pvt = sub;
+ tmp->nativeformats = l->capability;
+ if (!tmp->nativeformats)
+ tmp->nativeformats = default_capability;
+ fmt = ast_best_codec(tmp->nativeformats);
+ if (skinnydebug)
+ ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
+ if (sub->rtp) {
+ ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp));
+ }
+ if (state == AST_STATE_RING) {
+ tmp->rings = 1;
+ }
+ tmp->writeformat = fmt;
+ tmp->rawwriteformat = fmt;
+ tmp->readformat = fmt;
+ tmp->rawreadformat = fmt;
+ if (!ast_strlen_zero(l->language))
+ ast_string_field_set(tmp, language, l->language);
+ if (!ast_strlen_zero(l->accountcode))
+ ast_string_field_set(tmp, accountcode, l->accountcode);
+ if (l->amaflags)
+ tmp->amaflags = l->amaflags;
+
+ ast_module_ref(ast_module_info->self);
+ tmp->callgroup = l->callgroup;
+ tmp->pickupgroup = l->pickupgroup;
+
+ /* XXX Need to figure out how to handle CFwdNoAnswer */
+ if (l->cfwdtype & SKINNY_CFWD_ALL) {
+ ast_string_field_set(tmp, call_forward, l->call_forward_all);
+ } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
+ if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
+ ast_string_field_set(tmp, call_forward, l->call_forward_busy);
+ }
+ }
+
+ ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
+
+ /* Don't use ast_set_callerid() here because it will
+ * generate a needless NewCallerID event */
+ tmp->cid.cid_ani = ast_strdup(l->cid_num);
+
+ tmp->priority = 1;
+ tmp->adsicpe = AST_ADSI_UNAVAILABLE;
+
+ if (sub->rtp)
+ ast_jb_configure(tmp, &global_jbconf);
+
+ /* Set channel variables for this call from configuration */
+ for (v = l->chanvars ; v ; v = v->next)
+ pbx_builtin_setvar_helper(tmp, v->name, v->value);
+
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ tmp = NULL;
+ }
+ }
+ }
+ return tmp;
+}
+
+static int skinny_hold(struct skinny_subchannel *sub)
+{
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+ struct skinny_req *req;
+
+ /* Don't try to hold a channel that doesn't exist */
+ if (!sub || !sub->owner)
+ return 0;
+
+ /* Channel needs to be put on hold */
+ if (skinnydebug)
+ ast_verbose("Putting on Hold(%d)\n", l->instance);
+
+ ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
+ S_OR(l->mohsuggest, NULL),
+ !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
+
+ if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
+ return 0;
+
+ req->data.activatecallplane.lineInstance = htolel(l->instance);
+ transmit_response(s, req);
+
+ if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
+ return 0;
+
+ req->data.closereceivechannel.conferenceId = htolel(sub->callid);
+ req->data.closereceivechannel.partyId = htolel(sub->callid);
+ transmit_response(s, req);
+
+ if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
+ return 0;
+
+ req->data.stopmedia.conferenceId = htolel(sub->callid);
+ req->data.stopmedia.passThruPartyId = htolel(sub->callid);
+ transmit_response(s, req);
+
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
+ sub->onhold = 1;
+ return 1;
+}
+
+static int skinny_unhold(struct skinny_subchannel *sub)
+{
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+ struct skinny_req *req;
+
+ /* Don't try to unhold a channel that doesn't exist */
+ if (!sub || !sub->owner)
+ return 0;
+
+ /* Channel is on hold, so we will unhold */
+ if (skinnydebug)
+ ast_verbose("Taking off Hold(%d)\n", l->instance);
+
+ ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
+
+ if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
+ return 0;
+
+ req->data.activatecallplane.lineInstance = htolel(l->instance);
+ transmit_response(s, req);
+
+ transmit_connect(s, sub);
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+ sub->onhold = 0;
+ return 1;
+}
+
+static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
+{
+ if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
+ return -1;
+
+ transmit_response(s, req);
+ do_housekeeping(s);
+ return 1;
+}
+
+static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
+{
+ char name[16];
+ int res;
+
+ memcpy(&name, req->data.reg.name, sizeof(name));
+
+ res = skinny_register(req, s);
+ if (!res) {
+ ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
+ if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
+ return -1;
+
+ snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
+ transmit_response(s, req);
+ return 0;
+ }
+ ast_verb(3, "Device '%s' successfully registered\n", name);
+
+ if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
+ return -1;
+
+ req->data.regack.res[0] = '0';
+ req->data.regack.res[1] = '\0';
+ req->data.regack.keepAlive = htolel(keep_alive);
+ memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
+ req->data.regack.res2[0] = '0';
+ req->data.regack.res2[1] = '\0';
+ req->data.regack.secondaryKeepAlive = htolel(keep_alive);
+ transmit_response(s, req);
+ if (skinnydebug)
+ ast_verbose("Requesting capabilities\n");
+
+ if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
+ return -1;
+
+ transmit_response(s, req);
+
+ return res;
+}
+
+static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
+{
+ struct skinny_line *l = sub->parent;
+ struct skinny_device *d = l->parent;
+ struct skinnysession *s = d->session;
+ struct ast_channel *c = sub->owner;
+ pthread_t t;
+
+ if (l->hookstate == SKINNY_ONHOOK) {
+ l->hookstate = SKINNY_OFFHOOK;
+ transmit_speaker_mode(s, SKINNY_SPEAKERON);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ }
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+
+ if (l->cfwdtype & cfwdtype) {
+ set_callforwards(l, NULL, cfwdtype);
+ ast_safe_sleep(c, 500);
+ transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
+ transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
+ transmit_displaynotify(s, "CFwd disabled", 10);
+ if (sub->owner && sub->owner->_state != AST_STATE_UP) {
+ ast_indicate(c, -1);
+ ast_hangup(c);
+ }
+ transmit_cfwdstate(s, l);
+ } else {
+ l->getforward = cfwdtype;
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+ if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
+ ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ }
+ return 0;
+}
+static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
+{
+ /* no response necessary */
+ return 1;
+}
+
+static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_subchannel *sub = NULL;
+ struct skinny_line *l;
+ struct skinny_device *d = s->device;
+ struct ast_frame f = { 0, };
+ char dgt;
+ int digit;
+ int lineInstance;
+ int callReference;
+
+ digit = letohl(req->data.keypad.button);
+ lineInstance = letohl(req->data.keypad.lineInstance);
+ callReference = letohl(req->data.keypad.callReference);
+
+ if (digit == 14) {
+ dgt = '*';
+ } else if (digit == 15) {
+ dgt = '#';
+ } else if (digit >= 0 && digit <= 9) {
+ dgt = '0' + digit;
+ } else {
+ /* digit=10-13 (A,B,C,D ?), or
+ * digit is bad value
+ *
+ * probably should not end up here, but set
+ * value for backward compatibility, and log
+ * a warning.
+ */
+ dgt = '0' + digit;
+ ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
+ }
+
+ f.subclass = dgt;
+
+ f.src = "skinny";
+
+ if (lineInstance && callReference)
+ sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
+ else
+ sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+
+ if (!sub)
+ return 0;
+
+ l = sub->parent;
+ if (sub->owner) {
+ if (sub->owner->_state == 0) {
+ f.frametype = AST_FRAME_DTMF_BEGIN;
+ ast_queue_frame(sub->owner, &f);
+ }
+ /* XXX MUST queue this frame to all lines in threeway call if threeway call is active */
+ f.frametype = AST_FRAME_DTMF_END;
+ ast_queue_frame(sub->owner, &f);
+ /* XXX This seriously needs to be fixed */
+ if (sub->next && sub->next->owner) {
+ if (sub->owner->_state == 0) {
+ f.frametype = AST_FRAME_DTMF_BEGIN;
+ ast_queue_frame(sub->next->owner, &f);
+ }
+ f.frametype = AST_FRAME_DTMF_END;
+ ast_queue_frame(sub->next->owner, &f);
+ }
+ } else {
+ if (skinnydebug)
+ ast_verbose("No owner: %s\n", l->name);
+ }
+ return 1;
+}
+
+static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ struct skinny_subchannel *sub;
+ /*struct skinny_speeddial *sd;*/
+ struct ast_channel *c;
+ pthread_t t;
+ int event;
+ int instance;
+ int callreference;
+ /*int res = 0;*/
+
+ event = letohl(req->data.stimulus.stimulus);
+ instance = letohl(req->data.stimulus.stimulusInstance);
+ callreference = letohl(req->data.stimulus.callreference);
+ if (skinnydebug)
+ ast_verbose("callreference in handle_stimulus_message is '%d'\n", callreference);
+
+ /* Note that this call should be using the passed in instance and callreference */
+ sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+
+ if (!sub) {
+ l = find_line_by_instance(d, d->lastlineinstance);
+ if (!l) {
+ return 0;
+ }
+ } else {
+ l = sub->parent;
+ }
+
+ switch(event) {
+ case STIMULUS_REDIAL:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Redial(%d/%d)\n", instance, callreference);
+
+ if (ast_strlen_zero(l->lastnumberdialed)) {
+ ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
+ l->hookstate = SKINNY_ONHOOK;
+ transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
+ transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance);
+ break;
+ }
+
+ c = skinny_new(l, AST_STATE_DOWN);
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ l = sub->parent;
+ if (l->hookstate == SKINNY_ONHOOK) {
+ l->hookstate = SKINNY_OFFHOOK;
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ }
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+
+ if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ }
+ ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
+ if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
+ ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ }
+ break;
+ case STIMULUS_SPEEDDIAL:
+ {
+ struct skinny_speeddial *sd;
+
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
+ if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
+ return 0;
+ }
+
+ if (!sub || !sub->owner)
+ c = skinny_new(l, AST_STATE_DOWN);
+ else
+ c = sub->owner;
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ l = sub->parent;
+ if (l->hookstate == SKINNY_ONHOOK) {
+ l->hookstate = SKINNY_OFFHOOK;
+ transmit_speaker_mode(s, SKINNY_SPEAKERON);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ }
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+
+ if (!ast_ignore_pattern(c->context, sd->exten)) {
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ }
+ if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
+ ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
+ ast_copy_string(l->lastnumberdialed, sd->exten, sizeof(l->lastnumberdialed));
+
+ if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
+ ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ break;
+ }
+ }
+ }
+ break;
+ case STIMULUS_HOLD:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Hold(%d/%d)\n", instance, callreference);
+
+ if (!sub)
+ break;
+
+ if (sub->onhold) {
+ skinny_unhold(sub);
+ } else {
+ skinny_hold(sub);
+ }
+ break;
+ case STIMULUS_TRANSFER:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
+ /* XXX figure out how to transfer */
+ break;
+ case STIMULUS_CONFERENCE:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Conference(%d/%d)\n", instance, callreference);
+ /* XXX determine the best way to pull off a conference. Meetme? */
+ break;
+ case STIMULUS_VOICEMAIL:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
+
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ l = sub->parent;
+
+ if (ast_strlen_zero(l->vmexten)) /* Exit the call if no VM pilot */
+ break;
+
+ if (l->hookstate == SKINNY_ONHOOK){
+ l->hookstate = SKINNY_OFFHOOK;
+ transmit_speaker_mode(s, SKINNY_SPEAKERON);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ }
+
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+
+ if (!ast_ignore_pattern(c->context, vmexten)) {
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ }
+
+ if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
+ ast_copy_string(c->exten, l->vmexten, sizeof(c->exten));
+ ast_copy_string(l->lastnumberdialed, l->vmexten, sizeof(l->lastnumberdialed));
+ if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
+ ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ break;
+ }
+ }
+ break;
+ case STIMULUS_CALLPARK:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
+ /* XXX Park the call */
+ break;
+ case STIMULUS_DND:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: DND (%d/%d)\n", instance, callreference);
+
+ /* Do not disturb */
+ if (l->dnd != 0){
+ ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
+ l->dnd = 0;
+ transmit_lamp_indication(s, STIMULUS_DND, 1, SKINNY_LAMP_ON);
+ transmit_displaynotify(s, "DnD disabled", 10);
+ } else {
+ ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
+ l->dnd = 1;
+ transmit_lamp_indication(s, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
+ transmit_displaynotify(s, "DnD enabled", 10);
+ }
+ break;
+ case STIMULUS_FORWARDALL:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
+
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ handle_callforward_button(sub, SKINNY_CFWD_ALL);
+ }
+ break;
+ case STIMULUS_FORWARDBUSY:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
+
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ handle_callforward_button(sub, SKINNY_CFWD_BUSY);
+ }
+ break;
+ case STIMULUS_FORWARDNOANSWER:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
+
+#if 0 /* Not sure how to handle this yet */
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
+ }
+#endif
+ break;
+ case STIMULUS_DISPLAY:
+ /* Not sure what this is */
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Display(%d/%d)\n", instance, callreference);
+ break;
+ case STIMULUS_LINE:
+ if (skinnydebug)
+ ast_verbose("Received Stimulus: Line(%d/%d)\n", instance, callreference);
+
+ l = find_line_by_instance(d, instance);
+
+ if (!l) {
+ return 0;
+ }
+
+ /* turn the speaker on */
+ transmit_speaker_mode(s, SKINNY_SPEAKERON);
+ transmit_ringer_mode(s, SKINNY_RING_OFF);
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+
+ l->hookstate = SKINNY_OFFHOOK;
+
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+
+ if (sub && sub->outgoing) {
+ /* We're answering a ringing call */
+ ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+ transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
+ start_rtp(sub);
+ ast_setstate(sub->owner, AST_STATE_UP);
+ } else {
+ if (sub && sub->owner) {
+ ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
+ } else {
+ c = skinny_new(l, AST_STATE_DOWN);
+ if (c) {
+ sub = c->tech_pvt;
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
+
+ /* start the switch thread */
+ if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
+ ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ }
+ }
+ }
+ break;
+ default:
+ if (skinnydebug)
+ ast_verbose("RECEIVED UNKNOWN STIMULUS: %d(%d/%d)\n", event, instance, callreference);
+ break;
+ }
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+
+ return 1;
+}
+
+static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ struct skinny_subchannel *sub;
+ struct ast_channel *c;
+ pthread_t t;
+ int unknown1;
+ int unknown2;
+
+ unknown1 = letohl(req->data.offhook.unknown1);
+ unknown2 = letohl(req->data.offhook.unknown2);
+
+ sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+
+ if (!sub) {
+ l = find_line_by_instance(d, d->lastlineinstance);
+ if (!l) {
+ return 0;
+ }
+ } else {
+ l = sub->parent;
+ }
+
+ transmit_ringer_mode(s, SKINNY_RING_OFF);
+ l->hookstate = SKINNY_OFFHOOK;
+
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+
+ if (sub && sub->onhold) {
+ return 1;
+ }
+
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+
+ if (sub && sub->outgoing) {
+ /* We're answering a ringing call */
+ ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
+ start_rtp(sub);
+ ast_setstate(sub->owner, AST_STATE_UP);
+ } else {
+ if (sub && sub->owner) {
+ ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
+ } else {
+ c = skinny_new(l, AST_STATE_DOWN);
+ if (c) {
+ sub = c->tech_pvt;
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
+
+ /* start the switch thread */
+ if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
+ ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ }
+ }
+ }
+ return 1;
+}
+
+static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ struct skinny_subchannel *sub;
+ int unknown1;
+ int unknown2;
+
+ unknown1 = letohl(req->data.onhook.unknown1);
+ unknown2 = letohl(req->data.onhook.unknown2);
+
+ sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+
+ if (!sub) {
+ return 0;
+ }
+ l = sub->parent;
+
+ if (l->hookstate == SKINNY_ONHOOK) {
+ /* Something else already put us back on hook */
+ return 0;
+ }
+ l->hookstate = SKINNY_ONHOOK;
+
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+
+ if (sub->onhold) {
+ return 0;
+ }
+
+ sub->cxmode = SKINNY_CX_RECVONLY;
+ transmit_callstate(s, l->instance, l->hookstate, sub->callid);
+ if (skinnydebug)
+ ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name);
+ if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) {
+ /* We're allowed to transfer, we have two active calls and
+ we made at least one of the calls. Let's try and transfer */
+
+#if 0
+ if ((res = attempt_transfer(p)) < 0) {
+ if (sub->next && sub->next->owner) {
+ sub->next->alreadygone = 1;
+ ast_queue_hangup(sub->next->owner,1);
+ }
+ } else if (res) {
+ ast_log(LOG_WARNING, "Transfer attempt failed\n");
+ return 0;
+ }
+#endif
+ } else {
+ /* Hangup the current call */
+ /* If there is another active call, skinny_hangup will ring the phone with the other call */
+ if (sub->owner) {
+ sub->alreadygone = 1;
+ ast_queue_hangup(sub->owner);
+ } else {
+ ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
+ l->name, d->name, sub->callid);
+ }
+ }
+ if ((l->hookstate == SKINNY_ONHOOK) && (sub->next && !sub->next->rtp)) {
+ do_housekeeping(s);
+ }
+ return 1;
+}
+
+static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ uint32_t count = 0;
+ int codecs = 0;
+ int i;
+
+ count = letohl(req->data.caps.count);
+ if (count > SKINNY_MAX_CAPABILITIES) {
+ count = SKINNY_MAX_CAPABILITIES;
+ ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d). Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES);
+ }
+
+ for (i = 0; i < count; i++) {
+ int acodec = 0;
+ int scodec = 0;
+ scodec = letohl(req->data.caps.caps[i].codec);
+ acodec = codec_skinny2ast(scodec);
+ if (skinnydebug)
+ ast_verbose("Adding codec capability '%d (%d)'\n", acodec, scodec);
+ codecs |= acodec;
+ }
+
+ d->capability &= codecs;
+ ast_verbose("Device capability set to '%d'\n", d->capability);
+ for (l = d->lines; l; l = l->next) {
+ ast_mutex_lock(&l->lock);
+ l->capability = d->capability;
+ ast_mutex_unlock(&l->lock);
+ }
+
+ return 1;
+}
+
+static int handle_speed_dial_stat_req_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_speeddial *sd;
+ int instance;
+
+ instance = letohl(req->data.speeddialreq.speedDialNumber);
+
+ sd = find_speeddial_by_instance(d, instance, 0);
+
+ if (!sd) {
+ return 0;
+ }
+
+ if (!(req = req_alloc(sizeof(struct speed_dial_stat_res_message), SPEED_DIAL_STAT_RES_MESSAGE)))
+ return -1;
+
+ req->data.speeddialreq.speedDialNumber = htolel(instance);
+ ast_copy_string(req->data.speeddial.speedDialDirNumber, sd->exten, sizeof(req->data.speeddial.speedDialDirNumber));
+ ast_copy_string(req->data.speeddial.speedDialDisplayName, sd->label, sizeof(req->data.speeddial.speedDialDisplayName));
+
+ transmit_response(s, req);
+ return 1;
+}
+
+static int handle_line_state_req_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ struct skinny_speeddial *sd = NULL;
+ int instance;
+
+ instance = letohl(req->data.line.lineNumber);
+
+ ast_mutex_lock(&devicelock);
+
+ l = find_line_by_instance(d, instance);
+
+ if (!l) {
+ sd = find_speeddial_by_instance(d, instance, 1);
+ }
+
+ if (!l && !sd) {
+ return 0;
+ }
+
+ ast_mutex_unlock(&devicelock);
+
+ if (!(req = req_alloc(sizeof(struct line_stat_res_message), LINE_STAT_RES_MESSAGE)))
+ return -1;
+
+ req->data.linestat.lineNumber = letohl(instance);
+ if (!l) {
+ memcpy(req->data.linestat.lineDirNumber, sd->label, sizeof(req->data.linestat.lineDirNumber));
+ memcpy(req->data.linestat.lineDisplayName, sd->label, sizeof(req->data.linestat.lineDisplayName));
+ } else {
+ memcpy(req->data.linestat.lineDirNumber, l->name, sizeof(req->data.linestat.lineDirNumber));
+ memcpy(req->data.linestat.lineDisplayName, l->label, sizeof(req->data.linestat.lineDisplayName));
+ }
+ transmit_response(s,req);
+ return 1;
+}
+
+static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct timeval tv = ast_tvnow();
+ struct ast_tm cmtime;
+
+ if (!(req = req_alloc(sizeof(struct definetimedate_message), DEFINETIMEDATE_MESSAGE)))
+ return -1;
+
+ ast_localtime(&tv, &cmtime, NULL);
+ req->data.definetimedate.year = htolel(cmtime.tm_year+1900);
+ req->data.definetimedate.month = htolel(cmtime.tm_mon+1);
+ req->data.definetimedate.dayofweek = htolel(cmtime.tm_wday);
+ req->data.definetimedate.day = htolel(cmtime.tm_mday);
+ req->data.definetimedate.hour = htolel(cmtime.tm_hour);
+ req->data.definetimedate.minute = htolel(cmtime.tm_min);
+ req->data.definetimedate.seconds = htolel(cmtime.tm_sec);
+ req->data.definetimedate.milliseconds = htolel(cmtime.tm_usec / 1000);
+ req->data.definetimedate.timestamp = htolel(tv.tv_sec);
+ transmit_response(s, req);
+ return 1;
+}
+
+static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ int i;
+
+ struct skinny_speeddial *sd;
+ struct button_definition_template btn[42];
+ int lineInstance = 1;
+ int speeddialInstance = 1;
+ int buttonCount = 0;
+
+ if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE)))
+ return -1;
+
+ memset(&btn, 0, sizeof(btn));
+
+ get_button_template(s, btn);
+
+ for (i=0; i<42; i++) {
+ int btnSet = 0;
+ switch (btn[i].buttonDefinition) {
+ case BT_CUST_LINE:
+ /* assume failure */
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
+
+ for (l = d->lines; l; l = l->next) {
+ if (l->instance == lineInstance) {
+ ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
+ lineInstance++;
+ buttonCount++;
+ btnSet = 1;
+ break;
+ }
+ }
+
+ if (!btnSet) {
+ for (sd = d->speeddials; sd; sd = sd->next) {
+ if (sd->isHint && sd->instance == lineInstance) {
+ ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
+ lineInstance++;
+ buttonCount++;
+ btnSet = 1;
+ break;
+ }
+ }
+ }
+ break;
+ case BT_CUST_LINESPEEDDIAL:
+ /* assume failure */
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
+
+ for (l = d->lines; l; l = l->next) {
+ if (l->instance == lineInstance) {
+ ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
+ lineInstance++;
+ buttonCount++;
+ btnSet = 1;
+ break;
+ }
+ }
+
+ if (!btnSet) {
+ for (sd = d->speeddials; sd; sd = sd->next) {
+ if (sd->isHint && sd->instance == lineInstance) {
+ ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
+ lineInstance++;
+ buttonCount++;
+ btnSet = 1;
+ break;
+ } else if (!sd->isHint && sd->instance == speeddialInstance) {
+ ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance);
+ speeddialInstance++;
+ buttonCount++;
+ btnSet = 1;
+ break;
+ }
+ }
+ }
+ break;
+ case BT_LINE:
+ req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
+
+ for (l = d->lines; l; l = l->next) {
+ if (l->instance == lineInstance) {
+ ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
+ lineInstance++;
+ buttonCount++;
+ btnSet = 1;
+ break;
+ }
+ }
+ break;
+ case BT_SPEEDDIAL:
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
+ req->data.buttontemplate.definition[i].instanceNumber = 0;
+
+ for (sd = d->speeddials; sd; sd = sd->next) {
+ if (!sd->isHint && sd->instance == speeddialInstance) {
+ ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
+ req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance - 1);
+ speeddialInstance++;
+ buttonCount++;
+ btnSet = 1;
+ break;
+ }
+ }
+ break;
+ case BT_NONE:
+ break;
+ default:
+ ast_verbose("Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
+ req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
+ req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
+ buttonCount++;
+ btnSet = 1;
+ break;
+ }
+ }
+
+ req->data.buttontemplate.buttonOffset = htolel(0);
+ req->data.buttontemplate.buttonCount = htolel(buttonCount);
+ req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
+
+ if (skinnydebug)
+ ast_verbose("Sending %d template to %s\n",
+ d->type,
+ d->name);
+ transmit_response(s, req);
+ return 1;
+}
+
+static int handle_version_req_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ if (!(req = req_alloc(sizeof(struct version_res_message), VERSION_RES_MESSAGE)))
+ return -1;
+
+ ast_copy_string(req->data.version.version, d->version_id, sizeof(req->data.version.version));
+ transmit_response(s, req);
+ return 1;
+}
+
+static int handle_server_request_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ if (!(req = req_alloc(sizeof(struct server_res_message), SERVER_RES_MESSAGE)))
+ return -1;
+
+ memcpy(req->data.serverres.server[0].serverName, ourhost,
+ sizeof(req->data.serverres.server[0].serverName));
+ req->data.serverres.serverListenPort[0] = htolel(ourport);
+ req->data.serverres.serverIpAddr[0] = htolel(d->ourip.s_addr);
+ transmit_response(s, req);
+ return 1;
+}
+
+static int handle_alarm_message(struct skinny_req *req, struct skinnysession *s)
+{
+ /* no response necessary */
+ if (skinnydebug)
+ ast_verbose("Received Alarm Message: %s\n", req->data.alarm.displayMessage);
+
+ return 1;
+}
+
+static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ struct skinny_subchannel *sub;
+ struct ast_format_list fmt;
+ struct sockaddr_in sin;
+ struct sockaddr_in us;
+ uint32_t addr;
+ int port;
+ int status;
+ int passthruid;
+
+ status = letohl(req->data.openreceivechannelack.status);
+ if (status) {
+ ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
+ return 0;
+ }
+ addr = letohl(req->data.openreceivechannelack.ipAddr);
+ port = letohl(req->data.openreceivechannelack.port);
+ passthruid = letohl(req->data.openreceivechannelack.passThruId);
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = addr;
+ sin.sin_port = htons(port);
+
+ sub = find_subchannel_by_reference(d, passthruid);
+
+ if (!sub)
+ return 0;
+
+ l = sub->parent;
+
+ if (sub->rtp) {
+ ast_rtp_set_peer(sub->rtp, &sin);
+ ast_rtp_get_us(sub->rtp, &us);
+ } else {
+ ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
+ return 0;
+ }
+
+ if (skinnydebug)
+ ast_verbose("ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+
+ if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
+ return -1;
+
+ fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
+
+ if (skinnydebug)
+ ast_verbose("Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
+
+ req->data.startmedia.conferenceId = htolel(sub->callid);
+ req->data.startmedia.passThruPartyId = htolel(sub->callid);
+ req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
+ req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
+ req->data.startmedia.packetSize = htolel(fmt.cur_ms);
+ req->data.startmedia.payloadType = htolel(codec_ast2skinny(fmt.bits));
+ req->data.startmedia.qualifier.precedence = htolel(127);
+ req->data.startmedia.qualifier.vad = htolel(0);
+ req->data.startmedia.qualifier.packets = htolel(0);
+ req->data.startmedia.qualifier.bitRate = htolel(0);
+ transmit_response(s, req);
+
+ return 1;
+}
+
+static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ struct skinny_subchannel *sub = NULL;
+ struct ast_channel *c;
+ pthread_t t;
+
+ if (skinnydebug)
+ ast_verbose("Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
+
+ sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+
+ if (!sub) {
+ l = find_line_by_instance(d, d->lastlineinstance);
+ if (!l) {
+ return 0;
+ }
+ } else {
+ l = sub->parent;
+ }
+
+ c = skinny_new(l, AST_STATE_DOWN);
+
+ if(!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ l->hookstate = SKINNY_OFFHOOK;
+
+ sub = c->tech_pvt;
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+
+ if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ }
+ ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
+ if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
+ ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ }
+
+ return 1;
+}
+
+
+static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinnysession *s)
+{
+ int i;
+ int x;
+ int y;
+ const struct soft_key_definitions *softkeymode = soft_key_default_definitions;
+
+ if (!(req = req_alloc(sizeof(struct soft_key_set_res_message), SOFT_KEY_SET_RES_MESSAGE)))
+ return -1;
+
+ req->data.softkeysets.softKeySetOffset = htolel(0);
+ req->data.softkeysets.softKeySetCount = htolel(11);
+ req->data.softkeysets.totalSoftKeySetCount = htolel(11);
+ for (x = 0; x < sizeof(soft_key_default_definitions) / sizeof(struct soft_key_definitions); x++) {
+ const uint8_t *defaults = softkeymode->defaults;
+ /* XXX I wanted to get the size of the array dynamically, but that wasn't wanting to work.
+ This will have to do for now. */
+ for (y = 0; y < softkeymode->count; y++) {
+ for (i = 0; i < (sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); i++) {
+ if (defaults[y] == i+1) {
+ req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyTemplateIndex[y] = htolel(i+1);
+ req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyInfoIndex[y] = htolel(i+301);
+ }
+ }
+ }
+ softkeymode++;
+ }
+ transmit_response(s,req);
+ transmit_selectsoftkeys(s, 0, 0, KEYDEF_ONHOOK);
+ return 1;
+}
+
+static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s)
+{
+ struct skinny_device *d = s->device;
+ struct skinny_line *l;
+ struct skinny_subchannel *sub = NULL;
+ struct ast_channel *c;
+ pthread_t t;
+ int event;
+ int instance;
+ int callreference;
+
+ event = letohl(req->data.softkeyeventmessage.softKeyEvent);
+ instance = letohl(req->data.softkeyeventmessage.instance);
+ callreference = letohl(req->data.softkeyeventmessage.callreference);
+
+ if (instance) {
+ l = find_line_by_instance(d, instance);
+ if (callreference) {
+ sub = find_subchannel_by_instance_reference(d, instance, callreference);
+ } else {
+ sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference);
+ }
+ } else {
+ l = find_line_by_instance(d, d->lastlineinstance);
+ }
+
+ if (!l) {
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
+ return 0;
+ }
+
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+
+ switch(event) {
+ case SOFTKEY_NONE:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: None(%d/%d)\n", instance, callreference);
+ break;
+ case SOFTKEY_REDIAL:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
+
+ if (ast_strlen_zero(l->lastnumberdialed)) {
+ ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
+ l->hookstate = SKINNY_ONHOOK;
+ transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
+ transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance);
+ break;
+ }
+
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ if (l->hookstate == SKINNY_ONHOOK) {
+ l->hookstate = SKINNY_OFFHOOK;
+ transmit_speaker_mode(s, SKINNY_SPEAKERON);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ }
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+
+ if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ }
+ ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
+ if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
+ ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ }
+ break;
+ case SOFTKEY_NEWCALL: /* Actually the DIAL softkey */
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
+
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ /* transmit_ringer_mode(s,SKINNY_RING_OFF);
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); */
+
+ /* l->hookstate = SKINNY_OFFHOOK; */
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ if (l->hookstate == SKINNY_ONHOOK) {
+ l->hookstate = SKINNY_OFFHOOK;
+ transmit_speaker_mode(s, SKINNY_SPEAKERON);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ }
+
+ if (skinnydebug)
+ ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+ transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
+ transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
+
+ /* start the switch thread */
+ if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
+ ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
+ ast_hangup(c);
+ }
+ }
+ break;
+ case SOFTKEY_HOLD:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
+
+ if (sub) {
+ if (sub->onhold) {
+ skinny_unhold(sub);
+ } else {
+ skinny_hold(sub);
+ }
+ }
+
+ break;
+ case SOFTKEY_TRNSFER:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
+ /* XXX figure out how to transfer */
+ break;
+ case SOFTKEY_DND:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: DND(%d/%d)\n", instance, callreference);
+
+ /* Do not disturb */
+ if (l->dnd != 0){
+ ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
+ l->dnd = 0;
+ transmit_lamp_indication(s, STIMULUS_DND, 1, SKINNY_LAMP_ON);
+ transmit_displaynotify(s, "DnD disabled", 10);
+ } else {
+ ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
+ l->dnd = 1;
+ transmit_lamp_indication(s, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
+ transmit_displaynotify(s, "DnD enabled", 10);
+ }
+ break;
+ case SOFTKEY_CFWDALL:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
+
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ handle_callforward_button(sub, SKINNY_CFWD_ALL);
+ }
+ break;
+ case SOFTKEY_CFWDBUSY:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
+
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ handle_callforward_button(sub, SKINNY_CFWD_BUSY);
+ }
+ break;
+ case SOFTKEY_CFWDNOANSWER:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
+
+#if 0 /* Not sure how to handle this yet */
+ if (!sub || !sub->owner) {
+ c = skinny_new(l, AST_STATE_DOWN);
+ } else {
+ c = sub->owner;
+ }
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+ } else {
+ sub = c->tech_pvt;
+ handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
+ }
+#endif
+ break;
+ case SOFTKEY_BKSPC:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
+ break;
+ case SOFTKEY_ENDCALL:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
+
+ if (l->hookstate == SKINNY_ONHOOK) {
+ /* Something else already put us back on hook */
+ break;
+ }
+ if (sub) {
+ sub->cxmode = SKINNY_CX_RECVONLY;
+ l->hookstate = SKINNY_ONHOOK;
+ transmit_callstate(s, l->instance, l->hookstate, sub->callid);
+ if (skinnydebug)
+ ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name);
+ if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) {
+ /* We're allowed to transfer, we have two active calls and
+ we made at least one of the calls. Let's try and transfer */
+
+#if 0
+ if ((res = attempt_transfer(p)) < 0) {
+ if (sub->next && sub->next->owner) {
+ sub->next->alreadygone = 1;
+ ast_queue_hangup(sub->next->owner, 1);
+ }
+ } else if (res) {
+ ast_log(LOG_WARNING, "Transfer attempt failed\n");
+ break;
+ }
+#endif
+ } else {
+ /* Hangup the current call */
+ /* If there is another active call, skinny_hangup will ring the phone with the other call */
+ if (sub->owner) {
+ sub->alreadygone = 1;
+ ast_queue_hangup(sub->owner);
+ } else {
+ ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
+ l->name, d->name, sub->callid);
+ }
+ }
+ if ((l->hookstate == SKINNY_ONHOOK) && (sub->next && !sub->next->rtp)) {
+ do_housekeeping(s);
+ }
+ }
+ break;
+ case SOFTKEY_RESUME:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
+ break;
+ case SOFTKEY_ANSWER:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
+
+ transmit_ringer_mode(s,SKINNY_RING_OFF);
+ transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+
+ l->hookstate = SKINNY_OFFHOOK;
+
+ if (sub && sub->outgoing) {
+ /* We're answering a ringing call */
+ ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
+ transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+ transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+ transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
+ transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
+ start_rtp(sub);
+ ast_setstate(sub->owner, AST_STATE_UP);
+ }
+ break;
+ case SOFTKEY_INFO:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Info(%d/%d)\n", instance, callreference);
+ break;
+ case SOFTKEY_CONFRN:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
+ /* XXX determine the best way to pull off a conference. Meetme? */
+ break;
+ case SOFTKEY_PARK:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
+ /* XXX Park the call */
+ break;
+ case SOFTKEY_JOIN:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Join(%d/%d)\n", instance, callreference);
+ break;
+ case SOFTKEY_MEETME:
+ /* XXX How is this different from CONFRN? */
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
+ break;
+ case SOFTKEY_PICKUP:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
+ break;
+ case SOFTKEY_GPICKUP:
+ if (skinnydebug)
+ ast_verbose("Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
+ break;
+ default:
+ if (skinnydebug)
+ ast_verbose("Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
+ break;
+ }
+ ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+
+ return 1;
+}
+
+static int handle_unregister_message(struct skinny_req *req, struct skinnysession *s)
+{
+ return skinny_unregister(req, s);
+}
+
+static int handle_soft_key_template_req_message(struct skinny_req *req, struct skinnysession *s)
+{
+ if (!(req = req_alloc(sizeof(struct soft_key_template_res_message), SOFT_KEY_TEMPLATE_RES_MESSAGE)))
+ return -1;
+
+ req->data.softkeytemplate.softKeyOffset = htolel(0);
+ req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
+ req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
+ memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
+ soft_key_template_default,
+ sizeof(soft_key_template_default));
+ transmit_response(s,req);
+ return 1;
+}
+
+static int handle_headset_status_message(struct skinny_req *req, struct skinnysession *s)
+{
+ /* XXX umm...okay? Why do I care? */
+ return 1;
+}
+
+static int handle_register_available_lines_message(struct skinny_req *req, struct skinnysession *s)
+{
+ /* XXX I have no clue what this is for, but my phone was sending it, so... */
+ return 1;
+}
+
+static int handle_message(struct skinny_req *req, struct skinnysession *s)
+{
+ int res = 0;
+
+ if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
+ ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
+ ast_free(req);
+ return 0;
+ }
+
+ switch(letohl(req->e)) {
+ case KEEP_ALIVE_MESSAGE:
+ res = handle_keep_alive_message(req, s);
+ break;
+ case REGISTER_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Device %s is attempting to register\n", req->data.reg.name);
+
+ res = handle_register_message(req, s);
+ break;
+ case IP_PORT_MESSAGE:
+ res = handle_ip_port_message(req, s);
+ break;
+ case KEYPAD_BUTTON_MESSAGE:
+ {
+ struct skinny_device *d = s->device;
+ struct skinny_subchannel *sub;
+ int lineInstance;
+ int callReference;
+
+ if (skinnydebug)
+ ast_verbose("Collected digit: [%d]\n", letohl(req->data.keypad.button));
+
+ lineInstance = letohl(req->data.keypad.lineInstance);
+ callReference = letohl(req->data.keypad.callReference);
+
+ sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
+
+ if (sub && (sub->owner && sub->owner->_state < AST_STATE_UP)) {
+ char dgt;
+ int digit = letohl(req->data.keypad.button);
+
+ if (digit == 14) {
+ dgt = '*';
+ } else if (digit == 15) {
+ dgt = '#';
+ } else if (digit >= 0 && digit <= 9) {
+ dgt = '0' + digit;
+ } else {
+ /* digit=10-13 (A,B,C,D ?), or
+ * digit is bad value
+ *
+ * probably should not end up here, but set
+ * value for backward compatibility, and log
+ * a warning.
+ */
+ dgt = '0' + digit;
+ ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
+ }
+
+ d->exten[strlen(d->exten)] = dgt;
+ d->exten[strlen(d->exten)+1] = '\0';
+ } else
+ res = handle_keypad_button_message(req, s);
+ }
+ break;
+ case ENBLOC_CALL_MESSAGE:
+ res = handle_enbloc_call_message(req, s);
+ break;
+ case STIMULUS_MESSAGE:
+ res = handle_stimulus_message(req, s);
+ break;
+ case OFFHOOK_MESSAGE:
+ res = handle_offhook_message(req, s);
+ break;
+ case ONHOOK_MESSAGE:
+ res = handle_onhook_message(req, s);
+ break;
+ case CAPABILITIES_RES_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received CapabilitiesRes\n");
+
+ res = handle_capabilities_res_message(req, s);
+ break;
+ case SPEED_DIAL_STAT_REQ_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received SpeedDialStatRequest\n");
+
+ res = handle_speed_dial_stat_req_message(req, s);
+ break;
+ case LINE_STATE_REQ_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received LineStatRequest\n");
+ res = handle_line_state_req_message(req, s);
+ break;
+ case TIME_DATE_REQ_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received Time/Date Request\n");
+
+ res = handle_time_date_req_message(req, s);
+ break;
+ case BUTTON_TEMPLATE_REQ_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Buttontemplate requested\n");
+
+ res = handle_button_template_req_message(req, s);
+ break;
+ case VERSION_REQ_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Version Request\n");
+
+ res = handle_version_req_message(req, s);
+ break;
+ case SERVER_REQUEST_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received Server Request\n");
+
+ res = handle_server_request_message(req, s);
+ break;
+ case ALARM_MESSAGE:
+ res = handle_alarm_message(req, s);
+ break;
+ case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received Open Receive Channel Ack\n");
+
+ res = handle_open_receive_channel_ack_message(req, s);
+ break;
+ case SOFT_KEY_SET_REQ_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received SoftKeySetReq\n");
+
+ res = handle_soft_key_set_req_message(req, s);
+ break;
+ case SOFT_KEY_EVENT_MESSAGE:
+ res = handle_soft_key_event_message(req, s);
+ break;
+ case UNREGISTER_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received Unregister Request\n");
+
+ res = handle_unregister_message(req, s);
+ break;
+ case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
+ if (skinnydebug)
+ ast_verbose("Received SoftKey Template Request\n");
+
+ res = handle_soft_key_template_req_message(req, s);
+ break;
+ case HEADSET_STATUS_MESSAGE:
+ res = handle_headset_status_message(req, s);
+ break;
+ case REGISTER_AVAILABLE_LINES_MESSAGE:
+ res = handle_register_available_lines_message(req, s);
+ break;
+ default:
+ if (skinnydebug)
+ ast_verbose("RECEIVED UNKNOWN MESSAGE TYPE: %x\n", letohl(req->e));
+ break;
+ }
+ if (res >= 0 && req)
+ ast_free(req);
+ return res;
+}
+
+static void destroy_session(struct skinnysession *s)
+{
+ struct skinnysession *cur, *prev = NULL;
+ ast_mutex_lock(&sessionlock);
+ cur = sessions;
+ while(cur) {
+ if (cur == s) {
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ if (cur) {
+ if (prev) {
+ prev->next = cur->next;
+ } else {
+ sessions = cur->next;
+ }
+ if (s->fd > -1) {
+ close(s->fd);
+ }
+ ast_mutex_destroy(&s->lock);
+ ast_free(s);
+ } else {
+ ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
+ }
+ ast_mutex_unlock(&sessionlock);
+}
+
+static int get_input(struct skinnysession *s)
+{
+ int res;
+ int dlen = 0;
+ struct pollfd fds[1];
+
+ fds[0].fd = s->fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ res = poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
+ /* we add 10% to the keep_alive to deal */
+ /* with network delays, etc */
+ if (res < 0) {
+ if (errno != EINTR) {
+ ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
+ return res;
+ }
+ } else if (res == 0) {
+ if (skinnydebug)
+ ast_verbose("Skinny Client was lost, unregistering\n");
+ skinny_unregister(NULL, s);
+ return -1;
+ }
+
+ if (fds[0].revents) {
+ ast_mutex_lock(&s->lock);
+ memset(s->inbuf,0,sizeof(s->inbuf));
+ res = read(s->fd, s->inbuf, 4);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
+
+ if (skinnydebug)
+ ast_verbose("Skinny Client was lost, unregistering\n");
+
+ skinny_unregister(NULL,s);
+ ast_mutex_unlock(&s->lock);
+ return res;
+ } else if (res != 4) {
+ ast_log(LOG_WARNING, "Skinny Client sent less data than expected. Expected 4 but got %d.\n", res);
+ ast_mutex_unlock(&s->lock);
+
+ if (res == 0) {
+ if (skinnydebug)
+ ast_verbose("Skinny Client was lost, unregistering\n");
+ skinny_unregister(NULL, s);
+ }
+
+ return -1;
+ }
+
+ dlen = letohl(*(int *)s->inbuf);
+ if (dlen < 4) {
+ ast_log(LOG_WARNING, "Skinny Client sent invalid data.\n");
+ ast_mutex_unlock(&s->lock);
+ return -1;
+ }
+ if (dlen+8 > sizeof(s->inbuf)) {
+ dlen = sizeof(s->inbuf) - 8;
+ }
+ *(int *)s->inbuf = htolel(dlen);
+
+ res = read(s->fd, s->inbuf+4, dlen+4);
+ ast_mutex_unlock(&s->lock);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
+ return res;
+ } else if (res != (dlen+4)) {
+ ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
+ return -1;
+ }
+ return res;
+ }
+ return 0;
+}
+
+static struct skinny_req *skinny_req_parse(struct skinnysession *s)
+{
+ struct skinny_req *req;
+
+ if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
+ return NULL;
+
+ ast_mutex_lock(&s->lock);
+ memcpy(req, s->inbuf, skinny_header_size);
+ memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*(int*)(s->inbuf))-4);
+
+ ast_mutex_unlock(&s->lock);
+
+ if (letohl(req->e) < 0) {
+ ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
+ ast_free(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void *skinny_session(void *data)
+{
+ int res;
+ struct skinny_req *req;
+ struct skinnysession *s = data;
+
+ ast_verb(3, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
+
+ for (;;) {
+ res = get_input(s);
+ if (res < 0) {
+ break;
+ }
+
+ if (res > 0)
+ {
+ if (!(req = skinny_req_parse(s))) {
+ destroy_session(s);
+ return NULL;
+ }
+
+ res = handle_message(req, s);
+ if (res < 0) {
+ destroy_session(s);
+ return NULL;
+ }
+ }
+ }
+ ast_log(LOG_NOTICE, "Skinny Session returned: %s\n", strerror(errno));
+
+ if (s)
+ destroy_session(s);
+
+ return 0;
+}
+
+static void *accept_thread(void *ignore)
+{
+ int as;
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+ struct skinnysession *s;
+ struct protoent *p;
+ int arg = 1;
+ pthread_t tcp_thread;
+
+ for (;;) {
+ sinlen = sizeof(sin);
+ as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
+ if (as < 0) {
+ ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
+ continue;
+ }
+ p = getprotobyname("tcp");
+ if(p) {
+ if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
+ ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
+ }
+ }
+ if (!(s = ast_calloc(1, sizeof(struct skinnysession))))
+ continue;
+
+ memcpy(&s->sin, &sin, sizeof(sin));
+ ast_mutex_init(&s->lock);
+ s->fd = as;
+ ast_mutex_lock(&sessionlock);
+ s->next = sessions;
+ sessions = s;
+ ast_mutex_unlock(&sessionlock);
+
+ if (ast_pthread_create_detached(&tcp_thread, NULL, skinny_session, s)) {
+ destroy_session(s);
+ }
+ }
+ if (skinnydebug)
+ ast_verbose("killing accept thread\n");
+ close(as);
+ return 0;
+}
+
+static void *do_monitor(void *data)
+{
+ int res;
+
+ /* This thread monitors all the interfaces which are not yet in use
+ (and thus do not have a separate thread) indefinitely */
+ /* From here on out, we die whenever asked */
+ for(;;) {
+ pthread_testcancel();
+ /* Wait for sched or io */
+ res = ast_sched_wait(sched);
+ if ((res < 0) || (res > 1000)) {
+ res = 1000;
+ }
+ res = ast_io_wait(io, res);
+ ast_mutex_lock(&monlock);
+ if (res >= 0) {
+ ast_sched_runq(sched);
+ }
+ ast_mutex_unlock(&monlock);
+ }
+ /* Never reached */
+ return NULL;
+
+}
+
+static int restart_monitor(void)
+{
+ /* If we're supposed to be stopped -- stay stopped */
+ if (monitor_thread == AST_PTHREADT_STOP)
+ return 0;
+
+ ast_mutex_lock(&monlock);
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ /* Wake up the thread */
+ pthread_kill(monitor_thread, SIGURG);
+ } else {
+ /* Start a new monitor */
+ if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+static int skinny_devicestate(void *data)
+{
+ struct skinny_line *l;
+ char *tmp;
+
+ tmp = ast_strdupa(data);
+
+ l = find_line_by_name(tmp);
+
+ return get_devicestate(l);
+}
+
+static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+
+ struct skinny_line *l;
+ struct ast_channel *tmpc = NULL;
+ char tmp[256];
+ char *dest = data;
+
+ oldformat = format;
+
+ if (!(format &= AST_FORMAT_AUDIO_MASK)) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
+ return NULL;
+ }
+
+ ast_copy_string(tmp, dest, sizeof(tmp));
+ if (ast_strlen_zero(tmp)) {
+ ast_log(LOG_NOTICE, "Skinny channels require a device\n");
+ return NULL;
+ }
+ l = find_line_by_name(tmp);
+ if (!l) {
+ ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
+ return NULL;
+ }
+ ast_verb(3, "skinny_request(%s)\n", tmp);
+ tmpc = skinny_new(l, AST_STATE_DOWN);
+ if (!tmpc) {
+ ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
+ }
+ restart_monitor();
+ return tmpc;
+}
+
+static int reload_config(void)
+{
+ int on = 1;
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ char *cat;
+ struct skinny_device *d;
+ int oldport = ntohs(bindaddr.sin_port);
+ char *stringp, *context, *oldregcontext;
+ char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
+ struct ast_flags config_flags = { 0 };
+
+ if (gethostname(ourhost, sizeof(ourhost))) {
+ ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n");
+ return 0;
+ }
+ cfg = ast_config_load(config, config_flags);
+
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled\n", config);
+ return -1;
+ }
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ memset(&default_prefs, 0, sizeof(default_prefs));
+
+ /* Initialize copy of current global_regcontext for later use in removing stale contexts */
+ ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts));
+ oldregcontext = oldcontexts;
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ /* load the general section */
+ v = ast_variable_browse(cfg, "general");
+ while (v) {
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
+ v = v->next;
+ continue;
+ }
+
+ /* Create the interface list */
+ if (!strcasecmp(v->name, "bindaddr")) {
+ if (!(hp = ast_gethostbyname(v->value, &ahp))) {
+ ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
+ } else {
+ memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
+ }
+ } else if (!strcasecmp(v->name, "keepalive")) {
+ keep_alive = atoi(v->value);
+ } else if (!strcasecmp(v->name, "vmexten")) {
+ ast_copy_string(vmexten, v->value, sizeof(vmexten));
+ } else if (!strcasecmp(v->name, "regcontext")) {
+ ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
+ stringp = newcontexts;
+ /* Let's remove any contexts that are no longer defined in regcontext */
+ cleanup_stale_contexts(stringp, oldregcontext);
+ /* Create contexts if they don't exist already */
+ while ((context = strsep(&stringp, "&"))) {
+ ast_copy_string(used_context, context, sizeof(used_context));
+ if (!ast_context_find(context))
+ ast_context_create(NULL, context, "Skinny");
+ }
+ ast_copy_string(regcontext, v->value, sizeof(regcontext));
+ } else if (!strcasecmp(v->name, "dateformat")) {
+ memcpy(date_format, v->value, sizeof(date_format));
+ } else if (!strcasecmp(v->name, "tos")) {
+ if (ast_str2tos(v->value, &tos))
+ ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "tos_audio")) {
+ if (ast_str2tos(v->value, &tos_audio))
+ ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "tos_video")) {
+ if (ast_str2tos(v->value, &tos_video))
+ ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos")) {
+ if (ast_str2cos(v->value, &cos))
+ ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_audio")) {
+ if (ast_str2cos(v->value, &cos_audio))
+ ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_video")) {
+ if (ast_str2cos(v->value, &cos_video))
+ ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "allow")) {
+ ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 1);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 0);
+ } else if (!strcasecmp(v->name, "bindport")) {
+ if (sscanf(v->value, "%d", &ourport) == 1) {
+ bindaddr.sin_port = htons(ourport);
+ } else {
+ ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
+ }
+ }
+ v = v->next;
+ }
+
+ if (ntohl(bindaddr.sin_addr.s_addr)) {
+ __ourip = bindaddr.sin_addr;
+ } else {
+ hp = ast_gethostbyname(ourhost, &ahp);
+ if (!hp) {
+ ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
+ }
+ if (!ntohs(bindaddr.sin_port)) {
+ bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT);
+ }
+ bindaddr.sin_family = AF_INET;
+
+ /* load the device sections */
+ cat = ast_category_browse(cfg, NULL);
+ while(cat) {
+ if (!strcasecmp(cat, "general")) {
+ /* Nothing to do */
+#if 0
+ } else if (!strncasecmp(cat, "paging-", 7)) {
+ p = build_paging_device(cat, ast_variable_browse(cfg, cat));
+ if (p) {
+ }
+#endif
+ } else {
+ d = build_device(cat, ast_variable_browse(cfg, cat));
+ if (d) {
+ ast_verb(3, "Added device '%s'\n", d->name);
+ ast_mutex_lock(&devicelock);
+ d->next = devices;
+ devices = d;
+ ast_mutex_unlock(&devicelock);
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_mutex_lock(&netlock);
+ if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
+ close(skinnysock);
+ skinnysock = -1;
+ }
+ if (skinnysock < 0) {
+ skinnysock = socket(AF_INET, SOCK_STREAM, 0);
+ if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+ ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno));
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ if (skinnysock < 0) {
+ ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
+ } else {
+ if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+ ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
+ ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
+ strerror(errno));
+ close(skinnysock);
+ skinnysock = -1;
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ if (listen(skinnysock,DEFAULT_SKINNY_BACKLOG)) {
+ ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
+ ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
+ strerror(errno));
+ close(skinnysock);
+ skinnysock = -1;
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ ast_verb(2, "Skinny listening on %s:%d\n",
+ ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
+ ast_netsock_set_qos(skinnysock, tos, cos, "Skinny");
+ ast_pthread_create_background(&accept_t,NULL, accept_thread, NULL);
+ }
+ }
+ ast_mutex_unlock(&netlock);
+ ast_config_destroy(cfg);
+ return 1;
+}
+
+static void delete_devices(void)
+{
+ struct skinny_device *d, *dlast;
+ struct skinny_line *l, *llast;
+ struct skinny_speeddial *sd, *sdlast;
+ struct skinny_addon *a, *alast;
+
+ ast_mutex_lock(&devicelock);
+
+ /* Delete all devices */
+ for (d=devices;d;) {
+ /* Delete all lines for this device */
+ for (l=d->lines;l;) {
+ llast = l;
+ l = l->next;
+ ast_mutex_destroy(&llast->lock);
+ ast_free(llast);
+ }
+ /* Delete all speeddials for this device */
+ for (sd=d->speeddials;sd;) {
+ sdlast = sd;
+ sd = sd->next;
+ ast_mutex_destroy(&sdlast->lock);
+ ast_free(sdlast);
+ }
+ /* Delete all addons for this device */
+ for (a=d->addons;a;) {
+ alast = a;
+ a = a->next;
+ ast_mutex_destroy(&alast->lock);
+ ast_free(alast);
+ }
+ dlast = d;
+ d = d->next;
+ ast_free(dlast);
+ }
+ devices=NULL;
+ ast_mutex_unlock(&devicelock);
+}
+
+#if 0
+/*
+ * XXX This never worked properly anyways.
+ * Let's get rid of it, until we can fix it.
+ */
+static int reload(void)
+{
+ delete_devices();
+ reload_config();
+ restart_monitor();
+ return 0;
+}
+#endif
+
+static int load_module(void)
+{
+ int res = 0;
+
+ for (; res < (sizeof(soft_key_template_default) / sizeof(soft_key_template_default[0])); res++) {
+ soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
+ }
+ /* load and parse config */
+ res = reload_config();
+ if (res == -1) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ /* Make sure we can register our skinny channel type */
+ if (ast_channel_register(&skinny_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n");
+ return -1;
+ }
+
+ ast_rtp_proto_register(&skinny_rtp);
+ ast_cli_register_multiple(cli_skinny, sizeof(cli_skinny) / sizeof(struct ast_cli_entry));
+ sched = sched_context_create();
+ if (!sched) {
+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
+ }
+ io = io_context_create();
+ if (!io) {
+ ast_log(LOG_WARNING, "Unable to create I/O context\n");
+ }
+ /* And start the monitor for the first time */
+ restart_monitor();
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ struct skinnysession *s, *slast;
+ struct skinny_device *d;
+ struct skinny_line *l;
+ struct skinny_subchannel *sub;
+ struct ast_context *con;
+
+ ast_mutex_lock(&sessionlock);
+ /* Destroy all the interfaces and free their memory */
+ s = sessions;
+ while(s) {
+ slast = s;
+ s = s->next;
+ for (d = slast->device; d; d = d->next) {
+ for (l = d->lines; l; l = l->next) {
+ ast_mutex_lock(&l->lock);
+ for (sub = l->sub; sub; sub = sub->next) {
+ ast_mutex_lock(&sub->lock);
+ if (sub->owner) {
+ sub->alreadygone = 1;
+ ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
+ }
+ ast_mutex_unlock(&sub->lock);
+ }
+ ast_mutex_unlock(&l->lock);
+ }
+ }
+ if (slast->fd > -1)
+ close(slast->fd);
+ ast_mutex_destroy(&slast->lock);
+ ast_free(slast);
+ }
+ sessions = NULL;
+ ast_mutex_unlock(&sessionlock);
+
+ delete_devices();
+
+ ast_mutex_lock(&monlock);
+ if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) {
+ pthread_cancel(monitor_thread);
+ pthread_kill(monitor_thread, SIGURG);
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+
+ ast_mutex_lock(&netlock);
+ if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
+ pthread_cancel(accept_t);
+ pthread_kill(accept_t, SIGURG);
+ pthread_join(accept_t, NULL);
+ }
+ accept_t = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&netlock);
+
+ ast_rtp_proto_unregister(&skinny_rtp);
+ ast_channel_unregister(&skinny_tech);
+ ast_cli_unregister_multiple(cli_skinny, sizeof(cli_skinny) / sizeof(struct ast_cli_entry));
+
+ close(skinnysock);
+ if (sched)
+ sched_context_destroy(sched);
+
+ con = ast_context_find(used_context);
+ if (con)
+ ast_context_destroy(con, "Skinny");
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skinny Client Control Protocol (Skinny)",
+ .load = load_module,
+ .unload = unload_module,
+ );
diff --git a/trunk/channels/chan_unistim.c b/trunk/channels/chan_unistim.c
new file mode 100644
index 000000000..3021f7c55
--- /dev/null
+++ b/trunk/channels/chan_unistim.c
@@ -0,0 +1,5668 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * UNISTIM channel driver for asterisk
+ *
+ * Copyright (C) 2005 - 2007, Cedric Hans
+ *
+ * Cedric Hans <cedric.hans@mlkj.net>
+ *
+ * Asterisk 1.4 patch by Peter Be
+ *
+ * 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 chan_unistim channel driver for Asterisk
+ * \author Cedric Hans <cedric.hans@mlkj.net>
+ *
+ * Unistim (Unified Networks IP Stimulus) channel driver
+ * for Nortel i2002, i2004 and i2050
+ *
+ * \ingroup channel_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+#include <signal.h>
+
+#if defined(__CYGWIN__)
+/*
+ * cygwin headers are partly inconsistent. struct iovec is defined in sys/uio.h
+ * which is not included by default by sys/socket.h - in_pktinfo is defined in
+ * w32api/ws2tcpip.h but this probably has compatibility problems with sys/socket.h
+ * So for the time being we simply disable HAVE_PKTINFO when building under cygwin.
+ * This should be done in some common header, but for now this is the only file
+ * using iovec and in_pktinfo so it suffices to apply the fix here.
+ */
+#ifdef HAVE_PKTINFO
+#undef HAVE_PKTINFO
+#endif
+#endif /* __CYGWIN__ */
+
+#include "asterisk/paths.h" /* ast_config_AST_LOG_DIR used in (too ?) many places */
+#include "asterisk/network.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/event.h"
+#include "asterisk/rtp.h"
+#include "asterisk/netsock.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/causes.h"
+#include "asterisk/indications.h"
+
+/*! Beware, G729 and G723 are not supported by asterisk, except with the proper licence */
+#define CAPABILITY AST_FORMAT_ALAW | AST_FORMAT_ULAW /* | AST_FORMAT_G729A | AST_FORMAT_G723_1 */
+
+#define DEFAULTCONTEXT "default"
+#define DEFAULTCALLERID "Unknown"
+#define DEFAULTCALLERNAME " "
+#define USTM_LOG_DIR "unistimHistory"
+
+/*! Size of the transmit buffer */
+#define MAX_BUF_SIZE 64
+/*! Number of slots for the transmit queue */
+#define MAX_BUF_NUMBER 50
+/*! Try x times before removing the phone */
+#define NB_MAX_RETRANSMIT 8
+/*! Nb of milliseconds waited when no events are scheduled */
+#define IDLE_WAIT 1000
+/*! Wait x milliseconds before resending a packet */
+#define RETRANSMIT_TIMER 2000
+/*! How often the mailbox is checked for new messages */
+#define TIMER_MWI 10000
+/*! Not used */
+#define DEFAULT_CODEC 0x00
+#define SIZE_PAGE 4096
+#define DEVICE_NAME_LEN 16
+#define AST_CONFIG_MAX_PATH 255
+#define MAX_ENTRY_LOG 30
+
+#define SUB_REAL 0
+#define SUB_THREEWAY 1
+#define MAX_SUBS 2
+
+enum autoprovision {
+ AUTOPROVISIONING_NO = 0,
+ AUTOPROVISIONING_YES,
+ AUTOPROVISIONING_DB,
+ AUTOPROVISIONING_TN
+};
+
+enum autoprov_extn {
+ /*! Do not create an extension into the default dialplan */
+ EXTENSION_NONE = 0,
+ /*! Prompt user for an extension number and register it */
+ EXTENSION_ASK,
+ /*! Register an extension with the line=> value */
+ EXTENSION_LINE,
+ /*! Used with AUTOPROVISIONING_TN */
+ EXTENSION_TN
+};
+#define OUTPUT_HANDSET 0xC0
+#define OUTPUT_HEADPHONE 0xC1
+#define OUTPUT_SPEAKER 0xC2
+
+#define VOLUME_LOW 0x01
+#define VOLUME_LOW_SPEAKER 0x03
+#define VOLUME_NORMAL 0x02
+#define VOLUME_INSANELY_LOUD 0x07
+
+#define MUTE_OFF 0x00
+#define MUTE_ON 0xFF
+#define MUTE_ON_DISCRET 0xCE
+
+#define SIZE_HEADER 6
+#define SIZE_MAC_ADDR 17
+#define TEXT_LENGTH_MAX 24
+#define TEXT_LINE0 0x00
+#define TEXT_LINE1 0x20
+#define TEXT_LINE2 0x40
+#define TEXT_NORMAL 0x05
+#define TEXT_INVERSE 0x25
+#define STATUS_LENGTH_MAX 28
+
+#define FAV_ICON_NONE 0x00
+#define FAV_ICON_ONHOOK_BLACK 0x20
+#define FAV_ICON_ONHOOK_WHITE 0x21
+#define FAV_ICON_SPEAKER_ONHOOK_BLACK 0x22
+#define FAV_ICON_SPEAKER_ONHOOK_WHITE 0x23
+#define FAV_ICON_OFFHOOK_BLACK 0x24
+#define FAV_ICON_OFFHOOK_WHITE 0x25
+#define FAV_ICON_ONHOLD_BLACK 0x26
+#define FAV_ICON_ONHOLD_WHITE 0x27
+#define FAV_ICON_SPEAKER_OFFHOOK_BLACK 0x28
+#define FAV_ICON_SPEAKER_OFFHOOK_WHITE 0x29
+#define FAV_ICON_PHONE_BLACK 0x2A
+#define FAV_ICON_PHONE_WHITE 0x2B
+#define FAV_ICON_SPEAKER_ONHOLD_BLACK 0x2C
+#define FAV_ICON_SPEAKER_ONHOLD_WHITE 0x2D
+#define FAV_ICON_HEADPHONES 0x2E
+#define FAV_ICON_HEADPHONES_ONHOLD 0x2F
+#define FAV_ICON_HOME 0x30
+#define FAV_ICON_CITY 0x31
+#define FAV_ICON_SHARP 0x32
+#define FAV_ICON_PAGER 0x33
+#define FAV_ICON_CALL_CENTER 0x34
+#define FAV_ICON_FAX 0x35
+#define FAV_ICON_MAILBOX 0x36
+#define FAV_ICON_REFLECT 0x37
+#define FAV_ICON_COMPUTER 0x38
+#define FAV_ICON_FORWARD 0x39
+#define FAV_ICON_LOCKED 0x3A
+#define FAV_ICON_TRASH 0x3B
+#define FAV_ICON_INBOX 0x3C
+#define FAV_ICON_OUTBOX 0x3D
+#define FAV_ICON_MEETING 0x3E
+#define FAV_ICON_BOX 0x3F
+
+#define FAV_BLINK_FAST 0x20
+#define FAV_BLINK_SLOW 0x40
+
+#define FAV_MAX_LENGTH 0x0A
+
+static void dummy(char *dummy, ...)
+{
+ return;
+}
+
+/*! \brief Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+
+/* #define DUMP_PACKET 1 */
+/* #define DEBUG_TIMER ast_verbose */
+
+#define DEBUG_TIMER dummy
+/*! Enable verbose output. can also be set with the CLI */
+static int unistimdebug = 0;
+static int unistim_port;
+static enum autoprovision autoprovisioning = AUTOPROVISIONING_NO;
+static int unistim_keepalive;
+static int unistimsock = -1;
+static unsigned int tos = 0;
+static unsigned int tos_audio = 0;
+static unsigned int cos = 0;
+static unsigned int cos_audio = 0;
+static struct io_context *io;
+static struct sched_context *sched;
+static struct sockaddr_in public_ip = { 0, };
+/*! give the IP address for the last packet received */
+static struct sockaddr_in addr_from;
+/*! size of the sockaddr_in (in WSARecvFrom) */
+static unsigned int size_addr_from = sizeof(addr_from);
+/*! Receive buffer address */
+static unsigned char *buff;
+static int unistim_reloading = 0;
+AST_MUTEX_DEFINE_STATIC(unistim_reload_lock);
+AST_MUTEX_DEFINE_STATIC(usecnt_lock);
+static int usecnt = 0;
+/* extern char ast_config_AST_LOG_DIR[AST_CONFIG_MAX_PATH]; */
+
+/*! This is the thread for the monitor which checks for input on the channels
+ * which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+
+/*! Protect the monitoring thread, so only one process can kill or start it, and not
+ * when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+/*! Protect the session list */
+AST_MUTEX_DEFINE_STATIC(sessionlock);
+/*! Protect the device list */
+AST_MUTEX_DEFINE_STATIC(devicelock);
+
+enum phone_state {
+ STATE_INIT,
+ STATE_AUTHDENY,
+ STATE_MAINPAGE,
+ STATE_EXTENSION,
+ STATE_DIALPAGE,
+ STATE_RINGING,
+ STATE_CALL,
+ STATE_SELECTCODEC,
+ STATE_CLEANING,
+ STATE_HISTORY
+};
+
+enum handset_state {
+ STATE_ONHOOK,
+ STATE_OFFHOOK,
+};
+
+enum phone_key {
+ KEY_0 = 0x40,
+ KEY_1 = 0x41,
+ KEY_2 = 0x42,
+ KEY_3 = 0x43,
+ KEY_4 = 0x44,
+ KEY_5 = 0x45,
+ KEY_6 = 0x46,
+ KEY_7 = 0x47,
+ KEY_8 = 0x48,
+ KEY_9 = 0x49,
+ KEY_STAR = 0x4a,
+ KEY_SHARP = 0x4b,
+ KEY_UP = 0x4c,
+ KEY_DOWN = 0x4d,
+ KEY_RIGHT = 0x4e,
+ KEY_LEFT = 0x4f,
+ KEY_QUIT = 0x50,
+ KEY_COPY = 0x51,
+ KEY_FUNC1 = 0x54,
+ KEY_FUNC2 = 0x55,
+ KEY_FUNC3 = 0x56,
+ KEY_FUNC4 = 0x57,
+ KEY_ONHOLD = 0x5b,
+ KEY_HANGUP = 0x5c,
+ KEY_MUTE = 0x5d,
+ KEY_HEADPHN = 0x5e,
+ KEY_LOUDSPK = 0x5f,
+ KEY_FAV0 = 0x60,
+ KEY_FAV1 = 0x61,
+ KEY_FAV2 = 0x62,
+ KEY_FAV3 = 0x63,
+ KEY_FAV4 = 0x64,
+ KEY_FAV5 = 0x65,
+ KEY_COMPUTR = 0x7b,
+ KEY_CONF = 0x7c,
+ KEY_SNDHIST = 0x7d,
+ KEY_RCVHIST = 0x7e,
+ KEY_INDEX = 0x7f
+};
+
+struct tone_zone_unistim {
+ char country[3];
+ int freq1;
+ int freq2;
+};
+
+static const struct tone_zone_unistim frequency[] = {
+ {"us", 350, 440},
+ {"fr", 440, 0},
+ {"au", 413, 438},
+ {"nl", 425, 0},
+ {"uk", 350, 440},
+ {"fi", 425, 0},
+ {"es", 425, 0},
+ {"jp", 400, 0},
+ {"no", 425, 0},
+ {"at", 420, 0},
+ {"nz", 400, 0},
+ {"tw", 350, 440},
+ {"cl", 400, 0},
+ {"se", 425, 0},
+ {"be", 425, 0},
+ {"sg", 425, 0},
+ {"il", 414, 0},
+ {"br", 425, 0},
+ {"hu", 425, 0},
+ {"lt", 425, 0},
+ {"pl", 425, 0},
+ {"za", 400, 0},
+ {"pt", 425, 0},
+ {"ee", 425, 0},
+ {"mx", 425, 0},
+ {"in", 400, 0},
+ {"de", 425, 0},
+ {"ch", 425, 0},
+ {"dk", 425, 0},
+ {"cn", 450, 0},
+ {"--", 0, 0}
+};
+
+struct wsabuf {
+ u_long len;
+ unsigned char *buf;
+};
+
+struct systemtime {
+ unsigned short w_year;
+ unsigned short w_month;
+ unsigned short w_day_of_week;
+ unsigned short w_day;
+ unsigned short w_hour;
+ unsigned short w_minute;
+ unsigned short w_second;
+ unsigned short w_milliseconds;
+};
+
+struct unistim_subchannel {
+ ast_mutex_t lock;
+ /*! SUBS_REAL or SUBS_THREEWAY */
+ unsigned int subtype;
+ /*! Asterisk channel used by the subchannel */
+ struct ast_channel *owner;
+ /*! Unistim line */
+ struct unistim_line *parent;
+ /*! RTP handle */
+ struct ast_rtp *rtp;
+ int alreadygone;
+ char ringvolume;
+ char ringstyle;
+};
+
+/*!
+ * \todo Convert to stringfields
+ */
+struct unistim_line {
+ ast_mutex_t lock;
+ /*! Like 200 */
+ char name[80];
+ /*! Like USTM/200\@black */
+ char fullname[80];
+ /*! pointer to our current connection, channel... */
+ struct unistim_subchannel *subs[MAX_SUBS];
+ /*! Extension where to start */
+ char exten[AST_MAX_EXTENSION];
+ /*! Context to start in */
+ char context[AST_MAX_EXTENSION];
+ /*! Language for asterisk sounds */
+ char language[MAX_LANGUAGE];
+ /*! CallerID Number */
+ char cid_num[AST_MAX_EXTENSION];
+ /*! Mailbox for MWI */
+ char mailbox[AST_MAX_EXTENSION];
+ /*! Used by MWI */
+ int lastmsgssent;
+ /*! Used by MWI */
+ time_t nextmsgcheck;
+ /*! MusicOnHold class */
+ char musicclass[MAX_MUSICCLASS];
+ /*! Call group */
+ unsigned int callgroup;
+ /*! Pickup group */
+ unsigned int pickupgroup;
+ /*! Account code (for billing) */
+ char accountcode[80];
+ /*! AMA flags (for billing) */
+ int amaflags;
+ /*! Codec supported */
+ int capability;
+ struct unistim_line *next;
+ struct unistim_device *parent;
+};
+
+/*!
+ * \brief A device containing one or more lines
+ */
+static struct unistim_device {
+ int receiver_state; /*!< state of the receiver (see ReceiverState) */
+ int size_phone_number; /*!< size of the phone number */
+ char phone_number[16]; /*!< the phone number entered by the user */
+ char redial_number[16]; /*!< the last phone number entered by the user */
+ int phone_current; /*!< Number of the current phone */
+ int pos_fav; /*!< Position of the displayed favorites (used for scrolling) */
+ char id[18]; /*!< mac address of the current phone in ascii */
+ char name[DEVICE_NAME_LEN]; /*!< name of the device */
+ int softkeylinepos; /*!< position of the line softkey (default 0) */
+ char softkeylabel[6][11]; /*!< soft key label */
+ char softkeynumber[6][16]; /*!< number dialed when the soft key is pressed */
+ char softkeyicon[6]; /*!< icon number */
+ char softkeydevice[6][16]; /*!< name of the device monitored */
+ struct unistim_device *sp[6]; /*!< pointer to the device monitored by this soft key */
+ char maintext0[25]; /*!< when the phone is idle, display this string on line 0 */
+ char maintext1[25]; /*!< when the phone is idle, display this string on line 1 */
+ char maintext2[25]; /*!< when the phone is idle, display this string on line 2 */
+ char titledefault[13]; /*!< title (text before date/time) */
+ char datetimeformat; /*!< format used for displaying time/date */
+ char contrast; /*!< contrast */
+ char country[3]; /*!< country used for dial tone frequency */
+ struct ind_tone_zone *tz; /*!< Tone zone for res_indications (ring, busy, congestion) */
+ char ringvolume; /*!< Ring volume */
+ char ringstyle; /*!< Ring melody */
+ int rtp_port; /*!< RTP port used by the phone */
+ int rtp_method; /*!< Select the unistim data used to establish a RTP session */
+ int status_method; /*!< Select the unistim packet used for sending status text */
+ char codec_number; /*!< The current codec used to make calls */
+ int missed_call; /*!< Number of call unanswered */
+ int callhistory; /*!< Allowed to record call history */
+ char lst_cid[TEXT_LENGTH_MAX]; /*!< Last callerID received */
+ char lst_cnm[TEXT_LENGTH_MAX]; /*!< Last callername recevied */
+ char call_forward[AST_MAX_EXTENSION]; /*!< Forward number */
+ int output; /*!< Handset, headphone or speaker */
+ int previous_output; /*!< Previous output */
+ int volume; /*!< Default volume */
+ int mute; /*!< Mute mode */
+ int moh; /*!< Music on hold in progress */
+ int nat; /*!< Used by the obscure ast_rtp_setnat */
+ enum autoprov_extn extension; /*!< See ifdef EXTENSION for valid values */
+ char extension_number[11]; /*!< Extension number entered by the user */
+ char to_delete; /*!< Used in reload */
+ time_t start_call_timestamp; /*!< timestamp for the length calculation of the call */
+ struct ast_silence_generator *silence_generator;
+ struct unistim_line *lines;
+ struct ast_ha *ha;
+ struct unistimsession *session;
+ struct unistim_device *next;
+} *devices = NULL;
+
+static struct unistimsession {
+ ast_mutex_t lock;
+ struct sockaddr_in sin; /*!< IP address of the phone */
+ struct sockaddr_in sout; /*!< IP address of server */
+ int timeout; /*!< time-out in ticks : resend packet if no ack was received before the timeout occured */
+ unsigned short seq_phone; /*!< sequence number for the next packet (when we receive a request) */
+ unsigned short seq_server; /*!< sequence number for the next packet (when we send a request) */
+ unsigned short last_seq_ack; /*!< sequence number of the last ACK received */
+ unsigned long tick_next_ping; /*!< time for the next ping */
+ int last_buf_available; /*!< number of a free slot */
+ int nb_retransmit; /*!< number of retransmition */
+ int state; /*!< state of the phone (see phone_state) */
+ int size_buff_entry; /*!< size of the buffer used to enter datas */
+ char buff_entry[16]; /*!< Buffer for temporary datas */
+ char macaddr[18]; /*!< mac adress of the phone (not always available) */
+ struct wsabuf wsabufsend[MAX_BUF_NUMBER]; /*!< Size of each paquet stored in the buffer array & pointer to this buffer */
+ unsigned char buf[MAX_BUF_NUMBER][MAX_BUF_SIZE]; /*!< Buffer array used to keep the lastest non-acked paquets */
+ struct unistim_device *device;
+ struct unistimsession *next;
+} *sessions = NULL;
+
+/*!
+ * \page Unistim datagram formats
+ *
+ * Format of datagrams :
+ * bytes 0 & 1 : ffff for discovery packet, 0000 for everything else
+ * byte 2 : sequence number (high part)
+ * byte 3 : sequence number (low part)
+ * byte 4 : 2 = ask question or send info, 1 = answer or ACK, 0 = retransmit request
+ * byte 5 : direction, 1 = server to phone, 2 = phone to server arguments
+ */
+
+const static unsigned char packet_rcv_discovery[] =
+ { 0xff, 0xff, 0xff, 0xff, 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0x9e, 0x03, 0x08 };
+static unsigned char packet_send_discovery_ack[] =
+ { 0x00, 0x00, /*Initial Seq (2 bytes) */ 0x00, 0x00, 0x00, 0x01 };
+
+const static unsigned char packet_recv_firm_version[] =
+ { 0x00, 0x00, 0x00, 0x13, 0x9a, 0x0a, 0x02 };
+const static unsigned char packet_recv_pressed_key[] =
+ { 0x00, 0x00, 0x00, 0x13, 0x99, 0x04, 0x00 };
+const static unsigned char packet_recv_pick_up[] =
+ { 0x00, 0x00, 0x00, 0x13, 0x99, 0x03, 0x04 };
+const static unsigned char packet_recv_hangup[] =
+ { 0x00, 0x00, 0x00, 0x13, 0x99, 0x03, 0x03 };
+const static unsigned char packet_recv_r2[] = { 0x00, 0x00, 0x00, 0x13, 0x96, 0x03, 0x03 };
+
+/*! TransportAdapter */
+const static unsigned char packet_recv_resume_connection_with_server[] =
+ { 0xff, 0xff, 0xff, 0xff, 0x9e, 0x03, 0x08 };
+const static unsigned char packet_recv_mac_addr[] =
+ { 0xff, 0xff, 0xff, 0xff, 0x9a, 0x0d, 0x07, 0x31, 0x38 /*MacAddr */ };
+
+const static unsigned char packet_send_date_time3[] =
+ { 0x11, 0x09, 0x02, 0x02, /*Month */ 0x05, /*Day */ 0x06, /*Hour */ 0x07,
+/*Minutes */ 0x08, 0x32
+};
+const static unsigned char packet_send_date_time[] =
+ { 0x11, 0x09, 0x02, 0x0a, /*Month */ 0x05, /*Day */ 0x06, /*Hour */ 0x07, /*Minutes */
+0x08, 0x32, 0x17, 0x04, 0x24, 0x07, 0x19,
+ 0x04, 0x07, 0x00, 0x19, 0x05, 0x09, 0x3e, 0x0f, 0x16, 0x05, 0x00, 0x80, 0x00, 0x1e,
+ 0x05, 0x12, 0x00, 0x78
+};
+
+const static unsigned char packet_send_no_ring[] =
+ { 0x16, 0x04, 0x1a, 0x00, 0x16, 0x04, 0x11, 0x00 };
+const static unsigned char packet_send_s4[] =
+ { 0x16, 0x04, 0x1a, 0x00, 0x16, 0x04, 0x11, 0x00, 0x16, 0x06, 0x32, 0xdf, 0x00, 0xff,
+0x16, 0x05, 0x1c, 0x00, 0x00, 0x17, 0x05,
+ 0x0b, 0x00, 0x00, 0x19, 0x04, 0x00, 0x00, 0x19, 0x04, 0x00, 0x08, 0x19, 0x04, 0x00,
+ 0x10, 0x19, 0x04, 0x00, 0x18, 0x16, 0x05,
+ 0x31, 0x00, 0x00, 0x16, 0x05, 0x04, 0x00, 0x00
+};
+const static unsigned char packet_send_call[] =
+ { 0x16, 0x04, 0x1a, 0x00, 0x16, 0x04, 0x11, 0x00, 0x16, 0x06, 0x32, 0xdf,
+ 0x00, 0xff, 0x16, 0x05, 0x1c, 0x00, 0x00, 0x16, 0x0a, 0x38, 0x00, 0x12, 0xca, 0x03,
+ 0xc0, 0xc3, 0xc5, 0x16, 0x16, 0x30, 0x00,
+ 0x00, /*codec */ 0x12, 0x12, /* frames per packet */ 0x01, 0x5c, 0x00, /*port RTP */
+ 0x0f, 0xa0, /* port RTCP */ 0x9c, 0x41,
+ /*port RTP */ 0x0f, 0xa0, /* port RTCP */ 0x9c, 0x41, /* IP Address */ 0x0a, 0x01,
+ 0x16, 0x66
+};
+const static unsigned char packet_send_stream_based_tone_off[] =
+ { 0x16, 0x05, 0x1c, 0x00, 0x00 };
+
+/* const static unsigned char packet_send_Mute[] = { 0x16, 0x05, 0x04, 0x00, 0x00 };
+const static unsigned char packet_send_CloseAudioStreamRX[] = { 0x16, 0x05, 0x31, 0x00, 0xff };
+const static unsigned char packet_send_CloseAudioStreamTX[] = { 0x16, 0x05, 0x31, 0xff, 0x00 };*/
+const static unsigned char packet_send_stream_based_tone_on[] =
+ { 0x16, 0x06, 0x1b, 0x00, 0x00, 0x05 };
+const static unsigned char packet_send_stream_based_tone_single_freq[] =
+ { 0x16, 0x06, 0x1d, 0x00, 0x01, 0xb8 };
+const static unsigned char packet_send_stream_based_tone_dial_freq[] =
+ { 0x16, 0x08, 0x1d, 0x00, 0x01, 0xb8, 0x01, 0x5e };
+const static unsigned char packet_send_select_output[] =
+ { 0x16, 0x06, 0x32, 0xc0, 0x01, 0x00 };
+const static unsigned char packet_send_ring[] =
+ { 0x16, 0x06, 0x32, 0xdf, 0x00, 0xff, 0x16, 0x05, 0x1c, 0x00, 0x00, 0x16,
+ 0x04, 0x1a, 0x01, 0x16, 0x05, 0x12, 0x13 /* Ring type 10 to 17 */ , 0x18, 0x16, 0x04, 0x18, /* volume 00, 10, 20... */
+ 0x20, 0x16, 0x04, 0x10, 0x00
+};
+const static unsigned char packet_send_end_call[] =
+ { 0x16, 0x06, 0x32, 0xdf, 0x00, 0xff, 0x16, 0x05, 0x31, 0x00, 0x00, 0x19, 0x04, 0x00,
+0x10, 0x19, 0x04, 0x00, 0x18, 0x16, 0x05,
+ 0x04, 0x00, 0x00, 0x16, 0x04, 0x37, 0x10
+};
+const static unsigned char packet_send_s9[] =
+ { 0x16, 0x06, 0x32, 0xdf, 0x00, 0xff, 0x19, 0x04, 0x00, 0x10, 0x16, 0x05, 0x1c, 0x00,
+0x00 };
+const static unsigned char packet_send_rtp_packet_size[] =
+ { 0x16, 0x08, 0x38, 0x00, 0x00, 0xe0, 0x00, 0xa0 };
+const static unsigned char packet_send_jitter_buffer_conf[] =
+ { 0x16, 0x0e, 0x3a, 0x00, /* jitter */ 0x02, /* high water mark */ 0x04, 0x00, 0x00,
+/* early packet resync 2 bytes */ 0x3e, 0x80,
+ 0x00, 0x00, /* late packet resync 2 bytes */ 0x3e, 0x80
+};
+
+/* Duration in ms div 2 (0x20 = 64ms, 0x08 = 16ms)
+static unsigned char packet_send_StreamBasedToneCad[] =
+ { 0x16, 0x0a, 0x1e, 0x00, duration on 0x0a, duration off 0x0d, duration on 0x0a, duration off 0x0d, duration on 0x0a, duration off 0x2b }; */
+const static unsigned char packet_send_open_audio_stream_rx[] =
+ { 0x16, 0x1a, 0x30, 0x00, 0xff, /* Codec */ 0x00, 0x00, 0x01, 0x00, 0xb8, 0xb8, 0x0e,
+0x0e, 0x01, /* Port */ 0x14, 0x50, 0x00,
+ 0x00, /* Port */ 0x14, 0x50, 0x00, 0x00, /* Dest IP */ 0x0a, 0x93, 0x69, 0x05
+};
+const static unsigned char packet_send_open_audio_stream_tx[] =
+ { 0x16, 0x1a, 0x30, 0xff, 0x00, 0x00, /* Codec */ 0x00, 0x01, 0x00, 0xb8, 0xb8, 0x0e,
+0x0e, 0x01, /* Local port */ 0x14, 0x50,
+ 0x00, 0x00, /* Rmt Port */ 0x14, 0x50, 0x00, 0x00, /* Dest IP */ 0x0a, 0x93, 0x69, 0x05
+};
+
+const static unsigned char packet_send_open_audio_stream_rx3[] =
+ { 0x16, 0x1a, 0x30, 0x00, 0xff, /* Codec */ 0x00, 0x00, 0x02, 0x01, 0xb8, 0xb8, 0x06,
+0x06, 0x81, /* RTP Port */ 0x14, 0x50,
+/* RTCP Port */ 0x14,
+ 0x51, /* RTP Port */ 0x14, 0x50, /* RTCP Port */ 0x00, 0x00, /* Dest IP */ 0x0a, 0x93,
+ 0x69, 0x05
+};
+const static unsigned char packet_send_open_audio_stream_tx3[] =
+ { 0x16, 0x1a, 0x30, 0xff, 0x00, 0x00, /* Codec */ 0x00, 0x02, 0x01, 0xb8, 0xb8, 0x06,
+0x06, 0x81, /* RTP Local port */ 0x14, 0x50,
+ /* RTCP Port */ 0x00, 0x00, /* RTP Rmt Port */ 0x14, 0x50, /* RTCP Port */ 0x00, 0x00,
+ /* Dest IP */ 0x0a, 0x93, 0x69, 0x05
+};
+
+const static unsigned char packet_send_arrow[] = { 0x17, 0x04, 0x04, 0x00 };
+const static unsigned char packet_send_blink_cursor[] = { 0x17, 0x04, 0x10, 0x86 };
+const static unsigned char packet_send_date_time2[] = { 0x17, 0x04, 0x17, 0x3d, 0x11, 0x09, 0x02, 0x0a, /*Month */ 0x05, /*Day */
+ 0x06, /*Hour */ 0x07, /*Minutes */ 0x08, 0x32
+};
+const static unsigned char packet_send_Contrast[] =
+ { 0x17, 0x04, 0x24, /*Contrast */ 0x08 };
+const static unsigned char packet_send_StartTimer[] =
+ { 0x17, 0x05, 0x0b, 0x05, 0x00, 0x17, 0x08, 0x16, /* Text */ 0x44, 0x75, 0x72, 0xe9,
+0x65 };
+const static unsigned char packet_send_stop_timer[] = { 0x17, 0x05, 0x0b, 0x02, 0x00 };
+const static unsigned char packet_send_icon[] = { 0x17, 0x05, 0x14, /*pos */ 0x00, /*icon */ 0x25 }; /* display an icon in front of the text zone */
+const static unsigned char packet_send_S7[] = { 0x17, 0x06, 0x0f, 0x30, 0x07, 0x07 };
+const static unsigned char packet_send_set_pos_cursor[] =
+ { 0x17, 0x06, 0x10, 0x81, 0x04, /*pos */ 0x20 };
+
+/*static unsigned char packet_send_MonthLabelsDownload[] =
+ { 0x17, 0x0a, 0x15, Month (3 char) 0x46, 0x65, 0x62, 0x4d, 0xe4, 0x72, 0x20 }; */
+const static unsigned char packet_send_favorite[] =
+ { 0x17, 0x0f, 0x19, 0x10, /*pos */ 0x01, /*name */ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, /*end_name */ 0x19,
+ 0x05, 0x0f, /*pos */ 0x01, /*icone */ 0x00
+};
+const static unsigned char packet_send_title[] =
+ { 0x17, 0x10, 0x19, 0x02, /*text */ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20 /*end_text */ };
+const static unsigned char packet_send_text[] =
+ { 0x17, 0x1e, 0x1b, 0x04, /*pos */ 0x00, /*inverse */ 0x25, /*text */ 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ /*end_text */ 0x17, 0x04, 0x10, 0x87
+};
+const static unsigned char packet_send_status[] =
+ { 0x17, 0x20, 0x19, 0x08, /*text */ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 /*end_text */
+};
+const static unsigned char packet_send_status2[] =
+ { 0x17, 0x0b, 0x19, /* pos [08|28|48|68] */ 0x00, /* text */ 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20 /* end_text */ };
+
+const static unsigned char packet_send_led_update[] = { 0x19, 0x04, 0x00, 0x00 };
+
+const static unsigned char packet_send_query_basic_manager_04[] = { 0x1a, 0x04, 0x01, 0x04 };
+const static unsigned char packet_send_query_mac_address[] = { 0x1a, 0x04, 0x01, 0x08 };
+const static unsigned char packet_send_query_basic_manager_10[] = { 0x1a, 0x04, 0x01, 0x10 };
+const static unsigned char packet_send_S1[] = { 0x1a, 0x07, 0x07, 0x00, 0x00, 0x00, 0x13 };
+
+static unsigned char packet_send_ping[] =
+ { 0x1e, 0x05, 0x12, 0x00, /*Watchdog timer */ 0x78 };
+
+#define BUFFSEND unsigned char buffsend[64] = { 0x00, 0x00, 0xaa, 0xbb, 0x02, 0x01 }
+
+const static char tdesc[] = "UNISTIM Channel Driver";
+const static char type[] = "USTM";
+
+/*! Protos */
+static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state);
+static int load_module(void);
+static int reload(void);
+static int unload_module(void);
+static int reload_config(void);
+static void show_main_page(struct unistimsession *pte);
+static struct ast_channel *unistim_request(const char *type, int format,
+ void *data, int *cause);
+static int unistim_call(struct ast_channel *ast, char *dest, int timeout);
+static int unistim_hangup(struct ast_channel *ast);
+static int unistim_answer(struct ast_channel *ast);
+static struct ast_frame *unistim_read(struct ast_channel *ast);
+static int unistim_write(struct ast_channel *ast, struct ast_frame *frame);
+static int unistim_indicate(struct ast_channel *ast, int ind, const void *data,
+ size_t datalen);
+static int unistim_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int unistim_senddigit_begin(struct ast_channel *ast, char digit);
+static int unistim_senddigit_end(struct ast_channel *ast, char digit,
+ unsigned int duration);
+static int unistim_sendtext(struct ast_channel *ast, const char *text);
+
+static int write_entry_history(struct unistimsession *pte, FILE * f, char c,
+ char *line1);
+static void change_callerid(struct unistimsession *pte, int type, char *callerid);
+
+static const struct ast_channel_tech unistim_tech = {
+ .type = type,
+ .description = tdesc,
+ .capabilities = CAPABILITY,
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
+ .requester = unistim_request,
+ .call = unistim_call,
+ .hangup = unistim_hangup,
+ .answer = unistim_answer,
+ .read = unistim_read,
+ .write = unistim_write,
+ .indicate = unistim_indicate,
+ .fixup = unistim_fixup,
+ .send_digit_begin = unistim_senddigit_begin,
+ .send_digit_end = unistim_senddigit_end,
+ .send_text = unistim_sendtext,
+/* .bridge = ast_rtp_bridge, */
+};
+
+static void display_last_error(const char *sz_msg)
+{
+ time_t cur_time;
+
+ time(&cur_time);
+
+ /* Display the error message */
+ ast_log(LOG_WARNING, "%s %s : (%u) %s\n", ctime(&cur_time), sz_msg, errno,
+ strerror(errno));
+}
+
+static unsigned int get_tick_count(void)
+{
+ struct timeval tv = ast_tvnow();
+
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
+
+/* Send data to a phone without retransmit nor buffering */
+static void send_raw_client(int size, unsigned char *data, struct sockaddr_in *addr_to,
+ const struct sockaddr_in *addr_ourip)
+{
+#ifdef HAVE_PKTINFO
+ struct iovec msg_iov;
+ struct msghdr msg;
+ char buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
+ struct cmsghdr *ip_msg = (struct cmsghdr *) buffer;
+ struct in_pktinfo *pki = (struct in_pktinfo *) CMSG_DATA(ip_msg);
+
+ msg_iov.iov_base = data;
+ msg_iov.iov_len = size;
+
+ msg.msg_name = addr_to; /* optional address */
+ msg.msg_namelen = sizeof(struct sockaddr_in); /* size of address */
+ msg.msg_iov = &msg_iov; /* scatter/gather array */
+ msg.msg_iovlen = 1; /* # elements in msg_iov */
+ msg.msg_control = ip_msg; /* ancillary data */
+ msg.msg_controllen = sizeof(buffer); /* ancillary data buffer len */
+ msg.msg_flags = 0; /* flags on received message */
+
+ ip_msg->cmsg_len = CMSG_LEN(sizeof(*pki));
+ ip_msg->cmsg_level = IPPROTO_IP;
+ ip_msg->cmsg_type = IP_PKTINFO;
+ pki->ipi_ifindex = 0; /* Interface index, 0 = use interface specified in routing table */
+ pki->ipi_spec_dst.s_addr = addr_ourip->sin_addr.s_addr; /* Local address */
+ /* pki->ipi_addr = ; Header Destination address - ignored by kernel */
+
+#ifdef DUMP_PACKET
+ if (unistimdebug) {
+ int tmp;
+ char iabuf[INET_ADDRSTRLEN];
+ char iabuf2[INET_ADDRSTRLEN];
+ ast_verbose("\n**> From %s sending %d bytes to %s ***\n",
+ ast_inet_ntoa(addr_ourip->sin_addr), (int) size,
+ ast_inet_ntoa(addr_to->sin_addr));
+ for (tmp = 0; tmp < size; tmp++)
+ ast_verbose("%.2x ", (unsigned char) data[tmp]);
+ ast_verbose("\n******************************************\n");
+
+ }
+#endif
+
+ if (sendmsg(unistimsock, &msg, 0) == -1)
+ display_last_error("Error sending datas");
+#else
+ if (sendto(unistimsock, data, size, 0, (struct sockaddr *) addr_to, sizeof(*addr_to))
+ == -1)
+ display_last_error("Error sending datas");
+#endif
+}
+
+static void send_client(int size, const unsigned char *data, struct unistimsession *pte)
+{
+ unsigned int tick;
+ int buf_pos;
+ unsigned short *sdata = (unsigned short *) data;
+
+ ast_mutex_lock(&pte->lock);
+ buf_pos = pte->last_buf_available;
+
+ if (buf_pos >= MAX_BUF_NUMBER) {
+ ast_log(LOG_WARNING, "Error : send queue overflow\n");
+ ast_mutex_unlock(&pte->lock);
+ return;
+ }
+ sdata[1] = ntohs(++(pte->seq_server));
+ pte->wsabufsend[buf_pos].len = size;
+ memcpy(pte->wsabufsend[buf_pos].buf, data, size);
+
+ tick = get_tick_count();
+ pte->timeout = tick + RETRANSMIT_TIMER;
+
+/*#ifdef DUMP_PACKET */
+ if ((unistimdebug) && (option_verbose > 5)) {
+ ast_verbose("Sending datas with seq #0x%.4x Using slot #%d :\n", pte->seq_server,
+ buf_pos);
+ }
+/*#endif */
+ send_raw_client(pte->wsabufsend[buf_pos].len, pte->wsabufsend[buf_pos].buf, &(pte->sin),
+ &(pte->sout));
+ pte->last_buf_available++;
+ ast_mutex_unlock(&pte->lock);
+}
+
+static void send_ping(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if ((unistimdebug) && (option_verbose > 5))
+ ast_verbose("Sending ping\n");
+ pte->tick_next_ping = get_tick_count() + unistim_keepalive;
+ memcpy(buffsend + SIZE_HEADER, packet_send_ping, sizeof(packet_send_ping));
+ send_client(SIZE_HEADER + sizeof(packet_send_ping), buffsend, pte);
+}
+
+static int get_to_address(int fd, struct sockaddr_in *toAddr)
+{
+#ifdef HAVE_PKTINFO
+ int err;
+ struct msghdr msg;
+ struct {
+ struct cmsghdr cm;
+ int len;
+ struct in_addr address;
+ } ip_msg;
+
+ /* Zero out the structures before we use them */
+ /* This sets several key values to NULL */
+ memset(&msg, 0, sizeof(msg));
+ memset(&ip_msg, 0, sizeof(ip_msg));
+
+ /* Initialize the message structure */
+ msg.msg_control = &ip_msg;
+ msg.msg_controllen = sizeof(ip_msg);
+ /* Get info about the incoming packet */
+ err = recvmsg(fd, &msg, MSG_PEEK);
+ if (err == -1)
+ ast_log(LOG_WARNING, "recvmsg returned an error: %s\n", strerror(errno));
+ memcpy(&toAddr->sin_addr, &ip_msg.address, sizeof(struct in_addr));
+ return err;
+#else
+ memcpy(&toAddr, &public_ip, sizeof(&toAddr));
+ return 0;
+#endif
+}
+
+/* Allocate memory & initialize structures for a new phone */
+/* addr_from : ip address of the phone */
+static struct unistimsession *create_client(const struct sockaddr_in *addr_from)
+{
+ int tmp;
+ struct unistimsession *s;
+
+ if (!(s = ast_calloc(1, sizeof(*s))))
+ return NULL;
+
+ memcpy(&s->sin, addr_from, sizeof(struct sockaddr_in));
+ get_to_address(unistimsock, &s->sout);
+ if (unistimdebug) {
+ ast_verbose
+ ("Creating a new entry for the phone from %s received via server ip %s\n",
+ ast_inet_ntoa(addr_from->sin_addr), ast_inet_ntoa(s->sout.sin_addr));
+ }
+ ast_mutex_init(&s->lock);
+ ast_mutex_lock(&sessionlock);
+ s->next = sessions;
+ sessions = s;
+
+ s->timeout = get_tick_count() + RETRANSMIT_TIMER;
+ s->seq_phone = (short) 0x0000;
+ s->seq_server = (short) 0x0000;
+ s->last_seq_ack = (short) 0x000;
+ s->last_buf_available = 0;
+ s->nb_retransmit = 0;
+ s->state = STATE_INIT;
+ s->tick_next_ping = get_tick_count() + unistim_keepalive;
+ /* Initialize struct wsabuf */
+ for (tmp = 0; tmp < MAX_BUF_NUMBER; tmp++) {
+ s->wsabufsend[tmp].buf = s->buf[tmp];
+ }
+ ast_mutex_unlock(&sessionlock);
+ return s;
+}
+
+static void send_end_call(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending end call\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_end_call, sizeof(packet_send_end_call));
+ send_client(SIZE_HEADER + sizeof(packet_send_end_call), buffsend, pte);
+}
+
+static void set_ping_timer(struct unistimsession *pte)
+{
+ unsigned int tick = 0; /* XXX what is this for, anyways */
+
+ pte->timeout = pte->tick_next_ping;
+ DEBUG_TIMER("tick = %u next ping at %u tick\n", tick, pte->timeout);
+ return;
+}
+
+/* Checking if our send queue is empty,
+ * if true, setting up a timer for keepalive */
+static void check_send_queue(struct unistimsession *pte)
+{
+ /* Check if our send queue contained only one element */
+ if (pte->last_buf_available == 1) {
+ if ((unistimdebug) && (option_verbose > 5))
+ ast_verbose("Our single packet was ACKed.\n");
+ pte->last_buf_available--;
+ set_ping_timer(pte);
+ return;
+ }
+ /* Check if this ACK catch up our latest packet */
+ else if (pte->last_seq_ack + 1 == pte->seq_server + 1) {
+ if ((unistimdebug) && (option_verbose > 5))
+ ast_verbose("Our send queue is completely ACKed.\n");
+ pte->last_buf_available = 0; /* Purge the send queue */
+ set_ping_timer(pte);
+ return;
+ }
+ if ((unistimdebug) && (option_verbose > 5))
+ ast_verbose("We still have packets in our send queue\n");
+ return;
+}
+
+static void send_start_timer(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending start timer\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_StartTimer, sizeof(packet_send_StartTimer));
+ send_client(SIZE_HEADER + sizeof(packet_send_StartTimer), buffsend, pte);
+}
+
+static void send_stop_timer(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending stop timer\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_stop_timer, sizeof(packet_send_stop_timer));
+ send_client(SIZE_HEADER + sizeof(packet_send_stop_timer), buffsend, pte);
+}
+
+static void Sendicon(unsigned char pos, unsigned char status, struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending icon pos %d with status 0x%.2x\n", pos, status);
+ memcpy(buffsend + SIZE_HEADER, packet_send_icon, sizeof(packet_send_icon));
+ buffsend[9] = pos;
+ buffsend[10] = status;
+ send_client(SIZE_HEADER + sizeof(packet_send_icon), buffsend, pte);
+}
+
+static void send_tone(struct unistimsession *pte, uint16_t tone1, uint16_t tone2)
+{
+ BUFFSEND;
+ if (!tone1) {
+ if (unistimdebug)
+ ast_verbose("Sending Stream Based Tone Off\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_stream_based_tone_off,
+ sizeof(packet_send_stream_based_tone_off));
+ send_client(SIZE_HEADER + sizeof(packet_send_stream_based_tone_off), buffsend, pte);
+ return;
+ }
+ /* Since most of the world use a continuous tone, it's useless
+ if (unistimdebug)
+ ast_verbose ("Sending Stream Based Tone Cadence Download\n");
+ memcpy (buffsend + SIZE_HEADER, packet_send_StreamBasedToneCad, sizeof (packet_send_StreamBasedToneCad));
+ send_client (SIZE_HEADER + sizeof (packet_send_StreamBasedToneCad), buffsend, pte); */
+ if (unistimdebug)
+ ast_verbose("Sending Stream Based Tone Frequency Component List Download %d %d\n",
+ tone1, tone2);
+ tone1 *= 8;
+ if (!tone2) {
+ memcpy(buffsend + SIZE_HEADER, packet_send_stream_based_tone_single_freq,
+ sizeof(packet_send_stream_based_tone_single_freq));
+ buffsend[10] = (tone1 & 0xff00) >> 8;
+ buffsend[11] = (tone1 & 0x00ff);
+ send_client(SIZE_HEADER + sizeof(packet_send_stream_based_tone_single_freq), buffsend,
+ pte);
+ } else {
+ tone2 *= 8;
+ memcpy(buffsend + SIZE_HEADER, packet_send_stream_based_tone_dial_freq,
+ sizeof(packet_send_stream_based_tone_dial_freq));
+ buffsend[10] = (tone1 & 0xff00) >> 8;
+ buffsend[11] = (tone1 & 0x00ff);
+ buffsend[12] = (tone2 & 0xff00) >> 8;
+ buffsend[13] = (tone2 & 0x00ff);
+ send_client(SIZE_HEADER + sizeof(packet_send_stream_based_tone_dial_freq), buffsend,
+ pte);
+ }
+
+ if (unistimdebug)
+ ast_verbose("Sending Stream Based Tone On\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_stream_based_tone_on,
+ sizeof(packet_send_stream_based_tone_on));
+ send_client(SIZE_HEADER + sizeof(packet_send_stream_based_tone_on), buffsend, pte);
+}
+
+/* Positions for favorites
+ |--------------------|
+ | 5 2 |
+ | 4 1 |
+ | 3 0 |
+*/
+
+/* status (icons) : 00 = nothing, 2x/3x = see parser.h, 4x/5x = blink fast, 6x/7x = blink slow */
+static void
+send_favorite(unsigned char pos, unsigned char status, struct unistimsession *pte,
+ const char *text)
+{
+ BUFFSEND;
+ int i;
+
+ if (unistimdebug)
+ ast_verbose("Sending favorite pos %d with status 0x%.2x\n", pos, status);
+ memcpy(buffsend + SIZE_HEADER, packet_send_favorite, sizeof(packet_send_favorite));
+ buffsend[10] = pos;
+ buffsend[24] = pos;
+ buffsend[25] = status;
+ i = strlen(text);
+ if (i > FAV_MAX_LENGTH)
+ i = FAV_MAX_LENGTH;
+ memcpy(buffsend + FAV_MAX_LENGTH + 1, text, i);
+ send_client(SIZE_HEADER + sizeof(packet_send_favorite), buffsend, pte);
+}
+
+static void refresh_all_favorite(struct unistimsession *pte)
+{
+ int i = 0;
+
+ if (unistimdebug)
+ ast_verbose("Refreshing all favorite\n");
+ for (i = 0; i < 6; i++) {
+ if ((pte->device->softkeyicon[i] <= FAV_ICON_HEADPHONES_ONHOLD) &&
+ (pte->device->softkeylinepos != i))
+ send_favorite((unsigned char) i, pte->device->softkeyicon[i] + 1, pte,
+ pte->device->softkeylabel[i]);
+ else
+ send_favorite((unsigned char) i, pte->device->softkeyicon[i], pte,
+ pte->device->softkeylabel[i]);
+
+ }
+}
+
+/* Change the status for this phone (pte) and update for each phones where pte is bookmarked
+ * use FAV_ICON_*_BLACK constant in status parameters */
+static void change_favorite_icon(struct unistimsession *pte, unsigned char status)
+{
+ struct unistim_device *d = devices;
+ int i;
+ /* Update the current phone */
+ if (pte->state != STATE_CLEANING)
+ send_favorite(pte->device->softkeylinepos, status, pte,
+ pte->device->softkeylabel[pte->device->softkeylinepos]);
+ /* Notify other phones if we're in their bookmark */
+ while (d) {
+ for (i = 0; i < 6; i++) {
+ if (d->sp[i] == pte->device) { /* It's us ? */
+ if (d->softkeyicon[i] != status) { /* Avoid resending the same icon */
+ d->softkeyicon[i] = status;
+ if (d->session)
+ send_favorite(i, status + 1, d->session, d->softkeylabel[i]);
+ }
+ }
+ }
+ d = d->next;
+ }
+}
+
+static int RegisterExtension(const struct unistimsession *pte)
+{
+ if (unistimdebug)
+ ast_verbose("Trying to register extension '%s' into context '%s' to %s\n",
+ pte->device->extension_number, pte->device->lines->context,
+ pte->device->lines->fullname);
+ return ast_add_extension(pte->device->lines->context, 0,
+ pte->device->extension_number, 1, NULL, NULL, "Dial",
+ pte->device->lines->fullname, 0, "Unistim");
+}
+
+static int UnregisterExtension(const struct unistimsession *pte)
+{
+ if (unistimdebug)
+ ast_verbose("Trying to unregister extension '%s' context '%s'\n",
+ pte->device->extension_number, pte->device->lines->context);
+ return ast_context_remove_extension(pte->device->lines->context,
+ pte->device->extension_number, 1, "Unistim");
+}
+
+/* Free memory allocated for a phone */
+static void close_client(struct unistimsession *s)
+{
+ struct unistim_subchannel *sub;
+ struct unistimsession *cur, *prev = NULL;
+ ast_mutex_lock(&sessionlock);
+ cur = sessions;
+ /* Looking for the session in the linked chain */
+ while (cur) {
+ if (cur == s)
+ break;
+ prev = cur;
+ cur = cur->next;
+ }
+ if (cur) { /* Session found ? */
+ if (cur->device) { /* This session was registred ? */
+ s->state = STATE_CLEANING;
+ if (unistimdebug)
+ ast_verbose("close_client session %p device %p lines %p sub %p\n",
+ s, s->device, s->device->lines,
+ s->device->lines->subs[SUB_REAL]);
+ change_favorite_icon(s, FAV_ICON_NONE);
+ sub = s->device->lines->subs[SUB_REAL];
+ if (sub) {
+ if (sub->owner) { /* Call in progress ? */
+ if (unistimdebug)
+ ast_verbose("Aborting call\n");
+ ast_queue_hangup(sub->owner);
+ }
+ } else
+ ast_log(LOG_WARNING, "Freeing a client with no subchannel !\n");
+ if (!ast_strlen_zero(s->device->extension_number))
+ UnregisterExtension(s);
+ cur->device->session = NULL;
+ } else {
+ if (unistimdebug)
+ ast_verbose("Freeing an unregistered client\n");
+ }
+ if (prev)
+ prev->next = cur->next;
+ else
+ sessions = cur->next;
+ ast_mutex_destroy(&s->lock);
+ ast_free(s);
+ } else
+ ast_log(LOG_WARNING, "Trying to delete non-existant session %p?\n", s);
+ ast_mutex_unlock(&sessionlock);
+ return;
+}
+
+/* Return 1 if the session chained link was modified */
+static int send_retransmit(struct unistimsession *pte)
+{
+ int i;
+
+ ast_mutex_lock(&pte->lock);
+ if (++pte->nb_retransmit >= NB_MAX_RETRANSMIT) {
+ if (unistimdebug)
+ ast_verbose("Too many retransmit - freeing client\n");
+ ast_mutex_unlock(&pte->lock);
+ close_client(pte);
+ return 1;
+ }
+ pte->timeout = get_tick_count() + RETRANSMIT_TIMER;
+
+ for (i = pte->last_buf_available - (pte->seq_server - pte->last_seq_ack);
+ i < pte->last_buf_available; i++) {
+ if (i < 0) {
+ ast_log(LOG_WARNING,
+ "Asked to retransmit an ACKed slot ! last_buf_available=%d, seq_server = #0x%.4x last_seq_ack = #0x%.4x\n",
+ pte->last_buf_available, pte->seq_server, pte->last_seq_ack);
+ continue;
+ }
+
+ if (unistimdebug) {
+ unsigned short *sbuf = (unsigned short *) pte->wsabufsend[i].buf;
+ unsigned short seq;
+
+ seq = ntohs(sbuf[1]);
+ ast_verbose("Retransmit slot #%d (seq=#0x%.4x), last ack was #0x%.4x\n", i,
+ seq, pte->last_seq_ack);
+ }
+ send_raw_client(pte->wsabufsend[i].len, pte->wsabufsend[i].buf, &pte->sin,
+ &pte->sout);
+ }
+ ast_mutex_unlock(&pte->lock);
+ return 0;
+}
+
+/* inverse : TEXT_INVERSE : yes, TEXT_NORMAL : no */
+static void
+send_text(unsigned char pos, unsigned char inverse, struct unistimsession *pte,
+ const char *text)
+{
+ int i;
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending text at pos %d, inverse flag %d\n", pos, inverse);
+ memcpy(buffsend + SIZE_HEADER, packet_send_text, sizeof(packet_send_text));
+ buffsend[10] = pos;
+ buffsend[11] = inverse;
+ i = strlen(text);
+ if (i > TEXT_LENGTH_MAX)
+ i = TEXT_LENGTH_MAX;
+ memcpy(buffsend + 12, text, i);
+ send_client(SIZE_HEADER + sizeof(packet_send_text), buffsend, pte);
+}
+
+static void send_text_status(struct unistimsession *pte, const char *text)
+{
+ BUFFSEND;
+ int i;
+ if (unistimdebug)
+ ast_verbose("Sending status text\n");
+ if (pte->device) {
+ if (pte->device->status_method == 1) { /* For new firmware and i2050 soft phone */
+ int n = strlen(text);
+ /* Must send individual button separately */
+ int j;
+ for (i = 0, j = 0; i < 4; i++, j += 7) {
+ int pos = 0x08 + (i * 0x20);
+ memcpy(buffsend + SIZE_HEADER, packet_send_status2,
+ sizeof(packet_send_status2));
+
+ buffsend[9] = pos;
+ memcpy(buffsend + 10, (j < n) ? (text + j) : " ", 7);
+ send_client(SIZE_HEADER + sizeof(packet_send_status2), buffsend, pte);
+ }
+ return;
+ }
+ }
+
+
+ memcpy(buffsend + SIZE_HEADER, packet_send_status, sizeof(packet_send_status));
+ i = strlen(text);
+ if (i > STATUS_LENGTH_MAX)
+ i = STATUS_LENGTH_MAX;
+ memcpy(buffsend + 10, text, i);
+ send_client(SIZE_HEADER + sizeof(packet_send_status), buffsend, pte);
+
+}
+
+/* led values in hexa : 0 = bar off, 1 = bar on, 2 = bar 1s on/1s off, 3 = bar 2.5s on/0.5s off
+ * 4 = bar 0.6s on/0.3s off, 5 = bar 0.5s on/0.5s off, 6 = bar 2s on/0.5s off
+ * 7 = bar off, 8 = speaker off, 9 = speaker on, 10 = headphone off, 11 = headphone on
+ * 18 = mute off, 19 mute on */
+static void send_led_update(struct unistimsession *pte, unsigned char led)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending led_update (%x)\n", led);
+ memcpy(buffsend + SIZE_HEADER, packet_send_led_update, sizeof(packet_send_led_update));
+ buffsend[9] = led;
+ send_client(SIZE_HEADER + sizeof(packet_send_led_update), buffsend, pte);
+}
+
+/* output = OUTPUT_HANDSET, OUTPUT_HEADPHONE or OUTPUT_SPEAKER
+ * volume = VOLUME_LOW, VOLUME_NORMAL, VOLUME_INSANELY_LOUD
+ * mute = MUTE_OFF, MUTE_ON */
+static void
+send_select_output(struct unistimsession *pte, unsigned char output, unsigned char volume,
+ unsigned char mute)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending select output packet output=%x volume=%x mute=%x\n", output,
+ volume, mute);
+ memcpy(buffsend + SIZE_HEADER, packet_send_select_output,
+ sizeof(packet_send_select_output));
+ buffsend[9] = output;
+ if (output == OUTPUT_SPEAKER)
+ volume = VOLUME_LOW_SPEAKER;
+ else
+ volume = VOLUME_LOW;
+ buffsend[10] = volume;
+ if (mute == MUTE_ON_DISCRET)
+ buffsend[11] = MUTE_ON;
+ else
+ buffsend[11] = mute;
+ send_client(SIZE_HEADER + sizeof(packet_send_select_output), buffsend, pte);
+ if (mute == MUTE_OFF)
+ send_led_update(pte, 0x18);
+ else if (mute == MUTE_ON)
+ send_led_update(pte, 0x19);
+ pte->device->mute = mute;
+ if (output == OUTPUT_HANDSET) {
+ if (mute == MUTE_ON)
+ change_favorite_icon(pte, FAV_ICON_ONHOLD_BLACK);
+ else
+ change_favorite_icon(pte, FAV_ICON_OFFHOOK_BLACK);
+ send_led_update(pte, 0x08);
+ send_led_update(pte, 0x10);
+ } else if (output == OUTPUT_HEADPHONE) {
+ if (mute == MUTE_ON)
+ change_favorite_icon(pte, FAV_ICON_HEADPHONES_ONHOLD);
+ else
+ change_favorite_icon(pte, FAV_ICON_HEADPHONES);
+ send_led_update(pte, 0x08);
+ send_led_update(pte, 0x11);
+ } else if (output == OUTPUT_SPEAKER) {
+ send_led_update(pte, 0x10);
+ send_led_update(pte, 0x09);
+ if (pte->device->receiver_state == STATE_OFFHOOK) {
+ if (mute == MUTE_ON)
+ change_favorite_icon(pte, FAV_ICON_SPEAKER_ONHOLD_BLACK);
+ else
+ change_favorite_icon(pte, FAV_ICON_SPEAKER_ONHOOK_BLACK);
+ } else {
+ if (mute == MUTE_ON)
+ change_favorite_icon(pte, FAV_ICON_SPEAKER_ONHOLD_BLACK);
+ else
+ change_favorite_icon(pte, FAV_ICON_SPEAKER_OFFHOOK_BLACK);
+ }
+ } else
+ ast_log(LOG_WARNING, "Invalid ouput (%d)\n", output);
+ if (output != pte->device->output)
+ pte->device->previous_output = pte->device->output;
+ pte->device->output = output;
+}
+
+static void send_ring(struct unistimsession *pte, char volume, char style)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending ring packet\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_ring, sizeof(packet_send_ring));
+ buffsend[24] = style + 0x10;
+ buffsend[29] = volume * 0x10;
+ send_client(SIZE_HEADER + sizeof(packet_send_ring), buffsend, pte);
+}
+
+static void send_no_ring(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending no ring packet\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_no_ring, sizeof(packet_send_no_ring));
+ send_client(SIZE_HEADER + sizeof(packet_send_no_ring), buffsend, pte);
+}
+
+static void send_texttitle(struct unistimsession *pte, const char *text)
+{
+ BUFFSEND;
+ int i;
+ if (unistimdebug)
+ ast_verbose("Sending title text\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_title, sizeof(packet_send_title));
+ i = strlen(text);
+ if (i > 12)
+ i = 12;
+ memcpy(buffsend + 10, text, i);
+ send_client(SIZE_HEADER + sizeof(packet_send_title), buffsend, pte);
+
+}
+
+static void send_date_time(struct unistimsession *pte)
+{
+ BUFFSEND;
+ struct timeval tv = ast_tvnow();
+ struct ast_tm atm = { 0, };
+
+ if (unistimdebug)
+ ast_verbose("Sending Time & Date\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_date_time, sizeof(packet_send_date_time));
+ ast_localtime(&tv, &atm, NULL);
+ buffsend[10] = (unsigned char) atm.tm_mon + 1;
+ buffsend[11] = (unsigned char) atm.tm_mday;
+ buffsend[12] = (unsigned char) atm.tm_hour;
+ buffsend[13] = (unsigned char) atm.tm_min;
+ send_client(SIZE_HEADER + sizeof(packet_send_date_time), buffsend, pte);
+}
+
+static void send_date_time2(struct unistimsession *pte)
+{
+ BUFFSEND;
+ struct timeval tv = ast_tvnow();
+ struct ast_tm atm = { 0, };
+
+ if (unistimdebug)
+ ast_verbose("Sending Time & Date #2\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_date_time2, sizeof(packet_send_date_time2));
+ ast_localtime(&tv, &atm, NULL);
+ if (pte->device)
+ buffsend[9] = pte->device->datetimeformat;
+ else
+ buffsend[9] = 61;
+ buffsend[14] = (unsigned char) atm.tm_mon + 1;
+ buffsend[15] = (unsigned char) atm.tm_mday;
+ buffsend[16] = (unsigned char) atm.tm_hour;
+ buffsend[17] = (unsigned char) atm.tm_min;
+ send_client(SIZE_HEADER + sizeof(packet_send_date_time2), buffsend, pte);
+}
+
+static void send_date_time3(struct unistimsession *pte)
+{
+ BUFFSEND;
+ struct timeval tv = ast_tvnow();
+ struct ast_tm atm = { 0, };
+
+ if (unistimdebug)
+ ast_verbose("Sending Time & Date #3\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_date_time3, sizeof(packet_send_date_time3));
+ ast_localtime(&tv, &atm, NULL);
+ buffsend[10] = (unsigned char) atm.tm_mon + 1;
+ buffsend[11] = (unsigned char) atm.tm_mday;
+ buffsend[12] = (unsigned char) atm.tm_hour;
+ buffsend[13] = (unsigned char) atm.tm_min;
+ send_client(SIZE_HEADER + sizeof(packet_send_date_time3), buffsend, pte);
+}
+
+static void send_blink_cursor(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending set blink\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_blink_cursor, sizeof(packet_send_blink_cursor));
+ send_client(SIZE_HEADER + sizeof(packet_send_blink_cursor), buffsend, pte);
+ return;
+}
+
+/* pos : 0xab (a=0/2/4 = line ; b = row) */
+static void send_cursor_pos(struct unistimsession *pte, unsigned char pos)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending set cursor position\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_set_pos_cursor,
+ sizeof(packet_send_set_pos_cursor));
+ buffsend[11] = pos;
+ send_client(SIZE_HEADER + sizeof(packet_send_set_pos_cursor), buffsend, pte);
+ return;
+}
+
+static void rcv_resume_connection_with_server(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("ResumeConnectionWithServer received\n");
+ if (unistimdebug)
+ ast_verbose("Sending packet_send_query_mac_address\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_query_mac_address,
+ sizeof(packet_send_query_mac_address));
+ send_client(SIZE_HEADER + sizeof(packet_send_query_mac_address), buffsend, pte);
+ return;
+}
+
+static int unistim_register(struct unistimsession *s)
+{
+ struct unistim_device *d;
+
+ ast_mutex_lock(&devicelock);
+ d = devices;
+ while (d) {
+ if (!strcasecmp(s->macaddr, d->id)) {
+ /* XXX Deal with IP authentication */
+ s->device = d;
+ d->session = s;
+ d->codec_number = DEFAULT_CODEC;
+ d->pos_fav = 0;
+ d->missed_call = 0;
+ d->receiver_state = STATE_ONHOOK;
+ break;
+ }
+ d = d->next;
+ }
+ ast_mutex_unlock(&devicelock);
+
+ if (!d)
+ return 0;
+
+ return 1;
+}
+
+static int alloc_sub(struct unistim_line *l, int x)
+{
+ struct unistim_subchannel *sub;
+ if (!(sub = ast_calloc(1, sizeof(*sub))))
+ return 0;
+
+ if (unistimdebug)
+ ast_verbose(VERBOSE_PREFIX_3
+ "Allocating UNISTIM subchannel #%d on %s@%s ptr=%p\n", x, l->name,
+ l->parent->name, sub);
+ sub->parent = l;
+ sub->subtype = x;
+ l->subs[x] = sub;
+ ast_mutex_init(&sub->lock);
+ return 1;
+}
+
+static int unalloc_sub(struct unistim_line *p, int x)
+{
+ if (!x) {
+ ast_log(LOG_WARNING, "Trying to unalloc the real channel %s@%s?!?\n", p->name,
+ p->parent->name);
+ return -1;
+ }
+ if (unistimdebug)
+ ast_debug(1, "Released sub %d of channel %s@%s\n", x, p->name,
+ p->parent->name);
+ ast_mutex_destroy(&p->lock);
+ ast_free(p->subs[x]);
+ p->subs[x] = 0;
+ return 0;
+}
+
+static void rcv_mac_addr(struct unistimsession *pte, const unsigned char *buf)
+{
+ BUFFSEND;
+ int tmp, i = 0;
+ char addrmac[19];
+ int res = 0;
+ if (unistimdebug)
+ ast_verbose("Mac Address received : ");
+ for (tmp = 15; tmp < 15 + SIZE_HEADER; tmp++) {
+ sprintf(&addrmac[i], "%.2x", (unsigned char) buf[tmp]);
+ i += 2;
+ }
+ if (unistimdebug)
+ ast_verbose("%s\n", addrmac);
+ strcpy(pte->macaddr, addrmac);
+ res = unistim_register(pte);
+ if (!res) {
+ switch (autoprovisioning) {
+ case AUTOPROVISIONING_NO:
+ ast_log(LOG_WARNING, "No entry found for this phone : %s\n", addrmac);
+ pte->state = STATE_AUTHDENY;
+ break;
+ case AUTOPROVISIONING_YES:
+ {
+ struct unistim_device *d, *newd;
+ struct unistim_line *newl;
+ if (unistimdebug)
+ ast_verbose("New phone, autoprovisioning on\n");
+ /* First : locate the [template] section */
+ ast_mutex_lock(&devicelock);
+ d = devices;
+ while (d) {
+ if (!strcasecmp(d->name, "template")) {
+ /* Found, cloning this entry */
+ if (!(newd = ast_malloc(sizeof(*newd)))) {
+ ast_mutex_unlock(&devicelock);
+ return;
+ }
+
+ memcpy(newd, d, sizeof(*newd));
+ if (!(newl = ast_malloc(sizeof(*newl)))) {
+ ast_free(newd);
+ ast_mutex_unlock(&devicelock);
+ return;
+ }
+
+ memcpy(newl, d->lines, sizeof(*newl));
+ if (!alloc_sub(newl, SUB_REAL)) {
+ ast_free(newd);
+ ast_free(newl);
+ ast_mutex_unlock(&devicelock);
+ return;
+ }
+ /* Ok, now updating some fields */
+ ast_copy_string(newd->id, addrmac, sizeof(newd->id));
+ ast_copy_string(newd->name, addrmac, sizeof(newd->name));
+ if (newd->extension == EXTENSION_NONE)
+ newd->extension = EXTENSION_ASK;
+ newd->lines = newl;
+ newd->receiver_state = STATE_ONHOOK;
+ newd->session = pte;
+ newd->to_delete = -1;
+ pte->device = newd;
+ newd->next = NULL;
+ newl->parent = newd;
+ strcpy(newl->name, d->lines->name);
+ snprintf(d->lines->name, sizeof(d->lines->name), "%d",
+ atoi(d->lines->name) + 1);
+ snprintf(newl->fullname, sizeof(newl->fullname), "USTM/%s@%s",
+ newl->name, newd->name);
+ /* Go to the end of the linked chain */
+ while (d->next) {
+ d = d->next;
+ }
+ d->next = newd;
+ d = newd;
+ break;
+ }
+ d = d->next;
+ }
+ ast_mutex_unlock(&devicelock);
+ if (!d) {
+ ast_log(LOG_WARNING, "No entry [template] found in unistim.conf\n");
+ pte->state = STATE_AUTHDENY;
+ }
+ }
+ break;
+ case AUTOPROVISIONING_TN:
+ pte->state = STATE_AUTHDENY;
+ break;
+ case AUTOPROVISIONING_DB:
+ ast_log(LOG_WARNING,
+ "Autoprovisioning with database is not yet functional\n");
+ break;
+ default:
+ ast_log(LOG_WARNING, "Internal error : unknown autoprovisioning value = %d\n",
+ autoprovisioning);
+ }
+ }
+ if (pte->state != STATE_AUTHDENY) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfuly registered\n",
+ pte->device->name);
+ switch (pte->device->extension) {
+ case EXTENSION_NONE:
+ pte->state = STATE_MAINPAGE;
+ break;
+ case EXTENSION_ASK:
+ /* Checking if we already have an extension number */
+ if (ast_strlen_zero(pte->device->extension_number))
+ pte->state = STATE_EXTENSION;
+ else {
+ /* Yes, because of a phone reboot. We don't ask again for the TN */
+ if (RegisterExtension(pte))
+ pte->state = STATE_EXTENSION;
+ else
+ pte->state = STATE_MAINPAGE;
+ }
+ break;
+ case EXTENSION_LINE:
+ ast_copy_string(pte->device->extension_number, pte->device->lines->name,
+ sizeof(pte->device->extension_number));
+ if (RegisterExtension(pte))
+ pte->state = STATE_EXTENSION;
+ else
+ pte->state = STATE_MAINPAGE;
+ break;
+ case EXTENSION_TN:
+ /* If we are here, it's because of a phone reboot */
+ pte->state = STATE_MAINPAGE;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Internal error, extension value unknown : %d\n",
+ pte->device->extension);
+ pte->state = STATE_AUTHDENY;
+ break;
+ }
+ }
+ if (pte->state == STATE_EXTENSION) {
+ if (pte->device->extension != EXTENSION_TN)
+ pte->device->extension = EXTENSION_ASK;
+ pte->device->extension_number[0] = '\0';
+ }
+ if (unistimdebug)
+ ast_verbose("\nSending S1\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_S1, sizeof(packet_send_S1));
+ send_client(SIZE_HEADER + sizeof(packet_send_S1), buffsend, pte);
+
+ if (unistimdebug)
+ ast_verbose("Sending query_basic_manager_04\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_query_basic_manager_04,
+ sizeof(packet_send_query_basic_manager_04));
+ send_client(SIZE_HEADER + sizeof(packet_send_query_basic_manager_04), buffsend, pte);
+
+ if (unistimdebug)
+ ast_verbose("Sending query_basic_manager_10\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_query_basic_manager_10,
+ sizeof(packet_send_query_basic_manager_10));
+ send_client(SIZE_HEADER + sizeof(packet_send_query_basic_manager_10), buffsend, pte);
+
+ send_date_time(pte);
+ return;
+}
+
+static int write_entry_history(struct unistimsession *pte, FILE * f, char c, char *line1)
+{
+ if (fwrite(&c, 1, 1, f) != 1) {
+ display_last_error("Unable to write history log header.");
+ return -1;
+ }
+ if (fwrite(line1, TEXT_LENGTH_MAX, 1, f) != 1) {
+ display_last_error("Unable to write history entry - date.");
+ return -1;
+ }
+ if (fwrite(pte->device->lst_cid, TEXT_LENGTH_MAX, 1, f) != 1) {
+ display_last_error("Unable to write history entry - callerid.");
+ return -1;
+ }
+ if (fwrite(pte->device->lst_cnm, TEXT_LENGTH_MAX, 1, f) != 1) {
+ display_last_error("Unable to write history entry - callername.");
+ return -1;
+ }
+ return 0;
+}
+
+static int write_history(struct unistimsession *pte, char way, char ismissed)
+{
+ char tmp[AST_CONFIG_MAX_PATH], tmp2[AST_CONFIG_MAX_PATH];
+ char line1[TEXT_LENGTH_MAX + 1];
+ char count = 0, *histbuf;
+ int size;
+ FILE *f, *f2;
+ struct timeval tv = ast_tvnow();
+ struct ast_tm atm = { 0, };
+
+ if (!pte->device)
+ return -1;
+ if (!pte->device->callhistory)
+ return 0;
+ if (strchr(pte->device->name, '/') || (pte->device->name[0] == '.')) {
+ ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n",
+ pte->device->name);
+ return -1;
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, USTM_LOG_DIR);
+ if (ast_mkdir(tmp, 0770)) {
+ if (errno != EEXIST) {
+ display_last_error("Unable to create directory for history");
+ return -1;
+ }
+ }
+
+ ast_localtime(&tv, &atm, NULL);
+ if (ismissed) {
+ if (way == 'i')
+ strcpy(tmp2, "Miss");
+ else
+ strcpy(tmp2, "Fail");
+ } else
+ strcpy(tmp2, "Answ");
+ snprintf(line1, sizeof(line1), "%04d/%02d/%02d %02d:%02d:%02d %s",
+ atm.tm_year + 1900, atm.tm_mon + 1, atm.tm_mday, atm.tm_hour,
+ atm.tm_min, atm.tm_sec, tmp2);
+
+ snprintf(tmp, sizeof(tmp), "%s/%s/%s-%c.csv", ast_config_AST_LOG_DIR,
+ USTM_LOG_DIR, pte->device->name, way);
+ if ((f = fopen(tmp, "r"))) {
+ struct stat bufstat;
+
+ if (stat(tmp, &bufstat)) {
+ display_last_error("Unable to stat history log.");
+ fclose(f);
+ return -1;
+ }
+ size = 1 + (MAX_ENTRY_LOG * TEXT_LENGTH_MAX * 3);
+ if (bufstat.st_size != size) {
+ ast_log(LOG_WARNING,
+ "History file %s has an incorrect size (%d instead of %d). It will be replaced by a new one.",
+ tmp, (int) bufstat.st_size, size);
+ fclose(f);
+ f = NULL;
+ count = 1;
+ }
+ }
+
+ /* If we can't open the log file, we create a brand new one */
+ if (!f) {
+ char c = 1;
+ int i;
+
+ if ((errno != ENOENT) && (count == 0)) {
+ display_last_error("Unable to open history log.");
+ return -1;
+ }
+ f = fopen(tmp, "w");
+ if (!f) {
+ display_last_error("Unable to create history log.");
+ return -1;
+ }
+ if (write_entry_history(pte, f, c, line1)) {
+ fclose(f);
+ return -1;
+ }
+ memset(line1, ' ', TEXT_LENGTH_MAX);
+ for (i = 3; i < MAX_ENTRY_LOG * 3; i++) {
+ if (fwrite(line1, TEXT_LENGTH_MAX, 1, f) != 1) {
+ display_last_error("Unable to write history entry - stuffing.");
+ fclose(f);
+ return -1;
+ }
+ }
+ if (fclose(f))
+ display_last_error("Unable to close history - creation.");
+ return 0;
+ }
+ /* We can open the log file, we create a temporary one, we add our entry and copy the rest */
+ if (fread(&count, 1, 1, f) != 1) {
+ display_last_error("Unable to read history header.");
+ fclose(f);
+ return -1;
+ }
+ if (count > MAX_ENTRY_LOG) {
+ ast_log(LOG_WARNING, "Invalid count in history header of %s (%d max %d)\n", tmp,
+ count, MAX_ENTRY_LOG);
+ fclose(f);
+ return -1;
+ }
+ snprintf(tmp2, sizeof(tmp2), "%s/%s/%s-%c.csv.tmp", ast_config_AST_LOG_DIR,
+ USTM_LOG_DIR, pte->device->name, way);
+ if (!(f2 = fopen(tmp2, "w"))) {
+ display_last_error("Unable to create temporary history log.");
+ fclose(f);
+ return -1;
+ }
+
+ if (++count > MAX_ENTRY_LOG)
+ count = MAX_ENTRY_LOG;
+
+ if (write_entry_history(pte, f2, count, line1)) {
+ fclose(f);
+ fclose(f2);
+ return -1;
+ }
+
+ size = (MAX_ENTRY_LOG - 1) * TEXT_LENGTH_MAX * 3;
+ if (!(histbuf = ast_malloc(size))) {
+ fclose(f);
+ fclose(f2);
+ return -1;
+ }
+
+ if (fread(histbuf, size, 1, f) != 1) {
+ ast_free(histbuf);
+ fclose(f);
+ fclose(f2);
+ display_last_error("Unable to read previous history entries.");
+ return -1;
+ }
+ if (fwrite(histbuf, size, 1, f2) != 1) {
+ ast_free(histbuf);
+ fclose(f);
+ fclose(f2);
+ display_last_error("Unable to write previous history entries.");
+ return -1;
+ }
+ ast_free(histbuf);
+ if (fclose(f))
+ display_last_error("Unable to close history log.");
+ if (fclose(f2))
+ display_last_error("Unable to close temporary history log.");
+ if (unlink(tmp))
+ display_last_error("Unable to remove old history log.");
+ if (rename(tmp2, tmp))
+ display_last_error("Unable to rename new history log.");
+ return 0;
+}
+
+static void cancel_dial(struct unistimsession *pte)
+{
+ send_no_ring(pte);
+ pte->device->missed_call++;
+ write_history(pte, 'i', 1);
+ show_main_page(pte);
+ return;
+}
+
+static void swap_subs(struct unistim_line *p, int a, int b)
+{
+/* struct ast_channel *towner; */
+ struct ast_rtp *rtp;
+ int fds;
+
+ if (unistimdebug)
+ ast_verbose("Swapping %d and %d\n", a, b);
+
+ if ((!p->subs[a]->owner) || (!p->subs[b]->owner)) {
+ ast_log(LOG_WARNING,
+ "Attempted to swap subchannels with a null owner : sub #%d=%p sub #%d=%p\n",
+ a, p->subs[a]->owner, b, p->subs[b]->owner);
+ return;
+ }
+ rtp = p->subs[a]->rtp;
+ p->subs[a]->rtp = p->subs[b]->rtp;
+ p->subs[b]->rtp = rtp;
+
+ fds = p->subs[a]->owner->fds[0];
+ p->subs[a]->owner->fds[0] = p->subs[b]->owner->fds[0];
+ p->subs[b]->owner->fds[0] = fds;
+
+ fds = p->subs[a]->owner->fds[1];
+ p->subs[a]->owner->fds[1] = p->subs[b]->owner->fds[1];
+ p->subs[b]->owner->fds[1] = fds;
+}
+
+static int attempt_transfer(struct unistim_subchannel *p1, struct unistim_subchannel *p2)
+{
+ int res = 0;
+ struct ast_channel
+ *chana = NULL, *chanb = NULL, *bridgea = NULL, *bridgeb = NULL, *peera =
+ NULL, *peerb = NULL, *peerc = NULL, *peerd = NULL;
+
+ if (!p1->owner || !p2->owner) {
+ ast_log(LOG_WARNING, "Transfer attempted without dual ownership?\n");
+ return -1;
+ }
+ chana = p1->owner;
+ chanb = p2->owner;
+ bridgea = ast_bridged_channel(chana);
+ bridgeb = ast_bridged_channel(chanb);
+
+ if (bridgea) {
+ peera = chana;
+ peerb = chanb;
+ peerc = bridgea;
+ peerd = bridgeb;
+ } else if (bridgeb) {
+ peera = chanb;
+ peerb = chana;
+ peerc = bridgeb;
+ peerd = bridgea;
+ }
+
+ if (peera && peerb && peerc && (peerb != peerc)) {
+ /*ast_quiet_chan(peera);
+ ast_quiet_chan(peerb);
+ ast_quiet_chan(peerc);
+ ast_quiet_chan(peerd); */
+
+ if (peera->cdr && peerb->cdr) {
+ peerb->cdr = ast_cdr_append(peerb->cdr, peera->cdr);
+ } else if (peera->cdr) {
+ peerb->cdr = peera->cdr;
+ }
+ peera->cdr = NULL;
+
+ if (peerb->cdr && peerc->cdr) {
+ peerb->cdr = ast_cdr_append(peerb->cdr, peerc->cdr);
+ } else if (peerc->cdr) {
+ peerb->cdr = peerc->cdr;
+ }
+ peerc->cdr = NULL;
+
+ if (ast_channel_masquerade(peerb, peerc)) {
+ ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name,
+ peerc->name);
+ res = -1;
+ }
+ return res;
+ } else {
+ ast_log(LOG_NOTICE,
+ "Transfer attempted with no appropriate bridged calls to transfer\n");
+ if (chana)
+ ast_softhangup_nolock(chana, AST_SOFTHANGUP_DEV);
+ if (chanb)
+ ast_softhangup_nolock(chanb, AST_SOFTHANGUP_DEV);
+ return -1;
+ }
+ return 0;
+}
+
+void change_callerid(struct unistimsession *pte, int type, char *callerid)
+{
+ char *data;
+ int size;
+
+ if (type)
+ data = pte->device->lst_cnm;
+ else
+ data = pte->device->lst_cid;
+
+ /* This is very nearly strncpy(), except that the remaining buffer
+ * is padded with ' ', instead of '\0' */
+ memset(data, ' ', TEXT_LENGTH_MAX);
+ size = strlen(callerid);
+ if (size > TEXT_LENGTH_MAX)
+ size = TEXT_LENGTH_MAX;
+ memcpy(data, callerid, size);
+}
+
+static void close_call(struct unistimsession *pte)
+{
+ struct unistim_subchannel *sub;
+ struct unistim_line *l = pte->device->lines;
+
+ sub = pte->device->lines->subs[SUB_REAL];
+ send_stop_timer(pte);
+ if (sub->owner) {
+ sub->alreadygone = 1;
+ if (l->subs[SUB_THREEWAY]) {
+ l->subs[SUB_THREEWAY]->alreadygone = 1;
+ if (attempt_transfer(sub, l->subs[SUB_THREEWAY]) < 0)
+ ast_verbose("attempt_transfer failed.\n");
+ } else
+ ast_queue_hangup(sub->owner);
+ } else {
+ if (l->subs[SUB_THREEWAY]) {
+ if (l->subs[SUB_THREEWAY]->owner)
+ ast_queue_hangup(l->subs[SUB_THREEWAY]->owner);
+ else
+ ast_log(LOG_WARNING, "threeway sub without owner\n");
+ } else
+ ast_verbose("USTM(%s@%s-%d) channel already destroyed\n", sub->parent->name,
+ sub->parent->parent->name, sub->subtype);
+ }
+ change_callerid(pte, 0, pte->device->redial_number);
+ change_callerid(pte, 1, "");
+ write_history(pte, 'o', pte->device->missed_call);
+ pte->device->missed_call = 0;
+ show_main_page(pte);
+ return;
+}
+
+static void IgnoreCall(struct unistimsession *pte)
+{
+ send_no_ring(pte);
+ return;
+}
+
+static void *unistim_ss(void *data)
+{
+ struct ast_channel *chan = data;
+ struct unistim_subchannel *sub = chan->tech_pvt;
+ struct unistim_line *l = sub->parent;
+ struct unistimsession *s = l->parent->session;
+ int res;
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Starting switch on '%s@%s-%d' to %s\n",
+ l->name, l->parent->name, sub->subtype, s->device->phone_number);
+ ast_copy_string(chan->exten, s->device->phone_number, sizeof(chan->exten));
+ ast_copy_string(s->device->redial_number, s->device->phone_number,
+ sizeof(s->device->redial_number));
+ ast_setstate(chan, AST_STATE_RING);
+ res = ast_pbx_run(chan);
+ if (res) {
+ ast_log(LOG_WARNING, "PBX exited non-zero\n");
+ send_tone(s, 1000, 0);;
+ }
+ return NULL;
+}
+
+static void start_rtp(struct unistim_subchannel *sub)
+{
+ BUFFSEND;
+ struct sockaddr_in us;
+ struct sockaddr_in public;
+ struct sockaddr_in sin;
+ int codec;
+ struct sockaddr_in sout;
+
+ /* Sanity checks */
+ if (!sub) {
+ ast_log(LOG_WARNING, "start_rtp with a null subchannel !\n");
+ return;
+ }
+ if (!sub->parent) {
+ ast_log(LOG_WARNING, "start_rtp with a null line !\n");
+ return;
+ }
+ if (!sub->parent->parent) {
+ ast_log(LOG_WARNING, "start_rtp with a null device !\n");
+ return;
+ }
+ if (!sub->parent->parent->session) {
+ ast_log(LOG_WARNING, "start_rtp with a null session !\n");
+ return;
+ }
+ sout = sub->parent->parent->session->sout;
+
+ ast_mutex_lock(&sub->lock);
+ /* Allocate the RTP */
+ if (unistimdebug)
+ ast_verbose("Starting RTP. Bind on %s\n", ast_inet_ntoa(sout.sin_addr));
+ sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, sout.sin_addr);
+ if (!sub->rtp) {
+ ast_log(LOG_WARNING, "Unable to create RTP session: %s binaddr=%s\n",
+ strerror(errno), ast_inet_ntoa(sout.sin_addr));
+ ast_mutex_unlock(&sub->lock);
+ return;
+ }
+ if (sub->rtp && sub->owner) {
+ sub->owner->fds[0] = ast_rtp_fd(sub->rtp);
+ sub->owner->fds[1] = ast_rtcp_fd(sub->rtp);
+ }
+ if (sub->rtp) {
+ ast_rtp_setqos(sub->rtp, tos_audio, cos_audio, "UNISTIM RTP");
+ ast_rtp_setnat(sub->rtp, sub->parent->parent->nat);
+ }
+
+ /* Create the RTP connection */
+ ast_rtp_get_us(sub->rtp, &us);
+ sin.sin_family = AF_INET;
+ /* Setting up RTP for our side */
+ memcpy(&sin.sin_addr, &sub->parent->parent->session->sin.sin_addr,
+ sizeof(sin.sin_addr));
+ sin.sin_port = htons(sub->parent->parent->rtp_port);
+ ast_rtp_set_peer(sub->rtp, &sin);
+ if (!(sub->owner->nativeformats & sub->owner->readformat)) {
+ int fmt;
+ fmt = ast_best_codec(sub->owner->nativeformats);
+ ast_log(LOG_WARNING,
+ "Our read/writeformat has been changed to something incompatible : %s (%d), using %s (%d) best codec from %d\n",
+ ast_getformatname(sub->owner->readformat),
+ sub->owner->readformat, ast_getformatname(fmt), fmt,
+ sub->owner->nativeformats);
+ sub->owner->readformat = fmt;
+ sub->owner->writeformat = fmt;
+ }
+ codec = ast_rtp_lookup_code(sub->rtp, 1, sub->owner->readformat);
+ /* Setting up RTP of the phone */
+ if (public_ip.sin_family == 0) /* NAT IP override ? */
+ memcpy(&public, &us, sizeof(public)); /* No defined, using IP from recvmsg */
+ else
+ memcpy(&public, &public_ip, sizeof(public)); /* override */
+ if (unistimdebug) {
+ ast_verbose
+ ("RTP started : Our IP/port is : %s:%hd with codec %s (%d)\n",
+ ast_inet_ntoa(us.sin_addr),
+ htons(us.sin_port), ast_getformatname(sub->owner->readformat),
+ sub->owner->readformat);
+ ast_verbose("Starting phone RTP stack. Our public IP is %s\n",
+ ast_inet_ntoa(public.sin_addr));
+ }
+ if ((sub->owner->readformat == AST_FORMAT_ULAW) ||
+ (sub->owner->readformat == AST_FORMAT_ALAW)) {
+ if (unistimdebug)
+ ast_verbose("Sending packet_send_rtp_packet_size for codec %d\n", codec);
+ memcpy(buffsend + SIZE_HEADER, packet_send_rtp_packet_size,
+ sizeof(packet_send_rtp_packet_size));
+ buffsend[10] = codec;
+ send_client(SIZE_HEADER + sizeof(packet_send_rtp_packet_size), buffsend,
+ sub->parent->parent->session);
+ }
+ if (unistimdebug)
+ ast_verbose("Sending Jitter Buffer Parameters Configuration\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_jitter_buffer_conf,
+ sizeof(packet_send_jitter_buffer_conf));
+ send_client(SIZE_HEADER + sizeof(packet_send_jitter_buffer_conf), buffsend,
+ sub->parent->parent->session);
+ if (sub->parent->parent->rtp_method != 0) {
+ uint16_t rtcpsin_port = htons(us.sin_port) + 1; /* RTCP port is RTP + 1 */
+
+ if (unistimdebug)
+ ast_verbose("Sending OpenAudioStreamTX using method #%d\n",
+ sub->parent->parent->rtp_method);
+ if (sub->parent->parent->rtp_method == 3)
+ memcpy(buffsend + SIZE_HEADER, packet_send_open_audio_stream_tx3,
+ sizeof(packet_send_open_audio_stream_tx3));
+ else
+ memcpy(buffsend + SIZE_HEADER, packet_send_open_audio_stream_tx,
+ sizeof(packet_send_open_audio_stream_tx));
+ if (sub->parent->parent->rtp_method != 2) {
+ memcpy(buffsend + 28, &public.sin_addr, sizeof(public.sin_addr));
+ buffsend[20] = (htons(sin.sin_port) & 0xff00) >> 8;
+ buffsend[21] = (htons(sin.sin_port) & 0x00ff);
+ buffsend[23] = (rtcpsin_port & 0x00ff);
+ buffsend[22] = (rtcpsin_port & 0xff00) >> 8;
+ buffsend[25] = (us.sin_port & 0xff00) >> 8;
+ buffsend[24] = (us.sin_port & 0x00ff);
+ buffsend[27] = (rtcpsin_port & 0x00ff);
+ buffsend[26] = (rtcpsin_port & 0xff00) >> 8;
+ } else {
+ memcpy(buffsend + 23, &public.sin_addr, sizeof(public.sin_addr));
+ buffsend[15] = (htons(sin.sin_port) & 0xff00) >> 8;
+ buffsend[16] = (htons(sin.sin_port) & 0x00ff);
+ buffsend[20] = (us.sin_port & 0xff00) >> 8;
+ buffsend[19] = (us.sin_port & 0x00ff);
+ buffsend[11] = codec;
+ }
+ buffsend[12] = codec;
+ send_client(SIZE_HEADER + sizeof(packet_send_open_audio_stream_tx), buffsend,
+ sub->parent->parent->session);
+
+ if (unistimdebug)
+ ast_verbose("Sending OpenAudioStreamRX\n");
+ if (sub->parent->parent->rtp_method == 3)
+ memcpy(buffsend + SIZE_HEADER, packet_send_open_audio_stream_rx3,
+ sizeof(packet_send_open_audio_stream_rx3));
+ else
+ memcpy(buffsend + SIZE_HEADER, packet_send_open_audio_stream_rx,
+ sizeof(packet_send_open_audio_stream_rx));
+ if (sub->parent->parent->rtp_method != 2) {
+ memcpy(buffsend + 28, &public.sin_addr, sizeof(public.sin_addr));
+ buffsend[20] = (htons(sin.sin_port) & 0xff00) >> 8;
+ buffsend[21] = (htons(sin.sin_port) & 0x00ff);
+ buffsend[23] = (rtcpsin_port & 0x00ff);
+ buffsend[22] = (rtcpsin_port & 0xff00) >> 8;
+ buffsend[25] = (us.sin_port & 0xff00) >> 8;
+ buffsend[24] = (us.sin_port & 0x00ff);
+ buffsend[27] = (rtcpsin_port & 0x00ff);
+ buffsend[26] = (rtcpsin_port & 0xff00) >> 8;
+ } else {
+ memcpy(buffsend + 23, &public.sin_addr, sizeof(public.sin_addr));
+ buffsend[15] = (htons(sin.sin_port) & 0xff00) >> 8;
+ buffsend[16] = (htons(sin.sin_port) & 0x00ff);
+ buffsend[20] = (us.sin_port & 0xff00) >> 8;
+ buffsend[19] = (us.sin_port & 0x00ff);
+ buffsend[12] = codec;
+ }
+ buffsend[11] = codec;
+ send_client(SIZE_HEADER + sizeof(packet_send_open_audio_stream_rx), buffsend,
+ sub->parent->parent->session);
+ } else {
+ uint16_t rtcpsin_port = htons(us.sin_port) + 1; /* RTCP port is RTP + 1 */
+
+ if (unistimdebug)
+ ast_verbose("Sending packet_send_call default method\n");
+
+ memcpy(buffsend + SIZE_HEADER, packet_send_call, sizeof(packet_send_call));
+ memcpy(buffsend + 53, &public.sin_addr, sizeof(public.sin_addr));
+ /* Destination port when sending RTP */
+ buffsend[49] = (us.sin_port & 0x00ff);
+ buffsend[50] = (us.sin_port & 0xff00) >> 8;
+ /* Destination port when sending RTCP */
+ buffsend[52] = (rtcpsin_port & 0x00ff);
+ buffsend[51] = (rtcpsin_port & 0xff00) >> 8;
+ /* Codec */
+ buffsend[40] = codec;
+ buffsend[41] = codec;
+ if (sub->owner->readformat == AST_FORMAT_ULAW)
+ buffsend[42] = 1; /* 1 = 20ms (160 bytes), 2 = 40ms (320 bytes) */
+ else if (sub->owner->readformat == AST_FORMAT_ALAW)
+ buffsend[42] = 1; /* 1 = 20ms (160 bytes), 2 = 40ms (320 bytes) */
+ else if (sub->owner->readformat == AST_FORMAT_G723_1)
+ buffsend[42] = 2; /* 1 = 30ms (24 bytes), 2 = 60 ms (48 bytes) */
+ else if (sub->owner->readformat == AST_FORMAT_G729A)
+ buffsend[42] = 2; /* 1 = 10ms (10 bytes), 2 = 20ms (20 bytes) */
+ else
+ ast_log(LOG_WARNING, "Unsupported codec %s (%d) !\n",
+ ast_getformatname(sub->owner->readformat), sub->owner->readformat);
+ /* Source port for transmit RTP and Destination port for receiving RTP */
+ buffsend[45] = (htons(sin.sin_port) & 0xff00) >> 8;
+ buffsend[46] = (htons(sin.sin_port) & 0x00ff);
+ buffsend[47] = (rtcpsin_port & 0xff00) >> 8;
+ buffsend[48] = (rtcpsin_port & 0x00ff);
+ send_client(SIZE_HEADER + sizeof(packet_send_call), buffsend,
+ sub->parent->parent->session);
+ }
+ ast_mutex_unlock(&sub->lock);
+}
+
+static void SendDialTone(struct unistimsession *pte)
+{
+ int i;
+ /* No country defined ? Using US tone */
+ if (ast_strlen_zero(pte->device->country)) {
+ if (unistimdebug)
+ ast_verbose("No country defined, using US tone\n");
+ send_tone(pte, 350, 440);
+ return;
+ }
+ if (strlen(pte->device->country) != 2) {
+ if (unistimdebug)
+ ast_verbose("Country code != 2 char, using US tone\n");
+ send_tone(pte, 350, 440);
+ return;
+ }
+ i = 0;
+ while (frequency[i].freq1) {
+ if ((frequency[i].country[0] == pte->device->country[0]) &&
+ (frequency[i].country[1] == pte->device->country[1])) {
+ if (unistimdebug)
+ ast_verbose("Country code found (%s), freq1=%d freq2=%d\n",
+ frequency[i].country, frequency[i].freq1, frequency[i].freq2);
+ send_tone(pte, frequency[i].freq1, frequency[i].freq2);
+ }
+ i++;
+ }
+}
+
+static void handle_dial_page(struct unistimsession *pte)
+{
+ pte->state = STATE_DIALPAGE;
+ if (pte->device->call_forward[0] == -1) {
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Enter forward");
+ send_text_status(pte, "ForwardCancel BackSpcErase");
+ if (pte->device->call_forward[1] != 0) {
+ char tmp[TEXT_LENGTH_MAX + 1];
+
+ ast_copy_string(pte->device->phone_number, pte->device->call_forward + 1,
+ sizeof(pte->device->phone_number));
+ pte->device->size_phone_number = strlen(pte->device->phone_number);
+ if (pte->device->size_phone_number > 15)
+ pte->device->size_phone_number = 15;
+ strcpy(tmp, "Number : ...............");
+ memcpy(tmp + 9, pte->device->phone_number, pte->device->size_phone_number);
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmp);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte,
+ (unsigned char) (TEXT_LINE2 + 0x09 +
+ pte->device->size_phone_number));
+ send_led_update(pte, 0);
+ return;
+ }
+ } else {
+ if ((pte->device->output == OUTPUT_HANDSET) &&
+ (pte->device->receiver_state == STATE_ONHOOK))
+ send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
+ else
+ send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
+ SendDialTone(pte);
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Enter the number to dial");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "and press Call");
+ send_text_status(pte, "Call Redial BackSpcErase");
+ }
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, "Number : ...............");
+ send_blink_cursor(pte);
+ send_cursor_pos(pte, TEXT_LINE2 + 0x09);
+ pte->device->size_phone_number = 0;
+ pte->device->phone_number[0] = 0;
+ change_favorite_icon(pte, FAV_ICON_PHONE_BLACK);
+ Sendicon(TEXT_LINE0, FAV_ICON_NONE, pte);
+ pte->device->missed_call = 0;
+ send_led_update(pte, 0);
+ return;
+}
+
+/* Step 1 : Music On Hold for peer, Dialing screen for us */
+static void TransferCallStep1(struct unistimsession *pte)
+{
+ struct unistim_subchannel *sub;
+ struct unistim_line *p = pte->device->lines;
+
+ sub = p->subs[SUB_REAL];
+
+ if (!sub->owner) {
+ ast_log(LOG_WARNING, "Unable to find subchannel for music on hold\n");
+ return;
+ }
+ if (p->subs[SUB_THREEWAY]) {
+ if (unistimdebug)
+ ast_verbose("Transfer canceled, hangup our threeway channel\n");
+ if (p->subs[SUB_THREEWAY]->owner)
+ ast_queue_hangup(p->subs[SUB_THREEWAY]->owner);
+ else
+ ast_log(LOG_WARNING, "Canceling a threeway channel without owner\n");
+ return;
+ }
+ /* Start music on hold if appropriate */
+ if (pte->device->moh)
+ ast_log(LOG_WARNING, "Transfer with peer already listening music on hold\n");
+ else {
+ if (ast_bridged_channel(p->subs[SUB_REAL]->owner)) {
+ ast_moh_start(ast_bridged_channel(p->subs[SUB_REAL]->owner),
+ pte->device->lines->musicclass, NULL);
+ pte->device->moh = 1;
+ } else {
+ ast_log(LOG_WARNING, "Unable to find peer subchannel for music on hold\n");
+ return;
+ }
+ }
+ /* Silence our channel */
+ if (!pte->device->silence_generator) {
+ pte->device->silence_generator =
+ ast_channel_start_silence_generator(p->subs[SUB_REAL]->owner);
+ if (pte->device->silence_generator == NULL)
+ ast_log(LOG_WARNING, "Unable to start a silence generator.\n");
+ else if (unistimdebug)
+ ast_verbose("Starting silence generator\n");
+ }
+ handle_dial_page(pte);
+}
+
+/* From phone to PBX */
+static void HandleCallOutgoing(struct unistimsession *s)
+{
+ struct ast_channel *c;
+ struct unistim_subchannel *sub;
+ pthread_t t;
+ s->state = STATE_CALL;
+ sub = s->device->lines->subs[SUB_REAL];
+ if (!sub) {
+ ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
+ return;
+ }
+ if (!sub->owner) { /* A call is already in progress ? */
+ c = unistim_new(sub, AST_STATE_DOWN); /* No, starting a new one */
+ if (c) {
+ /* Need to start RTP before calling ast_pbx_run */
+ if (!sub->rtp)
+ start_rtp(sub);
+ send_select_output(s, s->device->output, s->device->volume, MUTE_OFF);
+ send_text(TEXT_LINE0, TEXT_NORMAL, s, "Calling :");
+ send_text(TEXT_LINE1, TEXT_NORMAL, s, s->device->phone_number);
+ send_text(TEXT_LINE2, TEXT_NORMAL, s, "Dialing...");
+ send_text_status(s, "Hangup");
+ /* start switch */
+ if (ast_pthread_create(&t, NULL, unistim_ss, c)) {
+ display_last_error("Unable to create switch thread");
+ ast_queue_hangup(c);
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n",
+ sub->parent->name, s->device->name);
+ } else { /* We already have a call, so we switch in a threeway call */
+
+ if (s->device->moh) {
+ struct unistim_subchannel *sub;
+ struct unistim_line *p = s->device->lines;
+ sub = p->subs[SUB_REAL];
+
+ if (!sub->owner) {
+ ast_log(LOG_WARNING, "Unable to find subchannel for music on hold\n");
+ return;
+ }
+ if (p->subs[SUB_THREEWAY]) {
+ ast_log(LOG_WARNING,
+ "Can't transfer while an another transfer is taking place\n");
+ return;
+ }
+ if (!alloc_sub(p, SUB_THREEWAY)) {
+ ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n");
+ return;
+ }
+ /* Stop the silence generator */
+ if (s->device->silence_generator) {
+ if (unistimdebug)
+ ast_verbose("Stopping silence generator\n");
+ ast_channel_stop_silence_generator(sub->owner,
+ s->device->silence_generator);
+ s->device->silence_generator = NULL;
+ }
+ send_tone(s, 0, 0);
+ /* Make new channel */
+ c = unistim_new(p->subs[SUB_THREEWAY], AST_STATE_DOWN);
+ if (!c) {
+ ast_log(LOG_WARNING, "Cannot allocate new structure on channel %p\n", p);
+ return;
+ }
+ /* Swap things around between the three-way and real call */
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ send_select_output(s, s->device->output, s->device->volume, MUTE_OFF);
+ send_text(TEXT_LINE0, TEXT_NORMAL, s, "Calling (pre-transfer)");
+ send_text(TEXT_LINE1, TEXT_NORMAL, s, s->device->phone_number);
+ send_text(TEXT_LINE2, TEXT_NORMAL, s, "Dialing...");
+ send_text_status(s, "TransfrCancel");
+
+ if (ast_pthread_create(&t, NULL, unistim_ss, p->subs[SUB_THREEWAY]->owner)) {
+ ast_log(LOG_WARNING, "Unable to start simple switch on channel %p\n", p);
+ ast_hangup(c);
+ return;
+ }
+ if (unistimdebug)
+ ast_verbose
+ ("Started three way call on channel %p (%s) subchan %d\n",
+ p->subs[SUB_THREEWAY]->owner, p->subs[SUB_THREEWAY]->owner->name,
+ p->subs[SUB_THREEWAY]->subtype);
+ } else
+ ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
+ }
+ return;
+}
+
+/* From PBX to phone */
+static void HandleCallIncoming(struct unistimsession *s)
+{
+ struct unistim_subchannel *sub;
+ s->state = STATE_CALL;
+ s->device->missed_call = 0;
+ send_no_ring(s);
+ sub = s->device->lines->subs[SUB_REAL];
+ if (!sub) {
+ ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
+ return;
+ } else if (unistimdebug)
+ ast_verbose("Handle Call Incoming for %s@%s\n", sub->parent->name,
+ s->device->name);
+ start_rtp(sub);
+ if (!sub->rtp)
+ ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", sub->parent->name,
+ s->device->name);
+ ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
+ send_text(TEXT_LINE2, TEXT_NORMAL, s, "is on-line");
+ send_text_status(s, "Hangup Transf");
+ send_start_timer(s);
+
+ if ((s->device->output == OUTPUT_HANDSET) &&
+ (s->device->receiver_state == STATE_ONHOOK))
+ send_select_output(s, OUTPUT_SPEAKER, s->device->volume, MUTE_OFF);
+ else
+ send_select_output(s, s->device->output, s->device->volume, MUTE_OFF);
+ s->device->start_call_timestamp = time(0);
+ write_history(s, 'i', 0);
+ return;
+}
+
+static int unistim_do_senddigit(struct unistimsession *pte, char digit)
+{
+
+ struct ast_frame f = { 0, };
+ struct unistim_subchannel *sub;
+ sub = pte->device->lines->subs[SUB_REAL];
+ if (!sub->owner) {
+ ast_log(LOG_WARNING, "Unable to find subchannel in dtmf senddigit\n");
+ return -1;
+ }
+ if (unistimdebug)
+ ast_verbose("Send Digit %c\n", digit);
+ switch (digit) {
+ case '0':
+ send_tone(pte, 941, 1336);
+ break;
+ case '1':
+ send_tone(pte, 697, 1209);
+ break;
+ case '2':
+ send_tone(pte, 697, 1336);
+ break;
+ case '3':
+ send_tone(pte, 697, 1477);
+ break;
+ case '4':
+ send_tone(pte, 770, 1209);
+ break;
+ case '5':
+ send_tone(pte, 770, 1336);
+ break;
+ case '6':
+ send_tone(pte, 770, 1477);
+ break;
+ case '7':
+ send_tone(pte, 852, 1209);
+ break;
+ case '8':
+ send_tone(pte, 852, 1336);
+ break;
+ case '9':
+ send_tone(pte, 852, 1477);
+ break;
+ case 'A':
+ send_tone(pte, 697, 1633);
+ break;
+ case 'B':
+ send_tone(pte, 770, 1633);
+ break;
+ case 'C':
+ send_tone(pte, 852, 1633);
+ break;
+ case 'D':
+ send_tone(pte, 941, 1633);
+ break;
+ case '*':
+ send_tone(pte, 941, 1209);
+ break;
+ case '#':
+ send_tone(pte, 941, 1477);
+ break;
+ default:
+ send_tone(pte, 500, 2000);
+ }
+ usleep(150000); /* XXX Less than perfect, blocking an important thread is not a good idea */
+ send_tone(pte, 0, 0);
+ f.frametype = AST_FRAME_DTMF;
+ f.subclass = digit;
+ f.src = "unistim";
+ ast_queue_frame(sub->owner, &f);
+ return 0;
+}
+
+static void key_call(struct unistimsession *pte, char keycode)
+{
+ if ((keycode >= KEY_0) && (keycode <= KEY_SHARP)) {
+ if (keycode == KEY_SHARP)
+ keycode = '#';
+ else if (keycode == KEY_STAR)
+ keycode = '*';
+ else
+ keycode -= 0x10;
+ unistim_do_senddigit(pte, keycode);
+ return;
+ }
+ switch (keycode) {
+ case KEY_HANGUP:
+ case KEY_FUNC1:
+ close_call(pte);
+ break;
+ case KEY_FUNC2:
+ TransferCallStep1(pte);
+ break;
+ case KEY_HEADPHN:
+ if (pte->device->output == OUTPUT_HEADPHONE)
+ send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
+ else
+ send_select_output(pte, OUTPUT_HEADPHONE, pte->device->volume, MUTE_OFF);
+ break;
+ case KEY_LOUDSPK:
+ if (pte->device->output != OUTPUT_SPEAKER)
+ send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
+ else
+ send_select_output(pte, pte->device->previous_output, pte->device->volume,
+ MUTE_OFF);
+ break;
+ case KEY_MUTE:
+ if (!pte->device->moh) {
+ if (pte->device->mute == MUTE_ON)
+ send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
+ else
+ send_select_output(pte, pte->device->output, pte->device->volume, MUTE_ON);
+ break;
+ }
+ case KEY_ONHOLD:
+ {
+ struct unistim_subchannel *sub;
+ struct ast_channel *bridgepeer = NULL;
+ sub = pte->device->lines->subs[SUB_REAL];
+ if (!sub->owner) {
+ ast_log(LOG_WARNING, "Unable to find subchannel for music on hold\n");
+ return;
+ }
+ if ((bridgepeer = ast_bridged_channel(sub->owner))) {
+ if (pte->device->moh) {
+ ast_moh_stop(bridgepeer);
+ pte->device->moh = 0;
+ send_select_output(pte, pte->device->output, pte->device->volume,
+ MUTE_OFF);
+ } else {
+ ast_moh_start(bridgepeer, pte->device->lines->musicclass, NULL);
+ pte->device->moh = 1;
+ send_select_output(pte, pte->device->output, pte->device->volume,
+ MUTE_ON);
+ }
+ } else
+ ast_log(LOG_WARNING,
+ "Unable to find peer subchannel for music on hold\n");
+ break;
+ }
+ }
+ return;
+}
+
+static void key_ringing(struct unistimsession *pte, char keycode)
+{
+ if (keycode == KEY_FAV0 + pte->device->softkeylinepos) {
+ HandleCallIncoming(pte);
+ return;
+ }
+ switch (keycode) {
+ case KEY_HANGUP:
+ case KEY_FUNC4:
+ IgnoreCall(pte);
+ break;
+ case KEY_FUNC1:
+ HandleCallIncoming(pte);
+ break;
+ }
+ return;
+}
+
+static void Keyfavorite(struct unistimsession *pte, char keycode)
+{
+ int fav;
+
+ if ((keycode < KEY_FAV1) && (keycode > KEY_FAV5)) {
+ ast_log(LOG_WARNING, "It's not a favorite key\n");
+ return;
+ }
+ if (keycode == KEY_FAV0)
+ return;
+ fav = keycode - KEY_FAV0;
+ if (pte->device->softkeyicon[fav] == 0)
+ return;
+ ast_copy_string(pte->device->phone_number, pte->device->softkeynumber[fav],
+ sizeof(pte->device->phone_number));
+ HandleCallOutgoing(pte);
+ return;
+}
+
+static void key_dial_page(struct unistimsession *pte, char keycode)
+{
+ if (keycode == KEY_FUNC3) {
+ if (pte->device->size_phone_number <= 1)
+ keycode = KEY_FUNC4;
+ else {
+ pte->device->size_phone_number -= 2;
+ keycode = pte->device->phone_number[pte->device->size_phone_number] + 0x10;
+ }
+ }
+ if ((keycode >= KEY_0) && (keycode <= KEY_SHARP)) {
+ char tmpbuf[] = "Number : ...............";
+ int i = 0;
+
+ if (pte->device->size_phone_number >= 15)
+ return;
+ if (pte->device->size_phone_number == 0)
+ send_tone(pte, 0, 0);
+ while (i < pte->device->size_phone_number) {
+ tmpbuf[i + 9] = pte->device->phone_number[i];
+ i++;
+ }
+ if (keycode == KEY_SHARP)
+ keycode = '#';
+ else if (keycode == KEY_STAR)
+ keycode = '*';
+ else
+ keycode -= 0x10;
+ tmpbuf[i + 9] = keycode;
+ pte->device->phone_number[i] = keycode;
+ pte->device->size_phone_number++;
+ pte->device->phone_number[i + 1] = 0;
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmpbuf);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte, (unsigned char) (TEXT_LINE2 + 0x0a + i));
+ return;
+ }
+ if (keycode == KEY_FUNC4) {
+
+ pte->device->size_phone_number = 0;
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, "Number : ...............");
+ send_blink_cursor(pte);
+ send_cursor_pos(pte, TEXT_LINE2 + 0x09);
+ return;
+ }
+
+ if (pte->device->call_forward[0] == -1) {
+ if (keycode == KEY_FUNC1) {
+ ast_copy_string(pte->device->call_forward, pte->device->phone_number,
+ sizeof(pte->device->call_forward));
+ show_main_page(pte);
+ } else if ((keycode == KEY_FUNC2) || (keycode == KEY_HANGUP)) {
+ pte->device->call_forward[0] = '\0';
+ show_main_page(pte);
+ }
+ return;
+ }
+ switch (keycode) {
+ case KEY_FUNC2:
+ if (ast_strlen_zero(pte->device->redial_number))
+ break;
+ ast_copy_string(pte->device->phone_number, pte->device->redial_number,
+ sizeof(pte->device->phone_number));
+ case KEY_FUNC1:
+ HandleCallOutgoing(pte);
+ break;
+ case KEY_HANGUP:
+ if (pte->device->lines->subs[SUB_REAL]->owner) {
+ /* Stop the silence generator */
+ if (pte->device->silence_generator) {
+ if (unistimdebug)
+ ast_verbose("Stopping silence generator\n");
+ ast_channel_stop_silence_generator(pte->device->lines->subs[SUB_REAL]->
+ owner, pte->device->silence_generator);
+ pte->device->silence_generator = NULL;
+ }
+ send_tone(pte, 0, 0);
+ ast_moh_stop(ast_bridged_channel(pte->device->lines->subs[SUB_REAL]->owner));
+ pte->device->moh = 0;
+ pte->state = STATE_CALL;
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Dialing canceled,");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "switching back to");
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, "previous call.");
+ send_text_status(pte, "Hangup Transf");
+ } else
+ show_main_page(pte);
+ break;
+ case KEY_FAV1:
+ case KEY_FAV2:
+ case KEY_FAV3:
+ case KEY_FAV4:
+ case KEY_FAV5:
+ Keyfavorite(pte, keycode);
+ break;
+ case KEY_LOUDSPK:
+ if (pte->device->output == OUTPUT_SPEAKER) {
+ if (pte->device->receiver_state == STATE_OFFHOOK)
+ send_select_output(pte, pte->device->previous_output, pte->device->volume,
+ MUTE_OFF);
+ else
+ show_main_page(pte);
+ } else
+ send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
+ break;
+ case KEY_HEADPHN:
+ if (pte->device->output == OUTPUT_HEADPHONE) {
+ if (pte->device->receiver_state == STATE_OFFHOOK)
+ send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
+ else
+ show_main_page(pte);
+ } else
+ send_select_output(pte, OUTPUT_HEADPHONE, pte->device->volume, MUTE_OFF);
+ break;
+ }
+ return;
+}
+
+#define SELECTCODEC_START_ENTRY_POS 15
+#define SELECTCODEC_MAX_LENGTH 2
+#define SELECTCODEC_MSG "Codec number : .."
+static void HandleSelectCodec(struct unistimsession *pte)
+{
+ char buf[30], buf2[5];
+
+ pte->state = STATE_SELECTCODEC;
+ strcpy(buf, "Using codec ");
+ sprintf(buf2, "%d", pte->device->codec_number);
+ strcat(buf, buf2);
+ strcat(buf, " (G711u=0,");
+
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, buf);
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "G723=4,G711a=8,G729A=18)");
+ send_text(TEXT_LINE2, TEXT_INVERSE, pte, SELECTCODEC_MSG);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte, TEXT_LINE2 + SELECTCODEC_START_ENTRY_POS);
+ pte->size_buff_entry = 0;
+ send_text_status(pte, "Select BackSpcErase Cancel");
+ return;
+}
+
+static void key_select_codec(struct unistimsession *pte, char keycode)
+{
+ if (keycode == KEY_FUNC2) {
+ if (pte->size_buff_entry <= 1)
+ keycode = KEY_FUNC3;
+ else {
+ pte->size_buff_entry -= 2;
+ keycode = pte->buff_entry[pte->size_buff_entry] + 0x10;
+ }
+ }
+ if ((keycode >= KEY_0) && (keycode <= KEY_9)) {
+ char tmpbuf[] = SELECTCODEC_MSG;
+ int i = 0;
+
+ if (pte->size_buff_entry >= SELECTCODEC_MAX_LENGTH)
+ return;
+
+ while (i < pte->size_buff_entry) {
+ tmpbuf[i + SELECTCODEC_START_ENTRY_POS] = pte->buff_entry[i];
+ i++;
+ }
+ tmpbuf[i + SELECTCODEC_START_ENTRY_POS] = keycode - 0x10;
+ pte->buff_entry[i] = keycode - 0x10;
+ pte->size_buff_entry++;
+ send_text(TEXT_LINE2, TEXT_INVERSE, pte, tmpbuf);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte,
+ (unsigned char) (TEXT_LINE2 + SELECTCODEC_START_ENTRY_POS + 1 + i));
+ return;
+ }
+
+ switch (keycode) {
+ case KEY_FUNC1:
+ if (pte->size_buff_entry == 1)
+ pte->device->codec_number = pte->buff_entry[0] - 48;
+ else if (pte->size_buff_entry == 2)
+ pte->device->codec_number =
+ ((pte->buff_entry[0] - 48) * 10) + (pte->buff_entry[1] - 48);
+ show_main_page(pte);
+ break;
+ case KEY_FUNC3:
+ pte->size_buff_entry = 0;
+ send_text(TEXT_LINE2, TEXT_INVERSE, pte, SELECTCODEC_MSG);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte, TEXT_LINE2 + SELECTCODEC_START_ENTRY_POS);
+ break;
+ case KEY_HANGUP:
+ case KEY_FUNC4:
+ show_main_page(pte);
+ break;
+ }
+ return;
+}
+
+#define SELECTEXTENSION_START_ENTRY_POS 0
+#define SELECTEXTENSION_MAX_LENGTH 10
+#define SELECTEXTENSION_MSG ".........."
+static void ShowExtensionPage(struct unistimsession *pte)
+{
+ pte->state = STATE_EXTENSION;
+
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Please enter a Terminal");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Number (TN) :");
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, SELECTEXTENSION_MSG);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte, TEXT_LINE2 + SELECTEXTENSION_START_ENTRY_POS);
+ send_text_status(pte, "Enter BackSpcErase");
+ pte->size_buff_entry = 0;
+ return;
+}
+
+static void key_select_extension(struct unistimsession *pte, char keycode)
+{
+ if (keycode == KEY_FUNC2) {
+ if (pte->size_buff_entry <= 1)
+ keycode = KEY_FUNC3;
+ else {
+ pte->size_buff_entry -= 2;
+ keycode = pte->buff_entry[pte->size_buff_entry] + 0x10;
+ }
+ }
+ if ((keycode >= KEY_0) && (keycode <= KEY_9)) {
+ char tmpbuf[] = SELECTEXTENSION_MSG;
+ int i = 0;
+
+ if (pte->size_buff_entry >= SELECTEXTENSION_MAX_LENGTH)
+ return;
+
+ while (i < pte->size_buff_entry) {
+ tmpbuf[i + SELECTEXTENSION_START_ENTRY_POS] = pte->buff_entry[i];
+ i++;
+ }
+ tmpbuf[i + SELECTEXTENSION_START_ENTRY_POS] = keycode - 0x10;
+ pte->buff_entry[i] = keycode - 0x10;
+ pte->size_buff_entry++;
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmpbuf);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte,
+ (unsigned char) (TEXT_LINE2 + SELECTEXTENSION_START_ENTRY_POS + 1 +
+ i));
+ return;
+ }
+
+ switch (keycode) {
+ case KEY_FUNC1:
+ if (pte->size_buff_entry < 1)
+ return;
+ if (autoprovisioning == AUTOPROVISIONING_TN) {
+ struct unistim_device *d;
+
+ /* First step : looking for this TN in our device list */
+ ast_mutex_lock(&devicelock);
+ d = devices;
+ pte->buff_entry[pte->size_buff_entry] = '\0';
+ while (d) {
+ if (d->id[0] == 'T') { /* It's a TN device ? */
+ /* It's the TN we're looking for ? */
+ if (!strcmp((d->id) + 1, pte->buff_entry)) {
+ pte->device = d;
+ d->session = pte;
+ d->codec_number = DEFAULT_CODEC;
+ d->pos_fav = 0;
+ d->missed_call = 0;
+ d->receiver_state = STATE_ONHOOK;
+ strcpy(d->id, pte->macaddr);
+ pte->device->extension_number[0] = 'T';
+ pte->device->extension = EXTENSION_TN;
+ ast_copy_string((pte->device->extension_number) + 1,
+ pte->buff_entry, pte->size_buff_entry + 1);
+ ast_mutex_unlock(&devicelock);
+ show_main_page(pte);
+ refresh_all_favorite(pte);
+ return;
+ }
+ }
+ d = d->next;
+ }
+ ast_mutex_unlock(&devicelock);
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Invalid Terminal Number.");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Please try again :");
+ send_cursor_pos(pte,
+ (unsigned char) (TEXT_LINE2 + SELECTEXTENSION_START_ENTRY_POS +
+ pte->size_buff_entry));
+ send_blink_cursor(pte);
+ } else {
+ ast_copy_string(pte->device->extension_number, pte->buff_entry,
+ pte->size_buff_entry + 1);
+ if (RegisterExtension(pte)) {
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Invalid extension.");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Please try again :");
+ send_cursor_pos(pte,
+ (unsigned char) (TEXT_LINE2 +
+ SELECTEXTENSION_START_ENTRY_POS +
+ pte->size_buff_entry));
+ send_blink_cursor(pte);
+ } else
+ show_main_page(pte);
+ }
+ break;
+ case KEY_FUNC3:
+ pte->size_buff_entry = 0;
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, SELECTEXTENSION_MSG);
+ send_blink_cursor(pte);
+ send_cursor_pos(pte, TEXT_LINE2 + SELECTEXTENSION_START_ENTRY_POS);
+ break;
+ }
+ return;
+}
+
+static int ReformatNumber(char *number)
+{
+ int pos = 0, i = 0, size = strlen(number);
+
+ for (; i < size; i++) {
+ if ((number[i] >= '0') && (number[i] <= '9')) {
+ if (i == pos) {
+ pos++;
+ continue;
+ }
+ number[pos] = number[i];
+ pos++;
+ }
+ }
+ number[pos] = 0;
+ return pos;
+}
+
+static void show_entry_history(struct unistimsession *pte, FILE ** f)
+{
+ char line[TEXT_LENGTH_MAX + 1], status[STATUS_LENGTH_MAX + 1], func1[10], func2[10],
+ func3[10];
+
+ if (fread(line, TEXT_LENGTH_MAX, 1, *f) != 1) {
+ display_last_error("Can't read history date entry");
+ fclose(*f);
+ return;
+ }
+ line[sizeof(line) - 1] = '\0';
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, line);
+ if (fread(line, TEXT_LENGTH_MAX, 1, *f) != 1) {
+ display_last_error("Can't read callerid entry");
+ fclose(*f);
+ return;
+ }
+ line[sizeof(line) - 1] = '\0';
+ ast_copy_string(pte->device->lst_cid, line, sizeof(pte->device->lst_cid));
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, line);
+ if (fread(line, TEXT_LENGTH_MAX, 1, *f) != 1) {
+ display_last_error("Can't read callername entry");
+ fclose(*f);
+ return;
+ }
+ line[sizeof(line) - 1] = '\0';
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, line);
+ fclose(*f);
+
+ snprintf(line, sizeof(line), "Call %03d/%03d", pte->buff_entry[2],
+ pte->buff_entry[1]);
+ send_texttitle(pte, line);
+
+ if (pte->buff_entry[2] == 1)
+ strcpy(func1, " ");
+ else
+ strcpy(func1, "Prvious");
+ if (pte->buff_entry[2] >= pte->buff_entry[1])
+ strcpy(func2, " ");
+ else
+ strcpy(func2, "Next ");
+ if (ReformatNumber(pte->device->lst_cid))
+ strcpy(func3, "Redial ");
+ else
+ strcpy(func3, " ");
+ snprintf(status, sizeof(status), "%s%s%sCancel", func1, func2, func3);
+ send_text_status(pte, status);
+}
+
+static char OpenHistory(struct unistimsession *pte, char way, FILE ** f)
+{
+ char tmp[AST_CONFIG_MAX_PATH];
+ char count;
+
+ snprintf(tmp, sizeof(tmp), "%s/%s/%s-%c.csv", ast_config_AST_LOG_DIR,
+ USTM_LOG_DIR, pte->device->name, way);
+ *f = fopen(tmp, "r");
+ if (!*f) {
+ display_last_error("Unable to open history file");
+ return 0;
+ }
+ if (fread(&count, 1, 1, *f) != 1) {
+ display_last_error("Unable to read history header - display.");
+ fclose(*f);
+ return 0;
+ }
+ if (count > MAX_ENTRY_LOG) {
+ ast_log(LOG_WARNING, "Invalid count in history header of %s (%d max %d)\n", tmp,
+ count, MAX_ENTRY_LOG);
+ fclose(*f);
+ return 0;
+ }
+ return count;
+}
+
+static void show_history(struct unistimsession *pte, char way)
+{
+ FILE *f;
+ char count;
+
+ if (!pte->device)
+ return;
+ if (!pte->device->callhistory)
+ return;
+ count = OpenHistory(pte, way, &f);
+ if (!count)
+ return;
+ pte->buff_entry[0] = way;
+ pte->buff_entry[1] = count;
+ pte->buff_entry[2] = 1;
+ show_entry_history(pte, &f);
+ pte->state = STATE_HISTORY;
+}
+
+static void show_main_page(struct unistimsession *pte)
+{
+ char tmpbuf[TEXT_LENGTH_MAX + 1];
+
+
+ if ((pte->device->extension == EXTENSION_ASK) &&
+ (ast_strlen_zero(pte->device->extension_number))) {
+ ShowExtensionPage(pte);
+ return;
+ }
+
+ pte->state = STATE_MAINPAGE;
+
+ send_tone(pte, 0, 0);
+ send_select_output(pte, pte->device->output, pte->device->volume, MUTE_ON_DISCRET);
+ pte->device->lines->lastmsgssent = 0;
+ send_favorite(pte->device->softkeylinepos, FAV_ICON_ONHOOK_BLACK, pte,
+ pte->device->softkeylabel[pte->device->softkeylinepos]);
+ if (!ast_strlen_zero(pte->device->call_forward)) {
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Call forwarded to :");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, pte->device->call_forward);
+ Sendicon(TEXT_LINE0, FAV_ICON_REFLECT + FAV_BLINK_SLOW, pte);
+ send_text_status(pte, "Dial Redial NoForwd");
+ } else {
+ if ((pte->device->extension == EXTENSION_ASK) ||
+ (pte->device->extension == EXTENSION_TN))
+ send_text_status(pte, "Dial Redial ForwardUnregis");
+ else
+ send_text_status(pte, "Dial Redial Forward");
+
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, pte->device->maintext1);
+ if (pte->device->missed_call == 0)
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, pte->device->maintext0);
+ else {
+ sprintf(tmpbuf, "%d unanswered call(s)", pte->device->missed_call);
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, tmpbuf);
+ Sendicon(TEXT_LINE0, FAV_ICON_CALL_CENTER + FAV_BLINK_SLOW, pte);
+ }
+ }
+ if (ast_strlen_zero(pte->device->maintext2)) {
+ strcpy(tmpbuf, "IP : ");
+ strcat(tmpbuf, ast_inet_ntoa(pte->sin.sin_addr));
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmpbuf);
+ } else
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, pte->device->maintext2);
+ send_texttitle(pte, pte->device->titledefault);
+ change_favorite_icon(pte, FAV_ICON_ONHOOK_BLACK);
+}
+
+static void key_main_page(struct unistimsession *pte, char keycode)
+{
+ if (pte->device->missed_call) {
+ Sendicon(TEXT_LINE0, FAV_ICON_NONE, pte);
+ pte->device->missed_call = 0;
+ }
+ if ((keycode >= KEY_0) && (keycode <= KEY_SHARP)) {
+ handle_dial_page(pte);
+ key_dial_page(pte, keycode);
+ return;
+ }
+ switch (keycode) {
+ case KEY_FUNC1:
+ handle_dial_page(pte);
+ break;
+ case KEY_FUNC2:
+ if (ast_strlen_zero(pte->device->redial_number))
+ break;
+ if ((pte->device->output == OUTPUT_HANDSET) &&
+ (pte->device->receiver_state == STATE_ONHOOK))
+ send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
+ else
+ send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
+
+ ast_copy_string(pte->device->phone_number, pte->device->redial_number,
+ sizeof(pte->device->phone_number));
+ HandleCallOutgoing(pte);
+ break;
+ case KEY_FUNC3:
+ if (!ast_strlen_zero(pte->device->call_forward)) {
+ /* Cancel call forwarding */
+ memmove(pte->device->call_forward + 1, pte->device->call_forward,
+ sizeof(pte->device->call_forward));
+ pte->device->call_forward[0] = '\0';
+ Sendicon(TEXT_LINE0, FAV_ICON_NONE, pte);
+ pte->device->output = OUTPUT_HANDSET; /* Seems to be reseted somewhere */
+ show_main_page(pte);
+ break;
+ }
+ pte->device->call_forward[0] = -1;
+ handle_dial_page(pte);
+ break;
+ case KEY_FUNC4:
+ if (pte->device->extension == EXTENSION_ASK) {
+ UnregisterExtension(pte);
+ pte->device->extension_number[0] = '\0';
+ ShowExtensionPage(pte);
+ } else if (pte->device->extension == EXTENSION_TN) {
+ ast_mutex_lock(&devicelock);
+ strcpy(pte->device->id, pte->device->extension_number);
+ pte->buff_entry[0] = '\0';
+ pte->size_buff_entry = 0;
+ pte->device->session = NULL;
+ pte->device = NULL;
+ ast_mutex_unlock(&devicelock);
+ ShowExtensionPage(pte);
+ }
+ break;
+ case KEY_FAV0:
+ handle_dial_page(pte);
+ break;
+ case KEY_FAV1:
+ case KEY_FAV2:
+ case KEY_FAV3:
+ case KEY_FAV4:
+ case KEY_FAV5:
+ if ((pte->device->output == OUTPUT_HANDSET) &&
+ (pte->device->receiver_state == STATE_ONHOOK))
+ send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
+ else
+ send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
+ Keyfavorite(pte, keycode);
+ break;
+ case KEY_CONF:
+ HandleSelectCodec(pte);
+ break;
+ case KEY_LOUDSPK:
+ send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
+ handle_dial_page(pte);
+ break;
+ case KEY_HEADPHN:
+ send_select_output(pte, OUTPUT_HEADPHONE, pte->device->volume, MUTE_OFF);
+ handle_dial_page(pte);
+ break;
+ case KEY_SNDHIST:
+ show_history(pte, 'o');
+ break;
+ case KEY_RCVHIST:
+ show_history(pte, 'i');
+ break;
+ }
+ return;
+}
+
+static void key_history(struct unistimsession *pte, char keycode)
+{
+ FILE *f;
+ char count;
+ long offset;
+
+ switch (keycode) {
+ case KEY_UP:
+ case KEY_LEFT:
+ case KEY_FUNC1:
+ if (pte->buff_entry[2] <= 1)
+ return;
+ pte->buff_entry[2]--;
+ count = OpenHistory(pte, pte->buff_entry[0], &f);
+ if (!count)
+ return;
+ offset = ((pte->buff_entry[2] - 1) * TEXT_LENGTH_MAX * 3);
+ if (fseek(f, offset, SEEK_CUR)) {
+ display_last_error("Unable to seek history entry.");
+ fclose(f);
+ return;
+ }
+ show_entry_history(pte, &f);
+ break;
+ case KEY_DOWN:
+ case KEY_RIGHT:
+ case KEY_FUNC2:
+ if (pte->buff_entry[2] >= pte->buff_entry[1])
+ return;
+ pte->buff_entry[2]++;
+ count = OpenHistory(pte, pte->buff_entry[0], &f);
+ if (!count)
+ return;
+ offset = ((pte->buff_entry[2] - 1) * TEXT_LENGTH_MAX * 3);
+ if (fseek(f, offset, SEEK_CUR)) {
+ display_last_error("Unable to seek history entry.");
+ fclose(f);
+ return;
+ }
+ show_entry_history(pte, &f);
+ break;
+ case KEY_FUNC3:
+ if (!ReformatNumber(pte->device->lst_cid))
+ break;
+ ast_copy_string(pte->device->redial_number, pte->device->lst_cid,
+ sizeof(pte->device->redial_number));
+ key_main_page(pte, KEY_FUNC2);
+ break;
+ case KEY_FUNC4:
+ case KEY_HANGUP:
+ show_main_page(pte);
+ break;
+ case KEY_SNDHIST:
+ if (pte->buff_entry[0] == 'i')
+ show_history(pte, 'o');
+ else
+ show_main_page(pte);
+ break;
+ case KEY_RCVHIST:
+ if (pte->buff_entry[0] == 'i')
+ show_main_page(pte);
+ else
+ show_history(pte, 'i');
+ break;
+ }
+ return;
+}
+
+static void init_phone_step2(struct unistimsession *pte)
+{
+ BUFFSEND;
+ if (unistimdebug)
+ ast_verbose("Sending S4\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_s4, sizeof(packet_send_s4));
+ send_client(SIZE_HEADER + sizeof(packet_send_s4), buffsend, pte);
+ send_date_time2(pte);
+ send_date_time3(pte);
+ if (unistimdebug)
+ ast_verbose("Sending S7\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_S7, sizeof(packet_send_S7));
+ send_client(SIZE_HEADER + sizeof(packet_send_S7), buffsend, pte);
+ if (unistimdebug)
+ ast_verbose("Sending Contrast\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_Contrast, sizeof(packet_send_Contrast));
+ if (pte->device != NULL)
+ buffsend[9] = pte->device->contrast;
+ send_client(SIZE_HEADER + sizeof(packet_send_Contrast), buffsend, pte);
+
+ if (unistimdebug)
+ ast_verbose("Sending S9\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_s9, sizeof(packet_send_s9));
+ send_client(SIZE_HEADER + sizeof(packet_send_s9), buffsend, pte);
+ send_no_ring(pte);
+
+ if (unistimdebug)
+ ast_verbose("Sending S7\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_S7, sizeof(packet_send_S7));
+ send_client(SIZE_HEADER + sizeof(packet_send_S7), buffsend, pte);
+ send_led_update(pte, 0);
+ send_ping(pte);
+ if (pte->state < STATE_MAINPAGE) {
+ if (autoprovisioning == AUTOPROVISIONING_TN) {
+ ShowExtensionPage(pte);
+ return;
+ } else {
+ int i;
+ char tmp[30];
+
+ for (i = 1; i < 6; i++)
+ send_favorite(i, 0, pte, "");
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Sorry, this phone is not");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, "registred in unistim.cfg");
+ strcpy(tmp, "MAC = ");
+ strcat(tmp, pte->macaddr);
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmp);
+ send_text_status(pte, "");
+ send_texttitle(pte, "UNISTIM for*");
+ return;
+ }
+ }
+ show_main_page(pte);
+ refresh_all_favorite(pte);
+ if (unistimdebug)
+ ast_verbose("Sending arrow\n");
+ memcpy(buffsend + SIZE_HEADER, packet_send_arrow, sizeof(packet_send_arrow));
+ send_client(SIZE_HEADER + sizeof(packet_send_arrow), buffsend, pte);
+ return;
+}
+
+static void process_request(int size, unsigned char *buf, struct unistimsession *pte)
+{
+ char tmpbuf[255];
+ if (memcmp
+ (buf + SIZE_HEADER, packet_recv_resume_connection_with_server,
+ sizeof(packet_recv_resume_connection_with_server)) == 0) {
+ rcv_resume_connection_with_server(pte);
+ return;
+ }
+ if (memcmp(buf + SIZE_HEADER, packet_recv_firm_version, sizeof(packet_recv_firm_version)) ==
+ 0) {
+ buf[size] = 0;
+ if (unistimdebug)
+ ast_verbose("Got the firmware version : '%s'\n", buf + 13);
+ init_phone_step2(pte);
+ return;
+ }
+ if (memcmp(buf + SIZE_HEADER, packet_recv_mac_addr, sizeof(packet_recv_mac_addr)) == 0) {
+ rcv_mac_addr(pte, buf);
+ return;
+ }
+ if (memcmp(buf + SIZE_HEADER, packet_recv_r2, sizeof(packet_recv_r2)) == 0) {
+ if (unistimdebug)
+ ast_verbose("R2 received\n");
+ return;
+ }
+
+ if (pte->state < STATE_MAINPAGE) {
+ if (unistimdebug)
+ ast_verbose("Request not authorized in this state\n");
+ return;
+ }
+ if (!memcmp(buf + SIZE_HEADER, packet_recv_pressed_key, sizeof(packet_recv_pressed_key))) {
+ char keycode = buf[13];
+
+ if (unistimdebug)
+ ast_verbose("Key pressed : keycode = 0x%.2x - current state : %d\n", keycode,
+ pte->state);
+
+ switch (pte->state) {
+ case STATE_INIT:
+ if (unistimdebug)
+ ast_verbose("No keys allowed in the init state\n");
+ break;
+ case STATE_AUTHDENY:
+ if (unistimdebug)
+ ast_verbose("No keys allowed in authdeny state\n");
+ break;
+ case STATE_MAINPAGE:
+ key_main_page(pte, keycode);
+ break;
+ case STATE_DIALPAGE:
+ key_dial_page(pte, keycode);
+ break;
+ case STATE_RINGING:
+ key_ringing(pte, keycode);
+ break;
+ case STATE_CALL:
+ key_call(pte, keycode);
+ break;
+ case STATE_EXTENSION:
+ key_select_extension(pte, keycode);
+ break;
+ case STATE_SELECTCODEC:
+ key_select_codec(pte, keycode);
+ break;
+ case STATE_HISTORY:
+ key_history(pte, keycode);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Key : Unknown state\n");
+ }
+ return;
+ }
+ if (memcmp(buf + SIZE_HEADER, packet_recv_pick_up, sizeof(packet_recv_pick_up)) == 0) {
+ if (unistimdebug)
+ ast_verbose("Handset off hook\n");
+ if (!pte->device) /* We are not yet registred (asking for a TN in AUTOPROVISIONING_TN) */
+ return;
+ pte->device->receiver_state = STATE_OFFHOOK;
+ if (pte->device->output == OUTPUT_HEADPHONE)
+ send_select_output(pte, OUTPUT_HEADPHONE, pte->device->volume, MUTE_OFF);
+ else
+ send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
+ if (pte->state == STATE_RINGING)
+ HandleCallIncoming(pte);
+ else if ((pte->state == STATE_DIALPAGE) || (pte->state == STATE_CALL))
+ send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
+ else if (pte->state == STATE_EXTENSION) /* We must have a TN before calling */
+ return;
+ else {
+ send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
+ handle_dial_page(pte);
+ }
+ return;
+ }
+ if (memcmp(buf + SIZE_HEADER, packet_recv_hangup, sizeof(packet_recv_hangup)) == 0) {
+ if (unistimdebug)
+ ast_verbose("Handset on hook\n");
+ if (!pte->device)
+ return;
+ pte->device->receiver_state = STATE_ONHOOK;
+ if (pte->state == STATE_CALL)
+ close_call(pte);
+ else if (pte->device->lines->subs[SUB_REAL]->owner)
+ close_call(pte);
+ else if (pte->state == STATE_EXTENSION)
+ return;
+ else
+ show_main_page(pte);
+ return;
+ }
+ strcpy(tmpbuf, ast_inet_ntoa(pte->sin.sin_addr));
+ strcat(tmpbuf, " Unknown request packet\n");
+ if (unistimdebug)
+ ast_debug(1, "%s", tmpbuf);
+ return;
+}
+
+static void parsing(int size, unsigned char *buf, struct unistimsession *pte,
+ struct sockaddr_in *addr_from)
+{
+ unsigned short *sbuf = (unsigned short *) buf;
+ unsigned short seq;
+ char tmpbuf[255];
+
+ strcpy(tmpbuf, ast_inet_ntoa(addr_from->sin_addr));
+
+ if (size < 10) {
+ if (size == 0) {
+ ast_log(LOG_WARNING, "%s Read error\n", tmpbuf);
+ } else {
+ ast_log(LOG_NOTICE, "%s Packet too short - ignoring\n", tmpbuf);
+ }
+ return;
+ }
+ if (sbuf[0] == 0xffff) { /* Starting with 0xffff ? *//* Yes, discovery packet ? */
+ if (size != sizeof(packet_rcv_discovery)) {
+ ast_log(LOG_NOTICE, "%s Invalid size of a discovery packet\n", tmpbuf);
+ } else {
+ if (memcmp(buf, packet_rcv_discovery, sizeof(packet_rcv_discovery)) == 0) {
+ if (unistimdebug)
+ ast_verbose("Discovery packet received - Sending Discovery ACK\n");
+ if (pte) { /* A session was already active for this IP ? */
+ if (pte->state == STATE_INIT) { /* Yes, but it's a dupe */
+ if (unistimdebug)
+ ast_verbose("Duplicated Discovery packet\n");
+ send_raw_client(sizeof(packet_send_discovery_ack),
+ packet_send_discovery_ack, addr_from, &pte->sout);
+ pte->seq_phone = (short) 0x0000; /* reset sequence number */
+ } else { /* No, probably a reboot, phone side */
+ close_client(pte); /* Cleanup the previous session */
+ if (create_client(addr_from))
+ send_raw_client(sizeof(packet_send_discovery_ack),
+ packet_send_discovery_ack, addr_from, &pte->sout);
+ }
+ } else {
+ /* Creating new entry in our phone list */
+ if ((pte = create_client(addr_from)))
+ send_raw_client(sizeof(packet_send_discovery_ack),
+ packet_send_discovery_ack, addr_from, &pte->sout);
+ }
+ return;
+ }
+ ast_log(LOG_NOTICE, "%s Invalid discovery packet\n", tmpbuf);
+ }
+ return;
+ }
+ if (!pte) {
+ if (unistimdebug)
+ ast_verbose("%s Not a discovery packet from an unknown source : ignoring\n",
+ tmpbuf);
+ return;
+ }
+
+ if (sbuf[0] != 0) { /* Starting with something else than 0x0000 ? */
+ ast_log(LOG_NOTICE, "Unknown packet received - ignoring\n");
+ return;
+ }
+ if (buf[5] != 2) {
+ ast_log(LOG_NOTICE, "%s Wrong direction : got 0x%.2x expected 0x02\n", tmpbuf,
+ buf[5]);
+ return;
+ }
+ seq = ntohs(sbuf[1]);
+ if (buf[4] == 1) {
+ ast_mutex_lock(&pte->lock);
+ if ((unistimdebug) && (option_verbose > 5))
+ ast_verbose("ACK received for packet #0x%.4x\n", seq);
+ pte->nb_retransmit = 0;
+
+ if ((pte->last_seq_ack) + 1 == seq) {
+ pte->last_seq_ack++;
+ check_send_queue(pte);
+ ast_mutex_unlock(&pte->lock);
+ return;
+ }
+ if (pte->last_seq_ack > seq) {
+ if (pte->last_seq_ack == 0xffff) {
+ ast_verbose("ACK at 0xffff, restarting counter.\n");
+ pte->last_seq_ack = 0;
+ } else
+ ast_log(LOG_NOTICE,
+ "%s Warning : ACK received for an already ACKed packet : #0x%.4x we are at #0x%.4x\n",
+ tmpbuf, seq, pte->last_seq_ack);
+ ast_mutex_unlock(&pte->lock);
+ return;
+ }
+ if (pte->seq_server < seq) {
+ ast_log(LOG_NOTICE,
+ "%s Error : ACK received for a non-existant packet : #0x%.4x\n",
+ tmpbuf, pte->seq_server);
+ ast_mutex_unlock(&pte->lock);
+ return;
+ }
+ if (unistimdebug)
+ ast_verbose("%s ACK gap : Received ACK #0x%.4x, previous was #0x%.4x\n",
+ tmpbuf, seq, pte->last_seq_ack);
+ pte->last_seq_ack = seq;
+ check_send_queue(pte);
+ ast_mutex_unlock(&pte->lock);
+ return;
+ }
+ if (buf[4] == 2) {
+ if (unistimdebug)
+ ast_verbose("Request received\n");
+ if (pte->seq_phone == seq) {
+ /* Send ACK */
+ buf[4] = 1;
+ buf[5] = 1;
+ send_raw_client(SIZE_HEADER, buf, addr_from, &pte->sout);
+ pte->seq_phone++;
+
+ process_request(size, buf, pte);
+ return;
+ }
+ if (pte->seq_phone > seq) {
+ ast_log(LOG_NOTICE,
+ "%s Warning : received a retransmitted packet : #0x%.4x (we are at #0x%.4x)\n",
+ tmpbuf, seq, pte->seq_phone);
+ /* BUG ? pte->device->seq_phone = seq; */
+ /* Send ACK */
+ buf[4] = 1;
+ buf[5] = 1;
+ send_raw_client(SIZE_HEADER, buf, addr_from, &pte->sout);
+ return;
+ }
+ ast_log(LOG_NOTICE,
+ "%s Warning : we lost a packet : received #0x%.4x (we are at #0x%.4x)\n",
+ tmpbuf, seq, pte->seq_phone);
+ return;
+ }
+ if (buf[4] == 0) {
+ ast_log(LOG_NOTICE, "%s Retransmit request for packet #0x%.4x\n", tmpbuf, seq);
+ if (pte->last_seq_ack > seq) {
+ ast_log(LOG_NOTICE,
+ "%s Error : received a request for an already ACKed packet : #0x%.4x\n",
+ tmpbuf, pte->last_seq_ack);
+ return;
+ }
+ if (pte->seq_server < seq) {
+ ast_log(LOG_NOTICE,
+ "%s Error : received a request for a non-existant packet : #0x%.4x\n",
+ tmpbuf, pte->seq_server);
+ return;
+ }
+ send_retransmit(pte);
+ return;
+ }
+ ast_log(LOG_NOTICE, "%s Unknown request : got 0x%.2x expected 0x00,0x01 or 0x02\n",
+ tmpbuf, buf[4]);
+ return;
+}
+
+static struct unistimsession *channel_to_session(struct ast_channel *ast)
+{
+ struct unistim_subchannel *sub;
+ if (!ast) {
+ ast_log(LOG_WARNING, "Unistim callback function called with a null channel\n");
+ return NULL;
+ }
+ if (!ast->tech_pvt) {
+ ast_log(LOG_WARNING, "Unistim callback function called without a tech_pvt\n");
+ return NULL;
+ }
+ sub = ast->tech_pvt;
+
+ if (!sub->parent) {
+ ast_log(LOG_WARNING, "Unistim callback function called without a line\n");
+ return NULL;
+ }
+ if (!sub->parent->parent) {
+ ast_log(LOG_WARNING, "Unistim callback function called without a device\n");
+ return NULL;
+ }
+ if (!sub->parent->parent->session) {
+ ast_log(LOG_WARNING, "Unistim callback function called without a session\n");
+ return NULL;
+ }
+ return sub->parent->parent->session;
+}
+
+/*--- unistim_call: Initiate UNISTIM call from PBX ---*/
+/* used from the dial() application */
+static int unistim_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ int res = 0;
+ struct unistim_subchannel *sub;
+ struct unistimsession *session;
+
+ session = channel_to_session(ast);
+ if (!session) {
+ ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
+ return -1;
+ }
+
+ sub = ast->tech_pvt;
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "unistim_call called on %s, neither down nor reserved\n",
+ ast->name);
+ return -1;
+ }
+
+ if (unistimdebug)
+ ast_verbose(VERBOSE_PREFIX_3 "unistim_call(%s)\n", ast->name);
+
+ session->state = STATE_RINGING;
+ Sendicon(TEXT_LINE0, FAV_ICON_NONE, session);
+
+ if (sub->owner) {
+ if (sub->owner->cid.cid_num) {
+ send_text(TEXT_LINE1, TEXT_NORMAL, session, sub->owner->cid.cid_num);
+ change_callerid(session, 0, sub->owner->cid.cid_num);
+ } else {
+ send_text(TEXT_LINE1, TEXT_NORMAL, session, DEFAULTCALLERID);
+ change_callerid(session, 0, DEFAULTCALLERID);
+ }
+ if (sub->owner->cid.cid_name) {
+ send_text(TEXT_LINE0, TEXT_NORMAL, session, sub->owner->cid.cid_name);
+ change_callerid(session, 1, sub->owner->cid.cid_name);
+ } else {
+ send_text(TEXT_LINE0, TEXT_NORMAL, session, DEFAULTCALLERNAME);
+ change_callerid(session, 1, DEFAULTCALLERNAME);
+ }
+ }
+ send_text(TEXT_LINE2, TEXT_NORMAL, session, "is calling you.");
+ send_text_status(session, "Accept Ignore");
+
+ if (sub->ringstyle == -1)
+ send_ring(session, session->device->ringvolume, session->device->ringstyle);
+ else {
+ if (sub->ringvolume == -1)
+ send_ring(session, session->device->ringvolume, sub->ringstyle);
+ else
+ send_ring(session, sub->ringvolume, sub->ringstyle);
+ }
+ change_favorite_icon(session, FAV_ICON_SPEAKER_ONHOOK_BLACK + FAV_BLINK_FAST);
+
+ ast_setstate(ast, AST_STATE_RINGING);
+ ast_queue_control(ast, AST_CONTROL_RINGING);
+ return res;
+}
+
+/*--- unistim_hangup: Hangup UNISTIM call */
+static int unistim_hangup(struct ast_channel *ast)
+{
+ struct unistim_subchannel *sub;
+ struct unistim_line *l;
+ struct unistimsession *s;
+
+ s = channel_to_session(ast);
+ sub = ast->tech_pvt;
+ if (!s) {
+ ast_debug(1, "Asked to hangup channel not connected\n");
+ ast_mutex_lock(&sub->lock);
+ sub->owner = NULL;
+ ast->tech_pvt = NULL;
+ sub->alreadygone = 0;
+ ast_mutex_unlock(&sub->lock);
+ if (sub->rtp) {
+ if (unistimdebug)
+ ast_verbose("Destroying RTP session\n");
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+ return 0;
+ }
+ l = sub->parent;
+ if (unistimdebug)
+ ast_verbose("unistim_hangup(%s) on %s@%s\n", ast->name, l->name, l->parent->name);
+
+ if ((l->subs[SUB_THREEWAY]) && (sub->subtype == SUB_REAL)) {
+ if (unistimdebug)
+ ast_verbose("Real call disconnected while talking to threeway\n");
+ sub->owner = NULL;
+ ast->tech_pvt = NULL;
+ return 0;
+ }
+ if ((l->subs[SUB_REAL]->owner) && (sub->subtype == SUB_THREEWAY) &&
+ (sub->alreadygone == 0)) {
+ if (unistimdebug)
+ ast_verbose("threeway call disconnected, switching to real call\n");
+ send_text(TEXT_LINE0, TEXT_NORMAL, s, "Three way call canceled,");
+ send_text(TEXT_LINE1, TEXT_NORMAL, s, "switching back to");
+ send_text(TEXT_LINE2, TEXT_NORMAL, s, "previous call.");
+ send_text_status(s, "Hangup Transf");
+ ast_moh_stop(ast_bridged_channel(l->subs[SUB_REAL]->owner));
+ swap_subs(l, SUB_THREEWAY, SUB_REAL);
+ l->parent->moh = 0;
+ ast_mutex_lock(&sub->lock);
+ sub->owner = NULL;
+ ast->tech_pvt = NULL;
+ ast_mutex_unlock(&sub->lock);
+ unalloc_sub(l, SUB_THREEWAY);
+ return 0;
+ }
+ ast_mutex_lock(&sub->lock);
+ sub->owner = NULL;
+ ast->tech_pvt = NULL;
+ sub->alreadygone = 0;
+ ast_mutex_unlock(&sub->lock);
+ if (!s) {
+ if (unistimdebug)
+ ast_verbose("Asked to hangup channel not connected (no session)\n");
+ if (sub->rtp) {
+ if (unistimdebug)
+ ast_verbose("Destroying RTP session\n");
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+ return 0;
+ }
+ if (sub->subtype == SUB_REAL) {
+ /* Stop the silence generator */
+ if (s->device->silence_generator) {
+ if (unistimdebug)
+ ast_verbose("Stopping silence generator\n");
+ if (sub->owner)
+ ast_channel_stop_silence_generator(sub->owner,
+ s->device->silence_generator);
+ else
+ ast_log(LOG_WARNING,
+ "Trying to stop silence generator on a null channel !\n");
+ s->device->silence_generator = NULL;
+ }
+ }
+ l->parent->moh = 0;
+ send_no_ring(s);
+ send_end_call(s);
+ if (sub->rtp) {
+ if (unistimdebug)
+ ast_verbose("Destroying RTP session\n");
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ } else if (unistimdebug)
+ ast_verbose("No RTP session to destroy\n");
+ if (l->subs[SUB_THREEWAY]) {
+ if (unistimdebug)
+ ast_verbose("Cleaning other subchannels\n");
+ unalloc_sub(l, SUB_THREEWAY);
+ }
+ if (s->state == STATE_RINGING)
+ cancel_dial(s);
+ else if (s->state == STATE_CALL)
+ close_call(s);
+
+ return 0;
+}
+
+/*--- unistim_answer: Answer UNISTIM call */
+static int unistim_answer(struct ast_channel *ast)
+{
+ int res = 0;
+ struct unistim_subchannel *sub;
+ struct unistim_line *l;
+ struct unistimsession *s;
+
+ s = channel_to_session(ast);
+ if (!s) {
+ ast_log(LOG_WARNING, "unistim_answer on a disconnected device ?\n");
+ return -1;
+ }
+ sub = ast->tech_pvt;
+ l = sub->parent;
+
+ if ((!sub->rtp) && (!l->subs[SUB_THREEWAY]))
+ start_rtp(sub);
+ if (unistimdebug)
+ ast_verbose("unistim_answer(%s) on %s@%s-%d\n", ast->name, l->name,
+ l->parent->name, sub->subtype);
+ send_text(TEXT_LINE2, TEXT_NORMAL, l->parent->session, "is now on-line");
+ if (l->subs[SUB_THREEWAY])
+ send_text_status(l->parent->session, "Transf Cancel");
+ else
+ send_text_status(l->parent->session, "Hangup Transf");
+ send_start_timer(l->parent->session);
+ if (ast->_state != AST_STATE_UP)
+ ast_setstate(ast, AST_STATE_UP);
+ return res;
+}
+
+/*--- unistimsock_read: Read data from UNISTIM socket ---*/
+/* Successful messages is connected to UNISTIM call and forwarded to parsing() */
+static int unistimsock_read(int *id, int fd, short events, void *ignore)
+{
+ struct sockaddr_in addr_from = { 0, };
+ struct unistimsession *cur = NULL;
+ int found = 0;
+ int tmp = 0;
+ int dw_num_bytes_rcvd;
+#ifdef DUMP_PACKET
+ int dw_num_bytes_rcvdd;
+ char iabuf[INET_ADDRSTRLEN];
+#endif
+
+ dw_num_bytes_rcvd =
+ recvfrom(unistimsock, buff, SIZE_PAGE, 0, (struct sockaddr *) &addr_from,
+ &size_addr_from);
+ if (dw_num_bytes_rcvd == -1) {
+ if (errno == EAGAIN)
+ ast_log(LOG_NOTICE, "UNISTIM: Received packet with bad UDP checksum\n");
+ else if (errno != ECONNREFUSED)
+ ast_log(LOG_WARNING, "Recv error %d (%s)\n", errno, strerror(errno));
+ return 1;
+ }
+
+ /* Looking in the phone list if we already have a registration for him */
+ ast_mutex_lock(&sessionlock);
+ cur = sessions;
+ while (cur) {
+ if (cur->sin.sin_addr.s_addr == addr_from.sin_addr.s_addr) {
+ found = 1;
+ break;
+ }
+ tmp++;
+ cur = cur->next;
+ }
+ ast_mutex_unlock(&sessionlock);
+
+#ifdef DUMP_PACKET
+ if (unistimdebug)
+ ast_verbose("\n*** Dump %d bytes from %s - phone_table[%d] ***\n",
+ dw_num_bytes_rcvd, ast_inet_ntoa(addr_from.sin_addr), tmp);
+ for (dw_num_bytes_rcvdd = 0; dw_num_bytes_rcvdd < dw_num_bytes_rcvd;
+ dw_num_bytes_rcvdd++)
+ ast_verbose("%.2x ", (unsigned char) buff[dw_num_bytes_rcvdd]);
+ ast_verbose("\n******************************************\n");
+#endif
+
+ if (!found) {
+ if (unistimdebug)
+ ast_verbose("Received a packet from an unknown source\n");
+ parsing(dw_num_bytes_rcvd, buff, NULL, (struct sockaddr_in *) &addr_from);
+
+ } else
+ parsing(dw_num_bytes_rcvd, buff, cur, (struct sockaddr_in *) &addr_from);
+
+ return 1;
+}
+
+static struct ast_frame *unistim_rtp_read(const struct ast_channel *ast,
+ const struct unistim_subchannel *sub)
+{
+ /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */
+ struct ast_frame *f;
+
+ if (!ast) {
+ ast_log(LOG_WARNING, "Channel NULL while reading\n");
+ return &ast_null_frame;
+ }
+
+ if (!sub->rtp) {
+ ast_log(LOG_WARNING, "RTP handle NULL while reading on subchannel %d\n",
+ sub->subtype);
+ return &ast_null_frame;
+ }
+
+ switch (ast->fdno) {
+ case 0:
+ f = ast_rtp_read(sub->rtp); /* RTP Audio */
+ break;
+ case 1:
+ f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */
+ break;
+ default:
+ f = &ast_null_frame;
+ }
+
+ if (sub->owner) {
+ /* We already hold the channel lock */
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass != sub->owner->nativeformats) {
+ ast_debug(1,
+ "Oooh, format changed from %s (%d) to %s (%d)\n",
+ ast_getformatname(sub->owner->nativeformats),
+ sub->owner->nativeformats, ast_getformatname(f->subclass),
+ f->subclass);
+
+ sub->owner->nativeformats = f->subclass;
+ ast_set_read_format(sub->owner, sub->owner->readformat);
+ ast_set_write_format(sub->owner, sub->owner->writeformat);
+ }
+ }
+ }
+
+ return f;
+}
+
+static struct ast_frame *unistim_read(struct ast_channel *ast)
+{
+ struct ast_frame *fr;
+ struct unistim_subchannel *sub = ast->tech_pvt;
+
+ ast_mutex_lock(&sub->lock);
+ fr = unistim_rtp_read(ast, sub);
+ ast_mutex_unlock(&sub->lock);
+
+ return fr;
+}
+
+static int unistim_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct unistim_subchannel *sub = ast->tech_pvt;
+ int res = 0;
+
+ if (frame->frametype != AST_FRAME_VOICE) {
+ if (frame->frametype == AST_FRAME_IMAGE)
+ return 0;
+ else {
+ ast_log(LOG_WARNING, "Can't send %d type frames with unistim_write\n",
+ frame->frametype);
+ return 0;
+ }
+ } else {
+ if (!(frame->subclass & ast->nativeformats)) {
+ ast_log(LOG_WARNING,
+ "Asked to transmit frame type %s (%d), while native formats is %s (%d) (read/write = %s (%d)/%d)\n",
+ ast_getformatname(frame->subclass), frame->subclass,
+ ast_getformatname(ast->nativeformats), ast->nativeformats,
+ ast_getformatname(ast->readformat), ast->readformat,
+ ast->writeformat);
+ return -1;
+ }
+ }
+
+ if (sub) {
+ ast_mutex_lock(&sub->lock);
+ if (sub->rtp) {
+ res = ast_rtp_write(sub->rtp, frame);
+ }
+ ast_mutex_unlock(&sub->lock);
+ }
+
+ return res;
+}
+
+static int unistim_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct unistim_subchannel *p = newchan->tech_pvt;
+ struct unistim_line *l = p->parent;
+
+ ast_mutex_lock(&p->lock);
+
+ ast_debug(1, "New owner for channel USTM/%s@%s-%d is %s\n", l->name,
+ l->parent->name, p->subtype, newchan->name);
+
+ if (p->owner != oldchan) {
+ ast_log(LOG_WARNING, "old channel wasn't %s (%p) but was %s (%p)\n",
+ oldchan->name, oldchan, p->owner->name, p->owner);
+ return -1;
+ }
+
+ p->owner = newchan;
+
+ ast_mutex_unlock(&p->lock);
+
+ return 0;
+
+}
+
+static char *control2str(int ind)
+{
+ switch (ind) {
+ case AST_CONTROL_HANGUP:
+ return "Other end has hungup";
+ case AST_CONTROL_RING:
+ return "Local ring";
+ case AST_CONTROL_RINGING:
+ return "Remote end is ringing";
+ case AST_CONTROL_ANSWER:
+ return "Remote end has answered";
+ case AST_CONTROL_BUSY:
+ return "Remote end is busy";
+ case AST_CONTROL_TAKEOFFHOOK:
+ return "Make it go off hook";
+ case AST_CONTROL_OFFHOOK:
+ return "Line is off hook";
+ case AST_CONTROL_CONGESTION:
+ return "Congestion (circuits busy)";
+ case AST_CONTROL_FLASH:
+ return "Flash hook";
+ case AST_CONTROL_WINK:
+ return "Wink";
+ case AST_CONTROL_OPTION:
+ return "Set a low-level option";
+ case AST_CONTROL_RADIO_KEY:
+ return "Key Radio";
+ case AST_CONTROL_RADIO_UNKEY:
+ return "Un-Key Radio";
+ case -1:
+ return "Stop tone";
+ }
+ return "UNKNOWN";
+}
+
+static void in_band_indication(struct ast_channel *ast, const struct ind_tone_zone *tz,
+ const char *indication)
+{
+ const struct ind_tone_zone_sound *ts = NULL;
+
+ ts = ast_get_indication_tone(tz, indication);
+
+ if (ts && ts->data[0])
+ ast_playtones_start(ast, 0, ts->data, 1);
+ else
+ ast_log(LOG_WARNING, "Unable to get indication tone for %s\n", indication);
+}
+
+static int unistim_indicate(struct ast_channel *ast, int ind, const void *data,
+ size_t datalen)
+{
+ struct unistim_subchannel *sub;
+ struct unistim_line *l;
+ struct unistimsession *s;
+
+ if (unistimdebug) {
+ ast_verbose(VERBOSE_PREFIX_3 "Asked to indicate '%s' condition on channel %s\n",
+ control2str(ind), ast->name);
+ }
+
+ s = channel_to_session(ast);
+ if (!s)
+ return -1;
+
+ sub = ast->tech_pvt;
+ l = sub->parent;
+
+ switch (ind) {
+ case AST_CONTROL_RINGING:
+ if (ast->_state != AST_STATE_UP) {
+ send_text(TEXT_LINE2, TEXT_NORMAL, s, "Ringing...");
+ in_band_indication(ast, l->parent->tz, "ring");
+ s->device->missed_call = -1;
+ break;
+ }
+ return -1;
+ case AST_CONTROL_BUSY:
+ if (ast->_state != AST_STATE_UP) {
+ sub->alreadygone = 1;
+ send_text(TEXT_LINE2, TEXT_NORMAL, s, "Busy");
+ in_band_indication(ast, l->parent->tz, "busy");
+ s->device->missed_call = -1;
+ break;
+ }
+ return -1;
+ case AST_CONTROL_CONGESTION:
+ if (ast->_state != AST_STATE_UP) {
+ sub->alreadygone = 1;
+ send_text(TEXT_LINE2, TEXT_NORMAL, s, "Congestion");
+ in_band_indication(ast, l->parent->tz, "congestion");
+ s->device->missed_call = -1;
+ break;
+ }
+ return -1;
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, data, NULL);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ case AST_CONTROL_PROGRESS:
+ break;
+ case -1:
+ ast_playtones_stop(ast);
+ s->device->missed_call = 0;
+ break;
+ case AST_CONTROL_PROCEEDING:
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct unistim_subchannel *find_subchannel_by_name(const char *dest)
+{
+ struct unistim_line *l;
+ struct unistim_device *d;
+ char line[256];
+ char *at;
+ char *device;
+
+ ast_copy_string(line, dest, sizeof(line));
+ at = strchr(line, '@');
+ if (!at) {
+ ast_log(LOG_NOTICE, "Device '%s' has no @ (at) sign!\n", dest);
+ return NULL;
+ }
+ *at = '\0';
+ at++;
+ device = at;
+ ast_mutex_lock(&devicelock);
+ d = devices;
+ at = strchr(device, '/'); /* Extra options ? */
+ if (at)
+ *at = '\0';
+ while (d) {
+ if (!strcasecmp(d->name, device)) {
+ if (unistimdebug)
+ ast_verbose("Found device: %s\n", d->name);
+ /* Found the device */
+ l = d->lines;
+ while (l) {
+ /* Search for the right line */
+ if (!strcasecmp(l->name, line)) {
+ l->subs[SUB_REAL]->ringvolume = -1;
+ l->subs[SUB_REAL]->ringstyle = -1;
+ if (at) { /* Other options ? */
+ at++; /* Skip slash */
+ if (*at == 'r') { /* distinctive ring */
+ at++;
+ if ((*at < '0') || (*at > '7')) /* ring style */
+ ast_log(LOG_WARNING, "Invalid ring selection (%s)", at);
+ else {
+ char ring_volume = -1;
+ char ring_style = *at - '0';
+ at++;
+ if ((*at >= '0') && (*at <= '3')) /* ring volume */
+ ring_volume = *at - '0';
+ if (unistimdebug)
+ ast_verbose
+ ("Distinctive ring : style #%d volume %d\n",
+ ring_style, ring_volume);
+ l->subs[SUB_REAL]->ringvolume = ring_volume;
+ l->subs[SUB_REAL]->ringstyle = ring_style;
+ }
+ }
+ }
+ ast_mutex_unlock(&devicelock);
+ return l->subs[SUB_REAL];
+ }
+ l = l->next;
+ }
+ }
+ d = d->next;
+ }
+ /* Device not found */
+ ast_mutex_unlock(&devicelock);
+
+ return NULL;
+}
+
+static int unistim_senddigit_begin(struct ast_channel *ast, char digit)
+{
+ struct unistimsession *pte = channel_to_session(ast);
+
+ if (!pte)
+ return -1;
+
+ return unistim_do_senddigit(pte, digit);
+}
+
+static int unistim_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct unistimsession *pte = channel_to_session(ast);
+ struct ast_frame f = { 0, };
+ struct unistim_subchannel *sub;
+
+ sub = pte->device->lines->subs[SUB_REAL];
+
+ if (!sub->owner) {
+ ast_log(LOG_WARNING, "Unable to find subchannel in dtmf senddigiti_end\n");
+ return -1;
+ }
+
+ if (unistimdebug)
+ ast_verbose("Send Digit off %c\n", digit);
+
+ if (!pte)
+ return -1;
+
+ send_tone(pte, 0, 0);
+ f.frametype = AST_FRAME_DTMF;
+ f.subclass = digit;
+ f.src = "unistim";
+ ast_queue_frame(sub->owner, &f);
+
+ return 0;
+}
+
+/*--- unistim_sendtext: Display a text on the phone screen ---*/
+/* Called from PBX core text message functions */
+static int unistim_sendtext(struct ast_channel *ast, const char *text)
+{
+ struct unistimsession *pte = channel_to_session(ast);
+ int size;
+ char tmp[TEXT_LENGTH_MAX + 1];
+
+ if (unistimdebug)
+ ast_verbose("unistim_sendtext called\n");
+
+ if (!text) {
+ ast_log(LOG_WARNING, "unistim_sendtext called with a null text\n");
+ return 1;
+ }
+
+ size = strlen(text);
+ if (text[0] == '@') {
+ int pos = 0, i = 1, tok = 0, sz = 0;
+ char label[11];
+ char number[16];
+ char icon = '\0';
+ char cur = '\0';
+
+ memset(label, 0, 11);
+ memset(number, 0, 16);
+ while (text[i]) {
+ cur = text[i++];
+ switch (tok) {
+ case 0:
+ if ((cur < '0') && (cur > '5')) {
+ ast_log(LOG_WARNING,
+ "sendtext failed : position must be a number beetween 0 and 5\n");
+ return 1;
+ }
+ pos = cur - '0';
+ tok = 1;
+ continue;
+ case 1:
+ if (cur != '@') {
+ ast_log(LOG_WARNING, "sendtext failed : invalid position\n");
+ return 1;
+ }
+ tok = 2;
+ continue;
+ case 2:
+ if ((cur < '3') && (cur > '6')) {
+ ast_log(LOG_WARNING,
+ "sendtext failed : icon must be a number beetween 32 and 63 (first digit invalid)\n");
+ return 1;
+ }
+ icon = (cur - '0') * 10;
+ tok = 3;
+ continue;
+ case 3:
+ if ((cur < '0') && (cur > '9')) {
+ ast_log(LOG_WARNING,
+ "sendtext failed : icon must be a number beetween 32 and 63 (second digit invalid)\n");
+ return 1;
+ }
+ icon += (cur - '0');
+ tok = 4;
+ continue;
+ case 4:
+ if (cur != '@') {
+ ast_log(LOG_WARNING,
+ "sendtext failed : icon must be a number beetween 32 and 63 (too many digits)\n");
+ return 1;
+ }
+ tok = 5;
+ continue;
+ case 5:
+ if (cur == '@') {
+ tok = 6;
+ sz = 0;
+ continue;
+ }
+ if (sz > 10)
+ continue;
+ label[sz] = cur;
+ sz++;
+ continue;
+ case 6:
+ if (sz > 15) {
+ ast_log(LOG_WARNING,
+ "sendtext failed : extension too long = %d (15 car max)\n",
+ sz);
+ return 1;
+ }
+ number[sz] = cur;
+ sz++;
+ continue;
+ }
+ }
+ if (tok != 6) {
+ ast_log(LOG_WARNING, "sendtext failed : incomplet command\n");
+ return 1;
+ }
+ if (!pte->device) {
+ ast_log(LOG_WARNING, "sendtext failed : no device ?\n");
+ return 1;
+ }
+ strcpy(pte->device->softkeylabel[pos], label);
+ strcpy(pte->device->softkeynumber[pos], number);
+ pte->device->softkeyicon[pos] = icon;
+ send_favorite(pos, icon, pte, label);
+ return 0;
+ }
+
+ if (size <= TEXT_LENGTH_MAX * 2) {
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Message :");
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, text);
+ if (size <= TEXT_LENGTH_MAX) {
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, "");
+ return 0;
+ }
+ memcpy(tmp, text + TEXT_LENGTH_MAX, TEXT_LENGTH_MAX);
+ tmp[sizeof(tmp) - 1] = '\0';
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmp);
+ return 0;
+ }
+ send_text(TEXT_LINE0, TEXT_NORMAL, pte, text);
+ memcpy(tmp, text + TEXT_LENGTH_MAX, TEXT_LENGTH_MAX);
+ tmp[sizeof(tmp) - 1] = '\0';
+ send_text(TEXT_LINE1, TEXT_NORMAL, pte, tmp);
+ memcpy(tmp, text + TEXT_LENGTH_MAX * 2, TEXT_LENGTH_MAX);
+ tmp[sizeof(tmp) - 1] = '\0';
+ send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmp);
+ return 0;
+}
+
+/*--- unistim_send_mwi_to_peer: Send message waiting indication ---*/
+static int unistim_send_mwi_to_peer(struct unistimsession *s, unsigned int tick)
+{
+ struct ast_event *event;
+ int new, old;
+ char *mailbox, *context;
+ struct unistim_line *peer = s->device->lines;
+
+ context = mailbox = ast_strdupa(peer->mailbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ event = ast_event_get_cached(AST_EVENT_MWI,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_END);
+
+ if (event) {
+ new = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+ old = ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
+ ast_event_destroy(event);
+ } else /* Fall back on checking the mailbox directly */
+ ast_app_inboxcount(peer->mailbox, &new, &old);
+
+ peer->nextmsgcheck = tick + TIMER_MWI;
+
+ /* Return now if it's the same thing we told them last time */
+ if (((new << 8) | (old)) == peer->lastmsgssent)
+ return 0;
+
+ peer->lastmsgssent = ((new << 8) | (old));
+ if (new == 0)
+ send_led_update(s, 0);
+ else
+ send_led_update(s, 1);
+
+ return 0;
+}
+
+/*--- unistim_new: Initiate a call in the UNISTIM channel */
+/* called from unistim_request (calls from the pbx ) */
+static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state)
+{
+ struct ast_channel *tmp;
+ struct unistim_line *l;
+ int fmt;
+
+ if (!sub) {
+ ast_log(LOG_WARNING, "subchannel null in unistim_new\n");
+ return NULL;
+ }
+ if (!sub->parent) {
+ ast_log(LOG_WARNING, "no line for subchannel %p\n", sub);
+ return NULL;
+ }
+ l = sub->parent;
+ tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten,
+ l->context, l->amaflags, "%s-%08x", l->fullname, (int) (long) sub);
+ if (unistimdebug)
+ ast_verbose("unistim_new sub=%d (%p) chan=%p\n", sub->subtype, sub, tmp);
+ if (!tmp) {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ return NULL;
+ }
+
+ tmp->nativeformats = l->capability;
+ if (!tmp->nativeformats)
+ tmp->nativeformats = CAPABILITY;
+ fmt = ast_best_codec(tmp->nativeformats);
+ if (unistimdebug)
+ ast_verbose
+ ("Best codec = %d from nativeformats %d (line cap=%d global=%d)\n", fmt,
+ tmp->nativeformats, l->capability, CAPABILITY);
+ ast_string_field_build(tmp, name, "USTM/%s@%s-%d", l->name, l->parent->name,
+ sub->subtype);
+ if ((sub->rtp) && (sub->subtype == 0)) {
+ if (unistimdebug)
+ ast_verbose("New unistim channel with a previous rtp handle ?\n");
+ tmp->fds[0] = ast_rtp_fd(sub->rtp);
+ tmp->fds[1] = ast_rtcp_fd(sub->rtp);
+ }
+ if (sub->rtp)
+ ast_jb_configure(tmp, &global_jbconf);
+
+/* tmp->type = type; */
+ ast_setstate(tmp, state);
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->writeformat = fmt;
+ tmp->rawwriteformat = fmt;
+ tmp->readformat = fmt;
+ tmp->rawreadformat = fmt;
+ tmp->tech_pvt = sub;
+ tmp->tech = &unistim_tech;
+ if (!ast_strlen_zero(l->language))
+ ast_string_field_set(tmp, language, l->language);
+ sub->owner = tmp;
+ ast_mutex_lock(&usecnt_lock);
+ usecnt++;
+ ast_mutex_unlock(&usecnt_lock);
+ ast_update_use_count();
+ tmp->callgroup = l->callgroup;
+ tmp->pickupgroup = l->pickupgroup;
+ ast_string_field_set(tmp, call_forward, l->parent->call_forward);
+ if (!ast_strlen_zero(l->cid_num)) {
+ char *name, *loc, *instr;
+ instr = ast_strdup(l->cid_num);
+ if (instr) {
+ ast_callerid_parse(instr, &name, &loc);
+ tmp->cid.cid_num = ast_strdup(loc);
+ tmp->cid.cid_name = ast_strdup(name);
+ ast_free(instr);
+ }
+ }
+ tmp->priority = 1;
+ if (state != AST_STATE_DOWN) {
+ if (unistimdebug)
+ ast_verbose("Starting pbx in unistim_new\n");
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ tmp = NULL;
+ }
+ }
+
+ return tmp;
+}
+
+static void *do_monitor(void *data)
+{
+ struct unistimsession *cur = NULL;
+ unsigned int dw_timeout = 0;
+ unsigned int tick;
+ int res;
+ int reloading;
+
+ /* Add an I/O event to our UDP socket */
+ if (unistimsock > -1)
+ ast_io_add(io, unistimsock, unistimsock_read, AST_IO_IN, NULL);
+
+ /* This thread monitors our UDP socket and timers */
+ for (;;) {
+ /* This loop is executed at least every IDLE_WAITus (1s) or every time a packet is received */
+ /* Looking for the smallest time-out value */
+ tick = get_tick_count();
+ dw_timeout = UINT_MAX;
+ ast_mutex_lock(&sessionlock);
+ cur = sessions;
+ DEBUG_TIMER("checking timeout for session %p with tick = %u\n", cur, tick);
+ while (cur) {
+ DEBUG_TIMER("checking timeout for session %p timeout = %u\n", cur,
+ cur->timeout);
+ /* Check if we have miss something */
+ if (cur->timeout <= tick) {
+ DEBUG_TIMER("Event for session %p\n", cur);
+ /* If the queue is empty, send a ping */
+ if (cur->last_buf_available == 0)
+ send_ping(cur);
+ else {
+ if (send_retransmit(cur)) {
+ DEBUG_TIMER("The chained link was modified, restarting...\n");
+ cur = sessions;
+ dw_timeout = UINT_MAX;
+ continue;
+ }
+ }
+ }
+ if (dw_timeout > cur->timeout - tick)
+ dw_timeout = cur->timeout - tick;
+ /* Checking if the phone is logged on for a new MWI */
+ if (cur->device) {
+ if ((!ast_strlen_zero(cur->device->lines->mailbox)) &&
+ ((tick >= cur->device->lines->nextmsgcheck))) {
+ DEBUG_TIMER("Checking mailbox for MWI\n");
+ unistim_send_mwi_to_peer(cur, tick);
+ break;
+ }
+ }
+ cur = cur->next;
+ }
+ ast_mutex_unlock(&sessionlock);
+ DEBUG_TIMER("Waiting for %dus\n", dw_timeout);
+ res = dw_timeout;
+ /* We should not wait more than IDLE_WAIT */
+ if ((res < 0) || (res > IDLE_WAIT))
+ res = IDLE_WAIT;
+ /* Wait for UDP messages for a maximum of res us */
+ res = ast_io_wait(io, res); /* This function will call unistimsock_read if a packet is received */
+ /* Check for a reload request */
+ ast_mutex_lock(&unistim_reload_lock);
+ reloading = unistim_reloading;
+ unistim_reloading = 0;
+ ast_mutex_unlock(&unistim_reload_lock);
+ if (reloading) {
+ if (option_verbose > 0)
+ ast_verbose(VERBOSE_PREFIX_1 "Reloading unistim.conf...\n");
+ reload_config();
+ }
+ pthread_testcancel();
+ }
+ /* Never reached */
+ return NULL;
+}
+
+/*--- restart_monitor: Start the channel monitor thread ---*/
+static int restart_monitor(void)
+{
+ pthread_attr_t attr;
+ /* If we're supposed to be stopped -- stay stopped */
+ if (monitor_thread == AST_PTHREADT_STOP)
+ return 0;
+ if (ast_mutex_lock(&monlock)) {
+ ast_log(LOG_WARNING, "Unable to lock monitor\n");
+ return -1;
+ }
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ /* Wake up the thread */
+ pthread_kill(monitor_thread, SIGURG);
+ } else {
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ /* Start a new monitor */
+ if (ast_pthread_create(&monitor_thread, &attr, do_monitor, NULL) < 0) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+/*--- unistim_request: PBX interface function ---*/
+/* UNISTIM calls initiated by the PBX arrive here */
+static struct ast_channel *unistim_request(const char *type, int format, void *data,
+ int *cause)
+{
+ int oldformat;
+ struct unistim_subchannel *sub;
+ struct ast_channel *tmpc = NULL;
+ char tmp[256];
+ char *dest = data;
+
+ oldformat = format;
+ format &= CAPABILITY;
+ ast_log(LOG_NOTICE,
+ "Asked to get a channel of format %s while capability is %d result : %s (%d) \n",
+ ast_getformatname(oldformat), CAPABILITY, ast_getformatname(format), format);
+ if (!format) {
+ ast_log(LOG_NOTICE,
+ "Asked to get a channel of unsupported format %s while capability is %s\n",
+ ast_getformatname(oldformat), ast_getformatname(CAPABILITY));
+ return NULL;
+ }
+
+ ast_copy_string(tmp, dest, sizeof(tmp));
+ if (ast_strlen_zero(tmp)) {
+ ast_log(LOG_NOTICE, "Unistim channels require a device\n");
+ return NULL;
+ }
+
+ sub = find_subchannel_by_name(tmp);
+ if (!sub) {
+ ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
+ *cause = AST_CAUSE_CONGESTION;
+ return NULL;
+ }
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "unistim_request(%s)\n", tmp);
+ /* Busy ? */
+ if (sub->owner) {
+ if (unistimdebug)
+ ast_verbose("Can't create channel : Busy !\n");
+ *cause = AST_CAUSE_BUSY;
+ return NULL;
+ }
+ sub->parent->capability = format;
+ tmpc = unistim_new(sub, AST_STATE_DOWN);
+ if (!tmpc)
+ ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
+ if (unistimdebug)
+ ast_verbose("unistim_request owner = %p\n", sub->owner);
+ restart_monitor();
+
+ /* and finish */
+ return tmpc;
+}
+
+static char *unistim_info(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct unistim_device *device = devices;
+ struct unistim_line *line;
+ struct unistim_subchannel *sub;
+ struct unistimsession *s;
+ int i;
+ struct ast_channel *tmp;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "unistim info";
+ e->usage =
+ "Usage: unistim info\n"
+ " Dump internal structures.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL; /* no completion */
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "Dumping internal structures :\ndevice\n->line\n-->sub\n");
+ while (device) {
+ ast_cli(a->fd, "\nname=%s id=%s line=%p ha=%p sess=%p device=%p\n",
+ device->name, device->id, device->lines, device->ha, device->session,
+ device);
+ line = device->lines;
+ while (line) {
+ ast_cli(a->fd,
+ "->name=%s fullname=%s exten=%s callid=%s cap=%d device=%p line=%p\n",
+ line->name, line->fullname, line->exten, line->cid_num,
+ line->capability, line->parent, line);
+ for (i = 0; i < MAX_SUBS; i++) {
+ sub = line->subs[i];
+ if (!sub)
+ continue;
+ if (!sub->owner)
+ tmp = (void *) -42;
+ else
+ tmp = sub->owner->_bridge;
+ if (sub->subtype != i)
+ ast_cli(a->fd, "Warning ! subchannel->subs[%d] have a subtype=%d\n", i,
+ sub->subtype);
+ ast_cli(a->fd,
+ "-->subtype=%d chan=%p rtp=%p bridge=%p line=%p alreadygone=%d\n",
+ sub->subtype, sub->owner, sub->rtp, tmp, sub->parent,
+ sub->alreadygone);
+ }
+ line = line->next;
+ }
+ device = device->next;
+ }
+ ast_cli(a->fd, "\nSessions:\n");
+ ast_mutex_lock(&sessionlock);
+ s = sessions;
+ while (s) {
+ ast_cli(a->fd,
+ "sin=%s timeout=%u state=%d macaddr=%s device=%p session=%p\n",
+ ast_inet_ntoa(s->sin.sin_addr), s->timeout, s->state, s->macaddr,
+ s->device, s);
+ s = s->next;
+ }
+ ast_mutex_unlock(&sessionlock);
+
+ return CLI_SUCCESS;
+}
+
+static char *unistim_sp(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ BUFFSEND;
+ struct unistim_subchannel *sub;
+ int i, j = 0, len;
+ unsigned char c, cc;
+ char tmp[256];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "unistim sp";
+ e->usage =
+ "Usage: unistim sp USTM/line@name hexa\n"
+ " unistim sp USTM/1000@hans 19040004\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL; /* no completion */
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ if (strlen(a->argv[2]) < 9)
+ return CLI_SHOWUSAGE;
+
+ len = strlen(a->argv[3]);
+ if (len % 2)
+ return CLI_SHOWUSAGE;
+
+ ast_copy_string(tmp, a->argv[2] + 5, sizeof(tmp));
+ sub = find_subchannel_by_name(tmp);
+ if (!sub) {
+ ast_cli(a->fd, "Can't find '%s'\n", tmp);
+ return CLI_SUCCESS;
+ }
+ if (!sub->parent->parent->session) {
+ ast_cli(a->fd, "'%s' is not connected\n", tmp);
+ return CLI_SUCCESS;
+ }
+ ast_cli(a->fd, "Sending '%s' to %s (%p)\n", a->argv[3], tmp, sub->parent->parent->session);
+ for (i = 0; i < len; i++) {
+ c = a->argv[3][i];
+ if (c >= 'a')
+ c -= 'a' - 10;
+ else
+ c -= '0';
+ i++;
+ cc = a->argv[3][i];
+ if (cc >= 'a')
+ cc -= 'a' - 10;
+ else
+ cc -= '0';
+ tmp[j++] = (c << 4) | cc;
+ }
+ memcpy(buffsend + SIZE_HEADER, tmp, j);
+ send_client(SIZE_HEADER + j, buffsend, sub->parent->parent->session);
+ return CLI_SUCCESS;
+}
+
+static char *unistim_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "unistim set debug {on|off}";
+ e->usage =
+ "Usage: unistim set debug\n"
+ " Display debug messages.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL; /* no completion */
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ if (!strcasecmp(a->argv[3], "on")) {
+ unistimdebug = 1;
+ ast_cli(a->fd, "UNISTIM Debugging Enabled\n");
+ } else if (!strcasecmp(a->argv[3], "off")) {
+ unistimdebug = 0;
+ ast_cli(a->fd, "UNISTIM Debugging Disabled\n");
+ } else
+ return CLI_SHOWUSAGE;
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief --- unistim_reload: Force reload of module from cli ---
+ * Runs in the asterisk main thread, so don't do anything useful
+ * but setting a flag and waiting for do_monitor to do the job
+ * in our thread */
+static char *unistim_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "unistim reload";
+ e->usage =
+ "Usage: unistim reload\n"
+ " Reloads UNISTIM configuration from unistim.conf\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL; /* no completion */
+ }
+
+ if (e && a && a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ if (unistimdebug)
+ ast_verbose("reload unistim\n");
+
+ ast_mutex_lock(&unistim_reload_lock);
+ if (!unistim_reloading)
+ unistim_reloading = 1;
+ ast_mutex_unlock(&unistim_reload_lock);
+
+ restart_monitor();
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry unistim_cli[] = {
+ AST_CLI_DEFINE(unistim_reload, "Reload UNISTIM configuration"),
+ AST_CLI_DEFINE(unistim_info, "Show UNISTIM info"),
+ AST_CLI_DEFINE(unistim_sp, "Send packet (for reverse engineering)"),
+ AST_CLI_DEFINE(unistim_do_debug, "Toggle UNITSTIM debugging"),
+};
+
+static void unquote(char *out, const char *src, int maxlen)
+{
+ int len = strlen(src);
+ if (!len)
+ return;
+ if ((len > 1) && src[0] == '\"') {
+ /* This is a quoted string */
+ src++;
+ /* Don't take more than what's there */
+ len--;
+ if (maxlen > len - 1)
+ maxlen = len - 1;
+ memcpy(out, src, maxlen);
+ ((char *) out)[maxlen] = '\0';
+ } else
+ memcpy(out, src, maxlen);
+ return;
+}
+
+static int ParseBookmark(const char *text, struct unistim_device *d)
+{
+ char line[256];
+ char *at;
+ char *number;
+ char *icon;
+ int p;
+ int len = strlen(text);
+
+ ast_copy_string(line, text, sizeof(line));
+ /* Position specified ? */
+ if ((len > 2) && (line[1] == '@')) {
+ p = line[0];
+ if ((p >= '0') && (p <= '5'))
+ p -= '0';
+ else {
+ ast_log(LOG_WARNING,
+ "Invalid position for bookmark : must be between 0 and 5\n");
+ return 0;
+ }
+ if (d->softkeyicon[p] != 0) {
+ ast_log(LOG_WARNING, "Invalid position %d for bookmark : already used\n:", p);
+ return 0;
+ }
+ memmove(line, line + 2, sizeof(line));
+ } else {
+ /* No position specified, looking for a free slot */
+ for (p = 0; p <= 5; p++) {
+ if (!d->softkeyicon[p])
+ break;
+ }
+ if (p > 5) {
+ ast_log(LOG_WARNING, "No more free bookmark position\n");
+ return 0;
+ }
+ }
+ at = strchr(line, '@');
+ if (!at) {
+ ast_log(LOG_NOTICE, "Bookmark entry '%s' has no @ (at) sign!\n", text);
+ return 0;
+ }
+ *at = '\0';
+ at++;
+ number = at;
+ at = strchr(at, '@');
+ if (ast_strlen_zero(number)) {
+ ast_log(LOG_NOTICE, "Bookmark entry '%s' has no number\n", text);
+ return 0;
+ }
+ if (ast_strlen_zero(line)) {
+ ast_log(LOG_NOTICE, "Bookmark entry '%s' has no description\n", text);
+ return 0;
+ }
+
+ at = strchr(number, '@');
+ if (!at)
+ d->softkeyicon[p] = FAV_ICON_SHARP; /* default icon */
+ else {
+ *at = '\0';
+ at++;
+ icon = at;
+ if (ast_strlen_zero(icon)) {
+ ast_log(LOG_NOTICE, "Bookmark entry '%s' has no icon value\n", text);
+ return 0;
+ }
+ if (strncmp(icon, "USTM/", 5))
+ d->softkeyicon[p] = atoi(icon);
+ else {
+ d->softkeyicon[p] = 1;
+ ast_copy_string(d->softkeydevice[p], icon + 5, sizeof(d->softkeydevice[p]));
+ }
+ }
+ ast_copy_string(d->softkeylabel[p], line, sizeof(d->softkeylabel[p]));
+ ast_copy_string(d->softkeynumber[p], number, sizeof(d->softkeynumber[p]));
+ if (unistimdebug)
+ ast_verbose("New bookmark at pos %d label='%s' number='%s' icon=%x\n",
+ p, d->softkeylabel[p], d->softkeynumber[p], d->softkeyicon[p]);
+ return 1;
+}
+
+/* Looking for dynamic icons entries in bookmarks */
+static void finish_bookmark(void)
+{
+ struct unistim_device *d = devices;
+ int i;
+ while (d) {
+ for (i = 0; i < 6; i++) {
+ if (d->softkeyicon[i] == 1) { /* Something for us */
+ struct unistim_device *d2 = devices;
+ while (d2) {
+ if (!strcmp(d->softkeydevice[i], d2->name)) {
+ d->sp[i] = d2;
+ d->softkeyicon[i] = 0;
+ break;
+ }
+ d2 = d2->next;
+ }
+ if (d->sp[i] == NULL)
+ ast_log(LOG_NOTICE, "Bookmark entry with device %s not found\n",
+ d->softkeydevice[i]);
+ }
+ }
+ d = d->next;
+ }
+}
+
+static struct unistim_device *build_device(const char *cat, const struct ast_variable *v)
+{
+ struct unistim_device *d;
+ struct unistim_line *l = NULL;
+ int create = 1;
+ int nbsoftkey, dateformat, timeformat, callhistory;
+ char linelabel[AST_MAX_EXTENSION];
+ char context[AST_MAX_EXTENSION];
+ char ringvolume, ringstyle;
+
+ /* First, we need to know if we already have this name in our list */
+ /* Get a lock for the device chained list */
+ ast_mutex_lock(&devicelock);
+ d = devices;
+ while (d) {
+ if (!strcmp(d->name, cat)) {
+ /* Yep, we alreay have this one */
+ if (unistimsock < 0) {
+ /* It's a dupe */
+ ast_log(LOG_WARNING, "Duplicate entry found (%s), ignoring.\n", cat);
+ ast_mutex_unlock(&devicelock);
+ return NULL;
+ }
+ /* we're reloading right now */
+ create = 0;
+ l = d->lines;
+ break;
+ }
+ d = d->next;
+ }
+ ast_mutex_unlock(&devicelock);
+ if (create) {
+ if (!(d = ast_calloc(1, sizeof(*d))))
+ return NULL;
+
+ if (!(l = ast_calloc(1, sizeof(*l)))) {
+ ast_free(d);
+ return NULL;
+ }
+ ast_copy_string(d->name, cat, sizeof(d->name));
+ }
+ ast_copy_string(context, DEFAULTCONTEXT, sizeof(context));
+ d->contrast = -1;
+ d->output = OUTPUT_HANDSET;
+ d->previous_output = OUTPUT_HANDSET;
+ d->volume = VOLUME_LOW;
+ d->mute = MUTE_OFF;
+ linelabel[0] = '\0';
+ dateformat = 1;
+ timeformat = 1;
+ ringvolume = 2;
+ callhistory = 1;
+ ringstyle = 3;
+ nbsoftkey = 0;
+ while (v) {
+ if (!strcasecmp(v->name, "rtp_port"))
+ d->rtp_port = atoi(v->value);
+ else if (!strcasecmp(v->name, "rtp_method"))
+ d->rtp_method = atoi(v->value);
+ else if (!strcasecmp(v->name, "status_method"))
+ d->status_method = atoi(v->value);
+ else if (!strcasecmp(v->name, "device"))
+ ast_copy_string(d->id, v->value, sizeof(d->id));
+ else if (!strcasecmp(v->name, "tn"))
+ ast_copy_string(d->extension_number, v->value, sizeof(d->extension_number));
+ else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny"))
+ d->ha = ast_append_ha(v->name, v->value, d->ha, NULL);
+ else if (!strcasecmp(v->name, "context"))
+ ast_copy_string(context, v->value, sizeof(context));
+ else if (!strcasecmp(v->name, "maintext0"))
+ unquote(d->maintext0, v->value, sizeof(d->maintext0) - 1);
+ else if (!strcasecmp(v->name, "maintext1"))
+ unquote(d->maintext1, v->value, sizeof(d->maintext1) - 1);
+ else if (!strcasecmp(v->name, "maintext2"))
+ unquote(d->maintext2, v->value, sizeof(d->maintext2) - 1);
+ else if (!strcasecmp(v->name, "titledefault"))
+ unquote(d->titledefault, v->value, sizeof(d->titledefault) - 1);
+ else if (!strcasecmp(v->name, "dateformat"))
+ dateformat = atoi(v->value);
+ else if (!strcasecmp(v->name, "timeformat"))
+ timeformat = atoi(v->value);
+ else if (!strcasecmp(v->name, "contrast")) {
+ d->contrast = atoi(v->value);
+ if ((d->contrast < 0) || (d->contrast > 15)) {
+ ast_log(LOG_WARNING, "constrast must be beetween 0 and 15");
+ d->contrast = 8;
+ }
+ } else if (!strcasecmp(v->name, "nat"))
+ d->nat = ast_true(v->value);
+ else if (!strcasecmp(v->name, "ringvolume"))
+ ringvolume = atoi(v->value);
+ else if (!strcasecmp(v->name, "ringstyle"))
+ ringstyle = atoi(v->value);
+ else if (!strcasecmp(v->name, "callhistory"))
+ callhistory = atoi(v->value);
+ else if (!strcasecmp(v->name, "callerid")) {
+ if (!strcasecmp(v->value, "asreceived"))
+ l->cid_num[0] = '\0';
+ else
+ ast_copy_string(l->cid_num, v->value, sizeof(l->cid_num));
+ } else if (!strcasecmp(v->name, "language"))
+ ast_copy_string(l->language, v->value, sizeof(l->language));
+ else if (!strcasecmp(v->name, "country"))
+ ast_copy_string(d->country, v->value, sizeof(d->country));
+ else if (!strcasecmp(v->name, "accountcode"))
+ ast_copy_string(l->accountcode, v->value, sizeof(l->accountcode));
+ else if (!strcasecmp(v->name, "amaflags")) {
+ int y;
+ y = ast_cdr_amaflags2int(v->value);
+ if (y < 0)
+ ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value,
+ v->lineno);
+ else
+ l->amaflags = y;
+ } else if (!strcasecmp(v->name, "musiconhold"))
+ ast_copy_string(l->musicclass, v->value, sizeof(l->musicclass));
+ else if (!strcasecmp(v->name, "callgroup"))
+ l->callgroup = ast_get_group(v->value);
+ else if (!strcasecmp(v->name, "pickupgroup"))
+ l->pickupgroup = ast_get_group(v->value);
+ else if (!strcasecmp(v->name, "mailbox"))
+ ast_copy_string(l->mailbox, v->value, sizeof(l->mailbox));
+ else if (!strcasecmp(v->name, "linelabel"))
+ unquote(linelabel, v->value, sizeof(linelabel) - 1);
+ else if (!strcasecmp(v->name, "extension")) {
+ if (!strcasecmp(v->value, "none"))
+ d->extension = EXTENSION_NONE;
+ else if (!strcasecmp(v->value, "ask"))
+ d->extension = EXTENSION_ASK;
+ else if (!strcasecmp(v->value, "line"))
+ d->extension = EXTENSION_LINE;
+ else
+ ast_log(LOG_WARNING, "Unknown extension option.\n");
+ } else if (!strcasecmp(v->name, "bookmark")) {
+ if (nbsoftkey > 5)
+ ast_log(LOG_WARNING,
+ "More than 6 softkeys defined. Ignoring new entries.\n");
+ else {
+ if (ParseBookmark(v->value, d))
+ nbsoftkey++;
+ }
+ } else if (!strcasecmp(v->name, "line")) {
+ int len = strlen(linelabel);
+
+ if (nbsoftkey) {
+ ast_log(LOG_WARNING,
+ "You must use bookmark AFTER line=>. Only one line is supported in this version\n");
+ if (create) {
+ ast_free(d);
+ ast_free(l);
+ }
+ return NULL;
+ }
+ if (create) {
+ ast_mutex_init(&l->lock);
+ } else {
+ d->to_delete = 0;
+ /* reset bookmarks */
+ memset(d->softkeylabel, 0, sizeof(d->softkeylabel));
+ memset(d->softkeynumber, 0, sizeof(d->softkeynumber));
+ memset(d->softkeyicon, 0, sizeof(d->softkeyicon));
+ memset(d->softkeydevice, 0, sizeof(d->softkeydevice));
+ memset(d->sp, 0, sizeof(d->sp));
+ }
+ ast_copy_string(l->name, v->value, sizeof(l->name));
+ snprintf(l->fullname, sizeof(l->fullname), "USTM/%s@%s", l->name, d->name);
+ d->softkeyicon[0] = FAV_ICON_ONHOOK_BLACK;
+ if (!len) /* label is undefined ? */
+ ast_copy_string(d->softkeylabel[0], v->value, sizeof(d->softkeylabel[0]));
+ else {
+ if ((len > 2) && (linelabel[1] == '@')) {
+ d->softkeylinepos = linelabel[0];
+ if ((d->softkeylinepos >= '0') && (d->softkeylinepos <= '5')) {
+ d->softkeylinepos -= '0';
+ d->softkeyicon[0] = 0;
+ } else {
+ ast_log(LOG_WARNING,
+ "Invalid position for linelabel : must be between 0 and 5\n");
+ d->softkeylinepos = 0;
+ }
+ ast_copy_string(d->softkeylabel[d->softkeylinepos], linelabel + 2,
+ sizeof(d->softkeylabel[d->softkeylinepos]));
+ d->softkeyicon[d->softkeylinepos] = FAV_ICON_ONHOOK_BLACK;
+ } else
+ ast_copy_string(d->softkeylabel[0], linelabel,
+ sizeof(d->softkeylabel[0]));
+ }
+ nbsoftkey++;
+ ast_copy_string(l->context, context, sizeof(l->context));
+ if (!ast_strlen_zero(l->mailbox)) {
+ if (unistimdebug)
+ ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n",
+ l->mailbox, d->name, l->name);
+ }
+
+ l->capability = CAPABILITY;
+ l->parent = d;
+
+ if (create) {
+ if (!alloc_sub(l, SUB_REAL)) {
+ ast_mutex_destroy(&l->lock);
+ ast_free(l);
+ ast_free(d);
+ return NULL;
+ }
+ l->next = d->lines;
+ d->lines = l;
+ }
+ } else
+ ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name,
+ v->lineno);
+ v = v->next;
+ }
+ d->ringvolume = ringvolume;
+ d->ringstyle = ringstyle;
+ d->callhistory = callhistory;
+ d->tz = ast_get_indication_zone(d->country);
+ if ((d->tz == NULL) && !ast_strlen_zero(d->country))
+ ast_log(LOG_WARNING, "Country '%s' was not found in indications.conf\n",
+ d->country);
+ d->datetimeformat = 56 + (dateformat * 4);
+ d->datetimeformat += timeformat;
+ if (!d->lines) {
+ ast_log(LOG_ERROR, "An Unistim device must have at least one line!\n");
+ ast_mutex_destroy(&l->lock);
+ ast_free(l);
+ ast_free(d);
+ return NULL;
+ }
+ if ((autoprovisioning == AUTOPROVISIONING_TN) &&
+ (!ast_strlen_zero(d->extension_number))) {
+ d->extension = EXTENSION_TN;
+ if (!ast_strlen_zero(d->id))
+ ast_log(LOG_WARNING,
+ "tn= and device= can't be used together. Ignoring device= entry\n");
+ d->id[0] = 'T'; /* magic : this is a tn entry */
+ ast_copy_string((d->id) + 1, d->extension_number, sizeof(d->id) - 1);
+ d->extension_number[0] = '\0';
+ } else if (ast_strlen_zero(d->id)) {
+ if (strcmp(d->name, "template")) {
+ ast_log(LOG_ERROR, "You must specify the mac address with device=\n");
+ ast_mutex_destroy(&l->lock);
+ ast_free(l);
+ ast_free(d);
+ return NULL;
+ } else
+ strcpy(d->id, "000000000000");
+ }
+ if (!d->rtp_port)
+ d->rtp_port = 10000;
+ if (d->contrast == -1)
+ d->contrast = 8;
+ if (ast_strlen_zero(d->maintext0))
+ strcpy(d->maintext0, "Welcome");
+ if (ast_strlen_zero(d->maintext1))
+ strcpy(d->maintext1, d->name);
+ if (ast_strlen_zero(d->titledefault)) {
+ struct ast_tm tm = { 0, };
+ struct timeval cur_time = ast_tvnow();
+
+ if ((ast_localtime(&cur_time, &tm, 0)) == 0 || ast_strlen_zero(tm.tm_zone)) {
+ display_last_error("Error in ast_localtime()");
+ ast_copy_string(d->titledefault, "UNISTIM for*", 12);
+ } else {
+ if (strlen(tm.tm_zone) < 4) {
+ strcpy(d->titledefault, "TimeZone ");
+ strcat(d->titledefault, tm.tm_zone);
+ } else if (strlen(tm.tm_zone) < 9) {
+ strcpy(d->titledefault, "TZ ");
+ strcat(d->titledefault, tm.tm_zone);
+ } else
+ ast_copy_string(d->titledefault, tm.tm_zone, 12);
+ }
+ }
+ /* Update the chained link if it's a new device */
+ if (create) {
+ ast_mutex_lock(&devicelock);
+ d->next = devices;
+ devices = d;
+ ast_mutex_unlock(&devicelock);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Added device '%s'\n", d->name);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Device '%s' reloaded\n", d->name);
+ }
+ return d;
+}
+
+/*--- reload_config: Re-read unistim.conf config file ---*/
+static int reload_config(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ struct sockaddr_in bindaddr = { 0, };
+ char *config = "unistim.conf";
+ char *cat;
+ struct unistim_device *d;
+ const int reuseFlag = 1;
+ struct unistimsession *s;
+ struct ast_flags config_flags = { 0, };
+
+ cfg = ast_config_load(config, config_flags);
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+ return -1;
+ }
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ unistim_keepalive = 120;
+ unistim_port = 0;
+ v = ast_variable_browse(cfg, "general");
+ while (v) {
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+ continue;
+
+ if (!strcasecmp(v->name, "keepalive"))
+ unistim_keepalive = atoi(v->value);
+ else if (!strcasecmp(v->name, "port"))
+ unistim_port = atoi(v->value);
+ else if (!strcasecmp(v->name, "tos")) {
+ if (ast_str2tos(v->value, &tos))
+ ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "tos_audio")) {
+ if (ast_str2tos(v->value, &tos_audio))
+ ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos")) {
+ if (ast_str2cos(v->value, &cos))
+ ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "cos_audio")) {
+ if (ast_str2cos(v->value, &cos_audio))
+ ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "autoprovisioning")) {
+ if (!strcasecmp(v->value, "no"))
+ autoprovisioning = AUTOPROVISIONING_NO;
+ else if (!strcasecmp(v->value, "yes"))
+ autoprovisioning = AUTOPROVISIONING_YES;
+ else if (!strcasecmp(v->value, "db"))
+ autoprovisioning = AUTOPROVISIONING_DB;
+ else if (!strcasecmp(v->value, "tn"))
+ autoprovisioning = AUTOPROVISIONING_TN;
+ else
+ ast_log(LOG_WARNING, "Unknown autoprovisioning option.\n");
+ } else if (!strcasecmp(v->name, "public_ip")) {
+ if (!ast_strlen_zero(v->value)) {
+ if (!(hp = ast_gethostbyname(v->value, &ahp)))
+ ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
+ else {
+ memcpy(&public_ip.sin_addr, hp->h_addr, sizeof(public_ip.sin_addr));
+ public_ip.sin_family = AF_INET;
+ }
+ }
+ }
+ v = v->next;
+ }
+ if ((unistim_keepalive < 10) ||
+ (unistim_keepalive >
+ 255 - (((NB_MAX_RETRANSMIT + 1) * RETRANSMIT_TIMER) / 1000))) {
+ ast_log(LOG_ERROR, "keepalive is invalid in %s\n", config);
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ packet_send_ping[4] =
+ unistim_keepalive + (((NB_MAX_RETRANSMIT + 1) * RETRANSMIT_TIMER) / 1000);
+ if ((unistim_port < 1) || (unistim_port > 65535)) {
+ ast_log(LOG_ERROR, "port is not set or invalid in %s\n", config);
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ unistim_keepalive *= 1000;
+
+ ast_mutex_lock(&devicelock);
+ d = devices;
+ while (d) {
+ if (d->to_delete >= 0)
+ d->to_delete = 1;
+ d = d->next;
+ }
+ ast_mutex_unlock(&devicelock);
+ /* load the device sections */
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ d = build_device(cat, ast_variable_browse(cfg, cat));
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_mutex_lock(&devicelock);
+ d = devices;
+ while (d) {
+ if (d->to_delete) {
+ int i;
+
+ if (unistimdebug)
+ ast_verbose("Removing device '%s'\n", d->name);
+ if (!d->lines) {
+ ast_log(LOG_ERROR, "Device '%s' without a line !, aborting\n", d->name);
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ if (!d->lines->subs[0]) {
+ ast_log(LOG_ERROR, "Device '%s' without a subchannel !, aborting\n",
+ d->name);
+ ast_config_destroy(cfg);
+ return 0;
+ }
+ if (d->lines->subs[0]->owner) {
+ ast_log(LOG_WARNING,
+ "Device '%s' was not deleted : a call is in progress. Try again later.\n",
+ d->name);
+ d = d->next;
+ continue;
+ }
+ ast_mutex_destroy(&d->lines->subs[0]->lock);
+ ast_free(d->lines->subs[0]);
+ for (i = 1; i < MAX_SUBS; i++) {
+ if (d->lines->subs[i]) {
+ ast_log(LOG_WARNING,
+ "Device '%s' with threeway call subchannels allocated, aborting.\n",
+ d->name);
+ break;
+ }
+ }
+ if (i < MAX_SUBS) {
+ d = d->next;
+ continue;
+ }
+ ast_mutex_destroy(&d->lines->lock);
+ ast_free(d->lines);
+ if (d->session) {
+ if (sessions == d->session)
+ sessions = d->session->next;
+ else {
+ s = sessions;
+ while (s) {
+ if (s->next == d->session) {
+ s->next = d->session->next;
+ break;
+ }
+ s = s->next;
+ }
+ }
+ ast_mutex_destroy(&d->session->lock);
+ ast_free(d->session);
+ }
+ if (devices == d)
+ devices = d->next;
+ else {
+ struct unistim_device *d2 = devices;
+ while (d2) {
+ if (d2->next == d) {
+ d2->next = d->next;
+ break;
+ }
+ d2 = d2->next;
+ }
+ }
+ ast_free(d);
+ d = devices;
+ continue;
+ }
+ d = d->next;
+ }
+ finish_bookmark();
+ ast_mutex_unlock(&devicelock);
+ ast_config_destroy(cfg);
+ ast_mutex_lock(&sessionlock);
+ s = sessions;
+ while (s) {
+ if (s->device)
+ refresh_all_favorite(s);
+ s = s->next;
+ }
+ ast_mutex_unlock(&sessionlock);
+ /* We don't recreate a socket when reloading (locks would be necessary). */
+ if (unistimsock > -1)
+ return 0;
+ bindaddr.sin_addr.s_addr = INADDR_ANY;
+ bindaddr.sin_port = htons(unistim_port);
+ bindaddr.sin_family = AF_INET;
+ unistimsock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (unistimsock < 0) {
+ ast_log(LOG_WARNING, "Unable to create UNISTIM socket: %s\n", strerror(errno));
+ return -1;
+ }
+#ifdef HAVE_PKTINFO
+ {
+ const int pktinfoFlag = 1;
+ setsockopt(unistimsock, IPPROTO_IP, IP_PKTINFO, &pktinfoFlag,
+ sizeof(pktinfoFlag));
+ }
+#else
+ if (public_ip.sin_family == 0) {
+ ast_log(LOG_WARNING,
+ "Your OS does not support IP_PKTINFO, you must set public_ip.\n");
+ unistimsock = -1;
+ return -1;
+ }
+#endif
+ setsockopt(unistimsock, SOL_SOCKET, SO_REUSEADDR, (const char *) &reuseFlag,
+ sizeof(reuseFlag));
+ if (bind(unistimsock, (struct sockaddr *) &bindaddr, sizeof(bindaddr)) < 0) {
+ ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
+ ast_inet_ntoa(bindaddr.sin_addr), htons(bindaddr.sin_port),
+ strerror(errno));
+ close(unistimsock);
+ unistimsock = -1;
+ } else {
+ if (option_verbose > 1) {
+ ast_verbose(VERBOSE_PREFIX_2
+ "UNISTIM Listening on %s:%d\n",
+ ast_inet_ntoa(bindaddr.sin_addr), htons(bindaddr.sin_port));
+ }
+ ast_netsock_set_qos(unistimsock, tos, cos, "UNISTIM");
+ }
+ return 0;
+}
+
+static enum ast_rtp_get_result unistim_get_vrtp_peer(struct ast_channel *chan,
+ struct ast_rtp **rtp)
+{
+ return AST_RTP_TRY_NATIVE;
+}
+
+static enum ast_rtp_get_result unistim_get_rtp_peer(struct ast_channel *chan,
+ struct ast_rtp **rtp)
+{
+ struct unistim_subchannel *sub;
+ enum ast_rtp_get_result res = AST_RTP_GET_FAILED;
+
+ if (unistimdebug)
+ ast_verbose("unistim_get_rtp_peer called\n");
+
+ sub = chan->tech_pvt;
+ if (sub && sub->rtp) {
+ *rtp = sub->rtp;
+ res = AST_RTP_TRY_NATIVE;
+ }
+
+ return res;
+}
+
+static int unistim_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp,
+ struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+{
+ struct unistim_subchannel *sub;
+
+ if (unistimdebug)
+ ast_verbose("unistim_set_rtp_peer called\n");
+
+ sub = chan->tech_pvt;
+
+ if (sub)
+ return 0;
+
+ return -1;
+}
+
+static struct ast_rtp_protocol unistim_rtp = {
+ .type = type,
+ .get_rtp_info = unistim_get_rtp_peer,
+ .get_vrtp_info = unistim_get_vrtp_peer,
+ .set_rtp_peer = unistim_set_rtp_peer,
+};
+
+/*--- load_module: PBX load module - initialization ---*/
+int load_module(void)
+{
+ int res;
+
+ if (!(buff = ast_malloc(SIZE_PAGE)))
+ goto buff_failed;
+
+ io = io_context_create();
+ if (!io) {
+ ast_log(LOG_ERROR, "Failed to allocate IO context\n");
+ goto io_failed;
+ }
+
+ sched = sched_context_create();
+ if (!sched) {
+ ast_log(LOG_ERROR, "Failed to allocate scheduler context\n");
+ goto sched_failed;
+ }
+
+ res = reload_config();
+ if (res)
+ return AST_MODULE_LOAD_DECLINE;
+
+ /* Make sure we can register our unistim channel type */
+ if (ast_channel_register(&unistim_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel type '%s'\n", type);
+ goto chanreg_failed;
+ }
+
+ ast_rtp_proto_register(&unistim_rtp);
+
+ ast_cli_register_multiple(unistim_cli, ARRAY_LEN(unistim_cli));
+
+ restart_monitor();
+
+ return AST_MODULE_LOAD_SUCCESS;
+
+chanreg_failed:
+ /*! XXX \todo Leaking anything allocated by reload_config() ... */
+ sched_context_destroy(sched);
+ sched = NULL;
+sched_failed:
+ io_context_destroy(io);
+ io = NULL;
+io_failed:
+ ast_free(buff);
+ buff = NULL;
+buff_failed:
+ return AST_MODULE_LOAD_FAILURE;
+}
+
+static int unload_module(void)
+{
+ /* First, take us out of the channel loop */
+ if (sched)
+ sched_context_destroy(sched);
+
+ ast_cli_unregister_multiple(unistim_cli, ARRAY_LEN(unistim_cli));
+
+ ast_channel_unregister(&unistim_tech);
+ ast_rtp_proto_unregister(&unistim_rtp);
+
+ ast_mutex_lock(&monlock);
+ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
+ pthread_cancel(monitor_thread);
+ pthread_kill(monitor_thread, SIGURG);
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+
+ if (buff)
+ ast_free(buff);
+ if (unistimsock > -1)
+ close(unistimsock);
+
+ return 0;
+}
+
+/*! reload: Part of Asterisk module interface ---*/
+int reload(void)
+{
+ unistim_reload(NULL, 0, NULL);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "UNISTIM Protocol (USTM)",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/trunk/channels/chan_usbradio.c b/trunk/channels/chan_usbradio.c
new file mode 100644
index 000000000..9ebfa44e8
--- /dev/null
+++ b/trunk/channels/chan_usbradio.c
@@ -0,0 +1,2494 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ * Copyright (C) 2007, Jim Dixon
+ *
+ * Jim Dixon, WB6NIL <jim@lambdatel.com>
+ * Steve Henke, W9SH <w9sh@arrl.net>
+ * Based upon work by Mark Spencer <markster@digium.com> and Luigi Rizzo
+ *
+ * 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 Channel driver for CM108 USB Cards with Radio Interface
+ *
+ * \author Jim Dixon <jim@lambdatel.com>
+ * \author Steve Henke <w9sh@arrl.net>
+ *
+ * \par See also
+ * \arg \ref Config_usbradio
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>asound</depend>
+ <depend>usb</depend>
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <usb.h>
+#include <alsa/asoundlib.h>
+
+#define CHAN_USBRADIO 1
+
+#define DEBUG_USBRADIO 0
+#define DEBUG_CAPTURES 1
+
+#define DEBUG_CAP_RX_OUT 0
+#define DEBUG_CAP_TX_OUT 0
+
+#define DEBUG_FILETEST 0
+
+#define RX_CAP_RAW_FILE "/tmp/rx_cap_in.pcm"
+#define RX_CAP_TRACE_FILE "/tmp/rx_trace.pcm"
+#define RX_CAP_OUT_FILE "/tmp/rx_cap_out.pcm"
+
+#define TX_CAP_RAW_FILE "/tmp/tx_cap_in.pcm"
+#define TX_CAP_TRACE_FILE "/tmp/tx_trace.pcm"
+#define TX_CAP_OUT_FILE "/tmp/tx_cap_out.pcm"
+
+#define MIXER_PARAM_MIC_PLAYBACK_SW "Mic Playback Switch"
+#define MIXER_PARAM_MIC_PLAYBACK_VOL "Mic Playback Volume"
+#define MIXER_PARAM_MIC_CAPTURE_SW "Mic Capture Switch"
+#define MIXER_PARAM_MIC_CAPTURE_VOL "Mic Capture Volume"
+#define MIXER_PARAM_MIC_BOOST "Auto Gain Control"
+#define MIXER_PARAM_SPKR_PLAYBACK_SW "Speaker Playback Switch"
+#define MIXER_PARAM_SPKR_PLAYBACK_VOL "Speaker Playback Volume"
+
+#include "./xpmr/xpmr.h"
+
+#if 0
+#define traceusb1(a, ...) ast_debug(4, a __VA_ARGS__)
+#else
+#define traceusb1(a, ...)
+#endif
+
+#if 0
+#define traceusb2(a, ...) ast_debug(4, a __VA_ARGS__)
+#else
+#define traceusb2(a, ...)
+#endif
+
+#ifdef __linux
+#include <linux/soundcard.h>
+#elif defined(__FreeBSD__)
+#include <sys/soundcard.h>
+#else
+#include <soundcard.h>
+#endif
+
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/callerid.h"
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/endian.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/dsp.h"
+
+#define C108_VENDOR_ID 0x0d8c
+#define C108_PRODUCT_ID 0x000c
+#define C108_HID_INTERFACE 3
+
+#define HID_REPORT_GET 0x01
+#define HID_REPORT_SET 0x09
+
+#define HID_RT_INPUT 0x01
+#define HID_RT_OUTPUT 0x02
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = "",
+};
+static struct ast_jb_conf global_jbconf;
+
+/*!
+ * usbradio.conf parameters are
+START_CONFIG
+
+[general]
+ ; General config options, with default values shown.
+ ; You should use one section per device, with [general] being used
+ ; for the device.
+ ;
+ ;
+ ; debug = 0x0 ; misc debug flags, default is 0
+
+ ; Set the device to use for I/O
+ ; devicenum = 0
+ ; Set hardware type here
+ ; hdwtype=0 ; 0=limey, 1=sph
+
+ ; rxboostset=0 ; no rx gain boost
+ ; rxctcssrelax=1 ; reduce talkoff from radios w/o CTCSS Tx HPF
+ ; rxctcssfreq=100.0 ; rx ctcss freq in floating point. must be in table
+ ; txctcssfreq=100.0 ; tx ctcss freq, any frequency permitted
+
+ ; carrierfrom=dsp ;no,usb,usbinvert,dsp,vox
+ ; ctcssfrom=dsp ;no,usb,dsp
+
+ ; rxdemod=flat ; input type from radio: no,speaker,flat
+ ; txprelim=yes ; output is pre-emphasised and limited
+ ; txtoctype=no ; no,phase,notone
+
+ ; txmixa=composite ;no,voice,tone,composite,auxvoice
+ ; txmixb=no ;no,voice,tone,composite,auxvoice
+
+ ; invertptt=0
+
+ ;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+ ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
+ ; USBRADIO channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The USBRADIO channel can't accept jitter,
+ ; thus an enabled jitterbuffer on the receive USBRADIO side will always
+ ; be used if the sending side can create jitter.
+
+ ; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+ ; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usualy sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+ ; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of an USBRADIO
+ ; channel. Two implementations are currenlty available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+ ; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+ ;-----------------------------------------------------------------------------------
+
+
+END_CONFIG
+
+ */
+
+/*!
+ * The following parameters are used in the driver:
+ *
+ * FRAME_SIZE the size of an audio frame, in samples.
+ * 160 is used almost universally, so you should not change it.
+ *
+ * FRAGS the argument for the SETFRAGMENT ioctl.
+ * Overridden by the 'frags' parameter in usbradio.conf
+ *
+ * Bits 0-7 are the base-2 log of the device's block size,
+ * bits 16-31 are the number of blocks in the driver's queue.
+ * There are a lot of differences in the way this parameter
+ * is supported by different drivers, so you may need to
+ * experiment a bit with the value.
+ * A good default for linux is 30 blocks of 64 bytes, which
+ * results in 6 frames of 320 bytes (160 samples).
+ * FreeBSD works decently with blocks of 256 or 512 bytes,
+ * leaving the number unspecified.
+ * Note that this only refers to the device buffer size,
+ * this module will then try to keep the lenght of audio
+ * buffered within small constraints.
+ *
+ * QUEUE_SIZE The max number of blocks actually allowed in the device
+ * driver's buffer, irrespective of the available number.
+ * Overridden by the 'queuesize' parameter in usbradio.conf
+ *
+ * Should be >=2, and at most as large as the hw queue above
+ * (otherwise it will never be full).
+ */
+
+#define FRAME_SIZE 160
+#define QUEUE_SIZE 20
+
+#if defined(__FreeBSD__)
+#define FRAGS 0x8
+#else
+#define FRAGS ( ( (6 * 5) << 16 ) | 0xc )
+#endif
+
+/*
+ * XXX text message sizes are probably 256 chars, but i am
+ * not sure if there is a suitable definition anywhere.
+ */
+#define TEXT_SIZE 256
+
+#if 0
+#define TRYOPEN 1 /* try to open on startup */
+#endif
+#define O_CLOSE 0x444 /* special 'close' mode for device */
+/* Which device to use */
+#if defined( __OpenBSD__ ) || defined( __NetBSD__ )
+#define DEV_DSP "/dev/audio"
+#else
+#define DEV_DSP "/dev/dsp"
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+static char *config = "usbradio.conf"; /* default config file */
+static char *config1 = "usbradio_tune.conf"; /* tune config file */
+
+static FILE *frxcapraw = NULL, *frxcaptrace = NULL, *frxoutraw = NULL;
+static FILE *ftxcapraw = NULL, *ftxcaptrace = NULL, *ftxoutraw = NULL;
+
+static int usbradio_debug;
+#if 0 /* maw asdf sph */
+static int usbradio_debug_level = 0;
+#endif
+
+enum {RX_AUDIO_NONE,RX_AUDIO_SPEAKER,RX_AUDIO_FLAT};
+enum {CD_IGNORE,CD_XPMR_NOISE,CD_XPMR_VOX,CD_HID,CD_HID_INVERT};
+enum {SD_IGNORE,SD_HID,SD_HID_INVERT,SD_XPMR}; /* no,external,externalinvert,software */
+enum {RX_KEY_CARRIER,RX_KEY_CARRIER_CODE};
+enum {TX_OUT_OFF,TX_OUT_VOICE,TX_OUT_LSD,TX_OUT_COMPOSITE,TX_OUT_AUX};
+enum {TOC_NONE,TOC_PHASE,TOC_NOTONE};
+
+/* DECLARE STRUCTURES */
+
+/*
+ * descriptor for one of our channels.
+ * There is one used for 'default' values (from the [general] entry in
+ * the configuration file), and then one instance for each device
+ * (the default is cloned from [general], others are only created
+ * if the relevant section exists).
+ */
+struct chan_usbradio_pvt {
+ struct chan_usbradio_pvt *next;
+
+ char *name;
+
+ int total_blocks; /* total blocks in the output device */
+ int sounddev;
+ enum { M_UNSET, M_FULL, M_READ, M_WRITE } duplex;
+ i16 cdMethod;
+ int autoanswer;
+ int autohangup;
+ int hookstate;
+ unsigned int queuesize; /* max fragments in queue */
+ unsigned int frags; /* parameter for SETFRAGMENT */
+
+ int warned; /* various flags used for warnings */
+#define WARN_used_blocks 1
+#define WARN_speed 2
+#define WARN_frag 4
+ int w_errors; /* overfull in the write path */
+ struct timeval lastopen;
+
+ int overridecontext;
+ int mute;
+
+ /* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must
+ * be representable in 16 bits to avoid overflows.
+ */
+#define BOOST_SCALE (1<<9)
+#define BOOST_MAX 40 /* slightly less than 7 bits */
+ int boost; /* input boost, scaled by BOOST_SCALE */
+ char devicenum;
+ int spkrmax;
+ int micmax;
+
+ pthread_t sthread;
+ pthread_t hidthread;
+
+ int stophid;
+ struct ast_channel *owner;
+ char ext[AST_MAX_EXTENSION];
+ char ctx[AST_MAX_CONTEXT];
+ char language[MAX_LANGUAGE];
+ char cid_name[256]; /* XXX */
+ char cid_num[256]; /* XXX */
+ char mohinterpret[MAX_MUSICCLASS];
+
+ /* buffers used in usbradio_write, 2 per int by 2 channels by 6 times oversampling (48KS/s) */
+ char usbradio_write_buf[FRAME_SIZE * 2 * 2 * 6];
+ char usbradio_write_buf_1[FRAME_SIZE * 2 * 2 * 6];
+
+ int usbradio_write_dst;
+ /* buffers used in usbradio_read - AST_FRIENDLY_OFFSET space for headers
+ * plus enough room for a full frame
+ */
+ char usbradio_read_buf[FRAME_SIZE * (2 * 12) + AST_FRIENDLY_OFFSET];
+ char usbradio_read_buf_8k[FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET];
+ int readpos; /* read position above */
+ struct ast_frame read_f; /* returned by usbradio_read */
+
+
+ char debuglevel;
+ char radioduplex;
+
+ char lastrx;
+ char rxhidsq;
+ char rxcarrierdetect; /*!< status from pmr channel */
+ char rxctcssdecode; /*!< status from pmr channel */
+
+ char rxkeytype;
+ char rxkeyed; /*!< indicates rx signal present */
+
+ char lasttx;
+ char txkeyed; /*! tx key request from upper layers */
+ char txchankey;
+ char txtestkey;
+
+ time_t lasthidtime;
+ struct ast_dsp *dsp;
+
+ t_pmr_chan *pmrChan;
+
+ char rxcpusaver;
+ char txcpusaver;
+
+ char rxdemod;
+ float rxgain;
+ char rxcdtype;
+ char rxsdtype;
+ int rxsquelchadj; /*!< this copy needs to be here for initialization */
+ char txtoctype;
+
+ char txprelim;
+ float txctcssgain;
+ char txmixa;
+ char txmixb;
+
+ char invertptt;
+
+ char rxctcssrelax;
+ float rxctcssgain;
+ float rxctcssfreq;
+ float txctcssfreq;
+
+ int rxmixerset;
+ int rxboostset;
+ float rxvoiceadj;
+ float rxctcssadj;
+ int txmixaset;
+ int txmixbset;
+ int txctcssadj;
+
+ int hdwtype;
+ int hid_gpio_ctl;
+ int hid_gpio_ctl_loc;
+ int hid_io_cor;
+ int hid_io_cor_loc;
+ int hid_io_ctcss;
+ int hid_io_ctcss_loc;
+ int hid_io_ptt;
+ int hid_gpio_loc;
+
+ struct {
+ unsigned rxcapraw:1;
+ unsigned txcapraw:1;
+ unsigned txcap2:1;
+ unsigned rxcap2:1;
+ } b;
+};
+
+/* maw add additional defaults !!! */
+static struct chan_usbradio_pvt usbradio_default = {
+ .sounddev = -1,
+ .duplex = M_UNSET, /* XXX check this */
+ .autoanswer = 1,
+ .autohangup = 1,
+ .queuesize = QUEUE_SIZE,
+ .frags = FRAGS,
+ .ext = "s",
+ .ctx = "default",
+ .readpos = AST_FRIENDLY_OFFSET, /* start here on reads */
+ .lastopen = { 0, 0 },
+ .boost = BOOST_SCALE,
+};
+
+/* DECLARE FUNCTION PROTOTYPES */
+
+static void store_txtoctype(struct chan_usbradio_pvt *o, const char *s);
+static int hidhdwconfig(struct chan_usbradio_pvt *o);
+static int set_txctcss_level(struct chan_usbradio_pvt *o);
+static void pmrdump(struct chan_usbradio_pvt *o);
+static void mult_set(struct chan_usbradio_pvt *o);
+static int mult_calc(int value);
+static void mixer_write(struct chan_usbradio_pvt *o);
+static void tune_rxinput(struct chan_usbradio_pvt *o);
+static void tune_rxvoice(struct chan_usbradio_pvt *o);
+static void tune_rxctcss(struct chan_usbradio_pvt *o);
+static void tune_txoutput(struct chan_usbradio_pvt *o, int value);
+static void tune_write(struct chan_usbradio_pvt *o);
+
+static char *usbradio_active; /* the active device */
+
+static int setformat(struct chan_usbradio_pvt *o, int mode);
+
+static struct ast_channel *usbradio_request(const char *type, int format, void *data
+, int *cause);
+static int usbradio_digit_begin(struct ast_channel *c, char digit);
+static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration);
+static int usbradio_text(struct ast_channel *c, const char *text);
+static int usbradio_hangup(struct ast_channel *c);
+static int usbradio_answer(struct ast_channel *c);
+static struct ast_frame *usbradio_read(struct ast_channel *chan);
+static int usbradio_call(struct ast_channel *c, char *dest, int timeout);
+static int usbradio_write(struct ast_channel *chan, struct ast_frame *f);
+static int usbradio_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen);
+static int usbradio_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+#if DEBUG_FILETEST == 1
+static int RxTestIt(struct chan_usbradio_pvt *o);
+#endif
+
+static char tdesc[] = "USB (CM108) Radio Channel Driver";
+
+static const struct ast_channel_tech usbradio_tech = {
+ .type = "Radio",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_SLINEAR,
+ .requester = usbradio_request,
+ .send_digit_begin = usbradio_digit_begin,
+ .send_digit_end = usbradio_digit_end,
+ .send_text = usbradio_text,
+ .hangup = usbradio_hangup,
+ .answer = usbradio_answer,
+ .read = usbradio_read,
+ .call = usbradio_call,
+ .write = usbradio_write,
+ .indicate = usbradio_indicate,
+ .fixup = usbradio_fixup,
+};
+
+/* Call with: devnum: alsa major device number, param: ascii Formal
+Parameter Name, val1, first or only value, val2 second value, or 0
+if only 1 value. Values: 0-99 (percent) or 0-1 for baboon.
+
+Note: must add -lasound to end of linkage */
+
+static int amixer_max(int devnum,char *param)
+{
+ int rv,type;
+ char str[15];
+ snd_hctl_t *hctl;
+ snd_ctl_elem_id_t *id;
+ snd_hctl_elem_t *elem;
+ snd_ctl_elem_info_t *info;
+
+ snprintf(str, sizeof(str), "hw:%d", devnum);
+ if (snd_hctl_open(&hctl, str, 0))
+ return -1;
+ snd_hctl_load(hctl);
+ id = alloca(snd_ctl_elem_id_sizeof());
+ memset(id, 0, snd_ctl_elem_id_sizeof());
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, param);
+ elem = snd_hctl_find_elem(hctl, id);
+ if (!elem) {
+ snd_hctl_close(hctl);
+ return -1;
+ }
+ info = alloca(snd_ctl_elem_info_sizeof());
+ memset(info, 0, snd_ctl_elem_info_sizeof());
+ snd_hctl_elem_info(elem,info);
+ type = snd_ctl_elem_info_get_type(info);
+ rv = 0;
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ rv = snd_ctl_elem_info_get_max(info);
+ break;
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ rv = 1;
+ break;
+ }
+ snd_hctl_close(hctl);
+ return(rv);
+}
+
+/*! \brief Call with: devnum: alsa major device number, param: ascii Formal
+Parameter Name, val1, first or only value, val2 second value, or 0
+if only 1 value. Values: 0-99 (percent) or 0-1 for baboon.
+
+Note: must add -lasound to end of linkage */
+
+static int setamixer(int devnum, char *param, int v1, int v2)
+{
+ int type;
+ char str[15];
+ snd_hctl_t *hctl;
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_value_t *control;
+ snd_hctl_elem_t *elem;
+ snd_ctl_elem_info_t *info;
+
+ snprintf(str, sizeof(str), "hw:%d", devnum);
+ if (snd_hctl_open(&hctl, str, 0))
+ return -1;
+ snd_hctl_load(hctl);
+ id = alloca(snd_ctl_elem_id_sizeof());
+ memset(id, 0, snd_ctl_elem_id_sizeof());
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, param);
+ elem = snd_hctl_find_elem(hctl, id);
+ if (!elem) {
+ snd_hctl_close(hctl);
+ return -1;
+ }
+ info = alloca(snd_ctl_elem_info_sizeof());
+ memset(info, 0, snd_ctl_elem_info_sizeof());
+ snd_hctl_elem_info(elem,info);
+ type = snd_ctl_elem_info_get_type(info);
+ control = alloca(snd_ctl_elem_value_sizeof());
+ memset(control, 0, snd_ctl_elem_value_sizeof());
+ snd_ctl_elem_value_set_id(control, id);
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ snd_ctl_elem_value_set_integer(control, 0, v1);
+ if (v2 > 0) snd_ctl_elem_value_set_integer(control, 1, v2);
+ break;
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ snd_ctl_elem_value_set_integer(control, 0, (v1 != 0));
+ break;
+ }
+ if (snd_hctl_elem_write(elem, control)) {
+ snd_hctl_close(hctl);
+ return(-1);
+ }
+ snd_hctl_close(hctl);
+ return 0;
+}
+
+static void hid_set_outputs(struct usb_dev_handle *handle,
+ unsigned char *outputs)
+{
+ usb_control_msg(handle,
+ USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
+ HID_REPORT_SET,
+ 0 + (HID_RT_OUTPUT << 8),
+ C108_HID_INTERFACE,
+ (char *)outputs, 4, 5000);
+}
+
+static void hid_get_inputs(struct usb_dev_handle *handle,
+ unsigned char *inputs)
+{
+ usb_control_msg(handle,
+ USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
+ HID_REPORT_GET,
+ 0 + (HID_RT_INPUT << 8),
+ C108_HID_INTERFACE,
+ (char *)inputs, 4, 5000);
+}
+
+static struct usb_device *hid_device_init(void)
+{
+ struct usb_bus *usb_bus;
+ struct usb_device *dev;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+ for (usb_bus = usb_busses; usb_bus; usb_bus = usb_bus->next) {
+ for (dev = usb_bus->devices; dev; dev = dev->next) {
+ if ((dev->descriptor.idVendor == C108_VENDOR_ID) && (dev->descriptor.idProduct == C108_PRODUCT_ID))
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+static int hidhdwconfig(struct chan_usbradio_pvt *o)
+{
+ if (o->hdwtype == 1) { /*sphusb */
+ o->hid_gpio_ctl = 0x08; /* set GPIO4 to output mode */
+ o->hid_gpio_ctl_loc = 2; /* For CTL of GPIO */
+ o->hid_io_cor = 4; /* GPIO3 is COR */
+ o->hid_io_cor_loc = 1; /* GPIO3 is COR */
+ o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */
+ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */
+ o->hid_io_ptt = 8; /* GPIO 4 is PTT */
+ o->hid_gpio_loc = 1; /* For ALL GPIO */
+ } else if (o->hdwtype == 0) { /* dudeusb */
+ o->hid_gpio_ctl = 0x0c;/* set GPIO 3 & 4 to output mode */
+ o->hid_gpio_ctl_loc = 2; /* For CTL of GPIO */
+ o->hid_io_cor = 2; /* VOLD DN is COR */
+ o->hid_io_cor_loc = 0; /* VOL DN COR */
+ o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */
+ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */
+ o->hid_io_ptt = 4; /* GPIO 3 is PTT */
+ o->hid_gpio_loc = 1; /* For ALL GPIO */
+ } else if (o->hdwtype == 3) { /* custom version */
+ o->hid_gpio_ctl = 0x0c; /* set GPIO 3 & 4 to output mode */
+ o->hid_gpio_ctl_loc = 2; /* For CTL of GPIO */
+ o->hid_io_cor = 2; /* VOLD DN is COR */
+ o->hid_io_cor_loc = 0; /* VOL DN COR */
+ o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */
+ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */
+ o->hid_io_ptt = 4; /* GPIO 3 is PTT */
+ o->hid_gpio_loc = 1; /* For ALL GPIO */
+ }
+
+ return 0;
+}
+
+
+static void *hidthread(void *arg)
+{
+ unsigned char buf[4], keyed;
+ char lastrx, txtmp;
+ struct usb_device *usb_dev;
+ struct usb_dev_handle *usb_handle;
+ struct chan_usbradio_pvt *o = arg;
+
+ usb_dev = hid_device_init();
+ if (usb_dev == NULL) {
+ ast_log(LOG_ERROR, "USB HID device not found\n");
+ pthread_exit(NULL);
+ }
+ usb_handle = usb_open(usb_dev);
+ if (usb_handle == NULL) {
+ ast_log(LOG_ERROR, "Not able to open USB device\n");
+ pthread_exit(NULL);
+ }
+ if (usb_claim_interface(usb_handle, C108_HID_INTERFACE) < 0) {
+ if (usb_detach_kernel_driver_np(usb_handle, C108_HID_INTERFACE) < 0) {
+ ast_log(LOG_ERROR, "Not able to detach the USB device\n");
+ pthread_exit(NULL);
+ }
+ if (usb_claim_interface(usb_handle, C108_HID_INTERFACE) < 0) {
+ ast_log(LOG_ERROR, "Not able to claim the USB device\n");
+ pthread_exit(NULL);
+ }
+ }
+ memset(buf, 0, sizeof(buf));
+ buf[2] = o->hid_gpio_ctl;
+ buf[1] = 0;
+ hid_set_outputs(usb_handle, buf);
+ traceusb1("hidthread: Starting normally!!\n");
+ lastrx = 0;
+ while (!o->stophid) {
+ buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl;
+ hid_get_inputs(usb_handle, buf);
+ keyed = !(buf[o->hid_io_cor_loc] & o->hid_io_cor);
+ if (keyed != o->rxhidsq) {
+ if (o->debuglevel)
+ ast_log(LOG_NOTICE, "chan_usbradio() hidthread: update rxhidsq = %d\n", keyed);
+ o->rxhidsq = keyed;
+ }
+
+ /* if change in tx stuff */
+ txtmp = 0;
+ if (o->txkeyed || o->txchankey || o->txtestkey || o->pmrChan->txPttOut)
+ txtmp = 1;
+
+ if (o->lasttx != txtmp) {
+ o->lasttx = txtmp;
+ if (o->debuglevel)
+ ast_log(LOG_NOTICE, "hidthread: tx set to %d\n", txtmp);
+ buf[o->hid_gpio_loc] = 0;
+ if (txtmp)
+ buf[o->hid_gpio_loc] = o->hid_io_ptt;
+ buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl;
+ hid_set_outputs(usb_handle, buf);
+ }
+
+ time(&o->lasthidtime);
+ usleep(50000);
+ }
+ buf[o->hid_gpio_loc] = 0;
+ if (o->invertptt)
+ buf[o->hid_gpio_loc] = o->hid_io_ptt;
+ buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl;
+ hid_set_outputs(usb_handle, buf);
+ pthread_exit(0);
+}
+
+/*! \brief
+ * returns a pointer to the descriptor with the given name
+ */
+static struct chan_usbradio_pvt *find_desc(char *dev)
+{
+ struct chan_usbradio_pvt *o = NULL;
+
+ if (!dev)
+ ast_log(LOG_WARNING, "null dev\n");
+
+ for (o = usbradio_default.next; o && o->name && dev && strcmp(o->name, dev) != 0; o = o->next);
+
+ if (!o)
+ ast_log(LOG_WARNING, "could not find <%s>\n", dev ? dev : "--no-device--");
+
+ return o;
+}
+
+/*! \brief
+ * split a string in extension-context, returns pointers to malloc'ed
+ * strings.
+ * If we do not have 'overridecontext' then the last @ is considered as
+ * a context separator, and the context is overridden.
+ * This is usually not very necessary as you can play with the dialplan,
+ * and it is nice not to need it because you have '@' in SIP addresses.
+ * Return value is the buffer address.
+ */
+#if 0
+static char *ast_ext_ctx(const char *src, char **ext, char **ctx)
+{
+ struct chan_usbradio_pvt *o = find_desc(usbradio_active);
+
+ if (ext == NULL || ctx == NULL)
+ return NULL; /* error */
+
+ *ext = *ctx = NULL;
+
+ if (src && *src != '\0')
+ *ext = ast_strdup(src);
+
+ if (*ext == NULL)
+ return NULL;
+
+ if (!o->overridecontext) {
+ /* parse from the right */
+ *ctx = strrchr(*ext, '@');
+ if (*ctx)
+ *(*ctx)++ = '\0';
+ }
+
+ return *ext;
+}
+#endif
+
+/*! \brief
+ * Returns the number of blocks used in the audio output channel
+ */
+static int used_blocks(struct chan_usbradio_pvt *o)
+{
+ struct audio_buf_info info;
+
+ if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) {
+ if (!(o->warned & WARN_used_blocks)) {
+ ast_log(LOG_WARNING, "Error reading output space\n");
+ o->warned |= WARN_used_blocks;
+ }
+ return 1;
+ }
+
+ if (o->total_blocks == 0) {
+ ast_debug(4, "fragtotal %d size %d avail %d\n", info.fragstotal, info.fragsize, info.fragments);
+ o->total_blocks = info.fragments;
+ }
+
+ return o->total_blocks - info.fragments;
+}
+
+/*! \brief Write an exactly FRAME_SIZE sized frame */
+static int soundcard_writeframe(struct chan_usbradio_pvt *o, short *data)
+{
+ int res;
+
+ if (o->sounddev < 0)
+ setformat(o, O_RDWR);
+ if (o->sounddev < 0)
+ return 0; /* not fatal */
+ /*
+ * Nothing complex to manage the audio device queue.
+ * If the buffer is full just drop the extra, otherwise write.
+ * XXX in some cases it might be useful to write anyways after
+ * a number of failures, to restart the output chain.
+ */
+ res = used_blocks(o);
+ if (res > o->queuesize) { /* no room to write a block */
+ if (o->w_errors++ == 0 && (usbradio_debug & 0x4))
+ ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, o->w_errors);
+ return 0;
+ }
+ o->w_errors = 0;
+
+ return write(o->sounddev, ((void *) data), FRAME_SIZE * 2 * 12);
+}
+
+/*
+ * reset and close the device if opened,
+ * then open and initialize it in the desired mode,
+ * trigger reads and writes so we can start using it.
+ */
+static int setformat(struct chan_usbradio_pvt *o, int mode)
+{
+ int fmt, desired, res, fd;
+ char device[20];
+
+ if (o->sounddev >= 0) {
+ ioctl(o->sounddev, SNDCTL_DSP_RESET, 0);
+ close(o->sounddev);
+ o->duplex = M_UNSET;
+ o->sounddev = -1;
+ }
+ if (mode == O_CLOSE) /* we are done */
+ return 0;
+ if (ast_tvdiff_ms(ast_tvnow(), o->lastopen) < 1000)
+ return -1; /* don't open too often */
+ o->lastopen = ast_tvnow();
+ strcpy(device, "/dev/dsp");
+ if (o->devicenum)
+ snprintf(device + strlen("/dev/dsp"), sizeof(device) - strlen("/dev/dsp"), "%d", o->devicenum);
+ fd = o->sounddev = open(device, mode | O_NONBLOCK);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to re-open DSP device %d: %s\n", o->devicenum, strerror(errno));
+ return -1;
+ }
+ if (o->owner)
+ o->owner->fds[0] = fd;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ fmt = AFMT_S16_LE;
+#else
+ fmt = AFMT_S16_BE;
+#endif
+ res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
+ return -1;
+ }
+ switch (mode) {
+ case O_RDWR:
+ res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+ /* Check to see if duplex set (FreeBSD Bug) */
+ res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt);
+ if (res == 0 && (fmt & DSP_CAP_DUPLEX)) {
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Console is full duplex\n");
+ o->duplex = M_FULL;
+ };
+ break;
+ case O_WRONLY:
+ o->duplex = M_WRITE;
+ break;
+ case O_RDONLY:
+ o->duplex = M_READ;
+ break;
+ }
+
+ fmt = 1;
+ res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ return -1;
+ }
+ fmt = desired = 48000; /* 8000 Hz desired */
+ res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ return -1;
+ }
+ if (fmt != desired) {
+ if (!(o->warned & WARN_speed)) {
+ ast_log(LOG_WARNING,
+ "Requested %d Hz, got %d Hz -- sound may be choppy\n",
+ desired, fmt);
+ o->warned |= WARN_speed;
+ }
+ }
+ /*
+ * on Freebsd, SETFRAGMENT does not work very well on some cards.
+ * Default to use 256 bytes, let the user override
+ */
+ if (o->frags) {
+ fmt = o->frags;
+ res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
+ if (res < 0) {
+ if (!(o->warned & WARN_frag)) {
+ ast_log(LOG_WARNING,
+ "Unable to set fragment size -- sound may be choppy\n");
+ o->warned |= WARN_frag;
+ }
+ }
+ }
+ /* on some cards, we need SNDCTL_DSP_SETTRIGGER to start outputting */
+ res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+ res = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &res);
+ /* it may fail if we are in half duplex, never mind */
+ return 0;
+}
+
+/*
+ * some of the standard methods supported by channels.
+ */
+static int usbradio_digit_begin(struct ast_channel *c, char digit)
+{
+ return 0;
+}
+
+static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+ /* no better use for received digits than print them */
+ ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
+ digit, duration);
+ return 0;
+}
+
+static int usbradio_text(struct ast_channel *c, const char *text)
+{
+ /* print received messages */
+ ast_verbose(" << Console Received text %s >> \n", text);
+ return 0;
+}
+
+/*
+ * handler for incoming calls. Either autoanswer, or start ringing
+ */
+static int usbradio_call(struct ast_channel *c, char *dest, int timeout)
+{
+ struct chan_usbradio_pvt *o = c->tech_pvt;
+
+ time(&o->lasthidtime);
+ ast_pthread_create_background(&o->hidthread, NULL, hidthread, o);
+ ast_setstate(c, AST_STATE_UP);
+ return 0;
+}
+
+/*
+ * remote side answered the phone
+ */
+static int usbradio_answer(struct ast_channel *c)
+{
+ ast_setstate(c, AST_STATE_UP);
+
+ return 0;
+}
+
+static int usbradio_hangup(struct ast_channel *c)
+{
+ struct chan_usbradio_pvt *o = c->tech_pvt;
+
+ c->tech_pvt = NULL;
+ o->owner = NULL;
+ ast_module_unref(ast_module_info->self);
+ if (o->hookstate) {
+ if (o->autoanswer || o->autohangup) {
+ /* Assume auto-hangup too */
+ o->hookstate = 0;
+ setformat(o, O_CLOSE);
+ }
+ }
+ o->stophid = 1;
+ pthread_join(o->hidthread, NULL);
+ return 0;
+}
+
+
+/* used for data coming from the network */
+static int usbradio_write(struct ast_channel *c, struct ast_frame *f)
+{
+ int src,datalen;
+ struct chan_usbradio_pvt *o = c->tech_pvt;
+
+ traceusb2("usbradio_write() o->nosound=%d\n", o->nosound); /*sph maw asdf */
+
+ /*
+ * we could receive a block which is not a multiple of our
+ * FRAME_SIZE, so buffer it locally and write to the device
+ * in FRAME_SIZE chunks.
+ * Keep the residue stored for future use.
+ */
+
+ if (o->txkeyed || o->txtestkey)
+ o->pmrChan->txPttIn = 1;
+ else
+ o->pmrChan->txPttIn = 0;
+
+ #if DEBUG_CAPTURES == 1 /* to write input data to a file datalen=320 */
+ if (ftxcapraw && o->b.txcapraw) {
+ i16 i, tbuff[f->datalen];
+ for (i = 0; i < f->datalen; i += 2) {
+ tbuff[i] = ((i16 *)(f->data))[i / 2];
+ tbuff[i + 1] = o->txkeyed * M_Q13;
+ }
+ fwrite(tbuff, 2, f->datalen, ftxcapraw);
+ /*fwrite(f->data,1,f->datalen,ftxcapraw); */
+ }
+ #endif
+
+ PmrTx(o->pmrChan,(i16*)f->data,(i16*)o->usbradio_write_buf_1);
+
+ #if 0 /* to write 48KS/s stereo data to a file */
+ if (!ftxoutraw) ftxoutraw = fopen(TX_CAP_OUT_FILE,"w");
+ if (ftxoutraw) fwrite(o->usbradio_write_buf_1,1,f->datalen * 2 * 6,ftxoutraw);
+ #endif
+
+ #if DEBUG_CAPTURES == 1
+ if (o->b.txcap2 && ftxcaptrace)
+ fwrite((o->pmrChan->ptxDebug), 1, FRAME_SIZE * 2 * 16, ftxcaptrace);
+ #endif
+
+ src = 0; /* read position into f->data */
+ datalen = f->datalen * 12;
+ while (src < datalen) {
+ /* Compute spare room in the buffer */
+ int l = sizeof(o->usbradio_write_buf) - o->usbradio_write_dst;
+
+ if (datalen - src >= l) { /* enough to fill a frame */
+ memcpy(o->usbradio_write_buf + o->usbradio_write_dst, o->usbradio_write_buf_1 + src, l);
+ soundcard_writeframe(o, (short *) o->usbradio_write_buf);
+ src += l;
+ o->usbradio_write_dst = 0;
+ } else { /* copy residue */
+ l = datalen - src;
+ memcpy(o->usbradio_write_buf + o->usbradio_write_dst, o->usbradio_write_buf_1 + src, l);
+ src += l; /* but really, we are done */
+ o->usbradio_write_dst += l;
+ }
+ }
+ return 0;
+}
+
+static struct ast_frame *usbradio_read(struct ast_channel *c)
+{
+ int res;
+ struct chan_usbradio_pvt *o = c->tech_pvt;
+ struct ast_frame *f = &o->read_f, *f1;
+ struct ast_frame wf = { AST_FRAME_CONTROL };
+ time_t now;
+
+ traceusb2("usbradio_read()\n"); /* sph maw asdf */
+
+ if (o->lasthidtime) {
+ time(&now);
+ if ((now - o->lasthidtime) > 3) {
+ ast_log(LOG_ERROR, "HID process has died or something!!\n");
+ return NULL;
+ }
+ }
+ if (o->lastrx && (!o->rxkeyed)) {
+ o->lastrx = 0;
+ wf.subclass = AST_CONTROL_RADIO_UNKEY;
+ ast_queue_frame(o->owner, &wf);
+ } else if ((!o->lastrx) && (o->rxkeyed)) {
+ o->lastrx = 1;
+ wf.subclass = AST_CONTROL_RADIO_KEY;
+ ast_queue_frame(o->owner, &wf);
+ }
+ /* XXX can be simplified returning &ast_null_frame */
+ /* prepare a NULL frame in case we don't have enough data to return */
+ memset(f, 0, sizeof(struct ast_frame));
+ f->frametype = AST_FRAME_NULL;
+ f->src = usbradio_tech.type;
+
+ res = read(o->sounddev, o->usbradio_read_buf + o->readpos,
+ sizeof(o->usbradio_read_buf) - o->readpos);
+ if (res < 0) /* audio data not ready, return a NULL frame */
+ return f;
+
+ o->readpos += res;
+ if (o->readpos < sizeof(o->usbradio_read_buf)) /* not enough samples */
+ return f;
+
+ if (o->mute)
+ return f;
+
+ #if DEBUG_CAPTURES == 1
+ if (o->b.rxcapraw && frxcapraw)
+ fwrite((o->usbradio_read_buf + AST_FRIENDLY_OFFSET), 1, FRAME_SIZE * 2 * 2 * 6, frxcapraw);
+ #endif
+
+ #if 1
+ PmrRx( o->pmrChan,
+ (i16 *)(o->usbradio_read_buf + AST_FRIENDLY_OFFSET),
+ (i16 *)(o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET));
+
+ #else
+ static FILE *hInput;
+ i16 iBuff[FRAME_SIZE * 2 * 6];
+
+ o->pmrChan->b.rxCapture = 1;
+
+ if(!hInput) {
+ hInput = fopen("/usr/src/xpmr/testdata/rx_in.pcm", "r");
+ if(!hInput) {
+ ast_log(LOG_ERROR, " Input Data File Not Found.\n");
+ return 0;
+ }
+ }
+
+ if (0 == fread((void *)iBuff, 2, FRAME_SIZE * 2 * 6, hInput))
+ exit;
+
+ PmrRx( o->pmrChan,
+ (i16 *)iBuff,
+ (i16 *)(o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET));
+
+ #endif
+
+ #if 0
+ if (!frxoutraw) frxoutraw = fopen(RX_CAP_OUT_FILE, "w");
+ if (frxoutraw) fwrite((o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET), 1, FRAME_SIZE * 2, frxoutraw);
+ #endif
+
+ #if DEBUG_CAPTURES == 1
+ if (frxcaptrace && o->b.rxcap2) fwrite((o->pmrChan->prxDebug), 1, FRAME_SIZE * 2 * 16, frxcaptrace);
+ #endif
+
+ if (o->rxcdtype == CD_HID && (o->pmrChan->rxExtCarrierDetect != o->rxhidsq))
+ o->pmrChan->rxExtCarrierDetect = o->rxhidsq;
+ if (o->rxcdtype == CD_HID_INVERT && (o->pmrChan->rxExtCarrierDetect == o->rxhidsq))
+ o->pmrChan->rxExtCarrierDetect = !o->rxhidsq;
+
+ if ( (o->rxcdtype == CD_HID && o->rxhidsq) ||
+ (o->rxcdtype == CD_HID_INVERT && !o->rxhidsq) ||
+ (o->rxcdtype == CD_XPMR_NOISE && o->pmrChan->rxCarrierDetect) ||
+ (o->rxcdtype == CD_XPMR_VOX && o->pmrChan->rxCarrierDetect) )
+ res = 1;
+ else
+ res = 0;
+
+ if (res != o->rxcarrierdetect) {
+ o->rxcarrierdetect = res;
+ if (o->debuglevel)
+ ast_debug(4, "rxcarrierdetect = %d\n", res);
+ }
+
+ if (o->pmrChan->rxCtcss->decode != o->rxctcssdecode) {
+ if (o->debuglevel)
+ ast_debug(4, "rxctcssdecode = %d\n", o->pmrChan->rxCtcss->decode);
+ o->rxctcssdecode = o->pmrChan->rxCtcss->decode;
+ }
+
+ if ( ( o->rxctcssfreq && (o->rxctcssdecode == o->pmrChan->rxCtcssIndex)) ||
+ ( !o->rxctcssfreq && o->rxcarrierdetect) )
+ o->rxkeyed = 1;
+ else
+ o->rxkeyed = 0;
+
+
+ o->readpos = AST_FRIENDLY_OFFSET; /* reset read pointer for next frame */
+ if (c->_state != AST_STATE_UP) /* drop data if frame is not up */
+ return f;
+ /* ok we can build and deliver the frame to the caller */
+ f->frametype = AST_FRAME_VOICE;
+ f->subclass = AST_FORMAT_SLINEAR;
+ f->samples = FRAME_SIZE;
+ f->datalen = FRAME_SIZE * 2;
+ f->data = o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET;
+ if (o->boost != BOOST_SCALE) { /* scale and clip values */
+ int i, x;
+ int16_t *p = (int16_t *) f->data;
+ for (i = 0; i < f->samples; i++) {
+ x = (p[i] * o->boost) / BOOST_SCALE;
+ if (x > 32767)
+ x = 32767;
+ else if (x < -32768)
+ x = -32768;
+ p[i] = x;
+ }
+ }
+
+ f->offset = AST_FRIENDLY_OFFSET;
+ if (o->dsp) {
+ f1 = ast_dsp_process(c, o->dsp, f);
+ if ((f1->frametype == AST_FRAME_DTMF_END) || (f1->frametype == AST_FRAME_DTMF_BEGIN)) {
+ if ((f1->subclass == 'm') || (f1->subclass == 'u'))
+ f1->frametype = AST_FRAME_DTMF_BEGIN;
+ if (f1->frametype == AST_FRAME_DTMF_END)
+ ast_log(LOG_NOTICE,"Got DTMF char %c\n",f1->subclass);
+ return f1;
+ }
+ }
+ return f;
+}
+
+static int usbradio_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct chan_usbradio_pvt *o = newchan->tech_pvt;
+ ast_log(LOG_WARNING,"usbradio_fixup()\n");
+ o->owner = newchan;
+ return 0;
+}
+
+static int usbradio_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen)
+{
+ struct chan_usbradio_pvt *o = c->tech_pvt;
+ int res = 0;
+
+ switch (cond) {
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_RINGING:
+ case -1:
+ res = -1;
+ break;
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_VIDUPDATE:
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verbose(" << Console Has Been Placed on Hold >> \n");
+ ast_moh_start(c, data, o->mohinterpret);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verbose(" << Console Has Been Retrieved from Hold >> \n");
+ ast_moh_stop(c);
+ break;
+ case AST_CONTROL_RADIO_KEY:
+ o->txkeyed = 1;
+ if (o->debuglevel)
+ ast_verbose(" << Radio Transmit On. >> \n");
+ break;
+ case AST_CONTROL_RADIO_UNKEY:
+ o->txkeyed = 0;
+ if (o->debuglevel)
+ ast_verbose(" << Radio Transmit Off. >> \n");
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, c->name);
+ return -1;
+ }
+
+ return res;
+}
+
+/*
+ * allocate a new channel.
+ */
+static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, char *ctx, int state)
+{
+ struct ast_channel *c;
+ char device[15] = "dsp";
+
+ if (o->devicenum)
+ snprintf(device + 3, sizeof(device) - 3, "%d", o->devicenum);
+ c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "usbRadio/%s", device);
+ if (c == NULL)
+ return NULL;
+ c->tech = &usbradio_tech;
+ if (o->sounddev < 0)
+ setformat(o, O_RDWR);
+ c->fds[0] = o->sounddev; /* -1 if device closed, override later */
+ c->nativeformats = AST_FORMAT_SLINEAR;
+ c->readformat = AST_FORMAT_SLINEAR;
+ c->writeformat = AST_FORMAT_SLINEAR;
+ c->tech_pvt = o;
+
+ if (!ast_strlen_zero(o->language))
+ ast_string_field_set(c, language, o->language);
+ /* Don't use ast_set_callerid() here because it will
+ * generate a needless NewCallerID event */
+ c->cid.cid_num = ast_strdup(o->cid_num);
+ c->cid.cid_ani = ast_strdup(o->cid_num);
+ c->cid.cid_name = ast_strdup(o->cid_name);
+ if (!ast_strlen_zero(ext))
+ c->cid.cid_dnid = ast_strdup(ext);
+
+ o->owner = c;
+ ast_module_ref(ast_module_info->self);
+ ast_jb_configure(c, &global_jbconf);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(c)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", c->name);
+ ast_hangup(c);
+ o->owner = c = NULL;
+ /* XXX what about the channel itself ? */
+ /* XXX what about usecnt ? */
+ }
+ }
+
+ return c;
+}
+
+static struct ast_channel *usbradio_request(const char *type, int format, void *data, int *cause)
+{
+ struct ast_channel *c;
+ struct chan_usbradio_pvt *o = find_desc(data);
+
+ ast_debug(4, "usbradio_request ty <%s> data 0x%p <%s>\n", type, data, (char *) data);
+ if (o == NULL) {
+ ast_log(LOG_NOTICE, "Device %s not found\n", (char *) data);
+ /* XXX we could default to 'dsp' perhaps ? */
+ return NULL;
+ }
+ if ((format & AST_FORMAT_SLINEAR) == 0) {
+ ast_log(LOG_NOTICE, "Format 0x%x unsupported\n", format);
+ return NULL;
+ }
+ if (o->owner) {
+ ast_log(LOG_NOTICE, "Already have a call (chan %p) on the usb channel\n", o->owner);
+ *cause = AST_CAUSE_BUSY;
+ return NULL;
+ }
+ c = usbradio_new(o, NULL, NULL, AST_STATE_DOWN);
+ if (c == NULL) {
+ ast_log(LOG_WARNING, "Unable to create new usb channel\n");
+ return NULL;
+ }
+ return c;
+}
+
+static char *handle_cli_radio_key(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_usbradio_pvt *o = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "radio key";
+ e->usage =
+ "Usage: radio key\n"
+ " Simulates COR active.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ o = find_desc(usbradio_active);
+ o->txtestkey = 1;
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_radio_unkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_usbradio_pvt *o = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "radio unkey";
+ e->usage =
+ "Usage: radio unkey\n"
+ " Simulates COR un-active.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ o = find_desc(usbradio_active);
+ o->txtestkey = 0;
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_radio_tune(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_usbradio_pvt *o = NULL;
+ int i = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "radio tune [rxnoise|rxvoice|rxtone|rxsquelch|rxcap|rxtracecap|"
+ "txvoice|txtone|txcap|txtracecap|auxvoice|nocap|dump|save]";
+ /* radio tune 6 3000 measured tx value */
+ e->usage =
+ "Usage: radio tune <function>\n"
+ " rxnoise\n"
+ " rxvoice\n"
+ " rxtone\n"
+ " rxsquelch [newsetting]\n"
+ " rxcap\n"
+ " rxtracecap\n"
+ " txvoice [newsetting]\n"
+ " txtone [newsetting]\n"
+ " txcap\n"
+ " txtracecap\n"
+ " auxvoice [newsetting]\n"
+ " nocap\n"
+ " dump\n"
+ " save (settings to tuning file)\n"
+ "\n"
+ " All [newsetting]s are values 0-999\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc < 2) || (a->argc > 4))
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 2) { /* just show stuff */
+ ast_cli(a->fd, "Output A is currently set to %s.\n",
+ o->txmixa == TX_OUT_COMPOSITE ? "composite" :
+ o->txmixa == TX_OUT_VOICE ? "voice" :
+ o->txmixa == TX_OUT_LSD ? "tone" :
+ o->txmixa == TX_OUT_AUX ? "auxvoice" :
+ "off");
+
+ ast_cli(a->fd, "Output B is currently set to %s.\n",
+ o->txmixb == TX_OUT_COMPOSITE ? "composite" :
+ o->txmixb == TX_OUT_VOICE ? "voice" :
+ o->txmixb == TX_OUT_LSD ? "tone" :
+ o->txmixb == TX_OUT_AUX ? "auxvoice" :
+ "off");
+
+ ast_cli(a->fd, "Tx Voice Level currently set to %d\n", o->txmixaset);
+ ast_cli(a->fd, "Tx Tone Level currently set to %d\n", o->txctcssadj);
+ ast_cli(a->fd, "Rx Squelch currently set to %d\n", o->rxsquelchadj);
+ return CLI_SHOWUSAGE;
+ }
+
+ o = find_desc(usbradio_active);
+
+ if (!strcasecmp(a->argv[2], "rxnoise"))
+ tune_rxinput(o);
+ else if (!strcasecmp(a->argv[2], "rxvoice"))
+ tune_rxvoice(o);
+ else if (!strcasecmp(a->argv[2], "rxtone"))
+ tune_rxctcss(o);
+ else if (!strcasecmp(a->argv[2], "rxsquelch")) {
+ if (a->argc == 3) {
+ ast_cli(a->fd, "Current Signal Strength is %d\n", ((32767 - o->pmrChan->rxRssi) * 1000 / 32767));
+ ast_cli(a->fd, "Current Squelch setting is %d\n", o->rxsquelchadj);
+#if 0
+ ast_cli(a->fd,"Current Raw RSSI is %d\n",o->pmrChan->rxRssi);
+ ast_cli(a->fd,"Current (real) Squelch setting is %d\n",*(o->pmrChan->prxSquelchAdjust));
+#endif
+ } else {
+ i = atoi(a->argv[3]);
+ if ((i < 0) || (i > 999))
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "Changed Squelch setting to %d\n", i);
+ o->rxsquelchadj = i;
+ *(o->pmrChan->prxSquelchAdjust) = ((999 - i) * 32767) / 1000;
+ }
+ } else if (!strcasecmp(a->argv[2], "txvoice")) {
+ i = 0;
+
+ if ((o->txmixa != TX_OUT_VOICE) && (o->txmixb != TX_OUT_VOICE) &&
+ (o->txmixa != TX_OUT_COMPOSITE) && (o->txmixb != TX_OUT_COMPOSITE)) {
+ ast_log(LOG_ERROR, "No txvoice output configured.\n");
+ } else if (a->argc == 3) {
+ if ((o->txmixa == TX_OUT_VOICE) || (o->txmixa == TX_OUT_COMPOSITE))
+ ast_cli(a->fd, "Current txvoice setting on Channel A is %d\n", o->txmixaset);
+ else
+ ast_cli(a->fd, "Current txvoice setting on Channel B is %d\n", o->txmixbset);
+ } else {
+ i = atoi(a->argv[3]);
+ if ((i < 0) || (i > 999))
+ return CLI_SHOWUSAGE;
+
+ if ((o->txmixa == TX_OUT_VOICE) || (o->txmixa == TX_OUT_COMPOSITE)) {
+ o->txmixaset = i;
+ ast_cli(a->fd, "Changed txvoice setting on Channel A to %d\n", o->txmixaset);
+ } else {
+ o->txmixbset = i;
+ ast_cli(a->fd, "Changed txvoice setting on Channel B to %d\n", o->txmixbset);
+ }
+ mixer_write(o);
+ mult_set(o);
+ ast_cli(a->fd, "Changed Tx Voice Output setting to %d\n", i);
+ }
+ tune_txoutput(o,i);
+ } else if (!strcasecmp(a->argv[2], "auxvoice")) {
+ i = 0;
+ if ( (o->txmixa != TX_OUT_AUX) && (o->txmixb != TX_OUT_AUX))
+ ast_log(LOG_WARNING, "No auxvoice output configured.\n");
+ else if (a->argc == 3) {
+ if (o->txmixa == TX_OUT_AUX)
+ ast_cli(a->fd, "Current auxvoice setting on Channel A is %d\n", o->txmixaset);
+ else
+ ast_cli(a->fd, "Current auxvoice setting on Channel B is %d\n", o->txmixbset);
+ } else {
+ i = atoi(a->argv[3]);
+ if ((i < 0) || (i > 999))
+ return CLI_SHOWUSAGE;
+ if (o->txmixa == TX_OUT_AUX) {
+ o->txmixbset = i;
+ ast_cli(a->fd, "Changed auxvoice setting on Channel A to %d\n", o->txmixaset);
+ } else {
+ o->txmixbset = i;
+ ast_cli(a->fd, "Changed auxvoice setting on Channel B to %d\n", o->txmixbset);
+ }
+ mixer_write(o);
+ mult_set(o);
+ }
+ /* tune_auxoutput(o,i); */
+ } else if (!strcasecmp(a->argv[2], "txtone")) {
+ if (a->argc == 3)
+ ast_cli(a->fd, "Current Tx CTCSS modulation setting = %d\n", o->txctcssadj);
+ else {
+ i = atoi(a->argv[3]);
+ if ((i < 0) || (i > 999))
+ return CLI_SHOWUSAGE;
+ o->txctcssadj = i;
+ set_txctcss_level(o);
+ ast_cli(a->fd, "Changed Tx CTCSS modulation setting to %i\n", i);
+ }
+ o->txtestkey = 1;
+ usleep(5000000);
+ o->txtestkey = 0;
+ } else if (!strcasecmp(a->argv[2],"dump"))
+ pmrdump(o);
+ else if (!strcasecmp(a->argv[2],"nocap")) {
+ ast_cli(a->fd, "File capture (trace) was rx=%d tx=%d and now off.\n", o->b.rxcap2, o->b.txcap2);
+ ast_cli(a->fd, "File capture (raw) was rx=%d tx=%d and now off.\n", o->b.rxcapraw, o->b.txcapraw);
+ o->b.rxcapraw = o->b.txcapraw = o->b.rxcap2 = o->b.txcap2 = o->pmrChan->b.rxCapture = o->pmrChan->b.txCapture = 0;
+ if (frxcapraw) {
+ fclose(frxcapraw);
+ frxcapraw = NULL;
+ }
+ if (frxcaptrace) {
+ fclose(frxcaptrace);
+ frxcaptrace = NULL;
+ }
+ if (frxoutraw) {
+ fclose(frxoutraw);
+ frxoutraw = NULL;
+ }
+ if (ftxcapraw) {
+ fclose(ftxcapraw);
+ ftxcapraw = NULL;
+ }
+ if (ftxcaptrace) {
+ fclose(ftxcaptrace);
+ ftxcaptrace = NULL;
+ }
+ if (ftxoutraw) {
+ fclose(ftxoutraw);
+ ftxoutraw = NULL;
+ }
+ } else if (!strcasecmp(a->argv[2], "rxtracecap")) {
+ if (!frxcaptrace)
+ frxcaptrace = fopen(RX_CAP_TRACE_FILE, "w");
+ ast_cli(a->fd, "Trace rx on.\n");
+ o->b.rxcap2 = o->pmrChan->b.rxCapture = 1;
+ } else if (!strcasecmp(a->argv[2], "txtracecap")) {
+ if (!ftxcaptrace)
+ ftxcaptrace = fopen(TX_CAP_TRACE_FILE, "w");
+ ast_cli(a->fd, "Trace tx on.\n");
+ o->b.txcap2 = o->pmrChan->b.txCapture = 1;
+ } else if (!strcasecmp(a->argv[2], "rxcap")) {
+ if (!frxcapraw)
+ frxcapraw = fopen(RX_CAP_RAW_FILE, "w");
+ ast_cli(a->fd, "cap rx raw on.\n");
+ o->b.rxcapraw = 1;
+ } else if (!strcasecmp(a->argv[2], "txcap")) {
+ if (!ftxcapraw)
+ ftxcapraw = fopen(TX_CAP_RAW_FILE, "w");
+ ast_cli(a->fd, "cap tx raw on.\n");
+ o->b.txcapraw = 1;
+ } else if (!strcasecmp(a->argv[2], "save")) {
+ tune_write(o);
+ ast_cli(a->fd, "Saved radio tuning settings to usbradio_tune.conf\n");
+ } else
+ return CLI_SHOWUSAGE;
+ return CLI_SUCCESS;
+}
+
+/*
+ set transmit ctcss modulation level
+ adjust mixer output or internal gain depending on output type
+ setting range is 0.0 to 0.9
+*/
+static int set_txctcss_level(struct chan_usbradio_pvt *o)
+{
+ if (o->txmixa == TX_OUT_LSD) {
+ o->txmixaset = (151 * o->txctcssadj) / 1000;
+ mixer_write(o);
+ mult_set(o);
+ } else if (o->txmixb == TX_OUT_LSD) {
+ o->txmixbset = (151 * o->txctcssadj) / 1000;
+ mixer_write(o);
+ mult_set(o);
+ } else {
+ *o->pmrChan->ptxCtcssAdjust = (o->txctcssadj * M_Q8) / 1000;
+ }
+ return 0;
+}
+/*
+ CLI debugging on and off
+*/
+static char *handle_cli_radio_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_usbradio_pvt *o = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "radio set debug [off]";
+ e->usage =
+ "Usage: radio set debug [off]\n"
+ " Enable/Disable radio debugging.\n";
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 4 && strncasecmp(a->argv[3], "off", 3))
+ return CLI_SHOWUSAGE;
+
+ o = find_desc(usbradio_active);
+
+ if (a->argc == 3)
+ o->debuglevel = 1;
+ else
+ o->debuglevel = 0;
+
+ ast_cli(a->fd, "USB Radio debugging %s.\n", o->debuglevel ? "enabled" : "disabled");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_usbradio[] = {
+ AST_CLI_DEFINE(handle_cli_radio_key, "Simulate Rx Signal Present"),
+ AST_CLI_DEFINE(handle_cli_radio_unkey, "Simulate Rx Signal Lusb"),
+ AST_CLI_DEFINE(handle_cli_radio_tune, "Radio Tune"),
+ AST_CLI_DEFINE(handle_cli_radio_set_debug, "Enable/Disable Radio Debugging"),
+};
+
+/*
+ * store the callerid components
+ */
+#if 0
+static void store_callerid(struct chan_usbradio_pvt *o, const char *s)
+{
+ ast_callerid_split(s, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
+}
+#endif
+
+static void store_rxdemod(struct chan_usbradio_pvt *o, const char *s)
+{
+ if (!strcasecmp(s, "no")) {
+ o->rxdemod = RX_AUDIO_NONE;
+ } else if (!strcasecmp(s, "speaker")) {
+ o->rxdemod = RX_AUDIO_SPEAKER;
+ } else if (!strcasecmp(s, "flat")) {
+ o->rxdemod = RX_AUDIO_FLAT;
+ } else {
+ ast_log(LOG_WARNING, "Unrecognized rxdemod parameter: %s\n", s);
+ }
+
+ ast_debug(4, "set rxdemod = %s\n", s);
+}
+
+
+static void store_txmixa(struct chan_usbradio_pvt *o, const char *s)
+{
+ if (!strcasecmp(s, "no"))
+ o->txmixa = TX_OUT_OFF;
+
+ else if (!strcasecmp(s, "voice"))
+ o->txmixa = TX_OUT_VOICE;
+ else if (!strcasecmp(s, "tone"))
+ o->txmixa = TX_OUT_LSD;
+ else if (!strcasecmp(s, "composite"))
+ o->txmixa = TX_OUT_COMPOSITE;
+ else if (!strcasecmp(s, "auxvoice"))
+ o->txmixb = TX_OUT_AUX;
+ else
+ ast_log(LOG_WARNING, "Unrecognized txmixa parameter: %s\n", s);
+
+ ast_debug(4, "set txmixa = %s\n", s);
+}
+
+static void store_txmixb(struct chan_usbradio_pvt *o, const char *s)
+{
+ if (!strcasecmp(s, "no"))
+ o->txmixb = TX_OUT_OFF;
+ else if (!strcasecmp(s, "voice"))
+ o->txmixb = TX_OUT_VOICE;
+ else if (!strcasecmp(s, "tone"))
+ o->txmixb = TX_OUT_LSD;
+ else if (!strcasecmp(s, "composite"))
+ o->txmixb = TX_OUT_COMPOSITE;
+ else if (!strcasecmp(s, "auxvoice"))
+ o->txmixb = TX_OUT_AUX;
+ else
+ ast_log(LOG_WARNING, "Unrecognized txmixb parameter: %s\n", s);
+
+ ast_debug(4, "set txmixb = %s\n", s);
+}
+
+static void store_rxcdtype(struct chan_usbradio_pvt *o, const char *s)
+{
+ if (!strcasecmp(s, "no"))
+ o->rxcdtype = CD_IGNORE;
+ else if (!strcasecmp(s, "usb"))
+ o->rxcdtype = CD_HID;
+ else if (!strcasecmp(s, "dsp"))
+ o->rxcdtype = CD_XPMR_NOISE;
+ else if (!strcasecmp(s, "vox"))
+ o->rxcdtype = CD_XPMR_VOX;
+ else if (!strcasecmp(s, "usbinvert"))
+ o->rxcdtype = CD_HID_INVERT;
+ else
+ ast_log(LOG_WARNING, "Unrecognized rxcdtype parameter: %s\n", s);
+
+ ast_debug(4, "set rxcdtype = %s\n", s);
+}
+
+static void store_rxsdtype(struct chan_usbradio_pvt *o, const char *s)
+{
+ if (!strcasecmp(s, "no") || !strcasecmp(s, "SD_IGNORE"))
+ o->rxsdtype = SD_IGNORE;
+ else if (!strcasecmp(s, "usb") || !strcasecmp(s, "SD_HID"))
+ o->rxsdtype = SD_HID;
+ else if (!strcasecmp(s, "usbinvert") || !strcasecmp(s, "SD_HID_INVERT"))
+ o->rxsdtype = SD_HID_INVERT;
+ else if (!strcasecmp(s, "software") || !strcasecmp(s, "SD_XPMR"))
+ o->rxsdtype = SD_XPMR;
+ else
+ ast_log(LOG_WARNING, "Unrecognized rxsdtype parameter: %s\n", s);
+
+ ast_debug(4, "set rxsdtype = %s\n", s);
+}
+
+static void store_rxgain(struct chan_usbradio_pvt *o, const char *s)
+{
+ float f;
+ if (sscanf(s, "%f", &f) == 1)
+ o->rxgain = f;
+ ast_debug(4, "set rxgain = %f\n", f);
+}
+
+static void store_rxvoiceadj(struct chan_usbradio_pvt *o, const char *s)
+{
+ float f;
+ if (sscanf(s, "%f", &f) == 1)
+ o->rxvoiceadj = f;
+ ast_debug(4, "set rxvoiceadj = %f\n", f);
+}
+
+static void store_rxctcssadj(struct chan_usbradio_pvt *o, const char *s)
+{
+ float f;
+ if (sscanf(s, "%f", &f) == 1)
+ o->rxctcssadj = f;
+ ast_debug(4, "set rxctcssadj = %f\n", f);
+}
+
+static void store_txtoctype(struct chan_usbradio_pvt *o, const char *s)
+{
+ if (!strcasecmp(s, "no") || !strcasecmp(s, "TOC_NONE"))
+ o->txtoctype = TOC_NONE;
+ else if (!strcasecmp(s, "phase") || !strcasecmp(s, "TOC_PHASE"))
+ o->txtoctype = TOC_PHASE;
+ else if (!strcasecmp(s, "notone") || !strcasecmp(s, "TOC_NOTONE"))
+ o->txtoctype = TOC_NOTONE;
+ else
+ ast_log(LOG_WARNING, "Unrecognized txtoctype parameter: %s\n", s);
+
+ ast_debug(4, "set txtoctype = %s\n", s);
+}
+
+static void store_rxctcssfreq(struct chan_usbradio_pvt *o, const char *s)
+{
+ float f;
+ if (sscanf(s, "%f", &f) == 1)
+ o->rxctcssfreq = f;
+ ast_debug(4, "set rxctcss = %f\n", f);
+}
+
+static void store_txctcssfreq(struct chan_usbradio_pvt *o, const char *s)
+{
+ float f;
+ if (sscanf(s, "%f", &f) == 1)
+ o->txctcssfreq = f;
+ ast_debug(4, "set txctcss = %f\n", f);
+}
+
+static void tune_txoutput(struct chan_usbradio_pvt *o, int value)
+{
+ o->txtestkey = 1;
+ o->pmrChan->txPttIn = 1;
+
+#if 0
+ /* generate 1KHz tone at 7200 peak */
+ o->pmrChan->spsSigGen1->freq = 10000;
+ o->pmrChan->spsSigGen1->outputGain = (float)(0.22 * M_Q8);
+ o->pmrChan->b.startSpecialTone = 1;
+#endif
+
+ TxTestTone(o->pmrChan, 1);
+
+ usleep(5000000);
+ /* o->pmrChan->b.stopSpecialTone = 1; */
+ usleep(100000);
+
+ TxTestTone(o->pmrChan, 0);
+
+ o->pmrChan->txPttIn = 0;
+ o->txtestkey = 0;
+}
+
+static void tune_rxinput(struct chan_usbradio_pvt *o)
+{
+ const int target = 23000;
+ const int tolerance = 2000;
+ const int settingmin = 1;
+ const int settingstart = 2;
+ const int maxtries = 12;
+
+ float settingmax;
+
+ int setting = 0, tries = 0, tmpdiscfactor, meas;
+ int tunetype = 0;
+
+ settingmax = o->micmax;
+
+ if (o->pmrChan->rxDemod)
+ tunetype = 1;
+
+ setting = settingstart;
+
+ while (tries < maxtries) {
+ setamixer(o->devicenum, MIXER_PARAM_MIC_CAPTURE_VOL, setting, 0);
+ setamixer(o->devicenum, MIXER_PARAM_MIC_BOOST, o->rxboostset, 0);
+ usleep(100000);
+ if (o->rxcdtype == CD_XPMR_VOX || o->rxdemod == RX_AUDIO_SPEAKER) {
+ ast_debug(4, "Measure Direct Input\n");
+ o->pmrChan->spsMeasure->source = o->pmrChan->spsRx->source;
+ o->pmrChan->spsMeasure->discfactor = 1000;
+ o->pmrChan->spsMeasure->enabled = 1;
+ o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0;
+ usleep(400000);
+ meas = o->pmrChan->spsMeasure->apeak;
+ o->pmrChan->spsMeasure->enabled = 0;
+ } else {
+ ast_debug(4, "Measure HF Noise\n");
+ tmpdiscfactor = o->pmrChan->spsRx->discfactor;
+ o->pmrChan->spsRx->discfactor = (i16)1000;
+ o->pmrChan->spsRx->discounteru = o->pmrChan->spsRx->discounterl = 0;
+ o->pmrChan->spsRx->amax = o->pmrChan->spsRx->amin = 0;
+ usleep(200000);
+ meas = o->pmrChan->rxRssi;
+ o->pmrChan->spsRx->discfactor = tmpdiscfactor;
+ o->pmrChan->spsRx->discounteru = o->pmrChan->spsRx->discounterl = 0;
+ o->pmrChan->spsRx->amax = o->pmrChan->spsRx->amin = 0;
+ }
+ if (!meas)
+ meas++;
+ ast_log(LOG_NOTICE, "tries=%d, setting=%d, meas=%i\n", tries, setting, meas);
+
+ if ( meas < (target - tolerance) || meas > (target + tolerance) || tries < 3)
+ setting = setting * target / meas;
+ else if (tries > 4 && meas > (target - tolerance) && meas < (target + tolerance) )
+ break;
+
+ if (setting < settingmin)
+ setting = settingmin;
+ else if (setting > settingmax)
+ setting = settingmax;
+
+ tries++;
+ }
+ ast_log(LOG_NOTICE, "DONE tries=%d, setting=%d, meas=%i\n", tries,
+ (setting * 1000) / o->micmax, meas);
+ if (meas < (target - tolerance) || meas > (target + tolerance))
+ ast_log(LOG_NOTICE, "ERROR: RX INPUT ADJUST FAILED.\n");
+ else {
+ ast_log(LOG_NOTICE, "INFO: RX INPUT ADJUST SUCCESS.\n");
+ o->rxmixerset = (setting * 1000) / o->micmax;
+ }
+}
+/*
+*/
+static void tune_rxvoice(struct chan_usbradio_pvt *o)
+{
+ const int target = 7200; /* peak */
+ const int tolerance = 360; /* peak to peak */
+ const float settingmin = 0.1;
+ const float settingmax = 4;
+ const float settingstart = 1;
+ const int maxtries = 12;
+
+ float setting;
+
+ int tries = 0, meas;
+
+ ast_log(LOG_NOTICE, "INFO: RX VOICE ADJUST START.\n");
+ ast_log(LOG_NOTICE, "target=%d tolerance=%d\n", target, tolerance);
+
+ if (!o->pmrChan->spsMeasure)
+ ast_log(LOG_ERROR, "NO MEASURE BLOCK.\n");
+
+ if (!o->pmrChan->spsMeasure->source || !o->pmrChan->prxVoiceAdjust )
+ ast_log(LOG_ERROR, "NO SOURCE OR MEASURE SETTING.\n");
+
+ o->pmrChan->spsMeasure->source = o->pmrChan->spsRxOut->sink;
+ o->pmrChan->spsMeasure->enabled = 1;
+ o->pmrChan->spsMeasure->discfactor = 1000;
+
+ setting=settingstart;
+
+ ast_debug(4, "ERROR: NO MEASURE BLOCK.\n");
+
+ while (tries < maxtries) {
+ *(o->pmrChan->prxVoiceAdjust) = setting * M_Q8;
+ usleep(10000);
+ o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0;
+ usleep(1000000);
+ meas = o->pmrChan->spsMeasure->apeak;
+ ast_log(LOG_NOTICE, "tries=%d, setting=%f, meas=%i\n", tries, setting, meas);
+
+ if (meas < (target - tolerance) || meas > (target + tolerance) || tries < 3)
+ setting = setting * target / meas;
+ else if (tries > 4 && meas > (target - tolerance) && meas < (target + tolerance))
+ break;
+ if (setting < settingmin)
+ setting = settingmin;
+ else if (setting > settingmax)
+ setting = settingmax;
+
+ tries++;
+ }
+
+ o->pmrChan->spsMeasure->enabled = 0;
+
+ ast_log(LOG_NOTICE, "DONE tries=%d, setting=%f, meas=%f\n", tries, setting, (float)meas);
+ if (meas < (target - tolerance) || meas > (target + tolerance))
+ ast_log(LOG_ERROR, "RX VOICE GAIN ADJUST FAILED.\n");
+ else {
+ ast_log(LOG_NOTICE, "RX VOICE GAIN ADJUST SUCCESS.\n");
+ o->rxvoiceadj = setting;
+ }
+}
+
+static void tune_rxctcss(struct chan_usbradio_pvt *o)
+{
+ const int target = 4096;
+ const int tolerance = 100;
+ const float settingmin = 0.1;
+ const float settingmax = 4;
+ const float settingstart = 1;
+ const int maxtries = 12;
+
+ float setting;
+ int tries = 0, meas;
+
+ ast_log(LOG_NOTICE, "RX CTCSS ADJUST START.\n");
+ ast_log(LOG_NOTICE, "target=%d tolerance=%d \n", target, tolerance);
+
+ o->pmrChan->spsMeasure->source = o->pmrChan->prxCtcssMeasure;
+ o->pmrChan->spsMeasure->discfactor = 400;
+ o->pmrChan->spsMeasure->enabled = 1;
+
+ setting = settingstart;
+
+ while (tries < maxtries) {
+ *(o->pmrChan->prxCtcssAdjust) = setting * M_Q8;
+ usleep(10000);
+ o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0;
+ usleep(500000);
+ meas = o->pmrChan->spsMeasure->apeak;
+ ast_debug(4, "tries=%d, setting=%f, meas=%i\n", tries, setting, meas);
+
+ if (meas < (target - tolerance) || meas > (target + tolerance) || tries < 3)
+ setting = setting * target / meas;
+ else if (tries > 4 && meas > (target - tolerance) && meas < (target + tolerance))
+ break;
+ if (setting < settingmin)
+ setting = settingmin;
+ else if (setting > settingmax)
+ setting = settingmax;
+
+ tries++;
+ }
+ o->pmrChan->spsMeasure->enabled = 0;
+ ast_debug(4, "DONE tries=%d, setting=%f, meas=%f\n", tries, setting, (float)meas);
+ if (meas < (target - tolerance) || meas > (target + tolerance))
+ ast_log(LOG_ERROR, "RX CTCSS GAIN ADJUST FAILED.\n");
+ else {
+ ast_log(LOG_NOTICE, "RX CTCSS GAIN ADJUST SUCCESS.\n");
+ o->rxctcssadj = setting;
+ }
+}
+/*
+ this file then is included in chan_usbradio.conf
+ #include /etc/asterisk/usbradio_tune.conf
+*/
+static void tune_write(struct chan_usbradio_pvt *o)
+{
+ FILE *fp;
+
+ fp = fopen("/etc/asterisk/usbradio_tune.conf", "w");
+
+ if (!strcmp(o->name, "dsp"))
+ fprintf(fp, "[general]\n");
+ else
+ fprintf(fp, "[%s]\n", o->name);
+
+ fprintf(fp, "; name=%s\n", o->name);
+ fprintf(fp, "; devicenum=%d\n", o->devicenum);
+
+ fprintf(fp, "rxmixerset=%d\n", o->rxmixerset);
+ fprintf(fp, "rxboostset=%d\n", o->rxboostset);
+ fprintf(fp, "txmixaset=%d\n", o->txmixaset);
+ fprintf(fp, "txmixbset=%d\n", o->txmixbset);
+
+ fprintf(fp, "rxvoiceadj=%f\n", o->rxvoiceadj);
+ fprintf(fp, "rxctcssadj=%f\n", o->rxctcssadj);
+ fprintf(fp, "txctcssadj=%d\n", o->txctcssadj);
+
+ fprintf(fp, "rxsquelchadj=%d\n", o->rxsquelchadj);
+ fclose(fp);
+}
+
+static void mixer_write(struct chan_usbradio_pvt *o)
+{
+ setamixer(o->devicenum, MIXER_PARAM_MIC_PLAYBACK_SW, 0, 0);
+ setamixer(o->devicenum, MIXER_PARAM_MIC_PLAYBACK_VOL, 0, 0);
+ setamixer(o->devicenum, MIXER_PARAM_SPKR_PLAYBACK_SW, 1, 0);
+ setamixer(o->devicenum, MIXER_PARAM_SPKR_PLAYBACK_VOL,
+ o->txmixaset * o->spkrmax / 1000,
+ o->txmixbset * o->spkrmax / 1000);
+ setamixer(o->devicenum, MIXER_PARAM_MIC_CAPTURE_VOL,
+ o->rxmixerset * o->micmax / 1000, 0);
+ setamixer(o->devicenum, MIXER_PARAM_MIC_BOOST, o->rxboostset, 0);
+ setamixer(o->devicenum, MIXER_PARAM_MIC_CAPTURE_SW, 1, 0);
+}
+/*
+ adjust dsp multiplier to add resolution to tx level adjustment
+*/
+static void mult_set(struct chan_usbradio_pvt *o)
+{
+
+ if (o->pmrChan->spsTxOutA) {
+ o->pmrChan->spsTxOutA->outputGain =
+ mult_calc((o->txmixaset * 152) / 1000);
+ }
+ if (o->pmrChan->spsTxOutB) {
+ o->pmrChan->spsTxOutB->outputGain =
+ mult_calc((o->txmixbset * 152) / 1000);
+ }
+}
+/*
+ * input 0 - 151 outputs are pot and multiplier
+ */
+static int mult_calc(int value)
+{
+ const int multx = M_Q8;
+ int pot, mult;
+
+ pot= ((int)(value / 4) * 4) + 2;
+ mult = multx - ((multx * (3 - (value % 4))) / (pot + 2));
+ return mult;
+}
+
+#define pd(x) ast_debug(4, #x" = %d\n", x)
+#define pp(x) ast_debug(4, #x" = %p\n", x)
+#define ps(x) ast_debug(4, #x" = %s\n", x)
+#define pf(x) ast_debug(4, #x" = %f\n", x)
+/*
+*/
+static void pmrdump(struct chan_usbradio_pvt *o)
+{
+ t_pmr_chan *p;
+
+ p = o->pmrChan;
+
+ ast_debug(4, "odump()\n");
+
+ pd(o->devicenum);
+
+ pd(o->rxdemod);
+ pd(o->rxcdtype);
+ pd(o->rxsdtype);
+ pd(o->txtoctype);
+
+ pd(o->rxmixerset);
+ pf(o->rxvoiceadj);
+ pf(o->rxctcssadj);
+ pd(o->rxsquelchadj);
+
+ pd(o->txprelim);
+ pd(o->txmixa);
+ pd(o->txmixb);
+
+ pd(o->txmixaset);
+ pd(o->txmixbset);
+
+ ast_debug(4, "pmrdump()\n");
+
+ ast_debug(4, "prxSquelchAdjust=%d\n", *(o->pmrChan->prxSquelchAdjust));
+
+ pd(p->rxCarrierPoint);
+ pd(p->rxCarrierHyst);
+
+ pd(p->rxCtcss->relax);
+ pf(p->rxCtcssFreq);
+ pd(p->rxCtcssIndex);
+ pf(p->txCtcssFreq);
+
+ pd(p->txMixA);
+ pd(p->txMixB);
+
+ pd(p->rxDeEmpEnable);
+ pd(p->rxCenterSlicerEnable);
+ pd(p->rxCtcssDecodeEnable);
+ pd(p->rxDcsDecodeEnable);
+
+ pd(p->txHpfEnable);
+ pd(p->txLimiterEnable);
+ pd(p->txPreEmpEnable);
+ pd(p->txLpfEnable);
+
+ if (p->spsTxOutA)
+ pd(p->spsTxOutA->outputGain);
+ if (p->spsTxOutB)
+ pd(p->spsTxOutB->outputGain);
+
+ return;
+}
+
+
+/*
+ * grab fields from the config file, init the descriptor and open the device.
+ */
+static struct chan_usbradio_pvt *store_config(struct ast_config *cfg, char *ctg)
+{
+ struct ast_variable *v;
+ struct chan_usbradio_pvt *o;
+ struct ast_config *cfg1;
+ struct ast_flags config_flags = { 0 };
+
+ if (ctg == NULL) {
+ traceusb1(" store_config() ctg == NULL\n");
+ o = &usbradio_default;
+ ctg = "general";
+ } else {
+ if (!(o = ast_calloc(1, sizeof(*o)))){
+ return NULL;
+ }
+ *o = usbradio_default;
+ /* "general" is also the default thing */
+ if (strcmp(ctg, "general") == 0) {
+ o->name = ast_strdup("dsp");
+ usbradio_active = o->name;
+ } else
+ o->name = ast_strdup(ctg);
+ }
+
+ strcpy(o->mohinterpret, "default");
+ o->micmax = amixer_max(o->devicenum, MIXER_PARAM_MIC_CAPTURE_VOL);
+ o->spkrmax = amixer_max(o->devicenum, MIXER_PARAM_SPKR_PLAYBACK_VOL);
+ /* fill other fields from configuration */
+ for (v = ast_variable_browse(cfg, ctg); v; v = v->next) {
+
+ /* handle jb conf */
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+ continue;
+ CV_START(v->name, v->value);
+
+ CV_UINT("frags", o->frags);
+ CV_UINT("queuesize", o->queuesize);
+ CV_UINT("devicenum", o->devicenum);
+ CV_UINT("debug", usbradio_debug);
+ CV_BOOL("rxcpusaver", o->rxcpusaver);
+ CV_BOOL("txcpusaver", o->txcpusaver);
+ CV_BOOL("invertptt", o->invertptt);
+ CV_F("rxdemod", store_rxdemod(o, v->value));
+ CV_BOOL("txprelim", o->txprelim);;
+ CV_F("txmixa", store_txmixa(o, v->value));
+ CV_F("txmixb", store_txmixb(o, v->value));
+ CV_F("carrierfrom", store_rxcdtype(o, v->value));
+ CV_F("rxsdtype", store_rxsdtype(o, v->value));
+ CV_F("rxctcssfreq", store_rxctcssfreq(o, v->value));
+ CV_F("txctcssfreq", store_txctcssfreq(o, v->value));
+ CV_F("rxgain", store_rxgain(o, v->value));
+ CV_BOOL("rxboostset", o->rxboostset);
+ CV_UINT("rxctcssrelax", o->rxctcssrelax);
+ CV_F("txtoctype", store_txtoctype(o, v->value));
+ CV_UINT("hdwtype", o->hdwtype);
+ CV_UINT("duplex", o->radioduplex);
+
+ CV_END;
+ }
+
+ cfg1 = ast_config_load(config1, config_flags);
+ if (!cfg1) {
+ o->rxmixerset = 500;
+ o->txmixaset = 500;
+ o->txmixbset = 500;
+ o->rxvoiceadj = 0.5;
+ o->rxctcssadj = 0.5;
+ o->txctcssadj = 200;
+ o->rxsquelchadj = 500;
+ ast_log(LOG_WARNING, "File %s not found, using default parameters.\n", config1);
+ } else {
+ for (v = ast_variable_browse(cfg1, ctg); v; v = v->next) {
+
+ CV_START(v->name, v->value);
+ CV_UINT("rxmixerset", o->rxmixerset);
+ CV_UINT("txmixaset", o->txmixaset);
+ CV_UINT("txmixbset", o->txmixbset);
+ CV_F("rxvoiceadj", store_rxvoiceadj(o, v->value));
+ CV_F("rxctcssadj", store_rxctcssadj(o, v->value));
+ CV_UINT("txctcssadj", o->txctcssadj);
+ CV_UINT("rxsquelchadj", o->rxsquelchadj);
+ CV_END;
+ }
+ ast_config_destroy(cfg1);
+ }
+
+ o->debuglevel = 0;
+
+ if (o == &usbradio_default) /* we are done with the default */
+ return NULL;
+
+ o->lastopen = ast_tvnow(); /* don't leave it 0 or tvdiff may wrap */
+ o->dsp = ast_dsp_new();
+ if (o->dsp) {
+ ast_dsp_set_features(o->dsp, DSP_FEATURE_DTMF_DETECT);
+ ast_dsp_digitmode(o->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_RELAXDTMF);
+ }
+
+ if (o->rxctcssfreq != 0 && o->rxdemod == RX_AUDIO_SPEAKER)
+ ast_log(LOG_ERROR, "Incompatable Options o->rxctcssfreq=%f and o->rxdemod=speaker\n", o->rxctcssfreq);
+
+ if (o->pmrChan == NULL) {
+ t_pmr_chan tChan;
+
+ memset(&tChan, 0, sizeof(tChan));
+
+ tChan.rxDemod = o->rxdemod;
+ tChan.rxCdType = o->rxcdtype;
+
+ tChan.txMod = o->txprelim;
+
+ tChan.txMixA = o->txmixa;
+ tChan.txMixB = o->txmixb;
+
+ tChan.rxCpuSaver = o->rxcpusaver;
+ tChan.txCpuSaver = o->txcpusaver;
+
+ tChan.rxCtcssFreq = o->rxctcssfreq;
+ tChan.txCtcssFreq = o->txctcssfreq;
+
+ o->pmrChan = createPmrChannel(&tChan, FRAME_SIZE);
+
+ o->pmrChan->radioDuplex = o->radioduplex;
+
+ o->pmrChan->rxCpuSaver = o->rxcpusaver;
+ o->pmrChan->txCpuSaver = o->txcpusaver;
+
+ *(o->pmrChan->prxSquelchAdjust) =
+ ((999 - o->rxsquelchadj) * 32767) / 1000;
+
+ o->pmrChan->spsRx->outputGain = o->rxvoiceadj*M_Q8;
+
+ o->pmrChan->txTocType = o->txtoctype;
+
+ if ((o->txmixa == TX_OUT_LSD) ||
+ (o->txmixa == TX_OUT_COMPOSITE) ||
+ (o->txmixb == TX_OUT_LSD) ||
+ (o->txmixb == TX_OUT_COMPOSITE)) {
+ *(o->pmrChan->prxCtcssAdjust) = o->rxctcssadj * M_Q8;
+ set_txctcss_level(o);
+ }
+
+ o->pmrChan->rxCtcss->relax = o->rxctcssrelax;
+
+ }
+
+ if ((o->txmixa != TX_OUT_VOICE) && (o->txmixb != TX_OUT_VOICE) &&
+ (o->txmixa != TX_OUT_COMPOSITE) && (o->txmixb != TX_OUT_COMPOSITE))
+ ast_log(LOG_ERROR, "No txvoice output configured.\n");
+
+ if (o->txctcssfreq &&
+ o->txmixa != TX_OUT_LSD && o->txmixa != TX_OUT_COMPOSITE &&
+ o->txmixb != TX_OUT_LSD && o->txmixb != TX_OUT_COMPOSITE)
+ ast_log(LOG_ERROR, "No txtone output configured.\n");
+
+ if (o->rxctcssfreq && o->pmrChan->rxCtcssIndex < 0)
+ ast_log(LOG_ERROR, "Invalid CTCSS Frequency.\n");
+
+ /* RxTestIt(o); */
+
+ mixer_write(o);
+ mult_set(o);
+ hidhdwconfig(o);
+
+ /* pmrdump(o); */
+
+ /* link into list of devices */
+ if (o != &usbradio_default) {
+ o->next = usbradio_default.next;
+ usbradio_default.next = o;
+ }
+ return o;
+}
+
+#if DEBUG_FILETEST == 1
+/*
+ Test It on a File
+*/
+int RxTestIt(struct chan_usbradio_pvt *o)
+{
+ const int numSamples = SAMPLES_PER_BLOCK;
+ const int numChannels = 16;
+
+ i16 sample, i, ii;
+
+ i32 txHangTime;
+
+ i16 txEnable;
+
+ t_pmr_chan tChan;
+ t_pmr_chan *pChan;
+
+ FILE *hInput = NULL, *hOutput = NULL, *hOutputTx = NULL;
+
+ i16 iBuff[numSamples * 2 * 6], oBuff[numSamples];
+
+ ast_debug(4, "RxTestIt()\n");
+
+ pChan = o->pmrChan;
+ pChan->b.txCapture = 1;
+ pChan->b.rxCapture = 1;
+
+ txEnable = 0;
+
+ hInput = fopen("/usr/src/xpmr/testdata/rx_in.pcm", "r");
+ if (!hInput){
+ ast_debug(4, " RxTestIt() File Not Found.\n");
+ return 0;
+ }
+ hOutput = fopen("/usr/src/xpmr/testdata/rx_debug.pcm", "w");
+
+ ast_debug(4, " RxTestIt() Working...\n");
+
+ while (!feof(hInput)) {
+ fread((void *)iBuff, 2, numSamples * 2 * 6, hInput);
+
+ if (txHangTime)
+ txHangTime -= numSamples;
+ if (txHangTime < 0)
+ txHangTime = 0;
+
+ if (pChan->rxCtcss->decode)
+ txHangTime = (8000 / 1000 * 2000);
+
+ if (pChan->rxCtcss->decode && !txEnable) {
+ txEnable = 1;
+ /* pChan->inputBlanking = (8000 / 1000 * 200); */
+ } else if (!pChan->rxCtcss->decode && txEnable) {
+ txEnable = 0;
+ }
+
+ PmrRx(pChan, iBuff, oBuff);
+
+ fwrite((void *)pChan->prxDebug, 2, numSamples * numChannels, hOutput);
+ }
+ pChan->b.txCapture = 0;
+ pChan->b.rxCapture = 0;
+
+ if (hInput)
+ fclose(hInput);
+ if (hOutput)
+ fclose(hOutput);
+
+ ast_debug(4, " RxTestIt() Complete.\n");
+
+ return 0;
+}
+#endif
+
+#include "./xpmr/xpmr.c"
+/*
+*/
+static int load_module(void)
+{
+ struct ast_config *cfg = NULL;
+ char *ctg = NULL;
+ struct ast_flags config_flags = { 0 };
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ /* load config file */
+ if (!(cfg = ast_config_load(config, config_flags))) {
+ ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ do {
+ store_config(cfg, ctg);
+ } while ( (ctg = ast_category_browse(cfg, ctg)) != NULL);
+
+ ast_config_destroy(cfg);
+
+ if (find_desc(usbradio_active) == NULL) {
+ ast_log(LOG_NOTICE, "Device %s not found\n", usbradio_active);
+ /* XXX we could default to 'dsp' perhaps ? */
+ /* XXX should cleanup allocated memory etc. */
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (ast_channel_register(&usbradio_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel type 'usb'\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ ast_cli_register_multiple(cli_usbradio, sizeof(cli_usbradio) / sizeof(struct ast_cli_entry));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+/*
+*/
+static int unload_module(void)
+{
+ struct chan_usbradio_pvt *o;
+
+ ast_log(LOG_WARNING, "unload_module() called\n");
+
+ ast_channel_unregister(&usbradio_tech);
+ ast_cli_unregister_multiple(cli_usbradio, sizeof(cli_usbradio) / sizeof(struct ast_cli_entry));
+
+ for (o = usbradio_default.next; o; o = o->next) {
+
+ ast_log(LOG_WARNING, "destroyPmrChannel() called\n");
+ if (o->pmrChan)
+ destroyPmrChannel(o->pmrChan);
+
+ #if DEBUG_CAPTURES == 1
+ if (frxcapraw) { fclose(frxcapraw); frxcapraw = NULL; }
+ if (frxcaptrace) { fclose(frxcaptrace); frxcaptrace = NULL; }
+ if (frxoutraw) { fclose(frxoutraw); frxoutraw = NULL; }
+ if (ftxcapraw) { fclose(ftxcapraw); ftxcapraw = NULL; }
+ if (ftxcaptrace) { fclose(ftxcaptrace); ftxcaptrace = NULL; }
+ if (ftxoutraw) { fclose(ftxoutraw); ftxoutraw = NULL; }
+ #endif
+
+ close(o->sounddev);
+ if (o->dsp)
+ ast_dsp_free(o->dsp);
+ if (o->owner)
+ ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD);
+ if (o->owner) /* XXX how ??? */
+ return -1;
+ /* XXX what about the thread ? */
+ /* XXX what about the memory allocated ? */
+ }
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "usb Console Channel Driver");
+
+/* end of file */
+
+
diff --git a/trunk/channels/chan_vpb.cc b/trunk/channels/chan_vpb.cc
new file mode 100644
index 000000000..eee797b18
--- /dev/null
+++ b/trunk/channels/chan_vpb.cc
@@ -0,0 +1,2899 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2003, Paul Bagyenda
+ * Paul Bagyenda <bagyenda@dsmagic.com>
+ * Copyright (C) 2004 - 2005, Ben Kramer
+ * Ben Kramer <ben@voicetronix.com.au>
+ *
+ * Daniel Bichara <daniel@bichara.com.br> - Brazilian CallerID detection (c)2004
+ *
+ * Welber Silveira - welberms@magiclink.com.br - (c)2004
+ * Copying CLID string to propper structure after detection
+ *
+ * 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 VoiceTronix Interface driver
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>vpbapi</depend>
+ ***/
+
+extern "C" {
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/callerid.h"
+#include "asterisk/dsp.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+}
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+
+#include <vpbapi.h>
+#include <assert.h>
+
+#ifdef pthread_create
+#undef pthread_create
+#endif
+
+#define DEFAULT_GAIN 0
+#define DEFAULT_ECHO_CANCEL 1
+
+#define VPB_SAMPLES 160
+#define VPB_MAX_BUF VPB_SAMPLES*4 + AST_FRIENDLY_OFFSET
+
+#define VPB_NULL_EVENT 200
+
+#define VPB_WAIT_TIMEOUT 4000
+
+#define MAX_VPB_GAIN 12.0
+#define MIN_VPB_GAIN -12.0
+
+#define DTMF_CALLERID
+#define DTMF_CID_START 'D'
+#define DTMF_CID_STOP 'C'
+
+/**/
+#if defined(__cplusplus) || defined(c_plusplus)
+ extern "C" {
+#endif
+/**/
+
+static const char desc[] = "VoiceTronix V6PCI/V12PCI/V4PCI API Support";
+static const char tdesc[] = "Standard VoiceTronix API Driver";
+static const char config[] = "vpb.conf";
+
+/* Default context for dialtone mode */
+static char context[AST_MAX_EXTENSION] = "default";
+
+/* Default language */
+static char language[MAX_LANGUAGE] = "";
+
+static int gruntdetect_timeout = 3600000; /* Grunt detect timeout is 1hr. */
+
+static const int prefformat = AST_FORMAT_SLINEAR;
+
+/* Protect the interface list (of vpb_pvt's) */
+AST_MUTEX_DEFINE_STATIC(iflock);
+
+/* Protect the monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+/* This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread;
+
+static int mthreadactive = -1; /* Flag for monitoring monitorthread.*/
+
+
+static int restart_monitor(void);
+
+/* The private structures of the VPB channels are
+ linked for selecting outgoing channels */
+
+#define MODE_DIALTONE 1
+#define MODE_IMMEDIATE 2
+#define MODE_FXO 3
+
+/* Pick a country or add your own! */
+/* These are the tones that are played to the user */
+#define TONES_AU
+/* #define TONES_USA */
+
+#ifdef TONES_AU
+static VPB_TONE Dialtone = {440, 440, 440, -10, -10, -10, 5000, 0 };
+static VPB_TONE Busytone = {470, 0, 0, -10, -100, -100, 5000, 0 };
+static VPB_TONE Ringbacktone = {400, 50, 440, -10, -10, -10, 1400, 800 };
+#endif
+#ifdef TONES_USA
+static VPB_TONE Dialtone = {350, 440, 0, -16, -16, -100, 10000, 0};
+static VPB_TONE Busytone = {480, 620, 0, -10, -10, -100, 500, 500};
+static VPB_TONE Ringbacktone = {440, 480, 0, -20, -20, -100, 2000, 4000};
+#endif
+
+/* grunt tone defn's */
+#if 0
+static VPB_DETECT toned_grunt = { 3, VPB_GRUNT, 1, 2000, 3000, 0, 0, -40, 0, 0, 0, 40, { { VPB_DELAY, 1000, 0, 0 }, { VPB_RISING, 0, 40, 0 }, { 0, 100, 0, 0 } } };
+#endif
+static VPB_DETECT toned_ungrunt = { 2, VPB_GRUNT, 1, 2000, 1, 0, 0, -40, 0, 0, 30, 40, { { 0, 0, 0, 0 } } };
+
+/* Use loop polarity detection for CID */
+static int UsePolarityCID=0;
+
+/* Use loop drop detection */
+static int UseLoopDrop=1;
+
+/* To use or not to use Native bridging */
+static int UseNativeBridge=1;
+
+/* Use Asterisk Indication or VPB */
+static int use_ast_ind=0;
+
+/* Use Asterisk DTMF detection or VPB */
+static int use_ast_dtmfdet=0;
+
+static int relaxdtmf=0;
+
+/* Use Asterisk DTMF play back or VPB */
+static int use_ast_dtmf=0;
+
+/* Break for DTMF on native bridge ? */
+static int break_for_dtmf=1;
+
+/* Set EC suppression threshold */
+static int ec_supp_threshold=-1;
+
+/* Inter Digit Delay for collecting DTMF's */
+static int dtmf_idd = 3000;
+
+#define TIMER_PERIOD_RINGBACK 2000
+#define TIMER_PERIOD_BUSY 700
+#define TIMER_PERIOD_RING 4000
+static int timer_period_ring = TIMER_PERIOD_RING;
+
+#define VPB_EVENTS_ALL (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP|VPB_MPLAY_UNDERFLOW \
+ |VPB_MRECORD_OVERFLOW|VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
+ |VPB_MRING_OFF|VPB_MDROP|VPB_MSTATION_FLASH)
+#define VPB_EVENTS_NODROP (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP|VPB_MPLAY_UNDERFLOW \
+ |VPB_MRECORD_OVERFLOW|VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
+ |VPB_MRING_OFF|VPB_MSTATION_FLASH)
+#define VPB_EVENTS_NODTMF (VPB_MRING|VPB_MDIGIT|VPB_MTONEDETECT|VPB_MTIMEREXP|VPB_MPLAY_UNDERFLOW \
+ |VPB_MRECORD_OVERFLOW|VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
+ |VPB_MRING_OFF|VPB_MDROP|VPB_MSTATION_FLASH)
+#define VPB_EVENTS_STAT (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP|VPB_MPLAY_UNDERFLOW \
+ |VPB_MRECORD_OVERFLOW|VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
+ |VPB_MRING_OFF|VPB_MSTATION_FLASH)
+
+
+/* Dialing parameters for Australia */
+/* #define DIAL_WITH_CALL_PROGRESS */
+VPB_TONE_MAP DialToneMap[] = { { VPB_BUSY_AUST, VPB_CALL_DISCONNECT, 0 },
+ { VPB_DIAL, VPB_CALL_DIALTONE, 0 },
+ { VPB_RINGBACK_308, VPB_CALL_RINGBACK, 0 },
+ { VPB_BUSY_AUST, VPB_CALL_BUSY, 0 },
+ { VPB_GRUNT, VPB_CALL_GRUNT, 0 },
+ { 0, 0, 1 } };
+#define VPB_DIALTONE_WAIT 2000 /* Wait up to 2s for a dialtone */
+#define VPB_RINGWAIT 4000 /* Wait up to 4s for ring tone after dialing */
+#define VPB_CONNECTED_WAIT 4000 /* If no ring tone detected for 4s then consider call connected */
+#define TIMER_PERIOD_NOANSWER 120000 /* Let it ring for 120s before deciding theres noone there */
+
+#define MAX_BRIDGES_V4PCI 2
+#define MAX_BRIDGES_V12PCI 128
+
+/* port states */
+#define VPB_STATE_ONHOOK 0
+#define VPB_STATE_OFFHOOK 1
+#define VPB_STATE_DIALLING 2
+#define VPB_STATE_JOINED 3
+#define VPB_STATE_GETDTMF 4
+#define VPB_STATE_PLAYDIAL 5
+#define VPB_STATE_PLAYBUSY 6
+#define VPB_STATE_PLAYRING 7
+
+#define VPB_GOT_RXHWG 1
+#define VPB_GOT_TXHWG 2
+#define VPB_GOT_RXSWG 4
+#define VPB_GOT_TXSWG 8
+
+typedef struct {
+ int inuse;
+ struct ast_channel *c0, *c1, **rc;
+ struct ast_frame **fo;
+ int flags;
+ ast_mutex_t lock;
+ ast_cond_t cond;
+ int endbridge;
+} vpb_bridge_t;
+
+static vpb_bridge_t * bridges;
+static int max_bridges = MAX_BRIDGES_V4PCI;
+
+AST_MUTEX_DEFINE_STATIC(bridge_lock);
+
+typedef enum {
+ vpb_model_unknown = 0,
+ vpb_model_v4pci,
+ vpb_model_v12pci
+} vpb_model_t;
+
+static struct vpb_pvt {
+
+ ast_mutex_t owner_lock; /* Protect blocks that expect ownership to remain the same */
+ struct ast_channel *owner; /* Channel who owns us, possibly NULL */
+
+ int golock; /* Got owner lock ? */
+
+ int mode; /* fxo/imediate/dialtone*/
+ int handle; /* Handle for vpb interface */
+
+ int state; /* used to keep port state (internal to driver) */
+
+ int group; /* Which group this port belongs to */
+ ast_group_t callgroup; /* Call group */
+ ast_group_t pickupgroup; /* Pickup group */
+
+
+ char dev[256]; /* Device name, eg vpb/1-1 */
+ vpb_model_t vpb_model; /* card model */
+
+ struct ast_frame f, fr; /* Asterisk frame interface */
+ char buf[VPB_MAX_BUF]; /* Static buffer for reading frames */
+
+ int dialtone; /* NOT USED */
+ float txgain, rxgain; /* Hardware gain control */
+ float txswgain, rxswgain; /* Software gain control */
+
+ int wantdtmf; /* Waiting for DTMF. */
+ char context[AST_MAX_EXTENSION]; /* The context for this channel */
+
+ char ext[AST_MAX_EXTENSION]; /* DTMF buffer for the ext[ens] */
+ char language[MAX_LANGUAGE]; /* language being used */
+ char callerid[AST_MAX_EXTENSION]; /* CallerId used for directly connected phone */
+ int callerid_type; /* Caller ID type: 0=>none 1=>vpb 2=>AstV23 3=>AstBell */
+ char cid_num[AST_MAX_EXTENSION];
+ char cid_name[AST_MAX_EXTENSION];
+
+ int dtmf_caller_pos; /* DTMF CallerID detection (Brazil)*/
+
+ int lastoutput; /* Holds the last Audio format output'ed */
+ int lastinput; /* Holds the last Audio format input'ed */
+ int last_ignore_dtmf;
+
+ void *busy_timer; /* Void pointer for busy vpb_timer */
+ int busy_timer_id; /* unique timer ID for busy timer */
+
+ void *ringback_timer; /* Void pointer for ringback vpb_timer */
+ int ringback_timer_id; /* unique timer ID for ringback timer */
+
+ void *ring_timer; /* Void pointer for ring vpb_timer */
+ int ring_timer_id; /* unique timer ID for ring timer */
+
+ void *dtmfidd_timer; /* Void pointer for DTMF IDD vpb_timer */
+ int dtmfidd_timer_id; /* unique timer ID for DTMF IDD timer */
+
+ struct ast_dsp *vad; /* AST Voice Activation Detection dsp */
+
+ struct timeval lastgrunt; /* time stamp of last grunt event */
+
+ ast_mutex_t lock; /* This one just protects bridge ptr below */
+ vpb_bridge_t *bridge;
+
+ int stopreads; /* Stop reading...*/
+ int read_state; /* Read state */
+ int chuck_count; /* a count of packets weve chucked away!*/
+ pthread_t readthread; /* For monitoring read channel. One per owned channel. */
+
+ ast_mutex_t record_lock; /* This one prevents reentering a record_buf block */
+ ast_mutex_t play_lock; /* This one prevents reentering a play_buf block */
+ int play_buf_time; /* How long the last play_buf took */
+ struct timeval lastplay; /* Last play time */
+
+ ast_mutex_t play_dtmf_lock;
+ char play_dtmf[16];
+
+ int faxhandled; /* has a fax tone been handled ? */
+
+ struct vpb_pvt *next; /* Next channel in list */
+
+} *iflist = NULL;
+
+static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, char *context);
+static void *do_chanreads(void *pvt);
+static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause);
+static int vpb_digit_begin(struct ast_channel *ast, char digit);
+static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int vpb_call(struct ast_channel *ast, char *dest, int timeout);
+static int vpb_hangup(struct ast_channel *ast);
+static int vpb_answer(struct ast_channel *ast);
+static struct ast_frame *vpb_read(struct ast_channel *ast);
+static int vpb_write(struct ast_channel *ast, struct ast_frame *frame);
+static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
+static int vpb_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+static struct ast_channel_tech vpb_tech = {
+ type: "vpb",
+ description: tdesc,
+ capabilities: AST_FORMAT_SLINEAR,
+ properties: 0,
+ requester: vpb_request,
+ devicestate: NULL,
+ send_digit_begin: vpb_digit_begin,
+ send_digit_end: vpb_digit_end,
+ call: vpb_call,
+ hangup: vpb_hangup,
+ answer: vpb_answer,
+ read: vpb_read,
+ write: vpb_write,
+ send_text: NULL,
+ send_image: NULL,
+ send_html: NULL,
+ exception: NULL,
+ bridge: ast_vpb_bridge,
+ indicate: vpb_indicate,
+ fixup: vpb_fixup,
+ setoption: NULL,
+ queryoption: NULL,
+ transfer: NULL,
+ write_video: NULL,
+ bridged_channel: NULL
+};
+
+static struct ast_channel_tech vpb_tech_indicate = {
+ type: "vpb",
+ description: tdesc,
+ capabilities: AST_FORMAT_SLINEAR,
+ properties: 0,
+ requester: vpb_request,
+ devicestate: NULL,
+ send_digit_begin: vpb_digit_begin,
+ send_digit_end: vpb_digit_end,
+ call: vpb_call,
+ hangup: vpb_hangup,
+ answer: vpb_answer,
+ read: vpb_read,
+ write: vpb_write,
+ send_text: NULL,
+ send_image: NULL,
+ send_html: NULL,
+ exception: NULL,
+ bridge: ast_vpb_bridge,
+ indicate: NULL,
+ fixup: vpb_fixup,
+ setoption: NULL,
+ queryoption: NULL,
+ transfer: NULL,
+ write_video: NULL,
+ bridged_channel: NULL
+};
+
+/* Can't get ast_vpb_bridge() working on v4pci without either a horrible
+* high pitched feedback noise or bad hiss noise depending on gain settings
+* Get asterisk to do the bridging
+*/
+#define BAD_V4PCI_BRIDGE
+
+/* This one enables a half duplex bridge which may be required to prevent high pitched
+ * feedback when getting asterisk to do the bridging and when using certain gain settings.
+ */
+/* #define HALF_DUPLEX_BRIDGE */
+
+/* This is the Native bridge code, which Asterisk will try before using its own bridging code */
+static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+{
+ struct vpb_pvt *p0 = (struct vpb_pvt *)c0->tech_pvt;
+ struct vpb_pvt *p1 = (struct vpb_pvt *)c1->tech_pvt;
+ int i;
+ int res;
+ struct ast_channel *cs[3];
+ struct ast_channel *who;
+ struct ast_frame *f;
+
+ cs[0] = c0;
+ cs[1] = c1;
+
+ #ifdef BAD_V4PCI_BRIDGE
+ if(p0->vpb_model==vpb_model_v4pci)
+ return AST_BRIDGE_FAILED_NOWARN;
+ #endif
+ if ( UseNativeBridge != 1){
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+/*
+ ast_mutex_lock(&p0->lock);
+ ast_mutex_lock(&p1->lock);
+*/
+
+ /* Bridge channels, check if we can. I believe we always can, so find a slot.*/
+
+ ast_mutex_lock(&bridge_lock); {
+ for (i = 0; i < max_bridges; i++)
+ if (!bridges[i].inuse)
+ break;
+ if (i < max_bridges) {
+ bridges[i].inuse = 1;
+ bridges[i].endbridge = 0;
+ bridges[i].flags = flags;
+ bridges[i].rc = rc;
+ bridges[i].fo = fo;
+ bridges[i].c0 = c0;
+ bridges[i].c1 = c1;
+ }
+ } ast_mutex_unlock(&bridge_lock);
+
+ if (i == max_bridges) {
+ ast_log(LOG_WARNING, "%s: vpb_bridge: Failed to bridge %s and %s!\n", p0->dev, c0->name, c1->name);
+ ast_mutex_unlock(&p0->lock);
+ ast_mutex_unlock(&p1->lock);
+ return AST_BRIDGE_FAILED_NOWARN;
+ } else {
+ /* Set bridge pointers. You don't want to take these locks while holding bridge lock.*/
+ ast_mutex_lock(&p0->lock); {
+ p0->bridge = &bridges[i];
+ } ast_mutex_unlock(&p0->lock);
+
+ ast_mutex_lock(&p1->lock); {
+ p1->bridge = &bridges[i];
+ } ast_mutex_unlock(&p1->lock);
+
+ ast_verb(2, "%s: vpb_bridge: Bridging call entered with [%s, %s]\n",p0->dev, c0->name, c1->name);
+ }
+
+ ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name);
+
+ #ifdef HALF_DUPLEX_BRIDGE
+
+ ast_verb(2, "%s: vpb_bridge: Starting half-duplex bridge [%s, %s]\n",p0->dev, c0->name, c1->name);
+
+ int dir = 0;
+
+ memset(p0->buf, 0, sizeof p0->buf);
+ memset(p1->buf, 0, sizeof p1->buf);
+
+ vpb_record_buf_start(p0->handle, VPB_ALAW);
+ vpb_record_buf_start(p1->handle, VPB_ALAW);
+
+ vpb_play_buf_start(p0->handle, VPB_ALAW);
+ vpb_play_buf_start(p1->handle, VPB_ALAW);
+
+ while( !bridges[i].endbridge ) {
+ struct vpb_pvt *from, *to;
+ if(++dir%2) {
+ from = p0;
+ to = p1;
+ } else {
+ from = p1;
+ to = p0;
+ }
+ vpb_record_buf_sync(from->handle, from->buf, VPB_SAMPLES);
+ vpb_play_buf_sync(to->handle, from->buf, VPB_SAMPLES);
+ }
+
+ vpb_record_buf_finish(p0->handle);
+ vpb_record_buf_finish(p1->handle);
+
+ vpb_play_buf_finish(p0->handle);
+ vpb_play_buf_finish(p1->handle);
+
+ ast_verb(2, "%s: vpb_bridge: Finished half-duplex bridge [%s, %s]\n",p0->dev, c0->name, c1->name);
+
+ res = VPB_OK;
+
+ #else
+
+ res = vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_ON, i+1 /* resource 1 & 2 only for V4PCI*/ );
+ if (res == VPB_OK) {
+ /* pthread_cond_wait(&bridges[i].cond, &bridges[i].lock);*/ /* Wait for condition signal. */
+ while( !bridges[i].endbridge ) {
+ /* Are we really ment to be doing nothing ?!?! */
+ who = ast_waitfor_n(cs, 2, &timeoutms);
+ if (!who) {
+ if (!timeoutms) {
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ ast_debug(1, "%s: vpb_bridge: Empty frame read...\n",p0->dev);
+ /* check for hangup / whentohangup */
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ f = ast_read(who);
+ if (!f || ((f->frametype == AST_FRAME_DTMF) &&
+ (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
+ *fo = f;
+ *rc = who;
+ ast_debug(1, "%s: vpb_bridge: Got a [%s]\n",p0->dev, f ? "digit" : "hangup");
+/*
+ if ((c0->tech_pvt == pvt0) && (!ast_check_hangup(c0))) {
+ if (pr0->set_rtp_peer(c0, NULL, NULL, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
+ }
+ if ((c1->tech_pvt == pvt1) && (!ast_check_hangup(c1))) {
+ if (pr1->set_rtp_peer(c1, NULL, NULL, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
+ }
+*/
+ /* That's all we needed */
+ /*return 0; */
+ /* Check if we need to break */
+ if (break_for_dtmf){
+ break;
+ }
+ else if ((f->frametype == AST_FRAME_DTMF) && ((f->subclass == '#')||(f->subclass == '*'))){
+ break;
+ }
+ } else {
+ if ((f->frametype == AST_FRAME_DTMF) ||
+ (f->frametype == AST_FRAME_VOICE) ||
+ (f->frametype == AST_FRAME_VIDEO))
+ {
+ /* Forward voice or DTMF frames if they happen upon us */
+ /* Actually I dont think we want to forward on any frames!
+ if (who == c0) {
+ ast_write(c1, f);
+ } else if (who == c1) {
+ ast_write(c0, f);
+ }
+ */
+ }
+ ast_frfree(f);
+ }
+ /* Swap priority not that it's a big deal at this point */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ };
+ vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_OFF, i+1 /* resource 1 & 2 only for V4PCI*/ );
+ }
+
+ #endif
+
+ ast_mutex_lock(&bridge_lock); {
+ bridges[i].inuse = 0;
+ } ast_mutex_unlock(&bridge_lock);
+
+ p0->bridge = NULL;
+ p1->bridge = NULL;
+
+
+ ast_verb(2, "Bridging call done with [%s, %s] => %d\n", c0->name, c1->name, res);
+
+/*
+ ast_mutex_unlock(&p0->lock);
+ ast_mutex_unlock(&p1->lock);
+*/
+ return (res==VPB_OK) ? AST_BRIDGE_COMPLETE : AST_BRIDGE_FAILED;
+}
+
+/* Caller ID can be located in different positions between the rings depending on your Telco
+ * Australian (Telstra) callerid starts 700ms after 1st ring and finishes 1.5s after first ring
+ * Use ANALYSE_CID to record rings and determine location of callerid
+ */
+/* #define ANALYSE_CID */
+#define RING_SKIP 300
+#define CID_MSECS 2000
+
+static void get_callerid(struct vpb_pvt *p)
+{
+ short buf[CID_MSECS*8]; /* 8kHz sampling rate */
+ struct timeval cid_record_time;
+ int rc;
+ struct ast_channel *owner = p->owner;
+/*
+ char callerid[AST_MAX_EXTENSION] = "";
+*/
+#ifdef ANALYSE_CID
+ void * ws;
+ char * file="cidsams.wav";
+#endif
+
+
+ if( ast_mutex_trylock(&p->record_lock) == 0 ) {
+
+ cid_record_time = ast_tvnow();
+ ast_verb(4, "CID record - start\n");
+
+ /* Skip any trailing ringtone */
+ if (UsePolarityCID != 1){
+ vpb_sleep(RING_SKIP);
+ }
+
+ ast_verb(4, "CID record - skipped %dms trailing ring\n",
+ ast_tvdiff_ms(ast_tvnow(), cid_record_time));
+ cid_record_time = ast_tvnow();
+
+ /* Record bit between the rings which contains the callerid */
+ vpb_record_buf_start(p->handle, VPB_LINEAR);
+ rc = vpb_record_buf_sync(p->handle, (char*)buf, sizeof(buf));
+ vpb_record_buf_finish(p->handle);
+#ifdef ANALYSE_CID
+ vpb_wave_open_write(&ws, file, VPB_LINEAR);
+ vpb_wave_write(ws,(char*)buf,sizeof(buf));
+ vpb_wave_close_write(ws);
+#endif
+
+ ast_verb(4, "CID record - recorded %dms between rings\n",
+ ast_tvdiff_ms(ast_tvnow(), cid_record_time));
+
+ ast_mutex_unlock(&p->record_lock);
+
+ if( rc != VPB_OK ) {
+ ast_log(LOG_ERROR, "Failed to record caller id sample on %s\n", p->dev );
+ return;
+ }
+
+ VPB_CID *cli_struct = new VPB_CID;
+ cli_struct->ra_cldn[0]=0;
+ cli_struct->ra_cn[0]=0;
+ /* This decodes FSK 1200baud type callerid */
+ if ((rc=vpb_cid_decode2(cli_struct, buf, CID_MSECS*8)) == VPB_OK ) {
+ /*
+ if (owner->cid.cid_num)
+ ast_free(owner->cid.cid_num);
+ owner->cid.cid_num=NULL;
+ if (owner->cid.cid_name)
+ ast_free(owner->cid.cid_name);
+ owner->cid.cid_name=NULL;
+ */
+
+ if (cli_struct->ra_cldn[0]=='\0'){
+ /*
+ owner->cid.cid_num = ast_strdup(cli_struct->cldn);
+ owner->cid.cid_name = ast_strdup(cli_struct->cn);
+ */
+ if (owner){
+ ast_set_callerid(owner, cli_struct->cldn, cli_struct->cn, cli_struct->cldn);
+ } else {
+ strcpy(p->cid_num, cli_struct->cldn);
+ strcpy(p->cid_name, cli_struct->cn);
+
+ }
+ ast_verb(4, "CID record - got [%s] [%s]\n",owner->cid.cid_num,owner->cid.cid_name );
+ snprintf(p->callerid,sizeof(p->callerid)-1,"%s %s",cli_struct->cldn,cli_struct->cn);
+ }
+ else {
+ ast_log(LOG_ERROR,"CID record - No caller id avalable on %s \n", p->dev);
+ }
+
+ } else {
+ ast_log(LOG_ERROR, "CID record - Failed to decode caller id on %s - %s\n", p->dev, vpb_strerror(rc) );
+ strncpy(p->callerid,"unknown", sizeof(p->callerid) - 1);
+ }
+ delete cli_struct;
+
+ } else
+ ast_log(LOG_ERROR, "CID record - Failed to set record mode for caller id on %s\n", p->dev );
+}
+
+static void get_callerid_ast(struct vpb_pvt *p)
+{
+ struct callerid_state *cs;
+ char buf[1024];
+ char *name=NULL, *number=NULL;
+ int flags;
+ int rc=0,vrc;
+ int sam_count=0;
+ struct ast_channel *owner = p->owner;
+ int which_cid;
+/*
+ float old_gain;
+*/
+#ifdef ANALYSE_CID
+ void * ws;
+ char * file="cidsams.wav";
+#endif
+
+ if(p->callerid_type == 1) {
+ ast_verb(4, "Collected caller ID already\n");
+ return;
+ }
+ else if(p->callerid_type == 2 ) {
+ which_cid=CID_SIG_V23;
+ ast_verb(4, "Collecting Caller ID v23...\n");
+ }
+ else if(p->callerid_type == 3) {
+ which_cid=CID_SIG_BELL;
+ ast_verb(4, "Collecting Caller ID bell...\n");
+ }
+ else {
+ ast_verb(4, "Caller ID disabled\n");
+ return;
+ }
+/* vpb_sleep(RING_SKIP); */
+/* vpb_record_get_gain(p->handle, &old_gain); */
+ cs = callerid_new(which_cid);
+ if (cs){
+#ifdef ANALYSE_CID
+ vpb_wave_open_write(&ws, file, VPB_MULAW);
+ vpb_record_set_gain(p->handle, 3.0);
+ vpb_record_set_hw_gain(p->handle,12.0);
+#endif
+ vpb_record_buf_start(p->handle, VPB_MULAW);
+ while((rc == 0)&&(sam_count<8000*3)){
+ vrc = vpb_record_buf_sync(p->handle, (char*)buf, sizeof(buf));
+ if (vrc != VPB_OK)
+ ast_log(LOG_ERROR, "%s: Caller ID couldnt read audio buffer!\n",p->dev);
+ rc = callerid_feed(cs,(unsigned char *)buf,sizeof(buf),AST_FORMAT_ULAW);
+#ifdef ANALYSE_CID
+ vpb_wave_write(ws,(char*)buf,sizeof(buf));
+#endif
+ sam_count+=sizeof(buf);
+ ast_verb(4, "Collecting Caller ID samples [%d][%d]...\n",sam_count,rc);
+ }
+ vpb_record_buf_finish(p->handle);
+#ifdef ANALYSE_CID
+ vpb_wave_close_write(ws);
+#endif
+ if (rc == 1){
+ callerid_get(cs, &name, &number, &flags);
+ ast_verb(1, "%s: Caller ID name [%s] number [%s] flags [%d]\n",p->dev,name, number,flags);
+ }
+ else {
+ ast_log(LOG_ERROR, "%s: Failed to decode Caller ID \n", p->dev );
+ }
+/* vpb_record_set_gain(p->handle, old_gain); */
+/* vpb_record_set_hw_gain(p->handle,6.0); */
+ }
+ else {
+ ast_log(LOG_ERROR, "%s: Failed to create Caller ID struct\n", p->dev );
+ }
+ if (owner->cid.cid_num) {
+ ast_free(owner->cid.cid_num);
+ owner->cid.cid_num = NULL;
+ }
+ if (owner->cid.cid_name) {
+ ast_free(owner->cid.cid_name);
+ owner->cid.cid_name = NULL;
+ }
+ if (number)
+ ast_shrink_phone_number(number);
+ ast_set_callerid(owner,
+ number, name,
+ owner->cid.cid_ani ? NULL : number);
+ if (!ast_strlen_zero(name)){
+ snprintf(p->callerid,(sizeof(p->callerid)-1),"%s %s",number,name);
+ } else {
+ snprintf(p->callerid,(sizeof(p->callerid)-1),"%s",number);
+ }
+ if (cs)
+ callerid_free(cs);
+}
+
+/* Terminate any tones we are presently playing */
+static void stoptone( int handle)
+{
+ int ret;
+ VPB_EVENT je;
+ while(vpb_playtone_state(handle)!=VPB_OK){
+ vpb_tone_terminate(handle);
+ ret = vpb_get_event_ch_async(handle,&je);
+ if ((ret == VPB_OK)&&(je.type != VPB_DIALEND)){
+ ast_verb(4, "Stop tone collected a wrong event!![%d]\n",je.type);
+/* vpb_put_event(&je); */
+ }
+ vpb_sleep(10);
+ }
+}
+
+/* Safe vpb_playtone_async */
+static int playtone( int handle, VPB_TONE *tone)
+{
+ int ret=VPB_OK;
+ stoptone(handle);
+ ast_verb(4, "[%02d]: Playing tone\n", handle);
+ ret = vpb_playtone_async(handle, tone);
+ return ret;
+}
+
+static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
+{
+ struct ast_frame f = {AST_FRAME_CONTROL}; /* default is control, Clear rest. */
+ int endbridge = 0;
+ int res=0;
+
+ ast_verb(4, "%s: handle_owned: got event: [%d=>%d]\n", p->dev, e->type, e->data);
+
+ f.src = "vpb";
+ switch (e->type) {
+ case VPB_RING:
+ if (p->mode == MODE_FXO) {
+ f.subclass = AST_CONTROL_RING;
+ vpb_timer_stop(p->ring_timer);
+ vpb_timer_start(p->ring_timer);
+ } else
+ f.frametype = AST_FRAME_NULL; /* ignore ring on station port. */
+ break;
+
+ case VPB_RING_OFF:
+ f.frametype = AST_FRAME_NULL;
+ break;
+
+ case VPB_TIMEREXP:
+ if (e->data == p->busy_timer_id) {
+ playtone(p->handle,&Busytone);
+ p->state = VPB_STATE_PLAYBUSY;
+ vpb_timer_stop(p->busy_timer);
+ vpb_timer_start(p->busy_timer);
+ f.frametype = AST_FRAME_NULL;
+ } else if (e->data == p->ringback_timer_id) {
+ playtone(p->handle, &Ringbacktone);
+ vpb_timer_stop(p->ringback_timer);
+ vpb_timer_start(p->ringback_timer);
+ f.frametype = AST_FRAME_NULL;
+ } else if (e->data == p->ring_timer_id) {
+ /* We didnt get another ring in time! */
+ if (p->owner->_state != AST_STATE_UP) {
+ /* Assume caller has hung up */
+ vpb_timer_stop(p->ring_timer);
+ f.subclass = AST_CONTROL_HANGUP;
+ } else {
+ vpb_timer_stop(p->ring_timer);
+ f.frametype = AST_FRAME_NULL;
+ }
+
+ } else {
+ f.frametype = AST_FRAME_NULL; /* Ignore. */
+ }
+ break;
+
+ case VPB_DTMF_DOWN:
+ case VPB_DTMF:
+ if (use_ast_dtmfdet){
+ f.frametype = AST_FRAME_NULL;
+ } else if (p->owner->_state == AST_STATE_UP) {
+ f.frametype = AST_FRAME_DTMF;
+ f.subclass = e->data;
+ } else
+ f.frametype = AST_FRAME_NULL;
+ break;
+
+ case VPB_TONEDETECT:
+ if (e->data == VPB_BUSY || e->data == VPB_BUSY_308 || e->data == VPB_BUSY_AUST ) {
+ ast_verb(4, "%s: handle_owned: got event: BUSY\n", p->dev);
+ if (p->owner->_state == AST_STATE_UP) {
+ f.subclass = AST_CONTROL_HANGUP;
+ }
+ else {
+ f.subclass = AST_CONTROL_BUSY;
+ }
+ }
+ else if (e->data == VPB_FAX){
+ if (!p->faxhandled){
+ if (strcmp(p->owner->exten, "fax")) {
+ const char *target_context = S_OR(p->owner->macrocontext, p->owner->context);
+
+ if (ast_exists_extension(p->owner, target_context, "fax", 1, p->owner->cid.cid_num)) {
+ ast_verb(3, "Redirecting %s to fax extension\n", p->owner->name);
+ /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
+ pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten);
+ if (ast_async_goto(p->owner, target_context, "fax", 1))
+ ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name, target_context);
+ } else
+ ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
+ } else
+ ast_debug(1, "Already in a fax extension, not redirecting\n");
+ } else
+ ast_debug(1, "Fax already handled\n");
+
+ }
+ else if (e->data == VPB_GRUNT) {
+ if ( ast_tvdiff_ms(ast_tvnow(), p->lastgrunt) > gruntdetect_timeout ) {
+ /* Nothing heard on line for a very long time
+ * Timeout connection */
+ ast_verb(3, "grunt timeout\n");
+ ast_log(LOG_NOTICE,"%s: Line hangup due of lack of conversation\n",p->dev);
+ f.subclass = AST_CONTROL_HANGUP;
+ } else {
+ p->lastgrunt = ast_tvnow();
+ f.frametype = AST_FRAME_NULL;
+ }
+ }
+ else {
+ f.frametype = AST_FRAME_NULL;
+ }
+ break;
+
+ case VPB_CALLEND:
+ #ifdef DIAL_WITH_CALL_PROGRESS
+ if (e->data == VPB_CALL_CONNECTED)
+ f.subclass = AST_CONTROL_ANSWER;
+ else if (e->data == VPB_CALL_NO_DIAL_TONE || e->data == VPB_CALL_NO_RING_BACK)
+ f.subclass = AST_CONTROL_CONGESTION;
+ else if (e->data == VPB_CALL_NO_ANSWER || e->data == VPB_CALL_BUSY)
+ f.subclass = AST_CONTROL_BUSY;
+ else if (e->data == VPB_CALL_DISCONNECTED)
+ f.subclass = AST_CONTROL_HANGUP;
+ #else
+ ast_log(LOG_NOTICE,"%s: Got call progress callback but blind dialing \n", p->dev);
+ f.frametype = AST_FRAME_NULL;
+ #endif
+ break;
+
+ case VPB_STATION_OFFHOOK:
+ f.subclass = AST_CONTROL_ANSWER;
+ break;
+
+ case VPB_DROP:
+ if ((p->mode == MODE_FXO)&&(UseLoopDrop)){ /* ignore loop drop on stations */
+ if (p->owner->_state == AST_STATE_UP)
+ f.subclass = AST_CONTROL_HANGUP;
+ else
+ f.frametype = AST_FRAME_NULL;
+ }
+ break;
+ case VPB_LOOP_ONHOOK:
+ if (p->owner->_state == AST_STATE_UP)
+ f.subclass = AST_CONTROL_HANGUP;
+ else
+ f.frametype = AST_FRAME_NULL;
+ break;
+ case VPB_STATION_ONHOOK:
+ f.subclass = AST_CONTROL_HANGUP;
+ break;
+
+ case VPB_STATION_FLASH:
+ f.subclass = AST_CONTROL_FLASH;
+ break;
+
+ /* Called when dialing has finished and ringing starts
+ * No indication that call has really been answered when using blind dialing
+ */
+ case VPB_DIALEND:
+ if (p->state < 5){
+ f.subclass = AST_CONTROL_ANSWER;
+ ast_verb(2, "%s: Dialend\n", p->dev);
+ } else {
+ f.frametype = AST_FRAME_NULL;
+ }
+ break;
+
+ case VPB_PLAY_UNDERFLOW:
+ f.frametype = AST_FRAME_NULL;
+ vpb_reset_play_fifo_alarm(p->handle);
+ break;
+
+ case VPB_RECORD_OVERFLOW:
+ f.frametype = AST_FRAME_NULL;
+ vpb_reset_record_fifo_alarm(p->handle);
+ break;
+
+ default:
+ f.frametype = AST_FRAME_NULL;
+ break;
+ }
+
+/*
+ ast_verb(4, "%s: LOCKING in handle_owned [%d]\n", p->dev,res);
+ res = ast_mutex_lock(&p->lock);
+ ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ {
+ if (p->bridge) { /* Check what happened, see if we need to report it. */
+ switch (f.frametype) {
+ case AST_FRAME_DTMF:
+ if ( !(p->bridge->c0 == p->owner &&
+ (p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_0) ) &&
+ !(p->bridge->c1 == p->owner &&
+ (p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_1) ))
+ /* Kill bridge, this is interesting. */
+ endbridge = 1;
+ break;
+
+ case AST_FRAME_CONTROL:
+ if (!(p->bridge->flags & AST_BRIDGE_IGNORE_SIGS))
+ #if 0
+ if (f.subclass == AST_CONTROL_BUSY ||
+ f.subclass == AST_CONTROL_CONGESTION ||
+ f.subclass == AST_CONTROL_HANGUP ||
+ f.subclass == AST_CONTROL_FLASH)
+ #endif
+ endbridge = 1;
+ break;
+
+ default:
+ break;
+ }
+ if (endbridge) {
+ if (p->bridge->fo)
+ *p->bridge->fo = ast_frisolate(&f);
+ if (p->bridge->rc)
+ *p->bridge->rc = p->owner;
+
+ ast_mutex_lock(&p->bridge->lock); {
+ p->bridge->endbridge = 1;
+ ast_cond_signal(&p->bridge->cond);
+ } ast_mutex_unlock(&p->bridge->lock);
+ }
+ }
+ }
+
+ if (endbridge){
+ res = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in handle_owned [%d]\n", p->dev,res);
+*/
+ return 0;
+ }
+
+ ast_verb(4, "%s: handle_owned: Prepared frame type[%d]subclass[%d], bridge=%p owner=[%s]\n",
+ p->dev, f.frametype, f.subclass, (void *)p->bridge, p->owner->name);
+
+ /* Trylock used here to avoid deadlock that can occur if we
+ * happen to be in here handling an event when hangup is called
+ * Problem is that hangup holds p->owner->lock
+ */
+ if ((f.frametype >= 0)&& (f.frametype != AST_FRAME_NULL)&&(p->owner)) {
+ if (ast_mutex_trylock(&p->owner->lock)==0) {
+ ast_queue_frame(p->owner, &f);
+ ast_mutex_unlock(&p->owner->lock);
+ ast_verb(4, "%s: handled_owned: Queued Frame to [%s]\n", p->dev,p->owner->name);
+ } else {
+ ast_verbose("%s: handled_owned: Missed event %d/%d \n",
+ p->dev,f.frametype, f.subclass);
+ }
+ }
+ res = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in handle_owned [%d]\n", p->dev,res);
+*/
+
+ return 0;
+}
+
+static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e)
+{
+ char s[2] = {0};
+ struct ast_channel *owner = p->owner;
+ char cid_num[256];
+ char cid_name[256];
+/*
+ struct ast_channel *c;
+*/
+
+ char str[VPB_MAX_STR];
+ vpb_translate_event(e, str);
+ ast_verb(4, "%s: handle_notowned: mode=%d, event[%d][%s]=[%d]\n", p->dev, p->mode, e->type,str, e->data);
+
+ switch(e->type) {
+ case VPB_LOOP_ONHOOK:
+ case VPB_LOOP_POLARITY:
+ if (UsePolarityCID == 1){
+ ast_verb(4, "Polarity reversal\n");
+ if(p->callerid_type == 1) {
+ ast_verb(4, "Using VPB Caller ID\n");
+ get_callerid(p); /* UK CID before 1st ring*/
+ }
+/* get_callerid_ast(p); */ /* Caller ID using the ast functions */
+ }
+ break;
+ case VPB_RING:
+ if (p->mode == MODE_FXO) /* FXO port ring, start * */ {
+ vpb_new(p, AST_STATE_RING, p->context);
+ if (UsePolarityCID != 1){
+ if(p->callerid_type == 1) {
+ ast_verb(4, "Using VPB Caller ID\n");
+ get_callerid(p); /* Australian CID only between 1st and 2nd ring */
+ }
+ get_callerid_ast(p); /* Caller ID using the ast functions */
+ }
+ else {
+ ast_log(LOG_ERROR, "Setting caller ID: %s %s\n",p->cid_num, p->cid_name);
+ ast_set_callerid(p->owner, p->cid_num, p->cid_name, p->cid_num);
+ p->cid_num[0]=0;
+ p->cid_name[0]=0;
+ }
+
+ vpb_timer_stop(p->ring_timer);
+ vpb_timer_start(p->ring_timer);
+ }
+ break;
+
+ case VPB_RING_OFF:
+ break;
+
+ case VPB_STATION_OFFHOOK:
+ if (p->mode == MODE_IMMEDIATE)
+ vpb_new(p,AST_STATE_RING, p->context);
+ else {
+ ast_verb(4, "%s: handle_notowned: playing dialtone\n",p->dev);
+ playtone(p->handle, &Dialtone);
+ p->state=VPB_STATE_PLAYDIAL;
+ p->wantdtmf = 1;
+ p->ext[0] = 0; /* Just to be sure & paranoid.*/
+ }
+ break;
+
+ case VPB_DIALEND:
+ if (p->mode == MODE_DIALTONE){
+ if (p->state == VPB_STATE_PLAYDIAL) {
+ playtone(p->handle, &Dialtone);
+ p->wantdtmf = 1;
+ p->ext[0] = 0; /* Just to be sure & paranoid. */
+ }
+ /* These are not needed as they have timers to restart them
+ else if (p->state == VPB_STATE_PLAYBUSY) {
+ playtone(p->handle, &Busytone);
+ p->wantdtmf = 1;
+ p->ext[0] = 0;
+ }
+ else if (p->state == VPB_STATE_PLAYRING) {
+ playtone(p->handle, &Ringbacktone);
+ p->wantdtmf = 1;
+ p->ext[0] = 0;
+ }
+ */
+ } else {
+ ast_verb(4, "%s: handle_notowned: Got a DIALEND when not really expected\n",p->dev);
+ }
+ break;
+
+ case VPB_STATION_ONHOOK: /* clear ext */
+ stoptone(p->handle);
+ p->wantdtmf = 1 ;
+ p->ext[0] = 0;
+ p->state=VPB_STATE_ONHOOK;
+ break;
+ case VPB_TIMEREXP:
+ if (e->data == p->dtmfidd_timer_id) {
+ if (ast_exists_extension(NULL, p->context, p->ext, 1, p->callerid)){
+ ast_verb(4, "%s: handle_notowned: DTMF IDD timer out, matching on [%s] in [%s]\n", p->dev,p->ext , p->context);
+
+ vpb_new(p,AST_STATE_RING, p->context);
+ }
+ } else if (e->data == p->ring_timer_id) {
+ /* We didnt get another ring in time! */
+ if (p->owner){
+ if (p->owner->_state != AST_STATE_UP) {
+ /* Assume caller has hung up */
+ vpb_timer_stop(p->ring_timer);
+ }
+ } else {
+ /* No owner any more, Assume caller has hung up */
+ vpb_timer_stop(p->ring_timer);
+ }
+ }
+ break;
+
+ case VPB_DTMF:
+ if (p->state == VPB_STATE_ONHOOK){
+ /* DTMF's being passed while on-hook maybe Caller ID */
+ if ( p->mode == MODE_FXO ) {
+ if ( e->data == DTMF_CID_START ) { /* CallerID Start signal */
+ p->dtmf_caller_pos = 0; /* Leaves the first digit out */
+ memset(p->callerid,0,AST_MAX_EXTENSION);
+ }
+ else if ( e->data == DTMF_CID_STOP ) { /* CallerID End signal */
+ p->callerid[p->dtmf_caller_pos] = '\0';
+ ast_verb(3, " %s: DTMF CallerID %s\n",p->dev,p->callerid);
+ if (owner){
+ /*
+ if (owner->cid.cid_num)
+ ast_free(owner->cid.cid_num);
+ owner->cid.cid_num=NULL;
+ if (owner->cid.cid_name)
+ ast_free(owner->cid.cid_name);
+ owner->cid.cid_name=NULL;
+ owner->cid.cid_num = strdup(p->callerid);
+ */
+ cid_name[0] = '\0';
+ cid_num[0] = '\0';
+ ast_callerid_split(p->callerid, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
+ ast_set_callerid(owner, cid_num, cid_name, cid_num);
+
+ } else
+ ast_verb(3, " %s: DTMF CallerID: no owner to assign CID \n",p->dev);
+ } else if ( p->dtmf_caller_pos < AST_MAX_EXTENSION ) {
+ if ( p->dtmf_caller_pos >= 0 )
+ p->callerid[p->dtmf_caller_pos] = e->data;
+ p->dtmf_caller_pos++;
+ }
+ }
+ break;
+ }
+ if (p->wantdtmf == 1) {
+ stoptone(p->handle);
+ p->wantdtmf = 0;
+ }
+ p->state=VPB_STATE_GETDTMF;
+ s[0] = e->data;
+ strncat(p->ext, s, sizeof(p->ext) - strlen(p->ext) - 1);
+ #if 0
+ if (!strcmp(p->ext,ast_pickup_ext())) {
+ /* Call pickup has been dialled! */
+ if (ast_pickup_call(c)) {
+ /* Call pickup wasnt possible */
+ }
+ }
+ else
+ #endif
+ if (ast_exists_extension(NULL, p->context, p->ext, 1, p->callerid)){
+ if ( ast_canmatch_extension(NULL, p->context, p->ext, 1, p->callerid)){
+ ast_verb(4, "%s: handle_notowned: Multiple matches on [%s] in [%s]\n", p->dev,p->ext , p->context);
+ /* Start DTMF IDD timer */
+ vpb_timer_stop(p->dtmfidd_timer);
+ vpb_timer_start(p->dtmfidd_timer);
+ }
+ else {
+ ast_verb(4, "%s: handle_notowned: Matched on [%s] in [%s]\n", p->dev,p->ext , p->context);
+ vpb_new(p,AST_STATE_UP, p->context);
+ }
+ } else if (!ast_canmatch_extension(NULL, p->context, p->ext, 1, p->callerid)){
+ if (ast_exists_extension(NULL, "default", p->ext, 1, p->callerid)) {
+ vpb_new(p,AST_STATE_UP, "default");
+ } else if (!ast_canmatch_extension(NULL, "default", p->ext, 1, p->callerid)) {
+ ast_verb(4, "%s: handle_notowned: can't match anything in %s or default\n", p->dev, p->context);
+ playtone(p->handle, &Busytone);
+ vpb_timer_stop(p->busy_timer);
+ vpb_timer_start(p->busy_timer);
+ p->state = VPB_STATE_PLAYBUSY;
+ }
+ }
+ break;
+
+ default:
+ /* Ignore.*/
+ break;
+ }
+
+ ast_verb(4, "%s: handle_notowned: mode=%d, [%d=>%d]\n", p->dev, p->mode, e->type, e->data);
+
+ return 0;
+}
+
+static void *do_monitor(void *unused)
+{
+
+ /* Monitor thread, doesn't die until explicitly killed. */
+
+ ast_verb(2, "Starting vpb monitor thread[%ld]\n",
+ pthread_self());
+
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ for(;;) {
+ VPB_EVENT e;
+ VPB_EVENT je;
+ char str[VPB_MAX_STR];
+ struct vpb_pvt *p;
+
+ /*
+ ast_verb(4, "Monitor waiting for event\n");
+ */
+
+ int res = vpb_get_event_sync(&e, VPB_WAIT_TIMEOUT);
+ if( (res==VPB_NO_EVENTS) || (res==VPB_TIME_OUT) ){
+ /*
+ if (res == VPB_NO_EVENTS){
+ ast_verb(4, "No events....\n");
+ } else {
+ ast_verb(4, "No events, timed out....\n");
+ }
+ */
+ continue;
+ }
+
+ if (res != VPB_OK) {
+ ast_log(LOG_ERROR,"Monitor get event error %s\n", vpb_strerror(res) );
+ ast_verbose("Monitor get event error %s\n", vpb_strerror(res) );
+ continue;
+ }
+
+ str[0] = 0;
+
+ p = NULL;
+
+ ast_mutex_lock(&monlock); {
+ //XXX useless braces, remove them and fix indenting
+ if (e.type == VPB_NULL_EVENT)
+ ast_verb(4, "Monitor got null event\n");
+ else {
+ vpb_translate_event(&e, str);
+ if (strlen(str)>1){
+ str[(strlen(str)-1)]='\0';
+ }
+
+ ast_mutex_lock(&iflock); {
+ p = iflist;
+ while (p && p->handle != e.handle)
+ p = p->next;
+ } ast_mutex_unlock(&iflock);
+
+ if (p)
+ ast_verb(4, "%s: Event [%d=>%s] \n",
+ p ? p->dev : "null", e.type, str );
+ }
+
+ } ast_mutex_unlock(&monlock);
+
+ if (!p) {
+ if (e.type != VPB_NULL_EVENT){
+ ast_log(LOG_WARNING, "Got event [%s][%d], no matching iface!\n", str,e.type);
+ ast_verb(4, "vpb/ERR: No interface for Event [%d=>%s] \n",e.type,str );
+ }
+ continue;
+ }
+
+ /* flush the event from the channel event Q */
+ vpb_get_event_ch_async(e.handle,&je);
+ vpb_translate_event(&je, str);
+ ast_verb(5, "%s: Flushing event [%d]=>%s\n",p->dev,je.type,str);
+
+ /* Check for ownership and locks */
+ if ((p->owner)&&(!p->golock)){
+ /* Need to get owner lock */
+ /* Safely grab both p->lock and p->owner->lock so that there
+ cannot be a race with something from the other side */
+ /*
+ ast_mutex_lock(&p->lock);
+ while(ast_mutex_trylock(&p->owner->lock)) {
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ if (!p->owner)
+ break;
+ }
+ if (p->owner)
+ p->golock=1;
+ */
+ }
+ /* Two scenarios: Are you owned or not. */
+ if (p->owner) {
+ monitor_handle_owned(p, &e);
+ } else {
+ monitor_handle_notowned(p, &e);
+ }
+ /* if ((!p->owner)&&(p->golock)){
+ ast_mutex_unlock(&p->owner->lock);
+ ast_mutex_unlock(&p->lock);
+ }
+ */
+
+ }
+
+ return NULL;
+}
+
+static int restart_monitor(void)
+{
+ int error = 0;
+
+ /* If we're supposed to be stopped -- stay stopped */
+ if (mthreadactive == -2)
+ return 0;
+
+ ast_verb(4, "Restarting monitor\n");
+
+ ast_mutex_lock(&monlock); {
+ if (monitor_thread == pthread_self()) {
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ error = -1;
+ ast_verb(4, "Monitor trying to kill monitor\n");
+ }
+ else {
+ if (mthreadactive != -1) {
+ /* Why do other drivers kill the thread? No need says I, simply awake thread with event. */
+ VPB_EVENT e;
+ e.handle = 0;
+ e.type = VPB_NULL_EVENT;
+ e.data = 0;
+
+ ast_verb(4, "Trying to reawake monitor\n");
+
+ vpb_put_event(&e);
+ } else {
+ /* Start a new monitor */
+ int pid = ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL);
+ ast_verb(4, "Created new monitor thread %d\n",pid);
+ if (pid < 0) {
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ error = -1;
+ } else
+ mthreadactive = 0; /* Started the thread!*/
+ }
+ }
+ } ast_mutex_unlock(&monlock);
+
+ ast_verb(4, "Monitor restarted\n");
+
+ return error;
+}
+
+/* Per board config that must be called after vpb_open() */
+static void mkbrd(vpb_model_t model, int echo_cancel)
+{
+ if(!bridges) {
+ if(model==vpb_model_v4pci)
+ max_bridges = MAX_BRIDGES_V4PCI;
+ bridges = ast_calloc(1, max_bridges * sizeof(vpb_bridge_t));
+ if(!bridges)
+ ast_log(LOG_ERROR, "Failed to initialize bridges\n");
+ else {
+ for(int i = 0; i < max_bridges; i++ ) {
+ ast_mutex_init(&bridges[i].lock);
+ ast_cond_init(&bridges[i].cond, NULL);
+ }
+ }
+ }
+ if(!echo_cancel) {
+ if (model==vpb_model_v4pci) {
+ vpb_echo_canc_disable();
+ ast_log(LOG_NOTICE, "Voicetronix echo cancellation OFF\n");
+ }
+ else {
+ /* need to it port by port for OpenSwitch*/
+ }
+ } else {
+ if (model==vpb_model_v4pci) {
+ vpb_echo_canc_enable();
+ ast_log(LOG_NOTICE, "Voicetronix echo cancellation ON\n");
+ if (ec_supp_threshold > -1){
+ #ifdef VPB_PRI
+ vpb_echo_canc_set_sup_thresh(0,(short *)&ec_supp_threshold);
+ #else
+ vpb_echo_canc_set_sup_thresh((short *)&ec_supp_threshold);
+ #endif
+ ast_log(LOG_NOTICE, "Voicetronix EC Sup Thres set\n");
+ }
+ }
+ else {
+ /* need to it port by port for OpenSwitch*/
+ }
+ }
+}
+
+static struct vpb_pvt *mkif(int board, int channel, int mode, int gains, float txgain, float rxgain,
+ float txswgain, float rxswgain, int bal1, int bal2, int bal3,
+ char * callerid, int echo_cancel, int group, ast_group_t callgroup, ast_group_t pickupgroup )
+{
+ struct vpb_pvt *tmp;
+ char buf[64];
+
+ tmp = ast_calloc(1, sizeof(*tmp));
+
+ if (!tmp)
+ return NULL;
+
+ tmp->handle = vpb_open(board, channel);
+
+ if (tmp->handle < 0) {
+ ast_log(LOG_WARNING, "Unable to create channel vpb/%d-%d: %s\n",
+ board, channel, strerror(errno));
+ ast_free(tmp);
+ return NULL;
+ }
+
+ snprintf(tmp->dev, sizeof(tmp->dev), "vpb/%d-%d", board, channel);
+
+ tmp->mode = mode;
+
+ tmp->group = group;
+ tmp->callgroup = callgroup;
+ tmp->pickupgroup = pickupgroup;
+
+ /* Initilize dtmf caller ID position variable */
+ tmp->dtmf_caller_pos=0;
+
+ strncpy(tmp->language, language, sizeof(tmp->language) - 1);
+ strncpy(tmp->context, context, sizeof(tmp->context) - 1);
+
+ tmp->callerid_type=0;
+ if(callerid) {
+ if (strcasecmp(callerid,"on")==0){
+ tmp->callerid_type =1;
+ strncpy(tmp->callerid, "unknown", sizeof(tmp->callerid) - 1);
+ }
+ else if (strcasecmp(callerid,"v23")==0){
+ tmp->callerid_type =2;
+ strncpy(tmp->callerid, "unknown", sizeof(tmp->callerid) - 1);
+ }
+ else if (strcasecmp(callerid,"bell")==0){
+ tmp->callerid_type =3;
+ strncpy(tmp->callerid, "unknown", sizeof(tmp->callerid) - 1);
+ }
+ else {
+ strncpy(tmp->callerid, callerid, sizeof(tmp->callerid) - 1);
+ }
+ } else {
+ strncpy(tmp->callerid, "unknown", sizeof(tmp->callerid) - 1);
+ }
+
+ /* check if codec balances have been set in the config file */
+ if (bal3>=0) {
+ if ((bal1>=0) && !(bal1 & 32)) bal1 |= 32;
+ vpb_set_codec_reg(tmp->handle, 0x42, bal3);
+ }
+ if(bal1>=0) vpb_set_codec_reg(tmp->handle, 0x32, bal1);
+ if(bal2>=0) vpb_set_codec_reg(tmp->handle, 0x3a, bal2);
+
+ if (gains & VPB_GOT_TXHWG){
+ if (txgain > MAX_VPB_GAIN){
+ tmp->txgain = MAX_VPB_GAIN;
+ }
+ else if (txgain < MIN_VPB_GAIN){
+ tmp->txgain = MIN_VPB_GAIN;
+ }
+ else {
+ tmp->txgain = txgain;
+ }
+
+ ast_log(LOG_NOTICE,"VPB setting Tx Hw gain to [%f]\n",tmp->txgain);
+ vpb_play_set_hw_gain(tmp->handle, tmp->txgain);
+ }
+
+ if (gains & VPB_GOT_RXHWG){
+ if (rxgain > MAX_VPB_GAIN){
+ tmp->rxgain = MAX_VPB_GAIN;
+ }
+ else if (rxgain < MIN_VPB_GAIN){
+ tmp->rxgain = MIN_VPB_GAIN;
+ }
+ else {
+ tmp->rxgain = rxgain;
+ }
+ ast_log(LOG_NOTICE,"VPB setting Rx Hw gain to [%f]\n",tmp->rxgain);
+ vpb_record_set_hw_gain(tmp->handle,tmp->rxgain);
+ }
+
+ if (gains & VPB_GOT_TXSWG){
+ tmp->txswgain = txswgain;
+ ast_log(LOG_NOTICE,"VPB setting Tx Sw gain to [%f]\n",tmp->txswgain);
+ vpb_play_set_gain(tmp->handle, tmp->txswgain);
+ }
+
+ if (gains & VPB_GOT_RXSWG){
+ tmp->rxswgain = rxswgain;
+ ast_log(LOG_NOTICE,"VPB setting Rx Sw gain to [%f]\n",tmp->rxswgain);
+ vpb_record_set_gain(tmp->handle, tmp->rxswgain);
+ }
+
+ tmp->vpb_model = vpb_model_unknown;
+ if( vpb_get_model(buf) == VPB_OK ) {
+ if(strcmp(buf,"V12PCI")==0)
+ tmp->vpb_model = vpb_model_v12pci;
+ else if(strcmp(buf,"VPB4")==0)
+ tmp->vpb_model = vpb_model_v4pci;
+ }
+
+ ast_mutex_init(&tmp->owner_lock);
+ ast_mutex_init(&tmp->lock);
+ ast_mutex_init(&tmp->record_lock);
+ ast_mutex_init(&tmp->play_lock);
+ ast_mutex_init(&tmp->play_dtmf_lock);
+
+ /* set default read state */
+ tmp->read_state = 0;
+
+ tmp->golock=0;
+
+ tmp->busy_timer_id = vpb_timer_get_unique_timer_id();
+ vpb_timer_open(&tmp->busy_timer, tmp->handle, tmp->busy_timer_id, TIMER_PERIOD_BUSY);
+
+ tmp->ringback_timer_id = vpb_timer_get_unique_timer_id();
+ vpb_timer_open(&tmp->ringback_timer, tmp->handle, tmp->ringback_timer_id, TIMER_PERIOD_RINGBACK);
+
+ tmp->ring_timer_id = vpb_timer_get_unique_timer_id();
+ vpb_timer_open(&tmp->ring_timer, tmp->handle, tmp->ring_timer_id, timer_period_ring);
+
+ tmp->dtmfidd_timer_id = vpb_timer_get_unique_timer_id();
+ vpb_timer_open(&tmp->dtmfidd_timer, tmp->handle, tmp->dtmfidd_timer_id, dtmf_idd);
+
+ if (mode == MODE_FXO){
+ if (use_ast_dtmfdet)
+ vpb_set_event_mask(tmp->handle, VPB_EVENTS_NODTMF );
+ else
+ vpb_set_event_mask(tmp->handle, VPB_EVENTS_ALL );
+ }
+ else {
+/*
+ if (use_ast_dtmfdet)
+ vpb_set_event_mask(tmp->handle, VPB_EVENTS_NODTMF );
+ else
+*/
+ vpb_set_event_mask(tmp->handle, VPB_EVENTS_STAT );
+ }
+
+ if ((tmp->vpb_model == vpb_model_v12pci) && (echo_cancel)){
+ vpb_hostecho_on(tmp->handle);
+ }
+ if (use_ast_dtmfdet) {
+ tmp->vad = ast_dsp_new();
+ ast_dsp_set_features(tmp->vad, DSP_FEATURE_DTMF_DETECT);
+ ast_dsp_digitmode(tmp->vad, DSP_DIGITMODE_DTMF);
+ if (relaxdtmf)
+ ast_dsp_digitmode(tmp->vad, DSP_DIGITMODE_DTMF|DSP_DIGITMODE_RELAXDTMF);
+ }
+ else {
+ tmp->vad = NULL;
+ }
+
+ /* define grunt tone */
+ vpb_settonedet(tmp->handle,&toned_ungrunt);
+
+ ast_log(LOG_NOTICE,"Voicetronix %s channel %s initialized (rxsg=%f/txsg=%f/rxhg=%f/txhg=%f)(0x%x/0x%x/0x%x)\n",
+ (tmp->vpb_model==vpb_model_v4pci)?"V4PCI": (tmp->vpb_model==vpb_model_v12pci)?"V12PCI":"[Unknown model]",
+ tmp->dev, tmp->rxswgain, tmp->txswgain, tmp->rxgain, tmp->txgain, bal1, bal2, bal3 );
+
+ return tmp;
+}
+
+static int vpb_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
+ int res = 0;
+ int tmp = 0;
+
+ if (use_ast_ind == 1) {
+ ast_verb(4, "%s: vpb_indicate called when using Ast Indications !?!\n", p->dev);
+ return 0;
+ }
+
+ ast_verb(4, "%s: vpb_indicate [%d] state[%d]\n", p->dev, condition,ast->_state);
+/*
+ if (ast->_state != AST_STATE_UP) {
+ ast_verb(4, "%s: vpb_indicate Not in AST_STATE_UP\n", p->dev, condition,ast->_state);
+ return res;
+ }
+*/
+
+/*
+ ast_verb(4, "%s: LOCKING in indicate \n", p->dev);
+ ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ ast_mutex_lock(&p->lock);
+ switch(condition) {
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ if (ast->_state == AST_STATE_UP) {
+ playtone(p->handle, &Busytone);
+ p->state = VPB_STATE_PLAYBUSY;
+ vpb_timer_stop(p->busy_timer);
+ vpb_timer_start(p->busy_timer);
+ }
+ break;
+ case AST_CONTROL_RINGING:
+ if (ast->_state == AST_STATE_UP) {
+ playtone(p->handle, &Ringbacktone);
+ p->state = VPB_STATE_PLAYRING;
+ ast_verb(4, "%s: vpb indicate: setting ringback timer [%d]\n", p->dev,p->ringback_timer_id);
+
+ vpb_timer_stop(p->ringback_timer);
+ vpb_timer_start(p->ringback_timer);
+ }
+ break;
+ case AST_CONTROL_ANSWER:
+ case -1: /* -1 means stop playing? */
+ vpb_timer_stop(p->ringback_timer);
+ vpb_timer_stop(p->busy_timer);
+ stoptone(p->handle);
+ break;
+ case AST_CONTROL_HANGUP:
+ if (ast->_state == AST_STATE_UP) {
+ playtone(p->handle, &Busytone);
+ p->state = VPB_STATE_PLAYBUSY;
+ vpb_timer_stop(p->busy_timer);
+ vpb_timer_start(p->busy_timer);
+ }
+ break;
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, (const char *) data, NULL);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ default:
+ res = 0;
+ break;
+ }
+ tmp = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in indicate [%d]\n", p->dev,tmp);
+*/
+ return res;
+}
+
+static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)newchan->tech_pvt;
+ int res = 0;
+
+/*
+ ast_verb(4, "%s: LOCKING in fixup \n", p->dev);
+ ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ ast_mutex_lock(&p->lock);
+ ast_debug(1, "New owner for channel %s is %s\n", p->dev, newchan->name);
+
+ if (p->owner == oldchan) {
+ p->owner = newchan;
+ }
+
+ if (newchan->_state == AST_STATE_RINGING){
+ if (use_ast_ind == 1) {
+ ast_verb(4, "%s: vpb_fixup Calling ast_indicate\n", p->dev);
+ ast_indicate(newchan, AST_CONTROL_RINGING);
+ }
+ else {
+ ast_verb(4, "%s: vpb_fixup Calling vpb_indicate\n", p->dev);
+ vpb_indicate(newchan, AST_CONTROL_RINGING, NULL, 0);
+ }
+ }
+
+ res= ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in fixup [%d]\n", p->dev,res);
+*/
+ return 0;
+}
+
+static int vpb_digit_begin(struct ast_channel *ast, char digit)
+{
+ /* XXX Modify this callback to let Asterisk control the length of DTMF */
+ return 0;
+}
+static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
+ char s[2];
+ int res = 0;
+
+ if (use_ast_dtmf){
+ ast_verb(4, "%s: vpb_digit: asked to play digit[%c] but we are using asterisk dtmf play back?!\n", p->dev, digit);
+ return 0;
+ }
+
+/*
+ ast_verb(4, "%s: LOCKING in digit \n", p->dev);
+ ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ ast_mutex_lock(&p->lock);
+
+
+ s[0] = digit;
+ s[1] = '\0';
+
+ ast_verb(4, "%s: vpb_digit: asked to play digit[%s]\n", p->dev, s);
+
+ ast_mutex_lock(&p->play_dtmf_lock);
+ strncat(p->play_dtmf,s,sizeof(*p->play_dtmf));
+ ast_mutex_unlock(&p->play_dtmf_lock);
+
+ res = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in digit [%d]\n", p->dev,res);
+*/
+ return 0;
+}
+
+/* Places a call out of a VPB channel */
+static int vpb_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
+ int res = 0,i;
+ char *s = strrchr(dest, '/');
+ char dialstring[254] = "";
+ int tmp = 0;
+
+/*
+ ast_verb(4, "%s: LOCKING in call \n", p->dev);
+ ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ ast_mutex_lock(&p->lock);
+ ast_verb(4, "%s: starting call to [%s]\n", p->dev,dest);
+
+ if (s)
+ s = s + 1;
+ else
+ s = dest;
+ strncpy(dialstring, s, sizeof(dialstring) - 1);
+ for (i=0; dialstring[i] != '\0' ; i++) {
+ if ((dialstring[i] == 'w') || (dialstring[i] == 'W'))
+ dialstring[i] = ',';
+ else if ((dialstring[i] == 'f') || (dialstring[i] == 'F'))
+ dialstring[i] = '&';
+ }
+
+ if (ast->_state != AST_STATE_DOWN && ast->_state != AST_STATE_RESERVED) {
+ ast_log(LOG_WARNING, "vpb_call on %s neither down nor reserved!\n", ast->name);
+ tmp = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in call [%d]\n", p->dev,tmp);
+*/
+ return -1;
+ }
+ if (p->mode != MODE_FXO) /* Station port, ring it. */
+ res = vpb_ring_station_async(p->handle, VPB_RING_STATION_ON,0);
+ else {
+ VPB_CALL call;
+
+ /* Dial must timeout or it can leave channels unuseable */
+ if( timeout == 0 )
+ timeout = TIMER_PERIOD_NOANSWER;
+ else
+ timeout = timeout * 1000; /* convert from secs to ms. */
+
+ /* These timeouts are only used with call progress dialing */
+ call.dialtones = 1; /* Number of dialtones to get outside line */
+ call.dialtone_timeout = VPB_DIALTONE_WAIT; /* Wait this long for dialtone (ms) */
+ call.ringback_timeout = VPB_RINGWAIT; /* Wait this long for ringing after dialing (ms) */
+ call.inter_ringback_timeout = VPB_CONNECTED_WAIT; /* If ringing stops for this long consider it connected (ms) */
+ call.answer_timeout = timeout; /* Time to wait for answer after ringing starts (ms) */
+ memcpy( &call.tone_map, DialToneMap, sizeof(DialToneMap) );
+ vpb_set_call(p->handle, &call);
+
+ ast_verb(2, "%s: Calling %s on %s \n",p->dev, dialstring, ast->name);
+
+ int j;
+ ast_verb(2, "%s: Dial parms for %s %d/%dms/%dms/%dms/%dms\n", p->dev
+ , ast->name, call.dialtones, call.dialtone_timeout
+ , call.ringback_timeout, call.inter_ringback_timeout
+ , call.answer_timeout );
+ for( j=0; !call.tone_map[j].terminate; j++ )
+ ast_verb(2, "%s: Dial parms for %s tone %d->%d\n", p->dev,
+ ast->name, call.tone_map[j].tone_id, call.tone_map[j].call_id);
+
+ ast_verb(4, "%s: Disabling Loop Drop detection\n",p->dev);
+ vpb_disable_event(p->handle, VPB_MDROP);
+ vpb_sethook_sync(p->handle,VPB_OFFHOOK);
+ p->state=VPB_STATE_OFFHOOK;
+
+ #ifndef DIAL_WITH_CALL_PROGRESS
+ vpb_sleep(300);
+ ast_verb(4, "%s: Enabling Loop Drop detection\n",p->dev);
+ vpb_enable_event(p->handle, VPB_MDROP);
+ res = vpb_dial_async(p->handle, dialstring);
+ #else
+ ast_verb(4, "%s: Enabling Loop Drop detection\n",p->dev);
+ vpb_enable_event(p->handle, VPB_MDROP);
+ res = vpb_call_async(p->handle, dialstring);
+ #endif
+
+ if (res != VPB_OK) {
+ ast_debug(1, "Call on %s to %s failed: %s\n", ast->name, s, vpb_strerror(res));
+ res = -1;
+ } else
+ res = 0;
+ }
+
+ ast_verb(3, "%s: VPB Calling %s [t=%d] on %s returned %d\n",p->dev , s, timeout, ast->name, res);
+ if (res == 0) {
+ ast_setstate(ast, AST_STATE_RINGING);
+ ast_queue_control(ast,AST_CONTROL_RINGING);
+ }
+
+ if (!p->readthread){
+ ast_pthread_create(&p->readthread, NULL, do_chanreads, (void *)p);
+ }
+
+ tmp = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in call [%d]\n", p->dev,tmp);
+*/
+ return res;
+}
+
+static int vpb_hangup(struct ast_channel *ast)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
+ VPB_EVENT je;
+ char str[VPB_MAX_STR];
+ int res =0 ;
+
+/*
+ ast_verb(4, "%s: LOCKING in hangup \n", p->dev);
+ ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+ ast_verb(4, "%s: LOCKING pthread_self(%d)\n", p->dev,pthread_self());
+ ast_mutex_lock(&p->lock);
+*/
+ ast_verb(2, "%s: Hangup requested\n", ast->name);
+
+ if (!ast->tech || !ast->tech_pvt) {
+ ast_log(LOG_WARNING, "%s: channel not connected?\n", ast->name);
+ res = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in hangup [%d]\n", p->dev,res);
+*/
+ /* Free up ast dsp if we have one */
+ if ((use_ast_dtmfdet)&&(p->vad)) {
+ ast_dsp_free(p->vad);
+ p->vad = NULL;
+ }
+ return 0;
+ }
+
+
+
+ /* Stop record */
+ p->stopreads = 1;
+ if( p->readthread ){
+ pthread_join(p->readthread, NULL);
+ ast_verb(4, "%s: stopped record thread \n",ast->name);
+ }
+
+ /* Stop play */
+ if (p->lastoutput != -1) {
+ ast_verb(2, "%s: Ending play mode \n",ast->name);
+ vpb_play_terminate(p->handle);
+ ast_mutex_lock(&p->play_lock); {
+ vpb_play_buf_finish(p->handle);
+ } ast_mutex_unlock(&p->play_lock);
+ }
+
+ ast_verb(4, "%s: Setting state down\n",ast->name);
+ ast_setstate(ast,AST_STATE_DOWN);
+
+
+/*
+ ast_verb(4, "%s: LOCKING in hangup \n", p->dev);
+ ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+ ast_verb(4, "%s: LOCKING pthread_self(%d)\n", p->dev,pthread_self());
+*/
+ ast_mutex_lock(&p->lock);
+
+ if (p->mode != MODE_FXO) {
+ /* station port. */
+ vpb_ring_station_async(p->handle, VPB_RING_STATION_OFF,0);
+ if(p->state!=VPB_STATE_ONHOOK){
+ /* This is causing a "dial end" "play tone" loop
+ playtone(p->handle, &Busytone);
+ p->state = VPB_STATE_PLAYBUSY;
+ ast_verb(5, "%s: Station offhook[%d], playing busy tone\n",
+ ast->name,p->state);
+ */
+ }
+ else {
+ stoptone(p->handle);
+ }
+ #ifdef VPB_PRI
+ vpb_setloop_async(p->handle, VPB_OFFHOOK);
+ vpb_sleep(100);
+ vpb_setloop_async(p->handle, VPB_ONHOOK);
+ #endif
+ } else {
+ stoptone(p->handle); /* Terminates any dialing */
+ vpb_sethook_sync(p->handle, VPB_ONHOOK);
+ p->state=VPB_STATE_ONHOOK;
+ }
+ while (VPB_OK==vpb_get_event_ch_async(p->handle,&je)){
+ vpb_translate_event(&je, str);
+ ast_verb(4, "%s: Flushing event [%d]=>%s\n",ast->name,je.type,str);
+ }
+
+ p->readthread = 0;
+ p->lastoutput = -1;
+ p->lastinput = -1;
+ p->last_ignore_dtmf = 1;
+ p->ext[0] = 0;
+ p->dialtone = 0;
+
+ p->owner = NULL;
+ ast->tech_pvt=NULL;
+
+ /* Free up ast dsp if we have one */
+ if ((use_ast_dtmfdet)&&(p->vad)) {
+ ast_dsp_free(p->vad);
+ p->vad = NULL;
+ }
+
+ ast_verb(2, "%s: Hangup complete\n", ast->name);
+
+ restart_monitor();
+/*
+ ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ res = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in hangup [%d]\n", p->dev,res);
+ ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ return 0;
+}
+
+static int vpb_answer(struct ast_channel *ast)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
+ int res = 0;
+/*
+ VPB_EVENT je;
+ int ret;
+ ast_verb(4, "%s: LOCKING in answer \n", p->dev);
+ ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner);
+*/
+ ast_mutex_lock(&p->lock);
+
+ ast_verb(4, "%s: Answering channel\n",p->dev);
+
+ if (p->mode == MODE_FXO){
+ ast_verb(4, "%s: Disabling Loop Drop detection\n",p->dev);
+ vpb_disable_event(p->handle, VPB_MDROP);
+ }
+
+ if (ast->_state != AST_STATE_UP) {
+ if (p->mode == MODE_FXO){
+ vpb_sethook_sync(p->handle, VPB_OFFHOOK);
+ p->state=VPB_STATE_OFFHOOK;
+/* vpb_sleep(500);
+ ret = vpb_get_event_ch_async(p->handle,&je);
+ if ((ret == VPB_OK)&&((je.type != VPB_DROP)&&(je.type != VPB_RING))){
+ ast_verb(4, "%s: Answer collected a wrong event!!\n",p->dev);
+ vpb_put_event(&je);
+ }
+*/
+ }
+ ast_setstate(ast, AST_STATE_UP);
+
+ ast_verb(2, "%s: Answered call on %s [%s]\n", p->dev,
+ ast->name,(p->mode == MODE_FXO)?"FXO":"FXS");
+
+ ast->rings = 0;
+ if( !p->readthread ){
+ /* res = ast_mutex_unlock(&p->lock); */
+ /* ast_verbose("%s: unLOCKING in answer [%d]\n", p->dev,res); */
+ ast_pthread_create(&p->readthread, NULL, do_chanreads, (void *)p);
+ } else {
+ ast_verb(4, "%s: Record thread already running!!\n",p->dev);
+ }
+ } else {
+ ast_verb(4, "%s: Answered state is up\n",p->dev);
+ /* res = ast_mutex_unlock(&p->lock); */
+ /* ast_verbose("%s: unLOCKING in answer [%d]\n", p->dev,res); */
+ }
+ vpb_sleep(500);
+ if (p->mode == MODE_FXO){
+ ast_verb(4, "%s: Re-enabling Loop Drop detection\n",p->dev);
+ vpb_enable_event(p->handle,VPB_MDROP);
+ }
+ res = ast_mutex_unlock(&p->lock);
+/*
+ ast_verb(4, "%s: unLOCKING in answer [%d]\n", p->dev,res);
+*/
+ return 0;
+}
+
+static struct ast_frame *vpb_read(struct ast_channel *ast)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
+ static struct ast_frame f = {AST_FRAME_NULL};
+
+ f.src = "vpb";
+ ast_log(LOG_NOTICE, "%s: vpb_read: should never be called!\n", p->dev);
+ ast_verbose("%s: vpb_read: should never be called!\n", p->dev);
+
+ return &f;
+}
+
+static inline int ast2vpbformat(int ast_format)
+{
+ switch(ast_format) {
+ case AST_FORMAT_ALAW:
+ return VPB_ALAW;
+ case AST_FORMAT_SLINEAR:
+ return VPB_LINEAR;
+ case AST_FORMAT_ULAW:
+ return VPB_MULAW;
+ case AST_FORMAT_ADPCM:
+ return VPB_OKIADPCM;
+ default:
+ return -1;
+ }
+}
+
+static inline char * ast2vpbformatname(int ast_format)
+{
+ switch(ast_format) {
+ case AST_FORMAT_ALAW:
+ return "AST_FORMAT_ALAW:VPB_ALAW";
+ case AST_FORMAT_SLINEAR:
+ return "AST_FORMAT_SLINEAR:VPB_LINEAR";
+ case AST_FORMAT_ULAW:
+ return "AST_FORMAT_ULAW:VPB_MULAW";
+ case AST_FORMAT_ADPCM:
+ return "AST_FORMAT_ADPCM:VPB_OKIADPCM";
+ default:
+ return "UNKN:UNKN";
+ }
+}
+
+static inline int astformatbits(int ast_format)
+{
+ switch(ast_format) {
+ case AST_FORMAT_ALAW:
+ case AST_FORMAT_ULAW:
+ return 8;
+ case AST_FORMAT_SLINEAR:
+ return 16;
+ case AST_FORMAT_ADPCM:
+ return 4;
+ default:
+ return 8;
+ }
+}
+
+int a_gain_vector(float g, short *v, int n)
+{
+ int i;
+ float tmp;
+ for ( i = 0; i<n; i++) {
+ tmp = g*v[i];
+ if (tmp > 32767.0)
+ tmp = 32767.0;
+ if (tmp < -32768.0)
+ tmp = -32768.0;
+ v[i] = (short)tmp;
+ }
+ return(i);
+}
+
+/* Writes a frame of voice data to a VPB channel */
+static int vpb_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
+ int res = 0, fmt = 0;
+ struct timeval play_buf_time_start;
+ int tdiff;
+
+/* ast_mutex_lock(&p->lock); */
+ ast_verb(6, "%s: vpb_write: Writing to channel\n", p->dev);
+
+ if (frame->frametype != AST_FRAME_VOICE) {
+ ast_verb(4, "%s: vpb_write: Don't know how to handle from type %d\n", ast->name, frame->frametype);
+/* ast_mutex_unlock(&p->lock); */
+ return 0;
+ } else if (ast->_state != AST_STATE_UP) {
+ ast_verb(4, "%s: vpb_write: Attempt to Write frame type[%d]subclass[%d] on not up chan(state[%d])\n",ast->name, frame->frametype, frame->subclass,ast->_state);
+ p->lastoutput = -1;
+/* ast_mutex_unlock(&p->lock); */
+ return 0;
+ }
+/* ast_debug(1, "%s: vpb_write: Checked frame type..\n", p->dev); */
+
+
+ fmt = ast2vpbformat(frame->subclass);
+ if (fmt < 0) {
+ ast_log(LOG_WARNING, "%s: vpb_write: Cannot handle frames of %d format!\n",ast->name, frame->subclass);
+ return -1;
+ }
+
+ tdiff = ast_tvdiff_ms(ast_tvnow(), p->lastplay);
+ ast_debug(1, "%s: vpb_write: time since last play(%d) \n", p->dev, tdiff);
+ if (tdiff < (VPB_SAMPLES/8 - 1)){
+ ast_debug(1, "%s: vpb_write: Asked to play too often (%d) (%d)\n", p->dev, tdiff,frame->datalen);
+// return 0;
+ }
+ p->lastplay = ast_tvnow();
+/*
+ ast_debug(1, "%s: vpb_write: Checked frame format..\n", p->dev);
+*/
+
+ ast_mutex_lock(&p->play_lock);
+
+/*
+ ast_debug(1, "%s: vpb_write: Got play lock..\n", p->dev);
+*/
+
+ /* Check if we have set up the play_buf */
+ if (p->lastoutput == -1) {
+ vpb_play_buf_start(p->handle, fmt);
+ ast_verb(2, "%s: vpb_write: Starting play mode (codec=%d)[%s]\n",p->dev,fmt,ast2vpbformatname(frame->subclass));
+ p->lastoutput = fmt;
+ ast_mutex_unlock(&p->play_lock);
+ return 0;
+ } else if (p->lastoutput != fmt) {
+ vpb_play_buf_finish(p->handle);
+ vpb_play_buf_start(p->handle, fmt);
+ ast_verb(2, "%s: vpb_write: Changed play format (%d=>%d)\n",p->dev,p->lastoutput,fmt);
+ ast_mutex_unlock(&p->play_lock);
+ return 0;
+ }
+ p->lastoutput = fmt;
+
+
+
+ /* Apply extra gain ! */
+ if( p->txswgain > MAX_VPB_GAIN )
+ a_gain_vector(p->txswgain - MAX_VPB_GAIN , (short*)frame->data, frame->datalen/sizeof(short));
+
+/* ast_debug(1, "%s: vpb_write: Applied gain..\n", p->dev); */
+/* ast_debug(1, "%s: vpb_write: play_buf_time %d\n", p->dev, p->play_buf_time); */
+
+ if ((p->read_state == 1)&&(p->play_buf_time<5)){
+ play_buf_time_start = ast_tvnow();
+/* res = vpb_play_buf_sync(p->handle, (char*)frame->data, tdiff*8*2); */
+ res = vpb_play_buf_sync(p->handle, (char*)frame->data, frame->datalen);
+ if(res == VPB_OK) {
+ short * data = (short*)frame->data;
+ ast_verb(6, "%s: vpb_write: Wrote chan (codec=%d) %d %d\n", p->dev, fmt, data[0],data[1]);
+ }
+ p->play_buf_time = ast_tvdiff_ms(ast_tvnow(), play_buf_time_start);
+ }
+ else {
+ p->chuck_count++;
+ ast_debug(1, "%s: vpb_write: Tossed data away, tooooo much data!![%d]\n", p->dev,p->chuck_count);
+ p->play_buf_time=0;
+ }
+
+ ast_mutex_unlock(&p->play_lock);
+/* ast_mutex_unlock(&p->lock); */
+ ast_verb(6, "%s: vpb_write: Done Writing to channel\n", p->dev);
+ return 0;
+}
+
+/* Read monitor thread function. */
+static void *do_chanreads(void *pvt)
+{
+ struct vpb_pvt *p = (struct vpb_pvt *)pvt;
+ struct ast_frame *fr = &p->fr;
+ char *readbuf = ((char *)p->buf) + AST_FRIENDLY_OFFSET;
+ int bridgerec = 0;
+ int afmt, readlen, res, fmt, trycnt=0;
+ int ignore_dtmf;
+ const char * getdtmf_var = NULL;
+
+ fr->frametype = AST_FRAME_VOICE;
+ fr->src = "vpb";
+ fr->mallocd = 0;
+ fr->delivery.tv_sec = 0;
+ fr->delivery.tv_usec = 0;
+ fr->samples = VPB_SAMPLES;
+ fr->offset = AST_FRIENDLY_OFFSET;
+ memset(p->buf, 0, sizeof p->buf);
+
+ ast_verb(3, "%s: chanreads: starting thread\n", p->dev);
+ ast_mutex_lock(&p->record_lock);
+
+ p->stopreads = 0;
+ p->read_state = 1;
+ while (!p->stopreads && p->owner) {
+
+ ast_verb(5, "%s: chanreads: Starting cycle ...\n", p->dev);
+ ast_verb(5, "%s: chanreads: Checking bridge \n", p->dev);
+ if (p->bridge) {
+ if (p->bridge->c0 == p->owner && (p->bridge->flags & AST_BRIDGE_REC_CHANNEL_0))
+ bridgerec = 1;
+ else if (p->bridge->c1 == p->owner && (p->bridge->flags & AST_BRIDGE_REC_CHANNEL_1))
+ bridgerec = 1;
+ else
+ bridgerec = 0;
+ } else {
+ ast_verb(5, "%s: chanreads: No native bridge.\n", p->dev);
+ if (p->owner->_bridge){
+ ast_verb(5, "%s: chanreads: Got Asterisk bridge with [%s].\n", p->dev,p->owner->_bridge->name);
+ bridgerec = 1;
+ }
+ else {
+ bridgerec = 0;
+ }
+ }
+
+/* if ( (p->owner->_state != AST_STATE_UP) || !bridgerec) */
+ if ( (p->owner->_state != AST_STATE_UP) )
+ {
+ if (p->owner->_state != AST_STATE_UP)
+ ast_verb(5, "%s: chanreads: Im not up[%d]\n", p->dev,p->owner->_state);
+ else
+ ast_verb(5, "%s: chanreads: No bridgerec[%d]\n", p->dev,bridgerec);
+ vpb_sleep(10);
+ continue;
+ }
+
+ /* Voicetronix DTMF detection can be triggered off ordinary speech
+ * This leads to annoying beeps during the conversation
+ * Avoid this problem by just setting VPB_GETDTMF when you want to listen for DTMF
+ */
+ /* ignore_dtmf = 1; */
+ ignore_dtmf = 0; /* set this to 1 to turn this feature on */
+ getdtmf_var = pbx_builtin_getvar_helper(p->owner,"VPB_GETDTMF");
+ if( getdtmf_var && ( strcasecmp( getdtmf_var, "yes" ) == 0 ) )
+ ignore_dtmf = 0;
+
+ if(( ignore_dtmf != p->last_ignore_dtmf ) &&(!use_ast_dtmfdet)){
+ ast_verb(2, "%s:Now %s DTMF \n",
+ p->dev, ignore_dtmf ? "ignoring" : "listening for");
+ vpb_set_event_mask(p->handle, ignore_dtmf ? VPB_EVENTS_NODTMF : VPB_EVENTS_ALL );
+ }
+ p->last_ignore_dtmf = ignore_dtmf;
+
+ /* Play DTMF digits here to avoid problem you get if playing a digit during
+ * a record operation
+ */
+ ast_verb(6, "%s: chanreads: Checking dtmf's \n", p->dev);
+ ast_mutex_lock(&p->play_dtmf_lock);
+ if( p->play_dtmf[0] ) {
+ /* Try to ignore DTMF event we get after playing digit */
+ /* This DTMF is played by asterisk and leads to an annoying trailing beep on CISCO phones */
+ if( !ignore_dtmf)
+ vpb_set_event_mask(p->handle, VPB_EVENTS_NODTMF );
+ if (p->bridge == NULL){
+ vpb_dial_sync(p->handle,p->play_dtmf);
+ ast_verb(2, "%s: chanreads: Played DTMF %s\n",p->dev,p->play_dtmf);
+ }
+ else {
+ ast_verb(2, "%s: chanreads: Not playing DTMF frame on native bridge\n", p->dev);
+ }
+ p->play_dtmf[0] = '\0';
+ ast_mutex_unlock(&p->play_dtmf_lock);
+ vpb_sleep(700); /* Long enough to miss echo and DTMF event */
+ if( !ignore_dtmf)
+ vpb_set_event_mask(p->handle, VPB_EVENTS_ALL );
+ continue;
+ }
+ ast_mutex_unlock(&p->play_dtmf_lock);
+
+/* afmt = (p->owner) ? p->owner->rawreadformat : AST_FORMAT_SLINEAR; */
+ if (p->owner){
+ afmt = p->owner->rawreadformat;
+/* ast_debug(1,"%s: Record using owner format [%s]\n", p->dev, ast2vpbformatname(afmt)); */
+ }
+ else {
+ afmt = AST_FORMAT_SLINEAR;
+/* ast_debug(1,"%s: Record using default format [%s]\n", p->dev, ast2vpbformatname(afmt)); */
+ }
+ fmt = ast2vpbformat(afmt);
+ if (fmt < 0) {
+ ast_log(LOG_WARNING,"%s: Record failure (unsupported format %d)\n", p->dev, afmt);
+ return NULL;
+ }
+ readlen = VPB_SAMPLES * astformatbits(afmt) / 8;
+
+ if (p->lastinput == -1) {
+ vpb_record_buf_start(p->handle, fmt);
+ vpb_reset_record_fifo_alarm(p->handle);
+ p->lastinput = fmt;
+ ast_verb(2, "%s: Starting record mode (codec=%d)[%s]\n",p->dev,fmt,ast2vpbformatname(afmt));
+ continue;
+ } else if (p->lastinput != fmt) {
+ vpb_record_buf_finish(p->handle);
+ vpb_record_buf_start(p->handle, fmt);
+ p->lastinput = fmt;
+ ast_verb(2, "%s: Changed record format (%d=>%d)\n",p->dev,p->lastinput,fmt);
+ continue;
+ }
+
+ /* Read only if up and not bridged, or a bridge for which we can read. */
+ ast_verb(6, "%s: chanreads: getting buffer!\n", p->dev);
+ if( (res = vpb_record_buf_sync(p->handle, readbuf, readlen) ) == VPB_OK ) {
+ ast_verb(6, "%s: chanreads: got buffer!\n", p->dev);
+ /* Apply extra gain ! */
+ if( p->rxswgain > MAX_VPB_GAIN )
+ a_gain_vector(p->rxswgain - MAX_VPB_GAIN , (short*)readbuf, readlen/sizeof(short));
+ ast_verb(6, "%s: chanreads: applied gain\n", p->dev);
+
+ fr->subclass = afmt;
+ fr->data = readbuf;
+ fr->datalen = readlen;
+ fr->frametype = AST_FRAME_VOICE;
+
+ if ((use_ast_dtmfdet)&&(p->vad)){
+ fr = ast_dsp_process(p->owner,p->vad,fr);
+ if (fr && (fr->frametype == AST_FRAME_DTMF))
+ ast_debug(1, "%s: chanreads: Detected DTMF '%c'\n",p->dev, fr->subclass);
+ if (fr->subclass == 'm'){
+ /* conf mute request */
+ fr->frametype = AST_FRAME_NULL;
+ fr->subclass = 0;
+ }
+ else if (fr->subclass == 'u'){
+ /* Unmute */
+ fr->frametype = AST_FRAME_NULL;
+ fr->subclass = 0;
+ }
+ else if (fr->subclass == 'f'){
+ }
+ }
+ /* Using trylock here to prevent deadlock when channel is hungup
+ * (ast_hangup() immediately gets lock)
+ */
+ if (p->owner && !p->stopreads ) {
+ ast_verb(6, "%s: chanreads: queueing buffer on read frame q (state[%d])\n", p->dev,p->owner->_state);
+ do {
+ res = ast_mutex_trylock(&p->owner->lock);
+ trycnt++;
+ } while((res !=0)&&(trycnt<300));
+ if (res==0) {
+ ast_queue_frame(p->owner, fr);
+ ast_mutex_unlock(&p->owner->lock);
+ } else {
+ ast_verb(5, "%s: chanreads: Couldnt get lock after %d tries!\n", p->dev,trycnt);
+ }
+ trycnt=0;
+
+/*
+ res = ast_mutex_trylock(&p->owner->lock);
+ if (res==0) {
+ ast_queue_frame(p->owner, fr);
+ ast_mutex_unlock(&p->owner->lock);
+ } else {
+ if (res == EINVAL )
+ ast_verb(5, "%s: chanreads: try owner->lock gave me EINVAL[%d]\n", p->dev,res);
+ else if (res == EBUSY )
+ ast_verb(5, "%s: chanreads: try owner->lock gave me EBUSY[%d]\n", p->dev,res);
+ while(res !=0){
+ res = ast_mutex_trylock(&p->owner->lock);
+ }
+ if (res==0) {
+ ast_queue_frame(p->owner, fr);
+ ast_mutex_unlock(&p->owner->lock);
+ }
+ else {
+ if (res == EINVAL )
+ ast_verb(5, "%s: chanreads: try owner->lock gave me EINVAL[%d]\n", p->dev,res);
+ else if (res == EBUSY )
+ ast_verb(5, "%s: chanreads: try owner->lock gave me EBUSY[%d]\n", p->dev,res);
+ ast_verb(5, "%s: chanreads: Couldnt get lock on owner[%s][%d][%d] channel to send frame!\n", p->dev,p->owner->name,(int)p->owner->lock.__m_owner,(int)p->owner->lock.__m_count);
+ }
+ }
+*/
+ short * data = (short*)readbuf;
+ ast_verb(7, "%s: Read channel (codec=%d) %d %d\n", p->dev, fmt, data[0], data[1] );
+ }
+ else {
+ ast_verb(5, "%s: p->stopreads[%d] p->owner[%p]\n", p->dev, p->stopreads,(void *)p->owner);
+ }
+ }
+ ast_verb(5, "%s: chanreads: Finished cycle...\n", p->dev);
+ }
+ p->read_state=0;
+
+ /* When stopreads seen, go away! */
+ vpb_record_buf_finish(p->handle);
+ p->read_state=0;
+ ast_mutex_unlock(&p->record_lock);
+
+ ast_verb(2, "%s: Ending record mode (%d/%s)\n",
+ p->dev, p->stopreads, p->owner? "yes" : "no");
+ return NULL;
+}
+
+static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state state, char *context)
+{
+ struct ast_channel *tmp;
+ char cid_num[256];
+ char cid_name[256];
+
+ if (me->owner) {
+ ast_log(LOG_WARNING, "Called vpb_new on owned channel (%s) ?!\n", me->dev);
+ return NULL;
+ }
+ ast_verb(4, "%s: New call for context [%s]\n",me->dev,context);
+
+ tmp = ast_channel_alloc(1, state, 0, 0, "", me->ext, me->context, 0, me->dev);
+ if (tmp) {
+ if (use_ast_ind == 1){
+ tmp->tech = &vpb_tech_indicate;
+ }
+ else {
+ tmp->tech = &vpb_tech;
+ }
+
+ tmp->callgroup = me->callgroup;
+ tmp->pickupgroup = me->pickupgroup;
+
+ /* Linear is the preferred format. Although Voicetronix supports other formats
+ * they are all converted to/from linear in the vpb code. Best for us to use
+ * linear since we can then adjust volume in this modules.
+ */
+ tmp->nativeformats = prefformat;
+ tmp->rawreadformat = AST_FORMAT_SLINEAR;
+ tmp->rawwriteformat = AST_FORMAT_SLINEAR;
+ if (state == AST_STATE_RING) {
+ tmp->rings = 1;
+ cid_name[0] = '\0';
+ cid_num[0] = '\0';
+ ast_callerid_split(me->callerid, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
+ ast_set_callerid(tmp, cid_num, cid_name, cid_num);
+ }
+ tmp->tech_pvt = me;
+
+ strncpy(tmp->context, context, sizeof(tmp->context)-1);
+ if (strlen(me->ext))
+ strncpy(tmp->exten, me->ext, sizeof(tmp->exten)-1);
+ else
+ strncpy(tmp->exten, "s", sizeof(tmp->exten) - 1);
+ if (strlen(me->language))
+ ast_string_field_set(tmp, language, me->language);
+
+ me->owner = tmp;
+
+ me->bridge = NULL;
+ me->lastoutput = -1;
+ me->lastinput = -1;
+ me->last_ignore_dtmf = 1;
+ me->readthread = 0;
+ me->play_dtmf[0] = '\0';
+ me->faxhandled =0;
+
+ me->lastgrunt = ast_tvnow(); /* Assume at least one grunt tone seen now. */
+ me->lastplay = ast_tvnow(); /* Assume at least one grunt tone seen now. */
+
+ if (state != AST_STATE_DOWN) {
+ if ((me->mode != MODE_FXO)&&(state != AST_STATE_UP)){
+ vpb_answer(tmp);
+ }
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ }
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ }
+ return tmp;
+}
+
+static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+ struct vpb_pvt *p;
+ struct ast_channel *tmp = NULL;
+ char *name = ast_strdup(data ? (char *)data : "");
+ char *s, *sepstr;
+ int group=-1;
+
+ oldformat = format;
+ format &= prefformat;
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+ return NULL;
+ }
+
+ sepstr = name;
+ s = strsep(&sepstr, "/"); /* Handle / issues */
+ if (!s)
+ s = "";
+ /* Check if we are looking for a group */
+ if (toupper(name[0]) == 'G' || toupper(name[0])=='R') {
+ group=atoi(name+1);
+ }
+ /* Search for an unowned channel */
+ ast_mutex_lock(&iflock); {
+ p = iflist;
+ while(p) {
+ if (group == -1){
+ if (strncmp(s, p->dev + 4, sizeof p->dev) == 0) {
+ if (!p->owner) {
+ tmp = vpb_new(p, AST_STATE_DOWN, p->context);
+ break;
+ }
+ }
+ }
+ else {
+ if ((p->group == group) && (!p->owner)) {
+ tmp = vpb_new(p, AST_STATE_DOWN, p->context);
+ break;
+ }
+ }
+ p = p->next;
+ }
+ } ast_mutex_unlock(&iflock);
+
+
+ ast_verb(2, " %s requested, got: [%s]\n", name, tmp ? tmp->name : "None");
+
+ ast_free(name);
+
+ restart_monitor();
+ return tmp;
+}
+
+static float parse_gain_value(char *gain_type, char *value)
+{
+ float gain;
+
+ /* try to scan number */
+ if (sscanf(value, "%f", &gain) != 1)
+ {
+ ast_log(LOG_ERROR, "Invalid %s value '%s' in '%s' config\n", value, gain_type, config);
+ return DEFAULT_GAIN;
+ }
+
+
+ /* percentage? */
+ /*if (value[strlen(value) - 1] == '%') */
+ /* return gain / (float)100; */
+
+ return gain;
+}
+
+
+static int unload_module()
+{
+ struct vpb_pvt *p;
+ /* First, take us out of the channel loop */
+ if (use_ast_ind == 1){
+ ast_channel_unregister(&vpb_tech_indicate);
+ }
+ else {
+ ast_channel_unregister(&vpb_tech);
+ }
+
+ ast_mutex_lock(&iflock); {
+ /* Hangup all interfaces if they have an owner */
+ p = iflist;
+ while(p) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ p = p->next;
+ }
+ iflist = NULL;
+ } ast_mutex_unlock(&iflock);
+
+ ast_mutex_lock(&monlock); {
+ if (mthreadactive > -1) {
+ pthread_cancel(monitor_thread);
+ pthread_join(monitor_thread, NULL);
+ }
+ mthreadactive = -2;
+ } ast_mutex_unlock(&monlock);
+
+ ast_mutex_lock(&iflock); {
+ /* Destroy all the interfaces and free their memory */
+
+ while(iflist) {
+ p = iflist;
+ ast_mutex_destroy(&p->lock);
+ pthread_cancel(p->readthread);
+ ast_mutex_destroy(&p->owner_lock);
+ ast_mutex_destroy(&p->record_lock);
+ ast_mutex_destroy(&p->play_lock);
+ ast_mutex_destroy(&p->play_dtmf_lock);
+ p->readthread = 0;
+
+ vpb_close(p->handle);
+
+ iflist = iflist->next;
+
+ ast_free(p);
+ }
+ iflist = NULL;
+ } ast_mutex_unlock(&iflock);
+
+ ast_mutex_lock(&bridge_lock); {
+ memset(bridges, 0, sizeof bridges);
+ } ast_mutex_unlock(&bridge_lock);
+ ast_mutex_destroy(&bridge_lock);
+ for(int i = 0; i < max_bridges; i++ ) {
+ ast_mutex_destroy(&bridges[i].lock);
+ ast_cond_destroy(&bridges[i].cond);
+ }
+ ast_free(bridges);
+
+ return 0;
+}
+
+static int load_module()
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { 0 };
+ struct vpb_pvt *tmp;
+ int board = 0, group = 0;
+ ast_group_t callgroup = 0;
+ ast_group_t pickupgroup = 0;
+ int mode = MODE_IMMEDIATE;
+ float txgain = DEFAULT_GAIN, rxgain = DEFAULT_GAIN;
+ float txswgain = 0, rxswgain = 0;
+ int got_gain=0;
+ int first_channel = 1;
+ int echo_cancel = DEFAULT_ECHO_CANCEL;
+ int error = 0; /* Error flag */
+ int bal1 = -1; /* Special value - means do not set */
+ int bal2 = -1;
+ int bal3 = -1;
+ char * callerid = NULL;
+
+ cfg = ast_config_load(config, config_flags);
+
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ vpb_seterrormode(VPB_ERROR_CODE);
+
+ ast_mutex_lock(&iflock); {
+ v = ast_variable_browse(cfg, "general");
+ while (v){
+ if (strcasecmp(v->name, "cards") == 0) {
+ ast_log(LOG_NOTICE,"VPB Driver configured to use [%d] cards\n",atoi(v->value));
+ }
+ else if (strcasecmp(v->name, "indication") == 0) {
+ use_ast_ind = 1;
+ ast_log(LOG_NOTICE,"VPB driver using Asterisk Indication functions!\n");
+ }
+ else if (strcasecmp(v->name, "break-for-dtmf") == 0) {
+ if (ast_true(v->value)){
+ break_for_dtmf = 1;
+ }
+ else {
+ break_for_dtmf = 0;
+ ast_log(LOG_NOTICE,"VPB driver not stopping for DTMF's in native bridge\n");
+ }
+ }
+ else if (strcasecmp(v->name, "ast-dtmf") == 0) {
+ use_ast_dtmf = 1;
+ ast_log(LOG_NOTICE,"VPB driver using Asterisk DTMF play functions!\n");
+ }
+ else if (strcasecmp(v->name, "ast-dtmf-det") == 0) {
+ use_ast_dtmfdet = 1;
+ ast_log(LOG_NOTICE,"VPB driver using Asterisk DTMF detection functions!\n");
+ }
+ else if (strcasecmp(v->name, "relaxdtmf") == 0) {
+ relaxdtmf = 1;
+ ast_log(LOG_NOTICE,"VPB driver using Relaxed DTMF with Asterisk DTMF detections functions!\n");
+ }
+ else if (strcasecmp(v->name, "timer_period_ring") ==0) {
+ timer_period_ring = atoi(v->value);
+ }
+ else if (strcasecmp(v->name, "ecsuppthres") ==0) {
+ ec_supp_threshold = atoi(v->value);
+ }
+ else if (strcasecmp(v->name, "dtmfidd") ==0) {
+ dtmf_idd = atoi(v->value);
+ ast_log(LOG_NOTICE,"VPB Driver setting DTMF IDD to [%d]ms\n",dtmf_idd);
+ }
+ v = v->next;
+ }
+
+ v = ast_variable_browse(cfg, "interfaces");
+ while(v) {
+ /* Create the interface list */
+ if (strcasecmp(v->name, "board") == 0) {
+ board = atoi(v->value);
+ } else if (strcasecmp(v->name, "group") == 0){
+ group = atoi(v->value);
+ } else if (strcasecmp(v->name, "callgroup") == 0){
+ callgroup = ast_get_group(v->value);
+ } else if (strcasecmp(v->name, "pickupgroup") == 0){
+ pickupgroup = ast_get_group(v->value);
+ } else if (strcasecmp(v->name, "usepolaritycid") == 0){
+ UsePolarityCID = atoi(v->value);
+ } else if (strcasecmp(v->name, "useloopdrop") == 0){
+ UseLoopDrop = atoi(v->value);
+ } else if (strcasecmp(v->name, "usenativebridge") == 0){
+ UseNativeBridge = atoi(v->value);
+ } else if (strcasecmp(v->name, "channel") == 0) {
+ int channel = atoi(v->value);
+ tmp = mkif(board, channel, mode, got_gain, txgain, rxgain, txswgain, rxswgain, bal1, bal2, bal3, callerid, echo_cancel,group,callgroup,pickupgroup);
+ if (tmp) {
+ if(first_channel) {
+ mkbrd( tmp->vpb_model, echo_cancel );
+ first_channel = 0;
+ }
+ tmp->next = iflist;
+ iflist = tmp;
+ } else {
+ ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value);
+ error = -1;
+ goto done;
+ }
+ } else if (strcasecmp(v->name, "language") == 0) {
+ strncpy(language, v->value, sizeof(language)-1);
+ } else if (strcasecmp(v->name, "callerid") == 0) {
+ callerid = ast_strdup(v->value);
+ } else if (strcasecmp(v->name, "mode") == 0) {
+ if (strncasecmp(v->value, "di", 2) == 0)
+ mode = MODE_DIALTONE;
+ else if (strncasecmp(v->value, "im", 2) == 0)
+ mode = MODE_IMMEDIATE;
+ else if (strncasecmp(v->value, "fx", 2) == 0)
+ mode = MODE_FXO;
+ else
+ ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value);
+ } else if (!strcasecmp(v->name, "context")) {
+ strncpy(context, v->value, sizeof(context)-1);
+ } else if (!strcasecmp(v->name, "echocancel")) {
+ if (!strcasecmp(v->value, "off"))
+ echo_cancel = 0;
+ } else if (strcasecmp(v->name, "txgain") == 0) {
+ txswgain = parse_gain_value(v->name, v->value);
+ got_gain |=VPB_GOT_TXSWG;
+ } else if (strcasecmp(v->name, "rxgain") == 0) {
+ rxswgain = parse_gain_value(v->name, v->value);
+ got_gain |=VPB_GOT_RXSWG;
+ } else if (strcasecmp(v->name, "txhwgain") == 0) {
+ txgain = parse_gain_value(v->name, v->value);
+ got_gain |=VPB_GOT_TXHWG;
+ } else if (strcasecmp(v->name, "rxhwgain") == 0) {
+ rxgain = parse_gain_value(v->name, v->value);
+ got_gain |=VPB_GOT_RXHWG;
+ } else if (strcasecmp(v->name, "bal1") == 0) {
+ bal1 = strtol(v->value, NULL, 16);
+ if(bal1<0 || bal1>255) {
+ ast_log(LOG_WARNING, "Bad bal1 value: %d\n", bal1);
+ bal1 = -1;
+ }
+ } else if (strcasecmp(v->name, "bal2") == 0) {
+ bal2 = strtol(v->value, NULL, 16);
+ if(bal2<0 || bal2>255) {
+ ast_log(LOG_WARNING, "Bad bal2 value: %d\n", bal2);
+ bal2 = -1;
+ }
+ } else if (strcasecmp(v->name, "bal3") == 0) {
+ bal3 = strtol(v->value, NULL, 16);
+ if(bal3<0 || bal3>255) {
+ ast_log(LOG_WARNING, "Bad bal3 value: %d\n", bal3);
+ bal3 = -1;
+ }
+ } else if (strcasecmp(v->name, "grunttimeout") == 0) {
+ gruntdetect_timeout = 1000*atoi(v->value);
+ }
+ v = v->next;
+ }
+
+ if (gruntdetect_timeout < 1000)
+ gruntdetect_timeout = 1000;
+
+ done: (void)0;
+ } ast_mutex_unlock(&iflock);
+
+ ast_config_destroy(cfg);
+
+ if (use_ast_ind == 1){
+ if (!error && ast_channel_register(&vpb_tech_indicate) != 0) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'vpb'\n");
+ error = -1;
+ }
+ else {
+ ast_log(LOG_NOTICE,"VPB driver Registered (w/AstIndication)\n");
+ }
+ }
+ else {
+ if (!error && ast_channel_register(&vpb_tech) != 0) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'vpb'\n");
+ error = -1;
+ }
+ else {
+ ast_log(LOG_NOTICE,"VPB driver Registered )\n");
+ }
+ }
+
+
+ if (error)
+ unload_module();
+ else
+ restart_monitor(); /* And start the monitor for the first time */
+
+ return error;
+}
+
+/**/
+#if defined(__cplusplus) || defined(c_plusplus)
+ }
+#endif
+/**/
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "VoiceTronix API driver");
diff --git a/trunk/channels/chan_zap.c b/trunk/channels/chan_zap.c
new file mode 100644
index 000000000..08dc2aead
--- /dev/null
+++ b/trunk/channels/chan_zap.c
@@ -0,0 +1,14253 @@
+/*
+ * 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 Zaptel Pseudo TDM interface
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * Connects to the Zaptel telephony library as well as
+ * libpri. Libpri is optional and needed only if you are
+ * going to use ISDN connections.
+ *
+ * You need to install libraries before you attempt to compile
+ * and install the Zaptel channel.
+ *
+ * \par See also
+ * \arg \ref Config_zap
+ *
+ * \ingroup channel_drivers
+ *
+ * \todo Deprecate the "musiconhold" configuration option post 1.4
+ */
+
+/*** MODULEINFO
+ <depend>res_smdi</depend>
+ <depend>zaptel_vldtmf</depend>
+ <depend>zaptel</depend>
+ <depend>tonezone</depend>
+ <depend>res_features</depend>
+ <use>pri</use>
+ <use>ss7</use>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef __NetBSD__
+#include <pthread.h>
+#include <signal.h>
+#else
+#include <sys/signal.h>
+#endif
+#include <sys/ioctl.h>
+#include <math.h>
+#include <ctype.h>
+#include "asterisk/zapata.h"
+
+#ifdef HAVE_PRI
+#include <libpri.h>
+#endif
+
+#ifdef HAVE_SS7
+#include <libss7.h>
+#endif
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/file.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/callerid.h"
+#include "asterisk/adsi.h"
+#include "asterisk/cli.h"
+#include "asterisk/cdr.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/say.h"
+#include "asterisk/tdd.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/astdb.h"
+#include "asterisk/manager.h"
+#include "asterisk/causes.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+#include "asterisk/transcap.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/smdi.h"
+#include "asterisk/astobj.h"
+#include "asterisk/event.h"
+
+#define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */
+
+#ifdef ZT_SPANINFO_HAS_LINECONFIG
+static const char *lbostr[] = {
+"0 db (CSU)/0-133 feet (DSX-1)",
+"133-266 feet (DSX-1)",
+"266-399 feet (DSX-1)",
+"399-533 feet (DSX-1)",
+"533-655 feet (DSX-1)",
+"-7.5db (CSU)",
+"-15db (CSU)",
+"-22.5db (CSU)"
+};
+#endif
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+#if !defined(ZT_SIG_EM_E1) || (defined(HAVE_PRI) && !defined(ZT_SIG_HARDHDLC))
+#error "Your Zaptel is too old. Please update"
+#endif
+
+#ifndef ZT_TONEDETECT
+/* Work around older code with no tone detect */
+#define ZT_EVENT_DTMFDOWN 0
+#define ZT_EVENT_DTMFUP 0
+#endif
+
+/* define this to send PRI user-user information elements */
+#undef SUPPORT_USERUSER
+
+/*!
+ * \note Define ZHONE_HACK to cause us to go off hook and then back on hook when
+ * the user hangs up to reset the state machine so ring works properly.
+ * This is used to be able to support kewlstart by putting the zhone in
+ * groundstart mode since their forward disconnect supervision is entirely
+ * broken even though their documentation says it isn't and their support
+ * is entirely unwilling to provide any assistance with their channel banks
+ * even though their web site says they support their products for life.
+ */
+/* #define ZHONE_HACK */
+
+/*! \note
+ * Define if you want to check the hook state for an FXO (FXS signalled) interface
+ * before dialing on it. Certain FXO interfaces always think they're out of
+ * service with this method however.
+ */
+/* #define ZAP_CHECK_HOOKSTATE */
+
+/*! \brief Typically, how many rings before we should send Caller*ID */
+#define DEFAULT_CIDRINGS 1
+
+#define CHANNEL_PSEUDO -12
+
+#define AST_LAW(p) (((p)->law == ZT_LAW_ALAW) ? AST_FORMAT_ALAW : AST_FORMAT_ULAW)
+
+
+/*! \brief Signaling types that need to use MF detection should be placed in this macro */
+#define NEED_MFDETECT(p) (((p)->sig == SIG_FEATDMF) || ((p)->sig == SIG_FEATDMF_TA) || ((p)->sig == SIG_E911) || ((p)->sig == SIG_FGC_CAMA) || ((p)->sig == SIG_FGC_CAMAMF) || ((p)->sig == SIG_FEATB))
+
+static const char tdesc[] = "Zapata Telephony Driver"
+#ifdef HAVE_PRI
+ " w/PRI"
+#endif
+#ifdef HAVE_SS7
+ " w/SS7"
+#endif
+;
+
+static const char config[] = "zapata.conf";
+
+#define SIG_EM ZT_SIG_EM
+#define SIG_EMWINK (0x0100000 | ZT_SIG_EM)
+#define SIG_FEATD (0x0200000 | ZT_SIG_EM)
+#define SIG_FEATDMF (0x0400000 | ZT_SIG_EM)
+#define SIG_FEATB (0x0800000 | ZT_SIG_EM)
+#define SIG_E911 (0x1000000 | ZT_SIG_EM)
+#define SIG_FEATDMF_TA (0x2000000 | ZT_SIG_EM)
+#define SIG_FGC_CAMA (0x4000000 | ZT_SIG_EM)
+#define SIG_FGC_CAMAMF (0x8000000 | ZT_SIG_EM)
+#define SIG_FXSLS ZT_SIG_FXSLS
+#define SIG_FXSGS ZT_SIG_FXSGS
+#define SIG_FXSKS ZT_SIG_FXSKS
+#define SIG_FXOLS ZT_SIG_FXOLS
+#define SIG_FXOGS ZT_SIG_FXOGS
+#define SIG_FXOKS ZT_SIG_FXOKS
+#define SIG_PRI ZT_SIG_CLEAR
+#define SIG_BRI (0x2000000 | ZT_SIG_CLEAR)
+#define SIG_BRI_PTMP (0X4000000 | ZT_SIG_CLEAR)
+#define SIG_SS7 (0x1000000 | ZT_SIG_CLEAR)
+#define SIG_SF ZT_SIG_SF
+#define SIG_SFWINK (0x0100000 | ZT_SIG_SF)
+#define SIG_SF_FEATD (0x0200000 | ZT_SIG_SF)
+#define SIG_SF_FEATDMF (0x0400000 | ZT_SIG_SF)
+#define SIG_SF_FEATB (0x0800000 | ZT_SIG_SF)
+#define SIG_EM_E1 ZT_SIG_EM_E1
+#define SIG_GR303FXOKS (0x0100000 | ZT_SIG_FXOKS)
+#define SIG_GR303FXSKS (0x0100000 | ZT_SIG_FXSKS)
+
+#ifdef LOTS_OF_SPANS
+#define NUM_SPANS ZT_MAX_SPANS
+#else
+#define NUM_SPANS 32
+#endif
+#define NUM_DCHANS 4 /*!< No more than 4 d-channels */
+#define MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */
+
+#define CHAN_PSEUDO -2
+
+#define DCHAN_PROVISIONED (1 << 0)
+#define DCHAN_NOTINALARM (1 << 1)
+#define DCHAN_UP (1 << 2)
+
+#define DCHAN_AVAILABLE (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP)
+
+/* Overlap dialing option types */
+#define ZAP_OVERLAPDIAL_NONE 0
+#define ZAP_OVERLAPDIAL_OUTGOING 1
+#define ZAP_OVERLAPDIAL_INCOMING 2
+#define ZAP_OVERLAPDIAL_BOTH (ZAP_OVERLAPDIAL_INCOMING|ZAP_OVERLAPDIAL_OUTGOING)
+
+
+#define CALLPROGRESS_PROGRESS 1
+#define CALLPROGRESS_FAX_OUTGOING 2
+#define CALLPROGRESS_FAX_INCOMING 4
+#define CALLPROGRESS_FAX (CALLPROGRESS_FAX_INCOMING | CALLPROGRESS_FAX_OUTGOING)
+
+static char defaultcic[64] = "";
+static char defaultozz[64] = "";
+
+/*! Run this script when the MWI state changes on an FXO line, if mwimonitor is enabled */
+static char mwimonitornotify[PATH_MAX] = "";
+
+static char progzone[10] = "";
+
+static int usedistinctiveringdetection = 0;
+static int distinctiveringaftercid = 0;
+
+static int numbufs = 4;
+
+static int mwilevel = 512;
+
+#ifdef HAVE_PRI
+static struct ast_channel inuse;
+#ifdef PRI_GETSET_TIMERS
+static int pritimers[PRI_MAX_TIMERS];
+#endif
+static int pridebugfd = -1;
+static char pridebugfilename[1024] = "";
+#endif
+
+/*! \brief Wait up to 16 seconds for first digit (FXO logic) */
+static int firstdigittimeout = 16000;
+
+/*! \brief How long to wait for following digits (FXO logic) */
+static int gendigittimeout = 8000;
+
+/*! \brief How long to wait for an extra digit, if there is an ambiguous match */
+static int matchdigittimeout = 3000;
+
+/*! \brief Protect the interface list (of zt_pvt's) */
+AST_MUTEX_DEFINE_STATIC(iflock);
+
+
+static int ifcount = 0;
+
+#ifdef HAVE_PRI
+AST_MUTEX_DEFINE_STATIC(pridebugfdlock);
+#endif
+
+/*! \brief Protect the monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+/*! \brief This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+
+static int restart_monitor(void);
+
+static enum ast_bridge_result zt_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
+
+static int zt_sendtext(struct ast_channel *c, const char *text);
+
+static void mwi_event_cb(const struct ast_event *event, void *userdata)
+{
+ /* This module does not handle MWI in an event-based manner. However, it
+ * subscribes to MWI for each mailbox that is configured so that the core
+ * knows that we care about it. Then, chan_zap will get the MWI from the
+ * event cache instead of checking the mailbox directly. */
+}
+
+/*! \brief Avoid the silly zt_getevent which ignores a bunch of events */
+static inline int zt_get_event(int fd)
+{
+ int j;
+ if (ioctl(fd, ZT_GETEVENT, &j) == -1)
+ return -1;
+ return j;
+}
+
+/*! \brief Avoid the silly zt_waitevent which ignores a bunch of events */
+static inline int zt_wait_event(int fd)
+{
+ int i, j = 0;
+ i = ZT_IOMUX_SIGEVENT;
+ if (ioctl(fd, ZT_IOMUX, &i) == -1)
+ return -1;
+ if (ioctl(fd, ZT_GETEVENT, &j) == -1)
+ return -1;
+ return j;
+}
+
+/*! Chunk size to read -- we use 20ms chunks to make things happy. */
+#define READ_SIZE 160
+
+#define MASK_AVAIL (1 << 0) /*!< Channel available for PRI use */
+#define MASK_INUSE (1 << 1) /*!< Channel currently in use */
+
+#define CALLWAITING_SILENT_SAMPLES ( (300 * 8) / READ_SIZE) /*!< 300 ms */
+#define CALLWAITING_REPEAT_SAMPLES ( (10000 * 8) / READ_SIZE) /*!< 300 ms */
+#define CIDCW_EXPIRE_SAMPLES ( (500 * 8) / READ_SIZE) /*!< 500 ms */
+#define MIN_MS_SINCE_FLASH ( (2000) ) /*!< 2000 ms */
+#define DEFAULT_RINGT ( (8000 * 8) / READ_SIZE)
+
+struct zt_pvt;
+
+static int ringt_base = DEFAULT_RINGT;
+
+#ifdef HAVE_SS7
+
+#define LINKSTATE_INALARM (1 << 0)
+#define LINKSTATE_STARTING (1 << 1)
+#define LINKSTATE_UP (1 << 2)
+#define LINKSTATE_DOWN (1 << 3)
+
+#define SS7_NAI_DYNAMIC -1
+
+struct zt_ss7 {
+ pthread_t master; /*!< Thread of master */
+ ast_mutex_t lock;
+ int fds[NUM_DCHANS];
+ int numsigchans;
+ int linkstate[NUM_DCHANS];
+ int numchans;
+ int type;
+ enum {
+ LINKSET_STATE_DOWN = 0,
+ LINKSET_STATE_UP
+ } state;
+ char called_nai; /*!< Called Nature of Address Indicator */
+ char calling_nai; /*!< Calling Nature of Address Indicator */
+ char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */
+ char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */
+ char subscriberprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */
+ char unknownprefix[20]; /*!< for unknown dialplans */
+ struct ss7 *ss7;
+ struct zt_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
+};
+
+static struct zt_ss7 linksets[NUM_SPANS];
+
+static int cur_ss7type = -1;
+static int cur_linkset = -1;
+static int cur_pointcode = -1;
+static int cur_cicbeginswith = -1;
+static int cur_adjpointcode = -1;
+static int cur_networkindicator = -1;
+static int cur_defaultdpc = -1;
+#endif /* HAVE_SS7 */
+
+#ifdef HAVE_PRI
+
+#define PVT_TO_CHANNEL(p) (((p)->prioffset) | ((p)->logicalspan << 8) | (p->pri->mastertrunkgroup ? 0x10000 : 0))
+#define PRI_CHANNEL(p) ((p) & 0xff)
+#define PRI_SPAN(p) (((p) >> 8) & 0xff)
+#define PRI_EXPLICIT(p) (((p) >> 16) & 0x01)
+
+struct zt_pri {
+ pthread_t master; /*!< Thread of master */
+ ast_mutex_t lock; /*!< Mutex */
+ char idleext[AST_MAX_EXTENSION]; /*!< Where to idle extra calls */
+ char idlecontext[AST_MAX_CONTEXT]; /*!< What context to use for idle */
+ char idledial[AST_MAX_EXTENSION]; /*!< What to dial before dumping */
+ int minunused; /*!< Min # of channels to keep empty */
+ int minidle; /*!< Min # of "idling" calls to keep active */
+ int nodetype; /*!< Node type */
+ int switchtype; /*!< Type of switch to emulate */
+ int nsf; /*!< Network-Specific Facilities */
+ int dialplan; /*!< Dialing plan */
+ int localdialplan; /*!< Local dialing plan */
+ char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */
+ char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */
+ char localprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */
+ char privateprefix[20]; /*!< for private dialplans */
+ char unknownprefix[20]; /*!< for unknown dialplans */
+ int dchannels[NUM_DCHANS]; /*!< What channel are the dchannels on */
+ int trunkgroup; /*!< What our trunkgroup is */
+ int mastertrunkgroup; /*!< What trunk group is our master */
+ int prilogicalspan; /*!< Logical span number within trunk group */
+ int numchans; /*!< Num of channels we represent */
+ int overlapdial; /*!< In overlap dialing mode */
+ int facilityenable; /*!< Enable facility IEs */
+ struct pri *dchans[NUM_DCHANS]; /*!< Actual d-channels */
+ int dchanavail[NUM_DCHANS]; /*!< Whether each channel is available */
+ struct pri *pri; /*!< Currently active D-channel */
+ int debug;
+ int fds[NUM_DCHANS]; /*!< FD's for d-channels */
+ int offset;
+ int span;
+ int resetting;
+ int resetpos;
+ time_t lastreset; /*!< time when unused channels were last reset */
+ long resetinterval; /*!< Interval (in seconds) for resetting unused channels */
+ int sig;
+ struct zt_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
+ struct zt_pvt *crvs; /*!< Member CRV structs */
+ struct zt_pvt *crvend; /*!< Pointer to end of CRV structs */
+};
+
+
+static struct zt_pri pris[NUM_SPANS];
+
+#if 0
+#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)
+#else
+#define DEFAULT_PRI_DEBUG 0
+#endif
+
+static inline void pri_rel(struct zt_pri *pri)
+{
+ ast_mutex_unlock(&pri->lock);
+}
+
+#else
+/*! Shut up the compiler */
+struct zt_pri;
+#endif
+
+#define SUB_REAL 0 /*!< Active call */
+#define SUB_CALLWAIT 1 /*!< Call-Waiting call on hold */
+#define SUB_THREEWAY 2 /*!< Three-way call */
+
+/* Polarity states */
+#define POLARITY_IDLE 0
+#define POLARITY_REV 1
+
+
+static struct zt_distRings drings;
+
+struct distRingData {
+ int ring[3];
+ int range;
+};
+struct ringContextData {
+ char contextData[AST_MAX_CONTEXT];
+};
+struct zt_distRings {
+ struct distRingData ringnum[3];
+ struct ringContextData ringContext[3];
+};
+
+static char *subnames[] = {
+ "Real",
+ "Callwait",
+ "Threeway"
+};
+
+struct zt_subchannel {
+ int zfd;
+ struct ast_channel *owner;
+ int chan;
+ short buffer[AST_FRIENDLY_OFFSET/2 + READ_SIZE];
+ struct ast_frame f; /*!< One frame for each channel. How did this ever work before? */
+ unsigned int needringing:1;
+ unsigned int needbusy:1;
+ unsigned int needcongestion:1;
+ unsigned int needcallerid:1;
+ unsigned int needanswer:1;
+ unsigned int needflash:1;
+ unsigned int needhold:1;
+ unsigned int needunhold:1;
+ unsigned int linear:1;
+ unsigned int inthreeway:1;
+ ZT_CONFINFO curconf;
+};
+
+#define CONF_USER_REAL (1 << 0)
+#define CONF_USER_THIRDCALL (1 << 1)
+
+#define MAX_SLAVES 4
+
+static struct zt_pvt {
+ ast_mutex_t lock;
+ struct ast_channel *owner; /*!< Our current active owner (if applicable) */
+ /*!< Up to three channels can be associated with this call */
+
+ struct zt_subchannel sub_unused; /*!< Just a safety precaution */
+ struct zt_subchannel subs[3]; /*!< Sub-channels */
+ struct zt_confinfo saveconf; /*!< Saved conference info */
+
+ struct zt_pvt *slaves[MAX_SLAVES]; /*!< Slave to us (follows our conferencing) */
+ struct zt_pvt *master; /*!< Master to us (we follow their conferencing) */
+ int inconference; /*!< If our real should be in the conference */
+
+ int sig; /*!< Signalling style */
+ int radio; /*!< radio type */
+ int outsigmod; /*!< Outbound Signalling style (modifier) */
+ int oprmode; /*!< "Operator Services" mode */
+ struct zt_pvt *oprpeer; /*!< "Operator Services" peer tech_pvt ptr */
+ float cid_rxgain; /*!< "Gain to apply during caller id */
+ float rxgain;
+ float txgain;
+ int tonezone; /*!< tone zone for this chan, or -1 for default */
+ struct zt_pvt *next; /*!< Next channel in list */
+ struct zt_pvt *prev; /*!< Prev channel in list */
+
+ /* flags */
+ unsigned int adsi:1;
+ unsigned int answeronpolarityswitch:1;
+ unsigned int busydetect:1;
+ unsigned int callreturn:1;
+ unsigned int callwaiting:1;
+ unsigned int callwaitingcallerid:1;
+ unsigned int cancallforward:1;
+ unsigned int canpark:1;
+ unsigned int confirmanswer:1; /*!< Wait for '#' to confirm answer */
+ unsigned int destroy:1;
+ unsigned int didtdd:1; /*!< flag to say its done it once */
+ unsigned int dialednone:1;
+ unsigned int dialing:1;
+ unsigned int digital:1;
+ unsigned int dnd:1;
+ unsigned int echobreak:1;
+ unsigned int echocanbridged:1;
+ unsigned int echocanon:1;
+ unsigned int faxhandled:1; /*!< Has a fax tone already been handled? */
+ unsigned int firstradio:1;
+ unsigned int hanguponpolarityswitch:1;
+ unsigned int hardwaredtmf:1;
+ unsigned int hidecallerid:1;
+ unsigned int hidecalleridname:1; /*!< Hide just the name not the number for legacy PBX use */
+ unsigned int ignoredtmf:1;
+ unsigned int immediate:1; /*!< Answer before getting digits? */
+ unsigned int inalarm:1;
+ unsigned int mate:1; /*!< flag to say its in MATE mode */
+ unsigned int outgoing:1;
+ /* unsigned int overlapdial:1; unused and potentially confusing */
+ unsigned int permcallwaiting:1;
+ unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */
+ unsigned int priindication_oob:1;
+ unsigned int priexclusive:1;
+ unsigned int pulse:1;
+ unsigned int pulsedial:1; /*!< whether a pulse dial phone is detected */
+ unsigned int restrictcid:1; /*!< Whether restrict the callerid -> only send ANI */
+ unsigned int threewaycalling:1;
+ unsigned int transfer:1;
+ unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */
+ unsigned int use_callingpres:1; /*!< Whether to use the callingpres the calling switch sends */
+ unsigned int usedistinctiveringdetection:1;
+ unsigned int zaptrcallerid:1; /*!< should we use the callerid from incoming call on zap transfer or not */
+ unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */
+ unsigned int mwimonitor:1; /*!< monitor this FXO port for MWI indication from other end */
+ unsigned int mwimonitoractive:1; /*!< an MWI monitor thread is currently active */
+ /* Channel state or unavilability flags */
+ unsigned int inservice:1;
+ unsigned int locallyblocked:1;
+ unsigned int remotelyblocked:1;
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ unsigned int rlt:1;
+ unsigned int alerting:1;
+ unsigned int alreadyhungup:1;
+ unsigned int isidlecall:1;
+ unsigned int proceeding:1;
+ unsigned int progress:1;
+ unsigned int resetting:1;
+ unsigned int setup_ack:1;
+#endif
+ unsigned int use_smdi:1; /* Whether to use SMDI on this channel */
+ struct ast_smdi_interface *smdi_iface; /* The serial port to listen for SMDI data on */
+
+ struct zt_distRings drings;
+
+ char context[AST_MAX_CONTEXT];
+ char defcontext[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ char language[MAX_LANGUAGE];
+ char mohinterpret[MAX_MUSICCLASS];
+ char mohsuggest[MAX_MUSICCLASS];
+#if defined(PRI_ANI) || defined(HAVE_SS7)
+ char cid_ani[AST_MAX_EXTENSION];
+#endif
+ int cid_ani2;
+ char cid_num[AST_MAX_EXTENSION];
+ int cid_ton; /*!< Type Of Number (TON) */
+ char cid_name[AST_MAX_EXTENSION];
+ char lastcid_num[AST_MAX_EXTENSION];
+ char lastcid_name[AST_MAX_EXTENSION];
+ char *origcid_num; /*!< malloced original callerid */
+ char *origcid_name; /*!< malloced original callerid */
+ char callwait_num[AST_MAX_EXTENSION];
+ char callwait_name[AST_MAX_EXTENSION];
+ char rdnis[AST_MAX_EXTENSION];
+ char dnid[AST_MAX_EXTENSION];
+ ast_group_t group;
+ int law;
+ int confno; /*!< Our conference */
+ int confusers; /*!< Who is using our conference */
+ int propconfno; /*!< Propagated conference number */
+ ast_group_t callgroup;
+ ast_group_t pickupgroup;
+ struct ast_variable *vars;
+ int channel; /*!< Channel Number or CRV */
+ int span; /*!< Span number */
+ time_t guardtime; /*!< Must wait this much time before using for new call */
+ int cid_signalling; /*!< CID signalling type bell202 or v23 */
+ int cid_start; /*!< CID start indicator, polarity or ring */
+ int callingpres; /*!< The value of callling presentation that we're going to use when placing a PRI call */
+ int callwaitingrepeat; /*!< How many samples to wait before repeating call waiting */
+ int cidcwexpire; /*!< When to expire our muting for CID/CW */
+ unsigned char *cidspill;
+ int cidpos;
+ int cidlen;
+ int ringt;
+ int ringt_base;
+ int stripmsd;
+ int callwaitcas;
+ int callwaitrings;
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+ struct {
+ struct zt_echocanparams head;
+ struct zt_echocanparam params[ZT_MAX_ECHOCANPARAMS];
+ } echocancel;
+#else
+ int echocancel;
+#endif
+ int echotraining;
+ char echorest[20];
+ int busycount;
+ int busy_tonelength;
+ int busy_quietlength;
+ int callprogress;
+ struct timeval flashtime; /*!< Last flash-hook time */
+ struct ast_dsp *dsp;
+ int cref; /*!< Call reference number */
+ ZT_DIAL_OPERATION dop;
+ int whichwink; /*!< SIG_FEATDMF_TA Which wink are we on? */
+ char finaldial[64];
+ char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
+ int amaflags; /*!< AMA Flags */
+ struct tdd_state *tdd; /*!< TDD flag */
+ char call_forward[AST_MAX_EXTENSION];
+ char mailbox[AST_MAX_EXTENSION];
+ struct ast_event_sub *mwi_event_sub;
+ char dialdest[256];
+ int onhooktime;
+ int msgstate;
+ int distinctivering; /*!< Which distinctivering to use */
+ int cidrings; /*!< Which ring to deliver CID on */
+ int dtmfrelax; /*!< whether to run in relaxed DTMF mode */
+ int fake_event;
+ int polarityonanswerdelay;
+ struct timeval polaritydelaytv;
+ int sendcalleridafter;
+#ifdef HAVE_PRI
+ struct zt_pri *pri;
+ struct zt_pvt *bearer;
+ struct zt_pvt *realcall;
+ q931_call *call;
+ int prioffset;
+ int logicalspan;
+#endif
+ int polarity;
+ int dsp_features;
+#ifdef HAVE_SS7
+ struct zt_ss7 *ss7;
+ struct isup_call *ss7call;
+ char charge_number[50];
+ char gen_add_number[50];
+ char gen_dig_number[50];
+ unsigned char gen_add_num_plan;
+ unsigned char gen_add_nai;
+ unsigned char gen_add_pres_ind;
+ unsigned char gen_add_type;
+ unsigned char gen_dig_type;
+ unsigned char gen_dig_scheme;
+ char jip_number[50];
+ unsigned char lspi_type;
+ unsigned char lspi_scheme;
+ unsigned char lspi_context;
+ char lspi_ident[50];
+ unsigned int call_ref_ident;
+ unsigned int call_ref_pc;
+ int transcap;
+ int cic; /*!< CIC associated with channel */
+ unsigned int dpc; /*!< CIC's DPC */
+ unsigned int loopedback:1;
+#endif
+ char begindigit;
+} *iflist = NULL, *ifend = NULL;
+
+/*! \brief Channel configuration from zapata.conf .
+ * This struct is used for parsing the [channels] section of zapata.conf.
+ * Generally there is a field here for every possible configuration item.
+ *
+ * The state of fields is saved along the parsing and whenever a 'channel'
+ * statement is reached, the current zt_chan_conf is used to configure the
+ * channel (struct zt_pvt)
+ *
+ * \see zt_chan_init for the default values.
+ */
+struct zt_chan_conf {
+ struct zt_pvt chan;
+#ifdef HAVE_PRI
+ struct zt_pri pri;
+#endif
+
+#ifdef HAVE_SS7
+ struct zt_ss7 ss7;
+#endif
+ ZT_PARAMS timing;
+ int is_sig_auto; /*!< Use channel signalling from Zaptel? */
+
+ char smdi_port[SMDI_MAX_FILENAME_LEN];
+};
+
+/** returns a new zt_chan_conf with default values (by-value) */
+static struct zt_chan_conf zt_chan_conf_default(void) {
+ /* recall that if a field is not included here it is initialized
+ * to 0 or equivalent
+ */
+ struct zt_chan_conf conf = {
+#ifdef HAVE_PRI
+ .pri = {
+ .nsf = PRI_NSF_NONE,
+ .switchtype = PRI_SWITCH_NI2,
+ .dialplan = PRI_NATIONAL_ISDN + 1,
+ .localdialplan = PRI_NATIONAL_ISDN + 1,
+ .nodetype = PRI_CPE,
+
+ .minunused = 2,
+ .idleext = "",
+ .idledial = "",
+ .internationalprefix = "",
+ .nationalprefix = "",
+ .localprefix = "",
+ .privateprefix = "",
+ .unknownprefix = "",
+ .resetinterval = -1,
+ },
+#endif
+#ifdef HAVE_SS7
+ .ss7 = {
+ .called_nai = SS7_NAI_NATIONAL,
+ .calling_nai = SS7_NAI_NATIONAL,
+ .internationalprefix = "",
+ .nationalprefix = "",
+ .subscriberprefix = "",
+ .unknownprefix = ""
+ },
+#endif
+ .chan = {
+ .context = "default",
+ .cid_num = "",
+ .cid_name = "",
+ .mohinterpret = "default",
+ .mohsuggest = "",
+ .transfertobusy = 1,
+
+ .cid_signalling = CID_SIG_BELL,
+ .cid_start = CID_START_RING,
+ .zaptrcallerid = 0,
+ .use_callerid = 1,
+ .sig = -1,
+ .outsigmod = -1,
+
+ .cid_rxgain = +5.0,
+
+ .tonezone = -1,
+
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+ .echocancel.head.tap_length = 1,
+#else
+ .echocancel = 1,
+#endif
+
+ .busycount = 3,
+
+ .accountcode = "",
+
+ .mailbox = "",
+
+
+ .polarityonanswerdelay = 600,
+
+ .sendcalleridafter = DEFAULT_CIDRINGS
+ },
+ .timing = {
+ .prewinktime = -1,
+ .preflashtime = -1,
+ .winktime = -1,
+ .flashtime = -1,
+ .starttime = -1,
+ .rxwinktime = -1,
+ .rxflashtime = -1,
+ .debouncetime = -1
+ },
+ .is_sig_auto = 1,
+ .smdi_port = "/dev/ttyS0",
+ };
+
+ return conf;
+}
+
+
+static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause);
+static int zt_digit_begin(struct ast_channel *ast, char digit);
+static int zt_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int zt_sendtext(struct ast_channel *c, const char *text);
+static int zt_call(struct ast_channel *ast, char *rdest, int timeout);
+static int zt_hangup(struct ast_channel *ast);
+static int zt_answer(struct ast_channel *ast);
+static struct ast_frame *zt_read(struct ast_channel *ast);
+static int zt_write(struct ast_channel *ast, struct ast_frame *frame);
+static struct ast_frame *zt_exception(struct ast_channel *ast);
+static int zt_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen);
+static int zt_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int zt_setoption(struct ast_channel *chan, int option, void *data, int datalen);
+static int zt_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len);
+
+static const struct ast_channel_tech zap_tech = {
+ .type = "Zap",
+ .description = tdesc,
+ .capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW,
+ .requester = zt_request,
+ .send_digit_begin = zt_digit_begin,
+ .send_digit_end = zt_digit_end,
+ .send_text = zt_sendtext,
+ .call = zt_call,
+ .hangup = zt_hangup,
+ .answer = zt_answer,
+ .read = zt_read,
+ .write = zt_write,
+ .bridge = zt_bridge,
+ .exception = zt_exception,
+ .indicate = zt_indicate,
+ .fixup = zt_fixup,
+ .setoption = zt_setoption,
+ .func_channel_read = zt_func_read,
+};
+
+#ifdef HAVE_PRI
+#define GET_CHANNEL(p) ((p)->bearer ? (p)->bearer->channel : p->channel)
+#else
+#define GET_CHANNEL(p) ((p)->channel)
+#endif
+
+struct zt_pvt *round_robin[32];
+
+#ifdef HAVE_PRI
+static inline int pri_grab(struct zt_pvt *pvt, struct zt_pri *pri)
+{
+ int res;
+ /* Grab the lock first */
+ do {
+ res = ast_mutex_trylock(&pri->lock);
+ if (res) {
+ ast_mutex_unlock(&pvt->lock);
+ /* Release the lock and try again */
+ usleep(1);
+ ast_mutex_lock(&pvt->lock);
+ }
+ } while (res);
+ /* Then break the poll */
+ pthread_kill(pri->master, SIGURG);
+ return 0;
+}
+#endif
+
+#ifdef HAVE_SS7
+static inline void ss7_rel(struct zt_ss7 *ss7)
+{
+ ast_mutex_unlock(&ss7->lock);
+}
+
+static inline int ss7_grab(struct zt_pvt *pvt, struct zt_ss7 *pri)
+{
+ int res;
+ /* Grab the lock first */
+ do {
+ res = ast_mutex_trylock(&pri->lock);
+ if (res) {
+ ast_mutex_unlock(&pvt->lock);
+ /* Release the lock and try again */
+ usleep(1);
+ ast_mutex_lock(&pvt->lock);
+ }
+ } while (res);
+ /* Then break the poll */
+ pthread_kill(pri->master, SIGURG);
+ return 0;
+}
+#endif
+#define NUM_CADENCE_MAX 25
+static int num_cadence = 4;
+static int user_has_defined_cadences = 0;
+
+static struct zt_ring_cadence cadences[NUM_CADENCE_MAX] = {
+ { { 125, 125, 2000, 4000 } }, /*!< Quick chirp followed by normal ring */
+ { { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /*!< British style ring */
+ { { 125, 125, 125, 125, 125, 4000 } }, /*!< Three short bursts */
+ { { 1000, 500, 2500, 5000 } }, /*!< Long ring */
+};
+
+/*! \brief cidrings says in which pause to transmit the cid information, where the first pause
+ * is 1, the second pause is 2 and so on.
+ */
+
+static int cidrings[NUM_CADENCE_MAX] = {
+ 2, /*!< Right after first long ring */
+ 4, /*!< Right after long part */
+ 3, /*!< After third chirp */
+ 2, /*!< Second spell */
+};
+
+#define ISTRUNK(p) ((p->sig == SIG_FXSLS) || (p->sig == SIG_FXSKS) || \
+ (p->sig == SIG_FXSGS) || (p->sig == SIG_PRI))
+
+#define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */)
+#define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */)
+
+static int zt_get_index(struct ast_channel *ast, struct zt_pvt *p, int nullok)
+{
+ int res;
+ if (p->subs[0].owner == ast)
+ res = 0;
+ else if (p->subs[1].owner == ast)
+ res = 1;
+ else if (p->subs[2].owner == ast)
+ res = 2;
+ else {
+ res = -1;
+ if (!nullok)
+ ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n");
+ }
+ return res;
+}
+
+#ifdef HAVE_PRI
+static void wakeup_sub(struct zt_pvt *p, int a, struct zt_pri *pri)
+#else
+static void wakeup_sub(struct zt_pvt *p, int a, void *pri)
+#endif
+{
+#ifdef HAVE_PRI
+ if (pri)
+ ast_mutex_unlock(&pri->lock);
+#endif
+ for (;;) {
+ if (p->subs[a].owner) {
+ if (ast_channel_trylock(p->subs[a].owner)) {
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ } else {
+ ast_queue_frame(p->subs[a].owner, &ast_null_frame);
+ ast_channel_unlock(p->subs[a].owner);
+ break;
+ }
+ } else
+ break;
+ }
+#ifdef HAVE_PRI
+ if (pri)
+ ast_mutex_lock(&pri->lock);
+#endif
+}
+
+static void zap_queue_frame(struct zt_pvt *p, struct ast_frame *f, void *data)
+{
+#ifdef HAVE_PRI
+ struct zt_pri *pri = (struct zt_pri*) data;
+#endif
+#ifdef HAVE_SS7
+ struct zt_ss7 *ss7 = (struct zt_ss7*) data;
+#endif
+ /* We must unlock the PRI to avoid the possibility of a deadlock */
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ if (data) {
+ switch (p->sig) {
+#ifdef HAVE_PRI
+ case SIG_BRI:
+ case SIG_BRI_PTMP:
+ case SIG_PRI:
+ ast_mutex_unlock(&pri->lock);
+ break;
+#endif
+#ifdef HAVE_SS7
+ case SIG_SS7:
+ ast_mutex_unlock(&ss7->lock);
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+#endif
+ for (;;) {
+ if (p->owner) {
+ if (ast_channel_trylock(p->owner)) {
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ } else {
+ ast_queue_frame(p->owner, f);
+ ast_channel_unlock(p->owner);
+ break;
+ }
+ } else
+ break;
+ }
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ if (data) {
+ switch (p->sig) {
+#ifdef HAVE_PRI
+ case SIG_BRI:
+ case SIG_BRI_PTMP:
+ case SIG_PRI:
+ ast_mutex_lock(&pri->lock);
+ break;
+#endif
+#ifdef HAVE_SS7
+ case SIG_SS7:
+ ast_mutex_lock(&ss7->lock);
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+#endif
+}
+
+static int restore_gains(struct zt_pvt *p);
+
+static void swap_subs(struct zt_pvt *p, int a, int b)
+{
+ int tchan;
+ int tinthreeway;
+ struct ast_channel *towner;
+
+ ast_debug(1, "Swapping %d and %d\n", a, b);
+
+ tchan = p->subs[a].chan;
+ towner = p->subs[a].owner;
+ tinthreeway = p->subs[a].inthreeway;
+
+ p->subs[a].chan = p->subs[b].chan;
+ p->subs[a].owner = p->subs[b].owner;
+ p->subs[a].inthreeway = p->subs[b].inthreeway;
+
+ p->subs[b].chan = tchan;
+ p->subs[b].owner = towner;
+ p->subs[b].inthreeway = tinthreeway;
+
+ if (p->subs[a].owner)
+ ast_channel_set_fd(p->subs[a].owner, 0, p->subs[a].zfd);
+ if (p->subs[b].owner)
+ ast_channel_set_fd(p->subs[b].owner, 0, p->subs[b].zfd);
+ wakeup_sub(p, a, NULL);
+ wakeup_sub(p, b, NULL);
+}
+
+static int zt_open(char *fn)
+{
+ int fd;
+ int isnum;
+ int chan = 0;
+ int bs;
+ int x;
+ isnum = 1;
+ for (x = 0; x < strlen(fn); x++) {
+ if (!isdigit(fn[x])) {
+ isnum = 0;
+ break;
+ }
+ }
+ if (isnum) {
+ chan = atoi(fn);
+ if (chan < 1) {
+ ast_log(LOG_WARNING, "Invalid channel number '%s'\n", fn);
+ return -1;
+ }
+ fn = "/dev/zap/channel";
+ }
+ fd = open(fn, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open '%s': %s\n", fn, strerror(errno));
+ return -1;
+ }
+ if (chan) {
+ if (ioctl(fd, ZT_SPECIFY, &chan)) {
+ x = errno;
+ close(fd);
+ errno = x;
+ ast_log(LOG_WARNING, "Unable to specify channel %d: %s\n", chan, strerror(errno));
+ return -1;
+ }
+ }
+ bs = READ_SIZE;
+ if (ioctl(fd, ZT_SET_BLOCKSIZE, &bs) == -1) {
+ ast_log(LOG_WARNING, "Unable to set blocksize '%d': %s\n", bs, strerror(errno));
+ x = errno;
+ close(fd);
+ errno = x;
+ return -1;
+ }
+ return fd;
+}
+
+static void zt_close(int fd)
+{
+ if (fd > 0)
+ close(fd);
+}
+
+static int zt_setlinear(int zfd, int linear)
+{
+ int res;
+ res = ioctl(zfd, ZT_SETLINEAR, &linear);
+ if (res)
+ return res;
+ return 0;
+}
+
+
+static int alloc_sub(struct zt_pvt *p, int x)
+{
+ ZT_BUFFERINFO bi;
+ int res;
+ if (p->subs[x].zfd < 0) {
+ p->subs[x].zfd = zt_open("/dev/zap/pseudo");
+ if (p->subs[x].zfd > -1) {
+ res = ioctl(p->subs[x].zfd, ZT_GET_BUFINFO, &bi);
+ if (!res) {
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = numbufs;
+ res = ioctl(p->subs[x].zfd, ZT_SET_BUFINFO, &bi);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set buffer policy on channel %d\n", x);
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d\n", x);
+ if (ioctl(p->subs[x].zfd, ZT_CHANNO, &p->subs[x].chan) == 1) {
+ ast_log(LOG_WARNING, "Unable to get channel number for pseudo channel on FD %d\n", p->subs[x].zfd);
+ zt_close(p->subs[x].zfd);
+ p->subs[x].zfd = -1;
+ return -1;
+ }
+ ast_debug(1, "Allocated %s subchannel on FD %d channel %d\n", subnames[x], p->subs[x].zfd, p->subs[x].chan);
+ return 0;
+ } else
+ ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+ return -1;
+ }
+ ast_log(LOG_WARNING, "%s subchannel of %d already in use\n", subnames[x], p->channel);
+ return -1;
+}
+
+static int unalloc_sub(struct zt_pvt *p, int x)
+{
+ if (!x) {
+ ast_log(LOG_WARNING, "Trying to unalloc the real channel %d?!?\n", p->channel);
+ return -1;
+ }
+ ast_debug(1, "Released sub %d of channel %d\n", x, p->channel);
+ if (p->subs[x].zfd > -1) {
+ zt_close(p->subs[x].zfd);
+ }
+ p->subs[x].zfd = -1;
+ p->subs[x].linear = 0;
+ p->subs[x].chan = 0;
+ p->subs[x].owner = NULL;
+ p->subs[x].inthreeway = 0;
+ p->polarity = POLARITY_IDLE;
+ memset(&p->subs[x].curconf, 0, sizeof(p->subs[x].curconf));
+ return 0;
+}
+
+static int digit_to_dtmfindex(char digit)
+{
+ if (isdigit(digit))
+ return ZT_TONE_DTMF_BASE + (digit - '0');
+ else if (digit >= 'A' && digit <= 'D')
+ return ZT_TONE_DTMF_A + (digit - 'A');
+ else if (digit >= 'a' && digit <= 'd')
+ return ZT_TONE_DTMF_A + (digit - 'a');
+ else if (digit == '*')
+ return ZT_TONE_DTMF_s;
+ else if (digit == '#')
+ return ZT_TONE_DTMF_p;
+ else
+ return -1;
+}
+
+static int zt_digit_begin(struct ast_channel *chan, char digit)
+{
+ struct zt_pvt *pvt;
+ int index;
+ int dtmf = -1;
+
+ pvt = chan->tech_pvt;
+
+ ast_mutex_lock(&pvt->lock);
+
+ index = zt_get_index(chan, pvt, 0);
+
+ if ((index != SUB_REAL) || !pvt->owner)
+ goto out;
+
+#ifdef HAVE_PRI
+ if (((pvt->sig == SIG_PRI) || (pvt->sig == SIG_BRI) || (pvt->sig == SIG_BRI_PTMP))
+ && (chan->_state == AST_STATE_DIALING) && !pvt->proceeding) {
+ if (pvt->setup_ack) {
+ if (!pri_grab(pvt, pvt->pri)) {
+ pri_information(pvt->pri->pri, pvt->call, digit);
+ pri_rel(pvt->pri);
+ } else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", pvt->span);
+ } else if (strlen(pvt->dialdest) < sizeof(pvt->dialdest) - 1) {
+ int res;
+ ast_debug(1, "Queueing digit '%c' since setup_ack not yet received\n", digit);
+ res = strlen(pvt->dialdest);
+ pvt->dialdest[res++] = digit;
+ pvt->dialdest[res] = '\0';
+ }
+ goto out;
+ }
+#endif
+ if ((dtmf = digit_to_dtmfindex(digit)) == -1)
+ goto out;
+
+ if (pvt->pulse || ioctl(pvt->subs[SUB_REAL].zfd, ZT_SENDTONE, &dtmf)) {
+ int res;
+ ZT_DIAL_OPERATION zo = {
+ .op = ZT_DIAL_OP_APPEND,
+ };
+
+ zo.dialstr[0] = 'T';
+ zo.dialstr[1] = digit;
+ zo.dialstr[2] = '\0';
+ if ((res = ioctl(pvt->subs[SUB_REAL].zfd, ZT_DIAL, &zo)))
+ ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit);
+ else
+ pvt->dialing = 1;
+ } else {
+ ast_debug(1, "Started VLDTMF digit '%c'\n", digit);
+ pvt->dialing = 1;
+ pvt->begindigit = digit;
+ }
+
+out:
+ ast_mutex_unlock(&pvt->lock);
+
+ return 0;
+}
+
+static int zt_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
+{
+ struct zt_pvt *pvt;
+ int res = 0;
+ int index;
+ int x;
+
+ pvt = chan->tech_pvt;
+
+ ast_mutex_lock(&pvt->lock);
+
+ index = zt_get_index(chan, pvt, 0);
+
+ if ((index != SUB_REAL) || !pvt->owner || pvt->pulse)
+ goto out;
+
+#ifdef HAVE_PRI
+ /* This means that the digit was already sent via PRI signalling */
+ if (((pvt->sig == SIG_PRI) || (pvt->sig == SIG_BRI) || (pvt->sig == SIG_BRI_PTMP))
+ && !pvt->begindigit)
+ goto out;
+#endif
+
+ if (pvt->begindigit) {
+ x = -1;
+ ast_debug(1, "Ending VLDTMF digit '%c'\n", digit);
+ res = ioctl(pvt->subs[SUB_REAL].zfd, ZT_SENDTONE, &x);
+ pvt->dialing = 0;
+ pvt->begindigit = 0;
+ }
+
+out:
+ ast_mutex_unlock(&pvt->lock);
+
+ return res;
+}
+
+static char *events[] = {
+ "No event",
+ "On hook",
+ "Ring/Answered",
+ "Wink/Flash",
+ "Alarm",
+ "No more alarm",
+ "HDLC Abort",
+ "HDLC Overrun",
+ "HDLC Bad FCS",
+ "Dial Complete",
+ "Ringer On",
+ "Ringer Off",
+ "Hook Transition Complete",
+ "Bits Changed",
+ "Pulse Start",
+ "Timer Expired",
+ "Timer Ping",
+ "Polarity Reversal",
+ "Ring Begin",
+};
+
+static struct {
+ int alarm;
+ char *name;
+} alarms[] = {
+ { ZT_ALARM_RED, "Red Alarm" },
+ { ZT_ALARM_YELLOW, "Yellow Alarm" },
+ { ZT_ALARM_BLUE, "Blue Alarm" },
+ { ZT_ALARM_RECOVER, "Recovering" },
+ { ZT_ALARM_LOOPBACK, "Loopback" },
+ { ZT_ALARM_NOTOPEN, "Not Open" },
+ { ZT_ALARM_NONE, "None" },
+};
+
+static char *alarm2str(int alarm)
+{
+ int x;
+ for (x = 0; x < sizeof(alarms) / sizeof(alarms[0]); x++) {
+ if (alarms[x].alarm & alarm)
+ return alarms[x].name;
+ }
+ return alarm ? "Unknown Alarm" : "No Alarm";
+}
+
+static char *event2str(int event)
+{
+ static char buf[256];
+ if ((event < (sizeof(events) / sizeof(events[0]))) && (event > -1))
+ return events[event];
+ sprintf(buf, "Event %d", event); /* safe */
+ return buf;
+}
+
+#ifdef HAVE_PRI
+static char *dialplan2str(int dialplan)
+{
+ if (dialplan == -1 || dialplan == -2) {
+ return("Dynamically set dialplan in ISDN");
+ }
+ return (pri_plan2str(dialplan));
+}
+#endif
+
+static char *zap_sig2str(int sig)
+{
+ static char buf[256];
+ switch (sig) {
+ case SIG_EM:
+ return "E & M Immediate";
+ case SIG_EMWINK:
+ return "E & M Wink";
+ case SIG_EM_E1:
+ return "E & M E1";
+ case SIG_FEATD:
+ return "Feature Group D (DTMF)";
+ case SIG_FEATDMF:
+ return "Feature Group D (MF)";
+ case SIG_FEATDMF_TA:
+ return "Feature Groud D (MF) Tandem Access";
+ case SIG_FEATB:
+ return "Feature Group B (MF)";
+ case SIG_E911:
+ return "E911 (MF)";
+ case SIG_FGC_CAMA:
+ return "FGC/CAMA (Dialpulse)";
+ case SIG_FGC_CAMAMF:
+ return "FGC/CAMA (MF)";
+ case SIG_FXSLS:
+ return "FXS Loopstart";
+ case SIG_FXSGS:
+ return "FXS Groundstart";
+ case SIG_FXSKS:
+ return "FXS Kewlstart";
+ case SIG_FXOLS:
+ return "FXO Loopstart";
+ case SIG_FXOGS:
+ return "FXO Groundstart";
+ case SIG_FXOKS:
+ return "FXO Kewlstart";
+ case SIG_PRI:
+ return "ISDN PRI";
+ case SIG_BRI:
+ return "ISDN BRI Point to Point";
+ case SIG_BRI_PTMP:
+ return "ISDN BRI Point to MultiPoint";
+ case SIG_SS7:
+ return "SS7";
+ case SIG_SF:
+ return "SF (Tone) Immediate";
+ case SIG_SFWINK:
+ return "SF (Tone) Wink";
+ case SIG_SF_FEATD:
+ return "SF (Tone) with Feature Group D (DTMF)";
+ case SIG_SF_FEATDMF:
+ return "SF (Tone) with Feature Group D (MF)";
+ case SIG_SF_FEATB:
+ return "SF (Tone) with Feature Group B (MF)";
+ case SIG_GR303FXOKS:
+ return "GR-303 with FXOKS";
+ case SIG_GR303FXSKS:
+ return "GR-303 with FXSKS";
+ case 0:
+ return "Pseudo";
+ default:
+ snprintf(buf, sizeof(buf), "Unknown signalling %d", sig);
+ return buf;
+ }
+}
+
+#define sig2str zap_sig2str
+
+static int conf_add(struct zt_pvt *p, struct zt_subchannel *c, int index, int slavechannel)
+{
+ /* If the conference already exists, and we're already in it
+ don't bother doing anything */
+ ZT_CONFINFO zi;
+
+ memset(&zi, 0, sizeof(zi));
+ zi.chan = 0;
+
+ if (slavechannel > 0) {
+ /* If we have only one slave, do a digital mon */
+ zi.confmode = ZT_CONF_DIGITALMON;
+ zi.confno = slavechannel;
+ } else {
+ if (!index) {
+ /* Real-side and pseudo-side both participate in conference */
+ zi.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER |
+ ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
+ } else
+ zi.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+ zi.confno = p->confno;
+ }
+ if ((zi.confno == c->curconf.confno) && (zi.confmode == c->curconf.confmode))
+ return 0;
+ if (c->zfd < 0)
+ return 0;
+ if (ioctl(c->zfd, ZT_SETCONF, &zi)) {
+ ast_log(LOG_WARNING, "Failed to add %d to conference %d/%d\n", c->zfd, zi.confmode, zi.confno);
+ return -1;
+ }
+ if (slavechannel < 1) {
+ p->confno = zi.confno;
+ }
+ memcpy(&c->curconf, &zi, sizeof(c->curconf));
+ ast_debug(1, "Added %d to conference %d/%d\n", c->zfd, c->curconf.confmode, c->curconf.confno);
+ return 0;
+}
+
+static int isourconf(struct zt_pvt *p, struct zt_subchannel *c)
+{
+ /* If they're listening to our channel, they're ours */
+ if ((p->channel == c->curconf.confno) && (c->curconf.confmode == ZT_CONF_DIGITALMON))
+ return 1;
+ /* If they're a talker on our (allocated) conference, they're ours */
+ if ((p->confno > 0) && (p->confno == c->curconf.confno) && (c->curconf.confmode & ZT_CONF_TALKER))
+ return 1;
+ return 0;
+}
+
+static int conf_del(struct zt_pvt *p, struct zt_subchannel *c, int index)
+{
+ ZT_CONFINFO zi;
+ if (/* Can't delete if there's no zfd */
+ (c->zfd < 0) ||
+ /* Don't delete from the conference if it's not our conference */
+ !isourconf(p, c)
+ /* Don't delete if we don't think it's conferenced at all (implied) */
+ ) return 0;
+ memset(&zi, 0, sizeof(zi));
+ zi.chan = 0;
+ zi.confno = 0;
+ zi.confmode = 0;
+ if (ioctl(c->zfd, ZT_SETCONF, &zi)) {
+ ast_log(LOG_WARNING, "Failed to drop %d from conference %d/%d\n", c->zfd, c->curconf.confmode, c->curconf.confno);
+ return -1;
+ }
+ ast_debug(1, "Removed %d from conference %d/%d\n", c->zfd, c->curconf.confmode, c->curconf.confno);
+ memcpy(&c->curconf, &zi, sizeof(c->curconf));
+ return 0;
+}
+
+static int isslavenative(struct zt_pvt *p, struct zt_pvt **out)
+{
+ int x;
+ int useslavenative;
+ struct zt_pvt *slave = NULL;
+ /* Start out optimistic */
+ useslavenative = 1;
+ /* Update conference state in a stateless fashion */
+ for (x = 0; x < 3; x++) {
+ /* Any three-way calling makes slave native mode *definitely* out
+ of the question */
+ if ((p->subs[x].zfd > -1) && p->subs[x].inthreeway)
+ useslavenative = 0;
+ }
+ /* If we don't have any 3-way calls, check to see if we have
+ precisely one slave */
+ if (useslavenative) {
+ for (x = 0; x < MAX_SLAVES; x++) {
+ if (p->slaves[x]) {
+ if (slave) {
+ /* Whoops already have a slave! No
+ slave native and stop right away */
+ slave = NULL;
+ useslavenative = 0;
+ break;
+ } else {
+ /* We have one slave so far */
+ slave = p->slaves[x];
+ }
+ }
+ }
+ }
+ /* If no slave, slave native definitely out */
+ if (!slave)
+ useslavenative = 0;
+ else if (slave->law != p->law) {
+ useslavenative = 0;
+ slave = NULL;
+ }
+ if (out)
+ *out = slave;
+ return useslavenative;
+}
+
+static int reset_conf(struct zt_pvt *p)
+{
+ ZT_CONFINFO zi;
+ memset(&zi, 0, sizeof(zi));
+ p->confno = -1;
+ memset(&p->subs[SUB_REAL].curconf, 0, sizeof(p->subs[SUB_REAL].curconf));
+ if (p->subs[SUB_REAL].zfd > -1) {
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_SETCONF, &zi))
+ ast_log(LOG_WARNING, "Failed to reset conferencing on channel %d!\n", p->channel);
+ }
+ return 0;
+}
+
+static int update_conf(struct zt_pvt *p)
+{
+ int needconf = 0;
+ int x;
+ int useslavenative;
+ struct zt_pvt *slave = NULL;
+
+ useslavenative = isslavenative(p, &slave);
+ /* Start with the obvious, general stuff */
+ for (x = 0; x < 3; x++) {
+ /* Look for three way calls */
+ if ((p->subs[x].zfd > -1) && p->subs[x].inthreeway) {
+ conf_add(p, &p->subs[x], x, 0);
+ needconf++;
+ } else {
+ conf_del(p, &p->subs[x], x);
+ }
+ }
+ /* If we have a slave, add him to our conference now. or DAX
+ if this is slave native */
+ for (x = 0; x < MAX_SLAVES; x++) {
+ if (p->slaves[x]) {
+ if (useslavenative)
+ conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p));
+ else {
+ conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, 0);
+ needconf++;
+ }
+ }
+ }
+ /* If we're supposed to be in there, do so now */
+ if (p->inconference && !p->subs[SUB_REAL].inthreeway) {
+ if (useslavenative)
+ conf_add(p, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(slave));
+ else {
+ conf_add(p, &p->subs[SUB_REAL], SUB_REAL, 0);
+ needconf++;
+ }
+ }
+ /* If we have a master, add ourselves to his conference */
+ if (p->master) {
+ if (isslavenative(p->master, NULL)) {
+ conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p->master));
+ } else {
+ conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, 0);
+ }
+ }
+ if (!needconf) {
+ /* Nobody is left (or should be left) in our conference.
+ Kill it. */
+ p->confno = -1;
+ }
+ ast_debug(1, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf);
+ return 0;
+}
+
+static void zt_enable_ec(struct zt_pvt *p)
+{
+ int x;
+ int res;
+ if (!p)
+ return;
+ if (p->echocanon) {
+ ast_debug(1, "Echo cancellation already on\n");
+ return;
+ }
+ if (p->digital) {
+ ast_debug(1, "Echo cancellation isn't required on digital connection\n");
+ return;
+ }
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+ if (p->echocancel.head.tap_length) {
+#else
+ if (p->echocancel) {
+#endif
+ if ((p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP) || (p->sig == SIG_PRI) || (p->sig == SIG_SS7)) {
+ x = 1;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &x);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to enable audio mode on channel %d (%s)\n", p->channel, strerror(errno));
+ }
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOCANCEL_PARAMS, &p->echocancel);
+#else
+ x = p->echocancel;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOCANCEL, &x);
+#endif
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to enable echo cancellation on channel %d (%s)\n", p->channel, strerror(errno));
+ } else {
+ p->echocanon = 1;
+ ast_debug(1, "Enabled echo cancellation on channel %d\n", p->channel);
+ }
+ } else
+ ast_debug(1, "No echo cancellation requested\n");
+}
+
+static void zt_train_ec(struct zt_pvt *p)
+{
+ int x;
+ int res;
+
+ if (p && p->echocanon && p->echotraining) {
+ x = p->echotraining;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOTRAIN, &x);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to request echo training on channel %d\n", p->channel);
+ else
+ ast_debug(1, "Engaged echo training on channel %d\n", p->channel);
+ } else {
+ ast_debug(1, "No echo training requested\n");
+ }
+}
+
+static void zt_disable_ec(struct zt_pvt *p)
+{
+ int res;
+
+ if (p->echocanon) {
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+ struct zt_echocanparams ecp = { .tap_length = 0 };
+
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOCANCEL_PARAMS, &ecp);
+#else
+ int x = 0;
+
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOCANCEL, &x);
+#endif
+
+ if (res)
+ ast_log(LOG_WARNING, "Unable to disable echo cancellation on channel %d\n", p->channel);
+ else
+ ast_debug(1, "Disabled echo cancellation on channel %d\n", p->channel);
+ }
+
+ p->echocanon = 0;
+}
+
+static void fill_txgain(struct zt_gains *g, float gain, int law)
+{
+ int j;
+ int k;
+ float linear_gain = pow(10.0, gain / 20.0);
+
+ switch (law) {
+ case ZT_LAW_ALAW:
+ for (j = 0; j < (sizeof(g->txgain) / sizeof(g->txgain[0])); j++) {
+ if (gain) {
+ k = (int) (((float) AST_ALAW(j)) * linear_gain);
+ if (k > 32767) k = 32767;
+ if (k < -32767) k = -32767;
+ g->txgain[j] = AST_LIN2A(k);
+ } else {
+ g->txgain[j] = j;
+ }
+ }
+ break;
+ case ZT_LAW_MULAW:
+ for (j = 0; j < (sizeof(g->txgain) / sizeof(g->txgain[0])); j++) {
+ if (gain) {
+ k = (int) (((float) AST_MULAW(j)) * linear_gain);
+ if (k > 32767) k = 32767;
+ if (k < -32767) k = -32767;
+ g->txgain[j] = AST_LIN2MU(k);
+ } else {
+ g->txgain[j] = j;
+ }
+ }
+ break;
+ }
+}
+
+static void fill_rxgain(struct zt_gains *g, float gain, int law)
+{
+ int j;
+ int k;
+ float linear_gain = pow(10.0, gain / 20.0);
+
+ switch (law) {
+ case ZT_LAW_ALAW:
+ for (j = 0; j < (sizeof(g->rxgain) / sizeof(g->rxgain[0])); j++) {
+ if (gain) {
+ k = (int) (((float) AST_ALAW(j)) * linear_gain);
+ if (k > 32767) k = 32767;
+ if (k < -32767) k = -32767;
+ g->rxgain[j] = AST_LIN2A(k);
+ } else {
+ g->rxgain[j] = j;
+ }
+ }
+ break;
+ case ZT_LAW_MULAW:
+ for (j = 0; j < (sizeof(g->rxgain) / sizeof(g->rxgain[0])); j++) {
+ if (gain) {
+ k = (int) (((float) AST_MULAW(j)) * linear_gain);
+ if (k > 32767) k = 32767;
+ if (k < -32767) k = -32767;
+ g->rxgain[j] = AST_LIN2MU(k);
+ } else {
+ g->rxgain[j] = j;
+ }
+ }
+ break;
+ }
+}
+
+static int set_actual_txgain(int fd, int chan, float gain, int law)
+{
+ struct zt_gains g;
+ int res;
+
+ memset(&g, 0, sizeof(g));
+ g.chan = chan;
+ res = ioctl(fd, ZT_GETGAINS, &g);
+ if (res) {
+ ast_debug(1, "Failed to read gains: %s\n", strerror(errno));
+ return res;
+ }
+
+ fill_txgain(&g, gain, law);
+
+ return ioctl(fd, ZT_SETGAINS, &g);
+}
+
+static int set_actual_rxgain(int fd, int chan, float gain, int law)
+{
+ struct zt_gains g;
+ int res;
+
+ memset(&g, 0, sizeof(g));
+ g.chan = chan;
+ res = ioctl(fd, ZT_GETGAINS, &g);
+ if (res) {
+ ast_debug(1, "Failed to read gains: %s\n", strerror(errno));
+ return res;
+ }
+
+ fill_rxgain(&g, gain, law);
+
+ return ioctl(fd, ZT_SETGAINS, &g);
+}
+
+static int set_actual_gain(int fd, int chan, float rxgain, float txgain, int law)
+{
+ return set_actual_txgain(fd, chan, txgain, law) | set_actual_rxgain(fd, chan, rxgain, law);
+}
+
+static int bump_gains(struct zt_pvt *p)
+{
+ int res;
+
+ /* Bump receive gain by value stored in cid_rxgain */
+ res = set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain + p->cid_rxgain, p->txgain, p->law);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to bump gain: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int restore_gains(struct zt_pvt *p)
+{
+ int res;
+
+ res = set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to restore gains: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline int zt_set_hook(int fd, int hs)
+{
+ int x, res;
+
+ x = hs;
+ res = ioctl(fd, ZT_HOOK, &x);
+
+ if (res < 0) {
+ if (errno == EINPROGRESS)
+ return 0;
+ ast_log(LOG_WARNING, "zt hook failed: %s\n", strerror(errno));
+ }
+
+ return res;
+}
+
+static inline int zt_confmute(struct zt_pvt *p, int muted)
+{
+ int x, y, res;
+ x = muted;
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
+ y = 1;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &y);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set audio mode on '%d'\n", p->channel);
+ }
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_CONFMUTE, &x);
+ if (res < 0)
+ ast_log(LOG_WARNING, "zt confmute(%d) failed on channel %d: %s\n", muted, p->channel, strerror(errno));
+ return res;
+}
+
+static int save_conference(struct zt_pvt *p)
+{
+ struct zt_confinfo c;
+ int res;
+ if (p->saveconf.confmode) {
+ ast_log(LOG_WARNING, "Can't save conference -- already in use\n");
+ return -1;
+ }
+ p->saveconf.chan = 0;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_GETCONF, &p->saveconf);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to get conference info: %s\n", strerror(errno));
+ p->saveconf.confmode = 0;
+ return -1;
+ }
+ c.chan = 0;
+ c.confno = 0;
+ c.confmode = ZT_CONF_NORMAL;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_SETCONF, &c);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to set conference info: %s\n", strerror(errno));
+ return -1;
+ }
+ ast_debug(1, "Disabled conferencing\n");
+ return 0;
+}
+
+/*!
+ * \brief Send MWI state change
+ *
+ * \arg mailbox_full This is the mailbox associated with the FXO line that the
+ * MWI state has changed on.
+ * \arg thereornot This argument should simply be set to 1 or 0, to indicate
+ * whether there are messages waiting or not.
+ *
+ * \return nothing
+ *
+ * This function does two things:
+ *
+ * 1) It generates an internal Asterisk event notifying any other module that
+ * cares about MWI that the state of a mailbox has changed.
+ *
+ * 2) It runs the script specified by the mwimonitornotify option to allow
+ * some custom handling of the state change.
+ */
+static void notify_message(char *mailbox_full, int thereornot)
+{
+ char s[sizeof(mwimonitornotify) + 80];
+ struct ast_event *event;
+ char *mailbox, *context;
+
+ /* Strip off @default */
+ context = mailbox = ast_strdupa(mailbox_full);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ if (!(event = ast_event_new(AST_EVENT_MWI,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot,
+ AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot,
+ AST_EVENT_IE_END))) {
+ return;
+ }
+
+ ast_event_queue_and_cache(event,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
+ AST_EVENT_IE_END);
+
+ if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) {
+ snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot);
+ ast_safe_system(s);
+ }
+}
+
+static int restore_conference(struct zt_pvt *p)
+{
+ int res;
+ if (p->saveconf.confmode) {
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_SETCONF, &p->saveconf);
+ p->saveconf.confmode = 0;
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+ ast_debug(1, "Restored conferencing\n");
+ return 0;
+}
+
+static int send_callerid(struct zt_pvt *p);
+
+static int send_cwcidspill(struct zt_pvt *p)
+{
+ p->callwaitcas = 0;
+ p->cidcwexpire = 0;
+ if (!(p->cidspill = ast_malloc(MAX_CALLERID_SIZE)))
+ return -1;
+ p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, p->callwait_name, p->callwait_num, AST_LAW(p));
+ /* Make sure we account for the end */
+ p->cidlen += READ_SIZE * 4;
+ p->cidpos = 0;
+ send_callerid(p);
+ ast_verb(3, "CPE supports Call Waiting Caller*ID. Sending '%s/%s'\n", p->callwait_name, p->callwait_num);
+ return 0;
+}
+
+static int has_voicemail(struct zt_pvt *p)
+{
+ int new_msgs;
+ struct ast_event *event;
+ char *mailbox, *context;
+
+ mailbox = context = ast_strdupa(p->mailbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ event = ast_event_get_cached(AST_EVENT_MWI,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_END);
+
+ if (event) {
+ new_msgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+ ast_event_destroy(event);
+ } else
+ new_msgs = ast_app_has_voicemail(p->mailbox, NULL);
+
+ return new_msgs;
+}
+
+static int send_callerid(struct zt_pvt *p)
+{
+ /* Assumes spill in p->cidspill, p->cidlen in length and we're p->cidpos into it */
+ int res;
+ /* Take out of linear mode if necessary */
+ if (p->subs[SUB_REAL].linear) {
+ p->subs[SUB_REAL].linear = 0;
+ zt_setlinear(p->subs[SUB_REAL].zfd, 0);
+ }
+ while (p->cidpos < p->cidlen) {
+ res = write(p->subs[SUB_REAL].zfd, p->cidspill + p->cidpos, p->cidlen - p->cidpos);
+ if (res < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ else {
+ ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+ if (!res)
+ return 0;
+ p->cidpos += res;
+ }
+ ast_free(p->cidspill);
+ p->cidspill = NULL;
+ if (p->callwaitcas) {
+ /* Wait for CID/CW to expire */
+ p->cidcwexpire = CIDCW_EXPIRE_SAMPLES;
+ } else
+ restore_conference(p);
+ return 0;
+}
+
+static int zt_callwait(struct ast_channel *ast)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ p->callwaitingrepeat = CALLWAITING_REPEAT_SAMPLES;
+ if (p->cidspill) {
+ ast_log(LOG_WARNING, "Spill already exists?!?\n");
+ ast_free(p->cidspill);
+ }
+ if (!(p->cidspill = ast_malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4)))
+ return -1;
+ save_conference(p);
+ /* Silence */
+ memset(p->cidspill, 0x7f, 2400 + 600 + READ_SIZE * 4);
+ if (!p->callwaitrings && p->callwaitingcallerid) {
+ ast_gen_cas(p->cidspill, 1, 2400 + 680, AST_LAW(p));
+ p->callwaitcas = 1;
+ p->cidlen = 2400 + 680 + READ_SIZE * 4;
+ } else {
+ ast_gen_cas(p->cidspill, 1, 2400, AST_LAW(p));
+ p->callwaitcas = 0;
+ p->cidlen = 2400 + READ_SIZE * 4;
+ }
+ p->cidpos = 0;
+ send_callerid(p);
+
+ return 0;
+}
+
+#ifdef HAVE_SS7
+static unsigned char cid_pres2ss7pres(int cid_pres)
+{
+ return (cid_pres >> 5) & 0x03;
+}
+
+static unsigned char cid_pres2ss7screen(int cid_pres)
+{
+ return cid_pres & 0x03;
+}
+#endif
+
+static int zt_call(struct ast_channel *ast, char *rdest, int timeout)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ int x, res, index,mysig;
+ char *c, *n, *l;
+#ifdef HAVE_PRI
+ char *s = NULL;
+#endif
+ char dest[256]; /* must be same length as p->dialdest */
+ ast_mutex_lock(&p->lock);
+ ast_copy_string(dest, rdest, sizeof(dest));
+ ast_copy_string(p->dialdest, rdest, sizeof(p->dialdest));
+ if ((ast->_state == AST_STATE_BUSY)) {
+ p->subs[SUB_REAL].needbusy = 1;
+ ast_mutex_unlock(&p->lock);
+ return 0;
+ }
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "zt_call called on %s, neither down nor reserved\n", ast->name);
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ p->dialednone = 0;
+ if ((p->radio || (p->oprmode < 0))) /* if a radio channel, up immediately */
+ {
+ /* Special pseudo -- automatically up */
+ ast_setstate(ast, AST_STATE_UP);
+ ast_mutex_unlock(&p->lock);
+ return 0;
+ }
+ x = ZT_FLUSH_READ | ZT_FLUSH_WRITE;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_FLUSH, &x);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", p->channel);
+ p->outgoing = 1;
+
+ set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law);
+
+ mysig = p->sig;
+ if (p->outsigmod > -1)
+ mysig = p->outsigmod;
+
+ switch (mysig) {
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ if (p->owner == ast) {
+ /* Normal ring, on hook */
+
+ /* Don't send audio while on hook, until the call is answered */
+ p->dialing = 1;
+ if (p->use_callerid) {
+ /* Generate the Caller-ID spill if desired */
+ if (p->cidspill) {
+ ast_log(LOG_WARNING, "cidspill already exists??\n");
+ ast_free(p->cidspill);
+ }
+ p->callwaitcas = 0;
+ if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) {
+ p->cidlen = ast_callerid_generate(p->cidspill, ast->cid.cid_name, ast->cid.cid_num, AST_LAW(p));
+ p->cidpos = 0;
+ send_callerid(p);
+ }
+ }
+ /* Choose proper cadence */
+ if ((p->distinctivering > 0) && (p->distinctivering <= num_cadence)) {
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_SETCADENCE, &cadences[p->distinctivering - 1]))
+ ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s'\n", p->distinctivering, ast->name);
+ p->cidrings = cidrings[p->distinctivering - 1];
+ } else {
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_SETCADENCE, NULL))
+ ast_log(LOG_WARNING, "Unable to reset default ring on '%s'\n", ast->name);
+ p->cidrings = p->sendcalleridafter;
+ }
+
+ /* nick@dccinc.com 4/3/03 mods to allow for deferred dialing */
+ c = strchr(dest, '/');
+ if (c)
+ c++;
+ if (c && (strlen(c) < p->stripmsd)) {
+ ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+ c = NULL;
+ }
+ if (c) {
+ p->dop.op = ZT_DIAL_OP_REPLACE;
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "Tw%s", c);
+ ast_debug(1, "FXO: setup deferred dialstring: %s\n", c);
+ } else {
+ p->dop.dialstr[0] = '\0';
+ }
+ x = ZT_RING;
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x) && (errno != EINPROGRESS)) {
+ ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ p->dialing = 1;
+ } else {
+ /* Call waiting call */
+ p->callwaitrings = 0;
+ if (ast->cid.cid_num)
+ ast_copy_string(p->callwait_num, ast->cid.cid_num, sizeof(p->callwait_num));
+ else
+ p->callwait_num[0] = '\0';
+ if (ast->cid.cid_name)
+ ast_copy_string(p->callwait_name, ast->cid.cid_name, sizeof(p->callwait_name));
+ else
+ p->callwait_name[0] = '\0';
+ /* Call waiting tone instead */
+ if (zt_callwait(ast)) {
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ /* Make ring-back */
+ if (tone_zone_play_tone(p->subs[SUB_CALLWAIT].zfd, ZT_TONE_RINGTONE))
+ ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name);
+
+ }
+ n = ast->cid.cid_name;
+ l = ast->cid.cid_num;
+ if (l)
+ ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num));
+ else
+ p->lastcid_num[0] = '\0';
+ if (n)
+ ast_copy_string(p->lastcid_name, n, sizeof(p->lastcid_name));
+ else
+ p->lastcid_name[0] = '\0';
+ ast_setstate(ast, AST_STATE_RINGING);
+ index = zt_get_index(ast, p, 0);
+ if (index > -1) {
+ p->subs[index].needringing = 1;
+ }
+ break;
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+ case SIG_EMWINK:
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_FEATD:
+ case SIG_FEATDMF:
+ case SIG_E911:
+ case SIG_FGC_CAMA:
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ case SIG_SFWINK:
+ case SIG_SF:
+ case SIG_SF_FEATD:
+ case SIG_SF_FEATDMF:
+ case SIG_FEATDMF_TA:
+ case SIG_SF_FEATB:
+ c = strchr(dest, '/');
+ if (c)
+ c++;
+ else
+ c = "";
+ if (strlen(c) < p->stripmsd) {
+ ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+#ifdef HAVE_PRI
+ /* Start the trunk, if not GR-303 */
+ if (!p->pri) {
+#endif
+ x = ZT_START;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
+ if (res < 0) {
+ if (errno != EINPROGRESS) {
+ ast_log(LOG_WARNING, "Unable to start channel: %s\n", strerror(errno));
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ }
+#ifdef HAVE_PRI
+ }
+#endif
+ ast_debug(1, "Dialing '%s'\n", c);
+ p->dop.op = ZT_DIAL_OP_REPLACE;
+
+ c += p->stripmsd;
+
+ switch (mysig) {
+ case SIG_FEATD:
+ l = ast->cid.cid_num;
+ if (l)
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c);
+ else
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c);
+ break;
+ case SIG_FEATDMF:
+ l = ast->cid.cid_num;
+ if (l)
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c);
+ else
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*02#*%s#", c);
+ break;
+ case SIG_FEATDMF_TA:
+ {
+ const char *cic, *ozz;
+
+ /* If you have to go through a Tandem Access point you need to use this */
+ ozz = pbx_builtin_getvar_helper(p->owner, "FEATDMF_OZZ");
+ if (!ozz)
+ ozz = defaultozz;
+ cic = pbx_builtin_getvar_helper(p->owner, "FEATDMF_CIC");
+ if (!cic)
+ cic = defaultcic;
+ if (!ozz || !cic) {
+ ast_log(LOG_WARNING, "Unable to dial channel of type feature group D MF tandem access without CIC or OZZ set\n");
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s%s#", ozz, cic);
+ snprintf(p->finaldial, sizeof(p->finaldial), "M*%s#", c);
+ p->whichwink = 0;
+ }
+ break;
+ case SIG_E911:
+ ast_copy_string(p->dop.dialstr, "M*911#", sizeof(p->dop.dialstr));
+ break;
+ case SIG_FGC_CAMA:
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%s", c);
+ break;
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s#", c);
+ break;
+ default:
+ if (p->pulse)
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%sw", c);
+ else
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%sw", c);
+ break;
+ }
+
+ if (p->echotraining && (strlen(p->dop.dialstr) > 4)) {
+ memset(p->echorest, 'w', sizeof(p->echorest) - 1);
+ strcpy(p->echorest + (p->echotraining / 400) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
+ p->echorest[sizeof(p->echorest) - 1] = '\0';
+ p->echobreak = 1;
+ p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
+ } else
+ p->echobreak = 0;
+ if (!res) {
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop)) {
+ x = ZT_ONHOOK;
+ ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
+ ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(errno));
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ } else
+ ast_debug(1, "Deferring dialing...\n");
+
+ p->dialing = 1;
+ if (ast_strlen_zero(c))
+ p->dialednone = 1;
+ ast_setstate(ast, AST_STATE_DIALING);
+ break;
+ case 0:
+ /* Special pseudo -- automatically up*/
+ ast_setstate(ast, AST_STATE_UP);
+ break;
+ case SIG_PRI:
+ case SIG_BRI:
+ case SIG_BRI_PTMP:
+ case SIG_SS7:
+ /* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */
+ p->dialdest[0] = '\0';
+ break;
+ default:
+ ast_debug(1, "not yet implemented\n");
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+#ifdef HAVE_SS7
+ if (p->ss7) {
+ char ss7_called_nai;
+ int called_nai_strip;
+ char ss7_calling_nai;
+ int calling_nai_strip;
+ const char *charge_str = NULL;
+ const char *gen_address = NULL;
+ const char *gen_digits = NULL;
+ const char *gen_dig_type = NULL;
+ const char *gen_dig_scheme = NULL;
+ const char *jip_digits = NULL;
+ const char *lspi_ident = NULL;
+ const char *rlt_flag = NULL;
+ const char *call_ref_id = NULL;
+ const char *call_ref_pc = NULL;
+
+ c = strchr(dest, '/');
+ if (c)
+ c++;
+ else
+ c = dest;
+
+ if (!p->hidecallerid) {
+ l = ast->cid.cid_num;
+ } else {
+ l = NULL;
+ }
+
+ if (ss7_grab(p, p->ss7)) {
+ ast_log(LOG_WARNING, "Failed to grab SS7!\n");
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ p->digital = IS_DIGITAL(ast->transfercapability);
+ p->ss7call = isup_new_call(p->ss7->ss7);
+
+ if (!p->ss7call) {
+ ss7_rel(p->ss7);
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n");
+ return -1;
+ }
+
+ called_nai_strip = 0;
+ ss7_called_nai = p->ss7->called_nai;
+ if (ss7_called_nai == SS7_NAI_DYNAMIC) { /* compute dynamically */
+ if (strncmp(c + p->stripmsd, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
+ called_nai_strip = strlen(p->ss7->internationalprefix);
+ ss7_called_nai = SS7_NAI_INTERNATIONAL;
+ } else if (strncmp(c + p->stripmsd, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
+ called_nai_strip = strlen(p->ss7->nationalprefix);
+ ss7_called_nai = SS7_NAI_NATIONAL;
+ } else {
+ ss7_called_nai = SS7_NAI_SUBSCRIBER;
+ }
+ }
+ isup_set_called(p->ss7call, c + p->stripmsd + called_nai_strip, ss7_called_nai, p->ss7->ss7);
+
+ calling_nai_strip = 0;
+ ss7_calling_nai = p->ss7->calling_nai;
+ if ((l != NULL) && (ss7_calling_nai == SS7_NAI_DYNAMIC)) { /* compute dynamically */
+ if (strncmp(l, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
+ calling_nai_strip = strlen(p->ss7->internationalprefix);
+ ss7_calling_nai = SS7_NAI_INTERNATIONAL;
+ } else if (strncmp(l, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
+ calling_nai_strip = strlen(p->ss7->nationalprefix);
+ ss7_calling_nai = SS7_NAI_NATIONAL;
+ } else {
+ ss7_calling_nai = SS7_NAI_SUBSCRIBER;
+ }
+ }
+ isup_set_calling(p->ss7call, l ? (l + calling_nai_strip) : NULL, ss7_calling_nai,
+ p->use_callingpres ? cid_pres2ss7pres(ast->cid.cid_pres) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED),
+ p->use_callingpres ? cid_pres2ss7screen(ast->cid.cid_pres) : SS7_SCREENING_USER_PROVIDED );
+
+ isup_set_oli(p->ss7call, ast->cid.cid_ani2);
+ isup_init_call(p->ss7->ss7, p->ss7call, p->cic, p->dpc);
+
+ /* Set the charge number if it is set */
+ charge_str = pbx_builtin_getvar_helper(ast, "SS7_CHARGE_NUMBER");
+ if (charge_str)
+ isup_set_charge(p->ss7call, charge_str, SS7_ANI_CALLING_PARTY_SUB_NUMBER, 0x10);
+
+ gen_address = pbx_builtin_getvar_helper(ast, "SS7_GENERIC_ADDRESS");
+ if (gen_address)
+ isup_set_gen_address(p->ss7call, gen_address, p->gen_add_nai,p->gen_add_pres_ind, p->gen_add_num_plan,p->gen_add_type); /* need to add some types here for NAI,PRES,TYPE */
+
+ gen_digits = pbx_builtin_getvar_helper(ast, "SS7_GENERIC_DIGITS");
+ gen_dig_type = pbx_builtin_getvar_helper(ast, "SS7_GENERIC_DIGTYPE");
+ gen_dig_scheme = pbx_builtin_getvar_helper(ast, "SS7_GENERIC_DIGSCHEME");
+ if (gen_digits)
+ isup_set_gen_digits(p->ss7call, gen_digits, atoi(gen_dig_type), atoi(gen_dig_scheme));
+
+ jip_digits = pbx_builtin_getvar_helper(ast, "SS7_JIP");
+ if (jip_digits)
+ isup_set_jip_digits(p->ss7call, jip_digits);
+
+ lspi_ident = pbx_builtin_getvar_helper(ast, "SS7_LSPI_IDENT");
+ if (lspi_ident)
+ isup_set_lspi(p->ss7call, lspi_ident, 0x18, 0x7, 0x00);
+
+ rlt_flag = pbx_builtin_getvar_helper(ast, "SS7_RLT_ON");
+ if ((rlt_flag) && ((strncmp("NO", rlt_flag, strlen(rlt_flag))) != 0 ))
+ isup_set_lspi(p->ss7call, rlt_flag, 0x18, 0x7, 0x00); /* Setting for Nortel DMS-250/500 */
+
+ call_ref_id = pbx_builtin_getvar_helper(ast, "SS7_CALLREF_IDENT");
+ call_ref_pc = pbx_builtin_getvar_helper(ast, "SS7_CALLREF_PC");
+ if (call_ref_id) {
+ isup_set_callref(p->ss7call, atoi(call_ref_id),
+ call_ref_pc ? atoi(call_ref_pc) : 0);
+ }
+
+ isup_iam(p->ss7->ss7, p->ss7call);
+ ast_setstate(ast, AST_STATE_DIALING);
+ ss7_rel(p->ss7);
+ }
+#endif /* HAVE_SS7 */
+#ifdef HAVE_PRI
+ if (p->pri) {
+ struct pri_sr *sr;
+#ifdef SUPPORT_USERUSER
+ const char *useruser;
+#endif
+ int pridialplan;
+ int dp_strip;
+ int prilocaldialplan;
+ int ldp_strip;
+ int exclusive;
+ const char *rr_str;
+ int redirect_reason;
+
+ c = strchr(dest, '/');
+ if (c)
+ c++;
+ else
+ c = dest;
+
+ l = NULL;
+ n = NULL;
+
+ if (!p->hidecallerid) {
+ l = ast->cid.cid_num;
+ if (!p->hidecalleridname) {
+ n = ast->cid.cid_name;
+ }
+ }
+
+ if (strlen(c) < p->stripmsd) {
+ ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ if (mysig != SIG_FXSKS) {
+ p->dop.op = ZT_DIAL_OP_REPLACE;
+ s = strchr(c + p->stripmsd, 'w');
+ if (s) {
+ if (strlen(s) > 1)
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%s", s);
+ else
+ p->dop.dialstr[0] = '\0';
+ *s = '\0';
+ } else {
+ p->dop.dialstr[0] = '\0';
+ }
+ }
+ if (pri_grab(p, p->pri)) {
+ ast_log(LOG_WARNING, "Failed to grab PRI!\n");
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ if (!(p->call = pri_new_call(p->pri->pri))) {
+ ast_log(LOG_WARNING, "Unable to create call on channel %d\n", p->channel);
+ pri_rel(p->pri);
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+ if (!(sr = pri_sr_new())) {
+ ast_log(LOG_WARNING, "Failed to allocate setup request channel %d\n", p->channel);
+ pri_rel(p->pri);
+ ast_mutex_unlock(&p->lock);
+ }
+ if (p->bearer || (mysig == SIG_FXSKS)) {
+ if (p->bearer) {
+ ast_debug(1, "Oooh, I have a bearer on %d (%d:%d)\n", PVT_TO_CHANNEL(p->bearer), p->bearer->logicalspan, p->bearer->channel);
+ p->bearer->call = p->call;
+ } else
+ ast_debug(1, "I'm being setup with no bearer right now...\n");
+
+ pri_set_crv(p->pri->pri, p->call, p->channel, 0);
+ }
+ p->digital = IS_DIGITAL(ast->transfercapability);
+ /* Add support for exclusive override */
+ if (p->priexclusive)
+ exclusive = 1;
+ else {
+ /* otherwise, traditional behavior */
+ if (p->pri->nodetype == PRI_NETWORK)
+ exclusive = 0;
+ else
+ exclusive = 1;
+ }
+
+ pri_sr_set_channel(sr, p->bearer ? PVT_TO_CHANNEL(p->bearer) : PVT_TO_CHANNEL(p), exclusive, 1);
+ pri_sr_set_bearer(sr, p->digital ? PRI_TRANS_CAP_DIGITAL : ast->transfercapability,
+ (p->digital ? -1 :
+ ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW)));
+ if (p->pri->facilityenable)
+ pri_facility_enable(p->pri->pri);
+
+ ast_verb(3, "Requested transfer capability: 0x%.2x - %s\n", ast->transfercapability, ast_transfercapability2str(ast->transfercapability));
+ dp_strip = 0;
+ pridialplan = p->pri->dialplan - 1;
+ if (pridialplan == -2 || pridialplan == -3) { /* compute dynamically */
+ if (strncmp(c + p->stripmsd, p->pri->internationalprefix, strlen(p->pri->internationalprefix)) == 0) {
+ if (pridialplan == -2) {
+ dp_strip = strlen(p->pri->internationalprefix);
+ }
+ pridialplan = PRI_INTERNATIONAL_ISDN;
+ } else if (strncmp(c + p->stripmsd, p->pri->nationalprefix, strlen(p->pri->nationalprefix)) == 0) {
+ if (pridialplan == -2) {
+ dp_strip = strlen(p->pri->nationalprefix);
+ }
+ pridialplan = PRI_NATIONAL_ISDN;
+ } else {
+ pridialplan = PRI_LOCAL_ISDN;
+ }
+ }
+ while (c[p->stripmsd] > '9' && c[p->stripmsd] != '*' && c[p->stripmsd] != '#') {
+ switch (c[p->stripmsd]) {
+ case 'U':
+ pridialplan = (PRI_TON_UNKNOWN << 4) | (pridialplan & 0xf);
+ break;
+ case 'I':
+ pridialplan = (PRI_TON_INTERNATIONAL << 4) | (pridialplan & 0xf);
+ break;
+ case 'N':
+ pridialplan = (PRI_TON_NATIONAL << 4) | (pridialplan & 0xf);
+ break;
+ case 'L':
+ pridialplan = (PRI_TON_NET_SPECIFIC << 4) | (pridialplan & 0xf);
+ break;
+ case 'S':
+ pridialplan = (PRI_TON_SUBSCRIBER << 4) | (pridialplan & 0xf);
+ break;
+ case 'V':
+ pridialplan = (PRI_TON_ABBREVIATED << 4) | (pridialplan & 0xf);
+ break;
+ case 'R':
+ pridialplan = (PRI_TON_RESERVED << 4) | (pridialplan & 0xf);
+ break;
+ case 'u':
+ pridialplan = PRI_NPI_UNKNOWN | (pridialplan & 0xf0);
+ break;
+ case 'e':
+ pridialplan = PRI_NPI_E163_E164 | (pridialplan & 0xf0);
+ break;
+ case 'x':
+ pridialplan = PRI_NPI_X121 | (pridialplan & 0xf0);
+ break;
+ case 'f':
+ pridialplan = PRI_NPI_F69 | (pridialplan & 0xf0);
+ break;
+ case 'n':
+ pridialplan = PRI_NPI_NATIONAL | (pridialplan & 0xf0);
+ break;
+ case 'p':
+ pridialplan = PRI_NPI_PRIVATE | (pridialplan & 0xf0);
+ break;
+ case 'r':
+ pridialplan = PRI_NPI_RESERVED | (pridialplan & 0xf0);
+ break;
+ default:
+ if (isalpha(*c))
+ ast_log(LOG_WARNING, "Unrecognized pridialplan %s modifier: %c\n", *c > 'Z' ? "NPI" : "TON", *c);
+ }
+ c++;
+ }
+ pri_sr_set_called(sr, c + p->stripmsd + dp_strip, pridialplan, s ? 1 : 0);
+
+ ldp_strip = 0;
+ prilocaldialplan = p->pri->localdialplan - 1;
+ if ((l != NULL) && (prilocaldialplan == -2 || prilocaldialplan == -3)) { /* compute dynamically */
+ if (strncmp(l, p->pri->internationalprefix, strlen(p->pri->internationalprefix)) == 0) {
+ if (prilocaldialplan == -2) {
+ ldp_strip = strlen(p->pri->internationalprefix);
+ }
+ prilocaldialplan = PRI_INTERNATIONAL_ISDN;
+ } else if (strncmp(l, p->pri->nationalprefix, strlen(p->pri->nationalprefix)) == 0) {
+ if (prilocaldialplan == -2) {
+ ldp_strip = strlen(p->pri->nationalprefix);
+ }
+ prilocaldialplan = PRI_NATIONAL_ISDN;
+ } else {
+ prilocaldialplan = PRI_LOCAL_ISDN;
+ }
+ }
+ if (l != NULL) {
+ while (*l > '9' && *l != '*' && *l != '#') {
+ switch (*l) {
+ case 'U':
+ prilocaldialplan = (PRI_TON_UNKNOWN << 4) | (prilocaldialplan & 0xf);
+ break;
+ case 'I':
+ prilocaldialplan = (PRI_TON_INTERNATIONAL << 4) | (prilocaldialplan & 0xf);
+ break;
+ case 'N':
+ prilocaldialplan = (PRI_TON_NATIONAL << 4) | (prilocaldialplan & 0xf);
+ break;
+ case 'L':
+ prilocaldialplan = (PRI_TON_NET_SPECIFIC << 4) | (prilocaldialplan & 0xf);
+ break;
+ case 'S':
+ prilocaldialplan = (PRI_TON_SUBSCRIBER << 4) | (prilocaldialplan & 0xf);
+ break;
+ case 'V':
+ prilocaldialplan = (PRI_TON_ABBREVIATED << 4) | (prilocaldialplan & 0xf);
+ break;
+ case 'R':
+ prilocaldialplan = (PRI_TON_RESERVED << 4) | (prilocaldialplan & 0xf);
+ break;
+ case 'u':
+ prilocaldialplan = PRI_NPI_UNKNOWN | (prilocaldialplan & 0xf0);
+ break;
+ case 'e':
+ prilocaldialplan = PRI_NPI_E163_E164 | (prilocaldialplan & 0xf0);
+ break;
+ case 'x':
+ prilocaldialplan = PRI_NPI_X121 | (prilocaldialplan & 0xf0);
+ break;
+ case 'f':
+ prilocaldialplan = PRI_NPI_F69 | (prilocaldialplan & 0xf0);
+ break;
+ case 'n':
+ prilocaldialplan = PRI_NPI_NATIONAL | (prilocaldialplan & 0xf0);
+ break;
+ case 'p':
+ prilocaldialplan = PRI_NPI_PRIVATE | (prilocaldialplan & 0xf0);
+ break;
+ case 'r':
+ prilocaldialplan = PRI_NPI_RESERVED | (prilocaldialplan & 0xf0);
+ break;
+ default:
+ if (isalpha(*l))
+ ast_log(LOG_WARNING, "Unrecognized prilocaldialplan %s modifier: %c\n", *c > 'Z' ? "NPI" : "TON", *c);
+ }
+ l++;
+ }
+ }
+ pri_sr_set_caller(sr, l ? (l + ldp_strip) : NULL, n, prilocaldialplan,
+ p->use_callingpres ? ast->cid.cid_pres : (l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE));
+ if ((rr_str = pbx_builtin_getvar_helper(ast, "PRIREDIRECTREASON"))) {
+ if (!strcasecmp(rr_str, "UNKNOWN"))
+ redirect_reason = 0;
+ else if (!strcasecmp(rr_str, "BUSY"))
+ redirect_reason = 1;
+ else if (!strcasecmp(rr_str, "NO_REPLY"))
+ redirect_reason = 2;
+ else if (!strcasecmp(rr_str, "UNCONDITIONAL"))
+ redirect_reason = 15;
+ else
+ redirect_reason = PRI_REDIR_UNCONDITIONAL;
+ } else
+ redirect_reason = PRI_REDIR_UNCONDITIONAL;
+ pri_sr_set_redirecting(sr, ast->cid.cid_rdnis, p->pri->localdialplan - 1, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, redirect_reason);
+
+#ifdef SUPPORT_USERUSER
+ /* User-user info */
+ useruser = pbx_builtin_getvar_helper(p->owner, "USERUSERINFO");
+
+ if (useruser)
+ pri_sr_set_useruser(sr, useruser);
+#endif
+
+ if (pri_setup(p->pri->pri, p->call, sr)) {
+ ast_log(LOG_WARNING, "Unable to setup call to %s (using %s)\n",
+ c + p->stripmsd + dp_strip, dialplan2str(p->pri->dialplan));
+ pri_rel(p->pri);
+ ast_mutex_unlock(&p->lock);
+ pri_sr_free(sr);
+ return -1;
+ }
+ pri_sr_free(sr);
+ ast_setstate(ast, AST_STATE_DIALING);
+ pri_rel(p->pri);
+ }
+#endif
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static void destroy_zt_pvt(struct zt_pvt **pvt)
+{
+ struct zt_pvt *p = *pvt;
+ /* Remove channel from the list */
+ if (p->prev)
+ p->prev->next = p->next;
+ if (p->next)
+ p->next->prev = p->prev;
+ if (p->use_smdi)
+ ASTOBJ_UNREF(p->smdi_iface, ast_smdi_interface_destroy);
+ if (p->mwi_event_sub)
+ ast_event_unsubscribe(p->mwi_event_sub);
+ if (p->vars)
+ ast_variables_destroy(p->vars);
+ ast_mutex_destroy(&p->lock);
+ ast_free(p);
+ *pvt = NULL;
+}
+
+static int destroy_channel(struct zt_pvt *prev, struct zt_pvt *cur, int now)
+{
+ int owned = 0;
+ int i = 0;
+
+ if (!now) {
+ if (cur->owner) {
+ owned = 1;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (cur->subs[i].owner) {
+ owned = 1;
+ }
+ }
+ if (!owned) {
+ if (prev) {
+ prev->next = cur->next;
+ if (prev->next)
+ prev->next->prev = prev;
+ else
+ ifend = prev;
+ } else {
+ iflist = cur->next;
+ if (iflist)
+ iflist->prev = NULL;
+ else
+ ifend = NULL;
+ }
+ if (cur->subs[SUB_REAL].zfd > -1) {
+ zt_close(cur->subs[SUB_REAL].zfd);
+ }
+ destroy_zt_pvt(&cur);
+ }
+ } else {
+ if (prev) {
+ prev->next = cur->next;
+ if (prev->next)
+ prev->next->prev = prev;
+ else
+ ifend = prev;
+ } else {
+ iflist = cur->next;
+ if (iflist)
+ iflist->prev = NULL;
+ else
+ ifend = NULL;
+ }
+ if (cur->subs[SUB_REAL].zfd > -1) {
+ zt_close(cur->subs[SUB_REAL].zfd);
+ }
+ destroy_zt_pvt(&cur);
+ }
+ return 0;
+}
+
+#ifdef HAVE_PRI
+static char *zap_send_keypad_facility_app = "ZapSendKeypadFacility";
+
+static char *zap_send_keypad_facility_synopsis = "Send digits out of band over a PRI";
+
+static char *zap_send_keypad_facility_descrip =
+" ZapSendKeypadFacility(): This application will send the given string of digits in a Keypad Facility\n"
+" IE over the current channel.\n";
+
+static int zap_send_keypad_facility_exec(struct ast_channel *chan, void *data)
+{
+ /* Data will be our digit string */
+ struct zt_pvt *p;
+ char *digits = (char *) data;
+
+ if (ast_strlen_zero(digits)) {
+ ast_debug(1, "No digit string sent to application!\n");
+ return -1;
+ }
+
+ p = (struct zt_pvt *)chan->tech_pvt;
+
+ if (!p) {
+ ast_debug(1, "Unable to find technology private\n");
+ return -1;
+ }
+
+ ast_mutex_lock(&p->lock);
+
+ if (!p->pri || !p->call) {
+ ast_debug(1, "Unable to find pri or call on channel!\n");
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+
+ if (!pri_grab(p, p->pri)) {
+ pri_keypad_facility(p->pri->pri, p->call, digits);
+ pri_rel(p->pri);
+ } else {
+ ast_debug(1, "Unable to grab pri to send keypad facility!\n");
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+
+ ast_mutex_unlock(&p->lock);
+
+ return 0;
+}
+
+static int pri_is_up(struct zt_pri *pri)
+{
+ int x;
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if (pri->dchanavail[x] == DCHAN_AVAILABLE)
+ return 1;
+ }
+ return 0;
+}
+
+static int pri_assign_bearer(struct zt_pvt *crv, struct zt_pri *pri, struct zt_pvt *bearer)
+{
+ bearer->owner = &inuse;
+ bearer->realcall = crv;
+ crv->subs[SUB_REAL].zfd = bearer->subs[SUB_REAL].zfd;
+ if (crv->subs[SUB_REAL].owner)
+ ast_channel_set_fd(crv->subs[SUB_REAL].owner, 0, crv->subs[SUB_REAL].zfd);
+ crv->bearer = bearer;
+ crv->call = bearer->call;
+ crv->pri = pri;
+ return 0;
+}
+
+static char *pri_order(int level)
+{
+ switch (level) {
+ case 0:
+ return "Primary";
+ case 1:
+ return "Secondary";
+ case 2:
+ return "Tertiary";
+ case 3:
+ return "Quaternary";
+ default:
+ return "<Unknown>";
+ }
+}
+
+/* Returns fd of the active dchan */
+static int pri_active_dchan_fd(struct zt_pri *pri)
+{
+ int x = -1;
+
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if ((pri->dchans[x] == pri->pri))
+ break;
+ }
+
+ return pri->fds[x];
+}
+
+static int pri_find_dchan(struct zt_pri *pri)
+{
+ int oldslot = -1;
+ struct pri *old;
+ int newslot = -1;
+ int x;
+ old = pri->pri;
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if ((pri->dchanavail[x] == DCHAN_AVAILABLE) && (newslot < 0))
+ newslot = x;
+ if (pri->dchans[x] == old) {
+ oldslot = x;
+ }
+ }
+ if (newslot < 0) {
+ newslot = 0;
+ ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n",
+ pri->dchannels[newslot]);
+ }
+ if (old && (oldslot != newslot))
+ ast_log(LOG_NOTICE, "Switching from from d-channel %d to channel %d!\n",
+ pri->dchannels[oldslot], pri->dchannels[newslot]);
+ pri->pri = pri->dchans[newslot];
+ return 0;
+}
+#endif
+
+static int zt_hangup(struct ast_channel *ast)
+{
+ int res;
+ int index,x, law;
+ /*static int restore_gains(struct zt_pvt *p);*/
+ struct zt_pvt *p = ast->tech_pvt;
+ struct zt_pvt *tmp = NULL;
+ struct zt_pvt *prev = NULL;
+ ZT_PARAMS par;
+
+ ast_debug(1, "zt_hangup(%s)\n", ast->name);
+ if (!ast->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+
+ ast_mutex_lock(&p->lock);
+
+ index = zt_get_index(ast, p, 1);
+
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
+ x = 1;
+ ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
+ }
+
+ x = 0;
+ zt_confmute(p, 0);
+ restore_gains(p);
+ if (p->origcid_num) {
+ ast_copy_string(p->cid_num, p->origcid_num, sizeof(p->cid_num));
+ ast_free(p->origcid_num);
+ p->origcid_num = NULL;
+ }
+ if (p->origcid_name) {
+ ast_copy_string(p->cid_name, p->origcid_name, sizeof(p->cid_name));
+ ast_free(p->origcid_name);
+ p->origcid_name = NULL;
+ }
+ if (p->dsp)
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax);
+ if (p->exten)
+ p->exten[0] = '\0';
+
+ ast_debug(1, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
+ p->channel, index, p->subs[SUB_REAL].zfd, p->subs[SUB_CALLWAIT].zfd, p->subs[SUB_THREEWAY].zfd);
+ p->ignoredtmf = 0;
+
+ if (index > -1) {
+ /* Real channel, do some fixup */
+ p->subs[index].owner = NULL;
+ p->subs[index].needanswer = 0;
+ p->subs[index].needflash = 0;
+ p->subs[index].needringing = 0;
+ p->subs[index].needbusy = 0;
+ p->subs[index].needcongestion = 0;
+ p->subs[index].linear = 0;
+ p->subs[index].needcallerid = 0;
+ p->polarity = POLARITY_IDLE;
+ zt_setlinear(p->subs[index].zfd, 0);
+ if (index == SUB_REAL) {
+ if ((p->subs[SUB_CALLWAIT].zfd > -1) && (p->subs[SUB_THREEWAY].zfd > -1)) {
+ ast_debug(1, "Normal call hung up with both three way call and a call waiting call in place?\n");
+ if (p->subs[SUB_CALLWAIT].inthreeway) {
+ /* We had flipped over to answer a callwait and now it's gone */
+ ast_debug(1, "We were flipped over to the callwait, moving back and unowning.\n");
+ /* Move to the call-wait, but un-own us until they flip back. */
+ swap_subs(p, SUB_CALLWAIT, SUB_REAL);
+ unalloc_sub(p, SUB_CALLWAIT);
+ p->owner = NULL;
+ } else {
+ /* The three way hung up, but we still have a call wait */
+ ast_debug(1, "We were in the threeway and have a callwait still. Ditching the threeway.\n");
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ unalloc_sub(p, SUB_THREEWAY);
+ if (p->subs[SUB_REAL].inthreeway) {
+ /* This was part of a three way call. Immediately make way for
+ another call */
+ ast_debug(1, "Call was complete, setting owner to former third call\n");
+ p->owner = p->subs[SUB_REAL].owner;
+ } else {
+ /* This call hasn't been completed yet... Set owner to NULL */
+ ast_debug(1, "Call was incomplete, setting owner to NULL\n");
+ p->owner = NULL;
+ }
+ p->subs[SUB_REAL].inthreeway = 0;
+ }
+ } else if (p->subs[SUB_CALLWAIT].zfd > -1) {
+ /* Move to the call-wait and switch back to them. */
+ swap_subs(p, SUB_CALLWAIT, SUB_REAL);
+ unalloc_sub(p, SUB_CALLWAIT);
+ p->owner = p->subs[SUB_REAL].owner;
+ if (p->owner->_state != AST_STATE_UP)
+ p->subs[SUB_REAL].needanswer = 1;
+ if (ast_bridged_channel(p->subs[SUB_REAL].owner))
+ ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+ } else if (p->subs[SUB_THREEWAY].zfd > -1) {
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ unalloc_sub(p, SUB_THREEWAY);
+ if (p->subs[SUB_REAL].inthreeway) {
+ /* This was part of a three way call. Immediately make way for
+ another call */
+ ast_debug(1, "Call was complete, setting owner to former third call\n");
+ p->owner = p->subs[SUB_REAL].owner;
+ } else {
+ /* This call hasn't been completed yet... Set owner to NULL */
+ ast_debug(1, "Call was incomplete, setting owner to NULL\n");
+ p->owner = NULL;
+ }
+ p->subs[SUB_REAL].inthreeway = 0;
+ }
+ } else if (index == SUB_CALLWAIT) {
+ /* Ditch the holding callwait call, and immediately make it availabe */
+ if (p->subs[SUB_CALLWAIT].inthreeway) {
+ /* This is actually part of a three way, placed on hold. Place the third part
+ on music on hold now */
+ if (p->subs[SUB_THREEWAY].owner && ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
+ ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD,
+ S_OR(p->mohsuggest, NULL),
+ !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+ }
+ p->subs[SUB_THREEWAY].inthreeway = 0;
+ /* Make it the call wait now */
+ swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
+ unalloc_sub(p, SUB_THREEWAY);
+ } else
+ unalloc_sub(p, SUB_CALLWAIT);
+ } else if (index == SUB_THREEWAY) {
+ if (p->subs[SUB_CALLWAIT].inthreeway) {
+ /* The other party of the three way call is currently in a call-wait state.
+ Start music on hold for them, and take the main guy out of the third call */
+ if (p->subs[SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
+ ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
+ S_OR(p->mohsuggest, NULL),
+ !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+ }
+ p->subs[SUB_CALLWAIT].inthreeway = 0;
+ }
+ p->subs[SUB_REAL].inthreeway = 0;
+ /* If this was part of a three way call index, let us make
+ another three way call */
+ unalloc_sub(p, SUB_THREEWAY);
+ } else {
+ /* This wasn't any sort of call, but how are we an index? */
+ ast_log(LOG_WARNING, "Index found but not any type of call?\n");
+ }
+ }
+
+ if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) {
+ p->owner = NULL;
+ p->ringt = 0;
+ p->distinctivering = 0;
+ p->confirmanswer = 0;
+ p->cidrings = 1;
+ p->outgoing = 0;
+ p->digital = 0;
+ p->faxhandled = 0;
+ p->pulsedial = 0;
+ p->onhooktime = time(NULL);
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ p->proceeding = 0;
+ p->progress = 0;
+ p->alerting = 0;
+ p->setup_ack = 0;
+ p->rlt = 0;
+#endif
+ if (p->dsp) {
+ ast_dsp_free(p->dsp);
+ p->dsp = NULL;
+ }
+
+ law = ZT_LAW_DEFAULT;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_SETLAW, &law);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to set law on channel %d to default\n", p->channel);
+ /* Perform low level hangup if no owner left */
+#ifdef HAVE_SS7
+ if (p->ss7) {
+ if (p->ss7call) {
+ if (!ss7_grab(p, p->ss7)) {
+ if (!p->alreadyhungup) {
+ const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE");
+ int icause = ast->hangupcause ? ast->hangupcause : -1;
+
+ if (cause) {
+ if (atoi(cause))
+ icause = atoi(cause);
+ }
+ isup_rel(p->ss7->ss7, p->ss7call, icause);
+ ss7_rel(p->ss7);
+ p->alreadyhungup = 1;
+ } else
+ ast_log(LOG_WARNING, "Trying to hangup twice!\n");
+ } else {
+ ast_log(LOG_WARNING, "Unable to grab SS7 on CIC %d\n", p->cic);
+ res = -1;
+ }
+ }
+ }
+#endif
+#ifdef HAVE_PRI
+ if (p->pri) {
+#ifdef SUPPORT_USERUSER
+ const char *useruser = pbx_builtin_getvar_helper(ast,"USERUSERINFO");
+#endif
+
+ /* Make sure we have a call (or REALLY have a call in the case of a PRI) */
+ if (p->call && (!p->bearer || (p->bearer->call == p->call))) {
+ if (!pri_grab(p, p->pri)) {
+ if (p->alreadyhungup) {
+ ast_debug(1, "Already hungup... Calling hangup once, and clearing call\n");
+
+#ifdef SUPPORT_USERUSER
+ pri_call_set_useruser(p->call, useruser);
+#endif
+
+ pri_hangup(p->pri->pri, p->call, -1);
+ p->call = NULL;
+ if (p->bearer)
+ p->bearer->call = NULL;
+ } else {
+ const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE");
+ int icause = ast->hangupcause ? ast->hangupcause : -1;
+ ast_debug(1, "Not yet hungup... Calling hangup once with icause, and clearing call\n");
+
+#ifdef SUPPORT_USERUSER
+ pri_call_set_useruser(p->call, useruser);
+#endif
+
+ p->alreadyhungup = 1;
+ if (p->bearer)
+ p->bearer->alreadyhungup = 1;
+ if (cause) {
+ if (atoi(cause))
+ icause = atoi(cause);
+ }
+ pri_hangup(p->pri->pri, p->call, icause);
+ }
+ if (res < 0)
+ ast_log(LOG_WARNING, "pri_disconnect failed\n");
+ pri_rel(p->pri);
+ } else {
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ res = -1;
+ }
+ } else {
+ if (p->bearer)
+ ast_debug(1, "Bearer call is %p, while ours is still %p\n", p->bearer->call, p->call);
+ p->call = NULL;
+ res = 0;
+ }
+ }
+#endif
+ if (p->sig && ((p->sig != SIG_PRI) && (p->sig != SIG_SS7) && (p->sig != SIG_BRI) && (p->sig != SIG_BRI_PTMP)))
+ res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
+ }
+ switch (p->sig) {
+ case SIG_FXOGS:
+ case SIG_FXOLS:
+ case SIG_FXOKS:
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &par);
+ if (!res) {
+#if 0
+ ast_debug(1, "Hanging up channel %d, offhook = %d\n", p->channel, par.rxisoffhook);
+#endif
+ /* If they're off hook, try playing congestion */
+ if ((par.rxisoffhook) && (!(p->radio || (p->oprmode < 0))))
+ tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
+ else
+ tone_zone_play_tone(p->subs[SUB_REAL].zfd, -1);
+ }
+ break;
+ case SIG_FXSGS:
+ case SIG_FXSLS:
+ case SIG_FXSKS:
+ /* Make sure we're not made available for at least two seconds assuming
+ we were actually used for an inbound or outbound call. */
+ if (ast->_state != AST_STATE_RESERVED) {
+ time(&p->guardtime);
+ p->guardtime += 2;
+ }
+ break;
+ default:
+ tone_zone_play_tone(p->subs[SUB_REAL].zfd, -1);
+ }
+ if (p->cidspill)
+ ast_free(p->cidspill);
+ if (p->sig)
+ zt_disable_ec(p);
+ x = 0;
+ ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
+ ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0);
+ p->didtdd = 0;
+ p->cidspill = NULL;
+ p->callwaitcas = 0;
+ p->callwaiting = p->permcallwaiting;
+ p->hidecallerid = p->permhidecallerid;
+ p->dialing = 0;
+ p->rdnis[0] = '\0';
+ update_conf(p);
+ reset_conf(p);
+ /* Restore data mode */
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
+ x = 0;
+ ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
+ }
+#ifdef HAVE_PRI
+ if (p->bearer) {
+ ast_debug(1, "Freeing up bearer channel %d\n", p->bearer->channel);
+ /* Free up the bearer channel as well, and
+ don't use its file descriptor anymore */
+ update_conf(p->bearer);
+ reset_conf(p->bearer);
+ p->bearer->owner = NULL;
+ p->bearer->realcall = NULL;
+ p->bearer = NULL;
+ p->subs[SUB_REAL].zfd = -1;
+ p->pri = NULL;
+ }
+#endif
+ restart_monitor();
+ }
+
+ p->callwaitingrepeat = 0;
+ p->cidcwexpire = 0;
+ p->oprmode = 0;
+ ast->tech_pvt = NULL;
+ ast_mutex_unlock(&p->lock);
+ ast_module_unref(ast_module_info->self);
+ ast_verb(3, "Hungup '%s'\n", ast->name);
+
+ ast_mutex_lock(&iflock);
+ tmp = iflist;
+ prev = NULL;
+ if (p->destroy) {
+ while (tmp) {
+ if (tmp == p) {
+ destroy_channel(prev, tmp, 0);
+ break;
+ } else {
+ prev = tmp;
+ tmp = tmp->next;
+ }
+ }
+ }
+ ast_mutex_unlock(&iflock);
+ return 0;
+}
+
+static int zt_answer(struct ast_channel *ast)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ int res = 0;
+ int index;
+ int oldstate = ast->_state;
+ ast_setstate(ast, AST_STATE_UP);
+ ast_mutex_lock(&p->lock);
+ index = zt_get_index(ast, p, 0);
+ if (index < 0)
+ index = SUB_REAL;
+ /* nothing to do if a radio channel */
+ if ((p->radio || (p->oprmode < 0))) {
+ ast_mutex_unlock(&p->lock);
+ return 0;
+ }
+ switch (p->sig) {
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+ p->ringt = 0;
+ /* Fall through */
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_EMWINK:
+ case SIG_FEATD:
+ case SIG_FEATDMF:
+ case SIG_FEATDMF_TA:
+ case SIG_E911:
+ case SIG_FGC_CAMA:
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ case SIG_SF:
+ case SIG_SFWINK:
+ case SIG_SF_FEATD:
+ case SIG_SF_FEATDMF:
+ case SIG_SF_FEATB:
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ /* Pick up the line */
+ ast_debug(1, "Took %s off hook\n", ast->name);
+ if (p->hanguponpolarityswitch) {
+ p->polaritydelaytv = ast_tvnow();
+ }
+ res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+ p->dialing = 0;
+ if ((index == SUB_REAL) && p->subs[SUB_THREEWAY].inthreeway) {
+ if (oldstate == AST_STATE_RINGING) {
+ ast_debug(1, "Finally swapping real and threeway\n");
+ tone_zone_play_tone(p->subs[SUB_THREEWAY].zfd, -1);
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ p->owner = p->subs[SUB_REAL].owner;
+ }
+ }
+ if (p->sig & __ZT_SIG_FXS) {
+ zt_enable_ec(p);
+ zt_train_ec(p);
+ }
+ break;
+#ifdef HAVE_PRI
+ case SIG_BRI:
+ case SIG_BRI_PTMP:
+ case SIG_PRI:
+ /* Send a pri acknowledge */
+ if (!pri_grab(p, p->pri)) {
+ p->proceeding = 1;
+ res = pri_answer(p->pri->pri, p->call, 0, !p->digital);
+ pri_rel(p->pri);
+ } else {
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ res = -1;
+ }
+ break;
+#endif
+#ifdef HAVE_SS7
+ case SIG_SS7:
+ if (!ss7_grab(p, p->ss7)) {
+ p->proceeding = 1;
+ res = isup_anm(p->ss7->ss7, p->ss7call);
+ ss7_rel(p->ss7);
+ } else {
+ ast_log(LOG_WARNING, "Unable to grab SS7 on span %d\n", p->span);
+ res = -1;
+ }
+ break;
+#endif
+ case 0:
+ ast_mutex_unlock(&p->lock);
+ return 0;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel);
+ res = -1;
+ }
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static int zt_setoption(struct ast_channel *chan, int option, void *data, int datalen)
+{
+ char *cp;
+ signed char *scp;
+ int x;
+ int index;
+ struct zt_pvt *p = chan->tech_pvt, *pp;
+ struct oprmode *oprmode;
+
+
+ /* all supported options require data */
+ if (!data || (datalen < 1)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (option) {
+ case AST_OPTION_TXGAIN:
+ scp = (signed char *) data;
+ index = zt_get_index(chan, p, 0);
+ if (index < 0) {
+ ast_log(LOG_WARNING, "No index in TXGAIN?\n");
+ return -1;
+ }
+ ast_debug(1, "Setting actual tx gain on %s to %f\n", chan->name, p->txgain + (float) *scp);
+ return set_actual_txgain(p->subs[index].zfd, 0, p->txgain + (float) *scp, p->law);
+ case AST_OPTION_RXGAIN:
+ scp = (signed char *) data;
+ index = zt_get_index(chan, p, 0);
+ if (index < 0) {
+ ast_log(LOG_WARNING, "No index in RXGAIN?\n");
+ return -1;
+ }
+ ast_debug(1, "Setting actual rx gain on %s to %f\n", chan->name, p->rxgain + (float) *scp);
+ return set_actual_rxgain(p->subs[index].zfd, 0, p->rxgain + (float) *scp, p->law);
+ case AST_OPTION_TONE_VERIFY:
+ if (!p->dsp)
+ break;
+ cp = (char *) data;
+ switch (*cp) {
+ case 1:
+ ast_debug(1, "Set option TONE VERIFY, mode: MUTECONF(1) on %s\n",chan->name);
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | p->dtmfrelax); /* set mute mode if desired */
+ break;
+ case 2:
+ ast_debug(1, "Set option TONE VERIFY, mode: MUTECONF/MAX(2) on %s\n",chan->name);
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX | p->dtmfrelax); /* set mute mode if desired */
+ break;
+ default:
+ ast_debug(1, "Set option TONE VERIFY, mode: OFF(0) on %s\n",chan->name);
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); /* set mute mode if desired */
+ break;
+ }
+ break;
+ case AST_OPTION_TDD:
+ /* turn on or off TDD */
+ cp = (char *) data;
+ p->mate = 0;
+ if (!*cp) { /* turn it off */
+ ast_debug(1, "Set option TDD MODE, value: OFF(0) on %s\n",chan->name);
+ if (p->tdd)
+ tdd_free(p->tdd);
+ p->tdd = 0;
+ break;
+ }
+ ast_debug(1, "Set option TDD MODE, value: %s(%d) on %s\n",
+ (*cp == 2) ? "MATE" : "ON", (int) *cp, chan->name);
+ zt_disable_ec(p);
+ /* otherwise, turn it on */
+ if (!p->didtdd) { /* if havent done it yet */
+ unsigned char mybuf[41000], *buf;
+ int size, res, fd, len;
+ struct pollfd fds[1];
+
+ buf = mybuf;
+ memset(buf, 0x7f, sizeof(mybuf)); /* set to silence */
+ ast_tdd_gen_ecdisa(buf + 16000, 16000); /* put in tone */
+ len = 40000;
+ index = zt_get_index(chan, p, 0);
+ if (index < 0) {
+ ast_log(LOG_WARNING, "No index in TDD?\n");
+ return -1;
+ }
+ fd = p->subs[index].zfd;
+ while (len) {
+ if (ast_check_hangup(chan))
+ return -1;
+ size = len;
+ if (size > READ_SIZE)
+ size = READ_SIZE;
+ fds[0].fd = fd;
+ fds[0].events = POLLPRI | POLLOUT;
+ fds[0].revents = 0;
+ res = poll(fds, 1, -1);
+ if (!res) {
+ ast_debug(1, "poll (for write) ret. 0 on channel %d\n", p->channel);
+ continue;
+ }
+ /* if got exception */
+ if (fds[0].revents & POLLPRI)
+ return -1;
+ if (!(fds[0].revents & POLLOUT)) {
+ ast_debug(1, "write fd not ready on channel %d\n", p->channel);
+ continue;
+ }
+ res = write(fd, buf, size);
+ if (res != size) {
+ if (res == -1) return -1;
+ ast_debug(1, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel);
+ break;
+ }
+ len -= size;
+ buf += size;
+ }
+ p->didtdd = 1; /* set to have done it now */
+ }
+ if (*cp == 2) { /* Mate mode */
+ if (p->tdd)
+ tdd_free(p->tdd);
+ p->tdd = 0;
+ p->mate = 1;
+ break;
+ }
+ if (!p->tdd) { /* if we dont have one yet */
+ p->tdd = tdd_new(); /* allocate one */
+ }
+ break;
+ case AST_OPTION_RELAXDTMF: /* Relax DTMF decoding (or not) */
+ if (!p->dsp)
+ break;
+ cp = (char *) data;
+ ast_debug(1, "Set option RELAX DTMF, value: %s(%d) on %s\n",
+ *cp ? "ON" : "OFF", (int) *cp, chan->name);
+ ast_dsp_digitmode(p->dsp, ((*cp) ? DSP_DIGITMODE_RELAXDTMF : DSP_DIGITMODE_DTMF) | p->dtmfrelax);
+ break;
+ case AST_OPTION_AUDIO_MODE: /* Set AUDIO mode (or not) */
+ cp = (char *) data;
+ if (!*cp) {
+ ast_debug(1, "Set option AUDIO MODE, value: OFF(0) on %s\n", chan->name);
+ x = 0;
+ zt_disable_ec(p);
+ } else {
+ ast_debug(1, "Set option AUDIO MODE, value: ON(1) on %s\n", chan->name);
+ x = 1;
+ }
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &x) == -1)
+ ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", p->channel, x);
+ break;
+ case AST_OPTION_OPRMODE: /* Operator services mode */
+ oprmode = (struct oprmode *) data;
+ pp = oprmode->peer->tech_pvt;
+ p->oprmode = pp->oprmode = 0;
+ /* setup peers */
+ p->oprpeer = pp;
+ pp->oprpeer = p;
+ /* setup modes, if any */
+ if (oprmode->mode)
+ {
+ pp->oprmode = oprmode->mode;
+ p->oprmode = -oprmode->mode;
+ }
+ ast_debug(1, "Set Operator Services mode, value: %d on %s/%s\n",
+ oprmode->mode, chan->name,oprmode->peer->name);
+ break;
+ case AST_OPTION_ECHOCAN:
+ cp = (char *) data;
+ if (*cp) {
+ ast_debug(1, "Enabling echo cancelation on %s\n", chan->name);
+ zt_enable_ec(p);
+ } else {
+ ast_debug(1, "Disabling echo cancelation on %s\n", chan->name);
+ zt_disable_ec(p);
+ }
+ break;
+ }
+ errno = 0;
+
+ return 0;
+}
+
+static int zt_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
+{
+ struct zt_pvt *p = chan->tech_pvt;
+
+ if (!strcasecmp(data, "rxgain")) {
+ ast_mutex_lock(&p->lock);
+ snprintf(buf, len, "%f", p->rxgain);
+ ast_mutex_unlock(&p->lock);
+ } else if (!strcasecmp(data, "txgain")) {
+ ast_mutex_lock(&p->lock);
+ snprintf(buf, len, "%f", p->txgain);
+ ast_mutex_unlock(&p->lock);
+ } else {
+ ast_copy_string(buf, "", len);
+ }
+ return 0;
+}
+
+
+static void zt_unlink(struct zt_pvt *slave, struct zt_pvt *master, int needlock)
+{
+ /* Unlink a specific slave or all slaves/masters from a given master */
+ int x;
+ int hasslaves;
+ if (!master)
+ return;
+ if (needlock) {
+ ast_mutex_lock(&master->lock);
+ if (slave) {
+ while (ast_mutex_trylock(&slave->lock)) {
+ ast_mutex_unlock(&master->lock);
+ usleep(1);
+ ast_mutex_lock(&master->lock);
+ }
+ }
+ }
+ hasslaves = 0;
+ for (x = 0; x < MAX_SLAVES; x++) {
+ if (master->slaves[x]) {
+ if (!slave || (master->slaves[x] == slave)) {
+ /* Take slave out of the conference */
+ ast_debug(1, "Unlinking slave %d from %d\n", master->slaves[x]->channel, master->channel);
+ conf_del(master, &master->slaves[x]->subs[SUB_REAL], SUB_REAL);
+ conf_del(master->slaves[x], &master->subs[SUB_REAL], SUB_REAL);
+ master->slaves[x]->master = NULL;
+ master->slaves[x] = NULL;
+ } else
+ hasslaves = 1;
+ }
+ if (!hasslaves)
+ master->inconference = 0;
+ }
+ if (!slave) {
+ if (master->master) {
+ /* Take master out of the conference */
+ conf_del(master->master, &master->subs[SUB_REAL], SUB_REAL);
+ conf_del(master, &master->master->subs[SUB_REAL], SUB_REAL);
+ hasslaves = 0;
+ for (x = 0; x < MAX_SLAVES; x++) {
+ if (master->master->slaves[x] == master)
+ master->master->slaves[x] = NULL;
+ else if (master->master->slaves[x])
+ hasslaves = 1;
+ }
+ if (!hasslaves)
+ master->master->inconference = 0;
+ }
+ master->master = NULL;
+ }
+ update_conf(master);
+ if (needlock) {
+ if (slave)
+ ast_mutex_unlock(&slave->lock);
+ ast_mutex_unlock(&master->lock);
+ }
+}
+
+static void zt_link(struct zt_pvt *slave, struct zt_pvt *master) {
+ int x;
+ if (!slave || !master) {
+ ast_log(LOG_WARNING, "Tried to link to/from NULL??\n");
+ return;
+ }
+ for (x = 0; x < MAX_SLAVES; x++) {
+ if (!master->slaves[x]) {
+ master->slaves[x] = slave;
+ break;
+ }
+ }
+ if (x >= MAX_SLAVES) {
+ ast_log(LOG_WARNING, "Replacing slave %d with new slave, %d\n", master->slaves[MAX_SLAVES - 1]->channel, slave->channel);
+ master->slaves[MAX_SLAVES - 1] = slave;
+ }
+ if (slave->master)
+ ast_log(LOG_WARNING, "Replacing master %d with new master, %d\n", slave->master->channel, master->channel);
+ slave->master = master;
+
+ ast_debug(1, "Making %d slave to master %d at %d\n", slave->channel, master->channel, x);
+}
+
+static void disable_dtmf_detect(struct zt_pvt *p)
+{
+#ifdef ZT_TONEDETECT
+ int val;
+#endif
+
+ p->ignoredtmf = 1;
+
+#ifdef ZT_TONEDETECT
+ val = 0;
+ ioctl(p->subs[SUB_REAL].zfd, ZT_TONEDETECT, &val);
+#endif
+ if (!p->hardwaredtmf && p->dsp) {
+ p->dsp_features &= ~DSP_FEATURE_DTMF_DETECT;
+ ast_dsp_set_features(p->dsp, p->dsp_features);
+ }
+}
+
+static void enable_dtmf_detect(struct zt_pvt *p)
+{
+#ifdef ZT_TONEDETECT
+ int val;
+#endif
+
+ if (p->channel == CHAN_PSEUDO)
+ return;
+
+ p->ignoredtmf = 0;
+
+#ifdef ZT_TONEDETECT
+ val = ZT_TONEDETECT_ON | ZT_TONEDETECT_MUTE;
+ ioctl(p->subs[SUB_REAL].zfd, ZT_TONEDETECT, &val);
+#endif
+ if (!p->hardwaredtmf && p->dsp) {
+ p->dsp_features |= DSP_FEATURE_DTMF_DETECT;
+ ast_dsp_set_features(p->dsp, p->dsp_features);
+ }
+}
+
+static enum ast_bridge_result zt_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+{
+ struct ast_channel *who;
+ struct zt_pvt *p0, *p1, *op0, *op1;
+ struct zt_pvt *master = NULL, *slave = NULL;
+ struct ast_frame *f;
+ int inconf = 0;
+ int nothingok = 1;
+ int ofd0, ofd1;
+ int oi0, oi1, i0 = -1, i1 = -1, t0, t1;
+ int os0 = -1, os1 = -1;
+ int priority = 0;
+ struct ast_channel *oc0, *oc1;
+ enum ast_bridge_result res;
+
+#ifdef PRI_2BCT
+ int triedtopribridge = 0;
+ q931_call *q931c0 = NULL, *q931c1 = NULL;
+#endif
+
+ /* For now, don't attempt to native bridge if either channel needs DTMF detection.
+ There is code below to handle it properly until DTMF is actually seen,
+ but due to currently unresolved issues it's ignored...
+ */
+
+ if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
+ return AST_BRIDGE_FAILED_NOWARN;
+
+ ast_channel_lock(c0);
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+
+ p0 = c0->tech_pvt;
+ p1 = c1->tech_pvt;
+ /* cant do pseudo-channels here */
+ if (!p0 || (!p0->sig) || !p1 || (!p1->sig)) {
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ oi0 = zt_get_index(c0, p0, 0);
+ oi1 = zt_get_index(c1, p1, 0);
+ if ((oi0 < 0) || (oi1 < 0)) {
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+
+ op0 = p0 = c0->tech_pvt;
+ op1 = p1 = c1->tech_pvt;
+ ofd0 = c0->fds[0];
+ ofd1 = c1->fds[0];
+ oc0 = p0->owner;
+ oc1 = p1->owner;
+
+ if (ast_mutex_trylock(&p0->lock)) {
+ /* Don't block, due to potential for deadlock */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ ast_log(LOG_NOTICE, "Avoiding deadlock...\n");
+ return AST_BRIDGE_RETRY;
+ }
+ if (ast_mutex_trylock(&p1->lock)) {
+ /* Don't block, due to potential for deadlock */
+ ast_mutex_unlock(&p0->lock);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ ast_log(LOG_NOTICE, "Avoiding deadlock...\n");
+ return AST_BRIDGE_RETRY;
+ }
+
+ if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) {
+ if (p0->owner && p1->owner) {
+ /* If we don't have a call-wait in a 3-way, and we aren't in a 3-way, we can be master */
+ if (!p0->subs[SUB_CALLWAIT].inthreeway && !p1->subs[SUB_REAL].inthreeway) {
+ master = p0;
+ slave = p1;
+ inconf = 1;
+ } else if (!p1->subs[SUB_CALLWAIT].inthreeway && !p0->subs[SUB_REAL].inthreeway) {
+ master = p1;
+ slave = p0;
+ inconf = 1;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Both calls are callwaits or 3-ways? That's clever...?\n");
+ ast_log(LOG_WARNING, "p0: chan %d/%d/CW%d/3W%d, p1: chan %d/%d/CW%d/3W%d\n",
+ p0->channel,
+ oi0, (p0->subs[SUB_CALLWAIT].zfd > -1) ? 1 : 0,
+ p0->subs[SUB_REAL].inthreeway, p0->channel,
+ oi0, (p1->subs[SUB_CALLWAIT].zfd > -1) ? 1 : 0,
+ p1->subs[SUB_REAL].inthreeway);
+ }
+ nothingok = 0;
+ }
+ } else if ((oi0 == SUB_REAL) && (oi1 == SUB_THREEWAY)) {
+ if (p1->subs[SUB_THREEWAY].inthreeway) {
+ master = p1;
+ slave = p0;
+ nothingok = 0;
+ }
+ } else if ((oi0 == SUB_THREEWAY) && (oi1 == SUB_REAL)) {
+ if (p0->subs[SUB_THREEWAY].inthreeway) {
+ master = p0;
+ slave = p1;
+ nothingok = 0;
+ }
+ } else if ((oi0 == SUB_REAL) && (oi1 == SUB_CALLWAIT)) {
+ /* We have a real and a call wait. If we're in a three way call, put us in it, otherwise,
+ don't put us in anything */
+ if (p1->subs[SUB_CALLWAIT].inthreeway) {
+ master = p1;
+ slave = p0;
+ nothingok = 0;
+ }
+ } else if ((oi0 == SUB_CALLWAIT) && (oi1 == SUB_REAL)) {
+ /* Same as previous */
+ if (p0->subs[SUB_CALLWAIT].inthreeway) {
+ master = p0;
+ slave = p1;
+ nothingok = 0;
+ }
+ }
+ ast_debug(1, "master: %d, slave: %d, nothingok: %d\n",
+ master ? master->channel : 0, slave ? slave->channel : 0, nothingok);
+ if (master && slave) {
+ /* Stop any tones, or play ringtone as appropriate. If they're bridged
+ in an active threeway call with a channel that is ringing, we should
+ indicate ringing. */
+ if ((oi1 == SUB_THREEWAY) &&
+ p1->subs[SUB_THREEWAY].inthreeway &&
+ p1->subs[SUB_REAL].owner &&
+ p1->subs[SUB_REAL].inthreeway &&
+ (p1->subs[SUB_REAL].owner->_state == AST_STATE_RINGING)) {
+ ast_debug(1, "Playing ringback on %s since %s is in a ringing three-way\n", c0->name, c1->name);
+ tone_zone_play_tone(p0->subs[oi0].zfd, ZT_TONE_RINGTONE);
+ os1 = p1->subs[SUB_REAL].owner->_state;
+ } else {
+ ast_debug(1, "Stopping tones on %d/%d talking to %d/%d\n", p0->channel, oi0, p1->channel, oi1);
+ tone_zone_play_tone(p0->subs[oi0].zfd, -1);
+ }
+ if ((oi0 == SUB_THREEWAY) &&
+ p0->subs[SUB_THREEWAY].inthreeway &&
+ p0->subs[SUB_REAL].owner &&
+ p0->subs[SUB_REAL].inthreeway &&
+ (p0->subs[SUB_REAL].owner->_state == AST_STATE_RINGING)) {
+ ast_debug(1, "Playing ringback on %s since %s is in a ringing three-way\n", c1->name, c0->name);
+ tone_zone_play_tone(p1->subs[oi1].zfd, ZT_TONE_RINGTONE);
+ os0 = p0->subs[SUB_REAL].owner->_state;
+ } else {
+ ast_debug(1, "Stopping tones on %d/%d talking to %d/%d\n", p1->channel, oi1, p0->channel, oi0);
+ tone_zone_play_tone(p1->subs[oi0].zfd, -1);
+ }
+ if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) {
+ if (!p0->echocanbridged || !p1->echocanbridged) {
+ /* Disable echo cancellation if appropriate */
+ zt_disable_ec(p0);
+ zt_disable_ec(p1);
+ }
+ }
+ zt_link(slave, master);
+ master->inconference = inconf;
+ } else if (!nothingok)
+ ast_log(LOG_WARNING, "Can't link %d/%s with %d/%s\n", p0->channel, subnames[oi0], p1->channel, subnames[oi1]);
+
+ update_conf(p0);
+ update_conf(p1);
+ t0 = p0->subs[SUB_REAL].inthreeway;
+ t1 = p1->subs[SUB_REAL].inthreeway;
+
+ ast_mutex_unlock(&p0->lock);
+ ast_mutex_unlock(&p1->lock);
+
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ /* Native bridge failed */
+ if ((!master || !slave) && !nothingok) {
+ zt_enable_ec(p0);
+ zt_enable_ec(p1);
+ return AST_BRIDGE_FAILED;
+ }
+
+ ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name);
+
+ if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL))
+ disable_dtmf_detect(op0);
+
+ if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL))
+ disable_dtmf_detect(op1);
+
+ for (;;) {
+ struct ast_channel *c0_priority[2] = {c0, c1};
+ struct ast_channel *c1_priority[2] = {c1, c0};
+
+ /* Here's our main loop... Start by locking things, looking for private parts,
+ and then balking if anything is wrong */
+
+ ast_channel_lock(c0);
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+
+ p0 = c0->tech_pvt;
+ p1 = c1->tech_pvt;
+
+ if (op0 == p0)
+ i0 = zt_get_index(c0, p0, 1);
+ if (op1 == p1)
+ i1 = zt_get_index(c1, p1, 1);
+
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ if (!timeoutms ||
+ (op0 != p0) ||
+ (op1 != p1) ||
+ (ofd0 != c0->fds[0]) ||
+ (ofd1 != c1->fds[0]) ||
+ (p0->subs[SUB_REAL].owner && (os0 > -1) && (os0 != p0->subs[SUB_REAL].owner->_state)) ||
+ (p1->subs[SUB_REAL].owner && (os1 > -1) && (os1 != p1->subs[SUB_REAL].owner->_state)) ||
+ (oc0 != p0->owner) ||
+ (oc1 != p1->owner) ||
+ (t0 != p0->subs[SUB_REAL].inthreeway) ||
+ (t1 != p1->subs[SUB_REAL].inthreeway) ||
+ (oi0 != i0) ||
+ (oi1 != i1)) {
+ ast_debug(1, "Something changed out on %d/%d to %d/%d, returning -3 to restart\n",
+ op0->channel, oi0, op1->channel, oi1);
+ res = AST_BRIDGE_RETRY;
+ goto return_from_bridge;
+ }
+
+#ifdef PRI_2BCT
+ q931c0 = p0->call;
+ q931c1 = p1->call;
+ if (p0->transfer && p1->transfer
+ && q931c0 && q931c1
+ && !triedtopribridge) {
+ pri_channel_bridge(q931c0, q931c1);
+ triedtopribridge = 1;
+ }
+#endif
+
+ who = ast_waitfor_n(priority ? c0_priority : c1_priority, 2, &timeoutms);
+ if (!who) {
+ ast_debug(1, "Ooh, empty read...\n");
+ continue;
+ }
+ f = ast_read(who);
+ if (!f || (f->frametype == AST_FRAME_CONTROL)) {
+ *fo = f;
+ *rc = who;
+ res = AST_BRIDGE_COMPLETE;
+ goto return_from_bridge;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ if ((who == c0) && p0->pulsedial) {
+ ast_write(c1, f);
+ } else if ((who == c1) && p1->pulsedial) {
+ ast_write(c0, f);
+ } else {
+ *fo = f;
+ *rc = who;
+ res = AST_BRIDGE_COMPLETE;
+ goto return_from_bridge;
+ }
+ }
+ ast_frfree(f);
+
+ /* Swap who gets priority */
+ priority = !priority;
+ }
+
+return_from_bridge:
+ if (op0 == p0)
+ zt_enable_ec(p0);
+
+ if (op1 == p1)
+ zt_enable_ec(p1);
+
+ if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL))
+ enable_dtmf_detect(op0);
+
+ if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL))
+ enable_dtmf_detect(op1);
+
+ zt_unlink(slave, master, 1);
+
+ return res;
+}
+
+static int zt_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct zt_pvt *p = newchan->tech_pvt;
+ int x;
+ ast_mutex_lock(&p->lock);
+ ast_debug(1, "New owner for channel %d is %s\n", p->channel, newchan->name);
+ if (p->owner == oldchan) {
+ p->owner = newchan;
+ }
+ for (x = 0; x < 3; x++)
+ if (p->subs[x].owner == oldchan) {
+ if (!x)
+ zt_unlink(NULL, p, 0);
+ p->subs[x].owner = newchan;
+ }
+ if (newchan->_state == AST_STATE_RINGING)
+ zt_indicate(newchan, AST_CONTROL_RINGING, NULL, 0);
+ update_conf(p);
+ ast_mutex_unlock(&p->lock);
+ return 0;
+}
+
+static int zt_ring_phone(struct zt_pvt *p)
+{
+ int x;
+ int res;
+ /* Make sure our transmit state is on hook */
+ x = 0;
+ x = ZT_ONHOOK;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
+ do {
+ x = ZT_RING;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
+ if (res) {
+ switch (errno) {
+ case EBUSY:
+ case EINTR:
+ /* Wait just in case */
+ usleep(10000);
+ continue;
+ case EINPROGRESS:
+ res = 0;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Couldn't ring the phone: %s\n", strerror(errno));
+ res = 0;
+ }
+ }
+ } while (res);
+ return res;
+}
+
+static void *ss_thread(void *data);
+
+static struct ast_channel *zt_new(struct zt_pvt *, int, int, int, int, int);
+
+static int attempt_transfer(struct zt_pvt *p)
+{
+ /* In order to transfer, we need at least one of the channels to
+ actually be in a call bridge. We can't conference two applications
+ together (but then, why would we want to?) */
+ if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
+ /* The three-way person we're about to transfer to could still be in MOH, so
+ stop if now if appropriate */
+ if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner))
+ ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_UNHOLD);
+ if (p->subs[SUB_REAL].owner->_state == AST_STATE_RINGING) {
+ ast_indicate(ast_bridged_channel(p->subs[SUB_REAL].owner), AST_CONTROL_RINGING);
+ }
+ if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RING) {
+ tone_zone_play_tone(p->subs[SUB_THREEWAY].zfd, ZT_TONE_RINGTONE);
+ }
+ if (p->subs[SUB_REAL].owner->cdr) {
+ /* Move CDR from second channel to current one */
+ p->subs[SUB_THREEWAY].owner->cdr =
+ ast_cdr_append(p->subs[SUB_THREEWAY].owner->cdr, p->subs[SUB_REAL].owner->cdr);
+ p->subs[SUB_REAL].owner->cdr = NULL;
+ }
+ if (ast_bridged_channel(p->subs[SUB_REAL].owner)->cdr) {
+ /* Move CDR from second channel's bridge to current one */
+ p->subs[SUB_THREEWAY].owner->cdr =
+ ast_cdr_append(p->subs[SUB_THREEWAY].owner->cdr, ast_bridged_channel(p->subs[SUB_REAL].owner)->cdr);
+ ast_bridged_channel(p->subs[SUB_REAL].owner)->cdr = NULL;
+ }
+ if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, ast_bridged_channel(p->subs[SUB_REAL].owner))) {
+ ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+ ast_bridged_channel(p->subs[SUB_REAL].owner)->name, p->subs[SUB_THREEWAY].owner->name);
+ return -1;
+ }
+ /* Orphan the channel after releasing the lock */
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ unalloc_sub(p, SUB_THREEWAY);
+ } else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
+ ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+ if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RINGING) {
+ ast_indicate(ast_bridged_channel(p->subs[SUB_THREEWAY].owner), AST_CONTROL_RINGING);
+ }
+ if (p->subs[SUB_REAL].owner->_state == AST_STATE_RING) {
+ tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_RINGTONE);
+ }
+ if (p->subs[SUB_THREEWAY].owner->cdr) {
+ /* Move CDR from second channel to current one */
+ p->subs[SUB_REAL].owner->cdr =
+ ast_cdr_append(p->subs[SUB_REAL].owner->cdr, p->subs[SUB_THREEWAY].owner->cdr);
+ p->subs[SUB_THREEWAY].owner->cdr = NULL;
+ }
+ if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)->cdr) {
+ /* Move CDR from second channel's bridge to current one */
+ p->subs[SUB_REAL].owner->cdr =
+ ast_cdr_append(p->subs[SUB_REAL].owner->cdr, ast_bridged_channel(p->subs[SUB_THREEWAY].owner)->cdr);
+ ast_bridged_channel(p->subs[SUB_THREEWAY].owner)->cdr = NULL;
+ }
+ if (ast_channel_masquerade(p->subs[SUB_REAL].owner, ast_bridged_channel(p->subs[SUB_THREEWAY].owner))) {
+ ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+ ast_bridged_channel(p->subs[SUB_THREEWAY].owner)->name, p->subs[SUB_REAL].owner->name);
+ return -1;
+ }
+ /* Three-way is now the REAL */
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ ast_channel_unlock(p->subs[SUB_REAL].owner);
+ unalloc_sub(p, SUB_THREEWAY);
+ /* Tell the caller not to hangup */
+ return 1;
+ } else {
+ ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
+ p->subs[SUB_REAL].owner->name, p->subs[SUB_THREEWAY].owner->name);
+ p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ return -1;
+ }
+ return 0;
+}
+
+static int check_for_conference(struct zt_pvt *p)
+{
+ ZT_CONFINFO ci;
+ /* Fine if we already have a master, etc */
+ if (p->master || (p->confno > -1))
+ return 0;
+ memset(&ci, 0, sizeof(ci));
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_GETCONF, &ci)) {
+ ast_log(LOG_WARNING, "Failed to get conference info on channel %d\n", p->channel);
+ return 0;
+ }
+ /* If we have no master and don't have a confno, then
+ if we're in a conference, it's probably a MeetMe room or
+ some such, so don't let us 3-way out! */
+ if ((p->subs[SUB_REAL].curconf.confno != ci.confno) || (p->subs[SUB_REAL].curconf.confmode != ci.confmode)) {
+ ast_verb(3, "Avoiding 3-way call when in an external conference\n");
+ return 1;
+ }
+ return 0;
+}
+
+/*! Checks channel for alarms
+ * \param p a channel to check for alarms.
+ * \returns the alarms on the span to which the channel belongs, or alarms on
+ * the channel if no span alarms.
+ */
+static int get_alarms(struct zt_pvt *p)
+{
+ int res;
+ ZT_SPANINFO zi;
+#if defined(HAVE_ZAPTEL_CHANALARMS)
+ struct zt_params params;
+#endif
+
+ memset(&zi, 0, sizeof(zi));
+ zi.spanno = p->span;
+ if ((res = ioctl(p->subs[SUB_REAL].zfd, ZT_SPANSTAT, &zi)) >= 0) {
+ if (zi.alarms != ZT_ALARM_NONE)
+ return zi.alarms;
+ }
+
+#if defined(HAVE_ZAPTEL_CHANALARMS)
+ /* No alarms on the span. Check for channel alarms. */
+ if ((res = ioctl(p->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &params)) >= 0)
+ return params.chan_alarms;
+#endif
+
+ ast_log(LOG_WARNING, "Unable to determine alarm on channel %d\n", p->channel);
+
+ return ZT_ALARM_NONE;
+}
+
+static void zt_handle_dtmfup(struct ast_channel *ast, int index, struct ast_frame **dest)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ struct ast_frame *f = *dest;
+
+ ast_debug(1, "DTMF digit: %c on %s\n", f->subclass, ast->name);
+
+ if (p->confirmanswer) {
+ ast_debug(1, "Confirm answer on %s!\n", ast->name);
+ /* Upon receiving a DTMF digit, consider this an answer confirmation instead
+ of a DTMF digit */
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ *dest = &p->subs[index].f;
+ /* Reset confirmanswer so DTMF's will behave properly for the duration of the call */
+ p->confirmanswer = 0;
+ } else if (p->callwaitcas) {
+ if ((f->subclass == 'A') || (f->subclass == 'D')) {
+ ast_debug(1, "Got some DTMF, but it's for the CAS\n");
+ if (p->cidspill)
+ ast_free(p->cidspill);
+ send_cwcidspill(p);
+ }
+ if ((f->subclass != 'm') && (f->subclass != 'u'))
+ p->callwaitcas = 0;
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ *dest = &p->subs[index].f;
+ } else if (f->subclass == 'f') {
+ /* Fax tone -- Handle and return NULL */
+ if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) {
+ p->faxhandled++;
+ if (strcmp(ast->exten, "fax")) {
+ const char *target_context = S_OR(ast->macrocontext, ast->context);
+
+ if (ast_exists_extension(ast, target_context, "fax", 1, ast->cid.cid_num)) {
+ ast_verb(3, "Redirecting %s to fax extension\n", ast->name);
+ /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
+ pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
+ if (ast_async_goto(ast, target_context, "fax", 1))
+ ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context);
+ } else
+ ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
+ } else
+ ast_debug(1, "Already in a fax extension, not redirecting\n");
+ } else
+ ast_debug(1, "Fax already handled\n");
+ zt_confmute(p, 0);
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ *dest = &p->subs[index].f;
+ } else if (f->subclass == 'm') {
+ /* Confmute request */
+ zt_confmute(p, 1);
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ *dest = &p->subs[index].f;
+ } else if (f->subclass == 'u') {
+ /* Unmute */
+ zt_confmute(p, 0);
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ *dest = &p->subs[index].f;
+ } else
+ zt_confmute(p, 0);
+}
+
+static struct ast_frame *zt_handle_event(struct ast_channel *ast)
+{
+ int res, x;
+ int index, mysig;
+ char *c;
+ struct zt_pvt *p = ast->tech_pvt;
+ pthread_t threadid;
+ struct ast_channel *chan;
+ struct ast_frame *f;
+
+ index = zt_get_index(ast, p, 0);
+ mysig = p->sig;
+ if (p->outsigmod > -1)
+ mysig = p->outsigmod;
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ p->subs[index].f.datalen = 0;
+ p->subs[index].f.samples = 0;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = 0;
+ p->subs[index].f.src = "zt_handle_event";
+ p->subs[index].f.data = NULL;
+ f = &p->subs[index].f;
+
+ if (index < 0)
+ return &p->subs[index].f;
+ if (p->fake_event) {
+ res = p->fake_event;
+ p->fake_event = 0;
+ } else
+ res = zt_get_event(p->subs[index].zfd);
+
+ ast_debug(1, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index);
+
+ if (res & (ZT_EVENT_PULSEDIGIT | ZT_EVENT_DTMFUP)) {
+ p->pulsedial = (res & ZT_EVENT_PULSEDIGIT) ? 1 : 0;
+ ast_debug(1, "Detected %sdigit '%c'\n", p->pulsedial ? "pulse ": "", res & 0xff);
+#ifdef HAVE_PRI
+ if (!p->proceeding && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && (p->pri->overlapdial & ZAP_OVERLAPDIAL_INCOMING)) {
+ /* absorb event */
+ } else {
+#endif
+ p->subs[index].f.frametype = AST_FRAME_DTMF_END;
+ p->subs[index].f.subclass = res & 0xff;
+#ifdef HAVE_PRI
+ }
+#endif
+ zt_handle_dtmfup(ast, index, &f);
+ return f;
+ }
+
+ if (res & ZT_EVENT_DTMFDOWN) {
+ ast_debug(1, "DTMF Down '%c'\n", res & 0xff);
+ /* Mute conference */
+ zt_confmute(p, 1);
+ p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
+ p->subs[index].f.subclass = res & 0xff;
+ return &p->subs[index].f;
+ }
+
+ switch (res) {
+#ifdef ZT_EVENT_EC_DISABLED
+ case ZT_EVENT_EC_DISABLED:
+ ast_verb(3, "Channel %d echo canceler disabled due to CED detection\n", p->channel);
+ p->echocanon = 0;
+ break;
+#endif
+ case ZT_EVENT_BITSCHANGED:
+ ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
+ case ZT_EVENT_PULSE_START:
+ /* Stop tone if there's a pulse start and the PBX isn't started */
+ if (!ast->pbx)
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+ break;
+ case ZT_EVENT_DIALCOMPLETE:
+ if (p->inalarm) break;
+ if ((p->radio || (p->oprmode < 0))) break;
+ if (ioctl(p->subs[index].zfd,ZT_DIALING,&x) == -1) {
+ ast_debug(1, "ZT_DIALING ioctl failed on %s\n",ast->name);
+ return NULL;
+ }
+ if (!x) { /* if not still dialing in driver */
+ zt_enable_ec(p);
+ if (p->echobreak) {
+ zt_train_ec(p);
+ ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr));
+ p->dop.op = ZT_DIAL_OP_REPLACE;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop);
+ p->echobreak = 0;
+ } else {
+ p->dialing = 0;
+ if ((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) {
+ /* if thru with dialing after offhook */
+ if (ast->_state == AST_STATE_DIALING_OFFHOOK) {
+ ast_setstate(ast, AST_STATE_UP);
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ break;
+ } else { /* if to state wait for offhook to dial rest */
+ /* we now wait for off hook */
+ ast_setstate(ast,AST_STATE_DIALING_OFFHOOK);
+ }
+ }
+ if (ast->_state == AST_STATE_DIALING) {
+ if ((p->callprogress & CALLPROGRESS_PROGRESS) && CANPROGRESSDETECT(p) && p->dsp && p->outgoing) {
+ ast_debug(1, "Done dialing, but waiting for progress detection before doing more...\n");
+ } else if (p->confirmanswer || (!p->dialednone && ((mysig == SIG_EM) || (mysig == SIG_EM_E1) || (mysig == SIG_EMWINK) || (mysig == SIG_FEATD) || (mysig == SIG_FEATDMF_TA) || (mysig == SIG_FEATDMF) || (mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF) || (mysig == SIG_FEATB) || (mysig == SIG_SF) || (mysig == SIG_SFWINK) || (mysig == SIG_SF_FEATD) || (mysig == SIG_SF_FEATDMF) || (mysig == SIG_SF_FEATB)))) {
+ ast_setstate(ast, AST_STATE_RINGING);
+ } else if (!p->answeronpolarityswitch) {
+ ast_setstate(ast, AST_STATE_UP);
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ /* If aops=0 and hops=1, this is necessary */
+ p->polarity = POLARITY_REV;
+ } else {
+ /* Start clean, so we can catch the change to REV polarity when party answers */
+ p->polarity = POLARITY_IDLE;
+ }
+ }
+ }
+ }
+ break;
+ case ZT_EVENT_ALARM:
+#ifdef HAVE_PRI
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
+ if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) {
+ /* T309 is not enabled : hangup calls when alarm occurs */
+ if (p->call) {
+ if (p->pri && p->pri->pri) {
+ if (!pri_grab(p, p->pri)) {
+ pri_hangup(p->pri->pri, p->call, -1);
+ pri_destroycall(p->pri->pri, p->call);
+ p->call = NULL;
+ pri_rel(p->pri);
+ } else
+ ast_log(LOG_WARNING, "Failed to grab PRI!\n");
+ } else
+ ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n");
+ }
+ if (p->owner)
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+ }
+ if (p->bearer)
+ p->bearer->inalarm = 1;
+ else
+#endif
+ p->inalarm = 1;
+ res = get_alarms(p);
+ ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", p->channel, alarm2str(res));
+ manager_event(EVENT_FLAG_SYSTEM, "Alarm",
+ "Alarm: %s\r\n"
+ "Channel: %d\r\n",
+ alarm2str(res), p->channel);
+#ifdef HAVE_PRI
+ if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) {
+ /* fall through intentionally */
+ } else {
+ break;
+ }
+#endif
+#ifdef HAVE_SS7
+ if (p->sig == SIG_SS7)
+ break;
+#endif
+ case ZT_EVENT_ONHOOK:
+ if (p->radio) {
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_RADIO_UNKEY;
+ break;
+ }
+ if (p->oprmode < 0)
+ {
+ if (p->oprmode != -1) break;
+ if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS))
+ {
+ /* Make sure it starts ringing */
+ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_RINGOFF);
+ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_RING);
+ save_conference(p->oprpeer);
+ tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].zfd, ZT_TONE_RINGTONE);
+ }
+ break;
+ }
+ switch (p->sig) {
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ p->onhooktime = time(NULL);
+ p->msgstate = -1;
+ /* Check for some special conditions regarding call waiting */
+ if (index == SUB_REAL) {
+ /* The normal line was hung up */
+ if (p->subs[SUB_CALLWAIT].owner) {
+ /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */
+ swap_subs(p, SUB_CALLWAIT, SUB_REAL);
+ ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel);
+ unalloc_sub(p, SUB_CALLWAIT);
+#if 0
+ p->subs[index].needanswer = 0;
+ p->subs[index].needringing = 0;
+#endif
+ p->callwaitingrepeat = 0;
+ p->cidcwexpire = 0;
+ p->owner = NULL;
+ /* Don't start streaming audio yet if the incoming call isn't up yet */
+ if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP)
+ p->dialing = 1;
+ zt_ring_phone(p);
+ } else if (p->subs[SUB_THREEWAY].owner) {
+ unsigned int mssinceflash;
+ /* Here we have to retain the lock on both the main channel, the 3-way channel, and
+ the private structure -- not especially easy or clean */
+ while (p->subs[SUB_THREEWAY].owner && ast_channel_trylock(p->subs[SUB_THREEWAY].owner)) {
+ /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */
+ ast_mutex_unlock(&p->lock);
+ ast_channel_unlock(ast);
+ usleep(1);
+ /* We can grab ast and p in that order, without worry. We should make sure
+ nothing seriously bad has happened though like some sort of bizarre double
+ masquerade! */
+ ast_channel_lock(ast);
+ ast_mutex_lock(&p->lock);
+ if (p->owner != ast) {
+ ast_log(LOG_WARNING, "This isn't good...\n");
+ return NULL;
+ }
+ }
+ if (!p->subs[SUB_THREEWAY].owner) {
+ ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n");
+ return NULL;
+ }
+ mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime);
+ ast_debug(1, "Last flash was %d ms ago\n", mssinceflash);
+ if (mssinceflash < MIN_MS_SINCE_FLASH) {
+ /* It hasn't been long enough since the last flashook. This is probably a bounce on
+ hanging up. Hangup both channels now */
+ if (p->subs[SUB_THREEWAY].owner)
+ ast_queue_hangup(p->subs[SUB_THREEWAY].owner);
+ p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel);
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ } else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) {
+ if (p->transfer) {
+ /* In any case this isn't a threeway call anymore */
+ p->subs[SUB_REAL].inthreeway = 0;
+ p->subs[SUB_THREEWAY].inthreeway = 0;
+ /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */
+ if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) {
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ /* Swap subs and dis-own channel */
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ p->owner = NULL;
+ /* Ring the phone */
+ zt_ring_phone(p);
+ } else {
+ if ((res = attempt_transfer(p)) < 0) {
+ p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ if (p->subs[SUB_THREEWAY].owner)
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ } else if (res) {
+ /* Don't actually hang up at this point */
+ if (p->subs[SUB_THREEWAY].owner)
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ break;
+ }
+ }
+ } else {
+ p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ if (p->subs[SUB_THREEWAY].owner)
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ }
+ } else {
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ /* Swap subs and dis-own channel */
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ p->owner = NULL;
+ /* Ring the phone */
+ zt_ring_phone(p);
+ }
+ }
+ } else {
+ ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", index);
+ }
+ /* Fall through */
+ default:
+ zt_disable_ec(p);
+ return NULL;
+ }
+ break;
+ case ZT_EVENT_RINGOFFHOOK:
+ if (p->inalarm) break;
+ if (p->oprmode < 0)
+ {
+ if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS))
+ {
+ /* Make sure it stops ringing */
+ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_RINGOFF);
+ tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].zfd, -1);
+ restore_conference(p->oprpeer);
+ }
+ break;
+ }
+ if (p->radio)
+ {
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_RADIO_KEY;
+ break;
+ }
+ /* for E911, its supposed to wait for offhook then dial
+ the second half of the dial string */
+ if (((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) {
+ c = strchr(p->dialdest, '/');
+ if (c)
+ c++;
+ else
+ c = p->dialdest;
+ if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c);
+ else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr));
+ if (strlen(p->dop.dialstr) > 4) {
+ memset(p->echorest, 'w', sizeof(p->echorest) - 1);
+ strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
+ p->echorest[sizeof(p->echorest) - 1] = '\0';
+ p->echobreak = 1;
+ p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
+ } else
+ p->echobreak = 0;
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop)) {
+ x = ZT_ONHOOK;
+ ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
+ ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(errno));
+ return NULL;
+ }
+ p->dialing = 1;
+ return &p->subs[index].f;
+ }
+ switch (p->sig) {
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ switch (ast->_state) {
+ case AST_STATE_RINGING:
+ zt_enable_ec(p);
+ zt_train_ec(p);
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ /* Make sure it stops ringing */
+ zt_set_hook(p->subs[index].zfd, ZT_OFFHOOK);
+ ast_debug(1, "channel %d answered\n", p->channel);
+ if (p->cidspill) {
+ /* Cancel any running CallerID spill */
+ ast_free(p->cidspill);
+ p->cidspill = NULL;
+ }
+ p->dialing = 0;
+ p->callwaitcas = 0;
+ if (p->confirmanswer) {
+ /* Ignore answer if "confirm answer" is enabled */
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ } else if (!ast_strlen_zero(p->dop.dialstr)) {
+ /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d\n", p->channel);
+ p->dop.dialstr[0] = '\0';
+ return NULL;
+ } else {
+ ast_debug(1, "Sent FXO deferred digit string: %s\n", p->dop.dialstr);
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ p->dialing = 1;
+ }
+ p->dop.dialstr[0] = '\0';
+ ast_setstate(ast, AST_STATE_DIALING);
+ } else
+ ast_setstate(ast, AST_STATE_UP);
+ return &p->subs[index].f;
+ case AST_STATE_DOWN:
+ ast_setstate(ast, AST_STATE_RING);
+ ast->rings = 1;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_OFFHOOK;
+ ast_debug(1, "channel %d picked up\n", p->channel);
+ return &p->subs[index].f;
+ case AST_STATE_UP:
+ /* Make sure it stops ringing */
+ zt_set_hook(p->subs[index].zfd, ZT_OFFHOOK);
+ /* Okay -- probably call waiting*/
+ if (ast_bridged_channel(p->owner))
+ ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+ p->subs[index].needunhold = 1;
+ break;
+ case AST_STATE_RESERVED:
+ /* Start up dialtone */
+ if (has_voicemail(p))
+ res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_STUTTER);
+ else
+ res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE);
+ break;
+ default:
+ ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state);
+ }
+ break;
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+ if (ast->_state == AST_STATE_RING) {
+ p->ringt = p->ringt_base;
+ }
+
+ /* If we get a ring then we cannot be in
+ * reversed polarity. So we reset to idle */
+ ast_debug(1, "Setting IDLE polarity due "
+ "to ring. Old polarity was %d\n",
+ p->polarity);
+ p->polarity = POLARITY_IDLE;
+
+ /* Fall through */
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_EMWINK:
+ case SIG_FEATD:
+ case SIG_FEATDMF:
+ case SIG_FEATDMF_TA:
+ case SIG_E911:
+ case SIG_FGC_CAMA:
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ case SIG_SF:
+ case SIG_SFWINK:
+ case SIG_SF_FEATD:
+ case SIG_SF_FEATDMF:
+ case SIG_SF_FEATB:
+ if (ast->_state == AST_STATE_PRERING)
+ ast_setstate(ast, AST_STATE_RING);
+ if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) {
+ ast_debug(1, "Ring detected\n");
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_RING;
+ } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) {
+ ast_debug(1, "Line answered\n");
+ if (p->confirmanswer) {
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ } else {
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ ast_setstate(ast, AST_STATE_UP);
+ }
+ } else if (ast->_state != AST_STATE_RING)
+ ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig);
+ }
+ break;
+#ifdef ZT_EVENT_RINGBEGIN
+ case ZT_EVENT_RINGBEGIN:
+ switch (p->sig) {
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+ if (ast->_state == AST_STATE_RING) {
+ p->ringt = p->ringt_base;
+ }
+ break;
+ }
+ break;
+#endif
+ case ZT_EVENT_RINGEROFF:
+ if (p->inalarm) break;
+ if ((p->radio || (p->oprmode < 0))) break;
+ ast->rings++;
+ if ((ast->rings > p->cidrings) && (p->cidspill)) {
+ ast_log(LOG_WARNING, "Didn't finish Caller-ID spill. Cancelling.\n");
+ ast_free(p->cidspill);
+ p->cidspill = NULL;
+ p->callwaitcas = 0;
+ }
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_RINGING;
+ break;
+ case ZT_EVENT_RINGERON:
+ break;
+ case ZT_EVENT_NOALARM:
+ p->inalarm = 0;
+#ifdef HAVE_PRI
+ /* Extremely unlikely but just in case */
+ if (p->bearer)
+ p->bearer->inalarm = 0;
+#endif
+ ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel);
+ manager_event(EVENT_FLAG_SYSTEM, "AlarmClear",
+ "Channel: %d\r\n", p->channel);
+ break;
+ case ZT_EVENT_WINKFLASH:
+ if (p->inalarm) break;
+ if (p->radio) break;
+ if (p->oprmode < 0) break;
+ if (p->oprmode > 1)
+ {
+ struct zt_params par;
+
+ if (ioctl(p->oprpeer->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &par) != -1)
+ {
+ if (!par.rxisoffhook)
+ {
+ /* Make sure it stops ringing */
+ zt_set_hook(p->oprpeer->subs[SUB_REAL].zfd, ZT_RINGOFF);
+ zt_set_hook(p->oprpeer->subs[SUB_REAL].zfd, ZT_RING);
+ save_conference(p);
+ tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_RINGTONE);
+ }
+ }
+ break;
+ }
+ /* Remember last time we got a flash-hook */
+ p->flashtime = ast_tvnow();
+ switch (mysig) {
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n",
+ index, p->subs[SUB_REAL].zfd, p->subs[SUB_CALLWAIT].zfd, p->subs[SUB_THREEWAY].zfd);
+ p->callwaitcas = 0;
+
+ if (index != SUB_REAL) {
+ ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", index, p->channel);
+ goto winkflashdone;
+ }
+
+ if (p->subs[SUB_CALLWAIT].owner) {
+ /* Swap to call-wait */
+ swap_subs(p, SUB_REAL, SUB_CALLWAIT);
+ tone_zone_play_tone(p->subs[SUB_REAL].zfd, -1);
+ p->owner = p->subs[SUB_REAL].owner;
+ ast_debug(1, "Making %s the new owner\n", p->owner->name);
+ if (p->owner->_state == AST_STATE_RINGING) {
+ ast_setstate(p->owner, AST_STATE_UP);
+ p->subs[SUB_REAL].needanswer = 1;
+ }
+ p->callwaitingrepeat = 0;
+ p->cidcwexpire = 0;
+ /* Start music on hold if appropriate */
+ if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
+ ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
+ S_OR(p->mohsuggest, NULL),
+ !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+ }
+ p->subs[SUB_CALLWAIT].needhold = 1;
+ if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
+ ast_queue_control_data(p->subs[SUB_REAL].owner, AST_CONTROL_HOLD,
+ S_OR(p->mohsuggest, NULL),
+ !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+ }
+ p->subs[SUB_REAL].needunhold = 1;
+ } else if (!p->subs[SUB_THREEWAY].owner) {
+ char cid_num[256];
+ char cid_name[256];
+
+ if (!p->threewaycalling) {
+ /* Just send a flash if no 3-way calling */
+ p->subs[SUB_REAL].needflash = 1;
+ goto winkflashdone;
+ } else if (!check_for_conference(p)) {
+ if (p->zaptrcallerid && p->owner) {
+ if (p->owner->cid.cid_num)
+ ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num));
+ if (p->owner->cid.cid_name)
+ ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name));
+ }
+ /* XXX This section needs much more error checking!!! XXX */
+ /* Start a 3-way call if feasible */
+ if (!((ast->pbx) ||
+ (ast->_state == AST_STATE_UP) ||
+ (ast->_state == AST_STATE_RING))) {
+ ast_debug(1, "Flash when call not up or ringing\n");
+ goto winkflashdone;
+ }
+ if (alloc_sub(p, SUB_THREEWAY)) {
+ ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n");
+ goto winkflashdone;
+ }
+ /* Make new channel */
+ chan = zt_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0);
+ if (p->zaptrcallerid) {
+ if (!p->origcid_num)
+ p->origcid_num = ast_strdup(p->cid_num);
+ if (!p->origcid_name)
+ p->origcid_name = ast_strdup(p->cid_name);
+ ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num));
+ ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name));
+ }
+ /* Swap things around between the three-way and real call */
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ /* Disable echo canceller for better dialing */
+ zt_disable_ec(p);
+ res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_DIALRECALL);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
+ p->owner = chan;
+ if (!chan) {
+ ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel);
+ } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+ ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
+ res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
+ zt_enable_ec(p);
+ ast_hangup(chan);
+ } else {
+ ast_verb(3, "Started three way call on channel %d\n", p->channel);
+ /* Start music on hold if appropriate */
+ if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
+ ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD,
+ S_OR(p->mohsuggest, NULL),
+ !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+ }
+ p->subs[SUB_THREEWAY].needhold = 1;
+ }
+ }
+ } else {
+ /* Already have a 3 way call */
+ if (p->subs[SUB_THREEWAY].inthreeway) {
+ /* Call is already up, drop the last person */
+ ast_debug(1, "Got flash with three way call up, dropping last call on %d\n", p->channel);
+ /* If the primary call isn't answered yet, use it */
+ if ((p->subs[SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_UP)) {
+ /* Swap back -- we're dropping the real 3-way that isn't finished yet*/
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ p->owner = p->subs[SUB_REAL].owner;
+ }
+ /* Drop the last call and stop the conference */
+ ast_verb(3, "Dropping three-way call on %s\n", p->subs[SUB_THREEWAY].owner->name);
+ p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ p->subs[SUB_REAL].inthreeway = 0;
+ p->subs[SUB_THREEWAY].inthreeway = 0;
+ } else {
+ /* Lets see what we're up to */
+ if (((ast->pbx) || (ast->_state == AST_STATE_UP)) &&
+ (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) {
+ int otherindex = SUB_THREEWAY;
+
+ ast_verb(3, "Building conference on call on %s and %s\n", p->subs[SUB_THREEWAY].owner->name, p->subs[SUB_REAL].owner->name);
+ /* Put them in the threeway, and flip */
+ p->subs[SUB_THREEWAY].inthreeway = 1;
+ p->subs[SUB_REAL].inthreeway = 1;
+ if (ast->_state == AST_STATE_UP) {
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ otherindex = SUB_REAL;
+ }
+ if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner))
+ ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD);
+ p->subs[otherindex].needunhold = 1;
+ p->owner = p->subs[SUB_REAL].owner;
+ if (ast->_state == AST_STATE_RINGING) {
+ ast_debug(1, "Enabling ringtone on real and threeway\n");
+ res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_RINGTONE);
+ res = tone_zone_play_tone(p->subs[SUB_THREEWAY].zfd, ZT_TONE_RINGTONE);
+ }
+ } else {
+ ast_verb(3, "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name);
+ swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ p->owner = p->subs[SUB_REAL].owner;
+ if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner))
+ ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+ p->subs[SUB_REAL].needunhold = 1;
+ zt_enable_ec(p);
+ }
+
+ }
+ }
+ winkflashdone:
+ update_conf(p);
+ break;
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_EMWINK:
+ case SIG_FEATD:
+ case SIG_SF:
+ case SIG_SFWINK:
+ case SIG_SF_FEATD:
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ if (option_debug) {
+ if (p->dialing)
+ ast_debug(1, "Ignoring wink on channel %d\n", p->channel);
+ else
+ ast_debug(1, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel);
+ }
+ break;
+ case SIG_FEATDMF_TA:
+ switch (p->whichwink) {
+ case 0:
+ ast_debug(1, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
+ snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
+ break;
+ case 1:
+ ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr));
+ break;
+ case 2:
+ ast_log(LOG_WARNING, "Received unexpected wink on channel of type SIG_FEATDMF_TA\n");
+ return NULL;
+ }
+ p->whichwink++;
+ /* Fall through */
+ case SIG_FEATDMF:
+ case SIG_E911:
+ case SIG_FGC_CAMAMF:
+ case SIG_FGC_CAMA:
+ case SIG_FEATB:
+ case SIG_SF_FEATDMF:
+ case SIG_SF_FEATB:
+ /* FGD MF *Must* wait for wink */
+ if (!ast_strlen_zero(p->dop.dialstr))
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop);
+ else if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d\n", p->channel);
+ p->dop.dialstr[0] = '\0';
+ return NULL;
+ } else
+ ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr);
+
+ p->dop.dialstr[0] = '\0';
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig);
+ }
+ break;
+ case ZT_EVENT_HOOKCOMPLETE:
+ if (p->inalarm) break;
+ if ((p->radio || (p->oprmode < 0))) break;
+ switch (mysig) {
+ case SIG_FXSLS: /* only interesting for FXS */
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_EMWINK:
+ case SIG_FEATD:
+ case SIG_SF:
+ case SIG_SFWINK:
+ case SIG_SF_FEATD:
+ if (!ast_strlen_zero(p->dop.dialstr))
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop);
+ else if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d\n", p->channel);
+ p->dop.dialstr[0] = '\0';
+ return NULL;
+ } else
+ ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr);
+
+ p->dop.dialstr[0] = '\0';
+ p->dop.op = ZT_DIAL_OP_REPLACE;
+ break;
+ case SIG_FEATDMF:
+ case SIG_FEATDMF_TA:
+ case SIG_E911:
+ case SIG_FGC_CAMA:
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ case SIG_SF_FEATDMF:
+ case SIG_SF_FEATB:
+ ast_debug(1, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel);
+ break;
+ default:
+ break;
+ }
+ break;
+ case ZT_EVENT_POLARITY:
+ /*
+ * If we get a Polarity Switch event, check to see
+ * if we should change the polarity state and
+ * mark the channel as UP or if this is an indication
+ * of remote end disconnect.
+ */
+ if (p->polarity == POLARITY_IDLE) {
+ p->polarity = POLARITY_REV;
+ if (p->answeronpolarityswitch &&
+ ((ast->_state == AST_STATE_DIALING) ||
+ (ast->_state == AST_STATE_RINGING))) {
+ ast_debug(1, "Answering on polarity switch!\n");
+ ast_setstate(p->owner, AST_STATE_UP);
+ if (p->hanguponpolarityswitch) {
+ p->polaritydelaytv = ast_tvnow();
+ }
+ } else
+ ast_debug(1, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state);
+
+ }
+ /* Removed else statement from here as it was preventing hangups from ever happening*/
+ /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */
+ if (p->hanguponpolarityswitch &&
+ (p->polarityonanswerdelay > 0) &&
+ (p->polarity == POLARITY_REV) &&
+ ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) {
+ /* Added log_debug information below to provide a better indication of what is going on */
+ ast_debug(1, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
+
+ if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) {
+ ast_debug(1, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel);
+ ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
+ p->polarity = POLARITY_IDLE;
+ } else
+ ast_debug(1, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state);
+
+ } else {
+ p->polarity = POLARITY_IDLE;
+ ast_debug(1, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state);
+ }
+ /* Added more log_debug information below to provide a better indication of what is going on */
+ ast_debug(1, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
+ break;
+ default:
+ ast_debug(1, "Dunno what to do with event %d on channel %d\n", res, p->channel);
+ }
+ return &p->subs[index].f;
+}
+
+static struct ast_frame *__zt_exception(struct ast_channel *ast)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ int res;
+ int usedindex=-1;
+ int index;
+ struct ast_frame *f;
+
+
+ index = zt_get_index(ast, p, 1);
+
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.datalen = 0;
+ p->subs[index].f.samples = 0;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = 0;
+ p->subs[index].f.subclass = 0;
+ p->subs[index].f.delivery = ast_tv(0,0);
+ p->subs[index].f.src = "zt_exception";
+ p->subs[index].f.data = NULL;
+
+
+ if ((!p->owner) && (!(p->radio || (p->oprmode < 0)))) {
+ /* If nobody owns us, absorb the event appropriately, otherwise
+ we loop indefinitely. This occurs when, during call waiting, the
+ other end hangs up our channel so that it no longer exists, but we
+ have neither FLASH'd nor ONHOOK'd to signify our desire to
+ change to the other channel. */
+ if (p->fake_event) {
+ res = p->fake_event;
+ p->fake_event = 0;
+ } else
+ res = zt_get_event(p->subs[SUB_REAL].zfd);
+ /* Switch to real if there is one and this isn't something really silly... */
+ if ((res != ZT_EVENT_RINGEROFF) && (res != ZT_EVENT_RINGERON) &&
+ (res != ZT_EVENT_HOOKCOMPLETE)) {
+ ast_debug(1, "Restoring owner of channel %d on event %d\n", p->channel, res);
+ p->owner = p->subs[SUB_REAL].owner;
+ if (p->owner && ast_bridged_channel(p->owner))
+ ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+ p->subs[SUB_REAL].needunhold = 1;
+ }
+ switch (res) {
+ case ZT_EVENT_ONHOOK:
+ zt_disable_ec(p);
+ if (p->owner) {
+ ast_verb(3, "Channel %s still has call, ringing phone\n", p->owner->name);
+ zt_ring_phone(p);
+ p->callwaitingrepeat = 0;
+ p->cidcwexpire = 0;
+ } else
+ ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+ update_conf(p);
+ break;
+ case ZT_EVENT_RINGOFFHOOK:
+ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+ if (p->owner && (p->owner->_state == AST_STATE_RINGING)) {
+ p->subs[SUB_REAL].needanswer = 1;
+ p->dialing = 0;
+ }
+ break;
+ case ZT_EVENT_HOOKCOMPLETE:
+ case ZT_EVENT_RINGERON:
+ case ZT_EVENT_RINGEROFF:
+ /* Do nothing */
+ break;
+ case ZT_EVENT_WINKFLASH:
+ p->flashtime = ast_tvnow();
+ if (p->owner) {
+ ast_verb(3, "Channel %d flashed to other channel %s\n", p->channel, p->owner->name);
+ if (p->owner->_state != AST_STATE_UP) {
+ /* Answer if necessary */
+ usedindex = zt_get_index(p->owner, p, 0);
+ if (usedindex > -1) {
+ p->subs[usedindex].needanswer = 1;
+ }
+ ast_setstate(p->owner, AST_STATE_UP);
+ }
+ p->callwaitingrepeat = 0;
+ p->cidcwexpire = 0;
+ if (ast_bridged_channel(p->owner))
+ ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+ p->subs[SUB_REAL].needunhold = 1;
+ } else
+ ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+ update_conf(p);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res));
+ }
+ f = &p->subs[index].f;
+ return f;
+ }
+ if (!(p->radio || (p->oprmode < 0)))
+ ast_debug(1, "Exception on %d, channel %d\n", ast->fds[0],p->channel);
+ /* If it's not us, return NULL immediately */
+ if (ast != p->owner) {
+ ast_log(LOG_WARNING, "We're %s, not %s\n", ast->name, p->owner->name);
+ f = &p->subs[index].f;
+ return f;
+ }
+ f = zt_handle_event(ast);
+ return f;
+}
+
+static struct ast_frame *zt_exception(struct ast_channel *ast)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ struct ast_frame *f;
+ ast_mutex_lock(&p->lock);
+ f = __zt_exception(ast);
+ ast_mutex_unlock(&p->lock);
+ return f;
+}
+
+static struct ast_frame *zt_read(struct ast_channel *ast)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ int res;
+ int index;
+ void *readbuf;
+ struct ast_frame *f;
+
+
+ ast_mutex_lock(&p->lock);
+
+ index = zt_get_index(ast, p, 0);
+
+ /* Hang up if we don't really exist */
+ if (index < 0) {
+ ast_log(LOG_WARNING, "We dont exist?\n");
+ ast_mutex_unlock(&p->lock);
+ return NULL;
+ }
+
+ if ((p->radio || (p->oprmode < 0)) && p->inalarm) return NULL;
+
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.datalen = 0;
+ p->subs[index].f.samples = 0;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = 0;
+ p->subs[index].f.subclass = 0;
+ p->subs[index].f.delivery = ast_tv(0,0);
+ p->subs[index].f.src = "zt_read";
+ p->subs[index].f.data = NULL;
+
+ /* make sure it sends initial key state as first frame */
+ if ((p->radio || (p->oprmode < 0)) && (!p->firstradio))
+ {
+ ZT_PARAMS ps;
+
+ ps.channo = p->channel;
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &ps) < 0) {
+ ast_mutex_unlock(&p->lock);
+ return NULL;
+ }
+ p->firstradio = 1;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ if (ps.rxisoffhook)
+ {
+ p->subs[index].f.subclass = AST_CONTROL_RADIO_KEY;
+ }
+ else
+ {
+ p->subs[index].f.subclass = AST_CONTROL_RADIO_UNKEY;
+ }
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+ if (p->ringt == 1) {
+ ast_mutex_unlock(&p->lock);
+ return NULL;
+ }
+ else if (p->ringt > 0)
+ p->ringt--;
+
+ if (p->subs[index].needringing) {
+ /* Send ringing frame if requested */
+ p->subs[index].needringing = 0;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_RINGING;
+ ast_setstate(ast, AST_STATE_RINGING);
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+
+ if (p->subs[index].needbusy) {
+ /* Send busy frame if requested */
+ p->subs[index].needbusy = 0;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_BUSY;
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+
+ if (p->subs[index].needcongestion) {
+ /* Send congestion frame if requested */
+ p->subs[index].needcongestion = 0;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_CONGESTION;
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+
+ if (p->subs[index].needcallerid) {
+ ast_set_callerid(ast, S_OR(p->lastcid_num, NULL),
+ S_OR(p->lastcid_name, NULL),
+ S_OR(p->lastcid_num, NULL)
+ );
+ p->subs[index].needcallerid = 0;
+ }
+
+ if (p->subs[index].needanswer) {
+ /* Send answer frame if requested */
+ p->subs[index].needanswer = 0;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+
+ if (p->subs[index].needflash) {
+ /* Send answer frame if requested */
+ p->subs[index].needflash = 0;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_FLASH;
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+
+ if (p->subs[index].needhold) {
+ /* Send answer frame if requested */
+ p->subs[index].needhold = 0;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_HOLD;
+ ast_mutex_unlock(&p->lock);
+ ast_debug(1, "Sending hold on '%s'\n", ast->name);
+ return &p->subs[index].f;
+ }
+
+ if (p->subs[index].needunhold) {
+ /* Send answer frame if requested */
+ p->subs[index].needunhold = 0;
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_UNHOLD;
+ ast_mutex_unlock(&p->lock);
+ ast_debug(1, "Sending unhold on '%s'\n", ast->name);
+ return &p->subs[index].f;
+ }
+
+ if (ast->rawreadformat == AST_FORMAT_SLINEAR) {
+ if (!p->subs[index].linear) {
+ p->subs[index].linear = 1;
+ res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set channel %d (index %d) to linear mode.\n", p->channel, index);
+ }
+ } else if ((ast->rawreadformat == AST_FORMAT_ULAW) ||
+ (ast->rawreadformat == AST_FORMAT_ALAW)) {
+ if (p->subs[index].linear) {
+ p->subs[index].linear = 0;
+ res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set channel %d (index %d) to companded mode.\n", p->channel, index);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Don't know how to read frames in format %s\n", ast_getformatname(ast->rawreadformat));
+ ast_mutex_unlock(&p->lock);
+ return NULL;
+ }
+ readbuf = ((unsigned char *)p->subs[index].buffer) + AST_FRIENDLY_OFFSET;
+ CHECK_BLOCKING(ast);
+ res = read(p->subs[index].zfd, readbuf, p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE);
+ ast_clear_flag(ast, AST_FLAG_BLOCKING);
+ /* Check for hangup */
+ if (res < 0) {
+ f = NULL;
+ if (res == -1) {
+ if (errno == EAGAIN) {
+ /* Return "NULL" frame if there is nobody there */
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ } else if (errno == ELAST) {
+ f = __zt_exception(ast);
+ } else
+ ast_log(LOG_WARNING, "zt_rec: %s\n", strerror(errno));
+ }
+ ast_mutex_unlock(&p->lock);
+ return f;
+ }
+ if (res != (p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE)) {
+ ast_debug(1, "Short read (%d/%d), must be an event...\n", res, p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE);
+ f = __zt_exception(ast);
+ ast_mutex_unlock(&p->lock);
+ return f;
+ }
+ if (p->tdd) { /* if in TDD mode, see if we receive that */
+ int c;
+
+ c = tdd_feed(p->tdd,readbuf,READ_SIZE);
+ if (c < 0) {
+ ast_debug(1,"tdd_feed failed\n");
+ ast_mutex_unlock(&p->lock);
+ return NULL;
+ }
+ if (c) { /* if a char to return */
+ p->subs[index].f.subclass = 0;
+ p->subs[index].f.frametype = AST_FRAME_TEXT;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = AST_FRIENDLY_OFFSET;
+ p->subs[index].f.data = p->subs[index].buffer + AST_FRIENDLY_OFFSET;
+ p->subs[index].f.datalen = 1;
+ *((char *) p->subs[index].f.data) = c;
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+ }
+ if (p->callwaitingrepeat)
+ p->callwaitingrepeat--;
+ if (p->cidcwexpire)
+ p->cidcwexpire--;
+ /* Repeat callwaiting */
+ if (p->callwaitingrepeat == 1) {
+ p->callwaitrings++;
+ zt_callwait(ast);
+ }
+ /* Expire CID/CW */
+ if (p->cidcwexpire == 1) {
+ ast_verb(3, "CPE does not support Call Waiting Caller*ID.\n");
+ restore_conference(p);
+ }
+ if (p->subs[index].linear) {
+ p->subs[index].f.datalen = READ_SIZE * 2;
+ } else
+ p->subs[index].f.datalen = READ_SIZE;
+
+ /* Handle CallerID Transmission */
+ if ((p->owner == ast) && p->cidspill &&((ast->_state == AST_STATE_UP) || (ast->rings == p->cidrings))) {
+ send_callerid(p);
+ }
+
+ p->subs[index].f.frametype = AST_FRAME_VOICE;
+ p->subs[index].f.subclass = ast->rawreadformat;
+ p->subs[index].f.samples = READ_SIZE;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = AST_FRIENDLY_OFFSET;
+ p->subs[index].f.data = p->subs[index].buffer + AST_FRIENDLY_OFFSET / sizeof(p->subs[index].buffer[0]);
+#if 0
+ ast_debug(1, "Read %d of voice on %s\n", p->subs[index].f.datalen, ast->name);
+#endif
+ if (p->dialing || /* Transmitting something */
+ (index && (ast->_state != AST_STATE_UP)) || /* Three-way or callwait that isn't up */
+ ((index == SUB_CALLWAIT) && !p->subs[SUB_CALLWAIT].inthreeway) /* Inactive and non-confed call-wait */
+ ) {
+ /* Whoops, we're still dialing, or in a state where we shouldn't transmit....
+ don't send anything */
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ p->subs[index].f.samples = 0;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = 0;
+ p->subs[index].f.data = NULL;
+ p->subs[index].f.datalen= 0;
+ }
+ if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) {
+ /* Perform busy detection. etc on the zap line */
+ f = ast_dsp_process(ast, p->dsp, &p->subs[index].f);
+ if (f) {
+ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_BUSY)) {
+ if ((ast->_state == AST_STATE_UP) && !p->outgoing) {
+ /* Treat this as a "hangup" instead of a "busy" on the assumption that
+ a busy */
+ f = NULL;
+ }
+ } else if (f->frametype == AST_FRAME_DTMF) {
+#ifdef HAVE_PRI
+ if (!p->proceeding && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri &&
+ ((!p->outgoing && (p->pri->overlapdial & ZAP_OVERLAPDIAL_INCOMING)) ||
+ (p->outgoing && (p->pri->overlapdial & ZAP_OVERLAPDIAL_OUTGOING)))) {
+ /* Don't accept in-band DTMF when in overlap dial mode */
+ f->frametype = AST_FRAME_NULL;
+ f->subclass = 0;
+ }
+#endif
+ /* DSP clears us of being pulse */
+ p->pulsedial = 0;
+ }
+ }
+ } else
+ f = &p->subs[index].f;
+
+ if (f && (f->frametype == AST_FRAME_DTMF))
+ zt_handle_dtmfup(ast, index, &f);
+
+ /* If we have a fake_event, trigger exception to handle it */
+ if (p->fake_event)
+ ast_set_flag(ast, AST_FLAG_EXCEPTION);
+
+ ast_mutex_unlock(&p->lock);
+ return f;
+}
+
+static int my_zt_write(struct zt_pvt *p, unsigned char *buf, int len, int index, int linear)
+{
+ int sent=0;
+ int size;
+ int res;
+ int fd;
+ fd = p->subs[index].zfd;
+ while (len) {
+ size = len;
+ if (size > (linear ? READ_SIZE * 2 : READ_SIZE))
+ size = (linear ? READ_SIZE * 2 : READ_SIZE);
+ res = write(fd, buf, size);
+ if (res != size) {
+ ast_debug(1, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel);
+ return sent;
+ }
+ len -= size;
+ buf += size;
+ }
+ return sent;
+}
+
+static int zt_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct zt_pvt *p = ast->tech_pvt;
+ int res;
+ int index;
+ index = zt_get_index(ast, p, 0);
+ if (index < 0) {
+ ast_log(LOG_WARNING, "%s doesn't really exist?\n", ast->name);
+ return -1;
+ }
+
+#if 0
+#ifdef HAVE_PRI
+ ast_mutex_lock(&p->lock);
+ if (!p->proceeding && p->sig==SIG_PRI && p->pri && !p->outgoing) {
+ if (p->pri->pri) {
+ if (!pri_grab(p, p->pri)) {
+ pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital);
+ pri_rel(p->pri);
+ } else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ }
+ p->proceeding=1;
+ }
+ ast_mutex_unlock(&p->lock);
+#endif
+#endif
+ /* Write a frame of (presumably voice) data */
+ if (frame->frametype != AST_FRAME_VOICE) {
+ if (frame->frametype != AST_FRAME_IMAGE)
+ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
+ return 0;
+ }
+ if ((frame->subclass != AST_FORMAT_SLINEAR) &&
+ (frame->subclass != AST_FORMAT_ULAW) &&
+ (frame->subclass != AST_FORMAT_ALAW)) {
+ ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
+ return -1;
+ }
+ if (p->dialing) {
+ ast_debug(1, "Dropping frame since I'm still dialing on %s...\n",ast->name);
+ return 0;
+ }
+ if (!p->owner) {
+ ast_debug(1, "Dropping frame since there is no active owner on %s...\n",ast->name);
+ return 0;
+ }
+ if (p->cidspill) {
+ ast_debug(1, "Dropping frame since I've still got a callerid spill\n");
+ return 0;
+ }
+ /* Return if it's not valid data */
+ if (!frame->data || !frame->datalen)
+ return 0;
+
+ if (frame->subclass == AST_FORMAT_SLINEAR) {
+ if (!p->subs[index].linear) {
+ p->subs[index].linear = 1;
+ res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set linear mode on channel %d\n", p->channel);
+ }
+ res = my_zt_write(p, (unsigned char *)frame->data, frame->datalen, index, 1);
+ } else {
+ /* x-law already */
+ if (p->subs[index].linear) {
+ p->subs[index].linear = 0;
+ res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to set companded mode on channel %d\n", p->channel);
+ }
+ res = my_zt_write(p, (unsigned char *)frame->data, frame->datalen, index, 0);
+ }
+ if (res < 0) {
+ ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int zt_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen)
+{
+ struct zt_pvt *p = chan->tech_pvt;
+ int res=-1;
+ int index;
+ int func = ZT_FLASH;
+ ast_mutex_lock(&p->lock);
+ index = zt_get_index(chan, p, 0);
+ ast_debug(1, "Requested indication %d on channel %s\n", condition, chan->name);
+ if (index == SUB_REAL) {
+ switch (condition) {
+ case AST_CONTROL_BUSY:
+#ifdef HAVE_PRI
+ if (p->priindication_oob && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))) {
+ chan->hangupcause = AST_CAUSE_USER_BUSY;
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ res = 0;
+ } else if (!p->progress &&
+ ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))
+ && p->pri && !p->outgoing) {
+ if (p->pri->pri) {
+ if (!pri_grab(p, p->pri)) {
+ pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1);
+ pri_rel(p->pri);
+ }
+ else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ }
+ p->progress = 1;
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_BUSY);
+ } else
+#endif
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_BUSY);
+ break;
+ case AST_CONTROL_RINGING:
+#ifdef HAVE_PRI
+ if ((!p->alerting) && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))
+ && p->pri && !p->outgoing && (chan->_state != AST_STATE_UP)) {
+ if (p->pri->pri) {
+ if (!pri_grab(p, p->pri)) {
+ pri_acknowledge(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital);
+ pri_rel(p->pri);
+ }
+ else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ }
+ p->alerting = 1;
+ }
+
+#endif
+#ifdef HAVE_SS7
+ if ((!p->alerting) && (p->sig == SIG_SS7) && p->ss7 && !p->outgoing && (chan->_state != AST_STATE_UP)) {
+ if (p->ss7->ss7) {
+ ss7_grab(p, p->ss7);
+
+ if ((isup_far(p->ss7->ss7, p->ss7call)) != -1)
+ p->rlt = 1;
+ if (p->rlt != 1) /* No need to send CPG if call will be RELEASE */
+ isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_ALERTING);
+ p->alerting = 1;
+ ss7_rel(p->ss7);
+ }
+ }
+#endif
+
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_RINGTONE);
+
+ if (chan->_state != AST_STATE_UP) {
+ if ((chan->_state != AST_STATE_RING) ||
+ ((p->sig != SIG_FXSKS) &&
+ (p->sig != SIG_FXSLS) &&
+ (p->sig != SIG_FXSGS)))
+ ast_setstate(chan, AST_STATE_RINGING);
+ }
+ break;
+ case AST_CONTROL_PROCEEDING:
+ ast_debug(1,"Received AST_CONTROL_PROCEEDING on %s\n",chan->name);
+#ifdef HAVE_PRI
+ if (!p->proceeding && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))
+ && p->pri && !p->outgoing) {
+ if (p->pri->pri) {
+ if (!pri_grab(p, p->pri)) {
+ pri_proceeding(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital);
+ pri_rel(p->pri);
+ }
+ else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ }
+ p->proceeding = 1;
+ }
+#endif
+#ifdef HAVE_SS7
+ /* This IF sends the FAR for an answered ALEG call */
+ if (chan->_state == AST_STATE_UP && (p->rlt != 1) && (p->sig == SIG_SS7)){
+ if ((isup_far(p->ss7->ss7, p->ss7call)) != -1)
+ p->rlt = 1;
+ }
+
+ if (!p->proceeding && p->sig == SIG_SS7 && p->ss7 && !p->outgoing) {
+ if (p->ss7->ss7) {
+ ss7_grab(p, p->ss7);
+ isup_acm(p->ss7->ss7, p->ss7call);
+ p->proceeding = 1;
+ ss7_rel(p->ss7);
+
+ }
+ }
+#endif
+ /* don't continue in ast_indicate */
+ res = 0;
+ break;
+ case AST_CONTROL_PROGRESS:
+ ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",chan->name);
+#ifdef HAVE_PRI
+ p->digital = 0; /* Digital-only calls isn't allows any inband progress messages */
+ if (!p->progress && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))
+ && p->pri && !p->outgoing) {
+ if (p->pri->pri) {
+ if (!pri_grab(p, p->pri)) {
+ pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1);
+ pri_rel(p->pri);
+ }
+ else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ }
+ p->progress = 1;
+ }
+#endif
+#ifdef HAVE_SS7
+ if (!p->progress && p->sig==SIG_SS7 && p->ss7 && !p->outgoing) {
+ if (p->ss7->ss7) {
+ ss7_grab(p, p->ss7);
+ isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_INBANDINFO);
+ p->progress = 1;
+ ss7_rel(p->ss7);
+
+ }
+ }
+#endif
+ /* don't continue in ast_indicate */
+ res = 0;
+ break;
+ case AST_CONTROL_CONGESTION:
+ chan->hangupcause = AST_CAUSE_CONGESTION;
+#ifdef HAVE_PRI
+ if (p->priindication_oob && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))) {
+ chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ res = 0;
+ } else if (!p->progress && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))
+ && p->pri && !p->outgoing) {
+ if (p->pri) {
+ if (!pri_grab(p, p->pri)) {
+ pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1);
+ pri_rel(p->pri);
+ } else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ }
+ p->progress = 1;
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ } else
+#endif
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ break;
+ case AST_CONTROL_HOLD:
+#ifdef HAVE_PRI
+ if (p->pri && !strcasecmp(p->mohinterpret, "passthrough")) {
+ if (!pri_grab(p, p->pri)) {
+ res = pri_notify(p->pri->pri, p->call, p->prioffset, PRI_NOTIFY_REMOTE_HOLD);
+ pri_rel(p->pri);
+ } else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ } else
+#endif
+ ast_moh_start(chan, data, p->mohinterpret);
+ break;
+ case AST_CONTROL_UNHOLD:
+#ifdef HAVE_PRI
+ if (p->pri && !strcasecmp(p->mohinterpret, "passthrough")) {
+ if (!pri_grab(p, p->pri)) {
+ res = pri_notify(p->pri->pri, p->call, p->prioffset, PRI_NOTIFY_REMOTE_RETRIEVAL);
+ pri_rel(p->pri);
+ } else
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ } else
+#endif
+ ast_moh_stop(chan);
+ break;
+ case AST_CONTROL_RADIO_KEY:
+ if (p->radio)
+ res = zt_set_hook(p->subs[index].zfd, ZT_OFFHOOK);
+ res = 0;
+ break;
+ case AST_CONTROL_RADIO_UNKEY:
+ if (p->radio)
+ res = zt_set_hook(p->subs[index].zfd, ZT_RINGOFF);
+ res = 0;
+ break;
+ case AST_CONTROL_FLASH:
+ /* flash hookswitch */
+ if (ISTRUNK(p) && (p->sig != SIG_PRI)) {
+ /* Clear out the dial buffer */
+ p->dop.dialstr[0] = '\0';
+ if ((ioctl(p->subs[SUB_REAL].zfd,ZT_HOOK,&func) == -1) && (errno != EINPROGRESS)) {
+ ast_log(LOG_WARNING, "Unable to flash external trunk on channel %s: %s\n",
+ chan->name, strerror(errno));
+ } else
+ res = 0;
+ } else
+ res = 0;
+ break;
+ case -1:
+ res = tone_zone_play_tone(p->subs[index].zfd, -1);
+ break;
+ }
+ } else
+ res = 0;
+ ast_mutex_unlock(&p->lock);
+ return res;
+}
+
+static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int index, int law, int transfercapability)
+{
+ struct ast_channel *tmp;
+ int deflaw;
+ int res;
+ int x,y;
+ int features;
+ struct ast_str *chan_name;
+ struct ast_variable *v;
+ ZT_PARAMS ps;
+ if (i->subs[index].owner) {
+ ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[index]);
+ return NULL;
+ }
+ y = 1;
+ chan_name = ast_str_alloca(32);
+ do {
+#ifdef HAVE_PRI
+ if (i->bearer || (i->pri && (i->sig == SIG_FXSKS)))
+ ast_str_set(&chan_name, 0, "%d:%d-%d", i->pri->trunkgroup, i->channel, y);
+ else
+#endif
+ if (i->channel == CHAN_PSEUDO)
+ ast_str_set(&chan_name, 0, "pseudo-%ld", ast_random());
+ else
+ ast_str_set(&chan_name, 0, "%d-%d", i->channel, y);
+ for (x = 0; x < 3; x++) {
+ if ((index != x) && i->subs[x].owner && !strcasecmp(chan_name->str, i->subs[x].owner->name))
+ break;
+ }
+ y++;
+ } while (x < 3);
+ tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "Zap/%s", chan_name->str);
+ if (!tmp)
+ return NULL;
+ tmp->tech = &zap_tech;
+ ps.channo = i->channel;
+ res = ioctl(i->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &ps);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to get parameters, assuming MULAW\n");
+ ps.curlaw = ZT_LAW_MULAW;
+ }
+ if (ps.curlaw == ZT_LAW_ALAW)
+ deflaw = AST_FORMAT_ALAW;
+ else
+ deflaw = AST_FORMAT_ULAW;
+ if (law) {
+ if (law == ZT_LAW_ALAW)
+ deflaw = AST_FORMAT_ALAW;
+ else
+ deflaw = AST_FORMAT_ULAW;
+ }
+ ast_channel_set_fd(tmp, 0, i->subs[index].zfd);
+ tmp->nativeformats = AST_FORMAT_SLINEAR | deflaw;
+ /* Start out assuming ulaw since it's smaller :) */
+ tmp->rawreadformat = deflaw;
+ tmp->readformat = deflaw;
+ tmp->rawwriteformat = deflaw;
+ tmp->writeformat = deflaw;
+ i->subs[index].linear = 0;
+ zt_setlinear(i->subs[index].zfd, i->subs[index].linear);
+ features = 0;
+ if (index == SUB_REAL) {
+ if (i->busydetect && CANBUSYDETECT(i))
+ features |= DSP_FEATURE_BUSY_DETECT;
+ if ((i->callprogress & CALLPROGRESS_PROGRESS) && CANPROGRESSDETECT(i))
+ features |= DSP_FEATURE_CALL_PROGRESS;
+ if ((!i->outgoing && (i->callprogress & CALLPROGRESS_FAX_INCOMING)) ||
+ (i->outgoing && (i->callprogress & CALLPROGRESS_FAX_OUTGOING))) {
+ features |= DSP_FEATURE_FAX_DETECT;
+ }
+#ifdef ZT_TONEDETECT
+ x = ZT_TONEDETECT_ON | ZT_TONEDETECT_MUTE;
+ if (ioctl(i->subs[index].zfd, ZT_TONEDETECT, &x)) {
+#endif
+ i->hardwaredtmf = 0;
+ features |= DSP_FEATURE_DTMF_DETECT;
+#ifdef ZT_TONEDETECT
+ } else if (NEED_MFDETECT(i)) {
+ i->hardwaredtmf = 1;
+ features |= DSP_FEATURE_DTMF_DETECT;
+ }
+#endif
+ }
+ if (features) {
+ if (i->dsp) {
+ ast_debug(1, "Already have a dsp on %s?\n", tmp->name);
+ } else {
+ if (i->channel != CHAN_PSEUDO)
+ i->dsp = ast_dsp_new();
+ else
+ i->dsp = NULL;
+ if (i->dsp) {
+ i->dsp_features = features & ~DSP_PROGRESS_TALK;
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ /* We cannot do progress detection until receives PROGRESS message */
+ if (i->outgoing && ((i->sig == SIG_PRI) || (i->sig == SIG_BRI) || (i->sig == SIG_BRI_PTMP) || (i->sig == SIG_SS7))) {
+ /* Remember requested DSP features, don't treat
+ talking as ANSWER */
+ features = 0;
+ }
+#endif
+ ast_dsp_set_features(i->dsp, features);
+ ast_dsp_digitmode(i->dsp, DSP_DIGITMODE_DTMF | i->dtmfrelax);
+ if (!ast_strlen_zero(progzone))
+ ast_dsp_set_call_progress_zone(i->dsp, progzone);
+ if (i->busydetect && CANBUSYDETECT(i)) {
+ ast_dsp_set_busy_count(i->dsp, i->busycount);
+ ast_dsp_set_busy_pattern(i->dsp, i->busy_tonelength, i->busy_quietlength);
+ }
+ }
+ }
+ }
+
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->tech_pvt = i;
+ if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS)) {
+ /* Only FXO signalled stuff can be picked up */
+ tmp->callgroup = i->callgroup;
+ tmp->pickupgroup = i->pickupgroup;
+ }
+ if (!ast_strlen_zero(i->language))
+ ast_string_field_set(tmp, language, i->language);
+ if (!i->owner)
+ i->owner = tmp;
+ if (!ast_strlen_zero(i->accountcode))
+ ast_string_field_set(tmp, accountcode, i->accountcode);
+ if (i->amaflags)
+ tmp->amaflags = i->amaflags;
+ i->subs[index].owner = tmp;
+ ast_copy_string(tmp->context, i->context, sizeof(tmp->context));
+ ast_string_field_set(tmp, call_forward, i->call_forward);
+ /* If we've been told "no ADSI" then enforce it */
+ if (!i->adsi)
+ tmp->adsicpe = AST_ADSI_UNAVAILABLE;
+ if (!ast_strlen_zero(i->exten))
+ ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
+ if (!ast_strlen_zero(i->rdnis))
+ tmp->cid.cid_rdnis = ast_strdup(i->rdnis);
+ if (!ast_strlen_zero(i->dnid))
+ tmp->cid.cid_dnid = ast_strdup(i->dnid);
+
+ /* Don't use ast_set_callerid() here because it will
+ * generate a needless NewCallerID event */
+#ifdef PRI_ANI
+ if (!ast_strlen_zero(i->cid_ani))
+ tmp->cid.cid_ani = ast_strdup(i->cid_ani);
+ else
+ tmp->cid.cid_ani = ast_strdup(i->cid_num);
+#else
+ tmp->cid.cid_ani = ast_strdup(i->cid_num);
+#endif
+ tmp->cid.cid_pres = i->callingpres;
+ tmp->cid.cid_ton = i->cid_ton;
+ tmp->cid.cid_ani2 = i->cid_ani2;
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ tmp->transfercapability = transfercapability;
+ pbx_builtin_setvar_helper(tmp, "TRANSFERCAPABILITY", ast_transfercapability2str(transfercapability));
+ if (transfercapability & AST_TRANS_CAP_DIGITAL)
+ i->digital = 1;
+ /* Assume calls are not idle calls unless we're told differently */
+ i->isidlecall = 0;
+ i->alreadyhungup = 0;
+#endif
+ /* clear the fake event in case we posted one before we had ast_channel */
+ i->fake_event = 0;
+ /* Assure there is no confmute on this channel */
+ zt_confmute(i, 0);
+ /* Configure the new channel jb */
+ ast_jb_configure(tmp, &global_jbconf);
+
+ for (v = i->vars ; v ; v = v->next)
+ pbx_builtin_setvar_helper(tmp, v->name, v->value);
+
+ if (startpbx) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ i->owner = NULL;
+ return NULL;
+ }
+ }
+
+ ast_module_ref(ast_module_info->self);
+ return tmp;
+}
+
+
+static int my_getsigstr(struct ast_channel *chan, char *str, const char *term, int ms)
+{
+ char c;
+
+ *str = 0; /* start with empty output buffer */
+ for (;;)
+ {
+ /* Wait for the first digit (up to specified ms). */
+ c = ast_waitfordigit(chan, ms);
+ /* if timeout, hangup or error, return as such */
+ if (c < 1)
+ return c;
+ *str++ = c;
+ *str = 0;
+ if (strchr(term, c))
+ return 1;
+ }
+}
+
+static int zt_wink(struct zt_pvt *p, int index)
+{
+ int j;
+ zt_set_hook(p->subs[index].zfd, ZT_WINK);
+ for (;;)
+ {
+ /* set bits of interest */
+ j = ZT_IOMUX_SIGEVENT;
+ /* wait for some happening */
+ if (ioctl(p->subs[index].zfd,ZT_IOMUX,&j) == -1) return(-1);
+ /* exit loop if we have it */
+ if (j & ZT_IOMUX_SIGEVENT) break;
+ }
+ /* get the event info */
+ if (ioctl(p->subs[index].zfd,ZT_GETEVENT,&j) == -1) return(-1);
+ return 0;
+}
+
+/*! enable or disable the chan_zap Do-Not-Disturb mode for a Zaptel channel
+ * @zapchan "Physical" Zaptel channel (e.g: Zap/5)
+ * @on: 1 to enable, 0 to disable
+ *
+ * chan_zap has a DND (Do Not Disturb) mode for each zapchan (physical
+ * zaptel channel). Use this to enable or disable it.
+ *
+ * \fixme the use of the word "channel" for those zapchans is really
+ * confusing.
+ */
+static void zap_dnd(struct zt_pvt *zapchan, int on)
+{
+ /* Do not disturb */
+ zapchan->dnd = on;
+ ast_verb(3, "%s DND on channel %d\n",
+ on? "Enabled" : "Disabled",
+ zapchan->channel);
+ manager_event(EVENT_FLAG_SYSTEM, "DNDState",
+ "Channel: Zap/%d\r\n"
+ "Status: %s\r\n", zapchan->channel,
+ on? "enabled" : "disabled");
+}
+
+static void *ss_thread(void *data)
+{
+ struct ast_channel *chan = data;
+ struct zt_pvt *p = chan->tech_pvt;
+ char exten[AST_MAX_EXTENSION] = "";
+ char exten2[AST_MAX_EXTENSION] = "";
+ unsigned char buf[256];
+ char dtmfcid[300];
+ char dtmfbuf[300];
+ struct callerid_state *cs = NULL;
+ char *name = NULL, *number = NULL;
+ int distMatches;
+ int curRingData[3];
+ int receivedRingT;
+ int counter1;
+ int counter;
+ int samples = 0;
+ struct ast_smdi_md_message *smdi_msg = NULL;
+ int flags;
+ int i;
+ int timeout;
+ int getforward = 0;
+ char *s1, *s2;
+ int len = 0;
+ int res;
+ int index;
+
+ /* in the bizarre case where the channel has become a zombie before we
+ even get started here, abort safely
+ */
+ if (!p) {
+ ast_log(LOG_WARNING, "Channel became a zombie before simple switch could be started (%s)\n", chan->name);
+ ast_hangup(chan);
+ return NULL;
+ }
+
+ ast_verb(3, "Starting simple switch on '%s'\n", chan->name);
+ index = zt_get_index(chan, p, 1);
+ if (index < 0) {
+ ast_log(LOG_WARNING, "Huh?\n");
+ ast_hangup(chan);
+ return NULL;
+ }
+ if (p->dsp)
+ ast_dsp_digitreset(p->dsp);
+ switch (p->sig) {
+#ifdef HAVE_PRI
+ case SIG_PRI:
+ case SIG_BRI:
+ case SIG_BRI_PTMP:
+ /* Now loop looking for an extension */
+ ast_copy_string(exten, p->exten, sizeof(exten));
+ len = strlen(exten);
+ res = 0;
+ while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) {
+ if (len && !ast_ignore_pattern(chan->context, exten))
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+ else
+ tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);
+ if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num))
+ timeout = matchdigittimeout;
+ else
+ timeout = gendigittimeout;
+ res = ast_waitfordigit(chan, timeout);
+ if (res < 0) {
+ ast_debug(1, "waitfordigit returned < 0...\n");
+ ast_hangup(chan);
+ return NULL;
+ } else if (res) {
+ exten[len++] = res;
+ exten[len] = '\0';
+ } else
+ break;
+ }
+ /* if no extension was received ('unspecified') on overlap call, use the 's' extension */
+ if (ast_strlen_zero(exten)) {
+ ast_verb(3, "Going to extension s|1 because of empty extension received on overlap call\n");
+ exten[0] = 's';
+ exten[1] = '\0';
+ }
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+ if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num)) {
+ /* Start the real PBX */
+ ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+ if (p->dsp) ast_dsp_digitreset(p->dsp);
+ zt_enable_ec(p);
+ ast_setstate(chan, AST_STATE_RING);
+ res = ast_pbx_run(chan);
+ if (res) {
+ ast_log(LOG_WARNING, "PBX exited non-zero!\n");
+ }
+ } else {
+ ast_debug(1, "No such possible extension '%s' in context '%s'\n", exten, chan->context);
+ chan->hangupcause = AST_CAUSE_UNALLOCATED;
+ ast_hangup(chan);
+ p->exten[0] = '\0';
+ /* Since we send release complete here, we won't get one */
+ p->call = NULL;
+ }
+ return NULL;
+ break;
+#endif
+ case SIG_FEATD:
+ case SIG_FEATDMF:
+ case SIG_FEATDMF_TA:
+ case SIG_E911:
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ case SIG_EMWINK:
+ case SIG_SF_FEATD:
+ case SIG_SF_FEATDMF:
+ case SIG_SF_FEATB:
+ case SIG_SFWINK:
+ if (zt_wink(p, index))
+ return NULL;
+ /* Fall through */
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_SF:
+ case SIG_FGC_CAMA:
+ res = tone_zone_play_tone(p->subs[index].zfd, -1);
+ if (p->dsp)
+ ast_dsp_digitreset(p->dsp);
+ /* set digit mode appropriately */
+ if (p->dsp) {
+ if (NEED_MFDETECT(p))
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MF | p->dtmfrelax);
+ else
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax);
+ }
+ memset(dtmfbuf, 0, sizeof(dtmfbuf));
+ /* Wait for the first digit only if immediate=no */
+ if (!p->immediate)
+ /* Wait for the first digit (up to 5 seconds). */
+ res = ast_waitfordigit(chan, 5000);
+ else
+ res = 0;
+ if (res > 0) {
+ /* save first char */
+ dtmfbuf[0] = res;
+ switch (p->sig) {
+ case SIG_FEATD:
+ case SIG_SF_FEATD:
+ res = my_getsigstr(chan, dtmfbuf + 1, "*", 3000);
+ if (res > 0)
+ res = my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
+ if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp);
+ break;
+ case SIG_FEATDMF_TA:
+ res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+ if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp);
+ if (zt_wink(p, index)) return NULL;
+ dtmfbuf[0] = 0;
+ /* Wait for the first digit (up to 5 seconds). */
+ res = ast_waitfordigit(chan, 5000);
+ if (res <= 0) break;
+ dtmfbuf[0] = res;
+ /* fall through intentionally */
+ case SIG_FEATDMF:
+ case SIG_E911:
+ case SIG_FGC_CAMAMF:
+ case SIG_SF_FEATDMF:
+ res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+ /* if international caca, do it again to get real ANO */
+ if ((p->sig == SIG_FEATDMF) && (dtmfbuf[1] != '0') && (strlen(dtmfbuf) != 14))
+ {
+ if (zt_wink(p, index)) return NULL;
+ dtmfbuf[0] = 0;
+ /* Wait for the first digit (up to 5 seconds). */
+ res = ast_waitfordigit(chan, 5000);
+ if (res <= 0) break;
+ dtmfbuf[0] = res;
+ res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+ }
+ if (res > 0) {
+ /* if E911, take off hook */
+ if (p->sig == SIG_E911)
+ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+ res = my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "#", 3000);
+ }
+ if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp);
+ break;
+ case SIG_FEATB:
+ case SIG_SF_FEATB:
+ res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+ if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp);
+ break;
+ case SIG_EMWINK:
+ /* if we received a '*', we are actually receiving Feature Group D
+ dial syntax, so use that mode; otherwise, fall through to normal
+ mode
+ */
+ if (res == '*') {
+ res = my_getsigstr(chan, dtmfbuf + 1, "*", 3000);
+ if (res > 0)
+ res = my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
+ if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp);
+ break;
+ }
+ default:
+ /* If we got the first digit, get the rest */
+ len = 1;
+ dtmfbuf[len] = '\0';
+ while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) {
+ if (ast_exists_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) {
+ timeout = matchdigittimeout;
+ } else {
+ timeout = gendigittimeout;
+ }
+ res = ast_waitfordigit(chan, timeout);
+ if (res < 0) {
+ ast_debug(1, "waitfordigit returned < 0...\n");
+ ast_hangup(chan);
+ return NULL;
+ } else if (res) {
+ dtmfbuf[len++] = res;
+ dtmfbuf[len] = '\0';
+ } else {
+ break;
+ }
+ }
+ break;
+ }
+ }
+ if (res == -1) {
+ ast_log(LOG_WARNING, "getdtmf on channel %d: %s\n", p->channel, strerror(errno));
+ ast_hangup(chan);
+ return NULL;
+ } else if (res < 0) {
+ ast_debug(1, "Got hung up before digits finished\n");
+ ast_hangup(chan);
+ return NULL;
+ }
+
+ if (p->sig == SIG_FGC_CAMA) {
+ char anibuf[100];
+
+ if (ast_safe_sleep(chan,1000) == -1) {
+ ast_hangup(chan);
+ return NULL;
+ }
+ zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MF | p->dtmfrelax);
+ res = my_getsigstr(chan, anibuf, "#", 10000);
+ if ((res > 0) && (strlen(anibuf) > 2)) {
+ if (anibuf[strlen(anibuf) - 1] == '#')
+ anibuf[strlen(anibuf) - 1] = 0;
+ ast_set_callerid(chan, anibuf + 2, NULL, anibuf + 2);
+ }
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax);
+ }
+
+ ast_copy_string(exten, dtmfbuf, sizeof(exten));
+ if (ast_strlen_zero(exten))
+ ast_copy_string(exten, "s", sizeof(exten));
+ if (p->sig == SIG_FEATD || p->sig == SIG_EMWINK) {
+ /* Look for Feature Group D on all E&M Wink and Feature Group D trunks */
+ if (exten[0] == '*') {
+ char *stringp=NULL;
+ ast_copy_string(exten2, exten, sizeof(exten2));
+ /* Parse out extension and callerid */
+ stringp=exten2 +1;
+ s1 = strsep(&stringp, "*");
+ s2 = strsep(&stringp, "*");
+ if (s2) {
+ if (!ast_strlen_zero(p->cid_num))
+ ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
+ else
+ ast_set_callerid(chan, s1, NULL, s1);
+ ast_copy_string(exten, s2, sizeof(exten));
+ } else
+ ast_copy_string(exten, s1, sizeof(exten));
+ } else if (p->sig == SIG_FEATD)
+ ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d. Assuming E&M Wink instead\n", p->channel);
+ }
+ if ((p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATDMF_TA)) {
+ if (exten[0] == '*') {
+ char *stringp=NULL;
+ ast_copy_string(exten2, exten, sizeof(exten2));
+ /* Parse out extension and callerid */
+ stringp=exten2 +1;
+ s1 = strsep(&stringp, "#");
+ s2 = strsep(&stringp, "#");
+ if (s2) {
+ if (!ast_strlen_zero(p->cid_num))
+ ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
+ else
+ if (*(s1 + 2))
+ ast_set_callerid(chan, s1 + 2, NULL, s1 + 2);
+ ast_copy_string(exten, s2 + 1, sizeof(exten));
+ } else
+ ast_copy_string(exten, s1 + 2, sizeof(exten));
+ } else
+ ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d. Assuming E&M Wink instead\n", p->channel);
+ }
+ if ((p->sig == SIG_E911) || (p->sig == SIG_FGC_CAMAMF)) {
+ if (exten[0] == '*') {
+ char *stringp=NULL;
+ ast_copy_string(exten2, exten, sizeof(exten2));
+ /* Parse out extension and callerid */
+ stringp=exten2 +1;
+ s1 = strsep(&stringp, "#");
+ s2 = strsep(&stringp, "#");
+ if (s2 && (*(s2 + 1) == '0')) {
+ if (*(s2 + 2))
+ ast_set_callerid(chan, s2 + 2, NULL, s2 + 2);
+ }
+ if (s1) ast_copy_string(exten, s1, sizeof(exten));
+ else ast_copy_string(exten, "911", sizeof(exten));
+ } else
+ ast_log(LOG_WARNING, "Got a non-E911/FGC CAMA input on channel %d. Assuming E&M Wink instead\n", p->channel);
+ }
+ if (p->sig == SIG_FEATB) {
+ if (exten[0] == '*') {
+ char *stringp=NULL;
+ ast_copy_string(exten2, exten, sizeof(exten2));
+ /* Parse out extension and callerid */
+ stringp=exten2 +1;
+ s1 = strsep(&stringp, "#");
+ ast_copy_string(exten, exten2 + 1, sizeof(exten));
+ } else
+ ast_log(LOG_WARNING, "Got a non-Feature Group B input on channel %d. Assuming E&M Wink instead\n", p->channel);
+ }
+ if ((p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATDMF_TA)) {
+ zt_wink(p, index);
+ /* some switches require a minimum guard time between
+ the last FGD wink and something that answers
+ immediately. This ensures it */
+ if (ast_safe_sleep(chan,100)) return NULL;
+ }
+ zt_enable_ec(p);
+ if (NEED_MFDETECT(p)) {
+ if (p->dsp) {
+ if (!p->hardwaredtmf)
+ ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax);
+ else {
+ ast_dsp_free(p->dsp);
+ p->dsp = NULL;
+ }
+ }
+ }
+
+ if (ast_exists_extension(chan, chan->context, exten, 1, chan->cid.cid_num)) {
+ ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+ if (p->dsp) ast_dsp_digitreset(p->dsp);
+ res = ast_pbx_run(chan);
+ if (res) {
+ ast_log(LOG_WARNING, "PBX exited non-zero\n");
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ }
+ return NULL;
+ } else {
+ ast_verb(2, "Unknown extension '%s' in context '%s' requested\n", exten, chan->context);
+ sleep(2);
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_INFO);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to start special tone on %d\n", p->channel);
+ else
+ sleep(1);
+ res = ast_streamfile(chan, "ss-noservice", chan->language);
+ if (res >= 0)
+ ast_waitstream(chan, "");
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ ast_hangup(chan);
+ return NULL;
+ }
+ break;
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ /* Read the first digit */
+ timeout = firstdigittimeout;
+ /* If starting a threeway call, never timeout on the first digit so someone
+ can use flash-hook as a "hold" feature */
+ if (p->subs[SUB_THREEWAY].owner)
+ timeout = 999999;
+ while (len < AST_MAX_EXTENSION-1) {
+ /* Read digit unless it's supposed to be immediate, in which case the
+ only answer is 's' */
+ if (p->immediate)
+ res = 's';
+ else
+ res = ast_waitfordigit(chan, timeout);
+ timeout = 0;
+ if (res < 0) {
+ ast_debug(1, "waitfordigit returned < 0...\n");
+ res = tone_zone_play_tone(p->subs[index].zfd, -1);
+ ast_hangup(chan);
+ return NULL;
+ } else if (res) {
+ ast_debug(1,"waitfordigit returned '%c' (%d), timeout = %d\n", res, res, timeout);
+ exten[len++]=res;
+ exten[len] = '\0';
+ }
+ if (!ast_ignore_pattern(chan->context, exten))
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+ else
+ tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);
+ if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && strcmp(exten, ast_parking_ext())) {
+ if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) {
+ if (getforward) {
+ /* Record this as the forwarding extension */
+ ast_copy_string(p->call_forward, exten, sizeof(p->call_forward));
+ ast_verb(3, "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel);
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ if (res)
+ break;
+ usleep(500000);
+ res = tone_zone_play_tone(p->subs[index].zfd, -1);
+ sleep(1);
+ memset(exten, 0, sizeof(exten));
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);
+ len = 0;
+ getforward = 0;
+ } else {
+ res = tone_zone_play_tone(p->subs[index].zfd, -1);
+ ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+ if (!ast_strlen_zero(p->cid_num)) {
+ if (!p->hidecallerid)
+ ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
+ else
+ ast_set_callerid(chan, NULL, NULL, p->cid_num);
+ }
+ if (!ast_strlen_zero(p->cid_name)) {
+ if (!p->hidecallerid)
+ ast_set_callerid(chan, NULL, p->cid_name, NULL);
+ }
+ ast_setstate(chan, AST_STATE_RING);
+ zt_enable_ec(p);
+ res = ast_pbx_run(chan);
+ if (res) {
+ ast_log(LOG_WARNING, "PBX exited non-zero\n");
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ }
+ return NULL;
+ }
+ } else {
+ /* It's a match, but they just typed a digit, and there is an ambiguous match,
+ so just set the timeout to matchdigittimeout and wait some more */
+ timeout = matchdigittimeout;
+ }
+ } else if (res == 0) {
+ ast_debug(1, "not enough digits (and no ambiguous match)...\n");
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ zt_wait_event(p->subs[index].zfd);
+ ast_hangup(chan);
+ return NULL;
+ } else if (p->callwaiting && !strcmp(exten, "*70")) {
+ ast_verb(3, "Disabling call waiting on %s\n", chan->name);
+ /* Disable call waiting if enabled */
+ p->callwaiting = 0;
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+ chan->name, strerror(errno));
+ }
+ len = 0;
+ ioctl(p->subs[index].zfd,ZT_CONFDIAG,&len);
+ memset(exten, 0, sizeof(exten));
+ timeout = firstdigittimeout;
+
+ } else if (!strcmp(exten,ast_pickup_ext())) {
+ /* Scan all channels and see if any there
+ * ringing channqels with that have call groups
+ * that equal this channels pickup group
+ */
+ if (index == SUB_REAL) {
+ /* Switch us from Third call to Call Wait */
+ if (p->subs[SUB_THREEWAY].owner) {
+ /* If you make a threeway call and the *8# a call, it should actually
+ look like a callwait */
+ alloc_sub(p, SUB_CALLWAIT);
+ swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
+ unalloc_sub(p, SUB_THREEWAY);
+ }
+ zt_enable_ec(p);
+ if (ast_pickup_call(chan)) {
+ ast_debug(1, "No call pickup possible...\n");
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ zt_wait_event(p->subs[index].zfd);
+ }
+ ast_hangup(chan);
+ return NULL;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Got *8# on call not on real\n");
+ ast_hangup(chan);
+ return NULL;
+ }
+
+ } else if (!p->hidecallerid && !strcmp(exten, "*67")) {
+ ast_verb(3, "Disabling Caller*ID on %s\n", chan->name);
+ /* Disable Caller*ID if enabled */
+ p->hidecallerid = 1;
+ if (chan->cid.cid_num)
+ ast_free(chan->cid.cid_num);
+ chan->cid.cid_num = NULL;
+ if (chan->cid.cid_name)
+ ast_free(chan->cid.cid_name);
+ chan->cid.cid_name = NULL;
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+ chan->name, strerror(errno));
+ }
+ len = 0;
+ memset(exten, 0, sizeof(exten));
+ timeout = firstdigittimeout;
+ } else if (p->callreturn && !strcmp(exten, "*69")) {
+ res = 0;
+ if (!ast_strlen_zero(p->lastcid_num)) {
+ res = ast_say_digit_str(chan, p->lastcid_num, "", chan->language);
+ }
+ if (!res)
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ break;
+ } else if (!strcmp(exten, "*78")) {
+ zap_dnd(p, 1);
+ /* Do not disturb */
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ getforward = 0;
+ memset(exten, 0, sizeof(exten));
+ len = 0;
+ } else if (!strcmp(exten, "*79")) {
+ zap_dnd(p, 0);
+ /* Do not disturb */
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ getforward = 0;
+ memset(exten, 0, sizeof(exten));
+ len = 0;
+ } else if (p->cancallforward && !strcmp(exten, "*72")) {
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ getforward = 1;
+ memset(exten, 0, sizeof(exten));
+ len = 0;
+ } else if (p->cancallforward && !strcmp(exten, "*73")) {
+ ast_verb(3, "Cancelling call forwarding on channel %d\n", p->channel);
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ memset(p->call_forward, 0, sizeof(p->call_forward));
+ getforward = 0;
+ memset(exten, 0, sizeof(exten));
+ len = 0;
+ } else if ((p->transfer || p->canpark) && !strcmp(exten, ast_parking_ext()) &&
+ p->subs[SUB_THREEWAY].owner &&
+ ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
+ /* This is a three way call, the main call being a real channel,
+ and we're parking the first call. */
+ ast_masq_park_call(ast_bridged_channel(p->subs[SUB_THREEWAY].owner), chan, 0, NULL);
+ ast_verb(3, "Parking call to '%s'\n", chan->name);
+ break;
+ } else if (!ast_strlen_zero(p->lastcid_num) && !strcmp(exten, "*60")) {
+ ast_verb(3, "Blacklisting number %s\n", p->lastcid_num);
+ res = ast_db_put("blacklist", p->lastcid_num, "1");
+ if (!res) {
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ memset(exten, 0, sizeof(exten));
+ len = 0;
+ }
+ } else if (p->hidecallerid && !strcmp(exten, "*82")) {
+ ast_verb(3, "Enabling Caller*ID on %s\n", chan->name);
+ /* Enable Caller*ID if enabled */
+ p->hidecallerid = 0;
+ if (chan->cid.cid_num)
+ ast_free(chan->cid.cid_num);
+ chan->cid.cid_num = NULL;
+ if (chan->cid.cid_name)
+ ast_free(chan->cid.cid_name);
+ chan->cid.cid_name = NULL;
+ ast_set_callerid(chan, p->cid_num, p->cid_name, NULL);
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+ chan->name, strerror(errno));
+ }
+ len = 0;
+ memset(exten, 0, sizeof(exten));
+ timeout = firstdigittimeout;
+ } else if (!strcmp(exten, "*0")) {
+ struct ast_channel *nbridge =
+ p->subs[SUB_THREEWAY].owner;
+ struct zt_pvt *pbridge = NULL;
+ /* set up the private struct of the bridged one, if any */
+ if (nbridge && ast_bridged_channel(nbridge))
+ pbridge = ast_bridged_channel(nbridge)->tech_pvt;
+ if (nbridge && pbridge &&
+ (nbridge->tech == &zap_tech) &&
+ (ast_bridged_channel(nbridge)->tech == &zap_tech) &&
+ ISTRUNK(pbridge)) {
+ int func = ZT_FLASH;
+ /* Clear out the dial buffer */
+ p->dop.dialstr[0] = '\0';
+ /* flash hookswitch */
+ if ((ioctl(pbridge->subs[SUB_REAL].zfd,ZT_HOOK,&func) == -1) && (errno != EINPROGRESS)) {
+ ast_log(LOG_WARNING, "Unable to flash external trunk on channel %s: %s\n",
+ nbridge->name, strerror(errno));
+ }
+ swap_subs(p, SUB_REAL, SUB_THREEWAY);
+ unalloc_sub(p, SUB_THREEWAY);
+ p->owner = p->subs[SUB_REAL].owner;
+ if (ast_bridged_channel(p->subs[SUB_REAL].owner))
+ ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+ ast_hangup(chan);
+ return NULL;
+ } else {
+ tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ zt_wait_event(p->subs[index].zfd);
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+ swap_subs(p, SUB_REAL, SUB_THREEWAY);
+ unalloc_sub(p, SUB_THREEWAY);
+ p->owner = p->subs[SUB_REAL].owner;
+ ast_hangup(chan);
+ return NULL;
+ }
+ } else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) &&
+ ((exten[0] != '*') || (strlen(exten) > 2))) {
+ ast_debug(1, "Can't match %s from '%s' in context %s\n", exten, chan->cid.cid_num ? chan->cid.cid_num : "<Unknown Caller>", chan->context);
+ break;
+ }
+ if (!timeout)
+ timeout = gendigittimeout;
+ if (len && !ast_ignore_pattern(chan->context, exten))
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+ }
+ break;
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+#ifdef HAVE_PRI
+ if (p->pri) {
+ /* This is a GR-303 trunk actually. Wait for the first ring... */
+ struct ast_frame *f;
+ int res;
+ time_t start;
+
+ time(&start);
+ ast_setstate(chan, AST_STATE_RING);
+ while (time(NULL) < start + 3) {
+ res = ast_waitfor(chan, 1000);
+ if (res) {
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_WARNING, "Whoa, hangup while waiting for first ring!\n");
+ ast_hangup(chan);
+ return NULL;
+ } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
+ res = 1;
+ } else
+ res = 0;
+ ast_frfree(f);
+ if (res) {
+ ast_debug(1, "Got ring!\n");
+ res = 0;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ /* check for SMDI messages */
+ if (p->use_smdi && p->smdi_iface) {
+ smdi_msg = ast_smdi_md_message_wait(p->smdi_iface, SMDI_MD_WAIT_TIMEOUT);
+
+ if (smdi_msg != NULL) {
+ ast_copy_string(chan->exten, smdi_msg->fwd_st, sizeof(chan->exten));
+
+ if (smdi_msg->type == 'B')
+ pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "b");
+ else if (smdi_msg->type == 'N')
+ pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "u");
+
+ ast_debug(1, "Recieved SMDI message on %s\n", chan->name);
+ } else {
+ ast_log(LOG_WARNING, "SMDI enabled but no SMDI message present\n");
+ }
+ }
+
+ if (p->use_callerid && (p->cid_signalling == CID_SIG_SMDI && smdi_msg)) {
+ number = smdi_msg->calling_st;
+
+ /* If we want caller id, we're in a prering state due to a polarity reversal
+ * and we're set to use a polarity reversal to trigger the start of caller id,
+ * grab the caller id and wait for ringing to start... */
+ } else if (p->use_callerid && (chan->_state == AST_STATE_PRERING && (p->cid_start == CID_START_POLARITY || p->cid_start == CID_START_POLARITY_IN))) {
+ /* If set to use DTMF CID signalling, listen for DTMF */
+ if (p->cid_signalling == CID_SIG_DTMF) {
+ int i = 0;
+ cs = NULL;
+ ast_debug(1, "Receiving DTMF cid on "
+ "channel %s\n", chan->name);
+ zt_setlinear(p->subs[index].zfd, 0);
+ res = 2000;
+ for (;;) {
+ struct ast_frame *f;
+ res = ast_waitfor(chan, res);
+ if (res <= 0) {
+ ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. "
+ "Exiting simple switch\n");
+ ast_hangup(chan);
+ return NULL;
+ }
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_DTMF) {
+ dtmfbuf[i++] = f->subclass;
+ ast_debug(1, "CID got digit '%c'\n", f->subclass);
+ res = 2000;
+ }
+ ast_frfree(f);
+ if (chan->_state == AST_STATE_RING ||
+ chan->_state == AST_STATE_RINGING)
+ break; /* Got ring */
+ }
+ dtmfbuf[i] = '\0';
+ zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+ /* Got cid and ring. */
+ ast_debug(1, "CID got string '%s'\n", dtmfbuf);
+ callerid_get_dtmf(dtmfbuf, dtmfcid, &flags);
+ ast_debug(1, "CID is '%s', flags %d\n",
+ dtmfcid, flags);
+ /* If first byte is NULL, we have no cid */
+ if (!ast_strlen_zero(dtmfcid))
+ number = dtmfcid;
+ else
+ number = NULL;
+ /* If set to use V23 Signalling, launch our FSK gubbins and listen for it */
+ } else if ((p->cid_signalling == CID_SIG_V23) || (p->cid_signalling == CID_SIG_V23_JP)) {
+ cs = callerid_new(p->cid_signalling);
+ if (cs) {
+ samples = 0;
+#if 1
+ bump_gains(p);
+#endif
+ /* Take out of linear mode for Caller*ID processing */
+ zt_setlinear(p->subs[index].zfd, 0);
+
+ /* First we wait and listen for the Caller*ID */
+ for (;;) {
+ i = ZT_IOMUX_READ | ZT_IOMUX_SIGEVENT;
+ if ((res = ioctl(p->subs[index].zfd, ZT_IOMUX, &i))) {
+ ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ if (i & ZT_IOMUX_SIGEVENT) {
+ res = zt_get_event(p->subs[index].zfd);
+ ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
+
+ if (p->cid_signalling == CID_SIG_V23_JP) {
+#ifdef ZT_EVENT_RINGBEGIN
+ if (res == ZT_EVENT_RINGBEGIN) {
+ res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+ usleep(1);
+ }
+#endif
+ } else {
+ res = 0;
+ break;
+ }
+ } else if (i & ZT_IOMUX_READ) {
+ res = read(p->subs[index].zfd, buf, sizeof(buf));
+ if (res < 0) {
+ if (errno != ELAST) {
+ ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ break;
+ }
+ samples += res;
+
+ if (p->cid_signalling == CID_SIG_V23_JP) {
+ res = callerid_feed_jp(cs, buf, res, AST_LAW(p));
+ } else {
+ res = callerid_feed(cs, buf, res, AST_LAW(p));
+ }
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno));
+ break;
+ } else if (res)
+ break;
+ else if (samples > (8000 * 10))
+ break;
+ }
+ }
+ if (res == 1) {
+ callerid_get(cs, &name, &number, &flags);
+ ast_log(LOG_NOTICE, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags);
+ }
+ if (res < 0) {
+ ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", chan->name);
+ }
+
+ if (p->cid_signalling == CID_SIG_V23_JP) {
+ res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK);
+ usleep(1);
+ res = 4000;
+ } else {
+
+ /* Finished with Caller*ID, now wait for a ring to make sure there really is a call coming */
+ res = 2000;
+ }
+
+ for (;;) {
+ struct ast_frame *f;
+ res = ast_waitfor(chan, res);
+ if (res <= 0) {
+ ast_log(LOG_WARNING, "CID timed out waiting for ring. "
+ "Exiting simple switch\n");
+ ast_hangup(chan);
+ return NULL;
+ }
+ f = ast_read(chan);
+ ast_frfree(f);
+ if (chan->_state == AST_STATE_RING ||
+ chan->_state == AST_STATE_RINGING)
+ break; /* Got ring */
+ }
+
+ /* We must have a ring by now, so, if configured, lets try to listen for
+ * distinctive ringing */
+ if (p->usedistinctiveringdetection == 1) {
+ len = 0;
+ distMatches = 0;
+ /* Clear the current ring data array so we dont have old data in it. */
+ for (receivedRingT = 0; receivedRingT < (sizeof(curRingData) / sizeof(curRingData[0])); receivedRingT++)
+ curRingData[receivedRingT] = 0;
+ receivedRingT = 0;
+ counter = 0;
+ counter1 = 0;
+ /* Check to see if context is what it should be, if not set to be. */
+ if (strcmp(p->context,p->defcontext) != 0) {
+ ast_copy_string(p->context, p->defcontext, sizeof(p->context));
+ ast_copy_string(chan->context,p->defcontext,sizeof(chan->context));
+ }
+
+ for (;;) {
+ i = ZT_IOMUX_READ | ZT_IOMUX_SIGEVENT;
+ if ((res = ioctl(p->subs[index].zfd, ZT_IOMUX, &i))) {
+ ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ if (i & ZT_IOMUX_SIGEVENT) {
+ res = zt_get_event(p->subs[index].zfd);
+ ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
+ res = 0;
+ /* Let us detect distinctive ring */
+
+ curRingData[receivedRingT] = p->ringt;
+
+ if (p->ringt < p->ringt_base/2)
+ break;
+ /* Increment the ringT counter so we can match it against
+ values in zapata.conf for distinctive ring */
+ if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0])))
+ break;
+ } else if (i & ZT_IOMUX_READ) {
+ res = read(p->subs[index].zfd, buf, sizeof(buf));
+ if (res < 0) {
+ if (errno != ELAST) {
+ ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ break;
+ }
+ if (p->ringt)
+ p->ringt--;
+ if (p->ringt == 1) {
+ res = -1;
+ break;
+ }
+ }
+ }
+ /* this only shows up if you have n of the dring patterns filled in */
+ ast_verb(3, "Detected ring pattern: %d,%d,%d\n",curRingData[0],curRingData[1],curRingData[2]);
+ for (counter = 0; counter < 3; counter++) {
+ /* Check to see if the rings we received match any of the ones in zapata.conf for this
+ channel */
+ distMatches = 0;
+ for (counter1 = 0; counter1 < 3; counter1++) {
+ ast_verb(3, "Ring pattern check range: %d\n", p->drings.ringnum[counter].range);
+ if (p->drings.ringnum[counter].ring[counter1] == -1) {
+ ast_verb(3, "Pattern ignore (-1) detected, so matching pattern %d regardless.\n",
+ curRingData[counter1]);
+ distMatches++;
+ }
+ else if (curRingData[counter1] <= (p->drings.ringnum[counter].ring[counter1] + p->drings.ringnum[counter].range) &&
+ curRingData[counter1] >= (p->drings.ringnum[counter].ring[counter1] - p->drings.ringnum[counter].range)) {
+ ast_verb(3, "Ring pattern matched in range: %d to %d\n",
+ (p->drings.ringnum[counter].ring[counter1] - p->drings.ringnum[counter].range),
+ (p->drings.ringnum[counter].ring[counter1] + p->drings.ringnum[counter].range));
+ distMatches++;
+ }
+ }
+
+ if (distMatches == 3) {
+ /* The ring matches, set the context to whatever is for distinctive ring.. */
+ ast_copy_string(p->context, p->drings.ringContext[counter].contextData, sizeof(p->context));
+ ast_copy_string(chan->context, p->drings.ringContext[counter].contextData, sizeof(chan->context));
+ ast_verb(3, "Distinctive Ring matched context %s\n",p->context);
+ break;
+ }
+ }
+ }
+ /* Restore linear mode (if appropriate) for Caller*ID processing */
+ zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+#if 1
+ restore_gains(p);
+#endif
+ } else
+ ast_log(LOG_WARNING, "Unable to get caller ID space\n");
+ } else {
+ ast_log(LOG_WARNING, "Channel %s in prering "
+ "state, but I have nothing to do. "
+ "Terminating simple switch, should be "
+ "restarted by the actual ring.\n",
+ chan->name);
+ ast_hangup(chan);
+ return NULL;
+ }
+ } else if (p->use_callerid && p->cid_start == CID_START_RING) {
+ if (p->cid_signalling == CID_SIG_DTMF) {
+ int i = 0;
+ cs = NULL;
+ zt_setlinear(p->subs[index].zfd, 0);
+ res = 2000;
+ for (;;) {
+ struct ast_frame *f;
+ res = ast_waitfor(chan, res);
+ if (res <= 0) {
+ ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. "
+ "Exiting simple switch\n");
+ ast_hangup(chan);
+ return NULL;
+ }
+ f = ast_read(chan);
+ if (f->frametype == AST_FRAME_DTMF) {
+ dtmfbuf[i++] = f->subclass;
+ ast_log(LOG_DEBUG, "CID got digit '%c'\n", f->subclass);
+ res = 2000;
+ }
+ ast_frfree(f);
+
+ if (p->ringt_base == p->ringt)
+ break;
+
+ }
+ dtmfbuf[i] = '\0';
+ zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+ /* Got cid and ring. */
+ callerid_get_dtmf(dtmfbuf, dtmfcid, &flags);
+ ast_log(LOG_DEBUG, "CID is '%s', flags %d\n",
+ dtmfcid, flags);
+ /* If first byte is NULL, we have no cid */
+ if (!ast_strlen_zero(dtmfcid))
+ number = dtmfcid;
+ else
+ number = NULL;
+ /* If set to use V23 Signalling, launch our FSK gubbins and listen for it */
+ } else {
+ /* FSK Bell202 callerID */
+ cs = callerid_new(p->cid_signalling);
+ if (cs) {
+#if 1
+ bump_gains(p);
+#endif
+ samples = 0;
+ len = 0;
+ distMatches = 0;
+ /* Clear the current ring data array so we dont have old data in it. */
+ for (receivedRingT = 0; receivedRingT < (sizeof(curRingData) / sizeof(curRingData[0])); receivedRingT++)
+ curRingData[receivedRingT] = 0;
+ receivedRingT = 0;
+ counter = 0;
+ counter1 = 0;
+ /* Check to see if context is what it should be, if not set to be. */
+ if (strcmp(p->context,p->defcontext) != 0) {
+ ast_copy_string(p->context, p->defcontext, sizeof(p->context));
+ ast_copy_string(chan->context,p->defcontext,sizeof(chan->context));
+ }
+
+ /* Take out of linear mode for Caller*ID processing */
+ zt_setlinear(p->subs[index].zfd, 0);
+ for (;;) {
+ i = ZT_IOMUX_READ | ZT_IOMUX_SIGEVENT;
+ if ((res = ioctl(p->subs[index].zfd, ZT_IOMUX, &i))) {
+ ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ if (i & ZT_IOMUX_SIGEVENT) {
+ res = zt_get_event(p->subs[index].zfd);
+ ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
+ /* If we get a PR event, they hung up while processing calerid */
+ if ( res == ZT_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) {
+ ast_log(LOG_DEBUG, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel);
+ p->polarity = POLARITY_IDLE;
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ res = 0;
+ /* Let us detect callerid when the telco uses distinctive ring */
+
+ curRingData[receivedRingT] = p->ringt;
+
+ if (p->ringt < p->ringt_base/2)
+ break;
+ /* Increment the ringT counter so we can match it against
+ values in zapata.conf for distinctive ring */
+ if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0])))
+ break;
+ } else if (i & ZT_IOMUX_READ) {
+ res = read(p->subs[index].zfd, buf, sizeof(buf));
+ if (res < 0) {
+ if (errno != ELAST) {
+ ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ break;
+ }
+ if (p->ringt)
+ p->ringt--;
+ if (p->ringt == 1) {
+ res = -1;
+ break;
+ }
+ samples += res;
+ res = callerid_feed(cs, buf, res, AST_LAW(p));
+ if (res < 0) {
+ ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno));
+ break;
+ } else if (res)
+ break;
+ else if (samples > (8000 * 10))
+ break;
+ }
+ }
+ if (res == 1) {
+ callerid_get(cs, &name, &number, &flags);
+ ast_debug(1, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags);
+ }
+ if (distinctiveringaftercid == 1) {
+ /* Clear the current ring data array so we dont have old data in it. */
+ for (receivedRingT = 0; receivedRingT < 3; receivedRingT++) {
+ curRingData[receivedRingT] = 0;
+ }
+ receivedRingT = 0;
+ ast_verb(3, "Detecting post-CID distinctive ring\n");
+ for (;;) {
+ i = ZT_IOMUX_READ | ZT_IOMUX_SIGEVENT;
+ if ((res = ioctl(p->subs[index].zfd, ZT_IOMUX, &i))) {
+ ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ if (i & ZT_IOMUX_SIGEVENT) {
+ res = zt_get_event(p->subs[index].zfd);
+ ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
+ res = 0;
+ /* Let us detect callerid when the telco uses distinctive ring */
+
+ curRingData[receivedRingT] = p->ringt;
+
+ if (p->ringt < p->ringt_base/2)
+ break;
+ /* Increment the ringT counter so we can match it against
+ values in zapata.conf for distinctive ring */
+ if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0])))
+ break;
+ } else if (i & ZT_IOMUX_READ) {
+ res = read(p->subs[index].zfd, buf, sizeof(buf));
+ if (res < 0) {
+ if (errno != ELAST) {
+ ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+ callerid_free(cs);
+ ast_hangup(chan);
+ return NULL;
+ }
+ break;
+ }
+ if (p->ringt)
+ p->ringt--;
+ if (p->ringt == 1) {
+ res = -1;
+ break;
+ }
+ }
+ }
+ }
+ if (p->usedistinctiveringdetection == 1) {
+ /* this only shows up if you have n of the dring patterns filled in */
+ ast_verb(3, "Detected ring pattern: %d,%d,%d\n",curRingData[0],curRingData[1],curRingData[2]);
+
+ for (counter = 0; counter < 3; counter++) {
+ /* Check to see if the rings we received match any of the ones in zapata.conf for this
+ channel */
+ /* this only shows up if you have n of the dring patterns filled in */
+ ast_verb(3, "Checking %d,%d,%d\n",
+ p->drings.ringnum[counter].ring[0],
+ p->drings.ringnum[counter].ring[1],
+ p->drings.ringnum[counter].ring[2]);
+ distMatches = 0;
+ for (counter1 = 0; counter1 < 3; counter1++) {
+ ast_verb(3, "Ring pattern check range: %d\n", p->drings.ringnum[counter].range);
+ if (p->drings.ringnum[counter].ring[counter1] == -1) {
+ ast_verb(3, "Pattern ignore (-1) detected, so matching pattern %d regardless.\n",
+ curRingData[counter1]);
+ distMatches++;
+ }
+ else if (curRingData[counter1] <= (p->drings.ringnum[counter].ring[counter1] + p->drings.ringnum[counter].range) &&
+ curRingData[counter1] >= (p->drings.ringnum[counter].ring[counter1] - p->drings.ringnum[counter].range)) {
+ ast_verb(3, "Ring pattern matched in range: %d to %d\n",
+ (p->drings.ringnum[counter].ring[counter1] - p->drings.ringnum[counter].range),
+ (p->drings.ringnum[counter].ring[counter1] + p->drings.ringnum[counter].range));
+ distMatches++;
+ }
+ }
+ if (distMatches == 3) {
+ /* The ring matches, set the context to whatever is for distinctive ring.. */
+ ast_copy_string(p->context, p->drings.ringContext[counter].contextData, sizeof(p->context));
+ ast_copy_string(chan->context, p->drings.ringContext[counter].contextData, sizeof(chan->context));
+ ast_verb(3, "Distinctive Ring matched context %s\n",p->context);
+ break;
+ }
+ }
+ }
+ /* Restore linear mode (if appropriate) for Caller*ID processing */
+ zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+#if 1
+ restore_gains(p);
+#endif
+ if (res < 0) {
+ ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", chan->name);
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to get caller ID space\n");
+ }
+ }
+ else
+ cs = NULL;
+
+ if (number)
+ ast_shrink_phone_number(number);
+ ast_set_callerid(chan, number, name, number);
+
+ if (smdi_msg)
+ ASTOBJ_UNREF(smdi_msg, ast_smdi_md_message_destroy);
+
+ if (cs)
+ callerid_free(cs);
+
+ ast_setstate(chan, AST_STATE_RING);
+ chan->rings = 1;
+ p->ringt = p->ringt_base;
+ res = ast_pbx_run(chan);
+ if (res) {
+ ast_hangup(chan);
+ ast_log(LOG_WARNING, "PBX exited non-zero\n");
+ }
+ return NULL;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to handle simple switch with signalling %s on channel %d\n", sig2str(p->sig), p->channel);
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
+ }
+ res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
+ ast_hangup(chan);
+ return NULL;
+}
+
+struct mwi_thread_data {
+ struct zt_pvt *pvt;
+ unsigned char buf[READ_SIZE];
+ size_t len;
+};
+
+static int calc_energy(const unsigned char *buf, int len, int law)
+{
+ int x;
+ int sum = 0;
+
+ if (!len)
+ return 0;
+
+ for (x = 0; x < len; x++)
+ sum += abs(law == AST_FORMAT_ULAW ? AST_MULAW(buf[x]) : AST_ALAW(buf[x]));
+
+ return sum / len;
+}
+
+static void *mwi_thread(void *data)
+{
+ struct mwi_thread_data *mtd = data;
+ struct callerid_state *cs;
+ pthread_attr_t attr;
+ pthread_t threadid;
+ int samples = 0;
+ char *name, *number;
+ int flags;
+ int i, res;
+ unsigned int spill_done = 0;
+ int spill_result = -1;
+
+ if (!(cs = callerid_new(mtd->pvt->cid_signalling))) {
+ mtd->pvt->mwimonitoractive = 0;
+
+ return NULL;
+ }
+
+ callerid_feed(cs, mtd->buf, mtd->len, AST_LAW(mtd->pvt));
+
+ bump_gains(mtd->pvt);
+
+ for (;;) {
+ i = ZT_IOMUX_READ | ZT_IOMUX_SIGEVENT;
+ if ((res = ioctl(mtd->pvt->subs[SUB_REAL].zfd, ZT_IOMUX, &i))) {
+ ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
+ goto quit;
+ }
+
+ if (i & ZT_IOMUX_SIGEVENT) {
+ struct ast_channel *chan;
+
+ /* If we get an event, cancel and go to the simple switch to let it deal with it */
+ res = zt_get_event(mtd->pvt->subs[SUB_REAL].zfd);
+ ast_log(LOG_NOTICE, "Got event %d (%s)... Passing along to ss_thread\n", res, event2str(res));
+ callerid_free(cs);
+
+ restore_gains(mtd->pvt);
+ mtd->pvt->ringt = mtd->pvt->ringt_base;
+
+ if ((chan = zt_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, 0))) {
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if (ast_pthread_create(&threadid, &attr, ss_thread, chan)) {
+ ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", mtd->pvt->channel);
+ res = tone_zone_play_tone(mtd->pvt->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", mtd->pvt->channel);
+ ast_hangup(chan);
+ goto quit;
+ }
+ goto quit_no_clean;
+
+ } else {
+ ast_log(LOG_WARNING, "Could not create channel to handle call\n");
+ }
+ } else if (i & ZT_IOMUX_READ) {
+ if ((res = read(mtd->pvt->subs[SUB_REAL].zfd, mtd->buf, sizeof(mtd->buf))) < 0) {
+ if (errno != ELAST) {
+ ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+ goto quit;
+ }
+ break;
+ }
+ samples += res;
+ if (!spill_done) {
+ if ((spill_result = callerid_feed(cs, mtd->buf, res, AST_LAW(mtd->pvt))) < 0) {
+ ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno));
+ break;
+ } else if (spill_result) {
+ spill_done = 1;
+ }
+ } else {
+ /* keep reading data until the energy level drops below the threshold
+ so we don't get another 'trigger' on the remaining carrier signal
+ */
+ if (calc_energy(mtd->buf, res, AST_LAW(mtd->pvt)) <= mwilevel)
+ break;
+ }
+ if (samples > (8000 * 4)) /*Termination case - time to give up*/
+ break;
+ }
+ }
+
+ if (spill_result == 1) {
+ callerid_get(cs, &name, &number, &flags);
+ if (flags & CID_MSGWAITING) {
+ ast_log(LOG_NOTICE, "mwi: Have Messages on channel %d\n", mtd->pvt->channel);
+ notify_message(mtd->pvt->mailbox, 1);
+ } else if (flags & CID_NOMSGWAITING) {
+ ast_log(LOG_NOTICE, "mwi: No Messages on channel %d\n", mtd->pvt->channel);
+ notify_message(mtd->pvt->mailbox, 0);
+ } else {
+ ast_log(LOG_NOTICE, "mwi: Status unknown on channel %d\n", mtd->pvt->channel);
+ }
+ }
+
+
+quit:
+ callerid_free(cs);
+
+ restore_gains(mtd->pvt);
+
+quit_no_clean:
+ mtd->pvt->mwimonitoractive = 0;
+
+ ast_free(mtd);
+
+ return NULL;
+}
+
+/* destroy a Zaptel channel, identified by its number */
+static int zap_destroy_channel_bynum(int channel)
+{
+ struct zt_pvt *tmp = NULL;
+ struct zt_pvt *prev = NULL;
+
+ tmp = iflist;
+ while (tmp) {
+ if (tmp->channel == channel) {
+ destroy_channel(prev, tmp, 1);
+ return RESULT_SUCCESS;
+ }
+ prev = tmp;
+ tmp = tmp->next;
+ }
+ return RESULT_FAILURE;
+}
+
+static int handle_init_event(struct zt_pvt *i, int event)
+{
+ int res;
+ pthread_t threadid;
+ struct ast_channel *chan;
+
+ /* Handle an event on a given channel for the monitor thread. */
+
+ switch (event) {
+ case ZT_EVENT_NONE:
+ case ZT_EVENT_BITSCHANGED:
+ break;
+ case ZT_EVENT_WINKFLASH:
+ case ZT_EVENT_RINGOFFHOOK:
+ if (i->inalarm) break;
+ if (i->radio) break;
+ /* Got a ring/answer. What kind of channel are we? */
+ switch (i->sig) {
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ res = zt_set_hook(i->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+ if (res && (errno == EBUSY))
+ break;
+ if (i->cidspill) {
+ /* Cancel VMWI spill */
+ ast_free(i->cidspill);
+ i->cidspill = NULL;
+ }
+ if (i->immediate) {
+ zt_enable_ec(i);
+ /* The channel is immediately up. Start right away */
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_RINGTONE);
+ chan = zt_new(i, AST_STATE_RING, 1, SUB_REAL, 0, 0);
+ if (!chan) {
+ ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
+ }
+ } else {
+ /* Check for callerid, digits, etc */
+ chan = zt_new(i, AST_STATE_RESERVED, 0, SUB_REAL, 0, 0);
+ if (chan) {
+ if (has_voicemail(i))
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_STUTTER);
+ else
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play dialtone on channel %d, do you have defaultzone and loadzone defined?\n", i->channel);
+ if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+ ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
+ ast_hangup(chan);
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to create channel\n");
+ }
+ break;
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+ i->ringt = i->ringt_base;
+ /* Fall through */
+ case SIG_EMWINK:
+ case SIG_FEATD:
+ case SIG_FEATDMF:
+ case SIG_FEATDMF_TA:
+ case SIG_E911:
+ case SIG_FGC_CAMA:
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_SFWINK:
+ case SIG_SF_FEATD:
+ case SIG_SF_FEATDMF:
+ case SIG_SF_FEATB:
+ case SIG_SF:
+ /* Check for callerid, digits, etc */
+ if (i->cid_start == CID_START_POLARITY_IN) {
+ chan = zt_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0);
+ } else {
+ chan = zt_new(i, AST_STATE_RING, 0, SUB_REAL, 0, 0);
+ }
+ if (chan && ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+ ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
+ ast_hangup(chan);
+ } else if (!chan) {
+ ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel);
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to handle ring/answer with signalling %s on channel %d\n", sig2str(i->sig), i->channel);
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
+ return -1;
+ }
+ break;
+ case ZT_EVENT_NOALARM:
+ i->inalarm = 0;
+ ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel);
+ manager_event(EVENT_FLAG_SYSTEM, "AlarmClear",
+ "Channel: %d\r\n", i->channel);
+ break;
+ case ZT_EVENT_ALARM:
+ i->inalarm = 1;
+ res = get_alarms(i);
+ ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", i->channel, alarm2str(res));
+ manager_event(EVENT_FLAG_SYSTEM, "Alarm",
+ "Alarm: %s\r\n"
+ "Channel: %d\r\n",
+ alarm2str(res), i->channel);
+ /* fall thru intentionally */
+ case ZT_EVENT_ONHOOK:
+ if (i->radio)
+ break;
+ /* Back on hook. Hang up. */
+ switch (i->sig) {
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FEATD:
+ case SIG_FEATDMF:
+ case SIG_FEATDMF_TA:
+ case SIG_E911:
+ case SIG_FGC_CAMA:
+ case SIG_FGC_CAMAMF:
+ case SIG_FEATB:
+ case SIG_EM:
+ case SIG_EM_E1:
+ case SIG_EMWINK:
+ case SIG_SF_FEATD:
+ case SIG_SF_FEATDMF:
+ case SIG_SF_FEATB:
+ case SIG_SF:
+ case SIG_SFWINK:
+ case SIG_FXSLS:
+ case SIG_FXSGS:
+ case SIG_FXSKS:
+ case SIG_GR303FXSKS:
+ zt_disable_ec(i);
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
+ zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK);
+ break;
+ case SIG_GR303FXOKS:
+ case SIG_FXOKS:
+ zt_disable_ec(i);
+ /* Diddle the battery for the zhone */
+#ifdef ZHONE_HACK
+ zt_set_hook(i->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+ usleep(1);
+#endif
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
+ zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK);
+ break;
+ case SIG_PRI:
+ case SIG_SS7:
+ case SIG_BRI:
+ case SIG_BRI_PTMP:
+ zt_disable_ec(i);
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", sig2str(i->sig), i->channel);
+ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
+ return -1;
+ }
+ break;
+ case ZT_EVENT_POLARITY:
+ switch (i->sig) {
+ case SIG_FXSLS:
+ case SIG_FXSKS:
+ case SIG_FXSGS:
+ /* We have already got a PR before the channel was
+ created, but it wasn't handled. We need polarity
+ to be REV for remote hangup detection to work.
+ At least in Spain */
+ if (i->hanguponpolarityswitch)
+ i->polarity = POLARITY_REV;
+ if (i->cid_start == CID_START_POLARITY || i->cid_start == CID_START_POLARITY_IN) {
+ i->polarity = POLARITY_REV;
+ ast_verb(2, "Starting post polarity "
+ "CID detection on channel %d\n",
+ i->channel);
+ chan = zt_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0);
+ if (chan && ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+ ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
+ }
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "handle_init_event detected "
+ "polarity reversal on non-FXO (SIG_FXS) "
+ "interface %d\n", i->channel);
+ }
+ break;
+ case ZT_EVENT_REMOVED: /* destroy channel */
+ ast_log(LOG_NOTICE,
+ "Got ZT_EVENT_REMOVED. Destroying channel %d\n",
+ i->channel);
+ zap_destroy_channel_bynum(i->channel);
+ break;
+ }
+ return 0;
+}
+
+static void *do_monitor(void *data)
+{
+ int count, res, res2, spoint, pollres=0;
+ struct zt_pvt *i;
+ struct zt_pvt *last = NULL;
+ time_t thispass = 0, lastpass = 0;
+ int found;
+ char buf[1024];
+ struct pollfd *pfds=NULL;
+ int lastalloc = -1;
+ /* This thread monitors all the frame relay interfaces which are not yet in use
+ (and thus do not have a separate thread) indefinitely */
+ /* From here on out, we die whenever asked */
+#if 0
+ if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
+ ast_log(LOG_WARNING, "Unable to set cancel type to asynchronous\n");
+ return NULL;
+ }
+ ast_debug(1, "Monitor starting...\n");
+#endif
+ for (;;) {
+ /* Lock the interface list */
+ ast_mutex_lock(&iflock);
+ if (!pfds || (lastalloc != ifcount)) {
+ if (pfds) {
+ ast_free(pfds);
+ pfds = NULL;
+ }
+ if (ifcount) {
+ if (!(pfds = ast_calloc(1, ifcount * sizeof(*pfds)))) {
+ ast_mutex_unlock(&iflock);
+ return NULL;
+ }
+ }
+ lastalloc = ifcount;
+ }
+ /* Build the stuff we're going to poll on, that is the socket of every
+ zt_pvt that does not have an associated owner channel */
+ count = 0;
+ i = iflist;
+ while (i) {
+ if ((i->subs[SUB_REAL].zfd > -1) && i->sig && (!i->radio)) {
+ if (!i->owner && !i->subs[SUB_REAL].owner && !i->mwimonitoractive) {
+ /* This needs to be watched, as it lacks an owner */
+ pfds[count].fd = i->subs[SUB_REAL].zfd;
+ pfds[count].events = POLLPRI;
+ pfds[count].revents = 0;
+ /* If we are monitoring for VMWI or sending CID, we need to
+ read from the channel as well */
+ if (i->cidspill || i->mwimonitor)
+ pfds[count].events |= POLLIN;
+ count++;
+ }
+ }
+ i = i->next;
+ }
+ /* Okay, now that we know what to do, release the interface lock */
+ ast_mutex_unlock(&iflock);
+
+ pthread_testcancel();
+ /* Wait at least a second for something to happen */
+ res = poll(pfds, count, 1000);
+ pthread_testcancel();
+ /* Okay, poll has finished. Let's see what happened. */
+ if (res < 0) {
+ if ((errno != EAGAIN) && (errno != EINTR))
+ ast_log(LOG_WARNING, "poll return %d: %s\n", res, strerror(errno));
+ continue;
+ }
+ /* Alright, lock the interface list again, and let's look and see what has
+ happened */
+ ast_mutex_lock(&iflock);
+ found = 0;
+ spoint = 0;
+ lastpass = thispass;
+ thispass = time(NULL);
+ i = iflist;
+ while (i) {
+ if (thispass != lastpass) {
+ if (!found && ((i == last) || ((i == iflist) && !last))) {
+ last = i;
+ if (last) {
+ if (!last->cidspill && !last->owner && !ast_strlen_zero(last->mailbox) && (thispass - last->onhooktime > 3) &&
+ (last->sig & __ZT_SIG_FXO)) {
+ res = has_voicemail(last);
+ if (last->msgstate != res) {
+ int x;
+ ast_debug(1, "Message status for %s changed from %d to %d on %d\n", last->mailbox, last->msgstate, res, last->channel);
+#ifdef ZT_VMWI
+ res2 = ioctl(last->subs[SUB_REAL].zfd, ZT_VMWI, res);
+ if (res2)
+ ast_log(LOG_DEBUG, "Unable to control message waiting led on channel %d\n", last->channel);
+#endif
+ x = ZT_FLUSH_BOTH;
+ res2 = ioctl(last->subs[SUB_REAL].zfd, ZT_FLUSH, &x);
+ if (res2)
+ ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", last->channel);
+ if ((last->cidspill = ast_calloc(1, MAX_CALLERID_SIZE))) {
+ /* Turn on on hook transfer for 4 seconds */
+ x = 4000;
+ ioctl(last->subs[SUB_REAL].zfd, ZT_ONHOOKTRANSFER, &x);
+ last->cidlen = vmwi_generate(last->cidspill, res, 1, AST_LAW(last));
+ last->cidpos = 0;
+ last->msgstate = res;
+ last->onhooktime = thispass;
+ }
+ found ++;
+ }
+ }
+ last = last->next;
+ }
+ }
+ }
+ if ((i->subs[SUB_REAL].zfd > -1) && i->sig) {
+ if (i->radio && !i->owner)
+ {
+ res = zt_get_event(i->subs[SUB_REAL].zfd);
+ if (res)
+ {
+ ast_debug(1, "Monitor doohicky got event %s on radio channel %d\n", event2str(res), i->channel);
+ /* Don't hold iflock while handling init events */
+ ast_mutex_unlock(&iflock);
+ handle_init_event(i, res);
+ ast_mutex_lock(&iflock);
+ }
+ i = i->next;
+ continue;
+ }
+ pollres = ast_fdisset(pfds, i->subs[SUB_REAL].zfd, count, &spoint);
+ if (pollres & POLLIN) {
+ if (i->owner || i->subs[SUB_REAL].owner) {
+#ifdef HAVE_PRI
+ if (!i->pri)
+#endif
+ ast_log(LOG_WARNING, "Whoa.... I'm owned but found (%d) in read...\n", i->subs[SUB_REAL].zfd);
+ i = i->next;
+ continue;
+ }
+ if (!i->cidspill && !i->mwimonitor) {
+ ast_log(LOG_WARNING, "Whoa.... I'm reading but have no cidspill (%d)...\n", i->subs[SUB_REAL].zfd);
+ i = i->next;
+ continue;
+ }
+ res = read(i->subs[SUB_REAL].zfd, buf, sizeof(buf));
+ if (res > 0) {
+ if (i->mwimonitor) {
+ if (calc_energy((unsigned char *) buf, res, AST_LAW(i)) > mwilevel) {
+ pthread_attr_t attr;
+ pthread_t threadid;
+ struct mwi_thread_data *mtd;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ ast_log(LOG_DEBUG, "Maybe some MWI on port %d!\n", i->channel);
+ if ((mtd = ast_calloc(1, sizeof(*mtd)))) {
+ mtd->pvt = i;
+ memcpy(mtd->buf, buf, res);
+ mtd->len = res;
+ if (ast_pthread_create_background(&threadid, &attr, mwi_thread, mtd)) {
+ ast_log(LOG_WARNING, "Unable to start mwi thread on channel %d\n", i->channel);
+ ast_free(mtd);
+ }
+ i->mwimonitoractive = 1;
+ }
+ }
+ } else if (i->cidspill) {
+ /* We read some number of bytes. Write an equal amount of data */
+ if (res > i->cidlen - i->cidpos)
+ res = i->cidlen - i->cidpos;
+ res2 = write(i->subs[SUB_REAL].zfd, i->cidspill + i->cidpos, res);
+ if (res2 > 0) {
+ i->cidpos += res2;
+ if (i->cidpos >= i->cidlen) {
+ free(i->cidspill);
+ i->cidspill = 0;
+ i->cidpos = 0;
+ i->cidlen = 0;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Write failed: %s\n", strerror(errno));
+ i->msgstate = -1;
+ }
+ }
+ } else {
+ ast_log(LOG_WARNING, "Read failed with %d: %s\n", res, strerror(errno));
+ }
+ ast_debug(1, "Monitor doohicky got event %s on channel %d\n", event2str(res), i->channel);
+ /* Don't hold iflock while handling init events -- race with chlock */
+ ast_mutex_unlock(&iflock);
+ handle_init_event(i, res);
+ ast_mutex_lock(&iflock);
+ }
+ if (pollres & POLLPRI) {
+ if (i->owner || i->subs[SUB_REAL].owner) {
+#ifdef HAVE_PRI
+ if (!i->pri)
+#endif
+ ast_log(LOG_WARNING, "Whoa.... I'm owned but found (%d)...\n", i->subs[SUB_REAL].zfd);
+ i = i->next;
+ continue;
+ }
+ res = zt_get_event(i->subs[SUB_REAL].zfd);
+ ast_debug(1, "Monitor doohicky got event %s on channel %d\n", event2str(res), i->channel);
+ /* Don't hold iflock while handling init events */
+ ast_mutex_unlock(&iflock);
+ handle_init_event(i, res);
+ ast_mutex_lock(&iflock);
+ }
+ }
+ i=i->next;
+ }
+ ast_mutex_unlock(&iflock);
+ }
+ /* Never reached */
+ return NULL;
+
+}
+
+static int restart_monitor(void)
+{
+ /* If we're supposed to be stopped -- stay stopped */
+ if (monitor_thread == AST_PTHREADT_STOP)
+ return 0;
+ ast_mutex_lock(&monlock);
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ /* Wake up the thread */
+ pthread_kill(monitor_thread, SIGURG);
+ } else {
+ /* Start a new monitor */
+ if (ast_pthread_create_detached_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+#ifdef HAVE_PRI
+static int pri_resolve_span(int *span, int channel, int offset, struct zt_spaninfo *si)
+{
+ int x;
+ int trunkgroup;
+ /* Get appropriate trunk group if there is one */
+ trunkgroup = pris[*span].mastertrunkgroup;
+ if (trunkgroup) {
+ /* Select a specific trunk group */
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (pris[x].trunkgroup == trunkgroup) {
+ *span = x;
+ return 0;
+ }
+ }
+ ast_log(LOG_WARNING, "Channel %d on span %d configured to use nonexistent trunk group %d\n", channel, *span, trunkgroup);
+ *span = -1;
+ } else {
+ if (pris[*span].trunkgroup) {
+ ast_log(LOG_WARNING, "Unable to use span %d implicitly since it is trunk group %d (please use spanmap)\n", *span, pris[*span].trunkgroup);
+ *span = -1;
+ } else if (pris[*span].mastertrunkgroup) {
+ ast_log(LOG_WARNING, "Unable to use span %d implicitly since it is already part of trunk group %d\n", *span, pris[*span].mastertrunkgroup);
+ *span = -1;
+ } else {
+ if (si->totalchans == 31) { /* if it's an E1 */
+ pris[*span].dchannels[0] = 16 + offset;
+ } else { /* T1 or BRI: D Channel is the last Channel */
+ pris[*span].dchannels[0] =
+ si->totalchans + offset;
+ }
+ pris[*span].dchanavail[0] |= DCHAN_PROVISIONED;
+ pris[*span].offset = offset;
+ pris[*span].span = *span + 1;
+ }
+ }
+ return 0;
+}
+
+static int pri_create_trunkgroup(int trunkgroup, int *channels)
+{
+ struct zt_spaninfo si;
+ ZT_PARAMS p;
+ int fd;
+ int span;
+ int ospan=0;
+ int x,y;
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (pris[x].trunkgroup == trunkgroup) {
+ ast_log(LOG_WARNING, "Trunk group %d already exists on span %d, Primary d-channel %d\n", trunkgroup, x + 1, pris[x].dchannels[0]);
+ return -1;
+ }
+ }
+ for (y = 0; y < NUM_DCHANS; y++) {
+ if (!channels[y])
+ break;
+ memset(&si, 0, sizeof(si));
+ memset(&p, 0, sizeof(p));
+ fd = open("/dev/zap/channel", O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Failed to open channel: %s\n", strerror(errno));
+ return -1;
+ }
+ x = channels[y];
+ if (ioctl(fd, ZT_SPECIFY, &x)) {
+ ast_log(LOG_WARNING, "Failed to specify channel %d: %s\n", channels[y], strerror(errno));
+ zt_close(fd);
+ return -1;
+ }
+ if (ioctl(fd, ZT_GET_PARAMS, &p)) {
+ ast_log(LOG_WARNING, "Failed to get channel parameters for channel %d: %s\n", channels[y], strerror(errno));
+ return -1;
+ }
+ if (ioctl(fd, ZT_SPANSTAT, &si)) {
+ ast_log(LOG_WARNING, "Failed go get span information on channel %d (span %d)\n", channels[y], p.spanno);
+ zt_close(fd);
+ return -1;
+ }
+ span = p.spanno - 1;
+ if (pris[span].trunkgroup) {
+ ast_log(LOG_WARNING, "Span %d is already provisioned for trunk group %d\n", span + 1, pris[span].trunkgroup);
+ zt_close(fd);
+ return -1;
+ }
+ if (pris[span].pvts[0]) {
+ ast_log(LOG_WARNING, "Span %d is already provisioned with channels (implicit PRI maybe?)\n", span + 1);
+ zt_close(fd);
+ return -1;
+ }
+ if (!y) {
+ pris[span].trunkgroup = trunkgroup;
+ pris[span].offset = channels[y] - p.chanpos;
+ ospan = span;
+ }
+ pris[ospan].dchannels[y] = channels[y];
+ pris[ospan].dchanavail[y] |= DCHAN_PROVISIONED;
+ pris[span].span = span + 1;
+ zt_close(fd);
+ }
+ return 0;
+}
+
+static int pri_create_spanmap(int span, int trunkgroup, int logicalspan)
+{
+ if (pris[span].mastertrunkgroup) {
+ ast_log(LOG_WARNING, "Span %d is already part of trunk group %d, cannot add to trunk group %d\n", span + 1, pris[span].mastertrunkgroup, trunkgroup);
+ return -1;
+ }
+ pris[span].mastertrunkgroup = trunkgroup;
+ pris[span].prilogicalspan = logicalspan;
+ return 0;
+}
+
+#endif
+
+#ifdef HAVE_SS7
+
+static unsigned int parse_pointcode(const char *pcstring)
+{
+ unsigned int code1, code2, code3;
+ int numvals;
+
+ numvals = sscanf(pcstring, "%d-%d-%d", &code1, &code2, &code3);
+ if (numvals == 1)
+ return code1;
+ if (numvals == 3)
+ return (code1 << 16) | (code2 << 8) | code3;
+
+ return 0;
+}
+
+static struct zt_ss7 * ss7_resolve_linkset(int linkset)
+{
+ if ((linkset < 0) || (linkset >= NUM_SPANS))
+ return NULL;
+ else
+ return &linksets[linkset - 1];
+}
+#endif /* HAVE_SS7 */
+
+/* converts a Zaptel sigtype to signalling as can be configured from
+ * zapata.conf.
+ * While both have basically the same values, this will later be the
+ * place to add filters and sanity checks
+ */
+static int sigtype_to_signalling(int sigtype)
+{
+ return sigtype;
+}
+
+static struct zt_pvt *mkintf(int channel, struct zt_chan_conf conf, struct zt_pri *pri, int reloading)
+{
+ /* Make a zt_pvt structure for this interface (or CRV if "pri" is specified) */
+ struct zt_pvt *tmp = NULL, *tmp2, *prev = NULL;
+ char fn[80];
+#if 1
+ struct zt_bufferinfo bi;
+#endif
+ struct zt_spaninfo si;
+ int res;
+ int span=0;
+ int here = 0;
+ int x;
+ struct zt_pvt **wlist;
+ struct zt_pvt **wend;
+ ZT_PARAMS p;
+
+ wlist = &iflist;
+ wend = &ifend;
+
+#ifdef HAVE_PRI
+ if (pri) {
+ wlist = &pri->crvs;
+ wend = &pri->crvend;
+ }
+#endif
+
+ tmp2 = *wlist;
+ prev = NULL;
+
+ while (tmp2) {
+ if (!tmp2->destroy) {
+ if (tmp2->channel == channel) {
+ tmp = tmp2;
+ here = 1;
+ break;
+ }
+ if (tmp2->channel > channel) {
+ break;
+ }
+ }
+ prev = tmp2;
+ tmp2 = tmp2->next;
+ }
+
+ if (!here && !reloading) {
+ if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ ast_mutex_init(&tmp->lock);
+ ifcount++;
+ for (x = 0; x < 3; x++)
+ tmp->subs[x].zfd = -1;
+ tmp->channel = channel;
+ }
+
+ if (tmp) {
+ if (!here) {
+ if ((channel != CHAN_PSEUDO) && !pri) {
+ snprintf(fn, sizeof(fn), "%d", channel);
+ /* Open non-blocking */
+ if (!here)
+ tmp->subs[SUB_REAL].zfd = zt_open(fn);
+ /* Allocate a zapata structure */
+ if (tmp->subs[SUB_REAL].zfd < 0) {
+ ast_log(LOG_ERROR, "Unable to open channel %d: %s\nhere = %d, tmp->channel = %d, channel = %d\n", channel, strerror(errno), here, tmp->channel, channel);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ memset(&p, 0, sizeof(p));
+ res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &p);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "Unable to get parameters\n");
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (conf.is_sig_auto)
+ conf.chan.sig = sigtype_to_signalling(p.sigtype);
+ if (p.sigtype != (conf.chan.sig & 0x3ffff)) {
+ ast_log(LOG_ERROR, "Signalling requested on channel %d is %s but line is in %s signalling\n", channel, sig2str(conf.chan.sig), sig2str(p.sigtype));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ tmp->law = p.curlaw;
+ tmp->span = p.spanno;
+ span = p.spanno - 1;
+ } else {
+ if (channel == CHAN_PSEUDO)
+ conf.chan.sig = 0;
+ else if ((conf.chan.sig != SIG_FXOKS) && (conf.chan.sig != SIG_FXSKS)) {
+ ast_log(LOG_ERROR, "CRV's must use FXO/FXS Kewl Start (fxo_ks/fxs_ks) signalling only.\n");
+ return NULL;
+ }
+ }
+#ifdef HAVE_SS7
+ if (conf.chan.sig == SIG_SS7) {
+ struct zt_ss7 *ss7;
+ int clear = 0;
+ if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &clear)) {
+ ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d of span %d: %s\n", channel, p.spanno, strerror(errno));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+
+ ss7 = ss7_resolve_linkset(cur_linkset);
+ if (!ss7) {
+ ast_log(LOG_ERROR, "Unable to find linkset %d\n", cur_linkset);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (cur_cicbeginswith < 0) {
+ ast_log(LOG_ERROR, "Need to set cicbeginswith for the channels!\n");
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+
+ tmp->cic = cur_cicbeginswith++;
+
+ /* DB: Add CIC's DPC information */
+ tmp->dpc = cur_defaultdpc;
+
+ tmp->ss7 = ss7;
+ tmp->ss7call = NULL;
+ ss7->pvts[ss7->numchans++] = tmp;
+
+ ast_copy_string(linksets[span].internationalprefix, conf.ss7.internationalprefix, sizeof(linksets[span].internationalprefix));
+ ast_copy_string(linksets[span].nationalprefix, conf.ss7.nationalprefix, sizeof(linksets[span].nationalprefix));
+ ast_copy_string(linksets[span].subscriberprefix, conf.ss7.subscriberprefix, sizeof(linksets[span].subscriberprefix));
+ ast_copy_string(linksets[span].unknownprefix, conf.ss7.unknownprefix, sizeof(linksets[span].unknownprefix));
+
+ linksets[span].called_nai = conf.ss7.called_nai;
+ linksets[span].calling_nai = conf.ss7.calling_nai;
+ }
+#endif
+#ifdef HAVE_PRI
+ if ((conf.chan.sig == SIG_PRI) || (conf.chan.sig == SIG_BRI) || (conf.chan.sig == SIG_BRI_PTMP) || (conf.chan.sig == SIG_GR303FXOKS) || (conf.chan.sig == SIG_GR303FXSKS)) {
+ int offset;
+ int myswitchtype;
+ int matchesdchan;
+ int x,y;
+ offset = 0;
+ if (((conf.chan.sig == SIG_PRI) || (conf.chan.sig == SIG_BRI) || (conf.chan.sig == SIG_BRI_PTMP))
+ && ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &offset)) {
+ ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d of span %d: %s\n", channel, p.spanno, strerror(errno));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (span >= NUM_SPANS) {
+ ast_log(LOG_ERROR, "Channel %d does not lie on a span I know of (%d)\n", channel, span);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ } else {
+ si.spanno = 0;
+ if (ioctl(tmp->subs[SUB_REAL].zfd,ZT_SPANSTAT,&si) == -1) {
+ ast_log(LOG_ERROR, "Unable to get span status: %s\n", strerror(errno));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ /* Store the logical span first based upon the real span */
+ tmp->logicalspan = pris[span].prilogicalspan;
+ pri_resolve_span(&span, channel, (channel - p.chanpos), &si);
+ if (span < 0) {
+ ast_log(LOG_WARNING, "Channel %d: Unable to find locate channel/trunk group!\n", channel);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if ((conf.chan.sig == SIG_PRI) ||
+ (conf.chan.sig == SIG_BRI) ||
+ (conf.chan.sig == SIG_BRI_PTMP))
+ myswitchtype = conf.pri.switchtype;
+ else
+ myswitchtype = PRI_SWITCH_GR303_TMC;
+ /* Make sure this isn't a d-channel */
+ matchesdchan=0;
+ for (x = 0; x < NUM_SPANS; x++) {
+ for (y = 0; y < NUM_DCHANS; y++) {
+ if (pris[x].dchannels[y] == tmp->channel) {
+ matchesdchan = 1;
+ break;
+ }
+ }
+ }
+ offset = p.chanpos;
+ if (!matchesdchan) {
+ if (pris[span].nodetype && (pris[span].nodetype != conf.pri.nodetype)) {
+ ast_log(LOG_ERROR, "Span %d is already a %s node\n", span + 1, pri_node2str(pris[span].nodetype));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (pris[span].switchtype && (pris[span].switchtype != myswitchtype)) {
+ ast_log(LOG_ERROR, "Span %d is already a %s switch\n", span + 1, pri_switch2str(pris[span].switchtype));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if ((pris[span].dialplan) && (pris[span].dialplan != conf.pri.dialplan)) {
+ ast_log(LOG_ERROR, "Span %d is already a %s dialing plan\n", span + 1, dialplan2str(pris[span].dialplan));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (!ast_strlen_zero(pris[span].idledial) && strcmp(pris[span].idledial, conf.pri.idledial)) {
+ ast_log(LOG_ERROR, "Span %d already has idledial '%s'.\n", span + 1, conf.pri.idledial);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (!ast_strlen_zero(pris[span].idleext) && strcmp(pris[span].idleext, conf.pri.idleext)) {
+ ast_log(LOG_ERROR, "Span %d already has idleext '%s'.\n", span + 1, conf.pri.idleext);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (pris[span].minunused && (pris[span].minunused != conf.pri.minunused)) {
+ ast_log(LOG_ERROR, "Span %d already has minunused of %d.\n", span + 1, conf.pri.minunused);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (pris[span].minidle && (pris[span].minidle != conf.pri.minidle)) {
+ ast_log(LOG_ERROR, "Span %d already has minidle of %d.\n", span + 1, conf.pri.minidle);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (pris[span].numchans >= MAX_CHANNELS) {
+ ast_log(LOG_ERROR, "Unable to add channel %d: Too many channels in trunk group %d!\n", channel,
+ pris[span].trunkgroup);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+
+ pris[span].sig = conf.chan.sig;
+ pris[span].nodetype = conf.pri.nodetype;
+ pris[span].switchtype = myswitchtype;
+ pris[span].nsf = conf.pri.nsf;
+ pris[span].dialplan = conf.pri.dialplan;
+ pris[span].localdialplan = conf.pri.localdialplan;
+ pris[span].pvts[pris[span].numchans++] = tmp;
+ pris[span].minunused = conf.pri.minunused;
+ pris[span].minidle = conf.pri.minidle;
+ pris[span].overlapdial = conf.pri.overlapdial;
+ pris[span].facilityenable = conf.pri.facilityenable;
+ ast_copy_string(pris[span].idledial, conf.pri.idledial, sizeof(pris[span].idledial));
+ ast_copy_string(pris[span].idleext, conf.pri.idleext, sizeof(pris[span].idleext));
+ ast_copy_string(pris[span].internationalprefix, conf.pri.internationalprefix, sizeof(pris[span].internationalprefix));
+ ast_copy_string(pris[span].nationalprefix, conf.pri.nationalprefix, sizeof(pris[span].nationalprefix));
+ ast_copy_string(pris[span].localprefix, conf.pri.localprefix, sizeof(pris[span].localprefix));
+ ast_copy_string(pris[span].privateprefix, conf.pri.privateprefix, sizeof(pris[span].privateprefix));
+ ast_copy_string(pris[span].unknownprefix, conf.pri.unknownprefix, sizeof(pris[span].unknownprefix));
+ pris[span].resetinterval = conf.pri.resetinterval;
+
+ tmp->pri = &pris[span];
+ tmp->prioffset = offset;
+ tmp->call = NULL;
+ } else {
+ ast_log(LOG_ERROR, "Channel %d is reserved for D-channel.\n", offset);
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ }
+ } else {
+ tmp->prioffset = 0;
+ }
+#endif
+ } else {
+ conf.chan.sig = tmp->sig;
+ conf.chan.radio = tmp->radio;
+ memset(&p, 0, sizeof(p));
+ if (tmp->subs[SUB_REAL].zfd > -1)
+ res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &p);
+ }
+ /* Adjust starttime on loopstart and kewlstart trunks to reasonable values */
+ if ((conf.chan.sig == SIG_FXSKS) || (conf.chan.sig == SIG_FXSLS) ||
+ (conf.chan.sig == SIG_EM) || (conf.chan.sig == SIG_EM_E1) || (conf.chan.sig == SIG_EMWINK) ||
+ (conf.chan.sig == SIG_FEATD) || (conf.chan.sig == SIG_FEATDMF) || (conf.chan.sig == SIG_FEATDMF_TA) ||
+ (conf.chan.sig == SIG_FEATB) || (conf.chan.sig == SIG_E911) ||
+ (conf.chan.sig == SIG_SF) || (conf.chan.sig == SIG_SFWINK) || (conf.chan.sig == SIG_FGC_CAMA) || (conf.chan.sig == SIG_FGC_CAMAMF) ||
+ (conf.chan.sig == SIG_SF_FEATD) || (conf.chan.sig == SIG_SF_FEATDMF) ||
+ (conf.chan.sig == SIG_SF_FEATB)) {
+ p.starttime = 250;
+ }
+ if (conf.chan.radio) {
+ /* XXX Waiting to hear back from Jim if these should be adjustable XXX */
+ p.channo = channel;
+ p.rxwinktime = 1;
+ p.rxflashtime = 1;
+ p.starttime = 1;
+ p.debouncetime = 5;
+ }
+ if (!conf.chan.radio) {
+ p.channo = channel;
+ /* Override timing settings based on config file */
+ if (conf.timing.prewinktime >= 0)
+ p.prewinktime = conf.timing.prewinktime;
+ if (conf.timing.preflashtime >= 0)
+ p.preflashtime = conf.timing.preflashtime;
+ if (conf.timing.winktime >= 0)
+ p.winktime = conf.timing.winktime;
+ if (conf.timing.flashtime >= 0)
+ p.flashtime = conf.timing.flashtime;
+ if (conf.timing.starttime >= 0)
+ p.starttime = conf.timing.starttime;
+ if (conf.timing.rxwinktime >= 0)
+ p.rxwinktime = conf.timing.rxwinktime;
+ if (conf.timing.rxflashtime >= 0)
+ p.rxflashtime = conf.timing.rxflashtime;
+ if (conf.timing.debouncetime >= 0)
+ p.debouncetime = conf.timing.debouncetime;
+ }
+
+ /* dont set parms on a pseudo-channel (or CRV) */
+ if (tmp->subs[SUB_REAL].zfd >= 0)
+ {
+ res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_SET_PARAMS, &p);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "Unable to set parameters\n");
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ }
+#if 1
+ if (!here && (tmp->subs[SUB_REAL].zfd > -1)) {
+ memset(&bi, 0, sizeof(bi));
+ res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_GET_BUFINFO, &bi);
+ if (!res) {
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = numbufs;
+ res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_SET_BUFINFO, &bi);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set buffer policy on channel %d\n", channel);
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d\n", channel);
+ }
+#endif
+ tmp->immediate = conf.chan.immediate;
+ tmp->transfertobusy = conf.chan.transfertobusy;
+ if (conf.chan.sig & __ZT_SIG_FXS)
+ tmp->mwimonitor = conf.chan.mwimonitor;
+ tmp->sig = conf.chan.sig;
+ tmp->outsigmod = conf.chan.outsigmod;
+ tmp->radio = conf.chan.radio;
+ tmp->ringt_base = ringt_base;
+ tmp->firstradio = 0;
+ if ((conf.chan.sig == SIG_FXOKS) || (conf.chan.sig == SIG_FXOLS) || (conf.chan.sig == SIG_FXOGS))
+ tmp->permcallwaiting = conf.chan.callwaiting;
+ else
+ tmp->permcallwaiting = 0;
+ /* Flag to destroy the channel must be cleared on new mkif. Part of changes for reload to work */
+ tmp->destroy = 0;
+ tmp->drings = drings;
+ tmp->usedistinctiveringdetection = usedistinctiveringdetection;
+ tmp->callwaitingcallerid = conf.chan.callwaitingcallerid;
+ tmp->threewaycalling = conf.chan.threewaycalling;
+ tmp->adsi = conf.chan.adsi;
+ tmp->use_smdi = conf.chan.use_smdi;
+ tmp->permhidecallerid = conf.chan.hidecallerid;
+ tmp->callreturn = conf.chan.callreturn;
+ tmp->echocancel = conf.chan.echocancel;
+ tmp->echotraining = conf.chan.echotraining;
+ tmp->pulse = conf.chan.pulse;
+ tmp->echocanbridged = conf.chan.echocanbridged;
+ tmp->busydetect = conf.chan.busydetect;
+ tmp->busycount = conf.chan.busycount;
+ tmp->busy_tonelength = conf.chan.busy_tonelength;
+ tmp->busy_quietlength = conf.chan.busy_quietlength;
+ tmp->callprogress = conf.chan.callprogress;
+ tmp->cancallforward = conf.chan.cancallforward;
+ tmp->dtmfrelax = conf.chan.dtmfrelax;
+ tmp->callwaiting = tmp->permcallwaiting;
+ tmp->hidecallerid = tmp->permhidecallerid;
+ tmp->channel = channel;
+ tmp->stripmsd = conf.chan.stripmsd;
+ tmp->use_callerid = conf.chan.use_callerid;
+ tmp->cid_signalling = conf.chan.cid_signalling;
+ tmp->cid_start = conf.chan.cid_start;
+ tmp->zaptrcallerid = conf.chan.zaptrcallerid;
+ tmp->restrictcid = conf.chan.restrictcid;
+ tmp->use_callingpres = conf.chan.use_callingpres;
+ tmp->priindication_oob = conf.chan.priindication_oob;
+ tmp->priexclusive = conf.chan.priexclusive;
+ if (tmp->usedistinctiveringdetection) {
+ if (!tmp->use_callerid) {
+ ast_log(LOG_NOTICE, "Distinctive Ring detect requires 'usecallerid' be on\n");
+ tmp->use_callerid = 1;
+ }
+ }
+
+ if (tmp->cid_signalling == CID_SIG_SMDI) {
+ if (!tmp->use_smdi) {
+ ast_log(LOG_WARNING, "SMDI callerid requires SMDI to be enabled, enabling...\n");
+ tmp->use_smdi = 1;
+ }
+ }
+ if (tmp->use_smdi) {
+ tmp->smdi_iface = ast_smdi_interface_find(conf.smdi_port);
+ if (!(tmp->smdi_iface)) {
+ ast_log(LOG_ERROR, "Invalid SMDI port specfied, disabling SMDI support\n");
+ tmp->use_smdi = 0;
+ }
+ }
+
+ ast_copy_string(tmp->accountcode, conf.chan.accountcode, sizeof(tmp->accountcode));
+ tmp->amaflags = conf.chan.amaflags;
+ if (!here) {
+ tmp->confno = -1;
+ tmp->propconfno = -1;
+ }
+ tmp->canpark = conf.chan.canpark;
+ tmp->transfer = conf.chan.transfer;
+ ast_copy_string(tmp->defcontext,conf.chan.context,sizeof(tmp->defcontext));
+ ast_copy_string(tmp->language, conf.chan.language, sizeof(tmp->language));
+ ast_copy_string(tmp->mohinterpret, conf.chan.mohinterpret, sizeof(tmp->mohinterpret));
+ ast_copy_string(tmp->mohsuggest, conf.chan.mohsuggest, sizeof(tmp->mohsuggest));
+ ast_copy_string(tmp->context, conf.chan.context, sizeof(tmp->context));
+ ast_copy_string(tmp->cid_num, conf.chan.cid_num, sizeof(tmp->cid_num));
+ tmp->cid_ton = 0;
+ ast_copy_string(tmp->cid_name, conf.chan.cid_name, sizeof(tmp->cid_name));
+ ast_copy_string(tmp->mailbox, conf.chan.mailbox, sizeof(tmp->mailbox));
+ if (!ast_strlen_zero(tmp->mailbox)) {
+ char *mailbox, *context;
+ mailbox = context = ast_strdupa(tmp->mailbox);
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+ tmp->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL,
+ AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+ AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
+ AST_EVENT_IE_END);
+ }
+ tmp->msgstate = -1;
+ tmp->group = conf.chan.group;
+ tmp->callgroup = conf.chan.callgroup;
+ tmp->pickupgroup= conf.chan.pickupgroup;
+ if (conf.chan.vars) {
+ tmp->vars = conf.chan.vars;
+ conf.chan.vars = NULL;
+ }
+ tmp->cid_rxgain = conf.chan.cid_rxgain;
+ tmp->rxgain = conf.chan.rxgain;
+ tmp->txgain = conf.chan.txgain;
+ tmp->tonezone = conf.chan.tonezone;
+ tmp->onhooktime = time(NULL);
+ if (tmp->subs[SUB_REAL].zfd > -1) {
+ set_actual_gain(tmp->subs[SUB_REAL].zfd, 0, tmp->rxgain, tmp->txgain, tmp->law);
+ if (tmp->dsp)
+ ast_dsp_digitmode(tmp->dsp, DSP_DIGITMODE_DTMF | tmp->dtmfrelax);
+ update_conf(tmp);
+ if (!here) {
+ if ((conf.chan.sig != SIG_BRI) && (conf.chan.sig != SIG_BRI_PTMP) && (conf.chan.sig != SIG_PRI) && (conf.chan.sig != SIG_SS7))
+ /* Hang it up to be sure it's good */
+ zt_set_hook(tmp->subs[SUB_REAL].zfd, ZT_ONHOOK);
+ }
+ ioctl(tmp->subs[SUB_REAL].zfd,ZT_SETTONEZONE,&tmp->tonezone);
+#ifdef HAVE_PRI
+ /* the dchannel is down so put the channel in alarm */
+ if (tmp->pri && !pri_is_up(tmp->pri))
+ tmp->inalarm = 1;
+ else
+ tmp->inalarm = 0;
+#endif
+ memset(&si, 0, sizeof(si));
+ if (ioctl(tmp->subs[SUB_REAL].zfd,ZT_SPANSTAT,&si) == -1) {
+ ast_log(LOG_ERROR, "Unable to get span status: %s\n", strerror(errno));
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ if (si.alarms) tmp->inalarm = 1;
+ }
+
+ tmp->polarityonanswerdelay = conf.chan.polarityonanswerdelay;
+ tmp->answeronpolarityswitch = conf.chan.answeronpolarityswitch;
+ tmp->hanguponpolarityswitch = conf.chan.hanguponpolarityswitch;
+ tmp->sendcalleridafter = conf.chan.sendcalleridafter;
+ if (!here) {
+ tmp->locallyblocked = tmp->remotelyblocked = 0;
+ if ((conf.chan.sig == SIG_PRI) || (conf.chan.sig == SIG_BRI) || (conf.chan.sig == SIG_BRI_PTMP) || (conf.chan.sig == SIG_SS7))
+ tmp->inservice = 0;
+ else /* We default to in service on protocols that don't have a reset */
+ tmp->inservice = 1;
+ }
+ }
+ if (tmp && !here) {
+ /* nothing on the iflist */
+ if (!*wlist) {
+ *wlist = tmp;
+ tmp->prev = NULL;
+ tmp->next = NULL;
+ *wend = tmp;
+ } else {
+ /* at least one member on the iflist */
+ struct zt_pvt *working = *wlist;
+
+ /* check if we maybe have to put it on the begining */
+ if (working->channel > tmp->channel) {
+ tmp->next = *wlist;
+ tmp->prev = NULL;
+ (*wlist)->prev = tmp;
+ *wlist = tmp;
+ } else {
+ /* go through all the members and put the member in the right place */
+ while (working) {
+ /* in the middle */
+ if (working->next) {
+ if (working->channel < tmp->channel && working->next->channel > tmp->channel) {
+ tmp->next = working->next;
+ tmp->prev = working;
+ working->next->prev = tmp;
+ working->next = tmp;
+ break;
+ }
+ } else {
+ /* the last */
+ if (working->channel < tmp->channel) {
+ working->next = tmp;
+ tmp->next = NULL;
+ tmp->prev = working;
+ *wend = tmp;
+ break;
+ }
+ }
+ working = working->next;
+ }
+ }
+ }
+ }
+ return tmp;
+}
+
+static inline int available(struct zt_pvt *p, int channelmatch, int groupmatch, int *busy, int *channelmatched, int *groupmatched)
+{
+ int res;
+ ZT_PARAMS par;
+
+ /* First, check group matching */
+ if (groupmatch) {
+ if ((p->group & groupmatch) != groupmatch)
+ return 0;
+ *groupmatched = 1;
+ }
+ /* Check to see if we have a channel match */
+ if (channelmatch != -1) {
+ if (p->channel != channelmatch)
+ return 0;
+ *channelmatched = 1;
+ }
+ /* We're at least busy at this point */
+ if (busy) {
+ if ((p->sig == SIG_FXOKS) || (p->sig == SIG_FXOLS) || (p->sig == SIG_FXOGS))
+ *busy = 1;
+ }
+ /* If do not disturb, definitely not */
+ if (p->dnd)
+ return 0;
+ /* If guard time, definitely not */
+ if (p->guardtime && (time(NULL) < p->guardtime))
+ return 0;
+
+ if (p->locallyblocked || p->remotelyblocked)
+ return 0;
+
+ /* If no owner definitely available */
+ if (!p->owner) {
+#ifdef HAVE_PRI
+ /* Trust PRI */
+ if (p->pri) {
+ if (p->resetting || p->call)
+ return 0;
+ else
+ return 1;
+ }
+#endif
+#ifdef HAVE_SS7
+ /* Trust SS7 */
+ if (p->ss7) {
+ if (p->ss7call)
+ return 0;
+ else
+ return 1;
+ }
+#endif
+ if (!(p->radio || (p->oprmode < 0)))
+ {
+ if (!p->sig || (p->sig == SIG_FXSLS))
+ return 1;
+ /* Check hook state */
+ if (p->subs[SUB_REAL].zfd > -1)
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &par);
+ else {
+ /* Assume not off hook on CVRS */
+ res = 0;
+ par.rxisoffhook = 0;
+ }
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to check hook state on channel %d\n", p->channel);
+ } else if ((p->sig == SIG_FXSKS) || (p->sig == SIG_FXSGS)) {
+ /* When "onhook" that means no battery on the line, and thus
+ it is out of service..., if it's on a TDM card... If it's a channel
+ bank, there is no telling... */
+ if (par.rxbits > -1)
+ return 1;
+ if (par.rxisoffhook)
+ return 1;
+ else
+#ifdef ZAP_CHECK_HOOKSTATE
+ return 0;
+#else
+ return 1;
+#endif
+ } else if (par.rxisoffhook) {
+ ast_debug(1, "Channel %d off hook, can't use\n", p->channel);
+ /* Not available when the other end is off hook */
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ /* If it's not an FXO, forget about call wait */
+ if ((p->sig != SIG_FXOKS) && (p->sig != SIG_FXOLS) && (p->sig != SIG_FXOGS))
+ return 0;
+
+ if (!p->callwaiting) {
+ /* If they don't have call waiting enabled, then for sure they're unavailable at this point */
+ return 0;
+ }
+
+ if (p->subs[SUB_CALLWAIT].zfd > -1) {
+ /* If there is already a call waiting call, then we can't take a second one */
+ return 0;
+ }
+
+ if ((p->owner->_state != AST_STATE_UP) &&
+ ((p->owner->_state != AST_STATE_RINGING) || p->outgoing)) {
+ /* If the current call is not up, then don't allow the call */
+ return 0;
+ }
+ if ((p->subs[SUB_THREEWAY].owner) && (!p->subs[SUB_THREEWAY].inthreeway)) {
+ /* Can't take a call wait when the three way calling hasn't been merged yet. */
+ return 0;
+ }
+ /* We're cool */
+ return 1;
+}
+
+static struct zt_pvt *chandup(struct zt_pvt *src)
+{
+ struct zt_pvt *p;
+ ZT_BUFFERINFO bi;
+ int res;
+
+ if ((p = ast_malloc(sizeof(*p)))) {
+ memcpy(p, src, sizeof(struct zt_pvt));
+ ast_mutex_init(&p->lock);
+ p->subs[SUB_REAL].zfd = zt_open("/dev/zap/pseudo");
+ /* Allocate a zapata structure */
+ if (p->subs[SUB_REAL].zfd < 0) {
+ ast_log(LOG_ERROR, "Unable to dup channel: %s\n", strerror(errno));
+ destroy_zt_pvt(&p);
+ return NULL;
+ }
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_GET_BUFINFO, &bi);
+ if (!res) {
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = numbufs;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_SET_BUFINFO, &bi);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set buffer policy on dup channel\n");
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to check buffer policy on dup channel\n");
+ }
+ p->destroy = 1;
+ p->next = iflist;
+ iflist = p;
+ return p;
+}
+
+
+#ifdef HAVE_PRI
+static int pri_find_empty_chan(struct zt_pri *pri, int backwards)
+{
+ int x;
+ if (backwards)
+ x = pri->numchans;
+ else
+ x = 0;
+ for (;;) {
+ if (backwards && (x < 0))
+ break;
+ if (!backwards && (x >= pri->numchans))
+ break;
+ if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner) {
+ ast_debug(1, "Found empty available channel %d/%d\n",
+ pri->pvts[x]->logicalspan, pri->pvts[x]->prioffset);
+ return x;
+ }
+ if (backwards)
+ x--;
+ else
+ x++;
+ }
+ return -1;
+}
+#endif
+
+static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause)
+{
+ int groupmatch = 0;
+ int channelmatch = -1;
+ int roundrobin = 0;
+ int callwait = 0;
+ int busy = 0;
+ struct zt_pvt *p;
+ struct ast_channel *tmp = NULL;
+ char *dest=NULL;
+ int x;
+ char *s;
+ char opt=0;
+ int res=0, y=0;
+ int backwards = 0;
+#ifdef HAVE_PRI
+ int crv;
+ int bearer = -1;
+ int trunkgroup;
+ struct zt_pri *pri=NULL;
+#endif
+ struct zt_pvt *exit, *start, *end;
+ ast_mutex_t *lock;
+ int channelmatched = 0;
+ int groupmatched = 0;
+
+ /* Assume we're locking the iflock */
+ lock = &iflock;
+ start = iflist;
+ end = ifend;
+ if (data) {
+ dest = ast_strdupa((char *)data);
+ } else {
+ ast_log(LOG_WARNING, "Channel requested with no data\n");
+ return NULL;
+ }
+ if (toupper(dest[0]) == 'G' || toupper(dest[0])=='R') {
+ /* Retrieve the group number */
+ char *stringp=NULL;
+ stringp=dest + 1;
+ s = strsep(&stringp, "/");
+ if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) {
+ ast_log(LOG_WARNING, "Unable to determine group for data %s\n", (char *)data);
+ return NULL;
+ }
+ groupmatch = 1 << x;
+ if (toupper(dest[0]) == 'G') {
+ if (dest[0] == 'G') {
+ backwards = 1;
+ p = ifend;
+ } else
+ p = iflist;
+ } else {
+ if (dest[0] == 'R') {
+ backwards = 1;
+ p = round_robin[x]?round_robin[x]->prev:ifend;
+ if (!p)
+ p = ifend;
+ } else {
+ p = round_robin[x]?round_robin[x]->next:iflist;
+ if (!p)
+ p = iflist;
+ }
+ roundrobin = 1;
+ }
+ } else {
+ char *stringp=NULL;
+ stringp=dest;
+ s = strsep(&stringp, "/");
+ p = iflist;
+ if (!strcasecmp(s, "pseudo")) {
+ /* Special case for pseudo */
+ x = CHAN_PSEUDO;
+ channelmatch = x;
+ }
+#ifdef HAVE_PRI
+ else if ((res = sscanf(s, "%d:%d%c%d", &trunkgroup, &crv, &opt, &y)) > 1) {
+ if ((trunkgroup < 1) || (crv < 1)) {
+ ast_log(LOG_WARNING, "Unable to determine trunk group and CRV for data %s\n", (char *)data);
+ return NULL;
+ }
+ res--;
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (pris[x].trunkgroup == trunkgroup) {
+ pri = pris + x;
+ lock = &pri->lock;
+ start = pri->crvs;
+ end = pri->crvend;
+ break;
+ }
+ }
+ if (!pri) {
+ ast_log(LOG_WARNING, "Unable to find trunk group %d\n", trunkgroup);
+ return NULL;
+ }
+ channelmatch = crv;
+ p = pris[x].crvs;
+ }
+#endif
+ else if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) {
+ ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", (char *)data);
+ return NULL;
+ } else {
+ channelmatch = x;
+ }
+ }
+ /* Search for an unowned channel */
+ ast_mutex_lock(lock);
+ exit = p;
+ while (p && !tmp) {
+ if (roundrobin)
+ round_robin[x] = p;
+#if 0
+ ast_verbose("name = %s, %d, %d, %d\n",p->owner ? p->owner->name : "<none>", p->channel, channelmatch, groupmatch);
+#endif
+
+ if (p && available(p, channelmatch, groupmatch, &busy, &channelmatched, &groupmatched)) {
+ ast_debug(1, "Using channel %d\n", p->channel);
+ if (p->inalarm)
+ goto next;
+
+ callwait = (p->owner != NULL);
+#ifdef HAVE_PRI
+ if (pri && (p->subs[SUB_REAL].zfd < 0)) {
+ if (p->sig != SIG_FXSKS) {
+ /* Gotta find an actual channel to use for this
+ CRV if this isn't a callwait */
+ bearer = pri_find_empty_chan(pri, 0);
+ if (bearer < 0) {
+ ast_log(LOG_NOTICE, "Out of bearer channels on span %d for call to CRV %d:%d\n", pri->span, trunkgroup, crv);
+ p = NULL;
+ break;
+ }
+ pri_assign_bearer(p, pri, pri->pvts[bearer]);
+ } else {
+ if (alloc_sub(p, 0)) {
+ ast_log(LOG_NOTICE, "Failed to allocate place holder pseudo channel!\n");
+ p = NULL;
+ break;
+ } else
+ ast_debug(1, "Allocated placeholder pseudo channel\n");
+
+ p->pri = pri;
+ }
+ }
+#endif
+ if (p->channel == CHAN_PSEUDO) {
+ p = chandup(p);
+ if (!p) {
+ break;
+ }
+ }
+ if (p->owner) {
+ if (alloc_sub(p, SUB_CALLWAIT)) {
+ p = NULL;
+ break;
+ }
+ }
+ p->outgoing = 1;
+ tmp = zt_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0);
+#ifdef HAVE_PRI
+ if (p->bearer) {
+ /* Log owner to bearer channel, too */
+ p->bearer->owner = tmp;
+ }
+#endif
+ /* Make special notes */
+ if (res > 1) {
+ if (opt == 'c') {
+ /* Confirm answer */
+ p->confirmanswer = 1;
+ } else if (opt == 'r') {
+ /* Distinctive ring */
+ if (res < 3)
+ ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", (char *)data);
+ else
+ p->distinctivering = y;
+ } else if (opt == 'd') {
+ /* If this is an ISDN call, make it digital */
+ p->digital = 1;
+ if (tmp)
+ tmp->transfercapability = AST_TRANS_CAP_DIGITAL;
+ } else {
+ ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data);
+ }
+ }
+ /* Note if the call is a call waiting call */
+ if (tmp && callwait)
+ tmp->cdrflags |= AST_CDR_CALLWAIT;
+ break;
+ }
+next:
+ if (backwards) {
+ p = p->prev;
+ if (!p)
+ p = end;
+ } else {
+ p = p->next;
+ if (!p)
+ p = start;
+ }
+ /* stop when you roll to the one that we started from */
+ if (p == exit)
+ break;
+ }
+ ast_mutex_unlock(lock);
+ restart_monitor();
+ if (callwait)
+ *cause = AST_CAUSE_BUSY;
+ else if (!tmp) {
+ if (channelmatched) {
+ if (busy)
+ *cause = AST_CAUSE_BUSY;
+ } else if (groupmatched) {
+ *cause = AST_CAUSE_CONGESTION;
+ }
+ }
+
+ return tmp;
+}
+
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+static int zt_setlaw(int zfd, int law)
+{
+ return ioctl(zfd, ZT_SETLAW, &law);
+}
+#endif
+
+#ifdef HAVE_SS7
+
+static int ss7_find_cic(struct zt_ss7 *linkset, int cic)
+{
+ int i;
+ int winner = -1;
+ for (i = 0; i < linkset->numchans; i++) {
+ if (linkset->pvts[i] && (linkset->pvts[i]->cic == cic)) {
+ winner = i;
+ }
+ }
+ return winner;
+}
+
+static void ss7_handle_cqm(struct zt_ss7 *linkset, int startcic, int endcic)
+{
+ unsigned char status[32];
+ struct zt_pvt *p = NULL;
+ int i, offset;
+
+ for (i = 0; i < linkset->numchans; i++) {
+ if (linkset->pvts[i] && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic))) {
+ p = linkset->pvts[i];
+ offset = p->cic - startcic;
+ status[offset] = 0;
+ if (p->locallyblocked)
+ status[offset] |= (1 << 0) | (1 << 4);
+ if (p->remotelyblocked)
+ status[offset] |= (1 << 1) | (1 << 5);
+ if (p->ss7call) {
+ if (p->outgoing)
+ status[offset] |= (1 << 3);
+ else
+ status[offset] |= (1 << 2);
+ } else
+ status[offset] |= 0x3 << 2;
+ }
+ }
+
+ if (p)
+ isup_cqr(linkset->ss7, startcic, endcic, p->dpc, status);
+ else
+ ast_log(LOG_WARNING, "Could not find any equipped circuits within CQM CICs\n");
+
+}
+
+static inline void ss7_block_cics(struct zt_ss7 *linkset, int startcic, int endcic, unsigned char state[], int block)
+{
+ int i;
+
+ for (i = 0; i < linkset->numchans; i++) {
+ if (linkset->pvts[i] && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic))) {
+ if (state) {
+ if (state[i])
+ linkset->pvts[i]->remotelyblocked = block;
+ } else
+ linkset->pvts[i]->remotelyblocked = block;
+ }
+ }
+}
+
+static void ss7_inservice(struct zt_ss7 *linkset, int startcic, int endcic)
+{
+ int i;
+
+ for (i = 0; i < linkset->numchans; i++) {
+ if (linkset->pvts[i] && (linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic))
+ linkset->pvts[i]->inservice = 1;
+ }
+}
+
+static void ss7_reset_linkset(struct zt_ss7 *linkset)
+{
+ int i, startcic = -1, endcic, dpc;
+
+ if (linkset->numchans <= 0)
+ return;
+
+ startcic = linkset->pvts[0]->cic;
+ /* DB: CIC's DPC fix */
+ dpc = linkset->pvts[0]->dpc;
+
+ for (i = 0; i < linkset->numchans; i++) {
+ if (linkset->pvts[i+1] && linkset->pvts[i+1]->dpc == dpc && ((linkset->pvts[i+1]->cic - linkset->pvts[i]->cic) == 1) && (linkset->pvts[i]->cic - startcic < 31)) {
+ continue;
+ } else {
+ endcic = linkset->pvts[i]->cic;
+ ast_verbose("Resetting CICs %d to %d\n", startcic, endcic);
+ isup_grs(linkset->ss7, startcic, endcic, dpc);
+
+ /* DB: CIC's DPC fix */
+ if (linkset->pvts[i+1]) {
+ startcic = linkset->pvts[i+1]->cic;
+ dpc = linkset->pvts[i+1]->dpc;
+ }
+ }
+ }
+}
+
+static void zt_loopback(struct zt_pvt *p, int enable)
+{
+ if (p->loopedback != enable) {
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_LOOPBACK, &enable)) {
+ ast_log(LOG_WARNING, "Unable to set loopback on channel %d\n", p->channel);
+ return;
+ }
+ p->loopedback = enable;
+ }
+}
+
+static void ss7_start_call(struct zt_pvt *p, struct zt_ss7 *linkset)
+{
+ struct ss7 *ss7 = linkset->ss7;
+ int res;
+ int law = 1;
+ struct ast_channel *c;
+ char tmp[256];
+
+ if (ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1)
+ ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", p->channel, law);
+
+ if (linkset->type == SS7_ITU)
+ law = ZT_LAW_ALAW;
+ else
+ law = ZT_LAW_MULAW;
+
+ res = zt_setlaw(p->subs[SUB_REAL].zfd, law);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to set law on channel %d\n", p->channel);
+
+ p->proceeding = 1;
+ isup_acm(ss7, p->ss7call);
+
+ ast_mutex_unlock(&linkset->lock);
+ c = zt_new(p, AST_STATE_RING, 1, SUB_REAL, law, 0);
+ ast_mutex_lock(&linkset->lock);
+ if (c)
+ ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic);
+ else
+ ast_log(LOG_WARNING, "Unable to start PBX on CIC %d\n", p->cic);
+
+ if (!ast_strlen_zero(p->charge_number)) {
+ pbx_builtin_setvar_helper(c, "SS7_CHARGE_NUMBER", p->charge_number);
+ /* Clear this after we set it */
+ p->charge_number[0] = 0;
+ }
+ if (!ast_strlen_zero(p->gen_add_number)) {
+ pbx_builtin_setvar_helper(c, "SS7_GENERIC_ADDRESS", p->gen_add_number);
+ /* Clear this after we set it */
+ p->gen_add_number[0] = 0;
+ }
+ if (!ast_strlen_zero(p->jip_number)) {
+ pbx_builtin_setvar_helper(c, "SS7_JIP", p->jip_number);
+ /* Clear this after we set it */
+ p->jip_number[0] = 0;
+ }
+ if (!ast_strlen_zero(p->gen_dig_number)) {
+ pbx_builtin_setvar_helper(c, "SS7_GENERIC_DIGITS", p->gen_dig_number);
+ /* Clear this after we set it */
+ p->gen_dig_number[0] = 0;
+ }
+
+ snprintf(tmp, sizeof(tmp), "%d", p->gen_dig_type);
+ pbx_builtin_setvar_helper(c, "SS7_GENERIC_DIGTYPE", tmp);
+ /* Clear this after we set it */
+ p->gen_dig_type = 0;
+
+ snprintf(tmp, sizeof(tmp), "%d", p->gen_dig_scheme);
+ pbx_builtin_setvar_helper(c, "SS7_GENERIC_DIGSCHEME", tmp);
+ /* Clear this after we set it */
+ p->gen_dig_scheme = 0;
+
+ if (!ast_strlen_zero(p->lspi_ident)) {
+ pbx_builtin_setvar_helper(c, "SS7_LSPI_IDENT", p->lspi_ident);
+ /* Clear this after we set it */
+ p->lspi_ident[0] = 0;
+ }
+
+ snprintf(tmp, sizeof(tmp), "%d", p->call_ref_ident);
+ pbx_builtin_setvar_helper(c, "SS7_CALLREF_IDENT", tmp);
+ /* Clear this after we set it */
+ p->call_ref_ident = 0;
+
+ snprintf(tmp, sizeof(tmp), "%d", p->call_ref_pc);
+ pbx_builtin_setvar_helper(c, "SS7_CALLREF_PC", tmp);
+ /* Clear this after we set it */
+ p->call_ref_pc = 0;
+
+}
+
+static void ss7_apply_plan_to_number(char *buf, size_t size, const struct zt_ss7 *ss7, const char *number, const unsigned nai)
+{
+ switch (nai) {
+ case SS7_NAI_INTERNATIONAL:
+ snprintf(buf, size, "%s%s", ss7->internationalprefix, number);
+ break;
+ case SS7_NAI_NATIONAL:
+ snprintf(buf, size, "%s%s", ss7->nationalprefix, number);
+ break;
+ case SS7_NAI_SUBSCRIBER:
+ snprintf(buf, size, "%s%s", ss7->subscriberprefix, number);
+ break;
+ case SS7_NAI_UNKNOWN:
+ snprintf(buf, size, "%s%s", ss7->unknownprefix, number);
+ break;
+ default:
+ snprintf(buf, size, "%s", number);
+ break;
+ }
+}
+static int ss7_pres_scr2cid_pres(char presentation_ind, char screening_ind)
+{
+ return ((presentation_ind & 0x3) << 5) | (screening_ind & 0x3);
+}
+
+static void *ss7_linkset(void *data)
+{
+ int res, i;
+ struct timeval *next = NULL, tv;
+ struct zt_ss7 *linkset = (struct zt_ss7 *) data;
+ struct ss7 *ss7 = linkset->ss7;
+ ss7_event *e = NULL;
+ struct zt_pvt *p;
+ int chanpos;
+ struct pollfd pollers[NUM_DCHANS];
+ int cic;
+ unsigned int dpc;
+ int nextms = 0;
+
+ ss7_start(ss7);
+
+ while(1) {
+ ast_mutex_lock(&linkset->lock);
+ if ((next = ss7_schedule_next(ss7))) {
+ tv = ast_tvnow();
+ tv.tv_sec = next->tv_sec - tv.tv_sec;
+ tv.tv_usec = next->tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec -= 1;
+ }
+ if (tv.tv_sec < 0) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ }
+ nextms = tv.tv_sec * 1000;
+ nextms += tv.tv_usec / 1000;
+ }
+ ast_mutex_unlock(&linkset->lock);
+
+ for (i = 0; i < linkset->numsigchans; i++) {
+ pollers[i].fd = linkset->fds[i];
+ pollers[i].events = POLLIN | POLLOUT | POLLPRI;
+ pollers[i].revents = 0;
+ }
+
+ res = poll(pollers, linkset->numsigchans, nextms);
+ if ((res < 0) && (errno != EINTR)) {
+ ast_log(LOG_ERROR, "poll(%s)\n", strerror(errno));
+ } else if (!res) {
+ ast_mutex_lock(&linkset->lock);
+ ss7_schedule_run(ss7);
+ ast_mutex_unlock(&linkset->lock);
+ continue;
+ }
+
+ ast_mutex_lock(&linkset->lock);
+ for (i = 0; i < linkset->numsigchans; i++) {
+ if (pollers[i].revents & POLLPRI) {
+ int x;
+ if (ioctl(pollers[i].fd, ZT_GETEVENT, &x)) {
+ ast_log(LOG_ERROR, "Error in exception retrieval!\n");
+ }
+ switch (x) {
+ case ZT_EVENT_OVERRUN:
+ ast_debug(1, "Overrun detected!\n");
+ break;
+ case ZT_EVENT_BADFCS:
+ ast_debug(1, "Bad FCS\n");
+ break;
+ case ZT_EVENT_ABORT:
+ ast_debug(1, "HDLC Abort\n");
+ break;
+ case ZT_EVENT_ALARM:
+ ast_log(LOG_ERROR, "Alarm on link!\n");
+ linkset->linkstate[i] |= (LINKSTATE_DOWN | LINKSTATE_INALARM);
+ linkset->linkstate[i] &= ~LINKSTATE_UP;
+ ss7_link_alarm(ss7, pollers[i].fd);
+ break;
+ case ZT_EVENT_NOALARM:
+ ast_log(LOG_ERROR, "Alarm cleared on link\n");
+ linkset->linkstate[i] &= ~(LINKSTATE_INALARM | LINKSTATE_DOWN);
+ linkset->linkstate[i] |= LINKSTATE_STARTING;
+ ss7_link_noalarm(ss7, pollers[i].fd);
+ break;
+ default:
+ ast_log(LOG_ERROR, "Got exception %d!\n", x);
+ break;
+ }
+ }
+
+ if (pollers[i].revents & POLLIN) {
+ ast_mutex_lock(&linkset->lock);
+ res = ss7_read(ss7, pollers[i].fd);
+ ast_mutex_unlock(&linkset->lock);
+ }
+ if (pollers[i].revents & POLLOUT) {
+ ast_mutex_lock(&linkset->lock);
+ res = ss7_write(ss7, pollers[i].fd);
+ ast_mutex_unlock(&linkset->lock);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "Error in write %s", strerror(errno));
+ }
+ }
+ }
+
+#if 0
+ if (res < 0)
+ exit(-1);
+#endif
+
+ while ((e = ss7_check_event(ss7))) {
+ switch (e->e) {
+ case SS7_EVENT_UP:
+ if (linkset->state != LINKSET_STATE_UP) {
+ ast_verbose("--- SS7 Up ---\n");
+ ss7_reset_linkset(linkset);
+ }
+ linkset->state = LINKSET_STATE_UP;
+ break;
+ case SS7_EVENT_DOWN:
+ ast_verbose("--- SS7 Down ---\n");
+ linkset->state = LINKSET_STATE_DOWN;
+ for (i = 0; i < linkset->numchans; i++) {
+ struct zt_pvt *p = linkset->pvts[i];
+ if (p)
+ p->inalarm = 1;
+ }
+ break;
+ case MTP2_LINK_UP:
+ ast_debug(1, "MTP2 link up\n");
+ break;
+ case ISUP_EVENT_CPG:
+ chanpos = ss7_find_cic(linkset, e->cpg.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "CPG on unconfigured CIC %d\n", e->cpg.cic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_mutex_lock(&p->lock);
+ switch (e->cpg.event) {
+ case CPG_EVENT_ALERTING:
+ p->alerting = 1;
+ p->subs[SUB_REAL].needringing = 1;
+ break;
+ case CPG_EVENT_PROGRESS:
+ case CPG_EVENT_INBANDINFO:
+ {
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, };
+ ast_debug(1, "Queuing frame PROGRESS on CIC %d\n", p->cic);
+ zap_queue_frame(p, &f, linkset);
+ p->progress = 1;
+ if (p->dsp && p->dsp_features) {
+ ast_dsp_set_features(p->dsp, p->dsp_features);
+ p->dsp_features = 0;
+ }
+ }
+ break;
+ default:
+ ast_debug(1, "Do not handle CPG with event type 0x%x\n", e->cpg.event);
+ }
+
+ ast_mutex_unlock(&p->lock);
+ break;
+ case ISUP_EVENT_RSC:
+ ast_verbose("Resetting CIC %d\n", e->rsc.cic);
+ chanpos = ss7_find_cic(linkset, e->rsc.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "RSC on unconfigured CIC %d\n", e->rsc.cic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_mutex_lock(&p->lock);
+ p->inservice = 1;
+ p->remotelyblocked = 0;
+ dpc = p->dpc;
+ isup_set_call_dpc(e->rsc.call, dpc);
+ ast_mutex_unlock(&p->lock);
+ isup_rlc(ss7, e->rsc.call);
+ break;
+ case ISUP_EVENT_GRS:
+ ast_debug(1, "Got Reset for CICs %d to %d: Acknowledging\n", e->grs.startcic, e->grs.endcic);
+ chanpos = ss7_find_cic(linkset, e->grs.startcic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "GRS on unconfigured CIC %d\n", e->grs.startcic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ isup_gra(ss7, e->grs.startcic, e->grs.endcic, p->dpc);
+ ss7_block_cics(linkset, e->grs.startcic, e->grs.endcic, NULL, 0);
+ break;
+ case ISUP_EVENT_CQM:
+ ast_debug(1, "Got Circuit group query message from CICs %d to %d\n", e->cqm.startcic, e->cqm.endcic);
+ ss7_handle_cqm(linkset, e->cqm.startcic, e->cqm.endcic);
+ break;
+ case ISUP_EVENT_GRA:
+ ast_verbose("Got reset acknowledgement from CIC %d to %d.\n", e->gra.startcic, e->gra.endcic);
+ ss7_inservice(linkset, e->gra.startcic, e->gra.endcic);
+ ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.status, 1);
+ break;
+ case ISUP_EVENT_IAM:
+ ast_debug(1, "Got IAM for CIC %d and called number %s, calling number %s\n", e->iam.cic, e->iam.called_party_num, e->iam.calling_party_num);
+ chanpos = ss7_find_cic(linkset, e->iam.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "IAM on unconfigured CIC %d\n", e->iam.cic);
+ isup_rel(ss7, e->iam.call, -1);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_mutex_lock(&p->lock);
+ if (p->owner) {
+ if (p->ss7call == e->iam.call) {
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_WARNING, "Duplicate IAM requested on CIC %d\n", e->iam.cic);
+ break;
+ } else {
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_WARNING, "Ring requested on CIC %d already in use!\n", e->iam.cic);
+ break;
+ }
+ }
+
+ dpc = p->dpc;
+ p->ss7call = e->iam.call;
+ isup_set_call_dpc(p->ss7call, dpc);
+
+ if ( (p->use_callerid) && (!ast_strlen_zero(e->iam.calling_party_num)) )
+ {
+ ss7_apply_plan_to_number(p->cid_num, sizeof(p->cid_num), linkset, e->iam.calling_party_num, e->iam.calling_nai);
+ p->callingpres = ss7_pres_scr2cid_pres(e->iam.presentation_ind, e->iam.screening_ind);
+ }
+ else
+ p->cid_num[0] = 0;
+
+ if (p->immediate) {
+ p->exten[0] = 's';
+ p->exten[1] = '\0';
+ } else if (!ast_strlen_zero(e->iam.called_party_num)) {
+ char *st;
+ ss7_apply_plan_to_number(p->exten, sizeof(p->exten), linkset, e->iam.called_party_num, e->iam.called_nai);
+ st = strchr(p->exten, '#');
+ if (st)
+ *st = '\0';
+ } else
+ p->exten[0] = '\0';
+
+ p->cid_ani[0] = '\0';
+ p->cid_name[0] = '\0';
+ p->cid_ani2 = e->iam.oli_ani2;
+ p->cid_ton = 0;
+ ast_copy_string(p->charge_number, e->iam.charge_number, sizeof(p->charge_number));
+ ast_copy_string(p->gen_add_number, e->iam.gen_add_number, sizeof(p->gen_add_number));
+ p->gen_add_type = e->iam.gen_add_type;
+ p->gen_add_nai = e->iam.gen_add_nai;
+ p->gen_add_pres_ind = e->iam.gen_add_pres_ind;
+ p->gen_add_num_plan = e->iam.gen_add_num_plan;
+ ast_copy_string(p->gen_dig_number, e->iam.gen_dig_number, sizeof(p->gen_dig_number));
+ p->gen_dig_type = e->iam.gen_dig_type;
+ p->gen_dig_scheme = e->iam.gen_dig_scheme;
+ ast_copy_string(p->jip_number, e->iam.jip_number, sizeof(p->jip_number));
+
+ /* Set DNID */
+ if (!ast_strlen_zero(e->iam.called_party_num))
+ ss7_apply_plan_to_number(p->dnid, sizeof(p->dnid), linkset, e->iam.called_party_num, e->iam.called_nai);
+
+ if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+
+ if (e->iam.cot_check_required) {
+ zt_loopback(p, 1);
+ } else
+ ss7_start_call(p, linkset);
+ } else {
+ ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten);
+ isup_rel(ss7, e->iam.call, -1);
+ }
+ ast_mutex_unlock(&p->lock);
+ break;
+ case ISUP_EVENT_COT:
+ chanpos = ss7_find_cic(linkset, e->cot.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "COT on unconfigured CIC %d\n", e->cot.cic);
+ isup_rel(ss7, e->cot.call, -1);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+
+ zt_loopback(p, 0);
+
+ ss7_start_call(p, linkset);
+ break;
+ case ISUP_EVENT_CCR:
+ ast_debug(1, "Got CCR request on CIC %d\n", e->ccr.cic);
+ chanpos = ss7_find_cic(linkset, e->ccr.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "CCR on unconfigured CIC %d\n", e->ccr.cic);
+ break;
+ }
+
+ p = linkset->pvts[chanpos];
+
+ ast_mutex_lock(&p->lock);
+ zt_loopback(p, 1);
+ ast_mutex_unlock(&p->lock);
+
+ isup_lpa(linkset->ss7, e->ccr.cic, p->dpc);
+ break;
+ case ISUP_EVENT_REL:
+ chanpos = ss7_find_cic(linkset, e->rel.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "REL on unconfigured CIC %d\n", e->rel.cic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_mutex_lock(&p->lock);
+ if (p->owner) {
+ p->owner->hangupcause = e->rel.cause;
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ } else
+ ast_log(LOG_WARNING, "REL on channel (CIC %d) without owner!\n", p->cic);
+
+ /* End the loopback if we have one */
+ zt_loopback(p, 0);
+
+ isup_rlc(ss7, e->rel.call);
+ p->ss7call = NULL;
+
+ ast_mutex_unlock(&p->lock);
+ break;
+ case ISUP_EVENT_ACM:
+ chanpos = ss7_find_cic(linkset, e->acm.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "ACM on unconfigured CIC %d\n", e->acm.cic);
+ isup_rel(ss7, e->acm.call, -1);
+ break;
+ } else {
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROCEEDING, };
+
+ p = linkset->pvts[chanpos];
+
+ ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic);
+
+ if (e->acm.call_ref_ident > 0) {
+ p->rlt = 1; /* Setting it but not using it here*/
+ }
+
+ ast_mutex_lock(&p->lock);
+ zap_queue_frame(p, &f, linkset);
+ p->proceeding = 1;
+
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ case ISUP_EVENT_CGB:
+ chanpos = ss7_find_cic(linkset, e->cgb.startcic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "CGB on unconfigured CIC %d\n", e->cgb.startcic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic, e->cgb.status, 1);
+ isup_cgba(linkset->ss7, e->cgb.startcic, e->cgb.endcic, p->dpc, e->cgb.status, e->cgb.type);
+ break;
+ case ISUP_EVENT_CGU:
+ chanpos = ss7_find_cic(linkset, e->cgu.startcic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "CGU on unconfigured CIC %d\n", e->cgu.startcic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic, e->cgu.status, 0);
+ isup_cgua(linkset->ss7, e->cgu.startcic, e->cgu.endcic, p->dpc, e->cgu.status, e->cgu.type);
+ break;
+ case ISUP_EVENT_UCIC:
+ chanpos = ss7_find_cic(linkset, e->ucic.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "UCIC on unconfigured CIC %d\n", e->ucic.cic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_debug(1, "Unequiped Circuit Id Code on CIC %d\n", e->ucic.cic);
+ ast_mutex_lock(&p->lock);
+ p->remotelyblocked = 1;
+ p->inservice = 0;
+ ast_mutex_unlock(&p->lock); //doesn't require a SS7 acknowledgement
+ break;
+ case ISUP_EVENT_BLO:
+ chanpos = ss7_find_cic(linkset, e->blo.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "BLO on unconfigured CIC %d\n", e->blo.cic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_debug(1, "Blocking CIC %d\n", e->blo.cic);
+ ast_mutex_lock(&p->lock);
+ p->remotelyblocked = 1;
+ ast_mutex_unlock(&p->lock);
+ isup_bla(linkset->ss7, e->blo.cic, p->dpc);
+ break;
+ case ISUP_EVENT_BLA:
+ chanpos = ss7_find_cic(linkset, e->bla.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "BLA on unconfigured CIC %d\n", e->bla.cic);
+ break;
+ }
+ ast_debug(1, "Blocking CIC %d\n", e->bla.cic);
+ p = linkset->pvts[chanpos];
+ ast_mutex_lock(&p->lock);
+ p->locallyblocked = 1;
+ ast_mutex_unlock(&p->lock);
+ break;
+ case ISUP_EVENT_UBL:
+ chanpos = ss7_find_cic(linkset, e->ubl.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "UBL on unconfigured CIC %d\n", e->ubl.cic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_debug(1, "Unblocking CIC %d\n", e->ubl.cic);
+ ast_mutex_lock(&p->lock);
+ p->remotelyblocked = 0;
+ ast_mutex_unlock(&p->lock);
+ isup_uba(linkset->ss7, e->ubl.cic, p->dpc);
+ break;
+ case ISUP_EVENT_UBA:
+ chanpos = ss7_find_cic(linkset, e->uba.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "UBA on unconfigured CIC %d\n", e->uba.cic);
+ break;
+ }
+ p = linkset->pvts[chanpos];
+ ast_debug(1, "Unblocking CIC %d\n", e->uba.cic);
+ ast_mutex_lock(&p->lock);
+ p->locallyblocked = 0;
+ ast_mutex_unlock(&p->lock);
+ break;
+ case ISUP_EVENT_CON:
+ case ISUP_EVENT_ANM:
+ if (e->e == ISUP_EVENT_CON)
+ cic = e->con.cic;
+ else
+ cic = e->anm.cic;
+
+ chanpos = ss7_find_cic(linkset, cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "ANM/CON on unconfigured CIC %d\n", cic);
+ isup_rel(ss7, (e->e == ISUP_EVENT_ANM) ? e->anm.call : e->con.call, -1);
+ break;
+ } else {
+ p = linkset->pvts[chanpos];
+ ast_mutex_lock(&p->lock);
+ p->subs[SUB_REAL].needanswer = 1;
+ if (p->dsp && p->dsp_features) {
+ ast_dsp_set_features(p->dsp, p->dsp_features);
+ p->dsp_features = 0;
+ }
+ zt_enable_ec(p);
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ case ISUP_EVENT_RLC:
+ chanpos = ss7_find_cic(linkset, e->rlc.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "RLC on unconfigured CIC %d\n", e->rlc.cic);
+ break;
+ } else {
+ p = linkset->pvts[chanpos];
+ ast_mutex_lock(&p->lock);
+ if (p->alreadyhungup)
+ p->ss7call = NULL;
+ else
+ ast_log(LOG_NOTICE, "Received RLC out and we haven't sent REL. Ignoring.\n");
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ case ISUP_EVENT_FAA:
+ chanpos = ss7_find_cic(linkset, e->faa.cic);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "FAA on unconfigured CIC %d\n", e->faa.cic);
+ break;
+ } else {
+ p = linkset->pvts[chanpos];
+ ast_debug(1, "FAA received on CIC %d\n", e->faa.cic);
+ ast_mutex_lock(&p->lock);
+ if (p->alreadyhungup){
+ p->ss7call = NULL;
+ ast_log(LOG_NOTICE, "Received FAA and we haven't sent FAR. Ignoring.\n");
+ }
+ ast_mutex_unlock(&p->lock);
+ }
+ break;
+ default:
+ ast_debug(1, "Unknown event %s\n", ss7_event2str(e->e));
+ break;
+ }
+ }
+ ast_mutex_unlock(&linkset->lock);
+ }
+
+ return 0;
+}
+
+static void zt_ss7_message(struct ss7 *ss7, char *s)
+{
+#if 0
+ int i;
+
+ for (i = 0; i < NUM_SPANS; i++)
+ if (linksets[i].ss7 == ss7)
+ break;
+
+ ast_verbose("[%d] %s", i+1, s);
+#else
+ ast_verbose("%s", s);
+#endif
+}
+
+static void zt_ss7_error(struct ss7 *ss7, char *s)
+{
+#if 0
+ int i;
+
+ for (i = 0; i < NUM_SPANS; i++)
+ if (linksets[i].ss7 == ss7)
+ break;
+
+#else
+ ast_log(LOG_ERROR, "%s", s);
+#endif
+}
+
+#endif /* HAVE_SS7 */
+
+#ifdef HAVE_PRI
+static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv)
+{
+ struct zt_pvt *p;
+ p = pri->crvs;
+ while (p) {
+ if (p->channel == crv)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+
+static int pri_find_principle(struct zt_pri *pri, int channel)
+{
+ int x;
+ int span = PRI_SPAN(channel);
+ int spanfd;
+ ZT_PARAMS param;
+ int principle = -1;
+ int explicit = PRI_EXPLICIT(channel);
+ channel = PRI_CHANNEL(channel);
+
+ if (!explicit) {
+ spanfd = pri_active_dchan_fd(pri);
+ if (ioctl(spanfd, ZT_GET_PARAMS, &param))
+ return -1;
+ span = pris[param.spanno - 1].prilogicalspan;
+ }
+
+ for (x = 0; x < pri->numchans; x++) {
+ if (pri->pvts[x] && (pri->pvts[x]->prioffset == channel) && (pri->pvts[x]->logicalspan == span)) {
+ principle = x;
+ break;
+ }
+ }
+
+ return principle;
+}
+
+static int pri_fixup_principle(struct zt_pri *pri, int principle, q931_call *c)
+{
+ int x;
+ struct zt_pvt *crv;
+ if (!c) {
+ if (principle < 0)
+ return -1;
+ return principle;
+ }
+ if ((principle > -1) &&
+ (principle < pri->numchans) &&
+ (pri->pvts[principle]) &&
+ (pri->pvts[principle]->call == c))
+ return principle;
+ /* First, check for other bearers */
+ for (x = 0; x < pri->numchans; x++) {
+ if (!pri->pvts[x])
+ continue;
+ if (pri->pvts[x]->call == c) {
+ /* Found our call */
+ if (principle != x) {
+ ast_verb(3, "Moving call from channel %d to channel %d\n",
+ pri->pvts[x]->channel, pri->pvts[principle]->channel);
+ if (pri->pvts[principle]->owner) {
+ ast_log(LOG_WARNING, "Can't fix up channel from %d to %d because %d is already in use\n",
+ pri->pvts[x]->channel, pri->pvts[principle]->channel, pri->pvts[principle]->channel);
+ return -1;
+ }
+ /* Fix it all up now */
+ pri->pvts[principle]->owner = pri->pvts[x]->owner;
+ if (pri->pvts[principle]->owner) {
+ ast_string_field_build(pri->pvts[principle]->owner, name,
+ "Zap/%d:%d-%d", pri->trunkgroup,
+ pri->pvts[principle]->channel, 1);
+ pri->pvts[principle]->owner->tech_pvt = pri->pvts[principle];
+ ast_channel_set_fd(pri->pvts[principle]->owner, 0, pri->pvts[principle]->subs[SUB_REAL].zfd);
+ pri->pvts[principle]->subs[SUB_REAL].owner = pri->pvts[x]->subs[SUB_REAL].owner;
+ } else
+ ast_log(LOG_WARNING, "Whoa, there's no owner, and we're having to fix up channel %d to channel %d\n", pri->pvts[x]->channel, pri->pvts[principle]->channel);
+ pri->pvts[principle]->call = pri->pvts[x]->call;
+ /* Free up the old channel, now not in use */
+ pri->pvts[x]->subs[SUB_REAL].owner = NULL;
+ pri->pvts[x]->owner = NULL;
+ pri->pvts[x]->call = NULL;
+ }
+ return principle;
+ }
+ }
+ /* Now check for a CRV with no bearer */
+ crv = pri->crvs;
+ while (crv) {
+ if (crv->call == c) {
+ /* This is our match... Perform some basic checks */
+ if (crv->bearer)
+ ast_log(LOG_WARNING, "Trying to fix up call which already has a bearer which isn't the one we think it is\n");
+ else if (pri->pvts[principle]->owner)
+ ast_log(LOG_WARNING, "Tring to fix up a call to a bearer which already has an owner!\n");
+ else {
+ /* Looks good. Drop the pseudo channel now, clear up the assignment, and
+ wakeup the potential sleeper */
+ zt_close(crv->subs[SUB_REAL].zfd);
+ pri->pvts[principle]->call = crv->call;
+ pri_assign_bearer(crv, pri, pri->pvts[principle]);
+ ast_debug(1, "Assigning bearer %d/%d to CRV %d:%d\n",
+ pri->pvts[principle]->logicalspan, pri->pvts[principle]->prioffset,
+ pri->trunkgroup, crv->channel);
+ wakeup_sub(crv, SUB_REAL, pri);
+ }
+ return principle;
+ }
+ crv = crv->next;
+ }
+ ast_log(LOG_WARNING, "Call specified, but not found?\n");
+ return -1;
+}
+
+static void *do_idle_thread(void *vchan)
+{
+ struct ast_channel *chan = vchan;
+ struct zt_pvt *pvt = chan->tech_pvt;
+ struct ast_frame *f;
+ char ex[80];
+ /* Wait up to 30 seconds for an answer */
+ int newms, ms = 30000;
+ ast_verb(3, "Initiating idle call on channel %s\n", chan->name);
+ snprintf(ex, sizeof(ex), "%d/%s", pvt->channel, pvt->pri->idledial);
+ if (ast_call(chan, ex, 0)) {
+ ast_log(LOG_WARNING, "Idle dial failed on '%s' to '%s'\n", chan->name, ex);
+ ast_hangup(chan);
+ return NULL;
+ }
+ while ((newms = ast_waitfor(chan, ms)) > 0) {
+ f = ast_read(chan);
+ if (!f) {
+ /* Got hangup */
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch (f->subclass) {
+ case AST_CONTROL_ANSWER:
+ /* Launch the PBX */
+ ast_copy_string(chan->exten, pvt->pri->idleext, sizeof(chan->exten));
+ ast_copy_string(chan->context, pvt->pri->idlecontext, sizeof(chan->context));
+ chan->priority = 1;
+ ast_verb(4, "Idle channel '%s' answered, sending to %s@%s\n", chan->name, chan->exten, chan->context);
+ ast_pbx_run(chan);
+ /* It's already hungup, return immediately */
+ return NULL;
+ case AST_CONTROL_BUSY:
+ ast_verb(4, "Idle channel '%s' busy, waiting...\n", chan->name);
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(4, "Idle channel '%s' congested, waiting...\n", chan->name);
+ break;
+ };
+ }
+ ast_frfree(f);
+ ms = newms;
+ }
+ /* Hangup the channel since nothing happend */
+ ast_hangup(chan);
+ return NULL;
+}
+
+#ifndef PRI_RESTART
+#error "Upgrade your libpri"
+#endif
+static void zt_pri_message(struct pri *pri, char *s)
+{
+ int x, y;
+ int dchan = -1, span = -1;
+ int dchancount = 0;
+
+ if (pri) {
+ for (x = 0; x < NUM_SPANS; x++) {
+ for (y = 0; y < NUM_DCHANS; y++) {
+ if (pris[x].dchans[y])
+ dchancount++;
+
+ if (pris[x].dchans[y] == pri)
+ dchan = y;
+ }
+ if (dchan >= 0) {
+ span = x;
+ break;
+ }
+ dchancount = 0;
+ }
+ if (dchancount > 1 && (span > -1))
+ ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s);
+ else
+ ast_verbose("%s", s);
+ } else
+ ast_verbose("%s", s);
+
+ ast_mutex_lock(&pridebugfdlock);
+
+ if (pridebugfd >= 0)
+ write(pridebugfd, s, strlen(s));
+
+ ast_mutex_unlock(&pridebugfdlock);
+}
+
+static void zt_pri_error(struct pri *pri, char *s)
+{
+ int x, y;
+ int dchan = -1, span = -1;
+ int dchancount = 0;
+
+ if (pri) {
+ for (x = 0; x < NUM_SPANS; x++) {
+ for (y = 0; y < NUM_DCHANS; y++) {
+ if (pris[x].dchans[y])
+ dchancount++;
+
+ if (pris[x].dchans[y] == pri)
+ dchan = y;
+ }
+ if (dchan >= 0) {
+ span = x;
+ break;
+ }
+ dchancount = 0;
+ }
+ if ((dchancount > 1) && (span > -1))
+ ast_log(LOG_ERROR, "[Span %d D-Channel %d] PRI: %s", span, dchan, s);
+ else
+ ast_log(LOG_ERROR, "%s", s);
+ } else
+ ast_log(LOG_ERROR, "%s", s);
+
+ ast_mutex_lock(&pridebugfdlock);
+
+ if (pridebugfd >= 0)
+ write(pridebugfd, s, strlen(s));
+
+ ast_mutex_unlock(&pridebugfdlock);
+}
+
+static int pri_check_restart(struct zt_pri *pri)
+{
+ do {
+ pri->resetpos++;
+ } while ((pri->resetpos < pri->numchans) &&
+ (!pri->pvts[pri->resetpos] ||
+ pri->pvts[pri->resetpos]->call ||
+ pri->pvts[pri->resetpos]->resetting));
+ if (pri->resetpos < pri->numchans) {
+ /* Mark the channel as resetting and restart it */
+ pri->pvts[pri->resetpos]->resetting = 1;
+ pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[pri->resetpos]));
+ } else {
+ pri->resetting = 0;
+ time(&pri->lastreset);
+ }
+ return 0;
+}
+
+static int pri_hangup_all(struct zt_pvt *p, struct zt_pri *pri)
+{
+ int x;
+ int redo;
+ ast_mutex_unlock(&pri->lock);
+ ast_mutex_lock(&p->lock);
+ do {
+ redo = 0;
+ for (x = 0; x < 3; x++) {
+ while (p->subs[x].owner && ast_channel_trylock(p->subs[x].owner)) {
+ redo++;
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ }
+ if (p->subs[x].owner) {
+ ast_queue_hangup(p->subs[x].owner);
+ ast_channel_unlock(p->subs[x].owner);
+ }
+ }
+ } while (redo);
+ ast_mutex_unlock(&p->lock);
+ ast_mutex_lock(&pri->lock);
+ return 0;
+}
+
+static char * redirectingreason2str(int redirectingreason)
+{
+ switch (redirectingreason) {
+ case 0:
+ return "UNKNOWN";
+ case 1:
+ return "BUSY";
+ case 2:
+ return "NO_REPLY";
+ case 0xF:
+ return "UNCONDITIONAL";
+ default:
+ return "NOREDIRECT";
+ }
+}
+
+static void apply_plan_to_number(char *buf, size_t size, const struct zt_pri *pri, const char *number, const int plan)
+{
+ if (pri->dialplan == -2) { /* autodetect the TON but leave the number untouched */
+ snprintf(buf, size, "%s", number);
+ return;
+ }
+ switch (plan) {
+ case PRI_INTERNATIONAL_ISDN: /* Q.931 dialplan == 0x11 international dialplan => prepend international prefix digits */
+ snprintf(buf, size, "%s%s", pri->internationalprefix, number);
+ break;
+ case PRI_NATIONAL_ISDN: /* Q.931 dialplan == 0x21 national dialplan => prepend national prefix digits */
+ snprintf(buf, size, "%s%s", pri->nationalprefix, number);
+ break;
+ case PRI_LOCAL_ISDN: /* Q.931 dialplan == 0x41 local dialplan => prepend local prefix digits */
+ snprintf(buf, size, "%s%s", pri->localprefix, number);
+ break;
+ case PRI_PRIVATE: /* Q.931 dialplan == 0x49 private dialplan => prepend private prefix digits */
+ snprintf(buf, size, "%s%s", pri->privateprefix, number);
+ break;
+ case PRI_UNKNOWN: /* Q.931 dialplan == 0x00 unknown dialplan => prepend unknown prefix digits */
+ snprintf(buf, size, "%s%s", pri->unknownprefix, number);
+ break;
+ default: /* other Q.931 dialplan => don't twiddle with callingnum */
+ snprintf(buf, size, "%s", number);
+ break;
+ }
+}
+
+
+static void *pri_dchannel(void *vpri)
+{
+ struct zt_pri *pri = vpri;
+ pri_event *e;
+ struct pollfd fds[NUM_DCHANS];
+ int res;
+ int chanpos = 0;
+ int x;
+ int haveidles;
+ int activeidles;
+ int nextidle = -1;
+ struct ast_channel *c;
+ struct timeval tv, lowest, *next;
+ struct timeval lastidle = ast_tvnow();
+ int doidling=0;
+ char *cc;
+ char idlen[80];
+ struct ast_channel *idle;
+ pthread_t p;
+ time_t t;
+ int i, which=-1;
+ int numdchans;
+ int cause=0;
+ struct zt_pvt *crv;
+ pthread_t threadid;
+ char ani2str[6];
+ char plancallingnum[256];
+ char plancallingani[256];
+ char calledtonstr[10];
+
+ if (!ast_strlen_zero(pri->idledial) && !ast_strlen_zero(pri->idleext)) {
+ /* Need to do idle dialing, check to be sure though */
+ cc = strchr(pri->idleext, '@');
+ if (cc) {
+ *cc = '\0';
+ cc++;
+ ast_copy_string(pri->idlecontext, cc, sizeof(pri->idlecontext));
+#if 0
+ /* Extensions may not be loaded yet */
+ if (!ast_exists_extension(NULL, pri->idlecontext, pri->idleext, 1, NULL))
+ ast_log(LOG_WARNING, "Extension '%s @ %s' does not exist\n", pri->idleext, pri->idlecontext);
+ else
+#endif
+ doidling = 1;
+ } else
+ ast_log(LOG_WARNING, "Idle dial string '%s' lacks '@context'\n", pri->idleext);
+ }
+ for (;;) {
+ for (i = 0; i < NUM_DCHANS; i++) {
+ if (!pri->dchannels[i])
+ break;
+ fds[i].fd = pri->fds[i];
+ fds[i].events = POLLIN | POLLPRI;
+ fds[i].revents = 0;
+ }
+ numdchans = i;
+ time(&t);
+ ast_mutex_lock(&pri->lock);
+ if (pri->switchtype != PRI_SWITCH_GR303_TMC && (pri->resetinterval > 0)) {
+ if (pri->resetting && pri_is_up(pri)) {
+ if (pri->resetpos < 0)
+ pri_check_restart(pri);
+ } else {
+ if (!pri->resetting && (t - pri->lastreset) >= pri->resetinterval) {
+ pri->resetting = 1;
+ pri->resetpos = -1;
+ }
+ }
+ }
+ /* Look for any idle channels if appropriate */
+ if (doidling && pri_is_up(pri)) {
+ nextidle = -1;
+ haveidles = 0;
+ activeidles = 0;
+ for (x = pri->numchans; x >= 0; x--) {
+ if (pri->pvts[x] && !pri->pvts[x]->owner &&
+ !pri->pvts[x]->call) {
+ if (haveidles < pri->minunused) {
+ haveidles++;
+ } else if (!pri->pvts[x]->resetting) {
+ nextidle = x;
+ break;
+ }
+ } else if (pri->pvts[x] && pri->pvts[x]->owner && pri->pvts[x]->isidlecall)
+ activeidles++;
+ }
+ if (nextidle > -1) {
+ if (ast_tvdiff_ms(ast_tvnow(), lastidle) > 1000) {
+ /* Don't create a new idle call more than once per second */
+ snprintf(idlen, sizeof(idlen), "%d/%s", pri->pvts[nextidle]->channel, pri->idledial);
+ idle = zt_request("Zap", AST_FORMAT_ULAW, idlen, &cause);
+ if (idle) {
+ pri->pvts[nextidle]->isidlecall = 1;
+ if (ast_pthread_create_background(&p, NULL, do_idle_thread, idle)) {
+ ast_log(LOG_WARNING, "Unable to start new thread for idle channel '%s'\n", idle->name);
+ zt_hangup(idle);
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to request channel 'Zap/%s' for idle call\n", idlen);
+ lastidle = ast_tvnow();
+ }
+ } else if ((haveidles < pri->minunused) &&
+ (activeidles > pri->minidle)) {
+ /* Mark something for hangup if there is something
+ that can be hungup */
+ for (x = pri->numchans; x >= 0; x--) {
+ /* find a candidate channel */
+ if (pri->pvts[x] && pri->pvts[x]->owner && pri->pvts[x]->isidlecall) {
+ pri->pvts[x]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ haveidles++;
+ /* Stop if we have enough idle channels or
+ can't spare any more active idle ones */
+ if ((haveidles >= pri->minunused) ||
+ (activeidles <= pri->minidle))
+ break;
+ }
+ }
+ }
+ }
+ /* Start with reasonable max */
+ lowest = ast_tv(60, 0);
+ for (i = 0; i < NUM_DCHANS; i++) {
+ /* Find lowest available d-channel */
+ if (!pri->dchannels[i])
+ break;
+ if ((next = pri_schedule_next(pri->dchans[i]))) {
+ /* We need relative time here */
+ tv = ast_tvsub(*next, ast_tvnow());
+ if (tv.tv_sec < 0) {
+ tv = ast_tv(0,0);
+ }
+ if (doidling || pri->resetting) {
+ if (tv.tv_sec > 1) {
+ tv = ast_tv(1, 0);
+ }
+ } else {
+ if (tv.tv_sec > 60) {
+ tv = ast_tv(60, 0);
+ }
+ }
+ } else if (doidling || pri->resetting) {
+ /* Make sure we stop at least once per second if we're
+ monitoring idle channels */
+ tv = ast_tv(1,0);
+ } else {
+ /* Don't poll for more than 60 seconds */
+ tv = ast_tv(60, 0);
+ }
+ if (!i || ast_tvcmp(tv, lowest) < 0) {
+ lowest = tv;
+ }
+ }
+ ast_mutex_unlock(&pri->lock);
+
+ e = NULL;
+ res = poll(fds, numdchans, lowest.tv_sec * 1000 + lowest.tv_usec / 1000);
+
+ ast_mutex_lock(&pri->lock);
+ if (!res) {
+ for (which = 0; which < NUM_DCHANS; which++) {
+ if (!pri->dchans[which])
+ break;
+ /* Just a timeout, run the scheduler */
+ e = pri_schedule_run(pri->dchans[which]);
+ if (e)
+ break;
+ }
+ } else if (res > -1) {
+ for (which = 0; which < NUM_DCHANS; which++) {
+ if (!pri->dchans[which])
+ break;
+ if (fds[which].revents & POLLPRI) {
+ /* Check for an event */
+ x = 0;
+ res = ioctl(pri->fds[which], ZT_GETEVENT, &x);
+ if (x) {
+ ast_log(LOG_NOTICE, "PRI got event: %s (%d) on %s D-channel of span %d\n", event2str(x), x, pri_order(which), pri->span);
+ manager_event(EVENT_FLAG_SYSTEM, "PRIEvent",
+ "PRIEvent: %s\r\n"
+ "PRIEventCode: %d\r\n"
+ "D-channel: %s\r\n"
+ "Span: %d\r\n",
+ event2str(x),
+ x,
+ pri_order(which),
+ pri->span
+ );
+ }
+ /* Keep track of alarm state */
+ if (x == ZT_EVENT_ALARM) {
+ pri->dchanavail[which] &= ~(DCHAN_NOTINALARM | DCHAN_UP);
+ pri_find_dchan(pri);
+ } else if (x == ZT_EVENT_NOALARM) {
+ pri->dchanavail[which] |= DCHAN_NOTINALARM;
+ pri_restart(pri->dchans[which]);
+ }
+
+ ast_debug(1, "Got event %s (%d) on D-channel for span %d\n", event2str(x), x, pri->span);
+ } else if (fds[which].revents & POLLIN) {
+ e = pri_check_event(pri->dchans[which]);
+ }
+ if (e)
+ break;
+ }
+ } else if (errno != EINTR)
+ ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno));
+
+ if (e) {
+ if (pri->debug)
+ pri_dump_event(pri->dchans[which], e);
+ if (e->e != PRI_EVENT_DCHAN_DOWN)
+ pri->dchanavail[which] |= DCHAN_UP;
+
+ if ((e->e != PRI_EVENT_DCHAN_UP) && (e->e != PRI_EVENT_DCHAN_DOWN) && (pri->pri != pri->dchans[which]))
+ /* Must be an NFAS group that has the secondary dchan active */
+ pri->pri = pri->dchans[which];
+
+ switch (e->e) {
+ case PRI_EVENT_DCHAN_UP:
+ ast_verb(2, "%s D-Channel on span %d up\n", pri_order(which), pri->span);
+ pri->dchanavail[which] |= DCHAN_UP;
+ if (!pri->pri) pri_find_dchan(pri);
+
+ /* Note presense of D-channel */
+ time(&pri->lastreset);
+
+ /* Restart in 5 seconds */
+ if (pri->resetinterval > -1) {
+ pri->lastreset -= pri->resetinterval;
+ pri->lastreset += 5;
+ }
+ pri->resetting = 0;
+ /* Take the channels from inalarm condition */
+ for (i = 0; i < pri->numchans; i++)
+ if (pri->pvts[i]) {
+ pri->pvts[i]->inalarm = 0;
+ }
+ break;
+ case PRI_EVENT_DCHAN_DOWN:
+ ast_verb(2, "%s D-Channel on span %d down\n", pri_order(which), pri->span);
+ pri->dchanavail[which] &= ~DCHAN_UP;
+ pri_find_dchan(pri);
+ if (!pri_is_up(pri)) {
+ pri->resetting = 0;
+ /* Hangup active channels and put them in alarm mode */
+ for (i = 0; i < pri->numchans; i++) {
+ struct zt_pvt *p = pri->pvts[i];
+ if (p) {
+ if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) {
+ /* T309 is not enabled : hangup calls when alarm occurs */
+ if (p->call) {
+ if (p->pri && p->pri->pri) {
+ pri_hangup(p->pri->pri, p->call, -1);
+ pri_destroycall(p->pri->pri, p->call);
+ p->call = NULL;
+ } else
+ ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n");
+ }
+ if (p->realcall) {
+ pri_hangup_all(p->realcall, pri);
+ } else if (p->owner)
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+ p->inalarm = 1;
+ }
+ }
+ }
+ break;
+ case PRI_EVENT_RESTART:
+ if (e->restart.channel > -1) {
+ chanpos = pri_find_principle(pri, e->restart.channel);
+ if (chanpos < 0)
+ ast_log(LOG_WARNING, "Restart requested on odd/unavailable channel number %d/%d on span %d\n",
+ PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
+ else {
+ ast_verb(3, "B-channel %d/%d restarted on span %d\n",
+ PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (pri->pvts[chanpos]->call) {
+ pri_destroycall(pri->pri, pri->pvts[chanpos]->call);
+ pri->pvts[chanpos]->call = NULL;
+ }
+ /* Force soft hangup if appropriate */
+ if (pri->pvts[chanpos]->realcall)
+ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ else if (pri->pvts[chanpos]->owner)
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ } else {
+ ast_verb(3, "Restart on requested on entire span %d\n", pri->span);
+ for (x = 0; x < pri->numchans; x++)
+ if (pri->pvts[x]) {
+ ast_mutex_lock(&pri->pvts[x]->lock);
+ if (pri->pvts[x]->call) {
+ pri_destroycall(pri->pri, pri->pvts[x]->call);
+ pri->pvts[x]->call = NULL;
+ }
+ if (pri->pvts[chanpos]->realcall)
+ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ else if (pri->pvts[x]->owner)
+ pri->pvts[x]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_mutex_unlock(&pri->pvts[x]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_KEYPAD_DIGIT:
+ chanpos = pri_find_principle(pri, e->digit.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "KEYPAD_DIGITs received on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->digit.channel), PRI_CHANNEL(e->digit.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->digit.call);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ /* queue DTMF frame if the PBX for this call was already started (we're forwarding KEYPAD_DIGITs further on */
+ if ((pri->overlapdial & ZAP_OVERLAPDIAL_INCOMING) && pri->pvts[chanpos]->call==e->digit.call && pri->pvts[chanpos]->owner) {
+ /* how to do that */
+ int digitlen = strlen(e->digit.digits);
+ char digit;
+ int i;
+ for (i = 0; i < digitlen; i++) {
+ digit = e->digit.digits[i];
+ {
+ struct ast_frame f = { AST_FRAME_DTMF, digit, };
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ }
+ }
+ }
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+
+ case PRI_EVENT_INFO_RECEIVED:
+ chanpos = pri_find_principle(pri, e->ring.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "INFO received on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->ring.call);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ /* queue DTMF frame if the PBX for this call was already started (we're forwarding INFORMATION further on */
+ if ((pri->overlapdial & ZAP_OVERLAPDIAL_INCOMING) && pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) {
+ /* how to do that */
+ int digitlen = strlen(e->ring.callednum);
+ char digit;
+ int i;
+ for (i = 0; i < digitlen; i++) {
+ digit = e->ring.callednum[i];
+ {
+ struct ast_frame f = { AST_FRAME_DTMF, digit, };
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ }
+ }
+ }
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_RING:
+ crv = NULL;
+ if (e->ring.channel == -1)
+ chanpos = pri_find_empty_chan(pri, 1);
+ else
+ chanpos = pri_find_principle(pri, e->ring.channel);
+ /* if no channel specified find one empty */
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
+ } else {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (pri->pvts[chanpos]->owner) {
+ if (pri->pvts[chanpos]->call == e->ring.call) {
+ ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n",
+ PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
+ break;
+ } else {
+ /* This is where we handle initial glare */
+ ast_debug(1, "Ring requested on channel %d/%d already in use or previously requested on span %d. Attempting to renegotiate channel.\n",
+ PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ chanpos = -1;
+ }
+ }
+ if (chanpos > -1)
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ if ((chanpos < 0) && (e->ring.flexible))
+ chanpos = pri_find_empty_chan(pri, 1);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (pri->switchtype == PRI_SWITCH_GR303_TMC) {
+ /* Should be safe to lock CRV AFAIK while bearer is still locked */
+ crv = pri_find_crv(pri, pri_get_crv(pri->pri, e->ring.call, NULL));
+ if (crv)
+ ast_mutex_lock(&crv->lock);
+ if (!crv || crv->owner) {
+ pri->pvts[chanpos]->call = NULL;
+ if (crv) {
+ if (crv->owner)
+ crv->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_log(LOG_WARNING, "Call received for busy CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span);
+ } else
+ ast_log(LOG_NOTICE, "Call received for unconfigured CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span);
+ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE);
+ if (crv)
+ ast_mutex_unlock(&crv->lock);
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ break;
+ }
+ }
+ pri->pvts[chanpos]->call = e->ring.call;
+ apply_plan_to_number(plancallingnum, sizeof(plancallingnum), pri, e->ring.callingnum, e->ring.callingplan);
+ if (pri->pvts[chanpos]->use_callerid) {
+ ast_shrink_phone_number(plancallingnum);
+ ast_copy_string(pri->pvts[chanpos]->cid_num, plancallingnum, sizeof(pri->pvts[chanpos]->cid_num));
+#ifdef PRI_ANI
+ if (!ast_strlen_zero(e->ring.callingani)) {
+ apply_plan_to_number(plancallingani, sizeof(plancallingani), pri, e->ring.callingani, e->ring.callingplanani);
+ ast_shrink_phone_number(plancallingani);
+ ast_copy_string(pri->pvts[chanpos]->cid_ani, plancallingani, sizeof(pri->pvts[chanpos]->cid_ani));
+ } else {
+ pri->pvts[chanpos]->cid_ani[0] = '\0';
+ }
+#endif
+ ast_copy_string(pri->pvts[chanpos]->cid_name, e->ring.callingname, sizeof(pri->pvts[chanpos]->cid_name));
+ pri->pvts[chanpos]->cid_ton = e->ring.callingplan; /* this is the callingplan (TON/NPI), e->ring.callingplan>>4 would be the TON */
+ } else {
+ pri->pvts[chanpos]->cid_num[0] = '\0';
+ pri->pvts[chanpos]->cid_ani[0] = '\0';
+ pri->pvts[chanpos]->cid_name[0] = '\0';
+ pri->pvts[chanpos]->cid_ton = 0;
+ }
+ apply_plan_to_number(pri->pvts[chanpos]->rdnis, sizeof(pri->pvts[chanpos]->rdnis), pri,
+ e->ring.redirectingnum, e->ring.callingplanrdnis);
+ /* If immediate=yes go to s|1 */
+ if (pri->pvts[chanpos]->immediate) {
+ ast_verb(3, "Going to extension s|1 because of immediate=yes\n");
+ pri->pvts[chanpos]->exten[0] = 's';
+ pri->pvts[chanpos]->exten[1] = '\0';
+ }
+ /* Get called number */
+ else if (!ast_strlen_zero(e->ring.callednum)) {
+ ast_copy_string(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten));
+ ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid));
+ } else if (pri->overlapdial)
+ pri->pvts[chanpos]->exten[0] = '\0';
+ else {
+ /* Some PRI circuits are set up to send _no_ digits. Handle them as 's'. */
+ pri->pvts[chanpos]->exten[0] = 's';
+ pri->pvts[chanpos]->exten[1] = '\0';
+ }
+ /* Set DNID on all incoming calls -- even immediate */
+ if (!ast_strlen_zero(e->ring.callednum))
+ ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid));
+ /* No number yet, but received "sending complete"? */
+ if (e->ring.complete && (ast_strlen_zero(e->ring.callednum))) {
+ ast_verb(3, "Going to extension s|1 because of Complete received\n");
+ pri->pvts[chanpos]->exten[0] = 's';
+ pri->pvts[chanpos]->exten[1] = '\0';
+ }
+ /* Make sure extension exists (or in overlap dial mode, can exist) */
+ if (((pri->overlapdial & ZAP_OVERLAPDIAL_INCOMING) && ast_canmatch_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) ||
+ ast_exists_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) {
+ /* Setup law */
+ int law;
+ if (pri->switchtype != PRI_SWITCH_GR303_TMC) {
+ /* Set to audio mode at this point */
+ law = 1;
+ if (ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1)
+ ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", pri->pvts[chanpos]->channel, law);
+ }
+ if (e->ring.layer1 == PRI_LAYER_1_ALAW)
+ law = ZT_LAW_ALAW;
+ else
+ law = ZT_LAW_MULAW;
+ res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pri->pvts[chanpos]->channel);
+ res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pri->pvts[chanpos]->channel);
+ if (e->ring.complete || !(pri->overlapdial & ZAP_OVERLAPDIAL_INCOMING)) {
+ /* Just announce proceeding */
+ pri->pvts[chanpos]->proceeding = 1;
+ pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0);
+ } else {
+ if (pri->switchtype != PRI_SWITCH_GR303_TMC)
+ pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
+ else
+ pri_answer(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
+ }
+ /* Get the use_callingpres state */
+ pri->pvts[chanpos]->callingpres = e->ring.callingpres;
+
+ /* Start PBX */
+ if ((pri->overlapdial & ZAP_OVERLAPDIAL_INCOMING) && ast_matchmore_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) {
+ /* Release the PRI lock while we create the channel */
+ ast_mutex_unlock(&pri->lock);
+ if (crv) {
+ /* Set bearer and such */
+ pri_assign_bearer(crv, pri, pri->pvts[chanpos]);
+ c = zt_new(crv, AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype);
+ pri->pvts[chanpos]->owner = &inuse;
+ ast_debug(1, "Started up crv %d:%d on bearer channel %d\n", pri->trunkgroup, crv->channel, crv->bearer->channel);
+ } else {
+ c = zt_new(pri->pvts[chanpos], AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype);
+ }
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+
+ if (!ast_strlen_zero(e->ring.callingsubaddr)) {
+ pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr);
+ }
+ if (e->ring.ani2 >= 0) {
+ snprintf(ani2str, 5, "%.2d", e->ring.ani2);
+ pbx_builtin_setvar_helper(c, "ANI2", ani2str);
+ pri->pvts[chanpos]->cid_ani2 = e->ring.ani2;
+ }
+
+#ifdef SUPPORT_USERUSER
+ if (!ast_strlen_zero(e->ring.useruserinfo)) {
+ pbx_builtin_setvar_helper(c, "USERUSERINFO", e->ring.useruserinfo);
+ }
+#endif
+
+ snprintf(calledtonstr, sizeof(calledtonstr)-1, "%d", e->ring.calledplan);
+ pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr);
+ if (e->ring.redirectingreason >= 0)
+ pbx_builtin_setvar_helper(c, "PRIREDIRECTREASON", redirectingreason2str(e->ring.redirectingreason));
+
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ ast_mutex_lock(&pri->lock);
+ if (c && !ast_pthread_create_detached(&threadid, NULL, ss_thread, c)) {
+ ast_verb(3, "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n",
+ plancallingnum, S_OR(pri->pvts[chanpos]->exten, "<unspecified>"),
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+ } else {
+ ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+ if (c)
+ ast_hangup(c);
+ else {
+ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
+ pri->pvts[chanpos]->call = NULL;
+ }
+ }
+ } else {
+ ast_mutex_unlock(&pri->lock);
+ /* Release PRI lock while we create the channel */
+ c = zt_new(pri->pvts[chanpos], AST_STATE_RING, 1, SUB_REAL, law, e->ring.ctype);
+ if (c) {
+ char calledtonstr[10];
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+
+ if (e->ring.ani2 >= 0) {
+ snprintf(ani2str, 5, "%d", e->ring.ani2);
+ pbx_builtin_setvar_helper(c, "ANI2", ani2str);
+ pri->pvts[chanpos]->cid_ani2 = e->ring.ani2;
+ }
+
+#ifdef SUPPORT_USERUSER
+ if (!ast_strlen_zero(e->ring.useruserinfo)) {
+ pbx_builtin_setvar_helper(c, "USERUSERINFO", e->ring.useruserinfo);
+ }
+#endif
+
+ if (e->ring.redirectingreason >= 0)
+ pbx_builtin_setvar_helper(c, "PRIREDIRECTREASON", redirectingreason2str(e->ring.redirectingreason));
+
+ snprintf(calledtonstr, sizeof(calledtonstr)-1, "%d", e->ring.calledplan);
+ pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr);
+
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ ast_mutex_lock(&pri->lock);
+
+ ast_verb(3, "Accepting call from '%s' to '%s' on channel %d/%d, span %d\n",
+ plancallingnum, pri->pvts[chanpos]->exten,
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+
+ zt_enable_ec(pri->pvts[chanpos]);
+ } else {
+
+ ast_mutex_lock(&pri->lock);
+
+ ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
+ pri->pvts[chanpos]->call = NULL;
+ }
+ }
+ } else {
+ ast_verb(3, "Extension '%s' in context '%s' from '%s' does not exist. Rejecting call on channel %d/%d, span %d\n",
+ pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context, pri->pvts[chanpos]->cid_num, pri->pvts[chanpos]->logicalspan,
+ pri->pvts[chanpos]->prioffset, pri->span);
+ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED);
+ pri->pvts[chanpos]->call = NULL;
+ pri->pvts[chanpos]->exten[0] = '\0';
+ }
+ if (crv)
+ ast_mutex_unlock(&crv->lock);
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ } else {
+ if (e->ring.flexible)
+ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+ else
+ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
+ }
+ break;
+ case PRI_EVENT_RINGING:
+ chanpos = pri_find_principle(pri, e->ringing.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->ringing.channel), PRI_CHANNEL(e->ringing.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->ringing.call);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Ringing requested on channel %d/%d not in use on span %d\n",
+ PRI_SPAN(e->ringing.channel), PRI_CHANNEL(e->ringing.channel), pri->span);
+ } else {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (ast_strlen_zero(pri->pvts[chanpos]->dop.dialstr)) {
+ zt_enable_ec(pri->pvts[chanpos]);
+ pri->pvts[chanpos]->subs[SUB_REAL].needringing = 1;
+ pri->pvts[chanpos]->alerting = 1;
+ } else
+ ast_debug(1, "Deferring ringing notification because of extra digits to dial...\n");
+
+#ifdef PRI_PROGRESS_MASK
+ if (e->ringing.progressmask & PRI_PROG_INBAND_AVAILABLE) {
+#else
+ if (e->ringing.progress == 8) {
+#endif
+ /* Now we can do call progress detection */
+ if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) {
+ /* RINGING detection isn't required because we got ALERTING signal */
+ ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features & ~DSP_PROGRESS_RINGING);
+ pri->pvts[chanpos]->dsp_features = 0;
+ }
+ }
+
+#ifdef SUPPORT_USERUSER
+ if (!ast_strlen_zero(e->ringing.useruserinfo)) {
+ struct ast_channel *owner = pri->pvts[chanpos]->owner;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->ringing.useruserinfo);
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ }
+#endif
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_PROGRESS:
+ /* Get chan value if e->e is not PRI_EVNT_RINGING */
+ chanpos = pri_find_principle(pri, e->proceeding.channel);
+ if (chanpos > -1) {
+#ifdef PRI_PROGRESS_MASK
+ if ((!pri->pvts[chanpos]->progress) || (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)) {
+#else
+ if ((!pri->pvts[chanpos]->progress) || (e->proceeding.progress == 8)) {
+#endif
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, };
+
+ if (e->proceeding.cause > -1) {
+ ast_verb(3, "PROGRESS with cause code %d received\n", e->proceeding.cause);
+
+ /* Work around broken, out of spec USER_BUSY cause in a progress message */
+ if (e->proceeding.cause == AST_CAUSE_USER_BUSY) {
+ if (pri->pvts[chanpos]->owner) {
+ ast_verb(3, "PROGRESS with 'user busy' received, signalling AST_CONTROL_BUSY instead of AST_CONTROL_PROGRESS\n");
+
+ pri->pvts[chanpos]->owner->hangupcause = e->proceeding.cause;
+ f.subclass = AST_CONTROL_BUSY;
+ }
+ }
+ }
+
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ ast_debug(1, "Queuing frame from PRI_EVENT_PROGRESS on channel %d/%d span %d\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span);
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+#ifdef PRI_PROGRESS_MASK
+ if (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) {
+#else
+ if (e->proceeding.progress == 8) {
+#endif
+ /* Now we can do call progress detection */
+ if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) {
+ ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features);
+ pri->pvts[chanpos]->dsp_features = 0;
+ }
+ }
+ pri->pvts[chanpos]->progress = 1;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_PROCEEDING:
+ chanpos = pri_find_principle(pri, e->proceeding.channel);
+ if (chanpos > -1) {
+ if (!pri->pvts[chanpos]->proceeding) {
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROCEEDING, };
+
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ ast_debug(1, "Queuing frame from PRI_EVENT_PROCEEDING on channel %d/%d span %d\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span);
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+#ifdef PRI_PROGRESS_MASK
+ if (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) {
+#else
+ if (e->proceeding.progress == 8) {
+#endif
+ /* Now we can do call progress detection */
+ if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) {
+ ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features);
+ pri->pvts[chanpos]->dsp_features = 0;
+ }
+ /* Bring voice path up */
+ f.subclass = AST_CONTROL_PROGRESS;
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ }
+ pri->pvts[chanpos]->proceeding = 1;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_FACNAME:
+ chanpos = pri_find_principle(pri, e->facname.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Facility Name requested on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->facname.channel), PRI_CHANNEL(e->facname.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->facname.call);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Facility Name requested on channel %d/%d not in use on span %d\n",
+ PRI_SPAN(e->facname.channel), PRI_CHANNEL(e->facname.channel), pri->span);
+ } else {
+ /* Re-use *69 field for PRI */
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ ast_copy_string(pri->pvts[chanpos]->lastcid_num, e->facname.callingnum, sizeof(pri->pvts[chanpos]->lastcid_num));
+ ast_copy_string(pri->pvts[chanpos]->lastcid_name, e->facname.callingname, sizeof(pri->pvts[chanpos]->lastcid_name));
+ pri->pvts[chanpos]->subs[SUB_REAL].needcallerid =1;
+ zt_enable_ec(pri->pvts[chanpos]);
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_ANSWER:
+ chanpos = pri_find_principle(pri, e->answer.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Answer on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->answer.call);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Answer requested on channel %d/%d not in use on span %d\n",
+ PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span);
+ } else {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ /* Now we can do call progress detection */
+
+ /* We changed this so it turns on the DSP no matter what... progress or no progress.
+ * By this time, we need DTMF detection and other features that were previously disabled
+ * -- Matt F */
+ if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) {
+ ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features);
+ pri->pvts[chanpos]->dsp_features = 0;
+ }
+ if (pri->pvts[chanpos]->realcall && (pri->pvts[chanpos]->realcall->sig == SIG_FXSKS)) {
+ ast_debug(1, "Starting up GR-303 trunk now that we got CONNECT...\n");
+ x = ZT_START;
+ res = ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_HOOK, &x);
+ if (res < 0) {
+ if (errno != EINPROGRESS) {
+ ast_log(LOG_WARNING, "Unable to start channel: %s\n", strerror(errno));
+ }
+ }
+ } else if (!ast_strlen_zero(pri->pvts[chanpos]->dop.dialstr)) {
+ pri->pvts[chanpos]->dialing = 1;
+ /* Send any "w" waited stuff */
+ res = ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_DIAL, &pri->pvts[chanpos]->dop);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d\n", pri->pvts[chanpos]->channel);
+ pri->pvts[chanpos]->dop.dialstr[0] = '\0';
+ } else
+ ast_debug(1, "Sent deferred digit string: %s\n", pri->pvts[chanpos]->dop.dialstr);
+
+ pri->pvts[chanpos]->dop.dialstr[0] = '\0';
+ } else if (pri->pvts[chanpos]->confirmanswer) {
+ ast_debug(1, "Waiting on answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel);
+ } else {
+ pri->pvts[chanpos]->subs[SUB_REAL].needanswer =1;
+ /* Enable echo cancellation if it's not on already */
+ zt_enable_ec(pri->pvts[chanpos]);
+ }
+
+#ifdef SUPPORT_USERUSER
+ if (!ast_strlen_zero(e->answer.useruserinfo)) {
+ struct ast_channel *owner = pri->pvts[chanpos]->owner;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->answer.useruserinfo);
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ }
+#endif
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_HANGUP:
+ chanpos = pri_find_principle(pri, e->hangup.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Hangup requested on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (!pri->pvts[chanpos]->alreadyhungup) {
+ /* we're calling here zt_hangup so once we get there we need to clear p->call after calling pri_hangup */
+ pri->pvts[chanpos]->alreadyhungup = 1;
+ if (pri->pvts[chanpos]->realcall)
+ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ else if (pri->pvts[chanpos]->owner) {
+ /* Queue a BUSY instead of a hangup if our cause is appropriate */
+ pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
+ if (pri->pvts[chanpos]->owner->_state == AST_STATE_UP)
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ else {
+ switch (e->hangup.cause) {
+ case PRI_CAUSE_USER_BUSY:
+ pri->pvts[chanpos]->subs[SUB_REAL].needbusy =1;
+ break;
+ case PRI_CAUSE_CALL_REJECTED:
+ case PRI_CAUSE_NETWORK_OUT_OF_ORDER:
+ case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ case PRI_CAUSE_SWITCH_CONGESTION:
+ case PRI_CAUSE_DESTINATION_OUT_OF_ORDER:
+ case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE:
+ pri->pvts[chanpos]->subs[SUB_REAL].needcongestion =1;
+ break;
+ default:
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+ }
+ }
+ ast_verb(3, "Channel %d/%d, span %d got hangup, cause %d\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, e->hangup.cause);
+ } else {
+ pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
+ pri->pvts[chanpos]->call = NULL;
+ }
+ if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) {
+ ast_verb(3, "Forcing restart of channel %d/%d on span %d since channel reported in use\n",
+ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
+ pri->pvts[chanpos]->resetting = 1;
+ }
+ if (e->hangup.aoc_units > -1)
+ ast_verb(3, "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
+
+#ifdef SUPPORT_USERUSER
+ if (pri->pvts[chanpos]->owner && !ast_strlen_zero(e->hangup.useruserinfo)) {
+ struct ast_channel *owner = pri->pvts[chanpos]->owner;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->hangup.useruserinfo);
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ }
+#endif
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ } else {
+ ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n",
+ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ }
+ }
+ break;
+#ifndef PRI_EVENT_HANGUP_REQ
+#error please update libpri
+#endif
+ case PRI_EVENT_HANGUP_REQ:
+ chanpos = pri_find_principle(pri, e->hangup.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (pri->pvts[chanpos]->realcall)
+ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ else if (pri->pvts[chanpos]->owner) {
+ pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
+ if (pri->pvts[chanpos]->owner->_state == AST_STATE_UP)
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ else {
+ switch (e->hangup.cause) {
+ case PRI_CAUSE_USER_BUSY:
+ pri->pvts[chanpos]->subs[SUB_REAL].needbusy =1;
+ break;
+ case PRI_CAUSE_CALL_REJECTED:
+ case PRI_CAUSE_NETWORK_OUT_OF_ORDER:
+ case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ case PRI_CAUSE_SWITCH_CONGESTION:
+ case PRI_CAUSE_DESTINATION_OUT_OF_ORDER:
+ case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE:
+ pri->pvts[chanpos]->subs[SUB_REAL].needcongestion =1;
+ break;
+ default:
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+ }
+ ast_verb(3, "Channel %d/%d, span %d got hangup request, cause %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span, e->hangup.cause);
+ if (e->hangup.aoc_units > -1)
+ ast_verb(3, "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
+ } else {
+ pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
+ pri->pvts[chanpos]->call = NULL;
+ }
+ if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) {
+ ast_verb(3, "Forcing restart of channel %d/%d span %d since channel reported in use\n",
+ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
+ pri->pvts[chanpos]->resetting = 1;
+ }
+
+#ifdef SUPPORT_USERUSER
+ if (!ast_strlen_zero(e->hangup.useruserinfo)) {
+ struct ast_channel *owner = pri->pvts[chanpos]->owner;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->hangup.useruserinfo);
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ }
+#endif
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ } else {
+ ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ }
+ }
+ break;
+ case PRI_EVENT_HANGUP_ACK:
+ chanpos = pri_find_principle(pri, e->hangup.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Hangup ACK requested on unconfigured channel number %d/%d span %d\n",
+ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ pri->pvts[chanpos]->call = NULL;
+ pri->pvts[chanpos]->resetting = 0;
+ if (pri->pvts[chanpos]->owner) {
+ ast_verb(3, "Channel %d/%d, span %d got hangup ACK\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ }
+
+#ifdef SUPPORT_USERUSER
+ if (!ast_strlen_zero(e->hangup.useruserinfo)) {
+ struct ast_channel *owner = pri->pvts[chanpos]->owner;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->hangup.useruserinfo);
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ }
+#endif
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ }
+ break;
+ case PRI_EVENT_CONFIG_ERR:
+ ast_log(LOG_WARNING, "PRI Error on span %d: %s\n", pri->trunkgroup, e->err.err);
+ break;
+ case PRI_EVENT_RESTART_ACK:
+ chanpos = pri_find_principle(pri, e->restartack.channel);
+ if (chanpos < 0) {
+ /* Sometime switches (e.g. I421 / British Telecom) don't give us the
+ channel number, so we have to figure it out... This must be why
+ everybody resets exactly a channel at a time. */
+ for (x = 0; x < pri->numchans; x++) {
+ if (pri->pvts[x] && pri->pvts[x]->resetting) {
+ chanpos = x;
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ ast_debug(1, "Assuming restart ack is really for channel %d/%d span %d\n", pri->pvts[chanpos]->logicalspan,
+ pri->pvts[chanpos]->prioffset, pri->span);
+ if (pri->pvts[chanpos]->realcall)
+ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ else if (pri->pvts[chanpos]->owner) {
+ ast_log(LOG_WARNING, "Got restart ack on channel %d/%d with owner on span %d\n", pri->pvts[chanpos]->logicalspan,
+ pri->pvts[chanpos]->prioffset, pri->span);
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+ pri->pvts[chanpos]->resetting = 0;
+ ast_verb(3, "B-channel %d/%d successfully restarted on span %d\n", pri->pvts[chanpos]->logicalspan,
+ pri->pvts[chanpos]->prioffset, pri->span);
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ if (pri->resetting)
+ pri_check_restart(pri);
+ break;
+ }
+ }
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Restart ACK requested on strange channel %d/%d span %d\n",
+ PRI_SPAN(e->restartack.channel), PRI_CHANNEL(e->restartack.channel), pri->span);
+ }
+ } else {
+ if (pri->pvts[chanpos]) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (pri->pvts[chanpos]->realcall)
+ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ else if (pri->pvts[chanpos]->owner) {
+ ast_log(LOG_WARNING, "Got restart ack on channel %d/%d span %d with owner\n",
+ PRI_SPAN(e->restartack.channel), PRI_CHANNEL(e->restartack.channel), pri->span);
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+ pri->pvts[chanpos]->resetting = 0;
+ pri->pvts[chanpos]->inservice = 1;
+ ast_verb(3, "B-channel %d/%d successfully restarted on span %d\n", pri->pvts[chanpos]->logicalspan,
+ pri->pvts[chanpos]->prioffset, pri->span);
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ if (pri->resetting)
+ pri_check_restart(pri);
+ }
+ }
+ break;
+ case PRI_EVENT_SETUP_ACK:
+ chanpos = pri_find_principle(pri, e->setup_ack.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Received SETUP_ACKNOWLEDGE on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->setup_ack.channel), PRI_CHANNEL(e->setup_ack.channel), pri->span);
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->setup_ack.call);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ pri->pvts[chanpos]->setup_ack = 1;
+ /* Send any queued digits */
+ for (x = 0;x < strlen(pri->pvts[chanpos]->dialdest); x++) {
+ ast_debug(1, "Sending pending digit '%c'\n", pri->pvts[chanpos]->dialdest[x]);
+ pri_information(pri->pri, pri->pvts[chanpos]->call,
+ pri->pvts[chanpos]->dialdest[x]);
+ }
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ } else
+ ast_log(LOG_WARNING, "Unable to move channel %d!\n", e->setup_ack.channel);
+ }
+ break;
+ case PRI_EVENT_NOTIFY:
+ chanpos = pri_find_principle(pri, e->notify.channel);
+ if (chanpos < 0) {
+ ast_log(LOG_WARNING, "Received NOTIFY on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(e->notify.channel), PRI_CHANNEL(e->notify.channel), pri->span);
+ } else {
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ switch (e->notify.info) {
+ case PRI_NOTIFY_REMOTE_HOLD:
+ f.subclass = AST_CONTROL_HOLD;
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ break;
+ case PRI_NOTIFY_REMOTE_RETRIEVAL:
+ f.subclass = AST_CONTROL_UNHOLD;
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ break;
+ }
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ break;
+ default:
+ ast_debug(1, "Event: %d\n", e->e);
+ }
+ }
+ ast_mutex_unlock(&pri->lock);
+ }
+ /* Never reached */
+ return NULL;
+}
+
+static int start_pri(struct zt_pri *pri)
+{
+ int res, x;
+ ZT_PARAMS p;
+ ZT_BUFFERINFO bi;
+ struct zt_spaninfo si;
+ int i;
+
+ for (i = 0; i < NUM_DCHANS; i++) {
+ if (!pri->dchannels[i])
+ break;
+ pri->fds[i] = open("/dev/zap/channel", O_RDWR);
+ x = pri->dchannels[i];
+ if ((pri->fds[i] < 0) || (ioctl(pri->fds[i],ZT_SPECIFY,&x) == -1)) {
+ ast_log(LOG_ERROR, "Unable to open D-channel %d (%s)\n", x, strerror(errno));
+ return -1;
+ }
+ res = ioctl(pri->fds[i], ZT_GET_PARAMS, &p);
+ if (res) {
+ zt_close(pri->fds[i]);
+ pri->fds[i] = -1;
+ ast_log(LOG_ERROR, "Unable to get parameters for D-channel %d (%s)\n", x, strerror(errno));
+ return -1;
+ }
+ if ((p.sigtype != ZT_SIG_HDLCFCS) && (p.sigtype != ZT_SIG_HARDHDLC)) {
+ zt_close(pri->fds[i]);
+ pri->fds[i] = -1;
+ ast_log(LOG_ERROR, "D-channel %d is not in HDLC/FCS mode. See /etc/zaptel.conf\n", x);
+ return -1;
+ }
+ memset(&si, 0, sizeof(si));
+ res = ioctl(pri->fds[i], ZT_SPANSTAT, &si);
+ if (res) {
+ zt_close(pri->fds[i]);
+ pri->fds[i] = -1;
+ ast_log(LOG_ERROR, "Unable to get span state for D-channel %d (%s)\n", x, strerror(errno));
+ }
+ if (!si.alarms)
+ pri->dchanavail[i] |= DCHAN_NOTINALARM;
+ else
+ pri->dchanavail[i] &= ~DCHAN_NOTINALARM;
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = 32;
+ bi.bufsize = 1024;
+ if (ioctl(pri->fds[i], ZT_SET_BUFINFO, &bi)) {
+ ast_log(LOG_ERROR, "Unable to set appropriate buffering on channel %d\n", x);
+ zt_close(pri->fds[i]);
+ pri->fds[i] = -1;
+ return -1;
+ }
+ switch (pri->sig) {
+ case SIG_BRI:
+ pri->dchans[i] = pri_new_bri(pri->fds[i], 1, pri->nodetype, pri->switchtype);
+ break;
+ case SIG_BRI_PTMP:
+ pri->dchans[i] = pri_new_bri(pri->fds[i], 0, pri->nodetype, pri->switchtype);
+ break;
+ default:
+ pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype);
+ }
+ /* Force overlap dial if we're doing GR-303! */
+ if (pri->switchtype == PRI_SWITCH_GR303_TMC)
+ pri->overlapdial |= ZAP_OVERLAPDIAL_BOTH;
+ pri_set_overlapdial(pri->dchans[i],(pri->overlapdial & ZAP_OVERLAPDIAL_OUTGOING)?1:0);
+ /* Enslave to master if appropriate */
+ if (i)
+ pri_enslave(pri->dchans[0], pri->dchans[i]);
+ if (!pri->dchans[i]) {
+ zt_close(pri->fds[i]);
+ pri->fds[i] = -1;
+ ast_log(LOG_ERROR, "Unable to create PRI structure\n");
+ return -1;
+ }
+ pri_set_debug(pri->dchans[i], DEFAULT_PRI_DEBUG);
+ pri_set_nsf(pri->dchans[i], pri->nsf);
+#ifdef PRI_GETSET_TIMERS
+ for (x = 0; x < PRI_MAX_TIMERS; x++) {
+ if (pritimers[x] != 0)
+ pri_set_timer(pri->dchans[i], x, pritimers[x]);
+ }
+#endif
+ }
+ /* Assume primary is the one we use */
+ pri->pri = pri->dchans[0];
+ pri->resetpos = -1;
+ if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) {
+ for (i = 0; i < NUM_DCHANS; i++) {
+ if (!pri->dchannels[i])
+ break;
+ zt_close(pri->fds[i]);
+ pri->fds[i] = -1;
+ }
+ ast_log(LOG_ERROR, "Unable to spawn D-channel: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static char *complete_span_helper(const char *line, const char *word, int pos, int state, int rpos)
+{
+ int which, span;
+ char *ret = NULL;
+
+ if (pos != rpos)
+ return ret;
+
+ for (which = span = 0; span < NUM_SPANS; span++) {
+ if (pris[span].pri && ++which > state) {
+ asprintf(&ret, "%d", span + 1); /* user indexes start from 1 */
+ break;
+ }
+ }
+ return ret;
+}
+
+static char *complete_span_4(const char *line, const char *word, int pos, int state)
+{
+ return complete_span_helper(line,word,pos,state,3);
+}
+
+static char *complete_span_5(const char *line, const char *word, int pos, int state)
+{
+ return complete_span_helper(line,word,pos,state,4);
+}
+
+static char *handle_pri_unset_debug_file(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri unset debug file";
+ e->usage = "Usage: pri unset debug file\n"
+ " Stop sending debug output to the previously \n"
+ " specified file\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ /* Assume it is unset */
+ ast_mutex_lock(&pridebugfdlock);
+ close(pridebugfd);
+ pridebugfd = -1;
+ ast_cli(a->fd, "PRI debug output to file disabled\n");
+ ast_mutex_unlock(&pridebugfdlock);
+ return CLI_SUCCESS;
+}
+
+static char *handle_pri_set_debug_file(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int myfd;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri set debug file";
+ e->usage = "Usage: pri set debug file [output-file]\n"
+ " Sends PRI debug output to the specified output file\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 5)
+ return CLI_SHOWUSAGE;
+
+ if (ast_strlen_zero(a->argv[4]))
+ return CLI_SHOWUSAGE;
+
+ myfd = open(a->argv[4], O_CREAT|O_WRONLY, AST_FILE_MODE);
+ if (myfd < 0) {
+ ast_cli(a->fd, "Unable to open '%s' for writing\n", a->argv[4]);
+ return CLI_SUCCESS;
+ }
+
+ ast_mutex_lock(&pridebugfdlock);
+
+ if (pridebugfd >= 0)
+ close(pridebugfd);
+
+ pridebugfd = myfd;
+ ast_copy_string(pridebugfilename,a->argv[4],sizeof(pridebugfilename));
+ ast_mutex_unlock(&pridebugfdlock);
+ ast_cli(a->fd, "PRI debug output will be sent to '%s'\n", a->argv[4]);
+ return CLI_SUCCESS;
+}
+
+static char *handle_pri_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int span;
+ int x;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri debug span";
+ e->usage =
+ "Usage: pri debug span <span>\n"
+ " Enables debugging on a given PRI span\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_span_4(a->line, a->word, a->pos, a->n);
+ }
+ if (a->argc < 4) {
+ return CLI_SHOWUSAGE;
+ }
+ span = atoi(a->argv[3]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid span %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+ if (!pris[span-1].pri) {
+ ast_cli(a->fd, "No PRI running on span %d\n", span);
+ return CLI_SUCCESS;
+ }
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if (pris[span-1].dchans[x])
+ pri_set_debug(pris[span-1].dchans[x], PRI_DEBUG_APDU |
+ PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE |
+ PRI_DEBUG_Q921_STATE);
+ }
+ ast_cli(a->fd, "Enabled debugging on span %d\n", span);
+ return CLI_SUCCESS;
+}
+
+
+
+static char *handle_pri_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int span;
+ int x;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri no debug span";
+ e->usage =
+ "Usage: pri no debug span <span>\n"
+ " Disables debugging on a given PRI span\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_span_5(a->line, a->word, a->pos, a->n);
+ }
+ if (a->argc < 5)
+ return CLI_SHOWUSAGE;
+
+ span = atoi(a->argv[4]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid span %s. Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+ if (!pris[span-1].pri) {
+ ast_cli(a->fd, "No PRI running on span %d\n", span);
+ return CLI_SUCCESS;
+ }
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if (pris[span-1].dchans[x])
+ pri_set_debug(pris[span-1].dchans[x], 0);
+ }
+ ast_cli(a->fd, "Disabled debugging on span %d\n", span);
+ return CLI_SUCCESS;
+}
+
+static char *handle_pri_really_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int span;
+ int x;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri intensive debug span";
+ e->usage =
+ "Usage: pri intensive debug span <span>\n"
+ " Enables debugging down to the Q.921 level\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_span_5(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 5)
+ return CLI_SHOWUSAGE;
+ span = atoi(a->argv[4]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid span %s. Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+ if (!pris[span-1].pri) {
+ ast_cli(a->fd, "No PRI running on span %d\n", span);
+ return CLI_SUCCESS;
+ }
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if (pris[span-1].dchans[x])
+ pri_set_debug(pris[span-1].dchans[x], PRI_DEBUG_APDU |
+ PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE |
+ PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_STATE);
+ }
+ ast_cli(a->fd, "Enabled EXTENSIVE debugging on span %d\n", span);
+ return CLI_SUCCESS;
+}
+
+static void build_status(char *s, size_t len, int status, int active)
+{
+ if (!s || len < 1) {
+ return;
+ }
+ s[0] = '\0';
+ if (status & DCHAN_PROVISIONED)
+ strncat(s, "Provisioned, ", len - strlen(s) - 1);
+ if (!(status & DCHAN_NOTINALARM))
+ strncat(s, "In Alarm, ", len - strlen(s) - 1);
+ if (status & DCHAN_UP)
+ strncat(s, "Up", len - strlen(s) - 1);
+ else
+ strncat(s, "Down", len - strlen(s) - 1);
+ if (active)
+ strncat(s, ", Active", len - strlen(s) - 1);
+ else
+ strncat(s, ", Standby", len - strlen(s) - 1);
+ s[len - 1] = '\0';
+}
+
+static char *handle_pri_show_spans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int span;
+ int x;
+ char status[256];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri show spans";
+ e->usage =
+ "Usage: pri show spans\n"
+ " Displays PRI Information\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ for (span = 0; span < NUM_SPANS; span++) {
+ if (pris[span].pri) {
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if (pris[span].dchannels[x]) {
+ build_status(status, sizeof(status), pris[span].dchanavail[x], pris[span].dchans[x] == pris[span].pri);
+ ast_cli(a->fd, "PRI span %d/%d: %s\n", span + 1, x, status);
+ }
+ }
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_pri_show_span(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int span;
+ int x;
+ char status[256];
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri show span";
+ e->usage =
+ "Usage: pri show span <span>\n"
+ " Displays PRI Information on a given PRI span\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_span_4(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+ span = atoi(a->argv[3]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid span '%s'. Should be a number from %d to %d\n", a->argv[3], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+ if (!pris[span-1].pri) {
+ ast_cli(a->fd, "No PRI running on span %d\n", span);
+ return CLI_SUCCESS;
+ }
+ for (x = 0; x < NUM_DCHANS; x++) {
+ if (pris[span-1].dchannels[x]) {
+#ifdef PRI_DUMP_INFO_STR
+ char *info_str = NULL;
+#endif
+ ast_cli(a->fd, "%s D-channel: %d\n", pri_order(x), pris[span-1].dchannels[x]);
+ build_status(status, sizeof(status), pris[span-1].dchanavail[x], pris[span-1].dchans[x] == pris[span-1].pri);
+ ast_cli(a->fd, "Status: %s\n", status);
+#ifdef PRI_DUMP_INFO_STR
+ info_str = pri_dump_info_str(pris[span-1].pri);
+ if (info_str) {
+ ast_cli(a->fd, "%s", info_str);
+ ast_free(info_str);
+ }
+#else
+ pri_dump_info(pris[span-1].pri);
+#endif
+ ast_cli(a->fd, "Overlap Recv: %s\n\n", (pris[span-1].overlapdial & ZAP_OVERLAPDIAL_INCOMING)?"Yes":"No");
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_pri_show_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int x;
+ int span;
+ int count=0;
+ int debug=0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pri show debug";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ for (span = 0; span < NUM_SPANS; span++) {
+ if (pris[span].pri) {
+ for (x = 0; x < NUM_DCHANS; x++) {
+ debug = 0;
+ if (pris[span].dchans[x]) {
+ debug = pri_get_debug(pris[span].dchans[x]);
+ ast_cli(a->fd, "Span %d: Debug: %s\tIntense: %s\n", span+1, (debug&PRI_DEBUG_Q931_STATE)? "Yes" : "No" ,(debug&PRI_DEBUG_Q921_RAW)? "Yes" : "No" );
+ count++;
+ }
+ }
+ }
+
+ }
+ ast_mutex_lock(&pridebugfdlock);
+ if (pridebugfd >= 0)
+ ast_cli(a->fd, "Logging PRI debug to file %s\n", pridebugfilename);
+ ast_mutex_unlock(&pridebugfdlock);
+
+ if (!count)
+ ast_cli(a->fd, "No debug set or no PRI running\n");
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry zap_pri_cli[] = {
+ AST_CLI_DEFINE(handle_pri_debug, "Enables PRI debugging on a span"),
+ AST_CLI_DEFINE(handle_pri_no_debug, "Disables PRI debugging on a span"),
+ AST_CLI_DEFINE(handle_pri_really_debug, "Enables REALLY INTENSE PRI debugging"),
+ AST_CLI_DEFINE(handle_pri_show_spans, "Displays PRI Information"),
+ AST_CLI_DEFINE(handle_pri_show_span, "Displays PRI Information"),
+ AST_CLI_DEFINE(handle_pri_show_debug, "Displays current PRI debug settings"),
+ AST_CLI_DEFINE(handle_pri_set_debug_file, "Sends PRI debug output to the specified file"),
+ AST_CLI_DEFINE(handle_pri_unset_debug_file, "Ends PRI debug output to file"),
+};
+
+#endif /* HAVE_PRI */
+
+static char *zap_destroy_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int channel;
+ int ret;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap destroy channel";
+ e->usage =
+ "Usage: zap destroy channel <chan num>\n"
+ " DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING. Immediately removes a given channel, whether it is in use or not\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ channel = atoi(a->argv[3]);
+ ret = zap_destroy_channel_bynum(channel);
+ return ( RESULT_SUCCESS == ret ) ? CLI_SUCCESS : CLI_FAILURE;
+}
+
+static int setup_zap(int reload);
+static int zap_restart(void)
+{
+ ast_verb(1, "Destroying channels and reloading Zaptel configuration.\n");
+ while (iflist) {
+ ast_debug(1, "Destroying Zaptel channel no. %d\n", iflist->channel);
+ /* Also updates iflist: */
+ destroy_channel(NULL, iflist, 1);
+ }
+ ast_debug(1, "Channels destroyed. Now re-reading config.\n");
+ if (setup_zap(2) != 0) {
+ ast_log(LOG_WARNING, "Reload channels from zap config failed!\n");
+ return 1;
+ }
+ return 0;
+}
+
+static char *zap_restart_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap restart";
+ e->usage =
+ "Usage: zap restart\n"
+ " Restarts the Zaptel channels: destroys them all and then\n"
+ " re-reads them from zapata.conf.\n"
+ " Note that this will STOP any running CALL on Zaptel channels.\n"
+ "";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ if (zap_restart() != 0)
+ return CLI_FAILURE;
+ return CLI_SUCCESS;
+}
+
+static int action_zaprestart(struct mansession *s, const struct message *m)
+{
+ if (zap_restart() != 0) {
+ astman_send_error(s, m, "Failed rereading Zaptel configuration");
+ return 1;
+ }
+ astman_send_ack(s, m, "ZapRestart: Success");
+ return 0;
+}
+
+static char *zap_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%7s %-10.10s %-15.15s %-10.10s %-20.20s %-10.10s %-10.10s\n"
+#define FORMAT2 "%7s %-10.10s %-15.15s %-10.10s %-20.20s %-10.10s %-10.10s\n"
+ unsigned int targetnum = 0;
+ int filtertype = 0;
+ struct zt_pvt *tmp = NULL;
+ char tmps[20] = "";
+ char statestr[20] = "";
+ char blockstr[20] = "";
+ ast_mutex_t *lock;
+ struct zt_pvt *start;
+#ifdef HAVE_PRI
+ int trunkgroup;
+ struct zt_pri *pri = NULL;
+ int x;
+#endif
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap show channels [trunkgroup|group|context]";
+ e->usage =
+ "Usage: zap show channels [ trunkgroup <trunkgroup> | group <group> | context <context> ]\n"
+ " Shows a list of available channels with optional filtering\n"
+ " <group> must be a number between 0 and 63\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ lock = &iflock;
+ start = iflist;
+
+ /* syntax: zap show channels [ group <group> | context <context> | trunkgroup <trunkgroup> ] */
+
+ if (!((a->argc == 3) || (a->argc == 5)))
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 5) {
+#ifdef HAVE_PRI
+ if (!strcasecmp(a->argv[3], "trunkgroup")) {
+ /* this option requires no special handling, so leave filtertype to zero */
+ if ((trunkgroup = atoi(a->argv[4])) < 1)
+ return CLI_SHOWUSAGE;
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (pris[x].trunkgroup == trunkgroup) {
+ pri = pris + x;
+ break;
+ }
+ }
+ if (pri) {
+ start = pri->crvs;
+ lock = &pri->lock;
+ } else {
+ ast_cli(a->fd, "No such trunk group %d\n", trunkgroup);
+ return CLI_FAILURE;
+ }
+ } else
+#endif
+ if (!strcasecmp(a->argv[3], "group")) {
+ targetnum = atoi(a->argv[4]);
+ if ((targetnum < 0) || (targetnum > 63))
+ return CLI_SHOWUSAGE;
+ targetnum = 1 << targetnum;
+ filtertype = 1;
+ } else if (!strcasecmp(a->argv[3], "context")) {
+ filtertype = 2;
+ }
+ }
+
+ ast_mutex_lock(lock);
+#ifdef HAVE_PRI
+ ast_cli(a->fd, FORMAT2, pri ? "CRV" : "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "State");
+#else
+ ast_cli(a->fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "State");
+#endif
+
+ tmp = start;
+ while (tmp) {
+ if (filtertype) {
+ switch(filtertype) {
+ case 1: /* zap show channels group <group> */
+ if (tmp->group != targetnum) {
+ tmp = tmp->next;
+ continue;
+ }
+ break;
+ case 2: /* zap show channels context <context> */
+ if (strcasecmp(tmp->context, a->argv[4])) {
+ tmp = tmp->next;
+ continue;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ if (tmp->channel > 0) {
+ snprintf(tmps, sizeof(tmps), "%d", tmp->channel);
+ } else
+ ast_copy_string(tmps, "pseudo", sizeof(tmps));
+
+ if (tmp->locallyblocked)
+ blockstr[0] = 'L';
+ else
+ blockstr[0] = ' ';
+
+ if (tmp->remotelyblocked)
+ blockstr[1] = 'R';
+ else
+ blockstr[1] = ' ';
+
+ blockstr[2] = '\0';
+
+ snprintf(statestr, sizeof(statestr), "%s", "In Service");
+
+ ast_cli(a->fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret, blockstr, statestr);
+ tmp = tmp->next;
+ }
+ ast_mutex_unlock(lock);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *zap_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int channel;
+ struct zt_pvt *tmp = NULL;
+ ZT_CONFINFO ci;
+ ZT_PARAMS ps;
+ int x;
+ ast_mutex_t *lock;
+ struct zt_pvt *start;
+#ifdef HAVE_PRI
+ char *c;
+ int trunkgroup;
+ struct zt_pri *pri=NULL;
+#endif
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap show channel";
+ e->usage =
+ "Usage: zap show channel <chan num>\n"
+ " Detailed information about a given channel\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ lock = &iflock;
+ start = iflist;
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+#ifdef HAVE_PRI
+ if ((c = strchr(a->argv[3], ':'))) {
+ if (sscanf(a->argv[3], "%d:%d", &trunkgroup, &channel) != 2)
+ return CLI_SHOWUSAGE;
+ if ((trunkgroup < 1) || (channel < 1))
+ return CLI_SHOWUSAGE;
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (pris[x].trunkgroup == trunkgroup) {
+ pri = pris + x;
+ break;
+ }
+ }
+ if (pri) {
+ start = pri->crvs;
+ lock = &pri->lock;
+ } else {
+ ast_cli(a->fd, "No such trunk group %d\n", trunkgroup);
+ return CLI_FAILURE;
+ }
+ } else
+#endif
+ channel = atoi(a->argv[3]);
+
+ ast_mutex_lock(lock);
+ tmp = start;
+ while (tmp) {
+ if (tmp->channel == channel) {
+#ifdef HAVE_PRI
+ if (pri)
+ ast_cli(a->fd, "Trunk/CRV: %d/%d\n", trunkgroup, tmp->channel);
+ else
+#endif
+ ast_cli(a->fd, "Channel: %d\n", tmp->channel);
+ ast_cli(a->fd, "File Descriptor: %d\n", tmp->subs[SUB_REAL].zfd);
+ ast_cli(a->fd, "Span: %d\n", tmp->span);
+ ast_cli(a->fd, "Extension: %s\n", tmp->exten);
+ ast_cli(a->fd, "Dialing: %s\n", tmp->dialing ? "yes" : "no");
+ ast_cli(a->fd, "Context: %s\n", tmp->context);
+ ast_cli(a->fd, "Caller ID: %s\n", tmp->cid_num);
+ ast_cli(a->fd, "Calling TON: %d\n", tmp->cid_ton);
+ ast_cli(a->fd, "Caller ID name: %s\n", tmp->cid_name);
+ ast_cli(a->fd, "Mailbox: %s\n", S_OR(tmp->mailbox, "none"));
+ if (tmp->vars) {
+ struct ast_variable *v;
+ ast_cli(a->fd, "Variables:\n");
+ for (v = tmp->vars ; v ; v = v->next)
+ ast_cli(a->fd, " %s = %s\n", v->name, v->value);
+ }
+ ast_cli(a->fd, "Destroy: %d\n", tmp->destroy);
+ ast_cli(a->fd, "InAlarm: %d\n", tmp->inalarm);
+ ast_cli(a->fd, "Signalling Type: %s\n", sig2str(tmp->sig));
+ ast_cli(a->fd, "Radio: %d\n", tmp->radio);
+ ast_cli(a->fd, "Owner: %s\n", tmp->owner ? tmp->owner->name : "<None>");
+ ast_cli(a->fd, "Real: %s%s%s\n", tmp->subs[SUB_REAL].owner ? tmp->subs[SUB_REAL].owner->name : "<None>", tmp->subs[SUB_REAL].inthreeway ? " (Confed)" : "", tmp->subs[SUB_REAL].linear ? " (Linear)" : "");
+ ast_cli(a->fd, "Callwait: %s%s%s\n", tmp->subs[SUB_CALLWAIT].owner ? tmp->subs[SUB_CALLWAIT].owner->name : "<None>", tmp->subs[SUB_CALLWAIT].inthreeway ? " (Confed)" : "", tmp->subs[SUB_CALLWAIT].linear ? " (Linear)" : "");
+ ast_cli(a->fd, "Threeway: %s%s%s\n", tmp->subs[SUB_THREEWAY].owner ? tmp->subs[SUB_THREEWAY].owner->name : "<None>", tmp->subs[SUB_THREEWAY].inthreeway ? " (Confed)" : "", tmp->subs[SUB_THREEWAY].linear ? " (Linear)" : "");
+ ast_cli(a->fd, "Confno: %d\n", tmp->confno);
+ ast_cli(a->fd, "Propagated Conference: %d\n", tmp->propconfno);
+ ast_cli(a->fd, "Real in conference: %d\n", tmp->inconference);
+ ast_cli(a->fd, "DSP: %s\n", tmp->dsp ? "yes" : "no");
+ ast_cli(a->fd, "Busy Detection: %s\n", tmp->busydetect ? "yes" : "no");
+ if (tmp->busydetect) {
+#if defined(BUSYDETECT_TONEONLY)
+ ast_cli(a->fd, " Busy Detector Helper: BUSYDETECT_TONEONLY\n");
+#elif defined(BUSYDETECT_COMPARE_TONE_AND_SILENCE)
+ ast_cli(a->fd, " Busy Detector Helper: BUSYDETECT_COMPARE_TONE_AND_SILENCE\n");
+#endif
+#ifdef BUSYDETECT_DEBUG
+ ast_cli(a->fd, " Busy Detector Debug: Enabled\n");
+#endif
+ ast_cli(a->fd, " Busy Count: %d\n", tmp->busycount);
+ ast_cli(a->fd, " Busy Pattern: %d,%d\n", tmp->busy_tonelength, tmp->busy_quietlength);
+ }
+ ast_cli(a->fd, "TDD: %s\n", tmp->tdd ? "yes" : "no");
+ ast_cli(a->fd, "Relax DTMF: %s\n", tmp->dtmfrelax ? "yes" : "no");
+ ast_cli(a->fd, "Dialing/CallwaitCAS: %d/%d\n", tmp->dialing, tmp->callwaitcas);
+ ast_cli(a->fd, "Default law: %s\n", tmp->law == ZT_LAW_MULAW ? "ulaw" : tmp->law == ZT_LAW_ALAW ? "alaw" : "unknown");
+ ast_cli(a->fd, "Fax Handled: %s\n", tmp->faxhandled ? "yes" : "no");
+ ast_cli(a->fd, "Pulse phone: %s\n", tmp->pulsedial ? "yes" : "no");
+ ast_cli(a->fd, "DND: %s\n", tmp->dnd ? "yes" : "no");
+ ast_cli(a->fd, "Echo Cancellation:\n");
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+ if (tmp->echocancel.head.tap_length) {
+ ast_cli(a->fd, "\t%d taps\n", tmp->echocancel.head.tap_length);
+ for (x = 0; x < tmp->echocancel.head.param_count; x++) {
+ ast_cli(a->fd, "\t\t%s: %ud\n", tmp->echocancel.params[x].name, tmp->echocancel.params[x].value);
+ }
+ ast_cli(a->fd, "\t%scurrently %s\n", tmp->echocanbridged ? "" : "(unless TDM bridged) ", tmp->echocanon ? "ON" : "OFF");
+ } else {
+ ast_cli(a->fd, "\tnone\n");
+ }
+#else
+ if (tmp->echocancel) {
+ ast_cli(a->fd, "\t%d taps\n", tmp->echocancel);
+ ast_cli(a->fd, "\t%scurrently %s\n", tmp->echocanbridged ? "" : "(unless TDM bridged) ", tmp->echocanon ? "ON" : "OFF");
+ }
+ else
+ ast_cli(a->fd, "\tnone\n");
+#endif
+ if (tmp->master)
+ ast_cli(a->fd, "Master Channel: %d\n", tmp->master->channel);
+ for (x = 0; x < MAX_SLAVES; x++) {
+ if (tmp->slaves[x])
+ ast_cli(a->fd, "Slave Channel: %d\n", tmp->slaves[x]->channel);
+ }
+#ifdef HAVE_SS7
+ if (tmp->ss7) {
+ ast_cli(a->fd, "CIC: %d\n", tmp->cic);
+ }
+#endif
+#ifdef HAVE_PRI
+ if (tmp->pri) {
+ ast_cli(a->fd, "PRI Flags: ");
+ if (tmp->resetting)
+ ast_cli(a->fd, "Resetting ");
+ if (tmp->call)
+ ast_cli(a->fd, "Call ");
+ if (tmp->bearer)
+ ast_cli(a->fd, "Bearer ");
+ ast_cli(a->fd, "\n");
+ if (tmp->logicalspan)
+ ast_cli(a->fd, "PRI Logical Span: %d\n", tmp->logicalspan);
+ else
+ ast_cli(a->fd, "PRI Logical Span: Implicit\n");
+ }
+
+#endif
+ memset(&ci, 0, sizeof(ci));
+ ps.channo = tmp->channel;
+ if (tmp->subs[SUB_REAL].zfd > -1) {
+ if (!ioctl(tmp->subs[SUB_REAL].zfd, ZT_GETCONF, &ci)) {
+ ast_cli(a->fd, "Actual Confinfo: Num/%d, Mode/0x%04x\n", ci.confno, ci.confmode);
+ }
+#ifdef ZT_GETCONFMUTE
+ if (!ioctl(tmp->subs[SUB_REAL].zfd, ZT_GETCONFMUTE, &x)) {
+ ast_cli(a->fd, "Actual Confmute: %s\n", x ? "Yes" : "No");
+ }
+#endif
+ if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &ps) < 0) {
+ ast_log(LOG_WARNING, "Failed to get parameters on channel %d\n", tmp->channel);
+ } else {
+ ast_cli(a->fd, "Hookstate (FXS only): %s\n", ps.rxisoffhook ? "Offhook" : "Onhook");
+ }
+ }
+ ast_mutex_unlock(lock);
+ return CLI_SUCCESS;
+ }
+ tmp = tmp->next;
+ }
+
+ ast_cli(a->fd, "Unable to find given channel %d\n", channel);
+ ast_mutex_unlock(lock);
+ return CLI_FAILURE;
+}
+
+static char *handle_zap_show_cadences(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, j;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap show cadences";
+ e->usage =
+ "Usage: zap show cadences\n"
+ " Shows all cadences currently defined\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ for (i = 0; i < num_cadence; i++) {
+ char output[1024];
+ char tmp[16], tmp2[64];
+ snprintf(tmp, sizeof(tmp), "r%d: ", i + 1);
+ term_color(output, tmp, COLOR_GREEN, COLOR_BLACK, sizeof(output));
+
+ for (j = 0; j < 16; j++) {
+ if (cadences[i].ringcadence[j] == 0)
+ break;
+ snprintf(tmp, sizeof(tmp), "%d", cadences[i].ringcadence[j]);
+ if (cidrings[i] * 2 - 1 == j)
+ term_color(tmp2, tmp, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp2) - 1);
+ else
+ term_color(tmp2, tmp, COLOR_GREEN, COLOR_BLACK, sizeof(tmp2) - 1);
+ if (j != 0)
+ strncat(output, ",", sizeof(output) - strlen(output) - 1);
+ strncat(output, tmp2, sizeof(output) - strlen(output) - 1);
+ }
+ ast_cli(a->fd,"%s\n",output);
+ }
+ return CLI_SUCCESS;
+}
+
+/* Based on irqmiss.c */
+static char *zap_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#ifdef ZT_SPANINFO_HAS_LINECONFIG
+ #define FORMAT "%-40.40s %-7.7s %-6d %-6d %-6d %-3.3s %-4.4s %-8.8s %s\n"
+ #define FORMAT2 "%-40.40s %-7.7s %-6.6s %-6.6s %-6.6s %-3.3s %-4.4s %-8.8s %s\n"
+#else
+ #define FORMAT "%-40.40s %-10.10s %-10d %-10d %-10d\n"
+ #define FORMAT2 "%-40.40s %-10.10s %-10.10s %-10.10s %-10.10s\n"
+#endif
+ int span;
+ int res;
+ char alarms[50];
+
+ int ctl;
+ ZT_SPANINFO s;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap show status";
+ e->usage =
+ "Usage: zap show status\n"
+ " Shows a list of Zaptel cards with status\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ ctl = open("/dev/zap/ctl", O_RDWR);
+ if (ctl < 0) {
+ ast_cli(a->fd, "No Zaptel interface found. Unable to open /dev/zap/ctl: %s\n", strerror(errno));
+ return CLI_FAILURE;
+ }
+ ast_cli(a->fd, FORMAT2, "Description", "Alarms", "IRQ", "bpviol", "CRC4"
+#ifdef ZT_SPANINFO_HAS_LINECONFIG
+ , "Framing", "Coding", "Options", "LBO"
+#endif
+ );
+
+ for (span = 1; span < ZT_MAX_SPANS; ++span) {
+ s.spanno = span;
+ res = ioctl(ctl, ZT_SPANSTAT, &s);
+ if (res) {
+ continue;
+ }
+ alarms[0] = '\0';
+ if (s.alarms > 0) {
+ if (s.alarms & ZT_ALARM_BLUE)
+ strcat(alarms, "BLU/");
+ if (s.alarms & ZT_ALARM_YELLOW)
+ strcat(alarms, "YEL/");
+ if (s.alarms & ZT_ALARM_RED)
+ strcat(alarms, "RED/");
+ if (s.alarms & ZT_ALARM_LOOPBACK)
+ strcat(alarms, "LB/");
+ if (s.alarms & ZT_ALARM_RECOVER)
+ strcat(alarms, "REC/");
+ if (s.alarms & ZT_ALARM_NOTOPEN)
+ strcat(alarms, "NOP/");
+ if (!strlen(alarms))
+ strcat(alarms, "UUU/");
+ if (strlen(alarms)) {
+ /* Strip trailing / */
+ alarms[strlen(alarms) - 1] = '\0';
+ }
+ } else {
+ if (s.numchans)
+ strcpy(alarms, "OK");
+ else
+ strcpy(alarms, "UNCONFIGURED");
+ }
+
+ ast_cli(a->fd, FORMAT, s.desc, alarms, s.irqmisses, s.bpvcount, s.crc4count
+#ifdef ZT_SPANINFO_HAS_LINECONFIG
+ , s.lineconfig & ZT_CONFIG_D4 ? "D4" :
+ s.lineconfig & ZT_CONFIG_ESF ? "ESF" :
+ s.lineconfig & ZT_CONFIG_CCS ? "CCS" :
+ "CAS"
+ , s.lineconfig & ZT_CONFIG_B8ZS ? "B8ZS" :
+ s.lineconfig & ZT_CONFIG_HDB3 ? "HDB3" :
+ s.lineconfig & ZT_CONFIG_AMI ? "AMI" :
+ "Unk"
+ , s.lineconfig & ZT_CONFIG_CRC4 ?
+ s.lineconfig & ZT_CONFIG_NOTOPEN ? "CRC4/YEL" : "CRC4" : "YEL"
+ , lbostr[s.lbo]
+#endif
+ );
+ }
+ close(ctl);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *zap_show_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int pseudo_fd = -1;
+ struct zt_versioninfo vi;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap show version";
+ e->usage =
+ "Usage: zap show version\n"
+ " Shows the Zaptel version in use\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if ((pseudo_fd = open("/dev/zap/ctl", O_RDONLY)) < 0) {
+ ast_cli(a->fd, "Failed to open control file to get version.\n");
+ return CLI_SUCCESS;
+ }
+
+ strcpy(vi.version, "Unknown");
+ strcpy(vi.echo_canceller, "Unknown");
+
+ if (ioctl(pseudo_fd, ZT_GETVERSION, &vi))
+ ast_cli(a->fd, "Failed to get version from control file.\n");
+ else
+ ast_cli(a->fd, "Zaptel Version: %s Echo Canceller: %s\n", vi.version, vi.echo_canceller);
+
+ close(pseudo_fd);
+
+ return CLI_SUCCESS;
+}
+
+#if defined(HAVE_ZAPTEL_HWGAIN)
+static char *zap_set_hwgain(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int channel;
+ int gain;
+ int tx;
+ struct zt_hwgain hwgain;
+ struct zt_pvt *tmp = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap set hwgain";
+ e->usage =
+ "Usage: zap set hwgain <rx|tx> <chan#> <gain>\n"
+ " Sets the hardware gain on a a given channel, overriding the\n"
+ " value provided at module loadtime, whether the channel is in\n"
+ " use or not. Changes take effect immediately.\n"
+ " <rx|tx> which direction do you want to change (relative to our module)\n"
+ " <chan num> is the channel number relative to the device\n"
+ " <gain> is the gain in dB (e.g. -3.5 for -3.5dB)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 6)
+ return CLI_SHOWUSAGE;
+
+ if (!strcasecmp("rx", a->argv[3]))
+ tx = 0; /* rx */
+ else if (!strcasecmp("tx", a->argv[3]))
+ tx = 1; /* tx */
+ else
+ return CLI_SHOWUSAGE;
+
+ channel = atoi(a->argv[4]);
+ gain = atof(a->argv[5])*10.0;
+
+ ast_mutex_lock(&iflock);
+
+ for (tmp = iflist; tmp; tmp = tmp->next) {
+
+ if (tmp->channel != channel)
+ continue;
+
+ if (tmp->subs[SUB_REAL].zfd == -1)
+ break;
+
+ hwgain.newgain = gain;
+ hwgain.tx = tx;
+ if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_SET_HWGAIN, &hwgain) < 0) {
+ ast_cli(a->fd, "Unable to set the hardware gain for channel %d\n", channel);
+ ast_mutex_unlock(&iflock);
+ return CLI_FAILURE;
+ }
+ ast_cli(a->fd, "hardware %s gain set to %d (%.1f dB) on channel %d\n",
+ tx ? "tx" : "rx", gain, (float)gain/10.0, channel);
+ break;
+ }
+
+ ast_mutex_unlock(&iflock);
+
+ if (tmp)
+ return CLI_SUCCESS;
+
+ ast_cli(a->fd, "Unable to find given channel %d\n", channel);
+ return CLI_FAILURE;
+
+}
+#endif
+
+static char *zap_set_swgain(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int channel;
+ float gain;
+ int tx;
+ int res;
+ ast_mutex_t *lock;
+ struct zt_pvt *tmp = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap set swgain";
+ e->usage =
+ "Usage: zap set swgain <rx|tx> <chan#> <gain>\n"
+ " Sets the software gain on a a given channel, overriding the\n"
+ " value provided at module loadtime, whether the channel is in\n"
+ " use or not. Changes take effect immediately.\n"
+ " <rx|tx> which direction do you want to change (relative to our module)\n"
+ " <chan num> is the channel number relative to the device\n"
+ " <gain> is the gain in dB (e.g. -3.5 for -3.5dB)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ lock = &iflock;
+
+ if (a->argc != 6)
+ return CLI_SHOWUSAGE;
+
+ if (!strcasecmp("rx", a->argv[3]))
+ tx = 0; /* rx */
+ else if (!strcasecmp("tx", a->argv[3]))
+ tx = 1; /* tx */
+ else
+ return CLI_SHOWUSAGE;
+
+ channel = atoi(a->argv[4]);
+ gain = atof(a->argv[5]);
+
+ ast_mutex_lock(lock);
+ for (tmp = iflist; tmp; tmp = tmp->next) {
+
+ if (tmp->channel != channel)
+ continue;
+
+ if (tmp->subs[SUB_REAL].zfd == -1)
+ break;
+
+ if (tx)
+ res = set_actual_txgain(tmp->subs[SUB_REAL].zfd, channel, gain, tmp->law);
+ else
+ res = set_actual_rxgain(tmp->subs[SUB_REAL].zfd, channel, gain, tmp->law);
+
+ if (res) {
+ ast_cli(a->fd, "Unable to set the software gain for channel %d\n", channel);
+ ast_mutex_unlock(lock);
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "software %s gain set to %.1f on channel %d\n",
+ tx ? "tx" : "rx", gain, channel);
+ break;
+ }
+ ast_mutex_unlock(lock);
+
+ if (tmp)
+ return CLI_SUCCESS;
+
+ ast_cli(a->fd, "Unable to find given channel %d\n", channel);
+ return CLI_FAILURE;
+
+}
+
+static char *zap_set_dnd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int channel;
+ int on;
+ struct zt_pvt *zt_chan = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "zap set dnd";
+ e->usage =
+ "Usage: zap set dnd <chan#> <on|off>\n"
+ " Sets/resets DND (Do Not Disturb) mode on a channel.\n"
+ " Changes take effect immediately.\n"
+ " <chan num> is the channel number\n"
+ " <on|off> Enable or disable DND mode?\n"
+ ;
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+
+ if ((channel = atoi(a->argv[3])) <= 0) {
+ ast_cli(a->fd, "Expected channel number, got '%s'\n", a->argv[3]);
+ return CLI_SHOWUSAGE;
+ }
+
+ if (ast_true(a->argv[4]))
+ on = 1;
+ else if (ast_false(a->argv[4]))
+ on = 0;
+ else {
+ ast_cli(a->fd, "Expected 'on' or 'off', got '%s'\n", a->argv[4]);
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_mutex_lock(&iflock);
+ for (zt_chan = iflist; zt_chan; zt_chan = zt_chan->next) {
+ if (zt_chan->channel != channel)
+ continue;
+
+ /* Found the channel. Actually set it */
+ zap_dnd(zt_chan, on);
+ break;
+ }
+ ast_mutex_unlock(&iflock);
+
+ if (!zt_chan) {
+ ast_cli(a->fd, "Unable to find given channel %d\n", channel);
+ return CLI_FAILURE;
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry zap_cli[] = {
+ AST_CLI_DEFINE(handle_zap_show_cadences, "List cadences"),
+ AST_CLI_DEFINE(zap_show_channels, "Show active zapata channels"),
+ AST_CLI_DEFINE(zap_show_channel, "Show information on a channel"),
+ AST_CLI_DEFINE(zap_destroy_channel, "Destroy a channel"),
+ AST_CLI_DEFINE(zap_restart_cmd, "Fully restart Zaptel channels"),
+ AST_CLI_DEFINE(zap_show_status, "Show all Zaptel cards status"),
+ AST_CLI_DEFINE(zap_show_version, "Show the Zaptel version in use"),
+#if defined(HAVE_ZAPTEL_HWGAIN)
+ AST_CLI_DEFINE(zap_set_hwgain, "Set hardware gain on a channel"),
+#endif
+ AST_CLI_DEFINE(zap_set_swgain, "Set software gain on a channel"),
+ AST_CLI_DEFINE(zap_set_dnd, "Set software gain on a channel"),
+};
+
+#define TRANSFER 0
+#define HANGUP 1
+
+static int zap_fake_event(struct zt_pvt *p, int mode)
+{
+ if (p) {
+ switch (mode) {
+ case TRANSFER:
+ p->fake_event = ZT_EVENT_WINKFLASH;
+ break;
+ case HANGUP:
+ p->fake_event = ZT_EVENT_ONHOOK;
+ break;
+ default:
+ ast_log(LOG_WARNING, "I don't know how to handle transfer event with this: %d on channel %s\n",mode, p->owner->name);
+ }
+ }
+ return 0;
+}
+static struct zt_pvt *find_channel(int channel)
+{
+ struct zt_pvt *p = iflist;
+ while (p) {
+ if (p->channel == channel) {
+ break;
+ }
+ p = p->next;
+ }
+ return p;
+}
+
+static int action_zapdndon(struct mansession *s, const struct message *m)
+{
+ struct zt_pvt *p = NULL;
+ const char *channel = astman_get_header(m, "ZapChannel");
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ p = find_channel(atoi(channel));
+ if (!p) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ p->dnd = 1;
+ astman_send_ack(s, m, "DND Enabled");
+ return 0;
+}
+
+static int action_zapdndoff(struct mansession *s, const struct message *m)
+{
+ struct zt_pvt *p = NULL;
+ const char *channel = astman_get_header(m, "ZapChannel");
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ p = find_channel(atoi(channel));
+ if (!p) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ p->dnd = 0;
+ astman_send_ack(s, m, "DND Disabled");
+ return 0;
+}
+
+static int action_transfer(struct mansession *s, const struct message *m)
+{
+ struct zt_pvt *p = NULL;
+ const char *channel = astman_get_header(m, "ZapChannel");
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ p = find_channel(atoi(channel));
+ if (!p) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ zap_fake_event(p,TRANSFER);
+ astman_send_ack(s, m, "ZapTransfer");
+ return 0;
+}
+
+static int action_transferhangup(struct mansession *s, const struct message *m)
+{
+ struct zt_pvt *p = NULL;
+ const char *channel = astman_get_header(m, "ZapChannel");
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ p = find_channel(atoi(channel));
+ if (!p) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ zap_fake_event(p,HANGUP);
+ astman_send_ack(s, m, "ZapHangup");
+ return 0;
+}
+
+static int action_zapdialoffhook(struct mansession *s, const struct message *m)
+{
+ struct zt_pvt *p = NULL;
+ const char *channel = astman_get_header(m, "ZapChannel");
+ const char *number = astman_get_header(m, "Number");
+ int i;
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ if (ast_strlen_zero(number)) {
+ astman_send_error(s, m, "No number specified");
+ return 0;
+ }
+ p = find_channel(atoi(channel));
+ if (!p) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ if (!p->owner) {
+ astman_send_error(s, m, "Channel does not have it's owner");
+ return 0;
+ }
+ for (i = 0; i < strlen(number); i++) {
+ struct ast_frame f = { AST_FRAME_DTMF, number[i] };
+ zap_queue_frame(p, &f, NULL);
+ }
+ astman_send_ack(s, m, "ZapDialOffhook");
+ return 0;
+}
+
+static int action_zapshowchannels(struct mansession *s, const struct message *m)
+{
+ struct zt_pvt *tmp = NULL;
+ const char *id = astman_get_header(m, "ActionID");
+ const char *zapchannel = astman_get_header(m, "ZapChannel");
+ char idText[256] = "";
+ int channels = 0;
+ int zapchanquery = -1;
+ if (!ast_strlen_zero(zapchannel)) {
+ zapchanquery = atoi(zapchannel);
+ }
+
+ astman_send_ack(s, m, "Zapata channel status will follow");
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText) - 1, "ActionID: %s\r\n", id);
+
+ ast_mutex_lock(&iflock);
+
+ tmp = iflist;
+ while (tmp) {
+ if (tmp->channel > 0) {
+ int alarm = get_alarms(tmp);
+
+ /* If a specific channel is queried for, only deliver status for that channel */
+ if (zapchanquery > 0 && tmp->channel != zapchanquery)
+ continue;
+
+ channels++;
+ if (tmp->owner) {
+ /* Add data if we have a current call */
+ astman_append(s,
+ "Event: ZapShowChannels\r\n"
+ "ZapChannel: %d\r\n"
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "AccountCode: %s\r\n"
+ "Signalling: %s\r\n"
+ "SignallingCode: %d\r\n"
+ "Context: %s\r\n"
+ "DND: %s\r\n"
+ "Alarm: %s\r\n"
+ "%s"
+ "\r\n",
+ tmp->channel,
+ tmp->owner->name,
+ tmp->owner->uniqueid,
+ tmp->owner->accountcode,
+ sig2str(tmp->sig),
+ tmp->sig,
+ tmp->context,
+ tmp->dnd ? "Enabled" : "Disabled",
+ alarm2str(alarm), idText);
+ } else {
+ astman_append(s,
+ "Event: ZapShowChannels\r\n"
+ "ZapChannel: %d\r\n"
+ "Signalling: %s\r\n"
+ "SignallingCode: %d\r\n"
+ "Context: %s\r\n"
+ "DND: %s\r\n"
+ "Alarm: %s\r\n"
+ "%s"
+ "\r\n",
+ tmp->channel, sig2str(tmp->sig), tmp->sig,
+ tmp->context,
+ tmp->dnd ? "Enabled" : "Disabled",
+ alarm2str(alarm), idText);
+ }
+ }
+
+ tmp = tmp->next;
+ }
+
+ ast_mutex_unlock(&iflock);
+
+ astman_append(s,
+ "Event: ZapShowChannelsComplete\r\n"
+ "%s"
+ "Items: %d\r\n"
+ "\r\n",
+ idText,
+ channels);
+ return 0;
+}
+
+static int __unload_module(void)
+{
+ int x;
+ struct zt_pvt *p, *pl;
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ int i;
+#endif
+
+#if defined(HAVE_PRI)
+ for (i = 0; i < NUM_SPANS; i++) {
+ if (pris[i].master != AST_PTHREADT_NULL)
+ pthread_cancel(pris[i].master);
+ }
+ ast_cli_unregister_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry));
+ ast_unregister_application(zap_send_keypad_facility_app);
+#endif
+
+ ast_cli_unregister_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
+ ast_manager_unregister( "ZapDialOffhook" );
+ ast_manager_unregister( "ZapHangup" );
+ ast_manager_unregister( "ZapTransfer" );
+ ast_manager_unregister( "ZapDNDoff" );
+ ast_manager_unregister( "ZapDNDon" );
+ ast_manager_unregister("ZapShowChannels");
+ ast_manager_unregister("ZapRestart");
+ ast_channel_unregister(&zap_tech);
+ ast_mutex_lock(&iflock);
+ /* Hangup all interfaces if they have an owner */
+ p = iflist;
+ while (p) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ p = p->next;
+ }
+ ast_mutex_unlock(&iflock);
+ ast_mutex_lock(&monlock);
+ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
+ pthread_cancel(monitor_thread);
+ pthread_kill(monitor_thread, SIGURG);
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+
+ ast_mutex_lock(&iflock);
+ /* Destroy all the interfaces and free their memory */
+ p = iflist;
+ while (p) {
+ /* Free any callerid */
+ if (p->cidspill)
+ ast_free(p->cidspill);
+ /* Close the zapata thingy */
+ if (p->subs[SUB_REAL].zfd > -1)
+ zt_close(p->subs[SUB_REAL].zfd);
+ pl = p;
+ p = p->next;
+ x = pl->channel;
+ /* Free associated memory */
+ if (pl)
+ destroy_zt_pvt(&pl);
+ ast_verb(3, "Unregistered channel %d\n", x);
+ }
+ iflist = NULL;
+ ifcount = 0;
+ ast_mutex_unlock(&iflock);
+
+#if defined(HAVE_PRI)
+ for (i = 0; i < NUM_SPANS; i++) {
+ if (pris[i].master && (pris[i].master != AST_PTHREADT_NULL))
+ pthread_join(pris[i].master, NULL);
+ zt_close(pris[i].fds[i]);
+ }
+#endif /* HAVE_PRI */
+
+#if defined(HAVE_SS7)
+ for (i = 0; i < NUM_SPANS; i++) {
+ if (linksets[i].master && (linksets[i].master != AST_PTHREADT_NULL))
+ pthread_join(linksets[i].master, NULL);
+ zt_close(linksets[i].fds[i]);
+ }
+#endif /* HAVE_SS7 */
+
+ return 0;
+}
+
+#ifdef HAVE_SS7
+static int linkset_addsigchan(int sigchan)
+{
+ struct zt_ss7 *link;
+ int res;
+ int curfd;
+ ZT_PARAMS p;
+ ZT_BUFFERINFO bi;
+ struct zt_spaninfo si;
+
+
+ link = ss7_resolve_linkset(cur_linkset);
+ if (!link) {
+ ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1);
+ return -1;
+ }
+
+ if (cur_ss7type < 0) {
+ ast_log(LOG_ERROR, "Unspecified or invalid ss7type\n");
+ return -1;
+ }
+
+ if (!link->ss7)
+ link->ss7 = ss7_new(cur_ss7type);
+
+ if (!link->ss7) {
+ ast_log(LOG_ERROR, "Can't create new SS7!\n");
+ return -1;
+ }
+
+ link->type = cur_ss7type;
+
+ if (cur_pointcode < 0) {
+ ast_log(LOG_ERROR, "Unspecified pointcode!\n");
+ return -1;
+ } else
+ ss7_set_pc(link->ss7, cur_pointcode);
+
+ if (sigchan < 0) {
+ ast_log(LOG_ERROR, "Invalid sigchan!\n");
+ return -1;
+ } else {
+ if (link->numsigchans >= NUM_DCHANS) {
+ ast_log(LOG_ERROR, "Too many sigchans on linkset %d\n", cur_linkset);
+ return -1;
+ }
+ curfd = link->numsigchans;
+
+ link->fds[curfd] = open("/dev/zap/channel", O_RDWR, 0600);
+ if ((link->fds[curfd] < 0) || (ioctl(link->fds[curfd],ZT_SPECIFY,&sigchan) == -1)) {
+ ast_log(LOG_ERROR, "Unable to open SS7 sigchan %d (%s)\n", sigchan, strerror(errno));
+ return -1;
+ }
+ res = ioctl(link->fds[curfd], ZT_GET_PARAMS, &p);
+ if (res) {
+ zt_close(link->fds[curfd]);
+ link->fds[curfd] = -1;
+ ast_log(LOG_ERROR, "Unable to get parameters for sigchan %d (%s)\n", sigchan, strerror(errno));
+ return -1;
+ }
+ if ((p.sigtype != ZT_SIG_HDLCFCS) && (p.sigtype != ZT_SIG_HARDHDLC)) {
+ zt_close(link->fds[curfd]);
+ link->fds[curfd] = -1;
+ ast_log(LOG_ERROR, "sigchan %d is not in HDLC/FCS mode. See /etc/zaptel.conf\n", sigchan);
+ return -1;
+ }
+
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = 32;
+ bi.bufsize = 512;
+
+ if (ioctl(link->fds[curfd], ZT_SET_BUFINFO, &bi)) {
+ ast_log(LOG_ERROR, "Unable to set appropriate buffering on channel %d\n", sigchan);
+ zt_close(link->fds[curfd]);
+ link->fds[curfd] = -1;
+ return -1;
+ }
+
+ ss7_add_link(link->ss7, SS7_TRANSPORT_ZAP, link->fds[curfd]);
+ link->numsigchans++;
+
+ memset(&si, 0, sizeof(si));
+ res = ioctl(link->fds[curfd], ZT_SPANSTAT, &si);
+ if (res) {
+ zt_close(link->fds[curfd]);
+ link->fds[curfd] = -1;
+ ast_log(LOG_ERROR, "Unable to get span state for sigchan %d (%s)\n", sigchan, strerror(errno));
+ }
+
+ if (!si.alarms) {
+ link->linkstate[curfd] = LINKSTATE_DOWN;
+ ss7_link_noalarm(link->ss7, link->fds[curfd]);
+ } else {
+ link->linkstate[curfd] = LINKSTATE_DOWN | LINKSTATE_INALARM;
+ ss7_link_alarm(link->ss7, link->fds[curfd]);
+ }
+ }
+
+ if (cur_adjpointcode < 0) {
+ ast_log(LOG_ERROR, "Unspecified adjpointcode!\n");
+ return -1;
+ } else {
+ ss7_set_adjpc(link->ss7, link->fds[curfd], cur_adjpointcode);
+ }
+
+ if (cur_defaultdpc < 0) {
+ ast_log(LOG_ERROR, "Unspecified defaultdpc!\n");
+ return -1;
+ }
+
+ if (cur_networkindicator < 0) {
+ ast_log(LOG_ERROR, "Invalid networkindicator!\n");
+ return -1;
+ } else
+ ss7_set_network_ind(link->ss7, cur_networkindicator);
+
+ return 0;
+}
+
+static char *handle_ss7_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int span;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ss7 no debug linkset";
+ e->usage =
+ "Usage: ss7 no debug linkset <span>\n"
+ " Disables debugging on a given SS7 linkset\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 5)
+ return CLI_SHOWUSAGE;
+ span = atoi(a->argv[4]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid linkset %s. Should be a number from %d to %d\n", a->argv[4], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+ if (!linksets[span-1].ss7) {
+ ast_cli(a->fd, "No SS7 running on linkset %d\n", span);
+ return CLI_SUCCESS;
+ }
+ if (linksets[span-1].ss7)
+ ss7_set_debug(linksets[span-1].ss7, 0);
+
+ ast_cli(a->fd, "Disabled debugging on linkset %d\n", span);
+ return CLI_SUCCESS;
+}
+
+static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int span;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ss7 debug linkset";
+ e->usage =
+ "Usage: ss7 debug linkset <linkset>\n"
+ " Enables debugging on a given SS7 linkset\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+ span = atoi(a->argv[3]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid linkset %s. Should be a number from %d to %d\n", a->argv[3], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+ if (!linksets[span-1].ss7) {
+ ast_cli(a->fd, "No SS7 running on linkset %d\n", span);
+ return CLI_SUCCESS;
+ }
+ if (linksets[span-1].ss7)
+ ss7_set_debug(linksets[span-1].ss7, SS7_DEBUG_MTP2 | SS7_DEBUG_MTP3 | SS7_DEBUG_ISUP);
+
+ ast_cli(a->fd, "Enabled debugging on linkset %d\n", span);
+ return CLI_SUCCESS;
+}
+
+static char *handle_ss7_block_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int linkset, cic;
+ int blocked = -1, i;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ss7 block cic";
+ e->usage =
+ "Usage: ss7 block cic <linkset> <CIC>\n"
+ " Sends a remote blocking request for the given CIC on the specified linkset\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc == 5)
+ linkset = atoi(a->argv[3]);
+ else
+ return CLI_SHOWUSAGE;
+
+ if ((linkset < 1) || (linkset > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+
+ if (!linksets[linkset-1].ss7) {
+ ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+ return CLI_SUCCESS;
+ }
+
+ cic = atoi(a->argv[4]);
+
+ if (cic < 1) {
+ ast_cli(a->fd, "Invalid CIC specified!\n");
+ return CLI_SUCCESS;
+ }
+
+ for (i = 0; i < linksets[linkset-1].numchans; i++) {
+ if (linksets[linkset-1].pvts[i]->cic == cic) {
+ blocked = linksets[linkset-1].pvts[i]->locallyblocked;
+ if (!blocked) {
+ ast_mutex_lock(&linksets[linkset-1].lock);
+ isup_blo(linksets[linkset-1].ss7, cic, linksets[linkset-1].pvts[i]->dpc);
+ ast_mutex_unlock(&linksets[linkset-1].lock);
+ }
+ }
+ }
+
+ if (blocked < 0) {
+ ast_cli(a->fd, "Invalid CIC specified!\n");
+ return CLI_SUCCESS;
+ }
+
+ if (!blocked)
+ ast_cli(a->fd, "Sent blocking request for linkset %d on CIC %d\n", linkset, cic);
+ else
+ ast_cli(a->fd, "CIC %d already locally blocked\n", cic);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_ss7_unblock_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int linkset, cic;
+ int i, blocked = -1;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ss7 unblock cic";
+ e->usage =
+ "Usage: ss7 unblock cic <linkset> <CIC>\n"
+ " Sends a remote unblocking request for the given CIC on the specified linkset\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 5)
+ linkset = atoi(a->argv[3]);
+ else
+ return CLI_SHOWUSAGE;
+
+ if ((linkset < 1) || (linkset > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+
+ if (!linksets[linkset-1].ss7) {
+ ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+ return CLI_SUCCESS;
+ }
+
+ cic = atoi(a->argv[4]);
+
+ if (cic < 1) {
+ ast_cli(a->fd, "Invalid CIC specified!\n");
+ return CLI_SUCCESS;
+ }
+
+ for (i = 0; i < linksets[linkset-1].numchans; i++) {
+ if (linksets[linkset-1].pvts[i]->cic == cic) {
+ blocked = linksets[linkset-1].pvts[i]->locallyblocked;
+ if (blocked) {
+ ast_mutex_lock(&linksets[linkset-1].lock);
+ isup_ubl(linksets[linkset-1].ss7, cic, linksets[linkset-1].pvts[i]->dpc);
+ ast_mutex_unlock(&linksets[linkset-1].lock);
+ }
+ }
+ }
+
+ if (blocked > 0)
+ ast_cli(a->fd, "Sent unblocking request for linkset %d on CIC %d\n", linkset, cic);
+ return CLI_SUCCESS;
+}
+
+static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int linkset;
+ struct zt_ss7 *ss7;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ss7 show linkset";
+ e->usage =
+ "Usage: ss7 show linkset <span>\n"
+ " Shows the status of an SS7 linkset.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+ linkset = atoi(a->argv[3]);
+ if ((linkset < 1) || (linkset > NUM_SPANS)) {
+ ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+ return CLI_SUCCESS;
+ }
+ if (!linksets[linkset-1].ss7) {
+ ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+ return CLI_SUCCESS;
+ }
+ if (linksets[linkset-1].ss7)
+ ss7 = &linksets[linkset-1];
+
+ ast_cli(a->fd, "SS7 linkset %d status: %s\n", linkset, (ss7->state == LINKSET_STATE_UP) ? "Up" : "Down");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry zap_ss7_cli[] = {
+ AST_CLI_DEFINE(handle_ss7_debug, "Enables SS7 debugging on a linkset"),
+ AST_CLI_DEFINE(handle_ss7_no_debug, "Disables SS7 debugging on a linkset"),
+ AST_CLI_DEFINE(handle_ss7_block_cic, "Disables SS7 debugging on a linkset"),
+ AST_CLI_DEFINE(handle_ss7_unblock_cic, "Disables SS7 debugging on a linkset"),
+ AST_CLI_DEFINE(handle_ss7_show_linkset, "Shows the status of a linkset"),
+};
+#endif /* HAVE_SS7 */
+
+static int unload_module(void)
+{
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ int y;
+#endif
+#ifdef HAVE_PRI
+ for (y = 0; y < NUM_SPANS; y++)
+ ast_mutex_destroy(&pris[y].lock);
+#endif
+#ifdef HAVE_SS7
+ for (y = 0; y < NUM_SPANS; y++)
+ ast_mutex_destroy(&linksets[y].lock);
+#endif /* HAVE_SS7 */
+ return __unload_module();
+}
+
+static int build_channels(struct zt_chan_conf conf, int iscrv, const char *value, int reload, int lineno, int *found_pseudo)
+{
+ char *c, *chan;
+ int x, start, finish;
+ struct zt_pvt *tmp;
+#ifdef HAVE_PRI
+ struct zt_pri *pri;
+ int trunkgroup, y;
+#endif
+
+ if ((reload == 0) && (conf.chan.sig < 0) && !conf.is_sig_auto) {
+ ast_log(LOG_ERROR, "Signalling must be specified before any channels are.\n");
+ return -1;
+ }
+
+ c = ast_strdupa(value);
+
+#ifdef HAVE_PRI
+ pri = NULL;
+ if (iscrv) {
+ if (sscanf(c, "%d:%n", &trunkgroup, &y) != 1) {
+ ast_log(LOG_WARNING, "CRV must begin with trunkgroup followed by a colon at line %d\n", lineno);
+ return -1;
+ }
+ if (trunkgroup < 1) {
+ ast_log(LOG_WARNING, "CRV trunk group must be a positive number at line %d\n", lineno);
+ return -1;
+ }
+ c += y;
+ for (y = 0; y < NUM_SPANS; y++) {
+ if (pris[y].trunkgroup == trunkgroup) {
+ pri = pris + y;
+ break;
+ }
+ }
+ if (!pri) {
+ ast_log(LOG_WARNING, "No such trunk group %d at CRV declaration at line %d\n", trunkgroup, lineno);
+ return -1;
+ }
+ }
+#endif
+
+ while ((chan = strsep(&c, ","))) {
+ if (sscanf(chan, "%d-%d", &start, &finish) == 2) {
+ /* Range */
+ } else if (sscanf(chan, "%d", &start)) {
+ /* Just one */
+ finish = start;
+ } else if (!strcasecmp(chan, "pseudo")) {
+ finish = start = CHAN_PSEUDO;
+ if (found_pseudo)
+ *found_pseudo = 1;
+ } else {
+ ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'\n", value, chan);
+ return -1;
+ }
+ if (finish < start) {
+ ast_log(LOG_WARNING, "Sillyness: %d < %d\n", start, finish);
+ x = finish;
+ finish = start;
+ start = x;
+ }
+
+ for (x = start; x <= finish; x++) {
+#ifdef HAVE_PRI
+ tmp = mkintf(x, conf, pri, reload);
+#else
+ tmp = mkintf(x, conf, NULL, reload);
+#endif
+
+ if (tmp) {
+#ifdef HAVE_PRI
+ if (pri)
+ ast_verb(3, "%s CRV %d:%d, %s signalling\n", reload ? "Reconfigured" : "Registered", trunkgroup, x, sig2str(tmp->sig));
+ else
+#endif
+ ast_verb(3, "%s channel %d, %s signalling\n", reload ? "Reconfigured" : "Registered", x, sig2str(tmp->sig));
+ } else {
+ ast_log(LOG_ERROR, "Unable to %s channel '%s'\n",
+ (reload == 1) ? "reconfigure" : "register", value);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** The length of the parameters list of 'zapchan'.
+ * \todo Move definition of MAX_CHANLIST_LEN to a proper place. */
+#define MAX_CHANLIST_LEN 80
+
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+static void process_echocancel(struct zt_chan_conf *confp, const char *data, unsigned int line)
+{
+ char *parse = ast_strdupa(data);
+ char *params[ZT_MAX_ECHOCANPARAMS + 1];
+ unsigned int param_count;
+ unsigned int x;
+
+ if (!(param_count = ast_app_separate_args(parse, ',', params, sizeof(params) / sizeof(params[0]))))
+ return;
+
+ memset(&confp->chan.echocancel, 0, sizeof(confp->chan.echocancel));
+
+ /* first parameter is tap length, process it here */
+
+ x = ast_strlen_zero(params[0]) ? 0 : atoi(params[0]);
+
+ if ((x == 32) || (x == 64) || (x == 128) || (x == 256) || (x == 512) || (x == 1024))
+ confp->chan.echocancel.head.tap_length = x;
+ else if ((confp->chan.echocancel.head.tap_length = ast_true(params[0])))
+ confp->chan.echocancel.head.tap_length = 128;
+
+ /* now process any remaining parameters */
+
+ for (x = 1; x < param_count; x++) {
+ struct {
+ char *name;
+ char *value;
+ } param;
+
+ if (ast_app_separate_args(params[x], '=', (char **) &param, 2) < 1) {
+ ast_log(LOG_WARNING, "Invalid echocancel parameter supplied at line %d: '%s'\n", line, params[x]);
+ continue;
+ }
+
+ if (ast_strlen_zero(param.name) || (strlen(param.name) > sizeof(confp->chan.echocancel.params[0].name)-1)) {
+ ast_log(LOG_WARNING, "Invalid echocancel parameter supplied at line %d: '%s'\n", line, param.name);
+ continue;
+ }
+
+ strcpy(confp->chan.echocancel.params[confp->chan.echocancel.head.param_count].name, param.name);
+
+ if (param.value) {
+ if (sscanf(param.value, "%ud", &confp->chan.echocancel.params[confp->chan.echocancel.head.param_count].value) != 1) {
+ ast_log(LOG_WARNING, "Invalid echocancel parameter value supplied at line %d: '%s'\n", line, param.value);
+ continue;
+ }
+ }
+ confp->chan.echocancel.head.param_count++;
+ }
+}
+#endif /* defined(HAVE_ZAPTEL_ECHOCANPARAMS) */
+
+static int process_zap(struct zt_chan_conf *confp, struct ast_variable *v, int reload, int skipchannels)
+{
+ struct zt_pvt *tmp;
+ const char *ringc; /* temporary string for parsing the dring number. */
+ int y;
+ int found_pseudo = 0;
+ char zapchan[MAX_CHANLIST_LEN] = {};
+
+ for (; v; v = v->next) {
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+ continue;
+
+ /* Create the interface list */
+ if (!strcasecmp(v->name, "channel")
+#ifdef HAVE_PRI
+ || !strcasecmp(v->name, "crv")
+#endif
+ ) {
+ int iscrv;
+ if (skipchannels)
+ continue;
+ iscrv = !strcasecmp(v->name, "crv");
+ if (build_channels(*confp, iscrv, v->value, reload, v->lineno, &found_pseudo))
+ return -1;
+ } else if (!strcasecmp(v->name, "zapchan")) {
+ ast_copy_string(zapchan, v->value, sizeof(zapchan));
+ } else if (!strcasecmp(v->name, "usedistinctiveringdetection")) {
+ usedistinctiveringdetection = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "distinctiveringaftercid")) {
+ distinctiveringaftercid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "dring1context")) {
+ ast_copy_string(confp->chan.drings.ringContext[0].contextData,v->value,sizeof(confp->chan.drings.ringContext[0].contextData));
+ } else if (!strcasecmp(v->name, "dring2context")) {
+ ast_copy_string(confp->chan.drings.ringContext[1].contextData,v->value,sizeof(confp->chan.drings.ringContext[1].contextData));
+ } else if (!strcasecmp(v->name, "dring3context")) {
+ ast_copy_string(confp->chan.drings.ringContext[2].contextData,v->value,sizeof(confp->chan.drings.ringContext[2].contextData));
+ } else if (!strcasecmp(v->name, "dring1range")) {
+ drings.ringnum[0].range = atoi(v->value);
+ /* 10 is a nice default. */
+ if (confp->chan.drings.ringnum[0].range == 0)
+ confp->chan.drings.ringnum[0].range = 10;
+ } else if (!strcasecmp(v->name, "dring2range")) {
+ confp->chan.drings.ringnum[1].range = atoi(v->value);
+ /* 10 is a nice default. */
+ if (confp->chan.drings.ringnum[1].range == 0)
+ confp->chan.drings.ringnum[1].range = 10;
+ } else if (!strcasecmp(v->name, "dring3range")) {
+ confp->chan.drings.ringnum[2].range = atoi(v->value);
+ /* 10 is a nice default. */
+ if (confp->chan.drings.ringnum[2].range == 0)
+ confp->chan.drings.ringnum[2].range = 10;
+ } else if (!strcasecmp(v->name, "dring1")) {
+ ringc = v->value;
+ sscanf(ringc, "%d,%d,%d", &confp->chan.drings.ringnum[0].ring[0], &confp->chan.drings.ringnum[0].ring[1], &confp->chan.drings.ringnum[0].ring[2]);
+ } else if (!strcasecmp(v->name, "dring2")) {
+ ringc = v->value;
+ sscanf(ringc,"%d,%d,%d", &confp->chan.drings.ringnum[1].ring[0], &confp->chan.drings.ringnum[1].ring[1], &confp->chan.drings.ringnum[1].ring[2]);
+ } else if (!strcasecmp(v->name, "dring3")) {
+ ringc = v->value;
+ sscanf(ringc, "%d,%d,%d", &confp->chan.drings.ringnum[2].ring[0], &confp->chan.drings.ringnum[2].ring[1], &confp->chan.drings.ringnum[2].ring[2]);
+ } else if (!strcasecmp(v->name, "usecallerid")) {
+ confp->chan.use_callerid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "cidsignalling")) {
+ if (!strcasecmp(v->value, "bell"))
+ confp->chan.cid_signalling = CID_SIG_BELL;
+ else if (!strcasecmp(v->value, "v23"))
+ confp->chan.cid_signalling = CID_SIG_V23;
+ else if (!strcasecmp(v->value, "dtmf"))
+ confp->chan.cid_signalling = CID_SIG_DTMF;
+ else if (!strcasecmp(v->value, "smdi"))
+ confp->chan.cid_signalling = CID_SIG_SMDI;
+ else if (!strcasecmp(v->value, "v23_jp"))
+ confp->chan.cid_signalling = CID_SIG_V23_JP;
+ else if (ast_true(v->value))
+ confp->chan.cid_signalling = CID_SIG_BELL;
+ } else if (!strcasecmp(v->name, "cidstart")) {
+ if (!strcasecmp(v->value, "ring"))
+ confp->chan.cid_start = CID_START_RING;
+ else if (!strcasecmp(v->value, "polarity_in"))
+ confp->chan.cid_start = CID_START_POLARITY_IN;
+ else if (!strcasecmp(v->value, "polarity"))
+ confp->chan.cid_start = CID_START_POLARITY;
+ else if (ast_true(v->value))
+ confp->chan.cid_start = CID_START_RING;
+ } else if (!strcasecmp(v->name, "threewaycalling")) {
+ confp->chan.threewaycalling = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "cancallforward")) {
+ confp->chan.cancallforward = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "relaxdtmf")) {
+ if (ast_true(v->value))
+ confp->chan.dtmfrelax = DSP_DIGITMODE_RELAXDTMF;
+ else
+ confp->chan.dtmfrelax = 0;
+ } else if (!strcasecmp(v->name, "mailbox")) {
+ ast_copy_string(confp->chan.mailbox, v->value, sizeof(confp->chan.mailbox));
+ } else if (!strcasecmp(v->name, "adsi")) {
+ confp->chan.adsi = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "usesmdi")) {
+ confp->chan.use_smdi = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "smdiport")) {
+ ast_copy_string(confp->smdi_port, v->value, sizeof(confp->smdi_port));
+ } else if (!strcasecmp(v->name, "transfer")) {
+ confp->chan.transfer = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "canpark")) {
+ confp->chan.canpark = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "echocancelwhenbridged")) {
+ confp->chan.echocanbridged = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "busydetect")) {
+ confp->chan.busydetect = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "busycount")) {
+ confp->chan.busycount = atoi(v->value);
+ } else if (!strcasecmp(v->name, "busypattern")) {
+ if (sscanf(v->value, "%d,%d", &confp->chan.busy_tonelength, &confp->chan.busy_quietlength) != 2) {
+ ast_log(LOG_ERROR, "busypattern= expects busypattern=tonelength,quietlength\n");
+ }
+ } else if (!strcasecmp(v->name, "callprogress")) {
+ confp->chan.callprogress &= ~CALLPROGRESS_PROGRESS;
+ if (ast_true(v->value))
+ confp->chan.callprogress |= CALLPROGRESS_PROGRESS;
+ } else if (!strcasecmp(v->name, "faxdetect")) {
+ confp->chan.callprogress &= ~CALLPROGRESS_FAX;
+ if (!strcasecmp(v->value, "incoming")) {
+ confp->chan.callprogress |= CALLPROGRESS_FAX_INCOMING;
+ } else if (!strcasecmp(v->value, "outgoing")) {
+ confp->chan.callprogress |= CALLPROGRESS_FAX_OUTGOING;
+ } else if (!strcasecmp(v->value, "both") || ast_true(v->value))
+ confp->chan.callprogress |= CALLPROGRESS_FAX_INCOMING | CALLPROGRESS_FAX_OUTGOING;
+ } else if (!strcasecmp(v->name, "echocancel")) {
+#if defined(HAVE_ZAPTEL_ECHOCANPARAMS)
+ process_echocancel(confp, v->value, v->lineno);
+#else
+ y = ast_strlen_zero(v->value) ? 0 : atoi(v->value);
+
+ if ((y == 32) || (y == 64) || (y == 128) || (y == 256) || (y == 512) || (y == 1024))
+ confp->chan.echocancel = y;
+ else if ((confp->chan.echocancel = ast_true(v->value)))
+ confp->chan.echocancel = 128;
+#endif
+ } else if (!strcasecmp(v->name, "echotraining")) {
+ if (sscanf(v->value, "%d", &y) == 1) {
+ if ((y < 10) || (y > 4000)) {
+ ast_log(LOG_WARNING, "Echo training time must be within the range of 10 to 4000 ms at line %d\n", v->lineno);
+ } else {
+ confp->chan.echotraining = y;
+ }
+ } else if (ast_true(v->value)) {
+ confp->chan.echotraining = 400;
+ } else
+ confp->chan.echotraining = 0;
+ } else if (!strcasecmp(v->name, "hidecallerid")) {
+ confp->chan.hidecallerid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "hidecalleridname")) {
+ confp->chan.hidecalleridname = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "pulsedial")) {
+ confp->chan.pulse = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callreturn")) {
+ confp->chan.callreturn = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callwaiting")) {
+ confp->chan.callwaiting = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callwaitingcallerid")) {
+ confp->chan.callwaitingcallerid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(confp->chan.context, v->value, sizeof(confp->chan.context));
+ } else if (!strcasecmp(v->name, "language")) {
+ ast_copy_string(confp->chan.language, v->value, sizeof(confp->chan.language));
+ } else if (!strcasecmp(v->name, "progzone")) {
+ ast_copy_string(progzone, v->value, sizeof(progzone));
+ } else if (!strcasecmp(v->name, "mohinterpret")
+ ||!strcasecmp(v->name, "musiconhold") || !strcasecmp(v->name, "musicclass")) {
+ ast_copy_string(confp->chan.mohinterpret, v->value, sizeof(confp->chan.mohinterpret));
+ } else if (!strcasecmp(v->name, "mohsuggest")) {
+ ast_copy_string(confp->chan.mohsuggest, v->value, sizeof(confp->chan.mohsuggest));
+ } else if (!strcasecmp(v->name, "stripmsd")) {
+ confp->chan.stripmsd = atoi(v->value);
+ } else if (!strcasecmp(v->name, "jitterbuffers")) {
+ numbufs = atoi(v->value);
+ } else if (!strcasecmp(v->name, "group")) {
+ confp->chan.group = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "callgroup")) {
+ if (!strcasecmp(v->value, "none"))
+ confp->chan.callgroup = 0;
+ else
+ confp->chan.callgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "pickupgroup")) {
+ if (!strcasecmp(v->value, "none"))
+ confp->chan.pickupgroup = 0;
+ else
+ confp->chan.pickupgroup = ast_get_group(v->value);
+ } else if (!strcasecmp(v->name, "setvar")) {
+ char *varname = ast_strdupa(v->value), *varval = NULL;
+ struct ast_variable *tmpvar;
+ if (varname && (varval = strchr(varname, '='))) {
+ *varval++ = '\0';
+ if ((tmpvar = ast_variable_new(varname, varval, ""))) {
+ tmpvar->next = confp->chan.vars;
+ confp->chan.vars = tmpvar;
+ }
+ }
+ } else if (!strcasecmp(v->name, "immediate")) {
+ confp->chan.immediate = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "transfertobusy")) {
+ confp->chan.transfertobusy = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "mwimonitor")) {
+ confp->chan.mwimonitor = ast_true(v->value) ? 1 : 0;
+ } else if (!strcasecmp(v->name, "cid_rxgain")) {
+ if (sscanf(v->value, "%f", &confp->chan.cid_rxgain) != 1) {
+ ast_log(LOG_WARNING, "Invalid cid_rxgain: %s\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "rxgain")) {
+ if (sscanf(v->value, "%f", &confp->chan.rxgain) != 1) {
+ ast_log(LOG_WARNING, "Invalid rxgain: %s\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "txgain")) {
+ if (sscanf(v->value, "%f", &confp->chan.txgain) != 1) {
+ ast_log(LOG_WARNING, "Invalid txgain: %s\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "tonezone")) {
+ if (sscanf(v->value, "%d", &confp->chan.tonezone) != 1) {
+ ast_log(LOG_WARNING, "Invalid tonezone: %s\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "callerid")) {
+ if (!strcasecmp(v->value, "asreceived")) {
+ confp->chan.cid_num[0] = '\0';
+ confp->chan.cid_name[0] = '\0';
+ } else {
+ ast_callerid_split(v->value, confp->chan.cid_name, sizeof(confp->chan.cid_name), confp->chan.cid_num, sizeof(confp->chan.cid_num));
+ }
+ } else if (!strcasecmp(v->name, "fullname")) {
+ ast_copy_string(confp->chan.cid_name, v->value, sizeof(confp->chan.cid_name));
+ } else if (!strcasecmp(v->name, "cid_number")) {
+ ast_copy_string(confp->chan.cid_num, v->value, sizeof(confp->chan.cid_num));
+ } else if (!strcasecmp(v->name, "useincomingcalleridonzaptransfer")) {
+ confp->chan.zaptrcallerid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "restrictcid")) {
+ confp->chan.restrictcid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "usecallingpres")) {
+ confp->chan.use_callingpres = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(confp->chan.accountcode, v->value, sizeof(confp->chan.accountcode));
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ y = ast_cdr_amaflags2int(v->value);
+ if (y < 0)
+ ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
+ else
+ confp->chan.amaflags = y;
+ } else if (!strcasecmp(v->name, "polarityonanswerdelay")) {
+ confp->chan.polarityonanswerdelay = atoi(v->value);
+ } else if (!strcasecmp(v->name, "answeronpolarityswitch")) {
+ confp->chan.answeronpolarityswitch = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "hanguponpolarityswitch")) {
+ confp->chan.hanguponpolarityswitch = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "sendcalleridafter")) {
+ confp->chan.sendcalleridafter = atoi(v->value);
+ } else if (!strcasecmp(v->name, "mwimonitornotify")) {
+ ast_copy_string(mwimonitornotify, v->value, sizeof(mwimonitornotify));
+ } else if (!reload){
+ if (!strcasecmp(v->name, "signalling") || !strcasecmp(v->name, "signaling")) {
+ int orig_radio = confp->chan.radio;
+ int orig_outsigmod = confp->chan.outsigmod;
+ int orig_auto = confp->is_sig_auto;
+
+ confp->chan.radio = 0;
+ confp->chan.outsigmod = -1;
+ confp->is_sig_auto = 0;
+ if (!strcasecmp(v->value, "em")) {
+ confp->chan.sig = SIG_EM;
+ } else if (!strcasecmp(v->value, "em_e1")) {
+ confp->chan.sig = SIG_EM_E1;
+ } else if (!strcasecmp(v->value, "em_w")) {
+ confp->chan.sig = SIG_EMWINK;
+ } else if (!strcasecmp(v->value, "fxs_ls")) {
+ confp->chan.sig = SIG_FXSLS;
+ } else if (!strcasecmp(v->value, "fxs_gs")) {
+ confp->chan.sig = SIG_FXSGS;
+ } else if (!strcasecmp(v->value, "fxs_ks")) {
+ confp->chan.sig = SIG_FXSKS;
+ } else if (!strcasecmp(v->value, "fxo_ls")) {
+ confp->chan.sig = SIG_FXOLS;
+ } else if (!strcasecmp(v->value, "fxo_gs")) {
+ confp->chan.sig = SIG_FXOGS;
+ } else if (!strcasecmp(v->value, "fxo_ks")) {
+ confp->chan.sig = SIG_FXOKS;
+ } else if (!strcasecmp(v->value, "fxs_rx")) {
+ confp->chan.sig = SIG_FXSKS;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "fxo_rx")) {
+ confp->chan.sig = SIG_FXOLS;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "fxs_tx")) {
+ confp->chan.sig = SIG_FXSLS;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "fxo_tx")) {
+ confp->chan.sig = SIG_FXOGS;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "em_rx")) {
+ confp->chan.sig = SIG_EM;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "em_tx")) {
+ confp->chan.sig = SIG_EM;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "em_rxtx")) {
+ confp->chan.sig = SIG_EM;
+ confp->chan.radio = 2;
+ } else if (!strcasecmp(v->value, "em_txrx")) {
+ confp->chan.sig = SIG_EM;
+ confp->chan.radio = 2;
+ } else if (!strcasecmp(v->value, "sf")) {
+ confp->chan.sig = SIG_SF;
+ } else if (!strcasecmp(v->value, "sf_w")) {
+ confp->chan.sig = SIG_SFWINK;
+ } else if (!strcasecmp(v->value, "sf_featd")) {
+ confp->chan.sig = SIG_FEATD;
+ } else if (!strcasecmp(v->value, "sf_featdmf")) {
+ confp->chan.sig = SIG_FEATDMF;
+ } else if (!strcasecmp(v->value, "sf_featb")) {
+ confp->chan.sig = SIG_SF_FEATB;
+ } else if (!strcasecmp(v->value, "sf")) {
+ confp->chan.sig = SIG_SF;
+ } else if (!strcasecmp(v->value, "sf_rx")) {
+ confp->chan.sig = SIG_SF;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "sf_tx")) {
+ confp->chan.sig = SIG_SF;
+ confp->chan.radio = 1;
+ } else if (!strcasecmp(v->value, "sf_rxtx")) {
+ confp->chan.sig = SIG_SF;
+ confp->chan.radio = 2;
+ } else if (!strcasecmp(v->value, "sf_txrx")) {
+ confp->chan.sig = SIG_SF;
+ confp->chan.radio = 2;
+ } else if (!strcasecmp(v->value, "featd")) {
+ confp->chan.sig = SIG_FEATD;
+ } else if (!strcasecmp(v->value, "featdmf")) {
+ confp->chan.sig = SIG_FEATDMF;
+ } else if (!strcasecmp(v->value, "featdmf_ta")) {
+ confp->chan.sig = SIG_FEATDMF_TA;
+ } else if (!strcasecmp(v->value, "e911")) {
+ confp->chan.sig = SIG_E911;
+ } else if (!strcasecmp(v->value, "fgccama")) {
+ confp->chan.sig = SIG_FGC_CAMA;
+ } else if (!strcasecmp(v->value, "fgccamamf")) {
+ confp->chan.sig = SIG_FGC_CAMAMF;
+ } else if (!strcasecmp(v->value, "featb")) {
+ confp->chan.sig = SIG_FEATB;
+#ifdef HAVE_PRI
+ } else if (!strcasecmp(v->value, "pri_net")) {
+ confp->chan.sig = SIG_PRI;
+ confp->pri.nodetype = PRI_NETWORK;
+ } else if (!strcasecmp(v->value, "pri_cpe")) {
+ confp->chan.sig = SIG_PRI;
+ confp->pri.nodetype = PRI_CPE;
+ } else if (!strcasecmp(v->value, "bri_cpe")) {
+ confp->chan.sig = SIG_BRI;
+ confp->pri.nodetype = PRI_CPE;
+ } else if (!strcasecmp(v->value, "bri_net")) {
+ confp->chan.sig = SIG_BRI;
+ confp->pri.nodetype = PRI_NETWORK;
+ } else if (!strcasecmp(v->value, "bri_cpe_ptmp")) {
+ confp->chan.sig = SIG_BRI_PTMP;
+ confp->pri.nodetype = PRI_CPE;
+ } else if (!strcasecmp(v->value, "bri_net_ptmp")) {
+ ast_log(LOG_WARNING, "How cool would it be if someone implemented this mode! For now, sucks for you.\n");
+ } else if (!strcasecmp(v->value, "gr303fxoks_net")) {
+ confp->chan.sig = SIG_GR303FXOKS;
+ confp->pri.nodetype = PRI_NETWORK;
+ } else if (!strcasecmp(v->value, "gr303fxsks_cpe")) {
+ confp->chan.sig = SIG_GR303FXSKS;
+ confp->pri.nodetype = PRI_CPE;
+#endif
+#ifdef HAVE_SS7
+ } else if (!strcasecmp(v->value, "ss7")) {
+ confp->chan.sig = SIG_SS7;
+#endif
+ } else if (!strcasecmp(v->value, "auto")) {
+ confp->is_sig_auto = 1;
+ } else {
+ confp->chan.outsigmod = orig_outsigmod;
+ confp->chan.radio = orig_radio;
+ confp->is_sig_auto = orig_auto;
+ ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "outsignalling") || !strcasecmp(v->name, "outsignaling")) {
+ if (!strcasecmp(v->value, "em")) {
+ confp->chan.outsigmod = SIG_EM;
+ } else if (!strcasecmp(v->value, "em_e1")) {
+ confp->chan.outsigmod = SIG_EM_E1;
+ } else if (!strcasecmp(v->value, "em_w")) {
+ confp->chan.outsigmod = SIG_EMWINK;
+ } else if (!strcasecmp(v->value, "sf")) {
+ confp->chan.outsigmod = SIG_SF;
+ } else if (!strcasecmp(v->value, "sf_w")) {
+ confp->chan.outsigmod = SIG_SFWINK;
+ } else if (!strcasecmp(v->value, "sf_featd")) {
+ confp->chan.outsigmod = SIG_FEATD;
+ } else if (!strcasecmp(v->value, "sf_featdmf")) {
+ confp->chan.outsigmod = SIG_FEATDMF;
+ } else if (!strcasecmp(v->value, "sf_featb")) {
+ confp->chan.outsigmod = SIG_SF_FEATB;
+ } else if (!strcasecmp(v->value, "sf")) {
+ confp->chan.outsigmod = SIG_SF;
+ } else if (!strcasecmp(v->value, "featd")) {
+ confp->chan.outsigmod = SIG_FEATD;
+ } else if (!strcasecmp(v->value, "featdmf")) {
+ confp->chan.outsigmod = SIG_FEATDMF;
+ } else if (!strcasecmp(v->value, "featdmf_ta")) {
+ confp->chan.outsigmod = SIG_FEATDMF_TA;
+ } else if (!strcasecmp(v->value, "e911")) {
+ confp->chan.outsigmod = SIG_E911;
+ } else if (!strcasecmp(v->value, "fgccama")) {
+ confp->chan.outsigmod = SIG_FGC_CAMA;
+ } else if (!strcasecmp(v->value, "fgccamamf")) {
+ confp->chan.outsigmod = SIG_FGC_CAMAMF;
+ } else if (!strcasecmp(v->value, "featb")) {
+ confp->chan.outsigmod = SIG_FEATB;
+ } else {
+ ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value);
+ }
+#ifdef HAVE_PRI
+ } else if (!strcasecmp(v->name, "pridialplan")) {
+ if (!strcasecmp(v->value, "national")) {
+ confp->pri.dialplan = PRI_NATIONAL_ISDN + 1;
+ } else if (!strcasecmp(v->value, "unknown")) {
+ confp->pri.dialplan = PRI_UNKNOWN + 1;
+ } else if (!strcasecmp(v->value, "private")) {
+ confp->pri.dialplan = PRI_PRIVATE + 1;
+ } else if (!strcasecmp(v->value, "international")) {
+ confp->pri.dialplan = PRI_INTERNATIONAL_ISDN + 1;
+ } else if (!strcasecmp(v->value, "local")) {
+ confp->pri.dialplan = PRI_LOCAL_ISDN + 1;
+ } else if (!strcasecmp(v->value, "dynamic")) {
+ confp->pri.dialplan = -1;
+ } else if (!strcasecmp(v->value, "redundant")) {
+ confp->pri.dialplan = -2;
+ } else {
+ ast_log(LOG_WARNING, "Unknown PRI dialplan '%s' at line %d.\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "prilocaldialplan")) {
+ if (!strcasecmp(v->value, "national")) {
+ confp->pri.localdialplan = PRI_NATIONAL_ISDN + 1;
+ } else if (!strcasecmp(v->value, "unknown")) {
+ confp->pri.localdialplan = PRI_UNKNOWN + 1;
+ } else if (!strcasecmp(v->value, "private")) {
+ confp->pri.localdialplan = PRI_PRIVATE + 1;
+ } else if (!strcasecmp(v->value, "international")) {
+ confp->pri.localdialplan = PRI_INTERNATIONAL_ISDN + 1;
+ } else if (!strcasecmp(v->value, "local")) {
+ confp->pri.localdialplan = PRI_LOCAL_ISDN + 1;
+ } else if (!strcasecmp(v->value, "dynamic")) {
+ confp->pri.localdialplan = -1;
+ } else if (!strcasecmp(v->value, "redundant")) {
+ confp->pri.localdialplan = -2;
+ } else {
+ ast_log(LOG_WARNING, "Unknown PRI localdialplan '%s' at line %d.\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "switchtype")) {
+ if (!strcasecmp(v->value, "national"))
+ confp->pri.switchtype = PRI_SWITCH_NI2;
+ else if (!strcasecmp(v->value, "ni1"))
+ confp->pri.switchtype = PRI_SWITCH_NI1;
+ else if (!strcasecmp(v->value, "dms100"))
+ confp->pri.switchtype = PRI_SWITCH_DMS100;
+ else if (!strcasecmp(v->value, "4ess"))
+ confp->pri.switchtype = PRI_SWITCH_ATT4ESS;
+ else if (!strcasecmp(v->value, "5ess"))
+ confp->pri.switchtype = PRI_SWITCH_LUCENT5E;
+ else if (!strcasecmp(v->value, "euroisdn"))
+ confp->pri.switchtype = PRI_SWITCH_EUROISDN_E1;
+ else if (!strcasecmp(v->value, "qsig"))
+ confp->pri.switchtype = PRI_SWITCH_QSIG;
+ else {
+ ast_log(LOG_ERROR, "Unknown switchtype '%s'\n", v->value);
+ return -1;
+ }
+ } else if (!strcasecmp(v->name, "nsf")) {
+ if (!strcasecmp(v->value, "sdn"))
+ confp->pri.nsf = PRI_NSF_SDN;
+ else if (!strcasecmp(v->value, "megacom"))
+ confp->pri.nsf = PRI_NSF_MEGACOM;
+ else if (!strcasecmp(v->value, "tollfreemegacom"))
+ confp->pri.nsf = PRI_NSF_TOLL_FREE_MEGACOM;
+ else if (!strcasecmp(v->value, "accunet"))
+ confp->pri.nsf = PRI_NSF_ACCUNET;
+ else if (!strcasecmp(v->value, "none"))
+ confp->pri.nsf = PRI_NSF_NONE;
+ else {
+ ast_log(LOG_WARNING, "Unknown network-specific facility '%s'\n", v->value);
+ confp->pri.nsf = PRI_NSF_NONE;
+ }
+ } else if (!strcasecmp(v->name, "priindication")) {
+ if (!strcasecmp(v->value, "outofband"))
+ confp->chan.priindication_oob = 1;
+ else if (!strcasecmp(v->value, "inband"))
+ confp->chan.priindication_oob = 0;
+ else
+ ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband' or 'outofband' at line %d\n",
+ v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "priexclusive")) {
+ confp->chan.priexclusive = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "internationalprefix")) {
+ ast_copy_string(confp->pri.internationalprefix, v->value, sizeof(confp->pri.internationalprefix));
+ } else if (!strcasecmp(v->name, "nationalprefix")) {
+ ast_copy_string(confp->pri.nationalprefix, v->value, sizeof(confp->pri.nationalprefix));
+ } else if (!strcasecmp(v->name, "localprefix")) {
+ ast_copy_string(confp->pri.localprefix, v->value, sizeof(confp->pri.localprefix));
+ } else if (!strcasecmp(v->name, "privateprefix")) {
+ ast_copy_string(confp->pri.privateprefix, v->value, sizeof(confp->pri.privateprefix));
+ } else if (!strcasecmp(v->name, "unknownprefix")) {
+ ast_copy_string(confp->pri.unknownprefix, v->value, sizeof(confp->pri.unknownprefix));
+ } else if (!strcasecmp(v->name, "resetinterval")) {
+ if (!strcasecmp(v->value, "never"))
+ confp->pri.resetinterval = -1;
+ else if (atoi(v->value) >= 60)
+ confp->pri.resetinterval = atoi(v->value);
+ else
+ ast_log(LOG_WARNING, "'%s' is not a valid reset interval, should be >= 60 seconds or 'never' at line %d\n",
+ v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "minunused")) {
+ confp->pri.minunused = atoi(v->value);
+ } else if (!strcasecmp(v->name, "minidle")) {
+ confp->pri.minidle = atoi(v->value);
+ } else if (!strcasecmp(v->name, "idleext")) {
+ ast_copy_string(confp->pri.idleext, v->value, sizeof(confp->pri.idleext));
+ } else if (!strcasecmp(v->name, "idledial")) {
+ ast_copy_string(confp->pri.idledial, v->value, sizeof(confp->pri.idledial));
+ } else if (!strcasecmp(v->name, "overlapdial")) {
+ if (ast_true(v->value)) {
+ confp->pri.overlapdial = ZAP_OVERLAPDIAL_BOTH;
+ } else if (!strcasecmp(v->value, "incoming")) {
+ confp->pri.overlapdial = ZAP_OVERLAPDIAL_INCOMING;
+ } else if (!strcasecmp(v->value, "outgoing")) {
+ confp->pri.overlapdial = ZAP_OVERLAPDIAL_OUTGOING;
+ } else if (!strcasecmp(v->value, "both") || ast_true(v->value)) {
+ confp->pri.overlapdial = ZAP_OVERLAPDIAL_BOTH;
+ } else {
+ confp->pri.overlapdial = ZAP_OVERLAPDIAL_NONE;
+ }
+ } else if (!strcasecmp(v->name, "pritimer")) {
+#ifdef PRI_GETSET_TIMERS
+ char tmp[20], *timerc, *c = tmp;
+ int timer, timeridx;
+ ast_copy_string(tmp, v->value, sizeof(tmp));
+ timerc = strsep(&c, ",");
+ if (timerc) {
+ timer = atoi(c);
+ if (!timer)
+ ast_log(LOG_WARNING, "'%s' is not a valid value for an ISDN timer\n", timerc);
+ else {
+ if ((timeridx = pri_timer2idx(timerc)) >= 0)
+ pritimers[timeridx] = timer;
+ else
+ ast_log(LOG_WARNING, "'%s' is not a valid ISDN timer\n", timerc);
+ }
+ } else
+ ast_log(LOG_WARNING, "'%s' is not a valid ISDN timer configuration string\n", v->value);
+
+ } else if (!strcasecmp(v->name, "facilityenable")) {
+ confp->pri.facilityenable = ast_true(v->value);
+#endif /* PRI_GETSET_TIMERS */
+#endif /* HAVE_PRI */
+#ifdef HAVE_SS7
+ } else if (!strcasecmp(v->name, "ss7type")) {
+ if (!strcasecmp(v->value, "itu")) {
+ cur_ss7type = SS7_ITU;
+ } else if (!strcasecmp(v->value, "ansi")) {
+ cur_ss7type = SS7_ANSI;
+ } else
+ ast_log(LOG_WARNING, "'%s' is an unknown ss7 switch type!\n", v->value);
+ } else if (!strcasecmp(v->name, "linkset")) {
+ cur_linkset = atoi(v->value);
+ } else if (!strcasecmp(v->name, "pointcode")) {
+ cur_pointcode = parse_pointcode(v->value);
+ } else if (!strcasecmp(v->name, "adjpointcode")) {
+ cur_adjpointcode = parse_pointcode(v->value);
+ } else if (!strcasecmp(v->name, "defaultdpc")) {
+ cur_defaultdpc = parse_pointcode(v->value);
+ } else if (!strcasecmp(v->name, "cicbeginswith")) {
+ cur_cicbeginswith = atoi(v->value);
+ } else if (!strcasecmp(v->name, "networkindicator")) {
+ if (!strcasecmp(v->value, "national"))
+ cur_networkindicator = SS7_NI_NAT;
+ else if (!strcasecmp(v->value, "national_spare"))
+ cur_networkindicator = SS7_NI_NAT_SPARE;
+ else if (!strcasecmp(v->value, "international"))
+ cur_networkindicator = SS7_NI_INT;
+ else if (!strcasecmp(v->value, "international_spare"))
+ cur_networkindicator = SS7_NI_INT_SPARE;
+ else
+ cur_networkindicator = -1;
+ } else if (!strcasecmp(v->name, "ss7_internationalprefix")) {
+ ast_copy_string(confp->ss7.internationalprefix, v->value, sizeof(confp->ss7.internationalprefix));
+ } else if (!strcasecmp(v->name, "ss7_nationalprefix")) {
+ ast_copy_string(confp->ss7.nationalprefix, v->value, sizeof(confp->ss7.nationalprefix));
+ } else if (!strcasecmp(v->name, "ss7_subscriberprefix")) {
+ ast_copy_string(confp->ss7.subscriberprefix, v->value, sizeof(confp->ss7.subscriberprefix));
+ } else if (!strcasecmp(v->name, "ss7_unknownprefix")) {
+ ast_copy_string(confp->ss7.unknownprefix, v->value, sizeof(confp->ss7.unknownprefix));
+ } else if (!strcasecmp(v->name, "ss7_called_nai")) {
+ if (!strcasecmp(v->value, "national")) {
+ confp->ss7.called_nai = SS7_NAI_NATIONAL;
+ } else if (!strcasecmp(v->value, "international")) {
+ confp->ss7.called_nai = SS7_NAI_INTERNATIONAL;
+ } else if (!strcasecmp(v->value, "subscriber")) {
+ confp->ss7.called_nai = SS7_NAI_SUBSCRIBER;
+ } else if (!strcasecmp(v->value, "dynamic")) {
+ confp->ss7.called_nai = SS7_NAI_DYNAMIC;
+ } else {
+ ast_log(LOG_WARNING, "Unknown SS7 called_nai '%s' at line %d.\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "ss7_calling_nai")) {
+ if (!strcasecmp(v->value, "national")) {
+ confp->ss7.calling_nai = SS7_NAI_NATIONAL;
+ } else if (!strcasecmp(v->value, "international")) {
+ confp->ss7.calling_nai = SS7_NAI_INTERNATIONAL;
+ } else if (!strcasecmp(v->value, "subscriber")) {
+ confp->ss7.calling_nai = SS7_NAI_SUBSCRIBER;
+ } else if (!strcasecmp(v->value, "dynamic")) {
+ confp->ss7.calling_nai = SS7_NAI_DYNAMIC;
+ } else {
+ ast_log(LOG_WARNING, "Unknown SS7 calling_nai '%s' at line %d.\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "sigchan")) {
+ int sigchan, res;
+ sigchan = atoi(v->value);
+ res = linkset_addsigchan(sigchan);
+ if (res < 0)
+ return -1;
+#endif /* HAVE_SS7 */
+ } else if (!strcasecmp(v->name, "cadence")) {
+ /* setup to scan our argument */
+ int element_count, c[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int i;
+ struct zt_ring_cadence new_cadence;
+ int cid_location = -1;
+ int firstcadencepos = 0;
+ char original_args[80];
+ int cadence_is_ok = 1;
+
+ ast_copy_string(original_args, v->value, sizeof(original_args));
+ /* 16 cadences allowed (8 pairs) */
+ element_count = sscanf(v->value, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11], &c[12], &c[13], &c[14], &c[15]);
+
+ /* Cadence must be even (on/off) */
+ if (element_count % 2 == 1) {
+ ast_log(LOG_ERROR, "Must be a silence duration for each ring duration: %s\n",original_args);
+ cadence_is_ok = 0;
+ }
+
+ /* Ring cadences cannot be negative */
+ for (i = 0; i < element_count; i++) {
+ if (c[i] == 0) {
+ ast_log(LOG_ERROR, "Ring or silence duration cannot be zero: %s\n", original_args);
+ cadence_is_ok = 0;
+ break;
+ } else if (c[i] < 0) {
+ if (i % 2 == 1) {
+ /* Silence duration, negative possibly okay */
+ if (cid_location == -1) {
+ cid_location = i;
+ c[i] *= -1;
+ } else {
+ ast_log(LOG_ERROR, "CID location specified twice: %s\n",original_args);
+ cadence_is_ok = 0;
+ break;
+ }
+ } else {
+ if (firstcadencepos == 0) {
+ firstcadencepos = i; /* only recorded to avoid duplicate specification */
+ /* duration will be passed negative to the Zaptel driver */
+ } else {
+ ast_log(LOG_ERROR, "First cadence position specified twice: %s\n",original_args);
+ cadence_is_ok = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Substitute our scanned cadence */
+ for (i = 0; i < 16; i++) {
+ new_cadence.ringcadence[i] = c[i];
+ }
+
+ if (cadence_is_ok) {
+ /* ---we scanned it without getting annoyed; now some sanity checks--- */
+ if (element_count < 2) {
+ ast_log(LOG_ERROR, "Minimum cadence is ring,pause: %s\n", original_args);
+ } else {
+ if (cid_location == -1) {
+ /* user didn't say; default to first pause */
+ cid_location = 1;
+ } else {
+ /* convert element_index to cidrings value */
+ cid_location = (cid_location + 1) / 2;
+ }
+ /* ---we like their cadence; try to install it--- */
+ if (!user_has_defined_cadences++)
+ /* this is the first user-defined cadence; clear the default user cadences */
+ num_cadence = 0;
+ if ((num_cadence+1) >= NUM_CADENCE_MAX)
+ ast_log(LOG_ERROR, "Already %d cadences; can't add another: %s\n", NUM_CADENCE_MAX, original_args);
+ else {
+ cadences[num_cadence] = new_cadence;
+ cidrings[num_cadence++] = cid_location;
+ ast_verb(3, "cadence 'r%d' added: %s\n",num_cadence,original_args);
+ }
+ }
+ }
+ } else if (!strcasecmp(v->name, "ringtimeout")) {
+ ringt_base = (atoi(v->value) * 8) / READ_SIZE;
+ } else if (!strcasecmp(v->name, "prewink")) {
+ confp->timing.prewinktime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "preflash")) {
+ confp->timing.preflashtime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "wink")) {
+ confp->timing.winktime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "flash")) {
+ confp->timing.flashtime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "start")) {
+ confp->timing.starttime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "rxwink")) {
+ confp->timing.rxwinktime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "rxflash")) {
+ confp->timing.rxflashtime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "debounce")) {
+ confp->timing.debouncetime = atoi(v->value);
+ } else if (!strcasecmp(v->name, "toneduration")) {
+ int toneduration;
+ int ctlfd;
+ int res;
+ struct zt_dialparams dps;
+
+ ctlfd = open("/dev/zap/ctl", O_RDWR);
+ if (ctlfd == -1) {
+ ast_log(LOG_ERROR, "Unable to open /dev/zap/ctl to set toneduration\n");
+ return -1;
+ }
+
+ toneduration = atoi(v->value);
+ if (toneduration > -1) {
+ dps.dtmf_tonelen = dps.mfv1_tonelen = toneduration;
+ res = ioctl(ctlfd, ZT_SET_DIALPARAMS, &dps);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "Invalid tone duration: %d ms\n", toneduration);
+ return -1;
+ }
+ }
+ close(ctlfd);
+ } else if (!strcasecmp(v->name, "defaultcic")) {
+ ast_copy_string(defaultcic, v->value, sizeof(defaultcic));
+ } else if (!strcasecmp(v->name, "defaultozz")) {
+ ast_copy_string(defaultozz, v->value, sizeof(defaultozz));
+ } else if (!strcasecmp(v->name, "mwilevel")) {
+ mwilevel = atoi(v->value);
+ }
+ } else if (!skipchannels)
+ ast_log(LOG_WARNING, "Ignoring %s\n", v->name);
+ }
+ if (zapchan[0]) {
+ /* The user has set 'zapchan' */
+ /*< \todo pass proper line number instead of 0 */
+ if (build_channels(*confp, 0, zapchan, reload, 0, &found_pseudo)) {
+ return -1;
+ }
+ }
+ /*< \todo why check for the pseudo in the per-channel section.
+ * Any actual use for manual setup of the pseudo channel? */
+ if (!found_pseudo && reload == 0) {
+ /* Make sure pseudo isn't a member of any groups if
+ we're automatically making it. */
+
+ confp->chan.group = 0;
+ confp->chan.callgroup = 0;
+ confp->chan.pickupgroup = 0;
+
+ tmp = mkintf(CHAN_PSEUDO, *confp, NULL, reload);
+
+ if (tmp) {
+ ast_verb(3, "Automatically generated pseudo channel\n");
+ } else {
+ ast_log(LOG_WARNING, "Unable to register pseudo channel!\n");
+ }
+ }
+ return 0;
+}
+
+static int setup_zap(int reload)
+{
+ struct ast_config *cfg, *ucfg;
+ struct ast_variable *v;
+ struct zt_chan_conf base_conf = zt_chan_conf_default();
+ struct zt_chan_conf conf;
+ struct ast_flags config_flags = { reload == 1 ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ int res;
+
+#ifdef HAVE_PRI
+ char *c;
+ int spanno;
+ int i;
+ int logicalspan;
+ int trunkgroup;
+ int dchannels[NUM_DCHANS];
+#endif
+
+ cfg = ast_config_load(config, config_flags);
+
+ /* Error if we have no config file */
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+ ucfg = ast_config_load("users.conf", config_flags);
+ if (ucfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ cfg = ast_config_load(config, config_flags);
+ } else {
+ ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+ ucfg = ast_config_load("users.conf", config_flags);
+ }
+
+ /* It's a little silly to lock it, but we mind as well just to be sure */
+ ast_mutex_lock(&iflock);
+#ifdef HAVE_PRI
+ if (!reload) {
+ /* Process trunkgroups first */
+ v = ast_variable_browse(cfg, "trunkgroups");
+ while (v) {
+ if (!strcasecmp(v->name, "trunkgroup")) {
+ trunkgroup = atoi(v->value);
+ if (trunkgroup > 0) {
+ if ((c = strchr(v->value, ','))) {
+ i = 0;
+ memset(dchannels, 0, sizeof(dchannels));
+ while (c && (i < NUM_DCHANS)) {
+ dchannels[i] = atoi(c + 1);
+ if (dchannels[i] < 0) {
+ ast_log(LOG_WARNING, "D-channel for trunk group %d must be a postiive number at line %d of zapata.conf\n", trunkgroup, v->lineno);
+ } else
+ i++;
+ c = strchr(c + 1, ',');
+ }
+ if (i) {
+ if (pri_create_trunkgroup(trunkgroup, dchannels)) {
+ ast_log(LOG_WARNING, "Unable to create trunk group %d with Primary D-channel %d at line %d of zapata.conf\n", trunkgroup, dchannels[0], v->lineno);
+ } else
+ ast_verb(2, "Created trunk group %d with Primary D-channel %d and %d backup%s\n", trunkgroup, dchannels[0], i - 1, (i == 1) ? "" : "s");
+ } else
+ ast_log(LOG_WARNING, "Trunk group %d lacks any valid D-channels at line %d of zapata.conf\n", trunkgroup, v->lineno);
+ } else
+ ast_log(LOG_WARNING, "Trunk group %d lacks a primary D-channel at line %d of zapata.conf\n", trunkgroup, v->lineno);
+ } else
+ ast_log(LOG_WARNING, "Trunk group identifier must be a positive integer at line %d of zapata.conf\n", v->lineno);
+ } else if (!strcasecmp(v->name, "spanmap")) {
+ spanno = atoi(v->value);
+ if (spanno > 0) {
+ if ((c = strchr(v->value, ','))) {
+ trunkgroup = atoi(c + 1);
+ if (trunkgroup > 0) {
+ if ((c = strchr(c + 1, ',')))
+ logicalspan = atoi(c + 1);
+ else
+ logicalspan = 0;
+ if (logicalspan >= 0) {
+ if (pri_create_spanmap(spanno - 1, trunkgroup, logicalspan)) {
+ ast_log(LOG_WARNING, "Failed to map span %d to trunk group %d (logical span %d)\n", spanno, trunkgroup, logicalspan);
+ } else
+ ast_verb(2, "Mapped span %d to trunk group %d (logical span %d)\n", spanno, trunkgroup, logicalspan);
+ } else
+ ast_log(LOG_WARNING, "Logical span must be a postive number, or '0' (for unspecified) at line %d of zapata.conf\n", v->lineno);
+ } else
+ ast_log(LOG_WARNING, "Trunk group must be a postive number at line %d of zapata.conf\n", v->lineno);
+ } else
+ ast_log(LOG_WARNING, "Missing trunk group for span map at line %d of zapata.conf\n", v->lineno);
+ } else
+ ast_log(LOG_WARNING, "Span number must be a postive integer at line %d of zapata.conf\n", v->lineno);
+ } else {
+ ast_log(LOG_NOTICE, "Ignoring unknown keyword '%s' in trunkgroups\n", v->name);
+ }
+ v = v->next;
+ }
+ }
+#endif
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ mwimonitornotify[0] = '\0';
+
+ v = ast_variable_browse(cfg, "channels");
+ res = process_zap(&base_conf, v, reload, 0);
+ ast_mutex_unlock(&iflock);
+ ast_config_destroy(cfg);
+ if (res)
+ return res;
+ if (ucfg) {
+ char *cat;
+ const char *chans;
+ process_zap(&base_conf, ast_variable_browse(ucfg, "general"), 1, 1);
+ for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
+ if (!strcasecmp(cat, "general"))
+ continue;
+ chans = ast_variable_retrieve(ucfg, cat, "zapchan");
+ if (!ast_strlen_zero(chans)) {
+ if (memcpy(&conf, &base_conf, sizeof(conf)) == NULL) {
+ ast_log(LOG_ERROR, "Not enough memory for conf copy\n");
+ ast_config_destroy(ucfg);
+ return -1;
+ }
+ process_zap(&conf, ast_variable_browse(ucfg, cat), reload, 0);
+ }
+ }
+ ast_config_destroy(ucfg);
+ }
+#ifdef HAVE_PRI
+ if (!reload) {
+ int x;
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (pris[x].pvts[0]) {
+ if (start_pri(pris + x)) {
+ ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1);
+ return -1;
+ } else
+ ast_verb(2, "Starting D-Channel on span %d\n", x + 1);
+ }
+ }
+ }
+#endif
+#ifdef HAVE_SS7
+ if (!reload) {
+ int x;
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (linksets[x].ss7) {
+ if (ast_pthread_create(&linksets[x].master, NULL, ss7_linkset, &linksets[x])) {
+ ast_log(LOG_ERROR, "Unable to start SS7 linkset on span %d\n", x + 1);
+ return -1;
+ } else
+ ast_verb(2, "Starting SS7 linkset on span %d\n", x + 1);
+ }
+ }
+ }
+#endif
+ /* And start the monitor for the first time */
+ restart_monitor();
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res;
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+ int y, i;
+#endif
+
+#ifdef HAVE_PRI
+ memset(pris, 0, sizeof(pris));
+ for (y = 0; y < NUM_SPANS; y++) {
+ ast_mutex_init(&pris[y].lock);
+ pris[y].offset = -1;
+ pris[y].master = AST_PTHREADT_NULL;
+ for (i = 0; i < NUM_DCHANS; i++)
+ pris[y].fds[i] = -1;
+ }
+ pri_set_error(zt_pri_error);
+ pri_set_message(zt_pri_message);
+ ast_register_application(zap_send_keypad_facility_app, zap_send_keypad_facility_exec,
+ zap_send_keypad_facility_synopsis, zap_send_keypad_facility_descrip);
+#endif
+#ifdef HAVE_SS7
+ memset(linksets, 0, sizeof(linksets));
+ for (y = 0; y < NUM_SPANS; y++) {
+ ast_mutex_init(&linksets[y].lock);
+ linksets[y].master = AST_PTHREADT_NULL;
+ for (i = 0; i < NUM_DCHANS; i++)
+ linksets[y].fds[i] = -1;
+ }
+ ss7_set_error(zt_ss7_error);
+ ss7_set_message(zt_ss7_message);
+#endif /* HAVE_SS7 */
+ res = setup_zap(0);
+ /* Make sure we can register our Zap channel type */
+ if (res)
+ return AST_MODULE_LOAD_DECLINE;
+ if (ast_channel_register(&zap_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Zap'\n");
+ __unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+#ifdef HAVE_PRI
+ ast_string_field_init(&inuse, 16);
+ ast_string_field_set(&inuse, name, "GR-303InUse");
+ ast_cli_register_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry));
+#endif
+#ifdef HAVE_SS7
+ ast_cli_register_multiple(zap_ss7_cli, sizeof(zap_ss7_cli) / sizeof(zap_ss7_cli[0]));
+#endif
+
+ ast_cli_register_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
+
+ memset(round_robin, 0, sizeof(round_robin));
+ ast_manager_register( "ZapTransfer", 0, action_transfer, "Transfer Zap Channel" );
+ ast_manager_register( "ZapHangup", 0, action_transferhangup, "Hangup Zap Channel" );
+ ast_manager_register( "ZapDialOffhook", 0, action_zapdialoffhook, "Dial over Zap channel while offhook" );
+ ast_manager_register( "ZapDNDon", 0, action_zapdndon, "Toggle Zap channel Do Not Disturb status ON" );
+ ast_manager_register( "ZapDNDoff", 0, action_zapdndoff, "Toggle Zap channel Do Not Disturb status OFF" );
+ ast_manager_register("ZapShowChannels", 0, action_zapshowchannels, "Show status zapata channels");
+ ast_manager_register("ZapRestart", 0, action_zaprestart, "Fully Restart Zaptel channels (terminates calls)");
+
+ return res;
+}
+
+static int zt_sendtext(struct ast_channel *c, const char *text)
+{
+#define END_SILENCE_LEN 400
+#define HEADER_MS 50
+#define TRAILER_MS 5
+#define HEADER_LEN ((HEADER_MS + TRAILER_MS) * 8)
+#define ASCII_BYTES_PER_CHAR 80
+
+ unsigned char *buf,*mybuf;
+ struct zt_pvt *p = c->tech_pvt;
+ struct pollfd fds[1];
+ int size,res,fd,len,x;
+ int bytes=0;
+ /* Initial carrier (imaginary) */
+ float cr = 1.0;
+ float ci = 0.0;
+ float scont = 0.0;
+ int index;
+
+ index = zt_get_index(c, p, 0);
+ if (index < 0) {
+ ast_log(LOG_WARNING, "Huh? I don't exist?\n");
+ return -1;
+ }
+ if (!text[0]) return(0); /* if nothing to send, dont */
+ if ((!p->tdd) && (!p->mate)) return(0); /* if not in TDD mode, just return */
+ if (p->mate)
+ buf = ast_malloc(((strlen(text) + 1) * ASCII_BYTES_PER_CHAR) + END_SILENCE_LEN + HEADER_LEN);
+ else
+ buf = ast_malloc(((strlen(text) + 1) * TDD_BYTES_PER_CHAR) + END_SILENCE_LEN);
+ if (!buf)
+ return -1;
+ mybuf = buf;
+ if (p->mate) {
+ int codec = AST_LAW(p);
+ for (x = 0; x < HEADER_MS; x++) { /* 50 ms of Mark */
+ PUT_CLID_MARKMS;
+ }
+ /* Put actual message */
+ for (x = 0; text[x]; x++) {
+ PUT_CLID(text[x]);
+ }
+ for (x = 0; x < TRAILER_MS; x++) { /* 5 ms of Mark */
+ PUT_CLID_MARKMS;
+ }
+ len = bytes;
+ buf = mybuf;
+ } else {
+ len = tdd_generate(p->tdd, buf, text);
+ if (len < 1) {
+ ast_log(LOG_ERROR, "TDD generate (len %d) failed!!\n", (int)strlen(text));
+ ast_free(mybuf);
+ return -1;
+ }
+ }
+ memset(buf + len, 0x7f, END_SILENCE_LEN);
+ len += END_SILENCE_LEN;
+ fd = p->subs[index].zfd;
+ while (len) {
+ if (ast_check_hangup(c)) {
+ ast_free(mybuf);
+ return -1;
+ }
+ size = len;
+ if (size > READ_SIZE)
+ size = READ_SIZE;
+ fds[0].fd = fd;
+ fds[0].events = POLLOUT | POLLPRI;
+ fds[0].revents = 0;
+ res = poll(fds, 1, -1);
+ if (!res) {
+ ast_debug(1, "poll (for write) ret. 0 on channel %d\n", p->channel);
+ continue;
+ }
+ /* if got exception */
+ if (fds[0].revents & POLLPRI) {
+ ast_free(mybuf);
+ return -1;
+ }
+ if (!(fds[0].revents & POLLOUT)) {
+ ast_debug(1, "write fd not ready on channel %d\n", p->channel);
+ continue;
+ }
+ res = write(fd, buf, size);
+ if (res != size) {
+ if (res == -1) {
+ ast_free(mybuf);
+ return -1;
+ }
+ ast_debug(1, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel);
+ break;
+ }
+ len -= size;
+ buf += size;
+ }
+ ast_free(mybuf);
+ return(0);
+}
+
+
+static int reload(void)
+{
+ int res = 0;
+
+ res = setup_zap(1);
+ if (res) {
+ ast_log(LOG_WARNING, "Reload of chan_zap.so is unsuccessful!\n");
+ return -1;
+ }
+ return 0;
+}
+
+/* This is a workaround so that menuselect displays a proper description
+ * AST_MODULE_INFO(, , "Zapata Telephony"
+ */
+
+#ifdef ZAPATA_PRI
+#define tdesc "Zapata Telephony w/PRI"
+#else
+#define tdesc "Zapata Telephony"
+#endif
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
+
diff --git a/trunk/channels/console_board.c b/trunk/channels/console_board.c
new file mode 100644
index 000000000..eafccc391
--- /dev/null
+++ b/trunk/channels/console_board.c
@@ -0,0 +1,329 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007-2008, Marta Carbone, Luigi Rizzo
+ *
+ * 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.
+ *
+ * $Revision$
+ */
+
+/*
+ * Message board implementation.
+ *
+ * A message board is a region of the SDL screen where
+ * messages can be printed, like on a terminal window.
+ *
+ * At the moment we support fix-size font.
+ *
+ * The text is stored in a buffer
+ * of fixed size (rows and cols). A portion of the buffer is
+ * visible on the screen, and the visible window can be moved up and
+ * down by dragging (not yet!)
+ *
+ * TODO: font dynamic allocation
+ *
+ * The region where the text is displayed on the screen is defined
+ * as keypad element, (the name is defined in the `region' variable
+ * so the board geometry can be read from the skin or from the
+ * configuration file).
+ */
+
+#include "asterisk.h" /* ast_strdupa */
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk/utils.h" /* ast_strdupa */
+#include "console_video.h" /* ast_strdupa */
+
+#ifdef HAVE_SDL /* we only use this code if SDL is available */
+#include <SDL/SDL.h>
+
+/* Fonts characterization. XXX should be read from the file */
+#define FONT_H 20 /* char height, pixels */
+#define FONT_W 9 /* char width, pixels */
+
+struct board {
+ int kb_output; /* identity of the board */
+ /* pointer to the destination surface (on the keypad window) */
+ SDL_Surface *screen; /* the main screen */
+ SDL_Rect *p_rect; /* where to write on the main screen */
+ SDL_Surface *blank; /* original content of the window */
+
+ int v_h; /* virtual text height, in lines */
+ int v_w; /* virtual text width, in lines (probably same as p_w) */
+ int p_h; /* physical (displayed) text height, in lines
+ * XXX p_h * FONT_H = pixel_height */
+ int p_w; /* physical (displayed) text width, in characters
+ * XXX p_w * FONT_W = pixel_width */
+
+ int cur_col; /* print position (free character) on the last line */
+ int cur_line; /* first (or last ?) virtual line displayed,
+ * 0 is the line at the bottom, 1 is the one above,...
+ */
+
+ SDL_Surface *font; /* points to a surface in the gui structure */
+ SDL_Rect *font_rects; /* pointer to the font rects */
+ char *text;
+ /* text buffer, v_h * v_w char.
+ * We make sure the buffer is always full,
+ * print on some position on the last line,
+ * and scroll up when appending new text
+ */
+};
+
+/*! \brief Initialize the board.
+ * return 0 on success, 1 on error
+ * TODO, if this is done at reload time,
+ * free resources before allocate new ones
+ * TODO: resource deallocation in case of error.
+ * TODO: move the font load at gui_initialization
+ * TODO: deallocation of the message history
+ */
+struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
+ SDL_Surface *font, SDL_Rect *font_rects);
+struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
+ SDL_Surface *font, SDL_Rect *font_rects)
+{
+ struct board *b = ast_calloc(1, sizeof (*b));
+ SDL_Rect br;
+
+ if (b == NULL)
+ return NULL;
+ /* font, points to the gui structure */
+ b->font = font;
+ b->font_rects = font_rects;
+
+ /* Destination rectangle on the screen - reference is the whole screen */
+ b->p_rect = dest;
+ b->screen = screen;
+
+ /* compute physical sizes */
+ b->p_h = b->p_rect->h/FONT_H;
+ b->p_w = b->p_rect->w/FONT_W;
+
+ /* virtual sizes */
+ b->v_h = b->p_h * 10; /* XXX 10 times larger */
+ b->v_w = b->p_w; /* same width */
+
+ /* the rectangle we actually use */
+ br.h = b->p_h * FONT_H; /* pixel sizes of the background */
+ br.w = b->p_w * FONT_W;
+ br.x = br.y = 0;
+
+ /* allocate a buffer for the text */
+ b->text = ast_calloc(b->v_w*b->v_h + 1, 1);
+ if (b->text == NULL) {
+ ast_log(LOG_WARNING, "Unable to allocate board history memory.\n");
+ ast_free(b);
+ return NULL;
+ }
+ memset(b->text, ' ', b->v_w * b->v_h); /* fill with spaces */
+
+ /* make a copy of the original rectangle, for cleaning up */
+ b->blank = SDL_CreateRGBSurface(screen->flags, br.w, br.h,
+ screen->format->BitsPerPixel,
+ screen->format->Rmask, screen->format->Gmask,
+ screen->format->Bmask, screen->format->Amask);
+
+ if (b->blank == NULL) {
+ ast_log(LOG_WARNING, "Unable to allocate board virtual screen: %s\n",
+ SDL_GetError());
+ ast_free(b->text);
+ ast_free(b);
+ return NULL;
+ }
+ SDL_BlitSurface(screen, b->p_rect, b->blank, &br);
+
+ /* Set color key, if not alpha channel present */
+ //colorkey = SDL_MapRGB(b->board_surface->format, 0, 0, 0);
+ //SDL_SetColorKey(b->board_surface, SDL_SRCCOLORKEY, colorkey);
+
+ b->cur_col = 0; /* current print column */
+ b->cur_line = 0; /* last line displayed */
+
+ ast_log(LOG_WARNING, "Message board %dx%d@%d,%d successfully initialized\n",
+ b->p_rect->w, b->p_rect->h,
+ b->p_rect->x, b->p_rect->y);
+ return b;
+}
+
+/* Render the text on the board surface.
+ * The first line to render is the one at v_h - p_h - cur_line,
+ * the size is p_h * p_w.
+ * XXX we assume here that p_w = v_w.
+ */
+static void render_board(struct board *b)
+{
+ int first_row = b->v_h - b->p_h - b->cur_line;
+ int first_char = b->v_w * first_row;
+ int last_char = first_char + b->p_h * b->v_w;
+ int i, col;
+ SDL_Rect dst;
+
+ /* top left char on the physical surface */
+ dst.w = FONT_W;
+ dst.h = FONT_H;
+ dst.x = b->p_rect->x;
+ dst.y = b->p_rect->y;
+
+
+ /* clean the surface board */
+ SDL_BlitSurface(b->blank, NULL, b->screen, b->p_rect);
+
+ /* blit all characters */
+ for (i = first_char, col = 0; i < last_char; i++) {
+ int c = b->text[i] - 32; /* XXX first 32 chars are not printable */
+ if (c < 0) /* buffer terminator or anything else is a blank */
+ c = 0;
+ SDL_BlitSurface(b->font, &b->font_rects[c], b->screen, &dst);
+ /* point dst to next char position */
+ dst.x += dst.w;
+ col++;
+ if (col >= b->v_w) { /* next row */
+ dst.x = b->p_rect->x;
+ dst.y += dst.h;
+ col = 0;
+ }
+ }
+ SDL_UpdateRects(b->screen, 1, b->p_rect); /* Update the screen */
+}
+
+void move_message_board(struct board *b, int dy)
+{
+ int cur = b->cur_line + dy;
+ if (cur < 0)
+ cur = 0;
+ else if (cur >= b->v_h - b->p_h)
+ cur = b->v_h - b->p_h - 1;
+ b->cur_line = cur;
+ render_board(b);
+}
+
+/* return the content of a board */
+const char *read_message(const struct board *b)
+{
+ return b->text;
+}
+
+int reset_board(struct board *b)
+{
+ memset(b->text, ' ', b->v_w * b->v_h); /* fill with spaces */
+ b->cur_col = 0;
+ b->cur_line = 0;
+ render_board(b);
+ return 0;
+}
+/* Store the message on the history board
+ * and blit on screen if required.
+ * XXX now easy. only regular chars
+ */
+int print_message(struct board *b, const char *s)
+{
+ int i, l, row, col;
+ char *dst;
+
+ if (ast_strlen_zero(s))
+ return 0;
+
+ l = strlen(s);
+ row = 0;
+ col = b->cur_col;
+ /* First, only check how much space we need.
+ * Starting from the current print position, we move
+ * it forward and down (if necessary) according to input
+ * characters (including newlines, tabs, backspaces...).
+ * At the end, row tells us how many rows to scroll, and
+ * col (ignored) is the final print position.
+ */
+ for (i = 0; i < l; i++) {
+ switch (s[i]) {
+ case '\r':
+ col = 0;
+ break;
+ case '\n':
+ col = 0;
+ row++;
+ break;
+ case '\b':
+ if (col > 0)
+ col--;
+ break;
+ default:
+ if (s[i] < 32) /* signed, so take up to 127 */
+ break;
+ col++;
+ if (col >= b->v_w) {
+ col -= b->v_w;
+ row++;
+ }
+ break;
+ }
+ }
+ /* scroll the text window */
+ if (row > 0) { /* need to scroll by 'row' rows */
+ memcpy(b->text, b->text + row * b->v_w, b->v_w * (b->v_h - row));
+ /* clean the destination area */
+ dst = b->text + b->v_w * (b->v_h - row - 1) + b->cur_col;
+ memset(dst, ' ', b->v_w - b->cur_col + b->v_w * row);
+ }
+ /* now do the actual printing. The print position is 'row' lines up
+ * from the bottom of the buffer, start at the same 'cur_col' as before.
+ * dst points to the beginning of the current line.
+ */
+ dst = b->text + b->v_w * (b->v_h - row - 1); /* start of current line */
+ col = b->cur_col;
+ for (i = 0; i < l; i++) {
+ switch (s[i]) {
+ case '\r':
+ col = 0;
+ break;
+ case '\n': /* move to beginning of next line */
+ dst[col] = '\0'; /* mark the rest of the line as empty */
+ col = 0;
+ dst += b->v_w;
+ break;
+ case '\b': /* one char back */
+ if (col > 0)
+ col--;
+ dst[col] = ' '; /* delete current char */
+ break;
+ default:
+ if (s[i] < 32) /* signed, so take up to 127 */
+ break; /* non printable */
+ dst[col] = s[i]; /* store character */
+ col++;
+ if (col >= b->v_w) {
+ col -= b->v_w;
+ dst += b->v_w;
+ }
+ break;
+ }
+ }
+ dst[col] = '\0'; /* the current position is empty */
+ b->cur_col = col;
+ /* everything is printed now, must do the rendering */
+ render_board(b);
+ return 1;
+}
+
+#if 0
+/*! \brief refresh the screen, and also grab a bunch of events.
+ */
+static int scroll_message(...)
+{
+if moving up, scroll text up;
+ if (gui->message_board.screen_cur > 0)
+ gui->message_board.screen_cur--;
+otherwise scroll text down.
+ if ((b->screen_cur + b->p_line) < b->board_next) {
+ gui->message_board.screen_cur++;
+#endif /* notyet */
+
+#endif /* HAVE_SDL */
diff --git a/trunk/channels/console_gui.c b/trunk/channels/console_gui.c
new file mode 100644
index 000000000..aa2f2cf95
--- /dev/null
+++ b/trunk/channels/console_gui.c
@@ -0,0 +1,1036 @@
+/*
+ * GUI for console video.
+ * The routines here are in charge of loading the keypad and handling events.
+ * $Revision$
+ */
+
+/*
+ * GUI layout, structure and management
+
+For the GUI we use SDL to create a large surface (gui->screen)
+containing tree sections: remote video on the left, local video
+on the right, and the keypad with all controls and text windows
+in the center.
+The central section is built using an image for the skin, fonts and
+other GUI elements. Comments embedded in the image to indicate to
+what function each area is mapped to.
+
+Mouse and keyboard events are detected on the whole surface, and
+handled differently according to their location:
+
+- drag on the local video window are used to move the captured
+ area (in the case of X11 grabber) or the picture-in-picture
+ location (in case of camera included on the X11 grab).
+- click on the keypad are mapped to the corresponding key;
+- drag on some keypad areas (sliders etc.) are mapped to the
+ corresponding functions;
+- keystrokes are used as keypad functions, or as text input
+ if we are in text-input mode.
+
+Configuration options control the appeareance of the gui:
+
+ keypad = /tmp/phone.jpg ; the skin
+ keypad_font = /tmp/font.ttf ; the font to use for output (XXX deprecated)
+
+ *
+ */
+
+#include "asterisk.h"
+#include "console_video.h"
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/utils.h" /* ast_calloc and ast_realloc */
+#include <math.h> /* sqrt */
+
+/* We use 3 'windows' in the GUI */
+enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX };
+
+#ifndef HAVE_SDL /* stubs if we don't have any sdl */
+static void show_frame(struct video_desc *env, int out) {}
+static void sdl_setup(struct video_desc *env) {}
+static struct gui_info *cleanup_sdl(struct gui_info *gui) { return NULL; }
+static void eventhandler(struct video_desc *env, const char *caption) {}
+static int keypad_cfg_read(struct gui_info *gui, const char *val) { return 0; }
+
+#else /* HAVE_SDL, the real rendering code */
+
+#include <SDL/SDL.h>
+#ifdef HAVE_SDL_IMAGE
+#include <SDL/SDL_image.h> /* for loading images */
+#endif
+
+enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
+struct keypad_entry {
+ int c; /* corresponding character */
+ int x0, y0, x1, y1, h; /* arguments */
+ enum kp_type type;
+};
+
+/* our representation of a displayed window. SDL can only do one main
+ * window so we map everything within that one
+ */
+struct display_window {
+ SDL_Overlay *bmp;
+ SDL_Rect rect; /* location of the window */
+};
+
+struct gui_info {
+ enum kb_output kb_output; /* where the keyboard output goes */
+ struct drag_info drag; /* info on the window are we dragging */
+ /* support for display. */
+ SDL_Surface *screen; /* the main window */
+
+ int outfd; /* fd for output */
+ SDL_Surface *keypad; /* the skin for the keypad */
+ SDL_Rect kp_rect; /* portion of the skin to display - default all */
+ SDL_Surface *font; /* font to be used */
+ SDL_Rect font_rects[96]; /* only printable chars */
+
+ /* each board has two rectangles,
+ * [0] is the geometry relative to the keypad,
+ * [1] is the geometry relative to the whole screen
+ */
+ SDL_Rect kp_msg[2]; /* incoming msg, relative to kpad */
+ struct board *bd_msg;
+
+ SDL_Rect kp_edit[2]; /* edit user input */
+ struct board *bd_edit;
+
+ SDL_Rect kp_dialed[2]; /* dialed number */
+ struct board *bd_dialed;
+
+ /* variable-size array mapping keypad regions to functions */
+ int kp_size, kp_used;
+ struct keypad_entry *kp;
+
+ struct display_window win[WIN_MAX];
+};
+
+/*! \brief free the resources in struct gui_info and the descriptor itself.
+ * Return NULL so we can assign the value back to the descriptor in case.
+ */
+static struct gui_info *cleanup_sdl(struct gui_info *gui)
+{
+ int i;
+
+ if (gui == NULL)
+ return NULL;
+
+ /* unload font file */
+ if (gui->font) {
+ SDL_FreeSurface(gui->font);
+ gui->font = NULL;
+ }
+
+ if (gui->outfd > -1)
+ close(gui->outfd);
+ if (gui->keypad)
+ SDL_FreeSurface(gui->keypad);
+ gui->keypad = NULL;
+ if (gui->kp)
+ ast_free(gui->kp);
+
+ /* uninitialize the SDL environment */
+ for (i = 0; i < WIN_MAX; i++) {
+ if (gui->win[i].bmp)
+ SDL_FreeYUVOverlay(gui->win[i].bmp);
+ }
+ bzero(gui, sizeof(gui));
+ ast_free(gui);
+ SDL_Quit();
+ return NULL;
+}
+
+/*
+ * Display video frames (from local or remote stream) using the SDL library.
+ * - Set the video mode to use the resolution specified by the codec context
+ * - Create a YUV Overlay to copy the frame into it;
+ * - After the frame is copied into the overlay, display it
+ *
+ * The size is taken from the configuration.
+ *
+ * 'out' is 0 for remote video, 1 for the local video
+ */
+static void show_frame(struct video_desc *env, int out)
+{
+ AVPicture *p_in, p_out;
+ struct fbuf_t *b_in, *b_out;
+ SDL_Overlay *bmp;
+ struct gui_info *gui = env->gui;
+
+ if (!gui)
+ return;
+
+ if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
+ b_in = &env->enc_in;
+ b_out = &env->loc_dpy;
+ p_in = NULL;
+ } else {
+ /* copy input format from the decoding context */
+ AVCodecContext *c;
+ if (env->in == NULL) /* XXX should not happen - decoder not ready */
+ return;
+ c = env->in->dec_ctx;
+ b_in = &env->in->dec_out;
+ b_in->pix_fmt = c->pix_fmt;
+ b_in->w = c->width;
+ b_in->h = c->height;
+
+ b_out = &env->rem_dpy;
+ p_in = (AVPicture *)env->in->d_frame;
+ }
+ bmp = gui->win[out].bmp;
+ SDL_LockYUVOverlay(bmp);
+ /* output picture info - this is sdl, YUV420P */
+ bzero(&p_out, sizeof(p_out));
+ p_out.data[0] = bmp->pixels[0];
+ p_out.data[1] = bmp->pixels[1];
+ p_out.data[2] = bmp->pixels[2];
+ p_out.linesize[0] = bmp->pitches[0];
+ p_out.linesize[1] = bmp->pitches[1];
+ p_out.linesize[2] = bmp->pitches[2];
+
+ my_scale(b_in, p_in, b_out, &p_out);
+
+ /* lock to protect access to Xlib by different threads. */
+ SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
+ SDL_UnlockYUVOverlay(bmp);
+}
+
+/*
+ * Identifiers for regions of the main window.
+ * Values between 0 and 127 correspond to ASCII characters.
+ * The corresponding strings to be used in the skin comment section
+ * are defined in gui_key_map.
+ */
+enum skin_area {
+ /* answer/close functions */
+ KEY_PICK_UP = 128,
+ KEY_HANG_UP = 129,
+
+ KEY_MUTE = 130,
+ KEY_AUTOANSWER = 131,
+ KEY_SENDVIDEO = 132,
+ KEY_LOCALVIDEO = 133,
+ KEY_REMOTEVIDEO = 134,
+ KEY_FLASH = 136,
+
+ /* sensitive areas for the various text windows */
+ KEY_MESSAGEBOARD = 140,
+ KEY_DIALEDBOARD = 141,
+ KEY_EDITBOARD = 142,
+
+ KEY_GUI_CLOSE = 199, /* close gui */
+ /* regions of the skin - displayed area, fonts, etc.
+ * XXX NOTE these are not sensitive areas.
+ */
+ KEY_KEYPAD = 200, /* the keypad - default to the whole image */
+ KEY_FONT = 201, /* the font. Maybe not really useful */
+ KEY_MESSAGE = 202, /* area for incoming messages */
+ KEY_DIALED = 203, /* area for dialed numbers */
+ KEY_EDIT = 204, /* area for editing user input */
+
+ /* areas outside the keypad - simulated */
+ KEY_OUT_OF_KEYPAD = 241,
+ KEY_REM_DPY = 242,
+ KEY_LOC_DPY = 243,
+ KEY_RESET = 253, /* the 'reset' keyword */
+ KEY_NONE = 254, /* invalid area */
+ KEY_DIGIT_BACKGROUND = 255, /* other areas within the keypad */
+};
+
+/*
+ * Handlers for the various keypad functions
+ */
+
+/* accumulate digits, possibly call dial if in connected mode */
+static void keypad_digit(struct video_desc *env, int digit)
+{
+ if (env->owner) { /* we have a call, send the digit */
+ struct ast_frame f = { AST_FRAME_DTMF, 0 };
+
+ f.subclass = digit;
+ ast_queue_frame(env->owner, &f);
+ } else { /* no call, accumulate digits */
+ char buf[2] = { digit, '\0' };
+ if (env->gui->bd_msg) /* XXX not strictly necessary ... */
+ print_message(env->gui->bd_msg, buf);
+ }
+}
+
+/* function used to toggle on/off the status of some variables */
+static char *keypad_toggle(struct video_desc *env, int index)
+{
+ ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
+
+ switch (index) {
+ case KEY_SENDVIDEO:
+ env->out.sendvideo = !env->out.sendvideo;
+ break;
+#ifdef notyet
+ case KEY_MUTE: {
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ o->mute = !o->mute;
+ }
+ break;
+ case KEY_AUTOANSWER: {
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ o->autoanswer = !o->autoanswer;
+ }
+ break;
+#endif
+ }
+ return NULL;
+}
+
+char *console_do_answer(int fd);
+/*
+ * Function called when the pick up button is pressed
+ * perform actions according the channel status:
+ *
+ * - if no one is calling us and no digits was pressed,
+ * the operation have no effects,
+ * - if someone is calling us we answer to the call.
+ * - if we have no call in progress and we pressed some
+ * digit, send the digit to the console.
+ */
+static void keypad_pick_up(struct video_desc *env)
+{
+ struct gui_info *gui = env->gui;
+
+ ast_log(LOG_WARNING, "keypad_pick_up called\n");
+
+ if (env->owner) { /* someone is calling us, just answer */
+ ast_cli_command(gui->outfd, "console answer");
+ } else { /* we have someone to call */
+ char buf[160];
+ const char *who = ast_skip_blanks(read_message(gui->bd_msg));
+ buf[sizeof(buf) - 1] = '\0';
+ snprintf(buf, sizeof(buf) - 1, "console dial %s", who);
+ ast_log(LOG_WARNING, "doing <%s>\n", buf);
+ print_message(gui->bd_dialed, "\n");
+ print_message(gui->bd_dialed, who);
+ reset_board(gui->bd_msg);
+ ast_cli_command(gui->outfd, buf);
+ }
+}
+
+#if 0 /* still unused */
+/*
+ * As an alternative to SDL_TTF, we can simply load the font from
+ * an image and blit characters on the background of the GUI.
+ *
+ * To generate a font we can use the 'fly' command with the
+ * following script (3 lines with 32 chars each)
+
+size 320,64
+name font.png
+transparent 0,0,0
+string 255,255,255, 0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
+string 255,255,255, 0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+string 255,255,255, 0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
+end
+
+ */
+
+/* Print given text on the gui */
+static int gui_output(struct video_desc *env, const char *text)
+{
+ return 1; /* error, not supported */
+}
+#endif
+
+static int video_geom(struct fbuf_t *b, const char *s);
+static void sdl_setup(struct video_desc *env);
+static int kp_match_area(const struct keypad_entry *e, int x, int y);
+
+static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
+{
+ drag->x_start = x;
+ drag->y_start = y;
+ drag->drag_window = win;
+}
+
+/*
+ * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
+ * index value and calling the right callback.
+ *
+ * x, y are referred to the upper left corner of the main SDL window.
+ */
+static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button)
+{
+ uint8_t index = KEY_OUT_OF_KEYPAD; /* the key or region of the display we clicked on */
+ struct gui_info *gui = env->gui;
+
+#if 0
+ ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
+ button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
+#endif
+ /* for each mousedown we end previous drag */
+ gui->drag.drag_window = DRAG_NONE;
+
+ /* define keypad boundary */
+ if (button.x < env->rem_dpy.w)
+ index = KEY_REM_DPY; /* click on remote video */
+ else if (button.x > env->rem_dpy.w + gui->keypad->w)
+ index = KEY_LOC_DPY; /* click on local video */
+ else if (button.y > gui->keypad->h)
+ index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
+ else if (gui->kp) {
+ int i;
+ for (i = 0; i < gui->kp_used; i++) {
+ if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
+ index = gui->kp[i].c;
+ break;
+ }
+ }
+ }
+
+ /* exec the function */
+ if (index < 128) { /* surely clicked on the keypad, don't care which key */
+ keypad_digit(env, index);
+ return;
+ }
+ switch (index) {
+ /* answer/close function */
+ case KEY_PICK_UP:
+ keypad_pick_up(env);
+ break;
+ case KEY_HANG_UP:
+ ast_cli_command(gui->outfd, "console hangup");
+ break;
+
+ /* other functions */
+ case KEY_MUTE:
+ case KEY_AUTOANSWER:
+ case KEY_SENDVIDEO:
+ keypad_toggle(env, index);
+ break;
+
+ case KEY_LOCALVIDEO:
+ break;
+ case KEY_REMOTEVIDEO:
+ break;
+
+ case KEY_MESSAGEBOARD:
+ if (button.button == SDL_BUTTON_LEFT)
+ set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
+ break;
+
+ /* press outside the keypad. right increases size, center decreases, left drags */
+ case KEY_LOC_DPY:
+ case KEY_REM_DPY:
+ if (button.button == SDL_BUTTON_LEFT) {
+ if (index == KEY_LOC_DPY)
+ set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
+ break;
+ } else {
+ char buf[128];
+ struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
+ sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
+ fb->w, fb->h);
+ video_geom(fb, buf);
+ sdl_setup(env);
+ }
+ break;
+ case KEY_OUT_OF_KEYPAD:
+ break;
+
+ case KEY_DIGIT_BACKGROUND:
+ break;
+ default:
+ ast_log(LOG_WARNING, "function not yet defined %i\n", index);
+ }
+}
+
+/*
+ * Handle SDL_KEYDOWN type event, put the key pressed
+ * in the dial buffer or in the text-message buffer,
+ * depending on the text_mode variable value.
+ *
+ * key is the SDLKey structure corresponding to the key pressed.
+ * Note that SDL returns modifiers (ctrl, shift, alt) as independent
+ * information so the key itself is not enough and we need to
+ * use a translation table, below - one line per entry,
+ * plain, shift, ctrl, ... using the first char as key.
+ */
+static const char *us_kbd_map[] = {
+ "`~", "1!", "2@", "3#", "4$", "5%", "6^",
+ "7&", "8*", "9(", "0)", "-_", "=+", "[{",
+ "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
+ "jJ\n",
+ NULL
+};
+
+static char map_key(SDL_keysym *ks)
+{
+ const char *s, **p = us_kbd_map;
+ int c = ks->sym;
+
+ if (c == '\r') /* map cr into lf */
+ c = '\n';
+ if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
+ return 0; /* only a modifier */
+ if (ks->mod == 0)
+ return c;
+ while ((s = *p) && s[0] != c)
+ p++;
+ if (s) { /* see if we have a modifier and a chance to use it */
+ int l = strlen(s), mod = 0;
+ if (l > 1)
+ mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
+ if (l > 2 + mod)
+ mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
+ if (l > 4 + mod)
+ mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
+ c = s[mod];
+ }
+ if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
+ c += 'A' - 'a';
+ return c;
+}
+
+static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
+{
+ char buf[2] = { map_key(ks), '\0' };
+ struct gui_info *gui = env->gui;
+ if (buf[0] == 0) /* modifier ? */
+ return;
+ switch (gui->kb_output) {
+ default:
+ break;
+ case KO_INPUT: /* to be completed */
+ break;
+ case KO_MESSAGE:
+ if (gui->bd_msg) {
+ print_message(gui->bd_msg, buf);
+ if (buf[0] == '\r' || buf[0] == '\n') {
+ keypad_pick_up(env);
+ }
+ }
+ break;
+
+ case KO_DIALED: /* to be completed */
+ break;
+ }
+
+ return;
+}
+
+static void grabber_move(struct video_out_desc *, int dx, int dy);
+
+int compute_drag(int *start, int end, int magnifier);
+int compute_drag(int *start, int end, int magnifier)
+{
+ int delta = end - *start;
+#define POLARITY -1
+ /* add a small quadratic term */
+ delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
+ delta *= POLARITY * magnifier;
+#undef POLARITY
+ *start = end;
+ return delta;
+}
+
+/*
+ * I am seeing some kind of deadlock or stall around
+ * SDL_PumpEvents() while moving the window on a remote X server
+ * (both xfree-4.4.0 and xorg 7.2)
+ * and windowmaker. It is unclear what causes it.
+ */
+
+/*! \brief refresh the screen, and also grab a bunch of events.
+ */
+static void eventhandler(struct video_desc *env, const char *caption)
+{
+ struct gui_info *gui = env->gui;
+ struct drag_info *drag;
+#define N_EVENTS 32
+ int i, n;
+ SDL_Event ev[N_EVENTS];
+
+ if (!gui)
+ return;
+ drag = &gui->drag;
+ if (caption)
+ SDL_WM_SetCaption(caption, NULL);
+
+#define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
+ while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
+ for (i = 0; i < n; i++) {
+#if 0
+ ast_log(LOG_WARNING, "------ event %d at %d %d\n",
+ ev[i].type, ev[i].button.x, ev[i].button.y);
+#endif
+ switch (ev[i].type) {
+ case SDL_KEYDOWN:
+ handle_keyboard_input(env, &ev[i].key.keysym);
+ break;
+ case SDL_MOUSEMOTION:
+ case SDL_MOUSEBUTTONUP:
+ if (drag->drag_window == DRAG_LOCAL) {
+ /* move the capture source */
+ int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3);
+ int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3);
+ grabber_move(&env->out, dx, dy);
+ } else if (drag->drag_window == DRAG_MESSAGE) {
+ /* scroll up/down the window */
+ int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
+ move_message_board(gui->bd_msg, dy);
+ }
+ if (ev[i].type == SDL_MOUSEBUTTONUP)
+ drag->drag_window = DRAG_NONE;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ handle_mousedown(env, ev[i].button);
+ break;
+ }
+ }
+ }
+ if (1) {
+ struct timeval b, a = ast_tvnow();
+ int i;
+ //SDL_Lock_EventThread();
+ SDL_PumpEvents();
+ b = ast_tvnow();
+ i = ast_tvdiff_ms(b, a);
+ if (i > 3)
+ fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
+ //SDL_Unlock_EventThread();
+ }
+}
+
+static SDL_Surface *load_image(const char *file)
+{
+ SDL_Surface *temp;
+
+#ifdef HAVE_SDL_IMAGE
+ temp = IMG_Load(file);
+#else
+ temp = SDL_LoadBMP(file);
+#endif
+ if (temp == NULL)
+ fprintf(stderr, "Unable to load image %s: %s\n",
+ file, SDL_GetError());
+ return temp;
+}
+
+static void keypad_setup(struct gui_info *gui, const char *kp_file);
+
+/* TODO: consistency checks, check for bpp, widht and height */
+/* Init the mask image used to grab the action. */
+static struct gui_info *gui_init(const char *keypad_file, const char *font)
+{
+ struct gui_info *gui = ast_calloc(1, sizeof(*gui));
+
+ if (gui == NULL)
+ return NULL;
+ /* initialize keypad status */
+ gui->kb_output = KO_MESSAGE; /* XXX temp */
+ gui->drag.drag_window = DRAG_NONE;
+ gui->outfd = -1;
+
+ keypad_setup(gui, keypad_file);
+ if (gui->keypad == NULL) /* no keypad, we are done */
+ return gui;
+ /* XXX load image */
+ if (!ast_strlen_zero(font)) {
+ int i;
+ SDL_Rect *r;
+
+ gui->font = load_image(font);
+ if (!gui->font) {
+ ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
+ goto error;
+ }
+ ast_log(LOG_WARNING, "Loaded font %s\n", font);
+ /* XXX hardwired constants - 3 rows of 32 chars */
+ r = gui->font_rects;
+#define FONT_H 20
+#define FONT_W 9
+ for (i = 0; i < 96; r++, i++) {
+ r->x = (i % 32 ) * FONT_W;
+ r->y = (i / 32 ) * FONT_H;
+ r->w = FONT_W;
+ r->h = FONT_H;
+ }
+ }
+
+ gui->outfd = open ("/dev/null", O_WRONLY); /* discard output, temporary */
+ if (gui->outfd < 0) {
+ ast_log(LOG_WARNING, "Unable output fd\n");
+ goto error;
+ }
+ return gui;
+
+error:
+ ast_free(gui);
+ return NULL;
+}
+
+/* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
+static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
+ int w, int h, int x, int y)
+{
+ win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
+ if (win->bmp == NULL)
+ return -1; /* error */
+ win->rect.x = x;
+ win->rect.y = y;
+ win->rect.w = w;
+ win->rect.h = h;
+ return 0;
+}
+
+static int keypad_cfg_read(struct gui_info *gui, const char *val);
+
+static void keypad_setup(struct gui_info *gui, const char *kp_file)
+{
+ FILE *fd;
+ char buf[1024];
+ const char region[] = "region";
+ int reg_len = strlen(region);
+ int in_comment = 0;
+
+ if (gui->keypad)
+ return;
+ gui->keypad = load_image(kp_file);
+ if (!gui->keypad)
+ return;
+ /* now try to read the keymap from the file. */
+ fd = fopen(kp_file, "r");
+ if (fd == NULL) {
+ ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
+ return;
+ }
+ /*
+ * If the keypad image has a comment field, try to read
+ * the button location from there. The block must start with
+ * a comment (or empty) line, and continue with entries like:
+ * region = token shape x0 y0 x1 y1 h
+ * ...
+ * (basically, lines have the same format as config file entries).
+ * You can add it to a jpeg file using wrjpgcom
+ */
+ while (fgets(buf, sizeof(buf), fd)) {
+ char *s;
+
+ if (!strstr(buf, region)) { /* no keyword yet */
+ if (!in_comment) /* still waiting for initial comment block */
+ continue;
+ else
+ break;
+ }
+ if (!in_comment) { /* first keyword, reset previous entries */
+ keypad_cfg_read(gui, "reset");
+ in_comment = 1;
+ }
+ s = ast_skip_blanks(buf);
+ ast_trim_blanks(s);
+ if (memcmp(s, region, reg_len))
+ break; /* keyword not found */
+ s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
+ if (*s++ != '=') /* missing separator */
+ break;
+ if (*s == '>') /* skip '>' if present */
+ s++;
+ keypad_cfg_read(gui, ast_skip_blanks(s));
+ }
+ fclose(fd);
+}
+
+struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
+ SDL_Surface *font, SDL_Rect *font_rects);
+
+/*! \brief initialize the boards we have in the keypad */
+static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
+{
+ if (r[0].w == 0 || r[0].h == 0)
+ return; /* not available */
+ r[1] = r[0]; /* copy geometry */
+ r[1].x += dx; /* add offset of main window */
+ r[1].y += dy;
+ if (*dst == NULL) { /* initial call */
+ *dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
+ } else {
+ /* call a refresh */
+ }
+}
+
+/*! \brief [re]set the main sdl window, useful in case of resize.
+ * We can tell the first from subsequent calls from the value of
+ * env->gui, which is NULL the first time.
+ */
+static void sdl_setup(struct video_desc *env)
+{
+ int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
+ int depth, maxw, maxh;
+ const SDL_VideoInfo *info;
+ int kp_w = 0, kp_h = 0; /* keypad width and height */
+ struct gui_info *gui = env->gui;
+
+ /*
+ * initialize the SDL environment. We have one large window
+ * with local and remote video, and a keypad.
+ * At the moment we arrange them statically, as follows:
+ * - on the left, the remote video;
+ * - on the center, the keypad
+ * - on the right, the local video
+ * We need to read in the skin for the keypad before creating the main
+ * SDL window, because the size is only known here.
+ */
+
+ if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
+ ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
+ SDL_GetError());
+ /* again not fatal, just we won't display anything */
+ return;
+ }
+ info = SDL_GetVideoInfo();
+ /* We want at least 16bpp to support YUV overlays.
+ * E.g with SDL_VIDEODRIVER = aalib the default is 8
+ */
+ depth = info->vfmt->BitsPerPixel;
+ if (depth < 16)
+ depth = 16;
+ if (!gui)
+ env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
+ if (!gui)
+ goto no_sdl;
+
+ if (gui->keypad) {
+ if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
+ kp_w = gui->kp_rect.w;
+ kp_h = gui->kp_rect.h;
+ } else {
+ kp_w = gui->keypad->w;
+ kp_h = gui->keypad->h;
+ }
+ }
+ /* XXX same for other boards */
+#define BORDER 5 /* border around our windows */
+ maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
+ maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
+ maxw += 4 * BORDER;
+ maxh += 2 * BORDER;
+ gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
+ if (!gui->screen) {
+ ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
+ goto no_sdl;
+ }
+
+ SDL_WM_SetCaption("Asterisk console Video Output", NULL);
+ if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
+ env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
+ goto no_sdl;
+ if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
+ env->loc_dpy.w, env->loc_dpy.h,
+ 3*BORDER+env->rem_dpy.w + kp_w, BORDER))
+ goto no_sdl;
+
+ /* display the skin, but do not free it as we need it later to
+ * restore text areas and maybe sliders too.
+ */
+ if (gui->keypad) {
+ struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
+ struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL;
+ /* set the coordinates of the keypad relative to the main screen */
+ dest->x = 2*BORDER + env->rem_dpy.w;
+ dest->y = BORDER;
+ dest->w = kp_w;
+ dest->h = kp_h;
+ SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
+ init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
+ init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
+ SDL_UpdateRects(gui->screen, 1, dest);
+ }
+ return;
+
+no_sdl:
+ /* free resources in case of errors */
+ env->gui = cleanup_sdl(gui);
+}
+
+/*
+ * Functions to determine if a point is within a region. Return 1 if success.
+ * First rotate the point, with
+ * x' = (x - x0) * cos A + (y - y0) * sin A
+ * y' = -(x - x0) * sin A + (y - y0) * cos A
+ * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
+ * l = sqrt( (x1-x0)^2 + (y1-y0)^2
+ * Then determine inclusion by simple comparisons i.e.:
+ * rectangle: x >= 0 && x < l && y >= 0 && y < h
+ * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
+ */
+static int kp_match_area(const struct keypad_entry *e, int x, int y)
+{
+ double xp, dx = (e->x1 - e->x0);
+ double yp, dy = (e->y1 - e->y0);
+ double l = sqrt(dx*dx + dy*dy);
+ int ret = 0;
+
+ if (l > 1) { /* large enough */
+ xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
+ yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
+ if (e->type == KP_RECT) {
+ ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
+ } else if (e->type == KP_CIRCLE) {
+ dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
+ ret = (dx < 1);
+ }
+ }
+#if 0
+ ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
+ ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
+#endif
+ return ret;
+}
+
+struct _s_k { const char *s; int k; };
+static struct _s_k gui_key_map[] = {
+ {"PICK_UP", KEY_PICK_UP },
+ {"PICKUP", KEY_PICK_UP },
+ {"HANG_UP", KEY_HANG_UP },
+ {"HANGUP", KEY_HANG_UP },
+ {"MUTE", KEY_MUTE },
+ {"FLASH", KEY_FLASH },
+ {"AUTOANSWER", KEY_AUTOANSWER },
+ {"SENDVIDEO", KEY_SENDVIDEO },
+ {"LOCALVIDEO", KEY_LOCALVIDEO },
+ {"REMOTEVIDEO", KEY_REMOTEVIDEO },
+ {"GUI_CLOSE", KEY_GUI_CLOSE },
+ {"MESSAGEBOARD", KEY_MESSAGEBOARD },
+ {"DIALEDBOARD", KEY_DIALEDBOARD },
+ {"EDITBOARD", KEY_EDITBOARD },
+ {"KEYPAD", KEY_KEYPAD }, /* x0 y0 w h - active area of the keypad */
+ {"MESSAGE", KEY_MESSAGE }, /* x0 y0 w h - incoming messages */
+ {"DIALED", KEY_DIALED }, /* x0 y0 w h - dialed number */
+ {"EDIT", KEY_EDIT }, /* x0 y0 w h - edit user input */
+ {"FONT", KEY_FONT }, /* x0 yo w h rows cols - location and format of the font */
+ {NULL, 0 } };
+
+static int gui_map_token(const char *s)
+{
+ /* map the string into token to be returned */
+ int i = atoi(s);
+ struct _s_k *p;
+ if (i > 0 || s[1] == '\0') /* numbers or single characters */
+ return (i > 9) ? i : s[0];
+ for (p = gui_key_map; p->s; p++) {
+ if (!strcasecmp(p->s, s))
+ return p->k;
+ }
+ return KEY_NONE; /* not found */
+}
+
+/*! \brief read a keypad entry line in the format
+ * reset
+ * token circle xc yc diameter
+ * token circle xc yc x1 y1 h # ellipse, main diameter and height
+ * token rect x0 y0 x1 y1 h # rectangle with main side and eight
+ * token is the token to be returned, either a character or a symbol
+ * as KEY_* above
+ * Return 1 on success, 0 on error.
+ */
+static int keypad_cfg_read(struct gui_info *gui, const char *val)
+{
+ struct keypad_entry e;
+ SDL_Rect *r = NULL;
+ char s1[16], s2[16];
+ int i, ret = 0; /* default, error */
+
+ if (gui == NULL || val == NULL)
+ return 0;
+
+ s1[0] = s2[0] = '\0';
+ bzero(&e, sizeof(e));
+ i = sscanf(val, "%14s %14s %d %d %d %d %d",
+ s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
+
+ e.c = gui_map_token(s1);
+ if (e.c == KEY_NONE)
+ return 0; /* nothing found */
+ switch (i) {
+ default:
+ break;
+ case 1: /* only "reset" is allowed */
+ if (e.c != KEY_RESET)
+ break;
+ if (gui->kp)
+ gui->kp_used = 0;
+ break;
+ case 5:
+ if (e.c == KEY_KEYPAD) /* active keypad area */
+ r = &gui->kp_rect;
+ else if (e.c == KEY_MESSAGE)
+ r = gui->kp_msg;
+ else if (e.c == KEY_DIALED)
+ r = gui->kp_dialed;
+ else if (e.c == KEY_EDIT)
+ r = gui->kp_edit;
+ if (r) {
+ r->x = atoi(s2);
+ r->y = e.x0;
+ r->w = e.y0;
+ r->h = e.x1;
+ break;
+ }
+ if (strcasecmp(s2, "circle")) /* invalid */
+ break;
+ /* token circle xc yc diameter */
+ e.h = e.x1;
+ e.y1 = e.y0; /* map radius in x1 y1 */
+ e.x1 = e.x0 + e.h; /* map radius in x1 y1 */
+ e.x0 = e.x0 - e.h; /* map radius in x1 y1 */
+ /* fallthrough */
+
+ case 7:
+ if (e.c == KEY_FONT) { /* font - x0 y0 w h rows cols */
+ ast_log(LOG_WARNING, "font not supported yet\n");
+ break;
+ }
+ /* token circle|rect x0 y0 x1 y1 h */
+ if (e.x1 < e.x0 || e.h <= 0) {
+ ast_log(LOG_WARNING, "error in coordinates\n");
+ e.type = 0;
+ break;
+ }
+ if (!strcasecmp(s2, "circle")) {
+ /* for a circle we specify the diameter but store center and radii */
+ e.type = KP_CIRCLE;
+ e.x0 = (e.x1 + e.x0) / 2;
+ e.y0 = (e.y1 + e.y0) / 2;
+ e.h = e.h / 2;
+ } else if (!strcasecmp(s2, "rect")) {
+ e.type = KP_RECT;
+ } else
+ break;
+ ret = 1;
+ }
+ // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
+ if (ret == 0)
+ return 0;
+ if (gui->kp_size == 0) {
+ gui->kp = ast_calloc(10, sizeof(e));
+ if (gui->kp == NULL) {
+ ast_log(LOG_WARNING, "cannot allocate kp");
+ return 0;
+ }
+ gui->kp_size = 10;
+ }
+ if (gui->kp_size == gui->kp_used) { /* must allocate */
+ struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
+ if (a == NULL) {
+ ast_log(LOG_WARNING, "cannot reallocate kp");
+ return 0;
+ }
+ gui->kp = a;
+ gui->kp_size += 10;
+ }
+ if (gui->kp_size == gui->kp_used)
+ return 0;
+ gui->kp[gui->kp_used++] = e;
+ // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
+ return 1;
+}
+#endif /* HAVE_SDL */
diff --git a/trunk/channels/console_video.c b/trunk/channels/console_video.c
new file mode 100644
index 000000000..554d03c39
--- /dev/null
+++ b/trunk/channels/console_video.c
@@ -0,0 +1,1035 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007-2008, Marta Carbone, Sergio Fadda, Luigi Rizzo
+ *
+ * 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.
+ */
+
+/*
+ * Experimental support for video sessions. We use SDL for rendering, ffmpeg
+ * as the codec library for encoding and decoding, and Video4Linux and X11
+ * to generate the local video stream.
+ *
+ * If one of these pieces is not available, either at compile time or at
+ * runtime, we do our best to run without it. Of course, no codec library
+ * means we can only deal with raw data, no SDL means we cannot do rendering,
+ * no V4L or X11 means we cannot generate data (but in principle we could
+ * stream from or record to a file).
+ *
+ * We need a recent (2007.07.12 or newer) version of ffmpeg to avoid warnings.
+ * Older versions might give 'deprecated' messages during compilation,
+ * thus not compiling in AST_DEVMODE, or don't have swscale, in which case
+ * you can try to compile #defining OLD_FFMPEG here.
+ *
+ * $Revision$
+ */
+
+//#define DROP_PACKETS 5 /* if set, drop this % of video packets */
+//#define OLD_FFMPEG 1 /* set for old ffmpeg with no swscale */
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include <sys/ioctl.h>
+#include "asterisk/cli.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+
+#include "console_video.h"
+
+/*
+The code is structured as follows.
+
+When a new console channel is created, we call console_video_start()
+to initialize SDL, the source, and the encoder/ decoder for the
+formats in use (XXX the latter two should be done later, once the
+codec negotiation is complete). Also, a thread is created to handle
+the video source and generate frames.
+
+While communication is on, the local source is generated by the
+video thread, which wakes up periodically, generates frames and
+enqueues them in chan->readq. Incoming rtp frames are passed to
+console_write_video(), decoded and passed to SDL for display.
+
+For as unfortunate and confusing as it can be, we need to deal with a
+number of different video representations (size, codec/pixel format,
+codec parameters), as follows:
+
+ loc_src is the data coming from the camera/X11/etc.
+ The format is typically constrained by the video source.
+
+ enc_in is the input required by the encoder.
+ Typically constrained in size by the encoder type.
+
+ enc_out is the bitstream transmitted over RTP.
+ Typically negotiated while the call is established.
+
+ loc_dpy is the format used to display the local video source.
+ Depending on user preferences this can have the same size as
+ loc_src_fmt, or enc_in_fmt, or thumbnail size (e.g. PiP output)
+
+ dec_in is the incoming RTP bitstream. Negotiated
+ during call establishment, it is not necessarily the same as
+ enc_in_fmt
+
+ dec_out the output of the decoder.
+ The format is whatever the other side sends, and the
+ buffer is allocated by avcodec_decode_... so we only
+ copy the data here.
+
+ rem_dpy the format used to display the remote stream
+
+We store the format info together with the buffer storing the data.
+As a future optimization, a format/buffer may reference another one
+if the formats are equivalent. This will save some unnecessary format
+conversion.
+
+
+In order to handle video you need to add to sip.conf (and presumably
+iax.conf too) the following:
+
+ [general](+)
+ videosupport=yes
+ allow=h263 ; this or other video formats
+ allow=h263p ; this or other video formats
+
+ */
+
+/*
+ * Codecs are absolutely necessary or we cannot do anything.
+ * SDL is optional (used for rendering only), so that we can still
+ * stream video withouth displaying it.
+ */
+#if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG)
+/* stubs if required pieces are missing */
+int console_write_video(struct ast_channel *chan, struct ast_frame *f)
+{
+ return 0; /* writing video not supported */
+}
+
+int console_video_cli(struct video_desc *env, const char *var, int fd)
+{
+ return 1; /* nothing matched */
+}
+
+int console_video_config(struct video_desc **penv, const char *var, const char *val)
+{
+ return 1; /* no configuration */
+}
+
+void console_video_start(struct video_desc *env, struct ast_channel *owner)
+{
+ ast_log(LOG_NOTICE, "voice only, console video support not present\n");
+}
+
+void console_video_uninit(struct video_desc *env)
+{
+}
+
+int console_video_formats = 0;
+
+#else /* defined(HAVE_FFMPEG) && defined(HAVE_SDL) */
+
+/*! The list of video formats we support. */
+int console_video_formats =
+ AST_FORMAT_H263_PLUS | AST_FORMAT_H263 |
+ AST_FORMAT_MP4_VIDEO | AST_FORMAT_H264 | AST_FORMAT_H261 ;
+
+
+
+static void my_scale(struct fbuf_t *in, AVPicture *p_in,
+ struct fbuf_t *out, AVPicture *p_out);
+
+struct video_codec_desc; /* forward declaration */
+/*
+ * Descriptor of the local source, made of the following pieces:
+ * + configuration info (geometry, device name, fps...). These are read
+ * from the config file and copied here before calling video_out_init();
+ * + the frame buffer (buf) and source pixel format, allocated at init time;
+ * + the encoding and RTP info, including timestamps to generate
+ * frames at the correct rate;
+ * + source-specific info, i.e. fd for /dev/video, dpy-image for x11, etc,
+ * filled in by grabber_open
+ * NOTE: loc_src.data == NULL means the rest of the struct is invalid, and
+ * the video source is not available.
+ */
+struct video_out_desc {
+ /* video device support.
+ * videodevice and geometry are read from the config file.
+ * At the right time we try to open it and allocate a buffer.
+ * If we are successful, webcam_bufsize > 0 and we can read.
+ */
+ /* all the following is config file info copied from the parent */
+ char videodevice[64];
+ int fps;
+ int bitrate;
+ int qmin;
+
+ int sendvideo;
+
+ struct fbuf_t loc_src_geometry; /* local source geometry only (from config file) */
+ struct fbuf_t enc_out; /* encoder output buffer, allocated in video_out_init() */
+
+ struct video_codec_desc *enc; /* encoder */
+ void *enc_ctx; /* encoding context */
+ AVCodec *codec;
+ AVFrame *enc_in_frame; /* enc_in mapped into avcodec format. */
+ /* The initial part of AVFrame is an AVPicture */
+ int mtu;
+ struct timeval last_frame; /* when we read the last frame ? */
+
+ struct grab_desc *grabber;
+ void *grabber_data;
+};
+
+/*
+ * The overall descriptor, with room for config info, video source and
+ * received data descriptors, SDL info, etc.
+ * This should be globally visible to all modules (grabber, vcodecs, gui)
+ * and contain all configurtion info.
+ */
+struct video_desc {
+ char codec_name[64]; /* the codec we use */
+
+ int stayopen; /* set if gui starts manually */
+ pthread_t vthread; /* video thread */
+ ast_mutex_t dec_lock; /* sync decoder and video thread */
+ int shutdown; /* set to shutdown vthread */
+ struct ast_channel *owner; /* owner channel */
+
+
+ struct fbuf_t enc_in; /* encoder input buffer, allocated in video_out_init() */
+
+ char keypad_file[256]; /* image for the keypad */
+ char keypad_font[256]; /* font for the keypad */
+
+ char sdl_videodriver[256];
+
+ struct fbuf_t rem_dpy; /* display remote video, no buffer (it is in win[WIN_REMOTE].bmp) */
+ struct fbuf_t loc_dpy; /* display local source, no buffer (managed by SDL in bmp[1]) */
+
+
+ /* local information for grabbers, codecs, gui */
+ struct gui_info *gui;
+ struct video_dec_desc *in; /* remote video descriptor */
+ struct video_out_desc out; /* local video descriptor */
+};
+
+static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p);
+
+void fbuf_free(struct fbuf_t *b)
+{
+ struct fbuf_t x = *b;
+
+ if (b->data && b->size)
+ ast_free(b->data);
+ bzero(b, sizeof(*b));
+ /* restore some fields */
+ b->w = x.w;
+ b->h = x.h;
+ b->pix_fmt = x.pix_fmt;
+}
+
+#include "vcodecs.c"
+#include "console_gui.c"
+
+/*! \brief Try to open a video source, return 0 on success, 1 on error */
+static int grabber_open(struct video_out_desc *v)
+{
+ struct grab_desc *g;
+ void *g_data;
+ int i;
+
+ for (i = 0; (g = console_grabbers[i]); i++) {
+ g_data = g->open(v->videodevice, &v->loc_src_geometry, v->fps);
+ if (g_data) {
+ v->grabber = g;
+ v->grabber_data = g_data;
+ return 0;
+ }
+ }
+ return 1; /* no source found */
+}
+
+/*! \brief complete a buffer from the local video source.
+ * Called by get_video_frames(), in turn called by the video thread.
+ */
+static struct fbuf_t *grabber_read(struct video_out_desc *v)
+{
+ struct timeval now = ast_tvnow();
+
+ if (v->grabber == NULL) /* not initialized */
+ return 0;
+
+ /* check if it is time to read */
+ if (ast_tvzero(v->last_frame))
+ v->last_frame = now;
+ if (ast_tvdiff_ms(now, v->last_frame) < 1000/v->fps)
+ return 0; /* too early */
+ v->last_frame = now; /* XXX actually, should correct for drift */
+ return v->grabber->read(v->grabber_data);
+}
+
+/*! \brief handler run when dragging with the left button on
+ * the local source window - the effect is to move the offset
+ * of the captured area.
+ */
+static void grabber_move(struct video_out_desc *v, int dx, int dy)
+{
+ if (v->grabber && v->grabber->move)
+ v->grabber->move(v->grabber_data, dx, dy);
+}
+
+/*
+ * Map the codec name to the library. If not recognised, use a default.
+ * This is useful in the output path where we decide by name, presumably.
+ */
+static struct video_codec_desc *map_config_video_format(char *name)
+{
+ int i;
+
+ for (i = 0; supported_codecs[i]; i++)
+ if (!strcasecmp(name, supported_codecs[i]->name))
+ break;
+ if (supported_codecs[i] == NULL) {
+ ast_log(LOG_WARNING, "Cannot find codec for '%s'\n", name);
+ i = 0;
+ strcpy(name, supported_codecs[i]->name);
+ }
+ ast_log(LOG_WARNING, "Using codec '%s'\n", name);
+ return supported_codecs[i];
+}
+
+
+/*! \brief uninitialize the descriptor for local video stream */
+static int video_out_uninit(struct video_desc *env)
+{
+ struct video_out_desc *v = &env->out;
+
+ /* XXX this should be a codec callback */
+ if (v->enc_ctx) {
+ AVCodecContext *enc_ctx = (AVCodecContext *)v->enc_ctx;
+ avcodec_close(enc_ctx);
+ av_free(enc_ctx);
+ v->enc_ctx = NULL;
+ }
+ if (v->enc_in_frame) {
+ av_free(v->enc_in_frame);
+ v->enc_in_frame = NULL;
+ }
+ v->codec = NULL; /* nothing to free, this is only a reference */
+ /* release the buffers */
+ fbuf_free(&env->enc_in);
+ fbuf_free(&v->enc_out);
+ /* close the grabber */
+ if (v->grabber) {
+ v->grabber_data = v->grabber->close(v->grabber_data);
+ v->grabber = NULL;
+ }
+ return -1;
+}
+
+/*
+ * Initialize the encoder for the local source:
+ * - enc_ctx, codec, enc_in_frame are used by ffmpeg for encoding;
+ * - enc_out is used to store the encoded frame (to be sent)
+ * - mtu is used to determine the max size of video fragment
+ * NOTE: we enter here with the video source already open.
+ */
+static int video_out_init(struct video_desc *env)
+{
+ int codec;
+ int size;
+ struct fbuf_t *enc_in;
+ struct video_out_desc *v = &env->out;
+
+ v->enc_ctx = NULL;
+ v->codec = NULL;
+ v->enc_in_frame = NULL;
+ v->enc_out.data = NULL;
+
+ codec = map_video_format(v->enc->format, CM_WR);
+ v->codec = avcodec_find_encoder(codec);
+ if (!v->codec) {
+ ast_log(LOG_WARNING, "Cannot find the encoder for format %d\n",
+ codec);
+ return -1; /* error, but nothing to undo yet */
+ }
+
+ v->mtu = 1400; /* set it early so the encoder can use it */
+
+ /* allocate the input buffer for encoding.
+ * ffmpeg only supports PIX_FMT_YUV420P for the encoding.
+ */
+ enc_in = &env->enc_in;
+ enc_in->pix_fmt = PIX_FMT_YUV420P;
+ enc_in->size = (enc_in->w * enc_in->h * 3)/2;
+ enc_in->data = ast_calloc(1, enc_in->size);
+ if (!enc_in->data) {
+ ast_log(LOG_WARNING, "Cannot allocate encoder input buffer\n");
+ return video_out_uninit(env);
+ }
+ /* construct an AVFrame that points into buf_in */
+ v->enc_in_frame = avcodec_alloc_frame();
+ if (!v->enc_in_frame) {
+ ast_log(LOG_WARNING, "Unable to allocate the encoding video frame\n");
+ return video_out_uninit(env);
+ }
+
+ /* parameters for PIX_FMT_YUV420P */
+ size = enc_in->w * enc_in->h;
+ v->enc_in_frame->data[0] = enc_in->data;
+ v->enc_in_frame->data[1] = v->enc_in_frame->data[0] + size;
+ v->enc_in_frame->data[2] = v->enc_in_frame->data[1] + size/4;
+ v->enc_in_frame->linesize[0] = enc_in->w;
+ v->enc_in_frame->linesize[1] = enc_in->w/2;
+ v->enc_in_frame->linesize[2] = enc_in->w/2;
+
+ /* now setup the parameters for the encoder.
+ * XXX should be codec-specific
+ */
+ {
+ AVCodecContext *enc_ctx = avcodec_alloc_context();
+ v->enc_ctx = enc_ctx;
+ enc_ctx->pix_fmt = enc_in->pix_fmt;
+ enc_ctx->width = enc_in->w;
+ enc_ctx->height = enc_in->h;
+ /* XXX rtp_callback ?
+ * rtp_mode so ffmpeg inserts as many start codes as possible.
+ */
+ enc_ctx->rtp_mode = 1;
+ enc_ctx->rtp_payload_size = v->mtu / 2; // mtu/2
+ enc_ctx->bit_rate = v->bitrate;
+ enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2;
+ enc_ctx->qmin = v->qmin; /* should be configured */
+ enc_ctx->time_base = (AVRational){1, v->fps};
+ enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds
+
+ v->enc->enc_init(v->enc_ctx);
+
+ if (avcodec_open(enc_ctx, v->codec) < 0) {
+ ast_log(LOG_WARNING, "Unable to initialize the encoder %d\n",
+ codec);
+ av_free(enc_ctx);
+ v->enc_ctx = NULL;
+ return video_out_uninit(env);
+ }
+ }
+ /*
+ * Allocate enough for the encoded bitstream. As we are compressing,
+ * we hope that the output is never larger than the input size.
+ */
+ v->enc_out.data = ast_calloc(1, enc_in->size);
+ v->enc_out.size = enc_in->size;
+ v->enc_out.used = 0;
+
+ return 0;
+}
+
+/*! \brief possibly uninitialize the video console.
+ * Called at the end of a call, should reset the 'owner' field,
+ * then possibly terminate the video thread if the gui has
+ * not been started manually.
+ * In practice, signal the thread and give it a bit of time to
+ * complete, giving up if it gets stuck. Because uninit
+ * is called from hangup with the channel locked, and the thread
+ * uses the chan lock, we need to unlock here. This is unsafe,
+ * and we should really use refcounts for the channels.
+ */
+void console_video_uninit(struct video_desc *env)
+{
+ int i, t = 100; /* initial wait is shorter, than make it longer */
+ if (env->stayopen == 0) { /* in a call */
+ env->shutdown = 1;
+ for (i=0; env->shutdown && i < 10; i++) {
+ if (env->owner)
+ ast_channel_unlock(env->owner);
+ usleep(t);
+ t = 1000000;
+ if (env->owner)
+ ast_channel_lock(env->owner);
+ }
+ }
+ env->owner = NULL; /* this is unconditional */
+}
+
+/*! fill an AVPicture from our fbuf info, as it is required by
+ * the image conversion routines in ffmpeg.
+ * XXX This depends on the format.
+ */
+static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p)
+{
+ /* provide defaults for commonly used formats */
+ int l4 = b->w * b->h/4; /* size of U or V frame */
+ int len = b->w; /* Y linesize, bytes */
+ int luv = b->w/2; /* U/V linesize, bytes */
+
+ bzero(p, sizeof(*p));
+ switch (b->pix_fmt) {
+ case PIX_FMT_RGB555:
+ case PIX_FMT_RGB565:
+ len *= 2;
+ luv = 0;
+ break;
+ case PIX_FMT_RGBA32:
+ len *= 4;
+ luv = 0;
+ break;
+ case PIX_FMT_YUYV422: /* Packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr */
+ len *= 2; /* all data in first plane, probably */
+ luv = 0;
+ break;
+ }
+ p->data[0] = b->data;
+ p->linesize[0] = len;
+ /* these are only valid for component images */
+ p->data[1] = luv ? b->data + 4*l4 : b->data+len;
+ p->data[2] = luv ? b->data + 5*l4 : b->data+len;
+ p->linesize[1] = luv;
+ p->linesize[2] = luv;
+ return p;
+}
+
+/*! convert/scale between an input and an output format.
+ * Old version of ffmpeg only have img_convert, which does not rescale.
+ * New versions use sws_scale which does both.
+ */
+static void my_scale(struct fbuf_t *in, AVPicture *p_in,
+ struct fbuf_t *out, AVPicture *p_out)
+{
+ AVPicture my_p_in, my_p_out;
+
+ if (p_in == NULL)
+ p_in = fill_pict(in, &my_p_in);
+ if (p_out == NULL)
+ p_out = fill_pict(out, &my_p_out);
+
+#ifdef OLD_FFMPEG
+ /* XXX img_convert is deprecated, and does not do rescaling */
+ img_convert(p_out, out->pix_fmt,
+ p_in, in->pix_fmt, in->w, in->h);
+#else /* XXX replacement */
+ {
+ struct SwsContext *convert_ctx;
+
+ convert_ctx = sws_getContext(in->w, in->h, in->pix_fmt,
+ out->w, out->h, out->pix_fmt,
+ SWS_BICUBIC, NULL, NULL, NULL);
+ if (convert_ctx == NULL) {
+ ast_log(LOG_ERROR, "FFMPEG::convert_cmodel : swscale context initialization failed");
+ return;
+ }
+ if (0)
+ ast_log(LOG_WARNING, "in %d %dx%d out %d %dx%d\n",
+ in->pix_fmt, in->w, in->h, out->pix_fmt, out->w, out->h);
+ sws_scale(convert_ctx,
+ p_in->data, p_in->linesize,
+ in->w, in->h, /* src slice */
+ p_out->data, p_out->linesize);
+
+ sws_freeContext(convert_ctx);
+ }
+#endif /* XXX replacement */
+}
+
+struct video_desc *get_video_desc(struct ast_channel *c);
+
+/*
+ * This function is called (by asterisk) for each video packet
+ * coming from the network (the 'in' path) that needs to be processed.
+ * We need to reconstruct the entire video frame before we can decode it.
+ * After a video packet is received we have to:
+ * - extract the bitstream with pre_process_data()
+ * - append the bitstream to a buffer
+ * - if the fragment is the last (RTP Marker) we decode it with decode_video()
+ * - after the decoding is completed we display the decoded frame with show_frame()
+ */
+int console_write_video(struct ast_channel *chan, struct ast_frame *f);
+int console_write_video(struct ast_channel *chan, struct ast_frame *f)
+{
+ struct video_desc *env = get_video_desc(chan);
+ struct video_dec_desc *v = env->in;
+
+ if (!env->gui) /* no gui, no rendering */
+ return 0;
+ if (v == NULL)
+ env->in = v = dec_init(f->subclass & ~1);
+ if (v == NULL) {
+ /* This is not fatal, but we won't have incoming video */
+ ast_log(LOG_WARNING, "Cannot initialize input decoder\n");
+ return 0;
+ }
+
+ if (v->dec_in_cur == NULL) /* no buffer for incoming frames, drop */
+ return 0;
+#if defined(DROP_PACKETS) && DROP_PACKETS > 0
+ /* Simulate lost packets */
+ if ((random() % 10000) <= 100*DROP_PACKETS) {
+ ast_log(LOG_NOTICE, "Packet lost [%d]\n", f->seqno);
+ return 0;
+ }
+#endif
+ if (v->discard) {
+ /*
+ * In discard mode, drop packets until we find one with
+ * the RTP marker set (which is the end of frame).
+ * Note that the RTP marker flag is sent as the LSB of the
+ * subclass, which is a bitmask of formats. The low bit is
+ * normally used for audio so there is no interference.
+ */
+ if (f->subclass & 0x01) {
+ v->dec_in_cur->used = 0;
+ v->dec_in_cur->ebit = 0;
+ v->next_seq = f->seqno + 1; /* wrap at 16 bit */
+ v->discard = 0;
+ ast_log(LOG_WARNING, "out of discard mode, frame %d\n", f->seqno);
+ }
+ return 0;
+ }
+
+ /*
+ * Only in-order fragments will be accepted. Remember seqno
+ * has 16 bit so there is wraparound. Also, ideally we could
+ * accept a bit of reordering, but at the moment we don't.
+ */
+ if (v->next_seq != f->seqno) {
+ ast_log(LOG_WARNING, "discarding frame out of order, %d %d\n",
+ v->next_seq, f->seqno);
+ v->discard = 1;
+ return 0;
+ }
+ v->next_seq++;
+
+ if (f->data == NULL || f->datalen < 2) {
+ ast_log(LOG_WARNING, "empty video frame, discard\n");
+ return 0;
+ }
+ if (v->d_callbacks->dec_decap(v->dec_in_cur, f->data, f->datalen)) {
+ ast_log(LOG_WARNING, "error in dec_decap, enter discard\n");
+ v->discard = 1;
+ }
+ if (f->subclass & 0x01) { // RTP Marker
+ /* prepare to decode: advance the buffer so the video thread knows. */
+ struct fbuf_t *tmp = v->dec_in_cur; /* store current pointer */
+ ast_mutex_lock(&env->dec_lock);
+ if (++v->dec_in_cur == &v->dec_in[N_DEC_IN]) /* advance to next, circular */
+ v->dec_in_cur = &v->dec_in[0];
+ if (v->dec_in_dpy == NULL) { /* were not displaying anything, so set it */
+ v->dec_in_dpy = tmp;
+ } else if (v->dec_in_dpy == v->dec_in_cur) { /* current slot is busy */
+ v->dec_in_cur = NULL;
+ }
+ ast_mutex_unlock(&env->dec_lock);
+ }
+ return 0;
+}
+
+
+/*! \brief read a frame from webcam or X11 through grabber_read(),
+ * display it, then encode and split it.
+ * Return a list of ast_frame representing the video fragments.
+ * The head pointer is returned by the function, the tail pointer
+ * is returned as an argument.
+ */
+static struct ast_frame *get_video_frames(struct video_desc *env, struct ast_frame **tail)
+{
+ struct video_out_desc *v = &env->out;
+ struct ast_frame *dummy;
+ struct fbuf_t *loc_src = grabber_read(v);
+
+ if (!loc_src)
+ return NULL; /* can happen, e.g. we are reading too early */
+
+ if (tail == NULL)
+ tail = &dummy;
+ *tail = NULL;
+ /* Scale the video for the encoder, then use it for local rendering
+ * so we will see the same as the remote party.
+ */
+ my_scale(loc_src, NULL, &env->enc_in, NULL);
+ show_frame(env, WIN_LOCAL);
+ if (!v->sendvideo)
+ return NULL;
+ if (v->enc_out.data == NULL) {
+ static volatile int a = 0;
+ if (a++ < 2)
+ ast_log(LOG_WARNING, "fail, no encoder output buffer\n");
+ return NULL;
+ }
+ v->enc->enc_run(v);
+ return v->enc->enc_encap(&v->enc_out, v->mtu, tail);
+}
+
+/*
+ * Helper thread to periodically poll the video source and enqueue the
+ * generated frames to the channel's queue.
+ * Using a separate thread also helps because the encoding can be
+ * computationally expensive so we don't want to starve the main thread.
+ */
+static void *video_thread(void *arg)
+{
+ struct video_desc *env = arg;
+ int count = 0;
+ char save_display[128] = "";
+
+ /* if sdl_videodriver is set, override the environment. Also,
+ * if it contains 'console' override DISPLAY around the call to SDL_Init
+ * so we use the console as opposed to the x11 version of aalib
+ */
+ if (!ast_strlen_zero(env->sdl_videodriver)) { /* override */
+ const char *s = getenv("DISPLAY");
+ setenv("SDL_VIDEODRIVER", env->sdl_videodriver, 1);
+ if (s && !strcasecmp(env->sdl_videodriver, "aalib-console")) {
+ ast_copy_string(save_display, s, sizeof(save_display));
+ unsetenv("DISPLAY");
+ }
+ }
+ sdl_setup(env);
+ if (!ast_strlen_zero(save_display))
+ setenv("DISPLAY", save_display, 1);
+
+ /* initialize grab coordinates */
+ env->out.loc_src_geometry.x = 0;
+ env->out.loc_src_geometry.y = 0;
+
+ ast_mutex_init(&env->dec_lock); /* used to sync decoder and renderer */
+
+ if (grabber_open(&env->out)) {
+ ast_log(LOG_WARNING, "cannot open local video source\n");
+ } else {
+#if 0
+ /* In principle, try to register the fd.
+ * In practice, many webcam drivers do not support select/poll,
+ * so don't bother and instead read periodically from the
+ * video thread.
+ */
+ if (env->out.fd >= 0)
+ ast_channel_set_fd(env->owner, 1, env->out.fd);
+#endif
+ video_out_init(env);
+ }
+
+ for (;;) {
+ struct timeval t = { 0, 50000 }; /* XXX 20 times/sec */
+ struct ast_frame *p, *f;
+ struct ast_channel *chan;
+ int fd;
+ char *caption = NULL, buf[160];
+
+ /* determine if video format changed */
+ if (count++ % 10 == 0) {
+ if (env->out.sendvideo)
+ sprintf(buf, "%s %s %dx%d @@ %dfps %dkbps",
+ env->out.videodevice, env->codec_name,
+ env->enc_in.w, env->enc_in.h,
+ env->out.fps, env->out.bitrate/1000);
+ else
+ sprintf(buf, "hold");
+ caption = buf;
+ }
+
+ /* manage keypad events */
+ /* XXX here we should always check for events,
+ * otherwise the drag will not work */
+ if (env->gui)
+ eventhandler(env, caption);
+
+ /* sleep for a while */
+ ast_select(0, NULL, NULL, NULL, &t);
+
+ if (env->in) {
+ struct video_dec_desc *v = env->in;
+
+ /*
+ * While there is something to display, call the decoder and free
+ * the buffer, possibly enabling the receiver to store new data.
+ */
+ while (v->dec_in_dpy) {
+ struct fbuf_t *tmp = v->dec_in_dpy; /* store current pointer */
+
+ if (v->d_callbacks->dec_run(v, tmp))
+ show_frame(env, WIN_REMOTE);
+ tmp->used = 0; /* mark buffer as free */
+ tmp->ebit = 0;
+ ast_mutex_lock(&env->dec_lock);
+ if (++v->dec_in_dpy == &v->dec_in[N_DEC_IN]) /* advance to next, circular */
+ v->dec_in_dpy = &v->dec_in[0];
+
+ if (v->dec_in_cur == NULL) /* receiver was idle, enable it... */
+ v->dec_in_cur = tmp; /* using the slot just freed */
+ else if (v->dec_in_dpy == v->dec_in_cur) /* this was the last slot */
+ v->dec_in_dpy = NULL; /* nothing more to display */
+ ast_mutex_unlock(&env->dec_lock);
+ }
+ }
+
+ if (env->shutdown)
+ break;
+ f = get_video_frames(env, &p); /* read and display */
+ if (!f)
+ continue;
+ chan = env->owner;
+ if (chan == NULL)
+ continue;
+ fd = chan->alertpipe[1];
+ ast_channel_lock(chan);
+
+ /* AST_LIST_INSERT_TAIL is only good for one frame, cannot use here */
+ if (chan->readq.first == NULL) {
+ chan->readq.first = f;
+ } else {
+ chan->readq.last->frame_list.next = f;
+ }
+ chan->readq.last = p;
+ /*
+ * more or less same as ast_queue_frame, but extra
+ * write on the alertpipe to signal frames.
+ */
+ if (fd > -1) {
+ int blah = 1, l = sizeof(blah);
+ for (p = f; p; p = AST_LIST_NEXT(p, frame_list)) {
+ if (write(fd, &blah, l) != l)
+ ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d: %s!\n",
+ chan->name, f->frametype, f->subclass, strerror(errno));
+ }
+ }
+ ast_channel_unlock(chan);
+ }
+ /* thread terminating, here could call the uninit */
+ /* uninitialize the local and remote video environments */
+ env->in = dec_uninit(env->in);
+ video_out_uninit(env);
+
+ if (env->gui)
+ env->gui = cleanup_sdl(env->gui);
+ ast_mutex_destroy(&env->dec_lock);
+ env->shutdown = 0;
+ return NULL;
+}
+
+static void copy_geometry(struct fbuf_t *src, struct fbuf_t *dst)
+{
+ if (dst->w == 0)
+ dst->w = src->w;
+ if (dst->h == 0)
+ dst->h = src->h;
+}
+
+/*! initialize the video environment.
+ * Apart from the formats (constant) used by sdl and the codec,
+ * we use enc_in as the basic geometry.
+ */
+static void init_env(struct video_desc *env)
+{
+ struct fbuf_t *c = &(env->out.loc_src_geometry); /* local source */
+ struct fbuf_t *ei = &(env->enc_in); /* encoder input */
+ struct fbuf_t *ld = &(env->loc_dpy); /* local display */
+ struct fbuf_t *rd = &(env->rem_dpy); /* remote display */
+
+ c->pix_fmt = PIX_FMT_YUV420P; /* default - camera format */
+ ei->pix_fmt = PIX_FMT_YUV420P; /* encoder input */
+ if (ei->w == 0 || ei->h == 0) {
+ ei->w = 352;
+ ei->h = 288;
+ }
+ ld->pix_fmt = rd->pix_fmt = PIX_FMT_YUV420P; /* sdl format */
+ /* inherit defaults */
+ copy_geometry(ei, c); /* camera inherits from encoder input */
+ copy_geometry(ei, rd); /* remote display inherits from encoder input */
+ copy_geometry(rd, ld); /* local display inherits from remote display */
+}
+
+/*!
+ * The first call to the video code, called by oss_new() or similar.
+ * Here we initialize the various components we use, namely SDL for display,
+ * ffmpeg for encoding/decoding, and a local video source.
+ * We do our best to progress even if some of the components are not
+ * available.
+ */
+void console_video_start(struct video_desc *env, struct ast_channel *owner)
+{
+ ast_log(LOG_WARNING, "env %p chan %p\n", env, owner);
+ if (env == NULL) /* video not initialized */
+ return;
+ env->owner = owner; /* work even if no owner is specified */
+ if (env->stayopen)
+ return; /* already initialized, nothing to do */
+ init_env(env);
+ env->out.enc = map_config_video_format(env->codec_name);
+
+ ast_log(LOG_WARNING, "start video out %s %dx%d\n",
+ env->codec_name, env->enc_in.w, env->enc_in.h);
+ /*
+ * Register all codecs supported by the ffmpeg library.
+ * We only need to do it once, but probably doesn't
+ * harm to do it multiple times.
+ */
+ avcodec_init();
+ avcodec_register_all();
+ av_log_set_level(AV_LOG_ERROR); /* only report errors */
+
+ if (env->out.fps == 0) {
+ env->out.fps = 15;
+ ast_log(LOG_WARNING, "fps unset, forcing to %d\n", env->out.fps);
+ }
+ if (env->out.bitrate == 0) {
+ env->out.bitrate = 65000;
+ ast_log(LOG_WARNING, "bitrate unset, forcing to %d\n", env->out.bitrate);
+ }
+ ast_pthread_create_background(&env->vthread, NULL, video_thread, env);
+ if (env->owner == NULL)
+ env->stayopen = 1; /* manually opened so don't close on hangup */
+}
+
+/*
+ * Parse a geometry string, accepting also common names for the formats.
+ * Trick: if we have a leading > or < and a numeric geometry,
+ * return the larger or smaller one.
+ * E.g. <352x288 gives the smaller one, 320x240
+ */
+static int video_geom(struct fbuf_t *b, const char *s)
+{
+ int w = 0, h = 0;
+
+ static struct {
+ const char *s; int w; int h;
+ } *fp, formats[] = {
+ {"16cif", 1408, 1152 },
+ {"xga", 1024, 768 },
+ {"4cif", 704, 576 },
+ {"vga", 640, 480 },
+ {"cif", 352, 288 },
+ {"qvga", 320, 240 },
+ {"qcif", 176, 144 },
+ {"sqcif", 128, 96 },
+ {NULL, 0, 0 },
+ };
+ if (*s == '<' || *s == '>')
+ sscanf(s+1,"%dx%d", &w, &h);
+ for (fp = formats; fp->s; fp++) {
+ if (*s == '>') { /* look for a larger one */
+ if (fp->w <= w) {
+ if (fp > formats)
+ fp--; /* back one step if possible */
+ break;
+ }
+ } else if (*s == '<') { /* look for a smaller one */
+ if (fp->w < w)
+ break;
+ } else if (!strcasecmp(s, fp->s)) { /* look for a string */
+ break;
+ }
+ }
+ if (*s == '<' && fp->s == NULL) /* smallest */
+ fp--;
+ if (fp->s) {
+ b->w = fp->w;
+ b->h = fp->h;
+ } else if (sscanf(s, "%dx%d", &b->w, &b->h) != 2) {
+ ast_log(LOG_WARNING, "Invalid video_size %s, using 352x288\n", s);
+ b->w = 352;
+ b->h = 288;
+ }
+ return 0;
+}
+
+/* extend ast_cli with video commands. Called by console_video_config */
+int console_video_cli(struct video_desc *env, const char *var, int fd)
+{
+ if (env == NULL)
+ return 1; /* unrecognised */
+
+ if (!strcasecmp(var, "videodevice")) {
+ ast_cli(fd, "videodevice is [%s]\n", env->out.videodevice);
+ } else if (!strcasecmp(var, "videocodec")) {
+ ast_cli(fd, "videocodec is [%s]\n", env->codec_name);
+ } else if (!strcasecmp(var, "sendvideo")) {
+ ast_cli(fd, "sendvideo is [%s]\n", env->out.sendvideo ? "on" : "off");
+ } else if (!strcasecmp(var, "video_size")) {
+ int in_w = 0, in_h = 0;
+ if (env->in) {
+ in_w = env->in->dec_out.w;
+ in_h = env->in->dec_out.h;
+ }
+ ast_cli(fd, "sizes: video %dx%d camera %dx%d local %dx%d remote %dx%d in %dx%d\n",
+ env->enc_in.w, env->enc_in.h,
+ env->out.loc_src_geometry.w, env->out.loc_src_geometry.h,
+ env->loc_dpy.w, env->loc_dpy.h,
+ env->rem_dpy.w, env->rem_dpy.h,
+ in_w, in_h);
+ } else if (!strcasecmp(var, "bitrate")) {
+ ast_cli(fd, "bitrate is [%d]\n", env->out.bitrate);
+ } else if (!strcasecmp(var, "qmin")) {
+ ast_cli(fd, "qmin is [%d]\n", env->out.qmin);
+ } else if (!strcasecmp(var, "fps")) {
+ ast_cli(fd, "fps is [%d]\n", env->out.fps);
+ } else if (!strcasecmp(var, "startgui")) {
+ console_video_start(env, NULL);
+ } else if (!strcasecmp(var, "stopgui") && env->stayopen != 0) {
+ env->stayopen = 0;
+ if (env->gui && env->owner)
+ ast_cli_command(-1, "console hangup");
+ else /* not in a call */
+ console_video_uninit(env);
+ } else {
+ return 1; /* unrecognised */
+ }
+ return 0; /* recognised */
+}
+
+/*! parse config command for video support. */
+int console_video_config(struct video_desc **penv,
+ const char *var, const char *val)
+{
+ struct video_desc *env;
+
+ if (penv == NULL) {
+ ast_log(LOG_WARNING, "bad argument penv=NULL\n");
+ return 1; /* error */
+ }
+ /* allocate the video descriptor first time we get here */
+ env = *penv;
+ if (env == NULL) {
+ env = *penv = ast_calloc(1, sizeof(struct video_desc));
+ if (env == NULL) {
+ ast_log(LOG_WARNING, "fail to allocate video_desc\n");
+ return 1; /* error */
+
+ }
+ /* set default values */
+ ast_copy_string(env->out.videodevice, "X11", sizeof(env->out.videodevice));
+ env->out.fps = 5;
+ env->out.bitrate = 65000;
+ env->out.sendvideo = 1;
+ env->out.qmin = 3;
+ }
+ CV_START(var, val);
+ CV_STR("videodevice", env->out.videodevice);
+ CV_BOOL("sendvideo", env->out.sendvideo);
+ CV_F("video_size", video_geom(&env->enc_in, val));
+ CV_F("camera_size", video_geom(&env->out.loc_src_geometry, val));
+ CV_F("local_size", video_geom(&env->loc_dpy, val));
+ CV_F("remote_size", video_geom(&env->rem_dpy, val));
+ CV_STR("keypad", env->keypad_file);
+ CV_F("region", keypad_cfg_read(env->gui, val));
+ CV_STR("keypad_font", env->keypad_font);
+ CV_STR("sdl_videodriver", env->sdl_videodriver);
+ CV_UINT("fps", env->out.fps);
+ CV_UINT("bitrate", env->out.bitrate);
+ CV_UINT("qmin", env->out.qmin);
+ CV_STR("videocodec", env->codec_name);
+ return 1; /* nothing found */
+
+ CV_END; /* the 'nothing found' case */
+ return 0; /* found something */
+}
+
+#endif /* video support */
diff --git a/trunk/channels/console_video.h b/trunk/channels/console_video.h
new file mode 100644
index 000000000..f426a5463
--- /dev/null
+++ b/trunk/channels/console_video.h
@@ -0,0 +1,127 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 Luigi Rizzo
+ *
+ * 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.
+ */
+
+/*
+ * Common header for console video support
+ *
+ * $Revision$
+ */
+
+#ifndef CONSOLE_VIDEO_H
+#define CONSOLE_VIDEO_H
+
+#if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG)
+#define CONSOLE_VIDEO_CMDS \
+ "console {device}"
+#else
+
+#include <ffmpeg/avcodec.h>
+#ifndef OLD_FFMPEG
+#include <ffmpeg/swscale.h> /* requires a recent ffmpeg */
+#endif
+
+#define CONSOLE_VIDEO_CMDS \
+ "console {videodevice|videocodec" \
+ "|video_size|bitrate|fps|qmin" \
+ "|sendvideo|keypad" \
+ "|sdl_videodriver" \
+ "|device|startgui|stopgui" \
+ "}"
+
+#endif /* HAVE_VIDEO_CONSOLE and others */
+
+/*
+ * In many places we use buffers to store the raw frames (but not only),
+ * so here is a structure to keep all the info. data = NULL means the
+ * structure is not initialized, so the other fields are invalid.
+ * size = 0 means the buffer is not malloc'ed so we don't have to free it.
+ */
+struct fbuf_t { /* frame buffers, dynamically allocated */
+ uint8_t *data; /* memory, malloced if size > 0, just reference
+ * otherwise */
+ int size; /* total size in bytes */
+ int used; /* space used so far */
+ int ebit; /* bits to ignore at the end */
+ int x; /* origin, if necessary */
+ int y;
+ int w; /* size */
+ int h;
+ int pix_fmt;
+};
+
+void fbuf_free(struct fbuf_t *);
+
+/* descriptor for a grabber */
+struct grab_desc {
+ const char *name;
+ void *(*open)(const char *name, struct fbuf_t *geom, int fps);
+ struct fbuf_t *(*read)(void *d);
+ void (*move)(void *d, int dx, int dy);
+ void *(*close)(void *d);
+};
+
+extern struct grab_desc *console_grabbers[];
+
+struct video_desc; /* opaque type for video support */
+struct video_desc *get_video_desc(struct ast_channel *c);
+
+/* linked by console_video.o */
+int console_write_video(struct ast_channel *chan, struct ast_frame *f);
+extern int console_video_formats;
+int console_video_cli(struct video_desc *env, const char *var, int fd);
+int console_video_config(struct video_desc **penv, const char *var, const char *val);
+void console_video_uninit(struct video_desc *env);
+void console_video_start(struct video_desc *env, struct ast_channel *owner);
+
+/* console_board.c */
+
+/* Where do we send the keyboard/keypad output */
+enum kb_output {
+ KO_NONE,
+ KO_INPUT, /* the local input window */
+ KO_DIALED, /* the 'dialed number' window */
+ KO_MESSAGE, /* the 'message' window */
+};
+
+enum drag_window { /* which window are we dragging */
+ DRAG_NONE,
+ DRAG_LOCAL, /* local video */
+ DRAG_REMOTE, /* remote video */
+ DRAG_DIALED, /* dialed number */
+ DRAG_INPUT, /* input window */
+ DRAG_MESSAGE, /* message window */
+};
+
+/*! \brief support for drag actions */
+struct drag_info {
+ int x_start; /* last known mouse position */
+ int y_start;
+ enum drag_window drag_window;
+};
+/*! \brief info related to the gui: button status, mouse coords, etc. */
+struct board;
+/* !\brief print a message on a board */
+void move_message_board(struct board *b, int dy);
+int print_message(struct board *b, const char *s);
+
+/*! \brief return the whole text from a board */
+const char *read_message(const struct board *b);
+
+/*! \brief reset the board to blank */
+int reset_board(struct board *b);
+
+#endif /* CONSOLE_VIDEO_H */
+/* end of file */
diff --git a/trunk/channels/h323/ChangeLog b/trunk/channels/h323/ChangeLog
new file mode 100644
index 000000000..ddbf08193
--- /dev/null
+++ b/trunk/channels/h323/ChangeLog
@@ -0,0 +1,43 @@
+Build
+ -- Hold lock when creating new H.323 channel to sync the audio channels
+ -- Decrement usage counter when appropriate
+ -- Actually unregister everything in unload_module
+ -- Add IP based authentication using 'host'in type=user's
+0.1.0
+ -- Intergration into the mainline Asterisk codebase
+ -- Remove reduandant debug info
+ -- Add Caller*id support
+ -- Inband DTMF
+ -- Retool port usage (to avoid possible seg fault condition)
+0.0.6
+ -- Configurable support for user-input (DTMF)
+ -- Reworked Gatekeeper support
+ -- Native bridging (but is still broken, help!)
+ -- Locally implement a non-broken G.723.1 Capability
+ -- Utilize the cleaner RTP method implemented by Mark
+ -- AllowGkRouted, thanks to Panny from http://hotlinks.co.uk
+ -- Clened up inbound call flow
+ -- Prefix, E.164 and Gateway support
+ -- Multi-homed support
+ -- Killed more seg's
+0.0.5
+ -- Added H.323 Alias support
+ -- Clened up inbound call flow
+ -- Fixed RTP port logic
+ -- Stomped on possible seg fault conditions thanks to Iain Stevenson
+0.0.4
+ -- Fixed one-way audio on inbound calls. Found
+ race condition in monitor thread.
+
+0.0.3
+ -- Changed name to chan_h323
+ -- Also renamed file names to futher avoid confusion
+
+0.0.2
+ -- First public offering
+ -- removed most hardcoded values
+ -- lots of changes to alias/exension operation
+
+0.0.1
+ -- initial build, lots of hardcoded crap
+ -- Proof of concept for External RTP
diff --git a/trunk/channels/h323/INSTALL.openh323 b/trunk/channels/h323/INSTALL.openh323
new file mode 100644
index 000000000..f46c37905
--- /dev/null
+++ b/trunk/channels/h323/INSTALL.openh323
@@ -0,0 +1,18 @@
+To build Open H.323 see:
+
+http://www.openh323.org/build.html#unix
+
+You only need to do 'make opt'. Anything else you will be simply waisting time and HD space.
+Also, you will notice they never tell you to 'make install' so don't do it.
+
+
+On FreeBSD, the Makefiles are configured to
+locate the compiled openh323 port, if it has
+been built. Here is one way to build
+openh323 and ptlib on such that the Makefiles
+find it:
+ # cd /usr/ports/net/openh323
+ # make
+It is not necessary to install the port. The
+asterisk makefiles do not use any files
+installed by the port.
diff --git a/trunk/channels/h323/Makefile.in b/trunk/channels/h323/Makefile.in
new file mode 100644
index 000000000..083250f55
--- /dev/null
+++ b/trunk/channels/h323/Makefile.in
@@ -0,0 +1,48 @@
+#
+# Makefile
+#
+# Make file for OpenH323 support layer
+#
+
+.PHONY: Makefile.ast clean
+
+default:: @OPENH323_BUILD@
+
+# Verify those options with main Makefile
+STDCCFLAGS = -DNDEBUG
+STDCCFLAGS += -I../../include -include ../../include/asterisk/autoconfig.h
+STDCCFLAGS += -fPIC
+#OPTCCFLAGS +=
+CFLAGS = -pipe
+TARGET = libchanh323.a
+TARGET += Makefile.ast
+SOURCES = ast_h323.cxx compat_h323.cxx cisco-h225.cxx caps_h323.cxx
+OBJDIR = .
+OBJS =
+
+ifndef OPENH323DIR
+OPENH323DIR=@OPENH323DIR@
+endif
+
+include $(OPENH323DIR)/openh323u.mak
+
+notrace::
+ $(MAKE) NOTRACE=1 opt
+
+$(SOURCES):: Makefile ../../Makefile
+ touch $@
+
+libchanh323.a: $(OBJS)
+ ar crv $@ $(OBJS)
+
+cisco-h225.cxx:: cisco-h225.asn
+ asnparser -m CISCO_H225 -c $<
+
+Makefile.ast:
+ @echo H323CFLAGS = $(STDCCFLAGS) $(OPTCCFLAGS) $(CFLAGS) >$@.tmp
+ @echo H323LDFLAGS = $(CFLAGS) $(LDFLAGS) >>$@.tmp
+ @echo H323LDLIBS = $(LDLIBS) $(ENDLDLIBS) $(ENDLDFLAGS) >>$@.tmp
+ @if [ -r $@ ] && cmp -s $@ $@.tmp; then rm -f $@.tmp; else mv -f $@.tmp $@; fi
+
+clean::
+ rm -f $(TARGET) $(OBJS) Makefile.ast *.dep
diff --git a/trunk/channels/h323/README b/trunk/channels/h323/README
new file mode 100644
index 000000000..875bf3668
--- /dev/null
+++ b/trunk/channels/h323/README
@@ -0,0 +1,144 @@
+ Open H.323 Channel Driver for Asterisk
+ By Jeremy McNamara
+ For The NuFone Network
+
+ First public release on November 10th, 2002
+
+ Dependancies (based on OpenH323/PWLib ones):
+ openssl-0.9.6b+
+ openssl-devel-0.9.6b+
+ expat-1.95+
+ expat-dev-1.95+
+
+Tested with Open H.323 version v1.18.0, PWLib v1.10.0 and GCC v3.2.2. Usage of any
+other (especially prior OpenH323 v1.17.3 and PWLib v1.9.2) versions is not
+supported.
+
+NOTICE: Whatever you do, DO NOT USE distrubution specific installs
+of Open H.323 and PWLib. In fact, you should check to make sure
+your distro did not install them for you without your knowledge.
+
+
+To compile this code
+--------------------
+Once PWLib and Open H.323 have been compiled per their specific build
+instructions, issue a make in the asterisk/channels/h323 directory with
+argument used to build PWLib and OpenH323 (for example, make opt), then go
+back to the Asterisk source top level directory and issue a make install.
+
+
+The most common compile error
+----------------------------
+If you receive ANYTHING that says 'undefined symbol' you are experiencing
+typical version skew. For example:
+
+libh323_linux_x86_r.so.1: undefined symbol: GetNumberValueAt__C14PAbstractArrayi
+
+You need to search and destroy every version of libh323 and libpt then
+completely recompile everything
+
+Example commands to make sure everything gets cleaned and then
+rebult in proper order:
+
+cd /path/to/pwlib
+./configure
+make clean opt
+cd /path/to/openh323
+./configure
+make clean opt
+cd /path/to/asterisk/channels/h323
+make opt
+cd /path/to/asterisk
+make install
+
+
+Most common run-time error
+-------------------------
+libpt_linux_x86_r.so.1: cannot open shared object file: No such
+file or directory
+
+You have not set the LD_LIBRARY_PATH environment variable.
+
+Example environment for sh/bash:
+
+PWLIBDIR=$HOME/pwlib
+export PWLIBDIR
+OPENH323DIR=$HOME/openh323
+export OPENH323DIR
+LD_LIBRARY_PATH=$PWLIBDIR/lib:$OPENH323DIR/lib
+export LD_LIBRARY_PATH
+
+We recomend puting the above directives into your /etc/profile so
+you do not have to remember to export those values every time you
+want to recompile. Make sure to logout and log back in, so your
+envrionment can pick up the new variables.
+
+
+Upgrading Asterisk
+-----------------
+After you cvs update (or make update) Asterisk you have to go into
+asterisk/channels/h323 and issue a make clean all, before compiling the
+rest of asterisk. Doing this process every time you upgrade Asterisk
+will ensure a sane build.
+
+
+Dialing an H.323 channel
+------------------------
+Without a gatekeeper:
+exten => _1NXXNXXXXXX,1,Dial,H323/${EXTEN}@peer
+or
+exten => _1NXXNXXXXXX,1,Dial,H323/${EXTEN}@ip.or.hostname
+
+'peer' is defined in h323.conf as:
+
+[peer]
+type=peer
+host=1.2.3.4
+disallow=all
+allow=ulaw
+
+Using a gatekeeper:
+exten => _1NXXNXXXXXX,1,Dial,H323/${EXTEN}
+
+When using a gatekeeper you cannot utilize the type=peer features,
+since the H.323 spec states that when a Gatekeeper is part of an H.323 network,
+the Gatekeeper shall be used for all communication.
+
+
+Developer Contact
+----------------
+If you have trouble contact 'JerJer' in #Asterisk on
+irc.freenode.net and/or send reasonable debug information to support@nufone.net.
+
+If are lucky enough to segfault this code please run a
+backtrace and send the gory details. Segmentation faults are not
+tolerated, no matter what Distro you run (even debian)!
+
+a simple bt example:
+
+# /usr/sbin/asterisk -vvvgc
+...
+[chan_h323.so]
+Segmentation Fault (core dumped)
+
+# ls core.*
+core.1976
+
+# gdb /usr/sbin/asterisk core.1976
+...lots of useless garbage here...
+(gdb) bt
+
+Send whatever shows up right after the 'bt'
+
+Also, a full debug screen output is almost needed. Make sure you are
+in the full console mode (-c) and turn on 'h.323 debug' or worst case
+senerio 'h.323 trace 4'. A nice way to capture debug info is with
+script (man script).
+
+If you are motivated to update/fix this code please submit a
+disclaimer along with the patch to the Asterisk bug
+tracker: http://bugs.digium.com/
+
+
+Jeremy McNamara
+The NuFone Network
diff --git a/trunk/channels/h323/TODO b/trunk/channels/h323/TODO
new file mode 100644
index 000000000..1e114ca3b
--- /dev/null
+++ b/trunk/channels/h323/TODO
@@ -0,0 +1,9 @@
+The NuFone Network's Open H.323 Channel Driver for Asterisk
+
+ TODO:
+
+ - H.323 Native Bridging
+
+ - Gatekeeping support (started)
+
+ - Acutally implement the options for broken H.323 stacks
diff --git a/trunk/channels/h323/ast_h323.cxx b/trunk/channels/h323/ast_h323.cxx
new file mode 100644
index 000000000..fd7d35dff
--- /dev/null
+++ b/trunk/channels/h323/ast_h323.cxx
@@ -0,0 +1,2637 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+/*
+ * ast_h323.cpp
+ *
+ * OpenH323 Channel Driver for ASTERISK PBX.
+ * By Jeremy McNamara
+ * For The NuFone Network
+ *
+ * chan_h323 has been derived from code created by
+ * Michael Manousos and Mark Spencer
+ *
+ * This file is part of the chan_h323 driver for Asterisk
+ *
+ * chan_h323 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.
+ *
+ * chan_h323 is distributed 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Version Info: $Id$
+ */
+#include <arpa/inet.h>
+
+#include <list>
+#include <string>
+#include <algorithm>
+
+#include <ptlib.h>
+#include <h323.h>
+#include <h323pdu.h>
+#include <h323neg.h>
+#include <mediafmt.h>
+#include <lid.h>
+#ifdef H323_H450
+#include "h4501.h"
+#include "h4504.h"
+#include "h45011.h"
+#include "h450pdu.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "asterisk/compat.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/astobj.h"
+#ifdef __cplusplus
+}
+#endif
+
+#include "chan_h323.h"
+#include "ast_h323.h"
+#include "cisco-h225.h"
+#include "caps_h323.h"
+
+/* PWlib Required Components */
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 0
+#define BUILD_TYPE ReleaseCode
+#define BUILD_NUMBER 0
+
+/** Counter for the number of connections */
+static int channelsOpen;
+
+/**
+ * We assume that only one endPoint should exist.
+ * The application cannot run the h323_end_point_create() more than once
+ * FIXME: Singleton this, for safety
+ */
+static MyH323EndPoint *endPoint = NULL;
+
+/** PWLib entry point */
+static MyProcess *localProcess = NULL;
+
+static int _timerChangePipe[2];
+
+static unsigned traceOptions = PTrace::Timestamp | PTrace::Thread | PTrace::FileAndLine;
+
+class PAsteriskLog : public PObject, public iostream {
+ PCLASSINFO(PAsteriskLog, PObject);
+
+ public:
+ PAsteriskLog() : iostream(cout.rdbuf()) { init(&buffer); }
+ ~PAsteriskLog() { flush(); }
+
+ private:
+ PAsteriskLog(const PAsteriskLog &) : iostream(cout.rdbuf()) { }
+ PAsteriskLog & operator=(const PAsteriskLog &) { return *this; }
+
+ class Buffer : public streambuf {
+ public:
+ virtual int overflow(int=EOF);
+ virtual int underflow();
+ virtual int sync();
+ PString string;
+ } buffer;
+ friend class Buffer;
+};
+
+static PAsteriskLog *logstream = NULL;
+
+int PAsteriskLog::Buffer::overflow(int c)
+{
+ if (pptr() >= epptr()) {
+ int ppos = pptr() - pbase();
+ char *newptr = string.GetPointer(string.GetSize() + 2000);
+ setp(newptr, newptr + string.GetSize() - 1);
+ pbump(ppos);
+ }
+ if (c != EOF) {
+ *pptr() = (char)c;
+ pbump(1);
+ }
+ return 0;
+}
+
+int PAsteriskLog::Buffer::underflow()
+{
+ return EOF;
+}
+
+int PAsteriskLog::Buffer::sync()
+{
+ char *str = strdup(string);
+ char *s, *s1;
+ char c;
+
+ /* Pass each line with different ast_verbose() call */
+ for (s = str; s && *s; s = s1) {
+ s1 = strchr(s, '\n');
+ if (!s1)
+ s1 = s + strlen(s);
+ else
+ s1++;
+ c = *s1;
+ *s1 = '\0';
+ ast_verbose("%s", s);
+ *s1 = c;
+ }
+ free(str);
+
+ string = PString();
+ char *base = string.GetPointer(2000);
+ setp(base, base + string.GetSize() - 1);
+ return 0;
+}
+
+static ostream &my_endl(ostream &os)
+{
+ if (logstream) {
+ PTrace::SetOptions(traceOptions);
+ return PTrace::End(os);
+ }
+ return endl(os);
+}
+
+#define cout \
+ (logstream ? (PTrace::ClearOptions((unsigned)-1), PTrace::Begin(0, __FILE__, __LINE__)) : std::cout)
+#define endl my_endl
+
+/* Special class designed to call cleanup code on module destruction */
+class MyH323_Shutdown {
+ public:
+ MyH323_Shutdown() { };
+ ~MyH323_Shutdown()
+ {
+ h323_end_process();
+ };
+};
+
+MyProcess::MyProcess(): PProcess("The NuFone Networks",
+ "H.323 Channel Driver for Asterisk",
+ MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER)
+{
+ /* Call shutdown when module being unload or asterisk has been stopped */
+ static MyH323_Shutdown x;
+
+ /* Fix missed one in PWLib */
+ PX_firstTimeStart = FALSE;
+ Resume();
+}
+
+MyProcess::~MyProcess()
+{
+ _timerChangePipe[0] = timerChangePipe[0];
+ _timerChangePipe[1] = timerChangePipe[1];
+}
+
+void MyProcess::Main()
+{
+ PTrace::Initialise(PTrace::GetLevel(), NULL, traceOptions);
+ PTrace::SetStream(logstream);
+
+ cout << " == Creating H.323 Endpoint" << endl;
+ if (endPoint) {
+ cout << " == ENDPOINT ALREADY CREATED" << endl;
+ return;
+ }
+ endPoint = new MyH323EndPoint();
+ /* Due to a bug in the H.323 recomendation/stack we should request a sane
+ amount of bandwidth from the GK - this function is ignored if not using a GK
+ We are requesting 128 (64k in each direction), which is the worst case codec. */
+ endPoint->SetInitialBandwidth(1280);
+}
+
+void PAssertFunc(const char *msg)
+{
+ ast_log(LOG_ERROR, "%s\n", msg);
+ /* XXX: Probably we need to crash here */
+}
+
+
+/** MyH323EndPoint
+ */
+MyH323EndPoint::MyH323EndPoint()
+ : H323EndPoint()
+{
+ /* Capabilities will be negotiated on per-connection basis */
+ capabilities.RemoveAll();
+
+ /* Reset call setup timeout to some more reasonable value than 1 minute */
+ signallingChannelCallTimeout = PTimeInterval(0, 0, 10); /* 10 minutes */
+}
+
+/** The fullAddress parameter is used directly in the MakeCall method so
+ * the General form for the fullAddress argument is :
+ * [alias@][transport$]host[:port]
+ * default values: alias = the same value as host.
+ * transport = ip.
+ * port = 1720.
+ */
+int MyH323EndPoint::MyMakeCall(const PString & dest, PString & token, void *_callReference, void *_opts)
+{
+ PString fullAddress;
+ MyH323Connection * connection;
+ H323Transport *transport = NULL;
+ unsigned int *callReference = (unsigned int *)_callReference;
+ call_options_t *opts = (call_options_t *)_opts;
+
+ /* Determine whether we are using a gatekeeper or not. */
+ if (GetGatekeeper()) {
+ fullAddress = dest;
+ if (h323debug) {
+ cout << " -- Making call to " << fullAddress << " using gatekeeper." << endl;
+ }
+ } else {
+ fullAddress = dest;
+ if (h323debug) {
+ cout << " -- Making call to " << fullAddress << " without gatekeeper." << endl;
+ }
+ /* Use bindaddr for outgoing calls too if we don't use gatekeeper */
+ if (listeners.GetSize() > 0) {
+ H323TransportAddress taddr = listeners[0].GetTransportAddress();
+ PIPSocket::Address addr;
+ WORD port;
+ if (taddr.GetIpAndPort(addr, port)) {
+ /* Create own transport for specific addresses only */
+ if (addr) {
+ if (h323debug)
+ cout << "Using " << addr << " for outbound call" << endl;
+ transport = new MyH323TransportTCP(*this, addr);
+ if (!transport)
+ cout << "Unable to create transport for outgoing call" << endl;
+ }
+ } else
+ cout << "Unable to get address and port" << endl;
+ }
+ }
+ if (!(connection = (MyH323Connection *)H323EndPoint::MakeCallLocked(fullAddress, token, opts, transport))) {
+ if (h323debug) {
+ cout << "Error making call to \"" << fullAddress << '"' << endl;
+ }
+ return 1;
+ }
+ *callReference = connection->GetCallReference();
+
+ if (h323debug) {
+ cout << "\t-- " << GetLocalUserName() << " is calling host " << fullAddress << endl;
+ cout << "\t-- Call token is " << (const char *)token << endl;
+ cout << "\t-- Call reference is " << *callReference << endl;
+#ifdef PTRACING
+ cout << "\t-- DTMF Payload is " << connection->dtmfCodec << endl;
+#endif
+ }
+ connection->Unlock();
+ return 0;
+}
+
+void MyH323EndPoint::SetEndpointTypeInfo( H225_EndpointType & info ) const
+{
+ H323EndPoint::SetEndpointTypeInfo(info);
+
+ if (terminalType == e_GatewayOnly){
+ info.RemoveOptionalField(H225_EndpointType::e_terminal);
+ info.IncludeOptionalField(H225_EndpointType::e_gateway);
+ }
+
+ info.m_gateway.IncludeOptionalField(H225_GatewayInfo::e_protocol);
+ info.m_gateway.m_protocol.SetSize(1);
+ H225_SupportedProtocols &protocol=info.m_gateway.m_protocol[0];
+ protocol.SetTag(H225_SupportedProtocols::e_voice);
+ PINDEX as=SupportedPrefixes.GetSize();
+ ((H225_VoiceCaps &)protocol).m_supportedPrefixes.SetSize(as);
+ for (PINDEX p=0; p<as; p++) {
+ H323SetAliasAddress(SupportedPrefixes[p], ((H225_VoiceCaps &)protocol).m_supportedPrefixes[p].m_prefix, H225_AliasAddress::e_dialedDigits);
+ }
+}
+
+void MyH323EndPoint::SetGateway(void)
+{
+ terminalType = e_GatewayOnly;
+}
+
+BOOL MyH323EndPoint::ClearCall(const PString & token, H323Connection::CallEndReason reason)
+{
+ if (h323debug) {
+#ifdef PTRACING
+ cout << "\t-- ClearCall: Request to clear call with token " << token << ", cause " << reason << endl;
+#else
+ cout << "\t-- ClearCall: Request to clear call with token " << token << ", cause [" << (int)reason << "]" << endl;
+#endif
+ }
+ return H323EndPoint::ClearCall(token, reason);
+}
+
+BOOL MyH323EndPoint::ClearCall(const PString & token)
+{
+ if (h323debug) {
+ cout << "\t-- ClearCall: Request to clear call with token " << token << endl;
+ }
+ return H323EndPoint::ClearCall(token, H323Connection::EndedByLocalUser);
+}
+
+void MyH323EndPoint::SendUserTone(const PString &token, char tone)
+{
+ H323Connection *connection = NULL;
+
+ connection = FindConnectionWithLock(token);
+ if (connection != NULL) {
+ connection->SendUserInputTone(tone, 500);
+ connection->Unlock();
+ }
+}
+
+void MyH323EndPoint::OnClosedLogicalChannel(H323Connection & connection, const H323Channel & channel)
+{
+ channelsOpen--;
+ if (h323debug) {
+ cout << "\t\tchannelsOpen = " << channelsOpen << endl;
+ }
+ H323EndPoint::OnClosedLogicalChannel(connection, channel);
+}
+
+BOOL MyH323EndPoint::OnConnectionForwarded(H323Connection & connection,
+ const PString & forwardParty,
+ const H323SignalPDU & pdu)
+{
+ if (h323debug) {
+ cout << "\t-- Call Forwarded to " << forwardParty << endl;
+ }
+ return FALSE;
+}
+
+BOOL MyH323EndPoint::ForwardConnection(H323Connection & connection,
+ const PString & forwardParty,
+ const H323SignalPDU & pdu)
+{
+ if (h323debug) {
+ cout << "\t-- Forwarding call to " << forwardParty << endl;
+ }
+ return H323EndPoint::ForwardConnection(connection, forwardParty, pdu);
+}
+
+void MyH323EndPoint::OnConnectionEstablished(H323Connection & connection, const PString & estCallToken)
+{
+ if (h323debug) {
+ cout << "\t=-= In OnConnectionEstablished for call " << connection.GetCallReference() << endl;
+ cout << "\t\t-- Connection Established with \"" << connection.GetRemotePartyName() << "\"" << endl;
+ }
+ on_connection_established(connection.GetCallReference(), (const char *)connection.GetCallToken());
+}
+
+/** OnConnectionCleared callback function is called upon the dropping of an established
+ * H323 connection.
+ */
+void MyH323EndPoint::OnConnectionCleared(H323Connection & connection, const PString & clearedCallToken)
+{
+ PString remoteName = connection.GetRemotePartyName();
+
+ switch (connection.GetCallEndReason()) {
+ case H323Connection::EndedByCallForwarded:
+ if (h323debug) {
+ cout << "-- " << remoteName << " has forwarded the call" << endl;
+ }
+ break;
+ case H323Connection::EndedByRemoteUser:
+ if (h323debug) {
+ cout << "-- " << remoteName << " has cleared the call" << endl;
+ }
+ break;
+ case H323Connection::EndedByCallerAbort:
+ if (h323debug) {
+ cout << "-- " << remoteName << " has stopped calling" << endl;
+ }
+ break;
+ case H323Connection::EndedByRefusal:
+ if (h323debug) {
+ cout << "-- " << remoteName << " did not accept your call" << endl;
+ }
+ break;
+ case H323Connection::EndedByRemoteBusy:
+ if (h323debug) {
+ cout << "-- " << remoteName << " was busy" << endl;
+ }
+ break;
+ case H323Connection::EndedByRemoteCongestion:
+ if (h323debug) {
+ cout << "-- Congested link to " << remoteName << endl;
+ }
+ break;
+ case H323Connection::EndedByNoAnswer:
+ if (h323debug) {
+ cout << "-- " << remoteName << " did not answer your call" << endl;
+ }
+ break;
+ case H323Connection::EndedByTransportFail:
+ if (h323debug) {
+ cout << "-- Call with " << remoteName << " ended abnormally" << endl;
+ }
+ break;
+ case H323Connection::EndedByCapabilityExchange:
+ if (h323debug) {
+ cout << "-- Could not find common codec with " << remoteName << endl;
+ }
+ break;
+ case H323Connection::EndedByNoAccept:
+ if (h323debug) {
+ cout << "-- Did not accept incoming call from " << remoteName << endl;
+ }
+ break;
+ case H323Connection::EndedByAnswerDenied:
+ if (h323debug) {
+ cout << "-- Refused incoming call from " << remoteName << endl;
+ }
+ break;
+ case H323Connection::EndedByNoUser:
+ if (h323debug) {
+ cout << "-- Remote endpoint could not find user: " << remoteName << endl;
+ }
+ break;
+ case H323Connection::EndedByNoBandwidth:
+ if (h323debug) {
+ cout << "-- Call to " << remoteName << " aborted, insufficient bandwidth." << endl;
+ }
+ break;
+ case H323Connection::EndedByUnreachable:
+ if (h323debug) {
+ cout << "-- " << remoteName << " could not be reached." << endl;
+ }
+ break;
+ case H323Connection::EndedByHostOffline:
+ if (h323debug) {
+ cout << "-- " << remoteName << " is not online." << endl;
+ }
+ break;
+ case H323Connection::EndedByNoEndPoint:
+ if (h323debug) {
+ cout << "-- No phone running for " << remoteName << endl;
+ }
+ break;
+ case H323Connection::EndedByConnectFail:
+ if (h323debug) {
+ cout << "-- Transport error calling " << remoteName << endl;
+ }
+ break;
+ default:
+ if (h323debug) {
+#ifdef PTRACING
+ cout << " -- Call with " << remoteName << " completed (" << connection.GetCallEndReason() << ")" << endl;
+#else
+ cout << " -- Call with " << remoteName << " completed ([" << (int)connection.GetCallEndReason() << "])" << endl;
+#endif
+ }
+ }
+
+ if (connection.IsEstablished()) {
+ if (h323debug) {
+ cout << "\t-- Call duration " << setprecision(0) << setw(5) << (PTime() - connection.GetConnectionStartTime()) << endl;
+ }
+ }
+ /* Invoke the PBX application registered callback */
+ on_connection_cleared(connection.GetCallReference(), clearedCallToken);
+ return;
+}
+
+H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference, void *userData, H323Transport *transport, H323SignalPDU *setupPDU)
+{
+ unsigned options = 0;
+ call_options_t *opts = (call_options_t *)userData;
+ MyH323Connection *conn;
+
+ if (opts && opts->fastStart) {
+ options |= H323Connection::FastStartOptionEnable;
+ } else {
+ options |= H323Connection::FastStartOptionDisable;
+ }
+ if (opts && opts->h245Tunneling) {
+ options |= H323Connection::H245TunnelingOptionEnable;
+ } else {
+ options |= H323Connection::H245TunnelingOptionDisable;
+ }
+/* Disable until I can figure out the proper way to deal with this */
+#if 0
+ if (opts->silenceSuppression) {
+ options |= H323Connection::SilenceSuppresionOptionEnable;
+ } else {
+ options |= H323Connection::SilenceSUppressionOptionDisable;
+ }
+#endif
+ conn = new MyH323Connection(*this, callReference, options);
+ if (conn) {
+ if (opts)
+ conn->SetCallOptions(opts, (setupPDU ? TRUE : FALSE));
+ }
+ return conn;
+}
+
+/* MyH323Connection Implementation */
+MyH323Connection::MyH323Connection(MyH323EndPoint & ep, unsigned callReference,
+ unsigned options)
+ : H323Connection(ep, callReference, options)
+{
+#ifdef H323_H450
+ /* Dispatcher will free out all registered handlers */
+ if (h450dispatcher)
+ delete h450dispatcher;
+ h450dispatcher = new H450xDispatcher(*this);
+ h4502handler = new H4502Handler(*this, *h450dispatcher);
+ h4504handler = new MyH4504Handler(*this, *h450dispatcher);
+ h4506handler = new H4506Handler(*this, *h450dispatcher);
+ h45011handler = new H45011Handler(*this, *h450dispatcher);
+#endif
+ cause = -1;
+ sessionId = 0;
+ bridging = FALSE;
+ holdHandling = progressSetup = progressAlert = 0;
+ dtmfMode = 0;
+ dtmfCodec[0] = dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)0;
+ redirect_reason = -1;
+ transfer_capability = -1;
+#ifdef TUNNELLING
+ tunnelOptions = remoteTunnelOptions = 0;
+#endif
+ if (h323debug) {
+ cout << " == New H.323 Connection created." << endl;
+ }
+ return;
+}
+
+MyH323Connection::~MyH323Connection()
+{
+ if (h323debug) {
+ cout << " == H.323 Connection deleted." << endl;
+ }
+ return;
+}
+
+BOOL MyH323Connection::OnReceivedProgress(const H323SignalPDU &pdu)
+{
+ BOOL isInband;
+ unsigned pi;
+
+ if (!H323Connection::OnReceivedProgress(pdu)) {
+ return FALSE;
+ }
+
+ if (!pdu.GetQ931().GetProgressIndicator(pi))
+ pi = 0;
+ if (h323debug) {
+ cout << "\t- Progress Indicator: " << pi << endl;
+ }
+
+ switch(pi) {
+ case Q931::ProgressNotEndToEndISDN:
+ case Q931::ProgressInbandInformationAvailable:
+ isInband = TRUE;
+ break;
+ default:
+ isInband = FALSE;
+ }
+ on_progress(GetCallReference(), (const char *)GetCallToken(), isInband);
+
+ return connectionState != ShuttingDownConnection;
+}
+
+BOOL MyH323Connection::MySendProgress()
+{
+ /* The code taken from H323Connection::AnsweringCall() but ALWAYS send
+ PROGRESS message, including slow start operations */
+ H323SignalPDU progressPDU;
+ H225_Progress_UUIE &prog = progressPDU.BuildProgress(*this);
+
+ if (!mediaWaitForConnect) {
+ if (SendFastStartAcknowledge(prog.m_fastStart))
+ prog.IncludeOptionalField(H225_Progress_UUIE::e_fastStart);
+ else {
+ if (connectionState == ShuttingDownConnection)
+ return FALSE;
+
+ /* Do early H.245 start */
+ earlyStart = TRUE;
+ if (!h245Tunneling) {
+ if (!H323Connection::StartControlChannel())
+ return FALSE;
+ prog.IncludeOptionalField(H225_Progress_UUIE::e_h245Address);
+ controlChannel->SetUpTransportPDU(prog.m_h245Address, TRUE);
+ }
+ }
+ }
+ progressPDU.GetQ931().SetProgressIndicator(Q931::ProgressInbandInformationAvailable);
+
+#ifdef TUNNELLING
+ EmbedTunneledInfo(progressPDU);
+#endif
+ HandleTunnelPDU(&progressPDU);
+ WriteSignalPDU(progressPDU);
+
+ return TRUE;
+}
+
+H323Connection::AnswerCallResponse MyH323Connection::OnAnswerCall(const PString & caller,
+ const H323SignalPDU & setupPDU,
+ H323SignalPDU & connectPDU)
+{
+ unsigned pi;
+
+ if (h323debug) {
+ cout << "\t=-= In OnAnswerCall for call " << GetCallReference() << endl;
+ }
+
+ if (connectionState == ShuttingDownConnection)
+ return H323Connection::AnswerCallDenied;
+
+ if (!setupPDU.GetQ931().GetProgressIndicator(pi)) {
+ pi = 0;
+ }
+ if (h323debug) {
+ cout << "\t\t- Progress Indicator: " << pi << endl;
+ }
+ if (progressAlert) {
+ pi = progressAlert;
+ } else if (pi == Q931::ProgressOriginNotISDN) {
+ pi = Q931::ProgressInbandInformationAvailable;
+ }
+ if (pi && alertingPDU) {
+ alertingPDU->GetQ931().SetProgressIndicator(pi);
+ }
+ if (h323debug) {
+ cout << "\t\t- Inserting PI of " << pi << " into ALERTING message" << endl;
+ }
+
+#ifdef TUNNELLING
+ if (alertingPDU)
+ EmbedTunneledInfo(*alertingPDU);
+ EmbedTunneledInfo(connectPDU);
+#endif
+
+ if (!on_answer_call(GetCallReference(), (const char *)GetCallToken())) {
+ return H323Connection::AnswerCallDenied;
+ }
+ /* The call will be answered later with "AnsweringCall()" function.
+ */
+ return ((pi || (fastStartState != FastStartDisabled)) ? AnswerCallDeferredWithMedia : AnswerCallDeferred);
+}
+
+BOOL MyH323Connection::OnAlerting(const H323SignalPDU & alertingPDU, const PString & username)
+{
+ if (h323debug) {
+ cout << "\t=-= In OnAlerting for call " << GetCallReference()
+ << ": sessionId=" << sessionId << endl;
+ cout << "\t-- Ringing phone for \"" << username << "\"" << endl;
+ }
+
+ if (on_progress) {
+ BOOL isInband;
+ unsigned alertingPI;
+
+ if (!alertingPDU.GetQ931().GetProgressIndicator(alertingPI)) {
+ alertingPI = 0;
+ }
+ if (h323debug) {
+ cout << "\t\t- Progress Indicator: " << alertingPI << endl;
+ }
+
+ switch(alertingPI) {
+ case Q931::ProgressNotEndToEndISDN:
+ case Q931::ProgressInbandInformationAvailable:
+ isInband = TRUE;
+ break;
+ default:
+ isInband = FALSE;
+ }
+ on_progress(GetCallReference(), (const char *)GetCallToken(), isInband);
+ }
+ on_chan_ringing(GetCallReference(), (const char *)GetCallToken() );
+ return connectionState != ShuttingDownConnection;
+}
+
+void MyH323Connection::SetCallOptions(void *o, BOOL isIncoming)
+{
+ call_options_t *opts = (call_options_t *)o;
+
+ progressSetup = opts->progress_setup;
+ progressAlert = opts->progress_alert;
+ holdHandling = opts->holdHandling;
+ dtmfCodec[0] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[0];
+ dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[1];
+ dtmfMode = opts->dtmfmode;
+
+ if (isIncoming) {
+ fastStartState = (opts->fastStart ? FastStartInitiate : FastStartDisabled);
+ h245Tunneling = (opts->h245Tunneling ? TRUE : FALSE);
+ } else {
+ SetLocalPartyName(PString(opts->cid_num));
+ SetDisplayName(PString(opts->cid_name));
+ if (opts->redirect_reason >= 0) {
+ rdnis = PString(opts->cid_rdnis);
+ redirect_reason = opts->redirect_reason;
+ }
+ cid_presentation = opts->presentation;
+ cid_ton = opts->type_of_number;
+ if (opts->transfer_capability >= 0) {
+ transfer_capability = opts->transfer_capability;
+ }
+ }
+ tunnelOptions = opts->tunnelOptions;
+}
+
+void MyH323Connection::SetCallDetails(void *callDetails, const H323SignalPDU &setupPDU, BOOL isIncoming)
+{
+ PString sourceE164;
+ PString destE164;
+ PString sourceAliases;
+ PString destAliases;
+ char *s, *s1;
+ call_details_t *cd = (call_details_t *)callDetails;
+
+ memset(cd, 0, sizeof(*cd));
+ cd->call_reference = GetCallReference();
+ cd->call_token = strdup((const char *)GetCallToken());
+
+ sourceE164 = "";
+ setupPDU.GetSourceE164(sourceE164);
+ cd->call_source_e164 = strdup((const char *)sourceE164);
+
+ destE164 = "";
+ setupPDU.GetDestinationE164(destE164);
+ cd->call_dest_e164 = strdup((const char *)destE164);
+
+ /* XXX Is it possible to have this information for outgoing calls too? XXX */
+ if (isIncoming) {
+ PString sourceName;
+ PIPSocket::Address Ip;
+ WORD sourcePort;
+ PString redirect_number;
+ unsigned redirect_reason;
+ unsigned plan, type, screening, presentation;
+ Q931::InformationTransferCapability capability;
+ unsigned transferRate, codingStandard, userInfoLayer1;
+
+ /* Fetch presentation and type information about calling party's number */
+ if (setupPDU.GetQ931().GetCallingPartyNumber(sourceName, &plan, &type, &presentation, &screening, 0, 0)) {
+ /* Construct fields back */
+ cd->type_of_number = (type << 4) | plan;
+ cd->presentation = (presentation << 5) | screening;
+ } else if (cd->call_source_e164[0]) {
+ cd->type_of_number = 0; /* UNKNOWN */
+ cd->presentation = 0x03; /* ALLOWED NETWORK NUMBER - Default */
+ if (setupPDU.GetQ931().HasIE(Q931::UserUserIE)) {
+ const H225_Setup_UUIE &setup_uuie = setupPDU.m_h323_uu_pdu.m_h323_message_body;
+ if (setup_uuie.HasOptionalField(H225_Setup_UUIE::e_presentationIndicator))
+ cd->presentation = (cd->presentation & 0x9f) | (((unsigned int)setup_uuie.m_presentationIndicator.GetTag()) << 5);
+ if (setup_uuie.HasOptionalField(H225_Setup_UUIE::e_screeningIndicator))
+ cd->presentation = (cd->presentation & 0xe0) | (((unsigned int)setup_uuie.m_screeningIndicator.GetValue()) & 0x1f);
+ }
+ } else {
+ cd->type_of_number = 0; /* UNKNOWN */
+ cd->presentation = 0x43; /* NUMBER NOT AVAILABLE */
+ }
+
+ sourceName = setupPDU.GetQ931().GetDisplayName();
+ cd->call_source_name = strdup((const char *)sourceName);
+
+ GetSignallingChannel()->GetRemoteAddress().GetIpAndPort(Ip, sourcePort);
+ cd->sourceIp = strdup((const char *)Ip.AsString());
+
+ if (setupPDU.GetQ931().GetRedirectingNumber(redirect_number, NULL, NULL, NULL, NULL, &redirect_reason, 0, 0, 0)) {
+ cd->redirect_number = strdup((const char *)redirect_number);
+ cd->redirect_reason = redirect_reason;
+ }
+ else
+ cd->redirect_reason = -1;
+
+ /* Fetch Q.931's transfer capability */
+ if (((Q931 &)setupPDU.GetQ931()).GetBearerCapabilities(capability, transferRate, &codingStandard, &userInfoLayer1))
+ cd->transfer_capability = ((unsigned int)capability & 0x1f) | (codingStandard << 5);
+ else
+ cd->transfer_capability = 0x00; /* ITU coding of Speech */
+
+ /* Don't show local username as called party name */
+ SetDisplayName(cd->call_dest_e164);
+ }
+
+ /* Convert complex strings */
+ // FIXME: deal more than one source alias
+ sourceAliases = setupPDU.GetSourceAliases();
+ s1 = strdup((const char *)sourceAliases);
+ if ((s = strchr(s1, ' ')) != NULL)
+ *s = '\0';
+ if ((s = strchr(s1, '\t')) != NULL)
+ *s = '\0';
+ cd->call_source_aliases = s1;
+
+ destAliases = setupPDU.GetDestinationAlias();
+ s1 = strdup((const char *)destAliases);
+ if ((s = strchr(s1, ' ')) != NULL)
+ *s = '\0';
+ if ((s = strchr(s1, '\t')) != NULL)
+ *s = '\0';
+ cd->call_dest_alias = s1;
+}
+
+#ifdef TUNNELLING
+static BOOL FetchInformationElements(Q931 &q931, const PBYTEArray &data)
+{
+ PINDEX offset = 0;
+
+ while (offset < data.GetSize()) {
+ // Get field discriminator
+ int discriminator = data[offset++];
+
+#if 0
+ /* Do not overwrite existing IEs */
+ if (q931.HasIE((Q931::InformationElementCodes)discriminator)) {
+ if ((discriminatir & 0x80) == 0)
+ offset += data[offset++];
+ if (offset > data.GetSize())
+ return FALSE;
+ continue;
+ }
+#endif
+
+ PBYTEArray * item = new PBYTEArray;
+
+ // For discriminator with high bit set there is no data
+ if ((discriminator & 0x80) == 0) {
+ int len = data[offset++];
+
+#if 0 // That is not H.225 but regular Q.931 (ISDN) IEs
+ if (discriminator == UserUserIE) {
+ // Special case of User-user field. See 7.2.2.31/H.225.0v4.
+ len <<= 8;
+ len |= data[offset++];
+
+ // we also have a protocol discriminator, which we ignore
+ offset++;
+
+ // before decrementing the length, make sure it is not zero
+ if (len == 0)
+ return FALSE;
+
+ // adjust for protocol discriminator
+ len--;
+ }
+#endif
+
+ if (offset + len > data.GetSize()) {
+ delete item;
+ return FALSE;
+ }
+
+ memcpy(item->GetPointer(len), (const BYTE *)data+offset, len);
+ offset += len;
+ }
+
+ q931.SetIE((Q931::InformationElementCodes)discriminator, *item);
+ delete item;
+ }
+ return TRUE;
+}
+
+static BOOL FetchCiscoTunneledInfo(Q931 &q931, const H323SignalPDU &pdu)
+{
+ BOOL res = FALSE;
+ const H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu;
+
+ if(uuPDU.HasOptionalField(H225_H323_UU_PDU::e_nonStandardControl)) {
+ for(int i = 0; i < uuPDU.m_nonStandardControl.GetSize(); ++i) {
+ const H225_NonStandardParameter &np = uuPDU.m_nonStandardControl[i];
+ const H225_NonStandardIdentifier &id = np.m_nonStandardIdentifier;
+ if (id.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) {
+ const H225_H221NonStandard &ni = id;
+ /* Check for Cisco */
+ if ((ni.m_t35CountryCode == 181) && (ni.m_t35Extension == 0) && (ni.m_manufacturerCode == 18)) {
+ const PBYTEArray &data = np.m_data;
+ if (h323debug)
+ cout << setprecision(0) << "Received non-standard Cisco extension data " << np.m_data << endl;
+ CISCO_H225_H323_UU_NonStdInfo c;
+ PPER_Stream strm(data);
+ if (c.Decode(strm)) {
+ BOOL haveIEs = FALSE;
+ if (h323debug)
+ cout << setprecision(0) << "H323_UU_NonStdInfo = " << c << endl;
+ if (c.HasOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_protoParam)) {
+ FetchInformationElements(q931, c.m_protoParam.m_qsigNonStdInfo.m_rawMesg);
+ haveIEs = TRUE;
+ }
+ if (c.HasOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_commonParam)) {
+ FetchInformationElements(q931, c.m_commonParam.m_redirectIEinfo.m_redirectIE);
+ haveIEs = TRUE;
+ }
+ if (haveIEs && h323debug)
+ cout << setprecision(0) << "Information elements collected:" << q931 << endl;
+ res = TRUE;
+ } else {
+ cout << "ERROR while decoding non-standard Cisco extension" << endl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+static BOOL EmbedCiscoTunneledInfo(H323SignalPDU &pdu)
+{
+ const static struct {
+ Q931::InformationElementCodes ie;
+ BOOL dontDelete;
+ } codes[] = {
+ { Q931::RedirectingNumberIE, },
+ { Q931::FacilityIE, },
+// { Q931::CallingPartyNumberIE, TRUE },
+ };
+
+ BOOL res = FALSE;
+ BOOL notRedirOnly = FALSE;
+ Q931 tmpQ931;
+ Q931 &q931 = pdu.GetQ931();
+
+ for(unsigned i = 0; i < (sizeof(codes) / sizeof(codes[0])); ++i) {
+ if (q931.HasIE(codes[i].ie)) {
+ tmpQ931.SetIE(codes[i].ie, q931.GetIE(codes[i].ie));
+ if (!codes[i].dontDelete)
+ q931.RemoveIE(codes[i].ie);
+ if (codes[i].ie != Q931::RedirectingNumberIE)
+ notRedirOnly = TRUE;
+ res = TRUE;
+ }
+ }
+ /* Have something to embed */
+ if (res) {
+ PBYTEArray msg;
+ if (!tmpQ931.Encode(msg))
+ return FALSE;
+ PBYTEArray ies(msg.GetPointer() + 5, msg.GetSize() - 5);
+
+ H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu;
+ if(!uuPDU.HasOptionalField(H225_H323_UU_PDU::e_nonStandardControl)) {
+ uuPDU.IncludeOptionalField(H225_H323_UU_PDU::e_nonStandardControl);
+ uuPDU.m_nonStandardControl.SetSize(0);
+ }
+ H225_NonStandardParameter *np = new H225_NonStandardParameter;
+ uuPDU.m_nonStandardControl.Append(np);
+ H225_NonStandardIdentifier &nsi = (*np).m_nonStandardIdentifier;
+ nsi.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
+ H225_H221NonStandard &ns = nsi;
+ ns.m_t35CountryCode = 181;
+ ns.m_t35Extension = 0;
+ ns.m_manufacturerCode = 18;
+
+ CISCO_H225_H323_UU_NonStdInfo c;
+ c.IncludeOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_version);
+ c.m_version = 0;
+
+ if (notRedirOnly) {
+ c.IncludeOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_protoParam);
+ CISCO_H225_QsigNonStdInfo &qsigInfo = c.m_protoParam.m_qsigNonStdInfo;
+ qsigInfo.m_iei = ies[0];
+ qsigInfo.m_rawMesg = ies;
+ } else {
+ c.IncludeOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_commonParam);
+ c.m_commonParam.m_redirectIEinfo.m_redirectIE = ies;
+ }
+ PPER_Stream stream;
+ c.Encode(stream);
+ stream.CompleteEncoding();
+ (*np).m_data = stream;
+ }
+ return res;
+}
+
+static const char OID_QSIG[] = "1.3.12.9";
+
+static BOOL FetchQSIGTunneledInfo(Q931 &q931, const H323SignalPDU &pdu)
+{
+ BOOL res = FALSE;
+ const H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu;
+ if (uuPDU.HasOptionalField(H225_H323_UU_PDU::e_tunnelledSignallingMessage)) {
+ const H225_H323_UU_PDU_tunnelledSignallingMessage &sig = uuPDU.m_tunnelledSignallingMessage;
+ const H225_TunnelledProtocol_id &proto = sig.m_tunnelledProtocolID.m_id;
+ if ((proto.GetTag() == H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) &&
+ (((const PASN_ObjectId &)proto).AsString() == OID_QSIG)) {
+ const H225_ArrayOf_PASN_OctetString &sigs = sig.m_messageContent;
+ for(int i = 0; i < sigs.GetSize(); ++i) {
+ const PASN_OctetString &msg = sigs[i];
+ if (h323debug)
+ cout << setprecision(0) << "Q.931 message data is " << msg << endl;
+ if(!q931.Decode((const PBYTEArray &)msg)) {
+ cout << "Error while decoding Q.931 message" << endl;
+ return FALSE;
+ }
+ res = TRUE;
+ if (h323debug)
+ cout << setprecision(0) << "Received QSIG message " << q931 << endl;
+ }
+ }
+ }
+ return res;
+}
+
+static H225_EndpointType *GetEndpointType(H323SignalPDU &pdu)
+{
+ if (!pdu.GetQ931().HasIE(Q931::UserUserIE))
+ return NULL;
+
+ H225_H323_UU_PDU_h323_message_body &body = pdu.m_h323_uu_pdu.m_h323_message_body;
+ switch (body.GetTag()) {
+ case H225_H323_UU_PDU_h323_message_body::e_setup:
+ return &((H225_Setup_UUIE &)body).m_sourceInfo;
+ case H225_H323_UU_PDU_h323_message_body::e_callProceeding:
+ return &((H225_CallProceeding_UUIE &)body).m_destinationInfo;
+ case H225_H323_UU_PDU_h323_message_body::e_connect:
+ return &((H225_Connect_UUIE &)body).m_destinationInfo;
+ case H225_H323_UU_PDU_h323_message_body::e_alerting:
+ return &((H225_Alerting_UUIE &)body).m_destinationInfo;
+ case H225_H323_UU_PDU_h323_message_body::e_facility:
+ return &((H225_Facility_UUIE &)body).m_destinationInfo;
+ case H225_H323_UU_PDU_h323_message_body::e_progress:
+ return &((H225_Progress_UUIE &)body).m_destinationInfo;
+ }
+ return NULL;
+}
+
+static BOOL QSIGTunnelRequested(H323SignalPDU &pdu)
+{
+ H225_EndpointType *epType = GetEndpointType(pdu);
+ if (epType) {
+ if (!(*epType).HasOptionalField(H225_EndpointType::e_supportedTunnelledProtocols)) {
+ return FALSE;
+ }
+ H225_ArrayOf_TunnelledProtocol &protos = (*epType).m_supportedTunnelledProtocols;
+ for (int i = 0; i < protos.GetSize(); ++i)
+ {
+ if ((protos[i].GetTag() == H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) &&
+ (((const PASN_ObjectId &)protos[i]).AsString() == OID_QSIG)) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static BOOL EmbedQSIGTunneledInfo(H323SignalPDU &pdu)
+{
+ const static Q931::InformationElementCodes codes[] =
+ { Q931::RedirectingNumberIE, Q931::FacilityIE };
+
+ Q931 &q931 = pdu.GetQ931();
+ PBYTEArray message;
+
+ q931.Encode(message);
+
+ /* Remove non-standard IEs */
+ for(unsigned i = 0; i < (sizeof(codes) / sizeof(codes[0])); ++i) {
+ if (q931.HasIE(codes[i])) {
+ q931.RemoveIE(codes[i]);
+ }
+ }
+
+ H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu;
+ H225_EndpointType *epType = GetEndpointType(pdu);
+ if (epType) {
+ if (!(*epType).HasOptionalField(H225_EndpointType::e_supportedTunnelledProtocols)) {
+ (*epType).IncludeOptionalField(H225_EndpointType::e_supportedTunnelledProtocols);
+ (*epType).m_supportedTunnelledProtocols.SetSize(0);
+ }
+ H225_ArrayOf_TunnelledProtocol &protos = (*epType).m_supportedTunnelledProtocols;
+ BOOL addQSIG = TRUE;
+ for (int i = 0; i < protos.GetSize(); ++i)
+ {
+ if ((protos[i].GetTag() == H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) &&
+ (((PASN_ObjectId &)protos[i]).AsString() == OID_QSIG)) {
+ addQSIG = FALSE;
+ break;
+ }
+ }
+ if (addQSIG) {
+ H225_TunnelledProtocol *proto = new H225_TunnelledProtocol;
+ (*proto).m_id.SetTag(H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID);
+ (PASN_ObjectId &)(proto->m_id) = OID_QSIG;
+ protos.Append(proto);
+ }
+ }
+ if (!uuPDU.HasOptionalField(H225_H323_UU_PDU::e_tunnelledSignallingMessage))
+ uuPDU.IncludeOptionalField(H225_H323_UU_PDU::e_tunnelledSignallingMessage);
+ H225_H323_UU_PDU_tunnelledSignallingMessage &sig = uuPDU.m_tunnelledSignallingMessage;
+ H225_TunnelledProtocol_id &proto = sig.m_tunnelledProtocolID.m_id;
+ if ((proto.GetTag() != H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) ||
+ (((const PASN_ObjectId &)proto).AsString() != OID_QSIG)) {
+ proto.SetTag(H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID);
+ (PASN_ObjectId &)proto = OID_QSIG;
+ sig.m_messageContent.SetSize(0);
+ }
+ PASN_OctetString *msg = new PASN_OctetString;
+ sig.m_messageContent.Append(msg);
+ *msg = message;
+ return TRUE;
+}
+
+BOOL MyH323Connection::EmbedTunneledInfo(H323SignalPDU &pdu)
+{
+ if ((tunnelOptions & H323_TUNNEL_QSIG) || (remoteTunnelOptions & H323_TUNNEL_QSIG))
+ EmbedQSIGTunneledInfo(pdu);
+ if ((tunnelOptions & H323_TUNNEL_CISCO) || (remoteTunnelOptions & H323_TUNNEL_CISCO))
+ EmbedCiscoTunneledInfo(pdu);
+
+ return TRUE;
+}
+
+/* Handle tunneled messages */
+BOOL MyH323Connection::HandleSignalPDU(H323SignalPDU &pdu)
+{
+ if (pdu.GetQ931().HasIE(Q931::UserUserIE)) {
+ Q931 tunneledInfo;
+ const Q931 *q931Info;
+
+ q931Info = NULL;
+ if (FetchCiscoTunneledInfo(tunneledInfo, pdu)) {
+ q931Info = &tunneledInfo;
+ remoteTunnelOptions |= H323_TUNNEL_CISCO;
+ }
+ if (FetchQSIGTunneledInfo(tunneledInfo, pdu)) {
+ q931Info = &tunneledInfo;
+ remoteTunnelOptions |= H323_TUNNEL_QSIG;
+ }
+ if (!(remoteTunnelOptions & H323_TUNNEL_QSIG) && QSIGTunnelRequested(pdu)) {
+ remoteTunnelOptions |= H323_TUNNEL_QSIG;
+ }
+ if (q931Info) {
+ if (q931Info->HasIE(Q931::RedirectingNumberIE)) {
+ pdu.GetQ931().SetIE(Q931::RedirectingNumberIE, q931Info->GetIE(Q931::RedirectingNumberIE));
+ if (h323debug) {
+ PString number;
+ unsigned reason;
+ if(q931Info->GetRedirectingNumber(number, NULL, NULL, NULL, NULL, &reason, 0, 0, 0))
+ cout << "Got redirection from " << number << ", reason " << reason << endl;
+ }
+ }
+ }
+ }
+
+ return H323Connection::HandleSignalPDU(pdu);
+}
+#endif
+
+BOOL MyH323Connection::OnReceivedSignalSetup(const H323SignalPDU & setupPDU)
+{
+ call_details_t cd;
+
+ if (h323debug) {
+ cout << "\t--Received SETUP message" << endl;
+ }
+
+ if (connectionState == ShuttingDownConnection)
+ return FALSE;
+
+ SetCallDetails(&cd, setupPDU, TRUE);
+
+ /* Notify Asterisk of the request */
+ call_options_t *res = on_incoming_call(&cd);
+
+ if (!res) {
+ if (h323debug) {
+ cout << "\t-- Call Failed" << endl;
+ }
+ return FALSE;
+ }
+
+ SetCallOptions(res, TRUE);
+
+ /* Disable fastStart if requested by remote side */
+ if (h245Tunneling && !setupPDU.m_h323_uu_pdu.m_h245Tunneling) {
+ masterSlaveDeterminationProcedure->Stop();
+ capabilityExchangeProcedure->Stop();
+ PTRACE(3, "H225\tFast Start DISABLED!");
+ h245Tunneling = FALSE;
+ }
+
+ return H323Connection::OnReceivedSignalSetup(setupPDU);
+}
+
+BOOL MyH323Connection::OnSendSignalSetup(H323SignalPDU & setupPDU)
+{
+ call_details_t cd;
+
+ if (h323debug) {
+ cout << "\t-- Sending SETUP message" << endl;
+ }
+
+ if (connectionState == ShuttingDownConnection)
+ return FALSE;
+
+ if (progressSetup)
+ setupPDU.GetQ931().SetProgressIndicator(progressSetup);
+
+ if (redirect_reason >= 0) {
+ setupPDU.GetQ931().SetRedirectingNumber(rdnis, 0, 0, 0, 0, redirect_reason);
+ /* OpenH323 incorrectly fills number IE when redirecting reason is specified - fix it */
+ PBYTEArray IE(setupPDU.GetQ931().GetIE(Q931::RedirectingNumberIE));
+ IE[0] = IE[0] & 0x7f;
+ IE[1] = IE[1] & 0x7f;
+ setupPDU.GetQ931().SetIE(Q931::RedirectingNumberIE, IE);
+ }
+
+ if (transfer_capability)
+ setupPDU.GetQ931().SetBearerCapabilities((Q931::InformationTransferCapability)(transfer_capability & 0x1f), 1, ((transfer_capability >> 5) & 3));
+
+ SetCallDetails(&cd, setupPDU, FALSE);
+
+ int res = on_outgoing_call(&cd);
+ if (!res) {
+ if (h323debug) {
+ cout << "\t-- Call Failed" << endl;
+ }
+ return FALSE;
+ }
+
+ /* OpenH323 will build calling party information with default
+ type and presentation information, so build it to be recorded
+ by embedding routines */
+ setupPDU.GetQ931().SetCallingPartyNumber(GetLocalPartyName(), (cid_ton >> 4) & 0x07,
+ cid_ton & 0x0f, (cid_presentation >> 5) & 0x03, cid_presentation & 0x1f);
+ setupPDU.GetQ931().SetDisplayName(GetDisplayName());
+
+#ifdef TUNNELLING
+ EmbedTunneledInfo(setupPDU);
+#endif
+
+ return H323Connection::OnSendSignalSetup(setupPDU);
+}
+
+static BOOL BuildFastStartList(const H323Channel & channel,
+ H225_ArrayOf_PASN_OctetString & array,
+ H323Channel::Directions reverseDirection)
+{
+ H245_OpenLogicalChannel open;
+ const H323Capability & capability = channel.GetCapability();
+
+ if (channel.GetDirection() != reverseDirection) {
+ if (!capability.OnSendingPDU(open.m_forwardLogicalChannelParameters.m_dataType))
+ return FALSE;
+ }
+ else {
+ if (!capability.OnSendingPDU(open.m_reverseLogicalChannelParameters.m_dataType))
+ return FALSE;
+
+ open.m_forwardLogicalChannelParameters.m_multiplexParameters.SetTag(
+ H245_OpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters::e_none);
+ open.m_forwardLogicalChannelParameters.m_dataType.SetTag(H245_DataType::e_nullData);
+ open.IncludeOptionalField(H245_OpenLogicalChannel::e_reverseLogicalChannelParameters);
+ }
+
+ if (!channel.OnSendingPDU(open))
+ return FALSE;
+
+ PTRACE(4, "H225\tBuild fastStart:\n " << setprecision(2) << open);
+ PINDEX last = array.GetSize();
+ array.SetSize(last+1);
+ array[last].EncodeSubType(open);
+
+ PTRACE(3, "H225\tBuilt fastStart for " << capability);
+ return TRUE;
+}
+
+H323Connection::CallEndReason MyH323Connection::SendSignalSetup(const PString & alias,
+ const H323TransportAddress & address)
+{
+ // Start the call, first state is asking gatekeeper
+ connectionState = AwaitingGatekeeperAdmission;
+
+ // Indicate the direction of call.
+ if (alias.IsEmpty())
+ remotePartyName = remotePartyAddress = address;
+ else {
+ remotePartyName = alias;
+ remotePartyAddress = alias + '@' + address;
+ }
+
+ // Start building the setup PDU to get various ID's
+ H323SignalPDU setupPDU;
+ H225_Setup_UUIE & setup = setupPDU.BuildSetup(*this, address);
+
+#ifdef H323_H450
+ h450dispatcher->AttachToSetup(setupPDU);
+#endif
+
+ // Save the identifiers generated by BuildSetup
+ setupPDU.GetQ931().GetCalledPartyNumber(remotePartyNumber);
+
+ H323TransportAddress gatekeeperRoute = address;
+
+ // Check for gatekeeper and do admission check if have one
+ H323Gatekeeper * gatekeeper = endpoint.GetGatekeeper();
+ H225_ArrayOf_AliasAddress newAliasAddresses;
+ if (gatekeeper != NULL) {
+ H323Gatekeeper::AdmissionResponse response;
+ response.transportAddress = &gatekeeperRoute;
+ response.aliasAddresses = &newAliasAddresses;
+ if (!gkAccessTokenOID)
+ response.accessTokenData = &gkAccessTokenData;
+ while (!gatekeeper->AdmissionRequest(*this, response, alias.IsEmpty())) {
+ PTRACE(1, "H225\tGatekeeper refused admission: "
+ << (response.rejectReason == UINT_MAX
+ ? PString("Transport error")
+ : H225_AdmissionRejectReason(response.rejectReason).GetTagName()));
+#ifdef H323_H450
+ h4502handler->onReceivedAdmissionReject(H4501_GeneralErrorList::e_notAvailable);
+#endif
+
+ switch (response.rejectReason) {
+ case H225_AdmissionRejectReason::e_calledPartyNotRegistered:
+ return EndedByNoUser;
+ case H225_AdmissionRejectReason::e_requestDenied:
+ return EndedByNoBandwidth;
+ case H225_AdmissionRejectReason::e_invalidPermission:
+ case H225_AdmissionRejectReason::e_securityDenial:
+ return EndedBySecurityDenial;
+ case H225_AdmissionRejectReason::e_resourceUnavailable:
+ return EndedByRemoteBusy;
+ case H225_AdmissionRejectReason::e_incompleteAddress:
+ if (OnInsufficientDigits())
+ break;
+ // Then default case
+ default:
+ return EndedByGatekeeper;
+ }
+
+ PString lastRemotePartyName = remotePartyName;
+ while (lastRemotePartyName == remotePartyName) {
+ Unlock(); // Release the mutex as can deadlock trying to clear call during connect.
+ digitsWaitFlag.Wait();
+ if (!Lock()) // Lock while checking for shutting down.
+ return EndedByCallerAbort;
+ }
+ }
+ mustSendDRQ = TRUE;
+ if (response.gatekeeperRouted) {
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_endpointIdentifier);
+ setup.m_endpointIdentifier = gatekeeper->GetEndpointIdentifier();
+ gatekeeperRouted = TRUE;
+ }
+ }
+
+#ifdef H323_TRANSNEXUS_OSP
+ // check for OSP server (if not using GK)
+ if (gatekeeper == NULL) {
+ OpalOSP::Provider * ospProvider = endpoint.GetOSPProvider();
+ if (ospProvider != NULL) {
+ OpalOSP::Transaction * transaction = new OpalOSP::Transaction();
+ if (transaction->Open(*ospProvider) != 0) {
+ PTRACE(1, "H225\tCannot create OSP transaction");
+ return EndedByOSPRefusal;
+ }
+
+ OpalOSP::Transaction::DestinationInfo destInfo;
+ if (!AuthoriseOSPTransaction(*transaction, destInfo)) {
+ delete transaction;
+ return EndedByOSPRefusal;
+ }
+
+ // save the transaction for use by the call
+ ospTransaction = transaction;
+
+ // retreive the call information
+ gatekeeperRoute = destInfo.destinationAddress;
+ newAliasAddresses.Append(new H225_AliasAddress(destInfo.calledNumber));
+
+ // insert the token
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_tokens);
+ destInfo.InsertToken(setup.m_tokens);
+ }
+ }
+#endif
+
+ // Update the field e_destinationAddress in the SETUP PDU to reflect the new
+ // alias received in the ACF (m_destinationInfo).
+ if (newAliasAddresses.GetSize() > 0) {
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_destinationAddress);
+ setup.m_destinationAddress = newAliasAddresses;
+
+ // Update the Q.931 Information Element (if is an E.164 address)
+ PString e164 = H323GetAliasAddressE164(newAliasAddresses);
+ if (!e164)
+ remotePartyNumber = e164;
+ }
+
+ if (addAccessTokenToSetup && !gkAccessTokenOID && !gkAccessTokenData.IsEmpty()) {
+ PString oid1, oid2;
+ PINDEX comma = gkAccessTokenOID.Find(',');
+ if (comma == P_MAX_INDEX)
+ oid1 = oid2 = gkAccessTokenOID;
+ else {
+ oid1 = gkAccessTokenOID.Left(comma);
+ oid2 = gkAccessTokenOID.Mid(comma+1);
+ }
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_tokens);
+ PINDEX last = setup.m_tokens.GetSize();
+ setup.m_tokens.SetSize(last+1);
+ setup.m_tokens[last].m_tokenOID = oid1;
+ setup.m_tokens[last].IncludeOptionalField(H235_ClearToken::e_nonStandard);
+ setup.m_tokens[last].m_nonStandard.m_nonStandardIdentifier = oid2;
+ setup.m_tokens[last].m_nonStandard.m_data = gkAccessTokenData;
+ }
+
+ if (!signallingChannel->SetRemoteAddress(gatekeeperRoute)) {
+ PTRACE(1, "H225\tInvalid "
+ << (gatekeeperRoute != address ? "gatekeeper" : "user")
+ << " supplied address: \"" << gatekeeperRoute << '"');
+ connectionState = AwaitingTransportConnect;
+ return EndedByConnectFail;
+ }
+
+ // Do the transport connect
+ connectionState = AwaitingTransportConnect;
+
+ // Release the mutex as can deadlock trying to clear call during connect.
+ Unlock();
+
+ signallingChannel->SetWriteTimeout(100);
+
+ BOOL connectFailed = !signallingChannel->Connect();
+
+ // Lock while checking for shutting down.
+ if (!Lock())
+ return EndedByCallerAbort;
+
+ // See if transport connect failed, abort if so.
+ if (connectFailed) {
+ connectionState = NoConnectionActive;
+ switch (signallingChannel->GetErrorNumber()) {
+ case ENETUNREACH :
+ return EndedByUnreachable;
+ case ECONNREFUSED :
+ return EndedByNoEndPoint;
+ case ETIMEDOUT :
+ return EndedByHostOffline;
+ }
+ return EndedByConnectFail;
+ }
+
+ PTRACE(3, "H225\tSending Setup PDU");
+ connectionState = AwaitingSignalConnect;
+
+ // Put in all the signalling addresses for link
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress);
+ signallingChannel->SetUpTransportPDU(setup.m_sourceCallSignalAddress, TRUE);
+ if (!setup.HasOptionalField(H225_Setup_UUIE::e_destCallSignalAddress)) {
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_destCallSignalAddress);
+ signallingChannel->SetUpTransportPDU(setup.m_destCallSignalAddress, FALSE);
+ }
+
+ // If a standard call do Fast Start (if required)
+ if (setup.m_conferenceGoal.GetTag() == H225_Setup_UUIE_conferenceGoal::e_create) {
+
+ // Get the local capabilities before fast start is handled
+ OnSetLocalCapabilities();
+
+ // Ask the application what channels to open
+ PTRACE(3, "H225\tCheck for Fast start by local endpoint");
+ fastStartChannels.RemoveAll();
+ OnSelectLogicalChannels();
+
+ // If application called OpenLogicalChannel, put in the fastStart field
+ if (!fastStartChannels.IsEmpty()) {
+ PTRACE(3, "H225\tFast start begun by local endpoint");
+ for (PINDEX i = 0; i < fastStartChannels.GetSize(); i++)
+ BuildFastStartList(fastStartChannels[i], setup.m_fastStart, H323Channel::IsReceiver);
+ if (setup.m_fastStart.GetSize() > 0)
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_fastStart);
+ }
+
+ // Search the capability set and see if we have video capability
+ for (PINDEX i = 0; i < localCapabilities.GetSize(); i++) {
+ switch (localCapabilities[i].GetMainType()) {
+ case H323Capability::e_Audio:
+ case H323Capability::e_UserInput:
+ break;
+
+ default: // Is video or other data (eg T.120)
+ setupPDU.GetQ931().SetBearerCapabilities(Q931::TransferUnrestrictedDigital, 6);
+ i = localCapabilities.GetSize(); // Break out of the for loop
+ break;
+ }
+ }
+ }
+
+ if (!OnSendSignalSetup(setupPDU))
+ return EndedByNoAccept;
+
+ // Do this again (was done when PDU was constructed) in case
+ // OnSendSignalSetup() changed something.
+// setupPDU.SetQ931Fields(*this, TRUE);
+ setupPDU.GetQ931().GetCalledPartyNumber(remotePartyNumber);
+
+ fastStartState = FastStartDisabled;
+ BOOL set_lastPDUWasH245inSETUP = FALSE;
+
+ if (h245Tunneling && doH245inSETUP) {
+ h245TunnelTxPDU = &setupPDU;
+
+ // Try and start the master/slave and capability exchange through the tunnel
+ // Note: this used to be disallowed but is now allowed as of H323v4
+ BOOL ok = StartControlNegotiations();
+
+ h245TunnelTxPDU = NULL;
+
+ if (!ok)
+ return EndedByTransportFail;
+
+ if (setup.m_fastStart.GetSize() > 0) {
+ // Now if fast start as well need to put this in setup specific field
+ // and not the generic H.245 tunneling field
+ setup.IncludeOptionalField(H225_Setup_UUIE::e_parallelH245Control);
+ setup.m_parallelH245Control = setupPDU.m_h323_uu_pdu.m_h245Control;
+ setupPDU.m_h323_uu_pdu.RemoveOptionalField(H225_H323_UU_PDU::e_h245Control);
+ set_lastPDUWasH245inSETUP = TRUE;
+ }
+ }
+
+ // Send the initial PDU
+ setupTime = PTime();
+ if (!WriteSignalPDU(setupPDU))
+ return EndedByTransportFail;
+
+ // WriteSignalPDU always resets lastPDUWasH245inSETUP.
+ // So set it here if required
+ if (set_lastPDUWasH245inSETUP)
+ lastPDUWasH245inSETUP = TRUE;
+
+ // Set timeout for remote party to answer the call
+ signallingChannel->SetReadTimeout(endpoint.GetSignallingChannelCallTimeout());
+
+ return NumCallEndReasons;
+}
+
+
+BOOL MyH323Connection::OnSendReleaseComplete(H323SignalPDU & releaseCompletePDU)
+{
+ if (h323debug) {
+ cout << "\t-- Sending RELEASE COMPLETE" << endl;
+ }
+ if (cause > 0)
+ releaseCompletePDU.GetQ931().SetCause((Q931::CauseValues)cause);
+
+#ifdef TUNNELLING
+ EmbedTunneledInfo(releaseCompletePDU);
+#endif
+
+ return H323Connection::OnSendReleaseComplete(releaseCompletePDU);
+}
+
+BOOL MyH323Connection::OnReceivedFacility(const H323SignalPDU & pdu)
+{
+ if (h323debug) {
+ cout << "\t-- Received Facility message... " << endl;
+ }
+ return H323Connection::OnReceivedFacility(pdu);
+}
+
+void MyH323Connection::OnReceivedReleaseComplete(const H323SignalPDU & pdu)
+{
+ if (h323debug) {
+ cout << "\t-- Received RELEASE COMPLETE message..." << endl;
+ }
+ if (on_hangup)
+ on_hangup(GetCallReference(), (const char *)GetCallToken(), pdu.GetQ931().GetCause());
+ return H323Connection::OnReceivedReleaseComplete(pdu);
+}
+
+BOOL MyH323Connection::OnClosingLogicalChannel(H323Channel & channel)
+{
+ if (h323debug) {
+ cout << "\t-- Closing logical channel..." << endl;
+ }
+ return H323Connection::OnClosingLogicalChannel(channel);
+}
+
+void MyH323Connection::SendUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp)
+{
+ SendUserInputModes mode = GetRealSendUserInputMode();
+// That is recursive call... Why?
+// on_receive_digit(GetCallReference(), tone, (const char *)GetCallToken());
+ if ((tone != ' ') || (mode == SendUserInputAsTone) || (mode == SendUserInputAsInlineRFC2833)) {
+ if (h323debug) {
+ cout << "\t-- Sending user input tone (" << tone << ") to remote" << endl;
+ }
+ H323Connection::SendUserInputTone(tone, duration);
+ }
+}
+
+void MyH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp)
+{
+ /* Why we should check this? */
+ if ((dtmfMode & (H323_DTMF_CISCO | H323_DTMF_RFC2833 | H323_DTMF_SIGNAL)) != 0) {
+ if (h323debug) {
+ cout << "\t-- Received user input tone (" << tone << ") from remote" << endl;
+ }
+ on_receive_digit(GetCallReference(), tone, (const char *)GetCallToken(), duration);
+ }
+}
+
+void MyH323Connection::OnUserInputString(const PString &value)
+{
+ if (h323debug) {
+ cout << "\t-- Received user input string (" << value << ") from remote." << endl;
+ }
+ on_receive_digit(GetCallReference(), value[0], (const char *)GetCallToken(), 0);
+}
+
+void MyH323Connection::OnSendCapabilitySet(H245_TerminalCapabilitySet & pdu)
+{
+ PINDEX i;
+
+ H323Connection::OnSendCapabilitySet(pdu);
+
+ H245_ArrayOf_CapabilityTableEntry & tables = pdu.m_capabilityTable;
+ for(i = 0; i < tables.GetSize(); i++)
+ {
+ H245_CapabilityTableEntry & entry = tables[i];
+ if (entry.HasOptionalField(H245_CapabilityTableEntry::e_capability)) {
+ H245_Capability & cap = entry.m_capability;
+ if (cap.GetTag() == H245_Capability::e_receiveRTPAudioTelephonyEventCapability) {
+ H245_AudioTelephonyEventCapability & atec = cap;
+ atec.m_dynamicRTPPayloadType = dtmfCodec[0];
+// on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)dtmfCodec[0]);
+#ifdef PTRACING
+ if (h323debug) {
+ cout << "\t-- Receiving RFC2833 on payload " <<
+ atec.m_dynamicRTPPayloadType << endl;
+ }
+#endif
+ }
+ }
+ }
+}
+
+void MyH323Connection::OnSetLocalCapabilities()
+{
+ if (on_setcapabilities)
+ on_setcapabilities(GetCallReference(), (const char *)callToken);
+}
+
+BOOL MyH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCaps,
+ const H245_MultiplexCapability * muxCap,
+ H245_TerminalCapabilitySetReject & reject)
+{
+ struct __codec__ {
+ unsigned int asterisk_codec;
+ unsigned int h245_cap;
+ const char *oid;
+ const char *formatName;
+ };
+ static const struct __codec__ codecs[] = {
+ { AST_FORMAT_G723_1, H245_AudioCapability::e_g7231 },
+ { AST_FORMAT_GSM, H245_AudioCapability::e_gsmFullRate },
+ { AST_FORMAT_ULAW, H245_AudioCapability::e_g711Ulaw64k },
+ { AST_FORMAT_ALAW, H245_AudioCapability::e_g711Alaw64k },
+ { AST_FORMAT_G729A, H245_AudioCapability::e_g729AnnexA },
+ { AST_FORMAT_G729A, H245_AudioCapability::e_g729 },
+ { AST_FORMAT_G726_AAL2, H245_AudioCapability::e_nonStandard, NULL, CISCO_G726r32 },
+#ifdef AST_FORMAT_MODEM
+ { AST_FORMAT_MODEM, H245_DataApplicationCapability_application::e_t38fax },
+#endif
+ { 0 }
+ };
+
+#if 0
+ static const struct __codec__ vcodecs[] = {
+#ifdef HAVE_H261
+ { AST_FORMAT_H261, H245_VideoCapability::e_h261VideoCapability },
+#endif
+#ifdef HAVE_H263
+ { AST_FORMAT_H263, H245_VideoCapability::e_h263VideoCapability },
+#endif
+#ifdef HAVE_H264
+ { AST_FORMAT_H264, H245_VideoCapability::e_genericVideoCapability, "0.0.8.241.0.0.1" },
+#endif
+ { 0 }
+ };
+#endif
+ struct ast_codec_pref prefs;
+ RTP_DataFrame::PayloadTypes pt;
+
+ if (!H323Connection::OnReceivedCapabilitySet(remoteCaps, muxCap, reject)) {
+ return FALSE;
+ }
+
+ memset(&prefs, 0, sizeof(prefs));
+ int peer_capabilities = 0;
+ for (int i = 0; i < remoteCapabilities.GetSize(); ++i) {
+ unsigned int subType = remoteCapabilities[i].GetSubType();
+ if (h323debug) {
+ cout << "Peer capability is " << remoteCapabilities[i] << endl;
+ }
+ switch(remoteCapabilities[i].GetMainType()) {
+ case H323Capability::e_Audio:
+ for (int x = 0; codecs[x].asterisk_codec > 0; ++x) {
+ if ((subType == codecs[x].h245_cap) && (!codecs[x].formatName || (!strcmp(codecs[x].formatName, (const char *)remoteCapabilities[i].GetFormatName())))) {
+ int ast_codec = codecs[x].asterisk_codec;
+ int ms = 0;
+ if (!(peer_capabilities & ast_codec)) {
+ struct ast_format_list format;
+ ast_codec_pref_append(&prefs, ast_codec);
+ format = ast_codec_pref_getsize(&prefs, ast_codec);
+ if ((ast_codec == AST_FORMAT_ALAW) || (ast_codec == AST_FORMAT_ULAW)) {
+ ms = remoteCapabilities[i].GetTxFramesInPacket();
+ if (ms > 60)
+ ms = format.cur_ms;
+ } else
+ ms = remoteCapabilities[i].GetTxFramesInPacket() * format.inc_ms;
+ ast_codec_pref_setsize(&prefs, ast_codec, ms);
+ }
+ if (h323debug) {
+ cout << "Found peer capability " << remoteCapabilities[i] << ", Asterisk code is " << ast_codec << ", frame size (in ms) is " << ms << endl;
+ }
+ peer_capabilities |= ast_codec;
+ }
+ }
+ break;
+ case H323Capability::e_Data:
+ if (!strcmp((const char *)remoteCapabilities[i].GetFormatName(), CISCO_DTMF_RELAY)) {
+ pt = remoteCapabilities[i].GetPayloadType();
+ if ((dtmfMode & H323_DTMF_CISCO) != 0) {
+ on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)pt, 1);
+// if (sendUserInputMode == SendUserInputAsTone)
+// sendUserInputMode = SendUserInputAsInlineRFC2833;
+ }
+#ifdef PTRACING
+ if (h323debug) {
+ cout << "\t-- Outbound Cisco RTP DTMF on payload " << pt << endl;
+ }
+#endif
+ }
+ break;
+ case H323Capability::e_UserInput:
+ if (!strcmp((const char *)remoteCapabilities[i].GetFormatName(), H323_UserInputCapability::SubTypeNames[H323_UserInputCapability::SignalToneRFC2833])) {
+ pt = remoteCapabilities[i].GetPayloadType();
+ if ((dtmfMode & H323_DTMF_RFC2833) != 0) {
+ on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)pt, 0);
+// if (sendUserInputMode == SendUserInputAsTone)
+// sendUserInputMode = SendUserInputAsInlineRFC2833;
+ }
+#ifdef PTRACING
+ if (h323debug) {
+ cout << "\t-- Outbound RFC2833 on payload " << pt << endl;
+ }
+#endif
+ }
+ break;
+#if 0
+ case H323Capability::e_Video:
+ for (int x = 0; vcodecs[x].asterisk_codec > 0; ++x) {
+ if (subType == vcodecs[x].h245_cap) {
+ H245_CapabilityIdentifier *cap = NULL;
+ H245_GenericCapability y;
+ if (vcodecs[x].oid) {
+ cap = new H245_CapabilityIdentifier(H245_CapabilityIdentifier::e_standard);
+ PASN_ObjectId &object_id = *cap;
+ object_id = vcodecs[x].oid;
+ y.m_capabilityIdentifier = *cap;
+ }
+ if ((subType != H245_VideoCapability::e_genericVideoCapability) ||
+ (vcodecs[x].oid && ((const H323GenericVideoCapability &)remoteCapabilities[i]).IsGenericMatch((const H245_GenericCapability)y))) {
+ if (h323debug) {
+ cout << "Found peer video capability " << remoteCapabilities[i] << ", Asterisk code is " << vcodecs[x].asterisk_codec << endl;
+ }
+ peer_capabilities |= vcodecs[x].asterisk_codec;
+ }
+ if (cap)
+ delete(cap);
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ if (h323debug) {
+ char caps_str[1024], caps2_str[1024];
+ ast_codec_pref_string(&prefs, caps2_str, sizeof(caps2_str));
+ cout << "Peer capabilities = " << ast_getformatname_multiple(caps_str, sizeof(caps_str), peer_capabilities)
+ << ", ordered list is " << caps2_str << endl;
+ }
+#if 0
+ redir_capabilities &= peer_capabilities;
+#endif
+ if (on_setpeercapabilities)
+ on_setpeercapabilities(GetCallReference(), (const char *)callToken, peer_capabilities, &prefs);
+
+ return TRUE;
+}
+
+H323Channel * MyH323Connection::CreateRealTimeLogicalChannel(const H323Capability & capability,
+ H323Channel::Directions dir,
+ unsigned sessionID,
+ const H245_H2250LogicalChannelParameters * /*param*/,
+ RTP_QOS * /*param*/ )
+{
+ /* Do not open tx channel when transmitter has been paused by empty TCS */
+ if ((dir == H323Channel::IsTransmitter) && transmitterSidePaused)
+ return NULL;
+
+ return new MyH323_ExternalRTPChannel(*this, capability, dir, sessionID);
+}
+
+/** This callback function is invoked once upon creation of each
+ * channel for an H323 session
+ */
+BOOL MyH323Connection::OnStartLogicalChannel(H323Channel & channel)
+{
+ /* Increase the count of channels we have open */
+ channelsOpen++;
+
+ if (h323debug) {
+ cout << "\t-- Started logical channel: "
+ << ((channel.GetDirection() == H323Channel::IsTransmitter) ? "sending " : ((channel.GetDirection() == H323Channel::IsReceiver) ? "receiving " : " "))
+ << (const char *)(channel.GetCapability()).GetFormatName() << endl;
+ cout << "\t\t-- channelsOpen = " << channelsOpen << endl;
+ }
+ return connectionState != ShuttingDownConnection;
+}
+
+void MyH323Connection::SetCapabilities(int caps, int dtmf_mode, void *_prefs, int pref_codec)
+{
+ PINDEX lastcap = -1; /* last common capability index */
+ int alreadysent = 0;
+ int codec;
+ int x, y;
+ char caps_str[1024];
+ struct ast_codec_pref *prefs = (struct ast_codec_pref *)_prefs;
+ struct ast_format_list format;
+ int frames_per_packet;
+ int max_frames_per_packet;
+ H323Capability *cap;
+
+ localCapabilities.RemoveAll();
+
+ if (h323debug) {
+ cout << "Setting capabilities to " << ast_getformatname_multiple(caps_str, sizeof(caps_str), caps) << endl;
+ ast_codec_pref_string(prefs, caps_str, sizeof(caps_str));
+ cout << "Capabilities in preference order is " << caps_str << endl;
+ }
+ /* Add audio codecs in preference order first, then
+ audio codecs without preference as allowed by mask */
+ for (y = 0, x = -1; x < 32 + 32; ++x) {
+ if (x < 0)
+ codec = pref_codec;
+ else if (y || (!(codec = ast_codec_pref_index(prefs, x)))) {
+ if (!y)
+ y = 1;
+ else
+ y <<= 1;
+ codec = y;
+ }
+ if (!(caps & codec) || (alreadysent & codec) || !(codec & AST_FORMAT_AUDIO_MASK))
+ continue;
+ alreadysent |= codec;
+ format = ast_codec_pref_getsize(prefs, codec);
+ frames_per_packet = (format.inc_ms ? format.cur_ms / format.inc_ms : format.cur_ms);
+ max_frames_per_packet = (format.inc_ms ? format.max_ms / format.inc_ms : 0);
+ switch(codec) {
+#if 0
+ case AST_FORMAT_SPEEX:
+ /* Not real sure if Asterisk acutally supports all
+ of the various different bit rates so add them
+ all and figure it out later*/
+
+ lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow2AudioCapability());
+ lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow3AudioCapability());
+ lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow4AudioCapability());
+ lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow5AudioCapability());
+ lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow6AudioCapability());
+ break;
+#endif
+ case AST_FORMAT_G729A:
+ AST_G729ACapability *g729aCap;
+ AST_G729Capability *g729Cap;
+ lastcap = localCapabilities.SetCapability(0, 0, g729aCap = new AST_G729ACapability(frames_per_packet));
+ lastcap = localCapabilities.SetCapability(0, 0, g729Cap = new AST_G729Capability(frames_per_packet));
+ if (max_frames_per_packet) {
+ g729aCap->SetTxFramesInPacket(max_frames_per_packet);
+ g729Cap->SetTxFramesInPacket(max_frames_per_packet);
+ }
+ break;
+ case AST_FORMAT_G723_1:
+ AST_G7231Capability *g7231Cap;
+ lastcap = localCapabilities.SetCapability(0, 0, g7231Cap = new AST_G7231Capability(frames_per_packet, TRUE));
+ if (max_frames_per_packet)
+ g7231Cap->SetTxFramesInPacket(max_frames_per_packet);
+ lastcap = localCapabilities.SetCapability(0, 0, g7231Cap = new AST_G7231Capability(frames_per_packet, FALSE));
+ if (max_frames_per_packet)
+ g7231Cap->SetTxFramesInPacket(max_frames_per_packet);
+ break;
+ case AST_FORMAT_GSM:
+ AST_GSM0610Capability *gsmCap;
+ lastcap = localCapabilities.SetCapability(0, 0, gsmCap = new AST_GSM0610Capability(frames_per_packet));
+ if (max_frames_per_packet)
+ gsmCap->SetTxFramesInPacket(max_frames_per_packet);
+ break;
+ case AST_FORMAT_ULAW:
+ AST_G711Capability *g711uCap;
+ lastcap = localCapabilities.SetCapability(0, 0, g711uCap = new AST_G711Capability(format.cur_ms, H323_G711Capability::muLaw));
+ if (format.max_ms)
+ g711uCap->SetTxFramesInPacket(format.max_ms);
+ break;
+ case AST_FORMAT_ALAW:
+ AST_G711Capability *g711aCap;
+ lastcap = localCapabilities.SetCapability(0, 0, g711aCap = new AST_G711Capability(format.cur_ms, H323_G711Capability::ALaw));
+ if (format.max_ms)
+ g711aCap->SetTxFramesInPacket(format.max_ms);
+ break;
+ case AST_FORMAT_G726_AAL2:
+ AST_CiscoG726Capability *g726Cap;
+ lastcap = localCapabilities.SetCapability(0, 0, g726Cap = new AST_CiscoG726Capability(frames_per_packet));
+ if (max_frames_per_packet)
+ g726Cap->SetTxFramesInPacket(max_frames_per_packet);
+ break;
+ default:
+ alreadysent &= ~codec;
+ break;
+ }
+ }
+
+ cap = new H323_UserInputCapability(H323_UserInputCapability::HookFlashH245);
+ if (cap && cap->IsUsable(*this)) {
+ lastcap++;
+ lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+ } else if (cap)
+ delete cap; /* Capability is not usable */
+
+ dtmfMode = dtmf_mode;
+ if (h323debug) {
+ cout << "DTMF mode is " << (int)dtmfMode << endl;
+ }
+ if (dtmfMode) {
+ lastcap++;
+ if (dtmfMode == H323_DTMF_INBAND) {
+ cap = new H323_UserInputCapability(H323_UserInputCapability::BasicString);
+ if (cap && cap->IsUsable(*this)) {
+ lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+ } else if (cap)
+ delete cap; /* Capability is not usable */
+ sendUserInputMode = SendUserInputAsString;
+ } else {
+ if ((dtmfMode & H323_DTMF_RFC2833) != 0) {
+ cap = new H323_UserInputCapability(H323_UserInputCapability::SignalToneRFC2833);
+ if (cap && cap->IsUsable(*this))
+ lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+ else {
+ dtmfMode |= H323_DTMF_SIGNAL;
+ if (cap)
+ delete cap; /* Capability is not usable */
+ }
+ }
+ if ((dtmfMode & H323_DTMF_CISCO) != 0) {
+ /* Try Cisco's RTP DTMF relay too, but prefer RFC2833 or h245-signal */
+ cap = new AST_CiscoDtmfCapability();
+ if (cap && cap->IsUsable(*this)) {
+ lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+ /* We cannot send Cisco RTP DTMFs, use h245-signal instead */
+ dtmfMode |= H323_DTMF_SIGNAL;
+ } else {
+ dtmfMode |= H323_DTMF_SIGNAL;
+ if (cap)
+ delete cap; /* Capability is not usable */
+ }
+ }
+ if ((dtmfMode & H323_DTMF_SIGNAL) != 0) {
+ /* Cisco usually sends DTMF correctly only through h245-alphanumeric or h245-signal */
+ cap = new H323_UserInputCapability(H323_UserInputCapability::SignalToneH245);
+ if (cap && cap->IsUsable(*this))
+ lastcap = localCapabilities.SetCapability(0, lastcap, cap);
+ else if (cap)
+ delete cap; /* Capability is not usable */
+ }
+ sendUserInputMode = SendUserInputAsTone; /* RFC2833 transmission handled at Asterisk level */
+ }
+ }
+
+ if (h323debug) {
+ cout << "Allowed Codecs for " << GetCallToken() << " (" << GetSignallingChannel()->GetLocalAddress() << "):\n\t" << setprecision(2) << localCapabilities << endl;
+ }
+}
+
+BOOL MyH323Connection::StartControlChannel(const H225_TransportAddress & h245Address)
+{
+ // Check that it is an IP address, all we support at the moment
+ if (h245Address.GetTag() != H225_TransportAddress::e_ipAddress
+#if P_HAS_IPV6
+ && h245Address.GetTag() != H225_TransportAddress::e_ip6Address
+#endif
+ ) {
+ PTRACE(1, "H225\tConnect of H245 failed: Unsupported transport");
+ return FALSE;
+ }
+
+ // Already have the H245 channel up.
+ if (controlChannel != NULL)
+ return TRUE;
+
+ PIPSocket::Address addr;
+ WORD port;
+ GetSignallingChannel()->GetLocalAddress().GetIpAndPort(addr, port);
+ if (addr) {
+ if (h323debug)
+ cout << "Using " << addr << " for outbound H.245 transport" << endl;
+ controlChannel = new MyH323TransportTCP(endpoint, addr);
+ } else
+ controlChannel = new H323TransportTCP(endpoint);
+ if (!controlChannel->SetRemoteAddress(h245Address)) {
+ PTRACE(1, "H225\tCould not extract H245 address");
+ delete controlChannel;
+ controlChannel = NULL;
+ return FALSE;
+ }
+ if (!controlChannel->Connect()) {
+ PTRACE(1, "H225\tConnect of H245 failed: " << controlChannel->GetErrorText());
+ delete controlChannel;
+ controlChannel = NULL;
+ return FALSE;
+ }
+
+ controlChannel->StartControlChannel(*this);
+ return TRUE;
+}
+
+#ifdef H323_H450
+void MyH323Connection::OnReceivedLocalCallHold(int linkedId)
+{
+ if (on_hold)
+ on_hold(GetCallReference(), (const char *)GetCallToken(), 1);
+}
+
+void MyH323Connection::OnReceivedLocalCallRetrieve(int linkedId)
+{
+ if (on_hold)
+ on_hold(GetCallReference(), (const char *)GetCallToken(), 0);
+}
+#endif
+
+void MyH323Connection::MyHoldCall(BOOL isHold)
+{
+ if (((holdHandling & H323_HOLD_NOTIFY) != 0) || ((holdHandling & H323_HOLD_Q931ONLY) != 0)) {
+ PBYTEArray x ((const BYTE *)(isHold ? "\xF9" : "\xFA"), 1);
+ H323SignalPDU signal;
+ signal.BuildNotify(*this);
+ signal.GetQ931().SetIE((Q931::InformationElementCodes)39 /* Q931::NotifyIE */, x);
+ if (h323debug)
+ cout << "Sending " << (isHold ? "HOLD" : "RETRIEVE") << " notification: " << signal << endl;
+ if ((holdHandling & H323_HOLD_Q931ONLY) != 0) {
+ PBYTEArray rawData;
+ signal.GetQ931().RemoveIE(Q931::UserUserIE);
+ signal.GetQ931().Encode(rawData);
+ signallingChannel->WritePDU(rawData);
+ } else
+ WriteSignalPDU(signal);
+ }
+#ifdef H323_H450
+ if ((holdHandling & H323_HOLD_H450) != 0) {
+ if (isHold)
+ h4504handler->HoldCall(TRUE);
+ else if (IsLocalHold())
+ h4504handler->RetrieveCall();
+ }
+#endif
+}
+
+
+/* MyH323_ExternalRTPChannel */
+MyH323_ExternalRTPChannel::MyH323_ExternalRTPChannel(MyH323Connection & connection,
+ const H323Capability & capability,
+ Directions direction,
+ unsigned id)
+ : H323_ExternalRTPChannel::H323_ExternalRTPChannel(connection, capability, direction, id)
+{
+ struct rtp_info *info;
+
+ /* Determine the Local (A side) IP Address and port */
+ info = on_external_rtp_create(connection.GetCallReference(), (const char *)connection.GetCallToken());
+ if (!info) {
+ cout << "\tERROR: on_external_rtp_create failure" << endl;
+ return;
+ } else {
+ localIpAddr = info->addr;
+ localPort = info->port;
+ /* tell the H.323 stack */
+ SetExternalAddress(H323TransportAddress(localIpAddr, localPort), H323TransportAddress(localIpAddr, localPort + 1));
+ /* clean up allocated memory */
+ free(info);
+ }
+
+ /* Get the payload code */
+ OpalMediaFormat format(capability.GetFormatName(), FALSE);
+ payloadCode = format.GetPayloadType();
+}
+
+MyH323_ExternalRTPChannel::~MyH323_ExternalRTPChannel()
+{
+ if (h323debug) {
+ cout << "\tExternalRTPChannel Destroyed" << endl;
+ }
+}
+
+BOOL MyH323_ExternalRTPChannel::Start(void)
+{
+ /* Call ancestor first */
+ if (!H323_ExternalRTPChannel::Start()) {
+ return FALSE;
+ }
+
+ if (h323debug) {
+ cout << "\t\tExternal RTP Session Starting" << endl;
+ cout << "\t\tRTP channel id " << sessionID << " parameters:" << endl;
+ }
+
+ /* Collect the remote information */
+ H323_ExternalRTPChannel::GetRemoteAddress(remoteIpAddr, remotePort);
+
+ if (h323debug) {
+ cout << "\t\t-- remoteIpAddress: " << remoteIpAddr << endl;
+ cout << "\t\t-- remotePort: " << remotePort << endl;
+ cout << "\t\t-- ExternalIpAddress: " << localIpAddr << endl;
+ cout << "\t\t-- ExternalPort: " << localPort << endl;
+ }
+ /* Notify Asterisk of remote RTP information */
+ on_start_rtp_channel(connection.GetCallReference(), (const char *)remoteIpAddr.AsString(), remotePort,
+ (const char *)connection.GetCallToken(), (int)payloadCode);
+ return TRUE;
+}
+
+BOOL MyH323_ExternalRTPChannel::OnReceivedAckPDU(const H245_H2250LogicalChannelAckParameters & param)
+{
+ if (h323debug) {
+ cout << " MyH323_ExternalRTPChannel::OnReceivedAckPDU" << endl;
+ }
+
+ if (H323_ExternalRTPChannel::OnReceivedAckPDU(param)) {
+ GetRemoteAddress(remoteIpAddr, remotePort);
+ if (h323debug) {
+ cout << " -- remoteIpAddress: " << remoteIpAddr << endl;
+ cout << " -- remotePort: " << remotePort << endl;
+ }
+ on_start_rtp_channel(connection.GetCallReference(), (const char *)remoteIpAddr.AsString(),
+ remotePort, (const char *)connection.GetCallToken(), (int)payloadCode);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#ifdef H323_H450
+MyH4504Handler::MyH4504Handler(MyH323Connection &_conn, H450xDispatcher &_disp)
+ :H4504Handler(_conn, _disp)
+{
+ conn = &_conn;
+}
+
+void MyH4504Handler::OnReceivedLocalCallHold(int linkedId)
+{
+ if (conn) {
+ conn->Lock();
+ conn->OnReceivedLocalCallHold(linkedId);
+ conn->Unlock();
+ }
+}
+
+void MyH4504Handler::OnReceivedLocalCallRetrieve(int linkedId)
+{
+ if (conn) {
+ conn->Lock();
+ conn->OnReceivedLocalCallRetrieve(linkedId);
+ conn->Unlock();
+ }
+}
+#endif
+
+
+/** IMPLEMENTATION OF C FUNCTIONS */
+
+/**
+ * The extern "C" directive takes care for
+ * the ANSI-C representation of linkable symbols
+ */
+
+extern "C" {
+
+int h323_end_point_exist(void)
+{
+ if (!endPoint) {
+ return 0;
+ }
+ return 1;
+}
+
+void h323_end_point_create(void)
+{
+ channelsOpen = 0;
+ logstream = new PAsteriskLog();
+ localProcess = new MyProcess();
+ localProcess->Main();
+}
+
+void h323_gk_urq(void)
+{
+ if (!h323_end_point_exist()) {
+ cout << " ERROR: [h323_gk_urq] No Endpoint, this is bad" << endl;
+ return;
+ }
+ endPoint->RemoveGatekeeper();
+}
+
+void h323_debug(int flag, unsigned level)
+{
+ if (flag) {
+ PTrace:: SetLevel(level);
+ } else {
+ PTrace:: SetLevel(0);
+ }
+}
+
+/** Installs the callback functions on behalf of the PBX application */
+void h323_callback_register(setup_incoming_cb ifunc,
+ setup_outbound_cb sfunc,
+ on_rtp_cb rtpfunc,
+ start_rtp_cb lfunc,
+ clear_con_cb clfunc,
+ chan_ringing_cb rfunc,
+ con_established_cb efunc,
+ receive_digit_cb dfunc,
+ answer_call_cb acfunc,
+ progress_cb pgfunc,
+ rfc2833_cb dtmffunc,
+ hangup_cb hangupfunc,
+ setcapabilities_cb capabilityfunc,
+ setpeercapabilities_cb peercapabilityfunc,
+ onhold_cb holdfunc)
+{
+ on_incoming_call = ifunc;
+ on_outgoing_call = sfunc;
+ on_external_rtp_create = rtpfunc;
+ on_start_rtp_channel = lfunc;
+ on_connection_cleared = clfunc;
+ on_chan_ringing = rfunc;
+ on_connection_established = efunc;
+ on_receive_digit = dfunc;
+ on_answer_call = acfunc;
+ on_progress = pgfunc;
+ on_set_rfc2833_payload = dtmffunc;
+ on_hangup = hangupfunc;
+ on_setcapabilities = capabilityfunc;
+ on_setpeercapabilities = peercapabilityfunc;
+ on_hold = holdfunc;
+}
+
+/**
+ * Add capability to the capability table of the end point.
+ */
+int h323_set_capabilities(const char *token, int cap, int dtmf_mode, struct ast_codec_pref *prefs, int pref_codec)
+{
+ MyH323Connection *conn;
+
+ if (!h323_end_point_exist()) {
+ cout << " ERROR: [h323_set_capablities] No Endpoint, this is bad" << endl;
+ return 1;
+ }
+ if (!token || !*token) {
+ cout << " ERROR: [h323_set_capabilities] Invalid call token specified." << endl;
+ return 1;
+ }
+
+ PString myToken(token);
+ conn = (MyH323Connection *)endPoint->FindConnectionWithLock(myToken);
+ if (!conn) {
+ cout << " ERROR: [h323_set_capabilities] Unable to find connection " << token << endl;
+ return 1;
+ }
+ conn->SetCapabilities((/*conn->bridging ? conn->redir_capabilities :*/ cap), dtmf_mode, prefs, pref_codec);
+ conn->Unlock();
+
+ return 0;
+}
+
+/** Start the H.323 listener */
+int h323_start_listener(int listenPort, struct sockaddr_in bindaddr)
+{
+
+ if (!h323_end_point_exist()) {
+ cout << "ERROR: [h323_start_listener] No Endpoint, this is bad!" << endl;
+ return 1;
+ }
+
+ PIPSocket::Address interfaceAddress(bindaddr.sin_addr);
+ if (!listenPort) {
+ listenPort = 1720;
+ }
+ /** H.323 listener */
+ H323ListenerTCP *tcpListener;
+ tcpListener = new H323ListenerTCP(*endPoint, interfaceAddress, (WORD)listenPort);
+ if (!endPoint->StartListener(tcpListener)) {
+ cout << "ERROR: Could not open H.323 listener port on " << ((H323ListenerTCP *) tcpListener)->GetListenerPort() << endl;
+ delete tcpListener;
+ return 1;
+ }
+ cout << " == H.323 listener started" << endl;
+ return 0;
+};
+
+int h323_set_alias(struct oh323_alias *alias)
+{
+ char *p;
+ char *num;
+ PString h323id(alias->name);
+ PString e164(alias->e164);
+ char *prefix;
+
+ if (!h323_end_point_exist()) {
+ cout << "ERROR: [h323_set_alias] No Endpoint, this is bad!" << endl;
+ return 1;
+ }
+
+ cout << "== Adding alias \"" << h323id << "\" to endpoint" << endl;
+ endPoint->AddAliasName(h323id);
+ endPoint->RemoveAliasName(localProcess->GetUserName());
+
+ if (!e164.IsEmpty()) {
+ cout << "== Adding E.164 \"" << e164 << "\" to endpoint" << endl;
+ endPoint->AddAliasName(e164);
+ }
+ if (strlen(alias->prefix)) {
+ p = prefix = strdup(alias->prefix);
+ while((num = strsep(&p, ",")) != (char *)NULL) {
+ cout << "== Adding Prefix \"" << num << "\" to endpoint" << endl;
+ endPoint->SupportedPrefixes += PString(num);
+ endPoint->SetGateway();
+ }
+ if (prefix)
+ free(prefix);
+ }
+ return 0;
+}
+
+void h323_set_id(char *id)
+{
+ PString h323id(id);
+
+ if (h323debug) {
+ cout << " == Using '" << h323id << "' as our H.323ID for this call" << endl;
+ }
+ /* EVIL HACK */
+ endPoint->SetLocalUserName(h323id);
+}
+
+void h323_show_tokens(void)
+{
+ cout << "Current call tokens: " << setprecision(2) << endPoint->GetAllConnections() << endl;
+}
+
+/** Establish Gatekeeper communiations, if so configured,
+ * register aliases for the H.323 endpoint to respond to.
+ */
+int h323_set_gk(int gatekeeper_discover, char *gatekeeper, char *secret)
+{
+ PString gkName = PString(gatekeeper);
+ PString pass = PString(secret);
+ H323TransportUDP *rasChannel;
+
+ if (!h323_end_point_exist()) {
+ cout << "ERROR: [h323_set_gk] No Endpoint, this is bad!" << endl;
+ return 1;
+ }
+
+ if (!gatekeeper) {
+ cout << "Error: Gatekeeper cannot be NULL" << endl;
+ return 1;
+ }
+ if (strlen(secret)) {
+ endPoint->SetGatekeeperPassword(pass);
+ }
+ if (gatekeeper_discover) {
+ /* discover the gk using multicast */
+ if (endPoint->DiscoverGatekeeper(new MyH323TransportUDP(*endPoint))) {
+ cout << "== Using " << (endPoint->GetGatekeeper())->GetName() << " as our Gatekeeper." << endl;
+ } else {
+ cout << "Warning: Could not find a gatekeeper." << endl;
+ return 1;
+ }
+ } else {
+ rasChannel = new MyH323TransportUDP(*endPoint);
+
+ if (!rasChannel) {
+ cout << "Error: No RAS Channel, this is bad" << endl;
+ return 1;
+ }
+ if (endPoint->SetGatekeeper(gkName, rasChannel)) {
+ cout << "== Using " << (endPoint->GetGatekeeper())->GetName() << " as our Gatekeeper." << endl;
+ } else {
+ cout << "Error registering with gatekeeper \"" << gkName << "\". " << endl;
+ /* XXX Maybe we should fire a new thread to attempt to re-register later and not kill asterisk here? */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Send a DTMF tone over the H323Connection with the
+ * specified token.
+ */
+void h323_send_tone(const char *call_token, char tone)
+{
+ if (!h323_end_point_exist()) {
+ cout << "ERROR: [h323_send_tone] No Endpoint, this is bad!" << endl;
+ return;
+ }
+ PString token = PString(call_token);
+ endPoint->SendUserTone(token, tone);
+}
+
+/** Make a call to the remote endpoint.
+ */
+int h323_make_call(char *dest, call_details_t *cd, call_options_t *call_options)
+{
+ int res;
+ PString token;
+ PString host(dest);
+
+ if (!h323_end_point_exist()) {
+ return 1;
+ }
+
+ res = endPoint->MyMakeCall(host, token, &cd->call_reference, call_options);
+ memcpy((char *)(cd->call_token), (const unsigned char *)token, token.GetLength());
+ return res;
+};
+
+int h323_clear_call(const char *call_token, int cause)
+{
+ H225_ReleaseCompleteReason dummy;
+ H323Connection::CallEndReason r = H323Connection::EndedByLocalUser;
+ MyH323Connection *connection;
+ const PString currentToken(call_token);
+
+ if (!h323_end_point_exist()) {
+ return 1;
+ }
+
+ if (cause) {
+ r = H323TranslateToCallEndReason((Q931::CauseValues)(cause), dummy);
+ }
+
+ connection = (MyH323Connection *)endPoint->FindConnectionWithLock(currentToken);
+ if (connection) {
+ connection->SetCause(cause);
+ connection->SetCallEndReason(r);
+ connection->Unlock();
+ }
+ endPoint->ClearCall(currentToken, r);
+ return 0;
+};
+
+/* Send Alerting PDU to H.323 caller */
+int h323_send_alerting(const char *token)
+{
+ const PString currentToken(token);
+ H323Connection * connection;
+
+ if (h323debug) {
+ cout << "\tSending alerting" << endl;
+ }
+ connection = endPoint->FindConnectionWithLock(currentToken);
+ if (!connection) {
+ cout << "No connection found for " << token << endl;
+ return -1;
+ }
+ connection->AnsweringCall(H323Connection::AnswerCallPending);
+ connection->Unlock();
+ return 0;
+}
+
+/* Send Progress PDU to H.323 caller */
+int h323_send_progress(const char *token)
+{
+ const PString currentToken(token);
+ H323Connection * connection;
+
+ connection = endPoint->FindConnectionWithLock(currentToken);
+ if (!connection) {
+ cout << "No connection found for " << token << endl;
+ return -1;
+ }
+#if 1
+ ((MyH323Connection *)connection)->MySendProgress();
+#else
+ connection->AnsweringCall(H323Connection::AnswerCallDeferredWithMedia);
+#endif
+ connection->Unlock();
+ return 0;
+}
+
+/** This function tells the h.323 stack to either
+ answer or deny an incoming call */
+int h323_answering_call(const char *token, int busy)
+{
+ const PString currentToken(token);
+ H323Connection * connection;
+
+ connection = endPoint->FindConnectionWithLock(currentToken);
+
+ if (!connection) {
+ cout << "No connection found for " << token << endl;
+ return -1;
+ }
+ if (!busy) {
+ if (h323debug) {
+ cout << "\tAnswering call " << token << endl;
+ }
+ connection->AnsweringCall(H323Connection::AnswerCallNow);
+ } else {
+ if (h323debug) {
+ cout << "\tdenying call " << token << endl;
+ }
+ connection->AnsweringCall(H323Connection::AnswerCallDenied);
+ }
+ connection->Unlock();
+ return 0;
+}
+
+int h323_soft_hangup(const char *data)
+{
+ PString token(data);
+ BOOL result;
+ cout << "Soft hangup" << endl;
+ result = endPoint->ClearCall(token);
+ return result;
+}
+
+/* alas, this doesn't work :( */
+void h323_native_bridge(const char *token, const char *them, char *capability)
+{
+ H323Channel *channel;
+ MyH323Connection *connection = (MyH323Connection *)endPoint->FindConnectionWithLock(token);
+
+ if (!connection) {
+ cout << "ERROR: No connection found, this is bad" << endl;
+ return;
+ }
+
+ cout << "Native Bridge: them [" << them << "]" << endl;
+
+ channel = connection->FindChannel(connection->sessionId, TRUE);
+ connection->bridging = TRUE;
+ connection->CloseLogicalChannelNumber(channel->GetNumber());
+
+ connection->Unlock();
+ return;
+
+}
+
+int h323_hold_call(const char *token, int is_hold)
+{
+ MyH323Connection *conn = (MyH323Connection *)endPoint->FindConnectionWithLock(token);
+ if (!conn) {
+ cout << "ERROR: No connection found, this is bad" << endl;
+ return -1;
+ }
+ conn->MyHoldCall((BOOL)is_hold);
+ conn->Unlock();
+ return 0;
+}
+
+#undef cout
+#undef endl
+void h323_end_process(void)
+{
+ if (endPoint) {
+ endPoint->ClearAllCalls();
+ endPoint->RemoveListener(NULL);
+ delete endPoint;
+ endPoint = NULL;
+ }
+ if (localProcess) {
+ delete localProcess;
+ localProcess = NULL;
+ close(_timerChangePipe[0]);
+ close(_timerChangePipe[1]);
+ }
+ if (logstream) {
+ PTrace::SetLevel(0);
+ PTrace::SetStream(&cout);
+ delete logstream;
+ logstream = NULL;
+ }
+}
+
+} /* extern "C" */
+
diff --git a/trunk/channels/h323/ast_h323.h b/trunk/channels/h323/ast_h323.h
new file mode 100644
index 000000000..39af427ae
--- /dev/null
+++ b/trunk/channels/h323/ast_h323.h
@@ -0,0 +1,189 @@
+/*
+ * ast_h323.h
+ *
+ * OpenH323 Channel Driver for ASTERISK PBX.
+ * By Jeremy McNamara
+ * For The NuFone Network
+ *
+ * This code has been derived from code created by
+ * Michael Manousos and Mark Spencer
+ *
+ * This file is part of the chan_h323 driver for Asterisk
+ *
+ * chan_h323 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.
+ *
+ * chan_h323 is distributed 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Version Info: $Id$
+ */
+
+#ifndef AST_H323_H
+#define AST_H323_H
+
+#define VERSION(a,b,c) ((a)*10000+(b)*100+(c))
+
+class MyH323EndPoint : public H323EndPoint
+{
+ PCLASSINFO(MyH323EndPoint, H323EndPoint);
+
+public:
+ MyH323EndPoint();
+ int MyMakeCall(const PString &, PString &, void *_callReference, void *_opts);
+ BOOL ClearCall(const PString &, H323Connection::CallEndReason reason);
+ BOOL ClearCall(const PString &);
+
+ void OnClosedLogicalChannel(H323Connection &, const H323Channel &);
+ void OnConnectionEstablished(H323Connection &, const PString &);
+ void OnConnectionCleared(H323Connection &, const PString &);
+ virtual H323Connection * CreateConnection(unsigned, void *, H323Transport *, H323SignalPDU *);
+ void SendUserTone(const PString &, char);
+ BOOL OnConnectionForwarded(H323Connection &, const PString &, const H323SignalPDU &);
+ BOOL ForwardConnection(H323Connection &, const PString &, const H323SignalPDU &);
+ void SetEndpointTypeInfo( H225_EndpointType & info ) const;
+ void SetGateway(void);
+ PStringArray SupportedPrefixes;
+};
+
+class MyH323Connection : public H323Connection
+{
+ PCLASSINFO(MyH323Connection, H323Connection);
+
+public:
+ MyH323Connection(MyH323EndPoint &, unsigned, unsigned);
+ ~MyH323Connection();
+ H323Channel * CreateRealTimeLogicalChannel(const H323Capability &,
+ H323Channel::Directions,
+ unsigned,
+ const H245_H2250LogicalChannelParameters *,
+ RTP_QOS *);
+ H323Connection::AnswerCallResponse OnAnswerCall(const PString &,
+ const H323SignalPDU &,
+ H323SignalPDU &);
+ void OnReceivedReleaseComplete(const H323SignalPDU &);
+ BOOL OnAlerting(const H323SignalPDU &, const PString &);
+ BOOL OnSendReleaseComplete(H323SignalPDU &);
+ BOOL OnReceivedSignalSetup(const H323SignalPDU &);
+ BOOL OnReceivedFacility(const H323SignalPDU &);
+ BOOL OnSendSignalSetup(H323SignalPDU &);
+ BOOL OnStartLogicalChannel(H323Channel &);
+ BOOL OnClosingLogicalChannel(H323Channel &);
+ virtual void SendUserInputTone(char tone, unsigned duration = 0, unsigned logicalChannel = 0, unsigned rtpTimestamp = 0);
+ virtual void OnUserInputTone(char, unsigned, unsigned, unsigned);
+ virtual void OnUserInputString(const PString &value);
+ BOOL OnReceivedProgress(const H323SignalPDU &);
+ BOOL MySendProgress();
+ void OnSendCapabilitySet(H245_TerminalCapabilitySet &);
+ void OnSetLocalCapabilities();
+ void SetCapabilities(int, int, void *, int);
+ BOOL OnReceivedCapabilitySet(const H323Capabilities &, const H245_MultiplexCapability *,
+ H245_TerminalCapabilitySetReject &);
+ void SetCause(int _cause) { cause = _cause; };
+ virtual BOOL StartControlChannel(const H225_TransportAddress & h245Address);
+ void SetCallOptions(void *opts, BOOL isIncoming);
+ void SetCallDetails(void *callDetails, const H323SignalPDU &setupPDU, BOOL isIncoming);
+ virtual H323Connection::CallEndReason SendSignalSetup(const PString&, const H323TransportAddress&);
+#ifdef TUNNELLING
+ virtual BOOL HandleSignalPDU(H323SignalPDU &pdu);
+ BOOL EmbedTunneledInfo(H323SignalPDU &pdu);
+#endif
+#ifdef H323_H450
+ virtual void OnReceivedLocalCallHold(int linkedId);
+ virtual void OnReceivedLocalCallRetrieve(int linkedId);
+#endif
+ void MyHoldCall(BOOL localHold);
+
+ PString sourceAliases;
+ PString destAliases;
+ PString sourceE164;
+ PString destE164;
+ int cid_presentation;
+ int cid_ton;
+ PString rdnis;
+ int redirect_reason;
+ int transfer_capability;
+
+ WORD sessionId;
+ BOOL bridging;
+#ifdef TUNNELLING
+ int remoteTunnelOptions;
+ int tunnelOptions;
+#endif
+
+ unsigned holdHandling;
+ unsigned progressSetup;
+ unsigned progressAlert;
+ int cause;
+
+ RTP_DataFrame::PayloadTypes dtmfCodec[2];
+ int dtmfMode;
+};
+
+class MyH323_ExternalRTPChannel : public H323_ExternalRTPChannel
+{
+ PCLASSINFO(MyH323_ExternalRTPChannel, H323_ExternalRTPChannel);
+
+public:
+ MyH323_ExternalRTPChannel(
+ MyH323Connection & connection,
+ const H323Capability & capability,
+ Directions direction,
+ unsigned sessionID);
+
+ ~MyH323_ExternalRTPChannel();
+
+ /* Overrides */
+ BOOL Start(void);
+ BOOL OnReceivedAckPDU(const H245_H2250LogicalChannelAckParameters & param);
+
+protected:
+ BYTE payloadCode;
+
+ PIPSocket::Address localIpAddr;
+ PIPSocket::Address remoteIpAddr;
+ WORD localPort;
+ WORD remotePort;
+};
+
+/**
+ * The MyProcess is a necessary descendant PProcess class so that the H323EndPoint
+ * objected to be created from within that class. (Solves the who owns main() problem).
+ */
+class MyProcess : public PProcess
+{
+ PCLASSINFO(MyProcess, PProcess);
+
+public:
+ MyProcess();
+ ~MyProcess();
+ void Main();
+};
+
+#ifdef H323_H450
+#include <h450pdu.h>
+
+class MyH4504Handler : public H4504Handler
+{
+ PCLASSINFO(MyH4504Handler, H4504Handler);
+
+public:
+ MyH4504Handler(MyH323Connection &_conn, H450xDispatcher &_disp);
+ virtual void OnReceivedLocalCallHold(int linkedId);
+ virtual void OnReceivedLocalCallRetrieve(int linkedId);
+
+private:
+ MyH323Connection *conn;
+};
+#endif
+
+#include "compat_h323.h"
+
+#endif /* !defined AST_H323_H */
diff --git a/trunk/channels/h323/caps_h323.cxx b/trunk/channels/h323/caps_h323.cxx
new file mode 100644
index 000000000..ebb90f3f2
--- /dev/null
+++ b/trunk/channels/h323/caps_h323.cxx
@@ -0,0 +1,383 @@
+#include <ptlib.h>
+#include <h323.h>
+#include <h245.h>
+#include "ast_h323.h"
+#include "caps_h323.h"
+
+#define DEFINE_G711_CAPABILITY(cls, code, capName) \
+class cls : public AST_G711Capability { \
+public: \
+ cls() : AST_G711Capability(240, code) { } \
+}; \
+H323_REGISTER_CAPABILITY(cls, capName) \
+
+DEFINE_G711_CAPABILITY(AST_G711ALaw64Capability, H323_G711Capability::ALaw, OPAL_G711_ALAW_64K);
+DEFINE_G711_CAPABILITY(AST_G711uLaw64Capability, H323_G711Capability::muLaw, OPAL_G711_ULAW_64K);
+H323_REGISTER_CAPABILITY(AST_G7231Capability, OPAL_G7231);
+H323_REGISTER_CAPABILITY(AST_G729Capability, OPAL_G729);
+H323_REGISTER_CAPABILITY(AST_G729ACapability, OPAL_G729A);
+H323_REGISTER_CAPABILITY(AST_GSM0610Capability, OPAL_GSM0610);
+H323_REGISTER_CAPABILITY(AST_CiscoG726Capability, CISCO_G726r32);
+H323_REGISTER_CAPABILITY(AST_CiscoDtmfCapability, CISCO_DTMF_RELAY);
+
+OPAL_MEDIA_FORMAT_DECLARE(OpalG711ALaw64kFormat,
+ OPAL_G711_ALAW_64K,
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::PCMA,
+ TRUE, // Needs jitter
+ 64000, // bits/sec
+ 8, // bytes/frame
+ 8, // 1 millisecond/frame
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+OPAL_MEDIA_FORMAT_DECLARE(OpalG711uLaw64kFormat,
+ OPAL_G711_ULAW_64K,
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::PCMU,
+ TRUE, // Needs jitter
+ 64000, // bits/sec
+ 8, // bytes/frame
+ 8, // 1 millisecond/frame
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+OPAL_MEDIA_FORMAT_DECLARE(OpalG729Format,
+ OPAL_G729,
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::G729,
+ TRUE, // Needs jitter
+ 8000, // bits/sec
+ 10, // bytes
+ 80, // 10 milliseconds
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+OPAL_MEDIA_FORMAT_DECLARE(OpalG729AFormat,
+ OPAL_G729 "A",
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::G729,
+ TRUE, // Needs jitter
+ 8000, // bits/sec
+ 10, // bytes
+ 80, // 10 milliseconds
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+OPAL_MEDIA_FORMAT_DECLARE(OpalG7231_6k3Format,
+ OPAL_G7231_6k3,
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::G7231,
+ TRUE, // Needs jitter
+ 6400, // bits/sec
+ 24, // bytes
+ 240, // 30 milliseconds
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+OPAL_MEDIA_FORMAT_DECLARE(OpalG7231A_6k3Format,
+ OPAL_G7231A_6k3,
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::G7231,
+ TRUE, // Needs jitter
+ 6400, // bits/sec
+ 24, // bytes
+ 240, // 30 milliseconds
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+OPAL_MEDIA_FORMAT_DECLARE(OpalGSM0610Format,
+ OPAL_GSM0610,
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::GSM,
+ TRUE, // Needs jitter
+ 13200, // bits/sec
+ 33, // bytes
+ 160, // 20 milliseconds
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+OPAL_MEDIA_FORMAT_DECLARE(OpalCiscoG726Format,
+ CISCO_G726r32,
+ OpalMediaFormat::DefaultAudioSessionID,
+ RTP_DataFrame::G726,
+ TRUE, // Needs jitter
+ 32000, // bits/sec
+ 4, // bytes
+ 8, // 1 millisecond
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+#if 0
+OPAL_MEDIA_FORMAT_DECLARE(OpalCiscoDTMFRelayFormat,
+ CISCO_DTMF_RELAY,
+ OpalMediaFormat::DefaultAudioSessionID,
+ (RTP_DataFrame::PayloadTypes)121, // Choose this for Cisco IOS compatibility
+ TRUE, // Needs jitter
+ 100, // bits/sec
+ 4, // bytes/frame
+ 8*150, // 150 millisecond
+ OpalMediaFormat::AudioTimeUnits,
+ 0);
+#endif
+
+/*
+ * Capability: G.711
+ */
+AST_G711Capability::AST_G711Capability(int rx_frames, H323_G711Capability::Mode m, H323_G711Capability::Speed s)
+ : H323AudioCapability(rx_frames, 30) // 240ms max, 30ms desired
+{
+ mode = m;
+ speed = s;
+}
+
+
+PObject * AST_G711Capability::Clone() const
+{
+ return new AST_G711Capability(*this);
+}
+
+
+unsigned AST_G711Capability::GetSubType() const
+{
+ static const unsigned G711SubType[2][2] = {
+ { H245_AudioCapability::e_g711Alaw64k, H245_AudioCapability::e_g711Alaw56k },
+ { H245_AudioCapability::e_g711Ulaw64k, H245_AudioCapability::e_g711Ulaw56k }
+ };
+ return G711SubType[mode][speed];
+}
+
+
+PString AST_G711Capability::GetFormatName() const
+{
+ static const char * const G711Name[2][2] = {
+ { OPAL_G711_ALAW_64K, OPAL_G711_ALAW_56K },
+ { OPAL_G711_ULAW_64K, OPAL_G711_ULAW_56K },
+ };
+ return G711Name[mode][speed];
+}
+
+
+H323Codec * AST_G711Capability::CreateCodec(H323Codec::Direction direction) const
+{
+ return NULL;
+}
+
+
+/*
+ * Capability: G.723.1
+ */
+AST_G7231Capability::AST_G7231Capability(int rx_frames, BOOL annexA_)
+ : H323AudioCapability(rx_frames, 4)
+{
+ annexA = annexA_;
+}
+
+PObject::Comparison AST_G7231Capability::Compare(const PObject & obj) const
+{
+ Comparison result = H323AudioCapability::Compare(obj);
+ if (result != EqualTo) {
+ return result;
+ }
+ PINDEX otherAnnexA = ((const AST_G7231Capability &)obj).annexA;
+ if (annexA < otherAnnexA) {
+ return LessThan;
+ }
+ if (annexA > otherAnnexA) {
+ return GreaterThan;
+ }
+ return EqualTo;
+}
+
+PObject * AST_G7231Capability::Clone() const
+{
+ return new AST_G7231Capability(*this);
+}
+
+PString AST_G7231Capability::GetFormatName() const
+{
+ return (annexA ? OPAL_G7231 "A" : OPAL_G7231);
+}
+
+unsigned AST_G7231Capability::GetSubType() const
+{
+ return H245_AudioCapability::e_g7231;
+}
+
+BOOL AST_G7231Capability::OnSendingPDU(H245_AudioCapability & cap,
+ unsigned packetSize) const
+{
+ cap.SetTag(H245_AudioCapability::e_g7231);
+ H245_AudioCapability_g7231 & g7231 = cap;
+ g7231.m_maxAl_sduAudioFrames = packetSize;
+ g7231.m_silenceSuppression = annexA;
+ return TRUE;
+}
+
+BOOL AST_G7231Capability::OnReceivedPDU(const H245_AudioCapability & cap,
+ unsigned & packetSize)
+{
+ if (cap.GetTag() != H245_AudioCapability::e_g7231) {
+ return FALSE;
+ }
+ const H245_AudioCapability_g7231 & g7231 = cap;
+ packetSize = g7231.m_maxAl_sduAudioFrames;
+ annexA = g7231.m_silenceSuppression;
+ return TRUE;
+}
+
+H323Codec * AST_G7231Capability::CreateCodec(H323Codec::Direction direction) const
+{
+ return NULL;
+}
+
+/*
+ * Capability: G.729
+ */
+AST_G729Capability::AST_G729Capability(int rx_frames)
+ : H323AudioCapability(rx_frames, 2)
+{
+}
+
+PObject * AST_G729Capability::Clone() const
+{
+ return new AST_G729Capability(*this);
+}
+
+unsigned AST_G729Capability::GetSubType() const
+{
+ return H245_AudioCapability::e_g729;
+}
+
+PString AST_G729Capability::GetFormatName() const
+{
+ return OPAL_G729;
+}
+
+H323Codec * AST_G729Capability::CreateCodec(H323Codec::Direction direction) const
+{
+ return NULL;
+}
+
+/*
+ * Capability: G.729A
+ */
+AST_G729ACapability::AST_G729ACapability(int rx_frames)
+ : H323AudioCapability(rx_frames, 6)
+{
+}
+
+PObject * AST_G729ACapability::Clone() const
+{
+ return new AST_G729ACapability(*this);
+}
+
+unsigned AST_G729ACapability::GetSubType() const
+{
+ return H245_AudioCapability::e_g729AnnexA;
+}
+
+PString AST_G729ACapability::GetFormatName() const
+{
+ return OPAL_G729A;
+}
+
+H323Codec * AST_G729ACapability::CreateCodec(H323Codec::Direction direction) const
+{
+ return NULL;
+}
+
+/*
+ * Capability: GSM full rate
+ */
+AST_GSM0610Capability::AST_GSM0610Capability(int rx_frames, int comfortNoise_, int scrambled_)
+ : H323AudioCapability(rx_frames, 2)
+{
+ comfortNoise = comfortNoise_;
+ scrambled = scrambled_;
+}
+
+PObject * AST_GSM0610Capability::Clone() const
+{
+ return new AST_GSM0610Capability(*this);
+}
+
+unsigned AST_GSM0610Capability::GetSubType() const
+{
+ return H245_AudioCapability::e_gsmFullRate;
+}
+
+BOOL AST_GSM0610Capability::OnSendingPDU(H245_AudioCapability & cap,
+ unsigned packetSize) const
+{
+ cap.SetTag(H245_AudioCapability::e_gsmFullRate);
+ H245_GSMAudioCapability & gsm = cap;
+ gsm.m_audioUnitSize = packetSize * 33;
+ gsm.m_comfortNoise = comfortNoise;
+ gsm.m_scrambled = scrambled;
+ return TRUE;
+}
+
+BOOL AST_GSM0610Capability::OnReceivedPDU(const H245_AudioCapability & cap,
+ unsigned & packetSize)
+{
+ if (cap.GetTag() != H245_AudioCapability::e_gsmFullRate)
+ return FALSE;
+ const H245_GSMAudioCapability & gsm = cap;
+ packetSize = (gsm.m_audioUnitSize + 32) / 33;
+ comfortNoise = gsm.m_comfortNoise;
+ scrambled = gsm.m_scrambled;
+
+ return TRUE;
+}
+
+PString AST_GSM0610Capability::GetFormatName() const
+{
+ return OPAL_GSM0610;
+}
+
+H323Codec * AST_GSM0610Capability::CreateCodec(H323Codec::Direction direction) const
+{
+ return NULL;
+}
+
+/*
+ * Capability: G.726 32 Kbps
+ */
+AST_CiscoG726Capability::AST_CiscoG726Capability(int rx_frames)
+ : H323NonStandardAudioCapability(rx_frames, 240,
+ 181, 0, 18,
+ (const BYTE *)"G726r32", 0)
+{
+}
+
+PObject *AST_CiscoG726Capability::Clone() const
+{
+ return new AST_CiscoG726Capability(*this);
+}
+
+H323Codec *AST_CiscoG726Capability::CreateCodec(H323Codec::Direction direction) const
+{
+ return NULL;
+}
+
+PString AST_CiscoG726Capability::GetFormatName() const
+{
+ return PString(CISCO_G726r32);
+}
+
+/*
+ * Capability: Cisco RTP DTMF Relay
+ */
+AST_CiscoDtmfCapability::AST_CiscoDtmfCapability()
+ : H323NonStandardDataCapability(0, 181, 0, 18, (const BYTE *)"RtpDtmfRelay", 0)
+{
+ rtpPayloadType = (RTP_DataFrame::PayloadTypes)121;
+}
+
+PObject *AST_CiscoDtmfCapability::Clone() const
+{
+ return new AST_CiscoDtmfCapability(*this);
+}
+
+H323Codec *AST_CiscoDtmfCapability::CreateCodec(H323Codec::Direction direction) const
+{
+ return NULL;
+}
+
+PString AST_CiscoDtmfCapability::GetFormatName() const
+{
+ return PString(CISCO_DTMF_RELAY);
+}
diff --git a/trunk/channels/h323/caps_h323.h b/trunk/channels/h323/caps_h323.h
new file mode 100644
index 000000000..8058054db
--- /dev/null
+++ b/trunk/channels/h323/caps_h323.h
@@ -0,0 +1,172 @@
+#ifndef __AST_H323CAPS_H
+#define __AST_H323CAPS_H
+
+/**This class describes the G.711 codec capability.
+ */
+class AST_G711Capability : public H323AudioCapability
+{
+ PCLASSINFO(AST_G711Capability, H323AudioCapability);
+
+public:
+ AST_G711Capability(int rx_frames = 125, H323_G711Capability::Mode _mode = H323_G711Capability::muLaw, H323_G711Capability::Speed _speed = H323_G711Capability::At64k);
+ virtual PObject *Clone() const;
+ virtual H323Codec * CreateCodec(H323Codec::Direction direction) const;
+ virtual unsigned GetSubType() const;
+ virtual PString GetFormatName() const;
+
+protected:
+ H323_G711Capability::Mode mode;
+ H323_G711Capability::Speed speed;
+};
+
+/**This class describes the G.723.1 codec capability.
+ */
+class AST_G7231Capability : public H323AudioCapability
+{
+ PCLASSINFO(AST_G7231Capability, H323AudioCapability);
+
+public:
+ AST_G7231Capability(int rx_frames = 7, BOOL annexA = TRUE);
+ Comparison Compare(const PObject & obj) const;
+ virtual PObject * Clone() const;
+ virtual H323Codec * CreateCodec(H323Codec::Direction direction) const;
+ virtual unsigned GetSubType() const;
+ virtual PString GetFormatName() const;
+ virtual BOOL OnSendingPDU(H245_AudioCapability & pdu, unsigned packetSize) const;
+ virtual BOOL OnReceivedPDU(const H245_AudioCapability & pdu, unsigned & packetSize);
+
+protected:
+ BOOL annexA;
+};
+
+/**This class describes the (fake) G729 codec capability.
+ */
+class AST_G729Capability : public H323AudioCapability
+{
+ PCLASSINFO(AST_G729Capability, H323AudioCapability);
+
+public:
+ AST_G729Capability(int rx_frames = 24);
+ /* Create a copy of the object. */
+ virtual PObject * Clone() const;
+
+ /* Create the codec instance, allocating resources as required. */
+ virtual H323Codec * CreateCodec(H323Codec::Direction direction) const;
+
+ /* Get the sub-type of the capability. This is a code dependent on the
+ main type of the capability.
+
+ This returns one of the four possible combinations of mode and speed
+ using the enum values of the protocol ASN H245_AudioCapability class. */
+ virtual unsigned GetSubType() const;
+
+ /* Get the name of the media data format this class represents. */
+ virtual PString GetFormatName() const;
+};
+
+/* This class describes the VoiceAge G729A codec capability. */
+class AST_G729ACapability : public H323AudioCapability
+{
+ PCLASSINFO(AST_G729ACapability, H323AudioCapability);
+
+public:
+ /* Create a new G.729A capability. */
+ AST_G729ACapability(int rx_frames = 24);
+
+ /* Create a copy of the object. */
+ virtual PObject * Clone() const;
+ /* Create the codec instance, allocating resources as required. */
+ virtual H323Codec * CreateCodec(H323Codec::Direction direction) const;
+
+ /* Get the sub-type of the capability. This is a code dependent on the
+ main type of the capability.
+
+ This returns one of the four possible combinations of mode and speed
+ using the enum values of the protocol ASN H245_AudioCapability class. */
+ virtual unsigned GetSubType() const;
+
+ /* Get the name of the media data format this class represents. */
+ virtual PString GetFormatName() const;
+};
+
+/* This class describes the GSM-06.10 codec capability. */
+class AST_GSM0610Capability : public H323AudioCapability
+{
+ PCLASSINFO(AST_GSM0610Capability, H323AudioCapability);
+
+public:
+ /* Create a new GSM capability. */
+ AST_GSM0610Capability(int rx_frames = 24, int comfortNoise = 0, int scrambled = 0);
+
+ /* Create a copy of the object. */
+ virtual PObject * Clone() const;
+
+ /* Create the codec instance, allocating resources as required. */
+ virtual H323Codec * CreateCodec(H323Codec::Direction direction) const;
+
+ /* Get the sub-type of the capability. This is a code dependent on the
+ main type of the capability.
+
+ This returns one of the four possible combinations of mode and speed
+ using the enum values of the protocol ASN H245_AudioCapability class. */
+ virtual unsigned GetSubType() const;
+
+ /* Get the name of the media data format this class represents. */
+ virtual PString GetFormatName() const;
+
+ BOOL OnSendingPDU(H245_AudioCapability & pdu, unsigned packetSize) const;
+ BOOL OnReceivedPDU(const H245_AudioCapability & pdu, unsigned & packetSize);
+
+protected:
+ int comfortNoise;
+ int scrambled;
+};
+
+#define CISCO_G726r32 "G726r32"
+
+class AST_CiscoG726Capability : public H323NonStandardAudioCapability {
+ PCLASSINFO(AST_CiscoG726Capability, H323NonStandardAudioCapability);
+
+public:
+ /* Create a new Cisco G.726 capability */
+ AST_CiscoG726Capability(int rx_frames = 80);
+
+ /* Create a copy of the object. */
+ virtual PObject * Clone() const;
+
+ /* Create the codec instance, allocating resources as required. */
+ virtual H323Codec * CreateCodec(H323Codec::Direction direction) const;
+
+ /* Get the name of the media data format this class represents. */
+ virtual PString GetFormatName() const;
+};
+
+#define CISCO_DTMF_RELAY "UserInput/RtpDtmfRelay"
+
+class AST_CiscoDtmfCapability : public H323NonStandardDataCapability
+{
+ PCLASSINFO(AST_CiscoDtmfCapability, H323NonStandardDataCapability);
+
+public:
+ /* Create a new Cisco RTP DTMF Relay capability */
+ AST_CiscoDtmfCapability();
+
+ /* Create a copy of the object. */
+ virtual PObject *Clone() const;
+
+ /* Create the codec instance, allocating resources as required. */
+ virtual H323Codec * CreateCodec(H323Codec::Direction direction) const;
+
+ /* Get the name of the media data format this class represents. */
+ virtual PString GetFormatName() const;
+
+ virtual H323Channel *CreateChannel(H323Connection &,
+ H323Channel::Directions,
+ unsigned,
+ const H245_H2250LogicalChannelParameters *) const
+ {
+ return NULL;
+ }
+};
+
+#endif /* __AST_H323CAPS_H */
diff --git a/trunk/channels/h323/chan_h323.h b/trunk/channels/h323/chan_h323.h
new file mode 100644
index 000000000..62b670fda
--- /dev/null
+++ b/trunk/channels/h323/chan_h323.h
@@ -0,0 +1,269 @@
+/*
+ * chan_h323.h
+ *
+ * OpenH323 Channel Driver for ASTERISK PBX.
+ * By Jeremy McNamara
+ * For The NuFone Network
+ *
+ * This code has been derived from code created by
+ * Michael Manousos and Mark Spencer
+ *
+ * This file is part of the chan_h323 driver for Asterisk
+ *
+ * chan_h323 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.
+ *
+ * chan_h323 is distributed 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Version Info: $Id$
+ */
+
+#include <arpa/inet.h>
+
+/*
+ * Enable support for sending/reception of tunnelled Q.SIG messages and
+ * some sort of IEs (especially RedirectingNumber) which Cisco CallManager
+ * isn't like to pass in standard Q.931 message.
+ *
+ */
+#define TUNNELLING
+
+#define H323_TUNNEL_CISCO (1 << 0)
+#define H323_TUNNEL_QSIG (1 << 1)
+
+#define H323_HOLD_NOTIFY (1 << 0)
+#define H323_HOLD_Q931ONLY (1 << 1)
+#define H323_HOLD_H450 (1 << 2)
+
+/** call_option struct holds various bits
+ * of information for each call */
+typedef struct call_options {
+ char cid_num[80];
+ char cid_name[80];
+ char cid_rdnis[80];
+ int redirect_reason;
+ int presentation;
+ int type_of_number;
+ int transfer_capability;
+ int fastStart;
+ int h245Tunneling;
+ int silenceSuppression;
+ int progress_setup;
+ int progress_alert;
+ int progress_audio;
+ int dtmfcodec[2];
+ int dtmfmode;
+ int capability;
+ int bridge;
+ int nat;
+ int tunnelOptions;
+ int holdHandling;
+ struct ast_codec_pref prefs;
+} call_options_t;
+
+/* structure to hold the valid asterisk users */
+struct oh323_user {
+ ASTOBJ_COMPONENTS(struct oh323_user);
+// char name[80];
+ char context[80];
+ char secret[80];
+ char accountcode[AST_MAX_ACCOUNT_CODE];
+ int amaflags;
+ int host;
+ struct sockaddr_in addr;
+ struct ast_ha *ha;
+ call_options_t options;
+};
+
+/* structure to hold the valid asterisk peers
+ All peers are registered to a GK if there is one */
+struct oh323_peer {
+ ASTOBJ_COMPONENTS(struct oh323_peer);
+ char mailbox[80];
+ int delme;
+ struct sockaddr_in addr;
+ struct ast_ha *ha;
+ call_options_t options;
+};
+
+/* structure to hold the H.323 aliases which get registered to
+ the H.323 endpoint and gatekeeper */
+struct oh323_alias {
+ ASTOBJ_COMPONENTS(struct oh323_alias);
+ char e164[20]; /* tells a GK to route this E.164 to this alias */
+ char prefix[500]; /* tells a GK this alias supports these prefixes */
+ char secret[20]; /* the H.235 password to send to the GK for authentication */
+ char context[80];
+};
+
+/** call_details struct call detail records
+ to asterisk for processing and used for matching up
+ asterisk channels to acutal h.323 connections */
+typedef struct call_details {
+ unsigned int call_reference;
+ char *call_token;
+ char *call_source_aliases;
+ char *call_dest_alias;
+ char *call_source_name;
+ char *call_source_e164;
+ char *call_dest_e164;
+ char *redirect_number;
+ int redirect_reason;
+ int presentation;
+ int type_of_number;
+ int transfer_capability;
+ char *sourceIp;
+} call_details_t;
+
+typedef struct rtp_info {
+ char addr[32];
+ unsigned int port;
+} rtp_info_t;
+
+/* This is a callback prototype function, called pass
+ DTMF down the RTP. */
+typedef int (*receive_digit_cb)(unsigned, char, const char *, int);
+extern receive_digit_cb on_receive_digit;
+
+/* This is a callback prototype function, called to collect
+ the external RTP port from Asterisk. */
+typedef rtp_info_t *(*on_rtp_cb)(unsigned, const char *);
+extern on_rtp_cb on_external_rtp_create;
+
+/* This is a callback prototype function, called to send
+ the remote IP and RTP port from H.323 to Asterisk */
+typedef void (*start_rtp_cb)(unsigned int, const char *, int, const char *, int);
+extern start_rtp_cb on_start_rtp_channel;
+
+/* This is a callback that happens when call progress is
+ * made, and handles inband progress */
+typedef int (*progress_cb)(unsigned, const char *, int);
+extern progress_cb on_progress;
+
+/* This is a callback prototype function, called upon
+ an incoming call happens. */
+typedef call_options_t *(*setup_incoming_cb)(call_details_t *);
+extern setup_incoming_cb on_incoming_call;
+
+/* This is a callback prototype function, called upon
+ an outbound call. */
+typedef int (*setup_outbound_cb)(call_details_t *);
+extern setup_outbound_cb on_outgoing_call;
+
+/* This is a callback prototype function, called when
+ OnAlerting is invoked */
+typedef void (*chan_ringing_cb)(unsigned, const char *);
+extern chan_ringing_cb on_chan_ringing;
+
+/* This is a callback protoype function, called when
+ OnConnectionEstablished is inovked */
+typedef void (*con_established_cb)(unsigned, const char *);
+extern con_established_cb on_connection_established;
+
+/* This is a callback prototype function, called when
+ OnConnectionCleared callback is invoked */
+typedef void (*clear_con_cb)(unsigned, const char *);
+extern clear_con_cb on_connection_cleared;
+
+/* This is a callback prototype function, called when
+ an H.323 call is answered */
+typedef int (*answer_call_cb)(unsigned, const char *);
+extern answer_call_cb on_answer_call;
+
+/* This is a callback prototype function, called when
+ we know which RTP payload type RFC2833 will be
+ transmitted */
+typedef void (*rfc2833_cb)(unsigned, const char *, int, int);
+extern rfc2833_cb on_set_rfc2833_payload;
+
+typedef void (*hangup_cb)(unsigned, const char *, int);
+extern hangup_cb on_hangup;
+
+typedef void (*setcapabilities_cb)(unsigned, const char *);
+extern setcapabilities_cb on_setcapabilities;
+
+typedef void (*setpeercapabilities_cb)(unsigned, const char *, int, struct ast_codec_pref *);
+extern setpeercapabilities_cb on_setpeercapabilities;
+
+typedef void (*onhold_cb)(unsigned, const char *, int);
+extern onhold_cb on_hold;
+
+/* debug flag */
+extern int h323debug;
+
+#define H323_DTMF_RFC2833 (1 << 0)
+#define H323_DTMF_CISCO (1 << 1)
+#define H323_DTMF_SIGNAL (1 << 2)
+#define H323_DTMF_INBAND (1 << 3)
+
+#define H323_DTMF_RFC2833_PT 101
+#define H323_DTMF_CISCO_PT 121
+
+#ifndef BOOL
+#define BOOL int
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ void h323_gk_urq(void);
+ void h323_end_point_create(void);
+ void h323_end_process(void);
+ int h323_end_point_exist(void);
+
+ void h323_debug(int, unsigned);
+
+ /* callback function handler*/
+ void h323_callback_register(setup_incoming_cb,
+ setup_outbound_cb,
+ on_rtp_cb,
+ start_rtp_cb,
+ clear_con_cb,
+ chan_ringing_cb,
+ con_established_cb,
+ receive_digit_cb,
+ answer_call_cb,
+ progress_cb,
+ rfc2833_cb,
+ hangup_cb,
+ setcapabilities_cb,
+ setpeercapabilities_cb,
+ onhold_cb);
+ int h323_set_capabilities(const char *, int, int, struct ast_codec_pref *, int);
+ int h323_set_alias(struct oh323_alias *);
+ int h323_set_gk(int, char *, char *);
+ void h323_set_id(char *);
+ void h323_show_tokens(void);
+
+ /* H323 listener related funcions */
+ int h323_start_listener(int, struct sockaddr_in);
+
+ void h323_native_bridge(const char *, const char *, char *);
+
+ /* Send a DTMF tone to remote endpoint */
+ void h323_send_tone(const char *call_token, char tone);
+
+ /* H323 create and destroy sessions */
+ int h323_make_call(char *dest, call_details_t *cd, call_options_t *);
+ int h323_clear_call(const char *, int cause);
+
+ /* H.323 alerting and progress */
+ int h323_send_alerting(const char *token);
+ int h323_send_progress(const char *token);
+ int h323_answering_call(const char *token, int);
+ int h323_soft_hangup(const char *data);
+ int h323_show_codec(int fd, int argc, char *argv[]);
+ int h323_hold_call(const char *token, int);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/trunk/channels/h323/cisco-h225.asn b/trunk/channels/h323/cisco-h225.asn
new file mode 100644
index 000000000..1372e67d5
--- /dev/null
+++ b/trunk/channels/h323/cisco-h225.asn
@@ -0,0 +1,74 @@
+CISCO-H225-MESSAGES DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+H323_UU_NonStdInfo ::= SEQUENCE
+{
+ version INTEGER OPTIONAL,
+ protoParam ProtoParam OPTIONAL,
+ commonParam CommonParam OPTIONAL,
+ ...,
+ dummy1 OCTET STRING OPTIONAL,
+ progIndParam ProgIndParam OPTIONAL,
+ callMgrParam CallMgrParam OPTIONAL,
+ callSignallingParam CallSignallingParam OPTIONAL,
+ dummy2 OCTET STRING OPTIONAL,
+ callPreserveParam CallPreserveParam OPTIONAL
+}
+
+CommonParam ::= SEQUENCE
+{
+ redirectIEinfo RedirectIEinfo,
+ ...
+}
+
+RedirectIEinfo ::= SEQUENCE
+{
+ redirectIE OCTET STRING,
+ ...
+}
+
+ProgIndParam ::= SEQUENCE
+{
+ progIndIEinfo ProgIndIEinfo,
+ ...
+}
+
+ProgIndIEinfo ::= SEQUENCE
+{
+ progIndIE OCTET STRING,
+ ...
+}
+
+ProtoParam ::= SEQUENCE
+{
+ qsigNonStdInfo QsigNonStdInfo,
+ ...
+}
+
+QsigNonStdInfo ::= SEQUENCE
+{
+ iei INTEGER,
+ rawMesg OCTET STRING,
+ ...
+}
+
+CallMgrParam ::= SEQUENCE
+{
+ interclusterVersion INTEGER,
+ enterpriseID OCTET STRING,
+ ...
+}
+
+CallPreserveParam ::= SEQUENCE
+{
+ callPreserveIE BOOLEAN,
+ ...
+}
+
+CallSignallingParam ::= SEQUENCE
+{
+ connectedNumber OCTET STRING (1..127) OPTIONAL,
+ ...
+}
+
+END
diff --git a/trunk/channels/h323/cisco-h225.cxx b/trunk/channels/h323/cisco-h225.cxx
new file mode 100644
index 000000000..37adc4e87
--- /dev/null
+++ b/trunk/channels/h323/cisco-h225.cxx
@@ -0,0 +1,853 @@
+//
+// cisco-h225.cxx
+//
+// Code automatically generated by asnparse.
+//
+
+#ifdef P_USE_PRAGMA
+#pragma implementation "cisco-h225.h"
+#endif
+
+#include <ptlib.h>
+#include "cisco-h225.h"
+
+#define new PNEW
+
+
+#if ! H323_DISABLE_CISCO_H225
+
+//
+// RedirectIEinfo
+//
+
+CISCO_H225_RedirectIEinfo::CISCO_H225_RedirectIEinfo(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_RedirectIEinfo::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+13) << "redirectIE = " << setprecision(indent) << m_redirectIE << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_RedirectIEinfo::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_RedirectIEinfo), PInvalidCast);
+#endif
+ const CISCO_H225_RedirectIEinfo & other = (const CISCO_H225_RedirectIEinfo &)obj;
+
+ Comparison result;
+
+ if ((result = m_redirectIE.Compare(other.m_redirectIE)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_RedirectIEinfo::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_redirectIE.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_RedirectIEinfo::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_redirectIE.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_RedirectIEinfo::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_redirectIE.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_RedirectIEinfo::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_RedirectIEinfo::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_RedirectIEinfo(*this);
+}
+
+
+//
+// ProgIndIEinfo
+//
+
+CISCO_H225_ProgIndIEinfo::CISCO_H225_ProgIndIEinfo(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_ProgIndIEinfo::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+12) << "progIndIE = " << setprecision(indent) << m_progIndIE << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_ProgIndIEinfo::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_ProgIndIEinfo), PInvalidCast);
+#endif
+ const CISCO_H225_ProgIndIEinfo & other = (const CISCO_H225_ProgIndIEinfo &)obj;
+
+ Comparison result;
+
+ if ((result = m_progIndIE.Compare(other.m_progIndIE)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_ProgIndIEinfo::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_progIndIE.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_ProgIndIEinfo::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_progIndIE.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_ProgIndIEinfo::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_progIndIE.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_ProgIndIEinfo::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_ProgIndIEinfo::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_ProgIndIEinfo(*this);
+}
+
+
+//
+// QsigNonStdInfo
+//
+
+CISCO_H225_QsigNonStdInfo::CISCO_H225_QsigNonStdInfo(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_QsigNonStdInfo::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+6) << "iei = " << setprecision(indent) << m_iei << '\n';
+ strm << setw(indent+10) << "rawMesg = " << setprecision(indent) << m_rawMesg << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_QsigNonStdInfo::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_QsigNonStdInfo), PInvalidCast);
+#endif
+ const CISCO_H225_QsigNonStdInfo & other = (const CISCO_H225_QsigNonStdInfo &)obj;
+
+ Comparison result;
+
+ if ((result = m_iei.Compare(other.m_iei)) != EqualTo)
+ return result;
+ if ((result = m_rawMesg.Compare(other.m_rawMesg)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_QsigNonStdInfo::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_iei.GetObjectLength();
+ length += m_rawMesg.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_QsigNonStdInfo::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_iei.Decode(strm))
+ return FALSE;
+ if (!m_rawMesg.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_QsigNonStdInfo::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_iei.Encode(strm);
+ m_rawMesg.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_QsigNonStdInfo::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_QsigNonStdInfo::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_QsigNonStdInfo(*this);
+}
+
+
+//
+// CallMgrParam
+//
+
+CISCO_H225_CallMgrParam::CISCO_H225_CallMgrParam(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_CallMgrParam::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+22) << "interclusterVersion = " << setprecision(indent) << m_interclusterVersion << '\n';
+ strm << setw(indent+15) << "enterpriseID = " << setprecision(indent) << m_enterpriseID << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_CallMgrParam::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_CallMgrParam), PInvalidCast);
+#endif
+ const CISCO_H225_CallMgrParam & other = (const CISCO_H225_CallMgrParam &)obj;
+
+ Comparison result;
+
+ if ((result = m_interclusterVersion.Compare(other.m_interclusterVersion)) != EqualTo)
+ return result;
+ if ((result = m_enterpriseID.Compare(other.m_enterpriseID)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_CallMgrParam::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_interclusterVersion.GetObjectLength();
+ length += m_enterpriseID.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_CallMgrParam::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_interclusterVersion.Decode(strm))
+ return FALSE;
+ if (!m_enterpriseID.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_CallMgrParam::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_interclusterVersion.Encode(strm);
+ m_enterpriseID.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_CallMgrParam::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_CallMgrParam::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_CallMgrParam(*this);
+}
+
+
+//
+// CallPreserveParam
+//
+
+CISCO_H225_CallPreserveParam::CISCO_H225_CallPreserveParam(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_CallPreserveParam::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+17) << "callPreserveIE = " << setprecision(indent) << m_callPreserveIE << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_CallPreserveParam::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_CallPreserveParam), PInvalidCast);
+#endif
+ const CISCO_H225_CallPreserveParam & other = (const CISCO_H225_CallPreserveParam &)obj;
+
+ Comparison result;
+
+ if ((result = m_callPreserveIE.Compare(other.m_callPreserveIE)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_CallPreserveParam::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_callPreserveIE.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_CallPreserveParam::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_callPreserveIE.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_CallPreserveParam::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_callPreserveIE.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_CallPreserveParam::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_CallPreserveParam::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_CallPreserveParam(*this);
+}
+
+
+//
+// CallSignallingParam
+//
+
+CISCO_H225_CallSignallingParam::CISCO_H225_CallSignallingParam(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 1, TRUE, 0)
+{
+ m_connectedNumber.SetConstraints(PASN_Object::FixedConstraint, 1, 127);
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_CallSignallingParam::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ if (HasOptionalField(e_connectedNumber))
+ strm << setw(indent+18) << "connectedNumber = " << setprecision(indent) << m_connectedNumber << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_CallSignallingParam::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_CallSignallingParam), PInvalidCast);
+#endif
+ const CISCO_H225_CallSignallingParam & other = (const CISCO_H225_CallSignallingParam &)obj;
+
+ Comparison result;
+
+ if ((result = m_connectedNumber.Compare(other.m_connectedNumber)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_CallSignallingParam::GetDataLength() const
+{
+ PINDEX length = 0;
+ if (HasOptionalField(e_connectedNumber))
+ length += m_connectedNumber.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_CallSignallingParam::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (HasOptionalField(e_connectedNumber) && !m_connectedNumber.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_CallSignallingParam::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ if (HasOptionalField(e_connectedNumber))
+ m_connectedNumber.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_CallSignallingParam::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_CallSignallingParam::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_CallSignallingParam(*this);
+}
+
+
+//
+// CommonParam
+//
+
+CISCO_H225_CommonParam::CISCO_H225_CommonParam(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_CommonParam::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+17) << "redirectIEinfo = " << setprecision(indent) << m_redirectIEinfo << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_CommonParam::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_CommonParam), PInvalidCast);
+#endif
+ const CISCO_H225_CommonParam & other = (const CISCO_H225_CommonParam &)obj;
+
+ Comparison result;
+
+ if ((result = m_redirectIEinfo.Compare(other.m_redirectIEinfo)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_CommonParam::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_redirectIEinfo.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_CommonParam::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_redirectIEinfo.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_CommonParam::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_redirectIEinfo.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_CommonParam::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_CommonParam::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_CommonParam(*this);
+}
+
+
+//
+// ProgIndParam
+//
+
+CISCO_H225_ProgIndParam::CISCO_H225_ProgIndParam(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_ProgIndParam::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+16) << "progIndIEinfo = " << setprecision(indent) << m_progIndIEinfo << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_ProgIndParam::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_ProgIndParam), PInvalidCast);
+#endif
+ const CISCO_H225_ProgIndParam & other = (const CISCO_H225_ProgIndParam &)obj;
+
+ Comparison result;
+
+ if ((result = m_progIndIEinfo.Compare(other.m_progIndIEinfo)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_ProgIndParam::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_progIndIEinfo.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_ProgIndParam::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_progIndIEinfo.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_ProgIndParam::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_progIndIEinfo.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_ProgIndParam::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_ProgIndParam::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_ProgIndParam(*this);
+}
+
+
+//
+// ProtoParam
+//
+
+CISCO_H225_ProtoParam::CISCO_H225_ProtoParam(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 0, TRUE, 0)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_ProtoParam::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ strm << setw(indent+17) << "qsigNonStdInfo = " << setprecision(indent) << m_qsigNonStdInfo << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_ProtoParam::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_ProtoParam), PInvalidCast);
+#endif
+ const CISCO_H225_ProtoParam & other = (const CISCO_H225_ProtoParam &)obj;
+
+ Comparison result;
+
+ if ((result = m_qsigNonStdInfo.Compare(other.m_qsigNonStdInfo)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_ProtoParam::GetDataLength() const
+{
+ PINDEX length = 0;
+ length += m_qsigNonStdInfo.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_ProtoParam::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (!m_qsigNonStdInfo.Decode(strm))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_ProtoParam::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ m_qsigNonStdInfo.Encode(strm);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_ProtoParam::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_ProtoParam::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_ProtoParam(*this);
+}
+
+
+//
+// H323_UU_NonStdInfo
+//
+
+CISCO_H225_H323_UU_NonStdInfo::CISCO_H225_H323_UU_NonStdInfo(unsigned tag, PASN_Object::TagClass tagClass)
+ : PASN_Sequence(tag, tagClass, 3, TRUE, 6)
+{
+}
+
+
+#ifndef PASN_NOPRINTON
+void CISCO_H225_H323_UU_NonStdInfo::PrintOn(ostream & strm) const
+{
+ int indent = strm.precision() + 2;
+ strm << "{\n";
+ if (HasOptionalField(e_version))
+ strm << setw(indent+10) << "version = " << setprecision(indent) << m_version << '\n';
+ if (HasOptionalField(e_protoParam))
+ strm << setw(indent+13) << "protoParam = " << setprecision(indent) << m_protoParam << '\n';
+ if (HasOptionalField(e_commonParam))
+ strm << setw(indent+14) << "commonParam = " << setprecision(indent) << m_commonParam << '\n';
+ if (HasOptionalField(e_dummy1))
+ strm << setw(indent+9) << "dummy1 = " << setprecision(indent) << m_dummy1 << '\n';
+ if (HasOptionalField(e_progIndParam))
+ strm << setw(indent+15) << "progIndParam = " << setprecision(indent) << m_progIndParam << '\n';
+ if (HasOptionalField(e_callMgrParam))
+ strm << setw(indent+15) << "callMgrParam = " << setprecision(indent) << m_callMgrParam << '\n';
+ if (HasOptionalField(e_callSignallingParam))
+ strm << setw(indent+22) << "callSignallingParam = " << setprecision(indent) << m_callSignallingParam << '\n';
+ if (HasOptionalField(e_dummy2))
+ strm << setw(indent+9) << "dummy2 = " << setprecision(indent) << m_dummy2 << '\n';
+ if (HasOptionalField(e_callPreserveParam))
+ strm << setw(indent+20) << "callPreserveParam = " << setprecision(indent) << m_callPreserveParam << '\n';
+ strm << setw(indent-1) << setprecision(indent-2) << "}";
+}
+#endif
+
+
+PObject::Comparison CISCO_H225_H323_UU_NonStdInfo::Compare(const PObject & obj) const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(PIsDescendant(&obj, CISCO_H225_H323_UU_NonStdInfo), PInvalidCast);
+#endif
+ const CISCO_H225_H323_UU_NonStdInfo & other = (const CISCO_H225_H323_UU_NonStdInfo &)obj;
+
+ Comparison result;
+
+ if ((result = m_version.Compare(other.m_version)) != EqualTo)
+ return result;
+ if ((result = m_protoParam.Compare(other.m_protoParam)) != EqualTo)
+ return result;
+ if ((result = m_commonParam.Compare(other.m_commonParam)) != EqualTo)
+ return result;
+
+ return PASN_Sequence::Compare(other);
+}
+
+
+PINDEX CISCO_H225_H323_UU_NonStdInfo::GetDataLength() const
+{
+ PINDEX length = 0;
+ if (HasOptionalField(e_version))
+ length += m_version.GetObjectLength();
+ if (HasOptionalField(e_protoParam))
+ length += m_protoParam.GetObjectLength();
+ if (HasOptionalField(e_commonParam))
+ length += m_commonParam.GetObjectLength();
+ return length;
+}
+
+
+BOOL CISCO_H225_H323_UU_NonStdInfo::Decode(PASN_Stream & strm)
+{
+ if (!PreambleDecode(strm))
+ return FALSE;
+
+ if (HasOptionalField(e_version) && !m_version.Decode(strm))
+ return FALSE;
+ if (HasOptionalField(e_protoParam) && !m_protoParam.Decode(strm))
+ return FALSE;
+ if (HasOptionalField(e_commonParam) && !m_commonParam.Decode(strm))
+ return FALSE;
+ if (!KnownExtensionDecode(strm, e_dummy1, m_dummy1))
+ return FALSE;
+ if (!KnownExtensionDecode(strm, e_progIndParam, m_progIndParam))
+ return FALSE;
+ if (!KnownExtensionDecode(strm, e_callMgrParam, m_callMgrParam))
+ return FALSE;
+ if (!KnownExtensionDecode(strm, e_callSignallingParam, m_callSignallingParam))
+ return FALSE;
+ if (!KnownExtensionDecode(strm, e_dummy2, m_dummy2))
+ return FALSE;
+ if (!KnownExtensionDecode(strm, e_callPreserveParam, m_callPreserveParam))
+ return FALSE;
+
+ return UnknownExtensionsDecode(strm);
+}
+
+
+void CISCO_H225_H323_UU_NonStdInfo::Encode(PASN_Stream & strm) const
+{
+ PreambleEncode(strm);
+
+ if (HasOptionalField(e_version))
+ m_version.Encode(strm);
+ if (HasOptionalField(e_protoParam))
+ m_protoParam.Encode(strm);
+ if (HasOptionalField(e_commonParam))
+ m_commonParam.Encode(strm);
+ KnownExtensionEncode(strm, e_dummy1, m_dummy1);
+ KnownExtensionEncode(strm, e_progIndParam, m_progIndParam);
+ KnownExtensionEncode(strm, e_callMgrParam, m_callMgrParam);
+ KnownExtensionEncode(strm, e_callSignallingParam, m_callSignallingParam);
+ KnownExtensionEncode(strm, e_dummy2, m_dummy2);
+ KnownExtensionEncode(strm, e_callPreserveParam, m_callPreserveParam);
+
+ UnknownExtensionsEncode(strm);
+}
+
+
+PObject * CISCO_H225_H323_UU_NonStdInfo::Clone() const
+{
+#ifndef PASN_LEANANDMEAN
+ PAssert(IsClass(CISCO_H225_H323_UU_NonStdInfo::Class()), PInvalidCast);
+#endif
+ return new CISCO_H225_H323_UU_NonStdInfo(*this);
+}
+
+
+#endif // if ! H323_DISABLE_CISCO_H225
+
+
+// End of cisco-h225.cxx
diff --git a/trunk/channels/h323/cisco-h225.h b/trunk/channels/h323/cisco-h225.h
new file mode 100644
index 000000000..7595b4b65
--- /dev/null
+++ b/trunk/channels/h323/cisco-h225.h
@@ -0,0 +1,299 @@
+//
+// cisco-h225.h
+//
+// Code automatically generated by asnparse.
+//
+
+#if ! H323_DISABLE_CISCO_H225
+
+#ifndef __CISCO_H225_H
+#define __CISCO_H225_H
+
+#ifdef P_USE_PRAGMA
+#pragma interface
+#endif
+
+#include <ptclib/asner.h>
+
+//
+// RedirectIEinfo
+//
+
+class CISCO_H225_RedirectIEinfo : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_RedirectIEinfo, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_RedirectIEinfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ PASN_OctetString m_redirectIE;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// ProgIndIEinfo
+//
+
+class CISCO_H225_ProgIndIEinfo : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_ProgIndIEinfo, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_ProgIndIEinfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ PASN_OctetString m_progIndIE;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// QsigNonStdInfo
+//
+
+class CISCO_H225_QsigNonStdInfo : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_QsigNonStdInfo, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_QsigNonStdInfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ PASN_Integer m_iei;
+ PASN_OctetString m_rawMesg;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// CallMgrParam
+//
+
+class CISCO_H225_CallMgrParam : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_CallMgrParam, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_CallMgrParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ PASN_Integer m_interclusterVersion;
+ PASN_OctetString m_enterpriseID;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// CallPreserveParam
+//
+
+class CISCO_H225_CallPreserveParam : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_CallPreserveParam, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_CallPreserveParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ PASN_Boolean m_callPreserveIE;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// CallSignallingParam
+//
+
+class CISCO_H225_CallSignallingParam : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_CallSignallingParam, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_CallSignallingParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ enum OptionalFields {
+ e_connectedNumber
+ };
+
+ PASN_OctetString m_connectedNumber;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// CommonParam
+//
+
+class CISCO_H225_CommonParam : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_CommonParam, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_CommonParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ CISCO_H225_RedirectIEinfo m_redirectIEinfo;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// ProgIndParam
+//
+
+class CISCO_H225_ProgIndParam : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_ProgIndParam, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_ProgIndParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ CISCO_H225_ProgIndIEinfo m_progIndIEinfo;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// ProtoParam
+//
+
+class CISCO_H225_ProtoParam : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_ProtoParam, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_ProtoParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ CISCO_H225_QsigNonStdInfo m_qsigNonStdInfo;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+//
+// H323_UU_NonStdInfo
+//
+
+class CISCO_H225_H323_UU_NonStdInfo : public PASN_Sequence
+{
+#ifndef PASN_LEANANDMEAN
+ PCLASSINFO(CISCO_H225_H323_UU_NonStdInfo, PASN_Sequence);
+#endif
+ public:
+ CISCO_H225_H323_UU_NonStdInfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass);
+
+ enum OptionalFields {
+ e_version,
+ e_protoParam,
+ e_commonParam,
+ e_dummy1,
+ e_progIndParam,
+ e_callMgrParam,
+ e_callSignallingParam,
+ e_dummy2,
+ e_callPreserveParam
+ };
+
+ PASN_Integer m_version;
+ CISCO_H225_ProtoParam m_protoParam;
+ CISCO_H225_CommonParam m_commonParam;
+ PASN_OctetString m_dummy1;
+ CISCO_H225_ProgIndParam m_progIndParam;
+ CISCO_H225_CallMgrParam m_callMgrParam;
+ CISCO_H225_CallSignallingParam m_callSignallingParam;
+ PASN_OctetString m_dummy2;
+ CISCO_H225_CallPreserveParam m_callPreserveParam;
+
+ PINDEX GetDataLength() const;
+ BOOL Decode(PASN_Stream & strm);
+ void Encode(PASN_Stream & strm) const;
+#ifndef PASN_NOPRINTON
+ void PrintOn(ostream & strm) const;
+#endif
+ Comparison Compare(const PObject & obj) const;
+ PObject * Clone() const;
+};
+
+
+#endif // __CISCO_H225_H
+
+#endif // if ! H323_DISABLE_CISCO_H225
+
+
+// End of cisco-h225.h
diff --git a/trunk/channels/h323/compat_h323.cxx b/trunk/channels/h323/compat_h323.cxx
new file mode 100644
index 000000000..eec7361b2
--- /dev/null
+++ b/trunk/channels/h323/compat_h323.cxx
@@ -0,0 +1,138 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+/*
+ * ast_h323.cpp
+ *
+ * OpenH323 Channel Driver for ASTERISK PBX.
+ * By Jeremy McNamara
+ * For The NuFone Network
+ *
+ * chan_h323 has been derived from code created by
+ * Michael Manousos and Mark Spencer
+ *
+ * This file is part of the chan_h323 driver for Asterisk
+ *
+ * chan_h323 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.
+ *
+ * chan_h323 is distributed 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Version Info: $Id$
+ */
+#include <ptlib.h>
+#include <h323.h>
+#include <transports.h>
+
+#include "ast_h323.h"
+
+#if VERSION(OPENH323_MAJOR,OPENH323_MINOR,OPENH323_BUILD) < VERSION(1,17,3)
+MyH323TransportTCP::MyH323TransportTCP(
+ H323EndPoint & endpoint,
+ PIPSocket::Address binding,
+ BOOL listen)
+ : H323TransportTCP(endpoint, binding, listen)
+{
+}
+
+BOOL MyH323TransportTCP::Connect()
+{
+ if (IsListening())
+ return TRUE;
+
+ PTCPSocket * socket = new PTCPSocket(remotePort);
+ Open(socket);
+
+ channelPointerMutex.StartRead();
+
+ socket->SetReadTimeout(10000/*endpoint.GetSignallingChannelConnectTimeout()*/);
+
+ localPort = endpoint.GetNextTCPPort();
+ WORD firstPort = localPort;
+ for (;;) {
+ PTRACE(4, "H323TCP\tConnecting to "
+ << remoteAddress << ':' << remotePort
+ << " (local port=" << localPort << ')');
+ if (socket->Connect(localAddress, localPort, remoteAddress))
+ break;
+
+ int errnum = socket->GetErrorNumber();
+ if (localPort == 0 || (errnum != EADDRINUSE && errnum != EADDRNOTAVAIL)) {
+ PTRACE(1, "H323TCP\tCould not connect to "
+ << remoteAddress << ':' << remotePort
+ << " (local port=" << localPort << ") - "
+ << socket->GetErrorText() << '(' << errnum << ')');
+ channelPointerMutex.EndRead();
+ return SetErrorValues(socket->GetErrorCode(), errnum);
+ }
+
+ localPort = endpoint.GetNextTCPPort();
+ if (localPort == firstPort) {
+ PTRACE(1, "H323TCP\tCould not bind to any port in range " <<
+ endpoint.GetTCPPortBase() << " to " << endpoint.GetTCPPortMax());
+ channelPointerMutex.EndRead();
+ return SetErrorValues(socket->GetErrorCode(), errnum);
+ }
+ }
+
+ socket->SetReadTimeout(PMaxTimeInterval);
+
+ channelPointerMutex.EndRead();
+
+ return OnOpen();
+}
+#endif
+
+BOOL MyH323TransportUDP::DiscoverGatekeeper(H323Gatekeeper &gk, H323RasPDU &pdu, const H323TransportAddress &address)
+{
+ PThread *thd = PThread::Current();
+
+ /* If we run in OpenH323's thread use it instead of creating new one */
+ if (thd)
+ return H323TransportUDP::DiscoverGatekeeper(gk, pdu, address);
+
+ /* Make copy of arguments to pass them into thread */
+ discoverGatekeeper = &gk;
+ discoverPDU = &pdu;
+ discoverAddress = &address;
+
+ /* Assume discovery thread isn't finished */
+ discoverReady = FALSE;
+
+ /* Create discovery thread */
+ thd = PThread::Create(PCREATE_NOTIFIER(DiscoverMain), 0,
+ PThread::NoAutoDeleteThread,
+ PThread::NormalPriority,
+ "GkDiscovery:%x");
+
+ /* Wait until discovery thread signal us its finished */
+ for(;;) {
+ discoverMutex.Wait();
+ if (discoverReady) /* Thread has been finished */
+ break;
+ discoverMutex.Signal();
+ }
+ discoverMutex.Signal();
+
+ /* Cleanup/delete thread */
+ thd->WaitForTermination();
+ delete thd;
+
+ return discoverResult;
+}
+
+void MyH323TransportUDP::DiscoverMain(PThread &thread, INT arg)
+{
+ PWaitAndSignal m(discoverMutex);
+
+ discoverResult = H323TransportUDP::DiscoverGatekeeper(*discoverGatekeeper, *discoverPDU, *discoverAddress);
+ discoverReady = TRUE;
+}
diff --git a/trunk/channels/h323/compat_h323.h b/trunk/channels/h323/compat_h323.h
new file mode 100644
index 000000000..5437898f4
--- /dev/null
+++ b/trunk/channels/h323/compat_h323.h
@@ -0,0 +1,94 @@
+#ifndef COMPAT_H323_H
+#define COMPAT_H323_H
+
+#if VERSION(OPENH323_MAJOR,OPENH323_MINOR,OPENH323_BUILD) < VERSION(1,17,3)
+/**
+ * Workaround for broken (less than 1.17.3) OpenH323 stack to be able to
+ * make TCP connections from specific address
+ */
+class MyH323TransportTCP : public H323TransportTCP
+{
+ PCLASSINFO(MyH323TransportTCP, H323TransportTCP);
+
+public:
+ MyH323TransportTCP(
+ H323EndPoint & endpoint, ///< H323 End Point object
+ PIPSocket::Address binding = PIPSocket::GetDefaultIpAny(), ///< Local interface to use
+ BOOL listen = FALSE ///< Flag for need to wait for remote to connect
+ );
+ /**Connect to the remote party.
+ */
+ virtual BOOL Connect();
+};
+#else
+#define MyH323TransportTCP H323TransportTCP
+#endif /* <VERSION(1,17,3) */
+
+class MyH323TransportUDP: public H323TransportUDP
+{
+ PCLASSINFO(MyH323TransportUDP, H323TransportUDP);
+
+public:
+ MyH323TransportUDP(H323EndPoint &endpoint,
+ PIPSocket::Address binding = PIPSocket::GetDefaultIpAny(),
+ WORD localPort = 0,
+ WORD remotePort = 0): H323TransportUDP(endpoint, binding, localPort, remotePort)
+ {
+ }
+ virtual BOOL DiscoverGatekeeper(H323Gatekeeper &,
+ H323RasPDU &,
+ const H323TransportAddress &);
+protected:
+ PDECLARE_NOTIFIER(PThread, MyH323TransportUDP, DiscoverMain);
+ H323Gatekeeper *discoverGatekeeper;
+ H323RasPDU *discoverPDU;
+ const H323TransportAddress *discoverAddress;
+ BOOL discoverResult;
+ BOOL discoverReady;
+ PMutex discoverMutex;
+};
+
+template <class _Abstract_T, typename _Key_T = PString>
+class MyPFactory: public PFactory<_Abstract_T, _Key_T>
+{
+public:
+ template <class _Concrete_T> class Worker: public PFactory<_Abstract_T, _Key_T>::WorkerBase
+ {
+ public:
+ Worker(const _Key_T &_key, bool singleton = false)
+ :PFactory<_Abstract_T, _Key_T>::WorkerBase(singleton), key(_key)
+ {
+ PFactory<_Abstract_T, _Key_T>::Register(key, this);
+ }
+ ~Worker()
+ {
+ PFactory<_Abstract_T, _Key_T>::Unregister(key);
+ }
+ protected:
+ virtual _Abstract_T *Create(const _Key_T &) const { return new _Concrete_T; }
+
+ private:
+ PString key;
+ };
+};
+
+#ifdef H323_REGISTER_CAPABILITY
+#undef H323_REGISTER_CAPABILITY
+#endif
+#define H323_REGISTER_CAPABILITY(cls, capName) static MyPFactory<H323Capability>::Worker<cls> cls##Factory(capName, true)
+
+#ifdef OPAL_MEDIA_FORMAT_DECLARE
+#undef OPAL_MEDIA_FORMAT_DECLARE
+#endif
+
+#define OPAL_MEDIA_FORMAT_DECLARE(classname, _fullName, _defaultSessionID, _rtpPayloadType, _needsJitter,_bandwidth, _frameSize, _frameTime, _timeUnits, _timeStamp) \
+class classname : public OpalMediaFormat \
+{ \
+ public: \
+ classname() \
+ : OpalMediaFormat(_fullName, _defaultSessionID, _rtpPayloadType, _needsJitter, _bandwidth, \
+ _frameSize, _frameTime, _timeUnits, _timeStamp){} \
+}; \
+static MyPFactory<OpalMediaFormat>::Worker<classname> classname##Factory(_fullName, true)
+
+#endif /* !defined AST_H323_H */
diff --git a/trunk/channels/h323/noexport.map b/trunk/channels/h323/noexport.map
new file mode 100644
index 000000000..b51f84263
--- /dev/null
+++ b/trunk/channels/h323/noexport.map
@@ -0,0 +1,5 @@
+{
+ global:
+ _Z11PAssertFuncPKc;
+ local: *;
+}; \ No newline at end of file
diff --git a/trunk/channels/iax2-parser.c b/trunk/channels/iax2-parser.c
new file mode 100644
index 000000000..a86bf7428
--- /dev/null
+++ b/trunk/channels/iax2-parser.c
@@ -0,0 +1,1080 @@
+/*
+ * 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 Implementation of Inter-Asterisk eXchange Protocol, v 2
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/utils.h"
+#include "asterisk/unaligned.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/threadstorage.h"
+
+#include "iax2.h"
+#include "iax2-parser.h"
+#include "iax2-provision.h"
+
+static int frames = 0;
+static int iframes = 0;
+static int oframes = 0;
+
+#if !defined(LOW_MEMORY)
+static void frame_cache_cleanup(void *data);
+
+/*! \brief A per-thread cache of iax_frame structures */
+AST_THREADSTORAGE_CUSTOM(frame_cache, NULL, frame_cache_cleanup);
+
+/*! \brief This is just so iax_frames, a list head struct for holding a list of
+ * iax_frame structures, is defined. */
+AST_LIST_HEAD_NOLOCK(iax_frames, iax_frame);
+#endif
+
+static void internaloutput(const char *str)
+{
+ fputs(str, stdout);
+}
+
+static void internalerror(const char *str)
+{
+ fprintf(stderr, "WARNING: %s", str);
+}
+
+static void (*outputf)(const char *str) = internaloutput;
+static void (*errorf)(const char *str) = internalerror;
+
+static void dump_addr(char *output, int maxlen, void *value, int len)
+{
+ struct sockaddr_in sin;
+ if (len == (int)sizeof(sin)) {
+ memcpy(&sin, value, len);
+ snprintf(output, maxlen, "IPV4 %s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ } else {
+ ast_copy_string(output, "Invalid Address", maxlen);
+ }
+}
+
+static void dump_string(char *output, int maxlen, void *value, int len)
+{
+ maxlen--;
+ if (maxlen > len)
+ maxlen = len;
+ strncpy(output, value, maxlen);
+ output[maxlen] = '\0';
+}
+
+static void dump_prefs(char *output, int maxlen, void *value, int len)
+{
+ struct ast_codec_pref pref;
+ int total_len = 0;
+
+ maxlen--;
+ total_len = maxlen;
+
+ if (maxlen > len)
+ maxlen = len;
+
+ strncpy(output, value, maxlen);
+ output[maxlen] = '\0';
+
+ ast_codec_pref_convert(&pref, output, total_len, 0);
+ memset(output,0,total_len);
+ ast_codec_pref_string(&pref, output, total_len);
+}
+
+static void dump_int(char *output, int maxlen, void *value, int len)
+{
+ if (len == (int)sizeof(unsigned int))
+ snprintf(output, maxlen, "%lu", (unsigned long)ntohl(get_unaligned_uint32(value)));
+ else
+ ast_copy_string(output, "Invalid INT", maxlen);
+}
+
+static void dump_short(char *output, int maxlen, void *value, int len)
+{
+ if (len == (int)sizeof(unsigned short))
+ snprintf(output, maxlen, "%d", ntohs(get_unaligned_uint16(value)));
+ else
+ ast_copy_string(output, "Invalid SHORT", maxlen);
+}
+
+static void dump_byte(char *output, int maxlen, void *value, int len)
+{
+ if (len == (int)sizeof(unsigned char))
+ snprintf(output, maxlen, "%d", *((unsigned char *)value));
+ else
+ ast_copy_string(output, "Invalid BYTE", maxlen);
+}
+
+static void dump_datetime(char *output, int maxlen, void *value, int len)
+{
+ struct ast_tm tm;
+ unsigned long val = (unsigned long) ntohl(get_unaligned_uint32(value));
+ if (len == (int)sizeof(unsigned int)) {
+ tm.tm_sec = (val & 0x1f) << 1;
+ tm.tm_min = (val >> 5) & 0x3f;
+ tm.tm_hour = (val >> 11) & 0x1f;
+ tm.tm_mday = (val >> 16) & 0x1f;
+ tm.tm_mon = ((val >> 21) & 0x0f) - 1;
+ tm.tm_year = ((val >> 25) & 0x7f) + 100;
+ ast_strftime(output, maxlen, "%Y-%m-%d %T", &tm);
+ } else
+ ast_copy_string(output, "Invalid DATETIME format!", maxlen);
+}
+
+static void dump_ipaddr(char *output, int maxlen, void *value, int len)
+{
+ struct sockaddr_in sin;
+ if (len == (int)sizeof(unsigned int)) {
+ memcpy(&sin.sin_addr, value, len);
+ snprintf(output, maxlen, "%s", ast_inet_ntoa(sin.sin_addr));
+ } else
+ ast_copy_string(output, "Invalid IPADDR", maxlen);
+}
+
+
+static void dump_prov_flags(char *output, int maxlen, void *value, int len)
+{
+ char buf[256] = "";
+ if (len == (int)sizeof(unsigned int))
+ snprintf(output, maxlen, "%lu (%s)", (unsigned long)ntohl(get_unaligned_uint32(value)),
+ iax_provflags2str(buf, sizeof(buf), ntohl(get_unaligned_uint32(value))));
+ else
+ ast_copy_string(output, "Invalid INT", maxlen);
+}
+
+static void dump_samprate(char *output, int maxlen, void *value, int len)
+{
+ char tmp[256]="";
+ int sr;
+ if (len == (int)sizeof(unsigned short)) {
+ sr = ntohs(*((unsigned short *)value));
+ if (sr & IAX_RATE_8KHZ)
+ strcat(tmp, ",8khz");
+ if (sr & IAX_RATE_11KHZ)
+ strcat(tmp, ",11.025khz");
+ if (sr & IAX_RATE_16KHZ)
+ strcat(tmp, ",16khz");
+ if (sr & IAX_RATE_22KHZ)
+ strcat(tmp, ",22.05khz");
+ if (sr & IAX_RATE_44KHZ)
+ strcat(tmp, ",44.1khz");
+ if (sr & IAX_RATE_48KHZ)
+ strcat(tmp, ",48khz");
+ if (strlen(tmp))
+ ast_copy_string(output, &tmp[1], maxlen);
+ else
+ ast_copy_string(output, "None Specified!\n", maxlen);
+ } else
+ ast_copy_string(output, "Invalid SHORT", maxlen);
+
+}
+
+static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len);
+static void dump_prov(char *output, int maxlen, void *value, int len)
+{
+ dump_prov_ies(output, maxlen, value, len);
+}
+
+static struct iax2_ie {
+ int ie;
+ char *name;
+ void (*dump)(char *output, int maxlen, void *value, int len);
+} ies[] = {
+ { IAX_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
+ { IAX_IE_CALLING_NUMBER, "CALLING NUMBER", dump_string },
+ { IAX_IE_CALLING_ANI, "ANI", dump_string },
+ { IAX_IE_CALLING_NAME, "CALLING NAME", dump_string },
+ { IAX_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
+ { IAX_IE_USERNAME, "USERNAME", dump_string },
+ { IAX_IE_PASSWORD, "PASSWORD", dump_string },
+ { IAX_IE_CAPABILITY, "CAPABILITY", dump_int },
+ { IAX_IE_FORMAT, "FORMAT", dump_int },
+ { IAX_IE_LANGUAGE, "LANGUAGE", dump_string },
+ { IAX_IE_VERSION, "VERSION", dump_short },
+ { IAX_IE_ADSICPE, "ADSICPE", dump_short },
+ { IAX_IE_DNID, "DNID", dump_string },
+ { IAX_IE_AUTHMETHODS, "AUTHMETHODS", dump_short },
+ { IAX_IE_CHALLENGE, "CHALLENGE", dump_string },
+ { IAX_IE_MD5_RESULT, "MD5 RESULT", dump_string },
+ { IAX_IE_RSA_RESULT, "RSA RESULT", dump_string },
+ { IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS", dump_addr },
+ { IAX_IE_REFRESH, "REFRESH", dump_short },
+ { IAX_IE_DPSTATUS, "DIALPLAN STATUS", dump_short },
+ { IAX_IE_CALLNO, "CALL NUMBER", dump_short },
+ { IAX_IE_CAUSE, "CAUSE", dump_string },
+ { IAX_IE_IAX_UNKNOWN, "UNKNOWN IAX CMD", dump_byte },
+ { IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short },
+ { IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" },
+ { IAX_IE_TRANSFERID, "TRANSFER ID", dump_int },
+ { IAX_IE_RDNIS, "REFERRING DNIS", dump_string },
+ { IAX_IE_PROVISIONING, "PROVISIONING", dump_prov },
+ { IAX_IE_AESPROVISIONING, "AES PROVISIONG" },
+ { IAX_IE_DATETIME, "DATE TIME", dump_datetime },
+ { IAX_IE_DEVICETYPE, "DEVICE TYPE", dump_string },
+ { IAX_IE_SERVICEIDENT, "SERVICE IDENT", dump_string },
+ { IAX_IE_FIRMWAREVER, "FIRMWARE VER", dump_short },
+ { IAX_IE_FWBLOCKDESC, "FW BLOCK DESC", dump_int },
+ { IAX_IE_FWBLOCKDATA, "FW BLOCK DATA" },
+ { IAX_IE_PROVVER, "PROVISIONG VER", dump_int },
+ { IAX_IE_CALLINGPRES, "CALLING PRESNTN", dump_byte },
+ { IAX_IE_CALLINGTON, "CALLING TYPEOFNUM", dump_byte },
+ { IAX_IE_CALLINGTNS, "CALLING TRANSITNET", dump_short },
+ { IAX_IE_SAMPLINGRATE, "SAMPLINGRATE", dump_samprate },
+ { IAX_IE_CAUSECODE, "CAUSE CODE", dump_byte },
+ { IAX_IE_ENCRYPTION, "ENCRYPTION", dump_short },
+ { IAX_IE_ENCKEY, "ENCRYPTION KEY" },
+ { IAX_IE_CODEC_PREFS, "CODEC_PREFS", dump_prefs },
+ { IAX_IE_RR_JITTER, "RR_JITTER", dump_int },
+ { IAX_IE_RR_LOSS, "RR_LOSS", dump_int },
+ { IAX_IE_RR_PKTS, "RR_PKTS", dump_int },
+ { IAX_IE_RR_DELAY, "RR_DELAY", dump_short },
+ { IAX_IE_RR_DROPPED, "RR_DROPPED", dump_int },
+ { IAX_IE_RR_OOO, "RR_OUTOFORDER", dump_int },
+ { IAX_IE_VARIABLE, "VARIABLE", dump_string },
+ { IAX_IE_OSPTOKEN, "OSPTOKEN" },
+};
+
+static struct iax2_ie prov_ies[] = {
+ { PROV_IE_USEDHCP, "USEDHCP" },
+ { PROV_IE_IPADDR, "IPADDR", dump_ipaddr },
+ { PROV_IE_SUBNET, "SUBNET", dump_ipaddr },
+ { PROV_IE_GATEWAY, "GATEWAY", dump_ipaddr },
+ { PROV_IE_PORTNO, "BINDPORT", dump_short },
+ { PROV_IE_USER, "USERNAME", dump_string },
+ { PROV_IE_PASS, "PASSWORD", dump_string },
+ { PROV_IE_LANG, "LANGUAGE", dump_string },
+ { PROV_IE_TOS, "TYPEOFSERVICE", dump_byte },
+ { PROV_IE_FLAGS, "FLAGS", dump_prov_flags },
+ { PROV_IE_FORMAT, "FORMAT", dump_int },
+ { PROV_IE_AESKEY, "AESKEY" },
+ { PROV_IE_SERVERIP, "SERVERIP", dump_ipaddr },
+ { PROV_IE_SERVERPORT, "SERVERPORT", dump_short },
+ { PROV_IE_NEWAESKEY, "NEWAESKEY" },
+ { PROV_IE_PROVVER, "PROV VERSION", dump_int },
+ { PROV_IE_ALTSERVER, "ALTSERVERIP", dump_ipaddr },
+};
+
+const char *iax_ie2str(int ie)
+{
+ int x;
+ for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
+ if (ies[x].ie == ie)
+ return ies[x].name;
+ }
+ return "Unknown IE";
+}
+
+
+static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len)
+{
+ int ielen;
+ int ie;
+ int x;
+ int found;
+ char interp[80];
+ char tmp[256];
+ if (len < 2)
+ return;
+ strcpy(output, "\n");
+ maxlen -= strlen(output); output += strlen(output);
+ while(len > 2) {
+ ie = iedata[0];
+ ielen = iedata[1];
+ if (ielen + 2> len) {
+ snprintf(tmp, (int)sizeof(tmp), "Total Prov IE length of %d bytes exceeds remaining prov frame length of %d bytes\n", ielen + 2, len);
+ ast_copy_string(output, tmp, maxlen);
+ maxlen -= strlen(output);
+ output += strlen(output);
+ return;
+ }
+ found = 0;
+ for (x=0;x<(int)sizeof(prov_ies) / (int)sizeof(prov_ies[0]); x++) {
+ if (prov_ies[x].ie == ie) {
+ if (prov_ies[x].dump) {
+ prov_ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
+ snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", prov_ies[x].name, interp);
+ ast_copy_string(output, tmp, maxlen);
+ maxlen -= strlen(output); output += strlen(output);
+ } else {
+ if (ielen)
+ snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
+ else
+ strcpy(interp, "Present");
+ snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", prov_ies[x].name, interp);
+ ast_copy_string(output, tmp, maxlen);
+ maxlen -= strlen(output); output += strlen(output);
+ }
+ found++;
+ }
+ }
+ if (!found) {
+ snprintf(tmp, (int)sizeof(tmp), " Unknown Prov IE %03d : Present\n", ie);
+ ast_copy_string(output, tmp, maxlen);
+ maxlen -= strlen(output); output += strlen(output);
+ }
+ iedata += (2 + ielen);
+ len -= (2 + ielen);
+ }
+}
+
+static void dump_ies(unsigned char *iedata, int len)
+{
+ int ielen;
+ int ie;
+ int x;
+ int found;
+ char interp[1024];
+ char tmp[1024];
+ if (len < 2)
+ return;
+ while(len > 2) {
+ ie = iedata[0];
+ ielen = iedata[1];
+ if (ielen + 2> len) {
+ snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
+ outputf(tmp);
+ return;
+ }
+ found = 0;
+ for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
+ if (ies[x].ie == ie) {
+ if (ies[x].dump) {
+ ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
+ snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp);
+ outputf(tmp);
+ } else {
+ if (ielen)
+ snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
+ else
+ strcpy(interp, "Present");
+ snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp);
+ outputf(tmp);
+ }
+ found++;
+ }
+ }
+ if (!found) {
+ snprintf(tmp, (int)sizeof(tmp), " Unknown IE %03d : Present\n", ie);
+ outputf(tmp);
+ }
+ iedata += (2 + ielen);
+ len -= (2 + ielen);
+ }
+ outputf("\n");
+}
+
+void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
+{
+ const char *frames[] = {
+ "(0?)",
+ "DTMF_E ",
+ "VOICE ",
+ "VIDEO ",
+ "CONTROL",
+ "NULL ",
+ "IAX ",
+ "TEXT ",
+ "IMAGE ",
+ "HTML ",
+ "CNG ",
+ "MODEM ",
+ "DTMF_B ",
+ };
+ const char *iaxs[] = {
+ "(0?)",
+ "NEW ",
+ "PING ",
+ "PONG ",
+ "ACK ",
+ "HANGUP ",
+ "REJECT ",
+ "ACCEPT ",
+ "AUTHREQ",
+ "AUTHREP",
+ "INVAL ",
+ "LAGRQ ",
+ "LAGRP ",
+ "REGREQ ",
+ "REGAUTH",
+ "REGACK ",
+ "REGREJ ",
+ "REGREL ",
+ "VNAK ",
+ "DPREQ ",
+ "DPREP ",
+ "DIAL ",
+ "TXREQ ",
+ "TXCNT ",
+ "TXACC ",
+ "TXREADY",
+ "TXREL ",
+ "TXREJ ",
+ "QUELCH ",
+ "UNQULCH",
+ "POKE ",
+ "PAGE ",
+ "MWI ",
+ "UNSPRTD",
+ "TRANSFR",
+ "PROVISN",
+ "FWDWNLD",
+ "FWDATA ",
+ "TXMEDIA"
+ };
+ const char *cmds[] = {
+ "(0?)",
+ "HANGUP ",
+ "RING ",
+ "RINGING",
+ "ANSWER ",
+ "BUSY ",
+ "TKOFFHK",
+ "OFFHOOK",
+ "CONGSTN",
+ "FLASH ",
+ "WINK ",
+ "OPTION ",
+ "RDKEY ",
+ "RDUNKEY",
+ "PROGRES",
+ "PROCDNG",
+ "HOLD ",
+ "UNHOLD ",
+ "VIDUPDT", };
+ struct ast_iax2_full_hdr *fh;
+ char retries[20];
+ char class2[20];
+ char subclass2[20];
+ const char *class;
+ const char *subclass;
+ char *dir;
+ char tmp[512];
+
+ switch(rx) {
+ case 0:
+ dir = "Tx";
+ break;
+ case 2:
+ dir = "TE";
+ break;
+ case 3:
+ dir = "RD";
+ break;
+ default:
+ dir = "Rx";
+ break;
+ }
+ if (f) {
+ fh = f->data;
+ snprintf(retries, sizeof(retries), "%03d", f->retries);
+ } else {
+ fh = fhi;
+ if (ntohs(fh->dcallno) & IAX_FLAG_RETRANS)
+ strcpy(retries, "Yes");
+ else
+ strcpy(retries, " No");
+ }
+ if (!(ntohs(fh->scallno) & IAX_FLAG_FULL)) {
+ /* Don't mess with mini-frames */
+ return;
+ }
+ if (fh->type >= (int)sizeof(frames)/(int)sizeof(frames[0])) {
+ snprintf(class2, sizeof(class2), "(%d?)", fh->type);
+ class = class2;
+ } else {
+ class = frames[(int)fh->type];
+ }
+ if (fh->type == AST_FRAME_DTMF_BEGIN || fh->type == AST_FRAME_DTMF_END) {
+ sprintf(subclass2, "%c", fh->csub);
+ subclass = subclass2;
+ } else if (fh->type == AST_FRAME_IAX) {
+ if (fh->csub >= (int)sizeof(iaxs)/(int)sizeof(iaxs[0])) {
+ snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
+ subclass = subclass2;
+ } else {
+ subclass = iaxs[(int)fh->csub];
+ }
+ } else if (fh->type == AST_FRAME_CONTROL) {
+ if (fh->csub >= (int)sizeof(cmds)/(int)sizeof(cmds[0])) {
+ snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
+ subclass = subclass2;
+ } else {
+ subclass = cmds[(int)fh->csub];
+ }
+ } else {
+ snprintf(subclass2, sizeof(subclass2), "%d", fh->csub);
+ subclass = subclass2;
+ }
+ snprintf(tmp, sizeof(tmp),
+ "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s Subclass: %s\n",
+ dir,
+ retries, fh->oseqno, fh->iseqno, class, subclass);
+ outputf(tmp);
+ snprintf(tmp, sizeof(tmp),
+ " Timestamp: %05lums SCall: %5.5d DCall: %5.5d [%s:%d]\n",
+ (unsigned long)ntohl(fh->ts),
+ ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS,
+ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+ outputf(tmp);
+ if (fh->type == AST_FRAME_IAX)
+ dump_ies(fh->iedata, datalen);
+}
+
+int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen)
+{
+ char tmp[256];
+ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+ snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", iax_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
+ errorf(tmp);
+ return -1;
+ }
+ ied->buf[ied->pos++] = ie;
+ ied->buf[ied->pos++] = datalen;
+ memcpy(ied->buf + ied->pos, data, datalen);
+ ied->pos += datalen;
+ return 0;
+}
+
+int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, const struct sockaddr_in *sin)
+{
+ return iax_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
+}
+
+int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value)
+{
+ unsigned int newval;
+ newval = htonl(value);
+ return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
+}
+
+int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value)
+{
+ unsigned short newval;
+ newval = htons(value);
+ return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
+}
+
+int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const char *str)
+{
+ return iax_ie_append_raw(ied, ie, str, strlen(str));
+}
+
+int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat)
+{
+ return iax_ie_append_raw(ied, ie, &dat, 1);
+}
+
+int iax_ie_append(struct iax_ie_data *ied, unsigned char ie)
+{
+ return iax_ie_append_raw(ied, ie, NULL, 0);
+}
+
+void iax_set_output(void (*func)(const char *))
+{
+ outputf = func;
+}
+
+void iax_set_error(void (*func)(const char *))
+{
+ errorf = func;
+}
+
+int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
+{
+ /* Parse data into information elements */
+ int len;
+ int ie;
+ char tmp[256], *tmp2;
+ struct ast_variable *var, *var2, *prev;
+ unsigned int count;
+ memset(ies, 0, (int)sizeof(struct iax_ies));
+ ies->msgcount = -1;
+ ies->firmwarever = -1;
+ ies->calling_ton = -1;
+ ies->calling_tns = -1;
+ ies->calling_pres = -1;
+ ies->samprate = IAX_RATE_8KHZ;
+ while(datalen >= 2) {
+ ie = data[0];
+ len = data[1];
+ if (len > datalen - 2) {
+ errorf("Information element length exceeds message size\n");
+ return -1;
+ }
+ switch(ie) {
+ case IAX_IE_CALLED_NUMBER:
+ ies->called_number = (char *)data + 2;
+ break;
+ case IAX_IE_CALLING_NUMBER:
+ ies->calling_number = (char *)data + 2;
+ break;
+ case IAX_IE_CALLING_ANI:
+ ies->calling_ani = (char *)data + 2;
+ break;
+ case IAX_IE_CALLING_NAME:
+ ies->calling_name = (char *)data + 2;
+ break;
+ case IAX_IE_CALLED_CONTEXT:
+ ies->called_context = (char *)data + 2;
+ break;
+ case IAX_IE_USERNAME:
+ ies->username = (char *)data + 2;
+ break;
+ case IAX_IE_PASSWORD:
+ ies->password = (char *)data + 2;
+ break;
+ case IAX_IE_CODEC_PREFS:
+ ies->codec_prefs = (char *)data + 2;
+ break;
+ case IAX_IE_CAPABILITY:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting capability to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else
+ ies->capability = ntohl(get_unaligned_uint32(data + 2));
+ break;
+ case IAX_IE_FORMAT:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting format to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else
+ ies->format = ntohl(get_unaligned_uint32(data + 2));
+ break;
+ case IAX_IE_LANGUAGE:
+ ies->language = (char *)data + 2;
+ break;
+ case IAX_IE_VERSION:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->version = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_ADSICPE:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting adsicpe to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->adsicpe = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_SAMPLINGRATE:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting samplingrate to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->samprate = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_DNID:
+ ies->dnid = (char *)data + 2;
+ break;
+ case IAX_IE_RDNIS:
+ ies->rdnis = (char *)data + 2;
+ break;
+ case IAX_IE_AUTHMETHODS:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting authmethods to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->authmethods = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_ENCRYPTION:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting encryption to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->encmethods = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_CHALLENGE:
+ ies->challenge = (char *)data + 2;
+ break;
+ case IAX_IE_MD5_RESULT:
+ ies->md5_result = (char *)data + 2;
+ break;
+ case IAX_IE_RSA_RESULT:
+ ies->rsa_result = (char *)data + 2;
+ break;
+ case IAX_IE_APPARENT_ADDR:
+ ies->apparent_addr = ((struct sockaddr_in *)(data + 2));
+ break;
+ case IAX_IE_REFRESH:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting refresh to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->refresh = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_DPSTATUS:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting dpstatus to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->dpstatus = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_CALLNO:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting callno to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->callno = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_CAUSE:
+ ies->cause = (char *)data + 2;
+ break;
+ case IAX_IE_CAUSECODE:
+ if (len != 1) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting causecode to be single byte but was %d\n", len);
+ errorf(tmp);
+ } else {
+ ies->causecode = data[2];
+ }
+ break;
+ case IAX_IE_IAX_UNKNOWN:
+ if (len == 1)
+ ies->iax_unknown = data[2];
+ else {
+ snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
+ errorf(tmp);
+ }
+ break;
+ case IAX_IE_MSGCOUNT:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->msgcount = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_AUTOANSWER:
+ ies->autoanswer = 1;
+ break;
+ case IAX_IE_MUSICONHOLD:
+ ies->musiconhold = 1;
+ break;
+ case IAX_IE_TRANSFERID:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting transferid to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else
+ ies->transferid = ntohl(get_unaligned_uint32(data + 2));
+ break;
+ case IAX_IE_DATETIME:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting date/time to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else
+ ies->datetime = ntohl(get_unaligned_uint32(data + 2));
+ break;
+ case IAX_IE_FIRMWAREVER:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting firmwarever to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->firmwarever = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_DEVICETYPE:
+ ies->devicetype = (char *)data + 2;
+ break;
+ case IAX_IE_SERVICEIDENT:
+ ies->serviceident = (char *)data + 2;
+ break;
+ case IAX_IE_FWBLOCKDESC:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected block desc to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else
+ ies->fwdesc = ntohl(get_unaligned_uint32(data + 2));
+ break;
+ case IAX_IE_FWBLOCKDATA:
+ ies->fwdata = data + 2;
+ ies->fwdatalen = len;
+ break;
+ case IAX_IE_ENCKEY:
+ ies->enckey = data + 2;
+ ies->enckeylen = len;
+ break;
+ case IAX_IE_PROVVER:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected provisioning version to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else {
+ ies->provverpres = 1;
+ ies->provver = ntohl(get_unaligned_uint32(data + 2));
+ }
+ break;
+ case IAX_IE_CALLINGPRES:
+ if (len == 1)
+ ies->calling_pres = data[2];
+ else {
+ snprintf(tmp, (int)sizeof(tmp), "Expected single byte callingpres, but was %d long\n", len);
+ errorf(tmp);
+ }
+ break;
+ case IAX_IE_CALLINGTON:
+ if (len == 1)
+ ies->calling_ton = data[2];
+ else {
+ snprintf(tmp, (int)sizeof(tmp), "Expected single byte callington, but was %d long\n", len);
+ errorf(tmp);
+ }
+ break;
+ case IAX_IE_CALLINGTNS:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting callingtns to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->calling_tns = ntohs(get_unaligned_uint16(data + 2));
+ break;
+ case IAX_IE_RR_JITTER:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected jitter rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else {
+ ies->rr_jitter = ntohl(get_unaligned_uint32(data + 2));
+ }
+ break;
+ case IAX_IE_RR_LOSS:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else {
+ ies->rr_loss = ntohl(get_unaligned_uint32(data + 2));
+ }
+ break;
+ case IAX_IE_RR_PKTS:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else {
+ ies->rr_pkts = ntohl(get_unaligned_uint32(data + 2));
+ }
+ break;
+ case IAX_IE_RR_DELAY:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else {
+ ies->rr_delay = ntohs(get_unaligned_uint16(data + 2));
+ }
+ break;
+ case IAX_IE_RR_DROPPED:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else {
+ ies->rr_dropped = ntohl(get_unaligned_uint32(data + 2));
+ }
+ break;
+ case IAX_IE_RR_OOO:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else {
+ ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2));
+ }
+ break;
+ case IAX_IE_VARIABLE:
+ ast_copy_string(tmp, (char *)data + 2, len + 1);
+ tmp2 = strchr(tmp, '=');
+ if (tmp2)
+ *tmp2++ = '\0';
+ else
+ tmp2 = "";
+ /* Existing variable or new variable? */
+ for (var2 = ies->vars, prev = NULL; var2; prev = var2, var2 = var2->next) {
+ if (strcmp(tmp, var2->name) == 0) {
+ int len = strlen(var2->value) + strlen(tmp2) + 1;
+ char *tmp3 = alloca(len);
+ snprintf(tmp3, len, "%s%s", var2->value, tmp2);
+ var = ast_variable_new(tmp, tmp3, var2->file);
+ var->next = var2->next;
+ if (prev)
+ prev->next = var;
+ else
+ ies->vars = var;
+ ast_free(var2);
+ break;
+ }
+ }
+ if (!var2) {
+ var = ast_variable_new(tmp, tmp2, "");
+ var->next = ies->vars;
+ ies->vars = var;
+ }
+ break;
+ case IAX_IE_OSPTOKEN:
+ if ((count = data[2]) < IAX_MAX_OSPBLOCK_NUM) {
+ ies->osptokenblock[count] = (char *)data + 2 + 1;
+ ies->ospblocklength[count] = len - 1;
+ } else {
+ snprintf(tmp, (int)sizeof(tmp), "Expected OSP token block index to be 0~%d but was %d\n", IAX_MAX_OSPBLOCK_NUM - 1, count);
+ errorf(tmp);
+ }
+ break;
+ default:
+ snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len);
+ outputf(tmp);
+ }
+ /* Overwrite information element with 0, to null terminate previous portion */
+ data[0] = 0;
+ datalen -= (len + 2);
+ data += (len + 2);
+ }
+ /* Null-terminate last field */
+ *data = '\0';
+ if (datalen) {
+ errorf("Invalid information element contents, strange boundary\n");
+ return -1;
+ }
+ return 0;
+}
+
+void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f)
+{
+ fr->af.frametype = f->frametype;
+ fr->af.subclass = f->subclass;
+ fr->af.mallocd = 0; /* Our frame is static relative to the container */
+ fr->af.datalen = f->datalen;
+ fr->af.samples = f->samples;
+ fr->af.offset = AST_FRIENDLY_OFFSET;
+ fr->af.src = f->src;
+ fr->af.delivery.tv_sec = 0;
+ fr->af.delivery.tv_usec = 0;
+ fr->af.data = fr->afdata;
+ fr->af.len = f->len;
+ if (fr->af.datalen) {
+ size_t copy_len = fr->af.datalen;
+ if (copy_len > fr->afdatalen) {
+ ast_log(LOG_ERROR, "Losing frame data because destination buffer size '%d' bytes not big enough for '%d' bytes in the frame\n",
+ (int) fr->afdatalen, (int) fr->af.datalen);
+ copy_len = fr->afdatalen;
+ }
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ /* We need to byte-swap slinear samples from network byte order */
+ if ((fr->af.frametype == AST_FRAME_VOICE) && (fr->af.subclass == AST_FORMAT_SLINEAR)) {
+ /* 2 bytes / sample for SLINEAR */
+ ast_swapcopy_samples(fr->af.data, f->data, copy_len / 2);
+ } else
+#endif
+ memcpy(fr->af.data, f->data, copy_len);
+ }
+}
+
+struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable)
+{
+ struct iax_frame *fr = NULL;
+
+#if !defined(LOW_MEMORY)
+ struct iax_frames *iax_frames = NULL;
+
+ /* Attempt to get a frame from this thread's cache */
+ if ((iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(iax_frames, fr, list) {
+ if (fr->afdatalen >= datalen) {
+ size_t afdatalen = fr->afdatalen;
+ AST_LIST_REMOVE_CURRENT(list);
+ memset(fr, 0, sizeof(*fr));
+ fr->afdatalen = afdatalen;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+ if (!fr) {
+ if (!(fr = ast_calloc_cache(1, sizeof(*fr) + datalen)))
+ return NULL;
+ fr->afdatalen = datalen;
+ }
+#else
+ if (!(fr = ast_calloc(1, sizeof(*fr) + datalen)))
+ return NULL;
+ fr->afdatalen = datalen;
+#endif
+
+
+ fr->direction = direction;
+ fr->retrans = -1;
+ fr->cacheable = cacheable;
+
+ if (fr->direction == DIRECTION_INGRESS)
+ ast_atomic_fetchadd_int(&iframes, 1);
+ else
+ ast_atomic_fetchadd_int(&oframes, 1);
+
+ ast_atomic_fetchadd_int(&frames, 1);
+
+ return fr;
+}
+
+void iax_frame_free(struct iax_frame *fr)
+{
+#if !defined(LOW_MEMORY)
+ struct iax_frames *iax_frames = NULL;
+#endif
+
+ /* Note: does not remove from scheduler! */
+ if (fr->direction == DIRECTION_INGRESS)
+ ast_atomic_fetchadd_int(&iframes, -1);
+ else if (fr->direction == DIRECTION_OUTGRESS)
+ ast_atomic_fetchadd_int(&oframes, -1);
+ else {
+ errorf("Attempt to double free frame detected\n");
+ return;
+ }
+ ast_atomic_fetchadd_int(&frames, -1);
+
+#if !defined(LOW_MEMORY)
+ if (!fr->cacheable || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
+ ast_free(fr);
+ return;
+ }
+
+ fr->direction = 0;
+ AST_LIST_INSERT_HEAD(iax_frames, fr, list);
+#else
+ ast_free(fr);
+#endif
+}
+
+#if !defined(LOW_MEMORY)
+static void frame_cache_cleanup(void *data)
+{
+ struct iax_frames *frames = data;
+ struct iax_frame *cur;
+
+ while ((cur = AST_LIST_REMOVE_HEAD(frames, list)))
+ ast_free(cur);
+
+ ast_free(frames);
+}
+#endif
+
+int iax_get_frames(void) { return frames; }
+int iax_get_iframes(void) { return iframes; }
+int iax_get_oframes(void) { return oframes; }
diff --git a/trunk/channels/iax2-parser.h b/trunk/channels/iax2-parser.h
new file mode 100644
index 000000000..e40669d3d
--- /dev/null
+++ b/trunk/channels/iax2-parser.h
@@ -0,0 +1,163 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Implementation of Inter-Asterisk eXchange
+ *
+ * Copyright (C) 2003, Digium
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+/*!\file
+ * \brief Implementation of the IAX2 protocol
+ */
+
+#ifndef _IAX2_PARSER_H
+#define _IAX2_PARSER_H
+
+#include "asterisk/linkedlists.h"
+
+struct iax_ies {
+ char *called_number;
+ char *calling_number;
+ char *calling_ani;
+ char *calling_name;
+ int calling_ton;
+ int calling_tns;
+ int calling_pres;
+ char *called_context;
+ char *username;
+ char *password;
+ unsigned int capability;
+ unsigned int format;
+ char *codec_prefs;
+ char *language;
+ int version;
+ unsigned short adsicpe;
+ char *dnid;
+ char *rdnis;
+ unsigned int authmethods;
+ unsigned int encmethods;
+ char *challenge;
+ char *md5_result;
+ char *rsa_result;
+ struct sockaddr_in *apparent_addr;
+ unsigned short refresh;
+ unsigned short dpstatus;
+ unsigned short callno;
+ char *cause;
+ unsigned char causecode;
+ unsigned char iax_unknown;
+ int msgcount;
+ int autoanswer;
+ int musiconhold;
+ unsigned int transferid;
+ unsigned int datetime;
+ char *devicetype;
+ char *serviceident;
+ int firmwarever;
+ unsigned int fwdesc;
+ unsigned char *fwdata;
+ unsigned char fwdatalen;
+ unsigned char *enckey;
+ unsigned char enckeylen;
+ unsigned int provver;
+ unsigned short samprate;
+ int provverpres;
+ unsigned int rr_jitter;
+ unsigned int rr_loss;
+ unsigned int rr_pkts;
+ unsigned short rr_delay;
+ unsigned int rr_dropped;
+ unsigned int rr_ooo;
+ struct ast_variable *vars;
+ char *osptokenblock[IAX_MAX_OSPBLOCK_NUM];
+ unsigned int ospblocklength[IAX_MAX_OSPBLOCK_NUM];
+};
+
+#define DIRECTION_INGRESS 1
+#define DIRECTION_OUTGRESS 2
+
+struct iax_frame {
+#ifdef LIBIAX
+ struct iax_session *session;
+ struct iax_event *event;
+#else
+ int sockfd;
+#endif
+
+ /* /Our/ call number */
+ unsigned short callno;
+ /* /Their/ call number */
+ unsigned short dcallno;
+ /* Start of raw frame (outgoing only) */
+ void *data;
+ /* Length of frame (outgoing only) */
+ int datalen;
+ /* How many retries so far? */
+ int retries;
+ /* Outgoing relative timestamp (ms) */
+ unsigned int ts;
+ /* How long to wait before retrying */
+ int retrytime;
+ /* Are we received out of order? */
+ unsigned int outoforder:1;
+ /* Have we been sent at all yet? */
+ unsigned int sentyet:1;
+ /* Non-zero if should be sent to transfer peer */
+ unsigned int transfer:1;
+ /* Non-zero if this is the final message */
+ unsigned int final:1;
+ /* Ingress or outgres */
+ unsigned int direction:2;
+ /* Can this frame be cached? */
+ unsigned int cacheable:1;
+ /* Outgoing Packet sequence number */
+ int oseqno;
+ /* Next expected incoming packet sequence number */
+ int iseqno;
+ /* Retransmission ID */
+ int retrans;
+ /* Easy linking */
+ AST_LIST_ENTRY(iax_frame) list;
+ /* Actual, isolated frame header */
+ struct ast_frame af;
+ /*! Amount of space _allocated_ for data */
+ size_t afdatalen;
+ unsigned char unused[AST_FRIENDLY_OFFSET];
+ unsigned char afdata[0]; /* Data for frame */
+};
+
+struct iax_ie_data {
+ unsigned char buf[1024];
+ int pos;
+};
+
+/* Choose a different function for output */
+void iax_set_output(void (*output)(const char *data));
+/* Choose a different function for errors */
+void iax_set_error(void (*output)(const char *data));
+void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen);
+
+const char *iax_ie2str(int ie);
+
+int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen);
+int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, const struct sockaddr_in *sin);
+int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value);
+int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value);
+int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const char *str);
+int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat);
+int iax_ie_append(struct iax_ie_data *ied, unsigned char ie);
+int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen);
+
+int iax_get_frames(void);
+int iax_get_iframes(void);
+int iax_get_oframes(void);
+
+void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f);
+struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable);
+void iax_frame_free(struct iax_frame *fr);
+#endif
diff --git a/trunk/channels/iax2-provision.c b/trunk/channels/iax2-provision.c
new file mode 100644
index 000000000..5b52a0934
--- /dev/null
+++ b/trunk/channels/iax2-provision.c
@@ -0,0 +1,538 @@
+/*
+ * 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 IAX Provisioning Protocol
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/md5.h"
+#include "asterisk/astdb.h"
+#include "asterisk/utils.h"
+#include "asterisk/acl.h"
+#include "iax2.h"
+#include "iax2-provision.h"
+#include "iax2-parser.h"
+
+static int provinit = 0;
+
+struct iax_template {
+ int dead;
+ char name[80];
+ char src[80];
+ struct iax_template *next;
+ char user[20];
+ char pass[20];
+ char lang[10];
+ unsigned short port;
+ unsigned int server;
+ unsigned short serverport;
+ unsigned int altserver;
+ unsigned int flags;
+ unsigned int format;
+ unsigned int tos;
+} *templates;
+
+static struct iax_flag {
+ char *name;
+ int value;
+} iax_flags[] = {
+ { "register", PROV_FLAG_REGISTER },
+ { "secure", PROV_FLAG_SECURE },
+ { "heartbeat", PROV_FLAG_HEARTBEAT },
+ { "debug", PROV_FLAG_DEBUG },
+ { "disablecid", PROV_FLAG_DIS_CALLERID },
+ { "disablecw", PROV_FLAG_DIS_CALLWAIT },
+ { "disablecidcw", PROV_FLAG_DIS_CIDCW },
+ { "disable3way", PROV_FLAG_DIS_THREEWAY },
+};
+
+char *iax_provflags2str(char *buf, int buflen, unsigned int flags)
+{
+ int x;
+
+ if (!buf || buflen < 1)
+ return NULL;
+
+ buf[0] = '\0';
+
+ for (x = 0; x < sizeof(iax_flags) / sizeof(iax_flags[0]); x++) {
+ if (flags & iax_flags[x].value){
+ strncat(buf, iax_flags[x].name, buflen - strlen(buf) - 1);
+ strncat(buf, ",", buflen - strlen(buf) - 1);
+ }
+ }
+
+ if (!ast_strlen_zero(buf))
+ buf[strlen(buf) - 1] = '\0';
+ else
+ strncpy(buf, "none", buflen - 1);
+
+ return buf;
+}
+
+static unsigned int iax_str2flags(const char *buf)
+{
+ int x;
+ int len;
+ int found;
+ unsigned int flags = 0;
+ char *e;
+ while(buf && *buf) {
+ e = strchr(buf, ',');
+ if (e)
+ len = e - buf;
+ else
+ len = 0;
+ found = 0;
+ for (x=0;x<sizeof(iax_flags) / sizeof(iax_flags[0]); x++) {
+ if ((len && !strncasecmp(iax_flags[x].name, buf, len)) ||
+ (!len && !strcasecmp(iax_flags[x].name, buf))) {
+ flags |= iax_flags[x].value;
+ break;
+ }
+ }
+ if (e) {
+ buf = e + 1;
+ while(*buf && (*buf < 33))
+ buf++;
+ } else
+ break;
+ }
+ return flags;
+}
+AST_MUTEX_DEFINE_STATIC(provlock);
+
+static struct iax_template *iax_template_find(const char *s, int allowdead)
+{
+ struct iax_template *cur;
+ cur = templates;
+ while(cur) {
+ if (!strcasecmp(s, cur->name)) {
+ if (!allowdead && cur->dead)
+ cur = NULL;
+ break;
+ }
+ cur = cur->next;
+ }
+ return cur;
+}
+
+char *iax_prov_complete_template(const char *line, const char *word, int pos, int state)
+{
+ struct iax_template *c;
+ int which=0;
+ char *ret = NULL;
+ int wordlen = strlen(word);
+
+ if (pos == 3) {
+ ast_mutex_lock(&provlock);
+ for (c = templates; c; c = c->next) {
+ if (!strncasecmp(word, c->name, wordlen) && ++which > state) {
+ ret = ast_strdup(c->name);
+ break;
+ }
+ }
+ ast_mutex_unlock(&provlock);
+ }
+ return ret;
+}
+
+static unsigned int prov_ver_calc(struct iax_ie_data *provdata)
+{
+ struct MD5Context md5;
+ unsigned int tmp[4];
+ MD5Init(&md5);
+ MD5Update(&md5, provdata->buf, provdata->pos);
+ MD5Final((unsigned char *)tmp, &md5);
+ return tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3];
+}
+
+int iax_provision_build(struct iax_ie_data *provdata, unsigned int *signature, const char *template, int force)
+{
+ struct iax_template *cur;
+ unsigned int sig;
+ char tmp[40];
+ memset(provdata, 0, sizeof(*provdata));
+ ast_mutex_lock(&provlock);
+ cur = iax_template_find(template, 1);
+ /* If no match, try searching for '*' */
+ if (!cur)
+ cur = iax_template_find("*", 1);
+ if (cur) {
+ /* found it -- add information elements as appropriate */
+ if (force || strlen(cur->user))
+ iax_ie_append_str(provdata, PROV_IE_USER, cur->user);
+ if (force || strlen(cur->pass))
+ iax_ie_append_str(provdata, PROV_IE_PASS, cur->pass);
+ if (force || strlen(cur->lang))
+ iax_ie_append_str(provdata, PROV_IE_LANG, cur->lang);
+ if (force || cur->port)
+ iax_ie_append_short(provdata, PROV_IE_PORTNO, cur->port);
+ if (force || cur->server)
+ iax_ie_append_int(provdata, PROV_IE_SERVERIP, cur->server);
+ if (force || cur->serverport)
+ iax_ie_append_short(provdata, PROV_IE_SERVERPORT, cur->serverport);
+ if (force || cur->altserver)
+ iax_ie_append_int(provdata, PROV_IE_ALTSERVER, cur->altserver);
+ if (force || cur->flags)
+ iax_ie_append_int(provdata, PROV_IE_FLAGS, cur->flags);
+ if (force || cur->format)
+ iax_ie_append_int(provdata, PROV_IE_FORMAT, cur->format);
+ if (force || cur->tos)
+ iax_ie_append_byte(provdata, PROV_IE_TOS, cur->tos);
+
+ /* Calculate checksum of message so far */
+ sig = prov_ver_calc(provdata);
+ if (signature)
+ *signature = sig;
+ /* Store signature */
+ iax_ie_append_int(provdata, PROV_IE_PROVVER, sig);
+ /* Cache signature for later verification so we need not recalculate all this */
+ snprintf(tmp, sizeof(tmp), "v0x%08x", sig);
+ ast_db_put("iax/provisioning/cache", template, tmp);
+ } else
+ ast_db_put("iax/provisioning/cache", template, "u");
+ ast_mutex_unlock(&provlock);
+ return cur ? 0 : -1;
+}
+
+int iax_provision_version(unsigned int *version, const char *template, int force)
+{
+ char tmp[80] = "";
+ struct iax_ie_data ied;
+ int ret=0;
+ memset(&ied, 0, sizeof(ied));
+
+ ast_mutex_lock(&provlock);
+ ast_db_get("iax/provisioning/cache", template, tmp, sizeof(tmp));
+ if (sscanf(tmp, "v%x", version) != 1) {
+ if (strcmp(tmp, "u")) {
+ ret = iax_provision_build(&ied, version, template, force);
+ if (ret)
+ ast_debug(1, "Unable to create provisioning packet for '%s'\n", template);
+ } else
+ ret = -1;
+ } else
+ ast_debug(1, "Retrieved cached version '%s' = '%08x'\n", tmp, *version);
+ ast_mutex_unlock(&provlock);
+ return ret;
+}
+
+static int iax_template_parse(struct iax_template *cur, struct ast_config *cfg, const char *s, const char *def)
+{
+ struct ast_variable *v;
+ int foundportno = 0;
+ int foundserverportno = 0;
+ int x;
+ struct in_addr ia;
+ struct hostent *hp;
+ struct ast_hostent h;
+ struct iax_template *src, tmp;
+ const char *t;
+ if (def) {
+ t = ast_variable_retrieve(cfg, s ,"template");
+ src = NULL;
+ if (t && strlen(t)) {
+ src = iax_template_find(t, 0);
+ if (!src)
+ ast_log(LOG_WARNING, "Unable to find base template '%s' for creating '%s'. Trying '%s'\n", t, s, def);
+ else
+ def = t;
+ }
+ if (!src) {
+ src = iax_template_find(def, 0);
+ if (!src)
+ ast_log(LOG_WARNING, "Unable to locate default base template '%s' for creating '%s', omitting.\n", def, s);
+ }
+ if (!src)
+ return -1;
+ ast_mutex_lock(&provlock);
+ /* Backup old data */
+ memcpy(&tmp, cur, sizeof(tmp));
+ /* Restore from src */
+ memcpy(cur, src, sizeof(tmp));
+ /* Restore important headers */
+ memcpy(cur->name, tmp.name, sizeof(cur->name));
+ cur->dead = tmp.dead;
+ cur->next = tmp.next;
+ ast_mutex_unlock(&provlock);
+ }
+ if (def)
+ strncpy(cur->src, def, sizeof(cur->src) - 1);
+ else
+ cur->src[0] = '\0';
+ v = ast_variable_browse(cfg, s);
+ while(v) {
+ if (!strcasecmp(v->name, "port") || !strcasecmp(v->name, "serverport")) {
+ if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < 65535)) {
+ if (!strcasecmp(v->name, "port")) {
+ cur->port = x;
+ foundportno = 1;
+ } else {
+ cur->serverport = x;
+ foundserverportno = 1;
+ }
+ } else
+ ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno);
+ } else if (!strcasecmp(v->name, "server") || !strcasecmp(v->name, "altserver")) {
+ hp = ast_gethostbyname(v->value, &h);
+ if (hp) {
+ memcpy(&ia, hp->h_addr, sizeof(ia));
+ if (!strcasecmp(v->name, "server"))
+ cur->server = ntohl(ia.s_addr);
+ else
+ cur->altserver = ntohl(ia.s_addr);
+ } else
+ ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno);
+ } else if (!strcasecmp(v->name, "codec")) {
+ if ((x = ast_getformatbyname(v->value)) > 0) {
+ cur->format = x;
+ } else
+ ast_log(LOG_WARNING, "Ignoring invalid codec '%s' for '%s' at line %d\n", v->value, s, v->lineno);
+ } else if (!strcasecmp(v->name, "tos")) {
+ if (ast_str2tos(v->value, &cur->tos))
+ ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "user")) {
+ strncpy(cur->user, v->value, sizeof(cur->user) - 1);
+ if (strcmp(cur->user, v->value))
+ ast_log(LOG_WARNING, "Truncating username from '%s' to '%s' for '%s' at line %d\n", v->value, cur->user, s, v->lineno);
+ } else if (!strcasecmp(v->name, "pass")) {
+ strncpy(cur->pass, v->value, sizeof(cur->pass) - 1);
+ if (strcmp(cur->pass, v->value))
+ ast_log(LOG_WARNING, "Truncating password from '%s' to '%s' for '%s' at line %d\n", v->value, cur->pass, s, v->lineno);
+ } else if (!strcasecmp(v->name, "language")) {
+ strncpy(cur->lang, v->value, sizeof(cur->lang) - 1);
+ if (strcmp(cur->lang, v->value))
+ ast_log(LOG_WARNING, "Truncating language from '%s' to '%s' for '%s' at line %d\n", v->value, cur->lang, s, v->lineno);
+ } else if (!strcasecmp(v->name, "flags")) {
+ cur->flags = iax_str2flags(v->value);
+ } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '+')) {
+ cur->flags |= iax_str2flags(v->value);
+ } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '-')) {
+ cur->flags &= ~iax_str2flags(v->value);
+ } else if (strcasecmp(v->name, "template")) {
+ ast_log(LOG_WARNING, "Unknown keyword '%s' in definition of '%s' at line %d\n", v->name, s, v->lineno);
+ }
+ v = v->next;
+ }
+ if (!foundportno)
+ cur->port = IAX_DEFAULT_PORTNO;
+ if (!foundserverportno)
+ cur->serverport = IAX_DEFAULT_PORTNO;
+ return 0;
+}
+
+static int iax_process_template(struct ast_config *cfg, char *s, char *def)
+{
+ /* Find an already existing one if there */
+ struct iax_template *cur;
+ int mallocd = 0;
+ cur = templates;
+ while(cur) {
+ if (!strcasecmp(cur->name, s))
+ break;
+ cur = cur->next;
+ }
+ if (!cur) {
+ mallocd = 1;
+ cur = ast_calloc(1, sizeof(*cur));
+ if (!cur) {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ return -1;
+ }
+ /* Initialize entry */
+ strncpy(cur->name, s, sizeof(cur->name) - 1);
+ cur->dead = 1;
+ }
+ if (!iax_template_parse(cur, cfg, s, def))
+ cur->dead = 0;
+
+ /* Link if we're mallocd */
+ if (mallocd) {
+ ast_mutex_lock(&provlock);
+ cur->next = templates;
+ templates = cur;
+ ast_mutex_unlock(&provlock);
+ }
+ return 0;
+}
+
+static const char *ifthere(const char *s)
+{
+ if (strlen(s))
+ return s;
+ else
+ return "<unspecified>";
+}
+
+static const char *iax_server(unsigned int addr)
+{
+ struct in_addr ia;
+
+ if (!addr)
+ return "<unspecified>";
+
+ ia.s_addr = htonl(addr);
+
+ return ast_inet_ntoa(ia);
+}
+
+
+static char *iax_show_provisioning(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct iax_template *cur;
+ char server[INET_ADDRSTRLEN];
+ char alternate[INET_ADDRSTRLEN];
+ char flags[80]; /* Has to be big enough for 'flags' too */
+ int found = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "iax2 show provisioning";
+ e->usage =
+ "Usage: iax2 show provisioning [template]\n"
+ " Lists all known IAX provisioning templates or a\n"
+ " specific one if specified.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return iax_prov_complete_template(a->line, a->word, a->pos, a->n);
+ }
+
+ if ((a->argc != 3) && (a->argc != 4))
+ return CLI_SHOWUSAGE;
+ ast_mutex_lock(&provlock);
+ for (cur = templates;cur;cur = cur->next) {
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3], cur->name))) {
+ if (found)
+ ast_cli(a->fd, "\n");
+ ast_copy_string(server, iax_server(cur->server), sizeof(server));
+ ast_copy_string(alternate, iax_server(cur->altserver), sizeof(alternate));
+ ast_cli(a->fd, "== %s ==\n", cur->name);
+ ast_cli(a->fd, "Base Templ: %s\n", strlen(cur->src) ? cur->src : "<none>");
+ ast_cli(a->fd, "Username: %s\n", ifthere(cur->user));
+ ast_cli(a->fd, "Secret: %s\n", ifthere(cur->pass));
+ ast_cli(a->fd, "Language: %s\n", ifthere(cur->lang));
+ ast_cli(a->fd, "Bind Port: %d\n", cur->port);
+ ast_cli(a->fd, "Server: %s\n", server);
+ ast_cli(a->fd, "Server Port: %d\n", cur->serverport);
+ ast_cli(a->fd, "Alternate: %s\n", alternate);
+ ast_cli(a->fd, "Flags: %s\n", iax_provflags2str(flags, sizeof(flags), cur->flags));
+ ast_cli(a->fd, "Format: %s\n", ast_getformatname(cur->format));
+ ast_cli(a->fd, "TOS: 0x%x\n", cur->tos);
+ found++;
+ }
+ }
+ ast_mutex_unlock(&provlock);
+ if (!found) {
+ if (a->argc == 3)
+ ast_cli(a->fd, "No provisioning templates found\n");
+ else
+ ast_cli(a->fd, "No provisioning template matching '%s' found\n", a->argv[3]);
+ }
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_iax2_provision[] = {
+ AST_CLI_DEFINE(iax_show_provisioning, "Display iax provisioning"),
+};
+
+static int iax_provision_init(void)
+{
+ ast_cli_register_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry));
+ provinit = 1;
+ return 0;
+}
+
+int iax_provision_unload(void)
+{
+ provinit = 0;
+ ast_cli_unregister_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry));
+ return 0;
+}
+
+int iax_provision_reload(int reload)
+{
+ struct ast_config *cfg;
+ struct iax_template *cur, *prev, *next;
+ char *cat;
+ int found = 0;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ if (!provinit)
+ iax_provision_init();
+ /* Mark all as dead. No need for locking */
+ cur = templates;
+ while(cur) {
+ cur->dead = 1;
+ cur = cur->next;
+ }
+ cfg = ast_config_load("iaxprov.conf", config_flags);
+ if (cfg != NULL && cfg != CONFIG_STATUS_FILEUNCHANGED) {
+ /* Load as appropriate */
+ cat = ast_category_browse(cfg, NULL);
+ while(cat) {
+ if (strcasecmp(cat, "general")) {
+ iax_process_template(cfg, cat, found ? "default" : NULL);
+ found++;
+ ast_verb(3, "Loaded provisioning template '%s'\n", cat);
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_config_destroy(cfg);
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ else
+ ast_log(LOG_NOTICE, "No IAX provisioning configuration found, IAX provisioning disabled.\n");
+ ast_mutex_lock(&provlock);
+ /* Drop dead entries while locked */
+ prev = NULL;
+ cur = templates;
+ while(cur) {
+ next = cur->next;
+ if (cur->dead) {
+ if (prev)
+ prev->next = next;
+ else
+ templates = next;
+ ast_free(cur);
+ } else
+ prev = cur;
+ cur = next;
+ }
+ ast_mutex_unlock(&provlock);
+ /* Purge cached signature DB entries */
+ ast_db_deltree("iax/provisioning/cache", NULL);
+ return 0;
+
+}
diff --git a/trunk/channels/iax2-provision.h b/trunk/channels/iax2-provision.h
new file mode 100644
index 000000000..b1dfd06d0
--- /dev/null
+++ b/trunk/channels/iax2-provision.h
@@ -0,0 +1,53 @@
+/*
+ * IAX Provisioning Protocol
+ *
+ * Sub-information elements
+ *
+ * Copyright (C) 2003, Digium
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ */
+
+/*! \file
+ * \brief IAX2 Provisioning protocol
+ */
+
+#include "iax2-parser.h"
+
+#define PROV_IE_USEDHCP 1 /* Presense only */
+#define PROV_IE_IPADDR 2 /* 32-bit */
+#define PROV_IE_SUBNET 3 /* 32-bit */
+#define PROV_IE_GATEWAY 4 /* 32-bit */
+#define PROV_IE_PORTNO 5 /* 16-bit */
+#define PROV_IE_USER 6 /* < 20 bytes */
+#define PROV_IE_PASS 7 /* < 20 bytes */
+#define PROV_IE_SERVERUSER 8 /* < 20 bytes */
+#define PROV_IE_SERVERPASS 9 /* < 20 bytes */
+#define PROV_IE_LANG 10 /* < 10 bytes */
+#define PROV_IE_TOS 11 /* 8-bits */
+#define PROV_IE_FLAGS 12 /* 32-bits */
+#define PROV_IE_FORMAT 13 /* 32-bits */
+#define PROV_IE_AESKEY 14 /* 128-bits */
+#define PROV_IE_SERVERIP 15 /* 32-bits */
+#define PROV_IE_SERVERPORT 16 /* 16-bits */
+#define PROV_IE_NEWAESKEY 17 /* 128-bits */
+#define PROV_IE_PROVVER 18 /* 32-bits */
+#define PROV_IE_ALTSERVER 19 /* 32-bits */
+
+#define PROV_FLAG_REGISTER (1 << 0)
+#define PROV_FLAG_SECURE (1 << 1)
+#define PROV_FLAG_HEARTBEAT (1 << 2)
+#define PROV_FLAG_DEBUG (1 << 3)
+
+#define PROV_FLAG_DIS_CALLERID (1 << 4) /* Caller-ID Disabled */
+#define PROV_FLAG_DIS_CALLWAIT (1 << 5) /* Caller-ID / Call Waiting Disable */
+#define PROV_FLAG_DIS_CIDCW (1 << 6) /* CID/CW Disabled */
+#define PROV_FLAG_DIS_THREEWAY (1 << 7) /* Three-way calling, transfer disabled */
+
+char *iax_provflags2str(char *buf, int buflen, unsigned int flags);
+int iax_provision_reload(int reload);
+int iax_provision_unload(void);
+int iax_provision_build(struct iax_ie_data *provdata, unsigned int *signature, const char *template, int force);
+int iax_provision_version(unsigned int *signature, const char *template, int force);
+char *iax_prov_complete_template(const char *line, const char *word, int pos, int state);
diff --git a/trunk/channels/iax2.h b/trunk/channels/iax2.h
new file mode 100644
index 000000000..b3947fffa
--- /dev/null
+++ b/trunk/channels/iax2.h
@@ -0,0 +1,277 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Implementation of Inter-Asterisk eXchange
+ *
+ * Copyright (C) 2003, Digium
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+/*! \file
+ * \brief
+ *
+ * Implementation of Inter-Asterisk eXchange, version 2
+ * \ref iax2-parser.c
+ * \ref iax2-parser.h
+ * \ref chan_iax2.c
+ */
+
+#ifndef _IAX2_H
+#define _IAX2_H
+
+/* Max version of IAX protocol we support */
+#define IAX_PROTO_VERSION 2
+
+#define IAX_MAX_CALLS 32768
+
+#define IAX_FLAG_FULL 0x8000
+
+#define IAX_FLAG_RETRANS 0x8000
+
+#define IAX_FLAG_SC_LOG 0x80
+
+#define IAX_MAX_SHIFT 0x1F
+
+#define IAX_WINDOW 64
+
+/*! Subclass for AST_FRAME_IAX */
+enum {
+ IAX_COMMAND_NEW = 1,
+ IAX_COMMAND_PING = 2,
+ IAX_COMMAND_PONG = 3,
+ IAX_COMMAND_ACK = 4,
+ IAX_COMMAND_HANGUP = 5,
+ IAX_COMMAND_REJECT = 6,
+ IAX_COMMAND_ACCEPT = 7,
+ IAX_COMMAND_AUTHREQ = 8,
+ IAX_COMMAND_AUTHREP = 9,
+ IAX_COMMAND_INVAL = 10,
+ IAX_COMMAND_LAGRQ = 11,
+ IAX_COMMAND_LAGRP = 12,
+ /*! Registration request */
+ IAX_COMMAND_REGREQ = 13,
+ /*! Registration authentication required */
+ IAX_COMMAND_REGAUTH = 14,
+ /*! Registration accepted */
+ IAX_COMMAND_REGACK = 15,
+ /*! Registration rejected */
+ IAX_COMMAND_REGREJ = 16,
+ /*! Force release of registration */
+ IAX_COMMAND_REGREL = 17,
+ /*! If we receive voice before valid first voice frame, send this */
+ IAX_COMMAND_VNAK = 18,
+ /*! Request status of a dialplan entry */
+ IAX_COMMAND_DPREQ = 19,
+ /*! Request status of a dialplan entry */
+ IAX_COMMAND_DPREP = 20,
+ /*! Request a dial on channel brought up TBD */
+ IAX_COMMAND_DIAL = 21,
+ /*! Transfer Request */
+ IAX_COMMAND_TXREQ = 22,
+ /*! Transfer Connect */
+ IAX_COMMAND_TXCNT = 23,
+ /*! Transfer Accepted */
+ IAX_COMMAND_TXACC = 24,
+ /*! Transfer ready */
+ IAX_COMMAND_TXREADY = 25,
+ /*! Transfer release */
+ IAX_COMMAND_TXREL = 26,
+ /*! Transfer reject */
+ IAX_COMMAND_TXREJ = 27,
+ /*! Stop audio/video transmission */
+ IAX_COMMAND_QUELCH = 28,
+ /*! Resume audio/video transmission */
+ IAX_COMMAND_UNQUELCH = 29,
+ /*! Like ping, but does not require an open connection */
+ IAX_COMMAND_POKE = 30,
+ /*! Paging description */
+ IAX_COMMAND_PAGE = 31,
+ /*! Stand-alone message waiting indicator */
+ IAX_COMMAND_MWI = 32,
+ /*! Unsupported message received */
+ IAX_COMMAND_UNSUPPORT = 33,
+ /*! Request remote transfer */
+ IAX_COMMAND_TRANSFER = 34,
+ /*! Provision device */
+ IAX_COMMAND_PROVISION = 35,
+ /*! Download firmware */
+ IAX_COMMAND_FWDOWNL = 36,
+ /*! Firmware Data */
+ IAX_COMMAND_FWDATA = 37,
+ /*! Transfer media only */
+ IAX_COMMAND_TXMEDIA = 38,
+};
+
+/*! By default require re-registration once per minute */
+#define IAX_DEFAULT_REG_EXPIRE 60
+
+/*! How long to wait before closing bridged call */
+#define IAX_LINGER_TIMEOUT 10
+
+#define IAX_DEFAULT_PORTNO 4569
+
+/*! IAX Information elements */
+#define IAX_IE_CALLED_NUMBER 1 /*!< Number/extension being called - string */
+#define IAX_IE_CALLING_NUMBER 2 /*!< Calling number - string */
+#define IAX_IE_CALLING_ANI 3 /*!< Calling number ANI for billing - string */
+#define IAX_IE_CALLING_NAME 4 /*!< Name of caller - string */
+#define IAX_IE_CALLED_CONTEXT 5 /*!< Context for number - string */
+#define IAX_IE_USERNAME 6 /*!< Username (peer or user) for authentication - string */
+#define IAX_IE_PASSWORD 7 /*!< Password for authentication - string */
+#define IAX_IE_CAPABILITY 8 /*!< Actual codec capability - unsigned int */
+#define IAX_IE_FORMAT 9 /*!< Desired codec format - unsigned int */
+#define IAX_IE_LANGUAGE 10 /*!< Desired language - string */
+#define IAX_IE_VERSION 11 /*!< Protocol version - short */
+#define IAX_IE_ADSICPE 12 /*!< CPE ADSI capability - short */
+#define IAX_IE_DNID 13 /*!< Originally dialed DNID - string */
+#define IAX_IE_AUTHMETHODS 14 /*!< Authentication method(s) - short */
+#define IAX_IE_CHALLENGE 15 /*!< Challenge data for MD5/RSA - string */
+#define IAX_IE_MD5_RESULT 16 /*!< MD5 challenge result - string */
+#define IAX_IE_RSA_RESULT 17 /*!< RSA challenge result - string */
+#define IAX_IE_APPARENT_ADDR 18 /*!< Apparent address of peer - struct sockaddr_in */
+#define IAX_IE_REFRESH 19 /*!< When to refresh registration - short */
+#define IAX_IE_DPSTATUS 20 /*!< Dialplan status - short */
+#define IAX_IE_CALLNO 21 /*!< Call number of peer - short */
+#define IAX_IE_CAUSE 22 /*!< Cause - string */
+#define IAX_IE_IAX_UNKNOWN 23 /*!< Unknown IAX command - byte */
+#define IAX_IE_MSGCOUNT 24 /*!< How many messages waiting - short */
+#define IAX_IE_AUTOANSWER 25 /*!< Request auto-answering -- none */
+#define IAX_IE_MUSICONHOLD 26 /*!< Request musiconhold with QUELCH -- none or string */
+#define IAX_IE_TRANSFERID 27 /*!< Transfer Request Identifier -- int */
+#define IAX_IE_RDNIS 28 /*!< Referring DNIS -- string */
+#define IAX_IE_PROVISIONING 29 /*!< Provisioning info */
+#define IAX_IE_AESPROVISIONING 30 /*!< AES Provisioning info */
+#define IAX_IE_DATETIME 31 /*!< Date/Time */
+#define IAX_IE_DEVICETYPE 32 /*!< Device Type -- string */
+#define IAX_IE_SERVICEIDENT 33 /*!< Service Identifier -- string */
+#define IAX_IE_FIRMWAREVER 34 /*!< Firmware revision -- u16 */
+#define IAX_IE_FWBLOCKDESC 35 /*!< Firmware block description -- u32 */
+#define IAX_IE_FWBLOCKDATA 36 /*!< Firmware block of data -- raw */
+#define IAX_IE_PROVVER 37 /*!< Provisioning Version (u32) */
+#define IAX_IE_CALLINGPRES 38 /*!< Calling presentation (u8) */
+#define IAX_IE_CALLINGTON 39 /*!< Calling type of number (u8) */
+#define IAX_IE_CALLINGTNS 40 /*!< Calling transit network select (u16) */
+#define IAX_IE_SAMPLINGRATE 41 /*!< Supported sampling rates (u16) */
+#define IAX_IE_CAUSECODE 42 /*!< Hangup cause (u8) */
+#define IAX_IE_ENCRYPTION 43 /*!< Encryption format (u16) */
+#define IAX_IE_ENCKEY 44 /*!< Encryption key (raw) */
+#define IAX_IE_CODEC_PREFS 45 /*!< Codec Negotiation */
+
+#define IAX_IE_RR_JITTER 46 /*!< Received jitter (as in RFC1889) u32 */
+#define IAX_IE_RR_LOSS 47 /*!< Received loss (high byte loss pct, low 24 bits loss count, as in rfc1889 */
+#define IAX_IE_RR_PKTS 48 /*!< Received frames (total frames received) u32 */
+#define IAX_IE_RR_DELAY 49 /*!< Max playout delay for received frames (in ms) u16 */
+#define IAX_IE_RR_DROPPED 50 /*!< Dropped frames (presumably by jitterbuf) u32 */
+#define IAX_IE_RR_OOO 51 /*!< Frames received Out of Order u32 */
+#define IAX_IE_VARIABLE 52 /*!< Remote variables */
+#define IAX_IE_OSPTOKEN 53 /*!< OSP token */
+
+#define IAX_MAX_OSPBLOCK_SIZE 254 /*!< Max OSP token block size, 255 bytes - 1 byte OSP token block index */
+#define IAX_MAX_OSPBLOCK_NUM 4
+#define IAX_MAX_OSPTOKEN_SIZE (IAX_MAX_OSPBLOCK_SIZE * IAX_MAX_OSPBLOCK_NUM)
+#define IAX_MAX_OSPBUFF_SIZE (IAX_MAX_OSPTOKEN_SIZE + 16)
+
+#define IAX_AUTH_PLAINTEXT (1 << 0)
+#define IAX_AUTH_MD5 (1 << 1)
+#define IAX_AUTH_RSA (1 << 2)
+
+#define IAX_ENCRYPT_AES128 (1 << 0)
+
+#define IAX_META_TRUNK 1 /*!< Trunk meta-message */
+#define IAX_META_VIDEO 2 /*!< Video frame */
+
+#define IAX_META_TRUNK_SUPERMINI 0 /*!< This trunk frame contains classic supermini frames */
+#define IAX_META_TRUNK_MINI 1 /*!< This trunk frame contains trunked mini frames */
+
+#define IAX_RATE_8KHZ (1 << 0) /*!< 8khz sampling (default if absent) */
+#define IAX_RATE_11KHZ (1 << 1) /*!< 11.025khz sampling */
+#define IAX_RATE_16KHZ (1 << 2) /*!< 16khz sampling */
+#define IAX_RATE_22KHZ (1 << 3) /*!< 22.05khz sampling */
+#define IAX_RATE_44KHZ (1 << 4) /*!< 44.1khz sampling */
+#define IAX_RATE_48KHZ (1 << 5) /*!< 48khz sampling */
+
+#define IAX_DPSTATUS_EXISTS (1 << 0)
+#define IAX_DPSTATUS_CANEXIST (1 << 1)
+#define IAX_DPSTATUS_NONEXISTENT (1 << 2)
+#define IAX_DPSTATUS_IGNOREPAT (1 << 14)
+#define IAX_DPSTATUS_MATCHMORE (1 << 15)
+
+/*! Full frames are always delivered reliably */
+struct ast_iax2_full_hdr {
+ unsigned short scallno; /*!< Source call number -- high bit must be 1 */
+ unsigned short dcallno; /*!< Destination call number -- high bit is 1 if retransmission */
+ unsigned int ts; /*!< 32-bit timestamp in milliseconds (from 1st transmission) */
+ unsigned char oseqno; /*!< Packet number (outgoing) */
+ unsigned char iseqno; /*!< Packet number (next incoming expected) */
+ unsigned char type; /*!< Frame type */
+ unsigned char csub; /*!< Compressed subclass */
+ unsigned char iedata[0];
+} __attribute__ ((__packed__));
+
+/*! Full frames are always delivered reliably */
+struct ast_iax2_full_enc_hdr {
+ unsigned short scallno; /*!< Source call number -- high bit must be 1 */
+ unsigned short dcallno; /*!< Destination call number -- high bit is 1 if retransmission */
+ unsigned char encdata[0];
+} __attribute__ ((__packed__));
+
+/*! Mini header is used only for voice frames -- delivered unreliably */
+struct ast_iax2_mini_hdr {
+ unsigned short callno; /*!< Source call number -- high bit must be 0, rest must be non-zero */
+ unsigned short ts; /*!< 16-bit Timestamp (high 16 bits from last ast_iax2_full_hdr) */
+ /* Frametype implicitly VOICE_FRAME */
+ /* subclass implicit from last ast_iax2_full_hdr */
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+
+/*! Mini header is used only for voice frames -- delivered unreliably */
+struct ast_iax2_mini_enc_hdr {
+ unsigned short callno; /*!< Source call number -- high bit must be 0, rest must be non-zero */
+ unsigned char encdata[0];
+} __attribute__ ((__packed__));
+
+struct ast_iax2_meta_hdr {
+ unsigned short zeros; /*!< Zeros field -- must be zero */
+ unsigned char metacmd; /*!< Meta command */
+ unsigned char cmddata; /*!< Command Data */
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+
+struct ast_iax2_video_hdr {
+ unsigned short zeros; /*!< Zeros field -- must be zero */
+ unsigned short callno; /*!< Video call number */
+ unsigned short ts; /*!< Timestamp and mark if present */
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+
+struct ast_iax2_meta_trunk_hdr {
+ unsigned int ts; /*!< 32-bit timestamp for all messages */
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+
+struct ast_iax2_meta_trunk_entry {
+ unsigned short callno; /*!< Call number */
+ unsigned short len; /*!< Length of data for this callno */
+} __attribute__ ((__packed__));
+
+/*! When trunktimestamps are used, we use this format instead */
+struct ast_iax2_meta_trunk_mini {
+ unsigned short len;
+ struct ast_iax2_mini_hdr mini; /*!< this is an actual miniframe */
+} __attribute__ ((__packed__));
+
+#define IAX_FIRMWARE_MAGIC 0x69617879
+
+struct ast_iax2_firmware_header {
+ unsigned int magic; /*!< Magic number */
+ unsigned short version; /*!< Software version */
+ unsigned char devname[16]; /*!< Device */
+ unsigned int datalen; /*!< Data length of file beyond header */
+ unsigned char chksum[16]; /*!< Checksum of all data */
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+#endif
diff --git a/trunk/channels/misdn/Makefile b/trunk/channels/misdn/Makefile
new file mode 100644
index 000000000..85478225b
--- /dev/null
+++ b/trunk/channels/misdn/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for chan_misdn support
+#
+ifneq ($(wildcard /usr/include/linux/mISDNdsp.h),)
+CFLAGS+=-DMISDN_1_2
+endif
+
+all:
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+portinfo: portinfo.o
+ $(CC) -o $@ $^ -lisdnnet -lmISDN -lpthread
+
+clean:
+ rm -rf *.a *.o *.so portinfo
diff --git a/trunk/channels/misdn/chan_misdn_config.h b/trunk/channels/misdn/chan_misdn_config.h
new file mode 100644
index 000000000..8c27f4f24
--- /dev/null
+++ b/trunk/channels/misdn/chan_misdn_config.h
@@ -0,0 +1,160 @@
+/*
+ * Chan_Misdn -- Channel Driver for Asterisk
+ *
+ * Interface to mISDN
+ *
+ * Copyright (C) 2004, Christian Richter
+ *
+ * Christian Richter <crich@beronet.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+/*! \file \brief
+ * Interface to mISDN - Config
+ * \author Christian Richter <crich@beronet.com>
+ */
+
+
+
+
+#ifndef CHAN_MISDN_CONFIG_H
+#define CHAN_MISDN_CONFIG_H
+
+#define BUFFERSIZE 512
+
+enum misdn_cfg_elements {
+
+ /* port config items */
+ MISDN_CFG_FIRST = 0,
+ MISDN_CFG_GROUPNAME, /* char[] */
+ MISDN_CFG_ALLOWED_BEARERS, /* char[] */
+ MISDN_CFG_FAR_ALERTING, /* int (bool) */
+ MISDN_CFG_RXGAIN, /* int */
+ MISDN_CFG_TXGAIN, /* int */
+ MISDN_CFG_TE_CHOOSE_CHANNEL, /* int (bool) */
+ MISDN_CFG_PMP_L1_CHECK, /* int (bool) */
+ MISDN_CFG_REJECT_CAUSE, /* int */
+ MISDN_CFG_ALARM_BLOCK, /* int (bool) */
+ MISDN_CFG_HDLC, /* int (bool) */
+ MISDN_CFG_CONTEXT, /* char[] */
+ MISDN_CFG_LANGUAGE, /* char[] */
+ MISDN_CFG_MUSICCLASS, /* char[] */
+ MISDN_CFG_CALLERID, /* char[] */
+ MISDN_CFG_METHOD, /* char[] */
+ MISDN_CFG_DIALPLAN, /* int */
+ MISDN_CFG_LOCALDIALPLAN, /* int */
+ MISDN_CFG_CPNDIALPLAN, /* int */
+ MISDN_CFG_NATPREFIX, /* char[] */
+ MISDN_CFG_INTERNATPREFIX, /* char[] */
+ MISDN_CFG_PRES, /* int */
+ MISDN_CFG_SCREEN, /* int */
+ MISDN_CFG_ALWAYS_IMMEDIATE, /* int (bool) */
+ MISDN_CFG_NODIALTONE, /* int (bool) */
+ MISDN_CFG_IMMEDIATE, /* int (bool) */
+ MISDN_CFG_SENDDTMF, /* int (bool) */
+ MISDN_CFG_ASTDTMF, /* int (bool) */
+ MISDN_CFG_HOLD_ALLOWED, /* int (bool) */
+ MISDN_CFG_EARLY_BCONNECT, /* int (bool) */
+ MISDN_CFG_INCOMING_EARLY_AUDIO, /* int (bool) */
+ MISDN_CFG_ECHOCANCEL, /* int */
+#ifdef MISDN_1_2
+ MISDN_CFG_PIPELINE, /* char[] */
+#endif
+
+#ifdef WITH_BEROEC
+ MISDN_CFG_BNECHOCANCEL,
+ MISDN_CFG_BNEC_ANTIHOWL,
+ MISDN_CFG_BNEC_NLP,
+ MISDN_CFG_BNEC_ZEROCOEFF,
+ MISDN_CFG_BNEC_TD,
+ MISDN_CFG_BNEC_ADAPT,
+#endif
+ MISDN_CFG_NEED_MORE_INFOS, /* bool */
+ MISDN_CFG_NOAUTORESPOND_ON_SETUP, /* bool */
+ MISDN_CFG_NTTIMEOUT, /* bool */
+ MISDN_CFG_BRIDGING, /* bool */
+ MISDN_CFG_JITTERBUFFER, /* int */
+ MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, /* int */
+ MISDN_CFG_CALLGROUP, /* ast_group_t */
+ MISDN_CFG_PICKUPGROUP, /* ast_group_t */
+ MISDN_CFG_MAX_IN, /* int */
+ MISDN_CFG_MAX_OUT, /* int */
+ MISDN_CFG_L1_TIMEOUT, /* int */
+ MISDN_CFG_OVERLAP_DIAL, /* int (bool)*/
+ MISDN_CFG_MSNS, /* char[] */
+ MISDN_CFG_FAXDETECT, /* char[] */
+ MISDN_CFG_FAXDETECT_CONTEXT, /* char[] */
+ MISDN_CFG_FAXDETECT_TIMEOUT, /* int */
+ MISDN_CFG_PTP, /* int (bool) */
+ MISDN_CFG_LAST,
+
+ /* general config items */
+ MISDN_GEN_FIRST,
+#ifndef MISDN_1_2
+ MISDN_GEN_MISDN_INIT, /* char[] */
+#endif
+ MISDN_GEN_DEBUG, /* int */
+ MISDN_GEN_TRACEFILE, /* char[] */
+ MISDN_GEN_BRIDGING, /* int (bool) */
+ MISDN_GEN_STOP_TONE, /* int (bool) */
+ MISDN_GEN_APPEND_DIGITS2EXTEN, /* int (bool) */
+ MISDN_GEN_DYNAMIC_CRYPT, /* int (bool) */
+ MISDN_GEN_CRYPT_PREFIX, /* char[] */
+ MISDN_GEN_CRYPT_KEYS, /* char[] */
+ MISDN_GEN_NTKEEPCALLS, /* int (bool) */
+ MISDN_GEN_NTDEBUGFLAGS, /* int */
+ MISDN_GEN_NTDEBUGFILE, /* char[] */
+ MISDN_GEN_LAST
+};
+
+enum misdn_cfg_method {
+ METHOD_STANDARD = 0,
+ METHOD_ROUND_ROBIN,
+ METHOD_STANDARD_DEC
+};
+
+/* you must call misdn_cfg_init before any other function of this header file */
+int misdn_cfg_init(int max_ports, int reload);
+void misdn_cfg_reload(void);
+void misdn_cfg_destroy(void);
+
+void misdn_cfg_update_ptp( void );
+
+/* if you requst a general config element, the port value is ignored. if the requested
+ * value is not available, or the buffer is too small, the buffer will be nulled (in
+ * case of a char* only its first byte will be nulled). */
+void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void* buf, int bufsize);
+
+/* returns the enum element for the given name, returns MISDN_CFG_FIRST if none was found */
+enum misdn_cfg_elements misdn_cfg_get_elem (char *name);
+
+/* fills the buffer with the name of the given config element */
+void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize);
+
+/* fills the buffer with the description of the given config element */
+void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default);
+
+/* fills the buffer with a ',' separated list of all active ports */
+void misdn_cfg_get_ports_string(char *ports);
+
+/* fills the buffer with a nice printable string representation of the config element */
+void misdn_cfg_get_config_string(int port, enum misdn_cfg_elements elem, char* buf, int bufsize);
+
+/* returns the next available port number. returns -1 if the last one was reached. */
+int misdn_cfg_get_next_port(int port);
+int misdn_cfg_get_next_port_spin(int port);
+
+int misdn_cfg_is_msn_valid(int port, char* msn);
+int misdn_cfg_is_port_valid(int port);
+int misdn_cfg_is_group_method(char *group, enum misdn_cfg_method meth);
+
+#if 0
+char *misdn_cfg_get_next_group(char *group);
+int misdn_cfg_get_next_port_in_group(int port, char *group);
+#endif
+
+struct ast_jb_conf *misdn_get_global_jbconf(void);
+
+#endif
diff --git a/trunk/channels/misdn/ie.c b/trunk/channels/misdn/ie.c
new file mode 100644
index 000000000..817e3d8cb
--- /dev/null
+++ b/trunk/channels/misdn/ie.c
@@ -0,0 +1,1422 @@
+
+/*
+ * Chan_Misdn -- Channel Driver for Asterisk
+ *
+ * Interface to mISDN
+ *
+ * Copyright (C) 2005, Christian Richter
+ *
+ * Christian Richter <crich@beronet.com>
+ *
+ * heaviliy patched from jollys ie.cpp, jolly gave me ALL
+ * rights for this code, i can even have my own copyright on it.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+/*! \file \brief
+ * Interface to mISDN
+ * \author Christian Richter <crich@beronet.com>
+ */
+
+/*
+ the pointer of enc_ie_* always points to the IE itself
+ if qi is not NULL (TE-mode), offset is set
+*/
+
+
+#include <string.h>
+
+#include <mISDNuser/mISDNlib.h>
+#include <mISDNuser/isdn_net.h>
+#include <mISDNuser/l3dss1.h>
+#include <mISDNuser/net_l3.h>
+#include "asterisk/localtime.h"
+
+
+
+#define MISDN_IE_DEBG 0
+
+/* support stuff */
+static void strnncpy(char *dest, char *src, int len, int dst_len)
+{
+ if (len > dst_len-1)
+ len = dst_len-1;
+ strncpy((char *)dest, (char *)src, len);
+ dest[len] = '\0';
+}
+
+
+/* IE_COMPLETE */
+static void enc_ie_complete(unsigned char **ntmode, msg_t *msg, int complete, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+
+ if (complete<0 || complete>1)
+ {
+ printf("%s: ERROR: complete(%d) is out of range.\n", __FUNCTION__, complete);
+ return;
+ }
+
+ if (complete)
+ if (MISDN_IE_DEBG) printf(" complete=%d\n", complete);
+
+ if (complete)
+ {
+ p = msg_put(msg, 1);
+ if (nt)
+ {
+ *ntmode = p;
+ } else
+ qi->QI_ELEMENT(sending_complete) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+
+ p[0] = IE_COMPLETE;
+ }
+}
+
+static void dec_ie_complete(unsigned char *p, Q931_info_t *qi, int *complete, int nt, struct misdn_bchannel *bc)
+{
+ *complete = 0;
+ if (!nt)
+ {
+ if (qi->QI_ELEMENT(sending_complete))
+ *complete = 1;
+ } else
+ if (p)
+ *complete = 1;
+
+ if (*complete)
+ if (MISDN_IE_DEBG) printf(" complete=%d\n", *complete);
+}
+
+
+/* IE_BEARER */
+static void enc_ie_bearer(unsigned char **ntmode, msg_t *msg, int coding, int capability, int mode, int rate, int multi, int user, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (coding<0 || coding>3)
+ {
+ printf("%s: ERROR: coding(%d) is out of range.\n", __FUNCTION__, coding);
+ return;
+ }
+ if (capability<0 || capability>31)
+ {
+ printf("%s: ERROR: capability(%d) is out of range.\n", __FUNCTION__, capability);
+ return;
+ }
+ if (mode<0 || mode>3)
+ {
+ printf("%s: ERROR: mode(%d) is out of range.\n", __FUNCTION__, mode);
+ return;
+ }
+ if (rate<0 || rate>31)
+ {
+ printf("%s: ERROR: rate(%d) is out of range.\n", __FUNCTION__, rate);
+ return;
+ }
+ if (multi>127)
+ {
+ printf("%s: ERROR: multi(%d) is out of range.\n", __FUNCTION__, multi);
+ return;
+ }
+ if (user>31)
+ {
+ printf("%s: ERROR: user L1(%d) is out of range.\n", __FUNCTION__, rate);
+ return;
+ }
+ if (rate!=24 && multi>=0)
+ {
+ printf("%s: WARNING: multi(%d) is only possible if rate(%d) would be 24.\n", __FUNCTION__, multi, rate);
+ multi = -1;
+ }
+
+ if (MISDN_IE_DEBG) printf(" coding=%d capability=%d mode=%d rate=%d multi=%d user=%d\n", coding, capability, mode, rate, multi, user);
+
+ l = 2 + (multi>=0) + (user>=0);
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(bearer_capability) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_BEARER;
+ p[1] = l;
+ p[2] = 0x80 + (coding<<5) + capability;
+ p[3] = 0x80 + (mode<<5) + rate;
+ if (multi >= 0)
+ p[4] = 0x80 + multi;
+ if (user >= 0)
+ p[4+(multi>=0)] = 0xa0 + user;
+}
+
+static void dec_ie_bearer(unsigned char *p, Q931_info_t *qi, int *coding, int *capability, int *mode, int *rate, int *multi, int *user,
+ int *async, int *urate, int *stopbits, int *dbits, int *parity, int nt, struct misdn_bchannel *bc)
+{
+ int octet;
+ *coding = -1;
+ *capability = -1;
+ *mode = -1;
+ *rate = -1;
+ *multi = -1;
+ *user = -1;
+ *async = -1;
+ *urate = -1;
+ *stopbits = -1;
+ *dbits = -1;
+ *parity = -1;
+
+ if (!nt)
+ {
+ p = NULL;
+#ifdef LLC_SUPPORT
+ if (qi->QI_ELEMENT(llc)) {
+
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(llc) + 1;
+ }
+#endif
+ if (qi->QI_ELEMENT(bearer_capability))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(bearer_capability) + 1;
+ }
+ if (!p)
+ return;
+
+ if (p[0] < 2)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *coding = (p[1]&0x60) >> 5;
+ *capability = p[1] & 0x1f;
+ octet = 2;
+ if (!(p[1] & 0x80))
+ octet++;
+
+ if (p[0] < octet)
+ goto done;
+
+ *mode = (p[octet]&0x60) >> 5;
+ *rate = p[octet] & 0x1f;
+
+ octet++;
+
+ if (p[0] < octet)
+ goto done;
+
+ if (*rate == 0x18) {
+ /* Rate multiplier only present if 64Kb/s base rate */
+ *multi = p[octet++] & 0x7f;
+ }
+
+ if (p[0] < octet)
+ goto done;
+
+ /* Start L1 info */
+ if ((p[octet] & 0x60) == 0x20) {
+ *user = p[octet] & 0x1f;
+
+ if (p[0] <= octet)
+ goto done;
+
+ if (p[octet++] & 0x80)
+ goto l2;
+
+ *async = !!(p[octet] & 0x40);
+ /* 0x20 is inband negotiation */
+ *urate = p[octet] & 0x1f;
+
+ if (p[0] <= octet)
+ goto done;
+
+ if (p[octet++] & 0x80)
+ goto l2;
+
+ /* Ignore next byte for now: Intermediate rate, NIC, flow control */
+
+ if (p[0] <= octet)
+ goto done;
+
+ if (p[octet++] & 0x80)
+ goto l2;
+
+ /* And the next one. Header, multiframe, mode, assignor/ee, negotiation */
+
+ if (p[0] <= octet)
+ goto done;
+
+ if (!p[octet++] & 0x80)
+ goto l2;
+
+ /* Wheee. V.110 speed information */
+
+ *stopbits = (p[octet] & 0x60) >> 5;
+ *dbits = (p[octet] & 0x18) >> 3;
+ *parity = p[octet] & 7;
+
+ octet++;
+ }
+ l2: /* Nobody seems to want the rest so we don't bother (yet) */
+ done:
+ if (MISDN_IE_DEBG) printf(" coding=%d capability=%d mode=%d rate=%d multi=%d user=%d async=%d urate=%d stopbits=%d dbits=%d parity=%d\n", *coding, *capability, *mode, *rate, *multi, *user, *async, *urate, *stopbits, *dbits, *parity);
+}
+
+
+/* IE_CALL_ID */
+#if 0
+static void enc_ie_call_id(unsigned char **ntmode, msg_t *msg, char *callid, int callid_len, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ char debug[25];
+ int i;
+
+ if (!callid || callid_len<=0)
+ {
+ return;
+ }
+ if (callid_len>8)
+ {
+ printf("%s: ERROR: callid_len(%d) is out of range.\n", __FUNCTION__, callid_len);
+ return;
+ }
+
+ i = 0;
+ while(i < callid_len)
+ {
+ if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]);
+ i++;
+ }
+
+ if (MISDN_IE_DEBG) printf(" callid%s\n", debug);
+
+ l = callid_len;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(call_id) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CALL_ID;
+ p[1] = l;
+ memcpy(p+2, callid, callid_len);
+}
+#endif
+
+#if 0
+static void dec_ie_call_id(unsigned char *p, Q931_info_t *qi, char *callid, int *callid_len, int nt, struct misdn_bchannel *bc)
+{
+ char debug[25];
+ int i;
+
+ *callid_len = -1;
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(call_id))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(call_id) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] > 8)
+ {
+ printf("%s: ERROR: IE too long (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *callid_len = p[0];
+ memcpy(callid, p+1, *callid_len);
+
+ i = 0;
+ while(i < *callid_len)
+ {
+ if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]);
+ i++;
+ }
+
+ if (MISDN_IE_DEBG) printf(" callid%s\n", debug);
+}
+#endif
+
+/* IE_CALLED_PN */
+static void enc_ie_called_pn(unsigned char **ntmode, msg_t *msg, int type, int plan, char *number, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (type<0 || type>7)
+ {
+ printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type);
+ return;
+ }
+ if (plan<0 || plan>15)
+ {
+ printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan);
+ return;
+ }
+ if (!number[0])
+ {
+ printf("%s: ERROR: number is not given.\n", __FUNCTION__);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d number='%s'\n", type, plan, number);
+
+ l = 1+strlen((char *)number);
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(called_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CALLED_PN;
+ p[1] = l;
+ p[2] = 0x80 + (type<<4) + plan;
+ strncpy((char *)p+3, (char *)number, strlen((char *)number));
+}
+
+static void dec_ie_called_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, char *number, int number_len, int nt, struct misdn_bchannel *bc)
+{
+ *type = -1;
+ *plan = -1;
+ *number = '\0';
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(called_nr))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(called_nr) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 2)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *type = (p[1]&0x70) >> 4;
+ *plan = p[1] & 0xf;
+ strnncpy(number, (char *)p+2, p[0]-1, number_len);
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d number='%s'\n", *type, *plan, number);
+}
+
+
+/* IE_CALLING_PN */
+static void enc_ie_calling_pn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, int screen, char *number, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (type<0 || type>7)
+ {
+ printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type);
+ return;
+ }
+ if (plan<0 || plan>15)
+ {
+ printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan);
+ return;
+ }
+ if (present>3)
+ {
+ printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present);
+ return;
+ }
+ if (present >= 0) if (screen<0 || screen>3)
+ {
+ printf("%s: ERROR: screen(%d) is out of range.\n", __FUNCTION__, screen);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", type, plan, present, screen, number);
+
+ l = 1;
+ if (number) if (number[0])
+ l += strlen((char *)number);
+ if (present >= 0)
+ l += 1;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(calling_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CALLING_PN;
+ p[1] = l;
+ if (present >= 0)
+ {
+ p[2] = 0x00 + (type<<4) + plan;
+ p[3] = 0x80 + (present<<5) + screen;
+ if (number) if (number[0])
+ strncpy((char *)p+4, (char *)number, strlen((char *)number));
+ } else
+ {
+ p[2] = 0x80 + (type<<4) + plan;
+ if (number) if (number[0])
+ strncpy((char *)p+3, (char *)number, strlen((char *)number));
+ }
+}
+
+static void dec_ie_calling_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc)
+{
+ *type = -1;
+ *plan = -1;
+ *present = -1;
+ *screen = -1;
+ *number = '\0';
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(calling_nr))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(calling_nr) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *type = (p[1]&0x70) >> 4;
+ *plan = p[1] & 0xf;
+ if (!(p[1] & 0x80))
+ {
+ if (p[0] < 2)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+ *present = (p[2]&0x60) >> 5;
+ *screen = p[2] & 0x3;
+ strnncpy(number, (char *)p+3, p[0]-2, number_len);
+ } else
+ {
+ strnncpy(number, (char *)p+2, p[0]-1, number_len);
+ /* SPECIAL workarround for IBT software bug */
+ /* if (number[0]==0x80) */
+ /* strcpy((char *)number, (char *)number+1); */
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", *type, *plan, *present, *screen, number);
+}
+
+
+/* IE_CONNECTED_PN */
+static void enc_ie_connected_pn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, int screen, char *number, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (type<0 || type>7)
+ {
+ printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type);
+ return;
+ }
+ if (plan<0 || plan>15)
+ {
+ printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan);
+ return;
+ }
+ if (present>3)
+ {
+ printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present);
+ return;
+ }
+ if (present >= 0) if (screen<0 || screen>3)
+ {
+ printf("%s: ERROR: screen(%d) is out of range.\n", __FUNCTION__, screen);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", type, plan, present, screen, number);
+
+ l = 1;
+ if (number) if (number[0])
+ l += strlen((char *)number);
+ if (present >= 0)
+ l += 1;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(connected_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CONNECT_PN;
+ p[1] = l;
+ if (present >= 0)
+ {
+ p[2] = 0x00 + (type<<4) + plan;
+ p[3] = 0x80 + (present<<5) + screen;
+ if (number) if (number[0])
+ strncpy((char *)p+4, (char *)number, strlen((char *)number));
+ } else
+ {
+ p[2] = 0x80 + (type<<4) + plan;
+ if (number) if (number[0])
+ strncpy((char *)p+3, (char *)number, strlen((char *)number));
+ }
+}
+
+static void dec_ie_connected_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc)
+{
+ *type = -1;
+ *plan = -1;
+ *present = -1;
+ *screen = -1;
+ *number = '\0';
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(connected_nr))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(connected_nr) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *type = (p[1]&0x70) >> 4;
+ *plan = p[1] & 0xf;
+ if (!(p[1] & 0x80))
+ {
+ if (p[0] < 2)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+ *present = (p[2]&0x60) >> 5;
+ *screen = p[2] & 0x3;
+ strnncpy(number, (char *)p+3, p[0]-2, number_len);
+ } else
+ {
+ strnncpy(number, (char *)p+2, p[0]-1, number_len);
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", *type, *plan, *present, *screen, number);
+}
+
+
+/* IE_CAUSE */
+static void enc_ie_cause(unsigned char **ntmode, msg_t *msg, int location, int cause, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (location<0 || location>7)
+ {
+ printf("%s: ERROR: location(%d) is out of range.\n", __FUNCTION__, location);
+ return;
+ }
+ if (cause<0 || cause>127)
+ {
+ printf("%s: ERROR: cause(%d) is out of range.\n", __FUNCTION__, cause);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" location=%d cause=%d\n", location, cause);
+
+ l = 2;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(cause) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CAUSE;
+ p[1] = l;
+ p[2] = 0x80 + location;
+ p[3] = 0x80 + cause;
+}
+
+#if 0
+static void enc_ie_cause_standalone(unsigned char **ntmode, msg_t *msg, int location, int cause, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p = msg_put(msg, 4);
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ if (ntmode)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(cause) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CAUSE;
+ p[1] = 2;
+ p[2] = 0x80 + location;
+ p[3] = 0x80 + cause;
+}
+#endif
+
+static void dec_ie_cause(unsigned char *p, Q931_info_t *qi, int *location, int *cause, int nt, struct misdn_bchannel *bc)
+{
+ *location = -1;
+ *cause = -1;
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(cause))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(cause) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 2)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *location = p[1] & 0x0f;
+ *cause = p[2] & 0x7f;
+
+ if (MISDN_IE_DEBG) printf(" location=%d cause=%d\n", *location, *cause);
+}
+
+
+/* IE_CHANNEL_ID */
+static void enc_ie_channel_id(unsigned char **ntmode, msg_t *msg, int exclusive, int channel, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+ int pri = stack->pri;
+
+ if (exclusive<0 || exclusive>1)
+ {
+ printf("%s: ERROR: exclusive(%d) is out of range.\n", __FUNCTION__, exclusive);
+ return;
+ }
+ if ((channel<0 || channel>0xff)
+ || (!pri && (channel>2 && channel<0xff))
+ || (pri && (channel>31 && channel<0xff))
+ || (pri && channel==16))
+ {
+ printf("%s: ERROR: channel(%d) is out of range.\n", __FUNCTION__, channel);
+ return;
+ }
+
+ /* if (MISDN_IE_DEBG) printf(" exclusive=%d channel=%d\n", exclusive, channel); */
+
+
+ if (!pri)
+ {
+ /* BRI */
+ l = 1;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(channel_id) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CHANNEL_ID;
+ p[1] = l;
+ if (channel == 0xff)
+ channel = 3;
+ p[2] = 0x80 + (exclusive<<3) + channel;
+ /* printf(" exclusive=%d channel=%d\n", exclusive, channel); */
+ } else
+ {
+ /* PRI */
+ if (channel == 0) /* no channel */
+ return; /* IE not present */
+/* if (MISDN_IE_DEBG) printf("channel = %d\n", channel); */
+ if (channel == 0xff) /* any channel */
+ {
+ l = 1;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(channel_id) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CHANNEL_ID;
+ p[1] = l;
+ p[2] = 0x80 + 0x20 + 0x03;
+/* if (MISDN_IE_DEBG) printf("%02x\n", p[2]); */
+ return; /* end */
+ }
+ l = 3;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(channel_id) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_CHANNEL_ID;
+ p[1] = l;
+ p[2] = 0x80 + 0x20 + (exclusive<<3) + 0x01;
+ p[3] = 0x80 + 3; /* CCITT, Number, B-type */
+ p[4] = 0x80 + channel;
+/* if (MISDN_IE_DEBG) printf("%02x %02x %02x\n", p[2], p[3], p[4]); */
+ }
+}
+
+static void dec_ie_channel_id(unsigned char *p, Q931_info_t *qi, int *exclusive, int *channel, int nt, struct misdn_bchannel *bc)
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+ int pri =stack->pri;
+
+ *exclusive = -1;
+ *channel = -1;
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(channel_id))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(channel_id) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ if (p[1] & 0x40)
+ {
+ printf("%s: ERROR: refering to channels of other interfaces is not supported.\n", __FUNCTION__);
+ return;
+ }
+ if (p[1] & 0x04)
+ {
+ printf("%s: ERROR: using d-channel is not supported.\n", __FUNCTION__);
+ return;
+ }
+
+ *exclusive = (p[1]&0x08) >> 3;
+ if (!pri)
+ {
+ /* BRI */
+ if (p[1] & 0x20)
+ {
+ printf("%s: ERROR: extended channel ID with non PRI interface.\n", __FUNCTION__);
+ return;
+ }
+ *channel = p[1] & 0x03;
+ if (*channel == 3)
+ *channel = 0xff;
+ } else
+ {
+ /* PRI */
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short for PRI (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+ if (!(p[1] & 0x20))
+ {
+ printf("%s: ERROR: basic channel ID with PRI interface.\n", __FUNCTION__);
+ return;
+ }
+ if ((p[1]&0x03) == 0x00)
+ {
+ /* no channel */
+ *channel = 0;
+ return;
+ }
+ if ((p[1]&0x03) == 0x03)
+ {
+ /* any channel */
+ *channel = 0xff;
+ return;
+ }
+ if (p[0] < 3)
+ {
+ printf("%s: ERROR: IE too short for PRI with channel(%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+ if (p[2] & 0x10)
+ {
+ printf("%s: ERROR: channel map not supported.\n", __FUNCTION__);
+ return;
+ }
+ *channel = p[3] & 0x7f;
+ if ( (*channel<1) | (*channel==16) | (*channel>31))
+ {
+ printf("%s: ERROR: PRI interface channel out of range (%d).\n", __FUNCTION__, *channel);
+ return;
+ }
+/* if (MISDN_IE_DEBG) printf("%02x %02x %02x\n", p[1], p[2], p[3]); */
+ }
+
+ if (MISDN_IE_DEBG) printf(" exclusive=%d channel=%d\n", *exclusive, *channel);
+}
+
+
+/* IE_DATE */
+static void enc_ie_date(unsigned char **ntmode, msg_t *msg, time_t ti, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+ struct timeval tv = { ti, 0 };
+ struct ast_tm tm;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (MISDN_IE_DEBG) printf(" year=%d month=%d day=%d hour=%d minute=%d\n", tm.tm_year%100, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
+
+ l = 5;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(date) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_DATE;
+ p[1] = l;
+ p[2] = tm.tm_year % 100;
+ p[3] = tm.tm_mon + 1;
+ p[4] = tm.tm_mday;
+ p[5] = tm.tm_hour;
+ p[6] = tm.tm_min;
+}
+
+
+/* IE_DISPLAY */
+static void enc_ie_display(unsigned char **ntmode, msg_t *msg, char *display, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (!display[0])
+ {
+ printf("%s: ERROR: display text not given.\n", __FUNCTION__);
+ return;
+ }
+
+ if (strlen((char *)display) > 80)
+ {
+ printf("%s: WARNING: display text too long (max 80 chars), cutting.\n", __FUNCTION__);
+ display[80] = '\0';
+ }
+
+ /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, strlen((char *)display)); */
+
+ l = strlen((char *)display);
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(display) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_DISPLAY;
+ p[1] = l;
+ strncpy((char *)p+2, (char *)display, strlen((char *)display));
+}
+
+#if 0
+static void dec_ie_display(unsigned char *p, Q931_info_t *qi, char *display, int display_len, int nt, struct misdn_bchannel *bc)
+{
+ *display = '\0';
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(display))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(display) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ strnncpy(display, (char *)p+1, p[0], display_len);
+
+ if (MISDN_IE_DEBG) printf(" display='%s'\n", display);
+}
+#endif
+
+/* IE_KEYPAD */
+#if 1
+static void enc_ie_keypad(unsigned char **ntmode, msg_t *msg, char *keypad, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (!keypad[0])
+ {
+ printf("%s: ERROR: keypad info not given.\n", __FUNCTION__);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" keypad='%s'\n", keypad);
+
+ l = strlen(keypad);
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(keypad) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_KEYPAD;
+ p[1] = l;
+ strncpy((char *)p+2, keypad, strlen(keypad));
+}
+#endif
+
+static void dec_ie_keypad(unsigned char *p, Q931_info_t *qi, char *keypad, int keypad_len, int nt, struct misdn_bchannel *bc)
+{
+ *keypad = '\0';
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(keypad))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(keypad) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ strnncpy(keypad, (char *)p+1, p[0], keypad_len);
+
+ if (MISDN_IE_DEBG) printf(" keypad='%s'\n", keypad);
+}
+
+
+/* IE_NOTIFY */
+#if 0
+static void enc_ie_notify(unsigned char **ntmode, msg_t *msg, int notify, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (notify<0 || notify>0x7f)
+ {
+ printf("%s: ERROR: notify(%d) is out of range.\n", __FUNCTION__, notify);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" notify=%d\n", notify);
+
+ l = 1;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(notify) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_NOTIFY;
+ p[1] = l;
+ p[2] = 0x80 + notify;
+}
+#endif
+
+#if 0
+static void dec_ie_notify(unsigned char *p, Q931_info_t *qi, int *notify, int nt, struct misdn_bchannel *bc)
+{
+ *notify = -1;
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(notify))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(notify) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *notify = p[1] & 0x7f;
+
+ if (MISDN_IE_DEBG) printf(" notify=%d\n", *notify);
+}
+#endif
+
+
+/* IE_PROGRESS */
+static void enc_ie_progress(unsigned char **ntmode, msg_t *msg, int coding, int location, int progress, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (coding<0 || coding>0x03)
+ {
+ printf("%s: ERROR: coding(%d) is out of range.\n", __FUNCTION__, coding);
+ return;
+ }
+ if (location<0 || location>0x0f)
+ {
+ printf("%s: ERROR: location(%d) is out of range.\n", __FUNCTION__, location);
+ return;
+ }
+ if (progress<0 || progress>0x7f)
+ {
+ printf("%s: ERROR: progress(%d) is out of range.\n", __FUNCTION__, progress);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" coding=%d location=%d progress=%d\n", coding, location, progress);
+
+ l = 2;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(progress) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_PROGRESS;
+ p[1] = l;
+ p[2] = 0x80 + (coding<<5) + location;
+ p[3] = 0x80 + progress;
+}
+
+static void dec_ie_progress(unsigned char *p, Q931_info_t *qi, int *coding, int *location, int *progress, int nt, struct misdn_bchannel *bc)
+{
+ *coding = -1;
+ *location = -1;
+ //*progress = -1;
+ *progress = 0;
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(progress))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(progress) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *coding = (p[1]&0x60) >> 5;
+ *location = p[1] & 0x0f;
+ *progress = p[2] & 0x7f;
+
+ if (MISDN_IE_DEBG) printf(" coding=%d location=%d progress=%d\n", *coding, *location, *progress);
+}
+
+
+/* IE_REDIR_NR (redirecting = during MT_SETUP) */
+static void enc_ie_redir_nr(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, int screen, int reason, char *number, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ if (type<0 || type>7)
+ {
+ printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type);
+ return;
+ }
+ if (plan<0 || plan>15)
+ {
+ printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan);
+ return;
+ }
+ if (present > 3)
+ {
+ printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present);
+ return;
+ }
+ if (present >= 0) if (screen<0 || screen>3)
+ {
+ printf("%s: ERROR: screen(%d) is out of range.\n", __FUNCTION__, screen);
+ return;
+ }
+ if (reason > 0x0f)
+ {
+ printf("%s: ERROR: reason(%d) is out of range.\n", __FUNCTION__, reason);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d readon=%d number='%s'\n", type, plan, present, screen, reason, number);
+
+ l = 1;
+ if (number)
+ l += strlen((char *)number);
+ if (present >= 0)
+ {
+ l += 1;
+ if (reason >= 0)
+ l += 1;
+ }
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(redirect_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_REDIR_NR;
+ p[1] = l;
+ if (present >= 0)
+ {
+ if (reason >= 0)
+ {
+ p[2] = 0x00 + (type<<4) + plan;
+ p[3] = 0x00 + (present<<5) + screen;
+ p[4] = 0x80 + reason;
+ if (number)
+ strncpy((char *)p+5, (char *)number, strlen((char *)number));
+ } else
+ {
+ p[2] = 0x00 + (type<<4) + plan;
+ p[3] = 0x80 + (present<<5) + screen;
+ if (number)
+ strncpy((char *)p+4, (char *)number, strlen((char *)number));
+ }
+ } else
+ {
+ p[2] = 0x80 + (type<<4) + plan;
+ if (number) if (number[0])
+ strncpy((char *)p+3, (char *)number, strlen((char *)number));
+ }
+}
+
+static void dec_ie_redir_nr(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, int *reason, char *number, int number_len, int nt, struct misdn_bchannel *bc)
+{
+ *type = -1;
+ *plan = -1;
+ *present = -1;
+ *screen = -1;
+ *reason = -1;
+ *number = '\0';
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(redirect_nr))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redirect_nr) + 1;
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *type = (p[1]&0x70) >> 4;
+ *plan = p[1] & 0xf;
+ if (!(p[1] & 0x80))
+ {
+ *present = (p[2]&0x60) >> 5;
+ *screen = p[2] & 0x3;
+ if (!(p[2] & 0x80))
+ {
+ *reason = p[3] & 0x0f;
+ strnncpy(number, (char *)p+4, p[0]-3, number_len);
+ } else
+ {
+ strnncpy(number, (char *)p+3, p[0]-2, number_len);
+ }
+ } else
+ {
+ strnncpy(number, (char *)p+2, p[0]-1, number_len);
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d reason=%d number='%s'\n", *type, *plan, *present, *screen, *reason, number);
+}
+
+
+/* IE_REDIR_DN (redirection = during MT_NOTIFY) */
+#if 0
+static void enc_ie_redir_dn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, char *number, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+/* Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); */
+ int l;
+
+ if (type<0 || type>7)
+ {
+ printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type);
+ return;
+ }
+ if (plan<0 || plan>15)
+ {
+ printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan);
+ return;
+ }
+ if (present > 3)
+ {
+ printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present);
+ return;
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d number='%s'\n", type, plan, present, number);
+
+ l = 1;
+ if (number)
+ l += strlen((char *)number);
+ if (present >= 0)
+ l += 1;
+ p = msg_put(msg, l+2);
+ if (nt)
+ *ntmode = p+1;
+ else
+/* #warning REINSERT redir_dn, when included in te-mode */
+ /*qi->QI_ELEMENT(redir_dn) = p - (unsigned char *)qi - sizeof(Q931_info_t)*/;
+ p[0] = IE_REDIR_DN;
+ p[1] = l;
+ if (present >= 0)
+ {
+ p[2] = 0x00 + (type<<4) + plan;
+ p[3] = 0x80 + (present<<5);
+ if (number)
+ strncpy((char *)p+4, (char *)number, strlen((char *)number));
+ } else
+ {
+ p[2] = 0x80 + (type<<4) + plan;
+ if (number)
+ strncpy((char *)p+3, (char *)number, strlen((char *)number));
+ }
+}
+#endif
+
+#if 0
+static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, char *number, int number_len, int nt, struct misdn_bchannel *bc)
+{
+ *type = -1;
+ *plan = -1;
+ *present = -1;
+ *number = '\0';
+
+ if (!nt)
+ {
+ p = NULL;
+/* #warning REINSERT redir_dn, when included in te-mode */
+/* if (qi->QI_ELEMENT(redir_dn)) */
+/* p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redir_dn) + 1; */
+ }
+ if (!p)
+ return;
+ if (p[0] < 1)
+ {
+ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]);
+ return;
+ }
+
+ *type = (p[1]&0x70) >> 4;
+ *plan = p[1] & 0xf;
+ if (!(p[1] & 0x80))
+ {
+ *present = (p[2]&0x60) >> 5;
+ strnncpy(number, (char *)p+3, p[0]-2, number_len);
+ } else
+ {
+ strnncpy(number, (char *)p+2, p[0]-1, number_len);
+ }
+
+ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d number='%s'\n", *type, *plan, *present, number);
+}
+#endif
+
+
+/* IE_USERUSER */
+#if 1
+static void enc_ie_useruser(unsigned char **ntmode, msg_t *msg, int protocol, char *user, int user_len, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ int l;
+
+ char debug[768];
+ int i;
+
+ if (protocol<0 || protocol>127)
+ {
+ printf("%s: ERROR: protocol(%d) is out of range.\n", __FUNCTION__, protocol);
+ return;
+ }
+ if (!user || user_len<=0)
+ {
+ return;
+ }
+
+ i = 0;
+ while(i < user_len)
+ {
+ if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", user[i]);
+ i++;
+ }
+
+ if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", protocol, debug);
+
+ l = user_len+1;
+ p = msg_put(msg, l+3);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(useruser) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_USER_USER;
+ p[1] = l;
+ p[2] = protocol;
+ memcpy(p+3, user, user_len);
+}
+#endif
+
+#if 1
+static void dec_ie_useruser(unsigned char *p, Q931_info_t *qi, int *protocol, char *user, int *user_len, int nt, struct misdn_bchannel *bc)
+{
+ char debug[768];
+ int i;
+
+ *user_len = 0;
+ *protocol = -1;
+
+ if (!nt)
+ {
+ p = NULL;
+ if (qi->QI_ELEMENT(useruser))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(useruser) + 1;
+ }
+ if (!p)
+ return;
+
+ *user_len = p[0]-1;
+ if (p[0] < 1)
+ return;
+ *protocol = p[1];
+ memcpy(user, p+2, (*user_len<=128)?*(user_len):128); /* clip to 128 maximum */
+
+ i = 0;
+ while(i < *user_len)
+ {
+ if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", user[i]);
+ i++;
+ }
+ debug[i*3] = '\0';
+
+ if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", *protocol, debug);
+}
+#endif
+
+/* IE_DISPLAY */
+static void enc_ie_restart_ind(unsigned char **ntmode, msg_t *msg, unsigned char rind, int nt, struct misdn_bchannel *bc)
+{
+ unsigned char *p;
+ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, strlen((char *)display)); */
+
+ p = msg_put(msg, 3);
+ if (nt)
+ *ntmode = p+1;
+ else
+ qi->QI_ELEMENT(restart_ind) = p - (unsigned char *)qi - sizeof(Q931_info_t);
+ p[0] = IE_RESTART_IND;
+ p[1] = 1;
+ p[2] = rind;
+
+}
+
diff --git a/trunk/channels/misdn/isdn_lib.c b/trunk/channels/misdn/isdn_lib.c
new file mode 100644
index 000000000..67ecb47fa
--- /dev/null
+++ b/trunk/channels/misdn/isdn_lib.c
@@ -0,0 +1,4582 @@
+/*
+ * Chan_Misdn -- Channel Driver for Asterisk
+ *
+ * Interface to mISDN
+ *
+ * Copyright (C) 2004, Christian Richter
+ *
+ * Christian Richter <crich@beronet.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+/*! \file \brief
+ * Interface to mISDN
+ * \author Christian Richter <crich@beronet.com>
+ */
+
+
+
+#include <syslog.h>
+#include <mISDNuser/isdn_debug.h>
+
+#include "isdn_lib_intern.h"
+#include "isdn_lib.h"
+
+void misdn_join_conf(struct misdn_bchannel *bc, int conf_id);
+void misdn_split_conf(struct misdn_bchannel *bc, int conf_id);
+
+int queue_cleanup_bc(struct misdn_bchannel *bc) ;
+
+int misdn_lib_get_l2_up(struct misdn_stack *stack);
+
+struct misdn_stack* get_misdn_stack( void );
+
+static int set_chan_in_stack(struct misdn_stack *stack, int channel);
+
+int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh);
+
+int misdn_lib_port_is_pri(int port)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) {
+ return stack->pri;
+ }
+ }
+
+ return -1;
+}
+
+int misdn_lib_port_is_nt(int port)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) {
+ return stack->nt;
+ }
+ }
+
+ return -1;
+}
+
+void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel)
+{
+ memset (dummybc,0,sizeof(struct misdn_bchannel));
+ dummybc->port=port;
+ if (l3id==0)
+ dummybc->l3_id = MISDN_ID_DUMMY;
+ else
+ dummybc->l3_id=l3id;
+
+ dummybc->nt=nt;
+ dummybc->dummy=1;
+ dummybc->channel=channel;
+}
+
+int misdn_lib_port_block(int port)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) {
+ stack->blocked=1;
+ return 0;
+ }
+ }
+ return -1;
+
+}
+
+int misdn_lib_port_unblock(int port)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) {
+ stack->blocked=0;
+ return 0;
+ }
+ }
+ return -1;
+
+}
+
+int misdn_lib_is_port_blocked(int port)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) {
+ return stack->blocked;
+ }
+ }
+ return -1;
+}
+
+int misdn_lib_is_ptp(int port)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) return stack->ptp;
+ }
+ return -1;
+}
+
+int misdn_lib_get_maxchans(int port)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) {
+ if (stack->pri)
+ return 30;
+ else
+ return 2;
+ }
+ }
+ return -1;
+}
+
+
+struct misdn_stack* get_stack_by_bc(struct misdn_bchannel *bc)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+
+ if (!bc) return NULL;
+
+ for ( ; stack; stack=stack->next) {
+ int i;
+ for (i=0; i <=stack->b_num; i++) {
+ if ( bc->port == stack->port) return stack;
+ }
+ }
+
+ return NULL;
+}
+
+
+void get_show_stack_details(int port, char *buf)
+{
+ struct misdn_stack *stack=get_misdn_stack();
+
+ for ( ; stack; stack=stack->next) {
+ if (stack->port == port) break;
+ }
+
+ if (stack) {
+ sprintf(buf, "* Port %d Type %s Prot. %s L2Link %s L1Link:%s Blocked:%d", stack->port, stack->nt?"NT":"TE", stack->ptp?"PTP":"PMP", stack->l2link?"UP":"DOWN", stack->l1link?"UP":"DOWN",stack->blocked);
+
+ } else {
+ buf[0]=0;
+ }
+
+}
+
+
+static int nt_err_cnt =0 ;
+
+enum global_states {
+ MISDN_INITIALIZING,
+ MISDN_INITIALIZED
+} ;
+
+static enum global_states global_state=MISDN_INITIALIZING;
+
+
+#include <mISDNuser/net_l2.h>
+#include <mISDNuser/tone.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "isdn_lib.h"
+
+
+struct misdn_lib {
+ int midev;
+ int midev_nt;
+
+ pthread_t event_thread;
+ pthread_t event_handler_thread;
+
+ void *user_data;
+
+ msg_queue_t upqueue;
+ msg_queue_t activatequeue;
+
+ sem_t new_msg;
+
+ struct misdn_stack *stack_list;
+} ;
+
+#ifndef ECHOCAN_ON
+#define ECHOCAN_ON 123
+#define ECHOCAN_OFF 124
+#endif
+
+#define MISDN_DEBUG 0
+
+void misdn_tx_jitter(struct misdn_bchannel *bc, int len);
+
+struct misdn_bchannel *find_bc_by_l3id(struct misdn_stack *stack, unsigned long l3id);
+
+struct misdn_bchannel *find_bc_by_confid(unsigned long confid);
+
+struct misdn_bchannel *stack_holder_find_bychan(struct misdn_stack *stack, int chan);
+
+int setup_bc(struct misdn_bchannel *bc);
+
+int manager_isdn_handler(iframe_t *frm ,msg_t *msg);
+
+int misdn_lib_port_restart(int port);
+int misdn_lib_pid_restart(int pid);
+
+extern struct isdn_msg msgs_g[];
+
+#define ISDN_PID_L3_B_USER 0x430000ff
+#define ISDN_PID_L4_B_USER 0x440000ff
+
+/* #define MISDN_IBUF_SIZE 1024 */
+#define MISDN_IBUF_SIZE 512
+
+/* Fine Tuning of Inband Signalling time */
+#define TONE_ALERT_CNT 41 /* 1 Sec */
+#define TONE_ALERT_SILENCE_CNT 200 /* 4 Sec */
+
+#define TONE_BUSY_CNT 20 /* ? */
+#define TONE_BUSY_SILENCE_CNT 48 /* ? */
+
+static int entity;
+
+static struct misdn_lib *glob_mgr;
+
+char tone_425_flip[TONE_425_SIZE];
+char tone_silence_flip[TONE_SILENCE_SIZE];
+
+static void misdn_lib_isdn_event_catcher(void *arg);
+static int handle_event_nt(void *dat, void *arg);
+
+
+void stack_holder_add(struct misdn_stack *stack, struct misdn_bchannel *holder);
+void stack_holder_remove(struct misdn_stack *stack, struct misdn_bchannel *holder);
+struct misdn_bchannel *stack_holder_find(struct misdn_stack *stack, unsigned long l3id);
+
+/* from isdn_lib.h */
+int init_bc(struct misdn_stack * stack, struct misdn_bchannel *bc, int midev, int port, int bidx, char *msn, int firsttime);
+struct misdn_stack* stack_init(int midev, int port, int ptp);
+void stack_destroy(struct misdn_stack* stack);
+ /* user iface */
+int te_lib_init( void ) ; /* returns midev */
+void te_lib_destroy(int midev) ;
+struct misdn_bchannel *manager_find_bc_by_pid(int pid);
+struct misdn_bchannel *manager_find_bc_holded(struct misdn_bchannel* bc);
+void manager_ph_control_block(struct misdn_bchannel *bc, int c1, void *c2, int c2_len);
+void manager_clean_bc(struct misdn_bchannel *bc );
+void manager_bchannel_setup (struct misdn_bchannel *bc);
+void manager_bchannel_cleanup (struct misdn_bchannel *bc);
+
+void ec_chunk( struct misdn_bchannel *bc, unsigned char *rxchunk, unsigned char *txchunk, int chunk_size);
+ /* end */
+int bchdev_echocancel_activate(struct misdn_bchannel* dev);
+void bchdev_echocancel_deactivate(struct misdn_bchannel* dev);
+/* end */
+
+
+static char *bearer2str(int cap) {
+ static char *bearers[]={
+ "Speech",
+ "Audio 3.1k",
+ "Unres Digital",
+ "Res Digital",
+ "Unknown Bearer"
+ };
+
+ switch (cap) {
+ case INFO_CAPABILITY_SPEECH:
+ return bearers[0];
+ break;
+ case INFO_CAPABILITY_AUDIO_3_1K:
+ return bearers[1];
+ break;
+ case INFO_CAPABILITY_DIGITAL_UNRESTRICTED:
+ return bearers[2];
+ break;
+ case INFO_CAPABILITY_DIGITAL_RESTRICTED:
+ return bearers[3];
+ break;
+ default:
+ return bearers[4];
+ break;
+ }
+}
+
+
+static char flip_table[256];
+
+static void init_flip_bits(void)
+{
+ int i,k;
+
+ for (i = 0 ; i < 256 ; i++) {
+ unsigned char sample = 0 ;
+ for (k = 0; k<8; k++) {
+ if ( i & 1 << k ) sample |= 0x80 >> k;
+ }
+ flip_table[i] = sample;
+ }
+}
+
+static char * flip_buf_bits ( char * buf , int len)
+{
+ int i;
+ char * start = buf;
+
+ for (i = 0 ; i < len; i++) {
+ buf[i] = flip_table[(unsigned char)buf[i]];
+ }
+
+ return start;
+}
+
+
+
+
+static msg_t *create_l2msg(int prim, int dinfo, int size) /* NT only */
+{
+ int i = 0;
+ msg_t *dmsg;
+
+ while(i < 10)
+ {
+ dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL);
+ if (dmsg)
+ return(dmsg);
+
+ if (!i)
+ printf("cannot allocate memory, trying again...\n");
+ i++;
+ usleep(300000);
+ }
+ printf("cannot allocate memory, system overloaded.\n");
+ exit(-1);
+}
+
+
+
+msg_t *create_l3msg(int prim, int mt, int dinfo, int size, int ntmode)
+{
+ int i = 0;
+ msg_t *dmsg;
+ Q931_info_t *qi;
+ iframe_t *frm;
+
+ if (!ntmode)
+ size = sizeof(Q931_info_t)+2;
+
+ while(i < 10) {
+ if (ntmode) {
+ dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL);
+ if (dmsg) {
+ return(dmsg);
+ }
+ } else {
+ dmsg = alloc_msg(size+256+mISDN_HEADER_LEN+DEFAULT_HEADROOM);
+ if (dmsg)
+ {
+ memset(msg_put(dmsg,size+mISDN_HEADER_LEN), 0, size+mISDN_HEADER_LEN);
+ frm = (iframe_t *)dmsg->data;
+ frm->prim = prim;
+ frm->dinfo = dinfo;
+ qi = (Q931_info_t *)(dmsg->data + mISDN_HEADER_LEN);
+ qi->type = mt;
+ return(dmsg);
+ }
+ }
+
+ if (!i) printf("cannot allocate memory, trying again...\n");
+ i++;
+ usleep(300000);
+ }
+ printf("cannot allocate memory, system overloaded.\n");
+ exit(-1);
+}
+
+
+static int send_msg (int midev, struct misdn_bchannel *bc, msg_t *dmsg)
+{
+ iframe_t *frm = (iframe_t *)dmsg->data;
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ if (!stack) {
+ cb_log(0,bc->port,"send_msg: IEK!! no stack\n ");
+ return -1;
+ }
+
+ frm->addr = (stack->upper_id | FLG_MSG_DOWN);
+ frm->dinfo = bc->l3_id;
+ frm->len = (dmsg->len) - mISDN_HEADER_LEN;
+
+ cb_log(4,stack->port,"Sending msg, prim:%x addr:%x dinfo:%x\n",frm->prim,frm->addr,frm->dinfo);
+
+ mISDN_write(midev, dmsg->data, dmsg->len, TIMEOUT_1SEC);
+ free_msg(dmsg);
+
+ return 0;
+}
+
+
+static int mypid=1;
+
+
+int misdn_cap_is_speech(int cap)
+/** Poor mans version **/
+{
+ if ( (cap != INFO_CAPABILITY_DIGITAL_UNRESTRICTED) &&
+ (cap != INFO_CAPABILITY_DIGITAL_RESTRICTED) ) return 1;
+ return 0;
+}
+
+int misdn_inband_avail(struct misdn_bchannel *bc)
+{
+
+ /*if ! early_bconnect we have never inband available*/
+ if ( ! bc->early_bconnect ) return 0;
+
+ switch (bc->progress_indicator) {
+ case INFO_PI_INBAND_AVAILABLE:
+ case INFO_PI_CALL_NOT_E2E_ISDN:
+ case INFO_PI_CALLED_NOT_ISDN:
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+
+static void dump_chan_list(struct misdn_stack *stack)
+{
+ int i;
+
+ for (i=0; i <= stack->b_num; i++) {
+ cb_log(6, stack->port, "Idx:%d stack->cchan:%d in_use:%d Chan:%d\n",i,stack->channels[i], stack->bc[i].in_use, i+1);
+ }
+}
+
+
+void misdn_dump_chanlist()
+{
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ dump_chan_list(stack);
+ }
+
+}
+
+int set_chan_in_stack(struct misdn_stack *stack, int channel)
+{
+
+ cb_log(4,stack->port,"set_chan_in_stack: %d\n",channel);
+ dump_chan_list(stack);
+ if (channel >=1 && channel <= MAX_BCHANS) {
+ if (!stack->channels[channel-1])
+ stack->channels[channel-1] = 1;
+ else {
+ cb_log(4,stack->port,"channel already in use:%d\n", channel );
+ return -1;
+ }
+ } else {
+ cb_log(0,stack->port,"couldn't set channel %d in\n", channel );
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+static int find_free_chan_in_stack(struct misdn_stack *stack, struct misdn_bchannel *bc, int channel, int dec)
+{
+ int i;
+ int chan=0;
+ int bnums = stack->pri ? stack->b_num : stack->b_num - 1;
+
+ if (bc->channel_found)
+ return 0;
+
+ bc->channel_found=1;
+
+ cb_log(5,stack->port,"find_free_chan: req_chan:%d\n",channel);
+
+ if (channel < 0 || channel > MAX_BCHANS) {
+ cb_log(0, stack->port, " !! out of bound call to find_free_chan_in_stack! (ch:%d)\n", channel);
+ return 0;
+ }
+
+ channel--;
+
+ if (dec) {
+ for (i = bnums; i >=0; i--) {
+ if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 Dchannel ;) and work with chan preselection */
+ if (!stack->channels[i]) {
+ cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1);
+ chan=i+1;
+ break;
+ }
+ }
+ }
+ } else {
+ for (i = 0; i <= bnums; i++) {
+ if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 Dchannel ;) and work with chan preselection */
+ if (!stack->channels[i]) {
+ cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1);
+ chan=i+1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!chan) {
+ cb_log (1, stack->port, " !! NO FREE CHAN IN STACK\n");
+ dump_chan_list(stack);
+ bc->out_cause=34;
+ return -1;
+ }
+
+ if (set_chan_in_stack(stack, chan)<0) {
+ cb_log (0, stack->port, "Channel Already in use:%d\n", chan);
+ bc->out_cause=44;
+ return -1;
+ }
+
+ bc->channel=chan;
+ return 0;
+}
+
+static int empty_chan_in_stack(struct misdn_stack *stack, int channel)
+{
+ if (channel<=0 || channel>MAX_BCHANS) {
+ cb_log(0,stack?stack->port:0, "empty_chan_in_stack: cannot empty channel %d\n",channel);
+ return -1;
+ }
+
+ cb_log (4, stack?stack->port:0, "empty_chan_in_stack: %d\n",channel);
+ stack->channels[channel-1] = 0;
+ dump_chan_list(stack);
+ return 0;
+}
+
+char *bc_state2str(enum bchannel_state state) {
+ int i;
+
+ struct bchan_state_s {
+ char *n;
+ enum bchannel_state s;
+ } states[] = {
+ {"BCHAN_CLEANED", BCHAN_CLEANED },
+ {"BCHAN_EMPTY", BCHAN_EMPTY},
+ {"BCHAN_SETUP", BCHAN_SETUP},
+ {"BCHAN_SETUPED", BCHAN_SETUPED},
+ {"BCHAN_ACTIVE", BCHAN_ACTIVE},
+ {"BCHAN_ACTIVATED", BCHAN_ACTIVATED},
+ {"BCHAN_BRIDGE", BCHAN_BRIDGE},
+ {"BCHAN_BRIDGED", BCHAN_BRIDGED},
+ {"BCHAN_RELEASE", BCHAN_RELEASE},
+ {"BCHAN_RELEASED", BCHAN_RELEASED},
+ {"BCHAN_CLEAN", BCHAN_CLEAN},
+ {"BCHAN_CLEAN_REQUEST", BCHAN_CLEAN_REQUEST},
+ {"BCHAN_ERROR", BCHAN_ERROR}
+ };
+
+ for (i=0; i< sizeof(states)/sizeof(struct bchan_state_s); i++)
+ if ( states[i].s == state)
+ return states[i].n;
+
+ return "UNKNOWN";
+}
+
+void bc_state_change(struct misdn_bchannel *bc, enum bchannel_state state)
+{
+ cb_log(5,bc->port,"BC_STATE_CHANGE: l3id:%x from:%s to:%s\n",
+ bc->l3_id,
+ bc_state2str(bc->bc_state),
+ bc_state2str(state) );
+
+ switch (state) {
+ case BCHAN_ACTIVATED:
+ if (bc->next_bc_state == BCHAN_BRIDGED) {
+ misdn_join_conf(bc, bc->conf_id);
+ bc->next_bc_state = BCHAN_EMPTY;
+ return;
+ }
+ default:
+ bc->bc_state=state;
+ break;
+ }
+}
+
+static void bc_next_state_change(struct misdn_bchannel *bc, enum bchannel_state state)
+{
+ cb_log(5,bc->port,"BC_NEXT_STATE_CHANGE: from:%s to:%s\n",
+ bc_state2str(bc->next_bc_state),
+ bc_state2str(state) );
+
+ bc->next_bc_state=state;
+}
+
+
+static void empty_bc(struct misdn_bchannel *bc)
+{
+ bc->dummy=0;
+
+ bc->bframe_len=0;
+
+ bc->cw= 0;
+
+ bc->dec=0;
+ bc->channel = 0;
+
+ bc->sending_complete = 0;
+
+ bc->restart_channel=0;
+
+ bc->conf_id = 0;
+
+ bc->need_more_infos = 0;
+
+ bc->send_dtmf=0;
+ bc->nodsp=0;
+ bc->nojitter=0;
+
+ bc->time_usec=0;
+
+ bc->rxgain=0;
+ bc->txgain=0;
+
+ bc->crypt=0;
+ bc->curptx=0; bc->curprx=0;
+
+ bc->crypt_key[0] = 0;
+
+ bc->generate_tone=0;
+ bc->tone_cnt=0;
+
+ bc->dnumplan=NUMPLAN_UNKNOWN;
+ bc->onumplan=NUMPLAN_UNKNOWN;
+ bc->rnumplan=NUMPLAN_UNKNOWN;
+ bc->cpnnumplan=NUMPLAN_UNKNOWN;
+
+
+ bc->active = 0;
+
+ bc->early_bconnect = 1;
+
+#ifdef MISDN_1_2
+ *bc->pipeline = 0;
+#else
+ bc->ec_enable = 0;
+ bc->ec_deftaps = 128;
+#endif
+
+ bc->AOCD_need_export = 0;
+
+ bc->orig=0;
+
+ bc->cause=16;
+ bc->out_cause=16;
+ bc->pres=0 ; /* screened */
+
+ bc->evq=EVENT_NOTHING;
+
+ bc->progress_coding=0;
+ bc->progress_location=0;
+ bc->progress_indicator=0;
+
+/** Set Default Bearer Caps **/
+ bc->capability=INFO_CAPABILITY_SPEECH;
+ bc->law=INFO_CODEC_ALAW;
+ bc->mode=0;
+ bc->rate=0x10;
+ bc->user1=0;
+ bc->urate=0;
+
+ bc->hdlc=0;
+
+
+ bc->info_dad[0] = 0;
+ bc->display[0] = 0;
+ bc->infos_pending[0] = 0;
+ bc->cad[0] = 0;
+ bc->oad[0] = 0;
+ bc->dad[0] = 0;
+ bc->rad[0] = 0;
+ bc->orig_dad[0] = 0;
+ bc->uu[0]=0;
+ bc->uulen=0;
+
+ bc->fac_in.Function = Fac_None;
+ bc->fac_out.Function = Fac_None;
+
+ bc->te_choose_channel = 0;
+ bc->channel_found= 0;
+}
+
+
+static int clean_up_bc(struct misdn_bchannel *bc)
+{
+ int ret=0;
+ unsigned char buff[32];
+ struct misdn_stack * stack;
+
+ cb_log(3, bc?bc->port:0, "$$$ CLEANUP CALLED pid:%d\n", bc?bc->pid:-1);
+
+ if (!bc ) return -1;
+ stack=get_stack_by_bc(bc);
+
+ if (!stack) return -1;
+
+ switch (bc->bc_state ) {
+ case BCHAN_CLEANED:
+ cb_log(5, stack->port, "$$$ Already cleaned up bc with stid :%x\n", bc->b_stid);
+ return -1;
+
+ default:
+ break;
+ }
+
+ cb_log(2, stack->port, "$$$ Cleaning up bc with stid :%x pid:%d\n", bc->b_stid, bc->pid);
+
+ manager_ec_disable(bc);
+
+ manager_bchannel_deactivate(bc);
+
+ mISDN_write_frame(stack->midev, buff, bc->layer_id|FLG_MSG_TARGET|FLG_MSG_DOWN, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+
+ bc->b_stid = 0;
+ bc_state_change(bc, BCHAN_CLEANED);
+
+ return ret;
+}
+
+
+
+static void clear_l3(struct misdn_stack *stack)
+{
+ int i;
+
+ for (i=0; i<=stack->b_num; i++) {
+ if (global_state == MISDN_INITIALIZED) {
+ cb_event(EVENT_CLEANUP, &stack->bc[i], NULL);
+ empty_chan_in_stack(stack,i+1);
+ empty_bc(&stack->bc[i]);
+ clean_up_bc(&stack->bc[i]);
+ stack->bc[i].in_use = 0;
+ }
+
+ }
+}
+
+static int newteid=0;
+
+#define MAXPROCS 0x100
+
+static int misdn_lib_get_l1_down(struct misdn_stack *stack)
+{
+ /* Pull Up L1 */
+ iframe_t act;
+ act.prim = PH_DEACTIVATE | REQUEST;
+ act.addr = stack->lower_id|FLG_MSG_DOWN;
+ act.dinfo = 0;
+ act.len = 0;
+
+ cb_log(1, stack->port, "SENDING PH_DEACTIVATE | REQ\n");
+ return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
+}
+
+
+static int misdn_lib_get_l2_down(struct misdn_stack *stack)
+{
+
+ if (stack->ptp && (stack->nt) ) {
+ msg_t *dmsg;
+ /* L2 */
+ dmsg = create_l2msg(DL_RELEASE| REQUEST, 0, 0);
+
+ if (stack->nst.manager_l3(&stack->nst, dmsg))
+ free_msg(dmsg);
+
+ } else {
+ iframe_t act;
+
+ act.prim = DL_RELEASE| REQUEST;
+ act.addr = (stack->upper_id |FLG_MSG_DOWN) ;
+
+ act.dinfo = 0;
+ act.len = 0;
+ return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
+ }
+
+ return 0;
+}
+
+
+static int misdn_lib_get_l1_up(struct misdn_stack *stack)
+{
+ /* Pull Up L1 */
+ iframe_t act;
+ act.prim = PH_ACTIVATE | REQUEST;
+ act.addr = (stack->upper_id | FLG_MSG_DOWN) ;
+
+
+ act.dinfo = 0;
+ act.len = 0;
+
+ return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
+
+}
+
+int misdn_lib_get_l2_up(struct misdn_stack *stack)
+{
+
+ if (stack->ptp && (stack->nt) ) {
+ msg_t *dmsg;
+ /* L2 */
+ dmsg = create_l2msg(DL_ESTABLISH | REQUEST, 0, 0);
+
+ if (stack->nst.manager_l3(&stack->nst, dmsg))
+ free_msg(dmsg);
+
+ } else {
+ iframe_t act;
+
+ act.prim = DL_ESTABLISH | REQUEST;
+ act.addr = (stack->upper_id |FLG_MSG_DOWN) ;
+
+ act.dinfo = 0;
+ act.len = 0;
+ return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
+ }
+
+ return 0;
+}
+
+#if 0
+static int misdn_lib_get_l2_te_ptp_up(struct misdn_stack *stack)
+{
+ iframe_t act;
+
+ act.prim = DL_ESTABLISH | REQUEST;
+ act.addr = (stack->upper_id & ~LAYER_ID_MASK) | 3 | FLG_MSG_DOWN;
+
+ act.dinfo = 0;
+ act.len = 0;
+ return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
+ return 0;
+}
+#endif
+
+static int misdn_lib_get_short_status(struct misdn_stack *stack)
+{
+ iframe_t act;
+
+
+ act.prim = MGR_SHORTSTATUS | REQUEST;
+
+ act.addr = (stack->upper_id | MSG_BROADCAST) ;
+
+ act.dinfo = SSTATUS_BROADCAST_BIT | SSTATUS_ALL;
+
+ act.len = 0;
+ return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC);
+}
+
+
+
+static int create_process (int midev, struct misdn_bchannel *bc) {
+ iframe_t ncr;
+ int l3_id;
+ int i;
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ if (stack->nt) {
+ if (find_free_chan_in_stack(stack, bc, bc->channel_preselected?bc->channel:0, 0)<0) return -1;
+ cb_log(4,stack->port, " --> found channel: %d\n",bc->channel);
+
+ for (i=0; i <= MAXPROCS; i++)
+ if (stack->procids[i]==0) break;
+
+ if (i== MAXPROCS) {
+ cb_log(0, stack->port, "Couldnt Create New ProcId.\n");
+ return -1;
+ }
+ stack->procids[i]=1;
+
+ l3_id = 0xff00 | i;
+
+ ncr.prim = CC_NEW_CR | REQUEST;
+
+ ncr.addr = (stack->upper_id | FLG_MSG_DOWN) ;
+
+ ncr.dinfo = l3_id;
+ ncr.len = 0;
+
+ bc->l3_id = l3_id;
+ cb_log(3, stack->port, " --> new_l3id %x\n",l3_id);
+
+ } else {
+ if (stack->ptp || bc->te_choose_channel) {
+ /* we know exactly which channels are in use */
+ if (find_free_chan_in_stack(stack, bc, bc->channel_preselected?bc->channel:0, bc->dec)<0) return -1;
+ cb_log(2,stack->port, " --> found channel: %d\n",bc->channel);
+ } else {
+ /* other phones could have made a call also on this port (ptmp) */
+ bc->channel=0xff;
+ }
+
+
+ /* if we are in te-mode, we need to create a process first */
+ if (newteid++ > 0xffff)
+ newteid = 0x0001;
+
+ l3_id = (entity<<16) | newteid;
+ /* preparing message */
+ ncr.prim = CC_NEW_CR | REQUEST;
+
+ ncr.addr = (stack->upper_id | FLG_MSG_DOWN) ;
+
+ ncr.dinfo =l3_id;
+ ncr.len = 0;
+ /* send message */
+
+ bc->l3_id = l3_id;
+ cb_log(3, stack->port, "--> new_l3id %x\n",l3_id);
+
+ mISDN_write(midev, &ncr, mISDN_HEADER_LEN+ncr.len, TIMEOUT_1SEC);
+ }
+
+ return l3_id;
+}
+
+
+void misdn_lib_setup_bc(struct misdn_bchannel *bc)
+{
+ clean_up_bc(bc);
+ setup_bc(bc);
+}
+
+
+int setup_bc(struct misdn_bchannel *bc)
+{
+ unsigned char buff[1025];
+ int midev, channel, b_stid, i;
+ mISDN_pid_t pid;
+ int ret;
+
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ if (!stack) {
+ cb_log(0, bc->port, "setup_bc: NO STACK FOUND!!\n");
+ return -1;
+ }
+
+ midev = stack->midev;
+ channel = bc->channel - 1 - (bc->channel > 16);
+ b_stid = stack->b_stids[channel >= 0 ? channel : 0];
+
+ switch (bc->bc_state) {
+ case BCHAN_CLEANED:
+ break;
+ default:
+ cb_log(4, stack->port, "$$$ bc already upsetted stid :%x (state:%s)\n", b_stid, bc_state2str(bc->bc_state) );
+ return -1;
+ }
+
+ cb_log(5, stack->port, "$$$ Setting up bc with stid :%x\n", b_stid);
+
+ /*check if the b_stid is alread initialized*/
+ for (i=0; i <= stack->b_num; i++) {
+ if (stack->bc[i].b_stid == b_stid) {
+ cb_log(0, bc->port, "setup_bc: b_stid:%x already in use !!!\n", b_stid);
+ return -1;
+ }
+ }
+
+ if (b_stid <= 0) {
+ cb_log(0, stack->port," -- Stid <=0 at the moment in channel:%d\n",channel);
+
+ bc_state_change(bc,BCHAN_ERROR);
+ return 1;
+ }
+
+ bc->b_stid = b_stid;
+
+ {
+ layer_info_t li;
+ memset(&li, 0, sizeof(li));
+
+ li.object_id = -1;
+ li.extentions = 0;
+
+ li.st = bc->b_stid; /* given idx */
+
+
+#define MISDN_DSP
+#ifndef MISDN_DSP
+ bc->nodsp=1;
+#endif
+ if ( bc->hdlc || bc->nodsp) {
+ cb_log(4, stack->port,"setup_bc: without dsp\n");
+ {
+ int l = sizeof(li.name);
+ strncpy(li.name, "B L3", l);
+ li.name[l-1] = 0;
+ }
+ li.pid.layermask = ISDN_LAYER((3));
+ li.pid.protocol[3] = ISDN_PID_L3_B_USER;
+
+ bc->layer=3;
+ } else {
+ cb_log(4, stack->port,"setup_bc: with dsp\n");
+ {
+ int l = sizeof(li.name);
+ strncpy(li.name, "B L4", l);
+ li.name[l-1] = 0;
+ }
+ li.pid.layermask = ISDN_LAYER((4));
+ li.pid.protocol[4] = ISDN_PID_L4_B_USER
+;
+ bc->layer=4;
+
+ }
+
+ ret = mISDN_new_layer(midev, &li);
+ if (ret ) {
+ cb_log(0, stack->port,"New Layer Err: %d %s\n",ret,strerror(errno));
+
+ bc_state_change(bc,BCHAN_ERROR);
+ return(-EINVAL);
+ }
+
+ bc->layer_id = li.id;
+ }
+
+ memset(&pid, 0, sizeof(pid));
+
+
+
+ cb_log(4, stack->port," --> Channel is %d\n", bc->channel);
+
+ if (bc->nodsp) {
+ cb_log(2, stack->port," --> TRANSPARENT Mode (no DSP, no HDLC)\n");
+ pid.protocol[1] = ISDN_PID_L1_B_64TRANS;
+ pid.protocol[2] = ISDN_PID_L2_B_TRANS;
+ pid.protocol[3] = ISDN_PID_L3_B_USER;
+ pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3));
+
+ } else if ( bc->hdlc ) {
+ cb_log(2, stack->port," --> HDLC Mode\n");
+ pid.protocol[1] = ISDN_PID_L1_B_64HDLC ;
+ pid.protocol[2] = ISDN_PID_L2_B_TRANS ;
+ pid.protocol[3] = ISDN_PID_L3_B_USER;
+ pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) ;
+ } else {
+ cb_log(2, stack->port," --> TRANSPARENT Mode\n");
+ pid.protocol[1] = ISDN_PID_L1_B_64TRANS;
+ pid.protocol[2] = ISDN_PID_L2_B_TRANS;
+ pid.protocol[3] = ISDN_PID_L3_B_DSP;
+ pid.protocol[4] = ISDN_PID_L4_B_USER;
+ pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) | ISDN_LAYER((4));
+
+ }
+
+ ret = mISDN_set_stack(midev, bc->b_stid, &pid);
+
+ if (ret){
+ cb_log(0, stack->port,"$$$ Set Stack Err: %d %s\n",ret,strerror(errno));
+
+ mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+
+ bc_state_change(bc,BCHAN_ERROR);
+ cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data);
+ return(-EINVAL);
+ }
+
+ ret = mISDN_get_setstack_ind(midev, bc->layer_id);
+
+ if (ret) {
+ cb_log(0, stack->port,"$$$ Set StackIND Err: %d %s\n",ret,strerror(errno));
+ mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+
+ bc_state_change(bc,BCHAN_ERROR);
+ cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data);
+ return(-EINVAL);
+ }
+
+ ret = mISDN_get_layerid(midev, bc->b_stid, bc->layer) ;
+
+ bc->addr = ret>0? ret : 0;
+
+ if (!bc->addr) {
+ cb_log(0, stack->port,"$$$ Get Layerid Err: %d %s\n",ret,strerror(errno));
+ mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+
+ bc_state_change(bc,BCHAN_ERROR);
+ cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data);
+ return (-EINVAL);
+ }
+
+ manager_bchannel_activate(bc);
+
+ bc_state_change(bc,BCHAN_ACTIVATED);
+
+ return 0;
+}
+
+
+
+/** IFACE **/
+int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int midev, int port, int bidx, char *msn, int firsttime)
+{
+ unsigned char buff[1025] = "";
+ iframe_t *frm = (iframe_t *)buff;
+ int ret;
+
+ if (!bc) return -1;
+
+ cb_log(8, port, "Init.BC %d.\n",bidx);
+
+ memset(bc, 0,sizeof(struct misdn_bchannel));
+
+ bc->send_lock=malloc(sizeof(struct send_lock));
+
+ pthread_mutex_init(&bc->send_lock->lock, NULL);
+
+ if (msn) {
+ int l = sizeof(bc->msn);
+ strncpy(bc->msn,msn, l);
+ bc->msn[l-1] = 0;
+ }
+
+
+ empty_bc(bc);
+ bc_state_change(bc, BCHAN_CLEANED);
+
+ bc->port=stack->port;
+ bc->nt=stack->nt?1:0;
+ bc->pri=stack->pri;
+
+ {
+ ibuffer_t* ibuf= init_ibuffer(MISDN_IBUF_SIZE);
+
+ if (!ibuf) return -1;
+
+ clear_ibuffer( ibuf);
+
+ ibuf->rsem=malloc(sizeof(sem_t));
+
+ bc->astbuf=ibuf;
+
+ if (sem_init(ibuf->rsem,1,0)<0)
+ sem_init(ibuf->rsem,0,0);
+
+ }
+
+ {
+ stack_info_t *stinf;
+ ret = mISDN_get_stack_info(midev, stack->port, buff, sizeof(buff));
+ if (ret < 0) {
+ cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ stinf = (stack_info_t *)&frm->data.p;
+
+ cb_log(8, port, " --> Child %x\n",stinf->child[bidx]);
+ }
+
+ return 0;
+}
+
+
+
+struct misdn_stack* stack_init( int midev, int port, int ptp )
+{
+ int ret;
+ unsigned char buff[1025];
+ iframe_t *frm = (iframe_t *)buff;
+ stack_info_t *stinf;
+ int i;
+ layer_info_t li;
+
+ struct misdn_stack *stack = malloc(sizeof(struct misdn_stack));
+ if (!stack ) return NULL;
+
+
+ cb_log(8, port, "Init. Stack.\n");
+
+ memset(stack,0,sizeof(struct misdn_stack));
+
+ for (i=0; i<MAX_BCHANS + 1; i++ ) stack->channels[i]=0;
+
+ stack->port=port;
+ stack->midev=midev;
+ stack->ptp=ptp;
+
+ stack->holding=NULL;
+ stack->pri=0;
+
+ msg_queue_init(&stack->downqueue);
+ msg_queue_init(&stack->upqueue);
+
+ /* query port's requirements */
+ ret = mISDN_get_stack_info(midev, port, buff, sizeof(buff));
+ if (ret < 0) {
+ cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret);
+ return(NULL);
+ }
+
+ stinf = (stack_info_t *)&frm->data.p;
+
+ stack->d_stid = stinf->id;
+ stack->b_num = stinf->childcnt;
+
+ for (i=0; i<=stinf->childcnt; i++)
+ stack->b_stids[i] = stinf->child[i];
+
+ switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) {
+ case ISDN_PID_L0_TE_S0:
+ stack->nt=0;
+ break;
+ case ISDN_PID_L0_NT_S0:
+ cb_log(8, port, "NT Stack\n");
+
+ stack->nt=1;
+ break;
+ case ISDN_PID_L0_TE_E1:
+ cb_log(8, port, "TE S2M Stack\n");
+ stack->nt=0;
+ stack->pri=1;
+ break;
+ case ISDN_PID_L0_NT_E1:
+ cb_log(8, port, "TE S2M Stack\n");
+ stack->nt=1;
+ stack->pri=1;
+
+ break;
+ default:
+ cb_log(0, port, "this is a unknown port type 0x%08x\n", stinf->pid.protocol[0]);
+
+ }
+
+ if (!stack->nt) {
+ if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP ) {
+ stack->ptp = 1;
+ } else {
+ stack->ptp = 0;
+ }
+ }
+
+ {
+ int ret;
+ int nt=stack->nt;
+
+ cb_log(8, port, "Init. Stack.\n");
+
+ memset(&li, 0, sizeof(li));
+ {
+ int l = sizeof(li.name);
+ strncpy(li.name,nt?"net l2":"user l4", l);
+ li.name[l-1] = 0;
+ }
+ li.object_id = -1;
+ li.extentions = 0;
+ li.pid.protocol[nt?2:4] = nt?ISDN_PID_L2_LAPD_NET:ISDN_PID_L4_CAPI20;
+ li.pid.layermask = ISDN_LAYER((nt?2:4));
+ li.st = stack->d_stid;
+
+
+ ret = mISDN_new_layer(midev, &li);
+ if (ret) {
+ cb_log(0, port, "%s: Cannot add layer %d to this port.\n", __FUNCTION__, nt?2:4);
+ return(NULL);
+ }
+
+
+ stack->upper_id = li.id;
+ ret = mISDN_register_layer(midev, stack->d_stid, stack->upper_id);
+ if (ret)
+ {
+ cb_log(0,port,"Cannot register layer %d of this port.\n", nt?2:4);
+ return(NULL);
+ }
+
+ stack->lower_id = mISDN_get_layerid(midev, stack->d_stid, nt?1:3);
+ if (stack->lower_id < 0) {
+ cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, nt?1:3);
+ return(NULL);
+ }
+
+ stack->upper_id = mISDN_get_layerid(midev, stack->d_stid, nt?2:4);
+ if (stack->upper_id < 0) {
+ cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, 2);
+ return(NULL);
+ }
+
+ cb_log(8, port, "NT Stacks upper_id %x\n",stack->upper_id);
+
+
+ /* create nst (nt-mode only) */
+ if (nt) {
+
+ memset(&stack->nst, 0, sizeof(net_stack_t));
+ memset(&stack->mgr, 0, sizeof(manager_t));
+
+ stack->mgr.nst = &stack->nst;
+ stack->nst.manager = &stack->mgr;
+
+ stack->nst.l3_manager = handle_event_nt;
+ stack->nst.device = midev;
+ stack->nst.cardnr = port;
+ stack->nst.d_stid = stack->d_stid;
+
+ stack->nst.feature = FEATURE_NET_HOLD;
+ if (stack->ptp)
+ stack->nst.feature |= FEATURE_NET_PTP;
+ if (stack->pri)
+ stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID;
+
+ stack->nst.l1_id = stack->lower_id;
+ stack->nst.l2_id = stack->upper_id;
+
+ msg_queue_init(&stack->nst.down_queue);
+
+ Isdnl2Init(&stack->nst);
+ Isdnl3Init(&stack->nst);
+
+ }
+
+ if (!stack->nt) {
+ /*assume L1 is up, we'll get DEACTIVATES soon, for non
+ * up L1s*/
+ stack->l1link=0;
+ }
+ stack->l1link=0;
+ stack->l2link=0;
+#if 0
+ if (!stack->nt) {
+ misdn_lib_get_short_status(stack);
+ } else {
+ misdn_lib_get_l1_up(stack);
+ if (!stack->ptp) misdn_lib_get_l1_up(stack);
+ misdn_lib_get_l2_up(stack);
+ }
+#endif
+
+ misdn_lib_get_short_status(stack);
+ misdn_lib_get_l1_up(stack);
+ misdn_lib_get_l2_up(stack);
+
+ }
+
+ cb_log(8,0,"stack_init: port:%d lowerId:%x upperId:%x\n",stack->port,stack->lower_id, stack->upper_id);
+
+ return stack;
+}
+
+
+void stack_destroy(struct misdn_stack* stack)
+{
+ char buf[1024];
+ if (!stack) return;
+
+ if (stack->nt) {
+ cleanup_Isdnl2(&stack->nst);
+ cleanup_Isdnl3(&stack->nst);
+ }
+
+ if (stack->lower_id)
+ mISDN_write_frame(stack->midev, buf, stack->lower_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+
+ if (stack->upper_id)
+ mISDN_write_frame(stack->midev, buf, stack->upper_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+}
+
+
+static struct misdn_stack * find_stack_by_addr(int addr)
+{
+ struct misdn_stack *stack;
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next) {
+ if ( (stack->upper_id&STACK_ID_MASK) == (addr&STACK_ID_MASK)) return stack;
+
+ }
+
+ return NULL;
+}
+
+
+static struct misdn_stack * find_stack_by_port(int port)
+{
+ struct misdn_stack *stack;
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next)
+ if (stack->port == port) return stack;
+
+ return NULL;
+}
+
+static struct misdn_stack * find_stack_by_mgr(manager_t* mgr_nt)
+{
+ struct misdn_stack *stack;
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next)
+ if ( &stack->mgr == mgr_nt) return stack;
+
+ return NULL;
+}
+
+static struct misdn_bchannel *find_bc_by_masked_l3id(struct misdn_stack *stack, unsigned long l3id, unsigned long mask)
+{
+ int i;
+ for (i=0; i<=stack->b_num; i++) {
+ if ( (stack->bc[i].l3_id & mask) == (l3id & mask)) return &stack->bc[i] ;
+ }
+ return stack_holder_find(stack,l3id);
+}
+
+
+struct misdn_bchannel *find_bc_by_l3id(struct misdn_stack *stack, unsigned long l3id)
+{
+ int i;
+ for (i=0; i<=stack->b_num; i++) {
+ if (stack->bc[i].l3_id == l3id) return &stack->bc[i] ;
+ }
+ return stack_holder_find(stack,l3id);
+}
+
+static struct misdn_bchannel *find_bc_holded(struct misdn_stack *stack)
+{
+ int i;
+ for (i=0; i<=stack->b_num; i++) {
+ if (stack->bc[i].holded ) return &stack->bc[i] ;
+ }
+ return NULL;
+}
+
+
+static struct misdn_bchannel *find_bc_by_addr(unsigned long addr)
+{
+ struct misdn_stack* stack;
+ int i;
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next) {
+ for (i=0; i<=stack->b_num; i++) {
+ if ( (stack->bc[i].addr&STACK_ID_MASK)==(addr&STACK_ID_MASK) || stack->bc[i].layer_id== addr ) {
+ return &stack->bc[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+struct misdn_bchannel *find_bc_by_confid(unsigned long confid)
+{
+ struct misdn_stack* stack;
+ int i;
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next) {
+ for (i=0; i<=stack->b_num; i++) {
+ if ( stack->bc[i].conf_id==confid ) {
+ return &stack->bc[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static struct misdn_bchannel *find_bc_by_channel(int port, int channel)
+{
+ struct misdn_stack* stack=find_stack_by_port(port);
+ int i;
+
+ if (!stack) return NULL;
+
+ for (i=0; i<=stack->b_num; i++) {
+ if ( stack->bc[i].channel== channel ) {
+ return &stack->bc[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+
+
+
+static int handle_event ( struct misdn_bchannel *bc, enum event_e event, iframe_t *frm)
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ if (!stack->nt) {
+
+ switch (event) {
+
+ case EVENT_CONNECT_ACKNOWLEDGE:
+ setup_bc(bc);
+
+ if ( *bc->crypt_key ) {
+ cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad);
+ manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) );
+ }
+
+ if (misdn_cap_is_speech(bc->capability)) {
+ if ( !bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0);
+ manager_ec_enable(bc);
+
+ if ( bc->txgain != 0 ) {
+ cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain);
+ manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain);
+ }
+ if ( bc->rxgain != 0 ) {
+ cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain);
+ manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain);
+ }
+ }
+
+ break;
+ case EVENT_CONNECT:
+
+ if ( *bc->crypt_key ) {
+ cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad);
+ manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) );
+ }
+ case EVENT_ALERTING:
+ case EVENT_PROGRESS:
+ case EVENT_PROCEEDING:
+ case EVENT_SETUP_ACKNOWLEDGE:
+ case EVENT_SETUP:
+ {
+ if (bc->channel == 0xff || bc->channel<=0)
+ bc->channel=0;
+
+ if (find_free_chan_in_stack(stack, bc, bc->channel, 0)<0){
+ if (!stack->pri && !stack->ptp) {
+ bc->cw=1;
+ break;
+ }
+
+ cb_log(0, stack->port, "Any Channel Requested, but we have no more!!\n");
+ misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE);
+ return -1;
+ }
+ }
+
+ setup_bc(bc);
+ break;
+
+ case EVENT_RELEASE_COMPLETE:
+ case EVENT_RELEASE:
+ break;
+ default:
+ break;
+ }
+ } else { /** NT MODE **/
+
+ }
+ return 0;
+}
+
+static int handle_cr ( struct misdn_stack *stack, iframe_t *frm)
+{
+ struct misdn_bchannel *bc;
+
+ if (!stack) return -1;
+
+ switch (frm->prim) {
+ case CC_NEW_CR|INDICATION:
+ cb_log(7, stack->port, " --> lib: NEW_CR Ind with l3id:%x on this port.\n",frm->dinfo);
+
+ bc = misdn_lib_get_free_bc(stack->port, 0, 1, 0);
+ if (!bc) {
+ cb_log(0, stack->port, " --> !! lib: No free channel!\n");
+ return -1;
+ }
+
+ cb_log(7, stack->port, " --> new_process: New L3Id: %x\n",frm->dinfo);
+ bc->l3_id=frm->dinfo;
+ return 1;
+ case CC_NEW_CR|CONFIRM:
+ return 1;
+ case CC_NEW_CR|REQUEST:
+ return 1;
+ case CC_RELEASE_CR|REQUEST:
+ return 1;
+ case CC_RELEASE_CR|CONFIRM:
+ break;
+ case CC_RELEASE_CR|INDICATION:
+ cb_log(4, stack->port, " --> lib: RELEASE_CR Ind with l3id:%x\n",frm->dinfo);
+ {
+ struct misdn_bchannel *bc=find_bc_by_l3id(stack, frm->dinfo);
+ struct misdn_bchannel dummybc;
+
+ if (!bc) {
+ cb_log(4, stack->port, " --> Didn't found BC so temporarly creating dummy BC (l3id:%x) on this port.\n", frm->dinfo);
+ misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0);
+
+ bc=&dummybc;
+ }
+
+ if (bc) {
+ int channel = bc->channel;
+ cb_log(4, stack->port, " --> lib: CLEANING UP l3id: %x\n",frm->dinfo);
+
+ /*bc->pid = 0;*/
+ bc->need_disconnect=0;
+ bc->need_release=0;
+ bc->need_release_complete=0;
+
+ cb_event(EVENT_CLEANUP, bc, glob_mgr->user_data);
+
+ empty_bc(bc);
+ clean_up_bc(bc);
+
+ if (channel>0)
+ empty_chan_in_stack(stack,channel);
+ bc->in_use=0;
+
+ dump_chan_list(stack);
+
+ if (bc->stack_holder) {
+ cb_log(4,stack->port, "REMOVEING Holder\n");
+ stack_holder_remove( stack, bc);
+ free(bc);
+ }
+ }
+ else {
+ if (stack->nt)
+ cb_log(4, stack->port, "BC with dinfo: %x not found.. (prim was %x and addr %x)\n",frm->dinfo, frm->prim, frm->addr);
+ }
+
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+/*Emptys bc if it's reserved (no SETUP out yet)*/
+void misdn_lib_release(struct misdn_bchannel *bc)
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ if (!stack) {
+ cb_log(1,0,"misdn_release: No Stack found\n");
+ return;
+ }
+
+ if (bc->channel>0)
+ empty_chan_in_stack(stack,bc->channel);
+
+ empty_bc(bc);
+ clean_up_bc(bc);
+ bc->in_use=0;
+}
+
+
+
+
+int misdn_lib_get_port_up (int port)
+{ /* Pull Up L1 */
+ struct misdn_stack *stack;
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next) {
+
+ if (stack->port == port) {
+
+ if (!stack->l1link)
+ misdn_lib_get_l1_up(stack);
+ if (!stack->l2link)
+ misdn_lib_get_l2_up(stack);
+
+ return 0;
+ }
+ }
+ return 0;
+}
+
+
+int misdn_lib_get_port_down (int port)
+{ /* Pull Down L1 */
+ struct misdn_stack *stack;
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next) {
+ if (stack->port == port) {
+ if (stack->l2link)
+ misdn_lib_get_l2_down(stack);
+ misdn_lib_get_l1_down(stack);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int misdn_lib_port_up(int port, int check)
+{
+ struct misdn_stack *stack;
+
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next) {
+
+ if (stack->port == port) {
+
+ if (stack->blocked) {
+ cb_log(0,port, "Port Blocked:%d L2:%d L1:%d\n", stack->blocked, stack->l2link, stack->l1link);
+ return -1;
+ }
+
+ if (stack->ptp ) {
+
+ if (stack->l1link && stack->l2link) {
+ return 1;
+ } else {
+ cb_log(1,port, "Port Down L2:%d L1:%d\n",
+ stack->l2link, stack->l1link);
+ return 0;
+ }
+ } else {
+ if ( !check || stack->l1link )
+ return 1;
+ else {
+ cb_log(1,port, "Port down PMP\n");
+ return 0;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh)
+{
+ struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo);
+ struct misdn_bchannel dummybc;
+ iframe_t frm; /* fake te frm to remove callref from global callreflist */
+ frm.dinfo = hh->dinfo;
+
+ frm.addr=stack->upper_id | FLG_MSG_DOWN;
+
+ frm.prim = CC_RELEASE_CR|INDICATION;
+ cb_log(4, stack->port, " --> CC_RELEASE_CR: Faking Realease_cr for %x l3id:%x\n",frm.addr, frm.dinfo);
+ /** removing procid **/
+ if (!bc) {
+ cb_log(4, stack->port, " --> Didn't found BC so temporarly creating dummy BC (l3id:%x) on this port.\n", hh->dinfo);
+ misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0);
+ bc=&dummybc;
+ }
+
+ if (bc) {
+ if ( (bc->l3_id & 0xff00) == 0xff00) {
+ cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", bc->l3_id&0xff);
+ stack->procids[bc->l3_id&0xff] = 0 ;
+ }
+ }
+ else cb_log(0, stack->port, "Couldnt find BC so I couldnt remove the Process!!!! this is a bad port.\n");
+
+ if (handle_cr(stack, &frm)<0) {
+ }
+
+ return 0 ;
+}
+
+int
+handle_event_nt(void *dat, void *arg)
+{
+ manager_t *mgr = (manager_t *)dat;
+ msg_t *msg = (msg_t *)arg;
+ mISDNuser_head_t *hh;
+ int reject=0;
+
+ struct misdn_stack *stack=find_stack_by_mgr(mgr);
+ int port;
+
+ if (!msg || !mgr)
+ return(-EINVAL);
+
+ hh=(mISDNuser_head_t*)msg->data;
+ port=stack->port;
+
+ cb_log(5, stack->port, " --> lib: prim %x dinfo %x\n",hh->prim, hh->dinfo);
+ {
+ switch(hh->prim){
+ case CC_RETRIEVE|INDICATION:
+ {
+ struct misdn_bchannel *bc, *hold_bc;
+
+ iframe_t frm; /* fake te frm to add callref to global callreflist */
+ frm.dinfo = hh->dinfo;
+
+ frm.addr=stack->upper_id | FLG_MSG_DOWN;
+
+ frm.prim = CC_NEW_CR|INDICATION;
+
+ if (handle_cr( stack, &frm)< 0) {
+ msg_t *dmsg;
+ cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo);
+ dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1);
+ stack->nst.manager_l3(&stack->nst, dmsg);
+ free_msg(msg);
+ return 0;
+ }
+
+ bc = find_bc_by_l3id(stack, hh->dinfo);
+ hold_bc = stack_holder_find(stack, bc->l3_id);
+ cb_log(4, stack->port, "bc_l3id:%x holded_bc_l3id:%x\n",bc->l3_id, hold_bc->l3_id);
+
+ if (hold_bc) {
+ cb_log(4, stack->port, "REMOVEING Holder\n");
+
+ /*swap the backup to our new channel back*/
+ stack_holder_remove(stack, hold_bc);
+ memcpy(bc, hold_bc, sizeof(*bc));
+ free(hold_bc);
+
+ bc->holded=0;
+ bc->b_stid=0;
+ }
+
+ }
+
+ break;
+
+ case CC_SETUP|CONFIRM:
+ {
+ struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo);
+ int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE));
+ cb_log(4, stack->port, " --> lib: Event_ind:SETUP CONFIRM [NT] : new L3ID is %x\n",l3id );
+
+ if (!bc) { cb_log(4, stack->port, "Bc Not found (after SETUP CONFIRM)\n"); return 0; }
+ cb_log (2,bc->port,"I IND :CC_SETUP|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id);
+ bc->l3_id=l3id;
+ cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data);
+ }
+ free_msg(msg);
+ return 0;
+
+ case CC_SETUP|INDICATION:
+ {
+ struct misdn_bchannel* bc=misdn_lib_get_free_bc(stack->port, 0, 1, 0);
+ if (!bc)
+ ERR_NO_CHANNEL:
+ {
+ msg_t *dmsg;
+ cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo);
+ dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1);
+ stack->nst.manager_l3(&stack->nst, dmsg);
+ free_msg(msg);
+ return 0;
+ }
+
+ cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo);
+ bc->l3_id=hh->dinfo;
+ }
+ break;
+
+ case CC_CONNECT_ACKNOWLEDGE|INDICATION:
+ break;
+
+ case CC_ALERTING|INDICATION:
+ case CC_PROCEEDING|INDICATION:
+ case CC_SETUP_ACKNOWLEDGE|INDICATION:
+ if(!stack->ptp) break;
+ case CC_CONNECT|INDICATION:
+ break;
+ case CC_DISCONNECT|INDICATION:
+ {
+ struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo);
+ if (!bc) {
+ bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000);
+ if (bc) {
+ int myprocid=bc->l3_id&0x0000ffff;
+ hh->dinfo=(hh->dinfo&0xffff0000)|myprocid;
+ cb_log(3,stack->port,"Reject dinfo: %x cause:%d\n",hh->dinfo,bc->cause);
+ reject=1;
+ }
+ }
+ }
+ break;
+
+ case CC_FACILITY|INDICATION:
+ {
+ struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo);
+ if (!bc) {
+ bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000);
+ if (bc) {
+ int myprocid=bc->l3_id&0x0000ffff;
+ hh->dinfo=(hh->dinfo&0xffff0000)|myprocid;
+ cb_log(4,bc->port,"Repaired reject Bug, new dinfo: %x\n",hh->dinfo);
+ }
+ }
+ }
+ break;
+
+ case CC_RELEASE_COMPLETE|INDICATION:
+ break;
+
+ case CC_SUSPEND|INDICATION:
+ {
+ msg_t *dmsg;
+ cb_log(4, stack->port, " --> Got Suspend, sending Reject for now\n");
+ dmsg = create_l3msg(CC_SUSPEND_REJECT | REQUEST,MT_SUSPEND_REJECT, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1);
+ stack->nst.manager_l3(&stack->nst, dmsg);
+ free_msg(msg);
+ return 0;
+ }
+ break;
+ case CC_RESUME|INDICATION:
+ break;
+
+ case CC_RELEASE|CONFIRM:
+ {
+ struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo);
+
+ if (bc) {
+ cb_log(1, stack->port, "CC_RELEASE|CONFIRM (l3id:%x), sending RELEASE_COMPLETE\n", hh->dinfo);
+ misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE);
+ }
+ }
+ break;
+
+ case CC_RELEASE|INDICATION:
+ break;
+
+ case CC_RELEASE_CR|INDICATION:
+ release_cr(stack, hh);
+ free_msg(msg);
+ return 0 ;
+ break;
+
+ case CC_NEW_CR|INDICATION:
+ /* Got New CR for bchan, for now I handle this one in */
+ /* connect_ack, Need to be changed */
+ {
+ struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo);
+ int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE));
+ if (!bc) { cb_log(0, stack->port, " --> In NEW_CR: didn't found bc ??\n"); return -1;};
+ if (((l3id&0xff00)!=0xff00) && ((bc->l3_id&0xff00)==0xff00)) {
+ cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", 0xff&bc->l3_id);
+ stack->procids[bc->l3_id&0xff] = 0 ;
+ }
+ cb_log(4, stack->port, "lib: Event_ind:CC_NEW_CR : very new L3ID is %x\n",l3id );
+
+ bc->l3_id =l3id;
+ cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data);
+
+ free_msg(msg);
+ return 0;
+ }
+
+ case DL_ESTABLISH | INDICATION:
+ case DL_ESTABLISH | CONFIRM:
+ {
+ cb_log(3, stack->port, "%% GOT L2 Activate Info.\n");
+
+ if (stack->ptp && stack->l2link) {
+ cb_log(0, stack->port, "%% GOT L2 Activate Info. but we're activated already.. this l2 is faulty, blocking port\n");
+ cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data);
+ }
+
+ if (stack->ptp && !stack->restart_sent) {
+ /* make sure we restart the interface of the
+ * other side */
+ stack->restart_sent=1;
+ misdn_lib_send_restart(stack->port, -1);
+
+ }
+
+ /* when we get the L2 UP, the L1 is UP definitely too*/
+ stack->l2link = 1;
+ stack->l2upcnt=0;
+
+ free_msg(msg);
+ return 0;
+ }
+ break;
+
+
+ case DL_RELEASE | INDICATION:
+ case DL_RELEASE | CONFIRM:
+ {
+ if (stack->ptp) {
+ cb_log(3 , stack->port, "%% GOT L2 DeActivate Info.\n");
+
+ if (stack->l2upcnt>3) {
+ cb_log(0 , stack->port, "!!! Could not Get the L2 up after 3 Attemps!!!\n");
+ } else {
+#if 0
+ if (stack->nt) misdn_lib_reinit_nt_stack(stack->port);
+#endif
+ if (stack->l1link) {
+ misdn_lib_get_l2_up(stack);
+ stack->l2upcnt++;
+ }
+ }
+
+ } else
+ cb_log(3, stack->port, "%% GOT L2 DeActivate Info.\n");
+
+ stack->l2link = 0;
+ free_msg(msg);
+ return 0;
+ }
+ break;
+ }
+ }
+
+ {
+ /* Parse Events and fire_up to App. */
+ struct misdn_bchannel *bc;
+ struct misdn_bchannel dummybc;
+
+ enum event_e event = isdn_msg_get_event(msgs_g, msg, 1);
+
+ bc=find_bc_by_l3id(stack, hh->dinfo);
+
+ if (!bc) {
+ cb_log(4, stack->port, " --> Didn't found BC so temporarly creating dummy BC (l3id:%x).\n", hh->dinfo);
+ misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0);
+ bc=&dummybc;
+ }
+ if (bc ) {
+ isdn_msg_parse_event(msgs_g,msg,bc, 1);
+
+ switch (event) {
+ case EVENT_SETUP:
+ if (bc->channel<=0 || bc->channel==0xff)
+ bc->channel=0;
+
+ if (find_free_chan_in_stack(stack,bc, bc->channel,0)<0)
+ goto ERR_NO_CHANNEL;
+ break;
+ case EVENT_RELEASE:
+ case EVENT_RELEASE_COMPLETE:
+ {
+ int channel=bc->channel;
+ int tmpcause=bc->cause;
+ empty_bc(bc);
+ bc->cause=tmpcause;
+ clean_up_bc(bc);
+
+ if (channel>0)
+ empty_chan_in_stack(stack,channel);
+ bc->in_use=0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(!isdn_get_info(msgs_g,event,1)) {
+ cb_log(4, stack->port, "Unknown Event Ind: prim %x dinfo %x\n",hh->prim, hh->dinfo);
+ } else {
+ if (reject) {
+ switch(bc->cause){
+ case 17:
+ cb_log(1, stack->port, "Siemens Busy reject..\n");
+
+ break;
+ default:
+ break;
+ }
+ }
+ cb_event(event, bc, glob_mgr->user_data);
+ }
+ } else {
+ cb_log(4, stack->port, "No BC found with l3id: prim %x dinfo %x\n",hh->prim, hh->dinfo);
+ }
+
+ free_msg(msg);
+ }
+
+
+ return 0;
+}
+
+
+static int handle_timers(msg_t* msg)
+{
+ iframe_t *frm= (iframe_t*)msg->data;
+ struct misdn_stack *stack;
+
+ /* Timer Stuff */
+ switch (frm->prim) {
+ case MGR_INITTIMER | CONFIRM:
+ case MGR_ADDTIMER | CONFIRM:
+ case MGR_DELTIMER | CONFIRM:
+ case MGR_REMOVETIMER | CONFIRM:
+ free_msg(msg);
+ return(1);
+ }
+
+
+
+ if (frm->prim==(MGR_TIMER | INDICATION) ) {
+ for (stack = glob_mgr->stack_list;
+ stack;
+ stack = stack->next) {
+ itimer_t *it;
+
+ if (!stack->nt) continue;
+
+ it = stack->nst.tlist;
+ /* find timer */
+ for(it=stack->nst.tlist;
+ it;
+ it=it->next) {
+ if (it->id == (int)frm->addr)
+ break;
+ }
+ if (it) {
+ int ret;
+ ret = mISDN_write_frame(stack->midev, msg->data, frm->addr,
+ MGR_TIMER | RESPONSE, 0, 0, NULL, TIMEOUT_1SEC);
+ test_and_clear_bit(FLG_TIMER_RUNING, (long unsigned int *)&it->Flags);
+ ret = it->function(it->data);
+ free_msg(msg);
+ return 1;
+ }
+ }
+
+ cb_log(0, 0, "Timer Msg without Timer ??\n");
+ free_msg(msg);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+void misdn_lib_tone_generator_start(struct misdn_bchannel *bc)
+{
+ bc->generate_tone=1;
+}
+
+void misdn_lib_tone_generator_stop(struct misdn_bchannel *bc)
+{
+ bc->generate_tone=0;
+}
+
+
+static int do_tone(struct misdn_bchannel *bc, int len)
+{
+ bc->tone_cnt=len;
+
+ if (bc->generate_tone) {
+ cb_event(EVENT_TONE_GENERATE, bc, glob_mgr->user_data);
+
+ if ( !bc->nojitter ) {
+ misdn_tx_jitter(bc,len);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#ifdef MISDN_SAVE_DATA
+static void misdn_save_data(int id, char *p1, int l1, char *p2, int l2)
+{
+ char n1[32],n2[32];
+ FILE *rx, *tx;
+
+ sprintf(n1,"/tmp/misdn-rx-%d.raw",id);
+ sprintf(n2,"/tmp/misdn-tx-%d.raw",id);
+
+ rx = fopen(n1,"a+");
+ tx = fopen(n2,"a+");
+
+ if (!rx || !tx) {
+ cb_log(0,0,"Couldn't open files: %s\n",strerror(errno));
+ return ;
+ }
+
+ fwrite(p1,1,l1,rx);
+ fwrite(p2,1,l2,tx);
+
+ fclose(rx);
+ fclose(tx);
+
+}
+#endif
+
+void misdn_tx_jitter(struct misdn_bchannel *bc, int len)
+{
+ char buf[4096 + mISDN_HEADER_LEN];
+ char *data=&buf[mISDN_HEADER_LEN];
+ iframe_t *txfrm= (iframe_t*)buf;
+ int jlen, r;
+
+ jlen=cb_jb_empty(bc,data,len);
+
+ if (jlen) {
+#ifdef MISDN_SAVE_DATA
+ misdn_save_data((bc->port*100+bc->channel), data, jlen, bc->bframe, bc->bframe_len);
+#endif
+ flip_buf_bits( data, jlen);
+
+ if (jlen < len) {
+ cb_log(7,bc->port,"Jitterbuffer Underrun.\n");
+ }
+
+ txfrm->prim = DL_DATA|REQUEST;
+
+ txfrm->dinfo = 0;
+
+ txfrm->addr = bc->addr|FLG_MSG_DOWN; /* | IF_DOWN; */
+
+ txfrm->len =jlen;
+ cb_log(9, bc->port, "Transmitting %d samples 2 misdn\n", txfrm->len);
+
+ r=mISDN_write( glob_mgr->midev, buf, txfrm->len + mISDN_HEADER_LEN, 8000 );
+ } else {
+#define MISDN_GEN_SILENCE
+#ifdef MISDN_GEN_SILENCE
+ int cnt=len/TONE_SILENCE_SIZE;
+ int rest=len%TONE_SILENCE_SIZE;
+ int i;
+
+ for (i=0; i<cnt; i++) {
+ memcpy(data, tone_silence_flip, TONE_SILENCE_SIZE );
+ data +=TONE_SILENCE_SIZE;
+ }
+
+ if (rest) {
+ memcpy(data, tone_silence_flip, rest);
+ }
+
+ txfrm->prim = DL_DATA|REQUEST;
+
+ txfrm->dinfo = 0;
+
+ txfrm->addr = bc->addr|FLG_MSG_DOWN; /* | IF_DOWN; */
+
+ txfrm->len =len;
+ cb_log(9, bc->port, "Transmitting %d samples 2 misdn\n", txfrm->len);
+
+ r=mISDN_write( glob_mgr->midev, buf, txfrm->len + mISDN_HEADER_LEN, 8000 );
+#endif
+
+ }
+}
+
+static int handle_bchan(msg_t *msg)
+{
+ iframe_t *frm= (iframe_t*)msg->data;
+ struct misdn_bchannel *bc=find_bc_by_addr(frm->addr);
+ struct misdn_stack *stack;
+
+ if (!bc) {
+ cb_log(1,0,"handle_bchan: BC not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo);
+ return 0 ;
+ }
+
+ stack = get_stack_by_bc(bc);
+
+ if (!stack) {
+ cb_log(0, bc->port,"handle_bchan: STACK not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo);
+ return 0;
+ }
+
+ switch (frm->prim) {
+
+ case MGR_SETSTACK| CONFIRM:
+ cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|CONFIRM pid:%d\n",bc->pid);
+ break;
+
+ case MGR_SETSTACK| INDICATION:
+ cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|IND pid:%d\n",bc->pid);
+ break;
+#if 0
+ AGAIN:
+ bc->addr = mISDN_get_layerid(stack->midev, bc->b_stid, bc->layer);
+ if (!bc->addr) {
+
+ if (errno == EAGAIN) {
+ usleep(1000);
+ goto AGAIN;
+ }
+
+ cb_log(0,stack->port,"$$$ Get Layer (%d) Id Error: %s\n",bc->layer,strerror(errno));
+
+ /* we kill the channel later, when we received some
+ data. */
+ bc->addr= frm->addr;
+ } else if ( bc->addr < 0) {
+ cb_log(0, stack->port,"$$$ bc->addr <0 Error:%s\n",strerror(errno));
+ bc->addr=0;
+ }
+
+ cb_log(4, stack->port," --> Got Adr %x\n", bc->addr);
+
+ free_msg(msg);
+
+
+ switch(bc->bc_state) {
+ case BCHAN_SETUP:
+ bc_state_change(bc,BCHAN_SETUPED);
+ break;
+
+ case BCHAN_CLEAN_REQUEST:
+ default:
+ cb_log(0, stack->port," --> STATE WASN'T SETUP (but %s) in SETSTACK|IND pid:%d\n",bc_state2str(bc->bc_state), bc->pid);
+ clean_up_bc(bc);
+ }
+ return 1;
+#endif
+
+ case MGR_DELLAYER| INDICATION:
+ cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|IND pid:%d\n",bc->pid);
+ break;
+
+ case MGR_DELLAYER| CONFIRM:
+ cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|CNF pid:%d\n",bc->pid);
+
+ bc->pid=0;
+ bc->addr=0;
+
+ free_msg(msg);
+ return 1;
+
+ case PH_ACTIVATE | INDICATION:
+ case DL_ESTABLISH | INDICATION:
+ cb_log(3, stack->port, "BCHAN: ACT Ind pid:%d\n", bc->pid);
+
+ free_msg(msg);
+ return 1;
+
+ case PH_ACTIVATE | CONFIRM:
+ case DL_ESTABLISH | CONFIRM:
+
+ cb_log(3, stack->port, "BCHAN: bchan ACT Confirm pid:%d\n",bc->pid);
+ free_msg(msg);
+
+ return 1;
+
+ case DL_ESTABLISH | REQUEST:
+ {
+ char buf[128];
+ mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_TARGET | FLG_MSG_DOWN, DL_ESTABLISH | CONFIRM, 0,0, NULL, TIMEOUT_1SEC);
+ }
+ free_msg(msg);
+ return 1;
+
+ case DL_RELEASE|REQUEST:
+ {
+ char buf[128];
+ mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_TARGET | FLG_MSG_DOWN, DL_RELEASE| CONFIRM, 0,0, NULL, TIMEOUT_1SEC);
+ }
+ free_msg(msg);
+ return 1;
+
+ case PH_DEACTIVATE | INDICATION:
+ case DL_RELEASE | INDICATION:
+ cb_log (3, stack->port, "BCHAN: DeACT Ind pid:%d\n",bc->pid);
+
+ free_msg(msg);
+ return 1;
+
+ case PH_DEACTIVATE | CONFIRM:
+ case DL_RELEASE | CONFIRM:
+ cb_log(3, stack->port, "BCHAN: DeACT Conf pid:%d\n",bc->pid);
+
+ free_msg(msg);
+ return 1;
+
+ case PH_CONTROL|INDICATION:
+ {
+ unsigned int cont = *((unsigned int *)&frm->data.p);
+
+ cb_log(4, stack->port, "PH_CONTROL: channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad);
+
+ if ((cont&~DTMF_TONE_MASK) == DTMF_TONE_VAL) {
+ int dtmf = cont & DTMF_TONE_MASK;
+ cb_log(4, stack->port, " --> DTMF TONE: %c\n",dtmf);
+ bc->dtmf=dtmf;
+ cb_event(EVENT_DTMF_TONE, bc, glob_mgr->user_data);
+
+ free_msg(msg);
+ return 1;
+ }
+ if (cont == BF_REJECT) {
+ cb_log(4, stack->port, " --> BF REJECT\n");
+ free_msg(msg);
+ return 1;
+ }
+ if (cont == BF_ACCEPT) {
+ cb_log(4, stack->port, " --> BF ACCEPT\n");
+ free_msg(msg);
+ return 1;
+ }
+ }
+ break;
+
+ case PH_DATA|REQUEST:
+ case DL_DATA|REQUEST:
+ cb_log(0, stack->port, "DL_DATA REQUEST \n");
+ do_tone(bc, 64);
+
+ free_msg(msg);
+ return 1;
+
+
+ case PH_DATA|INDICATION:
+ case DL_DATA|INDICATION:
+ {
+ bc->bframe = (void*)&frm->data.i;
+ bc->bframe_len = frm->len;
+
+ /** Anyway flip the bufbits **/
+ if ( misdn_cap_is_speech(bc->capability) )
+ flip_buf_bits(bc->bframe, bc->bframe_len);
+
+
+ if (!bc->bframe_len) {
+ cb_log(2, stack->port, "DL_DATA INDICATION bc->addr:%x frm->addr:%x\n", bc->addr, frm->addr);
+ free_msg(msg);
+ return 1;
+ }
+
+ if ( (bc->addr&STACK_ID_MASK) != (frm->addr&STACK_ID_MASK) ) {
+ cb_log(2, stack->port, "DL_DATA INDICATION bc->addr:%x frm->addr:%x\n", bc->addr, frm->addr);
+ free_msg(msg);
+ return 1;
+ }
+
+#if MISDN_DEBUG
+ cb_log(0, stack->port, "DL_DATA INDICATION Len %d\n", frm->len);
+
+#endif
+
+ if ( (bc->bc_state == BCHAN_ACTIVATED) && frm->len > 0) {
+ int t;
+
+#ifdef MISDN_B_DEBUG
+ cb_log(0,bc->port,"do_tone START\n");
+#endif
+ t=do_tone(bc,frm->len);
+
+#ifdef MISDN_B_DEBUG
+ cb_log(0,bc->port,"do_tone STOP (%d)\n",t);
+#endif
+ if ( !t ) {
+ int i;
+
+ if ( misdn_cap_is_speech(bc->capability)) {
+ if ( !bc->nojitter ) {
+#ifdef MISDN_B_DEBUG
+ cb_log(0,bc->port,"tx_jitter START\n");
+#endif
+ misdn_tx_jitter(bc,frm->len);
+#ifdef MISDN_B_DEBUG
+ cb_log(0,bc->port,"tx_jitter STOP\n");
+#endif
+ }
+ }
+
+#ifdef MISDN_B_DEBUG
+ cb_log(0,bc->port,"EVENT_B_DATA START\n");
+#endif
+
+ i = cb_event(EVENT_BCHAN_DATA, bc, glob_mgr->user_data);
+#ifdef MISDN_B_DEBUG
+ cb_log(0,bc->port,"EVENT_B_DATA STOP\n");
+#endif
+
+ if (i<0) {
+ cb_log(10,stack->port,"cb_event returned <0\n");
+ /*clean_up_bc(bc);*/
+ }
+ }
+ }
+ free_msg(msg);
+ return 1;
+ }
+
+
+ case PH_CONTROL | CONFIRM:
+ cb_log(4, stack->port, "PH_CONTROL|CNF bc->addr:%x\n", frm->addr);
+ free_msg(msg);
+ return 1;
+
+ case PH_DATA | CONFIRM:
+ case DL_DATA|CONFIRM:
+#if MISDN_DEBUG
+
+ cb_log(0, stack->port, "Data confirmed\n");
+
+#endif
+ free_msg(msg);
+ return 1;
+ case DL_DATA|RESPONSE:
+#if MISDN_DEBUG
+ cb_log(0, stack->port, "Data response\n");
+
+#endif
+ break;
+ }
+
+ return 0;
+}
+
+
+
+static int handle_frm_nt(msg_t *msg)
+{
+ iframe_t *frm= (iframe_t*)msg->data;
+ struct misdn_stack *stack;
+ int err=0;
+
+ stack=find_stack_by_addr( frm->addr );
+
+
+
+ if (!stack || !stack->nt) {
+ return 0;
+ }
+
+
+ if ((err=stack->nst.l1_l2(&stack->nst,msg))) {
+
+ if (nt_err_cnt > 0 ) {
+ if (nt_err_cnt < 100) {
+ nt_err_cnt++;
+ cb_log(0, stack->port, "NT Stack sends us error: %d \n", err);
+ } else if (nt_err_cnt < 105){
+ cb_log(0, stack->port, "NT Stack sends us error: %d over 100 times, so I'll stop this message\n", err);
+ nt_err_cnt = - 1;
+ }
+ }
+ free_msg(msg);
+ return 1;
+
+ }
+
+ return 1;
+}
+
+
+static int handle_frm(msg_t *msg)
+{
+ iframe_t *frm = (iframe_t*) msg->data;
+
+ struct misdn_stack *stack=find_stack_by_addr(frm->addr);
+
+ if (!stack || stack->nt) {
+ return 0;
+ }
+
+ cb_log(4,stack?stack->port:0,"handle_frm: frm->addr:%x frm->prim:%x\n",frm->addr,frm->prim);
+
+ {
+ struct misdn_bchannel dummybc;
+ struct misdn_bchannel *bc;
+ int ret=handle_cr(stack, frm);
+
+ if (ret<0) {
+ cb_log(3,stack?stack->port:0,"handle_frm: handle_cr <0 prim:%x addr:%x\n", frm->prim, frm->addr);
+
+
+ }
+
+ if(ret) {
+ free_msg(msg);
+ return 1;
+ }
+
+ bc=find_bc_by_l3id(stack, frm->dinfo);
+
+ if (!bc && (frm->prim==(CC_RESTART|CONFIRM)) ) {
+ misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0);
+ bc=&dummybc;
+ }
+
+handle_frm_bc:
+ if (bc ) {
+ enum event_e event = isdn_msg_get_event(msgs_g, msg, 0);
+ enum event_response_e response=RESPONSE_OK;
+ int ret;
+
+ isdn_msg_parse_event(msgs_g,msg,bc, 0);
+
+ /** Preprocess some Events **/
+ ret = handle_event(bc, event, frm);
+ if (ret<0) {
+ cb_log(0,stack->port,"couldn't handle event\n");
+ free_msg(msg);
+ return 1;
+ }
+ /* shoot up event to App: */
+ cb_log(5, stack->port, "lib Got Prim: Addr %x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo);
+
+ if(!isdn_get_info(msgs_g,event,0))
+ cb_log(0, stack->port, "Unknown Event Ind: Addr:%x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo);
+ else
+ response=cb_event(event, bc, glob_mgr->user_data);
+#if 1
+ if (event == EVENT_SETUP) {
+ switch (response) {
+ case RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE:
+
+ cb_log(0, stack->port, "TOTALY IGNORING SETUP \n");
+
+ break;
+ case RESPONSE_IGNORE_SETUP:
+ /* I think we should send CC_RELEASE_CR, but am not sure*/
+ bc->out_cause=16;
+
+ case RESPONSE_RELEASE_SETUP:
+ misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE);
+ if (bc->channel>0)
+ empty_chan_in_stack(stack, bc->channel);
+ empty_bc(bc);
+ bc_state_change(bc,BCHAN_CLEANED);
+ bc->in_use=0;
+
+ cb_log(0, stack->port, "GOT IGNORE SETUP\n");
+ break;
+ case RESPONSE_OK:
+ cb_log(4, stack->port, "GOT SETUP OK\n");
+
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (event == EVENT_RELEASE_COMPLETE) {
+ /* release bchannel only after we've anounced the RELEASE_COMPLETE */
+ int channel=bc->channel;
+ int tmpcause=bc->cause;
+ int tmp_out_cause=bc->out_cause;
+ empty_bc(bc);
+ bc->cause=tmpcause;
+ bc->out_cause=tmp_out_cause;
+ clean_up_bc(bc);
+
+ if (tmpcause == 44) {
+ cb_log(0,stack->port,"**** Received CAUSE:44, so not cleaning up channel %d\n", channel);
+ cb_log(0,stack->port,"**** This channel is now no longer available,\nplease try to restart it with 'misdn send restart <port> <channel>'\n");
+ set_chan_in_stack(stack, channel);
+ bc->channel=channel;
+ misdn_lib_send_restart(stack->port, channel);
+ } else {
+ if (channel>0)
+ empty_chan_in_stack(stack, channel);
+ }
+ bc->in_use=0;
+ }
+
+ if (event == EVENT_RESTART) {
+ cb_log(0, stack->port, "**** Received RESTART_ACK channel:%d\n", bc->restart_channel);
+ empty_chan_in_stack(stack, bc->restart_channel);
+ }
+
+ cb_log(5, stack->port, "Freeing Msg on prim:%x \n",frm->prim);
+
+
+ free_msg(msg);
+ return 1;
+#endif
+
+ } else {
+ struct misdn_bchannel dummybc;
+ if (frm->prim!=(CC_FACILITY|INDICATION))
+ cb_log(0, stack->port, " --> Didn't find BC so temporarly creating dummy BC (l3id:%x) on this port.\n", frm->dinfo);
+ else
+ cb_log(5, stack->port, " --> Using Dummy BC for FACILITy\n");
+
+ memset (&dummybc,0,sizeof(dummybc));
+ dummybc.port=stack->port;
+ dummybc.l3_id=frm->dinfo;
+ bc=&dummybc;
+ goto handle_frm_bc;
+ }
+ }
+
+ cb_log(4, stack->port, "TE_FRM_HANDLER: Returning 0 on prim:%x \n",frm->prim);
+ return 0;
+}
+
+
+static int handle_l1(msg_t *msg)
+{
+ iframe_t *frm = (iframe_t*) msg->data;
+ struct misdn_stack *stack = find_stack_by_addr(frm->addr);
+ int i ;
+
+ if (!stack) return 0 ;
+
+ switch (frm->prim) {
+ case PH_ACTIVATE | CONFIRM:
+ case PH_ACTIVATE | INDICATION:
+ cb_log (3, stack->port, "L1: PH L1Link Up!\n");
+ stack->l1link=1;
+
+ if (stack->nt) {
+
+ if (stack->nst.l1_l2(&stack->nst, msg))
+ free_msg(msg);
+
+ if (stack->ptp)
+ misdn_lib_get_l2_up(stack);
+ } else {
+ free_msg(msg);
+ }
+
+ for (i=0;i<=stack->b_num; i++) {
+ if (stack->bc[i].evq != EVENT_NOTHING) {
+ cb_log(4, stack->port, "Fireing Queued Event %s because L1 got up\n", isdn_get_info(msgs_g, stack->bc[i].evq, 0));
+ misdn_lib_send_event(&stack->bc[i],stack->bc[i].evq);
+ stack->bc[i].evq=EVENT_NOTHING;
+ }
+
+ }
+ return 1;
+
+ case PH_ACTIVATE | REQUEST:
+ free_msg(msg);
+ cb_log(3,stack->port,"L1: PH_ACTIVATE|REQUEST \n");
+ return 1;
+
+ case PH_DEACTIVATE | REQUEST:
+ free_msg(msg);
+ cb_log(3,stack->port,"L1: PH_DEACTIVATE|REQUEST \n");
+ return 1;
+
+ case PH_DEACTIVATE | CONFIRM:
+ case PH_DEACTIVATE | INDICATION:
+ cb_log (3, stack->port, "L1: PH L1Link Down! \n");
+
+#if 0
+ for (i=0; i<=stack->b_num; i++) {
+ if (global_state == MISDN_INITIALIZED) {
+ cb_event(EVENT_CLEANUP, &stack->bc[i], glob_mgr->user_data);
+ }
+ }
+#endif
+
+ if (stack->nt) {
+ if (stack->nst.l1_l2(&stack->nst, msg))
+ free_msg(msg);
+ } else {
+ free_msg(msg);
+ }
+
+ stack->l1link=0;
+ stack->l2link=0;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int handle_l2(msg_t *msg)
+{
+ iframe_t *frm = (iframe_t*) msg->data;
+
+ struct misdn_stack *stack = find_stack_by_addr(frm->addr);
+
+ if (!stack) {
+ return 0 ;
+ }
+
+ switch(frm->prim) {
+
+ case DL_ESTABLISH | REQUEST:
+ cb_log(1,stack->port,"DL_ESTABLISH|REQUEST \n");
+ return 1;
+ case DL_RELEASE | REQUEST:
+ cb_log(1,stack->port,"DL_RELEASE|REQUEST \n");
+ return 1;
+
+ case DL_ESTABLISH | INDICATION:
+ case DL_ESTABLISH | CONFIRM:
+ {
+ cb_log (3, stack->port, "L2: L2Link Up! \n");
+ if (stack->ptp && stack->l2link) {
+ cb_log (-1, stack->port, "L2: L2Link Up! but it's already UP.. must be faulty, blocking port\n");
+ cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data);
+ }
+ stack->l2link=1;
+ free_msg(msg);
+ return 1;
+ }
+ break;
+
+ case DL_RELEASE | INDICATION:
+ case DL_RELEASE | CONFIRM:
+ {
+ cb_log (3, stack->port, "L2: L2Link Down! \n");
+ stack->l2link=0;
+
+ free_msg(msg);
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int handle_mgmt(msg_t *msg)
+{
+ iframe_t *frm = (iframe_t*) msg->data;
+ struct misdn_stack *stack;
+
+ if ( (frm->addr == 0) && (frm->prim == (MGR_DELLAYER|CONFIRM)) ) {
+ cb_log(2, 0, "MGMT: DELLAYER|CONFIRM Addr: 0 !\n") ;
+ free_msg(msg);
+ return 1;
+ }
+
+ stack = find_stack_by_addr(frm->addr);
+
+ if (!stack) {
+ if (frm->prim == (MGR_DELLAYER|CONFIRM)) {
+ cb_log(2, 0, "MGMT: DELLAYER|CONFIRM Addr: %x !\n",
+ frm->addr) ;
+ free_msg(msg);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ switch(frm->prim) {
+ case MGR_SHORTSTATUS | INDICATION:
+ case MGR_SHORTSTATUS | CONFIRM:
+ cb_log(5, 0, "MGMT: Short status dinfo %x\n",frm->dinfo);
+
+ switch (frm->dinfo) {
+ case SSTATUS_L1_ACTIVATED:
+ cb_log(3, 0, "MGMT: SSTATUS: L1_ACTIVATED \n");
+ stack->l1link=1;
+
+ break;
+ case SSTATUS_L1_DEACTIVATED:
+ cb_log(3, 0, "MGMT: SSTATUS: L1_DEACTIVATED \n");
+ stack->l1link=0;
+#if 0
+ clear_l3(stack);
+#endif
+ break;
+
+ case SSTATUS_L2_ESTABLISHED:
+ cb_log(3, stack->port, "MGMT: SSTATUS: L2_ESTABLISH \n");
+ stack->l2link=1;
+ break;
+
+ case SSTATUS_L2_RELEASED:
+ cb_log(3, stack->port, "MGMT: SSTATUS: L2_RELEASED \n");
+ stack->l2link=0;
+ break;
+ }
+
+ free_msg(msg);
+ return 1;
+
+ case MGR_SETSTACK | INDICATION:
+ cb_log(4, stack->port, "MGMT: SETSTACK|IND dinfo %x\n",frm->dinfo);
+ free_msg(msg);
+ return 1;
+ case MGR_DELLAYER | CONFIRM:
+ cb_log(4, stack->port, "MGMT: DELLAYER|CNF dinfo %x\n",frm->dinfo) ;
+ free_msg(msg);
+ return 1;
+
+ }
+
+ /*
+ if ( (frm->prim & 0x0f0000) == 0x0f0000) {
+ cb_log(5, 0, "$$$ MGMT FRAME: prim %x addr %x dinfo %x\n",frm->prim, frm->addr, frm->dinfo) ;
+ free_msg(msg);
+ return 1;
+ } */
+
+ return 0;
+}
+
+
+static msg_t *fetch_msg(int midev)
+{
+ msg_t *msg=alloc_msg(MAX_MSG_SIZE);
+ int r;
+
+ if (!msg) {
+ cb_log(0, 0, "fetch_msg: alloc msg failed !!");
+ return NULL;
+ }
+
+ AGAIN:
+ r=mISDN_read(midev,msg->data,MAX_MSG_SIZE, TIMEOUT_10SEC);
+ msg->len=r;
+
+ if (r==0) {
+ free_msg(msg); /* danger, cauz usualy freeing in main_loop */
+ cb_log(6,0,"Got empty Msg..\n");
+ return NULL;
+ }
+
+ if (r<0) {
+ if (errno == EAGAIN) {
+ /*we wait for mISDN here*/
+ cb_log(4,0,"mISDN_read wants us to wait\n");
+ usleep(5000);
+ goto AGAIN;
+ }
+
+ cb_log(0,0,"mISDN_read returned :%d error:%s (%d)\n",r,strerror(errno),errno);
+ }
+
+#if 0
+ if (!(frm->prim == (DL_DATA|INDICATION) )|| (frm->prim == (PH_DATA|INDICATION)))
+ cb_log(0,0,"prim: %x dinfo:%x addr:%x msglen:%d frm->len:%d\n",frm->prim, frm->dinfo, frm->addr, msg->len,frm->len );
+#endif
+ return msg;
+}
+
+void misdn_lib_isdn_l1watcher(int port)
+{
+ struct misdn_stack *stack;
+
+ for (stack = glob_mgr->stack_list; stack && (stack->port != port); stack = stack->next)
+ ;
+
+ if (stack) {
+ cb_log(4, port, "Checking L1 State\n");
+ if (!stack->l1link) {
+ cb_log(4, port, "L1 State Down, trying to get it up again\n");
+ misdn_lib_get_short_status(stack);
+ misdn_lib_get_l1_up(stack);
+ misdn_lib_get_l2_up(stack);
+ }
+ }
+}
+
+static void misdn_lib_isdn_event_catcher(void *arg)
+{
+ struct misdn_lib *mgr = arg;
+ int zero_frm=0 , fff_frm=0 ;
+ int midev= mgr->midev;
+ int port=0;
+
+ while (1) {
+ msg_t *msg = fetch_msg(midev);
+ iframe_t *frm;
+
+
+ if (!msg) continue;
+
+ frm = (iframe_t*) msg->data;
+
+ /** When we make a call from NT2Ast we get this frames **/
+ if (frm->len == 0 && frm->addr == 0 && frm->dinfo == 0 && frm->prim == 0 ) {
+ zero_frm++;
+ free_msg(msg);
+ continue;
+ } else {
+ if (zero_frm) {
+ cb_log(0, port, "*** Alert: %d zero_frms caught\n", zero_frm);
+ zero_frm = 0 ;
+ }
+ }
+
+ /** I get this sometimes after setup_bc **/
+ if (frm->len == 0 && frm->dinfo == 0 && frm->prim == 0xffffffff ) {
+ fff_frm++;
+ free_msg(msg);
+ continue;
+ } else {
+ if (fff_frm) {
+ cb_log(0, port, "*** Alert: %d fff_frms caught\n", fff_frm);
+ fff_frm = 0 ;
+ }
+ }
+
+ manager_isdn_handler(frm, msg);
+ }
+
+}
+
+
+/** App Interface **/
+
+int te_lib_init() {
+ char buff[1025] = "";
+ iframe_t *frm=(iframe_t*)buff;
+ int midev=mISDN_open();
+ int ret;
+
+ if (midev<=0) return midev;
+
+/* create entity for layer 3 TE-mode */
+ mISDN_write_frame(midev, buff, 0, MGR_NEWENTITY | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+ ret = mISDN_read_frame(midev, frm, sizeof(iframe_t), 0, MGR_NEWENTITY | CONFIRM, TIMEOUT_1SEC);
+
+ if (ret < mISDN_HEADER_LEN) {
+ noentity:
+ fprintf(stderr, "cannot request MGR_NEWENTITY from mISDN: %s\n",strerror(errno));
+ exit(-1);
+ }
+
+ entity = frm->dinfo & 0xffff ;
+
+ if (!entity)
+ goto noentity;
+
+ return midev;
+
+}
+
+void te_lib_destroy(int midev)
+{
+ char buf[1024];
+ mISDN_write_frame(midev, buf, 0, MGR_DELENTITY | REQUEST, entity, 0, NULL, TIMEOUT_1SEC);
+
+ cb_log(4, 0, "Entetity deleted\n");
+ mISDN_close(midev);
+ cb_log(4, 0, "midev closed\n");
+}
+
+
+
+void misdn_lib_transfer(struct misdn_bchannel* holded_bc)
+{
+ holded_bc->holded=0;
+}
+
+struct misdn_bchannel *manager_find_bc_by_pid(int pid)
+{
+ struct misdn_stack *stack;
+ int i;
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next) {
+ for (i=0; i<=stack->b_num; i++)
+ if (stack->bc[i].pid == pid) return &stack->bc[i];
+ }
+
+ return NULL;
+}
+
+struct misdn_bchannel *manager_find_bc_holded(struct misdn_bchannel* bc)
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+ return find_bc_holded(stack);
+}
+
+
+
+static void prepare_bc(struct misdn_bchannel*bc, int channel)
+{
+ bc->channel = channel;
+ bc->channel_preselected = channel?1:0;
+ bc->in_use = 1;
+ bc->need_disconnect=1;
+ bc->need_release=1;
+ bc->need_release_complete=1;
+ bc->cause=16;
+
+ if (++mypid>5000) mypid=1;
+ bc->pid=mypid;
+
+#if 0
+ bc->addr=0;
+ bc->b_stid=0;
+ bc->layer_id=0;
+#endif
+}
+
+struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec)
+{
+ struct misdn_stack *stack;
+ int i;
+
+ if (channel < 0 || channel > MAX_BCHANS) {
+ cb_log(0,port,"Requested channel out of bounds (%d)\n",channel);
+ return NULL;
+ }
+
+ for (stack=glob_mgr->stack_list; stack; stack=stack->next) {
+
+ if (stack->port == port) {
+ int maxnum;
+
+ if (stack->blocked) {
+ cb_log(0,port,"Port is blocked\n");
+ return NULL;
+ }
+
+ if (channel > 0) {
+ if (channel <= stack->b_num) {
+ for (i = 0; i < stack->b_num; i++) {
+ if (stack->bc[i].in_use && stack->bc[i].channel == channel) {
+ cb_log(0,port,"Requested channel:%d on port:%d is already in use\n",channel, port);
+ return NULL;
+ }
+ }
+ } else {
+ cb_log(0,port,"Requested channel:%d is out of bounds on port:%d\n",channel, port);
+ return NULL;
+ }
+ }
+
+ maxnum = inout && !stack->pri && !stack->ptp ? stack->b_num + 1 : stack->b_num;
+
+ if (dec) {
+ for (i = maxnum-1; i>=0; i--) {
+ if (!stack->bc[i].in_use) {
+ /* 3. channel on bri means CW*/
+ if (!stack->pri && i==stack->b_num)
+ stack->bc[i].cw=1;
+
+ prepare_bc(&stack->bc[i], channel);
+ stack->bc[i].dec=1;
+ return &stack->bc[i];
+ }
+ }
+ } else {
+ for (i = 0; i <maxnum; i++) {
+ if (!stack->bc[i].in_use) {
+ /* 3. channel on bri means CW*/
+ if (!stack->pri && i==stack->b_num)
+ stack->bc[i].cw=1;
+
+ prepare_bc(&stack->bc[i], channel);
+ return &stack->bc[i];
+ }
+ }
+ }
+
+ cb_log(1,port,"There is no free channel on port (%d)\n",port);
+ return NULL;
+ }
+ }
+
+ cb_log(0,port,"Port is not configured (%d)\n",port);
+ return NULL;
+}
+
+
+static char *fac2str (enum FacFunction func)
+{
+ struct arr_el {
+ enum FacFunction p;
+ char *s ;
+ } arr[] = {
+ { Fac_None, "Fac_None" },
+ { Fac_CD, "Fac_CD"},
+ };
+
+ int i;
+
+ for (i=0; i < sizeof(arr)/sizeof( struct arr_el) ; i ++)
+ if ( arr[i].p==func) return arr[i].s;
+
+ return "unknown";
+}
+
+void misdn_lib_log_ies(struct misdn_bchannel *bc)
+{
+ struct misdn_stack *stack;
+
+ if (!bc) return;
+
+ stack = get_stack_by_bc(bc);
+
+ if (!stack) return;
+
+ cb_log(2, stack->port, " --> channel:%d mode:%s cause:%d ocause:%d rad:%s cad:%s\n", bc->channel, stack->nt?"NT":"TE", bc->cause, bc->out_cause, bc->rad, bc->cad);
+
+ cb_log(2, stack->port,
+ " --> info_dad:%s onumplan:%c dnumplan:%c rnumplan:%c cpnnumplan:%c\n",
+ bc->info_dad,
+ bc->onumplan>=0?'0'+bc->onumplan:' ',
+ bc->dnumplan>=0?'0'+bc->dnumplan:' ',
+ bc->rnumplan>=0?'0'+bc->rnumplan:' ',
+ bc->cpnnumplan>=0?'0'+bc->cpnnumplan:' '
+ );
+
+ cb_log(3, stack->port, " --> caps:%s pi:%x keypad:%s sending_complete:%d\n", bearer2str(bc->capability),bc->progress_indicator, bc->keypad, bc->sending_complete);
+ cb_log(4, stack->port, " --> screen:%d --> pres:%d\n",
+ bc->screen, bc->pres);
+
+ cb_log(4, stack->port, " --> addr:%x l3id:%x b_stid:%x layer_id:%x\n", bc->addr, bc->l3_id, bc->b_stid, bc->layer_id);
+
+ cb_log(4, stack->port, " --> facility:%s out_facility:%s\n",fac2str(bc->fac_in.Function),fac2str(bc->fac_out.Function));
+
+ cb_log(5, stack->port, " --> urate:%d rate:%d mode:%d user1:%d\n", bc->urate, bc->rate, bc->mode,bc->user1);
+
+ cb_log(5, stack->port, " --> bc:%x h:%d sh:%d\n", bc, bc->holded, bc->stack_holder);
+}
+
+void misdn_send_lock(struct misdn_bchannel *bc);
+void misdn_send_unlock(struct misdn_bchannel *bc);
+
+#define RETURN(a,b) {retval=a; goto b;}
+
+void misdn_send_lock(struct misdn_bchannel *bc)
+{
+ //cb_log(0,bc->port,"Locking bc->pid:%d\n", bc->pid);
+ if (bc->send_lock)
+ pthread_mutex_lock(&bc->send_lock->lock);
+}
+
+void misdn_send_unlock(struct misdn_bchannel *bc)
+{
+ //cb_log(0,bc->port,"UnLocking bc->pid:%d\n", bc->pid);
+ if (bc->send_lock)
+ pthread_mutex_unlock(&bc->send_lock->lock);
+}
+
+int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event )
+{
+ msg_t *msg;
+ int retval=0;
+ struct misdn_stack *stack;
+
+ if (!bc) RETURN(-1,OUT_POST_UNLOCK);
+
+ stack = get_stack_by_bc(bc);
+
+ if (!stack) {
+ cb_log(0,bc->port,"SENDEVENT: no Stack for event:%s oad:%s dad:%s \n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad);
+ RETURN(-1,OUT);
+ }
+
+ misdn_send_lock(bc);
+
+
+ cb_log(6,stack->port,"SENDEVENT: stack->nt:%d stack->uperid:%x\n",stack->nt, stack->upper_id);
+
+ if ( stack->nt && !stack->l1link) {
+ /** Queue Event **/
+ bc->evq=event;
+ cb_log(1, stack->port, "Queueing Event %s because L1 is down (btw. Activating L1)\n", isdn_get_info(msgs_g, event, 0));
+ misdn_lib_get_l1_up(stack);
+ RETURN(0,OUT);
+ }
+
+ cb_log(1, stack->port, "I SEND:%s oad:%s dad:%s pid:%d\n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad, bc->pid);
+ cb_log(4, stack->port, " --> bc_state:%s\n",bc_state2str(bc->bc_state));
+ misdn_lib_log_ies(bc);
+
+ switch (event) {
+ case EVENT_SETUP:
+ if (create_process(glob_mgr->midev, bc)<0) {
+ cb_log(0, stack->port, " No free channel at the moment @ send_event\n");
+
+ RETURN(-ENOCHAN,OUT);
+ }
+ break;
+
+ case EVENT_PROGRESS:
+ case EVENT_ALERTING:
+ case EVENT_PROCEEDING:
+ case EVENT_SETUP_ACKNOWLEDGE:
+ case EVENT_CONNECT:
+ if (!stack->nt) break;
+
+ case EVENT_RETRIEVE_ACKNOWLEDGE:
+
+ if (stack->nt) {
+ if (bc->channel <=0 ) { /* else we have the channel already */
+ if (find_free_chan_in_stack(stack, bc, 0, 0)<0) {
+ cb_log(0, stack->port, " No free channel at the moment\n");
+ /*FIXME: add disconnect*/
+ RETURN(-ENOCHAN,OUT);
+ }
+ }
+ /* Its that i generate channels */
+ }
+
+ retval=setup_bc(bc);
+ if (retval == -EINVAL) {
+ cb_log(0,bc->port,"send_event: setup_bc failed\n");
+ }
+
+ if (misdn_cap_is_speech(bc->capability)) {
+ if ((event==EVENT_CONNECT)||(event==EVENT_RETRIEVE_ACKNOWLEDGE)) {
+ if ( *bc->crypt_key ) {
+ cb_log(4, stack->port, " --> ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad);
+
+ manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) );
+ }
+
+ if (!bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0);
+ manager_ec_enable(bc);
+
+ if (bc->txgain != 0) {
+ cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain);
+ manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain);
+ }
+
+ if ( bc->rxgain != 0 ) {
+ cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain);
+ manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain);
+ }
+ }
+ }
+ break;
+
+ case EVENT_HOLD_ACKNOWLEDGE:
+ {
+ struct misdn_bchannel *holded_bc=malloc(sizeof(struct misdn_bchannel));
+ if (!holded_bc) {
+ cb_log(0,bc->port, "Could not allocate holded_bc!!!\n");
+ RETURN(-1,OUT);
+ }
+
+ /*backup the bc*/
+ memcpy(holded_bc,bc,sizeof(struct misdn_bchannel));
+ holded_bc->holded=1;
+ bc_state_change(holded_bc,BCHAN_CLEANED);
+
+ stack_holder_add(stack,holded_bc);
+
+ /*kill the bridge and clean the bchannel*/
+ if (stack->nt) {
+ int channel;
+ if (bc->bc_state == BCHAN_BRIDGED) {
+ struct misdn_bchannel *bc2;
+
+ misdn_split_conf(bc,bc->conf_id);
+ bc2 = find_bc_by_confid(bc->conf_id);
+ if (!bc2) {
+ cb_log(0,bc->port,"We have no second bc in bridge???\n");
+ } else {
+ misdn_split_conf(bc2,bc->conf_id);
+ }
+ }
+
+ channel = bc->channel;
+
+ empty_bc(bc);
+ clean_up_bc(bc);
+
+ if (channel>0)
+ empty_chan_in_stack(stack,channel);
+
+ bc->in_use=0;
+ }
+
+ }
+ break;
+
+ /* finishing the channel eh ? */
+ case EVENT_DISCONNECT:
+ if (!bc->need_disconnect) {
+ cb_log(0,bc->port," --> we have already send Disconnect\n");
+ RETURN(-1,OUT);
+ }
+
+ bc->need_disconnect=0;
+ break;
+ case EVENT_RELEASE:
+ if (!bc->need_release) {
+ cb_log(0,bc->port," --> we have already send Release\n");
+ RETURN(-1,OUT);
+ }
+ bc->need_disconnect=0;
+ bc->need_release=0;
+ break;
+ case EVENT_RELEASE_COMPLETE:
+ if (!bc->need_release_complete) {
+ cb_log(0,bc->port," --> we have already send Release_complete\n");
+ RETURN(-1,OUT);
+ }
+ bc->need_disconnect=0;
+ bc->need_release=0;
+ bc->need_release_complete=0;
+
+ if (!stack->nt) {
+ /*create clenaup in TE*/
+ int channel=bc->channel;
+
+ int tmpcause=bc->cause;
+ int tmp_out_cause=bc->out_cause;
+ empty_bc(bc);
+ bc->cause=tmpcause;
+ bc->out_cause=tmp_out_cause;
+ clean_up_bc(bc);
+
+ if (channel>0)
+ empty_chan_in_stack(stack,channel);
+
+ bc->in_use=0;
+ }
+ break;
+
+ case EVENT_CONNECT_ACKNOWLEDGE:
+
+ if ( bc->nt || misdn_cap_is_speech(bc->capability)) {
+ int retval=setup_bc(bc);
+ if (retval == -EINVAL){
+ cb_log(0,bc->port,"send_event: setup_bc failed\n");
+
+ }
+ }
+
+ if (misdn_cap_is_speech(bc->capability)) {
+ if ( !bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0);
+ manager_ec_enable(bc);
+
+ if ( bc->txgain != 0 ) {
+ cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain);
+ manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain);
+ }
+ if ( bc->rxgain != 0 ) {
+ cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain);
+ manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Later we should think about sending bchannel data directly to misdn. */
+ msg = isdn_msg_build_event(msgs_g, bc, event, stack->nt);
+ msg_queue_tail(&stack->downqueue, msg);
+ sem_post(&glob_mgr->new_msg);
+
+OUT:
+ misdn_send_unlock(bc);
+
+OUT_POST_UNLOCK:
+ return retval;
+}
+
+
+static int handle_err(msg_t *msg)
+{
+ iframe_t *frm = (iframe_t*) msg->data;
+
+
+ if (!frm->addr) {
+ static int cnt=0;
+ if (!cnt)
+ cb_log(0,0,"mISDN Msg without Address pr:%x dinfo:%x\n",frm->prim,frm->dinfo);
+ cnt++;
+ if (cnt>100) {
+ cb_log(0,0,"mISDN Msg without Address pr:%x dinfo:%x (already more than 100 of them)\n",frm->prim,frm->dinfo);
+ cnt=0;
+ }
+
+ free_msg(msg);
+ return 1;
+
+ }
+
+ switch (frm->prim) {
+ case MGR_SETSTACK|INDICATION:
+ return handle_bchan(msg);
+ break;
+
+ case MGR_SETSTACK|CONFIRM:
+ case MGR_CLEARSTACK|CONFIRM:
+ free_msg(msg) ;
+ return 1;
+ break;
+
+ case DL_DATA|CONFIRM:
+ cb_log(4,0,"DL_DATA|CONFIRM\n");
+ free_msg(msg);
+ return 1;
+
+ case PH_CONTROL|CONFIRM:
+ cb_log(4,0,"PH_CONTROL|CONFIRM\n");
+ free_msg(msg);
+ return 1;
+
+ case DL_DATA|INDICATION:
+ {
+ int port=(frm->addr&MASTER_ID_MASK) >> 8;
+ int channel=(frm->addr&CHILD_ID_MASK) >> 16;
+ struct misdn_bchannel *bc;
+
+ /*we flush the read buffer here*/
+
+ cb_log(9,0,"BCHAN DATA without BC: addr:%x port:%d channel:%d\n",frm->addr, port,channel);
+
+ free_msg(msg);
+ return 1;
+
+
+ bc = find_bc_by_channel(port, channel);
+
+ if (!bc) {
+ struct misdn_stack *stack = find_stack_by_port(port);
+
+ if (!stack) {
+ cb_log(0,0," --> stack not found\n");
+ free_msg(msg);
+ return 1;
+ }
+
+ cb_log(0,0," --> bc not found by channel\n");
+ if (stack->l2link)
+ misdn_lib_get_l2_down(stack);
+
+ if (stack->l1link)
+ misdn_lib_get_l1_down(stack);
+
+ free_msg(msg);
+ return 1;
+ }
+
+ cb_log(3,port," --> BC in state:%s\n", bc_state2str(bc->bc_state));
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+static int queue_l2l3(msg_t *msg)
+{
+ iframe_t *frm= (iframe_t*)msg->data;
+ struct misdn_stack *stack;
+ stack=find_stack_by_addr( frm->addr );
+
+
+ if (!stack) {
+ return 0;
+ }
+
+ msg_queue_tail(&stack->upqueue, msg);
+ sem_post(&glob_mgr->new_msg);
+ return 1;
+}
+#endif
+
+int manager_isdn_handler(iframe_t *frm ,msg_t *msg)
+{
+
+ if (frm->dinfo==0xffffffff && frm->prim==(PH_DATA|CONFIRM)) {
+ cb_log(0,0,"SERIOUS BUG, dinfo == 0xffffffff, prim == PH_DATA | CONFIRM !!!!\n");
+ }
+
+ if ( ((frm->addr | ISDN_PID_BCHANNEL_BIT )>> 28 ) == 0x5) {
+ if (handle_bchan(msg)) {
+ return 0 ;
+ }
+ }
+
+#ifdef RECV_FRM_SYSLOG_DEBUG
+ syslog(LOG_NOTICE,"mISDN recv: P(%02d): ADDR:%x PRIM:%x DINFO:%x\n",stack->port, frm->addr, frm->prim, frm->dinfo);
+#endif
+
+ if (handle_timers(msg))
+ return 0 ;
+
+
+ if (handle_mgmt(msg))
+ return 0 ;
+
+ if (handle_l2(msg))
+ return 0 ;
+
+ /* Its important to handle l1 AFTER l2 */
+ if (handle_l1(msg))
+ return 0 ;
+
+ if (handle_frm_nt(msg)) {
+ return 0;
+ }
+
+ if (handle_frm(msg)) {
+ return 0;
+ }
+
+ if (handle_err(msg)) {
+ return 0 ;
+ }
+
+ cb_log(0, 0, "Unhandled Message: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo);
+ free_msg(msg);
+
+
+ return 0;
+}
+
+
+
+
+int misdn_lib_get_port_info(int port)
+{
+ msg_t *msg=alloc_msg(MAX_MSG_SIZE);
+ iframe_t *frm;
+ struct misdn_stack *stack=find_stack_by_port(port);
+ if (!msg) {
+ cb_log(0, port, "misgn_lib_get_port: alloc_msg failed!\n");
+ return -1;
+ }
+ frm=(iframe_t*)msg->data;
+ if (!stack ) {
+ cb_log(0, port, "There is no Stack for this port.\n");
+ return -1;
+ }
+ /* activate bchannel */
+ frm->prim = CC_STATUS_ENQUIRY | REQUEST;
+
+ frm->addr = stack->upper_id| FLG_MSG_DOWN;
+
+ frm->dinfo = 0;
+ frm->len = 0;
+
+ msg_queue_tail(&glob_mgr->activatequeue, msg);
+ sem_post(&glob_mgr->new_msg);
+
+
+ return 0;
+}
+
+
+int queue_cleanup_bc(struct misdn_bchannel *bc)
+{
+ msg_t *msg=alloc_msg(MAX_MSG_SIZE);
+ iframe_t *frm;
+ if (!msg) {
+ cb_log(0, bc->port, "misgn_lib_get_port: alloc_msg failed!\n");
+ return -1;
+ }
+ frm=(iframe_t*)msg->data;
+
+ /* activate bchannel */
+ frm->prim = MGR_CLEARSTACK| REQUEST;
+
+ frm->addr = bc->l3_id;
+
+ frm->dinfo = bc->port;
+ frm->len = 0;
+
+ msg_queue_tail(&glob_mgr->activatequeue, msg);
+ sem_post(&glob_mgr->new_msg);
+
+ return 0;
+
+}
+
+int misdn_lib_pid_restart(int pid)
+{
+ struct misdn_bchannel *bc=manager_find_bc_by_pid(pid);
+
+ if (bc) {
+ manager_clean_bc(bc);
+ }
+ return 0;
+}
+
+/*Sends Restart message for every bchnanel*/
+int misdn_lib_send_restart(int port, int channel)
+{
+ struct misdn_stack *stack=find_stack_by_port(port);
+ struct misdn_bchannel dummybc;
+ /*default is all channels*/
+ cb_log(0, port, "Sending Restarts on this port.\n");
+
+ misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0);
+
+ /*default is all channels*/
+ if (channel <0) {
+ dummybc.channel=-1;
+ cb_log(0, port, "Restarting and all Interfaces\n");
+ misdn_lib_send_event(&dummybc, EVENT_RESTART);
+
+ return 0;
+ }
+
+ /*if a channel is specified we restart only this one*/
+ if (channel >0) {
+ int cnt;
+ dummybc.channel=channel;
+ cb_log(0, port, "Restarting and cleaning channel %d\n",channel);
+ misdn_lib_send_event(&dummybc, EVENT_RESTART);
+ /* clean up chan in stack, to be sure we don't think it's
+ * in use anymore */
+ for (cnt=0; cnt<=stack->b_num; cnt++) {
+ if (stack->bc[cnt].channel == channel) {
+ empty_bc(&stack->bc[cnt]);
+ clean_up_bc(&stack->bc[cnt]);
+ stack->bc[cnt].in_use=0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*reinitializes the L2/L3*/
+int misdn_lib_port_restart(int port)
+{
+ struct misdn_stack *stack=find_stack_by_port(port);
+
+ cb_log(0, port, "Restarting this port.\n");
+ if (stack) {
+ cb_log(0, port, "Stack:%p\n",stack);
+
+ clear_l3(stack);
+ {
+ msg_t *msg=alloc_msg(MAX_MSG_SIZE);
+ iframe_t *frm;
+
+ if (!msg) {
+ cb_log(0, port, "port_restart: alloc_msg failed\n");
+ return -1;
+ }
+
+ frm=(iframe_t*)msg->data;
+ /* we must activate if we are deactivated */
+ /* activate bchannel */
+ frm->prim = DL_RELEASE | REQUEST;
+ frm->addr = stack->upper_id | FLG_MSG_DOWN;
+
+ frm->dinfo = 0;
+ frm->len = 0;
+ msg_queue_tail(&glob_mgr->activatequeue, msg);
+ sem_post(&glob_mgr->new_msg);
+ }
+
+ if (stack->nt)
+ misdn_lib_reinit_nt_stack(stack->port);
+
+ }
+
+ return 0;
+}
+
+
+
+sem_t handler_started;
+
+static void manager_event_handler(void *arg)
+{
+ sem_post(&handler_started);
+ while (1) {
+ struct misdn_stack *stack;
+ msg_t *msg;
+
+ /** wait for events **/
+ sem_wait(&glob_mgr->new_msg);
+
+ for (msg=msg_dequeue(&glob_mgr->activatequeue);
+ msg;
+ msg=msg_dequeue(&glob_mgr->activatequeue)
+ )
+ {
+
+ iframe_t *frm = (iframe_t*) msg->data ;
+
+ switch ( frm->prim) {
+
+ case MGR_CLEARSTACK | REQUEST:
+ /*a queued bchannel cleanup*/
+ {
+ struct misdn_stack *stack=find_stack_by_port(frm->dinfo);
+ struct misdn_bchannel *bc;
+ if (!stack) {
+ cb_log(0,0,"no stack found with port [%d]!! so we cannot cleanup the bc\n",frm->dinfo);
+ free_msg(msg);
+ break;
+ }
+
+ bc = find_bc_by_l3id(stack, frm->addr);
+ if (bc) {
+ cb_log(1,bc->port,"CLEARSTACK queued, cleaning up\n");
+ clean_up_bc(bc);
+ } else {
+ cb_log(0,stack->port,"bc could not be cleaned correctly !! addr [%x]\n",frm->addr);
+ }
+ }
+ free_msg(msg);
+ break;
+ case MGR_SETSTACK | REQUEST :
+ break;
+ default:
+ mISDN_write(glob_mgr->midev, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC);
+ free_msg(msg);
+ }
+ }
+
+ for (stack=glob_mgr->stack_list;
+ stack;
+ stack=stack->next ) {
+
+ while ( (msg=msg_dequeue(&stack->upqueue)) ) {
+ /** Handle L2/3 Signalling after bchans **/
+ if (!handle_frm_nt(msg)) {
+ /* Maybe it's TE */
+ if (!handle_frm(msg)) {
+ /* wow none! */
+ cb_log(0,stack->port,"Wow we've got a strange issue while dequeueing a Frame\n");
+ }
+ }
+ }
+
+ /* Here we should check if we really want to
+ send all the messages we've queued, lets
+ assume we've queued a Disconnect, but
+ received it already from the other side!*/
+
+ while ( (msg=msg_dequeue(&stack->downqueue)) ) {
+ if (stack->nt ) {
+ if (stack->nst.manager_l3(&stack->nst, msg))
+ cb_log(0, stack->port, "Error@ Sending Message in NT-Stack.\n");
+
+ } else {
+ iframe_t *frm = (iframe_t *)msg->data;
+ struct misdn_bchannel *bc = find_bc_by_l3id(stack, frm->dinfo);
+ if (bc)
+ send_msg(glob_mgr->midev, bc, msg);
+ else {
+ if (frm->dinfo == MISDN_ID_GLOBAL || frm->dinfo == MISDN_ID_DUMMY ) {
+ struct misdn_bchannel dummybc;
+ cb_log(5,0," --> GLOBAL/DUMMY\n");
+ misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0);
+ send_msg(glob_mgr->midev, &dummybc, msg);
+ } else {
+ cb_log(0,0,"No bc for Message\n");
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+int misdn_lib_maxports_get() { /** BE AWARE WE HAVE NO CB_LOG HERE! **/
+
+ int i = mISDN_open();
+ int max=0;
+
+ if (i<0)
+ return -1;
+
+ max = mISDN_get_stack_count(i);
+
+ mISDN_close(i);
+
+ return max;
+}
+
+
+void misdn_lib_nt_keepcalls( int kc)
+{
+#ifdef FEATURE_NET_KEEPCALLS
+ if (kc) {
+ struct misdn_stack *stack=get_misdn_stack();
+ for ( ; stack; stack=stack->next) {
+ stack->nst.feature |= FEATURE_NET_KEEPCALLS;
+ }
+ }
+#endif
+}
+
+void misdn_lib_nt_debug_init( int flags, char *file )
+{
+ int static init=0;
+ char *f;
+
+ if (!flags)
+ f=NULL;
+ else
+ f=file;
+
+ if (!init) {
+ debug_init( flags , f, f, f);
+ init=1;
+ } else {
+ debug_close();
+ debug_init( flags , f, f, f);
+ }
+}
+
+
+int misdn_lib_init(char *portlist, struct misdn_lib_iface *iface, void *user_data)
+{
+ struct misdn_lib *mgr=calloc(1, sizeof(struct misdn_lib));
+ char *tok, *tokb;
+ char plist[1024];
+ int midev;
+ int port_count=0;
+
+ cb_log = iface->cb_log;
+ cb_event = iface->cb_event;
+ cb_jb_empty = iface->cb_jb_empty;
+
+ glob_mgr = mgr;
+
+ msg_init();
+
+ misdn_lib_nt_debug_init(0,NULL);
+
+ if (!portlist || (*portlist == 0) ) return 1;
+
+ init_flip_bits();
+
+ {
+ strncpy(plist,portlist, 1024);
+ plist[1023] = 0;
+ }
+
+ memcpy(tone_425_flip,tone_425,TONE_425_SIZE);
+ flip_buf_bits(tone_425_flip,TONE_425_SIZE);
+
+ memcpy(tone_silence_flip,tone_SILENCE,TONE_SILENCE_SIZE);
+ flip_buf_bits(tone_silence_flip,TONE_SILENCE_SIZE);
+
+ midev=te_lib_init();
+ mgr->midev=midev;
+
+ port_count=mISDN_get_stack_count(midev);
+
+ msg_queue_init(&mgr->activatequeue);
+
+ if (sem_init(&mgr->new_msg, 1, 0)<0)
+ sem_init(&mgr->new_msg, 0, 0);
+
+ for (tok=strtok_r(plist," ,",&tokb );
+ tok;
+ tok=strtok_r(NULL," ,",&tokb)) {
+ int port = atoi(tok);
+ struct misdn_stack *stack;
+ static int first=1;
+ int ptp=0;
+
+ if (strstr(tok, "ptp"))
+ ptp=1;
+
+ if (port > port_count) {
+ cb_log(0, port, "Couldn't Initialize this port since we have only %d ports\n", port_count);
+ exit(1);
+ }
+ stack=stack_init(midev, port, ptp);
+
+ if (!stack) {
+ perror("init_stack");
+ exit(1);
+ }
+
+ {
+ int i;
+ for(i=0;i<=stack->b_num; i++) {
+ int r;
+ if ((r=init_bc(stack, &stack->bc[i], stack->midev,port,i, "", 1))<0) {
+ cb_log(0, port, "Got Err @ init_bc :%d\n",r);
+ exit(1);
+ }
+ }
+ }
+
+ if (stack && first) {
+ mgr->stack_list=stack;
+ first=0;
+ continue;
+ }
+
+ if (stack) {
+ struct misdn_stack * help;
+ for ( help=mgr->stack_list; help; help=help->next )
+ if (help->next == NULL) break;
+ help->next=stack;
+ }
+
+ }
+
+ if (sem_init(&handler_started, 1, 0)<0)
+ sem_init(&handler_started, 0, 0);
+
+ cb_log(8, 0, "Starting Event Handler\n");
+ pthread_create( &mgr->event_handler_thread, NULL,(void*)manager_event_handler, mgr);
+
+ sem_wait(&handler_started) ;
+ cb_log(8, 0, "Starting Event Catcher\n");
+ pthread_create( &mgr->event_thread, NULL, (void*)misdn_lib_isdn_event_catcher, mgr);
+
+ cb_log(8, 0, "Event Catcher started\n");
+
+ global_state= MISDN_INITIALIZED;
+
+ return (mgr == NULL);
+}
+
+void misdn_lib_destroy()
+{
+ struct misdn_stack *help;
+ int i;
+
+ for ( help=glob_mgr->stack_list; help; help=help->next ) {
+ for(i=0;i<=help->b_num; i++) {
+ char buf[1024];
+ mISDN_write_frame(help->midev, buf, help->bc[i].addr, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC);
+ help->bc[i].addr = 0;
+ }
+ cb_log (1, help->port, "Destroying this port.\n");
+ stack_destroy(help);
+ }
+
+ if (global_state == MISDN_INITIALIZED) {
+ cb_log(4, 0, "Killing Handler Thread\n");
+ if ( pthread_cancel(glob_mgr->event_handler_thread) == 0 ) {
+ cb_log(4, 0, "Joining Handler Thread\n");
+ pthread_join(glob_mgr->event_handler_thread, NULL);
+ }
+
+ cb_log(4, 0, "Killing Main Thread\n");
+ if ( pthread_cancel(glob_mgr->event_thread) == 0 ) {
+ cb_log(4, 0, "Joining Main Thread\n");
+ pthread_join(glob_mgr->event_thread, NULL);
+ }
+ }
+
+ cb_log(1, 0, "Closing mISDN device\n");
+ te_lib_destroy(glob_mgr->midev);
+}
+
+char *manager_isdn_get_info(enum event_e event)
+{
+ return isdn_get_info(msgs_g , event, 0);
+}
+
+void manager_bchannel_activate(struct misdn_bchannel *bc)
+{
+ char buf[128];
+
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ if (!stack) {
+ cb_log(0, bc->port, "bchannel_activate: Stack not found !");
+ return ;
+ }
+
+ /* we must activate if we are deactivated */
+ clear_ibuffer(bc->astbuf);
+
+ cb_log(5, stack->port, "$$$ Bchan Activated addr %x\n", bc->addr);
+
+ mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_ESTABLISH | REQUEST, 0,0, NULL, TIMEOUT_1SEC);
+
+ return ;
+}
+
+
+void manager_bchannel_deactivate(struct misdn_bchannel * bc)
+{
+
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+ iframe_t dact;
+ char buf[128];
+
+ switch (bc->bc_state) {
+ case BCHAN_ACTIVATED:
+ break;
+ case BCHAN_BRIDGED:
+ misdn_split_conf(bc,bc->conf_id);
+ break;
+ default:
+ cb_log( 4, bc->port,"bchan_deactivate: called but not activated\n");
+ return ;
+
+ }
+
+ cb_log(5, stack->port, "$$$ Bchan deActivated addr %x\n", bc->addr);
+
+ bc->generate_tone=0;
+
+ dact.prim = DL_RELEASE | REQUEST;
+ dact.addr = bc->addr | FLG_MSG_DOWN;
+ dact.dinfo = 0;
+ dact.len = 0;
+ mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_RELEASE|REQUEST,0,0,NULL, TIMEOUT_1SEC);
+
+ clear_ibuffer(bc->astbuf);
+
+ bc_state_change(bc,BCHAN_RELEASE);
+
+ return;
+}
+
+
+int misdn_lib_tx2misdn_frm(struct misdn_bchannel *bc, void *data, int len)
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+ char buf[4096 + mISDN_HEADER_LEN];
+ iframe_t *frm = (iframe_t*)buf;
+ int r;
+
+ switch (bc->bc_state) {
+ case BCHAN_ACTIVATED:
+ case BCHAN_BRIDGED:
+ break;
+ default:
+ cb_log(3, bc->port, "BC not yet activated (state:%s)\n",bc_state2str(bc->bc_state));
+ return -1;
+ }
+
+ frm->prim = DL_DATA|REQUEST;
+ frm->dinfo = 0;
+ frm->addr = bc->addr | FLG_MSG_DOWN ;
+
+ frm->len = len;
+ memcpy(&buf[mISDN_HEADER_LEN], data,len);
+
+ if ( misdn_cap_is_speech(bc->capability) )
+ flip_buf_bits( &buf[mISDN_HEADER_LEN], len);
+ else
+ cb_log(6, stack->port, "Writing %d data bytes\n",len);
+
+ cb_log(9, stack->port, "Writing %d bytes 2 mISDN\n",len);
+ r=mISDN_write(stack->midev, buf, frm->len + mISDN_HEADER_LEN, TIMEOUT_INFINIT);
+ return 0;
+}
+
+
+
+/*
+ * send control information to the channel (dsp-module)
+ */
+void manager_ph_control(struct misdn_bchannel *bc, int c1, int c2)
+{
+ unsigned char buffer[mISDN_HEADER_LEN+2*sizeof(int)];
+ iframe_t *ctrl = (iframe_t *)buffer; /* preload data */
+ unsigned int *d = (unsigned int*)&ctrl->data.p;
+ /*struct misdn_stack *stack=get_stack_by_bc(bc);*/
+
+ cb_log(4,bc->port,"ph_control: c1:%x c2:%x\n",c1,c2);
+
+ ctrl->prim = PH_CONTROL | REQUEST;
+ ctrl->addr = bc->addr | FLG_MSG_DOWN;
+ ctrl->dinfo = 0;
+ ctrl->len = sizeof(unsigned int)*2;
+ *d++ = c1;
+ *d++ = c2;
+ mISDN_write(glob_mgr->midev, ctrl, mISDN_HEADER_LEN+ctrl->len, TIMEOUT_1SEC);
+}
+
+/*
+ * allow live control of channel parameters
+ */
+void isdn_lib_update_rxgain (struct misdn_bchannel *bc)
+{
+ manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain);
+}
+
+void isdn_lib_update_txgain (struct misdn_bchannel *bc)
+{
+ manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain);
+}
+
+void isdn_lib_update_ec (struct misdn_bchannel *bc)
+{
+#ifdef MISDN_1_2
+ if (*bc->pipeline)
+#else
+ if (bc->ec_enable)
+#endif
+ manager_ec_enable(bc);
+ else
+ manager_ec_disable(bc);
+}
+
+void isdn_lib_stop_dtmf (struct misdn_bchannel *bc)
+{
+ manager_ph_control(bc, DTMF_TONE_STOP, 0);
+}
+
+/*
+ * send control information to the channel (dsp-module)
+ */
+void manager_ph_control_block(struct misdn_bchannel *bc, int c1, void *c2, int c2_len)
+{
+ unsigned char buffer[mISDN_HEADER_LEN+sizeof(int)+c2_len];
+ iframe_t *ctrl = (iframe_t *)buffer;
+ unsigned int *d = (unsigned int *)&ctrl->data.p;
+ /*struct misdn_stack *stack=get_stack_by_bc(bc);*/
+
+ ctrl->prim = PH_CONTROL | REQUEST;
+ ctrl->addr = bc->addr | FLG_MSG_DOWN;
+ ctrl->dinfo = 0;
+ ctrl->len = sizeof(unsigned int) + c2_len;
+ *d++ = c1;
+ memcpy(d, c2, c2_len);
+ mISDN_write(glob_mgr->midev, ctrl, mISDN_HEADER_LEN+ctrl->len, TIMEOUT_1SEC);
+}
+
+
+
+
+void manager_clean_bc(struct misdn_bchannel *bc )
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ if (bc->channel>0)
+ empty_chan_in_stack(stack, bc->channel);
+ empty_bc(bc);
+ bc->in_use=0;
+
+ cb_event(EVENT_CLEANUP, bc, NULL);
+}
+
+
+void stack_holder_add(struct misdn_stack *stack, struct misdn_bchannel *holder)
+{
+ struct misdn_bchannel *help;
+ cb_log(4,stack->port, "*HOLDER: add %x\n",holder->l3_id);
+
+ holder->stack_holder=1;
+
+ if (!stack ) return ;
+
+ holder->next=NULL;
+
+ if (!stack->holding) {
+ stack->holding = holder;
+ return;
+ }
+
+ for (help=stack->holding;
+ help;
+ help=help->next) {
+ if (!help->next) {
+ help->next=holder;
+ break;
+ }
+ }
+
+}
+
+void stack_holder_remove(struct misdn_stack *stack, struct misdn_bchannel *holder)
+{
+ struct misdn_bchannel *h1;
+
+ if (!holder->stack_holder) return;
+
+ holder->stack_holder=0;
+
+ cb_log(4,stack->port, "*HOLDER: remove %x\n",holder->l3_id);
+ if (!stack || ! stack->holding) return;
+
+ if (holder == stack->holding) {
+ stack->holding = stack->holding->next;
+ return;
+ }
+
+ for (h1=stack->holding;
+ h1;
+ h1=h1->next) {
+ if (h1->next == holder) {
+ h1->next=h1->next->next;
+ return ;
+ }
+ }
+}
+
+struct misdn_bchannel *stack_holder_find_bychan(struct misdn_stack *stack, int chan)
+{
+ struct misdn_bchannel *help;
+
+ cb_log(4,stack?stack->port:0, "*HOLDER: find_bychan %c\n", chan);
+
+ if (!stack) return NULL;
+
+ for (help=stack->holding;
+ help;
+ help=help->next) {
+ if (help->channel == chan) {
+ cb_log(4,stack->port, "*HOLDER: found_bychan bc\n");
+ return help;
+ }
+ }
+
+ cb_log(4,stack->port, "*HOLDER: find_bychan nothing\n");
+ return NULL;
+
+}
+
+struct misdn_bchannel *stack_holder_find(struct misdn_stack *stack, unsigned long l3id)
+{
+ struct misdn_bchannel *help;
+
+ cb_log(4,stack?stack->port:0, "*HOLDER: find %x\n",l3id);
+
+ if (!stack) return NULL;
+
+ for (help=stack->holding;
+ help;
+ help=help->next) {
+ if (help->l3_id == l3id) {
+ cb_log(4,stack->port, "*HOLDER: found bc\n");
+ return help;
+ }
+ }
+
+ cb_log(4,stack->port, "*HOLDER: find nothing\n");
+ return NULL;
+}
+
+
+
+void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone)
+{
+ char buf[mISDN_HEADER_LEN + 128] = "";
+ iframe_t *frm = (iframe_t*)buf;
+
+ switch(tone) {
+ case TONE_DIAL:
+ manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_DIALTONE);
+ break;
+
+ case TONE_ALERTING:
+ manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_RINGING);
+ break;
+
+ case TONE_HANGUP:
+ manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_HANGUP);
+ break;
+
+ case TONE_NONE:
+ default:
+ manager_ph_control(bc, TONE_PATT_OFF, TONE_GERMAN_HANGUP);
+ }
+
+ frm->prim=DL_DATA|REQUEST;
+ frm->addr=bc->addr|FLG_MSG_DOWN;
+ frm->dinfo=0;
+ frm->len=128;
+
+ mISDN_write(glob_mgr->midev, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC);
+}
+
+
+void manager_ec_enable(struct misdn_bchannel *bc)
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ cb_log(4, stack?stack->port:0,"ec_enable\n");
+
+ if (!misdn_cap_is_speech(bc->capability)) {
+ cb_log(1, stack?stack->port:0, " --> no speech? cannot enable EC\n");
+ } else {
+
+#ifdef MISDN_1_2
+ if (*bc->pipeline) {
+ cb_log(3, stack?stack->port:0,"Sending Control PIPELINE_CFG %s\n",bc->pipeline);
+ manager_ph_control_block(bc, PIPELINE_CFG, bc->pipeline, strlen(bc->pipeline) + 1);
+ }
+#else
+ int ec_arr[2];
+
+ if (bc->ec_enable) {
+ cb_log(3, stack?stack->port:0,"Sending Control ECHOCAN_ON taps:%d\n",bc->ec_deftaps);
+
+ switch (bc->ec_deftaps) {
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ case 512:
+ case 1024:
+ cb_log(4, stack->port, "Taps is %d\n",bc->ec_deftaps);
+ break;
+ default:
+ cb_log(0, stack->port, "Taps should be power of 2\n");
+ bc->ec_deftaps=128;
+ }
+
+ ec_arr[0]=bc->ec_deftaps;
+ ec_arr[1]=0;
+
+ manager_ph_control_block(bc, ECHOCAN_ON, ec_arr, sizeof(ec_arr));
+ }
+#endif
+ }
+}
+
+
+
+void manager_ec_disable(struct misdn_bchannel *bc)
+{
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ cb_log(4, stack?stack->port:0," --> ec_disable\n");
+
+ if (!misdn_cap_is_speech(bc->capability)) {
+ cb_log(1, stack?stack->port:0, " --> no speech? cannot disable EC\n");
+ return;
+ }
+
+#ifdef MISDN_1_2
+ manager_ph_control_block(bc, PIPELINE_CFG, "", 0);
+#else
+ if ( ! bc->ec_enable) {
+ cb_log(3, stack?stack->port:0, "Sending Control ECHOCAN_OFF\n");
+ manager_ph_control(bc, ECHOCAN_OFF, 0);
+ }
+#endif
+}
+
+struct misdn_stack* get_misdn_stack() {
+ return glob_mgr->stack_list;
+}
+
+
+
+void misdn_join_conf(struct misdn_bchannel *bc, int conf_id)
+{
+ char data[16] = "";
+
+ bc_state_change(bc,BCHAN_BRIDGED);
+ manager_ph_control(bc, CMX_RECEIVE_OFF, 0);
+ manager_ph_control(bc, CMX_CONF_JOIN, conf_id);
+
+ cb_log(3,bc->port, "Joining bc:%x in conf:%d\n",bc->addr,conf_id);
+
+ misdn_lib_tx2misdn_frm(bc, data, sizeof(data) - 1);
+}
+
+
+void misdn_split_conf(struct misdn_bchannel *bc, int conf_id)
+{
+ bc_state_change(bc,BCHAN_ACTIVATED);
+ manager_ph_control(bc, CMX_RECEIVE_ON, 0);
+ manager_ph_control(bc, CMX_CONF_SPLIT, conf_id);
+
+ cb_log(4,bc->port, "Splitting bc:%x in conf:%d\n",bc->addr,conf_id);
+}
+
+void misdn_lib_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2) {
+ int conf_id = bc1->pid + 1;
+ struct misdn_bchannel *bc_list[] = { bc1, bc2, NULL };
+ struct misdn_bchannel **bc;
+
+ cb_log(4, bc1->port, "I Send: BRIDGE from:%d to:%d\n",bc1->port,bc2->port);
+
+ for (bc=bc_list; *bc; bc++) {
+ (*bc)->conf_id=conf_id;
+ cb_log(4, (*bc)->port, " --> bc_addr:%x\n",(*bc)->addr);
+
+ switch((*bc)->bc_state) {
+ case BCHAN_ACTIVATED:
+ misdn_join_conf(*bc,conf_id);
+ break;
+ default:
+ bc_next_state_change(*bc,BCHAN_BRIDGED);
+ break;
+ }
+ }
+}
+
+void misdn_lib_split_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2)
+{
+
+ struct misdn_bchannel *bc_list[]={
+ bc1,bc2,NULL
+ };
+ struct misdn_bchannel **bc;
+
+ for (bc=bc_list; *bc; bc++) {
+ if ( (*bc)->bc_state == BCHAN_BRIDGED){
+ misdn_split_conf( *bc, (*bc)->conf_id);
+ } else {
+ cb_log( 2, (*bc)->port, "BC not bridged (state:%s) so not splitting it\n",bc_state2str((*bc)->bc_state));
+ }
+ }
+
+}
+
+
+
+void misdn_lib_echo(struct misdn_bchannel *bc, int onoff)
+{
+ cb_log(3,bc->port, " --> ECHO %s\n", onoff?"ON":"OFF");
+ manager_ph_control(bc, onoff?CMX_ECHO_ON:CMX_ECHO_OFF, 0);
+}
+
+
+
+void misdn_lib_reinit_nt_stack(int port)
+{
+ struct misdn_stack *stack=find_stack_by_port(port);
+
+ if (stack) {
+ stack->l2link=0;
+ stack->blocked=0;
+
+ cleanup_Isdnl3(&stack->nst);
+ cleanup_Isdnl2(&stack->nst);
+
+
+ memset(&stack->nst, 0, sizeof(net_stack_t));
+ memset(&stack->mgr, 0, sizeof(manager_t));
+
+ stack->mgr.nst = &stack->nst;
+ stack->nst.manager = &stack->mgr;
+
+ stack->nst.l3_manager = handle_event_nt;
+ stack->nst.device = glob_mgr->midev;
+ stack->nst.cardnr = port;
+ stack->nst.d_stid = stack->d_stid;
+
+ stack->nst.feature = FEATURE_NET_HOLD;
+ if (stack->ptp)
+ stack->nst.feature |= FEATURE_NET_PTP;
+ if (stack->pri)
+ stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID;
+
+ stack->nst.l1_id = stack->lower_id;
+ stack->nst.l2_id = stack->upper_id;
+
+ msg_queue_init(&stack->nst.down_queue);
+
+ Isdnl2Init(&stack->nst);
+ Isdnl3Init(&stack->nst);
+
+ if (!stack->ptp)
+ misdn_lib_get_l1_up(stack);
+ misdn_lib_get_l2_up(stack);
+ }
+}
+
+
diff --git a/trunk/channels/misdn/isdn_lib.h b/trunk/channels/misdn/isdn_lib.h
new file mode 100644
index 000000000..731d497b3
--- /dev/null
+++ b/trunk/channels/misdn/isdn_lib.h
@@ -0,0 +1,488 @@
+/*
+ * Chan_Misdn -- Channel Driver for Asterisk
+ *
+ * Interface to mISDN
+ *
+ * Copyright (C) 2004, Christian Richter
+ *
+ * Christian Richter <crich@beronet.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+/*! \file \brief
+ *
+ * Interface to mISDN
+ *
+ * \author Christian Richter <crich@beronet.com>
+ */
+
+#ifndef TE_LIB
+#define TE_LIB
+
+#include <mISDNuser/suppserv.h>
+
+/** For initialization usage **/
+/* typedef int ie_nothing_t ;*/
+/** end of init usage **/
+
+
+/*
+ * uncomment the following to make chan_misdn create
+ * record files in /tmp/misdn-{rx|tx}-PortChannel format
+ * */
+
+/*#define MISDN_SAVE_DATA*/
+
+#ifdef WITH_BEROEC
+typedef int beroec_t;
+
+
+enum beroec_type {
+ BEROEC_FULLBAND=0,
+ BEROEC_SUBBAND,
+ BEROEC_FASTSUBBAND
+};
+
+void beroec_init(void);
+void beroec_exit(void);
+beroec_t *beroec_new(int tail, enum beroec_type type, int anti_howl,
+ int tonedisable, int zerocoeff, int adapt, int nlp);
+
+void beroec_destroy(beroec_t *ec);
+int beroec_cancel_alaw_chunk(beroec_t *ec,
+ char *send,
+ char *receive ,
+ int len);
+
+int beroec_version(void);
+#endif
+
+
+
+enum tone_e {
+ TONE_NONE=0,
+ TONE_DIAL,
+ TONE_ALERTING,
+ TONE_FAR_ALERTING,
+ TONE_BUSY,
+ TONE_HANGUP,
+ TONE_CUSTOM,
+ TONE_FILE
+};
+
+
+
+#define MAX_BCHANS 31
+
+enum bchannel_state {
+ BCHAN_CLEANED=0,
+ BCHAN_EMPTY,
+ BCHAN_SETUP,
+ BCHAN_SETUPED,
+ BCHAN_ACTIVE,
+ BCHAN_ACTIVATED,
+ BCHAN_BRIDGE,
+ BCHAN_BRIDGED,
+ BCHAN_RELEASE,
+ BCHAN_RELEASED,
+ BCHAN_CLEAN,
+ BCHAN_CLEAN_REQUEST,
+ BCHAN_ERROR
+};
+
+
+enum misdn_err_e {
+ ENOCHAN=1
+};
+
+
+enum mISDN_NUMBER_PLAN {
+ NUMPLAN_UNINITIALIZED=-1,
+ NUMPLAN_INTERNATIONAL=0x1,
+ NUMPLAN_NATIONAL=0x2,
+ NUMPLAN_SUBSCRIBER=0x4,
+ NUMPLAN_UNKNOWN=0x0
+};
+
+
+enum event_response_e {
+ RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE,
+ RESPONSE_IGNORE_SETUP,
+ RESPONSE_RELEASE_SETUP,
+ RESPONSE_ERR,
+ RESPONSE_OK
+};
+
+
+enum event_e {
+ EVENT_NOTHING,
+ EVENT_TONE_GENERATE,
+ EVENT_BCHAN_DATA,
+ EVENT_BCHAN_ACTIVATED,
+ EVENT_BCHAN_ERROR,
+ EVENT_CLEANUP,
+ EVENT_PROCEEDING,
+ EVENT_PROGRESS,
+ EVENT_SETUP,
+ EVENT_ALERTING,
+ EVENT_CONNECT,
+ EVENT_SETUP_ACKNOWLEDGE,
+ EVENT_CONNECT_ACKNOWLEDGE ,
+ EVENT_USER_INFORMATION,
+ EVENT_SUSPEND_REJECT,
+ EVENT_RESUME_REJECT,
+ EVENT_HOLD,
+ EVENT_SUSPEND,
+ EVENT_RESUME,
+ EVENT_HOLD_ACKNOWLEDGE,
+ EVENT_SUSPEND_ACKNOWLEDGE,
+ EVENT_RESUME_ACKNOWLEDGE,
+ EVENT_HOLD_REJECT,
+ EVENT_RETRIEVE,
+ EVENT_RETRIEVE_ACKNOWLEDGE,
+ EVENT_RETRIEVE_REJECT,
+ EVENT_DISCONNECT,
+ EVENT_RESTART,
+ EVENT_RELEASE,
+ EVENT_RELEASE_COMPLETE,
+ EVENT_FACILITY,
+ EVENT_NOTIFY,
+ EVENT_STATUS_ENQUIRY,
+ EVENT_INFORMATION,
+ EVENT_STATUS,
+ EVENT_TIMEOUT,
+ EVENT_DTMF_TONE,
+ EVENT_NEW_L3ID,
+ EVENT_NEW_BC,
+ EVENT_PORT_ALARM,
+ EVENT_NEW_CHANNEL,
+ EVENT_UNKNOWN
+};
+
+
+enum ie_name_e {
+ IE_DUMMY,
+ IE_LAST
+};
+
+enum { /* bearer capability */
+ INFO_CAPABILITY_SPEECH=0,
+ INFO_CAPABILITY_AUDIO_3_1K=0x10 ,
+ INFO_CAPABILITY_AUDIO_7K=0x11 ,
+ INFO_CAPABILITY_VIDEO =0x18,
+ INFO_CAPABILITY_DIGITAL_UNRESTRICTED =0x8,
+ INFO_CAPABILITY_DIGITAL_RESTRICTED =0x09,
+ INFO_CAPABILITY_DIGITAL_UNRESTRICTED_TONES
+};
+
+enum { /* progress indicators */
+ INFO_PI_CALL_NOT_E2E_ISDN =0x01,
+ INFO_PI_CALLED_NOT_ISDN =0x02,
+ INFO_PI_CALLER_NOT_ISDN =0x03,
+ INFO_PI_CALLER_RETURNED_TO_ISDN =0x04,
+ INFO_PI_INBAND_AVAILABLE =0x08,
+ INFO_PI_DELAY_AT_INTERF =0x0a,
+ INFO_PI_INTERWORKING_WITH_PUBLIC =0x10,
+ INFO_PI_INTERWORKING_NO_RELEASE =0x11,
+ INFO_PI_INTERWORKING_NO_RELEASE_PRE_ANSWER =0x12,
+ INFO_PI_INTERWORKING_NO_RELEASE_POST_ANSWER =0x13
+};
+
+enum { /*CODECS*/
+ INFO_CODEC_ULAW=2,
+ INFO_CODEC_ALAW=3
+};
+
+
+enum layer_e {
+ L3,
+ L2,
+ L1,
+ UNKNOWN
+};
+
+
+
+struct misdn_bchannel {
+ struct send_lock *send_lock;
+
+ int dummy;
+
+ int nt;
+ int pri;
+
+ int port;
+ /** init stuff **/
+ int b_stid;
+ /* int b_addr; */
+ int layer_id;
+
+ int layer;
+
+ /*state stuff*/
+ int need_disconnect;
+ int need_release;
+ int need_release_complete;
+
+ int dec;
+ /** var stuff**/
+ int l3_id;
+ int pid;
+ int ces;
+
+ int restart_channel;
+ int channel;
+ int channel_preselected;
+
+ int in_use;
+ int cw;
+ int addr;
+
+ char * bframe;
+ int bframe_len;
+ int time_usec;
+
+
+ void *astbuf;
+
+ void *misdnbuf;
+
+ int te_choose_channel;
+ int early_bconnect;
+
+ /* dtmf digit */
+ int dtmf;
+ int send_dtmf;
+
+ /* get setup ack */
+ int need_more_infos;
+
+ /* may there be more infos ?*/
+ int sending_complete;
+
+
+ /* wether we should use jollys dsp or not */
+ int nodsp;
+
+ /* wether we should use our jitter buf system or not */
+ int nojitter;
+
+ enum mISDN_NUMBER_PLAN dnumplan;
+ enum mISDN_NUMBER_PLAN rnumplan;
+ enum mISDN_NUMBER_PLAN onumplan;
+ enum mISDN_NUMBER_PLAN cpnnumplan;
+
+ int progress_coding;
+ int progress_location;
+ int progress_indicator;
+
+ struct FacParm fac_in;
+ struct FacParm fac_out;
+
+ /* storing the current AOCD info here */
+ enum FacFunction AOCDtype;
+ union {
+ struct FacAOCDCurrency currency;
+ struct FacAOCDChargingUnit chargingUnit;
+ } AOCD;
+ int AOCD_need_export;
+
+ enum event_e evq;
+
+ /*** CRYPTING STUFF ***/
+
+ int crypt;
+ int curprx;
+ int curptx;
+ char crypt_key[255];
+
+ int crypt_state;
+
+ /*char ast_dtmf_buf[255];
+ char misdn_dtmf_buf[255]; */
+
+ /*** CRYPTING STUFF END***/
+
+ int active;
+ int upset;
+
+ int generate_tone;
+ int tone_cnt;
+
+ enum bchannel_state bc_state;
+ enum bchannel_state next_bc_state;
+
+ int conf_id;
+
+ int holded;
+ int stack_holder;
+
+ int pres;
+ int screen;
+
+ int capability;
+ int law;
+ /** V110 Stuff **/
+ int rate;
+ int mode;
+
+ int user1;
+ int urate;
+ int hdlc;
+ /* V110 */
+
+ char display[84];
+ char msn[32];
+ char oad[32];
+ char rad[32];
+ char dad[32];
+ char cad[32];
+ char orig_dad[32];
+ char keypad[32];
+
+ char info_dad[64];
+ char infos_pending[64];
+
+/* unsigned char info_keypad[32]; */
+/* unsigned char clisub[24]; */
+/* unsigned char cldsub[24]; */
+
+ char uu[256];
+ int uulen;
+
+ int cause;
+ int out_cause;
+
+ /* struct misdn_bchannel hold_bc; */
+
+ /** list stuf **/
+
+#ifdef MISDN_1_2
+ char pipeline[128];
+#else
+ int ec_enable;
+ int ec_deftaps;
+#endif
+
+ int channel_found;
+
+ int orig;
+
+ int txgain;
+ int rxgain;
+
+ struct misdn_bchannel *next;
+};
+
+
+enum event_response_e (*cb_event) (enum event_e event, struct misdn_bchannel *bc, void *user_data);
+void (*cb_log) (int level, int port, char *tmpl, ...);
+int (*cb_jb_empty)(struct misdn_bchannel *bc, char *buffer, int len);
+
+struct misdn_lib_iface {
+ enum event_response_e (*cb_event)(enum event_e event, struct misdn_bchannel *bc, void *user_data);
+ void (*cb_log)(int level, int port, char *tmpl, ...);
+ int (*cb_jb_empty)(struct misdn_bchannel *bc, char *buffer, int len);
+};
+
+/***** USER IFACE **********/
+
+void misdn_lib_nt_keepcalls(int kc);
+
+void misdn_lib_nt_debug_init( int flags, char *file );
+
+int misdn_lib_init(char *portlist, struct misdn_lib_iface* iface, void *user_data);
+int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event );
+void misdn_lib_destroy(void);
+
+void misdn_lib_isdn_l1watcher(int port);
+
+void misdn_lib_log_ies(struct misdn_bchannel *bc);
+
+char *manager_isdn_get_info(enum event_e event);
+
+void misdn_lib_transfer(struct misdn_bchannel* holded_bc);
+
+struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec);
+
+void manager_bchannel_activate(struct misdn_bchannel *bc);
+void manager_bchannel_deactivate(struct misdn_bchannel * bc);
+
+int misdn_lib_tx2misdn_frm(struct misdn_bchannel *bc, void *data, int len);
+
+void manager_ph_control(struct misdn_bchannel *bc, int c1, int c2);
+
+void isdn_lib_update_rxgain (struct misdn_bchannel *bc);
+void isdn_lib_update_txgain (struct misdn_bchannel *bc);
+void isdn_lib_update_ec (struct misdn_bchannel *bc);
+void isdn_lib_stop_dtmf (struct misdn_bchannel *bc);
+
+int misdn_lib_port_restart(int port);
+int misdn_lib_pid_restart(int pid);
+int misdn_lib_send_restart(int port, int channel);
+
+int misdn_lib_get_port_info(int port);
+
+int misdn_lib_is_port_blocked(int port);
+int misdn_lib_port_block(int port);
+int misdn_lib_port_unblock(int port);
+
+int misdn_lib_port_is_pri(int port);
+int misdn_lib_port_is_nt(int port);
+
+int misdn_lib_port_up(int port, int notcheck);
+
+int misdn_lib_get_port_down(int port);
+
+int misdn_lib_get_port_up (int port) ;
+
+int misdn_lib_maxports_get(void) ;
+
+void misdn_lib_release(struct misdn_bchannel *bc);
+
+int misdn_cap_is_speech(int cap);
+int misdn_inband_avail(struct misdn_bchannel *bc);
+
+void manager_ec_enable(struct misdn_bchannel *bc);
+void manager_ec_disable(struct misdn_bchannel *bc);
+
+void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone);
+
+void get_show_stack_details(int port, char *buf);
+
+
+void misdn_lib_tone_generator_start(struct misdn_bchannel *bc);
+void misdn_lib_tone_generator_stop(struct misdn_bchannel *bc);
+
+
+void misdn_lib_setup_bc(struct misdn_bchannel *bc);
+
+void misdn_lib_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2);
+void misdn_lib_split_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2);
+
+void misdn_lib_echo(struct misdn_bchannel *bc, int onoff);
+
+int misdn_lib_is_ptp(int port);
+int misdn_lib_get_maxchans(int port);
+
+void misdn_lib_reinit_nt_stack(int port);
+
+#define PRI_TRANS_CAP_SPEECH 0x0
+#define PRI_TRANS_CAP_DIGITAL 0x08
+#define PRI_TRANS_CAP_RESTRICTED_DIGITAL 0x09
+#define PRI_TRANS_CAP_3_1K_AUDIO 0x10
+#define PRI_TRANS_CAP_7K_AUDIO 0x11
+
+
+
+char *bc_state2str(enum bchannel_state state);
+void bc_state_change(struct misdn_bchannel *bc, enum bchannel_state state);
+
+void misdn_dump_chanlist(void);
+
+void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel);
+
+
+#endif
diff --git a/trunk/channels/misdn/isdn_lib_intern.h b/trunk/channels/misdn/isdn_lib_intern.h
new file mode 100644
index 000000000..725ef963f
--- /dev/null
+++ b/trunk/channels/misdn/isdn_lib_intern.h
@@ -0,0 +1,124 @@
+#ifndef ISDN_LIB_INTERN
+#define ISDN_LIB_INTERN
+
+
+#include <mISDNuser/mISDNlib.h>
+#include <mISDNuser/isdn_net.h>
+#include <mISDNuser/l3dss1.h>
+#include <mISDNuser/net_l3.h>
+
+#include <pthread.h>
+
+#include "isdn_lib.h"
+
+#ifndef MISDNUSER_VERSION_CODE
+#error "You need a newer version of mISDNuser ..."
+#elif MISDNUSER_VERSION_CODE < MISDNUSER_VERSION(1, 0, 3)
+#error "You need a newer version of mISDNuser ..."
+#endif
+
+
+#define QI_ELEMENT(a) a.off
+
+
+#ifndef mISDNUSER_HEAD_SIZE
+
+#define mISDNUSER_HEAD_SIZE (sizeof(mISDNuser_head_t))
+/*#define mISDNUSER_HEAD_SIZE (sizeof(mISDN_head_t))*/
+#endif
+
+
+ibuffer_t *astbuf;
+ibuffer_t *misdnbuf;
+
+struct send_lock {
+ pthread_mutex_t lock;
+};
+
+
+struct isdn_msg {
+ unsigned long misdn_msg;
+
+ enum layer_e layer;
+ enum event_e event;
+
+ void (*msg_parser)(struct isdn_msg *msgs, msg_t *msg, struct misdn_bchannel *bc, int nt);
+ msg_t *(*msg_builder)(struct isdn_msg *msgs, struct misdn_bchannel *bc, int nt);
+ char *info;
+
+} ;
+
+/* for isdn_msg_parser.c */
+msg_t *create_l3msg(int prim, int mt, int dinfo , int size, int nt);
+
+
+
+struct misdn_stack {
+ /** is first element because &nst equals &mISDNlist **/
+ net_stack_t nst;
+ manager_t mgr;
+
+ int d_stid;
+
+ int b_num;
+
+ int b_stids[MAX_BCHANS + 1];
+
+ int ptp;
+
+ int l2upcnt;
+
+ int l2_id;
+ int lower_id;
+ int upper_id;
+
+
+ int blocked;
+
+ int l2link;
+
+ time_t l2establish;
+
+ int l1link;
+
+ int restart_sent;
+
+ int midev;
+
+ int nt;
+
+ int pri;
+
+
+ int procids[0x100+1];
+
+ msg_queue_t downqueue;
+ msg_queue_t upqueue;
+ int busy;
+
+ int port;
+ struct misdn_bchannel bc[MAX_BCHANS + 1];
+
+ struct misdn_bchannel* bc_list;
+
+ int channels[MAX_BCHANS + 1];
+
+
+ struct misdn_bchannel *holding; /* Queue which holds holded channels :) */
+
+ struct misdn_stack *next;
+};
+
+
+struct misdn_stack* get_stack_by_bc(struct misdn_bchannel *bc);
+
+int isdn_msg_get_index(struct isdn_msg msgs[], msg_t *frm, int nt);
+enum event_e isdn_msg_get_event(struct isdn_msg msgs[], msg_t *frm, int nt);
+int isdn_msg_parse_event(struct isdn_msg msgs[], msg_t *frm, struct misdn_bchannel *bc, int nt);
+char * isdn_get_info(struct isdn_msg msgs[], enum event_e event, int nt);
+msg_t * isdn_msg_build_event(struct isdn_msg msgs[], struct misdn_bchannel *bc, enum event_e event, int nt);
+int isdn_msg_get_index_by_event(struct isdn_msg msgs[], enum event_e event, int nt);
+char * isdn_msg_get_info(struct isdn_msg msgs[], msg_t *msg, int nt);
+
+
+#endif
diff --git a/trunk/channels/misdn/isdn_msg_parser.c b/trunk/channels/misdn/isdn_msg_parser.c
new file mode 100644
index 000000000..ebaf6a92f
--- /dev/null
+++ b/trunk/channels/misdn/isdn_msg_parser.c
@@ -0,0 +1,1353 @@
+/*
+ * Chan_Misdn -- Channel Driver for Asterisk
+ *
+ * Interface to mISDN
+ *
+ * Copyright (C) 2004, Christian Richter
+ *
+ * Christian Richter <crich@beronet.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+/*! \file \brief
+ * Interface to mISDN - message parser
+ * \author Christian Richter <crich@beronet.com>
+ */
+
+
+
+#include "isdn_lib_intern.h"
+
+
+#include "isdn_lib.h"
+
+#include "ie.c"
+
+
+static void set_channel(struct misdn_bchannel *bc, int channel)
+{
+
+ cb_log(3,bc->port,"set_channel: bc->channel:%d channel:%d\n", bc->channel, channel);
+
+
+ if (channel==0xff) {
+ /* any channel */
+ channel=-1;
+ }
+
+ /* ALERT: is that everytime true ? */
+ if (channel > 0 && bc->nt ) {
+
+ if (bc->channel && ( bc->channel != 0xff) ) {
+ cb_log(0,bc->port,"We already have a channel (%d)\n", bc->channel);
+ } else {
+ bc->channel = channel;
+ cb_event(EVENT_NEW_CHANNEL,bc,NULL);
+ }
+ }
+
+ if (channel > 0 && !bc->nt ) {
+ bc->channel = channel;
+ cb_event(EVENT_NEW_CHANNEL,bc,NULL);
+ }
+}
+
+static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ CALL_PROCEEDING_t *proceeding=(CALL_PROCEEDING_t*)((unsigned long)msg->data+ HEADER_LEN);
+ //struct misdn_stack *stack=get_stack_by_bc(bc);
+
+ {
+ int exclusive, channel;
+ dec_ie_channel_id(proceeding->CHANNEL_ID, (Q931_info_t *)proceeding, &exclusive, &channel, nt,bc);
+
+ set_channel(bc,channel);
+
+ }
+
+ dec_ie_progress(proceeding->PROGRESS, (Q931_info_t *)proceeding, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc);
+
+
+#ifdef DEBUG
+ printf("Parsing PROCEEDING Msg\n");
+#endif
+}
+static msg_t *build_proceeding (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ CALL_PROCEEDING_t *proceeding;
+ msg_t *msg =(msg_t*)create_l3msg(CC_PROCEEDING | REQUEST, MT_CALL_PROCEEDING, bc?bc->l3_id:-1, sizeof(CALL_PROCEEDING_t) ,nt);
+
+ proceeding=(CALL_PROCEEDING_t*)((msg->data+HEADER_LEN));
+
+ enc_ie_channel_id(&proceeding->CHANNEL_ID, msg, 1,bc->channel, nt,bc);
+
+ if (nt)
+ enc_ie_progress(&proceeding->PROGRESS, msg, 0, nt?1:5, 8, nt,bc);
+
+
+#ifdef DEBUG
+ printf("Building PROCEEDING Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_alerting (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ ALERTING_t *alerting=(ALERTING_t*)((unsigned long)(msg->data+HEADER_LEN));
+ //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN);
+
+ dec_ie_progress(alerting->PROGRESS, (Q931_info_t *)alerting, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc);
+
+#ifdef DEBUG
+ printf("Parsing ALERTING Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ ALERTING_t *alerting;
+ msg_t *msg =(msg_t*)create_l3msg(CC_ALERTING | REQUEST, MT_ALERTING, bc?bc->l3_id:-1, sizeof(ALERTING_t) ,nt);
+
+ alerting=(ALERTING_t*)((msg->data+HEADER_LEN));
+
+ enc_ie_channel_id(&alerting->CHANNEL_ID, msg, 1,bc->channel, nt,bc);
+
+ if (nt)
+ enc_ie_progress(&alerting->PROGRESS, msg, 0, nt?1:5, 8, nt,bc);
+#ifdef DEBUG
+ printf("Building ALERTING Msg\n");
+#endif
+ return msg;
+}
+
+
+static void parse_progress (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ PROGRESS_t *progress=(PROGRESS_t*)((unsigned long)(msg->data+HEADER_LEN));
+ //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN);
+
+ dec_ie_progress(progress->PROGRESS, (Q931_info_t *)progress, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc);
+
+#ifdef DEBUG
+ printf("Parsing PROGRESS Msg\n");
+#endif
+}
+
+static msg_t *build_progress (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ PROGRESS_t *progress;
+ msg_t *msg =(msg_t*)create_l3msg(CC_PROGRESS | REQUEST, MT_PROGRESS, bc?bc->l3_id:-1, sizeof(PROGRESS_t) ,nt);
+
+ progress=(PROGRESS_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building PROGRESS Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ SETUP_t *setup= (SETUP_t*)((unsigned long)msg->data+HEADER_LEN);
+ Q931_info_t *qi=(Q931_info_t*)((unsigned long)msg->data+HEADER_LEN);
+
+#ifdef DEBUG
+ printf("Parsing SETUP Msg\n");
+#endif
+ {
+ int type,plan,present, screen;
+ char id[32];
+ dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, id, sizeof(id)-1, nt,bc);
+
+ bc->onumplan=type;
+ strcpy(bc->oad, id);
+ switch (present) {
+ case 0:
+ bc->pres=0; /* screened */
+ break;
+ case 1:
+ bc->pres=1; /* not screened */
+ break;
+ default:
+ bc->pres=0;
+ }
+ switch (screen) {
+ case 0:
+ break;
+ default:
+ ;
+ }
+ }
+ {
+ int type, plan;
+ char number[32];
+ dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *)setup, &type, &plan, number, sizeof(number)-1, nt,bc);
+ strcpy(bc->dad, number);
+ bc->dnumplan=type;
+ }
+ {
+ char keypad[32];
+ dec_ie_keypad(setup->KEYPAD, (Q931_info_t *)setup, keypad, sizeof(keypad)-1, nt,bc);
+ strcpy(bc->keypad, keypad);
+ }
+
+ {
+ dec_ie_complete(setup->COMPLETE, (Q931_info_t *)setup, &bc->sending_complete, nt,bc);
+
+ }
+
+ {
+ int type, plan, present, screen, reason;
+ char id[32];
+ dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *)setup, &type, &plan, &present, &screen, &reason, id, sizeof(id)-1, nt,bc);
+
+ strcpy(bc->rad, id);
+ bc->rnumplan=type;
+ }
+ {
+ int coding, capability, mode, rate, multi, user, async, urate, stopbits, dbits, parity;
+ dec_ie_bearer(setup->BEARER, (Q931_info_t *)setup, &coding, &capability, &mode, &rate, &multi, &user, &async, &urate, &stopbits, &dbits, &parity, nt,bc);
+ switch (capability) {
+ case -1: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED;
+ break;
+ case 0: bc->capability=INFO_CAPABILITY_SPEECH;
+ break;
+ case 18: bc->capability=INFO_CAPABILITY_VIDEO;
+ break;
+ case 8: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED;
+ bc->user1 = user;
+ bc->urate = urate;
+
+ bc->rate = rate;
+ bc->mode = mode;
+ break;
+ case 9: bc->capability=INFO_CAPABILITY_DIGITAL_RESTRICTED;
+ break;
+ default:
+ break;
+ }
+
+ switch(user) {
+ case 2:
+ bc->law=INFO_CODEC_ULAW;
+ break;
+ case 3:
+ bc->law=INFO_CODEC_ALAW;
+ break;
+ default:
+ bc->law=INFO_CODEC_ALAW;
+
+ }
+
+ bc->capability=capability;
+ }
+ {
+ int exclusive, channel;
+ dec_ie_channel_id(setup->CHANNEL_ID, (Q931_info_t *)setup, &exclusive, &channel, nt,bc);
+
+ set_channel(bc,channel);
+ }
+
+ {
+ int protocol ;
+ dec_ie_useruser(setup->USER_USER, (Q931_info_t *)setup, &protocol, bc->uu, &bc->uulen, nt,bc);
+ if (bc->uulen) cb_log(1,bc->port,"USERUESRINFO:%s\n",bc->uu);
+ else
+ cb_log(1,bc->port,"NO USERUESRINFO\n");
+ }
+
+ dec_ie_progress(setup->PROGRESS, (Q931_info_t *)setup, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc);
+
+}
+
+#define ANY_CHANNEL 0xff /* IE attribut for 'any channel' */
+static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ SETUP_t *setup;
+ msg_t *msg =(msg_t*)create_l3msg(CC_SETUP | REQUEST, MT_SETUP, bc?bc->l3_id:-1, sizeof(SETUP_t) ,nt);
+
+ setup=(SETUP_t*)((msg->data+HEADER_LEN));
+
+ if (bc->channel == 0 || bc->channel == ANY_CHANNEL || bc->channel==-1)
+ enc_ie_channel_id(&setup->CHANNEL_ID, msg, 0, bc->channel, nt,bc);
+ else
+ enc_ie_channel_id(&setup->CHANNEL_ID, msg, 1, bc->channel, nt,bc);
+
+
+ {
+ int type=bc->onumplan,plan=1,present=bc->pres,screen=bc->screen;
+ enc_ie_calling_pn(&setup->CALLING_PN, msg, type, plan, present,
+ screen, bc->oad, nt, bc);
+ }
+
+ {
+ if (bc->dad[0])
+ enc_ie_called_pn(&setup->CALLED_PN, msg, bc->dnumplan, 1, bc->dad, nt,bc);
+ }
+
+ {
+ if (bc->rad[0])
+ enc_ie_redir_nr(&setup->REDIR_NR, msg, 1, 1, bc->pres, bc->screen, 0, bc->rad, nt,bc);
+ }
+
+ {
+ if (bc->keypad[0])
+ enc_ie_keypad(&setup->KEYPAD, msg, bc->keypad, nt,bc);
+ }
+
+
+ if (*bc->display) {
+ enc_ie_display(&setup->DISPLAY, msg, bc->display, nt,bc);
+ }
+
+ {
+ int coding=0, capability, mode=0 /* 2 for packet ! */
+ ,user, rate=0x10;
+
+ switch (bc->law) {
+ case INFO_CODEC_ULAW: user=2;
+ break;
+ case INFO_CODEC_ALAW: user=3;
+ break;
+ default:
+ user=3;
+ }
+
+ switch (bc->capability) {
+ case INFO_CAPABILITY_SPEECH: capability = 0;
+ break;
+ case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: capability = 8;
+ user=-1;
+ mode=bc->mode;
+ rate=bc->rate;
+ break;
+ case INFO_CAPABILITY_DIGITAL_RESTRICTED: capability = 9;
+ user=-1;
+ break;
+ default:
+ capability=bc->capability;
+ }
+
+
+
+ enc_ie_bearer(&setup->BEARER, msg, coding, capability, mode, rate, -1, user, nt,bc);
+ }
+
+ if (bc->sending_complete) {
+ enc_ie_complete(&setup->COMPLETE,msg, bc->sending_complete, nt, bc);
+ }
+
+ if (bc->uulen) {
+ int protocol=4;
+ enc_ie_useruser(&setup->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc);
+ cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu);
+ }
+
+#ifdef DEBUG
+ printf("Building SETUP Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ CONNECT_t *connect=(CONNECT_t*)((unsigned long)(msg->data+HEADER_LEN));
+
+ int plan,pres,screen;
+
+ bc->ces = connect->ces;
+ bc->ces = connect->ces;
+
+ dec_ie_progress(connect->PROGRESS, (Q931_info_t *)connect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc);
+
+ dec_ie_connected_pn(connect->CONNECT_PN,(Q931_info_t *)connect, &bc->cpnnumplan, &plan, &pres, &screen, bc->cad, 31, nt, bc);
+
+ /*
+ cb_log(1,bc->port,"CONNETED PN: %s cpn_dialplan:%d\n", connected_pn, type);
+ */
+
+#ifdef DEBUG
+ printf("Parsing CONNECT Msg\n");
+#endif
+}
+
+static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ CONNECT_t *connect;
+ msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | REQUEST, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_t) ,nt);
+
+ cb_log(6,bc->port,"BUILD_CONNECT: bc:%p bc->l3id:%d, nt:%d\n",bc,bc->l3_id,nt);
+
+ connect=(CONNECT_t*)((msg->data+HEADER_LEN));
+
+ if (nt) {
+ time_t now;
+ time(&now);
+ enc_ie_date(&connect->DATE, msg, now, nt,bc);
+ }
+
+ {
+ int type=bc->cpnnumplan, plan=1, present=2, screen=0;
+ enc_ie_connected_pn(&connect->CONNECT_PN, msg, type,plan, present, screen, bc->cad, nt , bc);
+ }
+
+#ifdef DEBUG
+ printf("Building CONNECT Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ SETUP_ACKNOWLEDGE_t *setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((unsigned long)(msg->data+HEADER_LEN));
+
+ {
+ int exclusive, channel;
+ dec_ie_channel_id(setup_acknowledge->CHANNEL_ID, (Q931_info_t *)setup_acknowledge, &exclusive, &channel, nt,bc);
+
+
+ set_channel(bc, channel);
+ }
+
+ dec_ie_progress(setup_acknowledge->PROGRESS, (Q931_info_t *)setup_acknowledge, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc);
+#ifdef DEBUG
+ printf("Parsing SETUP_ACKNOWLEDGE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_setup_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ SETUP_ACKNOWLEDGE_t *setup_acknowledge;
+ msg_t *msg =(msg_t*)create_l3msg(CC_SETUP_ACKNOWLEDGE | REQUEST, MT_SETUP_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SETUP_ACKNOWLEDGE_t) ,nt);
+
+ setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN));
+
+ enc_ie_channel_id(&setup_acknowledge->CHANNEL_ID, msg, 1,bc->channel, nt,bc);
+
+ if (nt)
+ enc_ie_progress(&setup_acknowledge->PROGRESS, msg, 0, nt?1:5, 8, nt,bc);
+
+#ifdef DEBUG
+ printf("Building SETUP_ACKNOWLEDGE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_connect_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing CONNECT_ACKNOWLEDGE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_connect_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ CONNECT_ACKNOWLEDGE_t *connect_acknowledge;
+ msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | RESPONSE, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_ACKNOWLEDGE_t) ,nt);
+
+ connect_acknowledge=(CONNECT_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN));
+
+ enc_ie_channel_id(&connect_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc);
+
+#ifdef DEBUG
+ printf("Building CONNECT_ACKNOWLEDGE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_user_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing USER_INFORMATION Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_user_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ USER_INFORMATION_t *user_information;
+ msg_t *msg =(msg_t*)create_l3msg(CC_USER_INFORMATION | REQUEST, MT_USER_INFORMATION, bc?bc->l3_id:-1, sizeof(USER_INFORMATION_t) ,nt);
+
+ user_information=(USER_INFORMATION_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building USER_INFORMATION Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_suspend_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing SUSPEND_REJECT Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_suspend_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ SUSPEND_REJECT_t *suspend_reject;
+ msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_REJECT | REQUEST, MT_SUSPEND_REJECT, bc?bc->l3_id:-1, sizeof(SUSPEND_REJECT_t) ,nt);
+
+ suspend_reject=(SUSPEND_REJECT_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building SUSPEND_REJECT Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_resume_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing RESUME_REJECT Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_resume_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RESUME_REJECT_t *resume_reject;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_REJECT | REQUEST, MT_RESUME_REJECT, bc?bc->l3_id:-1, sizeof(RESUME_REJECT_t) ,nt);
+
+ resume_reject=(RESUME_REJECT_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building RESUME_REJECT Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_hold (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing HOLD Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_hold (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ HOLD_t *hold;
+ msg_t *msg =(msg_t*)create_l3msg(CC_HOLD | REQUEST, MT_HOLD, bc?bc->l3_id:-1, sizeof(HOLD_t) ,nt);
+
+ hold=(HOLD_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building HOLD Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_suspend (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing SUSPEND Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_suspend (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ SUSPEND_t *suspend;
+ msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND | REQUEST, MT_SUSPEND, bc?bc->l3_id:-1, sizeof(SUSPEND_t) ,nt);
+
+ suspend=(SUSPEND_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building SUSPEND Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_resume (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing RESUME Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_resume (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RESUME_t *resume;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RESUME | REQUEST, MT_RESUME, bc?bc->l3_id:-1, sizeof(RESUME_t) ,nt);
+
+ resume=(RESUME_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building RESUME Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_hold_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing HOLD_ACKNOWLEDGE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_hold_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ HOLD_ACKNOWLEDGE_t *hold_acknowledge;
+ msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_ACKNOWLEDGE | REQUEST, MT_HOLD_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(HOLD_ACKNOWLEDGE_t) ,nt);
+
+ hold_acknowledge=(HOLD_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building HOLD_ACKNOWLEDGE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_suspend_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing SUSPEND_ACKNOWLEDGE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_suspend_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ SUSPEND_ACKNOWLEDGE_t *suspend_acknowledge;
+ msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_ACKNOWLEDGE | REQUEST, MT_SUSPEND_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SUSPEND_ACKNOWLEDGE_t) ,nt);
+
+ suspend_acknowledge=(SUSPEND_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building SUSPEND_ACKNOWLEDGE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_resume_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing RESUME_ACKNOWLEDGE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_resume_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RESUME_ACKNOWLEDGE_t *resume_acknowledge;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_ACKNOWLEDGE | REQUEST, MT_RESUME_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RESUME_ACKNOWLEDGE_t) ,nt);
+
+ resume_acknowledge=(RESUME_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building RESUME_ACKNOWLEDGE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_hold_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing HOLD_REJECT Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_hold_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ HOLD_REJECT_t *hold_reject;
+ msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_REJECT | REQUEST, MT_HOLD_REJECT, bc?bc->l3_id:-1, sizeof(HOLD_REJECT_t) ,nt);
+
+ hold_reject=(HOLD_REJECT_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building HOLD_REJECT Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_retrieve (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing RETRIEVE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_retrieve (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RETRIEVE_t *retrieve;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE | REQUEST, MT_RETRIEVE, bc?bc->l3_id:-1, sizeof(RETRIEVE_t) ,nt);
+
+ retrieve=(RETRIEVE_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building RETRIEVE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_retrieve_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing RETRIEVE_ACKNOWLEDGE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_retrieve_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RETRIEVE_ACKNOWLEDGE_t *retrieve_acknowledge;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_ACKNOWLEDGE | REQUEST, MT_RETRIEVE_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RETRIEVE_ACKNOWLEDGE_t) ,nt);
+
+ retrieve_acknowledge=(RETRIEVE_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN));
+
+ enc_ie_channel_id(&retrieve_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc);
+#ifdef DEBUG
+ printf("Building RETRIEVE_ACKNOWLEDGE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_retrieve_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing RETRIEVE_REJECT Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_retrieve_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RETRIEVE_REJECT_t *retrieve_reject;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_REJECT | REQUEST, MT_RETRIEVE_REJECT, bc?bc->l3_id:-1, sizeof(RETRIEVE_REJECT_t) ,nt);
+
+ retrieve_reject=(RETRIEVE_REJECT_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building RETRIEVE_REJECT Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_disconnect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ DISCONNECT_t *disconnect=(DISCONNECT_t*)((unsigned long)(msg->data+HEADER_LEN));
+ int location;
+ int cause;
+ dec_ie_cause(disconnect->CAUSE, (Q931_info_t *)(disconnect), &location, &cause, nt,bc);
+ if (cause>0) bc->cause=cause;
+
+ dec_ie_progress(disconnect->PROGRESS, (Q931_info_t *)disconnect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc);
+#ifdef DEBUG
+ printf("Parsing DISCONNECT Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ DISCONNECT_t *disconnect;
+ msg_t *msg =(msg_t*)create_l3msg(CC_DISCONNECT | REQUEST, MT_DISCONNECT, bc?bc->l3_id:-1, sizeof(DISCONNECT_t) ,nt);
+
+ disconnect=(DISCONNECT_t*)((msg->data+HEADER_LEN));
+
+ enc_ie_cause(&disconnect->CAUSE, msg, (nt)?1:0, bc->out_cause,nt,bc);
+ if (nt) enc_ie_progress(&disconnect->PROGRESS, msg, 0, nt?1:5, 8 ,nt,bc);
+
+ if (bc->uulen) {
+ int protocol=4;
+ enc_ie_useruser(&disconnect->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc);
+ cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu);
+ }
+
+#ifdef DEBUG
+ printf("Building DISCONNECT Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_restart (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RESTART_t *restart=(RESTART_t*)((unsigned long)(msg->data+HEADER_LEN));
+
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+
+#ifdef DEBUG
+ printf("Parsing RESTART Msg\n");
+#endif
+
+ {
+ int exclusive;
+ dec_ie_channel_id(restart->CHANNEL_ID, (Q931_info_t *)restart, &exclusive, &bc->restart_channel, nt,bc);
+ cb_log(3, stack->port, "CC_RESTART Request on channel:%d on this port.\n", bc->restart_channel);
+ }
+
+}
+
+static msg_t *build_restart (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RESTART_t *restart;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RESTART | REQUEST, MT_RESTART, bc?bc->l3_id:-1, sizeof(RESTART_t) ,nt);
+
+ restart=(RESTART_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building RESTART Msg\n");
+#endif
+
+ if (bc->channel > 0) {
+ enc_ie_channel_id(&restart->CHANNEL_ID, msg, 1,bc->channel, nt,bc);
+ enc_ie_restart_ind(&restart->RESTART_IND, msg, 0x80, nt, bc);
+ } else {
+ enc_ie_restart_ind(&restart->RESTART_IND, msg, 0x87, nt, bc);
+ }
+
+ cb_log(0,bc->port, "Restarting channel %d\n", bc->channel);
+ return msg;
+}
+
+static void parse_release (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RELEASE_t *release=(RELEASE_t*)((unsigned long)(msg->data+HEADER_LEN));
+ int location;
+ int cause;
+
+ dec_ie_cause(release->CAUSE, (Q931_info_t *)(release), &location, &cause, nt,bc);
+ if (cause>0) bc->cause=cause;
+#ifdef DEBUG
+ printf("Parsing RELEASE Msg\n");
+#endif
+
+
+}
+
+static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RELEASE_t *release;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE | REQUEST, MT_RELEASE, bc?bc->l3_id:-1, sizeof(RELEASE_t) ,nt);
+
+ release=(RELEASE_t*)((msg->data+HEADER_LEN));
+
+ if (bc->out_cause>= 0)
+ enc_ie_cause(&release->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc);
+
+ if (bc->uulen) {
+ int protocol=4;
+ enc_ie_useruser(&release->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc);
+ cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu);
+ }
+
+#ifdef DEBUG
+ printf("Building RELEASE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RELEASE_COMPLETE_t *release_complete=(RELEASE_COMPLETE_t*)((unsigned long)(msg->data+HEADER_LEN));
+ int location;
+ int cause;
+ iframe_t *frm = (iframe_t*) msg->data;
+
+ struct misdn_stack *stack=get_stack_by_bc(bc);
+ mISDNuser_head_t *hh;
+ hh=(mISDNuser_head_t*)msg->data;
+
+ /*hh=(mISDN_head_t*)msg->data;
+ mISDN_head_t *hh;*/
+
+ if (nt) {
+ if (hh->prim == (CC_RELEASE_COMPLETE|CONFIRM)) {
+ cb_log(0, stack->port, "CC_RELEASE_COMPLETE|CONFIRM [NT] \n");
+ return;
+ }
+ } else {
+ if (frm->prim == (CC_RELEASE_COMPLETE|CONFIRM)) {
+ cb_log(0, stack->port, "CC_RELEASE_COMPLETE|CONFIRM [TE] \n");
+ return;
+ }
+ }
+ dec_ie_cause(release_complete->CAUSE, (Q931_info_t *)(release_complete), &location, &cause, nt,bc);
+ if (cause>0) bc->cause=cause;
+
+#ifdef DEBUG
+ printf("Parsing RELEASE_COMPLETE Msg\n");
+#endif
+}
+
+static msg_t *build_release_complete (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ RELEASE_COMPLETE_t *release_complete;
+ msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, bc?bc->l3_id:-1, sizeof(RELEASE_COMPLETE_t) ,nt);
+
+ release_complete=(RELEASE_COMPLETE_t*)((msg->data+HEADER_LEN));
+
+ enc_ie_cause(&release_complete->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc);
+
+ if (bc->uulen) {
+ int protocol=4;
+ enc_ie_useruser(&release_complete->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc);
+ cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu);
+ }
+
+#ifdef DEBUG
+ printf("Building RELEASE_COMPLETE Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN;
+ FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN);
+ Q931_info_t *qi = (Q931_info_t*)(msg->data+HEADER_LEN);
+ unsigned char *p = NULL;
+ int err;
+
+#ifdef DEBUG
+ printf("Parsing FACILITY Msg\n");
+#endif
+
+ if (!bc->nt) {
+ if (qi->QI_ELEMENT(facility))
+ p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1;
+ } else {
+ p = facility->FACILITY;
+ }
+ if (!p)
+ return;
+
+ err = decodeFac(p, &(bc->fac_in));
+ if (err) {
+ cb_log(5, bc->port, "Decoding FACILITY failed! (%d)\n", err);
+ }
+}
+
+static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int len,
+ HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN;
+ unsigned char *ie_fac,
+ fac_tmp[256];
+ msg_t *msg =(msg_t*)create_l3msg(CC_FACILITY | REQUEST, MT_FACILITY, bc?bc->l3_id:-1, sizeof(FACILITY_t) ,nt);
+ FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN);
+ Q931_info_t *qi;
+
+#ifdef DEBUG
+ printf("Building FACILITY Msg\n");
+#endif
+
+ len = encodeFac(fac_tmp, &(bc->fac_out));
+ if (len <= 0)
+ return NULL;
+
+ ie_fac = msg_put(msg, len);
+ if (bc->nt) {
+ facility->FACILITY = ie_fac + 1;
+ } else {
+ qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+ qi->QI_ELEMENT(facility) = ie_fac - (unsigned char *)qi - sizeof(Q931_info_t);
+ }
+
+ memcpy(ie_fac, fac_tmp, len);
+
+ if (*bc->display) {
+ printf("Sending %s as Display\n", bc->display);
+ enc_ie_display(&facility->DISPLAY, msg, bc->display, nt,bc);
+ }
+
+ return msg;
+}
+
+static void parse_notify (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing NOTIFY Msg\n");
+#endif
+}
+
+static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ NOTIFY_t *notify;
+ msg_t *msg =(msg_t*)create_l3msg(CC_NOTIFY | REQUEST, MT_NOTIFY, bc?bc->l3_id:-1, sizeof(NOTIFY_t) ,nt);
+
+ notify=(NOTIFY_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building NOTIFY Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_status_enquiry (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing STATUS_ENQUIRY Msg\n");
+#endif
+}
+
+static msg_t *build_status_enquiry (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ STATUS_ENQUIRY_t *status_enquiry;
+ msg_t *msg =(msg_t*)create_l3msg(CC_STATUS_ENQUIRY | REQUEST, MT_STATUS_ENQUIRY, bc?bc->l3_id:-1, sizeof(STATUS_ENQUIRY_t) ,nt);
+
+ status_enquiry=(STATUS_ENQUIRY_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building STATUS_ENQUIRY Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ INFORMATION_t *information=(INFORMATION_t*)((unsigned long)(msg->data+HEADER_LEN));
+ {
+ int type, plan;
+ char number[32];
+ char keypad[32];
+ dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *)information, &type, &plan, number, sizeof(number)-1, nt, bc);
+ dec_ie_keypad(information->KEYPAD, (Q931_info_t *)information, keypad, sizeof(keypad)-1, nt, bc);
+ strcpy(bc->info_dad, number);
+ strcpy(bc->keypad,keypad);
+ }
+#ifdef DEBUG
+ printf("Parsing INFORMATION Msg\n");
+#endif
+}
+
+static msg_t *build_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ INFORMATION_t *information;
+ msg_t *msg =(msg_t*)create_l3msg(CC_INFORMATION | REQUEST, MT_INFORMATION, bc?bc->l3_id:-1, sizeof(INFORMATION_t) ,nt);
+
+ information=(INFORMATION_t*)((msg->data+HEADER_LEN));
+
+ {
+ enc_ie_called_pn(&information->CALLED_PN, msg, 0, 1, bc->info_dad, nt,bc);
+ }
+
+ {
+ if (*bc->display) {
+ printf("Sending %s as Display\n", bc->display);
+ enc_ie_display(&information->DISPLAY, msg, bc->display, nt,bc);
+ }
+ }
+
+#ifdef DEBUG
+ printf("Building INFORMATION Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_status (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ STATUS_t *status=(STATUS_t*)((unsigned long)(msg->data+HEADER_LEN));
+ int location;
+ int cause;
+
+ dec_ie_cause(status->CAUSE, (Q931_info_t *)(status), &location, &cause, nt,bc);
+ if (cause>0) bc->cause=cause;
+ ;
+
+#ifdef DEBUG
+ printf("Parsing STATUS Msg\n");
+#endif
+}
+
+static msg_t *build_status (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ STATUS_t *status;
+ msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt);
+
+ status=(STATUS_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building STATUS Msg\n");
+#endif
+ return msg;
+}
+
+static void parse_timeout (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+#ifdef DEBUG
+ printf("Parsing STATUS Msg\n");
+#endif
+}
+
+static msg_t *build_timeout (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt)
+{
+ int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN;
+ STATUS_t *status;
+ msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt);
+
+ status=(STATUS_t*)((msg->data+HEADER_LEN));
+
+#ifdef DEBUG
+ printf("Building STATUS Msg\n");
+#endif
+ return msg;
+}
+
+
+/************************************/
+
+
+
+
+/** Msg Array **/
+
+struct isdn_msg msgs_g[] = {
+ {CC_PROCEEDING,L3,EVENT_PROCEEDING,
+ parse_proceeding,build_proceeding,
+ "PROCEEDING"},
+ {CC_ALERTING,L3,EVENT_ALERTING,
+ parse_alerting,build_alerting,
+ "ALERTING"},
+ {CC_PROGRESS,L3,EVENT_PROGRESS,
+ parse_progress,build_progress,
+ "PROGRESS"},
+ {CC_SETUP,L3,EVENT_SETUP,
+ parse_setup,build_setup,
+ "SETUP"},
+ {CC_CONNECT,L3,EVENT_CONNECT,
+ parse_connect,build_connect,
+ "CONNECT"},
+ {CC_SETUP_ACKNOWLEDGE,L3,EVENT_SETUP_ACKNOWLEDGE,
+ parse_setup_acknowledge,build_setup_acknowledge,
+ "SETUP_ACKNOWLEDGE"},
+ {CC_CONNECT_ACKNOWLEDGE ,L3,EVENT_CONNECT_ACKNOWLEDGE ,
+ parse_connect_acknowledge ,build_connect_acknowledge,
+ "CONNECT_ACKNOWLEDGE "},
+ {CC_USER_INFORMATION,L3,EVENT_USER_INFORMATION,
+ parse_user_information,build_user_information,
+ "USER_INFORMATION"},
+ {CC_SUSPEND_REJECT,L3,EVENT_SUSPEND_REJECT,
+ parse_suspend_reject,build_suspend_reject,
+ "SUSPEND_REJECT"},
+ {CC_RESUME_REJECT,L3,EVENT_RESUME_REJECT,
+ parse_resume_reject,build_resume_reject,
+ "RESUME_REJECT"},
+ {CC_HOLD,L3,EVENT_HOLD,
+ parse_hold,build_hold,
+ "HOLD"},
+ {CC_SUSPEND,L3,EVENT_SUSPEND,
+ parse_suspend,build_suspend,
+ "SUSPEND"},
+ {CC_RESUME,L3,EVENT_RESUME,
+ parse_resume,build_resume,
+ "RESUME"},
+ {CC_HOLD_ACKNOWLEDGE,L3,EVENT_HOLD_ACKNOWLEDGE,
+ parse_hold_acknowledge,build_hold_acknowledge,
+ "HOLD_ACKNOWLEDGE"},
+ {CC_SUSPEND_ACKNOWLEDGE,L3,EVENT_SUSPEND_ACKNOWLEDGE,
+ parse_suspend_acknowledge,build_suspend_acknowledge,
+ "SUSPEND_ACKNOWLEDGE"},
+ {CC_RESUME_ACKNOWLEDGE,L3,EVENT_RESUME_ACKNOWLEDGE,
+ parse_resume_acknowledge,build_resume_acknowledge,
+ "RESUME_ACKNOWLEDGE"},
+ {CC_HOLD_REJECT,L3,EVENT_HOLD_REJECT,
+ parse_hold_reject,build_hold_reject,
+ "HOLD_REJECT"},
+ {CC_RETRIEVE,L3,EVENT_RETRIEVE,
+ parse_retrieve,build_retrieve,
+ "RETRIEVE"},
+ {CC_RETRIEVE_ACKNOWLEDGE,L3,EVENT_RETRIEVE_ACKNOWLEDGE,
+ parse_retrieve_acknowledge,build_retrieve_acknowledge,
+ "RETRIEVE_ACKNOWLEDGE"},
+ {CC_RETRIEVE_REJECT,L3,EVENT_RETRIEVE_REJECT,
+ parse_retrieve_reject,build_retrieve_reject,
+ "RETRIEVE_REJECT"},
+ {CC_DISCONNECT,L3,EVENT_DISCONNECT,
+ parse_disconnect,build_disconnect,
+ "DISCONNECT"},
+ {CC_RESTART,L3,EVENT_RESTART,
+ parse_restart,build_restart,
+ "RESTART"},
+ {CC_RELEASE,L3,EVENT_RELEASE,
+ parse_release,build_release,
+ "RELEASE"},
+ {CC_RELEASE_COMPLETE,L3,EVENT_RELEASE_COMPLETE,
+ parse_release_complete,build_release_complete,
+ "RELEASE_COMPLETE"},
+ {CC_FACILITY,L3,EVENT_FACILITY,
+ parse_facility,build_facility,
+ "FACILITY"},
+ {CC_NOTIFY,L3,EVENT_NOTIFY,
+ parse_notify,build_notify,
+ "NOTIFY"},
+ {CC_STATUS_ENQUIRY,L3,EVENT_STATUS_ENQUIRY,
+ parse_status_enquiry,build_status_enquiry,
+ "STATUS_ENQUIRY"},
+ {CC_INFORMATION,L3,EVENT_INFORMATION,
+ parse_information,build_information,
+ "INFORMATION"},
+ {CC_STATUS,L3,EVENT_STATUS,
+ parse_status,build_status,
+ "STATUS"},
+ {CC_TIMEOUT,L3,EVENT_TIMEOUT,
+ parse_timeout,build_timeout,
+ "TIMEOUT"},
+ {0,0,0,NULL,NULL,NULL}
+};
+
+#define msgs_max (sizeof(msgs_g)/sizeof(struct isdn_msg))
+
+/** INTERFACE FCTS ***/
+int isdn_msg_get_index(struct isdn_msg msgs[], msg_t *msg, int nt)
+{
+ int i;
+
+ if (nt){
+ mISDNuser_head_t *hh = (mISDNuser_head_t*)msg->data;
+
+ for (i=0; i< msgs_max -1; i++) {
+ if ( (hh->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i;
+ }
+
+ } else {
+ iframe_t *frm = (iframe_t*)msg->data;
+
+ for (i=0; i< msgs_max -1; i++)
+ if ( (frm->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i;
+ }
+
+ return -1;
+}
+
+int isdn_msg_get_index_by_event(struct isdn_msg msgs[], enum event_e event, int nt)
+{
+ int i;
+ for (i=0; i< msgs_max; i++)
+ if ( event == msgs[i].event) return i;
+
+ cb_log(10,0, "get_index: event not found!\n");
+
+ return -1;
+}
+
+enum event_e isdn_msg_get_event(struct isdn_msg msgs[], msg_t *msg, int nt)
+{
+ int i=isdn_msg_get_index(msgs, msg, nt);
+ if(i>=0) return msgs[i].event;
+ return EVENT_UNKNOWN;
+}
+
+char * isdn_msg_get_info(struct isdn_msg msgs[], msg_t *msg, int nt)
+{
+ int i=isdn_msg_get_index(msgs, msg, nt);
+ if(i>=0) return msgs[i].info;
+ return NULL;
+}
+
+
+char EVENT_CLEAN_INFO[] = "CLEAN_UP";
+char EVENT_DTMF_TONE_INFO[] = "DTMF_TONE";
+char EVENT_NEW_L3ID_INFO[] = "NEW_L3ID";
+char EVENT_NEW_BC_INFO[] = "NEW_BC";
+char EVENT_PORT_ALARM_INFO[] = "ALARM";
+char EVENT_NEW_CHANNEL_INFO[] = "NEW_CHANNEL";
+char EVENT_BCHAN_DATA_INFO[] = "BCHAN_DATA";
+char EVENT_BCHAN_ACTIVATED_INFO[] = "BCHAN_ACTIVATED";
+char EVENT_TONE_GENERATE_INFO[] = "TONE_GENERATE";
+char EVENT_BCHAN_ERROR_INFO[] = "BCHAN_ERROR";
+
+char * isdn_get_info(struct isdn_msg msgs[], enum event_e event, int nt)
+{
+ int i=isdn_msg_get_index_by_event(msgs, event, nt);
+
+ if(i>=0) return msgs[i].info;
+
+ if (event == EVENT_CLEANUP) return EVENT_CLEAN_INFO;
+ if (event == EVENT_DTMF_TONE) return EVENT_DTMF_TONE_INFO;
+ if (event == EVENT_NEW_L3ID) return EVENT_NEW_L3ID_INFO;
+ if (event == EVENT_NEW_BC) return EVENT_NEW_BC_INFO;
+ if (event == EVENT_NEW_CHANNEL) return EVENT_NEW_CHANNEL_INFO;
+ if (event == EVENT_BCHAN_DATA) return EVENT_BCHAN_DATA_INFO;
+ if (event == EVENT_BCHAN_ACTIVATED) return EVENT_BCHAN_ACTIVATED_INFO;
+ if (event == EVENT_TONE_GENERATE) return EVENT_TONE_GENERATE_INFO;
+ if (event == EVENT_PORT_ALARM) return EVENT_PORT_ALARM_INFO;
+ if (event == EVENT_BCHAN_ERROR) return EVENT_BCHAN_ERROR_INFO;
+
+ return NULL;
+}
+
+int isdn_msg_parse_event(struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt)
+{
+ int i=isdn_msg_get_index(msgs, msg, nt);
+ if(i<0) return -1;
+
+ msgs[i].msg_parser(msgs, msg, bc, nt);
+ return 0;
+}
+
+msg_t * isdn_msg_build_event(struct isdn_msg msgs[], struct misdn_bchannel *bc, enum event_e event, int nt)
+{
+ int i=isdn_msg_get_index_by_event(msgs, event, nt);
+ if(i<0) return NULL;
+
+ return msgs[i].msg_builder(msgs, bc, nt);
+}
diff --git a/trunk/channels/misdn/portinfo.c b/trunk/channels/misdn/portinfo.c
new file mode 100644
index 000000000..8223164e5
--- /dev/null
+++ b/trunk/channels/misdn/portinfo.c
@@ -0,0 +1,202 @@
+/*! \file \brief
+ * Interface to mISDN - ???
+ * \author Christian Richter <crich@beronet.com>
+ */
+
+
+#include "isdn_lib.h"
+#include "isdn_lib_intern.h"
+
+
+/*
+ * global function to show all available isdn ports
+ */
+void isdn_port_info(void)
+{
+ int err;
+ int i, ii, p;
+ int useable, nt, pri;
+ unsigned char buff[1025];
+ iframe_t *frm = (iframe_t *)buff;
+ stack_info_t *stinf;
+ int device;
+
+ /* open mISDN */
+ if ((device = mISDN_open()) < 0)
+ {
+ fprintf(stderr, "mISDN_open() failed: ret=%d errno=%d (%s) Check for mISDN modules and device.\n", device, errno, strerror(errno));
+ exit(-1);
+ }
+
+ /* get number of stacks */
+ i = 1;
+ ii = mISDN_get_stack_count(device);
+ printf("\n");
+ if (ii <= 0)
+ {
+ printf("Found no card. Please be sure to load card drivers.\n");
+ }
+
+ /* loop the number of cards and get their info */
+ while(i <= ii)
+ {
+ err = mISDN_get_stack_info(device, i, buff, sizeof(buff));
+ if (err <= 0)
+ {
+ fprintf(stderr, "mISDN_get_stack_info() failed: port=%d err=%d\n", i, err);
+ break;
+ }
+ stinf = (stack_info_t *)&frm->data.p;
+
+ nt = pri = 0;
+ useable = 1;
+
+ /* output the port info */
+ printf("Port %2d: ", i);
+ switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK)
+ {
+ case ISDN_PID_L0_TE_S0:
+ printf("TE-mode BRI S/T interface line (for phone lines)");
+#if 0
+ if (stinf->pid.protocol[0] & ISDN_PID_L0_TE_S0_HFC & ISDN_PID_FEATURE_MASK)
+ printf(" HFC multiport card");
+#endif
+ break;
+ case ISDN_PID_L0_NT_S0:
+ nt = 1;
+ printf("NT-mode BRI S/T interface port (for phones)");
+#if 0
+ if (stinf->pid.protocol[0] & ISDN_PID_L0_NT_S0_HFC & ISDN_PID_FEATURE_MASK)
+ printf(" HFC multiport card");
+#endif
+ break;
+ case ISDN_PID_L0_TE_U:
+ printf("TE-mode BRI U interface line");
+ break;
+ case ISDN_PID_L0_NT_U:
+ nt = 1;
+ printf("NT-mode BRI U interface port");
+ break;
+ case ISDN_PID_L0_TE_UP2:
+ printf("TE-mode BRI Up2 interface line");
+ break;
+ case ISDN_PID_L0_NT_UP2:
+ nt = 1;
+ printf("NT-mode BRI Up2 interface port");
+ break;
+ case ISDN_PID_L0_TE_E1:
+ pri = 1;
+ printf("TE-mode PRI E1 interface line (for phone lines)");
+#if 0
+ if (stinf->pid.protocol[0] & ISDN_PID_L0_TE_E1_HFC & ISDN_PID_FEATURE_MASK)
+ printf(" HFC-E1 card");
+#endif
+ break;
+ case ISDN_PID_L0_NT_E1:
+ nt = 1;
+ pri = 1;
+ printf("NT-mode PRI E1 interface port (for phones)");
+#if 0
+ if (stinf->pid.protocol[0] & ISDN_PID_L0_NT_E1_HFC & ISDN_PID_FEATURE_MASK)
+ printf(" HFC-E1 card");
+#endif
+ break;
+ default:
+ useable = 0;
+ printf("unknown type 0x%08x",stinf->pid.protocol[0]);
+ }
+ printf("\n");
+
+ if (nt)
+ {
+ if (stinf->pid.protocol[1] == 0)
+ {
+ useable = 0;
+ printf(" -> Missing layer 1 NT-mode protocol.\n");
+ }
+ p = 2;
+ while(p <= MAX_LAYER_NR) {
+ if (stinf->pid.protocol[p])
+ {
+ useable = 0;
+ printf(" -> Layer %d protocol 0x%08x is detected, but not allowed for NT lib.\n", p, stinf->pid.protocol[p]);
+ }
+ p++;
+ }
+ if (useable)
+ {
+ if (pri)
+ printf(" -> Interface is Point-To-Point (PRI).\n");
+ else
+ printf(" -> Interface can be Poin-To-Point/Multipoint.\n");
+ }
+ } else
+ {
+ if (stinf->pid.protocol[1] == 0)
+ {
+ useable = 0;
+ printf(" -> Missing layer 1 protocol.\n");
+ }
+ if (stinf->pid.protocol[2] == 0)
+ {
+ useable = 0;
+ printf(" -> Missing layer 2 protocol.\n");
+ }
+ if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP)
+ {
+ printf(" -> Interface is Poin-To-Point.\n");
+ }
+ if (stinf->pid.protocol[3] == 0)
+ {
+ useable = 0;
+ printf(" -> Missing layer 3 protocol.\n");
+ } else
+ {
+ printf(" -> Protocol: ");
+ switch(stinf->pid.protocol[3] & ~ISDN_PID_FEATURE_MASK)
+ {
+ case ISDN_PID_L3_DSS1USER:
+ printf("DSS1 (Euro ISDN)");
+ break;
+
+ default:
+ useable = 0;
+ printf("unknown protocol 0x%08x",stinf->pid.protocol[3]);
+ }
+ printf("\n");
+ }
+ p = 4;
+ while(p <= MAX_LAYER_NR) {
+ if (stinf->pid.protocol[p])
+ {
+ useable = 0;
+ printf(" -> Layer %d protocol 0x%08x is detected, but not allowed for TE lib.\n", p, stinf->pid.protocol[p]);
+ }
+ p++;
+ }
+ printf(" -> childcnt: %d\n",stinf->childcnt);
+ }
+
+ if (!useable)
+ printf(" * Port NOT useable for PBX\n");
+
+ printf("--------\n");
+
+ i++;
+ }
+ printf("\n");
+
+ /* close mISDN */
+ if ((err = mISDN_close(device)))
+ {
+ fprintf(stderr, "mISDN_close() failed: err=%d '%s'\n", err, strerror(err));
+ exit(-1);
+ }
+}
+
+
+int main()
+{
+ isdn_port_info();
+ return 0;
+}
diff --git a/trunk/channels/misdn_config.c b/trunk/channels/misdn_config.c
new file mode 100644
index 000000000..bbe23fb6c
--- /dev/null
+++ b/trunk/channels/misdn_config.c
@@ -0,0 +1,1158 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Christian Richter
+ *
+ * Christian Richter <crich@beronet.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 chan_misdn configuration management
+ * \author Christian Richter <crich@beronet.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "chan_misdn_config.h"
+
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/lock.h"
+#include "asterisk/pbx.h"
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+
+#define AST_LOAD_CFG ast_config_load
+#define AST_DESTROY_CFG ast_config_destroy
+
+#define NO_DEFAULT "<>"
+#define NONE 0
+
+#define GEN_CFG 1
+#define PORT_CFG 2
+#define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
+#define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+ .flags = 0,
+ .max_size = -1,
+ .resync_threshold = -1,
+ .impl = "",
+};
+
+static struct ast_jb_conf global_jbconf;
+
+enum misdn_cfg_type {
+ MISDN_CTYPE_STR,
+ MISDN_CTYPE_INT,
+ MISDN_CTYPE_BOOL,
+ MISDN_CTYPE_BOOLINT,
+ MISDN_CTYPE_MSNLIST,
+ MISDN_CTYPE_ASTGROUP
+};
+
+struct msn_list {
+ char *msn;
+ struct msn_list *next;
+};
+
+union misdn_cfg_pt {
+ char *str;
+ int *num;
+ struct msn_list *ml;
+ ast_group_t *grp;
+ void *any;
+};
+
+struct misdn_cfg_spec {
+ char name[BUFFERSIZE];
+ enum misdn_cfg_elements elem;
+ enum misdn_cfg_type type;
+ char def[BUFFERSIZE];
+ int boolint_def;
+ char desc[BUFFERSIZE];
+};
+
+
+static const char ports_description[] =
+ "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
+
+static const struct misdn_cfg_spec port_spec[] = {
+ { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE,
+ "Name of the portgroup." },
+ { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE,
+ "Here you can define which bearers should be allowed." },
+ { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE,
+ "Set this between -8 and 8 to change the RX Gain." },
+ { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE,
+ "Set this between -8 and 8 to change the TX Gain." },
+ { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE,
+ "Some telcos espacially in NL seem to need this set to yes,\n"
+ "\talso in switzerland this seems to be important." },
+ { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE,
+ "If we should generate ringing for chan_sip and others." },
+ { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "no", NONE,
+ "This option defines, if chan_misdn should check the L1 on a PMP\n"
+ "\tbefore makeing a group call on it. The L1 may go down for PMP Ports\n"
+ "\tso we might need this.\n"
+ "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
+ "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
+ "\tbecause of a lost Link or because the Provider shut it down..." },
+ { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "no", NONE ,
+ "Block this port if we have an alarm on it."
+ "default: yes\n" },
+ { "hdlc", MISDN_CFG_HDLC, MISDN_CTYPE_BOOL, "no", NONE,
+ "Set this to yes, if you want to bridge a mISDN data channel to\n"
+ "\tanother channel type or to an application." },
+ { "context", MISDN_CFG_CONTEXT, MISDN_CTYPE_STR, "default", NONE,
+ "Context to use for incoming calls." },
+ { "language", MISDN_CFG_LANGUAGE, MISDN_CTYPE_STR, "en", NONE,
+ "Language." },
+ { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE,
+ "Sets the musiconhold class." },
+ { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE,
+ "Sets the caller ID." },
+ { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE,
+ "Sets the method to use for channel selection:\n"
+ "\t standard - always choose the first free channel with the lowest number\n"
+ "\t round_robin - use the round robin algorithm to select a channel. use this\n"
+ "\t if you want to balance your load." },
+ { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE,
+ "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
+ "\n"
+ "\tThere are different types of the dialplan:\n"
+ "\n"
+ "\tdialplan -> outgoing Number\n"
+ "\tlocaldialplan -> callerid\n"
+ "\tcpndialplan -> connected party number\n"
+ "\n"
+ "\tdialplan options:\n"
+ "\n"
+ "\t0 - unknown\n"
+ "\t1 - International\n"
+ "\t2 - National\n"
+ "\t4 - Subscriber\n"
+ "\n"
+ "\tThis setting is used for outgoing calls." },
+ { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
+ "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
+ "\n"
+ "\tThere are different types of the dialplan:\n"
+ "\n"
+ "\tdialplan -> outgoing Number\n"
+ "\tlocaldialplan -> callerid\n"
+ "\tcpndialplan -> connected party number\n"
+ "\n"
+ "\tdialplan options:\n"
+ "\n"
+ "\t0 - unknown\n"
+ "\t1 - International\n"
+ "\t2 - National\n"
+ "\t4 - Subscriber\n"
+ "\n"
+ "\tThis setting is used for outgoing calls" },
+ { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
+ "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
+ "\n"
+ "\tThere are different types of the dialplan:\n"
+ "\n"
+ "\tdialplan -> outgoing Number\n"
+ "\tlocaldialplan -> callerid\n"
+ "\tcpndialplan -> connected party number\n"
+ "\n"
+ "\tdialplan options:\n"
+ "\n"
+ "\t0 - unknown\n"
+ "\t1 - International\n"
+ "\t2 - National\n"
+ "\t4 - Subscriber\n"
+ "\n"
+ "\tThis setting is used for outgoing calls." },
+ { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE,
+ "Prefix for national, this is put before the\n"
+ "\toad if an according dialplan is set by the other end." },
+ { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE,
+ "Prefix for international, this is put before the\n"
+ "\toad if an according dialplan is set by the other end." },
+ { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE,
+ "These (presentation and screen) are the exact isdn screening and presentation\n"
+ "\tindicators.\n"
+ "\tIf -1 is given for both values, the presentation indicators are used from\n"
+ "\tAsterisks SetCallerPres application.\n"
+ "\n"
+ "\tscreen=0, presentation=0 -> callerid presented not screened\n"
+ "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
+ { "screen", MISDN_CFG_SCREEN, MISDN_CTYPE_INT, "-1", NONE,
+ "These (presentation and screen) are the exact isdn screening and presentation\n"
+ "\tindicators.\n"
+ "\tIf -1 is given for both values, the presentation indicators are used from\n"
+ "\tAsterisks SetCallerPres application.\n"
+ "\n"
+ "\tscreen=0, presentation=0 -> callerid presented not screened\n"
+ "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
+ { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
+ "Enable this to get into the s dialplan-extension.\n"
+ "\tThere you can use DigitTimeout if you can't or don't want to use\n"
+ "\tisdn overlap dial.\n"
+ "\tNOTE: This will jump into the s extension for every exten!" },
+ { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE,
+ "Enable this to prevent chan_misdn to generate the dialtone\n"
+ "\tThis makes only sense together with the always_immediate=yes option\n"
+ "\tto generate your own dialtone with Playtones or so."},
+ { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
+ "Enable this if you want callers which called exactly the base\n"
+ "\tnumber (so no extension is set) to jump into the s extension.\n"
+ "\tIf the user dials something more, it jumps to the correct extension\n"
+ "\tinstead." },
+ { "senddtmf", MISDN_CFG_SENDDTMF, MISDN_CTYPE_BOOL, "no", NONE,
+ "Enable this if we should produce DTMF Tones ourselves." },
+ { "astdtmf", MISDN_CFG_ASTDTMF, MISDN_CTYPE_BOOL, "no", NONE,
+ "Enable this if you want to use the Asterisk dtmf detector\n"
+ "instead of the mISDN_dsp/hfcmulti one."
+ },
+ { "hold_allowed", MISDN_CFG_HOLD_ALLOWED, MISDN_CTYPE_BOOL, "no", NONE,
+ "Enable this to have support for hold and retrieve." },
+ { "early_bconnect", MISDN_CFG_EARLY_BCONNECT, MISDN_CTYPE_BOOL, "yes", NONE,
+ "Disable this if you don't mind correct handling of Progress Indicators." },
+ { "incoming_early_audio", MISDN_CFG_INCOMING_EARLY_AUDIO, MISDN_CTYPE_BOOL, "no", NONE,
+ "Turn this on if you like to send Tone Indications to a Incoming\n"
+ "\tisdn channel on a TE Port. Rarely used, only if the Telco allows\n"
+ "\tyou to send indications by yourself, normally the Telco sends the\n"
+ "\tindications to the remote party." },
+ { "echocancel", MISDN_CFG_ECHOCANCEL, MISDN_CTYPE_BOOLINT, "0", 128,
+ "This enables echocancellation, with the given number of taps.\n"
+ "\tBe aware, move this setting only to outgoing portgroups!\n"
+ "\tA value of zero turns echocancellation off.\n"
+ "\n"
+ "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" },
+#ifdef MISDN_1_2
+ { "pipeline", MISDN_CFG_PIPELINE, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
+ "Set the configuration string for the mISDN dsp pipeline.\n"
+ "\n"
+ "\tExample for enabling the mg2 echo cancellation module with deftaps\n"
+ "\tset to 128:\n"
+ "\t\tmg2ec(deftaps=128)" },
+#endif
+#ifdef WITH_BEROEC
+ { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64,
+ "echotail in ms (1-200)\n"},
+ { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE,
+ "Use antihowl\n"},
+ { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE,
+ "Nonlinear Processing (much faster adaption)"},
+ { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE,
+ "ZeroCoeffeciens\n"},
+ { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE,
+ "Disable Tone\n"},
+ { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE,
+ "Adaption mode (0=no,1=full,2=fast)\n"},
+#endif
+ { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE,
+ "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n"
+ "\tthis requests additional Infos, so we can waitfordigits without much\n"
+ "\tissues. This works only for PTP Ports" },
+ { "noautorespond_on_setup", MISDN_CFG_NOAUTORESPOND_ON_SETUP, MISDN_CTYPE_BOOL, "0", NONE,
+ "Do not send SETUP_ACKNOWLEDGE or PROCEEDING automatically to the calling Party.\n"
+ "Instead we directly jump into the dialplan. This might be useful for fast call\n"
+ "rejection, or for some broken switches, that need hangup causes like busy in the.\n"
+ "RELEASE_COMPLETE Message, instead of the DISCONNECT Message.\n"},
+ { "jitterbuffer", MISDN_CFG_JITTERBUFFER, MISDN_CTYPE_INT, "4000", NONE,
+ "The jitterbuffer." },
+ { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, MISDN_CTYPE_INT, "0", NONE,
+ "Change this threshold to enable dejitter functionality." },
+ { "callgroup", MISDN_CFG_CALLGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
+ "Callgroup." },
+ { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
+ "Pickupgroup." },
+ { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
+ "Defines the maximum amount of incoming calls per port for this group.\n"
+ "\tCalls which exceed the maximum will be marked with the channel varible\n"
+ "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
+ { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE,
+ "Defines the maximum amount of outgoing calls per port for this group\n"
+ "\texceeding calls will be rejected" },
+
+ { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE,
+ "Defines the cause with which a 3. call is rejected on PTMP BRI."},
+ { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
+ "Setup fax detection:\n"
+ "\t no - no fax detection\n"
+ "\t incoming - fax detection for incoming calls\n"
+ "\t outgoing - fax detection for outgoing calls\n"
+ "\t both - fax detection for incoming and outgoing calls\n"
+ "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
+ "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
+ { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
+ "Number of seconds the fax detection should do its job. After the given period of time,\n"
+ "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
+ "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
+ { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
+ "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
+ { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
+ "Watches the layer 1. If the layer 1 is down, it tries to\n"
+ "\tget it up. The timeout is given in seconds. with 0 as value it\n"
+ "\tdoes not watch the l1 at all\n"
+ "\n"
+ "\tThis option is only read at loading time of chan_misdn, which\n"
+ "\tmeans you need to unload and load chan_misdn to change the value,\n"
+ "\tan Asterisk restart should do the trick." },
+ { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
+ "Enables overlap dial for the given amount of seconds.\n"
+ "\tPossible values are positive integers or:\n"
+ "\t yes (= 4 seconds)\n"
+ "\t no (= 0 seconds = disabled)" },
+ { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE ,
+ "Set this to yes if you want calls disconnected in overlap mode\n"
+ "\twhen a timeout happens." },
+ { "bridging", MISDN_CFG_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
+ "Set this to yes/no, default is yes.\n"
+ "This can be used to have bridging enabled in general and to\n"
+ "disable it for specific ports. It makes sense to disable\n"
+ "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n"
+ "features with ISDN phones.\n"
+ },
+ { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
+ "MSN's for TE ports, listen on those numbers on the above ports, and\n"
+ "\tindicate the incoming calls to Asterisk.\n"
+ "\tHere you can give a comma seperated list, or simply an '*' for any msn." },
+};
+
+static const struct misdn_cfg_spec gen_spec[] = {
+ { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
+ "Sets the debugging flag:\n"
+ "\t0 - No Debug\n"
+ "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
+ "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
+ "\t3 - very Verbose, the above + lots of Driver specific infos\n"
+ "\t4 - even more Verbose than 3" },
+#ifndef MISDN_1_2
+ { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
+ "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
+#endif
+ { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
+ "Set the path to the massively growing trace file, if you want that." },
+ { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
+ "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
+ { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
+ "Stops dialtone after getting first digit on NT Port." },
+ { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
+ "Wether to append overlapdialed Digits to Extension or not." },
+ { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
+ "Wether to look out for dynamic crypting attempts." },
+ { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
+ "What is used for crypting Protocol." },
+ { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
+ "Keys for cryption, you reference them in the dialplan\n"
+ "\tLater also in dynamic encr." },
+ { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE,
+ "avoid dropping calls if the L2 goes down. some nortel pbx\n"
+ "do put down the L2/L1 for some milliseconds even if there\n"
+ "are running calls. with this option you can avoid dropping them\n" },
+ { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
+ "No description yet."},
+ { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
+ "No description yet." }
+};
+
+
+/* array of port configs, default is at position 0. */
+static union misdn_cfg_pt **port_cfg;
+/* max number of available ports, is set on init */
+static int max_ports;
+/* general config */
+static union misdn_cfg_pt *general_cfg;
+/* storing the ptp flag separated to save memory */
+static int *ptp;
+/* maps enum config elements to array positions */
+static int *map;
+
+static ast_mutex_t config_mutex;
+
+#define CLI_ERROR(name, value, section) ({ \
+ ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
+ "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
+})
+
+static int _enum_array_map (void)
+{
+ int i, j, ok;
+
+ for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
+ if (i == MISDN_CFG_PTP)
+ continue;
+ ok = 0;
+ for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
+ if (port_spec[j].elem == i) {
+ map[i] = j;
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
+ return -1;
+ }
+ }
+ for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
+ ok = 0;
+ for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
+ if (gen_spec[j].elem == i) {
+ map[i] = j;
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int get_cfg_position (const char *name, int type)
+{
+ int i;
+
+ switch (type) {
+ case PORT_CFG:
+ for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
+ if (!strcasecmp(name, port_spec[i].name))
+ return i;
+ }
+ break;
+ case GEN_CFG:
+ for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
+ if (!strcasecmp(name, gen_spec[i].name))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static inline void misdn_cfg_lock (void)
+{
+ ast_mutex_lock(&config_mutex);
+}
+
+static inline void misdn_cfg_unlock (void)
+{
+ ast_mutex_unlock(&config_mutex);
+}
+
+static void _free_msn_list (struct msn_list* iter)
+{
+ if (iter->next)
+ _free_msn_list(iter->next);
+ if (iter->msn)
+ ast_free(iter->msn);
+ ast_free(iter);
+}
+
+static void _free_port_cfg (void)
+{
+ int i, j;
+ int gn = map[MISDN_CFG_GROUPNAME];
+ union misdn_cfg_pt* free_list[max_ports + 2];
+
+ memset(free_list, 0, sizeof(free_list));
+ free_list[0] = port_cfg[0];
+ for (i = 1; i <= max_ports; ++i) {
+ if (port_cfg[i][gn].str) {
+ /* we always have a groupname in the non-default case, so this is fine */
+ for (j = 1; j <= max_ports; ++j) {
+ if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
+ break;
+ else if (!free_list[j]) {
+ free_list[j] = port_cfg[i];
+ break;
+ }
+ }
+ }
+ }
+ for (j = 0; free_list[j]; ++j) {
+ for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
+ if (free_list[j][i].any) {
+ if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
+ _free_msn_list(free_list[j][i].ml);
+ else
+ ast_free(free_list[j][i].any);
+ }
+ }
+ }
+}
+
+static void _free_general_cfg (void)
+{
+ int i;
+
+ for (i = 0; i < NUM_GEN_ELEMENTS; i++)
+ if (general_cfg[i].any)
+ ast_free(general_cfg[i].any);
+}
+
+void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
+{
+ int place;
+
+ if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
+ memset(buf, 0, bufsize);
+ ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
+ return;
+ }
+
+ misdn_cfg_lock();
+ if (elem == MISDN_CFG_PTP) {
+ if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
+ memset(buf, 0, bufsize);
+ } else {
+ if ((place = map[elem]) < 0) {
+ memset(buf, 0, bufsize);
+ ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
+ } else {
+ if (elem < MISDN_CFG_LAST) {
+ switch (port_spec[place].type) {
+ case MISDN_CTYPE_STR:
+ if (port_cfg[port][place].str) {
+ ast_copy_string(buf, port_cfg[port][place].str, bufsize);
+ } else if (port_cfg[0][place].str) {
+ ast_copy_string(buf, port_cfg[0][place].str, bufsize);
+ }
+ break;
+ default:
+ if (port_cfg[port][place].any)
+ memcpy(buf, port_cfg[port][place].any, bufsize);
+ else if (port_cfg[0][place].any)
+ memcpy(buf, port_cfg[0][place].any, bufsize);
+ else
+ memset(buf, 0, bufsize);
+ }
+ } else {
+ switch (gen_spec[place].type) {
+ case MISDN_CTYPE_STR:
+ ast_copy_string(buf, S_OR(general_cfg[place].str, ""), bufsize);
+ break;
+ default:
+ if (general_cfg[place].any)
+ memcpy(buf, general_cfg[place].any, bufsize);
+ else
+ memset(buf, 0, bufsize);
+ }
+ }
+ }
+ }
+ misdn_cfg_unlock();
+}
+
+enum misdn_cfg_elements misdn_cfg_get_elem(char *name)
+{
+ int pos;
+
+ /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
+ if (!strcmp(name, "ports"))
+ return MISDN_CFG_GROUPNAME;
+ if (!strcmp(name, "name"))
+ return MISDN_CFG_FIRST;
+
+ pos = get_cfg_position(name, PORT_CFG);
+ if (pos >= 0)
+ return port_spec[pos].elem;
+
+ pos = get_cfg_position(name, GEN_CFG);
+ if (pos >= 0)
+ return gen_spec[pos].elem;
+
+ return MISDN_CFG_FIRST;
+}
+
+void misdn_cfg_get_name(enum misdn_cfg_elements elem, void *buf, int bufsize)
+{
+ struct misdn_cfg_spec *spec = NULL;
+ int place = map[elem];
+
+ /* the ptp hack */
+ if (elem == MISDN_CFG_PTP) {
+ memset(buf, 0, 1);
+ return;
+ }
+
+ /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
+ if (elem == MISDN_CFG_GROUPNAME) {
+ if (!snprintf(buf, bufsize, "ports"))
+ memset(buf, 0, 1);
+ return;
+ }
+
+ if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
+ spec = (struct misdn_cfg_spec *)port_spec;
+ else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
+ spec = (struct misdn_cfg_spec *)gen_spec;
+
+ ast_copy_string(buf, spec ? spec[place].name : "", bufsize);
+}
+
+void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
+{
+ int place = map[elem];
+ struct misdn_cfg_spec *spec = NULL;
+
+ /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
+ if (elem == MISDN_CFG_GROUPNAME) {
+ ast_copy_string(buf, ports_description, bufsize);
+ if (buf_default && bufsize_default)
+ memset(buf_default, 0, 1);
+ return;
+ }
+
+ if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
+ spec = (struct misdn_cfg_spec *)port_spec;
+ else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
+ spec = (struct misdn_cfg_spec *)gen_spec;
+
+ if (!spec || !spec[place].desc)
+ memset(buf, 0, 1);
+ else {
+ ast_copy_string(buf, spec[place].desc, bufsize);
+ if (buf_default && bufsize) {
+ if (!strcmp(spec[place].def, NO_DEFAULT))
+ memset(buf_default, 0, 1);
+ else
+ ast_copy_string(buf_default, spec[place].def, bufsize_default);
+ }
+ }
+}
+
+int misdn_cfg_is_msn_valid (int port, char* msn)
+{
+ int re = 0;
+ struct msn_list *iter;
+
+ if (!misdn_cfg_is_port_valid(port)) {
+ ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
+ return 0;
+ }
+
+ misdn_cfg_lock();
+ if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
+ iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
+ else
+ iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
+ for (; iter; iter = iter->next)
+ if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
+ re = 1;
+ break;
+ }
+ misdn_cfg_unlock();
+
+ return re;
+}
+
+int misdn_cfg_is_port_valid (int port)
+{
+ int gn = map[MISDN_CFG_GROUPNAME];
+
+ return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
+}
+
+int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
+{
+ int i, re = 0;
+ char *method ;
+
+ misdn_cfg_lock();
+
+ method = port_cfg[0][map[MISDN_CFG_METHOD]].str;
+
+ for (i = 1; i <= max_ports; i++) {
+ if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
+ if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
+ method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ?
+ port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
+ }
+ }
+
+ if (method) {
+ switch (meth) {
+ case METHOD_STANDARD: re = !strcasecmp(method, "standard");
+ break;
+ case METHOD_ROUND_ROBIN: re = !strcasecmp(method, "round_robin");
+ break;
+ case METHOD_STANDARD_DEC: re = !strcasecmp(method, "standard_dec");
+ break;
+ }
+ }
+ misdn_cfg_unlock();
+
+ return re;
+}
+
+void misdn_cfg_get_ports_string (char *ports)
+{
+ char tmp[16];
+ int l, i;
+ int gn = map[MISDN_CFG_GROUPNAME];
+
+ *ports = 0;
+
+ misdn_cfg_lock();
+ for (i = 1; i <= max_ports; i++) {
+ if (port_cfg[i][gn].str) {
+ if (ptp[i])
+ sprintf(tmp, "%dptp,", i);
+ else
+ sprintf(tmp, "%d,", i);
+ strcat(ports, tmp);
+ }
+ }
+ misdn_cfg_unlock();
+
+ if ((l = strlen(ports)))
+ ports[l-1] = 0;
+}
+
+void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
+{
+ int place;
+ char tempbuf[BUFFERSIZE] = "";
+ struct msn_list *iter;
+
+ if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
+ *buf = 0;
+ ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
+ return;
+ }
+
+ place = map[elem];
+
+ misdn_cfg_lock();
+ if (elem == MISDN_CFG_PTP) {
+ snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
+ }
+ else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
+ switch (port_spec[place].type) {
+ case MISDN_CTYPE_INT:
+ case MISDN_CTYPE_BOOLINT:
+ if (port_cfg[port][place].num)
+ snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
+ else if (port_cfg[0][place].num)
+ snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
+ else
+ snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
+ break;
+ case MISDN_CTYPE_BOOL:
+ if (port_cfg[port][place].num)
+ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
+ else if (port_cfg[0][place].num)
+ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
+ else
+ snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
+ break;
+ case MISDN_CTYPE_ASTGROUP:
+ if (port_cfg[port][place].grp)
+ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
+ ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
+ else if (port_cfg[0][place].grp)
+ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
+ ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
+ else
+ snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
+ break;
+ case MISDN_CTYPE_MSNLIST:
+ if (port_cfg[port][place].ml)
+ iter = port_cfg[port][place].ml;
+ else
+ iter = port_cfg[0][place].ml;
+ if (iter) {
+ for (; iter; iter = iter->next)
+ sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
+ tempbuf[strlen(tempbuf)-2] = 0;
+ }
+ snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
+ break;
+ case MISDN_CTYPE_STR:
+ if ( port_cfg[port][place].str) {
+ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
+ } else if (port_cfg[0][place].str) {
+ snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
+ } else {
+ snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
+ }
+ break;
+ }
+ } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
+ switch (gen_spec[place].type) {
+ case MISDN_CTYPE_INT:
+ case MISDN_CTYPE_BOOLINT:
+ if (general_cfg[place].num)
+ snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
+ else
+ snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
+ break;
+ case MISDN_CTYPE_BOOL:
+ if (general_cfg[place].num)
+ snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
+ else
+ snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
+ break;
+ case MISDN_CTYPE_STR:
+ if ( general_cfg[place].str) {
+ snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
+ } else {
+ snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
+ }
+ break;
+ default:
+ snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
+ break;
+ }
+ } else {
+ *buf = 0;
+ ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
+ }
+ misdn_cfg_unlock();
+}
+
+int misdn_cfg_get_next_port (int port)
+{
+ int p = -1;
+ int gn = map[MISDN_CFG_GROUPNAME];
+
+ misdn_cfg_lock();
+ for (port++; port <= max_ports; port++) {
+ if (port_cfg[port][gn].str) {
+ p = port;
+ break;
+ }
+ }
+ misdn_cfg_unlock();
+
+ return p;
+}
+
+int misdn_cfg_get_next_port_spin (int port)
+{
+ int p = misdn_cfg_get_next_port(port);
+ return (p > 0) ? p : misdn_cfg_get_next_port(0);
+}
+
+static int _parse (union misdn_cfg_pt *dest, const char *value, enum misdn_cfg_type type, int boolint_def)
+{
+ int re = 0;
+ int len, tmp;
+ char *valtmp;
+ char *tmp2 = ast_strdupa(value);
+
+ switch (type) {
+ case MISDN_CTYPE_STR:
+ if ((len = strlen(value))) {
+ dest->str = ast_malloc((len + 1) * sizeof(char));
+ strncpy(dest->str, value, len);
+ dest->str[len] = 0;
+ } else {
+ dest->str = ast_malloc(sizeof(char));
+ dest->str[0] = 0;
+ }
+ break;
+ case MISDN_CTYPE_INT:
+ {
+ char *pat;
+ if (strchr(value,'x'))
+ pat="%x";
+ else
+ pat="%d";
+ if (sscanf(value, pat, &tmp)) {
+ dest->num = ast_malloc(sizeof(int));
+ memcpy(dest->num, &tmp, sizeof(int));
+ } else
+ re = -1;
+ }
+ break;
+ case MISDN_CTYPE_BOOL:
+ dest->num = ast_malloc(sizeof(int));
+ *(dest->num) = (ast_true(value) ? 1 : 0);
+ break;
+ case MISDN_CTYPE_BOOLINT:
+ dest->num = ast_malloc(sizeof(int));
+ if (sscanf(value, "%d", &tmp)) {
+ memcpy(dest->num, &tmp, sizeof(int));
+ } else {
+ *(dest->num) = (ast_true(value) ? boolint_def : 0);
+ }
+ break;
+ case MISDN_CTYPE_MSNLIST:
+ for (valtmp = strsep(&tmp2, ","); valtmp; valtmp = strsep(&tmp2, ",")) {
+ if ((len = strlen(valtmp))) {
+ struct msn_list *ml = ast_malloc(sizeof(*ml));
+ ml->msn = ast_calloc(len+1, sizeof(char));
+ strncpy(ml->msn, valtmp, len);
+ ml->next = dest->ml;
+ dest->ml = ml;
+ }
+ }
+ break;
+ case MISDN_CTYPE_ASTGROUP:
+ dest->grp = ast_malloc(sizeof(ast_group_t));
+ *(dest->grp) = ast_get_group(value);
+ break;
+ }
+
+ return re;
+}
+
+static void _build_general_config (struct ast_variable *v)
+{
+ int pos;
+
+ for (; v; v = v->next) {
+ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+ continue;
+ if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) ||
+ (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
+ CLI_ERROR(v->name, v->value, "general");
+ }
+}
+
+static void _build_port_config (struct ast_variable *v, char *cat)
+{
+ int pos, i;
+ union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
+ int cfg_for_ports[max_ports + 1];
+
+ if (!v || !cat)
+ return;
+
+ memset(cfg_tmp, 0, sizeof(cfg_tmp));
+ memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
+
+ if (!strcasecmp(cat, "default")) {
+ cfg_for_ports[0] = 1;
+ }
+
+ if (((pos = get_cfg_position("name", PORT_CFG)) < 0) ||
+ (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
+ CLI_ERROR(v->name, v->value, cat);
+ return;
+ }
+
+ for (; v; v = v->next) {
+ if (!strcasecmp(v->name, "ports")) {
+ char *token, *tmp = ast_strdupa(v->value);
+ char ptpbuf[BUFFERSIZE] = "";
+ int start, end;
+ for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) {
+ if (!*token)
+ continue;
+ if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
+ for (; start <= end; start++) {
+ if (start <= max_ports && start > 0) {
+ cfg_for_ports[start] = 1;
+ ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
+ } else
+ CLI_ERROR(v->name, v->value, cat);
+ }
+ } else {
+ if (sscanf(token, "%d%s", &start, ptpbuf)) {
+ if (start <= max_ports && start > 0) {
+ cfg_for_ports[start] = 1;
+ ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
+ } else
+ CLI_ERROR(v->name, v->value, cat);
+ } else
+ CLI_ERROR(v->name, v->value, cat);
+ }
+ }
+ } else {
+ if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) ||
+ (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
+ CLI_ERROR(v->name, v->value, cat);
+ }
+ }
+
+ for (i = 0; i < (max_ports + 1); ++i) {
+ if (cfg_for_ports[i]) {
+ memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
+ }
+ }
+}
+
+void misdn_cfg_update_ptp (void)
+{
+#ifndef MISDN_1_2
+ char misdn_init[BUFFERSIZE];
+ char line[BUFFERSIZE];
+ FILE *fp;
+ char *tok, *p, *end;
+ int port;
+
+ misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
+
+ if (!ast_strlen_zero(misdn_init)) {
+ fp = fopen(misdn_init, "r");
+ if (fp) {
+ while(fgets(line, sizeof(line), fp)) {
+ if (!strncmp(line, "nt_ptp", 6)) {
+ for (tok = strtok_r(line,",=", &p);
+ tok;
+ tok = strtok_r(NULL,",=", &p)) {
+ port = strtol(tok, &end, 10);
+ if (end != tok && misdn_cfg_is_port_valid(port)) {
+ misdn_cfg_lock();
+ ptp[port] = 1;
+ misdn_cfg_unlock();
+ }
+ }
+ }
+ }
+ fclose(fp);
+ } else {
+ ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
+ }
+ }
+#else
+ int i;
+ int proto;
+ char filename[128];
+ FILE *fp;
+
+ for (i = 1; i <= max_ports; ++i) {
+ snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
+ continue;
+ }
+ if (fscanf(fp, "0x%08x", &proto) != 1)
+ ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
+ else
+ ptp[i] = proto & 1<<5 ? 1 : 0;
+ fclose(fp);
+ }
+#endif
+}
+
+static void _fill_defaults (void)
+{
+ int i;
+
+ for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
+ if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
+ _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
+ }
+ for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
+ if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
+ _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
+ }
+}
+
+void misdn_cfg_reload (void)
+{
+ misdn_cfg_init(0, 1);
+}
+
+void misdn_cfg_destroy (void)
+{
+ misdn_cfg_lock();
+
+ _free_port_cfg();
+ _free_general_cfg();
+
+ ast_free(port_cfg);
+ ast_free(general_cfg);
+ ast_free(ptp);
+ ast_free(map);
+
+ misdn_cfg_unlock();
+ ast_mutex_destroy(&config_mutex);
+}
+
+int misdn_cfg_init(int this_max_ports, int reload)
+{
+ char config[] = "misdn.conf";
+ char *cat, *p;
+ int i;
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if (!(cfg = AST_LOAD_CFG(config, config_flags))) {
+ ast_log(LOG_WARNING, "missing file: misdn.conf\n");
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ ast_mutex_init(&config_mutex);
+
+ /* Copy the default jb config over global_jbconf */
+ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+ misdn_cfg_lock();
+
+ if (this_max_ports) {
+ /* this is the first run */
+ max_ports = this_max_ports;
+ map = ast_calloc(MISDN_GEN_LAST + 1, sizeof(int));
+ if (_enum_array_map())
+ return -1;
+ p = ast_calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
+ + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
+ port_cfg = (union misdn_cfg_pt **)p;
+ p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
+ for (i = 0; i <= max_ports; ++i) {
+ port_cfg[i] = (union misdn_cfg_pt *)p;
+ p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
+ }
+ general_cfg = ast_calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
+ ptp = ast_calloc(max_ports + 1, sizeof(int));
+ }
+ else {
+ /* misdn reload */
+ _free_port_cfg();
+ _free_general_cfg();
+ memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
+ memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
+ memset(ptp, 0, sizeof(int) * (max_ports + 1));
+ }
+
+ cat = ast_category_browse(cfg, NULL);
+
+ while(cat) {
+ v = ast_variable_browse(cfg, cat);
+ if (!strcasecmp(cat, "general")) {
+ _build_general_config(v);
+ } else {
+ _build_port_config(v, cat);
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+
+ _fill_defaults();
+
+ misdn_cfg_unlock();
+ AST_DESTROY_CFG(cfg);
+
+ return 0;
+}
+
+struct ast_jb_conf *misdn_get_global_jbconf() {
+ return &global_jbconf;
+}
diff --git a/trunk/channels/vcodecs.c b/trunk/channels/vcodecs.c
new file mode 100644
index 000000000..ae3770920
--- /dev/null
+++ b/trunk/channels/vcodecs.c
@@ -0,0 +1,1253 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007-2008, Sergio Fadda, Luigi Rizzo
+ *
+ * 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.
+ */
+
+/*
+ * Video codecs support for console_video.c
+ * $Revision$
+ */
+
+#include "asterisk.h"
+#include "console_video.h"
+#include "asterisk/frame.h"
+#include "asterisk/utils.h" /* ast_calloc() */
+
+struct video_out_desc;
+struct video_dec_desc;
+struct fbuf_t;
+
+/*
+ * Each codec is defined by a number of callbacks
+ */
+/*! \brief initialize the encoder */
+typedef int (*encoder_init_f)(AVCodecContext *v);
+
+/*! \brief actually call the encoder */
+typedef int (*encoder_encode_f)(struct video_out_desc *v);
+
+/*! \brief encapsulate the bistream in RTP frames */
+typedef struct ast_frame *(*encoder_encap_f)(struct fbuf_t *, int mtu,
+ struct ast_frame **tail);
+
+/*! \brief inizialize the decoder */
+typedef int (*decoder_init_f)(AVCodecContext *enc_ctx);
+
+/*! \brief extract the bitstream from RTP frames and store in the fbuf.
+ * return 0 if ok, 1 on error
+ */
+typedef int (*decoder_decap_f)(struct fbuf_t *b, uint8_t *data, int len);
+
+/*! \brief actually call the decoder */
+typedef int (*decoder_decode_f)(struct video_dec_desc *v, struct fbuf_t *b);
+
+struct video_codec_desc {
+ const char *name; /* format name */
+ int format; /* AST_FORMAT_* */
+ encoder_init_f enc_init;
+ encoder_encap_f enc_encap;
+ encoder_encode_f enc_run;
+ decoder_init_f dec_init;
+ decoder_decap_f dec_decap;
+ decoder_decode_f dec_run;
+};
+
+/*
+ * Descriptor for the incoming stream, with multiple buffers for the bitstream
+ * extracted from the RTP packets, RTP reassembly info, and a frame buffer
+ * for the decoded frame (buf).
+ * The descriptor is allocated as the first frame comes in.
+ *
+ * Incoming payload is stored in one of the dec_in[] buffers, which are
+ * emptied by the video thread. These buffers are organized in a circular
+ * queue, with dec_in_cur being the buffer in use by the incoming stream,
+ * and dec_in_dpy is the one being displayed. When the pointers need to
+ * be changed, we synchronize the access to them with dec_lock.
+ * When the list is full dec_in_cur = NULL (we cannot store new data),
+ * when the list is empty dec_in_dpy = NULL (we cannot display frames).
+ */
+struct video_dec_desc {
+ struct video_codec_desc *d_callbacks; /* decoder callbacks */
+ AVCodecContext *dec_ctx; /* information about the codec in the stream */
+ AVCodec *codec; /* reference to the codec */
+ AVFrame *d_frame; /* place to store the decoded frame */
+ AVCodecParserContext *parser;
+ uint16_t next_seq; /* must be 16 bit */
+ int discard; /* flag for discard status */
+#define N_DEC_IN 3 /* number of incoming buffers */
+ struct fbuf_t *dec_in_cur; /* buffer being filled in */
+ struct fbuf_t *dec_in_dpy; /* buffer to display */
+ struct fbuf_t dec_in[N_DEC_IN]; /* incoming bitstream, allocated/extended in fbuf_append() */
+ struct fbuf_t dec_out; /* decoded frame, no buffer (data is in AVFrame) */
+};
+
+#ifdef debugging_only
+
+/* some debugging code to check the bitstream:
+ * declare a bit buffer, initialize it, and fetch data from it.
+ */
+struct bitbuf {
+ const uint8_t *base;
+ int bitsize; /* total size in bits */
+ int ofs; /* next bit to read */
+};
+
+static struct bitbuf bitbuf_init(const uint8_t *base, int bitsize, int start_ofs)
+{
+ struct bitbuf a;
+ a.base = base;
+ a.bitsize = bitsize;
+ a.ofs = start_ofs;
+ return a;
+}
+
+static int bitbuf_left(struct bitbuf *b)
+{
+ return b->bitsize - b->ofs;
+}
+
+static uint32_t getbits(struct bitbuf *b, int n)
+{
+ int i, ofs;
+ const uint8_t *d;
+ uint8_t mask;
+ uint32_t retval = 0;
+ if (n> 31) {
+ ast_log(LOG_WARNING, "too many bits %d, max 32\n", n);
+ return 0;
+ }
+ if (n + b->ofs > b->bitsize) {
+ ast_log(LOG_WARNING, "bitbuf overflow %d of %d\n", n + b->ofs, b->bitsize);
+ n = b->bitsize - b->ofs;
+ }
+ ofs = 7 - b->ofs % 8; /* start from msb */
+ mask = 1 << ofs;
+ d = b->base + b->ofs / 8; /* current byte */
+ for (i=0 ; i < n; i++) {
+ retval += retval + (*d & mask ? 1 : 0); /* shift in new byte */
+ b->ofs++;
+ mask >>= 1;
+ if (mask == 0) {
+ d++;
+ mask = 0x80;
+ }
+ }
+ return retval;
+}
+
+static void check_h261(struct fbuf_t *b)
+{
+ struct bitbuf a = bitbuf_init(b->data, b->used * 8, 0);
+ uint32_t x, y;
+
+ x = getbits(&a, 20); /* PSC, 0000 0000 0000 0001 0000 */
+ if (x != 0x10) {
+ ast_log(LOG_WARNING, "bad PSC 0x%x\n", x);
+ return;
+ }
+ x = getbits(&a, 5); /* temporal reference */
+ y = getbits(&a, 6); /* ptype */
+ if (0)
+ ast_log(LOG_WARNING, "size %d TR %d PTY spl %d doc %d freeze %d %sCIF hi %d\n",
+ b->used,
+ x,
+ (y & 0x20) ? 1 : 0,
+ (y & 0x10) ? 1 : 0,
+ (y & 0x8) ? 1 : 0,
+ (y & 0x4) ? "" : "Q",
+ (y & 0x2) ? 1:0);
+ while ( (x = getbits(&a, 1)) == 1)
+ ast_log(LOG_WARNING, "PSPARE 0x%x\n", getbits(&a, 8));
+ // ast_log(LOG_WARNING, "PSPARE 0 - start GOB LAYER\n");
+ while ( (x = bitbuf_left(&a)) > 0) {
+ // ast_log(LOG_WARNING, "GBSC %d bits left\n", x);
+ x = getbits(&a, 16); /* GBSC 0000 0000 0000 0001 */
+ if (x != 0x1) {
+ ast_log(LOG_WARNING, "bad GBSC 0x%x\n", x);
+ break;
+ }
+ x = getbits(&a, 4); /* group number */
+ y = getbits(&a, 5); /* gquant */
+ if (x == 0) {
+ ast_log(LOG_WARNING, " bad GN %d\n", x);
+ break;
+ }
+ while ( (x = getbits(&a, 1)) == 1)
+ ast_log(LOG_WARNING, "GSPARE 0x%x\n", getbits(&a, 8));
+ while ( (x = bitbuf_left(&a)) > 0) { /* MB layer */
+ break;
+ }
+ }
+}
+
+void dump_buf(struct fbuf_t *b);
+void dump_buf(struct fbuf_t *b)
+{
+ int i, x, last2lines;
+ char buf[80];
+
+ last2lines = (b->used - 16) & ~0xf;
+ ast_log(LOG_WARNING, "buf size %d of %d\n", b->used, b->size);
+ for (i = 0; i < b->used; i++) {
+ x = i & 0xf;
+ if ( x == 0) { /* new line */
+ if (i != 0)
+ ast_log(LOG_WARNING, "%s\n", buf);
+ bzero(buf, sizeof(buf));
+ sprintf(buf, "%04x: ", i);
+ }
+ sprintf(buf + 6 + x*3, "%02x ", b->data[i]);
+ if (i > 31 && i < last2lines)
+ i = last2lines - 1;
+ }
+ if (buf[0])
+ ast_log(LOG_WARNING, "%s\n", buf);
+}
+#endif /* debugging_only */
+
+/*!
+ * Build an ast_frame for a given chunk of data, and link it into
+ * the queue, with possibly 'head' bytes at the beginning to
+ * fill in some fields later.
+ */
+static struct ast_frame *create_video_frame(uint8_t *start, uint8_t *end,
+ int format, int head, struct ast_frame *prev)
+{
+ int len = end-start;
+ uint8_t *data;
+ struct ast_frame *f;
+
+ data = ast_calloc(1, len+head);
+ f = ast_calloc(1, sizeof(*f));
+ if (f == NULL || data == NULL) {
+ ast_log(LOG_WARNING, "--- frame error f %p data %p len %d format %d\n",
+ f, data, len, format);
+ if (f)
+ ast_free(f);
+ if (data)
+ ast_free(data);
+ return NULL;
+ }
+ memcpy(data+head, start, len);
+ f->data = data;
+ f->mallocd = AST_MALLOCD_DATA | AST_MALLOCD_HDR;
+ //f->has_timing_info = 1;
+ //f->ts = ast_tvdiff_ms(ast_tvnow(), out->ts);
+ f->datalen = len+head;
+ f->frametype = AST_FRAME_VIDEO;
+ f->subclass = format;
+ f->samples = 0;
+ f->offset = 0;
+ f->src = "Console";
+ f->delivery.tv_sec = 0;
+ f->delivery.tv_usec = 0;
+ f->seqno = 0;
+ AST_LIST_NEXT(f, frame_list) = NULL;
+
+ if (prev)
+ AST_LIST_NEXT(prev, frame_list) = f;
+
+ return f;
+}
+
+
+/*
+ * Append a chunk of data to a buffer taking care of bit alignment
+ * Return 0 on success, != 0 on failure
+ */
+static int fbuf_append(struct fbuf_t *b, uint8_t *src, int len,
+ int sbit, int ebit)
+{
+ /*
+ * Allocate buffer. ffmpeg wants an extra FF_INPUT_BUFFER_PADDING_SIZE,
+ * and also wants 0 as a buffer terminator to prevent trouble.
+ */
+ int need = len + FF_INPUT_BUFFER_PADDING_SIZE;
+ int i;
+ uint8_t *dst, mask;
+
+ if (b->data == NULL) {
+ b->size = need;
+ b->used = 0;
+ b->ebit = 0;
+ b->data = ast_calloc(1, b->size);
+ } else if (b->used + need > b->size) {
+ b->size = b->used + need;
+ b->data = ast_realloc(b->data, b->size);
+ }
+ if (b->data == NULL) {
+ ast_log(LOG_WARNING, "alloc failure for %d, discard\n",
+ b->size);
+ return 1;
+ }
+ if (b->used == 0 && b->ebit != 0) {
+ ast_log(LOG_WARNING, "ebit not reset at start\n");
+ b->ebit = 0;
+ }
+ dst = b->data + b->used;
+ i = b->ebit + sbit; /* bits to ignore around */
+ if (i == 0) { /* easy case, just append */
+ /* do everything in the common block */
+ } else if (i == 8) { /* easy too, just handle the overlap byte */
+ mask = (1 << b->ebit) - 1;
+ /* update the last byte in the buffer */
+ dst[-1] &= ~mask; /* clear bits to ignore */
+ dst[-1] |= (*src & mask); /* append new bits */
+ src += 1; /* skip and prepare for common block */
+ len --;
+ } else { /* must shift the new block, not done yet */
+ ast_log(LOG_WARNING, "must handle shift %d %d at %d\n",
+ b->ebit, sbit, b->used);
+ return 1;
+ }
+ memcpy(dst, src, len);
+ b->used += len;
+ b->ebit = ebit;
+ b->data[b->used] = 0; /* padding */
+ return 0;
+}
+
+/*
+ * Here starts the glue code for the various supported video codecs.
+ * For each of them, we need to provide routines for initialization,
+ * calling the encoder, encapsulating the bitstream in ast_frames,
+ * extracting payload from ast_frames, and calling the decoder.
+ */
+
+/*--- h263+ support --- */
+
+/*! \brief initialization of h263p */
+static int h263p_enc_init(AVCodecContext *enc_ctx)
+{
+ /* modes supported are
+ - Unrestricted Motion Vector (annex D)
+ - Advanced Prediction (annex F)
+ - Advanced Intra Coding (annex I)
+ - Deblocking Filter (annex J)
+ - Slice Structure (annex K)
+ - Alternative Inter VLC (annex S)
+ - Modified Quantization (annex T)
+ */
+ enc_ctx->flags |=CODEC_FLAG_H263P_UMV; /* annex D */
+ enc_ctx->flags |=CODEC_FLAG_AC_PRED; /* annex f ? */
+ enc_ctx->flags |=CODEC_FLAG_H263P_SLICE_STRUCT; /* annex k */
+ enc_ctx->flags |= CODEC_FLAG_H263P_AIC; /* annex I */
+
+ return 0;
+}
+
+
+/*
+ * Create RTP/H.263 fragments to avoid IP fragmentation. We fragment on a
+ * PSC or a GBSC, but if we don't find a suitable place just break somewhere.
+ * Everything is byte-aligned.
+ */
+static struct ast_frame *h263p_encap(struct fbuf_t *b, int mtu,
+ struct ast_frame **tail)
+{
+ struct ast_frame *cur = NULL, *first = NULL;
+ uint8_t *d = b->data;
+ int len = b->used;
+ int l = len; /* size of the current fragment. If 0, must look for a psc */
+
+ for (;len > 0; len -= l, d += l) {
+ uint8_t *data;
+ struct ast_frame *f;
+ int i, h;
+
+ if (len >= 3 && d[0] == 0 && d[1] == 0 && d[2] >= 0x80) {
+ /* we are starting a new block, so look for a PSC. */
+ for (i = 3; i < len - 3; i++) {
+ if (d[i] == 0 && d[i+1] == 0 && d[i+2] >= 0x80) {
+ l = i;
+ break;
+ }
+ }
+ }
+ if (l > mtu || l > len) { /* psc not found, split */
+ l = MIN(len, mtu);
+ }
+ if (l < 1 || l > mtu) {
+ ast_log(LOG_WARNING, "--- frame error l %d\n", l);
+ break;
+ }
+
+ if (d[0] == 0 && d[1] == 0) { /* we start with a psc */
+ h = 0;
+ } else { /* no psc, create a header */
+ h = 2;
+ }
+
+ f = create_video_frame(d, d+l, AST_FORMAT_H263_PLUS, h, cur);
+ if (!f)
+ break;
+
+ data = f->data;
+ if (h == 0) { /* we start with a psc */
+ data[0] |= 0x04; // set P == 1, and we are done
+ } else { /* no psc, create a header */
+ data[0] = data[1] = 0; // P == 0
+ }
+
+ if (!cur)
+ first = f;
+ cur = f;
+ }
+
+ if (cur)
+ cur->subclass |= 1; // RTP Marker
+
+ *tail = cur; /* end of the list */
+ return first;
+}
+
+/*! \brief extract the bitstreem from the RTP payload.
+ * This is format dependent.
+ * For h263+, the format is defined in RFC 2429
+ * and basically has a fixed 2-byte header as follows:
+ * 5 bits RR reserved, shall be 0
+ * 1 bit P indicate a start/end condition,
+ * in which case the payload should be prepended
+ * by two zero-valued bytes.
+ * 1 bit V there is an additional VRC header after this header
+ * 6 bits PLEN length in bytes of extra picture header
+ * 3 bits PEBIT how many bits to be ignored in the last byte
+ *
+ * XXX the code below is not complete.
+ */
+static int h263p_decap(struct fbuf_t *b, uint8_t *data, int len)
+{
+ int PLEN;
+
+ if (len < 2) {
+ ast_log(LOG_WARNING, "invalid framesize %d\n", len);
+ return 1;
+ }
+ PLEN = ( (data[0] & 1) << 5 ) | ( (data[1] & 0xf8) >> 3);
+
+ if (PLEN > 0) {
+ data += PLEN;
+ len -= PLEN;
+ }
+ if (data[0] & 4) /* bit P */
+ data[0] = data[1] = 0;
+ else {
+ data += 2;
+ len -= 2;
+ }
+ return fbuf_append(b, data, len, 0, 0); /* ignore trail bits */
+}
+
+
+/*
+ * generic encoder, used by the various protocols supported here.
+ * We assume that the buffer is empty at the beginning.
+ */
+static int ffmpeg_encode(struct video_out_desc *v)
+{
+ struct fbuf_t *b = &v->enc_out;
+ int i;
+
+ b->used = avcodec_encode_video(v->enc_ctx, b->data, b->size, v->enc_in_frame);
+ i = avcodec_encode_video(v->enc_ctx, b->data + b->used, b->size - b->used, NULL); /* delayed frames ? */
+ if (i > 0) {
+ ast_log(LOG_WARNING, "have %d more bytes\n", i);
+ b->used += i;
+ }
+ return 0;
+}
+
+/*
+ * Generic decoder, which is used by h263p, h263 and h261 as it simply
+ * invokes ffmpeg's decoder.
+ * av_parser_parse should merge a randomly chopped up stream into
+ * proper frames. After that, if we have a valid frame, we decode it
+ * until the entire frame is processed.
+ */
+static int ffmpeg_decode(struct video_dec_desc *v, struct fbuf_t *b)
+{
+ uint8_t *src = b->data;
+ int srclen = b->used;
+ int full_frame = 0;
+
+ if (srclen == 0) /* no data */
+ return 0;
+ while (srclen) {
+ uint8_t *data;
+ int datalen, ret;
+ int len = av_parser_parse(v->parser, v->dec_ctx, &data, &datalen, src, srclen, 0, 0);
+
+ src += len;
+ srclen -= len;
+ /* The parser might return something it cannot decode, so it skips
+ * the block returning no data
+ */
+ if (data == NULL || datalen == 0)
+ continue;
+ ret = avcodec_decode_video(v->dec_ctx, v->d_frame, &full_frame, data, datalen);
+ if (full_frame == 1) /* full frame */
+ break;
+ if (ret < 0) {
+ ast_log(LOG_NOTICE, "Error decoding\n");
+ break;
+ }
+ }
+ if (srclen != 0) /* update b with leftover data */
+ bcopy(src, b->data, srclen);
+ b->used = srclen;
+ b->ebit = 0;
+ return full_frame;
+}
+
+static struct video_codec_desc h263p_codec = {
+ .name = "h263p",
+ .format = AST_FORMAT_H263_PLUS,
+ .enc_init = h263p_enc_init,
+ .enc_encap = h263p_encap,
+ .enc_run = ffmpeg_encode,
+ .dec_init = NULL,
+ .dec_decap = h263p_decap,
+ .dec_run = ffmpeg_decode
+};
+
+/*--- Plain h263 support --------*/
+
+static int h263_enc_init(AVCodecContext *enc_ctx)
+{
+ /* XXX check whether these are supported */
+ enc_ctx->flags |= CODEC_FLAG_H263P_UMV;
+ enc_ctx->flags |= CODEC_FLAG_H263P_AIC;
+ enc_ctx->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
+ enc_ctx->flags |= CODEC_FLAG_AC_PRED;
+
+ return 0;
+}
+
+/*
+ * h263 encapsulation is specified in RFC2190. There are three modes
+ * defined (A, B, C), with 4, 8 and 12 bytes of header, respectively.
+ * The header is made as follows
+ * 0.....................|.......................|.............|....31
+ * F:1 P:1 SBIT:3 EBIT:3 SRC:3 I:1 U:1 S:1 A:1 R:4 DBQ:2 TRB:3 TR:8
+ * FP = 0- mode A, (only one word of header)
+ * FP = 10 mode B, and also means this is an I or P frame
+ * FP = 11 mode C, and also means this is a PB frame.
+ * SBIT, EBIT nuber of bits to ignore at beginning (msbits) and end (lsbits)
+ * SRC bits 6,7,8 from the h263 PTYPE field
+ * I = 0 intra-coded, 1 = inter-coded (bit 9 from PTYPE)
+ * U = 1 for Unrestricted Motion Vector (bit 10 from PTYPE)
+ * S = 1 for Syntax Based Arith coding (bit 11 from PTYPE)
+ * A = 1 for Advanced Prediction (bit 12 from PTYPE)
+ * R = reserved, must be 0
+ * DBQ = differential quantization, DBQUANT from h263, 0 unless we are using
+ * PB frames
+ * TRB = temporal reference for bframes, also 0 unless this is a PB frame
+ * TR = temporal reference for P frames, also 0 unless PB frame.
+ *
+ * Mode B and mode C description omitted.
+ *
+ * An RTP frame can start with a PSC 0000 0000 0000 0000 1000 0
+ * or with a GBSC, which also has the first 17 bits as a PSC.
+ * Note - PSC are byte-aligned, GOB not necessarily. PSC start with
+ * PSC:22 0000 0000 0000 0000 1000 00 picture start code
+ * TR:8 .... .... temporal reference
+ * PTYPE:13 or more ptype...
+ * If we don't fragment a GOB SBIT and EBIT = 0.
+ * reference, 8 bit)
+ *
+ * The assumption below is that we start with a PSC.
+ */
+static struct ast_frame *h263_encap(struct fbuf_t *b, int mtu,
+ struct ast_frame **tail)
+{
+ uint8_t *d = b->data;
+ int start = 0, i, len = b->used;
+ struct ast_frame *f, *cur = NULL, *first = NULL;
+ const int pheader_len = 4; /* Use RFC-2190 Mode A */
+ uint8_t h263_hdr[12]; /* worst case, room for a type c header */
+ uint8_t *h = h263_hdr; /* shorthand */
+
+#define H263_MIN_LEN 6
+ if (len < H263_MIN_LEN) /* unreasonably small */
+ return NULL;
+
+ bzero(h263_hdr, sizeof(h263_hdr));
+ /* Now set the header bytes. Only type A by now,
+ * and h[0] = h[2] = h[3] = 0 by default.
+ * PTYPE starts 30 bits in the picture, so the first useful
+ * bit for us is bit 36 i.e. within d[4] (0 is the msbit).
+ * SRC = d[4] & 0x1c goes into data[1] & 0xe0
+ * I = d[4] & 0x02 goes into data[1] & 0x10
+ * U = d[4] & 0x01 goes into data[1] & 0x08
+ * S = d[5] & 0x80 goes into data[1] & 0x04
+ * A = d[5] & 0x40 goes into data[1] & 0x02
+ * R = 0 goes into data[1] & 0x01
+ * Optimizing it, we have
+ */
+ h[1] = ( (d[4] & 0x1f) << 3 ) | /* SRC, I, U */
+ ( (d[5] & 0xc0) >> 5 ); /* S, A, R */
+
+ /* now look for the next PSC or GOB header. First try to hit
+ * a '0' byte then look around for the 0000 0000 0000 0000 1 pattern
+ * which is both in the PSC and the GBSC.
+ */
+ for (i = H263_MIN_LEN, start = 0; start < len; start = i, i += 3) {
+ //ast_log(LOG_WARNING, "search at %d of %d/%d\n", i, start, len);
+ for (; i < len ; i++) {
+ uint8_t x, rpos, lpos;
+ int rpos_i; /* index corresponding to rpos */
+ if (d[i] != 0) /* cannot be in a GBSC */
+ continue;
+ if (i > len - 1)
+ break;
+ x = d[i+1];
+ if (x == 0) /* next is equally good */
+ continue;
+ /* see if around us we can make 16 '0' bits for the GBSC.
+ * Look for the first bit set on the right, and then
+ * see if we have enough 0 on the left.
+ * We are guaranteed to end before rpos == 0
+ */
+ for (rpos = 0x80, rpos_i = 8; rpos; rpos >>= 1, rpos_i--)
+ if (x & rpos) /* found the '1' bit in GBSC */
+ break;
+ x = d[i-1]; /* now look behind */
+ for (lpos = rpos; lpos ; lpos >>= 1)
+ if (x & lpos) /* too early, not a GBSC */
+ break;
+ if (lpos) /* as i said... */
+ continue;
+ /* now we have a GBSC starting somewhere in d[i-1],
+ * but it might be not byte-aligned
+ */
+ if (rpos == 0x80) { /* lucky case */
+ i = i - 1;
+ } else { /* XXX to be completed */
+ ast_log(LOG_WARNING, "unaligned GBSC 0x%x %d\n",
+ rpos, rpos_i);
+ }
+ break;
+ }
+ /* This frame is up to offset i (not inclusive).
+ * We do not split it yet even if larger than MTU.
+ */
+ f = create_video_frame(d + start, d+i, AST_FORMAT_H263,
+ pheader_len, cur);
+
+ if (!f)
+ break;
+ bcopy(h, f->data, 4); /* copy the h263 header */
+ /* XXX to do: if not aligned, fix sbit and ebit,
+ * then move i back by 1 for the next frame
+ */
+ if (!cur)
+ first = f;
+ cur = f;
+ }
+
+ if (cur)
+ cur->subclass |= 1; // RTP Marker
+
+ *tail = cur;
+ return first;
+}
+
+/* XXX We only drop the header here, but maybe we need more. */
+static int h263_decap(struct fbuf_t *b, uint8_t *data, int len)
+{
+ if (len < 4) {
+ ast_log(LOG_WARNING, "invalid framesize %d\n", len);
+ return 1; /* error */
+ }
+
+ if ( (data[0] & 0x80) == 0) {
+ len -= 4;
+ data += 4;
+ } else {
+ ast_log(LOG_WARNING, "unsupported mode 0x%x\n",
+ data[0]);
+ return 1;
+ }
+ return fbuf_append(b, data, len, 0, 0); /* XXX no bit alignment support yet */
+}
+
+static struct video_codec_desc h263_codec = {
+ .name = "h263",
+ .format = AST_FORMAT_H263,
+ .enc_init = h263_enc_init,
+ .enc_encap = h263_encap,
+ .enc_run = ffmpeg_encode,
+ .dec_init = NULL,
+ .dec_decap = h263_decap,
+ .dec_run = ffmpeg_decode
+
+};
+
+/*---- h261 support -----*/
+static int h261_enc_init(AVCodecContext *enc_ctx)
+{
+ /* It is important to set rtp_payload_size = 0, otherwise
+ * ffmpeg in h261 mode will produce output that it cannot parse.
+ * Also try to send I frames more frequently than with other codecs.
+ */
+ enc_ctx->rtp_payload_size = 0; /* important - ffmpeg fails otherwise */
+
+ return 0;
+}
+
+/*
+ * The encapsulation of H261 is defined in RFC4587 which obsoletes RFC2032
+ * The bitstream is preceded by a 32-bit header word:
+ * SBIT:3 EBIT:3 I:1 V:1 GOBN:4 MBAP:5 QUANT:5 HMVD:5 VMVD:5
+ * SBIT and EBIT are the bits to be ignored at beginning and end,
+ * I=1 if the stream has only INTRA frames - cannot change during the stream.
+ * V=0 if motion vector is not used. Cannot change.
+ * GOBN is the GOB number in effect at the start of packet, 0 if we
+ * start with a GOB header
+ * QUANT is the quantizer in effect, 0 if we start with GOB header
+ * HMVD reference horizontal motion vector. 10000 is forbidden
+ * VMVD reference vertical motion vector, as above.
+ * Packetization should occur at GOB boundaries, and if not possible
+ * with MacroBlock fragmentation. However it is likely that blocks
+ * are not bit-aligned so we must take care of this.
+ */
+static struct ast_frame *h261_encap(struct fbuf_t *b, int mtu,
+ struct ast_frame **tail)
+{
+ uint8_t *d = b->data;
+ int start = 0, i, len = b->used;
+ struct ast_frame *f, *cur = NULL, *first = NULL;
+ const int pheader_len = 4;
+ uint8_t h261_hdr[4];
+ uint8_t *h = h261_hdr; /* shorthand */
+ int sbit = 0, ebit = 0;
+
+#define H261_MIN_LEN 10
+ if (len < H261_MIN_LEN) /* unreasonably small */
+ return NULL;
+
+ bzero(h261_hdr, sizeof(h261_hdr));
+
+ /* Similar to the code in h263_encap, but the marker there is longer.
+ * Start a few bytes within the bitstream to avoid hitting the marker
+ * twice. Note we might access the buffer at len, but this is ok because
+ * the caller has it oversized.
+ */
+ for (i = H261_MIN_LEN, start = 0; start < len - 1; start = i, i += 4) {
+#if 0 /* test - disable packetization */
+ i = len; /* wrong... */
+#else
+ int found = 0, found_ebit = 0; /* last GBSC position found */
+ for (; i < len ; i++) {
+ uint8_t x, rpos, lpos;
+ if (d[i] != 0) /* cannot be in a GBSC */
+ continue;
+ x = d[i+1];
+ if (x == 0) /* next is equally good */
+ continue;
+ /* See if around us we find 15 '0' bits for the GBSC.
+ * Look for the first bit set on the right, and then
+ * see if we have enough 0 on the left.
+ * We are guaranteed to end before rpos == 0
+ */
+ for (rpos = 0x80, ebit = 7; rpos; ebit--, rpos >>= 1)
+ if (x & rpos) /* found the '1' bit in GBSC */
+ break;
+ x = d[i-1]; /* now look behind */
+ for (lpos = (rpos >> 1); lpos ; lpos >>= 1)
+ if (x & lpos) /* too early, not a GBSC */
+ break;
+ if (lpos) /* as i said... */
+ continue;
+ /* now we have a GBSC starting somewhere in d[i-1],
+ * but it might be not byte-aligned. Just remember it.
+ */
+ if (i - start > mtu) /* too large, stop now */
+ break;
+ found_ebit = ebit;
+ found = i;
+ i += 4; /* continue forward */
+ }
+ if (i >= len) { /* trim if we went too forward */
+ i = len;
+ ebit = 0; /* hopefully... should ask the bitstream ? */
+ }
+ if (i - start > mtu && found) {
+ /* use the previous GBSC, hope is within the mtu */
+ i = found;
+ ebit = found_ebit;
+ }
+#endif /* test */
+ if (i - start < 4) /* XXX too short ? */
+ continue;
+ /* This frame is up to offset i (not inclusive).
+ * We do not split it yet even if larger than MTU.
+ */
+ f = create_video_frame(d + start, d+i, AST_FORMAT_H261,
+ pheader_len, cur);
+
+ if (!f)
+ break;
+ /* recompute header with I=0, V=1 */
+ h[0] = ( (sbit & 7) << 5 ) | ( (ebit & 7) << 2 ) | 1;
+ bcopy(h, f->data, 4); /* copy the h261 header */
+ if (ebit) /* not aligned, restart from previous byte */
+ i--;
+ sbit = (8 - ebit) & 7;
+ ebit = 0;
+ if (!cur)
+ first = f;
+ cur = f;
+ }
+ if (cur)
+ cur->subclass |= 1; // RTP Marker
+
+ *tail = cur;
+ return first;
+}
+
+/*
+ * Pieces might be unaligned so we really need to put them together.
+ */
+static int h261_decap(struct fbuf_t *b, uint8_t *data, int len)
+{
+ int ebit, sbit;
+
+ if (len < 8) {
+ ast_log(LOG_WARNING, "invalid framesize %d\n", len);
+ return 1;
+ }
+ sbit = (data[0] >> 5) & 7;
+ ebit = (data[0] >> 2) & 7;
+ len -= 4;
+ data += 4;
+ return fbuf_append(b, data, len, sbit, ebit);
+}
+
+static struct video_codec_desc h261_codec = {
+ .name = "h261",
+ .format = AST_FORMAT_H261,
+ .enc_init = h261_enc_init,
+ .enc_encap = h261_encap,
+ .enc_run = ffmpeg_encode,
+ .dec_init = NULL,
+ .dec_decap = h261_decap,
+ .dec_run = ffmpeg_decode
+};
+
+/* mpeg4 support */
+static int mpeg4_enc_init(AVCodecContext *enc_ctx)
+{
+#if 0
+ //enc_ctx->flags |= CODEC_FLAG_LOW_DELAY; /*don't use b frames ?*/
+ enc_ctx->flags |= CODEC_FLAG_AC_PRED;
+ enc_ctx->flags |= CODEC_FLAG_H263P_UMV;
+ enc_ctx->flags |= CODEC_FLAG_QPEL;
+ enc_ctx->flags |= CODEC_FLAG_4MV;
+ enc_ctx->flags |= CODEC_FLAG_GMC;
+ enc_ctx->flags |= CODEC_FLAG_LOOP_FILTER;
+ enc_ctx->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
+#endif
+ enc_ctx->rtp_payload_size = 0; /* important - ffmpeg fails otherwise */
+ return 0;
+}
+
+/* simplistic encapsulation - just split frames in mtu-size units */
+static struct ast_frame *mpeg4_encap(struct fbuf_t *b, int mtu,
+ struct ast_frame **tail)
+{
+ struct ast_frame *f, *cur = NULL, *first = NULL;
+ uint8_t *d = b->data;
+ uint8_t *end = d + b->used;
+ int len;
+
+ for (;d < end; d += len, cur = f) {
+ len = MIN(mtu, end - d);
+ f = create_video_frame(d, d + len, AST_FORMAT_MP4_VIDEO, 0, cur);
+ if (!f)
+ break;
+ if (!first)
+ first = f;
+ }
+ if (cur)
+ cur->subclass |= 1;
+ *tail = cur;
+ return first;
+}
+
+static int mpeg4_decap(struct fbuf_t *b, uint8_t *data, int len)
+{
+ return fbuf_append(b, data, len, 0, 0);
+}
+
+static int mpeg4_decode(struct video_dec_desc *v, struct fbuf_t *b)
+{
+ int full_frame = 0, datalen = b->used;
+ int ret = avcodec_decode_video(v->dec_ctx, v->d_frame, &full_frame,
+ b->data, datalen);
+ if (ret < 0) {
+ ast_log(LOG_NOTICE, "Error decoding\n");
+ ret = datalen; /* assume we used everything. */
+ }
+ datalen -= ret;
+ if (datalen > 0) /* update b with leftover bytes */
+ bcopy(b->data + ret, b->data, datalen);
+ b->used = datalen;
+ b->ebit = 0;
+ return full_frame;
+}
+
+static struct video_codec_desc mpeg4_codec = {
+ .name = "mpeg4",
+ .format = AST_FORMAT_MP4_VIDEO,
+ .enc_init = mpeg4_enc_init,
+ .enc_encap = mpeg4_encap,
+ .enc_run = ffmpeg_encode,
+ .dec_init = NULL,
+ .dec_decap = mpeg4_decap,
+ .dec_run = mpeg4_decode
+};
+
+static int h264_enc_init(AVCodecContext *enc_ctx)
+{
+ enc_ctx->flags |= CODEC_FLAG_TRUNCATED;
+ //enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ //enc_ctx->flags2 |= CODEC_FLAG2_FASTPSKIP;
+ /* TODO: Maybe we need to add some other flags */
+ enc_ctx->rtp_mode = 0;
+ enc_ctx->rtp_payload_size = 0;
+ enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate;
+ return 0;
+}
+
+static int h264_dec_init(AVCodecContext *dec_ctx)
+{
+ dec_ctx->flags |= CODEC_FLAG_TRUNCATED;
+
+ return 0;
+}
+
+/*
+ * The structure of a generic H.264 stream is:
+ * - 0..n 0-byte(s), unused, optional. one zero-byte is always present
+ * in the first NAL before the start code prefix.
+ * - start code prefix (3 bytes): 0x000001
+ * (the first bytestream has a
+ * like these 0x00000001!)
+ * - NAL header byte ( F[1] | NRI[2] | Type[5] ) where type != 0
+ * - byte-stream
+ * - 0..n 0-byte(s) (padding, unused).
+ * Segmentation in RTP only needs to be done on start code prefixes.
+ * If fragments are too long... we don't support it yet.
+ * - encapsulate (or fragment) the byte-stream (with NAL header included)
+ */
+static struct ast_frame *h264_encap(struct fbuf_t *b, int mtu,
+ struct ast_frame **tail)
+{
+ struct ast_frame *f = NULL, *cur = NULL, *first = NULL;
+ uint8_t *d, *start = b->data;
+ uint8_t *end = start + b->used;
+
+ /* Search the first start code prefix - ITU-T H.264 sec. B.2,
+ * and move start right after that, on the NAL header byte.
+ */
+#define HAVE_NAL(x) (x[-4] == 0 && x[-3] == 0 && x[-2] == 0 && x[-1] == 1)
+ for (start += 4; start < end; start++) {
+ int ty = start[0] & 0x1f;
+ if (HAVE_NAL(start) && ty != 0 && ty != 31)
+ break;
+ }
+ /* if not found, or too short, we just skip the next loop and are done. */
+
+ /* Here follows the main loop to create frames. Search subsequent start
+ * codes, and then possibly fragment the unit into smaller fragments.
+ */
+ for (;start < end - 4; start = d) {
+ int size; /* size of current block */
+ uint8_t hdr[2]; /* add-on header when fragmenting */
+ int ty = 0;
+
+ /* now search next nal */
+ for (d = start + 4; d < end; d++) {
+ ty = d[0] & 0x1f;
+ if (HAVE_NAL(d))
+ break; /* found NAL */
+ }
+ /* have a block to send. d past the start code unless we overflow */
+ if (d >= end) { /* NAL not found */
+ d = end + 4;
+ } else if (ty == 0 || ty == 31) { /* found but invalid type, skip */
+ ast_log(LOG_WARNING, "skip invalid nal type %d at %d of %d\n",
+ ty, d - (uint8_t *)b->data, b->used);
+ continue;
+ }
+
+ size = d - start - 4; /* don't count the end */
+
+ if (size < mtu) { // test - don't fragment
+ // Single NAL Unit
+ f = create_video_frame(start, d - 4, AST_FORMAT_H264, 0, cur);
+ if (!f)
+ break;
+ if (!first)
+ first = f;
+
+ cur = f;
+ continue;
+ }
+
+ // Fragmented Unit (Mode A: no DON, very weak)
+ hdr[0] = (*start & 0xe0) | 28; /* mark as a fragmentation unit */
+ hdr[1] = (*start++ & 0x1f) | 0x80 ; /* keep type and set START bit */
+ size--; /* skip the NAL header */
+ while (size) {
+ uint8_t *data;
+ int frag_size = MIN(size, mtu);
+
+ f = create_video_frame(start, start+frag_size, AST_FORMAT_H264, 2, cur);
+ if (!f)
+ break;
+ size -= frag_size; /* skip this data block */
+ start += frag_size;
+
+ data = f->data;
+ data[0] = hdr[0];
+ data[1] = hdr[1] | (size == 0 ? 0x40 : 0); /* end bit if we are done */
+ hdr[1] &= ~0x80; /* clear start bit for subsequent frames */
+ if (!first)
+ first = f;
+ cur = f;
+ }
+ }
+
+ if (cur)
+ cur->subclass |= 1; // RTP Marker
+
+ *tail = cur;
+
+ return first;
+}
+
+static int h264_decap(struct fbuf_t *b, uint8_t *data, int len)
+{
+ /* Start Code Prefix (Annex B in specification) */
+ uint8_t scp[] = { 0x00, 0x00, 0x00, 0x01 };
+ int retval = 0;
+ int type, ofs = 0;
+
+ if (len < 2) {
+ ast_log(LOG_WARNING, "--- invalid len %d\n", len);
+ return 1;
+ }
+ /* first of all, check if the packet has F == 0 */
+ if (data[0] & 0x80) {
+ ast_log(LOG_WARNING, "--- forbidden packet; nal: %02x\n",
+ data[0]);
+ return 1;
+ }
+
+ type = data[0] & 0x1f;
+ switch (type) {
+ case 0:
+ case 31:
+ ast_log(LOG_WARNING, "--- invalid type: %d\n", type);
+ return 1;
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 29:
+ ast_log(LOG_WARNING, "--- encapsulation not supported : %d\n", type);
+ return 1;
+ case 28: /* FU-A Unit */
+ if (data[1] & 0x80) { // S == 1, import F and NRI from next
+ data[1] &= 0x1f; /* preserve type */
+ data[1] |= (data[0] & 0xe0); /* import F & NRI */
+ retval = fbuf_append(b, scp, sizeof(scp), 0, 0);
+ ofs = 1;
+ } else {
+ ofs = 2;
+ }
+ break;
+ default: /* From 1 to 23 (Single NAL Unit) */
+ retval = fbuf_append(b, scp, sizeof(scp), 0, 0);
+ }
+ if (!retval)
+ retval = fbuf_append(b, data + ofs, len - ofs, 0, 0);
+ if (retval)
+ ast_log(LOG_WARNING, "result %d\n", retval);
+ return retval;
+}
+
+static struct video_codec_desc h264_codec = {
+ .name = "h264",
+ .format = AST_FORMAT_H264,
+ .enc_init = h264_enc_init,
+ .enc_encap = h264_encap,
+ .enc_run = ffmpeg_encode,
+ .dec_init = h264_dec_init,
+ .dec_decap = h264_decap,
+ .dec_run = ffmpeg_decode
+};
+
+/*
+ * Table of translation between asterisk and ffmpeg formats.
+ * We need also a field for read and write (encoding and decoding), because
+ * e.g. H263+ uses different codec IDs in ffmpeg when encoding or decoding.
+ */
+struct _cm { /* map ffmpeg codec types to asterisk formats */
+ uint32_t ast_format; /* 0 is a terminator */
+ enum CodecID codec;
+ enum { CM_RD = 1, CM_WR = 2, CM_RDWR = 3 } rw; /* read or write or both ? */
+ //struct video_codec_desc *codec_desc;
+};
+
+static struct _cm video_formats[] = {
+ { AST_FORMAT_H263_PLUS, CODEC_ID_H263, CM_RD }, /* incoming H263P ? */
+ { AST_FORMAT_H263_PLUS, CODEC_ID_H263P, CM_WR },
+ { AST_FORMAT_H263, CODEC_ID_H263, CM_RD },
+ { AST_FORMAT_H263, CODEC_ID_H263, CM_WR },
+ { AST_FORMAT_H261, CODEC_ID_H261, CM_RDWR },
+ { AST_FORMAT_H264, CODEC_ID_H264, CM_RDWR },
+ { AST_FORMAT_MP4_VIDEO, CODEC_ID_MPEG4, CM_RDWR },
+ { 0, 0, 0 },
+};
+
+
+/*! \brief map an asterisk format into an ffmpeg one */
+static enum CodecID map_video_format(uint32_t ast_format, int rw)
+{
+ struct _cm *i;
+
+ for (i = video_formats; i->ast_format != 0; i++)
+ if (ast_format & i->ast_format && rw & i->rw && rw & i->rw)
+ return i->codec;
+ return CODEC_ID_NONE;
+}
+
+/* pointers to supported codecs. We assume the first one to be non null. */
+static struct video_codec_desc *supported_codecs[] = {
+ &h263p_codec,
+ &h264_codec,
+ &h263_codec,
+ &h261_codec,
+ &mpeg4_codec,
+ NULL
+};
+
+/*
+ * Map the AST_FORMAT to the library. If not recognised, fail.
+ * This is useful in the input path where we get frames.
+ */
+static struct video_codec_desc *map_video_codec(int fmt)
+{
+ int i;
+
+ for (i = 0; supported_codecs[i]; i++)
+ if (fmt == supported_codecs[i]->format) {
+ ast_log(LOG_WARNING, "using %s for format 0x%x\n",
+ supported_codecs[i]->name, fmt);
+ return supported_codecs[i];
+ }
+ return NULL;
+}
+
+/*! \brief uninitialize the descriptor for remote video stream */
+static struct video_dec_desc *dec_uninit(struct video_dec_desc *v)
+{
+ int i;
+
+ if (v == NULL) /* not initialized yet */
+ return NULL;
+ if (v->parser) {
+ av_parser_close(v->parser);
+ v->parser = NULL;
+ }
+ if (v->dec_ctx) {
+ avcodec_close(v->dec_ctx);
+ av_free(v->dec_ctx);
+ v->dec_ctx = NULL;
+ }
+ if (v->d_frame) {
+ av_free(v->d_frame);
+ v->d_frame = NULL;
+ }
+ v->codec = NULL; /* only a reference */
+ v->d_callbacks = NULL; /* forget the decoder */
+ v->discard = 1; /* start in discard mode */
+ for (i = 0; i < N_DEC_IN; i++)
+ fbuf_free(&v->dec_in[i]);
+ fbuf_free(&v->dec_out);
+ ast_free(v);
+ return NULL; /* error, in case someone cares */
+}
+
+/*
+ * initialize ffmpeg resources used for decoding frames from the network.
+ */
+static struct video_dec_desc *dec_init(uint32_t the_ast_format)
+{
+ enum CodecID codec;
+ struct video_dec_desc *v = ast_calloc(1, sizeof(*v));
+ if (v == NULL)
+ return NULL;
+
+ v->discard = 1;
+
+ v->d_callbacks = map_video_codec(the_ast_format);
+ if (v->d_callbacks == NULL) {
+ ast_log(LOG_WARNING, "cannot find video codec, drop input 0x%x\n", the_ast_format);
+ return dec_uninit(v);
+ }
+
+ codec = map_video_format(v->d_callbacks->format, CM_RD);
+
+ v->codec = avcodec_find_decoder(codec);
+ if (!v->codec) {
+ ast_log(LOG_WARNING, "Unable to find the decoder for format %d\n", codec);
+ return dec_uninit(v);
+ }
+ /*
+ * Initialize the codec context.
+ */
+ v->dec_ctx = avcodec_alloc_context();
+ if (!v->dec_ctx) {
+ ast_log(LOG_WARNING, "Cannot allocate the decoder context\n");
+ return dec_uninit(v);
+ }
+ /* XXX call dec_init() ? */
+ if (avcodec_open(v->dec_ctx, v->codec) < 0) {
+ ast_log(LOG_WARNING, "Cannot open the decoder context\n");
+ av_free(v->dec_ctx);
+ v->dec_ctx = NULL;
+ return dec_uninit(v);
+ }
+
+ v->parser = av_parser_init(codec);
+ if (!v->parser) {
+ ast_log(LOG_WARNING, "Cannot initialize the decoder parser\n");
+ return dec_uninit(v);
+ }
+
+ v->d_frame = avcodec_alloc_frame();
+ if (!v->d_frame) {
+ ast_log(LOG_WARNING, "Cannot allocate decoding video frame\n");
+ return dec_uninit(v);
+ }
+ v->dec_in_cur = &v->dec_in[0]; /* buffer for incoming frames */
+ v->dec_in_dpy = NULL; /* nothing to display */
+
+ return v; /* ok */
+}
+/*------ end codec specific code -----*/
diff --git a/trunk/channels/vgrabbers.c b/trunk/channels/vgrabbers.c
new file mode 100644
index 000000000..0e4e62f56
--- /dev/null
+++ b/trunk/channels/vgrabbers.c
@@ -0,0 +1,346 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007, Luigi Rizzo
+ *
+ * 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.
+ */
+
+/*
+ * Video grabbers used in console_video.
+ *
+ * $Revision$
+ *
+ * Each grabber is implemented through open/read/close calls,
+ * plus an additional move() function used e.g. to change origin
+ * for the X grabber (this may be extended in the future to support
+ * more controls e.g. resolution changes etc.).
+ *
+ * open() should try to open and initialize the grabber, returning NULL on error.
+ * On success it allocates a descriptor for its private data (including
+ * a buffer for the video) and returns a pointer to the descriptor.
+ * read() will return NULL on failure, or a pointer to a buffer with data
+ * on success.
+ * close() should release resources.
+ * move() is optional.
+ * For more details look at the X11 grabber below.
+ *
+ * NOTE: at the moment we expect uncompressed video frames in YUV format,
+ * because this is what current sources supply and also is a convenient
+ * format for display. It is conceivable that one might want to support
+ * an already compressed stream, in which case we should redesign the
+ * pipeline used for the local source, which at the moment is
+ *
+ * .->--[loc_dpy]
+ * [src]-->--[enc_in]--+
+ * `->--[enc_out]
+ */
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include <sys/ioctl.h>
+#include "asterisk/file.h"
+#include "asterisk/utils.h" /* ast_calloc */
+
+#include "console_video.h"
+
+#if defined(HAVE_VIDEO_CONSOLE)
+
+#ifdef HAVE_X11
+
+/* A simple X11 grabber, supporting only truecolor formats */
+
+#include <X11/Xlib.h>
+
+/*! \brief internal info used by the X11 grabber */
+struct grab_x11_desc {
+ Display *dpy;
+ XImage *image;
+ int screen_width; /* width of X screen */
+ int screen_height; /* height of X screen */
+ struct fbuf_t b; /* geometry and pointer into the XImage */
+};
+
+/*! \brief open the grabber.
+ * We use the special name 'X11' to indicate this grabber.
+ */
+static void *grab_x11_open(const char *name, struct fbuf_t *geom, int fps)
+{
+ XImage *im;
+ int screen_num;
+ struct grab_x11_desc *v;
+ struct fbuf_t *b;
+
+ if (strcasecmp(name, "X11"))
+ return NULL; /* not us */
+ v = ast_calloc(1, sizeof(*v));
+ if (v == NULL)
+ return NULL; /* no memory */
+
+ /* init the connection with the X server */
+ v->dpy = XOpenDisplay(NULL);
+ if (v->dpy == NULL) {
+ ast_log(LOG_WARNING, "error opening display\n");
+ goto error;
+ }
+
+ v->b = *geom; /* copy geometry */
+ b = &v->b; /* shorthand */
+ /* find width and height of the screen */
+ screen_num = DefaultScreen(v->dpy);
+ v->screen_width = DisplayWidth(v->dpy, screen_num);
+ v->screen_height = DisplayHeight(v->dpy, screen_num);
+
+ v->image = im = XGetImage(v->dpy,
+ RootWindow(v->dpy, DefaultScreen(v->dpy)),
+ b->x, b->y, b->w, b->h, AllPlanes, ZPixmap);
+ if (v->image == NULL) {
+ ast_log(LOG_WARNING, "error creating Ximage\n");
+ goto error;
+ }
+ switch (im->bits_per_pixel) {
+ case 32:
+ b->pix_fmt = PIX_FMT_RGBA32;
+ break;
+ case 16:
+ b->pix_fmt = (im->green_mask == 0x7e0) ? PIX_FMT_RGB565 : PIX_FMT_RGB555;
+ break;
+ }
+
+ ast_log(LOG_NOTICE, "image: data %p %d bpp fmt %d, mask 0x%lx 0x%lx 0x%lx\n",
+ im->data,
+ im->bits_per_pixel,
+ b->pix_fmt,
+ im->red_mask, im->green_mask, im->blue_mask);
+
+ /* set the pointer but not the size as this is not malloc'ed */
+ b->data = (uint8_t *)im->data;
+ return v;
+
+error:
+ /* XXX maybe XDestroy (v->image) ? */
+ if (v->dpy)
+ XCloseDisplay(v->dpy);
+ v->dpy = NULL;
+ ast_free(v);
+ return NULL;
+}
+
+static struct fbuf_t *grab_x11_read(void *desc)
+{
+ /* read frame from X11 */
+ struct grab_x11_desc *v = desc;
+ struct fbuf_t *b = &v->b;
+
+ XGetSubImage(v->dpy,
+ RootWindow(v->dpy, DefaultScreen(v->dpy)),
+ b->x, b->y, b->w, b->h, AllPlanes, ZPixmap, v->image, 0, 0);
+
+ b->data = (uint8_t *)v->image->data;
+ return b;
+}
+
+static int boundary_checks(int x, int limit)
+{
+ return (x <= 0) ? 0 : (x > limit ? limit : x);
+}
+
+/*! \brief move the origin for the grabbed area, making sure we do not
+ * overflow the screen.
+ */
+static void grab_x11_move(void *desc, int dx, int dy)
+{
+ struct grab_x11_desc *v = desc;
+
+ v->b.x = boundary_checks(v->b.x + dx, v->screen_width - v->b.w);
+ v->b.y = boundary_checks(v->b.y + dy, v->screen_height - v->b.h);
+}
+
+/*! \brief disconnect from the server and release memory */
+static void *grab_x11_close(void *desc)
+{
+ struct grab_x11_desc *v = desc;
+
+ XCloseDisplay(v->dpy);
+ v->dpy = NULL;
+ v->image = NULL;
+ ast_free(v);
+ return NULL;
+}
+
+static struct grab_desc grab_x11_desc = {
+ .name = "X11",
+ .open = grab_x11_open,
+ .read = grab_x11_read,
+ .move = grab_x11_move,
+ .close = grab_x11_close,
+};
+#endif /* HAVE_X11 */
+
+#ifdef HAVE_VIDEODEV_H
+#include <linux/videodev.h> /* Video4Linux stuff is only used in grab_v4l1_open() */
+
+struct grab_v4l1_desc {
+ int fd; /* device handle */
+ struct fbuf_t b; /* buffer (allocated) with grabbed image */
+};
+
+/*! \brief
+ * Open the local video source and allocate a buffer
+ * for storing the image.
+ */
+static void *grab_v4l1_open(const char *dev, struct fbuf_t *geom, int fps)
+{
+ struct video_window vw = { 0 }; /* camera attributes */
+ struct video_picture vp;
+ int fd, i;
+ struct grab_v4l1_desc *v;
+ struct fbuf_t *b;
+
+ fd = open(dev, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "error opening camera %s\n", dev);
+ return NULL;
+ }
+
+ v = ast_calloc(1, sizeof(*v));
+ if (v == NULL) {
+ ast_log(LOG_WARNING, "no memory for camera %s\n", dev);
+ close(fd);
+ return NULL; /* no memory */
+ }
+ v->fd = fd;
+ v->b = *geom;
+ b = &v->b; /* shorthand */
+
+ i = fcntl(fd, F_GETFL);
+ if (-1 == fcntl(fd, F_SETFL, i | O_NONBLOCK)) {
+ /* non fatal, just emit a warning */
+ ast_log(LOG_WARNING, "error F_SETFL for %s [%s]\n",
+ dev, strerror(errno));
+ }
+ /* set format for the camera.
+ * In principle we could retry with a different format if the
+ * one we are asking for is not supported.
+ */
+ vw.width = b->w;
+ vw.height = b->h;
+ vw.flags = fps << 16;
+ if (ioctl(fd, VIDIOCSWIN, &vw) == -1) {
+ ast_log(LOG_WARNING, "error setting format for %s [%s]\n",
+ dev, strerror(errno));
+ goto error;
+ }
+ if (ioctl(fd, VIDIOCGPICT, &vp) == -1) {
+ ast_log(LOG_WARNING, "error reading picture info\n");
+ goto error;
+ }
+ ast_log(LOG_WARNING,
+ "contrast %d bright %d colour %d hue %d white %d palette %d\n",
+ vp.contrast, vp.brightness,
+ vp.colour, vp.hue,
+ vp.whiteness, vp.palette);
+ /* set the video format. Here again, we don't necessary have to
+ * fail if the required format is not supported, but try to use
+ * what the camera gives us.
+ */
+ b->pix_fmt = vp.palette;
+ vp.palette = VIDEO_PALETTE_YUV420P;
+ if (ioctl(v->fd, VIDIOCSPICT, &vp) == -1) {
+ ast_log(LOG_WARNING, "error setting palette, using %d\n",
+ b->pix_fmt);
+ } else
+ b->pix_fmt = vp.palette;
+ /* allocate the source buffer.
+ * XXX, the code here only handles yuv411, for other formats
+ * we need to look at pix_fmt and set size accordingly
+ */
+ b->size = (b->w * b->h * 3)/2; /* yuv411 */
+ ast_log(LOG_WARNING, "videodev %s opened, size %dx%d %d\n",
+ dev, b->w, b->h, b->size);
+ b->data = ast_calloc(1, b->size);
+ if (!b->data) {
+ ast_log(LOG_WARNING, "error allocating buffer %d bytes\n",
+ b->size);
+ goto error;
+ }
+ ast_log(LOG_WARNING, "success opening camera\n");
+ return v;
+
+error:
+ close(v->fd);
+ fbuf_free(b);
+ ast_free(v);
+ return NULL;
+}
+
+/*! \brief read until error, no data or buffer full.
+ * This might be blocking but no big deal since we are in the
+ * display thread.
+ */
+static struct fbuf_t *grab_v4l1_read(void *desc)
+{
+ struct grab_v4l1_desc *v = desc;
+ struct fbuf_t *b = &v->b;
+ for (;;) {
+ int r, l = b->size - b->used;
+ r = read(v->fd, b->data + b->used, l);
+ // ast_log(LOG_WARNING, "read %d of %d bytes from webcam\n", r, l);
+ if (r < 0) /* read error */
+ break;
+ if (r == 0) /* no data */
+ break;
+ b->used += r;
+ if (r == l) {
+ b->used = 0; /* prepare for next frame */
+ return b;
+ }
+ }
+ return NULL;
+}
+
+static void *grab_v4l1_close(void *desc)
+{
+ struct grab_v4l1_desc *v = desc;
+
+ close(v->fd);
+ v->fd = -1;
+ fbuf_free(&v->b);
+ ast_free(v);
+ return NULL;
+}
+
+/*! \brief our descriptor. We don't have .move */
+static struct grab_desc grab_v4l1_desc = {
+ .name = "v4l1",
+ .open = grab_v4l1_open,
+ .read = grab_v4l1_read,
+ .close = grab_v4l1_close,
+};
+#endif /* HAVE_VIDEODEV_H */
+
+/*
+ * Here you can add more grabbers, e.g. V4L2, firewire,
+ * a file, a still image...
+ */
+
+/*! \brief The list of grabbers supported, with a NULL at the end */
+struct grab_desc *console_grabbers[] = {
+#ifdef HAVE_X11
+ &grab_x11_desc,
+#endif
+#ifdef HAVE_VIDEODEV_H
+ &grab_v4l1_desc,
+#endif
+ NULL
+};
+
+#endif /* HAVE_VIDEO_CONSOLE */
diff --git a/trunk/channels/xpmr/sinetabx.h b/trunk/channels/xpmr/sinetabx.h
new file mode 100755
index 000000000..4c4574850
--- /dev/null
+++ b/trunk/channels/xpmr/sinetabx.h
@@ -0,0 +1,290 @@
+/*
+ * sinetabx.h - for Xelatec Private Mobile Radio Processes
+ *
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ *
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.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 Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+
+#ifndef XPMR_SINETABX_H
+#define XPMR_SINETABX_H 1
+
+#define SAMPLES_PER_SINE 256
+
+const i16 sinetablex[]={
+0, // 0
+804, // 1
+1608, // 2
+2410, // 3
+3212, // 4
+4011, // 5
+4808, // 6
+5602, // 7
+6393, // 8
+7179, // 9
+7962, // 10
+8739, // 11
+9512, // 12
+10278, // 13
+11039, // 14
+11793, // 15
+12539, // 16
+13279, // 17
+14010, // 18
+14732, // 19
+15446, // 20
+16151, // 21
+16846, // 22
+17530, // 23
+18204, // 24
+18868, // 25
+19519, // 26
+20159, // 27
+20787, // 28
+21403, // 29
+22005, // 30
+22594, // 31
+23170, // 32
+23731, // 33
+24279, // 34
+24811, // 35
+25329, // 36
+25832, // 37
+26319, // 38
+26790, // 39
+27245, // 40
+27683, // 41
+28105, // 42
+28510, // 43
+28898, // 44
+29268, // 45
+29621, // 46
+29956, // 47
+30273, // 48
+30571, // 49
+30852, // 50
+31113, // 51
+31356, // 52
+31580, // 53
+31785, // 54
+31971, // 55
+32137, // 56
+32285, // 57
+32412, // 58
+32521, // 59
+32609, // 60
+32678, // 61
+32728, // 62
+32757, // 63
+32767, // 64
+32757, // 65
+32728, // 66
+32678, // 67
+32609, // 68
+32521, // 69
+32412, // 70
+32285, // 71
+32137, // 72
+31971, // 73
+31785, // 74
+31580, // 75
+31356, // 76
+31113, // 77
+30852, // 78
+30571, // 79
+30273, // 80
+29956, // 81
+29621, // 82
+29268, // 83
+28898, // 84
+28510, // 85
+28105, // 86
+27683, // 87
+27245, // 88
+26790, // 89
+26319, // 90
+25832, // 91
+25329, // 92
+24811, // 93
+24279, // 94
+23731, // 95
+23170, // 96
+22594, // 97
+22005, // 98
+21403, // 99
+20787, // 100
+20159, // 101
+19519, // 102
+18868, // 103
+18204, // 104
+17530, // 105
+16846, // 106
+16151, // 107
+15446, // 108
+14732, // 109
+14010, // 110
+13279, // 111
+12539, // 112
+11793, // 113
+11039, // 114
+10278, // 115
+9512, // 116
+8739, // 117
+7962, // 118
+7179, // 119
+6393, // 120
+5602, // 121
+4808, // 122
+4011, // 123
+3212, // 124
+2410, // 125
+1608, // 126
+804, // 127
+0, // 128
+-804, // 129
+-1608, // 130
+-2410, // 131
+-3212, // 132
+-4011, // 133
+-4808, // 134
+-5602, // 135
+-6393, // 136
+-7179, // 137
+-7962, // 138
+-8739, // 139
+-9512, // 140
+-10278, // 141
+-11039, // 142
+-11793, // 143
+-12539, // 144
+-13279, // 145
+-14010, // 146
+-14732, // 147
+-15446, // 148
+-16151, // 149
+-16846, // 150
+-17530, // 151
+-18204, // 152
+-18868, // 153
+-19519, // 154
+-20159, // 155
+-20787, // 156
+-21403, // 157
+-22005, // 158
+-22594, // 159
+-23170, // 160
+-23731, // 161
+-24279, // 162
+-24811, // 163
+-25329, // 164
+-25832, // 165
+-26319, // 166
+-26790, // 167
+-27245, // 168
+-27683, // 169
+-28105, // 170
+-28510, // 171
+-28898, // 172
+-29268, // 173
+-29621, // 174
+-29956, // 175
+-30273, // 176
+-30571, // 177
+-30852, // 178
+-31113, // 179
+-31356, // 180
+-31580, // 181
+-31785, // 182
+-31971, // 183
+-32137, // 184
+-32285, // 185
+-32412, // 186
+-32521, // 187
+-32609, // 188
+-32678, // 189
+-32728, // 190
+-32757, // 191
+-32767, // 192
+-32757, // 193
+-32728, // 194
+-32678, // 195
+-32609, // 196
+-32521, // 197
+-32412, // 198
+-32285, // 199
+-32137, // 200
+-31971, // 201
+-31785, // 202
+-31580, // 203
+-31356, // 204
+-31113, // 205
+-30852, // 206
+-30571, // 207
+-30273, // 208
+-29956, // 209
+-29621, // 210
+-29268, // 211
+-28898, // 212
+-28510, // 213
+-28105, // 214
+-27683, // 215
+-27245, // 216
+-26790, // 217
+-26319, // 218
+-25832, // 219
+-25329, // 220
+-24811, // 221
+-24279, // 222
+-23731, // 223
+-23170, // 224
+-22594, // 225
+-22005, // 226
+-21403, // 227
+-20787, // 228
+-20159, // 229
+-19519, // 230
+-18868, // 231
+-18204, // 232
+-17530, // 233
+-16846, // 234
+-16151, // 235
+-15446, // 236
+-14732, // 237
+-14010, // 238
+-13279, // 239
+-12539, // 240
+-11793, // 241
+-11039, // 242
+-10278, // 243
+-9512, // 244
+-8739, // 245
+-7962, // 246
+-7179, // 247
+-6393, // 248
+-5602, // 249
+-4808, // 250
+-4011, // 251
+-3212, // 252
+-2410, // 253
+-1608, // 254
+-804, // 255
+};
+
+#endif /* !XPMR_SINETABX_H */
diff --git a/trunk/channels/xpmr/xpmr.c b/trunk/channels/xpmr/xpmr.c
new file mode 100755
index 000000000..a799ca9d3
--- /dev/null
+++ b/trunk/channels/xpmr/xpmr.c
@@ -0,0 +1,2256 @@
+/*
+ * xpmr.c - Xelatec Private Mobile Radio Processes
+ *
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ *
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.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 Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+/*
+ FYI = For Your Information
+ PMR = Private Mobile Radio
+ RX = Receive
+ TX = Transmit
+ CTCSS = Continuous Tone Coded Squelch System
+ TONE = Same as above.
+ LSD = Low Speed Data, subaudible signaling. May be tones or codes.
+ VOX = Voice Operated Transmit
+ DSP = Digital Signal Processing
+ LPF = Low Pass Filter
+ FIR = Finite Impulse Response (Filter)
+ IIR = Infinite Impulse Response (Filter)
+*/
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "xpmr.h"
+#include "xpmr_coef.h"
+#include "sinetabx.h"
+
+static i16 pmrChanIndex=0; // count of created pmr instances
+
+/*
+ Convert a Frequency in Hz to a zero based CTCSS Table index
+*/
+i16 CtcssFreqIndex(float freq)
+{
+ i16 i,hit=-1;
+
+ for(i=0;i<CTCSS_NUM_CODES;i++){
+ if(freq==freq_ctcss[i])hit=i;
+ }
+ return hit;
+}
+/*
+ pmr_rx_frontend
+ Takes a block of data and low pass filters it.
+ Determines the amplitude of high frequency noise for carrier detect.
+ Decimates input data to change the rate.
+*/
+i16 pmr_rx_frontend(t_pmr_sps *mySps)
+{
+ #define DCgainBpfNoise 65536
+
+ i16 samples,iOutput, *input, *output, *noutput;
+ i16 *x, *coef, *coef2;
+ i32 i, naccum, outputGain, calcAdjust;
+ i64 y;
+ i16 nx, hyst, setpt, compOut;
+ i16 amax, amin, apeak, discounteru, discounterl, discfactor;
+ i16 decimator, decimate, doNoise;
+
+ TRACEX(("pmr_rx_frontend()\n"));
+
+ if(!mySps->enabled)return(1);
+
+ decimator = mySps->decimator;
+ decimate = mySps->decimate;
+
+ input = mySps->source;
+ output = mySps->sink;
+ noutput = mySps->parentChan->pRxNoise;
+
+ nx = mySps->nx;
+ coef = mySps->coef;
+ coef2 = mySps->coef2;
+
+ calcAdjust = mySps->calcAdjust;
+ outputGain = mySps->outputGain;
+
+ amax=mySps->amax;
+ amin=mySps->amin;
+ apeak=mySps->apeak;
+ discounteru=mySps->discounteru;
+ discounterl=mySps->discounterl;
+ discfactor=mySps->discfactor;
+ setpt=mySps->setpt;
+ hyst=mySps->hyst;
+ compOut=mySps->compOut;
+
+ samples=mySps->nSamples*decimate;
+ x=mySps->x;
+ iOutput=0;
+
+ if(mySps->parentChan->rxCdType!=CD_XPMR_VOX)doNoise=1;
+ else doNoise=0;
+
+ for(i=0;i<samples;i++)
+ {
+ i16 n;
+
+ //shift the old samples
+ for(n=nx-1; n>0; n--)
+ x[n] = x[n-1];
+
+ x[0] = input[i*2];
+
+ --decimator;
+
+ if(decimator<=0)
+ {
+ decimator=decimate;
+
+ y=0;
+ for(n=0; n<nx; n++)
+ y += coef[n] * x[n];
+
+ y=((y/calcAdjust)*outputGain)/M_Q8;
+
+ if(y>32767)y=32767;
+ else if(y<-32767)y=-32767;
+
+ output[iOutput]=y; // Rx Baseband decimated
+ noutput[iOutput++] = apeak; // Rx Noise
+ }
+
+ if(doNoise)
+ {
+ // calculate noise output
+ naccum=0;
+ for(n=0; n<nx; n++)
+ naccum += coef_fir_bpf_noise_1[n] * x[n];
+
+ naccum /= DCgainBpfNoise;
+
+ if(naccum>amax)
+ {
+ amax=naccum;
+ discounteru=discfactor;
+ }
+ else if(--discounteru<=0)
+ {
+ discounteru=discfactor;
+ amax=(i32)((amax*32700)/32768);
+ }
+
+ if(naccum<amin)
+ {
+ amin=naccum;
+ discounterl=discfactor;
+ }
+ else if(--discounterl<=0)
+ {
+ discounterl=discfactor;
+ amin=(i32)((amin*32700)/32768);
+ }
+
+ apeak=(amax-amin)/2;
+
+ } // if doNoise
+ }
+
+ if(doNoise)
+ {
+ ((t_pmr_chan *)(mySps->parentChan))->rxRssi=apeak;
+
+ if(apeak>setpt || (compOut&&(apeak>(setpt-hyst)))) compOut=1;
+ else compOut=0;
+ mySps->compOut=compOut;
+ mySps->amax=amax;
+ mySps->amin=amin;
+ mySps->apeak=apeak;
+ mySps->discounteru=discounteru;
+ mySps->discounterl=discounterl;
+ }
+
+ return 0;
+}
+/*
+ pmr general purpose fir
+ works on a block of samples
+*/
+i16 pmr_gp_fir(t_pmr_sps *mySps)
+{
+ i32 nsamples,inputGain,outputGain,calcAdjust;
+ i16 *input, *output;
+ i16 *x, *coef;
+ i32 i, ii;
+ i16 nx, hyst, setpt, compOut;
+ i16 amax, amin, apeak=0, discounteru=0, discounterl=0, discfactor;
+ i16 decimator, decimate, interpolate;
+ i16 numChanOut, selChanOut, mixOut, monoOut;
+
+ TRACEX(("pmr_gp_fir() %i\n",mySps->enabled));
+
+ if(!mySps->enabled)return(1);
+
+ inputGain = mySps->inputGain;
+ calcAdjust = mySps->calcAdjust;
+ outputGain = mySps->outputGain;
+
+ input = mySps->source;
+ output = mySps->sink;
+ x = mySps->x;
+ nx = mySps->nx;
+ coef = mySps->coef;
+
+ decimator = mySps->decimator;
+ decimate = mySps->decimate;
+ interpolate = mySps->interpolate;
+
+ setpt = mySps->setpt;
+ compOut = mySps->compOut;
+
+ inputGain = mySps->inputGain;
+ outputGain = mySps->outputGain;
+ numChanOut = mySps->numChanOut;
+ selChanOut = mySps->selChanOut;
+ mixOut = mySps->mixOut;
+ monoOut = mySps->monoOut;
+
+ amax=mySps->amax;
+ amin=mySps->amin;
+
+ discfactor=mySps->discfactor;
+ hyst=mySps->hyst;
+ setpt=mySps->setpt;
+ nsamples=mySps->nSamples;
+
+ if(mySps->option==3)
+ {
+ mySps->option=0;
+ mySps->enabled=0;
+ for(i=0;i<nsamples;i++)
+ {
+ if(monoOut)
+ output[(i*2)]=output[(i*2)+1]=0;
+ else
+ output[(i*numChanOut)+selChanOut]=0;
+ }
+ return 0;
+ }
+
+ ii=0;
+ for(i=0;i<nsamples;i++)
+ {
+ int ix;
+
+ int64_t y=0;
+
+ if(decimate<0)
+ {
+ decimator=decimate;
+ }
+
+ for(ix=0;ix<interpolate;ix++)
+ {
+ i16 n;
+ y=0;
+
+ for(n=nx-1; n>0; n--)
+ x[n] = x[n-1];
+ x[0] = (input[i]*inputGain)/M_Q8;
+
+ #if 0
+ --decimator;
+ if(decimator<=0)
+ {
+ decimator=decimate;
+ for(n=0; n<nx; n++)
+ y += coef[n] * x[n];
+ y /= (outputGain*3);
+ output[ii++]=y;
+ }
+ #else
+ for(n=0; n<nx; n++)
+ y += coef[n] * x[n];
+
+ y=((y/calcAdjust)*outputGain)/M_Q8;
+
+ if(mixOut){
+ if(monoOut){
+ output[(ii*2)]=output[(ii*2)+1]+=y;
+ }
+ else{
+ output[(ii*numChanOut)+selChanOut]+=y;
+ }
+ }
+ else{
+ if(monoOut){
+ output[(ii*2)]=output[(ii*2)+1]=y;
+ }
+ else{
+ output[(ii*numChanOut)+selChanOut]=y;
+ }
+ }
+ ii++;
+ #endif
+ }
+
+ // amplitude detector
+ if(setpt)
+ {
+ i16 accum=y;
+
+ if(accum>amax)
+ {
+ amax=accum;
+ discounteru=discfactor;
+ }
+ else if(--discounteru<=0)
+ {
+ discounteru=discfactor;
+ amax=(i32)((amax*32700)/32768);
+ }
+
+ if(accum<amin)
+ {
+ amin=accum;
+ discounterl=discfactor;
+ }
+ else if(--discounterl<=0)
+ {
+ discounterl=discfactor;
+ amin=(i32)((amin*32700)/32768);
+ }
+
+ apeak = (i32)(amax-amin)/2;
+
+ if(apeak>setpt)compOut=1;
+ else if(compOut&&(apeak<(setpt-hyst)))compOut=0;
+ }
+ }
+
+ mySps->decimator = decimator;
+
+ mySps->amax=amax;
+ mySps->amin=amin;
+ mySps->apeak=apeak;
+ mySps->discounteru=discounteru;
+ mySps->discounterl=discounterl;
+
+ mySps->compOut=compOut;
+
+ return 0;
+}
+/*
+ general purpose integrator lpf
+*/
+i16 gp_inte_00(t_pmr_sps *mySps)
+{
+ i16 npoints;
+ i16 *input, *output;
+
+ i32 inputGain, outputGain,calcAdjust;
+ i32 i;
+ i32 accum;
+
+ i32 state00;
+ i16 coeff00, coeff01;
+
+ TRACEX(("gp_inte_00() %i\n",mySps->enabled));
+ if(!mySps->enabled)return(1);
+
+ input = mySps->source;
+ output = mySps->sink;
+
+ npoints=mySps->nSamples;
+
+ inputGain=mySps->inputGain;
+ outputGain=mySps->outputGain;
+ calcAdjust=mySps->calcAdjust;
+
+ coeff00=((i16*)mySps->coef)[0];
+ coeff01=((i16*)mySps->coef)[1];
+ state00=((i32*)mySps->x)[0];
+
+ // note fixed gain of 2 to compensate for attenuation
+ // in passband
+
+ for(i=0;i<npoints;i++)
+ {
+ accum=input[i];
+ state00 = accum + (state00*coeff01)/M_Q15;
+ accum = (state00*coeff00)/(M_Q15/4);
+ output[i]=(accum*outputGain)/M_Q8;
+ }
+
+ ((i32*)(mySps->x))[0]=state00;
+
+ return 0;
+}
+/*
+ general purpose differentiator hpf
+*/
+i16 gp_diff(t_pmr_sps *mySps)
+{
+ i16 *input, *output;
+ i16 npoints;
+ i32 inputGain, outputGain, calcAdjust;
+ i32 i;
+ i32 temp0,temp1;
+ i16 x0;
+ i32 y0;
+ i16 a0,a1;
+ i16 b0;
+ i16 *coef;
+ i16 *x;
+
+ input = mySps->source;
+ output = mySps->sink;
+
+ npoints=mySps->nSamples;
+
+ inputGain=mySps->inputGain;
+ outputGain=mySps->outputGain;
+ calcAdjust=mySps->calcAdjust;
+
+ coef=(i16*)(mySps->coef);
+ x=(i16*)(mySps->x);
+ a0=coef[0];
+ a1=coef[1];
+ b0=coef[2];
+
+ x0=x[0];
+
+ TRACEX(("gp_diff()\n"));
+
+ for (i=0;i<npoints;i++)
+ {
+ temp0 = x0 * a1;
+ x0 = input[i];
+ temp1 = input[i] * a0;
+ y0 = (temp0 + temp1)/calcAdjust;
+ output[i]=(y0*outputGain)/M_Q8;
+ }
+
+ x[0]=x0;
+
+ return 0;
+}
+/* ----------------------------------------------------------------------
+ CenterSlicer
+*/
+i16 CenterSlicer(t_pmr_sps *mySps)
+{
+ i16 npoints,lhit,uhit;
+ i16 *input, *output, *buff;
+
+ i32 inputGain, outputGain, inputGainB;
+ i32 i;
+ i32 accum;
+
+ i32 amax; // buffer amplitude maximum
+ i32 amin; // buffer amplitude minimum
+ i32 apeak; // buffer amplitude peak
+ i32 center;
+ i32 setpt; // amplitude set point for peak tracking
+
+ i32 discounteru; // amplitude detector integrator discharge counter upper
+ i32 discounterl; // amplitude detector integrator discharge counter lower
+ i32 discfactor; // amplitude detector integrator discharge factor
+
+ TRACEX(("CenterSlicer() %i\n",mySps->enabled));
+
+ input = mySps->source;
+ output = mySps->sink;
+ buff = mySps->buff;
+
+ npoints=mySps->nSamples;
+
+ inputGain=mySps->inputGain;
+ outputGain=mySps->outputGain;
+ inputGainB=mySps->inputGainB;
+
+ amax=mySps->amax;
+ amin=mySps->amin;
+ setpt=mySps->setpt;
+ apeak=mySps->apeak;
+ discounteru=mySps->discounteru;
+ discounterl=mySps->discounterl;
+
+ discfactor=mySps->discfactor;
+ npoints=mySps->nSamples;
+
+ for(i=0;i<npoints;i++)
+ {
+ accum=input[i];
+
+ lhit=uhit=0;
+
+ if(accum>amax)
+ {
+ amax=accum;
+ uhit=1;
+ if(amin<(amax-setpt))
+ {
+ amin=(amax-setpt);
+ lhit=1;
+ }
+ }
+ else if(accum<amin)
+ {
+ amin=accum;
+ lhit=1;
+ if(amax>(amin+setpt))
+ {
+ amax=(amin+setpt);
+ uhit=1;
+ }
+ }
+
+ if(--discounteru<=0 && amax>0)
+ {
+ amax--;
+ uhit=1;
+ }
+
+ if(--discounterl<=0 && amin<0)
+ {
+ amin++;
+ lhit=1;
+ }
+
+ if(uhit)discounteru=discfactor;
+ if(lhit)discounterl=discfactor;
+
+ apeak = (amax-amin)/2;
+ center = (amax+amin)/2;
+ accum = accum - center;
+ output[i]=accum;
+
+ // do limiter function
+ if(accum>inputGainB)accum=inputGainB;
+ else if(accum<-inputGainB)accum=-inputGainB;
+ buff[i]=accum;
+
+ #if XPMR_DEBUG0 == 1
+ #if 0
+ mySps->debugBuff0[i]=center;
+ #endif
+ #if 0
+ if(mySps->parentChan->frameCountRx&0x01) mySps->parentChan->prxDebug1[i]=amax;
+ else mySps->parentChan->prxDebug1[i]=amin;
+ #endif
+ #endif
+ }
+
+ mySps->amax=amax;
+ mySps->amin=amin;
+ mySps->apeak=apeak;
+ mySps->discounteru=discounteru;
+ mySps->discounterl=discounterl;
+
+ return 0;
+}
+/* ----------------------------------------------------------------------
+ MeasureBlock
+ determine peak amplitude
+*/
+i16 MeasureBlock(t_pmr_sps *mySps)
+{
+ i16 npoints;
+ i16 *input, *output;
+
+ i32 inputGain, outputGain;
+ i32 i;
+ i32 accum;
+
+ i16 amax; // buffer amplitude maximum
+ i16 amin; // buffer amplitude minimum
+ i16 apeak=0; // buffer amplitude peak (peak to peak)/2
+ i16 setpt; // amplitude set point for amplitude comparator
+
+ i32 discounteru; // amplitude detector integrator discharge counter upper
+ i32 discounterl; // amplitude detector integrator discharge counter lower
+ i32 discfactor; // amplitude detector integrator discharge factor
+
+ TRACEX(("MeasureBlock() %i\n",mySps->enabled));
+
+ if(!mySps->enabled)return 1;
+
+ if(mySps->option==3)
+ {
+ mySps->amax = mySps->amin = mySps->apeak = \
+ mySps->discounteru = mySps->discounterl = \
+ mySps->enabled = 0;
+ return 1;
+ }
+
+ input = mySps->source;
+ output = mySps->sink;
+
+ npoints=mySps->nSamples;
+
+ inputGain=mySps->inputGain;
+ outputGain=mySps->outputGain;
+
+ amax=mySps->amax;
+ amin=mySps->amin;
+ setpt=mySps->setpt;
+ discounteru=mySps->discounteru;
+ discounterl=mySps->discounterl;
+
+ discfactor=mySps->discfactor;
+ npoints=mySps->nSamples;
+
+ for(i=0;i<npoints;i++)
+ {
+ accum=input[i];
+
+ if(accum>amax)
+ {
+ amax=accum;
+ discounteru=discfactor;
+ }
+ else if(--discounteru<=0)
+ {
+ discounteru=discfactor;
+ amax=(i32)((amax*32700)/32768);
+ }
+
+ if(accum<amin)
+ {
+ amin=accum;
+ discounterl=discfactor;
+ }
+ else if(--discounterl<=0)
+ {
+ discounterl=discfactor;
+ amin=(i32)((amin*32700)/32768);
+ }
+
+ apeak = (i32)(amax-amin)/2;
+ if(output)output[i]=apeak;
+ }
+
+ mySps->amax=amax;
+ mySps->amin=amin;
+ mySps->apeak=apeak;
+ mySps->discounteru=discounteru;
+ mySps->discounterl=discounterl;
+ if(apeak>=setpt) mySps->compOut=1;
+ else mySps->compOut=0;
+
+ //TRACEX((" -MeasureBlock()=%i\n",mySps->apeak));
+ return 0;
+}
+/*
+ SoftLimiter
+*/
+i16 SoftLimiter(t_pmr_sps *mySps)
+{
+ i16 npoints;
+ //i16 samples, lhit,uhit;
+ i16 *input, *output;
+
+ i32 inputGain, outputGain;
+ i32 i;
+ i32 accum;
+ i32 tmp;
+
+ i32 amax; // buffer amplitude maximum
+ i32 amin; // buffer amplitude minimum
+ //i32 apeak; // buffer amplitude peak
+ i32 setpt; // amplitude set point for amplitude comparator
+ i16 compOut; // amplitude comparator output
+
+ input = mySps->source;
+ output = mySps->sink;
+
+ inputGain=mySps->inputGain;
+ outputGain=mySps->outputGain;
+
+ npoints=mySps->nSamples;
+
+ setpt=mySps->setpt;
+ amax=(setpt*124)/128;
+ amin=-amax;
+
+ TRACEX(("SoftLimiter() %i %i %i) \n",amin, amax,setpt));
+
+ for(i=0;i<npoints;i++)
+ {
+ accum=input[i];
+ //accum=input[i]*mySps->inputGain/256;
+
+ if(accum>setpt)
+ {
+ tmp=((accum-setpt)*4)/128;
+ accum=setpt+tmp;
+ if(accum>amax)accum=amax;
+ compOut=1;
+ accum=setpt;
+ }
+ else if(accum<-setpt)
+ {
+ tmp=((accum+setpt)*4)/128;
+ accum=(-setpt)-tmp;
+ if(accum<amin)accum=amin;
+ compOut=1;
+ accum=-setpt;
+ }
+
+ output[i]=(accum*outputGain)/M_Q8;
+ }
+
+ return 0;
+}
+/*
+ SigGen() - sine, square function generator
+ sps overloaded values
+ discfactor = phase factor
+ discfactoru = phase index
+ if source is not NULL then mix it in!
+
+ sign table and output gain are in Q15 format (32767=.999)
+*/
+i16 SigGen(t_pmr_sps *mySps)
+{
+ #define PH_FRACT_FACT 128
+
+ i32 ph;
+ i16 i,outputgain,waveform,numChanOut,selChanOut;
+ i32 accum;
+
+ TRACEX(("SigGen(%i) \n",mySps->option));
+
+ if(!mySps->freq ||!mySps->enabled)return 0;
+
+ outputgain=mySps->outputGain;
+ waveform=0;
+ numChanOut=mySps->numChanOut;
+ selChanOut=mySps->selChanOut;
+
+ if(mySps->option==1)
+ {
+ mySps->option=0;
+ mySps->state=1;
+ mySps->discfactor=
+ (SAMPLES_PER_SINE*mySps->freq*PH_FRACT_FACT)/mySps->sampleRate/10;
+
+ TRACEX((" SigGen() discfactor = %i\n",mySps->discfactor));
+ if(mySps->discounterl)mySps->state=2;
+ }
+ else if(mySps->option==2)
+ {
+ i16 shiftfactor=CTCSS_TURN_OFF_SHIFT;
+ // phase shift request
+ mySps->option=0;
+ mySps->state=2;
+ mySps->discounterl=CTCSS_TURN_OFF_TIME-(2*MS_PER_FRAME); //
+
+ mySps->discounteru = \
+ (mySps->discounteru + (((SAMPLES_PER_SINE*shiftfactor)/360)*PH_FRACT_FACT)) % (SAMPLES_PER_SINE*PH_FRACT_FACT);
+ //printf("shiftfactor = %i\n",shiftfactor);
+ //shiftfactor+=10;
+ }
+ else if(mySps->option==3)
+ {
+ // stop it and clear the output buffer
+ mySps->option=0;
+ mySps->state=0;
+ mySps->enabled=0;
+ for(i=0;i<mySps->nSamples;i++)
+ mySps->sink[(i*numChanOut)+selChanOut]=0;
+ return(0);
+ }
+ else if(mySps->state==2)
+ {
+ // doing turn off
+ mySps->discounterl-=MS_PER_FRAME;
+ if(mySps->discounterl<=0)
+ {
+ mySps->option=3;
+ mySps->state=2;
+ }
+ }
+ else if(mySps->state==0)
+ {
+ return(0);
+ }
+
+ ph=mySps->discounteru;
+
+ for(i=0;i<mySps->nSamples;i++)
+ {
+ if(!waveform)
+ {
+ // sine
+ //tmp=(sinetablex[ph/PH_FRACT_FACT]*amplitude)/M_Q16;
+ accum=sinetablex[ph/PH_FRACT_FACT];
+ accum=(accum*outputgain)/M_Q8;
+ }
+ else
+ {
+ // square
+ if(ph>SAMPLES_PER_SINE/2)
+ accum=outputgain/M_Q8;
+ else
+ accum=-outputgain/M_Q8;
+ }
+
+ if(mySps->source)accum+=mySps->source[i];
+
+ mySps->sink[(i*numChanOut)+selChanOut]=accum;
+
+ ph=(ph+mySps->discfactor)%(SAMPLES_PER_SINE*PH_FRACT_FACT);
+ }
+
+ mySps->discounteru=ph;
+
+ return 0;
+}
+/*
+ adder/mixer
+ takes existing buffer and adds source buffer to destination buffer
+ sink buffer = (sink buffer * gain) + source buffer
+*/
+i16 pmrMixer(t_pmr_sps *mySps)
+{
+ i32 accum;
+ i16 i, *input, *inputB, *output;
+ i16 inputGain, inputGainB; // apply to input data in Q7.8 format
+ i16 outputGain; // apply to output data in Q7.8 format
+ i16 discounteru,discounterl,amax,amin,setpt,discfactor;
+ i16 npoints,uhit,lhit,apeak,measPeak;
+
+ TRACEX(("pmrMixer()\n"));
+
+ input = mySps->source;
+ inputB = mySps->sourceB;
+ output = mySps->sink;
+
+ inputGain=mySps->inputGain;
+ inputGainB=mySps->inputGainB;
+ outputGain=mySps->outputGain;
+
+ amax=mySps->amax;
+ amin=mySps->amin;
+ setpt=mySps->setpt;
+ discounteru=mySps->discounteru;
+ discounterl=mySps->discounteru;
+
+ discfactor=mySps->discfactor;
+ npoints=mySps->nSamples;
+ measPeak=mySps->measPeak;
+
+ for(i=0;i<npoints;i++)
+ {
+ accum = ((input[i]*inputGain)/M_Q8) +
+ ((inputB[i]*inputGainB)/M_Q8);
+
+ accum=(accum*outputGain)/M_Q8;
+ output[i]=accum;
+
+ if(measPeak){
+ lhit=uhit=0;
+
+ if(accum>amax){
+ amax=accum;
+ uhit=1;
+ if(amin<(amax-setpt)){
+ amin=(amax-setpt);
+ lhit=1;
+ }
+ }
+ else if(accum<amin){
+ amin=accum;
+ lhit=1;
+ if(amax>(amin+setpt)){
+ amax=(amin+setpt);
+ uhit=1;
+ }
+ }
+
+ if(--discounteru<=0 && amax>0){
+ amax--;
+ uhit=1;
+ }
+
+ if(--discounterl<=0 && amin<0){
+ amin++;
+ lhit=1;
+ }
+
+ if(uhit)discounteru=discfactor;
+ if(lhit)discounterl=discfactor;
+ }
+ }
+
+ if(measPeak){
+ apeak = (amax-amin)/2;
+ mySps->apeak=apeak;
+ mySps->amax=amax;
+ mySps->amin=amin;
+ mySps->discounteru=discounteru;
+ mySps->discounterl=discounterl;
+ }
+
+ return 0;
+}
+/*
+ DelayLine
+*/
+i16 DelayLine(t_pmr_sps *mySps)
+{
+ i16 *input, *output, *buff;
+ i16 i, npoints,buffsize,inindex,outindex;
+
+ TRACEX((" DelayLine() %i\n",mySps->enabled));
+
+ input = mySps->source;
+ output = mySps->sink;
+ buff = (i16*)(mySps->buff);
+ buffsize = mySps->buffSize;
+ npoints = mySps->nSamples;
+
+ outindex = mySps->buffOutIndex;
+ inindex = outindex + mySps->buffLead;
+
+ for(i=0;i<npoints;i++)
+ {
+ inindex %= buffsize;
+ outindex %= buffsize;
+
+ buff[inindex]=input[i];
+ output[i]=buff[outindex];
+ inindex++;
+ outindex++;
+ }
+ mySps->buffOutIndex=outindex;
+
+ return 0;
+}
+/*
+ Continuous Tone Coded Squelch (CTCSS) Detector
+*/
+i16 ctcss_detect(t_pmr_chan *pmrChan)
+{
+ i16 i,points2do, points=0, *pInput, hit, thit,relax;
+ i16 tnum, tmp, indexWas=0, indexNow, gain, peakwas=0, diffpeak;
+ i16 difftrig;
+ i16 lasttv0=0, lasttv1=0, lasttv2=0, tv0, tv1, tv2, indexDebug;
+
+ TRACEX(("ctcss_detect(%p) %i %i %i %i\n",pmrChan,
+ pmrChan->rxCtcss->enabled,
+ pmrChan->rxCtcssIndex,
+ pmrChan->rxCtcss->testIndex,
+ pmrChan->rxCtcss->decode));
+
+ if(!pmrChan->rxCtcss->enabled)return(1);
+
+ relax = pmrChan->rxCtcss->relax;
+ pInput = pmrChan->rxCtcss->input;
+ gain = pmrChan->rxCtcss->gain;
+
+ if(relax) difftrig=(-0.1*M_Q15);
+ else difftrig=(-0.05*M_Q15);
+
+ thit=hit=-1;
+
+ //TRACEX((" ctcss_detect() %i %i %i %i\n", CTCSS_NUM_CODES,0,0,0));
+
+ for(tnum=0;tnum<CTCSS_NUM_CODES;tnum++)
+ {
+ i32 accum, peak;
+ t_tdet *ptdet;
+ i16 fudgeFactor;
+ i16 binFactor;
+
+ //TRACEX((" ctcss_detect() tnum=%i %i\n",tnum,pmrChan->rxCtcssMap[tnum]));
+
+ if( (pmrChan->rxCtcssMap[tnum] < 0) ||
+ (pmrChan->rxCtcss->decode>=0 && (tnum!= pmrChan->rxCtcss->decode)) ||
+ (!pmrChan->rxCtcss->multiFreq && (tnum!= pmrChan->rxCtcssIndex))
+ )
+ continue;
+
+ //TRACEX((" ctcss_detect() tnum=%i\n",tnum));
+
+ ptdet=&(pmrChan->rxCtcss->tdet[tnum]);
+ indexDebug=0;
+ points=points2do=pmrChan->nSamplesRx;
+ fudgeFactor=ptdet->fudgeFactor;
+ binFactor=ptdet->binFactor;
+
+ while(ptdet->counter < (points2do*CTCSS_SCOUNT_MUL))
+ {
+ //TRACEX((" ctcss_detect() - inner loop\n"));
+ tmp=(ptdet->counter/CTCSS_SCOUNT_MUL)+1;
+ ptdet->counter-=(tmp*CTCSS_SCOUNT_MUL);
+ points2do-=tmp;
+ indexNow=points-points2do;
+
+ ptdet->counter += ptdet->counterFactor;
+
+ accum = pInput[indexNow-1]; // dude's major bug fix!
+
+ peakwas=ptdet->peak;
+
+ ptdet->z[ptdet->zIndex]+=
+ (((accum - ptdet->z[ptdet->zIndex])*binFactor)/M_Q15);
+
+ peak = abs(ptdet->z[0]-ptdet->z[2]) + abs(ptdet->z[1]-ptdet->z[3]);
+
+ if (ptdet->peak < peak)
+ ptdet->peak += ( ((peak-ptdet->peak)*binFactor)/M_Q15);
+ else
+ ptdet->peak=peak;
+
+ {
+ static const i16 a0=13723;
+ static const i16 a1=-13723;
+ i32 temp0,temp1;
+ i16 x0;
+
+ //differentiate
+ x0=ptdet->zd;
+ temp0 = x0 * a1;
+ ptdet->zd = ptdet->peak;
+ temp1 = ptdet->peak * a0;
+ diffpeak = (temp0 + temp1)/1024;
+ }
+
+ if(diffpeak<(-0.03*M_Q15))ptdet->dvd-=4;
+ else if(ptdet->dvd<0)ptdet->dvd++;
+
+ if((ptdet->dvd < -12) && diffpeak > (-0.02*M_Q15))ptdet->dvu+=2;
+ else if(ptdet->dvu)ptdet->dvu--;
+
+ tmp=ptdet->setpt;
+ if(pmrChan->rxCtcss->decode==tnum)
+ {
+ if(relax)tmp=(tmp*55)/100;
+ else tmp=(tmp*80)/100;
+ }
+
+ if(ptdet->peak > tmp)
+ {
+ if(ptdet->decode<(fudgeFactor*32))ptdet->decode++;
+ }
+ else if(pmrChan->rxCtcss->decode==tnum)
+ {
+ if(ptdet->peak > ptdet->hyst)ptdet->decode--;
+ else if(relax) ptdet->decode--;
+ else ptdet->decode-=4;
+ }
+ else
+ {
+ ptdet->decode=0;
+ }
+
+ if((pmrChan->rxCtcss->decode==tnum) && !relax && (ptdet->dvu > (0.00075*M_Q15)))
+ {
+ ptdet->decode=0;
+ ptdet->z[0]=ptdet->z[1]=ptdet->z[2]=ptdet->z[3]=ptdet->dvu=0;
+ //printf("ctcss_detect() turnoff code!\n");
+ }
+
+ if(ptdet->decode<0 || !pmrChan->rxCarrierDetect)ptdet->decode=0;
+
+ if(ptdet->decode>=fudgeFactor)thit=tnum;
+
+ #if XPMR_DEBUG0 == 1
+ //if(thit>=0 && thit==tnum)
+ // printf(" ctcss_detect() %i %i %i %i \n",tnum,ptdet->peak,ptdet->setpt,ptdet->hyst);
+
+ // tv0=accum;
+ tv0=ptdet->peak;
+ tv1=diffpeak;
+ tv2=ptdet->dvu;
+
+ //tv1=ptdet->zi*100;
+ while(indexDebug<indexNow)
+ {
+ if(indexDebug==0)lasttv0=ptdet->pDebug0[points-1];
+ if(ptdet->pDebug0)ptdet->pDebug0[indexDebug]=lasttv0;
+
+ if(indexDebug==0)lasttv1=ptdet->pDebug1[points-1];
+ if(ptdet->pDebug1)ptdet->pDebug1[indexDebug]=lasttv1;
+
+ if(indexDebug==0)lasttv2=ptdet->pDebug2[points-1];
+ if(ptdet->pDebug2)ptdet->pDebug2[indexDebug]=lasttv2;
+
+ indexDebug++;
+ }
+ lasttv0=tv0;
+ lasttv1=tv1;
+ lasttv2=tv2*100;
+ #endif
+ indexWas=indexNow;
+ ptdet->zIndex=(++ptdet->zIndex)%4;
+ }
+ ptdet->counter-=(points2do*CTCSS_SCOUNT_MUL);
+
+ #if XPMR_DEBUG0 == 1
+ for(i=indexWas;i<points;i++)
+ {
+ if(ptdet->pDebug0)ptdet->pDebug0[i]=lasttv0;
+ if(ptdet->pDebug1)ptdet->pDebug1[i]=lasttv1;
+ if(ptdet->pDebug2)ptdet->pDebug2[i]=lasttv2;
+ }
+ #endif
+ }
+
+ //TRACEX((" ctcss_detect() thit %i\n",thit));
+
+ if(pmrChan->rxCtcss->BlankingTimer>0)pmrChan->rxCtcss->BlankingTimer-=points;
+ if(pmrChan->rxCtcss->BlankingTimer<0)pmrChan->rxCtcss->BlankingTimer=0;
+
+ if(thit>=0 && pmrChan->rxCtcss->decode<0 && !pmrChan->rxCtcss->BlankingTimer)
+ {
+ pmrChan->rxCtcss->decode=thit;
+ }
+ else if(thit<0 && pmrChan->rxCtcss->decode>=0)
+ {
+ pmrChan->rxCtcss->BlankingTimer=SAMPLE_RATE_NETWORK/5;
+ pmrChan->rxCtcss->decode=-1;
+
+ for(tnum=0;tnum<CTCSS_NUM_CODES;tnum++)
+ {
+ t_tdet *ptdet=NULL;
+ ptdet=&(pmrChan->rxCtcss->tdet[tnum]);
+ ptdet->decode=0;
+ ptdet->z[0]=ptdet->z[1]=ptdet->z[2]=ptdet->z[3]=0;
+ }
+ }
+ //TRACEX((" ctcss_detect() thit %i %i\n",thit,pmrChan->rxCtcss->decode));
+ return(0);
+}
+/*
+ TxTestTone
+*/
+static i16 TxTestTone(t_pmr_chan *pChan, i16 function)
+{
+ if(function==1)
+ {
+ pChan->spsSigGen1->enabled=1;
+ pChan->spsSigGen1->option=1;
+ pChan->spsTx->source=pChan->spsSigGen1->sink;
+ }
+ else
+ {
+ pChan->spsSigGen1->option=3;
+ }
+ return 0;
+}
+/*
+ assumes:
+ sampling rate is 48KS/s
+ samples are all 16 bits
+ samples are filtered and decimated by 1/6th
+*/
+t_pmr_chan *createPmrChannel(t_pmr_chan *tChan, i16 numSamples)
+{
+ i16 i, *inputTmp;
+
+ t_pmr_chan *pChan;
+ t_pmr_sps *pSps;
+ t_dec_ctcss *pDecCtcss;
+ t_tdet *ptdet;
+
+ TRACEX(("createPmrChannel(%p,%i)\n",tChan,numSamples));
+
+ pChan = (t_pmr_chan *)calloc(sizeof(t_pmr_chan),1);
+
+ if(pChan==NULL)
+ {
+ printf("createPmrChannel() failed\n");
+ return(NULL);
+ }
+
+ pChan->nSamplesRx=numSamples;
+ pChan->nSamplesTx=numSamples;
+
+ pChan->index=pmrChanIndex++;
+
+ for(i=0;i<CTCSS_NUM_CODES;i++)
+ {
+ pChan->rxCtcssMap[i]=-1;
+ }
+
+ pChan->rxCtcssIndex=-1;
+
+ if(tChan==NULL)
+ {
+ pChan->rxNoiseSquelchEnable=0;
+ pChan->rxHpfEnable=0;
+ pChan->rxDeEmpEnable=0;
+ pChan->rxCenterSlicerEnable=0;
+ pChan->rxCtcssDecodeEnable=0;
+ pChan->rxDcsDecodeEnable=0;
+
+ pChan->rxCarrierPoint = 17000;
+ pChan->rxCarrierHyst = 2500;
+
+ pChan->rxCtcssFreq=103.5;
+
+ pChan->txHpfEnable=0;
+ pChan->txLimiterEnable=0;
+ pChan->txPreEmpEnable=0;
+ pChan->txLpfEnable=1;
+ pChan->txCtcssFreq=103.5;
+ pChan->txMixA=TX_OUT_VOICE;
+ pChan->txMixB=TX_OUT_LSD;
+ }
+ else
+ {
+ pChan->rxDemod=tChan->rxDemod;
+ pChan->rxCdType=tChan->rxCdType;
+ pChan->rxSquelchPoint = tChan->rxSquelchPoint;
+ pChan->rxCarrierHyst = 3000;
+ pChan->rxCtcssFreq=tChan->rxCtcssFreq;
+
+ for(i=0;i<CTCSS_NUM_CODES;i++)
+ pChan->rxCtcssMap[i]=tChan->rxCtcssMap[i];
+
+ pChan->txMod=tChan->txMod;
+ pChan->txHpfEnable=1;
+ pChan->txLpfEnable=1;
+ pChan->txCtcssFreq=tChan->txCtcssFreq;
+ pChan->txMixA=tChan->txMixA;
+ pChan->txMixB=tChan->txMixB;
+ pChan->radioDuplex=tChan->radioDuplex;
+ }
+
+ TRACEX(("misc settings \n"));
+
+ if(pChan->rxCdType==CD_XPMR_NOISE){
+ pChan->rxNoiseSquelchEnable=1;
+ }
+
+ if(pChan->rxDemod==RX_AUDIO_FLAT){
+ pChan->rxHpfEnable=1;
+ pChan->rxDeEmpEnable=1;
+ }
+
+ pChan->rxCarrierPoint=(pChan->rxSquelchPoint*32767)/100;
+ pChan->rxCarrierHyst = 3000; //pChan->rxCarrierPoint/15;
+
+ pChan->rxDcsDecodeEnable=0;
+
+ if(pChan->rxCtcssFreq!=0){
+ pChan->rxHpfEnable=1;
+ pChan->rxCenterSlicerEnable=1;
+ pChan->rxCtcssDecodeEnable=1;
+ pChan->rxCtcssIndex=CtcssFreqIndex(pChan->rxCtcssFreq);
+ }
+
+ if(pChan->txMod){
+ pChan->txPreEmpEnable=1;
+ pChan->txLimiterEnable=1;
+ }
+
+ TRACEX(("calloc buffers \n"));
+
+ pChan->pRxDemod = calloc(numSamples,2);
+ pChan->pRxNoise = calloc(numSamples,2);
+ pChan->pRxBase = calloc(numSamples,2);
+ pChan->pRxHpf = calloc(numSamples,2);
+ pChan->pRxLsd = calloc(numSamples,2);
+ pChan->pRxSpeaker = calloc(numSamples,2);
+ pChan->pRxCtcss = calloc(numSamples,2);
+ pChan->pRxDcTrack = calloc(numSamples,2);
+ pChan->pRxLsdLimit = calloc(numSamples,2);
+
+
+ pChan->pTxBase = calloc(numSamples,2);
+ pChan->pTxHpf = calloc(numSamples,2);
+ pChan->pTxPreEmp = calloc(numSamples,2);
+ pChan->pTxLimiter = calloc(numSamples,2);
+ pChan->pTxLsd = calloc(numSamples,2);
+ pChan->pTxLsdLpf = calloc(numSamples,2);
+ pChan->pTxComposite = calloc(numSamples,2);
+ pChan->pSigGen0 = calloc(numSamples,2);
+ pChan->pSigGen1 = calloc(numSamples,2);
+
+ pChan->pTxCode = calloc(numSamples,2);
+ pChan->pTxOut = calloc(numSamples,2*2*6); // output buffer
+
+ #if XPMR_DEBUG0 == 1
+ pChan->pTxPttIn = calloc(numSamples,2);
+ pChan->pTxPttOut = calloc(numSamples,2);
+ pChan->prxDebug0 = calloc(numSamples,2);
+ pChan->prxDebug1 = calloc(numSamples,2);
+ pChan->prxDebug2 = calloc(numSamples,2);
+ pChan->prxDebug3 = calloc(numSamples,2);
+ pChan->ptxDebug0 = calloc(numSamples,2);
+ pChan->ptxDebug1 = calloc(numSamples,2);
+ pChan->ptxDebug2 = calloc(numSamples,2);
+ pChan->ptxDebug3 = calloc(numSamples,2);
+ pChan->pNull = calloc(numSamples,2);
+ for(i=0;i<numSamples;i++)pChan->pNull[i]=((i%(numSamples/2))*8000)-4000;
+ #endif
+
+ TRACEX(("create ctcss\n"));
+
+ pDecCtcss = (t_dec_ctcss *)calloc(sizeof(t_dec_ctcss),1);
+
+ pChan->rxCtcss=pDecCtcss;
+ pDecCtcss->enabled=1;
+ pDecCtcss->gain=1*M_Q8;
+ pDecCtcss->limit=8192;
+ pDecCtcss->input=pChan->pRxLsdLimit;
+ pDecCtcss->testIndex=pChan->rxCtcssIndex;
+ if(!pDecCtcss->testIndex)pDecCtcss->testIndex=1;
+ pChan->rxCtcssMap[pChan->rxCtcssIndex]=pChan->rxCtcssIndex;
+ pDecCtcss->decode=-1;
+
+ for(i=0;i<CTCSS_NUM_CODES;i++)
+ {
+ ptdet=&(pChan->rxCtcss->tdet[i]);
+ ptdet->state=1;
+ ptdet->setpt=(M_Q15*0.067); // 0.069
+ ptdet->hyst =(M_Q15*0.020);
+ ptdet->counterFactor=coef_ctcss_div[i];
+ ptdet->binFactor=(M_Q15*0.135); // was 0.140
+ ptdet->fudgeFactor=8;
+ }
+
+ // General Purpose Function Generator
+ pSps=pChan->spsSigGen1=createPmrSps();
+ pSps->parentChan=pChan;
+ pSps->sink=pChan->pSigGen1;
+ pSps->numChanOut=1;
+ pSps->selChanOut=0;
+ pSps->sigProc=SigGen;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->sampleRate=SAMPLE_RATE_NETWORK;
+ pSps->freq=10000; // in increments of 0.1 Hz
+ pSps->outputGain=(.25*M_Q8);
+ pSps->option=0;
+ pSps->interpolate=1;
+ pSps->decimate=1;
+ pSps->enabled=0;
+
+
+ // CTCSS ENCODER
+ pSps = pChan->spsSigGen0 = createPmrSps();
+ pSps->parentChan=pChan;
+ pSps->sink=pChan->pTxLsd;
+ pSps->sigProc=SigGen;
+ pSps->numChanOut=1;
+ pSps->selChanOut=0;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->sampleRate=SAMPLE_RATE_NETWORK;
+ pSps->freq=pChan->txCtcssFreq*10; // in increments of 0.1 Hz
+ pSps->outputGain=(0.5*M_Q8);
+ pSps->option=0;
+ pSps->interpolate=1;
+ pSps->decimate=1;
+ pSps->enabled=0;
+
+
+ // Tx LSD Low Pass Filter
+ pSps=pChan->spsTxLsdLpf=pSps->nextSps=createPmrSps();
+ pSps->source=pChan->pTxLsd;
+ pSps->sink=pChan->pTxLsdLpf;
+ pSps->sigProc=pmr_gp_fir;
+ pSps->enabled=0;
+ pSps->numChanOut=1;
+ pSps->selChanOut=0;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->decimator=pSps->decimate=1;
+ pSps->interpolate=1;
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+
+ if(pChan->txCtcssFreq>203.0)
+ {
+ pSps->ncoef=taps_fir_lpf_250_9_66;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_lpf_250_9_66;
+ pSps->nx=taps_fir_lpf_250_9_66;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ pSps->calcAdjust=gain_fir_lpf_250_9_66;
+ }
+ else
+ {
+ pSps->ncoef=taps_fir_lpf_215_9_88;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_lpf_215_9_88;
+ pSps->nx=taps_fir_lpf_215_9_88;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ pSps->calcAdjust=gain_fir_lpf_215_9_88;
+ }
+
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+
+ if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+
+
+ // RX Process
+ TRACEX(("create rx\n"));
+ pSps = NULL;
+
+ // allocate space for first sps and set pointers
+ pSps=pChan->spsRx=createPmrSps();
+ pSps->parentChan=pChan;
+ pSps->source=NULL; //set when called
+ pSps->sink=pChan->pRxBase;
+ pSps->sigProc=pmr_rx_frontend;
+ pSps->enabled=1;
+ pSps->decimator=pSps->decimate=6;
+ pSps->interpolate=pSps->interpolate=1;
+ pSps->nSamples=pChan->nSamplesRx;
+ pSps->ncoef=taps_fir_bpf_noise_1;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_lpf_3K_1;
+ pSps->coef2=(void*)coef_fir_bpf_noise_1;
+ pSps->nx=taps_fir_bpf_noise_1;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_coef));
+ pSps->calcAdjust=(gain_fir_lpf_3K_1*256)/0x0100;
+ pSps->outputGain=(1.0*M_Q8);
+ pSps->discfactor=2;
+ pSps->hyst=pChan->rxCarrierHyst;
+ pSps->setpt=pChan->rxCarrierPoint;
+ pChan->prxSquelchAdjust=&pSps->setpt;
+ #if XPMR_DEBUG0 == 1
+ pSps->debugBuff0=pChan->pRxDemod;
+ pSps->debugBuff1=pChan->pRxNoise;
+ pSps->debugBuff2=pChan->prxDebug0;
+ #endif
+
+
+ // allocate space for next sps and set pointers
+ // Rx SubAudible Decoder Low Pass Filter
+ pSps=pSps->nextSps=createPmrSps();
+ pSps->parentChan=pChan;
+ pSps->source=pChan->pRxBase;
+ pSps->sink=pChan->pRxLsd;
+ pSps->sigProc=pmr_gp_fir;
+ pSps->enabled=1;
+ pSps->numChanOut=1;
+ pSps->selChanOut=0;
+ pSps->nSamples=pChan->nSamplesRx;
+ pSps->decimator=pSps->decimate=1;
+ pSps->interpolate=1;
+
+ if(pChan->rxCtcssFreq>203.5)
+ {
+ pSps->ncoef=taps_fir_lpf_250_9_66;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_lpf_250_9_66;
+ pSps->nx=taps_fir_lpf_250_9_66;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ pSps->calcAdjust=gain_fir_lpf_250_9_66;
+ }
+ else
+ {
+ pSps->ncoef=taps_fir_lpf_215_9_88;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_lpf_215_9_88;
+ pSps->nx=taps_fir_lpf_215_9_88;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ pSps->calcAdjust=gain_fir_lpf_215_9_88;
+ }
+
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+ pChan->prxCtcssMeasure=pSps->sink;
+ pChan->prxCtcssAdjust=&(pSps->outputGain);
+
+
+ // allocate space for next sps and set pointers
+ // CenterSlicer
+ if(pChan->rxCenterSlicerEnable)
+ {
+ pSps=pSps->nextSps=createPmrSps();
+ pSps->parentChan=pChan;
+ pSps->source=pChan->pRxLsd;
+ pSps->sink=pChan->pRxDcTrack;
+ pSps->buff=pChan->pRxLsdLimit;
+ pSps->sigProc=CenterSlicer;
+ pSps->enabled=1;
+ pSps->nSamples=pChan->nSamplesRx;
+ pSps->discfactor=800;
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+ pSps->setpt=3000;
+ pSps->inputGainB=1000; // limiter set point
+ }
+
+ // allocate space for next sps and set pointers
+ // Rx HPF
+ pSps=pSps->nextSps=createPmrSps();
+ pSps->parentChan=pChan;
+ pChan->spsRxHpf=pSps;
+ pSps->source=pChan->pRxBase;
+ pSps->sink=pChan->pRxHpf;
+ pSps->sigProc=pmr_gp_fir;
+ pSps->enabled=1;
+ pSps->numChanOut=1;
+ pSps->selChanOut=0;
+ pSps->nSamples=pChan->nSamplesRx;
+ pSps->decimator=pSps->decimate=1;
+ pSps->interpolate=1;
+ pSps->ncoef=taps_fir_hpf_300_9_66;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_hpf_300_9_66;
+ pSps->nx=taps_fir_hpf_300_9_66;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+ pSps->calcAdjust=gain_fir_hpf_300_9_66;
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+ pChan->spsRxOut=pSps;
+
+ // allocate space for next sps and set pointers
+ // Rx DeEmp
+ if(pChan->rxDeEmpEnable){
+ pSps=pSps->nextSps=createPmrSps();
+ pSps->parentChan=pChan;
+ pChan->spsRxDeEmp=pSps;
+ pSps->source=pChan->pRxHpf;
+ pSps->sink=pChan->pRxSpeaker;
+ pChan->spsRxOut=pSps; // OUTPUT STRUCTURE! maw
+ pSps->sigProc=gp_inte_00;
+ pSps->enabled=1;
+ pSps->nSamples=pChan->nSamplesRx;
+
+ pSps->ncoef=taps_int_lpf_300_1_2;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_int_lpf_300_1_2;
+
+ pSps->nx=taps_int_lpf_300_1_2;
+ pSps->size_x=4;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+ pSps->calcAdjust=gain_int_lpf_300_1_2/2;
+ pSps->inputGain=(1.0*M_Q8);
+ pSps->outputGain=(1.0*M_Q8);
+ pChan->prxVoiceMeasure=pSps->sink;
+ pChan->prxVoiceAdjust=&(pSps->outputGain);
+ }
+
+ if(pChan->rxDelayLineEnable)
+ {
+ TRACEX(("create delayline\n"));
+ pSps=pChan->spsDelayLine=pSps->nextSps=createPmrSps();
+ pSps->sigProc=DelayLine;
+ pSps->source=pChan->pRxSpeaker;
+ pSps->sink=pChan->pRxSpeaker;
+ pSps->enabled=0;
+ pSps->inputGain=1*M_Q8;
+ pSps->outputGain=1*M_Q8;
+ pSps->nSamples=pChan->nSamplesRx;
+ pSps->buffSize=4096;
+ pSps->buff=calloc(4096,2); // one second maximum
+ pSps->buffLead = (SAMPLE_RATE_NETWORK*0.100);
+ pSps->buffOutIndex=0;
+ }
+
+ if(pChan->rxCdType==CD_XPMR_VOX)
+ {
+ TRACEX(("create vox measureblock\n"));
+ pSps=pChan->spsRxVox=pSps->nextSps=createPmrSps();
+ pSps->sigProc=MeasureBlock;
+ pSps->parentChan=pChan;
+ pSps->source=pChan->pRxBase;
+ pSps->sink=pChan->prxDebug1;
+ pSps->inputGain=1*M_Q8;
+ pSps->outputGain=1*M_Q8;
+ pSps->nSamples=pChan->nSamplesRx;
+ pSps->discfactor=3;
+ pSps->setpt=(0.01*M_Q15);
+ pSps->hyst=(pSps->setpt/10);
+ pSps->enabled=1;
+ }
+
+ // tuning measure block
+ pSps=pChan->spsMeasure=pSps->nextSps=createPmrSps();
+ pSps->parentChan=pChan;
+ pSps->source=pChan->spsRx->sink;
+ pSps->sink=pChan->prxDebug2;
+ pSps->sigProc=MeasureBlock;
+ pSps->enabled=0;
+ pSps->nSamples=pChan->nSamplesRx;
+ pSps->discfactor=10;
+
+ pSps->nextSps=NULL; // last sps in chain RX
+
+
+ // CREATE TRANSMIT CHAIN
+ TRACEX((" create tx\n"));
+ inputTmp=NULL;
+ pSps = NULL;
+
+ // allocate space for first sps and set pointers
+
+ // Tx HPF SubAudible
+ if(pChan->txHpfEnable)
+ {
+ pSps=createPmrSps();
+ pChan->spsTx=pSps;
+ pSps->source=pChan->pTxBase;
+ pSps->sink=pChan->pTxHpf;
+ pSps->sigProc=pmr_gp_fir;
+ pSps->enabled=1;
+ pSps->numChanOut=1;
+ pSps->selChanOut=0;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->decimator=pSps->decimate=1;
+ pSps->interpolate=1;
+ pSps->ncoef=taps_fir_hpf_300_9_66;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_hpf_300_9_66;
+ pSps->nx=taps_fir_hpf_300_9_66;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+ pSps->calcAdjust=gain_fir_hpf_300_9_66;
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+ inputTmp=pChan->pTxHpf;
+ }
+
+ // Tx PreEmphasis
+ if(pChan->txPreEmpEnable)
+ {
+ if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+ else pSps=pSps->nextSps=createPmrSps();
+
+ pSps->parentChan=pChan;
+ pSps->source=inputTmp;
+ pSps->sink=pChan->pTxPreEmp;
+
+ pSps->sigProc=gp_diff;
+ pSps->enabled=1;
+ pSps->nSamples=pChan->nSamplesTx;
+
+ pSps->ncoef=taps_int_hpf_4000_1_2;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_int_hpf_4000_1_2;
+
+ pSps->nx=taps_int_hpf_4000_1_2;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+ pSps->outputGain=(1*M_Q8);
+ pSps->calcAdjust=gain_int_hpf_4000_1_2;
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+ inputTmp=pSps->sink;
+ }
+
+ // Tx Limiter
+ if(pChan->txLimiterEnable)
+ {
+ if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+ else pSps=pSps->nextSps=createPmrSps();
+ pSps->source=inputTmp;
+ pSps->sink=pChan->pTxLimiter;
+ pSps->sigProc=SoftLimiter;
+ pSps->enabled=1;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+ pSps->setpt=12000;
+ inputTmp=pSps->sink;
+ }
+
+ // Composite Mix of Voice and LSD
+ if((pChan->txMixA==TX_OUT_COMPOSITE)||(pChan->txMixB==TX_OUT_COMPOSITE))
+ {
+ if(pSps==NULL)
+ pSps=pChan->spsTx=createPmrSps();
+ else
+ pSps=pSps->nextSps=createPmrSps();
+ pSps->source=inputTmp;
+ pSps->sourceB=pChan->pTxLsdLpf; //asdf ??? !!! maw pTxLsdLpf
+ pSps->sink=pChan->pTxComposite;
+ pSps->sigProc=pmrMixer;
+ pSps->enabled=1;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->inputGain=2*M_Q8;
+ pSps->inputGainB=1*M_Q8/8;
+ pSps->outputGain=1*M_Q8;
+ pSps->setpt=0;
+ inputTmp=pSps->sink;
+ pChan->ptxCtcssAdjust=&pSps->inputGainB;
+ }
+
+ // Chan A Upsampler and Filter
+ if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+ else pSps=pSps->nextSps=createPmrSps();
+
+ pChan->spsTxOutA=pSps;
+ if(!pChan->spsTx)pChan->spsTx=pSps;
+ pSps->parentChan=pChan;
+
+ if(pChan->txMixA==TX_OUT_COMPOSITE)
+ {
+ pSps->source=pChan->pTxComposite;
+ }
+ else if(pChan->txMixA==TX_OUT_LSD)
+ {
+ pSps->source=pChan->pTxLsdLpf;
+ }
+ else if(pChan->txMixA==TX_OUT_VOICE)
+ {
+ pSps->source=pChan->pTxHpf;
+ }
+ else if (pChan->txMixA==TX_OUT_AUX)
+ {
+ pSps->source=inputTmp;
+ }
+ else
+ {
+ pSps->source=NULL;
+ }
+
+ pSps->sink=pChan->pTxOut;
+ pSps->sigProc=pmr_gp_fir;
+ pSps->enabled=1;
+ pSps->numChanOut=2;
+ pSps->selChanOut=0;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->interpolate=6;
+ pSps->ncoef=taps_fir_lpf_3K_1;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_lpf_3K_1;
+ pSps->nx=taps_fir_lpf_3K_1;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+ pSps->calcAdjust=gain_fir_lpf_3K_1;
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+ if(pChan->txMixA==pChan->txMixB)pSps->monoOut=1;
+ else pSps->monoOut=0;
+
+
+ // Chan B Upsampler and Filter
+ if((pChan->txMixA!=pChan->txMixB)&&(pChan->txMixB!=TX_OUT_OFF))
+ {
+ if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+ else pSps=pSps->nextSps=createPmrSps();
+
+ pChan->spsTxOutB=pSps;
+ pSps->parentChan=pChan;
+ if(pChan->txMixB==TX_OUT_COMPOSITE)
+ {
+ pSps->source=pChan->pTxComposite;
+ }
+ else if(pChan->txMixB==TX_OUT_LSD)
+ {
+ pSps->source=pChan->pTxLsdLpf;
+ // pChan->ptxCtcssAdjust=&pSps->inputGain;
+ }
+ else if(pChan->txMixB==TX_OUT_VOICE)
+ {
+ pSps->source=inputTmp;
+ }
+ else if(pChan->txMixB==TX_OUT_AUX)
+ {
+ pSps->source=pChan->pTxHpf;
+ }
+ else
+ {
+ pSps->source=NULL;
+ }
+
+ pSps->sink=pChan->pTxOut;
+ pSps->sigProc=pmr_gp_fir;
+ pSps->enabled=1;
+ pSps->numChanOut=2;
+ pSps->selChanOut=1;
+ pSps->mixOut=0;
+ pSps->nSamples=pChan->nSamplesTx;
+ pSps->interpolate=6;
+ pSps->ncoef=taps_fir_lpf_3K_1;
+ pSps->size_coef=2;
+ pSps->coef=(void*)coef_fir_lpf_3K_1;
+ pSps->nx=taps_fir_lpf_3K_1;
+ pSps->size_x=2;
+ pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+ if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+ pSps->calcAdjust=(gain_fir_lpf_3K_1);
+ pSps->inputGain=(1*M_Q8);
+ pSps->outputGain=(1*M_Q8);
+
+ }
+
+ pSps->nextSps=NULL;
+
+ #if XPMR_DEBUG0 == 1
+ {
+ t_tdet *ptdet;
+ TRACEX((" configure tracing\n"));
+
+ pChan->rxCtcss->pDebug0=calloc(numSamples,2);
+ pChan->rxCtcss->pDebug1=calloc(numSamples,2);
+ pChan->rxCtcss->pDebug2=calloc(numSamples,2);
+
+ for(i=0;i<CTCSS_NUM_CODES;i++){
+ ptdet=&(pChan->rxCtcss->tdet[i]);
+ ptdet->pDebug0=calloc(numSamples,2);
+ ptdet->pDebug1=calloc(numSamples,2);
+ ptdet->pDebug2=calloc(numSamples,2);
+ }
+
+ // buffer, 2 bytes per sample, and 16 channels
+ pChan->prxDebug=calloc(numSamples*16,2);
+ pChan->ptxDebug=calloc(numSamples*16,2);
+ }
+ #endif
+
+ TRACEX((" createPmrChannel() end\n"));
+
+ return pChan;
+}
+/*
+*/
+i16 destroyPmrChannel(t_pmr_chan *pChan)
+{
+ t_pmr_sps *pmr_sps, *tmp_sps;
+ i16 i;
+
+ TRACEX(("destroyPmrChannel()\n"));
+
+ free(pChan->pRxDemod);
+ free(pChan->pRxNoise);
+ free(pChan->pRxBase);
+ free(pChan->pRxHpf);
+ free(pChan->pRxLsd);
+ free(pChan->pRxSpeaker);
+ free(pChan->pRxDcTrack);
+ if(pChan->pRxLsdLimit)free(pChan->pRxLsdLimit);
+ free(pChan->pTxBase);
+ free(pChan->pTxHpf);
+ free(pChan->pTxPreEmp);
+ free(pChan->pTxLimiter);
+ free(pChan->pTxLsd);
+ free(pChan->pTxLsdLpf);
+ if(pChan->pTxComposite)free(pChan->pTxComposite);
+ free(pChan->pTxCode);
+ free(pChan->pTxOut);
+
+ if(pChan->pSigGen0)free(pChan->pSigGen0);
+ if(pChan->pSigGen1)free(pChan->pSigGen1);
+
+ #if XPMR_DEBUG0 == 1
+ free(pChan->pTxPttIn);
+ free(pChan->pTxPttOut);
+ if(pChan->prxDebug)free(pChan->prxDebug);
+ if(pChan->ptxDebug)free(pChan->ptxDebug);
+ free(pChan->rxCtcss->pDebug0);
+ free(pChan->rxCtcss->pDebug1);
+
+ free(pChan->prxDebug0);
+ free(pChan->prxDebug1);
+ free(pChan->prxDebug2);
+ free(pChan->prxDebug3);
+
+ free(pChan->ptxDebug0);
+ free(pChan->ptxDebug1);
+ free(pChan->ptxDebug2);
+ free(pChan->ptxDebug3);
+
+ for(i=0;i<CTCSS_NUM_CODES;i++)
+ {
+ free(pChan->rxCtcss->tdet[i].pDebug0);
+ free(pChan->rxCtcss->tdet[i].pDebug1);
+ free(pChan->rxCtcss->tdet[i].pDebug2);
+ }
+ #endif
+
+ free(pChan->pRxCtcss);
+
+ pmr_sps=pChan->spsRx;
+
+ while(pmr_sps)
+ {
+ tmp_sps = pmr_sps;
+ pmr_sps = tmp_sps->nextSps;
+ destroyPmrSps(tmp_sps);
+ }
+
+ free(pChan);
+
+ return 0;
+}
+/*
+*/
+t_pmr_sps *createPmrSps(void)
+{
+ t_pmr_sps *pSps;
+
+ TRACEX(("createPmrSps()\n"));
+
+ pSps = (t_pmr_sps *)calloc(sizeof(t_pmr_sps),1);
+
+ if(!pSps)printf("Error: createPmrSps()\n");
+
+ // pSps->x=calloc(pSps->nx,pSps->size_x);
+
+ return pSps;
+}
+/*
+*/
+i16 destroyPmrSps(t_pmr_sps *pSps)
+{
+ TRACEX(("destroyPmrSps(%i)\n",pSps->index));
+
+ if(pSps->x!=NULL)free(pSps->x);
+ free(pSps);
+ return 0;
+}
+/*
+ PmrRx does the whole buffer
+*/
+i16 PmrRx(t_pmr_chan *pChan, i16 *input, i16 *output)
+{
+ int i,ii;
+ t_pmr_sps *pmr_sps;
+
+ TRACEX(("PmrRx() %i\n",pChan->frameCountRx));
+
+ if(pChan==NULL){
+ printf("PmrRx() pChan == NULL\n");
+ return 1;
+ }
+
+ pChan->frameCountRx++;
+
+ pmr_sps=pChan->spsRx; // first sps
+ pmr_sps->source=input;
+
+ if(output!=NULL)pChan->spsRxOut->sink=output; //last sps
+
+ #if 0
+ if(pChan->inputBlanking>0)
+ {
+ pChan->inputBlanking-=pChan->nSamplesRx;
+ if(pChan->inputBlanking<0)pChan->inputBlanking=0;
+ for(i=0;i<pChan->nSamplesRx*6;i++)
+ input[i]=0;
+ }
+ #endif
+
+ // || (pChan->radioDuplex && (pChan->pttIn || pChan->pttOut)))
+ if(pChan->rxCpuSaver && !pChan->rxCarrierDetect)
+ {
+ if(pChan->spsRxHpf)pChan->spsRxHpf->enabled=0;
+ if(pChan->spsRxDeEmp)pChan->spsRxDeEmp->enabled=0;
+ }
+ else
+ {
+ if(pChan->spsRxHpf)pChan->spsRxHpf->enabled=1;
+ if(pChan->spsRxDeEmp)pChan->spsRxDeEmp->enabled=1;
+ }
+
+ i=0;
+ while(pmr_sps!=NULL && pmr_sps!=0)
+ {
+ TRACEX(("PmrRx() sps %i\n",i++));
+ pmr_sps->sigProc(pmr_sps);
+ pmr_sps = (t_pmr_sps *)(pmr_sps->nextSps);
+ //pmr_sps=NULL; // sph maw
+ }
+
+ #define XPMR_VOX_HANGTIME 2000
+
+ if(pChan->rxCdType==CD_XPMR_VOX)
+ {
+ if(pChan->spsRxVox->compOut)
+ {
+ pChan->rxVoxTimer=XPMR_VOX_HANGTIME; //VOX HangTime in ms
+ }
+ if(pChan->rxVoxTimer>0)
+ {
+ pChan->rxVoxTimer-=MS_PER_FRAME;
+ pChan->rxCarrierDetect=1;
+ }
+ else
+ {
+ pChan->rxVoxTimer=0;
+ pChan->rxCarrierDetect=0;
+ }
+ }
+ else
+ {
+ pChan->rxCarrierDetect=!pChan->spsRx->compOut;
+ }
+
+ if( !pChan->rxCpuSaver || pChan->rxCarrierDetect
+ || pChan->rxCtcss->decode!=-1) ctcss_detect(pChan);
+
+ #if XPMR_DEBUG0 == 1
+ // TRACEX(("Write file.\n"));
+ ii=0;
+ if(pChan->b.rxCapture)
+ {
+ for(i=0;i<pChan->nSamplesRx;i++)
+ {
+ pChan->prxDebug[ii++]=input[i*2*6]; // input data
+ pChan->prxDebug[ii++]=output[i]; // output data
+ pChan->prxDebug[ii++]=pChan->rxCarrierDetect*M_Q14; // carrier detect
+ if(pChan->rxCtcss)
+ pChan->prxDebug[ii++]=pChan->rxCtcss->decode*M_Q15/CTCSS_NUM_CODES; // decoded ctcss
+ else
+ pChan->prxDebug[ii++]=0;
+
+ pChan->prxDebug[ii++]=pChan->pRxNoise[i]; // rssi
+ pChan->prxDebug[ii++]=pChan->pRxBase[i]; // decimated, low pass filtered
+ pChan->prxDebug[ii++]=pChan->pRxHpf[i]; // output to network
+ pChan->prxDebug[ii++]=pChan->pRxSpeaker[i];
+
+ pChan->prxDebug[ii++]=pChan->pRxLsd[i]; // CTCSS Filtered
+ pChan->prxDebug[ii++]=pChan->pRxDcTrack[i]; // DC Restoration
+ pChan->prxDebug[ii++]=pChan->pRxLsdLimit[i]; // Amplitude Limited
+
+ //pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex+1].pDebug0[i]; // Upper Adjacent CTCSS Code
+ pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex].pDebug0[i]; // Primary CTCSS Code
+ pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex].pDebug1[i]; // dv/dt of decoder output
+ pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex].pDebug2[i];
+
+ //pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex-1].pDebug0[i]; // Lower Adjacent CTCSS Code
+
+ pChan->prxDebug[ii++]=pChan->prxDebug1[i]; // Measure Output for VOX
+ pChan->prxDebug[ii++]=pChan->prxDebug2[i]; // Measure Output for Tuning
+ }
+ }
+ #endif
+
+ return 0;
+}
+/*
+ PmrTx does the whole buffer
+*/
+i16 PmrTx(t_pmr_chan *pChan, i16 *input, i16 *output)
+{
+ int i, hit=0;
+ t_pmr_sps *pmr_sps;
+
+ pChan->frameCountTx++;
+
+ TRACEX(("PmrTx() %i\n",pChan->frameCountTx));
+
+ if(pChan==NULL){
+ printf("PmrTx() pChan == NULL\n");
+ return 1;
+ }
+
+ if(pChan->b.startSpecialTone)
+ {
+ pChan->b.startSpecialTone=0;
+ pChan->spsSigGen1->option=1;
+ pChan->spsSigGen1->enabled=1;
+ pChan->b.doingSpecialTone=1;
+ } else if(pChan->b.stopSpecialTone)
+ {
+ pChan->b.stopSpecialTone=0;
+ pChan->spsSigGen1->option=0;
+ pChan->b.doingSpecialTone=0;
+ pChan->spsSigGen1->enabled=0;
+ } else if(pChan->b.doingSpecialTone)
+ {
+ pChan->spsSigGen1->sink=output;
+ pChan->spsSigGen1->sigProc(pChan->spsSigGen1);
+ for(i=0;i<(pChan->nSamplesTx*2*6);i+=2)output[i+1]=output[i];
+ return 0;
+ }
+
+ // handle transmitter ptt input
+ hit=0;
+ if( pChan->txPttIn && pChan->txState==0)
+ {
+ pChan->txState = 2;
+ pChan->txPttOut=1;
+ pChan->spsSigGen0->freq=pChan->txCtcssFreq*10;
+ pChan->spsSigGen0->option=1;
+ pChan->spsSigGen0->enabled=1;
+ if(pChan->spsTxOutA)pChan->spsTxOutA->enabled=1;
+ if(pChan->spsTxOutB)pChan->spsTxOutB->enabled=1;
+ if(pChan->spsTxLsdLpf)pChan->spsTxLsdLpf->enabled=1;
+ TRACEX((" TxOn\n"));
+ }
+ else if(!pChan->txPttIn && pChan->txState==2)
+ {
+ if( pChan->txTocType==TOC_NONE || !pChan->txCtcssFreq )
+ {
+ hit=1;
+ TRACEX((" Tx Off Immediate.\n"));
+ }
+ else if(pChan->txCtcssFreq && pChan->txTocType==TOC_NOTONE)
+ {
+ pChan->txState=3;
+ pChan->txHangTime=TOC_NOTONE_TIME/MS_PER_FRAME;
+ pChan->spsSigGen0->option=3;
+ TRACEX((" Tx Turn Off No Tone Start.\n"));
+ }
+ else
+ {
+ pChan->txState=3;
+ pChan->txHangTime=0;
+ pChan->spsSigGen0->option=2;
+ TRACEX((" Tx Turn Off Phase Shift Start.\n"));
+ }
+ }
+ else if(pChan->txState==3)
+ {
+ if(pChan->txHangTime)
+ {
+ if(--pChan->txHangTime==0)hit=1;
+
+ }
+ else if(pChan->txHangTime<=0 && pChan->spsSigGen0->state==0)
+ {
+ hit=1;
+ TRACEX((" Tx Off TOC.\n"));
+ }
+ if(pChan->txPttIn)
+ {
+ TRACEX((" Tx Key During HangTime\n"));
+ if((pChan->txTocType==TOC_PHASE)||(pChan->txTocType==TOC_NONE))
+ {
+ pChan->txState = 2;
+ hit=0;
+ }
+ }
+ }
+
+ if( pChan->txCpuSaver && !hit && !pChan->txPttIn && !pChan->txPttOut && pChan->txState==0 ) return (1);
+
+ if(hit)
+ {
+ pChan->txPttOut=0;
+ pChan->txState=0;
+ if(pChan->spsTxLsdLpf)pChan->spsTxLsdLpf->option=3;
+ if(pChan->spsTxOutA)pChan->spsTxOutA->option=3;
+ if(pChan->spsTxOutB)pChan->spsTxOutB->option=3;
+ TRACEX((" Tx Off hit.\n"));
+ }
+
+ if(pChan->spsSigGen0)
+ {
+ pChan->spsSigGen0->sigProc(pChan->spsSigGen0);
+ pmr_sps=pChan->spsSigGen0->nextSps;
+ i=0;
+ while(pmr_sps!=NULL && pmr_sps!=0)
+ {
+ TRACEX((" PmrTx() subaudible sps %i\n",i++));
+ //printf(" CTCSS ENCODE %i %i\n",pChan->spsSigGen0->freq,pChan->spsSigGen0->outputGain);
+ pmr_sps->sigProc(pmr_sps);
+ pmr_sps = (t_pmr_sps *)(pmr_sps->nextSps);
+ }
+ }
+
+ if(pChan->spsSigGen1 && pChan->spsSigGen1->enabled)
+ {
+ pChan->spsSigGen1->sigProc(pChan->spsSigGen1);
+ }
+
+ // Do Voice
+ pmr_sps=pChan->spsTx;
+ if(!pChan->spsSigGen1->enabled)pmr_sps->source=input;
+ else input=pmr_sps->source;
+
+ if(output!=NULL)
+ {
+ if(pChan->spsTxOutA)pChan->spsTxOutA->sink=output;
+ if(pChan->spsTxOutB)pChan->spsTxOutB->sink=output;
+ }
+
+ i=0;
+ while(pmr_sps!=NULL && pmr_sps!=0)
+ {
+ TRACEX((" PmrTx() sps %i\n",i++));
+ pmr_sps->sigProc(pmr_sps);
+ pmr_sps = (t_pmr_sps *)(pmr_sps->nextSps);
+ }
+
+
+ if(pChan->txMixA==TX_OUT_OFF || !pChan->txPttOut){
+ for(i=0;i<pChan->nSamplesTx*2*6;i+=2)output[i]=0;
+ }
+
+ if(pChan->txMixB==TX_OUT_OFF || !pChan->txPttOut ){
+ for(i=0;i<pChan->nSamplesTx*2*6;i+=2)output[i+1]=0;
+ }
+
+ #if XPMR_DEBUG0 == 1
+ if(pChan->b.txCapture)
+ {
+ i16 ii=0;
+ for(i=0;i<pChan->nSamplesTx;i++)
+ {
+ pChan->ptxDebug[ii++]=input[i];
+ pChan->ptxDebug[ii++]=output[i*2*6];
+ pChan->ptxDebug[ii++]=output[(i*2*6)+1];
+ pChan->ptxDebug[ii++]=pChan->txPttIn*8192;
+
+ pChan->ptxDebug[ii++]=pChan->txPttOut*8192;
+ if(pChan->txHpfEnable)pChan->ptxDebug[ii++]=pChan->pTxHpf[i];
+ else pChan->ptxDebug[ii++]=0;
+ if(pChan->txPreEmpEnable)pChan->ptxDebug[ii++]=pChan->pTxPreEmp[i];
+ else pChan->ptxDebug[ii++]=0;
+ if(pChan->txLimiterEnable)pChan->ptxDebug[ii++]=pChan->pTxLimiter[i];
+ else pChan->ptxDebug[ii++]=0;
+
+ pChan->ptxDebug[ii++]=pChan->pTxLsd[i];
+ pChan->ptxDebug[ii++]=pChan->pTxLsdLpf[i];
+ pChan->ptxDebug[ii++]=pChan->pTxComposite[i];
+ pChan->ptxDebug[ii++]=pChan->pSigGen1[i];
+
+ #if 1
+ pChan->ptxDebug[ii++]=pChan->ptxDebug0[i];
+ pChan->ptxDebug[ii++]=pChan->ptxDebug1[i];
+ pChan->ptxDebug[ii++]=pChan->ptxDebug2[i];
+ pChan->ptxDebug[ii++]=pChan->ptxDebug3[i];
+ #else
+ pChan->ptxDebug[ii++]=0;
+ pChan->ptxDebug[ii++]=0;
+ pChan->ptxDebug[ii++]=0;
+ pChan->ptxDebug[ii++]=0;
+ #endif
+ }
+ }
+ #endif
+
+ return 0;
+}
+/* end of file */
diff --git a/trunk/channels/xpmr/xpmr.h b/trunk/channels/xpmr/xpmr.h
new file mode 100755
index 000000000..b39ce23d6
--- /dev/null
+++ b/trunk/channels/xpmr/xpmr.h
@@ -0,0 +1,543 @@
+/*
+ * xpmr.h - for Xelatec Private Mobile Radio Processes
+ *
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ *
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.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 Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+
+#ifndef XPMR_H
+#define XPMR_H 1
+
+#ifdef CHAN_USBRADIO
+#define XPMR_DEBUG0 1
+#define XPMR_TRACE 0
+#else
+#define XPMR_DEBUG0 1
+#define XPMR_TRACE 1
+#endif
+
+#if(XPMR_TRACE == 1)
+#define TRACEX(a) {printf a;}
+#define TRACEXL(a) {printf("%s @ %u : ",__FILE__ ,__LINE__); printf a; }
+#define TRACEXT(a) { struct timeval hack; gettimeofday(&hack,NULL); printf("%ld.",hack.tv_sec%100000); printf("%i : ",(int)hack.tv_usec); printf a; }
+#else
+#define TRACEX(a)
+#define TRACEXL(a)
+#define TRACEXT(a)
+#endif
+
+#define i8 int8_t
+#define u8 u_int8_t
+#define i16 int16_t
+#define u16 u_int16_t
+#define i32 int32_t
+#define u32 u_int32_t
+#define i64 int64_t
+#define u64 u_int64_t
+
+#define M_Q24 0x01000000 //
+#define M_Q23 0x00800000 //
+#define M_Q22 0x00400000 //
+#define M_Q21 0x00200000 //
+#define M_Q20 0x00100000 // 1048576
+#define M_Q19 0x00080000 // 524288
+#define M_Q18 0x00040000 // 262144
+#define M_Q17 0x00020000 // 131072
+#define M_Q16 0x00010000 // 65536
+#define M_Q15 0x00008000 // 32768
+#define M_Q14 0x00004000 // 16384
+#define M_Q13 0x00002000 // 8182
+#define M_Q12 0x00001000 // 4096
+#define M_Q11 0x00000800 // 2048
+#define M_Q10 0x00000400 // 1024
+#define M_Q9 0x00000200 // 512
+#define M_Q8 0x00000100 // 256
+#define M_Q7 0x00000080 // 128
+#define M_Q6 0x00000040 // 64
+#define M_Q5 0x00000020 // 32
+#define M_Q4 0x00000010 // 16
+#define M_Q3 0x00000008 // 16
+#define M_Q2 0x00000004 // 16
+#define M_Q1 0x00000002 // 16
+#define M_Q0 0x00000001 // 16
+
+#define RADIANS_PER_CYCLE (2*M_PI)
+
+#define SAMPLE_RATE_INPUT 48000
+#define SAMPLE_RATE_NETWORK 8000
+
+#define SAMPLES_PER_BLOCK 160
+#define MS_PER_FRAME 20
+
+#define CTCSS_NUM_CODES 38
+#define CTCSS_SCOUNT_MUL 100
+#define CTCSS_INTEGRATE 3932 // 32767*.120 // 120/1000 // 0.120
+#define CTCSS_INPUT_LIMIT 1000
+#define CTCSS_DETECT_POINT 1989
+#define CTCSS_HYSTERSIS 200
+
+#define CTCSS_TURN_OFF_TIME 160 // ms
+#define CTCSS_TURN_OFF_SHIFT 240 // degrees
+#define TOC_NOTONE_TIME 600 // ms
+
+#ifndef CHAN_USBRADIO
+enum {RX_AUDIO_NONE,RX_AUDIO_SPEAKER,RX_AUDIO_FLAT};
+enum {TX_AUDIO_NONE,TX_AUDIO_FLAT,TX_AUDIO_FILTERED,TX_AUDIO_PROC};
+enum {CD_IGNORE,CD_XPMR_NOISE,CD_XPMR_VOX,CD_HID,CD_HID_INVERT};
+enum {SD_IGNORE,SD_HID,SD_HID_INVERT,SD_XPMR}; // no,external,externalinvert,software
+enum {RX_KEY_CARRIER,RX_KEY_CARRIER_CODE};
+enum {TX_OUT_OFF,TX_OUT_VOICE,TX_OUT_LSD,TX_OUT_COMPOSITE,TX_OUT_AUX};
+enum {TOC_NONE,TOC_PHASE,TOC_NOTONE};
+#endif
+
+/*
+ one structure for each ctcss tone to decode
+*/
+typedef struct
+{
+ i16 counter; // counter to next sample
+ i16 counterFactor; // full divisor used to increment counter
+ i16 binFactor;
+ i16 fudgeFactor;
+ i16 peak; // peak amplitude now maw sph now
+ i16 enabled;
+ i16 state; // dead, running, error
+ i16 zIndex; // z bucket index
+ i16 z[4]; // maw sph today
+ i16 zi;
+ i16 dvu;
+ i16 dvd;
+ i16 zd;
+ i16 setpt;
+ i16 hyst;
+ i16 decode;
+ i16 diffpeak;
+ i16 debug; // value held from last pass
+ i16 *pDebug0; // pointer to debug output
+ i16 *pDebug1; // pointer to debug output
+ i16 *pDebug2; // pointer to debug output
+
+} t_tdet;
+
+typedef struct
+{
+ i16 enabled; // if 0 none, 0xFFFF all tones, or single tone
+ i16 *input;
+ i16 clamplitude;
+ i16 center;
+ i16 decode; // current ctcss decode index
+ i32 BlankingTimer;
+ u32 TurnOffTimer;
+ t_tdet tdet[CTCSS_NUM_CODES];
+ i16 gain;
+ i16 limit;
+ i16 *pDebug0;
+ i16 *pDebug1;
+ i16 *pDebug2;
+ i16 testIndex;
+ i16 multiFreq;
+ i8 relax;
+
+} t_dec_ctcss;
+
+typedef struct
+{
+ i16 enabled; // if 0 none, 0xFFFF all tones, or single tone
+ i16 clamplitude;
+ i16 center;
+ i16 decode; // current ctcss decode value
+ i32 BlankingTimer;
+ u32 TurnOffTimer;
+ i16 gain;
+ i16 limit;
+ i16 *pDebug0;
+ i16 *pDebug1;
+ i16 rxPolarity;
+} t_dec_dcs;
+
+/*
+ Low Speed Data decoding both polarities
+*/
+typedef struct
+{
+ i16 counter; // counter to next sample
+ i16 synced;
+ u32 syncCorr[2];
+ u32 data[2];
+ i16 state; // disabled, enabled,
+ i16 decode;
+ i16 debug;
+
+ i16 polarity;
+ u32 frameNum;
+
+ u16 area;
+ u16 chan;
+ u16 home;
+ u16 id;
+ u16 free;
+
+ u16 crc;
+ i16 rssi;
+
+} t_decLsd;
+
+
+/* general purpose pmr signal processing element */
+
+struct t_pmr_chan;
+
+typedef struct t_pmr_sps
+{
+ i16 index; // unique to each instance
+
+ i16 enabled; // enabled/disabled
+
+ struct t_pmr_chan *parentChan;
+
+ i16 *source; // source buffer
+ i16 *sourceB; // source buffer B
+ i16 *sink; // sink buffer
+
+ i16 numChanOut; // allows output direct to interleaved buffer
+ i16 selChanOut;
+
+ u32 ticks;
+
+ void *buff; // this structure's internal buffer
+
+ i16 *debugBuff0; // debug buffer
+ i16 *debugBuff1; // debug buffer
+ i16 *debugBuff2; // debug buffer
+ i16 *debugBuff3; // debug buffer
+
+ i16 nSamples; // number of samples in the buffer
+
+ u32 buffSize; // buffer maximum index
+ u32 buffInIndex; // index to current input point
+ u32 buffOutIndex; // index to current output point
+ u32 buffLead; // lead of input over output through cb
+
+ i16 decimate; // decimation or interpolation factor (could be put in coef's)
+ i16 interpolate;
+ i16 decimator; // like the state this must be saved between calls (could be put in x's)
+
+ u32 sampleRate; // in Hz for elements in this structure
+ u32 freq; // in 0.1 Hz
+
+ i16 measPeak; // do measure Peak
+ i16 amax; // buffer amplitude maximum
+ i16 amin; // buffer amplitude minimum
+ i16 apeak; // buffer amplitude peak value (peak to peak)/2
+ i16 setpt; // amplitude set point for amplitude comparator
+ i16 hyst; // hysterysis for amplitude comparator
+ i16 compOut; // amplitude comparator output
+
+ i32 discounteru; // amplitude detector integrator discharge counter upper
+ i32 discounterl; // amplitude detector integrator discharge counter lower
+ i32 discfactor; // amplitude detector integrator discharge factor
+
+ i16 err; // error condition
+ i16 option; // option / request zero
+ i16 state; // stopped, start, stopped assumes zero'd
+
+ i16 cleared; // output buffer cleared
+
+ i16 delay;
+ i16 decode;
+
+ i32 inputGain; // apply to input data ? in Q7.8 format
+ i32 inputGainB; // apply to input data ? in Q7.8 format
+ i32 outputGain; // apply to output data ? in Q7.8 format
+ i16 mixOut;
+ i16 monoOut;
+
+ i16 filterType; // iir, fir, 1, 2, 3, 4 ...
+
+ i16 (*sigProc)(struct t_pmr_sps *sps); // function to call
+
+ i32 calcAdjust; // final adjustment
+ i16 nx; // number of x history elements
+ i16 ncoef; // number of coefficients
+ i16 size_x; // size of each x history element
+ i16 size_coef; // size of each coefficient
+ void *x; // history registers
+ void *x2; // history registers, 2nd bank
+ void *coef; // coefficients
+ void *coef2; // coefficients 2
+
+ void *nextSps; // next Sps function
+
+} t_pmr_sps;
+
+/*
+ pmr channel
+*/
+typedef struct t_pmr_chan
+{
+ i16 index; // which one
+ i16 enabled; // enabled/disabled
+ i16 status; // ok, error, busy, idle, initializing
+
+ i16 nSamplesRx; // max frame size
+ i16 nSamplesTx;
+
+ i32 inputSampleRate; // in S/s 48000
+ i32 baseSampleRate; // in S/s 8000
+
+ i16 inputGain;
+ i16 inputOffset;
+
+ u32 frameCountRx; // number processed
+ u32 frameCountTx;
+
+ i32 txHangTime;
+ i32 txTurnOff;
+
+ i16 rxDC; // average DC value of input
+ i16 rxSqSet; // carrier squelch threshold
+ i16 rxSqHyst; // carrier squelch hysterysis
+ i16 rxRssi; // current Rssi level
+ i16 rxQuality; // signal quality metric
+ i16 rxCarrierDetect; // carrier detect
+ i16 rxCdType;
+ i16 rxExtCarrierDetect;
+ i32 inputBlanking; // Tx pulse eliminator
+
+ i16 rxDemod; // see enum
+ i16 txMod; //
+
+ i16 rxNoiseSquelchEnable;
+ i16 rxHpfEnable;
+ i16 rxDeEmpEnable;
+ i16 rxCenterSlicerEnable;
+ i16 rxCtcssDecodeEnable;
+ i16 rxDcsDecodeEnable;
+ i16 rxDelayLineEnable;
+
+ i16 txHpfEnable;
+ i16 txLimiterEnable;
+ i16 txPreEmpEnable;
+ i16 txLpfEnable;
+
+ char radioDuplex;
+
+ struct {
+ unsigned pmrNoiseSquelch:1;
+ unsigned rxHpf:1;
+ unsigned txHpf:1;
+ unsigned txLpf:1;
+ unsigned rxDeEmphasis:1;
+ unsigned txPreEmphasis:1;
+ unsigned startSpecialTone:1;
+ unsigned stopSpecialTone:1;
+ unsigned doingSpecialTone:1;
+ unsigned extCarrierDetect:1;
+ unsigned txCapture:1;
+ unsigned rxCapture:1;
+ }b;
+
+ i16 dummy;
+
+ i32 txScramFreq;
+ i32 rxScramFreq;
+
+ i16 gainVoice;
+ i16 gainSubAudible;
+
+ i16 txMixA; // Off, Ctcss, Voice, Composite
+ i16 txMixB; // Off, Ctcss, Voice, Composite
+
+ i16 rxMuting;
+
+ i16 rxCpuSaver;
+ i16 txCpuSaver;
+
+ i8 rxSqMode; // 0 open, 1 carrier, 2 coded
+
+ i8 cdMethod;
+
+ i16 rxSquelchPoint;
+
+ i16 rxCarrierPoint;
+ i16 rxCarrierHyst;
+
+ i16 rxCtcssMap[CTCSS_NUM_CODES];
+
+ i16 txCtcssTocShift;
+ i16 txCtcssTocTime;
+ i8 txTocType;
+
+ float txCtcssFreq;
+ float rxCtcssFreq;
+ float rxInputGain;
+
+ i16 rxCtcssIndex;
+
+ i16 txPttIn; // from external request
+ i16 txPttOut; // to radio hardware
+
+ i16 bandwidth; // wide/narrow
+ i16 txCompand; // type
+ i16 rxCompand; //
+
+ i16 txEqRight; // muted, flat, pre-emp limited filtered
+ i16 txEqLeft;
+
+ i16 txPotRight; //
+ i16 txPotLeft; //
+
+ i16 rxPotRight; //
+ i16 rxPotLeft; //
+
+ i16 function;
+
+ i16 txState; // off,settling,on,hangtime,turnoff
+
+ t_pmr_sps *spsMeasure; // measurement block
+
+ t_pmr_sps *spsRx; // 1st signal processing struct
+ t_pmr_sps *spsRxLsd;
+ t_pmr_sps *spsRxDeEmp;
+ t_pmr_sps *spsRxHpf;
+ t_pmr_sps *spsRxVox;
+ t_pmr_sps *spsDelayLine; // Last signal processing struct
+ t_pmr_sps *spsRxOut; // Last signal processing struct
+
+ t_pmr_sps *spsTx; // 1st signal processing struct
+
+ t_pmr_sps *spsTxLsdLpf;
+ t_pmr_sps *spsTxOutA; // Last signal processing struct
+
+ t_pmr_sps *spsTxOutB; // Last signal processing struct
+
+ t_pmr_sps *spsSigGen0; // ctcss
+ t_pmr_sps *spsSigGen1; // test and other tones
+
+ // tune tweaks
+
+ i32 rxVoxTimer; // Vox Hang Timer
+
+ i16 *prxSquelchAdjust;
+
+ // i16 *prxNoiseMeasure; // for autotune
+ // i32 *prxNoiseAdjust;
+
+ i16 *prxVoiceMeasure;
+ i32 *prxVoiceAdjust;
+
+ i16 *prxCtcssMeasure;
+ i32 *prxCtcssAdjust;
+
+ i16 *ptxVoiceAdjust; // from calling application
+ i32 *ptxCtcssAdjust; // from calling application
+
+ i32 *ptxLimiterAdjust; // from calling application
+
+ i16 *pRxDemod; // buffers
+ i16 *pRxBase; // decimated lpf input
+ i16 *pRxNoise;
+ i16 *pRxLsd; // subaudible only
+ i16 *pRxHpf; // subaudible removed
+ i16 *pRxDeEmp; // EIA Audio
+ i16 *pRxSpeaker; // EIA Audio
+ i16 *pRxDcTrack; // DC Restored LSD
+ i16 *pRxLsdLimit; // LSD Limited
+ i16 *pRxCtcss; //
+ i16 *pRxSquelch;
+
+ i16 *pTxBase; // input data
+ i16 *pTxHpf;
+ i16 *pTxPreEmp;
+ i16 *pTxLimiter;
+ i16 *pTxLsd;
+ i16 *pTxLsdLpf;
+ i16 *pTxComposite;
+ i16 *pTxMod; // upsampled, low pass filtered
+
+ i16 *pTxOut; //
+
+ i16 *pTxPttIn;
+ i16 *pTxPttOut;
+ i16 *pTxHang;
+ i16 *pTxCode;
+
+ i16 *pSigGen0;
+ i16 *pSigGen1;
+
+ i16 *pAlt0;
+ i16 *pAlt1;
+
+ i16 *pNull;
+
+ i16 *prxDebug; // consolidated debug buffer
+ i16 *ptxDebug; // consolidated debug buffer
+
+ i16 *prxDebug0;
+ i16 *prxDebug1;
+ i16 *prxDebug2;
+ i16 *prxDebug3;
+
+ i16 *ptxDebug0;
+ i16 *ptxDebug1;
+ i16 *ptxDebug2;
+ i16 *ptxDebug3;
+
+ t_dec_ctcss *rxCtcss;
+
+ i16 clamplitudeDcs;
+ i16 centerDcs;
+ u32 dcsBlankingTimer;
+ i16 dcsDecode; // current dcs decode value
+
+ i16 clamplitudeLsd;
+ i16 centerLsd;
+ t_decLsd decLsd[2]; // for both polarities
+
+} t_pmr_chan;
+
+static i16 TxTestTone(t_pmr_chan *pChan, i16 function);
+
+t_pmr_chan *createPmrChannel(t_pmr_chan *tChan, i16 numSamples);
+t_pmr_sps *createPmrSps(void);
+i16 destroyPmrChannel(t_pmr_chan *pChan);
+i16 destroyPmrSps(t_pmr_sps *pSps);
+i16 pmr_rx_frontend(t_pmr_sps *mySps);
+i16 pmr_gp_fir(t_pmr_sps *mySps);
+i16 pmr_gp_iir(t_pmr_sps *mySps);
+i16 gp_inte_00(t_pmr_sps *mySps);
+i16 gp_diff(t_pmr_sps *mySps);
+i16 CenterSlicer(t_pmr_sps *mySps);
+i16 ctcss_detect(t_pmr_chan *pmrChan);
+i16 SoftLimiter(t_pmr_sps *mySps);
+i16 SigGen(t_pmr_sps *mySps);
+i16 pmrMixer(t_pmr_sps *mySps);
+i16 DelayLine(t_pmr_sps *mySps);
+i16 PmrRx(t_pmr_chan *PmrChan, i16 *input, i16 *output);
+i16 PmrTx(t_pmr_chan *PmrChan, i16 *input, i16 *output);
+i16 CtcssFreqIndex(float freq);
+i16 MeasureBlock(t_pmr_sps *mySps);
+#endif /* ! XPMR_H */
+
+/* end of file */
+
+
+
diff --git a/trunk/channels/xpmr/xpmr_coef.h b/trunk/channels/xpmr/xpmr_coef.h
new file mode 100755
index 000000000..fda6762da
--- /dev/null
+++ b/trunk/channels/xpmr/xpmr_coef.h
@@ -0,0 +1,951 @@
+/*
+ * xpmr_coef.h - for Xelatec Private Mobile Radio Processes
+ *
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ *
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.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 Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+
+#ifndef XPMR_COEF_H
+#define XMPR_COEF_H 1
+
+// frequencies in 0.1 Hz
+const u32 dtmf_row[] =
+{
+ 6970, 7700, 8520, 9410
+};
+const u32 dtmf_col[] =
+{
+ 12090, 13360, 14770, 16330
+};
+
+const i16 coef_dcs_rx = 1488; // dcs rx data divisor for oversampling 8000/134.4
+const i16 coef_dcs_tx = 5952; // dcs tx data divisor
+
+const i16 coef_lsd_div = 672; // low speed data divisor
+const u32 coef_lsd_sync = 0x158; // 000101011000
+const u32 coef_lsd_sync_pattern[] = {0x0000000F, 0x0F0FF000};
+
+#define CTCSS_COEF_INT 120
+#define CTCSS_SAMPLE_RATE 8000
+#define TDIV(x) ((CTCSS_SAMPLE_RATE*1000/x)+5)/10
+
+i32 coef_ctcss[4][5]=
+{
+ // freq, divisor, integrator, filter
+ {770,TDIV(770),CTCSS_COEF_INT,0,0},
+ {1000,TDIV(1000),CTCSS_COEF_INT,0,0},
+ {1035,TDIV(1035),CTCSS_COEF_INT,0,0},
+ {0,0,0,0}
+};
+
+
+i16 coef_ctcss_div[]=
+{
+2985, // 00 067.0
+2782, // 01 071.9
+2688, // 02 074.4
+2597, // 03 077.0
+2509, // 04 079.7
+2424, // 05 082.5
+2342, // 06 085.4
+2260, // 07 088.5
+2186, // 08 091.5
+2110, // 09 094.8
+2053, // 10 097.4
+2000, // 11 100.0
+1932, // 12 103.5
+1866, // 13 107.2
+1803, // 14 110.9
+1742, // 15 114.8
+1684, // 16 118.8
+1626, // 17 123.0
+1571, // 18 127.3
+1517, // 19 131.8
+1465, // 20 136.5
+1415, // 21 141.3
+1368, // 22 146.2
+1321, // 23 151.4
+1276, // 24 156.7
+1233, // 25 162.2
+1191, // 26 167.9
+1151, // 27 173.8
+1112, // 28 179.9
+1074, // 29 186.2
+1037, // 30 192.8
+983, // 31 203.5
+949, // 32 210.7
+917, // 33 218.1
+886, // 34 225.7
+856, // 35 233.6
+827, // 36 241.8
+799 // 37 250.3
+};
+
+float freq_ctcss[]=
+{
+067.0, // 00
+071.9, // 01
+074.4, // 02
+077.0, // 03
+079.7, // 04
+082.5, // 05
+085.4, // 06
+088.5, // 07
+091.5, // 08
+094.8, // 09
+097.4, // 10
+100.0, // 11
+103.5, // 12
+107.2, // 13
+110.9, // 14
+114.8, // 15
+118.8, // 16
+123.0, // 17
+127.3, // 18
+131.8, // 19
+136.5, // 20
+141.3, // 21
+146.2, // 22
+151.4, // 23
+156.7, // 24
+162.2, // 25
+167.9, // 26
+173.8, // 27
+179.9, // 28
+186.2, // 29
+192.8, // 30
+203.5, // 31
+210.7 , // 32
+218.1 , // 33
+225.7 , // 34
+233.6 , // 35
+241.8 , // 36
+250.3 // 37
+};
+
+/*
+ noise squelch carrier detect filter
+*/
+static const int16_t taps_fir_bpf_noise_1 = 66;
+static const int32_t gain_fir_bpf_noise_1 = 65536;
+static const int16_t coef_fir_bpf_noise_1[] = {
+ 139,
+ -182,
+ -269,
+ -66,
+ 56,
+ 59,
+ 250,
+ 395,
+ -80,
+ -775,
+ -557,
+ 437,
+ 779,
+ 210,
+ -17,
+ 123,
+ -692,
+ -1664,
+ -256,
+ 2495,
+ 2237,
+ -1018,
+ -2133,
+ -478,
+ -1134,
+ -2711,
+ 2642,
+ 10453,
+ 4010,
+ -14385,
+ -16488,
+ 6954,
+ 23030,
+ 6954,
+ -16488,
+ -14385,
+ 4010,
+ 10453,
+ 2642,
+ -2711,
+ -1134,
+ -478,
+ -2133,
+ -1018,
+ 2237,
+ 2495,
+ -256,
+ -1664,
+ -692,
+ 123,
+ -17,
+ 210,
+ 779,
+ 437,
+ -557,
+ -775,
+ -80,
+ 395,
+ 250,
+ 59,
+ 56,
+ -66,
+ -269,
+ -182,
+ 139,
+ 257
+};
+/*
+ tbd
+*/
+static const int16_t taps_fir_lpf_3K_1 = 66;
+static const int32_t gain_fir_lpf_3K_1 = 131072;
+static const int16_t coef_fir_lpf_3K_1[] = {
+ 259,
+ 58,
+ -185,
+ -437,
+ -654,
+ -793,
+ -815,
+ -696,
+ -434,
+ -48,
+ 414,
+ 886,
+ 1284,
+ 1523,
+ 1529,
+ 1254,
+ 691,
+ -117,
+ -1078,
+ -2049,
+ -2854,
+ -3303,
+ -3220,
+ -2472,
+ -995,
+ 1187,
+ 3952,
+ 7086,
+ 10300,
+ 13270,
+ 15672,
+ 17236,
+ 17778,
+ 17236,
+ 15672,
+ 13270,
+ 10300,
+ 7086,
+ 3952,
+ 1187,
+ -995,
+ -2472,
+ -3220,
+ -3303,
+ -2854,
+ -2049,
+ -1078,
+ -117,
+ 691,
+ 1254,
+ 1529,
+ 1523,
+ 1284,
+ 886,
+ 414,
+ -48,
+ -434,
+ -696,
+ -815,
+ -793,
+ -654,
+ -437,
+ -185,
+ 58,
+ 259,
+ 393
+};
+
+/**************************************************************
+Filter type: Low Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.250000 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_lpf_250_11_64 = 64;
+static const int32_t gain_fir_lpf_250_11_64 = 262144;
+static const int16_t coef_fir_lpf_250_11_64[] =
+{
+ 366,
+ -3,
+ -418,
+ -865,
+ -1328,
+ -1788,
+ -2223,
+ -2609,
+ -2922,
+ -3138,
+ -3232,
+ -3181,
+ -2967,
+ -2573,
+ -1988,
+ -1206,
+ -228,
+ 937,
+ 2277,
+ 3767,
+ 5379,
+ 7077,
+ 8821,
+ 10564,
+ 12259,
+ 13855,
+ 15305,
+ 16563,
+ 17588,
+ 18346,
+ 18812,
+ 18968,
+ 18812,
+ 18346,
+ 17588,
+ 16563,
+ 15305,
+ 13855,
+ 12259,
+ 10564,
+ 8821,
+ 7077,
+ 5379,
+ 3767,
+ 2277,
+ 937,
+ -228,
+ -1206,
+ -1988,
+ -2573,
+ -2967,
+ -3181,
+ -3232,
+ -3138,
+ -2922,
+ -2609,
+ -2223,
+ -1788,
+ -1328,
+ -865,
+ -418,
+ -3,
+ 366,
+ 680
+};
+
+// de-emphasis integrator 300 Hz with 8KS/s
+// a0, b1
+static const int16_t taps_int_lpf_300_1_2 = 2;
+static const int32_t gain_int_lpf_300_1_2 = 8182;
+static const int16_t coef_int_lpf_300_1_2[]={
+6878,
+25889
+};
+
+// pre-emphasis differentiator 4000 Hz with 8KS/s
+// a0,a1,b0,
+static const int16_t taps_int_hpf_4000_1_2 = 2;
+static const int32_t gain_int_hpf_4000_1_2 = 16384;
+static const int16_t coef_int_hpf_4000_1_2[]={
+17610,
+-17610,
+2454
+};
+
+
+/*
+ ltr crc table
+ from http://www.radioreference.com/forums/showthread.php?t=24126
+*/
+
+static const u8 ltr_table[]=
+{
+0x38, // 00 Area 0111000
+0x1c, // 01 Channel 4 0011100
+0x0e, // 02 Channel 3 0001110
+0x46, // 03 Channel 2 1000110
+0x23, // 04 Channel 1 0100011
+0x51, // 05 Channel 0 1010001
+0x68, // 06 Home 4 1101000
+0x75, // 07 Home 3 1110101
+0x7a, // 08 Home 2 1111010
+0x3d, // 09 Home 1 0111101
+0x1f, // 10 Home 0 0011111
+0x4f, // 11 Group 7 1001111
+0x26, // 12 Group 6 0100110
+0x52, // 13 Group 5 1010010
+0x29, // 14 Group 4 0101001
+0x15, // 15 Group 3 0010101
+0x0d, // 16 Group 2 0001101
+0x45, // 17 Group 1 1000101
+0x62, // 18 Group 0 1100010
+0x31, // 19 Free 4 0110001
+0x19, // 20 Free 3 0011001
+0x0d, // 21 Free 2 0001101
+0x07, // 22 Free 1 0000111
+0x43 // 23 Free 0 1000011
+};
+
+static const i16 bitWeight[]=
+{
+0, // 0
+1, // 1
+1, // 2
+2, // 3
+1, // 4
+2, // 5
+2, // 6
+3, // 7
+1, // 8
+2, // 9
+2, // 10
+3, // 11
+2, // 12
+3, // 13
+3, // 14
+4, // 15
+1, // 16
+2, // 17
+2, // 18
+3, // 19
+2, // 20
+3, // 21
+3, // 22
+4, // 23
+2, // 24
+3, // 25
+3, // 26
+4, // 27
+3, // 28
+4, // 29
+4, // 30
+5, // 31
+1, // 32
+2, // 33
+2, // 34
+3, // 35
+2, // 36
+3, // 37
+3, // 38
+4, // 39
+2, // 40
+3, // 41
+3, // 42
+4, // 43
+3, // 44
+4, // 45
+4, // 46
+5, // 47
+2, // 48
+3, // 49
+3, // 50
+4, // 51
+3, // 52
+4, // 53
+4, // 54
+5, // 55
+3, // 56
+4, // 57
+4, // 58
+5, // 59
+4, // 60
+5, // 61
+5, // 62
+6, // 63
+1, // 64
+2, // 65
+2, // 66
+3, // 67
+2, // 68
+3, // 69
+3, // 70
+4, // 71
+2, // 72
+3, // 73
+3, // 74
+4, // 75
+3, // 76
+4, // 77
+4, // 78
+5, // 79
+2, // 80
+3, // 81
+3, // 82
+4, // 83
+3, // 84
+4, // 85
+4, // 86
+5, // 87
+3, // 88
+4, // 89
+4, // 90
+5, // 91
+4, // 92
+5, // 93
+5, // 94
+6, // 95
+2, // 96
+3, // 97
+3, // 98
+4, // 99
+3, // 100
+4, // 101
+4, // 102
+5, // 103
+3, // 104
+4, // 105
+4, // 106
+5, // 107
+4, // 108
+5, // 109
+5, // 110
+6, // 111
+3, // 112
+4, // 113
+4, // 114
+5, // 115
+4, // 116
+5, // 117
+5, // 118
+6, // 119
+4, // 120
+5, // 121
+5, // 122
+6, // 123
+5, // 124
+6, // 125
+6, // 126
+7, // 127
+1, // 128
+2, // 129
+2, // 130
+3, // 131
+2, // 132
+3, // 133
+3, // 134
+4, // 135
+2, // 136
+3, // 137
+3, // 138
+4, // 139
+3, // 140
+4, // 141
+4, // 142
+5, // 143
+2, // 144
+3, // 145
+3, // 146
+4, // 147
+3, // 148
+4, // 149
+4, // 150
+5, // 151
+3, // 152
+4, // 153
+4, // 154
+5, // 155
+4, // 156
+5, // 157
+5, // 158
+6, // 159
+2, // 160
+3, // 161
+3, // 162
+4, // 163
+3, // 164
+4, // 165
+4, // 166
+5, // 167
+3, // 168
+4, // 169
+4, // 170
+5, // 171
+4, // 172
+5, // 173
+5, // 174
+6, // 175
+3, // 176
+4, // 177
+4, // 178
+5, // 179
+4, // 180
+5, // 181
+5, // 182
+6, // 183
+4, // 184
+5, // 185
+5, // 186
+6, // 187
+5, // 188
+6, // 189
+6, // 190
+7, // 191
+2, // 192
+3, // 193
+3, // 194
+4, // 195
+3, // 196
+4, // 197
+4, // 198
+5, // 199
+3, // 200
+4, // 201
+4, // 202
+5, // 203
+4, // 204
+5, // 205
+5, // 206
+6, // 207
+3, // 208
+4, // 209
+4, // 210
+5, // 211
+4, // 212
+5, // 213
+5, // 214
+6, // 215
+4, // 216
+5, // 217
+5, // 218
+6, // 219
+5, // 220
+6, // 221
+6, // 222
+7, // 223
+3, // 224
+4, // 225
+4, // 226
+5, // 227
+4, // 228
+5, // 229
+5, // 230
+6, // 231
+4, // 232
+5, // 233
+5, // 234
+6, // 235
+5, // 236
+6, // 237
+6, // 238
+7, // 239
+4, // 240
+5, // 241
+5, // 242
+6, // 243
+5, // 244
+6, // 245
+6, // 246
+7, // 247
+5, // 248
+6, // 249
+6, // 250
+7, // 251
+6, // 252
+7, // 253
+7, // 254
+8 // 255
+};
+
+
+/*
+ ctcss decode filter
+*/
+/**************************************************************
+Filter type: Low Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.250000 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_lpf_250_9_66 = 66;
+static const int32_t gain_fir_lpf_250_9_66 = 262144;
+static const int16_t coef_fir_lpf_250_9_66[] =
+{
+ 676,
+ 364,
+ -3,
+ -415,
+ -860,
+-1320,
+-1777,
+-2209,
+-2593,
+-2904,
+-3119,
+-3212,
+-3162,
+-2949,
+-2557,
+-1975,
+-1198,
+ -226,
+ 932,
+ 2263,
+ 3744,
+ 5346,
+ 7034,
+ 8767,
+10499,
+12184,
+13770,
+15211,
+16462,
+17480,
+18234,
+18696,
+18852,
+18696,
+18234,
+17480,
+16462,
+15211,
+13770,
+12184,
+10499,
+ 8767,
+ 7034,
+ 5346,
+ 3744,
+ 2263,
+ 932,
+ -226,
+-1198,
+-1975,
+-2557,
+-2949,
+-3162,
+-3212,
+-3119,
+-2904,
+-2593,
+-2209,
+-1777,
+-1320,
+ -860,
+ -415,
+ -3,
+ 364,
+ 676,
+ 927
+};
+/* *************************************************************
+Filter type: Low Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.215 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_lpf_215_9_88 = 88;
+static const int32_t gain_fir_lpf_215_9_88 = 524288;
+static const int16_t coef_fir_lpf_215_9_88[] = {
+ 2038,
+ 2049,
+ 1991,
+ 1859,
+ 1650,
+ 1363,
+ 999,
+ 562,
+ 58,
+ -502,
+-1106,
+-1739,
+-2382,
+-3014,
+-3612,
+-4153,
+-4610,
+-4959,
+-5172,
+-5226,
+-5098,
+-4769,
+-4222,
+-3444,
+-2430,
+-1176,
+ 310,
+ 2021,
+ 3937,
+ 6035,
+ 8284,
+10648,
+13086,
+15550,
+17993,
+20363,
+22608,
+24677,
+26522,
+28099,
+29369,
+30299,
+30867,
+31058,
+30867,
+30299,
+29369,
+28099,
+26522,
+24677,
+22608,
+20363,
+17993,
+15550,
+13086,
+10648,
+ 8284,
+ 6035,
+ 3937,
+ 2021,
+ 310,
+-1176,
+-2430,
+-3444,
+-4222,
+-4769,
+-5098,
+-5226,
+-5172,
+-4959,
+-4610,
+-4153,
+-3612,
+-3014,
+-2382,
+-1739,
+-1106,
+ -502,
+ 58,
+ 562,
+ 999,
+ 1363,
+ 1650,
+ 1859,
+ 1991,
+ 2049,
+ 2038,
+ 1966
+};
+// end coef fir_lpf_215_9_88
+//
+/**************************************************************
+Filter type: High Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.300000 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_hpf_300_9_66 = 66;
+static const int32_t gain_fir_hpf_300_9_66 = 32768;
+static const int16_t coef_fir_hpf_300_9_66[] =
+{
+ -141,
+ -114,
+ -77,
+ -30,
+ 23,
+ 83,
+ 147,
+ 210,
+ 271,
+ 324,
+ 367,
+ 396,
+ 407,
+ 396,
+ 362,
+ 302,
+ 216,
+ 102,
+ -36,
+ -199,
+ -383,
+ -585,
+ -798,
+-1017,
+-1237,
+-1452,
+-1653,
+-1836,
+-1995,
+-2124,
+-2219,
+-2278,
+30463,
+-2278,
+-2219,
+-2124,
+-1995,
+-1836,
+-1653,
+-1452,
+-1237,
+-1017,
+ -798,
+ -585,
+ -383,
+ -199,
+ -36,
+ 102,
+ 216,
+ 302,
+ 362,
+ 396,
+ 407,
+ 396,
+ 367,
+ 324,
+ 271,
+ 210,
+ 147,
+ 83,
+ 23,
+ -30,
+ -77,
+ -114,
+ -141,
+ -158
+ };
+#endif /* !XPMR_COEF_H */
+/* end of file */
+
+
+
+
diff --git a/trunk/codecs/Makefile b/trunk/codecs/Makefile
new file mode 100644
index 000000000..b7647b2e5
--- /dev/null
+++ b/trunk/codecs/Makefile
@@ -0,0 +1,58 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for codec modules
+#
+# 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
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=codec
+MENUSELECT_CATEGORY=CODECS
+MENUSELECT_DESCRIPTION=Codec Translators
+
+LIBILBC:=ilbc/libilbc.a
+LIBLPC10:=lpc10/liblpc10.a
+LIBG722:=g722/libg722.a
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+ifneq ($(GSM_INTERNAL),no)
+GSM_INCLUDE:=-Igsm/inc
+$(if $(filter codec_gsm,$(EMBEDDED_MODS)),modules.link,codec_gsm.so): gsm/lib/libgsm.a
+endif
+
+clean::
+ $(MAKE) -C gsm clean
+ $(MAKE) -C lpc10 clean
+ $(MAKE) -C ilbc clean
+ $(MAKE) -C g722 clean
+
+gsm/lib/libgsm.a:
+ @mkdir -p gsm/lib
+ @$(MAKE) -C gsm lib/libgsm.a
+
+$(LIBLPC10):
+ @$(MAKE) -C lpc10 all
+
+$(if $(filter codec_lpc10,$(EMBEDDED_MODS)),modules.link,codec_lpc10.so): $(LIBLPC10)
+
+$(LIBILBC):
+ @$(MAKE) -C ilbc all
+
+$(if $(filter codec_ilbc,$(EMBEDDED_MODS)),modules.link,codec_ilbc.so): $(LIBILBC)
+
+FORCE:
+
+$(LIBG722): FORCE
+ @$(MAKE) -C g722 all
+
+$(if $(filter codec_g722,$(EMBEDDED_MODS)),modules.link,codec_g722.so): $(LIBG722)
diff --git a/trunk/codecs/adpcm_slin_ex.h b/trunk/codecs/adpcm_slin_ex.h
new file mode 100644
index 000000000..c3f86c72d
--- /dev/null
+++ b/trunk/codecs/adpcm_slin_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * adpcm_slin_ex.h --
+ *
+ * \brief 4-bit ADPCM data, 20 milliseconds worth at 8 kHz.
+ *
+ * Source: g723.example
+ *
+ * Copyright (C) 2001-2005, Digium, Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char adpcm_slin_ex[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/trunk/codecs/codec_a_mu.c b/trunk/codecs/codec_a_mu.c
new file mode 100644
index 000000000..a478dc7af
--- /dev/null
+++ b/trunk/codecs/codec_a_mu.c
@@ -0,0 +1,159 @@
+/*
+ * 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 codec_a_mu.c - translate between alaw and ulaw directly
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/alaw.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/utils.h"
+
+#define BUFFER_SAMPLES 8000 /* size for the translation buffers */
+
+static unsigned char mu2a[256];
+static unsigned char a2mu[256];
+
+/* Sample frame data (Mu data is okay) */
+
+#include "ulaw_slin_ex.h"
+
+/*! \brief convert frame data and store into the buffer */
+static int alawtoulaw_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int x = f->samples;
+ unsigned char *src = f->data;
+ unsigned char *dst = (unsigned char *)pvt->outbuf + pvt->samples;
+
+ pvt->samples += x;
+ pvt->datalen += x;
+
+ while (x--)
+ *dst++ = a2mu[*src++];
+
+ return 0;
+}
+
+/*! \brief convert frame data and store into the buffer */
+static int ulawtoalaw_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int x = f->samples;
+ unsigned char *src = f->data;
+ unsigned char *dst = (unsigned char *)pvt->outbuf + pvt->samples;
+
+ pvt->samples += x;
+ pvt->datalen += x;
+
+ while (x--)
+ *dst++ = mu2a[*src++];
+
+ return 0;
+}
+
+/*
+ * alawToLin_Sample. Just random data, somehow...
+ */
+static struct ast_frame *alawtoulaw_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_ALAW;
+ f.datalen = sizeof(ulaw_slin_ex);
+ f.samples = sizeof(ulaw_slin_ex);
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = ulaw_slin_ex; /* XXX what ? */
+ return &f;
+}
+
+static struct ast_frame *ulawtoalaw_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_ULAW;
+ f.datalen = sizeof(ulaw_slin_ex);
+ f.samples = sizeof(ulaw_slin_ex);
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = ulaw_slin_ex;
+ return &f;
+}
+
+static struct ast_translator alawtoulaw = {
+ .name = "alawtoulaw",
+ .srcfmt = AST_FORMAT_ALAW,
+ .dstfmt = AST_FORMAT_ULAW,
+ .framein = alawtoulaw_framein,
+ .sample = alawtoulaw_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES,
+};
+
+static struct ast_translator ulawtoalaw = {
+ .name = "ulawtoalaw",
+ .srcfmt = AST_FORMAT_ULAW,
+ .dstfmt = AST_FORMAT_ALAW,
+ .framein = ulawtoalaw_framein,
+ .sample = ulawtoalaw_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES,
+};
+
+/*! \brief standard module glue */
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&ulawtoalaw);
+ res |= ast_unregister_translator(&alawtoulaw);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+ int x;
+
+ for (x=0;x<256;x++) {
+ mu2a[x] = AST_LIN2A(AST_MULAW(x));
+ a2mu[x] = AST_LIN2MU(AST_ALAW(x));
+ }
+ res = ast_register_translator(&alawtoulaw);
+ if (!res)
+ res = ast_register_translator(&ulawtoalaw);
+ else
+ ast_unregister_translator(&alawtoulaw);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "A-law and Mulaw direct Coder/Decoder");
diff --git a/trunk/codecs/codec_adpcm.c b/trunk/codecs/codec_adpcm.c
new file mode 100644
index 000000000..63a1ab1bc
--- /dev/null
+++ b/trunk/codecs/codec_adpcm.c
@@ -0,0 +1,400 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Based on frompcm.c and topcm.c from the Emiliano MIPL browser/
+ * interpreter. See http://www.bsdtelephony.com.mx
+ *
+ * Copyright (c) 2001 - 2005 Digium, Inc.
+ * All rights reserved.
+ *
+ * Karl Sackett <krs@linux-support.net>, 2001-03-21
+ *
+ * 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 codec_adpcm.c - translate between signed linear and Dialogic ADPCM
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+
+/* define NOT_BLI to use a faster but not bit-level identical version */
+/* #define NOT_BLI */
+
+#define BUFFER_SAMPLES 8096 /* size for the translation buffers */
+
+/* Sample frame data */
+
+#include "slin_adpcm_ex.h"
+#include "adpcm_slin_ex.h"
+
+/*
+ * Step size index shift table
+ */
+
+static int indsft[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/*
+ * Step size table, where stpsz[i]=floor[16*(11/10)^i]
+ */
+
+static int stpsz[49] = {
+ 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73,
+ 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279,
+ 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
+ 1060, 1166, 1282, 1411, 1552
+};
+
+/*
+ * Decoder/Encoder state
+ * States for both encoder and decoder are synchronized
+ */
+struct adpcm_state {
+ int ssindex;
+ int signal;
+ int zero_count;
+ int next_flag;
+};
+
+/*
+ * Decode(encoded)
+ * Decodes the encoded nibble from the adpcm file.
+ *
+ * Results:
+ * Returns the encoded difference.
+ *
+ * Side effects:
+ * Sets the index to the step size table for the next encode.
+ */
+
+static inline short decode(int encoded, struct adpcm_state *state)
+{
+ int diff;
+ int step;
+ int sign;
+
+ step = stpsz[state->ssindex];
+
+ sign = encoded & 0x08;
+ encoded &= 0x07;
+#ifdef NOT_BLI
+ diff = (((encoded << 1) + 1) * step) >> 3;
+#else /* BLI code */
+ diff = step >> 3;
+ if (encoded & 4)
+ diff += step;
+ if (encoded & 2)
+ diff += step >> 1;
+ if (encoded & 1)
+ diff += step >> 2;
+ if ((encoded >> 1) & step & 0x1)
+ diff++;
+#endif
+ if (sign)
+ diff = -diff;
+
+ if (state->next_flag & 0x1)
+ state->signal -= 8;
+ else if (state->next_flag & 0x2)
+ state->signal += 8;
+
+ state->signal += diff;
+
+ if (state->signal > 2047)
+ state->signal = 2047;
+ else if (state->signal < -2047)
+ state->signal = -2047;
+
+ state->next_flag = 0;
+
+#ifdef AUTO_RETURN
+ if (encoded)
+ state->zero_count = 0;
+ else if (++(state->zero_count) == 24) {
+ state->zero_count = 0;
+ if (state->signal > 0)
+ state->next_flag = 0x1;
+ else if (state->signal < 0)
+ state->next_flag = 0x2;
+ }
+#endif
+
+ state->ssindex += indsft[encoded];
+ if (state->ssindex < 0)
+ state->ssindex = 0;
+ else if (state->ssindex > 48)
+ state->ssindex = 48;
+
+ return state->signal << 4;
+}
+
+/*
+ * Adpcm
+ * Takes a signed linear signal and encodes it as ADPCM
+ * For more information see http://support.dialogic.com/appnotes/adpcm.pdf
+ *
+ * Results:
+ * Foo.
+ *
+ * Side effects:
+ * signal gets updated with each pass.
+ */
+
+static inline int adpcm(short csig, struct adpcm_state *state)
+{
+ int diff;
+ int step;
+ int encoded;
+
+ /*
+ * Clip csig if too large or too small
+ */
+ csig >>= 4;
+
+ step = stpsz[state->ssindex];
+ diff = csig - state->signal;
+
+#ifdef NOT_BLI
+ if (diff < 0) {
+ encoded = (-diff << 2) / step;
+ if (encoded > 7)
+ encoded = 7;
+ encoded |= 0x08;
+ } else {
+ encoded = (diff << 2) / step;
+ if (encoded > 7)
+ encoded = 7;
+ }
+#else /* BLI code */
+ if (diff < 0) {
+ encoded = 8;
+ diff = -diff;
+ } else
+ encoded = 0;
+ if (diff >= step) {
+ encoded |= 4;
+ diff -= step;
+ }
+ step >>= 1;
+ if (diff >= step) {
+ encoded |= 2;
+ diff -= step;
+ }
+ step >>= 1;
+ if (diff >= step)
+ encoded |= 1;
+#endif /* NOT_BLI */
+
+ /* feedback to state */
+ decode(encoded, state);
+
+ return encoded;
+}
+
+/*----------------- Asterisk-codec glue ------------*/
+
+/*! \brief Workspace for translating signed linear signals to ADPCM. */
+struct adpcm_encoder_pvt {
+ struct adpcm_state state;
+ int16_t inbuf[BUFFER_SAMPLES]; /* Unencoded signed linear values */
+};
+
+/*! \brief Workspace for translating ADPCM signals to signed linear. */
+struct adpcm_decoder_pvt {
+ struct adpcm_state state;
+};
+
+/*! \brief decode 4-bit adpcm frame data and store in output buffer */
+static int adpcmtolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct adpcm_decoder_pvt *tmp = pvt->pvt;
+ int x = f->datalen;
+ unsigned char *src = f->data;
+ int16_t *dst = (int16_t *)pvt->outbuf + pvt->samples;
+
+ while (x--) {
+ *dst++ = decode((*src >> 4) & 0xf, &tmp->state);
+ *dst++ = decode(*src++ & 0x0f, &tmp->state);
+ }
+ pvt->samples += f->samples;
+ pvt->datalen += 2*f->samples;
+ return 0;
+}
+
+/*! \brief fill input buffer with 16-bit signed linear PCM values. */
+static int lintoadpcm_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct adpcm_encoder_pvt *tmp = pvt->pvt;
+
+ memcpy(&tmp->inbuf[pvt->samples], f->data, f->datalen);
+ pvt->samples += f->samples;
+ return 0;
+}
+
+/*! \brief convert inbuf and store into frame */
+static struct ast_frame *lintoadpcm_frameout(struct ast_trans_pvt *pvt)
+{
+ struct adpcm_encoder_pvt *tmp = pvt->pvt;
+ struct ast_frame *f;
+ int i;
+ int samples = pvt->samples; /* save original number */
+
+ if (samples < 2)
+ return NULL;
+
+ pvt->samples &= ~1; /* atomic size is 2 samples */
+
+ for (i = 0; i < pvt->samples; i += 2) {
+ pvt->outbuf[i/2] =
+ (adpcm(tmp->inbuf[i ], &tmp->state) << 4) |
+ (adpcm(tmp->inbuf[i+1], &tmp->state) );
+ };
+
+ f = ast_trans_frameout(pvt, pvt->samples/2, 0);
+
+ /*
+ * If there is a left over sample, move it to the beginning
+ * of the input buffer.
+ */
+
+ if (samples & 1) { /* move the leftover sample at beginning */
+ tmp->inbuf[0] = tmp->inbuf[samples - 1];
+ pvt->samples = 1;
+ }
+ return f;
+}
+
+
+/*! \brief AdpcmToLin_Sample */
+static struct ast_frame *adpcmtolin_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_ADPCM;
+ f.datalen = sizeof(adpcm_slin_ex);
+ f.samples = sizeof(adpcm_slin_ex) * 2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = adpcm_slin_ex;
+ return &f;
+}
+
+/*! \brief LinToAdpcm_Sample */
+static struct ast_frame *lintoadpcm_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_adpcm_ex);
+ /* Assume 8000 Hz */
+ f.samples = sizeof(slin_adpcm_ex) / 2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_adpcm_ex;
+ return &f;
+}
+
+static struct ast_translator adpcmtolin = {
+ .name = "adpcmtolin",
+ .srcfmt = AST_FORMAT_ADPCM,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .framein = adpcmtolin_framein,
+ .sample = adpcmtolin_sample,
+ .desc_size = sizeof(struct adpcm_decoder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .plc_samples = 160,
+};
+
+static struct ast_translator lintoadpcm = {
+ .name = "lintoadpcm",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_ADPCM,
+ .framein = lintoadpcm_framein,
+ .frameout = lintoadpcm_frameout,
+ .sample = lintoadpcm_sample,
+ .desc_size = sizeof (struct adpcm_encoder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES/ 2, /* 2 samples per byte */
+};
+
+static int parse_config(int reload)
+{
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+ struct ast_variable *var;
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ for (var = ast_variable_browse(cfg, "plc"); var ; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ adpcmtolin.useplc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "codec_adpcm: %susing generic PLC\n", adpcmtolin.useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+/*! \brief standard module glue */
+static int reload(void)
+{
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintoadpcm);
+ res |= ast_unregister_translator(&adpcmtolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ res = ast_register_translator(&adpcmtolin);
+ if (!res)
+ res = ast_register_translator(&lintoadpcm);
+ else
+ ast_unregister_translator(&adpcmtolin);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Adaptive Differential PCM Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_alaw.c b/trunk/codecs/codec_alaw.c
new file mode 100644
index 000000000..ba5808f78
--- /dev/null
+++ b/trunk/codecs/codec_alaw.c
@@ -0,0 +1,184 @@
+/*
+ * 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 codec_alaw.c - translate between signed linear and alaw
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/translate.h"
+#include "asterisk/alaw.h"
+#include "asterisk/utils.h"
+
+#define BUFFER_SAMPLES 8096 /* size for the translation buffers */
+
+/* Sample frame data (Mu data is okay) */
+
+#include "slin_ulaw_ex.h"
+#include "ulaw_slin_ex.h"
+
+/*! \brief decode frame into lin and fill output buffer. */
+static int alawtolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int i = f->samples;
+ unsigned char *src = f->data;
+ int16_t *dst = (int16_t *)pvt->outbuf + pvt->samples;
+
+ pvt->samples += i;
+ pvt->datalen += i * 2; /* 2 bytes/sample */
+
+ while (i--)
+ *dst++ = AST_ALAW(*src++);
+
+ return 0;
+}
+
+/*! \brief convert and store input samples in output buffer */
+static int lintoalaw_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int i = f->samples;
+ char *dst = pvt->outbuf + pvt->samples;
+ int16_t *src = f->data;
+
+ pvt->samples += i;
+ pvt->datalen += i; /* 1 byte/sample */
+
+ while (i--)
+ *dst++ = AST_LIN2A(*src++);
+
+ return 0;
+}
+
+/*! \brief alawToLin_Sample */
+static struct ast_frame *alawtolin_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_ALAW;
+ f.datalen = sizeof(ulaw_slin_ex);
+ f.samples = sizeof(ulaw_slin_ex);
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = ulaw_slin_ex;
+ return &f;
+}
+
+/*! \brief LinToalaw_Sample */
+static struct ast_frame *lintoalaw_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_ulaw_ex);
+ f.samples = sizeof(slin_ulaw_ex) / 2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_ulaw_ex;
+ return &f;
+}
+
+static struct ast_translator alawtolin = {
+ .name = "alawtolin",
+ .srcfmt = AST_FORMAT_ALAW,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .framein = alawtolin_framein,
+ .sample = alawtolin_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .plc_samples = 160,
+};
+
+static struct ast_translator lintoalaw = {
+ "lintoalaw",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_ALAW,
+ .framein = lintoalaw_framein,
+ .sample = lintoalaw_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES,
+};
+
+static int parse_config(int reload)
+{
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ alawtolin.useplc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "codec_alaw: %susing generic PLC\n", alawtolin.useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+/*! \brief standard module stuff */
+
+static int reload(void)
+{
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintoalaw);
+ res |= ast_unregister_translator(&alawtolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ res = ast_register_translator(&alawtolin);
+ if (!res)
+ res = ast_register_translator(&lintoalaw);
+ else
+ ast_unregister_translator(&alawtolin);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "A-law Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_g722.c b/trunk/codecs/codec_g722.c
new file mode 100644
index 000000000..99c3856bc
--- /dev/null
+++ b/trunk/codecs/codec_g722.c
@@ -0,0 +1,301 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, Digium, Inc.
+ *
+ * Matthew Fredrickson <creslin@digium.com>
+ * Russell Bryant <russell@digium.com>
+ *
+ * Special thanks to Steve Underwood for the implementation
+ * and for doing the 8khz<->g.722 direct translation 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 codec_g722.c - translate between signed linear and ITU G.722-64kbps
+ *
+ * \author Matthew Fredrickson <creslin@digium.com>
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \arg http://soft-switch.org/downloads/non-gpl-bits.tgz
+ * \arg http://lists.digium.com/pipermail/asterisk-dev/2006-September/022866.html
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+
+#define BUFFER_SAMPLES 8096 /* size for the translation buffers */
+#define BUF_SHIFT 5
+
+/* Sample frame data */
+
+#include "g722/g722.h"
+#include "slin_g722_ex.h"
+#include "g722_slin_ex.h"
+
+struct g722_encoder_pvt {
+ g722_encode_state_t g722;
+};
+
+struct g722_decoder_pvt {
+ g722_decode_state_t g722;
+};
+
+/*! \brief init a new instance of g722_encoder_pvt. */
+static int lintog722_new(struct ast_trans_pvt *pvt)
+{
+ struct g722_encoder_pvt *tmp = pvt->pvt;
+
+ g722_encode_init(&tmp->g722, 64000, G722_SAMPLE_RATE_8000);
+
+ return 0;
+}
+
+static int lin16tog722_new(struct ast_trans_pvt *pvt)
+{
+ struct g722_encoder_pvt *tmp = pvt->pvt;
+
+ g722_encode_init(&tmp->g722, 64000, 0);
+
+ return 0;
+}
+
+/*! \brief init a new instance of g722_encoder_pvt. */
+static int g722tolin_new(struct ast_trans_pvt *pvt)
+{
+ struct g722_decoder_pvt *tmp = pvt->pvt;
+
+ g722_decode_init(&tmp->g722, 64000, G722_SAMPLE_RATE_8000);
+
+ return 0;
+}
+
+static int g722tolin16_new(struct ast_trans_pvt *pvt)
+{
+ struct g722_decoder_pvt *tmp = pvt->pvt;
+
+ g722_decode_init(&tmp->g722, 64000, 0);
+
+ return 0;
+}
+
+static int g722tolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct g722_decoder_pvt *tmp = pvt->pvt;
+ int out_samples;
+
+ out_samples = g722_decode(&tmp->g722, (int16_t *) &pvt->outbuf[pvt->samples * sizeof(int16_t)],
+ (uint8_t *) f->data, f->samples);
+
+ pvt->samples += out_samples;
+
+ pvt->datalen += (out_samples * sizeof(int16_t));
+
+ return 0;
+}
+
+static int lintog722_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct g722_encoder_pvt *tmp = pvt->pvt;
+ int outlen;
+
+ outlen = g722_encode(&tmp->g722, (uint8_t *) (&pvt->outbuf[pvt->datalen]),
+ (int16_t *) f->data, f->samples);
+
+ pvt->samples += outlen;
+
+ pvt->datalen += outlen;
+
+ return 0;
+}
+
+static struct ast_frame *g722tolin_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_G722,
+ .datalen = sizeof(g722_slin_ex),
+ .samples = sizeof(g722_slin_ex) * 2,
+ .src = __PRETTY_FUNCTION__,
+ .data = g722_slin_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_frame *g722tolin16_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_G722,
+ .datalen = sizeof(slin_g722_ex),
+ .samples = sizeof(slin_g722_ex) * 2,
+ .src = __PRETTY_FUNCTION__,
+ .data = slin_g722_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_frame *lintog722_sample (void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .datalen = sizeof(slin_g722_ex),
+ .samples = sizeof(slin_g722_ex) / sizeof(slin_g722_ex[0]),
+ .src = __PRETTY_FUNCTION__,
+ .data = slin_g722_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_frame *lin16tog722_sample (void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR16,
+ .datalen = sizeof(slin_g722_ex),
+ .samples = sizeof(slin_g722_ex) / sizeof(slin_g722_ex[0]),
+ .src = __PRETTY_FUNCTION__,
+ .data = slin_g722_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_translator g722tolin = {
+ .name = "g722tolin",
+ .srcfmt = AST_FORMAT_G722,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = g722tolin_new, /* same for both directions */
+ .framein = g722tolin_framein,
+ .sample = g722tolin_sample,
+ .desc_size = sizeof(struct g722_decoder_pvt),
+ .buffer_samples = BUFFER_SAMPLES / sizeof(int16_t),
+ .buf_size = BUFFER_SAMPLES,
+ .plc_samples = 160,
+};
+
+static struct ast_translator lintog722 = {
+ .name = "lintog722",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_G722,
+ .newpvt = lintog722_new, /* same for both directions */
+ .framein = lintog722_framein,
+ .sample = lintog722_sample,
+ .desc_size = sizeof(struct g722_encoder_pvt),
+ .buffer_samples = BUFFER_SAMPLES * 2,
+ .buf_size = BUFFER_SAMPLES,
+};
+
+static struct ast_translator g722tolin16 = {
+ .name = "g722tolin16",
+ .srcfmt = AST_FORMAT_G722,
+ .dstfmt = AST_FORMAT_SLINEAR16,
+ .newpvt = g722tolin16_new, /* same for both directions */
+ .framein = g722tolin_framein,
+ .sample = g722tolin16_sample,
+ .desc_size = sizeof(struct g722_decoder_pvt),
+ .buffer_samples = BUFFER_SAMPLES / sizeof(int16_t),
+ .buf_size = BUFFER_SAMPLES,
+ .plc_samples = 160,
+};
+
+static struct ast_translator lin16tog722 = {
+ .name = "lin16tog722",
+ .srcfmt = AST_FORMAT_SLINEAR16,
+ .dstfmt = AST_FORMAT_G722,
+ .newpvt = lin16tog722_new, /* same for both directions */
+ .framein = lintog722_framein,
+ .sample = lin16tog722_sample,
+ .desc_size = sizeof(struct g722_encoder_pvt),
+ .buffer_samples = BUFFER_SAMPLES * 2,
+ .buf_size = BUFFER_SAMPLES,
+};
+
+static int parse_config(int reload)
+{
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ g722tolin.useplc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "codec_g722: %susing generic PLC\n",
+ g722tolin.useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+static int reload(void)
+{
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_unregister_translator(&g722tolin);
+ res |= ast_unregister_translator(&lintog722);
+ res |= ast_unregister_translator(&g722tolin16);
+ res |= ast_unregister_translator(&lin16tog722);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ res |= ast_register_translator(&g722tolin);
+ res |= ast_register_translator(&lintog722);
+ res |= ast_register_translator(&g722tolin16);
+ res |= ast_register_translator(&lin16tog722);
+
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ITU G.722-64kbps G722 Transcoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_g726.c b/trunk/codecs/codec_g726.c
new file mode 100644
index 000000000..c8a671ee8
--- /dev/null
+++ b/trunk/codecs/codec_g726.c
@@ -0,0 +1,960 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * Based on frompcm.c and topcm.c from the Emiliano MIPL browser/
+ * interpreter. See http://www.bsdtelephony.com.mx
+ *
+ * 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 codec_g726.c - translate between signed linear and ITU G.726-32kbps (both RFC3551 and AAL2 codeword packing)
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+
+#define WANT_ASM
+#include "log2comp.h"
+
+/* define NOT_BLI to use a faster but not bit-level identical version */
+/* #define NOT_BLI */
+
+#if defined(NOT_BLI)
+# if defined(_MSC_VER)
+typedef __int64 sint64;
+# elif defined(__GNUC__)
+typedef long long sint64;
+# else
+# error 64-bit integer type is not defined for your compiler/platform
+# endif
+#endif
+
+#define BUFFER_SAMPLES 8096 /* size for the translation buffers */
+#define BUF_SHIFT 5
+
+/* Sample frame data */
+
+#include "slin_g726_ex.h"
+#include "g726_slin_ex.h"
+
+/*
+ * The following is the definition of the state structure
+ * used by the G.726 encoder and decoder to preserve their internal
+ * state between successive calls. The meanings of the majority
+ * of the state structure fields are explained in detail in the
+ * CCITT Recommendation G.721. The field names are essentially identical
+ * to variable names in the bit level description of the coding algorithm
+ * included in this Recommendation.
+ */
+struct g726_state {
+ long yl; /* Locked or steady state step size multiplier. */
+ int yu; /* Unlocked or non-steady state step size multiplier. */
+ int dms; /* Short term energy estimate. */
+ int dml; /* Long term energy estimate. */
+ int ap; /* Linear weighting coefficient of 'yl' and 'yu'. */
+ int a[2]; /* Coefficients of pole portion of prediction filter.
+ * stored as fixed-point 1==2^14 */
+ int b[6]; /* Coefficients of zero portion of prediction filter.
+ * stored as fixed-point 1==2^14 */
+ int pk[2]; /* Signs of previous two samples of a partially
+ * reconstructed signal. */
+ int dq[6]; /* Previous 6 samples of the quantized difference signal
+ * stored as fixed point 1==2^12,
+ * or in internal floating point format */
+ int sr[2]; /* Previous 2 samples of the quantized difference signal
+ * stored as fixed point 1==2^12,
+ * or in internal floating point format */
+ int td; /* delayed tone detect, new in 1988 version */
+};
+
+static int qtab_721[7] = {-124, 80, 178, 246, 300, 349, 400};
+/*
+ * Maps G.721 code word to reconstructed scale factor normalized log
+ * magnitude values.
+ */
+static int _dqlntab[16] = {-2048, 4, 135, 213, 273, 323, 373, 425,
+ 425, 373, 323, 273, 213, 135, 4, -2048};
+
+/* Maps G.721 code word to log of scale factor multiplier. */
+static int _witab[16] = {-12, 18, 41, 64, 112, 198, 355, 1122,
+ 1122, 355, 198, 112, 64, 41, 18, -12};
+/*
+ * Maps G.721 code words to a set of values whose long and short
+ * term averages are computed and then compared to give an indication
+ * how stationary (steady state) the signal is.
+ */
+static int _fitab[16] = {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00,
+ 0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0};
+
+
+/*
+ * g72x_init_state()
+ *
+ * This routine initializes and/or resets the g726_state structure
+ * pointed to by 'state_ptr'.
+ * All the initial state values are specified in the CCITT G.721 document.
+ */
+static void g726_init_state(struct g726_state *state_ptr)
+{
+ int cnta;
+
+ state_ptr->yl = 34816;
+ state_ptr->yu = 544;
+ state_ptr->dms = 0;
+ state_ptr->dml = 0;
+ state_ptr->ap = 0;
+ for (cnta = 0; cnta < 2; cnta++) {
+ state_ptr->a[cnta] = 0;
+ state_ptr->pk[cnta] = 0;
+#ifdef NOT_BLI
+ state_ptr->sr[cnta] = 1;
+#else
+ state_ptr->sr[cnta] = 32;
+#endif
+ }
+ for (cnta = 0; cnta < 6; cnta++) {
+ state_ptr->b[cnta] = 0;
+#ifdef NOT_BLI
+ state_ptr->dq[cnta] = 1;
+#else
+ state_ptr->dq[cnta] = 32;
+#endif
+ }
+ state_ptr->td = 0;
+}
+
+/*
+ * quan()
+ *
+ * quantizes the input val against the table of integers.
+ * It returns i if table[i - 1] <= val < table[i].
+ *
+ * Using linear search for simple coding.
+ */
+static int quan(int val, int *table, int size)
+{
+ int i;
+
+ for (i = 0; i < size && val >= *table; ++i, ++table)
+ ;
+ return (i);
+}
+
+#ifdef NOT_BLI /* faster non-identical version */
+
+/*
+ * predictor_zero()
+ *
+ * computes the estimated signal from 6-zero predictor.
+ *
+ */
+static int predictor_zero(struct g726_state *state_ptr)
+{ /* divide by 2 is necessary here to handle negative numbers correctly */
+ int i;
+ sint64 sezi;
+ for (sezi = 0, i = 0; i < 6; i++) /* ACCUM */
+ sezi += (sint64)state_ptr->b[i] * state_ptr->dq[i];
+ return (int)(sezi >> 13) / 2 /* 2^14 */;
+}
+
+/*
+ * predictor_pole()
+ *
+ * computes the estimated signal from 2-pole predictor.
+ *
+ */
+static int predictor_pole(struct g726_state *state_ptr)
+{ /* divide by 2 is necessary here to handle negative numbers correctly */
+ return (int)(((sint64)state_ptr->a[1] * state_ptr->sr[1] +
+ (sint64)state_ptr->a[0] * state_ptr->sr[0]) >> 13) / 2 /* 2^14 */;
+}
+
+#else /* NOT_BLI - identical version */
+/*
+ * fmult()
+ *
+ * returns the integer product of the fixed-point number "an" (1==2^12) and
+ * "floating point" representation (4-bit exponent, 6-bit mantessa) "srn".
+ */
+static int fmult(int an, int srn)
+{
+ int anmag, anexp, anmant;
+ int wanexp, wanmant;
+ int retval;
+
+ anmag = (an > 0) ? an : ((-an) & 0x1FFF);
+ anexp = ilog2(anmag) - 5;
+ anmant = (anmag == 0) ? 32 :
+ (anexp >= 0) ? anmag >> anexp : anmag << -anexp;
+ wanexp = anexp + ((srn >> 6) & 0xF) - 13;
+
+ wanmant = (anmant * (srn & 077) + 0x30) >> 4;
+ retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) :
+ (wanmant >> -wanexp);
+
+ return (((an ^ srn) < 0) ? -retval : retval);
+}
+
+static int predictor_zero(struct g726_state *state_ptr)
+{
+ int i;
+ int sezi;
+ for (sezi = 0, i = 0; i < 6; i++) /* ACCUM */
+ sezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]);
+ return sezi;
+}
+
+static int predictor_pole(struct g726_state *state_ptr)
+{
+ return (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) +
+ fmult(state_ptr->a[0] >> 2, state_ptr->sr[0]));
+}
+
+#endif /* NOT_BLI */
+
+/*
+ * step_size()
+ *
+ * computes the quantization step size of the adaptive quantizer.
+ *
+ */
+static int step_size(struct g726_state *state_ptr)
+{
+ int y;
+ int dif;
+ int al;
+
+ if (state_ptr->ap >= 256)
+ return (state_ptr->yu);
+ else {
+ y = state_ptr->yl >> 6;
+ dif = state_ptr->yu - y;
+ al = state_ptr->ap >> 2;
+ if (dif > 0)
+ y += (dif * al) >> 6;
+ else if (dif < 0)
+ y += (dif * al + 0x3F) >> 6;
+ return (y);
+ }
+}
+
+/*
+ * quantize()
+ *
+ * Given a raw sample, 'd', of the difference signal and a
+ * quantization step size scale factor, 'y', this routine returns the
+ * ADPCM codeword to which that sample gets quantized. The step
+ * size scale factor division operation is done in the log base 2 domain
+ * as a subtraction.
+ */
+static int quantize(
+ int d, /* Raw difference signal sample */
+ int y, /* Step size multiplier */
+ int *table, /* quantization table */
+ int size) /* table size of integers */
+{
+ int dqm; /* Magnitude of 'd' */
+ int exp; /* Integer part of base 2 log of 'd' */
+ int mant; /* Fractional part of base 2 log */
+ int dl; /* Log of magnitude of 'd' */
+ int dln; /* Step size scale factor normalized log */
+ int i;
+
+ /*
+ * LOG
+ *
+ * Compute base 2 log of 'd', and store in 'dl'.
+ */
+ dqm = abs(d);
+ exp = ilog2(dqm);
+ if (exp < 0)
+ exp = 0;
+ mant = ((dqm << 7) >> exp) & 0x7F; /* Fractional portion. */
+ dl = (exp << 7) | mant;
+
+ /*
+ * SUBTB
+ *
+ * "Divide" by step size multiplier.
+ */
+ dln = dl - (y >> 2);
+
+ /*
+ * QUAN
+ *
+ * Obtain codword i for 'd'.
+ */
+ i = quan(dln, table, size);
+ if (d < 0) /* take 1's complement of i */
+ return ((size << 1) + 1 - i);
+ else if (i == 0) /* take 1's complement of 0 */
+ return ((size << 1) + 1); /* new in 1988 */
+ else
+ return (i);
+}
+
+/*
+ * reconstruct()
+ *
+ * Returns reconstructed difference signal 'dq' obtained from
+ * codeword 'i' and quantization step size scale factor 'y'.
+ * Multiplication is performed in log base 2 domain as addition.
+ */
+static int reconstruct(
+ int sign, /* 0 for non-negative value */
+ int dqln, /* G.72x codeword */
+ int y) /* Step size multiplier */
+{
+ int dql; /* Log of 'dq' magnitude */
+ int dex; /* Integer part of log */
+ int dqt;
+ int dq; /* Reconstructed difference signal sample */
+
+ dql = dqln + (y >> 2); /* ADDA */
+
+ if (dql < 0) {
+#ifdef NOT_BLI
+ return (sign) ? -1 : 1;
+#else
+ return (sign) ? -0x8000 : 0;
+#endif
+ } else { /* ANTILOG */
+ dex = (dql >> 7) & 15;
+ dqt = 128 + (dql & 127);
+#ifdef NOT_BLI
+ dq = ((dqt << 19) >> (14 - dex));
+ return (sign) ? -dq : dq;
+#else
+ dq = (dqt << 7) >> (14 - dex);
+ return (sign) ? (dq - 0x8000) : dq;
+#endif
+ }
+}
+
+/*
+ * update()
+ *
+ * updates the state variables for each output code
+ */
+static void update(
+ int code_size, /* distinguish 723_40 with others */
+ int y, /* quantizer step size */
+ int wi, /* scale factor multiplier */
+ int fi, /* for long/short term energies */
+ int dq, /* quantized prediction difference */
+ int sr, /* reconstructed signal */
+ int dqsez, /* difference from 2-pole predictor */
+ struct g726_state *state_ptr) /* coder state pointer */
+{
+ int cnt;
+ int mag; /* Adaptive predictor, FLOAT A */
+#ifndef NOT_BLI
+ int exp;
+#endif
+ int a2p=0; /* LIMC */
+ int a1ul; /* UPA1 */
+ int pks1; /* UPA2 */
+ int fa1;
+ int tr; /* tone/transition detector */
+ int ylint, thr2, dqthr;
+ int ylfrac, thr1;
+ int pk0;
+
+ pk0 = (dqsez < 0) ? 1 : 0; /* needed in updating predictor poles */
+
+#ifdef NOT_BLI
+ mag = abs(dq / 0x1000); /* prediction difference magnitude */
+#else
+ mag = dq & 0x7FFF; /* prediction difference magnitude */
+#endif
+ /* TRANS */
+ ylint = state_ptr->yl >> 15; /* exponent part of yl */
+ ylfrac = (state_ptr->yl >> 10) & 0x1F; /* fractional part of yl */
+ thr1 = (32 + ylfrac) << ylint; /* threshold */
+ thr2 = (ylint > 9) ? 31 << 10 : thr1; /* limit thr2 to 31 << 10 */
+ dqthr = (thr2 + (thr2 >> 1)) >> 1; /* dqthr = 0.75 * thr2 */
+ if (state_ptr->td == 0) /* signal supposed voice */
+ tr = 0;
+ else if (mag <= dqthr) /* supposed data, but small mag */
+ tr = 0; /* treated as voice */
+ else /* signal is data (modem) */
+ tr = 1;
+
+ /*
+ * Quantizer scale factor adaptation.
+ */
+
+ /* FUNCTW & FILTD & DELAY */
+ /* update non-steady state step size multiplier */
+ state_ptr->yu = y + ((wi - y) >> 5);
+
+ /* LIMB */
+ if (state_ptr->yu < 544) /* 544 <= yu <= 5120 */
+ state_ptr->yu = 544;
+ else if (state_ptr->yu > 5120)
+ state_ptr->yu = 5120;
+
+ /* FILTE & DELAY */
+ /* update steady state step size multiplier */
+ state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6);
+
+ /*
+ * Adaptive predictor coefficients.
+ */
+ if (tr == 1) { /* reset a's and b's for modem signal */
+ state_ptr->a[0] = 0;
+ state_ptr->a[1] = 0;
+ state_ptr->b[0] = 0;
+ state_ptr->b[1] = 0;
+ state_ptr->b[2] = 0;
+ state_ptr->b[3] = 0;
+ state_ptr->b[4] = 0;
+ state_ptr->b[5] = 0;
+ } else { /* update a's and b's */
+ pks1 = pk0 ^ state_ptr->pk[0]; /* UPA2 */
+
+ /* update predictor pole a[1] */
+ a2p = state_ptr->a[1] - (state_ptr->a[1] >> 7);
+ if (dqsez != 0) {
+ fa1 = (pks1) ? state_ptr->a[0] : -state_ptr->a[0];
+ if (fa1 < -8191) /* a2p = function of fa1 */
+ a2p -= 0x100;
+ else if (fa1 > 8191)
+ a2p += 0xFF;
+ else
+ a2p += fa1 >> 5;
+
+ if (pk0 ^ state_ptr->pk[1])
+ /* LIMC */
+ if (a2p <= -12160)
+ a2p = -12288;
+ else if (a2p >= 12416)
+ a2p = 12288;
+ else
+ a2p -= 0x80;
+ else if (a2p <= -12416)
+ a2p = -12288;
+ else if (a2p >= 12160)
+ a2p = 12288;
+ else
+ a2p += 0x80;
+ }
+
+ /* TRIGB & DELAY */
+ state_ptr->a[1] = a2p;
+
+ /* UPA1 */
+ /* update predictor pole a[0] */
+ state_ptr->a[0] -= state_ptr->a[0] >> 8;
+ if (dqsez != 0) {
+ if (pks1 == 0)
+ state_ptr->a[0] += 192;
+ else
+ state_ptr->a[0] -= 192;
+ }
+ /* LIMD */
+ a1ul = 15360 - a2p;
+ if (state_ptr->a[0] < -a1ul)
+ state_ptr->a[0] = -a1ul;
+ else if (state_ptr->a[0] > a1ul)
+ state_ptr->a[0] = a1ul;
+
+ /* UPB : update predictor zeros b[6] */
+ for (cnt = 0; cnt < 6; cnt++) {
+ if (code_size == 5) /* for 40Kbps G.723 */
+ state_ptr->b[cnt] -= state_ptr->b[cnt] >> 9;
+ else /* for G.721 and 24Kbps G.723 */
+ state_ptr->b[cnt] -= state_ptr->b[cnt] >> 8;
+ if (mag)
+ { /* XOR */
+ if ((dq ^ state_ptr->dq[cnt]) >= 0)
+ state_ptr->b[cnt] += 128;
+ else
+ state_ptr->b[cnt] -= 128;
+ }
+ }
+ }
+
+ for (cnt = 5; cnt > 0; cnt--)
+ state_ptr->dq[cnt] = state_ptr->dq[cnt-1];
+#ifdef NOT_BLI
+ state_ptr->dq[0] = dq;
+#else
+ /* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */
+ if (mag == 0) {
+ state_ptr->dq[0] = (dq >= 0) ? 0x20 : 0x20 - 0x400;
+ } else {
+ exp = ilog2(mag) + 1;
+ state_ptr->dq[0] = (dq >= 0) ?
+ (exp << 6) + ((mag << 6) >> exp) :
+ (exp << 6) + ((mag << 6) >> exp) - 0x400;
+ }
+#endif
+
+ state_ptr->sr[1] = state_ptr->sr[0];
+#ifdef NOT_BLI
+ state_ptr->sr[0] = sr;
+#else
+ /* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */
+ if (sr == 0) {
+ state_ptr->sr[0] = 0x20;
+ } else if (sr > 0) {
+ exp = ilog2(sr) + 1;
+ state_ptr->sr[0] = (exp << 6) + ((sr << 6) >> exp);
+ } else if (sr > -0x8000) {
+ mag = -sr;
+ exp = ilog2(mag) + 1;
+ state_ptr->sr[0] = (exp << 6) + ((mag << 6) >> exp) - 0x400;
+ } else
+ state_ptr->sr[0] = 0x20 - 0x400;
+#endif
+
+ /* DELAY A */
+ state_ptr->pk[1] = state_ptr->pk[0];
+ state_ptr->pk[0] = pk0;
+
+ /* TONE */
+ if (tr == 1) /* this sample has been treated as data */
+ state_ptr->td = 0; /* next one will be treated as voice */
+ else if (a2p < -11776) /* small sample-to-sample correlation */
+ state_ptr->td = 1; /* signal may be data */
+ else /* signal is voice */
+ state_ptr->td = 0;
+
+ /*
+ * Adaptation speed control.
+ */
+ state_ptr->dms += (fi - state_ptr->dms) >> 5; /* FILTA */
+ state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7); /* FILTB */
+
+ if (tr == 1)
+ state_ptr->ap = 256;
+ else if (y < 1536) /* SUBTC */
+ state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
+ else if (state_ptr->td == 1)
+ state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
+ else if (abs((state_ptr->dms << 2) - state_ptr->dml) >=
+ (state_ptr->dml >> 3))
+ state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
+ else
+ state_ptr->ap += (-state_ptr->ap) >> 4;
+}
+
+/*
+ * g726_decode()
+ *
+ * Description:
+ *
+ * Decodes a 4-bit code of G.726-32 encoded data of i and
+ * returns the resulting linear PCM, A-law or u-law value.
+ * return -1 for unknown out_coding value.
+ */
+static int g726_decode(int i, struct g726_state *state_ptr)
+{
+ int sezi, sez, se; /* ACCUM */
+ int y; /* MIX */
+ int sr; /* ADDB */
+ int dq;
+ int dqsez;
+
+ i &= 0x0f; /* mask to get proper bits */
+#ifdef NOT_BLI
+ sezi = predictor_zero(state_ptr);
+ sez = sezi;
+ se = sezi + predictor_pole(state_ptr); /* estimated signal */
+#else
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ se = (sezi + predictor_pole(state_ptr)) >> 1; /* estimated signal */
+#endif
+
+ y = step_size(state_ptr); /* dynamic quantizer step size */
+
+ dq = reconstruct(i & 8, _dqlntab[i], y); /* quantized diff. */
+
+#ifdef NOT_BLI
+ sr = se + dq; /* reconst. signal */
+ dqsez = dq + sez; /* pole prediction diff. */
+#else
+ sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconst. signal */
+ dqsez = sr - se + sez; /* pole prediction diff. */
+#endif
+
+ update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);
+
+#ifdef NOT_BLI
+ return (sr >> 10); /* sr was 26-bit dynamic range */
+#else
+ return (sr << 2); /* sr was 14-bit dynamic range */
+#endif
+}
+
+/*
+ * g726_encode()
+ *
+ * Encodes the input vale of linear PCM, A-law or u-law data sl and returns
+ * the resulting code. -1 is returned for unknown input coding value.
+ */
+static int g726_encode(int sl, struct g726_state *state_ptr)
+{
+ int sezi, se, sez; /* ACCUM */
+ int d; /* SUBTA */
+ int sr; /* ADDB */
+ int y; /* MIX */
+ int dqsez; /* ADDC */
+ int dq, i;
+
+#ifdef NOT_BLI
+ sl <<= 10; /* 26-bit dynamic range */
+
+ sezi = predictor_zero(state_ptr);
+ sez = sezi;
+ se = sezi + predictor_pole(state_ptr); /* estimated signal */
+#else
+ sl >>= 2; /* 14-bit dynamic range */
+
+ sezi = predictor_zero(state_ptr);
+ sez = sezi >> 1;
+ se = (sezi + predictor_pole(state_ptr)) >> 1; /* estimated signal */
+#endif
+
+ d = sl - se; /* estimation difference */
+
+ /* quantize the prediction difference */
+ y = step_size(state_ptr); /* quantizer step size */
+#ifdef NOT_BLI
+ d /= 0x1000;
+#endif
+ i = quantize(d, y, qtab_721, 7); /* i = G726 code */
+
+ dq = reconstruct(i & 8, _dqlntab[i], y); /* quantized est diff */
+
+#ifdef NOT_BLI
+ sr = se + dq; /* reconst. signal */
+ dqsez = dq + sez; /* pole prediction diff. */
+#else
+ sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconst. signal */
+ dqsez = sr - se + sez; /* pole prediction diff. */
+#endif
+
+ update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);
+
+ return (i);
+}
+
+/*
+ * Private workspace for translating signed linear signals to G726.
+ * Don't bother to define two distinct structs.
+ */
+
+struct g726_coder_pvt {
+ /* buffer any odd byte in input - 0x80 + (value & 0xf) if present */
+ unsigned char next_flag;
+ struct g726_state g726;
+};
+
+/*! \brief init a new instance of g726_coder_pvt. */
+static int lintog726_new(struct ast_trans_pvt *pvt)
+{
+ struct g726_coder_pvt *tmp = pvt->pvt;
+
+ g726_init_state(&tmp->g726);
+
+ return 0;
+}
+
+/*! \brief decode packed 4-bit G726 values (AAL2 packing) and store in buffer. */
+static int g726aal2tolin_framein (struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct g726_coder_pvt *tmp = pvt->pvt;
+ unsigned char *src = f->data;
+ int16_t *dst = (int16_t *) pvt->outbuf + pvt->samples;
+ unsigned int i;
+
+ for (i = 0; i < f->datalen; i++) {
+ *dst++ = g726_decode((src[i] >> 4) & 0xf, &tmp->g726);
+ *dst++ = g726_decode(src[i] & 0x0f, &tmp->g726);
+ }
+
+ pvt->samples += f->samples;
+ pvt->datalen += 2 * f->samples; /* 2 bytes/sample */
+
+ return 0;
+}
+
+/*! \brief compress and store data (4-bit G726 samples, AAL2 packing) in outbuf */
+static int lintog726aal2_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct g726_coder_pvt *tmp = pvt->pvt;
+ int16_t *src = f->data;
+ unsigned int i;
+
+ for (i = 0; i < f->samples; i++) {
+ unsigned char d = g726_encode(src[i], &tmp->g726); /* this sample */
+
+ if (tmp->next_flag & 0x80) { /* merge with leftover sample */
+ pvt->outbuf[pvt->datalen++] = ((tmp->next_flag & 0xf)<< 4) | d;
+ pvt->samples += 2; /* 2 samples per byte */
+ tmp->next_flag = 0;
+ } else {
+ tmp->next_flag = 0x80 | d;
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief decode packed 4-bit G726 values (RFC3551 packing) and store in buffer. */
+static int g726tolin_framein (struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct g726_coder_pvt *tmp = pvt->pvt;
+ unsigned char *src = f->data;
+ int16_t *dst = (int16_t *) pvt->outbuf + pvt->samples;
+ unsigned int i;
+
+ for (i = 0; i < f->datalen; i++) {
+ *dst++ = g726_decode(src[i] & 0x0f, &tmp->g726);
+ *dst++ = g726_decode((src[i] >> 4) & 0xf, &tmp->g726);
+ }
+
+ pvt->samples += f->samples;
+ pvt->datalen += 2 * f->samples; /* 2 bytes/sample */
+
+ return 0;
+}
+
+/*! \brief compress and store data (4-bit G726 samples, RFC3551 packing) in outbuf */
+static int lintog726_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct g726_coder_pvt *tmp = pvt->pvt;
+ int16_t *src = f->data;
+ unsigned int i;
+
+ for (i = 0; i < f->samples; i++) {
+ unsigned char d = g726_encode(src[i], &tmp->g726); /* this sample */
+
+ if (tmp->next_flag & 0x80) { /* merge with leftover sample */
+ pvt->outbuf[pvt->datalen++] = (d << 4) | (tmp->next_flag & 0xf);
+ pvt->samples += 2; /* 2 samples per byte */
+ tmp->next_flag = 0;
+ } else {
+ tmp->next_flag = 0x80 | d;
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief convert G726-32 RFC3551 packed data into AAL2 packed data (or vice-versa) */
+static int g726tog726aal2_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ unsigned char *src = f->data;
+ unsigned char *dst = (unsigned char *) pvt->outbuf + pvt->samples;
+ unsigned int i;
+
+ for (i = 0; i < f->datalen; i++)
+ *dst++ = ((src[i] & 0xf) << 4) | (src[i] >> 4);
+
+ pvt->samples += f->samples;
+ pvt->datalen += f->samples; /* 1 byte/sample */
+
+ return 0;
+}
+
+static struct ast_frame *g726tolin_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_G726,
+ .datalen = sizeof(g726_slin_ex),
+ .samples = sizeof(g726_slin_ex) * 2, /* 2 samples per byte */
+ .src = __PRETTY_FUNCTION__,
+ .data = g726_slin_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_frame *lintog726_sample (void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .datalen = sizeof(slin_g726_ex),
+ .samples = sizeof(slin_g726_ex) / 2, /* 1 sample per 2 bytes */
+ .src = __PRETTY_FUNCTION__,
+ .data = slin_g726_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_translator g726tolin = {
+ .name = "g726tolin",
+ .srcfmt = AST_FORMAT_G726,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = lintog726_new, /* same for both directions */
+ .framein = g726tolin_framein,
+ .sample = g726tolin_sample,
+ .desc_size = sizeof(struct g726_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .plc_samples = 160,
+};
+
+static struct ast_translator lintog726 = {
+ .name = "lintog726",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_G726,
+ .newpvt = lintog726_new, /* same for both directions */
+ .framein = lintog726_framein,
+ .sample = lintog726_sample,
+ .desc_size = sizeof(struct g726_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES/2,
+};
+
+static struct ast_translator g726aal2tolin = {
+ .name = "g726aal2tolin",
+ .srcfmt = AST_FORMAT_G726_AAL2,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = lintog726_new, /* same for both directions */
+ .framein = g726aal2tolin_framein,
+ .sample = g726tolin_sample,
+ .desc_size = sizeof(struct g726_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .plc_samples = 160,
+};
+
+static struct ast_translator lintog726aal2 = {
+ .name = "lintog726aal2",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_G726_AAL2,
+ .newpvt = lintog726_new, /* same for both directions */
+ .framein = lintog726aal2_framein,
+ .sample = lintog726_sample,
+ .desc_size = sizeof(struct g726_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES / 2,
+};
+
+static struct ast_translator g726tog726aal2 = {
+ .name = "g726tog726aal2",
+ .srcfmt = AST_FORMAT_G726,
+ .dstfmt = AST_FORMAT_G726_AAL2,
+ .framein = g726tog726aal2_framein, /* same for both directions */
+ .sample = lintog726_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES,
+};
+
+static struct ast_translator g726aal2tog726 = {
+ .name = "g726aal2tog726",
+ .srcfmt = AST_FORMAT_G726_AAL2,
+ .dstfmt = AST_FORMAT_G726,
+ .framein = g726tog726aal2_framein, /* same for both directions */
+ .sample = lintog726_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES,
+};
+
+static int parse_config(int reload)
+{
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ g726tolin.useplc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "codec_g726: %susing generic PLC\n",
+ g726tolin.useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+static int reload(void)
+{
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_unregister_translator(&g726tolin);
+ res |= ast_unregister_translator(&lintog726);
+
+ res |= ast_unregister_translator(&g726aal2tolin);
+ res |= ast_unregister_translator(&lintog726aal2);
+
+ res |= ast_unregister_translator(&g726aal2tog726);
+ res |= ast_unregister_translator(&g726tog726aal2);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ res |= ast_register_translator(&g726tolin);
+ res |= ast_register_translator(&lintog726);
+
+ res |= ast_register_translator(&g726aal2tolin);
+ res |= ast_register_translator(&lintog726aal2);
+
+ res |= ast_register_translator(&g726aal2tog726);
+ res |= ast_register_translator(&g726tog726aal2);
+
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ITU G.726-32kbps G726 Transcoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_gsm.c b/trunk/codecs/codec_gsm.c
new file mode 100644
index 000000000..bb4af8cfb
--- /dev/null
+++ b/trunk/codecs/codec_gsm.c
@@ -0,0 +1,286 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * The GSM code is from TOAST. Copyright information for that package is available
+ * in the GSM directory.
+ *
+ * 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 Translate between signed linear and Global System for Mobile Communications (GSM)
+ *
+ * \ingroup codecs
+ */
+
+/*** MODULEINFO
+ <depend>gsm</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/translate.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+
+#ifdef HAVE_GSM_HEADER
+#include "gsm.h"
+#elif defined(HAVE_GSM_GSM_HEADER)
+#include <gsm/gsm.h>
+#endif
+
+#include "../formats/msgsm.h"
+
+/* Sample frame data */
+#include "slin_gsm_ex.h"
+#include "gsm_slin_ex.h"
+
+#define BUFFER_SAMPLES 8000
+#define GSM_SAMPLES 160
+#define GSM_FRAME_LEN 33
+#define MSGSM_FRAME_LEN 65
+
+struct gsm_translator_pvt { /* both gsm2lin and lin2gsm */
+ gsm gsm;
+ int16_t buf[BUFFER_SAMPLES]; /* lin2gsm, temporary storage */
+};
+
+static int gsm_new(struct ast_trans_pvt *pvt)
+{
+ struct gsm_translator_pvt *tmp = pvt->pvt;
+
+ return (tmp->gsm = gsm_create()) ? 0 : -1;
+}
+
+static struct ast_frame *lintogsm_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_gsm_ex);
+ /* Assume 8000 Hz */
+ f.samples = sizeof(slin_gsm_ex)/2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_gsm_ex;
+ return &f;
+}
+
+static struct ast_frame *gsmtolin_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_GSM;
+ f.datalen = sizeof(gsm_slin_ex);
+ /* All frames are 20 ms long */
+ f.samples = GSM_SAMPLES;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = gsm_slin_ex;
+ return &f;
+}
+
+/*! \brief decode and store in outbuf. */
+static int gsmtolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct gsm_translator_pvt *tmp = pvt->pvt;
+ int x;
+ int16_t *dst = (int16_t *)pvt->outbuf;
+ /* guess format from frame len. 65 for MSGSM, 33 for regular GSM */
+ int flen = (f->datalen % MSGSM_FRAME_LEN == 0) ?
+ MSGSM_FRAME_LEN : GSM_FRAME_LEN;
+
+ for (x=0; x < f->datalen; x += flen) {
+ unsigned char data[2 * GSM_FRAME_LEN];
+ unsigned char *src;
+ int len;
+ if (flen == MSGSM_FRAME_LEN) {
+ len = 2*GSM_SAMPLES;
+ src = data;
+ /* Translate MSGSM format to Real GSM format before feeding in */
+ /* XXX what's the point here! we should just work
+ * on the full format.
+ */
+ conv65(f->data + x, data);
+ } else {
+ len = GSM_SAMPLES;
+ src = f->data + x;
+ }
+ /* XXX maybe we don't need to check */
+ if (pvt->samples + len > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ if (gsm_decode(tmp->gsm, src, dst + pvt->samples)) {
+ ast_log(LOG_WARNING, "Invalid GSM data (1)\n");
+ return -1;
+ }
+ pvt->samples += GSM_SAMPLES;
+ pvt->datalen += 2 * GSM_SAMPLES;
+ if (flen == MSGSM_FRAME_LEN) {
+ if (gsm_decode(tmp->gsm, data + GSM_FRAME_LEN, dst + pvt->samples)) {
+ ast_log(LOG_WARNING, "Invalid GSM data (2)\n");
+ return -1;
+ }
+ pvt->samples += GSM_SAMPLES;
+ pvt->datalen += 2 * GSM_SAMPLES;
+ }
+ }
+ return 0;
+}
+
+/*! \brief store samples into working buffer for later decode */
+static int lintogsm_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct gsm_translator_pvt *tmp = pvt->pvt;
+
+ /* XXX We should look at how old the rest of our stream is, and if it
+ is too old, then we should overwrite it entirely, otherwise we can
+ get artifacts of earlier talk that do not belong */
+ if (pvt->samples + f->samples > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ memcpy(tmp->buf + pvt->samples, f->data, f->datalen);
+ pvt->samples += f->samples;
+ return 0;
+}
+
+/*! \brief encode and produce a frame */
+static struct ast_frame *lintogsm_frameout(struct ast_trans_pvt *pvt)
+{
+ struct gsm_translator_pvt *tmp = pvt->pvt;
+ int datalen = 0;
+ int samples = 0;
+
+ /* We can't work on anything less than a frame in size */
+ if (pvt->samples < GSM_SAMPLES)
+ return NULL;
+ while (pvt->samples >= GSM_SAMPLES) {
+ /* Encode a frame of data */
+ gsm_encode(tmp->gsm, tmp->buf + samples, (gsm_byte *) pvt->outbuf + datalen);
+ datalen += GSM_FRAME_LEN;
+ samples += GSM_SAMPLES;
+ pvt->samples -= GSM_SAMPLES;
+ }
+
+ /* Move the data at the end of the buffer to the front */
+ if (pvt->samples)
+ memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+
+ return ast_trans_frameout(pvt, datalen, samples);
+}
+
+static void gsm_destroy_stuff(struct ast_trans_pvt *pvt)
+{
+ struct gsm_translator_pvt *tmp = pvt->pvt;
+ if (tmp->gsm)
+ gsm_destroy(tmp->gsm);
+}
+
+static struct ast_translator gsmtolin = {
+ .name = "gsmtolin",
+ .srcfmt = AST_FORMAT_GSM,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = gsm_new,
+ .framein = gsmtolin_framein,
+ .destroy = gsm_destroy_stuff,
+ .sample = gsmtolin_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .desc_size = sizeof (struct gsm_translator_pvt ),
+ .plc_samples = GSM_SAMPLES,
+};
+
+static struct ast_translator lintogsm = {
+ .name = "lintogsm",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_GSM,
+ .newpvt = gsm_new,
+ .framein = lintogsm_framein,
+ .frameout = lintogsm_frameout,
+ .destroy = gsm_destroy_stuff,
+ .sample = lintogsm_sample,
+ .desc_size = sizeof (struct gsm_translator_pvt ),
+ .buf_size = (BUFFER_SAMPLES * GSM_FRAME_LEN + GSM_SAMPLES - 1)/GSM_SAMPLES,
+};
+
+
+static int parse_config(int reload)
+{
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ gsmtolin.useplc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "codec_gsm: %susing generic PLC\n", gsmtolin.useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+/*! \brief standard module glue */
+static int reload(void)
+{
+ if (parse_config(1)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintogsm);
+ if (!res)
+ res = ast_unregister_translator(&gsmtolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ res = ast_register_translator(&gsmtolin);
+ if (!res)
+ res=ast_register_translator(&lintogsm);
+ else
+ ast_unregister_translator(&gsmtolin);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "GSM Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_ilbc.c b/trunk/codecs/codec_ilbc.c
new file mode 100644
index 000000000..bee27938f
--- /dev/null
+++ b/trunk/codecs/codec_ilbc.c
@@ -0,0 +1,235 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * The iLBC code is from The IETF code base and is copyright The Internet Society (2004)
+ *
+ * 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 Translate between signed linear and Internet Low Bitrate Codec
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+
+#include "ilbc/iLBC_encode.h"
+#include "ilbc/iLBC_decode.h"
+
+/* Sample frame data */
+#include "slin_ilbc_ex.h"
+#include "ilbc_slin_ex.h"
+
+#define USE_ILBC_ENHANCER 0
+#define ILBC_MS 30
+/* #define ILBC_MS 20 */
+
+#define ILBC_FRAME_LEN 50 /* apparently... */
+#define ILBC_SAMPLES 240 /* 30ms at 8000 hz */
+#define BUFFER_SAMPLES 8000
+
+struct ilbc_coder_pvt {
+ iLBC_Enc_Inst_t enc;
+ iLBC_Dec_Inst_t dec;
+ /* Enough to store a full second */
+ int16_t buf[BUFFER_SAMPLES];
+};
+
+static int lintoilbc_new(struct ast_trans_pvt *pvt)
+{
+ struct ilbc_coder_pvt *tmp = pvt->pvt;
+
+ initEncode(&tmp->enc, ILBC_MS);
+
+ return 0;
+}
+
+static int ilbctolin_new(struct ast_trans_pvt *pvt)
+{
+ struct ilbc_coder_pvt *tmp = pvt->pvt;
+
+ initDecode(&tmp->dec, ILBC_MS, USE_ILBC_ENHANCER);
+
+ return 0;
+}
+
+static struct ast_frame *lintoilbc_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_ilbc_ex);
+ f.samples = sizeof(slin_ilbc_ex)/2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_ilbc_ex;
+ return &f;
+}
+
+static struct ast_frame *ilbctolin_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_ILBC;
+ f.datalen = sizeof(ilbc_slin_ex);
+ /* All frames are 30 ms long */
+ f.samples = ILBC_SAMPLES;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = ilbc_slin_ex;
+ return &f;
+}
+
+/*! \brief decode a frame and store in outbuf */
+static int ilbctolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct ilbc_coder_pvt *tmp = pvt->pvt;
+ int plc_mode = 1; /* 1 = normal data, 0 = plc */
+ /* Assuming there's space left, decode into the current buffer at
+ the tail location. Read in as many frames as there are */
+ int x,i;
+ int16_t *dst = (int16_t *)pvt->outbuf;
+ float tmpf[ILBC_SAMPLES];
+
+ if (f->datalen == 0) { /* native PLC, set fake f->datalen and clear plc_mode */
+ f->datalen = ILBC_FRAME_LEN;
+ f->samples = ILBC_SAMPLES;
+ plc_mode = 0; /* do native plc */
+ pvt->samples += ILBC_SAMPLES;
+ }
+
+ if (f->datalen % ILBC_FRAME_LEN) {
+ ast_log(LOG_WARNING, "Huh? An ilbc frame that isn't a multiple of 50 bytes long from %s (%d)?\n", f->src, f->datalen);
+ return -1;
+ }
+
+ for (x=0; x < f->datalen ; x += ILBC_FRAME_LEN) {
+ if (pvt->samples + ILBC_SAMPLES > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ iLBC_decode(tmpf, plc_mode ? f->data + x : NULL, &tmp->dec, plc_mode);
+ for ( i=0; i < ILBC_SAMPLES; i++)
+ dst[pvt->samples + i] = tmpf[i];
+ pvt->samples += ILBC_SAMPLES;
+ pvt->datalen += 2*ILBC_SAMPLES;
+ }
+ return 0;
+}
+
+/*! \brief store a frame into a temporary buffer, for later decoding */
+static int lintoilbc_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct ilbc_coder_pvt *tmp = pvt->pvt;
+
+ /* Just add the frames to our stream */
+ /* XXX We should look at how old the rest of our stream is, and if it
+ is too old, then we should overwrite it entirely, otherwise we can
+ get artifacts of earlier talk that do not belong */
+ memcpy(tmp->buf + pvt->samples, f->data, f->datalen);
+ pvt->samples += f->samples;
+ return 0;
+}
+
+/*! \brief encode the temporary buffer and generate a frame */
+static struct ast_frame *lintoilbc_frameout(struct ast_trans_pvt *pvt)
+{
+ struct ilbc_coder_pvt *tmp = pvt->pvt;
+ int datalen = 0;
+ int samples = 0;
+
+ /* We can't work on anything less than a frame in size */
+ if (pvt->samples < ILBC_SAMPLES)
+ return NULL;
+ while (pvt->samples >= ILBC_SAMPLES) {
+ float tmpf[ILBC_SAMPLES];
+ int i;
+
+ /* Encode a frame of data */
+ for (i = 0 ; i < ILBC_SAMPLES ; i++)
+ tmpf[i] = tmp->buf[samples + i];
+ iLBC_encode((unsigned char *) pvt->outbuf + datalen, tmpf, &tmp->enc);
+
+ datalen += ILBC_FRAME_LEN;
+ samples += ILBC_SAMPLES;
+ pvt->samples -= ILBC_SAMPLES;
+ }
+
+ /* Move the data at the end of the buffer to the front */
+ if (pvt->samples)
+ memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+
+ return ast_trans_frameout(pvt, datalen, samples);
+}
+
+static struct ast_translator ilbctolin = {
+ .name = "ilbctolin",
+ .srcfmt = AST_FORMAT_ILBC,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = ilbctolin_new,
+ .framein = ilbctolin_framein,
+ .sample = ilbctolin_sample,
+ .desc_size = sizeof(struct ilbc_coder_pvt),
+ .buf_size = BUFFER_SAMPLES * 2,
+ .native_plc = 1,
+};
+
+static struct ast_translator lintoilbc = {
+ .name = "lintoilbc",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_ILBC,
+ .newpvt = lintoilbc_new,
+ .framein = lintoilbc_framein,
+ .frameout = lintoilbc_frameout,
+ .sample = lintoilbc_sample,
+ .desc_size = sizeof(struct ilbc_coder_pvt),
+ .buf_size = (BUFFER_SAMPLES * ILBC_FRAME_LEN + ILBC_SAMPLES - 1) / ILBC_SAMPLES,
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintoilbc);
+ res |= ast_unregister_translator(&ilbctolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_translator(&ilbctolin);
+ if (!res)
+ res=ast_register_translator(&lintoilbc);
+ else
+ ast_unregister_translator(&ilbctolin);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "iLBC Coder/Decoder");
diff --git a/trunk/codecs/codec_lpc10.c b/trunk/codecs/codec_lpc10.c
new file mode 100644
index 000000000..1e4ec37f3
--- /dev/null
+++ b/trunk/codecs/codec_lpc10.c
@@ -0,0 +1,311 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * The lpc10 code is from a library used by nautilus, modified to be a bit
+ * nicer to the compiler.
+ * See http://www.arl.wustl.edu/~jaf/
+ *
+ * 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 Translate between signed linear and LPC10 (Linear Predictor Code)
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/translate.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+
+#include "lpc10/lpc10.h"
+
+/* Sample frame data */
+#include "slin_lpc10_ex.h"
+#include "lpc10_slin_ex.h"
+
+/* We use a very strange format here... I have no idea why... The frames are 180
+ samples long, which isn't even an even number of milliseconds... Not only that
+ but we hvae to waste two bits of each frame to keep them ending on a byte boundary
+ because the frames are 54 bits long */
+
+#define LPC10_BYTES_IN_COMPRESSED_FRAME (LPC10_BITS_IN_COMPRESSED_FRAME + 7)/8
+
+#define BUFFER_SAMPLES 8000
+
+struct lpc10_coder_pvt {
+ union {
+ struct lpc10_encoder_state *enc;
+ struct lpc10_decoder_state *dec;
+ } lpc10;
+ /* Enough to store a full second */
+ short buf[BUFFER_SAMPLES];
+ int longer;
+};
+
+static int lpc10_enc_new(struct ast_trans_pvt *pvt)
+{
+ struct lpc10_coder_pvt *tmp = pvt->pvt;
+
+ return (tmp->lpc10.enc = create_lpc10_encoder_state()) ? 0 : -1;
+}
+
+static int lpc10_dec_new(struct ast_trans_pvt *pvt)
+{
+ struct lpc10_coder_pvt *tmp = pvt->pvt;
+
+ return (tmp->lpc10.dec = create_lpc10_decoder_state()) ? 0 : -1;
+}
+
+static struct ast_frame *lintolpc10_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_lpc10_ex);
+ /* Assume 8000 Hz */
+ f.samples = LPC10_SAMPLES_PER_FRAME;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_lpc10_ex;
+ return &f;
+}
+
+static struct ast_frame *lpc10tolin_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_LPC10;
+ f.datalen = sizeof(lpc10_slin_ex);
+ /* All frames are 22 ms long (maybe a little more -- why did he choose
+ LPC10_SAMPLES_PER_FRAME sample frames anyway?? */
+ f.samples = LPC10_SAMPLES_PER_FRAME;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = lpc10_slin_ex;
+ return &f;
+}
+
+static void extract_bits(INT32 *bits, unsigned char *c)
+{
+ int x;
+ for (x=0;x<LPC10_BITS_IN_COMPRESSED_FRAME;x++) {
+ if (*c & (0x80 >> (x & 7)))
+ bits[x] = 1;
+ else
+ bits[x] = 0;
+ if ((x & 7) == 7)
+ c++;
+ }
+}
+
+/* XXX note lpc10_encode() produces one bit per word in bits[] */
+static void build_bits(unsigned char *c, INT32 *bits)
+{
+ unsigned char mask=0x80;
+ int x;
+ *c = 0;
+ for (x=0;x<LPC10_BITS_IN_COMPRESSED_FRAME;x++) {
+ if (bits[x])
+ *c |= mask;
+ mask = mask >> 1;
+ if ((x % 8)==7) {
+ c++;
+ *c = 0;
+ mask = 0x80;
+ }
+ }
+}
+
+static int lpc10tolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct lpc10_coder_pvt *tmp = pvt->pvt;
+ int16_t *dst = (int16_t *)pvt->outbuf;
+ int len = 0;
+
+ while (len + LPC10_BYTES_IN_COMPRESSED_FRAME <= f->datalen) {
+ int x;
+ float tmpbuf[LPC10_SAMPLES_PER_FRAME];
+ INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME]; /* XXX see note */
+ if (pvt->samples + LPC10_SAMPLES_PER_FRAME > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ extract_bits(bits, f->data + len);
+ if (lpc10_decode(bits, tmpbuf, tmp->lpc10.dec)) {
+ ast_log(LOG_WARNING, "Invalid lpc10 data\n");
+ return -1;
+ }
+ for (x=0;x<LPC10_SAMPLES_PER_FRAME;x++) {
+ /* Convert to a short between -1.0 and 1.0 */
+ dst[pvt->samples + x] = (int16_t)(32768.0 * tmpbuf[x]);
+ }
+
+ pvt->samples += LPC10_SAMPLES_PER_FRAME;
+ pvt->datalen += 2*LPC10_SAMPLES_PER_FRAME;
+ len += LPC10_BYTES_IN_COMPRESSED_FRAME;
+ }
+ if (len != f->datalen)
+ printf("Decoded %d, expected %d\n", len, f->datalen);
+ return 0;
+}
+
+static int lintolpc10_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct lpc10_coder_pvt *tmp = pvt->pvt;
+
+ /* Just add the frames to our stream */
+ if (pvt->samples + f->samples > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ memcpy(tmp->buf + pvt->samples, f->data, f->datalen);
+ pvt->samples += f->samples;
+ return 0;
+}
+
+static struct ast_frame *lintolpc10_frameout(struct ast_trans_pvt *pvt)
+{
+ struct lpc10_coder_pvt *tmp = pvt->pvt;
+ int x;
+ int datalen = 0; /* output frame */
+ int samples = 0; /* output samples */
+ float tmpbuf[LPC10_SAMPLES_PER_FRAME];
+ INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME]; /* XXX what ??? */
+ /* We can't work on anything less than a frame in size */
+ if (pvt->samples < LPC10_SAMPLES_PER_FRAME)
+ return NULL;
+ while (pvt->samples >= LPC10_SAMPLES_PER_FRAME) {
+ /* Encode a frame of data */
+ for (x=0;x<LPC10_SAMPLES_PER_FRAME;x++)
+ tmpbuf[x] = (float)tmp->buf[x + samples] / 32768.0;
+ lpc10_encode(tmpbuf, bits, tmp->lpc10.enc);
+ build_bits((unsigned char *) pvt->outbuf + datalen, bits);
+ datalen += LPC10_BYTES_IN_COMPRESSED_FRAME;
+ samples += LPC10_SAMPLES_PER_FRAME;
+ pvt->samples -= LPC10_SAMPLES_PER_FRAME;
+ /* Use one of the two left over bits to record if this is a 22 or 23 ms frame...
+ important for IAX use */
+ tmp->longer = 1 - tmp->longer;
+ }
+ /* Move the data at the end of the buffer to the front */
+ if (pvt->samples)
+ memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+ return ast_trans_frameout(pvt, datalen, samples);
+}
+
+
+static void lpc10_destroy(struct ast_trans_pvt *arg)
+{
+ struct lpc10_coder_pvt *pvt = arg->pvt;
+ /* Enc and DEC are both just allocated, so they can be freed */
+ ast_free(pvt->lpc10.enc);
+}
+
+static struct ast_translator lpc10tolin = {
+ .name = "lpc10tolin",
+ .srcfmt = AST_FORMAT_LPC10,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = lpc10_dec_new,
+ .framein = lpc10tolin_framein,
+ .destroy = lpc10_destroy,
+ .sample = lpc10tolin_sample,
+ .desc_size = sizeof(struct lpc10_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .plc_samples = LPC10_SAMPLES_PER_FRAME,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator lintolpc10 = {
+ .name = "lintolpc10",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_LPC10,
+ .newpvt = lpc10_enc_new,
+ .framein = lintolpc10_framein,
+ .frameout = lintolpc10_frameout,
+ .destroy = lpc10_destroy,
+ .sample = lintolpc10_sample,
+ .desc_size = sizeof(struct lpc10_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = LPC10_BYTES_IN_COMPRESSED_FRAME * (1 + BUFFER_SAMPLES / LPC10_SAMPLES_PER_FRAME),
+};
+
+static int parse_config(int reload)
+{
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ lpc10tolin.useplc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "codec_lpc10: %susing generic PLC\n",
+ lpc10tolin.useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+static int reload(void)
+{
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintolpc10);
+ res |= ast_unregister_translator(&lpc10tolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ res = ast_register_translator(&lpc10tolin);
+ if (!res)
+ res = ast_register_translator(&lintolpc10);
+ else
+ ast_unregister_translator(&lpc10tolin);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "LPC10 2.4kbps Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_resample.c b/trunk/codecs/codec_resample.c
new file mode 100644
index 000000000..251d99c23
--- /dev/null
+++ b/trunk/codecs/codec_resample.c
@@ -0,0 +1,237 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Russell Bryant <russell@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 Resample slinear audio
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+/* These are for SHRT_MAX and FLT_MAX -- { */
+#if defined(__Darwin__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
+#include <float.h>
+#else
+#include <values.h>
+#endif
+#include <limits.h>
+/* } */
+
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/libresample.h"
+
+#include "slin_resample_ex.h"
+
+#define RESAMPLER_QUALITY 0
+
+#define OUTBUF_SIZE 8096
+
+struct slin16_to_slin8_pvt {
+ void *resampler;
+ float resample_factor;
+};
+
+struct slin8_to_slin16_pvt {
+ void *resampler;
+ float resample_factor;
+};
+
+static int slin16_to_slin8_new(struct ast_trans_pvt *pvt)
+{
+ struct slin16_to_slin8_pvt *resamp_pvt = pvt->pvt;
+
+ resamp_pvt->resample_factor = 0.5;
+
+ if (!(resamp_pvt->resampler = resample_open(RESAMPLER_QUALITY, 0.5, 0.5)))
+ return -1;
+
+ return 0;
+}
+
+static int slin8_to_slin16_new(struct ast_trans_pvt *pvt)
+{
+ struct slin8_to_slin16_pvt *resamp_pvt = pvt->pvt;
+
+ resamp_pvt->resample_factor = 2.0;
+
+ if (!(resamp_pvt->resampler = resample_open(RESAMPLER_QUALITY, 2.0, 2.0)))
+ return -1;
+
+ return 0;
+}
+
+static void slin16_to_slin8_destroy(struct ast_trans_pvt *pvt)
+{
+ struct slin16_to_slin8_pvt *resamp_pvt = pvt->pvt;
+
+ if (resamp_pvt->resampler)
+ resample_close(resamp_pvt->resampler);
+}
+
+static void slin8_to_slin16_destroy(struct ast_trans_pvt *pvt)
+{
+ struct slin8_to_slin16_pvt *resamp_pvt = pvt->pvt;
+
+ if (resamp_pvt->resampler)
+ resample_close(resamp_pvt->resampler);
+}
+
+static int resample_frame(struct ast_trans_pvt *pvt,
+ void *resampler, float resample_factor, struct ast_frame *f)
+{
+ int total_in_buf_used = 0;
+ int total_out_buf_used = 0;
+ int16_t *in_buf = (int16_t *) f->data;
+ int16_t *out_buf = (int16_t *) pvt->outbuf + pvt->samples;
+ float in_buf_f[f->samples];
+ float out_buf_f[2048];
+ int res = 0;
+ int i;
+
+ for (i = 0; i < f->samples; i++)
+ in_buf_f[i] = in_buf[i] * (FLT_MAX / SHRT_MAX);
+
+ while (total_in_buf_used < f->samples) {
+ int in_buf_used, out_buf_used;
+
+ out_buf_used = resample_process(resampler, resample_factor,
+ &in_buf_f[total_in_buf_used], f->samples - total_in_buf_used,
+ 0, &in_buf_used,
+ &out_buf_f[total_out_buf_used], ARRAY_LEN(out_buf_f) - total_out_buf_used);
+
+ if (out_buf_used < 0)
+ break;
+
+ total_out_buf_used += out_buf_used;
+ total_in_buf_used += in_buf_used;
+
+ if (total_out_buf_used == ARRAY_LEN(out_buf_f)) {
+ ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size\n");
+ res = -1;
+ break;
+ }
+ }
+
+ for (i = 0; i < total_out_buf_used; i++)
+ out_buf[i] = out_buf_f[i] * (SHRT_MAX / FLT_MAX);
+
+ pvt->samples += total_out_buf_used;
+ pvt->datalen += (total_out_buf_used * sizeof(int16_t));
+
+ return res;
+}
+
+static int slin16_to_slin8_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct slin16_to_slin8_pvt *resamp_pvt = pvt->pvt;
+ void *resampler = resamp_pvt->resampler;
+ float resample_factor = resamp_pvt->resample_factor;
+
+ return resample_frame(pvt, resampler, resample_factor, f);
+}
+
+static int slin8_to_slin16_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct slin8_to_slin16_pvt *resamp_pvt = pvt->pvt;
+ void *resampler = resamp_pvt->resampler;
+ float resample_factor = resamp_pvt->resample_factor;
+
+ return resample_frame(pvt, resampler, resample_factor, f);
+}
+
+static struct ast_frame *slin16_to_slin8_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR16,
+ .datalen = sizeof(slin16_slin8_ex),
+ .samples = sizeof(slin16_slin8_ex) / sizeof(slin16_slin8_ex[0]),
+ .src = __PRETTY_FUNCTION__,
+ .data = slin16_slin8_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_frame *slin8_to_slin16_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .datalen = sizeof(slin8_slin16_ex),
+ .samples = sizeof(slin8_slin16_ex) / sizeof(slin8_slin16_ex[0]),
+ .src = __PRETTY_FUNCTION__,
+ .data = slin8_slin16_ex,
+ };
+
+ return &f;
+}
+
+static struct ast_translator slin16_to_slin8 = {
+ .name = "slin16_to_slin8",
+ .srcfmt = AST_FORMAT_SLINEAR16,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = slin16_to_slin8_new,
+ .destroy = slin16_to_slin8_destroy,
+ .framein = slin16_to_slin8_framein,
+ .sample = slin16_to_slin8_sample,
+ .desc_size = sizeof(struct slin16_to_slin8_pvt),
+ .buffer_samples = (OUTBUF_SIZE / sizeof(int16_t)),
+ .buf_size = OUTBUF_SIZE,
+};
+
+static struct ast_translator slin8_to_slin16 = {
+ .name = "slin8_to_slin16",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_SLINEAR16,
+ .newpvt = slin8_to_slin16_new,
+ .destroy = slin8_to_slin16_destroy,
+ .framein = slin8_to_slin16_framein,
+ .sample = slin8_to_slin16_sample,
+ .desc_size = sizeof(struct slin8_to_slin16_pvt),
+ .buffer_samples = (OUTBUF_SIZE / sizeof(int16_t)),
+ .buf_size = OUTBUF_SIZE,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_unregister_translator(&slin16_to_slin8);
+ res |= ast_unregister_translator(&slin8_to_slin16);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_register_translator(&slin16_to_slin8);
+ res |= ast_register_translator(&slin8_to_slin16);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SLIN Resampling Codec");
diff --git a/trunk/codecs/codec_speex.c b/trunk/codecs/codec_speex.c
new file mode 100644
index 000000000..035f9958b
--- /dev/null
+++ b/trunk/codecs/codec_speex.c
@@ -0,0 +1,503 @@
+/*
+ * 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 Translate between signed linear and Speex (Open Codec)
+ *
+ * \note This work was motivated by Jeremy McNamara
+ * hacked to be configurable by anthm and bkw 9/28/2004
+ *
+ * \ingroup codecs
+ *
+ * \extref The Speex library - http://www.speex.org
+ *
+ */
+
+/*** MODULEINFO
+ <depend>speex</depend>
+ <use>speexdsp</use>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <speex/speex.h>
+
+/* We require a post 1.1.8 version of Speex to enable preprocessing
+ and better type handling */
+#ifdef _SPEEX_TYPES_H
+#include <speex/speex_preprocess.h>
+#endif
+
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+
+/* Sample frame data */
+#include "slin_speex_ex.h"
+#include "speex_slin_ex.h"
+
+/* codec variables */
+static int quality = 3;
+static int complexity = 2;
+static int enhancement = 0;
+static int vad = 0;
+static int vbr = 0;
+static float vbr_quality = 4;
+static int abr = 0;
+static int dtx = 0; /* set to 1 to enable silence detection */
+
+static int preproc = 0;
+static int pp_vad = 0;
+static int pp_agc = 0;
+static float pp_agc_level = 8000; /* XXX what is this 8000 ? */
+static int pp_denoise = 0;
+static int pp_dereverb = 0;
+static float pp_dereverb_decay = 0.4;
+static float pp_dereverb_level = 0.3;
+
+#define TYPE_SILENCE 0x2
+#define TYPE_HIGH 0x0
+#define TYPE_LOW 0x1
+#define TYPE_MASK 0x3
+
+#define BUFFER_SAMPLES 8000
+#define SPEEX_SAMPLES 160
+
+struct speex_coder_pvt {
+ void *speex;
+ SpeexBits bits;
+ int framesize;
+ int silent_state;
+#ifdef _SPEEX_TYPES_H
+ SpeexPreprocessState *pp;
+ spx_int16_t buf[BUFFER_SAMPLES];
+#else
+ int16_t buf[BUFFER_SAMPLES]; /* input, waiting to be compressed */
+#endif
+};
+
+
+static int lintospeex_new(struct ast_trans_pvt *pvt)
+{
+ struct speex_coder_pvt *tmp = pvt->pvt;
+
+ if (!(tmp->speex = speex_encoder_init(&speex_nb_mode)))
+ return -1;
+
+ speex_bits_init(&tmp->bits);
+ speex_bits_reset(&tmp->bits);
+ speex_encoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_COMPLEXITY, &complexity);
+#ifdef _SPEEX_TYPES_H
+ if (preproc) {
+ tmp->pp = speex_preprocess_state_init(tmp->framesize, 8000); /* XXX what is this 8000 ? */
+ speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_VAD, &pp_vad);
+ speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC, &pp_agc);
+ speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &pp_agc_level);
+ speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DENOISE, &pp_denoise);
+ speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB, &pp_dereverb);
+ speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &pp_dereverb_decay);
+ speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &pp_dereverb_level);
+ }
+#endif
+ if (!abr && !vbr) {
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_QUALITY, &quality);
+ if (vad)
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_VAD, &vad);
+ }
+ if (vbr) {
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR, &vbr);
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR_QUALITY, &vbr_quality);
+ }
+ if (abr)
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_ABR, &abr);
+ if (dtx)
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_DTX, &dtx);
+ tmp->silent_state = 0;
+
+ return 0;
+}
+
+static int speextolin_new(struct ast_trans_pvt *pvt)
+{
+ struct speex_coder_pvt *tmp = pvt->pvt;
+
+ if (!(tmp->speex = speex_decoder_init(&speex_nb_mode)))
+ return -1;
+
+ speex_bits_init(&tmp->bits);
+ speex_decoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
+ if (enhancement)
+ speex_decoder_ctl(tmp->speex, SPEEX_SET_ENH, &enhancement);
+
+ return 0;
+}
+
+static struct ast_frame *lintospeex_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_speex_ex);
+ /* Assume 8000 Hz */
+ f.samples = sizeof(slin_speex_ex)/2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_speex_ex;
+ return &f;
+}
+
+static struct ast_frame *speextolin_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SPEEX;
+ f.datalen = sizeof(speex_slin_ex);
+ /* All frames are 20 ms long */
+ f.samples = SPEEX_SAMPLES;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = speex_slin_ex;
+ return &f;
+}
+
+/*! \brief convert and store into outbuf */
+static int speextolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct speex_coder_pvt *tmp = pvt->pvt;
+
+ /* Assuming there's space left, decode into the current buffer at
+ the tail location. Read in as many frames as there are */
+ int x;
+ int res;
+ int16_t *dst = (int16_t *)pvt->outbuf;
+ /* XXX fout is a temporary buffer, may have different types */
+#ifdef _SPEEX_TYPES_H
+ spx_int16_t fout[1024];
+#else
+ float fout[1024];
+#endif
+
+ if (f->datalen == 0) { /* Native PLC interpolation */
+ if (pvt->samples + tmp->framesize > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+#ifdef _SPEEX_TYPES_H
+ speex_decode_int(tmp->speex, NULL, dst + pvt->samples);
+#else
+ speex_decode(tmp->speex, NULL, fout);
+ for (x=0;x<tmp->framesize;x++) {
+ dst[pvt->samples + x] = (int16_t)fout[x];
+ }
+#endif
+ pvt->samples += tmp->framesize;
+ pvt->datalen += 2 * tmp->framesize; /* 2 bytes/sample */
+ return 0;
+ }
+
+ /* Read in bits */
+ speex_bits_read_from(&tmp->bits, f->data, f->datalen);
+ for (;;) {
+#ifdef _SPEEX_TYPES_H
+ res = speex_decode_int(tmp->speex, &tmp->bits, fout);
+#else
+ res = speex_decode(tmp->speex, &tmp->bits, fout);
+#endif
+ if (res < 0)
+ break;
+ if (pvt->samples + tmp->framesize > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ for (x = 0 ; x < tmp->framesize; x++)
+ dst[pvt->samples + x] = (int16_t)fout[x];
+ pvt->samples += tmp->framesize;
+ pvt->datalen += 2 * tmp->framesize; /* 2 bytes/sample */
+ }
+ return 0;
+}
+
+/*! \brief store input frame in work buffer */
+static int lintospeex_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct speex_coder_pvt *tmp = pvt->pvt;
+
+ /* XXX We should look at how old the rest of our stream is, and if it
+ is too old, then we should overwrite it entirely, otherwise we can
+ get artifacts of earlier talk that do not belong */
+ memcpy(tmp->buf + pvt->samples, f->data, f->datalen);
+ pvt->samples += f->samples;
+ return 0;
+}
+
+/*! \brief convert work buffer and produce output frame */
+static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
+{
+ struct speex_coder_pvt *tmp = pvt->pvt;
+ int is_speech=1;
+ int datalen = 0; /* output bytes */
+ int samples = 0; /* output samples */
+
+ /* We can't work on anything less than a frame in size */
+ if (pvt->samples < tmp->framesize)
+ return NULL;
+ speex_bits_reset(&tmp->bits);
+ while (pvt->samples >= tmp->framesize) {
+#ifdef _SPEEX_TYPES_H
+ /* Preprocess audio */
+ if (preproc)
+ is_speech = speex_preprocess(tmp->pp, tmp->buf + samples, NULL);
+ /* Encode a frame of data */
+ if (is_speech) {
+ /* If DTX enabled speex_encode returns 0 during silence */
+ is_speech = speex_encode_int(tmp->speex, tmp->buf + samples, &tmp->bits) || !dtx;
+ } else {
+ /* 5 zeros interpreted by Speex as silence (submode 0) */
+ speex_bits_pack(&tmp->bits, 0, 5);
+ }
+#else
+ {
+ float fbuf[1024];
+ int x;
+ /* Convert to floating point */
+ for (x = 0; x < tmp->framesize; x++)
+ fbuf[x] = tmp->buf[samples + x];
+ /* Encode a frame of data */
+ is_speech = speex_encode(tmp->speex, fbuf, &tmp->bits) || !dtx;
+ }
+#endif
+ samples += tmp->framesize;
+ pvt->samples -= tmp->framesize;
+ }
+
+ /* Move the data at the end of the buffer to the front */
+ if (pvt->samples)
+ memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+
+ /* Use AST_FRAME_CNG to signify the start of any silence period */
+ if (is_speech) {
+ tmp->silent_state = 0;
+ } else {
+ if (tmp->silent_state) {
+ return NULL;
+ } else {
+ tmp->silent_state = 1;
+ speex_bits_reset(&tmp->bits);
+ memset(&pvt->f, 0, sizeof(pvt->f));
+ pvt->f.frametype = AST_FRAME_CNG;
+ pvt->f.samples = samples;
+ /* XXX what now ? format etc... */
+ }
+ }
+
+ /* Terminate bit stream */
+ speex_bits_pack(&tmp->bits, 15, 5);
+ datalen = speex_bits_write(&tmp->bits, pvt->outbuf, pvt->t->buf_size);
+ return ast_trans_frameout(pvt, datalen, samples);
+}
+
+static void speextolin_destroy(struct ast_trans_pvt *arg)
+{
+ struct speex_coder_pvt *pvt = arg->pvt;
+
+ speex_decoder_destroy(pvt->speex);
+ speex_bits_destroy(&pvt->bits);
+}
+
+static void lintospeex_destroy(struct ast_trans_pvt *arg)
+{
+ struct speex_coder_pvt *pvt = arg->pvt;
+#ifdef _SPEEX_TYPES_H
+ if (preproc)
+ speex_preprocess_state_destroy(pvt->pp);
+#endif
+ speex_encoder_destroy(pvt->speex);
+ speex_bits_destroy(&pvt->bits);
+}
+
+static struct ast_translator speextolin = {
+ .name = "speextolin",
+ .srcfmt = AST_FORMAT_SPEEX,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .newpvt = speextolin_new,
+ .framein = speextolin_framein,
+ .destroy = speextolin_destroy,
+ .sample = speextolin_sample,
+ .desc_size = sizeof(struct speex_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .native_plc = 1,
+};
+
+static struct ast_translator lintospeex = {
+ .name = "lintospeex",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_SPEEX,
+ .newpvt = lintospeex_new,
+ .framein = lintospeex_framein,
+ .frameout = lintospeex_frameout,
+ .destroy = lintospeex_destroy,
+ .sample = lintospeex_sample,
+ .desc_size = sizeof(struct speex_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */
+};
+
+static int parse_config(int reload)
+{
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+ struct ast_variable *var;
+ int res;
+ float res_f;
+
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ for (var = ast_variable_browse(cfg, "speex"); var; var = var->next) {
+ if (!strcasecmp(var->name, "quality")) {
+ res = abs(atoi(var->value));
+ if (res > -1 && res < 11) {
+ ast_verb(3, "CODEC SPEEX: Setting Quality to %d\n",res);
+ quality = res;
+ } else
+ ast_log(LOG_ERROR,"Error Quality must be 0-10\n");
+ } else if (!strcasecmp(var->name, "complexity")) {
+ res = abs(atoi(var->value));
+ if (res > -1 && res < 11) {
+ ast_verb(3, "CODEC SPEEX: Setting Complexity to %d\n",res);
+ complexity = res;
+ } else
+ ast_log(LOG_ERROR,"Error! Complexity must be 0-10\n");
+ } else if (!strcasecmp(var->name, "vbr_quality")) {
+ if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0 && res_f <= 10) {
+ ast_verb(3, "CODEC SPEEX: Setting VBR Quality to %f\n",res_f);
+ vbr_quality = res_f;
+ } else
+ ast_log(LOG_ERROR,"Error! VBR Quality must be 0-10\n");
+ } else if (!strcasecmp(var->name, "abr_quality")) {
+ ast_log(LOG_ERROR,"Error! ABR Quality setting obsolete, set ABR to desired bitrate\n");
+ } else if (!strcasecmp(var->name, "enhancement")) {
+ enhancement = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: Perceptual Enhancement Mode. [%s]\n",enhancement ? "on" : "off");
+ } else if (!strcasecmp(var->name, "vbr")) {
+ vbr = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: VBR Mode. [%s]\n",vbr ? "on" : "off");
+ } else if (!strcasecmp(var->name, "abr")) {
+ res = abs(atoi(var->value));
+ if (res >= 0) {
+ if (res > 0)
+ ast_verb(3, "CODEC SPEEX: Setting ABR target bitrate to %d\n",res);
+ else
+ ast_verb(3, "CODEC SPEEX: Disabling ABR\n");
+ abr = res;
+ } else
+ ast_log(LOG_ERROR,"Error! ABR target bitrate must be >= 0\n");
+ } else if (!strcasecmp(var->name, "vad")) {
+ vad = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: VAD Mode. [%s]\n",vad ? "on" : "off");
+ } else if (!strcasecmp(var->name, "dtx")) {
+ dtx = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: DTX Mode. [%s]\n",dtx ? "on" : "off");
+ } else if (!strcasecmp(var->name, "preprocess")) {
+ preproc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: Preprocessing. [%s]\n",preproc ? "on" : "off");
+ } else if (!strcasecmp(var->name, "pp_vad")) {
+ pp_vad = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: Preprocessor VAD. [%s]\n",pp_vad ? "on" : "off");
+ } else if (!strcasecmp(var->name, "pp_agc")) {
+ pp_agc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: Preprocessor AGC. [%s]\n",pp_agc ? "on" : "off");
+ } else if (!strcasecmp(var->name, "pp_agc_level")) {
+ if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0) {
+ ast_verb(3, "CODEC SPEEX: Setting preprocessor AGC Level to %f\n",res_f);
+ pp_agc_level = res_f;
+ } else
+ ast_log(LOG_ERROR,"Error! Preprocessor AGC Level must be >= 0\n");
+ } else if (!strcasecmp(var->name, "pp_denoise")) {
+ pp_denoise = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: Preprocessor Denoise. [%s]\n",pp_denoise ? "on" : "off");
+ } else if (!strcasecmp(var->name, "pp_dereverb")) {
+ pp_dereverb = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: Preprocessor Dereverb. [%s]\n",pp_dereverb ? "on" : "off");
+ } else if (!strcasecmp(var->name, "pp_dereverb_decay")) {
+ if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0) {
+ ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Decay to %f\n",res_f);
+ pp_dereverb_decay = res_f;
+ } else
+ ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Decay must be >= 0\n");
+ } else if (!strcasecmp(var->name, "pp_dereverb_level")) {
+ if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0) {
+ ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Level to %f\n",res_f);
+ pp_dereverb_level = res_f;
+ } else
+ ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Level must be >= 0\n");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+static int reload(void)
+{
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintospeex);
+ res |= ast_unregister_translator(&speextolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ res=ast_register_translator(&speextolin);
+ if (!res)
+ res=ast_register_translator(&lintospeex);
+ else
+ ast_unregister_translator(&speextolin);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Speex Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_ulaw.c b/trunk/codecs/codec_ulaw.c
new file mode 100644
index 000000000..ebd1928a2
--- /dev/null
+++ b/trunk/codecs/codec_ulaw.c
@@ -0,0 +1,195 @@
+/*
+ * 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 codec_ulaw.c - translate between signed linear and ulaw
+ *
+ * \ingroup codecs
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/utils.h"
+
+#define BUFFER_SAMPLES 8096 /* size for the translation buffers */
+
+/* Sample frame data */
+
+#include "slin_ulaw_ex.h"
+#include "ulaw_slin_ex.h"
+
+/*! \brief convert and store samples in outbuf */
+static int ulawtolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int i = f->samples;
+ unsigned char *src = f->data;
+ int16_t *dst = (int16_t *)pvt->outbuf + pvt->samples;
+
+ pvt->samples += i;
+ pvt->datalen += i * 2; /* 2 bytes/sample */
+
+ /* convert and copy in outbuf */
+ while (i--)
+ *dst++ = AST_MULAW(*src++);
+
+ return 0;
+}
+
+/*! \brief convert and store samples in outbuf */
+static int lintoulaw_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int i = f->samples;
+ char *dst = pvt->outbuf + pvt->samples;
+ int16_t *src = f->data;
+
+ pvt->samples += i;
+ pvt->datalen += i; /* 1 byte/sample */
+
+ while (i--)
+ *dst++ = AST_LIN2MU(*src++);
+
+ return 0;
+}
+
+/*! * \brief ulawToLin_Sample */
+static struct ast_frame *ulawtolin_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_ULAW;
+ f.datalen = sizeof(ulaw_slin_ex);
+ f.samples = sizeof(ulaw_slin_ex);
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = ulaw_slin_ex;
+ return &f;
+}
+
+/*!
+ * \brief LinToulaw_Sample
+ */
+
+static struct ast_frame *lintoulaw_sample(void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_ulaw_ex);
+ /* Assume 8000 Hz */
+ f.samples = sizeof(slin_ulaw_ex) / 2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_ulaw_ex;
+ return &f;
+}
+
+/*!
+ * \brief The complete translator for ulawToLin.
+ */
+
+static struct ast_translator ulawtolin = {
+ .name = "ulawtolin",
+ .srcfmt = AST_FORMAT_ULAW,
+ .dstfmt = AST_FORMAT_SLINEAR,
+ .framein = ulawtolin_framein,
+ .sample = ulawtolin_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .plc_samples = 160,
+};
+
+/*!
+ * \brief The complete translator for LinToulaw.
+ */
+
+static struct ast_translator lintoulaw = {
+ .name = "lintoulaw",
+ .srcfmt = AST_FORMAT_SLINEAR,
+ .dstfmt = AST_FORMAT_ULAW,
+ .framein = lintoulaw_framein,
+ .sample = lintoulaw_sample,
+ .buf_size = BUFFER_SAMPLES,
+ .buffer_samples = BUFFER_SAMPLES,
+};
+
+static int parse_config(int reload)
+{
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+ for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ ulawtolin.useplc = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "codec_ulaw: %susing generic PLC\n", ulawtolin.useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+static int reload(void)
+{
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintoulaw);
+ res |= ast_unregister_translator(&ulawtolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ res = ast_register_translator(&ulawtolin);
+ if (!res)
+ res = ast_register_translator(&lintoulaw);
+ else
+ ast_unregister_translator(&ulawtolin);
+ if (res)
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "mu-Law Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/codec_zap.c b/trunk/codecs/codec_zap.c
new file mode 100644
index 000000000..01d9a1ed1
--- /dev/null
+++ b/trunk/codecs/codec_zap.c
@@ -0,0 +1,488 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Zaptel native transcoding support
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@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 Translate between various formats natively through Zaptel transcoding
+ *
+ * \ingroup codecs
+ */
+
+/*** MODULEINFO
+ <depend>zaptel_transcode</depend>
+ <depend>zaptel</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <zaptel/zaptel.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/translate.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+
+#define BUFFER_SAMPLES 8000
+
+static unsigned int global_useplc = 0;
+
+static struct channel_usage {
+ int total;
+ int encoders;
+ int decoders;
+} channels;
+
+static char *handle_cli_transcoder_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry cli[] = {
+ AST_CLI_DEFINE(handle_cli_transcoder_show, "Display Zaptel transcoder utilization.")
+};
+
+struct format_map {
+ unsigned int map[32][32];
+};
+
+static struct format_map global_format_map = { { { 0 } } };
+
+struct translator {
+ struct ast_translator t;
+ AST_LIST_ENTRY(translator) entry;
+};
+
+static AST_LIST_HEAD_STATIC(translators, translator);
+
+struct pvt {
+ int fd;
+ int fake;
+#ifdef DEBUG_TRANSCODE
+ int totalms;
+ int lasttotalms;
+#endif
+ struct zt_transcode_header *hdr;
+};
+
+static char *handle_cli_transcoder_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct channel_usage copy;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "transcoder show";
+ e->usage =
+ "Usage: transcoder show\n"
+ " Displays channel utilization of Zaptel transcoder(s).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ copy = channels;
+
+ if (copy.total == 0)
+ ast_cli(a->fd, "No Zaptel transcoders found.\n");
+ else
+ ast_cli(a->fd, "%d/%d encoders/decoders of %d channels are in use.\n", copy.encoders, copy.decoders, copy.total);
+
+ return CLI_SUCCESS;
+}
+
+static int zap_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct pvt *ztp = pvt->pvt;
+ struct zt_transcode_header *hdr = ztp->hdr;
+
+ if (!f->subclass) {
+ /* Fake a return frame for calculation purposes */
+ ztp->fake = 2;
+ pvt->samples = f->samples;
+ return 0;
+ }
+
+ if (!hdr->srclen)
+ /* Copy at front of buffer */
+ hdr->srcoffset = 0;
+
+ if (hdr->srclen + f->datalen > sizeof(hdr->srcdata)) {
+ ast_log(LOG_WARNING, "Out of space for codec translation!\n");
+ return -1;
+ }
+
+ if (hdr->srclen + f->datalen + hdr->srcoffset > sizeof(hdr->srcdata)) {
+ /* Very unlikely */
+ memmove(hdr->srcdata, hdr->srcdata + hdr->srcoffset, hdr->srclen);
+ hdr->srcoffset = 0;
+ }
+
+ memcpy(hdr->srcdata + hdr->srcoffset + hdr->srclen, f->data, f->datalen);
+ hdr->srclen += f->datalen;
+ pvt->samples += f->samples;
+
+ return -1;
+}
+
+static struct ast_frame *zap_frameout(struct ast_trans_pvt *pvt)
+{
+ struct pvt *ztp = pvt->pvt;
+ struct zt_transcode_header *hdr = ztp->hdr;
+ unsigned int x;
+
+ if (ztp->fake == 2) {
+ ztp->fake = 1;
+ pvt->f.frametype = AST_FRAME_VOICE;
+ pvt->f.subclass = 0;
+ pvt->f.samples = 160;
+ pvt->f.data = NULL;
+ pvt->f.offset = 0;
+ pvt->f.datalen = 0;
+ pvt->f.mallocd = 0;
+ ast_set_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR);
+ pvt->samples = 0;
+ } else if (ztp->fake == 1) {
+ return NULL;
+ } else {
+ if (hdr->dstlen) {
+#ifdef DEBUG_TRANSCODE
+ ztp->totalms += hdr->dstsamples;
+ if ((ztp->totalms - ztp->lasttotalms) > 8000) {
+ printf("Whee %p, %d (%d to %d)\n", ztp, hdr->dstlen, ztp->lasttotalms, ztp->totalms);
+ ztp->lasttotalms = ztp->totalms;
+ }
+#endif
+ pvt->f.frametype = AST_FRAME_VOICE;
+ pvt->f.subclass = hdr->dstfmt;
+ pvt->f.samples = hdr->dstsamples;
+ pvt->f.data = hdr->dstdata + hdr->dstoffset;
+ pvt->f.offset = hdr->dstoffset;
+ pvt->f.datalen = hdr->dstlen;
+ pvt->f.mallocd = 0;
+ ast_set_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR);
+ pvt->samples -= pvt->f.samples;
+ hdr->dstlen = 0;
+
+ } else {
+ if (hdr->srclen) {
+ hdr->dstoffset = AST_FRIENDLY_OFFSET;
+ x = ZT_TCOP_TRANSCODE;
+ if (ioctl(ztp->fd, ZT_TRANSCODE_OP, &x))
+ ast_log(LOG_WARNING, "Failed to transcode: %s\n", strerror(errno));
+ }
+ return NULL;
+ }
+ }
+
+ return &pvt->f;
+}
+
+static void zap_destroy(struct ast_trans_pvt *pvt)
+{
+ struct pvt *ztp = pvt->pvt;
+ unsigned int x;
+
+ x = ZT_TCOP_RELEASE;
+ if (ioctl(ztp->fd, ZT_TRANSCODE_OP, &x))
+ ast_log(LOG_WARNING, "Failed to release transcoder channel: %s\n", strerror(errno));
+
+ switch (ztp->hdr->dstfmt) {
+ case AST_FORMAT_G729A:
+ case AST_FORMAT_G723_1:
+ ast_atomic_fetchadd_int(&channels.encoders, -1);
+ break;
+ default:
+ ast_atomic_fetchadd_int(&channels.decoders, -1);
+ break;
+ }
+
+ munmap(ztp->hdr, sizeof(*ztp->hdr));
+ close(ztp->fd);
+}
+
+static int zap_translate(struct ast_trans_pvt *pvt, int dest, int source)
+{
+ /* Request translation through zap if possible */
+ int fd;
+ unsigned int x = ZT_TCOP_ALLOCATE;
+ struct pvt *ztp = pvt->pvt;
+ struct zt_transcode_header *hdr;
+ int flags;
+
+ if ((fd = open("/dev/zap/transcode", O_RDWR)) < 0)
+ return -1;
+ flags = fcntl(fd, F_GETFL);
+ if (flags > - 1) {
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+ ast_log(LOG_WARNING, "Could not set non-block mode!\n");
+ }
+
+
+ if ((hdr = mmap(NULL, sizeof(*hdr), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ ast_log(LOG_ERROR, "Memory Map failed for transcoding (%s)\n", strerror(errno));
+ close(fd);
+
+ return -1;
+ }
+
+ if (hdr->magic != ZT_TRANSCODE_MAGIC) {
+ ast_log(LOG_ERROR, "Transcoder header (%08x) wasn't magic. Abandoning\n", hdr->magic);
+ munmap(hdr, sizeof(*hdr));
+ close(fd);
+
+ return -1;
+ }
+
+ hdr->srcfmt = (1 << source);
+ hdr->dstfmt = (1 << dest);
+ if (ioctl(fd, ZT_TRANSCODE_OP, &x)) {
+ ast_log(LOG_ERROR, "Unable to attach transcoder: %s\n", strerror(errno));
+ munmap(hdr, sizeof(*hdr));
+ close(fd);
+
+ return -1;
+ }
+
+ ztp = pvt->pvt;
+ ztp->fd = fd;
+ ztp->hdr = hdr;
+
+ switch (hdr->dstfmt) {
+ case AST_FORMAT_G729A:
+ case AST_FORMAT_G723_1:
+ ast_atomic_fetchadd_int(&channels.encoders, +1);
+ break;
+ default:
+ ast_atomic_fetchadd_int(&channels.decoders, +1);
+ break;
+ }
+
+ return 0;
+}
+
+static int zap_new(struct ast_trans_pvt *pvt)
+{
+ return zap_translate(pvt, pvt->t->dstfmt, pvt->t->srcfmt);
+}
+
+static struct ast_frame *fakesrc_sample(void)
+{
+ /* Don't bother really trying to test hardware ones. */
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .samples = 160,
+ .src = __PRETTY_FUNCTION__
+ };
+
+ return &f;
+}
+
+static int register_translator(int dst, int src)
+{
+ struct translator *zt;
+ int res;
+
+ if (!(zt = ast_calloc(1, sizeof(*zt))))
+ return -1;
+
+ snprintf((char *) (zt->t.name), sizeof(zt->t.name), "zap%sto%s",
+ ast_getformatname((1 << src)), ast_getformatname((1 << dst)));
+ zt->t.srcfmt = (1 << src);
+ zt->t.dstfmt = (1 << dst);
+ zt->t.newpvt = zap_new;
+ zt->t.framein = zap_framein;
+ zt->t.frameout = zap_frameout;
+ zt->t.destroy = zap_destroy;
+ zt->t.sample = fakesrc_sample;
+ zt->t.useplc = global_useplc;
+ zt->t.buf_size = BUFFER_SAMPLES * 2;
+ zt->t.desc_size = sizeof(struct pvt);
+ if ((res = ast_register_translator(&zt->t))) {
+ ast_free(zt);
+ return -1;
+ }
+
+ AST_LIST_LOCK(&translators);
+ AST_LIST_INSERT_HEAD(&translators, zt, entry);
+ AST_LIST_UNLOCK(&translators);
+
+ global_format_map.map[dst][src] = 1;
+
+ return res;
+}
+
+static void drop_translator(int dst, int src)
+{
+ struct translator *cur;
+
+ AST_LIST_LOCK(&translators);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&translators, cur, entry) {
+ if (cur->t.srcfmt != src)
+ continue;
+
+ if (cur->t.dstfmt != dst)
+ continue;
+
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_unregister_translator(&cur->t);
+ ast_free(cur);
+ global_format_map.map[dst][src] = 0;
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&translators);
+}
+
+static void unregister_translators(void)
+{
+ struct translator *cur;
+
+ AST_LIST_LOCK(&translators);
+ while ((cur = AST_LIST_REMOVE_HEAD(&translators, entry))) {
+ ast_unregister_translator(&cur->t);
+ ast_free(cur);
+ }
+ AST_LIST_UNLOCK(&translators);
+}
+
+static int parse_config(int reload)
+{
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
+
+ if (cfg == NULL)
+ return 0;
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
+ if (!strcasecmp(var->name, "genericplc")) {
+ global_useplc = ast_true(var->value);
+ ast_verb(3, "codec_zap: %susing generic PLC\n",
+ global_useplc ? "" : "not ");
+ }
+ }
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+static void build_translators(struct format_map *map, unsigned int dstfmts, unsigned int srcfmts)
+{
+ unsigned int src, dst;
+
+ for (src = 0; src < 32; src++) {
+ for (dst = 0; dst < 32; dst++) {
+ if (!(srcfmts & (1 << src)))
+ continue;
+
+ if (!(dstfmts & (1 << dst)))
+ continue;
+
+ if (global_format_map.map[dst][src])
+ continue;
+
+ if (!register_translator(dst, src))
+ map->map[dst][src] = 1;
+ }
+ }
+}
+
+static int find_transcoders(void)
+{
+ struct zt_transcode_info info = { 0, };
+ struct format_map map = { { { 0 } } };
+ int fd, res;
+ unsigned int x, y;
+
+ if ((fd = open("/dev/zap/transcode", O_RDWR)) < 0) {
+ ast_verbose(VERBOSE_PREFIX_2 "No hardware transcoders found.\n");
+ return 0;
+ }
+
+ info.op = ZT_TCOP_GETINFO;
+ for (info.tcnum = 0; !(res = ioctl(fd, ZT_TRANSCODE_OP, &info)); info.tcnum++) {
+ ast_verb(2, "Found transcoder '%s'.\n", info.name);
+ build_translators(&map, info.dstfmts, info.srcfmts);
+ ast_atomic_fetchadd_int(&channels.total, info.numchannels / 2);
+ }
+
+ close(fd);
+
+ if (!info.tcnum)
+ ast_verb(2, "No hardware transcoders found.\n");
+
+ for (x = 0; x < 32; x++) {
+ for (y = 0; y < 32; y++) {
+ if (!map.map[x][y] && global_format_map.map[x][y])
+ drop_translator(x, y);
+ }
+ }
+
+ return 0;
+}
+
+static int reload(void)
+{
+ struct translator *cur;
+
+ if (parse_config(1))
+ return AST_MODULE_LOAD_DECLINE;
+
+ AST_LIST_LOCK(&translators);
+ AST_LIST_TRAVERSE(&translators, cur, entry)
+ cur->t.useplc = global_useplc;
+ AST_LIST_UNLOCK(&translators);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_cli_unregister_multiple(cli, sizeof(cli) / sizeof(cli[0]));
+ unregister_translators();
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ if (parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+ find_transcoders();
+ ast_cli_register_multiple(cli, sizeof(cli) / sizeof(cli[0]));
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Generic Zaptel Transcoder Codec Translator",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/codecs/g722/Makefile b/trunk/codecs/g722/Makefile
new file mode 100644
index 000000000..dee23c6e8
--- /dev/null
+++ b/trunk/codecs/g722/Makefile
@@ -0,0 +1,18 @@
+LIB=libg722.a
+CFLAGS+=-fPIC
+
+include $(ASTTOPDIR)/Makefile.rules
+
+OBJS=g722_encode.o g722_decode.o
+
+all: $(LIB)
+
+$(LIB): $(OBJS)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+clean:
+ rm -f $(LIB) *.o
+ rm -f .*.o.d
+ rm -f *.s *.i
diff --git a/trunk/codecs/g722/g722.h b/trunk/codecs/g722/g722.h
new file mode 100644
index 000000000..f57b1c882
--- /dev/null
+++ b/trunk/codecs/g722/g722.h
@@ -0,0 +1,148 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * g722.h - The ITU G.722 codec.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2005 Steve Underwood
+ *
+ * Despite my general liking of the GPL, I place my own contributions
+ * to this code in the public domain for the benefit of all mankind -
+ * even the slimy ones who might try to proprietize my work and use it
+ * to my detriment.
+ *
+ * Based on a single channel G.722 codec which is:
+ *
+ ***** Copyright (c) CMU 1993 *****
+ * Computer Science, Speech Group
+ * Chengxiang Lu and Alex Hauptmann
+ *
+ * $Id$
+ */
+
+
+/*! \file */
+
+#if !defined(_G722_H_)
+#define _G722_H_
+
+/*! \page g722_page G.722 encoding and decoding
+\section g722_page_sec_1 What does it do?
+The G.722 module is a bit exact implementation of the ITU G.722 specification for all three
+specified bit rates - 64000bps, 56000bps and 48000bps. It passes the ITU tests.
+
+To allow fast and flexible interworking with narrow band telephony, the encoder and decoder
+support an option for the linear audio to be an 8k samples/second stream. In this mode the
+codec is considerably faster, and still fully compatible with wideband terminals using G.722.
+
+\section g722_page_sec_2 How does it work?
+???.
+*/
+
+enum
+{
+ G722_SAMPLE_RATE_8000 = 0x0001,
+ G722_PACKED = 0x0002
+};
+
+#ifndef INT16_MAX
+#define INT16_MAX 32767
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32768)
+#endif
+
+typedef struct
+{
+ /*! TRUE if the operating in the special ITU test mode, with the band split filters
+ disabled. */
+ int itu_test_mode;
+ /*! TRUE if the G.722 data is packed */
+ int packed;
+ /*! TRUE if encode from 8k samples/second */
+ int eight_k;
+ /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */
+ int bits_per_sample;
+
+ /*! Signal history for the QMF */
+ int x[24];
+
+ struct
+ {
+ int s;
+ int sp;
+ int sz;
+ int r[3];
+ int a[3];
+ int ap[3];
+ int p[3];
+ int d[7];
+ int b[7];
+ int bp[7];
+ int sg[7];
+ int nb;
+ int det;
+ } band[2];
+
+ unsigned int in_buffer;
+ int in_bits;
+ unsigned int out_buffer;
+ int out_bits;
+} g722_encode_state_t;
+
+typedef struct
+{
+ /*! TRUE if the operating in the special ITU test mode, with the band split filters
+ disabled. */
+ int itu_test_mode;
+ /*! TRUE if the G.722 data is packed */
+ int packed;
+ /*! TRUE if decode to 8k samples/second */
+ int eight_k;
+ /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */
+ int bits_per_sample;
+
+ /*! Signal history for the QMF */
+ int x[24];
+
+ struct
+ {
+ int s;
+ int sp;
+ int sz;
+ int r[3];
+ int a[3];
+ int ap[3];
+ int p[3];
+ int d[7];
+ int b[7];
+ int bp[7];
+ int sg[7];
+ int nb;
+ int det;
+ } band[2];
+
+ unsigned int in_buffer;
+ int in_bits;
+ unsigned int out_buffer;
+ int out_bits;
+} g722_decode_state_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options);
+int g722_encode_release(g722_encode_state_t *s);
+int g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len);
+
+g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options);
+int g722_decode_release(g722_decode_state_t *s);
+int g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/trunk/codecs/g722/g722_decode.c b/trunk/codecs/g722/g722_decode.c
new file mode 100644
index 000000000..1625acc86
--- /dev/null
+++ b/trunk/codecs/g722/g722_decode.c
@@ -0,0 +1,398 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * g722_decode.c - The ITU G.722 codec, decode part.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2005 Steve Underwood
+ *
+ * Despite my general liking of the GPL, I place my own contributions
+ * to this code in the public domain for the benefit of all mankind -
+ * even the slimy ones who might try to proprietize my work and use it
+ * to my detriment.
+ *
+ * Based in part on a single channel G.722 codec which is:
+ *
+ * Copyright (c) CMU 1993
+ * Computer Science, Speech Group
+ * Chengxiang Lu and Alex Hauptmann
+ *
+ * $Id$
+ */
+
+/*! \file */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <memory.h>
+#include <stdlib.h>
+#if 0
+#include <tgmath.h>
+#endif
+
+#include "g722.h"
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+static __inline__ int16_t saturate(int32_t amp)
+{
+ int16_t amp16;
+
+ /* Hopefully this is optimised for the common case - not clipping */
+ amp16 = (int16_t) amp;
+ if (amp == amp16)
+ return amp16;
+ if (amp > INT16_MAX)
+ return INT16_MAX;
+ return INT16_MIN;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void block4(g722_decode_state_t *s, int band, int d);
+
+static void block4(g722_decode_state_t *s, int band, int d)
+{
+ int wd1;
+ int wd2;
+ int wd3;
+ int i;
+
+ /* Block 4, RECONS */
+ s->band[band].d[0] = d;
+ s->band[band].r[0] = saturate(s->band[band].s + d);
+
+ /* Block 4, PARREC */
+ s->band[band].p[0] = saturate(s->band[band].sz + d);
+
+ /* Block 4, UPPOL2 */
+ for (i = 0; i < 3; i++)
+ s->band[band].sg[i] = s->band[band].p[i] >> 15;
+ wd1 = saturate(s->band[band].a[1] << 2);
+
+ wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1;
+ if (wd2 > 32767)
+ wd2 = 32767;
+ wd3 = (s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128;
+ wd3 += (wd2 >> 7);
+ wd3 += (s->band[band].a[2]*32512) >> 15;
+ if (wd3 > 12288)
+ wd3 = 12288;
+ else if (wd3 < -12288)
+ wd3 = -12288;
+ s->band[band].ap[2] = wd3;
+
+ /* Block 4, UPPOL1 */
+ s->band[band].sg[0] = s->band[band].p[0] >> 15;
+ s->band[band].sg[1] = s->band[band].p[1] >> 15;
+ wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192;
+ wd2 = (s->band[band].a[1]*32640) >> 15;
+
+ s->band[band].ap[1] = saturate(wd1 + wd2);
+ wd3 = saturate(15360 - s->band[band].ap[2]);
+ if (s->band[band].ap[1] > wd3)
+ s->band[band].ap[1] = wd3;
+ else if (s->band[band].ap[1] < -wd3)
+ s->band[band].ap[1] = -wd3;
+
+ /* Block 4, UPZERO */
+ wd1 = (d == 0) ? 0 : 128;
+ s->band[band].sg[0] = d >> 15;
+ for (i = 1; i < 7; i++)
+ {
+ s->band[band].sg[i] = s->band[band].d[i] >> 15;
+ wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1;
+ wd3 = (s->band[band].b[i]*32640) >> 15;
+ s->band[band].bp[i] = saturate(wd2 + wd3);
+ }
+
+ /* Block 4, DELAYA */
+ for (i = 6; i > 0; i--)
+ {
+ s->band[band].d[i] = s->band[band].d[i - 1];
+ s->band[band].b[i] = s->band[band].bp[i];
+ }
+
+ for (i = 2; i > 0; i--)
+ {
+ s->band[band].r[i] = s->band[band].r[i - 1];
+ s->band[band].p[i] = s->band[band].p[i - 1];
+ s->band[band].a[i] = s->band[band].ap[i];
+ }
+
+ /* Block 4, FILTEP */
+ wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]);
+ wd1 = (s->band[band].a[1]*wd1) >> 15;
+ wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]);
+ wd2 = (s->band[band].a[2]*wd2) >> 15;
+ s->band[band].sp = saturate(wd1 + wd2);
+
+ /* Block 4, FILTEZ */
+ s->band[band].sz = 0;
+ for (i = 6; i > 0; i--)
+ {
+ wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]);
+ s->band[band].sz += (s->band[band].b[i]*wd1) >> 15;
+ }
+ s->band[band].sz = saturate(s->band[band].sz);
+
+ /* Block 4, PREDIC */
+ s->band[band].s = saturate(s->band[band].sp + s->band[band].sz);
+}
+/*- End of function --------------------------------------------------------*/
+
+g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options)
+{
+ if (s == NULL)
+ {
+ if ((s = (g722_decode_state_t *) malloc(sizeof(*s))) == NULL)
+ return NULL;
+ }
+ memset(s, 0, sizeof(*s));
+ if (rate == 48000)
+ s->bits_per_sample = 6;
+ else if (rate == 56000)
+ s->bits_per_sample = 7;
+ else
+ s->bits_per_sample = 8;
+ if ((options & G722_SAMPLE_RATE_8000))
+ s->eight_k = TRUE;
+ if ((options & G722_PACKED) && s->bits_per_sample != 8)
+ s->packed = TRUE;
+ else
+ s->packed = FALSE;
+ s->band[0].det = 32;
+ s->band[1].det = 8;
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+int g722_decode_release(g722_decode_state_t *s)
+{
+ free(s);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int g722_decode(g722_decode_state_t *s, int16_t amp[], const uint8_t g722_data[], int len)
+{
+ static const int wl[8] = {-60, -30, 58, 172, 334, 538, 1198, 3042 };
+ static const int rl42[16] = {0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 };
+ static const int ilb[32] =
+ {
+ 2048, 2093, 2139, 2186, 2233, 2282, 2332,
+ 2383, 2435, 2489, 2543, 2599, 2656, 2714,
+ 2774, 2834, 2896, 2960, 3025, 3091, 3158,
+ 3228, 3298, 3371, 3444, 3520, 3597, 3676,
+ 3756, 3838, 3922, 4008
+ };
+ static const int wh[3] = {0, -214, 798};
+ static const int rh2[4] = {2, 1, 2, 1};
+ static const int qm2[4] = {-7408, -1616, 7408, 1616};
+ static const int qm4[16] =
+ {
+ 0, -20456, -12896, -8968,
+ -6288, -4240, -2584, -1200,
+ 20456, 12896, 8968, 6288,
+ 4240, 2584, 1200, 0
+ };
+ static const int qm5[32] =
+ {
+ -280, -280, -23352, -17560,
+ -14120, -11664, -9752, -8184,
+ -6864, -5712, -4696, -3784,
+ -2960, -2208, -1520, -880,
+ 23352, 17560, 14120, 11664,
+ 9752, 8184, 6864, 5712,
+ 4696, 3784, 2960, 2208,
+ 1520, 880, 280, -280
+ };
+ static const int qm6[64] =
+ {
+ -136, -136, -136, -136,
+ -24808, -21904, -19008, -16704,
+ -14984, -13512, -12280, -11192,
+ -10232, -9360, -8576, -7856,
+ -7192, -6576, -6000, -5456,
+ -4944, -4464, -4008, -3576,
+ -3168, -2776, -2400, -2032,
+ -1688, -1360, -1040, -728,
+ 24808, 21904, 19008, 16704,
+ 14984, 13512, 12280, 11192,
+ 10232, 9360, 8576, 7856,
+ 7192, 6576, 6000, 5456,
+ 4944, 4464, 4008, 3576,
+ 3168, 2776, 2400, 2032,
+ 1688, 1360, 1040, 728,
+ 432, 136, -432, -136
+ };
+ static const int qmf_coeffs[12] =
+ {
+ 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11,
+ };
+
+ int dlowt;
+ int rlow;
+ int ihigh;
+ int dhigh;
+ int rhigh;
+ int xout1;
+ int xout2;
+ int wd1;
+ int wd2;
+ int wd3;
+ int code;
+ int outlen;
+ int i;
+ int j;
+
+ outlen = 0;
+ rhigh = 0;
+ for (j = 0; j < len; )
+ {
+ if (s->packed)
+ {
+ /* Unpack the code bits */
+ if (s->in_bits < s->bits_per_sample)
+ {
+ s->in_buffer |= (g722_data[j++] << s->in_bits);
+ s->in_bits += 8;
+ }
+ code = s->in_buffer & ((1 << s->bits_per_sample) - 1);
+ s->in_buffer >>= s->bits_per_sample;
+ s->in_bits -= s->bits_per_sample;
+ }
+ else
+ {
+ code = g722_data[j++];
+ }
+
+ switch (s->bits_per_sample)
+ {
+ default:
+ case 8:
+ wd1 = code & 0x3F;
+ ihigh = (code >> 6) & 0x03;
+ wd2 = qm6[wd1];
+ wd1 >>= 2;
+ break;
+ case 7:
+ wd1 = code & 0x1F;
+ ihigh = (code >> 5) & 0x03;
+ wd2 = qm5[wd1];
+ wd1 >>= 1;
+ break;
+ case 6:
+ wd1 = code & 0x0F;
+ ihigh = (code >> 4) & 0x03;
+ wd2 = qm4[wd1];
+ break;
+ }
+ /* Block 5L, LOW BAND INVQBL */
+ wd2 = (s->band[0].det*wd2) >> 15;
+ /* Block 5L, RECONS */
+ rlow = s->band[0].s + wd2;
+ /* Block 6L, LIMIT */
+ if (rlow > 16383)
+ rlow = 16383;
+ else if (rlow < -16384)
+ rlow = -16384;
+
+ /* Block 2L, INVQAL */
+ wd2 = qm4[wd1];
+ dlowt = (s->band[0].det*wd2) >> 15;
+
+ /* Block 3L, LOGSCL */
+ wd2 = rl42[wd1];
+ wd1 = (s->band[0].nb*127) >> 7;
+ wd1 += wl[wd2];
+ if (wd1 < 0)
+ wd1 = 0;
+ else if (wd1 > 18432)
+ wd1 = 18432;
+ s->band[0].nb = wd1;
+
+ /* Block 3L, SCALEL */
+ wd1 = (s->band[0].nb >> 6) & 31;
+ wd2 = 8 - (s->band[0].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[0].det = wd3 << 2;
+
+ block4(s, 0, dlowt);
+
+ if (!s->eight_k)
+ {
+ /* Block 2H, INVQAH */
+ wd2 = qm2[ihigh];
+ dhigh = (s->band[1].det*wd2) >> 15;
+ /* Block 5H, RECONS */
+ rhigh = dhigh + s->band[1].s;
+ /* Block 6H, LIMIT */
+ if (rhigh > 16383)
+ rhigh = 16383;
+ else if (rhigh < -16384)
+ rhigh = -16384;
+
+ /* Block 2H, INVQAH */
+ wd2 = rh2[ihigh];
+ wd1 = (s->band[1].nb*127) >> 7;
+ wd1 += wh[wd2];
+ if (wd1 < 0)
+ wd1 = 0;
+ else if (wd1 > 22528)
+ wd1 = 22528;
+ s->band[1].nb = wd1;
+
+ /* Block 3H, SCALEH */
+ wd1 = (s->band[1].nb >> 6) & 31;
+ wd2 = 10 - (s->band[1].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[1].det = wd3 << 2;
+
+ block4(s, 1, dhigh);
+ }
+
+ if (s->itu_test_mode)
+ {
+ amp[outlen++] = (int16_t) (rlow << 1);
+ amp[outlen++] = (int16_t) (rhigh << 1);
+ }
+ else
+ {
+ if (s->eight_k)
+ {
+ amp[outlen++] = (int16_t) rlow;
+ }
+ else
+ {
+ /* Apply the receive QMF */
+ for (i = 0; i < 22; i++)
+ s->x[i] = s->x[i + 2];
+ s->x[22] = rlow + rhigh;
+ s->x[23] = rlow - rhigh;
+
+ xout1 = 0;
+ xout2 = 0;
+ for (i = 0; i < 12; i++)
+ {
+ xout2 += s->x[2*i]*qmf_coeffs[i];
+ xout1 += s->x[2*i + 1]*qmf_coeffs[11 - i];
+ }
+ amp[outlen++] = (int16_t) (xout1 >> 12);
+ amp[outlen++] = (int16_t) (xout2 >> 12);
+ }
+ }
+ }
+ return outlen;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/trunk/codecs/g722/g722_encode.c b/trunk/codecs/g722/g722_encode.c
new file mode 100644
index 000000000..ad7e6b522
--- /dev/null
+++ b/trunk/codecs/g722/g722_encode.c
@@ -0,0 +1,400 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * g722_encode.c - The ITU G.722 codec, encode part.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2005 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * Despite my general liking of the GPL, I place my own contributions
+ * to this code in the public domain for the benefit of all mankind -
+ * even the slimy ones who might try to proprietize my work and use it
+ * to my detriment.
+ *
+ * Based on a single channel 64kbps only G.722 codec which is:
+ *
+ ***** Copyright (c) CMU 1993 *****
+ * Computer Science, Speech Group
+ * Chengxiang Lu and Alex Hauptmann
+ *
+ * $Id$
+ */
+
+/*! \file */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <memory.h>
+#include <stdlib.h>
+#if 0
+#include <tgmath.h>
+#endif
+
+#include "g722.h"
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+static __inline__ int16_t saturate(int32_t amp)
+{
+ int16_t amp16;
+
+ /* Hopefully this is optimised for the common case - not clipping */
+ amp16 = (int16_t) amp;
+ if (amp == amp16)
+ return amp16;
+ if (amp > INT16_MAX)
+ return INT16_MAX;
+ return INT16_MIN;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void block4(g722_encode_state_t *s, int band, int d)
+{
+ int wd1;
+ int wd2;
+ int wd3;
+ int i;
+
+ /* Block 4, RECONS */
+ s->band[band].d[0] = d;
+ s->band[band].r[0] = saturate(s->band[band].s + d);
+
+ /* Block 4, PARREC */
+ s->band[band].p[0] = saturate(s->band[band].sz + d);
+
+ /* Block 4, UPPOL2 */
+ for (i = 0; i < 3; i++)
+ s->band[band].sg[i] = s->band[band].p[i] >> 15;
+ wd1 = saturate(s->band[band].a[1] << 2);
+
+ wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1;
+ if (wd2 > 32767)
+ wd2 = 32767;
+ wd3 = (wd2 >> 7) + ((s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128);
+ wd3 += (s->band[band].a[2]*32512) >> 15;
+ if (wd3 > 12288)
+ wd3 = 12288;
+ else if (wd3 < -12288)
+ wd3 = -12288;
+ s->band[band].ap[2] = wd3;
+
+ /* Block 4, UPPOL1 */
+ s->band[band].sg[0] = s->band[band].p[0] >> 15;
+ s->band[band].sg[1] = s->band[band].p[1] >> 15;
+ wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192;
+ wd2 = (s->band[band].a[1]*32640) >> 15;
+
+ s->band[band].ap[1] = saturate(wd1 + wd2);
+ wd3 = saturate(15360 - s->band[band].ap[2]);
+ if (s->band[band].ap[1] > wd3)
+ s->band[band].ap[1] = wd3;
+ else if (s->band[band].ap[1] < -wd3)
+ s->band[band].ap[1] = -wd3;
+
+ /* Block 4, UPZERO */
+ wd1 = (d == 0) ? 0 : 128;
+ s->band[band].sg[0] = d >> 15;
+ for (i = 1; i < 7; i++)
+ {
+ s->band[band].sg[i] = s->band[band].d[i] >> 15;
+ wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1;
+ wd3 = (s->band[band].b[i]*32640) >> 15;
+ s->band[band].bp[i] = saturate(wd2 + wd3);
+ }
+
+ /* Block 4, DELAYA */
+ for (i = 6; i > 0; i--)
+ {
+ s->band[band].d[i] = s->band[band].d[i - 1];
+ s->band[band].b[i] = s->band[band].bp[i];
+ }
+
+ for (i = 2; i > 0; i--)
+ {
+ s->band[band].r[i] = s->band[band].r[i - 1];
+ s->band[band].p[i] = s->band[band].p[i - 1];
+ s->band[band].a[i] = s->band[band].ap[i];
+ }
+
+ /* Block 4, FILTEP */
+ wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]);
+ wd1 = (s->band[band].a[1]*wd1) >> 15;
+ wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]);
+ wd2 = (s->band[band].a[2]*wd2) >> 15;
+ s->band[band].sp = saturate(wd1 + wd2);
+
+ /* Block 4, FILTEZ */
+ s->band[band].sz = 0;
+ for (i = 6; i > 0; i--)
+ {
+ wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]);
+ s->band[band].sz += (s->band[band].b[i]*wd1) >> 15;
+ }
+ s->band[band].sz = saturate(s->band[band].sz);
+
+ /* Block 4, PREDIC */
+ s->band[band].s = saturate(s->band[band].sp + s->band[band].sz);
+}
+/*- End of function --------------------------------------------------------*/
+
+g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options)
+{
+ if (s == NULL)
+ {
+ if ((s = (g722_encode_state_t *) malloc(sizeof(*s))) == NULL)
+ return NULL;
+ }
+ memset(s, 0, sizeof(*s));
+ if (rate == 48000)
+ s->bits_per_sample = 6;
+ else if (rate == 56000)
+ s->bits_per_sample = 7;
+ else
+ s->bits_per_sample = 8;
+ if ((options & G722_SAMPLE_RATE_8000))
+ s->eight_k = TRUE;
+ if ((options & G722_PACKED) && s->bits_per_sample != 8)
+ s->packed = TRUE;
+ else
+ s->packed = FALSE;
+ s->band[0].det = 32;
+ s->band[1].det = 8;
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+int g722_encode_release(g722_encode_state_t *s)
+{
+ free(s);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int g722_encode(g722_encode_state_t *s, uint8_t g722_data[], const int16_t amp[], int len)
+{
+ static const int q6[32] =
+ {
+ 0, 35, 72, 110, 150, 190, 233, 276,
+ 323, 370, 422, 473, 530, 587, 650, 714,
+ 786, 858, 940, 1023, 1121, 1219, 1339, 1458,
+ 1612, 1765, 1980, 2195, 2557, 2919, 0, 0
+ };
+ static const int iln[32] =
+ {
+ 0, 63, 62, 31, 30, 29, 28, 27,
+ 26, 25, 24, 23, 22, 21, 20, 19,
+ 18, 17, 16, 15, 14, 13, 12, 11,
+ 10, 9, 8, 7, 6, 5, 4, 0
+ };
+ static const int ilp[32] =
+ {
+ 0, 61, 60, 59, 58, 57, 56, 55,
+ 54, 53, 52, 51, 50, 49, 48, 47,
+ 46, 45, 44, 43, 42, 41, 40, 39,
+ 38, 37, 36, 35, 34, 33, 32, 0
+ };
+ static const int wl[8] =
+ {
+ -60, -30, 58, 172, 334, 538, 1198, 3042
+ };
+ static const int rl42[16] =
+ {
+ 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0
+ };
+ static const int ilb[32] =
+ {
+ 2048, 2093, 2139, 2186, 2233, 2282, 2332,
+ 2383, 2435, 2489, 2543, 2599, 2656, 2714,
+ 2774, 2834, 2896, 2960, 3025, 3091, 3158,
+ 3228, 3298, 3371, 3444, 3520, 3597, 3676,
+ 3756, 3838, 3922, 4008
+ };
+ static const int qm4[16] =
+ {
+ 0, -20456, -12896, -8968,
+ -6288, -4240, -2584, -1200,
+ 20456, 12896, 8968, 6288,
+ 4240, 2584, 1200, 0
+ };
+ static const int qm2[4] =
+ {
+ -7408, -1616, 7408, 1616
+ };
+ static const int qmf_coeffs[12] =
+ {
+ 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11,
+ };
+ static const int ihn[3] = {0, 1, 0};
+ static const int ihp[3] = {0, 3, 2};
+ static const int wh[3] = {0, -214, 798};
+ static const int rh2[4] = {2, 1, 2, 1};
+
+ int dlow;
+ int dhigh;
+ int el;
+ int wd;
+ int wd1;
+ int ril;
+ int wd2;
+ int il4;
+ int ih2;
+ int wd3;
+ int eh;
+ int mih;
+ int i;
+ int j;
+ /* Low and high band PCM from the QMF */
+ int xlow;
+ int xhigh;
+ int g722_bytes;
+ /* Even and odd tap accumulators */
+ int sumeven;
+ int sumodd;
+ int ihigh;
+ int ilow;
+ int code;
+
+ g722_bytes = 0;
+ xhigh = 0;
+ for (j = 0; j < len; )
+ {
+ if (s->itu_test_mode)
+ {
+ xlow =
+ xhigh = amp[j++] >> 1;
+ }
+ else
+ {
+ if (s->eight_k)
+ {
+ xlow = amp[j++];
+ }
+ else
+ {
+ /* Apply the transmit QMF */
+ /* Shuffle the buffer down */
+ for (i = 0; i < 22; i++)
+ s->x[i] = s->x[i + 2];
+ s->x[22] = amp[j++];
+ s->x[23] = amp[j++];
+
+ /* Discard every other QMF output */
+ sumeven = 0;
+ sumodd = 0;
+ for (i = 0; i < 12; i++)
+ {
+ sumodd += s->x[2*i]*qmf_coeffs[i];
+ sumeven += s->x[2*i + 1]*qmf_coeffs[11 - i];
+ }
+ xlow = (sumeven + sumodd) >> 13;
+ xhigh = (sumeven - sumodd) >> 13;
+ }
+ }
+ /* Block 1L, SUBTRA */
+ el = saturate(xlow - s->band[0].s);
+
+ /* Block 1L, QUANTL */
+ wd = (el >= 0) ? el : -(el + 1);
+
+ for (i = 1; i < 30; i++)
+ {
+ wd1 = (q6[i]*s->band[0].det) >> 12;
+ if (wd < wd1)
+ break;
+ }
+ ilow = (el < 0) ? iln[i] : ilp[i];
+
+ /* Block 2L, INVQAL */
+ ril = ilow >> 2;
+ wd2 = qm4[ril];
+ dlow = (s->band[0].det*wd2) >> 15;
+
+ /* Block 3L, LOGSCL */
+ il4 = rl42[ril];
+ wd = (s->band[0].nb*127) >> 7;
+ s->band[0].nb = wd + wl[il4];
+ if (s->band[0].nb < 0)
+ s->band[0].nb = 0;
+ else if (s->band[0].nb > 18432)
+ s->band[0].nb = 18432;
+
+ /* Block 3L, SCALEL */
+ wd1 = (s->band[0].nb >> 6) & 31;
+ wd2 = 8 - (s->band[0].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[0].det = wd3 << 2;
+
+ block4(s, 0, dlow);
+
+ if (s->eight_k)
+ {
+ /* Just leave the high bits as zero */
+ code = (0xC0 | ilow) >> (8 - s->bits_per_sample);
+ }
+ else
+ {
+ /* Block 1H, SUBTRA */
+ eh = saturate(xhigh - s->band[1].s);
+
+ /* Block 1H, QUANTH */
+ wd = (eh >= 0) ? eh : -(eh + 1);
+ wd1 = (564*s->band[1].det) >> 12;
+ mih = (wd >= wd1) ? 2 : 1;
+ ihigh = (eh < 0) ? ihn[mih] : ihp[mih];
+
+ /* Block 2H, INVQAH */
+ wd2 = qm2[ihigh];
+ dhigh = (s->band[1].det*wd2) >> 15;
+
+ /* Block 3H, LOGSCH */
+ ih2 = rh2[ihigh];
+ wd = (s->band[1].nb*127) >> 7;
+ s->band[1].nb = wd + wh[ih2];
+ if (s->band[1].nb < 0)
+ s->band[1].nb = 0;
+ else if (s->band[1].nb > 22528)
+ s->band[1].nb = 22528;
+
+ /* Block 3H, SCALEH */
+ wd1 = (s->band[1].nb >> 6) & 31;
+ wd2 = 10 - (s->band[1].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[1].det = wd3 << 2;
+
+ block4(s, 1, dhigh);
+ code = ((ihigh << 6) | ilow) >> (8 - s->bits_per_sample);
+ }
+
+ if (s->packed)
+ {
+ /* Pack the code bits */
+ s->out_buffer |= (code << s->out_bits);
+ s->out_bits += s->bits_per_sample;
+ if (s->out_bits >= 8)
+ {
+ g722_data[g722_bytes++] = (uint8_t) (s->out_buffer & 0xFF);
+ s->out_bits -= 8;
+ s->out_buffer >>= 8;
+ }
+ }
+ else
+ {
+ g722_data[g722_bytes++] = (uint8_t) code;
+ }
+ }
+ return g722_bytes;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/trunk/codecs/g722_slin_ex.h b/trunk/codecs/g722_slin_ex.h
new file mode 100644
index 000000000..f8d3de5d0
--- /dev/null
+++ b/trunk/codecs/g722_slin_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * \brief g722_slin_ex.h --
+ *
+ * 4-bit ADPCM data, 20 milliseconds worth at 8 kHz.
+ *
+ * Source: g723.example
+ *
+ * Copyright (C) 2001-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char g722_slin_ex[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/trunk/codecs/g726_slin_ex.h b/trunk/codecs/g726_slin_ex.h
new file mode 100644
index 000000000..80cbf00f4
--- /dev/null
+++ b/trunk/codecs/g726_slin_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * adpcm_slin_ex.h --
+ *
+ * \brief 4-bit G.726 data, 20 milliseconds worth at 8 kHz.
+ *
+ * Source: g726.example
+ *
+ * Copyright (C) 2001-2005, Digium, Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char g726_slin_ex[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/trunk/codecs/gsm/COPYRIGHT b/trunk/codecs/gsm/COPYRIGHT
new file mode 100644
index 000000000..eba0e523b
--- /dev/null
+++ b/trunk/codecs/gsm/COPYRIGHT
@@ -0,0 +1,16 @@
+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
diff --git a/trunk/codecs/gsm/Makefile b/trunk/codecs/gsm/Makefile
new file mode 100644
index 000000000..bb783a454
--- /dev/null
+++ b/trunk/codecs/gsm/Makefile
@@ -0,0 +1,543 @@
+# Copyright 1992-1996 by Jutta Degener and Carsten Bormann, Technische
+# Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+# details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+
+# Machine- or installation dependent flags you should configure to port
+
+SASR = -DSASR
+######### Define SASR if >> is a signed arithmetic shift (-1 >> 1 == -1)
+
+#MULHACK = -DUSE_FLOAT_MUL
+######### Define this if your host multiplies floats faster than integers,
+######### e.g. on a SPARCstation.
+
+#FAST = -DFAST
+######### Define together with USE_FLOAT_MUL to enable the GSM library's
+######### approximation option for incorrect, but good-enough results.
+
+# LTP_CUT = -DLTP_CUT
+LTP_CUT =
+######### Define to enable the GSM library's long-term correlation
+######### approximation option---faster, but worse; works for
+######### both integer and floating point multiplications.
+######### This flag is still in the experimental stage.
+
+WAV49 = -DWAV49
+#WAV49 =
+######### Define to enable the GSM library's option to pack GSM frames
+######### in the style used by the WAV #49 format. If you want to write
+######### a tool that produces .WAV files which contain GSM-encoded data,
+######### define this, and read about the GSM_OPT_WAV49 option in the
+######### manual page on gsm_option(3).
+
+#K6OPT = -DK6OPT
+#K6OPT =
+######### Define 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. The compile will
+######### probably require gcc.
+
+ifeq (, $(findstring $(OSARCH) , Darwin SunOS ))
+ifeq (, $(findstring $(PROC) , x86_64 amd64 ultrasparc sparc64 arm armv5b armeb ppc powerpc ppc64 ia64 s390 bfin mipsel ))
+ifeq (, $(findstring $(shell uname -m) , ppc ppc64 alpha armv4l s390 ))
+OPTIMIZE+=-march=$(PROC)
+endif
+endif
+endif
+
+#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
+ifeq ($(PROC),ultrasparc)
+OPTIMIZE+=-mcpu=v8 -mtune=$(PROC) -O3
+endif
+
+PG =
+#PG = -g -pg
+######### Profiling flags. If you don't know what that means, leave it blank.
+
+# Choose a compiler. The code works both with ANSI and K&R-C.
+# Use -DNeedFunctionPrototypes to compile with, -UNeedFunctionPrototypes to
+# compile without, function prototypes in the header files.
+#
+# You can use the -DSTUPID_COMPILER to circumvent some compilers'
+# static limits regarding the number of subexpressions in a statement.
+
+# CC = cc
+# CCFLAGS = -c -DSTUPID_COMPILER
+
+# CC = /usr/lang/acc
+# CCFLAGS = -c -O
+
+CCFLAGS += -c -DNeedFunctionPrototypes=1 -funroll-loops $(OPTIMIZE)
+
+# LD = gcc
+# LDFLAGS =
+
+
+# If your compiler needs additional flags/libraries, regardless of
+# the source compiled, configure them here.
+
+# CCINC = -I/usr/gnu/lib/gcc-2.1/gcc-lib/sparc-sun-sunos4.1.2/2.1/include
+######### Includes needed by $(CC)
+
+# LDINC = -L/usr/gnu/lib/gcc-2.1/gcc-lib/sparc-sun-sunos4.1.2/2.1
+######### Library paths needed by $(LD)
+
+# LDLIB = -lgcc
+######### Additional libraries needed by $(LD)
+
+
+# Where do you want to install libraries, binaries, a header file
+# and the manual pages?
+#
+# Leave INSTALL_ROOT empty (or just don't execute "make install") to
+# not install gsm and toast outside of this directory.
+
+INSTALL_ROOT =
+
+# Where do you want to install the gsm library, header file, and manpages?
+#
+# Leave GSM_INSTALL_ROOT empty to not install the GSM library outside of
+# this directory.
+
+GSM_INSTALL_ROOT = $(INSTALL_ROOT)
+GSM_INSTALL_LIB = $(GSM_INSTALL_ROOT)/lib
+GSM_INSTALL_INC = $(GSM_INSTALL_ROOT)/inc
+GSM_INSTALL_MAN = $(GSM_INSTALL_ROOT)/man/man3
+
+
+# Where do you want to install the toast binaries and their manpage?
+#
+# Leave TOAST_INSTALL_ROOT empty to not install the toast binaries outside
+# of this directory.
+
+TOAST_INSTALL_ROOT = $(INSTALL_ROOT)
+TOAST_INSTALL_BIN = $(TOAST_INSTALL_ROOT)/bin
+TOAST_INSTALL_MAN = $(TOAST_INSTALL_ROOT)/man/man1
+
+# Other tools
+
+SHELL = /bin/sh
+LN = ln
+BASENAME = basename
+AR = ar
+ARFLAGS = cr
+RMFLAGS = -f
+FIND = find
+COMPRESS = compress
+COMPRESSFLAGS =
+# RANLIB = true
+RANLIB = ranlib
+
+#
+# You shouldn't have to configure below this line if you're porting.
+#
+
+
+# Local Directories
+
+ROOT = .
+ADDTST = $(ROOT)/add-test
+TST = $(ROOT)/tst
+MAN = $(ROOT)/man
+BIN = $(ROOT)/bin
+SRC = $(ROOT)/src
+LIB = $(ROOT)/lib
+TLS = $(ROOT)/tls
+INC = $(ROOT)/inc
+
+# Flags
+
+DEBUG = -DNDEBUG
+######### Remove -DNDEBUG to enable assertions.
+
+ASTCFLAGS += $(PG) $(CCFLAGS) $(SASR) $(DEBUG) $(MULHACK) $(FAST) \
+ $(LTP_CUT) $(WAV49) $(K6OPT) $(CCINC) -I$(INC)
+######### It's $(CC) $(CFLAGS)
+
+LFLAGS = $(PG) $(LDFLAGS) $(LDINC)
+######### It's $(LD) $(LFLAGS)
+
+
+# Targets
+
+LIBGSM = $(LIB)/libgsm.a
+LIBGSMSO= $(LIB)/libgsm.so
+
+TOAST = $(BIN)/toast
+UNTOAST = $(BIN)/untoast
+TCAT = $(BIN)/tcat
+
+# Headers
+
+GSM_HEADERS = $(INC)/gsm.h
+
+HEADERS = $(INC)/proto.h \
+ $(INC)/unproto.h \
+ $(INC)/config.h \
+ $(INC)/private.h \
+ $(INC)/gsm.h \
+ $(INC)/toast.h \
+ $(TLS)/taste.h
+
+# Sources
+
+GSM_SOURCES = $(SRC)/add.c \
+ $(SRC)/code.c \
+ $(SRC)/debug.c \
+ $(SRC)/decode.c \
+ $(SRC)/long_term.c \
+ $(SRC)/lpc.c \
+ $(SRC)/preprocess.c \
+ $(SRC)/rpe.c \
+ $(SRC)/gsm_destroy.c \
+ $(SRC)/gsm_decode.c \
+ $(SRC)/gsm_encode.c \
+ $(SRC)/gsm_explode.c \
+ $(SRC)/gsm_implode.c \
+ $(SRC)/gsm_create.c \
+ $(SRC)/gsm_print.c \
+ $(SRC)/gsm_option.c \
+ $(SRC)/short_term.c \
+ $(SRC)/table.c
+
+# add k6-specific code only if not on a non-k6 hardware or proc.
+# XXX Keep a space after each findstring argument
+# XXX should merge with GSM_OBJECTS
+ifeq ($(OSARCH),linux-gnu)
+ifeq (,$(findstring $(shell uname -m) , x86_64 amd64 ppc ppc64 alpha armv4l sparc64 parisc s390 ))
+ifeq (,$(findstring $(PROC) , arm armv5b armeb powerpc ia64 s390 bfin mipsel ))
+GSM_SOURCES+= $(SRC)/k6opt.s
+endif
+endif
+endif
+
+TOAST_SOURCES = $(SRC)/toast.c \
+ $(SRC)/toast_lin.c \
+ $(SRC)/toast_ulaw.c \
+ $(SRC)/toast_alaw.c \
+ $(SRC)/toast_audio.c
+
+SOURCES = $(GSM_SOURCES) \
+ $(TOAST_SOURCES) \
+ $(ADDTST)/add_test.c \
+ $(TLS)/sour.c \
+ $(TLS)/ginger.c \
+ $(TLS)/sour1.dta \
+ $(TLS)/sour2.dta \
+ $(TLS)/bitter.c \
+ $(TLS)/bitter.dta \
+ $(TLS)/taste.c \
+ $(TLS)/sweet.c \
+ $(TST)/cod2lin.c \
+ $(TST)/cod2txt.c \
+ $(TST)/gsm2cod.c \
+ $(TST)/lin2cod.c \
+ $(TST)/lin2txt.c
+
+# Object files
+
+GSM_OBJECTS = $(SRC)/add.o \
+ $(SRC)/code.o \
+ $(SRC)/debug.o \
+ $(SRC)/decode.o \
+ $(SRC)/long_term.o \
+ $(SRC)/lpc.o \
+ $(SRC)/preprocess.o \
+ $(SRC)/rpe.o \
+ $(SRC)/gsm_destroy.o \
+ $(SRC)/gsm_decode.o \
+ $(SRC)/gsm_encode.o \
+ $(SRC)/gsm_explode.o \
+ $(SRC)/gsm_implode.o \
+ $(SRC)/gsm_create.o \
+ $(SRC)/gsm_print.o \
+ $(SRC)/gsm_option.o \
+ $(SRC)/short_term.o \
+ $(SRC)/table.o
+
+ifeq ($(OSARCH),linux-gnu)
+ifeq (,$(findstring $(shell uname -m) , x86_64 amd64 ppc ppc64 alpha armv4l sparc64 parisc ))
+ifeq (,$(findstring $(PROC) , arm armv5b armeb powerpc ia64 bfin mipsel ))
+GSM_OBJECTS+= $(SRC)/k6opt.o
+endif
+endif
+endif
+
+TOAST_OBJECTS = $(SRC)/toast.o \
+ $(SRC)/toast_lin.o \
+ $(SRC)/toast_ulaw.o \
+ $(SRC)/toast_alaw.o \
+ $(SRC)/toast_audio.o
+
+OBJECTS = $(GSM_OBJECTS) $(TOAST_OBJECTS)
+
+# Manuals
+
+GSM_MANUALS = $(MAN)/gsm.3 \
+ $(MAN)/gsm_explode.3 \
+ $(MAN)/gsm_option.3 \
+ $(MAN)/gsm_print.3
+
+TOAST_MANUALS = $(MAN)/toast.1
+
+MANUALS = $(GSM_MANUALS) $(TOAST_MANUALS) $(MAN)/bitter.1
+
+# Other stuff in the distribution
+
+STUFF = ChangeLog \
+ INSTALL \
+ MACHINES \
+ MANIFEST \
+ Makefile \
+ README \
+ $(ADDTST)/add_test.dta \
+ $(TLS)/bitter.dta \
+ $(TST)/run
+
+
+# Install targets
+
+GSM_INSTALL_TARGETS = \
+ $(GSM_INSTALL_LIB)/libgsm.a \
+ $(GSM_INSTALL_INC)/gsm.h \
+ $(GSM_INSTALL_MAN)/gsm.3 \
+ $(GSM_INSTALL_MAN)/gsm_explode.3 \
+ $(GSM_INSTALL_MAN)/gsm_option.3 \
+ $(GSM_INSTALL_MAN)/gsm_print.3
+
+TOAST_INSTALL_TARGETS = \
+ $(TOAST_INSTALL_BIN)/toast \
+ $(TOAST_INSTALL_BIN)/tcat \
+ $(TOAST_INSTALL_BIN)/untoast \
+ $(TOAST_INSTALL_MAN)/toast.1
+
+
+# Default rules
+
+include $(ASTTOPDIR)/Makefile.rules
+
+# Target rules
+
+all: $(LIBGSM) $(LIBGSMSO) $(TOAST) $(TCAT) $(UNTOAST)
+ @-echo $(ROOT): Done.
+
+tst: $(TST)/lin2cod $(TST)/cod2lin $(TOAST) $(TST)/test-result
+ @-echo tst: Done.
+
+addtst: $(ADDTST)/add $(ADDTST)/add_test.dta
+ $(ADDTST)/add < $(ADDTST)/add_test.dta > /dev/null
+ @-echo addtst: Done.
+
+misc: $(TLS)/sweet $(TLS)/bitter $(TLS)/sour $(TLS)/ginger \
+ $(TST)/lin2txt $(TST)/cod2txt $(TST)/gsm2cod
+ @-echo misc: Done.
+
+install: toastinstall gsminstall
+ @-echo install: Done.
+
+
+# The basic API: libgsm
+
+$(LIBGSMSO): $(LIB) $(GSM_OBJECTS)
+ $(LD) -o $@.1.0.10 -shared -Xlinker -soname -Xlinker libgsm.so.1 $(GSM_OBJECTS) -lc
+ ln -fs libgsm.so.1.0.10 lib/libgsm.so.1
+ ln -fs libgsm.so.1.0.10 lib/libgsm.so
+
+$(LIBGSM): $(GSM_OBJECTS)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+
+# Toast, Untoast and Tcat -- the compress-like frontends to gsm.
+
+$(TOAST): $(BIN) $(TOAST_OBJECTS) $(LIBGSM)
+ $(LD) $(LFLAGS) -o $(TOAST) $(TOAST_OBJECTS) $(LIBGSMSO) $(LDLIB)
+
+$(UNTOAST): $(BIN) $(TOAST)
+ -rm $(RMFLAGS) $(UNTOAST)
+ $(LN) toast $(UNTOAST)
+
+$(TCAT): $(BIN) $(TOAST)
+ -rm $(RMFLAGS) $(TCAT)
+ $(LN) toast $(TCAT)
+
+
+# The local bin and lib directories
+
+$(BIN):
+ if [ ! -d $(BIN) ] ; then mkdir $(BIN) ; fi
+
+$(LIB):
+ if [ ! -d $(LIB) ] ; then mkdir $(LIB) ; fi
+
+
+# Installation
+
+gsminstall:
+ -if [ x"$(GSM_INSTALL_ROOT)" != x ] ; then \
+ $(MAKE) $(GSM_INSTALL_TARGETS) ; \
+ fi
+
+toastinstall:
+ -if [ x"$(TOAST_INSTALL_ROOT)" != x ]; then \
+ $(MAKE) $(TOAST_INSTALL_TARGETS); \
+ fi
+
+gsmuninstall:
+ -if [ x"$(GSM_INSTALL_ROOT)" != x ] ; then \
+ rm $(RMFLAGS) $(GSM_INSTALL_TARGETS) ; \
+ fi
+
+toastuninstall:
+ -if [ x"$(TOAST_INSTALL_ROOT)" != x ] ; then \
+ rm $(RMFLAGS) $(TOAST_INSTALL_TARGETS); \
+ fi
+
+$(TOAST_INSTALL_BIN)/toast: $(TOAST)
+ -rm $@
+ cp $(TOAST) $@
+ chmod 755 $@
+
+$(TOAST_INSTALL_BIN)/untoast: $(TOAST_INSTALL_BIN)/toast
+ -rm $@
+ ln $? $@
+
+$(TOAST_INSTALL_BIN)/tcat: $(TOAST_INSTALL_BIN)/toast
+ -rm $@
+ ln $? $@
+
+$(TOAST_INSTALL_MAN)/toast.1: $(MAN)/toast.1
+ -rm $@
+ cp $? $@
+ chmod 444 $@
+
+$(GSM_INSTALL_MAN)/gsm.3: $(MAN)/gsm.3
+ -rm $@
+ cp $? $@
+ chmod 444 $@
+
+$(GSM_INSTALL_MAN)/gsm_option.3: $(MAN)/gsm_option.3
+ -rm $@
+ cp $? $@
+ chmod 444 $@
+
+$(GSM_INSTALL_MAN)/gsm_explode.3: $(MAN)/gsm_explode.3
+ -rm $@
+ cp $? $@
+ chmod 444 $@
+
+$(GSM_INSTALL_MAN)/gsm_print.3: $(MAN)/gsm_print.3
+ -rm $@
+ cp $? $@
+ chmod 444 $@
+
+$(GSM_INSTALL_INC)/gsm.h: $(INC)/gsm.h
+ -rm $@
+ cp $? $@
+ chmod 444 $@
+
+$(GSM_INSTALL_LIB)/libgsm.a: $(LIBGSM)
+ -rm $@
+ cp $? $@
+ chmod 444 $@
+
+
+# Distribution
+
+dist: gsm-1.0.tar.Z
+ @echo dist: Done.
+
+gsm-1.0.tar.Z: $(STUFF) $(SOURCES) $(HEADERS) $(MANUALS)
+ ( cd $(ROOT)/..; \
+ tar cvf - `cat $(ROOT)/gsm-1.0/MANIFEST \
+ | sed '/^#/d'` \
+ ) | $(COMPRESS) $(COMPRESSFLAGS) > $(ROOT)/gsm-1.0.tar.Z
+
+# Clean
+
+uninstall: toastuninstall gsmuninstall
+ @-echo uninstall: Done.
+
+semi-clean:
+ -rm $(RMFLAGS) */*.o \
+ $(TST)/lin2cod $(TST)/lin2txt \
+ $(TST)/cod2lin $(TST)/cod2txt \
+ $(TST)/gsm2cod \
+ $(TST)/*.*.*
+ -$(FIND) . \( -name core -o -name foo \) \
+ -print | xargs rm $(RMFLAGS)
+
+clean: semi-clean
+ -rm $(RMFLAGS) $(LIBGSM) $(ADDTST)/add \
+ $(TOAST) $(TCAT) $(UNTOAST) \
+ $(ROOT)/gsm-1.0.tar.Z
+ rm -rf lib
+ rm -f .*.d
+
+# Two tools that helped me generate gsm_encode.c and gsm_decode.c,
+# but aren't generally needed to port this.
+
+$(TLS)/sweet: $(TLS)/sweet.o $(TLS)/taste.o
+ $(LD) $(LFLAGS) -o $(TLS)/sweet \
+ $(TLS)/sweet.o $(TLS)/taste.o $(LDLIB)
+
+$(TLS)/bitter: $(TLS)/bitter.o $(TLS)/taste.o
+ $(LD) $(LFLAGS) -o $(TLS)/bitter \
+ $(TLS)/bitter.o $(TLS)/taste.o $(LDLIB)
+
+# A version of the same family that Jeff Chilton used to implement
+# the WAV #49 GSM format.
+
+$(TLS)/ginger: $(TLS)/ginger.o $(TLS)/taste.o
+ $(LD) $(LFLAGS) -o $(TLS)/ginger \
+ $(TLS)/ginger.o $(TLS)/taste.o $(LDLIB)
+
+$(TLS)/sour: $(TLS)/sour.o $(TLS)/taste.o
+ $(LD) $(LFLAGS) -o $(TLS)/sour \
+ $(TLS)/sour.o $(TLS)/taste.o $(LDLIB)
+
+# Run $(ADDTST)/add < $(ADDTST)/add_test.dta to make sure the
+# basic arithmetic functions work as intended.
+
+$(ADDTST)/add: $(ADDTST)/add_test.o
+ $(LD) $(LFLAGS) -o $(ADDTST)/add $(ADDTST)/add_test.o $(LDLIB)
+
+
+# Various conversion programs between linear, text, .gsm and the code
+# format used by the tests we ran (.cod). We paid for the test data,
+# so I guess we can't just provide them with this package. Still,
+# if you happen to have them lying around, here's the code.
+#
+# You can use gsm2cod | cod2txt independently to look at what's
+# coded inside the compressed frames, although this shouldn't be
+# hard to roll on your own using the gsm_print() function from
+# the API.
+
+
+$(TST)/test-result: $(TST)/lin2cod $(TST)/cod2lin $(TOAST) $(TST)/run
+ ( cd $(TST); ./run )
+
+$(TST)/lin2txt: $(TST)/lin2txt.o $(LIBGSM)
+ $(LD) $(LFLAGS) -o $(TST)/lin2txt \
+ $(TST)/lin2txt.o $(LIBGSM) $(LDLIB)
+
+$(TST)/lin2cod: $(TST)/lin2cod.o $(LIBGSM)
+ $(LD) $(LFLAGS) -o $(TST)/lin2cod \
+ $(TST)/lin2cod.o $(LIBGSM) $(LDLIB)
+
+$(TST)/gsm2cod: $(TST)/gsm2cod.o $(LIBGSM)
+ $(LD) $(LFLAGS) -o $(TST)/gsm2cod \
+ $(TST)/gsm2cod.o $(LIBGSM) $(LDLIB)
+
+$(TST)/cod2txt: $(TST)/cod2txt.o $(LIBGSM)
+ $(LD) $(LFLAGS) -o $(TST)/cod2txt \
+ $(TST)/cod2txt.o $(LIBGSM) $(LDLIB)
+
+$(TST)/cod2lin: $(TST)/cod2lin.o $(LIBGSM)
+ $(LD) $(LFLAGS) -o $(TST)/cod2lin \
+ $(TST)/cod2lin.o $(LIBGSM) $(LDLIB)
diff --git a/trunk/codecs/gsm/README b/trunk/codecs/gsm/README
new file mode 100644
index 000000000..cb6af85cf
--- /dev/null
+++ b/trunk/codecs/gsm/README
@@ -0,0 +1,37 @@
+
+GSM 06.10 13 kbit/s RPE/LTP speech compression available
+--------------------------------------------------------
+
+The Communications and Operating Systems Research Group (KBS) at the
+Technische Universitaet Berlin is currently working on a set of
+UNIX-based tools for computer-mediated telecooperation that will be
+made freely available.
+
+As part of this effort we are publishing an implementation of the
+European GSM 06.10 provisional standard for full-rate speech
+transcoding, prI-ETS 300 036, which uses RPE/LTP (residual pulse
+excitation/long term prediction) coding at 13 kbit/s.
+
+GSM 06.10 compresses frames of 160 13-bit samples (8 kHz sampling
+rate, i.e. a frame rate of 50 Hz) into 260 bits; for compatibility
+with typical UNIX applications, our implementation turns frames of 160
+16-bit linear samples into 33-byte frames (1650 Bytes/s).
+The quality of the algorithm is good enough for reliable speaker
+recognition; even music often survives transcoding in recognizable
+form (given the bandwidth limitations of 8 kHz sampling rate).
+
+The interfaces offered are a front end modelled after compress(1), and
+a library API. Compression and decompression run faster than realtime
+on most SPARCstations. The implementation has been verified against the
+ETSI standard test patterns.
+
+Jutta Degener (jutta@cs.tu-berlin.de)
+Carsten Bormann (cabo@cs.tu-berlin.de)
+
+Communications and Operating Systems Research Group, TU Berlin
+Fax: +49.30.31425156, Phone: +49.30.31424315
+
+--
+Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
diff --git a/trunk/codecs/gsm/inc/config.h b/trunk/codecs/gsm/inc/config.h
new file mode 100644
index 000000000..e0b0632be
--- /dev/null
+++ b/trunk/codecs/gsm/inc/config.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header$*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#if 0
+efine SIGHANDLER_T int /* signal handlers are void */
+efine HAS_SYSV_SIGNAL 1 /* sigs not blocked/reset? */
+#endif
+
+#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */
+#if 0
+efine HAS_LIMITS_H 1 /* /usr/include/limits.h */
+#endif
+#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */
+#if 0
+efine HAS_ERRNO_DECL 1 /* errno.h declares errno */
+#endif
+
+#define HAS_FSTAT 1 /* fstat syscall */
+#define HAS_FCHMOD 1 /* fchmod syscall */
+#define HAS_CHMOD 1 /* chmod syscall */
+#define HAS_FCHOWN 1 /* fchown syscall */
+#define HAS_CHOWN 1 /* chown syscall */
+#if 0
+efine HAS__FSETMODE 1 /* _fsetmode -- set file mode */
+#endif
+
+#define HAS_STRING_H 1 /* /usr/include/string.h */
+#if 0
+efine HAS_STRINGS_H 1 /* /usr/include/strings.h */
+#endif
+
+#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */
+#define HAS_UTIME 1 /* POSIX utime(path, times) */
+#if 0
+efine HAS_UTIMES 1 /* use utimes() syscall instead */
+#endif
+#define HAS_UTIME_H 1 /* UTIME header file */
+#if 0
+efine HAS_UTIMBUF 1 /* struct utimbuf */
+efine HAS_UTIMEUSEC 1 /* microseconds in utimbuf? */
+#endif
+
+#endif /* CONFIG_H */
diff --git a/trunk/codecs/gsm/inc/gsm.h b/trunk/codecs/gsm/inc/gsm.h
new file mode 100644
index 000000000..81065e512
--- /dev/null
+++ b/trunk/codecs/gsm/inc/gsm.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header$*/
+
+#ifndef GSM_H
+#define GSM_H
+
+#ifdef __cplusplus
+# define NeedFunctionPrototypes 1
+#endif
+
+#if __STDC__
+# define NeedFunctionPrototypes 1
+#endif
+
+#ifdef _NO_PROTO
+# undef NeedFunctionPrototypes
+#endif
+
+#ifdef NeedFunctionPrototypes
+# include <stdio.h> /* for FILE * */
+#endif
+
+#undef GSM_P
+#if NeedFunctionPrototypes
+# define GSM_P( protos ) protos
+#else
+# define GSM_P( protos ) ( /* protos */ )
+#endif
+
+/*
+ * Interface
+ */
+
+typedef struct gsm_state * gsm;
+typedef short gsm_signal; /* signed 16 bit */
+typedef unsigned char gsm_byte;
+typedef gsm_byte gsm_frame[33]; /* 33 * 8 bits */
+
+#define GSM_MAGIC 0xD /* 13 kbit/s RPE-LTP */
+
+#define GSM_PATCHLEVEL 10
+#define GSM_MINOR 0
+#define GSM_MAJOR 1
+
+#define GSM_OPT_VERBOSE 1
+#define GSM_OPT_FAST 2
+#define GSM_OPT_LTP_CUT 3
+#define GSM_OPT_WAV49 4
+#define GSM_OPT_FRAME_INDEX 5
+#define GSM_OPT_FRAME_CHAIN 6
+
+extern gsm gsm_create GSM_P((void));
+extern void gsm_destroy GSM_P((gsm));
+
+extern int gsm_print GSM_P((FILE *, gsm, gsm_byte *));
+extern int gsm_option GSM_P((gsm, int, int *));
+
+extern void gsm_encode GSM_P((gsm, gsm_signal *, gsm_byte *));
+extern int gsm_decode GSM_P((gsm, gsm_byte *, gsm_signal *));
+
+extern int gsm_explode GSM_P((gsm, gsm_byte *, gsm_signal *));
+extern void gsm_implode GSM_P((gsm, gsm_signal *, gsm_byte *));
+
+#undef GSM_P
+
+#endif /* GSM_H */
diff --git a/trunk/codecs/gsm/inc/private.h b/trunk/codecs/gsm/inc/private.h
new file mode 100644
index 000000000..7021030ac
--- /dev/null
+++ b/trunk/codecs/gsm/inc/private.h
@@ -0,0 +1,312 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header$*/
+
+#ifndef PRIVATE_H
+#define PRIVATE_H
+
+typedef short word; /* 16 bit signed int */
+typedef long longword; /* 32 bit signed int */
+
+typedef unsigned short uword; /* unsigned word */
+typedef unsigned long ulongword; /* unsigned longword */
+
+struct gsm_state {
+
+ word dp0[ 280 ];
+
+ word z1; /* preprocessing.c, Offset_com. */
+ longword L_z2; /* Offset_com. */
+ int mp; /* Preemphasis */
+
+ word u[8]; /* short_term_aly_filter.c */
+ word LARpp[2][8]; /* */
+ word j; /* */
+
+ word ltp_cut; /* long_term.c, LTP crosscorr. */
+ word nrp; /* 40 */ /* long_term.c, synthesis */
+ word v[9]; /* short_term.c, synthesis */
+ word msr; /* decoder.c, Postprocessing */
+
+ char verbose; /* only used if !NDEBUG */
+ char fast; /* only used if FAST */
+
+ char wav_fmt; /* only used if WAV49 defined */
+ unsigned char frame_index; /* odd/even chaining */
+ unsigned char frame_chain; /* half-byte to carry forward */
+};
+
+
+#define MIN_WORD (-32767 - 1)
+#define MAX_WORD 32767
+
+#define MIN_LONGWORD (-2147483647 - 1)
+#define MAX_LONGWORD 2147483647
+
+#ifdef SASR /* flag: >> is a signed arithmetic shift right */
+#undef SASR
+#define SASR(x, by) ((x) >> (by))
+#else
+#define SASR(x, by) ((x) >= 0 ? (x) >> (by) : (~(-((x) + 1) >> (by))))
+#endif /* SASR */
+
+#include "proto.h"
+
+/*
+ * Prototypes from add.c
+ */
+extern word gsm_mult P((word a, word b));
+extern longword gsm_L_mult P((word a, word b));
+extern word gsm_mult_r P((word a, word b));
+
+extern word gsm_div P((word num, word denum));
+
+extern word gsm_add P(( word a, word b ));
+extern longword gsm_L_add P(( longword a, longword b ));
+
+extern word gsm_sub P((word a, word b));
+extern longword gsm_L_sub P((longword a, longword b));
+
+extern word gsm_abs P((word a));
+
+extern word gsm_norm P(( longword a ));
+
+extern longword gsm_L_asl P((longword a, int n));
+extern word gsm_asl P((word a, int n));
+
+extern longword gsm_L_asr P((longword a, int n));
+extern word gsm_asr P((word a, int n));
+
+/*
+ * Inlined functions from add.h
+ */
+
+/*
+ * #define GSM_MULT_R(a, b) (* word a, word b, !(a == b == MIN_WORD) *) \
+ * (0x0FFFF & SASR(((longword)(a) * (longword)(b) + 16384), 15))
+ */
+#define GSM_MULT_R(a, b) /* word a, word b, !(a == b == MIN_WORD) */ \
+ (SASR( ((longword)(a) * (longword)(b) + 16384), 15 ))
+
+# define GSM_MULT(a,b) /* word a, word b, !(a == b == MIN_WORD) */ \
+ (SASR( ((longword)(a) * (longword)(b)), 15 ))
+
+# define GSM_L_MULT(a, b) /* word a, word b */ \
+ (((longword)(a) * (longword)(b)) << 1)
+
+#if defined(__GNUC__) && defined(__i386__)
+
+static __inline__ int GSM_L_ADD(int a, int b)
+{
+ __asm__ __volatile__(
+
+ "addl %2,%0; jno 0f; movl $0x7fffffff,%0; adcl $0,%0; 0:"
+ : "=r" (a)
+ : "0" (a), "ir" (b)
+ : "cc"
+ );
+ return(a);
+}
+
+static __inline__ short GSM_ADD(short a, short b)
+{
+ __asm__ __volatile__(
+ "addw %2,%0; jno 0f; movw $0x7fff,%0; adcw $0,%0; 0:"
+ : "=r" (a)
+ : "0" (a), "ir" (b)
+ : "cc"
+ );
+ return(a);
+}
+
+static __inline__ short GSM_SUB(short a, short b)
+{
+ __asm__ __volatile__(
+ "subw %2,%0; jno 0f; movw $0x7fff,%0; adcw $0,%0; 0:"
+ : "=r" (a)
+ : "0" (a), "ir" (b)
+ : "cc"
+ );
+ return(a);
+}
+
+#else
+
+#ifdef WIN32
+#define inline __inline
+#define __inline__ __inline
+#endif
+
+# define GSM_L_ADD(a, b) \
+ ( (a) < 0 ? ( (b) >= 0 ? (a) + (b) \
+ : (utmp = (ulongword)-((a) + 1) + (ulongword)-((b) + 1)) \
+ >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)utmp-2 ) \
+ : ((b) <= 0 ? (a) + (b) \
+ : (utmp = (ulongword)(a) + (ulongword)(b)) >= MAX_LONGWORD \
+ ? MAX_LONGWORD : utmp))
+
+static inline word GSM_ADD(longword a, longword b)
+{
+ register longword ltmp;
+ ltmp = a + b;
+ return (word)((ulongword) (ltmp - MIN_WORD) > MAX_WORD - MIN_WORD ? (ltmp > 0 ? MAX_WORD : MIN_WORD) : ltmp);
+};
+
+static inline word GSM_SUB(longword a, longword b)
+{
+ register longword ltmp;
+ ltmp = a - b;
+ return (word)(ltmp >= MAX_WORD ? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp);
+};
+
+#endif
+
+# define GSM_ABS(a) ((a) < 0 ? ((a) == MIN_WORD ? MAX_WORD : -(a)) : (a))
+
+/* Use these if necessary:
+
+# define GSM_MULT_R(a, b) gsm_mult_r(a, b)
+# define GSM_MULT(a, b) gsm_mult(a, b)
+# define GSM_L_MULT(a, b) gsm_L_mult(a, b)
+
+# define GSM_L_ADD(a, b) gsm_L_add(a, b)
+# define GSM_ADD(a, b) gsm_add(a, b)
+# define GSM_SUB(a, b) gsm_sub(a, b)
+
+# define GSM_ABS(a) gsm_abs(a)
+
+*/
+
+/*
+ * More prototypes from implementations..
+ */
+extern void Gsm_Coder P((
+ struct gsm_state * S,
+ word * s, /* [0..159] samples IN */
+ word * LARc, /* [0..7] LAR coefficients OUT */
+ word * Nc, /* [0..3] LTP lag OUT */
+ word * bc, /* [0..3] coded LTP gain OUT */
+ word * Mc, /* [0..3] RPE grid selection OUT */
+ word * xmaxc,/* [0..3] Coded maximum amplitude OUT */
+ word * xMc /* [13*4] normalized RPE samples OUT */));
+
+extern void Gsm_Long_Term_Predictor P(( /* 4x for 160 samples */
+ struct gsm_state * S,
+ word * d, /* [0..39] residual signal IN */
+ word * dp, /* [-120..-1] d' IN */
+ word * e, /* [0..40] OUT */
+ word * dpp, /* [0..40] OUT */
+ word * Nc, /* correlation lag OUT */
+ word * bc /* gain factor OUT */));
+
+extern void Gsm_LPC_Analysis P((
+ struct gsm_state * S,
+ word * s, /* 0..159 signals IN/OUT */
+ word * LARc)); /* 0..7 LARc's OUT */
+
+extern void Gsm_Preprocess P((
+ struct gsm_state * S,
+ word * s, word * so));
+
+extern void Gsm_Encoding P((
+ struct gsm_state * S,
+ word * e,
+ word * ep,
+ word * xmaxc,
+ word * Mc,
+ word * xMc));
+
+extern void Gsm_Short_Term_Analysis_Filter P((
+ struct gsm_state * S,
+ word * LARc, /* coded log area ratio [0..7] IN */
+ word * d /* st res. signal [0..159] IN/OUT */));
+
+extern void Gsm_Decoder P((
+ struct gsm_state * S,
+ word * LARcr, /* [0..7] IN */
+ word * Ncr, /* [0..3] IN */
+ word * bcr, /* [0..3] IN */
+ word * Mcr, /* [0..3] IN */
+ word * xmaxcr, /* [0..3] IN */
+ word * xMcr, /* [0..13*4] IN */
+ word * s)); /* [0..159] OUT */
+
+extern void Gsm_Decoding P((
+ struct gsm_state * S,
+ word xmaxcr,
+ word Mcr,
+ word * xMcr, /* [0..12] IN */
+ word * erp)); /* [0..39] OUT */
+
+extern void Gsm_Long_Term_Synthesis_Filtering P((
+ struct gsm_state* S,
+ word Ncr,
+ word bcr,
+ word * erp, /* [0..39] IN */
+ word * drp)); /* [-120..-1] IN, [0..40] OUT */
+
+void Gsm_RPE_Decoding P((
+ struct gsm_state *S,
+ word xmaxcr,
+ word Mcr,
+ word * xMcr, /* [0..12], 3 bits IN */
+ word * erp)); /* [0..39] OUT */
+
+void Gsm_RPE_Encoding P((
+ struct gsm_state * S,
+ word * e, /* -5..-1][0..39][40..44 IN/OUT */
+ word * xmaxc, /* OUT */
+ word * Mc, /* OUT */
+ word * xMc)); /* [0..12] OUT */
+
+extern void Gsm_Short_Term_Synthesis_Filter P((
+ struct gsm_state * S,
+ word * LARcr, /* log area ratios [0..7] IN */
+ word * drp, /* received d [0...39] IN */
+ word * s)); /* signal s [0..159] OUT */
+
+extern void Gsm_Update_of_reconstructed_short_time_residual_signal P((
+ word * dpp, /* [0...39] IN */
+ word * ep, /* [0...39] IN */
+ word * dp)); /* [-120...-1] IN/OUT */
+
+/*
+ * Tables from table.c
+ */
+#ifndef GSM_TABLE_C
+
+extern word gsm_A[8], gsm_B[8], gsm_MIC[8], gsm_MAC[8];
+extern word gsm_INVA[8];
+extern word gsm_DLB[4], gsm_QLB[4];
+extern word gsm_H[11];
+extern word gsm_NRFAC[8];
+extern word gsm_FAC[8];
+
+#endif /* GSM_TABLE_C */
+
+/*
+ * Debugging
+ */
+#ifdef NDEBUG
+
+# define gsm_debug_words(a, b, c, d) /* nil */
+# define gsm_debug_longwords(a, b, c, d) /* nil */
+# define gsm_debug_word(a, b) /* nil */
+# define gsm_debug_longword(a, b) /* nil */
+
+#else /* !NDEBUG => DEBUG */
+
+ extern void gsm_debug_words P((char * name, int, int, word *));
+ extern void gsm_debug_longwords P((char * name, int, int, longword *));
+ extern void gsm_debug_longword P((char * name, longword));
+ extern void gsm_debug_word P((char * name, word));
+
+#endif /* !NDEBUG */
+
+#include "unproto.h"
+
+#endif /* PRIVATE_H */
diff --git a/trunk/codecs/gsm/inc/proto.h b/trunk/codecs/gsm/inc/proto.h
new file mode 100644
index 000000000..68e1ecf05
--- /dev/null
+++ b/trunk/codecs/gsm/inc/proto.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header$*/
+
+#ifndef PROTO_H
+#define PROTO_H
+
+#ifdef __cplusplus
+# define NeedFunctionPrototypes 1
+#endif
+
+#ifdef __STDC__
+# define NeedFunctionPrototypes 1
+#endif
+
+#ifdef _NO_PROTO
+# undef NeedFunctionPrototypes
+#endif
+
+#undef P /* gnu stdio.h actually defines this... */
+#undef P0
+#undef P1
+#undef P2
+#undef P3
+#undef P4
+#undef P5
+#undef P6
+#undef P7
+#undef P8
+
+#if NeedFunctionPrototypes
+
+# define P( protos ) protos
+
+# define P0() (void)
+# define P1(x, a) (a)
+# define P2(x, a, b) (a, b)
+# define P3(x, a, b, c) (a, b, c)
+# define P4(x, a, b, c, d) (a, b, c, d)
+# define P5(x, a, b, c, d, e) (a, b, c, d, e)
+# define P6(x, a, b, c, d, e, f) (a, b, c, d, e, f)
+# define P7(x, a, b, c, d, e, f, g) (a, b, c, d, e, f, g)
+# define P8(x, a, b, c, d, e, f, g, h) (a, b, c, d, e, f, g, h)
+
+#else /* !NeedFunctionPrototypes */
+
+# define P( protos ) ( /* protos */ )
+
+# define P0() ()
+# define P1(x, a) x a;
+# define P2(x, a, b) x a; b;
+# define P3(x, a, b, c) x a; b; c;
+# define P4(x, a, b, c, d) x a; b; c; d;
+# define P5(x, a, b, c, d, e) x a; b; c; d; e;
+# define P6(x, a, b, c, d, e, f) x a; b; c; d; e; f;
+# define P7(x, a, b, c, d, e, f, g) x a; b; c; d; e; f; g;
+# define P8(x, a, b, c, d, e, f, g, h) x a; b; c; d; e; f; g; h;
+
+#endif /* !NeedFunctionPrototypes */
+
+#endif /* PROTO_H */
diff --git a/trunk/codecs/gsm/inc/unproto.h b/trunk/codecs/gsm/inc/unproto.h
new file mode 100644
index 000000000..ccd565109
--- /dev/null
+++ b/trunk/codecs/gsm/inc/unproto.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header$*/
+
+#ifdef PROTO_H /* sic */
+#undef PROTO_H
+
+#undef P
+#undef P0
+#undef P1
+#undef P2
+#undef P3
+#undef P4
+#undef P5
+#undef P6
+#undef P7
+#undef P8
+
+#endif /* PROTO_H */
diff --git a/trunk/codecs/gsm/libgsm.vcproj b/trunk/codecs/gsm/libgsm.vcproj
new file mode 100644
index 000000000..f94b209d5
--- /dev/null
+++ b/trunk/codecs/gsm/libgsm.vcproj
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="libgsm"
+ ProjectGUID="{8FD2E297-4096-47E5-9258-C48FF1841523}"
+ RootNamespace="libgsm"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="inc"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="inc"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\src\add.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\code.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\debug.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\decode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_create.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_decode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_destroy.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_encode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_explode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_implode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_option.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\gsm_print.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\long_term.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\lpc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\preprocess.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\rpe.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\short_term.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\table.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\inc\config.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\gsm.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\private.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\proto.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\unproto.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/trunk/codecs/gsm/src/add.c b/trunk/codecs/gsm/src/add.c
new file mode 100644
index 000000000..f23d27f16
--- /dev/null
+++ b/trunk/codecs/gsm/src/add.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+/*
+ * See private.h for the more commonly used macro versions.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+#define saturate(x) \
+ ((x) < MIN_WORD ? MIN_WORD : (x) > MAX_WORD ? MAX_WORD: (x))
+
+word gsm_add P2((a,b), word a, word b)
+{
+ longword sum = (longword)a + (longword)b;
+ return (word)saturate(sum);
+}
+
+word gsm_sub P2((a,b), word a, word b)
+{
+ longword diff = (longword)a - (longword)b;
+ return (word)saturate(diff);
+}
+
+word gsm_mult P2((a,b), word a, word b)
+{
+ if (a == MIN_WORD && b == MIN_WORD) return MAX_WORD;
+ else return (word)SASR( (longword)a * (longword)b, 15 );
+}
+
+word gsm_mult_r P2((a,b), word a, word b)
+{
+ if (b == MIN_WORD && a == MIN_WORD) return MAX_WORD;
+ else {
+ longword prod = (longword)a * (longword)b + 16384;
+ prod >>= 15;
+ return (word)(prod & 0xFFFF);
+ }
+}
+
+word gsm_abs P1((a), word a)
+{
+ return a < 0 ? (a == MIN_WORD ? MAX_WORD : -a) : a;
+}
+
+longword gsm_L_mult P2((a,b),word a, word b)
+{
+ assert( a != MIN_WORD || b != MIN_WORD );
+ return ((longword)a * (longword)b) << 1;
+}
+
+longword gsm_L_add P2((a,b), longword a, longword b)
+{
+ if (a < 0) {
+ if (b >= 0) return a + b;
+ else {
+ ulongword A = (ulongword)-(a + 1) + (ulongword)-(b + 1);
+ return A >= MAX_LONGWORD ? MIN_LONGWORD :-(longword)A-2;
+ }
+ }
+ else if (b <= 0) return a + b;
+ else {
+ ulongword A = (ulongword)a + (ulongword)b;
+ return A > MAX_LONGWORD ? MAX_LONGWORD : A;
+ }
+}
+
+longword gsm_L_sub P2((a,b), longword a, longword b)
+{
+ if (a >= 0) {
+ if (b >= 0) return a - b;
+ else {
+ /* a>=0, b<0 */
+
+ ulongword A = (ulongword)a + -(b + 1);
+ return A >= MAX_LONGWORD ? MAX_LONGWORD : (A + 1);
+ }
+ }
+ else if (b <= 0) return a - b;
+ else {
+ /* a<0, b>0 */
+
+ ulongword A = (ulongword)-(a + 1) + b;
+ return A >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)A - 1;
+ }
+}
+
+static unsigned char const bitoff[ 256 ] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+word gsm_norm P1((a), longword a )
+/*
+ * the number of left shifts needed to normalize the 32 bit
+ * variable L_var1 for positive values on the interval
+ *
+ * with minimum of
+ * minimum of 1073741824 (01000000000000000000000000000000) and
+ * maximum of 2147483647 (01111111111111111111111111111111)
+ *
+ *
+ * and for negative values on the interval with
+ * minimum of -2147483648 (-10000000000000000000000000000000) and
+ * maximum of -1073741824 ( -1000000000000000000000000000000).
+ *
+ * in order to normalize the result, the following
+ * operation must be done: L_norm_var1 = L_var1 << norm( L_var1 );
+ *
+ * (That's 'ffs', only from the left, not the right..)
+ */
+{
+ assert(a != 0);
+
+ if (a < 0) {
+ if (a <= -1073741824) return 0;
+ a = ~a;
+ }
+
+ return a & 0xffff0000
+ ? ( a & 0xff000000
+ ? -1 + bitoff[ 0xFF & (a >> 24) ]
+ : 7 + bitoff[ 0xFF & (a >> 16) ] )
+ : ( a & 0xff00
+ ? 15 + bitoff[ 0xFF & (a >> 8) ]
+ : 23 + bitoff[ 0xFF & a ] );
+}
+
+longword gsm_L_asl P2((a,n), longword a, int n)
+{
+ if (n >= 32) return 0;
+ if (n <= -32) return -(a < 0);
+ if (n < 0) return gsm_L_asr(a, -n);
+ return a << n;
+}
+
+word gsm_asl P2((a,n), word a, int n)
+{
+ if (n >= 16) return 0;
+ if (n <= -16) return -(a < 0);
+ if (n < 0) return gsm_asr(a, -n);
+ return a << n;
+}
+
+longword gsm_L_asr P2((a,n), longword a, int n)
+{
+ if (n >= 32) return -(a < 0);
+ if (n <= -32) return 0;
+ if (n < 0) return a << -n;
+
+# ifdef SASR
+ return a >> n;
+# else
+ if (a >= 0) return a >> n;
+ else return -(longword)( -(ulongword)a >> n );
+# endif
+}
+
+word gsm_asr P2((a,n), word a, int n)
+{
+ if (n >= 16) return -(a < 0);
+ if (n <= -16) return 0;
+ if (n < 0) return a << -n;
+
+# ifdef SASR
+ return a >> n;
+# else
+ if (a >= 0) return a >> n;
+ else return -(word)( -(uword)a >> n );
+# endif
+}
+
+/*
+ * (From p. 46, end of section 4.2.5)
+ *
+ * NOTE: The following lines gives [sic] one correct implementation
+ * of the div(num, denum) arithmetic operation. Compute div
+ * which is the integer division of num by denum: with denum
+ * >= num > 0
+ */
+
+word gsm_div P2((num,denum), word num, word denum)
+{
+ longword L_num = num;
+ longword L_denum = denum;
+ word div = 0;
+ int k = 15;
+
+ /* The parameter num sometimes becomes zero.
+ * Although this is explicitly guarded against in 4.2.5,
+ * we assume that the result should then be zero as well.
+ */
+
+ /* assert(num != 0); */
+
+ assert(num >= 0 && denum >= num);
+ if (num == 0)
+ return 0;
+
+ while (k--) {
+ div <<= 1;
+ L_num <<= 1;
+
+ if (L_num >= L_denum) {
+ L_num -= L_denum;
+ div++;
+ }
+ }
+
+ return div;
+}
diff --git a/trunk/codecs/gsm/src/code.c b/trunk/codecs/gsm/src/code.c
new file mode 100644
index 000000000..4d195dfbd
--- /dev/null
+++ b/trunk/codecs/gsm/src/code.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "config.h"
+
+#ifdef HAS_STRING_H
+#include <string.h>
+#else
+# include "proto.h"
+ extern char * memcpy P((char *, char *, int));
+#endif
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+/*
+ * 4.2 FIXED POINT IMPLEMENTATION OF THE RPE-LTP CODER
+ */
+
+void Gsm_Coder P8((S,s,LARc,Nc,bc,Mc,xmaxc,xMc),
+
+ struct gsm_state * S,
+
+ word * s, /* [0..159] samples IN */
+
+/*
+ * The RPE-LTD coder works on a frame by frame basis. The length of
+ * the frame is equal to 160 samples. Some computations are done
+ * once per frame to produce at the output of the coder the
+ * LARc[1..8] parameters which are the coded LAR coefficients and
+ * also to realize the inverse filtering operation for the entire
+ * frame (160 samples of signal d[0..159]). These parts produce at
+ * the output of the coder:
+ */
+
+ word * LARc, /* [0..7] LAR coefficients OUT */
+
+/*
+ * Procedure 4.2.11 to 4.2.18 are to be executed four times per
+ * frame. That means once for each sub-segment RPE-LTP analysis of
+ * 40 samples. These parts produce at the output of the coder:
+ */
+
+ word * Nc, /* [0..3] LTP lag OUT */
+ word * bc, /* [0..3] coded LTP gain OUT */
+ word * Mc, /* [0..3] RPE grid selection OUT */
+ word * xmaxc,/* [0..3] Coded maximum amplitude OUT */
+ word * xMc /* [13*4] normalized RPE samples OUT */
+)
+{
+ int k;
+ word * dp = S->dp0 + 120; /* [ -120...-1 ] */
+ word * dpp = dp; /* [ 0...39 ] */
+
+ static word e[50];
+
+ word so[160];
+
+ Gsm_Preprocess (S, s, so);
+ Gsm_LPC_Analysis (S, so, LARc);
+ Gsm_Short_Term_Analysis_Filter (S, LARc, so);
+
+ for (k = 0; k <= 3; k++, xMc += 13) {
+
+ Gsm_Long_Term_Predictor ( S,
+ so+k*40, /* d [0..39] IN */
+ dp, /* dp [-120..-1] IN */
+ e + 5, /* e [0..39] OUT */
+ dpp, /* dpp [0..39] OUT */
+ Nc++,
+ bc++);
+
+ Gsm_RPE_Encoding ( S,
+ e + 5, /* e ][0..39][ IN/OUT */
+ xmaxc++, Mc++, xMc );
+ /*
+ * Gsm_Update_of_reconstructed_short_time_residual_signal
+ * ( dpp, e + 5, dp );
+ */
+
+ { register int i;
+ for (i = 0; i <= 39; i++)
+ dp[ i ] = GSM_ADD( e[5 + i], dpp[i] );
+ }
+ dp += 40;
+ dpp += 40;
+
+ }
+ (void)memcpy( (char *)S->dp0, (char *)(S->dp0 + 160),
+ 120 * sizeof(*S->dp0) );
+}
diff --git a/trunk/codecs/gsm/src/debug.c b/trunk/codecs/gsm/src/debug.c
new file mode 100644
index 000000000..22dfa8082
--- /dev/null
+++ b/trunk/codecs/gsm/src/debug.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "private.h"
+
+#ifndef NDEBUG
+
+/* If NDEBUG _is_ defined and no debugging should be performed,
+ * calls to functions in this module are #defined to nothing
+ * in private.h.
+ */
+
+#include <stdio.h>
+#include "proto.h"
+
+void gsm_debug_words P4( (name, from, to, ptr),
+ char * name,
+ int from,
+ int to,
+ word * ptr)
+{
+ int nprinted = 0;
+
+ fprintf( stderr, "%s [%d .. %d]: ", name, from, to );
+ while (from <= to) {
+ fprintf(stderr, "%d ", ptr[ from ] );
+ from++;
+ if (nprinted++ >= 7) {
+ nprinted = 0;
+ if (from < to) putc('\n', stderr);
+ }
+ }
+ putc('\n', stderr);
+}
+
+void gsm_debug_longwords P4( (name, from, to, ptr),
+ char * name,
+ int from,
+ int to,
+ longword * ptr)
+{
+ int nprinted = 0;
+
+ fprintf( stderr, "%s [%d .. %d]: ", name, from, to );
+ while (from <= to) {
+
+ fprintf(stderr, "%d ", ptr[ from ] );
+ from++;
+ if (nprinted++ >= 7) {
+ nprinted = 0;
+ if (from < to) putc('\n', stderr);
+ }
+ }
+ putc('\n', stderr);
+}
+
+void gsm_debug_longword P2( (name, value),
+ char * name,
+ longword value )
+{
+ fprintf(stderr, "%s: %d\n", name, (long)value );
+}
+
+void gsm_debug_word P2( (name, value),
+ char * name,
+ word value )
+{
+ fprintf(stderr, "%s: %d\n", name, (long)value);
+}
+
+#endif
diff --git a/trunk/codecs/gsm/src/decode.c b/trunk/codecs/gsm/src/decode.c
new file mode 100644
index 000000000..093ac64df
--- /dev/null
+++ b/trunk/codecs/gsm/src/decode.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include <stdio.h>
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+/*
+ * 4.3 FIXED POINT IMPLEMENTATION OF THE RPE-LTP DECODER
+ */
+
+static void Postprocessing P2((S,s),
+ struct gsm_state * S,
+ register word * s)
+{
+ register int k;
+ register word msr = S->msr;
+ register word tmp;
+
+ for (k = 160; k--; s++) {
+ tmp = (word)GSM_MULT_R( msr, 28180 );
+ msr = GSM_ADD(*s, tmp); /* Deemphasis */
+ *s = GSM_ADD(msr, msr) & 0xFFF8; /* Truncation & Upscaling */
+ }
+ S->msr = msr;
+}
+
+void Gsm_Decoder P8((S,LARcr, Ncr,bcr,Mcr,xmaxcr,xMcr,s),
+ struct gsm_state * S,
+
+ word * LARcr, /* [0..7] IN */
+
+ word * Ncr, /* [0..3] IN */
+ word * bcr, /* [0..3] IN */
+ word * Mcr, /* [0..3] IN */
+ word * xmaxcr, /* [0..3] IN */
+ word * xMcr, /* [0..13*4] IN */
+
+ word * s) /* [0..159] OUT */
+{
+ int j, k;
+ word erp[40], wt[160];
+ word * drp = S->dp0 + 120;
+
+ for (j=0; j <= 3; j++, xmaxcr++, bcr++, Ncr++, Mcr++, xMcr += 13) {
+
+ Gsm_RPE_Decoding( S, *xmaxcr, *Mcr, xMcr, erp );
+ Gsm_Long_Term_Synthesis_Filtering( S, *Ncr, *bcr, erp, drp );
+
+ for (k = 0; k <= 39; k++) wt[ j * 40 + k ] = drp[ k ];
+ }
+
+ Gsm_Short_Term_Synthesis_Filter( S, LARcr, wt, s );
+ Postprocessing(S, s);
+}
diff --git a/trunk/codecs/gsm/src/gsm_create.c b/trunk/codecs/gsm/src/gsm_create.c
new file mode 100644
index 000000000..a59aa2f2a
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_create.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+static char const ident[] = "$Header$";
+
+#include "config.h"
+
+#ifdef HAS_STRING_H
+#include <string.h>
+#else
+# include "proto.h"
+ extern char * memset P((char *, int, int));
+#endif
+
+#ifdef HAS_STDLIB_H
+# include <stdlib.h>
+#else
+# ifdef HAS_MALLOC_H
+# include <malloc.h>
+# else
+ extern char * malloc();
+# endif
+#endif
+
+#include <stdio.h>
+
+#include "gsm.h"
+#include "private.h"
+#include "proto.h"
+
+gsm gsm_create P0()
+{
+ gsm r;
+
+ r = (gsm)malloc(sizeof(struct gsm_state));
+ if (!r) return r;
+
+ memset((char *)r, 0, sizeof(*r));
+ r->nrp = 40;
+
+ return r;
+}
diff --git a/trunk/codecs/gsm/src/gsm_decode.c b/trunk/codecs/gsm/src/gsm_decode.c
new file mode 100644
index 000000000..7318ba2d4
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_decode.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_decode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target)
+{
+ word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+#ifdef WAV49
+ if (s->wav_fmt) {
+
+ uword sr = 0;
+
+ s->frame_index = !s->frame_index;
+ if (s->frame_index) {
+
+ sr = *c++;
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 2;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 4;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 2;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2; /* 5 */
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+ xmc[0] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 10 */
+ xmc[6] = sr & 0x7; sr >>= 3;
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+ xmc[13] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 15 */
+ xmc[14] = sr & 0x7; sr >>= 3;
+ xmc[15] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4; /* 20 */
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+ xmc[26] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 25 */
+ xmc[35] = sr & 0x7; sr >>= 3;
+ xmc[36] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+ xmc[39] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 30 */
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ xmc[44] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+
+ s->frame_chain = sr & 0xf;
+ }
+ else {
+ sr = s->frame_chain;
+ sr |= (uword)*c++ << 4; /* 1 */
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr = *c++;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 3;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 5 */
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+ xmc[0] = sr & 0x7; sr >>= 3;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ xmc[6] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 10 */
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+ xmc[13] = sr & 0x7; sr >>= 3;
+ xmc[14] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 15 */
+ xmc[15] = sr & 0x7; sr >>= 3;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1; /* 20 */
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+ xmc[26] = sr & 0x7; sr >>= 3;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ xmc[35] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 25 */
+ xmc[36] = sr & 0x7; sr >>= 3;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+ xmc[39] = sr & 0x7; sr >>= 3;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 30 */
+ xmc[44] = sr & 0x7; sr >>= 3;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+ }
+ }
+ else
+#endif
+ {
+ /* GSM_MAGIC = (*c >> 4) & 0xF; */
+
+ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
+
+ LARc[0] = (*c++ & 0xF) << 2; /* 1 */
+ LARc[0] |= (*c >> 6) & 0x3;
+ LARc[1] = *c++ & 0x3F;
+ LARc[2] = (*c >> 3) & 0x1F;
+ LARc[3] = (*c++ & 0x7) << 2;
+ LARc[3] |= (*c >> 6) & 0x3;
+ LARc[4] = (*c >> 2) & 0xF;
+ LARc[5] = (*c++ & 0x3) << 2;
+ LARc[5] |= (*c >> 6) & 0x3;
+ LARc[6] = (*c >> 3) & 0x7;
+ LARc[7] = *c++ & 0x7;
+ Nc[0] = (*c >> 1) & 0x7F;
+ bc[0] = (*c++ & 0x1) << 1;
+ bc[0] |= (*c >> 7) & 0x1;
+ Mc[0] = (*c >> 5) & 0x3;
+ xmaxc[0] = (*c++ & 0x1F) << 1;
+ xmaxc[0] |= (*c >> 7) & 0x1;
+ xmc[0] = (*c >> 4) & 0x7;
+ xmc[1] = (*c >> 1) & 0x7;
+ xmc[2] = (*c++ & 0x1) << 2;
+ xmc[2] |= (*c >> 6) & 0x3;
+ xmc[3] = (*c >> 3) & 0x7;
+ xmc[4] = *c++ & 0x7;
+ xmc[5] = (*c >> 5) & 0x7;
+ xmc[6] = (*c >> 2) & 0x7;
+ xmc[7] = (*c++ & 0x3) << 1; /* 10 */
+ xmc[7] |= (*c >> 7) & 0x1;
+ xmc[8] = (*c >> 4) & 0x7;
+ xmc[9] = (*c >> 1) & 0x7;
+ xmc[10] = (*c++ & 0x1) << 2;
+ xmc[10] |= (*c >> 6) & 0x3;
+ xmc[11] = (*c >> 3) & 0x7;
+ xmc[12] = *c++ & 0x7;
+ Nc[1] = (*c >> 1) & 0x7F;
+ bc[1] = (*c++ & 0x1) << 1;
+ bc[1] |= (*c >> 7) & 0x1;
+ Mc[1] = (*c >> 5) & 0x3;
+ xmaxc[1] = (*c++ & 0x1F) << 1;
+ xmaxc[1] |= (*c >> 7) & 0x1;
+ xmc[13] = (*c >> 4) & 0x7;
+ xmc[14] = (*c >> 1) & 0x7;
+ xmc[15] = (*c++ & 0x1) << 2;
+ xmc[15] |= (*c >> 6) & 0x3;
+ xmc[16] = (*c >> 3) & 0x7;
+ xmc[17] = *c++ & 0x7;
+ xmc[18] = (*c >> 5) & 0x7;
+ xmc[19] = (*c >> 2) & 0x7;
+ xmc[20] = (*c++ & 0x3) << 1;
+ xmc[20] |= (*c >> 7) & 0x1;
+ xmc[21] = (*c >> 4) & 0x7;
+ xmc[22] = (*c >> 1) & 0x7;
+ xmc[23] = (*c++ & 0x1) << 2;
+ xmc[23] |= (*c >> 6) & 0x3;
+ xmc[24] = (*c >> 3) & 0x7;
+ xmc[25] = *c++ & 0x7;
+ Nc[2] = (*c >> 1) & 0x7F;
+ bc[2] = (*c++ & 0x1) << 1; /* 20 */
+ bc[2] |= (*c >> 7) & 0x1;
+ Mc[2] = (*c >> 5) & 0x3;
+ xmaxc[2] = (*c++ & 0x1F) << 1;
+ xmaxc[2] |= (*c >> 7) & 0x1;
+ xmc[26] = (*c >> 4) & 0x7;
+ xmc[27] = (*c >> 1) & 0x7;
+ xmc[28] = (*c++ & 0x1) << 2;
+ xmc[28] |= (*c >> 6) & 0x3;
+ xmc[29] = (*c >> 3) & 0x7;
+ xmc[30] = *c++ & 0x7;
+ xmc[31] = (*c >> 5) & 0x7;
+ xmc[32] = (*c >> 2) & 0x7;
+ xmc[33] = (*c++ & 0x3) << 1;
+ xmc[33] |= (*c >> 7) & 0x1;
+ xmc[34] = (*c >> 4) & 0x7;
+ xmc[35] = (*c >> 1) & 0x7;
+ xmc[36] = (*c++ & 0x1) << 2;
+ xmc[36] |= (*c >> 6) & 0x3;
+ xmc[37] = (*c >> 3) & 0x7;
+ xmc[38] = *c++ & 0x7;
+ Nc[3] = (*c >> 1) & 0x7F;
+ bc[3] = (*c++ & 0x1) << 1;
+ bc[3] |= (*c >> 7) & 0x1;
+ Mc[3] = (*c >> 5) & 0x3;
+ xmaxc[3] = (*c++ & 0x1F) << 1;
+ xmaxc[3] |= (*c >> 7) & 0x1;
+ xmc[39] = (*c >> 4) & 0x7;
+ xmc[40] = (*c >> 1) & 0x7;
+ xmc[41] = (*c++ & 0x1) << 2;
+ xmc[41] |= (*c >> 6) & 0x3;
+ xmc[42] = (*c >> 3) & 0x7;
+ xmc[43] = *c++ & 0x7; /* 30 */
+ xmc[44] = (*c >> 5) & 0x7;
+ xmc[45] = (*c >> 2) & 0x7;
+ xmc[46] = (*c++ & 0x3) << 1;
+ xmc[46] |= (*c >> 7) & 0x1;
+ xmc[47] = (*c >> 4) & 0x7;
+ xmc[48] = (*c >> 1) & 0x7;
+ xmc[49] = (*c++ & 0x1) << 2;
+ xmc[49] |= (*c >> 6) & 0x3;
+ xmc[50] = (*c >> 3) & 0x7;
+ xmc[51] = *c & 0x7; /* 33 */
+ }
+
+ Gsm_Decoder(s, LARc, Nc, bc, Mc, xmaxc, xmc, target);
+
+ return 0;
+}
diff --git a/trunk/codecs/gsm/src/gsm_destroy.c b/trunk/codecs/gsm/src/gsm_destroy.c
new file mode 100644
index 000000000..4807c0acd
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_destroy.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "gsm.h"
+#include "config.h"
+#include "proto.h"
+
+#ifdef HAS_STDLIB_H
+# include <stdlib.h>
+#else
+# ifdef HAS_MALLOC_H
+# include <malloc.h>
+# else
+ extern void free();
+# endif
+#endif
+
+void gsm_destroy P1((S), gsm S)
+{
+ if (S) free((char *)S);
+}
diff --git a/trunk/codecs/gsm/src/gsm_encode.c b/trunk/codecs/gsm/src/gsm_encode.c
new file mode 100644
index 000000000..62338300e
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_encode.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+void gsm_encode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c)
+{
+ word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+ Gsm_Coder(s, source, LARc, Nc, bc, Mc, xmaxc, xmc);
+
+
+ /* variable size
+
+ GSM_MAGIC 4
+
+ LARc[0] 6
+ LARc[1] 6
+ LARc[2] 5
+ LARc[3] 5
+ LARc[4] 4
+ LARc[5] 4
+ LARc[6] 3
+ LARc[7] 3
+
+ Nc[0] 7
+ bc[0] 2
+ Mc[0] 2
+ xmaxc[0] 6
+ xmc[0] 3
+ xmc[1] 3
+ xmc[2] 3
+ xmc[3] 3
+ xmc[4] 3
+ xmc[5] 3
+ xmc[6] 3
+ xmc[7] 3
+ xmc[8] 3
+ xmc[9] 3
+ xmc[10] 3
+ xmc[11] 3
+ xmc[12] 3
+
+ Nc[1] 7
+ bc[1] 2
+ Mc[1] 2
+ xmaxc[1] 6
+ xmc[13] 3
+ xmc[14] 3
+ xmc[15] 3
+ xmc[16] 3
+ xmc[17] 3
+ xmc[18] 3
+ xmc[19] 3
+ xmc[20] 3
+ xmc[21] 3
+ xmc[22] 3
+ xmc[23] 3
+ xmc[24] 3
+ xmc[25] 3
+
+ Nc[2] 7
+ bc[2] 2
+ Mc[2] 2
+ xmaxc[2] 6
+ xmc[26] 3
+ xmc[27] 3
+ xmc[28] 3
+ xmc[29] 3
+ xmc[30] 3
+ xmc[31] 3
+ xmc[32] 3
+ xmc[33] 3
+ xmc[34] 3
+ xmc[35] 3
+ xmc[36] 3
+ xmc[37] 3
+ xmc[38] 3
+
+ Nc[3] 7
+ bc[3] 2
+ Mc[3] 2
+ xmaxc[3] 6
+ xmc[39] 3
+ xmc[40] 3
+ xmc[41] 3
+ xmc[42] 3
+ xmc[43] 3
+ xmc[44] 3
+ xmc[45] 3
+ xmc[46] 3
+ xmc[47] 3
+ xmc[48] 3
+ xmc[49] 3
+ xmc[50] 3
+ xmc[51] 3
+ */
+
+#ifdef WAV49
+
+ if (s->wav_fmt) {
+ s->frame_index = !s->frame_index;
+ if (s->frame_index) {
+
+ uword sr;
+
+ sr = 0;
+ sr = sr >> 6 | LARc[0] << 10;
+ sr = sr >> 6 | LARc[1] << 10;
+ *c++ = sr >> 4;
+ sr = sr >> 5 | LARc[2] << 11;
+ *c++ = sr >> 7;
+ sr = sr >> 5 | LARc[3] << 11;
+ sr = sr >> 4 | LARc[4] << 12;
+ *c++ = sr >> 6;
+ sr = sr >> 4 | LARc[5] << 12;
+ sr = sr >> 3 | LARc[6] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | LARc[7] << 13;
+ sr = sr >> 7 | Nc[0] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[0] << 14;
+ sr = sr >> 2 | Mc[0] << 14;
+ sr = sr >> 6 | xmaxc[0] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[0] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[1] << 13;
+ sr = sr >> 3 | xmc[2] << 13;
+ sr = sr >> 3 | xmc[3] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[4] << 13;
+ sr = sr >> 3 | xmc[5] << 13;
+ sr = sr >> 3 | xmc[6] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[7] << 13;
+ sr = sr >> 3 | xmc[8] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[9] << 13;
+ sr = sr >> 3 | xmc[10] << 13;
+ sr = sr >> 3 | xmc[11] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[12] << 13;
+ sr = sr >> 7 | Nc[1] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[1] << 14;
+ sr = sr >> 2 | Mc[1] << 14;
+ sr = sr >> 6 | xmaxc[1] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[13] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[14] << 13;
+ sr = sr >> 3 | xmc[15] << 13;
+ sr = sr >> 3 | xmc[16] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[17] << 13;
+ sr = sr >> 3 | xmc[18] << 13;
+ sr = sr >> 3 | xmc[19] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[20] << 13;
+ sr = sr >> 3 | xmc[21] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[22] << 13;
+ sr = sr >> 3 | xmc[23] << 13;
+ sr = sr >> 3 | xmc[24] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[25] << 13;
+ sr = sr >> 7 | Nc[2] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[2] << 14;
+ sr = sr >> 2 | Mc[2] << 14;
+ sr = sr >> 6 | xmaxc[2] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[26] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[27] << 13;
+ sr = sr >> 3 | xmc[28] << 13;
+ sr = sr >> 3 | xmc[29] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[30] << 13;
+ sr = sr >> 3 | xmc[31] << 13;
+ sr = sr >> 3 | xmc[32] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[33] << 13;
+ sr = sr >> 3 | xmc[34] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[35] << 13;
+ sr = sr >> 3 | xmc[36] << 13;
+ sr = sr >> 3 | xmc[37] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[38] << 13;
+ sr = sr >> 7 | Nc[3] << 9;
+ *c++ = sr >> 5;
+ sr = sr >> 2 | bc[3] << 14;
+ sr = sr >> 2 | Mc[3] << 14;
+ sr = sr >> 6 | xmaxc[3] << 10;
+ *c++ = sr >> 3;
+ sr = sr >> 3 | xmc[39] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[40] << 13;
+ sr = sr >> 3 | xmc[41] << 13;
+ sr = sr >> 3 | xmc[42] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[43] << 13;
+ sr = sr >> 3 | xmc[44] << 13;
+ sr = sr >> 3 | xmc[45] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[46] << 13;
+ sr = sr >> 3 | xmc[47] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[48] << 13;
+ sr = sr >> 3 | xmc[49] << 13;
+ sr = sr >> 3 | xmc[50] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[51] << 13;
+ sr = sr >> 4;
+ *c = sr >> 8;
+ s->frame_chain = *c;
+ }
+ else {
+ uword sr;
+
+ sr = 0;
+ sr = sr >> 4 | s->frame_chain << 12;
+ sr = sr >> 6 | LARc[0] << 10;
+ *c++ = sr >> 6;
+ sr = sr >> 6 | LARc[1] << 10;
+ *c++ = sr >> 8;
+ sr = sr >> 5 | LARc[2] << 11;
+ sr = sr >> 5 | LARc[3] << 11;
+ *c++ = sr >> 6;
+ sr = sr >> 4 | LARc[4] << 12;
+ sr = sr >> 4 | LARc[5] << 12;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | LARc[6] << 13;
+ sr = sr >> 3 | LARc[7] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[0] << 9;
+ sr = sr >> 2 | bc[0] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[0] << 14;
+ sr = sr >> 6 | xmaxc[0] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[0] << 13;
+ sr = sr >> 3 | xmc[1] << 13;
+ sr = sr >> 3 | xmc[2] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[3] << 13;
+ sr = sr >> 3 | xmc[4] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[5] << 13;
+ sr = sr >> 3 | xmc[6] << 13;
+ sr = sr >> 3 | xmc[7] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[8] << 13;
+ sr = sr >> 3 | xmc[9] << 13;
+ sr = sr >> 3 | xmc[10] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[11] << 13;
+ sr = sr >> 3 | xmc[12] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[1] << 9;
+ sr = sr >> 2 | bc[1] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[1] << 14;
+ sr = sr >> 6 | xmaxc[1] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[13] << 13;
+ sr = sr >> 3 | xmc[14] << 13;
+ sr = sr >> 3 | xmc[15] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[16] << 13;
+ sr = sr >> 3 | xmc[17] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[18] << 13;
+ sr = sr >> 3 | xmc[19] << 13;
+ sr = sr >> 3 | xmc[20] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[21] << 13;
+ sr = sr >> 3 | xmc[22] << 13;
+ sr = sr >> 3 | xmc[23] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[24] << 13;
+ sr = sr >> 3 | xmc[25] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[2] << 9;
+ sr = sr >> 2 | bc[2] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[2] << 14;
+ sr = sr >> 6 | xmaxc[2] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[26] << 13;
+ sr = sr >> 3 | xmc[27] << 13;
+ sr = sr >> 3 | xmc[28] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[29] << 13;
+ sr = sr >> 3 | xmc[30] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[31] << 13;
+ sr = sr >> 3 | xmc[32] << 13;
+ sr = sr >> 3 | xmc[33] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[34] << 13;
+ sr = sr >> 3 | xmc[35] << 13;
+ sr = sr >> 3 | xmc[36] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[37] << 13;
+ sr = sr >> 3 | xmc[38] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 7 | Nc[3] << 9;
+ sr = sr >> 2 | bc[3] << 14;
+ *c++ = sr >> 7;
+ sr = sr >> 2 | Mc[3] << 14;
+ sr = sr >> 6 | xmaxc[3] << 10;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[39] << 13;
+ sr = sr >> 3 | xmc[40] << 13;
+ sr = sr >> 3 | xmc[41] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[42] << 13;
+ sr = sr >> 3 | xmc[43] << 13;
+ *c++ = sr >> 8;
+ sr = sr >> 3 | xmc[44] << 13;
+ sr = sr >> 3 | xmc[45] << 13;
+ sr = sr >> 3 | xmc[46] << 13;
+ *c++ = sr >> 7;
+ sr = sr >> 3 | xmc[47] << 13;
+ sr = sr >> 3 | xmc[48] << 13;
+ sr = sr >> 3 | xmc[49] << 13;
+ *c++ = sr >> 6;
+ sr = sr >> 3 | xmc[50] << 13;
+ sr = sr >> 3 | xmc[51] << 13;
+ *c++ = sr >> 8;
+ }
+ }
+
+ else
+
+#endif /* WAV49 */
+ {
+
+ *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */
+ | ((LARc[0] >> 2) & 0xF);
+ *c++ = ((LARc[0] & 0x3) << 6)
+ | (LARc[1] & 0x3F);
+ *c++ = ((LARc[2] & 0x1F) << 3)
+ | ((LARc[3] >> 2) & 0x7);
+ *c++ = ((LARc[3] & 0x3) << 6)
+ | ((LARc[4] & 0xF) << 2)
+ | ((LARc[5] >> 2) & 0x3);
+ *c++ = ((LARc[5] & 0x3) << 6)
+ | ((LARc[6] & 0x7) << 3)
+ | (LARc[7] & 0x7);
+ *c++ = ((Nc[0] & 0x7F) << 1)
+ | ((bc[0] >> 1) & 0x1);
+ *c++ = ((bc[0] & 0x1) << 7)
+ | ((Mc[0] & 0x3) << 5)
+ | ((xmaxc[0] >> 1) & 0x1F);
+ *c++ = ((xmaxc[0] & 0x1) << 7)
+ | ((xmc[0] & 0x7) << 4)
+ | ((xmc[1] & 0x7) << 1)
+ | ((xmc[2] >> 2) & 0x1);
+ *c++ = ((xmc[2] & 0x3) << 6)
+ | ((xmc[3] & 0x7) << 3)
+ | (xmc[4] & 0x7);
+ *c++ = ((xmc[5] & 0x7) << 5) /* 10 */
+ | ((xmc[6] & 0x7) << 2)
+ | ((xmc[7] >> 1) & 0x3);
+ *c++ = ((xmc[7] & 0x1) << 7)
+ | ((xmc[8] & 0x7) << 4)
+ | ((xmc[9] & 0x7) << 1)
+ | ((xmc[10] >> 2) & 0x1);
+ *c++ = ((xmc[10] & 0x3) << 6)
+ | ((xmc[11] & 0x7) << 3)
+ | (xmc[12] & 0x7);
+ *c++ = ((Nc[1] & 0x7F) << 1)
+ | ((bc[1] >> 1) & 0x1);
+ *c++ = ((bc[1] & 0x1) << 7)
+ | ((Mc[1] & 0x3) << 5)
+ | ((xmaxc[1] >> 1) & 0x1F);
+ *c++ = ((xmaxc[1] & 0x1) << 7)
+ | ((xmc[13] & 0x7) << 4)
+ | ((xmc[14] & 0x7) << 1)
+ | ((xmc[15] >> 2) & 0x1);
+ *c++ = ((xmc[15] & 0x3) << 6)
+ | ((xmc[16] & 0x7) << 3)
+ | (xmc[17] & 0x7);
+ *c++ = ((xmc[18] & 0x7) << 5)
+ | ((xmc[19] & 0x7) << 2)
+ | ((xmc[20] >> 1) & 0x3);
+ *c++ = ((xmc[20] & 0x1) << 7)
+ | ((xmc[21] & 0x7) << 4)
+ | ((xmc[22] & 0x7) << 1)
+ | ((xmc[23] >> 2) & 0x1);
+ *c++ = ((xmc[23] & 0x3) << 6)
+ | ((xmc[24] & 0x7) << 3)
+ | (xmc[25] & 0x7);
+ *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */
+ | ((bc[2] >> 1) & 0x1);
+ *c++ = ((bc[2] & 0x1) << 7)
+ | ((Mc[2] & 0x3) << 5)
+ | ((xmaxc[2] >> 1) & 0x1F);
+ *c++ = ((xmaxc[2] & 0x1) << 7)
+ | ((xmc[26] & 0x7) << 4)
+ | ((xmc[27] & 0x7) << 1)
+ | ((xmc[28] >> 2) & 0x1);
+ *c++ = ((xmc[28] & 0x3) << 6)
+ | ((xmc[29] & 0x7) << 3)
+ | (xmc[30] & 0x7);
+ *c++ = ((xmc[31] & 0x7) << 5)
+ | ((xmc[32] & 0x7) << 2)
+ | ((xmc[33] >> 1) & 0x3);
+ *c++ = ((xmc[33] & 0x1) << 7)
+ | ((xmc[34] & 0x7) << 4)
+ | ((xmc[35] & 0x7) << 1)
+ | ((xmc[36] >> 2) & 0x1);
+ *c++ = ((xmc[36] & 0x3) << 6)
+ | ((xmc[37] & 0x7) << 3)
+ | (xmc[38] & 0x7);
+ *c++ = ((Nc[3] & 0x7F) << 1)
+ | ((bc[3] >> 1) & 0x1);
+ *c++ = ((bc[3] & 0x1) << 7)
+ | ((Mc[3] & 0x3) << 5)
+ | ((xmaxc[3] >> 1) & 0x1F);
+ *c++ = ((xmaxc[3] & 0x1) << 7)
+ | ((xmc[39] & 0x7) << 4)
+ | ((xmc[40] & 0x7) << 1)
+ | ((xmc[41] >> 2) & 0x1);
+ *c++ = ((xmc[41] & 0x3) << 6) /* 30 */
+ | ((xmc[42] & 0x7) << 3)
+ | (xmc[43] & 0x7);
+ *c++ = ((xmc[44] & 0x7) << 5)
+ | ((xmc[45] & 0x7) << 2)
+ | ((xmc[46] >> 1) & 0x3);
+ *c++ = ((xmc[46] & 0x1) << 7)
+ | ((xmc[47] & 0x7) << 4)
+ | ((xmc[48] & 0x7) << 1)
+ | ((xmc[49] >> 2) & 0x1);
+ *c++ = ((xmc[49] & 0x3) << 6)
+ | ((xmc[50] & 0x7) << 3)
+ | (xmc[51] & 0x7);
+
+ }
+}
diff --git a/trunk/codecs/gsm/src/gsm_explode.c b/trunk/codecs/gsm/src/gsm_explode.c
new file mode 100644
index 000000000..a906fc2ed
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_explode.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "private.h"
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_explode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target)
+{
+# define LARc target
+# define Nc *((gsm_signal (*) [17])(target + 8))
+# define bc *((gsm_signal (*) [17])(target + 9))
+# define Mc *((gsm_signal (*) [17])(target + 10))
+# define xmaxc *((gsm_signal (*) [17])(target + 11))
+
+
+#ifdef WAV49
+ if (s->wav_fmt) {
+
+ uword sr = 0;
+
+ if (s->frame_index == 1) {
+
+ sr = *c++;
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 2;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 4;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 2;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2; /* 5 */
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 10 */
+ xmc[6] = sr & 0x7; sr >>= 3;
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 29 - 13)
+
+ xmc[13] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 15 */
+ xmc[14] = sr & 0x7; sr >>= 3;
+ xmc[15] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4; /* 20 */
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+
+#undef xmc
+#define xmc (target + 46 - 26)
+
+ xmc[26] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 25 */
+ xmc[35] = sr & 0x7; sr >>= 3;
+ xmc[36] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 30 */
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ xmc[44] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+
+ s->frame_chain = sr & 0xf;
+ }
+ else {
+ sr = s->frame_chain;
+ sr |= (uword)*c++ << 4; /* 1 */
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr = *c++;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 3;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 5 */
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ xmc[6] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 10 */
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 29 - 13)
+
+ xmc[13] = sr & 0x7; sr >>= 3;
+ xmc[14] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 15 */
+ xmc[15] = sr & 0x7; sr >>= 3;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1; /* 20 */
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (target + 46 - 26)
+ xmc[26] = sr & 0x7; sr >>= 3;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ xmc[35] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 25 */
+ xmc[36] = sr & 0x7; sr >>= 3;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+
+#undef xmc
+#define xmc (target + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 30 */
+ xmc[44] = sr & 0x7; sr >>= 3;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+ }
+ }
+ else
+#endif
+ {
+ /* GSM_MAGIC = (*c >> 4) & 0xF; */
+
+ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
+
+ LARc[0] = (*c++ & 0xF) << 2; /* 1 */
+ LARc[0] |= (*c >> 6) & 0x3;
+ LARc[1] = *c++ & 0x3F;
+ LARc[2] = (*c >> 3) & 0x1F;
+ LARc[3] = (*c++ & 0x7) << 2;
+ LARc[3] |= (*c >> 6) & 0x3;
+ LARc[4] = (*c >> 2) & 0xF;
+ LARc[5] = (*c++ & 0x3) << 2;
+ LARc[5] |= (*c >> 6) & 0x3;
+ LARc[6] = (*c >> 3) & 0x7;
+ LARc[7] = *c++ & 0x7;
+
+ Nc[0] = (*c >> 1) & 0x7F;
+
+ bc[0] = (*c++ & 0x1) << 1;
+ bc[0] |= (*c >> 7) & 0x1;
+
+ Mc[0] = (*c >> 5) & 0x3;
+
+ xmaxc[0] = (*c++ & 0x1F) << 1;
+ xmaxc[0] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 12)
+
+ xmc[0] = (*c >> 4) & 0x7;
+ xmc[1] = (*c >> 1) & 0x7;
+ xmc[2] = (*c++ & 0x1) << 2;
+ xmc[2] |= (*c >> 6) & 0x3;
+ xmc[3] = (*c >> 3) & 0x7;
+ xmc[4] = *c++ & 0x7;
+ xmc[5] = (*c >> 5) & 0x7;
+ xmc[6] = (*c >> 2) & 0x7;
+ xmc[7] = (*c++ & 0x3) << 1; /* 10 */
+ xmc[7] |= (*c >> 7) & 0x1;
+ xmc[8] = (*c >> 4) & 0x7;
+ xmc[9] = (*c >> 1) & 0x7;
+ xmc[10] = (*c++ & 0x1) << 2;
+ xmc[10] |= (*c >> 6) & 0x3;
+ xmc[11] = (*c >> 3) & 0x7;
+ xmc[12] = *c++ & 0x7;
+
+ Nc[1] = (*c >> 1) & 0x7F;
+
+ bc[1] = (*c++ & 0x1) << 1;
+ bc[1] |= (*c >> 7) & 0x1;
+
+ Mc[1] = (*c >> 5) & 0x3;
+
+ xmaxc[1] = (*c++ & 0x1F) << 1;
+ xmaxc[1] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 29 - 13)
+
+ xmc[13] = (*c >> 4) & 0x7;
+ xmc[14] = (*c >> 1) & 0x7;
+ xmc[15] = (*c++ & 0x1) << 2;
+ xmc[15] |= (*c >> 6) & 0x3;
+ xmc[16] = (*c >> 3) & 0x7;
+ xmc[17] = *c++ & 0x7;
+ xmc[18] = (*c >> 5) & 0x7;
+ xmc[19] = (*c >> 2) & 0x7;
+ xmc[20] = (*c++ & 0x3) << 1;
+ xmc[20] |= (*c >> 7) & 0x1;
+ xmc[21] = (*c >> 4) & 0x7;
+ xmc[22] = (*c >> 1) & 0x7;
+ xmc[23] = (*c++ & 0x1) << 2;
+ xmc[23] |= (*c >> 6) & 0x3;
+ xmc[24] = (*c >> 3) & 0x7;
+ xmc[25] = *c++ & 0x7;
+
+ Nc[2] = (*c >> 1) & 0x7F;
+
+ bc[2] = (*c++ & 0x1) << 1; /* 20 */
+ bc[2] |= (*c >> 7) & 0x1;
+
+ Mc[2] = (*c >> 5) & 0x3;
+
+ xmaxc[2] = (*c++ & 0x1F) << 1;
+ xmaxc[2] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 46 - 26)
+
+ xmc[26] = (*c >> 4) & 0x7;
+ xmc[27] = (*c >> 1) & 0x7;
+ xmc[28] = (*c++ & 0x1) << 2;
+ xmc[28] |= (*c >> 6) & 0x3;
+ xmc[29] = (*c >> 3) & 0x7;
+ xmc[30] = *c++ & 0x7;
+ xmc[31] = (*c >> 5) & 0x7;
+ xmc[32] = (*c >> 2) & 0x7;
+ xmc[33] = (*c++ & 0x3) << 1;
+ xmc[33] |= (*c >> 7) & 0x1;
+ xmc[34] = (*c >> 4) & 0x7;
+ xmc[35] = (*c >> 1) & 0x7;
+ xmc[36] = (*c++ & 0x1) << 2;
+ xmc[36] |= (*c >> 6) & 0x3;
+ xmc[37] = (*c >> 3) & 0x7;
+ xmc[38] = *c++ & 0x7;
+
+ Nc[3] = (*c >> 1) & 0x7F;
+
+ bc[3] = (*c++ & 0x1) << 1;
+ bc[3] |= (*c >> 7) & 0x1;
+
+ Mc[3] = (*c >> 5) & 0x3;
+
+ xmaxc[3] = (*c++ & 0x1F) << 1;
+ xmaxc[3] |= (*c >> 7) & 0x1;
+
+#undef xmc
+#define xmc (target + 63 - 39)
+
+ xmc[39] = (*c >> 4) & 0x7;
+ xmc[40] = (*c >> 1) & 0x7;
+ xmc[41] = (*c++ & 0x1) << 2;
+ xmc[41] |= (*c >> 6) & 0x3;
+ xmc[42] = (*c >> 3) & 0x7;
+ xmc[43] = *c++ & 0x7; /* 30 */
+ xmc[44] = (*c >> 5) & 0x7;
+ xmc[45] = (*c >> 2) & 0x7;
+ xmc[46] = (*c++ & 0x3) << 1;
+ xmc[46] |= (*c >> 7) & 0x1;
+ xmc[47] = (*c >> 4) & 0x7;
+ xmc[48] = (*c >> 1) & 0x7;
+ xmc[49] = (*c++ & 0x1) << 2;
+ xmc[49] |= (*c >> 6) & 0x3;
+ xmc[50] = (*c >> 3) & 0x7;
+ xmc[51] = *c & 0x7; /* 33 */
+ }
+
+ return 0;
+}
diff --git a/trunk/codecs/gsm/src/gsm_implode.c b/trunk/codecs/gsm/src/gsm_implode.c
new file mode 100644
index 000000000..453b8cf39
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_implode.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+void gsm_implode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c)
+{
+ /* variable size index
+
+ GSM_MAGIC 4 -
+
+ LARc[0] 6 0
+ LARc[1] 6 1
+ LARc[2] 5 2
+ LARc[3] 5 3
+ LARc[4] 4 4
+ LARc[5] 4 5
+ LARc[6] 3 6
+ LARc[7] 3 7
+
+ Nc[0] 7 8
+ bc[0] 2 9
+ Mc[0] 2 10
+ xmaxc[0] 6 11
+ xmc[0] 3 12
+ xmc[1] 3 13
+ xmc[2] 3 14
+ xmc[3] 3 15
+ xmc[4] 3 16
+ xmc[5] 3 17
+ xmc[6] 3 18
+ xmc[7] 3 19
+ xmc[8] 3 20
+ xmc[9] 3 21
+ xmc[10] 3 22
+ xmc[11] 3 23
+ xmc[12] 3 24
+
+ Nc[1] 7 25
+ bc[1] 2 26
+ Mc[1] 2 27
+ xmaxc[1] 6 28
+ xmc[13] 3 29
+ xmc[14] 3 30
+ xmc[15] 3 31
+ xmc[16] 3 32
+ xmc[17] 3 33
+ xmc[18] 3 34
+ xmc[19] 3 35
+ xmc[20] 3 36
+ xmc[21] 3 37
+ xmc[22] 3 38
+ xmc[23] 3 39
+ xmc[24] 3 40
+ xmc[25] 3 41
+
+ Nc[2] 7 42
+ bc[2] 2 43
+ Mc[2] 2 44
+ xmaxc[2] 6 45
+ xmc[26] 3 46
+ xmc[27] 3 47
+ xmc[28] 3 48
+ xmc[29] 3 49
+ xmc[30] 3 50
+ xmc[31] 3 51
+ xmc[32] 3 52
+ xmc[33] 3 53
+ xmc[34] 3 54
+ xmc[35] 3 55
+ xmc[36] 3 56
+ xmc[37] 3 57
+ xmc[38] 3 58
+
+ Nc[3] 7 59
+ bc[3] 2 60
+ Mc[3] 2 61
+ xmaxc[3] 6 62
+ xmc[39] 3 63
+ xmc[40] 3 64
+ xmc[41] 3 65
+ xmc[42] 3 66
+ xmc[43] 3 67
+ xmc[44] 3 68
+ xmc[45] 3 69
+ xmc[46] 3 70
+ xmc[47] 3 71
+ xmc[48] 3 72
+ xmc[49] 3 73
+ xmc[50] 3 74
+ xmc[51] 3 75
+ */
+
+ /* There are 76 parameters per frame. The first eight are
+ * unique. The remaining 68 are four identical subframes of
+ * 17 parameters each. gsm_implode converts from a representation
+ * of these parameters as values in one array of signed words
+ * to the "packed" version of a GSM frame.
+ */
+
+# define LARc source
+# define Nc *((gsm_signal (*) [17])(source + 8))
+# define bc *((gsm_signal (*) [17])(source + 9))
+# define Mc *((gsm_signal (*) [17])(source + 10))
+# define xmaxc *((gsm_signal (*) [17])(source + 11))
+
+#ifdef WAV49
+ if (s->wav_fmt) {
+
+ uword sr = 0;
+ if (s->frame_index == 0) {
+
+ sr = *c++;
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 2;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 4;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 2;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2; /* 5 */
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 10 */
+ xmc[6] = sr & 0x7; sr >>= 3;
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 29 - 13)
+ xmc[13] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 15 */
+ xmc[14] = sr & 0x7; sr >>= 3;
+ xmc[15] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4; /* 20 */
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 46 - 26)
+ xmc[26] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 25 */
+ xmc[35] = sr & 0x7; sr >>= 3;
+ xmc[36] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 30 */
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ xmc[44] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+
+ s->frame_chain = sr & 0xf;
+ }
+ else {
+ sr = s->frame_chain;
+ sr |= (uword)*c++ << 4; /* 1 */
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr = *c++;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 3;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 5 */
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 12)
+ xmc[0] = sr & 0x7; sr >>= 3;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ xmc[6] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 10 */
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 29 - 13)
+ xmc[13] = sr & 0x7; sr >>= 3;
+ xmc[14] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 15 */
+ xmc[15] = sr & 0x7; sr >>= 3;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1; /* 20 */
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 46 - 26)
+ xmc[26] = sr & 0x7; sr >>= 3;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ xmc[35] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 25 */
+ xmc[36] = sr & 0x7; sr >>= 3;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+#undef xmc
+#define xmc (source + 63 - 39)
+
+ xmc[39] = sr & 0x7; sr >>= 3;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 30 */
+ xmc[44] = sr & 0x7; sr >>= 3;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+ }
+ }
+ else
+#endif
+ {
+
+ *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */
+ | ((LARc[0] >> 2) & 0xF);
+ *c++ = ((LARc[0] & 0x3) << 6)
+ | (LARc[1] & 0x3F);
+ *c++ = ((LARc[2] & 0x1F) << 3)
+ | ((LARc[3] >> 2) & 0x7);
+ *c++ = ((LARc[3] & 0x3) << 6)
+ | ((LARc[4] & 0xF) << 2)
+ | ((LARc[5] >> 2) & 0x3);
+ *c++ = ((LARc[5] & 0x3) << 6)
+ | ((LARc[6] & 0x7) << 3)
+ | (LARc[7] & 0x7);
+
+
+ *c++ = ((Nc[0] & 0x7F) << 1)
+
+
+ | ((bc[0] >> 1) & 0x1);
+ *c++ = ((bc[0] & 0x1) << 7)
+
+
+ | ((Mc[0] & 0x3) << 5)
+
+ | ((xmaxc[0] >> 1) & 0x1F);
+ *c++ = ((xmaxc[0] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 12)
+
+ | ((xmc[0] & 0x7) << 4)
+ | ((xmc[1] & 0x7) << 1)
+ | ((xmc[2] >> 2) & 0x1);
+ *c++ = ((xmc[2] & 0x3) << 6)
+ | ((xmc[3] & 0x7) << 3)
+ | (xmc[4] & 0x7);
+ *c++ = ((xmc[5] & 0x7) << 5) /* 10 */
+ | ((xmc[6] & 0x7) << 2)
+ | ((xmc[7] >> 1) & 0x3);
+ *c++ = ((xmc[7] & 0x1) << 7)
+ | ((xmc[8] & 0x7) << 4)
+ | ((xmc[9] & 0x7) << 1)
+ | ((xmc[10] >> 2) & 0x1);
+ *c++ = ((xmc[10] & 0x3) << 6)
+ | ((xmc[11] & 0x7) << 3)
+ | (xmc[12] & 0x7);
+
+
+ *c++ = ((Nc[1] & 0x7F) << 1)
+
+
+ | ((bc[1] >> 1) & 0x1);
+ *c++ = ((bc[1] & 0x1) << 7)
+
+
+ | ((Mc[1] & 0x3) << 5)
+
+
+ | ((xmaxc[1] >> 1) & 0x1F);
+ *c++ = ((xmaxc[1] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 29 - 13)
+
+ | ((xmc[13] & 0x7) << 4)
+ | ((xmc[14] & 0x7) << 1)
+ | ((xmc[15] >> 2) & 0x1);
+ *c++ = ((xmc[15] & 0x3) << 6)
+ | ((xmc[16] & 0x7) << 3)
+ | (xmc[17] & 0x7);
+ *c++ = ((xmc[18] & 0x7) << 5)
+ | ((xmc[19] & 0x7) << 2)
+ | ((xmc[20] >> 1) & 0x3);
+ *c++ = ((xmc[20] & 0x1) << 7)
+ | ((xmc[21] & 0x7) << 4)
+ | ((xmc[22] & 0x7) << 1)
+ | ((xmc[23] >> 2) & 0x1);
+ *c++ = ((xmc[23] & 0x3) << 6)
+ | ((xmc[24] & 0x7) << 3)
+ | (xmc[25] & 0x7);
+
+
+ *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */
+
+
+ | ((bc[2] >> 1) & 0x1);
+ *c++ = ((bc[2] & 0x1) << 7)
+
+
+ | ((Mc[2] & 0x3) << 5)
+
+
+ | ((xmaxc[2] >> 1) & 0x1F);
+ *c++ = ((xmaxc[2] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 46 - 26)
+
+ | ((xmc[26] & 0x7) << 4)
+ | ((xmc[27] & 0x7) << 1)
+ | ((xmc[28] >> 2) & 0x1);
+ *c++ = ((xmc[28] & 0x3) << 6)
+ | ((xmc[29] & 0x7) << 3)
+ | (xmc[30] & 0x7);
+ *c++ = ((xmc[31] & 0x7) << 5)
+ | ((xmc[32] & 0x7) << 2)
+ | ((xmc[33] >> 1) & 0x3);
+ *c++ = ((xmc[33] & 0x1) << 7)
+ | ((xmc[34] & 0x7) << 4)
+ | ((xmc[35] & 0x7) << 1)
+ | ((xmc[36] >> 2) & 0x1);
+ *c++ = ((xmc[36] & 0x3) << 6)
+ | ((xmc[37] & 0x7) << 3)
+ | (xmc[38] & 0x7);
+
+
+ *c++ = ((Nc[3] & 0x7F) << 1)
+
+
+ | ((bc[3] >> 1) & 0x1);
+ *c++ = ((bc[3] & 0x1) << 7)
+
+
+ | ((Mc[3] & 0x3) << 5)
+
+
+ | ((xmaxc[3] >> 1) & 0x1F);
+ *c++ = ((xmaxc[3] & 0x1) << 7)
+
+#undef xmc
+#define xmc (source + 63 - 39)
+
+ | ((xmc[39] & 0x7) << 4)
+ | ((xmc[40] & 0x7) << 1)
+ | ((xmc[41] >> 2) & 0x1);
+ *c++ = ((xmc[41] & 0x3) << 6) /* 30 */
+ | ((xmc[42] & 0x7) << 3)
+ | (xmc[43] & 0x7);
+ *c++ = ((xmc[44] & 0x7) << 5)
+ | ((xmc[45] & 0x7) << 2)
+ | ((xmc[46] >> 1) & 0x3);
+ *c++ = ((xmc[46] & 0x1) << 7)
+ | ((xmc[47] & 0x7) << 4)
+ | ((xmc[48] & 0x7) << 1)
+ | ((xmc[49] >> 2) & 0x1);
+ *c++ = ((xmc[49] & 0x3) << 6)
+ | ((xmc[50] & 0x7) << 3)
+ | (xmc[51] & 0x7);
+ }
+}
diff --git a/trunk/codecs/gsm/src/gsm_option.c b/trunk/codecs/gsm/src/gsm_option.c
new file mode 100644
index 000000000..280780132
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_option.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_option P3((r, opt, val), gsm r, int opt, int * val)
+{
+ int result = -1;
+
+ switch (opt) {
+ case GSM_OPT_LTP_CUT:
+#ifdef LTP_CUT
+ result = r->ltp_cut;
+ if (val) r->ltp_cut = *val;
+#endif
+ break;
+
+ case GSM_OPT_VERBOSE:
+#ifndef NDEBUG
+ result = r->verbose;
+ if (val) r->verbose = *val;
+#endif
+ break;
+
+ case GSM_OPT_FAST:
+
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+ result = r->fast;
+ if (val) r->fast = !!*val;
+#endif
+ break;
+
+ case GSM_OPT_FRAME_CHAIN:
+
+#ifdef WAV49
+ result = r->frame_chain;
+ if (val) r->frame_chain = *val;
+#endif
+ break;
+
+ case GSM_OPT_FRAME_INDEX:
+
+#ifdef WAV49
+ result = r->frame_index;
+ if (val) r->frame_index = *val;
+#endif
+ break;
+
+ case GSM_OPT_WAV49:
+
+#ifdef WAV49
+ result = r->wav_fmt;
+ if (val) r->wav_fmt = !!*val;
+#endif
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
diff --git a/trunk/codecs/gsm/src/gsm_print.c b/trunk/codecs/gsm/src/gsm_print.c
new file mode 100644
index 000000000..af745bc48
--- /dev/null
+++ b/trunk/codecs/gsm/src/gsm_print.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include <stdio.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_print P3((f, s, c), FILE * f, gsm s, gsm_byte * c)
+{
+ word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+ /* GSM_MAGIC = (*c >> 4) & 0xF; */
+
+ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
+
+ LARc[0] = (*c++ & 0xF) << 2; /* 1 */
+ LARc[0] |= (*c >> 6) & 0x3;
+ LARc[1] = *c++ & 0x3F;
+ LARc[2] = (*c >> 3) & 0x1F;
+ LARc[3] = (*c++ & 0x7) << 2;
+ LARc[3] |= (*c >> 6) & 0x3;
+ LARc[4] = (*c >> 2) & 0xF;
+ LARc[5] = (*c++ & 0x3) << 2;
+ LARc[5] |= (*c >> 6) & 0x3;
+ LARc[6] = (*c >> 3) & 0x7;
+ LARc[7] = *c++ & 0x7;
+
+
+ Nc[0] = (*c >> 1) & 0x7F;
+ bc[0] = (*c++ & 0x1) << 1;
+ bc[0] |= (*c >> 7) & 0x1;
+ Mc[0] = (*c >> 5) & 0x3;
+ xmaxc[0] = (*c++ & 0x1F) << 1;
+ xmaxc[0] |= (*c >> 7) & 0x1;
+ xmc[0] = (*c >> 4) & 0x7;
+ xmc[1] = (*c >> 1) & 0x7;
+ xmc[2] = (*c++ & 0x1) << 2;
+ xmc[2] |= (*c >> 6) & 0x3;
+ xmc[3] = (*c >> 3) & 0x7;
+ xmc[4] = *c++ & 0x7;
+ xmc[5] = (*c >> 5) & 0x7;
+ xmc[6] = (*c >> 2) & 0x7;
+ xmc[7] = (*c++ & 0x3) << 1; /* 10 */
+ xmc[7] |= (*c >> 7) & 0x1;
+ xmc[8] = (*c >> 4) & 0x7;
+ xmc[9] = (*c >> 1) & 0x7;
+ xmc[10] = (*c++ & 0x1) << 2;
+ xmc[10] |= (*c >> 6) & 0x3;
+ xmc[11] = (*c >> 3) & 0x7;
+ xmc[12] = *c++ & 0x7;
+
+ Nc[1] = (*c >> 1) & 0x7F;
+ bc[1] = (*c++ & 0x1) << 1;
+ bc[1] |= (*c >> 7) & 0x1;
+ Mc[1] = (*c >> 5) & 0x3;
+ xmaxc[1] = (*c++ & 0x1F) << 1;
+ xmaxc[1] |= (*c >> 7) & 0x1;
+ xmc[13] = (*c >> 4) & 0x7;
+ xmc[14] = (*c >> 1) & 0x7;
+ xmc[15] = (*c++ & 0x1) << 2;
+ xmc[15] |= (*c >> 6) & 0x3;
+ xmc[16] = (*c >> 3) & 0x7;
+ xmc[17] = *c++ & 0x7;
+ xmc[18] = (*c >> 5) & 0x7;
+ xmc[19] = (*c >> 2) & 0x7;
+ xmc[20] = (*c++ & 0x3) << 1;
+ xmc[20] |= (*c >> 7) & 0x1;
+ xmc[21] = (*c >> 4) & 0x7;
+ xmc[22] = (*c >> 1) & 0x7;
+ xmc[23] = (*c++ & 0x1) << 2;
+ xmc[23] |= (*c >> 6) & 0x3;
+ xmc[24] = (*c >> 3) & 0x7;
+ xmc[25] = *c++ & 0x7;
+
+
+ Nc[2] = (*c >> 1) & 0x7F;
+ bc[2] = (*c++ & 0x1) << 1; /* 20 */
+ bc[2] |= (*c >> 7) & 0x1;
+ Mc[2] = (*c >> 5) & 0x3;
+ xmaxc[2] = (*c++ & 0x1F) << 1;
+ xmaxc[2] |= (*c >> 7) & 0x1;
+ xmc[26] = (*c >> 4) & 0x7;
+ xmc[27] = (*c >> 1) & 0x7;
+ xmc[28] = (*c++ & 0x1) << 2;
+ xmc[28] |= (*c >> 6) & 0x3;
+ xmc[29] = (*c >> 3) & 0x7;
+ xmc[30] = *c++ & 0x7;
+ xmc[31] = (*c >> 5) & 0x7;
+ xmc[32] = (*c >> 2) & 0x7;
+ xmc[33] = (*c++ & 0x3) << 1;
+ xmc[33] |= (*c >> 7) & 0x1;
+ xmc[34] = (*c >> 4) & 0x7;
+ xmc[35] = (*c >> 1) & 0x7;
+ xmc[36] = (*c++ & 0x1) << 2;
+ xmc[36] |= (*c >> 6) & 0x3;
+ xmc[37] = (*c >> 3) & 0x7;
+ xmc[38] = *c++ & 0x7;
+
+ Nc[3] = (*c >> 1) & 0x7F;
+ bc[3] = (*c++ & 0x1) << 1;
+ bc[3] |= (*c >> 7) & 0x1;
+ Mc[3] = (*c >> 5) & 0x3;
+ xmaxc[3] = (*c++ & 0x1F) << 1;
+ xmaxc[3] |= (*c >> 7) & 0x1;
+
+ xmc[39] = (*c >> 4) & 0x7;
+ xmc[40] = (*c >> 1) & 0x7;
+ xmc[41] = (*c++ & 0x1) << 2;
+ xmc[41] |= (*c >> 6) & 0x3;
+ xmc[42] = (*c >> 3) & 0x7;
+ xmc[43] = *c++ & 0x7; /* 30 */
+ xmc[44] = (*c >> 5) & 0x7;
+ xmc[45] = (*c >> 2) & 0x7;
+ xmc[46] = (*c++ & 0x3) << 1;
+ xmc[46] |= (*c >> 7) & 0x1;
+ xmc[47] = (*c >> 4) & 0x7;
+ xmc[48] = (*c >> 1) & 0x7;
+ xmc[49] = (*c++ & 0x1) << 2;
+ xmc[49] |= (*c >> 6) & 0x3;
+ xmc[50] = (*c >> 3) & 0x7;
+ xmc[51] = *c & 0x7; /* 33 */
+
+ fprintf(f,
+ "LARc:\t%2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d\n",
+ LARc[0],LARc[1],LARc[2],LARc[3],LARc[4],LARc[5],LARc[6],LARc[7]);
+
+ fprintf(f, "#1: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[0], bc[0], Mc[0], xmaxc[0]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[0],xmc[1],xmc[2],xmc[3],xmc[4],xmc[5],xmc[6],
+ xmc[7],xmc[8],xmc[9],xmc[10],xmc[11],xmc[12] );
+
+ fprintf(f, "#2: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[1], bc[1], Mc[1], xmaxc[1]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[13+0],xmc[13+1],xmc[13+2],xmc[13+3],xmc[13+4],xmc[13+5],
+ xmc[13+6], xmc[13+7],xmc[13+8],xmc[13+9],xmc[13+10],xmc[13+11],
+ xmc[13+12] );
+
+ fprintf(f, "#3: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[2], bc[2], Mc[2], xmaxc[2]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[26+0],xmc[26+1],xmc[26+2],xmc[26+3],xmc[26+4],xmc[26+5],
+ xmc[26+6], xmc[26+7],xmc[26+8],xmc[26+9],xmc[26+10],xmc[26+11],
+ xmc[26+12] );
+
+ fprintf(f, "#4: Nc %4.4d bc %d Mc %d xmaxc %d\n",
+ Nc[3], bc[3], Mc[3], xmaxc[3]);
+ fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+ xmc[39+0],xmc[39+1],xmc[39+2],xmc[39+3],xmc[39+4],xmc[39+5],
+ xmc[39+6], xmc[39+7],xmc[39+8],xmc[39+9],xmc[39+10],xmc[39+11],
+ xmc[39+12] );
+
+ return 0;
+}
diff --git a/trunk/codecs/gsm/src/k6opt.h b/trunk/codecs/gsm/src/k6opt.h
new file mode 100644
index 000000000..16ea2ac8d
--- /dev/null
+++ b/trunk/codecs/gsm/src/k6opt.h
@@ -0,0 +1,84 @@
+/* k6opt.h vector functions optimized for MMX extensions to x86
+ *
+ * Copyright (C) 1999 by Stanley J. Brooks <stabro@megsinet.net>
+ *
+ * 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;
+ * not even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE.
+ *
+ * Chicago, 03.12.1999
+ * Stanley J. Brooks
+ */
+
+extern void Weighting_filter P2((e, x),
+ const word * e, /* signal [-5..0.39.44] IN */
+ word * x /* signal [0..39] OUT */
+)
+;
+
+extern longword k6maxcc P3((wt,dp,Nc_out),
+ const word *wt,
+ const word *dp,
+ word * Nc_out /* OUT */
+)
+;
+/*
+ * k6maxmin(p,n,out[])
+ * input p[n] is array of shorts (require n>0)
+ * returns (long) maximum absolute value..
+ * if out!=NULL, also returns out[0] the maximum and out[1] the minimum
+ */
+extern longword k6maxmin P3((p,n,out),
+ const word *p,
+ int n,
+ word *out /* out[0] is max, out[1] is min */
+)
+;
+
+extern longword k6iprod P3((p,q,n),
+ const word *p,
+ const word *q,
+ int n
+)
+;
+
+/*
+ * k6vsraw(p,n,bits)
+ * input p[n] is array of shorts (require n>0)
+ * shift/round each to the right by bits>=0 bits.
+ */
+extern void k6vsraw P3((p,n,bits),
+ const word *p,
+ int n,
+ int bits
+)
+;
+
+/*
+ * k6vsllw(p,n,bits)
+ * input p[n] is array of shorts (require n>0)
+ * shift each to the left by bits>=0 bits.
+ */
+extern void k6vsllw P3((p,n,bits),
+ const word *p,
+ int n,
+ int bits
+)
+;
+
+#if 1 /* there isn't any significant speed gain from mmx here: */
+extern void Short_term_analysis_filteringx P4((u0,rp0,k_n,s),
+ register word * u0,
+ register word * rp0, /* [0..7] IN */
+ register int k_n, /* k_end - k_start */
+ register word * s /* [0..n-1] IN/OUT */
+)
+;
+/*
+#define Short_term_analysis_filtering Short_term_analysis_filteringx
+*/
+#endif
diff --git a/trunk/codecs/gsm/src/k6opt.s b/trunk/codecs/gsm/src/k6opt.s
new file mode 100644
index 000000000..d84d54cbf
--- /dev/null
+++ b/trunk/codecs/gsm/src/k6opt.s
@@ -0,0 +1,739 @@
+ .file "k6opt.s"
+ .version "01.01"
+/* gcc2_compiled.: */
+.section .rodata
+ .align 4
+ .type coefs,@object
+ .size coefs,24
+coefs:
+ .value -134
+ .value -374
+ .value 0
+ .value 2054
+ .value 5741
+ .value 8192
+ .value 5741
+ .value 2054
+ .value 0
+ .value -374
+ .value -134
+ .value 0
+.text
+ .align 4
+/* void Weighting_filter (const short *e, short *x) */
+.globl Weighting_filter
+ .type Weighting_filter,@function
+Weighting_filter:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ movl 12(%ebp),%edi
+ movl 8(%ebp),%ebx
+ addl $-10,%ebx
+ emms
+ movl $0x1000,%eax; movd %eax,%mm5 /* for rounding */
+ movq coefs,%mm1
+ movq coefs+8,%mm2
+ movq coefs+16,%mm3
+ xorl %esi,%esi
+ .p2align 2
+.L21:
+ movq (%ebx,%esi,2),%mm0
+ pmaddwd %mm1,%mm0
+
+ movq 8(%ebx,%esi,2),%mm4
+ pmaddwd %mm2,%mm4
+ paddd %mm4,%mm0
+
+ movq 16(%ebx,%esi,2),%mm4
+ pmaddwd %mm3,%mm4
+ paddd %mm4,%mm0
+
+ movq %mm0,%mm4
+ punpckhdq %mm0,%mm4 /* mm4 has high int32 of mm0 dup'd */
+ paddd %mm4,%mm0;
+
+ paddd %mm5,%mm0 /* add for roundoff */
+ psrad $13,%mm0
+ packssdw %mm0,%mm0
+ movd %mm0,%eax /* ax has result */
+ movw %ax,(%edi,%esi,2)
+ incl %esi
+ cmpl $39,%esi
+ jle .L21
+ emms
+ popl %ebx
+ popl %esi
+ popl %edi
+ leave
+ ret
+.Lfe1:
+ .size Weighting_filter,.Lfe1-Weighting_filter
+
+.macro ccstep n
+.if \n
+ movq \n(%edi),%mm1
+ movq \n(%esi),%mm2
+.else
+ movq (%edi),%mm1
+ movq (%esi),%mm2
+.endif
+ pmaddwd %mm2,%mm1
+ paddd %mm1,%mm0
+.endm
+
+ .align 4
+/* long k6maxcc(const short *wt, const short *dp, short *Nc_out) */
+.globl k6maxcc
+ .type k6maxcc,@function
+k6maxcc:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ emms
+ movl 8(%ebp),%edi
+ movl 12(%ebp),%esi
+ movl $0,%edx /* will be maximum inner-product */
+ movl $40,%ebx
+ movl %ebx,%ecx /* will be index of max inner-product */
+ subl $80,%esi
+ .p2align 2
+.L41:
+ movq (%edi),%mm0
+ movq (%esi),%mm2
+ pmaddwd %mm2,%mm0
+ ccstep 8
+ ccstep 16
+ ccstep 24
+ ccstep 32
+ ccstep 40
+ ccstep 48
+ ccstep 56
+ ccstep 64
+ ccstep 72
+
+ movq %mm0,%mm1
+ punpckhdq %mm0,%mm1 /* mm1 has high int32 of mm0 dup'd */
+ paddd %mm1,%mm0;
+ movd %mm0,%eax /* eax has result */
+
+ cmpl %edx,%eax
+ jle .L40
+ movl %eax,%edx
+ movl %ebx,%ecx
+ .p2align 2
+.L40:
+ subl $2,%esi
+ incl %ebx
+ cmpl $120,%ebx
+ jle .L41
+ movl 16(%ebp),%eax
+ movw %cx,(%eax)
+ movl %edx,%eax
+ emms
+ popl %ebx
+ popl %esi
+ popl %edi
+ leave
+ ret
+.Lfe2:
+ .size k6maxcc,.Lfe2-k6maxcc
+
+
+ .align 4
+/* long k6iprod (const short *p, const short *q, int n) */
+.globl k6iprod
+ .type k6iprod,@function
+k6iprod:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %edi
+ pushl %esi
+ emms
+ pxor %mm0,%mm0
+ movl 8(%ebp),%esi
+ movl 12(%ebp),%edi
+ movl 16(%ebp),%eax
+ leal -32(%esi,%eax,2),%edx /* edx = top - 32 */
+
+ cmpl %edx,%esi; ja .L202
+
+ .p2align 2
+.L201:
+ ccstep 0
+ ccstep 8
+ ccstep 16
+ ccstep 24
+
+ addl $32,%esi
+ addl $32,%edi
+ cmpl %edx,%esi; jbe .L201
+
+ .p2align 2
+.L202:
+ addl $24,%edx /* now edx = top-8 */
+ cmpl %edx,%esi; ja .L205
+
+ .p2align 2
+.L203:
+ ccstep 0
+
+ addl $8,%esi
+ addl $8,%edi
+ cmpl %edx,%esi; jbe .L203
+
+ .p2align 2
+.L205:
+ addl $4,%edx /* now edx = top-4 */
+ cmpl %edx,%esi; ja .L207
+
+ movd (%edi),%mm1
+ movd (%esi),%mm2
+ pmaddwd %mm2,%mm1
+ paddd %mm1,%mm0
+
+ addl $4,%esi
+ addl $4,%edi
+
+ .p2align 2
+.L207:
+ addl $2,%edx /* now edx = top-2 */
+ cmpl %edx,%esi; ja .L209
+
+ movswl (%edi),%eax
+ movd %eax,%mm1
+ movswl (%esi),%eax
+ movd %eax,%mm2
+ pmaddwd %mm2,%mm1
+ paddd %mm1,%mm0
+
+ .p2align 2
+.L209:
+ movq %mm0,%mm1
+ punpckhdq %mm0,%mm1 /* mm1 has high int32 of mm0 dup'd */
+ paddd %mm1,%mm0;
+ movd %mm0,%eax /* eax has result */
+
+ emms
+ popl %esi
+ popl %edi
+ leave
+ ret
+.Lfe3:
+ .size k6iprod,.Lfe3-k6iprod
+
+
+ .align 4
+/* void k6vsraw P3((short *p, int n, int bits) */
+.globl k6vsraw
+ .type k6vsraw,@function
+k6vsraw:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl 8(%ebp),%esi
+ movl 16(%ebp),%ecx
+ andl %ecx,%ecx; jle .L399
+ movl 12(%ebp),%eax
+ leal -16(%esi,%eax,2),%edx /* edx = top - 16 */
+ emms
+ movd %ecx,%mm3
+ movq ones,%mm2
+ psllw %mm3,%mm2; psrlw $1,%mm2
+ cmpl %edx,%esi; ja .L306
+
+ .p2align 2
+.L302: /* 8 words per iteration */
+ movq (%esi),%mm0
+ movq 8(%esi),%mm1
+ paddsw %mm2,%mm0
+ psraw %mm3,%mm0;
+ paddsw %mm2,%mm1
+ psraw %mm3,%mm1;
+ movq %mm0,(%esi)
+ movq %mm1,8(%esi)
+ addl $16,%esi
+ cmpl %edx,%esi
+ jbe .L302
+
+ .p2align 2
+.L306:
+ addl $12,%edx /* now edx = top-4 */
+ cmpl %edx,%esi; ja .L310
+
+ .p2align 2
+.L308: /* do up to 6 words, two at a time */
+ movd (%esi),%mm0
+ paddsw %mm2,%mm0
+ psraw %mm3,%mm0;
+ movd %mm0,(%esi)
+ addl $4,%esi
+ cmpl %edx,%esi
+ jbe .L308
+
+ .p2align 2
+.L310:
+ addl $2,%edx /* now edx = top-2 */
+ cmpl %edx,%esi; ja .L315
+
+ movzwl (%esi),%eax
+ movd %eax,%mm0
+ paddsw %mm2,%mm0
+ psraw %mm3,%mm0;
+ movd %mm0,%eax
+ movw %ax,(%esi)
+
+ .p2align 2
+.L315:
+ emms
+.L399:
+ popl %esi
+ leave
+ ret
+.Lfe4:
+ .size k6vsraw,.Lfe4-k6vsraw
+
+ .align 4
+/* void k6vsllw P3((short *p, int n, int bits) */
+.globl k6vsllw
+ .type k6vsllw,@function
+k6vsllw:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl 8(%ebp),%esi
+ movl 16(%ebp),%ecx
+ andl %ecx,%ecx; jle .L499
+ movl 12(%ebp),%eax
+ leal -16(%esi,%eax,2),%edx /* edx = top - 16 */
+ emms
+ movd %ecx,%mm3
+ cmpl %edx,%esi; ja .L406
+
+ .p2align 2
+.L402: /* 8 words per iteration */
+ movq (%esi),%mm0
+ movq 8(%esi),%mm1
+ psllw %mm3,%mm0;
+ psllw %mm3,%mm1;
+ movq %mm0,(%esi)
+ movq %mm1,8(%esi)
+ addl $16,%esi
+ cmpl %edx,%esi
+ jbe .L402
+
+ .p2align 2
+.L406:
+ addl $12,%edx /* now edx = top-4 */
+ cmpl %edx,%esi; ja .L410
+
+ .p2align 2
+.L408: /* do up to 6 words, two at a time */
+ movd (%esi),%mm0
+ psllw %mm3,%mm0;
+ movd %mm0,(%esi)
+ addl $4,%esi
+ cmpl %edx,%esi
+ jbe .L408
+
+ .p2align 2
+.L410:
+ addl $2,%edx /* now edx = top-2 */
+ cmpl %edx,%esi; ja .L415
+
+ movzwl (%esi),%eax
+ movd %eax,%mm0
+ psllw %mm3,%mm0;
+ movd %mm0,%eax
+ movw %ax,(%esi)
+
+ .p2align 2
+.L415:
+ emms
+.L499:
+ popl %esi
+ leave
+ ret
+.Lfe5:
+ .size k6vsllw,.Lfe5-k6vsllw
+
+
+.section .rodata
+ .align 4
+ .type extremes,@object
+ .size extremes,8
+extremes:
+ .long 0x80008000
+ .long 0x7fff7fff
+ .type ones,@object
+ .size ones,8
+ones:
+ .long 0x00010001
+ .long 0x00010001
+
+.text
+ .align 4
+/* long k6maxmin (const short *p, int n, short *out) */
+.globl k6maxmin
+ .type k6maxmin,@function
+k6maxmin:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ emms
+ movl 8(%ebp),%esi
+ movl 12(%ebp),%eax
+ leal -8(%esi,%eax,2),%edx
+
+ cmpl %edx,%esi
+ jbe .L52
+ movd extremes,%mm0
+ movd extremes+4,%mm1
+ jmp .L58
+
+ .p2align 2
+.L52:
+ movq (%esi),%mm0 /* mm0 will be max's */
+ movq %mm0,%mm1 /* mm1 will be min's */
+ addl $8,%esi
+ cmpl %edx,%esi
+ ja .L56
+
+ .p2align 2
+.L54:
+ movq (%esi),%mm2
+
+ movq %mm2,%mm3
+ pcmpgtw %mm0,%mm3 /* mm3 is bitmask for words where mm2 > mm0 */
+ movq %mm3,%mm4
+ pand %mm2,%mm3 /* mm3 is mm2 masked to new max's */
+ pandn %mm0,%mm4 /* mm4 is mm0 masked to its max's */
+ por %mm3,%mm4
+ movq %mm4,%mm0 /* now mm0 is updated max's */
+
+ movq %mm1,%mm3
+ pcmpgtw %mm2,%mm3 /* mm3 is bitmask for words where mm2 < mm1 */
+ pand %mm3,%mm2 /* mm2 is mm2 masked to new min's */
+ pandn %mm1,%mm3 /* mm3 is mm1 masked to its min's */
+ por %mm3,%mm2
+ movq %mm2,%mm1 /* now mm1 is updated min's */
+
+ addl $8,%esi
+ cmpl %edx,%esi
+ jbe .L54
+
+ .p2align 2
+.L56: /* merge down the 4-word max/mins to lower 2 words */
+
+ movq %mm0,%mm2
+ psrlq $32,%mm2
+ movq %mm2,%mm3
+ pcmpgtw %mm0,%mm3 /* mm3 is bitmask for words where mm2 > mm0 */
+ pand %mm3,%mm2 /* mm2 is mm2 masked to new max's */
+ pandn %mm0,%mm3 /* mm3 is mm0 masked to its max's */
+ por %mm3,%mm2
+ movq %mm2,%mm0 /* now mm0 is updated max's */
+
+ movq %mm1,%mm2
+ psrlq $32,%mm2
+ movq %mm1,%mm3
+ pcmpgtw %mm2,%mm3 /* mm3 is bitmask for words where mm2 < mm1 */
+ pand %mm3,%mm2 /* mm2 is mm2 masked to new min's */
+ pandn %mm1,%mm3 /* mm3 is mm1 masked to its min's */
+ por %mm3,%mm2
+ movq %mm2,%mm1 /* now mm1 is updated min's */
+
+ .p2align 2
+.L58:
+ addl $4,%edx /* now dx = top-4 */
+ cmpl %edx,%esi
+ ja .L62
+ /* here, there are >= 2 words of input remaining */
+ movd (%esi),%mm2
+
+ movq %mm2,%mm3
+ pcmpgtw %mm0,%mm3 /* mm3 is bitmask for words where mm2 > mm0 */
+ movq %mm3,%mm4
+ pand %mm2,%mm3 /* mm3 is mm2 masked to new max's */
+ pandn %mm0,%mm4 /* mm4 is mm0 masked to its max's */
+ por %mm3,%mm4
+ movq %mm4,%mm0 /* now mm0 is updated max's */
+
+ movq %mm1,%mm3
+ pcmpgtw %mm2,%mm3 /* mm3 is bitmask for words where mm2 < mm1 */
+ pand %mm3,%mm2 /* mm2 is mm2 masked to new min's */
+ pandn %mm1,%mm3 /* mm3 is mm1 masked to its min's */
+ por %mm3,%mm2
+ movq %mm2,%mm1 /* now mm1 is updated min's */
+
+ addl $4,%esi
+
+ .p2align 2
+.L62:
+ /* merge down the 2-word max/mins to 1 word */
+
+ movq %mm0,%mm2
+ psrlq $16,%mm2
+ movq %mm2,%mm3
+ pcmpgtw %mm0,%mm3 /* mm3 is bitmask for words where mm2 > mm0 */
+ pand %mm3,%mm2 /* mm2 is mm2 masked to new max's */
+ pandn %mm0,%mm3 /* mm3 is mm0 masked to its max's */
+ por %mm3,%mm2
+ movd %mm2,%ecx /* cx is max so far */
+
+ movq %mm1,%mm2
+ psrlq $16,%mm2
+ movq %mm1,%mm3
+ pcmpgtw %mm2,%mm3 /* mm3 is bitmask for words where mm2 < mm1 */
+ pand %mm3,%mm2 /* mm2 is mm2 masked to new min's */
+ pandn %mm1,%mm3 /* mm3 is mm1 masked to its min's */
+ por %mm3,%mm2
+ movd %mm2,%eax /* ax is min so far */
+
+ addl $2,%edx /* now dx = top-2 */
+ cmpl %edx,%esi
+ ja .L65
+
+ /* here, there is one word of input left */
+ cmpw (%esi),%cx
+ jge .L64
+ movw (%esi),%cx
+ .p2align 2
+.L64:
+ cmpw (%esi),%ax
+ jle .L65
+ movw (%esi),%ax
+
+ .p2align 2
+.L65: /* (finally!) cx is the max, ax the min */
+ movswl %cx,%ecx
+ movswl %ax,%eax
+
+ movl 16(%ebp),%edx /* ptr to output max,min vals */
+ andl %edx,%edx; jz .L77
+ movw %cx,(%edx) /* max */
+ movw %ax,2(%edx) /* min */
+ .p2align 2
+.L77:
+ /* now calculate max absolute val */
+ negl %eax
+ cmpl %ecx,%eax
+ jge .L81
+ movl %ecx,%eax
+ .p2align 2
+.L81:
+ emms
+ popl %esi
+ leave
+ ret
+.Lfe6:
+ .size k6maxmin,.Lfe6-k6maxmin
+
+/* void Short_term_analysis_filtering (short *u0, const short *rp0, int kn, short *s) */
+ .equiv pm_u0,8
+ .equiv pm_rp0,12
+ .equiv pm_kn,16
+ .equiv pm_s,20
+ .equiv lv_u_top,-4
+ .equiv lv_s_top,-8
+ .equiv lv_rp,-40 /* local version of rp0 with each word twice */
+ .align 4
+.globl Short_term_analysis_filteringx
+ .type Short_term_analysis_filteringx,@function
+Short_term_analysis_filteringx:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $40,%esp
+ pushl %edi
+ pushl %esi
+
+ movl pm_rp0(%ebp),%esi;
+ leal lv_rp(%ebp),%edi;
+ cld
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ emms
+ movl $0x4000,%eax;
+ movd %eax,%mm4;
+ punpckldq %mm4,%mm4 /* (0x00004000,0x00004000) for rounding dword product pairs */
+
+ movl pm_u0(%ebp),%eax
+ addl $16,%eax
+ movl %eax,lv_u_top(%ebp) /* UTOP */
+ movl pm_s(%ebp),%edx /* edx is local s ptr throughout below */
+ movl pm_kn(%ebp),%eax
+ leal (%edx,%eax,2),%eax
+ movl %eax,lv_s_top(%ebp)
+ cmpl %eax,%edx
+ jae .L179
+ .p2align 2
+.L181:
+ leal lv_rp(%ebp),%esi /* RP */
+ movl pm_u0(%ebp),%edi /* U */
+ movw (%edx),%ax /* (0,DI) */
+ roll $16,%eax
+ movw (%edx),%ax /* (DI,DI) */
+ .p2align 2
+.L185: /* RP is %esi */
+ movl %eax,%ecx
+ movw (%edi),%ax /* (DI,U) */
+ movd (%esi),%mm3 /* mm3 is (0,0,RP,RP) */
+ movw %cx,(%edi)
+
+ movd %eax,%mm2 /* mm2 is (0,0,DI,U) */
+ rorl $16,%eax
+ movd %eax,%mm1 /* mm1 is (0,0,U,DI) */
+
+ movq %mm1,%mm0
+ pmullw %mm3,%mm0
+ pmulhw %mm3,%mm1
+ punpcklwd %mm1,%mm0 /* mm0 is (RP*U,RP*DI) */
+ paddd %mm4,%mm0 /* mm4 is 0x00004000,0x00004000 */
+ psrad $15,%mm0 /* (RP*U,RP*DI) adjusted */
+ packssdw %mm0,%mm0 /* (*,*,RP*U,RP*DI) adjusted and saturated to word */
+ paddsw %mm2,%mm0 /* mm0 is (?,?, DI', U') */
+ movd %mm0,%eax /* (DI,U') */
+
+ addl $2,%edi
+ addl $4,%esi
+ cmpl lv_u_top(%ebp),%edi
+ jb .L185
+
+ rorl $16,%eax
+ movw %ax,(%edx) /* last DI goes to *s */
+ addl $2,%edx /* next s */
+ cmpl lv_s_top(%ebp),%edx
+ jb .L181
+ .p2align 2
+.L179:
+ emms
+ popl %esi
+ popl %edi
+ leave
+ ret
+.Lfe7:
+ .size Short_term_analysis_filteringx,.Lfe7-Short_term_analysis_filteringx
+
+.end
+
+/* 'as' macro's seem to be case-insensitive */
+.macro STEP n
+.if \n
+ movd \n(%esi),%mm3 /* mm3 is (0,0,RP,RP) */
+.else
+ movd (%esi),%mm3 /* mm3 is (0,0,RP,RP) */
+.endif
+ movq %mm5,%mm1;
+ movd %mm4,%ecx; movw %cx,%ax /* (DI,U) */
+ psllq $48,%mm1; psrlq $16,%mm4; por %mm1,%mm4
+ psllq $48,%mm0; psrlq $16,%mm5; por %mm0,%mm5
+
+ movd %eax,%mm2 /* mm2 is (0,0,DI,U) */
+ rorl $16,%eax
+ movd %eax,%mm1 /* mm1 is (0,0,U,DI) */
+
+ movq %mm1,%mm0
+ pmullw %mm3,%mm0
+ pmulhw %mm3,%mm1
+ punpcklwd %mm1,%mm0 /* mm0 is (RP*U,RP*DI) */
+ paddd %mm6,%mm0 /* mm6 is 0x00004000,0x00004000 */
+ psrad $15,%mm0 /* (RP*U,RP*DI) adjusted */
+ packssdw %mm0,%mm0 /* (*,*,RP*U,RP*DI) adjusted and saturated to word */
+ paddsw %mm2,%mm0 /* mm0 is (?,?, DI', U') */
+ movd %mm0,%eax /* (DI,U') */
+.endm
+
+/* void Short_term_analysis_filtering (short *u0, const short *rp0, int kn, short *s) */
+ .equiv pm_u0,8
+ .equiv pm_rp0,12
+ .equiv pm_kn,16
+ .equiv pm_s,20
+ .equiv lv_rp_top,-4
+ .equiv lv_s_top,-8
+ .equiv lv_rp,-40 /* local version of rp0 with each word twice */
+ .align 4
+.globl Short_term_analysis_filteringx
+ .type Short_term_analysis_filteringx,@function
+Short_term_analysis_filteringx:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $56,%esp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+
+ movl pm_rp0(%ebp),%esi;
+ leal lv_rp(%ebp),%edi;
+ cld
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ lodsw; stosw; stosw
+ movl %edi,lv_rp_top(%ebp)
+ emms
+
+ movl $0x4000,%eax;
+ movd %eax,%mm6;
+ punpckldq %mm6,%mm6 /* (0x00004000,0x00004000) for rounding dword product pairs */
+
+ movl pm_u0(%ebp),%ebx
+ movq (%ebx),%mm4; movq 8(%ebx),%mm5 /* the 8 u's */
+ movl pm_s(%ebp),%edx /* edx is local s ptr throughout below */
+ movl pm_kn(%ebp),%eax
+ leal (%edx,%eax,2),%eax
+ movl %eax,lv_s_top(%ebp)
+ cmpl %eax,%edx
+ jae .L179
+ .p2align 2
+.L181:
+ leal lv_rp(%ebp),%esi /* RP */
+ movw (%edx),%ax /* (0,DI) */
+ roll $16,%eax
+ movw (%edx),%ax /* (DI,DI) */
+ movd %eax,%mm0
+ .p2align 2
+.L185: /* RP is %esi */
+ step 0
+ step 4
+ step 8
+ step 12
+/*
+ step 16
+ step 20
+ step 24
+ step 28
+*/
+ addl $16,%esi
+ cmpl lv_rp_top(%ebp),%esi
+ jb .L185
+
+ rorl $16,%eax
+ movw %ax,(%edx) /* last DI goes to *s */
+ addl $2,%edx /* next s */
+ cmpl lv_s_top(%ebp),%edx
+ jb .L181
+.L179:
+ movq %mm4,(%ebx); movq %mm5,8(%ebx) /* the 8 u's */
+ emms
+ popl %ebx
+ popl %esi
+ popl %edi
+ leave
+ ret
+.Lfe7:
+ .size Short_term_analysis_filteringx,.Lfe7-Short_term_analysis_filteringx
+ .ident "GCC: (GNU) 2.95.2 19991109 (Debian GNU/Linux)"
diff --git a/trunk/codecs/gsm/src/long_term.c b/trunk/codecs/gsm/src/long_term.c
new file mode 100644
index 000000000..83b6fdf85
--- /dev/null
+++ b/trunk/codecs/gsm/src/long_term.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+#ifdef K6OPT
+#include "k6opt.h"
+#endif
+/*
+ * 4.2.11 .. 4.2.12 LONG TERM PREDICTOR (LTP) SECTION
+ */
+
+
+/*
+ * This module computes the LTP gain (bc) and the LTP lag (Nc)
+ * for the long term analysis filter. This is done by calculating a
+ * maximum of the cross-correlation function between the current
+ * sub-segment short term residual signal d[0..39] (output of
+ * the short term analysis filter; for simplification the index
+ * of this array begins at 0 and ends at 39 for each sub-segment of the
+ * RPE-LTP analysis) and the previous reconstructed short term
+ * residual signal dp[ -120 .. -1 ]. A dynamic scaling must be
+ * performed to avoid overflow.
+ */
+
+ /* The next procedure exists in six versions. First two integer
+ * version (if USE_FLOAT_MUL is not defined); then four floating
+ * point versions, twice with proper scaling (USE_FLOAT_MUL defined),
+ * once without (USE_FLOAT_MUL and FAST defined, and fast run-time
+ * option used). Every pair has first a Cut version (see the -C
+ * option to toast or the LTP_CUT option to gsm_option()), then the
+ * uncut one. (For a detailed explanation of why this is altogether
+ * a bad idea, see Henry Spencer and Geoff Collyer, ``#ifdef Considered
+ * Harmful''.)
+ */
+
+#ifndef USE_FLOAT_MUL
+
+#ifdef LTP_CUT
+
+static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out),
+
+ struct gsm_state * st,
+
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+ word wt[40];
+
+ longword L_result;
+ longword L_max, L_power;
+ word R, S, dmax, scal, best_k;
+ word ltp_cut;
+
+ register word temp, wt_k;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) {
+ dmax = temp;
+ best_k = k;
+ }
+ }
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+ assert(scal >= 0);
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+ wt_k = SASR(d[best_k], scal);
+
+ for (lambda = 40; lambda <= 120; lambda++) {
+ L_result = (longword)wt_k * dp[best_k - lambda];
+ if (L_result > L_max) {
+ Nc = lambda;
+ L_max = L_result;
+ }
+ }
+ *Nc_out = Nc;
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = SASR( L_max << temp, 16 );
+ S = SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#endif /* LTP_CUT */
+
+static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k;
+#ifndef K6OPT
+ register int lambda;
+#endif
+ word Nc, bc;
+ word wt[40];
+
+ longword L_max, L_power;
+ word R, S, dmax, scal;
+ register word temp;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) dmax = temp;
+ }
+
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+
+ assert(scal >= 0);
+
+ /* Initialization of a working array wt
+ */
+
+ for (k = 0; k <= 39; k++) wt[k] = SASR( d[k], scal );
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+# ifdef K6OPT
+ L_max = k6maxcc(wt,dp,&Nc);
+# else
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda++) {
+
+# undef STEP
+# define STEP(k) (longword)wt[k] * dp[k - lambda]
+
+ register longword L_result;
+
+ L_result = STEP(0) ; L_result += STEP(1) ;
+ L_result += STEP(2) ; L_result += STEP(3) ;
+ L_result += STEP(4) ; L_result += STEP(5) ;
+ L_result += STEP(6) ; L_result += STEP(7) ;
+ L_result += STEP(8) ; L_result += STEP(9) ;
+ L_result += STEP(10) ; L_result += STEP(11) ;
+ L_result += STEP(12) ; L_result += STEP(13) ;
+ L_result += STEP(14) ; L_result += STEP(15) ;
+ L_result += STEP(16) ; L_result += STEP(17) ;
+ L_result += STEP(18) ; L_result += STEP(19) ;
+ L_result += STEP(20) ; L_result += STEP(21) ;
+ L_result += STEP(22) ; L_result += STEP(23) ;
+ L_result += STEP(24) ; L_result += STEP(25) ;
+ L_result += STEP(26) ; L_result += STEP(27) ;
+ L_result += STEP(28) ; L_result += STEP(29) ;
+ L_result += STEP(30) ; L_result += STEP(31) ;
+ L_result += STEP(32) ; L_result += STEP(33) ;
+ L_result += STEP(34) ; L_result += STEP(35) ;
+ L_result += STEP(36) ; L_result += STEP(37) ;
+ L_result += STEP(38) ; L_result += STEP(39) ;
+
+ if (L_result > L_max) {
+
+ Nc = lambda;
+ L_max = L_result;
+ }
+ }
+# endif
+ *Nc_out = Nc;
+
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = (word)SASR( L_max << temp, 16 );
+ S = (word)SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#else /* USE_FLOAT_MUL */
+
+#ifdef LTP_CUT
+
+static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out),
+ struct gsm_state * st, /* IN */
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+ word ltp_cut;
+
+ float wt_float[40];
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ longword L_max, L_power;
+ word R, S, dmax, scal;
+ register word temp;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) dmax = temp;
+ }
+
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+
+ assert(scal >= 0);
+ ltp_cut = (longword)SASR(dmax, scal) * st->ltp_cut / 100;
+
+
+ /* Initialization of a working array wt
+ */
+
+ for (k = 0; k < 40; k++) {
+ register word w = SASR( d[k], scal );
+ if (w < 0 ? w > -ltp_cut : w < ltp_cut) {
+ wt_float[k] = 0.0;
+ }
+ else {
+ wt_float[k] = w;
+ }
+ }
+ for (k = -120; k < 0; k++) dp_float[k] = dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda += 9) {
+
+ /* Calculate L_result for l = lambda .. lambda + 9.
+ */
+ register float *lp = dp_float - lambda;
+
+ register float W;
+ register float a = lp[-8], b = lp[-7], c = lp[-6],
+ d = lp[-5], e = lp[-4], f = lp[-3],
+ g = lp[-2], h = lp[-1];
+ register float E;
+ register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+ S5 = 0, S6 = 0, S7 = 0, S8 = 0;
+
+# undef STEP
+# define STEP(K, a, b, c, d, e, f, g, h) \
+ if ((W = wt_float[K]) != 0.0) { \
+ E = W * a; S8 += E; \
+ E = W * b; S7 += E; \
+ E = W * c; S6 += E; \
+ E = W * d; S5 += E; \
+ E = W * e; S4 += E; \
+ E = W * f; S3 += E; \
+ E = W * g; S2 += E; \
+ E = W * h; S1 += E; \
+ a = lp[K]; \
+ E = W * a; S0 += E; } else (a = lp[K])
+
+# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h)
+# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a)
+# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b)
+# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c)
+# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d)
+# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e)
+# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f)
+# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g)
+
+ STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);
+ STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);
+
+ STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);
+ STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);
+
+ STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);
+ STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);
+
+ STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);
+ STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);
+
+ STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);
+ STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);
+
+ if (S0 > L_max) { L_max = S0; Nc = lambda; }
+ if (S1 > L_max) { L_max = S1; Nc = lambda + 1; }
+ if (S2 > L_max) { L_max = S2; Nc = lambda + 2; }
+ if (S3 > L_max) { L_max = S3; Nc = lambda + 3; }
+ if (S4 > L_max) { L_max = S4; Nc = lambda + 4; }
+ if (S5 > L_max) { L_max = S5; Nc = lambda + 5; }
+ if (S6 > L_max) { L_max = S6; Nc = lambda + 6; }
+ if (S7 > L_max) { L_max = S7; Nc = lambda + 7; }
+ if (S8 > L_max) { L_max = S8; Nc = lambda + 8; }
+
+ }
+ *Nc_out = Nc;
+
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = SASR( L_max << temp, 16 );
+ S = SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#endif /* LTP_CUT */
+
+static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+
+ float wt_float[40];
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ longword L_max, L_power;
+ word R, S, dmax, scal;
+ register word temp;
+
+ /* Search of the optimum scaling of d[0..39].
+ */
+ dmax = 0;
+
+ for (k = 0; k <= 39; k++) {
+ temp = d[k];
+ temp = GSM_ABS( temp );
+ if (temp > dmax) dmax = temp;
+ }
+
+ temp = 0;
+ if (dmax == 0) scal = 0;
+ else {
+ assert(dmax > 0);
+ temp = gsm_norm( (longword)dmax << 16 );
+ }
+
+ if (temp > 6) scal = 0;
+ else scal = 6 - temp;
+
+ assert(scal >= 0);
+
+ /* Initialization of a working array wt
+ */
+
+ for (k = 0; k < 40; k++) wt_float[k] = SASR( d[k], scal );
+ for (k = -120; k < 0; k++) dp_float[k] = dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda += 9) {
+
+ /* Calculate L_result for l = lambda .. lambda + 9.
+ */
+ register float *lp = dp_float - lambda;
+
+ register float W;
+ register float a = lp[-8], b = lp[-7], c = lp[-6],
+ d = lp[-5], e = lp[-4], f = lp[-3],
+ g = lp[-2], h = lp[-1];
+ register float E;
+ register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+ S5 = 0, S6 = 0, S7 = 0, S8 = 0;
+
+# undef STEP
+# define STEP(K, a, b, c, d, e, f, g, h) \
+ W = wt_float[K]; \
+ E = W * a; S8 += E; \
+ E = W * b; S7 += E; \
+ E = W * c; S6 += E; \
+ E = W * d; S5 += E; \
+ E = W * e; S4 += E; \
+ E = W * f; S3 += E; \
+ E = W * g; S2 += E; \
+ E = W * h; S1 += E; \
+ a = lp[K]; \
+ E = W * a; S0 += E
+
+# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h)
+# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a)
+# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b)
+# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c)
+# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d)
+# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e)
+# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f)
+# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g)
+
+ STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);
+ STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);
+
+ STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);
+ STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);
+
+ STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);
+ STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);
+
+ STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);
+ STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);
+
+ STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);
+ STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);
+
+ if (S0 > L_max) { L_max = S0; Nc = lambda; }
+ if (S1 > L_max) { L_max = S1; Nc = lambda + 1; }
+ if (S2 > L_max) { L_max = S2; Nc = lambda + 2; }
+ if (S3 > L_max) { L_max = S3; Nc = lambda + 3; }
+ if (S4 > L_max) { L_max = S4; Nc = lambda + 4; }
+ if (S5 > L_max) { L_max = S5; Nc = lambda + 5; }
+ if (S6 > L_max) { L_max = S6; Nc = lambda + 6; }
+ if (S7 > L_max) { L_max = S7; Nc = lambda + 7; }
+ if (S8 > L_max) { L_max = S8; Nc = lambda + 8; }
+ }
+ *Nc_out = Nc;
+
+ L_max <<= 1;
+
+ /* Rescaling of L_max
+ */
+ assert(scal <= 100 && scal >= -100);
+ L_max = L_max >> (6 - scal); /* sub(6, scal) */
+
+ assert( Nc <= 120 && Nc >= 40);
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ L_power = 0;
+ for (k = 0; k <= 39; k++) {
+
+ register longword L_temp;
+
+ L_temp = SASR( dp[k - Nc], 3 );
+ L_power += L_temp * L_temp;
+ }
+ L_power <<= 1; /* from L_MULT */
+
+ /* Normalization of L_max and L_power
+ */
+
+ if (L_max <= 0) {
+ *bc_out = 0;
+ return;
+ }
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ temp = gsm_norm( L_power );
+
+ R = SASR( L_max << temp, 16 );
+ S = SASR( L_power << temp, 16 );
+
+ /* Coding of the LTP gain
+ */
+
+ /* Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break;
+ *bc_out = bc;
+}
+
+#ifdef FAST
+#ifdef LTP_CUT
+
+static void Cut_Fast_Calculation_of_the_LTP_parameters P5((st,
+ d,dp,bc_out,Nc_out),
+ struct gsm_state * st, /* IN */
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ register float wt_float;
+ word Nc, bc;
+ word wt_max, best_k, ltp_cut;
+
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ register float L_result, L_max, L_power;
+
+ wt_max = 0;
+
+ for (k = 0; k < 40; ++k) {
+ if ( d[k] > wt_max) wt_max = d[best_k = k];
+ else if (-d[k] > wt_max) wt_max = -d[best_k = k];
+ }
+
+ assert(wt_max >= 0);
+ wt_float = (float)wt_max;
+
+ for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda++) {
+ L_result = wt_float * dp_float[best_k - lambda];
+ if (L_result > L_max) {
+ Nc = lambda;
+ L_max = L_result;
+ }
+ }
+
+ *Nc_out = Nc;
+ if (L_max <= 0.) {
+ *bc_out = 0;
+ return;
+ }
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ dp_float -= Nc;
+ L_power = 0;
+ for (k = 0; k < 40; ++k) {
+ register float f = dp_float[k];
+ L_power += f * f;
+ }
+
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ /* Coding of the LTP gain
+ * Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ lambda = L_max / L_power * 32768.;
+ for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break;
+ *bc_out = bc;
+}
+
+#endif /* LTP_CUT */
+
+static void Fast_Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out),
+ register word * d, /* [0..39] IN */
+ register word * dp, /* [-120..-1] IN */
+ word * bc_out, /* OUT */
+ word * Nc_out /* OUT */
+)
+{
+ register int k, lambda;
+ word Nc, bc;
+
+ float wt_float[40];
+ float dp_float_base[120], * dp_float = dp_float_base + 120;
+
+ register float L_max, L_power;
+
+ for (k = 0; k < 40; ++k) wt_float[k] = (float)d[k];
+ for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k];
+
+ /* Search for the maximum cross-correlation and coding of the LTP lag
+ */
+ L_max = 0;
+ Nc = 40; /* index for the maximum cross-correlation */
+
+ for (lambda = 40; lambda <= 120; lambda += 9) {
+
+ /* Calculate L_result for l = lambda .. lambda + 9.
+ */
+ register float *lp = dp_float - lambda;
+
+ register float W;
+ register float a = lp[-8], b = lp[-7], c = lp[-6],
+ d = lp[-5], e = lp[-4], f = lp[-3],
+ g = lp[-2], h = lp[-1];
+ register float E;
+ register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0,
+ S5 = 0, S6 = 0, S7 = 0, S8 = 0;
+
+# undef STEP
+# define STEP(K, a, b, c, d, e, f, g, h) \
+ W = wt_float[K]; \
+ E = W * a; S8 += E; \
+ E = W * b; S7 += E; \
+ E = W * c; S6 += E; \
+ E = W * d; S5 += E; \
+ E = W * e; S4 += E; \
+ E = W * f; S3 += E; \
+ E = W * g; S2 += E; \
+ E = W * h; S1 += E; \
+ a = lp[K]; \
+ E = W * a; S0 += E
+
+# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h)
+# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a)
+# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b)
+# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c)
+# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d)
+# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e)
+# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f)
+# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g)
+
+ STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3);
+ STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7);
+
+ STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11);
+ STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15);
+
+ STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19);
+ STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23);
+
+ STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27);
+ STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31);
+
+ STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35);
+ STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39);
+
+ if (S0 > L_max) { L_max = S0; Nc = lambda; }
+ if (S1 > L_max) { L_max = S1; Nc = lambda + 1; }
+ if (S2 > L_max) { L_max = S2; Nc = lambda + 2; }
+ if (S3 > L_max) { L_max = S3; Nc = lambda + 3; }
+ if (S4 > L_max) { L_max = S4; Nc = lambda + 4; }
+ if (S5 > L_max) { L_max = S5; Nc = lambda + 5; }
+ if (S6 > L_max) { L_max = S6; Nc = lambda + 6; }
+ if (S7 > L_max) { L_max = S7; Nc = lambda + 7; }
+ if (S8 > L_max) { L_max = S8; Nc = lambda + 8; }
+ }
+ *Nc_out = Nc;
+
+ if (L_max <= 0.) {
+ *bc_out = 0;
+ return;
+ }
+
+ /* Compute the power of the reconstructed short term residual
+ * signal dp[..]
+ */
+ dp_float -= Nc;
+ L_power = 0;
+ for (k = 0; k < 40; ++k) {
+ register float f = dp_float[k];
+ L_power += f * f;
+ }
+
+ if (L_max >= L_power) {
+ *bc_out = 3;
+ return;
+ }
+
+ /* Coding of the LTP gain
+ * Table 4.3a must be used to obtain the level DLB[i] for the
+ * quantization of the LTP gain b to get the coded version bc.
+ */
+ lambda = L_max / L_power * 32768.;
+ for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break;
+ *bc_out = bc;
+}
+
+#endif /* FAST */
+#endif /* USE_FLOAT_MUL */
+
+
+/* 4.2.12 */
+
+static void Long_term_analysis_filtering P6((bc,Nc,dp,d,dpp,e),
+ word bc, /* IN */
+ word Nc, /* IN */
+ register word * dp, /* previous d [-120..-1] IN */
+ register word * d, /* d [0..39] IN */
+ register word * dpp, /* estimate [0..39] OUT */
+ register word * e /* long term res. signal [0..39] OUT */
+)
+/*
+ * In this part, we have to decode the bc parameter to compute
+ * the samples of the estimate dpp[0..39]. The decoding of bc needs the
+ * use of table 4.3b. The long term residual signal e[0..39]
+ * is then calculated to be fed to the RPE encoding section.
+ */
+{
+ register int k;
+
+# undef STEP
+# define STEP(BP) \
+ for (k = 0; k <= 39; k++) { \
+ dpp[k] = (word)GSM_MULT_R( BP, dp[k - Nc]); \
+ e[k] = GSM_SUB( d[k], dpp[k] ); \
+ }
+
+ switch (bc) {
+ case 0: STEP( 3277 ); break;
+ case 1: STEP( 11469 ); break;
+ case 2: STEP( 21299 ); break;
+ case 3: STEP( 32767 ); break;
+ }
+}
+
+void Gsm_Long_Term_Predictor P7((S,d,dp,e,dpp,Nc,bc), /* 4x for 160 samples */
+
+ struct gsm_state * S,
+
+ word * d, /* [0..39] residual signal IN */
+ word * dp, /* [-120..-1] d' IN */
+
+ word * e, /* [0..39] OUT */
+ word * dpp, /* [0..39] OUT */
+ word * Nc, /* correlation lag OUT */
+ word * bc /* gain factor OUT */
+)
+{
+ assert( d ); assert( dp ); assert( e );
+ assert( dpp); assert( Nc ); assert( bc );
+
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+ if (S->fast)
+#if defined (LTP_CUT)
+ if (S->ltp_cut)
+ Cut_Fast_Calculation_of_the_LTP_parameters(S,
+ d, dp, bc, Nc);
+ else
+#endif /* LTP_CUT */
+ Fast_Calculation_of_the_LTP_parameters(d, dp, bc, Nc );
+ else
+#endif /* FAST & USE_FLOAT_MUL */
+#ifdef LTP_CUT
+ if (S->ltp_cut)
+ Cut_Calculation_of_the_LTP_parameters(S, d, dp, bc, Nc);
+ else
+#endif
+ Calculation_of_the_LTP_parameters(d, dp, bc, Nc);
+
+ Long_term_analysis_filtering( *bc, *Nc, dp, d, dpp, e );
+}
+
+/* 4.3.2 */
+void Gsm_Long_Term_Synthesis_Filtering P5((S,Ncr,bcr,erp,drp),
+ struct gsm_state * S,
+
+ word Ncr,
+ word bcr,
+ register word * erp, /* [0..39] IN */
+ register word * drp /* [-120..-1] IN, [-120..40] OUT */
+)
+/*
+ * This procedure uses the bcr and Ncr parameter to realize the
+ * long term synthesis filtering. The decoding of bcr needs
+ * table 4.3b.
+ */
+{
+ register int k;
+ word brp, drpp, Nr;
+
+ /* Check the limits of Nr.
+ */
+ Nr = Ncr < 40 || Ncr > 120 ? S->nrp : Ncr;
+ S->nrp = Nr;
+ assert(Nr >= 40 && Nr <= 120);
+
+ /* Decoding of the LTP gain bcr
+ */
+ brp = gsm_QLB[ bcr ];
+
+ /* Computation of the reconstructed short term residual
+ * signal drp[0..39]
+ */
+ assert(brp != MIN_WORD);
+
+ for (k = 0; k <= 39; k++) {
+ drpp = (word)GSM_MULT_R( brp, drp[ k - Nr ] );
+ drp[k] = GSM_ADD( erp[k], drpp );
+ }
+
+ /*
+ * Update of the reconstructed short term residual signal
+ * drp[ -1..-120 ]
+ */
+
+ for (k = 0; k <= 119; k++) drp[ -120 + k ] = drp[ -80 + k ];
+}
diff --git a/trunk/codecs/gsm/src/lpc.c b/trunk/codecs/gsm/src/lpc.c
new file mode 100644
index 000000000..744149e02
--- /dev/null
+++ b/trunk/codecs/gsm/src/lpc.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+#ifdef K6OPT
+#include "k6opt.h"
+#endif
+
+#undef P
+
+/*
+ * 4.2.4 .. 4.2.7 LPC ANALYSIS SECTION
+ */
+
+/* 4.2.4 */
+
+
+static void Autocorrelation P2((s, L_ACF),
+ word * s, /* [0..159] IN/OUT */
+ longword * L_ACF) /* [0..8] OUT */
+/*
+ * The goal is to compute the array L_ACF[k]. The signal s[i] must
+ * be scaled in order to avoid an overflow situation.
+ */
+{
+#ifndef K6OPT
+ register int k, i;
+ word temp;
+#endif
+
+ word smax, scalauto;
+
+#ifdef USE_FLOAT_MUL
+ float float_s[160];
+#endif
+
+ /* Dynamic scaling of the array s[0..159]
+ */
+
+ /* Search for the maximum.
+ */
+#ifndef K6OPT
+ smax = 0;
+ for (k = 0; k <= 159; k++) {
+ temp = GSM_ABS( s[k] );
+ if (temp > smax) smax = temp;
+ }
+#else
+ {
+ longword lmax;
+ lmax = k6maxmin(s,160,NULL);
+ smax = (lmax > MAX_WORD) ? MAX_WORD : lmax;
+ }
+#endif
+ /* Computation of the scaling factor.
+ */
+ if (smax == 0) scalauto = 0;
+ else {
+ assert(smax > 0);
+ scalauto = 4 - gsm_norm( (longword)smax << 16 );/* sub(4,..) */
+ }
+
+ /* Scaling of the array s[0...159]
+ */
+
+ if (scalauto > 0) {
+# ifndef K6OPT
+
+# ifdef USE_FLOAT_MUL
+# define SCALE(n) \
+ case n: for (k = 0; k <= 159; k++) \
+ float_s[k] = (float) \
+ (s[k] = GSM_MULT_R(s[k], 16384 >> (n-1)));\
+ break;
+# else
+# define SCALE(n) \
+ case n: for (k = 0; k <= 159; k++) \
+ s[k] = (word)GSM_MULT_R( s[k], 16384 >> (n-1) );\
+ break;
+# endif /* USE_FLOAT_MUL */
+
+ switch (scalauto) {
+ SCALE(1)
+ SCALE(2)
+ SCALE(3)
+ SCALE(4)
+ }
+# undef SCALE
+
+# else /* K6OPT */
+ k6vsraw(s,160,scalauto);
+# endif
+ }
+# ifdef USE_FLOAT_MUL
+ else for (k = 0; k <= 159; k++) float_s[k] = (float) s[k];
+# endif
+
+ /* Compute the L_ACF[..].
+ */
+#ifndef K6OPT
+ {
+# ifdef USE_FLOAT_MUL
+ register float * sp = float_s;
+ register float sl = *sp;
+
+# define STEP(k) L_ACF[k] += (longword)(sl * sp[ -(k) ]);
+# else
+ word * sp = s;
+ word sl = *sp;
+
+# define STEP(k) L_ACF[k] += ((longword)sl * sp[ -(k) ]);
+# endif
+
+# define NEXTI sl = *++sp
+
+
+ for (k = 9; k--; L_ACF[k] = 0) ;
+
+ STEP (0);
+ NEXTI;
+ STEP(0); STEP(1);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6);
+ NEXTI;
+ STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); STEP(7);
+
+ for (i = 8; i <= 159; i++) {
+
+ NEXTI;
+
+ STEP(0);
+ STEP(1); STEP(2); STEP(3); STEP(4);
+ STEP(5); STEP(6); STEP(7); STEP(8);
+ }
+
+ for (k = 9; k--; L_ACF[k] <<= 1) ;
+
+ }
+
+#else
+ {
+ int k;
+ for (k=0; k<9; k++) {
+ L_ACF[k] = 2*k6iprod(s,s+k,160-k);
+ }
+ }
+#endif
+ /* Rescaling of the array s[0..159]
+ */
+ if (scalauto > 0) {
+ assert(scalauto <= 4);
+#ifndef K6OPT
+ for (k = 160; k--; *s++ <<= scalauto) ;
+# else /* K6OPT */
+ k6vsllw(s,160,scalauto);
+# endif
+ }
+}
+
+#if defined(USE_FLOAT_MUL) && defined(FAST)
+
+static void Fast_Autocorrelation P2((s, L_ACF),
+ word * s, /* [0..159] IN/OUT */
+ longword * L_ACF) /* [0..8] OUT */
+{
+ register int k, i;
+ float f_L_ACF[9];
+ float scale;
+
+ float s_f[160];
+ register float *sf = s_f;
+
+ for (i = 0; i < 160; ++i) sf[i] = s[i];
+ for (k = 0; k <= 8; k++) {
+ register float L_temp2 = 0;
+ register float *sfl = sf - k;
+ for (i = k; i < 160; ++i) L_temp2 += sf[i] * sfl[i];
+ f_L_ACF[k] = L_temp2;
+ }
+ scale = MAX_LONGWORD / f_L_ACF[0];
+
+ for (k = 0; k <= 8; k++) {
+ L_ACF[k] = f_L_ACF[k] * scale;
+ }
+}
+#endif /* defined (USE_FLOAT_MUL) && defined (FAST) */
+
+/* 4.2.5 */
+
+static void Reflection_coefficients P2( (L_ACF, r),
+ longword * L_ACF, /* 0...8 IN */
+ register word * r /* 0...7 OUT */
+)
+{
+ register int i, m, n;
+ register word temp;
+ word ACF[9]; /* 0..8 */
+ word P[ 9]; /* 0..8 */
+ word K[ 9]; /* 2..8 */
+
+ /* Schur recursion with 16 bits arithmetic.
+ */
+
+ if (L_ACF[0] == 0) {
+ for (i = 8; i--; *r++ = 0) ;
+ return;
+ }
+
+ assert( L_ACF[0] != 0 );
+ temp = gsm_norm( L_ACF[0] );
+
+ assert(temp >= 0 && temp < 32);
+
+ /* ? overflow ? */
+ for (i = 0; i <= 8; i++) ACF[i] = (word)SASR( L_ACF[i] << temp, 16 );
+
+ /* Initialize array P[..] and K[..] for the recursion.
+ */
+
+ for (i = 1; i <= 7; i++) K[ i ] = ACF[ i ];
+ for (i = 0; i <= 8; i++) P[ i ] = ACF[ i ];
+
+ /* Compute reflection coefficients
+ */
+ for (n = 1; n <= 8; n++, r++) {
+
+ temp = P[1];
+ temp = GSM_ABS(temp);
+ if (P[0] < temp) {
+ for (i = n; i <= 8; i++) *r++ = 0;
+ return;
+ }
+
+ *r = gsm_div( temp, P[0] );
+
+ assert(*r >= 0);
+ if (P[1] > 0) *r = -*r; /* r[n] = sub(0, r[n]) */
+ assert (*r != MIN_WORD);
+ if (n == 8) return;
+
+ /* Schur recursion
+ */
+ temp = (word)GSM_MULT_R( P[1], *r );
+ P[0] = GSM_ADD( P[0], temp );
+
+ for (m = 1; m <= 8 - n; m++) {
+ temp = (word)GSM_MULT_R( K[ m ], *r );
+ P[m] = GSM_ADD( P[ m+1 ], temp );
+
+ temp = (word)GSM_MULT_R( P[ m+1 ], *r );
+ K[m] = GSM_ADD( K[ m ], temp );
+ }
+ }
+}
+
+/* 4.2.6 */
+
+static void Transformation_to_Log_Area_Ratios P1((r),
+ register word * r /* 0..7 IN/OUT */
+)
+/*
+ * The following scaling for r[..] and LAR[..] has been used:
+ *
+ * r[..] = integer( real_r[..]*32768. ); -1 <= real_r < 1.
+ * LAR[..] = integer( real_LAR[..] * 16384 );
+ * with -1.625 <= real_LAR <= 1.625
+ */
+{
+ register word temp;
+ register int i;
+
+
+ /* Computation of the LAR[0..7] from the r[0..7]
+ */
+ for (i = 1; i <= 8; i++, r++) {
+
+ temp = *r;
+ temp = GSM_ABS(temp);
+ assert(temp >= 0);
+
+ if (temp < 22118) {
+ temp >>= 1;
+ } else if (temp < 31130) {
+ assert( temp >= 11059 );
+ temp -= 11059;
+ } else {
+ assert( temp >= 26112 );
+ temp -= 26112;
+ temp <<= 2;
+ }
+
+ *r = *r < 0 ? -temp : temp;
+ assert( *r != MIN_WORD );
+ }
+}
+
+/* 4.2.7 */
+
+static void Quantization_and_coding P1((LAR),
+ register word * LAR /* [0..7] IN/OUT */
+)
+{
+ register word temp;
+
+
+ /* This procedure needs four tables; the following equations
+ * give the optimum scaling for the constants:
+ *
+ * A[0..7] = integer( real_A[0..7] * 1024 )
+ * B[0..7] = integer( real_B[0..7] * 512 )
+ * MAC[0..7] = maximum of the LARc[0..7]
+ * MIC[0..7] = minimum of the LARc[0..7]
+ */
+
+# undef STEP
+# define STEP( A, B, MAC, MIC ) \
+ temp = (word)GSM_MULT( A, *LAR ); \
+ temp = GSM_ADD( temp, B ); \
+ temp = GSM_ADD( temp, 256 ); \
+ temp = (word)SASR( temp, 9 ); \
+ *LAR = temp>MAC ? MAC - MIC : (temp<MIC ? 0 : temp - MIC); \
+ LAR++;
+
+ STEP( 20480, 0, 31, -32 );
+ STEP( 20480, 0, 31, -32 );
+ STEP( 20480, 2048, 15, -16 );
+ STEP( 20480, -2560, 15, -16 );
+
+ STEP( 13964, 94, 7, -8 );
+ STEP( 15360, -1792, 7, -8 );
+ STEP( 8534, -341, 3, -4 );
+ STEP( 9036, -1144, 3, -4 );
+
+# undef STEP
+}
+
+void Gsm_LPC_Analysis P3((S, s,LARc),
+ struct gsm_state *S,
+ word * s, /* 0..159 signals IN/OUT */
+ word * LARc) /* 0..7 LARc's OUT */
+{
+ longword L_ACF[9];
+
+#if defined(USE_FLOAT_MUL) && defined(FAST)
+ if (S->fast) Fast_Autocorrelation (s, L_ACF );
+ else
+#endif
+ Autocorrelation (s, L_ACF );
+ Reflection_coefficients (L_ACF, LARc );
+ Transformation_to_Log_Area_Ratios (LARc);
+ Quantization_and_coding (LARc);
+}
diff --git a/trunk/codecs/gsm/src/preprocess.c b/trunk/codecs/gsm/src/preprocess.c
new file mode 100644
index 000000000..eacdac851
--- /dev/null
+++ b/trunk/codecs/gsm/src/preprocess.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+/* 4.2.0 .. 4.2.3 PREPROCESSING SECTION
+ *
+ * After A-law to linear conversion (or directly from the
+ * Ato D converter) the following scaling is assumed for
+ * input to the RPE-LTP algorithm:
+ *
+ * in: 0.1.....................12
+ * S.v.v.v.v.v.v.v.v.v.v.v.v.*.*.*
+ *
+ * Where S is the sign bit, v a valid bit, and * a "don't care" bit.
+ * The original signal is called sop[..]
+ *
+ * out: 0.1................... 12
+ * S.S.v.v.v.v.v.v.v.v.v.v.v.v.0.0
+ */
+
+
+void Gsm_Preprocess P3((S, s, so),
+ struct gsm_state * S,
+ word * s,
+ word * so ) /* [0..159] IN/OUT */
+{
+ word z1 = S->z1;
+ longword L_z2 = S->L_z2;
+ word mp = S->mp;
+ word s1;
+ word SO;
+ ulongword utmp; /* for L_ADD */
+ register int k = 160;
+
+ (void) utmp;
+
+ while (k--) {
+
+ /* 4.2.1 Downscaling of the input signal
+ */
+ /* SO = SASR( *s, 3 ) << 2;*/
+ SO = SASR( *s, 1 ) & ~3;
+ s++;
+
+ assert (SO >= -0x4000); /* downscaled by */
+ assert (SO <= 0x3FFC); /* previous routine. */
+
+
+ /* 4.2.2 Offset compensation
+ *
+ * This part implements a high-pass filter and requires extended
+ * arithmetic precision for the recursive part of this filter.
+ * The input of this procedure is the array so[0...159] and the
+ * output the array sof[ 0...159 ].
+ */
+ /* Compute the non-recursive part
+ */
+
+ s1 = SO - z1; /* s1 = gsm_sub( *so, z1 ); */
+ z1 = SO;
+
+ assert(s1 != MIN_WORD);
+
+ /* SJB Remark: float might be faster than the mess that follows */
+
+ /* Compute the recursive part
+ */
+
+ /* Execution of a 31 bv 16 bits multiplication
+ */
+ {
+ word msp;
+#ifndef __GNUC__
+ word lsp;
+#endif
+ longword L_s2;
+ longword L_temp;
+
+ L_s2 = s1;
+ L_s2 <<= 15;
+#ifndef __GNUC__
+ msp = (word)SASR( L_z2, 15 );
+ lsp = (word)(L_z2 & 0x7fff); /* gsm_L_sub(L_z2,(msp<<15)); */
+
+ L_s2 += GSM_MULT_R( lsp, 32735 );
+ L_temp = (longword)msp * 32735; /* GSM_L_MULT(msp,32735) >> 1;*/
+ L_z2 = GSM_L_ADD( L_temp, L_s2 );
+ /* above does L_z2 = L_z2 * 0x7fd5/0x8000 + L_s2 */
+#else
+ L_z2 = ((long long)L_z2*32735 + 0x4000)>>15;
+ /* alternate (ansi) version of above line does slightly different rounding:
+ * L_temp = L_z2 >> 9;
+ * L_temp += L_temp >> 5;
+ * L_temp = (++L_temp) >> 1;
+ * L_z2 = L_z2 - L_temp;
+ */
+ L_z2 = GSM_L_ADD(L_z2,L_s2);
+#endif
+ /* Compute sof[k] with rounding
+ */
+ L_temp = GSM_L_ADD( L_z2, 16384 );
+
+ /* 4.2.3 Preemphasis
+ */
+
+ msp = (word)GSM_MULT_R( mp, -28180 );
+ mp = (word)SASR( L_temp, 15 );
+ *so++ = GSM_ADD( mp, msp );
+ }
+ }
+
+ S->z1 = z1;
+ S->L_z2 = L_z2;
+ S->mp = mp;
+}
diff --git a/trunk/codecs/gsm/src/rpe.c b/trunk/codecs/gsm/src/rpe.c
new file mode 100644
index 000000000..1c354795d
--- /dev/null
+++ b/trunk/codecs/gsm/src/rpe.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+/* 4.2.13 .. 4.2.17 RPE ENCODING SECTION
+ */
+
+/* 4.2.13 */
+#ifdef K6OPT
+#include "k6opt.h"
+#else
+static void Weighting_filter P2((e, x),
+ register word * e, /* signal [-5..0.39.44] IN */
+ word * x /* signal [0..39] OUT */
+)
+/*
+ * The coefficients of the weighting filter are stored in a table
+ * (see table 4.4). The following scaling is used:
+ *
+ * H[0..10] = integer( real_H[ 0..10] * 8192 );
+ */
+{
+ /* word wt[ 50 ]; */
+
+ register longword L_result;
+ register int k /* , i */ ;
+
+ /* Initialization of a temporary working array wt[0...49]
+ */
+
+ /* for (k = 0; k <= 4; k++) wt[k] = 0;
+ * for (k = 5; k <= 44; k++) wt[k] = *e++;
+ * for (k = 45; k <= 49; k++) wt[k] = 0;
+ *
+ * (e[-5..-1] and e[40..44] are allocated by the caller,
+ * are initially zero and are not written anywhere.)
+ */
+ e -= 5;
+
+ /* Compute the signal x[0..39]
+ */
+ for (k = 0; k <= 39; k++) {
+
+ L_result = 8192 >> 1;
+
+ /* for (i = 0; i <= 10; i++) {
+ * L_temp = GSM_L_MULT( wt[k+i], gsm_H[i] );
+ * L_result = GSM_L_ADD( L_result, L_temp );
+ * }
+ */
+
+#undef STEP
+#define STEP( i, H ) (e[ k + i ] * (longword)H)
+
+ /* Every one of these multiplications is done twice --
+ * but I don't see an elegant way to optimize this.
+ * Do you?
+ */
+
+#ifdef STUPID_COMPILER
+ L_result += STEP( 0, -134 ) ;
+ L_result += STEP( 1, -374 ) ;
+ /* + STEP( 2, 0 ) */
+ L_result += STEP( 3, 2054 ) ;
+ L_result += STEP( 4, 5741 ) ;
+ L_result += STEP( 5, 8192 ) ;
+ L_result += STEP( 6, 5741 ) ;
+ L_result += STEP( 7, 2054 ) ;
+ /* + STEP( 8, 0 ) */
+ L_result += STEP( 9, -374 ) ;
+ L_result += STEP( 10, -134 ) ;
+#else
+ L_result +=
+ STEP( 0, -134 )
+ + STEP( 1, -374 )
+ /* + STEP( 2, 0 ) */
+ + STEP( 3, 2054 )
+ + STEP( 4, 5741 )
+ + STEP( 5, 8192 )
+ + STEP( 6, 5741 )
+ + STEP( 7, 2054 )
+ /* + STEP( 8, 0 ) */
+ + STEP( 9, -374 )
+ + STEP(10, -134 )
+ ;
+#endif
+
+ /* L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x2) *)
+ * L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x4) *)
+ *
+ * x[k] = SASR( L_result, 16 );
+ */
+
+ /* 2 adds vs. >>16 => 14, minus one shift to compensate for
+ * those we lost when replacing L_MULT by '*'.
+ */
+
+ L_result = SASR( L_result, 13 );
+ x[k] = (word)( L_result < MIN_WORD ? MIN_WORD
+ : (L_result > MAX_WORD ? MAX_WORD : L_result ));
+ }
+}
+#endif /* K6OPT */
+
+/* 4.2.14 */
+
+static void RPE_grid_selection P3((x,xM,Mc_out),
+ word * x, /* [0..39] IN */
+ word * xM, /* [0..12] OUT */
+ word * Mc_out /* OUT */
+)
+/*
+ * The signal x[0..39] is used to select the RPE grid which is
+ * represented by Mc.
+ */
+{
+ /* register word temp1; */
+ register int /* m, */ i;
+ register longword L_result, L_temp;
+ longword EM; /* xxx should be L_EM? */
+ word Mc;
+
+ longword L_common_0_3;
+
+ EM = 0;
+ Mc = 0;
+
+ /* for (m = 0; m <= 3; m++) {
+ * L_result = 0;
+ *
+ *
+ * for (i = 0; i <= 12; i++) {
+ *
+ * temp1 = SASR( x[m + 3*i], 2 );
+ *
+ * assert(temp1 != MIN_WORD);
+ *
+ * L_temp = GSM_L_MULT( temp1, temp1 );
+ * L_result = GSM_L_ADD( L_temp, L_result );
+ * }
+ *
+ * if (L_result > EM) {
+ * Mc = m;
+ * EM = L_result;
+ * }
+ * }
+ */
+
+#undef STEP
+#define STEP( m, i ) L_temp = SASR( x[m + 3 * i], 2 ); \
+ L_result += L_temp * L_temp;
+
+ /* common part of 0 and 3 */
+
+ L_result = 0;
+ STEP( 0, 1 ); STEP( 0, 2 ); STEP( 0, 3 ); STEP( 0, 4 );
+ STEP( 0, 5 ); STEP( 0, 6 ); STEP( 0, 7 ); STEP( 0, 8 );
+ STEP( 0, 9 ); STEP( 0, 10); STEP( 0, 11); STEP( 0, 12);
+ L_common_0_3 = L_result;
+
+ /* i = 0 */
+
+ STEP( 0, 0 );
+ L_result <<= 1; /* implicit in L_MULT */
+ EM = L_result;
+
+ /* i = 1 */
+
+ L_result = 0;
+ STEP( 1, 0 );
+ STEP( 1, 1 ); STEP( 1, 2 ); STEP( 1, 3 ); STEP( 1, 4 );
+ STEP( 1, 5 ); STEP( 1, 6 ); STEP( 1, 7 ); STEP( 1, 8 );
+ STEP( 1, 9 ); STEP( 1, 10); STEP( 1, 11); STEP( 1, 12);
+ L_result <<= 1;
+ if (L_result > EM) {
+ Mc = 1;
+ EM = L_result;
+ }
+
+ /* i = 2 */
+
+ L_result = 0;
+ STEP( 2, 0 );
+ STEP( 2, 1 ); STEP( 2, 2 ); STEP( 2, 3 ); STEP( 2, 4 );
+ STEP( 2, 5 ); STEP( 2, 6 ); STEP( 2, 7 ); STEP( 2, 8 );
+ STEP( 2, 9 ); STEP( 2, 10); STEP( 2, 11); STEP( 2, 12);
+ L_result <<= 1;
+ if (L_result > EM) {
+ Mc = 2;
+ EM = L_result;
+ }
+
+ /* i = 3 */
+
+ L_result = L_common_0_3;
+ STEP( 3, 12 );
+ L_result <<= 1;
+ if (L_result > EM) {
+ Mc = 3;
+ EM = L_result;
+ }
+
+ /**/
+
+ /* Down-sampling by a factor 3 to get the selected xM[0..12]
+ * RPE sequence.
+ */
+ for (i = 0; i <= 12; i ++) xM[i] = x[Mc + 3*i];
+ *Mc_out = Mc;
+}
+
+/* 4.12.15 */
+
+static void APCM_quantization_xmaxc_to_exp_mant P3((xmaxc,exp_out,mant_out),
+ word xmaxc, /* IN */
+ word * exp_out, /* OUT */
+ word * mant_out ) /* OUT */
+{
+ word exp, mant;
+
+ /* Compute exponent and mantissa of the decoded version of xmaxc
+ */
+
+ exp = 0;
+ if (xmaxc > 15) exp = SASR(xmaxc, 3) - 1;
+ mant = xmaxc - (exp << 3);
+
+ if (mant == 0) {
+ exp = -4;
+ mant = 7;
+ }
+ else {
+ while (mant <= 7) {
+ mant = mant << 1 | 1;
+ exp--;
+ }
+ mant -= 8;
+ }
+
+ assert( exp >= -4 && exp <= 6 );
+ assert( mant >= 0 && mant <= 7 );
+
+ *exp_out = exp;
+ *mant_out = mant;
+}
+
+static void APCM_quantization P5((xM,xMc,mant_out,exp_out,xmaxc_out),
+ word * xM, /* [0..12] IN */
+
+ word * xMc, /* [0..12] OUT */
+ word * mant_out, /* OUT */
+ word * exp_out, /* OUT */
+ word * xmaxc_out /* OUT */
+)
+{
+ int i, itest;
+
+ word xmax, xmaxc, temp, temp1, temp2;
+ word exp, mant;
+
+
+ /* Find the maximum absolute value xmax of xM[0..12].
+ */
+
+ xmax = 0;
+ for (i = 0; i <= 12; i++) {
+ temp = xM[i];
+ temp = GSM_ABS(temp);
+ if (temp > xmax) xmax = temp;
+ }
+
+ /* Qantizing and coding of xmax to get xmaxc.
+ */
+
+ exp = 0;
+ temp = SASR( xmax, 9 );
+ itest = 0;
+
+ for (i = 0; i <= 5; i++) {
+
+ itest |= (temp <= 0);
+ temp = SASR( temp, 1 );
+
+ assert(exp <= 5);
+ if (itest == 0) exp++; /* exp = add (exp, 1) */
+ }
+
+ assert(exp <= 6 && exp >= 0);
+ temp = exp + 5;
+
+ assert(temp <= 11 && temp >= 0);
+ xmaxc = gsm_add( SASR(xmax, temp), exp << 3 );
+
+ /* Quantizing and coding of the xM[0..12] RPE sequence
+ * to get the xMc[0..12]
+ */
+
+ APCM_quantization_xmaxc_to_exp_mant( xmaxc, &exp, &mant );
+
+ /* This computation uses the fact that the decoded version of xmaxc
+ * can be calculated by using the exponent and the mantissa part of
+ * xmaxc (logarithmic table).
+ * So, this method avoids any division and uses only a scaling
+ * of the RPE samples by a function of the exponent. A direct
+ * multiplication by the inverse of the mantissa (NRFAC[0..7]
+ * found in table 4.5) gives the 3 bit coded version xMc[0..12]
+ * of the RPE samples.
+ */
+
+
+ /* Direct computation of xMc[0..12] using table 4.5
+ */
+
+ assert( exp <= 4096 && exp >= -4096);
+ assert( mant >= 0 && mant <= 7 );
+
+ temp1 = 6 - exp; /* normalization by the exponent */
+ temp2 = gsm_NRFAC[ mant ]; /* inverse mantissa */
+
+ for (i = 0; i <= 12; i++) {
+
+ assert(temp1 >= 0 && temp1 < 16);
+
+ temp = xM[i] << temp1;
+ temp = (word)GSM_MULT( temp, temp2 );
+ temp = SASR(temp, 12);
+ xMc[i] = temp + 4; /* see note below */
+ }
+
+ /* NOTE: This equation is used to make all the xMc[i] positive.
+ */
+
+ *mant_out = mant;
+ *exp_out = exp;
+ *xmaxc_out = xmaxc;
+}
+
+/* 4.2.16 */
+
+static void APCM_inverse_quantization P4((xMc,mant,exp,xMp),
+ register word * xMc, /* [0..12] IN */
+ word mant,
+ word exp,
+ register word * xMp) /* [0..12] OUT */
+/*
+ * This part is for decoding the RPE sequence of coded xMc[0..12]
+ * samples to obtain the xMp[0..12] array. Table 4.6 is used to get
+ * the mantissa of xmaxc (FAC[0..7]).
+ */
+{
+ int i;
+ word temp, temp1, temp2, temp3;
+
+ assert( mant >= 0 && mant <= 7 );
+
+ temp1 = gsm_FAC[ mant ]; /* see 4.2-15 for mant */
+ temp2 = gsm_sub( 6, exp ); /* see 4.2-15 for exp */
+ temp3 = gsm_asl( 1, gsm_sub( temp2, 1 ));
+
+ for (i = 13; i--;) {
+
+ assert( *xMc <= 7 && *xMc >= 0 ); /* 3 bit unsigned */
+
+ /* temp = gsm_sub( *xMc++ << 1, 7 ); */
+ temp = (*xMc++ << 1) - 7; /* restore sign */
+ assert( temp <= 7 && temp >= -7 ); /* 4 bit signed */
+
+ temp <<= 12; /* 16 bit signed */
+ temp = (word)GSM_MULT_R( temp1, temp );
+ temp = GSM_ADD( temp, temp3 );
+ *xMp++ = gsm_asr( temp, temp2 );
+ }
+}
+
+/* 4.2.17 */
+
+static void RPE_grid_positioning P3((Mc,xMp,ep),
+ word Mc, /* grid position IN */
+ register word * xMp, /* [0..12] IN */
+ register word * ep /* [0..39] OUT */
+)
+/*
+ * This procedure computes the reconstructed long term residual signal
+ * ep[0..39] for the LTP analysis filter. The inputs are the Mc
+ * which is the grid position selection and the xMp[0..12] decoded
+ * RPE samples which are upsampled by a factor of 3 by inserting zero
+ * values.
+ */
+{
+ int i = 13;
+
+ assert(0 <= Mc && Mc <= 3);
+
+ switch (Mc) {
+ case 3: *ep++ = 0;
+ case 2: do {
+ *ep++ = 0;
+ case 1: *ep++ = 0;
+ case 0: *ep++ = *xMp++;
+ } while (--i);
+ }
+ while (++Mc < 4) *ep++ = 0;
+
+ /*
+
+ int i, k;
+ for (k = 0; k <= 39; k++) ep[k] = 0;
+ for (i = 0; i <= 12; i++) {
+ ep[ Mc + (3*i) ] = xMp[i];
+ }
+ */
+}
+
+/* 4.2.18 */
+
+/* This procedure adds the reconstructed long term residual signal
+ * ep[0..39] to the estimated signal dpp[0..39] from the long term
+ * analysis filter to compute the reconstructed short term residual
+ * signal dp[-40..-1]; also the reconstructed short term residual
+ * array dp[-120..-41] is updated.
+ */
+
+#if 0 /* Has been inlined in code.c */
+void Gsm_Update_of_reconstructed_short_time_residual_signal P3((dpp, ep, dp),
+ word * dpp, /* [0...39] IN */
+ word * ep, /* [0...39] IN */
+ word * dp) /* [-120...-1] IN/OUT */
+{
+ int k;
+
+ for (k = 0; k <= 79; k++)
+ dp[ -120 + k ] = dp[ -80 + k ];
+
+ for (k = 0; k <= 39; k++)
+ dp[ -40 + k ] = gsm_add( ep[k], dpp[k] );
+}
+#endif /* Has been inlined in code.c */
+
+void Gsm_RPE_Encoding P5((S,e,xmaxc,Mc,xMc),
+
+ struct gsm_state * S,
+
+ word * e, /* -5..-1][0..39][40..44 IN/OUT */
+ word * xmaxc, /* OUT */
+ word * Mc, /* OUT */
+ word * xMc) /* [0..12] OUT */
+{
+ word x[40];
+ word xM[13], xMp[13];
+ word mant, exp;
+
+ Weighting_filter(e, x);
+ RPE_grid_selection(x, xM, Mc);
+
+ APCM_quantization( xM, xMc, &mant, &exp, xmaxc);
+ APCM_inverse_quantization( xMc, mant, exp, xMp);
+
+ RPE_grid_positioning( *Mc, xMp, e );
+
+}
+
+void Gsm_RPE_Decoding P5((S, xmaxcr, Mcr, xMcr, erp),
+ struct gsm_state * S,
+
+ word xmaxcr,
+ word Mcr,
+ word * xMcr, /* [0..12], 3 bits IN */
+ word * erp /* [0..39] OUT */
+)
+{
+ word exp, mant;
+ word xMp[ 13 ];
+
+ APCM_quantization_xmaxc_to_exp_mant( xmaxcr, &exp, &mant );
+ APCM_inverse_quantization( xMcr, mant, exp, xMp );
+ RPE_grid_positioning( Mcr, xMp, erp );
+
+}
diff --git a/trunk/codecs/gsm/src/short_term.c b/trunk/codecs/gsm/src/short_term.c
new file mode 100644
index 000000000..43c592c04
--- /dev/null
+++ b/trunk/codecs/gsm/src/short_term.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+#ifdef K6OPT
+#include "k6opt.h"
+
+#define Short_term_analysis_filtering Short_term_analysis_filteringx
+
+#endif
+/*
+ * SHORT TERM ANALYSIS FILTERING SECTION
+ */
+
+/* 4.2.8 */
+
+static void Decoding_of_the_coded_Log_Area_Ratios P2((LARc,LARpp),
+ word * LARc, /* coded log area ratio [0..7] IN */
+ word * LARpp) /* out: decoded .. */
+{
+ register word temp1 /* , temp2 */;
+
+ /* This procedure requires for efficient implementation
+ * two tables.
+ *
+ * INVA[1..8] = integer( (32768 * 8) / real_A[1..8])
+ * MIC[1..8] = minimum value of the LARc[1..8]
+ */
+
+ /* Compute the LARpp[1..8]
+ */
+
+ /* for (i = 1; i <= 8; i++, B++, MIC++, INVA++, LARc++, LARpp++) {
+ *
+ * temp1 = GSM_ADD( *LARc, *MIC ) << 10;
+ * temp2 = *B << 1;
+ * temp1 = GSM_SUB( temp1, temp2 );
+ *
+ * assert(*INVA != MIN_WORD);
+ *
+ * temp1 = GSM_MULT_R( *INVA, temp1 );
+ * *LARpp = GSM_ADD( temp1, temp1 );
+ * }
+ */
+
+#undef STEP
+#define STEP( B, MIC, INVA ) \
+ temp1 = GSM_ADD( *LARc++, MIC ) << 10; \
+ temp1 = GSM_SUB( temp1, B << 1 ); \
+ temp1 = (word)GSM_MULT_R( INVA, temp1 ); \
+ *LARpp++ = GSM_ADD( temp1, temp1 );
+
+ STEP( 0, -32, 13107 );
+ STEP( 0, -32, 13107 );
+ STEP( 2048, -16, 13107 );
+ STEP( -2560, -16, 13107 );
+
+ STEP( 94, -8, 19223 );
+ STEP( -1792, -8, 17476 );
+ STEP( -341, -4, 31454 );
+ STEP( -1144, -4, 29708 );
+
+ /* NOTE: the addition of *MIC is used to restore
+ * the sign of *LARc.
+ */
+}
+
+/* 4.2.9 */
+/* Computation of the quantized reflection coefficients
+ */
+
+/* 4.2.9.1 Interpolation of the LARpp[1..8] to get the LARp[1..8]
+ */
+
+/*
+ * Within each frame of 160 analyzed speech samples the short term
+ * analysis and synthesis filters operate with four different sets of
+ * coefficients, derived from the previous set of decoded LARs(LARpp(j-1))
+ * and the actual set of decoded LARs (LARpp(j))
+ *
+ * (Initial value: LARpp(j-1)[1..8] = 0.)
+ */
+
+static void Coefficients_0_12 P3((LARpp_j_1, LARpp_j, LARp),
+ register word * LARpp_j_1,
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+
+ for (i = 1; i <= 8; i++, LARp++, LARpp_j_1++, LARpp_j++) {
+ *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 ));
+ *LARp = GSM_ADD( *LARp, SASR( *LARpp_j_1, 1));
+ }
+}
+
+static void Coefficients_13_26 P3((LARpp_j_1, LARpp_j, LARp),
+ register word * LARpp_j_1,
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+ for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) {
+ *LARp = GSM_ADD( SASR( *LARpp_j_1, 1), SASR( *LARpp_j, 1 ));
+ }
+}
+
+static void Coefficients_27_39 P3((LARpp_j_1, LARpp_j, LARp),
+ register word * LARpp_j_1,
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+
+ for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) {
+ *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 ));
+ *LARp = GSM_ADD( *LARp, SASR( *LARpp_j, 1 ));
+ }
+}
+
+
+static void Coefficients_40_159 P2((LARpp_j, LARp),
+ register word * LARpp_j,
+ register word * LARp)
+{
+ register int i;
+
+ for (i = 1; i <= 8; i++, LARp++, LARpp_j++)
+ *LARp = *LARpp_j;
+}
+
+/* 4.2.9.2 */
+
+static void LARp_to_rp P1((LARp),
+ register word * LARp) /* [0..7] IN/OUT */
+/*
+ * The input of this procedure is the interpolated LARp[0..7] array.
+ * The reflection coefficients, rp[i], are used in the analysis
+ * filter and in the synthesis filter.
+ */
+{
+ register int i;
+ register word temp;
+
+ for (i = 1; i <= 8; i++, LARp++) {
+
+ /* temp = GSM_ABS( *LARp );
+ *
+ * if (temp < 11059) temp <<= 1;
+ * else if (temp < 20070) temp += 11059;
+ * else temp = GSM_ADD( temp >> 2, 26112 );
+ *
+ * *LARp = *LARp < 0 ? -temp : temp;
+ */
+
+ if (*LARp < 0) {
+ temp = *LARp == MIN_WORD ? MAX_WORD : -(*LARp);
+ *LARp = - ((temp < 11059) ? temp << 1
+ : ((temp < 20070) ? temp + 11059
+ : GSM_ADD( temp >> 2, 26112 )));
+ } else {
+ temp = *LARp;
+ *LARp = (temp < 11059) ? temp << 1
+ : ((temp < 20070) ? temp + 11059
+ : GSM_ADD( temp >> 2, 26112 ));
+ }
+ }
+}
+
+
+/* 4.2.10 */
+#ifndef Short_term_analysis_filtering
+
+/* SJB Remark:
+ * I tried 2 MMX versions of this function, neither is significantly
+ * faster than the C version which follows. MMX might be useful if
+ * one were processing 2 input streams in parallel.
+ */
+static void Short_term_analysis_filtering P4((u0,rp0,k_n,s),
+ register word * u0,
+ register word * rp0, /* [0..7] IN */
+ register int k_n, /* k_end - k_start */
+ register word * s /* [0..n-1] IN/OUT */
+)
+/*
+ * This procedure computes the short term residual signal d[..] to be fed
+ * to the RPE-LTP loop from the s[..] signal and from the local rp[..]
+ * array (quantized reflection coefficients). As the call of this
+ * procedure can be done in many ways (see the interpolation of the LAR
+ * coefficient), it is assumed that the computation begins with index
+ * k_start (for arrays d[..] and s[..]) and stops with index k_end
+ * (k_start and k_end are defined in 4.2.9.1). This procedure also
+ * needs to keep the array u0[0..7] in memory for each call.
+ */
+{
+ register word * u_top = u0 + 8;
+ register word * s_top = s + k_n;
+
+ while (s < s_top) {
+ register word *u, *rp ;
+ register longword di, u_out;
+ di = u_out = *s;
+ for (rp=rp0, u=u0; u<u_top;) {
+ register longword ui, rpi;
+ ui = *u;
+ *u++ = (word)u_out;
+ rpi = *rp++;
+ u_out = ui + (((rpi*di)+0x4000)>>15);
+ di = di + (((rpi*ui)+0x4000)>>15);
+ /* make the common case fastest: */
+ if ((u_out == (word)u_out) && (di == (word)di)) continue;
+ /* otherwise do slower fixup (saturation) */
+ if (u_out>MAX_WORD) u_out=MAX_WORD;
+ else if (u_out<MIN_WORD) u_out=MIN_WORD;
+ if (di>MAX_WORD) di=MAX_WORD;
+ else if (di<MIN_WORD) di=MIN_WORD;
+ }
+ *s++ = (word)di;
+ }
+}
+#endif
+
+#if defined(USE_FLOAT_MUL) && defined(FAST)
+
+static void Fast_Short_term_analysis_filtering P4((u,rp,k_n,s),
+ register word * u;
+ register word * rp, /* [0..7] IN */
+ register int k_n, /* k_end - k_start */
+ register word * s /* [0..n-1] IN/OUT */
+)
+{
+ register int i;
+
+ float uf[8],
+ rpf[8];
+
+ register float scalef = 3.0517578125e-5;
+ register float sav, di, temp;
+
+ for (i = 0; i < 8; ++i) {
+ uf[i] = u[i];
+ rpf[i] = rp[i] * scalef;
+ }
+ for (; k_n--; s++) {
+ sav = di = *s;
+ for (i = 0; i < 8; ++i) {
+ register float rpfi = rpf[i];
+ register float ufi = uf[i];
+
+ uf[i] = sav;
+ temp = rpfi * di + ufi;
+ di += rpfi * ufi;
+ sav = temp;
+ }
+ *s = di;
+ }
+ for (i = 0; i < 8; ++i) u[i] = uf[i];
+}
+#endif /* ! (defined (USE_FLOAT_MUL) && defined (FAST)) */
+
+/*
+ * SJB Remark: modified Short_term_synthesis_filtering() below
+ * for significant (abt 35%) speedup of decompression.
+ * (gcc-2.95, k6 cpu)
+ * Please don't change this without benchmarking decompression
+ * to see that you haven't harmed speed.
+ * This function burns most of CPU time for untoasting.
+ * Unfortunately, didn't see any good way to benefit from mmx.
+ */
+static void Short_term_synthesis_filtering P5((S,rrp,k,wt,sr),
+ struct gsm_state * S,
+ register word * rrp, /* [0..7] IN */
+ register int k, /* k_end - k_start */
+ register word * wt, /* [0..k-1] IN */
+ register word * sr /* [0..k-1] OUT */
+)
+{
+ register word * v = S->v;
+ register int i;
+ register longword sri;
+
+ while (k--) {
+ sri = *wt++;
+ for (i = 8; i--;) {
+ register longword tmp1, tmp2;
+
+ /* sri = GSM_SUB( sri, gsm_mult_r( rrp[i], v[i] ) );
+ */
+ tmp1 = rrp[i];
+ tmp2 = v[i];
+
+ tmp2 = (( tmp1 * tmp2 + 16384) >> 15) ;
+ /* saturation done below */
+ sri -= tmp2;
+ if (sri != (word)sri) {
+ sri = (sri<0)? MIN_WORD:MAX_WORD;
+ }
+ /* v[i+1] = GSM_ADD( v[i], gsm_mult_r( rrp[i], sri ) );
+ */
+
+ tmp1 = (( tmp1 * sri + 16384) >> 15) ;
+ /* saturation done below */
+ tmp1 += v[i];
+ if (tmp1 != (word)tmp1) {
+ tmp1 = (tmp1<0)? MIN_WORD:MAX_WORD;
+ }
+ v[i+1] = (word)tmp1;
+ }
+ *sr++ = v[0] = (word)sri;
+ }
+}
+
+
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+
+static void Fast_Short_term_synthesis_filtering P5((S,rrp,k,wt,sr),
+ struct gsm_state * S,
+ register word * rrp, /* [0..7] IN */
+ register int k, /* k_end - k_start */
+ register word * wt, /* [0..k-1] IN */
+ register word * sr /* [0..k-1] OUT */
+)
+{
+ register word * v = S->v;
+ register int i;
+
+ float va[9], rrpa[8];
+ register float scalef = 3.0517578125e-5, temp;
+
+ for (i = 0; i < 8; ++i) {
+ va[i] = v[i];
+ rrpa[i] = (float)rrp[i] * scalef;
+ }
+ while (k--) {
+ register float sri = *wt++;
+ for (i = 8; i--;) {
+ sri -= rrpa[i] * va[i];
+ if (sri < -32768.) sri = -32768.;
+ else if (sri > 32767.) sri = 32767.;
+
+ temp = va[i] + rrpa[i] * sri;
+ if (temp < -32768.) temp = -32768.;
+ else if (temp > 32767.) temp = 32767.;
+ va[i+1] = temp;
+ }
+ *sr++ = va[0] = sri;
+ }
+ for (i = 0; i < 9; ++i) v[i] = va[i];
+}
+
+#endif /* defined(FAST) && defined(USE_FLOAT_MUL) */
+
+void Gsm_Short_Term_Analysis_Filter P3((S,LARc,s),
+
+ struct gsm_state * S,
+
+ word * LARc, /* coded log area ratio [0..7] IN */
+ word * s /* signal [0..159] IN/OUT */
+)
+{
+ word * LARpp_j = S->LARpp[ S->j ];
+ word * LARpp_j_1 = S->LARpp[ S->j ^= 1 ];
+
+ word LARp[8];
+
+#undef FILTER
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+# define FILTER (* (S->fast \
+ ? Fast_Short_term_analysis_filtering \
+ : Short_term_analysis_filtering ))
+
+#else
+# define FILTER Short_term_analysis_filtering
+#endif
+
+ Decoding_of_the_coded_Log_Area_Ratios( LARc, LARpp_j );
+
+ Coefficients_0_12( LARpp_j_1, LARpp_j, LARp );
+ LARp_to_rp( LARp );
+ FILTER( S->u, LARp, 13, s);
+
+ Coefficients_13_26( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S->u, LARp, 14, s + 13);
+
+ Coefficients_27_39( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S->u, LARp, 13, s + 27);
+
+ Coefficients_40_159( LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S->u, LARp, 120, s + 40);
+
+}
+
+void Gsm_Short_Term_Synthesis_Filter P4((S, LARcr, wt, s),
+ struct gsm_state * S,
+
+ word * LARcr, /* received log area ratios [0..7] IN */
+ word * wt, /* received d [0..159] IN */
+
+ word * s /* signal s [0..159] OUT */
+)
+{
+ word * LARpp_j = S->LARpp[ S->j ];
+ word * LARpp_j_1 = S->LARpp[ S->j ^=1 ];
+
+ word LARp[8];
+
+#undef FILTER
+#if defined(FAST) && defined(USE_FLOAT_MUL)
+
+# define FILTER (* (S->fast \
+ ? Fast_Short_term_synthesis_filtering \
+ : Short_term_synthesis_filtering ))
+#else
+# define FILTER Short_term_synthesis_filtering
+#endif
+
+ Decoding_of_the_coded_Log_Area_Ratios( LARcr, LARpp_j );
+
+ Coefficients_0_12( LARpp_j_1, LARpp_j, LARp );
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 13, wt, s );
+
+ Coefficients_13_26( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 14, wt + 13, s + 13 );
+
+ Coefficients_27_39( LARpp_j_1, LARpp_j, LARp);
+ LARp_to_rp( LARp );
+ FILTER( S, LARp, 13, wt + 27, s + 27 );
+
+ Coefficients_40_159( LARpp_j, LARp );
+ LARp_to_rp( LARp );
+ FILTER(S, LARp, 120, wt + 40, s + 40);
+}
diff --git a/trunk/codecs/gsm/src/table.c b/trunk/codecs/gsm/src/table.c
new file mode 100644
index 000000000..16a04118c
--- /dev/null
+++ b/trunk/codecs/gsm/src/table.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header$ */
+
+/* Most of these tables are inlined at their point of use.
+ */
+
+/* 4.4 TABLES USED IN THE FIXED POINT IMPLEMENTATION OF THE RPE-LTP
+ * CODER AND DECODER
+ *
+ * (Most of them inlined, so watch out.)
+ */
+
+#define GSM_TABLE_C
+#include "private.h"
+#include "gsm.h"
+
+/* Table 4.1 Quantization of the Log.-Area Ratios
+ */
+/* i 1 2 3 4 5 6 7 8 */
+word gsm_A[8] = {20480, 20480, 20480, 20480, 13964, 15360, 8534, 9036};
+word gsm_B[8] = { 0, 0, 2048, -2560, 94, -1792, -341, -1144};
+word gsm_MIC[8] = { -32, -32, -16, -16, -8, -8, -4, -4 };
+word gsm_MAC[8] = { 31, 31, 15, 15, 7, 7, 3, 3 };
+
+
+/* Table 4.2 Tabulation of 1/A[1..8]
+ */
+word gsm_INVA[8]={ 13107, 13107, 13107, 13107, 19223, 17476, 31454, 29708 };
+
+
+/* Table 4.3a Decision level of the LTP gain quantizer
+ */
+/* bc 0 1 2 3 */
+word gsm_DLB[4] = { 6554, 16384, 26214, 32767 };
+
+
+/* Table 4.3b Quantization levels of the LTP gain quantizer
+ */
+/* bc 0 1 2 3 */
+word gsm_QLB[4] = { 3277, 11469, 21299, 32767 };
+
+
+/* Table 4.4 Coefficients of the weighting filter
+ */
+/* i 0 1 2 3 4 5 6 7 8 9 10 */
+word gsm_H[11] = {-134, -374, 0, 2054, 5741, 8192, 5741, 2054, 0, -374, -134 };
+
+
+/* Table 4.5 Normalized inverse mantissa used to compute xM/xmax
+ */
+/* i 0 1 2 3 4 5 6 7 */
+word gsm_NRFAC[8] = { 29128, 26215, 23832, 21846, 20165, 18725, 17476, 16384 };
+
+
+/* Table 4.6 Normalized direct mantissa used to compute xM/xmax
+ */
+/* i 0 1 2 3 4 5 6 7 */
+word gsm_FAC[8] = { 18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767 };
diff --git a/trunk/codecs/gsm_slin_ex.h b/trunk/codecs/gsm_slin_ex.h
new file mode 100644
index 000000000..2f001abec
--- /dev/null
+++ b/trunk/codecs/gsm_slin_ex.h
@@ -0,0 +1,16 @@
+/*! \file
+ * \brief 8-bit raw data
+ *
+ * Source: gsm.example
+ *
+ * Copyright (C) 1999-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char gsm_slin_ex[] = {
+0xda, 0xa6, 0xac, 0x2d, 0xa3, 0x50, 000, 0x49, 0x24, 0x92,
+0x49, 0x24, 0x50, 0x40, 0x49, 0x24, 0x92, 0x37, 0x24, 0x52,
+000, 0x49, 0x24, 0x92, 0x47, 0x24, 0x50, 0x80, 0x46, 0xe3,
+0x6d, 0xb8, 0xdc };
diff --git a/trunk/codecs/ilbc/FrameClassify.c b/trunk/codecs/ilbc/FrameClassify.c
new file mode 100644
index 000000000..c982fd667
--- /dev/null
+++ b/trunk/codecs/ilbc/FrameClassify.c
@@ -0,0 +1,110 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ FrameClassify.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include "iLBC_define.h"
+#include "FrameClassify.h"
+
+/*---------------------------------------------------------------*
+ * Classification of subframes to localize start state
+ *--------------------------------------------------------------*/
+
+int FrameClassify( /* index to the max-energy sub-frame */
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i/o) the encoder state structure */
+ float *residual /* (i) lpc residual signal */
+) {
+ float max_ssqEn, fssqEn[NSUB_MAX], bssqEn[NSUB_MAX], *pp;
+ int n, l, max_ssqEn_n;
+ const float ssqEn_win[NSUB_MAX-1]={(float)0.8,(float)0.9,
+ (float)1.0,(float)0.9,(float)0.8};
+ const float sampEn_win[5]={(float)1.0/(float)6.0,
+ (float)2.0/(float)6.0, (float)3.0/(float)6.0,
+ (float)4.0/(float)6.0, (float)5.0/(float)6.0};
+
+ /* init the front and back energies to zero */
+
+ memset(fssqEn, 0, NSUB_MAX*sizeof(float));
+ memset(bssqEn, 0, NSUB_MAX*sizeof(float));
+
+ /* Calculate front of first seqence */
+
+ n=0;
+ pp=residual;
+
+
+ for (l=0; l<5; l++) {
+ fssqEn[n] += sampEn_win[l] * (*pp) * (*pp);
+ pp++;
+ }
+ for (l=5; l<SUBL; l++) {
+ fssqEn[n] += (*pp) * (*pp);
+ pp++;
+ }
+
+ /* Calculate front and back of all middle sequences */
+
+ for (n=1; n<iLBCenc_inst->nsub-1; n++) {
+ pp=residual+n*SUBL;
+ for (l=0; l<5; l++) {
+ fssqEn[n] += sampEn_win[l] * (*pp) * (*pp);
+ bssqEn[n] += (*pp) * (*pp);
+ pp++;
+ }
+ for (l=5; l<SUBL-5; l++) {
+ fssqEn[n] += (*pp) * (*pp);
+ bssqEn[n] += (*pp) * (*pp);
+ pp++;
+ }
+ for (l=SUBL-5; l<SUBL; l++) {
+ fssqEn[n] += (*pp) * (*pp);
+ bssqEn[n] += sampEn_win[SUBL-l-1] * (*pp) * (*pp);
+ pp++;
+ }
+ }
+
+ /* Calculate back of last seqence */
+
+ n=iLBCenc_inst->nsub-1;
+ pp=residual+n*SUBL;
+ for (l=0; l<SUBL-5; l++) {
+ bssqEn[n] += (*pp) * (*pp);
+ pp++;
+ }
+ for (l=SUBL-5; l<SUBL; l++) {
+ bssqEn[n] += sampEn_win[SUBL-l-1] * (*pp) * (*pp);
+ pp++;
+ }
+
+ /* find the index to the weighted 80 sample with
+ most energy */
+
+ if (iLBCenc_inst->mode==20) l=1;
+ else l=0;
+
+ max_ssqEn=(fssqEn[0]+bssqEn[1])*ssqEn_win[l];
+ max_ssqEn_n=1;
+ for (n=2; n<iLBCenc_inst->nsub; n++) {
+
+ l++;
+
+
+ if ((fssqEn[n-1]+bssqEn[n])*ssqEn_win[l] > max_ssqEn) {
+ max_ssqEn=(fssqEn[n-1]+bssqEn[n]) *
+ ssqEn_win[l];
+ max_ssqEn_n=n;
+ }
+ }
+
+ return max_ssqEn_n;
+}
+
+
diff --git a/trunk/codecs/ilbc/FrameClassify.h b/trunk/codecs/ilbc/FrameClassify.h
new file mode 100644
index 000000000..35cbd325b
--- /dev/null
+++ b/trunk/codecs/ilbc/FrameClassify.h
@@ -0,0 +1,26 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ FrameClassify.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+
+
+#ifndef __iLBC_FRAMECLASSIFY_H
+#define __iLBC_FRAMECLASSIFY_H
+
+int FrameClassify( /* index to the max-energy sub-frame */
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i/o) the encoder state structure */
+ float *residual /* (i) lpc residual signal */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/LPCdecode.c b/trunk/codecs/ilbc/LPCdecode.c
new file mode 100644
index 000000000..f4bc9896d
--- /dev/null
+++ b/trunk/codecs/ilbc/LPCdecode.c
@@ -0,0 +1,152 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ LPC_decode.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <string.h>
+
+#include "helpfun.h"
+#include "lsf.h"
+#include "iLBC_define.h"
+#include "LPCdecode.h"
+#include "constants.h"
+
+/*---------------------------------------------------------------*
+ * interpolation of lsf coefficients for the decoder
+ *--------------------------------------------------------------*/
+
+void LSFinterpolate2a_dec(
+ float *a, /* (o) lpc coefficients for a sub-frame */
+ float *lsf1, /* (i) first lsf coefficient vector */
+
+
+ float *lsf2, /* (i) second lsf coefficient vector */
+ float coef, /* (i) interpolation weight */
+ int length /* (i) length of lsf vectors */
+){
+ float lsftmp[LPC_FILTERORDER];
+
+ interpolate(lsftmp, lsf1, lsf2, coef, length);
+ lsf2a(a, lsftmp);
+}
+
+/*---------------------------------------------------------------*
+ * obtain dequantized lsf coefficients from quantization index
+ *--------------------------------------------------------------*/
+
+void SimplelsfDEQ(
+ float *lsfdeq, /* (o) dequantized lsf coefficients */
+ int *index, /* (i) quantization index */
+ int lpc_n /* (i) number of LPCs */
+){
+ int i, j, pos, cb_pos;
+
+ /* decode first LSF */
+
+ pos = 0;
+ cb_pos = 0;
+ for (i = 0; i < LSF_NSPLIT; i++) {
+ for (j = 0; j < dim_lsfCbTbl[i]; j++) {
+ lsfdeq[pos + j] = lsfCbTbl[cb_pos +
+ (long)(index[i])*dim_lsfCbTbl[i] + j];
+ }
+ pos += dim_lsfCbTbl[i];
+ cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i];
+ }
+
+ if (lpc_n>1) {
+
+ /* decode last LSF */
+
+ pos = 0;
+ cb_pos = 0;
+ for (i = 0; i < LSF_NSPLIT; i++) {
+ for (j = 0; j < dim_lsfCbTbl[i]; j++) {
+ lsfdeq[LPC_FILTERORDER + pos + j] =
+ lsfCbTbl[cb_pos +
+ (long)(index[LSF_NSPLIT + i])*
+ dim_lsfCbTbl[i] + j];
+ }
+ pos += dim_lsfCbTbl[i];
+ cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i];
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+
+
+ * obtain synthesis and weighting filters form lsf coefficients
+ *---------------------------------------------------------------*/
+
+void DecoderInterpolateLSF(
+ float *syntdenum, /* (o) synthesis filter coefficients */
+ float *weightdenum, /* (o) weighting denumerator
+ coefficients */
+ float *lsfdeq, /* (i) dequantized lsf coefficients */
+ int length, /* (i) length of lsf coefficient vector */
+ iLBC_Dec_Inst_t *iLBCdec_inst
+ /* (i) the decoder state structure */
+){
+ int i, pos, lp_length;
+ float lp[LPC_FILTERORDER + 1], *lsfdeq2;
+
+ lsfdeq2 = lsfdeq + length;
+ lp_length = length + 1;
+
+ if (iLBCdec_inst->mode==30) {
+ /* sub-frame 1: Interpolation between old and first */
+
+ LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold, lsfdeq,
+ lsf_weightTbl_30ms[0], length);
+ memcpy(syntdenum,lp,lp_length*sizeof(float));
+ bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM,
+ lp_length);
+
+ /* sub-frames 2 to 6: interpolation between first
+ and last LSF */
+
+ pos = lp_length;
+ for (i = 1; i < 6; i++) {
+ LSFinterpolate2a_dec(lp, lsfdeq, lsfdeq2,
+ lsf_weightTbl_30ms[i], length);
+ memcpy(syntdenum + pos,lp,lp_length*sizeof(float));
+ bwexpand(weightdenum + pos, lp,
+ LPC_CHIRP_WEIGHTDENUM, lp_length);
+ pos += lp_length;
+ }
+ }
+ else {
+ pos = 0;
+ for (i = 0; i < iLBCdec_inst->nsub; i++) {
+ LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold,
+ lsfdeq, lsf_weightTbl_20ms[i], length);
+ memcpy(syntdenum+pos,lp,lp_length*sizeof(float));
+ bwexpand(weightdenum+pos, lp, LPC_CHIRP_WEIGHTDENUM,
+ lp_length);
+ pos += lp_length;
+ }
+ }
+
+ /* update memory */
+
+
+
+ if (iLBCdec_inst->mode==30)
+ memcpy(iLBCdec_inst->lsfdeqold, lsfdeq2,
+ length*sizeof(float));
+ else
+ memcpy(iLBCdec_inst->lsfdeqold, lsfdeq,
+ length*sizeof(float));
+
+}
+
+
diff --git a/trunk/codecs/ilbc/LPCdecode.h b/trunk/codecs/ilbc/LPCdecode.h
new file mode 100644
index 000000000..1f55a622e
--- /dev/null
+++ b/trunk/codecs/ilbc/LPCdecode.h
@@ -0,0 +1,44 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ LPC_decode.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_LPC_DECODE_H
+#define __iLBC_LPC_DECODE_H
+
+void LSFinterpolate2a_dec(
+
+
+ float *a, /* (o) lpc coefficients for a sub-frame */
+ float *lsf1, /* (i) first lsf coefficient vector */
+ float *lsf2, /* (i) second lsf coefficient vector */
+ float coef, /* (i) interpolation weight */
+ int length /* (i) length of lsf vectors */
+);
+
+void SimplelsfDEQ(
+ float *lsfdeq, /* (o) dequantized lsf coefficients */
+ int *index, /* (i) quantization index */
+ int lpc_n /* (i) number of LPCs */
+);
+
+void DecoderInterpolateLSF(
+ float *syntdenum, /* (o) synthesis filter coefficients */
+ float *weightdenum, /* (o) weighting denumerator
+ coefficients */
+ float *lsfdeq, /* (i) dequantized lsf coefficients */
+ int length, /* (i) length of lsf coefficient vector */
+ iLBC_Dec_Inst_t *iLBCdec_inst
+ /* (i) the decoder state structure */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/LPCencode.c b/trunk/codecs/ilbc/LPCencode.c
new file mode 100644
index 000000000..4d5fcff21
--- /dev/null
+++ b/trunk/codecs/ilbc/LPCencode.c
@@ -0,0 +1,228 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ LPCencode.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <string.h>
+
+
+
+#include "iLBC_define.h"
+#include "LPCencode.h"
+#include "helpfun.h"
+#include "lsf.h"
+#include "constants.h"
+
+/*----------------------------------------------------------------*
+ * lpc analysis (subrutine to LPCencode)
+ *---------------------------------------------------------------*/
+
+static void SimpleAnalysis(
+ float *lsf, /* (o) lsf coefficients */
+ float *data, /* (i) new data vector */
+ iLBC_Enc_Inst_t *iLBCenc_inst
+ /* (i/o) the encoder state structure */
+){
+ int k, is;
+ float temp[BLOCKL_MAX], lp[LPC_FILTERORDER + 1];
+ float lp2[LPC_FILTERORDER + 1];
+ float r[LPC_FILTERORDER + 1];
+
+ is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl;
+ memcpy(iLBCenc_inst->lpc_buffer+is,data,
+ iLBCenc_inst->blockl*sizeof(float));
+
+ /* No lookahead, last window is asymmetric */
+
+ for (k = 0; k < iLBCenc_inst->lpc_n; k++) {
+
+ is = LPC_LOOKBACK;
+
+ if (k < (iLBCenc_inst->lpc_n - 1)) {
+ window(temp, lpc_winTbl,
+ iLBCenc_inst->lpc_buffer, BLOCKL_MAX);
+ } else {
+ window(temp, lpc_asymwinTbl,
+ iLBCenc_inst->lpc_buffer + is, BLOCKL_MAX);
+ }
+
+ autocorr(r, temp, BLOCKL_MAX, LPC_FILTERORDER);
+ window(r, r, lpc_lagwinTbl, LPC_FILTERORDER + 1);
+
+ levdurb(lp, temp, r, LPC_FILTERORDER);
+ bwexpand(lp2, lp, LPC_CHIRP_SYNTDENUM, LPC_FILTERORDER+1);
+
+ a2lsf(lsf + k*LPC_FILTERORDER, lp2);
+ }
+ is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl;
+ memmove(iLBCenc_inst->lpc_buffer,
+ iLBCenc_inst->lpc_buffer+LPC_LOOKBACK+BLOCKL_MAX-is,
+ is*sizeof(float));
+}
+
+/*----------------------------------------------------------------*
+
+
+ * lsf interpolator and conversion from lsf to a coefficients
+ * (subrutine to SimpleInterpolateLSF)
+ *---------------------------------------------------------------*/
+
+static void LSFinterpolate2a_enc(
+ float *a, /* (o) lpc coefficients */
+ float *lsf1,/* (i) first set of lsf coefficients */
+ float *lsf2,/* (i) second set of lsf coefficients */
+ float coef, /* (i) weighting coefficient to use between
+ lsf1 and lsf2 */
+ long length /* (i) length of coefficient vectors */
+){
+ float lsftmp[LPC_FILTERORDER];
+
+ interpolate(lsftmp, lsf1, lsf2, coef, length);
+ lsf2a(a, lsftmp);
+}
+
+/*----------------------------------------------------------------*
+ * lsf interpolator (subrutine to LPCencode)
+ *---------------------------------------------------------------*/
+
+static void SimpleInterpolateLSF(
+ float *syntdenum, /* (o) the synthesis filter denominator
+ resulting from the quantized
+ interpolated lsf */
+ float *weightdenum, /* (o) the weighting filter denominator
+ resulting from the unquantized
+ interpolated lsf */
+ float *lsf, /* (i) the unquantized lsf coefficients */
+ float *lsfdeq, /* (i) the dequantized lsf coefficients */
+ float *lsfold, /* (i) the unquantized lsf coefficients of
+ the previous signal frame */
+ float *lsfdeqold, /* (i) the dequantized lsf coefficients of
+ the previous signal frame */
+ int length, /* (i) should equate LPC_FILTERORDER */
+ iLBC_Enc_Inst_t *iLBCenc_inst
+ /* (i/o) the encoder state structure */
+){
+ int i, pos, lp_length;
+ float lp[LPC_FILTERORDER + 1], *lsf2, *lsfdeq2;
+
+ lsf2 = lsf + length;
+ lsfdeq2 = lsfdeq + length;
+ lp_length = length + 1;
+
+ if (iLBCenc_inst->mode==30) {
+ /* sub-frame 1: Interpolation between old and first
+ set of lsf coefficients */
+
+ LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq,
+ lsf_weightTbl_30ms[0], length);
+ memcpy(syntdenum,lp,lp_length*sizeof(float));
+ LSFinterpolate2a_enc(lp, lsfold, lsf,
+
+
+ lsf_weightTbl_30ms[0], length);
+ bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM, lp_length);
+
+ /* sub-frame 2 to 6: Interpolation between first
+ and second set of lsf coefficients */
+
+ pos = lp_length;
+ for (i = 1; i < iLBCenc_inst->nsub; i++) {
+ LSFinterpolate2a_enc(lp, lsfdeq, lsfdeq2,
+ lsf_weightTbl_30ms[i], length);
+ memcpy(syntdenum + pos,lp,lp_length*sizeof(float));
+
+ LSFinterpolate2a_enc(lp, lsf, lsf2,
+ lsf_weightTbl_30ms[i], length);
+ bwexpand(weightdenum + pos, lp,
+ LPC_CHIRP_WEIGHTDENUM, lp_length);
+ pos += lp_length;
+ }
+ }
+ else {
+ pos = 0;
+ for (i = 0; i < iLBCenc_inst->nsub; i++) {
+ LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq,
+ lsf_weightTbl_20ms[i], length);
+ memcpy(syntdenum+pos,lp,lp_length*sizeof(float));
+ LSFinterpolate2a_enc(lp, lsfold, lsf,
+ lsf_weightTbl_20ms[i], length);
+ bwexpand(weightdenum+pos, lp,
+ LPC_CHIRP_WEIGHTDENUM, lp_length);
+ pos += lp_length;
+ }
+ }
+
+ /* update memory */
+
+ if (iLBCenc_inst->mode==30) {
+ memcpy(lsfold, lsf2, length*sizeof(float));
+ memcpy(lsfdeqold, lsfdeq2, length*sizeof(float));
+ }
+ else {
+ memcpy(lsfold, lsf, length*sizeof(float));
+ memcpy(lsfdeqold, lsfdeq, length*sizeof(float));
+ }
+}
+
+/*----------------------------------------------------------------*
+ * lsf quantizer (subrutine to LPCencode)
+ *---------------------------------------------------------------*/
+
+static void SimplelsfQ(
+ float *lsfdeq, /* (o) dequantized lsf coefficients
+ (dimension FILTERORDER) */
+ int *index, /* (o) quantization index */
+ float *lsf, /* (i) the lsf coefficient vector to be
+
+
+ quantized (dimension FILTERORDER ) */
+ int lpc_n /* (i) number of lsf sets to quantize */
+){
+ /* Quantize first LSF with memoryless split VQ */
+ SplitVQ(lsfdeq, index, lsf, lsfCbTbl, LSF_NSPLIT,
+ dim_lsfCbTbl, size_lsfCbTbl);
+
+ if (lpc_n==2) {
+ /* Quantize second LSF with memoryless split VQ */
+ SplitVQ(lsfdeq + LPC_FILTERORDER, index + LSF_NSPLIT,
+ lsf + LPC_FILTERORDER, lsfCbTbl, LSF_NSPLIT,
+ dim_lsfCbTbl, size_lsfCbTbl);
+ }
+}
+
+/*----------------------------------------------------------------*
+ * lpc encoder
+ *---------------------------------------------------------------*/
+
+void LPCencode(
+ float *syntdenum, /* (i/o) synthesis filter coefficients
+ before/after encoding */
+ float *weightdenum, /* (i/o) weighting denumerator
+ coefficients before/after
+ encoding */
+ int *lsf_index, /* (o) lsf quantization index */
+ float *data, /* (i) lsf coefficients to quantize */
+ iLBC_Enc_Inst_t *iLBCenc_inst
+ /* (i/o) the encoder state structure */
+){
+ float lsf[LPC_FILTERORDER * LPC_N_MAX];
+ float lsfdeq[LPC_FILTERORDER * LPC_N_MAX];
+ int change=0;
+
+ SimpleAnalysis(lsf, data, iLBCenc_inst);
+ SimplelsfQ(lsfdeq, lsf_index, lsf, iLBCenc_inst->lpc_n);
+ change=LSF_check(lsfdeq, LPC_FILTERORDER, iLBCenc_inst->lpc_n);
+ SimpleInterpolateLSF(syntdenum, weightdenum,
+ lsf, lsfdeq, iLBCenc_inst->lsfold,
+ iLBCenc_inst->lsfdeqold, LPC_FILTERORDER, iLBCenc_inst);
+}
+
+
+
diff --git a/trunk/codecs/ilbc/LPCencode.h b/trunk/codecs/ilbc/LPCencode.h
new file mode 100644
index 000000000..5712163c2
--- /dev/null
+++ b/trunk/codecs/ilbc/LPCencode.h
@@ -0,0 +1,29 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ LPCencode.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_LPCENCOD_H
+#define __iLBC_LPCENCOD_H
+
+void LPCencode(
+ float *syntdenum, /* (i/o) synthesis filter coefficients
+ before/after encoding */
+ float *weightdenum, /* (i/o) weighting denumerator coefficients
+ before/after encoding */
+ int *lsf_index, /* (o) lsf quantization index */
+ float *data, /* (i) lsf coefficients to quantize */
+ iLBC_Enc_Inst_t *iLBCenc_inst
+ /* (i/o) the encoder state structure */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/Makefile b/trunk/codecs/ilbc/Makefile
new file mode 100644
index 000000000..c3db840ef
--- /dev/null
+++ b/trunk/codecs/ilbc/Makefile
@@ -0,0 +1,21 @@
+LIB=libilbc.a
+CFLAGS+=-fPIC
+
+OBJS=anaFilter.o iCBSearch.o packing.o \
+ constants.o gainquant.o iLBC_decode.o StateConstructW.o \
+ createCB.o getCBvec.o iLBC_encode.o StateSearchW.o doCPLC.o \
+ helpfun.o syntFilter.o enhancer.o hpInput.o LPCdecode.o \
+ filter.o hpOutput.o LPCencode.o FrameClassify.o iCBConstruct.o lsf.o
+
+include $(ASTTOPDIR)/Makefile.rules
+
+all: $(LIB)
+
+$(LIB): $(OBJS)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+clean:
+ rm -f $(LIB) *.o .*.d
+ rm -f *.s *.i
diff --git a/trunk/codecs/ilbc/StateConstructW.c b/trunk/codecs/ilbc/StateConstructW.c
new file mode 100644
index 000000000..e48fe035d
--- /dev/null
+++ b/trunk/codecs/ilbc/StateConstructW.c
@@ -0,0 +1,76 @@
+
+/******************************************************************
+
+
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ StateConstructW.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <string.h>
+
+#include "iLBC_define.h"
+#include "StateConstructW.h"
+#include "constants.h"
+#include "filter.h"
+
+/*----------------------------------------------------------------*
+ * decoding of the start state
+ *---------------------------------------------------------------*/
+
+void StateConstructW(
+ int idxForMax, /* (i) 6-bit index for the quantization of
+ max amplitude */
+ int *idxVec, /* (i) vector of quantization indexes */
+ float *syntDenum, /* (i) synthesis filter denumerator */
+ float *out, /* (o) the decoded state vector */
+ int len /* (i) length of a state vector */
+){
+ float maxVal, tmpbuf[LPC_FILTERORDER+2*STATE_LEN], *tmp,
+ numerator[LPC_FILTERORDER+1];
+ float foutbuf[LPC_FILTERORDER+2*STATE_LEN], *fout;
+ int k,tmpi;
+
+ /* decoding of the maximum value */
+
+ maxVal = state_frgqTbl[idxForMax];
+ maxVal = (float)pow(10,maxVal)/(float)4.5;
+
+ /* initialization of buffers and coefficients */
+
+ memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float));
+ memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float));
+ for (k=0; k<LPC_FILTERORDER; k++) {
+ numerator[k]=syntDenum[LPC_FILTERORDER-k];
+ }
+ numerator[LPC_FILTERORDER]=syntDenum[0];
+ tmp = &tmpbuf[LPC_FILTERORDER];
+ fout = &foutbuf[LPC_FILTERORDER];
+
+ /* decoding of the sample values */
+
+ for (k=0; k<len; k++) {
+ tmpi = len-1-k;
+ /* maxVal = 1/scal */
+
+
+ tmp[k] = maxVal*state_sq3Tbl[idxVec[tmpi]];
+ }
+
+ /* circular convolution with all-pass filter */
+
+ memset(tmp+len, 0, len*sizeof(float));
+ ZeroPoleFilter(tmp, numerator, syntDenum, 2*len,
+ LPC_FILTERORDER, fout);
+ for (k=0;k<len;k++) {
+ out[k] = fout[len-1-k]+fout[2*len-1-k];
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/StateConstructW.h b/trunk/codecs/ilbc/StateConstructW.h
new file mode 100644
index 000000000..bf9332b4a
--- /dev/null
+++ b/trunk/codecs/ilbc/StateConstructW.h
@@ -0,0 +1,27 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ StateConstructW.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_STATECONSTRUCTW_H
+#define __iLBC_STATECONSTRUCTW_H
+
+void StateConstructW(
+ int idxForMax, /* (i) 6-bit index for the quantization of
+ max amplitude */
+ int *idxVec, /* (i) vector of quantization indexes */
+ float *syntDenum, /* (i) synthesis filter denumerator */
+ float *out, /* (o) the decoded state vector */
+ int len /* (i) length of a state vector */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/StateSearchW.c b/trunk/codecs/ilbc/StateSearchW.c
new file mode 100644
index 000000000..b87b9f3c6
--- /dev/null
+++ b/trunk/codecs/ilbc/StateSearchW.c
@@ -0,0 +1,194 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ StateSearchW.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <string.h>
+
+#include "iLBC_define.h"
+#include "StateSearchW.h"
+#include "constants.h"
+#include "filter.h"
+#include "helpfun.h"
+
+/*----------------------------------------------------------------*
+ * predictive noise shaping encoding of scaled start state
+ * (subrutine for StateSearchW)
+ *---------------------------------------------------------------*/
+
+void AbsQuantW(
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i) Encoder instance */
+ float *in, /* (i) vector to encode */
+ float *syntDenum, /* (i) denominator of synthesis filter */
+ float *weightDenum, /* (i) denominator of weighting filter */
+ int *out, /* (o) vector of quantizer indexes */
+ int len, /* (i) length of vector to encode and
+ vector of quantizer indexes */
+ int state_first /* (i) position of start state in the
+ 80 vec */
+){
+ float *syntOut;
+ float syntOutBuf[LPC_FILTERORDER+STATE_SHORT_LEN_30MS];
+ float toQ, xq;
+ int n;
+ int index;
+
+ /* initialization of buffer for filtering */
+
+ memset(syntOutBuf, 0, LPC_FILTERORDER*sizeof(float));
+
+
+
+ /* initialization of pointer for filtering */
+
+ syntOut = &syntOutBuf[LPC_FILTERORDER];
+
+ /* synthesis and weighting filters on input */
+
+ if (state_first) {
+ AllPoleFilter (in, weightDenum, SUBL, LPC_FILTERORDER);
+ } else {
+ AllPoleFilter (in, weightDenum,
+ iLBCenc_inst->state_short_len-SUBL,
+ LPC_FILTERORDER);
+ }
+
+ /* encoding loop */
+
+ for (n=0; n<len; n++) {
+
+ /* time update of filter coefficients */
+
+ if ((state_first)&&(n==SUBL)){
+ syntDenum += (LPC_FILTERORDER+1);
+ weightDenum += (LPC_FILTERORDER+1);
+
+ /* synthesis and weighting filters on input */
+ AllPoleFilter (&in[n], weightDenum, len-n,
+ LPC_FILTERORDER);
+
+ } else if ((state_first==0)&&
+ (n==(iLBCenc_inst->state_short_len-SUBL))) {
+ syntDenum += (LPC_FILTERORDER+1);
+ weightDenum += (LPC_FILTERORDER+1);
+
+ /* synthesis and weighting filters on input */
+ AllPoleFilter (&in[n], weightDenum, len-n,
+ LPC_FILTERORDER);
+
+ }
+
+ /* prediction of synthesized and weighted input */
+
+ syntOut[n] = 0.0;
+ AllPoleFilter (&syntOut[n], weightDenum, 1,
+ LPC_FILTERORDER);
+
+ /* quantization */
+
+ toQ = in[n]-syntOut[n];
+ sort_sq(&xq, &index, toQ, state_sq3Tbl, 8);
+ out[n]=index;
+ syntOut[n] = state_sq3Tbl[out[n]];
+
+ /* update of the prediction filter */
+
+
+
+ AllPoleFilter(&syntOut[n], weightDenum, 1,
+ LPC_FILTERORDER);
+ }
+}
+
+/*----------------------------------------------------------------*
+ * encoding of start state
+ *---------------------------------------------------------------*/
+
+void StateSearchW(
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i) Encoder instance */
+ float *residual,/* (i) target residual vector */
+ float *syntDenum, /* (i) lpc synthesis filter */
+ float *weightDenum, /* (i) weighting filter denuminator */
+ int *idxForMax, /* (o) quantizer index for maximum
+ amplitude */
+ int *idxVec, /* (o) vector of quantization indexes */
+ int len, /* (i) length of all vectors */
+ int state_first /* (i) position of start state in the
+ 80 vec */
+){
+ float dtmp, maxVal;
+ float tmpbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS];
+ float *tmp, numerator[1+LPC_FILTERORDER];
+ float foutbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS], *fout;
+ int k;
+ float qmax, scal;
+
+ /* initialization of buffers and filter coefficients */
+
+ memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float));
+ memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float));
+ for (k=0; k<LPC_FILTERORDER; k++) {
+ numerator[k]=syntDenum[LPC_FILTERORDER-k];
+ }
+ numerator[LPC_FILTERORDER]=syntDenum[0];
+ tmp = &tmpbuf[LPC_FILTERORDER];
+ fout = &foutbuf[LPC_FILTERORDER];
+
+ /* circular convolution with the all-pass filter */
+
+ memcpy(tmp, residual, len*sizeof(float));
+ memset(tmp+len, 0, len*sizeof(float));
+ ZeroPoleFilter(tmp, numerator, syntDenum, 2*len,
+ LPC_FILTERORDER, fout);
+ for (k=0; k<len; k++) {
+ fout[k] += fout[k+len];
+ }
+
+ /* identification of the maximum amplitude value */
+
+ maxVal = fout[0];
+
+
+ for (k=1; k<len; k++) {
+
+ if (fout[k]*fout[k] > maxVal*maxVal){
+ maxVal = fout[k];
+ }
+ }
+ maxVal=(float)fabs(maxVal);
+
+ /* encoding of the maximum amplitude value */
+
+ if (maxVal < 10.0) {
+ maxVal = 10.0;
+ }
+ maxVal = (float)log10(maxVal);
+ sort_sq(&dtmp, idxForMax, maxVal, state_frgqTbl, 64);
+
+ /* decoding of the maximum amplitude representation value,
+ and corresponding scaling of start state */
+
+ maxVal=state_frgqTbl[*idxForMax];
+ qmax = (float)pow(10,maxVal);
+ scal = (float)(4.5)/qmax;
+ for (k=0; k<len; k++){
+ fout[k] *= scal;
+ }
+
+ /* predictive noise shaping encoding of scaled start state */
+
+ AbsQuantW(iLBCenc_inst, fout,syntDenum,
+ weightDenum,idxVec, len, state_first);
+}
+
+
diff --git a/trunk/codecs/ilbc/StateSearchW.h b/trunk/codecs/ilbc/StateSearchW.h
new file mode 100644
index 000000000..7b91476d9
--- /dev/null
+++ b/trunk/codecs/ilbc/StateSearchW.h
@@ -0,0 +1,48 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ StateSearchW.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_STATESEARCHW_H
+#define __iLBC_STATESEARCHW_H
+
+void AbsQuantW(
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i) Encoder instance */
+ float *in, /* (i) vector to encode */
+ float *syntDenum, /* (i) denominator of synthesis filter */
+ float *weightDenum, /* (i) denominator of weighting filter */
+ int *out, /* (o) vector of quantizer indexes */
+ int len, /* (i) length of vector to encode and
+ vector of quantizer indexes */
+ int state_first /* (i) position of start state in the
+ 80 vec */
+);
+
+void StateSearchW(
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i) Encoder instance */
+ float *residual,/* (i) target residual vector */
+ float *syntDenum, /* (i) lpc synthesis filter */
+ float *weightDenum, /* (i) weighting filter denuminator */
+ int *idxForMax, /* (o) quantizer index for maximum
+ amplitude */
+ int *idxVec, /* (o) vector of quantization indexes */
+ int len, /* (i) length of all vectors */
+ int state_first /* (i) position of start state in the
+
+
+ 80 vec */
+);
+
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/anaFilter.c b/trunk/codecs/ilbc/anaFilter.c
new file mode 100644
index 000000000..f88fefaa0
--- /dev/null
+++ b/trunk/codecs/ilbc/anaFilter.c
@@ -0,0 +1,71 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ anaFilter.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <string.h>
+#include "anaFilter.h"
+#include "iLBC_define.h"
+
+/*----------------------------------------------------------------*
+ * LP analysis filter.
+
+
+ *---------------------------------------------------------------*/
+
+void anaFilter(
+ float *In, /* (i) Signal to be filtered */
+ float *a, /* (i) LP parameters */
+ int len,/* (i) Length of signal */
+ float *Out, /* (o) Filtered signal */
+ float *mem /* (i/o) Filter state */
+){
+ int i, j;
+ float *po, *pi, *pm, *pa;
+
+ po = Out;
+
+ /* Filter first part using memory from past */
+
+ for (i=0; i<LPC_FILTERORDER; i++) {
+ pi = &In[i];
+ pm = &mem[LPC_FILTERORDER-1];
+ pa = a;
+ *po=0.0;
+ for (j=0; j<=i; j++) {
+ *po+=(*pa++)*(*pi--);
+ }
+ for (j=i+1; j<LPC_FILTERORDER+1; j++) {
+
+ *po+=(*pa++)*(*pm--);
+ }
+ po++;
+ }
+
+ /* Filter last part where the state is entierly
+ in the input vector */
+
+ for (i=LPC_FILTERORDER; i<len; i++) {
+ pi = &In[i];
+ pa = a;
+ *po=0.0;
+ for (j=0; j<LPC_FILTERORDER+1; j++) {
+ *po+=(*pa++)*(*pi--);
+ }
+ po++;
+ }
+
+ /* Update state vector */
+
+ memcpy(mem, &In[len-LPC_FILTERORDER],
+ LPC_FILTERORDER*sizeof(float));
+}
+
+
diff --git a/trunk/codecs/ilbc/anaFilter.h b/trunk/codecs/ilbc/anaFilter.h
new file mode 100644
index 000000000..5b18b7065
--- /dev/null
+++ b/trunk/codecs/ilbc/anaFilter.h
@@ -0,0 +1,26 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ anaFilter.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_ANAFILTER_H
+#define __iLBC_ANAFILTER_H
+
+void anaFilter(
+ float *In, /* (i) Signal to be filtered */
+ float *a, /* (i) LP parameters */
+ int len,/* (i) Length of signal */
+ float *Out, /* (o) Filtered signal */
+ float *mem /* (i/o) Filter state */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/constants.c b/trunk/codecs/ilbc/constants.c
new file mode 100644
index 000000000..c466c0f7b
--- /dev/null
+++ b/trunk/codecs/ilbc/constants.c
@@ -0,0 +1,729 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ constants.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include "iLBC_define.h"
+
+/* ULP bit allocation */
+
+ /* 20 ms frame */
+
+const iLBC_ULP_Inst_t ULP_20msTbl = {
+ /* LSF */
+ { {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0},
+ {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}},
+ /* Start state location, gain and samples */
+ {2,0,0,0,0},
+ {1,0,0,0,0},
+ {6,0,0,0,0},
+ {0,1,2,0,0},
+ /* extra CB index and extra CB gain */
+ {{6,0,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
+ {{2,0,3,0,0}, {1,1,2,0,0}, {0,0,3,0,0}},
+ /* CB index and CB gain */
+ { {{7,0,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
+ {{0,0,8,0,0}, {0,0,8,0,0}, {0,0,8,0,0}},
+ {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}},
+ {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}},
+ { {{1,2,2,0,0}, {1,1,2,0,0}, {0,0,3,0,0}},
+ {{1,1,3,0,0}, {0,2,2,0,0}, {0,0,3,0,0}},
+ {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}},
+ {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}}
+};
+
+ /* 30 ms frame */
+
+const iLBC_ULP_Inst_t ULP_30msTbl = {
+ /* LSF */
+ { {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0},
+
+
+ {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0}},
+ /* Start state location, gain and samples */
+ {3,0,0,0,0},
+ {1,0,0,0,0},
+ {6,0,0,0,0},
+ {0,1,2,0,0},
+ /* extra CB index and extra CB gain */
+ {{4,2,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
+ {{1,1,3,0,0}, {1,1,2,0,0}, {0,0,3,0,0}},
+ /* CB index and CB gain */
+ { {{6,1,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
+ {{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}},
+ {{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}},
+ {{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}}},
+ { {{1,2,2,0,0}, {1,2,1,0,0}, {0,0,3,0,0}},
+ {{0,2,3,0,0}, {0,2,2,0,0}, {0,0,3,0,0}},
+ {{0,1,4,0,0}, {0,1,3,0,0}, {0,0,3,0,0}},
+ {{0,1,4,0,0}, {0,1,3,0,0}, {0,0,3,0,0}}}
+};
+
+/* HP Filters */
+
+float hpi_zero_coefsTbl[3] = {
+ (float)0.92727436, (float)-1.8544941, (float)0.92727436
+};
+float hpi_pole_coefsTbl[3] = {
+ (float)1.0, (float)-1.9059465, (float)0.9114024
+};
+float hpo_zero_coefsTbl[3] = {
+ (float)0.93980581, (float)-1.8795834, (float)0.93980581
+};
+float hpo_pole_coefsTbl[3] = {
+ (float)1.0, (float)-1.9330735, (float)0.93589199
+};
+
+/* LP Filter */
+
+float lpFilt_coefsTbl[FILTERORDER_DS]={
+ (float)-0.066650, (float)0.125000, (float)0.316650,
+ (float)0.414063, (float)0.316650,
+ (float)0.125000, (float)-0.066650
+};
+
+/* State quantization tables */
+
+float state_sq3Tbl[8] = {
+ (float)-3.719849, (float)-2.177490, (float)-1.130005,
+ (float)-0.309692, (float)0.444214, (float)1.329712,
+ (float)2.436279, (float)3.983887
+};
+
+float state_frgqTbl[64] = {
+ (float)1.000085, (float)1.071695, (float)1.140395,
+ (float)1.206868, (float)1.277188, (float)1.351503,
+
+
+ (float)1.429380, (float)1.500727, (float)1.569049,
+ (float)1.639599, (float)1.707071, (float)1.781531,
+ (float)1.840799, (float)1.901550, (float)1.956695,
+ (float)2.006750, (float)2.055474, (float)2.102787,
+ (float)2.142819, (float)2.183592, (float)2.217962,
+ (float)2.257177, (float)2.295739, (float)2.332967,
+ (float)2.369248, (float)2.402792, (float)2.435080,
+ (float)2.468598, (float)2.503394, (float)2.539284,
+ (float)2.572944, (float)2.605036, (float)2.636331,
+ (float)2.668939, (float)2.698780, (float)2.729101,
+ (float)2.759786, (float)2.789834, (float)2.818679,
+ (float)2.848074, (float)2.877470, (float)2.906899,
+ (float)2.936655, (float)2.967804, (float)3.000115,
+ (float)3.033367, (float)3.066355, (float)3.104231,
+ (float)3.141499, (float)3.183012, (float)3.222952,
+ (float)3.265433, (float)3.308441, (float)3.350823,
+ (float)3.395275, (float)3.442793, (float)3.490801,
+ (float)3.542514, (float)3.604064, (float)3.666050,
+ (float)3.740994, (float)3.830749, (float)3.938770,
+ (float)4.101764
+};
+
+/* CB tables */
+
+int search_rangeTbl[5][CB_NSTAGES]={{58,58,58}, {108,44,44},
+ {108,108,108}, {108,108,108}, {108,108,108}};
+int stMemLTbl=85;
+int memLfTbl[NASUB_MAX]={147,147,147,147};
+
+/* expansion filter(s) */
+
+float cbfiltersTbl[CB_FILTERLEN]={
+ (float)-0.034180, (float)0.108887, (float)-0.184326,
+ (float)0.806152, (float)0.713379, (float)-0.144043,
+ (float)0.083740, (float)-0.033691
+};
+
+/* Gain Quantization */
+
+float gain_sq3Tbl[8]={
+ (float)-1.000000, (float)-0.659973, (float)-0.330017,
+ (float)0.000000, (float)0.250000, (float)0.500000,
+ (float)0.750000, (float)1.00000};
+
+float gain_sq4Tbl[16]={
+ (float)-1.049988, (float)-0.900024, (float)-0.750000,
+ (float)-0.599976, (float)-0.450012, (float)-0.299988,
+ (float)-0.150024, (float)0.000000, (float)0.150024,
+ (float)0.299988, (float)0.450012, (float)0.599976,
+ (float)0.750000, (float)0.900024, (float)1.049988,
+ (float)1.200012};
+
+float gain_sq5Tbl[32]={
+ (float)0.037476, (float)0.075012, (float)0.112488,
+
+
+ (float)0.150024, (float)0.187500, (float)0.224976,
+ (float)0.262512, (float)0.299988, (float)0.337524,
+ (float)0.375000, (float)0.412476, (float)0.450012,
+ (float)0.487488, (float)0.525024, (float)0.562500,
+ (float)0.599976, (float)0.637512, (float)0.674988,
+ (float)0.712524, (float)0.750000, (float)0.787476,
+ (float)0.825012, (float)0.862488, (float)0.900024,
+ (float)0.937500, (float)0.974976, (float)1.012512,
+ (float)1.049988, (float)1.087524, (float)1.125000,
+ (float)1.162476, (float)1.200012};
+
+/* Enhancer - Upsamling a factor 4 (ENH_UPS0 = 4) */
+float polyphaserTbl[ENH_UPS0*(2*ENH_FL0+1)]={
+ (float)0.000000, (float)0.000000, (float)0.000000,
+(float)1.000000,
+ (float)0.000000, (float)0.000000, (float)0.000000,
+ (float)0.015625, (float)-0.076904, (float)0.288330,
+(float)0.862061,
+ (float)-0.106445, (float)0.018799, (float)-0.015625,
+ (float)0.023682, (float)-0.124268, (float)0.601563,
+(float)0.601563,
+ (float)-0.124268, (float)0.023682, (float)-0.023682,
+ (float)0.018799, (float)-0.106445, (float)0.862061,
+(float)0.288330,
+ (float)-0.076904, (float)0.015625, (float)-0.018799};
+
+float enh_plocsTbl[ENH_NBLOCKS_TOT] = {(float)40.0, (float)120.0,
+ (float)200.0, (float)280.0, (float)360.0,
+ (float)440.0, (float)520.0, (float)600.0};
+
+/* LPC analysis and quantization */
+
+int dim_lsfCbTbl[LSF_NSPLIT] = {3, 3, 4};
+int size_lsfCbTbl[LSF_NSPLIT] = {64,128,128};
+
+
+float lsfmeanTbl[LPC_FILTERORDER] = {
+ (float)0.281738, (float)0.445801, (float)0.663330,
+ (float)0.962524, (float)1.251831, (float)1.533081,
+ (float)1.850586, (float)2.137817, (float)2.481445,
+ (float)2.777344};
+
+float lsf_weightTbl_30ms[6] = {(float)(1.0/2.0), (float)1.0,
+(float)(2.0/3.0),
+ (float)(1.0/3.0), (float)0.0, (float)0.0};
+
+float lsf_weightTbl_20ms[4] = {(float)(3.0/4.0), (float)(2.0/4.0),
+ (float)(1.0/4.0), (float)(0.0)};
+
+/* Hanning LPC window */
+float lpc_winTbl[BLOCKL_MAX]={
+ (float)0.000183, (float)0.000671, (float)0.001526,
+ (float)0.002716, (float)0.004242, (float)0.006104,
+ (float)0.008301, (float)0.010834, (float)0.013702,
+
+
+ (float)0.016907, (float)0.020416, (float)0.024261,
+ (float)0.028442, (float)0.032928, (float)0.037750,
+ (float)0.042877, (float)0.048309, (float)0.054047,
+ (float)0.060089, (float)0.066437, (float)0.073090,
+ (float)0.080017, (float)0.087219, (float)0.094727,
+ (float)0.102509, (float)0.110535, (float)0.118835,
+ (float)0.127411, (float)0.136230, (float)0.145294,
+ (float)0.154602, (float)0.164154, (float)0.173920,
+ (float)0.183899, (float)0.194122, (float)0.204529,
+ (float)0.215149, (float)0.225952, (float)0.236938,
+ (float)0.248108, (float)0.259460, (float)0.270966,
+ (float)0.282654, (float)0.294464, (float)0.306396,
+ (float)0.318481, (float)0.330688, (float)0.343018,
+ (float)0.355438, (float)0.367981, (float)0.380585,
+ (float)0.393280, (float)0.406067, (float)0.418884,
+ (float)0.431763, (float)0.444702, (float)0.457672,
+ (float)0.470673, (float)0.483704, (float)0.496735,
+ (float)0.509766, (float)0.522797, (float)0.535828,
+ (float)0.548798, (float)0.561768, (float)0.574677,
+ (float)0.587524, (float)0.600342, (float)0.613068,
+ (float)0.625732, (float)0.638306, (float)0.650787,
+ (float)0.663147, (float)0.675415, (float)0.687561,
+ (float)0.699585, (float)0.711487, (float)0.723206,
+ (float)0.734802, (float)0.746216, (float)0.757477,
+ (float)0.768585, (float)0.779480, (float)0.790192,
+ (float)0.800720, (float)0.811005, (float)0.821106,
+ (float)0.830994, (float)0.840668, (float)0.850067,
+ (float)0.859253, (float)0.868225, (float)0.876892,
+ (float)0.885345, (float)0.893524, (float)0.901428,
+ (float)0.909058, (float)0.916412, (float)0.923492,
+ (float)0.930267, (float)0.936768, (float)0.942963,
+ (float)0.948853, (float)0.954437, (float)0.959717,
+ (float)0.964691, (float)0.969360, (float)0.973694,
+ (float)0.977692, (float)0.981384, (float)0.984741,
+ (float)0.987762, (float)0.990479, (float)0.992828,
+ (float)0.994873, (float)0.996552, (float)0.997925,
+ (float)0.998932, (float)0.999603, (float)0.999969,
+ (float)0.999969, (float)0.999603, (float)0.998932,
+ (float)0.997925, (float)0.996552, (float)0.994873,
+ (float)0.992828, (float)0.990479, (float)0.987762,
+ (float)0.984741, (float)0.981384, (float)0.977692,
+ (float)0.973694, (float)0.969360, (float)0.964691,
+ (float)0.959717, (float)0.954437, (float)0.948853,
+ (float)0.942963, (float)0.936768, (float)0.930267,
+ (float)0.923492, (float)0.916412, (float)0.909058,
+ (float)0.901428, (float)0.893524, (float)0.885345,
+ (float)0.876892, (float)0.868225, (float)0.859253,
+ (float)0.850067, (float)0.840668, (float)0.830994,
+ (float)0.821106, (float)0.811005, (float)0.800720,
+ (float)0.790192, (float)0.779480, (float)0.768585,
+ (float)0.757477, (float)0.746216, (float)0.734802,
+ (float)0.723206, (float)0.711487, (float)0.699585,
+ (float)0.687561, (float)0.675415, (float)0.663147,
+ (float)0.650787, (float)0.638306, (float)0.625732,
+
+
+ (float)0.613068, (float)0.600342, (float)0.587524,
+ (float)0.574677, (float)0.561768, (float)0.548798,
+ (float)0.535828, (float)0.522797, (float)0.509766,
+ (float)0.496735, (float)0.483704, (float)0.470673,
+ (float)0.457672, (float)0.444702, (float)0.431763,
+ (float)0.418884, (float)0.406067, (float)0.393280,
+ (float)0.380585, (float)0.367981, (float)0.355438,
+ (float)0.343018, (float)0.330688, (float)0.318481,
+ (float)0.306396, (float)0.294464, (float)0.282654,
+ (float)0.270966, (float)0.259460, (float)0.248108,
+ (float)0.236938, (float)0.225952, (float)0.215149,
+ (float)0.204529, (float)0.194122, (float)0.183899,
+ (float)0.173920, (float)0.164154, (float)0.154602,
+ (float)0.145294, (float)0.136230, (float)0.127411,
+ (float)0.118835, (float)0.110535, (float)0.102509,
+ (float)0.094727, (float)0.087219, (float)0.080017,
+ (float)0.073090, (float)0.066437, (float)0.060089,
+ (float)0.054047, (float)0.048309, (float)0.042877,
+ (float)0.037750, (float)0.032928, (float)0.028442,
+ (float)0.024261, (float)0.020416, (float)0.016907,
+ (float)0.013702, (float)0.010834, (float)0.008301,
+ (float)0.006104, (float)0.004242, (float)0.002716,
+ (float)0.001526, (float)0.000671, (float)0.000183
+};
+
+/* Asymmetric LPC window */
+float lpc_asymwinTbl[BLOCKL_MAX]={
+ (float)0.000061, (float)0.000214, (float)0.000458,
+ (float)0.000824, (float)0.001282, (float)0.001831,
+ (float)0.002472, (float)0.003235, (float)0.004120,
+ (float)0.005066, (float)0.006134, (float)0.007294,
+ (float)0.008545, (float)0.009918, (float)0.011383,
+ (float)0.012939, (float)0.014587, (float)0.016357,
+ (float)0.018219, (float)0.020172, (float)0.022217,
+ (float)0.024353, (float)0.026611, (float)0.028961,
+ (float)0.031372, (float)0.033905, (float)0.036530,
+ (float)0.039276, (float)0.042084, (float)0.044983,
+ (float)0.047974, (float)0.051086, (float)0.054260,
+ (float)0.057526, (float)0.060883, (float)0.064331,
+ (float)0.067871, (float)0.071503, (float)0.075226,
+ (float)0.079010, (float)0.082916, (float)0.086884,
+ (float)0.090942, (float)0.095062, (float)0.099304,
+ (float)0.103607, (float)0.107971, (float)0.112427,
+ (float)0.116974, (float)0.121582, (float)0.126282,
+ (float)0.131073, (float)0.135895, (float)0.140839,
+ (float)0.145813, (float)0.150879, (float)0.156006,
+ (float)0.161224, (float)0.166504, (float)0.171844,
+ (float)0.177246, (float)0.182709, (float)0.188263,
+ (float)0.193848, (float)0.199524, (float)0.205231,
+ (float)0.211029, (float)0.216858, (float)0.222778,
+ (float)0.228729, (float)0.234741, (float)0.240814,
+ (float)0.246918, (float)0.253082, (float)0.259308,
+ (float)0.265564, (float)0.271881, (float)0.278259,
+ (float)0.284668, (float)0.291107, (float)0.297607,
+
+
+ (float)0.304138, (float)0.310730, (float)0.317322,
+ (float)0.323975, (float)0.330658, (float)0.337372,
+ (float)0.344147, (float)0.350922, (float)0.357727,
+ (float)0.364594, (float)0.371460, (float)0.378357,
+ (float)0.385284, (float)0.392212, (float)0.399170,
+ (float)0.406158, (float)0.413177, (float)0.420197,
+ (float)0.427246, (float)0.434296, (float)0.441376,
+ (float)0.448456, (float)0.455536, (float)0.462646,
+ (float)0.469757, (float)0.476868, (float)0.483978,
+ (float)0.491089, (float)0.498230, (float)0.505341,
+ (float)0.512451, (float)0.519592, (float)0.526703,
+ (float)0.533813, (float)0.540924, (float)0.548004,
+ (float)0.555084, (float)0.562164, (float)0.569244,
+ (float)0.576294, (float)0.583313, (float)0.590332,
+ (float)0.597321, (float)0.604309, (float)0.611267,
+ (float)0.618195, (float)0.625092, (float)0.631989,
+ (float)0.638855, (float)0.645660, (float)0.652466,
+ (float)0.659241, (float)0.665985, (float)0.672668,
+ (float)0.679352, (float)0.685974, (float)0.692566,
+ (float)0.699127, (float)0.705658, (float)0.712128,
+ (float)0.718536, (float)0.724945, (float)0.731262,
+ (float)0.737549, (float)0.743805, (float)0.750000,
+ (float)0.756134, (float)0.762238, (float)0.768280,
+ (float)0.774261, (float)0.780182, (float)0.786072,
+ (float)0.791870, (float)0.797638, (float)0.803314,
+ (float)0.808960, (float)0.814514, (float)0.820038,
+ (float)0.825470, (float)0.830841, (float)0.836151,
+ (float)0.841400, (float)0.846558, (float)0.851654,
+ (float)0.856689, (float)0.861633, (float)0.866516,
+ (float)0.871338, (float)0.876068, (float)0.880737,
+ (float)0.885315, (float)0.889801, (float)0.894226,
+ (float)0.898560, (float)0.902832, (float)0.907013,
+ (float)0.911102, (float)0.915100, (float)0.919037,
+ (float)0.922882, (float)0.926636, (float)0.930328,
+ (float)0.933899, (float)0.937408, (float)0.940796,
+ (float)0.944122, (float)0.947357, (float)0.950470,
+ (float)0.953522, (float)0.956482, (float)0.959351,
+ (float)0.962097, (float)0.964783, (float)0.967377,
+ (float)0.969849, (float)0.972229, (float)0.974518,
+ (float)0.976715, (float)0.978821, (float)0.980835,
+ (float)0.982727, (float)0.984528, (float)0.986237,
+ (float)0.987854, (float)0.989380, (float)0.990784,
+ (float)0.992096, (float)0.993317, (float)0.994415,
+ (float)0.995422, (float)0.996338, (float)0.997162,
+ (float)0.997864, (float)0.998474, (float)0.998962,
+ (float)0.999390, (float)0.999695, (float)0.999878,
+ (float)0.999969, (float)0.999969, (float)0.996918,
+ (float)0.987701, (float)0.972382, (float)0.951050,
+ (float)0.923889, (float)0.891022, (float)0.852631,
+ (float)0.809021, (float)0.760406, (float)0.707092,
+ (float)0.649445, (float)0.587799, (float)0.522491,
+ (float)0.453979, (float)0.382690, (float)0.309021,
+ (float)0.233459, (float)0.156433, (float)0.078461
+};
+
+
+
+/* Lag window for LPC */
+float lpc_lagwinTbl[LPC_FILTERORDER + 1]={
+ (float)1.000100, (float)0.998890, (float)0.995569,
+ (float)0.990057, (float)0.982392,
+ (float)0.972623, (float)0.960816, (float)0.947047,
+ (float)0.931405, (float)0.913989, (float)0.894909};
+
+/* LSF quantization*/
+float lsfCbTbl[64 * 3 + 128 * 3 + 128 * 4] = {
+(float)0.155396, (float)0.273193, (float)0.451172,
+(float)0.390503, (float)0.648071, (float)1.002075,
+(float)0.440186, (float)0.692261, (float)0.955688,
+(float)0.343628, (float)0.642334, (float)1.071533,
+(float)0.318359, (float)0.491577, (float)0.670532,
+(float)0.193115, (float)0.375488, (float)0.725708,
+(float)0.364136, (float)0.510376, (float)0.658691,
+(float)0.297485, (float)0.527588, (float)0.842529,
+(float)0.227173, (float)0.365967, (float)0.563110,
+(float)0.244995, (float)0.396729, (float)0.636475,
+(float)0.169434, (float)0.300171, (float)0.520264,
+(float)0.312866, (float)0.464478, (float)0.643188,
+(float)0.248535, (float)0.429932, (float)0.626099,
+(float)0.236206, (float)0.491333, (float)0.817139,
+(float)0.334961, (float)0.625122, (float)0.895752,
+(float)0.343018, (float)0.518555, (float)0.698608,
+(float)0.372803, (float)0.659790, (float)0.945435,
+(float)0.176880, (float)0.316528, (float)0.581421,
+(float)0.416382, (float)0.625977, (float)0.805176,
+(float)0.303223, (float)0.568726, (float)0.915039,
+(float)0.203613, (float)0.351440, (float)0.588135,
+(float)0.221191, (float)0.375000, (float)0.614746,
+(float)0.199951, (float)0.323364, (float)0.476074,
+(float)0.300781, (float)0.433350, (float)0.566895,
+(float)0.226196, (float)0.354004, (float)0.507568,
+(float)0.300049, (float)0.508179, (float)0.711670,
+(float)0.312012, (float)0.492676, (float)0.763428,
+(float)0.329956, (float)0.541016, (float)0.795776,
+(float)0.373779, (float)0.604614, (float)0.928833,
+(float)0.210571, (float)0.452026, (float)0.755249,
+(float)0.271118, (float)0.473267, (float)0.662476,
+(float)0.285522, (float)0.436890, (float)0.634399,
+(float)0.246704, (float)0.565552, (float)0.859009,
+(float)0.270508, (float)0.406250, (float)0.553589,
+(float)0.361450, (float)0.578491, (float)0.813843,
+(float)0.342651, (float)0.482788, (float)0.622437,
+(float)0.340332, (float)0.549438, (float)0.743164,
+(float)0.200439, (float)0.336304, (float)0.540894,
+(float)0.407837, (float)0.644775, (float)0.895142,
+(float)0.294678, (float)0.454834, (float)0.699097,
+(float)0.193115, (float)0.344482, (float)0.643188,
+(float)0.275757, (float)0.420776, (float)0.598755,
+(float)0.380493, (float)0.608643, (float)0.861084,
+(float)0.222778, (float)0.426147, (float)0.676514,
+
+
+(float)0.407471, (float)0.700195, (float)1.053101,
+(float)0.218384, (float)0.377197, (float)0.669922,
+(float)0.313232, (float)0.454102, (float)0.600952,
+(float)0.347412, (float)0.571533, (float)0.874146,
+(float)0.238037, (float)0.405396, (float)0.729492,
+(float)0.223877, (float)0.412964, (float)0.822021,
+(float)0.395264, (float)0.582153, (float)0.743896,
+(float)0.247925, (float)0.485596, (float)0.720581,
+(float)0.229126, (float)0.496582, (float)0.907715,
+(float)0.260132, (float)0.566895, (float)1.012695,
+(float)0.337402, (float)0.611572, (float)0.978149,
+(float)0.267822, (float)0.447632, (float)0.769287,
+(float)0.250610, (float)0.381714, (float)0.530029,
+(float)0.430054, (float)0.805054, (float)1.221924,
+(float)0.382568, (float)0.544067, (float)0.701660,
+(float)0.383545, (float)0.710327, (float)1.149170,
+(float)0.271362, (float)0.529053, (float)0.775513,
+(float)0.246826, (float)0.393555, (float)0.588623,
+(float)0.266846, (float)0.422119, (float)0.676758,
+(float)0.311523, (float)0.580688, (float)0.838623,
+(float)1.331177, (float)1.576782, (float)1.779541,
+(float)1.160034, (float)1.401978, (float)1.768188,
+(float)1.161865, (float)1.525146, (float)1.715332,
+(float)0.759521, (float)0.913940, (float)1.119873,
+(float)0.947144, (float)1.121338, (float)1.282471,
+(float)1.015015, (float)1.557007, (float)1.804932,
+(float)1.172974, (float)1.402100, (float)1.692627,
+(float)1.087524, (float)1.474243, (float)1.665405,
+(float)0.899536, (float)1.105225, (float)1.406250,
+(float)1.148438, (float)1.484741, (float)1.796265,
+(float)0.785645, (float)1.209839, (float)1.567749,
+(float)0.867798, (float)1.166504, (float)1.450684,
+(float)0.922485, (float)1.229858, (float)1.420898,
+(float)0.791260, (float)1.123291, (float)1.409546,
+(float)0.788940, (float)0.966064, (float)1.340332,
+(float)1.051147, (float)1.272827, (float)1.556641,
+(float)0.866821, (float)1.181152, (float)1.538818,
+(float)0.906738, (float)1.373535, (float)1.607910,
+(float)1.244751, (float)1.581421, (float)1.933838,
+(float)0.913940, (float)1.337280, (float)1.539673,
+(float)0.680542, (float)0.959229, (float)1.662720,
+(float)0.887207, (float)1.430542, (float)1.800781,
+(float)0.912598, (float)1.433594, (float)1.683960,
+(float)0.860474, (float)1.060303, (float)1.455322,
+(float)1.005127, (float)1.381104, (float)1.706909,
+(float)0.800781, (float)1.363892, (float)1.829102,
+(float)0.781860, (float)1.124390, (float)1.505981,
+(float)1.003662, (float)1.471436, (float)1.684692,
+(float)0.981323, (float)1.309570, (float)1.618042,
+(float)1.228760, (float)1.554321, (float)1.756470,
+(float)0.734375, (float)0.895752, (float)1.225586,
+(float)0.841797, (float)1.055664, (float)1.249268,
+(float)0.920166, (float)1.119385, (float)1.486206,
+(float)0.894409, (float)1.539063, (float)1.828979,
+
+
+(float)1.283691, (float)1.543335, (float)1.858276,
+(float)0.676025, (float)0.933105, (float)1.490845,
+(float)0.821289, (float)1.491821, (float)1.739868,
+(float)0.923218, (float)1.144653, (float)1.580566,
+(float)1.057251, (float)1.345581, (float)1.635864,
+(float)0.888672, (float)1.074951, (float)1.353149,
+(float)0.942749, (float)1.195435, (float)1.505493,
+(float)1.492310, (float)1.788086, (float)2.039673,
+(float)1.070313, (float)1.634399, (float)1.860962,
+(float)1.253296, (float)1.488892, (float)1.686035,
+(float)0.647095, (float)0.864014, (float)1.401855,
+(float)0.866699, (float)1.254883, (float)1.453369,
+(float)1.063965, (float)1.532593, (float)1.731323,
+(float)1.167847, (float)1.521484, (float)1.884033,
+(float)0.956055, (float)1.502075, (float)1.745605,
+(float)0.928711, (float)1.288574, (float)1.479614,
+(float)1.088013, (float)1.380737, (float)1.570801,
+(float)0.905029, (float)1.186768, (float)1.371948,
+(float)1.057861, (float)1.421021, (float)1.617432,
+(float)1.108276, (float)1.312500, (float)1.501465,
+(float)0.979492, (float)1.416992, (float)1.624268,
+(float)1.276001, (float)1.661011, (float)2.007935,
+(float)0.993042, (float)1.168579, (float)1.331665,
+(float)0.778198, (float)0.944946, (float)1.235962,
+(float)1.223755, (float)1.491333, (float)1.815674,
+(float)0.852661, (float)1.350464, (float)1.722290,
+(float)1.134766, (float)1.593140, (float)1.787354,
+(float)1.051392, (float)1.339722, (float)1.531006,
+(float)0.803589, (float)1.271240, (float)1.652100,
+(float)0.755737, (float)1.143555, (float)1.639404,
+(float)0.700928, (float)0.837280, (float)1.130371,
+(float)0.942749, (float)1.197876, (float)1.669800,
+(float)0.993286, (float)1.378296, (float)1.566528,
+(float)0.801025, (float)1.095337, (float)1.298950,
+(float)0.739990, (float)1.032959, (float)1.383667,
+(float)0.845703, (float)1.072266, (float)1.543823,
+(float)0.915649, (float)1.072266, (float)1.224487,
+(float)1.021973, (float)1.226196, (float)1.481323,
+(float)0.999878, (float)1.204102, (float)1.555908,
+(float)0.722290, (float)0.913940, (float)1.340210,
+(float)0.673340, (float)0.835938, (float)1.259521,
+(float)0.832397, (float)1.208374, (float)1.394165,
+(float)0.962158, (float)1.576172, (float)1.912842,
+(float)1.166748, (float)1.370850, (float)1.556763,
+(float)0.946289, (float)1.138550, (float)1.400391,
+(float)1.035034, (float)1.218262, (float)1.386475,
+(float)1.393799, (float)1.717773, (float)2.000244,
+(float)0.972656, (float)1.260986, (float)1.760620,
+(float)1.028198, (float)1.288452, (float)1.484619,
+(float)0.773560, (float)1.258057, (float)1.756714,
+(float)1.080322, (float)1.328003, (float)1.742676,
+(float)0.823975, (float)1.450806, (float)1.917725,
+(float)0.859009, (float)1.016602, (float)1.191895,
+(float)0.843994, (float)1.131104, (float)1.645020,
+
+
+(float)1.189697, (float)1.702759, (float)1.894409,
+(float)1.346680, (float)1.763184, (float)2.066040,
+(float)0.980469, (float)1.253784, (float)1.441650,
+(float)1.338135, (float)1.641968, (float)1.932739,
+(float)1.223267, (float)1.424194, (float)1.626465,
+(float)0.765747, (float)1.004150, (float)1.579102,
+(float)1.042847, (float)1.269165, (float)1.647461,
+(float)0.968750, (float)1.257568, (float)1.555786,
+(float)0.826294, (float)0.993408, (float)1.275146,
+(float)0.742310, (float)0.950439, (float)1.430542,
+(float)1.054321, (float)1.439819, (float)1.828003,
+(float)1.072998, (float)1.261719, (float)1.441895,
+(float)0.859375, (float)1.036377, (float)1.314819,
+(float)0.895752, (float)1.267212, (float)1.605591,
+(float)0.805420, (float)0.962891, (float)1.142334,
+(float)0.795654, (float)1.005493, (float)1.468506,
+(float)1.105347, (float)1.313843, (float)1.584839,
+(float)0.792236, (float)1.221802, (float)1.465698,
+(float)1.170532, (float)1.467651, (float)1.664063,
+(float)0.838257, (float)1.153198, (float)1.342163,
+(float)0.968018, (float)1.198242, (float)1.391235,
+(float)1.250122, (float)1.623535, (float)1.823608,
+(float)0.711670, (float)1.058350, (float)1.512085,
+(float)1.204834, (float)1.454468, (float)1.739136,
+(float)1.137451, (float)1.421753, (float)1.620117,
+(float)0.820435, (float)1.322754, (float)1.578247,
+(float)0.798706, (float)1.005005, (float)1.213867,
+(float)0.980713, (float)1.324951, (float)1.512939,
+(float)1.112305, (float)1.438843, (float)1.735596,
+(float)1.135498, (float)1.356689, (float)1.635742,
+(float)1.101318, (float)1.387451, (float)1.686523,
+(float)0.849854, (float)1.276978, (float)1.523438,
+(float)1.377930, (float)1.627563, (float)1.858154,
+(float)0.884888, (float)1.095459, (float)1.287476,
+(float)1.289795, (float)1.505859, (float)1.756592,
+(float)0.817505, (float)1.384155, (float)1.650513,
+(float)1.446655, (float)1.702148, (float)1.931885,
+(float)0.835815, (float)1.023071, (float)1.385376,
+(float)0.916626, (float)1.139038, (float)1.335327,
+(float)0.980103, (float)1.174072, (float)1.453735,
+(float)1.705688, (float)2.153809, (float)2.398315, (float)2.743408,
+(float)1.797119, (float)2.016846, (float)2.445679, (float)2.701904,
+(float)1.990356, (float)2.219116, (float)2.576416, (float)2.813477,
+(float)1.849365, (float)2.190918, (float)2.611572, (float)2.835083,
+(float)1.657959, (float)1.854370, (float)2.159058, (float)2.726196,
+(float)1.437744, (float)1.897705, (float)2.253174, (float)2.655396,
+(float)2.028687, (float)2.247314, (float)2.542358, (float)2.875854,
+(float)1.736938, (float)1.922119, (float)2.185913, (float)2.743408,
+(float)1.521606, (float)1.870972, (float)2.526855, (float)2.786987,
+(float)1.841431, (float)2.050659, (float)2.463623, (float)2.857666,
+(float)1.590088, (float)2.067261, (float)2.427979, (float)2.794434,
+(float)1.746826, (float)2.057373, (float)2.320190, (float)2.800781,
+(float)1.734619, (float)1.940552, (float)2.306030, (float)2.826416,
+(float)1.786255, (float)2.204468, (float)2.457520, (float)2.795288,
+
+
+(float)1.861084, (float)2.170532, (float)2.414551, (float)2.763672,
+(float)2.001465, (float)2.307617, (float)2.552734, (float)2.811890,
+(float)1.784424, (float)2.124146, (float)2.381592, (float)2.645508,
+(float)1.888794, (float)2.135864, (float)2.418579, (float)2.861206,
+(float)2.301147, (float)2.531250, (float)2.724976, (float)2.913086,
+(float)1.837769, (float)2.051270, (float)2.261963, (float)2.553223,
+(float)2.012939, (float)2.221191, (float)2.440186, (float)2.678101,
+(float)1.429565, (float)1.858276, (float)2.582275, (float)2.845703,
+(float)1.622803, (float)1.897705, (float)2.367310, (float)2.621094,
+(float)1.581543, (float)1.960449, (float)2.515869, (float)2.736450,
+(float)1.419434, (float)1.933960, (float)2.394653, (float)2.746704,
+(float)1.721924, (float)2.059570, (float)2.421753, (float)2.769653,
+(float)1.911011, (float)2.220703, (float)2.461060, (float)2.740723,
+(float)1.581177, (float)1.860840, (float)2.516968, (float)2.874634,
+(float)1.870361, (float)2.098755, (float)2.432373, (float)2.656494,
+(float)2.059692, (float)2.279785, (float)2.495605, (float)2.729370,
+(float)1.815674, (float)2.181519, (float)2.451538, (float)2.680542,
+(float)1.407959, (float)1.768311, (float)2.343018, (float)2.668091,
+(float)2.168701, (float)2.394653, (float)2.604736, (float)2.829346,
+(float)1.636230, (float)1.865723, (float)2.329102, (float)2.824219,
+(float)1.878906, (float)2.139526, (float)2.376709, (float)2.679810,
+(float)1.765381, (float)1.971802, (float)2.195435, (float)2.586914,
+(float)2.164795, (float)2.410889, (float)2.673706, (float)2.903198,
+(float)2.071899, (float)2.331055, (float)2.645874, (float)2.907104,
+(float)2.026001, (float)2.311523, (float)2.594849, (float)2.863892,
+(float)1.948975, (float)2.180786, (float)2.514893, (float)2.797852,
+(float)1.881836, (float)2.130859, (float)2.478149, (float)2.804199,
+(float)2.238159, (float)2.452759, (float)2.652832, (float)2.868286,
+(float)1.897949, (float)2.101685, (float)2.524292, (float)2.880127,
+(float)1.856445, (float)2.074585, (float)2.541016, (float)2.791748,
+(float)1.695557, (float)2.199097, (float)2.506226, (float)2.742676,
+(float)1.612671, (float)1.877075, (float)2.435425, (float)2.732910,
+(float)1.568848, (float)1.786499, (float)2.194580, (float)2.768555,
+(float)1.953369, (float)2.164551, (float)2.486938, (float)2.874023,
+(float)1.388306, (float)1.725342, (float)2.384521, (float)2.771851,
+(float)2.115356, (float)2.337769, (float)2.592896, (float)2.864014,
+(float)1.905762, (float)2.111328, (float)2.363525, (float)2.789307,
+(float)1.882568, (float)2.332031, (float)2.598267, (float)2.827637,
+(float)1.683594, (float)2.088745, (float)2.361938, (float)2.608643,
+(float)1.874023, (float)2.182129, (float)2.536133, (float)2.766968,
+(float)1.861938, (float)2.070435, (float)2.309692, (float)2.700562,
+(float)1.722168, (float)2.107422, (float)2.477295, (float)2.837646,
+(float)1.926880, (float)2.184692, (float)2.442627, (float)2.663818,
+(float)2.123901, (float)2.337280, (float)2.553101, (float)2.777466,
+(float)1.588135, (float)1.911499, (float)2.212769, (float)2.543945,
+(float)2.053955, (float)2.370850, (float)2.712158, (float)2.939941,
+(float)2.210449, (float)2.519653, (float)2.770386, (float)2.958618,
+(float)2.199463, (float)2.474731, (float)2.718262, (float)2.919922,
+(float)1.960083, (float)2.175415, (float)2.608032, (float)2.888794,
+(float)1.953735, (float)2.185181, (float)2.428223, (float)2.809570,
+(float)1.615234, (float)2.036499, (float)2.576538, (float)2.834595,
+(float)1.621094, (float)2.028198, (float)2.431030, (float)2.664673,
+(float)1.824951, (float)2.267456, (float)2.514526, (float)2.747925,
+(float)1.994263, (float)2.229126, (float)2.475220, (float)2.833984,
+
+
+(float)1.746338, (float)2.011353, (float)2.588257, (float)2.826904,
+(float)1.562866, (float)2.135986, (float)2.471680, (float)2.687256,
+(float)1.748901, (float)2.083496, (float)2.460938, (float)2.686279,
+(float)1.758057, (float)2.131470, (float)2.636597, (float)2.891602,
+(float)2.071289, (float)2.299072, (float)2.550781, (float)2.814331,
+(float)1.839600, (float)2.094360, (float)2.496460, (float)2.723999,
+(float)1.882202, (float)2.088257, (float)2.636841, (float)2.923096,
+(float)1.957886, (float)2.153198, (float)2.384399, (float)2.615234,
+(float)1.992920, (float)2.351196, (float)2.654419, (float)2.889771,
+(float)2.012817, (float)2.262451, (float)2.643799, (float)2.903076,
+(float)2.025635, (float)2.254761, (float)2.508423, (float)2.784058,
+(float)2.316040, (float)2.589355, (float)2.794189, (float)2.963623,
+(float)1.741211, (float)2.279541, (float)2.578491, (float)2.816284,
+(float)1.845337, (float)2.055786, (float)2.348511, (float)2.822021,
+(float)1.679932, (float)1.926514, (float)2.499756, (float)2.835693,
+(float)1.722534, (float)1.946899, (float)2.448486, (float)2.728760,
+(float)1.829834, (float)2.043213, (float)2.580444, (float)2.867676,
+(float)1.676636, (float)2.071655, (float)2.322510, (float)2.704834,
+(float)1.791504, (float)2.113525, (float)2.469727, (float)2.784058,
+(float)1.977051, (float)2.215088, (float)2.497437, (float)2.726929,
+(float)1.800171, (float)2.106689, (float)2.357788, (float)2.738892,
+(float)1.827759, (float)2.170166, (float)2.525879, (float)2.852417,
+(float)1.918335, (float)2.132813, (float)2.488403, (float)2.728149,
+(float)1.916748, (float)2.225098, (float)2.542603, (float)2.857666,
+(float)1.761230, (float)1.976074, (float)2.507446, (float)2.884521,
+(float)2.053711, (float)2.367432, (float)2.608032, (float)2.837646,
+(float)1.595337, (float)2.000977, (float)2.307129, (float)2.578247,
+(float)1.470581, (float)2.031250, (float)2.375854, (float)2.647583,
+(float)1.801392, (float)2.128052, (float)2.399780, (float)2.822876,
+(float)1.853638, (float)2.066650, (float)2.429199, (float)2.751465,
+(float)1.956299, (float)2.163696, (float)2.394775, (float)2.734253,
+(float)1.963623, (float)2.275757, (float)2.585327, (float)2.865234,
+(float)1.887451, (float)2.105469, (float)2.331787, (float)2.587402,
+(float)2.120117, (float)2.443359, (float)2.733887, (float)2.941406,
+(float)1.506348, (float)1.766968, (float)2.400513, (float)2.851807,
+(float)1.664551, (float)1.981079, (float)2.375732, (float)2.774414,
+(float)1.720703, (float)1.978882, (float)2.391479, (float)2.640991,
+(float)1.483398, (float)1.814819, (float)2.434448, (float)2.722290,
+(float)1.769043, (float)2.136597, (float)2.563721, (float)2.774414,
+(float)1.810791, (float)2.049316, (float)2.373901, (float)2.613647,
+(float)1.788330, (float)2.005981, (float)2.359131, (float)2.723145,
+(float)1.785156, (float)1.993164, (float)2.399780, (float)2.832520,
+(float)1.695313, (float)2.022949, (float)2.522583, (float)2.745117,
+(float)1.584106, (float)1.965576, (float)2.299927, (float)2.715576,
+(float)1.894897, (float)2.249878, (float)2.655884, (float)2.897705,
+(float)1.720581, (float)1.995728, (float)2.299438, (float)2.557007,
+(float)1.619385, (float)2.173950, (float)2.574219, (float)2.787964,
+(float)1.883179, (float)2.220459, (float)2.474365, (float)2.825073,
+(float)1.447632, (float)2.045044, (float)2.555542, (float)2.744873,
+(float)1.502686, (float)2.156616, (float)2.653320, (float)2.846558,
+(float)1.711548, (float)1.944092, (float)2.282959, (float)2.685791,
+(float)1.499756, (float)1.867554, (float)2.341064, (float)2.578857,
+(float)1.916870, (float)2.135132, (float)2.568237, (float)2.826050,
+(float)1.498047, (float)1.711182, (float)2.223267, (float)2.755127,
+
+
+(float)1.808716, (float)1.997559, (float)2.256470, (float)2.758545,
+(float)2.088501, (float)2.402710, (float)2.667358, (float)2.890259,
+(float)1.545044, (float)1.819214, (float)2.324097, (float)2.692993,
+(float)1.796021, (float)2.012573, (float)2.505737, (float)2.784912,
+(float)1.786499, (float)2.041748, (float)2.290405, (float)2.650757,
+(float)1.938232, (float)2.264404, (float)2.529053, (float)2.796143
+};
+
+
diff --git a/trunk/codecs/ilbc/constants.h b/trunk/codecs/ilbc/constants.h
new file mode 100644
index 000000000..e27754d8f
--- /dev/null
+++ b/trunk/codecs/ilbc/constants.h
@@ -0,0 +1,74 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ constants.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+
+
+******************************************************************/
+
+#ifndef __iLBC_CONSTANTS_H
+#define __iLBC_CONSTANTS_H
+
+#include "iLBC_define.h"
+
+
+/* ULP bit allocation */
+
+extern const iLBC_ULP_Inst_t ULP_20msTbl;
+extern const iLBC_ULP_Inst_t ULP_30msTbl;
+
+/* high pass filters */
+
+extern float hpi_zero_coefsTbl[];
+extern float hpi_pole_coefsTbl[];
+extern float hpo_zero_coefsTbl[];
+extern float hpo_pole_coefsTbl[];
+
+/* low pass filters */
+extern float lpFilt_coefsTbl[];
+
+/* LPC analysis and quantization */
+
+extern float lpc_winTbl[];
+extern float lpc_asymwinTbl[];
+extern float lpc_lagwinTbl[];
+extern float lsfCbTbl[];
+extern float lsfmeanTbl[];
+extern int dim_lsfCbTbl[];
+extern int size_lsfCbTbl[];
+extern float lsf_weightTbl_30ms[];
+extern float lsf_weightTbl_20ms[];
+
+/* state quantization tables */
+
+extern float state_sq3Tbl[];
+extern float state_frgqTbl[];
+
+/* gain quantization tables */
+
+extern float gain_sq3Tbl[];
+extern float gain_sq4Tbl[];
+extern float gain_sq5Tbl[];
+
+/* adaptive codebook definitions */
+
+extern int search_rangeTbl[5][CB_NSTAGES];
+extern int memLfTbl[];
+extern int stMemLTbl;
+extern float cbfiltersTbl[CB_FILTERLEN];
+
+
+
+/* enhancer definitions */
+
+extern float polyphaserTbl[];
+extern float enh_plocsTbl[];
+
+#endif
+
diff --git a/trunk/codecs/ilbc/createCB.c b/trunk/codecs/ilbc/createCB.c
new file mode 100644
index 000000000..a7efd9380
--- /dev/null
+++ b/trunk/codecs/ilbc/createCB.c
@@ -0,0 +1,216 @@
+
+
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ createCB.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include "iLBC_define.h"
+#include "createCB.h"
+#include "constants.h"
+#include <string.h>
+#include <math.h>
+
+/*----------------------------------------------------------------*
+ * Construct an additional codebook vector by filtering the
+ * initial codebook buffer. This vector is then used to expand
+ * the codebook with an additional section.
+ *---------------------------------------------------------------*/
+
+void filteredCBvecs(
+ float *cbvectors, /* (o) Codebook vectors for the
+ higher section */
+ float *mem, /* (i) Buffer to create codebook
+ vector from */
+ int lMem /* (i) Length of buffer */
+){
+ int j, k;
+ float *pp, *pp1;
+ float tempbuff2[CB_MEML+CB_FILTERLEN];
+ float *pos;
+
+ memset(tempbuff2, 0, (CB_HALFFILTERLEN-1)*sizeof(float));
+ memcpy(&tempbuff2[CB_HALFFILTERLEN-1], mem, lMem*sizeof(float));
+ memset(&tempbuff2[lMem+CB_HALFFILTERLEN-1], 0,
+ (CB_HALFFILTERLEN+1)*sizeof(float));
+
+ /* Create codebook vector for higher section by filtering */
+
+ /* do filtering */
+ pos=cbvectors;
+ memset(pos, 0, lMem*sizeof(float));
+ for (k=0; k<lMem; k++) {
+ pp=&tempbuff2[k];
+ pp1=&cbfiltersTbl[CB_FILTERLEN-1];
+ for (j=0;j<CB_FILTERLEN;j++) {
+ (*pos)+=(*pp++)*(*pp1--);
+ }
+ pos++;
+ }
+}
+
+
+
+/*----------------------------------------------------------------*
+ * Search the augmented part of the codebook to find the best
+ * measure.
+ *----------------------------------------------------------------*/
+
+void searchAugmentedCB(
+ int low, /* (i) Start index for the search */
+ int high, /* (i) End index for the search */
+ int stage, /* (i) Current stage */
+ int startIndex, /* (i) Codebook index for the first
+ aug vector */
+ float *target, /* (i) Target vector for encoding */
+ float *buffer, /* (i) Pointer to the end of the buffer for
+ augmented codebook construction */
+ float *max_measure, /* (i/o) Currently maximum measure */
+ int *best_index,/* (o) Currently the best index */
+ float *gain, /* (o) Currently the best gain */
+ float *energy, /* (o) Energy of augmented codebook
+ vectors */
+ float *invenergy/* (o) Inv energy of augmented codebook
+ vectors */
+) {
+ int icount, ilow, j, tmpIndex;
+ float *pp, *ppo, *ppi, *ppe, crossDot, alfa;
+ float weighted, measure, nrjRecursive;
+ float ftmp;
+
+ /* Compute the energy for the first (low-5)
+ noninterpolated samples */
+ nrjRecursive = (float) 0.0;
+ pp = buffer - low + 1;
+ for (j=0; j<(low-5); j++) {
+ nrjRecursive += ( (*pp)*(*pp) );
+ pp++;
+ }
+ ppe = buffer - low;
+
+
+ for (icount=low; icount<=high; icount++) {
+
+ /* Index of the codebook vector used for retrieving
+ energy values */
+ tmpIndex = startIndex+icount-20;
+
+ ilow = icount-4;
+
+ /* Update the energy recursively to save complexity */
+ nrjRecursive = nrjRecursive + (*ppe)*(*ppe);
+ ppe--;
+ energy[tmpIndex] = nrjRecursive;
+
+ /* Compute cross dot product for the first (low-5)
+ samples */
+ crossDot = (float) 0.0;
+
+
+ pp = buffer-icount;
+ for (j=0; j<ilow; j++) {
+ crossDot += target[j]*(*pp++);
+ }
+
+ /* interpolation */
+ alfa = (float) 0.2;
+ ppo = buffer-4;
+ ppi = buffer-icount-4;
+ for (j=ilow; j<icount; j++) {
+ weighted = ((float)1.0-alfa)*(*ppo)+alfa*(*ppi);
+ ppo++;
+ ppi++;
+ energy[tmpIndex] += weighted*weighted;
+ crossDot += target[j]*weighted;
+ alfa += (float)0.2;
+ }
+
+ /* Compute energy and cross dot product for the
+ remaining samples */
+ pp = buffer - icount;
+ for (j=icount; j<SUBL; j++) {
+ energy[tmpIndex] += (*pp)*(*pp);
+ crossDot += target[j]*(*pp++);
+ }
+
+ if (energy[tmpIndex]>0.0) {
+ invenergy[tmpIndex]=(float)1.0/(energy[tmpIndex]+EPS);
+ } else {
+ invenergy[tmpIndex] = (float) 0.0;
+ }
+
+ if (stage==0) {
+ measure = (float)-10000000.0;
+
+ if (crossDot > 0.0) {
+ measure = crossDot*crossDot*invenergy[tmpIndex];
+ }
+ }
+ else {
+ measure = crossDot*crossDot*invenergy[tmpIndex];
+ }
+
+ /* check if measure is better */
+ ftmp = crossDot*invenergy[tmpIndex];
+
+ if ((measure>*max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
+ *best_index = tmpIndex;
+ *max_measure = measure;
+ *gain = ftmp;
+ }
+ }
+}
+
+
+
+
+/*----------------------------------------------------------------*
+ * Recreate a specific codebook vector from the augmented part.
+ *
+ *----------------------------------------------------------------*/
+
+void createAugmentedVec(
+ int index, /* (i) Index for the augmented vector
+ to be created */
+ float *buffer, /* (i) Pointer to the end of the buffer for
+ augmented codebook construction */
+ float *cbVec/* (o) The construced codebook vector */
+) {
+ int ilow, j;
+ float *pp, *ppo, *ppi, alfa, alfa1, weighted;
+
+ ilow = index-5;
+
+ /* copy the first noninterpolated part */
+
+ pp = buffer-index;
+ memcpy(cbVec,pp,sizeof(float)*index);
+
+ /* interpolation */
+
+ alfa1 = (float)0.2;
+ alfa = 0.0;
+ ppo = buffer-5;
+ ppi = buffer-index-5;
+ for (j=ilow; j<index; j++) {
+ weighted = ((float)1.0-alfa)*(*ppo)+alfa*(*ppi);
+ ppo++;
+ ppi++;
+ cbVec[j] = weighted;
+ alfa += alfa1;
+ }
+
+ /* copy the second noninterpolated part */
+
+ pp = buffer - index;
+ memcpy(cbVec+index,pp,sizeof(float)*(SUBL-index));
+}
+
+
diff --git a/trunk/codecs/ilbc/createCB.h b/trunk/codecs/ilbc/createCB.h
new file mode 100644
index 000000000..10150ee07
--- /dev/null
+++ b/trunk/codecs/ilbc/createCB.h
@@ -0,0 +1,56 @@
+
+/******************************************************************
+
+
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ createCB.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_CREATECB_H
+#define __iLBC_CREATECB_H
+
+void filteredCBvecs(
+ float *cbvectors, /* (o) Codebook vector for the
+ higher section */
+ float *mem, /* (i) Buffer to create codebook
+ vectors from */
+ int lMem /* (i) Length of buffer */
+);
+
+void searchAugmentedCB(
+ int low, /* (i) Start index for the search */
+ int high, /* (i) End index for the search */
+ int stage, /* (i) Current stage */
+ int startIndex, /* (i) CB index for the first
+ augmented vector */
+ float *target, /* (i) Target vector for encoding */
+ float *buffer, /* (i) Pointer to the end of the
+ buffer for augmented codebook
+ construction */
+ float *max_measure, /* (i/o) Currently maximum measure */
+ int *best_index,/* (o) Currently the best index */
+ float *gain, /* (o) Currently the best gain */
+ float *energy, /* (o) Energy of augmented
+ codebook vectors */
+ float *invenergy/* (o) Inv energy of aug codebook
+ vectors */
+);
+
+void createAugmentedVec(
+ int index, /* (i) Index for the aug vector
+ to be created */
+ float *buffer, /* (i) Pointer to the end of the
+ buffer for augmented codebook
+ construction */
+ float *cbVec /* (o) The construced codebook vector */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/doCPLC.c b/trunk/codecs/ilbc/doCPLC.c
new file mode 100644
index 000000000..bb4b56c10
--- /dev/null
+++ b/trunk/codecs/ilbc/doCPLC.c
@@ -0,0 +1,259 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ doCPLC.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "iLBC_define.h"
+#include "doCPLC.h"
+
+/*----------------------------------------------------------------*
+ * Compute cross correlation and pitch gain for pitch prediction
+ * of last subframe at given lag.
+ *---------------------------------------------------------------*/
+
+static void compCorr(
+ float *cc, /* (o) cross correlation coefficient */
+ float *gc, /* (o) gain */
+ float *pm,
+ float *buffer, /* (i) signal buffer */
+ int lag, /* (i) pitch lag */
+ int bLen, /* (i) length of buffer */
+ int sRange /* (i) correlation search length */
+){
+
+
+ int i;
+ float ftmp1, ftmp2, ftmp3;
+
+ /* Guard against getting outside buffer */
+ if ((bLen-sRange-lag)<0) {
+ sRange=bLen-lag;
+ }
+
+ ftmp1 = 0.0;
+ ftmp2 = 0.0;
+ ftmp3 = 0.0;
+ for (i=0; i<sRange; i++) {
+ ftmp1 += buffer[bLen-sRange+i] *
+ buffer[bLen-sRange+i-lag];
+ ftmp2 += buffer[bLen-sRange+i-lag] *
+ buffer[bLen-sRange+i-lag];
+ ftmp3 += buffer[bLen-sRange+i] *
+ buffer[bLen-sRange+i];
+ }
+
+ if (ftmp2 > 0.0) {
+ *cc = ftmp1*ftmp1/ftmp2;
+ *gc = (float)fabs(ftmp1/ftmp2);
+ *pm=(float)fabs(ftmp1)/
+ ((float)sqrt(ftmp2)*(float)sqrt(ftmp3));
+ }
+ else {
+ *cc = 0.0;
+ *gc = 0.0;
+ *pm=0.0;
+ }
+}
+
+/*----------------------------------------------------------------*
+ * Packet loss concealment routine. Conceals a residual signal
+ * and LP parameters. If no packet loss, update state.
+ *---------------------------------------------------------------*/
+
+void doThePLC(
+ float *PLCresidual, /* (o) concealed residual */
+ float *PLClpc, /* (o) concealed LP parameters */
+ int PLI, /* (i) packet loss indicator
+ 0 - no PL, 1 = PL */
+ float *decresidual, /* (i) decoded residual */
+ float *lpc, /* (i) decoded LPC (only used for no PL) */
+ int inlag, /* (i) pitch lag */
+ iLBC_Dec_Inst_t *iLBCdec_inst
+ /* (i/o) decoder instance */
+){
+ int lag=20, randlag;
+ float gain, maxcc;
+ float use_gain;
+ float gain_comp, maxcc_comp, per, max_per;
+ int i, pick, use_lag;
+
+
+ float ftmp, randvec[BLOCKL_MAX], pitchfact, energy;
+
+ /* Packet Loss */
+
+ if (PLI == 1) {
+
+ iLBCdec_inst->consPLICount += 1;
+
+ /* if previous frame not lost,
+ determine pitch pred. gain */
+
+ if (iLBCdec_inst->prevPLI != 1) {
+
+ /* Search around the previous lag to find the
+ best pitch period */
+
+ lag=inlag-3;
+ compCorr(&maxcc, &gain, &max_per,
+ iLBCdec_inst->prevResidual,
+ lag, iLBCdec_inst->blockl, 60);
+ for (i=inlag-2;i<=inlag+3;i++) {
+ compCorr(&maxcc_comp, &gain_comp, &per,
+ iLBCdec_inst->prevResidual,
+ i, iLBCdec_inst->blockl, 60);
+
+ if (maxcc_comp>maxcc) {
+ maxcc=maxcc_comp;
+ gain=gain_comp;
+ lag=i;
+ max_per=per;
+ }
+ }
+
+ }
+
+ /* previous frame lost, use recorded lag and periodicity */
+
+ else {
+ lag=iLBCdec_inst->prevLag;
+ max_per=iLBCdec_inst->per;
+ }
+
+ /* downscaling */
+
+ use_gain=1.0;
+ if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>320)
+ use_gain=(float)0.9;
+ else if (iLBCdec_inst->consPLICount*
+ iLBCdec_inst->blockl>2*320)
+ use_gain=(float)0.7;
+ else if (iLBCdec_inst->consPLICount*
+ iLBCdec_inst->blockl>3*320)
+ use_gain=(float)0.5;
+ else if (iLBCdec_inst->consPLICount*
+
+
+ iLBCdec_inst->blockl>4*320)
+ use_gain=(float)0.0;
+
+ /* mix noise and pitch repeatition */
+ ftmp=(float)sqrt(max_per);
+ if (ftmp>(float)0.7)
+ pitchfact=(float)1.0;
+ else if (ftmp>(float)0.4)
+ pitchfact=(ftmp-(float)0.4)/((float)0.7-(float)0.4);
+ else
+ pitchfact=0.0;
+
+
+ /* avoid repetition of same pitch cycle */
+ use_lag=lag;
+ if (lag<80) {
+ use_lag=2*lag;
+ }
+
+ /* compute concealed residual */
+
+ energy = 0.0;
+ for (i=0; i<iLBCdec_inst->blockl; i++) {
+
+ /* noise component */
+
+ iLBCdec_inst->seed=(iLBCdec_inst->seed*69069L+1) &
+ (0x80000000L-1);
+ randlag = 50 + ((signed long) iLBCdec_inst->seed)%70;
+ pick = i - randlag;
+
+ if (pick < 0) {
+ randvec[i] =
+ iLBCdec_inst->prevResidual[
+ iLBCdec_inst->blockl+pick];
+ } else {
+ randvec[i] = randvec[pick];
+ }
+
+ /* pitch repeatition component */
+ pick = i - use_lag;
+
+ if (pick < 0) {
+ PLCresidual[i] =
+ iLBCdec_inst->prevResidual[
+ iLBCdec_inst->blockl+pick];
+ } else {
+ PLCresidual[i] = PLCresidual[pick];
+ }
+
+ /* mix random and periodicity component */
+
+ if (i<80)
+ PLCresidual[i] = use_gain*(pitchfact *
+
+
+ PLCresidual[i] +
+ ((float)1.0 - pitchfact) * randvec[i]);
+ else if (i<160)
+ PLCresidual[i] = (float)0.95*use_gain*(pitchfact *
+ PLCresidual[i] +
+ ((float)1.0 - pitchfact) * randvec[i]);
+ else
+ PLCresidual[i] = (float)0.9*use_gain*(pitchfact *
+ PLCresidual[i] +
+ ((float)1.0 - pitchfact) * randvec[i]);
+
+ energy += PLCresidual[i] * PLCresidual[i];
+ }
+
+ /* less than 30 dB, use only noise */
+
+ if (sqrt(energy/(float)iLBCdec_inst->blockl) < 30.0) {
+ gain=0.0;
+ for (i=0; i<iLBCdec_inst->blockl; i++) {
+ PLCresidual[i] = randvec[i];
+ }
+ }
+
+ /* use old LPC */
+
+ memcpy(PLClpc,iLBCdec_inst->prevLpc,
+ (LPC_FILTERORDER+1)*sizeof(float));
+
+ }
+
+ /* no packet loss, copy input */
+
+ else {
+ memcpy(PLCresidual, decresidual,
+ iLBCdec_inst->blockl*sizeof(float));
+ memcpy(PLClpc, lpc, (LPC_FILTERORDER+1)*sizeof(float));
+ iLBCdec_inst->consPLICount = 0;
+ }
+
+ /* update state */
+
+ if (PLI) {
+ iLBCdec_inst->prevLag = lag;
+ iLBCdec_inst->per=max_per;
+ }
+
+ iLBCdec_inst->prevPLI = PLI;
+ memcpy(iLBCdec_inst->prevLpc, PLClpc,
+ (LPC_FILTERORDER+1)*sizeof(float));
+ memcpy(iLBCdec_inst->prevResidual, PLCresidual,
+ iLBCdec_inst->blockl*sizeof(float));
+}
+
+
+
+
diff --git a/trunk/codecs/ilbc/doCPLC.h b/trunk/codecs/ilbc/doCPLC.h
new file mode 100644
index 000000000..a619f0def
--- /dev/null
+++ b/trunk/codecs/ilbc/doCPLC.h
@@ -0,0 +1,32 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ doCPLC.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+
+
+******************************************************************/
+
+#ifndef __iLBC_DOLPC_H
+#define __iLBC_DOLPC_H
+
+void doThePLC(
+ float *PLCresidual, /* (o) concealed residual */
+ float *PLClpc, /* (o) concealed LP parameters */
+ int PLI, /* (i) packet loss indicator
+ 0 - no PL, 1 = PL */
+ float *decresidual, /* (i) decoded residual */
+ float *lpc, /* (i) decoded LPC (only used for no PL) */
+ int inlag, /* (i) pitch lag */
+ iLBC_Dec_Inst_t *iLBCdec_inst
+ /* (i/o) decoder instance */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/enhancer.c b/trunk/codecs/ilbc/enhancer.c
new file mode 100644
index 000000000..7c7829802
--- /dev/null
+++ b/trunk/codecs/ilbc/enhancer.c
@@ -0,0 +1,665 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ enhancer.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include "iLBC_define.h"
+#include "enhancer.h"
+#include "constants.h"
+#include "filter.h"
+
+/*----------------------------------------------------------------*
+
+
+ * Find index in array such that the array element with said
+ * index is the element of said array closest to "value"
+ * according to the squared-error criterion
+ *---------------------------------------------------------------*/
+
+static void NearestNeighbor(
+ int *index, /* (o) index of array element closest
+ to value */
+ float *array, /* (i) data array */
+ float value,/* (i) value */
+ int arlength/* (i) dimension of data array */
+){
+ int i;
+ float bestcrit,crit;
+
+ crit=array[0]-value;
+ bestcrit=crit*crit;
+ *index=0;
+ for (i=1; i<arlength; i++) {
+ crit=array[i]-value;
+ crit=crit*crit;
+
+ if (crit<bestcrit) {
+ bestcrit=crit;
+ *index=i;
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * compute cross correlation between sequences
+ *---------------------------------------------------------------*/
+
+static void mycorr1(
+ float* corr, /* (o) correlation of seq1 and seq2 */
+ float* seq1, /* (i) first sequence */
+ int dim1, /* (i) dimension first seq1 */
+ const float *seq2, /* (i) second sequence */
+ int dim2 /* (i) dimension seq2 */
+){
+ int i,j;
+
+ for (i=0; i<=dim1-dim2; i++) {
+ corr[i]=0.0;
+ for (j=0; j<dim2; j++) {
+ corr[i] += seq1[i+j] * seq2[j];
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * upsample finite array assuming zeros outside bounds
+ *---------------------------------------------------------------*/
+
+
+
+static void enh_upsample(
+ float* useq1, /* (o) upsampled output sequence */
+ float* seq1,/* (i) unupsampled sequence */
+ int dim1, /* (i) dimension seq1 */
+ int hfl /* (i) polyphase filter length=2*hfl+1 */
+){
+ float *pu,*ps;
+ int i,j,k,q,filterlength,hfl2;
+ const float *polyp[ENH_UPS0]; /* pointers to
+ polyphase columns */
+ const float *pp;
+
+ /* define pointers for filter */
+
+ filterlength=2*hfl+1;
+
+ if ( filterlength > dim1 ) {
+ hfl2=(int) (dim1/2);
+ for (j=0; j<ENH_UPS0; j++) {
+ polyp[j]=polyphaserTbl+j*filterlength+hfl-hfl2;
+ }
+ hfl=hfl2;
+ filterlength=2*hfl+1;
+ }
+ else {
+ for (j=0; j<ENH_UPS0; j++) {
+ polyp[j]=polyphaserTbl+j*filterlength;
+ }
+ }
+
+ /* filtering: filter overhangs left side of sequence */
+
+ pu=useq1;
+ for (i=hfl; i<filterlength; i++) {
+ for (j=0; j<ENH_UPS0; j++) {
+ *pu=0.0;
+ pp = polyp[j];
+ ps = seq1+i;
+ for (k=0; k<=i; k++) {
+ *pu += *ps-- * *pp++;
+ }
+ pu++;
+ }
+ }
+
+ /* filtering: simple convolution=inner products */
+
+ for (i=filterlength; i<dim1; i++) {
+ for (j=0;j<ENH_UPS0; j++){
+ *pu=0.0;
+ pp = polyp[j];
+ ps = seq1+i;
+ for (k=0; k<filterlength; k++) {
+ *pu += *ps-- * *pp++;
+
+
+ }
+ pu++;
+ }
+ }
+
+ /* filtering: filter overhangs right side of sequence */
+
+ for (q=1; q<=hfl; q++) {
+ for (j=0; j<ENH_UPS0; j++) {
+ *pu=0.0;
+ pp = polyp[j]+q;
+ ps = seq1+dim1-1;
+ for (k=0; k<filterlength-q; k++) {
+ *pu += *ps-- * *pp++;
+ }
+ pu++;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------*
+ * find segment starting near idata+estSegPos that has highest
+ * correlation with idata+centerStartPos through
+ * idata+centerStartPos+ENH_BLOCKL-1 segment is found at a
+ * resolution of ENH_UPSO times the original of the original
+ * sampling rate
+ *---------------------------------------------------------------*/
+
+static void refiner(
+ float *seg, /* (o) segment array */
+ float *updStartPos, /* (o) updated start point */
+ float* idata, /* (i) original data buffer */
+ int idatal, /* (i) dimension of idata */
+ int centerStartPos, /* (i) beginning center segment */
+ float estSegPos,/* (i) estimated beginning other segment */
+ float period /* (i) estimated pitch period */
+){
+ int estSegPosRounded,searchSegStartPos,searchSegEndPos,corrdim;
+ int tloc,tloc2,i,st,en,fraction;
+ float vect[ENH_VECTL],corrVec[ENH_CORRDIM],maxv;
+ float corrVecUps[ENH_CORRDIM*ENH_UPS0];
+
+ /* defining array bounds */
+
+ estSegPosRounded=(int)(estSegPos - 0.5);
+
+ searchSegStartPos=estSegPosRounded-ENH_SLOP;
+
+ if (searchSegStartPos<0) {
+ searchSegStartPos=0;
+ }
+ searchSegEndPos=estSegPosRounded+ENH_SLOP;
+
+
+
+ if (searchSegEndPos+ENH_BLOCKL >= idatal) {
+ searchSegEndPos=idatal-ENH_BLOCKL-1;
+ }
+ corrdim=searchSegEndPos-searchSegStartPos+1;
+
+ /* compute upsampled correlation (corr33) and find
+ location of max */
+
+ mycorr1(corrVec,idata+searchSegStartPos,
+ corrdim+ENH_BLOCKL-1,idata+centerStartPos,ENH_BLOCKL);
+ enh_upsample(corrVecUps,corrVec,corrdim,ENH_FL0);
+ tloc=0; maxv=corrVecUps[0];
+ for (i=1; i<ENH_UPS0*corrdim; i++) {
+
+ if (corrVecUps[i]>maxv) {
+ tloc=i;
+ maxv=corrVecUps[i];
+ }
+ }
+
+ /* make vector can be upsampled without ever running outside
+ bounds */
+
+ *updStartPos= (float)searchSegStartPos +
+ (float)tloc/(float)ENH_UPS0+(float)1.0;
+ tloc2=(int)(tloc/ENH_UPS0);
+
+ if (tloc>tloc2*ENH_UPS0) {
+ tloc2++;
+ }
+ st=searchSegStartPos+tloc2-ENH_FL0;
+
+ if (st<0) {
+ memset(vect,0,-st*sizeof(float));
+ memcpy(&vect[-st],idata, (ENH_VECTL+st)*sizeof(float));
+ }
+ else {
+ en=st+ENH_VECTL;
+
+ if (en>idatal) {
+ memcpy(vect, &idata[st],
+ (ENH_VECTL-(en-idatal))*sizeof(float));
+ memset(&vect[ENH_VECTL-(en-idatal)], 0,
+ (en-idatal)*sizeof(float));
+ }
+ else {
+ memcpy(vect, &idata[st], ENH_VECTL*sizeof(float));
+ }
+ }
+ fraction=tloc2*ENH_UPS0-tloc;
+
+ /* compute the segment (this is actually a convolution) */
+
+ mycorr1(seg,vect,ENH_VECTL,polyphaserTbl+(2*ENH_FL0+1)*fraction,
+
+
+ 2*ENH_FL0+1);
+}
+
+/*----------------------------------------------------------------*
+ * find the smoothed output data
+ *---------------------------------------------------------------*/
+
+static void smath(
+ float *odata, /* (o) smoothed output */
+ float *sseq,/* (i) said second sequence of waveforms */
+ int hl, /* (i) 2*hl+1 is sseq dimension */
+ float alpha0/* (i) max smoothing energy fraction */
+){
+ int i,k;
+ float w00,w10,w11,A,B,C,*psseq,err,errs;
+ float surround[BLOCKL_MAX]; /* shape contributed by other than
+ current */
+ float wt[2*ENH_HL+1]; /* waveform weighting to get
+ surround shape */
+ float denom;
+
+ /* create shape of contribution from all waveforms except the
+ current one */
+
+ for (i=1; i<=2*hl+1; i++) {
+ wt[i-1] = (float)0.5*(1 - (float)cos(2*PI*i/(2*hl+2)));
+ }
+ wt[hl]=0.0; /* for clarity, not used */
+ for (i=0; i<ENH_BLOCKL; i++) {
+ surround[i]=sseq[i]*wt[0];
+ }
+ for (k=1; k<hl; k++) {
+ psseq=sseq+k*ENH_BLOCKL;
+ for(i=0;i<ENH_BLOCKL; i++) {
+ surround[i]+=psseq[i]*wt[k];
+ }
+ }
+ for (k=hl+1; k<=2*hl; k++) {
+ psseq=sseq+k*ENH_BLOCKL;
+ for(i=0;i<ENH_BLOCKL; i++) {
+ surround[i]+=psseq[i]*wt[k];
+ }
+ }
+
+ /* compute some inner products */
+
+ w00 = w10 = w11 = 0.0;
+ psseq=sseq+hl*ENH_BLOCKL; /* current block */
+ for (i=0; i<ENH_BLOCKL;i++) {
+ w00+=psseq[i]*psseq[i];
+ w11+=surround[i]*surround[i];
+ w10+=surround[i]*psseq[i];
+ }
+
+
+
+ if (fabs(w11) < 1.0) {
+ w11=1.0;
+ }
+ C = (float)sqrt( w00/w11);
+
+ /* first try enhancement without power-constraint */
+
+ errs=0.0;
+ psseq=sseq+hl*ENH_BLOCKL;
+ for (i=0; i<ENH_BLOCKL; i++) {
+ odata[i]=C*surround[i];
+ err=psseq[i]-odata[i];
+ errs+=err*err;
+ }
+
+ /* if constraint violated by first try, add constraint */
+
+ if (errs > alpha0 * w00) {
+ if ( w00 < 1) {
+ w00=1;
+ }
+ denom = (w11*w00-w10*w10)/(w00*w00);
+
+ if (denom > 0.0001) { /* eliminates numerical problems
+ for if smooth */
+ A = (float)sqrt( (alpha0- alpha0*alpha0/4)/denom);
+ B = -alpha0/2 - A * w10/w00;
+ B = B+1;
+ }
+ else { /* essentially no difference between cycles;
+ smoothing not needed */
+ A= 0.0;
+ B= 1.0;
+ }
+
+ /* create smoothed sequence */
+
+ psseq=sseq+hl*ENH_BLOCKL;
+ for (i=0; i<ENH_BLOCKL; i++) {
+ odata[i]=A*surround[i]+B*psseq[i];
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * get the pitch-synchronous sample sequence
+ *---------------------------------------------------------------*/
+
+static void getsseq(
+ float *sseq, /* (o) the pitch-synchronous sequence */
+ float *idata, /* (i) original data */
+ int idatal, /* (i) dimension of data */
+ int centerStartPos, /* (i) where current block starts */
+ float *period, /* (i) rough-pitch-period array */
+
+
+ float *plocs, /* (i) where periods of period array
+ are taken */
+ int periodl, /* (i) dimension period array */
+ int hl /* (i) 2*hl+1 is the number of sequences */
+){
+ int i,centerEndPos,q;
+ float blockStartPos[2*ENH_HL+1];
+ int lagBlock[2*ENH_HL+1];
+ float plocs2[ENH_PLOCSL];
+ float *psseq;
+
+ centerEndPos=centerStartPos+ENH_BLOCKL-1;
+
+ /* present */
+
+ NearestNeighbor(lagBlock+hl,plocs,
+ (float)0.5*(centerStartPos+centerEndPos),periodl);
+
+ blockStartPos[hl]=(float)centerStartPos;
+ psseq=sseq+ENH_BLOCKL*hl;
+ memcpy(psseq, idata+centerStartPos, ENH_BLOCKL*sizeof(float));
+
+ /* past */
+
+ for (q=hl-1; q>=0; q--) {
+ blockStartPos[q]=blockStartPos[q+1]-period[lagBlock[q+1]];
+ NearestNeighbor(lagBlock+q,plocs,
+ blockStartPos[q]+
+ ENH_BLOCKL_HALF-period[lagBlock[q+1]], periodl);
+
+
+ if (blockStartPos[q]-ENH_OVERHANG>=0) {
+ refiner(sseq+q*ENH_BLOCKL, blockStartPos+q, idata,
+ idatal, centerStartPos, blockStartPos[q],
+ period[lagBlock[q+1]]);
+ } else {
+ psseq=sseq+q*ENH_BLOCKL;
+ memset(psseq, 0, ENH_BLOCKL*sizeof(float));
+ }
+ }
+
+ /* future */
+
+ for (i=0; i<periodl; i++) {
+ plocs2[i]=plocs[i]-period[i];
+ }
+ for (q=hl+1; q<=2*hl; q++) {
+ NearestNeighbor(lagBlock+q,plocs2,
+ blockStartPos[q-1]+ENH_BLOCKL_HALF,periodl);
+
+ blockStartPos[q]=blockStartPos[q-1]+period[lagBlock[q]];
+ if (blockStartPos[q]+ENH_BLOCKL+ENH_OVERHANG<idatal) {
+ refiner(sseq+ENH_BLOCKL*q, blockStartPos+q, idata,
+ idatal, centerStartPos, blockStartPos[q],
+
+
+ period[lagBlock[q]]);
+ }
+ else {
+ psseq=sseq+q*ENH_BLOCKL;
+ memset(psseq, 0, ENH_BLOCKL*sizeof(float));
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * perform enhancement on idata+centerStartPos through
+ * idata+centerStartPos+ENH_BLOCKL-1
+ *---------------------------------------------------------------*/
+
+static void enhancer(
+ float *odata, /* (o) smoothed block, dimension blockl */
+ float *idata, /* (i) data buffer used for enhancing */
+ int idatal, /* (i) dimension idata */
+ int centerStartPos, /* (i) first sample current block
+ within idata */
+ float alpha0, /* (i) max correction-energy-fraction
+ (in [0,1]) */
+ float *period, /* (i) pitch period array */
+ float *plocs, /* (i) locations where period array
+ values valid */
+ int periodl /* (i) dimension of period and plocs */
+){
+ float sseq[(2*ENH_HL+1)*ENH_BLOCKL];
+
+ /* get said second sequence of segments */
+
+ getsseq(sseq,idata,idatal,centerStartPos,period,
+ plocs,periodl,ENH_HL);
+
+ /* compute the smoothed output from said second sequence */
+
+ smath(odata,sseq,ENH_HL,alpha0);
+
+}
+
+/*----------------------------------------------------------------*
+ * cross correlation
+ *---------------------------------------------------------------*/
+
+float xCorrCoef(
+ float *target, /* (i) first array */
+ float *regressor, /* (i) second array */
+ int subl /* (i) dimension arrays */
+){
+ int i;
+ float ftmp1, ftmp2;
+
+ ftmp1 = 0.0;
+ ftmp2 = 0.0;
+
+
+ for (i=0; i<subl; i++) {
+ ftmp1 += target[i]*regressor[i];
+ ftmp2 += regressor[i]*regressor[i];
+ }
+
+ if (ftmp1 > 0.0) {
+ return (float)(ftmp1*ftmp1/ftmp2);
+ }
+ else {
+ return (float)0.0;
+ }
+}
+
+/*----------------------------------------------------------------*
+ * interface for enhancer
+ *---------------------------------------------------------------*/
+
+int enhancerInterface(
+ float *out, /* (o) enhanced signal */
+ float *in, /* (i) unenhanced signal */
+ iLBC_Dec_Inst_t *iLBCdec_inst /* (i) buffers etc */
+){
+ float *enh_buf, *enh_period;
+ int iblock, isample;
+ int lag=0, ilag, i, ioffset;
+ float cc, maxcc;
+ float ftmp1, ftmp2;
+ float *inPtr, *enh_bufPtr1, *enh_bufPtr2;
+ float plc_pred[ENH_BLOCKL];
+
+ float lpState[6], downsampled[(ENH_NBLOCKS*ENH_BLOCKL+120)/2];
+ int inLen=ENH_NBLOCKS*ENH_BLOCKL+120;
+ int start, plc_blockl, inlag;
+
+ enh_buf=iLBCdec_inst->enh_buf;
+ enh_period=iLBCdec_inst->enh_period;
+
+ memmove(enh_buf, &enh_buf[iLBCdec_inst->blockl],
+ (ENH_BUFL-iLBCdec_inst->blockl)*sizeof(float));
+
+ memcpy(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in,
+ iLBCdec_inst->blockl*sizeof(float));
+
+ if (iLBCdec_inst->mode==30)
+ plc_blockl=ENH_BLOCKL;
+ else
+ plc_blockl=40;
+
+ /* when 20 ms frame, move processing one block */
+ ioffset=0;
+ if (iLBCdec_inst->mode==20) ioffset=1;
+
+ i=3-ioffset;
+ memmove(enh_period, &enh_period[i],
+
+
+ (ENH_NBLOCKS_TOT-i)*sizeof(float));
+
+ /* Set state information to the 6 samples right before
+ the samples to be downsampled. */
+
+ memcpy(lpState,
+ enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-126,
+ 6*sizeof(float));
+
+ /* Down sample a factor 2 to save computations */
+
+ DownSample(enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-120,
+ lpFilt_coefsTbl, inLen-ioffset*ENH_BLOCKL,
+ lpState, downsampled);
+
+ /* Estimate the pitch in the down sampled domain. */
+ for (iblock = 0; iblock<ENH_NBLOCKS-ioffset; iblock++) {
+
+ lag = 10;
+ maxcc = xCorrCoef(downsampled+60+iblock*
+ ENH_BLOCKL_HALF, downsampled+60+iblock*
+ ENH_BLOCKL_HALF-lag, ENH_BLOCKL_HALF);
+ for (ilag=11; ilag<60; ilag++) {
+ cc = xCorrCoef(downsampled+60+iblock*
+ ENH_BLOCKL_HALF, downsampled+60+iblock*
+ ENH_BLOCKL_HALF-ilag, ENH_BLOCKL_HALF);
+
+ if (cc > maxcc) {
+ maxcc = cc;
+ lag = ilag;
+ }
+ }
+
+ /* Store the estimated lag in the non-downsampled domain */
+ enh_period[iblock+ENH_NBLOCKS_EXTRA+ioffset] = (float)lag*2;
+
+
+ }
+
+
+ /* PLC was performed on the previous packet */
+ if (iLBCdec_inst->prev_enh_pl==1) {
+
+ inlag=(int)enh_period[ENH_NBLOCKS_EXTRA+ioffset];
+
+ lag = inlag-1;
+ maxcc = xCorrCoef(in, in+lag, plc_blockl);
+ for (ilag=inlag; ilag<=inlag+1; ilag++) {
+ cc = xCorrCoef(in, in+ilag, plc_blockl);
+
+ if (cc > maxcc) {
+ maxcc = cc;
+ lag = ilag;
+ }
+ }
+
+
+
+ enh_period[ENH_NBLOCKS_EXTRA+ioffset-1]=(float)lag;
+
+ /* compute new concealed residual for the old lookahead,
+ mix the forward PLC with a backward PLC from
+ the new frame */
+
+ inPtr=&in[lag-1];
+
+ enh_bufPtr1=&plc_pred[plc_blockl-1];
+
+ if (lag>plc_blockl) {
+ start=plc_blockl;
+ } else {
+ start=lag;
+ }
+
+ for (isample = start; isample>0; isample--) {
+ *enh_bufPtr1-- = *inPtr--;
+ }
+
+ enh_bufPtr2=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl];
+ for (isample = (plc_blockl-1-lag); isample>=0; isample--)
+{
+ *enh_bufPtr1-- = *enh_bufPtr2--;
+ }
+
+ /* limit energy change */
+ ftmp2=0.0;
+ ftmp1=0.0;
+ for (i=0;i<plc_blockl;i++) {
+ ftmp2+=enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl-i]*
+ enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl-i];
+ ftmp1+=plc_pred[i]*plc_pred[i];
+ }
+ ftmp1=(float)sqrt(ftmp1/(float)plc_blockl);
+ ftmp2=(float)sqrt(ftmp2/(float)plc_blockl);
+ if (ftmp1>(float)2.0*ftmp2 && ftmp1>0.0) {
+ for (i=0;i<plc_blockl-10;i++) {
+ plc_pred[i]*=(float)2.0*ftmp2/ftmp1;
+ }
+ for (i=plc_blockl-10;i<plc_blockl;i++) {
+ plc_pred[i]*=(float)(i-plc_blockl+10)*
+ ((float)1.0-(float)2.0*ftmp2/ftmp1)/(float)(10)+
+ (float)2.0*ftmp2/ftmp1;
+ }
+ }
+
+ enh_bufPtr1=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl];
+ for (i=0; i<plc_blockl; i++) {
+ ftmp1 = (float) (i+1) / (float) (plc_blockl+1);
+ *enh_bufPtr1 *= ftmp1;
+ *enh_bufPtr1 += ((float)1.0-ftmp1)*
+ plc_pred[plc_blockl-1-i];
+ enh_bufPtr1--;
+ }
+
+
+ }
+
+ if (iLBCdec_inst->mode==20) {
+ /* Enhancer with 40 samples delay */
+ for (iblock = 0; iblock<2; iblock++) {
+ enhancer(out+iblock*ENH_BLOCKL, enh_buf,
+ ENH_BUFL, (5+iblock)*ENH_BLOCKL+40,
+ ENH_ALPHA0, enh_period, enh_plocsTbl,
+ ENH_NBLOCKS_TOT);
+ }
+ } else if (iLBCdec_inst->mode==30) {
+ /* Enhancer with 80 samples delay */
+ for (iblock = 0; iblock<3; iblock++) {
+ enhancer(out+iblock*ENH_BLOCKL, enh_buf,
+ ENH_BUFL, (4+iblock)*ENH_BLOCKL,
+ ENH_ALPHA0, enh_period, enh_plocsTbl,
+ ENH_NBLOCKS_TOT);
+ }
+ }
+
+ return (lag*2);
+}
+
+
diff --git a/trunk/codecs/ilbc/enhancer.h b/trunk/codecs/ilbc/enhancer.h
new file mode 100644
index 000000000..aa37b1843
--- /dev/null
+++ b/trunk/codecs/ilbc/enhancer.h
@@ -0,0 +1,33 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ enhancer.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __ENHANCER_H
+#define __ENHANCER_H
+
+#include "iLBC_define.h"
+
+float xCorrCoef(
+ float *target, /* (i) first array */
+ float *regressor, /* (i) second array */
+ int subl /* (i) dimension arrays */
+);
+
+int enhancerInterface(
+ float *out, /* (o) the enhanced recidual signal */
+ float *in, /* (i) the recidual signal to enhance */
+ iLBC_Dec_Inst_t *iLBCdec_inst
+ /* (i/o) the decoder state structure */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/filter.c b/trunk/codecs/ilbc/filter.c
new file mode 100644
index 000000000..f4ba63386
--- /dev/null
+++ b/trunk/codecs/ilbc/filter.c
@@ -0,0 +1,168 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ filter.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+
+
+#include "iLBC_define.h"
+#include "filter.h"
+
+/*----------------------------------------------------------------*
+ * all-pole filter
+ *---------------------------------------------------------------*/
+
+void AllPoleFilter(
+ float *InOut, /* (i/o) on entrance InOut[-orderCoef] to
+ InOut[-1] contain the state of the
+ filter (delayed samples). InOut[0] to
+ InOut[lengthInOut-1] contain the filter
+ input, on en exit InOut[-orderCoef] to
+ InOut[-1] is unchanged and InOut[0] to
+ InOut[lengthInOut-1] contain filtered
+ samples */
+ float *Coef,/* (i) filter coefficients, Coef[0] is assumed
+ to be 1.0 */
+ int lengthInOut,/* (i) number of input/output samples */
+ int orderCoef /* (i) number of filter coefficients */
+){
+ int n,k;
+
+ for(n=0;n<lengthInOut;n++){
+ for(k=1;k<=orderCoef;k++){
+ *InOut -= Coef[k]*InOut[-k];
+ }
+ InOut++;
+ }
+}
+
+/*----------------------------------------------------------------*
+ * all-zero filter
+ *---------------------------------------------------------------*/
+
+void AllZeroFilter(
+ float *In, /* (i) In[0] to In[lengthInOut-1] contain
+ filter input samples */
+ float *Coef,/* (i) filter coefficients (Coef[0] is assumed
+ to be 1.0) */
+ int lengthInOut,/* (i) number of input/output samples */
+ int orderCoef, /* (i) number of filter coefficients */
+ float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
+ contain the filter state, on exit Out[0]
+ to Out[lengthInOut-1] contain filtered
+ samples */
+){
+ int n,k;
+
+ for(n=0;n<lengthInOut;n++){
+ *Out = Coef[0]*In[0];
+ for(k=1;k<=orderCoef;k++){
+ *Out += Coef[k]*In[-k];
+ }
+
+
+ Out++;
+ In++;
+ }
+}
+
+/*----------------------------------------------------------------*
+ * pole-zero filter
+ *---------------------------------------------------------------*/
+
+void ZeroPoleFilter(
+ float *In, /* (i) In[0] to In[lengthInOut-1] contain
+ filter input samples In[-orderCoef] to
+ In[-1] contain state of all-zero
+ section */
+ float *ZeroCoef,/* (i) filter coefficients for all-zero
+ section (ZeroCoef[0] is assumed to
+ be 1.0) */
+ float *PoleCoef,/* (i) filter coefficients for all-pole section
+ (ZeroCoef[0] is assumed to be 1.0) */
+ int lengthInOut,/* (i) number of input/output samples */
+ int orderCoef, /* (i) number of filter coefficients */
+ float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
+ contain state of all-pole section. On
+ exit Out[0] to Out[lengthInOut-1]
+ contain filtered samples */
+){
+ AllZeroFilter(In,ZeroCoef,lengthInOut,orderCoef,Out);
+ AllPoleFilter(Out,PoleCoef,lengthInOut,orderCoef);
+}
+
+/*----------------------------------------------------------------*
+ * downsample (LP filter and decimation)
+ *---------------------------------------------------------------*/
+
+void DownSample (
+ float *In, /* (i) input samples */
+ float *Coef, /* (i) filter coefficients */
+ int lengthIn, /* (i) number of input samples */
+ float *state, /* (i) filter state */
+ float *Out /* (o) downsampled output */
+){
+ float o;
+ float *Out_ptr = Out;
+ float *Coef_ptr, *In_ptr;
+ float *state_ptr;
+ int i, j, stop;
+
+ /* LP filter and decimate at the same time */
+
+ for (i = DELAY_DS; i < lengthIn; i+=FACTOR_DS)
+ {
+ Coef_ptr = &Coef[0];
+ In_ptr = &In[i];
+ state_ptr = &state[FILTERORDER_DS-2];
+
+
+
+ o = (float)0.0;
+
+ stop = (i < FILTERORDER_DS) ? i + 1 : FILTERORDER_DS;
+
+ for (j = 0; j < stop; j++)
+ {
+ o += *Coef_ptr++ * (*In_ptr--);
+ }
+ for (j = i + 1; j < FILTERORDER_DS; j++)
+ {
+ o += *Coef_ptr++ * (*state_ptr--);
+ }
+
+ *Out_ptr++ = o;
+ }
+
+ /* Get the last part (use zeros as input for the future) */
+
+ for (i=(lengthIn+FACTOR_DS); i<(lengthIn+DELAY_DS);
+ i+=FACTOR_DS) {
+
+ o=(float)0.0;
+
+ if (i<lengthIn) {
+ Coef_ptr = &Coef[0];
+ In_ptr = &In[i];
+ for (j=0; j<FILTERORDER_DS; j++) {
+ o += *Coef_ptr++ * (*Out_ptr--);
+ }
+ } else {
+ Coef_ptr = &Coef[i-lengthIn];
+ In_ptr = &In[lengthIn-1];
+ for (j=0; j<FILTERORDER_DS-(i-lengthIn); j++) {
+ o += *Coef_ptr++ * (*In_ptr--);
+ }
+ }
+ *Out_ptr++ = o;
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/filter.h b/trunk/codecs/ilbc/filter.h
new file mode 100644
index 000000000..95d3b5445
--- /dev/null
+++ b/trunk/codecs/ilbc/filter.h
@@ -0,0 +1,73 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ filter.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_FILTER_H
+#define __iLBC_FILTER_H
+
+void AllPoleFilter(
+ float *InOut, /* (i/o) on entrance InOut[-orderCoef] to
+ InOut[-1] contain the state of the
+ filter (delayed samples). InOut[0] to
+ InOut[lengthInOut-1] contain the filter
+ input, on en exit InOut[-orderCoef] to
+ InOut[-1] is unchanged and InOut[0] to
+ InOut[lengthInOut-1] contain filtered
+ samples */
+ float *Coef,/* (i) filter coefficients, Coef[0] is assumed
+ to be 1.0 */
+ int lengthInOut,/* (i) number of input/output samples */
+ int orderCoef /* (i) number of filter coefficients */
+);
+
+
+
+void AllZeroFilter(
+ float *In, /* (i) In[0] to In[lengthInOut-1] contain
+ filter input samples */
+ float *Coef,/* (i) filter coefficients (Coef[0] is assumed
+ to be 1.0) */
+ int lengthInOut,/* (i) number of input/output samples */
+ int orderCoef, /* (i) number of filter coefficients */
+ float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
+ contain the filter state, on exit Out[0]
+ to Out[lengthInOut-1] contain filtered
+ samples */
+);
+
+void ZeroPoleFilter(
+ float *In, /* (i) In[0] to In[lengthInOut-1] contain filter
+ input samples In[-orderCoef] to In[-1]
+ contain state of all-zero section */
+ float *ZeroCoef,/* (i) filter coefficients for all-zero
+ section (ZeroCoef[0] is assumed to
+ be 1.0) */
+ float *PoleCoef,/* (i) filter coefficients for all-pole section
+ (ZeroCoef[0] is assumed to be 1.0) */
+ int lengthInOut,/* (i) number of input/output samples */
+ int orderCoef, /* (i) number of filter coefficients */
+ float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
+ contain state of all-pole section. On
+ exit Out[0] to Out[lengthInOut-1]
+ contain filtered samples */
+);
+
+void DownSample (
+ float *In, /* (i) input samples */
+ float *Coef, /* (i) filter coefficients */
+ int lengthIn, /* (i) number of input samples */
+ float *state, /* (i) filter state */
+ float *Out /* (o) downsampled output */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/gainquant.c b/trunk/codecs/ilbc/gainquant.c
new file mode 100644
index 000000000..33dc19474
--- /dev/null
+++ b/trunk/codecs/ilbc/gainquant.c
@@ -0,0 +1,107 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ gainquant.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+
+
+******************************************************************/
+
+#include <string.h>
+#include <math.h>
+#include "constants.h"
+#include "gainquant.h"
+#include "filter.h"
+
+/*----------------------------------------------------------------*
+ * quantizer for the gain in the gain-shape coding of residual
+ *---------------------------------------------------------------*/
+
+float gainquant(/* (o) quantized gain value */
+ float in, /* (i) gain value */
+ float maxIn,/* (i) maximum of gain value */
+ int cblen, /* (i) number of quantization indices */
+ int *index /* (o) quantization index */
+){
+ int i, tindex;
+ float minmeasure,measure, *cb, scale;
+
+ /* ensure a lower bound on the scaling factor */
+
+ scale=maxIn;
+
+ if (scale<0.1) {
+ scale=(float)0.1;
+ }
+
+ /* select the quantization table */
+
+ if (cblen == 8) {
+ cb = gain_sq3Tbl;
+ } else if (cblen == 16) {
+ cb = gain_sq4Tbl;
+ } else {
+ cb = gain_sq5Tbl;
+ }
+
+ /* select the best index in the quantization table */
+
+ minmeasure=10000000.0;
+ tindex=0;
+ for (i=0; i<cblen; i++) {
+ measure=(in-scale*cb[i])*(in-scale*cb[i]);
+
+ if (measure<minmeasure) {
+ tindex=i;
+ minmeasure=measure;
+ }
+ }
+ *index=tindex;
+
+ /* return the quantized value */
+
+
+
+ return scale*cb[tindex];
+}
+
+/*----------------------------------------------------------------*
+ * decoder for quantized gains in the gain-shape coding of
+ * residual
+ *---------------------------------------------------------------*/
+
+float gaindequant( /* (o) quantized gain value */
+ int index, /* (i) quantization index */
+ float maxIn,/* (i) maximum of unquantized gain */
+ int cblen /* (i) number of quantization indices */
+){
+ float scale;
+
+ /* obtain correct scale factor */
+
+ scale=(float)fabs(maxIn);
+
+ if (scale<0.1) {
+ scale=(float)0.1;
+ }
+
+ /* select the quantization table and return the decoded value */
+
+ if (cblen==8) {
+ return scale*gain_sq3Tbl[index];
+ } else if (cblen==16) {
+ return scale*gain_sq4Tbl[index];
+ }
+ else if (cblen==32) {
+ return scale*gain_sq5Tbl[index];
+ }
+
+ return 0.0;
+}
+
+
diff --git a/trunk/codecs/ilbc/gainquant.h b/trunk/codecs/ilbc/gainquant.h
new file mode 100644
index 000000000..f613b3565
--- /dev/null
+++ b/trunk/codecs/ilbc/gainquant.h
@@ -0,0 +1,31 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ gainquant.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_GAINQUANT_H
+#define __iLBC_GAINQUANT_H
+
+float gainquant(/* (o) quantized gain value */
+ float in, /* (i) gain value */
+ float maxIn,/* (i) maximum of gain value */
+ int cblen, /* (i) number of quantization indices */
+ int *index /* (o) quantization index */
+);
+
+float gaindequant( /* (o) quantized gain value */
+ int index, /* (i) quantization index */
+ float maxIn,/* (i) maximum of unquantized gain */
+ int cblen /* (i) number of quantization indices */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/getCBvec.c b/trunk/codecs/ilbc/getCBvec.c
new file mode 100644
index 000000000..081abb216
--- /dev/null
+++ b/trunk/codecs/ilbc/getCBvec.c
@@ -0,0 +1,181 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ getCBvec.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include "iLBC_define.h"
+#include "getCBvec.h"
+#include "constants.h"
+#include <string.h>
+
+/*----------------------------------------------------------------*
+ * Construct codebook vector for given index.
+ *---------------------------------------------------------------*/
+
+void getCBvec(
+ float *cbvec, /* (o) Constructed codebook vector */
+ float *mem, /* (i) Codebook buffer */
+ int index, /* (i) Codebook index */
+ int lMem, /* (i) Length of codebook buffer */
+ int cbveclen/* (i) Codebook vector length */
+){
+ int j, k, n, memInd, sFilt;
+ float tmpbuf[CB_MEML];
+ int base_size;
+ int ilow, ihigh;
+ float alfa, alfa1;
+
+ /* Determine size of codebook sections */
+
+ base_size=lMem-cbveclen+1;
+
+ if (cbveclen==SUBL) {
+ base_size+=cbveclen/2;
+ }
+
+ /* No filter -> First codebook section */
+
+
+
+ if (index<lMem-cbveclen+1) {
+
+ /* first non-interpolated vectors */
+
+ k=index+cbveclen;
+ /* get vector */
+ memcpy(cbvec, mem+lMem-k, cbveclen*sizeof(float));
+
+ } else if (index < base_size) {
+
+ k=2*(index-(lMem-cbveclen+1))+cbveclen;
+
+ ihigh=k/2;
+ ilow=ihigh-5;
+
+ /* Copy first noninterpolated part */
+
+ memcpy(cbvec, mem+lMem-k/2, ilow*sizeof(float));
+
+ /* interpolation */
+
+ alfa1=(float)0.2;
+ alfa=0.0;
+ for (j=ilow; j<ihigh; j++) {
+ cbvec[j]=((float)1.0-alfa)*mem[lMem-k/2+j]+
+ alfa*mem[lMem-k+j];
+ alfa+=alfa1;
+ }
+
+ /* Copy second noninterpolated part */
+
+ memcpy(cbvec+ihigh, mem+lMem-k+ihigh,
+ (cbveclen-ihigh)*sizeof(float));
+
+ }
+
+ /* Higher codebbok section based on filtering */
+
+ else {
+
+ /* first non-interpolated vectors */
+
+ if (index-base_size<lMem-cbveclen+1) {
+ float tempbuff2[CB_MEML+CB_FILTERLEN+1];
+ float *pos;
+ float *pp, *pp1;
+
+ memset(tempbuff2, 0,
+ CB_HALFFILTERLEN*sizeof(float));
+ memcpy(&tempbuff2[CB_HALFFILTERLEN], mem,
+ lMem*sizeof(float));
+ memset(&tempbuff2[lMem+CB_HALFFILTERLEN], 0,
+ (CB_HALFFILTERLEN+1)*sizeof(float));
+
+
+
+ k=index-base_size+cbveclen;
+ sFilt=lMem-k;
+ memInd=sFilt+1-CB_HALFFILTERLEN;
+
+ /* do filtering */
+ pos=cbvec;
+ memset(pos, 0, cbveclen*sizeof(float));
+ for (n=0; n<cbveclen; n++) {
+ pp=&tempbuff2[memInd+n+CB_HALFFILTERLEN];
+ pp1=&cbfiltersTbl[CB_FILTERLEN-1];
+ for (j=0; j<CB_FILTERLEN; j++) {
+ (*pos)+=(*pp++)*(*pp1--);
+ }
+ pos++;
+ }
+ }
+
+ /* interpolated vectors */
+
+ else {
+ float tempbuff2[CB_MEML+CB_FILTERLEN+1];
+
+ float *pos;
+ float *pp, *pp1;
+ int i;
+
+ memset(tempbuff2, 0,
+ CB_HALFFILTERLEN*sizeof(float));
+ memcpy(&tempbuff2[CB_HALFFILTERLEN], mem,
+ lMem*sizeof(float));
+ memset(&tempbuff2[lMem+CB_HALFFILTERLEN], 0,
+ (CB_HALFFILTERLEN+1)*sizeof(float));
+
+ k=2*(index-base_size-
+ (lMem-cbveclen+1))+cbveclen;
+ sFilt=lMem-k;
+ memInd=sFilt+1-CB_HALFFILTERLEN;
+
+ /* do filtering */
+ pos=&tmpbuf[sFilt];
+ memset(pos, 0, k*sizeof(float));
+ for (i=0; i<k; i++) {
+ pp=&tempbuff2[memInd+i+CB_HALFFILTERLEN];
+ pp1=&cbfiltersTbl[CB_FILTERLEN-1];
+ for (j=0; j<CB_FILTERLEN; j++) {
+ (*pos)+=(*pp++)*(*pp1--);
+ }
+ pos++;
+ }
+
+ ihigh=k/2;
+ ilow=ihigh-5;
+
+
+
+ /* Copy first noninterpolated part */
+
+ memcpy(cbvec, tmpbuf+lMem-k/2,
+ ilow*sizeof(float));
+
+ /* interpolation */
+
+ alfa1=(float)0.2;
+ alfa=0.0;
+ for (j=ilow; j<ihigh; j++) {
+ cbvec[j]=((float)1.0-alfa)*
+ tmpbuf[lMem-k/2+j]+alfa*tmpbuf[lMem-k+j];
+ alfa+=alfa1;
+ }
+
+ /* Copy second noninterpolated part */
+
+ memcpy(cbvec+ihigh, tmpbuf+lMem-k+ihigh,
+ (cbveclen-ihigh)*sizeof(float));
+ }
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/getCBvec.h b/trunk/codecs/ilbc/getCBvec.h
new file mode 100644
index 000000000..8b1f8c85c
--- /dev/null
+++ b/trunk/codecs/ilbc/getCBvec.h
@@ -0,0 +1,28 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ getCBvec.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_GETCBVEC_H
+#define __iLBC_GETCBVEC_H
+
+
+
+void getCBvec(
+ float *cbvec, /* (o) Constructed codebook vector */
+ float *mem, /* (i) Codebook buffer */
+ int index, /* (i) Codebook index */
+ int lMem, /* (i) Length of codebook buffer */
+ int cbveclen/* (i) Codebook vector length */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/helpfun.c b/trunk/codecs/ilbc/helpfun.c
new file mode 100644
index 000000000..02d83c971
--- /dev/null
+++ b/trunk/codecs/ilbc/helpfun.c
@@ -0,0 +1,308 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ helpfun.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+
+#include "iLBC_define.h"
+#include "helpfun.h"
+#include "constants.h"
+
+/*----------------------------------------------------------------*
+ * calculation of auto correlation
+ *---------------------------------------------------------------*/
+
+void autocorr(
+ float *r, /* (o) autocorrelation vector */
+ const float *x, /* (i) data vector */
+ int N, /* (i) length of data vector */
+ int order /* largest lag for calculated
+ autocorrelations */
+){
+ int lag, n;
+ float sum;
+
+ for (lag = 0; lag <= order; lag++) {
+ sum = 0;
+ for (n = 0; n < N - lag; n++) {
+ sum += x[n] * x[n+lag];
+ }
+ r[lag] = sum;
+ }
+}
+
+
+
+/*----------------------------------------------------------------*
+ * window multiplication
+ *---------------------------------------------------------------*/
+
+void window(
+ float *z, /* (o) the windowed data */
+ const float *x, /* (i) the original data vector */
+ const float *y, /* (i) the window */
+ int N /* (i) length of all vectors */
+){
+ int i;
+
+ for (i = 0; i < N; i++) {
+ z[i] = x[i] * y[i];
+ }
+}
+
+/*----------------------------------------------------------------*
+ * levinson-durbin solution for lpc coefficients
+ *---------------------------------------------------------------*/
+
+void levdurb(
+ float *a, /* (o) lpc coefficient vector starting
+ with 1.0 */
+ float *k, /* (o) reflection coefficients */
+ float *r, /* (i) autocorrelation vector */
+ int order /* (i) order of lpc filter */
+){
+ float sum, alpha;
+ int m, m_h, i;
+
+ a[0] = 1.0;
+
+ if (r[0] < EPS) { /* if r[0] <= 0, set LPC coeff. to zero */
+ for (i = 0; i < order; i++) {
+ k[i] = 0;
+ a[i+1] = 0;
+ }
+ } else {
+ a[1] = k[0] = -r[1]/r[0];
+ alpha = r[0] + r[1] * k[0];
+ for (m = 1; m < order; m++){
+ sum = r[m + 1];
+ for (i = 0; i < m; i++){
+ sum += a[i+1] * r[m - i];
+ }
+ k[m] = -sum / alpha;
+ alpha += k[m] * sum;
+ m_h = (m + 1) >> 1;
+ for (i = 0; i < m_h; i++){
+ sum = a[i+1] + k[m] * a[m - i];
+ a[m - i] += k[m] * a[i+1];
+ a[i+1] = sum;
+
+
+ }
+ a[m+1] = k[m];
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * interpolation between vectors
+ *---------------------------------------------------------------*/
+
+void interpolate(
+ float *out, /* (o) the interpolated vector */
+ float *in1, /* (i) the first vector for the
+ interpolation */
+ float *in2, /* (i) the second vector for the
+ interpolation */
+ float coef, /* (i) interpolation weights */
+ int length /* (i) length of all vectors */
+){
+ int i;
+ float invcoef;
+
+ invcoef = (float)1.0 - coef;
+ for (i = 0; i < length; i++) {
+ out[i] = coef * in1[i] + invcoef * in2[i];
+ }
+}
+
+/*----------------------------------------------------------------*
+ * lpc bandwidth expansion
+ *---------------------------------------------------------------*/
+
+void bwexpand(
+ float *out, /* (o) the bandwidth expanded lpc
+ coefficients */
+ float *in, /* (i) the lpc coefficients before bandwidth
+ expansion */
+ float coef, /* (i) the bandwidth expansion factor */
+ int length /* (i) the length of lpc coefficient vectors */
+){
+ int i;
+ float chirp;
+
+ chirp = coef;
+
+ out[0] = in[0];
+ for (i = 1; i < length; i++) {
+ out[i] = chirp * in[i];
+ chirp *= coef;
+ }
+}
+
+/*----------------------------------------------------------------*
+ * vector quantization
+
+
+ *---------------------------------------------------------------*/
+
+void vq(
+ float *Xq, /* (o) the quantized vector */
+ int *index, /* (o) the quantization index */
+ const float *CB,/* (i) the vector quantization codebook */
+ float *X, /* (i) the vector to quantize */
+ int n_cb, /* (i) the number of vectors in the codebook */
+ int dim /* (i) the dimension of all vectors */
+){
+ int i, j;
+ int pos, minindex;
+ float dist, tmp, mindist;
+
+ pos = 0;
+ mindist = FLOAT_MAX;
+ minindex = 0;
+ for (j = 0; j < n_cb; j++) {
+ dist = X[0] - CB[pos];
+ dist *= dist;
+ for (i = 1; i < dim; i++) {
+ tmp = X[i] - CB[pos + i];
+ dist += tmp*tmp;
+ }
+
+ if (dist < mindist) {
+ mindist = dist;
+ minindex = j;
+ }
+ pos += dim;
+ }
+ for (i = 0; i < dim; i++) {
+ Xq[i] = CB[minindex*dim + i];
+ }
+ *index = minindex;
+}
+
+/*----------------------------------------------------------------*
+ * split vector quantization
+ *---------------------------------------------------------------*/
+
+void SplitVQ(
+ float *qX, /* (o) the quantized vector */
+ int *index, /* (o) a vector of indexes for all vector
+ codebooks in the split */
+ float *X, /* (i) the vector to quantize */
+ const float *CB,/* (i) the quantizer codebook */
+ int nsplit, /* the number of vector splits */
+ const int *dim, /* the dimension of X and qX */
+ const int *cbsize /* the number of vectors in the codebook */
+){
+ int cb_pos, X_pos, i;
+
+ cb_pos = 0;
+
+
+ X_pos= 0;
+ for (i = 0; i < nsplit; i++) {
+ vq(qX + X_pos, index + i, CB + cb_pos, X + X_pos,
+ cbsize[i], dim[i]);
+ X_pos += dim[i];
+ cb_pos += dim[i] * cbsize[i];
+ }
+}
+
+/*----------------------------------------------------------------*
+ * scalar quantization
+ *---------------------------------------------------------------*/
+
+void sort_sq(
+ float *xq, /* (o) the quantized value */
+ int *index, /* (o) the quantization index */
+ float x, /* (i) the value to quantize */
+ const float *cb,/* (i) the quantization codebook */
+ int cb_size /* (i) the size of the quantization codebook */
+){
+ int i;
+
+ if (x <= cb[0]) {
+ *index = 0;
+ *xq = cb[0];
+ } else {
+ i = 0;
+ while ((x > cb[i]) && i < cb_size - 1) {
+ i++;
+ }
+
+ if (x > ((cb[i] + cb[i - 1])/2)) {
+ *index = i;
+ *xq = cb[i];
+ } else {
+ *index = i - 1;
+ *xq = cb[i - 1];
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * check for stability of lsf coefficients
+ *---------------------------------------------------------------*/
+
+int LSF_check( /* (o) 1 for stable lsf vectors and 0 for
+ nonstable ones */
+ float *lsf, /* (i) a table of lsf vectors */
+ int dim, /* (i) the dimension of each lsf vector */
+ int NoAn /* (i) the number of lsf vectors in the
+ table */
+){
+ int k,n,m, Nit=2, change=0,pos;
+ float tmp;
+
+
+ static float eps=(float)0.039; /* 50 Hz */
+ static float eps2=(float)0.0195;
+ static float maxlsf=(float)3.14; /* 4000 Hz */
+ static float minlsf=(float)0.01; /* 0 Hz */
+
+ /* LSF separation check*/
+
+ for (n=0; n<Nit; n++) { /* Run through a couple of times */
+ for (m=0; m<NoAn; m++) { /* Number of analyses per frame */
+ for (k=0; k<(dim-1); k++) {
+ pos=m*dim+k;
+
+ if ((lsf[pos+1]-lsf[pos])<eps) {
+
+ if (lsf[pos+1]<lsf[pos]) {
+ tmp=lsf[pos+1];
+ lsf[pos+1]= lsf[pos]+eps2;
+ lsf[pos]= lsf[pos+1]-eps2;
+ } else {
+ lsf[pos]-=eps2;
+ lsf[pos+1]+=eps2;
+ }
+ change=1;
+ }
+
+ if (lsf[pos]<minlsf) {
+ lsf[pos]=minlsf;
+ change=1;
+ }
+
+ if (lsf[pos]>maxlsf) {
+ lsf[pos]=maxlsf;
+ change=1;
+ }
+ }
+ }
+ }
+
+ return change;
+}
+
+
diff --git a/trunk/codecs/ilbc/helpfun.h b/trunk/codecs/ilbc/helpfun.h
new file mode 100644
index 000000000..291a761d9
--- /dev/null
+++ b/trunk/codecs/ilbc/helpfun.h
@@ -0,0 +1,101 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ helpfun.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_HELPFUN_H
+#define __iLBC_HELPFUN_H
+
+void autocorr(
+ float *r, /* (o) autocorrelation vector */
+ const float *x, /* (i) data vector */
+ int N, /* (i) length of data vector */
+ int order /* largest lag for calculated
+ autocorrelations */
+);
+
+void window(
+ float *z, /* (o) the windowed data */
+ const float *x, /* (i) the original data vector */
+ const float *y, /* (i) the window */
+ int N /* (i) length of all vectors */
+);
+
+
+
+void levdurb(
+ float *a, /* (o) lpc coefficient vector starting
+ with 1.0 */
+ float *k, /* (o) reflection coefficients */
+ float *r, /* (i) autocorrelation vector */
+ int order /* (i) order of lpc filter */
+);
+
+void interpolate(
+ float *out, /* (o) the interpolated vector */
+ float *in1, /* (i) the first vector for the
+ interpolation */
+ float *in2, /* (i) the second vector for the
+ interpolation */
+ float coef, /* (i) interpolation weights */
+ int length /* (i) length of all vectors */
+);
+
+void bwexpand(
+ float *out, /* (o) the bandwidth expanded lpc
+ coefficients */
+ float *in, /* (i) the lpc coefficients before bandwidth
+ expansion */
+ float coef, /* (i) the bandwidth expansion factor */
+ int length /* (i) the length of lpc coefficient vectors */
+);
+
+void vq(
+ float *Xq, /* (o) the quantized vector */
+ int *index, /* (o) the quantization index */
+ const float *CB,/* (i) the vector quantization codebook */
+ float *X, /* (i) the vector to quantize */
+ int n_cb, /* (i) the number of vectors in the codebook */
+ int dim /* (i) the dimension of all vectors */
+);
+
+void SplitVQ(
+ float *qX, /* (o) the quantized vector */
+ int *index, /* (o) a vector of indexes for all vector
+ codebooks in the split */
+ float *X, /* (i) the vector to quantize */
+ const float *CB,/* (i) the quantizer codebook */
+ int nsplit, /* the number of vector splits */
+ const int *dim, /* the dimension of X and qX */
+ const int *cbsize /* the number of vectors in the codebook */
+);
+
+
+void sort_sq(
+ float *xq, /* (o) the quantized value */
+ int *index, /* (o) the quantization index */
+ float x, /* (i) the value to quantize */
+ const float *cb,/* (i) the quantization codebook */
+
+
+ int cb_size /* (i) the size of the quantization codebook */
+);
+
+int LSF_check( /* (o) 1 for stable lsf vectors and 0 for
+ nonstable ones */
+ float *lsf, /* (i) a table of lsf vectors */
+ int dim, /* (i) the dimension of each lsf vector */
+ int NoAn /* (i) the number of lsf vectors in the
+ table */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/hpInput.c b/trunk/codecs/ilbc/hpInput.c
new file mode 100644
index 000000000..16b98f3df
--- /dev/null
+++ b/trunk/codecs/ilbc/hpInput.c
@@ -0,0 +1,60 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ hpInput.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include "constants.h"
+#include "hpInput.h"
+
+/*----------------------------------------------------------------*
+ * Input high-pass filter
+ *---------------------------------------------------------------*/
+
+void hpInput(
+ float *In, /* (i) vector to filter */
+ int len, /* (i) length of vector to filter */
+ float *Out, /* (o) the resulting filtered vector */
+ float *mem /* (i/o) the filter state */
+){
+ int i;
+ float *pi, *po;
+
+ /* all-zero section*/
+
+ pi = &In[0];
+ po = &Out[0];
+ for (i=0; i<len; i++) {
+ *po = hpi_zero_coefsTbl[0] * (*pi);
+ *po += hpi_zero_coefsTbl[1] * mem[0];
+ *po += hpi_zero_coefsTbl[2] * mem[1];
+
+ mem[1] = mem[0];
+ mem[0] = *pi;
+ po++;
+
+
+ pi++;
+
+ }
+
+ /* all-pole section*/
+
+ po = &Out[0];
+ for (i=0; i<len; i++) {
+ *po -= hpi_pole_coefsTbl[1] * mem[2];
+ *po -= hpi_pole_coefsTbl[2] * mem[3];
+
+ mem[3] = mem[2];
+ mem[2] = *po;
+ po++;
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/hpInput.h b/trunk/codecs/ilbc/hpInput.h
new file mode 100644
index 000000000..1de47003f
--- /dev/null
+++ b/trunk/codecs/ilbc/hpInput.h
@@ -0,0 +1,27 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ hpInput.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+
+
+#ifndef __iLBC_HPINPUT_H
+#define __iLBC_HPINPUT_H
+
+void hpInput(
+ float *In, /* (i) vector to filter */
+ int len, /* (i) length of vector to filter */
+ float *Out, /* (o) the resulting filtered vector */
+ float *mem /* (i/o) the filter state */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/hpOutput.c b/trunk/codecs/ilbc/hpOutput.c
new file mode 100644
index 000000000..fdc0f6db9
--- /dev/null
+++ b/trunk/codecs/ilbc/hpOutput.c
@@ -0,0 +1,59 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ hpOutput.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+
+
+******************************************************************/
+
+#include "constants.h"
+#include "hpOutput.h"
+/*----------------------------------------------------------------*
+ * Output high-pass filter
+ *---------------------------------------------------------------*/
+
+void hpOutput(
+ float *In, /* (i) vector to filter */
+ int len,/* (i) length of vector to filter */
+ float *Out, /* (o) the resulting filtered vector */
+ float *mem /* (i/o) the filter state */
+){
+ int i;
+ float *pi, *po;
+
+ /* all-zero section*/
+
+ pi = &In[0];
+ po = &Out[0];
+ for (i=0; i<len; i++) {
+ *po = hpo_zero_coefsTbl[0] * (*pi);
+ *po += hpo_zero_coefsTbl[1] * mem[0];
+ *po += hpo_zero_coefsTbl[2] * mem[1];
+
+ mem[1] = mem[0];
+ mem[0] = *pi;
+ po++;
+ pi++;
+
+ }
+
+ /* all-pole section*/
+
+ po = &Out[0];
+ for (i=0; i<len; i++) {
+ *po -= hpo_pole_coefsTbl[1] * mem[2];
+ *po -= hpo_pole_coefsTbl[2] * mem[3];
+
+ mem[3] = mem[2];
+ mem[2] = *po;
+ po++;
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/hpOutput.h b/trunk/codecs/ilbc/hpOutput.h
new file mode 100644
index 000000000..8092947a1
--- /dev/null
+++ b/trunk/codecs/ilbc/hpOutput.h
@@ -0,0 +1,25 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ hpOutput.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_HPOUTPUT_H
+#define __iLBC_HPOUTPUT_H
+
+void hpOutput(
+ float *In, /* (i) vector to filter */
+ int len,/* (i) length of vector to filter */
+ float *Out, /* (o) the resulting filtered vector */
+ float *mem /* (i/o) the filter state */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/iCBConstruct.c b/trunk/codecs/ilbc/iCBConstruct.c
new file mode 100644
index 000000000..dbee586c2
--- /dev/null
+++ b/trunk/codecs/ilbc/iCBConstruct.c
@@ -0,0 +1,108 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iCBConstruct.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+
+#include "iLBC_define.h"
+#include "iCBConstruct.h"
+#include "gainquant.h"
+#include "getCBvec.h"
+
+/*----------------------------------------------------------------*
+ * Convert the codebook indexes to make the search easier
+ *---------------------------------------------------------------*/
+
+
+
+void index_conv_enc(
+ int *index /* (i/o) Codebook indexes */
+){
+ int k;
+
+ for (k=1; k<CB_NSTAGES; k++) {
+
+ if ((index[k]>=108)&&(index[k]<172)) {
+ index[k]-=64;
+ } else if (index[k]>=236) {
+ index[k]-=128;
+ } else {
+ /* ERROR */
+ }
+ }
+}
+
+void index_conv_dec(
+ int *index /* (i/o) Codebook indexes */
+){
+ int k;
+
+ for (k=1; k<CB_NSTAGES; k++) {
+
+ if ((index[k]>=44)&&(index[k]<108)) {
+ index[k]+=64;
+ } else if ((index[k]>=108)&&(index[k]<128)) {
+ index[k]+=128;
+ } else {
+ /* ERROR */
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * Construct decoded vector from codebook and gains.
+ *---------------------------------------------------------------*/
+
+void iCBConstruct(
+ float *decvector, /* (o) Decoded vector */
+ int *index, /* (i) Codebook indices */
+ int *gain_index,/* (i) Gain quantization indices */
+ float *mem, /* (i) Buffer for codevector construction */
+ int lMem, /* (i) Length of buffer */
+ int veclen, /* (i) Length of vector */
+ int nStages /* (i) Number of codebook stages */
+){
+ int j,k;
+ float gain[CB_NSTAGES];
+ float cbvec[SUBL];
+
+ /* gain de-quantization */
+
+ gain[0] = gaindequant(gain_index[0], 1.0, 32);
+
+
+ if (nStages > 1) {
+ gain[1] = gaindequant(gain_index[1],
+ (float)fabs(gain[0]), 16);
+ }
+ if (nStages > 2) {
+ gain[2] = gaindequant(gain_index[2],
+ (float)fabs(gain[1]), 8);
+ }
+
+ /* codebook vector construction and construction of
+ total vector */
+
+ getCBvec(cbvec, mem, index[0], lMem, veclen);
+ for (j=0;j<veclen;j++){
+ decvector[j] = gain[0]*cbvec[j];
+ }
+ if (nStages > 1) {
+ for (k=1; k<nStages; k++) {
+ getCBvec(cbvec, mem, index[k], lMem, veclen);
+ for (j=0;j<veclen;j++) {
+ decvector[j] += gain[k]*cbvec[j];
+ }
+ }
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/iCBConstruct.h b/trunk/codecs/ilbc/iCBConstruct.h
new file mode 100644
index 000000000..be218c347
--- /dev/null
+++ b/trunk/codecs/ilbc/iCBConstruct.h
@@ -0,0 +1,38 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+
+
+ iCBConstruct.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_ICBCONSTRUCT_H
+#define __iLBC_ICBCONSTRUCT_H
+
+void index_conv_enc(
+ int *index /* (i/o) Codebook indexes */
+);
+
+void index_conv_dec(
+ int *index /* (i/o) Codebook indexes */
+);
+
+void iCBConstruct(
+ float *decvector, /* (o) Decoded vector */
+ int *index, /* (i) Codebook indices */
+ int *gain_index,/* (i) Gain quantization indices */
+ float *mem, /* (i) Buffer for codevector construction */
+ int lMem, /* (i) Length of buffer */
+ int veclen, /* (i) Length of vector */
+ int nStages /* (i) Number of codebook stages */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/iCBSearch.c b/trunk/codecs/ilbc/iCBSearch.c
new file mode 100644
index 000000000..c047297e3
--- /dev/null
+++ b/trunk/codecs/ilbc/iCBSearch.c
@@ -0,0 +1,480 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iCBSearch.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <string.h>
+
+#include "iLBC_define.h"
+#include "iCBSearch.h"
+#include "gainquant.h"
+#include "createCB.h"
+#include "filter.h"
+#include "constants.h"
+
+/*----------------------------------------------------------------*
+ * Search routine for codebook encoding and gain quantization.
+ *---------------------------------------------------------------*/
+
+void iCBSearch(
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i) the encoder state structure */
+ int *index, /* (o) Codebook indices */
+ int *gain_index,/* (o) Gain quantization indices */
+ float *intarget,/* (i) Target vector for encoding */
+ float *mem, /* (i) Buffer for codebook construction */
+ int lMem, /* (i) Length of buffer */
+ int lTarget, /* (i) Length of vector */
+ int nStages, /* (i) Number of codebook stages */
+ float *weightDenum, /* (i) weighting filter coefficients */
+ float *weightState, /* (i) weighting filter state */
+ int block /* (i) the sub-block number */
+){
+ int i, j, icount, stage, best_index, range, counter;
+ float max_measure, gain, measure, crossDot, ftmp;
+ float gains[CB_NSTAGES];
+ float target[SUBL];
+ int base_index, sInd, eInd, base_size;
+ int sIndAug=0, eIndAug=0;
+ float buf[CB_MEML+SUBL+2*LPC_FILTERORDER];
+
+
+ float invenergy[CB_EXPAND*128], energy[CB_EXPAND*128];
+ float *pp, *ppi=0, *ppo=0, *ppe=0;
+ float cbvectors[CB_MEML];
+ float tene, cene, cvec[SUBL];
+ float aug_vec[SUBL];
+
+ memset(cvec,0,SUBL*sizeof(float));
+
+ /* Determine size of codebook sections */
+
+ base_size=lMem-lTarget+1;
+
+ if (lTarget==SUBL) {
+ base_size=lMem-lTarget+1+lTarget/2;
+ }
+
+ /* setup buffer for weighting */
+
+ memcpy(buf,weightState,sizeof(float)*LPC_FILTERORDER);
+ memcpy(buf+LPC_FILTERORDER,mem,lMem*sizeof(float));
+ memcpy(buf+LPC_FILTERORDER+lMem,intarget,lTarget*sizeof(float));
+
+ /* weighting */
+
+ AllPoleFilter(buf+LPC_FILTERORDER, weightDenum,
+ lMem+lTarget, LPC_FILTERORDER);
+
+ /* Construct the codebook and target needed */
+
+ memcpy(target, buf+LPC_FILTERORDER+lMem, lTarget*sizeof(float));
+
+ tene=0.0;
+ for (i=0; i<lTarget; i++) {
+ tene+=target[i]*target[i];
+ }
+
+ /* Prepare search over one more codebook section. This section
+ is created by filtering the original buffer with a filter. */
+
+ filteredCBvecs(cbvectors, buf+LPC_FILTERORDER, lMem);
+
+ /* The Main Loop over stages */
+
+ for (stage=0; stage<nStages; stage++) {
+
+ range = search_rangeTbl[block][stage];
+
+ /* initialize search measure */
+
+ max_measure = (float)-10000000.0;
+ gain = (float)0.0;
+ best_index = 0;
+
+ /* Compute cross dot product between the target
+
+
+ and the CB memory */
+
+ crossDot=0.0;
+ pp=buf+LPC_FILTERORDER+lMem-lTarget;
+ for (j=0; j<lTarget; j++) {
+ crossDot += target[j]*(*pp++);
+ }
+
+ if (stage==0) {
+
+ /* Calculate energy in the first block of
+ 'lTarget' sampels. */
+ ppe = energy;
+ ppi = buf+LPC_FILTERORDER+lMem-lTarget-1;
+ ppo = buf+LPC_FILTERORDER+lMem-1;
+
+ *ppe=0.0;
+ pp=buf+LPC_FILTERORDER+lMem-lTarget;
+ for (j=0; j<lTarget; j++) {
+ *ppe+=(*pp)*(*pp);
+ pp++;
+ }
+
+ if (*ppe>0.0) {
+ invenergy[0] = (float) 1.0 / (*ppe + EPS);
+ } else {
+ invenergy[0] = (float) 0.0;
+ }
+ ppe++;
+
+ measure=(float)-10000000.0;
+
+ if (crossDot > 0.0) {
+ measure = crossDot*crossDot*invenergy[0];
+ }
+ }
+ else {
+ measure = crossDot*crossDot*invenergy[0];
+ }
+
+ /* check if measure is better */
+ ftmp = crossDot*invenergy[0];
+
+ if ((measure>max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
+ best_index = 0;
+ max_measure = measure;
+ gain = ftmp;
+ }
+
+ /* loop over the main first codebook section,
+ full search */
+
+ for (icount=1; icount<range; icount++) {
+
+ /* calculate measure */
+
+
+
+ crossDot=0.0;
+ pp = buf+LPC_FILTERORDER+lMem-lTarget-icount;
+
+ for (j=0; j<lTarget; j++) {
+ crossDot += target[j]*(*pp++);
+ }
+
+ if (stage==0) {
+ *ppe++ = energy[icount-1] + (*ppi)*(*ppi) -
+ (*ppo)*(*ppo);
+ ppo--;
+ ppi--;
+
+ if (energy[icount]>0.0) {
+ invenergy[icount] =
+ (float)1.0/(energy[icount]+EPS);
+ } else {
+ invenergy[icount] = (float) 0.0;
+ }
+
+ measure=(float)-10000000.0;
+
+ if (crossDot > 0.0) {
+ measure = crossDot*crossDot*invenergy[icount];
+ }
+ }
+ else {
+ measure = crossDot*crossDot*invenergy[icount];
+ }
+
+ /* check if measure is better */
+ ftmp = crossDot*invenergy[icount];
+
+ if ((measure>max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
+ best_index = icount;
+ max_measure = measure;
+ gain = ftmp;
+ }
+ }
+
+ /* Loop over augmented part in the first codebook
+ * section, full search.
+ * The vectors are interpolated.
+ */
+
+ if (lTarget==SUBL) {
+
+ /* Search for best possible cb vector and
+ compute the CB-vectors' energy. */
+ searchAugmentedCB(20, 39, stage, base_size-lTarget/2,
+ target, buf+LPC_FILTERORDER+lMem,
+ &max_measure, &best_index, &gain, energy,
+ invenergy);
+
+
+ }
+
+ /* set search range for following codebook sections */
+
+ base_index=best_index;
+
+ /* unrestricted search */
+
+ if (CB_RESRANGE == -1) {
+ sInd=0;
+ eInd=range-1;
+ sIndAug=20;
+ eIndAug=39;
+ }
+
+ /* restriced search around best index from first
+ codebook section */
+
+ else {
+ /* Initialize search indices */
+ sIndAug=0;
+ eIndAug=0;
+ sInd=base_index-CB_RESRANGE/2;
+ eInd=sInd+CB_RESRANGE;
+
+ if (lTarget==SUBL) {
+
+ if (sInd<0) {
+
+ sIndAug = 40 + sInd;
+ eIndAug = 39;
+ sInd=0;
+
+ } else if ( base_index < (base_size-20) ) {
+
+ if (eInd > range) {
+ sInd -= (eInd-range);
+ eInd = range;
+ }
+ } else { /* base_index >= (base_size-20) */
+
+ if (sInd < (base_size-20)) {
+ sIndAug = 20;
+ sInd = 0;
+ eInd = 0;
+ eIndAug = 19 + CB_RESRANGE;
+
+ if(eIndAug > 39) {
+ eInd = eIndAug-39;
+ eIndAug = 39;
+ }
+ } else {
+ sIndAug = 20 + sInd - (base_size-20);
+ eIndAug = 39;
+
+
+ sInd = 0;
+ eInd = CB_RESRANGE - (eIndAug-sIndAug+1);
+ }
+ }
+
+ } else { /* lTarget = 22 or 23 */
+
+ if (sInd < 0) {
+ eInd -= sInd;
+ sInd = 0;
+ }
+
+ if(eInd > range) {
+ sInd -= (eInd - range);
+ eInd = range;
+ }
+ }
+ }
+
+ /* search of higher codebook section */
+
+ /* index search range */
+ counter = sInd;
+ sInd += base_size;
+ eInd += base_size;
+
+
+ if (stage==0) {
+ ppe = energy+base_size;
+ *ppe=0.0;
+
+ pp=cbvectors+lMem-lTarget;
+ for (j=0; j<lTarget; j++) {
+ *ppe+=(*pp)*(*pp);
+ pp++;
+ }
+
+ ppi = cbvectors + lMem - 1 - lTarget;
+ ppo = cbvectors + lMem - 1;
+
+ for (j=0; j<(range-1); j++) {
+ *(ppe+1) = *ppe + (*ppi)*(*ppi) - (*ppo)*(*ppo);
+ ppo--;
+ ppi--;
+ ppe++;
+ }
+ }
+
+ /* loop over search range */
+
+ for (icount=sInd; icount<eInd; icount++) {
+
+ /* calculate measure */
+
+ crossDot=0.0;
+
+
+ pp=cbvectors + lMem - (counter++) - lTarget;
+
+ for (j=0;j<lTarget;j++) {
+ crossDot += target[j]*(*pp++);
+ }
+
+ if (energy[icount]>0.0) {
+ invenergy[icount] =(float)1.0/(energy[icount]+EPS);
+ } else {
+ invenergy[icount] =(float)0.0;
+ }
+
+ if (stage==0) {
+
+ measure=(float)-10000000.0;
+
+ if (crossDot > 0.0) {
+ measure = crossDot*crossDot*
+ invenergy[icount];
+ }
+ }
+ else {
+ measure = crossDot*crossDot*invenergy[icount];
+ }
+
+ /* check if measure is better */
+ ftmp = crossDot*invenergy[icount];
+
+ if ((measure>max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
+ best_index = icount;
+ max_measure = measure;
+ gain = ftmp;
+ }
+ }
+
+ /* Search the augmented CB inside the limited range. */
+
+ if ((lTarget==SUBL)&&(sIndAug!=0)) {
+ searchAugmentedCB(sIndAug, eIndAug, stage,
+ 2*base_size-20, target, cbvectors+lMem,
+ &max_measure, &best_index, &gain, energy,
+ invenergy);
+ }
+
+ /* record best index */
+
+ index[stage] = best_index;
+
+ /* gain quantization */
+
+ if (stage==0){
+
+ if (gain<0.0){
+ gain = 0.0;
+
+
+ }
+
+ if (gain>CB_MAXGAIN) {
+ gain = (float)CB_MAXGAIN;
+ }
+ gain = gainquant(gain, 1.0, 32, &gain_index[stage]);
+ }
+ else {
+ if (stage==1) {
+ gain = gainquant(gain, (float)fabs(gains[stage-1]),
+ 16, &gain_index[stage]);
+ } else {
+ gain = gainquant(gain, (float)fabs(gains[stage-1]),
+ 8, &gain_index[stage]);
+ }
+ }
+
+ /* Extract the best (according to measure)
+ codebook vector */
+
+ if (lTarget==(STATE_LEN-iLBCenc_inst->state_short_len)) {
+
+ if (index[stage]<base_size) {
+ pp=buf+LPC_FILTERORDER+lMem-lTarget-index[stage];
+ } else {
+ pp=cbvectors+lMem-lTarget-
+ index[stage]+base_size;
+ }
+ } else {
+
+ if (index[stage]<base_size) {
+ if (index[stage]<(base_size-20)) {
+ pp=buf+LPC_FILTERORDER+lMem-
+ lTarget-index[stage];
+ } else {
+ createAugmentedVec(index[stage]-base_size+40,
+ buf+LPC_FILTERORDER+lMem,aug_vec);
+ pp=aug_vec;
+ }
+ } else {
+ int filterno, position;
+
+ filterno=index[stage]/base_size;
+ position=index[stage]-filterno*base_size;
+
+
+ if (position<(base_size-20)) {
+ pp=cbvectors+filterno*lMem-lTarget-
+ index[stage]+filterno*base_size;
+ } else {
+ createAugmentedVec(
+ index[stage]-(filterno+1)*base_size+40,
+ cbvectors+filterno*lMem,aug_vec);
+ pp=aug_vec;
+
+
+ }
+ }
+ }
+
+ /* Subtract the best codebook vector, according
+ to measure, from the target vector */
+
+ for (j=0;j<lTarget;j++) {
+ cvec[j] += gain*(*pp);
+ target[j] -= gain*(*pp++);
+ }
+
+ /* record quantized gain */
+
+ gains[stage]=gain;
+
+ }/* end of Main Loop. for (stage=0;... */
+
+ /* Gain adjustment for energy matching */
+ cene=0.0;
+ for (i=0; i<lTarget; i++) {
+ cene+=cvec[i]*cvec[i];
+ }
+ j=gain_index[0];
+
+ for (i=gain_index[0]; i<32; i++) {
+ ftmp=cene*gain_sq5Tbl[i]*gain_sq5Tbl[i];
+
+ if ((ftmp<(tene*gains[0]*gains[0])) &&
+ (gain_sq5Tbl[j]<(2.0*gains[0]))) {
+ j=i;
+ }
+ }
+ gain_index[0]=j;
+}
+
+
diff --git a/trunk/codecs/ilbc/iCBSearch.h b/trunk/codecs/ilbc/iCBSearch.h
new file mode 100644
index 000000000..989f6ec03
--- /dev/null
+++ b/trunk/codecs/ilbc/iCBSearch.h
@@ -0,0 +1,35 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iCBSearch.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_ICBSEARCH_H
+#define __iLBC_ICBSEARCH_H
+
+void iCBSearch(
+ iLBC_Enc_Inst_t *iLBCenc_inst,
+ /* (i) the encoder state structure */
+ int *index, /* (o) Codebook indices */
+ int *gain_index,/* (o) Gain quantization indices */
+ float *intarget,/* (i) Target vector for encoding */
+ float *mem, /* (i) Buffer for codebook construction */
+ int lMem, /* (i) Length of buffer */
+ int lTarget, /* (i) Length of vector */
+ int nStages, /* (i) Number of codebook stages */
+ float *weightDenum, /* (i) weighting filter coefficients */
+
+
+ float *weightState, /* (i) weighting filter state */
+ int block /* (i) the sub-block number */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/iLBC_decode.c b/trunk/codecs/ilbc/iLBC_decode.c
new file mode 100644
index 000000000..22bfff792
--- /dev/null
+++ b/trunk/codecs/ilbc/iLBC_decode.c
@@ -0,0 +1,619 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iLBC_decode.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "iLBC_define.h"
+#include "iLBC_decode.h"
+#include "StateConstructW.h"
+#include "LPCdecode.h"
+#include "iCBConstruct.h"
+#include "doCPLC.h"
+#include "helpfun.h"
+#include "constants.h"
+#include "packing.h"
+#include "string.h"
+#include "enhancer.h"
+#include "hpOutput.h"
+#include "syntFilter.h"
+
+/*----------------------------------------------------------------*
+ * Initiation of decoder instance.
+ *---------------------------------------------------------------*/
+
+short initDecode( /* (o) Number of decoded
+ samples */
+ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */
+ int mode, /* (i) frame size mode */
+ int use_enhancer /* (i) 1 to use enhancer
+ 0 to run without
+ enhancer */
+){
+ int i;
+
+ iLBCdec_inst->mode = mode;
+
+ if (mode==30) {
+ iLBCdec_inst->blockl = BLOCKL_30MS;
+ iLBCdec_inst->nsub = NSUB_30MS;
+ iLBCdec_inst->nasub = NASUB_30MS;
+ iLBCdec_inst->lpc_n = LPC_N_30MS;
+
+
+ iLBCdec_inst->no_of_bytes = NO_OF_BYTES_30MS;
+ iLBCdec_inst->no_of_words = NO_OF_WORDS_30MS;
+ iLBCdec_inst->state_short_len=STATE_SHORT_LEN_30MS;
+ /* ULP init */
+ iLBCdec_inst->ULP_inst=&ULP_30msTbl;
+ }
+ else if (mode==20) {
+ iLBCdec_inst->blockl = BLOCKL_20MS;
+ iLBCdec_inst->nsub = NSUB_20MS;
+ iLBCdec_inst->nasub = NASUB_20MS;
+ iLBCdec_inst->lpc_n = LPC_N_20MS;
+ iLBCdec_inst->no_of_bytes = NO_OF_BYTES_20MS;
+ iLBCdec_inst->no_of_words = NO_OF_WORDS_20MS;
+ iLBCdec_inst->state_short_len=STATE_SHORT_LEN_20MS;
+ /* ULP init */
+ iLBCdec_inst->ULP_inst=&ULP_20msTbl;
+ }
+ else {
+ exit(2);
+ }
+
+ memset(iLBCdec_inst->syntMem, 0,
+ LPC_FILTERORDER*sizeof(float));
+ memcpy((*iLBCdec_inst).lsfdeqold, lsfmeanTbl,
+ LPC_FILTERORDER*sizeof(float));
+
+ memset(iLBCdec_inst->old_syntdenum, 0,
+ ((LPC_FILTERORDER + 1)*NSUB_MAX)*sizeof(float));
+ for (i=0; i<NSUB_MAX; i++)
+ iLBCdec_inst->old_syntdenum[i*(LPC_FILTERORDER+1)]=1.0;
+
+ iLBCdec_inst->last_lag = 20;
+
+ iLBCdec_inst->prevLag = 120;
+ iLBCdec_inst->per = 0.0;
+ iLBCdec_inst->consPLICount = 0;
+ iLBCdec_inst->prevPLI = 0;
+ iLBCdec_inst->prevLpc[0] = 1.0;
+ memset(iLBCdec_inst->prevLpc+1,0,
+ LPC_FILTERORDER*sizeof(float));
+ memset(iLBCdec_inst->prevResidual, 0, BLOCKL_MAX*sizeof(float));
+ iLBCdec_inst->seed=777;
+
+ memset(iLBCdec_inst->hpomem, 0, 4*sizeof(float));
+
+ iLBCdec_inst->use_enhancer = use_enhancer;
+ memset(iLBCdec_inst->enh_buf, 0, ENH_BUFL*sizeof(float));
+ for (i=0;i<ENH_NBLOCKS_TOT;i++)
+ iLBCdec_inst->enh_period[i]=(float)40.0;
+
+ iLBCdec_inst->prev_enh_pl = 0;
+
+ return (iLBCdec_inst->blockl);
+}
+
+
+
+/*----------------------------------------------------------------*
+ * frame residual decoder function (subrutine to iLBC_decode)
+ *---------------------------------------------------------------*/
+
+static void Decode(
+ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state
+ structure */
+ float *decresidual, /* (o) decoded residual frame */
+ int start, /* (i) location of start
+ state */
+ int idxForMax, /* (i) codebook index for the
+ maximum value */
+ int *idxVec, /* (i) codebook indexes for the
+ samples in the start
+ state */
+ float *syntdenum, /* (i) the decoded synthesis
+ filter coefficients */
+ int *cb_index, /* (i) the indexes for the
+ adaptive codebook */
+ int *gain_index, /* (i) the indexes for the
+ corresponding gains */
+ int *extra_cb_index, /* (i) the indexes for the
+ adaptive codebook part
+ of start state */
+ int *extra_gain_index, /* (i) the indexes for the
+ corresponding gains */
+ int state_first /* (i) 1 if non adaptive part
+ of start state comes
+ first 0 if that part
+ comes last */
+){
+ float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML];
+ int k, meml_gotten, Nfor, Nback, i;
+ int diff, start_pos;
+ int subcount, subframe;
+
+ diff = STATE_LEN - iLBCdec_inst->state_short_len;
+
+ if (state_first == 1) {
+ start_pos = (start-1)*SUBL;
+ } else {
+ start_pos = (start-1)*SUBL + diff;
+ }
+
+ /* decode scalar part of start state */
+
+ StateConstructW(idxForMax, idxVec,
+ &syntdenum[(start-1)*(LPC_FILTERORDER+1)],
+ &decresidual[start_pos], iLBCdec_inst->state_short_len);
+
+
+ if (state_first) { /* put adaptive part in the end */
+
+
+
+ /* setup memory */
+
+ memset(mem, 0,
+ (CB_MEML-iLBCdec_inst->state_short_len)*sizeof(float));
+ memcpy(mem+CB_MEML-iLBCdec_inst->state_short_len,
+ decresidual+start_pos,
+ iLBCdec_inst->state_short_len*sizeof(float));
+
+ /* construct decoded vector */
+
+ iCBConstruct(
+ &decresidual[start_pos+iLBCdec_inst->state_short_len],
+ extra_cb_index, extra_gain_index, mem+CB_MEML-stMemLTbl,
+ stMemLTbl, diff, CB_NSTAGES);
+
+ }
+ else {/* put adaptive part in the beginning */
+
+ /* create reversed vectors for prediction */
+
+ for (k=0; k<diff; k++) {
+ reverseDecresidual[k] =
+ decresidual[(start+1)*SUBL-1-
+ (k+iLBCdec_inst->state_short_len)];
+ }
+
+ /* setup memory */
+
+ meml_gotten = iLBCdec_inst->state_short_len;
+ for (k=0; k<meml_gotten; k++){
+ mem[CB_MEML-1-k] = decresidual[start_pos + k];
+ }
+ memset(mem, 0, (CB_MEML-k)*sizeof(float));
+
+ /* construct decoded vector */
+
+ iCBConstruct(reverseDecresidual, extra_cb_index,
+ extra_gain_index, mem+CB_MEML-stMemLTbl, stMemLTbl,
+ diff, CB_NSTAGES);
+
+ /* get decoded residual from reversed vector */
+
+ for (k=0; k<diff; k++) {
+ decresidual[start_pos-1-k] = reverseDecresidual[k];
+ }
+ }
+
+ /* counter for predicted sub-frames */
+
+ subcount=0;
+
+ /* forward prediction of sub-frames */
+
+ Nfor = iLBCdec_inst->nsub-start-1;
+
+
+
+ if ( Nfor > 0 ){
+
+ /* setup memory */
+
+ memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float));
+ memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL,
+ STATE_LEN*sizeof(float));
+
+ /* loop over sub-frames to encode */
+
+ for (subframe=0; subframe<Nfor; subframe++) {
+
+ /* construct decoded vector */
+
+ iCBConstruct(&decresidual[(start+1+subframe)*SUBL],
+ cb_index+subcount*CB_NSTAGES,
+ gain_index+subcount*CB_NSTAGES,
+ mem+CB_MEML-memLfTbl[subcount],
+ memLfTbl[subcount], SUBL, CB_NSTAGES);
+
+ /* update memory */
+
+ memmove(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
+ memcpy(mem+CB_MEML-SUBL,
+ &decresidual[(start+1+subframe)*SUBL],
+ SUBL*sizeof(float));
+
+ subcount++;
+
+ }
+
+ }
+
+ /* backward prediction of sub-frames */
+
+ Nback = start-1;
+
+ if ( Nback > 0 ) {
+
+ /* setup memory */
+
+ meml_gotten = SUBL*(iLBCdec_inst->nsub+1-start);
+
+ if ( meml_gotten > CB_MEML ) {
+ meml_gotten=CB_MEML;
+ }
+ for (k=0; k<meml_gotten; k++) {
+ mem[CB_MEML-1-k] = decresidual[(start-1)*SUBL + k];
+ }
+ memset(mem, 0, (CB_MEML-k)*sizeof(float));
+
+ /* loop over subframes to decode */
+
+
+
+ for (subframe=0; subframe<Nback; subframe++) {
+
+ /* construct decoded vector */
+
+ iCBConstruct(&reverseDecresidual[subframe*SUBL],
+ cb_index+subcount*CB_NSTAGES,
+ gain_index+subcount*CB_NSTAGES,
+ mem+CB_MEML-memLfTbl[subcount], memLfTbl[subcount],
+ SUBL, CB_NSTAGES);
+
+ /* update memory */
+
+ memmove(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
+ memcpy(mem+CB_MEML-SUBL,
+ &reverseDecresidual[subframe*SUBL],
+ SUBL*sizeof(float));
+
+ subcount++;
+ }
+
+ /* get decoded residual from reversed vector */
+
+ for (i=0; i<SUBL*Nback; i++)
+ decresidual[SUBL*Nback - i - 1] =
+ reverseDecresidual[i];
+ }
+}
+
+/*----------------------------------------------------------------*
+ * main decoder function
+ *---------------------------------------------------------------*/
+
+void iLBC_decode(
+ float *decblock, /* (o) decoded signal block */
+ unsigned char *bytes, /* (i) encoded signal bits */
+ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state
+ structure */
+ int mode /* (i) 0: bad packet, PLC,
+ 1: normal */
+){
+ float data[BLOCKL_MAX];
+ float lsfdeq[LPC_FILTERORDER*LPC_N_MAX];
+ float PLCresidual[BLOCKL_MAX], PLClpc[LPC_FILTERORDER + 1];
+ float zeros[BLOCKL_MAX], one[LPC_FILTERORDER + 1];
+ int k, i, start, idxForMax, pos, lastpart, ulp;
+ int lag, ilag;
+ float cc, maxcc;
+ int idxVec[STATE_LEN];
+ int check;
+ int gain_index[NASUB_MAX*CB_NSTAGES],
+ extra_gain_index[CB_NSTAGES];
+ int cb_index[CB_NSTAGES*NASUB_MAX], extra_cb_index[CB_NSTAGES];
+ int lsf_i[LSF_NSPLIT*LPC_N_MAX];
+ int state_first;
+
+
+ int last_bit;
+ unsigned char *pbytes;
+ float weightdenum[(LPC_FILTERORDER + 1)*NSUB_MAX];
+ int order_plus_one;
+ float syntdenum[NSUB_MAX*(LPC_FILTERORDER+1)];
+ float decresidual[BLOCKL_MAX];
+
+ if (mode>0) { /* the data are good */
+
+ /* decode data */
+
+ pbytes=bytes;
+ pos=0;
+
+ /* Set everything to zero before decoding */
+
+ for (k=0; k<LSF_NSPLIT*LPC_N_MAX; k++) {
+ lsf_i[k]=0;
+ }
+ start=0;
+ state_first=0;
+ idxForMax=0;
+ for (k=0; k<iLBCdec_inst->state_short_len; k++) {
+ idxVec[k]=0;
+ }
+ for (k=0; k<CB_NSTAGES; k++) {
+ extra_cb_index[k]=0;
+ }
+ for (k=0; k<CB_NSTAGES; k++) {
+ extra_gain_index[k]=0;
+ }
+ for (i=0; i<iLBCdec_inst->nasub; i++) {
+ for (k=0; k<CB_NSTAGES; k++) {
+ cb_index[i*CB_NSTAGES+k]=0;
+ }
+ }
+ for (i=0; i<iLBCdec_inst->nasub; i++) {
+ for (k=0; k<CB_NSTAGES; k++) {
+ gain_index[i*CB_NSTAGES+k]=0;
+ }
+ }
+
+ /* loop over ULP classes */
+
+ for (ulp=0; ulp<3; ulp++) {
+
+ /* LSF */
+ for (k=0; k<LSF_NSPLIT*iLBCdec_inst->lpc_n; k++){
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->lsf_bits[k][ulp], &pos);
+ packcombine(&lsf_i[k], lastpart,
+ iLBCdec_inst->ULP_inst->lsf_bits[k][ulp]);
+ }
+
+
+
+ /* Start block info */
+
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->start_bits[ulp], &pos);
+ packcombine(&start, lastpart,
+ iLBCdec_inst->ULP_inst->start_bits[ulp]);
+
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->startfirst_bits[ulp], &pos);
+ packcombine(&state_first, lastpart,
+ iLBCdec_inst->ULP_inst->startfirst_bits[ulp]);
+
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->scale_bits[ulp], &pos);
+ packcombine(&idxForMax, lastpart,
+ iLBCdec_inst->ULP_inst->scale_bits[ulp]);
+
+ for (k=0; k<iLBCdec_inst->state_short_len; k++) {
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->state_bits[ulp], &pos);
+ packcombine(idxVec+k, lastpart,
+ iLBCdec_inst->ULP_inst->state_bits[ulp]);
+ }
+
+ /* 23/22 (20ms/30ms) sample block */
+
+ for (k=0; k<CB_NSTAGES; k++) {
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->extra_cb_index[k][ulp],
+ &pos);
+ packcombine(extra_cb_index+k, lastpart,
+ iLBCdec_inst->ULP_inst->extra_cb_index[k][ulp]);
+ }
+ for (k=0; k<CB_NSTAGES; k++) {
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->extra_cb_gain[k][ulp],
+ &pos);
+ packcombine(extra_gain_index+k, lastpart,
+ iLBCdec_inst->ULP_inst->extra_cb_gain[k][ulp]);
+ }
+
+ /* The two/four (20ms/30ms) 40 sample sub-blocks */
+
+ for (i=0; i<iLBCdec_inst->nasub; i++) {
+ for (k=0; k<CB_NSTAGES; k++) {
+ unpack( &pbytes, &lastpart,
+ iLBCdec_inst->ULP_inst->cb_index[i][k][ulp],
+ &pos);
+ packcombine(cb_index+i*CB_NSTAGES+k, lastpart,
+ iLBCdec_inst->ULP_inst->cb_index[i][k][ulp]);
+ }
+ }
+
+ for (i=0; i<iLBCdec_inst->nasub; i++) {
+
+
+ for (k=0; k<CB_NSTAGES; k++) {
+ unpack( &pbytes, &lastpart,
+
+ iLBCdec_inst->ULP_inst->cb_gain[i][k][ulp],
+ &pos);
+ packcombine(gain_index+i*CB_NSTAGES+k, lastpart,
+ iLBCdec_inst->ULP_inst->cb_gain[i][k][ulp]);
+ }
+ }
+ }
+ /* Extract last bit. If it is 1 this indicates an
+ empty/lost frame */
+ unpack( &pbytes, &last_bit, 1, &pos);
+
+ /* Check for bit errors or empty/lost frames */
+ if (start<1)
+ mode = 0;
+ if (iLBCdec_inst->mode==20 && start>3)
+ mode = 0;
+ if (iLBCdec_inst->mode==30 && start>5)
+ mode = 0;
+ if (last_bit==1)
+ mode = 0;
+
+ if (mode==1) { /* No bit errors was detected,
+ continue decoding */
+
+ /* adjust index */
+ index_conv_dec(cb_index);
+
+ /* decode the lsf */
+
+ SimplelsfDEQ(lsfdeq, lsf_i, iLBCdec_inst->lpc_n);
+ check=LSF_check(lsfdeq, LPC_FILTERORDER,
+ iLBCdec_inst->lpc_n);
+ DecoderInterpolateLSF(syntdenum, weightdenum,
+ lsfdeq, LPC_FILTERORDER, iLBCdec_inst);
+
+ Decode(iLBCdec_inst, decresidual, start, idxForMax,
+ idxVec, syntdenum, cb_index, gain_index,
+ extra_cb_index, extra_gain_index,
+ state_first);
+
+ /* preparing the plc for a future loss! */
+
+ doThePLC(PLCresidual, PLClpc, 0, decresidual,
+ syntdenum +
+ (LPC_FILTERORDER + 1)*(iLBCdec_inst->nsub - 1),
+ (*iLBCdec_inst).last_lag, iLBCdec_inst);
+
+
+ memcpy(decresidual, PLCresidual,
+ iLBCdec_inst->blockl*sizeof(float));
+ }
+
+
+
+ }
+
+ if (mode == 0) {
+ /* the data is bad (either a PLC call
+ * was made or a severe bit error was detected)
+ */
+
+ /* packet loss conceal */
+
+ memset(zeros, 0, BLOCKL_MAX*sizeof(float));
+
+ one[0] = 1;
+ memset(one+1, 0, LPC_FILTERORDER*sizeof(float));
+
+ start=0;
+
+ doThePLC(PLCresidual, PLClpc, 1, zeros, one,
+ (*iLBCdec_inst).last_lag, iLBCdec_inst);
+ memcpy(decresidual, PLCresidual,
+ iLBCdec_inst->blockl*sizeof(float));
+
+ order_plus_one = LPC_FILTERORDER + 1;
+ for (i = 0; i < iLBCdec_inst->nsub; i++) {
+ memcpy(syntdenum+(i*order_plus_one), PLClpc,
+ order_plus_one*sizeof(float));
+ }
+ }
+
+ if (iLBCdec_inst->use_enhancer == 1) {
+
+ /* post filtering */
+
+ iLBCdec_inst->last_lag =
+ enhancerInterface(data, decresidual, iLBCdec_inst);
+
+ /* synthesis filtering */
+
+ if (iLBCdec_inst->mode==20) {
+ /* Enhancer has 40 samples delay */
+ i=0;
+ syntFilter(data + i*SUBL,
+ iLBCdec_inst->old_syntdenum +
+ (i+iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1),
+ SUBL, iLBCdec_inst->syntMem);
+ for (i=1; i < iLBCdec_inst->nsub; i++) {
+ syntFilter(data + i*SUBL,
+ syntdenum + (i-1)*(LPC_FILTERORDER+1),
+ SUBL, iLBCdec_inst->syntMem);
+ }
+ } else if (iLBCdec_inst->mode==30) {
+ /* Enhancer has 80 samples delay */
+ for (i=0; i < 2; i++) {
+ syntFilter(data + i*SUBL,
+
+
+ iLBCdec_inst->old_syntdenum +
+ (i+iLBCdec_inst->nsub-2)*(LPC_FILTERORDER+1),
+ SUBL, iLBCdec_inst->syntMem);
+ }
+ for (i=2; i < iLBCdec_inst->nsub; i++) {
+ syntFilter(data + i*SUBL,
+ syntdenum + (i-2)*(LPC_FILTERORDER+1), SUBL,
+ iLBCdec_inst->syntMem);
+ }
+ }
+
+ } else {
+
+ /* Find last lag */
+ lag = 20;
+ maxcc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL],
+ &decresidual[BLOCKL_MAX-ENH_BLOCKL-lag], ENH_BLOCKL);
+
+ for (ilag=21; ilag<120; ilag++) {
+ cc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL],
+ &decresidual[BLOCKL_MAX-ENH_BLOCKL-ilag],
+ ENH_BLOCKL);
+
+ if (cc > maxcc) {
+ maxcc = cc;
+ lag = ilag;
+ }
+ }
+ iLBCdec_inst->last_lag = lag;
+
+ /* copy data and run synthesis filter */
+
+ memcpy(data, decresidual,
+ iLBCdec_inst->blockl*sizeof(float));
+ for (i=0; i < iLBCdec_inst->nsub; i++) {
+ syntFilter(data + i*SUBL,
+ syntdenum + i*(LPC_FILTERORDER+1), SUBL,
+ iLBCdec_inst->syntMem);
+ }
+ }
+
+ /* high pass filtering on output if desired, otherwise
+ copy to out */
+
+ hpOutput(data, iLBCdec_inst->blockl,
+ decblock,iLBCdec_inst->hpomem);
+
+ /* memcpy(decblock,data,iLBCdec_inst->blockl*sizeof(float));*/
+
+ memcpy(iLBCdec_inst->old_syntdenum, syntdenum,
+
+ iLBCdec_inst->nsub*(LPC_FILTERORDER+1)*sizeof(float));
+
+ iLBCdec_inst->prev_enh_pl=0;
+
+
+
+ if (mode==0) { /* PLC was used */
+ iLBCdec_inst->prev_enh_pl=1;
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/iLBC_decode.h b/trunk/codecs/ilbc/iLBC_decode.h
new file mode 100644
index 000000000..0d5c905cd
--- /dev/null
+++ b/trunk/codecs/ilbc/iLBC_decode.h
@@ -0,0 +1,40 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iLBC_decode.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_ILBCDECODE_H
+#define __iLBC_ILBCDECODE_H
+
+#include "iLBC_define.h"
+
+short initDecode( /* (o) Number of decoded
+ samples */
+ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */
+ int mode, /* (i) frame size mode */
+ int use_enhancer /* (i) 1 to use enhancer
+ 0 to run without
+ enhancer */
+);
+
+void iLBC_decode(
+ float *decblock, /* (o) decoded signal block */
+ unsigned char *bytes, /* (i) encoded signal bits */
+ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state
+ structure */
+ int mode /* (i) 0: bad packet, PLC,
+ 1: normal */
+);
+
+
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/iLBC_define.h b/trunk/codecs/ilbc/iLBC_define.h
new file mode 100644
index 000000000..72cfbd585
--- /dev/null
+++ b/trunk/codecs/ilbc/iLBC_define.h
@@ -0,0 +1,200 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iLBC_define.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+#include <string.h>
+
+#ifndef __iLBC_ILBCDEFINE_H
+#define __iLBC_ILBCDEFINE_H
+
+/* general codec settings */
+
+#define BLOCKL_20MS 160
+#define BLOCKL_30MS 240
+#define BLOCKL_MAX 240
+#define NSUB_20MS 4
+#define NSUB_30MS 6
+#define NSUB_MAX 6
+#define NASUB_20MS 2
+#define NASUB_30MS 4
+#define NASUB_MAX 4
+#define SUBL 40
+#define STATE_LEN 80
+#define STATE_SHORT_LEN_30MS 58
+#define STATE_SHORT_LEN_20MS 57
+
+/* LPC settings */
+
+#define LPC_FILTERORDER 10
+#define LPC_CHIRP_SYNTDENUM (float)0.9025
+#define LPC_CHIRP_WEIGHTDENUM (float)0.4222
+#define LPC_LOOKBACK 60
+#define LPC_N_20MS 1
+#define LPC_N_30MS 2
+#define LPC_N_MAX 2
+#define LPC_ASYMDIFF 20
+#define LPC_BW (float)60.0
+#define LPC_WN (float)1.0001
+#define LSF_NSPLIT 3
+
+#define LSF_NUMBER_OF_STEPS 4
+#define LPC_HALFORDER (LPC_FILTERORDER/2)
+
+/* cb settings */
+
+#define CB_NSTAGES 3
+#define CB_EXPAND 2
+#define CB_MEML 147
+#define CB_FILTERLEN 2*4
+#define CB_HALFFILTERLEN 4
+#define CB_RESRANGE 34
+#define CB_MAXGAIN (float)1.3
+
+/* enhancer */
+
+#define ENH_BLOCKL 80 /* block length */
+#define ENH_BLOCKL_HALF (ENH_BLOCKL/2)
+#define ENH_HL 3 /* 2*ENH_HL+1 is number blocks
+ in said second sequence */
+#define ENH_SLOP 2 /* max difference estimated and
+ correct pitch period */
+#define ENH_PLOCSL 20 /* pitch-estimates and pitch-
+ locations buffer length */
+#define ENH_OVERHANG 2
+#define ENH_UPS0 4 /* upsampling rate */
+#define ENH_FL0 3 /* 2*FLO+1 is the length of
+ each filter */
+#define ENH_VECTL (ENH_BLOCKL+2*ENH_FL0)
+#define ENH_CORRDIM (2*ENH_SLOP+1)
+#define ENH_NBLOCKS (BLOCKL_MAX/ENH_BLOCKL)
+#define ENH_NBLOCKS_EXTRA 5
+#define ENH_NBLOCKS_TOT 8 /* ENH_NBLOCKS +
+ ENH_NBLOCKS_EXTRA */
+#define ENH_BUFL (ENH_NBLOCKS_TOT)*ENH_BLOCKL
+#define ENH_ALPHA0 (float)0.05
+
+/* Down sampling */
+
+#define FILTERORDER_DS 7
+#define DELAY_DS 3
+#define FACTOR_DS 2
+
+/* bit stream defs */
+
+#define NO_OF_BYTES_20MS 38
+#define NO_OF_BYTES_30MS 50
+#define NO_OF_WORDS_20MS 19
+#define NO_OF_WORDS_30MS 25
+#define STATE_BITS 3
+#define BYTE_LEN 8
+#define ULP_CLASSES 3
+
+/* help parameters */
+
+
+#define FLOAT_MAX (float)1.0e37
+#define EPS (float)2.220446049250313e-016
+#define PI (float)3.14159265358979323846
+#define MIN_SAMPLE -32768
+#define MAX_SAMPLE 32767
+#define TWO_PI (float)6.283185307
+#define PI2 (float)0.159154943
+
+/* type definition encoder instance */
+typedef struct iLBC_ULP_Inst_t_ {
+ int lsf_bits[6][ULP_CLASSES+2];
+ int start_bits[ULP_CLASSES+2];
+ int startfirst_bits[ULP_CLASSES+2];
+ int scale_bits[ULP_CLASSES+2];
+ int state_bits[ULP_CLASSES+2];
+ int extra_cb_index[CB_NSTAGES][ULP_CLASSES+2];
+ int extra_cb_gain[CB_NSTAGES][ULP_CLASSES+2];
+ int cb_index[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2];
+ int cb_gain[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2];
+} iLBC_ULP_Inst_t;
+
+/* type definition encoder instance */
+typedef struct iLBC_Enc_Inst_t_ {
+
+ /* flag for frame size mode */
+ int mode;
+
+ /* basic parameters for different frame sizes */
+ int blockl;
+ int nsub;
+ int nasub;
+ int no_of_bytes, no_of_words;
+ int lpc_n;
+ int state_short_len;
+ const iLBC_ULP_Inst_t *ULP_inst;
+
+ /* analysis filter state */
+ float anaMem[LPC_FILTERORDER];
+
+ /* old lsf parameters for interpolation */
+ float lsfold[LPC_FILTERORDER];
+ float lsfdeqold[LPC_FILTERORDER];
+
+ /* signal buffer for LP analysis */
+ float lpc_buffer[LPC_LOOKBACK + BLOCKL_MAX];
+
+ /* state of input HP filter */
+ float hpimem[4];
+
+} iLBC_Enc_Inst_t;
+
+/* type definition decoder instance */
+typedef struct iLBC_Dec_Inst_t_ {
+
+
+ /* flag for frame size mode */
+ int mode;
+
+ /* basic parameters for different frame sizes */
+ int blockl;
+ int nsub;
+ int nasub;
+ int no_of_bytes, no_of_words;
+ int lpc_n;
+ int state_short_len;
+ const iLBC_ULP_Inst_t *ULP_inst;
+
+ /* synthesis filter state */
+ float syntMem[LPC_FILTERORDER];
+
+ /* old LSF for interpolation */
+ float lsfdeqold[LPC_FILTERORDER];
+
+ /* pitch lag estimated in enhancer and used in PLC */
+ int last_lag;
+
+ /* PLC state information */
+ int prevLag, consPLICount, prevPLI, prev_enh_pl;
+ float prevLpc[LPC_FILTERORDER+1];
+ float prevResidual[NSUB_MAX*SUBL];
+ float per;
+ unsigned long seed;
+
+ /* previous synthesis filter parameters */
+ float old_syntdenum[(LPC_FILTERORDER + 1)*NSUB_MAX];
+
+ /* state of output HP filter */
+ float hpomem[4];
+
+ /* enhancer state information */
+ int use_enhancer;
+ float enh_buf[ENH_BUFL];
+ float enh_period[ENH_NBLOCKS_TOT];
+
+} iLBC_Dec_Inst_t;
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/iLBC_encode.c b/trunk/codecs/ilbc/iLBC_encode.c
new file mode 100644
index 000000000..bd1496498
--- /dev/null
+++ b/trunk/codecs/ilbc/iLBC_encode.c
@@ -0,0 +1,514 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iLBC_encode.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "iLBC_define.h"
+#include "iLBC_encode.h"
+#include "LPCencode.h"
+#include "FrameClassify.h"
+#include "StateSearchW.h"
+#include "StateConstructW.h"
+#include "helpfun.h"
+#include "constants.h"
+#include "packing.h"
+#include "iCBSearch.h"
+#include "iCBConstruct.h"
+#include "hpInput.h"
+#include "anaFilter.h"
+#include "syntFilter.h"
+
+/*----------------------------------------------------------------*
+ * Initiation of encoder instance.
+ *---------------------------------------------------------------*/
+
+short initEncode( /* (o) Number of bytes
+ encoded */
+ iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */
+ int mode /* (i) frame size mode */
+){
+ iLBCenc_inst->mode = mode;
+ if (mode==30) {
+ iLBCenc_inst->blockl = BLOCKL_30MS;
+ iLBCenc_inst->nsub = NSUB_30MS;
+
+
+ iLBCenc_inst->nasub = NASUB_30MS;
+ iLBCenc_inst->lpc_n = LPC_N_30MS;
+ iLBCenc_inst->no_of_bytes = NO_OF_BYTES_30MS;
+ iLBCenc_inst->no_of_words = NO_OF_WORDS_30MS;
+ iLBCenc_inst->state_short_len=STATE_SHORT_LEN_30MS;
+ /* ULP init */
+ iLBCenc_inst->ULP_inst=&ULP_30msTbl;
+ }
+ else if (mode==20) {
+ iLBCenc_inst->blockl = BLOCKL_20MS;
+ iLBCenc_inst->nsub = NSUB_20MS;
+ iLBCenc_inst->nasub = NASUB_20MS;
+ iLBCenc_inst->lpc_n = LPC_N_20MS;
+ iLBCenc_inst->no_of_bytes = NO_OF_BYTES_20MS;
+ iLBCenc_inst->no_of_words = NO_OF_WORDS_20MS;
+ iLBCenc_inst->state_short_len=STATE_SHORT_LEN_20MS;
+ /* ULP init */
+ iLBCenc_inst->ULP_inst=&ULP_20msTbl;
+ }
+ else {
+ exit(2);
+ }
+
+ memset((*iLBCenc_inst).anaMem, 0,
+ LPC_FILTERORDER*sizeof(float));
+ memcpy((*iLBCenc_inst).lsfold, lsfmeanTbl,
+ LPC_FILTERORDER*sizeof(float));
+ memcpy((*iLBCenc_inst).lsfdeqold, lsfmeanTbl,
+ LPC_FILTERORDER*sizeof(float));
+ memset((*iLBCenc_inst).lpc_buffer, 0,
+ (LPC_LOOKBACK+BLOCKL_MAX)*sizeof(float));
+ memset((*iLBCenc_inst).hpimem, 0, 4*sizeof(float));
+
+ return (iLBCenc_inst->no_of_bytes);
+}
+
+/*----------------------------------------------------------------*
+ * main encoder function
+ *---------------------------------------------------------------*/
+
+void iLBC_encode(
+ unsigned char *bytes, /* (o) encoded data bits iLBC */
+ float *block, /* (o) speech vector to
+ encode */
+ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder
+ state */
+){
+
+ float data[BLOCKL_MAX];
+ float residual[BLOCKL_MAX], reverseResidual[BLOCKL_MAX];
+
+ int start, idxForMax, idxVec[STATE_LEN];
+ float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML];
+ int n, k, meml_gotten, Nfor, Nback, i, pos;
+
+
+ int gain_index[CB_NSTAGES*NASUB_MAX],
+ extra_gain_index[CB_NSTAGES];
+ int cb_index[CB_NSTAGES*NASUB_MAX],extra_cb_index[CB_NSTAGES];
+ int lsf_i[LSF_NSPLIT*LPC_N_MAX];
+ unsigned char *pbytes;
+ int diff, start_pos, state_first;
+ float en1, en2;
+ int index, ulp, firstpart;
+ int subcount, subframe;
+ float weightState[LPC_FILTERORDER];
+ float syntdenum[NSUB_MAX*(LPC_FILTERORDER+1)];
+ float weightdenum[NSUB_MAX*(LPC_FILTERORDER+1)];
+ float decresidual[BLOCKL_MAX];
+
+ /* high pass filtering of input signal if such is not done
+ prior to calling this function */
+
+ hpInput(block, iLBCenc_inst->blockl,
+ data, (*iLBCenc_inst).hpimem);
+
+ /* otherwise simply copy */
+
+ /*memcpy(data,block,iLBCenc_inst->blockl*sizeof(float));*/
+
+ /* LPC of hp filtered input data */
+
+ LPCencode(syntdenum, weightdenum, lsf_i, data, iLBCenc_inst);
+
+
+ /* inverse filter to get residual */
+
+ for (n=0; n<iLBCenc_inst->nsub; n++) {
+ anaFilter(&data[n*SUBL], &syntdenum[n*(LPC_FILTERORDER+1)],
+ SUBL, &residual[n*SUBL], iLBCenc_inst->anaMem);
+ }
+
+ /* find state location */
+
+ start = FrameClassify(iLBCenc_inst, residual);
+
+ /* check if state should be in first or last part of the
+ two subframes */
+
+ diff = STATE_LEN - iLBCenc_inst->state_short_len;
+ en1 = 0;
+ index = (start-1)*SUBL;
+ for (i = 0; i < iLBCenc_inst->state_short_len; i++) {
+ en1 += residual[index+i]*residual[index+i];
+ }
+ en2 = 0;
+ index = (start-1)*SUBL+diff;
+ for (i = 0; i < iLBCenc_inst->state_short_len; i++) {
+ en2 += residual[index+i]*residual[index+i];
+ }
+
+
+
+
+ if (en1 > en2) {
+ state_first = 1;
+ start_pos = (start-1)*SUBL;
+ } else {
+ state_first = 0;
+ start_pos = (start-1)*SUBL + diff;
+ }
+
+ /* scalar quantization of state */
+
+ StateSearchW(iLBCenc_inst, &residual[start_pos],
+ &syntdenum[(start-1)*(LPC_FILTERORDER+1)],
+ &weightdenum[(start-1)*(LPC_FILTERORDER+1)], &idxForMax,
+ idxVec, iLBCenc_inst->state_short_len, state_first);
+
+ StateConstructW(idxForMax, idxVec,
+ &syntdenum[(start-1)*(LPC_FILTERORDER+1)],
+ &decresidual[start_pos], iLBCenc_inst->state_short_len);
+
+ /* predictive quantization in state */
+
+ if (state_first) { /* put adaptive part in the end */
+
+ /* setup memory */
+
+ memset(mem, 0,
+ (CB_MEML-iLBCenc_inst->state_short_len)*sizeof(float));
+ memcpy(mem+CB_MEML-iLBCenc_inst->state_short_len,
+ decresidual+start_pos,
+ iLBCenc_inst->state_short_len*sizeof(float));
+ memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
+
+ /* encode sub-frames */
+
+ iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index,
+ &residual[start_pos+iLBCenc_inst->state_short_len],
+ mem+CB_MEML-stMemLTbl,
+ stMemLTbl, diff, CB_NSTAGES,
+ &weightdenum[start*(LPC_FILTERORDER+1)],
+ weightState, 0);
+
+ /* construct decoded vector */
+
+ iCBConstruct(
+ &decresidual[start_pos+iLBCenc_inst->state_short_len],
+ extra_cb_index, extra_gain_index,
+ mem+CB_MEML-stMemLTbl,
+ stMemLTbl, diff, CB_NSTAGES);
+
+ }
+ else { /* put adaptive part in the beginning */
+
+
+
+ /* create reversed vectors for prediction */
+
+ for (k=0; k<diff; k++) {
+ reverseResidual[k] = residual[(start+1)*SUBL-1
+ -(k+iLBCenc_inst->state_short_len)];
+ }
+
+ /* setup memory */
+
+ meml_gotten = iLBCenc_inst->state_short_len;
+ for (k=0; k<meml_gotten; k++) {
+ mem[CB_MEML-1-k] = decresidual[start_pos + k];
+ }
+ memset(mem, 0, (CB_MEML-k)*sizeof(float));
+ memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
+
+ /* encode sub-frames */
+
+ iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index,
+ reverseResidual, mem+CB_MEML-stMemLTbl, stMemLTbl,
+ diff, CB_NSTAGES,
+ &weightdenum[(start-1)*(LPC_FILTERORDER+1)],
+ weightState, 0);
+
+ /* construct decoded vector */
+
+ iCBConstruct(reverseDecresidual, extra_cb_index,
+ extra_gain_index, mem+CB_MEML-stMemLTbl, stMemLTbl,
+ diff, CB_NSTAGES);
+
+ /* get decoded residual from reversed vector */
+
+ for (k=0; k<diff; k++) {
+ decresidual[start_pos-1-k] = reverseDecresidual[k];
+ }
+ }
+
+ /* counter for predicted sub-frames */
+
+ subcount=0;
+
+ /* forward prediction of sub-frames */
+
+ Nfor = iLBCenc_inst->nsub-start-1;
+
+
+ if ( Nfor > 0 ) {
+
+ /* setup memory */
+
+ memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float));
+ memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL,
+ STATE_LEN*sizeof(float));
+ memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
+
+
+
+ /* loop over sub-frames to encode */
+
+ for (subframe=0; subframe<Nfor; subframe++) {
+
+ /* encode sub-frame */
+
+ iCBSearch(iLBCenc_inst, cb_index+subcount*CB_NSTAGES,
+ gain_index+subcount*CB_NSTAGES,
+ &residual[(start+1+subframe)*SUBL],
+ mem+CB_MEML-memLfTbl[subcount],
+ memLfTbl[subcount], SUBL, CB_NSTAGES,
+ &weightdenum[(start+1+subframe)*
+ (LPC_FILTERORDER+1)],
+ weightState, subcount+1);
+
+ /* construct decoded vector */
+
+ iCBConstruct(&decresidual[(start+1+subframe)*SUBL],
+ cb_index+subcount*CB_NSTAGES,
+ gain_index+subcount*CB_NSTAGES,
+ mem+CB_MEML-memLfTbl[subcount],
+ memLfTbl[subcount], SUBL, CB_NSTAGES);
+
+ /* update memory */
+
+ memmove(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
+ memcpy(mem+CB_MEML-SUBL,
+ &decresidual[(start+1+subframe)*SUBL],
+ SUBL*sizeof(float));
+ memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
+
+ subcount++;
+ }
+ }
+
+
+ /* backward prediction of sub-frames */
+
+ Nback = start-1;
+
+
+ if ( Nback > 0 ) {
+
+ /* create reverse order vectors */
+
+ for (n=0; n<Nback; n++) {
+ for (k=0; k<SUBL; k++) {
+ reverseResidual[n*SUBL+k] =
+ residual[(start-1)*SUBL-1-n*SUBL-k];
+ reverseDecresidual[n*SUBL+k] =
+ decresidual[(start-1)*SUBL-1-n*SUBL-k];
+ }
+ }
+
+
+
+ /* setup memory */
+
+ meml_gotten = SUBL*(iLBCenc_inst->nsub+1-start);
+
+
+ if ( meml_gotten > CB_MEML ) {
+ meml_gotten=CB_MEML;
+ }
+ for (k=0; k<meml_gotten; k++) {
+ mem[CB_MEML-1-k] = decresidual[(start-1)*SUBL + k];
+ }
+ memset(mem, 0, (CB_MEML-k)*sizeof(float));
+ memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
+
+ /* loop over sub-frames to encode */
+
+ for (subframe=0; subframe<Nback; subframe++) {
+
+ /* encode sub-frame */
+
+ iCBSearch(iLBCenc_inst, cb_index+subcount*CB_NSTAGES,
+ gain_index+subcount*CB_NSTAGES,
+ &reverseResidual[subframe*SUBL],
+ mem+CB_MEML-memLfTbl[subcount],
+ memLfTbl[subcount], SUBL, CB_NSTAGES,
+ &weightdenum[(start-2-subframe)*
+ (LPC_FILTERORDER+1)],
+ weightState, subcount+1);
+
+ /* construct decoded vector */
+
+ iCBConstruct(&reverseDecresidual[subframe*SUBL],
+ cb_index+subcount*CB_NSTAGES,
+ gain_index+subcount*CB_NSTAGES,
+ mem+CB_MEML-memLfTbl[subcount],
+ memLfTbl[subcount], SUBL, CB_NSTAGES);
+
+ /* update memory */
+
+ memmove(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
+ memcpy(mem+CB_MEML-SUBL,
+ &reverseDecresidual[subframe*SUBL],
+ SUBL*sizeof(float));
+ memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
+
+ subcount++;
+
+ }
+
+ /* get decoded residual from reversed vector */
+
+ for (i=0; i<SUBL*Nback; i++) {
+ decresidual[SUBL*Nback - i - 1] =
+
+
+ reverseDecresidual[i];
+ }
+ }
+ /* end encoding part */
+
+ /* adjust index */
+ index_conv_enc(cb_index);
+
+ /* pack bytes */
+
+ pbytes=bytes;
+ pos=0;
+
+ /* loop over the 3 ULP classes */
+
+ for (ulp=0; ulp<3; ulp++) {
+
+ /* LSF */
+ for (k=0; k<LSF_NSPLIT*iLBCenc_inst->lpc_n; k++) {
+ packsplit(&lsf_i[k], &firstpart, &lsf_i[k],
+ iLBCenc_inst->ULP_inst->lsf_bits[k][ulp],
+ iLBCenc_inst->ULP_inst->lsf_bits[k][ulp]+
+ iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+1]+
+ iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->lsf_bits[k][ulp], &pos);
+ }
+
+ /* Start block info */
+
+ packsplit(&start, &firstpart, &start,
+ iLBCenc_inst->ULP_inst->start_bits[ulp],
+ iLBCenc_inst->ULP_inst->start_bits[ulp]+
+ iLBCenc_inst->ULP_inst->start_bits[ulp+1]+
+ iLBCenc_inst->ULP_inst->start_bits[ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->start_bits[ulp], &pos);
+
+ packsplit(&state_first, &firstpart, &state_first,
+ iLBCenc_inst->ULP_inst->startfirst_bits[ulp],
+ iLBCenc_inst->ULP_inst->startfirst_bits[ulp]+
+ iLBCenc_inst->ULP_inst->startfirst_bits[ulp+1]+
+ iLBCenc_inst->ULP_inst->startfirst_bits[ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->startfirst_bits[ulp], &pos);
+
+ packsplit(&idxForMax, &firstpart, &idxForMax,
+ iLBCenc_inst->ULP_inst->scale_bits[ulp],
+ iLBCenc_inst->ULP_inst->scale_bits[ulp]+
+ iLBCenc_inst->ULP_inst->scale_bits[ulp+1]+
+ iLBCenc_inst->ULP_inst->scale_bits[ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->scale_bits[ulp], &pos);
+
+
+
+ for (k=0; k<iLBCenc_inst->state_short_len; k++) {
+ packsplit(idxVec+k, &firstpart, idxVec+k,
+ iLBCenc_inst->ULP_inst->state_bits[ulp],
+ iLBCenc_inst->ULP_inst->state_bits[ulp]+
+ iLBCenc_inst->ULP_inst->state_bits[ulp+1]+
+ iLBCenc_inst->ULP_inst->state_bits[ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->state_bits[ulp], &pos);
+ }
+
+ /* 23/22 (20ms/30ms) sample block */
+
+ for (k=0;k<CB_NSTAGES;k++) {
+ packsplit(extra_cb_index+k, &firstpart,
+ extra_cb_index+k,
+ iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp],
+ iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp]+
+ iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+1]+
+ iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp],
+ &pos);
+ }
+
+ for (k=0;k<CB_NSTAGES;k++) {
+ packsplit(extra_gain_index+k, &firstpart,
+ extra_gain_index+k,
+ iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp],
+ iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp]+
+ iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+1]+
+ iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp],
+ &pos);
+ }
+
+ /* The two/four (20ms/30ms) 40 sample sub-blocks */
+
+ for (i=0; i<iLBCenc_inst->nasub; i++) {
+ for (k=0; k<CB_NSTAGES; k++) {
+ packsplit(cb_index+i*CB_NSTAGES+k, &firstpart,
+ cb_index+i*CB_NSTAGES+k,
+ iLBCenc_inst->ULP_inst->cb_index[i][k][ulp],
+ iLBCenc_inst->ULP_inst->cb_index[i][k][ulp]+
+ iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+1]+
+ iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->cb_index[i][k][ulp],
+ &pos);
+ }
+ }
+
+ for (i=0; i<iLBCenc_inst->nasub; i++) {
+ for (k=0; k<CB_NSTAGES; k++) {
+
+
+ packsplit(gain_index+i*CB_NSTAGES+k, &firstpart,
+ gain_index+i*CB_NSTAGES+k,
+ iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp],
+ iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp]+
+ iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+1]+
+ iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+2]);
+ dopack( &pbytes, firstpart,
+ iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp],
+ &pos);
+ }
+ }
+ }
+
+ /* set the last bit to zero (otherwise the decoder
+ will treat it as a lost frame) */
+ dopack( &pbytes, 0, 1, &pos);
+}
+
+
+
diff --git a/trunk/codecs/ilbc/iLBC_encode.h b/trunk/codecs/ilbc/iLBC_encode.h
new file mode 100644
index 000000000..3628e7545
--- /dev/null
+++ b/trunk/codecs/ilbc/iLBC_encode.h
@@ -0,0 +1,37 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ iLBC_encode.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_ILBCENCODE_H
+#define __iLBC_ILBCENCODE_H
+
+#include "iLBC_define.h"
+
+short initEncode( /* (o) Number of bytes
+ encoded */
+ iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */
+ int mode /* (i) frame size mode */
+);
+
+void iLBC_encode(
+
+
+ unsigned char *bytes, /* (o) encoded data bits iLBC */
+ float *block, /* (o) speech vector to
+ encode */
+ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder
+ state */
+);
+
+#endif
+
+
+
diff --git a/trunk/codecs/ilbc/libilbc.vcproj b/trunk/codecs/ilbc/libilbc.vcproj
new file mode 100644
index 000000000..4d83fdc9f
--- /dev/null
+++ b/trunk/codecs/ilbc/libilbc.vcproj
@@ -0,0 +1,353 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="libilbc"
+ ProjectGUID="{989BB874-7AF1-44CB-8E5C-CC8113D267E8}"
+ RootNamespace="libilbc"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\anaFilter.c"
+ >
+ </File>
+ <File
+ RelativePath=".\constants.c"
+ >
+ </File>
+ <File
+ RelativePath=".\createCB.c"
+ >
+ </File>
+ <File
+ RelativePath=".\doCPLC.c"
+ >
+ </File>
+ <File
+ RelativePath=".\enhancer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\filter.c"
+ >
+ </File>
+ <File
+ RelativePath=".\FrameClassify.c"
+ >
+ </File>
+ <File
+ RelativePath=".\gainquant.c"
+ >
+ </File>
+ <File
+ RelativePath=".\getCBvec.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helpfun.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hpInput.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hpOutput.c"
+ >
+ </File>
+ <File
+ RelativePath=".\iCBConstruct.c"
+ >
+ </File>
+ <File
+ RelativePath=".\iCBSearch.c"
+ >
+ </File>
+ <File
+ RelativePath=".\iLBC_decode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\iLBC_encode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\LPCdecode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\LPCencode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\lsf.c"
+ >
+ </File>
+ <File
+ RelativePath=".\packing.c"
+ >
+ </File>
+ <File
+ RelativePath=".\StateConstructW.c"
+ >
+ </File>
+ <File
+ RelativePath=".\StateSearchW.c"
+ >
+ </File>
+ <File
+ RelativePath=".\syntFilter.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\anaFilter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\constants.h"
+ >
+ </File>
+ <File
+ RelativePath=".\createCB.h"
+ >
+ </File>
+ <File
+ RelativePath=".\doCPLC.h"
+ >
+ </File>
+ <File
+ RelativePath=".\enhancer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\FrameClassify.h"
+ >
+ </File>
+ <File
+ RelativePath=".\gainquant.h"
+ >
+ </File>
+ <File
+ RelativePath=".\getCBvec.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helpfun.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hpInput.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hpOutput.h"
+ >
+ </File>
+ <File
+ RelativePath=".\iCBConstruct.h"
+ >
+ </File>
+ <File
+ RelativePath=".\iCBSearch.h"
+ >
+ </File>
+ <File
+ RelativePath=".\iLBC_decode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\iLBC_define.h"
+ >
+ </File>
+ <File
+ RelativePath=".\iLBC_encode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\LPCdecode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\LPCencode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\lsf.h"
+ >
+ </File>
+ <File
+ RelativePath=".\packing.h"
+ >
+ </File>
+ <File
+ RelativePath=".\StateConstructW.h"
+ >
+ </File>
+ <File
+ RelativePath=".\StateSearchW.h"
+ >
+ </File>
+ <File
+ RelativePath=".\syntFilter.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/trunk/codecs/ilbc/lsf.c b/trunk/codecs/ilbc/lsf.c
new file mode 100644
index 000000000..055c21cb5
--- /dev/null
+++ b/trunk/codecs/ilbc/lsf.c
@@ -0,0 +1,264 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ lsf.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <string.h>
+#include <math.h>
+
+#include "iLBC_define.h"
+#include "lsf.h"
+
+/*----------------------------------------------------------------*
+ * conversion from lpc coefficients to lsf coefficients
+ *---------------------------------------------------------------*/
+
+void a2lsf(
+ float *freq,/* (o) lsf coefficients */
+ float *a /* (i) lpc coefficients */
+){
+ float steps[LSF_NUMBER_OF_STEPS] =
+ {(float)0.00635, (float)0.003175, (float)0.0015875,
+ (float)0.00079375};
+ float step;
+ int step_idx;
+ int lsp_index;
+ float p[LPC_HALFORDER];
+ float q[LPC_HALFORDER];
+ float p_pre[LPC_HALFORDER];
+
+
+ float q_pre[LPC_HALFORDER];
+ float old_p, old_q, *old;
+ float *pq_coef;
+ float omega, old_omega;
+ int i;
+ float hlp, hlp1, hlp2, hlp3, hlp4, hlp5;
+
+ for (i=0; i<LPC_HALFORDER; i++) {
+ p[i] = (float)-1.0 * (a[i + 1] + a[LPC_FILTERORDER - i]);
+ q[i] = a[LPC_FILTERORDER - i] - a[i + 1];
+ }
+
+ p_pre[0] = (float)-1.0 - p[0];
+ p_pre[1] = - p_pre[0] - p[1];
+ p_pre[2] = - p_pre[1] - p[2];
+ p_pre[3] = - p_pre[2] - p[3];
+ p_pre[4] = - p_pre[3] - p[4];
+ p_pre[4] = p_pre[4] / 2;
+
+ q_pre[0] = (float)1.0 - q[0];
+ q_pre[1] = q_pre[0] - q[1];
+ q_pre[2] = q_pre[1] - q[2];
+ q_pre[3] = q_pre[2] - q[3];
+ q_pre[4] = q_pre[3] - q[4];
+ q_pre[4] = q_pre[4] / 2;
+
+ omega = 0.0;
+ old_omega = 0.0;
+
+ old_p = FLOAT_MAX;
+ old_q = FLOAT_MAX;
+
+ /* Here we loop through lsp_index to find all the
+ LPC_FILTERORDER roots for omega. */
+
+ for (lsp_index = 0; lsp_index<LPC_FILTERORDER; lsp_index++) {
+
+ /* Depending on lsp_index being even or odd, we
+ alternatively solve the roots for the two LSP equations. */
+
+
+ if ((lsp_index & 0x1) == 0) {
+ pq_coef = p_pre;
+ old = &old_p;
+ } else {
+ pq_coef = q_pre;
+ old = &old_q;
+ }
+
+ /* Start with low resolution grid */
+
+ for (step_idx = 0, step = steps[step_idx];
+ step_idx < LSF_NUMBER_OF_STEPS;){
+
+
+
+ /* cos(10piw) + pq(0)cos(8piw) + pq(1)cos(6piw) +
+ pq(2)cos(4piw) + pq(3)cod(2piw) + pq(4) */
+
+ hlp = (float)cos(omega * TWO_PI);
+ hlp1 = (float)2.0 * hlp + pq_coef[0];
+ hlp2 = (float)2.0 * hlp * hlp1 - (float)1.0 +
+ pq_coef[1];
+ hlp3 = (float)2.0 * hlp * hlp2 - hlp1 + pq_coef[2];
+ hlp4 = (float)2.0 * hlp * hlp3 - hlp2 + pq_coef[3];
+ hlp5 = hlp * hlp4 - hlp3 + pq_coef[4];
+
+
+ if (((hlp5 * (*old)) <= 0.0) || (omega >= 0.5)){
+
+ if (step_idx == (LSF_NUMBER_OF_STEPS - 1)){
+
+ if (fabs(hlp5) >= fabs(*old)) {
+ freq[lsp_index] = omega - step;
+ } else {
+ freq[lsp_index] = omega;
+ }
+
+
+ if ((*old) >= 0.0){
+ *old = (float)-1.0 * FLOAT_MAX;
+ } else {
+ *old = FLOAT_MAX;
+ }
+
+ omega = old_omega;
+ step_idx = 0;
+
+ step_idx = LSF_NUMBER_OF_STEPS;
+ } else {
+
+ if (step_idx == 0) {
+ old_omega = omega;
+ }
+
+ step_idx++;
+ omega -= steps[step_idx];
+
+ /* Go back one grid step */
+
+ step = steps[step_idx];
+ }
+ } else {
+
+ /* increment omega until they are of different sign,
+ and we know there is at least one root between omega
+ and old_omega */
+ *old = hlp5;
+ omega += step;
+ }
+
+
+ }
+ }
+
+ for (i = 0; i<LPC_FILTERORDER; i++) {
+ freq[i] = freq[i] * TWO_PI;
+ }
+}
+
+/*----------------------------------------------------------------*
+ * conversion from lsf coefficients to lpc coefficients
+ *---------------------------------------------------------------*/
+
+void lsf2a(
+ float *a_coef, /* (o) lpc coefficients */
+ float *freq /* (i) lsf coefficients */
+){
+ int i, j;
+ float hlp;
+ float p[LPC_HALFORDER], q[LPC_HALFORDER];
+ float a[LPC_HALFORDER + 1], a1[LPC_HALFORDER],
+ a2[LPC_HALFORDER];
+ float b[LPC_HALFORDER + 1], b1[LPC_HALFORDER],
+ b2[LPC_HALFORDER];
+
+ for (i=0; i<LPC_FILTERORDER; i++) {
+ freq[i] = freq[i] * PI2;
+ }
+
+ /* Check input for ill-conditioned cases. This part is not
+ found in the TIA standard. It involves the following 2 IF
+ blocks. If "freq" is judged ill-conditioned, then we first
+ modify freq[0] and freq[LPC_HALFORDER-1] (normally
+ LPC_HALFORDER = 10 for LPC applications), then we adjust
+ the other "freq" values slightly */
+
+
+ if ((freq[0] <= 0.0) || (freq[LPC_FILTERORDER - 1] >= 0.5)){
+
+
+ if (freq[0] <= 0.0) {
+ freq[0] = (float)0.022;
+ }
+
+
+ if (freq[LPC_FILTERORDER - 1] >= 0.5) {
+ freq[LPC_FILTERORDER - 1] = (float)0.499;
+ }
+
+ hlp = (freq[LPC_FILTERORDER - 1] - freq[0]) /
+ (float) (LPC_FILTERORDER - 1);
+
+ for (i=1; i<LPC_FILTERORDER; i++) {
+ freq[i] = freq[i - 1] + hlp;
+ }
+
+
+ }
+
+ memset(a1, 0, LPC_HALFORDER*sizeof(float));
+ memset(a2, 0, LPC_HALFORDER*sizeof(float));
+ memset(b1, 0, LPC_HALFORDER*sizeof(float));
+ memset(b2, 0, LPC_HALFORDER*sizeof(float));
+ memset(a, 0, (LPC_HALFORDER+1)*sizeof(float));
+ memset(b, 0, (LPC_HALFORDER+1)*sizeof(float));
+
+ /* p[i] and q[i] compute cos(2*pi*omega_{2j}) and
+ cos(2*pi*omega_{2j-1} in eqs. 4.2.2.2-1 and 4.2.2.2-2.
+ Note that for this code p[i] specifies the coefficients
+ used in .Q_A(z) while q[i] specifies the coefficients used
+ in .P_A(z) */
+
+ for (i=0; i<LPC_HALFORDER; i++) {
+ p[i] = (float)cos(TWO_PI * freq[2 * i]);
+ q[i] = (float)cos(TWO_PI * freq[2 * i + 1]);
+ }
+
+ a[0] = 0.25;
+ b[0] = 0.25;
+
+ for (i= 0; i<LPC_HALFORDER; i++) {
+ a[i + 1] = a[i] - 2 * p[i] * a1[i] + a2[i];
+ b[i + 1] = b[i] - 2 * q[i] * b1[i] + b2[i];
+ a2[i] = a1[i];
+ a1[i] = a[i];
+ b2[i] = b1[i];
+ b1[i] = b[i];
+ }
+
+ for (j=0; j<LPC_FILTERORDER; j++) {
+
+ if (j == 0) {
+ a[0] = 0.25;
+ b[0] = -0.25;
+ } else {
+ a[0] = b[0] = 0.0;
+ }
+
+ for (i=0; i<LPC_HALFORDER; i++) {
+ a[i + 1] = a[i] - 2 * p[i] * a1[i] + a2[i];
+ b[i + 1] = b[i] - 2 * q[i] * b1[i] + b2[i];
+ a2[i] = a1[i];
+ a1[i] = a[i];
+ b2[i] = b1[i];
+ b1[i] = b[i];
+ }
+
+ a_coef[j + 1] = 2 * (a[LPC_HALFORDER] + b[LPC_HALFORDER]);
+ }
+
+ a_coef[0] = 1.0;
+
+
+}
+
+
diff --git a/trunk/codecs/ilbc/lsf.h b/trunk/codecs/ilbc/lsf.h
new file mode 100644
index 000000000..ec43cacda
--- /dev/null
+++ b/trunk/codecs/ilbc/lsf.h
@@ -0,0 +1,30 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ lsf.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+
+
+******************************************************************/
+
+#ifndef __iLBC_LSF_H
+#define __iLBC_LSF_H
+
+void a2lsf(
+ float *freq,/* (o) lsf coefficients */
+ float *a /* (i) lpc coefficients */
+);
+
+void lsf2a(
+ float *a_coef, /* (o) lpc coefficients */
+ float *freq /* (i) lsf coefficients */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/packing.c b/trunk/codecs/ilbc/packing.c
new file mode 100644
index 000000000..3071032e0
--- /dev/null
+++ b/trunk/codecs/ilbc/packing.c
@@ -0,0 +1,175 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ packing.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "iLBC_define.h"
+#include "constants.h"
+#include "helpfun.h"
+#include "packing.h"
+#include "string.h"
+
+/*----------------------------------------------------------------*
+ * splitting an integer into first most significant bits and
+ * remaining least significant bits
+ *---------------------------------------------------------------*/
+
+void packsplit(
+ int *index, /* (i) the value to split */
+ int *firstpart, /* (o) the value specified by most
+ significant bits */
+ int *rest, /* (o) the value specified by least
+ significant bits */
+ int bitno_firstpart, /* (i) number of bits in most
+ significant part */
+ int bitno_total /* (i) number of bits in full range
+ of value */
+){
+ int bitno_rest = bitno_total-bitno_firstpart;
+
+
+
+ *firstpart = *index>>(bitno_rest);
+ *rest = *index-(*firstpart<<(bitno_rest));
+}
+
+/*----------------------------------------------------------------*
+ * combining a value corresponding to msb's with a value
+ * corresponding to lsb's
+ *---------------------------------------------------------------*/
+
+void packcombine(
+ int *index, /* (i/o) the msb value in the
+ combined value out */
+ int rest, /* (i) the lsb value */
+ int bitno_rest /* (i) the number of bits in the
+ lsb part */
+){
+ *index = *index<<bitno_rest;
+ *index += rest;
+}
+
+/*----------------------------------------------------------------*
+ * packing of bits into bitstream, i.e., vector of bytes
+ *---------------------------------------------------------------*/
+
+void dopack(
+ unsigned char **bitstream, /* (i/o) on entrance pointer to
+ place in bitstream to pack
+ new data, on exit pointer
+ to place in bitstream to
+ pack future data */
+ int index, /* (i) the value to pack */
+ int bitno, /* (i) the number of bits that the
+ value will fit within */
+ int *pos /* (i/o) write position in the
+ current byte */
+){
+ int posLeft;
+
+ /* Clear the bits before starting in a new byte */
+
+ if ((*pos)==0) {
+ **bitstream=0;
+ }
+
+ while (bitno>0) {
+
+ /* Jump to the next byte if end of this byte is reached*/
+
+ if (*pos==8) {
+ *pos=0;
+ (*bitstream)++;
+ **bitstream=0;
+ }
+
+
+
+ posLeft=8-(*pos);
+
+ /* Insert index into the bitstream */
+
+ if (bitno <= posLeft) {
+ **bitstream |= (unsigned char)(index<<(posLeft-bitno));
+ *pos+=bitno;
+ bitno=0;
+ } else {
+ **bitstream |= (unsigned char)(index>>(bitno-posLeft));
+
+ *pos=8;
+ index-=((index>>(bitno-posLeft))<<(bitno-posLeft));
+
+ bitno-=posLeft;
+ }
+ }
+}
+
+/*----------------------------------------------------------------*
+ * unpacking of bits from bitstream, i.e., vector of bytes
+ *---------------------------------------------------------------*/
+
+void unpack(
+ unsigned char **bitstream, /* (i/o) on entrance pointer to
+ place in bitstream to
+ unpack new data from, on
+ exit pointer to place in
+ bitstream to unpack future
+ data from */
+ int *index, /* (o) resulting value */
+ int bitno, /* (i) number of bits used to
+ represent the value */
+ int *pos /* (i/o) read position in the
+ current byte */
+){
+ int BitsLeft;
+
+ *index=0;
+
+ while (bitno>0) {
+
+ /* move forward in bitstream when the end of the
+ byte is reached */
+
+ if (*pos==8) {
+ *pos=0;
+ (*bitstream)++;
+ }
+
+ BitsLeft=8-(*pos);
+
+ /* Extract bits to index */
+
+
+
+ if (BitsLeft>=bitno) {
+ *index+=((((**bitstream)<<(*pos)) & 0xFF)>>(8-bitno));
+
+ *pos+=bitno;
+ bitno=0;
+ } else {
+
+ if ((8-bitno)>0) {
+ *index+=((((**bitstream)<<(*pos)) & 0xFF)>>
+ (8-bitno));
+ *pos=8;
+ } else {
+ *index+=(((int)(((**bitstream)<<(*pos)) & 0xFF))<<
+ (bitno-8));
+ *pos=8;
+ }
+ bitno-=BitsLeft;
+ }
+ }
+}
+
+
diff --git a/trunk/codecs/ilbc/packing.h b/trunk/codecs/ilbc/packing.h
new file mode 100644
index 000000000..ed114b1c1
--- /dev/null
+++ b/trunk/codecs/ilbc/packing.h
@@ -0,0 +1,67 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ packing.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __PACKING_H
+#define __PACKING_H
+
+void packsplit(
+ int *index, /* (i) the value to split */
+ int *firstpart, /* (o) the value specified by most
+ significant bits */
+ int *rest, /* (o) the value specified by least
+ significant bits */
+ int bitno_firstpart, /* (i) number of bits in most
+ significant part */
+ int bitno_total /* (i) number of bits in full range
+ of value */
+);
+
+void packcombine(
+ int *index, /* (i/o) the msb value in the
+ combined value out */
+ int rest, /* (i) the lsb value */
+ int bitno_rest /* (i) the number of bits in the
+ lsb part */
+);
+
+void dopack(
+ unsigned char **bitstream, /* (i/o) on entrance pointer to
+ place in bitstream to pack
+ new data, on exit pointer
+ to place in bitstream to
+ pack future data */
+ int index, /* (i) the value to pack */
+ int bitno, /* (i) the number of bits that the
+ value will fit within */
+ int *pos /* (i/o) write position in the
+ current byte */
+);
+
+void unpack(
+ unsigned char **bitstream, /* (i/o) on entrance pointer to
+
+
+ place in bitstream to
+ unpack new data from, on
+ exit pointer to place in
+ bitstream to unpack future
+ data from */
+ int *index, /* (o) resulting value */
+ int bitno, /* (i) number of bits used to
+ represent the value */
+ int *pos /* (i/o) read position in the
+ current byte */
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc/syntFilter.c b/trunk/codecs/ilbc/syntFilter.c
new file mode 100644
index 000000000..bb7f7a559
--- /dev/null
+++ b/trunk/codecs/ilbc/syntFilter.c
@@ -0,0 +1,108 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ syntFilter.c
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#include "iLBC_define.h"
+#include "syntFilter.h"
+
+/*----------------------------------------------------------------*
+ * LP synthesis filter.
+ *---------------------------------------------------------------*/
+
+void syntFilter(
+ float *Out, /* (i/o) Signal to be filtered */
+ float *a, /* (i) LP parameters */
+ int len, /* (i) Length of signal */
+ float *mem /* (i/o) Filter state */
+){
+ int i, j;
+ float *po, *pi, *pa, *pm;
+
+ po=Out;
+
+ /* Filter first part using memory from past */
+
+ for (i=0; i<LPC_FILTERORDER; i++) {
+ pi=&Out[i-1];
+ pa=&a[1];
+ pm=&mem[LPC_FILTERORDER-1];
+ for (j=1; j<=i; j++) {
+ *po-=(*pa++)*(*pi--);
+ }
+ for (j=i+1; j<LPC_FILTERORDER+1; j++) {
+ *po-=(*pa++)*(*pm--);
+ }
+ po++;
+ }
+
+ /* Filter last part where the state is entierly in
+ the output vector */
+
+ for (i=LPC_FILTERORDER; i<len; i++) {
+
+
+ pi=&Out[i-1];
+ pa=&a[1];
+ for (j=1; j<LPC_FILTERORDER+1; j++) {
+ *po-=(*pa++)*(*pi--);
+ }
+ po++;
+ }
+
+ /* Update state vector */
+
+ memcpy(mem, &Out[len-LPC_FILTERORDER],
+ LPC_FILTERORDER*sizeof(float));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/trunk/codecs/ilbc/syntFilter.h b/trunk/codecs/ilbc/syntFilter.h
new file mode 100644
index 000000000..c155fdb0b
--- /dev/null
+++ b/trunk/codecs/ilbc/syntFilter.h
@@ -0,0 +1,27 @@
+
+/******************************************************************
+
+ iLBC Speech Coder ANSI-C Source Code
+
+ syntFilter.h
+
+ Copyright (C) The Internet Society (2004).
+ All Rights Reserved.
+
+******************************************************************/
+
+#ifndef __iLBC_SYNTFILTER_H
+#define __iLBC_SYNTFILTER_H
+
+void syntFilter(
+ float *Out, /* (i/o) Signal to be filtered */
+ float *a, /* (i) LP parameters */
+ int len, /* (i) Length of signal */
+ float *mem /* (i/o) Filter state */
+
+
+);
+
+#endif
+
+
diff --git a/trunk/codecs/ilbc_slin_ex.h b/trunk/codecs/ilbc_slin_ex.h
new file mode 100644
index 000000000..b9f4bd4e8
--- /dev/null
+++ b/trunk/codecs/ilbc_slin_ex.h
@@ -0,0 +1,17 @@
+/*! \file
+ * \brief Raw 8-bit data
+ *
+ * Source: ilbc.out
+ *
+ * Copyright (C) 1999-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char ilbc_slin_ex[] = {
+0xff, 0xa0, 0xff, 0xfa, 0xf, 0x60, 0x12, 0x11, 0xa2, 0x47,
+0x22, 0x8c, 00, 00, 0x1, 0x2, 0x80, 0x43, 0xa0, 0x40,
+0x33, 0xff, 0xcf, 0xc0, 0xf3, 0xf3, 0x3f, 0x8f, 0x3f, 0xff,
+0xff, 0xff, 0xff, 0xfc, 0xf9, 0xe5, 0x55, 0x78, 0xb, 0xca,
+0xe1, 0x27, 0x94, 0x7b, 0xa8, 0x91, 0x2c, 0x36, 0x8, 0x56 };
diff --git a/trunk/codecs/log2comp.h b/trunk/codecs/log2comp.h
new file mode 100644
index 000000000..56f2d8305
--- /dev/null
+++ b/trunk/codecs/log2comp.h
@@ -0,0 +1,74 @@
+/*! \file
+ * \brief log2comp.h - various base 2 log computation versions
+ *
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * \author Alex Volkov <codepro@usa.net>
+ *
+ * Copyright (c) 2004 - 2005, Digium Inc.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ *
+ * Define WANT_ASM before including this file to use assembly
+ * whenever possible
+ */
+
+#if defined(_MSC_VER)
+# define inline __inline
+#elif defined(__GNUC__)
+# define inline __inline__
+#else
+# define inline
+#endif
+
+#if defined(WANT_ASM) && defined(_MSC_VER) && defined(_M_IX86)
+/* MS C Inline Asm */
+# pragma warning( disable : 4035 )
+static inline int ilog2(int val) { __asm
+{
+ xor eax, eax
+ dec eax
+ bsr eax, val
+}}
+# pragma warning( default : 4035 )
+#elif defined(WANT_ASM) && defined(__GNUC__) && (defined(__i386__) || defined(i386))
+/* GNU Inline Asm */
+static inline int ilog2(int val)
+{
+ int a;
+ __asm__
+ ("\
+ xorl %0, %0 ;\
+ decl %0 ;\
+ bsrl %1, %0 ;\
+ "
+ : "=r" (a)
+ : "mr" (val)
+ : "cc"
+ );
+ return a;
+}
+#elif defined(WANT_ASM) && defined(__GNUC__) && defined(__powerpc__)
+static inline int ilog2(int val)
+{
+ int a;
+ __asm__ ("cntlzw %0,%1"
+ : "=r" (a)
+ : "r" (val)
+ );
+ return 31-a;
+}
+#else
+/* no ASM for this compiler and/or platform */
+/* rather slow base 2 log computation
+ * Using looped shift.
+ */
+static inline int ilog2(int val)
+{
+ int i;
+ for (i = -1; val; ++i, val >>= 1)
+ ;
+ return (i);
+}
+#endif
diff --git a/trunk/codecs/lpc10/Makefile b/trunk/codecs/lpc10/Makefile
new file mode 100644
index 000000000..c98131c26
--- /dev/null
+++ b/trunk/codecs/lpc10/Makefile
@@ -0,0 +1,78 @@
+#
+# Makefile for LPC-10 speech coder library (unix)
+#
+
+# default C compiler
+CC?= gcc
+
+#
+# These definitions for CFLAGS and LIB_TARGET_DIR are used when one
+# runs make in the lpc10 directory, without environment variables that
+# override them. When make is run in this directory from a makefile
+# for an application that uses the LPC10 coder, there are environment
+# variables set for CFLAGS and LIB_TARGET_DIR that override these
+# definitions.
+#
+
+LIB_TARGET_DIR = .
+
+#
+# -I$(LIB_TARGET_DIR) option needed so that #include "machine.h"
+# directives can find the machine.h file.
+#
+
+CFLAGS+= -fPIC -Wno-comment
+
+# The code here generates lots of warnings, so compiling with -Werror
+# fails miserably. Remove it for the time being.
+ASTCFLAGS:= $(ASTCFLAGS:-Werror=)
+
+#fix for PPC processors and ALPHA, And UltraSparc too
+ifneq ($(OSARCH),Darwin)
+ ifneq ($(findstring BSD,${OSARCH}),BSD)
+ ifneq ($(PROC),ppc)
+ ifneq ($(PROC),x86_64)
+ ifneq ($(PROC),alpha)
+#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
+ ifeq ($(PROC),ultrasparc)
+ CFLAGS+= -mtune=$(PROC) -mcpu=v8 -O3 -fomit-frame-pointer
+ else
+ ifneq ($(OSARCH),SunOS)
+ ifneq ($(OSARCH),arm)
+# CFLAGS+= -march=$(PROC)
+ endif
+ endif
+ endif
+ endif
+ endif
+ endif
+ endif
+endif
+
+LIB = $(LIB_TARGET_DIR)/liblpc10.a
+
+.PHONY: all clean
+
+include $(ASTTOPDIR)/Makefile.rules
+
+all: $(LIB)
+
+OBJ=f2clib.o analys.o bsynz.o chanwr.o dcbias.o decode.o \
+ deemp.o difmag.o dyptrk.o encode.o energy.o ham84.o \
+ hp100.o invert.o irc2pc.o ivfilt.o lpcdec.o lpcenc.o \
+ lpcini.o lpfilt.o median.o mload.o onset.o pitsyn.o \
+ placea.o placev.o preemp.o prepro.o random.o rcchk.o \
+ synths.o tbdm.o voicin.o vparms.o
+
+$(LIB): $(OBJ)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+clean:
+ rm -f *.o $(LIB) .*.d
+ rm -f *.s *.i
diff --git a/trunk/codecs/lpc10/README b/trunk/codecs/lpc10/README
new file mode 100644
index 000000000..30abe4c97
--- /dev/null
+++ b/trunk/codecs/lpc10/README
@@ -0,0 +1,89 @@
+Tue Aug 20 16:19:51 CDT 1996
+Andy Fingerhut (jaf@arl.wustl.edu)
+
+In release 1.4, there are quite a few hand modifications to the C code
+that was automatically created from the Fortran code with f2c. They
+are all summarized in change log comments at the beginning of the
+changed files. All of the original files from f2c were checked in to
+RCS before modification, so it is possible to see exactly what changes
+were made, for the extremely curious. That precaution was also for my
+benefit, in case I ever recompile the Fortran sources, and want to
+make similar changes to that new C source code.
+
+Below is the README file for this directory included with the 1.3
+release of the LPC-10 package. A few parts of it are a little out of
+date, but it is correct for the most part.
+
+
+Sun Jul 7 15:30:31 CDT 1996
+Andy Fingerhut (jaf@arl.wustl.edu)
+
+To create the LPC-10 library, copy the appropriate makefile to the
+proper name for easy use, e.g., for Unix, copy makefile.unx to the
+file "Makefile". The file makefile.dos has been used with some
+version of the 'nmake' utility that comes with the Microsoft C
+compiler (the same one used for Nautilus v1.5a, which I believe
+specifies Microsoft C version 7.0 or later).
+
+Then edit the file lpc10.h in the directory above. It should already
+be set up to work properly on any Unix compiler for which "int" is 32
+bits and "short" is 16 bits, and under the Microsoft C compiler
+configured so that "long" is 32 bits and "int" is 16 bits. There must
+be a typedef for the two types INT32 and INT16 in that file. You
+should choose types that compile to those sizes using your compiler,
+because there are places in the LPC-10 code that expect INT16's to
+have exactly 16 bits (at least, I *think* they must be no larger), and
+INT32's to have exactly 32 bits.
+
+
+A few notes on how these files were created
+-------------------------------------------
+
+(This section is mostly for my benefit, so I can remember what I did.
+You don't need to read it if you just want to use this package. It
+might be useful to read it if you change the Fortran sources and want
+to recreate a usable library of C sources. -- Andy)
+
+These C sources were created automatically from the Fortran sources
+using f2c, for the most part. Listed below are the extra
+modifications that were made after this automatic conversion. Many of
+them were made so that it was not necessary to install f2c in order to
+use this LPC-10 coder.
+
+1.
+
+Put all of those files that were necessary for only the coder, rather
+than an application that uses the coder, into this subdirectory called
+lpc10.
+
+2.
+
+Copied f2c.h from the f2c distribution into this subdirectory. Some
+modifications were made to the "typedef" statements in this file, to
+explicitly indicate the sizes (in bits) that different integer types
+should be. The types INT32 and INT16 must be defined in a file called
+lpc10.h in the directory above. Created the file f2clib.c, containing
+only the functions pow_ii(), r_sign(), and i_nint() from the f2c
+library.
+
+3.
+
+The f2c output originally had a file called contrl_com.c, that defined
+a small structure containing a few variables that were used in many
+different functions of the LPC10 code. Every file containing
+functions that used it defined it as "extern", while contrl_com.c
+actually allocated storage for the structure. Bill Dorsey, one of the
+lead developers of Nautilus, said that the Microsoft C compiler had
+problems either compiling this file, or linking it with all of the
+other compiled files, so he just eliminated that file and removed the
+"extern" keyword from the one of the files that declared it that way.
+The file chosen (arbitrarily) was analys.c.
+
+4.
+
+Copied the makefiles for Unix and Microsoft C from the Nautilus v1.5a
+distribution into the lpc10 directory. Modified them to take out
+references to Nautilus. These makefiles don't create an executable,
+but a library of compiled functions called liblpc10.a (Unix) or
+LPC10.LIB (DOS). This library can be used when linking an executable
+that calls the functions lpcini_(), lpcenc_(), and lpcdec_().
diff --git a/trunk/codecs/lpc10/analys.c b/trunk/codecs/lpc10/analys.c
new file mode 100644
index 000000000..50e95703d
--- /dev/null
+++ b/trunk/codecs/lpc10/analys.c
@@ -0,0 +1,649 @@
+/*
+
+$Log$
+Revision 1.16 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.15 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.2 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:16:01 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:29:08 jaf
+ * Initial revision
+ *
+
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int analys_(real *speech, integer *voice, integer *pitch, real *rms, real *rc, struct lpc10_encoder_state *st);
+/* comlen contrl_ 12 */
+/*:ref: preemp_ 14 5 6 6 4 6 6 */
+/*:ref: onset_ 14 7 6 4 4 4 4 4 4 */
+/*:ref: placev_ 14 11 4 4 4 4 4 4 4 4 4 4 4 */
+/*:ref: lpfilt_ 14 4 6 6 4 4 */
+/*:ref: ivfilt_ 14 5 6 6 4 4 6 */
+/*:ref: tbdm_ 14 8 6 4 4 4 6 4 4 4 */
+/*:ref: voicin_ 14 12 4 6 6 4 4 6 6 4 6 4 4 4 */
+/*:ref: dyptrk_ 14 6 6 4 4 4 4 4 */
+/*:ref: placea_ 14 9 4 4 4 4 4 4 4 4 4 */
+/*:ref: dcbias_ 14 3 4 6 6 */
+/*:ref: energy_ 14 3 4 6 6 */
+/*:ref: mload_ 14 6 4 4 4 6 6 6 */
+/*:ref: invert_ 14 4 4 6 6 6 */
+/*:ref: rcchk_ 14 3 4 6 6 */
+/*:ref: initonset_ 14 0 */
+/*:ref: initvoicin_ 14 0 */
+/*:ref: initdyptrk_ 14 0 */
+/* Rerunning f2c -P may change prototypes or declarations. */
+#endif
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* Table of constant values */
+
+static integer c__10 = 10;
+static integer c__181 = 181;
+static integer c__720 = 720;
+static integer c__3 = 3;
+static integer c__90 = 90;
+static integer c__156 = 156;
+static integer c__307 = 307;
+static integer c__462 = 462;
+static integer c__312 = 312;
+static integer c__60 = 60;
+static integer c__1 = 1;
+
+/* ****************************************************************** */
+
+/* ANALYS Version 55 */
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:16:01 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:29:08 jaf
+ * Initial revision
+ * */
+/* Revision 1.9 1996/05/23 19:41:07 jaf */
+/* Commented out some unnecessary lines that were reading uninitialized */
+/* values. */
+
+/* Revision 1.8 1996/03/27 23:57:55 jaf */
+/* Added some comments about which indices of the local buffers INBUF, */
+/* LPBUF, etc., get read or modified by some of the subroutine calls. I */
+/* just did this while trying to figure out the discrepancy between the */
+/* embedded code compiled with all local variables implicitly saved, and */
+/* without. */
+
+/* I added some debugging write statements in hopes of finding a problem. */
+/* None of them ever printed anything while running with the long input */
+/* speech file dam9.spd provided in the distribution. */
+
+/* Revision 1.7 1996/03/27 18:06:20 jaf */
+/* Commented out access to MAXOSP, which is just a debugging variable */
+/* that was defined in the COMMON block CONTRL in contrl.fh. */
+
+/* Revision 1.6 1996/03/26 19:31:33 jaf */
+/* Commented out trace statements. */
+
+/* Revision 1.5 1996/03/21 15:19:35 jaf */
+/* Added comments for ENTRY PITDEC. */
+
+/* Revision 1.4 1996/03/19 20:54:27 jaf */
+/* Added a line to INITANALYS. See comments there. */
+
+/* Revision 1.3 1996/03/19 20:52:49 jaf */
+/* Rearranged the order of the local variables quite a bit, to separate */
+/* them into groups of "constants", "locals that don't need to be saved */
+/* from one call to the next", and "local that do need to be saved from */
+/* one call to the next". */
+
+/* Several locals in the last set should have been given initial values, */
+/* but weren't. I gave them all initial values of 0. */
+
+/* Added a separate ENTRY INITANALYS that initializes all local state */
+/* that should be, and also calls the corresponding entries of the */
+/* subroutines called by ANALYS that also have local state. */
+
+/* There used to be DATA statements in ANALYS. I got rid of most of */
+/* them, and added a local logical variable FIRST that calls the entry */
+/* INITANALYS on the first call to ANALYS. This is just so that one need */
+/* not remember to call INITANALYS first in order for the state to be */
+/* initialized. */
+
+/* Revision 1.2 1996/03/11 23:29:32 jaf */
+/* Added several comments with my own personal questions about the */
+/* Fortran 77 meaning of the parameters passed to the subroutine PREEMP. */
+
+/* Revision 1.1 1996/02/07 14:42:29 jaf */
+/* Initial revision */
+
+
+/* ****************************************************************** */
+
+/* SUBROUTINE ANALYS */
+
+/* Input: */
+/* SPEECH */
+/* Indices 1 through LFRAME read. */
+/* Output: */
+/* VOICE */
+/* Indices 1 through 2 written. */
+/* PITCH */
+/* Written in subroutine DYPTRK, and then perhaps read and written */
+/* some more. */
+/* RMS */
+/* Written. */
+/* RC */
+/* Indices 1 through ORDER written (ORDER defined in contrl.fh). */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITANALYS. */
+
+
+/* ENTRY PITDEC */
+
+/* Input: */
+/* PITCH - Encoded pitch index */
+/* Output: */
+/* PTAU - Decoded pitch period */
+
+/* This entry has no local state. It accesses a "constant" array */
+/* declared in ANALYS. */
+
+/* Subroutine */ int analys_(real *speech, integer *voice, integer
+ *pitch, real *rms, real *rc, struct lpc10_encoder_state *st)
+{
+ /* Initialized data */
+
+ static integer tau[60] = { 20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,
+ 35,36,37,38,39,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,
+ 74,76,78,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,
+ 140,144,148,152,156 };
+ static integer buflim[4] = { 181,720,25,720 };
+ static real precoef = .9375f;
+
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ real amdf[60];
+ integer half;
+ real abuf[156];
+ real *bias;
+ extern /* Subroutine */ int tbdm_(real *, integer *, integer *, integer *,
+ real *, integer *, integer *, integer *);
+ integer *awin;
+ integer midx, ewin[6] /* was [2][3] */;
+ real ivrc[2], temp;
+ real *zpre;
+ integer *vwin;
+ integer i__, j, lanal;
+ extern /* Subroutine */ int rcchk_(integer *, real *, real *), mload_(
+ integer *, integer *, integer *, real *, real *, real *);
+ real *inbuf, *pebuf;
+ real *lpbuf, *ivbuf;
+ real *rcbuf;
+ integer *osbuf;
+ extern /* Subroutine */ int onset_(real *, integer *, integer *, integer *
+ , integer *, integer *, integer *, struct lpc10_encoder_state *);
+ integer *osptr;
+ extern int dcbias_(integer *, real *, real *);
+ integer ipitch;
+ integer *obound;
+ extern /* Subroutine */ int preemp_(real *, real *, integer *, real *,
+ real *), voicin_(integer *, real *, real *, integer *, integer *,
+ real *, real *, integer *, real *, integer *, integer *, integer *,
+ struct lpc10_encoder_state *);
+ integer *voibuf;
+ integer mintau;
+ real *rmsbuf;
+ extern /* Subroutine */ int lpfilt_(real *, real *, integer *, integer *),
+ ivfilt_(real *, real *, integer *, integer *, real *), energy_(
+ integer *, real *, real *), invert_(integer *, real *, real *,
+ real *);
+ integer minptr, maxptr;
+ extern /* Subroutine */ int dyptrk_(real *, integer *, integer *, integer
+ *, integer *, integer *, struct lpc10_encoder_state *);
+ real phi[100] /* was [10][10] */, psi[10];
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:16:01 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:29:08 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Arguments to ANALYS */
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:16:01 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:29:08 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Arguments to entry PITDEC (below) */
+/* Parameters/constants */
+/* Constants */
+/* NF = Number of frames */
+/* AF = Frame in which analysis is done */
+/* OSLEN = Length of the onset buffer */
+/* LTAU = Number of pitch lags */
+/* SBUFL, SBUFH = Start and end index of speech buffers */
+/* LBUFL, LBUFH = Start and end index of LPF speech buffer */
+/* MINWIN, MAXWIN = Min and Max length of voicing (and analysis) windows
+*/
+/* PWLEN, PWINH, PWINL = Length, upper and lower limits of pitch window
+ */
+/* DVWINL, DVWINH = Default lower and upper limits of voicing window */
+/* The tables TAU and BUFLIM, and the variable PRECOEF, are not */
+/* Fortran PARAMETER's, but they are initialized with DATA */
+/* statements, and never modified. Thus, they need not have SAVE */
+/* statements for them to keep their values from one invocation to
+*/
+/* the next. */
+/* Local variables that need not be saved */
+/* Local state */
+/* Data Buffers */
+/* INBUF Raw speech (with DC bias removed each frame) */
+/* PEBUF Preemphasized speech */
+/* LPBUF Low pass speech buffer */
+/* IVBUF Inverse filtered speech */
+/* OSBUF Indexes of onsets in speech buffers */
+/* VWIN Voicing window indices */
+/* AWIN Analysis window indices */
+/* EWIN Energy window indices */
+/* VOIBUF Voicing decisions on windows in VWIN */
+/* RMSBUF RMS energy */
+/* RCBUF Reflection Coefficients */
+
+/* Pitch is handled separately from the above parameters. */
+/* The following variables deal with pitch: */
+/* MIDX Encoded initial pitch estimate for analysis frame */
+/* IPITCH Initial pitch computed for frame AF (decoded from MIDX) */
+/* PITCH The encoded pitch value (index into TAU) for the present */
+/* frame (delayed and smoothed by Dyptrack) */
+ /* Parameter adjustments */
+ if (speech) {
+ --speech;
+ }
+ if (voice) {
+ --voice;
+ }
+ if (rc) {
+ --rc;
+ }
+
+ /* Function Body */
+
+/* Calculations are done on future frame due to requirements */
+/* of the pitch tracker. Delay RMS and RC's 2 frames to give */
+/* current frame parameters on return. */
+/* Update all buffers */
+
+ inbuf = &(st->inbuf[0]);
+ pebuf = &(st->pebuf[0]);
+ lpbuf = &(st->lpbuf[0]);
+ ivbuf = &(st->ivbuf[0]);
+ bias = &(st->bias);
+ osbuf = &(st->osbuf[0]);
+ osptr = &(st->osptr);
+ obound = &(st->obound[0]);
+ vwin = &(st->vwin[0]);
+ awin = &(st->awin[0]);
+ voibuf = &(st->voibuf[0]);
+ rmsbuf = &(st->rmsbuf[0]);
+ rcbuf = &(st->rcbuf[0]);
+ zpre = &(st->zpre);
+
+ i__1 = 720 - contrl_1.lframe;
+ for (i__ = 181; i__ <= i__1; ++i__) {
+ inbuf[i__ - 181] = inbuf[contrl_1.lframe + i__ - 181];
+ pebuf[i__ - 181] = pebuf[contrl_1.lframe + i__ - 181];
+ }
+ i__1 = 540 - contrl_1.lframe;
+ for (i__ = 229; i__ <= i__1; ++i__) {
+ ivbuf[i__ - 229] = ivbuf[contrl_1.lframe + i__ - 229];
+ }
+ i__1 = 720 - contrl_1.lframe;
+ for (i__ = 25; i__ <= i__1; ++i__) {
+ lpbuf[i__ - 25] = lpbuf[contrl_1.lframe + i__ - 25];
+ }
+ j = 1;
+ i__1 = (*osptr) - 1;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (osbuf[i__ - 1] > contrl_1.lframe) {
+ osbuf[j - 1] = osbuf[i__ - 1] - contrl_1.lframe;
+ ++j;
+ }
+ }
+ *osptr = j;
+ voibuf[0] = voibuf[2];
+ voibuf[1] = voibuf[3];
+ for (i__ = 1; i__ <= 2; ++i__) {
+ vwin[(i__ << 1) - 2] = vwin[((i__ + 1) << 1) - 2] - contrl_1.lframe;
+ vwin[(i__ << 1) - 1] = vwin[((i__ + 1) << 1) - 1] - contrl_1.lframe;
+ awin[(i__ << 1) - 2] = awin[((i__ + 1) << 1) - 2] - contrl_1.lframe;
+ awin[(i__ << 1) - 1] = awin[((i__ + 1) << 1) - 1] - contrl_1.lframe;
+/* EWIN(*,J) is unused for J .NE. AF, so the following shift is
+*/
+/* unnecessary. It also causes error messages when the C versio
+n */
+/* of the code created from this by f2c is run with Purify. It
+*/
+/* correctly complains that uninitialized memory is being read.
+*/
+/* EWIN(1,I) = EWIN(1,I+1) - LFRAME */
+/* EWIN(2,I) = EWIN(2,I+1) - LFRAME */
+ obound[i__ - 1] = obound[i__];
+ voibuf[i__ * 2] = voibuf[(i__ + 1) * 2];
+ voibuf[(i__ << 1) + 1] = voibuf[((i__ + 1) << 1) + 1];
+ rmsbuf[i__ - 1] = rmsbuf[i__];
+ i__1 = contrl_1.order;
+ for (j = 1; j <= i__1; ++j) {
+ rcbuf[j + i__ * 10 - 11] = rcbuf[j + (i__ + 1) * 10 - 11];
+ }
+ }
+/* Copy input speech, scale to sign+12 bit integers */
+/* Remove long term DC bias. */
+/* If the average value in the frame was over 1/4096 (after current
+*/
+/* BIAS correction), then subtract that much more from samples in */
+/* next frame. If the average value in the frame was under */
+/* -1/4096, add 1/4096 more to samples in next frame. In all other
+*/
+/* cases, keep BIAS the same. */
+ temp = 0.f;
+ i__1 = contrl_1.lframe;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ inbuf[720 - contrl_1.lframe + i__ - 181] = speech[i__] * 4096.f -
+ (*bias);
+ temp += inbuf[720 - contrl_1.lframe + i__ - 181];
+ }
+ if (temp > (real) contrl_1.lframe) {
+ *bias += 1;
+ }
+ if (temp < (real) (-contrl_1.lframe)) {
+ *bias += -1;
+ }
+/* Place Voicing Window */
+ i__ = 721 - contrl_1.lframe;
+ preemp_(&inbuf[i__ - 181], &pebuf[i__ - 181], &contrl_1.lframe, &precoef,
+ zpre);
+ onset_(pebuf, osbuf, osptr, &c__10, &c__181, &c__720, &contrl_1.lframe, st);
+
+/* MAXOSP is just a debugging variable. */
+
+/* MAXOSP = MAX( MAXOSP, OSPTR ) */
+
+ placev_(osbuf, osptr, &c__10, &obound[2], vwin, &c__3, &contrl_1.lframe,
+ &c__90, &c__156, &c__307, &c__462);
+/* The Pitch Extraction algorithm estimates the pitch for a frame
+*/
+/* of speech by locating the minimum of the average magnitude difference
+ */
+/* function (AMDF). The AMDF operates on low-pass, inverse filtered */
+/* speech. (The low-pass filter is an 800 Hz, 19 tap, equiripple, FIR
+*/
+/* filter and the inverse filter is a 2nd-order LPC filter.) The pitch
+*/
+/* estimate is later refined by dynamic programming (DYPTRK). However,
+*/
+/* since some of DYPTRK's parameters are a function of the voicing */
+/* decisions, a voicing decision must precede the final pitch estimation.
+*/
+/* See subroutines LPFILT, IVFILT, and TBDM. */
+/* LPFILT reads indices LBUFH-LFRAME-29 = 511 through LBUFH = 720 */
+/* of INBUF, and writes indices LBUFH+1-LFRAME = 541 through LBUFH
+*/
+/* = 720 of LPBUF. */
+ lpfilt_(&inbuf[228], &lpbuf[384], &c__312, &contrl_1.lframe);
+/* IVFILT reads indices (PWINH-LFRAME-7) = 353 through PWINH = 540
+*/
+/* of LPBUF, and writes indices (PWINH-LFRAME+1) = 361 through */
+/* PWINH = 540 of IVBUF. */
+ ivfilt_(&lpbuf[204], ivbuf, &c__312, &contrl_1.lframe, ivrc);
+/* TBDM reads indices PWINL = 229 through */
+/* (PWINL-1)+MAXWIN+(TAU(LTAU)-TAU(1))/2 = 452 of IVBUF, and writes
+*/
+/* indices 1 through LTAU = 60 of AMDF. */
+ tbdm_(ivbuf, &c__156, tau, &c__60, amdf, &minptr, &maxptr, &mintau);
+/* Voicing decisions are made for each half frame of input speech.
+*/
+/* An initial voicing classification is made for each half of the */
+/* analysis frame, and the voicing decisions for the present frame */
+/* are finalized. See subroutine VOICIN. */
+/* The voicing detector (VOICIN) classifies the input signal as */
+/* unvoiced (including silence) or voiced using the AMDF windowed */
+/* maximum-to-minimum ratio, the zero crossing rate, energy measures, */
+/* reflection coefficients, and prediction gains. */
+/* The pitch and voicing rules apply smoothing and isolated */
+/* corrections to the pitch and voicing estimates and, in the process,
+*/
+/* introduce two frames of delay into the corrected pitch estimates and
+*/
+/* voicing decisions. */
+ for (half = 1; half <= 2; ++half) {
+ voicin_(&vwin[4], inbuf, lpbuf, buflim, &half, &amdf[minptr - 1], &
+ amdf[maxptr - 1], &mintau, ivrc, obound, voibuf, &c__3, st);
+ }
+/* Find the minimum cost pitch decision over several frames */
+/* given the current voicing decision and the AMDF array */
+ dyptrk_(amdf, &c__60, &minptr, &voibuf[7], pitch, &midx, st);
+ ipitch = tau[midx - 1];
+/* Place spectrum analysis and energy windows */
+ placea_(&ipitch, voibuf, &obound[2], &c__3, vwin, awin, ewin, &
+ contrl_1.lframe, &c__156);
+/* Remove short term DC bias over the analysis window, Put result in ABUF
+*/
+ lanal = awin[5] + 1 - awin[4];
+ dcbias_(&lanal, &pebuf[awin[4] - 181], abuf);
+/* ABUF(1:LANAL) is now defined. It is equal to */
+/* PEBUF(AWIN(1,AF):AWIN(2,AF)) corrected for short term DC bias. */
+/* Compute RMS over integer number of pitch periods within the */
+/* analysis window. */
+/* Note that in a hardware implementation this computation may be */
+/* simplified by using diagonal elements of PHI computed by MLOAD. */
+ i__1 = ewin[5] - ewin[4] + 1;
+ energy_(&i__1, &abuf[ewin[4] - awin[4]], &rmsbuf[2]);
+/* Matrix load and invert, check RC's for stability */
+ mload_(&contrl_1.order, &c__1, &lanal, abuf, phi, psi);
+ invert_(&contrl_1.order, phi, psi, &rcbuf[20]);
+ rcchk_(&contrl_1.order, &rcbuf[10], &rcbuf[20]);
+/* Set return parameters */
+ voice[1] = voibuf[2];
+ voice[2] = voibuf[3];
+ *rms = rmsbuf[0];
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ rc[i__] = rcbuf[i__ - 1];
+ }
+ return 0;
+} /* analys_ */
diff --git a/trunk/codecs/lpc10/bsynz.c b/trunk/codecs/lpc10/bsynz.c
new file mode 100644
index 000000000..daf9105d6
--- /dev/null
+++ b/trunk/codecs/lpc10/bsynz.c
@@ -0,0 +1,447 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:18:55 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:58 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int bsynz_(real *coef, integer *ip, integer *iv, real *sout, real *rms, real *ratio, real *g2pass, struct lpc10_decoder_state *st);
+/* comlen contrl_ 12 */
+/*:ref: random_ 4 0 */
+#endif
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* ***************************************************************** */
+
+/* BSYNZ Version 54 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:18:55 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:58 jaf
+ * Initial revision
+ * */
+/* Revision 1.4 1996/03/27 18:11:22 jaf */
+/* Changed the range of NOISE printed out in the debugging statements, */
+/* even though they are commented out. I didn't discover this until I */
+/* tried comparing two different versions of the LPC-10 coder, each with */
+/* full tracing enabled. */
+
+/* Revision 1.3 1996/03/26 19:33:23 jaf */
+/* Commented out trace statements. */
+
+/* Revision 1.2 1996/03/20 17:12:54 jaf */
+/* Added comments about which indices of array arguments are read or */
+/* written. */
+
+/* Rearranged local variable declarations to indicate which need to be */
+/* saved from one invocation to the next. Added entry INITBSYNZ to */
+/* reinitialize the local state variables, if desired. */
+
+/* Revision 1.1 1996/02/07 14:43:15 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Synthesize One Pitch Epoch */
+
+/* Input: */
+/* COEF - Predictor coefficients */
+/* Indices 1 through ORDER read. */
+/* IP - Pitch period (number of samples to synthesize) */
+/* IV - Voicing for the current epoch */
+/* RMS - Energy for the current epoch */
+/* RATIO - Energy slope for plosives */
+/* G2PASS- Sharpening factor for 2 pass synthesis */
+/* Output: */
+/* SOUT - Synthesized speech */
+/* Indices 1 through IP written. */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITBSYNZ. */
+
+/* Subroutine */ int bsynz_(real *coef, integer *ip, integer *iv,
+ real *sout, real *rms, real *ratio, real *g2pass,
+ struct lpc10_decoder_state *st)
+{
+ /* Initialized data */
+
+ integer *ipo;
+ real *rmso;
+ static integer kexc[25] = { 8,-16,26,-48,86,-162,294,-502,718,-728,184,
+ 672,-610,-672,184,728,718,502,294,162,86,48,26,16,8 };
+ real *exc;
+ real *exc2;
+ real *lpi1;
+ real *lpi2;
+ real *lpi3;
+ real *hpi1;
+ real *hpi2;
+ real *hpi3;
+
+ /* System generated locals */
+ integer i__1, i__2;
+ real r__1, r__2;
+
+ /* Builtin functions */
+ double sqrt(doublereal);
+
+ /* Local variables */
+ real gain, xssq;
+ integer i__, j, k;
+ real noise[166], pulse;
+ integer px;
+ real sscale;
+ extern integer random_(struct lpc10_decoder_state *);
+ real xy, sum, ssq;
+ real lpi0, hpi0;
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:18:55 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:58 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:18:55 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:58 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Function return value definitions */
+/* Parameters/constants */
+/* KEXC is not a Fortran PARAMETER, but it is an array initialized
+*/
+/* with a DATA statement that is never modified. */
+/* Local variables that need not be saved */
+/* NOISE is declared with range (1:MAXPIT+MAXORD), but only indices
+*/
+/* ORDER+1 through ORDER+IP are ever used, and I think that IP */
+/* .LE. MAXPIT. Why not declare it to be in the range (1:MAXPIT) */
+/* and use that range? */
+/* Local state */
+/* I believe that only indices 1 through ORDER of EXC need to be */
+/* saved from one invocation to the next, but we may as well save */
+/* the whole array. */
+/* None of these local variables were given initial values in the */
+/* original code. I'm guessing that 0 is a reasonable initial */
+/* value for all of them. */
+ /* Parameter adjustments */
+ if (coef) {
+ --coef;
+ }
+ if (sout) {
+ --sout;
+ }
+
+ /* Function Body */
+ ipo = &(st->ipo);
+ exc = &(st->exc[0]);
+ exc2 = &(st->exc2[0]);
+ lpi1 = &(st->lpi1);
+ lpi2 = &(st->lpi2);
+ lpi3 = &(st->lpi3);
+ hpi1 = &(st->hpi1);
+ hpi2 = &(st->hpi2);
+ hpi3 = &(st->hpi3);
+ rmso = &(st->rmso_bsynz);
+
+/* MAXPIT+MAXORD=166 */
+/* Calculate history scale factor XY and scale filter state */
+/* Computing MIN */
+ r__1 = *rmso / (*rms + 1e-6f);
+ xy = min(r__1,8.f);
+ *rmso = *rms;
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ exc2[i__ - 1] = exc2[*ipo + i__ - 1] * xy;
+ }
+ *ipo = *ip;
+ if (*iv == 0) {
+/* Generate white noise for unvoiced */
+ i__1 = *ip;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ exc[contrl_1.order + i__ - 1] = (real) (random_(st) / 64);
+ }
+/* Impulse doublet excitation for plosives */
+/* (RANDOM()+32768) is in the range 0 to 2**16-1. Therefore the
+ */
+/* following expression should be evaluated using integers with
+at */
+/* least 32 bits (16 isn't enough), and PX should be in the rang
+e */
+/* ORDER+1+0 through ORDER+1+(IP-2) .EQ. ORDER+IP-1. */
+ px = (random_(st) + 32768) * (*ip - 1) / 65536 + contrl_1.order + 1;
+ r__1 = *ratio / 4 * 1.f;
+ pulse = r__1 * 342;
+ if (pulse > 2e3f) {
+ pulse = 2e3f;
+ }
+ exc[px - 1] += pulse;
+ exc[px] -= pulse;
+/* Load voiced excitation */
+ } else {
+ sscale = (real)sqrt((real) (*ip)) / 6.928f;
+ i__1 = *ip;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ exc[contrl_1.order + i__ - 1] = 0.f;
+ if (i__ <= 25) {
+ exc[contrl_1.order + i__ - 1] = sscale * kexc[i__ - 1];
+ }
+ lpi0 = exc[contrl_1.order + i__ - 1];
+ r__2 = exc[contrl_1.order + i__ - 1] * .125f + *lpi1 * .75f;
+ r__1 = r__2 + *lpi2 * .125f;
+ exc[contrl_1.order + i__ - 1] = r__1 + *lpi3 * 0.f;
+ *lpi3 = *lpi2;
+ *lpi2 = *lpi1;
+ *lpi1 = lpi0;
+ }
+ i__1 = *ip;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ noise[contrl_1.order + i__ - 1] = random_(st) * 1.f / 64;
+ hpi0 = noise[contrl_1.order + i__ - 1];
+ r__2 = noise[contrl_1.order + i__ - 1] * -.125f + *hpi1 * .25f;
+ r__1 = r__2 + *hpi2 * -.125f;
+ noise[contrl_1.order + i__ - 1] = r__1 + *hpi3 * 0.f;
+ *hpi3 = *hpi2;
+ *hpi2 = *hpi1;
+ *hpi1 = hpi0;
+ }
+ i__1 = *ip;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ exc[contrl_1.order + i__ - 1] += noise[contrl_1.order + i__ - 1];
+ }
+ }
+/* Synthesis filters: */
+/* Modify the excitation with all-zero filter 1 + G*SUM */
+ xssq = 0.f;
+ i__1 = *ip;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ k = contrl_1.order + i__;
+ sum = 0.f;
+ i__2 = contrl_1.order;
+ for (j = 1; j <= i__2; ++j) {
+ sum += coef[j] * exc[k - j - 1];
+ }
+ sum *= *g2pass;
+ exc2[k - 1] = sum + exc[k - 1];
+ }
+/* Synthesize using the all pole filter 1 / (1 - SUM) */
+ i__1 = *ip;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ k = contrl_1.order + i__;
+ sum = 0.f;
+ i__2 = contrl_1.order;
+ for (j = 1; j <= i__2; ++j) {
+ sum += coef[j] * exc2[k - j - 1];
+ }
+ exc2[k - 1] = sum + exc2[k - 1];
+ xssq += exc2[k - 1] * exc2[k - 1];
+ }
+/* Save filter history for next epoch */
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ exc[i__ - 1] = exc[*ip + i__ - 1];
+ exc2[i__ - 1] = exc2[*ip + i__ - 1];
+ }
+/* Apply gain to match RMS */
+ r__1 = *rms * *rms;
+ ssq = r__1 * *ip;
+ gain = (real)sqrt(ssq / xssq);
+ i__1 = *ip;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ sout[i__] = gain * exc2[contrl_1.order + i__ - 1];
+ }
+ return 0;
+} /* bsynz_ */
diff --git a/trunk/codecs/lpc10/chanwr.c b/trunk/codecs/lpc10/chanwr.c
new file mode 100644
index 000000000..cefcdd145
--- /dev/null
+++ b/trunk/codecs/lpc10/chanwr.c
@@ -0,0 +1,232 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:20:24 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Revision 1.1 1996/08/19 22:40:31 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+/* *********************************************************************** */
+
+/* CHANL Version 49 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:20:24 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Revision 1.1 1996/08/19 22:40:31 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/21 15:14:57 jaf */
+/* Added comments about which indices of argument arrays are read or */
+/* written, and about the one bit of local state in CHANWR. CHANRD */
+/* has no local state. */
+
+/* Revision 1.2 1996/03/13 18:55:10 jaf */
+/* Comments added explaining which of the local variables of this */
+/* subroutine need to be saved from one invocation to the next, and which */
+/* do not. */
+
+/* Revision 1.1 1996/02/07 14:43:31 jaf */
+/* Initial revision */
+
+
+/* *********************************************************************** */
+
+/* CHANWR: */
+/* Place quantized parameters into bitstream */
+
+/* Input: */
+/* ORDER - Number of reflection coefficients (not really variable) */
+/* IPITV - Quantized pitch/voicing parameter */
+/* IRMS - Quantized energy parameter */
+/* IRC - Quantized reflection coefficients */
+/* Indices 1 through ORDER read. */
+/* Output: */
+/* IBITS - Serial bitstream */
+/* Indices 1 through 54 written. */
+/* Bit 54, the SYNC bit, alternates from one call to the next. */
+
+/* Subroutine CHANWR maintains one bit of local state from one call to */
+/* the next, in the variable ISYNC. I believe that this one bit is only */
+/* intended to allow a receiver to resynchronize its interpretation of */
+/* the bit stream, by looking for which of the 54 bits alternates every */
+/* frame time. This is just a simple framing mechanism that is not */
+/* useful when other, higher overhead framing mechanisms are used to */
+/* transmit the coded frames. */
+
+/* I'm not going to make an entry to reinitialize this bit, since it */
+/* doesn't help a receiver much to know whether the first sync bit is a 0 */
+/* or a 1. It needs to examine several frames in sequence to have */
+/* reasonably good assurance that its framing is correct. */
+
+
+/* CHANRD: */
+/* Reconstruct parameters from bitstream */
+
+/* Input: */
+/* ORDER - Number of reflection coefficients (not really variable) */
+/* IBITS - Serial bitstream */
+/* Indices 1 through 53 read (SYNC bit is ignored). */
+/* Output: */
+/* IPITV - Quantized pitch/voicing parameter */
+/* IRMS - Quantized energy parameter */
+/* IRC - Quantized reflection coefficients */
+/* Indices 1 through ORDER written */
+
+/* Entry CHANRD has no local state. */
+
+
+
+/* IBITS is 54 bits of LPC data ordered as follows: */
+/* R1-0, R2-0, R3-0, P-0, A-0, */
+/* R1-1, R2-1, R3-1, P-1, A-1, */
+/* R1-2, R4-0, R3-2, A-2, P-2, R4-1, */
+/* R1-3, R2-2, R3-3, R4-2, A-3, */
+/* R1-4, R2-3, R3-4, R4-3, A-4, */
+/* P-3, R2-4, R7-0, R8-0, P-4, R4-4, */
+/* R5-0, R6-0, R7-1,R10-0, R8-1, */
+/* R5-1, R6-1, R7-2, R9-0, P-5, */
+/* R5-2, R6-2,R10-1, R8-2, P-6, R9-1, */
+/* R5-3, R6-3, R7-3, R9-2, R8-3, SYNC */
+/* Subroutine */ int chanwr_0_(int n__, integer *order, integer *ipitv,
+ integer *irms, integer *irc, integer *ibits,
+ struct lpc10_encoder_state *st)
+{
+ /* Initialized data */
+
+ integer *isync;
+ static integer bit[10] = { 2,4,8,8,8,8,16,16,16,16 };
+ static integer iblist[53] = { 13,12,11,1,2,13,12,11,1,2,13,10,11,2,1,10,
+ 13,12,11,10,2,13,12,11,10,2,1,12,7,6,1,10,9,8,7,4,6,9,8,7,5,1,9,8,
+ 4,6,1,5,9,8,7,5,6 };
+
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ integer itab[13], i__;
+
+/* Arguments */
+/* Parameters/constants */
+/* These arrays are not Fortran PARAMETER's, but they are defined */
+/* by DATA statements below, and their contents are never altered.
+*/
+/* Local variables that need not be saved */
+/* Local state */
+/* ISYNC is only used by CHANWR, not by ENTRY CHANRD. */
+
+ /* Parameter adjustments */
+ --irc;
+ --ibits;
+
+ /* Function Body */
+ switch(n__) {
+ case 1: goto L_chanrd;
+ }
+
+ isync = &(st->isync);
+
+/* ***********************************************************************
+ */
+/* Place quantized parameters into bitstream */
+/* ***********************************************************************
+ */
+/* Place parameters into ITAB */
+ itab[0] = *ipitv;
+ itab[1] = *irms;
+ itab[2] = 0;
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ itab[i__ + 2] = irc[*order + 1 - i__] & 32767;
+ }
+/* Put 54 bits into IBITS array */
+ for (i__ = 1; i__ <= 53; ++i__) {
+ ibits[i__] = itab[iblist[i__ - 1] - 1] & 1;
+ itab[iblist[i__ - 1] - 1] /= 2;
+ }
+ ibits[54] = *isync & 1;
+ *isync = 1 - *isync;
+ return 0;
+/* ***********************************************************************
+ */
+/* Reconstruct parameters from bitstream */
+/* ***********************************************************************
+ */
+
+L_chanrd:
+/* Reconstruct ITAB */
+ for (i__ = 1; i__ <= 13; ++i__) {
+ itab[i__ - 1] = 0;
+ }
+ for (i__ = 1; i__ <= 53; ++i__) {
+ itab[iblist[54 - i__ - 1] - 1] = (itab[iblist[54 - i__ - 1] - 1] << 1)
+ + ibits[54 - i__];
+ }
+/* Sign extend RC's */
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if ((itab[i__ + 2] & bit[i__ - 1]) != 0) {
+ itab[i__ + 2] -= bit[i__ - 1] << 1;
+ }
+ }
+/* Restore variables */
+ *ipitv = itab[0];
+ *irms = itab[1];
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ irc[i__] = itab[*order + 4 - i__ - 1];
+ }
+ return 0;
+} /* chanwr_ */
+
+/* Subroutine */ int chanwr_(integer *order, integer *ipitv, integer *irms,
+ integer *irc, integer *ibits, struct lpc10_encoder_state *st)
+{
+ return chanwr_0_(0, order, ipitv, irms, irc, ibits, st);
+ }
+
+/* Subroutine */ int chanrd_(integer *order, integer *ipitv, integer *irms,
+ integer *irc, integer *ibits)
+{
+ return chanwr_0_(1, order, ipitv, irms, irc, ibits, 0);
+ }
diff --git a/trunk/codecs/lpc10/dcbias.c b/trunk/codecs/lpc10/dcbias.c
new file mode 100644
index 000000000..d5a7d644f
--- /dev/null
+++ b/trunk/codecs/lpc10/dcbias.c
@@ -0,0 +1,107 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:40:23 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int dcbias_(integer *len, real *speech, real *sigout);
+#endif
+
+/* ********************************************************************* */
+
+/* DCBIAS Version 50 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:40:23 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/18 21:19:22 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 16:44:53 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:44:21 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Calculate and remove DC bias from buffer. */
+
+/* Input: */
+/* LEN - Length of speech buffers */
+/* SPEECH - Input speech buffer */
+/* Indices 1 through LEN read. */
+/* Output: */
+/* SIGOUT - Output speech buffer */
+/* Indices 1 through LEN written */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int dcbias_(integer *len, real *speech, real *sigout)
+{
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ real bias;
+ integer i__;
+
+/* Arguments */
+/* Local variables that need not be saved */
+ /* Parameter adjustments */
+ --sigout;
+ --speech;
+
+ /* Function Body */
+ bias = 0.f;
+ i__1 = *len;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ bias += speech[i__];
+ }
+ bias /= *len;
+ i__1 = *len;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ sigout[i__] = speech[i__] - bias;
+ }
+ return 0;
+} /* dcbias_ */
+
diff --git a/trunk/codecs/lpc10/decode.c b/trunk/codecs/lpc10/decode.c
new file mode 100644
index 000000000..08b8b9192
--- /dev/null
+++ b/trunk/codecs/lpc10/decode.c
@@ -0,0 +1,625 @@
+/*
+
+$Log$
+Revision 1.16 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.15 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.2 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:22:39 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:38 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int decode_(integer *ipitv, integer *irms, integer *irc, integer *voice, integer *pitch, real *rms, real *rc, struct lpc10_decoder_state *st);
+/* comlen contrl_ 12 */
+/*:ref: ham84_ 14 3 4 4 4 */
+/*:ref: median_ 4 3 4 4 4 */
+#endif
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* Table of constant values */
+
+static integer c__2 = 2;
+
+/* ***************************************************************** */
+
+/* DECODE Version 54 */
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:22:39 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:38 jaf
+ * Initial revision
+ * */
+/* Revision 1.5 1996/05/23 20:06:03 jaf */
+/* Assigned PITCH a "default" value on the first call, since otherwise it */
+/* would be left uninitialized. */
+
+/* Revision 1.4 1996/03/26 19:35:18 jaf */
+/* Commented out trace statements. */
+
+/* Revision 1.3 1996/03/21 21:10:50 jaf */
+/* Added entry INITDECODE to reinitialize the local state of subroutine */
+/* DECODE. */
+
+/* Revision 1.2 1996/03/21 21:04:50 jaf */
+/* Determined which local variables should be saved from one invocation */
+/* to the next, and guessed initial values for some that should have been */
+/* saved, but weren't given initial values. Many of the arrays are */
+/* "constants", and many local variables are only used if the "global" */
+/* variable CORRP is .TRUE. */
+
+/* Added comments explaining which indices of array arguments are read or */
+/* written. */
+
+/* Revision 1.1 1996/02/12 03:21:10 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* This subroutine provides error correction and decoding */
+/* for all LPC parameters */
+
+/* Input: */
+/* IPITV - Index value of pitch */
+/* IRMS - Coded Energy */
+/* CORRP - Error correction: */
+/* If FALSE, parameters are decoded directly with no delay. If TRUE, */
+/* most important parameter bits are protected by Hamming code and */
+/* median smoothed. This requires an additional frame of delay. */
+/* Input/Output: */
+/* IRC - Coded Reflection Coefficients */
+/* Indices 1 through ORDER always read, then written. */
+/* Output: */
+/* VOICE - Half frame voicing decisions */
+/* Indices 1 through 2 written. */
+/* PITCH - Decoded pitch */
+/* RMS - Energy */
+/* RC - Reflection coefficients */
+/* Indices 1 through ORDER written. */
+
+/* NOTE: Zero RC's should be done more directly, but this would affect */
+/* coded parameter printout. */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITDECODE. */
+
+/* Subroutine */ int decode_(integer *ipitv, integer *irms,
+ integer *irc, integer *voice, integer *pitch, real *rms, real *rc,
+ struct lpc10_decoder_state *st)
+{
+ /* Initialized data */
+
+ logical *first;
+ static integer ethrs = 2048;
+ static integer ethrs1 = 128;
+ static integer ethrs2 = 1024;
+ static integer ethrs3 = 2048;
+ static integer ivtab[32] = { 24960,24960,24960,24960,25480,25480,25483,
+ 25480,16640,1560,1560,1560,16640,1816,1563,1560,24960,24960,24859,
+ 24856,26001,25881,25915,25913,1560,1560,7800,3640,1561,1561,3643,
+ 3641 };
+ static real corth[32] /* was [4][8] */ = { 32767.f,10.f,5.f,0.f,
+ 32767.f,8.f,4.f,0.f,32.f,6.4f,3.2f,0.f,32.f,6.4f,3.2f,0.f,32.f,
+ 11.2f,6.4f,0.f,32.f,11.2f,6.4f,0.f,16.f,5.6f,3.2f,0.f,16.f,5.6f,
+ 3.2f,0.f };
+ static integer detau[128] = { 0,0,0,3,0,3,3,31,0,3,3,21,3,3,29,30,0,3,3,
+ 20,3,25,27,26,3,23,58,22,3,24,28,3,0,3,3,3,3,39,33,32,3,37,35,36,
+ 3,38,34,3,3,42,46,44,50,40,48,3,54,3,56,3,52,3,3,1,0,3,3,108,3,78,
+ 100,104,3,84,92,88,156,80,96,3,3,74,70,72,66,76,68,3,62,3,60,3,64,
+ 3,3,1,3,116,132,112,148,152,3,3,140,3,136,3,144,3,3,1,124,120,128,
+ 3,3,3,3,1,3,3,3,1,3,1,1,1 };
+ static integer rmst[64] = { 1024,936,856,784,718,656,600,550,502,460,420,
+ 384,352,328,294,270,246,226,206,188,172,158,144,132,120,110,102,
+ 92,84,78,70,64,60,54,50,46,42,38,34,32,30,26,24,22,20,18,17,16,15,
+ 14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 };
+ static integer detab7[32] = { 4,11,18,25,32,39,46,53,60,66,72,77,82,87,92,
+ 96,101,104,108,111,114,115,117,119,121,122,123,124,125,126,127,
+ 127 };
+ static real descl[8] = { .6953f,.625f,.5781f,.5469f,.5312f,.5391f,.4688f,
+ .3828f };
+ integer *ivp2h;
+ static integer deadd[8] = { 1152,-2816,-1536,-3584,-1280,-2432,768,-1920 }
+ ;
+ static integer qb[8] = { 511,511,1023,1023,1023,1023,2047,4095 };
+ static integer nbit[10] = { 8,8,5,5,4,4,4,4,3,2 };
+ static integer zrc[10] = { 0,0,0,0,0,3,0,2,0,0 };
+ static integer bit[5] = { 2,4,8,16,32 };
+ integer *iovoic;
+ integer *iavgp;
+ integer *iptold;
+ integer *erate;
+ integer *drc;
+ integer *dpit;
+ integer *drms;
+
+ /* System generated locals */
+ integer i__1, i__2;
+
+ /* Builtin functions */
+ integer pow_ii(integer *, integer *);
+
+ /* Local variables */
+ extern /* Subroutine */ int ham84_(integer *, integer *, integer *);
+ integer ipit, iout, i__, icorf, index, ivoic, ixcor, i1, i2, i4;
+ extern integer median_(integer *, integer *, integer *);
+ integer ishift, errcnt, lsb;
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:22:39 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:38 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Arguments */
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:22:39 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:38 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Function return value definitions */
+
+/* Parameters/constants */
+
+/* The variables below that are not Fortran PARAMETER's are */
+/* initialized with DATA statements, and then never modified. */
+/* The following are used regardless of CORRP's value. */
+
+/* DETAU, NBIT, QB, DEADD, DETAB7, RMST, DESCL */
+
+/* The following are used only if CORRP is .TRUE. */
+
+/* ETHRS, ETHRS1, ETHRS2, ETHRS3, IVTAB, BIT, CORTH, ZRC */
+
+/* Local variables that need not be saved */
+
+/* The following are used regardless of CORRP's value */
+/* The following are used only if CORRP is .TRUE. */
+
+/* Local state */
+
+/* The following are used regardless of CORRP's value */
+/* The following are used only if CORRP is .TRUE. */
+/* I am guessing the initial values for IVP2H, IOVOIC, DRC, DPIT, */
+/* and DRMS. They should be checked to see if they are reasonable.
+*/
+/* I'm also guessing for ERATE, but I think 0 is the right initial
+*/
+/* value. */
+ /* Parameter adjustments */
+ if (irc) {
+ --irc;
+ }
+ if (voice) {
+ --voice;
+ }
+ if (rc) {
+ --rc;
+ }
+
+ /* Function Body */
+
+ iptold = &(st->iptold);
+ first = &(st->first);
+ ivp2h = &(st->ivp2h);
+ iovoic = &(st->iovoic);
+ iavgp = &(st->iavgp);
+ erate = &(st->erate);
+ drc = &(st->drc[0]);
+ dpit = &(st->dpit[0]);
+ drms = &(st->drms[0]);
+
+/* DATA statements for "constants" defined above. */
+/* IF (LISTL.GE.3) WRITE(FDEBUG,800) IPITV,IRMS,(IRC(J),J=1,ORDER) */
+/* 800 FORMAT(1X,' <<ERRCOR IN>>',T32,6X,I6,I5,T50,10I8) */
+/* If no error correction, do pitch and voicing then jump to decode */
+ i4 = detau[*ipitv];
+ if (! contrl_1.corrp) {
+ voice[1] = 1;
+ voice[2] = 1;
+ if (*ipitv <= 1) {
+ voice[1] = 0;
+ }
+ if (*ipitv == 0 || *ipitv == 2) {
+ voice[2] = 0;
+ }
+ *pitch = i4;
+ if (*pitch <= 4) {
+ *pitch = *iptold;
+ }
+ if (voice[1] == 1 && voice[2] == 1) {
+ *iptold = *pitch;
+ }
+ if (voice[1] != voice[2]) {
+ *pitch = *iptold;
+ }
+ goto L900;
+ }
+/* Do error correction pitch and voicing */
+ if (i4 > 4) {
+ dpit[0] = i4;
+ ivoic = 2;
+ *iavgp = (*iavgp * 15 + i4 + 8) / 16;
+ } else {
+ ivoic = i4;
+ dpit[0] = *iavgp;
+ }
+ drms[0] = *irms;
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ drc[i__ * 3 - 3] = irc[i__];
+ }
+/* Determine index to IVTAB from V/UV decision */
+/* If error rate is high then use alternate table */
+ index = (*ivp2h << 4) + (*iovoic << 2) + ivoic + 1;
+ i1 = ivtab[index - 1];
+ ipit = i1 & 3;
+ icorf = i1 / 8;
+ if (*erate < ethrs) {
+ icorf /= 64;
+ }
+/* Determine error rate: 4=high 1=low */
+ ixcor = 4;
+ if (*erate < ethrs3) {
+ ixcor = 3;
+ }
+ if (*erate < ethrs2) {
+ ixcor = 2;
+ }
+ if (*erate < ethrs1) {
+ ixcor = 1;
+ }
+/* Voice/unvoice decision determined from bits 0 and 1 of IVTAB */
+ voice[1] = icorf / 2 & 1;
+ voice[2] = icorf & 1;
+/* Skip decoding on first frame because present data not yet available */
+ if (*first) {
+ *first = FALSE_;
+/* Assign PITCH a "default" value on the first call, since */
+/* otherwise it would be left uninitialized. The two lines
+*/
+/* below were copied from above, since it seemed like a */
+/* reasonable thing to do for the first call. */
+ *pitch = i4;
+ if (*pitch <= 4) {
+ *pitch = *iptold;
+ }
+ goto L500;
+ }
+/* If bit 4 of ICORF is set then correct RMS and RC(1) - RC(4). */
+/* Determine error rate and correct errors using a Hamming 8,4 code */
+/* during transition or unvoiced frame. If IOUT is negative, */
+/* more than 1 error occurred, use previous frame's parameters. */
+ if ((icorf & bit[3]) != 0) {
+ errcnt = 0;
+ lsb = drms[1] & 1;
+ index = (drc[22] << 4) + drms[1] / 2;
+ ham84_(&index, &iout, &errcnt);
+ drms[1] = drms[2];
+ if (iout >= 0) {
+ drms[1] = (iout << 1) + lsb;
+ }
+ for (i__ = 1; i__ <= 4; ++i__) {
+ if (i__ == 1) {
+ i1 = ((drc[25] & 7) << 1) + (drc[28] & 1);
+ } else {
+ i1 = drc[(9 - i__) * 3 - 2] & 15;
+ }
+ i2 = drc[(5 - i__) * 3 - 2] & 31;
+ lsb = i2 & 1;
+ index = (i1 << 4) + i2 / 2;
+ ham84_(&index, &iout, &errcnt);
+ if (iout >= 0) {
+ iout = (iout << 1) + lsb;
+ if ((iout & 16) == 16) {
+ iout += -32;
+ }
+ } else {
+ iout = drc[(5 - i__) * 3 - 1];
+ }
+ drc[(5 - i__) * 3 - 2] = iout;
+ }
+/* Determine error rate */
+ *erate = (integer)(*erate * .96875f + errcnt * 102);
+ }
+/* Get unsmoothed RMS, RC's, and PITCH */
+ *irms = drms[1];
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ irc[i__] = drc[i__ * 3 - 2];
+ }
+ if (ipit == 1) {
+ dpit[1] = dpit[2];
+ }
+ if (ipit == 3) {
+ dpit[1] = dpit[0];
+ }
+ *pitch = dpit[1];
+/* If bit 2 of ICORF is set then smooth RMS and RC's, */
+ if ((icorf & bit[1]) != 0) {
+ if ((i__1 = drms[1] - drms[0], (real) abs(i__1)) >= corth[ixcor + 3]
+ && (i__2 = drms[1] - drms[2], (real) abs(i__2)) >= corth[
+ ixcor + 3]) {
+ *irms = median_(&drms[2], &drms[1], drms);
+ }
+ for (i__ = 1; i__ <= 6; ++i__) {
+ if ((i__1 = drc[i__ * 3 - 2] - drc[i__ * 3 - 3], (real) abs(i__1))
+ >= corth[ixcor + ((i__ + 2) << 2) - 5] && (i__2 = drc[i__ *
+ 3 - 2] - drc[i__ * 3 - 1], (real) abs(i__2)) >= corth[
+ ixcor + ((i__ + 2) << 2) - 5]) {
+ irc[i__] = median_(&drc[i__ * 3 - 1], &drc[i__ * 3 - 2], &drc[
+ i__ * 3 - 3]);
+ }
+ }
+ }
+/* If bit 3 of ICORF is set then smooth pitch */
+ if ((icorf & bit[2]) != 0) {
+ if ((i__1 = dpit[1] - dpit[0], (real) abs(i__1)) >= corth[ixcor - 1]
+ && (i__2 = dpit[1] - dpit[2], (real) abs(i__2)) >= corth[
+ ixcor - 1]) {
+ *pitch = median_(&dpit[2], &dpit[1], dpit);
+ }
+ }
+/* If bit 5 of ICORF is set then RC(5) - RC(10) are loaded with */
+/* values so that after quantization bias is removed in decode */
+/* the values will be zero. */
+L500:
+ if ((icorf & bit[4]) != 0) {
+ i__1 = contrl_1.order;
+ for (i__ = 5; i__ <= i__1; ++i__) {
+ irc[i__] = zrc[i__ - 1];
+ }
+ }
+/* House keeping - one frame delay */
+ *iovoic = ivoic;
+ *ivp2h = voice[2];
+ dpit[2] = dpit[1];
+ dpit[1] = dpit[0];
+ drms[2] = drms[1];
+ drms[1] = drms[0];
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ drc[i__ * 3 - 1] = drc[i__ * 3 - 2];
+ drc[i__ * 3 - 2] = drc[i__ * 3 - 3];
+ }
+L900:
+/* IF (LISTL.GE.3)WRITE(FDEBUG,801)VOICE,PITCH,IRMS,(IRC(J),J=1,ORDER) */
+/* 801 FORMAT(1X,'<<ERRCOR OUT>>',T32,2I3,I6,I5,T50,10I8) */
+/* Decode RMS */
+ *irms = rmst[(31 - *irms) * 2];
+/* Decode RC(1) and RC(2) from log-area-ratios */
+/* Protect from illegal coded value (-16) caused by bit errors */
+ for (i__ = 1; i__ <= 2; ++i__) {
+ i2 = irc[i__];
+ i1 = 0;
+ if (i2 < 0) {
+ i1 = 1;
+ i2 = -i2;
+ if (i2 > 15) {
+ i2 = 0;
+ }
+ }
+ i2 = detab7[i2 * 2];
+ if (i1 == 1) {
+ i2 = -i2;
+ }
+ ishift = 15 - nbit[i__ - 1];
+ irc[i__] = i2 * pow_ii(&c__2, &ishift);
+ }
+/* Decode RC(3)-RC(10) to sign plus 14 bits */
+ i__1 = contrl_1.order;
+ for (i__ = 3; i__ <= i__1; ++i__) {
+ i2 = irc[i__];
+ ishift = 15 - nbit[i__ - 1];
+ i2 *= pow_ii(&c__2, &ishift);
+ i2 += qb[i__ - 3];
+ irc[i__] = (integer)(i2 * descl[i__ - 3] + deadd[i__ - 3]);
+ }
+/* IF (LISTL.GE.3) WRITE(FDEBUG,811) IRMS, (IRC(I),I=1,ORDER) */
+/* 811 FORMAT(1X,'<<DECODE OUT>>',T45,I4,1X,10I8) */
+/* Scale RMS and RC's to reals */
+ *rms = (real) (*irms);
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ rc[i__] = irc[i__] / 16384.f;
+ }
+ return 0;
+} /* decode_ */
diff --git a/trunk/codecs/lpc10/deemp.c b/trunk/codecs/lpc10/deemp.c
new file mode 100644
index 000000000..c3b5909ff
--- /dev/null
+++ b/trunk/codecs/lpc10/deemp.c
@@ -0,0 +1,154 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:23:46 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:34 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int deemp_(real *x, integer *n, struct lpc10_decoder_state *st);
+#endif
+
+/* ***************************************************************** */
+
+/* DEEMP Version 48 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:23:46 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:34 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/20 15:54:37 jaf */
+/* Added comments about which indices of array arguments are read or */
+/* written. */
+
+/* Added entry INITDEEMP to reinitialize the local state variables, if */
+/* desired. */
+
+/* Revision 1.2 1996/03/14 22:11:13 jaf */
+/* Comments added explaining which of the local variables of this */
+/* subroutine need to be saved from one invocation to the next, and which */
+/* do not. */
+
+/* Revision 1.1 1996/02/07 14:44:53 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* De-Emphasize output speech with 1 / ( 1 - .75z**-1 ) */
+/* cascaded with 200 Hz high pass filter */
+/* ( 1 - 1.9998z**-1 + z**-2 ) / ( 1 - 1.75z**-1 + .78z**-2 ) */
+
+/* WARNING! The coefficients above may be out of date with the code */
+/* below. Either that, or some kind of transformation was performed */
+/* on the coefficients above to create the code below. */
+
+/* Input: */
+/* N - Number of samples */
+/* Input/Output: */
+/* X - Speech */
+/* Indices 1 through N are read before being written. */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITDEEMP. */
+
+/* Subroutine */ int deemp_(real *x, integer *n, struct lpc10_decoder_state *st)
+{
+ /* Initialized data */
+
+ real *dei1;
+ real *dei2;
+ real *deo1;
+ real *deo2;
+ real *deo3;
+
+ /* System generated locals */
+ integer i__1;
+ real r__1;
+
+ /* Local variables */
+ integer k;
+ real dei0;
+
+/* Arguments */
+/* Local variables that need not be saved */
+/* Local state */
+/* All of the locals saved below were not given explicit initial */
+/* values in the original code. I think 0 is a safe choice. */
+ /* Parameter adjustments */
+ if (x) {
+ --x;
+ }
+
+ /* Function Body */
+
+ dei1 = &(st->dei1);
+ dei2 = &(st->dei2);
+ deo1 = &(st->deo1);
+ deo2 = &(st->deo2);
+ deo3 = &(st->deo3);
+
+ i__1 = *n;
+ for (k = 1; k <= i__1; ++k) {
+ dei0 = x[k];
+ r__1 = x[k] - *dei1 * 1.9998f + *dei2;
+ x[k] = r__1 + *deo1 * 2.5f - *deo2 * 2.0925f + *deo3 * .585f;
+ *dei2 = *dei1;
+ *dei1 = dei0;
+ *deo3 = *deo2;
+ *deo2 = *deo1;
+ *deo1 = x[k];
+ }
+ return 0;
+} /* deemp_ */
diff --git a/trunk/codecs/lpc10/difmag.c b/trunk/codecs/lpc10/difmag.c
new file mode 100644
index 000000000..ab59e8c9a
--- /dev/null
+++ b/trunk/codecs/lpc10/difmag.c
@@ -0,0 +1,133 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:32:31 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int difmag_(real *speech, integer *lpita, integer *tau, integer *ltau, integer *maxlag, real *amdf, integer *minptr, integer *maxptr);
+#endif
+
+/* ********************************************************************** */
+
+/* DIFMAG Version 49 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:14 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:31 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/15 23:09:39 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 14:41:31 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:45:04 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Compute Average Magnitude Difference Function */
+
+/* Inputs: */
+/* SPEECH - Low pass filtered speech */
+/* Indices MIN_N1 through MAX_N1+LPITA-1 are read, where */
+/* MIN_N1 = (MAXLAG - MAX_TAU)/2+1 MAX_TAU = max of TAU(I) for I=1,LTAU
+*/
+/* MAX_N1 = (MAXLAG - MIN_TAU)/2+1 MIN_TAU = min of TAU(I) for I=1,LTAU
+*/
+/* LPITA - Length of speech buffer */
+/* TAU - Table of lags */
+/* Indices 1 through LTAU read. */
+/* LTAU - Number of lag values to compute */
+/* MAXLAG - Maximum possible lag value */
+/* Outputs: */
+/* (All of these outputs are also read, but only after being written.) */
+/* AMDF - Average Magnitude Difference for each lag in TAU */
+/* Indices 1 through LTAU written */
+/* MINPTR - Index of minimum AMDF value */
+/* MAXPTR - Index of maximum AMDF value */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int difmag_(real *speech, integer *lpita, integer *tau,
+ integer *ltau, integer *maxlag, real *amdf, integer *minptr, integer *
+ maxptr)
+{
+ /* System generated locals */
+ integer i__1, i__2;
+ real r__1;
+
+ /* Local variables */
+ integer i__, j, n1, n2;
+ real sum;
+
+/* Arguments */
+/* Local variables that need not be saved */
+/* Local state */
+/* None */
+ /* Parameter adjustments */
+ --amdf;
+ --tau;
+ --speech;
+
+ /* Function Body */
+ *minptr = 1;
+ *maxptr = 1;
+ i__1 = *ltau;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ n1 = (*maxlag - tau[i__]) / 2 + 1;
+ n2 = n1 + *lpita - 1;
+ sum = 0.f;
+ i__2 = n2;
+ for (j = n1; j <= i__2; j += 4) {
+ sum += (r__1 = speech[j] - speech[j + tau[i__]], abs(r__1));
+ }
+ amdf[i__] = sum;
+ if (amdf[i__] < amdf[*minptr]) {
+ *minptr = i__;
+ }
+ if (amdf[i__] > amdf[*maxptr]) {
+ *maxptr = i__;
+ }
+ }
+ return 0;
+} /* difmag_ */
+
diff --git a/trunk/codecs/lpc10/dyptrk.c b/trunk/codecs/lpc10/dyptrk.c
new file mode 100644
index 000000000..216b55994
--- /dev/null
+++ b/trunk/codecs/lpc10/dyptrk.c
@@ -0,0 +1,405 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:25:29 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:26 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int dyptrk_(real *amdf, integer *ltau, integer *minptr, integer *voice, integer *pitch, integer *midx, struct lpc10_encoder_state *st);
+/* comlen contrl_ 12 */
+#endif
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* ********************************************************************* */
+
+/* DYPTRK Version 52 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:25:29 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:26 jaf
+ * Initial revision
+ * */
+/* Revision 1.5 1996/03/26 19:35:35 jaf */
+/* Commented out trace statements. */
+
+/* Revision 1.4 1996/03/19 18:03:22 jaf */
+/* Replaced the initialization "DATA P/60*DEPTH*0/" with "DATA P/120*0/", */
+/* because apparently Fortran (or at least f2c) can't handle expressions */
+/* like that. */
+
+/* Revision 1.3 1996/03/19 17:38:32 jaf */
+/* Added comments about the local variables that should be saved from one */
+/* invocation to the next. None of them were given initial values in the */
+/* original code, but from my testing, it appears that initializing them */
+/* all to 0 works. */
+
+/* Added entry INITDYPTRK to reinitialize these local variables. */
+
+/* Revision 1.2 1996/03/13 16:32:17 jaf */
+/* Comments added explaining which of the local variables of this */
+/* subroutine need to be saved from one invocation to the next, and which */
+/* do not. */
+
+/* WARNING! Some of them that should are never given initial values in */
+/* this code. Hopefully, Fortran 77 defines initial values for them, but */
+/* even so, giving them explicit initial values is preferable. */
+
+/* Revision 1.1 1996/02/07 14:45:14 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Dynamic Pitch Tracker */
+
+/* Input: */
+/* AMDF - Average Magnitude Difference Function array */
+/* Indices 1 through LTAU read, and MINPTR */
+/* LTAU - Number of lags in AMDF */
+/* MINPTR - Location of minimum AMDF value */
+/* VOICE - Voicing decision */
+/* Output: */
+/* PITCH - Smoothed pitch value, 2 frames delayed */
+/* MIDX - Initial estimate of current frame pitch */
+/* Compile time constant: */
+/* DEPTH - Number of frames to trace back */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITDYPTRK. */
+
+/* Subroutine */ int dyptrk_(real *amdf, integer *ltau, integer *
+ minptr, integer *voice, integer *pitch, integer *midx,
+ struct lpc10_encoder_state *st)
+{
+ /* Initialized data */
+
+ real *s;
+ integer *p;
+ integer *ipoint;
+ real *alphax;
+
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ integer pbar;
+ real sbar;
+ integer path[2], iptr, i__, j;
+ real alpha, minsc, maxsc;
+
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:25:29 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:26 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Parameters/constants */
+/* Local variables that need not be saved */
+/* Note that PATH is only used for debugging purposes, and can be */
+/* removed. */
+/* Local state */
+/* It would be a bit more "general" to define S(LTAU), if Fortran */
+/* allows the argument of a function to be used as the dimension of
+*/
+/* a local array variable. */
+/* IPOINT is always in the range 0 to DEPTH-1. */
+/* WARNING! */
+
+/* In the original version of this subroutine, IPOINT, ALPHAX, */
+/* every element of S, and potentially any element of P with the */
+/* second index value .NE. IPTR were read without being given */
+/* initial values (all indices of P with second index equal to */
+/* IPTR are all written before being read in this subroutine). */
+
+/* From examining the code carefully, it appears that all of these
+*/
+/* should be saved from one invocation to the next. */
+
+/* I've run lpcsim with the "-l 6" option to see all of the */
+/* debugging information that is printed out by this subroutine */
+/* below, and it appears that S, P, IPOINT, and ALPHAX are all */
+/* initialized to 0 (these initial values would likely be different
+*/
+/* on different platforms, compilers, etc.). Given that the output
+*/
+/* of the coder sounds reasonable, I'm going to initialize these */
+/* variables to 0 explicitly. */
+
+ s = &(st->s[0]);
+ p = &(st->p[0]);
+ ipoint = &(st->ipoint);
+ alphax = &(st->alphax);
+
+
+ /* Parameter adjustments */
+ if (amdf) {
+ --amdf;
+ }
+
+ /* Function Body */
+
+/* Calculate the confidence factor ALPHA, used as a threshold slope in
+*/
+/* SEESAW. If unvoiced, set high slope so that every point in P array
+*/
+/* is marked as a potential pitch frequency. A scaled up version (ALPHAX
+)*/
+/* is used to maintain arithmetic precision. */
+ if (*voice == 1) {
+ *alphax = *alphax * .75f + amdf[*minptr] / 2.f;
+ } else {
+ *alphax *= .984375f;
+ }
+ alpha = *alphax / 16;
+ if (*voice == 0 && *alphax < 128.f) {
+ alpha = 8.f;
+ }
+/* SEESAW: Construct a pitch pointer array and intermediate winner functio
+n*/
+/* Left to right pass: */
+ iptr = *ipoint + 1;
+ p[iptr * 60 - 60] = 1;
+ i__ = 1;
+ pbar = 1;
+ sbar = s[0];
+ i__1 = *ltau;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ sbar += alpha;
+ if (sbar < s[i__ - 1]) {
+ s[i__ - 1] = sbar;
+ p[i__ + iptr * 60 - 61] = pbar;
+ } else {
+ sbar = s[i__ - 1];
+ p[i__ + iptr * 60 - 61] = i__;
+ pbar = i__;
+ }
+ }
+/* Right to left pass: */
+ i__ = pbar - 1;
+ sbar = s[i__];
+ while(i__ >= 1) {
+ sbar += alpha;
+ if (sbar < s[i__ - 1]) {
+ s[i__ - 1] = sbar;
+ p[i__ + iptr * 60 - 61] = pbar;
+ } else {
+ pbar = p[i__ + iptr * 60 - 61];
+ i__ = pbar;
+ sbar = s[i__ - 1];
+ }
+ --i__;
+ }
+/* Update S using AMDF */
+/* Find maximum, minimum, and location of minimum */
+ s[0] += amdf[1] / 2;
+ minsc = s[0];
+ maxsc = minsc;
+ *midx = 1;
+ i__1 = *ltau;
+ for (i__ = 2; i__ <= i__1; ++i__) {
+ s[i__ - 1] += amdf[i__] / 2;
+ if (s[i__ - 1] > maxsc) {
+ maxsc = s[i__ - 1];
+ }
+ if (s[i__ - 1] < minsc) {
+ *midx = i__;
+ minsc = s[i__ - 1];
+ }
+ }
+/* Subtract MINSC from S to prevent overflow */
+ i__1 = *ltau;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ s[i__ - 1] -= minsc;
+ }
+ maxsc -= minsc;
+/* Use higher octave pitch if significant null there */
+ j = 0;
+ for (i__ = 20; i__ <= 40; i__ += 10) {
+ if (*midx > i__) {
+ if (s[*midx - i__ - 1] < maxsc / 4) {
+ j = i__;
+ }
+ }
+ }
+ *midx -= j;
+/* TRACE: look back two frames to find minimum cost pitch estimate */
+ j = *ipoint;
+ *pitch = *midx;
+ for (i__ = 1; i__ <= 2; ++i__) {
+ j = j % 2 + 1;
+ *pitch = p[*pitch + j * 60 - 61];
+ path[i__ - 1] = *pitch;
+ }
+
+/* The following statement subtracts one from IPOINT, mod DEPTH. I
+*/
+/* think the author chose to add DEPTH-1, instead of subtracting 1,
+*/
+/* because then it will work even if MOD doesn't work as desired on
+*/
+/* negative arguments. */
+
+ *ipoint = (*ipoint + 1) % 2;
+ return 0;
+} /* dyptrk_ */
diff --git a/trunk/codecs/lpc10/encode.c b/trunk/codecs/lpc10/encode.c
new file mode 100644
index 000000000..b81799f6b
--- /dev/null
+++ b/trunk/codecs/lpc10/encode.c
@@ -0,0 +1,373 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:32:21 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int encode_(integer *voice, integer *pitch, real *rms, real *rc, integer *ipitch, integer *irms, integer *irc);
+/* comlen contrl_ 12 */
+#endif
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* Table of constant values */
+
+static integer c__2 = 2;
+
+/* ***************************************************************** */
+
+/* ENCODE Version 54 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:21 jaf
+ * Initial revision
+ * */
+/* Revision 1.5 1996/03/26 19:35:50 jaf */
+/* Commented out trace statements. */
+
+/* Revision 1.4 1996/03/21 00:26:29 jaf */
+/* Added the comment that this subroutine has no local state. */
+
+/* In the last check-in, I forgot to mention that I had added comments */
+/* explaining which indices of array arguments are read or written. */
+
+/* Revision 1.3 1996/03/21 00:22:39 jaf */
+/* Added comments explaining that all local arrays are effectively */
+/* constants. */
+
+/* Revision 1.2 1996/03/13 18:48:33 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:45:29 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Quantize LPC parameters for transmission */
+
+/* INPUTS: */
+/* VOICE - Half frame voicing decisions */
+/* Indices 1 through 2 read. */
+/* PITCH - Pitch */
+/* RMS - Energy */
+/* RC - Reflection coefficients */
+/* Indices 1 through ORDER read. */
+/* CORRP - Error Correction: TRUE = yes, FALSE = none */
+/* (this is defined in file control.fh) */
+/* OUTPUTS: */
+/* IPITCH - Coded pitch and voicing */
+/* IRMS - Quantized energy */
+/* IRC - Quantized reflection coefficients */
+/* Indices 1 through MAX(ORDER,2) written. */
+/* If CORRP is .TRUE., then indices 1 through 10 written */
+/* for unvoiced frames. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int encode_(integer *voice, integer *pitch, real *rms, real *
+ rc, integer *ipitch, integer *irms, integer *irc)
+{
+ /* Initialized data */
+
+ static integer enctab[16] = { 0,7,11,12,13,10,6,1,14,9,5,2,3,4,8,15 };
+ static integer entau[60] = { 19,11,27,25,29,21,23,22,30,14,15,7,39,38,46,
+ 42,43,41,45,37,53,49,51,50,54,52,60,56,58,26,90,88,92,84,86,82,83,
+ 81,85,69,77,73,75,74,78,70,71,67,99,97,113,112,114,98,106,104,108,
+ 100,101,76 };
+ static integer enadd[8] = { 1920,-768,2432,1280,3584,1536,2816,-1152 };
+ static real enscl[8] = { .0204f,.0167f,.0145f,.0147f,.0143f,.0135f,.0125f,
+ .0112f };
+ static integer enbits[8] = { 6,5,4,4,4,4,3,3 };
+ static integer entab6[64] = { 0,0,0,0,0,0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,
+ 3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,9,9,
+ 9,10,10,11,11,12,13,14,15 };
+ static integer rmst[64] = { 1024,936,856,784,718,656,600,550,502,460,420,
+ 384,352,328,294,270,246,226,206,188,172,158,144,132,120,110,102,
+ 92,84,78,70,64,60,54,50,46,42,38,34,32,30,26,24,22,20,18,17,16,15,
+ 14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 };
+
+ /* System generated locals */
+ integer i__1, i__2;
+
+ /* Builtin functions */
+ integer pow_ii(integer *, integer *);
+
+ /* Local variables */
+ integer idel, nbit, i__, j, i2, i3, mrk;
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:21 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:21 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Parameters/constants */
+/* These arrays are not Fortran PARAMETER's, but they are defined */
+/* by DATA statements below, and their contents are never altered.
+*/
+/* Local variables that need not be saved */
+ /* Parameter adjustments */
+ --irc;
+ --rc;
+ --voice;
+
+ /* Function Body */
+/* Scale RMS and RC's to integers */
+ *irms = (integer)*rms;
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ irc[i__] = (integer)(rc[i__] * 32768.f);
+ }
+/* IF(LISTL.GE.3)WRITE(FDEBUG,800)VOICE,PITCH,IRMS,(IRC(I),I=1,ORDER) */
+/* 800 FORMAT(1X,/,' <<ENCODE IN>>',T32,2I3,I6,I5,T50,10I8) */
+/* Encode pitch and voicing */
+ if (voice[1] != 0 && voice[2] != 0) {
+ *ipitch = entau[*pitch - 1];
+ } else {
+ if (contrl_1.corrp) {
+ *ipitch = 0;
+ if (voice[1] != voice[2]) {
+ *ipitch = 127;
+ }
+ } else {
+ *ipitch = (voice[1] << 1) + voice[2];
+ }
+ }
+/* Encode RMS by binary table search */
+ j = 32;
+ idel = 16;
+ *irms = min(*irms,1023);
+ while(idel > 0) {
+ if (*irms > rmst[j - 1]) {
+ j -= idel;
+ }
+ if (*irms < rmst[j - 1]) {
+ j += idel;
+ }
+ idel /= 2;
+ }
+ if (*irms > rmst[j - 1]) {
+ --j;
+ }
+ *irms = 31 - j / 2;
+/* Encode RC(1) and (2) as log-area-ratios */
+ for (i__ = 1; i__ <= 2; ++i__) {
+ i2 = irc[i__];
+ mrk = 0;
+ if (i2 < 0) {
+ i2 = -i2;
+ mrk = 1;
+ }
+ i2 /= 512;
+ i2 = min(i2,63);
+ i2 = entab6[i2];
+ if (mrk != 0) {
+ i2 = -i2;
+ }
+ irc[i__] = i2;
+ }
+/* Encode RC(3) - (10) linearly, remove bias then scale */
+ i__1 = contrl_1.order;
+ for (i__ = 3; i__ <= i__1; ++i__) {
+ i2 = irc[i__] / 2;
+ i2 = (integer)((i2 + enadd[contrl_1.order + 1 - i__ - 1]) * enscl[
+ contrl_1.order + 1 - i__ - 1]);
+/* Computing MIN */
+ i__2 = max(i2,-127);
+ i2 = min(i__2,127);
+ nbit = enbits[contrl_1.order + 1 - i__ - 1];
+ i3 = 0;
+ if (i2 < 0) {
+ i3 = -1;
+ }
+ i2 /= pow_ii(&c__2, &nbit);
+ if (i3 == -1) {
+ --i2;
+ }
+ irc[i__] = i2;
+ }
+/* Protect the most significant bits of the most */
+/* important parameters during non-voiced frames. */
+/* RC(1) - RC(4) are protected using 20 parity bits */
+/* replacing RC(5) - RC(10). */
+ if (contrl_1.corrp) {
+ if (*ipitch == 0 || *ipitch == 127) {
+ irc[5] = enctab[(irc[1] & 30) / 2];
+ irc[6] = enctab[(irc[2] & 30) / 2];
+ irc[7] = enctab[(irc[3] & 30) / 2];
+ irc[8] = enctab[(*irms & 30) / 2];
+ irc[9] = enctab[(irc[4] & 30) / 2] / 2;
+ irc[10] = enctab[(irc[4] & 30) / 2] & 1;
+ }
+ }
+/* IF(LISTL.GE.3)WRITE(FDEBUG,801)VOICE,IPITCH,IRMS,(IRC(J),J=1,ORDER) */
+/* 801 FORMAT(1X,'<<ENCODE OUT>>',T32,2I3,I6,I5,T50,10I8) */
+ return 0;
+} /* encode_ */
+
diff --git a/trunk/codecs/lpc10/energy.c b/trunk/codecs/lpc10/energy.c
new file mode 100644
index 000000000..eada04bef
--- /dev/null
+++ b/trunk/codecs/lpc10/energy.c
@@ -0,0 +1,103 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:32:17 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int energy_(integer *len, real *speech, real *rms);
+#endif
+
+/* ********************************************************************* */
+
+/* ENERGY Version 50 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:17 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/18 21:17:41 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 16:46:02 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:45:40 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Compute RMS energy. */
+
+/* Input: */
+/* LEN - Length of speech buffer */
+/* SPEECH - Speech buffer */
+/* Indices 1 through LEN read. */
+/* Output: */
+/* RMS - Root Mean Square energy */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int energy_(integer *len, real *speech, real *rms)
+{
+ /* System generated locals */
+ integer i__1;
+
+ /* Builtin functions */
+ double sqrt(doublereal);
+
+ /* Local variables */
+ integer i__;
+
+/* Arguments */
+/* Local variables that need not be saved */
+ /* Parameter adjustments */
+ --speech;
+
+ /* Function Body */
+ *rms = 0.f;
+ i__1 = *len;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ *rms += speech[i__] * speech[i__];
+ }
+ *rms = (real)sqrt(*rms / *len);
+ return 0;
+} /* energy_ */
+
diff --git a/trunk/codecs/lpc10/f2c.h b/trunk/codecs/lpc10/f2c.h
new file mode 100644
index 000000000..e50d642e0
--- /dev/null
+++ b/trunk/codecs/lpc10/f2c.h
@@ -0,0 +1,325 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:26:28 jaf
+ * Any typedef defining a type that was used in lpc10_encoder_state or
+ * lpc10_decoder_state struct's was commented out here and added to
+ * lpc10.h.
+ *
+ * Revision 1.1 1996/08/19 22:32:13 jaf
+ * Initial revision
+ *
+
+*/
+
+/*
+ * f2c.h
+ *
+ * SCCS ID: @(#)f2c.h 1.2 96/05/19
+ */
+
+/* f2c.h -- Standard Fortran to C header file */
+
+/** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed."
+
+ - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */
+
+#ifndef F2C_INCLUDE
+#define F2C_INCLUDE
+
+#include "lpc10.h"
+
+/*typedef long int integer;*/
+/*typedef INT32 integer;*/
+/*typedef short int shortint;*/
+/*typedef INT16 shortint;*/
+/*typedef float real;*/
+/* doublereal only used for function arguments to sqrt, exp, etc. */
+typedef double doublereal;
+/* 32 bits seems wasteful, but there really aren't that many logical
+ * variables around, and making them 32 bits could avoid word
+ * alignment problems, perhaps. */
+/*typedef long int logical;*/
+/*typedef INT32 logical;*/
+/* The following types are not used in the translated C code for the
+ * LPC-10 coder, but they might be needed by the definitions down
+ * below, so they don't cause compilation errors. */
+typedef char *address;
+typedef struct { real r, i; } complex;
+typedef struct { doublereal r, i; } doublecomplex;
+typedef short int shortlogical;
+typedef char logical1;
+typedef char integer1;
+/* typedef long long longint; */ /* system-dependent */
+
+#define TRUE_ (1)
+#define FALSE_ (0)
+
+/* Extern is for use with -E */
+#ifndef Extern
+#define Extern extern
+#endif
+
+/* I/O stuff */
+
+#ifdef f2c_i2
+/* for -i2 */
+typedef short flag;
+typedef short ftnlen;
+typedef short ftnint;
+#else
+typedef long int flag;
+typedef long int ftnlen;
+typedef long int ftnint;
+#endif
+
+/*external read, write*/
+typedef struct
+{ flag cierr;
+ ftnint ciunit;
+ flag ciend;
+ char *cifmt;
+ ftnint cirec;
+} cilist;
+
+/*internal read, write*/
+typedef struct
+{ flag icierr;
+ char *iciunit;
+ flag iciend;
+ char *icifmt;
+ ftnint icirlen;
+ ftnint icirnum;
+} icilist;
+
+/*open*/
+typedef struct
+{ flag oerr;
+ ftnint ounit;
+ char *ofnm;
+ ftnlen ofnmlen;
+ char *osta;
+ char *oacc;
+ char *ofm;
+ ftnint orl;
+ char *oblnk;
+} olist;
+
+/*close*/
+typedef struct
+{ flag cerr;
+ ftnint cunit;
+ char *csta;
+} cllist;
+
+/*rewind, backspace, endfile*/
+typedef struct
+{ flag aerr;
+ ftnint aunit;
+} alist;
+
+/* inquire */
+typedef struct
+{ flag inerr;
+ ftnint inunit;
+ char *infile;
+ ftnlen infilen;
+ ftnint *inex; /*parameters in standard's order*/
+ ftnint *inopen;
+ ftnint *innum;
+ ftnint *innamed;
+ char *inname;
+ ftnlen innamlen;
+ char *inacc;
+ ftnlen inacclen;
+ char *inseq;
+ ftnlen inseqlen;
+ char *indir;
+ ftnlen indirlen;
+ char *infmt;
+ ftnlen infmtlen;
+ char *inform;
+ ftnint informlen;
+ char *inunf;
+ ftnlen inunflen;
+ ftnint *inrecl;
+ ftnint *innrec;
+ char *inblank;
+ ftnlen inblanklen;
+} inlist;
+
+#define VOID void
+
+union Multitype { /* for multiple entry points */
+ integer1 g;
+ shortint h;
+ integer i;
+ /* longint j; */
+ real r;
+ doublereal d;
+ complex c;
+ doublecomplex z;
+ };
+
+typedef union Multitype Multitype;
+
+/*typedef long int Long;*/ /* No longer used; formerly in Namelist */
+
+struct Vardesc { /* for Namelist */
+ char *name;
+ char *addr;
+ ftnlen *dims;
+ int type;
+ };
+typedef struct Vardesc Vardesc;
+
+struct Namelist {
+ char *name;
+ Vardesc **vars;
+ int nvars;
+ };
+typedef struct Namelist Namelist;
+
+#define abs(x) ((x) >= 0 ? (x) : -(x))
+#define dabs(x) (doublereal)abs(x)
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+#define dmin(a,b) (doublereal)min(a,b)
+#define dmax(a,b) (doublereal)max(a,b)
+
+/* procedure parameter types for -A and -C++ */
+
+#define F2C_proc_par_types 1
+#ifdef __cplusplus
+typedef int /* Unknown procedure type */ (*U_fp)(...);
+typedef shortint (*J_fp)(...);
+typedef integer (*I_fp)(...);
+typedef real (*R_fp)(...);
+typedef doublereal (*D_fp)(...), (*E_fp)(...);
+typedef /* Complex */ VOID (*C_fp)(...);
+typedef /* Double Complex */ VOID (*Z_fp)(...);
+typedef logical (*L_fp)(...);
+typedef shortlogical (*K_fp)(...);
+typedef /* Character */ VOID (*H_fp)(...);
+typedef /* Subroutine */ int (*S_fp)(...);
+#else
+typedef int /* Unknown procedure type */ (*U_fp)(VOID);
+typedef shortint (*J_fp)(VOID);
+typedef integer (*I_fp)(VOID);
+typedef real (*R_fp)(VOID);
+typedef doublereal (*D_fp)(VOID), (*E_fp)(VOID);
+typedef /* Complex */ VOID (*C_fp)(VOID);
+typedef /* Double Complex */ VOID (*Z_fp)(VOID);
+typedef logical (*L_fp)(VOID);
+typedef shortlogical (*K_fp)(VOID);
+typedef /* Character */ VOID (*H_fp)(VOID);
+typedef /* Subroutine */ int (*S_fp)(VOID);
+#endif
+/* E_fp is for real functions when -R is not specified */
+typedef VOID C_f; /* complex function */
+typedef VOID H_f; /* character function */
+typedef VOID Z_f; /* double complex function */
+typedef doublereal E_f; /* real function with -R not specified */
+
+/* undef any lower-case symbols that your C compiler predefines, e.g.: */
+
+#ifndef Skip_f2c_Undefs
+#undef cray
+#undef gcos
+#undef mc68010
+#undef mc68020
+#undef mips
+#undef pdp11
+#undef sgi
+#undef sparc
+#undef sun
+#undef sun2
+#undef sun3
+#undef sun4
+#undef u370
+#undef u3b
+#undef u3b2
+#undef u3b5
+#undef unix
+#undef vax
+#endif
+
+#ifdef KR_headers
+extern integer pow_ii(ap, bp);
+extern double r_sign(a,b);
+extern integer i_nint(x);
+#else
+extern integer pow_ii(integer *ap, integer *bp);
+extern double r_sign(real *a, real *b);
+extern integer i_nint(real *x);
+
+#endif
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int bsynz_(real *coef, integer *ip, integer *iv,
+ real *sout, real *rms, real *ratio, real *g2pass,
+ struct lpc10_decoder_state *st);
+extern int chanwr_(integer *order, integer *ipitv, integer *irms,
+ integer *irc, integer *ibits, struct lpc10_encoder_state *st);
+extern int chanrd_(integer *order, integer *ipitv, integer *irms,
+ integer *irc, integer *ibits);
+extern int chanwr_0_(int n__, integer *order, integer *ipitv,
+ integer *irms, integer *irc, integer *ibits,
+ struct lpc10_encoder_state *st);
+extern int dcbias_(integer *len, real *speech, real *sigout);
+extern int decode_(integer *ipitv, integer *irms, integer *irc,
+ integer *voice, integer *pitch, real *rms,
+ real *rc, struct lpc10_decoder_state *st);
+extern int deemp_(real *x, integer *n, struct lpc10_decoder_state *st);
+extern int difmag_(real *speech, integer *lpita, integer *tau, integer *ltau,
+ integer *maxlag, real *amdf, integer *minptr, integer *maxptr);
+extern int dyptrk_(real *amdf, integer *ltau, integer *
+ minptr, integer *voice, integer *pitch, integer *midx,
+ struct lpc10_encoder_state *st);
+extern int encode_(integer *voice, integer *pitch, real *rms, real *rc,
+ integer *ipitch, integer *irms, integer *irc);
+extern int energy_(integer *len, real *speech, real *rms);
+extern int ham84_(integer *input, integer *output, integer *errcnt);
+extern int hp100_(real *speech, integer *start, integer *end,
+ struct lpc10_encoder_state *st);
+extern int inithp100_(void);
+extern int invert_(integer *order, real *phi, real *psi, real *rc);
+extern int irc2pc_(real *rc, real *pc, integer *order, real *gprime, real *g2pass);
+extern int ivfilt_(real *lpbuf, real *ivbuf, integer *len, integer *nsamp, real *ivrc);
+extern int lpcdec_(integer *bits, real *speech);
+extern int initlpcdec_(void);
+extern int lpcenc_(real *speech, integer *bits);
+extern int initlpcenc_(void);
+extern int lpfilt_(real *inbuf, real *lpbuf, integer *len, integer *nsamp);
+extern integer median_(integer *d1, integer *d2, integer *d3);
+extern int mload_(integer *order, integer *awins, integer *awinf, real *speech, real *phi, real *psi);
+extern int onset_(real *pebuf, integer *osbuf, integer *osptr, integer *oslen, integer *sbufl, integer *sbufh, integer *lframe, struct lpc10_encoder_state *st);
+extern int pitsyn_(integer *order, integer *voice, integer *pitch, real *rms, real *rc, integer *lframe, integer *ivuv, integer *ipiti, real *rmsi, real *rci, integer *nout, real *ratio, struct lpc10_decoder_state *st);
+extern int placea_(integer *ipitch, integer *voibuf, integer *obound, integer *af, integer *vwin, integer *awin, integer *ewin, integer *lframe, integer *maxwin);
+extern int placev_(integer *osbuf, integer *osptr, integer *oslen, integer *obound, integer *vwin, integer *af, integer *lframe, integer *minwin, integer *maxwin, integer *dvwinl, integer *dvwinh);
+extern int preemp_(real *inbuf, real *pebuf, integer *nsamp, real *coef, real *z__);
+extern int prepro_(real *speech, integer *length,
+ struct lpc10_encoder_state *st);
+extern int decode_(integer *ipitv, integer *irms, integer *irc, integer *voice, integer *pitch, real *rms, real *rc, struct lpc10_decoder_state *st);
+extern integer random_(struct lpc10_decoder_state *st);
+extern int rcchk_(integer *order, real *rc1f, real *rc2f);
+extern int synths_(integer *voice, integer *pitch, real *rms, real *rc, real *speech, integer *k, struct lpc10_decoder_state *st);
+extern int tbdm_(real *speech, integer *lpita, integer *tau, integer *ltau, real *amdf, integer *minptr, integer *maxptr, integer *mintau);
+extern int voicin_(integer *vwin, real *inbuf, real *lpbuf, integer *buflim, integer *half, real *minamd, real *maxamd, integer *mintau, real *ivrc, integer *obound, integer *voibuf, integer *af, struct lpc10_encoder_state *st);
+extern int vparms_(integer *vwin, real *inbuf, real *lpbuf, integer *buflim, integer *half, real *dither, integer *mintau, integer *zc, integer *lbe, integer *fbe, real *qs, real *rc1, real *ar_b__, real *ar_f__);
+
+#endif
+
+
+#endif /* ! defined F2C_INCLUDE */
diff --git a/trunk/codecs/lpc10/f2clib.c b/trunk/codecs/lpc10/f2clib.c
new file mode 100644
index 000000000..037172828
--- /dev/null
+++ b/trunk/codecs/lpc10/f2clib.c
@@ -0,0 +1,85 @@
+/*
+
+$Log$
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:32:10 jaf
+ * Initial revision
+ *
+
+*/
+
+/*
+ * f2clib.c
+ *
+ * SCCS ID: @(#)f2clib.c 1.2 96/05/19
+ */
+
+#include "f2c.h"
+
+#ifdef KR_headers
+integer pow_ii(ap, bp) integer *ap, *bp;
+#else
+integer pow_ii(integer *ap, integer *bp)
+#endif
+{
+ integer pow, x, n;
+ unsigned long u;
+
+ x = *ap;
+ n = *bp;
+
+ if (n <= 0) {
+ if (n == 0 || x == 1)
+ return 1;
+ if (x != -1)
+ return x == 0 ? 0 : 1/x;
+ n = -n;
+ }
+ u = n;
+ for(pow = 1; ; )
+ {
+ if(u & 01)
+ pow *= x;
+ if(u >>= 1)
+ x *= x;
+ else
+ break;
+ }
+ return(pow);
+ }
+
+
+
+#ifdef KR_headers
+double r_sign(a,b) real *a, *b;
+#else
+double r_sign(real *a, real *b)
+#endif
+{
+double x;
+x = (*a >= 0 ? *a : - *a);
+return( *b >= 0 ? x : -x);
+}
+
+
+
+#ifdef KR_headers
+double floor();
+integer i_nint(x) real *x;
+#else
+#undef abs
+#include "math.h"
+integer i_nint(real *x)
+#endif
+{
+return( (integer)((*x)>=0 ?
+ floor(*x + .5) : -floor(.5 - *x)) );
+}
diff --git a/trunk/codecs/lpc10/ham84.c b/trunk/codecs/lpc10/ham84.c
new file mode 100644
index 000000000..fddd8f3c0
--- /dev/null
+++ b/trunk/codecs/lpc10/ham84.c
@@ -0,0 +1,126 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:32:07 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int ham84_(integer *input, integer *output, integer *errcnt);
+#endif
+
+/* ***************************************************************** */
+
+/* HAM84 Version 45G */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:07 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/21 15:26:00 jaf */
+/* Put comment header in standard form. */
+
+/* Revision 1.2 1996/03/13 22:00:13 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:47:04 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Hamming 8,4 Decoder - can correct 1 out of seven bits */
+/* and can detect up to two errors. */
+
+/* Input: */
+/* INPUT - Seven bit data word, 4 bits parameter and */
+/* 4 bits parity information */
+/* Input/Output: */
+/* ERRCNT - Sums errors detected by Hamming code */
+/* Output: */
+/* OUTPUT - 4 corrected parameter bits */
+
+/* This subroutine is entered with an eight bit word in INPUT. The 8th */
+/* bit is parity and is stripped off. The remaining 7 bits address the */
+/* hamming 8,4 table and the output OUTPUT from the table gives the 4 */
+/* bits of corrected data. If bit 4 is set, no error was detected. */
+/* ERRCNT is the number of errors counted. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int ham84_(integer *input, integer *output, integer *errcnt)
+{
+ /* Initialized data */
+
+ static integer dactab[128] = { 16,0,0,3,0,5,14,7,0,9,14,11,14,13,30,14,0,
+ 9,2,7,4,7,7,23,9,25,10,9,12,9,14,7,0,5,2,11,5,21,6,5,8,11,11,27,
+ 12,5,14,11,2,1,18,2,12,5,2,7,12,9,2,11,28,12,12,15,0,3,3,19,4,13,
+ 6,3,8,13,10,3,13,29,14,13,4,1,10,3,20,4,4,7,10,9,26,10,4,13,10,15,
+ 8,1,6,3,6,5,22,6,24,8,8,11,8,13,6,15,1,17,2,1,4,1,6,15,8,1,10,15,
+ 12,15,15,31 };
+
+ integer i__, j, parity;
+
+/* Arguments */
+/* Parameters/constants */
+/* Local variables that need not be saved */
+/* Determine parity of input word */
+ parity = *input & 255;
+ parity ^= parity / 16;
+ parity ^= parity / 4;
+ parity ^= parity / 2;
+ parity &= 1;
+ i__ = dactab[*input & 127];
+ *output = i__ & 15;
+ j = i__ & 16;
+ if (j != 0) {
+/* No errors detected in seven bits */
+ if (parity != 0) {
+ ++(*errcnt);
+ }
+ } else {
+/* One or two errors detected */
+ ++(*errcnt);
+ if (parity == 0) {
+/* Two errors detected */
+ ++(*errcnt);
+ *output = -1;
+ }
+ }
+ return 0;
+} /* ham84_ */
+
diff --git a/trunk/codecs/lpc10/hp100.c b/trunk/codecs/lpc10/hp100.c
new file mode 100644
index 000000000..9df9e5939
--- /dev/null
+++ b/trunk/codecs/lpc10/hp100.c
@@ -0,0 +1,169 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:28:05 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:04 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int hp100_(real *speech, integer *start, integer *end,
+ struct lpc10_encoder_state *st);
+extern int inithp100_(void);
+#endif
+
+/* ********************************************************************* */
+
+/* HP100 Version 55 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:28:05 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:32:04 jaf
+ * Initial revision
+ * */
+/* Revision 1.6 1996/03/15 16:45:25 jaf */
+/* Rearranged a few comments. */
+
+/* Revision 1.5 1996/03/14 23:20:54 jaf */
+/* Added comments about when INITHP100 should be used. */
+
+/* Revision 1.4 1996/03/14 23:08:08 jaf */
+/* Added an entry named INITHP100 that initializes the local state of */
+/* subroutine HP100. */
+
+/* Revision 1.3 1996/03/14 22:09:20 jaf */
+/* Comments added explaining which of the local variables of this */
+/* subroutine need to be saved from one invocation to the next, and which */
+/* do not. */
+
+/* Revision 1.2 1996/02/12 15:05:54 jaf */
+/* Added lots of comments explaining why I changed one line, which was a */
+/* declaration with initializations. */
+
+/* Revision 1.1 1996/02/07 14:47:12 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* 100 Hz High Pass Filter */
+
+/* Jan 92 - corrected typo (1.937148 to 1.935715), */
+/* rounded coefficients to 7 places, */
+/* corrected and merged gain (.97466**4), */
+/* merged numerator into first two sections. */
+
+/* Input: */
+/* start, end - Range of samples to filter */
+/* Input/Output: */
+/* speech(end) - Speech data. */
+/* Indices start through end are read and modified. */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITHP100. */
+/* Subroutine */ int hp100_(real *speech, integer *start, integer *end,
+ struct lpc10_encoder_state *st)
+{
+ /* Temporary local copies of variables in lpc10_encoder_state.
+ I've only created these because it might cause the loop below
+ to execute a bit faster to access local variables, rather than
+ variables in the lpc10_encoder_state structure. It is just a
+ guess that it will be faster. */
+
+ real z11;
+ real z21;
+ real z12;
+ real z22;
+
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ integer i__;
+ real si, err;
+
+/* Arguments */
+/* Local variables that need not be saved */
+/* Local state */
+ /* Parameter adjustments */
+ if (speech) {
+ --speech;
+ }
+
+ /* Function Body */
+
+ z11 = st->z11;
+ z21 = st->z21;
+ z12 = st->z12;
+ z22 = st->z22;
+
+ i__1 = *end;
+ for (i__ = *start; i__ <= i__1; ++i__) {
+ si = speech[i__];
+ err = si + z11 * 1.859076f - z21 * .8648249f;
+ si = err - z11 * 2.f + z21;
+ z21 = z11;
+ z11 = err;
+ err = si + z12 * 1.935715f - z22 * .9417004f;
+ si = err - z12 * 2.f + z22;
+ z22 = z12;
+ z12 = err;
+ speech[i__] = si * .902428f;
+ }
+
+ st->z11 = z11;
+ st->z21 = z21;
+ st->z12 = z12;
+ st->z22 = z22;
+
+ return 0;
+} /* hp100_ */
diff --git a/trunk/codecs/lpc10/invert.c b/trunk/codecs/lpc10/invert.c
new file mode 100644
index 000000000..912117f9c
--- /dev/null
+++ b/trunk/codecs/lpc10/invert.c
@@ -0,0 +1,193 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:32:00 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int invert_(integer *order, real *phi, real *psi, real *rc);
+#endif
+
+/* **************************************************************** */
+
+/* INVERT Version 45G */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:00 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/18 20:52:47 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 16:51:32 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Eliminated a comment from the original, describing a local array X */
+/* that appeared nowhere in the code. */
+
+/* Revision 1.1 1996/02/07 14:47:20 jaf */
+/* Initial revision */
+
+
+/* **************************************************************** */
+
+/* Invert a covariance matrix using Choleski decomposition method. */
+
+/* Input: */
+/* ORDER - Analysis order */
+/* PHI(ORDER,ORDER) - Covariance matrix */
+/* Indices (I,J) read, where ORDER .GE. I .GE. J .GE. 1.*/
+/* All other indices untouched. */
+/* PSI(ORDER) - Column vector to be predicted */
+/* Indices 1 through ORDER read. */
+/* Output: */
+/* RC(ORDER) - Pseudo reflection coefficients */
+/* Indices 1 through ORDER written, and then possibly read.
+*/
+/* Internal: */
+/* V(ORDER,ORDER) - Temporary matrix */
+/* Same indices written as read from PHI. */
+/* Many indices may be read and written again after */
+/* initially being copied from PHI, but all indices */
+/* are written before being read. */
+
+/* NOTE: Temporary matrix V is not needed and may be replaced */
+/* by PHI if the original PHI values do not need to be preserved. */
+
+/* Subroutine */ int invert_(integer *order, real *phi, real *psi, real *rc)
+{
+ /* System generated locals */
+ integer phi_dim1, phi_offset, i__1, i__2, i__3;
+ real r__1, r__2;
+
+ /* Local variables */
+ real save;
+ integer i__, j, k;
+ real v[100] /* was [10][10] */;
+
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:32:00 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Parameters/constants */
+/* Local variables that need not be saved */
+/* Decompose PHI into V * D * V' where V is a triangular matrix whose */
+/* main diagonal elements are all 1, V' is the transpose of V, and */
+/* D is a vector. Here D(n) is stored in location V(n,n). */
+ /* Parameter adjustments */
+ --rc;
+ --psi;
+ phi_dim1 = *order;
+ phi_offset = phi_dim1 + 1;
+ phi -= phi_offset;
+
+ /* Function Body */
+ i__1 = *order;
+ for (j = 1; j <= i__1; ++j) {
+ i__2 = *order;
+ for (i__ = j; i__ <= i__2; ++i__) {
+ v[i__ + j * 10 - 11] = phi[i__ + j * phi_dim1];
+ }
+ i__2 = j - 1;
+ for (k = 1; k <= i__2; ++k) {
+ save = v[j + k * 10 - 11] * v[k + k * 10 - 11];
+ i__3 = *order;
+ for (i__ = j; i__ <= i__3; ++i__) {
+ v[i__ + j * 10 - 11] -= v[i__ + k * 10 - 11] * save;
+ }
+ }
+/* Compute intermediate results, which are similar to RC's */
+ if ((r__1 = v[j + j * 10 - 11], abs(r__1)) < 1e-10f) {
+ goto L100;
+ }
+ rc[j] = psi[j];
+ i__2 = j - 1;
+ for (k = 1; k <= i__2; ++k) {
+ rc[j] -= rc[k] * v[j + k * 10 - 11];
+ }
+ v[j + j * 10 - 11] = 1.f / v[j + j * 10 - 11];
+ rc[j] *= v[j + j * 10 - 11];
+/* Computing MAX */
+/* Computing MIN */
+ r__2 = rc[j];
+ r__1 = min(r__2,.999f);
+ rc[j] = max(r__1,-.999f);
+ }
+ return 0;
+/* Zero out higher order RC's if algorithm terminated early */
+L100:
+ i__1 = *order;
+ for (i__ = j; i__ <= i__1; ++i__) {
+ rc[i__] = 0.f;
+ }
+/* Back substitute for PC's (if needed) */
+/* 110 DO J = ORDER,1,-1 */
+/* PC(J) = RC(J) */
+/* DO I = 1,J-1 */
+/* PC(J) = PC(J) - PC(I)*V(J,I) */
+/* END DO */
+/* END DO */
+ return 0;
+} /* invert_ */
+
diff --git a/trunk/codecs/lpc10/irc2pc.c b/trunk/codecs/lpc10/irc2pc.c
new file mode 100644
index 000000000..b96ff0d66
--- /dev/null
+++ b/trunk/codecs/lpc10/irc2pc.c
@@ -0,0 +1,151 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:31:56 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int irc2pc_(real *rc, real *pc, integer *order, real *gprime, real *g2pass);
+#endif
+
+/* ***************************************************************** */
+
+/* IRC2PC Version 48 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:56 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/20 15:47:19 jaf */
+/* Added comments about which indices of array arguments are read or */
+/* written. */
+
+/* Revision 1.2 1996/03/14 16:59:04 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:47:27 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Convert Reflection Coefficients to Predictor Coeficients */
+
+/* Inputs: */
+/* RC - Reflection coefficients */
+/* Indices 1 through ORDER read. */
+/* ORDER - Number of RC's */
+/* GPRIME - Excitation modification gain */
+/* Outputs: */
+/* PC - Predictor coefficients */
+/* Indices 1 through ORDER written. */
+/* Indices 1 through ORDER-1 are read after being written. */
+/* G2PASS - Excitation modification sharpening factor */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int irc2pc_(real *rc, real *pc, integer *order, real *gprime,
+ real *g2pass)
+{
+ /* System generated locals */
+ integer i__1, i__2;
+
+ /* Builtin functions */
+ double sqrt(doublereal);
+
+ /* Local variables */
+ real temp[10];
+ integer i__, j;
+
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:56 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Local variables that need not be saved */
+ /* Parameter adjustments */
+ --pc;
+ --rc;
+
+ /* Function Body */
+ *g2pass = 1.f;
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ *g2pass *= 1.f - rc[i__] * rc[i__];
+ }
+ *g2pass = *gprime * (real)sqrt(*g2pass);
+ pc[1] = rc[1];
+ i__1 = *order;
+ for (i__ = 2; i__ <= i__1; ++i__) {
+ i__2 = i__ - 1;
+ for (j = 1; j <= i__2; ++j) {
+ temp[j - 1] = pc[j] - rc[i__] * pc[i__ - j];
+ }
+ i__2 = i__ - 1;
+ for (j = 1; j <= i__2; ++j) {
+ pc[j] = temp[j - 1];
+ }
+ pc[i__] = rc[i__];
+ }
+ return 0;
+} /* irc2pc_ */
+
diff --git a/trunk/codecs/lpc10/ivfilt.c b/trunk/codecs/lpc10/ivfilt.c
new file mode 100644
index 000000000..784de2571
--- /dev/null
+++ b/trunk/codecs/lpc10/ivfilt.c
@@ -0,0 +1,136 @@
+/*
+
+$Log$
+Revision 1.16 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.15 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.2 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:31:53 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int ivfilt_(real *lpbuf, real *ivbuf, integer *len, integer *nsamp, real *ivrc);
+#endif
+
+/* ********************************************************************* */
+
+/* IVFILT Version 48 */
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:53 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/15 21:36:29 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 00:01:00 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:47:34 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* 2nd order inverse filter, speech is decimated 4:1 */
+
+/* Input: */
+/* LEN - Length of speech buffers */
+/* NSAMP - Number of samples to filter */
+/* LPBUF - Low pass filtered speech buffer */
+/* Indices LEN-NSAMP-7 through LEN read. */
+/* Output: */
+/* IVBUF - Inverse filtered speech buffer */
+/* Indices LEN-NSAMP+1 through LEN written. */
+/* IVRC - Inverse filter reflection coefficients (for voicing) */
+/* Indices 1 and 2 both written (also read, but only after writing).
+*/
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int ivfilt_(real *lpbuf, real *ivbuf, integer *len, integer *
+ nsamp, real *ivrc)
+{
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ integer i__, j, k;
+ real r__[3], pc1, pc2;
+
+/* Arguments */
+/* Local variables that need not be saved */
+/* Local state */
+/* None */
+/* Calculate Autocorrelations */
+ /* Parameter adjustments */
+ --ivbuf;
+ --lpbuf;
+ --ivrc;
+
+ /* Function Body */
+ for (i__ = 1; i__ <= 3; ++i__) {
+ r__[i__ - 1] = 0.f;
+ k = (i__ - 1) << 2;
+ i__1 = *len;
+ for (j = (i__ << 2) + *len - *nsamp; j <= i__1; j += 2) {
+ r__[i__ - 1] += lpbuf[j] * lpbuf[j - k];
+ }
+ }
+/* Calculate predictor coefficients */
+ pc1 = 0.f;
+ pc2 = 0.f;
+ ivrc[1] = 0.f;
+ ivrc[2] = 0.f;
+ if (r__[0] > 1e-10f) {
+ ivrc[1] = r__[1] / r__[0];
+ ivrc[2] = (r__[2] - ivrc[1] * r__[1]) / (r__[0] - ivrc[1] * r__[1]);
+ pc1 = ivrc[1] - ivrc[1] * ivrc[2];
+ pc2 = ivrc[2];
+ }
+/* Inverse filter LPBUF into IVBUF */
+ i__1 = *len;
+ for (i__ = *len + 1 - *nsamp; i__ <= i__1; ++i__) {
+ ivbuf[i__] = lpbuf[i__] - pc1 * lpbuf[i__ - 4] - pc2 * lpbuf[i__ - 8];
+ }
+ return 0;
+} /* ivfilt_ */
+
diff --git a/trunk/codecs/lpc10/liblpc10.vcproj b/trunk/codecs/lpc10/liblpc10.vcproj
new file mode 100644
index 000000000..a2449a109
--- /dev/null
+++ b/trunk/codecs/lpc10/liblpc10.vcproj
@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="liblpc10"
+ ProjectGUID="{FF1D238A-9D59-4850-838E-78182E05736B}"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="4"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;P_R_O_T_O_T_Y_P_E_S"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="4"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;P_R_O_T_O_T_Y_P_E_S"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\f2c.h"
+ >
+ </File>
+ <File
+ RelativePath=".\lpc10.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\analys.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bsynz.c"
+ >
+ </File>
+ <File
+ RelativePath=".\chanwr.c"
+ >
+ </File>
+ <File
+ RelativePath=".\dcbias.c"
+ >
+ </File>
+ <File
+ RelativePath=".\decode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\deemp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\difmag.c"
+ >
+ </File>
+ <File
+ RelativePath=".\dyptrk.c"
+ >
+ </File>
+ <File
+ RelativePath=".\encode.c"
+ >
+ </File>
+ <File
+ RelativePath=".\energy.c"
+ >
+ </File>
+ <File
+ RelativePath=".\f2clib.c"
+ >
+ </File>
+ <File
+ RelativePath=".\ham84.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hp100.c"
+ >
+ </File>
+ <File
+ RelativePath=".\invert.c"
+ >
+ </File>
+ <File
+ RelativePath=".\irc2pc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\ivfilt.c"
+ >
+ </File>
+ <File
+ RelativePath=".\lpcdec.c"
+ >
+ </File>
+ <File
+ RelativePath=".\lpcenc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\lpcini.c"
+ >
+ </File>
+ <File
+ RelativePath=".\lpfilt.c"
+ >
+ </File>
+ <File
+ RelativePath=".\median.c"
+ >
+ </File>
+ <File
+ RelativePath=".\mload.c"
+ >
+ </File>
+ <File
+ RelativePath=".\onset.c"
+ >
+ </File>
+ <File
+ RelativePath=".\pitsyn.c"
+ >
+ </File>
+ <File
+ RelativePath=".\placea.c"
+ >
+ </File>
+ <File
+ RelativePath=".\placev.c"
+ >
+ </File>
+ <File
+ RelativePath=".\preemp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prepro.c"
+ >
+ </File>
+ <File
+ RelativePath=".\random.c"
+ >
+ </File>
+ <File
+ RelativePath=".\rcchk.c"
+ >
+ </File>
+ <File
+ RelativePath=".\synths.c"
+ >
+ </File>
+ <File
+ RelativePath=".\tbdm.c"
+ >
+ </File>
+ <File
+ RelativePath=".\voicin.c"
+ >
+ </File>
+ <File
+ RelativePath=".\vparms.c"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/trunk/codecs/lpc10/lpc10.h b/trunk/codecs/lpc10/lpc10.h
new file mode 100644
index 000000000..a57f84f3f
--- /dev/null
+++ b/trunk/codecs/lpc10/lpc10.h
@@ -0,0 +1,256 @@
+/*
+
+$Log$
+Revision 1.18 2004/08/31 13:32:11 markster
+Merge NetBSD and Courtesty tone with modifications (bug #2329)
+
+Revision 1.17 2003/10/26 18:50:49 markster
+Make it build and run on MacOS X
+
+Revision 1.3 2003/10/26 18:50:49 markster
+Make it build and run on MacOS X
+
+Revision 1.2 2003/04/23 19:13:35 markster
+More OpenBSD patches
+
+Revision 1.1.1.2 2003/03/16 22:37:30 matteo
+dom mar 16 23:37:23 CET 2003
+
+Revision 1.2 2003/03/16 16:09:48 markster
+Mere James's cleanups for fewer build warnings
+
+Revision 1.1 2000/01/05 00:20:06 markster
+Add broken lpc10 code... It's not too far from working I don't think...
+
+ * Revision 1.1 1996/08/19 22:47:31 jaf
+ * Initial revision
+ *
+
+*/
+
+#ifndef __LPC10_H__
+#define __LPC10_H__
+
+#define P_R_O_T_O_T_Y_P_E_S
+
+#define LPC10_SAMPLES_PER_FRAME 180
+#define LPC10_BITS_IN_COMPRESSED_FRAME 54
+
+
+/*
+
+ The "#if defined"'s in this file are by no means intended to be
+ complete. They are what Nautilus uses, which has been successfully
+ compiled under DOS with the Microsoft C compiler, and under a few
+ versions of Unix with the GNU C compiler.
+
+ */
+
+#if defined(unix) || defined(__unix__) || defined(__NetBSD__)
+typedef short INT16;
+typedef int INT32;
+#endif
+
+
+#if defined(__MSDOS__) || defined(MSDOS)
+typedef int INT16;
+typedef long INT32;
+#endif
+
+#if defined(__APPLE__)
+typedef short INT16;
+typedef int INT32;
+#endif
+
+#if defined(WIN32) && defined(_MSC_VER)
+typedef __int16 INT16;
+typedef __int32 INT32;
+#pragma warning(disable: 4005)
+#endif
+
+
+/* The initial values for every member of this structure is 0, except
+ where noted in comments. */
+
+/* These two lines are copied from f2c.h. There should be a more
+ elegant way of doing this than having the same declarations in two
+ files. */
+
+typedef float real;
+typedef INT32 integer;
+typedef INT32 logical;
+typedef INT16 shortint;
+
+struct lpc10_encoder_state {
+ /* State used only by function hp100 */
+ real z11;
+ real z21;
+ real z12;
+ real z22;
+
+ /* State used by function analys */
+ real inbuf[540], pebuf[540];
+ real lpbuf[696], ivbuf[312];
+ real bias;
+ integer osbuf[10]; /* no initial value necessary */
+ integer osptr; /* initial value 1 */
+ integer obound[3];
+ integer vwin[6] /* was [2][3] */; /* initial value vwin[4] = 307; vwin[5] = 462; */
+ integer awin[6] /* was [2][3] */; /* initial value awin[4] = 307; awin[5] = 462; */
+ integer voibuf[8] /* was [2][4] */;
+ real rmsbuf[3];
+ real rcbuf[30] /* was [10][3] */;
+ real zpre;
+
+
+ /* State used by function onset */
+ real n;
+ real d__; /* initial value 1.f */
+ real fpc; /* no initial value necessary */
+ real l2buf[16];
+ real l2sum1;
+ integer l2ptr1; /* initial value 1 */
+ integer l2ptr2; /* initial value 9 */
+ integer lasti; /* no initial value necessary */
+ logical hyst; /* initial value FALSE_ */
+
+ /* State used by function voicin */
+ real dither; /* initial value 20.f */
+ real snr;
+ real maxmin;
+ real voice[6] /* was [2][3] */; /* initial value is probably unnecessary */
+ integer lbve, lbue, fbve, fbue;
+ integer ofbue, sfbue;
+ integer olbue, slbue;
+ /* Initial values:
+ lbve = 3000;
+ fbve = 3000;
+ fbue = 187;
+ ofbue = 187;
+ sfbue = 187;
+ lbue = 93;
+ olbue = 93;
+ slbue = 93;
+ snr = (real) (fbve / fbue << 6);
+ */
+
+ /* State used by function dyptrk */
+ real s[60];
+ integer p[120] /* was [60][2] */;
+ integer ipoint;
+ real alphax;
+
+ /* State used by function chanwr */
+ integer isync;
+
+};
+
+
+struct lpc10_decoder_state {
+
+ /* State used by function decode */
+ integer iptold; /* initial value 60 */
+ logical first; /* initial value TRUE_ */
+ integer ivp2h;
+ integer iovoic;
+ integer iavgp; /* initial value 60 */
+ integer erate;
+ integer drc[30] /* was [3][10] */;
+ integer dpit[3];
+ integer drms[3];
+
+ /* State used by function synths */
+ real buf[360];
+ integer buflen; /* initial value 180 */
+
+ /* State used by function pitsyn */
+ integer ivoico; /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ integer ipito; /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ real rmso; /* initial value 1.f */
+ real rco[10]; /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ integer jsamp; /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ logical first_pitsyn; /* initial value TRUE_ */
+
+ /* State used by function bsynz */
+ integer ipo;
+ real exc[166];
+ real exc2[166];
+ real lpi1;
+ real lpi2;
+ real lpi3;
+ real hpi1;
+ real hpi2;
+ real hpi3;
+ real rmso_bsynz;
+
+ /* State used by function random */
+ integer j; /* initial value 2 */
+ integer k; /* initial value 5 */
+ shortint y[5]; /* initial value { -21161,-8478,30892,-10216,16950 } */
+
+ /* State used by function deemp */
+ real dei1;
+ real dei2;
+ real deo1;
+ real deo2;
+ real deo3;
+
+};
+
+
+
+/*
+
+ Calling sequence:
+
+ Call create_lpc10_encoder_state(), which returns a pointer to an
+ already initialized lpc10_encoder_state structure.
+
+ lpc10_encode reads indices 0 through (LPC10_SAMPLES_PER_FRAME-1) of
+ array speech[], and writes indices 0 through
+ (LPC10_BITS_IN_COMPRESSED_FRAME-1) of array bits[], and both reads
+ and writes the lpc10_encoder_state structure contents. The
+ lpc10_encoder_state structure should *not* be initialized for every
+ frame of encoded speech. Once at the beginning of execution, done
+ automatically for you by create_lpc10_encoder_state(), is enough.
+
+ init_lpc10_encoder_state() reinitializes the lpc10_encoder_state
+ structure. This might be useful if you are finished processing one
+ sound sample, and want to reuse the same lpc10_encoder_state
+ structure to process another sound sample. There might be other
+ uses as well.
+
+ Note that the comments in the lpc10/lpcenc.c file imply that indices
+ 1 through 180 of array speech[] are read. These comments were
+ written for the Fortran version of the code, before it was
+ automatically converted to C by the conversion program f2c. f2c
+ seems to use the convention that the pointers to arrays passed as
+ function arguments point to the first index used in the Fortran
+ code, whatever index that might be (usually 1), and then it modifies
+ the pointer inside of the function, like so:
+
+ if (speech) {
+ --speech;
+ }
+
+ So that the code can access the first value at index 1 and the last
+ at index 180. This makes the translated C code "closer" to the
+ original Fortran code.
+
+ The calling sequence for the decoder is similar to the encoder. The
+ only significant difference is that the array bits[] is read
+ (indices 0 through (LPC10_BITS_IN_COMPRESSED_FRAME-1)), and the
+ array speech[] is written (indices 0 through
+ (LPC10_SAMPLES_PER_FRAME-1)).
+
+ */
+
+struct lpc10_encoder_state * create_lpc10_encoder_state (void);
+void init_lpc10_encoder_state (struct lpc10_encoder_state *st);
+int lpc10_encode (real *speech, INT32 *bits, struct lpc10_encoder_state *st);
+
+struct lpc10_decoder_state * create_lpc10_decoder_state (void);
+void init_lpc10_decoder_state (struct lpc10_decoder_state *st);
+int lpc10_decode (INT32 *bits, real *speech, struct lpc10_decoder_state *st);
+
+#endif /* __LPC10_H__ */
diff --git a/trunk/codecs/lpc10/lpcdec.c b/trunk/codecs/lpc10/lpcdec.c
new file mode 100644
index 000000000..dd859ffce
--- /dev/null
+++ b/trunk/codecs/lpc10/lpcdec.c
@@ -0,0 +1,297 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:30:11 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Changed name of function from lpcenc_ to lpc10_encode, simply to make
+ * all lpc10 functions have more consistent naming with each other.
+ *
+ * Revision 1.1 1996/08/19 22:31:48 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int lpcdec_(integer *bits, real *speech);
+extern int initlpcdec_(void);
+/* comlen contrl_ 12 */
+/*:ref: chanrd_ 14 5 4 4 4 4 4 */
+/*:ref: decode_ 14 7 4 4 4 4 4 6 6 */
+/*:ref: synths_ 14 6 4 4 6 6 6 4 */
+/*:ref: initdecode_ 14 0 */
+/*:ref: initsynths_ 14 0 */
+#endif
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* Table of constant values */
+
+static integer c__10 = 10;
+
+/* ***************************************************************** */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:30:11 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Changed name of function from lpcenc_ to lpc10_encode, simply to make
+ * all lpc10 functions have more consistent naming with each other.
+ *
+ * Revision 1.1 1996/08/19 22:31:48 jaf
+ * Initial revision
+ * */
+/* Revision 1.1 1996/03/28 00:03:00 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Decode 54 bits to one frame of 180 speech samples. */
+
+/* Input: */
+/* BITS - 54 encoded bits, stored 1 per array element. */
+/* Indices 1 through 53 read (SYNC bit ignored). */
+/* Output: */
+/* SPEECH - Speech encoded as real values in the range [-1,+1]. */
+/* Indices 1 through 180 written. */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITLPCDEC. */
+
+/* Subroutine */ int lpc10_decode(integer *bits, real *speech,
+ struct lpc10_decoder_state *st)
+{
+ integer irms, voice[2], pitch, ipitv;
+ extern /* Subroutine */ int decode_(integer *, integer *, integer *,
+ integer *, integer *, real *, real *, struct lpc10_decoder_state *);
+ real rc[10];
+ extern /* Subroutine */ int chanrd_(integer *, integer *, integer *,
+ integer *, integer *), synths_(integer *,
+ integer *, real *, real *, real *, integer *,
+ struct lpc10_decoder_state *);
+ integer irc[10], len;
+ real rms;
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:30:11 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Changed name of function from lpcenc_ to lpc10_encode, simply to make
+ * all lpc10 functions have more consistent naming with each other.
+ *
+ * Revision 1.1 1996/08/19 22:31:48 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:30:11 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Changed name of function from lpcenc_ to lpc10_encode, simply to make
+ * all lpc10 functions have more consistent naming with each other.
+ *
+ * Revision 1.1 1996/08/19 22:31:48 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Local variables that need not be saved */
+/* Uncoded speech parameters */
+/* Coded speech parameters */
+/* Others */
+/* Local state */
+/* None */
+ /* Parameter adjustments */
+ if (bits) {
+ --bits;
+ }
+ if (speech) {
+ --speech;
+ }
+
+ /* Function Body */
+
+ chanrd_(&c__10, &ipitv, &irms, irc, &bits[1]);
+ decode_(&ipitv, &irms, irc, voice, &pitch, &rms, rc, st);
+ synths_(voice, &pitch, &rms, rc, &speech[1], &len, st);
+ return 0;
+} /* lpcdec_ */
diff --git a/trunk/codecs/lpc10/lpcenc.c b/trunk/codecs/lpc10/lpcenc.c
new file mode 100644
index 000000000..989a2defd
--- /dev/null
+++ b/trunk/codecs/lpc10/lpcenc.c
@@ -0,0 +1,181 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:31:21 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Changed name of function from lpcenc_ to lpc10_encode, simply to make
+ * all lpc10 functions have more consistent naming with each other.
+ *
+ * Revision 1.1 1996/08/19 22:31:44 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int lpcenc_(real *speech, integer *bits);
+extern int initlpcenc_(void);
+/*:ref: prepro_ 14 2 6 4 */
+/*:ref: analys_ 14 5 6 4 4 6 6 */
+/*:ref: encode_ 14 7 4 4 6 6 4 4 4 */
+/*:ref: chanwr_ 14 5 4 4 4 4 4 */
+/*:ref: initprepro_ 14 0 */
+/*:ref: initanalys_ 14 0 */
+#endif
+
+/* Table of constant values */
+
+static integer c__180 = 180;
+static integer c__10 = 10;
+
+/* ***************************************************************** */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:31:21 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Changed name of function from lpcenc_ to lpc10_encode, simply to make
+ * all lpc10 functions have more consistent naming with each other.
+ *
+ * Revision 1.1 1996/08/19 22:31:44 jaf
+ * Initial revision
+ * */
+/* Revision 1.2 1996/03/28 00:01:22 jaf */
+/* Commented out some trace statements. */
+
+/* Revision 1.1 1996/03/28 00:00:27 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Encode one frame of 180 speech samples to 54 bits. */
+
+/* Input: */
+/* SPEECH - Speech encoded as real values in the range [-1,+1]. */
+/* Indices 1 through 180 read, and modified (by PREPRO). */
+/* Output: */
+/* BITS - 54 encoded bits, stored 1 per array element. */
+/* Indices 1 through 54 written. */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITLPCENC. */
+
+/* Subroutine */ int lpc10_encode(real *speech, integer *bits,
+ struct lpc10_encoder_state *st)
+{
+ integer irms, voice[2], pitch, ipitv;
+ real rc[10];
+ extern /* Subroutine */ int encode_(integer *, integer *, real *, real *,
+ integer *, integer *, integer *), chanwr_(integer *, integer *,
+ integer *, integer *, integer *, struct lpc10_encoder_state *),
+ analys_(real *, integer *,
+ integer *, real *, real *, struct lpc10_encoder_state *),
+ prepro_(real *, integer *, struct lpc10_encoder_state *);
+ integer irc[10];
+ real rms;
+
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:31:21 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Changed name of function from lpcenc_ to lpc10_encode, simply to make
+ * all lpc10 functions have more consistent naming with each other.
+ *
+ * Revision 1.1 1996/08/19 22:31:44 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Local variables that need not be saved */
+/* Uncoded speech parameters */
+/* Coded speech parameters */
+/* Local state */
+/* None */
+ /* Parameter adjustments */
+ if (speech) {
+ --speech;
+ }
+ if (bits) {
+ --bits;
+ }
+
+ /* Function Body */
+ prepro_(&speech[1], &c__180, st);
+ analys_(&speech[1], voice, &pitch, &rms, rc, st);
+ encode_(voice, &pitch, &rms, rc, &ipitv, &irms, irc);
+ chanwr_(&c__10, &ipitv, &irms, irc, &bits[1], st);
+ return 0;
+} /* lpcenc_ */
diff --git a/trunk/codecs/lpc10/lpcini.c b/trunk/codecs/lpc10/lpcini.c
new file mode 100644
index 000000000..ebe229a5c
--- /dev/null
+++ b/trunk/codecs/lpc10/lpcini.c
@@ -0,0 +1,446 @@
+/*
+
+$Log$
+Revision 1.18 2003/10/21 18:08:11 markster
+Fix include order
+
+Revision 1.5 2003/10/21 18:08:11 markster
+Fix include order
+
+Revision 1.4 2003/10/21 02:57:29 markster
+FreeBSD patch, take 2
+
+Revision 1.3 2003/10/16 21:11:30 martinp
+Revert the previous patch since it's braking compilation
+
+Revision 1.1 2003/02/12 13:59:15 matteo
+Initial revision
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:35:41 jaf
+ * Added functions for allocating and initializing lpc10_encoder_state
+ * and lpc10_decoder_state structures.
+ *
+ * Revision 1.1 1996/08/19 22:31:40 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include <stdlib.h>
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int lpcini_(void);
+/* comlen contrl_ 12 */
+/*:ref: initlpcenc_ 14 0 */
+/*:ref: initlpcdec_ 14 0 */
+#endif
+
+/* Common Block Declarations */
+
+struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* ***************************************************************** */
+
+/* $Log$
+ * Revision 1.18 2003/10/21 18:08:11 markster
+ * Fix include order
+ *
+ * Revision 1.5 2003/10/21 18:08:11 markster
+ * Fix include order
+ *
+ * Revision 1.4 2003/10/21 02:57:29 markster
+ * FreeBSD patch, take 2
+ *
+ * Revision 1.3 2003/10/16 21:11:30 martinp
+ * Revert the previous patch since it's braking compilation
+ *
+ * Revision 1.1 2003/02/12 13:59:15 matteo
+ * Initial revision
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:35:41 jaf
+ * Added functions for allocating and initializing lpc10_encoder_state
+ * and lpc10_decoder_state structures.
+ *
+ * Revision 1.1 1996/08/19 22:31:40 jaf
+ * Initial revision
+ * */
+/* Revision 1.1 1996/03/28 00:04:05 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Initialize COMMON block variables used by LPC-10 encoder and decoder, */
+/* and call initialization routines for both of them. */
+
+/* Subroutine */ int lpcini_(void)
+{
+
+/* $Log$
+ * Revision 1.18 2003/10/21 18:08:11 markster
+ * Fix include order
+ *
+ * Revision 1.5 2003/10/21 18:08:11 markster
+ * Fix include order
+ *
+ * Revision 1.4 2003/10/21 02:57:29 markster
+ * FreeBSD patch, take 2
+ *
+ * Revision 1.3 2003/10/16 21:11:30 martinp
+ * Revert the previous patch since it's braking compilation
+ *
+ * Revision 1.1 2003/02/12 13:59:15 matteo
+ * Initial revision
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:35:41 jaf
+ * Added functions for allocating and initializing lpc10_encoder_state
+ * and lpc10_decoder_state structures.
+ *
+ * Revision 1.1 1996/08/19 22:31:40 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* $Log$
+ * Revision 1.18 2003/10/21 18:08:11 markster
+ * Fix include order
+ *
+ * Revision 1.5 2003/10/21 18:08:11 markster
+ * Fix include order
+ *
+ * Revision 1.4 2003/10/21 02:57:29 markster
+ * FreeBSD patch, take 2
+ *
+ * Revision 1.3 2003/10/16 21:11:30 martinp
+ * Revert the previous patch since it's braking compilation
+ *
+ * Revision 1.1 2003/02/12 13:59:15 matteo
+ * Initial revision
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:35:41 jaf
+ * Added functions for allocating and initializing lpc10_encoder_state
+ * and lpc10_decoder_state structures.
+ *
+ * Revision 1.1 1996/08/19 22:31:40 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+ contrl_1.order = 10;
+ contrl_1.lframe = 180;
+ contrl_1.corrp = TRUE_;
+ return 0;
+} /* lpcini_ */
+
+
+
+/* Allocate memory for, and initialize, the state that needs to be
+ kept from encoding one frame to the next for a single
+ LPC-10-compressed audio stream. Return 0 if malloc fails,
+ otherwise return pointer to new structure. */
+
+struct lpc10_encoder_state *
+create_lpc10_encoder_state()
+{
+ struct lpc10_encoder_state *st;
+
+ st = (struct lpc10_encoder_state *)
+ malloc((unsigned) sizeof (struct lpc10_encoder_state));
+ if (st != 0) {
+ init_lpc10_encoder_state(st);
+ }
+ return (st);
+}
+
+
+
+void init_lpc10_encoder_state(struct lpc10_encoder_state *st)
+{
+ int i;
+
+ lpcini_();
+
+ /* State used only by function hp100 */
+ st->z11 = 0.0f;
+ st->z21 = 0.0f;
+ st->z12 = 0.0f;
+ st->z22 = 0.0f;
+
+ /* State used by function analys */
+ for (i = 0; i < 540; i++) {
+ st->inbuf[i] = 0.0f;
+ st->pebuf[i] = 0.0f;
+ }
+ for (i = 0; i < 696; i++) {
+ st->lpbuf[i] = 0.0f;
+ }
+ for (i = 0; i < 312; i++) {
+ st->ivbuf[i] = 0.0f;
+ }
+ st->bias = 0.0f;
+ /* integer osbuf[10]; */ /* no initial value necessary */
+ st->osptr = 1;
+ for (i = 0; i < 3; i++) {
+ st->obound[i] = 0;
+ }
+ st->vwin[4] = 307;
+ st->vwin[5] = 462;
+ st->awin[4] = 307;
+ st->awin[5] = 462;
+ for (i = 0; i < 8; i++) {
+ st->voibuf[i] = 0;
+ }
+ for (i = 0; i < 3; i++) {
+ st->rmsbuf[i] = 0.0f;
+ }
+ for (i = 0; i < 30; i++) {
+ st->rcbuf[i] = 0.0f;
+ }
+ st->zpre = 0.0f;
+
+
+ /* State used by function onset */
+ st->n = 0.0f;
+ st->d__ = 1.0f;
+ /* real fpc; */ /* no initial value necessary */
+ for (i = 0; i < 16; i++) {
+ st->l2buf[i] = 0.0f;
+ }
+ st->l2sum1 = 0.0f;
+ st->l2ptr1 = 1;
+ st->l2ptr2 = 9;
+ /* integer lasti; */ /* no initial value necessary */
+ st->hyst = FALSE_;
+
+ /* State used by function voicin */
+ st->dither = 20.0f;
+ st->maxmin = 0.0f;
+ for (i = 0; i < 6; i++) {
+ st->voice[i] = 0.0f;
+ }
+ st->lbve = 3000;
+ st->fbve = 3000;
+ st->fbue = 187;
+ st->ofbue = 187;
+ st->sfbue = 187;
+ st->lbue = 93;
+ st->olbue = 93;
+ st->slbue = 93;
+ st->snr = (real) (st->fbve / st->fbue << 6);
+
+ /* State used by function dyptrk */
+ for (i = 0; i < 60; i++) {
+ st->s[i] = 0.0f;
+ }
+ for (i = 0; i < 120; i++) {
+ st->p[i] = 0;
+ }
+ st->ipoint = 0;
+ st->alphax = 0.0f;
+
+ /* State used by function chanwr */
+ st->isync = 0;
+
+}
+
+
+
+/* Allocate memory for, and initialize, the state that needs to be
+ kept from decoding one frame to the next for a single
+ LPC-10-compressed audio stream. Return 0 if malloc fails,
+ otherwise return pointer to new structure. */
+
+struct lpc10_decoder_state *
+create_lpc10_decoder_state()
+{
+ struct lpc10_decoder_state *st;
+
+ st = (struct lpc10_decoder_state *)
+ malloc((unsigned) sizeof (struct lpc10_decoder_state));
+ if (st != 0) {
+ init_lpc10_decoder_state(st);
+ }
+ return (st);
+}
+
+
+
+void init_lpc10_decoder_state(struct lpc10_decoder_state *st)
+{
+ int i;
+
+ lpcini_();
+
+ /* State used by function decode */
+ st->iptold = 60;
+ st->first = TRUE_;
+ st->ivp2h = 0;
+ st->iovoic = 0;
+ st->iavgp = 60;
+ st->erate = 0;
+ for (i = 0; i < 30; i++) {
+ st->drc[i] = 0;
+ }
+ for (i = 0; i < 3; i++) {
+ st->dpit[i] = 0;
+ st->drms[i] = 0;
+ }
+
+ /* State used by function synths */
+ for (i = 0; i < 360; i++) {
+ st->buf[i] = 0.0f;
+ }
+ st->buflen = 180;
+
+ /* State used by function pitsyn */
+ /* ivoico; */ /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ /* ipito; */ /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ st->rmso = 1.0f;
+ /* rco[10]; */ /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ /* integer jsamp; */ /* no initial value necessary as long as first_pitsyn is initially TRUE_ */
+ st->first_pitsyn = TRUE_;
+
+ /* State used by function bsynz */
+ st->ipo = 0;
+ for (i = 0; i < 166; i++) {
+ st->exc[i] = 0.0f;
+ st->exc2[i] = 0.0f;
+ }
+ st->lpi1 = 0.0f;
+ st->lpi2 = 0.0f;
+ st->lpi3 = 0.0f;
+ st->hpi1 = 0.0f;
+ st->hpi2 = 0.0f;
+ st->hpi3 = 0.0f;
+ st->rmso_bsynz = 0.0f;
+
+ /* State used by function random */
+ st->j = 2;
+ st->k = 5;
+ st->y[0] = (shortint) -21161;
+ st->y[1] = (shortint) -8478;
+ st->y[2] = (shortint) 30892;
+ st->y[3] = (shortint) -10216;
+ st->y[4] = (shortint) 16950;
+
+ /* State used by function deemp */
+ st->dei1 = 0.0f;
+ st->dei2 = 0.0f;
+ st->deo1 = 0.0f;
+ st->deo2 = 0.0f;
+ st->deo3 = 0.0f;
+}
diff --git a/trunk/codecs/lpc10/lpfilt.c b/trunk/codecs/lpc10/lpfilt.c
new file mode 100644
index 000000000..375528921
--- /dev/null
+++ b/trunk/codecs/lpc10/lpfilt.c
@@ -0,0 +1,125 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:31:35 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int lpfilt_(real *inbuf, real *lpbuf, integer *len, integer *nsamp);
+#endif
+
+/* *********************************************************************** */
+
+/* LPFILT Version 55 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:35 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/15 16:53:49 jaf */
+/* Just put comment header in standard form. */
+
+/* Revision 1.2 1996/03/12 23:58:06 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:47:44 jaf */
+/* Initial revision */
+
+
+/* *********************************************************************** */
+
+/* 31 Point Equiripple FIR Low-Pass Filter */
+/* Linear phase, delay = 15 samples */
+
+/* Passband: ripple = 0.25 dB, cutoff = 800 Hz */
+/* Stopband: atten. = 40. dB, cutoff = 1240 Hz */
+
+/* Inputs: */
+/* LEN - Length of speech buffers */
+/* NSAMP - Number of samples to filter */
+/* INBUF - Input speech buffer */
+/* Indices len-nsamp-29 through len are read. */
+/* Output: */
+/* LPBUF - Low passed speech buffer (must be different array than INBUF) */
+/* Indices len+1-nsamp through len are written. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int lpfilt_(real *inbuf, real *lpbuf, integer *len, integer *
+ nsamp)
+{
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ integer j;
+ real t;
+
+/* Arguments */
+/* Parameters/constants */
+/* Local variables that need not be saved */
+/* Local state */
+/* None */
+ /* Parameter adjustments */
+ --lpbuf;
+ --inbuf;
+
+ /* Function Body */
+ i__1 = *len;
+ for (j = *len + 1 - *nsamp; j <= i__1; ++j) {
+ t = (inbuf[j] + inbuf[j - 30]) * -.0097201988f;
+ t += (inbuf[j - 1] + inbuf[j - 29]) * -.0105179986f;
+ t += (inbuf[j - 2] + inbuf[j - 28]) * -.0083479648f;
+ t += (inbuf[j - 3] + inbuf[j - 27]) * 5.860774e-4f;
+ t += (inbuf[j - 4] + inbuf[j - 26]) * .0130892089f;
+ t += (inbuf[j - 5] + inbuf[j - 25]) * .0217052232f;
+ t += (inbuf[j - 6] + inbuf[j - 24]) * .0184161253f;
+ t += (inbuf[j - 7] + inbuf[j - 23]) * 3.39723e-4f;
+ t += (inbuf[j - 8] + inbuf[j - 22]) * -.0260797087f;
+ t += (inbuf[j - 9] + inbuf[j - 21]) * -.0455563702f;
+ t += (inbuf[j - 10] + inbuf[j - 20]) * -.040306855f;
+ t += (inbuf[j - 11] + inbuf[j - 19]) * 5.029835e-4f;
+ t += (inbuf[j - 12] + inbuf[j - 18]) * .0729262903f;
+ t += (inbuf[j - 13] + inbuf[j - 17]) * .1572008878f;
+ t += (inbuf[j - 14] + inbuf[j - 16]) * .2247288674f;
+ t += inbuf[j - 15] * .250535965f;
+ lpbuf[j] = t;
+ }
+ return 0;
+} /* lpfilt_ */
+
diff --git a/trunk/codecs/lpc10/median.c b/trunk/codecs/lpc10/median.c
new file mode 100644
index 000000000..383c46e89
--- /dev/null
+++ b/trunk/codecs/lpc10/median.c
@@ -0,0 +1,89 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:31:31 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern integer median_(integer *d1, integer *d2, integer *d3);
+#endif
+
+/* ********************************************************************* */
+
+/* MEDIAN Version 45G */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:31 jaf
+ * Initial revision
+ * */
+/* Revision 1.2 1996/03/14 22:30:22 jaf */
+/* Just rearranged the comments and local variable declarations a bit. */
+
+/* Revision 1.1 1996/02/07 14:47:53 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Find median of three values */
+
+/* Input: */
+/* D1,D2,D3 - Three input values */
+/* Output: */
+/* MEDIAN - Median value */
+
+integer median_(integer *d1, integer *d2, integer *d3)
+{
+ /* System generated locals */
+ integer ret_val;
+
+/* Arguments */
+ ret_val = *d2;
+ if (*d2 > *d1 && *d2 > *d3) {
+ ret_val = *d1;
+ if (*d3 > *d1) {
+ ret_val = *d3;
+ }
+ } else if (*d2 < *d1 && *d2 < *d3) {
+ ret_val = *d1;
+ if (*d3 < *d1) {
+ ret_val = *d3;
+ }
+ }
+ return ret_val;
+} /* median_ */
+
diff --git a/trunk/codecs/lpc10/mload.c b/trunk/codecs/lpc10/mload.c
new file mode 100644
index 000000000..1cdb0647c
--- /dev/null
+++ b/trunk/codecs/lpc10/mload.c
@@ -0,0 +1,163 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:31:25 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int mload_(integer *order, integer *awins, integer *awinf, real *speech, real *phi, real *psi);
+#endif
+
+/* ***************************************************************** */
+
+/* MLOAD Version 48 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:25 jaf
+ * Initial revision
+ * */
+/* Revision 1.5 1996/03/27 23:59:51 jaf */
+/* Added some more accurate comments about which indices of the argument */
+/* array SPEECH are read. I thought that this might be the cause of a */
+/* problem I've been having, but it isn't. */
+
+/* Revision 1.4 1996/03/26 19:16:53 jaf */
+/* Commented out the code at the end that copied the lower triangular */
+/* half of PHI into the upper triangular half (making the resulting */
+/* matrix symmetric). The upper triangular half was never used by later */
+/* code in subroutine ANALYS. */
+
+/* Revision 1.3 1996/03/18 21:16:00 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 16:47:41 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:48:01 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Load a covariance matrix. */
+
+/* Input: */
+/* ORDER - Analysis order */
+/* AWINS - Analysis window start */
+/* AWINF - Analysis window finish */
+/* SPEECH(AWINF) - Speech buffer */
+/* Indices MIN(AWINS, AWINF-(ORDER-1)) through */
+/* MAX(AWINF, AWINS+(ORDER-1)) read. */
+/* As long as (AWINF-AWINS) .GE. (ORDER-1), */
+/* this is just indices AWINS through AWINF. */
+/* Output: */
+/* PHI(ORDER,ORDER) - Covariance matrix */
+/* Lower triangular half and diagonal written, and read.*/
+/* Upper triangular half untouched. */
+/* PSI(ORDER) - Prediction vector */
+/* Indices 1 through ORDER written, */
+/* and most are read after that. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int mload_(integer *order, integer *awins, integer *awinf,
+ real *speech, real *phi, real *psi)
+{
+ /* System generated locals */
+ integer phi_dim1, phi_offset, i__1, i__2;
+
+ /* Local variables */
+ integer c__, i__, r__, start;
+
+/* Arguments */
+/* Local variables that need not be saved */
+/* Load first column of triangular covariance matrix PHI */
+ /* Parameter adjustments */
+ --psi;
+ phi_dim1 = *order;
+ phi_offset = phi_dim1 + 1;
+ phi -= phi_offset;
+ --speech;
+
+ /* Function Body */
+ start = *awins + *order;
+ i__1 = *order;
+ for (r__ = 1; r__ <= i__1; ++r__) {
+ phi[r__ + phi_dim1] = 0.f;
+ i__2 = *awinf;
+ for (i__ = start; i__ <= i__2; ++i__) {
+ phi[r__ + phi_dim1] += speech[i__ - 1] * speech[i__ - r__];
+ }
+ }
+/* Load last element of vector PSI */
+ psi[*order] = 0.f;
+ i__1 = *awinf;
+ for (i__ = start; i__ <= i__1; ++i__) {
+ psi[*order] += speech[i__] * speech[i__ - *order];
+ }
+/* End correct to get additional columns of PHI */
+ i__1 = *order;
+ for (r__ = 2; r__ <= i__1; ++r__) {
+ i__2 = r__;
+ for (c__ = 2; c__ <= i__2; ++c__) {
+ phi[r__ + c__ * phi_dim1] = phi[r__ - 1 + (c__ - 1) * phi_dim1] -
+ speech[*awinf + 1 - r__] * speech[*awinf + 1 - c__] +
+ speech[start - r__] * speech[start - c__];
+ }
+ }
+/* End correct to get additional elements of PSI */
+ i__1 = *order - 1;
+ for (c__ = 1; c__ <= i__1; ++c__) {
+ psi[c__] = phi[c__ + 1 + phi_dim1] - speech[start - 1] * speech[start
+ - 1 - c__] + speech[*awinf] * speech[*awinf - c__];
+ }
+/* Copy lower triangular section into upper (why bother?) */
+/* I'm commenting this out, since the upper triangular half of PHI
+*/
+/* is never used by later code, unless a sufficiently high level of
+*/
+/* tracing is turned on. */
+/* DO R = 1,ORDER */
+/* DO C = 1,R-1 */
+/* PHI(C,R) = PHI(R,C) */
+/* END DO */
+/* END DO */
+ return 0;
+} /* mload_ */
+
diff --git a/trunk/codecs/lpc10/onset.c b/trunk/codecs/lpc10/onset.c
new file mode 100644
index 000000000..ddca3b477
--- /dev/null
+++ b/trunk/codecs/lpc10/onset.c
@@ -0,0 +1,324 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:37:55 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:31:18 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int onset_(real *pebuf, integer *osbuf, integer *osptr, integer *oslen, integer *sbufl, integer *sbufh, integer *lframe, struct lpc10_encoder_state *st);
+#endif
+
+/* Table of constant values */
+
+static real c_b2 = 1.f;
+
+/* ****************************************************************** */
+
+/* ONSET Version 49 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:37:55 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:31:18 jaf
+ * Initial revision
+ * */
+/* Revision 1.5 1996/03/15 16:41:01 jaf */
+/* Just rearranged INITONSET assignment orders to be consistent with */
+/* order of DATA statements in ONSET. */
+
+/* Revision 1.4 1996/03/15 15:48:27 jaf */
+/* Changed some comments, and only reordered the DATA statements (their */
+/* meaning wasn't changed). */
+
+/* Revision 1.3 1996/03/14 23:53:06 jaf */
+/* Added an entry INITONSET that reinitializes the local state variables */
+/* of subroutine ONSET. */
+
+/* Rearranged quite a few comments, adding more explaining which */
+/* arguments were inputs, and how the modified ones can be changed. */
+
+/* Revision 1.2 1996/03/12 23:53:00 jaf */
+/* Lots of comments added about the local state of this subroutine that */
+/* must be saved from one invocation to the next. */
+
+/* One constant 180 replaced with LFRAME, which should be "more general", */
+/* even though it would probably require many more changes than this to */
+/* get this coder to work for other frame sizes. */
+
+/* Revision 1.1 1996/02/07 14:48:09 jaf */
+/* Initial revision */
+
+
+/* ****************************************************************** */
+
+/* Floating point version */
+
+
+/* Detection of onsets in (or slightly preceding) the futuremost frame */
+/* of speech. */
+
+
+/* Input: */
+/* PEBUF(SBUFL:SBUFH) - Preemphasized speech */
+/* Indices SBUFH-LFRAME through SBUFH are read. */
+/* OSLEN - Maximum number of onsets that can be stored in OSBUF. */
+/* SBUFL, SBUFH - Range of PEBUF */
+/* LFRAME - length of a frame, in samples */
+/* Input/Output: */
+/* OSBUF(OSLEN) - Buffer which holds sorted indexes of onsets */
+/* Indices A through B are modified, where A */
+/* is the original value of OSPTR, and B is the final */
+/* value of OSPTR-1. B is at most OSLEN. */
+/* OSPTR - Free pointer into OSBUF */
+/* Initial value should be .LE. OSLEN+1. */
+/* If so, final value grows by one for each new onset */
+/* found, and final value will be .LE. OSLEN+1. */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this subroutine, or */
+/* reinitialize its state for any other reason, call the ENTRY INITONSET. */
+
+/* Subroutine */ int onset_(real *pebuf, integer *osbuf, integer *
+ osptr, integer *oslen, integer *sbufl, integer *sbufh, integer *
+ lframe, struct lpc10_encoder_state *st)
+{
+ /* Initialized data */
+
+ real *n;
+ real *d__;
+ real *l2buf;
+ real *l2sum1;
+ integer *l2ptr1;
+ integer *l2ptr2;
+ logical *hyst;
+
+ /* System generated locals */
+ integer pebuf_offset, i__1;
+ real r__1;
+
+ /* Builtin functions */
+ double r_sign(real *, real *);
+
+ /* Local variables */
+ integer i__;
+ integer *lasti;
+ real l2sum2;
+ real *fpc;
+
+/* Arguments */
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:37:55 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:31:18 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Parameters/constants */
+/* Parameters for onset detection algorithm: */
+/* L2 Threshold for filtered slope of FPC (function of L2WID!) */
+/* L2LAG Lag due to both filters which compute filtered slope of FPC */
+/* L2WID Width of the filter which computes the slope of FPC */
+/* OSHYST The number of samples of slope(FPC) which must be below */
+/* the threshold before a new onset may be declared. */
+/* Local variables that need not be saved */
+/* Local state */
+/* Variables */
+/* N, D Numerator and denominator of prediction filters */
+/* FPC Current prediction coefs */
+/* L2BUF, L2SUM1, L2SUM2 State of slope filter */
+/* The only "significant" change I've made is to change L2SUM2 out
+*/
+/* of the list of local variables that need to be saved, since it */
+/* didn't need to be. */
+/* L2SUM1 need not be, but avoiding saving it would require a small
+*/
+/* change to the body of the code. See comments below for an */
+/* example of how the code could be changed to avoid saving L2SUM1.
+*/
+/* FPC and LASTI are saved from one invocation to the next, but */
+/* they are not given initial values. This is acceptable, because
+*/
+/* FPC will be assigned a value the first time that this function */
+/* is called after D is initialized to 1, since the formula to */
+/* change D will not change it to 0 in one step, and the IF (D */
+/* .NE. 0) statement will execute its THEN part, initializing FPC.
+*/
+
+/* LASTI's value will not be used until HYST is .TRUE., and */
+/* whenever HYST is changed from its initial value of .FALSE., */
+/* LASTI is assigned a value. */
+/* In a C version of this coder, it would be nice if all of these */
+/* saved things, in this and all other subroutines, could be stored
+*/
+/* in a single struct lpc10_coder_state_t, initialized with a call
+*/
+/* to a function like lpc10_init(&lpc10_coder_state). In this way,
+*/
+/* a program that used these functions could conveniently alternate
+*/
+/* coding more than one distinct audio stream. */
+
+ n = &(st->n);
+ d__ = &(st->d__);
+ fpc = &(st->fpc);
+ l2buf = &(st->l2buf[0]);
+ l2sum1 = &(st->l2sum1);
+ l2ptr1 = &(st->l2ptr1);
+ l2ptr2 = &(st->l2ptr2);
+ lasti = &(st->lasti);
+ hyst = &(st->hyst);
+
+ /* Parameter adjustments */
+ if (osbuf) {
+ --osbuf;
+ }
+ if (pebuf) {
+ pebuf_offset = *sbufl;
+ pebuf -= pebuf_offset;
+ }
+
+ /* Function Body */
+
+/* The following line subtracted a hard-coded "180" from LASTI, */
+/* instead of using a variable like LFRAME or a constant like */
+/* MAXFRM. I changed it to LFRAME, for "generality". */
+ if (*hyst) {
+ *lasti -= *lframe;
+ }
+ i__1 = *sbufh;
+ for (i__ = *sbufh - *lframe + 1; i__ <= i__1; ++i__) {
+/* Compute FPC; Use old FPC on divide by zero; Clamp FPC to +/- 1.
+*/
+ *n = (pebuf[i__] * pebuf[i__ - 1] + (*n) * 63.f) / 64.f;
+/* Computing 2nd power */
+ r__1 = pebuf[i__ - 1];
+ *d__ = (r__1 * r__1 + (*d__) * 63.f) / 64.f;
+ if ((*d__) != 0.f) {
+ if (abs(*n) > (*d__)) {
+ *fpc = (real)r_sign(&c_b2, n);
+ } else {
+ *fpc = (*n) / (*d__);
+ }
+ }
+/* Filter FPC */
+/* In order to allow L2SUM1 not to be saved from one invocation
+of */
+/* this subroutine to the next, one could change the sequence of
+ */
+/* assignments below, up to the IF statement, to the following.
+ In */
+/* addition, the initial value of L2PTR2 should be changed to */
+/* L2WID/2 instead of L2WID/2+1. */
+
+/* L2SUM1 = L2BUF(L2PTR2) */
+/* L2PTR2 = MOD(L2PTR2,L2WID)+1 */
+/* L2SUM1 = L2SUM1 - L2BUF(L2PTR2) + FPC */
+/* L2BUF(L2PTR2) = L2SUM1 */
+
+/* * The following lines didn't change from the original: */
+/* L2SUM2 = L2BUF(L2PTR1) */
+/* L2BUF(L2PTR1) = FPC */
+/* L2PTR1 = MOD(L2PTR1,L2WID)+1 */
+
+ l2sum2 = l2buf[*l2ptr1 - 1];
+ *l2sum1 = *l2sum1 - l2buf[*l2ptr2 - 1] + *fpc;
+ l2buf[*l2ptr2 - 1] = *l2sum1;
+ l2buf[*l2ptr1 - 1] = *fpc;
+ *l2ptr1 = *l2ptr1 % 16 + 1;
+ *l2ptr2 = *l2ptr2 % 16 + 1;
+ if ((r__1 = *l2sum1 - l2sum2, abs(r__1)) > 1.7f) {
+ if (! (*hyst)) {
+/* Ignore if buffer full */
+ if (*osptr <= *oslen) {
+ osbuf[*osptr] = i__ - 9;
+ ++(*osptr);
+ }
+ *hyst = TRUE_;
+ }
+ *lasti = i__;
+/* After one onset detection, at least OSHYST sample times m
+ust go */
+/* by before another is allowed to occur. */
+ } else if ((*hyst) && i__ - *lasti >= 10) {
+ *hyst = FALSE_;
+ }
+ }
+ return 0;
+} /* onset_ */
diff --git a/trunk/codecs/lpc10/pitsyn.c b/trunk/codecs/lpc10/pitsyn.c
new file mode 100644
index 000000000..ea8177e22
--- /dev/null
+++ b/trunk/codecs/lpc10/pitsyn.c
@@ -0,0 +1,583 @@
+/*
+
+$Log$
+Revision 1.16 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.15 2003/11/23 22:14:32 markster
+Various warning cleanups
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:40:12 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:31:12 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int pitsyn_(integer *order, integer *voice, integer *pitch, real *rms, real *rc, integer *lframe, integer *ivuv, integer *ipiti, real *rmsi, real *rci, integer *nout, real *ratio, struct lpc10_decoder_state *st);
+#endif
+
+/* ***************************************************************** */
+
+/* PITSYN Version 53 */
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/11/23 22:14:32 markster
+ * Various warning cleanups
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:40:12 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:31:12 jaf
+ * Initial revision
+ * */
+/* Revision 1.2 1996/03/25 18:49:07 jaf */
+/* Added commments about which indices of array arguments are read or */
+/* written. */
+
+/* Rearranged local variable declarations to indicate which need to be */
+/* saved from one invocation to the next. Added entry INITPITSYN to */
+/* reinitialize local state variables, if desired. */
+
+/* Added lots of comments about proving that the maximum number of pitch */
+/* periods (NOUT) that can be returned is 16. The call to STOP that */
+/* could happen if NOUT got too large was removed as a result. */
+
+/* Also proved that the total number of samples returned from N calls, */
+/* each with identical values of LFRAME, will always be in the range */
+/* N*LFRAME-MAXPIT+1 to N*LFRAME. */
+
+/* Revision 1.1 1996/02/07 14:48:18 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* Synthesize a single pitch epoch */
+
+/* Input: */
+/* ORDER - Synthesis order (number of RC's) */
+/* VOICE - Half frame voicing decisions */
+/* Indices 1 through 2 read. */
+/* LFRAME - Length of speech buffer */
+/* Input/Output: */
+/* PITCH - Pitch */
+/* This value should be in the range MINPIT (20) to MAXPIT */
+/* (156), inclusive. */
+/* PITCH can be modified under some conditions. */
+/* RMS - Energy (can be modified) */
+/* RMS is changed to 1 if the value passed in is less than 1. */
+/* RC - Reflection coefficients */
+/* Indices 1 through ORDER can be temporarily overwritten with */
+/* RCO, and then replaced with original values, under some */
+/* conditions. */
+/* Output: */
+/* IVUV - Pitch epoch voicing decisions */
+/* Indices (I) of IVUV, IPITI, and RMSI are written, */
+/* and indices (J,I) of RCI are written, */
+/* where I ranges from 1 to NOUT, and J ranges from 1 to ORDER. */
+/* IPITI - Pitch epoch length */
+/* RMSI - Pitch epoch energy */
+/* RCI - Pitch epoch RC's */
+/* NOUT - Number of pitch periods in this frame */
+/* This is at least 0, at least 1 if MAXPIT .LT. LFRAME (this */
+/* is currently true on every call), and can never be more than */
+/* (LFRAME+MAXPIT-1)/PITCH, which is currently 16 with */
+/* LFRAME=180, MAXPIT=156, and PITCH .GE. 20, as SYNTHS */
+/* guarantees when it calls this subroutine. */
+/* RATIO - Previous to present energy ratio */
+/* Always assigned a value. */
+
+/* Subroutine */ int pitsyn_(integer *order, integer *voice,
+ integer *pitch, real *rms, real *rc, integer *lframe, integer *ivuv,
+ integer *ipiti, real *rmsi, real *rci, integer *nout, real *ratio,
+ struct lpc10_decoder_state *st)
+{
+ /* Initialized data */
+
+ real *rmso;
+ logical *first;
+
+ /* System generated locals */
+ integer rci_dim1 = 0, rci_offset, i__1, i__2;
+ real r__1;
+
+ /* Builtin functions */
+ double log(doublereal), exp(doublereal);
+
+ /* Local variables */
+ real alrn, alro, yarc[10], prop;
+ integer i__, j, vflag, jused, lsamp;
+ integer *jsamp;
+ real slope;
+ integer *ipito;
+ real uvpit;
+ integer ip, nl, ivoice;
+ integer *ivoico;
+ integer istart;
+ real *rco;
+ real xxy;
+
+/* Arguments */
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/11/23 22:14:32 markster
+ * Various warning cleanups
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:40:12 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:31:12 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Local variables that need not be saved */
+/* LSAMP is initialized in the IF (FIRST) THEN clause, but it is */
+/* not used the first time through, and it is given a value before
+*/
+/* use whenever FIRST is .FALSE., so it appears unnecessary to */
+/* assign it a value when FIRST is .TRUE. */
+/* Local state */
+/* FIRST - .TRUE. only on first call to PITSYN. */
+/* IVOICO - Previous VOICE(2) value. */
+/* IPITO - Previous PITCH value. */
+/* RMSO - Previous RMS value. */
+/* RCO - Previous RC values. */
+
+/* JSAMP - If this routine is called N times with identical values of */
+/* LFRAME, then the total length of all pitch periods returned */
+/* is always N*LFRAME-JSAMP, and JSAMP is always in the range 0
+*/
+/* to MAXPIT-1 (see below for why this is so). Thus JSAMP is */
+/* the number of samples "left over" from the previous call to */
+/* PITSYN, that haven't been "used" in a pitch period returned */
+/* from this subroutine. Every time this subroutine is called,
+*/
+/* it returns pitch periods with a total length of at most */
+/* LFRAME+JSAMP. */
+
+/* IVOICO, IPITO, RCO, and JSAMP need not be assigned an initial value */
+/* with a DATA statement, because they are always initialized on the */
+/* first call to PITSYN. */
+
+/* FIRST and RMSO should be initialized with DATA statements, because */
+/* even on the first call, they are used before being initialized. */
+ /* Parameter adjustments */
+ if (rc) {
+ --rc;
+ }
+ if (rci) {
+ rci_dim1 = *order;
+ rci_offset = rci_dim1 + 1;
+ rci -= rci_offset;
+ }
+ if (voice) {
+ --voice;
+ }
+ if (ivuv) {
+ --ivuv;
+ }
+ if (ipiti) {
+ --ipiti;
+ }
+ if (rmsi) {
+ --rmsi;
+ }
+
+ /* Function Body */
+ ivoico = &(st->ivoico);
+ ipito = &(st->ipito);
+ rmso = &(st->rmso);
+ rco = &(st->rco[0]);
+ jsamp = &(st->jsamp);
+ first = &(st->first_pitsyn);
+
+ if (*rms < 1.f) {
+ *rms = 1.f;
+ }
+ if (*rmso < 1.f) {
+ *rmso = 1.f;
+ }
+ uvpit = 0.f;
+ *ratio = *rms / (*rmso + 8.f);
+ if (*first) {
+ lsamp = 0;
+ ivoice = voice[2];
+ if (ivoice == 0) {
+ *pitch = *lframe / 4;
+ }
+ *nout = *lframe / *pitch;
+ *jsamp = *lframe - *nout * *pitch;
+
+/* SYNTHS only calls this subroutine with PITCH in the range
+20 */
+/* to 156. LFRAME = MAXFRM = 180, so NOUT is somewhere in th
+e */
+/* range 1 to 9. */
+
+/* JSAMP is "LFRAME mod PITCH", so it is in the range 0 to */
+/* (PITCH-1), or 0 to MAXPIT-1=155, after the first call. */
+
+ i__1 = *nout;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ i__2 = *order;
+ for (j = 1; j <= i__2; ++j) {
+ rci[j + i__ * rci_dim1] = rc[j];
+ }
+ ivuv[i__] = ivoice;
+ ipiti[i__] = *pitch;
+ rmsi[i__] = *rms;
+ }
+ *first = FALSE_;
+ } else {
+ vflag = 0;
+ lsamp = *lframe + *jsamp;
+ slope = (*pitch - *ipito) / (real) lsamp;
+ *nout = 0;
+ jused = 0;
+ istart = 1;
+ if (voice[1] == *ivoico && voice[2] == voice[1]) {
+ if (voice[2] == 0) {
+/* SSUV - - 0 , 0 , 0 */
+ *pitch = *lframe / 4;
+ *ipito = *pitch;
+ if (*ratio > 8.f) {
+ *rmso = *rms;
+ }
+ }
+/* SSVC - - 1 , 1 , 1 */
+ slope = (*pitch - *ipito) / (real) lsamp;
+ ivoice = voice[2];
+ } else {
+ if (*ivoico != 1) {
+ if (*ivoico == voice[1]) {
+/* UV2VC2 - - 0 , 0 , 1 */
+ nl = lsamp - *lframe / 4;
+ } else {
+/* UV2VC1 - - 0 , 1 , 1 */
+ nl = lsamp - *lframe * 3 / 4;
+ }
+ ipiti[1] = nl / 2;
+ ipiti[2] = nl - ipiti[1];
+ ivuv[1] = 0;
+ ivuv[2] = 0;
+ rmsi[1] = *rmso;
+ rmsi[2] = *rmso;
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ rci[i__ + rci_dim1] = rco[i__ - 1];
+ rci[i__ + (rci_dim1 << 1)] = rco[i__ - 1];
+ rco[i__ - 1] = rc[i__];
+ }
+ slope = 0.f;
+ *nout = 2;
+ *ipito = *pitch;
+ jused = nl;
+ istart = nl + 1;
+ ivoice = 1;
+ } else {
+ if (*ivoico != voice[1]) {
+/* VC2UV1 - - 1 , 0 , 0 */
+ lsamp = *lframe / 4 + *jsamp;
+ } else {
+/* VC2UV2 - - 1 , 1 , 0 */
+ lsamp = *lframe * 3 / 4 + *jsamp;
+ }
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ yarc[i__ - 1] = rc[i__];
+ rc[i__] = rco[i__ - 1];
+ }
+ ivoice = 1;
+ slope = 0.f;
+ vflag = 1;
+ }
+ }
+/* Here is the value of most variables that are used below, depending
+on */
+/* the values of IVOICO, VOICE(1), and VOICE(2). VOICE(1) and VOICE(2
+) */
+/* are input arguments, and IVOICO is the value of VOICE(2) on the */
+/* previous call (see notes for the IF (NOUT .NE. 0) statement near th
+e */
+/* end). Each of these three values is either 0 or 1. These three */
+/* values below are given as 3-bit long strings, in the order IVOICO,
+*/
+/* VOICE(1), and VOICE(2). It appears that the code above assumes tha
+t */
+/* the bit sequences 010 and 101 never occur, but I wonder whether a
+*/
+/* large enough number of bit errors in the channel could cause such a
+ */
+/* thing to happen, and if so, could that cause NOUT to ever go over 1
+1? */
+
+/* Note that all of the 180 values in the table are really LFRAME, but
+ */
+/* 180 has fewer characters, and it makes the table a little more */
+/* concrete. If LFRAME is ever changed, keep this in mind. Similarly
+, */
+/* 135's are 3*LFRAME/4, and 45's are LFRAME/4. If LFRAME is not a */
+/* multiple of 4, then the 135 for NL-JSAMP is actually LFRAME-LFRAME/
+4, */
+/* and the 45 for NL-JSAMP is actually LFRAME-3*LFRAME/4. */
+
+/* Note that LSAMP-JSAMP is given as the variable. This was just for
+*/
+/* brevity, to avoid adding "+JSAMP" to all of the column entries. */
+/* Similarly for NL-JSAMP. */
+
+/* Variable | 000 001 011,010 111 110 100,101 */
+/* ------------+-------------------------------------------------- */
+/* ISTART | 1 NL+1 NL+1 1 1 1 */
+/* LSAMP-JSAMP | 180 180 180 180 135 45 */
+/* IPITO | 45 PITCH PITCH oldPITCH oldPITCH oldPITCH */
+/* SLOPE | 0 0 0 seebelow 0 0 */
+/* JUSED | 0 NL NL 0 0 0 */
+/* PITCH | 45 PITCH PITCH PITCH PITCH PITCH */
+/* NL-JSAMP | -- 135 45 -- -- -- */
+/* VFLAG | 0 0 0 0 1 1 */
+/* NOUT | 0 2 2 0 0 0 */
+/* IVOICE | 0 1 1 1 1 1 */
+
+/* while_loop | once once once once twice twice */
+
+/* ISTART | -- -- -- -- JUSED+1 JUSED+1 */
+/* LSAMP-JSAMP | -- -- -- -- 180 180 */
+/* IPITO | -- -- -- -- oldPITCH oldPITCH */
+/* SLOPE | -- -- -- -- 0 0 */
+/* JUSED | -- -- -- -- ?? ?? */
+/* PITCH | -- -- -- -- PITCH PITCH */
+/* NL-JSAMP | -- -- -- -- -- -- */
+/* VFLAG | -- -- -- -- 0 0 */
+/* NOUT | -- -- -- -- ?? ?? */
+/* IVOICE | -- -- -- -- 0 0 */
+
+
+/* UVPIT is always 0.0 on the first pass through the DO WHILE (.TRUE.)
+ */
+/* loop below. */
+
+/* The only possible non-0 value of SLOPE (in column 111) is */
+/* (PITCH-IPITO)/FLOAT(LSAMP) */
+
+/* Column 101 is identical to 100. Any good properties we can prove
+*/
+/* for 100 will also hold for 101. Similarly for 010 and 011. */
+
+/* SYNTHS calls this subroutine with PITCH restricted to the range 20
+to */
+/* 156. IPITO is similarly restricted to this range, after the first
+*/
+/* call. IP below is also restricted to this range, given the */
+/* definitions of IPITO, SLOPE, UVPIT, and that I is in the range ISTA
+RT */
+/* to LSAMP. */
+
+ while(TRUE_) {
+
+/* JUSED is the total length of all pitch periods curr
+ently */
+/* in the output arrays, in samples. */
+
+/* An invariant of the DO I = ISTART,LSAMP loop below,
+ under */
+/* the condition that IP is always in the range 1 thro
+ugh */
+/* MAXPIT, is: */
+
+/* (I - MAXPIT) .LE. JUSED .LE. (I-1) */
+
+/* Note that the final value of I is LSAMP+1, so that
+after */
+/* the DO loop is complete, we know: */
+
+/* (LSAMP - MAXPIT + 1) .LE. JUSED .LE. LSAMP */
+
+ i__1 = lsamp;
+ for (i__ = istart; i__ <= i__1; ++i__) {
+ r__1 = *ipito + slope * i__;
+ ip = (integer)(r__1 + .5f);
+ if (uvpit != 0.f) {
+ ip = (integer)uvpit;
+ }
+ if (ip <= i__ - jused) {
+ ++(*nout);
+
+/* The following check is no longer nece
+ssary, now that */
+/* we can prove that NOUT will never go
+over 16. */
+
+/* IF (NOUT .GT. 16) STOP 'PITSYN: too many epochs'
+*/
+
+ ipiti[*nout] = ip;
+ *pitch = ip;
+ ivuv[*nout] = ivoice;
+ jused += ip;
+ prop = (jused - ip / 2) / (real) lsamp;
+ i__2 = *order;
+ for (j = 1; j <= i__2; ++j) {
+ alro = (real)log((rco[j - 1] + 1) / (1 - rco[j - 1]));
+ alrn = (real)log((rc[j] + 1) / (1 - rc[j]));
+ xxy = alro + prop * (alrn - alro);
+ xxy = (real)exp(xxy);
+ rci[j + *nout * rci_dim1] = (xxy - 1) / (xxy + 1);
+ }
+ rmsi[*nout] = (real)(log(*rmso) + prop * (log(*rms) - log(*rmso)));
+ rmsi[*nout] = (real)exp(rmsi[*nout]);
+ }
+ }
+ if (vflag != 1) {
+ goto L100;
+ }
+
+/* I want to prove what range UVPIT must lie in after
+the */
+/* assignments to it below. To do this, I must determ
+ine */
+/* what range (LSAMP-ISTART) must lie in, after the */
+/* assignments to ISTART and LSAMP below. */
+
+/* Let oldLSAMP be the value of LSAMP at this point in
+ the */
+/* execution. This is 135+JSAMP in state 110, or 45+J
+SAMP in */
+/* states 100 or 101. */
+
+/* Given the loop invariant on JUSED above, we know th
+at: */
+
+/* (oldLSAMP - MAXPIT + 1) .LE. JUSED .LE. oldLSAMP */
+
+/* ISTART is one more than this. */
+
+/* Let newLSAMP be the value assigned to LSAMP below.
+ This */
+/* is 180+JSAMP. Thus (newLSAMP-oldLSAMP) is either 4
+5 or */
+/* 135, depending on the state. */
+
+/* Thus, the range of newLSAMP-ISTART is: */
+
+/* (newLSAMP-(oldLSAMP+1)) .LE. newLSAMP-ISTART */
+/* .LE. (newLSAMP-(oldLSAMP - MAXPIT + 2)) */
+
+/* or: */
+
+/* 46 .LE. newLSAMP-ISTART .LE. 133+MAXPIT .EQ. 289 */
+
+/* Therefore, UVPIT is in the range 23 to 144 after th
+e first */
+/* assignment to UVPIT below, and after the conditiona
+l */
+/* assignment, it is in the range 23 to 90. */
+
+/* The important thing is that it is in the range 20 t
+o 156, */
+/* so that in the loop above, IP is always in this ran
+ge. */
+
+ vflag = 0;
+ istart = jused + 1;
+ lsamp = *lframe + *jsamp;
+ slope = 0.f;
+ ivoice = 0;
+ uvpit = (real) ((lsamp - istart) / 2);
+ if (uvpit > 90.f) {
+ uvpit /= 2;
+ }
+ *rmso = *rms;
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ rc[i__] = yarc[i__ - 1];
+ rco[i__ - 1] = yarc[i__ - 1];
+ }
+ }
+L100:
+ *jsamp = lsamp - jused;
+ }
+/* Given that the maximum pitch period MAXPIT .LT. LFRAME (this is
+*/
+/* currently true on every call, since SYNTHS always sets */
+/* LFRAME=180), NOUT will always be .GE. 1 at this point. */
+ if (*nout != 0) {
+ *ivoico = voice[2];
+ *ipito = *pitch;
+ *rmso = *rms;
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ rco[i__ - 1] = rc[i__];
+ }
+ }
+ return 0;
+} /* pitsyn_ */
diff --git a/trunk/codecs/lpc10/placea.c b/trunk/codecs/lpc10/placea.c
new file mode 100644
index 000000000..dacb50e7a
--- /dev/null
+++ b/trunk/codecs/lpc10/placea.c
@@ -0,0 +1,242 @@
+/*
+
+$Log$
+Revision 1.16 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.15 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.2 2003/09/19 01:20:22 markster
+Code cleanups (bug #66)
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.3 2001/04/12 21:27:53 markh
+app_record now supports wildcards of sort so your output file is not overwritten every time it's run. File.h got a documentation update on the ast_fileexists to include the return call. Watch out for the placea.c placev.c code, it's updates have not been tested yet. Just a few parenthesis to make it compile nicer on newer gcc versions with all the -W flags set.
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:31:07 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int placea_(integer *ipitch, integer *voibuf, integer *obound, integer *af, integer *vwin, integer *awin, integer *ewin, integer *lframe, integer *maxwin);
+#endif
+
+/* *********************************************************************** */
+
+/* PLACEA Version 48 */
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.2 2003/09/19 01:20:22 markster
+ * Code cleanups (bug #66)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.3 2001/04/12 21:27:53 markh
+ * app_record now supports wildcards of sort so your output file is not overwritten every time it's run. File.h got a documentation update on the ast_fileexists to include the return call. Watch out for the placea.c placev.c code, it's updates have not been tested yet. Just a few parenthesis to make it compile nicer on newer gcc versions with all the -W flags set.
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:07 jaf
+ * Initial revision
+ * */
+/* Revision 1.5 1996/03/19 20:41:55 jaf */
+/* Added some conditions satisfied by the output values in EWIN. */
+
+/* Revision 1.4 1996/03/19 20:24:17 jaf */
+/* Added some conditions satisfied by the output values in AWIN. */
+
+/* Revision 1.3 1996/03/18 21:40:04 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 16:43:09 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:48:31 jaf */
+/* Initial revision */
+
+
+/* *********************************************************************** */
+/* Input: */
+/* IPITCH */
+/* VOIBUF */
+/* Indices (2,AF-2), (1,AF-1), (2,AF-1), (1,AF), and (2,AF) read.*/
+/* All other indices untouched. */
+/* OBOUND */
+/* AF */
+/* VWIN */
+/* Indices (1,AF) and (2,AF) read. */
+/* All other indices untouched. */
+/* LFRAME */
+/* MAXWIN */
+/* Input/Output: */
+/* AWIN */
+/* Index (1,AF-1) read. */
+/* Indices (1,AF) and (2,AF) written, and then read. */
+/* All other indices untouched. */
+/* In all cases (except possibly one), the final values will */
+/* satisfy the condition: AWIN(2,AF)-AWIN(1,AF)+1 = MAXWIN. */
+/* In that other case, */
+/* AWIN(1,AF)=VWIN(1,AF) and AWIN(2,AF)=VWIN(2,AF). */
+/* Output: */
+/* EWIN */
+/* Indices (1,AF) and (2,AF) written. */
+/* All other indices untouched. */
+/* In all cases, the final values will satisfy the condition: */
+/* AWIN(1,AF) .LE. EWIN(1,AF) .LE. EWIN(2,AF) .LE. AWIN(2,AF) */
+/* In other words, the energy window is a sub-window of */
+/* the analysis window. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int placea_(integer *ipitch, integer *voibuf, integer *
+ obound, integer *af, integer *vwin, integer *awin, integer *ewin,
+ integer *lframe, integer *maxwin)
+{
+ /* System generated locals */
+ real r__1;
+
+ /* Builtin functions */
+ integer i_nint(real *);
+
+ /* Local variables */
+ logical allv, winv;
+ integer i__, j, k, l, hrange;
+ logical ephase;
+ integer lrange;
+
+/* Arguments */
+/* Local variables that need not be saved */
+ /* Parameter adjustments */
+ ewin -= 3;
+ awin -= 3;
+ vwin -= 3;
+ --voibuf;
+
+ /* Function Body */
+ lrange = (*af - 2) * *lframe + 1;
+ hrange = *af * *lframe;
+/* Place the Analysis window based on the voicing window */
+/* placement, onsets, tentative voicing decision, and pitch. */
+
+/* Case 1: Sustained Voiced Speech */
+/* If the five most recent voicing decisions are */
+/* voiced, then the window is placed phase-synchronously with the */
+/* previous window, as close to the present voicing window if possible.
+*/
+/* If onsets bound the voicing window, then preference is given to */
+/* a phase-synchronous placement which does not overlap these onsets. */
+
+/* Case 2: Voiced Transition */
+/* If at least one voicing decision in AF is voicied, and there are no
+*/
+/* onsets, then the window is placed as in case 1. */
+
+/* Case 3: Unvoiced Speech or Onsets */
+/* If both voicing decisions in AF are unvoiced, or there are onsets, */
+/* then the window is placed coincident with the voicing window. */
+
+/* Note: During phase-synchronous placement of windows, the length */
+/* is not altered from MAXWIN, since this would defeat the purpose */
+/* of phase-synchronous placement. */
+/* Check for case 1 and case 2 */
+ allv = voibuf[((*af - 2) << 1) + 2] == 1;
+ allv = allv && voibuf[((*af - 1) << 1) + 1] == 1;
+ allv = allv && voibuf[((*af - 1) << 1) + 2] == 1;
+ allv = allv && voibuf[(*af << 1) + 1] == 1;
+ allv = allv && voibuf[(*af << 1) + 2] == 1;
+ winv = voibuf[(*af << 1) + 1] == 1 || voibuf[(*af << 1) + 2] == 1;
+ if (allv || (winv && *obound == 0)) {
+/* APHASE: Phase synchronous window placement. */
+/* Get minimum lower index of the window. */
+ i__ = (lrange + *ipitch - 1 - awin[((*af - 1) << 1) + 1]) / *ipitch;
+ i__ *= *ipitch;
+ i__ += awin[((*af - 1) << 1) + 1];
+/* L = the actual length of this frame's analysis window. */
+ l = *maxwin;
+/* Calculate the location where a perfectly centered window would star
+t. */
+ k = (vwin[(*af << 1) + 1] + vwin[(*af << 1) + 2] + 1 - l) / 2;
+/* Choose the actual location to be the pitch multiple closest to this
+. */
+ r__1 = (real) (k - i__) / *ipitch;
+ awin[(*af << 1) + 1] = i__ + i_nint(&r__1) * *ipitch;
+ awin[(*af << 1) + 2] = awin[(*af << 1) + 1] + l - 1;
+/* If there is an onset bounding the right of the voicing window and t
+he */
+/* analysis window overlaps that, then move the analysis window backwa
+rd */
+/* to avoid this onset. */
+ if (*obound >= 2 && awin[(*af << 1) + 2] > vwin[(*af << 1) + 2]) {
+ awin[(*af << 1) + 1] -= *ipitch;
+ awin[(*af << 1) + 2] -= *ipitch;
+ }
+/* Similarly for the left of the voicing window. */
+ if ((*obound == 1 || *obound == 3) && awin[(*af << 1) + 1] < vwin[(*
+ af << 1) + 1]) {
+ awin[(*af << 1) + 1] += *ipitch;
+ awin[(*af << 1) + 2] += *ipitch;
+ }
+/* If this placement puts the analysis window above HRANGE, then */
+/* move it backward an integer number of pitch periods. */
+ while(awin[(*af << 1) + 2] > hrange) {
+ awin[(*af << 1) + 1] -= *ipitch;
+ awin[(*af << 1) + 2] -= *ipitch;
+ }
+/* Similarly if the placement puts the analysis window below LRANGE.
+*/
+ while(awin[(*af << 1) + 1] < lrange) {
+ awin[(*af << 1) + 1] += *ipitch;
+ awin[(*af << 1) + 2] += *ipitch;
+ }
+/* Make Energy window be phase-synchronous. */
+ ephase = TRUE_;
+/* Case 3 */
+ } else {
+ awin[(*af << 1) + 1] = vwin[(*af << 1) + 1];
+ awin[(*af << 1) + 2] = vwin[(*af << 1) + 2];
+ ephase = FALSE_;
+ }
+/* RMS is computed over an integer number of pitch periods in the analysis
+ */
+/*window. When it is not placed phase-synchronously, it is placed as clos
+e*/
+/* as possible to onsets. */
+ j = (awin[(*af << 1) + 2] - awin[(*af << 1) + 1] + 1) / *ipitch * *ipitch;
+ if (j == 0 || ! winv) {
+ ewin[(*af << 1) + 1] = vwin[(*af << 1) + 1];
+ ewin[(*af << 1) + 2] = vwin[(*af << 1) + 2];
+ } else if (! ephase && *obound == 2) {
+ ewin[(*af << 1) + 1] = awin[(*af << 1) + 2] - j + 1;
+ ewin[(*af << 1) + 2] = awin[(*af << 1) + 2];
+ } else {
+ ewin[(*af << 1) + 1] = awin[(*af << 1) + 1];
+ ewin[(*af << 1) + 2] = awin[(*af << 1) + 1] + j - 1;
+ }
+ return 0;
+} /* placea_ */
+
diff --git a/trunk/codecs/lpc10/placev.c b/trunk/codecs/lpc10/placev.c
new file mode 100644
index 000000000..56e72c4b8
--- /dev/null
+++ b/trunk/codecs/lpc10/placev.c
@@ -0,0 +1,275 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.3 2001/04/12 21:27:53 markh
+app_record now supports wildcards of sort so your output file is not overwritten every time it's run. File.h got a documentation update on the ast_fileexists to include the return call. Watch out for the placea.c placev.c code, it's updates have not been tested yet. Just a few parenthesis to make it compile nicer on newer gcc versions with all the -W flags set.
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:31:02 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int placev_(integer *osbuf, integer *osptr, integer *oslen, integer *obound, integer *vwin, integer *af, integer *lframe, integer *minwin, integer *maxwin, integer *dvwinl, integer *dvwinh);
+#endif
+
+/* ****************************************************************** */
+
+/* PLACEV Version 48 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.3 2001/04/12 21:27:53 markh
+ * app_record now supports wildcards of sort so your output file is not overwritten every time it's run. File.h got a documentation update on the ast_fileexists to include the return call. Watch out for the placea.c placev.c code, it's updates have not been tested yet. Just a few parenthesis to make it compile nicer on newer gcc versions with all the -W flags set.
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:31:02 jaf
+ * Initial revision
+ * */
+/* Revision 1.6 1996/03/19 20:42:19 jaf */
+/* Added some conditions satisfied by the output values in VWIN. */
+
+/* Revision 1.5 1996/03/19 18:37:56 jaf */
+/* Strengthened the specification of which indices of VWIN are read and */
+/* written. */
+
+/* Revision 1.4 1996/03/15 16:38:33 jaf */
+/* One tiny comment added. */
+
+/* Revision 1.3 1996/03/15 16:36:13 jaf */
+/* Added comments giving In/Out status of arguments. */
+
+/* Revision 1.2 1996/03/12 23:56:01 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:48:39 jaf */
+/* Initial revision */
+
+
+/* ****************************************************************** */
+
+/* Input: */
+/* OSBUF Buffer which holds sorted indexes of onsets */
+/* I believe that only indices 1 through OSPTR-1 can be read. */
+/* OSLEN */
+/* OSPTR Free pointer into OSBUF */
+/* AF */
+/* LFRAME */
+/* MINWIN */
+/* MAXWIN */
+/* DVWINL */
+/* DVWINH (This argument is never used. Should it be?) */
+/* Input/Output: */
+/* VWIN Buffer of Voicing Window Positions (Modified) */
+/* Index (2,AF-1) is read. */
+/* Indices (1,AF) and (2,AF) are written, */
+/* and then possibly read. */
+/* All other indices are unused. */
+/* In all cases, the final values will satsify the condition:*/
+/* VWIN(2,AF)-VWIN(1,AF)+1 .LE. MAXWIN */
+/* I'm not certain yet, but they may also satisfy: */
+/* MINWIN .LE. VWIN(2,AF)-VWIN(1,AF)+1 */
+/* Output: */
+/* OBOUND This variable is set by this procedure and used */
+/* in placing analysis windows (PLACEA). Bit 1 */
+/* indicates whether an onset bounds the left side */
+/* of the voicing window, and bit 2 indicates whether */
+/* an onset bounds the right side of the voicing window. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int placev_(integer *osbuf, integer *osptr, integer *oslen,
+ integer *obound, integer *vwin, integer *af, integer *lframe, integer
+ *minwin, integer *maxwin, integer *dvwinl, integer *dvwinh)
+{
+ /* System generated locals */
+ integer i__1, i__2;
+
+ /* Local variables */
+ logical crit;
+ integer i__, q, osptr1, hrange, lrange;
+
+/* Arguments */
+/* Local variables that need not be saved */
+/* Variables */
+/* LRANGE, HRANGE Range in which window is placed */
+/* OSPTR1 OSPTR excluding samples in 3F */
+/* Local state */
+/* None */
+/* Voicing Window Placement */
+
+/* __________________ __________________ ______________ */
+/* | | | */
+/* | 1F | 2F | 3F ... */
+/* |__________________|__________________|______________ */
+
+/* Previous | */
+/* Window | */
+/* ...________| */
+
+/* | | */
+/* ------>| This window's placement range |<------ */
+/* | | */
+
+/* There are three cases. Note that these are different from those */
+/* given in the LPC-10e phase 1 report. */
+
+/* 1. If there are no onsets in this range, then the voicing window */
+/* is centered in the pitch window. If such a placement is not within
+*/
+/* the window's placement range, then the window is placed in the left-
+*/
+/* most portion of the placement range. Its length is always MAXWIN. */
+
+/* 2. If the first onset is in 2F and there is sufficient room to place
+ */
+/* the window immediately before this onset, then the window is placed
+*/
+/* there, and its length is set to the maximum possible under these */
+/* constraints. */
+
+/* "Critical Region Exception": If there is another onset in 2F */
+/* such that a window can be placed between the two onsets, the */
+/* window is placed there (ie, as in case 3). */
+
+/* 3. Otherwise, the window is placed immediately after the onset. The
+ */
+/* window's length */
+/* is the longest length that can fit in the range under these constraint
+s,*/
+/* except that the window may be shortened even further to avoid overlapp
+ing*/
+/* other onsets in the placement range. In any case, the window's length
+*/
+/* is at least MINWIN. */
+
+/* Note that the values of MINWIN and LFRAME must be chosen such */
+/* that case 2 = false implies case 3 = true. This means that */
+/* MINWIN <= LFRAME/2. If this were not the case, then a fourth case */
+/* would have to be added for when the window cannot fit either before
+*/
+/* or after the onset. */
+
+/* Note also that onsets which weren't in 2F last time may be in 1F this
+ */
+/* time, due to the filter delays in computing onsets. The result is tha
+t*/
+/* occasionally a voicing window will overlap that onset. The only way
+*/
+/* to circumvent this problem is to add more delay in processing input
+*/
+/* speech. In the trade-off between delay and window-placement, window
+*/
+/* placement lost. */
+/* Compute the placement range */
+ /* Parameter adjustments */
+ --osbuf;
+ vwin -= 3;
+
+ /* Function Body */
+/* Computing MAX */
+ i__1 = vwin[((*af - 1) << 1) + 2] + 1, i__2 = (*af - 2) * *lframe + 1;
+ lrange = max(i__1,i__2);
+ hrange = *af * *lframe;
+/* Compute OSPTR1, so the following code only looks at relevant onsets. */
+ for (osptr1 = *osptr - 1; osptr1 >= 1; --osptr1) {
+ if (osbuf[osptr1] <= hrange) {
+ goto L90;
+ }
+ }
+L90:
+ ++osptr1;
+/* Check for case 1 first (fast case): */
+ if (osptr1 <= 1 || osbuf[osptr1 - 1] < lrange) {
+/* Computing MAX */
+ i__1 = vwin[((*af - 1) << 1) + 2] + 1;
+ vwin[(*af << 1) + 1] = max(i__1,*dvwinl);
+ vwin[(*af << 1) + 2] = vwin[(*af << 1) + 1] + *maxwin - 1;
+ *obound = 0;
+ } else {
+/* Search backward in OSBUF for first onset in range. */
+/* This code relies on the above check being performed first. */
+ for (q = osptr1 - 1; q >= 1; --q) {
+ if (osbuf[q] < lrange) {
+ goto L100;
+ }
+ }
+L100:
+ ++q;
+/* Check for case 2 (placement before onset): */
+/* Check for critical region exception: */
+ i__1 = osptr1 - 1;
+ for (i__ = q + 1; i__ <= i__1; ++i__) {
+ if (osbuf[i__] - osbuf[q] >= *minwin) {
+ crit = TRUE_;
+ goto L105;
+ }
+ }
+ crit = FALSE_;
+L105:
+/* Computing MAX */
+ i__1 = (*af - 1) * *lframe, i__2 = lrange + *minwin - 1;
+ if (! crit && osbuf[q] > max(i__1,i__2)) {
+ vwin[(*af << 1) + 2] = osbuf[q] - 1;
+/* Computing MAX */
+ i__1 = lrange, i__2 = vwin[(*af << 1) + 2] - *maxwin + 1;
+ vwin[(*af << 1) + 1] = max(i__1,i__2);
+ *obound = 2;
+/* Case 3 (placement after onset) */
+ } else {
+ vwin[(*af << 1) + 1] = osbuf[q];
+L110:
+ ++q;
+ if (q >= osptr1) {
+ goto L120;
+ }
+ if (osbuf[q] > vwin[(*af << 1) + 1] + *maxwin) {
+ goto L120;
+ }
+ if (osbuf[q] < vwin[(*af << 1) + 1] + *minwin) {
+ goto L110;
+ }
+ vwin[(*af << 1) + 2] = osbuf[q] - 1;
+ *obound = 3;
+ return 0;
+L120:
+/* Computing MIN */
+ i__1 = vwin[(*af << 1) + 1] + *maxwin - 1;
+ vwin[(*af << 1) + 2] = min(i__1,hrange);
+ *obound = 1;
+ }
+ }
+ return 0;
+} /* placev_ */
+
diff --git a/trunk/codecs/lpc10/preemp.c b/trunk/codecs/lpc10/preemp.c
new file mode 100644
index 000000000..645428c3c
--- /dev/null
+++ b/trunk/codecs/lpc10/preemp.c
@@ -0,0 +1,144 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:30:58 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int preemp_(real *inbuf, real *pebuf, integer *nsamp, real *coef, real *z__);
+#endif
+
+/* ******************************************************************* */
+
+/* PREEMP Version 55 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:30:58 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/14 23:16:29 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/11 23:23:34 jaf */
+/* Added a bunch of comments to an otherwise simple subroutine. */
+
+/* Revision 1.1 1996/02/07 14:48:48 jaf */
+/* Initial revision */
+
+
+/* ******************************************************************* */
+
+/* Preemphasize speech with a single-zero filter. */
+/* (When coef = .9375, preemphasis is as in LPC43.) */
+
+/* Inputs: */
+/* NSAMP - Number of samples to filter */
+/* INBUF - Input speech buffer */
+/* Indices 1 through NSAMP are read. */
+/* COEF - Preemphasis coefficient */
+/* Input/Output: */
+/* Z - Filter state */
+/* Output: */
+/* PEBUF - Preemphasized speech buffer (can be equal to INBUF) */
+/* Indices 1 through NSAMP are modified. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int preemp_(real *inbuf, real *pebuf, integer *nsamp, real *
+ coef, real *z__)
+{
+ /* System generated locals */
+ integer i__1;
+
+ /* Local variables */
+ real temp;
+ integer i__;
+
+/* Arguments */
+/* Local variables */
+
+/* None of these need to have their values saved from one */
+/* invocation to the next. */
+
+/* Logically, this subroutine computes the output sequence */
+/* pebuf(1:nsamp) defined by: */
+
+/* pebuf(i) = inbuf(i) - coef * inbuf(i-1) */
+
+/* where inbuf(0) is defined by the value of z given as input to */
+/* this subroutine. */
+
+/* What is this filter's frequency response and phase response? */
+
+/* Why is this filter applied to the speech? */
+
+/* Could it be more efficient to apply multiple filters */
+/* simultaneously, by combining them into one equivalent filter? */
+
+/* Are there ever cases when "factoring" one high-order filter into
+*/
+/* multiple smaller-order filter actually reduces the number of */
+/* arithmetic operations needed to perform them? */
+/* When I first read this subroutine, I didn't understand why the */
+/* variable temp was used. It seemed that the statements in the do
+*/
+/* loop could be replaced with the following: */
+
+/* pebuf(i) = inbuf(i) - coef * z */
+/* z = inbuf(i) */
+
+/* The reason for temp is so that even if pebuf and inbuf are the */
+/* same arrays in memory (i.e., they are aliased), then this */
+/* subroutine will still work correctly. I didn't realize this */
+/* until seeing the comment after PEBUF above that says "(can be */
+/* equal to INBUF)". */
+ /* Parameter adjustments */
+ --pebuf;
+ --inbuf;
+
+ /* Function Body */
+ i__1 = *nsamp;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ temp = inbuf[i__] - *coef * *z__;
+ *z__ = inbuf[i__];
+ pebuf[i__] = temp;
+/* L10: */
+ }
+ return 0;
+} /* preemp_ */
+
diff --git a/trunk/codecs/lpc10/prepro.c b/trunk/codecs/lpc10/prepro.c
new file mode 100644
index 000000000..d24ce0da3
--- /dev/null
+++ b/trunk/codecs/lpc10/prepro.c
@@ -0,0 +1,116 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:40:51 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:54 jaf
+ * Initial revision
+ *
+ */
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int prepro_(real *speech, integer *length,
+ struct lpc10_encoder_state *st);
+/*:ref: hp100_ 14 3 6 4 4 */
+/*:ref: inithp100_ 14 0 */
+#endif
+
+/* Table of constant values */
+
+static integer c__1 = 1;
+
+/* ********************************************************************* */
+
+/* PREPRO Version 48 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:40:51 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:54 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/14 23:22:56 jaf */
+/* Added comments about when INITPREPRO should be used. */
+
+/* Revision 1.2 1996/03/14 23:09:27 jaf */
+/* Added an entry named INITPREPRO that initializes the local state of */
+/* this subroutine, and those it calls (if any). */
+
+/* Revision 1.1 1996/02/07 14:48:54 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Pre-process input speech: */
+
+/* Inputs: */
+/* LENGTH - Number of SPEECH samples */
+/* Input/Output: */
+/* SPEECH(LENGTH) - Speech data. */
+/* Indices 1 through LENGTH are read and modified. */
+
+/* This subroutine has no local state maintained from one call to the */
+/* next, but HP100 does. If you want to switch to using a new audio */
+/* stream for this filter, or reinitialize its state for any other */
+/* reason, call the ENTRY INITPREPRO. */
+
+/* Subroutine */ int prepro_(real *speech, integer *length,
+ struct lpc10_encoder_state *st)
+{
+ extern /* Subroutine */ int hp100_(real *, integer *, integer *, struct lpc10_encoder_state *);
+
+/* Arguments */
+/* High Pass Filter at 100 Hz */
+ /* Parameter adjustments */
+ if (speech) {
+ --speech;
+ }
+
+ /* Function Body */
+ hp100_(&speech[1], &c__1, length, st);
+ return 0;
+} /* prepro_ */
diff --git a/trunk/codecs/lpc10/random.c b/trunk/codecs/lpc10/random.c
new file mode 100644
index 000000000..0f8e9b209
--- /dev/null
+++ b/trunk/codecs/lpc10/random.c
@@ -0,0 +1,125 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:41:32 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:49 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern integer random_(struct lpc10_decoder_state *st);
+#endif
+
+/* ********************************************************************** */
+
+/* RANDOM Version 49 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:41:32 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:49 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/20 16:13:54 jaf */
+/* Rearranged comments a little bit, and added comments explaining that */
+/* even though there is local state here, there is no need to create an */
+/* ENTRY for reinitializing it. */
+
+/* Revision 1.2 1996/03/14 22:25:29 jaf */
+/* Just rearranged the comments and local variable declarations a bit. */
+
+/* Revision 1.1 1996/02/07 14:49:01 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Pseudo random number generator based on Knuth, Vol 2, p. 27. */
+
+/* Function Return: */
+/* RANDOM - Integer variable, uniformly distributed over -32768 to 32767 */
+
+/* This subroutine maintains local state from one call to the next. */
+/* In the context of the LPC10 coder, there is no reason to reinitialize */
+/* this local state when switching between audio streams, because its */
+/* results are only used to generate noise for unvoiced frames. */
+
+integer random_(struct lpc10_decoder_state *st)
+{
+ /* Initialized data */
+
+ integer *j;
+ integer *k;
+ shortint *y;
+
+ /* System generated locals */
+ integer ret_val;
+
+/* Parameters/constants */
+/* Local state */
+/* The following is a 16 bit 2's complement addition, */
+/* with overflow checking disabled */
+
+ j = &(st->j);
+ k = &(st->k);
+ y = &(st->y[0]);
+
+ y[*k - 1] += y[*j - 1];
+ ret_val = y[*k - 1];
+ --(*k);
+ if (*k <= 0) {
+ *k = 5;
+ }
+ --(*j);
+ if (*j <= 0) {
+ *j = 5;
+ }
+ return ret_val;
+} /* random_ */
+
diff --git a/trunk/codecs/lpc10/rcchk.c b/trunk/codecs/lpc10/rcchk.c
new file mode 100644
index 000000000..6cb76ef7d
--- /dev/null
+++ b/trunk/codecs/lpc10/rcchk.c
@@ -0,0 +1,119 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:30:41 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int rcchk_(integer *order, real *rc1f, real *rc2f);
+#endif
+
+/* ********************************************************************* */
+
+/* RCCHK Version 45G */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:30:41 jaf
+ * Initial revision
+ * */
+/* Revision 1.4 1996/03/27 18:13:47 jaf */
+/* Commented out a call to subroutine ERROR. */
+
+/* Revision 1.3 1996/03/18 15:48:53 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 16:55:22 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:49:08 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Check RC's, repeat previous frame's RC's if unstable */
+
+/* Input: */
+/* ORDER - Number of RC's */
+/* RC1F - Previous frame's RC's */
+/* Indices 1 through ORDER may be read. */
+/* Input/Output: */
+/* RC2F - Present frame's RC's */
+/* Indices 1 through ORDER may be read, and written. */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int rcchk_(integer *order, real *rc1f, real *rc2f)
+{
+ /* System generated locals */
+ integer i__1;
+ real r__1;
+
+ /* Local variables */
+ integer i__;
+
+/* Arguments */
+/* Local variables that need not be saved */
+ /* Parameter adjustments */
+ --rc2f;
+ --rc1f;
+
+ /* Function Body */
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if ((r__1 = rc2f[i__], abs(r__1)) > .99f) {
+ goto L10;
+ }
+ }
+ return 0;
+/* Note: In version embedded in other software, all calls to ERROR
+*/
+/* should probably be removed. */
+L10:
+
+/* This call to ERROR is only needed for debugging purposes. */
+
+/* CALL ERROR('RCCHK',2,I) */
+ i__1 = *order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ rc2f[i__] = rc1f[i__];
+ }
+ return 0;
+} /* rcchk_ */
+
diff --git a/trunk/codecs/lpc10/synths.c b/trunk/codecs/lpc10/synths.c
new file mode 100644
index 000000000..4c5a70fac
--- /dev/null
+++ b/trunk/codecs/lpc10/synths.c
@@ -0,0 +1,425 @@
+/*
+
+$Log$
+Revision 1.16 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.15 2003/09/27 02:45:37 markster
+Fix various compiler warnings (bug #322)
+
+Revision 1.2 2003/09/27 02:45:37 markster
+Fix various compiler warnings (bug #322)
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:39 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:42:59 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:33 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int synths_(integer *voice, integer *pitch, real *rms, real *rc, real *speech, integer *k, struct lpc10_decoder_state *st);
+/* comlen contrl_ 12 */
+/*:ref: pitsyn_ 14 12 4 4 4 6 6 4 4 4 6 6 4 6 */
+/*:ref: irc2pc_ 14 5 6 6 4 6 6 */
+/*:ref: bsynz_ 14 7 6 4 4 6 6 6 6 */
+/*:ref: deemp_ 14 2 6 4 */
+/*:ref: initpitsyn_ 14 0 */
+/*:ref: initbsynz_ 14 0 */
+/*:ref: initdeemp_ 14 0 */
+#endif
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/* Table of constant values */
+
+static real c_b2 = .7f;
+
+/* ***************************************************************** */
+
+/* SYNTHS Version 54 */
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/27 02:45:37 markster
+ * Fix various compiler warnings (bug #322)
+ *
+ * Revision 1.2 2003/09/27 02:45:37 markster
+ * Fix various compiler warnings (bug #322)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:42:59 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:33 jaf
+ * Initial revision
+ * */
+/* Revision 1.5 1996/03/26 19:31:58 jaf */
+/* Commented out trace statements. */
+
+/* Revision 1.4 1996/03/25 19:41:01 jaf */
+/* Changed so that MAXFRM samples are always returned in the output array */
+/* SPEECH. */
+
+/* This required delaying the returned samples by MAXFRM sample times, */
+/* and remembering any "left over" samples returned by PITSYN from one */
+/* call of SYNTHS to the next. */
+
+/* Changed size of SPEECH from 2*MAXFRM to MAXFRM. Removed local */
+/* variable SOUT. Added local state variables BUF and BUFLEN. */
+
+/* Revision 1.3 1996/03/25 19:20:10 jaf */
+/* Added comments about the range of possible return values for argument */
+/* K, and increased the size of the arrays filled in by PITSYN from 11 to */
+/* 16, as has been already done inside of PITSYN. */
+
+/* Revision 1.2 1996/03/22 00:18:18 jaf */
+/* Added comments explaining meanings of input and output parameters, and */
+/* indicating which array indices can be read or written. */
+
+/* Added entry INITSYNTHS, which does nothing except call the */
+/* corresponding initialization entries for subroutines PITSYN, BSYNZ, */
+/* and DEEMP. */
+
+/* Revision 1.1 1996/02/07 14:49:44 jaf */
+/* Initial revision */
+
+
+/* ***************************************************************** */
+
+/* The note below is from the distributed version of the LPC10 coder. */
+/* The version of the code below has been modified so that SYNTHS always */
+/* has a constant frame length output of MAXFRM. */
+
+/* Also, BSYNZ and DEEMP need not be modified to work on variable */
+/* positions within an array. It is only necessary to pass the first */
+/* index desired as the array argument. What actually gets passed is the */
+/* address of that array position, which the subroutine treats as the */
+/* first index of the array. */
+
+/* This technique is used in subroutine ANALYS when calling PREEMP, so it */
+/* appears that multiple people wrote different parts of this LPC10 code, */
+/* and that they didn't necessarily have equivalent knowledge of Fortran */
+/* (not surprising). */
+
+/* NOTE: There is excessive buffering here, BSYNZ and DEEMP should be */
+/* changed to operate on variable positions within SOUT. Also, */
+/* the output length parameter is bogus, and PITSYN should be */
+/* rewritten to allow a constant frame length output. */
+
+/* Input: */
+/* VOICE - Half frame voicing decisions */
+/* Indices 1 through 2 read. */
+/* Input/Output: */
+/* PITCH - Pitch */
+/* PITCH is restricted to range 20 to 156, inclusive, */
+/* before calling subroutine PITSYN, and then PITSYN */
+/* can modify it further under some conditions. */
+/* RMS - Energy */
+/* Only use is for debugging, and passed to PITSYN. */
+/* See comments there for how it can be modified. */
+/* RC - Reflection coefficients */
+/* Indices 1 through ORDER restricted to range -.99 to .99, */
+/* before calling subroutine PITSYN, and then PITSYN */
+/* can modify it further under some conditions. */
+/* Output: */
+/* SPEECH - Synthesized speech samples. */
+/* Indices 1 through the final value of K are written. */
+/* K - Number of samples placed into array SPEECH. */
+/* This is always MAXFRM. */
+
+/* Subroutine */ int synths_(integer *voice, integer *pitch, real *
+ rms, real *rc, real *speech, integer *k, struct lpc10_decoder_state *st)
+{
+ /* Initialized data */
+
+ real *buf;
+ integer *buflen;
+
+ /* System generated locals */
+ integer i__1;
+ real r__1, r__2;
+
+ /* Local variables */
+ real rmsi[16];
+ integer nout, ivuv[16], i__, j;
+ extern /* Subroutine */ int deemp_(real *, integer *, struct lpc10_decoder_state *);
+ real ratio;
+ integer ipiti[16];
+ real g2pass;
+ real pc[10];
+ extern /* Subroutine */ int pitsyn_(integer *, integer *, integer *, real
+ *, real *, integer *, integer *, integer *, real *, real *,
+ integer *, real *, struct lpc10_decoder_state *);
+ real rci[160] /* was [10][16] */;
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/27 02:45:37 markster
+ * Fix various compiler warnings (bug #322)
+ *
+ * Revision 1.2 2003/09/27 02:45:37 markster
+ * Fix various compiler warnings (bug #322)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:42:59 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:33 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:03:47 jaf */
+/* Removed definitions for any constants that were no longer used. */
+
+/* Revision 1.2 1996/03/26 19:34:33 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:43:51 jaf */
+/* Initial revision */
+
+/* LPC Configuration parameters: */
+/* Frame size, Prediction order, Pitch period */
+/* Arguments */
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/09/27 02:45:37 markster
+ * Fix various compiler warnings (bug #322)
+ *
+ * Revision 1.2 2003/09/27 02:45:37 markster
+ * Fix various compiler warnings (bug #322)
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:39 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:42:59 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_decoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_decoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:33 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Parameters/constants */
+/* Local variables that need not be saved */
+/* Local state */
+/* BUF is a buffer of speech samples that would have been returned
+*/
+/* by the older version of SYNTHS, but the newer version doesn't, */
+/* so that the newer version can always return MAXFRM samples on */
+/* every call. This has the effect of delaying the return of */
+/* samples for one additional frame time. */
+
+/* Indices 1 through BUFLEN contain samples that are left over from
+*/
+/* the last call to SYNTHS. Given the way that PITSYN works, */
+/* BUFLEN should always be in the range MAXFRM-MAXPIT+1 through */
+/* MAXFRM, inclusive, after a call to SYNTHS is complete. */
+
+/* On the first call to SYNTHS (or the first call after */
+/* reinitializing with the entry INITSYNTHS), BUFLEN is MAXFRM, and
+*/
+/* a frame of silence is always returned. */
+ /* Parameter adjustments */
+ if (voice) {
+ --voice;
+ }
+ if (rc) {
+ --rc;
+ }
+ if (speech) {
+ --speech;
+ }
+
+ /* Function Body */
+ buf = &(st->buf[0]);
+ buflen = &(st->buflen);
+
+/* Computing MAX */
+ i__1 = min(*pitch,156);
+ *pitch = max(i__1,20);
+ i__1 = contrl_1.order;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+/* Computing MAX */
+/* Computing MIN */
+ r__2 = rc[i__];
+ r__1 = min(r__2,.99f);
+ rc[i__] = max(r__1,-.99f);
+ }
+ pitsyn_(&contrl_1.order, &voice[1], pitch, rms, &rc[1], &contrl_1.lframe,
+ ivuv, ipiti, rmsi, rci, &nout, &ratio, st);
+ if (nout > 0) {
+ i__1 = nout;
+ for (j = 1; j <= i__1; ++j) {
+
+/* Add synthesized speech for pitch period J to the en
+d of */
+/* BUF. */
+
+ irc2pc_(&rci[j * 10 - 10], pc, &contrl_1.order, &c_b2, &g2pass);
+ bsynz_(pc, &ipiti[j - 1], &ivuv[j - 1], &buf[*buflen], &rmsi[j - 1]
+ , &ratio, &g2pass, st);
+ deemp_(&buf[*buflen], &ipiti[j - 1], st);
+ *buflen += ipiti[j - 1];
+ }
+
+/* Copy first MAXFRM samples from BUF to output array SPEECH
+*/
+/* (scaling them), and then remove them from the beginning of
+ */
+/* BUF. */
+
+ for (i__ = 1; i__ <= 180; ++i__) {
+ speech[i__] = buf[i__ - 1] / 4096.f;
+ }
+ *k = 180;
+ *buflen += -180;
+ i__1 = *buflen;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ buf[i__ - 1] = buf[i__ + 179];
+ }
+ }
+ return 0;
+} /* synths_ */
diff --git a/trunk/codecs/lpc10/tbdm.c b/trunk/codecs/lpc10/tbdm.c
new file mode 100644
index 000000000..2f6f3d692
--- /dev/null
+++ b/trunk/codecs/lpc10/tbdm.c
@@ -0,0 +1,188 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:40 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:30:26 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int tbdm_(real *speech, integer *lpita, integer *tau, integer *ltau, real *amdf, integer *minptr, integer *maxptr, integer *mintau);
+/*:ref: difmag_ 14 8 6 4 4 4 4 6 4 4 */
+#endif
+
+/* ********************************************************************** */
+
+/* TBDM Version 49 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:40 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:30:26 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/18 22:14:00 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 14:48:37 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:49:54 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/*TURBO DIFMAG: Compute High Resolution Average Magnitude Difference Function
+*/
+
+/* Note: There are several constants in here that appear to depend on a */
+/* particular TAU table. That's not a problem for the LPC10 coder, but */
+/* watch out if you change the contents of TAU in the subroutine ANALYS. */
+
+/* Input: */
+/* SPEECH - Low pass filtered speech */
+/* Indices 1 through MAX+LPITA-1 are read, where: */
+/* MAX = (TAU(LTAU)-TAU(1))/2+1 */
+/* (If TAU(1) .LT. 39, then larger indices could be read */
+/* by the last call to DIFMAG below.) */
+/* LPITA - Length of speech buffer */
+/* TAU - Table of lags, sorted in increasing order. */
+/* Indices 1 through LTAU read. */
+/* LTAU - Number of lag values to compute */
+/* Output: */
+/* AMDF - Average Magnitude Difference for each lag in TAU */
+/* Indices 1 through LTAU written, and several might then be read.*/
+/* MINPTR - Index of minimum AMDF value */
+/* MAXPTR - Index of maximum AMDF value within +/- 1/2 octave of min */
+/* MINTAU - Lag corresponding to minimum AMDF value */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int tbdm_(real *speech, integer *lpita, integer *tau,
+ integer *ltau, real *amdf, integer *minptr, integer *maxptr, integer *
+ mintau)
+{
+ /* System generated locals */
+ integer i__1, i__2, i__3, i__4;
+
+ /* Local variables */
+ real amdf2[6];
+ integer minp2, ltau2, maxp2, i__;
+ extern /* Subroutine */ int difmag_(real *, integer *, integer *, integer
+ *, integer *, real *, integer *, integer *);
+ integer minamd, ptr, tau2[6];
+
+/* Arguments */
+/* REAL SPEECH(LPITA+TAU(LTAU)), AMDF(LTAU) */
+/* Stupid TOAST doesn't understand expressions */
+/* Local variables that need not be saved */
+/* Local state */
+/* None */
+/* Compute full AMDF using log spaced lags, find coarse minimum */
+ /* Parameter adjustments */
+ --speech;
+ --amdf;
+ --tau;
+
+ /* Function Body */
+ difmag_(&speech[1], lpita, &tau[1], ltau, &tau[*ltau], &amdf[1], minptr,
+ maxptr);
+ *mintau = tau[*minptr];
+ minamd = (integer)amdf[*minptr];
+/* Build table containing all lags within +/- 3 of the AMDF minimum */
+/* excluding all that have already been computed */
+ ltau2 = 0;
+ ptr = *minptr - 2;
+/* Computing MAX */
+ i__1 = *mintau - 3;
+/* Computing MIN */
+ i__3 = *mintau + 3, i__4 = tau[*ltau] - 1;
+ i__2 = min(i__3,i__4);
+ for (i__ = max(i__1,41); i__ <= i__2; ++i__) {
+ while(tau[ptr] < i__) {
+ ++ptr;
+ }
+ if (tau[ptr] != i__) {
+ ++ltau2;
+ tau2[ltau2 - 1] = i__;
+ }
+ }
+/* Compute AMDF of the new lags, if there are any, and choose one */
+/* if it is better than the coarse minimum */
+ if (ltau2 > 0) {
+ difmag_(&speech[1], lpita, tau2, &ltau2, &tau[*ltau], amdf2, &minp2, &
+ maxp2);
+ if (amdf2[minp2 - 1] < (real) minamd) {
+ *mintau = tau2[minp2 - 1];
+ minamd = (integer)amdf2[minp2 - 1];
+ }
+ }
+/* Check one octave up, if there are any lags not yet computed */
+ if (*mintau >= 80) {
+ i__ = *mintau / 2;
+ if ((i__ & 1) == 0) {
+ ltau2 = 2;
+ tau2[0] = i__ - 1;
+ tau2[1] = i__ + 1;
+ } else {
+ ltau2 = 1;
+ tau2[0] = i__;
+ }
+ difmag_(&speech[1], lpita, tau2, &ltau2, &tau[*ltau], amdf2, &minp2, &
+ maxp2);
+ if (amdf2[minp2 - 1] < (real) minamd) {
+ *mintau = tau2[minp2 - 1];
+ minamd = (integer)amdf2[minp2 - 1];
+ *minptr += -20;
+ }
+ }
+/* Force minimum of the AMDF array to the high resolution minimum */
+ amdf[*minptr] = (real) minamd;
+/* Find maximum of AMDF within 1/2 octave of minimum */
+/* Computing MAX */
+ i__2 = *minptr - 5;
+ *maxptr = max(i__2,1);
+/* Computing MIN */
+ i__1 = *minptr + 5;
+ i__2 = min(i__1,*ltau);
+ for (i__ = *maxptr + 1; i__ <= i__2; ++i__) {
+ if (amdf[i__] > amdf[*maxptr]) {
+ *maxptr = i__;
+ }
+ }
+ return 0;
+} /* tbdm_ */
+
diff --git a/trunk/codecs/lpc10/voicin.c b/trunk/codecs/lpc10/voicin.c
new file mode 100644
index 000000000..3605d2f2e
--- /dev/null
+++ b/trunk/codecs/lpc10/voicin.c
@@ -0,0 +1,786 @@
+/*
+
+$Log$
+Revision 1.16 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.15 2003/11/23 22:14:32 markster
+Various warning cleanups
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:40 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.2 1996/08/20 20:45:00 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:14 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int voicin_(integer *vwin, real *inbuf, real *lpbuf, integer *buflim, integer *half, real *minamd, real *maxamd, integer *mintau, real *ivrc, integer *obound, integer *voibuf, integer *af, struct lpc10_encoder_state *st);
+/* comlen contrl_ 12 */
+/*:ref: vparms_ 14 14 4 6 6 4 4 6 4 4 4 4 6 6 6 6 */
+#endif
+
+/* Common Block Declarations */
+
+extern struct {
+ integer order, lframe;
+ logical corrp;
+} contrl_;
+
+#define contrl_1 contrl_
+
+/****************************************************************************/
+
+/* VOICIN Version 52 */
+
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/11/23 22:14:32 markster
+ * Various warning cleanups
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:40 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:45:00 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:14 jaf
+ * Initial revision
+ * */
+/* Revision 1.10 1996/03/29 17:59:14 jaf */
+/* Avoided using VALUE(9), although it shouldn't affect the function of */
+/* the code at all, because it was always multiplied by VDC(9,SNRL), */
+/* which is 0 for all values of SNRL. Still, if VALUE(9) had an initial */
+/* value of IEEE NaN, it might cause trouble (I don't know how IEEE */
+/* defines Nan * 0. It should either be NaN or 0.) */
+
+/* Revision 1.9 1996/03/29 17:54:46 jaf */
+/* Added a few comments about the accesses made to argument array VOIBUF */
+/* and the local saved array VOICE. */
+
+/* Revision 1.8 1996/03/27 18:19:54 jaf */
+/* Added an assignment to VSTATE that does not affect the function of the */
+/* program at all. The only reason I put it in was so that the tracing */
+/* statements at the end, when enabled, will print a consistent value for */
+/* VSTATE when HALF .EQ. 1, rather than a garbage value that could change */
+/* from one call to the next. */
+
+/* Revision 1.7 1996/03/26 20:00:06 jaf */
+/* Removed the inclusion of the file "vcomm.fh", and put its contents */
+/* into this file. It was included nowhere else but here. */
+
+/* Revision 1.6 1996/03/26 19:38:09 jaf */
+/* Commented out trace statements. */
+
+/* Revision 1.5 1996/03/19 20:43:45 jaf */
+/* Added comments about which indices of OBOUND and VOIBUF can be */
+/* accessed, and whether they are read or written. VOIBUF is fairly */
+/* messy. */
+
+/* Revision 1.4 1996/03/19 15:00:58 jaf */
+/* Moved the DATA statements for the *VDC* variables later, as it is */
+/* apparently illegal to have DATA statements before local variable */
+/* declarations. */
+
+/* Revision 1.3 1996/03/19 00:10:49 jaf */
+/* Heavily commented the local variables that are saved from one */
+/* invocation to the next, and how the local variable FIRST is used to */
+/* avoid the need to assign most of them initial values with DATA */
+/* statements. */
+
+/* A few should be initialized, but aren't. I've guessed initial values */
+/* for two of these, SFBUE and SLBUE, and I've convinced myself that for */
+/* VOICE, the effects of uninitialized values will die out after 2 or 3 */
+/* frame times. It would still be good to choose initial values for */
+/* these, but I don't know what reasonable values would be (0 comes to */
+/* mind). */
+
+/* Revision 1.2 1996/03/13 16:09:28 jaf */
+/* Comments added explaining which of the local variables of this */
+/* subroutine need to be saved from one invocation to the next, and which */
+/* do not. */
+
+/* WARNING! Some of them that should are never given initial values in */
+/* this code. Hopefully, Fortran 77 defines initial values for them, but */
+/* even so, giving them explicit initial values is preferable. */
+
+/* WARNING! VALUE(9) is used, but never assigned a value. It should */
+/* probably be eliminated from the code. */
+
+/* Revision 1.1 1996/02/07 14:50:28 jaf */
+/* Initial revision */
+
+
+/****************************************************************************/
+
+/* Voicing Detection (VOICIN) makes voicing decisions for each half */
+/* frame of input speech. Tentative voicing decisions are made two frames*/
+/* in the future (2F) for each half frame. These decisions are carried */
+/* through one frame in the future (1F) to the present (P) frame where */
+/* they are examined and smoothed, resulting in the final voicing */
+/* decisions for each half frame. */
+/* The voicing parameter (signal measurement) column vector (VALUE) */
+/* is based on a rectangular window of speech samples determined by the */
+/* window placement algorithm. The voicing parameter vector contains the*/
+/* AMDF windowed maximum-to-minimum ratio, the zero crossing rate, energy*/
+/* measures, reflection coefficients, and prediction gains. The voicing */
+/* window is placed to avoid contamination of the voicing parameter vector*/
+/* with speech onsets. */
+/* The input signal is then classified as unvoiced (including */
+/* silence) or voiced. This decision is made by a linear discriminant */
+/* function consisting of a dot product of the voicing decision */
+/* coefficient (VDC) row vector with the measurement column vector */
+/* (VALUE). The VDC vector is 2-dimensional, each row vector is optimized*/
+/* for a particular signal-to-noise ratio (SNR). So, before the dot */
+/* product is performed, the SNR is estimated to select the appropriate */
+/* VDC vector. */
+/* The smoothing algorithm is a modified median smoother. The */
+/* voicing discriminant function is used by the smoother to determine how*/
+/* strongly voiced or unvoiced a signal is. The smoothing is further */
+/* modified if a speech onset and a voicing decision transition occur */
+/* within one half frame. In this case, the voicing decision transition */
+/* is extended to the speech onset. For transmission purposes, there are*/
+/* constraints on the duration and transition of voicing decisions. The */
+/* smoother takes these constraints into account. */
+/* Finally, the energy estimates are updated along with the dither */
+/* threshold used to calculate the zero crossing rate (ZC). */
+
+/* Inputs: */
+/* VWIN - Voicing window limits */
+/* The indices read of arrays VWIN, INBUF, LPBUF, and BUFLIM */
+/* are the same as those read by subroutine VPARMS. */
+/* INBUF - Input speech buffer */
+/* LPBUF - Low-pass filtered speech buffer */
+/* BUFLIM - INBUF and LPBUF limits */
+/* HALF - Present analysis half frame number */
+/* MINAMD - Minimum value of the AMDF */
+/* MAXAMD - Maximum value of the AMDF */
+/* MINTAU - Pointer to the lag of the minimum AMDF value */
+/* IVRC(2) - Inverse filter's RC's */
+/* Only index 2 of array IVRC read under normal operation. */
+/* (Index 1 is also read when debugging is turned on.) */
+/* OBOUND - Onset boundary descriptions */
+/* Indices 1 through 3 read if (HALF .NE. 1), otherwise untouched.
+*/
+/* AF - The analysis frame number */
+/* Output: */
+/* VOIBUF(2,0:AF) - Buffer of voicing decisions */
+/* Index (HALF,3) written. */
+/* If (HALF .EQ. 1), skip down to "Read (HALF,3)" below. */
+/* Indices (1,2), (2,1), (1,2), and (2,2) read. */
+/* One of the following is then done: */
+/* read (1,3) and possibly write (1,2) */
+/* read (1,3) and write (1,2) or (2,2) */
+/* write (2,1) */
+/* write (2,1) or (1,2) */
+/* read (1,0) and (1,3) and then write (2,2) or (1,1) */
+/* no reads or writes on VOIBUF */
+/* Finally, read (HALF,3) */
+/* Internal: */
+/* QS - Ratio of preemphasized to full-band energies */
+/* RC1 - First reflection coefficient */
+/* AR_B - Product of the causal forward and reverse pitch prediction gain
+s*/
+/* AR_F - Product of the noncausal forward and rev. pitch prediction gain
+s*/
+/* ZC - Zero crossing rate */
+/* DITHER - Zero crossing threshold level */
+/* MAXMIN - AMDF's 1 octave windowed maximum-to-minimum ratio */
+/* MINPTR - Location of minimum AMDF value */
+/* NVDC - Number of elements in each VDC vector */
+/* NVDCL - Number of VDC vectors */
+/* VDCL - SNR values corresponding to the set of VDC's */
+/* VDC - 2-D voicing decision coefficient vector */
+/* VALUE(9) - Voicing Parameters */
+/* VOICE(2,3)- History of LDA results */
+/* On every call when (HALF .EQ. 1), VOICE(*,I+1) is */
+/* shifted back to VOICE(*,I), for I=1,2. */
+/* VOICE(HALF,3) is written on every call. */
+/* Depending on several conditions, one or more of */
+/* (1,1), (1,2), (2,1), and (2,2) might then be read. */
+/* LBE - Ratio of low-band instantaneous to average energies */
+/* FBE - Ratio of full-band instantaneous to average energies */
+/* LBVE - Low band voiced energy */
+/* LBUE - Low band unvoiced energy */
+/* FBVE - Full band voiced energy */
+/* FBUE - Full band unvoiced energy */
+/* OFBUE - Previous full-band unvoiced energy */
+/* OLBUE - Previous low-band unvoiced energy */
+/* REF - Reference energy for initialization and DITHER threshold */
+/* SNR - Estimate of signal-to-noise ratio */
+/* SNR2 - Estimate of low-band signal-to-noise ratio */
+/* SNRL - SNR level number */
+/* OT - Onset transition present */
+/* VSTATE - Decimal interpretation of binary voicing classifications */
+/* FIRST - First call flag */
+
+/* This subroutine maintains local state from one call to the next. If */
+/* you want to switch to using a new audio stream for this filter, or */
+/* reinitialize its state for any other reason, call the ENTRY */
+/* INITVOICIN. */
+
+/* Subroutine */ int voicin_(integer *vwin, real *inbuf, real *
+ lpbuf, integer *buflim, integer *half, real *minamd, real *maxamd,
+ integer *mintau, real *ivrc, integer *obound, integer *voibuf,
+ integer *af, struct lpc10_encoder_state *st)
+{
+ /* Initialized data */
+
+ real *dither;
+ static real vdc[100] /* was [10][10] */ = { 0.f,1714.f,-110.f,
+ 334.f,-4096.f,-654.f,3752.f,3769.f,0.f,1181.f,0.f,874.f,-97.f,
+ 300.f,-4096.f,-1021.f,2451.f,2527.f,0.f,-500.f,0.f,510.f,-70.f,
+ 250.f,-4096.f,-1270.f,2194.f,2491.f,0.f,-1500.f,0.f,500.f,-10.f,
+ 200.f,-4096.f,-1300.f,2e3f,2e3f,0.f,-2e3f,0.f,500.f,0.f,0.f,
+ -4096.f,-1300.f,2e3f,2e3f,0.f,-2500.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,
+ 0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,
+ 0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,
+ 0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f };
+ static integer nvdcl = 5;
+ static real vdcl[10] = { 600.f,450.f,300.f,200.f,0.f,0.f,0.f,0.f,0.f,0.f }
+ ;
+
+ /* System generated locals */
+ integer inbuf_offset = 0, lpbuf_offset = 0, i__1, i__2;
+ real r__1, r__2;
+
+ /* Builtin functions */
+ integer i_nint(real *);
+ double sqrt(doublereal);
+
+ /* Local variables */
+ real ar_b__, ar_f__;
+ integer *lbve, *lbue, *fbve, *fbue;
+ integer snrl, i__;
+ integer *ofbue, *sfbue;
+ real *voice;
+ integer *olbue, *slbue;
+ real value[9];
+ integer zc;
+ logical ot;
+ real qs;
+ real *maxmin;
+ integer vstate;
+ real rc1;
+ extern /* Subroutine */ int vparms_(integer *, real *, real *, integer *,
+ integer *, real *, integer *, integer *, integer *, integer *,
+ real *, real *, real *, real *);
+ integer fbe, lbe;
+ real *snr;
+ real snr2;
+
+/* Global Variables: */
+/* Arguments */
+/* $Log$
+ * Revision 1.16 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.15 2003/11/23 22:14:32 markster
+ * Various warning cleanups
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:40 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.2 1996/08/20 20:45:00 jaf
+ * Removed all static local variables that were SAVE'd in the Fortran
+ * code, and put them in struct lpc10_encoder_state that is passed as an
+ * argument.
+ *
+ * Removed init function, since all initialization is now done in
+ * init_lpc10_encoder_state().
+ *
+ * Revision 1.1 1996/08/19 22:30:14 jaf
+ * Initial revision
+ * */
+/* Revision 1.3 1996/03/29 22:05:55 jaf */
+/* Commented out the common block variables that are not needed by the */
+/* embedded version. */
+
+/* Revision 1.2 1996/03/26 19:34:50 jaf */
+/* Added comments indicating which constants are not needed in an */
+/* application that uses the LPC-10 coder. */
+
+/* Revision 1.1 1996/02/07 14:44:09 jaf */
+/* Initial revision */
+
+/* LPC Processing control variables: */
+
+/* *** Read-only: initialized in setup */
+
+/* Files for Speech, Parameter, and Bitstream Input & Output, */
+/* and message and debug outputs. */
+
+/* Here are the only files which use these variables: */
+
+/* lpcsim.f setup.f trans.f error.f vqsetup.f */
+
+/* Many files which use fdebug are not listed, since it is only used in */
+/* those other files conditionally, to print trace statements. */
+/* integer fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* LPC order, Frame size, Quantization rate, Bits per frame, */
+/* Error correction */
+/* Subroutine SETUP is the only place where order is assigned a value, */
+/* and that value is 10. It could increase efficiency 1% or so to */
+/* declare order as a constant (i.e., a Fortran PARAMETER) instead of as
+*/
+/* a variable in a COMMON block, since it is used in many places in the */
+/* core of the coding and decoding routines. Actually, I take that back.
+*/
+/* At least when compiling with f2c, the upper bound of DO loops is */
+/* stored in a local variable before the DO loop begins, and then that is
+*/
+/* compared against on each iteration. */
+/* Similarly for lframe, which is given a value of MAXFRM in SETUP. */
+/* Similarly for quant, which is given a value of 2400 in SETUP. quant */
+/* is used in only a few places, and never in the core coding and */
+/* decoding routines, so it could be eliminated entirely. */
+/* nbits is similar to quant, and is given a value of 54 in SETUP. */
+/* corrp is given a value of .TRUE. in SETUP, and is only used in the */
+/* subroutines ENCODE and DECODE. It doesn't affect the speed of the */
+/* coder significantly whether it is .TRUE. or .FALSE., or whether it is
+*/
+/* a constant or a variable, since it is only examined once per frame. */
+/* Leaving it as a variable that is set to .TRUE. seems like a good */
+/* idea, since it does enable some error-correction capability for */
+/* unvoiced frames, with no change in the coding rate, and no noticeable
+*/
+/* quality difference in the decoded speech. */
+/* integer quant, nbits */
+/* *** Read/write: variables for debugging, not needed for LPC algorithm
+*/
+
+/* Current frame, Unstable frames, Output clip count, Max onset buffer,
+*/
+/* Debug listing detail level, Line count on listing page */
+
+/* nframe is not needed for an embedded LPC10 at all. */
+/* nunsfm is initialized to 0 in SETUP, and incremented in subroutine */
+/* ERROR, which is only called from RCCHK. When LPC10 is embedded into */
+/* an application, I would recommend removing the call to ERROR in RCCHK,
+*/
+/* and remove ERROR and nunsfm completely. */
+/* iclip is initialized to 0 in SETUP, and incremented in entry SWRITE in
+*/
+/* sread.f. When LPC10 is embedded into an application, one might want */
+/* to cause it to be incremented in a routine that takes the output of */
+/* SYNTHS and sends it to an audio device. It could be optionally */
+/* displayed, for those that might want to know what it is. */
+/* maxosp is never initialized to 0 in SETUP, although it probably should
+*/
+/* be, and it is updated in subroutine ANALYS. I doubt that its value */
+/* would be of much interest to an application in which LPC10 is */
+/* embedded. */
+/* listl and lincnt are not needed for an embedded LPC10 at all. */
+/* integer nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* common /contrl/ fsi, fso, fpi, fpo, fbi, fbo, pbin, fmsg, fdebug */
+/* common /contrl/ quant, nbits */
+/* common /contrl/ nframe, nunsfm, iclip, maxosp, listl, lincnt */
+/* Parameters/constants */
+/* Voicing coefficient and Linear Discriminant Analysis variables:
+*/
+/* Max number of VDC's and VDC levels */
+/* The following are not Fortran PARAMETER's, but they are */
+/* initialized with DATA statements, and never modified. */
+/* Actual number of VDC's and levels */
+/* Local variables that need not be saved */
+/* Note: */
+
+/* VALUE(1) through VALUE(8) are assigned values, but VALUE(9) */
+/* never is. Yet VALUE(9) is read in the loop that begins "DO I =
+*/
+/* 1, 9" below. I believe that this doesn't cause any problems in
+*/
+/* this subroutine, because all VDC(9,*) array elements are 0, and
+*/
+/* this is what is multiplied by VALUE(9) in all cases. Still, it
+*/
+/* would save a multiplication to change the loop to "DO I = 1, 8".
+*/
+/* Local state */
+/* WARNING! */
+
+/* VOICE, SFBUE, and SLBUE should be saved from one invocation to */
+/* the next, but they are never given an initial value. */
+
+/* Does Fortran 77 specify some default initial value, like 0, or */
+/* is it undefined? If it is undefined, then this code should be */
+/* corrected to specify an initial value. */
+
+/* For VOICE, note that it is "shifted" in the statement that */
+/* begins "IF (HALF .EQ. 1) THEN" below. Also, uninitialized */
+/* values in the VOICE array can only affect entries in the VOIBUF
+*/
+/* array that are for the same frame, or for an older frame. Thus
+*/
+/* the effects of uninitialized values in VOICE cannot linger on */
+/* for more than 2 or 3 frame times. */
+
+/* For SFBUE and SLBUE, the effects of uninitialized values can */
+/* linger on for many frame times, because their previous values */
+/* are exponentially decayed. Thus it is more important to choose
+*/
+/* initial values for these variables. I would guess that a */
+/* reasonable initial value for SFBUE is REF/16, the same as used */
+/* for FBUE and OFBUE. Similarly, SLBUE can be initialized to */
+/* REF/32, the same as for LBUE and OLBUE. */
+
+/* These guessed initial values should be validated by re-running */
+/* the modified program on some audio samples. */
+
+/* Declare and initialize filters: */
+
+ dither = (&st->dither);
+ snr = (&st->snr);
+ maxmin = (&st->maxmin);
+ voice = (&st->voice[0]);
+ lbve = (&st->lbve);
+ lbue = (&st->lbue);
+ fbve = (&st->fbve);
+ fbue = (&st->fbue);
+ ofbue = (&st->ofbue);
+ olbue = (&st->olbue);
+ sfbue = (&st->sfbue);
+ slbue = (&st->slbue);
+
+ /* Parameter adjustments */
+ if (vwin) {
+ --vwin;
+ }
+ if (buflim) {
+ --buflim;
+ }
+ if (inbuf) {
+ inbuf_offset = buflim[1];
+ inbuf -= inbuf_offset;
+ }
+ if (lpbuf) {
+ lpbuf_offset = buflim[3];
+ lpbuf -= lpbuf_offset;
+ }
+ if (ivrc) {
+ --ivrc;
+ }
+ if (obound) {
+ --obound;
+ }
+ if (voibuf) {
+ --voibuf;
+ }
+
+ /* Function Body */
+
+/* The following variables are saved from one invocation to the */
+/* next, but are not initialized with DATA statements. This is */
+/* acceptable, because FIRST is initialized ot .TRUE., and the */
+/* first time that this subroutine is then called, they are all */
+/* given initial values. */
+
+/* SNR */
+/* LBVE, LBUE, FBVE, FBUE, OFBUE, OLBUE */
+
+/* MAXMIN is initialized on the first call, assuming that HALF */
+/* .EQ. 1 on first call. This is how ANALYS calls this subroutine.
+*/
+
+/* Voicing Decision Parameter vector (* denotes zero coefficient): */
+
+/* * MAXMIN */
+/* LBE/LBVE */
+/* ZC */
+/* RC1 */
+/* QS */
+/* IVRC2 */
+/* aR_B */
+/* aR_F */
+/* * LOG(LBE/LBVE) */
+/* Define 2-D voicing decision coefficient vector according to the voicin
+g*/
+/* parameter order above. Each row (VDC vector) is optimized for a speci
+fic*/
+/* SNR. The last element of the vector is the constant. */
+/* E ZC RC1 Qs IVRC2 aRb aRf c */
+
+/* The VOICE array contains the result of the linear discriminant functio
+n*/
+/* (analog values). The VOIBUF array contains the hard-limited binary
+*/
+/* voicing decisions. The VOICE and VOIBUF arrays, according to FORTRAN
+ */
+/* memory allocation, are addressed as: */
+
+/* (half-frame number, future-frame number) */
+
+/* | Past | Present | Future1 | Future2 | */
+/* | 1,0 | 2,0 | 1,1 | 2,1 | 1,2 | 2,2 | 1,3 | 2,3 | ---> time */
+
+/* Update linear discriminant function history each frame: */
+ if (*half == 1) {
+ voice[0] = voice[2];
+ voice[1] = voice[3];
+ voice[2] = voice[4];
+ voice[3] = voice[5];
+ *maxmin = *maxamd / max(*minamd,1.f);
+ }
+/* Calculate voicing parameters twice per frame: */
+ vparms_(&vwin[1], &inbuf[inbuf_offset], &lpbuf[lpbuf_offset], &buflim[1],
+ half, dither, mintau, &zc, &lbe, &fbe, &qs, &rc1, &ar_b__, &
+ ar_f__);
+/* Estimate signal-to-noise ratio to select the appropriate VDC vector.
+*/
+/* The SNR is estimated as the running average of the ratio of the */
+/* running average full-band voiced energy to the running average */
+/* full-band unvoiced energy. SNR filter has gain of 63. */
+ r__1 = (*snr + *fbve / (real) max(*fbue,1)) * 63 / 64.f;
+ *snr = (real) i_nint(&r__1);
+ snr2 = *snr * *fbue / max(*lbue,1);
+/* Quantize SNR to SNRL according to VDCL thresholds. */
+ snrl = 1;
+ i__1 = nvdcl - 1;
+ for (snrl = 1; snrl <= i__1; ++snrl) {
+ if (snr2 > vdcl[snrl - 1]) {
+ goto L69;
+ }
+ }
+/* (Note: SNRL = NVDCL here) */
+L69:
+/* Linear discriminant voicing parameters: */
+ value[0] = *maxmin;
+ value[1] = (real) lbe / max(*lbve,1);
+ value[2] = (real) zc;
+ value[3] = rc1;
+ value[4] = qs;
+ value[5] = ivrc[2];
+ value[6] = ar_b__;
+ value[7] = ar_f__;
+/* Evaluation of linear discriminant function: */
+ voice[*half + 3] = vdc[snrl * 10 - 1];
+ for (i__ = 1; i__ <= 8; ++i__) {
+ voice[*half + 3] += vdc[i__ + snrl * 10 - 11] * value[i__ - 1];
+ }
+/* Classify as voiced if discriminant > 0, otherwise unvoiced */
+/* Voicing decision for current half-frame: 1 = Voiced; 0 = Unvoiced */
+ if (voice[*half + 3] > 0.f) {
+ voibuf[*half + 6] = 1;
+ } else {
+ voibuf[*half + 6] = 0;
+ }
+/* Skip voicing decision smoothing in first half-frame: */
+/* Give a value to VSTATE, so that trace statements below will print
+*/
+/* a consistent value from one call to the next when HALF .EQ. 1. */
+/* The value of VSTATE is not used for any other purpose when this is
+*/
+/* true. */
+ vstate = -1;
+ if (*half == 1) {
+ goto L99;
+ }
+/* Voicing decision smoothing rules (override of linear combination): */
+
+/* Unvoiced half-frames: At least two in a row. */
+/* -------------------- */
+
+/* Voiced half-frames: At least two in a row in one frame. */
+/* ------------------- Otherwise at least three in a row. */
+/* (Due to the way transition frames are encoded) */
+
+/* In many cases, the discriminant function determines how to smooth. */
+/* In the following chart, the decisions marked with a * may be overridden
+.*/
+
+/* Voicing override of transitions at onsets: */
+/* If a V/UV or UV/V voicing decision transition occurs within one-half
+*/
+/* frame of an onset bounding a voicing window, then the transition is */
+/* moved to occur at the onset. */
+
+/* P 1F */
+/* ----- ----- */
+/* 0 0 0 0 */
+/* 0 0 0* 1 (If there is an onset there) */
+/* 0 0 1* 0* (Based on 2F and discriminant distance) */
+/* 0 0 1 1 */
+/* 0 1* 0 0 (Always) */
+/* 0 1* 0* 1 (Based on discriminant distance) */
+/* 0* 1 1 0* (Based on past, 2F, and discriminant distance) */
+/* 0 1* 1 1 (If there is an onset there) */
+/* 1 0* 0 0 (If there is an onset there) */
+/* 1 0 0 1 */
+/* 1 0* 1* 0 (Based on discriminant distance) */
+/* 1 0* 1 1 (Always) */
+/* 1 1 0 0 */
+/* 1 1 0* 1* (Based on 2F and discriminant distance) */
+/* 1 1 1* 0 (If there is an onset there) */
+/* 1 1 1 1 */
+
+/* Determine if there is an onset transition between P and 1F. */
+/* OT (Onset Transition) is true if there is an onset between */
+/* P and 1F but not after 1F. */
+ ot = ((obound[1] & 2) != 0 || obound[2] == 1) && (obound[3] & 1) == 0;
+/* Multi-way dispatch on voicing decision history: */
+ vstate = (voibuf[3] << 3) + (voibuf[4] << 2) + (voibuf[5] << 1) + voibuf[
+ 6];
+ switch (vstate + 1) {
+ case 1: goto L99;
+ case 2: goto L1;
+ case 3: goto L2;
+ case 4: goto L99;
+ case 5: goto L4;
+ case 6: goto L5;
+ case 7: goto L6;
+ case 8: goto L7;
+ case 9: goto L8;
+ case 10: goto L99;
+ case 11: goto L10;
+ case 12: goto L11;
+ case 13: goto L99;
+ case 14: goto L13;
+ case 15: goto L14;
+ case 16: goto L99;
+ }
+L1:
+ if (ot && voibuf[7] == 1) {
+ voibuf[5] = 1;
+ }
+ goto L99;
+L2:
+ if (voibuf[7] == 0 || voice[2] < -voice[3]) {
+ voibuf[5] = 0;
+ } else {
+ voibuf[6] = 1;
+ }
+ goto L99;
+L4:
+ voibuf[4] = 0;
+ goto L99;
+L5:
+ if (voice[1] < -voice[2]) {
+ voibuf[4] = 0;
+ } else {
+ voibuf[5] = 1;
+ }
+ goto L99;
+/* VOIBUF(2,0) must be 0 */
+L6:
+ if (voibuf[1] == 1 || voibuf[7] == 1 || voice[3] > voice[0]) {
+ voibuf[6] = 1;
+ } else {
+ voibuf[3] = 1;
+ }
+ goto L99;
+L7:
+ if (ot) {
+ voibuf[4] = 0;
+ }
+ goto L99;
+L8:
+ if (ot) {
+ voibuf[4] = 1;
+ }
+ goto L99;
+L10:
+ if (voice[2] < -voice[1]) {
+ voibuf[5] = 0;
+ } else {
+ voibuf[4] = 1;
+ }
+ goto L99;
+L11:
+ voibuf[4] = 1;
+ goto L99;
+L13:
+ if (voibuf[7] == 0 && voice[3] < -voice[2]) {
+ voibuf[6] = 0;
+ } else {
+ voibuf[5] = 1;
+ }
+ goto L99;
+L14:
+ if (ot && voibuf[7] == 0) {
+ voibuf[5] = 0;
+ }
+/* GOTO 99 */
+L99:
+/* Now update parameters: */
+/* ---------------------- */
+
+/* During unvoiced half-frames, update the low band and full band unvoice
+d*/
+/* energy estimates (LBUE and FBUE) and also the zero crossing */
+/* threshold (DITHER). (The input to the unvoiced energy filters is */
+/* restricted to be less than 10dB above the previous inputs of the */
+/* filters.) */
+/* During voiced half-frames, update the low-pass (LBVE) and all-pass */
+/* (FBVE) voiced energy estimates. */
+ if (voibuf[*half + 6] == 0) {
+/* Computing MIN */
+ i__1 = fbe, i__2 = *ofbue * 3;
+ r__1 = (*sfbue * 63 + (min(i__1,i__2) << 3)) / 64.f;
+ *sfbue = i_nint(&r__1);
+ *fbue = *sfbue / 8;
+ *ofbue = fbe;
+/* Computing MIN */
+ i__1 = lbe, i__2 = *olbue * 3;
+ r__1 = (*slbue * 63 + (min(i__1,i__2) << 3)) / 64.f;
+ *slbue = i_nint(&r__1);
+ *lbue = *slbue / 8;
+ *olbue = lbe;
+ } else {
+ r__1 = (*lbve * 63 + lbe) / 64.f;
+ *lbve = i_nint(&r__1);
+ r__1 = (*fbve * 63 + fbe) / 64.f;
+ *fbve = i_nint(&r__1);
+ }
+/* Set dither threshold to yield proper zero crossing rates in the */
+/* presence of low frequency noise and low level signal input. */
+/* NOTE: The divisor is a function of REF, the expected energies. */
+/* Computing MIN */
+/* Computing MAX */
+ r__2 = (real)(sqrt((real) (*lbue * *lbve)) * 64 / 3000);
+ r__1 = max(r__2,1.f);
+ *dither = min(r__1,20.f);
+/* Voicing decisions are returned in VOIBUF. */
+ return 0;
+} /* voicin_ */
diff --git a/trunk/codecs/lpc10/vparms.c b/trunk/codecs/lpc10/vparms.c
new file mode 100644
index 000000000..c75b1b17d
--- /dev/null
+++ b/trunk/codecs/lpc10/vparms.c
@@ -0,0 +1,255 @@
+/*
+
+$Log$
+Revision 1.15 2004/06/26 03:50:14 markster
+Merge source cleanups (bug #1911)
+
+Revision 1.14 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+mer feb 12 14:56:57 CET 2003
+
+Revision 1.2 2000/01/05 08:20:40 markster
+Some OSS fixes and a few lpc changes to make it actually work
+
+ * Revision 1.1 1996/08/19 22:30:04 jaf
+ * Initial revision
+ *
+
+*/
+
+/* -- translated by f2c (version 19951025).
+ You must link the resulting object file with the libraries:
+ -lf2c -lm (in that order)
+*/
+
+#include "f2c.h"
+
+#ifdef P_R_O_T_O_T_Y_P_E_S
+extern int vparms_(integer *vwin, real *inbuf, real *lpbuf, integer *buflim, integer *half, real *dither, integer *mintau, integer *zc, integer *lbe, integer *fbe, real *qs, real *rc1, real *ar_b__, real *ar_f__);
+#endif
+
+/* Table of constant values */
+
+static real c_b2 = 1.f;
+
+/* ********************************************************************* */
+
+/* VPARMS Version 50 */
+
+/* $Log$
+ * Revision 1.15 2004/06/26 03:50:14 markster
+ * Merge source cleanups (bug #1911)
+ *
+ * Revision 1.14 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.1.1.1 2003/02/12 13:59:15 matteo
+ * mer feb 12 14:56:57 CET 2003
+ *
+ * Revision 1.2 2000/01/05 08:20:40 markster
+ * Some OSS fixes and a few lpc changes to make it actually work
+ *
+ * Revision 1.1 1996/08/19 22:30:04 jaf
+ * Initial revision
+ * */
+/* Revision 1.6 1996/03/29 18:01:16 jaf */
+/* Added some more comments about the range of INBUF and LPBUF that can */
+/* be read. Note that it is possible for index VWIN(2)+1 to be read from */
+/* INBUF, which might be outside of its defined range, although that will */
+/* require more careful checking. */
+
+/* Revision 1.5 1996/03/19 00:02:02 jaf */
+/* I just noticed that the argument DITHER is modified inside of this */
+/* subroutine. Comments were added explaining the possible final values. */
+
+/* Revision 1.4 1996/03/18 22:22:59 jaf */
+/* Finishing the job I said I did with the last check-in comments. */
+
+/* Revision 1.3 1996/03/18 22:22:17 jaf */
+/* Just added a few comments about which array indices of the arguments */
+/* are used, and mentioning that this subroutine has no local state. */
+
+/* Revision 1.2 1996/03/13 15:02:58 jaf */
+/* Comments added explaining that none of the local variables of this */
+/* subroutine need to be saved from one invocation to the next. */
+
+/* Revision 1.1 1996/02/07 14:50:42 jaf */
+/* Initial revision */
+
+
+/* ********************************************************************* */
+
+/* Calculate voicing parameters: */
+
+/* Input: */
+/* VWIN - Voicing window limits */
+/* Indices 1 through 2 read. */
+/* INBUF - Input speech buffer */
+/* Indices START-1 through STOP read, */
+/* where START and STOP are defined in the code (only written once).
+*/
+/* Note that STOP can be as large as VWIN(2)+1 ! */
+/* LPBUF - Low pass filtered speech */
+/* Indices START-MINTAU through STOP+MINTAU read, */
+/* where START and STOP are defined in the code (only written once).
+*/
+/* BUFLIM - Array bounds for INBUF and LPBUF */
+/* Indices 1 through 4 read. */
+/* HALF - Half frame (1 or 2) */
+/* MINTAU - Lag corresponding to minimum AMDF value (pitch estimate) */
+/* Input/Output: */
+/* DITHER - Zero crossing threshold */
+/* The resulting value might be the negation of the input */
+/* value. It might always be the same as the input value, */
+/* if the DO loop below always executes an even number of times. */
+/* Output: (all of them are written on every call) */
+/* ZC - Zero crossing rate */
+/* LBE - Low band energy (sum of magnitudes - SM) */
+/* FBE - Full band energy (SM) */
+/* QS - Ratio of 6 dB/oct preemphasized energy to full band energy */
+/* RC1 - First reflection coefficient */
+/* AR_B - Product of the causal forward and reverse pitch */
+/* prediction gains */
+/* AR_F - Product of the noncausal forward and reverse pitch */
+/* prediction gains */
+/* Internal: */
+/* OLDSGN - Previous sign of dithered signal */
+/* VLEN - Length of voicing window */
+/* START - Lower address of current half of voicing window */
+/* STOP - Upper address of current half of voicing window */
+/* E_0 - Energy of LPF speech (sum of squares - SS) */
+/* E_B - Energy of LPF speech backward one pitch period (SS) */
+/* E_F - Energy of LPF speech forward one pitch period (SS) */
+/* R_B - Autocovariance of LPF speech backward one pitch period */
+/* R_F - Autocovariance of LPF speech forward one pitch period */
+/* LP_RMS - Energy of LPF speech (sum of magnitudes - SM) */
+/* AP_RMS - Energy of all-pass speech (SM) */
+/* E_PRE - Energy of 6dB preemphasized speech (SM) */
+/* E0AP - Energy of all-pass speech (SS) */
+
+/* This subroutine has no local state. */
+
+/* Subroutine */ int vparms_(integer *vwin, real *inbuf, real *lpbuf, integer
+ *buflim, integer *half, real *dither, integer *mintau, integer *zc,
+ integer *lbe, integer *fbe, real *qs, real *rc1, real *ar_b__, real *
+ ar_f__)
+{
+ /* System generated locals */
+ integer inbuf_offset, lpbuf_offset, i__1;
+ real r__1, r__2;
+
+ /* Builtin functions */
+ double r_sign(real *, real *);
+ integer i_nint(real *);
+
+ /* Local variables */
+ integer vlen, stop, i__;
+ real e_pre__;
+ integer start;
+ real ap_rms__, e_0__, oldsgn, lp_rms__, e_b__, e_f__, r_b__, r_f__, e0ap;
+
+/* Arguments */
+/* Local variables that need not be saved */
+/* Calculate zero crossings (ZC) and several energy and correlation */
+/* measures on low band and full band speech. Each measure is taken */
+/* over either the first or the second half of the voicing window, */
+/* depending on the variable HALF. */
+ /* Parameter adjustments */
+ --vwin;
+ --buflim;
+ lpbuf_offset = buflim[3];
+ lpbuf -= lpbuf_offset;
+ inbuf_offset = buflim[1];
+ inbuf -= inbuf_offset;
+
+ /* Function Body */
+ lp_rms__ = 0.f;
+ ap_rms__ = 0.f;
+ e_pre__ = 0.f;
+ e0ap = 0.f;
+ *rc1 = 0.f;
+ e_0__ = 0.f;
+ e_b__ = 0.f;
+ e_f__ = 0.f;
+ r_f__ = 0.f;
+ r_b__ = 0.f;
+ *zc = 0;
+ vlen = vwin[2] - vwin[1] + 1;
+ start = vwin[1] + (*half - 1) * vlen / 2 + 1;
+ stop = start + vlen / 2 - 1;
+
+/* I'll use the symbol HVL in the table below to represent the value */
+/* VLEN/2. Note that if VLEN is odd, then HVL should be rounded down, */
+/* i.e., HVL = (VLEN-1)/2. */
+
+/* HALF START STOP */
+
+/* 1 VWIN(1)+1 VWIN(1)+HVL */
+/* 2 VWIN(1)+HVL+1 VWIN(1)+2*HVL */
+
+/* Note that if VLEN is even and HALF is 2, then STOP will be */
+/* VWIN(1)+VLEN = VWIN(2)+1. That could be bad, if that index of INBUF */
+/* is undefined. */
+
+ r__1 = inbuf[start - 1] - *dither;
+ oldsgn = (real)r_sign(&c_b2, &r__1);
+ i__1 = stop;
+ for (i__ = start; i__ <= i__1; ++i__) {
+ lp_rms__ += (r__1 = lpbuf[i__], abs(r__1));
+ ap_rms__ += (r__1 = inbuf[i__], abs(r__1));
+ e_pre__ += (r__1 = inbuf[i__] - inbuf[i__ - 1], abs(r__1));
+/* Computing 2nd power */
+ r__1 = inbuf[i__];
+ e0ap += r__1 * r__1;
+ *rc1 += inbuf[i__] * inbuf[i__ - 1];
+/* Computing 2nd power */
+ r__1 = lpbuf[i__];
+ e_0__ += r__1 * r__1;
+/* Computing 2nd power */
+ r__1 = lpbuf[i__ - *mintau];
+ e_b__ += r__1 * r__1;
+/* Computing 2nd power */
+ r__1 = lpbuf[i__ + *mintau];
+ e_f__ += r__1 * r__1;
+ r_f__ += lpbuf[i__] * lpbuf[i__ + *mintau];
+ r_b__ += lpbuf[i__] * lpbuf[i__ - *mintau];
+ r__1 = inbuf[i__] + *dither;
+ if (r_sign(&c_b2, &r__1) != oldsgn) {
+ ++(*zc);
+ oldsgn = -oldsgn;
+ }
+ *dither = -(*dither);
+ }
+/* Normalized short-term autocovariance coefficient at unit sample delay
+ */
+ *rc1 /= max(e0ap,1.f);
+/* Ratio of the energy of the first difference signal (6 dB/oct preemphas
+is)*/
+/* to the energy of the full band signal */
+/* Computing MAX */
+ r__1 = ap_rms__ * 2.f;
+ *qs = e_pre__ / max(r__1,1.f);
+/* aR_b is the product of the forward and reverse prediction gains, */
+/* looking backward in time (the causal case). */
+ *ar_b__ = r_b__ / max(e_b__,1.f) * (r_b__ / max(e_0__,1.f));
+/* aR_f is the same as aR_b, but looking forward in time (non causal case
+).*/
+ *ar_f__ = r_f__ / max(e_f__,1.f) * (r_f__ / max(e_0__,1.f));
+/* Normalize ZC, LBE, and FBE to old fixed window length of 180. */
+/* (The fraction 90/VLEN has a range of .58 to 1) */
+ r__2 = (real) (*zc << 1);
+ r__1 = r__2 * (90.f / vlen);
+ *zc = i_nint(&r__1);
+/* Computing MIN */
+ r__1 = lp_rms__ / 4 * (90.f / vlen);
+ i__1 = i_nint(&r__1);
+ *lbe = min(i__1,32767);
+/* Computing MIN */
+ r__1 = ap_rms__ / 4 * (90.f / vlen);
+ i__1 = i_nint(&r__1);
+ *fbe = min(i__1,32767);
+ return 0;
+} /* vparms_ */
+
diff --git a/trunk/codecs/lpc10_slin_ex.h b/trunk/codecs/lpc10_slin_ex.h
new file mode 100644
index 000000000..cf9f05999
--- /dev/null
+++ b/trunk/codecs/lpc10_slin_ex.h
@@ -0,0 +1,13 @@
+/*! \file
+ * \brief 8-bit raw data
+ *
+ * Source: example.lpc10
+ *
+ * Copyright (C) 1999-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char lpc10_slin_ex[] = {
+0x1, 0x8, 0x31, 0x8, 0x31, 0x80, 0x30 };
diff --git a/trunk/codecs/slin_adpcm_ex.h b/trunk/codecs/slin_adpcm_ex.h
new file mode 100644
index 000000000..801549a3c
--- /dev/null
+++ b/trunk/codecs/slin_adpcm_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * \brief slin_adpcm_ex.h --
+ *
+ * Signed 16-bit audio data, 10 milliseconds worth at 8 kHz.
+ *
+ * Source: g723.example
+ *
+ * Copyright (C) 2001-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_adpcm_ex[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/trunk/codecs/slin_g722_ex.h b/trunk/codecs/slin_g722_ex.h
new file mode 100644
index 000000000..4e6aea84a
--- /dev/null
+++ b/trunk/codecs/slin_g722_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * \brief slin_g722_ex.h --
+ *
+ * Signed 16-bit audio data, 10 milliseconds worth at 8 kHz.
+ *
+ * Source: g723.example
+ *
+ * Copyright (C) 2001-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_g722_ex[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/trunk/codecs/slin_g726_ex.h b/trunk/codecs/slin_g726_ex.h
new file mode 100644
index 000000000..8dad39cbd
--- /dev/null
+++ b/trunk/codecs/slin_g726_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * \brief slin_adpcm_ex.h --
+ *
+ * Signed 16-bit audio data, 10 milliseconds worth at 8 kHz.
+ *
+ * Source: g726.example
+ *
+ * Copyright (C) 2001-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_g726_ex[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/trunk/codecs/slin_gsm_ex.h b/trunk/codecs/slin_gsm_ex.h
new file mode 100644
index 000000000..7ac281800
--- /dev/null
+++ b/trunk/codecs/slin_gsm_ex.h
@@ -0,0 +1,28 @@
+/*! \file
+ * \brief Signed 16-bit audio data
+ *
+ * Source: gsm.example
+ *
+ * Copyright (C) 1999-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_gsm_ex[] = {
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 0xfff8, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 0x0008, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+0x0008, 000000, 000000, 000000, 0xfff8, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 0x0008, 000000, 000000, 000000 };
diff --git a/trunk/codecs/slin_ilbc_ex.h b/trunk/codecs/slin_ilbc_ex.h
new file mode 100644
index 000000000..b89655c6b
--- /dev/null
+++ b/trunk/codecs/slin_ilbc_ex.h
@@ -0,0 +1,28 @@
+/*! \file
+ * \brief Signed 16-bit audio data
+ *
+ * Source: gsm.example
+ *
+ * Copyright (C) 1999-2005, Digium Inc
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_ilbc_ex[] = {
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 0xfff8, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 0x0008, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+0x0008, 000000, 000000, 000000, 0xfff8, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 0x0008, 000000, 000000, 000000 };
diff --git a/trunk/codecs/slin_lpc10_ex.h b/trunk/codecs/slin_lpc10_ex.h
new file mode 100644
index 000000000..169e9a9e1
--- /dev/null
+++ b/trunk/codecs/slin_lpc10_ex.h
@@ -0,0 +1,21 @@
+/*! \file
+ * \brief Signed 16-bit audio data
+ *
+ * Source: example.slin
+ *
+ * Copyright (C) 1999-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_lpc10_ex[] = {
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000 };
diff --git a/trunk/codecs/slin_resample_ex.h b/trunk/codecs/slin_resample_ex.h
new file mode 100644
index 000000000..701654e39
--- /dev/null
+++ b/trunk/codecs/slin_resample_ex.h
@@ -0,0 +1,43 @@
+/*! \file
+ * \brief slin_resample_ex.h --
+ *
+ * Copyright (C) 2007, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ */
+
+static signed short slin16_slin8_ex[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static signed short slin8_slin16_ex[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/trunk/codecs/slin_speex_ex.h b/trunk/codecs/slin_speex_ex.h
new file mode 100644
index 000000000..0c6258c37
--- /dev/null
+++ b/trunk/codecs/slin_speex_ex.h
@@ -0,0 +1,262 @@
+/*! \file
+ * \brief Signed 16-bit audio data, 500ms of speech at 8kHz to ensure no DTX triggered
+ *
+ * Source: speex.example
+ *
+ * Copyright (C) 1999-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_speex_ex[] = {
+0x4a00, 0xa700, 0x6d00, 0x4900, 0x5800, 0x3e00, 0x1b00, 0x1400, 0xf9ff, 0xe4ff, 0x0a00, 0xf0ff, 0xbbff, 0x0200, 0x0800, 0xd6ff,
+0xf1ff, 0x1200, 0x0500, 0x1000, 0x0f00, 0x0700, 0x4100, 0x2800, 0xf4ff, 0x2900, 0x3100, 0xf4ff, 0xefff, 0xdaff, 0xdaff, 0xc2ff,
+0xa5ff, 0xccff, 0xd4ff, 0xc7ff, 0xe6ff, 0x0600, 0x0c00, 0x1900, 0x1200, 0x0d00, 0x2900, 0x1e00, 0xf4ff, 0x0000, 0x1200, 0xf5ff,
+0xe5ff, 0x0000, 0x0000, 0xf1ff, 0x0300, 0x0b00, 0xfdff, 0x0500, 0x0f00, 0x0600, 0x0d00, 0x0f00, 0x0000, 0xfdff, 0xfcff, 0xe5ff,
+0xdcff, 0xe0ff, 0xafff, 0xb2ff, 0xe4ff, 0xceff, 0xe2ff, 0x0000, 0x1600, 0x2600, 0x2400, 0x3f00, 0x4a00, 0x3d00, 0x2b00, 0x2f00,
+0x4300, 0x1000, 0x0300, 0x0700, 0xe9ff, 0xf3ff, 0xebff, 0xd9ff, 0xe5ff, 0xf9ff, 0xeeff, 0xe9ff, 0x0500, 0x0a00, 0x0300, 0x0800,
+0x1800, 0x1c00, 0x1000, 0x1300, 0x0d00, 0x0400, 0x0200, 0xf9ff, 0xe4ff, 0xe6ff, 0xe0ff, 0xd9ff, 0xecff, 0xf2ff, 0xe8ff, 0xfdff,
+0x1100, 0x0d00, 0x0b00, 0x1100, 0x1100, 0x1200, 0x0c00, 0x0400, 0x0900, 0x0d00, 0x0100, 0xfdff, 0x0000, 0xfdff, 0x0000, 0xffff,
+0xf5ff, 0xfcff, 0x0600, 0xfeff, 0x0000, 0x0300, 0x1300, 0x1700, 0x0300, 0x0700, 0xf0ff, 0xe1ff, 0xd7ff, 0xd1ff, 0xc6ff, 0xc2ff,
+0xf2ff, 0xf0ff, 0xecff, 0x0700, 0x3200, 0x2d00, 0x0400, 0x2200, 0x2900, 0x1900, 0x2400, 0x0500, 0x1900, 0x1f00, 0xfeff, 0x0d00,
+0x0600, 0xebff, 0xf5ff, 0x0600, 0xebff, 0xe3ff, 0x0c00, 0x0d00, 0xefff, 0x1100, 0x3a00, 0x0700, 0x1700, 0x2f00, 0xf7ff, 0x0700,
+0x2600, 0xd9ff, 0xb8ff, 0xeaff, 0xbeff, 0x9eff, 0xc7ff, 0xccff, 0xc4ff, 0xeeff, 0x0000, 0xfdff, 0x1200, 0x1d00, 0x3100, 0x3600,
+0x2d00, 0x3c00, 0x4700, 0x3000, 0x2000, 0x2300, 0x1800, 0x0900, 0xffff, 0xeaff, 0xe7ff, 0xe8ff, 0xd3ff, 0xc5ff, 0xe1ff, 0xedff,
+0xe4ff, 0xfaff, 0xe3ff, 0xacff, 0xcdff, 0xd4ff, 0xabff, 0xa6ff, 0xdaff, 0x0500, 0xf1ff, 0xfaff, 0x3800, 0x7000, 0x4100, 0x1c00,
+0x4b00, 0x5300, 0x3a00, 0x2a00, 0x1400, 0x2000, 0x1c00, 0x1500, 0x0500, 0xf6ff, 0x0000, 0x1300, 0xfaff, 0xe9ff, 0x0000, 0x0a00,
+0xfcff, 0xfaff, 0x1000, 0x1200, 0x0900, 0x0c00, 0x0400, 0x0000, 0x0800, 0xffff, 0xe7ff, 0xe2ff, 0xe6ff, 0xd5ff, 0xdaff, 0xe0ff,
+0xdcff, 0xeeff, 0x0200, 0xfbff, 0x0000, 0x1000, 0x1100, 0x1500, 0x1900, 0x1400, 0x1900, 0x1c00, 0x1500, 0x1000, 0x0d00, 0x0b00,
+0x0700, 0x0200, 0xf6ff, 0xf5ff, 0xf9ff, 0xecff, 0xe8ff, 0xeeff, 0xf4ff, 0xf4ff, 0xeeff, 0xecff, 0xebff, 0xd7ff, 0xd8ff, 0xeaff,
+0xe0ff, 0xddff, 0x0100, 0x0500, 0xf8ff, 0x1800, 0x2d00, 0x1d00, 0x1a00, 0x2100, 0x1f00, 0x1f00, 0x1a00, 0x0f00, 0x0300, 0x0d00,
+0x0e00, 0xfeff, 0x0000, 0xfeff, 0x0000, 0x0000, 0xf1ff, 0xfaff, 0x0a00, 0x0100, 0xf5ff, 0x0300, 0x0e00, 0x0300, 0x0200, 0x0000,
+0x0800, 0x0700, 0xf8ff, 0xf5ff, 0xf1ff, 0xefff, 0xe9ff, 0xe0ff, 0xe0ff, 0xedff, 0xf0ff, 0xedff, 0xfcff, 0xffff, 0xfdff, 0x0a00,
+0x0f00, 0x0e00, 0x0f00, 0x1700, 0x1400, 0x0700, 0x0500, 0x0800, 0x0000, 0x0200, 0x0400, 0xfaff, 0xffff, 0x0200, 0xfaff, 0xfbff,
+0x0300, 0x0400, 0x0400, 0x0100, 0xe7ff, 0xd4ff, 0xdbff, 0xe2ff, 0xe0ff, 0xd0ff, 0xe7ff, 0x0700, 0xf2ff, 0x0000, 0x2d00, 0x2000,
+0x1800, 0x2c00, 0x1f00, 0x2600, 0x2200, 0x0e00, 0x0e00, 0x0000, 0x0400, 0x0800, 0x0300, 0xfbff, 0x0500, 0x1d00, 0x0000, 0xfbff,
+0x2e00, 0x2500, 0x0600, 0x1c00, 0x2600, 0x1600, 0x0e00, 0x0200, 0xf9ff, 0x0000, 0xe1ff, 0xc0ff, 0xd5ff, 0xc9ff, 0xb6ff, 0xbcff,
+0xccff, 0xc9ff, 0xcbff, 0xe5ff, 0xeaff, 0xf0ff, 0xfeff, 0x0000, 0x0b00, 0x1f00, 0x2000, 0x1b00, 0x2000, 0x2400, 0x1800, 0x1200,
+0x1700, 0x0c00, 0x0b00, 0x0d00, 0xfdff, 0xf5ff, 0x0000, 0xf9ff, 0xefff, 0xfaff, 0xf7ff, 0xf0ff, 0xe7ff, 0xcbff, 0xd1ff, 0xedff,
+0xf5ff, 0xfcff, 0xfeff, 0x1e00, 0x3200, 0x2200, 0x3800, 0x4f00, 0x3800, 0x2c00, 0x4000, 0x3e00, 0x2e00, 0x2900, 0x1200, 0x0500,
+0x0700, 0x0000, 0xf2ff, 0xf2ff, 0xf8ff, 0xf5ff, 0xf7ff, 0xeaff, 0xefff, 0x0100, 0xfcff, 0xf9ff, 0x0000, 0x0700, 0xf9ff, 0xf2ff,
+0xf9ff, 0xf0ff, 0xf1ff, 0xe7ff, 0xddff, 0xe1ff, 0xddff, 0xd9ff, 0xdaff, 0xe1ff, 0xe2ff, 0xe5ff, 0xe5ff, 0xf0ff, 0xfaff, 0xf4ff,
+0xfdff, 0x1100, 0x0e00, 0x0e00, 0x1800, 0x1600, 0x1200, 0x1400, 0x1500, 0x1200, 0x0b00, 0x0600, 0x0c00, 0x0500, 0x0100, 0x0b00,
+0x0200, 0xfaff, 0xffff, 0xf9ff, 0xe8ff, 0xd0ff, 0xc9ff, 0xd5ff, 0xe0ff, 0xe6ff, 0xedff, 0xf5ff, 0x1100, 0x1300, 0x1f00, 0x3600,
+0x2900, 0x2200, 0x2c00, 0x3700, 0x2d00, 0x2000, 0x1a00, 0x0800, 0xfaff, 0xfcff, 0xf6ff, 0xf2ff, 0xebff, 0xf7ff, 0x0200, 0xf1ff,
+0xf5ff, 0x0d00, 0x0900, 0xf9ff, 0x1000, 0x2000, 0x0a00, 0xf9ff, 0xffff, 0xf9ff, 0xf2ff, 0xeeff, 0xdbff, 0xd8ff, 0xdbff, 0xdaff,
+0xdaff, 0xdfff, 0xe6ff, 0xecff, 0xedff, 0xf0ff, 0x0500, 0x0900, 0x0400, 0x0f00, 0x2600, 0x2700, 0x2200, 0x2b00, 0x1700, 0x1100,
+0x2a00, 0x2200, 0x0c00, 0x0200, 0x0100, 0x0200, 0xfeff, 0xfaff, 0xf7ff, 0xf3ff, 0xf1ff, 0xf5ff, 0xf6ff, 0xe8ff, 0xd3ff, 0xcdff,
+0xdbff, 0xebff, 0xedff, 0xf0ff, 0xfbff, 0x1400, 0x1700, 0x2000, 0x3400, 0x2700, 0x1a00, 0x2100, 0x2e00, 0x2900, 0x1e00, 0x0c00,
+0xfbff, 0xfbff, 0xf9ff, 0xf3ff, 0xf4ff, 0xe8ff, 0xecff, 0x0300, 0xf8ff, 0xf4ff, 0x0100, 0x0600, 0xf7ff, 0x0900, 0x1e00, 0x0f00,
+0x0900, 0x0700, 0x0000, 0x0000, 0x0000, 0xf5ff, 0xebff, 0xefff, 0xf3ff, 0xeeff, 0xf0ff, 0xf1ff, 0xefff, 0xf0ff, 0xf6ff, 0xfcff,
+0xf9ff, 0xf8ff, 0xfbff, 0x0300, 0x0900, 0x0800, 0x0900, 0x0100, 0x0200, 0x0c00, 0x0e00, 0x0300, 0xfdff, 0xfcff, 0xfbff, 0xfbff,
+0xf9ff, 0xf3ff, 0xf3ff, 0xecff, 0xf6ff, 0xfbff, 0xf4ff, 0xecff, 0xd6ff, 0xdcff, 0xf1ff, 0xf1ff, 0xeeff, 0xf6ff, 0x0600, 0x0a00,
+0x1000, 0x1e00, 0x1b00, 0x1100, 0x1800, 0x2800, 0x2500, 0x2600, 0x1c00, 0x0e00, 0x0900, 0x0e00, 0x0800, 0x0200, 0xfaff, 0xf5ff,
+0x0300, 0xf8ff, 0xfcff, 0x0500, 0x0500, 0x0000, 0xfaff, 0x0b00, 0x1c00, 0x0d00, 0xfdff, 0x0200, 0x0100, 0xfbff, 0xf5ff, 0xe8ff,
+0xe8ff, 0xecff, 0xdaff, 0xd1ff, 0xddff, 0xdeff, 0xd8ff, 0xdeff, 0xedff, 0xf3ff, 0xf8ff, 0x0100, 0x1100, 0x2300, 0x2800, 0x3100,
+0x3200, 0x3500, 0x3a00, 0x3e00, 0x3400, 0x2700, 0x2100, 0x1400, 0x0900, 0xf7ff, 0xfaff, 0xf3ff, 0xe2ff, 0xeaff, 0xe3ff, 0xdfff,
+0xd7ff, 0xc9ff, 0xdaff, 0xe4ff, 0xe0ff, 0xe4ff, 0xefff, 0xf4ff, 0xf6ff, 0x0000, 0x0100, 0x0000, 0x0600, 0x0c00, 0x0c00, 0x1000,
+0x1900, 0x1600, 0x1500, 0x1600, 0x1100, 0x1400, 0x1200, 0x1300, 0x0900, 0x0d00, 0x1400, 0x0e00, 0x1900, 0x1400, 0x0900, 0x0600,
+0x1600, 0x1900, 0x0800, 0x0300, 0x0000, 0xf5ff, 0xedff, 0xeaff, 0xdbff, 0xdcff, 0xcbff, 0xb7ff, 0xbfff, 0xc3ff, 0xc2ff, 0xbfff,
+0xcbff, 0xdaff, 0xe7ff, 0xf9ff, 0x0600, 0x1b00, 0x2c00, 0x2900, 0x3d00, 0x4b00, 0x4700, 0x4b00, 0x4a00, 0x4300, 0x3700, 0x3300,
+0x1f00, 0x1400, 0x1000, 0x0f00, 0x0c00, 0x0000, 0xf6ff, 0xe7ff, 0xe7ff, 0xdeff, 0xd3ff, 0xe1ff, 0xdeff, 0xd9ff, 0xe0ff, 0xe8ff,
+0xf1ff, 0xeaff, 0xe8ff, 0xf5ff, 0xf7ff, 0xf6ff, 0xf7ff, 0xf4ff, 0xf8ff, 0xfcff, 0x0100, 0xfcff, 0xf7ff, 0x0100, 0x0600, 0x0100,
+0x0700, 0x0200, 0x0c00, 0x1000, 0x0e00, 0x1c00, 0x1a00, 0x1a00, 0x1800, 0x2200, 0x1f00, 0x1600, 0x1700, 0x0b00, 0xfbff, 0xfcff,
+0xeaff, 0xd6ff, 0xdfff, 0xcbff, 0xc0ff, 0xc0ff, 0xbcff, 0xc1ff, 0xc5ff, 0xd1ff, 0xdaff, 0xecff, 0xffff, 0xfeff, 0x1000, 0x2700,
+0x2400, 0x2400, 0x3800, 0x3600, 0x3100, 0x3300, 0x2800, 0x2000, 0x1e00, 0x1d00, 0x0800, 0x0400, 0x1600, 0x0900, 0x0000, 0x0d00,
+0x0100, 0xefff, 0xf1ff, 0xedff, 0xe0ff, 0xe6ff, 0xf5ff, 0xe1ff, 0xdaff, 0xf3ff, 0xf6ff, 0xeeff, 0xf1ff, 0xf9ff, 0xfbff, 0x0000,
+0x0700, 0x0900, 0x0d00, 0x1c00, 0x1d00, 0x1b00, 0x1a00, 0x2500, 0x2500, 0x1a00, 0x1600, 0x0e00, 0x0c00, 0x0b00, 0x0500, 0x0100,
+0x0200, 0x0000, 0xfbff, 0xfcff, 0xfdff, 0xfcff, 0xfcff, 0xf9ff, 0xf6ff, 0xf3ff, 0xeaff, 0xe6ff, 0xecff, 0xe8ff, 0xdfff, 0xe6ff,
+0xddff, 0xdaff, 0xecff, 0xefff, 0xf2ff, 0xfdff, 0x0100, 0xffff, 0x0800, 0x1700, 0x1000, 0x1300, 0x2100, 0x1b00, 0x1800, 0x1a00,
+0x1200, 0x1000, 0x2000, 0x1100, 0x0000, 0x0700, 0x0500, 0xfdff, 0xfcff, 0x0300, 0xf2ff, 0xeaff, 0xe1ff, 0xcdff, 0xd2ff, 0xe0ff,
+0xe0ff, 0xd5ff, 0xdfff, 0xf3ff, 0xf7ff, 0xfcff, 0x1100, 0x1800, 0x1900, 0x2100, 0x2600, 0x2800, 0x2900, 0x2d00, 0x2400, 0x1b00,
+0x1a00, 0x1000, 0x0800, 0x0500, 0xfcff, 0xf1ff, 0xf4ff, 0xf6ff, 0xf2ff, 0xf6ff, 0xffff, 0xffff, 0x0000, 0x0a00, 0x0a00, 0xfbff,
+0x0200, 0x0700, 0xf9ff, 0xf2ff, 0xeaff, 0xedff, 0xdfff, 0xd9ff, 0xe3ff, 0xd8ff, 0xdbff, 0xddff, 0xe2ff, 0xefff, 0xf6ff, 0xffff,
+0x0000, 0x0d00, 0x1500, 0x1600, 0x2200, 0x2800, 0x2700, 0x2900, 0x3700, 0x2d00, 0x2500, 0x2c00, 0x2900, 0x1100, 0x0600, 0xfdff,
+0xedff, 0xeaff, 0xddff, 0xcfff, 0xc9ff, 0xc0ff, 0xa8ff, 0xa8ff, 0xbeff, 0xceff, 0xd6ff, 0xe1ff, 0xf4ff, 0x1000, 0x1100, 0x1e00,
+0x3e00, 0x3c00, 0x3c00, 0x3d00, 0x3e00, 0x3300, 0x3000, 0x2b00, 0x1300, 0x0b00, 0x0600, 0xfaff, 0xf6ff, 0xf4ff, 0xe9ff, 0xedff,
+0x0000, 0xf8ff, 0xfbff, 0x0400, 0x0300, 0x0a00, 0x1400, 0x1300, 0x0100, 0x0b00, 0x0400, 0xf9ff, 0xf8ff, 0xeeff, 0xe9ff, 0xdeff,
+0xe1ff, 0xdaff, 0xd7ff, 0xe5ff, 0xd2ff, 0xd3ff, 0xe3ff, 0xe7ff, 0xedff, 0xf5ff, 0x0000, 0x0600, 0x1300, 0x1f00, 0x1c00, 0x2600,
+0x2b00, 0x3100, 0x2d00, 0x3100, 0x2e00, 0x2600, 0x1900, 0x1100, 0x0400, 0xf3ff, 0xf3ff, 0xdcff, 0xdbff, 0xddff, 0xd2ff, 0xccff,
+0xbbff, 0xc3ff, 0xe4ff, 0xf4ff, 0xf6ff, 0xfbff, 0x1600, 0x2100, 0x1600, 0x2000, 0x2500, 0x1b00, 0x2700, 0x2100, 0x1400, 0x0d00,
+0x1300, 0x0800, 0xfbff, 0xf8ff, 0xecff, 0xeeff, 0x0000, 0xefff, 0xeeff, 0xfcff, 0x0000, 0xfeff, 0x0600, 0x0700, 0x0200, 0x1400,
+0x1600, 0x0700, 0x0d00, 0x1400, 0x0e00, 0x0900, 0x0b00, 0x0500, 0xfaff, 0x0600, 0x0000, 0xf2ff, 0xfaff, 0xf6ff, 0xe9ff, 0xecff,
+0xecff, 0xe2ff, 0xe7ff, 0xf3ff, 0xf6ff, 0xf4ff, 0x0000, 0x0000, 0xffff, 0x1400, 0x0f00, 0x0f00, 0x1500, 0x1400, 0x1100, 0x1200,
+0x0c00, 0x0400, 0x0500, 0xffff, 0x0200, 0xfbff, 0xf5ff, 0xfeff, 0xf5ff, 0xe5ff, 0xe3ff, 0xe4ff, 0xe6ff, 0x0a00, 0xffff, 0xf3ff,
+0x0900, 0x0d00, 0xfbff, 0x0700, 0x0c00, 0xffff, 0x1200, 0x1e00, 0xfdff, 0x0700, 0x1c00, 0x1200, 0x0f00, 0x1200, 0x1500, 0xfbff,
+0x1200, 0x2300, 0xf9ff, 0x0300, 0x0d00, 0xf2ff, 0xf2ff, 0xf5ff, 0xd8ff, 0xd0ff, 0xddff, 0xd8ff, 0xc4ff, 0xdbff, 0xe0ff, 0xd6ff,
+0xecff, 0xd8ff, 0xf7ff, 0x1100, 0xffff, 0x1500, 0x2a00, 0x1c00, 0x1800, 0x2200, 0x1500, 0x0800, 0x1d00, 0x1600, 0x0800, 0x1300,
+0x0500, 0xecff, 0x1100, 0x0a00, 0x1700, 0x0900, 0xffff, 0x2700, 0x1b00, 0x0b00, 0x1400, 0x1200, 0xecff, 0x1000, 0x1800, 0xdfff,
+0xfbff, 0xf1ff, 0x9bff, 0x90ff, 0x97ff, 0x81ff, 0xf2ff, 0xf9ff, 0x8cff, 0xcbff, 0x3300, 0x0200, 0x1300, 0x4f00, 0x1600, 0x2700,
+0x7400, 0x4000, 0x2a00, 0x4e00, 0x4b00, 0x1800, 0x2b00, 0x3900, 0x1c00, 0x2500, 0x1000, 0xf1ff, 0xf9ff, 0xf5ff, 0xdbff, 0xdbff,
+0x0000, 0x0b00, 0xd7ff, 0xcbff, 0xecff, 0xdfff, 0xebff, 0xe5ff, 0xafff, 0xdfff, 0xe5ff, 0xbeff, 0xc9ff, 0x0000, 0xe0ff, 0xcdff,
+0x1d00, 0x1b00, 0xefff, 0x1c00, 0x2100, 0x1100, 0x3200, 0x3400, 0x1d00, 0x2d00, 0x2600, 0x3b00, 0x3600, 0x4200, 0x0a00, 0x0000,
+0x5f00, 0x4000, 0x0000, 0x1000, 0x2800, 0xeaff, 0xd9ff, 0xcdff, 0xf8ff, 0x8400, 0xc1ff, 0xbefe, 0x56ff, 0x7fff, 0xe5fe, 0xf5ff,
+0x3600, 0x4aff, 0x24ff, 0x6800, 0xdf00, 0x1600, 0x6d00, 0x7b00, 0xf200, 0xd500, 0x0700, 0x2300, 0x3500, 0x0600, 0xeaff, 0xd1ff,
+0x95ff, 0xd5ff, 0x0300, 0xbcff, 0xe0ff, 0x0000, 0x2500, 0x3100, 0xdf00, 0x6700, 0x0300, 0x6900, 0x2a00, 0x3f00, 0x0400, 0x7dff,
+0x58ff, 0xb2ff, 0x7bff, 0x71ff, 0xa1ff, 0x7dff, 0xd0ff, 0x1700, 0x0000, 0x0700, 0x5900, 0x4b00, 0x4b00, 0x4700, 0xf8ff, 0x4300,
+0x5e00, 0x0d00, 0xdaff, 0x1300, 0x0e00, 0x0f00, 0x2d00, 0xeaff, 0x1400, 0x6200, 0x3a00, 0xe3ff, 0x0c00, 0x1600, 0xfbff, 0xe8ff,
+0xfbff, 0x6700, 0xe3ff, 0x8eff, 0xf2fe, 0x25fe, 0x9bfe, 0x54ff, 0x94ff, 0x94ff, 0x7dff, 0x3000, 0x9401, 0xce01, 0x5601, 0x0e01,
+0xd300, 0x4f01, 0xdf00, 0x6fff, 0x25ff, 0x79ff, 0x5eff, 0x51ff, 0xd4ff, 0xbaff, 0x1b00, 0xbe00, 0x4a00, 0xf3ff, 0x2800, 0x4500,
+0x5e00, 0x1800, 0x84ff, 0x8aff, 0xc7ff, 0xc2ff, 0x8aff, 0xd7fe, 0x16ff, 0x0000, 0x1300, 0xf7ff, 0xbcff, 0xc3ff, 0x5500, 0x7c00,
+0x5a00, 0x5400, 0x2d00, 0x2d00, 0x2b00, 0xf9ff, 0xfcff, 0x0500, 0xddff, 0x1d00, 0x6600, 0x7a00, 0x9200, 0x3700, 0x0e00, 0x5600,
+0x2500, 0xcfff, 0xfdff, 0xe3ff, 0xc5ff, 0xcdff, 0x00ff, 0x67ff, 0xb100, 0x3200, 0x3200, 0xc800, 0xcdff, 0xaaff, 0x4dff, 0x42fd,
+0xc4fd, 0x3500, 0x2a00, 0x99ff, 0x1d00, 0xe500, 0x4e02, 0xf802, 0x6f01, 0xd4ff, 0x4f00, 0x7300, 0x88ff, 0x20ff, 0x81fe, 0x9afe,
+0xb2ff, 0x6800, 0x9100, 0x9700, 0x3001, 0x5500, 0xf4ff, 0xa400, 0xb2ff, 0xdeff, 0x1500, 0x05ff, 0x4fff, 0xe6ff, 0xaaff, 0xc9ff,
+0x0000, 0xafff, 0xeeff, 0x8200, 0x1800, 0xc7ff, 0xf1ff, 0xd5ff, 0x3000, 0x1d00, 0x50ff, 0x89ff, 0x82ff, 0x7cff, 0x2000, 0x1d00,
+0x0d00, 0x9500, 0x8c00, 0x2d00, 0x5b00, 0x2200, 0xf8ff, 0x6700, 0x7800, 0x3e00, 0xa800, 0x9100, 0xbfff, 0xccff, 0xdaff, 0x9bff,
+0x8400, 0x98ff, 0xa4fe, 0xddff, 0x1200, 0x2e00, 0xf100, 0x3500, 0x73ff, 0x99ff, 0xd1fd, 0x97fd, 0x0500, 0x7800, 0x3400, 0xc700,
+0xfc00, 0x8201, 0x6002, 0x8201, 0xb5ff, 0xaeff, 0xa5ff, 0x15ff, 0x90ff, 0x53ff, 0xccfe, 0x8eff, 0x9d00, 0xef00, 0x0601, 0xf300,
+0xa4ff, 0x5cff, 0x0b00, 0xfcff, 0x0d00, 0xd9ff, 0x3cff, 0x60ff, 0x5400, 0x3400, 0x2b00, 0x4800, 0xbbff, 0x0300, 0x5a00, 0xf5ff,
+0xc7ff, 0xe5ff, 0x91ff, 0xfeff, 0x0000, 0x51ff, 0xa8ff, 0x57ff, 0x74ff, 0x3a00, 0x6500, 0x5400, 0x0e00, 0x2900, 0x3800, 0x3d00,
+0x3300, 0x1100, 0x4400, 0x7200, 0x5c00, 0x6f00, 0x4c00, 0xc8ff, 0xa6ff, 0xc4ff, 0x9bff, 0x6a00, 0x1000, 0xccfe, 0xc6ff, 0x7e00,
+0x8aff, 0x6b00, 0xae00, 0x7bff, 0x71ff, 0xa0fd, 0x05fd, 0xc5ff, 0x5001, 0x1801, 0x9a01, 0x3f01, 0x1701, 0xc102, 0xd901, 0x83ff,
+0x35ff, 0xa7fe, 0x52fe, 0x98ff, 0x9eff, 0xcbfe, 0x90ff, 0xe100, 0x4801, 0xa001, 0x5101, 0x59ff, 0x04ff, 0xc3ff, 0x1800, 0x4400,
+0xe7ff, 0x22ff, 0x1dff, 0x4000, 0x2c00, 0x4b00, 0x7800, 0xa3ff, 0xd9ff, 0x7b00, 0x1a00, 0xb7ff, 0xb4ff, 0x70ff, 0xb6ff, 0x2200,
+0xc9ff, 0x35ff, 0xaeff, 0x2b00, 0x4700, 0x5100, 0xe6ff, 0x1100, 0x5900, 0x0500, 0xf0ff, 0x2300, 0x5b00, 0xbd00, 0xec00, 0xc500,
+0x8200, 0x93ff, 0x39ff, 0xafff, 0xbfff, 0x5200, 0x9cff, 0x49fe, 0x7fff, 0xa600, 0xfcff, 0xaa00, 0x7000, 0xf8fe, 0x64fe, 0xf7fc,
+0x73fd, 0x2100, 0xa001, 0xe501, 0x4a02, 0x3701, 0xe500, 0x7402, 0x6601, 0xb5ff, 0x5eff, 0x69fe, 0x40fe, 0xc8ff, 0xe6ff, 0x1cff,
+0xd1ff, 0xc900, 0x6601, 0xdf01, 0xf300, 0x67ff, 0xdcfe, 0x50ff, 0x2500, 0x7400, 0x0900, 0x42ff, 0x32ff, 0xe2ff, 0x2e00, 0x8700,
+0x2800, 0x84ff, 0xd4ff, 0x5a00, 0x1000, 0xc1ff, 0xafff, 0x62ff, 0xa7ff, 0x2700, 0x1100, 0xc5ff, 0xe2ff, 0xeeff, 0xb8ff, 0x3200,
+0x8b00, 0x0d00, 0x2600, 0x7b00, 0xf4ff, 0x6dff, 0x3d00, 0x9600, 0x8800, 0x2201, 0xa500, 0xf1ff, 0xe7ff, 0x9fff, 0x70ff, 0xe9ff,
+0x3a00, 0x5fff, 0xe8fe, 0x52ff, 0x0f00, 0x8e00, 0xb300, 0x3800, 0x1aff, 0x77fe, 0x13fd, 0x9afc, 0x6dff, 0xe301, 0x5602, 0x9902,
+0x3b01, 0xa6ff, 0x1501, 0x1a02, 0xa300, 0xebff, 0x1aff, 0xe6fd, 0x09ff, 0x6700, 0x5800, 0xefff, 0x4500, 0xd100, 0x6601, 0x5301,
+0x5600, 0x0eff, 0x9dfe, 0x91ff, 0x2400, 0x8500, 0xd7ff, 0xeffe, 0x39ff, 0xddff, 0x8c00, 0xa500, 0x0c00, 0x86ff, 0xd0ff, 0x2f00,
+0x1200, 0xf1ff, 0xbfff, 0x88ff, 0xd1ff, 0x4900, 0x2100, 0xbcff, 0xb1ff, 0x9aff, 0xf1ff, 0x8300, 0x6400, 0x3b00, 0x3e00, 0x2400,
+0xa3ff, 0x0000, 0x8200, 0x4b00, 0xa800, 0xe600, 0x0300, 0x2cff, 0x82ff, 0x0b00, 0x4300, 0x5600, 0xa9ff, 0xcdfe, 0x15ff, 0x6700,
+0xed00, 0x8a00, 0xe3ff, 0x74fe, 0xe3fc, 0xd1fc, 0x98fe, 0x2601, 0x7b02, 0x8d02, 0x8001, 0x3f00, 0xd300, 0x9501, 0xfe00, 0xeeff,
+0x50ff, 0x67fe, 0x81fe, 0xe8ff, 0x4900, 0x1200, 0x6800, 0xc000, 0xc200, 0x0c01, 0xb100, 0x7bff, 0x2fff, 0x91ff, 0xd6ff, 0xfeff,
+0xfeff, 0x63ff, 0x35ff, 0x0700, 0x7100, 0x6600, 0x4400, 0xceff, 0x9cff, 0x1100, 0x4500, 0xdfff, 0xabff, 0x96ff, 0x96ff, 0x1000,
+0x5b00, 0x0f00, 0xc2ff, 0xe3ff, 0xfdff, 0x3a00, 0x9000, 0x4c00, 0x1400, 0xe6ff, 0xe2ff, 0xc0ff, 0xf3ff, 0x4300, 0x1a00, 0x5100,
+0x7300, 0x3100, 0xc2ff, 0xebff, 0x5a00, 0x1e00, 0xafff, 0x3aff, 0x3cff, 0x80ff, 0x5a00, 0xf500, 0xffff, 0x57ff, 0x39ff, 0xe8fd,
+0x65fd, 0x65ff, 0x7d01, 0xfb01, 0x5b02, 0x7501, 0x9aff, 0x6000, 0x7701, 0x6b00, 0x94ff, 0x31ff, 0xe2fd, 0x85fe, 0x9300, 0xab00,
+0x4900, 0x9300, 0x5900, 0x3b00, 0x0701, 0x9000, 0x47ff, 0x4eff, 0x6cff, 0xedff, 0x5c00, 0x0000, 0x33ff, 0x81ff, 0x6200, 0x7400,
+0x9b00, 0x3400, 0x67ff, 0x94ff, 0x3100, 0x2800, 0xfeff, 0xfdff, 0xa3ff, 0x9fff, 0x2100, 0x2400, 0xebff, 0xf5ff, 0xe4ff, 0xe0ff,
+0x3f00, 0x5100, 0x1600, 0x1a00, 0xe7ff, 0xe0ff, 0xd4ff, 0x0000, 0x1200, 0x0500, 0x5b00, 0xc200, 0x5000, 0xcbff, 0x3b00, 0xe5ff,
+0x9fff, 0xf0ff, 0xd4ff, 0x76ff, 0x40ff, 0x2200, 0x8c00, 0x0f00, 0x5700, 0x5fff, 0xdffc, 0xfbfc, 0xe8ff, 0xc501, 0xa202, 0xbc02,
+0x6300, 0x49ff, 0x0b01, 0x7001, 0x6f00, 0xe2ff, 0x72fe, 0x84fd, 0x7dff, 0xcc00, 0x7300, 0x7c00, 0x5e00, 0xf1ff, 0xa200, 0x2101,
+0xd4ff, 0x46ff, 0x5eff, 0x82ff, 0x4800, 0x7d00, 0xd2ff, 0x71ff, 0xe3ff, 0x1b00, 0x7b00, 0x5c00, 0xc4ff, 0x84ff, 0x76ff, 0xe5ff,
+0x2b00, 0x3500, 0x1500, 0xe2ff, 0x99ff, 0x73ff, 0x9bff, 0xc7ff, 0xc8ff, 0x0000, 0x2d00, 0x4100, 0x3400, 0x2600, 0xf1ff, 0xd6ff,
+0x0900, 0x1300, 0xe0ff, 0x5c00, 0xbc00, 0x4700, 0x2500, 0xd5ff, 0x5eff, 0xb5ff, 0x4800, 0x3200, 0x0a00, 0x5bff, 0x91ff, 0x9b00,
+0x7000, 0x6400, 0x0800, 0xa7fd, 0x41fc, 0x8dfe, 0x3c01, 0x0102, 0x4b03, 0x0002, 0x58ff, 0x8b00, 0xc401, 0xad00, 0x5c00, 0x7aff,
+0x8efd, 0x85fe, 0x5600, 0x4000, 0x5700, 0xd200, 0xa700, 0xd400, 0xbb01, 0x7500, 0x1bff, 0x11ff, 0x47ff, 0x0700, 0x7500, 0x3c00,
+0x67ff, 0x05ff, 0x91ff, 0x4300, 0x6700, 0x3300, 0x1200, 0x94ff, 0x8dff, 0x1e00, 0xd6ff, 0x98ff, 0xafff, 0x9bff, 0x55ff, 0x97ff,
+0xaeff, 0x86ff, 0xc1ff, 0x2f00, 0x5200, 0x7100, 0x7600, 0xfdff, 0x9fff, 0xc3ff, 0xe5ff, 0xd9ff, 0x1500, 0x7e00, 0x3c00, 0x8200,
+0x5900, 0x60ff, 0x8cff, 0x1e00, 0x3400, 0x4300, 0x3800, 0x1dff, 0xbffe, 0xd2ff, 0xcf00, 0xff00, 0x5c00, 0x36ff, 0xd4fd, 0x85fc,
+0x18fe, 0xa301, 0x2e02, 0xfc01, 0x9d02, 0xf400, 0x4400, 0xbf01, 0xea00, 0x40ff, 0x83ff, 0x05ff, 0x5afe, 0xc5ff, 0x5100, 0xa4ff,
+0x8f00, 0x8501, 0x2501, 0x2e01, 0xd300, 0x21ff, 0xe0fe, 0xd1ff, 0x0400, 0x5a00, 0xd2ff, 0x62ff, 0xcfff, 0xc0ff, 0xfbff, 0x2b00,
+0xf9ff, 0xd6ff, 0xf7ff, 0xfbff, 0xb7ff, 0xc6ff, 0x2eff, 0x52ff, 0x3900, 0x1500, 0xc2ff, 0xf0ff, 0xc6ff, 0xccff, 0x7f00, 0x9500,
+0x0200, 0x0b00, 0x0000, 0x98ff, 0xa3ff, 0xb9ff, 0xacff, 0x4600, 0x8a00, 0x9300, 0x1300, 0x94ff, 0xd4ff, 0x2a00, 0x1301, 0xce00,
+0x78ff, 0xa7fe, 0xa1fe, 0x76ff, 0xe200, 0xfe00, 0xecff, 0xadff, 0x6fff, 0xe4fd, 0x2afd, 0x51ff, 0x3f01, 0x8c01, 0x9202, 0xac01,
+0xf4ff, 0x0d01, 0x7601, 0x0800, 0xd5ff, 0xafff, 0x9cfe, 0x2dff, 0x2200, 0x85ff, 0xbdff, 0xd500, 0xc000, 0xd300, 0x2701, 0x0700,
+0x12ff, 0xb6ff, 0xf5ff, 0xebff, 0x7900, 0xc7ff, 0x4dff, 0x5c00, 0x9800, 0xf9ff, 0xa4ff, 0xa4ff, 0x6bff, 0xafff, 0x2a00, 0xceff,
+0xc2ff, 0xc6ff, 0xb9ff, 0x3600, 0x5a00, 0xdbff, 0xa1ff, 0xe1ff, 0x1500, 0x3e00, 0x5d00, 0x0400, 0xccff, 0x2000, 0x1900, 0xa7ff,
+0x77ff, 0x9dff, 0xfdff, 0x4a00, 0xcd00, 0x0300, 0x8bff, 0x0000, 0xf0ff, 0xa100, 0xbf00, 0xdaff, 0x04ff, 0x2aff, 0x76ff, 0xe1ff,
+0x8b00, 0x8100, 0x3700, 0x4700, 0x2fff, 0x1ffd, 0xd7fd, 0x6a00, 0x6c01, 0x9301, 0x9c01, 0x5900, 0x3c00, 0x9f01, 0x6201, 0xbfff,
+0x28ff, 0x69ff, 0x63ff, 0xf5ff, 0x0a00, 0x2cff, 0x91ff, 0x9e00, 0x1c01, 0x0d01, 0x7900, 0xa9ff, 0x3aff, 0x2b00, 0x3500, 0xc4ff,
+0xc5ff, 0x77ff, 0xb8ff, 0x9600, 0xaa00, 0xd4ff, 0x97ff, 0xdfff, 0xc3ff, 0x1a00, 0xf9ff, 0x65ff, 0x81ff, 0xa7ff, 0xd0ff, 0x2a00,
+0x0600, 0x0800, 0x2d00, 0xebff, 0x2000, 0x6d00, 0x6a00, 0x5900, 0x5700, 0x0c00, 0x85ff, 0x9bff, 0xe4ff, 0x0400, 0x4000, 0xd7ff,
+0x0f00, 0x1f00, 0xf2ff, 0xb800, 0x6c00, 0xacff, 0x59ff, 0x2eff, 0x5fff, 0x1f00, 0x5200, 0x1f00, 0x2700, 0x3e00, 0xcdfe, 0x09fd,
+0x87fd, 0x2300, 0x0802, 0x8401, 0x2f01, 0x7c00, 0x9c00, 0x0302, 0xb301, 0x8fff, 0xc2fe, 0x25ff, 0x0000, 0x6e00, 0xabff, 0xedfe,
+0x54ff, 0xa800, 0xa301, 0x4801, 0x1900, 0x8eff, 0xf2ff, 0x7000, 0x6c00, 0xc6ff, 0x02ff, 0x34ff, 0x0000, 0x6c00, 0x4f00, 0x55ff,
+0x65ff, 0x3c00, 0x4d00, 0x1d00, 0x81ff, 0x49ff, 0xaaff, 0x2300, 0x1600, 0xaaff, 0x92ff, 0xb7ff, 0x7100, 0x4900, 0xf8ff, 0x4900,
+0x5700, 0x9f00, 0xa100, 0x5b00, 0xb6ff, 0x8bff, 0x0700, 0x3300, 0x3e00, 0xa2ff, 0x99ff, 0x2e00, 0x1200, 0x9500, 0x9200, 0xceff,
+0x99ff, 0xa9ff, 0x77ff, 0x5fff, 0xc7ff, 0x1c00, 0x1b00, 0x5400, 0x9fff, 0xd8fd, 0xe8fc, 0xaefe, 0x7301, 0x8a01, 0xd700, 0x6b00,
+0x6600, 0xfa01, 0x9d02, 0x5e00, 0xddfe, 0x75ff, 0x1f00, 0x8500, 0x6dff, 0x3ffe, 0xc4fe, 0x7200, 0x5401, 0xe600, 0x2900, 0xd8ff,
+0x6900, 0xe800, 0x9400, 0xd8ff, 0x45ff, 0x6aff, 0xedff, 0xf4ff, 0xf3ff, 0x8cff, 0x7aff, 0x1e00, 0x3a00, 0xfcff, 0xc7ff, 0xbbff,
+0xd1ff, 0x1300, 0x1c00, 0xc1ff, 0xbfff, 0xd6ff, 0x1300, 0x5100, 0xf7ff, 0x77ff, 0xe4ff, 0x9000, 0xa400, 0x9a00, 0x5300, 0xacff,
+0xa5ff, 0x3a00, 0x3800, 0x0000, 0xe2ff, 0xe3ff, 0x4300, 0xc400, 0x5e00, 0x90ff, 0xb4ff, 0x3a00, 0xf6ff, 0x8eff, 0x86ff, 0xcbfe,
+0x49ff, 0xe400, 0x7f00, 0x5cff, 0x80fe, 0x61fd, 0x78fe, 0xf601, 0x4001, 0xabff, 0xc500, 0xe000, 0xf201, 0x0c03, 0x7e00, 0x6ffe,
+0xceff, 0x7100, 0xd0ff, 0x6dff, 0x5cfe, 0x4afe, 0x5800, 0x6d01, 0x0e00, 0xc7ff, 0x2e00, 0x2a00, 0xc800, 0xe500, 0x89ff, 0x3cff,
+0x5900, 0x1a00, 0xcfff, 0x0b00, 0x71ff, 0x87ff, 0x7000, 0x3100, 0x95ff, 0xe1ff, 0x0000, 0xe9ff, 0x4700, 0x0300, 0x7aff, 0xf1ff,
+0x3700, 0xc9ff, 0xfcff, 0x0000, 0xd0ff, 0x6800, 0x9500, 0xdfff, 0xfaff, 0xe7ff, 0xe9ff, 0x9600, 0x1700, 0xbbff, 0x0b00, 0x5300,
+0x9000, 0x9c00, 0x1900, 0x97ff, 0xd7ff, 0x6100, 0x2400, 0xbdfe, 0xe0fe, 0xb0ff, 0xb0ff, 0x6400, 0x5500, 0x0dff, 0xdafe, 0x38fe,
+0xe1fd, 0x6200, 0x3801, 0xeaff, 0xe200, 0xf901, 0xd901, 0x4802, 0x8001, 0x25ff, 0xd7ff, 0xfa00, 0x7bff, 0xc8fe, 0xe4fe, 0x76fe,
+0xb4ff, 0xa800, 0xd5ff, 0xa7ff, 0x4d00, 0x6d00, 0x6e00, 0xb600, 0xe3ff, 0xc3ff, 0x6600, 0x1b00, 0xa3ff, 0xc0ff, 0xbaff, 0x90ff,
+0x0a00, 0x1c00, 0x99ff, 0x0900, 0x5000, 0xd6ff, 0x0e00, 0x7400, 0x1f00, 0xf5ff, 0x0d00, 0xc3ff, 0xe6ff, 0x4400, 0xebff, 0x9dff,
+0xdfff, 0x5a00, 0x1c00, 0x80ff, 0xd4ff, 0x1f00, 0x1200, 0x4e00, 0x6f00, 0xd900, 0x7d00, 0x9aff, 0xdbff, 0x4000, 0x2300, 0xd1ff,
+0xa3ff, 0x89ff, 0x74ff, 0xbdff, 0xb5ff, 0xe8ff, 0x4900, 0x0b00, 0xc6ff, 0x5aff, 0x6dfe, 0xc6fe, 0x3400, 0x6700, 0x2300, 0x9500,
+0xa300, 0x2901, 0x0a02, 0xf200, 0x7fff, 0x3800, 0x7e00, 0xedff, 0xb8ff, 0x0fff, 0xeafe, 0xdeff, 0x4500, 0xdcff, 0xcaff, 0x0d00,
+0x3d00, 0x7b00, 0x7c00, 0xffff, 0xf5ff, 0x2600, 0x1500, 0xe2ff, 0xd8ff, 0xc6ff, 0xb8ff, 0xf7ff, 0xf6ff, 0xd4ff, 0xf7ff, 0x1300,
+0xf7ff, 0x0800, 0xf3ff, 0xbbff, 0xb5ff, 0xfcff, 0xcaff, 0xc4ff, 0xfcff, 0xd2ff, 0xfeff, 0x2500, 0x2000, 0x0400, 0x0000, 0x0200,
+0x1000, 0x1e00, 0x1900, 0x2a00, 0x1600, 0x2e00, 0x5500, 0x1b00, 0x1f00, 0x1200, 0x2600, 0x6600, 0x3700, 0xfbff, 0x2200, 0x1300,
+0xffff, 0xfbff, 0xcaff, 0x97ff, 0x9eff, 0x95ff, 0x9aff, 0xceff, 0xa2ff, 0x6fff, 0xabff, 0x0000, 0x2400, 0x0c00, 0xf2ff, 0x0f00,
+0x6100, 0x7500, 0x4200, 0xfeff, 0x1c00, 0x5a00, 0x4a00, 0x2300, 0x0b00, 0x0f00, 0x0900, 0x1900, 0xfcff, 0xe0ff, 0xfdff, 0x1200,
+0x1100, 0x1200, 0x1700, 0x1800, 0x1500, 0x1500, 0x0500, 0xf7ff, 0xf9ff, 0x0800, 0xffff, 0x0300, 0x0000, 0xccff, 0xdaff, 0x0200,
+0xe5ff, 0xa7ff, 0x9cff, 0xb4ff, 0xd0ff, 0xcaff, 0xdbff, 0xc9ff, 0xd3ff, 0x0a00, 0x0500, 0x0600, 0x1500, 0x1400, 0x2d00, 0x5900,
+0x3000, 0x0900, 0x1300, 0x0800, 0x2000, 0x3600, 0x0500, 0x0000, 0xf6ff, 0x0700, 0x1900, 0xf5ff, 0x1000, 0x1300, 0xcdff, 0xbdff,
+0xd7ff, 0xe0ff, 0xd6ff, 0xe6ff, 0xbfff, 0xdfff, 0x3500, 0x1700, 0x0000, 0x2900, 0x4c00, 0x4600, 0x4500, 0x4a00, 0x2100, 0x3c00,
+0x4000, 0x0a00, 0xeeff, 0xedff, 0xe6ff, 0xe1ff, 0xe0ff, 0xeeff, 0x0100, 0x0a00, 0x1300, 0x0900, 0x0500, 0x0c00, 0x0600, 0xf2ff,
+0xecff, 0xe1ff, 0xe3ff, 0xe2ff, 0xe6ff, 0xe6ff, 0xf8ff, 0x0500, 0x0200, 0x0800, 0xe2ff, 0xdfff, 0x0000, 0x0300, 0xffff, 0x0000,
+0x1200, 0x1300, 0x0600, 0x0c00, 0xfcff, 0x1200, 0x1000, 0xfcff, 0x0800, 0x0900, 0xfbff, 0xf6ff, 0xfeff, 0xfbff, 0xfeff, 0xf1ff,
+0xfbff, 0x0400, 0x0600, 0x1700, 0x2500, 0x0e00, 0xdbff, 0xb8ff, 0xc9ff, 0xcfff, 0xb2ff, 0xbeff, 0xcdff, 0xc7ff, 0xe2ff, 0x0500,
+0x2400, 0x1600, 0x2300, 0x3900, 0x4c00, 0x5400, 0x4200, 0x3a00, 0x3100, 0x3e00, 0x2e00, 0x0800, 0xf6ff, 0xf2ff, 0xf7ff, 0xfaff,
+0xf7ff, 0xf3ff, 0x0600, 0x0f00, 0x0300, 0xffff, 0xf9ff, 0xe6ff, 0x0a00, 0x1f00, 0x0a00, 0x0300, 0xf8ff, 0xecff, 0xf2ff, 0xfdff,
+0xccff, 0xbbff, 0xe5ff, 0xe6ff, 0xb3ff, 0xc7ff, 0x0000, 0xfaff, 0xf2ff, 0x0d00, 0x1000, 0x0e00, 0x2300, 0x1c00, 0x0f00, 0x1c00,
+0x1700, 0x0b00, 0x1000, 0x0c00, 0x0400, 0x0100, 0x0200, 0x0300, 0x0000, 0xfdff, 0x0300, 0x0500, 0x0700, 0x0a00, 0x0600, 0x0400,
+0x0100, 0xf5ff, 0xecff, 0xf2ff, 0xf5ff, 0xecff, 0x0200, 0xfdff, 0xe0ff, 0x0800, 0x1b00, 0x0300, 0x0000, 0x0700, 0x0500, 0x0400,
+0x0100, 0xf0ff, 0xefff, 0x0100, 0xfcff, 0xfaff, 0x0700, 0x0600, 0x0e00, 0x1500, 0x0800, 0x0500, 0x0d00, 0x1100, 0x0800, 0x0a00,
+0x0600, 0xf8ff, 0xfdff, 0x0400, 0x2200, 0x2800, 0x1500, 0x0f00, 0x1800, 0x0e00, 0xfcff, 0xf9ff, 0xefff, 0xe5ff, 0xc5ff, 0xb4ff,
+0xc3ff, 0xcfff, 0xcbff, 0xcdff, 0xe4ff, 0xf7ff, 0x0300, 0x1300, 0xffff, 0xf8ff, 0x0e00, 0x0900, 0xfeff, 0xfeff, 0xfeff, 0xfbff,
+0x0300, 0x0a00, 0x0a00, 0x0f00, 0x1100, 0x0d00, 0x0800, 0x0e00, 0x1600, 0x0d00, 0x0900, 0x0900, 0x0100, 0x0200, 0x0000, 0xebff,
+0xfbff, 0x0200, 0xdeff, 0xf8ff, 0x0c00, 0xf8ff, 0xefff, 0x0100, 0x0000, 0x0200, 0x0500, 0x0f00, 0x1900, 0x2400, 0x2d00, 0x1b00,
+0x1500, 0x1b00, 0x1e00, 0x1200, 0x0100, 0x0000, 0xfeff, 0x0200, 0x0400, 0xfeff, 0xfaff, 0x0200, 0x0b00, 0x1d00, 0x2e00, 0x0100,
+0xf7ff, 0x0d00, 0x0300, 0xf7ff, 0xfbff, 0xf5ff, 0xeaff, 0xe8ff, 0xc9ff, 0xccff, 0xe2ff, 0xe2ff, 0xdeff, 0xe5ff, 0xf0ff, 0xfaff,
+0x0900, 0x0700, 0xe8ff, 0xedff, 0x0300, 0xfdff, 0xf3ff, 0xf8ff, 0x0e00, 0x0b00, 0x1800, 0x1200, 0x0100, 0x0f00, 0x1300, 0x1100,
+0x0e00, 0x1700, 0x1500, 0x0800, 0x0500, 0xf1ff, 0xe5ff, 0xe3ff, 0xd9ff, 0xceff, 0xc6ff, 0xbeff, 0xe5ff, 0xf2ff, 0xebff, 0xf1ff,
+0x0200, 0xffff, 0x0900, 0x1000, 0x1000, 0x1300, 0x2a00, 0x3700, 0x2c00, 0x3000, 0x3000, 0x2300, 0x2100, 0x2500, 0x1d00, 0x1700,
+0x1b00, 0x1800, 0x1800, 0x1900, 0x0f00, 0x0a00, 0x1300, 0x1e00, 0x1600, 0x0f00, 0xffff, 0x0200, 0xf2ff, 0xe3ff, 0xdfff, 0xdbff,
+0xd5ff, 0xc2ff, 0xb8ff, 0xb3ff, 0xbaff, 0xc4ff, 0xc9ff, 0xd1ff, 0xe4ff, 0xfcff, 0xfbff, 0xfeff, 0x0c00, 0x0c00, 0x1300, 0x2200,
+0x2300, 0x2100, 0x2b00, 0x3700, 0x2c00, 0x2d00, 0x2d00, 0x2300, 0x2800, 0x2300, 0x1300, 0x1800, 0x1000, 0xedff, 0xe1ff, 0xd6ff,
+0xbcff, 0xb8ff, 0xb3ff, 0x98ff, 0x9bff, 0xb7ff, 0xcfff, 0xd6ff, 0xe5ff, 0xecff, 0xf5ff, 0x0000, 0x0600, 0x1600, 0x1100, 0x1a00,
+0x3100, 0x3700, 0x3800, 0x3700, 0x3b00, 0x3900, 0x3700, 0x3200, 0x3400, 0x3600, 0x2e00, 0x2800, 0x2700, 0x1e00, 0x1800, 0x1800,
+0x1000, 0x1400, 0x0900, 0xfcff, 0xf7ff, 0xf1ff, 0xe4ff, 0xd7ff, 0xd7ff, 0xd2ff, 0xc1ff, 0xc1ff, 0xc8ff, 0xc3ff, 0xc5ff, 0xd6ff,
+0xddff, 0xdfff, 0xf0ff, 0x0000, 0x0300, 0x0300, 0x0900, 0x0a00, 0x1700, 0x2200, 0x1700, 0x2000, 0x3100, 0x3200, 0x3300, 0x2e00,
+0x2800, 0x1d00, 0x2600, 0x2200, 0x1800, 0x1300, 0x0200, 0xf0ff, 0xd3ff, 0xc8ff, 0xc0ff, 0xbbff, 0xbbff, 0xb4ff, 0xaaff, 0xbaff,
+0xd7ff, 0xe9ff, 0xf2ff, 0xfcff, 0x0500, 0x1000, 0x1900, 0x1a00, 0x2100, 0x1b00, 0x1d00, 0x2a00, 0x1d00, 0x1000, 0x0b00, 0x0c00,
+0x0b00, 0x0600, 0x0100, 0x0400, 0x0900, 0x0900, 0x0600, 0x0500, 0x0800, 0x0f00, 0x1100, 0xfcff, 0x0900, 0x0c00, 0x0400, 0x0500,
+0x0000, 0xf8ff, 0xf3ff, 0xf7ff, 0xefff, 0xe7ff, 0xeeff, 0xf1ff, 0xf2ff, 0xf5ff, 0xfbff, 0xfdff, 0xffff, 0x0000, 0x0100, 0x0000,
+0xfeff, 0xffff, 0x0000, 0x0000, 0x0100, 0xffff, 0x0000, 0x0400, 0x0500, 0x0200, 0xffff, 0x0000, 0xfeff, 0xfcff, 0xfbff, 0xf7ff,
+0xf3ff, 0xf1ff, 0xf6ff, 0xf1ff, 0xf4ff, 0xf7ff, 0xfaff, 0xfeff, 0x0700, 0x0e00, 0x1300, 0x1e00, 0x2000, 0x2200, 0x2000, 0x2200,
+0x1d00, 0x1c00, 0x1d00, 0x1b00, 0x1600, 0x1b00, 0x1900, 0x1200, 0x0d00, 0x0a00, 0x0500, 0x0000, 0xebff, 0xddff, 0xe1ff, 0xe0ff,
+0xddff, 0xdcff, 0xdeff, 0xdeff, 0xe6ff, 0xebff, 0xeeff, 0xf4ff, 0xffff, 0x0100, 0x0a00, 0x0b00, 0x0100, 0x0600, 0x1600, 0x0e00,
+0x0a00, 0x1100, 0x1500, 0x1000, 0x1000, 0x0d00, 0x0b00, 0x0900, 0x0500, 0xfbff, 0xffff, 0x0200, 0x0000, 0x0100, 0x0100, 0x0200,
+0xfbff, 0x0000, 0xfaff, 0xf4ff, 0xedff, 0xedff, 0xe9ff, 0xe4ff, 0xe2ff, 0xdeff, 0xdbff, 0xe0ff, 0xe0ff, 0xe3ff, 0xe9ff, 0xf3ff,
+0xf7ff, 0x0700, 0x0b00, 0x0000, 0x0f00, 0x1b00, 0x1700, 0x1600, 0x2100, 0x2600, 0x2700, 0x2e00, 0x2600, 0x2300, 0x2000, 0x1800,
+0x0e00, 0x0b00, 0x0200, 0x0300, 0xf3ff, 0xe2ff, 0xe0ff, 0xe0ff, 0xe1ff, 0xdeff, 0xe3ff, 0xe4ff, 0xf2ff, 0xf1ff, 0xfcff, 0xf9ff,
+0xf8ff, 0x0000, 0x0300, 0x0900, 0x0a00, 0x0e00, 0x0300, 0x0600, 0x0400, 0x0400, 0x0100, 0x0200, 0x0000, 0x0000, 0xffff, 0xfcff,
+0x0100, 0x0100, 0x0300, 0x0400, 0x0800, 0x0500, 0x0500, 0x0000, 0xfaff, 0xffff, 0x0100, 0xfcff, 0xfeff, 0x0000, 0xfcff, 0xf6ff,
+0xf5ff, 0xf3ff, 0xf3ff, 0xf0ff, 0xf7ff, 0xfcff, 0x0100, 0x0e00, 0x1800, 0x1900, 0x1d00, 0x2800, 0x2400, 0x2000, 0x2400, 0x2000,
+0x1400, 0x0d00, 0x0000, 0xf9ff, 0xeeff, 0xe7ff, 0xe6ff, 0xe3ff, 0xe0ff, 0xdbff, 0xe6ff, 0xeeff, 0xf4ff, 0xfdff, 0xfeff, 0xffff,
+0x0000, 0x0400, 0x0000, 0x0500, 0x0500, 0x0900, 0x0500, 0x0600, 0x0c00, 0x0d00, 0x1200, 0x0c00, 0x0d00, 0x0a00, 0x0d00, 0x0b00,
+0x0500, 0x0100, 0x0100, 0xfeff, 0xfdff, 0xfaff, 0xfaff, 0xfaff, 0xfbff, 0xf9ff, 0xf8ff, 0xf8ff, 0xf7ff, 0xf8ff, 0xf6ff, 0xf3ff,
+0xf8ff, 0xf6ff, 0xf4ff, 0xf2ff, 0xf1ff, 0xefff, 0xf5ff, 0xf3ff, 0xf6ff, 0xfcff, 0xfdff, 0xfcff, 0xffff, 0x0100, 0x0000, 0x0000,
+0x0400, 0x1000, 0x1000, 0x1300, 0x1700, 0x1e00, 0x1400, 0x1200, 0x1400, 0x0e00, 0x1400, 0x1100, 0x0600, 0xfbff, 0xf3ff, 0xe9ff,
+0xebff, 0xe8ff, 0xe2ff, 0xe7ff, 0xebff, 0xf1ff, 0xf5ff, 0xfeff, 0x0100, 0xffff, 0xfaff, 0xfdff, 0xfeff, 0xfeff, 0xfeff, 0xffff,
+0x0900, 0x0c00, 0x1100, 0x1400, 0x1100, 0x1200, 0x0d00, 0x0b00, 0x0f00, 0x0f00, 0x0a00, 0x0600, 0xfeff, 0x0000, 0xfeff, 0xf9ff,
+0xfcff, 0xfaff, 0xf8ff, 0xfbff, 0xfeff, 0xf9ff, 0xfcff, 0xf7ff, 0xfbff, 0xf5ff, 0xfcff, 0x0900, 0x0000, 0x0a00, 0x0600, 0xfeff,
+0xfbff, 0x1700, 0x1000, 0x0800, 0x1100, 0x0800, 0xf2ff, 0x0400, 0x0300, 0x0000, 0x1d00, 0x1b00, 0x0b00, 0xf1ff, 0xd8ff, 0xddff,
+0xeeff, 0xdfff, 0xd0ff, 0xf0ff, 0xf3ff, 0xf4ff, 0x0300, 0x1000, 0xf9ff, 0xf3ff, 0x1000, 0x1e00, 0x0e00, 0xfdff, 0xfaff, 0xfdff,
+0x1100, 0xfeff, 0x1d00, 0x3100, 0x0300, 0xf8ff, 0x0c00, 0x1700, 0x1300, 0x0200, 0xfdff, 0x0900, 0x0800, 0xf8ff, 0xf9ff, 0xfaff,
+0xf3ff, 0x0400, 0xfcff, 0xf5ff, 0x0100, 0xfaff, 0xe3ff, 0xe4ff, 0xf8ff, 0x0000, 0x0300, 0xf1ff, 0xe1ff, 0x0000, 0xf7ff, 0xf4ff,
+0xefff, 0xdfff, 0x0900, 0x1600, 0xe0ff, 0xf5ff, 0x1500, 0x0900, 0xfdff, 0x0300, 0x1200, 0x1900, 0xffff, 0x0e00, 0x3e00, 0x3400,
+0xe3ff, 0xfbff, 0x2c00, 0xfdff, 0xe5ff, 0x0300, 0x0800, 0x0200, 0xe5ff, 0xe0ff, 0x0900, 0x1500, 0xdeff, 0xe1ff, 0xf8ff, 0xe0ff,
+0xddff, 0x1500, 0x1f00, 0xecff, 0x0c00, 0x6aff, 0x0700, 0x2600, 0xecfe, 0xac01, 0x8500, 0xa9fe, 0xa200, 0x6700, 0xb8ff, 0x8f00,
+0xf9ff, 0xa1ff, 0xd100, 0xc1fe, 0x8900, 0x4a01, 0x6dfe, 0xf9ff, 0x1301, 0xb1ff, 0xbaff, 0xac00, 0x1401, 0x7bfe, 0x16ff, 0x8e01,
+0x92ff, 0x03ff, 0x7202, 0xedff, 0x58fe, 0x6b00, 0x0300, 0xc6ff, 0x5600, 0xadff, 0xd5ff, 0xe800, 0x85ff, 0x16ff, 0xd000, 0x2600,
+0xacff, 0x3f00, 0x47fe, 0x6000, 0x3b02, 0xc1fe, 0x56fe, 0x3001, 0xc2ff, 0xfffe, 0xf4ff, 0x2400, 0x5000, 0x1f00, 0x38ff, 0x0200,
+0x6800, 0xc800, 0x45ff, 0xb1ff, 0xeb00, 0x0100, 0xaaff, 0x0f00, 0x7f00, 0x2400, 0x8d00, 0xa5ff, 0x0600, 0xa501, 0xa5ff, 0x67ff,
+0xb700, 0x0e00, 0xa6ff, 0xe1ff, 0x0c00, 0xf4ff, 0xf1ff, 0x29ff, 0x0cff, 0xb1ff, 0x2fff, 0xfffe, 0xe0ff, 0x1900, 0xe1ff, 0x1600,
+0x5b00, 0x6200, 0x7d00, 0x1f00, 0x1300, 0x6100, 0x4500, 0xf3ff, 0xb400, 0x7800, 0x5d00, 0x1600, 0x4500, 0x4700, 0x1200, 0x0a00,
+0xd5ff, 0x5a00, 0x3400, 0xf4ff, 0x3600, 0xbeff, 0xe1ff, 0x0300, 0xb6ff, 0xa5ff, 0xc2ff, 0x98ff, 0xc5ff, 0x2100, 0xdbff, 0x2d00,
+0x1400, 0xd4ff, 0x2e00, 0x0a00, 0xfaff, 0x0a00, 0x0300, 0xd9ff, 0xc3ff, 0x6600, 0x1d00, 0x80ff, 0x0700, 0x2300, 0x0eff, 0xacff,
+0x0500, 0xacff, 0x1400, 0x4600, 0x5000, 0x6400, 0xad00, 0x4900, 0x2c00, 0x7900, 0x3000, 0xc7ff, 0xbbff, 0x8cff, 0xa3ff, 0x5800,
+0xcfff, 0xddfe, 0x8fff, 0xc3ff, 0x78fe, 0x51ff, 0xceff, 0x62ff, 0x2b00, 0xbd00, 0x7300, 0xe000, 0x7901, 0x8900, 0x6e00, 0xf000,
+0x5100, 0xd5ff, 0xcfff, 0xacff, 0xe1ff, 0xc400, 0x71ff, 0xbffd, 0xacff, 0xfdff, 0x5dfe, 0x43ff, 0xa000, 0x9b00, 0xab00, 0x1f01,
+0x0b01, 0x7e01, 0x1f01, 0xddff, 0xfcff, 0x3200, 0x3fff, 0x16ff, 0x1500, 0xc7ff, 0xddfe, 0x64ff, 0xe6fe, 0x4efe, 0x26ff, 0x4dff,
+0x30ff, 0x2800, 0x2401, 0xe300, 0x6301, 0xdf01, 0xa801, 0xb001, 0x5701, 0xc500, 0x8100, 0x3600, 0xa7ff, 0xd6ff, 0x6100, 0x0eff,
+0x82fe, 0x9eff, 0xa8ff, 0x03ff, 0x97ff, 0x7300, 0x8200, 0x6600, 0x6000, 0x6700, 0x7c00, 0xdeff, 0x2fff, 0x57ff, 0x83ff, 0x26ff,
+0xf3fe, 0x37ff, 0x49ff, 0xe6fe, 0x04ff, 0x66ff, 0x8dff, 0xa6ff, 0x0100, 0x8300, 0xbc00, 0xf200, 0x2501, 0x6301, 0x6601, 0x2b01,
+0xed00, 0xbe00, 0x6400, 0x2400, 0x2f00, 0x0d00, 0xe0ff, 0xe5ff, 0xbeff, 0xc4ff, 0x0e00, 0x0600, 0xebff, 0xfbff, 0xd1ff, 0xa5ff,
+0x7eff, 0x48ff, 0x2dff, 0x35ff, 0x19ff, 0x62ff, 0x30ff, 0x40ff, 0xbfff, 0x6fff, 0x7eff, 0x0600, 0xe4ff, 0xbaff, 0x4000, 0xae00,
+0x8f00, 0x8500, 0xab00, 0xd500, 0xa900, 0x5800, 0x4d00, 0x4a00, 0x1f00, 0xe3ff, 0xd5ff, 0x0500, 0x0e00, 0x5eff, 0xbfff, 0x9500 };
diff --git a/trunk/codecs/slin_ulaw_ex.h b/trunk/codecs/slin_ulaw_ex.h
new file mode 100644
index 000000000..083acef3c
--- /dev/null
+++ b/trunk/codecs/slin_ulaw_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * \brief slin_ulaw_ex.h --
+ *
+ * Signed 16-bit audio data, 10 milliseconds worth at 8 kHz.
+ *
+ * Source: g723.example
+ *
+ * Copyright (C) 2001-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_ulaw_ex[] = {
+ 0xcac0, 0xcdeb, 0xd116, 0xd441, 0xd76c, 0xda97, 0xddc2, 0xe0ed,
+ 0x0000, 0x032b, 0x0656, 0x0981, 0x0cac, 0x0fd7, 0x1302, 0x162d,
+ 0x1958, 0x1c83, 0x1fae, 0x22d9, 0x2604, 0x292f, 0x2c5a, 0x2f85,
+ 0x32b0, 0x35db, 0x3906, 0x3c31, 0x3f5c, 0x4287, 0x45b2, 0x48dd,
+ 0xb168, 0xb493, 0xb7be, 0xbae9, 0xbe14, 0xc13f, 0xc46a, 0xc795,
+ 0xe418, 0xe743, 0xea6e, 0xed99, 0xf0c4, 0xf3ef, 0xf71a, 0xfa45,
+ 0x9810, 0x9b3b, 0x9e66, 0xa191, 0xa4bc, 0xa7e7, 0xab12, 0xae3d,
+ 0x4c08, 0x4f33, 0x525e, 0x5589, 0x58b4, 0x5bdf, 0x5f0a, 0x6235,
+ 0x6560, 0x688b, 0x6bb6, 0x6ee1, 0x720c, 0x7537, 0x7862, 0x7b8d,
+ 0x7eb8, 0x81e3, 0x850e, 0x8839, 0x8b64, 0x8e8f, 0x91ba, 0x94e5,
+};
diff --git a/trunk/codecs/speex_slin_ex.h b/trunk/codecs/speex_slin_ex.h
new file mode 100644
index 000000000..404743799
--- /dev/null
+++ b/trunk/codecs/speex_slin_ex.h
@@ -0,0 +1,16 @@
+/*! \file
+ * \brief Random Data data
+ *
+ * Source: speex.raw
+ *
+ * Copyright (C) 1999-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char speex_slin_ex[] = {
+0x2e, 0x8e, 0x0f, 0x9a, 0x20, 0000, 0x01, 0x7f, 0xff, 0xff,
+0xff, 0xff, 0xff, 0x91, 0000, 0xbf, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xdc, 0x80, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0x98, 0x7f, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xf7, 0x80 };
diff --git a/trunk/codecs/ulaw_slin_ex.h b/trunk/codecs/ulaw_slin_ex.h
new file mode 100644
index 000000000..a12556824
--- /dev/null
+++ b/trunk/codecs/ulaw_slin_ex.h
@@ -0,0 +1,25 @@
+/*! \file
+ * \brief ulaw_slin_ex.h --
+ *
+ * 4-bit ADPCM data, 20 milliseconds worth at 8 kHz.
+ *
+ * Source: g723.example
+ *
+ * Copyright (C) 2001-2005, Digium Inc.
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char ulaw_slin_ex[] = {
+ 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15,
+ 0x10, 0x18, 0x1b, 0x1e, 0x21, 0x24, 0x27, 0x2a,
+ 0x20, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3f,
+ 0x30, 0x42, 0x45, 0x48, 0x4b, 0x4e, 0x51, 0x54,
+ 0x40, 0x57, 0x5a, 0x5d, 0x60, 0x63, 0x66, 0x69,
+ 0x50, 0x6c, 0x6f, 0x72, 0x75, 0x78, 0x7b, 0x7e,
+ 0x60, 0x81, 0x84, 0x87, 0x8a, 0x8d, 0x90, 0x93,
+ 0x70, 0x96, 0x99, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8,
+ 0x80, 0xab, 0xae, 0xb1, 0xb4, 0xb7, 0xba, 0xbd,
+ 0x90, 0xc0, 0xc3, 0xc6, 0xc9, 0xcc, 0xcf, 0xd2
+};
diff --git a/trunk/config.guess b/trunk/config.guess
new file mode 100755
index 000000000..22906b339
--- /dev/null
+++ b/trunk/config.guess
@@ -0,0 +1,1495 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+# Inc.
+
+timestamp='2006-03-13'
+
+# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ case ${UNAME_MACHINE} in
+ pc98)
+ echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ x86:Interix*:[345]*)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ EM64T:Interix*:[345]*)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^CPU/{
+ s: ::g
+ p
+ }'`"
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^CPU/{
+ s: ::g
+ p
+ }'`"
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__sun)
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^LIBC/{
+ s: ::g
+ p
+ }'`"
+ test x"${LIBC}" != x && {
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit
+ }
+ test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
+and
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/config.sub b/trunk/config.sub
new file mode 100755
index 000000000..5705e543b
--- /dev/null
+++ b/trunk/config.sub
@@ -0,0 +1,1609 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+# Inc.
+
+timestamp='2006-03-07'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32r | m32rle | m68000 | m68k | m88k | maxq | mb | microblaze | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | mt \
+ | msp430 \
+ | nios | nios2 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m32c)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nios-* | nios2-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ m32c-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16c)
+ basic_machine=cr16c-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/configs/adsi.conf.sample b/trunk/configs/adsi.conf.sample
new file mode 100644
index 000000000..0f36f80da
--- /dev/null
+++ b/trunk/configs/adsi.conf.sample
@@ -0,0 +1,8 @@
+;
+; Sample ADSI Configuration file
+;
+[intro]
+alignment = center
+greeting => Welcome to the
+greeting => Asterisk
+greeting => Open Source PBX
diff --git a/trunk/configs/adtranvofr.conf.sample b/trunk/configs/adtranvofr.conf.sample
new file mode 100644
index 000000000..dc7bcfc7c
--- /dev/null
+++ b/trunk/configs/adtranvofr.conf.sample
@@ -0,0 +1,39 @@
+;
+; Voice over Frame Relay (Adtran style)
+;
+; Configuration file
+
+[interfaces]
+;
+; Default language
+;
+;language=en
+;
+; Lines for which we are the user termination. They accept incoming
+; and outgoing calls. We use the default context on the first 8 lines
+; used by internal phones.
+;
+context=default
+;user => voice00
+;user => voice01
+;user => voice02
+;user => voice03
+;user => voice04
+;user => voice05
+;user => voice06
+;user => voice07
+; Calls on 16 and 17 come from the outside world, so they get
+; a little bit special treatment
+context=remote
+;user => voice16
+;user => voice17
+;
+; Next we have lines which we only accept calls on, and typically
+; do not send outgoing calls on (i.e. these are where we are the
+; network termination)
+;
+;network => voice08
+;network => voice09
+;network => voice10
+;network => voice11
+;network => voice12
diff --git a/trunk/configs/agents.conf.sample b/trunk/configs/agents.conf.sample
new file mode 100644
index 000000000..eb0383f3c
--- /dev/null
+++ b/trunk/configs/agents.conf.sample
@@ -0,0 +1,105 @@
+;
+; Agent configuration
+;
+
+[general]
+;
+; Define whether callbacklogins should be stored in astdb for
+; persistence. Persistent logins will be reloaded after
+; Asterisk restarts.
+;
+persistentagents=yes
+
+; Enable or disable a single extension from logging in as multiple agents.
+; The default value is "yes".
+;multiplelogin=yes
+
+[agents]
+;
+; Define maxlogintries to allow agent to try max logins before
+; failed.
+; default to 3
+;
+;maxlogintries=5
+;
+;
+; Define autologoff times if appropriate. This is how long
+; the phone has to ring with no answer before the agent is
+; automatically logged off (in seconds)
+;
+;autologoff=15
+;
+; Define autologoffunavail to have agents automatically logged
+; out when the extension that they are at returns a CHANUNAVAIL
+; status when a call is attempted to be sent there.
+; Default is "no".
+;
+;autologoffunavail=yes
+;
+; Define ackcall to require an acknowledgement by '#' when
+; an agent logs in using agentcallbacklogin. Default is "no".
+;
+;ackcall=no
+;
+; Define endcall to allow an agent to hangup a call by '*'.
+; Default is "yes". Set this to "no" to ignore '*'.
+;
+;endcall=yes
+;
+; Define wrapuptime. This is the minimum amount of time when
+; after disconnecting before the caller can receive a new call
+; note this is in milliseconds.
+;
+;wrapuptime=5000
+;
+; Define the default musiconhold for agents
+; musiconhold => music_class
+;
+;musiconhold => default
+;
+; Define the default good bye sound file for agents
+; default to vm-goodbye
+;
+;agentgoodbye => goodbye_file
+;
+; Define updatecdr. This is whether or not to change the source
+; channel in the CDR record for this call to agent/agent_id so
+; that we know which agent generates the call
+;
+;updatecdr=no
+;
+; Group memberships for agents (may change in mid-file)
+;
+;group=3
+;group=1,2
+;group=
+;
+; --------------------------------------------------
+; This section is devoted to recording agent's calls
+; The keywords are global to the chan_agent channel driver
+;
+; Enable recording calls addressed to agents. It's turned off by default.
+;recordagentcalls=yes
+;
+; The format to be used to record the calls: wav, gsm, wav49.
+; By default its "wav".
+;recordformat=gsm
+;
+; The text to be added to the name of the recording. Allows forming a url link.
+;urlprefix=http://localhost/calls/
+;
+; The optional directory to save the conversations in. The default is
+; /var/spool/asterisk/monitor
+;savecallsin=/var/calls
+;
+; An optional custom beep sound file to play to always-connected agents.
+;custom_beep=beep
+;
+; --------------------------------------------------
+;
+; This section contains the agent definitions, in the form:
+;
+; agent => agentid,agentpassword,name
+;
+;agent => 1001,4321,Mark Spencer
+;agent => 1002,4321,Will Meadows
diff --git a/trunk/configs/alarmreceiver.conf.sample b/trunk/configs/alarmreceiver.conf.sample
new file mode 100644
index 000000000..bf767dea3
--- /dev/null
+++ b/trunk/configs/alarmreceiver.conf.sample
@@ -0,0 +1,80 @@
+;
+; alarmreceiver.conf
+;
+; Sample configuration file for the Asterisk alarm receiver application.
+;
+
+
+[general]
+
+;
+; Specify a timestamp format for the metadata section of the event files
+; Default is %a %b %d, %Y @ %H:%M:%S %Z
+
+timestampformat = %a %b %d, %Y @ %H:%M:%S %Z
+
+;
+; Specify a command to execute when the caller hangs up
+;
+; Default is none
+;
+
+;eventcmd = yourprogram -yourargs ...
+
+;
+; Specify a spool directory for the event files. This setting is required
+; if you want the app to be useful. Event files written to the spool
+; directory will be of the template event-XXXXXX, where XXXXXX is a random
+; and unique alphanumeric string.
+;
+; Default is none, and the events will be dropped on the floor.
+;
+
+eventspooldir = /tmp
+
+;
+; The alarmreceiver app can either log the events one-at-a-time to individual
+; files in the spool directory, or it can store them until the caller
+; disconnects and write them all to one file.
+;
+; The default setting for logindividualevents is no.
+;
+
+logindividualevents = no
+
+;
+; The timeout for receiving the first DTMF digit is adjustable from 1000 msec.
+; to 10000 msec. The default is 2000 msec. Note: if you wish to test the
+; receiver by entering digits manually, set this to a reasonable time out
+; like 10000 milliseconds.
+
+fdtimeout = 2000
+
+;
+; The timeout for receiving subsequent DTMF digits is adjustable from
+; 110 msec. to 4000 msec. The default is 200 msec. Note: if you wish to test
+; the receiver by entering digits manually, set this to a reasonable time out
+; like 4000 milliseconds.
+;
+
+sdtimeout = 200
+
+;
+; The loudness of the ACK and Kissoff tones is adjustable from 100 to 8192.
+; The default is 8192. This shouldn't need to be messed with, but is included
+; just in case there are problems with signal levels.
+;
+
+loudness = 8192
+
+;
+; The db-family setting allows the user to capture statistics on the number of
+; calls, and the errors the alarm receiver sees. The default is for no
+; db-family name to be defined and the database logging to be turned off.
+;
+
+;db-family = yourfamily:
+
+;
+; End of alarmreceiver.conf
+;
diff --git a/trunk/configs/alsa.conf.sample b/trunk/configs/alsa.conf.sample
new file mode 100644
index 000000000..f55030618
--- /dev/null
+++ b/trunk/configs/alsa.conf.sample
@@ -0,0 +1,62 @@
+;
+; Open Sound System Console Driver Configuration File
+;
+[general]
+;
+; Automatically answer incoming calls on the console? Choose yes if
+; for example you want to use this as an intercom.
+;
+autoanswer=yes
+;
+; Default context (is overridden with @context syntax)
+;
+context=local
+;
+; Default extension to call
+;
+extension=s
+;
+; Default language
+;
+;language=en
+;
+; Default Music on Hold class to use when this channel is placed on hold in
+; the case that the music class is not set on the channel with
+; Set(CHANNEL(musicclass)=whatever) in the dialplan and the peer channel
+; putting this one on hold did not suggest a class to use.
+;
+;mohinterpret=default
+;
+; Silence suppression can be enabled when sound is over a certain threshold.
+; The value for the threshold should probably be between 500 and 2000 or so,
+; but your mileage may vary. Use the echo test to evaluate the best setting.
+;silencesuppression = yes
+;silencethreshold = 1000
+;
+; To set which ALSA device to use, change this parameter
+;input_device=hw:0,0
+;output_device=hw:0,0
+
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
+ ; ALSA channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The ALSA channel can't accept jitter,
+ ; thus an enabled jitterbuffer on the receive ALSA side will always
+ ; be used if the sending side can create jitter.
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+
diff --git a/trunk/configs/amd.conf.sample b/trunk/configs/amd.conf.sample
new file mode 100644
index 000000000..ce4808a0c
--- /dev/null
+++ b/trunk/configs/amd.conf.sample
@@ -0,0 +1,18 @@
+;
+; Answering Machine Detection Configuration
+;
+
+[general]
+initial_silence = 2500 ; Maximum silence duration before the greeting.
+ ; If exceeded then MACHINE.
+greeting = 1500 ; Maximum length of a greeting. If exceeded then MACHINE.
+after_greeting_silence = 800 ; Silence after detecting a greeting.
+ ; If exceeded then HUMAN
+total_analysis_time = 5000 ; Maximum time allowed for the algorithm to decide
+ ; on a HUMAN or MACHINE
+min_word_length = 100 ; Minimum duration of Voice to considered as a word
+between_words_silence = 50 ; Minimum duration of silence after a word to consider
+ ; the audio what follows as a new word
+maximum_number_of_words = 3 ; Maximum number of words in the greeting.
+ ; If exceeded then MACHINE
+silence_threshold = 256
diff --git a/trunk/configs/asterisk.adsi b/trunk/configs/asterisk.adsi
new file mode 100644
index 000000000..a275502ac
--- /dev/null
+++ b/trunk/configs/asterisk.adsi
@@ -0,0 +1,159 @@
+;
+; Asterisk default ADSI script
+;
+;
+; Begin with the preamble requirements
+;
+DESCRIPTION "Asterisk PBX" ; Name of vendor
+VERSION 0x00 ; Version of stuff
+;SECURITY "_AST" ; Security code
+SECURITY 0X9BDBF7AC ; Security code
+FDN 0x0000000F ; Descriptor number
+
+;
+; Flags
+;
+FLAG "nocallwaiting"
+
+;
+; Predefined strings
+;
+DISPLAY "titles" IS "** Asterisk PBX **"
+DISPLAY "talkingto" IS "Call active." JUSTIFY LEFT
+DISPLAY "callname" IS "$Call1p" JUSTIFY LEFT
+DISPLAY "callnum" IS "$Call1s" JUSTIFY LEFT
+DISPLAY "incoming" IS "Incoming call!" JUSTIFY LEFT
+DISPLAY "ringing" IS "Calling... " JUSTIFY LEFT
+DISPLAY "callended" IS "Call ended." JUSTIFY LEFT
+DISPLAY "missedcall" IS "Missed call." JUSTIFY LEFT
+DISPLAY "busy" IS "Busy." JUSTIFY LEFT
+DISPLAY "reorder" IS "Reorder." JUSTIFY LEFT
+DISPLAY "cwdisabled" IS "Callwait disabled"
+DISPLAY "empty" IS "asdf"
+
+;
+; Begin soft key definitions
+;
+KEY "callfwd" IS "CallFwd" OR "Call Forward"
+ OFFHOOK
+ VOICEMODE
+ WAITDIALTONE
+ SENDDTMF "*60"
+ GOTO "offHook"
+ENDKEY
+
+KEY "vmail_OH" IS "VMail" OR "Voicemail"
+ OFFHOOK
+ VOICEMODE
+ WAITDIALTONE
+ SENDDTMF "8500"
+ENDKEY
+
+KEY "vmail" IS "VMail" OR "Voicemail"
+ SENDDTMF "8500"
+ENDKEY
+
+KEY "backspace" IS "BackSpc" OR "Backspace"
+ BACKSPACE
+ENDKEY
+
+KEY "cwdisable" IS "CWDsble" OR "Disable Call Wait"
+ SENDDTMF "*70"
+ SETFLAG "nocallwaiting"
+ SHOWDISPLAY "cwdisabled" AT 4
+ TIMERCLEAR
+ TIMERSTART 1
+ENDKEY
+
+KEY "cidblock" IS "CIDBlk" OR "Block Callerid"
+ SENDDTMF "*67"
+ SETFLAG "nocallwaiting"
+ENDKEY
+
+;
+; Begin main subroutine
+;
+
+SUB "main" IS
+ IFEVENT NEARANSWER THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1 NOUPDATE
+ SHOWDISPLAY "talkingto" AT 2 NOUPDATE
+ SHOWDISPLAY "callname" AT 3
+ SHOWDISPLAY "callnum" AT 4
+ GOTO "stableCall"
+ ENDIF
+ IFEVENT OFFHOOK THEN
+ CLEAR
+ CLEARFLAG "nocallwaiting"
+ CLEARDISPLAY
+ SHOWDISPLAY "titles" AT 1
+ SHOWKEYS "vmail"
+ SHOWKEYS "cidblock"
+ SHOWKEYS "cwdisable" UNLESS "nocallwaiting"
+ GOTO "offHook"
+ ENDIF
+ IFEVENT IDLE THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1
+ SHOWKEYS "vmail_OH"
+ ENDIF
+ IFEVENT CALLERID THEN
+ CLEAR
+; SHOWDISPLAY "titles" AT 1 NOUPDATE
+; SHOWDISPLAY "incoming" AT 2 NOUPDATE
+ SHOWDISPLAY "callname" AT 3 NOUPDATE
+ SHOWDISPLAY "callnum" AT 4
+ ENDIF
+ IFEVENT RING THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1 NOUPDATE
+ SHOWDISPLAY "incoming" AT 2
+ ENDIF
+ IFEVENT ENDOFRING THEN
+ SHOWDISPLAY "missedcall" AT 2
+ CLEAR
+ SHOWDISPLAY "titles" AT 1
+ SHOWKEYS "vmail_OH"
+ ENDIF
+ IFEVENT TIMER THEN
+ CLEAR
+ SHOWDISPLAY "empty" AT 4
+ ENDIF
+ENDSUB
+
+SUB "offHook" IS
+ IFEVENT FARRING THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1 NOUPDATE
+ SHOWDISPLAY "ringing" AT 2 NOUPDATE
+ SHOWDISPLAY "callname" at 3 NOUPDATE
+ SHOWDISPLAY "callnum" at 4
+ ENDIF
+ IFEVENT FARANSWER THEN
+ CLEAR
+ SHOWDISPLAY "talkingto" AT 2
+ GOTO "stableCall"
+ ENDIF
+ IFEVENT BUSY THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1 NOUPDATE
+ SHOWDISPLAY "busy" AT 2 NOUPDATE
+ SHOWDISPLAY "callname" at 3 NOUPDATE
+ SHOWDISPLAY "callnum" at 4
+ ENDIF
+ IFEVENT REORDER THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1 NOUPDATE
+ SHOWDISPLAY "reorder" AT 2 NOUPDATE
+ SHOWDISPLAY "callname" at 3 NOUPDATE
+ SHOWDISPLAY "callnum" at 4
+ ENDIF
+ENDSUB
+
+SUB "stableCall" IS
+ IFEVENT REORDER THEN
+ SHOWDISPLAY "callended" AT 2
+ ENDIF
+ENDSUB
+
diff --git a/trunk/configs/cdr.conf.sample b/trunk/configs/cdr.conf.sample
new file mode 100644
index 000000000..c2882c1f0
--- /dev/null
+++ b/trunk/configs/cdr.conf.sample
@@ -0,0 +1,148 @@
+;
+; Asterisk Call Detail Record engine configuration
+;
+; CDR is Call Detail Record, which provides logging services via a variety of
+; pluggable backend modules. Detailed call information can be recorded to
+; databases, files, etc. Useful for billing, fraud prevention, compliance with
+; Sarbanes-Oxley aka The Enron Act, QOS evaluations, and more.
+;
+
+[general]
+
+; Define whether or not to use CDR logging. Setting this to "no" will override
+; any loading of backend CDR modules. Default is "yes".
+;enable=yes
+
+; Define whether or not to log unanswered calls. Setting this to "yes" will
+; report every attempt to ring a phone in dialing attempts, when it was not
+; answered. For example, if you try to dial 3 extensions, and this option is "yes",
+; you will get 3 CDR's, one for each phone that was rung. Default is "no". Some
+; find this information horribly useless. Others find it very valuable. Note, in "yes"
+; mode, you will see one CDR, with one of the call targets on one side, and the originating
+; channel on the other, and then one CDR for each channel attempted. This may seem
+; redundant, but cannot be helped.
+;unanswered = no
+
+; Define the CDR batch mode, where instead of posting the CDR at the end of
+; every call, the data will be stored in a buffer to help alleviate load on the
+; asterisk server. Default is "no".
+;
+; WARNING WARNING WARNING
+; Use of batch mode may result in data loss after unsafe asterisk termination
+; ie. software crash, power failure, kill -9, etc.
+; WARNING WARNING WARNING
+;
+;batch=no
+
+; Define the maximum number of CDRs to accumulate in the buffer before posting
+; them to the backend engines. 'batch' must be set to 'yes'. Default is 100.
+;size=100
+
+; Define the maximum time to accumulate CDRs in the buffer before posting them
+; to the backend engines. If this time limit is reached, then it will post the
+; records, regardless of the value defined for 'size'. 'batch' must be set to
+; 'yes'. Note that time is in seconds. Default is 300 (5 minutes).
+;time=300
+
+; The CDR engine uses the internal asterisk scheduler to determine when to post
+; records. Posting can either occur inside the scheduler thread, or a new
+; thread can be spawned for the submission of every batch. For small batches,
+; it might be acceptable to just use the scheduler thread, so set this to "yes".
+; For large batches, say anything over size=10, a new thread is recommended, so
+; set this to "no". Default is "no".
+;scheduleronly=no
+
+; When shutting down asterisk, you can block until the CDRs are submitted. If
+; you don't, then data will likely be lost. You can always check the size of
+; the CDR batch buffer with the CLI "cdr status" command. To enable blocking on
+; submission of CDR data during asterisk shutdown, set this to "yes". Default
+; is "yes".
+;safeshutdown=yes
+
+; Normally, CDR's are not closed out until after all extensions are finished
+; executing. By enabling this option, the CDR will be ended before executing
+; the "h" extension so that CDR values such as "end" and "billsec" may be
+; retrieved inside of of this extension.
+;endbeforehexten=no
+
+;
+;
+; CHOOSING A CDR "BACKEND" (what kind of output to generate)
+;
+; To choose a backend, you have to make sure either the right category is
+; defined in this file, or that the appropriate config file exists, and has the
+; proper definitions in it. If there are any problems, usually, the entry will
+; silently ignored, and you get no output.
+;
+; Also, please note that you can generate CDR records in as many formats as you
+; wish. If you configure 5 different CDR formats, then each event will be logged
+; in 5 different places! In the example config files, all formats are commented
+; out except for the cdr-csv format.
+;
+; Here are all the possible back ends:
+;
+; csv, custom, manager, odbc, pgsql, radius, sqlite, tds
+; (also, mysql is available via the asterisk-addons, due to licensing
+; requirements)
+; (please note, also, that other backends can be created, by creating
+; a new backend module in the source cdr/ directory!)
+;
+; Some of the modules required to provide these backends will not build or install
+; unless some dependency requirements are met. Examples of this are pgsql, odbc,
+; etc. If you are not getting output as you would expect, the first thing to do
+; is to run the command "make menuselect", and check what modules are available,
+; by looking in the "2. Call Detail Recording" option in the main menu. If your
+; backend is marked with XXX, you know that the "configure" command could not find
+; the required libraries for that option.
+;
+; To get CDRs to be logged to the plain-jane /var/log/asterisk/cdr-csv/Master.csv
+; file, define the [csv] category in this file. No database necessary. The example
+; config files are set up to provide this kind of output by default.
+;
+; To get custom csv CDR records, make sure the cdr_custom.conf file
+; is present, and contains the proper [mappings] section. The advantage to
+; using this backend, is that you can define which fields to output, and in
+; what order. By default, the example configs are set up to mimic the cdr-csv
+; output. If you don't make any changes to the mappings, you are basically generating
+; the same thing as cdr-csv, but expending more CPU cycles to do so!
+;
+; To get manager events generated, make sure the cdr_manager.conf file exists,
+; and the [general] section is defined, with the single variable 'enabled = yes'.
+;
+; For odbc, make sure all the proper libs are installed, that "make menuselect"
+; shows that the modules are available, and the cdr_odbc.conf file exists, and
+; has a [global] section with the proper variables defined.
+;
+; For pgsql, make sure all the proper libs are installed, that "make menuselect"
+; shows that the modules are available, and the cdr_pgsql.conf file exists, and
+; has a [global] section with the proper variables defined.
+;
+; For logging to radius databases, make sure all the proper libs are installed, that
+; "make menuselect" shows that the modules are available, and the [radius]
+; category is defined in this file, and in that section, make sure the 'radiuscfg'
+; variable is properly pointing to an existing radiusclient.conf file.
+;
+; For logging to sqlite databases, make sure the 'cdr.db' file exists in the log directory,
+; which is usually /var/log/asterisk. Of course, the proper libraries should be available
+; during the 'configure' operation.
+;
+; For tds logging, make sure the proper libraries are available during the 'configure'
+; phase, and that cdr_tds.conf exists and is properly set up with a [global] category.
+;
+; Also, remember, that if you wish to log CDR info to a database, you will have to define
+; a specific table in that databse to make things work! See the doc directory for more details
+; on how to create this table in each database.
+;
+
+[csv]
+usegmtime=yes ; log date/time in GMT. Default is "no"
+loguniqueid=yes ; log uniqueid. Default is "no
+loguserfield=yes ; log user field. Default is "no
+
+;[radius]
+;usegmtime=yes ; log date/time in GMT
+;loguniqueid=yes ; log uniqueid
+;loguserfield=yes ; log user field
+; Set this to the location of the radiusclient-ng configuration file
+; The default is /etc/radiusclient-ng/radiusclient.conf
+;radiuscfg => /usr/local/etc/radiusclient-ng/radiusclient.conf
diff --git a/trunk/configs/cdr_adaptive_odbc.conf.sample b/trunk/configs/cdr_adaptive_odbc.conf.sample
new file mode 100644
index 000000000..a16626e58
--- /dev/null
+++ b/trunk/configs/cdr_adaptive_odbc.conf.sample
@@ -0,0 +1,40 @@
+;
+; This configuration defines the connections and tables for which CDRs may
+; be populated. Each context specifies a different CDR table to be used.
+;
+; The columns in the tables should match up word-for-word (case-insensitive)
+; to the CDR variables set in the dialplan. The natural advantage to this
+; system is that beyond setting up the configuration file to tell you what
+; tables to look at, there isn't anything more to do beyond creating the
+; columns for the fields that you want, and populating the corresponding
+; CDR variables in the dialplan. For the builtin variables only, you may
+; create aliases for the real column name.
+;
+; Please note that after adding columns to the database, it is necessary to
+; reload this module to get the new column names and types read.
+;
+; Warning: if you specify two contexts with exactly the same connection and
+; table names, you will get duplicate records in that table. So be careful.
+;
+
+;[first]
+;connection=mysql1
+;table=cdr
+
+;[second]
+;connection=mysql1
+;table=extracdr
+
+;[third]
+;connection=sqlserver
+;table=AsteriskCDR
+;alias src => source
+;alias channel => source_channel
+;alias dst => dest
+;alias dstchannel => dest_channel
+;
+; Any filter specified MUST match exactly or the CDR will be discarded
+;filter accountcode => somename
+;filter src => 123
+
+
diff --git a/trunk/configs/cdr_custom.conf.sample b/trunk/configs/cdr_custom.conf.sample
new file mode 100644
index 000000000..8bc2cb34e
--- /dev/null
+++ b/trunk/configs/cdr_custom.conf.sample
@@ -0,0 +1,10 @@
+;
+; Mappings for custom config file
+;
+; to get your csv output in a format tailored to your liking, uncomment the following
+; and look for the output in the cdr-custom/Master.csv file (usually in /var/log/asterisk).
+;
+;
+;[mappings]
+;Master.csv => "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration)}","${CDR(billsec)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}"
+
diff --git a/trunk/configs/cdr_manager.conf.sample b/trunk/configs/cdr_manager.conf.sample
new file mode 100644
index 000000000..aa596b718
--- /dev/null
+++ b/trunk/configs/cdr_manager.conf.sample
@@ -0,0 +1,15 @@
+;
+; Asterisk Call Management CDR
+;
+[general]
+enabled = no
+
+; The "mappings" category can be used to define additional "key: value" pairs
+; that will be included in the manager event. (after AccountCode, Source, etc).
+;
+; Each line like "varname => label" will include a "label: ${CDR(varname)}"
+; in the generated event where ${CDR(varname)} its replaced with its value
+;
+;[mappings]
+;rate => Rate
+;carrier => Carrier
diff --git a/trunk/configs/cdr_odbc.conf.sample b/trunk/configs/cdr_odbc.conf.sample
new file mode 100644
index 000000000..6245e37eb
--- /dev/null
+++ b/trunk/configs/cdr_odbc.conf.sample
@@ -0,0 +1,12 @@
+;
+; cdr_odbc.conf
+;
+
+;[global]
+;dsn=MySQL-test
+;username=username
+;password=password
+;loguniqueid=yes
+;dispositionstring=yes
+;table=cdr ;"cdr" is default table name
+;usegmtime=no ; set to "yes" to log in GMT
diff --git a/trunk/configs/cdr_pgsql.conf.sample b/trunk/configs/cdr_pgsql.conf.sample
new file mode 100644
index 000000000..0784c7b08
--- /dev/null
+++ b/trunk/configs/cdr_pgsql.conf.sample
@@ -0,0 +1,9 @@
+; Sample Asterisk config file for CDR logging to PostgresSQL
+
+[global]
+;hostname=localhost
+;port=5432
+;dbname=asterisk
+;password=password
+;user=postgres
+;table=cdr ;SQL table where CDRs will be inserted
diff --git a/trunk/configs/cdr_sqlite3_custom.conf.sample b/trunk/configs/cdr_sqlite3_custom.conf.sample
new file mode 100644
index 000000000..55872b383
--- /dev/null
+++ b/trunk/configs/cdr_sqlite3_custom.conf.sample
@@ -0,0 +1,7 @@
+;
+; Mappings for custom config file
+;
+[master] ; currently, only file "master.db" is supported, with only one table at a time.
+table => cdr
+columns => calldate, clid, dcontext, channel, dstchannel, lastapp, lastdata, duration, billsec, disposition, amaflags, accountcode, uniqueid, userfield, test
+values => '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration)}','${CDR(billsec)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}'
diff --git a/trunk/configs/cdr_tds.conf.sample b/trunk/configs/cdr_tds.conf.sample
new file mode 100644
index 000000000..d8c7d075c
--- /dev/null
+++ b/trunk/configs/cdr_tds.conf.sample
@@ -0,0 +1,11 @@
+; Sample Asterisk config file for CDR logging to FreeTDS
+
+;[global]
+;hostname=fs.malico.loc
+;port=1433
+;dbname=MalicoHN
+;user=mangUsr
+;password=
+;charset=BIG5
+;table=cdr
+
diff --git a/trunk/configs/codecs.conf.sample b/trunk/configs/codecs.conf.sample
new file mode 100644
index 000000000..c8caeab60
--- /dev/null
+++ b/trunk/configs/codecs.conf.sample
@@ -0,0 +1,65 @@
+[speex]
+; CBR encoding quality [0..10]
+; used only when vbr = false
+quality => 3
+
+; codec complexity [0..10]
+; tradeoff between cpu/quality
+complexity => 2
+
+; perceptual enhancement [true / false]
+; improves clarity of decoded speech
+enhancement => true
+
+; voice activity detection [true / false]
+; reduces bitrate when no voice detected, used only for CBR
+; (implicit in VBR/ABR)
+vad => true
+
+; variable bit rate [true / false]
+; uses bit rate proportionate to voice complexity
+vbr => true
+
+; available bit rate [bps, 0 = off]
+; encoding quality modulated to match this target bit rate
+; not recommended with dtx or pp_vad - may cause bandwidth spikes
+abr => 0
+
+; VBR encoding quality [0-10]
+; floating-point values allowed
+vbr_quality => 4
+
+; discontinuous transmission [true / false]
+; stops transmitting completely when silence is detected
+; pp_vad is far more effective but more CPU intensive
+dtx => false
+
+; preprocessor configuration
+; these options only affect Speex v1.1.8 or newer
+
+; enable preprocessor [true / false]
+; allows dsp functionality below but incurs CPU overhead
+preprocess => false
+
+; preproc voice activity detection [true / false]
+; more advanced equivalent of DTX, based on voice frequencies
+pp_vad => false
+
+; preproc automatic gain control [true / false]
+pp_agc => false
+pp_agc_level => 8000
+
+; preproc denoiser [true / false]
+pp_denoise => false
+
+; preproc dereverb [true / false]
+pp_dereverb => false
+pp_dereverb_decay => 0.4
+pp_dereverb_level => 0.3
+
+
+[plc]
+; for all codecs which do not support native PLC
+; this determines whether to perform generic PLC
+; there is a minor performance penalty for this
+genericplc => true
diff --git a/trunk/configs/console.conf.sample b/trunk/configs/console.conf.sample
new file mode 100644
index 000000000..820a04dd9
--- /dev/null
+++ b/trunk/configs/console.conf.sample
@@ -0,0 +1,68 @@
+;
+; Configuration for chan_console, a cross-platform console channel driver.
+;
+
+[general]
+
+; Set this option to "yes" to enable automatically answering calls on the
+; console. This is very useful if the console is used as an intercom.
+; The default value is "no".
+;
+;autoanswer = no
+
+; Set the default context to use for outgoing calls. This can be overridden by
+; dialing some extension@context, unless the overridecontext option is enabled.
+; The default is "default".
+;
+;context = default
+
+; Set the default extension to use for outgoing calls. The default is "s".
+;
+;extension = s
+
+; Set the default CallerID for created channels.
+;
+;callerid = MyName Here <(256) 428-6000>
+
+; Set the default language for created channels.
+;
+;language = en
+
+; If you set overridecontext to 'yes', then the whole dial string
+; will be interpreted as an extension, which is extremely useful
+; to dial SIP, IAX and other extensions which use the '@' character.
+; The default is "no".
+;
+;overridecontext = no ; if 'no', the last @ will start the context
+ ; if 'yes' the whole string is an extension.
+
+
+; Default Music on Hold class to use when this channel is placed on hold in
+; the case that the music class is not set on the channel with
+; Set(CHANNEL(musicclass)=whatever) in the dialplan and the peer channel
+; putting this one on hold did not suggest a class to use.
+;
+;mohinterpret=default
+
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
+ ; Console channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The Console channel can't accept jitter,
+ ; thus an enabled jitterbuffer on the receive Console side will always
+ ; be used if the sending side can create jitter.
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a Console
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
diff --git a/trunk/configs/dnsmgr.conf.sample b/trunk/configs/dnsmgr.conf.sample
new file mode 100644
index 000000000..e34dbcf0a
--- /dev/null
+++ b/trunk/configs/dnsmgr.conf.sample
@@ -0,0 +1,5 @@
+[general]
+;enable=yes ; enable creation of managed DNS lookups
+ ; default is 'no'
+;refreshinterval=1200 ; refresh managed DNS lookups every <n> seconds
+ ; default is 300 (5 minutes) \ No newline at end of file
diff --git a/trunk/configs/dundi.conf.sample b/trunk/configs/dundi.conf.sample
new file mode 100644
index 000000000..f62210659
--- /dev/null
+++ b/trunk/configs/dundi.conf.sample
@@ -0,0 +1,261 @@
+;
+; DUNDi configuration file
+;
+; For more information about DUNDi, see http://www.dundi.com
+;
+;
+[general]
+;
+; The "general" section contains general parameters relating
+; to the operation of the dundi client and server.
+;
+; The first part should be your complete contact information
+; should someone else in your peer group need to contact you.
+;
+;department=Your Department
+;organization=Your Company, Inc.
+;locality=Your City
+;stateprov=ST
+;country=US
+;email=your@email.com
+;phone=+12565551212
+;
+;
+; Specify bind address and port number. Default is
+; 4520
+;
+;bindaddr=0.0.0.0
+;port=4520
+;
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of the tos parameter.
+;tos=ef
+;
+; Our entity identifier (Should generally be the MAC address of the
+; machine it's running on. Defaults to the first eth address, but you
+; can override it here, as long as you set it to the MAC of *something*
+; you own!)
+;
+;entityid=00:07:E9:3B:76:60
+;
+; Peers shall cache our query responses for the specified time,
+; given in seconds. Default is 3600.
+;
+;cachetime=3600
+;
+; This defines the max depth in which to search the DUNDi system.
+; Note that the maximum time that we will wait for a response is
+; (2000 + 200 * ttl) ms.
+;
+ttl=32
+;
+; If we don't get ACK to our DPDISCOVER within 2000ms, and autokill is set
+; to yes, then we cancel the whole thing (that's enough time for one
+; retransmission only). This is used to keep things from stalling for a long
+; time for a host that is not available, but would be ill advised for bad
+; connections. In addition to 'yes' or 'no' you can also specify a number
+; of milliseconds. See 'qualify' for individual peers to turn on for just
+; a specific peer.
+;
+autokill=yes
+;
+; pbx_dundi creates a rotating key called "secret", under the family
+; 'secretpath'. The default family is dundi (resulting in
+; the key being held at dundi/secret).
+;
+;secretpath=dundi
+;
+; The 'storehistory' option (also changeable at runtime with
+; 'dundi store history' and 'dundi no store history') will
+; cause the DUNDi engine to keep track of the last several
+; queries and the amount of time each query took to execute
+; for the purpose of tracking slow nodes. This option is
+; off by default due to performance impacts.
+;
+;storehistory=yes
+
+[mappings]
+;
+; The "mappings" section maps DUNDi contexts
+; to contexts on the local asterisk system. Remember
+; that numbers that are made available under the e164
+; DUNDi context are regulated by the DUNDi General Peering
+; Agreement (GPA) if you are a member of the DUNDi E.164
+; Peering System.
+;
+; dundi_context => local_context,weight,tech,dest[,options]]
+;
+; 'dundi_context' is the name of the context being requested
+; within the DUNDi request
+;
+; 'local_context' is the name of the context on the local system
+; in which numbers can be looked up for which responses shall be given.
+;
+; 'weight' is the weight to use for the responses provided from this
+; mapping. The number must be >= 0 and < 60000. Since it is totally
+; valid to receive multiple responses to a query, responses received
+; with a lower weight are tried first. Note that the weight has a
+; special meaning in the e164 context - see the GPA for more details.
+;
+; 'tech' is the technology to use (IAX, SIP, H323)
+;
+; 'dest' is the destination to supply for reaching that number. The
+; following variables can be used in the destination string and will
+; be automatically substituted:
+; ${NUMBER}: The number being requested
+; ${IPADDR}: The IP address to connect to
+; ${SECRET}: The current rotating secret key to be used
+;
+; Further options may include:
+;
+; nounsolicited: No unsolicited calls of any type permitted via this
+; route
+; nocomunsolicit: No commercial unsolicited calls permitted via
+; this route
+; residential: This number is known to be a residence
+; commercial: This number is known to be a business
+; mobile: This number is known to be a mobile phone
+; nocomunsolicit: No commercial unsolicited calls permitted via
+; this route
+; nopartial: Do not search for partial matches
+;
+; There *must* exist an entry in mappings for DUNDi to respond
+; to any request, although it may be empty.
+;
+;e164 => dundi-e164-canonical,0,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
+;e164 => dundi-e164-customers,100,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
+;e164 => dundi-e164-via-pstn,400,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
+
+;digexten => default,0,IAX2,guest@lappy/${NUMBER}
+;asdf =>
+
+;
+; Weights for mappings can be set a few different ways:
+;
+; 1) It can be set as a static number.
+;testmap1 => context1,222,IAX2,guest@peer1/${NUMBER}
+;
+; 2) It can be an Asterisk global variable.
+;testmap2 => context2,${DUNDITESTVAR},IAX2,guest@peer2${NUMBER}
+;
+; 3) It can be retrieved using a dialplan function. This can be extremely
+; useful if you want to let an external script decide what the weight
+; in a response shouuld be.
+;testmap3 => context3,${SHELL(echo 123)},IAX2,guest@peer3/${NUMBER}
+;
+; Note than when using a global variable or dialplan function to set the
+; weight for a mapping, that response caching should be disabled if you
+; plan for these values to change frequently at all. If the results are
+; cached, then any change in value will not take effect until the cache
+; has expired.
+;
+
+;
+; The remaining sections represent the peers
+; that we fundamentally trust. The section name
+; represents the name and optionally at a specific
+; DUNDi context if you want the trust to be established
+; for only a specific DUNDi context.
+;
+; inkey - What key they will be authenticating to us with
+;
+; outkey - What key we use to authenticate to them
+;
+; host - What their host is
+;
+; order - What search order to use. May be 'primary', 'secondary',
+; 'tertiary' or 'quartiary'. In large systems, it is beneficial
+; to only query one up-stream host in order to maximize caching
+; value. Adding one with primary and one with secondary gives you
+; redundancy without sacrificing performance.
+;
+; include - Includes this peer when searching a particular context
+; for lookup (set "all" to perform all lookups with that
+; host. This is also the context in which peers are permitted
+; to precache.
+;
+; noinclude - Disincludes this peer when searching a particular context
+; for lookup (set "all" to perform no lookups with that
+; host.
+;
+; permit - Permits this peer to search a given DUNDi context on
+; the local system. Set "all" to permit this host to
+; lookup all contexts. This is also a context for which
+; we will create/forward PRECACHE commands.
+;
+; deny - Denies this peer to search a given DUNDi context on
+; the local system. Set "all" to deny this host to
+; lookup all contexts.
+;
+; model - inbound, outbound, or symmetric for whether we receive
+; requests only, transmit requests only, or do both.
+;
+; precache - Utilize/Permit precaching with this peer (to pre
+; cache means to provide an answer when no request
+; was made and is used so that machines with few
+; routes can push those routes up a to a higher level).
+; outgoing means we send precache routes to this peer,
+; incoming means we permit this peer to send us
+; precache routes. symmetric means we do both.
+;
+; Note: You cannot mix symmetric/outbound model with symmetric/inbound
+; precache, nor can you mix symmetric/inbound model with symmetric/outbound
+; precache.
+;
+;
+; The '*' peer is special and matches an unspecified entity
+;
+
+;
+; Sample Primary e164 DUNDi peer
+;
+;[00:50:8B:F3:75:BB]
+;model = symmetric
+;host = 64.215.96.114
+;inkey = digium
+;outkey = misery
+;include = e164
+;permit = e164
+;qualify = yes
+
+;
+; Sample Secondary e164 DUNDi peer
+;
+;[00:A0:C9:96:92:84]
+;model = symmetric
+;host = misery.digium.com
+;inkey = misery
+;outkey = ourkey
+;include = e164
+;permit = e164
+;qualify = yes
+;order = secondary
+
+;
+; Sample "push mode" downstream host
+;
+;[00:0C:76:96:75:28]
+;model = inbound
+;host = dynamic
+;precache = inbound
+;inkey = littleguy
+;outkey = ourkey
+;include = e164 ; In this case used only for precaching
+;permit = e164
+;qualify = yes
+
+;
+; Sample "push mode" upstream host
+;
+;[00:07:E9:3B:76:60]
+;model = outbound
+;precache = outbound
+;host = 216.207.245.34
+;register = yes
+;inkey = dhcp34
+;permit = all ; In this case used only for precaching
+;include = all
+;qualify = yes
+;outkey=foo
+
+;[*]
+;
diff --git a/trunk/configs/enum.conf.sample b/trunk/configs/enum.conf.sample
new file mode 100644
index 000000000..39c723175
--- /dev/null
+++ b/trunk/configs/enum.conf.sample
@@ -0,0 +1,22 @@
+;
+; ENUM Configuration for resolving phone numbers over DNS
+;
+; Sample config for Asterisk
+; This file is reloaded at "module reload enum" in the CLI
+;
+[general]
+;
+; The search list for domains may be customized. Domains are searched
+; in the order they are listed here.
+;
+search => e164.arpa
+;
+; If you'd like to use the E.164.org public ENUM registry in addition
+; to the official e164.arpa one, uncomment the following line
+;
+;search => e164.org
+;
+; As there are more H323 drivers available you have to select to which
+; drive a H323 URI will map. Default is "H323".
+;
+h323driver => H323
diff --git a/trunk/configs/extconfig.conf.sample b/trunk/configs/extconfig.conf.sample
new file mode 100644
index 000000000..4c3899fcd
--- /dev/null
+++ b/trunk/configs/extconfig.conf.sample
@@ -0,0 +1,63 @@
+;
+; Static and realtime external configuration
+; engine configuration
+;
+; Please read doc/extconfig.txt for basic table
+; formatting information.
+;
+[settings]
+;
+; Static configuration files:
+;
+; file.conf => driver,database[,table]
+;
+; maps a particular configuration file to the given
+; database driver, database and table (or uses the
+; name of the file as the table if not specified)
+;
+;uncomment to load queues.conf via the odbc engine.
+;
+;queues.conf => odbc,asterisk,ast_config
+;extensions.conf => sqlite,asterisk,ast_config
+;
+; The following files CANNOT be loaded from Realtime storage:
+; asterisk.conf
+; extconfig.conf (this file)
+; logger.conf
+;
+; Additionally, the following files cannot be loaded from
+; Realtime storage unless the storage driver is loaded
+; early using 'preload' statements in modules.conf:
+; manager.conf
+; cdr.conf
+; rtp.conf
+;
+;
+; Realtime configuration engine
+;
+; maps a particular family of realtime
+; configuration to a given database driver,
+; database and table (or uses the name of
+; the family if the table is not specified
+;
+;example => odbc,asterisk,alttable
+;example2 => ldap,"dc=oxymium,dc=net",example2
+;
+; "odbc" is shown in the examples below, but is not the only valid realtime
+; engine. There is:
+; odbc ... res_config_odbc
+; sqlite ... res_config_sqlite
+; pgsql ... res_config_pgsql
+;
+;iaxusers => odbc,asterisk
+;iaxpeers => odbc,asterisk
+;sipusers => odbc,asterisk
+;sippeers => odbc,asterisk
+;sipregs => odbc,asterisk
+;voicemail => odbc,asterisk
+;extensions => odbc,asterisk
+;queues => odbc,asterisk
+;queue_members => odbc,asterisk
+;musiconhold => mysql,asterisk
+;queue_log => mysql,aasterisk
+
diff --git a/trunk/configs/extensions.ael.sample b/trunk/configs/extensions.ael.sample
new file mode 100644
index 000000000..89436dc21
--- /dev/null
+++ b/trunk/configs/extensions.ael.sample
@@ -0,0 +1,448 @@
+//
+// Example AEL config file
+//
+//
+// Static extension configuration file, used by
+// the pbx_ael module. This is where you configure all your
+// inbound and outbound calls in Asterisk.
+//
+// This configuration file is reloaded
+// - With the "ael reload" command in the CLI
+// - With the "reload" command (that reloads everything) in the CLI
+
+// The "Globals" category contains global variables that can be referenced
+// in the dialplan by using the GLOBAL dialplan function:
+// ${GLOBAL(VARIABLE)}
+// ${${GLOBAL(VARIABLE)}} or ${text${GLOBAL(VARIABLE)}} or any hybrid
+// Unix/Linux environmental variables are reached with the ENV dialplan
+// function: ${ENV(VARIABLE)}
+//
+
+globals {
+ CONSOLE="Console/dsp"; // Console interface for demo
+ //CONSOLE=Zap/1
+ //CONSOLE=Phone/phone0
+ IAXINFO=guest; // IAXtel username/password
+ //IAXINFO="myuser:mypass";
+ TRUNK="Zap/g2"; // Trunk interface
+ //
+ // Note the 'g2' in the TRUNK variable above. It specifies which group (defined
+ // in zapata.conf) to dial, i.e. group 2, and how to choose a channel to use in
+ // the specified group. The four possible options are:
+ //
+ // g: select the lowest-numbered non-busy Zap channel
+ // (aka. ascending sequential hunt group).
+ // G: select the highest-numbered non-busy Zap channel
+ // (aka. descending sequential hunt group).
+ // r: use a round-robin search, starting at the next highest channel than last
+ // time (aka. ascending rotary hunt group).
+ // R: use a round-robin search, starting at the next lowest channel than last
+ // time (aka. descending rotary hunt group).
+ //
+ TRUNKMSD=1; // MSD digits to strip (usually 1 or 0)
+ //TRUNK=IAX2/user:pass@provider
+};
+
+//
+// Any category other than "General" and "Globals" represent
+// extension contexts, which are collections of extensions.
+//
+// Extension names may be numbers, letters, or combinations
+// thereof. If an extension name is prefixed by a '_'
+// character, it is interpreted as a pattern rather than a
+// literal. In patterns, some characters have special meanings:
+//
+// X - any digit from 0-9
+// Z - any digit from 1-9
+// N - any digit from 2-9
+// [1235-9] - any digit in the brackets (in this example, 1,2,3,5,6,7,8,9)
+// . - wildcard, matches anything remaining (e.g. _9011. matches
+// anything starting with 9011 excluding 9011 itself)
+// ! - wildcard, causes the matching process to complete as soon as
+// it can unambiguously determine that no other matches are possible
+//
+// For example the extension _NXXXXXX would match normal 7 digit dialings,
+// while _1NXXNXXXXXX would represent an area code plus phone number
+// preceded by a one.
+//
+// Each step of an extension is ordered by priority, which must
+// always start with 1 to be considered a valid extension. The priority
+// "next" or "n" means the previous priority plus one, regardless of whether
+// the previous priority was associated with the current extension or not.
+// The priority "same" or "s" means the same as the previously specified
+// priority, again regardless of whether the previous entry was for the
+// same extension. Priorities may be immediately followed by a plus sign
+// and another integer to add that amount (most useful with 's' or 'n').
+// Priorities may then also have an alias, or label, in
+// parenthesis after their name which can be used in goto situations
+//
+// Contexts contain several lines, one for each step of each
+// extension, which can take one of two forms as listed below,
+// with the first form being preferred. One may include another
+// context in the current one as well, optionally with a
+// date and time. Included contexts are included in the order
+// they are listed.
+//
+//context name {
+// exten-name => {
+// application(arg1,arg2,...);
+//
+// Timing list for includes is
+//
+// <time range>|<days of week>|<days of month>|<months>
+//
+// includes {
+// daytime|9:00-17:00|mon-fri|*|*;
+// };
+//
+// ignorepat can be used to instruct drivers to not cancel dialtone upon
+// receipt of a particular pattern. The most commonly used example is
+// of course '9' like this:
+//
+// ignorepat => 9;
+//
+// so that dialtone remains even after dialing a 9.
+//};
+
+
+//
+// Sample entries for extensions.conf
+//
+//
+context ael-dundi-e164-canonical {
+ //
+ // List canonical entries here
+ //
+ // 12564286000 => &ael-std-exten(6000,IAX2/foo);
+ // _125642860XX => Dial(IAX2/otherbox/${EXTEN:7});
+};
+
+context ael-dundi-e164-customers {
+ //
+ // If you are an ITSP or Reseller, list your customers here.
+ //
+ //_12564286000 => Dial(SIP/customer1);
+ //_12564286001 => Dial(IAX2/customer2);
+};
+
+context ael-dundi-e164-via-pstn {
+ //
+ // If you are freely delivering calls to the PSTN, list them here
+ //
+ //_1256428XXXX => Dial(Zap/g2/${EXTEN:7}); // Expose all of 256-428
+ //_1256325XXXX => Dial(Zap/g2/${EXTEN:7}); // Ditto for 256-325
+};
+
+context ael-dundi-e164-local {
+ //
+ // Context to put your dundi IAX2 or SIP user in for
+ // full access
+ //
+ includes {
+ ael-dundi-e164-canonical;
+ ael-dundi-e164-customers;
+ ael-dundi-e164-via-pstn;
+ };
+};
+
+context ael-dundi-e164-switch {
+ //
+ // Just a wrapper for the switch
+ //
+
+ switches {
+ DUNDi/e164;
+ };
+};
+
+context ael-dundi-e164-lookup {
+ //
+ // Locally to lookup, try looking for a local E.164 solution
+ // then try DUNDi if we don't have one.
+ //
+ includes {
+ ael-dundi-e164-local;
+ ael-dundi-e164-switch;
+ };
+ //
+};
+
+//
+// DUNDi can also be implemented as a Macro instead of using
+// the Local channel driver.
+//
+macro ael-dundi-e164(exten) {
+//
+// ARG1 is the extension to Dial
+//
+ goto ${exten}|1;
+ return;
+};
+
+//
+// Here are the entries you need to participate in the IAXTEL
+// call routing system. Most IAXTEL numbers begin with 1-700, but
+// there are exceptions. For more information, and to sign
+// up, please go to www.gnophone.com or www.iaxtel.com
+//
+context ael-iaxtel700 {
+ _91700XXXXXXX => Dial(IAX2/${IAXINFO}@iaxtel.com/${EXTEN:1}@iaxtel);
+};
+
+//
+// The SWITCH statement permits a server to share the dialplan with
+// another server. Use with care: Reciprocal switch statements are not
+// allowed (e.g. both A -> B and B -> A), and the switched server needs
+// to be on-line or else dialing can be severly delayed.
+//
+context ael-iaxprovider {
+ switches {
+ // IAX2/user:[key]@myserver/mycontext;
+ };
+};
+
+context ael-trunkint {
+ //
+ // International long distance through trunk
+ //
+ includes {
+ ael-dundi-e164-lookup;
+ };
+ _9011. => {
+ &ael-dundi-e164(${EXTEN:4});
+ Dial(${TRUNK}/${EXTEN:${TRUNKMSD}});
+ };
+};
+
+context ael-trunkld {
+ //
+ // Long distance context accessed through trunk
+ //
+ includes {
+ ael-dundi-e164-lookup;
+ };
+ _91NXXNXXXXXX => {
+ &ael-dundi-e164(${EXTEN:1});
+ Dial(${TRUNK}/${EXTEN:${TRUNKMSD}});
+ };
+};
+
+context ael-trunklocal {
+ //
+ // Local seven-digit dialing accessed through trunk interface
+ //
+ _9NXXXXXX => {
+ Dial(${TRUNK}/${EXTEN:${TRUNKMSD}});
+ };
+};
+
+context ael-trunktollfree {
+ //
+ // Long distance context accessed through trunk interface
+ //
+
+ _91800NXXXXXX => Dial(${TRUNK}/${EXTEN:${TRUNKMSD}});
+ _91888NXXXXXX => Dial(${TRUNK}/${EXTEN:${TRUNKMSD}});
+ _91877NXXXXXX => Dial(${TRUNK}/${EXTEN:${TRUNKMSD}});
+ _91866NXXXXXX => Dial(${TRUNK}/${EXTEN:${TRUNKMSD}});
+};
+
+context ael-international {
+ //
+ // Master context for international long distance
+ //
+ ignorepat => 9;
+ includes {
+ ael-longdistance;
+ ael-trunkint;
+ };
+};
+
+context ael-longdistance {
+ //
+ // Master context for long distance
+ //
+ ignorepat => 9;
+ includes {
+ ael-local;
+ ael-trunkld;
+ };
+};
+
+context ael-local {
+ //
+ // Master context for local, toll-free, and iaxtel calls only
+ //
+ ignorepat => 9;
+ includes {
+ ael-default;
+ ael-trunklocal;
+ ael-iaxtel700;
+ ael-trunktollfree;
+ ael-iaxprovider;
+ };
+};
+
+//
+// You can use an alternative switch type as well, to resolve
+// extensions that are not known here, for example with remote
+// IAX switching you transparently get access to the remote
+// Asterisk PBX
+//
+// switch => IAX2/user:password@bigserver/local
+//
+// An "lswitch" is like a switch but is literal, in that
+// variable substitution is not performed at load time
+// but is passed to the switch directly (presumably to
+// be substituted in the switch routine itself)
+//
+// lswitch => Loopback/12${EXTEN}@othercontext
+//
+// An "eswitch" is like a switch but the evaluation of
+// variable substitution is performed at runtime before
+// being passed to the switch routine.
+//
+// eswitch => IAX2/context@${CURSERVER}
+
+
+macro ael-std-exten-ael( ext , dev ) {
+ Dial(${dev}/${ext},20);
+ switch(${DIALSTATUS}) {
+ case BUSY:
+ Voicemail(${ext},b);
+ break;
+ default:
+ Voicemail(${ext},u);
+ };
+ catch a {
+ VoiceMailMain(${ext});
+ return;
+ };
+ return;
+};
+
+context ael-demo {
+ s => {
+ Wait(1);
+ Answer();
+ Set(TIMEOUT(digit)=5);
+ Set(TIMEOUT(response)=10);
+restart:
+ Background(demo-congrats);
+instructions:
+ for (x=0; ${x} < 3; x=${x} + 1) {
+ Background(demo-instruct);
+ WaitExten();
+ };
+ };
+ 2 => {
+ Background(demo-moreinfo);
+ goto s|instructions;
+ };
+ 3 => {
+ Set(LANGUAGE()=fr);
+ goto s|restart;
+ };
+ 1000 => {
+ goto ael-default|s|1;
+ };
+ 500 => {
+ Playback(demo-abouttotry);
+ Dial(IAX2/guest@misery.digium.com/s@default);
+ Playback(demo-nogo);
+ goto s|instructions;
+ };
+ 600 => {
+ Playback(demo-echotest);
+ Echo();
+ Playback(demo-echodone);
+ goto s|instructions;
+ };
+ _1234 => &ael-std-exten-ael(${EXTEN}, "IAX2");
+ 8500 => {
+ VoicemailMain();
+ goto s|instructions;
+ };
+ # => {
+ Playback(demo-thanks);
+ Hangup();
+ };
+ t => goto #|1;
+ i => Playback(invalid);
+};
+
+
+//
+// If you wish to use AEL for your default context, remove it
+// from extensions.conf (or change its name or comment it out)
+// and then uncomment the one here.
+//
+
+context ael-default {
+
+// By default we include the demo. In a production system, you
+// probably don't want to have the demo there.
+
+ includes {
+ ael-demo;
+ };
+//
+// Extensions like the two below can be used for FWD, Nikotel, sipgate etc.
+// Note that you must have a [sipprovider] section in sip.conf whereas
+// the otherprovider.net example does not require such a peer definition
+//
+//_41X. => Dial(SIP/${EXTEN:2}@sipprovider,,r);
+//_42X. => Dial(SIP/user:passwd@${EXTEN:2}@otherprovider.net,30,rT);
+
+// Real extensions would go here. Generally you want real extensions to be
+// 4 or 5 digits long (although there is no such requirement) and start with a
+// single digit that is fairly large (like 6 or 7) so that you have plenty of
+// room to overlap extensions and menu options without conflict. You can alias
+// them with names, too, and use global variables
+
+// 6245 => {
+// hint(SIP/Grandstream1&SIP/Xlite1,Joe Schmoe); // Channel hints for presence
+// Dial(SIP/Grandstream1,20,rt); // permit transfer
+// Dial(${HINT}/5245},20,rtT); // Use hint as listed
+// switch(${DIALSTATUS}) {
+// case BUSY:
+// Voicemail(6245,b);
+// return;
+// default:
+// Voicemail(6245,u);
+// return;
+// };
+// };
+
+// 6361 => Dial(IAX2/JaneDoe,,rm); // ring without time limit
+// 6389 => Dial(MGCP/aaln/1@192.168.0.14);
+// 6394 => Dial(Local/6275/n); // this will dial ${MARK}
+
+// 6275 => &ael-stdexten(6275,${MARK}); // assuming ${MARK} is something like Zap/2
+// mark => goto 6275|1; // alias mark to 6275
+// 6536 => &ael-stdexten(6236,${WIL}); // Ditto for wil
+// wil => goto 6236|1;
+//
+// Some other handy things are an extension for checking voicemail via
+// voicemailmain
+//
+// 8500 => {
+// VoicemailMain();
+// Hangup();
+// };
+//
+// Or a conference room (you'll need to edit meetme.conf to enable this room)
+//
+// 8600 => Meetme(1234);
+//
+// Or playing an announcement to the called party, as soon it answers
+//
+// 8700 => Dial(${MARK},30,A(/path/to/my/announcemsg))
+//
+// For more information on applications, just type "show applications" at your
+// friendly Asterisk CLI prompt.
+//
+// 'show application <command>' will show details of how you
+// use that particular application in this file, the dial plan.
+//
+}
diff --git a/trunk/configs/extensions.conf.sample b/trunk/configs/extensions.conf.sample
new file mode 100644
index 000000000..c6f9ad046
--- /dev/null
+++ b/trunk/configs/extensions.conf.sample
@@ -0,0 +1,614 @@
+; extensions.conf - the Asterisk dial plan
+;
+; Static extension configuration file, used by
+; the pbx_config module. This is where you configure all your
+; inbound and outbound calls in Asterisk.
+;
+; This configuration file is reloaded
+; - With the "dialplan reload" command in the CLI
+; - With the "reload" command (that reloads everything) in the CLI
+
+;
+; The "General" category is for certain variables.
+;
+[general]
+;
+; If static is set to no, or omitted, then the pbx_config will rewrite
+; this file when extensions are modified. Remember that all comments
+; made in the file will be lost when that happens.
+;
+; XXX Not yet implemented XXX
+;
+static=yes
+;
+; if static=yes and writeprotect=no, you can save dialplan by
+; CLI command "dialplan save" too
+;
+writeprotect=no
+;
+; If autofallthrough is set, then if an extension runs out of
+; things to do, it will terminate the call with BUSY, CONGESTION
+; or HANGUP depending on Asterisk's best guess. This is the default.
+;
+; If autofallthrough is not set, then if an extension runs out of
+; things to do, Asterisk will wait for a new extension to be dialed
+; (this is the original behavior of Asterisk 1.0 and earlier).
+;
+;autofallthrough=no
+;
+;
+;
+; If extenpatternmatchnew is set (true, yes, etc), then a new algorithm that uses
+; a Trie to find the best matching pattern is used. In dialplans
+; with more than about 20-40 extensions in a single context, this
+; new algorithm can provide a noticeable speedup.
+; With 50 extensions, the speedup is 1.32x
+; with 88 extensions, the speedup is 2.23x
+; with 138 extensions, the speedup is 3.44x
+; with 238 extensions, the speedup is 5.8x
+; with 438 extensions, the speedup is 10.4x
+; With 1000 extensions, the speedup is ~25x
+; with 10,000 extensions, the speedup is 374x
+; Basically, the new algorithm provides a flat response
+; time, no matter the number of extensions.
+;
+; By default, the old pattern matcher is used.
+;
+; ****This is a new feature! *********************
+; The new pattern matcher is for the brave, the bold, and
+; the desperate. If you have large dialplans (more than about 50 extensions
+; in a context), and/or high call volume, you might consider setting
+; this value to "yes" !!
+; Please, if you try this out, and are forced to return to the
+; old pattern matcher, please report your reasons in a bug report
+; on bugs.digium.com. We have made good progress in providing something
+; compatible with the old matcher; help us finish the job!
+;
+; This value can be switched at runtime using the cli command "dialplan set extenpatternmatchnew true"
+; or "dialplan set extenpatternmatchnew false", so you can experiment to your hearts content.
+;
+;extenpatternmatchnew=no
+;
+; If clearglobalvars is set, global variables will be cleared
+; and reparsed on an extensions reload, or Asterisk reload.
+;
+; If clearglobalvars is not set, then global variables will persist
+; through reloads, and even if deleted from the extensions.conf or
+; one of its included files, will remain set to the previous value.
+;
+; NOTE: A complication sets in, if you put your global variables into
+; the AEL file, instead of the extensions.conf file. With clearglobalvars
+; set, a "reload" will often leave the globals vars cleared, because it
+; is not unusual to have extensions.conf (which will have no globals)
+; load after the extensions.ael file (where the global vars are stored).
+; So, with "reload" in this particular situation, first the AEL file will
+; clear and then set all the global vars, then, later, when the extensions.conf
+; file is loaded, the global vars are all cleared, and then not set, because
+; they are not stored in the extensions.conf file.
+;
+clearglobalvars=no
+;
+; If priorityjumping is set to 'yes', then applications that support
+; 'jumping' to a different priority based on the result of their operations
+; will do so (this is backwards compatible behavior with pre-1.2 releases
+; of Asterisk). Individual applications can also be requested to do this
+; by passing a 'j' option in their arguments.
+;
+;priorityjumping=yes
+;
+; User context is where entries from users.conf are registered. The
+; default value is 'default'
+;
+;userscontext=default
+;
+; You can include other config files, use the #include command
+; (without the ';'). Note that this is different from the "include" command
+; that includes contexts within other contexts. The #include command works
+; in all asterisk configuration files.
+;#include "filename.conf"
+
+; The "Globals" category contains global variables that can be referenced
+; in the dialplan with the GLOBAL dialplan function:
+; ${GLOBAL(VARIABLE)}
+; ${${GLOBAL(VARIABLE)}} or ${text${GLOBAL(VARIABLE)}} or any hybrid
+; Unix/Linux environmental variables can be reached with the ENV dialplan
+; function: ${ENV(VARIABLE)}
+;
+[globals]
+CONSOLE=Console/dsp ; Console interface for demo
+;CONSOLE=Zap/1
+;CONSOLE=Phone/phone0
+IAXINFO=guest ; IAXtel username/password
+;IAXINFO=myuser:mypass
+TRUNK=Zap/g2 ; Trunk interface
+;
+; Note the 'g2' in the TRUNK variable above. It specifies which group (defined
+; in zapata.conf) to dial, i.e. group 2, and how to choose a channel to use in
+; the specified group. The four possible options are:
+;
+; g: select the lowest-numbered non-busy Zap channel
+; (aka. ascending sequential hunt group).
+; G: select the highest-numbered non-busy Zap channel
+; (aka. descending sequential hunt group).
+; r: use a round-robin search, starting at the next highest channel than last
+; time (aka. ascending rotary hunt group).
+; R: use a round-robin search, starting at the next lowest channel than last
+; time (aka. descending rotary hunt group).
+;
+TRUNKMSD=1 ; MSD digits to strip (usually 1 or 0)
+;TRUNK=IAX2/user:pass@provider
+
+;
+; Any category other than "General" and "Globals" represent
+; extension contexts, which are collections of extensions.
+;
+; Extension names may be numbers, letters, or combinations
+; thereof. If an extension name is prefixed by a '_'
+; character, it is interpreted as a pattern rather than a
+; literal. In patterns, some characters have special meanings:
+;
+; X - any digit from 0-9
+; Z - any digit from 1-9
+; N - any digit from 2-9
+; [1235-9] - any digit in the brackets (in this example, 1,2,3,5,6,7,8,9)
+; . - wildcard, matches anything remaining (e.g. _9011. matches
+; anything starting with 9011 excluding 9011 itself)
+; ! - wildcard, causes the matching process to complete as soon as
+; it can unambiguously determine that no other matches are possible
+;
+; For example the extension _NXXXXXX would match normal 7 digit dialings,
+; while _1NXXNXXXXXX would represent an area code plus phone number
+; preceded by a one.
+;
+; Each step of an extension is ordered by priority, which must
+; always start with 1 to be considered a valid extension. The priority
+; "next" or "n" means the previous priority plus one, regardless of whether
+; the previous priority was associated with the current extension or not.
+; The priority "same" or "s" means the same as the previously specified
+; priority, again regardless of whether the previous entry was for the
+; same extension. Priorities may be immediately followed by a plus sign
+; and another integer to add that amount (most useful with 's' or 'n').
+; Priorities may then also have an alias, or label, in
+; parenthesis after their name which can be used in goto situations
+;
+; Contexts contain several lines, one for each step of each
+; extension, which can take one of two forms as listed below,
+; with the first form being preferred. One may include another
+; context in the current one as well, optionally with a
+; date and time. Included contexts are included in the order
+; they are listed.
+;
+;[context]
+;exten => someexten,{priority|label{+|-}offset}[(alias)],application(arg1,arg2,...)
+;exten => someexten,{priority|label{+|-}offset}[(alias)],application,arg1|arg2...
+;
+; Timing list for includes is
+;
+; <time range>|<days of week>|<days of month>|<months>
+;
+; Note that ranges may be specified to wrap around the ends. Also, minutes are
+; fine-grained only down to the closest even minute.
+;
+;include => daytime|9:00-17:00|mon-fri|*|*
+;include => weekend|*|sat-sun|*|*
+;include => weeknights|17:02-8:58|mon-fri|*|*
+;
+; ignorepat can be used to instruct drivers to not cancel dialtone upon
+; receipt of a particular pattern. The most commonly used example is
+; of course '9' like this:
+;
+;ignorepat => 9
+;
+; so that dialtone remains even after dialing a 9.
+;
+
+;
+; Sample entries for extensions.conf
+;
+;
+[dundi-e164-canonical]
+;
+; List canonical entries here
+;
+;exten => 12564286000,1,Macro(stdexten,6000,IAX2/foo)
+;exten => _125642860XX,1,Dial(IAX2/otherbox/${EXTEN:7})
+
+[dundi-e164-customers]
+;
+; If you are an ITSP or Reseller, list your customers here.
+;
+;exten => _12564286000,1,Dial(SIP/customer1)
+;exten => _12564286001,1,Dial(IAX2/customer2)
+
+[dundi-e164-via-pstn]
+;
+; If you are freely delivering calls to the PSTN, list them here
+;
+;exten => _1256428XXXX,1,Dial(Zap/g2/${EXTEN:7}) ; Expose all of 256-428
+;exten => _1256325XXXX,1,Dial(Zap/g2/${EXTEN:7}) ; Ditto for 256-325
+
+[dundi-e164-local]
+;
+; Context to put your dundi IAX2 or SIP user in for
+; full access
+;
+include => dundi-e164-canonical
+include => dundi-e164-customers
+include => dundi-e164-via-pstn
+
+[dundi-e164-switch]
+;
+; Just a wrapper for the switch
+;
+switch => DUNDi/e164
+
+[dundi-e164-lookup]
+;
+; Locally to lookup, try looking for a local E.164 solution
+; then try DUNDi if we don't have one.
+;
+include => dundi-e164-local
+include => dundi-e164-switch
+;
+; DUNDi can also be implemented as a Macro instead of using
+; the Local channel driver.
+;
+[macro-dundi-e164]
+;
+; ARG1 is the extension to Dial
+;
+; Extension "s" is not a wildcard extension that matches "anything".
+; In macros, it is the start extension. In most other cases,
+; you have to goto "s" to execute that extension.
+;
+; For wildcard matches, see above - all pattern matches start with
+; an underscore.
+exten => s,1,Goto(${ARG1},1)
+include => dundi-e164-lookup
+
+;
+; Here are the entries you need to participate in the IAXTEL
+; call routing system. Most IAXTEL numbers begin with 1-700, but
+; there are exceptions. For more information, and to sign
+; up, please go to www.gnophone.com or www.iaxtel.com
+;
+[iaxtel700]
+exten => _91700XXXXXXX,1,Dial(IAX2/${GLOBAL(IAXINFO)}@iaxtel.com/${EXTEN:1}@iaxtel)
+
+;
+; The SWITCH statement permits a server to share the dialplan with
+; another server. Use with care: Reciprocal switch statements are not
+; allowed (e.g. both A -> B and B -> A), and the switched server needs
+; to be on-line or else dialing can be severly delayed.
+;
+[iaxprovider]
+;switch => IAX2/user:[key]@myserver/mycontext
+
+[trunkint]
+;
+; International long distance through trunk
+;
+exten => _9011.,1,Macro(dundi-e164,${EXTEN:4})
+exten => _9011.,n,Dial(${GLOBAL(TRUNK)}/${EXTEN:${GLOBAL(TRUNKMSD)}})
+
+[trunkld]
+;
+; Long distance context accessed through trunk
+;
+exten => _91NXXNXXXXXX,1,Macro(dundi-e164,${EXTEN:1})
+exten => _91NXXNXXXXXX,n,Dial(${GLOBAL(TRUNK)}/${EXTEN:${GLOBAL(TRUNKMSD)}})
+
+[trunklocal]
+;
+; Local seven-digit dialing accessed through trunk interface
+;
+exten => _9NXXXXXX,1,Dial(${GLOBAL(TRUNK)}/${EXTEN:${GLOBAL(TRUNKMSD)}})
+
+[trunktollfree]
+;
+; Long distance context accessed through trunk interface
+;
+exten => _91800NXXXXXX,1,Dial(${GLOBAL(TRUNK)}/${EXTEN:${GLOBAL(TRUNKMSD)}})
+exten => _91888NXXXXXX,1,Dial(${GLOBAL(TRUNK)}/${EXTEN:${GLOBAL(TRUNKMSD)}})
+exten => _91877NXXXXXX,1,Dial(${GLOBAL(TRUNK)}/${EXTEN:${GLOBAL(TRUNKMSD)}})
+exten => _91866NXXXXXX,1,Dial(${GLOBAL(TRUNK)}/${EXTEN:${GLOBAL(TRUNKMSD)}})
+
+[international]
+;
+; Master context for international long distance
+;
+ignorepat => 9
+include => longdistance
+include => trunkint
+
+[longdistance]
+;
+; Master context for long distance
+;
+ignorepat => 9
+include => local
+include => trunkld
+
+[local]
+;
+; Master context for local, toll-free, and iaxtel calls only
+;
+ignorepat => 9
+include => default
+include => trunklocal
+include => iaxtel700
+include => trunktollfree
+include => iaxprovider
+
+;Include parkedcalls (or the context you define in features conf)
+;to enable call parking.
+include => parkedcalls
+;
+; You can use an alternative switch type as well, to resolve
+; extensions that are not known here, for example with remote
+; IAX switching you transparently get access to the remote
+; Asterisk PBX
+;
+; switch => IAX2/user:password@bigserver/local
+;
+; An "lswitch" is like a switch but is literal, in that
+; variable substitution is not performed at load time
+; but is passed to the switch directly (presumably to
+; be substituted in the switch routine itself)
+;
+; lswitch => Loopback/12${EXTEN}@othercontext
+;
+; An "eswitch" is like a switch but the evaluation of
+; variable substitution is performed at runtime before
+; being passed to the switch routine.
+;
+; eswitch => IAX2/context@${CURSERVER}
+
+[macro-trunkdial]
+;
+; Standard trunk dial macro (hangs up on a dialstatus that should
+; terminate call)
+; ${ARG1} - What to dial
+;
+exten => s,1,Dial(${ARG1})
+exten => s,n,Goto(s-${DIALSTATUS},1)
+exten => s-NOANSWER,1,Hangup
+exten => s-BUSY,1,Hangup
+exten => _s-.,1,NoOp
+
+[macro-stdexten];
+;
+; Standard extension macro:
+; ${ARG1} - Extension (we could have used ${MACRO_EXTEN} here as well
+; ${ARG2} - Device(s) to ring
+;
+exten => s,1,Dial(${ARG2},20) ; Ring the interface, 20 seconds maximum
+exten => s,2,Goto(s-${DIALSTATUS},1) ; Jump based on status (NOANSWER,BUSY,CHANUNAVAIL,CONGESTION,ANSWER)
+
+exten => s-NOANSWER,1,Voicemail(${ARG1},u) ; If unavailable, send to voicemail w/ unavail announce
+exten => s-NOANSWER,2,Goto(default,s,1) ; If they press #, return to start
+
+exten => s-BUSY,1,Voicemail(${ARG1},b) ; If busy, send to voicemail w/ busy announce
+exten => s-BUSY,2,Goto(default,s,1) ; If they press #, return to start
+
+exten => _s-.,1,Goto(s-NOANSWER,1) ; Treat anything else as no answer
+
+exten => a,1,VoicemailMain(${ARG1}) ; If they press *, send the user into VoicemailMain
+
+[macro-stdPrivacyexten];
+;
+; Standard extension macro:
+; ${ARG1} - Extension (we could have used ${MACRO_EXTEN} here as well
+; ${ARG2} - Device(s) to ring
+; ${ARG3} - Optional DONTCALL context name to jump to (assumes the s,1 extension-priority)
+; ${ARG4} - Optional TORTURE context name to jump to (assumes the s,1 extension-priority)`
+;
+exten => s,1,Dial(${ARG2},20|p) ; Ring the interface, 20 seconds maximum, call screening
+ ; option (or use P for databased call screening)
+exten => s,2,Goto(s-${DIALSTATUS},1) ; Jump based on status (NOANSWER,BUSY,CHANUNAVAIL,CONGESTION,ANSWER)
+
+exten => s-NOANSWER,1,Voicemail(${ARG1},u) ; If unavailable, send to voicemail w/ unavail announce
+exten => s-NOANSWER,2,Goto(default,s,1) ; If they press #, return to start
+
+exten => s-BUSY,1,Voicemail(${ARG1},b) ; If busy, send to voicemail w/ busy announce
+exten => s-BUSY,2,Goto(default,s,1) ; If they press #, return to start
+
+exten => s-DONTCALL,1,Goto(${ARG3},s,1) ; Callee chose to send this call to a polite "Don't call again" script.
+
+exten => s-TORTURE,1,Goto(${ARG4},s,1) ; Callee chose to send this call to a telemarketer torture script.
+
+exten => _s-.,1,Goto(s-NOANSWER,1) ; Treat anything else as no answer
+
+exten => a,1,VoicemailMain(${ARG1}) ; If they press *, send the user into VoicemailMain
+
+[macro-page];
+;
+; Paging macro:
+;
+; Check to see if SIP device is in use and DO NOT PAGE if they are
+;
+; ${ARG1} - Device to page
+
+exten => s,1,ChanIsAvail(${ARG1}|js) ; j is for Jump and s is for ANY call
+exten => s,n,GoToIf([${AVAILSTATUS} = "1"]?autoanswer:fail)
+exten => s,n(autoanswer),Set(_ALERT_INFO="RA") ; This is for the PolyComs
+exten => s,n,SIPAddHeader(Call-Info: Answer-After=0) ; This is for the Grandstream, Snoms, and Others
+exten => s,n,NoOp() ; Add others here and Post on the Wiki!!!!
+exten => s,n,Dial(${ARG1}||)
+exten => s,n(fail),Hangup
+
+
+[demo]
+;
+; We start with what to do when a call first comes in.
+;
+exten => s,1,Wait(1) ; Wait a second, just for fun
+exten => s,n,Answer ; Answer the line
+exten => s,n,Set(TIMEOUT(digit)=5) ; Set Digit Timeout to 5 seconds
+exten => s,n,Set(TIMEOUT(response)=10) ; Set Response Timeout to 10 seconds
+exten => s,n(restart),BackGround(demo-congrats) ; Play a congratulatory message
+exten => s,n(instruct),BackGround(demo-instruct) ; Play some instructions
+exten => s,n,WaitExten ; Wait for an extension to be dialed.
+
+exten => 2,1,BackGround(demo-moreinfo) ; Give some more information.
+exten => 2,n,Goto(s,instruct)
+
+exten => 3,1,Set(LANGUAGE()=fr) ; Set language to french
+exten => 3,n,Goto(s,restart) ; Start with the congratulations
+
+exten => 1000,1,Goto(default,s,1)
+;
+; We also create an example user, 1234, who is on the console and has
+; voicemail, etc.
+;
+exten => 1234,1,Playback(transfer,skip) ; "Please hold while..."
+ ; (but skip if channel is not up)
+exten => 1234,n,Macro(stdexten,1234,${GLOBAL(CONSOLE)})
+
+exten => 1235,1,Voicemail(1234,u) ; Right to voicemail
+
+exten => 1236,1,Dial(Console/dsp) ; Ring forever
+exten => 1236,n,Voicemail(1234,b) ; Unless busy
+
+;
+; # for when they're done with the demo
+;
+exten => #,1,Playback(demo-thanks) ; "Thanks for trying the demo"
+exten => #,n,Hangup ; Hang them up.
+
+;
+; A timeout and "invalid extension rule"
+;
+exten => t,1,Goto(#,1) ; If they take too long, give up
+exten => i,1,Playback(invalid) ; "That's not valid, try again"
+
+;
+; Create an extension, 500, for dialing the
+; Asterisk demo.
+;
+exten => 500,1,Playback(demo-abouttotry); Let them know what's going on
+exten => 500,n,Dial(IAX2/guest@pbx.digium.com/s@default) ; Call the Asterisk demo
+exten => 500,n,Playback(demo-nogo) ; Couldn't connect to the demo site
+exten => 500,n,Goto(s,6) ; Return to the start over message.
+
+;
+; Create an extension, 600, for evaluating echo latency.
+;
+exten => 600,1,Playback(demo-echotest) ; Let them know what's going on
+exten => 600,n,Echo ; Do the echo test
+exten => 600,n,Playback(demo-echodone) ; Let them know it's over
+exten => 600,n,Goto(s,6) ; Start over
+
+;
+; You can use the Macro Page to intercom a individual user
+exten => 76245,1,Macro(page,SIP/Grandstream1)
+; or if your peernames are the same as extensions
+exten => _7XXX,1,Macro(page,SIP/${EXTEN})
+;
+;
+; System Wide Page at extension 7999
+;
+exten => 7999,1,Set(TIMEOUT(absolute)=60)
+exten => 7999,2,Page(Local/Grandstream1@page&Local/Xlite1@page&Local/1234@page/n|d)
+
+; Give voicemail at extension 8500
+;
+exten => 8500,1,VoicemailMain
+exten => 8500,n,Goto(s,6)
+;
+; Here's what a phone entry would look like (IXJ for example)
+;
+;exten => 1265,1,Dial(Phone/phone0,15)
+;exten => 1265,n,Goto(s,5)
+
+;
+; The page context calls up the page macro that sets variables needed for auto-answer
+; It is in is own context to make calling it from the Page() application as simple as
+; Local/{peername}@page
+;
+[page]
+exten => _X.,1,Macro(page,SIP/${EXTEN})
+
+;[mainmenu]
+;
+; Example "main menu" context with submenu
+;
+;exten => s,1,Answer
+;exten => s,n,Background(thanks) ; "Thanks for calling press 1 for sales, 2 for support, ..."
+;exten => s,n,WaitExten
+;exten => 1,1,Goto(submenu,s,1)
+;exten => 2,1,Hangup
+;include => default
+;
+;[submenu]
+;exten => s,1,Ringing ; Make them comfortable with 2 seconds of ringback
+;exten => s,n,Wait,2
+;exten => s,n,Background(submenuopts) ; "Thanks for calling the sales department. Press 1 for steve, 2 for..."
+;exten => s,n,WaitExten
+;exten => 1,1,Goto(default,steve,1)
+;exten => 2,1,Goto(default,mark,2)
+
+[default]
+;
+; By default we include the demo. In a production system, you
+; probably don't want to have the demo there.
+;
+include => demo
+
+;
+; An extension like the one below can be used for FWD, Nikotel, sipgate etc.
+; Note that you must have a [sipprovider] section in sip.conf
+;
+;exten => _41X.,1,Dial(SIP/${EXTEN:2}@sipprovider,,r)
+
+; Real extensions would go here. Generally you want real extensions to be
+; 4 or 5 digits long (although there is no such requirement) and start with a
+; single digit that is fairly large (like 6 or 7) so that you have plenty of
+; room to overlap extensions and menu options without conflict. You can alias
+; them with names, too, and use global variables
+
+;exten => 6245,hint,SIP/Grandstream1&SIP/Xlite1(Joe Schmoe) ; Channel hints for presence
+;exten => 6245,1,Dial(SIP/Grandstream1,20,rt) ; permit transfer
+;exten => 6245,n(dial),Dial(${HINT},20,rtT) ; Use hint as listed
+;exten => 6245,n,Voicemail(6245,u) ; Voicemail (unavailable)
+;exten => 6245,s+1,Hangup ; s+1, same as n
+;exten => 6245,dial+101,Voicemail(6245,b) ; Voicemail (busy)
+;exten => 6361,1,Dial(IAX2/JaneDoe,,rm) ; ring without time limit
+;exten => 6389,1,Dial(MGCP/aaln/1@192.168.0.14)
+;exten => 6390,1,Dial(JINGLE/caller/callee) ; Dial via jingle using labels
+;exten => 6391,1,Dial(JINGLE/asterisk@digium.com/mogorman@astjab.org) ;Dial via jingle using asterisk as the transport and calling mogorman.
+;exten => 6394,1,Dial(Local/6275/n) ; this will dial ${MARK}
+
+;exten => 6275,1,Macro(stdexten,6275,${MARK}) ; assuming ${MARK} is something like Zap/2
+;exten => mark,1,Goto(6275|1) ; alias mark to 6275
+;exten => 6536,1,Macro(stdexten,6236,${WIL}) ; Ditto for wil
+;exten => wil,1,Goto(6236|1)
+
+;If you want to subscribe to the status of a parking space, this is
+;how you do it. Subscribe to extension 6600 in sip, and you will see
+;the status of the first parking lot with this extensions' help
+;exten => 6600,hint,park:701@parkedcalls
+;exten => 6600,1,noop
+;
+; Some other handy things are an extension for checking voicemail via
+; voicemailmain
+;
+;exten => 8500,1,VoicemailMain
+;exten => 8500,n,Hangup
+;
+; Or a conference room (you'll need to edit meetme.conf to enable this room)
+;
+;exten => 8600,1,Meetme(1234)
+;
+; Or playing an announcement to the called party, as soon it answers
+;
+;exten = 8700,1,Dial(${MARK},30,A(/path/to/my/announcemsg))
+;
+; For more information on applications, just type "core show applications" at your
+; friendly Asterisk CLI prompt.
+;
+; "core show application <command>" will show details of how you
+; use that particular application in this file, the dial plan.
+; "core show functions" will list all dialplan functions
+; "core show function <COMMAND>" will show you more information about
+; one function. Remember that function names are UPPER CASE.
diff --git a/trunk/configs/extensions.lua.sample b/trunk/configs/extensions.lua.sample
new file mode 100644
index 000000000..691e461fb
--- /dev/null
+++ b/trunk/configs/extensions.lua.sample
@@ -0,0 +1,208 @@
+
+
+CONSOLE = "Console/dsp" -- Console interface for demo
+--CONSOLE = "Zap/1"
+--CONSOLE = "Phone/phone0"
+
+IAXINFO = "guest" -- IAXtel username/password
+--IAXINFO = "myuser:mypass"
+
+TRUNK = "Zap/g2"
+TRUNKMSD = 1
+-- TRUNK = "IAX2/user:pass@provider"
+
+
+--
+-- Extensions are expected to be defined in a global table named 'extensions'.
+-- The 'extensions' table should have a group of tables in it, each
+-- representing a context. Extensions are defined in each context. See below
+-- for examples.
+--
+-- This file can be automatically included in the extensions.conf file using
+-- the 'utils/build-extensions-conf.lua' script and a #exec statement in
+-- extensions.conf.
+--
+-- #exec /usr/bin/utils/build-extensions.conf.lua -c
+--
+-- The 'execincludes' option must be set to 'yes' in the [options] section of
+-- asterisk.conf for this to work properly.
+--
+-- Extension names may be numbers, letters, or combinations thereof. If
+-- an extension name is prefixed by a '_' character, it is interpreted as
+-- a pattern rather than a literal. In patterns, some characters have
+-- special meanings:
+--
+-- X - any digit from 0-9
+-- Z - any digit from 1-9
+-- N - any digit from 2-9
+-- [1235-9] - any digit in the brackets (in this example, 1,2,3,5,6,7,8,9)
+-- . - wildcard, matches anything remaining (e.g. _9011. matches
+-- anything starting with 9011 excluding 9011 itself)
+-- ! - wildcard, causes the matching process to complete as soon as
+-- it can unambiguously determine that no other matches are possible
+--
+-- For example the extension _NXXXXXX would match normal 7 digit
+-- dialings, while _1NXXNXXXXXX would represent an area code plus phone
+-- number preceded by a one.
+--
+-- If your extension has special characters in it such as '.' and '!' you must
+-- explicitly make it a string in the tabale definition:
+--
+-- ["_special."] = function;
+-- ["_special!"] = function;
+--
+-- There are no priorities. All extensions to asterisk appear to have a single
+-- priority as if they consist of a single priority.
+--
+-- Each context is defined as a table in the extensions table. The
+-- context names should be strings.
+--
+-- One context may be included in another context using the 'includes'
+-- extension. This extension should be set to a table containing a list
+-- of context names. Do not put references to tables in the includes
+-- table.
+--
+-- include = {"a", "b", "c"};
+--
+-- Channel variables can be accessed thorugh the global 'channel' table.
+--
+-- v = channel.var_name
+-- v = channel["var_name"]
+-- v.value
+-- v:get()
+--
+-- channel.var_name = "value"
+-- channel["var_name"] = "value"
+-- v:set("value")
+--
+-- channel.func_name(1,2,3):set("value")
+-- value = channel.func_name(1,2,3):get()
+--
+-- channel["func_name(1,2,3)"]:set("value")
+-- channel["func_name(1,2,3)"] = "value"
+-- value = channel["func_name(1,2,3)"]:get()
+--
+-- Note the use of the ':' operator to access the get() and set()
+-- methods.
+--
+-- Also notice the absence of the following constructs from the examples above:
+-- channel.func_name(1,2,3) = "value" -- this will NOT work
+-- value = channel.func_name(1,2,3) -- this will NOT work as expected
+--
+--
+-- Dialplan applications can be accessed through the global 'app' table.
+--
+-- app.Dial("Zap/1")
+-- app.dial("Zap/1")
+--
+-- More examples can be found below.
+--
+-- Before starting long running operations, an autoservice should be started
+-- using the autoservice_start() function. This autoservice will automatically
+-- be stopped before executing applications and dialplan functions and will be
+-- restarted afterwards. The autoservice can be stopped using
+-- autoservice_stop() and the autoservice_status() function will return true if
+-- an autoservice is currently running.
+--
+
+function outgoing_local(c, e)
+ app.dial("zap/1/" .. e, "", "")
+end
+
+function demo_instruct()
+ app.background("demo-instruct")
+ app.waitexten()
+end
+
+function demo_congrats()
+ app.background("demo-congrats")
+ demo_instruct()
+end
+
+-- Answer the chanel and play the demo sound files
+function demo_start(context, exten)
+ app.wait(1)
+ app.answer()
+
+ channel.TIMEOUT("digit"):set(5)
+ channel.TIMEOUT("response"):set(10)
+ -- app.set("TIMEOUT(digit)=5")
+ -- app.set("TIMEOUT(response)=10")
+
+ demo_congrats(context, exten)
+end
+
+function demo_hangup()
+ app.playback("demo-thanks")
+ app.hangup()
+end
+
+extensions = {
+ demo = {
+ s = demo_start;
+
+ ["2"] = function()
+ app.background("demo-moreinfo")
+ demo_instruct()
+ end;
+ ["3"] = function ()
+ channel.LANGUAGE():set("fr") -- set the language to french
+ demo_congrats()
+ end;
+
+ ["1000"] = function()
+ app.goto("default", "s", 1)
+ end;
+
+ ["1234"] = function()
+ app.playback("transfer", "skip")
+ -- do a dial here
+ end;
+
+ ["1235"] = function()
+ app.voicemail("1234", "u")
+ end;
+
+ ["1236"] = function()
+ app.dial("Console/dsp")
+ app.voicemail(1234, "b")
+ end;
+
+ ["#"] = demo_hangup;
+ t = demo_hangup;
+ i = function()
+ app.playback("invalid")
+ demo_instruct()
+ end;
+
+ ["500"] = function()
+ app.playback("demo-abouttotry")
+ app.dial("IAX2/guest@misery.digium.com/s@default")
+ app.playback("demo-nogo")
+ demo_instruct()
+ end;
+
+ ["600"] = function()
+ app.playback("demo-echotest")
+ app.echo()
+ app.playback("demo-echodone")
+ demo_instruct()
+ end;
+
+ ["8500"] = function()
+ app.voicemailmain()
+ demo_instruct()
+ end;
+
+ };
+
+ default = {
+ -- by default, do the demo
+ include = {"demo"};
+ };
+
+ ["local"] = {
+ ["_NXXXXXX"] = outgoing_local;
+ };
+}
+
diff --git a/trunk/configs/extensions_minivm.conf.sample b/trunk/configs/extensions_minivm.conf.sample
new file mode 100644
index 000000000..75f87c165
--- /dev/null
+++ b/trunk/configs/extensions_minivm.conf.sample
@@ -0,0 +1,159 @@
+; MINI-VOICEMAIL dialplan example
+; ---------------------------------------------------------------------------------------
+; ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+;
+;
+; This is an example on how to use the Mini-Voicemail system to build
+; voicemail systems.
+;
+;.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
+; A macro to test the MINIVMACCOUNT dialplan function
+; Currently, accountcode and pincode is not used in the application
+; They where added to be used in dialplan scripting
+;
+;
+[macro-minivmfunctest]
+exten => s,1,set(account=${ARGV1})
+exten => minivm,n,verbose(1,-------------------- Minivm Function test - Accoutn ${account} -------------)
+exten => s,n,verbose(1,---- Has account: ${MINIVMACCOUNT(${account}:hasaccount)})
+exten => s,n,verbose(1,---- Fullname: ${MINIVMACCOUNT(${account}:fullname)})
+exten => s,n,verbose(1,---- Email: ${MINIVMACCOUNT(${account}:email)})
+exten => s,n,verbose(1,---- Pager: ${MINIVMACCOUNT(${account}:pager)})
+exten => s,n,verbose(1,---- E-mail template: ${MINIVMACCOUNT(${account}:etemplate)})
+exten => s,n,verbose(1,---- Pager template: ${MINIVMACCOUNT(${account}:ptemplate)})
+exten => s,n,verbose(1,---- Account code: ${MINIVMACCOUNT(${account}:accountcode)})
+exten => s,n,verbose(1,---- Path: ${MINIVMACCOUNT(${account}:path)})
+exten => s,n,verbose(1,---- Pincode: ${MINIVMACCOUNT(${account}:pincode)})
+exten => s,n,verbose(1,---- Time zone: ${MINIVMACCOUNT(${account}:timezone)})
+exten => s,n,verbose(1,---- Language: ${MINIVMACCOUNT(${account}:language)})
+; This requires setvar=customerclass=gold in the account configuration
+exten => s,n,verbose(1,---- Var:customerclass: ${MINIVMACCOUNT(${account}:customerclass)})
+
+[minivm-scenario1]
+; minivmtest tests the dialplan function MINIVMACCOUNT
+; Check the output in the console with verbose set
+exten => minivmtest,1,answer
+exten => minivmtest,n,wait(0.5)
+exten => minivmtest,n,set(ACCOUNT=do-not-spam-me@example.com)
+exten => minivmtest,n,macro(minivmfunctest, ${ACCOUNT})
+exten => minivmtest,n,playback(beep)
+exten => minivmtest,n,hangup
+
+;.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
+; "minivm" tests a full scenario
+; Remember that users may hangup
+; This works both for users with accounts in minivm.conf and by just giving an e-mail address
+; without configuring an account
+exten => minivm,1,answer
+exten => minivm,n,wait(0.5) ; Wait for Voip channels to settle
+exten => minivm,n,set(account=oej@example.com)
+exten => minivm,n,noop(------------------------------------------- Minivm Greet -------------)
+exten => minivm,n,minivmgreet(${account})
+exten => minivm,n,verbose(1,-- MINIVM_GREET_STATUS = ${MINIVM_GREET_STATUS} )
+exten => minivm,n,noop(------------------------------------------- Minivm Record -------------)
+exten => minivm,n,minivmRecord(${account},b)
+exten => minivm,n,goto(minivmcleanup,1)
+
+; Cleanup after recording or hangup
+exten => minivmcleanup,1,noop(------------------------------------------- Minivm Notify -------------)
+;Increment voicemail counter with 1. The counter will be used in the e-mail message
+;and in the filename
+exten => minivmcleanup,n,gotoif($[${MINIVM_RECORD_STATUS} != SUCCESS]?minivmrecordfailure,1)
+exten => minivmcleanup,n,set(MINIVMCOUNTER(${account}:voicemailcounter:inc)=1)
+exten => minivmcleanup,n,set(MVM_COUNTER = ${MINIVMCOUNTER(${account}:voicemailcounter)})
+exten => minivmcleanup,n,minivmNotify(${account})
+exten => minivmcleanup,n,verbose(1,-- MINIVM_NOTIFY_STATUS = ${MINIVM_NOTIFY_STATUS} )
+; Now, clean up after sending voicemail
+exten => minivmcleanup,n,noop(------------------------------------------- Minivm Delete -------------)
+exten => minivmcleanup,n,minivmdelete()
+exten => minivmcleanup,n,verbose(1,-- MINIVM_DELETE_STATUS = ${MINIVM_DELETE_STATUS} )
+
+;Recording failed
+exten => minivmrecordfailure,1,playback(vm-sorry)
+exten => minivmrecordfailure,n,wait(1)
+exten => minivmrecordfailure,n,hangup
+
+; If the user hangs up during the recording, we need to clean up
+; And send notifications
+exten => h,1,gotoif($[x${MINIVM_DELETE_STATUS} != x] ?h,stop)
+exten => h,n,noop(------------------------------------------- HANGUP during voicemail recording -------------)
+exten => h,n,goto(minivmcleanup,1)
+exten => h,n(stop),noop(---Minivm DONE----)
+
+;.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
+; Extension to record a greeting message
+; Call this like:
+; macro(recordgreetings,alice@atlanta.example.com)
+;
+[macro-recordgreetings]
+exten => s,1,answer
+exten => s,n,wait(0.5)
+exten => s,n,set(account=${ARGV1])
+; This file give extra options not available here, needs to be edited
+; Change of password does not work
+exten => s,n(menu),background(vm-options)
+exten => 1,1,setvar(option=u)
+exten => 1,n,macro(minivmrec,${account},${option})
+exten => 1,n,goto(menu)
+exten => 2,1,setvar(option=b)
+exten => 2,n,macro(minivmrec,${account},${option})
+exten => 2,n,goto(menu)
+exten => 3,1,setvar(option=n)
+exten => 3,n,macro(minivmrec,${account},${option})
+exten => 3,n,goto(menu)
+exten => 4,1,setvar(option=t)
+exten => 4,n,macro(minivmrec,${account},${option})
+exten => 4,n,goto(menu)
+exten => *,1,playback(vm-thankyou)
+exten => *,n,wait(1)
+exten => *,n,hangup
+
+exten => i,1,playback(invalid)
+exten => i,n,goto(menu)
+
+[macro-minivmrec]
+exten => s,1,gotoif(${MINIVMACCOUNT(${account}:hasaccount)}?record)
+; Account is not configured in minivm.conf or realtime
+; Phony message, add something useful here
+exten => s,n,playback(privacy-incorrect)
+exten => s,n,macroreturn
+exten => record,1,minivmappmess(${ARGV1},${ARGV2})
+exten => record,n,noop(Recording status: ${MINIVM_APPMESS_STATUS})
+exten => record,n,macroreturn
+
+;.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
+; To set a counter and use a template for voicemail to users without acounts
+; use something like this
+;
+; email address is in the "account" channel variable. Set from ast_db or a script
+; based on called ID
+
+exten => sendvoicemail,1,answer
+exten => sendvoicemail,n,wait(0.5)
+exten => sendvoicemail,n,set(domain=${CUT(account,@,2)})
+exten => sendvoicemail,n,set(country=${CUT(domain,.,2)})
+exten => sendvoicemail,n,minivmgreet(${account})
+exten => sendvoicemail,n,minivmRecord(${account},b)
+exten => sendvoicemail,n,goto(sendvmcleanup)
+
+exten => sendvmcleanup,1,gotoif($[${MINIVM_RECORD_STATUS} != SUCCESS]?done)
+; The counter is set in the domain directory, so we don't create one directory per user
+; The counter has the email in the name of the counter, increase it
+; Set the MVM_COUNTER variable that we use in the template
+exten => sendvmcleanup,n,set(MINIVMCOUNTER(${account}:voicemailcounter:inc)=1)
+exten => sendvmcleanup,n,set(MVM_COUNTER = ${MINIVMCOUNTER(${account}:voicemailcounter)})
+; Increase a domain counter too, to see how many voicemails are sent to this domain
+; This is just for statistics
+exten => sendvmcleanup,n,set(MINIVMCOUNTER(${domain}:${domain}-all:inc) = 1)
+
+; Send voicemail in e-mail with country-specific template
+; The template need to be defined in minivm.conf
+;
+exten => sendvmcleanup,n,minivmNotify(${account}, ${country}_email)
+exten => sendvmcleanup,n,minivmDelete()
+
+exten => sendvmcleanup,n(done),wait(0.5)
+exten => sendvmcleanup,n,hangup
+
+exten => h,1,gotoif($[${MINIVM_RECORD_STATUS} = SUCCESS]?sendvmcleanup,1))
+
diff --git a/trunk/configs/features.conf.sample b/trunk/configs/features.conf.sample
new file mode 100644
index 000000000..c3026adda
--- /dev/null
+++ b/trunk/configs/features.conf.sample
@@ -0,0 +1,124 @@
+;
+; Sample Call Features (parking, transfer, etc) configuration
+;
+
+[general]
+parkext => 700 ; What extension to dial to park
+parkpos => 701-720 ; What extensions to park calls on. These needs to be
+ ; numeric, as Asterisk starts from the start position
+ ; and increments with one for the next parked call.
+context => parkedcalls ; Which context parked calls are in
+;parkingtime => 45 ; Number of seconds a call can be parked for
+ ; (default is 45 seconds)
+;courtesytone = beep ; Sound file to play to the parked caller
+ ; when someone dials a parked call
+ ; or the Touch Monitor is activated/deactivated.
+;parkedplay = caller ; Who to play the courtesy tone to when picking up a parked call
+ ; one of: parked, caller, both (default is caller)
+;parkedcalltransfers = caller ; Enables or disables DTMF based transfers when picking up a parked call.
+ ; one of: callee, caller, both, no (default is no)
+;parkedcallreparking = caller ; Enables or disables DTMF based parking when picking up a parked call.
+ ; one of: callee, caller, both, no (default is no)
+;adsipark = yes ; if you want ADSI parking announcements
+;findslot => next ; Continue to the 'next' free parking space.
+ ; Defaults to 'first' available
+;parkedmusicclass=default ; This is the MOH class to use for the parked channel
+ ; as long as the class is not set on the channel directly
+ ; using Set(CHANNEL(musicclass)=whatever) in the dialplan
+
+;transferdigittimeout => 3 ; Number of seconds to wait between digits when transferring a call
+ ; (default is 3 seconds)
+;xfersound = beep ; to indicate an attended transfer is complete
+;xferfailsound = beeperr ; to indicate a failed transfer
+;pickupexten = *8 ; Configure the pickup extension. (default is *8)
+;featuredigittimeout = 500 ; Max time (ms) between digits for
+ ; feature activation (default is 500 ms)
+;atxfernoanswertimeout = 15 ; Timeout for answer on attended transfer default is 15 seconds.
+;atxferdropcall = no ; If someone does an attended transfer, then hangs up before the transferred
+ ; caller is connected, then by default, the system will try to call back the
+ ; person that did the transfer. If this is set to "yes", the callback will
+ ; not be attempted and the transfer will just fail.
+;atxferloopdelay = 10 ; Number of seconds to sleep between retries (if atxferdropcall = no)
+;atxfercallbackretries = 2 ; Number of times to attempt to send the call back to the transferer.
+ ; By default, this is 2.
+
+; Note that the DTMF features listed below only work when two channels have answered and are bridged together.
+; They can not be used while the remote party is ringing or in progress. If you require this feature you can use
+; chan_local in combination with Answer to accomplish it.
+
+[featuremap]
+;blindxfer => #1 ; Blind transfer (default is #)
+;disconnect => *0 ; Disconnect (default is *)
+;automon => *1 ; One Touch Record a.k.a. Touch Monitor
+;atxfer => *2 ; Attended transfer
+;parkcall => #72 ; Park call (one step parking)
+;automixmon => *3 ; One Touch Record a.k.a. Touch MixMonitor
+
+[applicationmap]
+; Note that the DYNAMIC_FEATURES channel variable must be set to use the features
+; defined here. The value of DYNAMIC_FEATURES should be the names of the features
+; to allow the channel to use separated by '#'. For example:
+;
+; Set(DYNAMIC_FEATURES=myfeature1#myfeature2#myfeature3)
+;
+;
+; The syntax for declaring a dynamic feature is the following:
+;
+;<FeatureName> => <DTMF_sequence>,<ActivateOn>[/<ActivatedBy>],<Application>[,<AppArguments>[,MOH_Class]]
+;
+; FeatureName -> This is the name of the feature used in when setting the
+; DYNAMIC_FEATURES variable to enable usage of this feature.
+; DTMF_sequence -> This is the key sequence used to activate this feature.
+; ActivateOn -> This is the channel of the call that the application will be executed
+; on. Valid values are "self" and "peer". "self" means run the
+; application on the same channel that activated the feature. "peer"
+; means run the application on the opposite channel from the one that
+; has activated the feature.
+; ActivatedBy -> This is which channel is allowed to activate this feature. Valid
+; values are "caller", "callee", and "both". "both" is the default.
+; The "caller" is the channel that executed the Dial application, while
+; the "callee" is the channel called by the Dial application.
+; Application -> This is the application to execute.
+; AppArguments -> These are the arguments to be passed into the application.
+; MOH_Class -> This is the music on hold class to play while the idle
+; channel waits for the feature to complete. If left blank,
+; no music will be played.
+;
+;
+; IMPORTANT NOTE: The applicationmap is not intended to be used for all Asterisk
+; applications. When applications are used in extensions.conf, they are executed
+; by the PBX core. In this case, these applications are executed outside of the
+; PBX core, so it does *not* make sense to use any application which has any
+; concept of dialplan flow. Examples of this would be things like Macro, Goto,
+; Background, WaitExten, and many more.
+;
+; Enabling these features means that the PBX needs to stay in the media flow and
+; media will not be re-directed if DTMF is sent in the media stream.
+;
+; Example Usage:
+;
+;testfeature => #9,peer,Playback,tt-monkeys ;Allow both the caller and callee to play
+; ;tt-monkeys to the opposite channel
+;
+;pauseMonitor => #1,self/callee,Pausemonitor ;Allow the callee to pause monitoring
+; ;on their channel
+;unpauseMonitor => #3,self/callee,UnPauseMonitor ;Allow the callee to unpause monitoring
+; ;on their channel
+;
+; GROUPS
+; Groups are groupings of features defined in [applicationmap]
+; that can have their own key mappings.
+;
+; Groups are defined as a configuration section,
+; and can be set as part of DYNAMIC_FEATURES in
+; the same way that a normal feature can...
+; etc:
+;
+; Set(DYNAMIC_FEATURES=myGroupName);
+;
+; example:
+; [myGroupName] ; defines the group named myGroupName
+; testfeature => #9 ; associates testfeature with the group and the keycode #9
+; pauseMonitor ; associates pauseMonitor with the group and the keycode
+; ; defined in [applicationmap]
+
diff --git a/trunk/configs/festival.conf.sample b/trunk/configs/festival.conf.sample
new file mode 100644
index 000000000..774f1a16c
--- /dev/null
+++ b/trunk/configs/festival.conf.sample
@@ -0,0 +1,35 @@
+;
+; Festival Configuration
+;
+[general]
+;
+; Host which runs the festival server (default : localhost);
+;
+;host=localhost
+;
+; Port on host where the festival server runs (default : 1314)
+;
+;port=1314
+;
+; Use cache (yes, no - defaults to no)
+;
+;usecache=yes
+;
+; If usecache=yes, a directory to store waveform cache files.
+; The cache is never cleared (yet), so you must take care of cleaning it
+; yourself (just delete any or all files from the cache).
+; THIS DIRECTORY *MUST* EXIST and must be writable from the asterisk process.
+; Defaults to /tmp/
+;
+;cachedir=/var/lib/asterisk/festivalcache/
+;
+; Festival command to send to the server.
+; Defaults to: (tts_textasterisk "%s" 'file)(quit)\n
+; %s is replaced by the desired text to say. The command MUST end with a
+; (quit) directive, or the cache handling mechanism will hang. Do not
+; forget the \n at the end.
+;
+;festivalcommand=(tts_textasterisk "%s" 'file)(quit)\n
+;
+;
+
diff --git a/trunk/configs/followme.conf.sample b/trunk/configs/followme.conf.sample
new file mode 100644
index 000000000..697e5a69c
--- /dev/null
+++ b/trunk/configs/followme.conf.sample
@@ -0,0 +1,86 @@
+; Find-Me / Follow-Me Configuration File
+[general]
+;
+featuredigittimeout=>5000
+; The number of ms to wait for a digit input for the callee on whether to take the call or
+; not before we consider them "done" entering digits.
+;
+takecall=>1
+; The global default keypress for the callee to take taking the current call. This can be
+; a single digit or multiple digits. Default is "1".
+;
+declinecall=>2
+; The global default keypress for the callee to decline taking the current call. This can
+; be a single digit or multiple digits. Default is "2".
+;
+call-from-prompt=>followme/call-from
+; The global default for the 'Incoming call from' message.
+;
+norecording-prompt=>followme/no-recording
+; The global default for the 'You have an incoming call' message when the caller elects
+; not to leave their name or the option isn't set for them to do so.
+;
+options-prompt=>followme/options
+; The global default for the 'Press 1 to accept this call or press 2 to decline it' message.
+;
+pls-hold-prompt=>followme/pls-hold-while-try
+; The global default for 'Please hold while we try and connect your call' message.
+;
+status-prompt=>followme/status
+; The global default for 'The party you're calling isn't at their desk' message.
+;
+sorry-prompt=>followme/sorry
+; The global default for 'I'm sorry, but we were unable to locate your party' message.
+;
+;
+[default]
+musicclass=>default
+; The moh class that should be used for the caller while they are waiting to be connected.
+context=>default
+; The context to dial the numbers from
+number=>01233456,25
+; The a follow-me number to call. The format is:
+; number=> <number to call[&2nd #[&3rd #]]> [, <timeout value in seconds> [, <order in follow-me>] ]
+; You can specify as many of these numbers as you like. They will be dialed in the
+; order that you specify them in the config file OR as specified with the order field
+; on the number prompt. As you can see from the example, forked dialing of multiple
+; numbers in the same step is supported with this application if you'd like to dial
+; multiple numbers in the same followme step.
+; It's also important to note that the timeout value is not the same
+; as the timeout value you would use in app_dial. This timeout value is the amount of
+; time allowed between the time the dialing step starts and the callee makes a choice
+; on whether to take the call or not. That being the case, you may want to account for
+; this time, and make this timeout longer than a timeout you might specify in app_dial.
+takecall=>1
+; The keypress for the callee to take taking the current call. This can be
+; a single digit or multiple digits. Default is the global default.
+;
+declinecall=>2
+; The keypress for the callee to decline taking the current call. This can
+; be a single digit or multiple digits. Default is the global default.
+;
+call-from-prompt=>followme/call-from
+; The 'Incoming call from' message prompt. Default is the global default.
+;
+followme-norecording-prompt=>followme/no-recording
+; The 'You have an incoming call' message prompt when the caller elects
+; not to leave their name or the option isn't set for them to do so. Default
+; is the global default.
+;
+followme-options-prompt=>followme/options
+; The 'Press 1 to accept this call or press 2 to decline it' message prompt.
+; Default is the global default.
+;
+followme-pls-hold-prompt=>followme/pls-hold-while-try
+; The 'Please hold while we try and connect your call' message prompt.
+; Default is the global default.
+;
+followme-status-prompt=>followme/status
+; The 'The party you're calling isn't at their desk' message prompt.
+; Default is the global default.
+;
+followme-sorry-prompt=>followme/sorry
+; The 'I'm sorry, but we were unable to locate your party' message prompt. Default
+; is the global default.
+
+
diff --git a/trunk/configs/func_odbc.conf.sample b/trunk/configs/func_odbc.conf.sample
new file mode 100644
index 000000000..ff9e1c17b
--- /dev/null
+++ b/trunk/configs/func_odbc.conf.sample
@@ -0,0 +1,77 @@
+;
+; func_odbc.conf
+;
+; Each context is a separately defined function. By convention, all
+; functions are entirely uppercase, so the defined contexts should also
+; be all-uppercase, but there is nothing that enforces this. All functions
+; are case-sensitive, however.
+;
+; For substitution, you have ${ARG1}, ${ARG2} ... ${ARGn}
+; for the arguments to each SQL statement.
+;
+; In addition, for write statements, you have ${VAL1}, ${VAL2} ... ${VALn}
+; parsed, just like arguments, for the values. In addition, if you want the
+; whole value, never mind the parsing, you can get that with ${VALUE}.
+;
+;
+; If you have data which may potentially contain single ticks, you may wish
+; to use the dialplan function SQL_ESC() to escape the data prior to its
+; inclusion in the SQL statement.
+;
+;
+; The following variables are available in this configuration file:
+;
+; readhandle A comma-separated list of DSNs (from res_odbc.conf) to use when
+; executing the readsql statement. Each DSN is tried, in
+; succession, until the statement succeeds. You may specify up to
+; 5 DSNs per function class. If not specified, it will default to
+; the value of writehandle or dsn, if specified.
+; writehandle A comma-separated list of DSNs (from res_odbc.conf) to use when
+; executing the writesql statement. The same rules apply as to
+; readhandle. "dsn" is a synonym for "writehandle".
+; readsql The statement to execute when reading from the function class.
+; writesql The statement to execute when writing to the function class.
+; prefix Normally, all function classes are prefixed with "ODBC" to keep
+; them uniquely named. You may choose to change this prefix, which
+; may be useful to segregate a collection of certain function
+; classes from others.
+; escapecommas This option may be used to turn off the default behavior of
+; escaping commas which occur within a field. If commas are
+; escaped (the default behavior), then fields containing commas
+; will be treated as a single value when assigning to ARRAY() or
+; HASH(). If commas are not escaped, then values will be separated
+; at the comma within fields. Please note that turning this option
+; off is incompatible with the functionality of HASH().
+
+
+; ODBC_SQL - Allow an SQL statement to be built entirely in the dialplan
+[SQL]
+dsn=mysql1
+readsql=${ARG1}
+
+; ODBC_ANTIGF - A blacklist.
+[ANTIGF]
+dsn=mysql1,mysql2 ; Use mysql1 as the primary handle, but fall back to mysql2
+ ; if mysql1 is down. Supports up to 5 comma-separated
+ ; DSNs. "dsn" may also be specified as "readhandle" and
+ ; "writehandle", if it is important to separate reads and
+ ; writes to different databases.
+readsql=SELECT COUNT(*) FROM exgirlfriends WHERE callerid='${SQL_ESC(${ARG1})}'
+
+; ODBC_PRESENCE - Retrieve and update presence
+[PRESENCE]
+dsn=mysql1
+readsql=SELECT location FROM presence WHERE id='${SQL_ESC(${ARG1})}'
+writesql=UPDATE presence SET location='${SQL_ESC(${VAL1})}' WHERE id='${SQL_ESC(${ARG1})}'
+;prefix=OFFICE ; Changes this function from ODBC_PRESENCE to OFFICE_PRESENCE
+;escapecommas=no ; Normally, commas within a field are escaped such that each
+ ; field may be separated into individual variables with ARRAY.
+ ; This option turns that behavior off [default=yes].
+;mode=multirow ; Enable multirow fetching. Instead of returning results directly,
+ ; mode=multirow queries will return a result-id, which can be passed
+ ; multiple times to ODBC_FETCH, and that function will return each
+ ; row, in order. You can add to this the following parameter:
+;rowlimit=5 ; rowlimit will limit the number of rows retrieved and stored from
+ ; the database. If not specified, all rows, up to available memory,
+ ; will be retrieved and stored.
+
diff --git a/trunk/configs/gtalk.conf.sample b/trunk/configs/gtalk.conf.sample
new file mode 100644
index 000000000..da629b626
--- /dev/null
+++ b/trunk/configs/gtalk.conf.sample
@@ -0,0 +1,19 @@
+;[general]
+;context=default ;;Context to dump call into
+;allowguest=yes ;;Allow calls from people not in
+ ;;list of peers
+;
+;[guest] ;;special account for options on guest account
+;disallow=all
+;allow=ulaw
+;context=guest
+;
+;[ogorman]
+;username=ogorman@gmail.com ;;username of the peer your
+ ;;calling or accepting calls from
+;disallow=all
+;allow=ulaw
+;context=default
+;connection=asterisk ;;client or component in jabber.conf
+ ;;for the call to leave on.
+;
diff --git a/trunk/configs/h323.conf.sample b/trunk/configs/h323.conf.sample
new file mode 100644
index 000000000..5be321f33
--- /dev/null
+++ b/trunk/configs/h323.conf.sample
@@ -0,0 +1,207 @@
+; The NuFone Network's
+; Open H.323 driver configuration
+;
+[general]
+port = 1720
+;bindaddr = 1.2.3.4 ; this SHALL contain a single, valid IP address for this machine
+;
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of these parameters.
+;tos_audio=ef ; Sets TOS for RTP audio packets.
+;cos_audio=5 ; Sets 802.1p priority for RTP audio packets.
+;
+; You may specify a global default AMA flag for iaxtel calls. It must be
+; one of 'default', 'omit', 'billing', or 'documentation'. These flags
+; are used in the generation of call detail records.
+;
+;amaflags = default
+;
+; You may specify a default account for Call Detail Records in addition
+; to specifying on a per-user basis
+;
+;accountcode=lss0101
+;
+; You can fine tune codecs here using "allow" and "disallow" clauses
+; with specific codecs. Use "all" to represent all formats.
+;
+;disallow=all
+;allow=all ; turns on all installed codecs
+;disallow=g723.1 ; Hm... Proprietary, don't use it...
+;allow=gsm ; Always allow GSM, it's cool :)
+;allow=ulaw ; see doc/rtp-packetization for framing options
+;
+; User-Input Mode (DTMF)
+;
+; valid entries are: rfc2833, inband, cisco, h245-signal
+; default is rfc2833
+;dtmfmode=rfc2833
+;
+; Default RTP Payload to send RFC2833 DTMF on. This is used to
+; interoperate with broken gateways which cannot successfully
+; negotiate a RFC2833 payload type in the TerminalCapabilitySet.
+; To specify required payload type, put it after colon in dtmfmode
+; option like
+;dtmfmode=rfc2833:101
+; or
+;dtmfmode=cisco:121
+;
+; Set the gatekeeper
+; DISCOVER - Find the Gk address using multicast
+; DISABLE - Disable the use of a GK
+; <IP address> or <Host name> - The acutal IP address or hostname of your GK
+;gatekeeper = DISABLE
+;
+;
+; Tell Asterisk whether or not to accept Gatekeeper
+; routed calls or not. Normally this should always
+; be set to yes, unless you want to have finer control
+; over which users are allowed access to Asterisk.
+; Default: YES
+;
+;AllowGKRouted = yes
+;
+; When the channel works without gatekeeper, there is possible to
+; reject calls from anonymous (not listed in users) callers.
+; Default is to allow anonymous calls.
+;
+;AcceptAnonymous = yes
+;
+; Optionally you can determine a user by Source IP versus its H.323 alias.
+; Default behavour is to determine user by H.323 alias.
+;
+;UserByAlias=no
+;
+; Default context gets used in siutations where you are using
+; the GK routed model or no type=user was found. This gives you
+; the ability to either play an invalid message or to simply not
+; use user authentication at all.
+;
+;context=default
+;
+; Use this option to help Cisco (or other) gateways to setup backward voice
+; path to pass inband tones to calling user (see, for example,
+; http://www.cisco.com/warp/public/788/voip/ringback.html)
+;
+; Add PROGRESS information element to SETUP message sent on outbound calls
+; to notify about required backward voice path. Valid values are:
+; 0 - don't add PROGRESS information element (default);
+; 1 - call is not end-end ISDN, further call progress information can
+; possibly be available in-band;
+; 3 - origination address is non-ISDN (Cisco accepts this value only);
+; 8 - in-band information or an appropriate pattern is now available;
+;progress_setup = 3
+;
+; Add PROGRESS information element (IE) to ALERT message sent on incoming
+; calls to notify about required backwared voice path. Valid values are:
+; 0 - don't add PROGRESS IE (default);
+; 8 - in-band information or an appropriate pattern is now available;
+;progress_alert = 8
+;
+; Generate PROGRESS message when H.323 audio path has established to create
+; backward audio path at other end of a call.
+;progress_audio = yes
+;
+; Specify how to inject non-standard information into H.323 messages. When
+; the channel receives messages with tunneled information, it automatically
+; enables the same option for all further outgoing messages independedly on
+; options has been set by the configuration. This behavior is required, for
+; example, for Cisco CallManager when Q.SIG tunneling is enabled for a
+; gateway where Asterisk lives.
+; The option can be used multiple times, one option per line.
+;tunneling=none ; Totally disable tunneling (default)
+;tunneling=cisco ; Enable Cisco-specific tunneling
+;tunneling=qsig ; Enable tunneling via Q.SIG messages
+;
+; Specify how to pass hold notification to remote party. Default is to
+; use H.450.4 supplementary service message.
+;hold=none ; Do not pass hold/retrieve notifications
+;hold=notify ; Use H.225 NOTIFY message
+;hold=q931only ; Use stripped H.225 NOTIFY message (Q.931 part
+; ; only, usable for Cisco CallManager)
+;hold=h450 ; Pass notification as H.450.4 supplementary
+; ; service
+;
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
+ ; H323 channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The H323 channel can accept jitter,
+ ; thus an enabled jitterbuffer on the receive H323 side will only
+ ; be used if the sending side can create jitter and jbforce is
+ ; also set to yes.
+
+; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a H323
+ ; channel. Defaults to "no".
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usualy sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a H323
+ ; channel. Two implementations are currenlty available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+;
+; H.323 Alias definitions
+;
+; Type 'h323' will register aliases to the endpoint
+; and Gatekeeper, if there is one.
+;
+; Example: if someone calls time@your.asterisk.box.com
+; Asterisk will send the call to the extension 'time'
+; in the context default
+;
+; [default]
+; exten => time,1,Answer
+; exten => time,2,Playback,current-time
+;
+; Keyword's 'prefix' and 'e164' are only make sense when
+; used with a gatekeeper. You can specify either a prefix
+; or E.164 this endpoint is responsible for terminating.
+;
+; Example: The H.323 alias 'det-gw' will tell the gatekeeper
+; to route any call with the prefix 1248 to this alias. Keyword
+; e164 is used when you want to specifiy a full telephone
+; number. So a call to the number 18102341212 would be
+; routed to the H.323 alias 'time'.
+;
+;[time]
+;type=h323
+;e164=18102341212
+;context=default
+;
+;[det-gw]
+;type=h323
+;prefix=1248,1313
+;context=detroit
+;
+;
+; Inbound H.323 calls from BillyBob would land in the incoming
+; context with a maximum of 4 concurrent incoming calls
+;
+;
+; Note: If keyword 'incominglimit' are omitted Asterisk will not
+; enforce any maximum number of concurrent calls.
+;
+;[BillyBob]
+;type=user
+;host=192.168.1.1
+;context=incoming
+;incominglimit=4
+;h245Tunneling=no
+;
+;
+; Outbound H.323 call to Larry using SlowStart
+;
+;[Larry]
+;type=peer
+;host=192.168.2.1
+;fastStart=no
+
+
+
diff --git a/trunk/configs/http.conf.sample b/trunk/configs/http.conf.sample
new file mode 100644
index 000000000..b0c0047aa
--- /dev/null
+++ b/trunk/configs/http.conf.sample
@@ -0,0 +1,71 @@
+;
+; Asterisk Builtin mini-HTTP server
+;
+;
+; Note about Asterisk documentation:
+; If Asterisk was installed from a tarball, then the HTML documentation should
+; be installed in the static-http/docs directory which is
+; (/var/lib/asterisk/static-http/docs) on linux by default. If the Asterisk
+; HTTP server is enabled in this file by setting the "enabled", "bindaddr",
+; and "bindport" options, then you should be able to view the documentation
+; remotely by browsing to:
+; http://<server_ip>:<bindport/asterisk/static/docs/index.html
+;
+[general]
+;
+; Whether HTTP/HTTPS interface is enabled or not. Default is no.
+; This also affects manager/rawman/mxml access (see manager.conf)
+;
+;enabled=yes
+;
+; Address to bind to, both for HTTP and HTTPS. Default is 0.0.0.0
+;
+bindaddr=127.0.0.1
+;
+; Port to bind to for HTTP sessions (default is 8088)
+;
+;bindport=8088
+;
+; Prefix allows you to specify a prefix for all requests
+; to the server. The default is "asterisk" so that all
+; requests must begin with /asterisk
+;
+;prefix=asterisk
+;
+; Whether Asterisk should serve static content from http-static
+; Default is no.
+;
+;enablestatic=yes
+;
+; Redirect one URI to another. This is how you would set a
+; default page.
+; Syntax: redirect=<from here> <to there>
+; For example, if you are using the Asterisk-gui,
+; it is convenient to enable the following redirect:
+;
+;redirect = / /asterisk/static/config/cfgbasic.html
+;
+; HTTPS support. In addition to enabled=yes, you need to
+; explicitly enable ssl, define the port to use,
+; and have a certificate somewhere.
+; sslenable=yes ; enable ssl - default no.
+; sslbindport=4433 ; port to use - default is 8089
+; sslbindaddr=0.0.0.0 ; address to bind to - default is bindaddr.
+;
+; sslcert=/tmp/foo.pem ; path to the certificate
+;
+; To produce a certificate you can e.g. use openssl
+; openssl req -new -x509 -days 365 -nodes -out /tmp/foo.pem -keyout /tmp/foo.pem
+;
+
+; The post_mappings section maps URLs to real paths on the filesystem. If a
+; POST is done from within an authenticated manager session to one of the
+; configured POST mappings, then any files in the POST will be placed in the
+; configured directory.
+;
+;[post_mappings]
+;
+; In this example, if the prefix option is set to "asterisk", then using the
+; POST URL: /asterisk/uploads will put files in /var/lib/asterisk/uploads/.
+;uploads = /var/lib/asterisk/uploads/
+;
diff --git a/trunk/configs/iax.conf.sample b/trunk/configs/iax.conf.sample
new file mode 100644
index 000000000..2441f2cf4
--- /dev/null
+++ b/trunk/configs/iax.conf.sample
@@ -0,0 +1,434 @@
+
+; Inter-Asterisk eXchange driver definition
+;
+; This configuration is re-read at reload
+; or with the CLI command
+; reload chan_iax2.so
+;
+; General settings, like port number to bind to, and
+; an option address (the default is to bind to all
+; local addresses).
+;
+[general]
+;bindport=4569 ; bindport and bindaddr may be specified
+; ; NOTE: bindport must be specified BEFORE
+ ; bindaddr or may be specified on a specific
+ ; bindaddr if followed by colon and port
+ ; (e.g. bindaddr=192.168.0.1:4569)
+;bindaddr=192.168.0.1 ; more than once to bind to multiple
+; ; addresses, but the first will be the
+; ; default
+;
+; Set iaxcompat to yes if you plan to use layered switches or
+; some other scenario which may cause some delay when doing a
+; lookup in the dialplan. It incurs a small performance hit to
+; enable it. This option causes Asterisk to spawn a separate thread
+; when it receives an IAX DPREQ (Dialplan Request) instead of
+; blocking while it waits for a response.
+;
+;iaxcompat=yes
+;
+; Disable UDP checksums (if nochecksums is set, then no checkums will
+; be calculated/checked on systems supporting this feature)
+;
+;nochecksums=no
+;
+;
+; For increased security against brute force password attacks
+; enable "delayreject" which will delay the sending of authentication
+; reject for REGREQ or AUTHREP if there is a password.
+;
+;delayreject=yes
+;
+; You may specify a global default AMA flag for iaxtel calls. It must be
+; one of 'default', 'omit', 'billing', or 'documentation'. These flags
+; are used in the generation of call detail records.
+;
+;amaflags=default
+;
+; ADSI (Analog Display Services Interface) can be enabled if you have
+; (or may have) ADSI compatible CPE equipment
+;
+;adsi=no
+;
+; Perform an SRV lookup on outbound calls
+;
+;srvlookup=yes
+;
+; You may specify a default account for Call Detail Records in addition
+; to specifying on a per-user basis
+;
+;accountcode=lss0101
+;
+; You may specify a global default language for users.
+; Can be specified also on a per-user basis
+; If omitted, will fallback to english
+;
+;language=en
+;
+; This option specifies a preference for which music on hold class this channel
+; should listen to when put on hold if the music class has not been set on the
+; channel with Set(CHANNEL(musicclass)=whatever) in the dialplan, and the peer
+; channel putting this one on hold did not suggest a music class.
+;
+; If this option is set to "passthrough", then the hold message will always be
+; passed through as signalling instead of generating hold music locally.
+;
+; This option may be specified globally, or on a per-user or per-peer basis.
+;
+;mohinterpret=default
+;
+; This option specifies which music on hold class to suggest to the peer channel
+; when this channel places the peer on hold. It may be specified globally or on
+; a per-user or per-peer basis.
+;
+;mohsuggest=default
+;
+; Specify bandwidth of low, medium, or high to control which codecs are used
+; in general.
+;
+bandwidth=low
+;
+; You can also fine tune codecs here using "allow" and "disallow" clauses
+; with specific codecs. Use "all" to represent all formats.
+;
+;allow=all ; same as bandwidth=high
+;disallow=g723.1 ; Hm... Proprietary, don't use it...
+disallow=lpc10 ; Icky sound quality... Mr. Roboto.
+;allow=gsm ; Always allow GSM, it's cool :)
+;
+
+; You can adjust several parameters relating to the jitter buffer.
+; The jitter buffer's function is to compensate for varying
+; network delay.
+;
+; All the jitter buffer settings are in milliseconds.
+; The jitter buffer works for INCOMING audio - the outbound audio
+; will be dejittered by the jitter buffer at the other end.
+;
+; jitterbuffer=yes|no: global default as to whether you want
+; the jitter buffer at all.
+;
+; forcejitterbuffer=yes|no: in the ideal world, when we bridge VoIP channels
+; we don't want to do jitterbuffering on the switch, since the endpoints
+; can each handle this. However, some endpoints may have poor jitterbuffers
+; themselves, so this option will force * to always jitterbuffer, even in this
+; case.
+;
+; maxjitterbuffer: a maximum size for the jitter buffer.
+; Setting a reasonable maximum here will prevent the call delay
+; from rising to silly values in extreme situations; you'll hear
+; SOMETHING, even though it will be jittery.
+;
+; resyncthreshold: when the jitterbuffer notices a significant change in delay
+; that continues over a few frames, it will resync, assuming that the change in
+; delay was caused by a timestamping mix-up. The threshold for noticing a
+; change in delay is measured as twice the measured jitter plus this resync
+; threshold.
+; Resyncing can be disabled by setting this parameter to -1.
+;
+; maxjitterinterps: the maximum number of interpolation frames the jitterbuffer
+; should return in a row. Since some clients do not send CNG/DTX frames to
+; indicate silence, the jitterbuffer will assume silence has begun after
+; returning this many interpolations. This prevents interpolating throughout
+; a long silence.
+;
+;
+; jittertargetextra: number of milliseconds by which the new jitter buffer
+; will pad its size. the default is 40, so without modification, the new
+; jitter buffer will set its size to the jitter value plus 40 milliseconds.
+; increasing this value may help if your network normally has low jitter,
+; but occasionally has spikes.
+;
+
+jitterbuffer=no
+forcejitterbuffer=no
+;maxjitterbuffer=1000
+;maxjitterinterps=10
+;resyncthreshold=1000
+;jittertargetextra=40
+
+;trunkfreq=20 ; How frequently to send trunk msgs (in ms)
+
+; Should we send timestamps for the individual sub-frames within trunk frames?
+; There is a small bandwidth use for these (less than 1kbps/call), but they
+; ensure that frame timestamps get sent end-to-end properly. If both ends of
+; all your trunks go directly to TDM, _and_ your trunkfreq equals the frame
+; length for your codecs, you can probably suppress these. The receiver must
+; also support this feature, although they do not also need to have it enabled.
+;
+; trunktimestamps=yes
+;
+; Minimum and maximum amounts of time that IAX peers can request as
+; a registration expiration interval (in seconds).
+; minregexpire = 60
+; maxregexpire = 60
+;
+; With a large amount of traffic on IAX2 trunks, there is a risk of bad voice quality due to
+; the fact that the IAX2 trunking scheme depends on the Linux system to handle fragmentation of
+; UDP packets. This may not be very efficient.
+; This setting sets the maximum transmission unit for IAX2 UDP trunking.
+; default is 1240 bytes. Zero disables this functionality and let's the O/S handle fragmentation.
+;
+; trunkmtu = 0
+;
+
+; This option defines the maximum size an IAX2 trunk can grow to. The default value is 128000 bytes which
+; represents 40ms uncompressed linear with 200 channels. Depending on different things though
+; (codec in use and channels) you may need to make this value larger.
+; trunkmaxsize = 128000
+
+; IAX helper threads
+
+; Establishes the number of iax helper threads to handle I/O.
+; iaxthreadcount = 10
+; Establishes the number of extra dynamic threads that may be spawned to handle I/O
+; iaxmaxthreadcount = 100
+;
+; We can register with another IAX server to let him know where we are
+; in case we have a dynamic IP address for example
+;
+; Register with tormenta using username marko and password secretpass
+;
+;register => marko:secretpass@tormenta.linux-support.net
+;
+; Register joe at remote host with no password
+;
+;register => joe@remotehost:5656
+;
+; Register marko at tormenta.linux-support.net using RSA key "torkey"
+;
+;register => marko:[torkey]@tormenta.linux-support.net
+;
+; Sample Registration for iaxtel
+;
+; Visit http://www.iaxtel.com to register with iaxtel. Replace "user"
+; and "pass" with your username and password for iaxtel. Incoming
+; calls arrive at the "s" extension of "default" context.
+;
+;register => user:pass@iaxtel.com
+;
+; Sample Registration for IAX + FWD
+;
+; To register using IAX with FWD, it must be enabled by visiting the URL
+; http://www.fwdnet.net/index.php?section_id=112
+;
+; Note that you need an extension in you default context which matches
+; your free world dialup number. Please replace "FWDNumber" with your
+; FWD number and "passwd" with your password.
+;
+;register => FWDNumber:passwd@iax.fwdnet.net
+;
+;
+; You can disable authentication debugging to reduce the amount of
+; debugging traffic.
+;
+;authdebug=no
+;
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of these parameters.
+;tos=ef
+;cos=5
+;
+; If regcontext is specified, Asterisk will dynamically create and destroy
+; a NoOp priority 1 extension for a given peer who registers or unregisters
+; with us. The actual extension is the 'regexten' parameter of the registering
+; peer or its name if 'regexten' is not provided. More than one regexten
+; may be supplied if they are separated by '&'. Patterns may be used in
+; regexten.
+;
+;regcontext=iaxregistrations
+;
+; If we don't get ACK to our NEW within 2000ms, and autokill is set to yes,
+; then we cancel the whole thing (that's enough time for one retransmission
+; only). This is used to keep things from stalling for a long time for a host
+; that is not available, but would be ill advised for bad connections. In
+; addition to 'yes' or 'no' you can also specify a number of milliseconds.
+; See 'qualify' for individual peers to turn on for just a specific peer.
+;
+autokill=yes
+;
+; codecpriority controls the codec negotiation of an inbound IAX call.
+; This option is inherited to all user entities. It can also be defined
+; in each user entity separately which will override the setting in general.
+;
+; The valid values are:
+;
+; caller - Consider the callers preferred order ahead of the host's.
+; host - Consider the host's preferred order ahead of the caller's.
+; disabled - Disable the consideration of codec preference altogether.
+; (this is the original behaviour before preferences were added)
+; reqonly - Same as disabled, only do not consider capabilities if
+; the requested format is not available the call will only
+; be accepted if the requested format is available.
+;
+; The default value is 'host'
+;
+;codecpriority=host
+
+;rtcachefriends=yes ; Cache realtime friends by adding them to the internal list
+ ; just like friends added from the config file only on a
+ ; as-needed basis? (yes|no)
+
+;rtupdate=yes ; Send registry updates to database using realtime? (yes|no)
+ ; If set to yes, when a IAX2 peer registers successfully,
+ ; the ip address, the origination port, the registration period,
+ ; and the username of the peer will be set to database via realtime.
+ ; If not present, defaults to 'yes'.
+
+;rtautoclear=yes ; Auto-Expire friends created on the fly on the same schedule
+ ; as if it had just registered? (yes|no|<seconds>)
+ ; If set to yes, when the registration expires, the friend will
+ ; vanish from the configuration until requested again.
+ ; If set to an integer, friends expire within this number of
+ ; seconds instead of the registration interval.
+
+;rtignoreregexpire=yes ; When reading a peer from Realtime, if the peer's registration
+ ; has expired based on its registration interval, used the stored
+ ; address information regardless. (yes|no)
+
+; Guest sections for unauthenticated connection attempts. Just specify an
+; empty secret, or provide no secret section.
+;
+[guest]
+type=user
+context=default
+callerid="Guest IAX User"
+
+;
+; Trust Caller*ID Coming from iaxtel.com
+;
+[iaxtel]
+type=user
+context=default
+auth=rsa
+inkeys=iaxtel
+
+;
+; Trust Caller*ID Coming from iax.fwdnet.net
+;
+[iaxfwd]
+type=user
+context=default
+auth=rsa
+inkeys=freeworlddialup
+
+;
+; Trust callerid delivered over DUNDi/e164
+;
+;
+;[dundi]
+;type=user
+;dbsecret=dundi/secret
+;context=dundi-e164-local
+
+;
+; Further user sections may be added, specifying a context and a secret used
+; for connections with that given authentication name. Limited IP based
+; access control is allowed by use of "allow" and "deny" keywords. Multiple
+; rules are permitted. Multiple permitted contexts may be specified, in
+; which case the first will be the default. You can also override caller*ID
+; so that when you receive a call you set the Caller*ID to be what you want
+; instead of trusting what the remote user provides
+;
+; There are three authentication methods that are supported: md5, plaintext,
+; and rsa. The least secure is "plaintext", which sends passwords cleartext
+; across the net. "md5" uses a challenge/response md5 sum arrangement, but
+; still requires both ends have plain text access to the secret. "rsa" allows
+; unidirectional secret knowledge through public/private keys. If "rsa"
+; authentication is used, "inkeys" is a list of acceptable public keys on the
+; local system that can be used to authenticate the remote peer, separated by
+; the ":" character. "outkey" is a single, private key to use to authenticate
+; to the other side. Public keys are named /var/lib/asterisk/keys/<name>.pub
+; while private keys are named /var/lib/asterisk/keys/<name>.key. Private
+; keys should always be 3DES encrypted.
+;
+;
+; NOTE: All hostnames and IP addresses in this file are for example purposes
+; only; you should not expect any of them to actually be available for
+; your use.
+;
+;
+;[markster]
+;type=user
+;context=default
+;context=local
+;auth=md5,plaintext,rsa
+;secret=markpasswd
+;setvar=foo=bar
+;dbsecret=mysecrets/place ; Secrets can be stored in astdb, too
+;transfer=no ; Disable IAX native transfer
+;transfer=mediaonly ; When doing IAX native transfers, transfer
+ ; only media stream
+;jitterbuffer=yes ; Override global setting an enable jitter buffer
+; ; for this user
+;maxauthreq=10 ; Set maximum number of outstanding AUTHREQs waiting for replies. Any further authentication attempts will be blocked
+; ; if this limit is reached until they expire or a reply is received.
+;callerid="Mark Spencer" <(256) 428-6275>
+;deny=0.0.0.0/0.0.0.0
+;accountcode=markster0101
+;permit=209.16.236.73/255.255.255.0
+;language=en ; Use english as default language
+;
+; Peers may also be specified, with a secret and
+; a remote hostname.
+;
+[demo]
+type=peer
+username=asterisk
+secret=supersecret
+host=216.207.245.47
+;sendani=no
+;host=asterisk.linux-support.net
+;port=5036
+;mask=255.255.255.255
+;qualify=yes ; Make sure this peer is alive
+;qualifysmoothing = yes ; use an average of the last two PONG
+ ; results to reduce falsely detected LAGGED hosts
+ ; Default: Off
+;qualifyfreqok = 60000 ; how frequently to ping the peer when
+ ; everything seems to be ok, in milliseconds
+;qualifyfreqnotok = 10000 ; how frequently to ping the peer when it's
+ ; either LAGGED or UNAVAILABLE, in milliseconds
+;jitterbuffer=no ; Turn off jitter buffer for this peer
+
+;
+; Peers can remotely register as well, so that they can be mobile. Default
+; IP's can also optionally be given but are not required. Caller*ID can be
+; suggested to the other side as well if it is for example a phone instead of
+; another PBX.
+;
+
+;[dynamichost]
+;host=dynamic
+;secret=mysecret
+;mailbox=1234 ; Notify about mailbox 1234
+;inkeys=key1:key2
+;peercontext=local ; Default context to request for calls to peer
+;defaultip=216.207.245.34
+;callerid="Some Host" <(256) 428-6011>
+;
+
+;
+;[biggateway]
+;type=peer
+;host=192.168.0.1
+;context=*
+;secret=myscret
+;trunk=yes ; Use IAX2 trunking with this host
+;timezone=America/New_York ; Set a timezone for the date/time IE
+;
+
+;
+; Friends are a short cut for creating a user and
+; a peer with the same values.
+;
+;[marko]
+;type=friend
+;host=dynamic
+;regexten=1234
+;secret=moofoo ; Multiple secrets may be specified. For a "user", all
+;secret=foomoo ; specified entries will be accepted as valid. For a "peer",
+;secret=shazbot ; only the last specified secret will be used.
+;context=default
+;permit=0.0.0.0/0.0.0.0
+
diff --git a/trunk/configs/iaxprov.conf.sample b/trunk/configs/iaxprov.conf.sample
new file mode 100644
index 000000000..06891d785
--- /dev/null
+++ b/trunk/configs/iaxprov.conf.sample
@@ -0,0 +1,81 @@
+;
+; IAX2 Provisioning Information
+;
+; Contains provisioning information for templates and for specific service
+; entries.
+;
+; Templates provide a group of settings from which provisioning takes place.
+; A template may be based upon any template that has been specified before
+; it. If the template that an entry is based on is not specified then it is
+; presumed to be 'default' (unless it is the first of course).
+;
+; Templates which begin with 'si-' are used for provisioning units with
+; specific service identifiers. For example the entry "si-000364000126"
+; would be used when the device with the corresponding service identifier of
+; "000364000126" attempts to register or make a call.
+;
+[default]
+;
+; The port number the device should use to bind to. The default is 4569.
+;
+;port=4569
+;
+; server is our PRIMARY server for registration and placing calls
+;
+;server=192.168.69.3
+;
+; altserver is the BACKUP server for registration and placing calls in the
+; event the primary server is unavailable.
+;
+;altserver=192.168.69.4
+;
+; port is the port number to use for IAX2 outbound. The connections to the
+; server and altserver -- default is of course 4569.
+;serverport=4569
+;
+; language is the preferred language for the device
+;
+;language=en
+;
+; codec is the requested codec. The iaxy supports ulaw and adpcm
+;
+codec=ulaw
+;
+; flags is a comma separated list of flags which the device should
+; use and may contain any of the following keywords:
+;
+; "register" - Register with server
+; "secure" - Do not accept calls / provisioning not originated by the server
+; "heartbeat" - Generate status packets on port 9999 sent to 255.255.255.255
+; "debug" - Output extra debugging to port 9999
+;
+; Note that use can use += and -= to adjust parameters
+;
+flags=register,heartbeat
+;
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of this parameter.
+;tos=ef
+;
+; Example iaxy provisioning
+;
+;[si-000364000126]
+;user=iaxy
+;pass=bitsy
+;flags += debug
+
+;[si-000364000127]
+;user=iaxy2
+;pass=bitsy2
+;template=si-000364000126
+;flags += debug
+
+;
+;[*]
+;
+; If specified, the '*' provisioning is used for all devices which do not
+; have another provisioning entry within the file. If unspecified, no
+; provisioning will take place for devices which have no entry. DO NOT
+; USE A '*' PROVISIONING ENTRY UNLESS YOU KNOW WHAT YOU'RE DOING.
+;
+;template=default
+
diff --git a/trunk/configs/indications.conf.sample b/trunk/configs/indications.conf.sample
new file mode 100644
index 000000000..03a3fadb5
--- /dev/null
+++ b/trunk/configs/indications.conf.sample
@@ -0,0 +1,733 @@
+; indications.conf
+; Configuration file for location specific tone indications
+; used by the pbx_indications module.
+;
+; NOTE:
+; When adding countries to this file, please keep them in alphabetical
+; order according to the 2-character country codes!
+;
+; The [general] category is for certain global variables.
+; All other categories are interpreted as location specific indications
+;
+;
+[general]
+country=us ; default location
+
+
+; [example]
+; description = string
+; The full name of your country, in English.
+; alias = iso[,iso]*
+; List of other countries 2-letter iso codes, which have the same
+; tone indications.
+; ringcadence = num[,num]*
+; List of durations the physical bell rings.
+; dial = tonelist
+; Set of tones to be played when one picks up the hook.
+; busy = tonelist
+; Set of tones played when the receiving end is busy.
+; congestion = tonelist
+; Set of tones played when there is some congestion (on the network?)
+; callwaiting = tonelist
+; Set of tones played when there is a call waiting in the background.
+; dialrecall = tonelist
+; Not well defined; many phone systems play a recall dial tone after hook
+; flash.
+; record = tonelist
+; Set of tones played when call recording is in progress.
+; info = tonelist
+; Set of tones played with special information messages (e.g., "number is
+; out of service")
+; 'name' = tonelist
+; Every other variable will be available as a shortcut for the "PlayList" command
+; but will not be used automatically by Asterisk.
+;
+;
+; The tonelist itself is defined by a comma-separated sequence of elements.
+; Each element consist of a frequency (f) with an optional duration (in ms)
+; attached to it (f/duration). The frequency component may be a mixture of two
+; frequencies (f1+f2) or a frequency modulated by another frequency (f1*f2).
+; The implicit modulation depth is fixed at 90%, though.
+; If the list element starts with a !, that element is NOT repeated,
+; therefore, only if all elements start with !, the tonelist is time-limited,
+; all others will repeat indefinitely.
+;
+; concisely:
+; element = [!]freq[+|*freq2][/duration]
+; tonelist = element[,element]*
+;
+; Please note that SPACES ARE NOT ALLOWED in tone lists!
+;
+
+[at]
+description = Austria
+ringcadence = 1000,5000
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+dial = 420
+busy = 420/400,0/400
+ring = 420/1000,0/5000
+congestion = 420/200,0/200
+callwaiting = 420/40,0/1960
+dialrecall = 420
+; RECORDTONE - not specified
+record = 1400/80,0/14920
+info = 950/330,1450/330,1850/330,0/1000
+stutter = 380+420
+
+[au]
+description = Australia
+; Reference http://www.acif.org.au/__data/page/3303/S002_2001.pdf
+; Normal Ring
+ringcadence = 400,200,400,2000
+; Distinctive Ring 1 - Forwarded Calls
+; 400,400,200,200,400,1400
+; Distinctive Ring 2 - Selective Ring 2 + Operator + Recall
+; 400,400,200,2000
+; Distinctive Ring 3 - Multiple Subscriber Number 1
+; 200,200,400,2200
+; Distinctive Ring 4 - Selective Ring 1 + Centrex
+; 400,2600
+; Distinctive Ring 5 - Selective Ring 3
+; 400,400,200,400,200,1400
+; Distinctive Ring 6 - Multiple Subscriber Number 2
+; 200,400,200,200,400,1600
+; Distinctive Ring 7 - Multiple Subscriber Number 3 + Data Privacy
+; 200,400,200,400,200,1600
+; Tones
+dial = 413+438
+busy = 425/375,0/375
+ring = 413+438/400,0/200,413+438/400,0/2000
+; XXX Congestion: Should reduce by 10 db every other cadence XXX
+congestion = 425/375,0/375,420/375,0/375
+callwaiting = 425/200,0/200,425/200,0/4400
+dialrecall = 413+438
+; Record tone used for Call Intrusion/Recording or Conference
+record = !425/1000,!0/15000,425/360,0/15000
+info = 425/2500,0/500
+; Other Australian Tones
+; The STD "pips" indicate the call is not an untimed local call
+std = !525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100
+; Facility confirmation tone (eg. Call Forward Activated)
+facility = 425
+; Message Waiting "stutter" dialtone
+stutter = 413+438/100,0/40
+; Ringtone for calls to Telstra mobiles
+ringmobile = 400+450/400,0/200,400+450/400,0/2000
+
+[bg]
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+description = Bulgaria
+ringdance = 1000,4000
+;
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/250,0/250
+callwaiting = 425/150,0/150,425/150,0/4000
+dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+record = 1400/425,0/15000
+info = 950/330,1400/330,1800/330,0/1000
+stutter = 425/1500,0/100
+
+[br]
+description = Brazil
+ringcadence = 1000,4000
+dial = 425
+busy = 425/250,0/250
+ring = 425/1000,0/4000
+congestion = 425/250,0/250,425/750,0/250
+callwaiting = 425/50,0/1000
+; Dialrecall not used in Brazil standard (using UK standard)
+dialrecall = 350+440
+; Record tone is not used in Brazil, use busy tone
+record = 425/250,0/250
+; Info not used in Brazil standard (using UK standard)
+info = 950/330,1400/330,1800/330
+stutter = 350+440
+
+[be]
+description = Belgium
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,3000
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/3000
+congestion = 425/167,0/167
+callwaiting = 1400/175,0/175,1400/175,0/3500
+; DIALRECALL - not specified
+dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440"
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+info = 900/330,1400/330,1800/330,0/1000
+stutter = 425/1000,0/250
+
+[ch]
+description = Switzerland
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+callwaiting = 425/200,0/200,425/200,0/4000
+; DIALRECALL - not specified
+dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+; RECORDTONE - not specified
+record = 1400/80,0/15000
+info = 950/330,1400/330,1800/330,0/1000
+stutter = 425+340/1100,0/1100
+
+[cl]
+description = Chile
+; According to specs from Telefonica CTC Chile
+ringcadence = 1000,3000
+dial = 400
+busy = 400/500,0/500
+ring = 400/1000,0/3000
+congestion = 400/200,0/200
+callwaiting = 400/250,0/8750
+dialrecall = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
+record = 1400/500,0/15000
+info = 950/333,1400/333,1800/333,0/1000
+stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
+
+[cn]
+description = China
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+dial = 450
+busy = 450/350,0/350
+ring = 450/1000,0/4000
+congestion = 450/700,0/700
+callwaiting = 450/400,0/4000
+dialrecall = 450
+record = 950/400,0/10000
+info = 450/100,0/100,450/100,0/100,450/100,0/100,450/400,0/400
+; STUTTER - not specified
+stutter = 450+425
+
+[cz]
+description = Czech Republic
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+dial = 425/330,0/330,425/660,0/660
+busy = 425/330,0/330
+ring = 425/1000,0/4000
+congestion = 425/165,0/165
+callwaiting = 425/330,0/9000
+; DIALRECALL - not specified
+dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660
+; RECORDTONE - not specified
+record = 1400/500,0/14000
+info = 950/330,0/30,1400/330,0/30,1800/330,0/1000
+; STUTTER - not specified
+stutter = 425/450,0/50
+
+[de]
+description = Germany
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+dial = 425
+busy = 425/480,0/480
+ring = 425/1000,0/4000
+congestion = 425/240,0/240
+callwaiting = !425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,0
+; DIALRECALL - not specified
+dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+; RECORDTONE - not specified
+record = 1400/80,0/15000
+info = 950/330,1400/330,1800/330,0/1000
+stutter = 425+400
+
+[dk]
+description = Denmark
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+callwaiting = !425/200,!0/600,!425/200,!0/3000,!425/200,!0/200,!425/200,0
+; DIALRECALL - not specified
+dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+; RECORDTONE - not specified
+record = 1400/80,0/15000
+info = 950/330,1400/330,1800/330,0/1000
+; STUTTER - not specified
+stutter = 425/450,0/50
+
+[ee]
+description = Estonia
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+dial = 425
+busy = 425/300,0/300
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+; CALLWAIT not in accordance to ITU
+callwaiting = 950/650,0/325,950/325,0/30,1400/1300,0/2600
+; DIALRECALL - not specified
+dialrecall = 425/650,0/25
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+; INFO not in accordance to ITU
+info = 950/650,0/325,950/325,0/30,1400/1300,0/2600
+; STUTTER not specified
+stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+
+[es]
+description = Spain
+ringcadence = 1500,3000
+dial = 425
+busy = 425/200,0/200
+ring = 425/1500,0/3000
+congestion = 425/200,0/200,425/200,0/200,425/200,0/600
+callwaiting = 425/175,0/175,425/175,0/3500
+dialrecall = !425/200,!0/200,!425/200,!0/200,!425/200,!0/200,425
+record = 1400/500,0/15000
+info = 950/330,0/1000
+dialout = 500
+
+
+[fi]
+description = Finland
+ringcadence = 1000,4000
+dial = 425
+busy = 425/300,0/300
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+callwaiting = 425/150,0/150,425/150,0/8000
+dialrecall = 425/650,0/25
+record = 1400/500,0/15000
+info = 950/650,0/325,950/325,0/30,1400/1300,0/2600
+stutter = 425/650,0/25
+
+[fr]
+description = France
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1500,3500
+; Dialtone can also be 440+330
+dial = 440
+busy = 440/500,0/500
+ring = 440/1500,0/3500
+; CONGESTION - not specified
+congestion = 440/250,0/250
+callwait = 440/300,0/10000
+; DIALRECALL - not specified
+dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330
+stutter = !440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,440
+
+[gr]
+description = Greece
+ringcadence = 1000,4000
+dial = 425/200,0/300,425/700,0/800
+busy = 425/300,0/300
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+callwaiting = 425/150,0/150,425/150,0/8000
+dialrecall = 425/650,0/25
+record = 1400/400,0/15000
+info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
+stutter = 425/650,0/25
+
+[hu]
+description = Hungary
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1250,3750
+dial = 425
+busy = 425/300,0/300
+ring = 425/1250,0/3750
+congestion = 425/300,0/300
+callwaiting = 425/40,0/1960
+dialrecall = 425+450
+; RECORDTONE - not specified
+record = 1400/400,0/15000
+info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
+stutter = 350+375+400
+
+[il]
+description = Israel
+ringcadence = 1000,3000
+dial = 414
+busy = 414/500,0/500
+ring = 414/1000,0/3000
+congestion = 414/250,0/250
+callwaiting = 414/100,0/100,414/100,0/100,414/600,0/3000
+dialrecall = !414/100,!0/100,!414/100,!0/100,!414/100,!0/100,414
+record = 1400/500,0/15000
+info = 1000/330,1400/330,1800/330,0/1000
+stutter = !414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,414
+
+
+[in]
+description = India
+ringcadence = 400,200,400,2000
+dial = 400*25
+busy = 400/750,0/750
+ring = 400*25/400,0/200,400*25/400,0/2000
+congestion = 400/250,0/250
+callwaiting = 400/200,0/100,400/200,0/7500
+dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330,0/1000
+stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+
+[it]
+description = Italy
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+dial = 425/200,0/200,425/600,0/1000
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+callwaiting = 425/400,0/100,425/250,0/100,425/150,0/14000
+dialrecall = 470/400,425/400
+record = 1400/400,0/15000
+info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
+stutter = 470/400,425/400
+
+[lt]
+description = Lithuania
+ringcadence = 1000,4000
+dial = 425
+busy = 425/350,0/350
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+callwaiting = 425/150,0/150,425/150,0/4000
+; DIALRECALL - not specified
+dialrecall = 425/500,0/50
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
+; STUTTER - not specified
+stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+
+[jp]
+description = Japan
+ringcadence = 1000,2000
+dial = 400
+busy = 400/500,0/500
+ring = 400+15/1000,0/2000
+congestion = 400/500,0/500
+callwaiting = 400+16/500,0/8000
+dialrecall = !400/200,!0/200,!400/200,!0/200,!400/200,!0/200,400
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330,0
+stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
+
+[mx]
+description = Mexico
+ringcadence = 2000,4000
+dial = 425
+busy = 425/250,0/250
+ring = 425/1000,0/4000
+congestion = 425/250,0/250
+callwaiting = 425/200,0/600,425/200,0/10000
+dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+record = 1400/500,0/15000
+info = 950/330,0/30,1400/330,0/30,1800/330,0/1000
+stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+
+[my]
+description = Malaysia
+ringcadence = 2000,4000
+dial = 425
+busy = 425/500,0/500
+ring = 425/400,0/200
+congestion = 425/500,0/500
+
+[nl]
+description = Netherlands
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+ringcadence = 1000,4000
+; Most of these 425's can also be 450's
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/250,0/250
+callwaiting = 425/500,0/9500
+; DIALRECALL - not specified
+dialrecall = 425/500,0/50
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+info = 950/330,1400/330,1800/330,0/1000
+stutter = 425/500,0/50
+
+[no]
+description = Norway
+ringcadence = 1000,4000
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/200,0/200
+callwaiting = 425/200,0/600,425/200,0/10000
+dialrecall = 470/400,425/400
+record = 1400/400,0/15000
+info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
+stutter = 470/400,425/400
+
+[nz]
+description = New Zealand
+;NOTE - the ITU has different tonesets for NZ, but according to some residents there,
+; this is, indeed, the correct way to do it.
+ringcadence = 400,200,400,2000
+dial = 400
+busy = 400/250,0/250
+ring = 400+450/400,0/200,400+450/400,0/2000
+congestion = 400/375,0/375
+callwaiting = !400/200,!0/3000,!400/200,!0/3000,!400/200,!0/3000,!400/200
+dialrecall = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,400
+record = 1400/425,0/15000
+info = 400/750,0/100,400/750,0/100,400/750,0/100,400/750,0/400
+stutter = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400
+unobtainable = 400/75,0/100,400/75,0/100,400/75,0/100,400/75,0/400
+
+[ph]
+
+; reference http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+
+description = Philippines
+ringcadence = 1000,4000
+dial = 425
+busy = 480+620/500,0/500
+ring = 425+480/1000,0/4000
+congestion = 480+620/250,0/250
+callwaiting = 440/300,0/10000
+; DIALRECALL - not specified
+dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+; INFO - not specified
+info = !950/330,!1400/330,!1800/330,0
+; STUTTER - not specified
+stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+
+
+[pl]
+description = Poland
+ringcadence = 1000,4000
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/500,0/500
+callwaiting = 425/150,0/150,425/150,0/4000
+; DIALRECALL - not specified
+dialrecall = 425/500,0/50
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+; 950/1400/1800 3x0.33 on 1.0 off repeated 3 times
+info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000
+; STUTTER - not specified
+stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+
+[pt]
+description = Portugal
+ringcadence = 1000,5000
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/5000
+congestion = 425/200,0/200
+callwaiting = 440/300,0/10000
+dialrecall = 425/1000,0/200
+record = 1400/500,0/15000
+info = 950/330,1400/330,1800/330,0/1000
+stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+
+[ru]
+; References:
+; http://www.minsvyaz.ru/site.shtml?id=1806
+; http://www.aboutphone.info/lib/gost/45-223-2001.html
+description = Russian Federation / ex Soviet Union
+ringcadence = 1000,4000
+dial = 425
+busy = 425/350,0/350
+ring = 425/1000,0/4000
+congestion = 425/175,0/175
+callwaiting = 425/200,0/5000
+record = 1400/400,0/15000
+info = 950/330,1400/330,1800/330,0/1000
+dialrecall = 425/400,0/40
+stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+
+[se]
+description = Sweden
+ringcadence = 1000,5000
+dial = 425
+busy = 425/250,0/250
+ring = 425/1000,0/5000
+congestion = 425/250,0/750
+callwaiting = 425/200,0/500,425/200,0/9100
+dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+record = 1400/500,0/15000
+info = !950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,0
+stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
+; stutter = 425/320,0/20 ; Real swedish standard, not used for now
+
+[sg]
+description = Singapore
+; Singapore
+; Reference: http://www.ida.gov.sg/idaweb/doc/download/I397/ida_ts_pstn1_i4r2.pdf
+; Frequency specs are: 425 Hz +/- 20Hz; 24 Hz +/- 2Hz; modulation depth 100%; SIT +/- 50Hz
+ringcadence = 400,200,400,2000
+dial = 425
+ring = 425*24/400,0/200,425*24/400,0/2000 ; modulation should be 100%, not 90%
+busy = 425/750,0/750
+congestion = 425/250,0/250
+callwaiting = 425*24/300,0/200,425*24/300,0/3200
+stutter = !425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,425
+info = 950/330,1400/330,1800/330,0/1000 ; not currently in use acc. to reference
+dialrecall = 425*24/500,0/500,425/500,0/2500 ; unspecified in IDA reference, use repeating Holding Tone A,B
+record = 1400/500,0/15000 ; unspecified in IDA reference, use 0.5s tone every 15s
+; additionally defined in reference
+nutone = 425/2500,0/500
+intrusion = 425/250,0/2000
+warning = 425/624,0/4376 ; end of period tone, warning
+acceptance = 425/125,0/125
+holdinga = !425*24/500,!0/500 ; followed by holdingb
+holdingb = !425/500,!0/2500
+
+[th]
+description = Thailand
+ringcadence = 1000,4000
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+dial = 400*50
+busy = 400/500,0/500
+ring = 420/1000,0/5000
+congestion = 400/300,0/300
+callwaiting = 1000/400,10000/400,1000/400
+; DIALRECALL - not specified - use special dial tone instead.
+dialrecall = 400*50/400,0/100,400*50/400,0/100
+; RECORDTONE - not specified
+record = 1400/500,0/15000
+; INFO - specified as an announcement - use special information tones instead
+info = 950/330,1400/330,1800/330
+; STUTTER - not specified
+stutter = !400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,400
+
+[uk]
+description = United Kingdom
+ringcadence = 400,200,400,2000
+; These are the official tones taken from BT SIN350. The actual tones
+; used by BT include some volume differences so sound slightly different
+; from Asterisk-generated ones.
+dial = 350+440
+; Special dial is the intermittent dial tone heard when, for example,
+; you have a divert active on the line
+specialdial = 350+440/750,440/750
+; Busy is also called "Engaged"
+busy = 400/375,0/375
+; "Congestion" is the Beep-bip engaged tone
+congestion = 400/400,0/350,400/225,0/525
+; "Special Congestion" is not used by BT very often if at all
+specialcongestion = 400/200,1004/300
+unobtainable = 400
+ring = 400+450/400,0/200,400+450/400,0/2000
+callwaiting = 400/100,0/4000
+; BT seem to use "Special Call Waiting" rather than just "Call Waiting" tones
+specialcallwaiting = 400/250,0/250,400/250,0/250,400/250,0/5000
+; "Pips" used by BT on payphones. (Sounds wrong, but this is what BT claim it
+; is and I've not used a payphone for years)
+creditexpired = 400/125,0/125
+; These two are used to confirm/reject service requests on exchanges that
+; don't do voice announcements.
+confirm = 1400
+switching = 400/200,0/400,400/2000,0/400
+; This is the three rising tones Doo-dah-dee "Special Information Tone",
+; usually followed by the BT woman saying an appropriate message.
+info = 950/330,0/15,1400/330,0/15,1800/330,0/1000
+; Not listed in SIN350
+record = 1400/500,0/60000
+stutter = 350+440/750,440/750
+
+[us]
+description = United States / North America
+ringcadence = 2000,4000
+dial = 350+440
+busy = 480+620/500,0/500
+ring = 440+480/2000,0/4000
+congestion = 480+620/250,0/250
+callwaiting = 440/300,0/10000
+dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330,0
+stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+
+[us-old]
+description = United States Circa 1950/ North America
+ringcadence = 2000,4000
+dial = 600*120
+busy = 500*100/500,0/500
+ring = 420*40/2000,0/4000
+congestion = 500*100/250,0/250
+callwaiting = 440/300,0/10000
+dialrecall = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330,0
+stutter = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120
+
+[tw]
+description = Taiwan
+; http://nemesis.lonestar.org/reference/telecom/signaling/dialtone.html
+; http://nemesis.lonestar.org/reference/telecom/signaling/busy.html
+; http://www.iproducts.com.tw/ee/kylink/06ky-1000a.htm
+; http://www.pbx-manufacturer.com/ky120dx.htm
+; http://www.nettwerked.net/tones.txt
+; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/taiw_sup/taiw2.htm
+;
+; busy tone 480+620Hz 0.5 sec. on ,0.5 sec. off
+; reorder tone 480+620Hz 0.25 sec. on,0.25 sec. off
+; ringing tone 440+480Hz 1 sec. on ,2 sec. off
+;
+ringcadence = 1000,4000
+dial = 350+440
+busy = 480+620/500,0/500
+ring = 440+480/1000,0/2000
+congestion = 480+620/250,0/250
+callwaiting = 350+440/250,0/250,350+440/250,0/3250
+dialrecall = 300/1500,0/500
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330,0
+stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+
+[ve]
+; Tone definition source for ve found on
+; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
+description = Venezuela / South America
+ringcadence = 1000,4000
+dial = 425
+busy = 425/500,0/500
+ring = 425/1000,0/4000
+congestion = 425/250,0/250
+callwaiting = 400+450/300,0/6000
+dialrecall = 425
+record = 1400/500,0/15000
+info = !950/330,!1440/330,!1800/330,0/1000
+
+
+[za]
+description = South Africa
+; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/safr_sup/saf02.htm
+; (definitions for other countries can also be found there)
+; Note, though, that South Africa uses two switch types in their network --
+; Alcatel switches -- mainly in the Western Cape, and Siemens elsewhere.
+; The former use 383+417 in dial, ringback etc. The latter use 400*33
+; I've provided both, uncomment the ones you prefer
+ringcadence = 400,200,400,2000
+; dial/ring/callwaiting for the Siemens switches:
+dial = 400*33
+ring = 400*33/400,0/200,400*33/400,0/2000
+callwaiting = 400*33/250,0/250,400*33/250,0/250,400*33/250,0/250,400*33/250,0/250
+; dial/ring/callwaiting for the Alcatel switches:
+; dial = 383+417
+; ring = 383+417/400,0/200,383+417/400,0/2000
+; callwaiting = 383+417/250,0/250,383+417/250,0/250,383+417/250,0/250,383+417/250,0/250
+congestion = 400/250,0/250
+busy = 400/500,0/500
+dialrecall = 350+440
+; XXX Not sure about the RECORDTONE
+record = 1400/500,0/10000
+info = 950/330,1400/330,1800/330,0/330
+stutter = !400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,400*33
diff --git a/trunk/configs/jabber.conf.sample b/trunk/configs/jabber.conf.sample
new file mode 100644
index 000000000..7d45083f9
--- /dev/null
+++ b/trunk/configs/jabber.conf.sample
@@ -0,0 +1,21 @@
+[general]
+;debug=yes ;;Turn on debugging by default.
+;autoprune=yes ;;Auto remove users from buddy list.
+;autoregister=yes ;;Auto register users from buddy list.
+
+;[asterisk] ;;label
+;type=client ;;Client or Component connection
+;serverhost=astjab.org ;;Route to server for example,
+ ;; talk.google.com
+;username=asterisk@astjab.org/asterisk ;;Username with optional roster.
+;secret=blah ;;Password
+;priority=1 ;;Resource priority
+;port=5222 ;;Port to use defaults to 5222
+;usetls=yes ;;Use tls or not
+;usesasl=yes ;;Use sasl or not
+;buddy=mogorman@astjab.org ;;Manual addition of buddy to list.
+;status=available ;;One of: chat, available, away,
+ ;; xaway, or dnd
+;statusmessage="I am available" ;;Have custom status message for
+ ;;Asterisk.
+;timeout=100 ;;Timeout on the message stack.
diff --git a/trunk/configs/jingle.conf.sample b/trunk/configs/jingle.conf.sample
new file mode 100644
index 000000000..da629b626
--- /dev/null
+++ b/trunk/configs/jingle.conf.sample
@@ -0,0 +1,19 @@
+;[general]
+;context=default ;;Context to dump call into
+;allowguest=yes ;;Allow calls from people not in
+ ;;list of peers
+;
+;[guest] ;;special account for options on guest account
+;disallow=all
+;allow=ulaw
+;context=guest
+;
+;[ogorman]
+;username=ogorman@gmail.com ;;username of the peer your
+ ;;calling or accepting calls from
+;disallow=all
+;allow=ulaw
+;context=default
+;connection=asterisk ;;client or component in jabber.conf
+ ;;for the call to leave on.
+;
diff --git a/trunk/configs/logger.conf.sample b/trunk/configs/logger.conf.sample
new file mode 100644
index 000000000..a441ebd8f
--- /dev/null
+++ b/trunk/configs/logger.conf.sample
@@ -0,0 +1,96 @@
+;
+; Logging Configuration
+;
+; In this file, you configure logging to files or to
+; the syslog system.
+;
+; "logger reload" at the CLI will reload configuration
+; of the logging system.
+
+[general]
+;
+; Customize the display of debug message time stamps
+; this example is the ISO 8601 date format (yyyy-mm-dd HH:MM:SS)
+;
+; see strftime(3) Linux manual for format specifiers. Note that there is also
+; a fractional second parameter which may be used in this field. Use %1q
+; for tenths, %2q for hundredths, etc.
+;
+;dateformat=%F %T ; ISO 8601 date format
+;dateformat=%F %T.%3q ; with milliseconds
+;
+; This appends the hostname to the name of the log files.
+;appendhostname = yes
+;
+; This determines whether or not we log queue events to a file
+; (defaults to yes).
+;queue_log = no
+;
+; Set the queue_log filename
+; (defaults to queue_log)
+;queue_log_name = queue_log
+;
+; Log rotation strategy:
+; sequential: Rename archived logs in order, such that the newest
+; has the highest sequence number [default].
+; rotate: Rotate all the old files, such that the oldest has the
+; highest sequence number [this is the expected behavior
+; for Unix administrators].
+; timestamp: Rename the logfiles using a timestamp instead of a
+; sequence number when "logger rotate" is executed.
+;rotatestrategy = rotate
+;
+; Run a system command after rotating the files. This is mainly
+; useful for rotatestrategy=rotate. The example allows the last
+; two archive files to remain uncompressed, but after that point,
+; they are compressed on disk.
+;
+; exec_after_rotate=gzip -9 ${filename}.2
+;
+; This determines whether or not we log generic events to a file
+; (defaults to yes).
+;event_log = no
+;
+;
+; For each file, specify what to log.
+;
+; For console logging, you set options at start of
+; Asterisk with -v for verbose and -d for debug
+; See 'asterisk -h' for more information.
+;
+; Directory for log files is configures in asterisk.conf
+; option astlogdir
+;
+[logfiles]
+;
+; Format is "filename" and then "levels" of debugging to be included:
+; debug
+; notice
+; warning
+; error
+; verbose
+; dtmf
+;
+; Special filename "console" represents the system console
+;
+; We highly recommend that you DO NOT turn on debug mode if you are simply
+; running a production system. Debug mode turns on a LOT of extra messages,
+; most of which you are unlikely to understand without an understanding of
+; the underlying code. Do NOT report debug messages as code issues, unless
+; you have a specific issue that you are attempting to debug. They are
+; messages for just that -- debugging -- and do not rise to the level of
+; something that merit your attention as an Asterisk administrator. Debug
+; messages are also very verbose and can and do fill up logfiles quickly;
+; this is another reason not to have debug mode on a production system unless
+; you are in the process of debugging a specific issue.
+;
+;debug => debug
+console => notice,warning,error
+;console => notice,warning,error,debug
+messages => notice,warning,error
+;full => notice,warning,error,debug,verbose
+
+;syslog keyword : This special keyword logs to syslog facility
+;
+;syslog.local0 => notice,warning,error
+;
diff --git a/trunk/configs/manager.conf.sample b/trunk/configs/manager.conf.sample
new file mode 100644
index 000000000..80bea5a70
--- /dev/null
+++ b/trunk/configs/manager.conf.sample
@@ -0,0 +1,97 @@
+;
+; AMI - The Asterisk Manager Interface
+;
+; Third party application call management support and PBX event supervision
+;
+; This configuration file is read every time someone logs in
+;
+; Use the "manager show commands" at the CLI to list available manager commands
+; and their authorization levels.
+;
+; "manager show command <command>" will show a help text.
+;
+; ---------------------------- SECURITY NOTE -------------------------------
+; Note that you should not enable the AMI on a public IP address. If needed,
+; block this TCP port with iptables (or another FW software) and reach it
+; with IPsec, SSH, or SSL vpn tunnel. You can also make the manager
+; interface available over http/https if Asterisk's http server is enabled in
+; http.conf and if both "enabled" and "webenabled" are set to yes in
+; this file. Both default to no. httptimeout provides the maximum
+; timeout in seconds before a web based session is discarded. The
+; default is 60 seconds.
+;
+[general]
+enabled = no
+;webenabled = yes
+port = 5038
+
+;httptimeout = 60
+; a) httptimeout sets the Max-Age of the http cookie
+; b) httptimeout is the amount of time the webserver waits
+; on a action=waitevent request (actually its httptimeout-10)
+; c) httptimeout is also the amount of time the webserver keeps
+; a http session alive after completing a successful action
+
+bindaddr = 0.0.0.0
+
+; Parameters that control AMI over TLS. ("enabled" must be set too).
+; You can open a connection to this socket with e.g.
+;
+; openssl s_client -connect my_host:5039
+;
+; sslenable=no ; set to YES to enable it
+; sslbindport=5039 ; the port to bind to
+; sslbindaddr=0.0.0.0 ; address to bind to, default to bindaddr
+; sslcert=/tmp/asterisk.pem ; path to the certificate.
+
+
+;
+;allowmultiplelogin = yes ; IF set to no, rejects manager logins that are already in use.
+; ; The default is yes.
+;
+;displayconnects = yes
+;
+; Add a Unix epoch timestamp to events (not action responses)
+;
+;timestampevents = yes
+
+; debug = on ; enable some debugging info in AMI messages (default off).
+ ; Also accessible through the "manager debug" CLI command.
+;[mark]
+;secret = mysecret
+;deny=0.0.0.0/0.0.0.0
+;permit=209.16.236.73/255.255.255.0
+;
+; If the device connected via this user accepts input slowly,
+; the timeout for writes to it can be increased to keep it
+; from being disconnected (value is in milliseconds)
+;
+; writetimeout = 100
+;
+;displayconnects = yes ; Display on CLI user login/logoff
+;
+; Authorization for various classes
+;
+; Read authorization permits you to receive asynchronous events, in general.
+; Write authorization permits you to send commands and get back responses. The
+; following classes exist:
+;
+; system - General information about the system and ability to run system
+; management commands, such as Shutdown, Restart, and Reload.
+; call - Information about channels and ability to set information in a
+; running channel.
+; log - Logging information. Read-only.
+; verbose - Verbose information. Read-only.
+; agent - Information about queues and agents and ability to add queue
+; members to a queue.
+; user - Permission to send and receive UserEvent.
+; config - Ability to read and write configuration files.
+; command - Permission to run CLI commands. Write-only.
+; dtmf - Receive DTMF events. Read-only.
+; reporting - Ability to get information about the system.
+; cdr - Output of cdr_manager, if loaded. Read-only.
+; dialplan - Receive NewExten and VarSet events. Read-only.
+;
+;read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
+;write = system,call,agent,user,config,command,reporting
+
diff --git a/trunk/configs/meetme.conf.sample b/trunk/configs/meetme.conf.sample
new file mode 100644
index 000000000..a28c5d3c9
--- /dev/null
+++ b/trunk/configs/meetme.conf.sample
@@ -0,0 +1,45 @@
+;
+; Configuration file for MeetMe simple conference rooms for Asterisk of course.
+;
+; This configuration file is read every time you call app meetme()
+
+[general]
+;audiobuffers=32 ; The number of 20ms audio buffers to be used
+ ; when feeding audio frames from non-Zap channels
+ ; into the conference; larger numbers will allow
+ ; for the conference to 'de-jitter' audio that arrives
+ ; at different timing than the conference's timing
+ ; source, but can also allow for latency in hearing
+ ; the audio from the speaker. Minimum value is 2,
+ ; maximum value is 32.
+;
+; Conferences may be scheduled from realtime?
+;schedule=yes
+;
+; Update realtime when members login/out of the conference
+;logmembercount=no
+;
+; How much earlier than the start time should we allow participants to
+; join the conference (in seconds)?
+;fuzzystart=300
+;
+; If the participants join too early, how much time should we allow
+; to tell them that they've joined too early, rather than telling them
+; the conference simply doesn't exist (in seconds)?
+;earlyalert=3600
+;
+; How many seconds before the scheduled end of the conference should
+; the participants be warned?
+;endalert=120
+;
+[rooms]
+;
+; Usage is conf => confno[,pin][,adminpin]
+;
+; Note that once a participant has called the conference, a change to the pin
+; number done in this file will not take effect until there are no more users
+; in the conference and it goes away. When it is created again, it will have
+; the new pin number.
+;
+;conf => 1234
+;conf => 2345,9938
diff --git a/trunk/configs/mgcp.conf.sample b/trunk/configs/mgcp.conf.sample
new file mode 100644
index 000000000..104891e8a
--- /dev/null
+++ b/trunk/configs/mgcp.conf.sample
@@ -0,0 +1,110 @@
+;
+; MGCP Configuration for Asterisk
+;
+[general]
+;port = 2427
+;bindaddr = 0.0.0.0
+
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of these parameters.
+;tos=cs3 ; Sets TOS for signaling packets.
+;tos_audio=ef ; Sets TOS for RTP audio packets.
+;cos=3 ; Sets 802.1p priority for signaling packets.
+;cos_audio=5 ; Sets 802.1p priority for RTP audio packets.
+
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
+ ; MGCP channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The MGCP channel can accept jitter,
+ ; thus an enabled jitterbuffer on the receive MGCP side will only
+ ; be used if the sending side can create jitter and jbforce is
+ ; also set to yes.
+
+; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a MGCP
+ ; channel. Defaults to "no".
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a MGCP
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+
+;[dlinkgw]
+;host = 192.168.0.64
+;context = default
+;canreinvite = no
+;line => aaln/2
+;line => aaln/1
+
+;; The MGCP channel supports the following service codes:
+;; # - Transfer
+;; *67 - Calling Number Delivery Blocking
+;; *70 - Cancel Call Waiting
+;; *72 - Call Forwarding Activation
+;; *73 - Call Forwarding Deactivation
+;; *78 - Do Not Disturb Activation
+;; *79 - Do Not Disturb Deactivation
+;; *8 - Call pick-up
+;
+; known to work with Swissvoice IP10s
+;[192.168.1.20]
+;context=local
+;host=192.168.1.20
+;callerid = "John Doe" <123>
+;callgroup=0 ; in the range from 0 to 63
+;pickupgroup=0 ; in the range from 0 to 63
+;nat=no
+;threewaycalling=yes
+;transfer=yes ; transfer requires threewaycalling=yes. Use FLASH to transfer
+;callwaiting=yes ; this might be a cause of trouble for ip10s
+;cancallforward=yes
+;line => aaln/1
+;
+
+;[dph100]
+;
+; Supporting the DPH100M requires defining DLINK_BUGGY_FIRMWARE in
+; chan_mgcp.c in addition to enabling the slowsequence mode due to
+; bugs in the D-Link firmware
+;
+;context=local
+;host=dynamic
+;dtmfmode=none ; DTMF Mode can be 'none', 'rfc2833', or 'inband' or
+ ; 'hybrid' which starts in none and moves to inband. Default is none.
+;slowsequence=yes ; The DPH100M does not follow MGCP standards for sequencing
+;line => aaln/1
+
+; known to work with wave7optics FTTH LMGs
+;[192.168.1.20]
+;accountcode = 1000 ; record this in cdr as account identification for billing
+;amaflags = billing ; record this in cdr as flagged for 'billing',
+ ; 'documentation', or 'omit'
+;context = local
+;host = 192.168.1.20
+;wcardep = aaln/* ; enables wildcard endpoint and sets it to 'aaln/*'
+ ; another common format is '*'
+;callerid = "Duane Cox" <123> ; now lets setup line 1 using per endpoint configuration...
+;callwaiting = no
+;callreturn = yes
+;cancallforward = yes
+;canreinvite = no
+;transfer = no
+;dtmfmode = inband
+;line => aaln/1 ; now lets save this config to line1 aka aaln/1
+;callerid = "Duane Cox" <456> ; now lets setup line 2
+;callwaiting = no
+;callreturn = yes
+;cancallforward = yes
+;canreinvite = no
+;transfer = no
+;dtmfmode = inband
+;line => aaln/2 ; now lets save this config to line2 aka aaln/2
diff --git a/trunk/configs/minivm.conf.sample b/trunk/configs/minivm.conf.sample
new file mode 100644
index 000000000..21d18e0c6
--- /dev/null
+++ b/trunk/configs/minivm.conf.sample
@@ -0,0 +1,218 @@
+;
+; Mini-Voicemail Configuration
+; for the MiniVM set of applications
+;
+; MiniVM consists of the following dialplan applications
+; MinivmGreet Play personal prompts for busy/unavailable/temporary messages or default prompts
+; MinivmRecord Record voice prompts to account directory or default directory
+; MinivmNotify Notify via e-mail or pager - with or without attachment
+; MinivmDelete Delete voice prompt (filename as argument or channel variable set by MinivmRecord)
+;
+; MiniVM works without accounts (just give e-mail address as argument) or with accounts in
+; this configuration file or realtime. The idea is to build voicemail as building blocks so that
+; a complete and adaptive voicemail system can be built in the dialplan
+;
+;------------------------------ Variables to use in subject, from and message body ------------------
+; Change the from, body and/or subject, variables:
+; MVM_NAME, MVM_DUR, MVM_MSGNUM, VM_MAILBOX, MVM_CALLERID, MVM_CIDNUM,
+; MVM_CIDNAME, MVM_DATE
+;
+; In addition to these, you can set the MVM_COUNTER channel variable in the
+; dial plan and use that as a counter. It will also be used in the file name
+; of the media file attached to the message
+;
+; Note: The emailbody config row can only be up to 512 characters due to a
+; limitation in the Asterisk configuration subsystem.
+; To create longer mails, use the templatefile option when creating the template
+;----------------------------------------------------------------------------------------------------
+
+[general]
+; Default format for storing and sending voicemail
+; (only one format. Can also be set on a per-mailbox level)
+format=wav49
+;format=gsm
+;
+;Turn on logfile with the following syntax. One line per voicemail received
+;with minivmRecord()
+; Mailbox:domain:macrocontext:exten:priority:callerchan:callerid:origdate:origtime:duration:durationstatus:accountcode
+;logfile=/var/log/asterisk/minivm.log
+; Who the e-mail notification should appear to come from
+serveremail=asterisk
+;serveremail=asterisk@asterisk.example.com
+; Minimum length of a voicemail message in seconds for the message to be kept
+; The default is no minimum.
+;minmessage=3
+; How many seconds of silence before we end the recording
+maxsilence=10
+; Silence threshold (what we consider silence: the lower, the more sensitive)
+silencethreshold=128
+; How long greeting messages (busy/unavailable/temp/name) are allowed to be, in seconds
+;maxgreet=120
+; If you need to have an external program, i.e. /usr/bin/myapp called when a
+; voicemail is received by the server. The arguments are
+;
+; <app> <username@domain> <callerid-number> <callerid-name>
+;
+;externnotify=/usr/bin/myapp
+; The character set for voicemail messages can be specified here
+;charset=ISO-8859-1
+; Skip the "[PBX]:" string from the message title
+;pbxskip=yes
+; Change the From: string
+
+; You can override the default program to send e-mail if you wish, too
+; This is used both for e-mail and pager messages
+;mailcmd=/usr/sbin/sendmail -t
+;
+;--------------Default e-mail message template (used if no templates are used) ------
+;fromstring=The Asterisk PBX
+;
+
+;emailsubject=[PBX]: New message ${MVM_COUNER} in mailbox ${VM_MAILBOX}
+; The following definition is very close to the default, but the default shows
+; just the CIDNAME, if it is not null, otherwise just the CIDNUM, or "an unknown
+; caller", if they are both null.
+;emailbody=Dear ${MVM_NAME}:\n\n\tjust wanted to let you know you were just left a ${MVM_DUR} long message (number ${MVM_COUNTER})\nin mailbox ${MVM_MAILBOX} from ${MVM_CALLERID}, on ${MVM_DATE}, so you might\nwant to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n
+;
+; Set the date format on outgoing mails. Valid arguments can be found on the
+; strftime(3) man page
+;
+; Default
+emaildateformat=%A, %B %d, %Y at %r
+; 24h date format
+;emaildateformat=%A, %d %B %Y at %H:%M:%S
+;
+;--------------Default pager message template (used if no templates are used) ------
+; You can also change the Pager From: string, the pager body and/or subject.
+; The above defined variables also can be used here
+;pagerfromstring=The Asterisk PBX
+;pagersubject=New VM ${MVM_COUNTER}
+;pagerbody=New ${MVM_DUR} long msg in box ${MVM_MAILBOX}\nfrom ${MVM_CALLERID}, on ${MVM_DATE}
+;
+;
+;--------------Timezone definitions (used in voicemail accounts) -------------------
+;
+; Users may be located in different timezones, or may have different
+; message announcements for their introductory message when they enter
+; the voicemail system. Set the message and the timezone each user
+; hears here. Set the user into one of these zones with the tz= attribute
+; in the options field of the mailbox. Of course, language substitution
+; still applies here so you may have several directory trees that have
+; alternate language choices.
+;
+; Look in /usr/share/zoneinfo/ for names of timezones.
+; Look at the manual page for strftime for a quick tutorial on how the
+; variable substitution is done on the values below.
+;
+; Supported values:
+; 'filename' filename of a soundfile (single ticks around the filename
+; required)
+; ${VAR} variable substitution
+; A or a Day of week (Saturday, Sunday, ...)
+; B or b or h Month name (January, February, ...)
+; d or e numeric day of month (first, second, ..., thirty-first)
+; Y Year
+; I or l Hour, 12 hour clock
+; H Hour, 24 hour clock (single digit hours preceded by "oh")
+; k Hour, 24 hour clock (single digit hours NOT preceded by "oh")
+; M Minute, with 00 pronounced as "o'clock"
+; N Minute, with 00 pronounced as "hundred" (US military time)
+; P or p AM or PM
+; Q "today", "yesterday" or ABdY
+; (*note: not standard strftime value)
+; q "" (for today), "yesterday", weekday, or ABdY
+; (*note: not standard strftime value)
+; R 24 hour time, including minute
+;
+; The message here is not used in mini-voicemail, but stays for
+; backwards compatibility
+
+[zonemessages]
+eastern=America/New_York|'vm-received' Q 'digits/at' IMp
+central=America/Chicago|'vm-received' Q 'digits/at' IMp
+central24=America/Chicago|'vm-received' q 'digits/at' H N 'hours'
+military=Zulu|'vm-received' q 'digits/at' H N 'hours' 'phonetic/z_p'
+
+;----------------------- Message body templates---------------------
+; [template-name] ; "template-" is a verbatim marker
+; fromaddress = Your Friendly Asterisk Server
+; fromemail = asteriskvm@digium.com
+; subject = <string>
+; attachmedia = yes | no ; Add media file as attachment?
+; dateformat = <formatstring> ; See above
+; charset = <charset> ; Mime charset definition for e-mail messages
+; locale = <locale> ; Locale for LC_TIME - to get weekdays in local language
+; ; See your O/S documentation for proper settings for setlocale()
+; templatefile = <filename> ; File name (relative to Asterisk configuration directory,
+ ; or absolute
+; messagebody = Format ; Message body definition with variables
+;
+[template-sv_SE_email]
+messagebody=Hej ${MVM_NAME}:\n\n\tDu har fått ett röstbrevlåde-meddelande från ${MVM_CALLERID}.\nLängd: ${MVM_DUR}\nMailbox ${MVM_MAILBOX}\nDatum: ${MVM_DATE}. \nMeddelandet bifogas det här brevet. Om du inte kan läsa det, kontakta intern support. \nHälsningar\n\n\t\t\t\t--Asterisk\n
+subject = Du har fått röstmeddelande (se bilaga)
+fromemail = swedish-voicemail-service@stockholm.example.com
+fromaddress = Asterisk Röstbrevlåda
+charset=iso-8859-1
+attachmedia=yes
+dateformat=%A, %d %B %Y at %H:%M:%S
+locale=sv_SE
+
+[template-en_US_email]
+messagebody=Dear ${MVM_NAME}:\n\n\tjust wanted to let you know you were just left a ${MVM_DUR} long message \nin mailbox ${MVM_MAILBOX} from ${MVM_CALLERID}, on ${MVM_DATE}, so you might\nwant to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n
+subject = New voicemail
+charset=ascii
+attachmedia=yes
+dateformat=%A, %B %d, %Y at %r
+
+;[template-sv_SE_pager]
+;templatefile = templates/pager_sv_se.txt
+;subject = Du har fått voicemail
+;charset=iso-8859-1
+;attachmedia=no
+;locale=sv_SE
+
+;[template-nb_NO_email]
+;templatefile = templates/email_nb_NO.txt
+;subject = Du har fått voicemail
+;charset=iso-8859-1
+;locale=nb_NO
+
+;[template-en_US_email_southern]
+;templatefile = templates/email_en_US.txt
+;subject = Y'all got voicemail, honey!
+;charset=ascii
+
+;[template-en_UK_email]
+;templatefile = templates/email_en_us.txt
+;subject = Dear old chap, you've got an electronic communique
+;charset=ascii
+
+;----------------------- Mailbox accounts --------------------------
+;Template for mailbox definition - all options
+;
+; [username@domain] ; Has to be unique within domain (MWM_USERNAME, MWM_DOMAIN)
+; etemplate = sv_SE ; Email template from [templates]
+; ptemplate = en_US ; Pager template from [templates]
+; email = userpart@domain ; Extra e-mail address (overrides mailbox name)
+; pager = pageremail@domain ; E-mail address for pager messages
+; fullname = Mark Spencer ; Full name (MWM_NAME)
+; options = ; E-mail options, se below
+; accountcode = ; Account code (read in dialplan function MINIVMACCOUNT)
+; pincode = ; Numeric pin code (read in dialplan function MINIVMACCOUNT)
+; timezone=se ; Time zone
+; serveremail = asterisk@digium.com ; Who to send email from (overrides template if set)
+; externnotify = <application> ; External application for this account
+; volgain = ; Volume gain setting (requires "sox")
+; setvar=SERVICENAME=Voop.com Networks ; Extra variables to use in template
+
+; Remember that you can use Asterisk Configuration Templates (ACT)
+
+; [template@example.com](!) ; Declare template
+; setvar=customerdomain=example.com
+; setvar=customerclass=gold
+; etemplate = sv_se_email
+; serveremail = voicemail@example.com
+
+; [user2@example.com](template@example.com) ; Declare user2 account using template
+; fullname = Olle E. Johansson
+; ; User inherits everything from template
diff --git a/trunk/configs/misdn.conf.sample b/trunk/configs/misdn.conf.sample
new file mode 100644
index 000000000..65bdda3ea
--- /dev/null
+++ b/trunk/configs/misdn.conf.sample
@@ -0,0 +1,455 @@
+;
+; chan_misdn sample config
+;
+
+; general section:
+;
+; for debugging and general setup, things that are not bound to port groups
+;
+
+[general]
+;
+; Sets the Path to the misdn-init.conf (for nt_ptp mode checking)
+;
+misdn_init=/etc/misdn-init.conf
+
+; set debugging flag:
+; 0 - No Debug
+; 1 - mISDN Messages and * - Messages, and * - State changes
+; 2 - Messages + Message specific Informations (e.g. bearer capability)
+; 3 - very Verbose, the above + lots of Driver specific infos
+; 4 - even more Verbose than 3
+;
+; default value: 0
+;
+debug=0
+
+
+
+; set debugging file and flags for mISDNuser (NT-Stack)
+;
+; flags can be or'ed with the following values:
+;
+; DBGM_NET 0x00000001
+; DBGM_MSG 0x00000002
+; DBGM_FSM 0x00000004
+; DBGM_TEI 0x00000010
+; DBGM_L2 0x00000020
+; DBGM_L3 0x00000040
+; DBGM_L3DATA 0x00000080
+; DBGM_BC 0x00000100
+; DBGM_TONE 0x00000200
+; DBGM_BCDATA 0x00000400
+; DBGM_MAN 0x00001000
+; DBGM_APPL 0x00002000
+; DBGM_ISDN 0x00004000
+; DBGM_SOCK 0x00010000
+; DBGM_CONN 0x00020000
+; DBGM_CDATA 0x00040000
+; DBGM_DDATA 0x00080000
+; DBGM_SOUND 0x00100000
+; DBGM_SDATA 0x00200000
+; DBGM_TOPLEVEL 0x40000000
+; DBGM_ALL 0xffffffff
+;
+
+ntdebugflags=0
+ntdebugfile=/var/log/misdn-nt.log
+
+
+; some pbx systems do cut the L1 for some milliseconds, to avoid
+; dropping running calls, we can set this flag to yes and tell
+; mISDNuser not to drop the calls on L2_RELEASE
+ntkeepcalls=no
+
+; the big trace
+;
+; default value: [not set]
+;
+;tracefile=/var/log/asterisk/misdn.log
+
+
+; set to yes if you want mISDN_dsp to bridge the calls in HW
+;
+; default value: yes
+;
+bridging=no
+
+
+;
+; watches the L1s of every port. If one l1 is down it tries to
+; get it up. The timeout is given in seconds. with 0 as value it
+; does not watch the l1 at all
+;
+; default value: 0
+;
+; this option is only read at loading time of chan_misdn,
+; which means you need to unload and load chan_misdn to change the
+; value, an asterisk restart should do the trick
+;
+l1watcher_timeout=0
+
+; stops dialtone after getting first digit on nt Port
+;
+; default value: yes
+;
+stop_tone_after_first_digit=yes
+
+; whether to append overlapdialed Digits to Extension or not
+;
+; default value: yes
+;
+append_digits2exten=yes
+
+;;; CRYPTION STUFF
+
+; Whether to look for dynamic crypting attempt
+;
+; default value: no
+;
+dynamic_crypt=no
+
+; crypt_prefix, what is used for crypting Protocol
+;
+; default value: [not set]
+;
+crypt_prefix=**
+
+; Keys for cryption, you reference them in the dialplan
+; later also in dynamic encr.
+;
+; default value: [not set]
+;
+crypt_keys=test,muh
+
+; users sections:
+;
+; name your sections as you which but not "general" !
+; the sections are Groups, you can dial out in extensions.conf
+; with Dial(mISDN/g:extern/101) where extern is a section name,
+; chan_misdn tries every port in this section to find a
+; new free channel
+;
+
+; The default section is not a group section, it just contains config elements
+; which are inherited by group sections.
+;
+
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
+ ; SIP channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The SIP channel can accept jitter,
+ ; thus a jitterbuffer on the receive SIP side will be used only
+ ; if it is forced and enabled.
+
+; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a SIP
+ ; channel. Defaults to "no".
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmaxsize) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+
+[default]
+
+; define your default context here
+;
+; default value: default
+;
+context=misdn
+
+; language
+;
+; default value: en
+;
+language=en
+
+;
+; sets the musiconhold class
+;
+musicclass=default
+
+;
+; Either if we should produce DTMF Tones ourselves
+;
+senddtmf=yes
+
+;
+; If we should generate Ringing for chan_sip and others
+;
+far_alerting=no
+
+
+;
+; here you can define which bearers should be allowed
+;
+allowed_bearers=all
+
+; Prefixes for national and international, those are put before the
+; oad if an according dialplan is set by the other end.
+;
+; default values: nationalprefix : 0
+; internationalprefix : 00
+;
+nationalprefix=0
+internationalprefix=00
+
+; set rx/tx gains between -8 and 8 to change the RX/TX Gain
+;
+; default values: rxgain: 0
+; txgain: 0
+;
+rxgain=0
+txgain=0
+
+; some telcos especially in NL seem to need this set to yes, also in
+; switzerland this seems to be important
+;
+; default value: no
+;
+te_choose_channel=no
+
+
+
+;
+; This option defines, if chan_misdn should check the L1 on a PMP
+; before making a group call on it. The L1 may go down for PMP Ports
+; so we might need this.
+; But be aware! a broken or plugged off cable might be used for a group call
+; as well, since chan_misdn has no chance to distinguish if the L1 is down
+; because of a lost Link or because the Provider shut it down...
+;
+; default: no
+;
+pmp_l1_check=no
+
+
+;
+; in PMP this option defines which cause should be sent out to
+; the 3. caller. chan_misdn does not support callwaiting on TE
+; PMP side. This allows to modify the RELEASE_COMPLETE cause
+; at least.
+;
+reject_cause=16
+
+
+;
+; Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),
+; this requests additional Infos, so we can waitfordigits
+; without much issues. This works only for PTP Ports
+;
+; default value: no
+;
+need_more_infos=no
+
+
+;
+; set this to yes if you want to disconnect calls when a timeout occurs
+; for example during the overlapdial phase
+;
+nttimeout=no
+
+; set the method to use for channel selection:
+; standard - always choose the first free channel with the lowest number
+; round_robin - use the round robin algorithm to select a channel. use this
+; if you want to balance your load.
+;
+; default value: standard
+;
+method=standard
+
+
+; specify if chan_misdn should collect digits before going into the
+; dialplan, you can choose yes=4 Seconds, no, or specify the amount
+; of seconds you need;
+;
+overlapdial=yes
+
+;
+; dialplan means Type Of Number in ISDN Terms (for outgoing calls)
+;
+; there are different types of the dialplan:
+;
+; dialplan -> outgoing Number
+; localdialplan -> callerid
+; cpndialplan -> connected party number
+;
+; dialplan options:
+;
+; 0 - unknown
+; 1 - International
+; 2 - National
+; 4 - Subscriber
+;
+; This setting is used for outgoing calls
+;
+; default value: 0
+;
+dialplan=0
+localdialplan=0
+cpndialplan=0
+
+
+
+;
+; turn this to no if you don't mind correct handling of Progress Indicators
+;
+early_bconnect=yes
+
+
+;
+; turn this on if you like to send Tone Indications to a Incoming
+; isdn channel on a TE Port. Rarely used, only if the Telco allows
+; you to send indications by yourself, normally the Telco sends the
+; indications to the remote party.
+;
+; default: no
+;
+incoming_early_audio=no
+
+; uncomment the following to get into s extension at extension conf
+; there you can use DigitTimeout if you can't or don't want to use
+; isdn overlap dial.
+; note: This will jump into the s exten for every exten!
+;
+; default value: no
+;
+;always_immediate=no
+
+;
+; set this to yes if you want to generate your own dialtone
+; with always_immediate=yes, else chan_misdn generates the dialtone
+;
+; default value: no
+;
+nodialtone=no
+
+
+; uncomment the following if you want callers which called exactly the
+; base number (so no extension is set) jump to the s extension.
+; if the user dials something more it jumps to the correct extension
+; instead
+;
+; default value: no
+;
+;immediate=no
+
+; uncomment the following to have hold and retrieve support
+;
+; default value: no
+;
+;hold_allowed=yes
+
+; Pickup and Callgroup
+;
+; default values: not set = 0
+; range: 0-63
+;
+;callgroup=1
+;pickupgroup=1
+
+
+;
+; these are the exact isdn screening and presentation indicators
+; if -1 is given for both values the presentation indicators are used
+; from asterisks SetCallerPres application.
+; s=0, p=0 -> callerid presented not screened
+; s=1, p=1 -> callerid presented but screened (the remote end does not see it!)
+;
+; default values s=-1, p=-1
+presentation=-1
+screen=-1
+
+; this enables echocancellation, with the given number of taps
+; be aware, move this setting only to outgoing portgroups!
+; A value of zero turns echocancellation off.
+;
+; possible values are: 0,32,64,128,256,yes(=128),no(=0)
+;
+; default value: no
+;
+;echocancel=no
+
+; Set this to no to disable echotraining. You can enter a number > 10
+; the value is a multiple of 0.125 ms.
+;
+; default value: no
+; yes = 2000
+; no = 0
+;
+echotraining=no
+
+;
+; chan_misdns jitterbuffer, default 4000
+;
+jitterbuffer=4000
+
+;
+; change this threshold to enable dejitter functionality
+;
+jitterbuffer_upper_threshold=0
+
+
+;
+; change this to yes, if you want to bridge a mISDN data channel to
+; another channel type or to an application.
+;
+hdlc=no
+
+
+;
+; defines the maximum amount of incoming calls per port for
+; this group. Calls which exceed the maximum will be marked with
+; the channel variable MAX_OVERFLOW. It will contain the amount of
+; overflowed calls
+;
+max_incoming=-1
+
+;
+; defines the maximum amount of outgoing calls per port for this group
+; exceeding calls will be rejected
+;
+max_outgoing=-1
+
+[intern]
+; define your ports, e.g. 1,2 (depends on mISDN-driver loading order)
+ports=1,2
+; context where to go to when incoming Call on one of the above ports
+context=Intern
+
+[internPP]
+;
+; adding the postfix 'ptp' to a port number is obsolete now, chan_misdn
+; parses /etc/misdn-init.conf and sets the ptp mode to the corresponding
+; configs. For backwards compatibility you can still set ptp here.
+;
+ports=3
+
+[first_extern]
+; again port defs
+ports=4
+; again a context for incoming calls
+context=Extern1
+; msns for te ports, listen on those numbers on the above ports, and
+; indicate the incoming calls to asterisk
+; here you can give a comma separated list or simply an '*' for
+; any msn.
+msns=*
+
+; here an example with given msns
+[second_extern]
+ports=5
+context=Extern2
+callerid=15
+msns=102,144,101,104
diff --git a/trunk/configs/modules.conf.sample b/trunk/configs/modules.conf.sample
new file mode 100644
index 000000000..2ca3c03d4
--- /dev/null
+++ b/trunk/configs/modules.conf.sample
@@ -0,0 +1,39 @@
+;
+; Asterisk configuration file
+;
+; Module Loader configuration file
+;
+
+[modules]
+autoload=yes
+;
+; Any modules that need to be loaded before the Asterisk core has been
+; initialized (just after the logger has been initialized) can be loaded
+; using 'preload'. This will frequently be needed if you wish to map all
+; module configuration files into Realtime storage, since the Realtime
+; driver will need to be loaded before the modules using those configuration
+; files are initialized.
+;
+; An example of loading ODBC support would be:
+;preload => res_odbc.so
+;preload => res_config_odbc.so
+;
+; res_phoneprov requires func_strings.so to be loaded:
+preload => func_strings.so
+;
+; Uncomment the following if you wish to use the Speech Recognition API
+;preload => res_speech.so
+;
+; If you want, load the GTK console right away.
+;
+noload => pbx_gtkconsole.so
+;load => pbx_gtkconsole.so
+;
+load => res_musiconhold.so
+;
+; Load one of: chan_oss, alsa, or console (portaudio).
+; By default, load chan_oss only (automatically).
+;
+noload => chan_alsa.so
+;noload => chan_oss.so
+;noload => chan_console.so
diff --git a/trunk/configs/musiconhold.conf.sample b/trunk/configs/musiconhold.conf.sample
new file mode 100644
index 000000000..4df1afd4f
--- /dev/null
+++ b/trunk/configs/musiconhold.conf.sample
@@ -0,0 +1,79 @@
+;
+; Music on Hold -- Sample Configuration
+;
+[general]
+;cachertclasses=yes ; use 1 instance of moh class for all users who are using it,
+ ; decrease consumable cpu cycles and memory
+ ; disabled by default
+
+
+; valid mode options:
+; files -- read files from a directory in any Asterisk supported
+; media format
+; quietmp3 -- default
+; mp3 -- loud
+; mp3nb -- unbuffered
+; quietmp3nb -- quiet unbuffered
+; custom -- run a custom application (See examples below)
+
+; =========
+; File-based (native) music on hold
+; =========
+;
+; This plays files directly from the specified directory, no external
+; processes are required. Files are played in normal sorting order
+; (same as a sorted directory listing), and no volume or other
+; sound adjustments are available. If the file is available in
+; the same format as the channel's codec, then it will be played
+; without transcoding (same as Playback would do in the dialplan).
+; Files can be present in as many formats as you wish, and the
+; 'best' format will be chosen at playback time.
+;
+; NOTE:
+; If you are not using "autoload" in modules.conf, then you
+; must ensure that the format modules for any formats you wish
+; to use are loaded _before_ res_musiconhold. If you do not do
+; this, res_musiconhold will skip the files it is not able to
+; understand when it loads.
+;
+
+[default]
+mode=files
+directory=/var/lib/asterisk/moh
+;
+;[native-random]
+;mode=files
+;directory=/var/lib/asterisk/moh
+;digit=# ; If this option is set for a class, then when callers are
+; ; listening to music on hold, they can press this digit, and
+; ; they will switch to listening to this music class.
+;sort=random ; Sort the files in random order
+
+;[native-alphabetical]
+;mode=files
+;directory=/var/lib/asterisk/moh
+;sort=alpha ; Sort the files in alphabetical order. If this option is
+; ; not specified, the sort order is undefined.
+
+; =========
+; Other (non-native) playback methods
+; =========
+
+;[manual]
+;mode=custom
+; Note that with mode=custom, a directory is not required, such as when reading
+; from a stream.
+;directory=/var/lib/asterisk/mohmp3
+;application=/usr/bin/mpg123 -q -r 8000 -f 8192 -b 2048 --mono -s
+
+;[ulawstream]
+;mode=custom
+;application=/usr/bin/streamplayer 192.168.100.52 888
+;format=ulaw
+
+; mpg123 on Solaris does not always exit properly; madplay may be a better
+; choice
+;[solaris]
+;mode=custom
+;directory=/var/lib/asterisk/mohmp3
+;application=/site/sw/bin/madplay -Q -o raw:- --mono -R 8000 -a -12
diff --git a/trunk/configs/muted.conf.sample b/trunk/configs/muted.conf.sample
new file mode 100644
index 000000000..a5e869b99
--- /dev/null
+++ b/trunk/configs/muted.conf.sample
@@ -0,0 +1,39 @@
+#
+# Sample muted configuration file
+#
+# Copyright (C) 2004 Digium, Inc.
+#
+# First you have the host, username, and password
+# we use to connect to the asterisk system
+#
+# What is this? Well, haven't you ever wished you could automatically
+# turn down the volume on your stereo, CDPlayer, etc, when a call comes in,
+# and then return it to normal when the call ends? Well, this is a possible
+# mechanism to make this happen!
+# You have to fire up the new utils/muted, which runs as a daemon in the
+# background. This daemon connects to asterisk via a manager interface, and
+# also reads this config file from /etc/muted.conf. when the channels mentioned
+# are activated, it tweaks the sound levels on the sound card(s).
+# So, depending on the sound card, you may be able to run all your sound
+# generating devices thru your sound card, and use this mechanism to quiet
+# them down while you are on the phone. If anyone figures out how to make
+# this work with kids, please inform!!
+#
+host localhost
+user user
+pass pass
+#
+# List each channel we're supposed to watch
+#
+channel Zap/1
+channel Zap/2
+channel SIP/mark
+#
+# Mute level is the percentage of the current volume we should
+# lower the music to.
+#
+mutelevel 20
+#
+# Smooth fade makes the fadein/fadeout nicer sounding
+#
+smoothfade
diff --git a/trunk/configs/osp.conf.sample b/trunk/configs/osp.conf.sample
new file mode 100644
index 000000000..e3423373d
--- /dev/null
+++ b/trunk/configs/osp.conf.sample
@@ -0,0 +1,90 @@
+;
+; Open Settlement Protocol Sample Configuration File
+;
+; This file contains configuration of OSP server providers that are used by the
+; Asterisk OSP module. The section "general" is reserved for global options.
+; All other sections describe specific OSP Providers. The provider "default"
+; is used when no provider is otherwise specified.
+;
+; The "servicepoint" and "source" parameters must be configured. For most
+; implementations the other parameters in this file can be left unchanged.
+;
+[general]
+;
+; Enable cryptographic acceleration hardware.
+;
+;accelerate=no
+;
+; Defines the status of tokens that Asterisk will validate.
+; 0 - signed tokens only
+; 1 - unsigned tokens only
+; 2 - both signed and unsigned
+; The default value is 0, i.e. the Asterisk will only validate signed tokens.
+;
+;tokenformat=0
+;
+;[default]
+;
+; List all service points (OSP servers) for this provider.
+; Use either domain name or IP address. Most OSP servers use port 1080.
+;
+;servicepoint=http://osptestserver.transnexus.com:1080/osp
+;
+; Define the "source" device for requesting OSP authorization.
+; This value is usually the domain name or IP address of the the Asterisk server.
+;
+;source=domain name or [IP address in brackets]
+;
+; Define path and file name of crypto files.
+; The default path for crypto file is /var/lib/asterisk/keys. If no path is
+; defined, crypto files will in /var/lib/asterisk/keys directory.
+;
+; Specify the private key file name.
+; If this parameter is unspecified or not present, the default name will be the
+; osp.conf section name followed by "-privatekey.pem" (for example:
+; default-privatekey.pem)
+;
+;privatekey=pkey.pem
+;
+; Specify the local certificate file.
+; If this parameter is unspecified or not present, the default name will be the
+; osp.conf section name followed by "- localcert.pem " (for example:
+; default-localcert.pem)
+;
+;localcert=localcert.pem
+;
+; Specify one or more Certificate Authority key file names. If none are listed,
+; a single Certificate Authority key file name is added with the default name of
+; the osp.conf section name followed by "-cacert_0.pem " (for example:
+; default-cacert_0.pem)
+;
+;cacert=cacert_0.pem
+;
+; Configure parameters for OSP communication between Asterisk OSP client and OSP
+; servers.
+;
+; maxconnections: Max number of simultaneous connections to the provider OSP
+; server (default=20)
+; retrydelay: Extra delay between retries (default=0)
+; retrylimit: Max number of retries before giving up (default=2)
+; timeout: Timeout for response in milliseconds (default=500)
+;
+;maxconnections=20
+;retrydelay=0
+;retrylimit=2
+;timeout=500
+;
+; Set the authentication policy.
+; 0 - NO - Accept all calls.
+; 1 - YES - Accept calls with valid token or no token. Block calls with
+; invalid token.
+; 2 - EXCLUSIVE - Accept calls with valid token. Block calls with invalid token
+; or no token.
+; Default is 1,
+;
+;authpolicy=1
+;
+; Set the default destination protocol. The OSP module supports SIP, H323, and
+; IAX protocols. The default protocol is set to SIP.
+;
+;defaultprotocol=SIP
diff --git a/trunk/configs/oss.conf.sample b/trunk/configs/oss.conf.sample
new file mode 100644
index 000000000..24b0b38ee
--- /dev/null
+++ b/trunk/configs/oss.conf.sample
@@ -0,0 +1,138 @@
+;
+; Automatically generated from ../channels/chan_oss.c
+;
+
+[general]
+ ; General config options, with default values shown.
+ ; You should use one section per device, with [general] being used
+ ; for the first device and also as a template for other devices.
+ ;
+ ; All but 'debug' can go also in the device-specific sections.
+ ;
+ ; debug = 0x0 ; misc debug flags, default is 0
+
+ ; Set the device to use for I/O
+ ; device = /dev/dsp
+
+ ; Optional mixer command to run upon startup (e.g. to set
+ ; volume levels, mutes, etc.
+ ; mixer =
+
+ ; Software mic volume booster (or attenuator), useful for sound
+ ; cards or microphones with poor sensitivity. The volume level
+ ; is in dB, ranging from -20.0 to +20.0
+ ; boost = n ; mic volume boost in dB
+
+ ; Set the callerid for outgoing calls
+ ; callerid = John Doe <555-1234>
+
+ ; autoanswer = no ; no autoanswer on call
+ ; autohangup = yes ; hangup when other party closes
+ ; extension = s ; default extension to call
+ ; context = default ; default context for outgoing calls
+ ; language = "" ; default language
+
+ ; If you set overridecontext to 'yes', then the whole dial string
+ ; will be interpreted as an extension, which is extremely useful
+ ; to dial SIP, IAX and other extensions which use the '@' character.
+ ; The default is 'no' just for backward compatibility, but the
+ ; suggestion is to change it.
+ ; overridecontext = no ; if 'no', the last @ will start the context
+ ; if 'yes' the whole string is an extension.
+
+ ; low level device parameters in case you have problems with the
+ ; device driver on your operating system. You should not touch these
+ ; unless you know what you are doing.
+ ; queuesize = 10 ; frames in device driver
+ ; frags = 8 ; argument to SETFRAGMENT
+
+ ;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+ ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
+ ; OSS channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The OSS channel can't accept jitter,
+ ; thus an enabled jitterbuffer on the receive OSS side will always
+ ; be used if the sending side can create jitter.
+
+ ; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+ ; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+ ; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of an OSS
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+ ; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+ ;-----------------------------------------------------------------------------------
+
+; Below are the settings to support video. You can include them
+; in your general configuration as [general](+,video)
+; The parameters are all available through the CLI as "console name value"
+; Section names used here are only examples.
+
+[my_video](!) ; you can just include in your config
+ videodevice = /dev/video0 ; uses your V4L webcam as video source
+ videodevice = X11 ; X11 grabber. Dragging on the local display moves the origin.
+ videocodec = h263 ; also h261, h263p, h264, mpeg4, ...
+
+ ; video_size is the geometry used by the encoder.
+ ; Depending on the codec your choice is restricted.
+ video_size = 352x288 ; the format WIDTHxHEIGHT is also ok
+ video_size = cif ; sqcif, qcif, cif, qvga, vga, ...
+
+ ; You can also set the geometry used for the camera, local display and remote display.
+ ; The local window is on the right, the remote window is on the left.
+ ; Right clicking with the mouse on a video window increases the size,
+ ; center-clicking reduces the size.
+ camera_size = cif
+ remote_size = cif
+ local_size = qcif
+
+ bitrate = 60000 ; rate told to ffmpeg.
+ fps = 5 ; frames per second from the source.
+ ; qmin = 3 ; quantizer value passed to the encoder.
+
+; The keypad is made of an image (in any format supported by SDL_image)
+; and some configuration entries indicating the location and function of buttons.
+; These entries can also be contained in the comment field of the image,
+; which is a lot more convenient to manage.
+; E.g. for jpeg you can write them with wrjpgcom (part of libjpeg).
+; The format to define keys is
+; region = <event> <shape> x0 y0 x1 y1 h
+; where <event> is the event to be generated (a digit, pickup, hangup,...)
+; <shape> is the shape of the region (currently 'rect' and 'circle' are
+; supported, the latter is really an ellipse), x0 y0 x1 y1 are the
+; coordinates of the base of the rectangle or main diameter of the ellipse,
+; (they can be rotated) while h is the height of the rectangle or the other
+; diameter of the ellipse.
+;
+[my_skin](!)
+ keypad = /tmp/keypad.jpg
+ region = 1 rect 19 18 67 18 28
+ region = 2 rect 84 18 133 18 28
+ region = 3 rect 152 18 201 18 28
+ region = 4 rect 19 60 67 60 28
+ region = 5 rect 84 60 133 60 28
+ region = 6 rect 152 60 201 60 28
+ region = 7 rect 19 103 67 103 28
+ region = 8 rect 84 103 133 103 28
+ region = 9 rect 152 103 201 103 28
+ region = * rect 19 146 67 146 28
+ region = 0 rect 84 146 133 146 28
+ region = # rect 152 146 201 146 28
+ region = pickup rect 229 15 267 15 40
+ region = hangup rect 230 66 270 64 40
+ region = mute circle 232 141 264 141 33
+ region = sendvideo circle 235 185 266 185 33
+ region = autoanswer rect 228 212 275 212 50
+
+; uncomment this line to add video support
+; [default](+,my_video,my_skin)
+
+[card1]
+ ; device = /dev/dsp1 ; alternate device
+
diff --git a/trunk/configs/phone.conf.sample b/trunk/configs/phone.conf.sample
new file mode 100644
index 000000000..17204501e
--- /dev/null
+++ b/trunk/configs/phone.conf.sample
@@ -0,0 +1,51 @@
+;
+; Linux Telephony Interface
+;
+; Configuration file
+;
+[interfaces]
+;
+; Select a mode, either the phone jack provides dialtone, reads digits,
+; then starts PBX with the given extension (dialtone mode), or
+; immediately provides the PBX without reading any digits or providing
+; any dialtone (this is the immediate mode, the default). Also, you
+; can set the mode to "fxo" if you have a linejack to make it operate
+; properly. If you are using a Sigma Designs board you may set this to
+; "sig".
+;
+mode=immediate
+;mode=dialtone
+;mode=fxo
+;mode=sig
+;
+; You can decide which format to use by default, "g723.1", "g729", or "slinear".
+; Note that g729 is only supported for Sigma Designs boards.
+; XXX Be careful, sometimes the card causes kernel panics when running
+; in signed linear mode for some reason... XXX
+;
+format=slinear
+;format=g723.1
+;format=g729
+;
+; And set the echo cancellation to "off", "low", "medium", and "high".
+; This is not supported on all phones.
+;
+echocancel=medium
+;
+; You can optionally use VAD/CNG silence suppression
+;
+;silencesupression=yes
+;
+; List all devices we can use. Contexts may also be specified
+;
+;context=local
+;
+; You can set txgain and rxgain for each device in the same way as context.
+; If you want to change default gain value (1.0 =~ 100%) for device, simple
+; add txgain or rxgain line before device line. But remember, if you change
+; volume all cards listed below will be affected by these values. You can
+; use float values (1.0, 0.5, 2.0) or percentage values (100%, 150%, 50%).
+;
+;txgain=100%
+;rxgain=1.0
+;device => /dev/phone0
diff --git a/trunk/configs/phoneprov.conf.sample b/trunk/configs/phoneprov.conf.sample
new file mode 100644
index 000000000..8f55fa88b
--- /dev/null
+++ b/trunk/configs/phoneprov.conf.sample
@@ -0,0 +1,60 @@
+[general]
+; The default behavior of res_phoneprov will be to set the SERVER template variable to
+; the IP address that the phone uses to contact the provisioning server and the
+; SERVER_PORT variable to the bindport setting in sip.conf. Unless you have a very
+; unusual setup, you should not need to set serveraddr, serveriface, or serverport.
+
+;serveraddr=192.168.1.1 ; Override address to send to the phone to use as server address.
+;serveriface=eth0 ; Same as above, except an ethernet interface.
+ ; Useful for when the interface uses DHCP and the asterisk http
+ ; server listens on a different IP than chan_sip.
+;serverport=5060 ; Override port to send to the phone to use as server port.
+default_profile=polycom ; The default profile to use if none specified in users.conf
+
+; You can define profiles for different phones specifying what files to register
+; with the provisioning server. You can define either static files, or dynamically
+; generated files that can have dynamic names and point to templates that variables
+; can be substituted into. You can also set arbitrary variables for the profiles
+; templates to have access to. Example:
+
+;[example]
+;mime_type => application/octet-stream
+;static_file => example/firmware
+;static_file => example/default.cfg,text/xml
+;${TOUPPER(${MAC})}.cfg => templates/example-mac.cfg
+;setvar => DB_CIDNAME=${ODBC_CID_NAME_LOOKUP(${USERNAME})}
+
+; Dynamically generated files have a filename registered with variable substitution
+; with variables obtained while reading users.conf.
+
+; Built in variables and the options in users.conf that they come from
+; MAC (macaddress)
+; USERNAME (username)
+; DISPLAY_NAME (fullname)
+; SECRET (secret)
+; LABEL (label)
+; CALLERID (cid_number)
+; VOCIEMAIL_EXTEN (vmexten)
+; EXTENSION_LENGTH (localextenlength)
+
+; Built-in variables and the options in phoneprov.conf that they come from
+; SERVER (server)
+; SERVER_PORT (serverport)
+
+[polycom]
+staticdir => configs/ ; Sub directory of AST_DATA_DIR/phoneprov that static files reside
+ ; in. This allows a request to /phoneprov/sip.cfg to pull the file
+ ; from /phoneprov/configs/sip.cfg
+mime_type => text/xml ; Default mime type to use if one isn't specified or the
+ ; extension isn't recognized
+static_file => bootrom.ld,application/octet-stream ; Static files the phone will download
+static_file => bootrom.ver,plain/text ; static_file => filename,mime-type
+static_file => sip.ld,application/octet-stream
+static_file => sip.ver,plain/text
+static_file => sip.cfg
+static_file => custom.cfg
+${TOLOWER(${MAC})}.cfg => 000000000000.cfg ; Dynamically generated files.
+${TOLOWER(${MAC})}-phone.cfg => 000000000000-phone.cfg ; (relative to AST_DATA_DIR/phoneprov)
+config/${TOLOWER(${MAC})} => polycom.xml ; Dynamic Filename => template file
+${TOLOWER(${MAC})}-directory.xml => 000000000000-directory.xml
+setvar => CUSTOM_CONFIG=/var/lib/asterisk/phoneprov/configs/custom.cfg ; Custom variable
diff --git a/trunk/configs/queuerules.conf.sample b/trunk/configs/queuerules.conf.sample
new file mode 100644
index 000000000..5ab794be7
--- /dev/null
+++ b/trunk/configs/queuerules.conf.sample
@@ -0,0 +1,20 @@
+; It is possible to change the value of the QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY
+; channel variables in mid-call by defining rules in the queue for when to do so. This can allow for
+; a call to be opened to more members or potentially a different set of members.
+; The advantage to changing members this way as opposed to inserting the caller into a
+; different queue with more members or reinserting the caller into the same queue with a different
+; QUEUE_MAX_PENALTY or QUEUE_MIN_PENALTY set is that the caller does not lose his place in the queue.
+;
+; Note: There is a limitation to these rules; a caller will follow the penaltychange rules for
+; the queue that were defined at the time the caller entered the queue. If an update to the rules is
+; made during the the caller's stay in the queue, these will not be reflected for that caller.
+;
+; The syntax for these rules is
+; penaltychange => <number of seconds into the call>,<absolute or relative change to QUEUE_MAX_PENALTY>[,absolute or relative change to QUEUE_MIN_PENALTY]
+;
+; Example:
+; [myrule]
+; penaltychange => 30,+3 ; 30 seconds into the call increase the QUEUE_MAX_PENALTY by 3, no change to QUEUE_MIN_PENALTY
+; penaltychange => 60,10,5 ; 60 seconds into the call increase the QUEUE_MAX_PENALTY to 10 and increase the QUEUE_MIN_PENALTY to 5
+; penaltychange => 75,,7 ; 75 seconds into the call keep the QUEUE_MAX_PENALTY the same and increase the QUEUE_MIN_PENALTY to 7
+
diff --git a/trunk/configs/queues.conf.sample b/trunk/configs/queues.conf.sample
new file mode 100644
index 000000000..92c323773
--- /dev/null
+++ b/trunk/configs/queues.conf.sample
@@ -0,0 +1,403 @@
+[general]
+;
+; Global settings for call queues
+;
+; Persistent Members
+; Store each dynamic member in each queue in the astdb so that
+; when asterisk is restarted, each member will be automatically
+; read into their recorded queues. Default is 'yes'.
+;
+persistentmembers = yes
+;
+; Keep Stats
+; Keep queue statistics during a reload. Default is 'no'
+;
+keepstats = no
+;
+; AutoFill Behavior
+; The old/current behavior of the 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. The new behavior, enabled by setting
+; autofill=yes 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. This is
+; probably more along the lines of how a queue should work and
+; in most cases, you will want to enable this behavior. If you
+; do not specify or comment out this option, it will default to no
+; to keep backward compatibility with the old behavior.
+;
+autofill = yes
+;
+; Monitor Type
+; By setting monitor-type = MixMonitor, when specifying monitor-format
+; to enable recording of queue member conversations, app_queue will
+; now use the new MixMonitor application instead of Monitor so
+; the concept of "joining/mixing" the in/out files now goes away
+; when this is enabled. You can set the default type for all queues
+; here, and then also change monitor-type for individual queues within
+; queue by using the same configuration parameter within a queue
+; configuration block. If you do not specify or comment out this option,
+; it will default to the old 'Monitor' behavior to keep backward
+; compatibility.
+;
+monitor-type = MixMonitor
+;
+; UpdateCDR behavior.
+; This option is implemented to mimic chan_agents behavior of populating
+; CDR dstchannel field of a call with an agent name, which you can set
+; at the login time with AddQueueMember membername parameter.
+;
+; updatecdr = no
+
+;
+; Note that a timeout to fail out of a queue may be passed as part of
+; an application call from extensions.conf:
+; Queue(queuename|[options]|[optionalurl]|[announceoverride]|[timeout])
+; example: Queue(dave|t|||45)
+
+; shared_lastcall will make the lastcall and calls received be the same in
+; members logged in more than one queue.
+; This is useful to make the queue respect the wrapuptime of another queue
+; for a shared member
+;
+shared_lastcall=no
+;
+;[markq]
+;
+; A sample call queue
+;
+; Musicclass sets which music applies for this particular call queue.
+; The only class which can override this one is if the MOH class is set
+; directly on the channel using Set(CHANNEL(musicclass)=whatever) in the
+; dialplan.
+;
+;musicclass = default
+;
+; An announcement may be specified which is played for the member as
+; soon as they answer a call, typically to indicate to them which queue
+; this call should be answered as, so that agents or members who are
+; listening to more than one queue can differentiated how they should
+; engage the customer
+;
+;announce = queue-markq
+;
+; A strategy may be specified. Valid strategies include:
+;
+; ringall - ring all available channels until one answers (default)
+; leastrecent - ring interface which was least recently called by this queue
+; fewestcalls - ring the one with fewest completed calls from this queue
+; random - ring random interface
+; rrmemory - round robin with memory, remember where we left off last ring pass
+; linear - rings interfaces in the order specified in this configuration file.
+; If you use dynamic members, the members will be rung in the order in
+; which they were added
+; wrandom - rings random interface, but uses the member's penalty as a weight
+; when calculating their metric. So a member with penalty 0 will have
+; a metric somewhere between 0 and 1000, and a member with penalty 1 will
+; have a metric between 0 and 2000, and a member with penalty 2 will have
+; a metric between 0 and 3000. Please note, if using this strategy, the member
+; penalty is not the same as when using other queue strategies. It is ONLY used
+; as a weight for calculating metric.
+;
+;strategy = ringall
+;
+; Second settings for service level (default 0)
+; Used for service level statistics (calls answered within service level time
+; frame)
+;servicelevel = 60
+;
+; A context may be specified, in which if the user types a SINGLE
+; digit extension while they are in the queue, they will be taken out
+; of the queue and sent to that extension in this context.
+;
+;context = qoutcon
+;
+; How long do we let the phone ring before we consider this a timeout...
+;
+;timeout = 15
+;
+; How long do we wait before trying all the members again?
+;
+;retry = 5
+;
+; Weight of queue - when compared to other queues, higher weights get
+; first shot at available channels when the same channel is included in
+; more than one queue.
+;
+;weight=0
+;
+; After a successful call, how long to wait before sending a potentially
+; free member another call (default is 0, or no delay)
+;
+;wrapuptime=15
+;
+; Autofill will follow queue strategy but push multiple calls through
+; at same time until there are no more waiting callers or no more
+; available members. The per-queue setting of autofill allows you
+; to override the default setting on an individual queue level.
+;
+;autofill=yes
+;
+; Autopause will pause a queue member if they fail to answer a call
+;
+;autopause=yes
+;
+; Maximum number of people waiting in the queue (0 for unlimited)
+;
+;maxlen = 0
+;
+; If set to yes, just prior to the caller being bridged with a queue member
+; the following variables will be set
+; MEMBERINTERFACE is the interface name (eg. Agent/1234)
+; MEMBERNAME is the member name (eg. Joe Soap)
+; MEMBERCALLS is the number of calls that interface has taken,
+; MEMBERLASTCALL is the last time the member took a call.
+; MEMBERPENALTY is the penalty of the member
+; MEMBERDYNAMIC indicates if a member is dynamic or not
+; MEMBERREALTIME indicates if a member is realtime or not
+;
+;setinterfacevar=no
+;
+; If set to yes, just prior to the caller being bridged with a queue member
+; the following variables will be set:
+; QEHOLDTIME callers hold time
+; QEORIGINALPOS original position of the caller in the queue
+;
+;setqueueentryvar=no
+;
+; If set to yes, the following variables will be set
+; just prior to the caller being bridged with a queue member
+; and just prior to the caller leaving the queue
+; QUEUEMAX maxmimum number of calls allowed
+; QUEUESTRATEGY the strategy of the queue;
+; QUEUECALLS number of calls currently in the queue
+; QUEUEHOLDTIME current average hold time
+; QUEUECOMPLETED number of completed calls for the queue
+; QUEUEABANDONED number of abandoned calls
+; QUEUESRVLEVEL queue service level
+; QUEUESRVLEVELPERF current service level performance
+;
+;setqueuevar=no
+;
+; if set, run this macro when connected to the queue member
+; you can override this macro by setting the macro option on
+; the queue application
+;
+; membermacro=somemacro
+
+; How often to announce queue position and/or estimated
+; holdtime to caller (0=off)
+; Note that this value is ignored if the caller's queue
+; position has changed (see min-announce-frequency)
+;
+;announce-frequency = 90
+;
+; The absolute minimum time between the start of each
+; queue position and/or estimated holdtime announcement
+; This is useful for avoiding constant announcements
+; when the caller's queue position is changing frequently
+; (see announce-frequency)
+;
+;min-announce-frequency = 15
+;
+; How often to make any periodic announcement (see periodic-announce)
+;
+;periodic-announce-frequency=60
+;
+; Should we include estimated hold time in position announcements?
+; Either yes, no, or only once.
+; Hold time will be announced as the estimated time,
+; or "less than 2 minutes" when appropriate.
+;
+;announce-holdtime = yes|no|once
+;
+; Queue position announce?
+; Either yes or no. If turned off, only the holdtime will be announced (as
+; configured in announce-holdtime)
+;
+;announce-position = yes
+;
+; What's the rounding time for the seconds?
+; If this is non-zero, then we announce the seconds as well as the minutes
+; rounded to this value.
+; Valid values are 0, 1, 5, 10, 15, 20, and 30.
+;
+; announce-round-seconds = 10
+;
+; Use these sound files in making position/holdtime announcements. The
+; defaults are as listed below -- change only if you need to.
+;
+ ; ("You are now first in line.")
+;queue-youarenext = queue-youarenext
+ ; ("There are")
+;queue-thereare = queue-thereare
+ ; ("calls waiting.")
+;queue-callswaiting = queue-callswaiting
+ ; ("The current est. holdtime is")
+;queue-holdtime = queue-holdtime
+ ; ("minutes.")
+;queue-minutes = queue-minutes
+ ; ("seconds.")
+;queue-seconds = queue-seconds
+ ; ("Thank you for your patience.")
+;queue-thankyou = queue-thankyou
+ ; ("less than")
+;queue-lessthan = queue-less-than
+ ; ("Hold time")
+;queue-reporthold = queue-reporthold
+ ; ("All reps busy / wait for next")
+;periodic-announce = queue-periodic-announce
+;
+; A set of periodic announcements can be defined by separating
+; periodic announcements to reproduce by commas. For example:
+;periodic-announce = queue-periodic-announce,your-call-is-important,please-wait
+;
+; The announcements will be played in the order in which they are defined. After
+; playing the last announcement, the announcements begin again from the beginning.
+;
+; Calls may be recorded using Asterisk's monitor/MixMonitor resource
+; This can be enabled from within the Queue application, starting recording
+; when the call is actually picked up; thus, only successful calls are
+; recorded, and you are not recording while people are listening to MOH.
+; To enable monitoring, simply specify "monitor-format"; it will be disabled
+; otherwise.
+;
+; You can specify the monitor filename with by calling
+; Set(MONITOR_FILENAME=foo)
+; Otherwise it will use MONITOR_FILENAME=${UNIQUEID}
+;
+; Pick any one valid extension for monitor format recording. If you leave
+; monitor-format commented out, it will not record calls.
+;
+; monitor-format = gsm|wav|wav49
+;
+; Monitor Type
+; By setting monitor-type = MixMonitor, when specifying monitor-format
+; to enable recording of queue member conversations, app_queue will
+; now use the new MixMonitor application instead of Monitor so
+; the concept of "joining/mixing" the in/out files now goes away
+; when this is enabled. If you do not specify or comment out this option,
+; it will default to the old 'Monitor' behavior to keep backward
+; compatibility.
+;
+; monitor-type = MixMonitor
+;
+; ----------------------- TYPE MIXMONITOR OPTIONS -----------------------------
+;
+;
+; You can specify the options supplied to MixMonitor by calling
+; Set(MONITOR_OPTIONS=av(<x>)V(<x>)W(<x>))
+; The 'b' option for MixMonitor (only save audio to the file while bridged) is
+; implied.
+;
+; You can specify a post recording command to be executed after the end of
+; recording by calling
+; Set(MONITOR_EXEC=mv /var/spool/asterisk/monitor/^{MONITOR_FILENAME} /tmp/^{MONITOR_FILENAME})
+;
+; The command specified within the contents of MONITOR_EXEC will be executed when
+; the recording is over. Any strings matching ^{X} will be unescaped to ${X} and
+; all variables will be evaluated just prior to recording being started.
+;
+; The contents of MONITOR_FILENAME will also be unescaped from ^{X} to ${X} and
+; all variables will be evaluated just prior to recording being started.
+;
+;
+; This setting controls whether callers can join a queue with no members. There
+; are three choices:
+;
+; yes - callers can join a queue with no members or only unavailable members
+; no - callers cannot join a queue with no members
+; strict - callers cannot join a queue with no members or only unavailable
+; members
+; loose - same as strict, but paused queue members do not count as unavailable
+;
+; joinempty = yes
+;
+;
+; If you wish to remove callers from the queue when new callers cannot join,
+; set this setting to one of the same choices for 'joinempty'
+;
+; leavewhenempty = yes
+;
+;
+; If this is set to yes, the following manager events will be generated:
+; AgentCalled, AgentDump, AgentConnect, AgentComplete; setting this to
+; vars also sends all channel variables with the event.
+; (may generate some extra manager events, but probably ones you want)
+;
+; eventwhencalled = yes|no|vars
+;
+; If this is set to yes, the following manager events will be generated:
+; QueueMemberStatus
+; (may generate a WHOLE LOT of extra manager events)
+;
+; eventmemberstatus = no
+;
+; If you wish to report the caller's hold time to the member before they are
+; connected to the caller, set this to yes.
+;
+; reportholdtime = no
+;
+; If you want the queue to avoid sending calls to members whose devices are
+; known to be 'in use' (via the channel driver supporting that device state)
+; uncomment this option. (Note: only the SIP channel driver currently is able
+; to report 'in use'.)
+;
+; ringinuse = no
+;
+; If you wish to have a delay before the member is connected to the caller (or
+; before the member hears any announcement messages), set this to the number of
+; seconds to delay.
+;
+; memberdelay = 0
+;
+; If timeoutrestart is set to yes, then the timeout for an agent to answer is
+; reset if a BUSY or CONGESTION is received. This can be useful if agents
+; are able to cancel a call with reject or similar.
+;
+; timeoutrestart = no
+;
+; If you wish to implement a rule defined in queuerules.conf (see
+; configs/queuerules.conf.sample from the asterisk source directory for
+; more information about penalty rules) by default, you may specify this
+; by setting defaultrule to the rule's name
+;
+; defaultrule = myrule
+;
+; Each member of this call queue is listed on a separate line in
+; the form technology/dialstring. "member" means a normal member of a
+; queue. An optional penalty may be specified after a comma, such that
+; entries with higher penalties are considered last. An optional member
+; name may also be specified after a second comma, which is used in log
+; messages as a "friendly name". Multiple interfaces may share a single
+; member name. An optional state interface may be specified after a third
+; comma. This interface will be the one for which app_queue receives device
+; state notifications, even though the first interface specified is the one
+; that is actually called.
+;
+; It is important to ensure that channel drivers used for members are loaded
+; before app_queue.so itself or they may be marked invalid until reload. This
+; can be accomplished by explicitly listing them in modules.conf before app_queue.so
+;
+;member => Zap/1
+;member => Zap/2,10
+;member => Zap/3,10,Bob Johnson
+;member => Agent/1001
+;member => Agent/1002
+;member => Local/1000@default,0,John Smith,SIP/1000
+
+;
+; Note that using agent groups is probably not what you want. Strategies do
+; not propagate down to the Agent system so if you want round robin, least
+; recent, etc, you should list all the agents in this file individually and not
+; use agent groups.
+;
+;member => Agent/@1 ; Any agent in group 1
+;member => Agent/:1,1 ; Any agent in group 1, wait for first
+ ; available, but consider with penalty
+
diff --git a/trunk/configs/res_config_sqlite.conf b/trunk/configs/res_config_sqlite.conf
new file mode 100644
index 000000000..04e6ae2e7
--- /dev/null
+++ b/trunk/configs/res_config_sqlite.conf
@@ -0,0 +1,11 @@
+[general]
+
+; The database file.
+dbfile => /var/lib/asterisk/sqlite.db
+
+; Both config_table and cdr_table are optional. If config_table is omitted,
+; you must specify it in extconfig.conf. If it is both provided here and in
+; extconfig.conf, the value given here is used. If cdr_table is omitted, CDR
+; support is simply disabled.
+config_table => ast_config
+cdr_table => ast_cdr
diff --git a/trunk/configs/res_odbc.conf.sample b/trunk/configs/res_odbc.conf.sample
new file mode 100644
index 000000000..2fd9aa702
--- /dev/null
+++ b/trunk/configs/res_odbc.conf.sample
@@ -0,0 +1,48 @@
+;;; odbc setup file
+
+; ENV is a global set of environmental variables that will get set.
+; Note that all environmental variables can be seen by all connections,
+; so you can't have different values for different connections.
+[ENV]
+INFORMIXSERVER => my_special_database
+INFORMIXDIR => /opt/informix
+
+; All other sections are arbitrary names for database connections.
+
+[asterisk]
+enabled => no
+dsn => asterisk
+;username => myuser
+;password => mypass
+pre-connect => yes
+; What should we execute to ensure that our connection is still alive? The
+; statement should return a non-zero value in the first field of its first
+; record. The default is "select 1".
+;sanitysql => select 1
+
+
+[mysql2]
+enabled => no
+dsn => MySQL-asterisk
+username => myuser
+password => mypass
+pre-connect => yes
+
+; Certain servers, such as MS SQL Server and Sybase use the TDS protocol, which
+; limits the number of active queries per connection to 1. By setting up pools
+; of connections, Asterisk can be made to work with these servers.
+[sqlserver]
+enabled => no
+dsn => mickeysoft
+pooling => yes
+limit => 5
+username => oscar
+password => thegrouch
+pre-connect => yes
+sanitysql => select count(*) from systables
+; Many databases have a default of '\' to escape special characters. MS SQL
+; Server does not.
+backslash_is_escape => no
+
+
+
diff --git a/trunk/configs/res_pgsql.conf.sample b/trunk/configs/res_pgsql.conf.sample
new file mode 100644
index 000000000..1ec2293e2
--- /dev/null
+++ b/trunk/configs/res_pgsql.conf.sample
@@ -0,0 +1,14 @@
+;
+; Sample configuration for res_config_pgsql
+;
+; The value of dbhost may be either a hostname or an IP address.
+; If dbhost is commented out or the string "localhost", a connection
+; to the local host is assumed and dbsock is used instead of TCP/IP
+; to connect to the server.
+;
+[general]
+dbhost=127.0.0.1
+dbport=5432
+dbname=asterisk
+dbuser=asterisk
+dbpass=password
diff --git a/trunk/configs/res_snmp.conf.sample b/trunk/configs/res_snmp.conf.sample
new file mode 100644
index 000000000..5ca09d576
--- /dev/null
+++ b/trunk/configs/res_snmp.conf.sample
@@ -0,0 +1,10 @@
+;
+; Configuration file for res_snmp
+;
+
+[general]
+; We run as a subagent per default -- to run as a full agent
+; we must run as root (to be able to bind to port 161)
+;subagent = yes
+; SNMP must be explicitly enabled to be active
+;enabled = yes
diff --git a/trunk/configs/rpt.conf.sample b/trunk/configs/rpt.conf.sample
new file mode 100644
index 000000000..6aee784dc
--- /dev/null
+++ b/trunk/configs/rpt.conf.sample
@@ -0,0 +1,193 @@
+; Radio Repeater / Remote Base configuration file (for use with app_rpt)
+; As of app_rpt version 0.39, 12/20/2005
+;
+
+;[000] ; Node ID of first repeater
+
+;rxchannel = Zap/1 ; Rx audio/signalling channel
+; Note: if you use a unified interface (tx/rx on one channel), only
+; specify the rxchannel and the txchannel will be assumed from the rxchannel
+;txchannel = Zap/2 ; Tx audio/signalling channel
+;duplex = 2 ; (Optional) set duplex operating mode
+;; 0 = half duplex (telemetry and courtesy tones do not transmit)
+;; 1 = semi-half duplex (telemetry and courtesy tones transmit, but not
+;; repeated audio
+;; 2 = normal full-duplex mode (Default)
+;; 3 = full-duplex mode, without repeated audio from main input source
+;functions = functions-repeater ; DTMF function list
+;; specify this for a different function list then local when on link
+;;link_functions = functions-different ; DTMF function list for link
+;;phone_functions = functions-phone ; (optional) different functions for 'P' mode
+;;dphone_functions = functions-dphone ; (optional) different functions for 'D' mode
+;;nodes = nodes-different ; (optional) different node list
+;tonezone = us ; use US tones (default)
+;context = default ; dialing context for phone
+;callerid = "WB6NIL Repeater" <(213) 555-0123> ; Callerid for phone calls
+;idrecording = wb6nil ; id recording
+;accountcode=RADIO ; account code (optional)
+;funcchar = * ; function lead-in character (defaults to '*')
+;endchar = # ; command mode end character (defaults to '#')
+;;nobusyout=yes ; (optional) Do not busy-out reverse-patch when
+ ; normal patch in use
+;hangtime=1000 ; squelch tail hang time (in ms) (optional)
+;totime=100000 ; transmit time-out time (in ms) (optional)
+;idtime=30000 ; id interval time (in ms) (optional)
+;politeid=30000 ; time in milliseconds before ID timer
+ ; expires to try and ID in the tail.
+ ; (optional, default is 30000).
+;idtalkover=|iwb6nil/rpt ; Talkover ID (optional) default is none
+;unlinkedct=ct2 ; unlinked courtesy tone (optional) default is none
+
+;; The tailmessagetime,tailsquashedtime, and tailmessages need to be set
+;; to support tail messages. They can be omitted otherwise.
+;tailmessagetime=300000 ; Play a tail message every 5 mins
+;tailsquashedtime=30000 ; If squashed by another user,
+;; try again after 30 seconds
+;tailmessages=msg1,msg2,msg3 ;list of messages to be played for tail message
+
+; The default values for hangtime, time-out time, and id interval time are
+; 5 seconds (5000 ms), 3 minutes (180000 ms), and 5 minutes (300000 ms)
+; respectively
+
+;[001] ; Node ID of first repeater
+
+;rxchannel = Zap/3 ; Rx audio/signalling channel
+; Note: if you use a unified interface (tx/rx on one channel), only
+; specify the rxchannel and the txchannel will be assumed from the rxchannel
+;txchannel = Zap/4 ; Tx audio/signalling channel
+;functions = functions-repeater ; DTMF function list
+;; specify this for a different function list then local when on link
+;;link_functions = functions-different ; DTMF function list for link
+;;phone_functions = functions-phone ; (optional) different functions for 'P' mode
+;;dphone_functions = functions-dphone ; (optional) different functions for 'D' mode
+;;nodes = nodes-different ; (optional) different node list
+;tonezone = us ; use US tones (default)
+;context = default ; dialing context for phone
+;callerid = "WB6NIL Repeater" <(213) 555-0123> ; Callerid for phone calls
+;idrecording = wb6nil ; id recording
+;accountcode=RADIO ; account code (optional)
+;funcchar = * ; function lead-in character (defaults to '*')
+;endchar = # ; command mode end character (defaults to '#')
+;;nobusyout=yes ; (optional) Do not busy-out reverse-patch when
+ ; normal patch in use
+;hangtime=1000 ; squelch tail hang time (in ms) (optional)
+;totime=100000 ; transmit time-out time (in ms) (optional)
+;idtime=30000 ; id interval time (in ms) (optional)
+;politeid=30000 ; time in milliseconds before ID timer
+ ; expires to try and ID in the tail.
+ ; (optional, default is 30000).
+;idtalkover=|iwb6nil/rpt ; Talkover ID (optional) default is none
+;unlinkedct=ct2 ; unlinked courtesy tone (optional) default is none
+
+;[002] ; Node ID of remote base
+
+;rxchannel = Zap/5 ; Rx audio/signalling channel
+; Note: if you use a unified interface (tx/rx on one channel), only
+; specify the rxchannel and the txchannel will be assumed from the rxchannel
+;txchannel = Zap/6 ; Tx audio/signalling channel
+;functions = functions-remote
+;remote = ft897 ; Set remote=y for dumb remote or
+ ; remote=ft897 for Yaesu FT-897 or
+ ; remote=rbi for Doug Hall RBI1
+;iobase = 0x378 ; Specify IO port for parallel port (optional)
+
+;[functions-repeater]
+;1=ilink,1 ; Specific link disconnect
+;2=ilink,2 ; Specific Link connect - monitor only
+;3=ilink,3 ; Specific Link connect - transceive
+;4=ilink,4 ; Enter command mode on a specific link
+;7=ilink,5 ; Link status
+;;XX=ilink,6 ; Disconnect all links (not used here)
+
+;80=status,1 ; System info
+;81=status,2 ; Time
+;82=status,3 ; app_rpt.c Version
+
+;6=autopatchup ; Autopatch up
+;0=autopatchdn ; Autopatch down
+
+;90=cop,1 ; System warm boot
+;91=cop,2 ; System enable
+;92=cop,3 ; System disable
+
+;[functions-remote]
+
+;0=remote,1 ; Retrieve Memory
+;1=remote,2 ; Set freq.
+;2=remote,3 ; Set Rx PL tone.
+;40=remote,100 ; Rx PL off
+;41=remote,101 ; Rx PL on
+;42=remote,102 ; Tx PL off
+;43=remote,103 ; Tx PL on
+;44=remote,104 ; Low Pwr
+;45=remote,105 ; Med Pwr
+;46=remote,106 ; Hi Pwr
+;5=remote,5 ; Status
+
+;[telemetry]
+
+; Telemetry entries are shared across all repeaters
+; Can be a tone sequence, morse string, or a file
+;
+; |t - Tone escape sequence
+;
+; Tone sequences consist of 1 or more 4-tuple entries (freq1, freq2, duration, amplitude)
+; Single frequencies are created by setting freq1 or freq2 to zero.
+;
+; |m - Morse escape sequence
+;
+; Sends Morse code at the telemetry amplitude and telemetry frequency as defined in the
+; [morse] section.
+;
+; Follow with an alphanumeric string
+;
+; |i - Morse ID escape sequence
+;
+; Sends Morse code at the ID amplitude and ID frequency as defined in the
+; [morse] section.
+;
+; Follow with an alphanumeric string
+
+
+;ct1=|t(350,0,100,2048)(500,0,100,2048)(660,0,100,2048)
+;ct2=|t(660,880,150,2048)
+;ct3=|t(440,0,150,2048)
+;ct4=|t(550,0,150,2048)
+;ct5=|t(660,0,150,2048)
+;ct6=|t(880,0,150,2048)
+;ct7=|t(660,440,150,2048)
+;ct8=|t(700,1100,150,2048)
+;remotetx=|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048);
+;remotemon=|t(1600,0,75,2048)
+;cmdmode=|t(900,903,200,2048)
+;functcomplete=|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)
+
+
+;[morse]
+
+;speed=20 ; Approximate speed in WPM
+;frequency=800 ; Morse Telemetry Frequency
+;amplitude=4096 ; Morse Telemetry Amplitude
+;idfrequency=330 ; Morse ID Frequency
+;idamplitude=2048 ; Morse ID Amplitude
+
+;[nodes]
+
+;000 = context_A@foo.bar.com/1234,foo.bar.com
+;001 = context_B@baz.waldo.com/4321,baz.waldo.com
+;002 = context_C@pepper.salt.com/5678,pepper.salt.com,y ; this is a remote
+
+;of course, you can also specify these with domain names, but why rely
+;on DNS working unnecessarily?
+
+;[memory]
+
+;; this example gives you 146.460, simplex, 100.0 HZ PL, hi-power, transmit PL
+;00 = 146.460,100.0,sht
+;; this example gives you 146.940, minus offset, 100.0 HZ PL, low-power, no PL
+;01 = 146.940,100.0,-l
+
+; The format for these entries is: Receive-Freq,Receive-PL,Attrbutes
+; Attributes: l=low power, m=medium power, h=high power, -=minus offset,
+; s=simplex, +=plus offset, t=tx PL enable, r=rx PL enable
+
diff --git a/trunk/configs/rtp.conf.sample b/trunk/configs/rtp.conf.sample
new file mode 100644
index 000000000..a96a0a09b
--- /dev/null
+++ b/trunk/configs/rtp.conf.sample
@@ -0,0 +1,22 @@
+;
+; RTP Configuration
+;
+[general]
+;
+; RTP start and RTP end configure start and end addresses
+;
+; Defaults are rtpstart=5000 and rtpend=31000
+;
+rtpstart=10000
+rtpend=20000
+;
+; Whether to enable or disable UDP checksums on RTP traffic
+;
+;rtpchecksums=no
+;
+; The amount of time a DTMF digit with no 'end' marker should be
+; allowed to continue (in 'samples', 1/8000 of a second)
+;
+;dtmftimeout=3000
+; rtcpinterval = 5000 ; Milliseconds between rtcp reports
+ ;(min 500, max 60000, default 5000)
diff --git a/trunk/configs/say.conf.sample b/trunk/configs/say.conf.sample
new file mode 100644
index 000000000..e8b8ed7ae
--- /dev/null
+++ b/trunk/configs/say.conf.sample
@@ -0,0 +1,200 @@
+;
+; language configuration
+;
+
+[general]
+mode=old ; method for playing numbers and dates
+ ; old - using asterisk core function
+ ; new - using this configuration file
+
+; The new language routines produce strings of the form
+; prefix:[format:]data
+; that are matched against the rules in this file to produce
+; an output.
+;
+; The data is generally the string to be spelled (either a number,
+; an ascii string or a date/time in the format specified below).
+; It is available, in the right hand side of a rule, as variable ${SAY}.
+;
+; The format is optional and normally used only for date/time.
+; The prefix is used to select the pronunciation - standard
+; prefixes are
+; num used for numbers
+; enum used for enumerations
+; date for dates
+; time for times
+; datetime for dates and times
+; char for character strings
+; phonetic for phonetic strings
+; digit for digit strings
+;
+; but others can be used at will.
+;
+; Processing occurs as follows:
+; If the format is empty, or there is no format, the entire
+; string is matched against one of the pattern on the left hand side.
+; On the first match, the various comma-separated components on the right
+; hand side are pronounced, as follows:
+; + a component starting with a prefix: (i.e. with a ':' in it)
+; is re-processed according to these rules;
+; + a component without a ':' in it is considered a filename and
+; the corresponding file is played.
+;
+; If the format is non-empty, the format is split into its components
+; (individual characters, or filenames in single quotes), and then
+; filenames are played, whereas single characters are used to
+; generate a new string format:pat:data to be processed.
+;
+; DATES/AND TIMES assume that the date info is available in
+; the form YYYYMMDDHHmm.ss-dow-doy
+; with 4 digits for the year, 2 for month, day, hour, minutes, seconds,
+; one digit for the day-of-week, and 3 digits for the day-of-year.
+;
+; Example:
+; datetime::200604172030.00-4-102
+; (typical format for a date) is first matched against the line
+; datetime::. => date:AdBY 'digits/at' IMp:${SAY}
+; which is normally present with the default format for dates.
+; In turn, the format string "AdBY 'digits/at' IMp" results in
+; the sequence
+; date:A:200604172030.00-4-102
+; date:d:200604172030.00-4-102
+; date:B:200604172030.00-4-102
+; date:Y:200604172030.00-4-102
+; digits/at
+; date:I:200604172030.00-4-102
+; date:M:200604172030.00-4-102
+; date:p:200604172030.00-4-102
+;
+;
+; Remember, normally X Z N are special, and the search is
+; case insensitive, so you must use [X] [N] [Z] .. if you
+; want exact match.
+
+; We start with the basic rules that might be more-or-less
+; language-independent
+
+[digit-base](!) ; base rule for digit strings
+ ; XXX incomplete yet
+ _digit:[0-9] => digits/${SAY}
+ _digit:[-] => letters/dash
+ _digit:[*] => letters/star
+ _digit:[@] => letters/at
+ _digit:[0-9]. => digit:${SAY:0:1}, digit:${SAY:1}
+
+[date-base](!) ; base rules for dates and times
+ ; the 'SAY' variable contains YYYYMMDDHHmm.ss-dow-doy
+ ; these rule map the strftime attributes.
+ _date:Y:. => num:${SAY:0:4} ; year, 19xx
+ _date:[Bb]:. => digits/mon-$[${SAY:4:2}-1] ; month name, 0..11
+ _date:[Aa]:. => digits/day-${SAY:16:1} ; day of week
+ _date:[de]:. => num:${SAY:6:2} ; day of month
+ _date:[hH]:. => num:${SAY:8:2} ; hour
+ _date:[I]:. => num:$[${SAY:8:2} % 12] ; hour 0-12
+ _date:[M]:. => num:${SAY:10:2} ; minute
+ ; XXX too bad the '?' function does not remove the quotes
+ ; _date:[pP]:. => digits/$[ ${SAY:10:2} > 12 ? "p-m" :: "a-m"] ; am pm
+ _date:[pP]:. => digits/p-m ; am pm
+ _date:[S]:. => num:${SAY:13:2} ; seconds
+
+[en-base](!)
+ _[n]um:0. => num:${SAY:1}
+ _[n]um:X => digits/${SAY}
+ _[n]um:1X => digits/${SAY}
+ _[n]um:[2-9]0 => digits/${SAY}
+ _[n]um:[2-9][1-9] => digits/${SAY:0:1}0, num:${SAY:1}
+ _[n]um:XXX => num:${SAY:0:1}, digits/hundred, num:${SAY:1}
+
+ _[n]um:XXXX => num:${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
+ _[n]um:XXXXXX => num:${SAY:0:3}, digits/thousand, num:${SAY:3}
+
+ _[n]um:XXXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:XXXXXXXX => num:${SAY:0:2}, digits/million, num:${SAY:2}
+ _[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/million, num:${SAY:3}
+
+ _[n]um:XXXXXXXXXX => num:${SAY:0:1}, digits/billion, num:${SAY:1}
+ _[n]um:XXXXXXXXXXX => num:${SAY:0:2}, digits/billion, num:${SAY:2}
+ _[n]um:XXXXXXXXXXXX => num:${SAY:0:3}, digits/billion, num:${SAY:3}
+
+ ; enumeration
+ _e[n]um:X => digits/h-${SAY}
+ _e[n]um:1X => digits/h-${SAY}
+ _e[n]um:[2-9]0 => digits/h-${SAY}
+ _e[n]um:[2-9][1-9] => num:${SAY:0:1}0, digits/h-${SAY:1}
+ _e[n]um:[1-9]XX => num:${SAY:0:1}, digits/hundred, enum:${SAY:1}
+
+[it](digit-base,date-base)
+ _[n]um:0. => num:${SAY:1}
+ _[n]um:X => digits/${SAY}
+ _[n]um:1X => digits/${SAY}
+ _[n]um:[2-9]0 => digits/${SAY}
+ _[n]um:[2-9][1-9] => digits/${SAY:0:1}0, num:${SAY:1}
+ _[n]um:1XX => digits/hundred, num:${SAY:1}
+ _[n]um:[2-9]XX => num:${SAY:0:1}, digits/hundred, num:${SAY:1}
+
+ _[n]um:1XXX => digits/thousand, num:${SAY:1}
+ _[n]um:[2-9]XXX => num:${SAY:0:1}, digits/thousands, num:${SAY:1}
+ _[n]um:XXXXX => num:${SAY:0:2}, digits/thousands, num:${SAY:2}
+ _[n]um:XXXXXX => num:${SAY:0:3}, digits/thousands, num:${SAY:3}
+
+ _[n]um:1XXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:[2-9]XXXXXX => num:${SAY:0:1}, digits/millions, num:${SAY:1}
+ _[n]um:XXXXXXXX => num:${SAY:0:2}, digits/millions, num:${SAY:2}
+ _[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/millions, num:${SAY:3}
+
+ _datetime::. => date:AdBY 'digits/at' IMp:${SAY}
+ _date::. => date:AdBY:${SAY}
+ _time::. => date:IMp:${SAY}
+
+[en](en-base,date-base,digit-base)
+ _datetime::. => date:AdBY 'digits/at' IMp:${SAY}
+ _date::. => date:AdBY:${SAY}
+ _time::. => date:IMp:${SAY}
+
+[de](date-base,digit-base)
+ _[n]um:0. => num:${SAY:1}
+ _[n]um:X => digits/${SAY}
+ _[n]um:1X => digits/${SAY}
+ _[n]um:[2-9]0 => digits/${SAY}
+ _[n]um:[2-9][1-9] => digits/${SAY:1}-and, digits/${SAY:0:1}0
+ _[n]um:1XX => digits/ein, digits/hundred, num:${SAY:1}
+ _[n]um:[2-9]XX => digits/${SAY:0:1}, digits/hundred, num:${SAY:1}
+ _[n]um:1XXX => digits/ein, digits/thousand, num:${SAY:1}
+ _[n]um:[2-9]XXX => digits/${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
+ _[n]um:X00XXX => digits/${SAY:0:1}, digits/hundred, digits/thousand, num:${SAY:3}
+ _[n]um:XXXXXX => digits/${SAY:0:1}, digits/hundred, num:${SAY:1}
+ _[n]um:1XXXXXX => digits/eine, digits/million, num:${SAY:1}
+ _[n]um:[2-9]XXXXXX => digits/${SAY:0:1}, digits/millions, num:${SAY:1}
+ _[n]um:XXXXXXXX => num:${SAY:0:2}, digits/millions, num:${SAY:2}
+ _[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/millions, num:${SAY:3}
+
+ _datetime::. => date:AdBY 'digits/at' IMp:${SAY}
+ _date::. => date:AdBY:${SAY}
+ _time::. => date:IMp:${SAY}
+
+[hu](digit-base,date-base)
+ _[n]um:0. => num:${SAY:1}
+ _[n]um:X => digits/${SAY}
+ _[n]um:1[1-9] => digits/10en, digits/${SAY:1}
+ _[n]um:2[1-9] => digits/20on, digits/${SAY:1}
+ _[n]um:[1-9]0 => digits/${SAY}
+ _[n]um:[3-9][1-9] => digits/${SAY:0:1}0, num:${SAY:1}
+ _[n]um:XXX => num:${SAY:0:1}, digits/hundred, num:${SAY:1}
+
+ _[n]um:XXXX => num:${SAY:0:1}, digits/thousand, num:${SAY:1}
+ _[n]um:XXXXX => num:${SAY:0:2}, digits/thousand, num:${SAY:2}
+ _[n]um:XXXXXX => num:${SAY:0:3}, digits/thousand, num:${SAY:3}
+
+ _[n]um:XXXXXXX => num:${SAY:0:1}, digits/million, num:${SAY:1}
+ _[n]um:XXXXXXXX => num:${SAY:0:2}, digits/million, num:${SAY:2}
+ _[n]um:XXXXXXXXX => num:${SAY:0:3}, digits/million, num:${SAY:3}
+
+ _[n]um:XXXXXXXXXX => num:${SAY:0:1}, digits/billion, num:${SAY:1}
+ _[n]um:XXXXXXXXXXX => num:${SAY:0:2}, digits/billion, num:${SAY:2}
+ _[n]um:XXXXXXXXXXXX => num:${SAY:0:3}, digits/billion, num:${SAY:3}
+
+ _datetime::. => date:YBdA k 'ora' M 'perc':${SAY}
+ _date::. => date:YBdA:${SAY}
+ _time::. => date:k 'ora' M 'perc':${SAY}
diff --git a/trunk/configs/sip.conf.sample b/trunk/configs/sip.conf.sample
new file mode 100644
index 000000000..62eee41bb
--- /dev/null
+++ b/trunk/configs/sip.conf.sample
@@ -0,0 +1,896 @@
+;
+; SIP Configuration example for Asterisk
+;
+; SIP dial strings
+;-----------------------------------------------------------
+; In the dialplan (extensions.conf) you can use several
+; syntaxes for dialing SIP devices.
+; SIP/devicename
+; SIP/username@domain (SIP uri)
+; SIP/username@host:port
+; SIP/devicename/extension
+;
+;
+; Devicename
+; devicename is defined as a peer in a section below.
+;
+; username@domain
+; Call any SIP user on the Internet
+; (Don't forget to enable DNS SRV records if you want to use this)
+;
+; devicename/extension
+; If you define a SIP proxy as a peer below, you may call
+; SIP/proxyhostname/user or SIP/user@proxyhostname
+; where the proxyhostname is defined in a section below
+; This syntax also works with ATA's with FXO ports
+;
+; All of these dial strings specify the SIP request URI.
+; In addition, you can specify a specific To: header by adding an
+; exclamation mark after the dial string, like
+;
+; SIP/sales@mysipproxy!sales@edvina.net
+;
+; CLI Commands
+; -------------------------------------------------------------
+; Useful CLI commands to check peers/users:
+; sip show peers Show all SIP peers (including friends)
+; sip show users Show all SIP users (including friends)
+; sip show registry Show status of hosts we register with
+;
+; sip set debug Show all SIP messages
+;
+; sip reload Reload configuration file
+; Active SIP peers will not be reconfigured
+;
+
+; ** Deprecated configuration options **
+; The "call-limit" configuation option is deprecated. It still works in
+; this version of Asterisk, but will disappear in the next version.
+; You are encouraged to use the dialplan groupcount functionality
+; to enforce call limits instead of using this channel-specific method.
+;
+; You can still set limits per device in sip.conf or in a database by using
+; "setvar" to set variables that can be used in the dialplan for various limits.
+
+[general]
+context=default ; Default context for incoming calls
+;allowguest=no ; Allow or reject guest calls (default is yes)
+;match_auth_username=yes ; if available, match user entry using the
+ ; 'username' field from the authentication line
+ ; instead of the From: field.
+
+allowoverlap=no ; Disable overlap dialing support. (Default is yes)
+;allowtransfer=no ; Disable all transfers (unless enabled in peers or users)
+ ; Default is enabled
+;realm=mydomain.tld ; Realm for digest authentication
+ ; defaults to "asterisk". If you set a system name in
+ ; asterisk.conf, it defaults to that system name
+ ; Realms MUST be globally unique according to RFC 3261
+ ; Set this to your host name or domain name
+bindport=5060 ; UDP Port to bind to (SIP standard port is 5060)
+ ; bindport is the local UDP port that Asterisk will listen on
+bindaddr=0.0.0.0 ; IP address to bind to (0.0.0.0 binds to all)
+
+tcpenable=yes ; Enable server for incoming TCP connections (default is yes)
+tcpbindaddr=0.0.0.0 ; IP address for TCP server to bind to (0.0.0.0 binds to all interfaces)
+ ; Optionally add a port number, 192.168.1.1:5062 (default is port 5060)
+
+;tlsenable=no ; Enable server for incoming TLS (secure) connections (default is no)
+;tlsbindaddr=0.0.0.0 ; IP address for TLS server to bind to (0.0.0.0) binds to all interfaces)
+ ; Optionally add a port number, 192.168.1.1:5063 (default is port 5061)
+;tlscertfile=asterisk.pem ; Certificate file (*.pem only) to use for TLS connections
+ ; default is to look for "asterisk.pem" in current directory
+srvlookup=yes ; Enable DNS SRV lookups on outbound calls
+ ; Note: Asterisk only uses the first host
+ ; in SRV records
+ ; Disabling DNS SRV lookups disables the
+ ; ability to place SIP calls based on domain
+ ; names to some other SIP users on the Internet
+
+;domain=mydomain.tld ; Set default domain for this host
+ ; If configured, Asterisk will only allow
+ ; INVITE and REFER to non-local domains
+ ; Use "sip show domains" to list local domains
+;pedantic=yes ; Enable checking of tags in headers,
+ ; international character conversions in URIs
+ ; and multiline formatted headers for strict
+ ; SIP compatibility (defaults to "no")
+
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of these parameters.
+;tos_sip=cs3 ; Sets TOS for SIP packets.
+;tos_audio=ef ; Sets TOS for RTP audio packets.
+;tos_video=af41 ; Sets TOS for RTP video packets.
+;tos_text=af41 ; Sets TOS for RTP text packets.
+
+;cos_sip=3 ; Sets 802.1p priority for SIP packets.
+;cos_audio=5 ; Sets 802.1p priority for RTP audio packets.
+;cos_video=4 ; Sets 802.1p priority for RTP video packets.
+;cos_text=3 ; Sets 802.1p priority for RTP text packets.
+
+;maxexpiry=3600 ; Maximum allowed time of incoming registrations
+ ; and subscriptions (seconds)
+;minexpiry=60 ; Minimum length of registrations/subscriptions (default 60)
+;defaultexpiry=120 ; Default length of incoming/outgoing registration
+;qualifyfreq=60 ; Qualification: How often to check for the
+ ; host to be up in seconds
+ ; Set to low value if you use low timeout for
+ ; NAT of UDP sessions
+;notifymimetype=text/plain ; Allow overriding of mime type in MWI NOTIFY
+;buggymwi=no ; Cisco SIP firmware doesn't support the MWI RFC
+ ; fully. Enable this option to not get error messages
+ ; when sending MWI to phones with this bug.
+;vmexten=voicemail ; dialplan extension to reach mailbox sets the
+ ; Message-Account in the MWI notify message
+ ; defaults to "asterisk"
+;disallow=all ; First disallow all codecs
+;allow=ulaw ; Allow codecs in order of preference
+;allow=ilbc ; see doc/rtp-packetization for framing options
+;
+; This option specifies a preference for which music on hold class this channel
+; should listen to when put on hold if the music class has not been set on the
+; channel with Set(CHANNEL(musicclass)=whatever) in the dialplan, and the peer
+; channel putting this one on hold did not suggest a music class.
+;
+; This option may be specified globally, or on a per-user or per-peer basis.
+;
+;mohinterpret=default
+;
+; This option specifies which music on hold class to suggest to the peer channel
+; when this channel places the peer on hold. It may be specified globally or on
+; a per-user or per-peer basis.
+;
+;mohsuggest=default
+;
+;language=en ; Default language setting for all users/peers
+ ; This may also be set for individual users/peers
+;relaxdtmf=yes ; Relax dtmf handling
+;trustrpid = no ; If Remote-Party-ID should be trusted
+;sendrpid = yes ; If Remote-Party-ID should be sent
+;progressinband=never ; If we should generate in-band ringing always
+ ; use 'never' to never use in-band signalling, even in cases
+ ; where some buggy devices might not render it
+ ; Valid values: yes, no, never Default: never
+;useragent=Asterisk PBX ; Allows you to change the user agent string
+ ; The default user agent string also contains the Asterisk
+ ; version. If you don't want to expose this, change the
+ ; useragent string.
+;sdpsession=Asterisk PBX ; Allows you to change the SDP session name string, (s=)
+ ; Like the useragent parameter, the default user agent string
+ ; also contains the Asterisk version.
+;sdpowner=root ; Allows you to change the username field in the SDP owner string, (o=)
+ ; This field MUST NOT contain spaces
+;promiscredir = no ; If yes, allows 302 or REDIR to non-local SIP address
+ ; Note that promiscredir when redirects are made to the
+ ; local system will cause loops since Asterisk is incapable
+ ; of performing a "hairpin" call.
+;usereqphone = no ; If yes, ";user=phone" is added to uri that contains
+ ; a valid phone number
+;dtmfmode = rfc2833 ; Set default dtmfmode for sending DTMF. Default: rfc2833
+ ; Other options:
+ ; info : SIP INFO messages (application/dtmf-relay)
+ ; shortinfo : SIP INFO messages (application/dtmf)
+ ; inband : Inband audio (requires 64 kbit codec -alaw, ulaw)
+ ; auto : Use rfc2833 if offered, inband otherwise
+
+;compactheaders = yes ; send compact sip headers.
+;
+;videosupport=yes ; Turn on support for SIP video. You need to turn this on
+ ; in the this section to get any video support at all.
+ ; You can turn it off on a per peer basis if the general
+ ; video support is enabled, but you can't enable it for
+ ; one peer only without enabling in the general section.
+;maxcallbitrate=384 ; Maximum bitrate for video calls (default 384 kb/s)
+ ; Videosupport and maxcallbitrate is settable
+ ; for peers and users as well
+;callevents=no ; generate manager events when sip ua
+ ; performs events (e.g. hold)
+;alwaysauthreject = yes ; When an incoming INVITE or REGISTER is to be rejected,
+ ; for any reason, always reject with '401 Unauthorized'
+ ; instead of letting the requester know whether there was
+ ; a matching user or peer for their request
+
+;g726nonstandard = yes ; If the peer negotiates G726-32 audio, use AAL2 packing
+ ; order instead of RFC3551 packing order (this is required
+ ; for Sipura and Grandstream ATAs, among others). This is
+ ; contrary to the RFC3551 specification, the peer _should_
+ ; be negotiating AAL2-G726-32 instead :-(
+;outboundproxy=proxy.provider.domain ; send outbound signaling to this proxy, not directly to the devices
+;outboundproxy=proxy.provider.domain:8080 ; send outbound signaling to this proxy, not directly to the devices
+;outboundproxy=proxy.provider.domain,force ; Send ALL outbound signalling to proxy, ignoring route: headers
+;matchexterniplocally = yes ; Only substitute the externip or externhost setting if it matches
+ ; your localnet setting. Unless you have some sort of strange network
+ ; setup you will not need to enable this.
+
+;
+; If regcontext is specified, Asterisk will dynamically create and destroy a
+; NoOp priority 1 extension for a given peer who registers or unregisters with
+; us and have a "regexten=" configuration item.
+; Multiple contexts may be specified by separating them with '&'. The
+; actual extension is the 'regexten' parameter of the registering peer or its
+; name if 'regexten' is not provided. If more than one context is provided,
+; the context must be specified within regexten by appending the desired
+; context after '@'. More than one regexten may be supplied if they are
+; separated by '&'. Patterns may be used in regexten.
+;
+;regcontext=sipregistrations
+;regextenonqualify=yes ; Default "no"
+ ; If you have qualify on and the peer becomes unreachable
+ ; this setting will enforce inactivation of the regexten
+ ; extension for the peer
+;
+;--------------------------- SIP timers ----------------------------------------------------
+; These timers are used primarily in INVITE transactions.
+; The default for Timer T1 is 500 ms or the measured run-trip time between
+; Asterisk and the device if you have qualify=yes for the device.
+;
+;t1min=100 ; Minimum roundtrip time for messages to monitored hosts
+ ; Defaults to 100 ms
+;timert1=500 ; Default T1 timer
+ ; Defaults to 500 ms
+;timerb=32000 ; Call setup timer. If a provisional response is not received
+ ; in this amount of time, the call will autocongest
+ ; Defaults to 64*timert1
+
+;--------------------------- RTP timers ----------------------------------------------------
+; These timers are currently used for both audio and video streams. The RTP timeouts
+; are only applied to the audio channel.
+; The settings are settable in the global section as well as per device
+;
+;rtptimeout=60 ; Terminate call if 60 seconds of no RTP or RTCP activity
+ ; on the audio channel
+ ; when we're not on hold. This is to be able to hangup
+ ; a call in the case of a phone disappearing from the net,
+ ; like a powerloss or grandma tripping over a cable.
+;rtpholdtimeout=300 ; Terminate call if 300 seconds of no RTP or RTCP activity
+ ; on the audio channel
+ ; when we're on hold (must be > rtptimeout)
+;rtpkeepalive=<secs> ; Send keepalives in the RTP stream to keep NAT open
+ ; (default is off - zero)
+
+;--------------------------- SIP Session-Timers (RFC 4028)------------------------------------
+; SIP Session-Timers provide an end-to-end keep-alive mechanism for active SIP sessions.
+; This mechanism can detect and reclaim SIP channels that do not terminate through normal
+; signaling procedures. Session-Timers can be configured globally or at a user/peer level.
+; The operation of Session-Timers is driven by the following configuration parameters:
+;
+; * session-timers - Session-Timers feature operates in the following three modes:
+; originate : Request and run session-timers always
+; accept : Run session-timers only when requested by other UA
+; refuse : Do not run session timers in any case
+; The default mode of operation is 'accept'.
+; * session-expires - Maximum session refresh interval in seconds. Defaults to 1800 secs.
+; * session-minse - Minimum session refresh interval in seconds. Defualts to 90 secs.
+; * session-refresher - The session refresher (uac|uas). Defaults to 'uas'.
+;
+;session-timers=originate
+;session-expires=600
+;session-minse=90
+;session-refresher=uas
+
+
+;--------------------------- SIP DEBUGGING ---------------------------------------------------
+;sipdebug = yes ; Turn on SIP debugging by default, from
+ ; the moment the channel loads this configuration
+;recordhistory=yes ; Record SIP history by default
+ ; (see sip history / sip no history)
+;dumphistory=yes ; Dump SIP history at end of SIP dialogue
+ ; SIP history is output to the DEBUG logging channel
+
+
+;--------------------------- STATUS NOTIFICATIONS (SUBSCRIPTIONS) ----------------------------
+; You can subscribe to the status of extensions with a "hint" priority
+; (See extensions.conf.sample for examples)
+; chan_sip support two major formats for notifications: dialog-info and SIMPLE
+;
+; You will get more detailed reports (busy etc) if you have a call counter enabled
+; for a device.
+;
+; If you set the busylevel, we will indicate busy when we have a number of calls that
+; matches the busylevel treshold.
+;
+; For queues, you will need this level of detail in status reporting, regardless
+; if you use SIP subscriptions. Queues and manager use the same internal interface
+; for reading status information.
+;
+; Note: Subscriptions does not work if you have a realtime dialplan and use the
+; realtime switch.
+;
+;allowsubscribe=no ; Disable support for subscriptions. (Default is yes)
+;subscribecontext = default ; Set a specific context for SUBSCRIBE requests
+ ; Useful to limit subscriptions to local extensions
+ ; Settable per peer/user also
+;notifyringing = yes ; Notify subscriptions on RINGING state (default: no)
+;notifyhold = yes ; Notify subscriptions on HOLD state (default: no)
+ ; Turning on notifyringing and notifyhold will add a lot
+ ; more database transactions if you are using realtime.
+;callcounter = yes ; Enable call counters on devices. This can be set per
+ ; device too.
+;counteronpeer = yes ; Apply call counting on peers only. This will improve
+ ; status notification when you are using type=friend
+ ; Inbound calls, that really apply to the user part
+ ; of a friend will now be added to and compared with
+ ; the peer counter instead of applying two call counters,
+ ; one for the peer and one for the user.
+ ; "sip show inuse" will only show active calls on
+ ; the peer side of a "type=friend" object if this
+ ; setting is turned on.
+
+;----------------------------------------- T.38 FAX PASSTHROUGH SUPPORT -----------------------
+;
+; This setting is available in the [general] section as well as in device configurations.
+; Setting this to yes, enables T.38 fax (UDPTL) passthrough on SIP to SIP calls, provided
+; both parties have T38 support enabled in their Asterisk configuration
+; This has to be enabled in the general section for all devices to work. You can then
+; disable it on a per device basis.
+;
+; T.38 faxing only works in SIP to SIP calls, with no local or agent channel being used.
+;
+; t38pt_udptl = yes ; Default false
+;
+;----------------------------------------- OUTBOUND SIP REGISTRATIONS ------------------------
+; Asterisk can register as a SIP user agent to a SIP proxy (provider)
+; Format for the register statement is:
+; register => [transport://]user[:secret[:authuser]]@host[:port][/extension]
+;
+;
+;
+; If no extension is given, the 's' extension is used. The extension needs to
+; be defined in extensions.conf to be able to accept calls from this SIP proxy
+; (provider).
+;
+; host is either a host name defined in DNS or the name of a section defined
+; below.
+;
+; A similar effect can be achieved by adding a "callbackextension" option in a peer section.
+; this is equivalent to having the following line in the general section:
+;
+; register => username:secret@host/callbackextension
+;
+; and more readable because you don't have to write the parameters in two places
+; (note that the "port" is ignored - this is a bug that should be fixed).
+;
+; Examples:
+;
+;register => 1234:password@mysipprovider.com
+;
+; This will pass incoming calls to the 's' extension
+;
+;
+;register => 2345:password@sip_proxy/1234
+;
+; Register 2345 at sip provider 'sip_proxy'. Calls from this provider
+; connect to local extension 1234 in extensions.conf, default context,
+; unless you configure a [sip_proxy] section below, and configure a
+; context.
+; Tip 1: Avoid assigning hostname to a sip.conf section like [provider.com]
+; Tip 2: Use separate type=peer and type=user sections for SIP providers
+; (instead of type=friend) if you have calls in both directions
+
+;registertimeout=20 ; retry registration calls every 20 seconds (default)
+;registerattempts=10 ; Number of registration attempts before we give up
+ ; 0 = continue forever, hammering the other server
+ ; until it accepts the registration
+ ; Default is 0 tries, continue forever
+
+;----------------------------------------- NAT SUPPORT ------------------------
+;
+; WARNING: SIP operation behind a NAT is tricky and you really need
+; to read and understand well the following section.
+;
+; When Asterisk is behind a NAT device, the "local" address (and port) that
+; a socket is bound to has different values when seen from the inside or
+; from the outside of the NATted network. Unfortunately this address must
+; be communicated to the outside (e.g. in SIP and SDP messages), and in
+; order to determine the correct value Asterisk needs to know:
+;
+; + whether it is talking to someone "inside" or "outside" of the NATted network.
+; This is configured by assigning the "localnet" parameter with a list
+; of network addresses that are considered "inside" of the NATted network.
+; IF LOCALNET IS NOT SET, THE EXTERNAL ADDRESS WILL NOT BE SET CORRECTLY.
+; Multiple entries are allowed, e.g. a reasonable set is the following:
+;
+; localnet=192.168.0.0/255.255.0.0 ; RFC 1918 addresses
+; localnet=10.0.0.0/255.0.0.0 ; Also RFC1918
+; localnet=172.16.0.0/12 ; Another RFC1918 with CIDR notation
+; localnet=169.254.0.0/255.255.0.0 ; Zero conf local network
+;
+; + the "externally visible" address and port number to be used when talking
+; to a host outside the NAT. This information is derived by one of the
+; following (mutually exclusive) config file parameters:
+;
+; a. "externip = hostname[:port]" specifies a static address[:port] to
+; be used in SIP and SDP messages.
+; The hostname is looked up only once, when [re]loading sip.conf .
+; If a port number is not present, use the "bindport" value (which is
+; not guaranteed to work correctly, because a NAT box might remap the
+; port number as well as the address).
+; This approach can be useful if you have a NAT device where you can
+; configure the mapping statically. Examples:
+;
+; externip = 12.34.56.78 ; use this address.
+; externip = 12.34.56.78:9900 ; use this address and port.
+; externip = mynat.my.org:12600 ; Public address of my nat box.
+;
+; b. "externhost = hostname[:port]" is similar to "externip" except
+; that the hostname is looked up every "externrefresh" seconds
+; (default 10s). This can be useful when your NAT device lets you choose
+; the port mapping, but the IP address is dynamic.
+; Beware, you might suffer from service disruption when the name server
+; resolution fails. Examples:
+;
+; externhost=foo.dyndns.net ; refreshed periodically
+; externrefresh=180 ; change the refresh interval
+;
+; c. "stunaddr = stun.server[:port]" queries the STUN server specified
+; as an argument to obtain the external address/port.
+; Queries are also sent periodically every "externrefresh" seconds
+; (as a side effect, sending the query also acts as a keepalive for
+; the state entry on the nat box):
+;
+; stunaddr = foo.stun.com:3478
+; externrefresh = 15
+;
+; Note that at the moment all these mechanism work only for the SIP socket.
+; The IP address discovered with externip/externhost/STUN is reused for
+; media sessions as well, but the port numbers are not remapped so you
+; may still experience problems.
+;
+; NOTE 1: in some cases, NAT boxes will use different port numbers in
+; the internal<->external mapping. In these cases, the "externip" and
+; "externhost" might not help you configure addresses properly, and you
+; really need to use STUN.
+;
+; NOTE 2: when using "externip" or "externhost", the address part is
+; also used as the external address for media sessions.
+; If you use "stunaddr", STUN queries will be sent to the same server
+; also from media sockets, and this should permit a correct mapping of
+; the port numbers as well.
+;
+; In addition to the above, Asterisk has an additional "nat" parameter to
+; address NAT-related issues in incoming SIP or media sessions.
+; In particular, depending on the 'nat= ' settings described below, Asterisk
+; may override the address/port information specified in the SIP/SDP messages,
+; and use the information (sender address) supplied by the network stack instead.
+; However, this is only useful if the external traffic can reach us.
+; The following settings are allowed (both globally and in individual sections):
+;
+; nat = no ; default. Use NAT mode only according to RFC3581 (;rport)
+; nat = yes ; Always ignore info and assume NAT
+; nat = never ; Never attempt NAT mode or RFC3581 support
+; nat = route ; route = Assume NAT, don't send rport
+; ; (work around more UNIDEN bugs)
+
+;----------------------------------- MEDIA HANDLING --------------------------------
+; By default, Asterisk tries to re-invite the audio to an optimal path. If there's
+; no reason for Asterisk to stay in the media path, the media will be redirected.
+; This does not really work with in the case where Asterisk is outside and have
+; clients on the inside of a NAT. In that case, you want to set canreinvite=nonat
+;
+;canreinvite=yes ; Asterisk by default tries to redirect the
+ ; RTP media stream (audio) to go directly from
+ ; the caller to the callee. Some devices do not
+ ; support this (especially if one of them is behind a NAT).
+ ; The default setting is YES. If you have all clients
+ ; behind a NAT, or for some other reason wants Asterisk to
+ ; stay in the audio path, you may want to turn this off.
+
+ ; This setting also affect direct RTP
+ ; at call setup (a new feature in 1.4 - setting up the
+ ; call directly between the endpoints instead of sending
+ ; a re-INVITE).
+
+;directrtpsetup=yes ; Enable the new experimental direct RTP setup. This sets up
+ ; the call directly with media peer-2-peer without re-invites.
+ ; Will not work for video and cases where the callee sends
+ ; RTP payloads and fmtp headers in the 200 OK that does not match the
+ ; callers INVITE. This will also fail if canreinvite is enabled when
+ ; the device is actually behind NAT.
+
+;canreinvite=nonat ; An additional option is to allow media path redirection
+ ; (reinvite) but only when the peer where the media is being
+ ; sent is known to not be behind a NAT (as the RTP core can
+ ; determine it based on the apparent IP address the media
+ ; arrives from).
+
+;canreinvite=update ; Yet a third option... use UPDATE for media path redirection,
+ ; instead of INVITE. This can be combined with 'nonat', as
+ ; 'canreinvite=update,nonat'. It implies 'yes'.
+
+;----------------------------------------- REALTIME SUPPORT ------------------------
+; For additional information on ARA, the Asterisk Realtime Architecture,
+; please read realtime.txt and extconfig.txt in the /doc directory of the
+; source code.
+;
+;rtcachefriends=yes ; Cache realtime friends by adding them to the internal list
+ ; just like friends added from the config file only on a
+ ; as-needed basis? (yes|no)
+
+;rtsavesysname=yes ; Save systemname in realtime database at registration
+ ; Default= no
+
+;rtupdate=yes ; Send registry updates to database using realtime? (yes|no)
+ ; If set to yes, when a SIP UA registers successfully, the ip address,
+ ; the origination port, the registration period, and the username of
+ ; the UA will be set to database via realtime.
+ ; If not present, defaults to 'yes'.
+;rtautoclear=yes ; Auto-Expire friends created on the fly on the same schedule
+ ; as if it had just registered? (yes|no|<seconds>)
+ ; If set to yes, when the registration expires, the friend will
+ ; vanish from the configuration until requested again. If set
+ ; to an integer, friends expire within this number of seconds
+ ; instead of the registration interval.
+
+;ignoreregexpire=yes ; Enabling this setting has two functions:
+ ;
+ ; For non-realtime peers, when their registration expires, the
+ ; information will _not_ be removed from memory or the Asterisk database
+ ; if you attempt to place a call to the peer, the existing information
+ ; will be used in spite of it having expired
+ ;
+ ; For realtime peers, when the peer is retrieved from realtime storage,
+ ; the registration information will be used regardless of whether
+ ; it has expired or not; if it expires while the realtime peer
+ ; is still in memory (due to caching or other reasons), the
+ ; information will not be removed from realtime storage
+
+;----------------------------------------- SIP DOMAIN SUPPORT ------------------------
+; Incoming INVITE and REFER messages can be matched against a list of 'allowed'
+; domains, each of which can direct the call to a specific context if desired.
+; By default, all domains are accepted and sent to the default context or the
+; context associated with the user/peer placing the call.
+; Domains can be specified using:
+; domain=<domain>[,<context>]
+; Examples:
+; domain=myasterisk.dom
+; domain=customer.com,customer-context
+;
+; In addition, all the 'default' domains associated with a server should be
+; added if incoming request filtering is desired.
+; autodomain=yes
+;
+; To disallow requests for domains not serviced by this server:
+; allowexternaldomains=no
+
+;domain=mydomain.tld,mydomain-incoming
+ ; Add domain and configure incoming context
+ ; for external calls to this domain
+;domain=1.2.3.4 ; Add IP address as local domain
+ ; You can have several "domain" settings
+;allowexternaldomains=no ; Disable INVITE and REFER to non-local domains
+ ; Default is yes
+;autodomain=yes ; Turn this on to have Asterisk add local host
+ ; name and local IP to domain list.
+
+; fromdomain=mydomain.tld ; When making outbound SIP INVITEs to
+ ; non-peers, use your primary domain "identity"
+ ; for From: headers instead of just your IP
+ ; address. This is to be polite and
+ ; it may be a mandatory requirement for some
+ ; destinations which do not have a prior
+ ; account relationship with your server.
+
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
+ ; SIP channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The SIP channel can accept jitter,
+ ; thus a jitterbuffer on the receive SIP side will be used only
+ ; if it is forced and enabled.
+
+; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a SIP
+ ; channel. Defaults to "no".
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmaxsize) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+
+[authentication]
+; Global credentials for outbound calls, i.e. when a proxy challenges your
+; Asterisk server for authentication. These credentials override
+; any credentials in peer/register definition if realm is matched.
+;
+; This way, Asterisk can authenticate for outbound calls to other
+; realms. We match realm on the proxy challenge and pick an set of
+; credentials from this list
+; Syntax:
+; auth = <user>:<secret>@<realm>
+; auth = <user>#<md5secret>@<realm>
+; Example:
+;auth=mark:topsecret@digium.com
+;
+; You may also add auth= statements to [peer] definitions
+; Peer auth= override all other authentication settings if we match on realm
+
+;------------------------------------------------------------------------------
+; Users and peers have different settings available. Friends have all settings,
+; since a friend is both a peer and a user
+;
+; User config options: Peer configuration:
+; -------------------- -------------------
+; context context
+; callingpres callingpres
+; permit permit
+; deny deny
+; secret secret
+; md5secret md5secret
+; dtmfmode dtmfmode
+; canreinvite canreinvite
+; nat nat
+; callgroup callgroup
+; pickupgroup pickupgroup
+; language language
+; allow allow
+; disallow disallow
+; insecure insecure
+; trustrpid trustrpid
+; progressinband progressinband
+; promiscredir promiscredir
+; useclientcode useclientcode
+; accountcode accountcode
+; setvar setvar
+; callerid callerid
+; amaflags amaflags
+; call-limit call-limit (deprecated)
+; callcounter callcounter
+; allowoverlap allowoverlap
+; allowsubscribe allowsubscribe
+; allowtransfer allowtransfer
+; subscribecontext subscribecontext
+; videosupport videosupport
+; maxcallbitrate maxcallbitrate
+; rfc2833compensate mailbox
+; session-timers busylevel
+; session-expires
+; session-minse template
+; session-refresher fromdomain
+; regexten
+; fromuser
+; host
+; port
+; qualify
+; defaultip
+; defaultuser
+; rtptimeout
+; rtpholdtimeout
+; sendrpid
+; outboundproxy
+; rfc2833compensate
+; callbackextension
+; registertrying
+; session-timers
+; session-expires
+; session-minse
+; session-refresher
+; timert1
+; timerb
+; qualifyfreq
+
+
+;[sip_proxy]
+; For incoming calls only. Example: FWD (Free World Dialup)
+; We match on IP address of the proxy for incoming calls
+; since we can not match on username (caller id)
+;type=peer
+;context=from-fwd
+;host=fwd.pulver.com
+
+;[sip_proxy-out]
+;type=peer ; we only want to call out, not be called
+;secret=guessit
+;defaultuser=yourusername ; Authentication user for outbound proxies
+;fromuser=yourusername ; Many SIP providers require this!
+;fromdomain=provider.sip.domain
+;host=box.provider.com
+;usereqphone=yes ; This provider requires ";user=phone" on URI
+;callcounter=yes ; Enable call counter
+;busylevel=2 ; Signal busy at 2 or more calls
+;outboundproxy=proxy.provider.domain ; send outbound signaling to this proxy, not directly to the peer
+;port=80 ; The port number we want to connect to on the remote side
+ ; Also used as "defaultport" in combination with "defaultip" settings
+
+;--- sample definition for a provider
+;[provider1]
+;type=peer
+;host=sip.provider1.com
+;fromuser=4015552299 ; how your provider knows you
+;secret=youwillneverguessit
+;callbackextension=123 ; Register with this server and require calls coming back to this extension
+
+;------------------------------------------------------------------------------
+; Definitions of locally connected SIP devices
+;
+; type = user a device that authenticates to us by "from" field to place calls
+; type = peer a device we place calls to or that calls us and we match by host
+; type = friend two configurations (peer+user) in one
+;
+; For device names, we recommend using only a-z, numerics (0-9) and underscore
+;
+; For local phones, type=friend works most of the time
+;
+; If you have one-way audio, you probably have NAT problems.
+; If Asterisk is on a public IP, and the phone is inside of a NAT device
+; you will need to configure nat option for those phones.
+; Also, turn on qualify=yes to keep the nat session open
+;
+; Because you might have a large number of similar sections, it is generally
+; convenient to use templates for the common parameters, and add them
+; the the various sections. Examples are below, and we can even leave
+; the templates uncommented as they will not harm:
+
+[basic-options](!) ; a template
+ dtmfmode=rfc2833
+ context=from-office
+ type=friend
+
+[natted-phone](!,basic-options) ; another template inheriting basic-options
+ nat=yes
+ canreinvite=no
+ host=dynamic
+
+[public-phone](!,basic-options) ; another template inheriting basic-options
+ nat=no
+ canreinvite=yes
+
+[my-codecs](!) ; a template for my preferred codecs
+ disallow=all
+ allow=ilbc
+ allow=g729
+ allow=gsm
+ allow=g723
+ allow=ulaw
+
+[ulaw-phone](!) ; and another one for ulaw-only
+ disallow=all
+ allow=ulaw
+
+; and finally instantiate a few phones
+;
+; [2133](natted-phone,my-codecs)
+; secret = peekaboo
+; [2134](natted-phone,ulaw-phone)
+; secret = not_very_secret
+; [2136](public-phone,ulaw-phone)
+; secret = not_very_secret_either
+; ...
+;
+
+; Standard configurations not using templates look like this:
+;
+;[grandstream1]
+;type=friend
+;context=from-sip ; Where to start in the dialplan when this phone calls
+;callerid=John Doe <1234> ; Full caller ID, to override the phones config
+ ; on incoming calls to Asterisk
+;host=192.168.0.23 ; we have a static but private IP address
+ ; No registration allowed
+;nat=no ; there is not NAT between phone and Asterisk
+;canreinvite=yes ; allow RTP voice traffic to bypass Asterisk
+;dtmfmode=info ; either RFC2833 or INFO for the BudgeTone
+;call-limit=1 ; permit only 1 outgoing call and 1 incoming call at a time
+ ; from the phone to asterisk (deprecated)
+ ; 1 for the explicit peer, 1 for the explicit user,
+ ; remember that a friend equals 1 peer and 1 user in
+ ; memory
+ ; There is no combined call counter for a "friend"
+ ; so there's currently no way in sip.conf to limit
+ ; to one inbound or outbound call per phone. Use
+ ; the group counters in the dial plan for that.
+ ;
+;mailbox=1234@default ; mailbox 1234 in voicemail context "default"
+;disallow=all ; need to disallow=all before we can use allow=
+;allow=ulaw ; Note: In user sections the order of codecs
+ ; listed with allow= does NOT matter!
+;allow=alaw
+;allow=g723.1 ; Asterisk only supports g723.1 pass-thru!
+;allow=g729 ; Pass-thru only unless g729 license obtained
+;callingpres=allowed_passed_screen ; Set caller ID presentation
+ ; See README.callingpres for more information
+
+;[xlite1]
+; Turn off silence suppression in X-Lite ("Transmit Silence"=YES)!
+; Note that Xlite sends NAT keep-alive packets, so qualify=yes is not needed
+;type=friend
+;regexten=1234 ; When they register, create extension 1234
+;callerid="Jane Smith" <5678>
+;host=dynamic ; This device needs to register
+;nat=yes ; X-Lite is behind a NAT router
+;canreinvite=no ; Typically set to NO if behind NAT
+;disallow=all
+;allow=gsm ; GSM consumes far less bandwidth than ulaw
+;allow=ulaw
+;allow=alaw
+;mailbox=1234@default,1233@default ; Subscribe to status of multiple mailboxes
+;registertrying=yes ; Send a 100 Trying when the device registers.
+
+;[snom]
+;type=friend ; Friends place calls and receive calls
+;context=from-sip ; Context for incoming calls from this user
+;secret=blah
+;subscribecontext=localextensions ; Only allow SUBSCRIBE for local extensions
+;language=de ; Use German prompts for this user
+;host=dynamic ; This peer register with us
+;dtmfmode=inband ; Choices are inband, rfc2833, or info
+;defaultip=192.168.0.59 ; IP used until peer registers
+;mailbox=1234@context,2345 ; Mailbox(-es) for message waiting indicator
+;subscribemwi=yes ; Only send notifications if this phone
+ ; subscribes for mailbox notification
+;vmexten=voicemail ; dialplan extension to reach mailbox
+ ; sets the Message-Account in the MWI notify message
+ ; defaults to global vmexten which defaults to "asterisk"
+;disallow=all
+;allow=ulaw ; dtmfmode=inband only works with ulaw or alaw!
+
+
+;[polycom]
+;type=friend ; Friends place calls and receive calls
+;context=from-sip ; Context for incoming calls from this user
+;secret=blahpoly
+;host=dynamic ; This peer register with us
+;dtmfmode=rfc2833 ; Choices are inband, rfc2833, or info
+;defaultuser=polly ; Username to use in INVITE until peer registers
+;defaultip=192.168.40.123
+ ; Normally you do NOT need to set this parameter
+;disallow=all
+;allow=ulaw ; dtmfmode=inband only works with ulaw or alaw!
+;progressinband=no ; Polycom phones don't work properly with "never"
+
+
+;[pingtel]
+;type=friend
+;secret=blah
+;host=dynamic
+;insecure=port ; Allow matching of peer by IP address without
+ ; matching port number
+;insecure=invite ; Do not require authentication of incoming INVITEs
+;insecure=port,invite ; (both)
+;qualify=1000 ; Consider it down if it's 1 second to reply
+ ; Helps with NAT session
+ ; qualify=yes uses default value
+;qualifyfreq=60 ; Qualification: How often to check for the
+ ; host to be up in seconds
+ ; Set to low value if you use low timeout for
+ ; NAT of UDP sessions
+;
+; Call group and Pickup group should be in the range from 0 to 63
+;
+;callgroup=1,3-4 ; We are in caller groups 1,3,4
+;pickupgroup=1,3-5 ; We can do call pick-p for call group 1,3,4,5
+;defaultip=192.168.0.60 ; IP address to use if peer has not registered
+;deny=0.0.0.0/0.0.0.0 ; ACL: Control access to this account based on IP address
+;permit=192.168.0.60/255.255.255.0
+
+;[cisco1]
+;type=friend
+;secret=blah
+;qualify=200 ; Qualify peer is no more than 200ms away
+;nat=yes ; This phone may be natted
+ ; Send SIP and RTP to the IP address that packet is
+ ; received from instead of trusting SIP headers
+;host=dynamic ; This device registers with us
+;canreinvite=no ; Asterisk by default tries to redirect the
+ ; RTP media stream (audio) to go directly from
+ ; the caller to the callee. Some devices do not
+ ; support this (especially if one of them is
+ ; behind a NAT).
+;defaultip=192.168.0.4 ; IP address to use until registration
+;defaultuser=goran ; Username to use when calling this device before registration
+ ; Normally you do NOT need to set this parameter
+;setvar=CUSTID=5678 ; Channel variable to be set for all calls from this device
+
+;[pre14-asterisk]
+;type=friend
+;secret=digium
+;host=dynamic
+;rfc2833compensate=yes ; Compensate for pre-1.4 DTMF transmission from another Asterisk machine.
+ ; You must have this turned on or DTMF reception will work improperly.
diff --git a/trunk/configs/sip_notify.conf.sample b/trunk/configs/sip_notify.conf.sample
new file mode 100644
index 000000000..ca7dbe3d0
--- /dev/null
+++ b/trunk/configs/sip_notify.conf.sample
@@ -0,0 +1,22 @@
+[polycom-check-cfg]
+Event=>check-sync
+Content-Length=>0
+
+; Untested
+[sipura-check-cfg]
+Event=>resync
+Content-Length=>0
+
+; Untested
+[grandstream-check-cfg]
+Event=>sys-control
+
+; Untested
+[cisco-check-cfg]
+Event=>check-sync
+Content-Length=>0
+
+; Tested
+[snom-check-cfg]
+Event=>check-sync\;reboot=false
+Content-Length=>0
diff --git a/trunk/configs/skinny.conf.sample b/trunk/configs/skinny.conf.sample
new file mode 100644
index 000000000..26a6db6c7
--- /dev/null
+++ b/trunk/configs/skinny.conf.sample
@@ -0,0 +1,120 @@
+;
+; Skinny Configuration for Asterisk
+;
+[general]
+bindaddr=0.0.0.0 ; Address to bind to
+bindport=2000 ; Port to bind to, default tcp/2000
+dateformat=M-D-Y ; M,D,Y in any order (6 chars max)
+ ; "A" may also be used, but it must be at the end.
+ ; Use M for month, D for day, Y for year, A for 12-hour time.
+keepalive=120
+
+;vmexten=8500 ; Systemwide voicemailmain pilot number
+ ; It must be in the same context as the calling
+ ; device/line
+
+; If regcontext is specified, Asterisk will dynamically create and destroy a
+; NoOp priority 1 extension for a given line which registers or unregisters with
+; us and have a "regexten=" configuration item.
+; Multiple contexts may be specified by separating them with '&'. The
+; actual extension is the 'regexten' parameter of the registering line or its
+; name if 'regexten' is not provided. If more than one context is provided,
+; the context must be specified within regexten by appending the desired
+; context after '@'. More than one regexten may be supplied if they are
+; separated by '&'. Patterns may be used in regexten.
+;
+;regcontext=skinnyregistrations
+
+;allow=all ; see doc/rtp-packetization for framing options
+;disallow=
+
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of these parameters.
+;tos=cs3 ; Sets TOS for signaling packets.
+;tos_audio=ef ; Sets TOS for RTP audio packets.
+;tos_video=af41 ; Sets TOS for RTP video packets.
+;cos=3 ; Sets 802.1p priority for signaling packets.
+;cos_audio=5 ; Sets 802.1p priority for RTP audio packets.
+;cos_video=4 ; Sets 802.1p priority for RTP video packets.
+
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+;jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
+ ; skinny channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The skinny channel can accept
+ ; jitter, thus a jitterbuffer on the receive skinny side will be
+ ; used only if it is forced and enabled.
+
+;jbforce = no ; Forces the use of a jitterbuffer on the receive side of a skinny
+ ; channel. Defaults to "no".
+
+;jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+;jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+;jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a
+ ; skinny channel. Two implementations are currently available
+ ; - "fixed" (with size always equals to jbmaxsize)
+ ; - "adaptive" (with variable size, actually the new jb of IAX2).
+ ; Defaults to fixed.
+
+;jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+
+
+; Typical config for 12SP+
+;[florian]
+;device=SEP00D0BA847E6B
+;version=P002G204 ; Thanks critch
+;context=did
+;canreinvite=yes ; Allow media to go directly between two RTP endpoints.
+;line => 120 ; Dial(Skinny/120@florian)
+
+
+; Typical config for a 7910
+;[duba] ; Device name
+;device=SEP0007EB463101 ; Official identifier
+;version=P002F202 ; Firmware version identifier
+;host=192.168.1.144
+;permit=192.168.0/24 ; Optional, used for authentication
+;nat=yes
+;callerid="George W. Bush" <202-456-1414>
+;setvar=CUSTID=5678 ; Channel variable to be set for all calls from this device
+;mailbox=500
+;callwaiting=yes
+;transfer=yes
+;threewaycalling=yes
+;context=default
+;line => 500 ; Dial(Skinny/500@duba)
+;mohinterpret=default ; This option specifies a default music on hold class to
+ ; use when put on hold if the channel's moh class was not
+ ; explicitly set with Set(CHANNEL(musicclass)=whatever) and
+ ; the peer channel did not suggest a class to use.
+;mohsuggest=default ; This option specifies which music on hold class to suggest to the peer channel
+ ; when this channel places the peer on hold. It may be specified globally or on
+ ; a per-user or per-peer basis.
+
+; Typical config for a 7940 with dual 7914s
+;[support]
+;device=SEP0007EB463121
+;nat=yes
+;callerid="Customer Support" <810-234-1212>
+;mailbox=100
+;vmexten=8500 ; Device level voicemailmain pilot number
+;regexten=100
+;context=inbound
+;linelabel="Support Line" ; Displays next to the line
+ ; button on 7940's and 7960s
+;line => 100
+;callerid="John Chambers" <408-526-4000>
+;context=did
+;regexten=110
+;linelabel="John"
+;mailbox=110
+;line => 110
+;speeddial => 111,Jack Smith ; Adds a speeddial button to a device.
+;speeddial => 112@hints,Bob Peterson ; When a context is specified, the speeddial watches a dialplan hint.
+;addon => 7914
+;addon => 7914
diff --git a/trunk/configs/sla.conf.sample b/trunk/configs/sla.conf.sample
new file mode 100644
index 000000000..75acc2bc4
--- /dev/null
+++ b/trunk/configs/sla.conf.sample
@@ -0,0 +1,140 @@
+;
+; Configuration for Shared Line Appearances (SLA).
+;
+; See doc/asterisk.pdf for more information.
+;
+
+; ---- General Options ----------------
+[general]
+
+;attemptcallerid=no ; Attempt CallerID handling. The default value for this
+ ; is "no" because CallerID handling with an SLA setup is
+ ; known to not work properly in some situations. However,
+ ; feel free to enable it if you would like. If you do, and
+ ; you find problems, please do not report them.
+; -------------------------------------
+
+
+; ---- Trunk Declarations -------------
+;
+;[line1] ; Provide a name for this trunk.
+
+;type=trunk ; This line is what marks this entry as a trunk.
+
+;device=Zap/3 ; Map this trunk declaration to a specific device.
+ ; NOTE: You can not just put any type of channel here.
+ ; Zap channels can be directly used. IP trunks
+ ; require some indirect configuration which is
+ ; described in doc/asterisk.pdf.
+
+;autocontext=line1 ; This supports automatic generation of the dialplan entries
+ ; if the autocontext option is used. Each trunk should have
+ ; a unique context name. Then, in zapata.conf, this device
+ ; should be configured to have incoming calls go to this context.
+
+;ringtimeout=30 ; Set how long to allow this trunk to ring on an inbound call before hanging
+ ; it up as an unanswered call. The value is in seconds.
+
+;barge=no ; If this option is set to "no", then no station will be
+ ; allowed to join a call that is in progress. The default
+ ; value is "yes".
+
+;hold=private ; This option configure hold permissions for this trunk.
+ ; "open" - This means that any station can put this trunk
+ ; on hold, and any station can retrieve it from
+ ; hold. This is the default.
+ ; "private" - This means that once a station puts the
+ ; trunk on hold, no other station will be
+ ; allowed to retrieve the call from hold.
+
+;[line2]
+;type=trunk
+;device=Zap/4
+;autocontext=line2
+
+;[line3]
+;type=trunk
+;device=Zap/3
+;autocontext=line3
+
+;[line4]
+;type=trunk
+;device=Local/disa@line4_outbound ; A Local channel in combination with the Disa
+ ; application can be used to support IP trunks.
+ ; See doc/asterisk.pdf on more information on how
+ ; IP trunks work.
+;autocontext=line4
+; --------------------------------------
+
+
+; ---- Station Declarations ------------
+
+;[station1] ; Define a name for this station.
+
+;type=station ; This line indicates that this entry is a station.
+
+;device=SIP/station1 ; Each station must be mapped to a device.
+
+;autocontext=sla_stations ; This supports automatic generation of the dialplan entries if
+ ; the autocontext option is used. All stations can use the same
+ ; context without conflict. The device for this station should
+ ; have its context configured to the same one listed here.
+
+;ringtimeout=10 ; Set a timeout for how long to allow the station to ring for an
+ ; incoming call, in seconds.
+
+;ringdelay=10 ; Set a time for how long to wait before beginning to ring this station
+ ; once there is an incoming call, in seconds.
+
+;hold=private ; This option configure hold permissions for this station. Note
+ ; that if private hold is set in the trunk entry, that will override
+ ; anything here. However, if a trunk has open hold access, but this
+ ; station is set to private hold, then the private hold will be in
+ ; effect.
+ ; "open" - This means that once this station puts a call
+ ; on hold, any other station is allowed to retrieve
+ ; it. This is the default.
+ ; "private" - This means that once this station puts a
+ ; call on hold, no other station will be
+ ; allowed to retrieve the call from hold.
+
+
+;trunk=line1 ; Individually list all of the trunks that will appear on this station. This
+ ; order is significant. It should be the same order as they appear on the
+ ; phone. The order here defines the order of preference that the trunks will
+ ; be used.
+;trunk=line2
+;trunk=line3,ringdelay=5 ; A ring delay for the station can also be specified for a specific trunk.
+ ; If a ring delay is specified both for the whole station and for a specific
+ ; trunk on a station, the setting for the specific trunk will take priority.
+ ; This value is in seconds.
+
+;trunk=line4,ringtimeout=5 ; A ring timeout for the station can also be specified for a specific trunk.
+ ; If a ring timeout is specified both for the whole station and for a specific
+ ; trunk on a station, the setting for the specific trunk will take priority.
+ ; This value is in seconds.
+
+
+;[station](!) ; When there are a lot of stations that are configured the same way,
+ ; it is convenient to use a configuration template like this so that
+ ; the common settings stay in one place.
+;type=station
+;autocontext=sla_stations
+;trunk=line1
+;trunk=line2
+;trunk=line3
+;trunk=line4
+
+;[station2](station) ; Define a station that uses the configuration from the template "station".
+;device=SIP/station2
+;
+;[station3](station)
+;device=SIP/station3
+;
+;[station4](station)
+;device=SIP/station4
+;
+;[station5](station)
+;device=SIP/station5
+; --------------------------------------
+
diff --git a/trunk/configs/smdi.conf.sample b/trunk/configs/smdi.conf.sample
new file mode 100644
index 000000000..0325eba48
--- /dev/null
+++ b/trunk/configs/smdi.conf.sample
@@ -0,0 +1,43 @@
+; Asterisk SMDI configuration
+
+[interfaces]
+; Specify serial ports to listen for SMDI messages on below. These will be
+; referenced later in zapata.conf. If you do not specify any interfaces then
+; SMDI will be disabled. Interfaces can have several different attributes
+; associated with them.
+
+; Set the number of stop bits to use per character here. The default is no,
+; in which case one stop bit will be used.
+
+;twostopbits = no
+
+; Character size or bit length is the size of each character sent across the
+; link. Character size can be 7 or 8. The default is 7.
+
+;charsize = 7
+
+; If you need parity checking enabled you can turn it on here. Acceptable
+; values are even, odd, and none. The default is even.
+
+;paritybit = even
+
+; The baudrate to use for this port. Acceptable values are 1200, 2400, 4800,
+; and 9600. The default is 9600.
+
+;baudrate = 1200
+
+; Often the numbering scheme for a set of mailboxes or extensions will not be 7
+; or 10 digits (as SMDI requires). Use the msdstrip option to strip unused
+; digits from the start of numbers.
+
+;msdstrip = 0
+
+; Occasionally Asterisk and the SMDI switch may become out of sync. If this
+; happens, Asterisk will appear one or several calls behind as it processes
+; voicemail requests. To prevent this from happening, adjust the msgexpirytime.
+; This will make Asterisk discard old SMDI messages that have not yet been
+; processed. The default expiry time is 30000 milliseconds.
+
+;msgexpirytime = 30000
+
+;smdiport => /dev/ttyS0
diff --git a/trunk/configs/telcordia-1.adsi b/trunk/configs/telcordia-1.adsi
new file mode 100644
index 000000000..1486aa95e
--- /dev/null
+++ b/trunk/configs/telcordia-1.adsi
@@ -0,0 +1,83 @@
+;
+; Asterisk default ADSI script
+;
+;
+; Begin with the preamble requirements
+;
+DESCRIPTION "Telcordia Demo" ; Name of vendor
+VERSION 0x02 ; Version of stuff
+;SECURITY "_AST" ; Security code
+SECURITY 0x0000 ; Security code
+FDN 0x0000000f ; Descriptor number
+
+;
+; Predefined strings
+;
+DISPLAY "talkingto" IS "Talking To" "$Call1p" WRAP
+DISPLAY "titles" IS "20th Century IQ Svc"
+DISPLAY "newcall" IS "New Call From" "$Call1p" WRAP
+DISPLAY "ringing" IS "Ringing"
+
+;
+; Begin state definitions
+;
+STATE "callup" ; Call is currently up
+STATE "inactive" ; No active call
+
+;
+; Begin soft key definitions
+;
+KEY "CB_OH" IS "Block" OR "Call Block"
+ OFFHOOK
+ VOICEMODE
+ WAITDIALTONE
+ SENDDTMF "*60"
+ SUBSCRIPT "offHook"
+ENDKEY
+
+KEY "CB" IS "Block" OR "Call Block"
+ SENDDTMF "*60"
+ENDKEY
+
+;
+; Begin main subroutine
+;
+
+SUB "main" IS
+ IFEVENT NEARANSWER THEN
+ CLEAR
+ SHOWDISPLAY "talkingto" AT 1
+ GOTO "stableCall"
+ ENDIF
+ IFEVENT OFFHOOK THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1
+ SHOWKEYS "CB"
+ GOTO "offHook"
+ ENDIF
+ IFEVENT IDLE THEN
+ CLEAR
+ SHOWDISPLAY "titles" AT 1
+ SHOWKEYS "CB_OH"
+ ENDIF
+ IFEVENT CALLERID THEN
+ CLEAR
+ SHOWDISPLAY "newcall" AT 1
+ ENDIF
+ENDSUB
+
+SUB "offHook" IS
+ IFEVENT FARRING THEN
+ CLEAR
+ SHOWDISPLAY "ringing" AT 1
+ ENDIF
+ IFEVENT FARANSWER THEN
+ CLEAR
+ SHOWDISPLAY "talkingto" AT 1
+ GOTO "stableCall"
+ ENDIF
+ENDSUB
+
+SUB "stableCall" IS
+
+ENDSUB
diff --git a/trunk/configs/udptl.conf.sample b/trunk/configs/udptl.conf.sample
new file mode 100644
index 000000000..05a38d54e
--- /dev/null
+++ b/trunk/configs/udptl.conf.sample
@@ -0,0 +1,30 @@
+;
+; UDPTL Configuration (UDPTL is one of the transports for T.38)
+;
+[general]
+;
+; UDPTL start and UDPTL end configure start and end addresses
+;
+udptlstart=4000
+udptlend=4999
+;
+; Whether to enable or disable UDP checksums on UDPTL traffic
+;
+;udptlchecksums=no
+;
+; The error correction type to be sent
+;
+T38FaxUdpEC = t38UDPFEC
+;T38FaxUdpEC = t38UDPRedundancy
+;
+; The maximum length of a UDPTL packet
+;
+T38FaxMaxDatagram = 400
+;
+; The number of error correction entries in a UDPTL packet
+;
+udptlfecentries = 3
+;
+; The span over which parity is calculated for FEC in a UDPTL packet
+;
+udptlfecspan = 3
diff --git a/trunk/configs/unistim.conf.sample b/trunk/configs/unistim.conf.sample
new file mode 100644
index 000000000..649737317
--- /dev/null
+++ b/trunk/configs/unistim.conf.sample
@@ -0,0 +1,76 @@
+;
+; chan_unistim configuration file.
+;
+
+[general]
+port=5000 ; UDP port
+;
+; See qos.tex or Quality of Service section of asterisk.pdf for a description of these parameters.
+;tos=cs3 ; Sets TOS for signaling packets.
+;tos_audio=ef ; Sets TOS for RTP audio packets.
+;cos=3 ; Sets 802.1p priority for signaling packets.
+;cos_audio=5 ; Sets 802.1p priority for RTP audio packets.
+;
+;keepalive=120 ; in seconds, default = 120
+;public_ip= ; if asterisk is behind a nat, specify your public IP
+;autoprovisioning=no ; Allow undeclared phones to register an extension. See README for important
+ ; informations. no (default), yes, tn.
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
+ ; SIP channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The SIP channel can accept jitter,
+ ; thus a jitterbuffer on the receive SIP side will be used only
+ ; if it is forced and enabled.
+
+; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a SIP
+ ; channel. Defaults to "no".
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmaxsize) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+
+
+;[black] ; name of the device
+;device=000ae4012345 ; mac address of the phone
+;rtp_port=10000 ; RTP port used by the phone, default = 10000. RTCP = rtp_port+1
+;rtp_method=0 ; If you don't have sound, you can try 1, 2 or 3, default = 0
+;status_method=0 ; If you don't see status text, try 1, default = 0
+;titledefault=Asterisk ; default = "TimeZone (your time zone)". 12 characters max
+;maintext0="you can insert" ; default = "Welcome", 24 characters max
+;maintext1="a custom text" ; default = the name of the device, 24 characters max
+;maintext2="(main page)" ; default = the public IP of the phone, 24 characters max
+;dateformat=1 ; 0 = month/day, 1 (default) = day/month
+;timeformat=1 ; 0 = 0:00am ; 1 (default) = 0h00, 2 = 0:00
+;contrast=8 ; define the contrast of the LCD. From 0 to 15. Default = 8
+;country=us ; country (ccTLD) for dial tone frequency. See README, default = us
+;ringvolume=2 ; ring volume : 0,1,2,3, can be overrided by Dial(), default = 2
+;ringstyle=3 ; ring style : 0 to 7, can be overrided by Dial(), default = 3
+;callhistory=1 ; 0 = disable, 1 = enable call history, default = 1
+;callerid="Customer Support" <555-234-5678>
+;context=default ; context, default="default"
+;mailbox=1234 ; Specify the mailbox number. Used by Message Waiting Indication
+;linelabel="Support" ; Softkey label for the next line=> entry, 9 char max.
+;extension=none ; Add an extension into the dialplan. Only valid in context specified previously.
+ ; none=don't add (default), ask=prompt user, line=use the line number
+;line => 100 ; Only one line by device is currently supported.
+ ; Beware ! only bookmark and softkey entries are allowed after line=>
+;bookmark=Hans C.@123 ; Use a softkey to dial 123. Name : 9 char max
+;bookmark=Mailbox@011@54 ; 54 shows a mailbox icon. See #define FAV_ICON_ for other values (32 to 63)
+;bookmark=Test@*@USTM/violet ; Display an icon if violet is connected (dynamic), only for unistim device
+;bookmark=4@Pager@54321@51 ; Display a pager icon and dial 54321 when softkey 4 is pressed
+
+;[violet]
+;device=006038abcdef
+;line => 102
diff --git a/trunk/configs/usbradio.conf.sample b/trunk/configs/usbradio.conf.sample
new file mode 100644
index 000000000..5ba9815ca
--- /dev/null
+++ b/trunk/configs/usbradio.conf.sample
@@ -0,0 +1,54 @@
+[general]
+
+; General config options, with default values shown.
+; You should use one section per device, with [general] being used
+; for the device.
+
+; debug = 0x0 ; misc debug flags, default is 0
+
+; Set the device to use for I/O
+; devicenum = 0
+; Set hardware type here
+; hdwtype=0 ; 0=limey, 1=sph
+
+; rxboost=0 ; no rx gain boost
+; rxctcssrelax=1 ; reduce talkoff from radios w/o CTCSS Tx HPF
+; rxctcssfreq=100.0 ; rx ctcss freq in floating point. must be in table
+; txctcssfreq=100.0 ; tx ctcss freq, any frequency permitted
+
+; carrierfrom=dsp ;no,usb,usbinvert,dsp,vox
+; ctcssfrom=dsp ;no,usb,dsp
+
+; rxdemod=flat ; input type from radio: no,speaker,flat
+; txprelim=yes ; output is pre-emphasised and limited
+; txtoctype=no ; no,phase,notone
+
+; txmixa=composite ;no,voice,tone,composite,auxvoice
+; txmixb=no ;no,voice,tone,composite,auxvoice
+
+; invertptt=0
+
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
+ ; USBRADIO channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The USBRADIO channel can't accept jitter,
+ ; thus an enabled jitterbuffer on the receive USBRADIO side will always
+ ; be used if the sending side can create jitter.
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usualy sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of an USBRADIO
+ ; channel. Two implementations are currenlty available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+
+
diff --git a/trunk/configs/users.conf.sample b/trunk/configs/users.conf.sample
new file mode 100644
index 000000000..2a816d7e4
--- /dev/null
+++ b/trunk/configs/users.conf.sample
@@ -0,0 +1,79 @@
+;
+; User configuration
+;
+; Creating entries in users.conf is a "shorthand" for creating individual
+; entries in each configuration file. Using users.conf is not intended to
+; provide you with as much flexibility as using the separate configuration
+; files (e.g. sip.conf, iax.conf, etc) but is intended to accelerate the
+; simple task of adding users. Note that creating individual items (e.g.
+; custom SIP peers, IAX friends, etc.) will allow you to override specific
+; parameters within this file. Parameter names here are the same as they
+; appear in the other configuration files. There is no way to change the
+; value of a parameter here for just one subsystem.
+;
+
+[general]
+;
+; Full name of a user
+;
+fullname = New User
+;
+; Starting point of allocation of extensions
+;
+userbase = 6000
+;
+; Create voicemail mailbox and use use macro-stdexten
+;
+hasvoicemail = yes
+;
+; Set voicemail mailbox 6000 password to 1234
+;
+vmsecret = 1234
+;
+; Create SIP Peer
+;
+hassip = yes
+;
+; Create IAX friend
+;
+hasiax = yes
+;
+; Create H.323 friend
+;
+;hash323 = yes
+;
+; Create manager entry
+;
+hasmanager = no
+;
+; Set permissions for manager entry (see manager.conf.sample for documentation)
+; (defaults to *all* permissions)
+;managerread = system,call,log,verbose,command,agent,user,config
+;managerwrite = system,call,log,verbose,command,agent,user,config
+;
+; Remaining options are not specific to users.conf entries but are general.
+;
+callwaiting = yes
+threewaycalling = yes
+callwaitingcallerid = yes
+transfer = yes
+canpark = yes
+cancallforward = yes
+callreturn = yes
+callgroup = 1
+pickupgroup = 1
+
+
+;[6000]
+;fullname = Joe User
+;email = joe@foo.bar
+;secret = 1234
+;zapchan = 1
+;hasvoicemail = yes
+;vmsecret = 1234
+;hassip = yes
+;hasiax = no
+;hash323 = no
+;hasmanager = no
+;callwaiting = no
+;context = international
diff --git a/trunk/configs/voicemail.conf.sample b/trunk/configs/voicemail.conf.sample
new file mode 100644
index 000000000..ce403fe8e
--- /dev/null
+++ b/trunk/configs/voicemail.conf.sample
@@ -0,0 +1,307 @@
+;
+; Voicemail Configuration
+;
+
+;
+; NOTE: Asterisk has to edit this file to change a user's password. This does
+; not currently work with the "#include <file>" directive for Asterisk
+; configuration files, nor when using realtime static configuration.
+; Do not use them with this configuration file.
+;
+
+[general]
+; Formats for writing Voicemail. Note that when using IMAP storage for
+; voicemail, only the first format specified will be used.
+;format=g723sf|wav49|wav
+format=wav49|gsm|wav
+;
+; WARNING:
+; If you change the list of formats that you record voicemail in
+; when you have mailboxes that contain messages, you _MUST_ absolutely
+; manually go through those mailboxes and convert/delete/add the
+; the message files so that they appear to have been stored using
+; your new format list. If you don't do this, very unpleasant
+; things may happen to your users while they are retrieving and
+; manipulating their voicemail.
+;
+; In other words: don't change the format list on a production system
+; unless you are _VERY_ sure that you know what you are doing and are
+; prepared for the consequences.
+;
+; Who the e-mail notification should appear to come from
+serveremail=asterisk
+;serveremail=asterisk@linux-support.net
+; Should the email contain the voicemail as an attachment
+attach=yes
+; Maximum number of messages per folder. If not specified, a default value
+; (100) is used. Maximum value for this option is 9999.
+;maxmsg=100
+; Maximum length of a voicemail message in seconds
+;maxsecs=180
+; Minimum length of a voicemail message in seconds for the message to be kept
+; The default is no minimum.
+;minsecs=3
+; Maximum length of greetings in seconds
+;maxgreet=60
+; How many milliseconds to skip forward/back when rew/ff in message playback
+skipms=3000
+; How many seconds of silence before we end the recording
+maxsilence=10
+; Silence threshold (what we consider silence: the lower, the more sensitive)
+silencethreshold=128
+; Max number of failed login attempts
+maxlogins=3
+;
+; Move heard messages to the 'Old' folder automagically. Defaults to on.
+;moveheard=yes
+;
+; User context is where entries from users.conf are registered. The
+; default value is 'default'
+;
+;userscontext=default
+;
+; If you need to have an external program, i.e. /usr/bin/myapp
+; called when a voicemail is left, delivered, or your voicemailbox
+; is checked, uncomment this.
+;externnotify=/usr/bin/myapp
+
+; If you would also like to enable SMDI notification then set smdienable to yes.
+; You will also need to make sure smdiport is set to a valid port as specified in
+; smdi.conf.
+;smdienable=yes
+;smdiport=/dev/ttyS0
+
+; If you need to have an external program, i.e. /usr/bin/myapp
+; called when a voicemail password is changed, uncomment this:
+; Note: If this is set, the password will NOT be changed in voicemail.conf
+; If you would like to also change the password in voicemail.conf, use
+; the externpassnotify option below instead.
+;externpass=/usr/bin/myapp
+;externpassnotify=/usr/bin/myapp
+; For the directory, you can override the intro file if you want
+;directoryintro=dir-intro
+; The character set for voicemail messages can be specified here
+;charset=ISO-8859-1
+; The ADSI feature descriptor number to download to
+;adsifdn=0000000F
+; The ADSI security lock code
+;adsisec=9BDBF7AC
+; The ADSI voicemail application version number.
+;adsiver=1
+; Skip the "[PBX]:" string from the message title
+;pbxskip=yes
+; Change the From: string
+;fromstring=The Asterisk PBX
+; Permit finding entries for forward/compose from the directory
+;usedirectory=yes
+; Voicemail can be stored in a database using the ODBC driver.
+; The value of odbcstorage is the database connection configured
+; in res_odbc.conf.
+;odbcstorage=asterisk
+; The default table for ODBC voicemail storage is voicemessages.
+;odbctable=voicemessages
+;
+; Change the from, body and/or subject, variables:
+; VM_NAME, VM_DUR, VM_MSGNUM, VM_MAILBOX, VM_CALLERID, VM_CIDNUM,
+; VM_CIDNAME, VM_DATE
+;
+; Note: The emailbody config row can only be up to 512 characters due to a
+; limitation in the Asterisk configuration subsystem.
+;emailsubject=[PBX]: New message ${VM_MSGNUM} in mailbox ${VM_MAILBOX}
+; The following definition is very close to the default, but the default shows
+; just the CIDNAME, if it is not null, otherwise just the CIDNUM, or "an unknown
+; caller", if they are both null.
+;emailbody=Dear ${VM_NAME}:\n\n\tjust wanted to let you know you were just left a ${VM_DUR} long message (number ${VM_MSGNUM})\nin mailbox ${VM_MAILBOX} from ${VM_CALLERID}, on ${VM_DATE}, so you might\nwant to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n
+;
+; You can also change the Pager From: string, the pager body and/or subject.
+; The above defined variables also can be used here
+;pagerfromstring=The Asterisk PBX
+;pagersubject=New VM
+;pagerbody=New ${VM_DUR} long msg in box ${VM_MAILBOX}\nfrom ${VM_CALLERID}, on ${VM_DATE}
+;
+; Set the date format on outgoing mails. Valid arguments can be found on the
+; strftime(3) man page
+;
+; Default
+emaildateformat=%A, %B %d, %Y at %r
+; 24h date format
+;emaildateformat=%A, %d %B %Y at %H:%M:%S
+;
+; You can override the default program to send e-mail if you wish, too
+;
+;mailcmd=/usr/sbin/sendmail -t
+;
+;pollmailboxes=no ; If mailboxes are changed anywhere outside of app_voicemail,
+; ; then this option must be enabled for MWI to work. This
+; ; enables polling mailboxes for changes. Normally, it will
+; ; expect that changes are only made when someone called in
+; ; to one of the voicemail applications.
+; ; Examples of situations that would require this option are
+; ; web interfaces to voicemail or an email client in the case
+; ; of using IMAP storage.
+;
+;pollfreq=30 ; If the "pollmailboxes" option is enabled, this option
+; ; sets the polling frequency. The default is once every
+; ; 30 seconds.
+; If using IMAP storage, specify whether voicemail greetings should be stored
+; via IMAP. If no, then greetings are stored as if IMAP storage were not enabled
+;imapgreetings=no
+; If imapgreetings=yes, then specify which folder to store your greetings in. If
+; you do not specify a folder, then INBOX will be used
+;greetingsfolder=INBOX
+;
+; Users may be located in different timezones, or may have different
+; message announcements for their introductory message when they enter
+; the voicemail system. Set the message and the timezone each user
+; hears here. Set the user into one of these zones with the tz= attribute
+; in the options field of the mailbox. Of course, language substitution
+; still applies here so you may have several directory trees that have
+; alternate language choices.
+;
+; Look in /usr/share/zoneinfo/ for names of timezones.
+; Look at the manual page for strftime for a quick tutorial on how the
+; variable substitution is done on the values below.
+;
+; Supported values:
+; 'filename' filename of a soundfile (single ticks around the filename
+; required)
+; ${VAR} variable substitution
+; A or a Day of week (Saturday, Sunday, ...)
+; B or b or h Month name (January, February, ...)
+; d or e numeric day of month (first, second, ..., thirty-first)
+; Y Year
+; I or l Hour, 12 hour clock
+; H Hour, 24 hour clock (single digit hours preceded by "oh")
+; k Hour, 24 hour clock (single digit hours NOT preceded by "oh")
+; M Minute, with 00 pronounced as "o'clock"
+; N Minute, with 00 pronounced as "hundred" (US military time)
+; P or p AM or PM
+; Q "today", "yesterday" or ABdY
+; (*note: not standard strftime value)
+; q "" (for today), "yesterday", weekday, or ABdY
+; (*note: not standard strftime value)
+; R 24 hour time, including minute
+;
+;
+;
+; Each mailbox is listed in the form <mailbox>=<password>,<name>,<email>,<pager_email>,<options>
+; if the e-mail is specified, a message will be sent when a message is
+; received, to the given mailbox. If pager is specified, a message will be
+; sent there as well. If the password is prefixed by '-', then it is
+; considered to be unchangeable.
+;
+; Advanced options example is extension 4069
+; NOTE: All options can be expressed globally in the general section, and
+; overridden in the per-mailbox settings, unless listed otherwise.
+;
+; tz=central ; Timezone from zonemessages below. Irrelevant if envelope=no.
+; attach=yes ; Attach the voicemail to the notification email *NOT* the pager email
+; attachfmt=wav49 ; Which format to attach to the email. Normally this is the
+ ; first format specified in the format parameter above, but this
+ ; option lets you customize the format sent to particular mailboxes.
+ ; Useful if Windows users want wav49, but Linux users want gsm.
+ ; [per-mailbox only]
+; saycid=yes ; Say the caller id information before the message. If not described,
+ ; or set to no, it will be in the envelope
+; cidinternalcontexts=intern ; Internal Context for Name Playback instead of
+ ; extension digits when saying caller id.
+; sayduration=no ; Turn on/off the duration information before the message. [ON by default]
+; saydurationm=2 ; Specify the minimum duration to say. Default is 2 minutes
+; dialout=fromvm ; Context to dial out from [option 4 from mailbox's advanced menu].
+ ; If not specified, option 4 will not be listed and dialing out
+ ; from within VoiceMailMain() will not be permitted.
+sendvoicemail=yes ; Allow the user to compose and send a voicemail while inside
+ ; VoiceMailMain() [option 5 from mailbox's advanced menu].
+ ; If set to 'no', option 5 will not be listed.
+; searchcontexts=yes ; Current default behavior is to search only the default context
+ ; if one is not specified. The older behavior was to search all contexts.
+ ; This option restores the old behavior [DEFAULT=no]
+; callback=fromvm ; Context to call back from
+ ; if not listed, calling the sender back will not be permitted
+; review=yes ; Allow sender to review/rerecord their message before saving it [OFF by default
+; operator=yes ; Allow sender to hit 0 before/after/during leaving a voicemail to
+ ; reach an operator [OFF by default]
+; envelope=no ; Turn on/off envelope playback before message playback. [ON by default]
+ ; This does NOT affect option 3,3 from the advanced options menu
+; delete=yes ; After notification, the voicemail is deleted from the server. [per-mailbox only]
+ ; This is intended for use with users who wish to receive their
+ ; voicemail ONLY by email. Note: "deletevoicemail" is provided as an
+ ; equivalent option for Realtime configuration.
+; volgain=0.0 ; Emails bearing the voicemail may arrive in a volume too
+ ; quiet to be heard. This parameter allows you to specify how
+ ; much gain to add to the message when sending a voicemail.
+ ; NOTE: sox must be installed for this option to work.
+; nextaftercmd=yes ; Skips to the next message after hitting 7 or 9 to delete/save current message.
+ ; [global option only at this time]
+; forcename=yes ; Forces a new user to record their name. A new user is
+ ; determined by the password being the same as
+ ; the mailbox number. The default is "no".
+; forcegreetings=no ; This is the same as forcename, except for recording
+ ; greetings. The default is "no".
+; hidefromdir=yes ; Hide this mailbox from the directory produced by app_directory
+ ; The default is "no".
+; tempgreetwarn=yes ; Remind the user that their temporary greeting is set
+; vm-password=custom_sound
+ ; Customize which sound file is used instead of the default
+ ; prompt that says: "password"
+; vm-newpassword=custom_sound
+ ; Customize which sound file is used instead of the default
+ ; prompt that says: "Please enter your new password followed by
+ ; the pound key."
+; vm-passchanged=custom_sound
+ ; Customize which sound file is used instead of the default
+ ; prompt that says: "Your password has been changed."
+; vm-reenterpassword=custom_sound
+ ; Customize which sound file is used instead of the default
+ ; prompt that says: "Please re-enter your password followed by
+ ; the pound key"
+; vm-mismatch=custom_sound
+ ; Customize which sound file is used instead of the default
+ ; prompt that says: "The passwords you entered and re-entered
+ ; did not match. Please try again."
+; listen-control-forward-key=# ; Customize the key that fast-forwards message playback
+; listen-control-reverse-key=* ; Customize the key that rewinds message playback
+; listen-control-pause-key=0 ; Customize the key that pauses/unpauses message playback
+; listen-control-restart-key=2 ; Customize the key that restarts message playback
+; listen-control-stop-key=13456789 ; Customize the keys that interrupt message playback, probably all keys not set above
+
+; Maximum number of messages allowed in the 'Deleted' folder. If set to 0
+; or no then no deleted messages will be moved. If non-zero (max 9999) then up
+; to this number of messages will be automagically saved when they are
+; 'deleted' on a FIFO basis.
+; defaults to being off
+; backupdeleted=100
+
+
+[zonemessages]
+eastern=America/New_York|'vm-received' Q 'digits/at' IMp
+central=America/Chicago|'vm-received' Q 'digits/at' IMp
+central24=America/Chicago|'vm-received' q 'digits/at' H N 'hours'
+military=Zulu|'vm-received' q 'digits/at' H N 'hours' 'phonetic/z_p'
+european=Europe/Copenhagen|'vm-received' a d b 'digits/at' HM
+
+
+
+[default]
+; Define maximum number of messages per folder for a particular context.
+;maxmsg=50
+
+1234 => 4242,Example Mailbox,root@localhost
+;4200 => 9855,Mark Spencer,markster@linux-support.net,mypager@digium.com,attach=no|serveremail=myaddy@digium.com|tz=central|maxmsg=10
+;4300 => 3456,Ben Rigas,ben@american-computer.net
+;4310 => -5432,Sales,sales@marko.net
+;4069 => 6522,Matt Brooks,matt@marko.net,,|tz=central|attach=yes|saycid=yes|dialout=fromvm|callback=fromvm|review=yes|operator=yes|envelope=yes|moveheard=yes|sayduration=yes|saydurationm=1
+;4073 => 1099,Bianca Paige,bianca@biancapaige.com,,delete=1
+;4110 => 3443,Rob Flynn,rflynn@blueridge.net
+;4235 => 1234,Jim Holmes,jim@astricon.ips,,Tz=european
+
+
+;
+; Mailboxes may be organized into multiple contexts for
+; voicemail virtualhosting
+;
+
+[other]
+;The intro can be customized on a per-context basis
+;directoryintro=dir-company2
+1234 => 5678,Company2 User,root@localhost
diff --git a/trunk/configs/vpb.conf.sample b/trunk/configs/vpb.conf.sample
new file mode 100644
index 000000000..4a9b0b36a
--- /dev/null
+++ b/trunk/configs/vpb.conf.sample
@@ -0,0 +1,108 @@
+;
+; V6PCI/V12PCI config file for VoiceTronix Hardware
+;
+; Options for [general] section
+;
+; type = v12pci|v6pci|v4pci
+; cards = number of cards
+; To use Asterisk indication tones
+; indication = 1
+; none,-24db,-18db only for use with OpenLine4
+; ecsuppthres = 0|2048|4096
+; Inter Digit Delay timeout for when collecting DTMF tones for dialling
+; from a Station port, in ms
+; dtmfidd = 3000
+; To use Asterisk DTMF detection
+; ast-dtmf-det=1
+; Used with ast-dtmf-det
+; relaxdtmf=1
+; When a native bridge occurs between 2 vpb channels, it will only break
+; the connection for '#' and '*'
+; break-for-dtmf=no
+; Set the maximum period between received rings, default 4000ms
+; timer_period_ring=4000
+;
+; Options for [interface] section
+; board = board_number (1, 2, 3, ...)
+; channel = channel_number (1,2,3...)
+; mode = fxo|immediate|dialtone -- for type of line and line handling
+; context = starting context
+; echocancel = on|off (on by default of v4pci, off by default for others)
+; callerid = on|off|v23|bell (on => to collect caller ID if available between 1st/2nd rings using vpb functions)
+; (v23|bell => collect caller ID using asterisk functions)
+; Or for use with FXS channels a '"name" <location>' format can be used to set the channels CID
+;
+; UseLoopDrop = 0|1 (enables the use of Loop Drop detection, on by default in
+; some cases spurious loop-drops can cause unexpected
+; hangup detection)
+;
+; Gain settings
+; txgain => Transmit Software Gain (-12 => 12)
+; rxgain => Receive Software Gain (-12 => 12)
+; txhwgain => Transmit hardware gain (-12 => 12)
+; rxhwgain => Receive Hardware gain (-12 => 12)
+;
+; These are advanced settings and only mentioned for completeness.
+; bal1 => Hybrid balance codec register 1
+; bal2 => Hybrid balance codec register 2
+; bal3 => Hybrid balance codec register 3
+;
+; Dial translations - if you want a pause or hook-flash in your dial string
+; you can use "w" for pause (wait) or "f" for "hook-flash", eg:
+; exten => _9XXX,1,Dial(vpb/g1/ww${EXTEN:${TRUNKMSD}})
+;
+;
+
+[general]
+type = v12pci
+;type = v6pci
+;type = v4pci
+cards = 1
+
+[interfaces]
+
+board = 0
+echocancel = on
+
+
+; For OpenLine4 cards
+;context = demo
+;mode = fxo
+;channel = 0
+;channel = 1
+;channel = 2
+;channel = 3
+
+; For OpenSwith12 with jumpers at factory default
+context = demo
+mode = fxo
+channel = 8
+channel = 9
+channel = 10
+channel = 11
+
+context = local
+mode = dialtone
+channel = 0
+channel = 1
+channel = 2
+channel = 3
+channel = 4
+channel = 5
+channel = 6
+channel = 7
+;
+; For OpenSwitch6
+; Note that V6PCI channel numbers start at 7!
+;context = demo
+;mode = fxo
+;channel = 6
+;channel = 7
+
+;mode = dialtone
+;channel = 8
+;channel = 9
+;channel = 10
+;channel = 11
+
+
diff --git a/trunk/configs/zapata.conf.sample b/trunk/configs/zapata.conf.sample
new file mode 100644
index 000000000..b2146b6b5
--- /dev/null
+++ b/trunk/configs/zapata.conf.sample
@@ -0,0 +1,909 @@
+;
+; Zapata telephony interface
+;
+; Configuration file
+;
+; You need to restart Asterisk to re-configure the Zap channel
+; CLI> reload chan_zap.so
+; will reload the configuration file,
+; but not all configuration options are
+; re-configured during a reload (signalling, as well as
+; PRI and SS7-related settings cannot be changed on a
+; reload.
+;
+; This file documents many configuration variables. Normally unless you
+; know what a variable means or that it should be changed, there's no
+; reason to unrem lines.
+;
+; remmed-out examples below (those lines that begin with a ';' but no
+; space afterwards) typically show a value that is not the defauult value,
+; but would make sense under cetain circumstances. The default values
+; are usually sane. Thus you should typically not touch them unless you
+; know what they mean or you know you should change them.
+
+
+[trunkgroups]
+;
+; Trunk groups are used for NFAS or GR-303 connections.
+;
+; Group: Defines a trunk group.
+; trunkgroup => <trunkgroup>,<dchannel>[,<backup1>...]
+;
+; trunkgroup is the numerical trunk group to create
+; dchannel is the zap channel which will have the
+; d-channel for the trunk.
+; backup1 is an optional list of backup d-channels.
+;
+;trunkgroup => 1,24,48
+;trunkgroup => 1,24
+;
+; Spanmap: Associates a span with a trunk group
+; spanmap => <zapspan>,<trunkgroup>[,<logicalspan>]
+;
+; zapspan is the zap span number to associate
+; trunkgroup is the trunkgroup (specified above) for the mapping
+; logicalspan is the logical span number within the trunk group to use.
+; if unspecified, no logical span number is used.
+;
+;spanmap => 1,1,1
+;spanmap => 2,1,2
+;spanmap => 3,1,3
+;spanmap => 4,1,4
+
+[channels]
+;
+; Default language
+;
+;language=en
+;
+; Context for calls. Defaults to 'default'
+;
+;context=incoming
+;
+; Switchtype: Only used for PRI.
+;
+; national: National ISDN 2 (default)
+; dms100: Nortel DMS100
+; 4ess: AT&T 4ESS
+; 5ess: Lucent 5ESS
+; euroisdn: EuroISDN (common in Europe)
+; ni1: Old National ISDN 1
+; qsig: Q.SIG
+;
+;switchtype=euroisdn
+;
+; Some switches (AT&T especially) require network specific facility IE
+; supported values are currently 'none', 'sdn', 'megacom', 'tollfreemegacom', 'accunet'
+;
+; nsf cannot be changed on a reload.
+;
+;nsf=none
+;
+; PRI Dialplan: Only RARELY used for PRI.
+; PRI Local Dialplan: Only RARELY used for PRI (sets the calling number's
+; numbering plan)
+; pridialplan and prilocaldialplan cannot be changed on a reload.
+;
+; unknown: Unknown
+; private: Private ISDN
+; local: Local ISDN
+; national: National ISDN
+; international: International ISDN
+; dynamic: Dynamically selects the appropriate dialplan
+; redundant: Same as dynamic, except that the underlying number is not
+; changed (not common)
+;
+;pridialplan=national
+;prilocaldialplan=national
+;
+; pridialplan may be also set at dialtime, by prefixing the dialled number with
+; one of the following letters:
+; U - Unknown
+; I - International
+; N - National
+; L - Local (Net Specific)
+; S - Subscriber
+; V - Abbreviated
+; R - Reserved (should probably never be used but is included for completeness)
+;
+; Additionally, you may also set the following NPI bits (also by prefixing the
+; dialled string with one of the following letters):
+; u - Unknown
+; e - E.163/E.164 (ISDN/telephony)
+; x - X.121 (Data)
+; f - F.69 (Telex)
+; n - National
+; p - Private
+; r - Reserved (should probably never be used but is included for completeness)
+;
+; You may also set the prilocaldialplan in the same way, but by prefixing the
+; Caller*ID Number, rather than the dialled number. Please note that telcos
+; which require this kind of additional manipulation of the TON/NPI are *rare*.
+; Most telco PRIs will work fine simply by setting pridialplan to unknown or
+; dynamic.
+;
+;
+; PRI caller ID prefixes based on the given TON/NPI (dialplan)
+; This is especially needed for EuroISDN E1-PRIs
+;
+; None of the prefix settings can be changed on reload.
+;
+; sample 1 for Germany
+;internationalprefix = 00
+;nationalprefix = 0
+;localprefix = 0711
+;privateprefix = 07115678
+;unknownprefix =
+;
+; sample 2 for Germany
+;internationalprefix = +
+;nationalprefix = +49
+;localprefix = +49711
+;privateprefix = +497115678
+;unknownprefix =
+;
+; PRI resetinterval: sets the time in seconds between restart of unused
+; B channels; defaults to 'never'.
+;
+;resetinterval = 3600
+;
+; Overlap dialing mode (sending overlap digits)
+; Cannot be changed on a reload.
+;
+;overlapdial=yes
+;
+; PRI Out of band indications.
+; Enable this to report Busy and Congestion on a PRI using out-of-band
+; notification. Inband indication, as used by Asterisk doesn't seem to work
+; with all telcos.
+;
+; outofband: Signal Busy/Congestion out of band with RELEASE/DISCONNECT
+; inband: Signal Busy/Congestion using in-band tones (default)
+;
+; priindication cannot be changed on a reload.
+;
+;priindication = outofband
+;
+; If you need to override the existing channels selection routine and force all
+; PRI channels to be marked as exclusively selected, set this to yes.
+;
+; priexclusive cannot be changed on a reload.
+;
+;priexclusive = yes
+;
+; ISDN Timers
+; All of the ISDN timers and counters that are used are configurable. Specify
+; the timer name, and its value (in ms for timers).
+; K: Layer 2 max number of outstanding unacknowledged I frames (default 7)
+; N200: Layer 2 max number of retransmissions of a frame (default 3)
+; T200: Layer 2 max time before retransmission of a frame (default 1000 ms)
+; T203: Layer 2 max time without frames being exchanged (default 10000 ms)
+; T305: Wait for DISCONNECT acknowledge (default 30000 ms)
+; T308: Wait for RELEASE acknowledge (default 4000 ms)
+; T309: Maintain active calls on Layer 2 disconnection (default -1,
+; Asterisk clears calls)
+; EuroISDN: 6000 to 12000 ms, according to (N200 + 1) x T200 + 2s
+; May vary in other ISDN standards (Q.931 1993 : 90000 ms)
+; T313: Wait for CONNECT acknowledge, CPE side only (default 3000 ms)
+;
+;pritimer => t200,1000
+;pritimer => t313,4000
+;
+; To enable transmission of facility-based ISDN supplementary services (such
+; as caller name from CPE over facility), enable this option.
+; Cannot be changed on a reload.
+;
+;facilityenable = yes
+;
+; pritimer cannot be changed on a reload.
+;
+; Signalling method. The default is "auto". Valid values:
+; auto: Use the current value from Zaptel.
+; em: E & M
+; em_e1: E & M E1
+; em_w: E & M Wink
+; featd: Feature Group D (The fake, Adtran style, DTMF)
+; featdmf: Feature Group D (The real thing, MF (domestic, US))
+; featdmf_ta: Feature Group D (The real thing, MF (domestic, US)) through
+; a Tandem Access point
+; featb: Feature Group B (MF (domestic, US))
+; fgccama Feature Group C-CAMA (DP DNIS, MF ANI)
+; fgccamamf Feature Group C-CAMA MF (MF DNIS, MF ANI)
+; fxs_ls: FXS (Loop Start)
+; fxs_gs: FXS (Ground Start)
+; fxs_ks: FXS (Kewl Start)
+; fxo_ls: FXO (Loop Start)
+; fxo_gs: FXO (Ground Start)
+; fxo_ks: FXO (Kewl Start)
+; pri_cpe: PRI signalling, CPE side
+; pri_net: PRI signalling, Network side
+; gr303fxoks_net: GR-303 Signalling, FXO Loopstart, Network side
+; gr303fxsks_cpe: GR-303 Signalling, FXS Loopstart, CPE side
+; sf: SF (Inband Tone) Signalling
+; sf_w: SF Wink
+; sf_featd: SF Feature Group D (The fake, Adtran style, DTMF)
+; sf_featdmf: SF Feature Group D (The real thing, MF (domestic, US))
+; sf_featb: SF Feature Group B (MF (domestic, US))
+; e911: E911 (MF) style signalling
+; ss7: Signalling System 7
+;
+; The following are used for Radio interfaces:
+; fxs_rx: Receive audio/COR on an FXS kewlstart interface (FXO at the
+; channel bank)
+; fxs_tx: Transmit audio/PTT on an FXS loopstart interface (FXO at the
+; channel bank)
+; fxo_rx: Receive audio/COR on an FXO loopstart interface (FXS at the
+; channel bank)
+; fxo_tx: Transmit audio/PTT on an FXO groundstart interface (FXS at
+; the channel bank)
+; em_rx: Receive audio/COR on an E&M interface (1-way)
+; em_tx: Transmit audio/PTT on an E&M interface (1-way)
+; em_txrx: Receive audio/COR AND Transmit audio/PTT on an E&M interface
+; (2-way)
+; em_rxtx: Same as em_txrx (for our dyslexic friends)
+; sf_rx: Receive audio/COR on an SF interface (1-way)
+; sf_tx: Transmit audio/PTT on an SF interface (1-way)
+; sf_txrx: Receive audio/COR AND Transmit audio/PTT on an SF interface
+; (2-way)
+; sf_rxtx: Same as sf_txrx (for our dyslexic friends)
+; ss7: Signalling System 7
+;
+; signalling of a channel can not be changed on a reload.
+;
+;signalling=fxo_ls
+;
+; If you have an outbound signalling format that is different from format
+; specified above (but compatible), you can specify outbound signalling format,
+; (see below). The 'signalling' format specified will be the inbound signalling
+; format. If you only specify 'signalling', then it will be the format for
+; both inbound and outbound.
+;
+; outsignalling can only be one of:
+; em, em_e1, em_w, sf, sf_w, sf_featd, sf_featdmf, sf_featb, featd,
+; featdmf, featdmf_ta, e911, fgccama, fgccamamf
+;
+; outsignalling cannot be changed on a reload.
+;
+;signalling=featdmf
+;
+;outsignalling=featb
+;
+; For Feature Group D Tandem access, to set the default CIC and OZZ use these
+; parameters (Will not be updated on reload):
+;
+;defaultozz=0000
+;defaultcic=303
+;
+; A variety of timing parameters can be specified as well
+; The default values for those are "-1", which is to use the
+; compile-time defaults of the Zaptel kernel modules. The timing
+; parameters, (with the standard default from Zaptel):
+;
+; prewink: Pre-wink time (default 50ms)
+; preflash: Pre-flash time (default 50ms)
+; wink: Wink time (default 150ms)
+; flash: Flash time (default 750ms)
+; start: Start time (default 1500ms)
+; rxwink: Receiver wink time (default 300ms)
+; rxflash: Receiver flashtime (default 1250ms)
+; debounce: Debounce timing (default 600ms)
+;
+; None of them will update on a reload.
+;
+; How long generated tones (DTMF and MF) will be played on the channel
+; (in milliseconds).
+;
+; This is a global, rather than a per-channel setting. It will not be
+; updated on a reload.
+;
+;toneduration=100
+;
+; Whether or not to do distinctive ring detection on FXO lines:
+;
+;usedistinctiveringdetection=yes
+;
+; enable dring detection after caller ID for those countries like Australia
+; where the ring cadence is changed *after* the caller ID spill:
+;
+;distinctiveringaftercid=yes
+;
+; Whether or not to use caller ID:
+;
+usecallerid=yes
+;
+; Hide the name part and leave just the number part of the caller ID
+; string. Only applies to PRI channels.
+;hidecalleridname=yes
+;
+; Type of caller ID signalling in use
+; bell = bell202 as used in US (default)
+; v23 = v23 as used in the UK
+; v23_jp = v23 as used in Japan
+; dtmf = DTMF as used in Denmark, Sweden and Netherlands
+; smdi = Use SMDI for caller ID. Requires SMDI to be enabled (usesmdi).
+;
+;cidsignalling=v23
+;
+; What signals the start of caller ID
+; ring = a ring signals the start (default)
+; polarity = polarity reversal signals the start
+; polarity_IN = polarity reversal signals the start, for India,
+; for dtmf dialtone detection; using DTMF.
+; (see doc/India-CID.txt)
+;
+;cidstart=polarity
+;
+; Whether or not to hide outgoing caller ID (Override with *67 or *82)
+; (If your dialplan doesn't catch it)
+;
+;hidecallerid=yes
+;
+; The following option enables receiving MWI on FXO lines. The default
+; value is no. When this is enabled, and MWI notification indicates on or off,
+; the script specified by the mwimonitornotify option is executed. Also, an
+; internal Asterisk MWI event will be generated so that any other part of
+; Asterisk that cares about MWI state changes will get notified, just as if
+; the state change came from app_voicemail. The energy level that must be seen
+; before starting the MWI detection process can be set with 'mwilevel'.
+;
+;mwimonitor=no
+;mwilevel=512
+;
+; This option is used in conjunction with mwimonitor. This will get executed
+; when incoming MWI state changes. The script is passed 2 arguments. The
+; first is the corresponding mailbox, and the second is 1 or 0, indicating if
+; there are messages waiting or not.
+;
+;mwimonitornotify=/usr/local/bin/zapnotify.sh
+;
+; Whether or not to enable call waiting on internal extensions
+; With this set to 'yes', busy extensions will hear the call-waiting
+; tone, and can use hook-flash to switch between callers. The Dial()
+; app will not return the "BUSY" result for extensions.
+;
+callwaiting=yes
+;
+; Whether or not restrict outgoing caller ID (will be sent as ANI only, not
+; available for the user)
+; Mostly use with FXS ports
+;
+;restrictcid=no
+;
+; Whether or not use the caller ID presentation for the outgoing call that the
+; calling switch is sending.
+; See README.callingpres. FIXME: file no longer exists.
+;
+usecallingpres=yes
+;
+; Some countries (UK) have ring tones with different ring tones (ring-ring),
+; which means the caller ID needs to be set later on, and not just after
+; the first ring, as per the default (1).
+;
+;sendcalleridafter = 2
+;
+;
+; Support caller ID on Call Waiting
+;
+callwaitingcallerid=yes
+;
+; Support three-way calling
+;
+threewaycalling=yes
+;
+; Support flash-hook call transfer (requires three way calling)
+; Also enables call parking (overrides the 'canpark' parameter)
+;
+transfer=yes
+;
+; Allow call parking
+; ('canpark=no' is overridden by 'transfer=yes')
+;
+canpark=yes
+;
+; Support call forward variable
+;
+cancallforward=yes
+;
+; Whether or not to support Call Return (*69, if your dialplan doesn't
+; catch this first)
+;
+callreturn=yes
+;
+; Stutter dialtone support: If a mailbox is specified without a voicemail
+; context, then when voicemail is received in a mailbox in the default
+; voicemail context in voicemail.conf, taking the phone off hook will cause a
+; stutter dialtone instead of a normal one.
+;
+; If a mailbox is specified *with* a voicemail context, the same will result
+; if voicemail received in mailbox in the specified voicemail context.
+;
+; for default voicemail context, the example below is fine:
+;
+;mailbox=1234
+;
+; for any other voicemail context, the following will produce the stutter tone:
+;
+;mailbox=1234@context
+;
+; Enable echo cancellation
+; Use either "yes", "no", or a power of two from 32 to 256 if you wish to
+; actually set the number of taps of cancellation.
+;
+; Note that when setting the number of taps, the number 256 does not translate
+; to 256 ms of echo cancellation. echocancel=256 means 256 / 8 = 32 ms.
+;
+; Note that if any of your Zaptel cards have hardware echo cancellers,
+; then this setting only turns them on and off; numeric settings will
+; be treated as "yes". There are no special settings required for
+; hardware echo cancellers; when present and enabled in their kernel
+; modules, they take precedence over the software echo canceller compiled
+; into Zaptel automatically.
+;
+;
+echocancel=yes
+;
+; As of Zaptel 1.4.8, some Zaptel echo cancellers (software and hardware)
+; support adjustable parameters; these parameters can be supplied as
+; additional options to the 'echocancel' setting. Note that Asterisk
+; does not attempt to validate the parameters or their values, so if you
+; supply an invalid parameter you will not know the specific reason it
+; failed without checking the kernel message log for the error(s)
+; put there by Zaptel.
+;
+;echocancel=128,param1=32,param2=0,param3=14
+;
+; Generally, it is not necessary (and in fact undesirable) to echo cancel when
+; the circuit path is entirely TDM. You may, however, change this behavior
+; by enabling the echo canceller during pure TDM bridging below.
+;
+echocancelwhenbridged=yes
+;
+; In some cases, the echo canceller doesn't train quickly enough and there
+; is echo at the beginning of the call. Enabling echo training will cause
+; Zaptel to briefly mute the channel, send an impulse, and use the impulse
+; response to pre-train the echo canceller so it can start out with a much
+; closer idea of the actual echo. Value may be "yes", "no", or a number of
+; milliseconds to delay before training (default = 400)
+;
+; WARNING: In some cases this option can make echo worse! If you are
+; trying to debug an echo problem, it is worth checking to see if your echo
+; is better with the option set to yes or no. Use whatever setting gives
+; the best results.
+;
+; Note that these parameters do not apply to hardware echo cancellers.
+;
+;echotraining=yes
+;echotraining=800
+;
+; If you are having trouble with DTMF detection, you can relax the DTMF
+; detection parameters. Relaxing them may make the DTMF detector more likely
+; to have "talkoff" where DTMF is detected when it shouldn't be.
+;
+;relaxdtmf=yes
+;
+; You may also set the default receive and transmit gains (in dB)
+;
+; Gain Settings: increasing / decreasing the volume level on a channel.
+; The values are in db (decibells). A positive number
+; increases the volume level on a channel, and a
+; negavive value decreases volume level.
+;
+; There are several independent gain settings:
+; rxgain: gain for the rx (receive - into Asterisk) channel. Default: 0.0
+; txgain: gain for the tx (transmit - out of Asterisk Asterisk) channel.
+; Default: 0.0
+; cid_rxgain: set the gain just for the caller ID sounds Asterisk
+; emits. Default: 5.0 .
+
+;rxgain=2.0
+;txgain=3.0
+;
+; Logical groups can be assigned to allow outgoing roll-over. Groups range
+; from 0 to 63, and multiple groups can be specified. By default the
+; channel is not a member of any group.
+;
+; Note that an explicit empty value for 'group' is invalid, and will not
+; override a previous non-empty one. The same applies to callgroup and
+; pickupgroup as well.
+;
+group=1
+;
+; Ring groups (a.k.a. call groups) and pickup groups. If a phone is ringing
+; and it is a member of a group which is one of your pickup groups, then
+; you can answer it by picking up and dialing *8#. For simple offices, just
+; make these both the same. Groups range from 0 to 63.
+;
+callgroup=1
+pickupgroup=1
+
+; Channel variable to be set for all calls from this channel
+;setvar=CHANNEL=42
+
+;
+; Specify whether the channel should be answered immediately or if the simple
+; switch should provide dialtone, read digits, etc.
+; Note: If immediate=yes the dialplan execution will always start at extension
+; 's' priority 1 regardless of the dialed number!
+;
+;immediate=yes
+;
+; Specify whether flash-hook transfers to 'busy' channels should complete or
+; return to the caller performing the transfer (default is yes).
+;
+;transfertobusy=no
+;
+; caller ID can be set to "asreceived" or a specific number if you want to
+; override it. Note that "asreceived" only applies to trunk interfaces.
+; fullname sets just the
+;
+; fullname: sets just the name part.
+; cid_number: sets just the number part:
+;
+;callerid = 123456
+;
+;callerid = My Name <2564286000>
+; Which can also be written as:
+;cid_number = 2564286000
+;fullname = My Name
+;
+;callerid = asreceived
+;
+; should we use the caller ID from incoming call on zap transfer?
+;
+;useincomingcalleridonzaptransfer = yes
+;
+; AMA flags affects the recording of Call Detail Records. If specified
+; it may be 'default', 'omit', 'billing', or 'documentation'.
+;
+;amaflags=default
+;
+; Channels may be associated with an account code to ease
+; billing
+;
+;accountcode=lss0101
+;
+; ADSI (Analog Display Services Interface) can be enabled on a per-channel
+; basis if you have (or may have) ADSI compatible CPE equipment
+;
+;adsi=yes
+;
+; SMDI (Simplified Message Desk Interface) can be enabled on a per-channel
+; basis if you would like that channel to behave like an SMDI message desk.
+; The SMDI port specified should have already been defined in smdi.conf. The
+; default port is /dev/ttyS0.
+;
+;usesmdi=yes
+;smdiport=/dev/ttyS0
+;
+; On trunk interfaces (FXS) and E&M interfaces (E&M, Wink, Feature Group D
+; etc, it can be useful to perform busy detection either in an effort to
+; detect hangup or for detecting busies. This enables listening for
+; the beep-beep busy pattern.
+;
+;busydetect=yes
+;
+; If busydetect is enabled, it is also possible to specify how many busy tones
+; to wait for before hanging up. The default is 3, but it might be
+; safer to set to 6 or even 8. Mind that the higher the number, the more
+; time that will be needed to hangup a channel, but lowers the probability
+; that you will get random hangups.
+;
+;busycount=6
+;
+; If busydetect is enabled, it is also possible to specify the cadence of your
+; busy signal. In many countries, it is 500msec on, 500msec off. Without
+; busypattern specified, we'll accept any regular sound-silence pattern that
+; repeats <busycount> times as a busy signal. If you specify busypattern,
+; then we'll further check the length of the sound (tone) and silence, which
+; will further reduce the chance of a false positive.
+;
+;busypattern=500,500
+;
+; NOTE: In make menuselect, you'll find further options to tweak the busy
+; detector. If your country has a busy tone with the same length tone and
+; silence (as many countries do), consider enabling the
+; BUSYDETECT_COMPARE_TONE_AND_SILENCE option.
+;
+; To further detect which hangup tone your telco provider is sending, it is
+; useful to use the ztmonitor utility to record the audio that main/dsp.c
+; is receiving after the caller hangs up.
+;
+; Use a polarity reversal to mark when a outgoing call is answered by the
+; remote party.
+;
+;answeronpolarityswitch=yes
+;
+; In some countries, a polarity reversal is used to signal the disconnect of a
+; phone line. If the hanguponpolarityswitch option is selected, the call will
+; be considered "hung up" on a polarity reversal.
+;
+;hanguponpolarityswitch=yes
+;
+; polarityonanswerdelay: minimal time period (ms) between the answer
+; polarity switch and hangup polarity switch.
+; (default: 600ms)
+;
+; On trunk interfaces (FXS) it can be useful to attempt to follow the progress
+; of a call through RINGING, BUSY, and ANSWERING. If turned on, call
+; progress attempts to determine answer, busy, and ringing on phone lines.
+; This feature is HIGHLY EXPERIMENTAL and can easily detect false answers,
+; so don't count on it being very accurate.
+;
+; Few zones are supported at the time of this writing, but may be selected
+; with "progzone".
+;
+; progzone also affects the pattern used for buzydetect (unless
+; busypattern is set explicitly). The possible values are:
+; us (default)
+; ca (alias for 'us')
+; cr (Costa Rica)
+; br (Brazil, alias for 'cr')
+; uk
+;
+; This feature can also easily detect false hangups. The symptoms of this is
+; being disconnected in the middle of a call for no reason.
+;
+;callprogress=yes
+;progzone=uk
+;
+; Set the tonezone. Equivalent of the defaultzone settings in
+; /etc/zaptel.conf . This sets the tone zone by number.
+; Note that you'd still need to load tonezones (loadzone in zaptel.conf).
+; The default is -1: not to set anything.
+;tonezone = 0 ; 0 is US
+;
+; FXO (FXS signalled) devices must have a timeout to determine if there was a
+; hangup before the line was answered. This value can be tweaked to shorten
+; how long it takes before Zap considers a non-ringing line to have hungup.
+;
+; ringtimeout will not update on a reload.
+;
+;ringtimeout=8000
+;
+; For FXO (FXS signalled) devices, whether to use pulse dial instead of DTMF
+; Pulse digits from phones (FXS devices, FXO signalling) are always
+; detected.
+;
+;pulsedial=yes
+;
+; For fax detection, uncomment one of the following lines. The default is *OFF*
+;
+;faxdetect=both
+;faxdetect=incoming
+;faxdetect=outgoing
+;faxdetect=no
+;
+; This option specifies a preference for which music on hold class this channel
+; should listen to when put on hold if the music class has not been set on the
+; channel with Set(CHANNEL(musicclass)=whatever) in the dialplan, and the peer
+; channel putting this one on hold did not suggest a music class.
+;
+; If this option is set to "passthrough", then the hold message will always be
+; passed through as signalling instead of generating hold music locally. This
+; setting is only valid when used on a channel that uses digital signalling.
+;
+;mohinterpret=default
+;
+; This option specifies which music on hold class to suggest to the peer channel
+; when this channel places the peer on hold.
+;
+;mohsuggest=default
+;
+; PRI channels can have an idle extension and a minunused number. So long as
+; at least "minunused" channels are idle, chan_zap will try to call "idledial"
+; on them, and then dump them into the PBX in the "idleext" extension (which
+; is of the form exten@context). When channels are needed the "idle" calls
+; are disconnected (so long as there are at least "minidle" calls still
+; running, of course) to make more channels available. The primary use of
+; this is to create a dynamic service, where idle channels are bundled through
+; multilink PPP, thus more efficiently utilizing combined voice/data services
+; than conventional fixed mappings/muxings.
+;
+; Those settings cannot be changed on reload.
+;
+;idledial=6999
+;idleext=6999@dialout
+;minunused=2
+;minidle=1
+;
+; Configure jitter buffers in Zapata (each one is 20ms, default is 4)
+; This is set globally, rather than per-channel.
+;
+;jitterbuffers=4
+;
+;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
+ ; ZAP channel. Defaults to "no". An enabled jitterbuffer will
+ ; be used only if the sending side can create and the receiving
+ ; side can not accept jitter. The ZAP channel can't accept jitter,
+ ; thus an enabled jitterbuffer on the receive ZAP side will always
+ ; be used if the sending side can create jitter.
+
+; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds.
+
+; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
+ ; resynchronized. Useful to improve the quality of the voice, with
+ ; big jumps in/broken timestamps, usually sent from exotic devices
+ ; and programs. Defaults to 1000.
+
+; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a ZAP
+ ; channel. Two implementations are currently available - "fixed"
+ ; (with size always equals to jbmax-size) and "adaptive" (with
+ ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no".
+;-----------------------------------------------------------------------------------
+;
+; You can define your own custom ring cadences here. You can define up to 8
+; pairs. If the silence is negative, it indicates where the caller ID spill is
+; to be placed. Also, if you define any custom cadences, the default cadences
+; will be turned off.
+;
+; This setting is global, rather than per-channel. It will not update on
+; a reload.
+;
+; Syntax is: cadence=ring,silence[,ring,silence[...]]
+;
+; These are the default cadences:
+;
+;cadence=125,125,2000,-4000
+;cadence=250,250,500,1000,250,250,500,-4000
+;cadence=125,125,125,125,125,-4000
+;cadence=1000,500,2500,-5000
+;
+; Each channel consists of the channel number or range. It inherits the
+; parameters that were specified above its declaration.
+;
+; For GR-303, CRV's are created like channels except they must start with the
+; trunk group followed by a colon, e.g.:
+;
+; crv => 1:1
+; crv => 2:1-2,5-8
+;
+;
+;callerid="Green Phone"<(256) 428-6121>
+;channel => 1
+;callerid="Black Phone"<(256) 428-6122>
+;channel => 2
+;callerid="CallerID Phone" <(630) 372-1564>
+;channel => 3
+;callerid="Pac Tel Phone" <(256) 428-6124>
+;channel => 4
+;callerid="Uniden Dead" <(256) 428-6125>
+;channel => 5
+;callerid="Cortelco 2500" <(256) 428-6126>
+;channel => 6
+;callerid="Main TA 750" <(256) 428-6127>
+;channel => 44
+;
+; For example, maybe we have some other channels which start out in a
+; different context and use E & M signalling instead.
+;
+;context=remote
+;sigalling=em
+;channel => 15
+;channel => 16
+
+;signalling=em_w
+;
+; All those in group 0 I'll use for outgoing calls
+;
+; Strip most significant digit (9) before sending
+;
+;stripmsd=1
+;callerid=asreceived
+;group=0
+;signalling=fxs_ls
+;channel => 45
+
+;signalling=fxo_ls
+;group=1
+;callerid="Joe Schmoe" <(256) 428-6131>
+;channel => 25
+;callerid="Megan May" <(256) 428-6132>
+;channel => 26
+;callerid="Suzy Queue" <(256) 428-6233>
+;channel => 27
+;callerid="Larry Moe" <(256) 428-6234>
+;channel => 28
+;
+; Sample PRI (CPE) config: Specify the switchtype, the signalling as either
+; pri_cpe or pri_net for CPE or Network termination, and generally you will
+; want to create a single "group" for all channels of the PRI.
+;
+; switchtype cannot be changed on a reload.
+;
+; switchtype = national
+; signalling = pri_cpe
+; group = 2
+; channel => 1-23
+
+;
+
+; Used for distinctive ring support for x100p.
+; You can see the dringX patterns is to set any one of the dringXcontext fields
+; and they will be printed on the console when an inbound call comes in.
+;
+; dringXrange is used to change the acceptable ranges for "tone offsets". Defaults to 10.
+; Note: a range of 0 is NOT what you might expect - it instead forces it to the default.
+; A range of -1 will force it to always match.
+; Anything lower than -1 would presumably cause it to never match.
+;
+;dring1=95,0,0
+;dring1context=internal1
+;dring1range=10
+;dring2=325,95,0
+;dring2context=internal2
+;dring2range=10
+; If no pattern is matched here is where we go.
+;context=default
+;channel => 1
+
+; ---------------- Options for use with signalling=ss7 -----------------
+; None of them can be changed by a reload.
+;
+; Variant of SS7 signalling:
+; Options are itu and ansi
+;ss7type = itu
+
+; SS7 Called Nature of Address Indicator
+;
+; unknown: Unknown
+; subscriber: Subscriber
+; national: National
+; international: International
+; dynamic: Dynamically selects the appropriate dialplan
+;
+;ss7_called_nai=dynamic
+;
+; SS7 Calling Nature of Address Indicator
+;
+; unknown: Unknown
+; subscriber: Subscriber
+; national: National
+; international: International
+; dynamic: Dynamically selects the appropriate dialplan
+;
+;ss7_calling_nai=dynamic
+;
+;
+; sample 1 for Germany
+;ss7_internationalprefix = 00
+;ss7_nationalprefix = 0
+;ss7_subscriberprefix =
+;ss7_unknownprefix =
+;
+
+; All settings apply to linkset 1
+;linkset = 1
+
+; Point code of the linkset. For ITU, this is the decimal number
+; format of the point code. For ANSI, this can either be in decimal
+; number format or in the xxx-xxx-xxx format
+;pointcode = 1
+
+; Point code of node adjacent to this signalling link (Possibly the STP between you and
+; your destination). Point code format follows the same rules as above.
+;adjpointcode = 2
+
+; Default point code that you would like to assign to outgoing messages (in case of
+; routing through STPs, or using A links). Point code format follows the same rules
+; as above.
+;defaultdpc = 3
+
+; Begin CIC (Circuit indication codes) count with this number
+;cicbeginswith = 1
+
+; What the MTP3 network indicator bits should be set to. Choices are
+; national, national_spare, international, international_spare
+;networkindicator=international
+
+; First signalling channel
+;sigchan = 48
+
+; Channels to associate with CICs on this linkset
+;channel = 25-47
+;
+; For more information on setting up SS7, see the README file in libss7 or
+; the doc/ss7.txt file in the Asterisk source tree.
+; ----------------- SS7 Options ----------------------------------------
diff --git a/trunk/configure b/trunk/configure
new file mode 100755
index 000000000..e8ae1606d
--- /dev/null
+++ b/trunk/configure
@@ -0,0 +1,50171 @@
+#! /bin/sh
+# From configure.ac Revision: 98971 .
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.61 for asterisk 1.4.
+#
+# Report bugs to <www.asterisk.org>.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+#
+# "Asterisk"
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+ if (eval ":") 2>/dev/null; then
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+
+ if test $as_have_required = yes && (eval ":
+(as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=\$LINENO
+ as_lineno_2=\$LINENO
+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+ :
+else
+ as_candidate_shells=
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ case $as_dir in
+ /*)
+ for as_base in sh bash ksh sh5; do
+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+ done;;
+ esac
+done
+IFS=$as_save_IFS
+
+
+ for as_shell in $as_candidate_shells $SHELL; do
+ # Try only shells that exist, to save several forks.
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+ CONFIG_SHELL=$as_shell
+ as_have_required=yes
+ if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+ (exit $1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+ break
+fi
+
+fi
+
+ done
+
+ if test "x$CONFIG_SHELL" != x; then
+ for as_var in BASH_ENV ENV
+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ done
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+ if test $as_have_required = no; then
+ echo This script requires a shell more modern than all the
+ echo shells that I found on your system. Please install a
+ echo modern shell, or manually run the script under such a
+ echo shell if you do have one.
+ { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+ echo No shell found that supports shell functions.
+ echo Please tell autoconf@gnu.org about your system,
+ echo including any error possibly output before this
+ echo message
+}
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME='asterisk'
+PACKAGE_TARNAME='asterisk'
+PACKAGE_VERSION='1.4'
+PACKAGE_STRING='asterisk 1.4'
+PACKAGE_BUGREPORT='www.asterisk.org'
+
+ac_unique_file="main/asterisk.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_header_list=
+ac_func_list=
+ac_subst_vars='SHELL
+PATH_SEPARATOR
+PACKAGE_NAME
+PACKAGE_TARNAME
+PACKAGE_VERSION
+PACKAGE_STRING
+PACKAGE_BUGREPORT
+exec_prefix
+prefix
+program_transform_name
+bindir
+sbindir
+libexecdir
+datarootdir
+datadir
+sysconfdir
+sharedstatedir
+localstatedir
+includedir
+oldincludedir
+docdir
+infodir
+htmldir
+dvidir
+pdfdir
+psdir
+libdir
+localedir
+mandir
+DEFS
+ECHO_C
+ECHO_N
+ECHO_T
+LIBS
+build_alias
+host_alias
+target_alias
+build
+build_cpu
+build_vendor
+build_os
+host
+host_cpu
+host_vendor
+host_os
+CC
+CFLAGS
+LDFLAGS
+CPPFLAGS
+ac_ct_CC
+EXEEXT
+OBJEXT
+CPP
+GREP
+EGREP
+BUILD_PLATFORM
+BUILD_CPU
+BUILD_VENDOR
+BUILD_OS
+HOST_PLATFORM
+HOST_CPU
+HOST_VENDOR
+HOST_OS
+OSARCH
+WINARCH
+UNAME
+PBX_OSREV
+CXX
+LD
+RANLIB
+CXXFLAGS
+ac_ct_CXX
+CXXCPP
+SED
+AWK
+INSTALL_PROGRAM
+INSTALL_SCRIPT
+INSTALL_DATA
+LN_S
+GNU_MAKE
+STRIP
+AR
+GNU_LD
+FIND
+COMPRESS
+BASENAME
+ID
+DIRNAME
+LN
+DOT
+WGET
+RUBBER
+KPATHSEA
+FETCH
+DOWNLOAD
+SOXMIX
+acx_pthread_config
+PTHREAD_CC
+PTHREAD_LIBS
+PTHREAD_CFLAGS
+AST_DEVMODE
+ALSA_LIB
+ALSA_INCLUDE
+ALSA_DIR
+PBX_ALSA
+BKTR_LIB
+BKTR_INCLUDE
+BKTR_DIR
+PBX_BKTR
+CAP_LIB
+CAP_INCLUDE
+CAP_DIR
+PBX_CAP
+CURL_LIB
+CURL_INCLUDE
+CURL_DIR
+PBX_CURL
+CURSES_LIB
+CURSES_INCLUDE
+CURSES_DIR
+PBX_CURSES
+CRYPTO_LIB
+CRYPTO_INCLUDE
+CRYPTO_DIR
+PBX_CRYPTO
+GSM_LIB
+GSM_INCLUDE
+GSM_DIR
+PBX_GSM
+GTK_LIB
+GTK_INCLUDE
+GTK_DIR
+PBX_GTK
+GTK2_LIB
+GTK2_INCLUDE
+GTK2_DIR
+PBX_GTK2
+ICONV_LIB
+ICONV_INCLUDE
+ICONV_DIR
+PBX_ICONV
+IKSEMEL_LIB
+IKSEMEL_INCLUDE
+IKSEMEL_DIR
+PBX_IKSEMEL
+IMAP_TK_LIB
+IMAP_TK_INCLUDE
+IMAP_TK_DIR
+PBX_IMAP_TK
+ISDNNET_LIB
+ISDNNET_INCLUDE
+ISDNNET_DIR
+PBX_ISDNNET
+JACK_LIB
+JACK_INCLUDE
+JACK_DIR
+PBX_JACK
+LTDL_LIB
+LTDL_INCLUDE
+LTDL_DIR
+PBX_LTDL
+LUA_LIB
+LUA_INCLUDE
+LUA_DIR
+PBX_LUA
+MISDN_LIB
+MISDN_INCLUDE
+MISDN_DIR
+PBX_MISDN
+NBS_LIB
+NBS_INCLUDE
+NBS_DIR
+PBX_NBS
+NCURSES_LIB
+NCURSES_INCLUDE
+NCURSES_DIR
+PBX_NCURSES
+NETSNMP_LIB
+NETSNMP_INCLUDE
+NETSNMP_DIR
+PBX_NETSNMP
+NEWT_LIB
+NEWT_INCLUDE
+NEWT_DIR
+PBX_NEWT
+UNIXODBC_LIB
+UNIXODBC_INCLUDE
+UNIXODBC_DIR
+PBX_UNIXODBC
+OGG_LIB
+OGG_INCLUDE
+OGG_DIR
+PBX_OGG
+OSPTK_LIB
+OSPTK_INCLUDE
+OSPTK_DIR
+PBX_OSPTK
+OSS_LIB
+OSS_INCLUDE
+OSS_DIR
+PBX_OSS
+PGSQL_LIB
+PGSQL_INCLUDE
+PGSQL_DIR
+PBX_PGSQL
+POPT_LIB
+POPT_INCLUDE
+POPT_DIR
+PBX_POPT
+PORTAUDIO_LIB
+PORTAUDIO_INCLUDE
+PORTAUDIO_DIR
+PBX_PORTAUDIO
+PRI_LIB
+PRI_INCLUDE
+PRI_DIR
+PBX_PRI
+SS7_LIB
+SS7_INCLUDE
+SS7_DIR
+PBX_SS7
+PWLIB_LIB
+PWLIB_INCLUDE
+PWLIB_DIR
+PBX_PWLIB
+OPENH323_LIB
+OPENH323_INCLUDE
+OPENH323_DIR
+PBX_OPENH323
+RADIUS_LIB
+RADIUS_INCLUDE
+RADIUS_DIR
+PBX_RADIUS
+SPEEX_LIB
+SPEEX_INCLUDE
+SPEEX_DIR
+PBX_SPEEX
+SPEEXDSP_LIB
+SPEEXDSP_INCLUDE
+SPEEXDSP_DIR
+PBX_SPEEXDSP
+SQLITE_LIB
+SQLITE_INCLUDE
+SQLITE_DIR
+PBX_SQLITE
+SQLITE3_LIB
+SQLITE3_INCLUDE
+SQLITE3_DIR
+PBX_SQLITE3
+SUPPSERV_LIB
+SUPPSERV_INCLUDE
+SUPPSERV_DIR
+PBX_SUPPSERV
+OPENSSL_LIB
+OPENSSL_INCLUDE
+OPENSSL_DIR
+PBX_OPENSSL
+FREETDS_LIB
+FREETDS_INCLUDE
+FREETDS_DIR
+PBX_FREETDS
+TERMCAP_LIB
+TERMCAP_INCLUDE
+TERMCAP_DIR
+PBX_TERMCAP
+TINFO_LIB
+TINFO_INCLUDE
+TINFO_DIR
+PBX_TINFO
+TONEZONE_LIB
+TONEZONE_INCLUDE
+TONEZONE_DIR
+PBX_TONEZONE
+USB_LIB
+USB_INCLUDE
+USB_DIR
+PBX_USB
+VORBIS_LIB
+VORBIS_INCLUDE
+VORBIS_DIR
+PBX_VORBIS
+VPB_LIB
+VPB_INCLUDE
+VPB_DIR
+PBX_VPB
+X11_LIB
+X11_INCLUDE
+X11_DIR
+PBX_X11
+ZLIB_LIB
+ZLIB_INCLUDE
+ZLIB_DIR
+PBX_ZLIB
+ZAPTEL_LIB
+ZAPTEL_INCLUDE
+ZAPTEL_DIR
+PBX_ZAPTEL
+ZAPTEL_TRANSCODE_LIB
+ZAPTEL_TRANSCODE_INCLUDE
+ZAPTEL_TRANSCODE_DIR
+PBX_ZAPTEL_TRANSCODE
+ZAPTEL_VLDTMF_LIB
+ZAPTEL_VLDTMF_INCLUDE
+ZAPTEL_VLDTMF_DIR
+PBX_ZAPTEL_VLDTMF
+ZAPTEL_HWGAIN_LIB
+ZAPTEL_HWGAIN_INCLUDE
+ZAPTEL_HWGAIN_DIR
+PBX_ZAPTEL_HWGAIN
+ZAPTEL_ECHOCANPARAMS_LIB
+ZAPTEL_ECHOCANPARAMS_INCLUDE
+ZAPTEL_ECHOCANPARAMS_DIR
+PBX_ZAPTEL_ECHOCANPARAMS
+ZAPTEL_CHANALARMS_LIB
+ZAPTEL_CHANALARMS_INCLUDE
+ZAPTEL_CHANALARMS_DIR
+PBX_ZAPTEL_CHANALARMS
+SDL_LIB
+SDL_INCLUDE
+SDL_DIR
+PBX_SDL
+SDL_IMAGE_LIB
+SDL_IMAGE_INCLUDE
+SDL_IMAGE_DIR
+PBX_SDL_IMAGE
+FFMPEG_LIB
+FFMPEG_INCLUDE
+FFMPEG_DIR
+PBX_FFMPEG
+ALLOCA
+LIBOBJS
+POW_LIB
+HAS_POLL
+GC_CFLAGS
+GC_LDFLAGS
+AST_DECLARATION_AFTER_STATEMENT
+GSM_INTERNAL
+CONFIG_NETSNMP
+PG_CONFIG
+PTLIB_CONFIG
+PWLIBDIR
+PWLIB_INCDIR
+PWLIB_LIBDIR
+PWLIB_PLATFORM
+OPENH323DIR
+OPENH323_INCDIR
+OPENH323_LIBDIR
+OPENH323_SUFFIX
+OPENH323_BUILD
+EDITLINE_LIB
+PBX_H323
+PBX_IXJUSER
+CONFIG_SDL
+CONFIG_GTK
+PKGCONFIG
+CURL_CONFIG
+LTLIBOBJS'
+ac_subst_files=''
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+CXX
+CXXFLAGS
+CCC
+CXXCPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+ eval enable_$ac_feature=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+ eval enable_$ac_feature=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+ eval with_$ac_package=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+ eval with_$ac_package=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute directory names.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ { echo "$as_me: error: Working directory cannot be determined" >&2
+ { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ { echo "$as_me: error: pwd does not report name of working directory" >&2
+ { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$0" ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2
+ { (exit 1); exit 1; }; }
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures asterisk 1.4 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/asterisk]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of asterisk 1.4:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-dev-mode Turn on developer mode
+ --disable-largefile omit support for large files
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --with-asound=PATH use Advanced Linux Sound Architecture files in PATH
+ --with-execinfo=PATH use Stack Backtrace support files in PATH
+ --with-cap=PATH use POSIX 1.e capabilities files in PATH
+ --with-curl=PATH use cURL files in PATH
+ --with-curses=PATH use curses files in PATH
+ --with-crypto=PATH use OpenSSL Cryptography support files in PATH
+ --with-gsm=PATH use External GSM library files in PATH , use
+ 'internal' GSM otherwise
+ --with-gtk=PATH use gtk libraries files in PATH
+ --with-gtk2=PATH use gtk2 libraries files in PATH
+ --with-iconv=PATH use Iconv Library files in PATH
+ --with-iksemel=PATH use Iksemel Jabber Library files in PATH
+ --with-imap=PATH use UW IMAP Toolkit files in PATH
+ --with-isdnnet=PATH use ISDN4Linux Library files in PATH
+ --with-jack=PATH use Jack Audio Connection Kit files in PATH
+ --with-ltdl=PATH use libtool files in PATH
+ --with-lua=PATH use Lua files in PATH
+ --with-misdn=PATH use mISDN User Library files in PATH
+ --with-nbs=PATH use Network Broadcast Sound files in PATH
+ --with-ncurses=PATH use ncurses files in PATH
+ --with-netsnmp=PATH use Net-SNMP files in PATH
+ --with-newt=PATH use newt files in PATH
+ --with-odbc=PATH use unixODBC files in PATH
+ --with-ogg=PATH use OGG files in PATH
+ --with-osptk=PATH use OSP Toolkit files in PATH
+ --with-oss=PATH use Open Sound System files in PATH
+ --with-postgres=PATH use PostgreSQL files in PATH
+ --with-popt=PATH use popt files in PATH
+ --with-portaudio=PATH use PortAudio files in PATH
+ --with-pri=PATH use ISDN PRI files in PATH
+ --with-ss7=PATH use ISDN SS7 files in PATH
+ --with-pwlib=PATH use PWlib files in PATH
+ --with-h323=PATH use OpenH323 files in PATH
+ --with-radius=PATH use Radius Client files in PATH
+ --with-speex=PATH use Speex files in PATH
+ --with-speexdsp=PATH use Speexdsp files in PATH
+ --with-sqlite=PATH use SQLite files in PATH
+ --with-sqlite3=PATH use SQLite files in PATH
+ --with-suppserv=PATH use mISDN Supplemental Services files in PATH
+ --with-ssl=PATH use OpenSSL Secure Sockets Layer support files in
+ PATH
+ --with-tds=PATH use FreeTDS files in PATH
+ --with-termcap=PATH use Termcap files in PATH
+ --with-tinfo=PATH use Term Info files in PATH
+ --with-tonezone=PATH use tonezone files in PATH
+ --with-usb=PATH use usb files in PATH
+ --with-vorbis=PATH use Vorbis files in PATH
+ --with-vpb=PATH use Voicetronix API files in PATH
+ --with-x11=PATH use X11 support files in PATH
+ --with-z=PATH use zlib files in PATH
+ --with-zaptel=PATH use Zaptel files in PATH
+ --with-zaptel_transcode=PATH
+ use Zaptel Transcoder Support files in PATH
+ --with-zaptel_vldtmf=PATH
+ use Zaptel VLDTMF Support files in PATH
+ --with-zaptel_hwgain=PATH
+ use Zaptel Hardware Gain Support files in PATH
+ --with-zaptel_echocanparams=PATH
+ use Zaptel Echo Canceler Parameter Support files in
+ PATH
+ --with-zaptel_chanalarms=PATH
+ use Zaptel Channel Alarm Support files in PATH
+ --with-sdl=PATH use Sdl files in PATH
+ --with-SDL_image=PATH use Sdl Image library files in PATH
+ --with-avcodec=PATH use Ffmpeg and avcodec library files in PATH
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+ CXX C++ compiler command
+ CXXFLAGS C++ compiler flags
+ CXXCPP C++ preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <www.asterisk.org>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" || continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+asterisk configure 1.4
+generated by GNU Autoconf 2.61
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+
+"Asterisk"
+_ACEOF
+ exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by asterisk $as_me 1.4, which was
+generated by GNU Autoconf 2.61. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args '$ac_arg'"
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+ set x "$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+ set x "$prefix/share/config.site" "$prefix/etc/config.site"
+else
+ set x "$ac_default_prefix/share/config.site" \
+ "$ac_default_prefix/etc/config.site"
+fi
+shift
+for ac_site_file
+do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+ac_header_list="$ac_header_list sys/time.h"
+ac_header_list="$ac_header_list unistd.h"
+ac_func_list="$ac_func_list alarm"
+ac_header_list="$ac_header_list utime.h"
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+# cross-compile macros
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5
+echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
+echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
+ { (exit 1); exit 1; }; }
+
+{ echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6; }
+if test "${ac_cv_build+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+ { (exit 1); exit 1; }; }
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
+echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
+echo "$as_me: error: invalid value of canonical build" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6; }
+if test "${ac_cv_host+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
+echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) { { echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
+echo "$as_me: error: invalid value of canonical host" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+# check existence of the package
+
+
+# specify output header file
+ac_config_headers="$ac_config_headers include/asterisk/autoconfig.h"
+
+
+
+
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _GNU_SOURCE 1
+_ACEOF
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO: checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; }
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+#
+# List of possible output files, starting from the most likely.
+# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)
+# only as a last resort. b.out is created by i960 compilers.
+ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'
+#
+# The IRIX 6 linker writes into existing files which may not be
+# executable, retaining their permissions. Remove them first so a
+# subsequent execution test works.
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+
+{ echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6; }
+if test -z "$ac_file"; then
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+{ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; }
+{ echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6; }
+
+{ echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; }
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; }
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_c89=$ac_arg
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6; } ;;
+ xno)
+ { echo "$as_me:$LINENO: result: unsupported" >&5
+echo "${ECHO_T}unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Extract the first word of "grep ggrep" to use in msg output
+if test -z "$GREP"; then
+set dummy grep ggrep; ac_prog_name=$2
+if test "${ac_cv_path_GREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_path_GREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+ # Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+ $ac_path_GREP_found && break 3
+ done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+GREP="$ac_cv_path_GREP"
+if test -z "$GREP"; then
+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+echo "${ECHO_T}$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ # Extract the first word of "egrep" to use in msg output
+if test -z "$EGREP"; then
+set dummy egrep; ac_prog_name=$2
+if test "${ac_cv_path_EGREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_path_EGREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+ # Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+ $ac_path_EGREP_found && break 3
+ done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+EGREP="$ac_cv_path_EGREP"
+if test -z "$EGREP"; then
+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+echo "${ECHO_T}$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+
+{ echo "$as_me:$LINENO: checking for AIX" >&5
+echo $ECHO_N "checking for AIX... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef _AIX
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+cat >>confdefs.h <<\_ACEOF
+#define _ALL_SOURCE 1
+_ACEOF
+
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+rm -f conftest*
+
+
+{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking minix/config.h usability" >&5
+echo $ECHO_N "checking minix/config.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <minix/config.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking minix/config.h presence" >&5
+echo $ECHO_N "checking minix/config.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <minix/config.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: minix/config.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_minix_config_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6; }
+
+fi
+if test $ac_cv_header_minix_config_h = yes; then
+ MINIX=yes
+else
+ MINIX=
+fi
+
+
+if test "$MINIX" = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_SOURCE 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_1_SOURCE 2
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _MINIX 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+ { echo "$as_me:$LINENO: checking whether it is safe to define __EXTENSIONS__" >&5
+echo $ECHO_N "checking whether it is safe to define __EXTENSIONS__... $ECHO_C" >&6; }
+if test "${ac_cv_safe_to_define___extensions__+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+# define __EXTENSIONS__ 1
+ $ac_includes_default
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_safe_to_define___extensions__=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_safe_to_define___extensions__=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_safe_to_define___extensions__" >&5
+echo "${ECHO_T}$ac_cv_safe_to_define___extensions__" >&6; }
+ test $ac_cv_safe_to_define___extensions__ = yes &&
+ cat >>confdefs.h <<\_ACEOF
+#define __EXTENSIONS__ 1
+_ACEOF
+
+ cat >>confdefs.h <<\_ACEOF
+#define _POSIX_PTHREAD_SEMANTICS 1
+_ACEOF
+
+ cat >>confdefs.h <<\_ACEOF
+#define _TANDEM_SOURCE 1
+_ACEOF
+
+ # note- does not work on FreeBSD
+
+case "${host_os}" in
+ freebsd*)
+ ac_default_prefix=/usr/local
+ CPPFLAGS=-I/usr/local/include
+ LDFLAGS=-L/usr/local/lib
+ ;;
+
+ *)
+ ac_default_prefix=/usr
+ if test ${sysconfdir} = '${prefix}/etc'; then
+ sysconfdir=/etc
+ fi
+ if test ${mandir} = '${prefix}/man'; then
+ mandir=/usr/share/man
+ fi
+ ;;
+esac
+
+if test ${localstatedir} = '${prefix}/var'; then
+ localstatedir=/var
+fi
+
+BUILD_PLATFORM=${build}
+BUILD_CPU=${build_cpu}
+BUILD_VENDOR=${build_vendor}
+BUILD_OS=${build_os}
+
+
+
+
+
+
+HOST_PLATFORM=${host}
+HOST_CPU=${host_cpu}
+HOST_VENDOR=${host_vendor}
+HOST_OS=${host_os}
+
+
+
+
+
+
+WINARCH=0
+
+case "${host_os}" in
+ freebsd*)
+ OSARCH=FreeBSD
+ ;;
+ netbsd*)
+ OSARCH=NetBSD
+ ;;
+ openbsd*)
+ OSARCH=OpenBSD
+ ;;
+ solaris*)
+ OSARCH=SunOS
+ ;;
+ mingw32)
+ OSARCH=mingw32
+ WINARCH=1
+ ;;
+ cygwin)
+ OSARCH=cygwin
+ WINARCH=1
+ ;;
+ *)
+ OSARCH=${host_os}
+ ;;
+esac
+
+
+
+
+# check for uname
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}uname", so it can be a program name with args.
+set dummy ${ac_tool_prefix}uname; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_UNAME+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $UNAME in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_UNAME="$UNAME" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_UNAME="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+UNAME=$ac_cv_path_UNAME
+if test -n "$UNAME"; then
+ { echo "$as_me:$LINENO: result: $UNAME" >&5
+echo "${ECHO_T}$UNAME" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_UNAME"; then
+ ac_pt_UNAME=$UNAME
+ # Extract the first word of "uname", so it can be a program name with args.
+set dummy uname; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ac_pt_UNAME+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ac_pt_UNAME in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_UNAME="$ac_pt_UNAME" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_UNAME="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_UNAME=$ac_cv_path_ac_pt_UNAME
+if test -n "$ac_pt_UNAME"; then
+ { echo "$as_me:$LINENO: result: $ac_pt_UNAME" >&5
+echo "${ECHO_T}$ac_pt_UNAME" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_pt_UNAME" = x; then
+ UNAME="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ UNAME=$ac_pt_UNAME
+ fi
+else
+ UNAME="$ac_cv_path_UNAME"
+fi
+
+if test ! x"${UNAME}" = xNo; then
+ PBX_OSREV=$(${UNAME} -r)
+fi
+
+
+
+
+
+
+
+
+# cross-compile checks
+if test "${cross_compiling}" = "yes";
+then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}g++", so it can be a program name with args.
+set dummy ${ac_tool_prefix}g++; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CXX="${ac_tool_prefix}g++"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ { echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CXX"; then
+ ac_ct_CXX=$CXX
+ # Extract the first word of "g++", so it can be a program name with args.
+set dummy g++; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CXX"; then
+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CXX="g++"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CXX" = x; then
+ CXX=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CXX=$ac_ct_CXX
+ fi
+else
+ CXX="$ac_cv_prog_CXX"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ld; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_LD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$LD"; then
+ ac_cv_prog_LD="$LD" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LD="${ac_tool_prefix}ld"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+LD=$ac_cv_prog_LD
+if test -n "$LD"; then
+ { echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LD"; then
+ ac_ct_LD=$LD
+ # Extract the first word of "ld", so it can be a program name with args.
+set dummy ld; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_LD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_LD"; then
+ ac_cv_prog_ac_ct_LD="$ac_ct_LD" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_LD="ld"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LD=$ac_cv_prog_ac_ct_LD
+if test -n "$ac_ct_LD"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_LD" >&5
+echo "${ECHO_T}$ac_ct_LD" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_LD" = x; then
+ LD=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ LD=$ac_ct_LD
+ fi
+else
+ LD="$ac_cv_prog_LD"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+fi
+
+# Checks for programs.
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO: checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; }
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_c89=$ac_arg
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6; } ;;
+ xno)
+ { echo "$as_me:$LINENO: result: unsupported" >&5
+echo "${ECHO_T}unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+ if test -n "$CCC"; then
+ CXX=$CCC
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ { echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$CXX" && break
+ done
+fi
+if test -z "$CXX"; then
+ ac_ct_CXX=$CXX
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CXX"; then
+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CXX="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CXX" && break
+done
+
+ if test "x$ac_ct_CXX" = x; then
+ CXX="g++"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CXX=$ac_ct_CXX
+ fi
+fi
+
+ fi
+fi
+# Provide some information about the compiler.
+echo "$as_me:$LINENO: checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+{ echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6; }
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6; }
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+ ac_cxx_werror_flag=yes
+ ac_cv_prog_cxx_g=no
+ CXXFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cxx_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CXXFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+ CXXFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cxx_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+ CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+ if test "$GXX" = yes; then
+ CXXFLAGS="-g -O2"
+ else
+ CXXFLAGS="-g"
+ fi
+else
+ if test "$GXX" = yes; then
+ CXXFLAGS="-O2"
+ else
+ CXXFLAGS=
+ fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+{ echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5
+echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6; }
+if test -z "$CXXCPP"; then
+ if test "${ac_cv_prog_CXXCPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CXXCPP needs to be expanded
+ for CXXCPP in "$CXX -E" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CXXCPP=$CXXCPP
+
+fi
+ CXXCPP=$ac_cv_prog_CXXCPP
+else
+ ac_cv_prog_CXXCPP=$CXXCPP
+fi
+{ echo "$as_me:$LINENO: result: $CXXCPP" >&5
+echo "${ECHO_T}$CXXCPP" >&6; }
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# This macro is just copied into our local acinclude.m4 from libtool.m4 so that
+# the developers regenerating the configure script don't have to install libtool.
+{ echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+echo $ECHO_N "checking for a sed that does not truncate output... $ECHO_C" >&6; }
+if test "${ac_cv_path_SED+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" | sed 99q >conftest.sed
+ $as_unset ac_script || ac_script=
+ # Extract the first word of "sed gsed" to use in msg output
+if test -z "$SED"; then
+set dummy sed gsed; ac_prog_name=$2
+if test "${ac_cv_path_SED+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_path_SED_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+ # Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+ $ac_path_SED_found && break 3
+ done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+SED="$ac_cv_path_SED"
+if test -z "$SED"; then
+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in \$PATH" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in \$PATH" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_SED" >&5
+echo "${ECHO_T}$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+{ echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }
+if test "${ac_cv_prog_egrep+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+ then ac_cv_prog_egrep='grep -E'
+ else ac_cv_prog_egrep='egrep'
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6; }
+ EGREP=$ac_cv_prog_egrep
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { echo "$as_me:$LINENO: checking for ld used by $CC" >&5
+echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { echo "$as_me:$LINENO: checking for GNU ld" >&5
+echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6; }
+else
+ { echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6; }
+fi
+if test "${lt_cv_path_LD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ { echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+ { (exit 1); exit 1; }; }
+{ echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6; }
+if test "${lt_cv_prog_gnu_ld+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ echo "$as_me:$LINENO: result: $lt_cv_prog_gnu_ld" >&5
+echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+ # note, does not work on FreeBSD
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+IFS=$as_save_IFS
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ echo "$as_me:$LINENO: checking whether ln -s works" >&5
+echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+echo "${ECHO_T}no, using $LN_S" >&6; }
+fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+{ echo "$as_me:$LINENO: checking for GNU make" >&5
+echo $ECHO_N "checking for GNU make... $ECHO_C" >&6; }
+if test "${GNU_MAKE+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ GNU_MAKE='Not Found' ;
+ GNU_MAKE_VERSION_MAJOR=0 ;
+ GNU_MAKE_VERSION_MINOR=0 ;
+ for a in make gmake gnumake ; do
+ if test -z "$a" ; then continue ; fi ;
+ if ( sh -c "$a --version" 2> /dev/null | grep GNU 2>&1 > /dev/null ) ; then
+ GNU_MAKE=$a ;
+ GNU_MAKE_VERSION_MAJOR=`$GNU_MAKE --version | grep "GNU Make" | cut -f3 -d' ' | cut -f1 -d'.'`
+ GNU_MAKE_VERSION_MINOR=`$GNU_MAKE --version | grep "GNU Make" | cut -f2 -d'.' | cut -c1-2`
+ break;
+ fi
+ done ;
+
+fi
+{ echo "$as_me:$LINENO: result: $GNU_MAKE" >&5
+echo "${ECHO_T}$GNU_MAKE" >&6; } ;
+if test "x$GNU_MAKE" = "xNot Found" ; then
+ { { echo "$as_me:$LINENO: error: *** Please install GNU make. It is required to build Asterisk!" >&5
+echo "$as_me: error: *** Please install GNU make. It is required to build Asterisk!" >&2;}
+ { (exit 1); exit 1; }; }
+ exit 1
+fi
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_STRIP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $STRIP in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_STRIP="$STRIP" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_STRIP="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+STRIP=$ac_cv_path_STRIP
+if test -n "$STRIP"; then
+ { echo "$as_me:$LINENO: result: $STRIP" >&5
+echo "${ECHO_T}$STRIP" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_STRIP"; then
+ ac_pt_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ac_pt_STRIP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ac_pt_STRIP in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_STRIP="$ac_pt_STRIP" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_STRIP="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_STRIP=$ac_cv_path_ac_pt_STRIP
+if test -n "$ac_pt_STRIP"; then
+ { echo "$as_me:$LINENO: result: $ac_pt_STRIP" >&5
+echo "${ECHO_T}$ac_pt_STRIP" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_pt_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_pt_STRIP
+ fi
+else
+ STRIP="$ac_cv_path_STRIP"
+fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+if test -n "$AR"; then
+ { echo "$as_me:$LINENO: result: $AR" >&5
+echo "${ECHO_T}$AR" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_AR"; then
+ ac_pt_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ac_pt_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ac_pt_AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_AR="$ac_pt_AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_AR="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_AR=$ac_cv_path_ac_pt_AR
+if test -n "$ac_pt_AR"; then
+ { echo "$as_me:$LINENO: result: $ac_pt_AR" >&5
+echo "${ECHO_T}$ac_pt_AR" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_pt_AR" = x; then
+ AR=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_pt_AR
+ fi
+else
+ AR="$ac_cv_path_AR"
+fi
+
+
+GNU_LD=0
+if test "x$with_gnu_ld" = "xyes" ; then
+ GNU_LD=1
+fi
+
+
+# Extract the first word of "awk", so it can be a program name with args.
+set dummy awk; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_AWK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $AWK in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AWK="$AWK" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_AWK="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_AWK" && ac_cv_path_AWK=":"
+ ;;
+esac
+fi
+AWK=$ac_cv_path_AWK
+if test -n "$AWK"; then
+ { echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "grep", so it can be a program name with args.
+set dummy grep; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $GREP in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_GREP="$GREP" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_GREP="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_GREP" && ac_cv_path_GREP=":"
+ ;;
+esac
+fi
+GREP=$ac_cv_path_GREP
+if test -n "$GREP"; then
+ { echo "$as_me:$LINENO: result: $GREP" >&5
+echo "${ECHO_T}$GREP" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "find", so it can be a program name with args.
+set dummy find; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_FIND+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $FIND in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_FIND="$FIND" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_FIND="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_FIND" && ac_cv_path_FIND=":"
+ ;;
+esac
+fi
+FIND=$ac_cv_path_FIND
+if test -n "$FIND"; then
+ { echo "$as_me:$LINENO: result: $FIND" >&5
+echo "${ECHO_T}$FIND" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "compress", so it can be a program name with args.
+set dummy compress; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_COMPRESS+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $COMPRESS in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_COMPRESS="$COMPRESS" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_COMPRESS="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_COMPRESS" && ac_cv_path_COMPRESS=":"
+ ;;
+esac
+fi
+COMPRESS=$ac_cv_path_COMPRESS
+if test -n "$COMPRESS"; then
+ { echo "$as_me:$LINENO: result: $COMPRESS" >&5
+echo "${ECHO_T}$COMPRESS" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "basename", so it can be a program name with args.
+set dummy basename; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_BASENAME+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $BASENAME in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_BASENAME="$BASENAME" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_BASENAME="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_BASENAME" && ac_cv_path_BASENAME=":"
+ ;;
+esac
+fi
+BASENAME=$ac_cv_path_BASENAME
+if test -n "$BASENAME"; then
+ { echo "$as_me:$LINENO: result: $BASENAME" >&5
+echo "${ECHO_T}$BASENAME" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "id", so it can be a program name with args.
+set dummy id; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ID+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ID in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ID="$ID" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ID="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_ID" && ac_cv_path_ID=":"
+ ;;
+esac
+fi
+ID=$ac_cv_path_ID
+if test -n "$ID"; then
+ { echo "$as_me:$LINENO: result: $ID" >&5
+echo "${ECHO_T}$ID" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "dirname", so it can be a program name with args.
+set dummy dirname; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_DIRNAME+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $DIRNAME in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_DIRNAME="$DIRNAME" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_DIRNAME="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_DIRNAME" && ac_cv_path_DIRNAME=":"
+ ;;
+esac
+fi
+DIRNAME=$ac_cv_path_DIRNAME
+if test -n "$DIRNAME"; then
+ { echo "$as_me:$LINENO: result: $DIRNAME" >&5
+echo "${ECHO_T}$DIRNAME" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "sh", so it can be a program name with args.
+set dummy sh; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_SHELL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $SHELL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SHELL="$SHELL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SHELL="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SHELL" && ac_cv_path_SHELL=":"
+ ;;
+esac
+fi
+SHELL=$ac_cv_path_SHELL
+if test -n "$SHELL"; then
+ { echo "$as_me:$LINENO: result: $SHELL" >&5
+echo "${ECHO_T}$SHELL" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "ln", so it can be a program name with args.
+set dummy ln; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_LN+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $LN in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_LN="$LN" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_LN="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_LN" && ac_cv_path_LN=":"
+ ;;
+esac
+fi
+LN=$ac_cv_path_LN
+if test -n "$LN"; then
+ { echo "$as_me:$LINENO: result: $LN" >&5
+echo "${ECHO_T}$LN" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "dot", so it can be a program name with args.
+set dummy dot; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_DOT+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $DOT in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_DOT="$DOT" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_DOT="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_DOT" && ac_cv_path_DOT=":"
+ ;;
+esac
+fi
+DOT=$ac_cv_path_DOT
+if test -n "$DOT"; then
+ { echo "$as_me:$LINENO: result: $DOT" >&5
+echo "${ECHO_T}$DOT" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "wget", so it can be a program name with args.
+set dummy wget; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_WGET+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $WGET in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_WGET="$WGET" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_WGET="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_WGET" && ac_cv_path_WGET=":"
+ ;;
+esac
+fi
+WGET=$ac_cv_path_WGET
+if test -n "$WGET"; then
+ { echo "$as_me:$LINENO: result: $WGET" >&5
+echo "${ECHO_T}$WGET" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "rubber", so it can be a program name with args.
+set dummy rubber; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_RUBBER+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $RUBBER in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_RUBBER="$RUBBER" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_RUBBER="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_RUBBER" && ac_cv_path_RUBBER=":"
+ ;;
+esac
+fi
+RUBBER=$ac_cv_path_RUBBER
+if test -n "$RUBBER"; then
+ { echo "$as_me:$LINENO: result: $RUBBER" >&5
+echo "${ECHO_T}$RUBBER" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+# Extract the first word of "kpsewhich", so it can be a program name with args.
+set dummy kpsewhich; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_KPATHSEA+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $KPATHSEA in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_KPATHSEA="$KPATHSEA" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_KPATHSEA="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_KPATHSEA" && ac_cv_path_KPATHSEA=":"
+ ;;
+esac
+fi
+KPATHSEA=$ac_cv_path_KPATHSEA
+if test -n "$KPATHSEA"; then
+ { echo "$as_me:$LINENO: result: $KPATHSEA" >&5
+echo "${ECHO_T}$KPATHSEA" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+if test "${WGET}" != ":" ; then
+ DOWNLOAD=${WGET}
+else
+ # Extract the first word of "fetch", so it can be a program name with args.
+set dummy fetch; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_FETCH+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $FETCH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_FETCH="$FETCH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_FETCH="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_FETCH" && ac_cv_path_FETCH=":"
+ ;;
+esac
+fi
+FETCH=$ac_cv_path_FETCH
+if test -n "$FETCH"; then
+ { echo "$as_me:$LINENO: result: $FETCH" >&5
+echo "${ECHO_T}$FETCH" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ DOWNLOAD=${FETCH}
+fi
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}soxmix", so it can be a program name with args.
+set dummy ${ac_tool_prefix}soxmix; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_SOXMIX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$SOXMIX"; then
+ ac_cv_prog_SOXMIX="$SOXMIX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SOXMIX="${ac_tool_prefix}soxmix"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+SOXMIX=$ac_cv_prog_SOXMIX
+if test -n "$SOXMIX"; then
+ { echo "$as_me:$LINENO: result: $SOXMIX" >&5
+echo "${ECHO_T}$SOXMIX" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_SOXMIX"; then
+ ac_ct_SOXMIX=$SOXMIX
+ # Extract the first word of "soxmix", so it can be a program name with args.
+set dummy soxmix; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_SOXMIX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_SOXMIX"; then
+ ac_cv_prog_ac_ct_SOXMIX="$ac_ct_SOXMIX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_SOXMIX="soxmix"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_SOXMIX=$ac_cv_prog_ac_ct_SOXMIX
+if test -n "$ac_ct_SOXMIX"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_SOXMIX" >&5
+echo "${ECHO_T}$ac_ct_SOXMIX" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_SOXMIX" = x; then
+ SOXMIX=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ SOXMIX=$ac_ct_SOXMIX
+ fi
+else
+ SOXMIX="$ac_cv_prog_SOXMIX"
+fi
+
+if test "${SOXMIX}" != ":" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SOXMIX 1
+_ACEOF
+
+fi
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ { echo "$as_me:$LINENO: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5
+echo $ECHO_N "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... $ECHO_C" >&6; }
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_join ();
+int
+main ()
+{
+return pthread_join ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ acx_pthread_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ { echo "$as_me:$LINENO: result: $acx_pthread_ok" >&5
+echo "${ECHO_T}$acx_pthread_ok" >&6; }
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ { echo "$as_me:$LINENO: checking whether pthreads work without any flags" >&5
+echo $ECHO_N "checking whether pthreads work without any flags... $ECHO_C" >&6; }
+ ;;
+
+ -*)
+ { echo "$as_me:$LINENO: checking whether pthreads work with $flag" >&5
+echo $ECHO_N "checking whether pthreads work with $flag... $ECHO_C" >&6; }
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ # Extract the first word of "pthread-config", so it can be a program name with args.
+set dummy pthread-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_acx_pthread_config+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$acx_pthread_config"; then
+ ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_acx_pthread_config="yes"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no"
+fi
+fi
+acx_pthread_config=$ac_cv_prog_acx_pthread_config
+if test -n "$acx_pthread_config"; then
+ { echo "$as_me:$LINENO: result: $acx_pthread_config" >&5
+echo "${ECHO_T}$acx_pthread_config" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ { echo "$as_me:$LINENO: checking for the pthreads library -l$flag" >&5
+echo $ECHO_N "checking for the pthreads library -l$flag... $ECHO_C" >&6; }
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ acx_pthread_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ { echo "$as_me:$LINENO: result: $acx_pthread_ok" >&5
+echo "${ECHO_T}$acx_pthread_ok" >&6; }
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ { echo "$as_me:$LINENO: checking for joinable pthread attribute" >&5
+echo $ECHO_N "checking for joinable pthread attribute... $ECHO_C" >&6; }
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int attr=$attr; return attr;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ attr_name=$attr; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ { echo "$as_me:$LINENO: result: $attr_name" >&5
+echo "${ECHO_T}$attr_name" >&6; }
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+
+cat >>confdefs.h <<_ACEOF
+#define PTHREAD_CREATE_JOINABLE $attr_name
+_ACEOF
+
+ fi
+
+ { echo "$as_me:$LINENO: checking if more special flags are required for pthreads" >&5
+echo $ECHO_N "checking if more special flags are required for pthreads... $ECHO_C" >&6; }
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ { echo "$as_me:$LINENO: result: ${flag}" >&5
+echo "${ECHO_T}${flag}" >&6; }
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ for ac_prog in xlc_r cc_r
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_PTHREAD_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$PTHREAD_CC"; then
+ ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_PTHREAD_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
+if test -n "$PTHREAD_CC"; then
+ { echo "$as_me:$LINENO: result: $PTHREAD_CC" >&5
+echo "${ECHO_T}$PTHREAD_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$PTHREAD_CC" && break
+done
+test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}"
+
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+
+
+
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD 1
+_ACEOF
+
+ :
+else
+ acx_pthread_ok=no
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Check whether --enable-dev-mode was given.
+if test "${enable_dev_mode+set}" = set; then
+ enableval=$enable_dev_mode; case "${enableval}" in
+ y|ye|yes) AST_DEVMODE=yes ;;
+ n|no) AST_DEVMODE=no ;;
+ *) { { echo "$as_me:$LINENO: error: bad value ${enableval} for --enable-dev-mode" >&5
+echo "$as_me: error: bad value ${enableval} for --enable-dev-mode" >&2;}
+ { (exit 1); exit 1; }; } ;;
+ esac
+fi
+
+
+
+# AST_EXT_LIB_SETUP is used to tell configure to handle variables for
+# various packages.
+# $1 is the prefix for the variables in makeopts and autoconfig.h
+# $2 is the short comment, $4 is the long comment
+# $3 is the name used in --with- or --without- flags for configure.
+#
+# Package option names should be in alphabetical order
+# by the --with option name (the third field),
+# to make things easier for the users.
+
+
+ ALSA_DESCRIP="Advanced Linux Sound Architecture"
+ ALSA_OPTION="asound"
+
+# Check whether --with-asound was given.
+if test "${with_asound+set}" = set; then
+ withval=$with_asound;
+ case ${withval} in
+ n|no)
+ USE_ALSA=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ALSA"
+ ;;
+ *)
+ ALSA_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ALSA"
+ ;;
+ esac
+
+fi
+
+ PBX_ALSA=0
+
+
+
+
+
+
+# BKTR is used for backtrace support on platforms that do not
+# have it natively.
+
+ BKTR_DESCRIP="Stack Backtrace support"
+ BKTR_OPTION="execinfo"
+
+# Check whether --with-execinfo was given.
+if test "${with_execinfo+set}" = set; then
+ withval=$with_execinfo;
+ case ${withval} in
+ n|no)
+ USE_BKTR=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} BKTR"
+ ;;
+ *)
+ BKTR_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} BKTR"
+ ;;
+ esac
+
+fi
+
+ PBX_BKTR=0
+
+
+
+
+
+
+ CAP_DESCRIP="POSIX 1.e capabilities"
+ CAP_OPTION="cap"
+
+# Check whether --with-cap was given.
+if test "${with_cap+set}" = set; then
+ withval=$with_cap;
+ case ${withval} in
+ n|no)
+ USE_CAP=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} CAP"
+ ;;
+ *)
+ CAP_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} CAP"
+ ;;
+ esac
+
+fi
+
+ PBX_CAP=0
+
+
+
+
+
+
+ CURL_DESCRIP="cURL"
+ CURL_OPTION="curl"
+
+# Check whether --with-curl was given.
+if test "${with_curl+set}" = set; then
+ withval=$with_curl;
+ case ${withval} in
+ n|no)
+ USE_CURL=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} CURL"
+ ;;
+ *)
+ CURL_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} CURL"
+ ;;
+ esac
+
+fi
+
+ PBX_CURL=0
+
+
+
+
+
+
+ CURSES_DESCRIP="curses"
+ CURSES_OPTION="curses"
+
+# Check whether --with-curses was given.
+if test "${with_curses+set}" = set; then
+ withval=$with_curses;
+ case ${withval} in
+ n|no)
+ USE_CURSES=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} CURSES"
+ ;;
+ *)
+ CURSES_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} CURSES"
+ ;;
+ esac
+
+fi
+
+ PBX_CURSES=0
+
+
+
+
+
+
+ CRYPTO_DESCRIP="OpenSSL Cryptography support"
+ CRYPTO_OPTION="crypto"
+
+# Check whether --with-crypto was given.
+if test "${with_crypto+set}" = set; then
+ withval=$with_crypto;
+ case ${withval} in
+ n|no)
+ USE_CRYPTO=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} CRYPTO"
+ ;;
+ *)
+ CRYPTO_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} CRYPTO"
+ ;;
+ esac
+
+fi
+
+ PBX_CRYPTO=0
+
+
+
+
+
+
+ GSM_DESCRIP="External GSM library"
+ GSM_OPTION="gsm"
+
+# Check whether --with-gsm was given.
+if test "${with_gsm+set}" = set; then
+ withval=$with_gsm;
+ case ${withval} in
+ n|no)
+ USE_GSM=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} GSM"
+ ;;
+ *)
+ GSM_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} GSM"
+ ;;
+ esac
+
+fi
+
+ PBX_GSM=0
+
+
+
+
+
+
+ GTK_DESCRIP="gtk libraries"
+ GTK_OPTION="gtk"
+
+# Check whether --with-gtk was given.
+if test "${with_gtk+set}" = set; then
+ withval=$with_gtk;
+ case ${withval} in
+ n|no)
+ USE_GTK=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} GTK"
+ ;;
+ *)
+ GTK_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} GTK"
+ ;;
+ esac
+
+fi
+
+ PBX_GTK=0
+
+
+
+
+
+
+ GTK2_DESCRIP="gtk2 libraries"
+ GTK2_OPTION="gtk2"
+
+# Check whether --with-gtk2 was given.
+if test "${with_gtk2+set}" = set; then
+ withval=$with_gtk2;
+ case ${withval} in
+ n|no)
+ USE_GTK2=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} GTK2"
+ ;;
+ *)
+ GTK2_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} GTK2"
+ ;;
+ esac
+
+fi
+
+ PBX_GTK2=0
+
+
+
+
+
+
+ ICONV_DESCRIP="Iconv Library"
+ ICONV_OPTION="iconv"
+
+# Check whether --with-iconv was given.
+if test "${with_iconv+set}" = set; then
+ withval=$with_iconv;
+ case ${withval} in
+ n|no)
+ USE_ICONV=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ICONV"
+ ;;
+ *)
+ ICONV_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ICONV"
+ ;;
+ esac
+
+fi
+
+ PBX_ICONV=0
+
+
+
+
+
+
+ IKSEMEL_DESCRIP="Iksemel Jabber Library"
+ IKSEMEL_OPTION="iksemel"
+
+# Check whether --with-iksemel was given.
+if test "${with_iksemel+set}" = set; then
+ withval=$with_iksemel;
+ case ${withval} in
+ n|no)
+ USE_IKSEMEL=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} IKSEMEL"
+ ;;
+ *)
+ IKSEMEL_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} IKSEMEL"
+ ;;
+ esac
+
+fi
+
+ PBX_IKSEMEL=0
+
+
+
+
+
+
+ IMAP_TK_DESCRIP="UW IMAP Toolkit"
+ IMAP_TK_OPTION="imap"
+
+# Check whether --with-imap was given.
+if test "${with_imap+set}" = set; then
+ withval=$with_imap;
+ case ${withval} in
+ n|no)
+ USE_IMAP_TK=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} IMAP_TK"
+ ;;
+ *)
+ IMAP_TK_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} IMAP_TK"
+ ;;
+ esac
+
+fi
+
+ PBX_IMAP_TK=0
+
+
+
+
+
+
+ ISDNNET_DESCRIP="ISDN4Linux Library"
+ ISDNNET_OPTION="isdnnet"
+
+# Check whether --with-isdnnet was given.
+if test "${with_isdnnet+set}" = set; then
+ withval=$with_isdnnet;
+ case ${withval} in
+ n|no)
+ USE_ISDNNET=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ISDNNET"
+ ;;
+ *)
+ ISDNNET_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ISDNNET"
+ ;;
+ esac
+
+fi
+
+ PBX_ISDNNET=0
+
+
+
+
+
+
+ JACK_DESCRIP="Jack Audio Connection Kit"
+ JACK_OPTION="jack"
+
+# Check whether --with-jack was given.
+if test "${with_jack+set}" = set; then
+ withval=$with_jack;
+ case ${withval} in
+ n|no)
+ USE_JACK=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} JACK"
+ ;;
+ *)
+ JACK_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} JACK"
+ ;;
+ esac
+
+fi
+
+ PBX_JACK=0
+
+
+
+
+
+
+ LTDL_DESCRIP="libtool"
+ LTDL_OPTION="ltdl"
+
+# Check whether --with-ltdl was given.
+if test "${with_ltdl+set}" = set; then
+ withval=$with_ltdl;
+ case ${withval} in
+ n|no)
+ USE_LTDL=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} LTDL"
+ ;;
+ *)
+ LTDL_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} LTDL"
+ ;;
+ esac
+
+fi
+
+ PBX_LTDL=0
+
+
+
+
+
+
+ LUA_DESCRIP="Lua"
+ LUA_OPTION="lua"
+
+# Check whether --with-lua was given.
+if test "${with_lua+set}" = set; then
+ withval=$with_lua;
+ case ${withval} in
+ n|no)
+ USE_LUA=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} LUA"
+ ;;
+ *)
+ LUA_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} LUA"
+ ;;
+ esac
+
+fi
+
+ PBX_LUA=0
+
+
+
+
+
+
+ MISDN_DESCRIP="mISDN User Library"
+ MISDN_OPTION="misdn"
+
+# Check whether --with-misdn was given.
+if test "${with_misdn+set}" = set; then
+ withval=$with_misdn;
+ case ${withval} in
+ n|no)
+ USE_MISDN=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} MISDN"
+ ;;
+ *)
+ MISDN_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} MISDN"
+ ;;
+ esac
+
+fi
+
+ PBX_MISDN=0
+
+
+
+
+
+
+ NBS_DESCRIP="Network Broadcast Sound"
+ NBS_OPTION="nbs"
+
+# Check whether --with-nbs was given.
+if test "${with_nbs+set}" = set; then
+ withval=$with_nbs;
+ case ${withval} in
+ n|no)
+ USE_NBS=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} NBS"
+ ;;
+ *)
+ NBS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} NBS"
+ ;;
+ esac
+
+fi
+
+ PBX_NBS=0
+
+
+
+
+
+
+ NCURSES_DESCRIP="ncurses"
+ NCURSES_OPTION="ncurses"
+
+# Check whether --with-ncurses was given.
+if test "${with_ncurses+set}" = set; then
+ withval=$with_ncurses;
+ case ${withval} in
+ n|no)
+ USE_NCURSES=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} NCURSES"
+ ;;
+ *)
+ NCURSES_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} NCURSES"
+ ;;
+ esac
+
+fi
+
+ PBX_NCURSES=0
+
+
+
+
+
+
+ NETSNMP_DESCRIP="Net-SNMP"
+ NETSNMP_OPTION="netsnmp"
+
+# Check whether --with-netsnmp was given.
+if test "${with_netsnmp+set}" = set; then
+ withval=$with_netsnmp;
+ case ${withval} in
+ n|no)
+ USE_NETSNMP=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} NETSNMP"
+ ;;
+ *)
+ NETSNMP_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} NETSNMP"
+ ;;
+ esac
+
+fi
+
+ PBX_NETSNMP=0
+
+
+
+
+
+
+ NEWT_DESCRIP="newt"
+ NEWT_OPTION="newt"
+
+# Check whether --with-newt was given.
+if test "${with_newt+set}" = set; then
+ withval=$with_newt;
+ case ${withval} in
+ n|no)
+ USE_NEWT=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} NEWT"
+ ;;
+ *)
+ NEWT_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} NEWT"
+ ;;
+ esac
+
+fi
+
+ PBX_NEWT=0
+
+
+
+
+
+
+ UNIXODBC_DESCRIP="unixODBC"
+ UNIXODBC_OPTION="odbc"
+
+# Check whether --with-odbc was given.
+if test "${with_odbc+set}" = set; then
+ withval=$with_odbc;
+ case ${withval} in
+ n|no)
+ USE_UNIXODBC=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} UNIXODBC"
+ ;;
+ *)
+ UNIXODBC_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} UNIXODBC"
+ ;;
+ esac
+
+fi
+
+ PBX_UNIXODBC=0
+
+
+
+
+
+
+ OGG_DESCRIP="OGG"
+ OGG_OPTION="ogg"
+
+# Check whether --with-ogg was given.
+if test "${with_ogg+set}" = set; then
+ withval=$with_ogg;
+ case ${withval} in
+ n|no)
+ USE_OGG=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} OGG"
+ ;;
+ *)
+ OGG_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} OGG"
+ ;;
+ esac
+
+fi
+
+ PBX_OGG=0
+
+
+
+
+
+
+ OSPTK_DESCRIP="OSP Toolkit"
+ OSPTK_OPTION="osptk"
+
+# Check whether --with-osptk was given.
+if test "${with_osptk+set}" = set; then
+ withval=$with_osptk;
+ case ${withval} in
+ n|no)
+ USE_OSPTK=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} OSPTK"
+ ;;
+ *)
+ OSPTK_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} OSPTK"
+ ;;
+ esac
+
+fi
+
+ PBX_OSPTK=0
+
+
+
+
+
+
+ OSS_DESCRIP="Open Sound System"
+ OSS_OPTION="oss"
+
+# Check whether --with-oss was given.
+if test "${with_oss+set}" = set; then
+ withval=$with_oss;
+ case ${withval} in
+ n|no)
+ USE_OSS=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} OSS"
+ ;;
+ *)
+ OSS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} OSS"
+ ;;
+ esac
+
+fi
+
+ PBX_OSS=0
+
+
+
+
+
+
+ PGSQL_DESCRIP="PostgreSQL"
+ PGSQL_OPTION="postgres"
+
+# Check whether --with-postgres was given.
+if test "${with_postgres+set}" = set; then
+ withval=$with_postgres;
+ case ${withval} in
+ n|no)
+ USE_PGSQL=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} PGSQL"
+ ;;
+ *)
+ PGSQL_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} PGSQL"
+ ;;
+ esac
+
+fi
+
+ PBX_PGSQL=0
+
+
+
+
+
+
+ POPT_DESCRIP="popt"
+ POPT_OPTION="popt"
+
+# Check whether --with-popt was given.
+if test "${with_popt+set}" = set; then
+ withval=$with_popt;
+ case ${withval} in
+ n|no)
+ USE_POPT=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} POPT"
+ ;;
+ *)
+ POPT_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} POPT"
+ ;;
+ esac
+
+fi
+
+ PBX_POPT=0
+
+
+
+
+
+
+ PORTAUDIO_DESCRIP="PortAudio"
+ PORTAUDIO_OPTION="portaudio"
+
+# Check whether --with-portaudio was given.
+if test "${with_portaudio+set}" = set; then
+ withval=$with_portaudio;
+ case ${withval} in
+ n|no)
+ USE_PORTAUDIO=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} PORTAUDIO"
+ ;;
+ *)
+ PORTAUDIO_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} PORTAUDIO"
+ ;;
+ esac
+
+fi
+
+ PBX_PORTAUDIO=0
+
+
+
+
+
+
+ PRI_DESCRIP="ISDN PRI"
+ PRI_OPTION="pri"
+
+# Check whether --with-pri was given.
+if test "${with_pri+set}" = set; then
+ withval=$with_pri;
+ case ${withval} in
+ n|no)
+ USE_PRI=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} PRI"
+ ;;
+ *)
+ PRI_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} PRI"
+ ;;
+ esac
+
+fi
+
+ PBX_PRI=0
+
+
+
+
+
+
+ SS7_DESCRIP="ISDN SS7"
+ SS7_OPTION="ss7"
+
+# Check whether --with-ss7 was given.
+if test "${with_ss7+set}" = set; then
+ withval=$with_ss7;
+ case ${withval} in
+ n|no)
+ USE_SS7=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SS7"
+ ;;
+ *)
+ SS7_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SS7"
+ ;;
+ esac
+
+fi
+
+ PBX_SS7=0
+
+
+
+
+
+
+ PWLIB_DESCRIP="PWlib"
+ PWLIB_OPTION="pwlib"
+
+# Check whether --with-pwlib was given.
+if test "${with_pwlib+set}" = set; then
+ withval=$with_pwlib;
+ case ${withval} in
+ n|no)
+ USE_PWLIB=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} PWLIB"
+ ;;
+ *)
+ PWLIB_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} PWLIB"
+ ;;
+ esac
+
+fi
+
+ PBX_PWLIB=0
+
+
+
+
+
+
+ OPENH323_DESCRIP="OpenH323"
+ OPENH323_OPTION="h323"
+
+# Check whether --with-h323 was given.
+if test "${with_h323+set}" = set; then
+ withval=$with_h323;
+ case ${withval} in
+ n|no)
+ USE_OPENH323=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} OPENH323"
+ ;;
+ *)
+ OPENH323_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} OPENH323"
+ ;;
+ esac
+
+fi
+
+ PBX_OPENH323=0
+
+
+
+
+
+
+ RADIUS_DESCRIP="Radius Client"
+ RADIUS_OPTION="radius"
+
+# Check whether --with-radius was given.
+if test "${with_radius+set}" = set; then
+ withval=$with_radius;
+ case ${withval} in
+ n|no)
+ USE_RADIUS=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} RADIUS"
+ ;;
+ *)
+ RADIUS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} RADIUS"
+ ;;
+ esac
+
+fi
+
+ PBX_RADIUS=0
+
+
+
+
+
+
+ SPEEX_DESCRIP="Speex"
+ SPEEX_OPTION="speex"
+
+# Check whether --with-speex was given.
+if test "${with_speex+set}" = set; then
+ withval=$with_speex;
+ case ${withval} in
+ n|no)
+ USE_SPEEX=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SPEEX"
+ ;;
+ *)
+ SPEEX_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SPEEX"
+ ;;
+ esac
+
+fi
+
+ PBX_SPEEX=0
+
+
+
+
+
+
+ SPEEXDSP_DESCRIP="Speexdsp"
+ SPEEXDSP_OPTION="speexdsp"
+
+# Check whether --with-speexdsp was given.
+if test "${with_speexdsp+set}" = set; then
+ withval=$with_speexdsp;
+ case ${withval} in
+ n|no)
+ USE_SPEEXDSP=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SPEEXDSP"
+ ;;
+ *)
+ SPEEXDSP_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SPEEXDSP"
+ ;;
+ esac
+
+fi
+
+ PBX_SPEEXDSP=0
+
+
+
+
+
+
+ SQLITE_DESCRIP="SQLite"
+ SQLITE_OPTION="sqlite"
+
+# Check whether --with-sqlite was given.
+if test "${with_sqlite+set}" = set; then
+ withval=$with_sqlite;
+ case ${withval} in
+ n|no)
+ USE_SQLITE=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SQLITE"
+ ;;
+ *)
+ SQLITE_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SQLITE"
+ ;;
+ esac
+
+fi
+
+ PBX_SQLITE=0
+
+
+
+
+
+
+ SQLITE3_DESCRIP="SQLite"
+ SQLITE3_OPTION="sqlite3"
+
+# Check whether --with-sqlite3 was given.
+if test "${with_sqlite3+set}" = set; then
+ withval=$with_sqlite3;
+ case ${withval} in
+ n|no)
+ USE_SQLITE3=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SQLITE3"
+ ;;
+ *)
+ SQLITE3_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SQLITE3"
+ ;;
+ esac
+
+fi
+
+ PBX_SQLITE3=0
+
+
+
+
+
+
+ SUPPSERV_DESCRIP="mISDN Supplemental Services"
+ SUPPSERV_OPTION="suppserv"
+
+# Check whether --with-suppserv was given.
+if test "${with_suppserv+set}" = set; then
+ withval=$with_suppserv;
+ case ${withval} in
+ n|no)
+ USE_SUPPSERV=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SUPPSERV"
+ ;;
+ *)
+ SUPPSERV_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SUPPSERV"
+ ;;
+ esac
+
+fi
+
+ PBX_SUPPSERV=0
+
+
+
+
+
+
+ OPENSSL_DESCRIP="OpenSSL Secure Sockets Layer support"
+ OPENSSL_OPTION="ssl"
+
+# Check whether --with-ssl was given.
+if test "${with_ssl+set}" = set; then
+ withval=$with_ssl;
+ case ${withval} in
+ n|no)
+ USE_OPENSSL=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} OPENSSL"
+ ;;
+ *)
+ OPENSSL_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} OPENSSL"
+ ;;
+ esac
+
+fi
+
+ PBX_OPENSSL=0
+
+
+
+
+
+
+ FREETDS_DESCRIP="FreeTDS"
+ FREETDS_OPTION="tds"
+
+# Check whether --with-tds was given.
+if test "${with_tds+set}" = set; then
+ withval=$with_tds;
+ case ${withval} in
+ n|no)
+ USE_FREETDS=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} FREETDS"
+ ;;
+ *)
+ FREETDS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} FREETDS"
+ ;;
+ esac
+
+fi
+
+ PBX_FREETDS=0
+
+
+
+
+
+
+ TERMCAP_DESCRIP="Termcap"
+ TERMCAP_OPTION="termcap"
+
+# Check whether --with-termcap was given.
+if test "${with_termcap+set}" = set; then
+ withval=$with_termcap;
+ case ${withval} in
+ n|no)
+ USE_TERMCAP=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} TERMCAP"
+ ;;
+ *)
+ TERMCAP_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} TERMCAP"
+ ;;
+ esac
+
+fi
+
+ PBX_TERMCAP=0
+
+
+
+
+
+
+ TINFO_DESCRIP="Term Info"
+ TINFO_OPTION="tinfo"
+
+# Check whether --with-tinfo was given.
+if test "${with_tinfo+set}" = set; then
+ withval=$with_tinfo;
+ case ${withval} in
+ n|no)
+ USE_TINFO=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} TINFO"
+ ;;
+ *)
+ TINFO_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} TINFO"
+ ;;
+ esac
+
+fi
+
+ PBX_TINFO=0
+
+
+
+
+
+
+ TONEZONE_DESCRIP="tonezone"
+ TONEZONE_OPTION="tonezone"
+
+# Check whether --with-tonezone was given.
+if test "${with_tonezone+set}" = set; then
+ withval=$with_tonezone;
+ case ${withval} in
+ n|no)
+ USE_TONEZONE=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} TONEZONE"
+ ;;
+ *)
+ TONEZONE_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} TONEZONE"
+ ;;
+ esac
+
+fi
+
+ PBX_TONEZONE=0
+
+
+
+
+
+
+ USB_DESCRIP="usb"
+ USB_OPTION="usb"
+
+# Check whether --with-usb was given.
+if test "${with_usb+set}" = set; then
+ withval=$with_usb;
+ case ${withval} in
+ n|no)
+ USE_USB=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} USB"
+ ;;
+ *)
+ USB_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} USB"
+ ;;
+ esac
+
+fi
+
+ PBX_USB=0
+
+
+
+
+
+
+ VORBIS_DESCRIP="Vorbis"
+ VORBIS_OPTION="vorbis"
+
+# Check whether --with-vorbis was given.
+if test "${with_vorbis+set}" = set; then
+ withval=$with_vorbis;
+ case ${withval} in
+ n|no)
+ USE_VORBIS=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} VORBIS"
+ ;;
+ *)
+ VORBIS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} VORBIS"
+ ;;
+ esac
+
+fi
+
+ PBX_VORBIS=0
+
+
+
+
+
+
+ VPB_DESCRIP="Voicetronix API"
+ VPB_OPTION="vpb"
+
+# Check whether --with-vpb was given.
+if test "${with_vpb+set}" = set; then
+ withval=$with_vpb;
+ case ${withval} in
+ n|no)
+ USE_VPB=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} VPB"
+ ;;
+ *)
+ VPB_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} VPB"
+ ;;
+ esac
+
+fi
+
+ PBX_VPB=0
+
+
+
+
+
+
+ X11_DESCRIP="X11 support"
+ X11_OPTION="x11"
+
+# Check whether --with-x11 was given.
+if test "${with_x11+set}" = set; then
+ withval=$with_x11;
+ case ${withval} in
+ n|no)
+ USE_X11=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} X11"
+ ;;
+ *)
+ X11_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} X11"
+ ;;
+ esac
+
+fi
+
+ PBX_X11=0
+
+
+
+
+
+
+ ZLIB_DESCRIP="zlib"
+ ZLIB_OPTION="z"
+
+# Check whether --with-z was given.
+if test "${with_z+set}" = set; then
+ withval=$with_z;
+ case ${withval} in
+ n|no)
+ USE_ZLIB=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ZLIB"
+ ;;
+ *)
+ ZLIB_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ZLIB"
+ ;;
+ esac
+
+fi
+
+ PBX_ZLIB=0
+
+
+
+
+
+
+ ZAPTEL_DESCRIP="Zaptel"
+ ZAPTEL_OPTION="zaptel"
+
+# Check whether --with-zaptel was given.
+if test "${with_zaptel+set}" = set; then
+ withval=$with_zaptel;
+ case ${withval} in
+ n|no)
+ USE_ZAPTEL=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL"
+ ;;
+ *)
+ ZAPTEL_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL"
+ ;;
+ esac
+
+fi
+
+ PBX_ZAPTEL=0
+
+
+
+
+
+
+ ZAPTEL_TRANSCODE_DESCRIP="Zaptel Transcoder Support"
+ ZAPTEL_TRANSCODE_OPTION="zaptel_transcode"
+
+# Check whether --with-zaptel_transcode was given.
+if test "${with_zaptel_transcode+set}" = set; then
+ withval=$with_zaptel_transcode;
+ case ${withval} in
+ n|no)
+ USE_ZAPTEL_TRANSCODE=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_TRANSCODE"
+ ;;
+ *)
+ ZAPTEL_TRANSCODE_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_TRANSCODE"
+ ;;
+ esac
+
+fi
+
+ PBX_ZAPTEL_TRANSCODE=0
+
+
+
+
+
+
+ ZAPTEL_VLDTMF_DESCRIP="Zaptel VLDTMF Support"
+ ZAPTEL_VLDTMF_OPTION="zaptel_vldtmf"
+
+# Check whether --with-zaptel_vldtmf was given.
+if test "${with_zaptel_vldtmf+set}" = set; then
+ withval=$with_zaptel_vldtmf;
+ case ${withval} in
+ n|no)
+ USE_ZAPTEL_VLDTMF=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_VLDTMF"
+ ;;
+ *)
+ ZAPTEL_VLDTMF_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_VLDTMF"
+ ;;
+ esac
+
+fi
+
+ PBX_ZAPTEL_VLDTMF=0
+
+
+
+
+
+
+ ZAPTEL_HWGAIN_DESCRIP="Zaptel Hardware Gain Support"
+ ZAPTEL_HWGAIN_OPTION="zaptel_hwgain"
+
+# Check whether --with-zaptel_hwgain was given.
+if test "${with_zaptel_hwgain+set}" = set; then
+ withval=$with_zaptel_hwgain;
+ case ${withval} in
+ n|no)
+ USE_ZAPTEL_HWGAIN=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_HWGAIN"
+ ;;
+ *)
+ ZAPTEL_HWGAIN_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_HWGAIN"
+ ;;
+ esac
+
+fi
+
+ PBX_ZAPTEL_HWGAIN=0
+
+
+
+
+
+
+ ZAPTEL_ECHOCANPARAMS_DESCRIP="Zaptel Echo Canceler Parameter Support"
+ ZAPTEL_ECHOCANPARAMS_OPTION="zaptel_echocanparams"
+
+# Check whether --with-zaptel_echocanparams was given.
+if test "${with_zaptel_echocanparams+set}" = set; then
+ withval=$with_zaptel_echocanparams;
+ case ${withval} in
+ n|no)
+ USE_ZAPTEL_ECHOCANPARAMS=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_ECHOCANPARAMS"
+ ;;
+ *)
+ ZAPTEL_ECHOCANPARAMS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_ECHOCANPARAMS"
+ ;;
+ esac
+
+fi
+
+ PBX_ZAPTEL_ECHOCANPARAMS=0
+
+
+
+
+
+
+ ZAPTEL_CHANALARMS_DESCRIP="Zaptel Channel Alarm Support"
+ ZAPTEL_CHANALARMS_OPTION="zaptel_chanalarms"
+
+# Check whether --with-zaptel_chanalarms was given.
+if test "${with_zaptel_chanalarms+set}" = set; then
+ withval=$with_zaptel_chanalarms;
+ case ${withval} in
+ n|no)
+ USE_ZAPTEL_CHANALARMS=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_CHANALARMS"
+ ;;
+ *)
+ ZAPTEL_CHANALARMS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} ZAPTEL_CHANALARMS"
+ ;;
+ esac
+
+fi
+
+ PBX_ZAPTEL_CHANALARMS=0
+
+
+
+
+
+
+
+ SDL_DESCRIP="Sdl"
+ SDL_OPTION="sdl"
+
+# Check whether --with-sdl was given.
+if test "${with_sdl+set}" = set; then
+ withval=$with_sdl;
+ case ${withval} in
+ n|no)
+ USE_SDL=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SDL"
+ ;;
+ *)
+ SDL_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SDL"
+ ;;
+ esac
+
+fi
+
+ PBX_SDL=0
+
+
+
+
+
+
+ SDL_IMAGE_DESCRIP="Sdl Image library"
+ SDL_IMAGE_OPTION="SDL_image"
+
+# Check whether --with-SDL_image was given.
+if test "${with_SDL_image+set}" = set; then
+ withval=$with_SDL_image;
+ case ${withval} in
+ n|no)
+ USE_SDL_IMAGE=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} SDL_IMAGE"
+ ;;
+ *)
+ SDL_IMAGE_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} SDL_IMAGE"
+ ;;
+ esac
+
+fi
+
+ PBX_SDL_IMAGE=0
+
+
+
+
+
+
+ FFMPEG_DESCRIP="Ffmpeg and avcodec library"
+ FFMPEG_OPTION="avcodec"
+
+# Check whether --with-avcodec was given.
+if test "${with_avcodec+set}" = set; then
+ withval=$with_avcodec;
+ case ${withval} in
+ n|no)
+ USE_FFMPEG=no
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} FFMPEG"
+ ;;
+ *)
+ FFMPEG_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} FFMPEG"
+ ;;
+ esac
+
+fi
+
+ PBX_FFMPEG=0
+
+
+
+
+
+
+# check for basic system features and functionality before
+# checking for package libraries
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments. Useless!
+{ echo "$as_me:$LINENO: checking for working alloca.h" >&5
+echo $ECHO_N "checking for working alloca.h... $ECHO_C" >&6; }
+if test "${ac_cv_working_alloca_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <alloca.h>
+int
+main ()
+{
+char *p = (char *) alloca (2 * sizeof (int));
+ if (p) return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_working_alloca_h=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_working_alloca_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5
+echo "${ECHO_T}$ac_cv_working_alloca_h" >&6; }
+if test $ac_cv_working_alloca_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA_H 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for alloca" >&5
+echo $ECHO_N "checking for alloca... $ECHO_C" >&6; }
+if test "${ac_cv_func_alloca_works+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+# else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif
+# endif
+# endif
+# endif
+#endif
+
+int
+main ()
+{
+char *p = (char *) alloca (1);
+ if (p) return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_func_alloca_works=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_alloca_works=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5
+echo "${ECHO_T}$ac_cv_func_alloca_works" >&6; }
+
+if test $ac_cv_func_alloca_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA 1
+_ACEOF
+
+else
+ # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+# that cause trouble. Some versions do not even contain alloca or
+# contain a buggy version. If you still want to use their alloca,
+# use ar to extract alloca.o from them instead of compiling alloca.c.
+
+ALLOCA=\${LIBOBJDIR}alloca.$ac_objext
+
+cat >>confdefs.h <<\_ACEOF
+#define C_ALLOCA 1
+_ACEOF
+
+
+{ echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5
+echo $ECHO_N "checking whether \`alloca.c' needs Cray hooks... $ECHO_C" >&6; }
+if test "${ac_cv_os_cray+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#if defined CRAY && ! defined CRAY2
+webecray
+#else
+wenotbecray
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "webecray" >/dev/null 2>&1; then
+ ac_cv_os_cray=yes
+else
+ ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5
+echo "${ECHO_T}$ac_cv_os_cray" >&6; }
+if test $ac_cv_os_cray = yes; then
+ for ac_func in _getb67 GETB67 getb67; do
+ as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define CRAY_STACKSEG_END $ac_func
+_ACEOF
+
+ break
+fi
+
+ done
+fi
+
+{ echo "$as_me:$LINENO: checking stack direction for C alloca" >&5
+echo $ECHO_N "checking stack direction for C alloca... $ECHO_C" >&6; }
+if test "${ac_cv_c_stack_direction+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_c_stack_direction=0
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+find_stack_direction ()
+{
+ static char *addr = 0;
+ auto char dummy;
+ if (addr == 0)
+ {
+ addr = &dummy;
+ return find_stack_direction ();
+ }
+ else
+ return (&dummy > addr) ? 1 : -1;
+}
+
+int
+main ()
+{
+ return find_stack_direction () < 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_c_stack_direction=1
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_stack_direction=-1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5
+echo "${ECHO_T}$ac_cv_c_stack_direction" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+_ACEOF
+
+
+fi
+
+
+
+
+
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5
+echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6; }
+if test "${ac_cv_search_opendir+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_opendir=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_opendir+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_opendir+set}" = set; then
+ :
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6; }
+if test "${ac_cv_search_opendir+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_opendir=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_opendir+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_opendir+set}" = set; then
+ :
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5
+echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_wait_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_sys_wait_h=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_sys_wait_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_WAIT_H 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_header in arpa/inet.h fcntl.h inttypes.h libintl.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h strings.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utime.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+for ac_header in winsock.h winsock2.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+# Check whether --enable-largefile was given.
+if test "${enable_largefile+set}" = set; then
+ enableval=$enable_largefile;
+fi
+
+if test "$enable_largefile" != no; then
+
+ { echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5
+echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6; }
+if test "${ac_cv_sys_largefile_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_sys_largefile_CC=no
+ if test "$GCC" != yes; then
+ ac_save_CC=$CC
+ while :; do
+ # IRIX 6.2 and later do not support large files by default,
+ # so use the C compiler's -n32 option if that helps.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ CC="$CC -n32"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_sys_largefile_CC=' -n32'; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ break
+ done
+ CC=$ac_save_CC
+ rm -f conftest.$ac_ext
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5
+echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6; }
+ if test "$ac_cv_sys_largefile_CC" != no; then
+ CC=$CC$ac_cv_sys_largefile_CC
+ fi
+
+ { echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5
+echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6; }
+if test "${ac_cv_sys_file_offset_bits+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_sys_file_offset_bits=no; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_sys_file_offset_bits=64; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_cv_sys_file_offset_bits=unknown
+ break
+done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5
+echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6; }
+case $ac_cv_sys_file_offset_bits in #(
+ no | unknown) ;;
+ *)
+cat >>confdefs.h <<_ACEOF
+#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
+_ACEOF
+;;
+esac
+rm -f conftest*
+ if test $ac_cv_sys_file_offset_bits = unknown; then
+ { echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5
+echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6; }
+if test "${ac_cv_sys_large_files+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_sys_large_files=no; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _LARGE_FILES 1
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_sys_large_files=1; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_cv_sys_large_files=unknown
+ break
+done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5
+echo "${ECHO_T}$ac_cv_sys_large_files" >&6; }
+case $ac_cv_sys_large_files in #(
+ no | unknown) ;;
+ *)
+cat >>confdefs.h <<_ACEOF
+#define _LARGE_FILES $ac_cv_sys_large_files
+_ACEOF
+;;
+esac
+rm -f conftest*
+ fi
+fi
+
+
+# Checks for typedefs, structures, and compiler characteristics.
+{ echo "$as_me:$LINENO: checking for stdbool.h that conforms to C99" >&5
+echo $ECHO_N "checking for stdbool.h that conforms to C99... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdbool_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include <stdbool.h>
+#ifndef bool
+ "error: bool is not defined"
+#endif
+#ifndef false
+ "error: false is not defined"
+#endif
+#if false
+ "error: false is not 0"
+#endif
+#ifndef true
+ "error: true is not defined"
+#endif
+#if true != 1
+ "error: true is not 1"
+#endif
+#ifndef __bool_true_false_are_defined
+ "error: __bool_true_false_are_defined is not defined"
+#endif
+
+ struct s { _Bool s: 1; _Bool t; } s;
+
+ char a[true == 1 ? 1 : -1];
+ char b[false == 0 ? 1 : -1];
+ char c[__bool_true_false_are_defined == 1 ? 1 : -1];
+ char d[(bool) 0.5 == true ? 1 : -1];
+ bool e = &s;
+ char f[(_Bool) 0.0 == false ? 1 : -1];
+ char g[true];
+ char h[sizeof (_Bool)];
+ char i[sizeof s.t];
+ enum { j = false, k = true, l = false * true, m = true * 256 };
+ _Bool n[m];
+ char o[sizeof n == m * sizeof n[0] ? 1 : -1];
+ char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
+# if defined __xlc__ || defined __GNUC__
+ /* Catch a bug in IBM AIX xlc compiler version 6.0.0.0
+ reported by James Lemley on 2005-10-05; see
+ http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html
+ This test is not quite right, since xlc is allowed to
+ reject this program, as the initializer for xlcbug is
+ not one of the forms that C requires support for.
+ However, doing the test right would require a runtime
+ test, and that would make cross-compilation harder.
+ Let us hope that IBM fixes the xlc bug, and also adds
+ support for this kind of constant expression. In the
+ meantime, this test will reject xlc, which is OK, since
+ our stdbool.h substitute should suffice. We also test
+ this with GCC, where it should work, to detect more
+ quickly whether someone messes up the test in the
+ future. */
+ char digs[] = "0123456789";
+ int xlcbug = 1 / (&(digs + 5)[-2 + (bool) 1] == &digs[4] ? 1 : -1);
+# endif
+ /* Catch a bug in an HP-UX C compiler. See
+ http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
+ http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
+ */
+ _Bool q = true;
+ _Bool *pq = &q;
+
+int
+main ()
+{
+
+ *pq |= q;
+ *pq |= ! q;
+ /* Refer to every declared value, to avoid compiler optimizations. */
+ return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
+ + !m + !n + !o + !p + !q + !pq);
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdbool_h=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdbool_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdbool_h" >&5
+echo "${ECHO_T}$ac_cv_header_stdbool_h" >&6; }
+{ echo "$as_me:$LINENO: checking for _Bool" >&5
+echo $ECHO_N "checking for _Bool... $ECHO_C" >&6; }
+if test "${ac_cv_type__Bool+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef _Bool ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type__Bool=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type__Bool=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type__Bool" >&5
+echo "${ECHO_T}$ac_cv_type__Bool" >&6; }
+if test $ac_cv_type__Bool = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE__BOOL 1
+_ACEOF
+
+
+fi
+
+if test $ac_cv_header_stdbool_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_STDBOOL_H 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; }
+if test "${ac_cv_c_const+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+ /* Ultrix mips cc rejects this. */
+ typedef int charset[2];
+ const charset cs;
+ /* SunOS 4.1.1 cc rejects this. */
+ char const *const *pcpcc;
+ char **ppc;
+ /* NEC SVR4.0.2 mips cc rejects this. */
+ struct point {int x, y;};
+ static struct point const zero = {0,0};
+ /* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in
+ an arm of an if-expression whose if-part is not a constant
+ expression */
+ const char *g = "string";
+ pcpcc = &g + (g ? g-g : 0);
+ /* HPUX 7.0 cc rejects these. */
+ ++pcpcc;
+ ppc = (char**) pcpcc;
+ pcpcc = (char const *const *) ppc;
+ { /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+ if (s) return 0;
+ }
+ { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+ }
+ { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+ }
+ { /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+ }
+ { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+ if (!foo) return 0;
+ }
+ return !cs[0] && !zero.x;
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_const=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_c_const=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5
+echo "${ECHO_T}$ac_cv_c_const" >&6; }
+if test $ac_cv_c_const = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define const
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5
+echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6; }
+if test "${ac_cv_type_uid_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5
+echo "${ECHO_T}$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define uid_t int
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define gid_t int
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for inline" >&5
+echo $ECHO_N "checking for inline... $ECHO_C" >&6; }
+if test "${ac_cv_c_inline+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo () {return 0; }
+$ac_kw foo_t foo () {return 0; }
+#endif
+
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_inline=$ac_kw
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5
+echo "${ECHO_T}$ac_cv_c_inline" >&6; }
+
+
+case $ac_cv_c_inline in
+ inline | yes) ;;
+ *)
+ case $ac_cv_c_inline in
+ no) ac_val=;;
+ *) ac_val=$ac_cv_c_inline;;
+ esac
+ cat >>confdefs.h <<_ACEOF
+#ifndef __cplusplus
+#define inline $ac_val
+#endif
+_ACEOF
+ ;;
+esac
+
+{ echo "$as_me:$LINENO: checking for mode_t" >&5
+echo $ECHO_N "checking for mode_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_mode_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef mode_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_mode_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_mode_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_mode_t" >&5
+echo "${ECHO_T}$ac_cv_type_mode_t" >&6; }
+if test $ac_cv_type_mode_t = yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define mode_t int
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for off_t" >&5
+echo $ECHO_N "checking for off_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_off_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef off_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_off_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_off_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5
+echo "${ECHO_T}$ac_cv_type_off_t" >&6; }
+if test $ac_cv_type_off_t = yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long int
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for pid_t" >&5
+echo $ECHO_N "checking for pid_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_pid_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef pid_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_pid_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_pid_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5
+echo "${ECHO_T}$ac_cv_type_pid_t" >&6; }
+if test $ac_cv_type_pid_t = yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define pid_t int
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for size_t" >&5
+echo $ECHO_N "checking for size_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_size_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef size_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_size_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_size_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5
+echo "${ECHO_T}$ac_cv_type_size_t" >&6; }
+if test $ac_cv_type_size_t = yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for struct stat.st_blksize" >&5
+echo $ECHO_N "checking for struct stat.st_blksize... $ECHO_C" >&6; }
+if test "${ac_cv_member_struct_stat_st_blksize+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (ac_aggr.st_blksize)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_stat_st_blksize=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (sizeof ac_aggr.st_blksize)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_stat_st_blksize=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_member_struct_stat_st_blksize=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_blksize" >&5
+echo "${ECHO_T}$ac_cv_member_struct_stat_st_blksize" >&6; }
+if test $ac_cv_member_struct_stat_st_blksize = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5
+echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6; }
+if test "${ac_cv_header_time+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_time=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_time=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5
+echo "${ECHO_T}$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TIME_WITH_SYS_TIME 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
+echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6; }
+if test "${ac_cv_struct_tm+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+ int *p = &tm.tm_sec;
+ return !p;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_struct_tm=time.h
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_struct_tm=sys/time.h
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5
+echo "${ECHO_T}$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TM_IN_SYS_TIME 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for working volatile" >&5
+echo $ECHO_N "checking for working volatile... $ECHO_C" >&6; }
+if test "${ac_cv_c_volatile+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+volatile int x;
+int * volatile y = (int *) 0;
+return !x && !y;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_volatile=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_c_volatile=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_volatile" >&5
+echo "${ECHO_T}$ac_cv_c_volatile" >&6; }
+if test $ac_cv_c_volatile = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define volatile
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for ptrdiff_t" >&5
+echo $ECHO_N "checking for ptrdiff_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_ptrdiff_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef ptrdiff_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_ptrdiff_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_ptrdiff_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_ptrdiff_t" >&5
+echo "${ECHO_T}$ac_cv_type_ptrdiff_t" >&6; }
+if test $ac_cv_type_ptrdiff_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PTRDIFF_T 1
+_ACEOF
+
+
+fi
+
+
+# Checks for library functions.
+
+for ac_header in unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ echo "$as_me:$LINENO: checking for working chown" >&5
+echo $ECHO_N "checking for working chown... $ECHO_C" >&6; }
+if test "${ac_cv_func_chown_works+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_chown_works=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <fcntl.h>
+
+int
+main ()
+{
+ char *f = "conftest.chown";
+ struct stat before, after;
+
+ if (creat (f, 0600) < 0)
+ return 1;
+ if (stat (f, &before) < 0)
+ return 1;
+ if (chown (f, (uid_t) -1, (gid_t) -1) == -1)
+ return 1;
+ if (stat (f, &after) < 0)
+ return 1;
+ return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid);
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_chown_works=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_chown_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+rm -f conftest.chown
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_chown_works" >&5
+echo "${ECHO_T}$ac_cv_func_chown_works" >&6; }
+if test $ac_cv_func_chown_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CHOWN 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking whether closedir returns void" >&5
+echo $ECHO_N "checking whether closedir returns void... $ECHO_C" >&6; }
+if test "${ac_cv_func_closedir_void+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_closedir_void=yes
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header_dirent>
+#ifndef __cplusplus
+int closedir ();
+#endif
+
+int
+main ()
+{
+return closedir (opendir (".")) != 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_closedir_void=no
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_closedir_void=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_closedir_void" >&5
+echo "${ECHO_T}$ac_cv_func_closedir_void" >&6; }
+if test $ac_cv_func_closedir_void = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define CLOSEDIR_VOID 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for error_at_line" >&5
+echo $ECHO_N "checking for error_at_line... $ECHO_C" >&6; }
+if test "${ac_cv_lib_error_at_line+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <error.h>
+int
+main ()
+{
+error_at_line (0, 0, "", 0, "an error occurred");
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_error_at_line=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_error_at_line=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_error_at_line" >&5
+echo "${ECHO_T}$ac_cv_lib_error_at_line" >&6; }
+if test $ac_cv_lib_error_at_line = no; then
+ case " $LIBOBJS " in
+ *" error.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS error.$ac_objext"
+ ;;
+esac
+
+fi
+
+
+for ac_header in vfork.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_func in fork vfork
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+ { echo "$as_me:$LINENO: checking for working fork" >&5
+echo $ECHO_N "checking for working fork... $ECHO_C" >&6; }
+if test "${ac_cv_func_fork_works+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_fork_works=cross
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_fork_works=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_fork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5
+echo "${ECHO_T}$ac_cv_func_fork_works" >&6; }
+
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp* | *-*-uclinux* | *-*-linux-uclibc* )
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ { echo "$as_me:$LINENO: checking for working vfork" >&5
+echo $ECHO_N "checking for working vfork... $ECHO_C" >&6; }
+if test "${ac_cv_func_vfork_works+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_vfork_works=cross
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Thanks to Paul Eggert for this test. */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent. The compiler
+ is told about this with #include <vfork.h>, but some compilers
+ (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
+ static variable whose address is put into a register that is
+ clobbered by the vfork. */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0) {
+ perror ("vfork");
+ _exit(2);
+ }
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+
+int
+main ()
+{
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test (0);
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems. This
+ test uses lots of local variables, at least as many local
+ variables as main has allocated so far including compiler
+ temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
+ 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
+ reuse the register of parent for one of the local variables,
+ since it will think that parent can't possibly be used any more
+ in this routine. Assigning to the local variable will thus
+ munge parent in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+ from child file descriptors. If the child closes a descriptor
+ before it execs or exits, this munges the parent's descriptor
+ as well. Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ return (
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_vfork_works=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_vfork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5
+echo "${ECHO_T}$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_WORKING_VFORK 1
+_ACEOF
+
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define vfork fork
+_ACEOF
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_WORKING_FORK 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for _LARGEFILE_SOURCE value needed for large files" >&5
+echo $ECHO_N "checking for _LARGEFILE_SOURCE value needed for large files... $ECHO_C" >&6; }
+if test "${ac_cv_sys_largefile_source+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h> /* for off_t */
+ #include <stdio.h>
+int
+main ()
+{
+int (*fp) (FILE *, off_t, int) = fseeko;
+ return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_sys_largefile_source=no; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _LARGEFILE_SOURCE 1
+#include <sys/types.h> /* for off_t */
+ #include <stdio.h>
+int
+main ()
+{
+int (*fp) (FILE *, off_t, int) = fseeko;
+ return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_sys_largefile_source=1; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_cv_sys_largefile_source=unknown
+ break
+done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_source" >&5
+echo "${ECHO_T}$ac_cv_sys_largefile_source" >&6; }
+case $ac_cv_sys_largefile_source in #(
+ no | unknown) ;;
+ *)
+cat >>confdefs.h <<_ACEOF
+#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source
+_ACEOF
+;;
+esac
+rm -f conftest*
+
+# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug
+# in glibc 2.1.3, but that breaks too many other things.
+# If you want fseeko and ftello with glibc, upgrade to a fixed glibc.
+if test $ac_cv_sys_largefile_source != unknown; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_FSEEKO 1
+_ACEOF
+
+fi
+
+if test $ac_cv_c_compiler_gnu = yes; then
+ { echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5
+echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6; }
+if test "${ac_cv_prog_gcc_traditional+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_pattern="Autoconf.*'x'"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "$ac_pattern" >/dev/null 2>&1; then
+ ac_cv_prog_gcc_traditional=yes
+else
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "$ac_pattern" >/dev/null 2>&1; then
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5
+echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6; }
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+# XXX: these are commented out until we determine whether it matters if our malloc()
+# acts exactly like glibc's or not
+# AC_FUNC_MALLOC
+# AC_FUNC_REALLOC
+{ echo "$as_me:$LINENO: checking for working memcmp" >&5
+echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6; }
+if test "${ac_cv_func_memcmp_working+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_memcmp_working=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Some versions of memcmp are not 8-bit clean. */
+ char c0 = '\100', c1 = '\200', c2 = '\201';
+ if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
+ return 1;
+
+ /* The Next x86 OpenStep bug shows up only when comparing 16 bytes
+ or more and with at least one buffer not starting on a 4-byte boundary.
+ William Lewis provided this test program. */
+ {
+ char foo[21];
+ char bar[21];
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ char *a = foo + i;
+ char *b = bar + i;
+ strcpy (a, "--------01111111");
+ strcpy (b, "--------10000000");
+ if (memcmp (a, b, 16) >= 0)
+ return 1;
+ }
+ return 0;
+ }
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_memcmp_working=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_memcmp_working=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5
+echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6; }
+test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
+ *" memcmp.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
+ ;;
+esac
+
+
+
+
+
+
+for ac_header in $ac_header_list
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+
+
+for ac_func in $ac_func_list
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ echo "$as_me:$LINENO: checking for working mktime" >&5
+echo $ECHO_N "checking for working mktime... $ECHO_C" >&6; }
+if test "${ac_cv_func_working_mktime+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_working_mktime=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Test program from Paul Eggert and Tony Leneis. */
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifndef HAVE_ALARM
+# define alarm(X) /* empty */
+#endif
+
+/* Work around redefinition to rpl_putenv by other config tests. */
+#undef putenv
+
+static time_t time_t_max;
+static time_t time_t_min;
+
+/* Values we'll use to set the TZ environment variable. */
+static char *tz_strings[] = {
+ (char *) 0, "TZ=GMT0", "TZ=JST-9",
+ "TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00"
+};
+#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))
+
+/* Return 0 if mktime fails to convert a date in the spring-forward gap.
+ Based on a problem report from Andreas Jaeger. */
+static int
+spring_forward_gap ()
+{
+ /* glibc (up to about 1998-10-07) failed this test. */
+ struct tm tm;
+
+ /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
+ instead of "TZ=America/Vancouver" in order to detect the bug even
+ on systems that don't support the Olson extension, or don't have the
+ full zoneinfo tables installed. */
+ putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");
+
+ tm.tm_year = 98;
+ tm.tm_mon = 3;
+ tm.tm_mday = 5;
+ tm.tm_hour = 2;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ return mktime (&tm) != (time_t) -1;
+}
+
+static int
+mktime_test1 (now)
+ time_t now;
+{
+ struct tm *lt;
+ return ! (lt = localtime (&now)) || mktime (lt) == now;
+}
+
+static int
+mktime_test (now)
+ time_t now;
+{
+ return (mktime_test1 (now)
+ && mktime_test1 ((time_t) (time_t_max - now))
+ && mktime_test1 ((time_t) (time_t_min + now)));
+}
+
+static int
+irix_6_4_bug ()
+{
+ /* Based on code from Ariel Faigon. */
+ struct tm tm;
+ tm.tm_year = 96;
+ tm.tm_mon = 3;
+ tm.tm_mday = 0;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ mktime (&tm);
+ return tm.tm_mon == 2 && tm.tm_mday == 31;
+}
+
+static int
+bigtime_test (j)
+ int j;
+{
+ struct tm tm;
+ time_t now;
+ tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j;
+ now = mktime (&tm);
+ if (now != (time_t) -1)
+ {
+ struct tm *lt = localtime (&now);
+ if (! (lt
+ && lt->tm_year == tm.tm_year
+ && lt->tm_mon == tm.tm_mon
+ && lt->tm_mday == tm.tm_mday
+ && lt->tm_hour == tm.tm_hour
+ && lt->tm_min == tm.tm_min
+ && lt->tm_sec == tm.tm_sec
+ && lt->tm_yday == tm.tm_yday
+ && lt->tm_wday == tm.tm_wday
+ && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
+ == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+year_2050_test ()
+{
+ /* The correct answer for 2050-02-01 00:00:00 in Pacific time,
+ ignoring leap seconds. */
+ unsigned long int answer = 2527315200UL;
+
+ struct tm tm;
+ time_t t;
+ tm.tm_year = 2050 - 1900;
+ tm.tm_mon = 2 - 1;
+ tm.tm_mday = 1;
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+
+ /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
+ instead of "TZ=America/Vancouver" in order to detect the bug even
+ on systems that don't support the Olson extension, or don't have the
+ full zoneinfo tables installed. */
+ putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");
+
+ t = mktime (&tm);
+
+ /* Check that the result is either a failure, or close enough
+ to the correct answer that we can assume the discrepancy is
+ due to leap seconds. */
+ return (t == (time_t) -1
+ || (0 < t && answer - 120 <= t && t <= answer + 120));
+}
+
+int
+main ()
+{
+ time_t t, delta;
+ int i, j;
+
+ /* This test makes some buggy mktime implementations loop.
+ Give up after 60 seconds; a mktime slower than that
+ isn't worth using anyway. */
+ alarm (60);
+
+ for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2)
+ continue;
+ time_t_max--;
+ if ((time_t) -1 < 0)
+ for (time_t_min = -1; (time_t) (time_t_min * 2) < 0; time_t_min *= 2)
+ continue;
+ delta = time_t_max / 997; /* a suitable prime number */
+ for (i = 0; i < N_STRINGS; i++)
+ {
+ if (tz_strings[i])
+ putenv (tz_strings[i]);
+
+ for (t = 0; t <= time_t_max - delta; t += delta)
+ if (! mktime_test (t))
+ return 1;
+ if (! (mktime_test ((time_t) 1)
+ && mktime_test ((time_t) (60 * 60))
+ && mktime_test ((time_t) (60 * 60 * 24))))
+ return 1;
+
+ for (j = 1; 0 < j; j *= 2)
+ if (! bigtime_test (j))
+ return 1;
+ if (! bigtime_test (j - 1))
+ return 1;
+ }
+ return ! (irix_6_4_bug () && spring_forward_gap () && year_2050_test ());
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_working_mktime=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_working_mktime=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_working_mktime" >&5
+echo "${ECHO_T}$ac_cv_func_working_mktime" >&6; }
+if test $ac_cv_func_working_mktime = no; then
+ case " $LIBOBJS " in
+ *" mktime.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS mktime.$ac_objext"
+ ;;
+esac
+
+fi
+
+
+
+for ac_header in stdlib.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_func in getpagesize
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+{ echo "$as_me:$LINENO: checking for working mmap" >&5
+echo $ECHO_N "checking for working mmap... $ECHO_C" >&6; }
+if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_mmap_fixed_mapped=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+/* malloc might have been renamed as rpl_malloc. */
+#undef malloc
+
+/* Thanks to Mike Haertel and Jim Avera for this test.
+ Here is a matrix of mmap possibilities:
+ mmap private not fixed
+ mmap private fixed at somewhere currently unmapped
+ mmap private fixed at somewhere already mapped
+ mmap shared not fixed
+ mmap shared fixed at somewhere currently unmapped
+ mmap shared fixed at somewhere already mapped
+ For private mappings, we should verify that changes cannot be read()
+ back from the file, nor mmap's back from the file at a different
+ address. (There have been systems where private was not correctly
+ implemented like the infamous i386 svr4.0, and systems where the
+ VM page cache was not coherent with the file system buffer cache
+ like early versions of FreeBSD and possibly contemporary NetBSD.)
+ For shared mappings, we should conversely verify that changes get
+ propagated back to all the places they're supposed to be.
+
+ Grep wants private fixed already mapped.
+ The main things grep needs to know about mmap are:
+ * does it exist and is it safe to write into the mmap'd area
+ * how to use it (BSD variants) */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#if !defined STDC_HEADERS && !defined HAVE_STDLIB_H
+char *malloc ();
+#endif
+
+/* This mess was copied from the GNU getpagesize.h. */
+#ifndef HAVE_GETPAGESIZE
+/* Assume that all systems that can run configure have sys/param.h. */
+# ifndef HAVE_SYS_PARAM_H
+# define HAVE_SYS_PARAM_H 1
+# endif
+
+# ifdef _SC_PAGESIZE
+# define getpagesize() sysconf(_SC_PAGESIZE)
+# else /* no _SC_PAGESIZE */
+# ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+# ifdef EXEC_PAGESIZE
+# define getpagesize() EXEC_PAGESIZE
+# else /* no EXEC_PAGESIZE */
+# ifdef NBPG
+# define getpagesize() NBPG * CLSIZE
+# ifndef CLSIZE
+# define CLSIZE 1
+# endif /* no CLSIZE */
+# else /* no NBPG */
+# ifdef NBPC
+# define getpagesize() NBPC
+# else /* no NBPC */
+# ifdef PAGESIZE
+# define getpagesize() PAGESIZE
+# endif /* PAGESIZE */
+# endif /* no NBPC */
+# endif /* no NBPG */
+# endif /* no EXEC_PAGESIZE */
+# else /* no HAVE_SYS_PARAM_H */
+# define getpagesize() 8192 /* punt totally */
+# endif /* no HAVE_SYS_PARAM_H */
+# endif /* no _SC_PAGESIZE */
+
+#endif /* no HAVE_GETPAGESIZE */
+
+int
+main ()
+{
+ char *data, *data2, *data3;
+ int i, pagesize;
+ int fd;
+
+ pagesize = getpagesize ();
+
+ /* First, make a file with some known garbage in it. */
+ data = (char *) malloc (pagesize);
+ if (!data)
+ return 1;
+ for (i = 0; i < pagesize; ++i)
+ *(data + i) = rand ();
+ umask (0);
+ fd = creat ("conftest.mmap", 0600);
+ if (fd < 0)
+ return 1;
+ if (write (fd, data, pagesize) != pagesize)
+ return 1;
+ close (fd);
+
+ /* Next, try to mmap the file at a fixed address which already has
+ something else allocated at it. If we can, also make sure that
+ we see the same garbage. */
+ fd = open ("conftest.mmap", O_RDWR);
+ if (fd < 0)
+ return 1;
+ data2 = (char *) malloc (2 * pagesize);
+ if (!data2)
+ return 1;
+ data2 += (pagesize - ((long int) data2 & (pagesize - 1))) & (pagesize - 1);
+ if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED, fd, 0L))
+ return 1;
+ for (i = 0; i < pagesize; ++i)
+ if (*(data + i) != *(data2 + i))
+ return 1;
+
+ /* Finally, make sure that changes to the mapped area do not
+ percolate back to the file as seen by read(). (This is a bug on
+ some variants of i386 svr4.0.) */
+ for (i = 0; i < pagesize; ++i)
+ *(data2 + i) = *(data2 + i) + 1;
+ data3 = (char *) malloc (pagesize);
+ if (!data3)
+ return 1;
+ if (read (fd, data3, pagesize) != pagesize)
+ return 1;
+ for (i = 0; i < pagesize; ++i)
+ if (*(data + i) != *(data3 + i))
+ return 1;
+ close (fd);
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_mmap_fixed_mapped=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_mmap_fixed_mapped=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5
+echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6; }
+if test $ac_cv_func_mmap_fixed_mapped = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MMAP 1
+_ACEOF
+
+fi
+rm -f conftest.mmap
+
+
+
+for ac_header in sys/select.h sys/socket.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ echo "$as_me:$LINENO: checking types of arguments for select" >&5
+echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6; }
+if test "${ac_cv_func_select_args+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+ for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+ $ac_arg234, $ac_arg234, $ac_arg234,
+ $ac_arg5);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+done
+# Provide a safe default value.
+: ${ac_cv_func_select_args='int,int *,struct timeval *'}
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5
+echo "${ECHO_T}$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+{ echo "$as_me:$LINENO: checking for function prototypes" >&5
+echo $ECHO_N "checking for function prototypes... $ECHO_C" >&6; }
+if test "$ac_cv_prog_cc_c89" != no; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define PROTOTYPES 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define __PROTOTYPES 1
+_ACEOF
+
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+{ echo "$as_me:$LINENO: checking whether setvbuf arguments are reversed" >&5
+echo $ECHO_N "checking whether setvbuf arguments are reversed... $ECHO_C" >&6; }
+if test "${ac_cv_func_setvbuf_reversed+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_func_setvbuf_reversed=no
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdio.h>
+# ifdef PROTOTYPES
+ int (setvbuf) (FILE *, int, char *, size_t);
+# endif
+int
+main ()
+{
+char buf; return setvbuf (stdout, _IOLBF, &buf, 1);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdio.h>
+# ifdef PROTOTYPES
+ int (setvbuf) (FILE *, int, char *, size_t);
+# endif
+int
+main ()
+{
+char buf; return setvbuf (stdout, &buf, _IOLBF, 1);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ # It compiles and links either way, so it must not be declared
+ # with a prototype and most likely this is a K&R C compiler.
+ # Try running it.
+ if test "$cross_compiling" = yes; then
+ : # Assume setvbuf is not reversed when cross-compiling.
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+/* This call has the arguments reversed.
+ A reversed system may check and see that the address of buf
+ is not _IOLBF, _IONBF, or _IOFBF, and return nonzero. */
+ char buf;
+ if (setvbuf (stdout, _IOLBF, &buf, 1) != 0)
+ return 1;
+ putchar ('\r');
+ return 0; /* Non-reversed systems SEGV here. */
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_setvbuf_reversed=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+ ac_cv_func_setvbuf_reversed=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_setvbuf_reversed" >&5
+echo "${ECHO_T}$ac_cv_func_setvbuf_reversed" >&6; }
+if test $ac_cv_func_setvbuf_reversed = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define SETVBUF_REVERSED 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking return type of signal handlers" >&5
+echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6; }
+if test "${ac_cv_type_signal+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_signal=int
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_signal=void
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5
+echo "${ECHO_T}$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+{ echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5
+echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6; }
+if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ rm -f conftest.sym conftest.file
+echo >conftest.file
+if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_lstat_dereferences_slashed_symlink=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+ /* Linux will dereference the symlink and fail.
+ That is better in the sense that it means we will not
+ have to compile and use the lstat wrapper. */
+ return lstat ("conftest.sym/", &sbuf) == 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_lstat_dereferences_slashed_symlink=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+else
+ # If the `ln -s' command failed, then we probably don't even
+ # have an lstat function.
+ ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -f conftest.sym conftest.file
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5
+echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; }
+
+test $ac_cv_func_lstat_dereferences_slashed_symlink = yes &&
+
+cat >>confdefs.h <<_ACEOF
+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
+_ACEOF
+
+
+if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then
+ case " $LIBOBJS " in
+ *" lstat.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
+ ;;
+esac
+
+fi
+
+{ echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5
+echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6; }
+if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_stat_empty_string_bug=yes
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+ return stat ("", &sbuf) == 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_stat_empty_string_bug=no
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_stat_empty_string_bug=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5
+echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6; }
+if test $ac_cv_func_stat_empty_string_bug = yes; then
+ case " $LIBOBJS " in
+ *" stat.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS stat.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STAT_EMPTY_STRING_BUG 1
+_ACEOF
+
+fi
+
+{ echo "$as_me:$LINENO: checking for working strcoll" >&5
+echo $ECHO_N "checking for working strcoll... $ECHO_C" >&6; }
+if test "${ac_cv_func_strcoll_works+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_strcoll_works=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+return (strcoll ("abc", "def") >= 0 ||
+ strcoll ("ABC", "DEF") >= 0 ||
+ strcoll ("123", "456") >= 0)
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_strcoll_works=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_strcoll_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_strcoll_works" >&5
+echo "${ECHO_T}$ac_cv_func_strcoll_works" >&6; }
+if test $ac_cv_func_strcoll_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_STRCOLL 1
+_ACEOF
+
+fi
+
+
+for ac_func in strftime
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ # strftime is in -lintl on SCO UNIX.
+{ echo "$as_me:$LINENO: checking for strftime in -lintl" >&5
+echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6; }
+if test "${ac_cv_lib_intl_strftime+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lintl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char strftime ();
+int
+main ()
+{
+return strftime ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_intl_strftime=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_intl_strftime=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5
+echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6; }
+if test $ac_cv_lib_intl_strftime = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_STRFTIME 1
+_ACEOF
+
+LIBS="-lintl $LIBS"
+fi
+
+fi
+done
+
+{ echo "$as_me:$LINENO: checking for working strnlen" >&5
+echo $ECHO_N "checking for working strnlen... $ECHO_C" >&6; }
+if test "${ac_cv_func_strnlen_working+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_strnlen_working=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+#define S "foobar"
+#define S_LEN (sizeof S - 1)
+
+ /* At least one implementation is buggy: that of AIX 4.3 would
+ give strnlen (S, 1) == 3. */
+
+ int i;
+ for (i = 0; i < S_LEN + 1; ++i)
+ {
+ int expected = i <= S_LEN ? i : S_LEN;
+ if (strnlen (S, i) != expected)
+ return 1;
+ }
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_strnlen_working=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_strnlen_working=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_strnlen_working" >&5
+echo "${ECHO_T}$ac_cv_func_strnlen_working" >&6; }
+test $ac_cv_func_strnlen_working = no && case " $LIBOBJS " in
+ *" strnlen.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS strnlen.$ac_objext"
+ ;;
+esac
+
+
+{ echo "$as_me:$LINENO: checking for working strtod" >&5
+echo $ECHO_N "checking for working strtod... $ECHO_C" >&6; }
+if test "${ac_cv_func_strtod+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_strtod=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+$ac_includes_default
+#ifndef strtod
+double strtod ();
+#endif
+int
+main()
+{
+ {
+ /* Some versions of Linux strtod mis-parse strings with leading '+'. */
+ char *string = " +69";
+ char *term;
+ double value;
+ value = strtod (string, &term);
+ if (value != 69 || term != (string + 4))
+ return 1;
+ }
+
+ {
+ /* Under Solaris 2.4, strtod returns the wrong value for the
+ terminating character under some conditions. */
+ char *string = "NaN";
+ char *term;
+ strtod (string, &term);
+ if (term != string && *(term - 1) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_strtod=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_strtod=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_strtod" >&5
+echo "${ECHO_T}$ac_cv_func_strtod" >&6; }
+if test $ac_cv_func_strtod = no; then
+ case " $LIBOBJS " in
+ *" strtod.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS strtod.$ac_objext"
+ ;;
+esac
+
+{ echo "$as_me:$LINENO: checking for pow" >&5
+echo $ECHO_N "checking for pow... $ECHO_C" >&6; }
+if test "${ac_cv_func_pow+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define pow to an innocuous variant, in case <limits.h> declares pow.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define pow innocuous_pow
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char pow (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef pow
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_pow || defined __stub___pow
+choke me
+#endif
+
+int
+main ()
+{
+return pow ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_func_pow=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_pow=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_pow" >&5
+echo "${ECHO_T}$ac_cv_func_pow" >&6; }
+
+if test $ac_cv_func_pow = no; then
+ { echo "$as_me:$LINENO: checking for pow in -lm" >&5
+echo $ECHO_N "checking for pow in -lm... $ECHO_C" >&6; }
+if test "${ac_cv_lib_m_pow+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+int
+main ()
+{
+return pow ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_m_pow=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_m_pow=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_m_pow" >&5
+echo "${ECHO_T}$ac_cv_lib_m_pow" >&6; }
+if test $ac_cv_lib_m_pow = yes; then
+ POW_LIB=-lm
+else
+ { echo "$as_me:$LINENO: WARNING: cannot find library containing definition of pow" >&5
+echo "$as_me: WARNING: cannot find library containing definition of pow" >&2;}
+fi
+
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+{ echo "$as_me:$LINENO: checking whether utime accepts a null argument" >&5
+echo $ECHO_N "checking whether utime accepts a null argument... $ECHO_C" >&6; }
+if test "${ac_cv_func_utime_null+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ rm -f conftest.data; >conftest.data
+# Sequent interprets utime(file, 0) to mean use start of epoch. Wrong.
+if test "$cross_compiling" = yes; then
+ ac_cv_func_utime_null=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+ #ifdef HAVE_UTIME_H
+ # include <utime.h>
+ #endif
+int
+main ()
+{
+struct stat s, t;
+ return ! (stat ("conftest.data", &s) == 0
+ && utime ("conftest.data", 0) == 0
+ && stat ("conftest.data", &t) == 0
+ && t.st_mtime >= s.st_mtime
+ && t.st_mtime - s.st_mtime < 120);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_utime_null=yes
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_utime_null=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func_utime_null" >&5
+echo "${ECHO_T}$ac_cv_func_utime_null" >&6; }
+if test $ac_cv_func_utime_null = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_UTIME_NULL 1
+_ACEOF
+
+fi
+rm -f conftest.data
+
+
+for ac_func in vprintf
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+{ echo "$as_me:$LINENO: checking for _doprnt" >&5
+echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6; }
+if test "${ac_cv_func__doprnt+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define _doprnt innocuous__doprnt
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef _doprnt
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char _doprnt ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub__doprnt || defined __stub____doprnt
+choke me
+#endif
+
+int
+main ()
+{
+return _doprnt ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_func__doprnt=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func__doprnt=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5
+echo "${ECHO_T}$ac_cv_func__doprnt" >&6; }
+if test $ac_cv_func__doprnt = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DOPRNT 1
+_ACEOF
+
+fi
+
+fi
+done
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_func in asprintf atexit bzero dup2 endpwent ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+for ac_func in glob
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+{ echo "$as_me:$LINENO: checking for timersub in time.h" >&5
+echo $ECHO_N "checking for timersub in time.h... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/time.h>
+int
+main ()
+{
+struct timeval *a; timersub(a, a, a);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_TIMERSUB 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+if test "${ac_cv_header_sys_poll_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for sys/poll.h" >&5
+echo $ECHO_N "checking for sys/poll.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_poll_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_poll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_poll_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking sys/poll.h usability" >&5
+echo $ECHO_N "checking sys/poll.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sys/poll.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking sys/poll.h presence" >&5
+echo $ECHO_N "checking sys/poll.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/poll.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/poll.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sys/poll.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/poll.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/poll.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sys/poll.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sys/poll.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/poll.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/poll.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sys/poll.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for sys/poll.h" >&5
+echo $ECHO_N "checking for sys/poll.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_poll_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sys_poll_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_poll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_poll_h" >&6; }
+
+fi
+if test $ac_cv_header_sys_poll_h = yes; then
+ HAS_POLL=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_POLL_H 1
+_ACEOF
+
+fi
+
+
+
+
+# https support (in main/http.c) uses funopen on BSD systems,
+# fopencookie on linux
+
+
+for ac_func in funopen fopencookie
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+for ac_func in inet_aton
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+# check if we have IP_PKTINFO constant defined
+{ echo "$as_me:$LINENO: checking for IP_PKTINFO" >&5
+echo $ECHO_N "checking for IP_PKTINFO... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <netinet/in.h>
+int
+main ()
+{
+int pi = IP_PKTINFO;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PKTINFO 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+# some systems already have gethostbyname_r so we don't need to build ours in main/utils.c
+{ echo "$as_me:$LINENO: checking for library containing gethostbyname_r" >&5
+echo $ECHO_N "checking for library containing gethostbyname_r... $ECHO_C" >&6; }
+if test "${ac_cv_search_gethostbyname_r+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostbyname_r ();
+int
+main ()
+{
+return gethostbyname_r ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_gethostbyname_r=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_gethostbyname_r+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_gethostbyname_r+set}" = set; then
+ :
+else
+ ac_cv_search_gethostbyname_r=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname_r" >&5
+echo "${ECHO_T}$ac_cv_search_gethostbyname_r" >&6; }
+ac_res=$ac_cv_search_gethostbyname_r
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+{ echo "$as_me:$LINENO: checking for gethostbyname_r with 6 arguments" >&5
+echo $ECHO_N "checking for gethostbyname_r with 6 arguments... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+ #include <netdb.h>
+int
+main ()
+{
+struct hostent *he = gethostbyname_r((const char *)NULL, (struct hostent *)NULL, (char *)NULL, (int)0, (struct hostent **)NULL, (int *)NULL);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETHOSTBYNAME_R_6 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+{ echo "$as_me:$LINENO: checking for gethostbyname_r with 5 arguments" >&5
+echo $ECHO_N "checking for gethostbyname_r with 5 arguments... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+ #include <netdb.h>
+int
+main ()
+{
+struct hostent *he = gethostbyname_r((const char *)NULL, (struct hostent *)NULL, (char *)NULL, (int)0, (int *)NULL);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETHOSTBYNAME_R_5 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+if test "${ac_cv_header_byteswap_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for byteswap.h" >&5
+echo $ECHO_N "checking for byteswap.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_byteswap_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_byteswap_h" >&5
+echo "${ECHO_T}$ac_cv_header_byteswap_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking byteswap.h usability" >&5
+echo $ECHO_N "checking byteswap.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <byteswap.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking byteswap.h presence" >&5
+echo $ECHO_N "checking byteswap.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <byteswap.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: byteswap.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: byteswap.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: byteswap.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: byteswap.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: byteswap.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: byteswap.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: byteswap.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: byteswap.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: byteswap.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for byteswap.h" >&5
+echo $ECHO_N "checking for byteswap.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_byteswap_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_byteswap_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_byteswap_h" >&5
+echo "${ECHO_T}$ac_cv_header_byteswap_h" >&6; }
+
+fi
+if test $ac_cv_header_byteswap_h = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_BYTESWAP_H 1
+_ACEOF
+
+fi
+
+
+
+{ echo "$as_me:$LINENO: checking for __swap16 variant of <sys/endian.h> byteswapping macros" >&5
+echo $ECHO_N "checking for __swap16 variant of <sys/endian.h> byteswapping macros... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/endian.h>
+int
+main ()
+{
+int a = 1; int b = __swap16(a);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_ENDIAN_SWAP16 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+{ echo "$as_me:$LINENO: checking for bswap16 variant of <sys/endian.h> byteswapping macros" >&5
+echo $ECHO_N "checking for bswap16 variant of <sys/endian.h> byteswapping macros... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/endian.h>
+int
+main ()
+{
+int a = 1; int b = bswap16(a);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_ENDIAN_BSWAP16 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+if test "${cross_compiling}" = "no";
+then
+ { echo "$as_me:$LINENO: checking for /dev/urandom" >&5
+echo $ECHO_N "checking for /dev/urandom... $ECHO_C" >&6; }
+if test "${ac_cv_file__dev_urandom+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ test "$cross_compiling" = yes &&
+ { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
+echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
+ { (exit 1); exit 1; }; }
+if test -r "/dev/urandom"; then
+ ac_cv_file__dev_urandom=yes
+else
+ ac_cv_file__dev_urandom=no
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_file__dev_urandom" >&5
+echo "${ECHO_T}$ac_cv_file__dev_urandom" >&6; }
+if test $ac_cv_file__dev_urandom = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DEV_URANDOM 1
+_ACEOF
+
+fi
+
+fi
+
+
+ if test "x${PBX_PTHREAD_RWLOCK_INITIALIZER}" != "x1" -a "${USE_PTHREAD_RWLOCK_INITIALIZER}" != "no"; then
+ { echo "$as_me:$LINENO: checking for PTHREAD_RWLOCK_INITIALIZER in pthread.h" >&5
+echo $ECHO_N "checking for PTHREAD_RWLOCK_INITIALIZER in pthread.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${PTHREAD_RWLOCK_INITIALIZER_DIR}" != "x"; then
+ PTHREAD_RWLOCK_INITIALIZER_INCLUDE="-I${PTHREAD_RWLOCK_INITIALIZER_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${PTHREAD_RWLOCK_INITIALIZER_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+#if defined(PTHREAD_RWLOCK_INITIALIZER)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_PTHREAD_RWLOCK_INITIALIZER=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD_RWLOCK_INITIALIZER 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD_RWLOCK_INITIALIZER_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+{ echo "$as_me:$LINENO: checking for PTHREAD_RWLOCK_PREFER_WRITER_NP in pthread.h" >&5
+echo $ECHO_N "checking for PTHREAD_RWLOCK_PREFER_WRITER_NP in pthread.h... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int a = PTHREAD_RWLOCK_PREFER_WRITER_NP;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+{ echo "$as_me:$LINENO: checking for PTHREAD_MUTEX_RECURSIVE_NP in pthread.h" >&5
+echo $ECHO_N "checking for PTHREAD_MUTEX_RECURSIVE_NP in pthread.h... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int a = PTHREAD_MUTEX_RECURSIVE_NP;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD_MUTEX_RECURSIVE_NP 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+
+ if test "x${PBX_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP}" != "x1" -a "${USE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP}" != "no"; then
+ { echo "$as_me:$LINENO: checking for PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP in pthread.h" >&5
+echo $ECHO_N "checking for PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP in pthread.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP_DIR}" != "x"; then
+ PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP_INCLUDE="-I${PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+if test "${cross_compiling}" = "no";
+then
+{ echo "$as_me:$LINENO: checking for working epoll support" >&5
+echo $ECHO_N "checking for working epoll support... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/epoll.h>
+int
+main ()
+{
+int res = epoll_create(10);
+ if (res < 0)
+ return 1;
+ close (res);
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_EPOLL 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+
+{ echo "$as_me:$LINENO: checking for compiler atomic operations" >&5
+echo $ECHO_N "checking for compiler atomic operations... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+int foo1; int foo2 = __sync_fetch_and_add(&foo1, 1);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GCC_ATOMICS 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+
+{ echo "$as_me:$LINENO: checking for compiler 'attribute pure' support" >&5
+echo $ECHO_N "checking for compiler 'attribute pure' support... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+static int __attribute__((pure)) test(void) {}
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_pure 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ echo "$as_me:$LINENO: checking for compiler 'attribute malloc' support" >&5
+echo $ECHO_N "checking for compiler 'attribute malloc' support... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+static int __attribute__((malloc)) test(void) {}
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_malloc 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ echo "$as_me:$LINENO: checking for compiler 'attribute const' support" >&5
+echo $ECHO_N "checking for compiler 'attribute const' support... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+static int __attribute__((const)) test(void) {}
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_const 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ echo "$as_me:$LINENO: checking for compiler 'attribute unused' support" >&5
+echo $ECHO_N "checking for compiler 'attribute unused' support... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+static int __attribute__((unused)) test(void) {}
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_unused 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ echo "$as_me:$LINENO: checking for compiler 'attribute always_inline' support" >&5
+echo $ECHO_N "checking for compiler 'attribute always_inline' support... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+static int __attribute__((always_inline)) test(void) {}
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_always_inline 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ echo "$as_me:$LINENO: checking for compiler 'attribute deprecated' support" >&5
+echo $ECHO_N "checking for compiler 'attribute deprecated' support... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+static int __attribute__((deprecated)) test(void) {}
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_deprecated 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ echo "$as_me:$LINENO: checking for -ffunction-sections support" >&5
+echo $ECHO_N "checking for -ffunction-sections support... $ECHO_C" >&6; }
+saved_CFLAGS="${CFLAGS}"
+CFLAGS="${CFLAGS} -ffunction-sections"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+int x = 1;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ saved_LDFLAGS="${LDFLAGS}"
+ LDFLAGS="${LDFLAGS} -Wl,--gc-sections"
+ { echo "$as_me:$LINENO: checking for --gc-sections support" >&5
+echo $ECHO_N "checking for --gc-sections support... $ECHO_C" >&6; }
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+int x = 1;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ GC_CFLAGS="-ffunction-sections"
+ GC_LDFLAGS="-Wl,--gc-sections"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="${saved_LDFLAGS}"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+CFLAGS="${saved_CFLAGS}"
+
+
+
+{ echo "$as_me:$LINENO: checking for -Wdeclaration-after-statement support" >&5
+echo $ECHO_N "checking for -Wdeclaration-after-statement support... $ECHO_C" >&6; }
+if $(${CC} -Wdeclaration-after-statement -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ AST_DECLARATION_AFTER_STATEMENT=-Wdeclaration-after-statement
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ AST_DECLARATION_AFTER_STATEMENT=
+fi
+
+
+
+{ echo "$as_me:$LINENO: checking for sysinfo" >&5
+echo $ECHO_N "checking for sysinfo... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/sysinfo.h>
+int
+main ()
+{
+struct sysinfo sys_info; int uptime = sys_info.uptime
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYSINFO 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+{ echo "$as_me:$LINENO: checking for res_ninit" >&5
+echo $ECHO_N "checking for res_ninit... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <resolv.h>
+int
+main ()
+{
+int foo = res_ninit(NULL);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_RES_NINIT 1
+_ACEOF
+
+ { echo "$as_me:$LINENO: checking for res_ndestroy" >&5
+echo $ECHO_N "checking for res_ndestroy... $ECHO_C" >&6; }
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <resolv.h>
+int
+main ()
+{
+int foo = res_ndestroy(NULL);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_RES_NDESTROY 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+
+ if test "x${PBX_RTLD_NOLOAD}" != "x1" -a "${USE_RTLD_NOLOAD}" != "no"; then
+ { echo "$as_me:$LINENO: checking for RTLD_NOLOAD in dlfcn.h" >&5
+echo $ECHO_N "checking for RTLD_NOLOAD in dlfcn.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${RTLD_NOLOAD_DIR}" != "x"; then
+ RTLD_NOLOAD_INCLUDE="-I${RTLD_NOLOAD_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${RTLD_NOLOAD_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <dlfcn.h>
+int
+main ()
+{
+#if defined(RTLD_NOLOAD)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_RTLD_NOLOAD=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_RTLD_NOLOAD 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_RTLD_NOLOAD_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+
+ if test "x${PBX_IP_MTU_DISCOVER}" != "x1" -a "${USE_IP_MTU_DISCOVER}" != "no"; then
+ { echo "$as_me:$LINENO: checking for IP_MTU_DISCOVER in netinet/in.h" >&5
+echo $ECHO_N "checking for IP_MTU_DISCOVER in netinet/in.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${IP_MTU_DISCOVER_DIR}" != "x"; then
+ IP_MTU_DISCOVER_INCLUDE="-I${IP_MTU_DISCOVER_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${IP_MTU_DISCOVER_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <netinet/in.h>
+int
+main ()
+{
+#if defined(IP_MTU_DISCOVER)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_IP_MTU_DISCOVER=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_IP_MTU_DISCOVER 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_IP_MTU_DISCOVER_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+if test "${ac_cv_header_libkern_OSAtomic_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for libkern/OSAtomic.h" >&5
+echo $ECHO_N "checking for libkern/OSAtomic.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libkern_OSAtomic_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libkern_OSAtomic_h" >&5
+echo "${ECHO_T}$ac_cv_header_libkern_OSAtomic_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking libkern/OSAtomic.h usability" >&5
+echo $ECHO_N "checking libkern/OSAtomic.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <libkern/OSAtomic.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking libkern/OSAtomic.h presence" >&5
+echo $ECHO_N "checking libkern/OSAtomic.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <libkern/OSAtomic.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libkern/OSAtomic.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: libkern/OSAtomic.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for libkern/OSAtomic.h" >&5
+echo $ECHO_N "checking for libkern/OSAtomic.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libkern_OSAtomic_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_libkern_OSAtomic_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libkern_OSAtomic_h" >&5
+echo "${ECHO_T}$ac_cv_header_libkern_OSAtomic_h" >&6; }
+
+fi
+if test $ac_cv_header_libkern_OSAtomic_h = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSX_ATOMICS 1
+_ACEOF
+
+fi
+
+
+
+{ echo "$as_me:$LINENO: checking for int" >&5
+echo $ECHO_N "checking for int... $ECHO_C" >&6; }
+if test "${ac_cv_type_int+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef int ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_int=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_int=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_int" >&5
+echo "${ECHO_T}$ac_cv_type_int" >&6; }
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ echo "$as_me:$LINENO: checking size of int" >&5
+echo $ECHO_N "checking size of int... $ECHO_C" >&6; }
+if test "${ac_cv_sizeof_int+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+ typedef int ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+ typedef int ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_hi=$ac_mid; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_lo=`expr $ac_mid + 1`
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ ac_mid=`expr 2 '*' $ac_mid + 1`
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+ typedef int ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) < 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+ typedef int ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_lo=$ac_mid; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_hi=`expr '(' $ac_mid ')' - 1`
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ ac_mid=`expr 2 '*' $ac_mid`
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_lo= ac_hi=
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo`
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+ typedef int ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_hi=$ac_mid
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_lo=`expr '(' $ac_mid ')' + 1`
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in
+?*) ac_cv_sizeof_int=$ac_lo;;
+'') if test "$ac_cv_type_int" = yes; then
+ { { echo "$as_me:$LINENO: error: cannot compute sizeof (int)
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute sizeof (int)
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+ else
+ ac_cv_sizeof_int=0
+ fi ;;
+esac
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+ typedef int ac__type_sizeof_;
+static long int longval () { return (long int) (sizeof (ac__type_sizeof_)); }
+static unsigned long int ulongval () { return (long int) (sizeof (ac__type_sizeof_)); }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (((long int) (sizeof (ac__type_sizeof_))) < 0)
+ {
+ long int i = longval ();
+ if (i != ((long int) (sizeof (ac__type_sizeof_))))
+ return 1;
+ fprintf (f, "%ld\n", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ((long int) (sizeof (ac__type_sizeof_))))
+ return 1;
+ fprintf (f, "%lu\n", i);
+ }
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sizeof_int=`cat conftest.val`
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+if test "$ac_cv_type_int" = yes; then
+ { { echo "$as_me:$LINENO: error: cannot compute sizeof (int)
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute sizeof (int)
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+ else
+ ac_cv_sizeof_int=0
+ fi
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f conftest.val
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sizeof_int" >&5
+echo "${ECHO_T}$ac_cv_sizeof_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+_ACEOF
+
+
+
+# do the package library checks now
+
+
+if test "x${PBX_ALSA}" != "x1" -a "${USE_ALSA}" != "no"; then
+ pbxlibdir=""
+ # if --with-ALSA=DIR has been specified, use it.
+ if test "x${ALSA_DIR}" != "x"; then
+ if test -d ${ALSA_DIR}/lib; then
+ pbxlibdir="-L${ALSA_DIR}/lib"
+ else
+ pbxlibdir="-L${ALSA_DIR}"
+ fi
+ fi
+ pbxfuncname="snd_spcm_init"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ALSA_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_asound_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lasound" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lasound... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lasound ${pbxlibdir} -lm -ldl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ALSA_FOUND=yes
+else
+ AST_ALSA_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ALSA_FOUND}" = "yes"; then
+ ALSA_LIB="${pbxlibdir} -lasound -lm -ldl"
+ # if --with-ALSA=DIR has been specified, use it.
+ if test "x${ALSA_DIR}" != "x"; then
+ ALSA_INCLUDE="-I${ALSA_DIR}/include"
+ fi
+ ALSA_INCLUDE="${ALSA_INCLUDE} "
+ if test "xalsa/asoundlib.h" = "x" ; then # no header, assume found
+ ALSA_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ALSA_INCLUDE} "
+ if test "${ac_cv_header_alsa_asoundlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for alsa/asoundlib.h" >&5
+echo $ECHO_N "checking for alsa/asoundlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_alsa_asoundlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_alsa_asoundlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_alsa_asoundlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking alsa/asoundlib.h usability" >&5
+echo $ECHO_N "checking alsa/asoundlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <alsa/asoundlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking alsa/asoundlib.h presence" >&5
+echo $ECHO_N "checking alsa/asoundlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <alsa/asoundlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: alsa/asoundlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: alsa/asoundlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for alsa/asoundlib.h" >&5
+echo $ECHO_N "checking for alsa/asoundlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_alsa_asoundlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_alsa_asoundlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_alsa_asoundlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_alsa_asoundlib_h" >&6; }
+
+fi
+if test $ac_cv_header_alsa_asoundlib_h = yes; then
+ ALSA_HEADER_FOUND=1
+else
+ ALSA_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ALSA_HEADER_FOUND}" = "x0" ; then
+ ALSA_LIB=""
+ ALSA_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ALSA_LIB=""
+ fi
+ PBX_ALSA=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ALSA 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ALSA_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_CURSES}" != "x1" -a "${USE_CURSES}" != "no"; then
+ pbxlibdir=""
+ # if --with-CURSES=DIR has been specified, use it.
+ if test "x${CURSES_DIR}" != "x"; then
+ if test -d ${CURSES_DIR}/lib; then
+ pbxlibdir="-L${CURSES_DIR}/lib"
+ else
+ pbxlibdir="-L${CURSES_DIR}"
+ fi
+ fi
+ pbxfuncname="initscr"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_CURSES_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_curses_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lcurses" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lcurses... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcurses ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_CURSES_FOUND=yes
+else
+ AST_CURSES_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_CURSES_FOUND}" = "yes"; then
+ CURSES_LIB="${pbxlibdir} -lcurses "
+ # if --with-CURSES=DIR has been specified, use it.
+ if test "x${CURSES_DIR}" != "x"; then
+ CURSES_INCLUDE="-I${CURSES_DIR}/include"
+ fi
+ CURSES_INCLUDE="${CURSES_INCLUDE} "
+ if test "xcurses.h" = "x" ; then # no header, assume found
+ CURSES_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CURSES_INCLUDE} "
+ if test "${ac_cv_header_curses_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for curses.h" >&5
+echo $ECHO_N "checking for curses.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_curses_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_curses_h" >&5
+echo "${ECHO_T}$ac_cv_header_curses_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking curses.h usability" >&5
+echo $ECHO_N "checking curses.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <curses.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking curses.h presence" >&5
+echo $ECHO_N "checking curses.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <curses.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: curses.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: curses.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: curses.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: curses.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: curses.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: curses.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: curses.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: curses.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: curses.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: curses.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for curses.h" >&5
+echo $ECHO_N "checking for curses.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_curses_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_curses_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_curses_h" >&5
+echo "${ECHO_T}$ac_cv_header_curses_h" >&6; }
+
+fi
+if test $ac_cv_header_curses_h = yes; then
+ CURSES_HEADER_FOUND=1
+else
+ CURSES_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${CURSES_HEADER_FOUND}" = "x0" ; then
+ CURSES_LIB=""
+ CURSES_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ CURSES_LIB=""
+ fi
+ PBX_CURSES=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CURSES 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CURSES_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${host_os}" = "xlinux-gnu" ; then
+
+if test "x${PBX_CAP}" != "x1" -a "${USE_CAP}" != "no"; then
+ pbxlibdir=""
+ # if --with-CAP=DIR has been specified, use it.
+ if test "x${CAP_DIR}" != "x"; then
+ if test -d ${CAP_DIR}/lib; then
+ pbxlibdir="-L${CAP_DIR}/lib"
+ else
+ pbxlibdir="-L${CAP_DIR}"
+ fi
+ fi
+ pbxfuncname="cap_from_text"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_CAP_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_cap_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lcap" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lcap... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcap ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_CAP_FOUND=yes
+else
+ AST_CAP_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_CAP_FOUND}" = "yes"; then
+ CAP_LIB="${pbxlibdir} -lcap "
+ # if --with-CAP=DIR has been specified, use it.
+ if test "x${CAP_DIR}" != "x"; then
+ CAP_INCLUDE="-I${CAP_DIR}/include"
+ fi
+ CAP_INCLUDE="${CAP_INCLUDE} "
+ if test "xsys/capability.h" = "x" ; then # no header, assume found
+ CAP_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CAP_INCLUDE} "
+ if test "${ac_cv_header_sys_capability_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for sys/capability.h" >&5
+echo $ECHO_N "checking for sys/capability.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_capability_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_capability_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_capability_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking sys/capability.h usability" >&5
+echo $ECHO_N "checking sys/capability.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sys/capability.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking sys/capability.h presence" >&5
+echo $ECHO_N "checking sys/capability.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/capability.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sys/capability.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/capability.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/capability.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sys/capability.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sys/capability.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/capability.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sys/capability.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for sys/capability.h" >&5
+echo $ECHO_N "checking for sys/capability.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_capability_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sys_capability_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_capability_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_capability_h" >&6; }
+
+fi
+if test $ac_cv_header_sys_capability_h = yes; then
+ CAP_HEADER_FOUND=1
+else
+ CAP_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${CAP_HEADER_FOUND}" = "x0" ; then
+ CAP_LIB=""
+ CAP_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ CAP_LIB=""
+ fi
+ PBX_CAP=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CAP 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CAP_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+fi
+
+# BSD might not have exp2, and/or log2
+
+if test "x${PBX_EXP2L}" != "x1" -a "${USE_EXP2L}" != "no"; then
+ pbxlibdir=""
+ # if --with-EXP2L=DIR has been specified, use it.
+ if test "x${EXP2L_DIR}" != "x"; then
+ if test -d ${EXP2L_DIR}/lib; then
+ pbxlibdir="-L${EXP2L_DIR}/lib"
+ else
+ pbxlibdir="-L${EXP2L_DIR}"
+ fi
+ fi
+ pbxfuncname="exp2l"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_EXP2L_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_EXP2L_FOUND=yes
+else
+ AST_EXP2L_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_EXP2L_FOUND}" = "yes"; then
+ EXP2L_LIB="${pbxlibdir} -lm "
+ # if --with-EXP2L=DIR has been specified, use it.
+ if test "x${EXP2L_DIR}" != "x"; then
+ EXP2L_INCLUDE="-I${EXP2L_DIR}/include"
+ fi
+ EXP2L_INCLUDE="${EXP2L_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ EXP2L_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${EXP2L_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ EXP2L_HEADER_FOUND=1
+else
+ EXP2L_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${EXP2L_HEADER_FOUND}" = "x0" ; then
+ EXP2L_LIB=""
+ EXP2L_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ EXP2L_LIB=""
+ fi
+ PBX_EXP2L=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP2L 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP2L_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_LOG2L}" != "x1" -a "${USE_LOG2L}" != "no"; then
+ pbxlibdir=""
+ # if --with-LOG2L=DIR has been specified, use it.
+ if test "x${LOG2L_DIR}" != "x"; then
+ if test -d ${LOG2L_DIR}/lib; then
+ pbxlibdir="-L${LOG2L_DIR}/lib"
+ else
+ pbxlibdir="-L${LOG2L_DIR}"
+ fi
+ fi
+ pbxfuncname="log2l"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LOG2L_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LOG2L_FOUND=yes
+else
+ AST_LOG2L_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LOG2L_FOUND}" = "yes"; then
+ LOG2L_LIB="${pbxlibdir} -lm "
+ # if --with-LOG2L=DIR has been specified, use it.
+ if test "x${LOG2L_DIR}" != "x"; then
+ LOG2L_INCLUDE="-I${LOG2L_DIR}/include"
+ fi
+ LOG2L_INCLUDE="${LOG2L_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ LOG2L_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LOG2L_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ LOG2L_HEADER_FOUND=1
+else
+ LOG2L_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LOG2L_HEADER_FOUND}" = "x0" ; then
+ LOG2L_LIB=""
+ LOG2L_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LOG2L_LIB=""
+ fi
+ PBX_LOG2L=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG2L 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG2L_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_EXP10L}" != "x1" -a "${USE_EXP10L}" != "no"; then
+ pbxlibdir=""
+ # if --with-EXP10L=DIR has been specified, use it.
+ if test "x${EXP10L_DIR}" != "x"; then
+ if test -d ${EXP10L_DIR}/lib; then
+ pbxlibdir="-L${EXP10L_DIR}/lib"
+ else
+ pbxlibdir="-L${EXP10L_DIR}"
+ fi
+ fi
+ pbxfuncname="exp10l"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_EXP10L_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_EXP10L_FOUND=yes
+else
+ AST_EXP10L_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_EXP10L_FOUND}" = "yes"; then
+ EXP10L_LIB="${pbxlibdir} -lm "
+ # if --with-EXP10L=DIR has been specified, use it.
+ if test "x${EXP10L_DIR}" != "x"; then
+ EXP10L_INCLUDE="-I${EXP10L_DIR}/include"
+ fi
+ EXP10L_INCLUDE="${EXP10L_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ EXP10L_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${EXP10L_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ EXP10L_HEADER_FOUND=1
+else
+ EXP10L_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${EXP10L_HEADER_FOUND}" = "x0" ; then
+ EXP10L_LIB=""
+ EXP10L_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ EXP10L_LIB=""
+ fi
+ PBX_EXP10L=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP10L 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP10L_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_LOG10L}" != "x1" -a "${USE_LOG10L}" != "no"; then
+ pbxlibdir=""
+ # if --with-LOG10L=DIR has been specified, use it.
+ if test "x${LOG10L_DIR}" != "x"; then
+ if test -d ${LOG10L_DIR}/lib; then
+ pbxlibdir="-L${LOG10L_DIR}/lib"
+ else
+ pbxlibdir="-L${LOG10L_DIR}"
+ fi
+ fi
+ pbxfuncname="log10l"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LOG10L_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LOG10L_FOUND=yes
+else
+ AST_LOG10L_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LOG10L_FOUND}" = "yes"; then
+ LOG10L_LIB="${pbxlibdir} -lm "
+ # if --with-LOG10L=DIR has been specified, use it.
+ if test "x${LOG10L_DIR}" != "x"; then
+ LOG10L_INCLUDE="-I${LOG10L_DIR}/include"
+ fi
+ LOG10L_INCLUDE="${LOG10L_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ LOG10L_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LOG10L_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ LOG10L_HEADER_FOUND=1
+else
+ LOG10L_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LOG10L_HEADER_FOUND}" = "x0" ; then
+ LOG10L_LIB=""
+ LOG10L_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LOG10L_LIB=""
+ fi
+ PBX_LOG10L=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG10L 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG10L_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_SINL}" != "x1" -a "${USE_SINL}" != "no"; then
+ pbxlibdir=""
+ # if --with-SINL=DIR has been specified, use it.
+ if test "x${SINL_DIR}" != "x"; then
+ if test -d ${SINL_DIR}/lib; then
+ pbxlibdir="-L${SINL_DIR}/lib"
+ else
+ pbxlibdir="-L${SINL_DIR}"
+ fi
+ fi
+ pbxfuncname="sinl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SINL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SINL_FOUND=yes
+else
+ AST_SINL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SINL_FOUND}" = "yes"; then
+ SINL_LIB="${pbxlibdir} -lm "
+ # if --with-SINL=DIR has been specified, use it.
+ if test "x${SINL_DIR}" != "x"; then
+ SINL_INCLUDE="-I${SINL_DIR}/include"
+ fi
+ SINL_INCLUDE="${SINL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ SINL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SINL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ SINL_HEADER_FOUND=1
+else
+ SINL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SINL_HEADER_FOUND}" = "x0" ; then
+ SINL_LIB=""
+ SINL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SINL_LIB=""
+ fi
+ PBX_SINL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SINL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SINL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_COSL}" != "x1" -a "${USE_COSL}" != "no"; then
+ pbxlibdir=""
+ # if --with-COSL=DIR has been specified, use it.
+ if test "x${COSL_DIR}" != "x"; then
+ if test -d ${COSL_DIR}/lib; then
+ pbxlibdir="-L${COSL_DIR}/lib"
+ else
+ pbxlibdir="-L${COSL_DIR}"
+ fi
+ fi
+ pbxfuncname="cosl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_COSL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_COSL_FOUND=yes
+else
+ AST_COSL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_COSL_FOUND}" = "yes"; then
+ COSL_LIB="${pbxlibdir} -lm "
+ # if --with-COSL=DIR has been specified, use it.
+ if test "x${COSL_DIR}" != "x"; then
+ COSL_INCLUDE="-I${COSL_DIR}/include"
+ fi
+ COSL_INCLUDE="${COSL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ COSL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${COSL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ COSL_HEADER_FOUND=1
+else
+ COSL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${COSL_HEADER_FOUND}" = "x0" ; then
+ COSL_LIB=""
+ COSL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ COSL_LIB=""
+ fi
+ PBX_COSL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_COSL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_COSL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_TANL}" != "x1" -a "${USE_TANL}" != "no"; then
+ pbxlibdir=""
+ # if --with-TANL=DIR has been specified, use it.
+ if test "x${TANL_DIR}" != "x"; then
+ if test -d ${TANL_DIR}/lib; then
+ pbxlibdir="-L${TANL_DIR}/lib"
+ else
+ pbxlibdir="-L${TANL_DIR}"
+ fi
+ fi
+ pbxfuncname="tanl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TANL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TANL_FOUND=yes
+else
+ AST_TANL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TANL_FOUND}" = "yes"; then
+ TANL_LIB="${pbxlibdir} -lm "
+ # if --with-TANL=DIR has been specified, use it.
+ if test "x${TANL_DIR}" != "x"; then
+ TANL_INCLUDE="-I${TANL_DIR}/include"
+ fi
+ TANL_INCLUDE="${TANL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ TANL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TANL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ TANL_HEADER_FOUND=1
+else
+ TANL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TANL_HEADER_FOUND}" = "x0" ; then
+ TANL_LIB=""
+ TANL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TANL_LIB=""
+ fi
+ PBX_TANL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TANL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TANL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ASINL}" != "x1" -a "${USE_ASINL}" != "no"; then
+ pbxlibdir=""
+ # if --with-ASINL=DIR has been specified, use it.
+ if test "x${ASINL_DIR}" != "x"; then
+ if test -d ${ASINL_DIR}/lib; then
+ pbxlibdir="-L${ASINL_DIR}/lib"
+ else
+ pbxlibdir="-L${ASINL_DIR}"
+ fi
+ fi
+ pbxfuncname="asinl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ASINL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ASINL_FOUND=yes
+else
+ AST_ASINL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ASINL_FOUND}" = "yes"; then
+ ASINL_LIB="${pbxlibdir} -lm "
+ # if --with-ASINL=DIR has been specified, use it.
+ if test "x${ASINL_DIR}" != "x"; then
+ ASINL_INCLUDE="-I${ASINL_DIR}/include"
+ fi
+ ASINL_INCLUDE="${ASINL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ASINL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ASINL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ASINL_HEADER_FOUND=1
+else
+ ASINL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ASINL_HEADER_FOUND}" = "x0" ; then
+ ASINL_LIB=""
+ ASINL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ASINL_LIB=""
+ fi
+ PBX_ASINL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ASINL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ASINL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ACOSL}" != "x1" -a "${USE_ACOSL}" != "no"; then
+ pbxlibdir=""
+ # if --with-ACOSL=DIR has been specified, use it.
+ if test "x${ACOSL_DIR}" != "x"; then
+ if test -d ${ACOSL_DIR}/lib; then
+ pbxlibdir="-L${ACOSL_DIR}/lib"
+ else
+ pbxlibdir="-L${ACOSL_DIR}"
+ fi
+ fi
+ pbxfuncname="acosl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ACOSL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ACOSL_FOUND=yes
+else
+ AST_ACOSL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ACOSL_FOUND}" = "yes"; then
+ ACOSL_LIB="${pbxlibdir} -lm "
+ # if --with-ACOSL=DIR has been specified, use it.
+ if test "x${ACOSL_DIR}" != "x"; then
+ ACOSL_INCLUDE="-I${ACOSL_DIR}/include"
+ fi
+ ACOSL_INCLUDE="${ACOSL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ACOSL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ACOSL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ACOSL_HEADER_FOUND=1
+else
+ ACOSL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ACOSL_HEADER_FOUND}" = "x0" ; then
+ ACOSL_LIB=""
+ ACOSL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ACOSL_LIB=""
+ fi
+ PBX_ACOSL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ACOSL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ACOSL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ATANL}" != "x1" -a "${USE_ATANL}" != "no"; then
+ pbxlibdir=""
+ # if --with-ATANL=DIR has been specified, use it.
+ if test "x${ATANL_DIR}" != "x"; then
+ if test -d ${ATANL_DIR}/lib; then
+ pbxlibdir="-L${ATANL_DIR}/lib"
+ else
+ pbxlibdir="-L${ATANL_DIR}"
+ fi
+ fi
+ pbxfuncname="atanl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ATANL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ATANL_FOUND=yes
+else
+ AST_ATANL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ATANL_FOUND}" = "yes"; then
+ ATANL_LIB="${pbxlibdir} -lm "
+ # if --with-ATANL=DIR has been specified, use it.
+ if test "x${ATANL_DIR}" != "x"; then
+ ATANL_INCLUDE="-I${ATANL_DIR}/include"
+ fi
+ ATANL_INCLUDE="${ATANL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ATANL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ATANL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ATANL_HEADER_FOUND=1
+else
+ ATANL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ATANL_HEADER_FOUND}" = "x0" ; then
+ ATANL_LIB=""
+ ATANL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ATANL_LIB=""
+ fi
+ PBX_ATANL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATANL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATANL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ATAN2L}" != "x1" -a "${USE_ATAN2L}" != "no"; then
+ pbxlibdir=""
+ # if --with-ATAN2L=DIR has been specified, use it.
+ if test "x${ATAN2L_DIR}" != "x"; then
+ if test -d ${ATAN2L_DIR}/lib; then
+ pbxlibdir="-L${ATAN2L_DIR}/lib"
+ else
+ pbxlibdir="-L${ATAN2L_DIR}"
+ fi
+ fi
+ pbxfuncname="atan2l"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ATAN2L_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ATAN2L_FOUND=yes
+else
+ AST_ATAN2L_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ATAN2L_FOUND}" = "yes"; then
+ ATAN2L_LIB="${pbxlibdir} -lm "
+ # if --with-ATAN2L=DIR has been specified, use it.
+ if test "x${ATAN2L_DIR}" != "x"; then
+ ATAN2L_INCLUDE="-I${ATAN2L_DIR}/include"
+ fi
+ ATAN2L_INCLUDE="${ATAN2L_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ATAN2L_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ATAN2L_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ATAN2L_HEADER_FOUND=1
+else
+ ATAN2L_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ATAN2L_HEADER_FOUND}" = "x0" ; then
+ ATAN2L_LIB=""
+ ATAN2L_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ATAN2L_LIB=""
+ fi
+ PBX_ATAN2L=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATAN2L 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATAN2L_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_POWL}" != "x1" -a "${USE_POWL}" != "no"; then
+ pbxlibdir=""
+ # if --with-POWL=DIR has been specified, use it.
+ if test "x${POWL_DIR}" != "x"; then
+ if test -d ${POWL_DIR}/lib; then
+ pbxlibdir="-L${POWL_DIR}/lib"
+ else
+ pbxlibdir="-L${POWL_DIR}"
+ fi
+ fi
+ pbxfuncname="powl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_POWL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_POWL_FOUND=yes
+else
+ AST_POWL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_POWL_FOUND}" = "yes"; then
+ POWL_LIB="${pbxlibdir} -lm "
+ # if --with-POWL=DIR has been specified, use it.
+ if test "x${POWL_DIR}" != "x"; then
+ POWL_INCLUDE="-I${POWL_DIR}/include"
+ fi
+ POWL_INCLUDE="${POWL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ POWL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${POWL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ POWL_HEADER_FOUND=1
+else
+ POWL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${POWL_HEADER_FOUND}" = "x0" ; then
+ POWL_LIB=""
+ POWL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ POWL_LIB=""
+ fi
+ PBX_POWL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_POWL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_POWL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_SQRTL}" != "x1" -a "${USE_SQRTL}" != "no"; then
+ pbxlibdir=""
+ # if --with-SQRTL=DIR has been specified, use it.
+ if test "x${SQRTL_DIR}" != "x"; then
+ if test -d ${SQRTL_DIR}/lib; then
+ pbxlibdir="-L${SQRTL_DIR}/lib"
+ else
+ pbxlibdir="-L${SQRTL_DIR}"
+ fi
+ fi
+ pbxfuncname="sqrtl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SQRTL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SQRTL_FOUND=yes
+else
+ AST_SQRTL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SQRTL_FOUND}" = "yes"; then
+ SQRTL_LIB="${pbxlibdir} -lm "
+ # if --with-SQRTL=DIR has been specified, use it.
+ if test "x${SQRTL_DIR}" != "x"; then
+ SQRTL_INCLUDE="-I${SQRTL_DIR}/include"
+ fi
+ SQRTL_INCLUDE="${SQRTL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ SQRTL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SQRTL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ SQRTL_HEADER_FOUND=1
+else
+ SQRTL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SQRTL_HEADER_FOUND}" = "x0" ; then
+ SQRTL_LIB=""
+ SQRTL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SQRTL_LIB=""
+ fi
+ PBX_SQRTL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQRTL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQRTL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_RINTL}" != "x1" -a "${USE_RINTL}" != "no"; then
+ pbxlibdir=""
+ # if --with-RINTL=DIR has been specified, use it.
+ if test "x${RINTL_DIR}" != "x"; then
+ if test -d ${RINTL_DIR}/lib; then
+ pbxlibdir="-L${RINTL_DIR}/lib"
+ else
+ pbxlibdir="-L${RINTL_DIR}"
+ fi
+ fi
+ pbxfuncname="rintl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_RINTL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_RINTL_FOUND=yes
+else
+ AST_RINTL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_RINTL_FOUND}" = "yes"; then
+ RINTL_LIB="${pbxlibdir} -lm "
+ # if --with-RINTL=DIR has been specified, use it.
+ if test "x${RINTL_DIR}" != "x"; then
+ RINTL_INCLUDE="-I${RINTL_DIR}/include"
+ fi
+ RINTL_INCLUDE="${RINTL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ RINTL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${RINTL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ RINTL_HEADER_FOUND=1
+else
+ RINTL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${RINTL_HEADER_FOUND}" = "x0" ; then
+ RINTL_LIB=""
+ RINTL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ RINTL_LIB=""
+ fi
+ PBX_RINTL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RINTL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RINTL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_EXPL}" != "x1" -a "${USE_EXPL}" != "no"; then
+ pbxlibdir=""
+ # if --with-EXPL=DIR has been specified, use it.
+ if test "x${EXPL_DIR}" != "x"; then
+ if test -d ${EXPL_DIR}/lib; then
+ pbxlibdir="-L${EXPL_DIR}/lib"
+ else
+ pbxlibdir="-L${EXPL_DIR}"
+ fi
+ fi
+ pbxfuncname="expl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_EXPL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_EXPL_FOUND=yes
+else
+ AST_EXPL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_EXPL_FOUND}" = "yes"; then
+ EXPL_LIB="${pbxlibdir} -lm "
+ # if --with-EXPL=DIR has been specified, use it.
+ if test "x${EXPL_DIR}" != "x"; then
+ EXPL_INCLUDE="-I${EXPL_DIR}/include"
+ fi
+ EXPL_INCLUDE="${EXPL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ EXPL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${EXPL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ EXPL_HEADER_FOUND=1
+else
+ EXPL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${EXPL_HEADER_FOUND}" = "x0" ; then
+ EXPL_LIB=""
+ EXPL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ EXPL_LIB=""
+ fi
+ PBX_EXPL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXPL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXPL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_LOGL}" != "x1" -a "${USE_LOGL}" != "no"; then
+ pbxlibdir=""
+ # if --with-LOGL=DIR has been specified, use it.
+ if test "x${LOGL_DIR}" != "x"; then
+ if test -d ${LOGL_DIR}/lib; then
+ pbxlibdir="-L${LOGL_DIR}/lib"
+ else
+ pbxlibdir="-L${LOGL_DIR}"
+ fi
+ fi
+ pbxfuncname="logl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LOGL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LOGL_FOUND=yes
+else
+ AST_LOGL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LOGL_FOUND}" = "yes"; then
+ LOGL_LIB="${pbxlibdir} -lm "
+ # if --with-LOGL=DIR has been specified, use it.
+ if test "x${LOGL_DIR}" != "x"; then
+ LOGL_INCLUDE="-I${LOGL_DIR}/include"
+ fi
+ LOGL_INCLUDE="${LOGL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ LOGL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LOGL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ LOGL_HEADER_FOUND=1
+else
+ LOGL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LOGL_HEADER_FOUND}" = "x0" ; then
+ LOGL_LIB=""
+ LOGL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LOGL_LIB=""
+ fi
+ PBX_LOGL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOGL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOGL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_REMAINDERL}" != "x1" -a "${USE_REMAINDERL}" != "no"; then
+ pbxlibdir=""
+ # if --with-REMAINDERL=DIR has been specified, use it.
+ if test "x${REMAINDERL_DIR}" != "x"; then
+ if test -d ${REMAINDERL_DIR}/lib; then
+ pbxlibdir="-L${REMAINDERL_DIR}/lib"
+ else
+ pbxlibdir="-L${REMAINDERL_DIR}"
+ fi
+ fi
+ pbxfuncname="remainderl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_REMAINDERL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_REMAINDERL_FOUND=yes
+else
+ AST_REMAINDERL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_REMAINDERL_FOUND}" = "yes"; then
+ REMAINDERL_LIB="${pbxlibdir} -lm "
+ # if --with-REMAINDERL=DIR has been specified, use it.
+ if test "x${REMAINDERL_DIR}" != "x"; then
+ REMAINDERL_INCLUDE="-I${REMAINDERL_DIR}/include"
+ fi
+ REMAINDERL_INCLUDE="${REMAINDERL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ REMAINDERL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${REMAINDERL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ REMAINDERL_HEADER_FOUND=1
+else
+ REMAINDERL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${REMAINDERL_HEADER_FOUND}" = "x0" ; then
+ REMAINDERL_LIB=""
+ REMAINDERL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ REMAINDERL_LIB=""
+ fi
+ PBX_REMAINDERL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_REMAINDERL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_REMAINDERL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_FMODL}" != "x1" -a "${USE_FMODL}" != "no"; then
+ pbxlibdir=""
+ # if --with-FMODL=DIR has been specified, use it.
+ if test "x${FMODL_DIR}" != "x"; then
+ if test -d ${FMODL_DIR}/lib; then
+ pbxlibdir="-L${FMODL_DIR}/lib"
+ else
+ pbxlibdir="-L${FMODL_DIR}"
+ fi
+ fi
+ pbxfuncname="fmodl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_FMODL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_FMODL_FOUND=yes
+else
+ AST_FMODL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_FMODL_FOUND}" = "yes"; then
+ FMODL_LIB="${pbxlibdir} -lm "
+ # if --with-FMODL=DIR has been specified, use it.
+ if test "x${FMODL_DIR}" != "x"; then
+ FMODL_INCLUDE="-I${FMODL_DIR}/include"
+ fi
+ FMODL_INCLUDE="${FMODL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ FMODL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${FMODL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ FMODL_HEADER_FOUND=1
+else
+ FMODL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${FMODL_HEADER_FOUND}" = "x0" ; then
+ FMODL_LIB=""
+ FMODL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ FMODL_LIB=""
+ fi
+ PBX_FMODL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FMODL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FMODL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ROUNDL}" != "x1" -a "${USE_ROUNDL}" != "no"; then
+ pbxlibdir=""
+ # if --with-ROUNDL=DIR has been specified, use it.
+ if test "x${ROUNDL_DIR}" != "x"; then
+ if test -d ${ROUNDL_DIR}/lib; then
+ pbxlibdir="-L${ROUNDL_DIR}/lib"
+ else
+ pbxlibdir="-L${ROUNDL_DIR}"
+ fi
+ fi
+ pbxfuncname="roundl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ROUNDL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ROUNDL_FOUND=yes
+else
+ AST_ROUNDL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ROUNDL_FOUND}" = "yes"; then
+ ROUNDL_LIB="${pbxlibdir} -lm "
+ # if --with-ROUNDL=DIR has been specified, use it.
+ if test "x${ROUNDL_DIR}" != "x"; then
+ ROUNDL_INCLUDE="-I${ROUNDL_DIR}/include"
+ fi
+ ROUNDL_INCLUDE="${ROUNDL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ROUNDL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ROUNDL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ROUNDL_HEADER_FOUND=1
+else
+ ROUNDL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ROUNDL_HEADER_FOUND}" = "x0" ; then
+ ROUNDL_LIB=""
+ ROUNDL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ROUNDL_LIB=""
+ fi
+ PBX_ROUNDL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ROUNDL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ROUNDL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_TRUNCL}" != "x1" -a "${USE_TRUNCL}" != "no"; then
+ pbxlibdir=""
+ # if --with-TRUNCL=DIR has been specified, use it.
+ if test "x${TRUNCL_DIR}" != "x"; then
+ if test -d ${TRUNCL_DIR}/lib; then
+ pbxlibdir="-L${TRUNCL_DIR}/lib"
+ else
+ pbxlibdir="-L${TRUNCL_DIR}"
+ fi
+ fi
+ pbxfuncname="truncl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TRUNCL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TRUNCL_FOUND=yes
+else
+ AST_TRUNCL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TRUNCL_FOUND}" = "yes"; then
+ TRUNCL_LIB="${pbxlibdir} -lm "
+ # if --with-TRUNCL=DIR has been specified, use it.
+ if test "x${TRUNCL_DIR}" != "x"; then
+ TRUNCL_INCLUDE="-I${TRUNCL_DIR}/include"
+ fi
+ TRUNCL_INCLUDE="${TRUNCL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ TRUNCL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TRUNCL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ TRUNCL_HEADER_FOUND=1
+else
+ TRUNCL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TRUNCL_HEADER_FOUND}" = "x0" ; then
+ TRUNCL_LIB=""
+ TRUNCL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TRUNCL_LIB=""
+ fi
+ PBX_TRUNCL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TRUNCL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TRUNCL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_STRTOLD}" != "x1" -a "${USE_STRTOLD}" != "no"; then
+ pbxlibdir=""
+ # if --with-STRTOLD=DIR has been specified, use it.
+ if test "x${STRTOLD_DIR}" != "x"; then
+ if test -d ${STRTOLD_DIR}/lib; then
+ pbxlibdir="-L${STRTOLD_DIR}/lib"
+ else
+ pbxlibdir="-L${STRTOLD_DIR}"
+ fi
+ fi
+ pbxfuncname="strtold"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_STRTOLD_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_c_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lc" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lc... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_STRTOLD_FOUND=yes
+else
+ AST_STRTOLD_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_STRTOLD_FOUND}" = "yes"; then
+ STRTOLD_LIB="${pbxlibdir} -lc "
+ # if --with-STRTOLD=DIR has been specified, use it.
+ if test "x${STRTOLD_DIR}" != "x"; then
+ STRTOLD_INCLUDE="-I${STRTOLD_DIR}/include"
+ fi
+ STRTOLD_INCLUDE="${STRTOLD_INCLUDE} "
+ if test "xstdlib.h" = "x" ; then # no header, assume found
+ STRTOLD_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${STRTOLD_INCLUDE} "
+ if test "${ac_cv_header_stdlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for stdlib.h" >&5
+echo $ECHO_N "checking for stdlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_stdlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking stdlib.h usability" >&5
+echo $ECHO_N "checking stdlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <stdlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking stdlib.h presence" >&5
+echo $ECHO_N "checking stdlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: stdlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: stdlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: stdlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: stdlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: stdlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: stdlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: stdlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: stdlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for stdlib.h" >&5
+echo $ECHO_N "checking for stdlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_stdlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_stdlib_h" >&6; }
+
+fi
+if test $ac_cv_header_stdlib_h = yes; then
+ STRTOLD_HEADER_FOUND=1
+else
+ STRTOLD_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${STRTOLD_HEADER_FOUND}" = "x0" ; then
+ STRTOLD_LIB=""
+ STRTOLD_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ STRTOLD_LIB=""
+ fi
+ PBX_STRTOLD=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRTOLD 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRTOLD_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_FLOORL}" != "x1" -a "${USE_FLOORL}" != "no"; then
+ pbxlibdir=""
+ # if --with-FLOORL=DIR has been specified, use it.
+ if test "x${FLOORL_DIR}" != "x"; then
+ if test -d ${FLOORL_DIR}/lib; then
+ pbxlibdir="-L${FLOORL_DIR}/lib"
+ else
+ pbxlibdir="-L${FLOORL_DIR}"
+ fi
+ fi
+ pbxfuncname="floorl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_FLOORL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_FLOORL_FOUND=yes
+else
+ AST_FLOORL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_FLOORL_FOUND}" = "yes"; then
+ FLOORL_LIB="${pbxlibdir} -lm "
+ # if --with-FLOORL=DIR has been specified, use it.
+ if test "x${FLOORL_DIR}" != "x"; then
+ FLOORL_INCLUDE="-I${FLOORL_DIR}/include"
+ fi
+ FLOORL_INCLUDE="${FLOORL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ FLOORL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${FLOORL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ FLOORL_HEADER_FOUND=1
+else
+ FLOORL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${FLOORL_HEADER_FOUND}" = "x0" ; then
+ FLOORL_LIB=""
+ FLOORL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ FLOORL_LIB=""
+ fi
+ PBX_FLOORL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FLOORL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FLOORL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_CEILL}" != "x1" -a "${USE_CEILL}" != "no"; then
+ pbxlibdir=""
+ # if --with-CEILL=DIR has been specified, use it.
+ if test "x${CEILL_DIR}" != "x"; then
+ if test -d ${CEILL_DIR}/lib; then
+ pbxlibdir="-L${CEILL_DIR}/lib"
+ else
+ pbxlibdir="-L${CEILL_DIR}"
+ fi
+ fi
+ pbxfuncname="ceill"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_CEILL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_CEILL_FOUND=yes
+else
+ AST_CEILL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_CEILL_FOUND}" = "yes"; then
+ CEILL_LIB="${pbxlibdir} -lm "
+ # if --with-CEILL=DIR has been specified, use it.
+ if test "x${CEILL_DIR}" != "x"; then
+ CEILL_INCLUDE="-I${CEILL_DIR}/include"
+ fi
+ CEILL_INCLUDE="${CEILL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ CEILL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CEILL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ CEILL_HEADER_FOUND=1
+else
+ CEILL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${CEILL_HEADER_FOUND}" = "x0" ; then
+ CEILL_LIB=""
+ CEILL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ CEILL_LIB=""
+ fi
+ PBX_CEILL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CEILL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CEILL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_EXP2}" != "x1" -a "${USE_EXP2}" != "no"; then
+ pbxlibdir=""
+ # if --with-EXP2=DIR has been specified, use it.
+ if test "x${EXP2_DIR}" != "x"; then
+ if test -d ${EXP2_DIR}/lib; then
+ pbxlibdir="-L${EXP2_DIR}/lib"
+ else
+ pbxlibdir="-L${EXP2_DIR}"
+ fi
+ fi
+ pbxfuncname="exp2"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_EXP2_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_EXP2_FOUND=yes
+else
+ AST_EXP2_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_EXP2_FOUND}" = "yes"; then
+ EXP2_LIB="${pbxlibdir} -lm "
+ # if --with-EXP2=DIR has been specified, use it.
+ if test "x${EXP2_DIR}" != "x"; then
+ EXP2_INCLUDE="-I${EXP2_DIR}/include"
+ fi
+ EXP2_INCLUDE="${EXP2_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ EXP2_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${EXP2_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ EXP2_HEADER_FOUND=1
+else
+ EXP2_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${EXP2_HEADER_FOUND}" = "x0" ; then
+ EXP2_LIB=""
+ EXP2_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ EXP2_LIB=""
+ fi
+ PBX_EXP2=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP2 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP2_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_LOG2}" != "x1" -a "${USE_LOG2}" != "no"; then
+ pbxlibdir=""
+ # if --with-LOG2=DIR has been specified, use it.
+ if test "x${LOG2_DIR}" != "x"; then
+ if test -d ${LOG2_DIR}/lib; then
+ pbxlibdir="-L${LOG2_DIR}/lib"
+ else
+ pbxlibdir="-L${LOG2_DIR}"
+ fi
+ fi
+ pbxfuncname="log2"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LOG2_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LOG2_FOUND=yes
+else
+ AST_LOG2_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LOG2_FOUND}" = "yes"; then
+ LOG2_LIB="${pbxlibdir} -lm "
+ # if --with-LOG2=DIR has been specified, use it.
+ if test "x${LOG2_DIR}" != "x"; then
+ LOG2_INCLUDE="-I${LOG2_DIR}/include"
+ fi
+ LOG2_INCLUDE="${LOG2_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ LOG2_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LOG2_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ LOG2_HEADER_FOUND=1
+else
+ LOG2_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LOG2_HEADER_FOUND}" = "x0" ; then
+ LOG2_LIB=""
+ LOG2_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LOG2_LIB=""
+ fi
+ PBX_LOG2=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG2 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG2_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_EXP10}" != "x1" -a "${USE_EXP10}" != "no"; then
+ pbxlibdir=""
+ # if --with-EXP10=DIR has been specified, use it.
+ if test "x${EXP10_DIR}" != "x"; then
+ if test -d ${EXP10_DIR}/lib; then
+ pbxlibdir="-L${EXP10_DIR}/lib"
+ else
+ pbxlibdir="-L${EXP10_DIR}"
+ fi
+ fi
+ pbxfuncname="exp10"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_EXP10_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_EXP10_FOUND=yes
+else
+ AST_EXP10_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_EXP10_FOUND}" = "yes"; then
+ EXP10_LIB="${pbxlibdir} -lm "
+ # if --with-EXP10=DIR has been specified, use it.
+ if test "x${EXP10_DIR}" != "x"; then
+ EXP10_INCLUDE="-I${EXP10_DIR}/include"
+ fi
+ EXP10_INCLUDE="${EXP10_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ EXP10_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${EXP10_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ EXP10_HEADER_FOUND=1
+else
+ EXP10_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${EXP10_HEADER_FOUND}" = "x0" ; then
+ EXP10_LIB=""
+ EXP10_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ EXP10_LIB=""
+ fi
+ PBX_EXP10=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP10 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP10_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_LOG10}" != "x1" -a "${USE_LOG10}" != "no"; then
+ pbxlibdir=""
+ # if --with-LOG10=DIR has been specified, use it.
+ if test "x${LOG10_DIR}" != "x"; then
+ if test -d ${LOG10_DIR}/lib; then
+ pbxlibdir="-L${LOG10_DIR}/lib"
+ else
+ pbxlibdir="-L${LOG10_DIR}"
+ fi
+ fi
+ pbxfuncname="log10"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LOG10_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LOG10_FOUND=yes
+else
+ AST_LOG10_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LOG10_FOUND}" = "yes"; then
+ LOG10_LIB="${pbxlibdir} -lm "
+ # if --with-LOG10=DIR has been specified, use it.
+ if test "x${LOG10_DIR}" != "x"; then
+ LOG10_INCLUDE="-I${LOG10_DIR}/include"
+ fi
+ LOG10_INCLUDE="${LOG10_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ LOG10_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LOG10_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ LOG10_HEADER_FOUND=1
+else
+ LOG10_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LOG10_HEADER_FOUND}" = "x0" ; then
+ LOG10_LIB=""
+ LOG10_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LOG10_LIB=""
+ fi
+ PBX_LOG10=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG10 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG10_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_SIN}" != "x1" -a "${USE_SIN}" != "no"; then
+ pbxlibdir=""
+ # if --with-SIN=DIR has been specified, use it.
+ if test "x${SIN_DIR}" != "x"; then
+ if test -d ${SIN_DIR}/lib; then
+ pbxlibdir="-L${SIN_DIR}/lib"
+ else
+ pbxlibdir="-L${SIN_DIR}"
+ fi
+ fi
+ pbxfuncname="sin"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SIN_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SIN_FOUND=yes
+else
+ AST_SIN_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SIN_FOUND}" = "yes"; then
+ SIN_LIB="${pbxlibdir} -lm "
+ # if --with-SIN=DIR has been specified, use it.
+ if test "x${SIN_DIR}" != "x"; then
+ SIN_INCLUDE="-I${SIN_DIR}/include"
+ fi
+ SIN_INCLUDE="${SIN_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ SIN_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SIN_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ SIN_HEADER_FOUND=1
+else
+ SIN_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SIN_HEADER_FOUND}" = "x0" ; then
+ SIN_LIB=""
+ SIN_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SIN_LIB=""
+ fi
+ PBX_SIN=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SIN 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SIN_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_COS}" != "x1" -a "${USE_COS}" != "no"; then
+ pbxlibdir=""
+ # if --with-COS=DIR has been specified, use it.
+ if test "x${COS_DIR}" != "x"; then
+ if test -d ${COS_DIR}/lib; then
+ pbxlibdir="-L${COS_DIR}/lib"
+ else
+ pbxlibdir="-L${COS_DIR}"
+ fi
+ fi
+ pbxfuncname="cos"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_COS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_COS_FOUND=yes
+else
+ AST_COS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_COS_FOUND}" = "yes"; then
+ COS_LIB="${pbxlibdir} -lm "
+ # if --with-COS=DIR has been specified, use it.
+ if test "x${COS_DIR}" != "x"; then
+ COS_INCLUDE="-I${COS_DIR}/include"
+ fi
+ COS_INCLUDE="${COS_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ COS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${COS_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ COS_HEADER_FOUND=1
+else
+ COS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${COS_HEADER_FOUND}" = "x0" ; then
+ COS_LIB=""
+ COS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ COS_LIB=""
+ fi
+ PBX_COS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_COS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_COS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_TAN}" != "x1" -a "${USE_TAN}" != "no"; then
+ pbxlibdir=""
+ # if --with-TAN=DIR has been specified, use it.
+ if test "x${TAN_DIR}" != "x"; then
+ if test -d ${TAN_DIR}/lib; then
+ pbxlibdir="-L${TAN_DIR}/lib"
+ else
+ pbxlibdir="-L${TAN_DIR}"
+ fi
+ fi
+ pbxfuncname="tan"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TAN_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TAN_FOUND=yes
+else
+ AST_TAN_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TAN_FOUND}" = "yes"; then
+ TAN_LIB="${pbxlibdir} -lm "
+ # if --with-TAN=DIR has been specified, use it.
+ if test "x${TAN_DIR}" != "x"; then
+ TAN_INCLUDE="-I${TAN_DIR}/include"
+ fi
+ TAN_INCLUDE="${TAN_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ TAN_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TAN_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ TAN_HEADER_FOUND=1
+else
+ TAN_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TAN_HEADER_FOUND}" = "x0" ; then
+ TAN_LIB=""
+ TAN_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TAN_LIB=""
+ fi
+ PBX_TAN=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TAN 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TAN_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ASIN}" != "x1" -a "${USE_ASIN}" != "no"; then
+ pbxlibdir=""
+ # if --with-ASIN=DIR has been specified, use it.
+ if test "x${ASIN_DIR}" != "x"; then
+ if test -d ${ASIN_DIR}/lib; then
+ pbxlibdir="-L${ASIN_DIR}/lib"
+ else
+ pbxlibdir="-L${ASIN_DIR}"
+ fi
+ fi
+ pbxfuncname="asin"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ASIN_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ASIN_FOUND=yes
+else
+ AST_ASIN_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ASIN_FOUND}" = "yes"; then
+ ASIN_LIB="${pbxlibdir} -lm "
+ # if --with-ASIN=DIR has been specified, use it.
+ if test "x${ASIN_DIR}" != "x"; then
+ ASIN_INCLUDE="-I${ASIN_DIR}/include"
+ fi
+ ASIN_INCLUDE="${ASIN_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ASIN_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ASIN_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ASIN_HEADER_FOUND=1
+else
+ ASIN_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ASIN_HEADER_FOUND}" = "x0" ; then
+ ASIN_LIB=""
+ ASIN_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ASIN_LIB=""
+ fi
+ PBX_ASIN=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ASIN 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ASIN_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ACOS}" != "x1" -a "${USE_ACOS}" != "no"; then
+ pbxlibdir=""
+ # if --with-ACOS=DIR has been specified, use it.
+ if test "x${ACOS_DIR}" != "x"; then
+ if test -d ${ACOS_DIR}/lib; then
+ pbxlibdir="-L${ACOS_DIR}/lib"
+ else
+ pbxlibdir="-L${ACOS_DIR}"
+ fi
+ fi
+ pbxfuncname="acos"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ACOS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ACOS_FOUND=yes
+else
+ AST_ACOS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ACOS_FOUND}" = "yes"; then
+ ACOS_LIB="${pbxlibdir} -lm "
+ # if --with-ACOS=DIR has been specified, use it.
+ if test "x${ACOS_DIR}" != "x"; then
+ ACOS_INCLUDE="-I${ACOS_DIR}/include"
+ fi
+ ACOS_INCLUDE="${ACOS_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ACOS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ACOS_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ACOS_HEADER_FOUND=1
+else
+ ACOS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ACOS_HEADER_FOUND}" = "x0" ; then
+ ACOS_LIB=""
+ ACOS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ACOS_LIB=""
+ fi
+ PBX_ACOS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ACOS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ACOS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ATAN}" != "x1" -a "${USE_ATAN}" != "no"; then
+ pbxlibdir=""
+ # if --with-ATAN=DIR has been specified, use it.
+ if test "x${ATAN_DIR}" != "x"; then
+ if test -d ${ATAN_DIR}/lib; then
+ pbxlibdir="-L${ATAN_DIR}/lib"
+ else
+ pbxlibdir="-L${ATAN_DIR}"
+ fi
+ fi
+ pbxfuncname="atan"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ATAN_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ATAN_FOUND=yes
+else
+ AST_ATAN_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ATAN_FOUND}" = "yes"; then
+ ATAN_LIB="${pbxlibdir} -lm "
+ # if --with-ATAN=DIR has been specified, use it.
+ if test "x${ATAN_DIR}" != "x"; then
+ ATAN_INCLUDE="-I${ATAN_DIR}/include"
+ fi
+ ATAN_INCLUDE="${ATAN_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ATAN_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ATAN_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ATAN_HEADER_FOUND=1
+else
+ ATAN_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ATAN_HEADER_FOUND}" = "x0" ; then
+ ATAN_LIB=""
+ ATAN_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ATAN_LIB=""
+ fi
+ PBX_ATAN=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATAN 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATAN_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ATAN2}" != "x1" -a "${USE_ATAN2}" != "no"; then
+ pbxlibdir=""
+ # if --with-ATAN2=DIR has been specified, use it.
+ if test "x${ATAN2_DIR}" != "x"; then
+ if test -d ${ATAN2_DIR}/lib; then
+ pbxlibdir="-L${ATAN2_DIR}/lib"
+ else
+ pbxlibdir="-L${ATAN2_DIR}"
+ fi
+ fi
+ pbxfuncname="atan2"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ATAN2_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ATAN2_FOUND=yes
+else
+ AST_ATAN2_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ATAN2_FOUND}" = "yes"; then
+ ATAN2_LIB="${pbxlibdir} -lm "
+ # if --with-ATAN2=DIR has been specified, use it.
+ if test "x${ATAN2_DIR}" != "x"; then
+ ATAN2_INCLUDE="-I${ATAN2_DIR}/include"
+ fi
+ ATAN2_INCLUDE="${ATAN2_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ATAN2_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ATAN2_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ATAN2_HEADER_FOUND=1
+else
+ ATAN2_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ATAN2_HEADER_FOUND}" = "x0" ; then
+ ATAN2_LIB=""
+ ATAN2_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ATAN2_LIB=""
+ fi
+ PBX_ATAN2=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATAN2 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATAN2_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_POW}" != "x1" -a "${USE_POW}" != "no"; then
+ pbxlibdir=""
+ # if --with-POW=DIR has been specified, use it.
+ if test "x${POW_DIR}" != "x"; then
+ if test -d ${POW_DIR}/lib; then
+ pbxlibdir="-L${POW_DIR}/lib"
+ else
+ pbxlibdir="-L${POW_DIR}"
+ fi
+ fi
+ pbxfuncname="pow"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_POW_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_POW_FOUND=yes
+else
+ AST_POW_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_POW_FOUND}" = "yes"; then
+ POW_LIB="${pbxlibdir} -lm "
+ # if --with-POW=DIR has been specified, use it.
+ if test "x${POW_DIR}" != "x"; then
+ POW_INCLUDE="-I${POW_DIR}/include"
+ fi
+ POW_INCLUDE="${POW_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ POW_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${POW_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ POW_HEADER_FOUND=1
+else
+ POW_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${POW_HEADER_FOUND}" = "x0" ; then
+ POW_LIB=""
+ POW_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ POW_LIB=""
+ fi
+ PBX_POW=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_POW 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_POW_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_SQRT}" != "x1" -a "${USE_SQRT}" != "no"; then
+ pbxlibdir=""
+ # if --with-SQRT=DIR has been specified, use it.
+ if test "x${SQRT_DIR}" != "x"; then
+ if test -d ${SQRT_DIR}/lib; then
+ pbxlibdir="-L${SQRT_DIR}/lib"
+ else
+ pbxlibdir="-L${SQRT_DIR}"
+ fi
+ fi
+ pbxfuncname="sqrt"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SQRT_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SQRT_FOUND=yes
+else
+ AST_SQRT_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SQRT_FOUND}" = "yes"; then
+ SQRT_LIB="${pbxlibdir} -lm "
+ # if --with-SQRT=DIR has been specified, use it.
+ if test "x${SQRT_DIR}" != "x"; then
+ SQRT_INCLUDE="-I${SQRT_DIR}/include"
+ fi
+ SQRT_INCLUDE="${SQRT_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ SQRT_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SQRT_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ SQRT_HEADER_FOUND=1
+else
+ SQRT_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SQRT_HEADER_FOUND}" = "x0" ; then
+ SQRT_LIB=""
+ SQRT_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SQRT_LIB=""
+ fi
+ PBX_SQRT=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQRT 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQRT_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_RINT}" != "x1" -a "${USE_RINT}" != "no"; then
+ pbxlibdir=""
+ # if --with-RINT=DIR has been specified, use it.
+ if test "x${RINT_DIR}" != "x"; then
+ if test -d ${RINT_DIR}/lib; then
+ pbxlibdir="-L${RINT_DIR}/lib"
+ else
+ pbxlibdir="-L${RINT_DIR}"
+ fi
+ fi
+ pbxfuncname="rint"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_RINT_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_RINT_FOUND=yes
+else
+ AST_RINT_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_RINT_FOUND}" = "yes"; then
+ RINT_LIB="${pbxlibdir} -lm "
+ # if --with-RINT=DIR has been specified, use it.
+ if test "x${RINT_DIR}" != "x"; then
+ RINT_INCLUDE="-I${RINT_DIR}/include"
+ fi
+ RINT_INCLUDE="${RINT_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ RINT_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${RINT_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ RINT_HEADER_FOUND=1
+else
+ RINT_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${RINT_HEADER_FOUND}" = "x0" ; then
+ RINT_LIB=""
+ RINT_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ RINT_LIB=""
+ fi
+ PBX_RINT=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RINT 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RINT_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_EXP}" != "x1" -a "${USE_EXP}" != "no"; then
+ pbxlibdir=""
+ # if --with-EXP=DIR has been specified, use it.
+ if test "x${EXP_DIR}" != "x"; then
+ if test -d ${EXP_DIR}/lib; then
+ pbxlibdir="-L${EXP_DIR}/lib"
+ else
+ pbxlibdir="-L${EXP_DIR}"
+ fi
+ fi
+ pbxfuncname="exp"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_EXP_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_EXP_FOUND=yes
+else
+ AST_EXP_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_EXP_FOUND}" = "yes"; then
+ EXP_LIB="${pbxlibdir} -lm "
+ # if --with-EXP=DIR has been specified, use it.
+ if test "x${EXP_DIR}" != "x"; then
+ EXP_INCLUDE="-I${EXP_DIR}/include"
+ fi
+ EXP_INCLUDE="${EXP_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ EXP_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${EXP_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ EXP_HEADER_FOUND=1
+else
+ EXP_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${EXP_HEADER_FOUND}" = "x0" ; then
+ EXP_LIB=""
+ EXP_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ EXP_LIB=""
+ fi
+ PBX_EXP=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_EXP_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_LOG}" != "x1" -a "${USE_LOG}" != "no"; then
+ pbxlibdir=""
+ # if --with-LOG=DIR has been specified, use it.
+ if test "x${LOG_DIR}" != "x"; then
+ if test -d ${LOG_DIR}/lib; then
+ pbxlibdir="-L${LOG_DIR}/lib"
+ else
+ pbxlibdir="-L${LOG_DIR}"
+ fi
+ fi
+ pbxfuncname="log"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LOG_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LOG_FOUND=yes
+else
+ AST_LOG_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LOG_FOUND}" = "yes"; then
+ LOG_LIB="${pbxlibdir} -lm "
+ # if --with-LOG=DIR has been specified, use it.
+ if test "x${LOG_DIR}" != "x"; then
+ LOG_INCLUDE="-I${LOG_DIR}/include"
+ fi
+ LOG_INCLUDE="${LOG_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ LOG_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LOG_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ LOG_HEADER_FOUND=1
+else
+ LOG_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LOG_HEADER_FOUND}" = "x0" ; then
+ LOG_LIB=""
+ LOG_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LOG_LIB=""
+ fi
+ PBX_LOG=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LOG_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_REMAINDER}" != "x1" -a "${USE_REMAINDER}" != "no"; then
+ pbxlibdir=""
+ # if --with-REMAINDER=DIR has been specified, use it.
+ if test "x${REMAINDER_DIR}" != "x"; then
+ if test -d ${REMAINDER_DIR}/lib; then
+ pbxlibdir="-L${REMAINDER_DIR}/lib"
+ else
+ pbxlibdir="-L${REMAINDER_DIR}"
+ fi
+ fi
+ pbxfuncname="remainder"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_REMAINDER_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_REMAINDER_FOUND=yes
+else
+ AST_REMAINDER_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_REMAINDER_FOUND}" = "yes"; then
+ REMAINDER_LIB="${pbxlibdir} -lm "
+ # if --with-REMAINDER=DIR has been specified, use it.
+ if test "x${REMAINDER_DIR}" != "x"; then
+ REMAINDER_INCLUDE="-I${REMAINDER_DIR}/include"
+ fi
+ REMAINDER_INCLUDE="${REMAINDER_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ REMAINDER_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${REMAINDER_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ REMAINDER_HEADER_FOUND=1
+else
+ REMAINDER_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${REMAINDER_HEADER_FOUND}" = "x0" ; then
+ REMAINDER_LIB=""
+ REMAINDER_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ REMAINDER_LIB=""
+ fi
+ PBX_REMAINDER=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_REMAINDER 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_REMAINDER_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_FMOD}" != "x1" -a "${USE_FMOD}" != "no"; then
+ pbxlibdir=""
+ # if --with-FMOD=DIR has been specified, use it.
+ if test "x${FMOD_DIR}" != "x"; then
+ if test -d ${FMOD_DIR}/lib; then
+ pbxlibdir="-L${FMOD_DIR}/lib"
+ else
+ pbxlibdir="-L${FMOD_DIR}"
+ fi
+ fi
+ pbxfuncname="fmod"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_FMOD_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_FMOD_FOUND=yes
+else
+ AST_FMOD_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_FMOD_FOUND}" = "yes"; then
+ FMOD_LIB="${pbxlibdir} -lm "
+ # if --with-FMOD=DIR has been specified, use it.
+ if test "x${FMOD_DIR}" != "x"; then
+ FMOD_INCLUDE="-I${FMOD_DIR}/include"
+ fi
+ FMOD_INCLUDE="${FMOD_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ FMOD_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${FMOD_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ FMOD_HEADER_FOUND=1
+else
+ FMOD_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${FMOD_HEADER_FOUND}" = "x0" ; then
+ FMOD_LIB=""
+ FMOD_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ FMOD_LIB=""
+ fi
+ PBX_FMOD=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FMOD 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FMOD_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ROUND}" != "x1" -a "${USE_ROUND}" != "no"; then
+ pbxlibdir=""
+ # if --with-ROUND=DIR has been specified, use it.
+ if test "x${ROUND_DIR}" != "x"; then
+ if test -d ${ROUND_DIR}/lib; then
+ pbxlibdir="-L${ROUND_DIR}/lib"
+ else
+ pbxlibdir="-L${ROUND_DIR}"
+ fi
+ fi
+ pbxfuncname="round"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ROUND_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ROUND_FOUND=yes
+else
+ AST_ROUND_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ROUND_FOUND}" = "yes"; then
+ ROUND_LIB="${pbxlibdir} -lm "
+ # if --with-ROUND=DIR has been specified, use it.
+ if test "x${ROUND_DIR}" != "x"; then
+ ROUND_INCLUDE="-I${ROUND_DIR}/include"
+ fi
+ ROUND_INCLUDE="${ROUND_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ ROUND_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ROUND_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ ROUND_HEADER_FOUND=1
+else
+ ROUND_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ROUND_HEADER_FOUND}" = "x0" ; then
+ ROUND_LIB=""
+ ROUND_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ROUND_LIB=""
+ fi
+ PBX_ROUND=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ROUND 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ROUND_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_TRUNC}" != "x1" -a "${USE_TRUNC}" != "no"; then
+ pbxlibdir=""
+ # if --with-TRUNC=DIR has been specified, use it.
+ if test "x${TRUNC_DIR}" != "x"; then
+ if test -d ${TRUNC_DIR}/lib; then
+ pbxlibdir="-L${TRUNC_DIR}/lib"
+ else
+ pbxlibdir="-L${TRUNC_DIR}"
+ fi
+ fi
+ pbxfuncname="trunc"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TRUNC_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TRUNC_FOUND=yes
+else
+ AST_TRUNC_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TRUNC_FOUND}" = "yes"; then
+ TRUNC_LIB="${pbxlibdir} -lm "
+ # if --with-TRUNC=DIR has been specified, use it.
+ if test "x${TRUNC_DIR}" != "x"; then
+ TRUNC_INCLUDE="-I${TRUNC_DIR}/include"
+ fi
+ TRUNC_INCLUDE="${TRUNC_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ TRUNC_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TRUNC_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ TRUNC_HEADER_FOUND=1
+else
+ TRUNC_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TRUNC_HEADER_FOUND}" = "x0" ; then
+ TRUNC_LIB=""
+ TRUNC_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TRUNC_LIB=""
+ fi
+ PBX_TRUNC=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TRUNC 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TRUNC_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_STRTOD}" != "x1" -a "${USE_STRTOD}" != "no"; then
+ pbxlibdir=""
+ # if --with-STRTOD=DIR has been specified, use it.
+ if test "x${STRTOD_DIR}" != "x"; then
+ if test -d ${STRTOD_DIR}/lib; then
+ pbxlibdir="-L${STRTOD_DIR}/lib"
+ else
+ pbxlibdir="-L${STRTOD_DIR}"
+ fi
+ fi
+ pbxfuncname="strtod"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_STRTOD_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_c_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lc" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lc... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_STRTOD_FOUND=yes
+else
+ AST_STRTOD_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_STRTOD_FOUND}" = "yes"; then
+ STRTOD_LIB="${pbxlibdir} -lc "
+ # if --with-STRTOD=DIR has been specified, use it.
+ if test "x${STRTOD_DIR}" != "x"; then
+ STRTOD_INCLUDE="-I${STRTOD_DIR}/include"
+ fi
+ STRTOD_INCLUDE="${STRTOD_INCLUDE} "
+ if test "xstdlib.h" = "x" ; then # no header, assume found
+ STRTOD_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${STRTOD_INCLUDE} "
+ if test "${ac_cv_header_stdlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for stdlib.h" >&5
+echo $ECHO_N "checking for stdlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_stdlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking stdlib.h usability" >&5
+echo $ECHO_N "checking stdlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <stdlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking stdlib.h presence" >&5
+echo $ECHO_N "checking stdlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: stdlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: stdlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: stdlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: stdlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: stdlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: stdlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: stdlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: stdlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: stdlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for stdlib.h" >&5
+echo $ECHO_N "checking for stdlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_stdlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_stdlib_h" >&6; }
+
+fi
+if test $ac_cv_header_stdlib_h = yes; then
+ STRTOD_HEADER_FOUND=1
+else
+ STRTOD_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${STRTOD_HEADER_FOUND}" = "x0" ; then
+ STRTOD_LIB=""
+ STRTOD_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ STRTOD_LIB=""
+ fi
+ PBX_STRTOD=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRTOD 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRTOD_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_FLOOR}" != "x1" -a "${USE_FLOOR}" != "no"; then
+ pbxlibdir=""
+ # if --with-FLOOR=DIR has been specified, use it.
+ if test "x${FLOOR_DIR}" != "x"; then
+ if test -d ${FLOOR_DIR}/lib; then
+ pbxlibdir="-L${FLOOR_DIR}/lib"
+ else
+ pbxlibdir="-L${FLOOR_DIR}"
+ fi
+ fi
+ pbxfuncname="floor"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_FLOOR_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_FLOOR_FOUND=yes
+else
+ AST_FLOOR_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_FLOOR_FOUND}" = "yes"; then
+ FLOOR_LIB="${pbxlibdir} -lm "
+ # if --with-FLOOR=DIR has been specified, use it.
+ if test "x${FLOOR_DIR}" != "x"; then
+ FLOOR_INCLUDE="-I${FLOOR_DIR}/include"
+ fi
+ FLOOR_INCLUDE="${FLOOR_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ FLOOR_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${FLOOR_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ FLOOR_HEADER_FOUND=1
+else
+ FLOOR_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${FLOOR_HEADER_FOUND}" = "x0" ; then
+ FLOOR_LIB=""
+ FLOOR_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ FLOOR_LIB=""
+ fi
+ PBX_FLOOR=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FLOOR 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FLOOR_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_CEIL}" != "x1" -a "${USE_CEIL}" != "no"; then
+ pbxlibdir=""
+ # if --with-CEIL=DIR has been specified, use it.
+ if test "x${CEIL_DIR}" != "x"; then
+ if test -d ${CEIL_DIR}/lib; then
+ pbxlibdir="-L${CEIL_DIR}/lib"
+ else
+ pbxlibdir="-L${CEIL_DIR}"
+ fi
+ fi
+ pbxfuncname="ceil"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_CEIL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_m_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lm" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lm... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_CEIL_FOUND=yes
+else
+ AST_CEIL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_CEIL_FOUND}" = "yes"; then
+ CEIL_LIB="${pbxlibdir} -lm "
+ # if --with-CEIL=DIR has been specified, use it.
+ if test "x${CEIL_DIR}" != "x"; then
+ CEIL_INCLUDE="-I${CEIL_DIR}/include"
+ fi
+ CEIL_INCLUDE="${CEIL_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ CEIL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CEIL_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ CEIL_HEADER_FOUND=1
+else
+ CEIL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${CEIL_HEADER_FOUND}" = "x0" ; then
+ CEIL_LIB=""
+ CEIL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ CEIL_LIB=""
+ fi
+ PBX_CEIL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CEIL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CEIL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+GSM_INTERNAL="yes"
+
+GSM_SYSTEM="yes"
+if test "${USE_GSM}" != "no"; then
+ if test "${GSM_DIR}" = "internal"; then
+ GSM_SYSTEM="no"
+ elif test "${GSM_DIR}" != ""; then
+ GSM_INTERNAL="no"
+ fi
+ if test "${GSM_SYSTEM}" = "yes"; then
+ gsmlibdir=""
+ if test "x${GSM_DIR}" != "x"; then
+ if test -d ${GSM_DIR}/lib; then
+ gsmlibdir="-L${GSM_DIR}/lib"
+ else
+ gsmlibdir="-L${GSM_DIR}"
+ fi
+ fi
+ { echo "$as_me:$LINENO: checking for gsm_create in -lgsm" >&5
+echo $ECHO_N "checking for gsm_create in -lgsm... $ECHO_C" >&6; }
+if test "${ac_cv_lib_gsm_gsm_create+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgsm ${gsmlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gsm_create ();
+int
+main ()
+{
+return gsm_create ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_gsm_gsm_create=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_gsm_gsm_create=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_gsm_gsm_create" >&5
+echo "${ECHO_T}$ac_cv_lib_gsm_gsm_create" >&6; }
+if test $ac_cv_lib_gsm_gsm_create = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_GSM 1
+_ACEOF
+
+fi
+
+ if test "${ac_cv_lib_gsm_gsm_create}" = "yes"; then
+ if test "x${GSM_DIR}" != "x" ; then
+ as_ac_Header=`echo "ac_cv_header_${GSM_DIR}/include/gsm.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${GSM_DIR}/include/gsm.h" >&5
+echo $ECHO_N "checking for ${GSM_DIR}/include/gsm.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${GSM_DIR}/include/gsm.h usability" >&5
+echo $ECHO_N "checking ${GSM_DIR}/include/gsm.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${GSM_DIR}/include/gsm.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${GSM_DIR}/include/gsm.h presence" >&5
+echo $ECHO_N "checking ${GSM_DIR}/include/gsm.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${GSM_DIR}/include/gsm.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${GSM_DIR}/include/gsm.h" >&5
+echo $ECHO_N "checking for ${GSM_DIR}/include/gsm.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ GSM_HEADER_FOUND=1
+else
+ GSM_HEADER_FOUND=0
+fi
+
+
+ as_ac_Header=`echo "ac_cv_header_${GSM_DIR}/include/gsm/gsm.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${GSM_DIR}/include/gsm/gsm.h" >&5
+echo $ECHO_N "checking for ${GSM_DIR}/include/gsm/gsm.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${GSM_DIR}/include/gsm/gsm.h usability" >&5
+echo $ECHO_N "checking ${GSM_DIR}/include/gsm/gsm.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${GSM_DIR}/include/gsm/gsm.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${GSM_DIR}/include/gsm/gsm.h presence" >&5
+echo $ECHO_N "checking ${GSM_DIR}/include/gsm/gsm.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${GSM_DIR}/include/gsm/gsm.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${GSM_DIR}/include/gsm/gsm.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${GSM_DIR}/include/gsm/gsm.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${GSM_DIR}/include/gsm/gsm.h" >&5
+echo $ECHO_N "checking for ${GSM_DIR}/include/gsm/gsm.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ GSM_GSM_HEADER_FOUND=1
+else
+ GSM_GSM_HEADER_FOUND=0
+fi
+
+
+ else
+ if test "${ac_cv_header_gsm_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for gsm.h" >&5
+echo $ECHO_N "checking for gsm.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_gsm_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_gsm_h" >&5
+echo "${ECHO_T}$ac_cv_header_gsm_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking gsm.h usability" >&5
+echo $ECHO_N "checking gsm.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <gsm.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking gsm.h presence" >&5
+echo $ECHO_N "checking gsm.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <gsm.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: gsm.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: gsm.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: gsm.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: gsm.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: gsm.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: gsm.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: gsm.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: gsm.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: gsm.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: gsm.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for gsm.h" >&5
+echo $ECHO_N "checking for gsm.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_gsm_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_gsm_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_gsm_h" >&5
+echo "${ECHO_T}$ac_cv_header_gsm_h" >&6; }
+
+fi
+if test $ac_cv_header_gsm_h = yes; then
+ GSM_HEADER_FOUND=1
+else
+ GSM_HEADER_FOUND=0
+fi
+
+
+ if test "${ac_cv_header_gsm_gsm_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for gsm/gsm.h" >&5
+echo $ECHO_N "checking for gsm/gsm.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_gsm_gsm_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_gsm_gsm_h" >&5
+echo "${ECHO_T}$ac_cv_header_gsm_gsm_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking gsm/gsm.h usability" >&5
+echo $ECHO_N "checking gsm/gsm.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <gsm/gsm.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking gsm/gsm.h presence" >&5
+echo $ECHO_N "checking gsm/gsm.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <gsm/gsm.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: gsm/gsm.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: gsm/gsm.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: gsm/gsm.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: gsm/gsm.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: gsm/gsm.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: gsm/gsm.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: gsm/gsm.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: gsm/gsm.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: gsm/gsm.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for gsm/gsm.h" >&5
+echo $ECHO_N "checking for gsm/gsm.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_gsm_gsm_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_gsm_gsm_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_gsm_gsm_h" >&5
+echo "${ECHO_T}$ac_cv_header_gsm_gsm_h" >&6; }
+
+fi
+if test $ac_cv_header_gsm_gsm_h = yes; then
+ GSM_GSM_HEADER_FOUND=1
+else
+ GSM_GSM_HEADER_FOUND=0
+fi
+
+
+ fi
+ if test "${GSM_HEADER_FOUND}" = "0" ; then
+ if test "{GSM_GSM_HEADER_FOUND}" = "0" ; then
+ if test "x${GSM_MANDATORY}" = "xyes" ; then
+ { echo "$as_me:$LINENO: ***" >&5
+echo "$as_me: ***" >&6;}
+ { echo "$as_me:$LINENO: *** It appears that you do not have the gsm development package installed." >&5
+echo "$as_me: *** It appears that you do not have the gsm development package installed." >&6;}
+ { echo "$as_me:$LINENO: *** Please install it to include ${GSM_DESCRIP} support, or re-run configure" >&5
+echo "$as_me: *** Please install it to include ${GSM_DESCRIP} support, or re-run configure" >&6;}
+ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${GSM_OPTION}" >&5
+echo "$as_me: *** without explicitly specifying --with-${GSM_OPTION}" >&6;}
+ exit 1
+ fi
+ fi
+ fi
+ GSM_OK=0
+ if test "${GSM_HEADER_FOUND}" = "1" ; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_GSM_HEADER 1
+_ACEOF
+
+ GSM_OK=1
+ else
+ if test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_GSM_GSM_HEADER 1
+_ACEOF
+
+ GSM_OK=1
+ fi
+ fi
+ if test "${GSM_OK}" = "1" ; then
+ GSM_LIB="-lgsm"
+ if test "x${GSM_DIR}" != "x"; then
+ GSM_LIB="${gsmlibdir} ${GSM_LIB}"
+ GSM_INCLUDE="-I${GSM_DIR}/include"
+ fi
+ PBX_GSM=1
+ GSM_INTERNAL="no"
+ fi
+ fi
+ fi
+ if test "${GSM_INTERNAL}" = "yes"; then
+ PBX_GSM=1
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_GSM_HEADER 1
+_ACEOF
+
+ fi
+fi
+
+if test "${host_os}" != "linux-gnu" ; then
+
+if test "x${PBX_ICONV}" != "x1" -a "${USE_ICONV}" != "no"; then
+ pbxlibdir=""
+ # if --with-ICONV=DIR has been specified, use it.
+ if test "x${ICONV_DIR}" != "x"; then
+ if test -d ${ICONV_DIR}/lib; then
+ pbxlibdir="-L${ICONV_DIR}/lib"
+ else
+ pbxlibdir="-L${ICONV_DIR}"
+ fi
+ fi
+ pbxfuncname="iconv_open"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ICONV_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_iconv_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -liconv" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -liconv... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-liconv ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ICONV_FOUND=yes
+else
+ AST_ICONV_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ICONV_FOUND}" = "yes"; then
+ ICONV_LIB="${pbxlibdir} -liconv "
+ # if --with-ICONV=DIR has been specified, use it.
+ if test "x${ICONV_DIR}" != "x"; then
+ ICONV_INCLUDE="-I${ICONV_DIR}/include"
+ fi
+ ICONV_INCLUDE="${ICONV_INCLUDE} "
+ if test "xiconv.h" = "x" ; then # no header, assume found
+ ICONV_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ICONV_INCLUDE} "
+ if test "${ac_cv_header_iconv_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for iconv.h" >&5
+echo $ECHO_N "checking for iconv.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_iconv_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_iconv_h" >&5
+echo "${ECHO_T}$ac_cv_header_iconv_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking iconv.h usability" >&5
+echo $ECHO_N "checking iconv.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <iconv.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking iconv.h presence" >&5
+echo $ECHO_N "checking iconv.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <iconv.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: iconv.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: iconv.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iconv.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: iconv.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: iconv.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: iconv.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iconv.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: iconv.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iconv.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: iconv.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iconv.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: iconv.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iconv.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: iconv.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iconv.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: iconv.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for iconv.h" >&5
+echo $ECHO_N "checking for iconv.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_iconv_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_iconv_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_iconv_h" >&5
+echo "${ECHO_T}$ac_cv_header_iconv_h" >&6; }
+
+fi
+if test $ac_cv_header_iconv_h = yes; then
+ ICONV_HEADER_FOUND=1
+else
+ ICONV_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ICONV_HEADER_FOUND}" = "x0" ; then
+ ICONV_LIB=""
+ ICONV_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ICONV_LIB=""
+ fi
+ PBX_ICONV=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ICONV 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ICONV_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+else
+ PBX_ICONV=1
+fi
+
+
+if test "x${PBX_IKSEMEL}" != "x1" -a "${USE_IKSEMEL}" != "no"; then
+ pbxlibdir=""
+ # if --with-IKSEMEL=DIR has been specified, use it.
+ if test "x${IKSEMEL_DIR}" != "x"; then
+ if test -d ${IKSEMEL_DIR}/lib; then
+ pbxlibdir="-L${IKSEMEL_DIR}/lib"
+ else
+ pbxlibdir="-L${IKSEMEL_DIR}"
+ fi
+ fi
+ pbxfuncname="iks_start_sasl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_IKSEMEL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_iksemel_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -liksemel" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -liksemel... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-liksemel ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_IKSEMEL_FOUND=yes
+else
+ AST_IKSEMEL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_IKSEMEL_FOUND}" = "yes"; then
+ IKSEMEL_LIB="${pbxlibdir} -liksemel "
+ # if --with-IKSEMEL=DIR has been specified, use it.
+ if test "x${IKSEMEL_DIR}" != "x"; then
+ IKSEMEL_INCLUDE="-I${IKSEMEL_DIR}/include"
+ fi
+ IKSEMEL_INCLUDE="${IKSEMEL_INCLUDE} "
+ if test "xiksemel.h" = "x" ; then # no header, assume found
+ IKSEMEL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${IKSEMEL_INCLUDE} "
+ if test "${ac_cv_header_iksemel_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for iksemel.h" >&5
+echo $ECHO_N "checking for iksemel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_iksemel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_iksemel_h" >&5
+echo "${ECHO_T}$ac_cv_header_iksemel_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking iksemel.h usability" >&5
+echo $ECHO_N "checking iksemel.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <iksemel.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking iksemel.h presence" >&5
+echo $ECHO_N "checking iksemel.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <iksemel.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: iksemel.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: iksemel.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: iksemel.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: iksemel.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: iksemel.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: iksemel.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: iksemel.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: iksemel.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: iksemel.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for iksemel.h" >&5
+echo $ECHO_N "checking for iksemel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_iksemel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_iksemel_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_iksemel_h" >&5
+echo "${ECHO_T}$ac_cv_header_iksemel_h" >&6; }
+
+fi
+if test $ac_cv_header_iksemel_h = yes; then
+ IKSEMEL_HEADER_FOUND=1
+else
+ IKSEMEL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${IKSEMEL_HEADER_FOUND}" = "x0" ; then
+ IKSEMEL_LIB=""
+ IKSEMEL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ IKSEMEL_LIB=""
+ fi
+ PBX_IKSEMEL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_IKSEMEL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_IKSEMEL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "${USE_IMAP_TK}" != "no"; then
+ if test "${IMAP_TK_DIR}" = "system" ; then
+ { echo "$as_me:$LINENO: Checking for system c-client library..." >&5
+echo "$as_me: Checking for system c-client library..." >&6;}
+ elif test "${IMAP_TK_DIR}" = ""; then
+ IMAP_TK_DIR=`pwd`"/../imap-2004g"
+ if test -n "${IMAP_TK_MANDATORY}"; then
+ { echo "$as_me:$LINENO: The --with-imap option does not search your system for installed" >&5
+echo "$as_me: The --with-imap option does not search your system for installed" >&6;}
+ { echo "$as_me:$LINENO: c-client library/header files. Since you did not provide a path" >&5
+echo "$as_me: c-client library/header files. Since you did not provide a path" >&6;}
+ { echo "$as_me:$LINENO: the configure script will assume you have placed built the c-client" >&5
+echo "$as_me: the configure script will assume you have placed built the c-client" >&6;}
+ { echo "$as_me:$LINENO: files at ${IMAP_TK_DIR}, as outlined in the doc/imapstorage.txt file." >&5
+echo "$as_me: files at ${IMAP_TK_DIR}, as outlined in the doc/imapstorage.txt file." >&6;}
+ fi
+ fi
+ if test "${IMAP_TK_DIR}" != "system" ; then
+ { echo "$as_me:$LINENO: checking for UW IMAP Toolkit c-client library" >&5
+echo $ECHO_N "checking for UW IMAP Toolkit c-client library... $ECHO_C" >&6; }
+ fi
+ saved_cppflags="${CPPFLAGS}"
+ saved_libs="${LIBS}"
+ if test "${IMAP_TK_DIR}" = "system" ; then
+ imap_ldflags=""
+ imap_libs="-lc-client"
+ imap_include="-DUSE_SYSTEM_IMAP"
+ elif test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then
+ imap_ldflags=`cat ${IMAP_TK_DIR}/c-client/LDFLAGS`
+ imap_libs="${IMAP_TK_DIR}/c-client/c-client.a"
+ imap_include="-I${IMAP_TK_DIR}/c-client"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${imap_include}"
+ LIBS="${LIBS} ${imap_libs} "`echo ${imap_ldflags}`
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef USE_SYSTEM_IMAP
+ #include <imap/c-client.h>
+ #else
+ #include "c-client.h"
+ #endif
+ void mm_searched (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_exists (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_expunged (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_flags (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+ {
+ }
+ void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+ {
+ }
+ void mm_log (char *string,long errflg)
+ {
+ }
+ void mm_dlog (char *string)
+ {
+ }
+ void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
+ {
+ }
+ void mm_critical (MAILSTREAM *stream)
+ {
+ }
+ void mm_nocritical (MAILSTREAM *stream)
+ {
+ }
+ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+ {
+ }
+ void mm_fatal (char *string)
+ {
+ }
+int
+main ()
+{
+
+ MAILSTREAM *foo = mail_open(NULL, "", 0);
+
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_imap_tk="yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_imap_tk="no"
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ if test "${ac_cv_imap_tk}" = "yes"; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef USE_SYSTEM_IMAP
+ #include <imap/c-client.h>
+ #else
+ #include "c-client.h"
+ #endif
+ void mm_searched (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_exists (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_expunged (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_flags (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+ {
+ }
+ void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+ {
+ }
+ void mm_log (char *string,long errflg)
+ {
+ }
+ void mm_dlog (char *string)
+ {
+ }
+ void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
+ {
+ }
+ void mm_critical (MAILSTREAM *stream)
+ {
+ }
+ void mm_nocritical (MAILSTREAM *stream)
+ {
+ }
+ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+ {
+ }
+ void mm_fatal (char *string)
+ {
+ }
+int
+main ()
+{
+
+ long check = mail_expunge_full(NULL, "", 0);
+
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_imap_tk2006="yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_imap_tk2006="no"
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ fi
+ CPPFLAGS="${saved_cppflags}"
+ LIBS="${saved_libs}"
+ if test "${ac_cv_imap_tk}" = "yes"; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ IMAP_TK_LIB="${imap_libs} "`echo ${imap_ldflags}`
+ IMAP_TK_INCLUDE="${imap_include}"
+ PBX_IMAP_TK=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_IMAP_TK 1
+_ACEOF
+
+ if test "${ac_cv_imap_tk2006}" = "yes"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_IMAP_TK2006 1
+_ACEOF
+
+ fi
+ else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ fi
+fi
+
+
+if test "x${PBX_JACK}" != "x1" -a "${USE_JACK}" != "no"; then
+ pbxlibdir=""
+ # if --with-JACK=DIR has been specified, use it.
+ if test "x${JACK_DIR}" != "x"; then
+ if test -d ${JACK_DIR}/lib; then
+ pbxlibdir="-L${JACK_DIR}/lib"
+ else
+ pbxlibdir="-L${JACK_DIR}"
+ fi
+ fi
+ pbxfuncname="jack_activate"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_JACK_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_jack_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -ljack" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -ljack... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljack ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_JACK_FOUND=yes
+else
+ AST_JACK_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_JACK_FOUND}" = "yes"; then
+ JACK_LIB="${pbxlibdir} -ljack "
+ # if --with-JACK=DIR has been specified, use it.
+ if test "x${JACK_DIR}" != "x"; then
+ JACK_INCLUDE="-I${JACK_DIR}/include"
+ fi
+ JACK_INCLUDE="${JACK_INCLUDE} "
+ if test "xjack/jack.h" = "x" ; then # no header, assume found
+ JACK_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${JACK_INCLUDE} "
+ if test "${ac_cv_header_jack_jack_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for jack/jack.h" >&5
+echo $ECHO_N "checking for jack/jack.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_jack_jack_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_jack_jack_h" >&5
+echo "${ECHO_T}$ac_cv_header_jack_jack_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking jack/jack.h usability" >&5
+echo $ECHO_N "checking jack/jack.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <jack/jack.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking jack/jack.h presence" >&5
+echo $ECHO_N "checking jack/jack.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <jack/jack.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: jack/jack.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: jack/jack.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: jack/jack.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: jack/jack.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: jack/jack.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: jack/jack.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: jack/jack.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: jack/jack.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: jack/jack.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for jack/jack.h" >&5
+echo $ECHO_N "checking for jack/jack.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_jack_jack_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_jack_jack_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_jack_jack_h" >&5
+echo "${ECHO_T}$ac_cv_header_jack_jack_h" >&6; }
+
+fi
+if test $ac_cv_header_jack_jack_h = yes; then
+ JACK_HEADER_FOUND=1
+else
+ JACK_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${JACK_HEADER_FOUND}" = "x0" ; then
+ JACK_LIB=""
+ JACK_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ JACK_LIB=""
+ fi
+ PBX_JACK=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_JACK 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_JACK_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+# Needed by unixodbc
+
+if test "x${PBX_LTDL}" != "x1" -a "${USE_LTDL}" != "no"; then
+ pbxlibdir=""
+ # if --with-LTDL=DIR has been specified, use it.
+ if test "x${LTDL_DIR}" != "x"; then
+ if test -d ${LTDL_DIR}/lib; then
+ pbxlibdir="-L${LTDL_DIR}/lib"
+ else
+ pbxlibdir="-L${LTDL_DIR}"
+ fi
+ fi
+ pbxfuncname="lt_dlinit"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LTDL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ltdl_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lltdl" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lltdl... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lltdl ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LTDL_FOUND=yes
+else
+ AST_LTDL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LTDL_FOUND}" = "yes"; then
+ LTDL_LIB="${pbxlibdir} -lltdl "
+ # if --with-LTDL=DIR has been specified, use it.
+ if test "x${LTDL_DIR}" != "x"; then
+ LTDL_INCLUDE="-I${LTDL_DIR}/include"
+ fi
+ LTDL_INCLUDE="${LTDL_INCLUDE} "
+ if test "xltdl.h" = "x" ; then # no header, assume found
+ LTDL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LTDL_INCLUDE} "
+ if test "${ac_cv_header_ltdl_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for ltdl.h" >&5
+echo $ECHO_N "checking for ltdl.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_ltdl_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_ltdl_h" >&5
+echo "${ECHO_T}$ac_cv_header_ltdl_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ltdl.h usability" >&5
+echo $ECHO_N "checking ltdl.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <ltdl.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ltdl.h presence" >&5
+echo $ECHO_N "checking ltdl.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ltdl.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ltdl.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ltdl.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ltdl.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ltdl.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ltdl.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ltdl.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ltdl.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ltdl.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ltdl.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ltdl.h" >&5
+echo $ECHO_N "checking for ltdl.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_ltdl_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_ltdl_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_ltdl_h" >&5
+echo "${ECHO_T}$ac_cv_header_ltdl_h" >&6; }
+
+fi
+if test $ac_cv_header_ltdl_h = yes; then
+ LTDL_HEADER_FOUND=1
+else
+ LTDL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LTDL_HEADER_FOUND}" = "x0" ; then
+ LTDL_LIB=""
+ LTDL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LTDL_LIB=""
+ fi
+ PBX_LTDL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LTDL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LTDL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_MISDN}" != "x1" -a "${USE_MISDN}" != "no"; then
+ pbxlibdir=""
+ # if --with-MISDN=DIR has been specified, use it.
+ if test "x${MISDN_DIR}" != "x"; then
+ if test -d ${MISDN_DIR}/lib; then
+ pbxlibdir="-L${MISDN_DIR}/lib"
+ else
+ pbxlibdir="-L${MISDN_DIR}"
+ fi
+ fi
+ pbxfuncname="mISDN_open"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_MISDN_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_mISDN_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lmISDN" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lmISDN... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lmISDN ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_MISDN_FOUND=yes
+else
+ AST_MISDN_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_MISDN_FOUND}" = "yes"; then
+ MISDN_LIB="${pbxlibdir} -lmISDN "
+ # if --with-MISDN=DIR has been specified, use it.
+ if test "x${MISDN_DIR}" != "x"; then
+ MISDN_INCLUDE="-I${MISDN_DIR}/include"
+ fi
+ MISDN_INCLUDE="${MISDN_INCLUDE} "
+ if test "xmISDNuser/mISDNlib.h" = "x" ; then # no header, assume found
+ MISDN_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${MISDN_INCLUDE} "
+ if test "${ac_cv_header_mISDNuser_mISDNlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for mISDNuser/mISDNlib.h" >&5
+echo $ECHO_N "checking for mISDNuser/mISDNlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_mISDNuser_mISDNlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_mISDNuser_mISDNlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_mISDNuser_mISDNlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking mISDNuser/mISDNlib.h usability" >&5
+echo $ECHO_N "checking mISDNuser/mISDNlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <mISDNuser/mISDNlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking mISDNuser/mISDNlib.h presence" >&5
+echo $ECHO_N "checking mISDNuser/mISDNlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <mISDNuser/mISDNlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/mISDNlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: mISDNuser/mISDNlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for mISDNuser/mISDNlib.h" >&5
+echo $ECHO_N "checking for mISDNuser/mISDNlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_mISDNuser_mISDNlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_mISDNuser_mISDNlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_mISDNuser_mISDNlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_mISDNuser_mISDNlib_h" >&6; }
+
+fi
+if test $ac_cv_header_mISDNuser_mISDNlib_h = yes; then
+ MISDN_HEADER_FOUND=1
+else
+ MISDN_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${MISDN_HEADER_FOUND}" = "x0" ; then
+ MISDN_LIB=""
+ MISDN_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ MISDN_LIB=""
+ fi
+ PBX_MISDN=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_MISDN 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_MISDN_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "${PBX_MISDN}" = 1; then
+
+if test "x${PBX_ISDNNET}" != "x1" -a "${USE_ISDNNET}" != "no"; then
+ pbxlibdir=""
+ # if --with-ISDNNET=DIR has been specified, use it.
+ if test "x${ISDNNET_DIR}" != "x"; then
+ if test -d ${ISDNNET_DIR}/lib; then
+ pbxlibdir="-L${ISDNNET_DIR}/lib"
+ else
+ pbxlibdir="-L${ISDNNET_DIR}"
+ fi
+ fi
+ pbxfuncname="init_manager"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ISDNNET_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_isdnnet_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lisdnnet" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lisdnnet... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lisdnnet ${pbxlibdir} -lmISDN -lpthread $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ISDNNET_FOUND=yes
+else
+ AST_ISDNNET_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ISDNNET_FOUND}" = "yes"; then
+ ISDNNET_LIB="${pbxlibdir} -lisdnnet -lmISDN -lpthread"
+ # if --with-ISDNNET=DIR has been specified, use it.
+ if test "x${ISDNNET_DIR}" != "x"; then
+ ISDNNET_INCLUDE="-I${ISDNNET_DIR}/include"
+ fi
+ ISDNNET_INCLUDE="${ISDNNET_INCLUDE} "
+ if test "xmISDNuser/isdn_net.h" = "x" ; then # no header, assume found
+ ISDNNET_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ISDNNET_INCLUDE} "
+ if test "${ac_cv_header_mISDNuser_isdn_net_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for mISDNuser/isdn_net.h" >&5
+echo $ECHO_N "checking for mISDNuser/isdn_net.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_mISDNuser_isdn_net_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_mISDNuser_isdn_net_h" >&5
+echo "${ECHO_T}$ac_cv_header_mISDNuser_isdn_net_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking mISDNuser/isdn_net.h usability" >&5
+echo $ECHO_N "checking mISDNuser/isdn_net.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <mISDNuser/isdn_net.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking mISDNuser/isdn_net.h presence" >&5
+echo $ECHO_N "checking mISDNuser/isdn_net.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <mISDNuser/isdn_net.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/isdn_net.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: mISDNuser/isdn_net.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for mISDNuser/isdn_net.h" >&5
+echo $ECHO_N "checking for mISDNuser/isdn_net.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_mISDNuser_isdn_net_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_mISDNuser_isdn_net_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_mISDNuser_isdn_net_h" >&5
+echo "${ECHO_T}$ac_cv_header_mISDNuser_isdn_net_h" >&6; }
+
+fi
+if test $ac_cv_header_mISDNuser_isdn_net_h = yes; then
+ ISDNNET_HEADER_FOUND=1
+else
+ ISDNNET_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ISDNNET_HEADER_FOUND}" = "x0" ; then
+ ISDNNET_LIB=""
+ ISDNNET_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ISDNNET_LIB=""
+ fi
+ PBX_ISDNNET=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ISDNNET 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ISDNNET_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_SUPPSERV}" != "x1" -a "${USE_SUPPSERV}" != "no"; then
+ pbxlibdir=""
+ # if --with-SUPPSERV=DIR has been specified, use it.
+ if test "x${SUPPSERV_DIR}" != "x"; then
+ if test -d ${SUPPSERV_DIR}/lib; then
+ pbxlibdir="-L${SUPPSERV_DIR}/lib"
+ else
+ pbxlibdir="-L${SUPPSERV_DIR}"
+ fi
+ fi
+ pbxfuncname="encodeFac"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SUPPSERV_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_suppserv_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lsuppserv" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lsuppserv... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsuppserv ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SUPPSERV_FOUND=yes
+else
+ AST_SUPPSERV_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SUPPSERV_FOUND}" = "yes"; then
+ SUPPSERV_LIB="${pbxlibdir} -lsuppserv "
+ # if --with-SUPPSERV=DIR has been specified, use it.
+ if test "x${SUPPSERV_DIR}" != "x"; then
+ SUPPSERV_INCLUDE="-I${SUPPSERV_DIR}/include"
+ fi
+ SUPPSERV_INCLUDE="${SUPPSERV_INCLUDE} "
+ if test "xmISDNuser/suppserv.h" = "x" ; then # no header, assume found
+ SUPPSERV_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SUPPSERV_INCLUDE} "
+ if test "${ac_cv_header_mISDNuser_suppserv_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for mISDNuser/suppserv.h" >&5
+echo $ECHO_N "checking for mISDNuser/suppserv.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_mISDNuser_suppserv_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_mISDNuser_suppserv_h" >&5
+echo "${ECHO_T}$ac_cv_header_mISDNuser_suppserv_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking mISDNuser/suppserv.h usability" >&5
+echo $ECHO_N "checking mISDNuser/suppserv.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <mISDNuser/suppserv.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking mISDNuser/suppserv.h presence" >&5
+echo $ECHO_N "checking mISDNuser/suppserv.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <mISDNuser/suppserv.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: mISDNuser/suppserv.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: mISDNuser/suppserv.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for mISDNuser/suppserv.h" >&5
+echo $ECHO_N "checking for mISDNuser/suppserv.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_mISDNuser_suppserv_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_mISDNuser_suppserv_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_mISDNuser_suppserv_h" >&5
+echo "${ECHO_T}$ac_cv_header_mISDNuser_suppserv_h" >&6; }
+
+fi
+if test $ac_cv_header_mISDNuser_suppserv_h = yes; then
+ SUPPSERV_HEADER_FOUND=1
+else
+ SUPPSERV_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SUPPSERV_HEADER_FOUND}" = "x0" ; then
+ SUPPSERV_LIB=""
+ SUPPSERV_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SUPPSERV_LIB=""
+ fi
+ PBX_SUPPSERV=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SUPPSERV 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SUPPSERV_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+ if test "x${PBX_MISDN_FAC_RESULT}" != "x1" -a "${USE_MISDN_FAC_RESULT}" != "no"; then
+ { echo "$as_me:$LINENO: checking for Fac_RESULT in mISDNuser/suppserv.h" >&5
+echo $ECHO_N "checking for Fac_RESULT in mISDNuser/suppserv.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${MISDN_FAC_RESULT_DIR}" != "x"; then
+ MISDN_FAC_RESULT_INCLUDE="-I${MISDN_FAC_RESULT_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${MISDN_FAC_RESULT_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <mISDNuser/suppserv.h>
+int
+main ()
+{
+#if defined(Fac_RESULT)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_MISDN_FAC_RESULT=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MISDN_FAC_RESULT 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MISDN_FAC_RESULT_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+ if test "x${PBX_MISDN_FAC_ERROR}" != "x1" -a "${USE_MISDN_FAC_ERROR}" != "no"; then
+ { echo "$as_me:$LINENO: checking for Fac_ERROR in mISDNuser/suppserv.h" >&5
+echo $ECHO_N "checking for Fac_ERROR in mISDNuser/suppserv.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${MISDN_FAC_ERROR_DIR}" != "x"; then
+ MISDN_FAC_ERROR_INCLUDE="-I${MISDN_FAC_ERROR_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${MISDN_FAC_ERROR_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <mISDNuser/suppserv.h>
+int
+main ()
+{
+#if defined(Fac_ERROR)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_MISDN_FAC_ERROR=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MISDN_FAC_ERROR 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MISDN_FAC_ERROR_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+ if test "${ac_cv_header_linux_mISDNdsp_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for linux/mISDNdsp.h" >&5
+echo $ECHO_N "checking for linux/mISDNdsp.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_mISDNdsp_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_mISDNdsp_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_mISDNdsp_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking linux/mISDNdsp.h usability" >&5
+echo $ECHO_N "checking linux/mISDNdsp.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <linux/mISDNdsp.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking linux/mISDNdsp.h presence" >&5
+echo $ECHO_N "checking linux/mISDNdsp.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <linux/mISDNdsp.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/mISDNdsp.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: linux/mISDNdsp.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for linux/mISDNdsp.h" >&5
+echo $ECHO_N "checking for linux/mISDNdsp.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_mISDNdsp_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_linux_mISDNdsp_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_mISDNdsp_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_mISDNdsp_h" >&6; }
+
+fi
+if test $ac_cv_header_linux_mISDNdsp_h = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define MISDN_1_2 1
+_ACEOF
+
+fi
+
+
+fi
+
+
+if test "x${PBX_NBS}" != "x1" -a "${USE_NBS}" != "no"; then
+ pbxlibdir=""
+ # if --with-NBS=DIR has been specified, use it.
+ if test "x${NBS_DIR}" != "x"; then
+ if test -d ${NBS_DIR}/lib; then
+ pbxlibdir="-L${NBS_DIR}/lib"
+ else
+ pbxlibdir="-L${NBS_DIR}"
+ fi
+ fi
+ pbxfuncname="nbs_connect"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_NBS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_nbs_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lnbs" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lnbs... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnbs ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_NBS_FOUND=yes
+else
+ AST_NBS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_NBS_FOUND}" = "yes"; then
+ NBS_LIB="${pbxlibdir} -lnbs "
+ # if --with-NBS=DIR has been specified, use it.
+ if test "x${NBS_DIR}" != "x"; then
+ NBS_INCLUDE="-I${NBS_DIR}/include"
+ fi
+ NBS_INCLUDE="${NBS_INCLUDE} "
+ if test "xnbs.h" = "x" ; then # no header, assume found
+ NBS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${NBS_INCLUDE} "
+ if test "${ac_cv_header_nbs_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for nbs.h" >&5
+echo $ECHO_N "checking for nbs.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_nbs_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_nbs_h" >&5
+echo "${ECHO_T}$ac_cv_header_nbs_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking nbs.h usability" >&5
+echo $ECHO_N "checking nbs.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <nbs.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking nbs.h presence" >&5
+echo $ECHO_N "checking nbs.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <nbs.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: nbs.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: nbs.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: nbs.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: nbs.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: nbs.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: nbs.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: nbs.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: nbs.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: nbs.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: nbs.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: nbs.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: nbs.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: nbs.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: nbs.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: nbs.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: nbs.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for nbs.h" >&5
+echo $ECHO_N "checking for nbs.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_nbs_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_nbs_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_nbs_h" >&5
+echo "${ECHO_T}$ac_cv_header_nbs_h" >&6; }
+
+fi
+if test $ac_cv_header_nbs_h = yes; then
+ NBS_HEADER_FOUND=1
+else
+ NBS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${NBS_HEADER_FOUND}" = "x0" ; then
+ NBS_LIB=""
+ NBS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ NBS_LIB=""
+ fi
+ PBX_NBS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_NBS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_NBS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_NCURSES}" != "x1" -a "${USE_NCURSES}" != "no"; then
+ pbxlibdir=""
+ # if --with-NCURSES=DIR has been specified, use it.
+ if test "x${NCURSES_DIR}" != "x"; then
+ if test -d ${NCURSES_DIR}/lib; then
+ pbxlibdir="-L${NCURSES_DIR}/lib"
+ else
+ pbxlibdir="-L${NCURSES_DIR}"
+ fi
+ fi
+ pbxfuncname="initscr"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_NCURSES_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ncurses_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lncurses" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lncurses... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lncurses ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_NCURSES_FOUND=yes
+else
+ AST_NCURSES_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_NCURSES_FOUND}" = "yes"; then
+ NCURSES_LIB="${pbxlibdir} -lncurses "
+ # if --with-NCURSES=DIR has been specified, use it.
+ if test "x${NCURSES_DIR}" != "x"; then
+ NCURSES_INCLUDE="-I${NCURSES_DIR}/include"
+ fi
+ NCURSES_INCLUDE="${NCURSES_INCLUDE} "
+ if test "xcurses.h" = "x" ; then # no header, assume found
+ NCURSES_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${NCURSES_INCLUDE} "
+ if test "${ac_cv_header_curses_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for curses.h" >&5
+echo $ECHO_N "checking for curses.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_curses_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_curses_h" >&5
+echo "${ECHO_T}$ac_cv_header_curses_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking curses.h usability" >&5
+echo $ECHO_N "checking curses.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <curses.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking curses.h presence" >&5
+echo $ECHO_N "checking curses.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <curses.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: curses.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: curses.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: curses.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: curses.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: curses.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: curses.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: curses.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: curses.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: curses.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: curses.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: curses.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for curses.h" >&5
+echo $ECHO_N "checking for curses.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_curses_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_curses_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_curses_h" >&5
+echo "${ECHO_T}$ac_cv_header_curses_h" >&6; }
+
+fi
+if test $ac_cv_header_curses_h = yes; then
+ NCURSES_HEADER_FOUND=1
+else
+ NCURSES_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${NCURSES_HEADER_FOUND}" = "x0" ; then
+ NCURSES_LIB=""
+ NCURSES_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ NCURSES_LIB=""
+ fi
+ PBX_NCURSES=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_NCURSES 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_NCURSES_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+ if test "x${PBX_NETSNMP}" != "x1" -a "${USE_NETSNMP}" != "no"; then
+ PBX_NETSNMP=0
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}net-snmp-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}net-snmp-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CONFIG_NETSNMP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CONFIG_NETSNMP"; then
+ ac_cv_prog_CONFIG_NETSNMP="$CONFIG_NETSNMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CONFIG_NETSNMP="${ac_tool_prefix}net-snmp-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CONFIG_NETSNMP=$ac_cv_prog_CONFIG_NETSNMP
+if test -n "$CONFIG_NETSNMP"; then
+ { echo "$as_me:$LINENO: result: $CONFIG_NETSNMP" >&5
+echo "${ECHO_T}$CONFIG_NETSNMP" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CONFIG_NETSNMP"; then
+ ac_ct_CONFIG_NETSNMP=$CONFIG_NETSNMP
+ # Extract the first word of "net-snmp-config", so it can be a program name with args.
+set dummy net-snmp-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CONFIG_NETSNMP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CONFIG_NETSNMP"; then
+ ac_cv_prog_ac_ct_CONFIG_NETSNMP="$ac_ct_CONFIG_NETSNMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CONFIG_NETSNMP="net-snmp-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CONFIG_NETSNMP=$ac_cv_prog_ac_ct_CONFIG_NETSNMP
+if test -n "$ac_ct_CONFIG_NETSNMP"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CONFIG_NETSNMP" >&5
+echo "${ECHO_T}$ac_ct_CONFIG_NETSNMP" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CONFIG_NETSNMP" = x; then
+ CONFIG_NETSNMP="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CONFIG_NETSNMP=$ac_ct_CONFIG_NETSNMP
+ fi
+else
+ CONFIG_NETSNMP="$ac_cv_prog_CONFIG_NETSNMP"
+fi
+
+ if test ! "x${CONFIG_NETSNMP}" = xNo; then
+ if test x"" = x ; then A=--cflags ; else A="" ; fi
+ NETSNMP_INCLUDE=$(${CONFIG_NETSNMP} $A)
+ if test x"--agent-libs" = x ; then A=--libs ; else A="--agent-libs" ; fi
+ NETSNMP_LIB=$(${CONFIG_NETSNMP} $A)
+ if test x"#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>" != x ; then
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${NETSNMP_DIR}" != "x"; then
+ NETSNMP_INCLUDE="-I${NETSNMP_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${NETSNMP_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+ #include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+int
+main ()
+{
+ int callback = snmp_register_callback(0, 0, NULL, NULL);
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ PBX_NETSNMP=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_NETSNMP 1
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ else
+ PBX_NETSNMP=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_NETSNMP 1
+_ACEOF
+
+ fi
+ fi
+ fi
+
+
+
+if test "x${PBX_NEWT}" != "x1" -a "${USE_NEWT}" != "no"; then
+ pbxlibdir=""
+ # if --with-NEWT=DIR has been specified, use it.
+ if test "x${NEWT_DIR}" != "x"; then
+ if test -d ${NEWT_DIR}/lib; then
+ pbxlibdir="-L${NEWT_DIR}/lib"
+ else
+ pbxlibdir="-L${NEWT_DIR}"
+ fi
+ fi
+ pbxfuncname="newtBell"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_NEWT_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_newt_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lnewt" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lnewt... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnewt ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_NEWT_FOUND=yes
+else
+ AST_NEWT_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_NEWT_FOUND}" = "yes"; then
+ NEWT_LIB="${pbxlibdir} -lnewt "
+ # if --with-NEWT=DIR has been specified, use it.
+ if test "x${NEWT_DIR}" != "x"; then
+ NEWT_INCLUDE="-I${NEWT_DIR}/include"
+ fi
+ NEWT_INCLUDE="${NEWT_INCLUDE} "
+ if test "xnewt.h" = "x" ; then # no header, assume found
+ NEWT_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${NEWT_INCLUDE} "
+ if test "${ac_cv_header_newt_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for newt.h" >&5
+echo $ECHO_N "checking for newt.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_newt_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_newt_h" >&5
+echo "${ECHO_T}$ac_cv_header_newt_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking newt.h usability" >&5
+echo $ECHO_N "checking newt.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <newt.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking newt.h presence" >&5
+echo $ECHO_N "checking newt.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <newt.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: newt.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: newt.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: newt.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: newt.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: newt.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: newt.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: newt.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: newt.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: newt.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: newt.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: newt.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: newt.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: newt.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: newt.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: newt.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: newt.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for newt.h" >&5
+echo $ECHO_N "checking for newt.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_newt_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_newt_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_newt_h" >&5
+echo "${ECHO_T}$ac_cv_header_newt_h" >&6; }
+
+fi
+if test $ac_cv_header_newt_h = yes; then
+ NEWT_HEADER_FOUND=1
+else
+ NEWT_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${NEWT_HEADER_FOUND}" = "x0" ; then
+ NEWT_LIB=""
+ NEWT_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ NEWT_LIB=""
+ fi
+ PBX_NEWT=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_NEWT 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_NEWT_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_UNIXODBC}" != "x1" -a "${USE_UNIXODBC}" != "no"; then
+ pbxlibdir=""
+ # if --with-UNIXODBC=DIR has been specified, use it.
+ if test "x${UNIXODBC_DIR}" != "x"; then
+ if test -d ${UNIXODBC_DIR}/lib; then
+ pbxlibdir="-L${UNIXODBC_DIR}/lib"
+ else
+ pbxlibdir="-L${UNIXODBC_DIR}"
+ fi
+ fi
+ pbxfuncname="SQLConnect"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_UNIXODBC_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_odbc_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lodbc" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lodbc... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lodbc ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_UNIXODBC_FOUND=yes
+else
+ AST_UNIXODBC_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_UNIXODBC_FOUND}" = "yes"; then
+ UNIXODBC_LIB="${pbxlibdir} -lodbc "
+ # if --with-UNIXODBC=DIR has been specified, use it.
+ if test "x${UNIXODBC_DIR}" != "x"; then
+ UNIXODBC_INCLUDE="-I${UNIXODBC_DIR}/include"
+ fi
+ UNIXODBC_INCLUDE="${UNIXODBC_INCLUDE} "
+ if test "xsql.h" = "x" ; then # no header, assume found
+ UNIXODBC_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${UNIXODBC_INCLUDE} "
+ if test "${ac_cv_header_sql_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for sql.h" >&5
+echo $ECHO_N "checking for sql.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sql_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sql_h" >&5
+echo "${ECHO_T}$ac_cv_header_sql_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking sql.h usability" >&5
+echo $ECHO_N "checking sql.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sql.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking sql.h presence" >&5
+echo $ECHO_N "checking sql.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sql.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sql.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sql.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sql.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sql.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sql.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sql.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sql.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sql.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sql.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sql.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sql.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sql.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sql.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sql.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sql.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sql.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for sql.h" >&5
+echo $ECHO_N "checking for sql.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sql_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sql_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sql_h" >&5
+echo "${ECHO_T}$ac_cv_header_sql_h" >&6; }
+
+fi
+if test $ac_cv_header_sql_h = yes; then
+ UNIXODBC_HEADER_FOUND=1
+else
+ UNIXODBC_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${UNIXODBC_HEADER_FOUND}" = "x0" ; then
+ UNIXODBC_LIB=""
+ UNIXODBC_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ UNIXODBC_LIB=""
+ fi
+ PBX_UNIXODBC=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UNIXODBC 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UNIXODBC_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_OGG}" != "x1" -a "${USE_OGG}" != "no"; then
+ pbxlibdir=""
+ # if --with-OGG=DIR has been specified, use it.
+ if test "x${OGG_DIR}" != "x"; then
+ if test -d ${OGG_DIR}/lib; then
+ pbxlibdir="-L${OGG_DIR}/lib"
+ else
+ pbxlibdir="-L${OGG_DIR}"
+ fi
+ fi
+ pbxfuncname="ogg_sync_init"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_OGG_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ogg_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -logg" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -logg... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-logg ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_OGG_FOUND=yes
+else
+ AST_OGG_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_OGG_FOUND}" = "yes"; then
+ OGG_LIB="${pbxlibdir} -logg "
+ # if --with-OGG=DIR has been specified, use it.
+ if test "x${OGG_DIR}" != "x"; then
+ OGG_INCLUDE="-I${OGG_DIR}/include"
+ fi
+ OGG_INCLUDE="${OGG_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ OGG_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${OGG_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ OGG_HEADER_FOUND=1
+else
+ OGG_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${OGG_HEADER_FOUND}" = "x0" ; then
+ OGG_LIB=""
+ OGG_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ OGG_LIB=""
+ fi
+ PBX_OGG=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OGG 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OGG_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_BKTR}" != "x1" -a "${USE_BKTR}" != "no"; then
+ pbxlibdir=""
+ # if --with-BKTR=DIR has been specified, use it.
+ if test "x${BKTR_DIR}" != "x"; then
+ if test -d ${BKTR_DIR}/lib; then
+ pbxlibdir="-L${BKTR_DIR}/lib"
+ else
+ pbxlibdir="-L${BKTR_DIR}"
+ fi
+ fi
+ pbxfuncname="backtrace"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_BKTR_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_execinfo_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lexecinfo" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lexecinfo... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lexecinfo ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_BKTR_FOUND=yes
+else
+ AST_BKTR_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_BKTR_FOUND}" = "yes"; then
+ BKTR_LIB="${pbxlibdir} -lexecinfo "
+ # if --with-BKTR=DIR has been specified, use it.
+ if test "x${BKTR_DIR}" != "x"; then
+ BKTR_INCLUDE="-I${BKTR_DIR}/include"
+ fi
+ BKTR_INCLUDE="${BKTR_INCLUDE} "
+ if test "xexecinfo.h" = "x" ; then # no header, assume found
+ BKTR_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${BKTR_INCLUDE} "
+ if test "${ac_cv_header_execinfo_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for execinfo.h" >&5
+echo $ECHO_N "checking for execinfo.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_execinfo_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_execinfo_h" >&5
+echo "${ECHO_T}$ac_cv_header_execinfo_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking execinfo.h usability" >&5
+echo $ECHO_N "checking execinfo.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <execinfo.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking execinfo.h presence" >&5
+echo $ECHO_N "checking execinfo.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <execinfo.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: execinfo.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: execinfo.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: execinfo.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: execinfo.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: execinfo.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: execinfo.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: execinfo.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: execinfo.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: execinfo.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for execinfo.h" >&5
+echo $ECHO_N "checking for execinfo.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_execinfo_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_execinfo_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_execinfo_h" >&5
+echo "${ECHO_T}$ac_cv_header_execinfo_h" >&6; }
+
+fi
+if test $ac_cv_header_execinfo_h = yes; then
+ BKTR_HEADER_FOUND=1
+else
+ BKTR_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${BKTR_HEADER_FOUND}" = "x0" ; then
+ BKTR_LIB=""
+ BKTR_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ BKTR_LIB=""
+ fi
+ PBX_BKTR=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_BKTR 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_BKTR_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+# possible places for oss definitions
+
+if test "x${PBX_OSS}" != "x1" -a "${USE_OSS}" != "no"; then
+ pbxlibdir=""
+ # if --with-OSS=DIR has been specified, use it.
+ if test "x${OSS_DIR}" != "x"; then
+ if test -d ${OSS_DIR}/lib; then
+ pbxlibdir="-L${OSS_DIR}/lib"
+ else
+ pbxlibdir="-L${OSS_DIR}"
+ fi
+ fi
+ pbxfuncname=""
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_OSS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ossaudio_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lossaudio" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lossaudio... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lossaudio ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_OSS_FOUND=yes
+else
+ AST_OSS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_OSS_FOUND}" = "yes"; then
+ OSS_LIB="${pbxlibdir} -lossaudio "
+ # if --with-OSS=DIR has been specified, use it.
+ if test "x${OSS_DIR}" != "x"; then
+ OSS_INCLUDE="-I${OSS_DIR}/include"
+ fi
+ OSS_INCLUDE="${OSS_INCLUDE} "
+ if test "xlinux/soundcard.h" = "x" ; then # no header, assume found
+ OSS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${OSS_INCLUDE} "
+ if test "${ac_cv_header_linux_soundcard_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for linux/soundcard.h" >&5
+echo $ECHO_N "checking for linux/soundcard.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_soundcard_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_soundcard_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_soundcard_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking linux/soundcard.h usability" >&5
+echo $ECHO_N "checking linux/soundcard.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <linux/soundcard.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking linux/soundcard.h presence" >&5
+echo $ECHO_N "checking linux/soundcard.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <linux/soundcard.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: linux/soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: linux/soundcard.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: linux/soundcard.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: linux/soundcard.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: linux/soundcard.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: linux/soundcard.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: linux/soundcard.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: linux/soundcard.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for linux/soundcard.h" >&5
+echo $ECHO_N "checking for linux/soundcard.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_soundcard_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_linux_soundcard_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_soundcard_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_soundcard_h" >&6; }
+
+fi
+if test $ac_cv_header_linux_soundcard_h = yes; then
+ OSS_HEADER_FOUND=1
+else
+ OSS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${OSS_HEADER_FOUND}" = "x0" ; then
+ OSS_LIB=""
+ OSS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ OSS_LIB=""
+ fi
+ PBX_OSS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_OSS}" != "x1" -a "${USE_OSS}" != "no"; then
+ pbxlibdir=""
+ # if --with-OSS=DIR has been specified, use it.
+ if test "x${OSS_DIR}" != "x"; then
+ if test -d ${OSS_DIR}/lib; then
+ pbxlibdir="-L${OSS_DIR}/lib"
+ else
+ pbxlibdir="-L${OSS_DIR}"
+ fi
+ fi
+ pbxfuncname=""
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_OSS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ossaudio_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lossaudio" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lossaudio... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lossaudio ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_OSS_FOUND=yes
+else
+ AST_OSS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_OSS_FOUND}" = "yes"; then
+ OSS_LIB="${pbxlibdir} -lossaudio "
+ # if --with-OSS=DIR has been specified, use it.
+ if test "x${OSS_DIR}" != "x"; then
+ OSS_INCLUDE="-I${OSS_DIR}/include"
+ fi
+ OSS_INCLUDE="${OSS_INCLUDE} "
+ if test "xsys/soundcard.h" = "x" ; then # no header, assume found
+ OSS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${OSS_INCLUDE} "
+ if test "${ac_cv_header_sys_soundcard_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for sys/soundcard.h" >&5
+echo $ECHO_N "checking for sys/soundcard.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_soundcard_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_soundcard_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_soundcard_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking sys/soundcard.h usability" >&5
+echo $ECHO_N "checking sys/soundcard.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sys/soundcard.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking sys/soundcard.h presence" >&5
+echo $ECHO_N "checking sys/soundcard.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/soundcard.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sys/soundcard.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/soundcard.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/soundcard.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sys/soundcard.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sys/soundcard.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/soundcard.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/soundcard.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sys/soundcard.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for sys/soundcard.h" >&5
+echo $ECHO_N "checking for sys/soundcard.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_soundcard_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sys_soundcard_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_soundcard_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_soundcard_h" >&6; }
+
+fi
+if test $ac_cv_header_sys_soundcard_h = yes; then
+ OSS_HEADER_FOUND=1
+else
+ OSS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${OSS_HEADER_FOUND}" = "x0" ; then
+ OSS_LIB=""
+ OSS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ OSS_LIB=""
+ fi
+ PBX_OSS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_OSS}" != "x1" -a "${USE_OSS}" != "no"; then
+ pbxlibdir=""
+ # if --with-OSS=DIR has been specified, use it.
+ if test "x${OSS_DIR}" != "x"; then
+ if test -d ${OSS_DIR}/lib; then
+ pbxlibdir="-L${OSS_DIR}/lib"
+ else
+ pbxlibdir="-L${OSS_DIR}"
+ fi
+ fi
+ pbxfuncname="oss_ioctl_mixer"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_OSS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ossaudio_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lossaudio" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lossaudio... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lossaudio ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_OSS_FOUND=yes
+else
+ AST_OSS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_OSS_FOUND}" = "yes"; then
+ OSS_LIB="${pbxlibdir} -lossaudio "
+ # if --with-OSS=DIR has been specified, use it.
+ if test "x${OSS_DIR}" != "x"; then
+ OSS_INCLUDE="-I${OSS_DIR}/include"
+ fi
+ OSS_INCLUDE="${OSS_INCLUDE} "
+ if test "xsoundcard.h" = "x" ; then # no header, assume found
+ OSS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${OSS_INCLUDE} "
+ if test "${ac_cv_header_soundcard_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for soundcard.h" >&5
+echo $ECHO_N "checking for soundcard.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_soundcard_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_soundcard_h" >&5
+echo "${ECHO_T}$ac_cv_header_soundcard_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking soundcard.h usability" >&5
+echo $ECHO_N "checking soundcard.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <soundcard.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking soundcard.h presence" >&5
+echo $ECHO_N "checking soundcard.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <soundcard.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: soundcard.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: soundcard.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: soundcard.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: soundcard.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: soundcard.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: soundcard.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: soundcard.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: soundcard.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for soundcard.h" >&5
+echo $ECHO_N "checking for soundcard.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_soundcard_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_soundcard_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_soundcard_h" >&5
+echo "${ECHO_T}$ac_cv_header_soundcard_h" >&6; }
+
+fi
+if test $ac_cv_header_soundcard_h = yes; then
+ OSS_HEADER_FOUND=1
+else
+ OSS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${OSS_HEADER_FOUND}" = "x0" ; then
+ OSS_LIB=""
+ OSS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ OSS_LIB=""
+ fi
+ PBX_OSS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+PG_CONFIG=No
+if test "${USE_PGSQL}" != "no"; then
+ if test "x${PGSQL_DIR}" != "x"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pg_config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pg_config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_PG_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $PG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PG_CONFIG="$PG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in ${PGSQL_DIR}/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PG_CONFIG=$ac_cv_path_PG_CONFIG
+if test -n "$PG_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $PG_CONFIG" >&5
+echo "${ECHO_T}$PG_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PG_CONFIG"; then
+ ac_pt_PG_CONFIG=$PG_CONFIG
+ # Extract the first word of "pg_config", so it can be a program name with args.
+set dummy pg_config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ac_pt_PG_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ac_pt_PG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PG_CONFIG="$ac_pt_PG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in ${PGSQL_DIR}/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_PG_CONFIG=$ac_cv_path_ac_pt_PG_CONFIG
+if test -n "$ac_pt_PG_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $ac_pt_PG_CONFIG" >&5
+echo "${ECHO_T}$ac_pt_PG_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_pt_PG_CONFIG" = x; then
+ PG_CONFIG="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ PG_CONFIG=$ac_pt_PG_CONFIG
+ fi
+else
+ PG_CONFIG="$ac_cv_path_PG_CONFIG"
+fi
+
+ if test x"${PG_CONFIG}" = xNo; then
+ { echo "$as_me:$LINENO: ***" >&5
+echo "$as_me: ***" >&6;}
+ { echo "$as_me:$LINENO: *** pg_config was not found in the path you specified:" >&5
+echo "$as_me: *** pg_config was not found in the path you specified:" >&6;}
+ { echo "$as_me:$LINENO: *** ${PGSQL_DIR}/bin" >&5
+echo "$as_me: *** ${PGSQL_DIR}/bin" >&6;}
+ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5
+echo "$as_me: *** Either correct the installation, or run configure" >&6;}
+ { echo "$as_me:$LINENO: *** including --without-postgres" >&5
+echo "$as_me: *** including --without-postgres" >&6;}
+ exit 1
+ fi
+ else
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pg_config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pg_config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_PG_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $PG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PG_CONFIG="$PG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PG_CONFIG=$ac_cv_path_PG_CONFIG
+if test -n "$PG_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $PG_CONFIG" >&5
+echo "${ECHO_T}$PG_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PG_CONFIG"; then
+ ac_pt_PG_CONFIG=$PG_CONFIG
+ # Extract the first word of "pg_config", so it can be a program name with args.
+set dummy pg_config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ac_pt_PG_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ac_pt_PG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PG_CONFIG="$ac_pt_PG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_PG_CONFIG=$ac_cv_path_ac_pt_PG_CONFIG
+if test -n "$ac_pt_PG_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $ac_pt_PG_CONFIG" >&5
+echo "${ECHO_T}$ac_pt_PG_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_pt_PG_CONFIG" = x; then
+ PG_CONFIG="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ PG_CONFIG=$ac_pt_PG_CONFIG
+ fi
+else
+ PG_CONFIG="$ac_cv_path_PG_CONFIG"
+fi
+
+ fi
+fi
+if test "${PG_CONFIG}" != No; then
+ PGSQL_libdir=`${PG_CONFIG} --libdir`
+ PGSQL_includedir=`${PG_CONFIG} --includedir`
+ if test "x$?" != "x0" ; then
+ if test -n "${PGSQL_MANDATORY}" ; then
+ { echo "$as_me:$LINENO: ***" >&5
+echo "$as_me: ***" >&6;}
+ { echo "$as_me:$LINENO: *** The PostgreSQL installation on this system appears to be broken." >&5
+echo "$as_me: *** The PostgreSQL installation on this system appears to be broken." >&6;}
+ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5
+echo "$as_me: *** Either correct the installation, or run configure" >&6;}
+ { echo "$as_me:$LINENO: *** including --without-postgres" >&5
+echo "$as_me: *** including --without-postgres" >&6;}
+ exit 1
+ fi
+ else
+ { echo "$as_me:$LINENO: checking for PQescapeStringConn in -lpq" >&5
+echo $ECHO_N "checking for PQescapeStringConn in -lpq... $ECHO_C" >&6; }
+if test "${ac_cv_lib_pq_PQescapeStringConn+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpq -L${PGSQL_libdir} -lz $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char PQescapeStringConn ();
+int
+main ()
+{
+return PQescapeStringConn ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_pq_PQescapeStringConn=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_pq_PQescapeStringConn=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_pq_PQescapeStringConn" >&5
+echo "${ECHO_T}$ac_cv_lib_pq_PQescapeStringConn" >&6; }
+if test $ac_cv_lib_pq_PQescapeStringConn = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PGSQL 1
+_ACEOF
+
+fi
+
+
+ if test "${ac_cv_lib_pq_PQescapeStringConn}" = "yes"; then
+ PGSQL_LIB="-L${PGSQL_libdir} -lpq -lz"
+ PGSQL_INCLUDE="-I${PGSQL_includedir}"
+ PBX_PGSQL=1
+ elif test -n "${PGSQL_MANDATORY}";
+ then
+ { echo "$as_me:$LINENO: ***" >&5
+echo "$as_me: ***" >&6;}
+ { echo "$as_me:$LINENO: *** The PostgreSQL installation on this system appears to be broken." >&5
+echo "$as_me: *** The PostgreSQL installation on this system appears to be broken." >&6;}
+ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5
+echo "$as_me: *** Either correct the installation, or run configure" >&6;}
+ { echo "$as_me:$LINENO: *** including --without-postgres" >&5
+echo "$as_me: *** including --without-postgres" >&6;}
+ exit 1
+ fi
+ fi
+fi
+
+
+if test "x${PBX_POPT}" != "x1" -a "${USE_POPT}" != "no"; then
+ pbxlibdir=""
+ # if --with-POPT=DIR has been specified, use it.
+ if test "x${POPT_DIR}" != "x"; then
+ if test -d ${POPT_DIR}/lib; then
+ pbxlibdir="-L${POPT_DIR}/lib"
+ else
+ pbxlibdir="-L${POPT_DIR}"
+ fi
+ fi
+ pbxfuncname="poptStrerror"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_POPT_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_popt_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lpopt" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lpopt... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpopt ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_POPT_FOUND=yes
+else
+ AST_POPT_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_POPT_FOUND}" = "yes"; then
+ POPT_LIB="${pbxlibdir} -lpopt "
+ # if --with-POPT=DIR has been specified, use it.
+ if test "x${POPT_DIR}" != "x"; then
+ POPT_INCLUDE="-I${POPT_DIR}/include"
+ fi
+ POPT_INCLUDE="${POPT_INCLUDE} "
+ if test "xpopt.h" = "x" ; then # no header, assume found
+ POPT_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${POPT_INCLUDE} "
+ if test "${ac_cv_header_popt_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for popt.h" >&5
+echo $ECHO_N "checking for popt.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_popt_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_popt_h" >&5
+echo "${ECHO_T}$ac_cv_header_popt_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking popt.h usability" >&5
+echo $ECHO_N "checking popt.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <popt.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking popt.h presence" >&5
+echo $ECHO_N "checking popt.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <popt.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: popt.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: popt.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: popt.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: popt.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: popt.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: popt.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: popt.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: popt.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: popt.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: popt.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: popt.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: popt.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: popt.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: popt.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: popt.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: popt.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for popt.h" >&5
+echo $ECHO_N "checking for popt.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_popt_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_popt_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_popt_h" >&5
+echo "${ECHO_T}$ac_cv_header_popt_h" >&6; }
+
+fi
+if test $ac_cv_header_popt_h = yes; then
+ POPT_HEADER_FOUND=1
+else
+ POPT_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${POPT_HEADER_FOUND}" = "x0" ; then
+ POPT_LIB=""
+ POPT_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ POPT_LIB=""
+ fi
+ PBX_POPT=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_POPT 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_POPT_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_PORTAUDIO}" != "x1" -a "${USE_PORTAUDIO}" != "no"; then
+ pbxlibdir=""
+ # if --with-PORTAUDIO=DIR has been specified, use it.
+ if test "x${PORTAUDIO_DIR}" != "x"; then
+ if test -d ${PORTAUDIO_DIR}/lib; then
+ pbxlibdir="-L${PORTAUDIO_DIR}/lib"
+ else
+ pbxlibdir="-L${PORTAUDIO_DIR}"
+ fi
+ fi
+ pbxfuncname="Pa_GetDeviceCount"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_PORTAUDIO_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_portaudio_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lportaudio" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lportaudio... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lportaudio ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_PORTAUDIO_FOUND=yes
+else
+ AST_PORTAUDIO_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_PORTAUDIO_FOUND}" = "yes"; then
+ PORTAUDIO_LIB="${pbxlibdir} -lportaudio "
+ # if --with-PORTAUDIO=DIR has been specified, use it.
+ if test "x${PORTAUDIO_DIR}" != "x"; then
+ PORTAUDIO_INCLUDE="-I${PORTAUDIO_DIR}/include"
+ fi
+ PORTAUDIO_INCLUDE="${PORTAUDIO_INCLUDE} "
+ if test "xportaudio.h" = "x" ; then # no header, assume found
+ PORTAUDIO_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${PORTAUDIO_INCLUDE} "
+ if test "${ac_cv_header_portaudio_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for portaudio.h" >&5
+echo $ECHO_N "checking for portaudio.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_portaudio_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_portaudio_h" >&5
+echo "${ECHO_T}$ac_cv_header_portaudio_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking portaudio.h usability" >&5
+echo $ECHO_N "checking portaudio.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <portaudio.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking portaudio.h presence" >&5
+echo $ECHO_N "checking portaudio.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <portaudio.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: portaudio.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: portaudio.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: portaudio.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: portaudio.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: portaudio.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: portaudio.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: portaudio.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: portaudio.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: portaudio.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for portaudio.h" >&5
+echo $ECHO_N "checking for portaudio.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_portaudio_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_portaudio_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_portaudio_h" >&5
+echo "${ECHO_T}$ac_cv_header_portaudio_h" >&6; }
+
+fi
+if test $ac_cv_header_portaudio_h = yes; then
+ PORTAUDIO_HEADER_FOUND=1
+else
+ PORTAUDIO_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${PORTAUDIO_HEADER_FOUND}" = "x0" ; then
+ PORTAUDIO_LIB=""
+ PORTAUDIO_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ PORTAUDIO_LIB=""
+ fi
+ PBX_PORTAUDIO=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PORTAUDIO 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PORTAUDIO_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_PRI}" != "x1" -a "${USE_PRI}" != "no"; then
+ pbxlibdir=""
+ # if --with-PRI=DIR has been specified, use it.
+ if test "x${PRI_DIR}" != "x"; then
+ if test -d ${PRI_DIR}/lib; then
+ pbxlibdir="-L${PRI_DIR}/lib"
+ else
+ pbxlibdir="-L${PRI_DIR}"
+ fi
+ fi
+ pbxfuncname="pri_new_bri"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_PRI_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lpri" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lpri... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpri ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_PRI_FOUND=yes
+else
+ AST_PRI_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_PRI_FOUND}" = "yes"; then
+ PRI_LIB="${pbxlibdir} -lpri "
+ # if --with-PRI=DIR has been specified, use it.
+ if test "x${PRI_DIR}" != "x"; then
+ PRI_INCLUDE="-I${PRI_DIR}/include"
+ fi
+ PRI_INCLUDE="${PRI_INCLUDE} "
+ if test "xlibpri.h" = "x" ; then # no header, assume found
+ PRI_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${PRI_INCLUDE} "
+ if test "${ac_cv_header_libpri_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for libpri.h" >&5
+echo $ECHO_N "checking for libpri.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+echo "${ECHO_T}$ac_cv_header_libpri_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking libpri.h usability" >&5
+echo $ECHO_N "checking libpri.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <libpri.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking libpri.h presence" >&5
+echo $ECHO_N "checking libpri.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <libpri.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: libpri.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: libpri.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: libpri.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libpri.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: libpri.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libpri.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: libpri.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libpri.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: libpri.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: libpri.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libpri.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: libpri.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for libpri.h" >&5
+echo $ECHO_N "checking for libpri.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_libpri_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+echo "${ECHO_T}$ac_cv_header_libpri_h" >&6; }
+
+fi
+if test $ac_cv_header_libpri_h = yes; then
+ PRI_HEADER_FOUND=1
+else
+ PRI_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${PRI_HEADER_FOUND}" = "x0" ; then
+ PRI_LIB=""
+ PRI_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ PRI_LIB=""
+ fi
+ PBX_PRI=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PRI 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PRI_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then
+ pbxlibdir=""
+ # if --with-SS7=DIR has been specified, use it.
+ if test "x${SS7_DIR}" != "x"; then
+ if test -d ${SS7_DIR}/lib; then
+ pbxlibdir="-L${SS7_DIR}/lib"
+ else
+ pbxlibdir="-L${SS7_DIR}"
+ fi
+ fi
+ pbxfuncname="isup_cqr"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SS7_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ss7_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lss7" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lss7... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lss7 ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SS7_FOUND=yes
+else
+ AST_SS7_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SS7_FOUND}" = "yes"; then
+ SS7_LIB="${pbxlibdir} -lss7 "
+ # if --with-SS7=DIR has been specified, use it.
+ if test "x${SS7_DIR}" != "x"; then
+ SS7_INCLUDE="-I${SS7_DIR}/include"
+ fi
+ SS7_INCLUDE="${SS7_INCLUDE} "
+ if test "xlibss7.h" = "x" ; then # no header, assume found
+ SS7_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SS7_INCLUDE} "
+ if test "${ac_cv_header_libss7_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for libss7.h" >&5
+echo $ECHO_N "checking for libss7.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libss7_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libss7_h" >&5
+echo "${ECHO_T}$ac_cv_header_libss7_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking libss7.h usability" >&5
+echo $ECHO_N "checking libss7.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <libss7.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking libss7.h presence" >&5
+echo $ECHO_N "checking libss7.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <libss7.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: libss7.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: libss7.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libss7.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: libss7.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: libss7.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: libss7.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libss7.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: libss7.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libss7.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: libss7.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libss7.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: libss7.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libss7.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: libss7.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: libss7.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: libss7.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for libss7.h" >&5
+echo $ECHO_N "checking for libss7.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libss7_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_libss7_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libss7_h" >&5
+echo "${ECHO_T}$ac_cv_header_libss7_h" >&6; }
+
+fi
+if test $ac_cv_header_libss7_h = yes; then
+ SS7_HEADER_FOUND=1
+else
+ SS7_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SS7_HEADER_FOUND}" = "x0" ; then
+ SS7_LIB=""
+ SS7_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SS7_LIB=""
+ fi
+ PBX_SS7=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SS7 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SS7_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "${USE_PWLIB}" != "no"; then
+ if test -n "${PWLIB_DIR}"; then
+ PWLIBDIR="${PWLIB_DIR}"
+ fi
+
+
+
+PWLIB_INCDIR=
+PWLIB_LIBDIR=
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+if test "${PWLIBDIR:-unset}" != "unset" ; then
+ as_ac_Header=`echo "ac_cv_header_${PWLIBDIR}/version.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${PWLIBDIR}/version.h" >&5
+echo $ECHO_N "checking for ${PWLIBDIR}/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${PWLIBDIR}/version.h usability" >&5
+echo $ECHO_N "checking ${PWLIBDIR}/version.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${PWLIBDIR}/version.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${PWLIBDIR}/version.h presence" >&5
+echo $ECHO_N "checking ${PWLIBDIR}/version.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${PWLIBDIR}/version.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/version.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/version.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${PWLIBDIR}/version.h" >&5
+echo $ECHO_N "checking for ${PWLIBDIR}/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ HAS_PWLIB=1
+fi
+
+
+fi
+if test "${HAS_PWLIB:-unset}" = "unset" ; then
+ if test "${OPENH323DIR:-unset}" != "unset"; then
+ as_ac_Header=`echo "ac_cv_header_${OPENH323DIR}/../pwlib/version.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${OPENH323DIR}/../pwlib/version.h" >&5
+echo $ECHO_N "checking for ${OPENH323DIR}/../pwlib/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${OPENH323DIR}/../pwlib/version.h usability" >&5
+echo $ECHO_N "checking ${OPENH323DIR}/../pwlib/version.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${OPENH323DIR}/../pwlib/version.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${OPENH323DIR}/../pwlib/version.h presence" >&5
+echo $ECHO_N "checking ${OPENH323DIR}/../pwlib/version.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${OPENH323DIR}/../pwlib/version.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/../pwlib/version.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/../pwlib/version.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${OPENH323DIR}/../pwlib/version.h" >&5
+echo $ECHO_N "checking for ${OPENH323DIR}/../pwlib/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ HAS_PWLIB=1
+fi
+
+
+ fi
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ PWLIBDIR="${OPENH323DIR}/../pwlib"
+ else
+ as_ac_Header=`echo "ac_cv_header_${HOME}/pwlib/include/ptlib.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${HOME}/pwlib/include/ptlib.h" >&5
+echo $ECHO_N "checking for ${HOME}/pwlib/include/ptlib.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${HOME}/pwlib/include/ptlib.h usability" >&5
+echo $ECHO_N "checking ${HOME}/pwlib/include/ptlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${HOME}/pwlib/include/ptlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${HOME}/pwlib/include/ptlib.h presence" >&5
+echo $ECHO_N "checking ${HOME}/pwlib/include/ptlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${HOME}/pwlib/include/ptlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/pwlib/include/ptlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${HOME}/pwlib/include/ptlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${HOME}/pwlib/include/ptlib.h" >&5
+echo $ECHO_N "checking for ${HOME}/pwlib/include/ptlib.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ HAS_PWLIB=1
+fi
+
+
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ PWLIBDIR="${HOME}/pwlib"
+ else
+ if test "${ac_cv_header__usr_local_include_ptlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for /usr/local/include/ptlib.h" >&5
+echo $ECHO_N "checking for /usr/local/include/ptlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header__usr_local_include_ptlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header__usr_local_include_ptlib_h" >&5
+echo "${ECHO_T}$ac_cv_header__usr_local_include_ptlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking /usr/local/include/ptlib.h usability" >&5
+echo $ECHO_N "checking /usr/local/include/ptlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include </usr/local/include/ptlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking /usr/local/include/ptlib.h presence" >&5
+echo $ECHO_N "checking /usr/local/include/ptlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include </usr/local/include/ptlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/ptlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: /usr/local/include/ptlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for /usr/local/include/ptlib.h" >&5
+echo $ECHO_N "checking for /usr/local/include/ptlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header__usr_local_include_ptlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header__usr_local_include_ptlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header__usr_local_include_ptlib_h" >&5
+echo "${ECHO_T}$ac_cv_header__usr_local_include_ptlib_h" >&6; }
+
+fi
+if test $ac_cv_header__usr_local_include_ptlib_h = yes; then
+ HAS_PWLIB=1
+fi
+
+
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ # Extract the first word of "ptlib-config", so it can be a program name with args.
+set dummy ptlib-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_PTLIB_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $PTLIB_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PTLIB_CONFIG="$PTLIB_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /usr/local/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PTLIB_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PTLIB_CONFIG=$ac_cv_path_PTLIB_CONFIG
+if test -n "$PTLIB_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $PTLIB_CONFIG" >&5
+echo "${ECHO_T}$PTLIB_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ if test "${PTLIB_CONFIG:-unset}" = "unset" ; then
+ # Extract the first word of "ptlib-config", so it can be a program name with args.
+set dummy ptlib-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_PTLIB_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $PTLIB_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PTLIB_CONFIG="$PTLIB_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /usr/local/share/pwlib/make
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PTLIB_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PTLIB_CONFIG=$ac_cv_path_PTLIB_CONFIG
+if test -n "$PTLIB_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $PTLIB_CONFIG" >&5
+echo "${ECHO_T}$PTLIB_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ fi
+ PWLIB_INCDIR="/usr/local/include"
+ PWLIB_LIBDIR=`${PTLIB_CONFIG} --pwlibdir`
+ if test "${PWLIB_LIBDIR:-unset}" = "unset"; then
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/local/lib64"
+ else
+ PWLIB_LIBDIR="/usr/local/lib"
+ fi
+ fi
+ PWLIB_LIB=`${PTLIB_CONFIG} --ldflags --libs`
+ PWLIB_LIB="-L${PWLIB_LIBDIR} `echo ${PWLIB_LIB}`"
+ else
+ if test "${ac_cv_header__usr_include_ptlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for /usr/include/ptlib.h" >&5
+echo $ECHO_N "checking for /usr/include/ptlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header__usr_include_ptlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header__usr_include_ptlib_h" >&5
+echo "${ECHO_T}$ac_cv_header__usr_include_ptlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking /usr/include/ptlib.h usability" >&5
+echo $ECHO_N "checking /usr/include/ptlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include </usr/include/ptlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking /usr/include/ptlib.h presence" >&5
+echo $ECHO_N "checking /usr/include/ptlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include </usr/include/ptlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/include/ptlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: /usr/include/ptlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for /usr/include/ptlib.h" >&5
+echo $ECHO_N "checking for /usr/include/ptlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header__usr_include_ptlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header__usr_include_ptlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header__usr_include_ptlib_h" >&5
+echo "${ECHO_T}$ac_cv_header__usr_include_ptlib_h" >&6; }
+
+fi
+if test $ac_cv_header__usr_include_ptlib_h = yes; then
+ HAS_PWLIB=1
+fi
+
+
+ if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ # Extract the first word of "ptlib-config", so it can be a program name with args.
+set dummy ptlib-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_PTLIB_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $PTLIB_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PTLIB_CONFIG="$PTLIB_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /usr/share/pwlib/make
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PTLIB_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PTLIB_CONFIG=$ac_cv_path_PTLIB_CONFIG
+if test -n "$PTLIB_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $PTLIB_CONFIG" >&5
+echo "${ECHO_T}$PTLIB_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ PWLIB_INCDIR="/usr/include"
+ PWLIB_LIBDIR=`${PTLIB_CONFIG} --pwlibdir`
+ if test "${PWLIB_LIBDIR:-unset}" = "unset"; then
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/lib64"
+ else
+ PWLIB_LIBDIR="/usr/lib"
+ fi
+ fi
+ PWLIB_LIB=`${PTLIB_CONFIG} --ldflags --libs`
+ PWLIB_LIB="-L${PWLIB_LIBDIR} `echo ${PWLIB_LIB}`"
+ fi
+ fi
+ fi
+ fi
+fi
+
+#if test "${HAS_PWLIB:-unset}" = "unset" ; then
+# echo "Cannot find pwlib - please install or set PWLIBDIR and try again"
+# exit
+#fi
+
+if test "${HAS_PWLIB:-unset}" != "unset" ; then
+ if test "${PWLIBDIR:-unset}" = "unset" ; then
+ if test "${PTLIB_CONFIG:-unset}" != "unset" ; then
+ PWLIBDIR=`$PTLIB_CONFIG --prefix`
+ else
+ echo "Cannot find ptlib-config - please install and try again"
+ exit
+ fi
+ fi
+
+ if test "x$PWLIBDIR" = "x/usr" -o "x$PWLIBDIR" = "x/usr/"; then
+ PWLIBDIR="/usr/share/pwlib"
+ PWLIB_INCDIR="/usr/include"
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/lib64"
+ else
+ PWLIB_LIBDIR="/usr/lib"
+ fi
+ fi
+ if test "x$PWLIBDIR" = "x/usr/local" -o "x$PWLIBDIR" = "x/usr/"; then
+ PWLIBDIR="/usr/local/share/pwlib"
+ PWLIB_INCDIR="/usr/local/include"
+ if test "x$LIB64" != "x"; then
+ PWLIB_LIBDIR="/usr/local/lib64"
+ else
+ PWLIB_LIBDIR="/usr/local/lib"
+ fi
+ fi
+
+ if test "${PWLIB_INCDIR:-unset}" = "unset"; then
+ PWLIB_INCDIR="${PWLIBDIR}/include"
+ fi
+ if test "${PWLIB_LIBDIR:-unset}" = "unset"; then
+ PWLIB_LIBDIR="${PWLIBDIR}/lib"
+ fi
+
+
+
+
+fi
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ if test "${HAS_PWLIB:-unset}" != "unset"; then
+ PWLIB_VERSION=`grep "PWLIB_VERSION" ${PWLIB_INCDIR}/ptbuildopts.h | cut -f2 -d ' ' | sed -e 's/"//g'`
+ PWLIB_MAJOR_VERSION=`echo ${PWLIB_VERSION} | cut -f1 -d.`
+ PWLIB_MINOR_VERSION=`echo ${PWLIB_VERSION} | cut -f2 -d.`
+ PWLIB_BUILD_NUMBER=`echo ${PWLIB_VERSION} | cut -f3 -d.`
+ let PWLIB_VER=${PWLIB_MAJOR_VERSION}*10000+${PWLIB_MINOR_VERSION}*100+${PWLIB_BUILD_NUMBER}
+ let PWLIB_REQ=1*10000+9*100+2
+
+ { echo "$as_me:$LINENO: checking if PWLib version ${PWLIB_VERSION} is compatible with chan_h323" >&5
+echo $ECHO_N "checking if PWLib version ${PWLIB_VERSION} is compatible with chan_h323... $ECHO_C" >&6; }
+ if test ${PWLIB_VER} -lt ${PWLIB_REQ}; then
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ unset HAS_PWLIB
+ else
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ fi
+ fi
+
+
+ if test "${HAS_PWLIB:-unset}" != "unset"; then
+
+PWLIB_OSTYPE=
+case "$host_os" in
+ linux*) PWLIB_OSTYPE=linux ;
+ ;;
+ freebsd* ) PWLIB_OSTYPE=FreeBSD ;
+ ;;
+ openbsd* ) PWLIB_OSTYPE=OpenBSD ;
+ ENDLDLIBS="-lossaudio" ;
+ ;;
+ netbsd* ) PWLIB_OSTYPE=NetBSD ;
+ ENDLDLIBS="-lossaudio" ;
+ ;;
+ solaris* | sunos* ) PWLIB_OSTYPE=solaris ;
+ ;;
+ darwin* ) PWLIB_OSTYPE=Darwin ;
+ ;;
+ beos*) PWLIB_OSTYPE=beos ;
+ STDCCFLAGS="$STDCCFLAGS -D__BEOS__"
+ ;;
+ cygwin*) PWLIB_OSTYPE=cygwin ;
+ ;;
+ mingw*) PWLIB_OSTYPE=mingw ;
+ STDCCFLAGS="$STDCCFLAGS -mms-bitfields" ;
+ ENDLDLIBS="-lwinmm -lwsock32 -lsnmpapi -lmpr -lcomdlg32 -lgdi32 -lavicap32" ;
+ ;;
+ * ) PWLIB_OSTYPE="$host_os" ;
+ { echo "$as_me:$LINENO: WARNING: \"OS $PWLIB_OSTYPE not recognized - proceed with caution!\"" >&5
+echo "$as_me: WARNING: \"OS $PWLIB_OSTYPE not recognized - proceed with caution!\"" >&2;} ;
+ ;;
+esac
+
+PWLIB_MACHTYPE=
+case "$host_cpu" in
+ x86 | i686 | i586 | i486 | i386 ) PWLIB_MACHTYPE=x86
+ ;;
+
+ x86_64) PWLIB_MACHTYPE=x86_64 ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ alpha | alphaev56 | alphaev6 | alphaev67 | alphaev7) PWLIB_MACHTYPE=alpha ;
+ P_64BIT=1 ;
+ ;;
+
+ sparc ) PWLIB_MACHTYPE=sparc ;
+ ;;
+
+ powerpc ) PWLIB_MACHTYPE=ppc ;
+ ;;
+
+ ppc ) PWLIB_MACHTYPE=ppc ;
+ ;;
+
+ powerpc64 ) PWLIB_MACHTYPE=ppc64 ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ ppc64 ) PWLIB_MACHTYPE=ppc64 ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ ia64) PWLIB_MACHTYPE=ia64 ;
+ P_64BIT=1 ;
+ ;;
+
+ s390x) PWLIB_MACHTYPE=s390x ;
+ P_64BIT=1 ;
+ LIB64=1 ;
+ ;;
+
+ s390) PWLIB_MACHTYPE=s390 ;
+ ;;
+
+ * ) PWLIB_MACHTYPE="$host_cpu";
+ { echo "$as_me:$LINENO: WARNING: \"CPU $PWLIB_MACHTYPE not recognized - proceed with caution!\"" >&5
+echo "$as_me: WARNING: \"CPU $PWLIB_MACHTYPE not recognized - proceed with caution!\"" >&2;} ;;
+esac
+
+PWLIB_PLATFORM="${PWLIB_OSTYPE}_${PWLIB_MACHTYPE}"
+
+
+
+
+ PLATFORM_PWLIB="pt_${PWLIB_PLATFORM}_r"
+
+
+ if test "${HAS_PWLIB:-unset}" != "unset"; then
+ { echo "$as_me:$LINENO: checking PWLib installation validity" >&5
+echo $ECHO_N "checking PWLib installation validity... $ECHO_C" >&6; }
+
+ saved_cppflags="${CPPFLAGS}"
+ saved_libs="${LIBS}"
+ if test "${PWLIB_LIB:-unset}" != "unset"; then
+ LIBS="${LIBS} ${PWLIB_LIB} "
+ else
+ LIBS="${LIBS} -L${PWLIB_LIBDIR} -l${PLATFORM_PWLIB} "
+ fi
+ CPPFLAGS="${CPPFLAGS} -I${PWLIB_INCDIR} "
+
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include "ptlib.h"
+int
+main ()
+{
+BOOL q = PTime::IsDaylightSavings();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ ac_cv_lib_PWLIB="yes"
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ ac_cv_lib_PWLIB="no"
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ LIBS="${saved_libs}"
+ CPPFLAGS="${saved_cppflags}"
+
+ if test "${ac_cv_lib_PWLIB}" = "yes"; then
+ if test "${PWLIB_LIB:-undef}" = "undef"; then
+ if test "${PWLIB_LIBDIR}" != "" -a "${PWLIB_LIBDIR}" != "/usr/lib"; then
+ PWLIB_LIB="-L${PWLIB_LIBDIR} -l${PLATFORM_PWLIB}"
+ else
+ PWLIB_LIB="-l${PLATFORM_PWLIB}"
+ fi
+ fi
+ if test "${PWLIB_INCDIR}" != "" -a "${PWLIB_INCDIR}" != "/usr/include"; then
+ PWLIB_INCLUDE="-I${PWLIB_INCDIR}"
+ fi
+ PBX_PWLIB=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PWLIB 1
+_ACEOF
+
+ fi
+ fi
+
+ fi
+fi
+
+if test "${PBX_PWLIB}" = "1" -a "${USE_OPENH323}" != "no" ; then
+ if test -n "${OPENH323_DIR}"; then
+ OPENH323DIR="${OPENH323_DIR}"
+ fi
+
+OPENH323_INCDIR=
+OPENH323_LIBDIR=
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+if test "${OPENH323DIR:-unset}" != "unset" ; then
+ as_ac_Header=`echo "ac_cv_header_${OPENH323DIR}/version.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${OPENH323DIR}/version.h" >&5
+echo $ECHO_N "checking for ${OPENH323DIR}/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${OPENH323DIR}/version.h usability" >&5
+echo $ECHO_N "checking ${OPENH323DIR}/version.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${OPENH323DIR}/version.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${OPENH323DIR}/version.h presence" >&5
+echo $ECHO_N "checking ${OPENH323DIR}/version.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${OPENH323DIR}/version.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${OPENH323DIR}/version.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${OPENH323DIR}/version.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${OPENH323DIR}/version.h" >&5
+echo $ECHO_N "checking for ${OPENH323DIR}/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ HAS_OPENH323=1
+fi
+
+
+fi
+if test "${HAS_OPENH323:-unset}" = "unset" ; then
+ as_ac_Header=`echo "ac_cv_header_${PWLIBDIR}/../openh323/version.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${PWLIBDIR}/../openh323/version.h" >&5
+echo $ECHO_N "checking for ${PWLIBDIR}/../openh323/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${PWLIBDIR}/../openh323/version.h usability" >&5
+echo $ECHO_N "checking ${PWLIBDIR}/../openh323/version.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${PWLIBDIR}/../openh323/version.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${PWLIBDIR}/../openh323/version.h presence" >&5
+echo $ECHO_N "checking ${PWLIBDIR}/../openh323/version.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${PWLIBDIR}/../openh323/version.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${PWLIBDIR}/../openh323/version.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${PWLIBDIR}/../openh323/version.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${PWLIBDIR}/../openh323/version.h" >&5
+echo $ECHO_N "checking for ${PWLIBDIR}/../openh323/version.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ OPENH323DIR="${PWLIBDIR}/../openh323"; HAS_OPENH323=1
+fi
+
+
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="${PWLIBDIR}/../openh323"
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I${PWLIB_INCDIR}/openh323 -I${PWLIB_INCDIR}"
+ as_ac_Header=`echo "ac_cv_header_${OPENH323DIR}/include/h323.h" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${OPENH323DIR}/include/h323.h" >&5
+echo $ECHO_N "checking for ${OPENH323DIR}/include/h323.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ptlib.h>
+
+#include <${OPENH323DIR}/include/h323.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ :
+else
+ OPENH323_INCDIR="${PWLIB_INCDIR}/openh323"; OPENH323_LIBDIR="${PWLIB_LIBDIR}"
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ else
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I${HOME}/openh323/include -I${PWLIB_INCDIR}"
+ as_ac_Header=`echo "ac_cv_header_${HOME}/openh323/include/h323.h" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for ${HOME}/openh323/include/h323.h" >&5
+echo $ECHO_N "checking for ${HOME}/openh323/include/h323.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ${HOME}/openh323/include/h323.h usability" >&5
+echo $ECHO_N "checking ${HOME}/openh323/include/h323.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <${HOME}/openh323/include/h323.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ${HOME}/openh323/include/h323.h presence" >&5
+echo $ECHO_N "checking ${HOME}/openh323/include/h323.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <${HOME}/openh323/include/h323.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ${HOME}/openh323/include/h323.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ${HOME}/openh323/include/h323.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ${HOME}/openh323/include/h323.h" >&5
+echo $ECHO_N "checking for ${HOME}/openh323/include/h323.h... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ HAS_OPENH323=1
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="${HOME}/openh323"
+ else
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I/usr/local/include/openh323 -I${PWLIB_INCDIR}"
+ if test "${ac_cv_header__usr_local_include_openh323_h323_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for /usr/local/include/openh323/h323.h" >&5
+echo $ECHO_N "checking for /usr/local/include/openh323/h323.h... $ECHO_C" >&6; }
+if test "${ac_cv_header__usr_local_include_openh323_h323_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header__usr_local_include_openh323_h323_h" >&5
+echo "${ECHO_T}$ac_cv_header__usr_local_include_openh323_h323_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking /usr/local/include/openh323/h323.h usability" >&5
+echo $ECHO_N "checking /usr/local/include/openh323/h323.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include </usr/local/include/openh323/h323.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking /usr/local/include/openh323/h323.h presence" >&5
+echo $ECHO_N "checking /usr/local/include/openh323/h323.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include </usr/local/include/openh323/h323.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: /usr/local/include/openh323/h323.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: /usr/local/include/openh323/h323.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for /usr/local/include/openh323/h323.h" >&5
+echo $ECHO_N "checking for /usr/local/include/openh323/h323.h... $ECHO_C" >&6; }
+if test "${ac_cv_header__usr_local_include_openh323_h323_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header__usr_local_include_openh323_h323_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header__usr_local_include_openh323_h323_h" >&5
+echo "${ECHO_T}$ac_cv_header__usr_local_include_openh323_h323_h" >&6; }
+
+fi
+if test $ac_cv_header__usr_local_include_openh323_h323_h = yes; then
+ HAS_OPENH323=1
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="/usr/local/share/openh323"
+ OPENH323_INCDIR="/usr/local/include/openh323"
+ if test "x$LIB64" != "x"; then
+ OPENH323_LIBDIR="/usr/local/lib64"
+ else
+ OPENH323_LIBDIR="/usr/local/lib"
+ fi
+ else
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} -I/usr/include/openh323 -I${PWLIB_INCDIR}"
+ { echo "$as_me:$LINENO: checking for /usr/include/openh323/h323.h" >&5
+echo $ECHO_N "checking for /usr/include/openh323/h323.h... $ECHO_C" >&6; }
+if test "${ac_cv_header__usr_include_openh323_h323_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ptlib.h>
+
+#include </usr/include/openh323/h323.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header__usr_include_openh323_h323_h=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header__usr_include_openh323_h323_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header__usr_include_openh323_h323_h" >&5
+echo "${ECHO_T}$ac_cv_header__usr_include_openh323_h323_h" >&6; }
+if test $ac_cv_header__usr_include_openh323_h323_h = yes; then
+ HAS_OPENH323=1
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ OPENH323DIR="/usr/share/openh323"
+ OPENH323_INCDIR="/usr/include/openh323"
+ if test "x$LIB64" != "x"; then
+ OPENH323_LIBDIR="/usr/lib64"
+ else
+ OPENH323_LIBDIR="/usr/lib"
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+if test "${HAS_OPENH323:-unset}" != "unset" ; then
+ if test "${OPENH323_INCDIR:-unset}" = "unset"; then
+ OPENH323_INCDIR="${OPENH323DIR}/include"
+ fi
+ if test "${OPENH323_LIBDIR:-unset}" = "unset"; then
+ OPENH323_LIBDIR="${OPENH323DIR}/lib"
+ fi
+
+ OPENH323_LIBDIR="`cd ${OPENH323_LIBDIR}; pwd`"
+ OPENH323_INCDIR="`cd ${OPENH323_INCDIR}; pwd`"
+ OPENH323DIR="`cd ${OPENH323DIR}; pwd`"
+
+
+
+
+fi
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ if test "${HAS_OPENH323:-unset}" != "unset"; then
+ OPENH323_VERSION=`grep "OPENH323_VERSION" ${OPENH323_INCDIR}/openh323buildopts.h | cut -f2 -d ' ' | sed -e 's/"//g'`
+ OPENH323_MAJOR_VERSION=`echo ${OPENH323_VERSION} | cut -f1 -d.`
+ OPENH323_MINOR_VERSION=`echo ${OPENH323_VERSION} | cut -f2 -d.`
+ OPENH323_BUILD_NUMBER=`echo ${OPENH323_VERSION} | cut -f3 -d.`
+ let OPENH323_VER=${OPENH323_MAJOR_VERSION}*10000+${OPENH323_MINOR_VERSION}*100+${OPENH323_BUILD_NUMBER}
+ let OPENH323_REQ=1*10000+17*100+3
+
+ { echo "$as_me:$LINENO: checking if OpenH323 version ${OPENH323_VERSION} is compatible with chan_h323" >&5
+echo $ECHO_N "checking if OpenH323 version ${OPENH323_VERSION} is compatible with chan_h323... $ECHO_C" >&6; }
+ if test ${OPENH323_VER} -lt ${OPENH323_REQ}; then
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ unset HAS_OPENH323
+ else
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ fi
+ fi
+
+
+ if test "${HAS_OPENH323:-unset}" != "unset"; then
+ { echo "$as_me:$LINENO: checking OpenH323 build option" >&5
+echo $ECHO_N "checking OpenH323 build option... $ECHO_C" >&6; }
+ OPENH323_SUFFIX=
+ prefixes="h323_${PWLIB_PLATFORM}_ h323_ openh323"
+ for pfx in $prefixes; do
+ files=`ls -l ${OPENH323_LIBDIR}/lib${pfx}*.so* 2>/dev/null`
+ libfile=
+ if test -n "$files"; then
+ for f in $files; do
+ if test -f $f -a ! -L $f; then
+ libfile=`basename $f`
+ break;
+ fi
+ done
+ fi
+ if test -n "$libfile"; then
+ OPENH323_PREFIX=$pfx
+ break;
+ fi
+ done
+ if test "${libfile:-unset}" != "unset"; then
+ OPENH323_SUFFIX=`eval "echo ${libfile} | sed -e 's/lib${OPENH323_PREFIX}\([^.]*\)\..*/\1/'"`
+ fi
+ case "${OPENH323_SUFFIX}" in
+ n)
+ OPENH323_BUILD="notrace";;
+ r)
+ OPENH323_BUILD="opt";;
+ d)
+ OPENH323_BUILD="debug";;
+ *)
+ if test "${OPENH323_PREFIX:-undef}" = "openh323"; then
+ notrace=`eval "grep NOTRACE ${OPENH323DIR}/openh323u.mak | grep = | sed -e 's/[A-Z0-9_]*[ ]*=[ ]*//'"`
+ if test "x$notrace" = "x"; then
+ notrace="0"
+ fi
+ if test "$notrace" -ne 0; then
+ OPENH323_BUILD="notrace"
+ else
+ OPENH323_BUILD="opt"
+ fi
+ OPENH323_LIB="-l${OPENH323_PREFIX}"
+ else
+ OPENH323_BUILD="notrace"
+ fi
+ ;;
+ esac
+ { echo "$as_me:$LINENO: result: ${OPENH323_BUILD}" >&5
+echo "${ECHO_T}${OPENH323_BUILD}" >&6; }
+
+
+
+ fi
+
+ PLATFORM_OPENH323="h323_${PWLIB_PLATFORM}_${OPENH323_SUFFIX}"
+
+ if test "${HAS_OPENH323:-unset}" != "unset"; then
+ { echo "$as_me:$LINENO: checking OpenH323 installation validity" >&5
+echo $ECHO_N "checking OpenH323 installation validity... $ECHO_C" >&6; }
+
+ saved_cppflags="${CPPFLAGS}"
+ saved_libs="${LIBS}"
+ if test "${OPENH323_LIB:-unset}" != "unset"; then
+ LIBS="${LIBS} ${OPENH323_LIB} ${PWLIB_LIB}"
+ else
+ LIBS="${LIBS} -L${OPENH323_LIBDIR} -l${PLATFORM_OPENH323} ${PWLIB_LIB}"
+ fi
+ CPPFLAGS="${CPPFLAGS} -I${OPENH323_INCDIR} ${PWLIB_INCLUDE}"
+
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include "ptlib.h"
+ #include "h323.h"
+ #include "h323ep.h"
+int
+main ()
+{
+H323EndPoint ep = H323EndPoint();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ ac_cv_lib_OPENH323="yes"
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ ac_cv_lib_OPENH323="no"
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ LIBS="${saved_libs}"
+ CPPFLAGS="${saved_cppflags}"
+
+ if test "${ac_cv_lib_OPENH323}" = "yes"; then
+ if test "${OPENH323_LIB:-undef}" = "undef"; then
+ if test "${OPENH323_LIBDIR}" != "" -a "${OPENH323_LIBDIR}" != "/usr/lib"; then
+ OPENH323_LIB="-L${OPENH323_LIBDIR} -l${PLATFORM_OPENH323}"
+ else
+ OPENH323_LIB="-l${PLATFORM_OPENH323}"
+ fi
+ fi
+ if test "${OPENH323_INCDIR}" != "" -a "${OPENH323_INCDIR}" != "/usr/include"; then
+ OPENH323_INCLUDE="-I${OPENH323_INCDIR}"
+ fi
+ PBX_OPENH323=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_OPENH323 1
+_ACEOF
+
+ fi
+ fi
+
+fi
+
+LUA_INCLUDE="-I/usr/include/lua5.1"
+LUA_LIB="-llua5.1"
+
+if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
+ pbxlibdir=""
+ # if --with-LUA=DIR has been specified, use it.
+ if test "x${LUA_DIR}" != "x"; then
+ if test -d ${LUA_DIR}/lib; then
+ pbxlibdir="-L${LUA_DIR}/lib"
+ else
+ pbxlibdir="-L${LUA_DIR}"
+ fi
+ fi
+ pbxfuncname="luaL_newstate"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_LUA_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_lua5.1_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -llua5.1" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -llua5.1... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-llua5.1 ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_LUA_FOUND=yes
+else
+ AST_LUA_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_LUA_FOUND}" = "yes"; then
+ LUA_LIB="${pbxlibdir} -llua5.1 "
+ # if --with-LUA=DIR has been specified, use it.
+ if test "x${LUA_DIR}" != "x"; then
+ LUA_INCLUDE="-I${LUA_DIR}/include"
+ fi
+ LUA_INCLUDE="${LUA_INCLUDE} "
+ if test "xlua5.1/lua.h" = "x" ; then # no header, assume found
+ LUA_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE} "
+ if test "${ac_cv_header_lua5_1_lua_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for lua5.1/lua.h" >&5
+echo $ECHO_N "checking for lua5.1/lua.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_lua5_1_lua_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_lua5_1_lua_h" >&5
+echo "${ECHO_T}$ac_cv_header_lua5_1_lua_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking lua5.1/lua.h usability" >&5
+echo $ECHO_N "checking lua5.1/lua.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <lua5.1/lua.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking lua5.1/lua.h presence" >&5
+echo $ECHO_N "checking lua5.1/lua.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <lua5.1/lua.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: lua5.1/lua.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: lua5.1/lua.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for lua5.1/lua.h" >&5
+echo $ECHO_N "checking for lua5.1/lua.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_lua5_1_lua_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_lua5_1_lua_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_lua5_1_lua_h" >&5
+echo "${ECHO_T}$ac_cv_header_lua5_1_lua_h" >&6; }
+
+fi
+if test $ac_cv_header_lua5_1_lua_h = yes; then
+ LUA_HEADER_FOUND=1
+else
+ LUA_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${LUA_HEADER_FOUND}" = "x0" ; then
+ LUA_LIB=""
+ LUA_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ LUA_LIB=""
+ fi
+ PBX_LUA=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LUA 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LUA_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_RADIUS}" != "x1" -a "${USE_RADIUS}" != "no"; then
+ pbxlibdir=""
+ # if --with-RADIUS=DIR has been specified, use it.
+ if test "x${RADIUS_DIR}" != "x"; then
+ if test -d ${RADIUS_DIR}/lib; then
+ pbxlibdir="-L${RADIUS_DIR}/lib"
+ else
+ pbxlibdir="-L${RADIUS_DIR}"
+ fi
+ fi
+ pbxfuncname="rc_read_config"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_RADIUS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_radiusclient-ng_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lradiusclient-ng" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lradiusclient-ng... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lradiusclient-ng ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_RADIUS_FOUND=yes
+else
+ AST_RADIUS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_RADIUS_FOUND}" = "yes"; then
+ RADIUS_LIB="${pbxlibdir} -lradiusclient-ng "
+ # if --with-RADIUS=DIR has been specified, use it.
+ if test "x${RADIUS_DIR}" != "x"; then
+ RADIUS_INCLUDE="-I${RADIUS_DIR}/include"
+ fi
+ RADIUS_INCLUDE="${RADIUS_INCLUDE} "
+ if test "xradiusclient-ng.h" = "x" ; then # no header, assume found
+ RADIUS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${RADIUS_INCLUDE} "
+ if test "${ac_cv_header_radiusclient_ng_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for radiusclient-ng.h" >&5
+echo $ECHO_N "checking for radiusclient-ng.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_radiusclient_ng_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_radiusclient_ng_h" >&5
+echo "${ECHO_T}$ac_cv_header_radiusclient_ng_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking radiusclient-ng.h usability" >&5
+echo $ECHO_N "checking radiusclient-ng.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <radiusclient-ng.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking radiusclient-ng.h presence" >&5
+echo $ECHO_N "checking radiusclient-ng.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <radiusclient-ng.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: radiusclient-ng.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: radiusclient-ng.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for radiusclient-ng.h" >&5
+echo $ECHO_N "checking for radiusclient-ng.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_radiusclient_ng_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_radiusclient_ng_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_radiusclient_ng_h" >&5
+echo "${ECHO_T}$ac_cv_header_radiusclient_ng_h" >&6; }
+
+fi
+if test $ac_cv_header_radiusclient_ng_h = yes; then
+ RADIUS_HEADER_FOUND=1
+else
+ RADIUS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${RADIUS_HEADER_FOUND}" = "x0" ; then
+ RADIUS_LIB=""
+ RADIUS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ RADIUS_LIB=""
+ fi
+ PBX_RADIUS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RADIUS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RADIUS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_SPEEX}" != "x1" -a "${USE_SPEEX}" != "no"; then
+ pbxlibdir=""
+ # if --with-SPEEX=DIR has been specified, use it.
+ if test "x${SPEEX_DIR}" != "x"; then
+ if test -d ${SPEEX_DIR}/lib; then
+ pbxlibdir="-L${SPEEX_DIR}/lib"
+ else
+ pbxlibdir="-L${SPEEX_DIR}"
+ fi
+ fi
+ pbxfuncname="speex_encode"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SPEEX_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_speex_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lspeex" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lspeex... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lspeex ${pbxlibdir} -lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SPEEX_FOUND=yes
+else
+ AST_SPEEX_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SPEEX_FOUND}" = "yes"; then
+ SPEEX_LIB="${pbxlibdir} -lspeex -lm"
+ # if --with-SPEEX=DIR has been specified, use it.
+ if test "x${SPEEX_DIR}" != "x"; then
+ SPEEX_INCLUDE="-I${SPEEX_DIR}/include"
+ fi
+ SPEEX_INCLUDE="${SPEEX_INCLUDE} "
+ if test "xspeex/speex.h" = "x" ; then # no header, assume found
+ SPEEX_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SPEEX_INCLUDE} "
+ if test "${ac_cv_header_speex_speex_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for speex/speex.h" >&5
+echo $ECHO_N "checking for speex/speex.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_speex_speex_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_speex_speex_h" >&5
+echo "${ECHO_T}$ac_cv_header_speex_speex_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking speex/speex.h usability" >&5
+echo $ECHO_N "checking speex/speex.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <speex/speex.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking speex/speex.h presence" >&5
+echo $ECHO_N "checking speex/speex.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <speex/speex.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: speex/speex.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: speex/speex.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: speex/speex.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: speex/speex.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: speex/speex.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: speex/speex.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: speex/speex.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: speex/speex.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for speex/speex.h" >&5
+echo $ECHO_N "checking for speex/speex.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_speex_speex_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_speex_speex_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_speex_speex_h" >&5
+echo "${ECHO_T}$ac_cv_header_speex_speex_h" >&6; }
+
+fi
+if test $ac_cv_header_speex_speex_h = yes; then
+ SPEEX_HEADER_FOUND=1
+else
+ SPEEX_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SPEEX_HEADER_FOUND}" = "x0" ; then
+ SPEEX_LIB=""
+ SPEEX_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SPEEX_LIB=""
+ fi
+ PBX_SPEEX=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SPEEX 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SPEEX_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_SPEEXDSP}" != "x1" -a "${USE_SPEEXDSP}" != "no"; then
+ pbxlibdir=""
+ # if --with-SPEEXDSP=DIR has been specified, use it.
+ if test "x${SPEEXDSP_DIR}" != "x"; then
+ if test -d ${SPEEXDSP_DIR}/lib; then
+ pbxlibdir="-L${SPEEXDSP_DIR}/lib"
+ else
+ pbxlibdir="-L${SPEEXDSP_DIR}"
+ fi
+ fi
+ pbxfuncname="speex_preprocess_ctl"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SPEEXDSP_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_speexdsp_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lspeexdsp" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lspeexdsp... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lspeexdsp ${pbxlibdir} -lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SPEEXDSP_FOUND=yes
+else
+ AST_SPEEXDSP_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SPEEXDSP_FOUND}" = "yes"; then
+ SPEEXDSP_LIB="${pbxlibdir} -lspeexdsp -lm"
+ # if --with-SPEEXDSP=DIR has been specified, use it.
+ if test "x${SPEEXDSP_DIR}" != "x"; then
+ SPEEXDSP_INCLUDE="-I${SPEEXDSP_DIR}/include"
+ fi
+ SPEEXDSP_INCLUDE="${SPEEXDSP_INCLUDE} "
+ if test "xspeex/speex.h" = "x" ; then # no header, assume found
+ SPEEXDSP_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SPEEXDSP_INCLUDE} "
+ if test "${ac_cv_header_speex_speex_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for speex/speex.h" >&5
+echo $ECHO_N "checking for speex/speex.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_speex_speex_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_speex_speex_h" >&5
+echo "${ECHO_T}$ac_cv_header_speex_speex_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking speex/speex.h usability" >&5
+echo $ECHO_N "checking speex/speex.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <speex/speex.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking speex/speex.h presence" >&5
+echo $ECHO_N "checking speex/speex.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <speex/speex.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: speex/speex.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: speex/speex.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: speex/speex.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: speex/speex.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: speex/speex.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: speex/speex.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: speex/speex.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: speex/speex.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: speex/speex.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for speex/speex.h" >&5
+echo $ECHO_N "checking for speex/speex.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_speex_speex_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_speex_speex_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_speex_speex_h" >&5
+echo "${ECHO_T}$ac_cv_header_speex_speex_h" >&6; }
+
+fi
+if test $ac_cv_header_speex_speex_h = yes; then
+ SPEEXDSP_HEADER_FOUND=1
+else
+ SPEEXDSP_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SPEEXDSP_HEADER_FOUND}" = "x0" ; then
+ SPEEXDSP_LIB=""
+ SPEEXDSP_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SPEEXDSP_LIB=""
+ fi
+ PBX_SPEEXDSP=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SPEEXDSP 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SPEEXDSP_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_SQLITE}" != "x1" -a "${USE_SQLITE}" != "no"; then
+ pbxlibdir=""
+ # if --with-SQLITE=DIR has been specified, use it.
+ if test "x${SQLITE_DIR}" != "x"; then
+ if test -d ${SQLITE_DIR}/lib; then
+ pbxlibdir="-L${SQLITE_DIR}/lib"
+ else
+ pbxlibdir="-L${SQLITE_DIR}"
+ fi
+ fi
+ pbxfuncname="sqlite_exec"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SQLITE_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_sqlite_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lsqlite" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lsqlite... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsqlite ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SQLITE_FOUND=yes
+else
+ AST_SQLITE_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SQLITE_FOUND}" = "yes"; then
+ SQLITE_LIB="${pbxlibdir} -lsqlite "
+ # if --with-SQLITE=DIR has been specified, use it.
+ if test "x${SQLITE_DIR}" != "x"; then
+ SQLITE_INCLUDE="-I${SQLITE_DIR}/include"
+ fi
+ SQLITE_INCLUDE="${SQLITE_INCLUDE} "
+ if test "xsqlite.h" = "x" ; then # no header, assume found
+ SQLITE_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SQLITE_INCLUDE} "
+ if test "${ac_cv_header_sqlite_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for sqlite.h" >&5
+echo $ECHO_N "checking for sqlite.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sqlite_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sqlite_h" >&5
+echo "${ECHO_T}$ac_cv_header_sqlite_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking sqlite.h usability" >&5
+echo $ECHO_N "checking sqlite.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sqlite.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking sqlite.h presence" >&5
+echo $ECHO_N "checking sqlite.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sqlite.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sqlite.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sqlite.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sqlite.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sqlite.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sqlite.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sqlite.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sqlite.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sqlite.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for sqlite.h" >&5
+echo $ECHO_N "checking for sqlite.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sqlite_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sqlite_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sqlite_h" >&5
+echo "${ECHO_T}$ac_cv_header_sqlite_h" >&6; }
+
+fi
+if test $ac_cv_header_sqlite_h = yes; then
+ SQLITE_HEADER_FOUND=1
+else
+ SQLITE_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SQLITE_HEADER_FOUND}" = "x0" ; then
+ SQLITE_LIB=""
+ SQLITE_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SQLITE_LIB=""
+ fi
+ PBX_SQLITE=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQLITE 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQLITE_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_SQLITE3}" != "x1" -a "${USE_SQLITE3}" != "no"; then
+ pbxlibdir=""
+ # if --with-SQLITE3=DIR has been specified, use it.
+ if test "x${SQLITE3_DIR}" != "x"; then
+ if test -d ${SQLITE3_DIR}/lib; then
+ pbxlibdir="-L${SQLITE3_DIR}/lib"
+ else
+ pbxlibdir="-L${SQLITE3_DIR}"
+ fi
+ fi
+ pbxfuncname="sqlite3_open"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SQLITE3_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_sqlite3_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lsqlite3" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lsqlite3... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsqlite3 ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SQLITE3_FOUND=yes
+else
+ AST_SQLITE3_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SQLITE3_FOUND}" = "yes"; then
+ SQLITE3_LIB="${pbxlibdir} -lsqlite3 "
+ # if --with-SQLITE3=DIR has been specified, use it.
+ if test "x${SQLITE3_DIR}" != "x"; then
+ SQLITE3_INCLUDE="-I${SQLITE3_DIR}/include"
+ fi
+ SQLITE3_INCLUDE="${SQLITE3_INCLUDE} "
+ if test "xsqlite3.h" = "x" ; then # no header, assume found
+ SQLITE3_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SQLITE3_INCLUDE} "
+ if test "${ac_cv_header_sqlite3_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for sqlite3.h" >&5
+echo $ECHO_N "checking for sqlite3.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sqlite3_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sqlite3_h" >&5
+echo "${ECHO_T}$ac_cv_header_sqlite3_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking sqlite3.h usability" >&5
+echo $ECHO_N "checking sqlite3.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sqlite3.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking sqlite3.h presence" >&5
+echo $ECHO_N "checking sqlite3.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sqlite3.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sqlite3.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sqlite3.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sqlite3.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sqlite3.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sqlite3.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sqlite3.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sqlite3.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sqlite3.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sqlite3.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for sqlite3.h" >&5
+echo $ECHO_N "checking for sqlite3.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sqlite3_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sqlite3_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sqlite3_h" >&5
+echo "${ECHO_T}$ac_cv_header_sqlite3_h" >&6; }
+
+fi
+if test $ac_cv_header_sqlite3_h = yes; then
+ SQLITE3_HEADER_FOUND=1
+else
+ SQLITE3_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SQLITE3_HEADER_FOUND}" = "x0" ; then
+ SQLITE3_LIB=""
+ SQLITE3_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SQLITE3_LIB=""
+ fi
+ PBX_SQLITE3=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQLITE3 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQLITE3_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_CRYPTO}" != "x1" -a "${USE_CRYPTO}" != "no"; then
+ pbxlibdir=""
+ # if --with-CRYPTO=DIR has been specified, use it.
+ if test "x${CRYPTO_DIR}" != "x"; then
+ if test -d ${CRYPTO_DIR}/lib; then
+ pbxlibdir="-L${CRYPTO_DIR}/lib"
+ else
+ pbxlibdir="-L${CRYPTO_DIR}"
+ fi
+ fi
+ pbxfuncname="AES_encrypt"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_CRYPTO_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_crypto_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lcrypto" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lcrypto... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypto ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_CRYPTO_FOUND=yes
+else
+ AST_CRYPTO_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_CRYPTO_FOUND}" = "yes"; then
+ CRYPTO_LIB="${pbxlibdir} -lcrypto "
+ # if --with-CRYPTO=DIR has been specified, use it.
+ if test "x${CRYPTO_DIR}" != "x"; then
+ CRYPTO_INCLUDE="-I${CRYPTO_DIR}/include"
+ fi
+ CRYPTO_INCLUDE="${CRYPTO_INCLUDE} "
+ if test "xopenssl/aes.h" = "x" ; then # no header, assume found
+ CRYPTO_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CRYPTO_INCLUDE} "
+ if test "${ac_cv_header_openssl_aes_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for openssl/aes.h" >&5
+echo $ECHO_N "checking for openssl/aes.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_openssl_aes_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_openssl_aes_h" >&5
+echo "${ECHO_T}$ac_cv_header_openssl_aes_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking openssl/aes.h usability" >&5
+echo $ECHO_N "checking openssl/aes.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <openssl/aes.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking openssl/aes.h presence" >&5
+echo $ECHO_N "checking openssl/aes.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <openssl/aes.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: openssl/aes.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: openssl/aes.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: openssl/aes.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: openssl/aes.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: openssl/aes.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: openssl/aes.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: openssl/aes.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/aes.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: openssl/aes.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for openssl/aes.h" >&5
+echo $ECHO_N "checking for openssl/aes.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_openssl_aes_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_openssl_aes_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_openssl_aes_h" >&5
+echo "${ECHO_T}$ac_cv_header_openssl_aes_h" >&6; }
+
+fi
+if test $ac_cv_header_openssl_aes_h = yes; then
+ CRYPTO_HEADER_FOUND=1
+else
+ CRYPTO_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${CRYPTO_HEADER_FOUND}" = "x0" ; then
+ CRYPTO_LIB=""
+ CRYPTO_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ CRYPTO_LIB=""
+ fi
+ PBX_CRYPTO=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CRYPTO 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_CRYPTO_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "$PBX_CRYPTO" = "1";
+then
+
+if test "x${PBX_OPENSSL}" != "x1" -a "${USE_OPENSSL}" != "no"; then
+ pbxlibdir=""
+ # if --with-OPENSSL=DIR has been specified, use it.
+ if test "x${OPENSSL_DIR}" != "x"; then
+ if test -d ${OPENSSL_DIR}/lib; then
+ pbxlibdir="-L${OPENSSL_DIR}/lib"
+ else
+ pbxlibdir="-L${OPENSSL_DIR}"
+ fi
+ fi
+ pbxfuncname="ssl2_connect"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_OPENSSL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_ssl_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lssl" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lssl... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssl ${pbxlibdir} -lcrypto $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_OPENSSL_FOUND=yes
+else
+ AST_OPENSSL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_OPENSSL_FOUND}" = "yes"; then
+ OPENSSL_LIB="${pbxlibdir} -lssl -lcrypto"
+ # if --with-OPENSSL=DIR has been specified, use it.
+ if test "x${OPENSSL_DIR}" != "x"; then
+ OPENSSL_INCLUDE="-I${OPENSSL_DIR}/include"
+ fi
+ OPENSSL_INCLUDE="${OPENSSL_INCLUDE} "
+ if test "xopenssl/ssl.h" = "x" ; then # no header, assume found
+ OPENSSL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${OPENSSL_INCLUDE} "
+ if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for openssl/ssl.h" >&5
+echo $ECHO_N "checking for openssl/ssl.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ssl_h" >&5
+echo "${ECHO_T}$ac_cv_header_openssl_ssl_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking openssl/ssl.h usability" >&5
+echo $ECHO_N "checking openssl/ssl.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <openssl/ssl.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking openssl/ssl.h presence" >&5
+echo $ECHO_N "checking openssl/ssl.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <openssl/ssl.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: openssl/ssl.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: openssl/ssl.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: openssl/ssl.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: openssl/ssl.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: openssl/ssl.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: openssl/ssl.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: openssl/ssl.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for openssl/ssl.h" >&5
+echo $ECHO_N "checking for openssl/ssl.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_openssl_ssl_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ssl_h" >&5
+echo "${ECHO_T}$ac_cv_header_openssl_ssl_h" >&6; }
+
+fi
+if test $ac_cv_header_openssl_ssl_h = yes; then
+ OPENSSL_HEADER_FOUND=1
+else
+ OPENSSL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${OPENSSL_HEADER_FOUND}" = "x0" ; then
+ OPENSSL_LIB=""
+ OPENSSL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ OPENSSL_LIB=""
+ fi
+ PBX_OPENSSL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+fi
+
+if test "$PBX_OPENSSL" = "1";
+then
+
+if test "x${PBX_OSPTK}" != "x1" -a "${USE_OSPTK}" != "no"; then
+ pbxlibdir=""
+ # if --with-OSPTK=DIR has been specified, use it.
+ if test "x${OSPTK_DIR}" != "x"; then
+ if test -d ${OSPTK_DIR}/lib; then
+ pbxlibdir="-L${OSPTK_DIR}/lib"
+ else
+ pbxlibdir="-L${OSPTK_DIR}"
+ fi
+ fi
+ pbxfuncname="OSPPCryptoDecrypt"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_OSPTK_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_osptk_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -losptk" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -losptk... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-losptk ${pbxlibdir} -lcrypto -lssl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_OSPTK_FOUND=yes
+else
+ AST_OSPTK_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_OSPTK_FOUND}" = "yes"; then
+ OSPTK_LIB="${pbxlibdir} -losptk -lcrypto -lssl"
+ # if --with-OSPTK=DIR has been specified, use it.
+ if test "x${OSPTK_DIR}" != "x"; then
+ OSPTK_INCLUDE="-I${OSPTK_DIR}/include"
+ fi
+ OSPTK_INCLUDE="${OSPTK_INCLUDE} "
+ if test "xosp/osp.h" = "x" ; then # no header, assume found
+ OSPTK_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${OSPTK_INCLUDE} "
+ if test "${ac_cv_header_osp_osp_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for osp/osp.h" >&5
+echo $ECHO_N "checking for osp/osp.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_osp_osp_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_osp_osp_h" >&5
+echo "${ECHO_T}$ac_cv_header_osp_osp_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking osp/osp.h usability" >&5
+echo $ECHO_N "checking osp/osp.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <osp/osp.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking osp/osp.h presence" >&5
+echo $ECHO_N "checking osp/osp.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <osp/osp.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: osp/osp.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: osp/osp.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: osp/osp.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: osp/osp.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: osp/osp.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: osp/osp.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: osp/osp.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: osp/osp.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: osp/osp.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for osp/osp.h" >&5
+echo $ECHO_N "checking for osp/osp.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_osp_osp_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_osp_osp_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_osp_osp_h" >&5
+echo "${ECHO_T}$ac_cv_header_osp_osp_h" >&6; }
+
+fi
+if test $ac_cv_header_osp_osp_h = yes; then
+ OSPTK_HEADER_FOUND=1
+else
+ OSPTK_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${OSPTK_HEADER_FOUND}" = "x0" ; then
+ OSPTK_LIB=""
+ OSPTK_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ OSPTK_LIB=""
+ fi
+ PBX_OSPTK=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSPTK 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_OSPTK_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+fi
+
+
+if test "x${PBX_FREETDS}" != "x1" -a "${USE_FREETDS}" != "no"; then
+ pbxlibdir=""
+ # if --with-FREETDS=DIR has been specified, use it.
+ if test "x${FREETDS_DIR}" != "x"; then
+ if test -d ${FREETDS_DIR}/lib; then
+ pbxlibdir="-L${FREETDS_DIR}/lib"
+ else
+ pbxlibdir="-L${FREETDS_DIR}"
+ fi
+ fi
+ pbxfuncname="tds_version"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_FREETDS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_tds_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -ltds" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -ltds... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltds ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_FREETDS_FOUND=yes
+else
+ AST_FREETDS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_FREETDS_FOUND}" = "yes"; then
+ FREETDS_LIB="${pbxlibdir} -ltds "
+ # if --with-FREETDS=DIR has been specified, use it.
+ if test "x${FREETDS_DIR}" != "x"; then
+ FREETDS_INCLUDE="-I${FREETDS_DIR}/include"
+ fi
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} "
+ if test "xtds.h" = "x" ; then # no header, assume found
+ FREETDS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${FREETDS_INCLUDE} "
+ if test "${ac_cv_header_tds_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for tds.h" >&5
+echo $ECHO_N "checking for tds.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_tds_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_tds_h" >&5
+echo "${ECHO_T}$ac_cv_header_tds_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking tds.h usability" >&5
+echo $ECHO_N "checking tds.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <tds.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking tds.h presence" >&5
+echo $ECHO_N "checking tds.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <tds.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: tds.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: tds.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: tds.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: tds.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: tds.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: tds.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: tds.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: tds.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: tds.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: tds.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: tds.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: tds.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: tds.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: tds.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: tds.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: tds.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for tds.h" >&5
+echo $ECHO_N "checking for tds.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_tds_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_tds_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_tds_h" >&5
+echo "${ECHO_T}$ac_cv_header_tds_h" >&6; }
+
+fi
+if test $ac_cv_header_tds_h = yes; then
+ FREETDS_HEADER_FOUND=1
+else
+ FREETDS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${FREETDS_HEADER_FOUND}" = "x0" ; then
+ FREETDS_LIB=""
+ FREETDS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ FREETDS_LIB=""
+ fi
+ PBX_FREETDS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FREETDS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FREETDS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+if test "${PBX_FREETDS}" != "0";
+then
+ case `grep TDS_VERSION_NO ${FREETDS_DIR:-/usr}/include/tdsver.h` in
+ *0.64*)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_64"
+ ;;
+ *0.63*)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_63"
+ ;;
+ *0.62*)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_62"
+ ;;
+ *)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_PRE_0_62"
+ ;;
+ esac
+fi
+
+
+if test "x${PBX_TERMCAP}" != "x1" -a "${USE_TERMCAP}" != "no"; then
+ pbxlibdir=""
+ # if --with-TERMCAP=DIR has been specified, use it.
+ if test "x${TERMCAP_DIR}" != "x"; then
+ if test -d ${TERMCAP_DIR}/lib; then
+ pbxlibdir="-L${TERMCAP_DIR}/lib"
+ else
+ pbxlibdir="-L${TERMCAP_DIR}"
+ fi
+ fi
+ pbxfuncname="tgetent"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TERMCAP_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_termcap_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -ltermcap" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -ltermcap... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermcap ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TERMCAP_FOUND=yes
+else
+ AST_TERMCAP_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TERMCAP_FOUND}" = "yes"; then
+ TERMCAP_LIB="${pbxlibdir} -ltermcap "
+ # if --with-TERMCAP=DIR has been specified, use it.
+ if test "x${TERMCAP_DIR}" != "x"; then
+ TERMCAP_INCLUDE="-I${TERMCAP_DIR}/include"
+ fi
+ TERMCAP_INCLUDE="${TERMCAP_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ TERMCAP_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TERMCAP_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ TERMCAP_HEADER_FOUND=1
+else
+ TERMCAP_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TERMCAP_HEADER_FOUND}" = "x0" ; then
+ TERMCAP_LIB=""
+ TERMCAP_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TERMCAP_LIB=""
+ fi
+ PBX_TERMCAP=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TERMCAP 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TERMCAP_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_TINFO}" != "x1" -a "${USE_TINFO}" != "no"; then
+ pbxlibdir=""
+ # if --with-TINFO=DIR has been specified, use it.
+ if test "x${TINFO_DIR}" != "x"; then
+ if test -d ${TINFO_DIR}/lib; then
+ pbxlibdir="-L${TINFO_DIR}/lib"
+ else
+ pbxlibdir="-L${TINFO_DIR}"
+ fi
+ fi
+ pbxfuncname="tgetent"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TINFO_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_tinfo_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -ltinfo" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -ltinfo... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltinfo ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TINFO_FOUND=yes
+else
+ AST_TINFO_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TINFO_FOUND}" = "yes"; then
+ TINFO_LIB="${pbxlibdir} -ltinfo "
+ # if --with-TINFO=DIR has been specified, use it.
+ if test "x${TINFO_DIR}" != "x"; then
+ TINFO_INCLUDE="-I${TINFO_DIR}/include"
+ fi
+ TINFO_INCLUDE="${TINFO_INCLUDE} "
+ if test "x" = "x" ; then # no header, assume found
+ TINFO_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TINFO_INCLUDE} "
+ if test "${ac_cv_header_+set}" = set; then
+ { echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usability" >&5
+echo $ECHO_N "checking usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking presence" >&5
+echo $ECHO_N "checking presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: : accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: : proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: : present but cannot be compiled" >&5
+echo "$as_me: WARNING: : present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: : check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : see the Autoconf documentation" >&5
+echo "$as_me: WARNING: : see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: : section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: : proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: : in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: : in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for " >&5
+echo $ECHO_N "checking for ... $ECHO_C" >&6; }
+if test "${ac_cv_header_+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_" >&5
+echo "${ECHO_T}$ac_cv_header_" >&6; }
+
+fi
+if test $ac_cv_header_ = yes; then
+ TINFO_HEADER_FOUND=1
+else
+ TINFO_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TINFO_HEADER_FOUND}" = "x0" ; then
+ TINFO_LIB=""
+ TINFO_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TINFO_LIB=""
+ fi
+ PBX_TINFO=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TINFO 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TINFO_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "${host_os}" != "linux-gnu" ; then
+ tonezone_extra="-lm"
+fi
+
+# new tonezone, version 1.4.0
+
+if test "x${PBX_TONEZONE}" != "x1" -a "${USE_TONEZONE}" != "no"; then
+ pbxlibdir=""
+ # if --with-TONEZONE=DIR has been specified, use it.
+ if test "x${TONEZONE_DIR}" != "x"; then
+ if test -d ${TONEZONE_DIR}/lib; then
+ pbxlibdir="-L${TONEZONE_DIR}/lib"
+ else
+ pbxlibdir="-L${TONEZONE_DIR}"
+ fi
+ fi
+ pbxfuncname="tone_zone_find"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TONEZONE_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_tonezone_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -ltonezone" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -ltonezone... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltonezone ${pbxlibdir} ${tonezone_extra} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TONEZONE_FOUND=yes
+else
+ AST_TONEZONE_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TONEZONE_FOUND}" = "yes"; then
+ TONEZONE_LIB="${pbxlibdir} -ltonezone ${tonezone_extra}"
+ # if --with-TONEZONE=DIR has been specified, use it.
+ if test "x${TONEZONE_DIR}" != "x"; then
+ TONEZONE_INCLUDE="-I${TONEZONE_DIR}/include"
+ fi
+ TONEZONE_INCLUDE="${TONEZONE_INCLUDE} "
+ if test "xzaptel/tonezone.h" = "x" ; then # no header, assume found
+ TONEZONE_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TONEZONE_INCLUDE} "
+ if test "${ac_cv_header_zaptel_tonezone_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for zaptel/tonezone.h" >&5
+echo $ECHO_N "checking for zaptel/tonezone.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_tonezone_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_tonezone_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_tonezone_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking zaptel/tonezone.h usability" >&5
+echo $ECHO_N "checking zaptel/tonezone.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <zaptel/tonezone.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking zaptel/tonezone.h presence" >&5
+echo $ECHO_N "checking zaptel/tonezone.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/tonezone.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/tonezone.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: zaptel/tonezone.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for zaptel/tonezone.h" >&5
+echo $ECHO_N "checking for zaptel/tonezone.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_tonezone_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_zaptel_tonezone_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_tonezone_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_tonezone_h" >&6; }
+
+fi
+if test $ac_cv_header_zaptel_tonezone_h = yes; then
+ TONEZONE_HEADER_FOUND=1
+else
+ TONEZONE_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TONEZONE_HEADER_FOUND}" = "x0" ; then
+ TONEZONE_LIB=""
+ TONEZONE_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TONEZONE_LIB=""
+ fi
+ PBX_TONEZONE=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TONEZONE 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TONEZONE_VERSION 140
+_ACEOF
+
+ fi
+ fi
+fi
+
+# other case, old tonezone (0.80)
+
+if test "x${PBX_TONEZONE}" != "x1" -a "${USE_TONEZONE}" != "no"; then
+ pbxlibdir=""
+ # if --with-TONEZONE=DIR has been specified, use it.
+ if test "x${TONEZONE_DIR}" != "x"; then
+ if test -d ${TONEZONE_DIR}/lib; then
+ pbxlibdir="-L${TONEZONE_DIR}/lib"
+ else
+ pbxlibdir="-L${TONEZONE_DIR}"
+ fi
+ fi
+ pbxfuncname="tone_zone_find"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TONEZONE_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_tonezone_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -ltonezone" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -ltonezone... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltonezone ${pbxlibdir} ${tonezone_extra} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TONEZONE_FOUND=yes
+else
+ AST_TONEZONE_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TONEZONE_FOUND}" = "yes"; then
+ TONEZONE_LIB="${pbxlibdir} -ltonezone ${tonezone_extra}"
+ # if --with-TONEZONE=DIR has been specified, use it.
+ if test "x${TONEZONE_DIR}" != "x"; then
+ TONEZONE_INCLUDE="-I${TONEZONE_DIR}/include"
+ fi
+ TONEZONE_INCLUDE="${TONEZONE_INCLUDE} "
+ if test "xzaptel/zaptel.h" = "x" ; then # no header, assume found
+ TONEZONE_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TONEZONE_INCLUDE} "
+ if test "${ac_cv_header_zaptel_zaptel_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for zaptel/zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_zaptel_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking zaptel/zaptel.h usability" >&5
+echo $ECHO_N "checking zaptel/zaptel.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <zaptel/zaptel.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking zaptel/zaptel.h presence" >&5
+echo $ECHO_N "checking zaptel/zaptel.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for zaptel/zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_zaptel_zaptel_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_zaptel_h" >&6; }
+
+fi
+if test $ac_cv_header_zaptel_zaptel_h = yes; then
+ TONEZONE_HEADER_FOUND=1
+else
+ TONEZONE_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TONEZONE_HEADER_FOUND}" = "x0" ; then
+ TONEZONE_LIB=""
+ TONEZONE_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TONEZONE_LIB=""
+ fi
+ PBX_TONEZONE=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TONEZONE 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TONEZONE_VERSION 80
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_USB}" != "x1" -a "${USE_USB}" != "no"; then
+ pbxlibdir=""
+ # if --with-USB=DIR has been specified, use it.
+ if test "x${USB_DIR}" != "x"; then
+ if test -d ${USB_DIR}/lib; then
+ pbxlibdir="-L${USB_DIR}/lib"
+ else
+ pbxlibdir="-L${USB_DIR}"
+ fi
+ fi
+ pbxfuncname="usb_init"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_USB_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_usb_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lusb" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lusb... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lusb ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_USB_FOUND=yes
+else
+ AST_USB_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_USB_FOUND}" = "yes"; then
+ USB_LIB="${pbxlibdir} -lusb "
+ # if --with-USB=DIR has been specified, use it.
+ if test "x${USB_DIR}" != "x"; then
+ USB_INCLUDE="-I${USB_DIR}/include"
+ fi
+ USB_INCLUDE="${USB_INCLUDE} "
+ if test "xusb.h" = "x" ; then # no header, assume found
+ USB_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${USB_INCLUDE} "
+ if test "${ac_cv_header_usb_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for usb.h" >&5
+echo $ECHO_N "checking for usb.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_usb_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_usb_h" >&5
+echo "${ECHO_T}$ac_cv_header_usb_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking usb.h usability" >&5
+echo $ECHO_N "checking usb.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <usb.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking usb.h presence" >&5
+echo $ECHO_N "checking usb.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <usb.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: usb.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: usb.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: usb.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: usb.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: usb.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: usb.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: usb.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: usb.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: usb.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: usb.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: usb.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: usb.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: usb.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: usb.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: usb.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: usb.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for usb.h" >&5
+echo $ECHO_N "checking for usb.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_usb_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_usb_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_usb_h" >&5
+echo "${ECHO_T}$ac_cv_header_usb_h" >&6; }
+
+fi
+if test $ac_cv_header_usb_h = yes; then
+ USB_HEADER_FOUND=1
+else
+ USB_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${USB_HEADER_FOUND}" = "x0" ; then
+ USB_LIB=""
+ USB_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ USB_LIB=""
+ fi
+ PBX_USB=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_USB 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_USB_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+if test "x${PBX_VORBIS}" != "x1" -a "${USE_VORBIS}" != "no"; then
+ pbxlibdir=""
+ # if --with-VORBIS=DIR has been specified, use it.
+ if test "x${VORBIS_DIR}" != "x"; then
+ if test -d ${VORBIS_DIR}/lib; then
+ pbxlibdir="-L${VORBIS_DIR}/lib"
+ else
+ pbxlibdir="-L${VORBIS_DIR}"
+ fi
+ fi
+ pbxfuncname="vorbis_info_init"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_VORBIS_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_vorbis_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lvorbis" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lvorbis... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lvorbis ${pbxlibdir} -lm -lvorbisenc $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_VORBIS_FOUND=yes
+else
+ AST_VORBIS_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_VORBIS_FOUND}" = "yes"; then
+ VORBIS_LIB="${pbxlibdir} -lvorbis -lm -lvorbisenc"
+ # if --with-VORBIS=DIR has been specified, use it.
+ if test "x${VORBIS_DIR}" != "x"; then
+ VORBIS_INCLUDE="-I${VORBIS_DIR}/include"
+ fi
+ VORBIS_INCLUDE="${VORBIS_INCLUDE} "
+ if test "xvorbis/codec.h" = "x" ; then # no header, assume found
+ VORBIS_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${VORBIS_INCLUDE} "
+ if test "${ac_cv_header_vorbis_codec_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for vorbis/codec.h" >&5
+echo $ECHO_N "checking for vorbis/codec.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_vorbis_codec_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_vorbis_codec_h" >&5
+echo "${ECHO_T}$ac_cv_header_vorbis_codec_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking vorbis/codec.h usability" >&5
+echo $ECHO_N "checking vorbis/codec.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <vorbis/codec.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking vorbis/codec.h presence" >&5
+echo $ECHO_N "checking vorbis/codec.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <vorbis/codec.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: vorbis/codec.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: vorbis/codec.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: vorbis/codec.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: vorbis/codec.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: vorbis/codec.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: vorbis/codec.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: vorbis/codec.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: vorbis/codec.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: vorbis/codec.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for vorbis/codec.h" >&5
+echo $ECHO_N "checking for vorbis/codec.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_vorbis_codec_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_vorbis_codec_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_vorbis_codec_h" >&5
+echo "${ECHO_T}$ac_cv_header_vorbis_codec_h" >&6; }
+
+fi
+if test $ac_cv_header_vorbis_codec_h = yes; then
+ VORBIS_HEADER_FOUND=1
+else
+ VORBIS_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${VORBIS_HEADER_FOUND}" = "x0" ; then
+ VORBIS_LIB=""
+ VORBIS_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ VORBIS_LIB=""
+ fi
+ PBX_VORBIS=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_VORBIS 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_VORBIS_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+if test "${USE_VPB}" != "no"; then
+ { echo "$as_me:$LINENO: checking for vpb_open in -lvpb" >&5
+echo $ECHO_N "checking for vpb_open in -lvpb... $ECHO_C" >&6; }
+ saved_libs="${LIBS}"
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${VPB_DIR}" != "x"; then
+ if test -d ${VPB_DIR}/lib; then
+ vpblibdir=${VPB_DIR}/lib
+ else
+ vpblibdir=${VPB_DIR}
+ fi
+ LIBS="${LIBS} -L${vpblibdir}"
+ CPPFLAGS="${CPPFLAGS} -I${VPB_DIR}/include"
+ fi
+ LIBS="${LIBS} -lvpb -lpthread"
+ cat >conftest.$ac_ext <<_ACEOF
+
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <vpbapi.h>
+int
+main ()
+{
+int q = vpb_open(0,0);
+ ;
+ return 0;
+}
+
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ ac_cv_lib_vpb_vpb_open="yes"
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ ac_cv_lib_vpb_vpb_open="no"
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="${saved_libs}"
+ CPPFLAGS="${saved_cppflags}"
+ if test "${ac_cv_lib_vpb_vpb_open}" = "yes"; then
+ VPB_LIB="-lvpb"
+ if test "${VPB_DIR}" != ""; then
+ VPB_LIB="-L${vpblibdir} ${VPB_LIB}"
+ VPB_INCLUDE="-I${VPB_DIR}/include"
+ fi
+ PBX_VPB=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_VPB 1
+_ACEOF
+
+ fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+if test "x${PBX_ZLIB}" != "x1" -a "${USE_ZLIB}" != "no"; then
+ pbxlibdir=""
+ # if --with-ZLIB=DIR has been specified, use it.
+ if test "x${ZLIB_DIR}" != "x"; then
+ if test -d ${ZLIB_DIR}/lib; then
+ pbxlibdir="-L${ZLIB_DIR}/lib"
+ else
+ pbxlibdir="-L${ZLIB_DIR}"
+ fi
+ fi
+ pbxfuncname="compress"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ZLIB_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_z_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lz" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lz... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lz ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ZLIB_FOUND=yes
+else
+ AST_ZLIB_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ZLIB_FOUND}" = "yes"; then
+ ZLIB_LIB="${pbxlibdir} -lz "
+ # if --with-ZLIB=DIR has been specified, use it.
+ if test "x${ZLIB_DIR}" != "x"; then
+ ZLIB_INCLUDE="-I${ZLIB_DIR}/include"
+ fi
+ ZLIB_INCLUDE="${ZLIB_INCLUDE} "
+ if test "xzlib.h" = "x" ; then # no header, assume found
+ ZLIB_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ZLIB_INCLUDE} "
+ if test "${ac_cv_header_zlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for zlib.h" >&5
+echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_zlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking zlib.h usability" >&5
+echo $ECHO_N "checking zlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <zlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking zlib.h presence" >&5
+echo $ECHO_N "checking zlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: zlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: zlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: zlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: zlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: zlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: zlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: zlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for zlib.h" >&5
+echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_zlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_zlib_h" >&6; }
+
+fi
+if test $ac_cv_header_zlib_h = yes; then
+ ZLIB_HEADER_FOUND=1
+else
+ ZLIB_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ZLIB_HEADER_FOUND}" = "x0" ; then
+ ZLIB_LIB=""
+ ZLIB_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ZLIB_LIB=""
+ fi
+ PBX_ZLIB=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZLIB 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZLIB_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+# Check for various zaptel features and locations.
+# The version number, which goes into HAVE_ZAPTEL_VERSION,
+# will be used in the system headers to determine the location
+# of the zaptel.h header.
+
+
+ if test "x${PBX_ZAPTEL}" != "x1" -a "${USE_ZAPTEL}" != "no"; then
+ { echo "$as_me:$LINENO: checking for ZT_TONE_DTMF_BASE in zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for ZT_TONE_DTMF_BASE in zaptel/zaptel.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${ZAPTEL_DIR}" != "x"; then
+ ZAPTEL_INCLUDE="-I${ZAPTEL_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+int
+main ()
+{
+#if defined(ZT_TONE_DTMF_BASE)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_ZAPTEL=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_VERSION 140
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+ if test "x${PBX_ZAPTEL}" != "x1" -a "${USE_ZAPTEL}" != "no"; then
+ { echo "$as_me:$LINENO: checking for ZT_DIAL_OP_CANCEL in zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for ZT_DIAL_OP_CANCEL in zaptel/zaptel.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${ZAPTEL_DIR}" != "x"; then
+ ZAPTEL_INCLUDE="-I${ZAPTEL_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+int
+main ()
+{
+#if defined(ZT_DIAL_OP_CANCEL)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_ZAPTEL=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_VERSION 90
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+# Check for VLDTMF support
+
+ if test "x${PBX_ZAPTEL_VLDTMF}" != "x1" -a "${USE_ZAPTEL_VLDTMF}" != "no"; then
+ { echo "$as_me:$LINENO: checking for ZT_EVENT_REMOVED in zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for ZT_EVENT_REMOVED in zaptel/zaptel.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${ZAPTEL_VLDTMF_DIR}" != "x"; then
+ ZAPTEL_VLDTMF_INCLUDE="-I${ZAPTEL_VLDTMF_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_VLDTMF_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+int
+main ()
+{
+#if defined(ZT_EVENT_REMOVED)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_ZAPTEL_VLDTMF=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_VLDTMF 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_VLDTMF_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+# Check for echo canceler parameters support
+
+ if test "x${PBX_ZAPTEL_ECHOCANPARAMS}" != "x1" -a "${USE_ZAPTEL_ECHOCANPARAMS}" != "no"; then
+ { echo "$as_me:$LINENO: checking for ZT_ECHOCANCEL_PARAMS in zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for ZT_ECHOCANCEL_PARAMS in zaptel/zaptel.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${ZAPTEL_ECHOCANPARAMS_DIR}" != "x"; then
+ ZAPTEL_ECHOCANPARAMS_INCLUDE="-I${ZAPTEL_ECHOCANPARAMS_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_ECHOCANPARAMS_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+int
+main ()
+{
+#if defined(ZT_ECHOCANCEL_PARAMS)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_ZAPTEL_ECHOCANPARAMS=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_ECHOCANPARAMS 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_ECHOCANPARAMS_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+# Check for transcoder support
+
+ if test "x${PBX_ZAPTEL_TRANSCODE}" != "x1" -a "${USE_ZAPTEL_TRANSCODE}" != "no"; then
+ { echo "$as_me:$LINENO: checking for ZT_TCOP_ALLOCATE in zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for ZT_TCOP_ALLOCATE in zaptel/zaptel.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${ZAPTEL_TRANSCODE_DIR}" != "x"; then
+ ZAPTEL_TRANSCODE_INCLUDE="-I${ZAPTEL_TRANSCODE_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_TRANSCODE_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+int
+main ()
+{
+#if defined(ZT_TCOP_ALLOCATE)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_ZAPTEL_TRANSCODE=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_TRANSCODE 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_TRANSCODE_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+# Check for hwgain support
+
+ if test "x${PBX_ZAPTEL_HWGAIN}" != "x1" -a "${USE_ZAPTEL_HWGAIN}" != "no"; then
+ { echo "$as_me:$LINENO: checking for ZT_SET_HWGAIN in zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for ZT_SET_HWGAIN in zaptel/zaptel.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${ZAPTEL_HWGAIN_DIR}" != "x"; then
+ ZAPTEL_HWGAIN_INCLUDE="-I${ZAPTEL_HWGAIN_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_HWGAIN_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+int
+main ()
+{
+#if defined(ZT_SET_HWGAIN)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_ZAPTEL_HWGAIN=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_HWGAIN 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_HWGAIN_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+# Check for channel alarm support
+
+ if test "x${PBX_ZAPTEL_CHANALARMS}" != "x1" -a "${USE_ZAPTEL_CHANALARMS}" != "no"; then
+ { echo "$as_me:$LINENO: checking if \"size_t foo = sizeof(struct zt_params_v1)\" compiles using zaptel/zaptel.h" >&5
+echo $ECHO_N "checking if \"size_t foo = sizeof(struct zt_params_v1)\" compiles using zaptel/zaptel.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${ZAPTEL_CHANALARMS_DIR}" != "x"; then
+ ZAPTEL_CHANALARMS_INCLUDE="-I${ZAPTEL_CHANALARMS_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_CHANALARMS_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+int
+main ()
+{
+ size_t foo = sizeof(struct zt_params_v1);
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ PBX_ZAPTEL_CHANALARMS=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_CHANALARMS 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ZAPTEL_CHANALARMS_VERSION
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+# On FreeBSD, try old zaptel (0.80 or so) and pretend we have vldtmf
+case "${host_os}" in
+ freebsd*)
+
+if test "x${PBX_ZAPTEL}" != "x1" -a "${USE_ZAPTEL}" != "no"; then
+ pbxlibdir=""
+ # if --with-ZAPTEL=DIR has been specified, use it.
+ if test "x${ZAPTEL_DIR}" != "x"; then
+ if test -d ${ZAPTEL_DIR}/lib; then
+ pbxlibdir="-L${ZAPTEL_DIR}/lib"
+ else
+ pbxlibdir="-L${ZAPTEL_DIR}"
+ fi
+ fi
+ pbxfuncname=""
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ZAPTEL_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_zaptel_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lzaptel" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lzaptel... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lzaptel ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ZAPTEL_FOUND=yes
+else
+ AST_ZAPTEL_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ZAPTEL_FOUND}" = "yes"; then
+ ZAPTEL_LIB="${pbxlibdir} -lzaptel "
+ # if --with-ZAPTEL=DIR has been specified, use it.
+ if test "x${ZAPTEL_DIR}" != "x"; then
+ ZAPTEL_INCLUDE="-I${ZAPTEL_DIR}/include"
+ fi
+ ZAPTEL_INCLUDE="${ZAPTEL_INCLUDE} "
+ if test "xzaptel.h" = "x" ; then # no header, assume found
+ ZAPTEL_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_INCLUDE} "
+ if test "${ac_cv_header_zaptel_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for zaptel.h" >&5
+echo $ECHO_N "checking for zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking zaptel.h usability" >&5
+echo $ECHO_N "checking zaptel.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <zaptel.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking zaptel.h presence" >&5
+echo $ECHO_N "checking zaptel.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: zaptel.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: zaptel.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: zaptel.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: zaptel.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: zaptel.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: zaptel.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: zaptel.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for zaptel.h" >&5
+echo $ECHO_N "checking for zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_zaptel_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_h" >&6; }
+
+fi
+if test $ac_cv_header_zaptel_h = yes; then
+ ZAPTEL_HEADER_FOUND=1
+else
+ ZAPTEL_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ZAPTEL_HEADER_FOUND}" = "x0" ; then
+ ZAPTEL_LIB=""
+ ZAPTEL_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ZAPTEL_LIB=""
+ fi
+ PBX_ZAPTEL=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZAPTEL 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZAPTEL_VERSION 80
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ZAPTEL_VLDTMF}" != "x1" -a "${USE_ZAPTEL_VLDTMF}" != "no"; then
+ pbxlibdir=""
+ # if --with-ZAPTEL_VLDTMF=DIR has been specified, use it.
+ if test "x${ZAPTEL_VLDTMF_DIR}" != "x"; then
+ if test -d ${ZAPTEL_VLDTMF_DIR}/lib; then
+ pbxlibdir="-L${ZAPTEL_VLDTMF_DIR}/lib"
+ else
+ pbxlibdir="-L${ZAPTEL_VLDTMF_DIR}"
+ fi
+ fi
+ pbxfuncname=""
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ZAPTEL_VLDTMF_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_zaptel_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lzaptel" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lzaptel... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lzaptel ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ZAPTEL_VLDTMF_FOUND=yes
+else
+ AST_ZAPTEL_VLDTMF_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ZAPTEL_VLDTMF_FOUND}" = "yes"; then
+ ZAPTEL_VLDTMF_LIB="${pbxlibdir} -lzaptel "
+ # if --with-ZAPTEL_VLDTMF=DIR has been specified, use it.
+ if test "x${ZAPTEL_VLDTMF_DIR}" != "x"; then
+ ZAPTEL_VLDTMF_INCLUDE="-I${ZAPTEL_VLDTMF_DIR}/include"
+ fi
+ ZAPTEL_VLDTMF_INCLUDE="${ZAPTEL_VLDTMF_INCLUDE} "
+ if test "xzaptel/zaptel.h" = "x" ; then # no header, assume found
+ ZAPTEL_VLDTMF_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_VLDTMF_INCLUDE} "
+ if test "${ac_cv_header_zaptel_zaptel_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for zaptel/zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_zaptel_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking zaptel/zaptel.h usability" >&5
+echo $ECHO_N "checking zaptel/zaptel.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <zaptel/zaptel.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking zaptel/zaptel.h presence" >&5
+echo $ECHO_N "checking zaptel/zaptel.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel/zaptel.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel/zaptel.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: zaptel/zaptel.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for zaptel/zaptel.h" >&5
+echo $ECHO_N "checking for zaptel/zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_zaptel_zaptel_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_zaptel_h" >&6; }
+
+fi
+if test $ac_cv_header_zaptel_zaptel_h = yes; then
+ ZAPTEL_VLDTMF_HEADER_FOUND=1
+else
+ ZAPTEL_VLDTMF_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ZAPTEL_VLDTMF_HEADER_FOUND}" = "x0" ; then
+ ZAPTEL_VLDTMF_LIB=""
+ ZAPTEL_VLDTMF_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ZAPTEL_VLDTMF_LIB=""
+ fi
+ PBX_ZAPTEL_VLDTMF=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZAPTEL_VLDTMF 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZAPTEL_VLDTMF_VERSION 90
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_ZAPTEL_VLDTMF}" != "x1" -a "${USE_ZAPTEL_VLDTMF}" != "no"; then
+ pbxlibdir=""
+ # if --with-ZAPTEL_VLDTMF=DIR has been specified, use it.
+ if test "x${ZAPTEL_VLDTMF_DIR}" != "x"; then
+ if test -d ${ZAPTEL_VLDTMF_DIR}/lib; then
+ pbxlibdir="-L${ZAPTEL_VLDTMF_DIR}/lib"
+ else
+ pbxlibdir="-L${ZAPTEL_VLDTMF_DIR}"
+ fi
+ fi
+ pbxfuncname=""
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_ZAPTEL_VLDTMF_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_zaptel_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lzaptel" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lzaptel... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lzaptel ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_ZAPTEL_VLDTMF_FOUND=yes
+else
+ AST_ZAPTEL_VLDTMF_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_ZAPTEL_VLDTMF_FOUND}" = "yes"; then
+ ZAPTEL_VLDTMF_LIB="${pbxlibdir} -lzaptel "
+ # if --with-ZAPTEL_VLDTMF=DIR has been specified, use it.
+ if test "x${ZAPTEL_VLDTMF_DIR}" != "x"; then
+ ZAPTEL_VLDTMF_INCLUDE="-I${ZAPTEL_VLDTMF_DIR}/include"
+ fi
+ ZAPTEL_VLDTMF_INCLUDE="${ZAPTEL_VLDTMF_INCLUDE} "
+ if test "xzaptel.h" = "x" ; then # no header, assume found
+ ZAPTEL_VLDTMF_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${ZAPTEL_VLDTMF_INCLUDE} "
+ if test "${ac_cv_header_zaptel_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for zaptel.h" >&5
+echo $ECHO_N "checking for zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking zaptel.h usability" >&5
+echo $ECHO_N "checking zaptel.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <zaptel.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking zaptel.h presence" >&5
+echo $ECHO_N "checking zaptel.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: zaptel.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: zaptel.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: zaptel.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: zaptel.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: zaptel.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: zaptel.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: zaptel.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for zaptel.h" >&5
+echo $ECHO_N "checking for zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_zaptel_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_h" >&6; }
+
+fi
+if test $ac_cv_header_zaptel_h = yes; then
+ ZAPTEL_VLDTMF_HEADER_FOUND=1
+else
+ ZAPTEL_VLDTMF_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${ZAPTEL_VLDTMF_HEADER_FOUND}" = "x0" ; then
+ ZAPTEL_VLDTMF_LIB=""
+ ZAPTEL_VLDTMF_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ ZAPTEL_VLDTMF_LIB=""
+ fi
+ PBX_ZAPTEL_VLDTMF=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZAPTEL_VLDTMF 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ZAPTEL_VLDTMF_VERSION 80
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+ # other case, old tonezone (0.80)
+
+if test "x${PBX_TONEZONE}" != "x1" -a "${USE_TONEZONE}" != "no"; then
+ pbxlibdir=""
+ # if --with-TONEZONE=DIR has been specified, use it.
+ if test "x${TONEZONE_DIR}" != "x"; then
+ if test -d ${TONEZONE_DIR}/lib; then
+ pbxlibdir="-L${TONEZONE_DIR}/lib"
+ else
+ pbxlibdir="-L${TONEZONE_DIR}"
+ fi
+ fi
+ pbxfuncname="tone_zone_find"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_TONEZONE_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_tonezone_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -ltonezone" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -ltonezone... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltonezone ${pbxlibdir} ${tonezone_extra} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_TONEZONE_FOUND=yes
+else
+ AST_TONEZONE_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_TONEZONE_FOUND}" = "yes"; then
+ TONEZONE_LIB="${pbxlibdir} -ltonezone ${tonezone_extra}"
+ # if --with-TONEZONE=DIR has been specified, use it.
+ if test "x${TONEZONE_DIR}" != "x"; then
+ TONEZONE_INCLUDE="-I${TONEZONE_DIR}/include"
+ fi
+ TONEZONE_INCLUDE="${TONEZONE_INCLUDE} "
+ if test "xzaptel.h" = "x" ; then # no header, assume found
+ TONEZONE_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${TONEZONE_INCLUDE} "
+ if test "${ac_cv_header_zaptel_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for zaptel.h" >&5
+echo $ECHO_N "checking for zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking zaptel.h usability" >&5
+echo $ECHO_N "checking zaptel.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <zaptel.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking zaptel.h presence" >&5
+echo $ECHO_N "checking zaptel.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <zaptel.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: zaptel.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: zaptel.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: zaptel.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: zaptel.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: zaptel.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: zaptel.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: zaptel.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: zaptel.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: zaptel.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for zaptel.h" >&5
+echo $ECHO_N "checking for zaptel.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_zaptel_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_zaptel_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_zaptel_h" >&5
+echo "${ECHO_T}$ac_cv_header_zaptel_h" >&6; }
+
+fi
+if test $ac_cv_header_zaptel_h = yes; then
+ TONEZONE_HEADER_FOUND=1
+else
+ TONEZONE_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${TONEZONE_HEADER_FOUND}" = "x0" ; then
+ TONEZONE_LIB=""
+ TONEZONE_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ TONEZONE_LIB=""
+ fi
+ PBX_TONEZONE=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TONEZONE 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_TONEZONE_VERSION 80
+_ACEOF
+
+ fi
+ fi
+fi
+
+ ;;
+esac
+
+EDITLINE_LIB=""
+if test "x$TERMCAP_LIB" != "x" ; then
+ EDITLINE_LIB="$TERMCAP_LIB"
+elif test "x$TINFO_LIB" != "x" ; then
+ EDITLINE_LIB="$TINFO_LIB"
+elif test "x$CURSES_LIB" != "x" ; then
+ EDITLINE_LIB="$CURSES_LIB"
+elif test "x$NCURSES_LIB" != "x" ; then
+ EDITLINE_LIB="$NCURSES_LIB"
+else
+ { { echo "$as_me:$LINENO: error: *** termcap support not found" >&5
+echo "$as_me: error: *** termcap support not found" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+if test "${ac_cv_header_h323_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for h323.h" >&5
+echo $ECHO_N "checking for h323.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_h323_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_h323_h" >&5
+echo "${ECHO_T}$ac_cv_header_h323_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking h323.h usability" >&5
+echo $ECHO_N "checking h323.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <h323.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking h323.h presence" >&5
+echo $ECHO_N "checking h323.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <h323.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: h323.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: h323.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: h323.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: h323.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: h323.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: h323.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: h323.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: h323.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: h323.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: h323.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: h323.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: h323.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: h323.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: h323.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: h323.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: h323.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for h323.h" >&5
+echo $ECHO_N "checking for h323.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_h323_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_h323_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_h323_h" >&5
+echo "${ECHO_T}$ac_cv_header_h323_h" >&6; }
+
+fi
+if test $ac_cv_header_h323_h = yes; then
+ PBX_H323=1
+else
+ PBX_H323=0
+fi
+
+
+
+
+if test "${ac_cv_header_linux_compiler_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for linux/compiler.h" >&5
+echo $ECHO_N "checking for linux/compiler.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_compiler_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_compiler_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_compiler_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking linux/compiler.h usability" >&5
+echo $ECHO_N "checking linux/compiler.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <linux/compiler.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking linux/compiler.h presence" >&5
+echo $ECHO_N "checking linux/compiler.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <linux/compiler.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: linux/compiler.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: linux/compiler.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: linux/compiler.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: linux/compiler.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: linux/compiler.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: linux/compiler.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: linux/compiler.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/compiler.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: linux/compiler.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for linux/compiler.h" >&5
+echo $ECHO_N "checking for linux/compiler.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_compiler_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_linux_compiler_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_compiler_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_compiler_h" >&6; }
+
+fi
+if test $ac_cv_header_linux_compiler_h = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LINUX_COMPILER_H 1
+_ACEOF
+
+fi
+
+
+
+{ echo "$as_me:$LINENO: checking for linux/ixjuser.h" >&5
+echo $ECHO_N "checking for linux/ixjuser.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_ixjuser_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+ #include <linux/version.h>
+ #ifdef HAVE_LINUX_COMPILER_H
+ #include <linux/compiler.h>
+ #endif
+
+
+#include <linux/ixjuser.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_linux_ixjuser_h=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_linux_ixjuser_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_ixjuser_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_ixjuser_h" >&6; }
+if test $ac_cv_header_linux_ixjuser_h = yes; then
+ PBX_IXJUSER=1
+else
+ PBX_IXJUSER=0
+fi
+
+
+
+
+
+ if test "x${PBX_SDL}" != "x1" -a "${USE_SDL}" != "no"; then
+ PBX_SDL=0
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}sdl-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}sdl-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CONFIG_SDL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CONFIG_SDL"; then
+ ac_cv_prog_CONFIG_SDL="$CONFIG_SDL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CONFIG_SDL="${ac_tool_prefix}sdl-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CONFIG_SDL=$ac_cv_prog_CONFIG_SDL
+if test -n "$CONFIG_SDL"; then
+ { echo "$as_me:$LINENO: result: $CONFIG_SDL" >&5
+echo "${ECHO_T}$CONFIG_SDL" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CONFIG_SDL"; then
+ ac_ct_CONFIG_SDL=$CONFIG_SDL
+ # Extract the first word of "sdl-config", so it can be a program name with args.
+set dummy sdl-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CONFIG_SDL+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CONFIG_SDL"; then
+ ac_cv_prog_ac_ct_CONFIG_SDL="$ac_ct_CONFIG_SDL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CONFIG_SDL="sdl-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CONFIG_SDL=$ac_cv_prog_ac_ct_CONFIG_SDL
+if test -n "$ac_ct_CONFIG_SDL"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CONFIG_SDL" >&5
+echo "${ECHO_T}$ac_ct_CONFIG_SDL" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CONFIG_SDL" = x; then
+ CONFIG_SDL="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CONFIG_SDL=$ac_ct_CONFIG_SDL
+ fi
+else
+ CONFIG_SDL="$ac_cv_prog_CONFIG_SDL"
+fi
+
+ if test ! "x${CONFIG_SDL}" = xNo; then
+ if test x"" = x ; then A=--cflags ; else A="" ; fi
+ SDL_INCLUDE=$(${CONFIG_SDL} $A)
+ if test x"" = x ; then A=--libs ; else A="" ; fi
+ SDL_LIB=$(${CONFIG_SDL} $A)
+ if test x"" != x ; then
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${SDL_DIR}" != "x"; then
+ SDL_INCLUDE="-I${SDL_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${SDL_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+ ;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ PBX_SDL=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SDL 1
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ else
+ PBX_SDL=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SDL 1
+_ACEOF
+
+ fi
+ fi
+ fi
+
+
+if test "x${PBX_SDL_IMAGE}" != "x1" -a "${USE_SDL_IMAGE}" != "no"; then
+ pbxlibdir=""
+ # if --with-SDL_IMAGE=DIR has been specified, use it.
+ if test "x${SDL_IMAGE_DIR}" != "x"; then
+ if test -d ${SDL_IMAGE_DIR}/lib; then
+ pbxlibdir="-L${SDL_IMAGE_DIR}/lib"
+ else
+ pbxlibdir="-L${SDL_IMAGE_DIR}"
+ fi
+ fi
+ pbxfuncname="IMG_Load"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_SDL_IMAGE_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_SDL_image_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lSDL_image" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lSDL_image... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lSDL_image ${pbxlibdir} ${SDL_LIB} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_SDL_IMAGE_FOUND=yes
+else
+ AST_SDL_IMAGE_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_SDL_IMAGE_FOUND}" = "yes"; then
+ SDL_IMAGE_LIB="${pbxlibdir} -lSDL_image ${SDL_LIB}"
+ # if --with-SDL_IMAGE=DIR has been specified, use it.
+ if test "x${SDL_IMAGE_DIR}" != "x"; then
+ SDL_IMAGE_INCLUDE="-I${SDL_IMAGE_DIR}/include"
+ fi
+ SDL_IMAGE_INCLUDE="${SDL_IMAGE_INCLUDE} ${SDL_INCLUDE}"
+ if test "xSDL_image.h" = "x" ; then # no header, assume found
+ SDL_IMAGE_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${SDL_IMAGE_INCLUDE} ${SDL_INCLUDE}"
+ if test "${ac_cv_header_SDL_image_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for SDL_image.h" >&5
+echo $ECHO_N "checking for SDL_image.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_SDL_image_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_SDL_image_h" >&5
+echo "${ECHO_T}$ac_cv_header_SDL_image_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking SDL_image.h usability" >&5
+echo $ECHO_N "checking SDL_image.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <SDL_image.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking SDL_image.h presence" >&5
+echo $ECHO_N "checking SDL_image.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <SDL_image.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: SDL_image.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: SDL_image.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: SDL_image.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: SDL_image.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: SDL_image.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: SDL_image.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: SDL_image.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: SDL_image.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: SDL_image.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for SDL_image.h" >&5
+echo $ECHO_N "checking for SDL_image.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_SDL_image_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_SDL_image_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_SDL_image_h" >&5
+echo "${ECHO_T}$ac_cv_header_SDL_image_h" >&6; }
+
+fi
+if test $ac_cv_header_SDL_image_h = yes; then
+ SDL_IMAGE_HEADER_FOUND=1
+else
+ SDL_IMAGE_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${SDL_IMAGE_HEADER_FOUND}" = "x0" ; then
+ SDL_IMAGE_LIB=""
+ SDL_IMAGE_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ SDL_IMAGE_LIB=""
+ fi
+ PBX_SDL_IMAGE=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SDL_IMAGE 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SDL_IMAGE_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_FFMPEG}" != "x1" -a "${USE_FFMPEG}" != "no"; then
+ pbxlibdir=""
+ # if --with-FFMPEG=DIR has been specified, use it.
+ if test "x${FFMPEG_DIR}" != "x"; then
+ if test -d ${FFMPEG_DIR}/lib; then
+ pbxlibdir="-L${FFMPEG_DIR}/lib"
+ else
+ pbxlibdir="-L${FFMPEG_DIR}"
+ fi
+ fi
+ pbxfuncname="sws_getContext"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_FFMPEG_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_avcodec_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lavcodec" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lavcodec... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lavcodec ${pbxlibdir} -lpthread -lz -lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_FFMPEG_FOUND=yes
+else
+ AST_FFMPEG_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_FFMPEG_FOUND}" = "yes"; then
+ FFMPEG_LIB="${pbxlibdir} -lavcodec -lpthread -lz -lm"
+ # if --with-FFMPEG=DIR has been specified, use it.
+ if test "x${FFMPEG_DIR}" != "x"; then
+ FFMPEG_INCLUDE="-I${FFMPEG_DIR}/include"
+ fi
+ FFMPEG_INCLUDE="${FFMPEG_INCLUDE} "
+ if test "xffmpeg/avcodec.h" = "x" ; then # no header, assume found
+ FFMPEG_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${FFMPEG_INCLUDE} "
+ if test "${ac_cv_header_ffmpeg_avcodec_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for ffmpeg/avcodec.h" >&5
+echo $ECHO_N "checking for ffmpeg/avcodec.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_ffmpeg_avcodec_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_ffmpeg_avcodec_h" >&5
+echo "${ECHO_T}$ac_cv_header_ffmpeg_avcodec_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking ffmpeg/avcodec.h usability" >&5
+echo $ECHO_N "checking ffmpeg/avcodec.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <ffmpeg/avcodec.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking ffmpeg/avcodec.h presence" >&5
+echo $ECHO_N "checking ffmpeg/avcodec.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ffmpeg/avcodec.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: ffmpeg/avcodec.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: ffmpeg/avcodec.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for ffmpeg/avcodec.h" >&5
+echo $ECHO_N "checking for ffmpeg/avcodec.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_ffmpeg_avcodec_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_ffmpeg_avcodec_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_ffmpeg_avcodec_h" >&5
+echo "${ECHO_T}$ac_cv_header_ffmpeg_avcodec_h" >&6; }
+
+fi
+if test $ac_cv_header_ffmpeg_avcodec_h = yes; then
+ FFMPEG_HEADER_FOUND=1
+else
+ FFMPEG_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${FFMPEG_HEADER_FOUND}" = "x0" ; then
+ FFMPEG_LIB=""
+ FFMPEG_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ FFMPEG_LIB=""
+ fi
+ PBX_FFMPEG=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FFMPEG 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_FFMPEG_VERSION
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+# possible places for video4linux version 1
+if test "${ac_cv_header_linux_videodev_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for linux/videodev.h" >&5
+echo $ECHO_N "checking for linux/videodev.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_videodev_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_videodev_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_videodev_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking linux/videodev.h usability" >&5
+echo $ECHO_N "checking linux/videodev.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <linux/videodev.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking linux/videodev.h presence" >&5
+echo $ECHO_N "checking linux/videodev.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <linux/videodev.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: linux/videodev.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: linux/videodev.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: linux/videodev.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: linux/videodev.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: linux/videodev.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: linux/videodev.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: linux/videodev.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: linux/videodev.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: linux/videodev.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for linux/videodev.h" >&5
+echo $ECHO_N "checking for linux/videodev.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_videodev_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_linux_videodev_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_videodev_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_videodev_h" >&6; }
+
+fi
+if test $ac_cv_header_linux_videodev_h = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_VIDEODEV_H 1
+_ACEOF
+
+fi
+
+
+
+# possible places for X11
+
+if test "x${PBX_X11}" != "x1" -a "${USE_X11}" != "no"; then
+ pbxlibdir=""
+ # if --with-X11=DIR has been specified, use it.
+ if test "x${X11_DIR}" != "x"; then
+ if test -d ${X11_DIR}/lib; then
+ pbxlibdir="-L${X11_DIR}/lib"
+ else
+ pbxlibdir="-L${X11_DIR}"
+ fi
+ fi
+ pbxfuncname="XOpenDisplay"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_X11_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_X11_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lX11" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lX11... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lX11 ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_X11_FOUND=yes
+else
+ AST_X11_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_X11_FOUND}" = "yes"; then
+ X11_LIB="${pbxlibdir} -lX11 "
+ # if --with-X11=DIR has been specified, use it.
+ if test "x${X11_DIR}" != "x"; then
+ X11_INCLUDE="-I${X11_DIR}/include"
+ fi
+ X11_INCLUDE="${X11_INCLUDE} "
+ if test "xX11/Xlib.h" = "x" ; then # no header, assume found
+ X11_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${X11_INCLUDE} "
+ if test "${ac_cv_header_X11_Xlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for X11/Xlib.h" >&5
+echo $ECHO_N "checking for X11/Xlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_X11_Xlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_X11_Xlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_X11_Xlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking X11/Xlib.h usability" >&5
+echo $ECHO_N "checking X11/Xlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <X11/Xlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking X11/Xlib.h presence" >&5
+echo $ECHO_N "checking X11/Xlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <X11/Xlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: X11/Xlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: X11/Xlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: X11/Xlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: X11/Xlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: X11/Xlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: X11/Xlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: X11/Xlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: X11/Xlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for X11/Xlib.h" >&5
+echo $ECHO_N "checking for X11/Xlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_X11_Xlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_X11_Xlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_X11_Xlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_X11_Xlib_h" >&6; }
+
+fi
+if test $ac_cv_header_X11_Xlib_h = yes; then
+ X11_HEADER_FOUND=1
+else
+ X11_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${X11_HEADER_FOUND}" = "x0" ; then
+ X11_LIB=""
+ X11_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ X11_LIB=""
+ fi
+ PBX_X11=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_X11 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_X11_VERSION standard_path
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+if test "x${PBX_X11}" != "x1" -a "${USE_X11}" != "no"; then
+ pbxlibdir=""
+ # if --with-X11=DIR has been specified, use it.
+ if test "x${X11_DIR}" != "x"; then
+ if test -d ${X11_DIR}/lib; then
+ pbxlibdir="-L${X11_DIR}/lib"
+ else
+ pbxlibdir="-L${X11_DIR}"
+ fi
+ fi
+ pbxfuncname="XOpenDisplay"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_X11_FOUND=yes
+ else
+ as_ac_Lib=`echo "ac_cv_lib_X11_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lX11" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lX11... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lX11 ${pbxlibdir} $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_Lib=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+ AST_X11_FOUND=yes
+else
+ AST_X11_FOUND=no
+fi
+
+ fi
+
+ # now check for the header.
+ if test "${AST_X11_FOUND}" = "yes"; then
+ X11_LIB="${pbxlibdir} -lX11 "
+ # if --with-X11=DIR has been specified, use it.
+ if test "x${X11_DIR}" != "x"; then
+ X11_INCLUDE="-I${X11_DIR}/include"
+ fi
+ X11_INCLUDE="${X11_INCLUDE} -I/usr/X11R6/include"
+ if test "xX11/Xlib.h" = "x" ; then # no header, assume found
+ X11_HEADER_FOUND="1"
+ else # check for the header
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${X11_INCLUDE} -I/usr/X11R6/include"
+ if test "${ac_cv_header_X11_Xlib_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for X11/Xlib.h" >&5
+echo $ECHO_N "checking for X11/Xlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_X11_Xlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_X11_Xlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_X11_Xlib_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking X11/Xlib.h usability" >&5
+echo $ECHO_N "checking X11/Xlib.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <X11/Xlib.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking X11/Xlib.h presence" >&5
+echo $ECHO_N "checking X11/Xlib.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <X11/Xlib.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: X11/Xlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: X11/Xlib.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: X11/Xlib.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: X11/Xlib.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: X11/Xlib.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: X11/Xlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: X11/Xlib.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: X11/Xlib.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: X11/Xlib.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for X11/Xlib.h" >&5
+echo $ECHO_N "checking for X11/Xlib.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_X11_Xlib_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_X11_Xlib_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_X11_Xlib_h" >&5
+echo "${ECHO_T}$ac_cv_header_X11_Xlib_h" >&6; }
+
+fi
+if test $ac_cv_header_X11_Xlib_h = yes; then
+ X11_HEADER_FOUND=1
+else
+ X11_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${saved_cppflags}"
+ fi
+ if test "x${X11_HEADER_FOUND}" = "x0" ; then
+ X11_LIB=""
+ X11_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ X11_LIB=""
+ fi
+ PBX_X11=1
+ # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_X11 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_X11_VERSION X11R6
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+PBX_GTK=0
+
+ if test "x${PBX_GTK}" != "x1" -a "${USE_GTK}" != "no"; then
+ PBX_GTK=0
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gtk-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gtk-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CONFIG_GTK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CONFIG_GTK"; then
+ ac_cv_prog_CONFIG_GTK="$CONFIG_GTK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CONFIG_GTK="${ac_tool_prefix}gtk-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CONFIG_GTK=$ac_cv_prog_CONFIG_GTK
+if test -n "$CONFIG_GTK"; then
+ { echo "$as_me:$LINENO: result: $CONFIG_GTK" >&5
+echo "${ECHO_T}$CONFIG_GTK" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CONFIG_GTK"; then
+ ac_ct_CONFIG_GTK=$CONFIG_GTK
+ # Extract the first word of "gtk-config", so it can be a program name with args.
+set dummy gtk-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CONFIG_GTK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CONFIG_GTK"; then
+ ac_cv_prog_ac_ct_CONFIG_GTK="$ac_ct_CONFIG_GTK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CONFIG_GTK="gtk-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CONFIG_GTK=$ac_cv_prog_ac_ct_CONFIG_GTK
+if test -n "$ac_ct_CONFIG_GTK"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CONFIG_GTK" >&5
+echo "${ECHO_T}$ac_ct_CONFIG_GTK" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CONFIG_GTK" = x; then
+ CONFIG_GTK="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CONFIG_GTK=$ac_ct_CONFIG_GTK
+ fi
+else
+ CONFIG_GTK="$ac_cv_prog_CONFIG_GTK"
+fi
+
+ if test ! "x${CONFIG_GTK}" = xNo; then
+ if test x"--cflags gthread" = x ; then A=--cflags ; else A="--cflags gthread" ; fi
+ GTK_INCLUDE=$(${CONFIG_GTK} $A)
+ if test x"--libs gthread" = x ; then A=--libs ; else A="--libs gthread" ; fi
+ GTK_LIB=$(${CONFIG_GTK} $A)
+ if test x"" != x ; then
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${GTK_DIR}" != "x"; then
+ GTK_INCLUDE="-I${GTK_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${GTK_INCLUDE}"
+
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+ ;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ PBX_GTK=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GTK 1
+_ACEOF
+
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ else
+ PBX_GTK=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GTK 1
+_ACEOF
+
+ fi
+ fi
+ fi
+
+
+PBX_GTK2=0
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_PKGCONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$PKGCONFIG"; then
+ ac_cv_prog_PKGCONFIG="$PKGCONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_PKGCONFIG="${ac_tool_prefix}pkg-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+PKGCONFIG=$ac_cv_prog_PKGCONFIG
+if test -n "$PKGCONFIG"; then
+ { echo "$as_me:$LINENO: result: $PKGCONFIG" >&5
+echo "${ECHO_T}$PKGCONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_PKGCONFIG"; then
+ ac_ct_PKGCONFIG=$PKGCONFIG
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_PKGCONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_PKGCONFIG"; then
+ ac_cv_prog_ac_ct_PKGCONFIG="$ac_ct_PKGCONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_PKGCONFIG="pkg-config"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_PKGCONFIG=$ac_cv_prog_ac_ct_PKGCONFIG
+if test -n "$ac_ct_PKGCONFIG"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_PKGCONFIG" >&5
+echo "${ECHO_T}$ac_ct_PKGCONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_PKGCONFIG" = x; then
+ PKGCONFIG="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ PKGCONFIG=$ac_ct_PKGCONFIG
+ fi
+else
+ PKGCONFIG="$ac_cv_prog_PKGCONFIG"
+fi
+
+if test ! "x${PKGCONFIG}" = xNo; then
+ GTK2_INCLUDE=$(${PKGCONFIG} gtk+-2.0 --cflags 2>/dev/null)
+ GTK2_LIB=$(${PKGCONFIG} gtk+-2.0 --libs)
+ PBX_GTK2=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GTK2 1
+_ACEOF
+
+fi
+
+
+
+
+if test "${USE_CURL}" != "no"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}curl-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}curl-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_CURL_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $CURL_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_CURL_CONFIG="$CURL_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_CURL_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+CURL_CONFIG=$ac_cv_path_CURL_CONFIG
+if test -n "$CURL_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $CURL_CONFIG" >&5
+echo "${ECHO_T}$CURL_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_CURL_CONFIG"; then
+ ac_pt_CURL_CONFIG=$CURL_CONFIG
+ # Extract the first word of "curl-config", so it can be a program name with args.
+set dummy curl-config; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ac_pt_CURL_CONFIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ac_pt_CURL_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_CURL_CONFIG="$ac_pt_CURL_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_CURL_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_CURL_CONFIG=$ac_cv_path_ac_pt_CURL_CONFIG
+if test -n "$ac_pt_CURL_CONFIG"; then
+ { echo "$as_me:$LINENO: result: $ac_pt_CURL_CONFIG" >&5
+echo "${ECHO_T}$ac_pt_CURL_CONFIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_pt_CURL_CONFIG" = x; then
+ CURL_CONFIG="No"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CURL_CONFIG=$ac_pt_CURL_CONFIG
+ fi
+else
+ CURL_CONFIG="$ac_cv_path_CURL_CONFIG"
+fi
+
+ if test ! x"${CURL_CONFIG}" = xNo; then
+ # check for version
+ if test $(printf "%d" 0x$(${CURL_CONFIG} --vernum)) -ge $(printf "%d" 0x070907); then
+ CURL_INCLUDE=$(${CURL_CONFIG} --cflags)
+ CURL_LIB=$(${CURL_CONFIG} --libs)
+
+ { echo "$as_me:$LINENO: checking for curl_version() in curl/curl.h" >&5
+echo $ECHO_N "checking for curl_version() in curl/curl.h... $ECHO_C" >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CURL_INCLUDE}"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <curl/curl.h>
+int
+main ()
+{
+curl_version();
+ ;
+ return 0;
+}
+
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ ac_cv_curl_h="yes"
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ ac_cv_curl_h="no"
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ if test "${ac_cv_curl_h}" = "yes"; then
+ PBX_CURL=1
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CURL 1
+_ACEOF
+
+ fi
+ fi
+ fi
+fi
+
+ac_config_files="$ac_config_files build_tools/menuselect-deps makeopts channels/h323/Makefile"
+
+
+ { echo "$as_me:$LINENO: checking for mandatory modules: ${ac_mandatory_list}" >&5
+echo $ECHO_N "checking for mandatory modules: ${ac_mandatory_list}... $ECHO_C" >&6; }
+ err=0;
+ for i in ${ac_mandatory_list}; do
+ eval "a=\${PBX_$i}"
+ if test "x${a}" = "x1" ; then continue; fi
+ if test ${err} = "0" ; then { echo "$as_me:$LINENO: result: fail" >&5
+echo "${ECHO_T}fail" >&6; } ; fi
+ { echo "$as_me:$LINENO: result: " >&5
+echo "${ECHO_T}" >&6; }
+ eval "a=\${${i}_OPTION}"
+ { echo "$as_me:$LINENO: ***" >&5
+echo "$as_me: ***" >&6;}
+ { echo "$as_me:$LINENO: *** The $i installation appears to be missing or broken." >&5
+echo "$as_me: *** The $i installation appears to be missing or broken." >&6;}
+ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5
+echo "$as_me: *** Either correct the installation, or run configure" >&6;}
+ { echo "$as_me:$LINENO: *** including --without-${a}." >&5
+echo "$as_me: *** including --without-${a}." >&6;}
+ err=1
+ done
+ if test $err = 1 ; then exit 1; fi
+ { echo "$as_me:$LINENO: result: ok" >&5
+echo "${ECHO_T}ok" >&6; }
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { echo "$as_me:$LINENO: updating cache $cache_file" >&5
+echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by asterisk $as_me 1.4, which was
+generated by GNU Autoconf 2.61. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+asterisk config.status 1.4
+configured by $0, generated by GNU Autoconf 2.61,
+ with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2006 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ echo "$ac_cs_version"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ { echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ CONFIG_SHELL=$SHELL
+ export CONFIG_SHELL
+ exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "include/asterisk/autoconfig.h") CONFIG_HEADERS="$CONFIG_HEADERS include/asterisk/autoconfig.h" ;;
+ "build_tools/menuselect-deps") CONFIG_FILES="$CONFIG_FILES build_tools/menuselect-deps" ;;
+ "makeopts") CONFIG_FILES="$CONFIG_FILES makeopts" ;;
+ "channels/h323/Makefile") CONFIG_FILES="$CONFIG_FILES channels/h323/Makefile" ;;
+
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp=
+ trap 'exit_status=$?
+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+#
+# Set up the sed scripts for CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "$CONFIG_FILES"; then
+
+_ACEOF
+
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ cat >conf$$subs.sed <<_ACEOF
+SHELL!$SHELL$ac_delim
+PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim
+PACKAGE_NAME!$PACKAGE_NAME$ac_delim
+PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim
+PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim
+PACKAGE_STRING!$PACKAGE_STRING$ac_delim
+PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim
+exec_prefix!$exec_prefix$ac_delim
+prefix!$prefix$ac_delim
+program_transform_name!$program_transform_name$ac_delim
+bindir!$bindir$ac_delim
+sbindir!$sbindir$ac_delim
+libexecdir!$libexecdir$ac_delim
+datarootdir!$datarootdir$ac_delim
+datadir!$datadir$ac_delim
+sysconfdir!$sysconfdir$ac_delim
+sharedstatedir!$sharedstatedir$ac_delim
+localstatedir!$localstatedir$ac_delim
+includedir!$includedir$ac_delim
+oldincludedir!$oldincludedir$ac_delim
+docdir!$docdir$ac_delim
+infodir!$infodir$ac_delim
+htmldir!$htmldir$ac_delim
+dvidir!$dvidir$ac_delim
+pdfdir!$pdfdir$ac_delim
+psdir!$psdir$ac_delim
+libdir!$libdir$ac_delim
+localedir!$localedir$ac_delim
+mandir!$mandir$ac_delim
+DEFS!$DEFS$ac_delim
+ECHO_C!$ECHO_C$ac_delim
+ECHO_N!$ECHO_N$ac_delim
+ECHO_T!$ECHO_T$ac_delim
+LIBS!$LIBS$ac_delim
+build_alias!$build_alias$ac_delim
+host_alias!$host_alias$ac_delim
+target_alias!$target_alias$ac_delim
+build!$build$ac_delim
+build_cpu!$build_cpu$ac_delim
+build_vendor!$build_vendor$ac_delim
+build_os!$build_os$ac_delim
+host!$host$ac_delim
+host_cpu!$host_cpu$ac_delim
+host_vendor!$host_vendor$ac_delim
+host_os!$host_os$ac_delim
+CC!$CC$ac_delim
+CFLAGS!$CFLAGS$ac_delim
+LDFLAGS!$LDFLAGS$ac_delim
+CPPFLAGS!$CPPFLAGS$ac_delim
+ac_ct_CC!$ac_ct_CC$ac_delim
+EXEEXT!$EXEEXT$ac_delim
+OBJEXT!$OBJEXT$ac_delim
+CPP!$CPP$ac_delim
+GREP!$GREP$ac_delim
+EGREP!$EGREP$ac_delim
+BUILD_PLATFORM!$BUILD_PLATFORM$ac_delim
+BUILD_CPU!$BUILD_CPU$ac_delim
+BUILD_VENDOR!$BUILD_VENDOR$ac_delim
+BUILD_OS!$BUILD_OS$ac_delim
+HOST_PLATFORM!$HOST_PLATFORM$ac_delim
+HOST_CPU!$HOST_CPU$ac_delim
+HOST_VENDOR!$HOST_VENDOR$ac_delim
+HOST_OS!$HOST_OS$ac_delim
+OSARCH!$OSARCH$ac_delim
+WINARCH!$WINARCH$ac_delim
+UNAME!$UNAME$ac_delim
+PBX_OSREV!$PBX_OSREV$ac_delim
+CXX!$CXX$ac_delim
+LD!$LD$ac_delim
+RANLIB!$RANLIB$ac_delim
+CXXFLAGS!$CXXFLAGS$ac_delim
+ac_ct_CXX!$ac_ct_CXX$ac_delim
+CXXCPP!$CXXCPP$ac_delim
+SED!$SED$ac_delim
+AWK!$AWK$ac_delim
+INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim
+INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim
+INSTALL_DATA!$INSTALL_DATA$ac_delim
+LN_S!$LN_S$ac_delim
+GNU_MAKE!$GNU_MAKE$ac_delim
+STRIP!$STRIP$ac_delim
+AR!$AR$ac_delim
+GNU_LD!$GNU_LD$ac_delim
+FIND!$FIND$ac_delim
+COMPRESS!$COMPRESS$ac_delim
+BASENAME!$BASENAME$ac_delim
+ID!$ID$ac_delim
+DIRNAME!$DIRNAME$ac_delim
+LN!$LN$ac_delim
+DOT!$DOT$ac_delim
+WGET!$WGET$ac_delim
+RUBBER!$RUBBER$ac_delim
+KPATHSEA!$KPATHSEA$ac_delim
+FETCH!$FETCH$ac_delim
+DOWNLOAD!$DOWNLOAD$ac_delim
+SOXMIX!$SOXMIX$ac_delim
+acx_pthread_config!$acx_pthread_config$ac_delim
+_ACEOF
+
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
+ break
+ elif $ac_last_try; then
+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+ ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+CEOF$ac_eof
+_ACEOF
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ cat >conf$$subs.sed <<_ACEOF
+PTHREAD_CC!$PTHREAD_CC$ac_delim
+PTHREAD_LIBS!$PTHREAD_LIBS$ac_delim
+PTHREAD_CFLAGS!$PTHREAD_CFLAGS$ac_delim
+AST_DEVMODE!$AST_DEVMODE$ac_delim
+ALSA_LIB!$ALSA_LIB$ac_delim
+ALSA_INCLUDE!$ALSA_INCLUDE$ac_delim
+ALSA_DIR!$ALSA_DIR$ac_delim
+PBX_ALSA!$PBX_ALSA$ac_delim
+BKTR_LIB!$BKTR_LIB$ac_delim
+BKTR_INCLUDE!$BKTR_INCLUDE$ac_delim
+BKTR_DIR!$BKTR_DIR$ac_delim
+PBX_BKTR!$PBX_BKTR$ac_delim
+CAP_LIB!$CAP_LIB$ac_delim
+CAP_INCLUDE!$CAP_INCLUDE$ac_delim
+CAP_DIR!$CAP_DIR$ac_delim
+PBX_CAP!$PBX_CAP$ac_delim
+CURL_LIB!$CURL_LIB$ac_delim
+CURL_INCLUDE!$CURL_INCLUDE$ac_delim
+CURL_DIR!$CURL_DIR$ac_delim
+PBX_CURL!$PBX_CURL$ac_delim
+CURSES_LIB!$CURSES_LIB$ac_delim
+CURSES_INCLUDE!$CURSES_INCLUDE$ac_delim
+CURSES_DIR!$CURSES_DIR$ac_delim
+PBX_CURSES!$PBX_CURSES$ac_delim
+CRYPTO_LIB!$CRYPTO_LIB$ac_delim
+CRYPTO_INCLUDE!$CRYPTO_INCLUDE$ac_delim
+CRYPTO_DIR!$CRYPTO_DIR$ac_delim
+PBX_CRYPTO!$PBX_CRYPTO$ac_delim
+GSM_LIB!$GSM_LIB$ac_delim
+GSM_INCLUDE!$GSM_INCLUDE$ac_delim
+GSM_DIR!$GSM_DIR$ac_delim
+PBX_GSM!$PBX_GSM$ac_delim
+GTK_LIB!$GTK_LIB$ac_delim
+GTK_INCLUDE!$GTK_INCLUDE$ac_delim
+GTK_DIR!$GTK_DIR$ac_delim
+PBX_GTK!$PBX_GTK$ac_delim
+GTK2_LIB!$GTK2_LIB$ac_delim
+GTK2_INCLUDE!$GTK2_INCLUDE$ac_delim
+GTK2_DIR!$GTK2_DIR$ac_delim
+PBX_GTK2!$PBX_GTK2$ac_delim
+ICONV_LIB!$ICONV_LIB$ac_delim
+ICONV_INCLUDE!$ICONV_INCLUDE$ac_delim
+ICONV_DIR!$ICONV_DIR$ac_delim
+PBX_ICONV!$PBX_ICONV$ac_delim
+IKSEMEL_LIB!$IKSEMEL_LIB$ac_delim
+IKSEMEL_INCLUDE!$IKSEMEL_INCLUDE$ac_delim
+IKSEMEL_DIR!$IKSEMEL_DIR$ac_delim
+PBX_IKSEMEL!$PBX_IKSEMEL$ac_delim
+IMAP_TK_LIB!$IMAP_TK_LIB$ac_delim
+IMAP_TK_INCLUDE!$IMAP_TK_INCLUDE$ac_delim
+IMAP_TK_DIR!$IMAP_TK_DIR$ac_delim
+PBX_IMAP_TK!$PBX_IMAP_TK$ac_delim
+ISDNNET_LIB!$ISDNNET_LIB$ac_delim
+ISDNNET_INCLUDE!$ISDNNET_INCLUDE$ac_delim
+ISDNNET_DIR!$ISDNNET_DIR$ac_delim
+PBX_ISDNNET!$PBX_ISDNNET$ac_delim
+JACK_LIB!$JACK_LIB$ac_delim
+JACK_INCLUDE!$JACK_INCLUDE$ac_delim
+JACK_DIR!$JACK_DIR$ac_delim
+PBX_JACK!$PBX_JACK$ac_delim
+LTDL_LIB!$LTDL_LIB$ac_delim
+LTDL_INCLUDE!$LTDL_INCLUDE$ac_delim
+LTDL_DIR!$LTDL_DIR$ac_delim
+PBX_LTDL!$PBX_LTDL$ac_delim
+LUA_LIB!$LUA_LIB$ac_delim
+LUA_INCLUDE!$LUA_INCLUDE$ac_delim
+LUA_DIR!$LUA_DIR$ac_delim
+PBX_LUA!$PBX_LUA$ac_delim
+MISDN_LIB!$MISDN_LIB$ac_delim
+MISDN_INCLUDE!$MISDN_INCLUDE$ac_delim
+MISDN_DIR!$MISDN_DIR$ac_delim
+PBX_MISDN!$PBX_MISDN$ac_delim
+NBS_LIB!$NBS_LIB$ac_delim
+NBS_INCLUDE!$NBS_INCLUDE$ac_delim
+NBS_DIR!$NBS_DIR$ac_delim
+PBX_NBS!$PBX_NBS$ac_delim
+NCURSES_LIB!$NCURSES_LIB$ac_delim
+NCURSES_INCLUDE!$NCURSES_INCLUDE$ac_delim
+NCURSES_DIR!$NCURSES_DIR$ac_delim
+PBX_NCURSES!$PBX_NCURSES$ac_delim
+NETSNMP_LIB!$NETSNMP_LIB$ac_delim
+NETSNMP_INCLUDE!$NETSNMP_INCLUDE$ac_delim
+NETSNMP_DIR!$NETSNMP_DIR$ac_delim
+PBX_NETSNMP!$PBX_NETSNMP$ac_delim
+NEWT_LIB!$NEWT_LIB$ac_delim
+NEWT_INCLUDE!$NEWT_INCLUDE$ac_delim
+NEWT_DIR!$NEWT_DIR$ac_delim
+PBX_NEWT!$PBX_NEWT$ac_delim
+UNIXODBC_LIB!$UNIXODBC_LIB$ac_delim
+UNIXODBC_INCLUDE!$UNIXODBC_INCLUDE$ac_delim
+UNIXODBC_DIR!$UNIXODBC_DIR$ac_delim
+PBX_UNIXODBC!$PBX_UNIXODBC$ac_delim
+OGG_LIB!$OGG_LIB$ac_delim
+OGG_INCLUDE!$OGG_INCLUDE$ac_delim
+OGG_DIR!$OGG_DIR$ac_delim
+PBX_OGG!$PBX_OGG$ac_delim
+OSPTK_LIB!$OSPTK_LIB$ac_delim
+_ACEOF
+
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
+ break
+ elif $ac_last_try; then
+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+ ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-2.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+CEOF$ac_eof
+_ACEOF
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ cat >conf$$subs.sed <<_ACEOF
+OSPTK_INCLUDE!$OSPTK_INCLUDE$ac_delim
+OSPTK_DIR!$OSPTK_DIR$ac_delim
+PBX_OSPTK!$PBX_OSPTK$ac_delim
+OSS_LIB!$OSS_LIB$ac_delim
+OSS_INCLUDE!$OSS_INCLUDE$ac_delim
+OSS_DIR!$OSS_DIR$ac_delim
+PBX_OSS!$PBX_OSS$ac_delim
+PGSQL_LIB!$PGSQL_LIB$ac_delim
+PGSQL_INCLUDE!$PGSQL_INCLUDE$ac_delim
+PGSQL_DIR!$PGSQL_DIR$ac_delim
+PBX_PGSQL!$PBX_PGSQL$ac_delim
+POPT_LIB!$POPT_LIB$ac_delim
+POPT_INCLUDE!$POPT_INCLUDE$ac_delim
+POPT_DIR!$POPT_DIR$ac_delim
+PBX_POPT!$PBX_POPT$ac_delim
+PORTAUDIO_LIB!$PORTAUDIO_LIB$ac_delim
+PORTAUDIO_INCLUDE!$PORTAUDIO_INCLUDE$ac_delim
+PORTAUDIO_DIR!$PORTAUDIO_DIR$ac_delim
+PBX_PORTAUDIO!$PBX_PORTAUDIO$ac_delim
+PRI_LIB!$PRI_LIB$ac_delim
+PRI_INCLUDE!$PRI_INCLUDE$ac_delim
+PRI_DIR!$PRI_DIR$ac_delim
+PBX_PRI!$PBX_PRI$ac_delim
+SS7_LIB!$SS7_LIB$ac_delim
+SS7_INCLUDE!$SS7_INCLUDE$ac_delim
+SS7_DIR!$SS7_DIR$ac_delim
+PBX_SS7!$PBX_SS7$ac_delim
+PWLIB_LIB!$PWLIB_LIB$ac_delim
+PWLIB_INCLUDE!$PWLIB_INCLUDE$ac_delim
+PWLIB_DIR!$PWLIB_DIR$ac_delim
+PBX_PWLIB!$PBX_PWLIB$ac_delim
+OPENH323_LIB!$OPENH323_LIB$ac_delim
+OPENH323_INCLUDE!$OPENH323_INCLUDE$ac_delim
+OPENH323_DIR!$OPENH323_DIR$ac_delim
+PBX_OPENH323!$PBX_OPENH323$ac_delim
+RADIUS_LIB!$RADIUS_LIB$ac_delim
+RADIUS_INCLUDE!$RADIUS_INCLUDE$ac_delim
+RADIUS_DIR!$RADIUS_DIR$ac_delim
+PBX_RADIUS!$PBX_RADIUS$ac_delim
+SPEEX_LIB!$SPEEX_LIB$ac_delim
+SPEEX_INCLUDE!$SPEEX_INCLUDE$ac_delim
+SPEEX_DIR!$SPEEX_DIR$ac_delim
+PBX_SPEEX!$PBX_SPEEX$ac_delim
+SPEEXDSP_LIB!$SPEEXDSP_LIB$ac_delim
+SPEEXDSP_INCLUDE!$SPEEXDSP_INCLUDE$ac_delim
+SPEEXDSP_DIR!$SPEEXDSP_DIR$ac_delim
+PBX_SPEEXDSP!$PBX_SPEEXDSP$ac_delim
+SQLITE_LIB!$SQLITE_LIB$ac_delim
+SQLITE_INCLUDE!$SQLITE_INCLUDE$ac_delim
+SQLITE_DIR!$SQLITE_DIR$ac_delim
+PBX_SQLITE!$PBX_SQLITE$ac_delim
+SQLITE3_LIB!$SQLITE3_LIB$ac_delim
+SQLITE3_INCLUDE!$SQLITE3_INCLUDE$ac_delim
+SQLITE3_DIR!$SQLITE3_DIR$ac_delim
+PBX_SQLITE3!$PBX_SQLITE3$ac_delim
+SUPPSERV_LIB!$SUPPSERV_LIB$ac_delim
+SUPPSERV_INCLUDE!$SUPPSERV_INCLUDE$ac_delim
+SUPPSERV_DIR!$SUPPSERV_DIR$ac_delim
+PBX_SUPPSERV!$PBX_SUPPSERV$ac_delim
+OPENSSL_LIB!$OPENSSL_LIB$ac_delim
+OPENSSL_INCLUDE!$OPENSSL_INCLUDE$ac_delim
+OPENSSL_DIR!$OPENSSL_DIR$ac_delim
+PBX_OPENSSL!$PBX_OPENSSL$ac_delim
+FREETDS_LIB!$FREETDS_LIB$ac_delim
+FREETDS_INCLUDE!$FREETDS_INCLUDE$ac_delim
+FREETDS_DIR!$FREETDS_DIR$ac_delim
+PBX_FREETDS!$PBX_FREETDS$ac_delim
+TERMCAP_LIB!$TERMCAP_LIB$ac_delim
+TERMCAP_INCLUDE!$TERMCAP_INCLUDE$ac_delim
+TERMCAP_DIR!$TERMCAP_DIR$ac_delim
+PBX_TERMCAP!$PBX_TERMCAP$ac_delim
+TINFO_LIB!$TINFO_LIB$ac_delim
+TINFO_INCLUDE!$TINFO_INCLUDE$ac_delim
+TINFO_DIR!$TINFO_DIR$ac_delim
+PBX_TINFO!$PBX_TINFO$ac_delim
+TONEZONE_LIB!$TONEZONE_LIB$ac_delim
+TONEZONE_INCLUDE!$TONEZONE_INCLUDE$ac_delim
+TONEZONE_DIR!$TONEZONE_DIR$ac_delim
+PBX_TONEZONE!$PBX_TONEZONE$ac_delim
+USB_LIB!$USB_LIB$ac_delim
+USB_INCLUDE!$USB_INCLUDE$ac_delim
+USB_DIR!$USB_DIR$ac_delim
+PBX_USB!$PBX_USB$ac_delim
+VORBIS_LIB!$VORBIS_LIB$ac_delim
+VORBIS_INCLUDE!$VORBIS_INCLUDE$ac_delim
+VORBIS_DIR!$VORBIS_DIR$ac_delim
+PBX_VORBIS!$PBX_VORBIS$ac_delim
+VPB_LIB!$VPB_LIB$ac_delim
+VPB_INCLUDE!$VPB_INCLUDE$ac_delim
+VPB_DIR!$VPB_DIR$ac_delim
+PBX_VPB!$PBX_VPB$ac_delim
+X11_LIB!$X11_LIB$ac_delim
+X11_INCLUDE!$X11_INCLUDE$ac_delim
+X11_DIR!$X11_DIR$ac_delim
+PBX_X11!$PBX_X11$ac_delim
+ZLIB_LIB!$ZLIB_LIB$ac_delim
+ZLIB_INCLUDE!$ZLIB_INCLUDE$ac_delim
+_ACEOF
+
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
+ break
+ elif $ac_last_try; then
+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+ ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-3.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+CEOF$ac_eof
+_ACEOF
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ cat >conf$$subs.sed <<_ACEOF
+ZLIB_DIR!$ZLIB_DIR$ac_delim
+PBX_ZLIB!$PBX_ZLIB$ac_delim
+ZAPTEL_LIB!$ZAPTEL_LIB$ac_delim
+ZAPTEL_INCLUDE!$ZAPTEL_INCLUDE$ac_delim
+ZAPTEL_DIR!$ZAPTEL_DIR$ac_delim
+PBX_ZAPTEL!$PBX_ZAPTEL$ac_delim
+ZAPTEL_TRANSCODE_LIB!$ZAPTEL_TRANSCODE_LIB$ac_delim
+ZAPTEL_TRANSCODE_INCLUDE!$ZAPTEL_TRANSCODE_INCLUDE$ac_delim
+ZAPTEL_TRANSCODE_DIR!$ZAPTEL_TRANSCODE_DIR$ac_delim
+PBX_ZAPTEL_TRANSCODE!$PBX_ZAPTEL_TRANSCODE$ac_delim
+ZAPTEL_VLDTMF_LIB!$ZAPTEL_VLDTMF_LIB$ac_delim
+ZAPTEL_VLDTMF_INCLUDE!$ZAPTEL_VLDTMF_INCLUDE$ac_delim
+ZAPTEL_VLDTMF_DIR!$ZAPTEL_VLDTMF_DIR$ac_delim
+PBX_ZAPTEL_VLDTMF!$PBX_ZAPTEL_VLDTMF$ac_delim
+ZAPTEL_HWGAIN_LIB!$ZAPTEL_HWGAIN_LIB$ac_delim
+ZAPTEL_HWGAIN_INCLUDE!$ZAPTEL_HWGAIN_INCLUDE$ac_delim
+ZAPTEL_HWGAIN_DIR!$ZAPTEL_HWGAIN_DIR$ac_delim
+PBX_ZAPTEL_HWGAIN!$PBX_ZAPTEL_HWGAIN$ac_delim
+ZAPTEL_ECHOCANPARAMS_LIB!$ZAPTEL_ECHOCANPARAMS_LIB$ac_delim
+ZAPTEL_ECHOCANPARAMS_INCLUDE!$ZAPTEL_ECHOCANPARAMS_INCLUDE$ac_delim
+ZAPTEL_ECHOCANPARAMS_DIR!$ZAPTEL_ECHOCANPARAMS_DIR$ac_delim
+PBX_ZAPTEL_ECHOCANPARAMS!$PBX_ZAPTEL_ECHOCANPARAMS$ac_delim
+ZAPTEL_CHANALARMS_LIB!$ZAPTEL_CHANALARMS_LIB$ac_delim
+ZAPTEL_CHANALARMS_INCLUDE!$ZAPTEL_CHANALARMS_INCLUDE$ac_delim
+ZAPTEL_CHANALARMS_DIR!$ZAPTEL_CHANALARMS_DIR$ac_delim
+PBX_ZAPTEL_CHANALARMS!$PBX_ZAPTEL_CHANALARMS$ac_delim
+SDL_LIB!$SDL_LIB$ac_delim
+SDL_INCLUDE!$SDL_INCLUDE$ac_delim
+SDL_DIR!$SDL_DIR$ac_delim
+PBX_SDL!$PBX_SDL$ac_delim
+SDL_IMAGE_LIB!$SDL_IMAGE_LIB$ac_delim
+SDL_IMAGE_INCLUDE!$SDL_IMAGE_INCLUDE$ac_delim
+SDL_IMAGE_DIR!$SDL_IMAGE_DIR$ac_delim
+PBX_SDL_IMAGE!$PBX_SDL_IMAGE$ac_delim
+FFMPEG_LIB!$FFMPEG_LIB$ac_delim
+FFMPEG_INCLUDE!$FFMPEG_INCLUDE$ac_delim
+FFMPEG_DIR!$FFMPEG_DIR$ac_delim
+PBX_FFMPEG!$PBX_FFMPEG$ac_delim
+ALLOCA!$ALLOCA$ac_delim
+LIBOBJS!$LIBOBJS$ac_delim
+POW_LIB!$POW_LIB$ac_delim
+HAS_POLL!$HAS_POLL$ac_delim
+GC_CFLAGS!$GC_CFLAGS$ac_delim
+GC_LDFLAGS!$GC_LDFLAGS$ac_delim
+AST_DECLARATION_AFTER_STATEMENT!$AST_DECLARATION_AFTER_STATEMENT$ac_delim
+GSM_INTERNAL!$GSM_INTERNAL$ac_delim
+CONFIG_NETSNMP!$CONFIG_NETSNMP$ac_delim
+PG_CONFIG!$PG_CONFIG$ac_delim
+PTLIB_CONFIG!$PTLIB_CONFIG$ac_delim
+PWLIBDIR!$PWLIBDIR$ac_delim
+PWLIB_INCDIR!$PWLIB_INCDIR$ac_delim
+PWLIB_LIBDIR!$PWLIB_LIBDIR$ac_delim
+PWLIB_PLATFORM!$PWLIB_PLATFORM$ac_delim
+OPENH323DIR!$OPENH323DIR$ac_delim
+OPENH323_INCDIR!$OPENH323_INCDIR$ac_delim
+OPENH323_LIBDIR!$OPENH323_LIBDIR$ac_delim
+OPENH323_SUFFIX!$OPENH323_SUFFIX$ac_delim
+OPENH323_BUILD!$OPENH323_BUILD$ac_delim
+EDITLINE_LIB!$EDITLINE_LIB$ac_delim
+PBX_H323!$PBX_H323$ac_delim
+PBX_IXJUSER!$PBX_IXJUSER$ac_delim
+CONFIG_SDL!$CONFIG_SDL$ac_delim
+CONFIG_GTK!$CONFIG_GTK$ac_delim
+PKGCONFIG!$PKGCONFIG$ac_delim
+CURL_CONFIG!$CURL_CONFIG$ac_delim
+LTLIBOBJS!$LTLIBOBJS$ac_delim
+_ACEOF
+
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 66; then
+ break
+ elif $ac_last_try; then
+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+ ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-4.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+:end
+s/|#_!!_#|//g
+CEOF$ac_eof
+_ACEOF
+
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[ ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+fi # test -n "$CONFIG_FILES"
+
+
+for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5
+echo "$as_me: error: Invalid tag $ac_tag." >&2;}
+ { (exit 1); exit 1; }; };;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+ ac_file_inputs="$ac_file_inputs $ac_f"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input="Generated from "`IFS=:
+ echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ fi
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin";;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir="$ac_dir"
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+case `sed -n '/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+' $ac_file_inputs` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s&@configure_input@&$configure_input&;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" | sed -f "$tmp/subs-3.sed" | sed -f "$tmp/subs-4.sed" >$tmp/out
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&5
+echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&2;}
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out"; rm -f "$tmp/out";;
+ *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;
+ esac
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+_ACEOF
+
+# Transform confdefs.h into a sed script `conftest.defines', that
+# substitutes the proper values into config.h.in to produce config.h.
+rm -f conftest.defines conftest.tail
+# First, append a space to every undef/define line, to ease matching.
+echo 's/$/ /' >conftest.defines
+# Then, protect against being on the right side of a sed subst, or in
+# an unquoted here document, in config.status. If some macros were
+# called several times there might be several #defines for the same
+# symbol, which is useless. But do not sort them, since the last
+# AC_DEFINE must be honored.
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where
+# NAME is the cpp macro being defined, VALUE is the value it is being given.
+# PARAMS is the parameter list in the macro definition--in most cases, it's
+# just an empty string.
+ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*'
+ac_dB='\\)[ (].*,\\1define\\2'
+ac_dC=' '
+ac_dD=' ,'
+
+uniq confdefs.h |
+ sed -n '
+ t rset
+ :rset
+ s/^[ ]*#[ ]*define[ ][ ]*//
+ t ok
+ d
+ :ok
+ s/[\\&,]/\\&/g
+ s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p
+ s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p
+ ' >>conftest.defines
+
+# Remove the space that was appended to ease matching.
+# Then replace #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+# (The regexp can be short, since the line contains either #define or #undef.)
+echo 's/ $//
+s,^[ #]*u.*,/* & */,' >>conftest.defines
+
+# Break up conftest.defines:
+ac_max_sed_lines=50
+
+# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1"
+# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2"
+# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1"
+# et cetera.
+ac_in='$ac_file_inputs'
+ac_out='"$tmp/out1"'
+ac_nxt='"$tmp/out2"'
+
+while :
+do
+ # Write a here document:
+ cat >>$CONFIG_STATUS <<_ACEOF
+ # First, check the format of the line:
+ cat >"\$tmp/defines.sed" <<\\CEOF
+/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def
+/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def
+b
+:def
+_ACEOF
+ sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS
+ ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in
+ sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail
+ grep . conftest.tail >/dev/null || break
+ rm -f conftest.defines
+ mv conftest.tail conftest.defines
+done
+rm -f conftest.defines conftest.tail
+
+echo "ac_result=$ac_in" >>$CONFIG_STATUS
+cat >>$CONFIG_STATUS <<\_ACEOF
+ if test x"$ac_file" != x-; then
+ echo "/* $configure_input */" >"$tmp/config.h"
+ cat "$ac_result" >>"$tmp/config.h"
+ if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f $ac_file
+ mv "$tmp/config.h" $ac_file
+ fi
+ else
+ echo "/* $configure_input */"
+ cat "$ac_result"
+ fi
+ rm -f "$tmp/out12"
+ ;;
+
+
+ esac
+
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+if test "x${silent}" != "xyes" ; then
+echo
+echo " .\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$=.. "
+echo " .\$7\$7.. .7\$\$7:. "
+echo " .\$\$:. ,\$7.7 "
+echo " .\$7. 7\$\$\$\$ .\$\$77 "
+echo " ..\$\$. \$\$\$\$\$ .\$\$\$7 "
+echo " ..7\$ .?. \$\$\$\$\$ .?. 7\$\$\$."
+echo " \$.\$. .\$\$\$7. \$\$\$\$7 .7\$\$\$. .\$\$\$."
+echo " .777. .\$\$\$\$\$\$77\$\$\$77\$\$\$\$\$7. \$\$\$,"
+echo " \$\$\$~ .7\$\$\$\$\$\$\$\$\$\$\$\$\$7. .\$\$\$."
+echo ".\$\$7 .7\$\$\$\$\$\$\$7: ?\$\$\$."
+echo "\$\$\$ ?7\$\$\$\$\$\$\$\$\$\$I .\$\$\$7 "
+echo "\$\$\$ .7\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$ :\$\$\$. "
+echo "\$\$\$ \$\$\$\$\$\$7\$\$\$\$\$\$\$\$\$\$\$\$ .\$\$\$. "
+echo "\$\$\$ \$\$\$ 7\$\$\$7 .\$\$\$ .\$\$\$. "
+echo "\$\$\$\$ \$\$\$\$7 .\$\$\$. "
+echo "7\$\$\$7 7\$\$\$\$ 7\$\$\$ "
+echo " \$\$\$\$\$ \$\$\$ "
+echo " \$\$\$\$7. \$\$ (TM) "
+echo " \$\$\$\$\$\$\$. .7\$\$\$\$\$\$ \$\$ "
+echo " \$\$\$\$\$\$\$\$\$\$\$\$7\$\$\$\$\$\$\$\$\$.\$\$\$\$\$\$ "
+echo " \$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$. "
+echo
+fi
+
+{ echo "$as_me:$LINENO: Package configured for: " >&5
+echo "$as_me: Package configured for: " >&6;}
+{ echo "$as_me:$LINENO: OS type : $host_os" >&5
+echo "$as_me: OS type : $host_os" >&6;}
+{ echo "$as_me:$LINENO: Host CPU : $host_cpu" >&5
+echo "$as_me: Host CPU : $host_cpu" >&6;}
+{ echo "$as_me:$LINENO: build-cpu:vendor:os: $build_cpu : $build_vendor : $build_os :" >&5
+echo "$as_me: build-cpu:vendor:os: $build_cpu : $build_vendor : $build_os :" >&6;}
+{ echo "$as_me:$LINENO: host-cpu:vendor:os: $host_cpu : $host_vendor : $host_os :" >&5
+echo "$as_me: host-cpu:vendor:os: $host_cpu : $host_vendor : $host_os :" >&6;}
+if test "${cross_compiling}" = "yes"; then
+ { echo "$as_me:$LINENO: Cross Compilation = YES" >&5
+echo "$as_me: Cross Compilation = YES" >&6;}
+fi
diff --git a/trunk/configure.ac b/trunk/configure.ac
new file mode 100644
index 000000000..944dd0758
--- /dev/null
+++ b/trunk/configure.ac
@@ -0,0 +1,1208 @@
+# Process this file with autoconf to produce a configure script.
+#
+# Make sure we use autoconf 2.60 to generate the "configure" script,
+# in case we want to commit it. Other than that, version 2.59 is
+# perfectly fine for our purposes, so people who want to modify
+# this file just have to remember to set the AC_PREREQ argument
+# to something that suits their needs.
+
+AC_PREREQ(2.60)
+
+AC_INIT(asterisk, 1.4, www.asterisk.org)
+
+# cross-compile macros
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+
+# check existence of the package
+AC_CONFIG_SRCDIR([main/asterisk.c])
+
+# specify output header file
+AC_CONFIG_HEADER(include/asterisk/autoconfig.h)
+
+AC_COPYRIGHT("Asterisk")
+AC_REVISION($Revision$)
+
+AC_GNU_SOURCE
+AC_USE_SYSTEM_EXTENSIONS # note- does not work on FreeBSD
+
+case "${host_os}" in
+ freebsd*)
+ ac_default_prefix=/usr/local
+ CPPFLAGS=-I/usr/local/include
+ LDFLAGS=-L/usr/local/lib
+ ;;
+
+ *)
+ ac_default_prefix=/usr
+ if test ${sysconfdir} = '${prefix}/etc'; then
+ sysconfdir=/etc
+ fi
+ if test ${mandir} = '${prefix}/man'; then
+ mandir=/usr/share/man
+ fi
+ ;;
+esac
+
+if test ${localstatedir} = '${prefix}/var'; then
+ localstatedir=/var
+fi
+
+BUILD_PLATFORM=${build}
+BUILD_CPU=${build_cpu}
+BUILD_VENDOR=${build_vendor}
+BUILD_OS=${build_os}
+
+AC_SUBST(BUILD_PLATFORM)
+AC_SUBST(BUILD_CPU)
+AC_SUBST(BUILD_VENDOR)
+AC_SUBST(BUILD_OS)
+
+HOST_PLATFORM=${host}
+HOST_CPU=${host_cpu}
+HOST_VENDOR=${host_vendor}
+HOST_OS=${host_os}
+
+AC_SUBST(HOST_PLATFORM)
+AC_SUBST(HOST_CPU)
+AC_SUBST(HOST_VENDOR)
+AC_SUBST(HOST_OS)
+
+WINARCH=0
+
+case "${host_os}" in
+ freebsd*)
+ OSARCH=FreeBSD
+ ;;
+ netbsd*)
+ OSARCH=NetBSD
+ ;;
+ openbsd*)
+ OSARCH=OpenBSD
+ ;;
+ solaris*)
+ OSARCH=SunOS
+ ;;
+ mingw32)
+ OSARCH=mingw32
+ WINARCH=1
+ ;;
+ cygwin)
+ OSARCH=cygwin
+ WINARCH=1
+ ;;
+ *)
+ OSARCH=${host_os}
+ ;;
+esac
+
+AC_SUBST(OSARCH)
+AC_SUBST(WINARCH)
+
+# check for uname
+AC_PATH_TOOL([UNAME], [uname], No)
+if test ! x"${UNAME}" = xNo; then
+ PBX_OSREV=$(${UNAME} -r)
+fi
+AC_SUBST(PBX_OSREV)
+
+AH_TOP(
+#ifndef ASTERISK_AUTOCONFIG_H
+#define ASTERISK_AUTOCONFIG_H
+
+#include "asterisk/buildopts.h"
+
+)
+
+AH_BOTTOM(
+#endif
+)
+
+# cross-compile checks
+if test "${cross_compiling}" = "yes";
+then
+ AC_CHECK_TOOL(CC, gcc, :)
+ AC_CHECK_TOOL(CXX, g++, :)
+ AC_CHECK_TOOL(LD, ld, :)
+ AC_CHECK_TOOL(RANLIB, ranlib, :)
+fi
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_CPP
+AC_PROG_CXXCPP
+# This macro is just copied into our local acinclude.m4 from libtool.m4 so that
+# the developers regenerating the configure script don't have to install libtool.
+AST_PROG_LD # note, does not work on FreeBSD
+AC_PROG_AWK
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_RANLIB
+AST_CHECK_GNU_MAKE
+
+AC_PATH_TOOL([STRIP], [strip], :)
+AC_PATH_TOOL([AR], [ar], :)
+
+GNU_LD=0
+if test "x$with_gnu_ld" = "xyes" ; then
+ GNU_LD=1
+fi
+AC_SUBST(GNU_LD)
+
+AC_PATH_PROG([AWK], [awk], :)
+AC_PATH_PROG([GREP], [grep], :)
+AC_PATH_PROG([FIND], [find], :)
+AC_PATH_PROG([COMPRESS], [compress], :)
+AC_PATH_PROG([BASENAME], [basename], :)
+AC_PATH_PROG([ID], [id], :)
+AC_PATH_PROG([DIRNAME], [dirname], :)
+AC_PATH_PROG([SHELL], [sh], :)
+AC_PATH_PROG([LN], [ln], :)
+AC_PATH_PROG([DOT], [dot], :)
+AC_PATH_PROG([WGET], [wget], :)
+AC_PATH_PROG([RUBBER], [rubber], :)
+AC_PATH_PROG([KPATHSEA], [kpsewhich], :)
+if test "${WGET}" != ":" ; then
+ DOWNLOAD=${WGET}
+else
+ AC_PATH_PROG([FETCH], [fetch], [:])
+ DOWNLOAD=${FETCH}
+fi
+AC_SUBST(DOWNLOAD)
+
+AC_CHECK_TOOL([SOXMIX], [soxmix], [:])
+if test "${SOXMIX}" != ":" ; then
+ AC_DEFINE([HAVE_SOXMIX], 1, [Define to 1 if your system has soxmix application.])
+fi
+
+ACX_PTHREAD
+
+AC_LANG(C)
+
+AC_ARG_ENABLE(dev-mode,
+ [ --enable-dev-mode Turn on developer mode],
+ [case "${enableval}" in
+ y|ye|yes) AST_DEVMODE=yes ;;
+ n|no) AST_DEVMODE=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-dev-mode) ;;
+ esac])
+AC_SUBST(AST_DEVMODE)
+
+# AST_EXT_LIB_SETUP is used to tell configure to handle variables for
+# various packages.
+# $1 is the prefix for the variables in makeopts and autoconfig.h
+# $2 is the short comment, $4 is the long comment
+# $3 is the name used in --with- or --without- flags for configure.
+#
+# Package option names should be in alphabetical order
+# by the --with option name (the third field),
+# to make things easier for the users.
+
+AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound])
+
+# BKTR is used for backtrace support on platforms that do not
+# have it natively.
+AST_EXT_LIB_SETUP([BKTR], [Stack Backtrace support], [execinfo])
+AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap])
+AST_EXT_LIB_SETUP([CURL], [cURL], [curl])
+AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
+AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography support], [crypto])
+AST_EXT_LIB_SETUP([GSM], [External GSM library], [gsm], [, use 'internal' GSM otherwise])
+AST_EXT_LIB_SETUP([GTK], [gtk libraries], [gtk])
+AST_EXT_LIB_SETUP([GTK2], [gtk2 libraries], [gtk2])
+AST_EXT_LIB_SETUP([ICONV], [Iconv Library], [iconv])
+AST_EXT_LIB_SETUP([IKSEMEL], [Iksemel Jabber Library], [iksemel])
+AST_EXT_LIB_SETUP([IMAP_TK], [UW IMAP Toolkit], [imap])
+AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux Library], [isdnnet])
+AST_EXT_LIB_SETUP([JACK], [Jack Audio Connection Kit], [jack])
+AST_EXT_LIB_SETUP([LTDL], [libtool], [ltdl])
+AST_EXT_LIB_SETUP([LUA], [Lua], [lua])
+AST_EXT_LIB_SETUP([MISDN], [mISDN User Library], [misdn])
+AST_EXT_LIB_SETUP([NBS], [Network Broadcast Sound], [nbs])
+AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses])
+AST_EXT_LIB_SETUP([NETSNMP], [Net-SNMP], [netsnmp])
+AST_EXT_LIB_SETUP([NEWT], [newt], [newt])
+AST_EXT_LIB_SETUP([UNIXODBC], [unixODBC], [odbc])
+AST_EXT_LIB_SETUP([OGG], [OGG], [ogg])
+AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk])
+AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss])
+AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
+AST_EXT_LIB_SETUP([POPT], [popt], [popt])
+AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
+AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
+AST_EXT_LIB_SETUP([SS7], [ISDN SS7], [ss7])
+AST_EXT_LIB_SETUP([PWLIB], [PWlib], [pwlib])
+AST_EXT_LIB_SETUP([OPENH323], [OpenH323], [h323])
+AST_EXT_LIB_SETUP([RADIUS], [Radius Client], [radius])
+AST_EXT_LIB_SETUP([SPEEX], [Speex], [speex])
+AST_EXT_LIB_SETUP([SPEEXDSP], [Speexdsp], [speexdsp])
+AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite])
+AST_EXT_LIB_SETUP([SQLITE3], [SQLite], [sqlite3])
+AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv])
+AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL Secure Sockets Layer support], [ssl])
+AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds])
+AST_EXT_LIB_SETUP([TERMCAP], [Termcap], [termcap])
+AST_EXT_LIB_SETUP([TINFO], [Term Info], [tinfo])
+AST_EXT_LIB_SETUP([TONEZONE], [tonezone], [tonezone])
+AST_EXT_LIB_SETUP([USB], [usb], [usb])
+AST_EXT_LIB_SETUP([VORBIS], [Vorbis], [vorbis])
+AST_EXT_LIB_SETUP([VPB], [Voicetronix API], [vpb])
+AST_EXT_LIB_SETUP([X11], [X11 support], [x11])
+AST_EXT_LIB_SETUP([ZLIB], [zlib], [z])
+AST_EXT_LIB_SETUP([ZAPTEL], [Zaptel], [zaptel])
+AST_EXT_LIB_SETUP([ZAPTEL_TRANSCODE], [Zaptel Transcoder Support], [zaptel_transcode])
+AST_EXT_LIB_SETUP([ZAPTEL_VLDTMF], [Zaptel VLDTMF Support], [zaptel_vldtmf])
+AST_EXT_LIB_SETUP([ZAPTEL_HWGAIN], [Zaptel Hardware Gain Support], [zaptel_hwgain])
+AST_EXT_LIB_SETUP([ZAPTEL_ECHOCANPARAMS], [Zaptel Echo Canceler Parameter Support], [zaptel_echocanparams])
+AST_EXT_LIB_SETUP([ZAPTEL_CHANALARMS], [Zaptel Channel Alarm Support], [zaptel_chanalarms])
+
+AST_EXT_LIB_SETUP([SDL], [Sdl], [sdl])
+AST_EXT_LIB_SETUP([SDL_IMAGE], [Sdl Image library], [SDL_image])
+AST_EXT_LIB_SETUP([FFMPEG], [Ffmpeg and avcodec library], [avcodec])
+
+# check for basic system features and functionality before
+# checking for package libraries
+
+AC_FUNC_ALLOCA
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h libintl.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h strings.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utime.h])
+
+AC_CHECK_HEADERS([winsock.h winsock2.h])
+
+AC_SYS_LARGEFILE
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+AC_C_CONST
+AC_TYPE_UID_T
+AC_C_INLINE
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_CHECK_MEMBERS([struct stat.st_blksize])
+AC_HEADER_TIME
+AC_STRUCT_TM
+AC_C_VOLATILE
+AC_CHECK_TYPES([ptrdiff_t])
+
+# Checks for library functions.
+AC_FUNC_CHOWN
+AC_FUNC_CLOSEDIR_VOID
+AC_FUNC_ERROR_AT_LINE
+AST_FUNC_FORK
+AC_FUNC_FSEEKO
+AC_PROG_GCC_TRADITIONAL
+# XXX: these are commented out until we determine whether it matters if our malloc()
+# acts exactly like glibc's or not
+# AC_FUNC_MALLOC
+# AC_FUNC_REALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_MKTIME
+AC_FUNC_MMAP
+AC_FUNC_SELECT_ARGTYPES
+AC_FUNC_SETVBUF_REVERSED
+AC_TYPE_SIGNAL
+AC_FUNC_STAT
+AC_FUNC_STRCOLL
+AC_FUNC_STRFTIME
+AC_FUNC_STRNLEN
+AC_FUNC_STRTOD
+AC_FUNC_UTIME_NULL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([asprintf atexit bzero dup2 endpwent ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf])
+
+AC_CHECK_FUNCS([glob])
+
+AC_MSG_CHECKING(for timersub in time.h)
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([#include <sys/time.h>],
+ [struct timeval *a; timersub(a, a, a);]),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_TIMERSUB], 1, [Define to 1 if your system defines timersub.]),
+ AC_MSG_RESULT(no)
+)
+
+AC_CHECK_HEADER([sys/poll.h],
+ [HAS_POLL=1]
+ AC_DEFINE([HAVE_SYS_POLL_H], 1, [Define to 1 if your system has working sys/poll.h]),
+ )
+AC_SUBST(HAS_POLL)
+
+# https support (in main/http.c) uses funopen on BSD systems,
+# fopencookie on linux
+AC_CHECK_FUNCS([funopen fopencookie])
+
+AC_CHECK_FUNCS([inet_aton])
+
+# check if we have IP_PKTINFO constant defined
+AC_MSG_CHECKING(for IP_PKTINFO)
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([#include <netinet/in.h>],
+ [int pi = IP_PKTINFO;]),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_PKTINFO], 1, [Define to 1 if your system defines IP_PKTINFO.]),
+ AC_MSG_RESULT(no)
+)
+
+# some systems already have gethostbyname_r so we don't need to build ours in main/utils.c
+AC_SEARCH_LIBS(gethostbyname_r, [socket nsl])
+
+AC_MSG_CHECKING(for gethostbyname_r with 6 arguments)
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([#include <stdlib.h>
+ #include <netdb.h>],
+ [struct hostent *he = gethostbyname_r((const char *)NULL, (struct hostent *)NULL, (char *)NULL, (int)0, (struct hostent **)NULL, (int *)NULL);]),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if your system has gethostbyname_r with 6 arguments.]),
+ AC_MSG_RESULT(no)
+)
+
+AC_MSG_CHECKING(for gethostbyname_r with 5 arguments)
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([#include <stdlib.h>
+ #include <netdb.h>],
+ [struct hostent *he = gethostbyname_r((const char *)NULL, (struct hostent *)NULL, (char *)NULL, (int)0, (int *)NULL);]),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if your system has gethostbyname_r with 5 arguments.]),
+ AC_MSG_RESULT(no)
+)
+
+AC_CHECK_HEADER([byteswap.h], [AC_DEFINE_UNQUOTED([HAVE_BYTESWAP_H], 1, [Define to 1 if byteswap.h macros are available.])])
+
+AC_MSG_CHECKING(for __swap16 variant of <sys/endian.h> byteswapping macros)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([#include <sys/endian.h>], [int a = 1; int b = __swap16(a);]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_SYS_ENDIAN_SWAP16], 1, [Define to 1 if your sys/endian.h header file provides the __swap16 macro.]),
+AC_MSG_RESULT(no)
+)
+
+AC_MSG_CHECKING(for bswap16 variant of <sys/endian.h> byteswapping macros)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([#include <sys/endian.h>], [int a = 1; int b = bswap16(a);]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_SYS_ENDIAN_BSWAP16], 1, [Define to 1 if your sys/endian.h header file provides the bswap16 macro.]),
+AC_MSG_RESULT(no)
+)
+
+if test "${cross_compiling}" = "no";
+then
+ AC_CHECK_FILE(/dev/urandom, AC_DEFINE([HAVE_DEV_URANDOM], 1, [Define to 1 if your system has /dev/urandom.]))
+fi
+
+AST_C_DEFINE_CHECK([PTHREAD_RWLOCK_INITIALIZER], [PTHREAD_RWLOCK_INITIALIZER], [pthread.h])
+
+AC_MSG_CHECKING(for PTHREAD_RWLOCK_PREFER_WRITER_NP in pthread.h)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([#include <pthread.h>], [int a = PTHREAD_RWLOCK_PREFER_WRITER_NP;]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP], 1, [Define to 1 if your system defines PTHREAD_RWLOCK_PREFER_WRITER_NP in pthread.h]),
+AC_MSG_RESULT(no)
+)
+
+AC_MSG_CHECKING(for PTHREAD_MUTEX_RECURSIVE_NP in pthread.h)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([#include <pthread.h>], [int a = PTHREAD_MUTEX_RECURSIVE_NP;]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE_NP], 1, [Define to 1 if your system defines PTHREAD_MUTEX_RECURSIVE_NP in pthread.h]),
+AC_MSG_RESULT(no)
+)
+
+AST_C_DEFINE_CHECK([PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP], [PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP], [pthread.h])
+
+if test "${cross_compiling}" = "no";
+then
+AC_MSG_CHECKING(for working epoll support)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([#include <sys/epoll.h>], [int res = epoll_create(10);
+ if (res < 0)
+ return 1;
+ close (res);
+ return 0;]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_EPOLL], 1, [Define to 1 if your system has working epoll support.]),
+AC_MSG_RESULT(no)
+)
+fi
+
+AC_MSG_CHECKING(for compiler atomic operations)
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM([], [int foo1; int foo2 = __sync_fetch_and_add(&foo1, 1);]),
+AC_MSG_RESULT(yes)
+AC_DEFINE([HAVE_GCC_ATOMICS], 1, [Define to 1 if your GCC C compiler provides atomic operations.]),
+AC_MSG_RESULT(no)
+)
+
+AST_GCC_ATTRIBUTE(pure)
+AST_GCC_ATTRIBUTE(malloc)
+AST_GCC_ATTRIBUTE(const)
+AST_GCC_ATTRIBUTE(unused)
+AST_GCC_ATTRIBUTE(always_inline)
+AST_GCC_ATTRIBUTE(deprecated)
+
+AC_MSG_CHECKING(for -ffunction-sections support)
+saved_CFLAGS="${CFLAGS}"
+CFLAGS="${CFLAGS} -ffunction-sections"
+AC_COMPILE_IFELSE(
+ AC_LANG_PROGRAM([], [int x = 1;]),
+ AC_MSG_RESULT(yes)
+ [saved_LDFLAGS="${LDFLAGS}"]
+ [LDFLAGS="${LDFLAGS} -Wl,--gc-sections"]
+ AC_MSG_CHECKING(for --gc-sections support)
+ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([], [int x = 1;]),
+ AC_MSG_RESULT(yes)
+ [GC_CFLAGS="-ffunction-sections"]
+ [[GC_LDFLAGS="-Wl,--gc-sections"]],
+ AC_MSG_RESULT(no)
+ )
+ [LDFLAGS="${saved_LDFLAGS}"],
+ AC_MSG_RESULT(no)
+)
+CFLAGS="${saved_CFLAGS}"
+AC_SUBST(GC_CFLAGS)
+AC_SUBST(GC_LDFLAGS)
+
+AC_MSG_CHECKING(for -Wdeclaration-after-statement support)
+if $(${CC} -Wdeclaration-after-statement -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
+ AC_MSG_RESULT(yes)
+ AST_DECLARATION_AFTER_STATEMENT=-Wdeclaration-after-statement
+else
+ AC_MSG_RESULT(no)
+ AST_DECLARATION_AFTER_STATEMENT=
+fi
+AC_SUBST(AST_DECLARATION_AFTER_STATEMENT)
+
+
+AC_MSG_CHECKING(for sysinfo)
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([#include <sys/sysinfo.h>],
+ [struct sysinfo sys_info; int uptime = sys_info.uptime]),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_SYSINFO], 1, [Define to 1 if your system has sysinfo support]),
+ AC_MSG_RESULT(no)
+)
+
+AC_MSG_CHECKING(for res_ninit)
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([#include <resolv.h>],
+ [int foo = res_ninit(NULL);]),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_RES_NINIT], 1, [Define to 1 if your system has the re-entrant resolver functions.])
+ AC_MSG_CHECKING(for res_ndestroy)
+ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM([#include <resolv.h>],
+ [int foo = res_ndestroy(NULL);]),
+ AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_RES_NDESTROY], 1, [Define to 1 if your system has the ndestroy resolver function.]),
+ AC_MSG_RESULT(no)
+ ),
+ AC_MSG_RESULT(no)
+)
+
+AST_C_DEFINE_CHECK([RTLD_NOLOAD], [RTLD_NOLOAD], [dlfcn.h])
+
+AST_C_DEFINE_CHECK([IP_MTU_DISCOVER], [IP_MTU_DISCOVER], [netinet/in.h])
+
+AC_CHECK_HEADER([libkern/OSAtomic.h],
+ [AC_DEFINE_UNQUOTED([HAVE_OSX_ATOMICS], 1, [Define to 1 if OSX atomic operations are supported.])])
+
+AC_CHECK_SIZEOF(int)
+
+# do the package library checks now
+
+AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl])
+
+AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
+
+if test "x${host_os}" = "xlinux-gnu" ; then
+ AST_EXT_LIB_CHECK([CAP], [cap], [cap_from_text], [sys/capability.h])
+fi
+
+# BSD might not have exp2, and/or log2
+AST_EXT_LIB_CHECK([EXP2L], [m], [exp2l])
+AST_EXT_LIB_CHECK([LOG2L], [m], [log2l])
+AST_EXT_LIB_CHECK([EXP10L], [m], [exp10l])
+AST_EXT_LIB_CHECK([LOG10L], [m], [log10l])
+AST_EXT_LIB_CHECK([SINL], [m], [sinl])
+AST_EXT_LIB_CHECK([COSL], [m], [cosl])
+AST_EXT_LIB_CHECK([TANL], [m], [tanl])
+AST_EXT_LIB_CHECK([ASINL], [m], [asinl])
+AST_EXT_LIB_CHECK([ACOSL], [m], [acosl])
+AST_EXT_LIB_CHECK([ATANL], [m], [atanl])
+AST_EXT_LIB_CHECK([ATAN2L], [m], [atan2l])
+AST_EXT_LIB_CHECK([POWL], [m], [powl])
+AST_EXT_LIB_CHECK([SQRTL], [m], [sqrtl])
+AST_EXT_LIB_CHECK([RINTL], [m], [rintl])
+AST_EXT_LIB_CHECK([EXPL], [m], [expl])
+AST_EXT_LIB_CHECK([LOGL], [m], [logl])
+AST_EXT_LIB_CHECK([REMAINDERL], [m], [remainderl])
+AST_EXT_LIB_CHECK([FMODL], [m], [fmodl])
+AST_EXT_LIB_CHECK([ROUNDL], [m], [roundl])
+AST_EXT_LIB_CHECK([TRUNCL], [m], [truncl])
+AST_EXT_LIB_CHECK([STRTOLD], [c], [strtold], [stdlib.h])
+AST_EXT_LIB_CHECK([FLOORL], [m], [floorl])
+AST_EXT_LIB_CHECK([CEILL], [m], [ceill])
+AST_EXT_LIB_CHECK([EXP2], [m], [exp2])
+AST_EXT_LIB_CHECK([LOG2], [m], [log2])
+AST_EXT_LIB_CHECK([EXP10], [m], [exp10])
+AST_EXT_LIB_CHECK([LOG10], [m], [log10])
+AST_EXT_LIB_CHECK([SIN], [m], [sin])
+AST_EXT_LIB_CHECK([COS], [m], [cos])
+AST_EXT_LIB_CHECK([TAN], [m], [tan])
+AST_EXT_LIB_CHECK([ASIN], [m], [asin])
+AST_EXT_LIB_CHECK([ACOS], [m], [acos])
+AST_EXT_LIB_CHECK([ATAN], [m], [atan])
+AST_EXT_LIB_CHECK([ATAN2], [m], [atan2])
+AST_EXT_LIB_CHECK([POW], [m], [pow])
+AST_EXT_LIB_CHECK([SQRT], [m], [sqrt])
+AST_EXT_LIB_CHECK([RINT], [m], [rint])
+AST_EXT_LIB_CHECK([EXP], [m], [exp])
+AST_EXT_LIB_CHECK([LOG], [m], [log])
+AST_EXT_LIB_CHECK([REMAINDER], [m], [remainder])
+AST_EXT_LIB_CHECK([FMOD], [m], [fmod])
+AST_EXT_LIB_CHECK([ROUND], [m], [round])
+AST_EXT_LIB_CHECK([TRUNC], [m], [trunc])
+AST_EXT_LIB_CHECK([STRTOD], [c], [strtod], [stdlib.h])
+AST_EXT_LIB_CHECK([FLOOR], [m], [floor])
+AST_EXT_LIB_CHECK([CEIL], [m], [ceil])
+
+GSM_INTERNAL="yes"
+AC_SUBST(GSM_INTERNAL)
+GSM_SYSTEM="yes"
+if test "${USE_GSM}" != "no"; then
+ if test "${GSM_DIR}" = "internal"; then
+ GSM_SYSTEM="no"
+ elif test "${GSM_DIR}" != ""; then
+ GSM_INTERNAL="no"
+ fi
+ if test "${GSM_SYSTEM}" = "yes"; then
+ gsmlibdir=""
+ if test "x${GSM_DIR}" != "x"; then
+ if test -d ${GSM_DIR}/lib; then
+ gsmlibdir="-L${GSM_DIR}/lib"
+ else
+ gsmlibdir="-L${GSM_DIR}"
+ fi
+ fi
+ AC_CHECK_LIB([gsm], [gsm_create], AC_DEFINE_UNQUOTED([HAVE_GSM], 1,
+ [Define to indicate the GSM library]), [], ${gsmlibdir})
+ if test "${ac_cv_lib_gsm_gsm_create}" = "yes"; then
+ if test "x${GSM_DIR}" != "x" ; then
+ AC_CHECK_HEADER([${GSM_DIR}/include/gsm.h], [GSM_HEADER_FOUND=1], [GSM_HEADER_FOUND=0])
+ AC_CHECK_HEADER([${GSM_DIR}/include/gsm/gsm.h], [GSM_GSM_HEADER_FOUND=1], [GSM_GSM_HEADER_FOUND=0])
+ else
+ AC_CHECK_HEADER([gsm.h], [GSM_HEADER_FOUND=1], [GSM_HEADER_FOUND=0])
+ AC_CHECK_HEADER([gsm/gsm.h], [GSM_GSM_HEADER_FOUND=1], [GSM_GSM_HEADER_FOUND=0])
+ fi
+ if test "${GSM_HEADER_FOUND}" = "0" ; then
+ if test "{GSM_GSM_HEADER_FOUND}" = "0" ; then
+ if test "x${GSM_MANDATORY}" = "xyes" ; then
+ AC_MSG_NOTICE([***])
+ AC_MSG_NOTICE([*** It appears that you do not have the gsm development package installed.])
+ AC_MSG_NOTICE([*** Please install it to include ${GSM_DESCRIP} support, or re-run configure])
+ AC_MSG_NOTICE([*** without explicitly specifying --with-${GSM_OPTION}])
+ exit 1
+ fi
+ fi
+ fi
+ GSM_OK=0
+ if test "${GSM_HEADER_FOUND}" = "1" ; then
+ AC_DEFINE_UNQUOTED([HAVE_GSM_HEADER], 1, [Define to indicate that gsm.h has no prefix for its location])
+ GSM_OK=1
+ else
+ if test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
+ AC_DEFINE_UNQUOTED([HAVE_GSM_GSM_HEADER], 1, [Define to indicate that gsm.h is in gsm/gsm.h])
+ GSM_OK=1
+ fi
+ fi
+ if test "${GSM_OK}" = "1" ; then
+ GSM_LIB="-lgsm"
+ if test "x${GSM_DIR}" != "x"; then
+ GSM_LIB="${gsmlibdir} ${GSM_LIB}"
+ GSM_INCLUDE="-I${GSM_DIR}/include"
+ fi
+ PBX_GSM=1
+ GSM_INTERNAL="no"
+ fi
+ fi
+ fi
+ if test "${GSM_INTERNAL}" = "yes"; then
+ PBX_GSM=1
+ AC_DEFINE_UNQUOTED([HAVE_GSM_HEADER], 1, [Define to indicate that gsm.h has no prefix for its location])
+ fi
+fi
+
+if test "${host_os}" != "linux-gnu" ; then
+ AST_EXT_LIB_CHECK([ICONV], [iconv], [iconv_open], [iconv.h])
+else
+ PBX_ICONV=1
+fi
+
+AST_EXT_LIB_CHECK([IKSEMEL], [iksemel], [iks_start_sasl], [iksemel.h])
+
+if test "${USE_IMAP_TK}" != "no"; then
+ if test "${IMAP_TK_DIR}" = "system" ; then
+ AC_MSG_NOTICE([Checking for system c-client library...])
+ elif test "${IMAP_TK_DIR}" = ""; then
+ IMAP_TK_DIR=`pwd`"/../imap-2004g"
+ if test -n "${IMAP_TK_MANDATORY}"; then
+ AC_MSG_NOTICE([The --with-imap option does not search your system for installed])
+ AC_MSG_NOTICE([c-client library/header files. Since you did not provide a path])
+ AC_MSG_NOTICE([the configure script will assume you have placed built the c-client])
+ AC_MSG_NOTICE([files at ${IMAP_TK_DIR}, as outlined in the doc/imapstorage.txt file.])
+ fi
+ fi
+ if test "${IMAP_TK_DIR}" != "system" ; then
+ AC_MSG_CHECKING(for UW IMAP Toolkit c-client library)
+ fi
+ saved_cppflags="${CPPFLAGS}"
+ saved_libs="${LIBS}"
+ if test "${IMAP_TK_DIR}" = "system" ; then
+ imap_ldflags=""
+ imap_libs="-lc-client"
+ imap_include="-DUSE_SYSTEM_IMAP"
+ elif test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then
+ imap_ldflags=`cat ${IMAP_TK_DIR}/c-client/LDFLAGS`
+ imap_libs="${IMAP_TK_DIR}/c-client/c-client.a"
+ imap_include="-I${IMAP_TK_DIR}/c-client"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${imap_include}"
+ LIBS="${LIBS} ${imap_libs} "`echo ${imap_ldflags}`
+ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [#ifdef USE_SYSTEM_IMAP
+ #include <imap/c-client.h>
+ #else
+ #include "c-client.h"
+ #endif
+ void mm_searched (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_exists (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_expunged (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_flags (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+ {
+ }
+ void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+ {
+ }
+ void mm_log (char *string,long errflg)
+ {
+ }
+ void mm_dlog (char *string)
+ {
+ }
+ void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
+ {
+ }
+ void mm_critical (MAILSTREAM *stream)
+ {
+ }
+ void mm_nocritical (MAILSTREAM *stream)
+ {
+ }
+ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+ {
+ }
+ void mm_fatal (char *string)
+ {
+ }],
+ [
+ MAILSTREAM *foo = mail_open(NULL, "", 0);
+ ]
+ ),
+ [ac_cv_imap_tk="yes"],
+ [ac_cv_imap_tk="no"]
+ )
+ if test "${ac_cv_imap_tk}" = "yes"; then
+ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [#ifdef USE_SYSTEM_IMAP
+ #include <imap/c-client.h>
+ #else
+ #include "c-client.h"
+ #endif
+ void mm_searched (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_exists (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_expunged (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_flags (MAILSTREAM *stream,unsigned long number)
+ {
+ }
+ void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+ {
+ }
+ void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+ {
+ }
+ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+ {
+ }
+ void mm_log (char *string,long errflg)
+ {
+ }
+ void mm_dlog (char *string)
+ {
+ }
+ void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
+ {
+ }
+ void mm_critical (MAILSTREAM *stream)
+ {
+ }
+ void mm_nocritical (MAILSTREAM *stream)
+ {
+ }
+ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+ {
+ }
+ void mm_fatal (char *string)
+ {
+ }],
+ [
+ long check = mail_expunge_full(NULL, "", 0);
+ ]
+ ),
+ [ac_cv_imap_tk2006="yes"],
+ [ac_cv_imap_tk2006="no"]
+ )
+ fi
+ CPPFLAGS="${saved_cppflags}"
+ LIBS="${saved_libs}"
+ if test "${ac_cv_imap_tk}" = "yes"; then
+ AC_MSG_RESULT(yes)
+ IMAP_TK_LIB="${imap_libs} "`echo ${imap_ldflags}`
+ IMAP_TK_INCLUDE="${imap_include}"
+ PBX_IMAP_TK=1
+ AC_DEFINE([HAVE_IMAP_TK], 1, [Define if your system has the UW IMAP Toolkit c-client library.])
+ if test "${ac_cv_imap_tk2006}" = "yes"; then
+ AC_DEFINE([HAVE_IMAP_TK2006], 1, [Define if your system has the UW IMAP Toolkit c-client library version 2006 or greater.])
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+
+AST_EXT_LIB_CHECK([JACK], [jack], [jack_activate], [jack/jack.h])
+
+# Needed by unixodbc
+AST_EXT_LIB_CHECK([LTDL], [ltdl], [lt_dlinit], [ltdl.h], [])
+
+AST_EXT_LIB_CHECK([MISDN], [mISDN], [mISDN_open], [mISDNuser/mISDNlib.h])
+
+if test "${PBX_MISDN}" = 1; then
+ AST_EXT_LIB_CHECK([ISDNNET], [isdnnet], [init_manager], [mISDNuser/isdn_net.h], [-lmISDN -lpthread])
+ AST_EXT_LIB_CHECK([SUPPSERV], [suppserv], [encodeFac], [mISDNuser/suppserv.h])
+ AST_C_DEFINE_CHECK([MISDN_FAC_RESULT], [Fac_RESULT], [mISDNuser/suppserv.h])
+ AST_C_DEFINE_CHECK([MISDN_FAC_ERROR], [Fac_ERROR], [mISDNuser/suppserv.h])
+ AC_CHECK_HEADER([linux/mISDNdsp.h], [AC_DEFINE_UNQUOTED([MISDN_1_2], 1, [Build chan_misdn for mISDN 1.2 or later.])])
+fi
+
+AST_EXT_LIB_CHECK([NBS], [nbs], [nbs_connect], [nbs.h])
+
+AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h])
+
+AST_EXT_TOOL_CHECK([NETSNMP], [net-snmp], , [--agent-libs],
+[#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>],
+[int callback = snmp_register_callback(0, 0, NULL, NULL)])
+
+AST_EXT_LIB_CHECK([NEWT], [newt], [newtBell], [newt.h])
+
+AST_EXT_LIB_CHECK([UNIXODBC], [odbc], [SQLConnect], [sql.h], [])
+
+AST_EXT_LIB_CHECK([OGG], [ogg], [ogg_sync_init], [])
+
+AST_EXT_LIB_CHECK([BKTR], [execinfo], [backtrace], [execinfo.h])
+
+# possible places for oss definitions
+AST_EXT_LIB_CHECK([OSS], [ossaudio], [], [linux/soundcard.h])
+AST_EXT_LIB_CHECK([OSS], [ossaudio], [], [sys/soundcard.h])
+AST_EXT_LIB_CHECK([OSS], [ossaudio], [oss_ioctl_mixer], [soundcard.h])
+
+PG_CONFIG=No
+if test "${USE_PGSQL}" != "no"; then
+ if test "x${PGSQL_DIR}" != "x"; then
+ AC_PATH_TOOL([PG_CONFIG], [pg_config], No, [${PGSQL_DIR}/bin])
+ if test x"${PG_CONFIG}" = xNo; then
+ AC_MSG_NOTICE([***])
+ AC_MSG_NOTICE([*** pg_config was not found in the path you specified:])
+ AC_MSG_NOTICE([*** ${PGSQL_DIR}/bin])
+ AC_MSG_NOTICE([*** Either correct the installation, or run configure])
+ AC_MSG_NOTICE([*** including --without-postgres])
+ exit 1
+ fi
+ else
+ AC_PATH_TOOL([PG_CONFIG], [pg_config], No)
+ fi
+fi
+if test "${PG_CONFIG}" != No; then
+ PGSQL_libdir=`${PG_CONFIG} --libdir`
+ PGSQL_includedir=`${PG_CONFIG} --includedir`
+ if test "x$?" != "x0" ; then
+ if test -n "${PGSQL_MANDATORY}" ; then
+ AC_MSG_NOTICE([***])
+ AC_MSG_NOTICE([*** The PostgreSQL installation on this system appears to be broken.])
+ AC_MSG_NOTICE([*** Either correct the installation, or run configure])
+ AC_MSG_NOTICE([*** including --without-postgres])
+ exit 1
+ fi
+ else
+ AC_CHECK_LIB([pq], [PQescapeStringConn], AC_DEFINE_UNQUOTED([HAVE_PGSQL], 1,
+ [Define to indicate the PostgreSQL library]), [], -L${PGSQL_libdir} -lz)
+
+ if test "${ac_cv_lib_pq_PQescapeStringConn}" = "yes"; then
+ PGSQL_LIB="-L${PGSQL_libdir} -lpq -lz"
+ PGSQL_INCLUDE="-I${PGSQL_includedir}"
+ PBX_PGSQL=1
+ elif test -n "${PGSQL_MANDATORY}";
+ then
+ AC_MSG_NOTICE([***])
+ AC_MSG_NOTICE([*** The PostgreSQL installation on this system appears to be broken.])
+ AC_MSG_NOTICE([*** Either correct the installation, or run configure])
+ AC_MSG_NOTICE([*** including --without-postgres])
+ exit 1
+ fi
+ fi
+fi
+
+AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h])
+
+AST_EXT_LIB_CHECK([PORTAUDIO], [portaudio], [Pa_GetDeviceCount], [portaudio.h])
+
+AST_EXT_LIB_CHECK([PRI], [pri], [pri_new_bri], [libpri.h])
+
+AST_EXT_LIB_CHECK([SS7], [ss7], [isup_cqr], [libss7.h])
+
+if test "${USE_PWLIB}" != "no"; then
+ if test -n "${PWLIB_DIR}"; then
+ PWLIBDIR="${PWLIB_DIR}"
+ fi
+ AST_CHECK_PWLIB()
+ AST_CHECK_PWLIB_VERSION([PWLib], [PWLIB], [ptbuildopts.h], [1], [9], [2])
+
+ if test "${HAS_PWLIB:-unset}" != "unset"; then
+ AST_CHECK_OPENH323_PLATFORM()
+
+ PLATFORM_PWLIB="pt_${PWLIB_PLATFORM}_r"
+
+ AST_CHECK_PWLIB_BUILD([PWLib], [PWLIB],
+ [Define if your system has the PWLib libraries.],
+ [#include "ptlib.h"],
+ [BOOL q = PTime::IsDaylightSavings();])
+ fi
+fi
+
+if test "${PBX_PWLIB}" = "1" -a "${USE_OPENH323}" != "no" ; then
+ if test -n "${OPENH323_DIR}"; then
+ OPENH323DIR="${OPENH323_DIR}"
+ fi
+ AST_CHECK_OPENH323()
+ AST_CHECK_PWLIB_VERSION([OpenH323], [OPENH323], [openh323buildopts.h], [1], [17], [3])
+ AST_CHECK_OPENH323_BUILD()
+ PLATFORM_OPENH323="h323_${PWLIB_PLATFORM}_${OPENH323_SUFFIX}"
+ AST_CHECK_PWLIB_BUILD([OpenH323], [OPENH323],
+ [Define if your system has the OpenH323 libraries.],
+ [#include "ptlib.h"
+ #include "h323.h"
+ #include "h323ep.h"],
+ [H323EndPoint ep = H323EndPoint();],
+ [${PWLIB_INCLUDE}], [${PWLIB_LIB}])
+fi
+
+LUA_INCLUDE="-I/usr/include/lua5.1"
+LUA_LIB="-llua5.1"
+AST_EXT_LIB_CHECK([LUA], [lua5.1], [luaL_newstate], [lua5.1/lua.h])
+
+AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h])
+
+AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm])
+
+AST_EXT_LIB_CHECK([SPEEXDSP], [speexdsp], [speex_preprocess_ctl], [speex/speex.h], [-lm])
+
+AST_EXT_LIB_CHECK([SQLITE], [sqlite], [sqlite_exec], [sqlite.h])
+
+AST_EXT_LIB_CHECK([SQLITE3], [sqlite3], [sqlite3_open], [sqlite3.h])
+
+AST_EXT_LIB_CHECK([CRYPTO], [crypto], [AES_encrypt], [openssl/aes.h])
+
+if test "$PBX_CRYPTO" = "1";
+then
+ AST_EXT_LIB_CHECK([OPENSSL], [ssl], [ssl2_connect], [openssl/ssl.h], [-lcrypto])
+fi
+
+if test "$PBX_OPENSSL" = "1";
+then
+ AST_EXT_LIB_CHECK([OSPTK], [osptk], [OSPPCryptoDecrypt], [osp/osp.h], [-lcrypto -lssl])
+fi
+
+AST_EXT_LIB_CHECK([FREETDS], [tds], [tds_version], [tds.h])
+if test "${PBX_FREETDS}" != "0";
+then
+ case `grep TDS_VERSION_NO ${FREETDS_DIR:-/usr}/include/tdsver.h` in
+ *0.64*)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_64"
+ ;;
+ *0.63*)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_63"
+ ;;
+ *0.62*)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_62"
+ ;;
+ *)
+ FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_PRE_0_62"
+ ;;
+ esac
+fi
+
+AST_EXT_LIB_CHECK([TERMCAP], [termcap], [tgetent], [])
+
+AST_EXT_LIB_CHECK([TINFO], [tinfo], [tgetent], [])
+
+if test "${host_os}" != "linux-gnu" ; then
+ tonezone_extra="-lm"
+fi
+
+# new tonezone, version 1.4.0
+AST_EXT_LIB_CHECK([TONEZONE], [tonezone], [tone_zone_find], [zaptel/tonezone.h], [${tonezone_extra}], [], [140])
+# other case, old tonezone (0.80)
+AST_EXT_LIB_CHECK([TONEZONE], [tonezone], [tone_zone_find], [zaptel/zaptel.h], [${tonezone_extra}], [], [80])
+
+AST_EXT_LIB_CHECK([USB], [usb], [usb_init], [usb.h], [])
+
+AST_EXT_LIB_CHECK([VORBIS], [vorbis], [vorbis_info_init], [vorbis/codec.h], [-lm -lvorbisenc])
+
+AC_LANG_PUSH(C++)
+
+if test "${USE_VPB}" != "no"; then
+ AC_MSG_CHECKING(for vpb_open in -lvpb)
+ saved_libs="${LIBS}"
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${VPB_DIR}" != "x"; then
+ if test -d ${VPB_DIR}/lib; then
+ vpblibdir=${VPB_DIR}/lib
+ else
+ vpblibdir=${VPB_DIR}
+ fi
+ LIBS="${LIBS} -L${vpblibdir}"
+ CPPFLAGS="${CPPFLAGS} -I${VPB_DIR}/include"
+ fi
+ LIBS="${LIBS} -lvpb -lpthread"
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [#include <vpbapi.h>],
+ [int q = vpb_open(0,0);])
+ ],
+ [ AC_MSG_RESULT(yes)
+ ac_cv_lib_vpb_vpb_open="yes"
+ ],
+ [ AC_MSG_RESULT(no)
+ ac_cv_lib_vpb_vpb_open="no"
+ ]
+ )
+ LIBS="${saved_libs}"
+ CPPFLAGS="${saved_cppflags}"
+ if test "${ac_cv_lib_vpb_vpb_open}" = "yes"; then
+ VPB_LIB="-lvpb"
+ if test "${VPB_DIR}" != ""; then
+ VPB_LIB="-L${vpblibdir} ${VPB_LIB}"
+ VPB_INCLUDE="-I${VPB_DIR}/include"
+ fi
+ PBX_VPB=1
+ AC_DEFINE([HAVE_VPB], 1, [Define if your system has the VoiceTronix API libraries.])
+ fi
+fi
+
+AC_LANG_POP
+
+AST_EXT_LIB_CHECK([ZLIB], [z], [compress], [zlib.h])
+
+# Check for various zaptel features and locations.
+# The version number, which goes into HAVE_ZAPTEL_VERSION,
+# will be used in the system headers to determine the location
+# of the zaptel.h header.
+
+AST_C_DEFINE_CHECK([ZAPTEL], [ZT_TONE_DTMF_BASE], [zaptel/zaptel.h], [140])
+AST_C_DEFINE_CHECK([ZAPTEL], [ZT_DIAL_OP_CANCEL], [zaptel/zaptel.h], [90])
+
+# Check for VLDTMF support
+AST_C_DEFINE_CHECK([ZAPTEL_VLDTMF], [ZT_EVENT_REMOVED], [zaptel/zaptel.h])
+
+# Check for echo canceler parameters support
+AST_C_DEFINE_CHECK([ZAPTEL_ECHOCANPARAMS], [ZT_ECHOCANCEL_PARAMS], [zaptel/zaptel.h])
+
+# Check for transcoder support
+AST_C_DEFINE_CHECK([ZAPTEL_TRANSCODE], [ZT_TCOP_ALLOCATE], [zaptel/zaptel.h])
+
+# Check for hwgain support
+AST_C_DEFINE_CHECK([ZAPTEL_HWGAIN], [ZT_SET_HWGAIN], [zaptel/zaptel.h])
+
+# Check for channel alarm support
+AST_C_COMPILE_CHECK([ZAPTEL_CHANALARMS], [size_t foo = sizeof(struct zt_params_v1)], [zaptel/zaptel.h])
+
+# On FreeBSD, try old zaptel (0.80 or so) and pretend we have vldtmf
+case "${host_os}" in
+ freebsd*)
+ AST_EXT_LIB_CHECK([ZAPTEL], [zaptel],, [zaptel.h],,, [80])
+ AST_EXT_LIB_CHECK([ZAPTEL_VLDTMF], [zaptel],, [zaptel/zaptel.h],,, [90])
+ AST_EXT_LIB_CHECK([ZAPTEL_VLDTMF], [zaptel],, [zaptel.h],,, [80])
+
+ # other case, old tonezone (0.80)
+ AST_EXT_LIB_CHECK([TONEZONE], [tonezone], [tone_zone_find], [zaptel.h], [${tonezone_extra}],, [80])
+ ;;
+esac
+
+EDITLINE_LIB=""
+if test "x$TERMCAP_LIB" != "x" ; then
+ EDITLINE_LIB="$TERMCAP_LIB"
+elif test "x$TINFO_LIB" != "x" ; then
+ EDITLINE_LIB="$TINFO_LIB"
+elif test "x$CURSES_LIB" != "x" ; then
+ EDITLINE_LIB="$CURSES_LIB"
+elif test "x$NCURSES_LIB" != "x" ; then
+ EDITLINE_LIB="$NCURSES_LIB"
+else
+ AC_MSG_ERROR(*** termcap support not found)
+fi
+AC_SUBST(EDITLINE_LIB)
+
+AC_CHECK_HEADER([h323.h], [PBX_H323=1], [PBX_H323=0])
+AC_SUBST(PBX_H323)
+
+AC_CHECK_HEADER([linux/compiler.h],
+ [AC_DEFINE_UNQUOTED([HAVE_LINUX_COMPILER_H], 1, [Define to 1 if your system has linux/compiler.h.])])
+
+AC_CHECK_HEADER([linux/ixjuser.h], [PBX_IXJUSER=1], [PBX_IXJUSER=0], [
+ #include <linux/version.h>
+ #ifdef HAVE_LINUX_COMPILER_H
+ #include <linux/compiler.h>
+ #endif
+ ])
+AC_SUBST(PBX_IXJUSER)
+
+AST_EXT_TOOL_CHECK([SDL], [sdl])
+AST_EXT_LIB_CHECK([SDL_IMAGE], [SDL_image], [IMG_Load], [SDL_image.h], [${SDL_LIB}], [${SDL_INCLUDE}])
+AST_EXT_LIB_CHECK([FFMPEG], [avcodec], [sws_getContext], [ffmpeg/avcodec.h], [-lpthread -lz -lm])
+
+# possible places for video4linux version 1
+AC_CHECK_HEADER([linux/videodev.h],
+ [AC_DEFINE_UNQUOTED([HAVE_VIDEODEV_H], 1, [Define to 1 if your system has linux/videodev.h.])])
+
+# possible places for X11
+AST_EXT_LIB_CHECK([X11], [X11], [XOpenDisplay], [X11/Xlib.h],,, [standard_path])
+AST_EXT_LIB_CHECK([X11], [X11], [XOpenDisplay], [X11/Xlib.h],, [-I/usr/X11R6/include], [X11R6])
+
+PBX_GTK=0
+AST_EXT_TOOL_CHECK([GTK], [gtk], [--cflags gthread], [--libs gthread])
+
+PBX_GTK2=0
+AC_CHECK_TOOL(PKGCONFIG, pkg-config, No)
+if test ! "x${PKGCONFIG}" = xNo; then
+ GTK2_INCLUDE=$(${PKGCONFIG} gtk+-2.0 --cflags 2>/dev/null)
+ GTK2_LIB=$(${PKGCONFIG} gtk+-2.0 --libs)
+ PBX_GTK2=1
+ AC_DEFINE([HAVE_GTK2], 1, [Define if your system has the GTK2 libraries.])
+fi
+AC_SUBST(PBX_GTK2)
+AC_SUBST(GTK2_INCLUDE)
+AC_SUBST(GTK2_LIB)
+
+if test "${USE_CURL}" != "no"; then
+ AC_PATH_TOOL([CURL_CONFIG], [curl-config], No)
+ if test ! x"${CURL_CONFIG}" = xNo; then
+ # check for version
+ if test $(printf "%d" 0x$(${CURL_CONFIG} --vernum)) -ge $(printf "%d" 0x070907); then
+ CURL_INCLUDE=$(${CURL_CONFIG} --cflags)
+ CURL_LIB=$(${CURL_CONFIG} --libs)
+
+ AC_MSG_CHECKING(for curl_version() in curl/curl.h)
+ saved_cppflags="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CURL_INCLUDE}"
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [#include <curl/curl.h>],
+ [curl_version();])
+ ],[
+ AC_MSG_RESULT(yes)
+ ac_cv_curl_h="yes"
+ ],[
+ AC_MSG_RESULT(no)
+ ac_cv_curl_h="no"
+ ]
+ )
+ CPPFLAGS="${saved_cppflags}"
+ if test "${ac_cv_curl_h}" = "yes"; then
+ PBX_CURL=1
+ AC_DEFINE([HAVE_CURL], 1, [Define if your system has the curl libraries.])
+ fi
+ fi
+ fi
+fi
+
+AC_CONFIG_FILES([build_tools/menuselect-deps makeopts channels/h323/Makefile])
+AST_CHECK_MANDATORY
+
+AC_OUTPUT
+
+if test "x${silent}" != "xyes" ; then
+echo
+echo " .\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$=.. "
+echo " .\$7\$7.. .7\$\$7:. "
+echo " .\$\$:. ,\$7.7 "
+echo " .\$7. 7\$\$\$\$ .\$\$77 "
+echo " ..\$\$. \$\$\$\$\$ .\$\$\$7 "
+echo " ..7\$ .?. \$\$\$\$\$ .?. 7\$\$\$."
+echo " \$.\$. .\$\$\$7. \$\$\$\$7 .7\$\$\$. .\$\$\$."
+echo " .777. .\$\$\$\$\$\$77\$\$\$77\$\$\$\$\$7. \$\$\$,"
+echo " \$\$\$~ .7\$\$\$\$\$\$\$\$\$\$\$\$\$7. .\$\$\$."
+echo ".\$\$7 .7\$\$\$\$\$\$\$7: ?\$\$\$."
+echo "\$\$\$ ?7\$\$\$\$\$\$\$\$\$\$I .\$\$\$7 "
+echo "\$\$\$ .7\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$ :\$\$\$. "
+echo "\$\$\$ \$\$\$\$\$\$7\$\$\$\$\$\$\$\$\$\$\$\$ .\$\$\$. "
+echo "\$\$\$ \$\$\$ 7\$\$\$7 .\$\$\$ .\$\$\$. "
+echo "\$\$\$\$ \$\$\$\$7 .\$\$\$. "
+echo "7\$\$\$7 7\$\$\$\$ 7\$\$\$ "
+echo " \$\$\$\$\$ \$\$\$ "
+echo " \$\$\$\$7. \$\$ (TM) "
+echo " \$\$\$\$\$\$\$. .7\$\$\$\$\$\$ \$\$ "
+echo " \$\$\$\$\$\$\$\$\$\$\$\$7\$\$\$\$\$\$\$\$\$.\$\$\$\$\$\$ "
+echo " \$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$. "
+echo
+fi
+
+AC_MSG_NOTICE(Package configured for: )
+AC_MSG_NOTICE( OS type : $host_os)
+AC_MSG_NOTICE( Host CPU : $host_cpu)
+AC_MSG_NOTICE( build-cpu:vendor:os: $build_cpu : $build_vendor : $build_os :)
+AC_MSG_NOTICE( host-cpu:vendor:os: $host_cpu : $host_vendor : $host_os :)
+if test "${cross_compiling}" = "yes"; then
+ AC_MSG_NOTICE( Cross Compilation = YES)
+fi
diff --git a/trunk/contrib/README.festival b/trunk/contrib/README.festival
new file mode 100644
index 000000000..24912827c
--- /dev/null
+++ b/trunk/contrib/README.festival
@@ -0,0 +1,47 @@
+
+app_festival is an application that allows one to send text-to-speech commands
+to a background festival server, and to obtain the resulting waveform which
+gets sent down to the respective channel. app_festival also employs a waveform
+cache, so invariant text-to-speech strings ("Please press 1 for instructions")
+do not need to be dynamically generated all the time.
+
+You need :
+
+1) festival, patched to produce 8khz waveforms on output. Patch for Festival
+1.4.2 RELEASE are included. The patch adds a new command to festival
+(asterisk_tts).
+
+It is possible to run Festival without patches in the source-code. Just
+add this to your /etc/festival.scm or /usr/share/festival/festival/scm:
+
+ (define (tts_textasterisk string mode)
+ "(tts_textasterisk STRING MODE)
+ Apply tts to STRING. This function is specifically designed for
+ use in server mode so a single function call may synthesize the string.
+ This function name may be added to the server safe functions."
+ (let ((wholeutt (utt.synth (eval (list 'Utterance 'Text string)))))
+ (utt.wave.resample wholeutt 8000)
+ (utt.wave.rescale wholeutt 5)
+ (utt.send.wave.client wholeutt)))
+
+[See the comment with subject "Using Debian
+ festival >= 1.4.3-15 (no recompiling needed!)" on
+ http://www.voip-info.org/wiki-Asterisk+festival+installation for the
+ original mentioning of it]
+
+2) You may wish to obtain and install the asterisk-perl
+module by James Golovich <james@gnuinter.net>, from
+either CPAN, or his site: http://asterisk.gnuinter.net,
+as this contains a good example of how variable text
+can be tts'd via asterisk, namely the examples/tts-*.agi
+files there. It has been noted that the current expression
+evaluation capabilities of asterisk are not best suited
+for the generation and manipulation of text. AGI scripting
+can be ideal for these sorts of needs. For simpler usage,
+fixed, pre-recorded messages may be more amenable for your
+purposes.
+
+3) Before running asterisk, you have to run festival-server with a command
+like :
+
+/usr/local/festival/bin/festival --server > /dev/null 2>&1 &
diff --git a/trunk/contrib/asterisk-doxygen-header b/trunk/contrib/asterisk-doxygen-header
new file mode 100644
index 000000000..a8eebd6c3
--- /dev/null
+++ b/trunk/contrib/asterisk-doxygen-header
@@ -0,0 +1,10 @@
+<HTML>
+ <HEAD>
+ <TITLE>Asterisk.org: Developer Documentation ($date)</TITLE>
+ <LINK HREF="doxygen.css" REL="stylesheet" TYPE="text/css">
+ </HEAD>
+ <BODY BGCOLOR="#FFFFFF">
+<div><font size="2" align="right">$datetime</font></div>
+
+<h2>Asterisk developer's documentation</h2>
+<hr/>
diff --git a/trunk/contrib/asterisk-ices.xml b/trunk/contrib/asterisk-ices.xml
new file mode 100644
index 000000000..abc028c75
--- /dev/null
+++ b/trunk/contrib/asterisk-ices.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<ices>
+
+ <!-- run in background -->
+ <background>0</background>
+ <!-- where logs go. -->
+ <logpath>/var/log/ices</logpath>
+ <logfile>ices.log</logfile>
+ <!-- 1=error, 2=warn, 3=infoa ,4=debug -->
+ <loglevel>4</loglevel>
+ <!-- logfile is ignored if this is set to 1 -->
+ <consolelog>0</consolelog>
+
+ <!-- optional filename to write process id to -->
+ <!-- <pidfile>/home/ices/ices.pid</pidfile> -->
+
+ <stream>
+ <!-- metadata used for stream listing -->
+ <metadata>
+ <name>Example stream name</name>
+ <genre>Example genre</genre>
+ <description>A short description of your stream</description>
+ <url>http://mysite.org</url>
+ </metadata>
+
+ <!-- Input module.
+
+ This example uses the 'oss' module. It takes input from the
+ OSS audio device (e.g. line-in), and processes it for live
+ encoding. -->
+ <input>
+ <module>stdinpcm</module>
+ <param name="rate">8000</param>
+ <param name="channels">1</param>
+ <!-- Read metadata (from stdin by default, or -->
+ <!-- filename defined below (if the latter, only on SIGUSR1) -->
+ <param name="metadata">1</param>
+ <param name="metadatafilename">test</param>
+ </input>
+
+ <!-- Stream instance.
+
+ You may have one or more instances here. This allows you to
+ send the same input data to one or more servers (or to different
+ mountpoints on the same server). Each of them can have different
+ parameters. This is primarily useful for a) relaying to multiple
+ independent servers, and b) encoding/reencoding to multiple
+ bitrates.
+
+ If one instance fails (for example, the associated server goes
+ down, etc), the others will continue to function correctly.
+ This example defines a single instance doing live encoding at
+ low bitrate. -->
+
+ <instance>
+ <!-- Server details.
+
+ You define hostname and port for the server here, along
+ with the source password and mountpoint. -->
+
+ <hostname>localhost</hostname>
+ <port>8000</port>
+ <password>temppass</password>
+ <mount>/example.ogg</mount>
+ <yp>1</yp> <!-- allow stream to be advertised on YP, default 0 -->
+
+ <!-- Live encoding/reencoding:
+
+ channels and samplerate currently MUST match the channels
+ and samplerate given in the parameters to the oss input
+ module above or the remsaple/downmix section below. -->
+
+ <encode>
+ <quality>0</quality>
+ <samplerate>8000</samplerate>
+ <channels>1</channels>
+ </encode>
+
+ <!-- stereo->mono downmixing, enabled by setting this to 1 -->
+ <downmix>0</downmix>
+
+ <!-- resampling.
+
+ Set to the frequency (in Hz) you wish to resample to, -->
+
+ <!-- <resample>
+ <in-rate>44100</in-rate>
+ <out-rate>22050</out-rate>
+ </resample> -->
+ </instance>
+
+ </stream>
+</ices>
diff --git a/trunk/contrib/asterisk-ng-doxygen b/trunk/contrib/asterisk-ng-doxygen
new file mode 100644
index 000000000..f1259eea2
--- /dev/null
+++ b/trunk/contrib/asterisk-ng-doxygen
@@ -0,0 +1,1274 @@
+# Doxyfile 1.5.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "Asterisk - the Open Source PBX"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/api
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 3
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES = "extref=\xrefitem extref \"ExtRef\" \"External references\""
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 5
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT =
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = ./ \
+ main \
+ include \
+ include/asterisk \
+ channels \
+ channels/misdn \
+ funcs \
+ main/stdtime \
+ apps \
+ cdr \
+ codecs \
+ formats \
+ pbx \
+ agi \
+ res
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS = *.c \
+ *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH = ./ \
+ doc \
+ configs
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = images
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT =
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER = contrib/asterisk-doxygen-header
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT =
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT =
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = __GNUC__
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen will always
+# show the root nodes and its direct children regardless of this setting.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/trunk/contrib/dictionary.digium b/trunk/contrib/dictionary.digium
new file mode 100644
index 000000000..694605f54
--- /dev/null
+++ b/trunk/contrib/dictionary.digium
@@ -0,0 +1,31 @@
+#
+# Digium's Asterisk specific radius attributes
+# markster@digium.com
+#
+#
+
+VENDOR Digium 22736
+
+BEGIN-VENDOR Digium
+
+ATTRIBUTE Asterisk-Acc-Code 101 string Digium
+ATTRIBUTE Asterisk-Src 102 string Digium
+ATTRIBUTE Asterisk-Dst 103 string Digium
+ATTRIBUTE Asterisk-Dst-Ctx 104 string Digium
+ATTRIBUTE Asterisk-Clid 105 string Digium
+ATTRIBUTE Asterisk-Chan 106 string Digium
+ATTRIBUTE Asterisk-Dst-Chan 107 string Digium
+ATTRIBUTE Asterisk-Last-App 108 string Digium
+ATTRIBUTE Asterisk-Last-Data 109 string Digium
+ATTRIBUTE Asterisk-Start-Time 110 string Digium
+ATTRIBUTE Asterisk-Answer-Time 111 string Digium
+ATTRIBUTE Asterisk-End-Time 112 string Digium
+ATTRIBUTE Asterisk-Duration 113 integer Digium
+ATTRIBUTE Asterisk-Bill-Sec 114 integer Digium
+ATTRIBUTE Asterisk-Disposition 115 string Digium
+ATTRIBUTE Asterisk-AMA-Flags 116 string Digium
+ATTRIBUTE Asterisk-Unique-ID 117 string Digium
+ATTRIBUTE Asterisk-User-Field 118 string Digium
+
+END-VENDOR Digium
+
diff --git a/trunk/contrib/festival-1.4.1-diff b/trunk/contrib/festival-1.4.1-diff
new file mode 100644
index 000000000..23702a3a1
--- /dev/null
+++ b/trunk/contrib/festival-1.4.1-diff
@@ -0,0 +1,76 @@
+diff -ruN festival/lib/tts.scm myfestival/lib/tts.scm
+--- festival/lib/tts.scm Sun May 30 16:40:00 1999
++++ myfestival/lib/tts.scm Wed Apr 17 22:29:34 2002
+@@ -200,6 +200,15 @@
+ (utt.synth
+ (eval (list 'Utterance 'Text string)))))
+
++(define (tts_textasterisk string mode)
++ "(tts_textasterisk STRING MODE)
++Apply tts to STRING. This function is specifically designed for
++use in server mode so a single function call may synthesize the string.
++This function name maybe added to the server safe functions."
++ (utt.send.wave.asterisk
++ (utt.synth
++ (eval (list 'Utterance 'Text string)))))
++
+ (define (tts_return_to_client)
+ "(tts_return_to_client)
+ This function is called by clients who wish to return waveforms of
+diff -ruN festival/src/arch/festival/wave.cc myfestival/src/arch/festival/wave.cc
+--- festival/src/arch/festival/wave.cc Sat Jun 12 10:30:30 1999
++++ myfestival/src/arch/festival/wave.cc Thu Apr 18 10:55:32 2002
+@@ -375,6 +375,38 @@
+ type = "nist";
+ else
+ type = get_c_string(ltype);
++
++ w->save(tmpfile,type);
++ write(ft_server_socket,"WV\n",3);
++ socket_send_file(ft_server_socket,tmpfile);
++ unlink(tmpfile);
++
++ return utt;
++}
++
++static LISP utt_send_wave_asterisk(LISP utt)
++{
++ // Send the waveform to a client (must be acting as server)
++ EST_Utterance *u = utterance(utt);
++ EST_Wave *w;
++ EST_String tmpfile = make_tmp_filename();
++ LISP ltype;
++ EST_String type;
++
++ w = get_utt_wave(u);
++ if (ft_server_socket == -1)
++ {
++ cerr << "utt_send_wave_client: not in server mode" << endl;
++ festival_error();
++ }
++
++ ltype = ft_get_param("Wavefiletype");
++ if (ltype == NIL)
++ type = "nist";
++ else
++ type = get_c_string(ltype);
++ w->resample(8000);
++ w->rescale(5);
+ w->save(tmpfile,type);
+ write(ft_server_socket,"WV\n",3);
+ socket_send_file(ft_server_socket,tmpfile);
+@@ -434,6 +466,13 @@
+ "(utt.send.wave.client UTT)\n\
+ Sends wave in UTT to client. If not in server mode gives an error\n\
+ Note the client must be expecting to receive the waveform.");
++
++ init_subr_1("utt.send.wave.asterisk",utt_send_wave_asterisk,
++ "(utt.send.wave.asterisk UTT)\n\
++ Sends wave in UTT to client. If not in server mode gives an error\n\
++ Note the client must be expecting to receive the waveform. The waveform\n\
++ is rescaled and resampled according to what asterisk needs");
++
+ init_subr_2("utt.save.f0",utt_save_f0,
+ "(utt.save.f0 UTT FILENAME)\n\
+ Save F0 of UTT as esps track file in FILENAME.");
+
diff --git a/trunk/contrib/festival-1.4.2.diff b/trunk/contrib/festival-1.4.2.diff
new file mode 100644
index 000000000..d5d1e5d54
--- /dev/null
+++ b/trunk/contrib/festival-1.4.2.diff
@@ -0,0 +1,75 @@
+diff -u -r festival-1.4.2/lib/tts.scm festival-1.4.2-asterisk/lib/tts.scm
+--- festival-1.4.2/lib/tts.scm Wed Jan 8 09:54:14 2003
++++ festival-1.4.2-asterisk/lib/tts.scm Tue Jan 7 08:51:44 2003
+@@ -236,6 +236,15 @@
+ (utt.synth
+ (eval (list 'Utterance 'Text string))))))
+
++(define (tts_textasterisk string mode)
++ "(tts_textasterisk STRING MODE)
++Apply tts to STRING. This function is specifically designed for
++use in server mode so a single function call may synthesize the string.
++This function name may be added to the server safe functions."
++ (utt.send.wave.asterisk
++ (utt.synth
++ (eval (list 'Utterance 'Text string)))))
++
+ (define (tts_return_to_client)
+ "(tts_return_to_client)
+ This function is called by clients who wish to return waveforms of
+diff -u -r festival-1.4.2/src/arch/festival/wave.cc festival-1.4.2-asterisk/src/arch/festival/wave.cc
+--- festival-1.4.2/src/arch/festival/wave.cc Mon Jun 4 07:40:10 2001
++++ festival-1.4.2-asterisk/src/arch/festival/wave.cc Tue Jan 7 08:53:09 2003
+@@ -377,6 +377,38 @@
+ type = "nist";
+ else
+ type = get_c_string(ltype);
++
++ w->save(tmpfile,type);
++ write(ft_server_socket,"WV\n",3);
++ socket_send_file(ft_server_socket,tmpfile);
++ unlink(tmpfile);
++
++ return utt;
++}
++
++static LISP utt_send_wave_asterisk(LISP utt)
++{
++ // Send the waveform to a client (must be acting as server)
++ EST_Utterance *u = utterance(utt);
++ EST_Wave *w;
++ EST_String tmpfile = make_tmp_filename();
++ LISP ltype;
++ EST_String type;
++
++ w = get_utt_wave(u);
++ if (ft_server_socket == -1)
++ {
++ cerr << "utt_send_wave_client: not in server mode" << endl;
++ festival_error();
++ }
++
++ ltype = ft_get_param("Wavefiletype");
++ if (ltype == NIL)
++ type = "nist";
++ else
++ type = get_c_string(ltype);
++ w->resample(8000);
++ w->rescale(5);
+ w->save(tmpfile,type);
+ write(ft_server_socket,"WV\n",3);
+ socket_send_file(ft_server_socket,tmpfile);
+@@ -454,6 +486,13 @@
+ "(utt.send.wave.client UTT)\n\
+ Sends wave in UTT to client. If not in server mode gives an error\n\
+ Note the client must be expecting to receive the waveform.");
++
++ init_subr_1("utt.send.wave.asterisk",utt_send_wave_asterisk,
++ "(utt.send.wave.asterisk UTT)\n\
++ Sends wave in UTT to client. If not in server mode gives an error\n\
++ Note the client must be expecting to receive the waveform. The waveform\n\
++ is rescaled and resampled according to what asterisk needs");
++
+ init_subr_1("send_sexpr_to_client", send_sexpr_to_client,
+ "(send_sexpr_to_client SEXPR)\n\
+ Sends given sexpression to currently connected client.");
diff --git a/trunk/contrib/festival-1.4.3.diff b/trunk/contrib/festival-1.4.3.diff
new file mode 100644
index 000000000..13a9d92b8
--- /dev/null
+++ b/trunk/contrib/festival-1.4.3.diff
@@ -0,0 +1,93 @@
+diff -u -r festival-1.4.3/lib/tts.scm festival-1.4.3-asterisk/lib/tts.scm
+--- festival-1.4.3/lib/tts.scm 2003-01-09 07:39:22.000000000 -0800
++++ festival-1.4.3-asterisk/lib/tts.scm 2003-08-14 12:07:00.000000000 -0700
+@@ -234,6 +234,17 @@
+ (utt.synth
+ (eval (list 'Utterance 'Text string))))))
+
++;; begin tts_textasterisk
++(define (tts_textasterisk string mode)
++ "(tts_textasterisk STRING MODE)
++Apply tts to STRING. This function is specifically designed for
++use in server mode so a single function call may synthesize the string.
++This function name may be added to the server safe functions."
++ (utt.send.wave.asterisk
++ (utt.synth
++ (eval (list 'Utterance 'Text string)))))
++;; end tts_textasterisk
++
+ (define (tts_return_to_client)
+ "(tts_return_to_client)
+ This function is called by clients who wish to return waveforms of
+diff -u -r festival-1.4.3/src/arch/festival/wave.cc festival-1.4.3-asterisk/src/arch/festival/wave.cc
+--- festival-1.4.3/src/arch/festival/wave.cc 2003-01-13 11:09:55.000000000 -0800
++++ festival-1.4.3-asterisk/src/arch/festival/wave.cc 2003-08-14 12:10:53.000000000 -0700
+@@ -381,6 +381,7 @@
+ type = "nist";
+ else
+ type = get_c_string(ltype);
++
+ w->save(tmpfile,type);
+ #ifdef WIN32
+ send(ft_server_socket,"WV\n",3,0);
+@@ -393,6 +394,44 @@
+ return utt;
+ }
+
++// begin utt_send_wave_asterisk()
++static LISP utt_send_wave_asterisk(LISP utt)
++{
++ // Send the waveform to a client (must be acting as server)
++ EST_Utterance *u = utterance(utt);
++ EST_Wave *w;
++ EST_String tmpfile = make_tmp_filename();
++ LISP ltype;
++ EST_String type;
++
++ w = get_utt_wave(u);
++ if (ft_server_socket == -1)
++ {
++ cerr << "utt_send_wave_client: not in server mode" << endl;
++ festival_error();
++ }
++
++ ltype = ft_get_param("Wavefiletype");
++ if (ltype == NIL)
++ type = "nist";
++ else
++ type = get_c_string(ltype);
++ w->resample(8000);
++ w->rescale(5);
++
++ w->save(tmpfile,type);
++#ifdef WIN32
++ send(ft_server_socket,"WV\n",3,0);
++#else
++ write(ft_server_socket,"WV\n",3);
++#endif
++ socket_send_file(ft_server_socket,tmpfile);
++ unlink(tmpfile);
++
++ return utt;
++}
++// end utt_send_wave_asterisk()
++
+ static LISP send_sexpr_to_client(LISP l)
+ {
+ EST_String tmpfile = make_tmp_filename();
+@@ -465,6 +504,15 @@
+ "(utt.send.wave.client UTT)\n\
+ Sends wave in UTT to client. If not in server mode gives an error\n\
+ Note the client must be expecting to receive the waveform.");
++
++// begin asterisk mod
++ init_subr_1("utt.send.wave.asterisk",utt_send_wave_asterisk,
++ "(utt.send.wave.asterisk UTT)\n\
++ Sends wave in UTT to client. If not in server mode gives an error\n\
++ Note the client must be expecting to receive the waveform. The waveform\n\
++ is rescaled and resampled according to what asterisk needs");
++// end asterisk mod
++
+ init_subr_1("send_sexpr_to_client", send_sexpr_to_client,
+ "(send_sexpr_to_client SEXPR)\n\
+ Sends given sexpression to currently connected client.");
diff --git a/trunk/contrib/festival-1.95.diff b/trunk/contrib/festival-1.95.diff
new file mode 100644
index 000000000..2035d7f0f
--- /dev/null
+++ b/trunk/contrib/festival-1.95.diff
@@ -0,0 +1,107 @@
+diff -ur festival-195orig/festival/lib/multisyn/multisyn_pauses.scm festival-195/festival/lib/multisyn/multisyn_pauses.scm
+--- festival-195orig/festival/lib/multisyn/multisyn_pauses.scm 2004-06-21 08:19:30.000000000 -0600
++++ festival-195/festival/lib/multisyn/multisyn_pauses.scm 2005-01-12 18:53:27.000000000 -0700
+@@ -85,8 +85,8 @@
+ (let ((silence (car (cadr (car (PhoneSet.description '(silences))))))
+ (seg (item.relation (find_last_seg word) 'Segment))
+ pause_item)
+- (format t " inserting pause after: %s.\n" (item.name seg))
+- (format t " Inserting pause\n")
++; (format t " inserting pause after: %s.\n" (item.name seg))
++; (format t " Inserting pause\n")
+ ; if next seg is not silence insert one.
+ (if (or (not (item.next seg))
+ (not (string-equal (item.name (item.next seg)) silence)))
+diff -ur festival-195orig/festival/lib/tts.scm festival-195/festival/lib/tts.scm
+--- festival-195orig/festival/lib/tts.scm 2003-04-20 10:42:28.000000000 -0600
++++ festival-195/festival/lib/tts.scm 2005-01-04 09:21:31.000000000 -0700
+@@ -235,6 +235,17 @@
+ (utt.synth
+ (eval (list 'Utterance 'Text string))))))
+
++;; begin tts_textasterisk
++(define (tts_textasterisk string mode)
++ "(tts_textasterisk STRING MODE)
++Apply tts to STRING. This function is specifically designed for
++use in server mode so a single function call may synthesize the string.
++This function name may be added to the server safe functions."
++ (utt.send.wave.asterisk
++ (utt.synth
++ (eval (list 'Utterance 'Text string)))))
++;; end tts_textasterisk
++
+ (define (tts_return_to_client)
+ "(tts_return_to_client)
+ This function is called by clients who wish to return waveforms of
+diff -ur festival-195orig/festival/src/arch/festival/wave.cc festival-195/festival/src/arch/festival/wave.cc
+--- festival-195orig/festival/src/arch/festival/wave.cc 2004-06-21 14:52:42.000000000 -0600
++++ festival-195/festival/src/arch/festival/wave.cc 2005-01-04 09:26:24.000000000 -0700
+@@ -482,6 +482,7 @@
+ type = "nist";
+ else
+ type = get_c_string(ltype);
++
+ w->save(tmpfile,type);
+ #ifdef WIN32
+ send(ft_server_socket,"WV\n",3,0);
+@@ -494,6 +495,44 @@
+ return utt;
+ }
+
++// begin utt_send_wave_asterisk()
++static LISP utt_send_wave_asterisk(LISP utt)
++{
++ // Send the waveform to a client (must be acting as server)
++ EST_Utterance *u = utterance(utt);
++ EST_Wave *w;
++ EST_String tmpfile = make_tmp_filename();
++ LISP ltype;
++ EST_String type;
++
++ w = get_utt_wave(u);
++ if (ft_server_socket == -1)
++ {
++ cerr << "utt_send_wave_asterisk: not in server mode" << endl;
++ festival_error();
++ }
++
++ ltype = ft_get_param("Wavefiletype");
++ if (ltype == NIL)
++ type = "nist";
++ else
++ type = get_c_string(ltype);
++ w->resample(8000);
++ w->rescale(5);
++
++ w->save(tmpfile,type);
++#ifdef WIN32
++ send(ft_server_socket,"WV\n",3,0);
++#else
++ write(ft_server_socket,"WV\n",3);
++#endif
++ socket_send_file(ft_server_socket,tmpfile);
++ unlink(tmpfile);
++
++ return utt;
++}
++// end utt_send_wave_asterisk()
++
+ static LISP send_sexpr_to_client(LISP l)
+ {
+ EST_String tmpfile = make_tmp_filename();
+@@ -595,6 +634,15 @@
+ "(utt.send.wave.client UTT)\n\
+ Sends wave in UTT to client. If not in server mode gives an error\n\
+ Note the client must be expecting to receive the waveform.");
++
++// begin asterisk mod
++ init_subr_1("utt.send.wave.asterisk",utt_send_wave_asterisk,
++ "(utt.send.wave.asterisk UTT)\n\
++ Sends wave in UTT to client. If not in server mode gives an error\n\
++ Note the client must be expecting to receive the waveform. The waveform\n\
++ is rescaled and resampled according to what asterisk needs");
++// end asterisk mod
++
+ init_subr_1("send_sexpr_to_client", send_sexpr_to_client,
+ "(send_sexpr_to_client SEXPR)\n\
+ Sends given sexpression to currently connected client.");
diff --git a/trunk/contrib/firmware/iax/iaxy.bin b/trunk/contrib/firmware/iax/iaxy.bin
new file mode 100644
index 000000000..6f06c4cdb
--- /dev/null
+++ b/trunk/contrib/firmware/iax/iaxy.bin
Binary files differ
diff --git a/trunk/contrib/i18n.testsuite.conf b/trunk/contrib/i18n.testsuite.conf
new file mode 100644
index 000000000..8c4d1f705
--- /dev/null
+++ b/trunk/contrib/i18n.testsuite.conf
@@ -0,0 +1,136 @@
+; Test Internationalisation of SayNumber()
+; #include this into a suitable context
+; English
+exten => 841,1,Answer
+exten => 841,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 841,3,SetLanguage(en)
+exten => 841,4,SayNumber(183) ; one hundred eighty three (NB UK English would say one hundred & eighty three)
+exten => 841,5,Wait,1
+exten => 841,6,SayUnixTime() ; Say current date & time in "ABdY 'digits/at' IMp" format
+; French
+exten => 842,1,Answer
+exten => 842,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 842,3,SetLanguage(fr)
+exten => 842,4,SayNumber(1) ; one
+exten => 842,5,Wait,1
+exten => 842,6,SayNumber(1,f) ; one (feminine)
+exten => 842,7,Wait,1
+exten => 842,8,SayNumber(181) ; hundred eighty three
+exten => 842,9,Wait,1
+exten => 842,10,SayNumber(281) ; two hundred eighty three
+exten => 842,11,Wait,1
+exten => 842,12,SayNumber(1061) ; thousand sixty three
+exten => 842,13,Wait,1
+exten => 842,14,SayNumber(2061) ; two thousand sixty three
+exten => 842,15,Wait,1
+exten => 842,16,SayUnixTime()
+; Spanish
+exten => 843,1,Answer
+exten => 843,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 843,3,SetLanguage(es)
+exten => 843,4,Playback(digits/hundred)
+exten => 843,5,Wait,1
+exten => 843,6,SayNumber(1) ; one
+exten => 843,7,Wait,1
+exten => 843,8,SayNumber(1,f) ; one (feminine)
+exten => 843,9,Wait,1
+exten => 843,10,SayNumber(11) ; "dieci uno"
+exten => 843,11,Wait,1
+exten => 843,12,SayNumber(21) ; "veinti uno"
+exten => 843,13,Wait,1
+exten => 843,14,SayNumber(31) ; "thirty & one"
+exten => 843,15,Wait,1
+exten => 843,16,SayNumber(100) ; "cien"
+exten => 843,17,Wait,1
+exten => 843,18,SayNumber(101) ; "ciento uno"
+exten => 843,19,Wait,1
+exten => 843,20,SayNumber(200) ; "twohundred"
+exten => 843,21,Wait,1
+exten => 843,22,SayNumber(1000000) ; one million
+exten => 843,23,Wait,1
+exten => 843,24,SayNumber(2000000) ; two millions
+exten => 843,25,Wait,1
+exten => 843,26,SayUnixTime()
+; Portuguese
+exten => 844,1,Answer
+exten => 844,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 844,3,SetLanguage(pt)
+exten => 844,4,SayNumber(1) ; one
+exten => 844,5,Wait,1
+exten => 844,6,SayNumber(1,f) ; one (feminine)
+exten => 844,7,Wait,1
+exten => 844,8,SayNumber(2) ; two
+exten => 844,9,Wait,1
+exten => 844,10,SayNumber(2,f) ; two (feminine)
+exten => 844,11,Wait,1
+exten => 844,12,SayNumber(183) ; hundred& eighty three
+exten => 844,13,Wait,1
+exten => 844,14,SayUnixTime()
+; Italian
+exten => 845,1,Answer
+exten => 845,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 845,3,SetLanguage(it)
+exten => 845,4,SayNumber(21) ; "twentyone"
+exten => 845,5,Wait,1
+exten => 845,6,SayNumber(183) ; hundred eighty three
+exten => 845,7,Wait,1
+exten => 845,8,SayNumber(283) ; two hundred eighty three
+exten => 845,9,SayNumber(1063) ; thousand sixty three
+exten => 845,10,Wait,1
+exten => 845,11,SayNumber(2063) ; two thousands sixty three
+exten => 845,12,Wait,1
+exten => 845,13,SayUnixTime()
+; Dutch
+exten => 846,1,Answer
+exten => 846,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 846,3,SetLanguage(nl)
+exten => 846,4,SayUnixTime(||ABdY'digits/at'R)
+exten => 846,5,Wait,1
+; Danish
+exten => 847,1,Answer
+exten => 847,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 847,3,SetLanguage(da)
+exten => 847,4,SayNumber(68) ; eight-& sixty
+exten => 847,5,Wait,1
+exten => 847,6,SayNumber(2034) ; two thousand & four-& thirty
+exten => 847,7,Wait,1
+exten => 847,8,SayNumber(1000000) ; one million
+exten => 847,9,Wait,1
+exten => 847,10,SayNumber(2000000) ; two millions
+exten => 847,11,Wait,1
+exten => 847,12,SayUnixTime()
+; German
+exten => 848,1,Answer
+exten => 848,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 848,3,SetLanguage(de)
+exten => 848,4,SayNumber(68) ; eight-& sixty
+exten => 848,5,Wait,1
+exten => 848,6,SayNumber(100) ; "hundert"
+exten => 848,7,Wait,1
+exten => 848,8,SayNumber(101) ; "einhundert-einS"
+exten => 848,9,Wait,1
+exten => 848,10,SayNumber(1000) ; "tausend"
+exten => 848,11,Wait,1
+exten => 848,12,SayNumber(1001) ; "eintausend-einS" X tausend-einS
+exten => 848,13,Wait,1
+exten => 848,14,SayNumber(2134) ; two thousand one hundred four-& thirty
+exten => 848,15,Wait,1
+exten => 848,16,SayNumber(1001000) ; one million one thousand X million tausend
+exten => 848,17,Wait,1
+exten => 848,18,SayNumber(2002000) ; two millions two thousand
+exten => 848,19,Wait,1
+exten => 848,20,SayUnixTime()
+; Swedish
+exten => 849,1,Answer
+exten => 849,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 849,3,SetLanguage(se)
+exten => 849,4,SayUnixTime()
+exten => 849,5,Wait,1
+; Temp
+exten => 850,1,Answer
+exten => 850,2,Wait,1 ; Allow VoIP sessions time to initialise
+exten => 850,3,SetLanguage(de)
+exten => 850,4,Playback(digits/1)
+exten => 850,5,Wait,1
+exten => 850,6,Playback(digits/de-eins)
+exten => 850,7,Wait,1
diff --git a/trunk/contrib/init.d/rc.debian.asterisk b/trunk/contrib/init.d/rc.debian.asterisk
new file mode 100755
index 000000000..3926346d4
--- /dev/null
+++ b/trunk/contrib/init.d/rc.debian.asterisk
@@ -0,0 +1,85 @@
+#! /bin/sh
+# $Id$
+#
+# asterisk start the asterisk PBX
+#
+# Thu Nov 17 2005 Gregory Boehnlein <damin@nacs.net>
+# - Updated Version to 1.3
+# - Reversed behavior of LD_ASSUME_KERNEL=2.4.1
+# - Added detailed failure messages
+#
+# Sun Jul 18 2004 Gregory Boehnlein <damin@nacs.net>
+# - Updated Version to 1.2
+# - Added test for safe_asterisk
+# - Changed "stop gracefully" to "stop now"
+# - Added support for -U and -G command line options
+# - Modified "reload" to call asterisk -rx 'reload'
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+NAME=asterisk
+DESC="Asterisk PBX"
+# Full path to asterisk binary
+DAEMON=/usr/sbin/asterisk
+
+# Full path to safe_asterisk script
+SAFE_ASTERISK=/usr/sbin/safe_asterisk
+
+# Uncomment this ONLY if you know what you are doing.
+# export LD_ASSUME_KERNEL=2.4.1
+
+# Uncomment the following and set them to the user/groups that you
+# want to run Asterisk as. NOTE: this requires substantial work to
+# be sure that Asterisk's environment has permission to write the
+# files required for its operation, including logs, its comm
+# socket, the asterisk database, etc.
+#AST_USER="asterisk"
+#AST_GROUP="asterisk"
+
+if ! [ -x $DAEMON ] ; then
+ echo "ERROR: /usr/sbin/asterisk not found"
+ exit 0
+fi
+
+if ! [ -d /etc/asterisk ] ; then
+ echo "ERROR: /etc/asterisk directory not found"
+ exit 0
+fi
+
+set -e
+
+case "$1" in
+ start)
+ echo -n "Starting $DESC: "
+ if [ -f $SAFE_ASTERISK ] ; then
+ DAEMON=$SAFE_ASTERISK
+ fi
+ if [ $AST_USER ] ; then
+ ASTARGS="-U $AST_USER"
+ fi
+ if [ $AST_GROUP ] ; then
+ ASTARGS="`echo $ASTARGS` -G $AST_GROUP"
+ fi
+ start-stop-daemon --start --exec $DAEMON -- $ASTARGS
+ echo "$NAME."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ $DAEMON -rx 'stop now' > /dev/null 2> /dev/null && echo -n "$NAME"
+ echo "."
+ exit 0
+ ;;
+ reload)
+ echo "Reloading $DESC configuration files."
+ $DAEMON -rx 'reload' > /dev/null 2> /dev/null
+ ;;
+ restart|force-reload)
+ $DAEMON -rx 'restart gracefully' > /dev/null 2> /dev/null && echo -n "$NAME"
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/trunk/contrib/init.d/rc.gentoo.asterisk b/trunk/contrib/init.d/rc.gentoo.asterisk
new file mode 100755
index 000000000..3d963d6c0
--- /dev/null
+++ b/trunk/contrib/init.d/rc.gentoo.asterisk
@@ -0,0 +1,18 @@
+#!/sbin/runscript
+# $Id$
+
+depend() {
+ need net logger
+}
+
+start() {
+ ebegin "Starting Asterisk"
+ /usr/sbin/asterisk
+ eend $? "Failed to start Asterisk"
+}
+
+stop() {
+ ebegin "Stopping Asterisk"
+ kill $(cat /var/run/asterisk.pid)
+ eend $? "Failed to stop Asterisk"
+}
diff --git a/trunk/contrib/init.d/rc.mandrake.asterisk b/trunk/contrib/init.d/rc.mandrake.asterisk
new file mode 100755
index 000000000..1ffd25d37
--- /dev/null
+++ b/trunk/contrib/init.d/rc.mandrake.asterisk
@@ -0,0 +1,185 @@
+#!/bin/sh
+#
+# asterisk: Starts the asterisk service
+#
+# Version: @(#) /etc/rc.d/init.d/asterisk 1.0
+#
+# chkconfig: 2345 95 10
+# description: Starts the asterisk service
+#
+# processname: asterisk
+#
+
+# $Id$
+
+TTY=9 # TTY (if you want one) for Asterisk to run on
+CONSOLE=yes # Whether or not you want a console
+NOTIFY=root # Who to notify about crashes
+DUMPDROP=/tmp
+HOSTNAME=`hostname`
+if [ 0`readlink $0` = "0" ]; then
+ CONFIGFILE=/etc/sysconfig/`basename $0`
+else
+ CONFIG0=`readlink $0`
+ CONFIGFILE=/etc/sysconfig/`basename $CONFIG0`
+fi
+
+# Setup environment
+cd /usr/src
+if [ -f /usr/lib/asterisk/modules/chan_h323.so -a `grep -c ^noload=chan_h323.so /etc/asterisk/modules.conf` -eq 0 ]; then
+ OPENH323DIR=/usr/src/h323/openh323
+ PWLIBDIR=/usr/src/h323/pwlib
+else
+ OPENH323DIR=/usr/src/oh323/openh323
+ PWLIBDIR=/usr/src/oh323/pwlib
+fi
+
+# Put overrides in /etc/sysconfig/asterisk
+[ -f $CONFIGFILE ] && . $CONFIGFILE
+
+LD_LIBRARY_PATH=$OPENH323DIR/lib:$PWLIBDIR/lib
+export OPENH323DIR PWLIBDIR LD_LIBRARY_PATH
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+#
+# Don't fork when running "safely"
+#
+ASTARGS="-p"
+if [ "$TTY" != "" ]; then
+ if [ -c /dev/tty${TTY} ]; then
+ TTY=tty${TTY}
+ elif [ -c /dev/vc/${TTY} ]; then
+ TTY=vc/${TTY}
+ else
+ echo "Cannot find your TTY (${TTY})" >&2
+ exit 1
+ fi
+ ASTARGS="${ASTARGS} -vvv"
+ if [ "$CONSOLE" != "no" ]; then
+ ASTARGS="${ASTARGS} -c"
+ fi
+fi
+if [ ! -w ${DUMPDROP} ]; then
+ echo "Cannot write to ${DUMPDROP}" >&2
+ exit 1
+fi
+
+#
+# Let Asterisk dump core
+#
+ulimit -c unlimited
+
+#launch_asterisk()
+#{
+#}
+
+SIGMSG=("None", "Hangup" "Interrupt" "Quit" "Illegal instruction" "Trace trap" "IOT Trap" "Bus Error" "Floating-point exception" "Killed" "User-defined signal 1" "Segmentation violation" "User-defined signal 2" "Broken pipe" "Alarm clock" "Termination" "Stack fault")
+
+run_asterisk()
+{
+ while :; do
+
+ if [ "$TTY" != "" ]; then
+ cd /tmp
+ stty sane < /dev/${TTY}
+ asterisk ${ASTARGS} > /dev/${TTY} 2>&1 < /dev/${TTY}
+ else
+ cd /tmp
+ asterisk ${ASTARGS}
+ fi
+ EXITSTATUS=$?
+ echo "Asterisk ended with exit status $EXITSTATUS"
+ if [ "$EXITSTATUS" = "0" ]; then
+ # Properly shutdown....
+ echo "Asterisk shutdown normally."
+ exit 0
+ elif [ $EXITSTATUS -gt 128 ]; then
+ EXITSIGNAL=$(($EXITSTATUS - 128))
+ EXITMSG=${SIGMSG[$EXITSIGNAL]}
+ echo "Asterisk exited on signal $EXITSIGNAL - $EXITMSG."
+ if [ "$NOTIFY" != "" ]; then
+ echo "Asterisk exited on signal $EXITSIGNAL - $EXITMSG. Might want to take a peek." | \
+ mail -s "Asterisk Died ($HOSTNAME)" $NOTIFY
+ fi
+ if [ -f /tmp/core ]; then
+ mv /tmp/core ${DUMPDROP}/core.`hostname`-`date -Iseconds` &
+ fi
+ else
+ echo "Asterisk died with code $EXITSTATUS. Aborting."
+ if [ -f /tmp/core ]; then
+ mv /tmp/core ${DUMPDROP}/core.`hostname`-`date -Iseconds` &
+ fi
+ exit 0
+ fi
+ echo "Automatically restarting Asterisk."
+ done
+}
+
+case "$1" in
+ start)
+ gprintf "Starting asterisk: "
+ run_asterisk >/dev/null 2>&1 &
+ sleep 2 # Give it time to die
+ succeeded=`pidof asterisk|awk '{print NF}'`
+ if [ $succeeded = "0" ]; then
+ failure
+ else
+ success
+ fi
+ echo
+ ;;
+ stop)
+ gprintf "Stopping asterisk: "
+ asterisk -r -x "stop gracefully" >/dev/null 2>&1
+ killall -9 mpg123 2>/dev/null
+ success
+ echo
+ ;;
+ restart)
+ $0 stop
+ usleep 100000
+ $0 start
+ ;;
+ reload)
+ gprintf "Reloading asterisk: "
+ asterisk -r -x "reload" >/dev/null 2>&1
+ success
+ echo
+ ;;
+ stopnow)
+ gprintf "Stopping asterisk: "
+ asterisk -r -x "stop now" >/dev/null 2>&1
+ success
+ echo
+ ;;
+ restartnow)
+ $0 stopnow
+ $0 start
+ ;;
+ fullrestart)
+ $0 stop
+ service zaptel restart
+ $0 start
+ ;;
+ fullrestartnow)
+ $0 stopnow
+ service zaptel restart
+ $0 start
+ ;;
+ status)
+ succeeded=`pidof asterisk|awk '{print NF}'`
+ if [ $succeeded = "0" ]; then
+ echo "Asterisk is not running"
+ else
+ echo "Asterisk is currently running with $succeeded threads"
+ fi
+ ;;
+ *)
+ gprintf "*** Usage: $0 {start|stop[now]|reload|[full]restart[now]|status}\n"
+ exit 1
+esac
+
+exit 0
+
diff --git a/trunk/contrib/init.d/rc.mandrake.zaptel b/trunk/contrib/init.d/rc.mandrake.zaptel
new file mode 100755
index 000000000..2feaef4c7
--- /dev/null
+++ b/trunk/contrib/init.d/rc.mandrake.zaptel
@@ -0,0 +1,108 @@
+#!/bin/sh
+#
+# zaptel: Loads Asterisk modules
+#
+# Version: @(#) /etc/rc.d/init.d/zaptel 1.0
+#
+# chkconfig: 2345 90 10
+# description: Loads and unloads zaptel modules at boot time and shutdown.
+#
+# hide: true
+
+# $Id$
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Default modules - override in /etc/sysconfig/zaptel
+######################################
+MODULES="usb-uhci zaptel wcfxo wcusb"
+######################################
+
+# Resolve back to the basename (i.e. zaptel, not S90zaptel)
+if [ 0`readlink $0` = "0" ]; then
+ CONFIGFILE=/etc/sysconfig/`basename $0`
+else
+ CONFIG0=`readlink $0`
+ CONFIGFILE=/etc/sysconfig/`basename $CONFIG0`
+fi
+
+[ -f $CONFIGFILE ] && . $CONFIGFILE
+
+function probe() {
+ gprintf " $1"
+ modprobe -i $1
+ # It has to be in the module list, otherwise something is wrong
+ if lsmod | grep -c ^$1 >/dev/null; then
+ success
+ else
+ failure
+ fi
+ echo
+}
+
+function unprobe() {
+ gprintf " $1"
+ rmmod $1 >/dev/null 2>&1
+ # If it's still in the module list after removing it, there's something wrong.
+ if lsmod | grep -c ^$1 >/dev/null; then
+ failure
+ else
+ success
+ fi
+ echo
+}
+
+function reverse_modules() {
+ tmp=$MODULES
+ MODULES=''
+ for i in $tmp; do
+ MODULES="$i $MODULES" ;
+ done
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ gprintf "Loading Asterisk modules:\n"
+ for i in $MODULES; do
+ probe $i
+ usleep 100000 ;
+ done
+ ztcfg
+ ;;
+ stop)
+ gprintf "Unloading Asterisk modules:\n"
+ reverse_modules
+ for i in $MODULES; do
+ unprobe $i
+ usleep 100000 ;
+ done
+ ;;
+ status)
+ #ztcfg -vv
+ OK=1
+ gprintf "Checking Asterisk modules"
+ for i in $MODULES; do
+ if [ `lsmod | grep -c $i` -eq 0 ]; then
+ OK=0
+ fi
+ done
+ if [ $OK -gt 0 ]; then
+ success
+ else
+ failure
+ fi
+ echo
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ gprintf "*** Usage: $0 {start|stop|status|restart}\n"
+ exit 1
+esac
+
+exit 0
+
diff --git a/trunk/contrib/init.d/rc.redhat.asterisk b/trunk/contrib/init.d/rc.redhat.asterisk
new file mode 100755
index 000000000..27d633e70
--- /dev/null
+++ b/trunk/contrib/init.d/rc.redhat.asterisk
@@ -0,0 +1,136 @@
+#!/bin/sh
+# $Id$
+#
+# asterisk Starts, Stops and Reloads Asterisk.
+#
+# chkconfig: 2345 90 60
+# description: Asterisk PBX and telephony daemon.
+# processname: asterisk
+# pidfile: /var/run/asterisk.pid
+#
+# Thu Nov 17 2005 Gregory Boehnlein <damin@nacs.net>
+# - Updated Version to 1.3
+# - Reversed behavior of LD_ASSUME_KERNEL=2.4.1
+# - Added detailed failure messages
+#
+# Sun Jul 18 2004 Gregory Boehnlein <damin@nacs.net>
+# - Updated Version to 1.2
+# - Added test for safe_asterisk
+# - Verified SIGTERM issued by "killproc" ensures "stop gracefully"
+# - Added support for -U and -G command line options
+# - Modified "reload" to call asterisk -rx 'reload'
+
+# Use this option to specify a different configuration directory
+#AST_CONFIG=/etc/asterisk
+
+# Installation directory
+AST_SBIN=/usr/sbin
+
+# Uncomment the following and set them to the user/groups that you
+# want to run Asterisk as. NOTE: this requires substantial work to
+# be sure that Asterisk's environment has permission to write the
+# files required for its operation, including logs, its comm
+# socket, the asterisk database, etc.
+#AST_USER="asterisk"
+#AST_GROUP="asterisk"
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+if ! [ -x $AST_SBIN/asterisk ] ; then
+ echo "ERROR: $AST_SBIN/asterisk not found"
+ exit 0
+fi
+
+if ! [ -d $AST_CONFIG ] ; then
+ echo "ERROR: $AST_CONFIG directory not found"
+ exit 0
+fi
+
+# Uncomment this ONLY if you know what you are doing.
+# export LD_ASSUME_KERNEL=2.4.1
+
+# Full path to asterisk binary
+DAEMON=$AST_SBIN/asterisk
+
+# Full path to safe_asterisk script
+SAFE_ASTERISK=$AST_SBIN/safe_asterisk
+
+# Allow configuration overrides in /etc/sysconfig/asterisk
+CONFIG0=`readlink $0`
+if [ "$CONFIG0" = "" ]; then
+ CONFIGFILE=/etc/sysconfig/`basename $0`
+else
+ CONFIGFILE=/etc/sysconfig/`basename $CONFIG0`
+fi
+[ -x $CONFIGFILE ] && . $CONFIGFILE
+
+RETVAL=0
+
+start() {
+ # Start daemons.
+ echo -n $"Starting asterisk: "
+ if [ -f $SAFE_ASTERISK ] ; then
+ DAEMON=$SAFE_ASTERISK
+ fi
+ if [ $AST_USER ] ; then
+ ASTARGS="-U $AST_USER"
+ fi
+ if [ $AST_GROUP ] ; then
+ ASTARGS="$ASTARGS -G $AST_GROUP"
+ fi
+ if [ $AST_CONFIG ]; then
+ ASTARGS="$ASTARGS -C $AST_CONFIG"
+ fi
+ daemon $DAEMON $ASTARGS
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/asterisk
+ echo
+ return $RETVAL
+}
+
+stop() {
+ # Stop daemons.
+ echo -n $"Shutting down asterisk: "
+ killproc asterisk
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/asterisk
+ echo
+ return $RETVAL
+}
+
+restart() {
+ stop
+ start
+}
+
+reload() {
+ $DAEMON -rx 'reload' > /dev/null 2> /dev/null
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/asterisk ] && restart || :
+ ;;
+ status)
+ status asterisk
+ ;;
+ *)
+ echo "Usage: asterisk {start|stop|restart|reload|condrestart|status}"
+ exit 1
+esac
+
+exit $?
diff --git a/trunk/contrib/init.d/rc.slackware.asterisk b/trunk/contrib/init.d/rc.slackware.asterisk
new file mode 100755
index 000000000..0802bfcaa
--- /dev/null
+++ b/trunk/contrib/init.d/rc.slackware.asterisk
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Start/stop/restart Asterisk PBX
+#
+# Version: 1.0 - Paul Belanger <pabelanger@gmail.com>
+#
+# 03.29.2005 - Initial Version
+#
+# $Id$
+
+asterisk_start() {
+ if [ -x /usr/sbin/asterisk ]; then
+ echo "Starting Asterisk /usr/sbin/asterisk"
+ /usr/sbin/asterisk
+ fi
+}
+
+asterisk_stop() {
+ # If there is no PID file, ignore this request...
+ if [ -r /var/run/asterisk.pid ]; then
+ killall asterisk
+ fi
+}
+
+asterisk_restart() {
+ asterisk_stop
+ asterisk_start
+}
+
+case "$1" in
+ 'start')
+ asterisk_start
+ ;;
+ 'stop')
+ asterisk_stop
+ ;;
+ 'restart')
+ asterisk_restart
+ ;;
+ *)
+ echo "usage $0 start|stop|restart" ;;
+esac
+
diff --git a/trunk/contrib/init.d/rc.suse.asterisk b/trunk/contrib/init.d/rc.suse.asterisk
new file mode 100755
index 000000000..3eb1be55f
--- /dev/null
+++ b/trunk/contrib/init.d/rc.suse.asterisk
@@ -0,0 +1,127 @@
+#!/bin/sh
+# $Id: asterisk,v 1.3 2005/11/17 22:30:01 Gregory Boehnlein <damin@nacs.net>
+#
+# asterisk Starts, Stops and Reloads Asterisk.
+#
+# chkconfig: 2345 40 60
+# description: Asterisk PBX and telephony daemon.
+# processname: asterisk
+# pidfile: /var/run/asterisk.pid
+#
+# Thu Nov 17 2005 Gregory Boehnlein <damin@nacs.net>
+# - Updated Version to 1.3
+# - Reversed behavior of LD_ASSUME_KERNEL=2.4.1
+# - Added detailed failure messages
+#
+# Sun Jul 18 2004 Gregory Boehnlein <damin@nacs.net>
+# - Updated Version to 1.2
+# - Added test for safe_asterisk
+# - Verified SIGTERM issued by "killproc" ensures "stop gracefully"
+# - Added support for -U and -G command line options
+# - Modified "reload" to call asterisk -rx 'reload'
+
+### BEGIN INIT INFO
+# Provides: asterisk
+# Required-Start: +zaptel
+# Required-Stop:
+# Default-Start: 3 5
+# Default-Stop: 0 1 2 4 6
+# Description: zaptel - zaptel modules for Asterisk
+### END INIT INFO
+
+# Source function library.
+. /lib/lsb/init-functions
+
+if ! [ -x /usr/sbin/asterisk ] ; then
+ echo "ERROR: /usr/sbin/asterisk not found"
+ exit 0
+fi
+
+if ! [ -d /etc/asterisk ] ; then
+ echo "ERROR: /etc/asterisk directory not found"
+ exit 0
+fi
+
+# Uncomment this ONLY if you know what you are doing.
+# export LD_ASSUME_KERNEL=2.4.1
+
+# Full path to asterisk binary
+DAEMON=/usr/sbin/asterisk
+
+# Full path to safe_asterisk script
+SAFE_ASTERISK=/usr/sbin/safe_asterisk
+
+# Uncomment the following and set them to the user/groups that you
+# want to run Asterisk as. NOTE: this requires substantial work to
+# be sure that Asterisk's environment has permission to write the
+# files required for its operation, including logs, its comm
+# socket, the asterisk database, etc.
+#AST_USER="asterisk"
+#AST_GROUP="asterisk"
+
+RETVAL=0
+
+start() {
+ # Start daemons.
+ echo -n $"Starting asterisk: "
+ if [ -f $SAFE_ASTERISK ] ; then
+ DAEMON=$SAFE_ASTERISK
+ fi
+ if [ $AST_USER ] ; then
+ ASTARGS="-U $AST_USER"
+ fi
+ if [ $AST_GROUP ] ; then
+ ASTARGS="`echo $ASTARGS` -G $AST_GROUP"
+ fi
+ $DAEMON $ASTARGS
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/asterisk
+ echo
+ return $RETVAL
+}
+
+stop() {
+ # Stop daemons.
+ echo -n $"Shutting down asterisk: "
+ killproc asterisk
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/asterisk
+ echo
+ return $RETVAL
+}
+
+restart() {
+ stop
+ start
+}
+
+reload() {
+ $DAEMON -rx 'reload' > /dev/null 2> /dev/null
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/asterisk ] && restart || :
+ ;;
+ status)
+ status asterisk
+ ;;
+ *)
+ echo "Usage: asterisk {start|stop|restart|reload|condrestart|status}"
+ exit 1
+esac
+
+exit $?
diff --git a/trunk/contrib/scripts/README.messages-expire b/trunk/contrib/scripts/README.messages-expire
new file mode 100644
index 000000000..12f2b0e9c
--- /dev/null
+++ b/trunk/contrib/scripts/README.messages-expire
@@ -0,0 +1,20 @@
+messages-expire.pl
+
+messages-expire finds messages more than X days old and deletes them.
+Because the older messages will be the lower numbers in the folder (msg0000
+will be older than msg0005), just deleting msg0000 will not work.
+expire-messages then runs a routine that goes into every folder in every
+mailbox to reorganize. If the folder contains msg0000, no action is taken.
+If the folder does not, the rename routine takes the oldest message and
+names it msg0000, the next oldest message and names it msg0001 and so on.
+
+The file deletion is done by the -exec parameter to 'find'. It would be far
+more efficient to take the output from 'find' and just reorganize the
+directories from which we deleted a file. Something for the future...
+
+Keep in mind that messages are deleted at the beginning of the script you
+will have mailbox trouble if you check messages before the script
+reorganizes your mailbox.
+
+To use it, make sure the paths are right. Adjust $age (originally set to
+31) if necessary.
diff --git a/trunk/contrib/scripts/agents.php b/trunk/contrib/scripts/agents.php
new file mode 100644
index 000000000..51f8bdee3
--- /dev/null
+++ b/trunk/contrib/scripts/agents.php
@@ -0,0 +1,73 @@
+<?php
+
+ob_implicit_flush(false);
+
+$username = "drmac";
+$secret = "secret";
+
+$socket = fsockopen("127.0.0.1","5038", $errornum, $errorstr);
+
+$agents = array();
+$curr_agent = "";
+$better_status = array( 'AGENT_UNKNOWN' => 'Unknown',
+ 'AGENT_IDLE' => 'Idle',
+ 'AGENT_ONCALL' => 'On Call',
+ 'AGENT_LOGGEDOFF' => 'Not Logged In' );
+
+if(!$socket) {
+ print "Couldn't open socket. Error #" . $errornum . ": " . $errorstr;
+} else {
+ fputs($socket, "Action: Login\r\n");
+ fputs($socket, "UserName: $username\r\n");
+ fputs($socket, "Secret: $secret\r\n\r\n");
+ fputs($socket, "Action: Agents\r\n\r\n");
+ fputs($socket, "Action: Logoff\r\n\r\n");
+
+ while(!feof($socket)) {
+ $info = fscanf($socket, "%s\t%s\r\n");
+ switch($info[0]) {
+ case "Agent:":
+ $curr_agent = $info[1];
+ $agents[$curr_agent] = array();
+ break;
+ case "Name:":
+ $agents[$curr_agent]['Name'] = $info[1];
+ break;
+ case "Status:":
+ $agents[$curr_agent]['Status'] = $better_status[$info[1]];
+ break;
+ case "LoggedInChan:":
+ $agents[$curr_agent]['LoggedInChan'] = $info[1];
+ break;
+ case "LoggedInTime:":
+ if($info[1] != "0") {
+ $agents[$curr_agent]['LoggedInTime'] = date("D, M d Y g:ia", $info[1]);
+ } else {
+ $agents[$curr_agent]['LoggedInTime'] = "n/a";
+ }
+ break;
+ case "TalkingTo:":
+ $agents[$curr_agent]['TalkingTo'] = $info[1];
+ break;
+ default:
+ break;
+ }
+ }
+ fclose($socket);
+
+ print "<html><head><title>Agents Status</title></head>\n<body>\n";
+ print "<table width=\"800px\" border=\"1\">\n";
+ print " <tr><th>Agent #</th><th>Agent Name</th><th>Agent Location</th><th>Agent Status</th><th>Agent Talking To</th><th>Agent Login Time</th></tr>\n";
+
+ foreach( $agents as $agent=>$curr ) {
+ print " <tr>\n <td>" . $agent . "</td>\n";
+ print " <td>" . $curr['Name'] . "</td>\n";
+ print " <td>" . $curr['LoggedInChan'] . "</td>\n";
+ print " <td>" . $curr['Status'] . "</td>\n";
+ print " <td>" . $curr['TalkingTo'] . "</td>\n";
+ print " <td>" . $curr['LoggedInTime'] . "</td>\n </tr>\n";
+ }
+
+ print "</table>\n</body>\n</html>\n";
+}
+?>
diff --git a/trunk/contrib/scripts/ast_grab_core b/trunk/contrib/scripts/ast_grab_core
new file mode 100644
index 000000000..b2bd7b2ed
--- /dev/null
+++ b/trunk/contrib/scripts/ast_grab_core
@@ -0,0 +1,70 @@
+#!/bin/sh
+# $Id$
+# lame quickie script to snarf a core of a hung asterisk process.
+# bugs to ast_grab_core, blinky-lights.org (derrick daugherty)
+
+# we have found that gcore doesn't yield as useful a core file
+# as that yielded by a signal-caused core dump. So we are going to change
+# the strategy to sending a SEGV signal to the asterisk process,
+# and have it 'burn to the ground', leaving behind a core file.
+# the main difference is that you cannot control where the
+# core file will end up. We will assume that safe_asterisk was
+# used to start asterisk, and the core file should therefore end
+# up in /tmp (because safe_asterisk cd's there before starting asterisk).
+# if this is not the case, set DUMPDIR to the place where the core
+# file can be found.
+
+DATE=`date +%Y%m%d%H%M`
+DUMPDIR=/tmp
+HOSTNAME=`hostname`
+ADMINEMAIL="root@localhost"
+
+#the following should be improved
+if [ -e /etc/asterisk/asterisk.conf ]; then
+ RUNDIR=`awk -F"=>" '/astrundir/ {print $2}' /etc/asterisk/asterisk.conf`
+ PID=`cat ${RUNDIR}/asterisk.pid`
+elif [ -e /var/run/asterisk.pid ] ; then
+ PID=`cat /var/run/asterisk.pid`
+else
+ echo Could not find an asterisk.conf definition for astrundir, using \'ps\'
+ echo to try and determine process ID. This is not reliable.
+ PID=`ps auxwf|grep asterisk|grep vv|head -1|awk '{print $2}'`
+fi
+
+echo Snarfing asterisk core, this could take a few seconds depending
+echo on how much memory is in use.
+echo
+echo \*\*\* WARNING \*\*\* If the system is not already locked this will cause the
+echo \*\*\* WARNING \*\*\* process to STOP while memory is dumped to disk.
+echo
+
+/bin/kill -11 ${PID}
+
+echo Snarfed! ${DUMPDIR}/core.${PID}
+echo
+
+
+echo Trying for a backtrace of the captured core.
+/usr/bin/gdb /usr/sbin/asterisk ${DUMPDIR}/core.${PID} > ${DUMPDIR}/gdb_dump.${PID}.txt 2> /dev/null << EOF
+set prompt \n
+set print pretty\n
+echo --------------------------------------------------------------------------------\n
+echo INFO THREAD
+info thread
+echo --------------------------------------------------------------------------------\n
+echo THREAD APPLY ALL BT
+thread apply all bt
+echo --------------------------------------------------------------------------------\n
+echo THREAD APPLY ALL BT FULL
+thread apply all bt full
+quit
+EOF
+echo Done trying for a bt.
+
+
+echo Notifying admins of the core.
+/usr/bin/mail -s "${HOSTNAME} core dumped at ${DUMPDIR}/core.${PID}" ${ADMINEMAIL} < ${DUMPDIR}/gdb_dump.${PID}.txt
+echo Done.
+echo
+echo Reproducible deadlocks should be posted with a full backtrace and instructions
+echo to reproduce the issue at http://bugs.digium.com/ Thanks!
diff --git a/trunk/contrib/scripts/astgenkey b/trunk/contrib/scripts/astgenkey
new file mode 100644
index 000000000..637604896
--- /dev/null
+++ b/trunk/contrib/scripts/astgenkey
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Usage: astgenkey [ -q ] [ -n ] [keyname]
+#
+DES3=-des3
+if [ "$1" = "-q" ]; then
+ QUIET='y'
+ if [ "$2" = "-n" ]; then
+ DES3=
+ KEY=$3
+ else
+ KEY=$2
+ fi
+elif [ "$1" = "-n" ]; then
+ DES3=
+ if [ "$2" = "-q" ]; then
+ QUIET='y'
+ KEY=$3
+ else
+ KEY=$2
+ fi
+else
+ KEY=$1
+fi
+
+if [ "$QUIET" != 'y' ]; then
+ echo ""
+ echo "This script generates an RSA private and public key pair"
+ echo "in PEM format for use by Asterisk. You will be asked to"
+ echo "enter a passcode for your key multiple times. Please"
+ echo "enter the same code each time. The resulting files will"
+ echo "need to be moved to /var/lib/asterisk/keys if you want"
+ echo "to use them, and any private keys (.key files) will"
+ echo "need to be initialized at runtime either by running"
+ echo "Asterisk with the '-i' option, or with the 'init keys'"
+ echo "command once Asterisk is running."
+ echo ""
+ echo "Press ENTER to continue or ^C to cancel."
+ read BLAH
+fi
+
+while [ "$KEY" = "" ]; do
+ echo -n "Enter key name: "
+ read KEY
+done
+
+rm -f ${KEY}.key ${KEY}.pub
+
+echo "Generating SSL key '$KEY': "
+openssl genrsa -out ${KEY}.key ${DES3} 1024
+openssl rsa -in ${KEY}.key -pubout -out ${KEY}.pub
+
+if [ -f "${KEY}.key" ] && [ -f "${KEY}.pub" ]; then
+ if [ "$QUIET" != 'y' ]; then
+ echo "Key creation successful."
+ echo "Public key: ${KEY}.pub"
+ echo "Private key: ${KEY}.key"
+ fi
+else
+ echo "Unknown error creating keys."
+fi
diff --git a/trunk/contrib/scripts/astgenkey.8 b/trunk/contrib/scripts/astgenkey.8
new file mode 100644
index 000000000..8f8325982
--- /dev/null
+++ b/trunk/contrib/scripts/astgenkey.8
@@ -0,0 +1,129 @@
+.\" $Header$
+.\"
+.\" transcript compatibility for postscript use.
+.\"
+.\" synopsis: .P! <file.ps>
+.\"
+.de P!
+.fl
+\!!1 setgray
+.fl
+\\&.\"
+.fl
+\!!0 setgray
+.fl \" force out current output buffer
+\!!save /psv exch def currentpoint translate 0 0 moveto
+\!!/showpage{}def
+.fl \" prolog
+.sy sed \-e 's/^/!/' \\$1\" bring in postscript file
+\!!psv restore
+.
+.de pF
+.ie \\*(f1 .ds f1 \\n(.f
+.el .ie \\*(f2 .ds f2 \\n(.f
+.el .ie \\*(f3 .ds f3 \\n(.f
+.el .ie \\*(f4 .ds f4 \\n(.f
+.el .tm ? font overflow
+.ft \\$1
+..
+.de fP
+.ie !\\*(f4 \{\
+. ft \\*(f4
+. ds f4\"
+' br \}
+.el .ie !\\*(f3 \{\
+. ft \\*(f3
+. ds f3\"
+' br \}
+.el .ie !\\*(f2 \{\
+. ft \\*(f2
+. ds f2\"
+' br \}
+.el .ie !\\*(f1 \{\
+. ft \\*(f1
+. ds f1\"
+' br \}
+.el .tm ? font underflow
+..
+.ds f1\"
+.ds f2\"
+.ds f3\"
+.ds f4\"
+'\" t
+.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n
+.TH ASTGENKEY 8 "May 14th, 2005" "Asterisk" "Linux Programmer's Manual"
+.SH NAME
+.B astgenkey
+-- generates keys for for Asterisk IAX2 RSA authentication
+.SH SYNOPSIS
+.PP
+.B astgenkey
+[ -q ] [ -n ] [ \fIkeyname\fP ]
+
+.SH DESCRIPTION
+.B astgenkey
+This script generates an RSA private and public key pair in PEM format
+for use by Asterisk. The private key should be kept a secret, as it can
+be used to fake your system's identity. Thus by default (without the
+option
+.I -n
+) the script will create a passphrase-encrypted copy of your secret key:
+without entering the passphrase you won't be able to use it.
+
+However if you want to use such a key with Asterisk, you'll have to start
+it interactively, because the scripts that start asterisk can't use that
+encrypted key.
+
+The key is identified by a name. If you don't write the name on the
+command-line you'll be prompted for one. The outputs of the script are:
+
+.I name\fB.pub
+.RS
+The public key: not secret. Send this to the other side.
+.RE
+
+.I name\fB.key
+.RS
+The private key: secret.
+.RE
+
+Those files should be copied to
+.I /var/lib/asterisk/keys
+
+(The private key: on your system. The public key: on other systems)
+
+To see the currently-installed keys from the asterisk CLI, use the command
+
+.RS
+show keys
+.RE
+
+.SH OPTIONS
+.B -q
+.RS
+Run quietly.
+.RE
+
+.B -n
+.RS
+Don't encrypt the private key.
+.RE
+
+.SH FILES
+.I /var/lib/asterisk/keys
+.RS
+.RE
+
+.SH "SEE ALSO"
+asterisk(8), genrsa(1), rsa(1),
+
+http://www.voip-info.org/wiki-Asterisk+iax+rsa+auth
+
+.SH "AUTHOR"
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/trunk/contrib/scripts/autosupport b/trunk/contrib/scripts/autosupport
new file mode 100644
index 000000000..557ca6ce2
--- /dev/null
+++ b/trunk/contrib/scripts/autosupport
@@ -0,0 +1,163 @@
+#!/bin/sh
+#
+# Collect support information
+#
+# Copyright (C) 2005, Digium, Inc.
+#
+# Written by John Bigelow (support@digium.com)
+#
+# Distributed under the terms of the GNU General Public
+# License
+#
+
+OUTPUT=$HOME/digiuminfo
+
+if [ $UID -ne 0 ]; then
+
+ echo "You must be root to run this."
+
+exit 1
+fi
+
+echo
+echo "This will gather information about your system such as:"
+echo "pci listing, dmesg, running processes, and kernel version"
+echo "This may take up to half a minute to run. Please be patient."
+echo "To continue press 'y', to quit press any other key"
+read ans
+
+if [ "$ans" = "y" ]; then
+
+ rm -f $OUTPUT
+
+ echo "------------------" >> $OUTPUT;
+ echo "PCI LIST " >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ lspci -vb >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "INTERRUPTS" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ cat /proc/interrupts >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "DMESG OUTPUT" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ dmesg >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "RUNNING PROCESSES" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ ps aux >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "KERNEL VERSION" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ uname -a >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "CPU INFO" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ cat /proc/cpuinfo >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "HDPARM STATUS" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ hdparm /dev/hda >> $OUTPUT;
+ hdparm -i /dev/hda >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "ZAPTEL CONFIG" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ grep -v '^#' /etc/zaptel.conf >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "ZAPATA CONFIG" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ grep -v '^;' /etc/asterisk/zapata.conf >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "EXTENSIONS CONFIG" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ grep -v '^;' /etc/asterisk/extensions.conf >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+ echo "------------------" >> $OUTPUT;
+ echo "ZTTEST" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+ /usr/src/zaptel/zttest -c 20 >> $OUTPUT;
+ echo >> $OUTPUT;
+ echo >> $OUTPUT;
+
+else
+ echo "terminated";
+exit
+fi
+
+echo
+echo "Digium may require root level access to the system to help debug";
+echo "the problem you are experiencing. Do you want to provide login";
+echo "information at this time?";
+echo "Press 'y' for yes and any other key to exit and save the previous info collected"
+read login
+
+if [ "$login" = "y" ]; then
+
+ echo "------------------" >> $OUTPUT;
+ echo "LOGIN INFORMATION" >> $OUTPUT;
+ echo "------------------" >> $OUTPUT;
+
+ echo
+ echo "What is your root password?"
+ read rootpass
+
+ echo
+ echo "Root pass: "$rootpass >> $OUTPUT
+
+ echo
+ echo "What is your PUBLIC IP address?"
+ read ip
+
+ echo "IP address: "$ip >> $OUTPUT
+
+ echo
+ echo "Please provide any other login information that the technician"
+ echo "may need to know to login to the system'(press enter if not)'"
+ read adinfo
+
+ echo "Additional login info: "$adinfo >> $OUTPUT
+
+ echo
+ echo "All information has been stored in $OUTPUT,"
+ echo "Please attach this file to an email ticket you already"
+ echo "have open with Digium Tech Support."
+
+else
+ echo
+ echo "All information except login info has been stored in $OUTPUT,"
+ echo "Please send this file to an email ticket you already"
+ echo "have open with Digium Tech Support."
+exit
+fi
+
+
+
diff --git a/trunk/contrib/scripts/autosupport.8 b/trunk/contrib/scripts/autosupport.8
new file mode 100644
index 000000000..e356fcdbb
--- /dev/null
+++ b/trunk/contrib/scripts/autosupport.8
@@ -0,0 +1,41 @@
+.TH AUTOSUPPORT 8 "Jul 5th, 2005" "Asterisk" "Linux Programmer's Manual"
+.SH NAME
+.B autosupport
+\(em interactive script to provide Digium[tm]'s support with information
+.SH SYNOPSIS
+.PP
+.B autosupport
+
+.SH DESCRIPTION
+.B autoasupport
+is a script that is normally run by a user contacting Digium's support
+to automate gathering support information.
+
+It will probe the system for some configuration and run-time information,
+and will also prompt the user for some optional access information (IP
+address, login and password).
+
+The information is written to /root/digiuminfo which the user is expected
+to attach to a support ticket to Digium.
+
+The script must be run as root as it reads Asterisk's configuration and
+the disk information using hdparm(8).
+
+.SH FILES
+.B /root/digiuminfo
+.RS
+The output of the script goes there
+.RE
+
+.SH SEE ALSO
+asterisk(8)
+
+.SH "AUTHOR"
+autosupport was written by John Bigelow <support@digium.com>.
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/trunk/contrib/scripts/iax-friends.sql b/trunk/contrib/scripts/iax-friends.sql
new file mode 100644
index 000000000..717a27d07
--- /dev/null
+++ b/trunk/contrib/scripts/iax-friends.sql
@@ -0,0 +1,15 @@
+#
+# Table structure for table `iaxfriends`
+#
+
+CREATE TABLE `iaxfriends` (
+ `name` varchar(40) NOT NULL default '',
+ `secret` varchar(40) NOT NULL default '',
+ `context` varchar(40) NOT NULL default '',
+ `ipaddr` varchar(20) NOT NULL default '',
+ `port` int(6) NOT NULL default '0',
+ `regseconds` int(11) NOT NULL default '0',
+ `accountcode` varchar(20) NOT NULL default '',
+ PRIMARY KEY (`name`)
+) TYPE=MyISAM;
+
diff --git a/trunk/contrib/scripts/loadtest.tcl b/trunk/contrib/scripts/loadtest.tcl
new file mode 100644
index 000000000..9c50be338
--- /dev/null
+++ b/trunk/contrib/scripts/loadtest.tcl
@@ -0,0 +1,148 @@
+#!/usr/bin/tclsh
+#
+# Usage (as root):
+#
+# $ tclsh loadtest.tcl
+#
+# Copyleft 2005 by Chris Maj <cmaj_at_freedomcorpse_dot_com>
+#
+# Create a (huge) bunch of call files to dial via pbx_spool.
+# Defaults are selected with 'Enter' and, if all defaults
+# are selected, you'll dial Zap/1/s into default|s|1
+#
+
+
+# where Asterisk's pbx/pbx_spool.c will be looking for work
+set SPOOLDIR /var/spool/asterisk/outgoing
+# pbx_spool is fairly aggresive, so make files here first
+set TEMPDIR /tmp
+
+if { ![file writable $SPOOLDIR] } {
+ puts "Do you need to be root to write to $SPOOLDIR ?"
+ exit
+}
+
+if { ![file readable $TEMPDIR] } {
+ puts "Do you need to be root to read from $TEMPDIR ?"
+ exit
+}
+
+if { ![file writable $TEMPDIR] } {
+ puts "Do you need to be root to write to $TEMPDIR ?"
+ exit
+}
+
+# gets some input from the user
+proc get {var_ default_ prompt_} {
+ global $var_
+ puts $prompt_
+ if { $default_ != "" } {
+ puts -nonewline "(default: $default_) ? "
+ } else {
+ puts -nonewline "? "
+ }
+ flush stdout
+ gets stdin $var_
+ if { [set $var_] == "" && $default_ != "" } {
+ set $var_ $default_
+ }
+}
+
+# puts the user requested channels into a neat, ordered list
+proc splitchans {inch_} {
+ global changroup
+ set outch [list]
+ foreach range [split $inch_ {, }] {
+ set start [lindex [split $range -] 0]
+ set stop [lindex [split $range -] end]
+ if { [string is digit $start] && [string is digit $stop] } {
+ set ::changroup "channel"
+ for {set ch $start} {$ch <= $stop} {incr ch} {
+ if { [lsearch $outch $ch] == -1 } {
+ lappend outch $ch
+ }
+ }
+ } else {
+ set ::changroup "group"
+ foreach ch [split $range -] {
+ lappend outch $ch
+ }
+ }
+ }
+ return [lsort -dictionary $outch]
+}
+
+# writes out a file in the temporary directory,
+# then changes the mtime of the file before
+# sticking it into the outgoing spool directory
+# (where pbx_spool will be looking)
+proc spool {channel_ callcnt_ when_} {
+ set callstr "
+Channel: $::technology/$channel_/$::destination
+Context: $::context
+Extension: $::extension
+Priority: $::priority
+WaitTime: $::timeout
+RetryTime: $::retrytime
+MaxRetries: $::maxretries
+Callerid: $::clid
+SetVar: $::astvar
+Account: $::account
+"
+ set fn "loadtest.call$callcnt_.ch$channel_"
+ set fd [open $::TEMPDIR/$fn w]
+ puts $fd $callstr
+ close $fd
+ file mtime $::TEMPDIR/$fn $when_
+ file rename -force $::TEMPDIR/$fn $::SPOOLDIR/$fn
+}
+
+# prompt the user for some info
+get technology "Zap" "\nEnter technology type
+Zap, IAX, SIP, etc."
+get chans "1" "\nEnter channel(s) or group to test in formats like
+2\n1-4\n3 5 7 9\n1-23,25-47,49-71,73-95\ng4\ng2,g1"
+set channels [splitchans $chans]
+
+get destination "s" "\nEnter destination number"
+get context "default" "\nEnter context"
+get extension "s" "\nEnter extension"
+get priority "1" "\nEnter priority"
+get timeout "45" "\nEnter timeout for call to be answered in seconds"
+get maxretries "0" "\nEnter maximum number of retries"
+
+if { $maxretries > 0 } {
+ get retrytime "300" "\nEnter time between retries in seconds"
+} else {
+ set retrytime 300
+}
+
+get clid "" "\nEnter callerid"
+get astvar "" "\nEnter some extra variables"
+get account "loadtest" "\nEnter account code"
+get calls "1" "\nEnter number of test calls per $changroup"
+get period "60" "\nEnter period between placing calls on a particular $changroup in seconds"
+
+if { [llength $channels] > 1 } {
+ get rate "0" "\nEnter period between placing each call in seconds
+0 will send a call on each $changroup every $period seconds
+1 will send a call on $changroup [lindex $channels 0] at [expr {$period + 0}]s, [lindex $channels 1] at [expr {$period + 1 }]s, etc.
+5 will send a call on $changroup [lindex $channels 0] at [expr {$period + 0}]s, [lindex $channels 1] at [expr {$period + 5 }]s, etc."
+} else {
+ set rate 0
+}
+
+puts -nonewline "\nCreating spooled call files... "
+set now [clock seconds]
+set spoolcnt 0
+set spinner [list / - \\ |]
+for {set i 0} {$i < $calls} {incr i} {
+ foreach ch $channels {
+ set chidx [lsearch $channels $ch]
+ spool $ch [incr spoolcnt] [expr {$now + ($i * $period) + ($rate * $chidx)}]
+ puts -nonewline "\b"
+ puts -nonewline [lindex $spinner [expr {$spoolcnt % 4}]]
+ flush stdout
+ }
+}
+puts "\b$spoolcnt calls placed into $SPOOLDIR !"
diff --git a/trunk/contrib/scripts/lookup.agi b/trunk/contrib/scripts/lookup.agi
new file mode 100644
index 000000000..4b682b837
--- /dev/null
+++ b/trunk/contrib/scripts/lookup.agi
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+#
+# Use Reverse Lookups to populate valuable information
+#
+# Copyright (C) 2005 Digium, Inc.
+#
+# Mark Spencer <markster@digium.com>
+#
+# Based on work of Joe Fratantoni - BrakeDanceJ - Joe@UnrealDestination.com.
+#
+# This program is Free Software distributed under the terms of the GNU
+# General Public License version 2. See LICENSE for details.
+#
+#
+use LWP::UserAgent;
+my %AGI;
+my $debug = 0;
+$|=1;
+sub url_decode {
+ my @args = @_;
+ s/%([0-9A-F]{2})/chr hex $1/egios for @args;
+ s/\"//egios for @args;
+ return wantarray ? @args : $args[0];
+}
+
+while(<STDIN>) {
+ chomp;
+ last unless length($_);
+ if (/^agi_(\w+)\:\s+(.*)$/) {
+ $AGI{$1} = $2;
+ }
+}
+
+alarm(4);
+my $number = $AGI{'callerid'};
+$number =~ /(\d+)/;
+$number = $1;
+die("You must specify a number") unless $number;
+my $ua = LWP::UserAgent->new;
+$ua->agent("Asterisk");
+my $req = HTTP::Request->new(POST => 'http://www.411.com/10668/search/Reverse_Phone');
+$req->content_type('application/x-www-form-urlencoded');
+$req->content("phone=$number");
+my $res = $ua->request($req);
+if ($res->is_success) {
+ my $first, $last, $address, $street, $house, $city, $state, $zip, $phone;
+ if ($res->content =~ /PAGE: PHONE_NOT_FOUND/) {
+ # Limited Information
+ $res->content =~ /is a \s+([A-Za-z -]*), ([A-Z]{2}) \s+based phone number and the registered carrier is (.*)\.\s+/;
+ ($city, $state, $last) =
+ map { url_decode($_) } ($1, $2, $3);
+ $cidname = "$city, $state";
+ } else {
+ # Full Information
+ $res->content =~ /RM_HTML_FIRST_ESC_=(.*)&_RM_HTML_LAST_ESC_=(.*)&_RM_HTML_ADDRESS_ESC_=(.*)&_RM_HTML_STREET_ESC_=(.*)&_RM_HTML_HOUSE_ESC_=(.*)&_RM_HTML_CITY_ESC_=(.*)&_RM_HTML_STATE_ESC_=(.*)&_RM_HTML_ZIP_ESC_=(.*)&_RM_HTML_PHONE_ESC_=(.*)&CITY=(.*)&STATE=(.*)/;
+ ($first, $last, $address, $street, $house, $city, $state, $zip, $phone) =
+ map { url_decode($_) } ($1, $2, $3, $4, $5, $6, $7, $8, $9);
+ my $cidname = $last;
+ if ($first) {
+ $cidname = $first . " " . $last;
+ } else {
+ $cidname = $last;
+ }
+ }
+ print STDOUT "SET VARIABLE CALLERID(name) \"$cidname\"\n";
+ <STDIN>;
+ print STDOUT "SET VARIABLE CALLER_ZIP \"$zip\"\n";
+ <STDIN>;
+ print STDOUT "SET VARIABLE CALLER_STATE \"$state\"\n";
+ <STDIN>;
+ print STDOUT "SET VARIABLE CALLER_CITY \"$city\"\n";
+ <STDIN>;
+ print STDOUT "SET VARIABLE CALLER_ADDRESS \"$address\"\n";
+ <STDIN>;
+ print STDOUT "SET VARIABLE CALLER_LAST \"$last\"\n";
+ <STDIN>;
+ print STDOUT "SET VARIABLE CALLER_FIRST \"$first\"\n";
+ <STDIN>;
+ print STDERR "First: $first\n" .
+ "Last: $last\n" .
+ "Address: $address\n" .
+ "Street: $street\n" .
+ "House: $house\n" .
+ "City: $city\n" .
+ "State: $state\n" .
+ "Zip: $zip\n" .
+ "Phone: $phone\n" if $debug;
+} else {
+ print STDERR $res->status_line . "\n";
+}
diff --git a/trunk/contrib/scripts/managerproxy.pl b/trunk/contrib/scripts/managerproxy.pl
new file mode 100644
index 000000000..cdf79a239
--- /dev/null
+++ b/trunk/contrib/scripts/managerproxy.pl
@@ -0,0 +1,242 @@
+#!/usr/bin/perl -w
+#
+# Simple Asterisk Manager Proxy, Version 1.01
+# 2004-09-26
+# Copyright (c) 2004 David C. Troy <dave@popvox.com>
+#
+# This code is based on Flash Operator Panel 'op_server.pl'
+# by Nicolas Gudino
+#  Copyright (C) 2004.
+#
+# David C. Troy <dave@popvox.com>
+# Nicolas Gudino <nicolas@house.com.ar>
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License.
+#
+# Security consideration: This script will open your manager port
+# for unauthenticated logins. Be careful out there :-)
+#############################
+
+#############################
+# Perl Prerequisites
+#############################
+use strict;
+use IO::Socket;
+use IO::Select;
+use POSIX qw(setsid);
+
+#############################
+# User Configurable Options
+#############################
+# Configuration for logging in to your asterisk server
+# Check you Asterisk config file "manager.conf" for details
+my $manager_host = '127.0.0.1';
+my $manager_port = 5038;
+my $manager_user = 'your_username';
+my $manager_secret = 'your_secret';
+# Port For this proxy
+my $listen_port = 1234;
+my $manager_pid = "/var/run/asterisk_managerproxy.pid";
+
+#############################
+# Declarations
+#############################
+my %proxy_clients;
+my $O;
+my $p;
+my @S;
+my %blocks;
+my $debug = 0;
+
+$SIG{PIPE} = 'IGNORE';
+$SIG{INT} = 'close_all';
+$SIG{USR1} = 'list_clients';
+
+if (defined($ARGV[0]))
+{
+ if ($ARGV[0] eq "-d")
+ {
+ defined(my $pid = fork) or die "Can't Fork: $!";
+ exit if $pid;
+ setsid or die "Can't start a new session: $!";
+ open MYPIDFILE, ">$manager_pid";
+ print MYPIDFILE $$;
+ close MYPIDFILE;
+ }
+} else {
+ $debug = 1;
+}
+
+
+# Connect to manager
+$p =
+ new IO::Socket::INET->new(
+ PeerAddr => $manager_host,
+ PeerPort => $manager_port,
+ Proto => "tcp",
+ Type => SOCK_STREAM
+ )
+ or die "\nCould not connect to Asterisk Manager Port at $manager_host\n";
+
+$p->autoflush(1);
+
+# Login to Manager
+send_command_to_manager( "Action: Login\r\nUsername: $manager_user\r\nSecret: $manager_secret\r\n\r\n" );
+
+# Start up listener for new connections
+my $m =
+ new IO::Socket::INET(Listen => 1, LocalPort => $listen_port, ReuseAddr => 1)
+ or die "\nCan't listen to port $listen_port\n";
+$O = new IO::Select();
+$O->add($m);
+$O->add($p);
+$/ = "\0";
+
+sub manager_reconnect()
+{
+ my $attempt = 1;
+ my $total_attempts = 60;
+
+ do
+ {
+ log_debug("** Attempt reconnection to manager port # $attempt", 16);
+ $p =
+ new IO::Socket::INET->new(
+ PeerAddr => $manager_host,
+ PeerPort => $manager_port,
+ Proto => "tcp",
+ Type => SOCK_STREAM
+ );
+ $attempt++;
+ if ($attempt > $total_attempts)
+ {
+ die("!! Could not reconnect to Asterisk Manager port");
+ }
+ sleep(10); # wait 10 seconds before trying to reconnect
+ } until $p;
+ $O->add($p);
+ send_command_to_manager(
+ "Action: Login\r\nUsername: $manager_user\r\nSecret: $manager_secret\r\n\r\n"
+ );
+}
+
+# Loop, continuously processing new connections, input from those connections, and input from Manager conn
+while (1)
+{
+ while (@S = $O->can_read)
+ {
+ foreach (@S)
+ {
+ if ($_ == $m)
+ {
+ log_debug("** New client connection", 16);
+ my $C = $m->accept;
+ $proxy_clients{$C} = \$C;
+ print "New Connection: $C\n" if $debug;
+ $O->add($C);
+ } else {
+ # It's not a new client connection
+ my $i;
+ my $R = sysread($_, $i, 2); # 2048; interleave every two bytes?
+ if (defined($R) && $R == 0)
+ {
+ # Confirm it's really dead by trying to write to it?
+ my $T = syswrite($_, ' ', 2); # 2048
+ if (!defined($T))
+ {
+ # connection went away...
+ $O->remove($_);
+ $_->close;
+
+ # If we lost the socket for the Asterisk Mgr, then reconnect
+ if ($_ == $p)
+ {
+ log_debug(
+ "** Asterisk Manager connection lost!!!!!",
+ 16);
+ manager_reconnect();
+ } else {
+ # Remove handle from proxy_clients hash
+ print "Closed Connection: $_\n" if $debug;
+ delete $proxy_clients{$_};
+ }
+ }
+ }
+ else # Socket is active and we are ready to read something from it
+ {
+ $blocks{$_} .= $i;
+ next if ($blocks{$_} !~ /\r\n\r\n$/);
+ # do a 'next' unless we have completed a block; we are not ready to continue
+
+ # Process the completed block
+ # If block is from asterisk, send to clients
+ if ($_ == $p) {
+ # block is from asterisk, send to clients
+ print "asterisk: $_\n$blocks{$_}" if $debug;
+ my $cnt = 0;
+ foreach my $client (values %proxy_clients) {
+ print "writing to $$client...\n" if $debug;
+ syswrite($$client, $blocks{$_});
+ $cnt++;
+ }
+ print "sent block to $cnt clients\n" if $debug;
+ } else {
+ # Blocks are from clients, send to asterisk
+ syswrite($p, $blocks{$_});
+ print "client: $_\n$blocks{$_}\n" if $debug;
+ }
+ delete $blocks{$_};
+
+ } # end if read succeeded
+ } # end if new client connection
+ } # end foreach @S -> can read
+ } # while can read
+} # endless loop
+
+sub close_all
+{
+ log_debug("Exiting...", 0);
+
+ foreach my $hd ($O->handles)
+ {
+ $O->remove($hd);
+ close($hd);
+ }
+
+ exit(0);
+}
+
+sub send_command_to_manager
+{
+ my $comando = shift;
+ if (defined $p)
+ {
+ my @lineas = split("\r\n", $comando);
+ foreach my $linea (@lineas)
+ {
+ syswrite($p, "$linea\r\n");
+ log_debug("-> $linea", 2);
+ }
+ log_debug(" ", 2);
+ syswrite($p, "\r\n");
+ }
+}
+
+sub log_debug
+{
+ my $texto = shift;
+ $texto =~ s/\0//g;
+ print "$texto\n" if $debug;
+}
+
+sub list_clients()
+{
+ my $cnt = 0;
+ foreach my $client (values %proxy_clients) {
+ print "client: $$client\n";
+ $cnt++;
+ }
+ print "$cnt clients.\n\n";
+}
+
diff --git a/trunk/contrib/scripts/meetme.sql b/trunk/contrib/scripts/meetme.sql
new file mode 100644
index 000000000..19c4ed745
--- /dev/null
+++ b/trunk/contrib/scripts/meetme.sql
@@ -0,0 +1,12 @@
+--
+-- Table structure for Realtime meetme
+--
+
+CREATE TABLE meetme (
+ confno char(80) DEFAULT '0' NOT NULL,
+ pin char(20) NULL,
+ adminpin char(20) NULL,
+ members integer DEFAULT 0 NOT NULL,
+ PRIMARY KEY (confno)
+);
+
diff --git a/trunk/contrib/scripts/messages-expire.pl b/trunk/contrib/scripts/messages-expire.pl
new file mode 100644
index 000000000..993997899
--- /dev/null
+++ b/trunk/contrib/scripts/messages-expire.pl
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+#
+# Script to expire voicemail after a specified number of days
+# by Steve Creel <screel@turbs.com>
+#
+
+# Directory housing the voicemail spool for asterisk
+$dir = "/var/spool/asterisk/voicemail";
+
+# Context for which the script should be running
+$context = "default";
+
+# Age (Delete files older than $age days old)
+$age = 31;
+
+# Age for unheard messages (Defaults to same age for all messages)
+# Set to 0 to not delete unheard messages
+$unheardage = $age;
+
+
+# Delete all files older than $age and $unheardage
+# (named msg????.??? to get the audio and txt files,
+# but we don't delete greetings or the user's name)
+
+if($age==$unheardage) {
+
+ # Save time by doing one find if we're treating everything the same
+ system('find '.$dir.'/'.$context.' -name msg????.??? -mtime +'.$age.' -exec rm {} \; -exec echo Deleted {} \;');
+
+} else {
+
+ # Find everything not in a folder called 'INBOX' and delete it after $age days
+ system('find '.$dir.'/'.$context.' -path \'*INBOX*\' -prune -o -name msg????.??? -mtime +'.$age.' -exec rm {} \; -exec echo Deleted {} \;');
+
+ # If unheardage is set to 0, we won't delete any unheard messages
+ if($unheardage > 0) {
+
+ # Delete things that are in a folder called INBOX after $unheardage days
+ system('find '.$dir.'/'.$context.' -path \'*INBOX*\' -name msg????.??? -mtime +'.$unheardage.' -exec rm {} \; -exec echo Deleted {} \;');
+
+ }
+}
+
+# For testing - what number to we start when we renumber?
+$start = "0";
+
+# Rename to msg and a 4 digit number, 0 padded.
+$fnbase = sprintf "msg%04d", $start;
+
+# Make $dir include the context too
+$dir.="/".$context;
+
+( -d $dir ) || die "Can't read list of mailboxes ($dir): $!\n";
+@mailboxes = `ls -A1 $dir`;
+chomp(@mailboxes);
+
+$save_fnbase = $fnbase;
+
+foreach $mailbox (@mailboxes) {
+
+ ( -d $dir."/".$mailbox) || die "Can't read list of folders (".$dir."/".$mailbox."): $!\n";
+ @folders = `ls -A1 $dir/$mailbox`;
+ chomp(@folders);
+
+ foreach $folder (@folders) {
+ if (-d $dir."/".$mailbox."/".$folder) {
+ ( -d $dir."/".$mailbox."/".$folder) || die "Can't read list of messages (".$dir."/".$mailbox."/".$folder.") $!\n";
+ @files = `ls -A1 $dir/$mailbox/$folder/`;
+
+ # Sort so everything is in proper order.
+ @files = sort @files;
+ chomp(@files);
+
+ # If there is still (after deleting old files earlier in the
+ # script) a msg0000.txt, we don't need to shuffle anything
+ # in this folder.
+ if (-f $dir."/".$mailbox."/".$folder."/msg0000.txt") { next; }
+
+ foreach $ext (("WAV", "wav", "gsm", "txt")) {
+ # Reset the fnbase for each file type
+ $fnbase = $save_fnbase;
+
+ foreach $file (@files) {
+ if ( $file =~ /$ext/ ) {
+ chdir($dir."/".$mailbox."/".$folder."/") || die "Can't change folder: $!";
+ print "Renaming: ".$dir."/".$mailbox."/".$folder."/".$file." to ".$fnbase.".".$ext."\n";
+ rename($file, $fnbase.".".$ext) || die "Cannot rename: $!";
+ $fnbase++;
+ }
+ }
+ }
+ }
+ }
+}
+
+__END__ \ No newline at end of file
diff --git a/trunk/contrib/scripts/postgres_cdr.sql b/trunk/contrib/scripts/postgres_cdr.sql
new file mode 100644
index 000000000..a4701bd77
--- /dev/null
+++ b/trunk/contrib/scripts/postgres_cdr.sql
@@ -0,0 +1,33 @@
+
+/*
+ * Id: postgres_cdr.sql,v 1.8.2.11 2003/10/10 11:15:43 pnixon Exp $
+ *
+ * --- Peter Nixon [ codemonkey@peternixon.net ]
+ *
+ * This is a PostgreSQL schema for doing CDR accounting with Asterisk
+ *
+ * The calls will automatically be logged as long as the module is loaded.
+ *
+ */
+
+
+CREATE TABLE cdr (
+ AcctId BIGSERIAL PRIMARY KEY,
+ calldate TIMESTAMP with time zone NOT NULL DEFAULT now(),
+ clid VARCHAR(80) NOT NULL default '',
+ src VARCHAR(80) NOT NULL default '',
+ dst VARCHAR(80) NOT NULL default '',
+ dcontext VARCHAR(80) NOT NULL default '',
+ channel VARCHAR(80) NOT NULL default '',
+ dstchannel VARCHAR(80) NOT NULL default '',
+ lastapp VARCHAR(80) NOT NULL default '',
+ lastdata VARCHAR(80) NOT NULL default '',
+ duration INTEGER NOT NULL default '0',
+ billsec INTEGER NOT NULL default '0',
+ disposition VARCHAR(45) NOT NULL default '',
+ amaflags INTEGER NOT NULL default '0',
+ accountcode VARCHAR(20) NOT NULL default '',
+ uniqueid VARCHAR(32) NOT NULL default '',
+ userfield VARCHAR(255) NOT NULL default ''
+);
+
diff --git a/trunk/contrib/scripts/qview.pl b/trunk/contrib/scripts/qview.pl
new file mode 100644
index 000000000..940e474f7
--- /dev/null
+++ b/trunk/contrib/scripts/qview.pl
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+#
+# Asterisk Queue Viewer
+# Uses management interface to query call queues on a machine
+# (C) 2003 David C. Troy -- dave@toad.net
+#
+# This program is free software, distributed under the terms of the
+# GNU General Public License
+#
+
+use IO::Socket;
+use CGI qw(:standard);
+use CGI::Carp qw/fatalsToBrowser/;
+
+$host = "asterisk.yourdomain.com";
+$port = 5038;
+$user = "manager_user";
+$secret = "Manager_secret";
+$EOL = "\015\012";
+$BLANK = $EOL x 2;
+$queue = param('queue');
+
+$remote = IO::Socket::INET->new(
+ Proto => 'tcp', # protocol
+ PeerAddr=> $host, # Address of server
+ PeerPort=> $port, # port of server
+ Reuse => 1
+ ) or die "$!";
+
+$remote->autoflush(1); # Send immediately
+
+# Login and get our booty from Asterisk
+$logres = send_cmd("Action: Login${EOL}Username: $user${EOL}Secret: $secret$BLANK");
+$qinfo = send_cmd("Action: queues$BLANK$EOL");
+$logres = send_cmd("Action: Logoff$BLANK");
+close $remote; # Close socket
+
+my %qcalls = map { /(\S+)\s+has (\d+) calls.*?\n\n/sg; } $qinfo;
+my %qmax = map { /(\S+)\s+has \d+ calls \(max (\S+)\).*?\n\n/sg; } $qinfo;
+my %qstrat = map { /(\S+)\s+has \d+ calls \(max \S+\) in (\S+) strategy.*?\n\n/sg; } $qinfo;
+my %qmems = map { /(\S+)\s+has \d+ calls.*?Members:.*?\s{6}(.*?)\s{3}\S*?\s*?Callers/sg; } $qinfo;
+my %qcallers = map { /(\S+)\s+has \d+ calls.*?([No ]*Callers.*?)\n\n/sg; } $qinfo;
+
+print header();
+print start_html(-head=>meta({-http_equiv=>'Refresh', -content=>'120'}),
+ -title=>"PBX Queue Viewer",
+ -style=>{'src'=>'/pbxinfo.css'});
+print "<table width=850><tr>";
+
+$col = 0;
+
+foreach $q (keys %qcalls) {
+
+ $mems = $qmems{$q};
+ $mems =~ s/ //g;
+ $mems =~ s/\n/<br>\n/g;
+ $callers = $qcallers{$q};
+ $callers =~ s/ //g;
+ $callers =~ s/Callers:.*\n//g;
+ $callers =~ s/\n/<br>/g;
+
+ print qq{<td valign=top width=48%><table width=100%>
+<tr><th colspan=2><A HREF=/mrtg/qmon-$q.html>$q</A>&nbsp;&nbsp;$qcalls{$q} calls (max $qmax{$q}), $qstrat{$q} strategy</th></tr>
+<tr><td valign=top width=55%>$mems</td><td valign=top width=45%>$callers</td></tr>
+</table></td>
+};
+
+ print "</tr><tr>" if $col;
+ $col = 0 if $col++;
+
+}
+
+print "</table>";
+
+print end_html();
+
+exit(0);
+
+sub read_conn {
+
+ my $buf="";
+ while (<$remote>) {
+ last if $_ eq $EOL;
+ s/$EOL/\n/g;
+ $buf .= $_;
+ }
+
+ return $buf
+}
+
+sub send_cmd {
+ my $cmd = @_[0];
+
+ my $buf="";
+ print $remote $cmd;
+
+ $buf = read_conn();
+
+ return $buf;
+}
diff --git a/trunk/contrib/scripts/realtime_pgsql.sql b/trunk/contrib/scripts/realtime_pgsql.sql
new file mode 100644
index 000000000..c0de544ab
--- /dev/null
+++ b/trunk/contrib/scripts/realtime_pgsql.sql
@@ -0,0 +1,141 @@
+drop table extensions_conf;
+
+CREATE TABLE extensions_conf (
+id serial NOT NULL,
+context character varying(20) DEFAULT '' NOT NULL,
+exten character varying(20) DEFAULT '' NOT NULL,
+priority smallint DEFAULT 0 NOT NULL,
+app character varying(20) DEFAULT '' NOT NULL,
+appdata character varying(128)
+);
+
+drop table cdr;
+CREATE TABLE cdr (
+calldate timestamp with time zone DEFAULT now() NOT NULL,
+clid character varying(80) DEFAULT '' NOT NULL,
+src character varying(80) DEFAULT '' NOT NULL,
+dst character varying(80) DEFAULT '' NOT NULL,
+dcontext character varying(80) DEFAULT '' NOT NULL,
+channel character varying(80) DEFAULT '' NOT NULL,
+dstchannel character varying(80) DEFAULT '' NOT NULL,
+lastapp character varying(80) DEFAULT '' NOT NULL,
+lastdata character varying(80) DEFAULT '' NOT NULL,
+duration bigint DEFAULT 0::bigint NOT NULL,
+billsec bigint DEFAULT 0::bigint NOT NULL,
+disposition character varying(45) DEFAULT '' NOT NULL,
+amaflags bigint DEFAULT 0::bigint NOT NULL,
+accountcode character varying(20) DEFAULT '' NOT NULL,
+uniqueid character varying(32) DEFAULT '' NOT NULL,
+userfield character varying(255) DEFAULT '' NOT NULL
+);
+
+drop table sip_conf;
+CREATE TABLE sip_conf (
+id serial NOT NULL,
+name character varying(80) DEFAULT '' NOT NULL,
+accountcode character varying(20),
+amaflags character varying(7),
+callgroup character varying(10),
+callerid character varying(80),
+canreinvite character varying(3) DEFAULT 'yes',
+context character varying(80),
+defaultip character varying(15),
+dtmfmode character varying(7),
+fromuser character varying(80),
+fromdomain character varying(80),
+host character varying(31) DEFAULT '' NOT NULL,
+insecure character varying(4),
+"language" character varying(2),
+mailbox character varying(50),
+md5secret character varying(80),
+nat character varying(5) DEFAULT 'no' NOT NULL,
+permit character varying(95),
+deny character varying(95),
+mask character varying(95),
+pickupgroup character varying(10),
+port character varying(5) DEFAULT '' NOT NULL,
+qualify character varying(3),
+restrictcid character varying(1),
+rtptimeout character varying(3),
+rtpholdtimeout character varying(3),
+secret character varying(80),
+"type" character varying DEFAULT 'friend' NOT NULL,
+username character varying(80) DEFAULT '' NOT NULL,
+disallow character varying(100) DEFAULT 'all',
+allow character varying(100) DEFAULT 'g729;ilbc;gsm;ulaw;alaw',
+musiconhold character varying(100),
+regseconds bigint DEFAULT 0::bigint NOT NULL,
+ipaddr character varying(15) DEFAULT '' NOT NULL,
+regexten character varying(80) DEFAULT '' NOT NULL,
+cancallforward character varying(3) DEFAULT 'yes'
+);
+
+drop table voicemail_users;
+CREATE TABLE voicemail_users (
+id serial NOT NULL,
+customer_id bigint DEFAULT (0)::bigint NOT NULL,
+context character varying(50) DEFAULT '' NOT NULL,
+mailbox bigint DEFAULT (0)::bigint NOT NULL,
+"password" character varying(4) DEFAULT '0' NOT NULL,
+fullname character varying(50) DEFAULT '' NOT NULL,
+email character varying(50) DEFAULT '' NOT NULL,
+pager character varying(50) DEFAULT '' NOT NULL,
+stamp timestamp(6) without time zone NOT NULL
+);
+
+drop table queue_table;
+CREATE TABLE queue_table (
+name varchar(128),
+musiconhold varchar(128),
+announce varchar(128),
+context varchar(128),
+timeout int8,
+monitor_join bool,
+monitor_format varchar(128),
+queue_youarenext varchar(128),
+queue_thereare varchar(128),
+queue_callswaiting varchar(128),
+queue_holdtime varchar(128),
+queue_minutes varchar(128),
+queue_seconds varchar(128),
+queue_lessthan varchar(128),
+queue_thankyou varchar(128),
+queue_reporthold varchar(128),
+announce_frequency int8,
+announce_round_seconds int8,
+announce_holdtime varchar(128),
+retry int8,
+wrapuptime int8,
+maxlen int8,
+servicelevel int8,
+strategy varchar(128),
+joinempty varchar(128),
+leavewhenempty varchar(128),
+eventmemberstatus bool,
+eventwhencalled bool,
+reportholdtime bool,
+memberdelay int8,
+weight int8,
+timeoutrestart bool,
+PRIMARY KEY (name)
+) WITHOUT OIDS;
+ALTER TABLE queue_table OWNER TO asterisk;
+
+drop table queue_member_table;
+CREATE TABLE queue_member_table
+(
+queue_name varchar(128),
+interface varchar(128),
+penalty int8,
+PRIMARY KEY (queue_name, interface)
+) WITHOUT OIDS;
+
+GRANT ALL ON TABLE cdr TO asterisk;
+GRANT ALL ON TABLE extensions_conf TO asterisk;
+GRANT ALL ON TABLE sip_conf TO asterisk;
+GRANT ALL ON TABLE voicemail_users TO asterisk;
+GRANT ALL ON TABLE queue_member_table TO asterisk;
+GRANT ALL ON TABLE queue_table TO asterisk;
+
+
+
diff --git a/trunk/contrib/scripts/retrieve_extensions_from_mysql.pl b/trunk/contrib/scripts/retrieve_extensions_from_mysql.pl
new file mode 100644
index 000000000..ca195cfe5
--- /dev/null
+++ b/trunk/contrib/scripts/retrieve_extensions_from_mysql.pl
@@ -0,0 +1,113 @@
+#!/usr/bin/perl -Tw
+# Use these commands to create the appropriate tables in MySQL
+# If flags is 1 then this record is not included in the output extensions file
+#
+#CREATE TABLE extensions (
+# context CHAR(20) DEFAULT 'default' NOT NULL,
+# extension CHAR(20) NOT NULL,
+# priority INT(2) DEFAULT '1' NOT NULL,
+# application CHAR(20) NOT NULL,
+# args CHAR(50),
+# descr TEXT,
+# flags INT(1) DEFAULT '0' NOT NULL,
+# PRIMARY KEY(context, extension, priority)
+#);
+#
+#CREATE TABLE globals (
+# variable CHAR(20) NOT NULL,
+# value CHAR(50) NOT NULL,
+# PRIMARY KEY(variable, value)
+#);
+
+use DBI;
+################### BEGIN OF CONFIGURATION ####################
+
+# the name of the extensions table
+$table_name = "extensions";
+# the name of the globals table
+$global_table_name = "globals";
+# the path to the extensions.conf file
+# WARNING: this file will be substituted by the output of this program
+$extensions_conf = "/etc/asterisk/extensions.conf";
+# the name of the box the MySQL database is running on
+$hostname = "localhost";
+# the name of the database our tables are kept
+$database = "user";
+# username to connect to the database
+$username = "";
+# password to connect to the database
+$password = "";
+
+################### END OF CONFIGURATION #######################
+
+open EXTEN, ">$extensions_conf" || die "Cannot create/overwrite extensions file: $extensions_conf\n";
+
+$dbh = DBI->connect("dbi:mysql:dbname=$database;host=$hostname", "$username", "$password");
+$statement = "SELECT * from $global_table_name order by variable";
+my $result = $dbh->selectall_arrayref($statement);
+unless ($result) {
+ # check for errors after every single database call
+ print "dbh->selectall_arrayref($statement) failed!\n";
+ print "DBI::err=[$DBI::err]\n";
+ print "DBI::errstr=[$DBI::errstr]\n";
+ exit;
+}
+my @resultSet = @{$result};
+if ( $#resultSet > -1 ) {
+ print EXTEN "[globals]\n";
+ foreach $row (@{ $result }) {
+ my @result = @{ $row };
+ print EXTEN "$result[0] = $result[1]\n";
+ }
+ print EXTEN "\n";
+}
+
+$statement = "SELECT context from $table_name group by context";
+
+$result = $dbh->selectall_arrayref($statement);
+unless ($result) {
+ # check for errors after every single database call
+ print "dbh->selectall_arrayref($statement) failed!\n";
+ print "DBI::err=[$DBI::err]\n";
+ print "DBI::errstr=[$DBI::errstr]\n";
+}
+
+@resultSet = @{$result};
+if ( $#resultSet == -1 ) {
+ print "No extensions defined in $table_name\n";
+ exit;
+}
+
+foreach my $row ( @{ $result } ) {
+ my $context = @{ $row }[0];
+ print EXTEN "[$context]\n";
+ $statement = "SELECT * from $table_name where context='$context' order by extension, priority";
+ my $result = $dbh->selectall_arrayref($statement);
+ unless ($result) {
+ # check for errors after every single database call
+ print "dbh->selectall_arrayref($statement) failed!\n";
+ print "DBI::err=[$DBI::err]\n";
+ print "DBI::errstr=[$DBI::errstr]\n";
+ exit;
+ }
+
+ my @resSet = @{$result};
+ if ( $#resSet == -1 ) {
+ print "no results\n";
+ exit;
+ }
+ foreach my $row ( @{ $result } ) {
+ my @result = @{ $row };
+ if ($result[6] == 0) {
+ print EXTEN "exten => $result[1],$result[2],$result[3]";
+ print EXTEN "($result[4])" if defined $result[4];
+ print EXTEN "\t" if not defined $result[4];
+ print EXTEN "\t; $result[5]" if defined $result[5];
+ print EXTEN "\n";
+ }
+ }
+ print EXTEN "\n";
+}
+
+exit 0;
+
diff --git a/trunk/contrib/scripts/retrieve_extensions_from_sql.pl b/trunk/contrib/scripts/retrieve_extensions_from_sql.pl
new file mode 100644
index 000000000..cf17d0351
--- /dev/null
+++ b/trunk/contrib/scripts/retrieve_extensions_from_sql.pl
@@ -0,0 +1,158 @@
+#!/usr/bin/perl -Tw
+# Author: Peter Nixon <codemonkey@peternixon.net>
+# Date: April 2004
+# Copy Policy: GNU Public Licence Version 2 or later
+# URL: http://www.peternixon.net/code/
+# Supported: PostgreSQL, Oracle, MySQL
+# Copyright: 2004 Peter Nixon <codemonkey@petenixon.net>
+#
+# 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.
+#
+# $Id$
+#
+# Use these commands to create the appropriate SQL tables
+# If flags is 1 then the record is not included in the output extensions file
+#
+#CREATE TABLE extensions (
+# context VARCHAR(20) DEFAULT 'default' NOT NULL,
+# extension VARCHAR(20) NOT NULL,
+# priority INTEGER DEFAULT '1' NOT NULL,
+# application VARCHAR(20) NOT NULL,
+# args VARCHAR(50),
+# descr TEXT,
+# flags BOOLEAN DEFAULT '0' NOT NULL,
+# PRIMARY KEY(context, extension, priority)
+#);
+
+#CREATE TABLE globals (
+# variable VARCHAR(20) NOT NULL,
+# value VARCHAR(50) NOT NULL,
+# PRIMARY KEY(variable, value)
+#);
+
+use strict; # Make sure we write decent perl code
+
+require DBI; # We need database drivers for this thing to work
+
+################### BEGIN OF CONFIGURATION ####################
+
+my $table_name = "extensions"; # name of the extensions table
+my $global_table_name = "globals"; # name of the globals table
+my $extensions_conf = "/etc/asterisk/extensions.conf"; # path to extensions.conf
+# WARNING: this file will be substituted by the output of this program
+my $dbbrand = "Pg"; # Hint: "mysql" or any other Perl DBI driver.
+my $hostname = "localhost"; # The SQL server's hostname or IP
+my $database = "peter"; # the name of the database our tables are kept
+my $username = "peter"; # username to connect to the database
+my $password = ""; # password to connect to the database
+my $verbose = 1; # Verbosity Level (0 - 2)
+
+################### END OF CONFIGURATION #######################
+
+# You should not need to edit anything below here
+my $dbh;
+
+sub db_connect {
+ if ($verbose > 1) { print "DEBUG: Connecting to Database Host: $hostname\n" }
+ if ($hostname eq 'localhost') {
+ if ($verbose > 1) { print "DEBUG: SQL server is on localhost so using UNIX socket instead of network socket.\n" }
+ $dbh = DBI->connect("DBI:$dbbrand:dbname=$database", "$username", "$password")
+ or die "Couldn't connect to database: " . DBI->errstr;
+ }
+ else {
+ $dbh = DBI->connect("DBI:$dbbrand:dbname=$database;host=$hostname", "$username", "$password")
+ or die "Couldn't connect to database: " . DBI->errstr;
+ }
+}
+
+sub db_disconnect {
+ if ($verbose > 1) { print "DEBUG: Disconnecting from Database Host: $hostname\n" }
+ $dbh->disconnect
+ or warn "Disconnection failed: $DBI::errstr\n";
+}
+
+sub get_globals {
+ if ($verbose > 0) { print "Checking Database for [global] variables\n"; }
+ my $sth = $dbh->prepare("SELECT variable, value FROM $global_table_name ORDER BY variable")
+ or die "Couldn't prepare statement: " . $dbh->errstr;
+
+ $sth->execute() # Execute the query
+ or die "Couldn't execute SELECT statement: " . $sth->errstr;
+
+ if ($sth->rows > 0) {
+ print EXTEN "[globals]\n";
+ while (my @global = $sth->fetchrow_array()) {
+ print EXTEN "$global[0] = $global[1]\n";
+ }
+ print EXTEN "\n";
+ } else {
+ print "WARNING: You have no global variables set\n";
+ }
+ $sth->finish;
+}
+
+sub get_contexts {
+ if ($verbose > 0) { print "Checking Database for contexts\n"; }
+ my $sth = $dbh->prepare("SELECT context FROM $table_name GROUP BY context")
+ or die "Couldn't prepare statement: " . $dbh->errstr;
+
+ $sth->execute() # Execute the query
+ or die "Couldn't execute SELECT statement: " . $sth->errstr;
+
+ if ($sth->rows > 0) {
+ while (my @context = $sth->fetchrow_array()) {
+ print EXTEN "[$context[0]]\n";
+ &get_extensions($context[0]);
+ print EXTEN "\n";
+ }
+ print EXTEN "\n";
+ } else {
+ print "WARNING: You have no contexts defined in the $table_name table\n";
+ }
+ $sth->finish;
+}
+
+sub get_extensions {
+ my $context = $_[0]; my @extension;
+ if ($verbose > 0) { print " Checking Database for [$context] extensions\n"; }
+ my $sth = $dbh->prepare("SELECT extension, priority, application, args, descr FROM $table_name WHERE context='$context' AND flags = '0' ORDER BY extension, priority")
+ or die "Couldn't prepare statement: " . $dbh->errstr;
+
+ $sth->execute() # Execute the query
+ or die "Couldn't execute SELECT statement: " . $sth->errstr;
+
+ if ($sth->rows > 0) {
+ while (@extension = $sth->fetchrow_array()) {
+ print EXTEN "exten => $extension[0],$extension[1],$extension[2]";
+ print EXTEN "($extension[3])" if defined $extension[3];
+ print EXTEN " ; $extension[4]" if defined $extension[4];
+ print EXTEN "\n";
+ }
+ } else {
+ print "WARNING: You have no extensions for [$context]\n";
+ }
+ $sth->finish;
+}
+
+
+sub main {
+ open EXTEN, ">$extensions_conf" || die "Cannot create/overwrite extensions file: $extensions_conf\n";
+ &db_connect;
+ &get_globals;
+ &get_contexts;
+ &db_disconnect;
+ close EXTEN; # Close the file handle
+ if ($verbose > 0) { print "New $extensions_conf successfully written.\n"; }
+ return 1;
+}
+
+
+exit &main();
diff --git a/trunk/contrib/scripts/retrieve_sip_conf_from_mysql.pl b/trunk/contrib/scripts/retrieve_sip_conf_from_mysql.pl
new file mode 100644
index 000000000..03395a125
--- /dev/null
+++ b/trunk/contrib/scripts/retrieve_sip_conf_from_mysql.pl
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -Tw
+# Retrieves the sip user/peer entries from the database
+# Use these commands to create the appropriate tables in MySQL
+#
+#CREATE TABLE sip (id INT(11) DEFAULT -1 NOT NULL,keyword VARCHAR(20) NOT NULL,data VARCHAR(50) NOT NULL, flags INT(1) DEFAULT 0 NOT NULL,PRIMARY KEY (id,keyword));
+#
+# if flags = 1 then the records are not included in the output file
+
+use DBI;
+################### BEGIN OF CONFIGURATION ####################
+
+# the name of the extensions table
+$table_name = "sip";
+# the path to the extensions.conf file
+# WARNING: this file will be substituted by the output of this program
+$sip_conf = "/etc/asterisk/sip_additional.conf";
+# the name of the box the MySQL database is running on
+$hostname = "localhost";
+# the name of the database our tables are kept
+$database = "sip";
+# username to connect to the database
+$username = "root";
+# password to connect to the database
+$password = "";
+
+################### END OF CONFIGURATION #######################
+
+$additional = "";
+
+open EXTEN, ">$sip_conf" || die "Cannot create/overwrite extensions file: $sip_conf\n";
+
+$dbh = DBI->connect("dbi:mysql:dbname=$database;host=$hostname", "$username", "$password");
+$statement = "SELECT keyword,data from $table_name where id=0 and keyword <> 'account' and flags <> 1";
+my $result = $dbh->selectall_arrayref($statement);
+unless ($result) {
+ # check for errors after every single database call
+ print "dbh->selectall_arrayref($statement) failed!\n";
+ print "DBI::err=[$DBI::err]\n";
+ print "DBI::errstr=[$DBI::errstr]\n";
+ exit;
+}
+my @resultSet = @{$result};
+if ( $#resultSet > -1 ) {
+ foreach $row (@{ $result }) {
+ my @result = @{ $row };
+ $additional .= $result[0]."=".$result[1]."\n";
+ }
+}
+
+$statement = "SELECT data,id from $table_name where keyword='account' and flags <> 1 group by data";
+
+$result = $dbh->selectall_arrayref($statement);
+unless ($result) {
+ # check for errors after every single database call
+ print "dbh->selectall_arrayref($statement) failed!\n";
+ print "DBI::err=[$DBI::err]\n";
+ print "DBI::errstr=[$DBI::errstr]\n";
+}
+
+@resultSet = @{$result};
+if ( $#resultSet == -1 ) {
+ print "No sip accounts defined in $table_name\n";
+ exit;
+}
+
+foreach my $row ( @{ $result } ) {
+ my $account = @{ $row }[0];
+ my $id = @{ $row }[1];
+ print EXTEN "[$account]\n";
+ $statement = "SELECT keyword,data from $table_name where id=$id and keyword <> 'account' and flags <> 1 order by keyword";
+ my $result = $dbh->selectall_arrayref($statement);
+ unless ($result) {
+ # check for errors after every single database call
+ print "dbh->selectall_arrayref($statement) failed!\n";
+ print "DBI::err=[$DBI::err]\n";
+ print "DBI::errstr=[$DBI::errstr]\n";
+ exit;
+ }
+
+ my @resSet = @{$result};
+ if ( $#resSet == -1 ) {
+ print "no results\n";
+ exit;
+ }
+ foreach my $row ( @{ $result } ) {
+ my @result = @{ $row };
+ print EXTEN "$result[0]=$result[1]\n";
+ }
+ print EXTEN "$additional\n";
+}
+
+exit 0;
+
diff --git a/trunk/contrib/scripts/safe_asterisk b/trunk/contrib/scripts/safe_asterisk
new file mode 100644
index 000000000..05e44d895
--- /dev/null
+++ b/trunk/contrib/scripts/safe_asterisk
@@ -0,0 +1,184 @@
+#!/bin/bash
+# vim:textwidth=80:tabstop=4:shiftwidth=4:smartindent:autoindent
+
+CLIARGS="$*" # Grab any args passed to safe_asterisk
+TTY=9 # TTY (if you want one) for Asterisk to run on
+CONSOLE=yes # Whether or not you want a console
+#NOTIFY=ben@alkaloid.net # Who to notify about crashes
+#EXEC=/path/to/somescript # Run this command if Asterisk crashes
+#LOGFILE=/path/to/logfile # Where to place the normal logfile (disabled if blank)
+#SYSLOG=local0 # Which syslog facility to use (disabled if blank)
+MACHINE=`hostname` # To specify which machine has crashed when getting the mail
+DUMPDROP=/tmp
+SLEEPSECS=4
+ASTSBINDIR=__ASTERISK_SBIN_DIR__
+ASTPIDFILE=__ASTERISK_VARRUN_DIR__/asterisk.pid
+
+# comment this line out to have this script _not_ kill all mpg123 processes when
+# asterisk exits
+KILLALLMPG123=1
+
+# run asterisk with this priority
+PRIORITY=0
+
+# set system filemax on supported OSes if this variable is set
+# SYSMAXFILES=262144
+
+# set max files open with ulimit. On linux systems, this will be automatically
+# set to the system's maximum files open devided by two, if not set here.
+# MAXFILES=32768
+
+function message() {
+ echo "$1" >&2
+ if [ "$SYSLOG" != "" ]; then
+ logger -p "${SYSLOG}.warn" -t safe_asterisk[$$] "$1"
+ fi
+ if [ "$LOGFILE" != "" ]; then
+ echo "safe_asterisk[$$]: $1" >> "$LOGFILE"
+ fi
+}
+
+# since we're going to change priority and open files limits, we need to be
+# root. if running asterisk as other users, pass that to asterisk on the command
+# line.
+# if we're not root, fall back to standard everything.
+if [ `id -u` != 0 ]
+then
+ echo "Oops. I'm not root. Falling back to standard prio and file max." >&2
+ echo "This is NOT suitable for large systems." >&2
+ PRIORITY=0
+ message "safe_asterisk was started by `id -n` (uid `id -u`)."
+else
+ if `echo $OSTYPE | grep linux 2>&1 > /dev/null `
+ then
+ # maximum number of open files is set to the system maximum divided by two if
+ # MAXFILES is not set.
+ if [ "$MAXFILES" = "" ]
+ then
+ # just check if file-max is readable
+ if [ -r /proc/sys/fs/file-max ]
+ then
+ MAXFILES=$(( `cat /proc/sys/fs/file-max` / 2 ))
+ fi
+ fi
+ SYSCTL_MAXFILES="fs.file-max"
+ elif `echo $OSTYPE | grep darwin 2>&1 > /dev/null `
+ then
+ SYSCTL_MAXFILES="kern.maxfiles"
+ fi
+
+
+ if [ "$SYSMAXFILES" != "" ]
+ then
+ if [ "$SYSCTL_MAXFILES" != "" ]
+ then
+ sysctl -w $SYSCTL_MAXFILES=$SYSMAXFILES
+ fi
+ fi
+
+ # set the process's filemax to whatever set above
+ ulimit -n $MAXFILES
+
+fi
+
+#
+# Let Asterisk dump core
+#
+ulimit -c unlimited
+
+#
+# Don't fork when running "safely"
+#
+ASTARGS=""
+if [ "$TTY" != "" ]; then
+ if [ -c /dev/tty${TTY} ]; then
+ TTY=tty${TTY}
+ elif [ -c /dev/vc/${TTY} ]; then
+ TTY=vc/${TTY}
+ else
+ message "Cannot find specified TTY (${TTY})"
+ exit 1
+ fi
+ ASTARGS="${ASTARGS} -vvvg"
+ if [ "$CONSOLE" != "no" ]; then
+ ASTARGS="${ASTARGS} -c"
+ fi
+fi
+if [ ! -w ${DUMPDROP} ]; then
+ message "Cannot write to ${DUMPDROP}"
+ exit 1
+fi
+
+#
+# Don't die if stdout/stderr can't be written to
+#
+trap '' PIPE
+
+#
+# Run scripts to set any environment variables or do any other system-specific setup needed
+#
+
+if [ -d /etc/asterisk/startup.d ]; then
+ for script in /etc/asterisk/startup.d/*.sh; do
+ if [ -x ${script} ]; then
+ source ${script}
+ fi
+ done
+fi
+
+run_asterisk()
+{
+ while :; do
+
+ if [ "$TTY" != "" ]; then
+ cd /tmp
+ stty sane < /dev/${TTY}
+ nice -n $PRIORITY ${ASTSBINDIR}/asterisk -f ${CLIARGS} ${ASTARGS} >& /dev/${TTY} < /dev/${TTY}
+ else
+ cd /tmp
+ nice -n $PRIORITY ${ASTSBINDIR}/asterisk -f ${CLIARGS} ${ASTARGS}
+ fi
+ EXITSTATUS=$?
+ message "Asterisk ended with exit status $EXITSTATUS"
+ if [ "$EXITSTATUS" = "0" ]; then
+ # Properly shutdown....
+ message "Asterisk shutdown normally."
+ exit 0
+ elif [ $EXITSTATUS -gt 128 ]; then
+ let EXITSIGNAL=EXITSTATUS-128
+ echo "Asterisk exited on signal $EXITSIGNAL."
+ if [ "$NOTIFY" != "" ]; then
+ echo "Asterisk on $MACHINE exited on signal $EXITSIGNAL. Might want to take a peek." | \
+ mail -s "Asterisk Died" $NOTIFY
+ message "Exited on signal $EXITSIGNAL"
+ fi
+ if [ "$EXEC" != "" ]; then
+ $EXEC
+ fi
+
+ PID=`cat ${ASTPIDFILE}`
+ if [ -f /tmp/core.${PID} ]; then
+ mv /tmp/core.${PID} ${DUMPDROP}/core.`hostname`-`date -Iseconds` &
+ elif [ -f /tmp/core ]; then
+ mv /tmp/core ${DUMPDROP}/core.`hostname`-`date -Iseconds` &
+ fi
+ else
+ message "Asterisk died with code $EXITSTATUS."
+
+ PID=`cat ${ASTPIDFILE}`
+ if [ -f /tmp/core.${PID} ]; then
+ mv /tmp/core.${PID} ${DUMPDROP}/core.`hostname`-`date -Iseconds` &
+ elif [ -f /tmp/core ]; then
+ mv /tmp/core ${DUMPDROP}/core.`hostname`-`date -Iseconds` &
+ fi
+ fi
+ message "Automatically restarting Asterisk."
+ sleep $SLEEPSECS
+ if [ $KILLALLMPG123 ]
+ then
+ killall -9 mpg123
+ fi
+ done
+}
+
+run_asterisk &
diff --git a/trunk/contrib/scripts/safe_asterisk.8 b/trunk/contrib/scripts/safe_asterisk.8
new file mode 100644
index 000000000..ebd95142a
--- /dev/null
+++ b/trunk/contrib/scripts/safe_asterisk.8
@@ -0,0 +1,69 @@
+.TH SAFE_ASTERISK 8 "Jun 30th, 2005" "Asterisk" "Linux Programmer's Manual"
+.SH NAME
+.B safe_asterisk
+\(em A wrapper to run the asterisk executable in a loop
+.SH SYNOPSIS
+.PP
+.B safe_asterisk
+.I [ asterisk_params ]
+
+.SH DESCRIPTION
+.B safe_asterisk
+is a script that runs asterisk in a loop, which can be useful if you
+fear asterisk may crash.
+
+The script does not run in the background like a standard service. Rather,
+it runs in its own linux virtual console (9, by default).
+It also uses the option '-c' of asterisk(8) to avoid detaching asterisk
+from that terminal.
+
+safe_asterisk also runs asterisk with unlimited core file size, and thus
+asterisk will dump core in case of a crash.
+
+To get a "picture" of console 9, from another terminal (e.g: from a
+remote shell session) you can use:
+
+ screendump 9
+
+The init script of the Debian package should be able to run safe_asterisk
+as the asterisk service, if so configured. See coments in
+/etc/default/asterisk
+
+.SH FILES
+.B /tmp
+.RS
+safe_asterisk runs in that directory, rather than in / as usual.
+.RE
+
+.B /tmp/core
+.RS
+If core files were generated there, they may be
+.RE
+
+.B /etc/asterisk/startup.d
+.RS
+Files in this directory will be 'source'd by the safe_asterisk script before
+it starts Asterisk proper, allowing them to set additional environment variables
+or run any other steps that are needed for your system.
+.RE
+
+.SH BUGS
+While showing the output on a console is useful, using screen(1) as
+the terminal may be better.
+
+The script does not read configuration from standard location under /etc
+
+It uses fixed locations under /tmp , and thus may be exposed to a
+symlink attacks.
+
+.SH SEE ALSO
+asterisk(8), screendump(9)
+
+.SH "AUTHOR"
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/trunk/contrib/scripts/safe_asterisk_restart b/trunk/contrib/scripts/safe_asterisk_restart
new file mode 100644
index 000000000..81783149a
--- /dev/null
+++ b/trunk/contrib/scripts/safe_asterisk_restart
@@ -0,0 +1,110 @@
+#!/bin/bash
+# vim:textwidth=80:tabstop=4:shiftwidth=4:smartindent
+#
+# this scripts prompts the user thrice, then tells asterisk to please shut down,
+# then kills asterisk and related processes with SIGTERM, then kills asterisk
+# and related processes with SIGKILL, and then starts asterisk with
+# safe_asterisk. Three arguments are currently supported, --no-countdown,
+# --no-prompt and --no-stop-now-first
+
+LOGFILE=/var/log/asterisk/safe_asterisk_restart.log
+ASTERISK=/usr/sbin/asterisk
+SAFE_ASTERISK=/usr/sbin/safe_asterisk
+
+DELAY=1 # Seconds between steps in countdown
+COUNTDOWN_FROM=5 # Steps to count down
+DO_COUNTDOWN=1 # Should I do a countdown before restarting asterisk?
+DO_PROMPT=1 # Should I prompt the user?
+TRY_STOP_NOW_FIRST=1 # Attempt a 'stop now' before killing processes. Note
+ # that this might make this script hang if asterisk
+ # can't respond to the command.
+
+# processes to kill. Please list all AGI scripts here as well as the asterisk
+# processes, since asterisk may leave them unkilled.
+PROCVICTIMS="safe_asterisk asterisk mpg123"
+
+# helper functions
+# die ["string to print"]
+function die {
+ if [[ "$1" != "" ]]; then
+ echo $1
+ else
+ echo "ok. no harm done..."
+ fi
+ exit
+}
+
+# docmd "string to print" "cmd"
+function docmd {
+ printf "$1..."
+ `$2 >> $LOGFILE 2>&1`
+ RETCODE=$?
+ sleep $DELAY
+ if [[ "$RETCODE" == "0" ]]; then
+ echo " OK"
+ else
+ echo " FAILED"
+ fi
+}
+
+# prompt "string" "positive answer"
+function prompt {
+ printf "$1"
+ read answer
+ if [[ "$answer" != "$2" ]]; then
+ die
+ fi
+}
+
+# countdown secs
+function countdown {
+ echo -n "$1 "
+ if [[ $1 > 0 ]]; then
+ sleep 1
+ countdown $[ $1 - 1 ]
+ else
+ echo "boom!"
+ fi
+}
+
+# am I really root?
+if [[ "$UID" != "0" ]]; then
+ echo "Sorry, only root can do this." >&2
+ exit;
+fi
+
+echo "`date`: $0 invoked" >> $LOGFILE
+
+# bash
+for i
+do
+ if [[ "$i" == "--no-countdown" ]]
+ then
+ unset DO_COUNTDOWN
+ fi
+ if [[ "$i" == "--no-prompt" ]]
+ then
+ unset DO_PROMPT
+ fi
+ if [[ "$i" == "--no-stop-now-first" ]]
+ then
+ unset TRY_STOP_NOW_FIRST
+ fi
+done
+
+[[ $DO_PROMPT ]] && prompt "Are you sure you want to restart asterisk? (yes/no)? " "yes"
+[[ $DO_PROMPT ]] && prompt "Really sure? (yes/no)? " "yes"
+[[ $DO_PROMPT ]] && prompt "Absolutely positive? (YES/no)? " "YES"
+
+[[ $DO_COUNTDOWN ]] && echo "OK, I'll do it, but if you're not sure about this, press ctrl+c now."
+[[ $DO_COUNTDOWN ]] && countdown $COUNTDOWN_FROM
+
+# doing the dirty work
+[[ $TRY_STOP_NOW_FIRST ]] && docmd "Asking asterisk kindly to shutdown" "$ASTERISK -rx 'stop now'"
+docmd "Sending asterisk processes the TERM signal" "killall -15 $PROCVICTIMS"
+docmd "Sending asterisk processes KILL signal" "killall -9 $PROCVICTIMS"
+docmd "Starting safe_asterisk" "$SAFE_ASTERISK"
+for i in $PROCVICTIMS
+do
+ ps axf | grep -w $i | grep -v grep
+done
diff --git a/trunk/contrib/scripts/sip-friends.sql b/trunk/contrib/scripts/sip-friends.sql
new file mode 100644
index 000000000..15d7cb393
--- /dev/null
+++ b/trunk/contrib/scripts/sip-friends.sql
@@ -0,0 +1,14 @@
+#
+# Table structure for table `sipfriends`
+#
+
+CREATE TABLE `sipfriends` (
+ `name` varchar(40) NOT NULL default '',
+ `secret` varchar(40) NOT NULL default '',
+ `context` varchar(40) NOT NULL default '',
+ `username` varchar(40) default '',
+ `ipaddr` varchar(20) NOT NULL default '',
+ `port` int(6) NOT NULL default '0',
+ `regseconds` int(11) NOT NULL default '0',
+ PRIMARY KEY (`name`)
+) TYPE=MyISAM;
diff --git a/trunk/contrib/scripts/vmail.cgi b/trunk/contrib/scripts/vmail.cgi
new file mode 100644
index 000000000..a87fc1e84
--- /dev/null
+++ b/trunk/contrib/scripts/vmail.cgi
@@ -0,0 +1,1099 @@
+#!/usr/bin/perl
+#
+# Web based Voicemail for Asterisk
+#
+# Copyright (C) 2002, Linux Support Services, Inc.
+#
+# Distributed under the terms of the GNU General Public License
+#
+# Written by Mark Spencer <markster@linux-support.net>
+#
+# (icky, I know.... if you know better perl please help!)
+#
+#
+# Synchronization added by GDS Partners (www.gdspartners.com)
+# Stojan Sljivic (stojan.sljivic@gdspartners.com)
+#
+use CGI qw/:standard/;
+use Carp::Heavy;
+use CGI::Carp qw(fatalsToBrowser);
+use DBI;
+use Fcntl qw ( O_WRONLY O_CREAT O_EXCL );
+use Time::HiRes qw ( usleep );
+
+$context=""; # Define here your by default context (so you dont need to put voicemail@context in the login
+
+@validfolders = ( "INBOX", "Old", "Work", "Family", "Friends", "Cust1", "Cust2", "Cust3", "Cust4", "Cust5" );
+
+%formats = (
+ "wav" => {
+ name => "Uncompressed WAV",
+ mime => "audio/x-wav",
+ pref => 1
+ },
+ "WAV" => {
+ name => "GSM Compressed WAV",
+ mime => "audio/x-wav",
+ pref => 2
+ },
+ "gsm" => {
+ name => "Raw GSM Audio",
+ mime => "audio/x-gsm",
+ pref => 3
+ }
+);
+
+$astpath = "/_asterisk";
+
+$stdcontainerstart = "<table align=center width=600><tr><td>\n";
+$footer = "<hr><font size=-1><a href=\"http://www.asterisk.org\">The Asterisk Open Source PBX</a> Copyright 2004, <a href=\"http://www.digium.com\">Digium, Inc.</a></a>";
+$stdcontainerend = "</td></tr><tr><td align=right>$footer</td></tr></table>\n";
+
+sub lock_path() {
+
+ my($path) = @_;
+ my $rand;
+ my $rfile;
+ my $start;
+ my $res;
+
+ $rand = rand 99999999;
+ $rfile = "$path/.lock-$rand";
+
+ sysopen(RFILE, $rfile, O_WRONLY | O_CREAT | O_EXCL, 0666) or return -1;
+ close(RFILE);
+
+ $res = link($rfile, "$path/.lock");
+ $start = time;
+ if ($res == 0) {
+ while (($res == 0) && (time - $start <= 5)) {
+ $res = link($rfile, "$path/.lock");
+ usleep(1);
+ }
+ }
+ unlink($rfile);
+
+ if ($res == 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+sub unlock_path() {
+
+ my($path) = @_;
+
+ unlink("$path/.lock");
+}
+
+sub untaint() {
+
+ my($data) = @_;
+
+ if ($data =~ /^([-\@\w.]+)$/) {
+ $data = $1;
+ } else {
+ die "Security violation.";
+ }
+
+ return $data;
+}
+
+sub login_screen() {
+ print header;
+ my ($message) = @_;
+ print <<_EOH;
+
+<TITLE>Asterisk Web-Voicemail</TITLE>
+<BODY BGCOLOR="white">
+$stdcontainerstart
+<FORM METHOD="post">
+<input type=hidden name="action" value="login">
+<table align=center>
+<tr><td valign=top align=center rowspan=6><img align=center src="$astpath/animlogo.gif"></td></tr>
+<tr><td align=center colspan=2><font size=+2>Comedian Mail Login</font></td></tr>
+<tr><td align=center colspan=2><font size=+1>$message</font></td></tr>
+<tr><td>Mailbox:</td><td><input type=text name="mailbox"></td></tr>
+<tr><td>Password:</td><td><input type=password name="password"></td></tr>
+<tr><td align=right colspan=2><input value="Login" type=submit></td></tr>
+<input type=hidden name="context" value="$context">
+</table>
+</FORM>
+$stdcontainerend
+</BODY>\n
+_EOH
+
+}
+
+sub check_login()
+{
+ local ($filename, $startcat) = @_;
+ local ($mbox, $context) = split(/\@/, param('mailbox'));
+ local $pass = param('password');
+ local $category = $startcat;
+ local @fields;
+ local $tmp;
+ local (*VMAIL);
+ if (!$category) {
+ $category = "general";
+ }
+ if (!$context) {
+ $context = param('context');
+ }
+ if (!$context) {
+ $context = "default";
+ }
+ if (!$filename) {
+ $filename = "/etc/asterisk/voicemail.conf";
+ }
+# print header;
+# print "Including <h2>$filename</h2> while in <h2>$category</h2>...\n";
+ open(VMAIL, "<$filename") || die("Bleh, no $filename");
+ while(<VMAIL>) {
+ chomp;
+ if (/include\s\"([^\"]+)\"$/) {
+ ($tmp, $category) = &check_login("/etc/asterisk/$1", $category);
+ if (length($tmp)) {
+# print "Got '$tmp'\n";
+ return ($tmp, $category);
+ }
+ } elsif (/\[(.*)\]/) {
+ $category = $1;
+ } elsif ($category eq "general") {
+ if (/([^\s]+)\s*\=\s*(.*)/) {
+ if ($1 eq "dbname") {
+ $dbname = $2;
+ } elsif ($1 eq "dbpass") {
+ $dbpass = $2;
+ } elsif ($1 eq "dbhost") {
+ $dbhost = $2;
+ } elsif ($1 eq "dbuser") {
+ $dbuser = $2;
+ }
+ }
+ if ($dbname and $dbpass and $dbhost and $dbuser) {
+
+ # db variables are present. Use db for authentication.
+ my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass);
+ my $sth = $dbh->prepare(qq{select fullname,context from voicemail where mailbox='$mbox' and password='$pass' and context='$context'});
+ $sth->execute();
+ if (($fullname, $category) = $sth->fetchrow_array()) {;
+ return ($fullname ? $fullname : "Extension $mbox in $context",$category);
+ }
+ }
+ } elsif (($category ne "general") && ($category ne "zonemessages")) {
+ if (/([^\s]+)\s*\=\>?\s*(.*)/) {
+ @fields = split(/\,\s*/, $2);
+# print "<p>Mailbox is $1\n";
+ if (($mbox eq $1) && (($pass eq $fields[0]) || ("-${pass}" eq $fields[0])) && ($context eq $category)) {
+ return ($fields[1] ? $fields[1] : "Extension $mbox in $context", $category);
+ }
+ }
+ }
+ }
+ close(VMAIL);
+ return ("", $category);
+}
+
+sub validmailbox()
+{
+ local ($context, $mbox, $filename, $startcat) = @_;
+ local $category = $startcat;
+ local @fields;
+ local (*VMAIL);
+ if (!$context) {
+ $context = param('context');
+ }
+ if (!$context) {
+ $context = "default";
+ }
+ if (!$filename) {
+ $filename = "/etc/asterisk/voicemail.conf";
+ }
+ if (!$category) {
+ $category = "general";
+ }
+ open(VMAIL, "<$filename") || die("Bleh, no $filename");
+ while(<VMAIL>) {
+ chomp;
+ if (/include\s\"([^\"]+)\"$/) {
+ ($tmp, $category) = &validmailbox($mbox, $context, "/etc/asterisk/$1");
+ if ($tmp) {
+ return ($tmp, $category);
+ }
+ } elsif (/\[(.*)\]/) {
+ $category = $1;
+ } elsif ($category eq "general") {
+ if (/([^\s]+)\s*\=\s*(.*)/) {
+ if ($1 eq "dbname") {
+ $dbname = $2;
+ } elsif ($1 eq "dbpass") {
+ $dbpass = $2;
+ } elsif ($1 eq "dbhost") {
+ $dbhost = $2;
+ } elsif ($1 eq "dbuser") {
+ $dbuser = $2;
+ }
+ }
+ if ($dbname and $dbpass and $dbhost and $dbuser) {
+
+ # db variables are present. Use db for authentication.
+ my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass);
+ my $sth = $dbh->prepare(qq{select fullname,context from voicemail where mailbox='$mbox' and password='$pass' and context='$context'});
+ $sth->execute();
+ if (($fullname, $context) = $sth->fetchrow_array()) {;
+ return ($fullname ? $fullname : "unknown", $category);
+ }
+ }
+ } elsif (($category ne "general") && ($category ne "zonemessages") && ($category eq $context)) {
+ if (/([^\s]+)\s*\=\>?\s*(.*)/) {
+ @fields = split(/\,\s*/, $2);
+ if (($mbox eq $1) && ($context eq $category)) {
+ return ($fields[2] ? $fields[2] : "unknown", $category);
+ }
+ }
+ }
+ }
+ return ("", $category);
+}
+
+sub mailbox_options()
+{
+ local($context, $current, $filename, $category) = @_;
+ local (*VMAIL);
+ local $tmp2;
+ local $tmp;
+ if (!$filename) {
+ $filename = "/etc/asterisk/voicemail.conf";
+ }
+ if (!$category) {
+ $category = "general";
+ }
+# print header;
+# print "Including <h2>$filename</h2> while in <h2>$category</h2>...\n";
+ open(VMAIL, "<$filename") || die("Bleh, no voicemail.conf");
+ while(<VMAIL>) {
+ chomp;
+ s/\;.*$//;
+ if (/include\s\"([^\"]+)\"$/) {
+ ($tmp2, $category) = &mailbox_options($context, $current, "/etc/asterisk/$1", $category);
+# print "Got '$tmp2'...\n";
+ $tmp .= $tmp2;
+ } elsif (/\[(.*)\]/) {
+ $category = $1;
+ } elsif ($category eq "general") {
+ if (/([^\s]+)\s*\=\s*(.*)/) {
+ if ($1 eq "dbname") {
+ $dbname = $2;
+ } elsif ($1 eq "dbpass") {
+ $dbpass = $2;
+ } elsif ($1 eq "dbhost") {
+ $dbhost = $2;
+ } elsif ($1 eq "dbuser") {
+ $dbuser = $2;
+ }
+ }
+ if ($dbname and $dbpass and $dbhost and $dbuser) {
+
+ # db variables are present. Use db for authentication.
+ my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass);
+ my $sth = $dbh->prepare(qq{select mailbox,fullname,context from voicemail where context='$context' order by mailbox});
+ $sth->execute();
+ while (($mailbox, $fullname, $category) = $sth->fetchrow_array()) {
+ $text = $mailbox;
+ if ($fullname) {
+ $text .= " (".$fullname.")";
+ }
+ if ($mailbox eq $current) {
+ $tmp .= "<OPTION SELECTED>$text</OPTION>\n";
+ } else {
+ $tmp .= "<OPTION>$text</OPTION>\n";
+ }
+ }
+ return ($tmp, $category);
+ }
+ } elsif (($category ne "general") && ($category ne "zonemessages")) {
+ if (/([^\s]+)\s*\=\>?\s*(.*)/) {
+ @fields = split(/\,\s*/, $2);
+ $text = "$1";
+ if ($fields[1]) {
+ $text .= " ($fields[1])";
+ }
+ if ($1 eq $current) {
+ $tmp .= "<OPTION SELECTED>$text</OPTION>\n";
+ } else {
+ $tmp .= "<OPTION>$text</OPTION>\n";
+ }
+
+ }
+ }
+ }
+ close(VMAIL);
+ return ($tmp, $category);
+}
+
+sub mailbox_list()
+{
+ local ($name, $context, $current) = @_;
+ local $tmp;
+ local $text;
+ local $tmp;
+ local $opts;
+ if (!$context) {
+ $context = "default";
+ }
+ $tmp = "<SELECT name=\"$name\">\n";
+ ($opts) = &mailbox_options($context, $current);
+ $tmp .= $opts;
+ $tmp .= "</SELECT>\n";
+
+}
+
+sub msgcount()
+{
+ my ($context, $mailbox, $folder) = @_;
+ my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder";
+ if (opendir(DIR, $path)) {
+ my @msgs = grep(/^msg....\.txt$/, readdir(DIR));
+ closedir(DIR);
+ return sprintf "%d", $#msgs + 1;
+ }
+ return "0";
+}
+
+sub msgcountstr()
+{
+ my ($context, $mailbox, $folder) = @_;
+ my $count = &msgcount($context, $mailbox, $folder);
+ if ($count > 1) {
+ "$count messages";
+ } elsif ($count > 0) {
+ "$count message";
+ } else {
+ "no messages";
+ }
+}
+sub messages()
+{
+ my ($context, $mailbox, $folder) = @_;
+ my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder";
+ if (opendir(DIR, $path)) {
+ my @msgs = sort grep(/^msg....\.txt$/, readdir(DIR));
+ closedir(DIR);
+ return map { s/^msg(....)\.txt$/$1/; $_ } @msgs;
+ }
+ return ();
+}
+
+sub getcookie()
+{
+ my ($var) = @_;
+ return cookie($var);
+}
+
+sub makecookie()
+{
+ my ($format) = @_;
+ cookie(-name => "format", -value =>["$format"], -expires=>"+1y");
+}
+
+sub getfields()
+{
+ my ($context, $mailbox, $folder, $msg) = @_;
+ my $fields;
+ if (open(MSG, "</var/spool/asterisk/voicemail/$context/$mailbox/$folder/msg${msg}.txt")) {
+ while(<MSG>) {
+ s/\#.*$//g;
+ if (/^(\w+)\s*\=\s*(.*)$/) {
+ $fields->{$1} = $2;
+ }
+ }
+ close(MSG);
+ $fields->{'msgid'} = $msg;
+ } else { print "<BR>Unable to open '$msg' in '$mailbox', '$folder'\n<B>"; }
+ $fields;
+}
+
+sub message_prefs()
+{
+ my ($nextaction, $msgid) = @_;
+ my $folder = param('folder');
+ my $mbox = param('mailbox');
+ my $context = param('context');
+ my $passwd = param('password');
+ my $format = param('format');
+ if (!$format) {
+ $format = &getcookie('format');
+ }
+ print header;
+ print <<_EOH;
+
+<TITLE>Asterisk Web-Voicemail: Preferences</TITLE>
+<BODY BGCOLOR="white">
+$stdcontainerstart
+<FORM METHOD="post">
+<table width=100% align=center>
+<tr><td align=right colspan=3><font size=+2>Web Voicemail Preferences</font></td></tr>
+<tr><td align=left><font size=+1>Preferred&nbsp;Audio&nbsp;Format:</font></td><td colspan=2></td></tr>
+_EOH
+
+foreach $fmt (sort { $formats{$a}->{'pref'} <=> $formats{$b}->{'pref'} } keys %formats) {
+ my $clicked = "checked" if $fmt eq $format;
+ print "<tr><td></td><td align=left><input type=radio name=\"format\" $clicked value=\"$fmt\"></td><td width=100%>&nbsp;$formats{$fmt}->{name}</td></tr>\n";
+}
+
+print <<_EOH;
+<tr><td align=right colspan=3><input type=submit value="save settings..."></td></tr>
+</table>
+<input type=hidden name="action" value="$nextaction">
+<input type=hidden name="folder" value="$folder">
+<input type=hidden name="mailbox" value="$mbox">
+<input type=hidden name="context" value="$context">
+<input type=hidden name="password" value="$passwd">
+<input type=hidden name="msgid" value="$msgid">
+$stdcontainerend
+</BODY>\n
+_EOH
+
+}
+
+sub message_play()
+{
+ my ($message, $msgid) = @_;
+ my $folder = param('folder');
+ my ($mbox, $context) = split(/\@/, param('mailbox'));
+ my $passwd = param('password');
+ my $format = param('format');
+
+ my $fields;
+ if (!$context) {
+ $context = param('context');
+ }
+ if (!$context) {
+ $context = "default";
+ }
+
+ my $folders = &folder_list('newfolder', $context, $mbox, $folder);
+ my $mailboxes = &mailbox_list('forwardto', $context, $mbox);
+ if (!$format) {
+ $format = &getcookie('format');
+ }
+ if (!$format) {
+ &message_prefs("play", $msgid);
+ } else {
+ print header(-cookie => &makecookie($format));
+ $fields = &getfields($context, $mbox, $folder, $msgid);
+ if (!$fields) {
+ print "<BR>Bah!\n";
+ return;
+ }
+ my $duration = $fields->{'duration'};
+ if ($duration) {
+ $duration = sprintf "%d:%02d", $duration/60, $duration % 60;
+ } else {
+ $duration = "<i>Unknown</i>";
+ }
+ print <<_EOH;
+
+<TITLE>Asterisk Web-Voicemail: $folder Message $msgid</TITLE>
+<BODY BGCOLOR="white">
+$stdcontainerstart
+<FORM METHOD="post">
+<table width=100% align=center>
+<tr><td align=right colspan=3><font size=+1>$folder Message $msgid</font></td></tr>
+_EOH
+
+ print <<_EOH;
+<tr><td align=center colspan=3>
+<table>
+ <tr><td colspan=2 align=center><font size=+1>$folder <b>$msgid</b></font></td></tr>
+ <tr><td><b>Message:</b></td><td>$msgid</td></tr>\n
+ <tr><td><b>Mailbox:</b></td><td>$mbox\@$context</td></tr>\n
+ <tr><td><b>Folder:</b></td><td>$folder</td></tr>\n
+ <tr><td><b>From:</b></td><td>$fields->{callerid}</td></tr>\n
+ <tr><td><b>Duration:</b></td><td>$duration</td></tr>\n
+ <tr><td><b>Original Date:</b></td><td>$fields->{origdate}</td></tr>\n
+ <tr><td><b>Original Mailbox:</b></td><td>$fields->{origmailbox}</td></tr>\n
+ <tr><td><b>Caller Channel:</b></td><td>$fields->{callerchan}</td></tr>\n
+ <tr><td align=center colspan=2>
+ <input name="action" type=submit value="index">&nbsp;
+ <input name="action" type=submit value="delete ">&nbsp;
+ <input name="action" type=submit value="forward to -> ">&nbsp;
+ $mailboxes&nbsp;
+ <input name="action" type=submit value="save to ->">
+ $folders&nbsp;
+ <input name="action" type=submit value="play ">
+ <input name="action" type=submit value="download">
+</td></tr>
+<tr><td colspan=2 align=center>
+<embed width=400 height=40 src="vmail.cgi?action=audio&folder=$folder&mailbox=$mbox&context=$context&password=$passwd&msgid=$msgid&format=$format&dontcasheme=$$.$format" autostart=yes loop=false></embed>
+</td></tr></table>
+</td></tr>
+</table>
+<input type=hidden name="folder" value="$folder">
+<input type=hidden name="mailbox" value="$mbox">
+<input type=hidden name="context" value="$context">
+<input type=hidden name="password" value="$passwd">
+<input type=hidden name="msgid" value="$msgid">
+$stdcontainerend
+</BODY>\n
+_EOH
+ }
+}
+
+sub message_audio()
+{
+ my ($forcedownload) = @_;
+ my $folder = &untaint(param('folder'));
+ my $msgid = &untaint(param('msgid'));
+ my $mailbox = &untaint(param('mailbox'));
+ my $context = &untaint(param('context'));
+ my $format = param('format');
+ if (!$format) {
+ $format = &getcookie('format');
+ }
+ &untaint($format);
+
+ my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder/msg${msgid}.$format";
+
+ $msgid =~ /^\d\d\d\d$/ || die("Msgid Liar ($msgid)!");
+ grep(/^${format}$/, keys %formats) || die("Format Liar ($format)!");
+
+ # Mailbox and folder are already verified
+ if (open(AUDIO, "<$path")) {
+ $size = -s $path;
+ $|=1;
+ if ($forcedownload) {
+ print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size, -attachment => "msg${msgid}.$format");
+ } else {
+ print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size);
+ }
+
+ while(($amt = sysread(AUDIO, $data, 4096)) > 0) {
+ syswrite(STDOUT, $data, $amt);
+ }
+ close(AUDIO);
+ } else {
+ die("Hrm, can't seem to open $path\n");
+ }
+}
+
+sub message_index()
+{
+ my ($folder, $message) = @_;
+ my ($mbox, $context) = split(/\@/, param('mailbox'));
+ my $passwd = param('password');
+ my $message2;
+ my $msgcount;
+ my $hasmsg;
+ my $newmessages, $oldmessages;
+ my $format = param('format');
+ if (!$format) {
+ $format = &getcookie('format');
+ }
+ if (!$context) {
+ $context = param('context');
+ }
+ if (!$context) {
+ $context = "default";
+ }
+ if ($folder) {
+ $msgcount = &msgcountstr($context, $mbox, $folder);
+ $message2 = "&nbsp;&nbsp;&nbsp;Folder '$folder' has " . &msgcountstr($context, $mbox, $folder);
+ } else {
+ $newmessages = &msgcount($context, $mbox, "INBOX");
+ $oldmessages = &msgcount($context, $mbox, "Old");
+ if (($newmessages > 0) || ($oldmessages < 1)) {
+ $folder = "INBOX";
+ } else {
+ $folder = "Old";
+ }
+ $message2 = "You have";
+ if ($newmessages > 0) {
+ $message2 .= " <b>$newmessages</b> NEW";
+ if ($oldmessages > 0) {
+ $message2 .= "and <b>$oldmessages</b> OLD";
+ if ($oldmessages != 1) {
+ $message2 .= " messages.";
+ } else {
+ $message2 .= "message.";
+ }
+ } else {
+ if ($newmessages != 1) {
+ $message2 .= " messages.";
+ } else {
+ $message2 .= " message.";
+ }
+ }
+ } else {
+ if ($oldmessages > 0) {
+ $message2 .= " <b>$oldmessages</b> OLD";
+ if ($oldmessages != 1) {
+ $message2 .= " messages.";
+ } else {
+ $message2 .= " message.";
+ }
+ } else {
+ $message2 .= " <b>no</b> messages.";
+ }
+ }
+ }
+
+ my $folders = &folder_list('newfolder', $context, $mbox, $folder);
+ my $cfolders = &folder_list('changefolder', $context, $mbox, $folder);
+ my $mailboxes = &mailbox_list('forwardto', $context, $mbox);
+ print header(-cookie => &makecookie($format));
+ print <<_EOH;
+
+<TITLE>Asterisk Web-Voicemail: $mbox\@$context $folder</TITLE>
+<BODY BGCOLOR="white">
+$stdcontainerstart
+<FORM METHOD="post">
+<table width=100% align=center>
+<tr><td align=center colspan=2><font size=+2><I>$message</I></font></td></tr>
+<tr><td align=right colspan=2><font size=+1><b>$folder</b> Messages</font> <input type=submit name="action" value="change to ->">$cfolders</td></tr>
+<tr><td align=left colspan=2><font size=+1>$message2</font></td></tr>
+</table>
+<table width=100% align=center cellpadding=0 cellspacing=0>
+_EOH
+
+print "<tr><td>&nbsp;Msg</td><td>&nbsp;From</td><td>&nbsp;Duration</td><td>&nbsp;Date</td><td>&nbsp;</td></tr>\n";
+print "<tr><td><hr></td><td><hr></td><td><hr></td><td><hr></td><td></td></tr>\n";
+foreach $msg (&messages($context, $mbox, $folder)) {
+
+ $fields = &getfields($context, $mbox, $folder, $msg);
+ $duration = $fields->{'duration'};
+ if ($duration) {
+ $duration = sprintf "%d:%02d", $duration / 60, $duration % 60;
+ } else {
+ $duration = "<i>Unknown</i>";
+ }
+ $hasmsg++;
+ print "<tr><td><input type=checkbox name=\"msgselect\" value=\"$msg\">&nbsp;<b>$msg</b></td><td>$fields->{'callerid'}</td><td>$duration</td><td>$fields->{'origdate'}</td><td><input name='play$msg' alt=\"Play message $msg\" border=0 type=image align=left src=\"$astpath/play.gif\"></td></tr>\n";
+
+}
+if (!$hasmsg) {
+ print "<tr><td colspan=4 align=center><P><b><i>No messages</i></b><P></td></tr>";
+}
+
+print <<_EOH;
+</table>
+<table width=100% align=center>
+<tr><td align=right colspan=2>
+ <input type="submit" name="action" value="refresh">&nbsp;
+_EOH
+
+if ($hasmsg) {
+print <<_EOH;
+ <input type="submit" name="action" value="delete">&nbsp;
+ <input type="submit" name="action" value="save to ->">
+ $folders&nbsp;
+ <input type="submit" name="action" value="forward to ->">
+ $mailboxes
+_EOH
+}
+
+print <<_EOH;
+</td></tr>
+<tr><td align=right colspan=2>
+ <input type="submit" name="action" value="preferences">
+ <input type="submit" name="action" value="logout">
+</td></tr>
+</table>
+<input type=hidden name="folder" value="$folder">
+<input type=hidden name="mailbox" value="$mbox">
+<input type=hidden name="context" value="$context">
+<input type=hidden name="password" value="$passwd">
+</FORM>
+$stdcontainerend
+</BODY>\n
+_EOH
+}
+
+sub validfolder()
+{
+ my ($folder) = @_;
+ return grep(/^$folder$/, @validfolders);
+}
+
+sub folder_list()
+{
+ my ($name, $context, $mbox, $selected) = @_;
+ my $f;
+ my $count;
+ my $tmp = "<SELECT name=\"$name\">\n";
+ foreach $f (@validfolders) {
+ $count = &msgcount($context, $mbox, $f);
+ if ($f eq $selected) {
+ $tmp .= "<OPTION SELECTED>$f ($count)</OPTION>\n";
+ } else {
+ $tmp .= "<OPTION>$f ($count)</OPTION>\n";
+ }
+ }
+ $tmp .= "</SELECT>";
+}
+
+sub message_rename()
+{
+ my ($context, $mbox, $oldfolder, $old, $newfolder, $new) = @_;
+ my $oldfile, $newfile;
+ return if ($old eq $new) && ($oldfolder eq $newfolder);
+
+ if ($context =~ /^(\w+)$/) {
+ $context = $1;
+ } else {
+ die("Invalid Context<BR>\n");
+ }
+
+ if ($mbox =~ /^(\w+)$/) {
+ $mbox = $1;
+ } else {
+ die ("Invalid mailbox<BR>\n");
+ }
+
+ if ($oldfolder =~ /^(\w+)$/) {
+ $oldfolder = $1;
+ } else {
+ die("Invalid old folder<BR>\n");
+ }
+
+ if ($newfolder =~ /^(\w+)$/) {
+ $newfolder = $1;
+ } else {
+ die("Invalid new folder ($newfolder)<BR>\n");
+ }
+
+ if ($old =~ /^(\d\d\d\d)$/) {
+ $old = $1;
+ } else {
+ die("Invalid old Message<BR>\n");
+ }
+
+ if ($new =~ /^(\d\d\d\d)$/) {
+ $new = $1;
+ } else {
+ die("Invalid old Message<BR>\n");
+ }
+
+ my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$newfolder";
+ $path =~ /^(.*)$/;
+ $path = $1;
+ mkdir $path, 0770;
+ my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$oldfolder";
+ opendir(DIR, $path) || die("Unable to open directory\n");
+ my @files = grep /^msg${old}\.\w+$/, readdir(DIR);
+ closedir(DIR);
+ foreach $oldfile (@files) {
+ my $tmp = $oldfile;
+ if ($tmp =~ /^(msg${old}.\w+)$/) {
+ $tmp = $1;
+ $oldfile = $path . "/$tmp";
+ $tmp =~ s/msg${old}/msg${new}/;
+ $newfile = "/var/spool/asterisk/voicemail/$context/$mbox/$newfolder/$tmp";
+# print "Renaming $oldfile to $newfile<BR>\n";
+ rename($oldfile, $newfile);
+ }
+ }
+}
+
+sub file_copy()
+{
+ my ($orig, $new) = @_;
+ my $res;
+ my $data;
+ $orig =~ /^(.*)$/;
+ $orig = $1;
+ $new =~ /^(.*)$/;
+ $new = $1;
+ open(IN, "<$orig") || die("Unable to open '$orig'\n");
+ open(OUT, ">$new") || DIE("Unable to open '$new'\n");
+ while(($res = sysread(IN, $data, 4096)) > 0) {
+ syswrite(OUT, $data, $res);
+ }
+ close(OUT);
+ close(IN);
+}
+
+sub message_copy()
+{
+ my ($context, $mbox, $newmbox, $oldfolder, $old, $new) = @_;
+ my $oldfile, $newfile;
+ return if ($mbox eq $newmbox);
+
+ if ($mbox =~ /^(\w+)$/) {
+ $mbox = $1;
+ } else {
+ die ("Invalid mailbox<BR>\n");
+ }
+
+ if ($newmbox =~ /^(\w+)$/) {
+ $newmbox = $1;
+ } else {
+ die ("Invalid new mailbox<BR>\n");
+ }
+
+ if ($oldfolder =~ /^(\w+)$/) {
+ $oldfolder = $1;
+ } else {
+ die("Invalid old folder<BR>\n");
+ }
+
+ if ($old =~ /^(\d\d\d\d)$/) {
+ $old = $1;
+ } else {
+ die("Invalid old Message<BR>\n");
+ }
+
+ if ($new =~ /^(\d\d\d\d)$/) {
+ $new = $1;
+ } else {
+ die("Invalid old Message<BR>\n");
+ }
+
+ my $path = "/var/spool/asterisk/voicemail/$context/$newmbox";
+ $path =~ /^(.*)$/;
+ $path = $1;
+ mkdir $path, 0770;
+ my $path = "/var/spool/asterisk/voicemail/$context/$newmbox/INBOX";
+ $path =~ /^(.*)$/;
+ $path = $1;
+ mkdir $path, 0770;
+ my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$oldfolder";
+ opendir(DIR, $path) || die("Unable to open directory\n");
+ my @files = grep /^msg${old}\.\w+$/, readdir(DIR);
+ closedir(DIR);
+ foreach $oldfile (@files) {
+ my $tmp = $oldfile;
+ if ($tmp =~ /^(msg${old}.\w+)$/) {
+ $tmp = $1;
+ $oldfile = $path . "/$tmp";
+ $tmp =~ s/msg${old}/msg${new}/;
+ $newfile = "/var/spool/asterisk/voicemail/$context/$newmbox/INBOX/$tmp";
+# print "Copying $oldfile to $newfile<BR>\n";
+ &file_copy($oldfile, $newfile);
+ }
+ }
+}
+
+sub message_delete()
+{
+ my ($context, $mbox, $folder, $msg) = @_;
+ if ($mbox =~ /^(\w+)$/) {
+ $mbox = $1;
+ } else {
+ die ("Invalid mailbox<BR>\n");
+ }
+ if ($context =~ /^(\w+)$/) {
+ $context = $1;
+ } else {
+ die ("Invalid context<BR>\n");
+ }
+ if ($folder =~ /^(\w+)$/) {
+ $folder = $1;
+ } else {
+ die("Invalid folder<BR>\n");
+ }
+ if ($msg =~ /^(\d\d\d\d)$/) {
+ $msg = $1;
+ } else {
+ die("Invalid Message<BR>\n");
+ }
+ my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$folder";
+ opendir(DIR, $path) || die("Unable to open directory\n");
+ my @files = grep /^msg${msg}\.\w+$/, readdir(DIR);
+ closedir(DIR);
+ foreach $oldfile (@files) {
+ if ($oldfile =~ /^(msg${msg}.\w+)$/) {
+ $oldfile = $path . "/$1";
+# print "Deleting $oldfile<BR>\n";
+ unlink($oldfile);
+ }
+ }
+}
+
+sub message_forward()
+{
+ my ($toindex, @msgs) = @_;
+ my $folder = param('folder');
+ my ($mbox, $context) = split(/\@/, param('mailbox'));
+ my $newmbox = param('forwardto');
+ my $msg;
+ my $msgcount;
+ if (!$context) {
+ $context = param('context');
+ }
+ if (!$context) {
+ $context = "default";
+ }
+ $newmbox =~ s/(\w+)(\s+.*)?$/$1/;
+ if (!&validmailbox($context, $newmbox)) {
+ die("Bah! Not a valid mailbox '$newmbox'\n");
+ return "";
+ }
+
+ my $txt;
+ $context = &untaint($context);
+ $newmbox = &untaint($newmbox);
+ my $path = "/var/spool/asterisk/voicemail/$context/$newmbox/INBOX";
+ if (&lock_path($path) == 0) {
+ $msgcount = &msgcount($context, $newmbox, "INBOX");
+
+ if ($newmbox ne $mbox) {
+# print header;
+ foreach $msg (@msgs) {
+# print "Forwarding $msg from $mbox to $newmbox<BR>\n";
+ &message_copy($context, $mbox, $newmbox, $folder, $msg, sprintf "%04d", $msgcount);
+ $msgcount++;
+ }
+ $txt = "Forwarded messages " . join(', ', @msgs) . "to $newmbox";
+ } else {
+ $txt = "Can't forward messages to yourself!\n";
+ }
+ &unlock_path($path);
+ } else {
+ $txt = "Cannot forward messages: Unable to lock path.\n";
+ }
+ if ($toindex) {
+ &message_index($folder, $txt);
+ } else {
+ &message_play($txt, $msgs[0]);
+ }
+}
+
+sub message_delete_or_move()
+{
+ my ($toindex, $del, @msgs) = @_;
+ my $txt;
+ my $path;
+ my $y, $x;
+ my $folder = param('folder');
+ my $newfolder = param('newfolder') unless $del;
+ $newfolder =~ s/^(\w+)\s+.*$/$1/;
+ my ($mbox, $context) = split(/\@/, param('mailbox'));
+ if (!$context) {
+ $context = param('context');
+ }
+ if (!$context) {
+ $context = "default";
+ }
+ my $passwd = param('password');
+ $context = &untaint($context);
+ $mbox = &untaint($mbox);
+ $folder = &untaint($folder);
+ my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$folder";
+ if (&lock_path($path) == 0) {
+ my $msgcount = &msgcount($context, $mbox, $folder);
+ my $omsgcount = &msgcount($context, $mbox, $newfolder) if $newfolder;
+ # print header;
+ if ($newfolder ne $folder) {
+ $y = 0;
+ for ($x=0;$x<$msgcount;$x++) {
+ my $msg = sprintf "%04d", $x;
+ my $newmsg = sprintf "%04d", $y;
+ if (grep(/^$msg$/, @msgs)) {
+ if ($newfolder) {
+ &message_rename($context, $mbox, $folder, $msg, $newfolder, sprintf "%04d", $omsgcount);
+ $omsgcount++;
+ } else {
+ &message_delete($context, $mbox, $folder, $msg);
+ }
+ } else {
+ &message_rename($context, $mbox, $folder, $msg, $folder, $newmsg);
+ $y++;
+ }
+ }
+ if ($del) {
+ $txt = "Deleted messages " . join (', ', @msgs);
+ } else {
+ $txt = "Moved messages " . join (', ', @msgs) . " to $newfolder";
+ }
+ } else {
+ $txt = "Can't move a message to the same folder they're in already";
+ }
+ &unlock_path($path);
+ } else {
+ $txt = "Cannot move/delete messages: Unable to lock path.\n";
+ }
+ # Not as many messages now
+ $msgcount--;
+ if ($toindex || ($msgs[0] >= $msgcount)) {
+ &message_index($folder, $txt);
+ } else {
+ &message_play($txt, $msgs[0]);
+ }
+}
+
+if (param()) {
+ my $folder = param('folder');
+ my $changefolder = param('changefolder');
+ $changefolder =~ s/(\w+)\s+.*$/$1/;
+
+ my $newfolder = param('newfolder');
+ $newfolder =~ s/^(\w+)\s+.*$/$1/;
+ if ($newfolder && !&validfolder($newfolder)) {
+ print header;
+ die("Bah! new folder '$newfolder' isn't a folder.");
+ }
+ $action = param('action');
+ $msgid = param('msgid');
+ if (!$action) {
+ my ($tmp) = grep /^play\d\d\d\d\.x$/, param;
+ if ($tmp =~ /^play(\d\d\d\d)/) {
+ $msgid = $1;
+ $action = "play";
+ } else {
+ print header;
+ print "No message?<BR>\n";
+ return;
+ }
+ }
+ @msgs = param('msgselect');
+ @msgs = ($msgid) unless @msgs;
+ {
+ ($mailbox) = &check_login();
+ if (length($mailbox)) {
+ if ($action eq 'login') {
+ &message_index($folder, "Welcome, $mailbox");
+ } elsif (($action eq 'refresh') || ($action eq 'index')) {
+ &message_index($folder, "Welcome, $mailbox");
+ } elsif ($action eq 'change to ->') {
+ if (&validfolder($changefolder)) {
+ $folder = $changefolder;
+ &message_index($folder, "Welcome, $mailbox");
+ } else {
+ die("Bah! Not a valid change to folder '$changefolder'\n");
+ }
+ } elsif ($action eq 'play') {
+ &message_play("$mailbox $folder $msgid", $msgid);
+ } elsif ($action eq 'preferences') {
+ &message_prefs("refresh", $msgid);
+ } elsif ($action eq 'download') {
+ &message_audio(1);
+ } elsif ($action eq 'play ') {
+ &message_audio(0);
+ } elsif ($action eq 'audio') {
+ &message_audio(0);
+ } elsif ($action eq 'delete') {
+ &message_delete_or_move(1, 1, @msgs);
+ } elsif ($action eq 'delete ') {
+ &message_delete_or_move(0, 1, @msgs);
+ } elsif ($action eq 'forward to ->') {
+ &message_forward(1, @msgs);
+ } elsif ($action eq 'forward to -> ') {
+ &message_forward(0, @msgs);
+ } elsif ($action eq 'save to ->') {
+ &message_delete_or_move(1, 0, @msgs);
+ } elsif ($action eq 'save to -> ') {
+ &message_delete_or_move(0, 0, @msgs);
+ } elsif ($action eq 'logout') {
+ &login_screen("Logged out!\n");
+ }
+ } else {
+ sleep(1);
+ &login_screen("Login Incorrect!\n");
+ }
+ }
+} else {
+ &login_screen("\&nbsp;");
+}
diff --git a/trunk/contrib/scripts/vmdb.sql b/trunk/contrib/scripts/vmdb.sql
new file mode 100644
index 000000000..0b1fc38f1
--- /dev/null
+++ b/trunk/contrib/scripts/vmdb.sql
@@ -0,0 +1,66 @@
+DROP TABLE IF EXISTS voicemail;
+CREATE TABLE voicemail (
+ -- All of these column names are very specific, including "uniqueid". Do not change them if you wish voicemail to work.
+ uniqueid INT(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ -- Mailbox context.
+ context CHAR(80) NOT NULL DEFAULT 'default',
+ -- Mailbox number. Should be numeric.
+ mailbox CHAR(80) NOT NULL,
+ -- Must be numeric. Negative if you don't want it to be changed from VoicemailMain
+ password CHAR(80) NOT NULL,
+ -- Used in email and for Directory app
+ fullname CHAR(80),
+ -- Email address (will get sound file if attach=yes)
+ email CHAR(80),
+ -- Email address (won't get sound file)
+ pager CHAR(80),
+ -- Attach sound file to email - YES/no
+ attach CHAR(3),
+ -- Which sound format to attach
+ attachfmt CHAR(10),
+ -- Send email from this address
+ serveremail CHAR(80),
+ -- Prompts in alternative language
+ language CHAR(20),
+ -- Alternative timezone, as defined in voicemail.conf
+ tz CHAR(30),
+ -- Delete voicemail from server after sending email notification - yes/NO
+ deletevoicemail CHAR(3),
+ -- Read back CallerID information during playback - yes/NO
+ saycid CHAR(3),
+ -- Allow user to send voicemail from within VoicemailMain - YES/no
+ sendvoicemail CHAR(3),
+ -- Listen to voicemail and approve before sending - yes/NO
+ review CHAR(3),
+ -- Warn user a temporary greeting exists - yes/NO
+ tempgreetwarn CHAR(3),
+ -- Allow '0' to jump out during greeting - yes/NO
+ operator CHAR(3),
+ -- Hear date/time of message within VoicemailMain - YES/no
+ envelope CHAR(3),
+ -- Hear length of message within VoicemailMain - yes/NO
+ sayduration CHAR(3),
+ -- Minimum duration in minutes to say
+ saydurationm INT(3),
+ -- Force new user to record name when entering voicemail - yes/NO
+ forcename CHAR(3),
+ -- Force new user to record greetings when entering voicemail - yes/NO
+ forcegreetings CHAR(3),
+ -- Context in which to dial extension for callback
+ callback CHAR(80),
+ -- Context in which to dial extension (from advanced menu)
+ dialout CHAR(80),
+ -- Context in which to execute 0 or * escape during greeting
+ exitcontext CHAR(80),
+ -- Maximum length of message (in seconds)
+ maxmessage INT(5),
+ -- Maximum messages in a folder (100 if not specified)
+ maxmsg INT(5),
+ -- Increase DB gain on recorded message by this amount (0.0 means none)
+ volgain DECIMAL(5,2),
+ -- IMAP user for authentication (if using IMAP storage)
+ imapuser VARCHAR(80),
+ -- IMAP password for authentication (if using IMAP storage)
+ imappassword VARCHAR(80),
+ stamp timestamp
+);
diff --git a/trunk/contrib/thirdparty/spexxilbcfix_xlite.reg b/trunk/contrib/thirdparty/spexxilbcfix_xlite.reg
new file mode 100644
index 000000000..821fd5e2b
--- /dev/null
+++ b/trunk/contrib/thirdparty/spexxilbcfix_xlite.reg
Binary files differ
diff --git a/trunk/contrib/thirdparty/spexxilbcfix_xpro.reg b/trunk/contrib/thirdparty/spexxilbcfix_xpro.reg
new file mode 100644
index 000000000..472dcb44f
--- /dev/null
+++ b/trunk/contrib/thirdparty/spexxilbcfix_xpro.reg
Binary files differ
diff --git a/trunk/contrib/utils/README.rawplayer b/trunk/contrib/utils/README.rawplayer
new file mode 100644
index 000000000..146898a5c
--- /dev/null
+++ b/trunk/contrib/utils/README.rawplayer
@@ -0,0 +1,37 @@
+rawplayer is a simple C applet to stream raw music files in place of mpg123
+
+INSTALL
+
+compile the .c file and install:
+gcc -O2 rawplayer.c -o /usr/bin/rawplayer
+
+
+
+Converting MP3 to RAW
+
+Make track01.mp3 into track01.raw with sox (if compiled with mp3 support).
+sox -c 1 track01.mp3 -t raw -r 8000 -c 1 -s -w track01.raw
+
+Otherwise, use whatever app to turn track01.mp3 into track01.wav then use sox on the wav.
+sox -c 1 track01.wav -t raw -r 8000 -c 1 -s -w track01.raw
+
+
+Once you have the raw files put them in any dir on your system (eg /var/lib/asterisk/holdmusic_raw).
+and set up a class in musiconhold.conf like so:
+
+[classes]
+default => custom:/var/lib/asterisk/holdmusic_raw,/usr/bin/rawplayer
+
+
+This is the most efficient way to implement moh because no cpu usage is required to
+explode the very compressed mp3 data then downsample the music to the 8khz mono on the fly
+instead the data is already stored on the disk in the format that asterisk needs it to be
+and the player does little more than pick up frames from the file and hand them to right
+to the asterisk pipe where the audio is shared into all the channels who require it.
+
+
+If you have cpu to spare and want a simple mp3 solution consider the format_mp3 from
+asterisk-addons and the files based moh.
+
+
+
diff --git a/trunk/contrib/utils/eagi_proxy.c b/trunk/contrib/utils/eagi_proxy.c
new file mode 100644
index 000000000..03c7e0640
--- /dev/null
+++ b/trunk/contrib/utils/eagi_proxy.c
@@ -0,0 +1,419 @@
+/*
+ * Asterisk EAGI -> TCP/IP proxy
+ * by Danijel Korzinek (devil_slayer _at_ hotmail.com)
+ *
+ * This simple C application allows you to control asterisk thru one TCP/IP
+ * socket and listen to the conversation thru another socket.
+ *
+ * Great for ASR or wizzard-of-oz telephony systems!
+ *
+ * HOWTO:
+ * The program is compiled using the following command:
+ * gcc eagi_proxy.c -o eagi_proxy -lpthread
+ *
+ * In the dialplan, you can add something like this to the main context:
+ * exten => s,1,Answer
+ * exten => s,n,EAGI(/path/to/eagi_proxy)
+ * exten => s,n,Hangup
+ *
+ * To test the program you can use the netcat utility:
+ * (http://netcat.sourceforge.net/)
+ *
+ * -in one console run:
+ * nc -vv -l -p 8418 > /path/to/file.raw
+ * -in another run:
+ * nc -vv -l -p 8417
+ * -you can use any file for the signal or even /dev/null
+ * -you can change the port numbers in the sourcecode below
+ *
+ * Once you make the call, both programs will accept the incoming
+ * connection. The program on port 8417 will print out the enviornemnt
+ * (unless the appropriate define below is commented) and you can write
+ * any AGI command there (http://www.voip-info.org/wiki-Asterisk+AGI),
+ * e.g.:
+ * GET DATA /path/to/gsm/file 10000 4
+ *
+ * Finally, you can open the RAW file in any sound editor. The format is:
+ * RAW little-endian 8kHz 16bit
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <pthread.h>
+
+/* DEFINES */
+#define SIGNAL_PORT 8418
+#define COMMAND_PORT 8417
+#define SEND_ENVIORNMENT /*send the enviornment thru the socket*/
+/************************/
+
+
+#define BUFSIZE 1024
+char buf[BUFSIZE];
+
+#define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */
+char window[WINSIZE];
+
+#define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */
+char* winbuf;
+char *end, *bs, *be;
+/* winbuf - start of buffer
+ * end - end of buffer
+ * bs - start of data
+ * be - end of data
+ */
+
+int command_desc; /* command transfer descriptor */
+int speech_desc; /* speech signal descrriptor */
+char connected=1; /* connection state */
+
+int connect_to_host(char* host, int port); /* connect to a given host (name or IP) and given port number in nonblocking mode returning socket descriptor*/
+
+void read_full(int file, char* buffer, int num); /* read EXACTLY "num" ammount of bytes from "file" descriptor to "buffer"*/
+int read_some(int file, char* buffer, int size); /* read AT MOST "size" ammount of bytes */
+
+void write_buf(int file, char* buffer, int num); /* write "num" ammount of bytes to "file" descriptor and buffer the surplus if the write would block */
+int write_amap(int file, char* buffer, int num); /*write AT MOST "num" ammount of bytes and return ammount that was written*/
+
+void setnonblocking(int desc); /*sets the socket non-blocking; for polling */
+
+void finalize(); /* this function is run at exit */
+
+pthread_mutex_t command_mutex;/* command socket mutex */
+pthread_t stdin_thread,signal_thread;
+void* readStdin(void* ptr);
+void* readSignal(void* ptr);
+
+/* The program creates 3 threads:
+ * 1) Main thread - reads commands from the socket and sends them to asterisk
+ * 2) stdin_thread - reads asterisk output and sends it to the command socket
+ * 3) signal_thread - reads the sound from asterisk and sends it to the signal socket
+ */
+
+int main()
+{
+ int ret;
+
+ atexit(finalize);
+
+ setlinebuf(stdin);
+ setlinebuf(stdout);
+
+ winbuf=(char*)malloc(WINSIZE*WINBUF_NUM);
+ end=winbuf+WINSIZE*WINBUF_NUM;
+ bs=be=winbuf;
+
+ speech_desc=connect_to_host("localhost",SIGNAL_PORT);
+ if(speech_desc<0)
+ {
+ perror("signal socket");
+ return -1;
+ }
+
+
+ command_desc=connect_to_host("localhost",COMMAND_PORT);
+ if(command_desc<0)
+ {
+ perror("command socket");
+ return -1;
+ }
+
+ pthread_mutex_init(&command_mutex,NULL);
+ pthread_create(&stdin_thread,NULL,readStdin,NULL);
+ pthread_create(&signal_thread,NULL,readSignal,NULL);
+
+ while(connected)
+ {
+ pthread_mutex_lock(&command_mutex);
+ ret=read_some(command_desc,buf,BUFSIZE);
+ pthread_mutex_unlock(&command_mutex);
+ if(ret>0)
+ {
+ buf[ret]=0;
+ printf("%s",buf);
+ }
+ }
+
+ return 0;
+}
+
+void finalize()
+{
+ close(command_desc);
+ close(speech_desc);
+ free(winbuf);
+}
+
+void* readStdin(void* ptr)
+{
+ while(1)/*read enviornment*/
+ {
+ fgets(buf,BUFSIZE,stdin);
+ #ifdef SEND_ENVIORNMENT
+ pthread_mutex_lock(&command_mutex);
+ write_buf(command_desc,buf,strlen(buf));
+ pthread_mutex_unlock(&command_mutex);
+ #endif
+ if(feof(stdin) || buf[0]=='\n')
+ {
+ break;
+ }
+ }
+
+ while(connected)
+ {
+ fgets(buf,BUFSIZE,stdin);
+ pthread_mutex_lock(&command_mutex);
+ write_buf(command_desc,buf,strlen(buf));
+ pthread_mutex_unlock(&command_mutex);
+ }
+
+ pthread_exit(NULL);
+}
+
+void* readSignal(void* ptr)
+{
+ while(connected)
+ {
+ read_full(3,window,WINSIZE);
+ write_buf(speech_desc,window,WINSIZE);
+ }
+
+ pthread_exit(NULL);
+}
+
+
+void read_full(int file, char* buffer, int num)
+{
+ int count,pos=0;
+
+ while(num)
+ {
+ count=read(file,buffer+pos,num);
+ if(count==0 || (count<0 && errno!=EAGAIN))
+ {
+ connected=0;
+ return;
+ }
+ num-=count;
+ pos+=count;
+ }
+}
+
+int connect_to_host(char* name, int port)
+{
+ int address;
+ struct hostent* host_entity;
+ int res,desc;
+ int opts;
+ struct sockaddr_in host;
+
+
+ /* get adress */
+ if(!strcmp(name,"localhost"))
+ address=htonl(2130706433); /*127.0.0.1*/
+ else
+ {
+ address=inet_addr(name); /* check if it's an IP that's written in the string */
+ if(address==(in_addr_t)-1)
+ {
+ host_entity = gethostbyname(name); /* search for the host under this name */
+
+ if(!host_entity)
+ {
+ fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/
+ return -1;
+ }
+ address=*((int*)host_entity->h_addr);
+ }
+ }
+
+ desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
+ if(desc<0)
+ {
+ fprintf(stderr,"EAGI proxy: Cannot create socket!\n");
+ return -1;
+ }
+
+ memset((void*)&host,0,sizeof(struct sockaddr_in));
+
+ host.sin_family=AF_INET;
+ host.sin_port=htons(port);
+ host.sin_addr.s_addr=address;
+
+ res=connect(desc,(struct sockaddr*)&host,sizeof(host));
+ if(res<0)
+ {
+ fprintf(stderr,"EAGI proxy: Cannot connect!\n");
+ return -1;
+ }
+
+ /* set to non-blocking mode */
+ opts = fcntl(desc,F_GETFL);
+ if (opts < 0) {
+ perror("fcntl(F_GETFL)");
+ exit(EXIT_FAILURE);
+ }
+ opts = (opts | O_NONBLOCK);
+ if (fcntl(desc,F_SETFL,opts) < 0) {
+ perror("fcntl(F_SETFL)");
+ exit(EXIT_FAILURE);
+ }
+
+
+ return desc;
+}
+
+int read_some(int desc, char* buffer, int size)
+{
+ unsigned char c;
+ int res,i=0;
+
+ for(;;)
+ {
+ res=read(desc,&c,1);
+ if(res<1)
+ {
+ if(errno!=EAGAIN)
+ {
+ perror("Error reading");
+ connected=0;
+ }
+ break;
+ }
+ if(res==0)
+ {
+ connected=0;
+ break;
+ }
+
+ buffer[i]=c;
+ i++;
+ }
+
+ return i;
+}
+
+/* This is a tricky function! */
+void write_buf(int desc, char* buf, int size)
+{
+ int ret;
+
+ /*NOTE: AMAP -> as much as possible */
+
+ if(be!=bs)/* if data left in buffer */
+ {
+ if(be>bs)/* if buffer not split */
+ {
+ ret=write_amap(desc,bs,be-bs);/* write AMAP */
+ bs+=ret;/* shift the start of the buffer */
+ }
+ else/* if buffer is split */
+ {
+ ret=write_amap(desc,bs,end-bs);/* write higher part first */
+ if(ret==end-bs)/* if wrote whole of the higher part */
+ {
+ ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */
+ bs=winbuf+ret;/* shift start to new position */
+ }
+ else bs+=ret;/* if not wrote whole of higher part, only shift start */
+ }
+ }
+
+ if(be==bs)/* if buffer is empty now */
+ {
+ ret=write_amap(desc,buf,size);/* write AMAP of the new data */
+ buf+=ret;/* shift start of new data */
+ size-=ret;/* lower size of new data */
+ }
+
+ if(size)/* if new data still remains unsent */
+ {
+ if(be>=bs)/* if data not split */
+ {
+ if(size>end-be)/* if new data size doesn't fit higher end */
+ {
+ size-=end-be;/* reduce new data size by the higher end size */
+ memcpy(be,buf,end-be);/* copy to higher end */
+ be=winbuf;/* shift end to begining of buffer */
+ buf+=end-be;/* shift start of new data */
+ }
+ else/* if new data fits the higher end */
+ {
+ memcpy(be,buf,size);/* copy to higher end */
+ be+=size;/* shift end by size */
+ if(be>=end)/* if end goes beyond the buffer */
+ be=winbuf;/* restart */
+ size=0;/* everything copied */
+ }
+ }
+
+ if(size)/* if new data still remains */
+ {
+ if(size>=bs-be)/* if new data doesn't fit between end and start */
+ {
+ fprintf(stderr,"Buffer overflow!\n");
+ size=bs-be-1;/* reduce the size that we can copy */
+ }
+
+ if(size)/* if we can copy anything */
+ {
+ memcpy(be,buf,size);/* copy the new data between end and start */
+ be+=size;/* shift end by size */
+ }
+ }
+ }
+}
+
+int write_amap(int desc, char* buf, int size)
+{
+ int ret;
+ ret=write(desc,buf,size);
+ if(ret<0)
+ {
+ if(errno!=EAGAIN)
+ {
+ perror("Error writing");
+ connected=0;
+ }
+ return 0;
+ }
+ if(ret==0)
+ connected=0;
+
+ return ret;
+}
+
+
+void setnonblocking(int desc)
+{
+ int opts;
+
+ opts = fcntl(desc,F_GETFL);
+ if(opts < 0)
+ {
+ perror("fcntl(F_GETFL)");
+ exit(-1);
+ }
+
+ opts = (opts | O_NONBLOCK );
+ if(fcntl(desc,F_SETFL,opts) < 0)
+ {
+ perror("fcntl(F_SETFL)");
+ exit(-1);
+ }
+}
diff --git a/trunk/contrib/utils/rawplayer.c b/trunk/contrib/utils/rawplayer.c
new file mode 100644
index 000000000..2733264a0
--- /dev/null
+++ b/trunk/contrib/utils/rawplayer.c
@@ -0,0 +1,46 @@
+/*
+ Rawplayer.c simple raw file stdout player
+ (c) Anthony C Minessale II <anthmct@yahoo.com>
+
+ 2006-03-10: Bruno Rocha <bruno@3gnt.net>
+ - include <stdlib.h> to remove compiler warning on some platforms
+ - check for read/write errors (avoid 100% CPU usage in some asterisk failures)
+*/
+
+#define BUFLEN 320
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+static int deliver_file(char *path, int fdout) {
+ int fd = 0, bytes = 0, error = 0;
+ short buf[BUFLEN];
+
+ if ((fd = open(path,O_RDONLY))) {
+ while ((bytes=read(fd, buf, BUFLEN)) > 0) {
+ if(write(fdout, buf, bytes) < 0){
+ error = -2;
+ break;
+ }
+ }
+ if(fd)
+ close(fd);
+ } else
+ return -1;
+
+ return error;
+}
+
+
+int main(int argc, char *argv[]) {
+ int x = 0, fdout = 0;
+ fdout = fileno(stdout);
+ for (;;)
+ for (x = 1; x < argc ; x++) {
+ if(deliver_file(argv[x], fdout))
+ exit(1);
+ }
+}
+
diff --git a/trunk/contrib/utils/zones2indications.c b/trunk/contrib/utils/zones2indications.c
new file mode 100644
index 000000000..645cd0ed5
--- /dev/null
+++ b/trunk/contrib/utils/zones2indications.c
@@ -0,0 +1,153 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Tzafrir Cohen <tzafrir.cohen@xorcom.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 print libtonozone data as Asterisk indications.conf
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zaptel/tonezone.h>
+#include <unistd.h>
+
+#define PROGRAM "zones2indication"
+
+void print_tone_zone_sound(struct ind_tone_zone *zone_data, const char* name,
+ int toneid) {
+ int i;
+ for (i=0; i<ZT_TONE_MAX; i++) {
+ if (zone_data->tones[i].toneid == toneid){
+ printf("%s = %s\n", name, zone_data->tones[i].data);
+ break;
+ }
+ }
+}
+
+void print_indications(struct ind_tone_zone *zone_data) {
+ int i;
+
+ printf (
+ "[%s]\n"
+ "; Source: libtonezone.\n"
+ "description = %s\n"
+ "\n",
+ zone_data->country, zone_data->description
+ );
+
+ printf(
+ "ringcadence = "
+ );
+ for(i=0; ; i++) {
+ if (zone_data->ringcadence[i] == 0)
+ break;
+ if (i != 0)
+ putchar(',');
+ printf("%d",zone_data->ringcadence[i]);
+ }
+ putchar('\n');
+
+ print_tone_zone_sound(zone_data, "dial", ZT_TONE_DIALTONE);
+ print_tone_zone_sound(zone_data, "busy", ZT_TONE_BUSY);
+ print_tone_zone_sound(zone_data, "ring", ZT_TONE_RINGTONE);
+ print_tone_zone_sound(zone_data, "congestion", ZT_TONE_CONGESTION);
+ print_tone_zone_sound(zone_data, "callwaiting", ZT_TONE_CALLWAIT);
+ print_tone_zone_sound(zone_data, "dialrecall", ZT_TONE_DIALRECALL);
+ print_tone_zone_sound(zone_data, "record", ZT_TONE_RECORDTONE);
+ print_tone_zone_sound(zone_data, "info", ZT_TONE_INFO);
+ print_tone_zone_sound(zone_data, "stutter", ZT_TONE_STUTTER);
+ printf("\n\n");
+}
+
+int print_zone_by_id(int zone_num) {
+ struct tone_zone *zone_data = tone_zone_find_by_num(zone_num);
+
+ if (zone_data == NULL)
+ return 1;
+
+ print_indications(zone_data);
+
+ return 0;
+}
+
+int print_zone_by_country(char* country) {
+ struct tone_zone *zone_data = tone_zone_find(country);
+
+ if (zone_data == NULL)
+ return 1;
+
+ print_indications(zone_data);
+
+ return 0;
+}
+
+int print_all() {
+ int i;
+ /* loop over all possible zones */
+ for (i=0; ; i++) {
+ if (print_zone_by_id(i))
+ break;
+ }
+ return 0;
+}
+
+void usage() {
+ fprintf(stderr,
+ PROGRAM ": print libtonozone data as Asterisk indications.conf\n"
+ "\n"
+ "Usage:\n"
+ " " PROGRAM " -a Print all countries\n"
+ " " PROGRAM " -c <code> Select country by two-letter country code\n"
+ " " PROGRAM " -n <num> Select country by its internal libtonezone number\n"
+ " " PROGRAM " -h Print this text.\n"
+ );
+}
+
+int main(int argc, char* argv[]){
+ int country_code = -1;
+ int opt_print_all = 0;
+ int opt;
+ char* endptr = NULL;
+
+ while((opt = getopt(argc, argv, "ac:hn:")) != -1) {
+ switch(opt) {
+ case 'a':
+ return print_all();
+ case 'c':
+ return print_zone_by_country(optarg);
+ case 'h':
+ usage();
+ return 0;
+ case 'n':
+ printf("number is %s.\n", optarg);
+ country_code = strtol(optarg, &endptr, 10);
+ return print_zone_by_id(country_code);
+ /* FIXME: what if this is not a number?
+ if (endptr != NULL) {
+ fprintf(stderr, "Error: Invalid country code %s, %d.\n",optarg, country_code);
+ usage();
+ exit(1);
+ }
+ */
+ break;
+ }
+ }
+
+ /* If we got here, the user selected no option */
+ usage();
+ return 2;
+}
diff --git a/trunk/contrib/valgrind-RedHat-8.0.supp b/trunk/contrib/valgrind-RedHat-8.0.supp
new file mode 100644
index 000000000..a404d43fa
--- /dev/null
+++ b/trunk/contrib/valgrind-RedHat-8.0.supp
@@ -0,0 +1,41 @@
+#This valgrind suppresion file is supposed to be working with
+#Red Hat Linux release 8.0 (Psyche)
+#You can use it by calling valgrind this way:
+#cd /usr/src/asterisk
+#valgrind --gdb-attach=yes --suppressions=valgrind-RedHat-8.0.supp asterisk -vvv
+
+{
+ library_1
+ PThread
+ fun:pthread_error
+ fun:__pthread_mutex_destroy
+ obj:/lib/i686/libc-2.2.93.so
+}
+
+{
+ library 2
+ Cond
+ fun:elf_dynamic_do_rel.7
+ fun:_dl_relocate_object_internal
+ obj:/lib/i686/libc-2.2.93.so
+ fun:_dl_catch_error_internal
+}
+
+#==21922== Thread 16:
+#==21922== Syscall param ioctl(generic) contains uninitialised or
+#unaddressable byte(s)
+#==21922== at 0x420D3454: (within /lib/i686/libc-2.2.93.so)
+#==21922== by 0x8058D45: ast_call (channel.c:1356)
+#==21922== by 0x463027A7: ??? (app_dial.c:472)
+#==21922== by 0x805E2AE: pbx_exec (pbx.c:318)
+#==21922== Address 0x0 is not stack'd, malloc'd or free'd
+
+{
+ ioctl(........,NULL);
+ Param
+ ioctl(generic)
+ obj:/lib/i686/libc-2.2.93.so
+ fun:ast_call
+ fun:
+ fun:pbx_exec
+}
diff --git a/trunk/doc/CODING-GUIDELINES b/trunk/doc/CODING-GUIDELINES
new file mode 100644
index 000000000..1b53402ac
--- /dev/null
+++ b/trunk/doc/CODING-GUIDELINES
@@ -0,0 +1,684 @@
+ --------------------------------------
+ == Asterisk Coding Guidelines ==
+ --------------------------------------
+
+This document gives some basic indication on how the asterisk code
+is structured. The first part covers the structure and style of
+individual files. The second part (TO BE COMPLETED) covers the
+overall code structure and the build architecture.
+
+Please read it to the end to understand in detail how the asterisk
+code is organized, and to know how to extend asterisk or contribute
+new code.
+
+We are looking forward to your contributions to Asterisk - the
+Open Source PBX! As Asterisk is a large and in some parts very
+time-sensitive application, the code base needs to conform to
+a common set of coding rules so that many developers can enhance
+and maintain the code. Code also needs to be reviewed and tested
+so that it works and follows the general architecture and guide-
+lines, and is well documented.
+
+Asterisk is published under a dual-licensing scheme by Digium.
+To be accepted into the codebase, all non-trivial changes must be
+disclaimed to Digium or placed in the public domain. For more information
+see http://bugs.digium.com
+
+Patches should be in the form of a unified (-u) diff, made from a checkout
+from subversion.
+
+/usr/src/asterisk$ svn diff > mypatch
+
+If you would like to only include changes to certain files in the patch, you
+can list them in the "svn diff" command:
+
+/usr/src/asterisk$ svn diff somefile.c someotherfile.c > mypatch
+
+ -----------------------------------
+ == PART ONE: CODING GUIDELINES ==
+ -----------------------------------
+
+* General rules
+---------------
+
+- All code, filenames, function names and comments must be in ENGLISH.
+
+- Don't annotate your changes with comments like "/* JMG 4/20/04 */";
+ Comments should explain what the code does, not when something was changed
+ or who changed it. If you have done a larger contribution, make sure
+ that you are added to the CREDITS file.
+
+- Don't make unnecessary whitespace changes throughout the code.
+ If you make changes, submit them to the tracker as separate patches
+ that only include whitespace and formatting changes.
+
+- Don't use C++ type (//) comments.
+
+- Try to match the existing formatting of the file you are working on.
+
+- Use spaces instead of tabs when aligning in-line comments or #defines (this makes
+ your comments aligned even if the code is viewed with another tabsize)
+
+* File structure and header inclusion
+-------------------------------------
+
+Every C source file should start with a proper copyright
+and a brief description of the content of the file.
+Following that, you should immediately put the following lines:
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+"asterisk.h" resolves OS and compiler dependencies for the basic
+set of unix functions (data types, system calls, basic I/O
+libraries) and the basic Asterisk APIs.
+ASTERISK_FILE_VERSION() stores in the executable information
+about the file.
+
+Next, you should #include extra headers according to the functionality
+that your file uses or implements. For each group of functions that
+you use there is a common header, which covers OS header dependencies
+and defines the 'external' API of those functions (the equivalent
+of 'public' members of a class). As an example:
+
+ asterisk/module.h
+ if you are implementing a module, this should be included in one
+ of the files that are linked with the module.
+
+ asterisk/fileio.h
+ access to extra file I/O functions (stat, fstat, playing with
+ directories etc)
+
+ asterisk/network.h
+ basic network I/O - all of the socket library, select/poll,
+ and asterisk-specific (usually either thread-safe or reentrant
+ or both) functions to play with socket addresses.
+
+ asterisk/app.h
+ parsing of application arguments
+
+ asterisk/channel.h
+ struct ast_channel and functions to manipulate it
+
+For more information look at the headers in include/asterisk/ .
+These files are usually self-sufficient, i.e. they recursively #include
+all the extra headers they need.
+
+The equivalent of 'private' members of a class are either directly in
+the C source file, or in files named asterisk/mod_*.h to make it clear
+that they are not for inclusion by generic code.
+
+Keep the number of header files small by not including them unnecessarily.
+Don't cut&paste list of header files from other sources, but only include
+those you really need. Apart from obvious cases (e.g. module.h which
+is almost always necessary) write a short comment next to each #include to
+explain why you need it.
+
+
+* Declaration of functions and variables
+----------------------------------------
+
+- Do not declare variables mid-block (e.g. like recent GNU compilers support)
+ since it is harder to read and not portable to GCC 2.95 and others.
+
+- Functions and variables that are not intended to be used outside the module
+ must be declared static.
+
+- When reading integer numeric input with scanf (or variants), do _NOT_ use '%i'
+ unless you specifically want to allow non-base-10 input; '%d' is always a better
+ choice, since it will not silently turn numbers with leading zeros into base-8.
+
+- Strings that are coming from input should not be used as a first argument to
+ a formatted *printf function.
+
+* Use the internal API
+----------------------
+
+- Make sure you are aware of the string and data handling functions that exist
+ within Asterisk to enhance portability and in some cases to produce more
+ secure and thread-safe code. Check utils.c/utils.h for these.
+
+- If you need to create a detached thread, use the ast_pthread_create_detached()
+ normally or ast_pthread_create_detached_background() for a thread with a smaller
+ stack size. This reduces the replication of the code to handle the pthread_attr_t
+ structure.
+
+* Code formatting
+-----------------
+
+Roughly, Asterisk code formatting guidelines are generally equivalent to the
+following:
+
+# indent -i4 -ts4 -br -brs -cdw -lp -ce -nbfda -npcs -nprs -npsl -nbbo -saf -sai -saw -cs -l90 foo.c
+
+this means in verbose:
+ -i4: indent level 4
+ -ts4: tab size 4
+ -br: braces on if line
+ -brs: braces on struct decl line
+ -cdw: cuddle do while
+ -lp: line up continuation below parenthesis
+ -ce: cuddle else
+ -nbfda: dont break function decl args
+ -npcs: no space after function call names
+ -nprs: no space after parentheses
+ -npsl: dont break procedure type
+ -saf: space after for
+ -sai: space after if
+ -saw: space after while
+ -cs: space after cast
+ -ln90: line length 90 columns
+
+Function calls and arguments should be spaced in a consistent way across
+the codebase.
+ GOOD: foo(arg1, arg2);
+ GOOD: foo(arg1,arg2); /* Acceptable but not preferred */
+ BAD: foo (arg1, arg2);
+ BAD: foo( arg1, arg2 );
+ BAD: foo(arg1, arg2,arg3);
+
+Don't treat keywords (if, while, do, return) as if they were functions;
+leave space between the keyword and the expression used (if any). For 'return',
+don't even put parentheses around the expression, since they are not
+required.
+
+There is no shortage of whitespace characters :-) Use them when they make
+the code easier to read. For example:
+
+ for (str=foo;str;str=str->next)
+
+is harder to read than
+
+ for (str = foo; str; str = str->next)
+
+Following are examples of how code should be formatted.
+
+- Functions:
+int foo(int a, char *s)
+{
+ return 0;
+}
+
+- If statements:
+if (foo) {
+ bar();
+} else {
+ blah();
+}
+
+- Case statements:
+switch (foo) {
+case BAR:
+ blah();
+ break;
+case OTHER:
+ other();
+ break;
+}
+
+- No nested statements without braces, e.g.:
+
+for (x = 0; x < 5; x++)
+ if (foo)
+ if (bar)
+ baz();
+
+instead do:
+for (x = 0; x < 5; x++) {
+ if (foo) {
+ if (bar)
+ baz();
+ }
+}
+
+- Don't build code like this:
+
+if (foo) {
+ /* .... 50 lines of code ... */
+} else {
+ result = 0;
+ return;
+}
+
+Instead, try to minimize the number of lines of code that need to be
+indented, by only indenting the shortest case of the 'if'
+statement, like so:
+
+if (!foo) {
+ result = 0;
+ return;
+}
+
+.... 50 lines of code ....
+
+When this technique is used properly, it makes functions much easier to read
+and follow, especially those with more than one or two 'setup' operations
+that must succeed for the rest of the function to be able to execute.
+
+- Labels/goto are acceptable
+Proper use of this technique may occasionally result in the need for a
+label/goto combination so that error/failure conditions can exit the
+function while still performing proper cleanup. This is not a bad thing!
+Use of goto in this situation is encouraged, since it removes the need
+for excess code indenting without requiring duplication of cleanup code.
+
+- Never use an uninitialized variable
+Make sure you never use an uninitialized variable. The compiler will
+usually warn you if you do so. However, do not go too far the other way,
+and needlessly initialize variables that do not require it. If the first
+time you use a variable in a function is to store a value there, then
+initializing it at declaration is pointless, and will generate extra
+object code and data in the resulting binary with no purpose. When in doubt,
+trust the compiler to tell you when you need to initialize a variable;
+if it does not warn you, initialization is not needed.
+
+- Do not cast 'void *'
+Do not explicitly cast 'void *' into any other type, nor should you cast any
+other type into 'void *'. Implicit casts to/from 'void *' are explicitly
+allowed by the C specification. This means the results of malloc(), calloc(),
+alloca(), and similar functions do not _ever_ need to be cast to a specific
+type, and when you are passing a pointer to (for example) a callback function
+that accepts a 'void *' you do not need to cast into that type.
+
+* Function naming
+-----------------
+
+All public functions (those not marked 'static'), must be named "ast_<something>"
+and have a descriptive name.
+
+As an example, suppose you wanted to take a local function "find_feature", defined
+as static in a file, and used only in that file, and make it public, and use it
+in other files. You will have to remove the "static" declaration and define a
+prototype in an appropriate header file (usually in include/asterisk). A more
+specific name should be given, such as "ast_find_call_feature".
+
+* Variable naming
+-----------------
+
+- Global variables
+Name global variables (or local variables when you have a lot of them or
+are in a long function) something that will make sense to aliens who
+find your code in 100 years. All variable names should be in lower
+case, except when following external APIs or specifications that normally
+use upper- or mixed-case variable names; in that situation, it is
+preferable to follow the external API/specification for ease of
+understanding.
+
+Make some indication in the name of global variables which represent
+options that they are in fact intended to be global.
+ e.g.: static char global_something[80]
+
+- Don't use un-necessary typedef's
+Don't use 'typedef' just to shorten the amount of typing; there is no substantial
+benefit in this:
+struct foo { int bar; }; typedef struct foo foo_t;
+
+In fact, don't use 'variable type' suffixes at all; it's much preferable to
+just type 'struct foo' rather than 'foo_s'.
+
+- Use enums instead of #define where possible
+Use enums rather than long lists of #define-d numeric constants when possible;
+this allows structure members, local variables and function arguments to
+be declared as using the enum's type. For example:
+
+enum option {
+ OPT_FOO = 1
+ OPT_BAR = 2
+ OPT_BAZ = 4
+};
+
+static enum option global_option;
+
+static handle_option(const enum option opt)
+{
+ ...
+}
+
+Note: The compiler will _not_ force you to pass an entry from the enum
+as an argument to this function; this recommendation serves only to make
+the code clearer and somewhat self-documenting. In addition, when using
+switch/case blocks that switch on enum values, the compiler will warn
+you if you forget to handle one or more of the enum values, which can be
+handy.
+
+* String handling
+-----------------
+
+Don't use strncpy for copying whole strings; it does not guarantee that the
+output buffer will be null-terminated. Use ast_copy_string instead, which
+is also slightly more efficient (and allows passing the actual buffer
+size, which makes the code clearer).
+
+Don't use ast_copy_string (or any length-limited copy function) for copying
+fixed (known at compile time) strings into buffers, if the buffer is something
+that has been allocated in the function doing the copying. In that case, you
+know at the time you are writing the code whether the buffer is large enough
+for the fixed string or not, and if it's not, your code won't work anyway!
+Use strcpy() for this operation, or directly set the first two characters
+of the buffer if you are just trying to store a one-character string in the
+buffer. If you are trying to 'empty' the buffer, just store a single
+NULL character ('\0') in the first byte of the buffer; nothing else is
+needed, and any other method is wasteful.
+
+In addition, if the previous operations in the function have already
+determined that the buffer in use is adequately sized to hold the string
+you wish to put into it (even if you did not allocate the buffer yourself),
+use a direct strcpy(), as it can be inlined and optimized to simple
+processor operations, unlike ast_copy_string().
+
+* Use of functions
+------------------
+
+When making applications, always ast_strdupa(data) to a local pointer if
+you intend to parse the incoming data string.
+
+ if (data)
+ mydata = ast_strdupa(data);
+
+
+- Separating arguments to dialplan applications and functions
+Use ast_app_separate_args() to separate the arguments to your application
+once you have made a local copy of the string.
+
+- Parsing strings with strsep
+Use strsep() for parsing strings when possible; there is no worry about
+'re-entrancy' as with strtok(), and even though it modifies the original
+string (which the man page warns about), in many cases that is exactly
+what you want!
+
+- Create generic code!
+If you do the same or a similar operation more than one time, make it a
+function or macro.
+
+Make sure you are not duplicating any functionality already found in an
+API call somewhere. If you are duplicating functionality found in
+another static function, consider the value of creating a new API call
+which can be shared.
+
+* Handling of pointers and allocations
+--------------------------------------
+
+- Dereference or localize pointers
+Always dereference or localize pointers to things that are not yours like
+channel members in a channel that is not associated with the current
+thread and for which you do not have a lock.
+ channame = ast_strdupa(otherchan->name);
+
+- Use const on pointer arguments if possible
+Use const on pointer arguments which your function will not be modifying, as this
+allows the compiler to make certain optimizations. In general, use 'const'
+on any argument that you have no direct intention of modifying, as it can
+catch logic/typing errors in your code when you use the argument variable
+in a way that you did not intend.
+
+- Do not create your own linked list code - reuse!
+As a common example of this point, make an effort to use the lockable
+linked-list macros found in include/asterisk/linkedlists.h. They are
+efficient, easy to use and provide every operation that should be
+necessary for managing a singly-linked list (if something is missing,
+let us know!). Just because you see other open-coded list implementations
+in the source tree is no reason to continue making new copies of
+that code... There are also a number of common string manipulation
+and timeval manipulation functions in asterisk/strings.h and asterisk/time.h;
+use them when possible.
+
+- Avoid needless allocations!
+Avoid needless malloc(), strdup() calls. If you only need the value in
+the scope of your function try ast_strdupa() or declare structs on the
+stack and pass a pointer to them. However, be careful to _never_ call
+alloca(), ast_strdupa() or similar functions in the argument list
+of a function you are calling; this can cause very strange stack
+arrangements and produce unexpected behavior.
+
+-Allocations for structures
+When allocating/zeroing memory for a structure, use code like this:
+
+struct foo *tmp;
+
+...
+
+tmp = ast_calloc(1, sizeof(*tmp));
+
+Avoid the combination of ast_malloc() and memset(). Instead, always use
+ast_calloc(). This will allocate and zero the memory in a single operation.
+In the case that uninitialized memory is acceptable, there should be a comment
+in the code that states why this is the case.
+
+Using sizeof(*tmp) instead of sizeof(struct foo) eliminates duplication of the
+'struct foo' identifier, which makes the code easier to read and also ensures
+that if it is copy-and-pasted it won't require as much editing.
+
+The ast_* family of functions for memory allocation are functionally the same.
+They just add an Asterisk log error message in the case that the allocation
+fails for some reason. This eliminates the need to generate custom messages
+throughout the code to log that this has occurred.
+
+-String Duplications
+
+The functions strdup and strndup can *not* accept a NULL argument. This results
+in having code like this:
+
+ if (str)
+ newstr = strdup(str);
+ else
+ newstr = NULL;
+
+However, the ast_strdup and ast_strdup functions will happily accept a NULL
+argument without generating an error. The same code can be written as:
+
+ newstr = ast_strdup(str);
+
+Furthermore, it is unnecessary to have code that malloc/calloc's for the length
+of a string (+1 for the terminating '\0') and then using strncpy to copy the
+copy the string into the resulting buffer. This is the exact same thing as
+using ast_strdup.
+
+* CLI Commands
+--------------
+
+New CLI commands should be named using the module's name, followed by a verb
+and then any parameters that the command needs. For example:
+
+*CLI> iax2 show peer <peername>
+
+not
+
+*CLI> show iax2 peer <peername>
+
+* New dialplan applications/functions
+-------------------------------------
+
+There are two methods of adding functionality to the Asterisk
+dialplan: applications and functions. Applications (found generally in
+the apps/ directory) should be collections of code that interact with
+a channel and/or user in some significant way. Functions (which can be
+provided by any type of module) are used when the provided
+functionality is simple... getting/retrieving a value, for
+example. Functions should also be used when the operation is in no way
+related to a channel (a computation or string operation, for example).
+
+Applications are registered and invoked using the
+ast_register_application function; see the apps/app_skel.c file for an
+example.
+
+Functions are registered using 'struct ast_custom_function'
+structures and the ast_custom_function_register function.
+
+* Doxygen API Documentation Guidelines
+--------------------------------------
+
+When writing Asterisk API documentation the following format should be
+followed. Do not use the javadoc style.
+
+/*!
+ * \brief Do interesting stuff.
+ * \param thing1 interesting parameter 1.
+ * \param thing2 interesting parameter 2.
+ *
+ * This function does some interesting stuff.
+ *
+ * \return zero on success, -1 on error.
+ */
+int ast_interesting_stuff(int thing1, int thing2)
+{
+ return 0;
+}
+
+Notice the use of the \param, \brief, and \return constructs. These should be
+used to describe the corresponding pieces of the function being documented.
+Also notice the blank line after the last \param directive. All doxygen
+comments must be in one /*! */ block. If the function or struct does not need
+an extended description it can be left out.
+
+Please make sure to review the doxygen manual and make liberal use of the \a,
+\code, \c, \b, \note, \li and \e modifiers as appropriate.
+
+When documenting a 'static' function or an internal structure in a module,
+use the \internal modifier to ensure that the resulting documentation
+explicitly says 'for internal use only'.
+
+Structures should be documented as follows.
+
+/*!
+ * \brief A very interesting structure.
+ */
+struct interesting_struct
+{
+ /*! \brief A data member. */
+ int member1;
+
+ int member2; /*!< \brief Another data member. */
+}
+
+Note that /*! */ blocks document the construct immediately following them
+unless they are written, /*!< */, in which case they document the construct
+preceding them.
+
+It is very much preferred that documentation is not done inline, as done in
+the previous example for member2. The first reason for this is that it tends
+to encourage extremely brief, and often pointless, documentation since people
+try to keep the comment from making the line extremely long. However, if you
+insist on using inline comments, please indent the documentation with spaces!
+That way, all of the comments are properly aligned, regardless of what tab
+size is being used for viewing the code.
+
+* Finishing up before you submit your code
+------------------------------------------
+
+- Look at the code once more
+When you achieve your desired functionality, make another few refactor
+passes over the code to optimize it.
+
+- Read the patch
+Before submitting a patch, *read* the actual patch file to be sure that
+all the changes you expect to be there are, and that there are no
+surprising changes you did not expect. During your development, that
+part of Asterisk may have changed, so make sure you compare with the
+latest CVS.
+
+- Listen to advice
+If you are asked to make changes to your patch, there is a good chance
+the changes will introduce bugs, check it even more at this stage.
+Also remember that the bug marshal or co-developer that adds comments
+is only human, they may be in error :-)
+
+- Optimize, optimize, optimize
+If you are going to reuse a computed value, save it in a variable
+instead of recomputing it over and over. This can prevent you from
+making a mistake in subsequent computations, making it easier to correct
+if the formula has an error and may or may not help optimization but
+will at least help readability.
+
+Just an example (so don't over analyze it, that'd be a shame):
+
+const char *prefix = "pre";
+const char *postfix = "post";
+char *newname;
+char *name = "data";
+
+if (name && (newname = alloca(strlen(name) + strlen(prefix) + strlen(postfix) + 3)))
+ snprintf(newname, strlen(name) + strlen(prefix) + strlen(postfix) + 3, "%s/%s/%s", prefix, name, postfix);
+
+...vs this alternative:
+
+const char *prefix = "pre";
+const char *postfix = "post";
+char *newname;
+char *name = "data";
+int len = 0;
+
+if (name && (len = strlen(name) + strlen(prefix) + strlen(postfix) + 3) && (newname = alloca(len)))
+ snprintf(newname, len, "%s/%s/%s", prefix, name, postfix);
+
+* Creating new manager events?
+------------------------------
+If you create new AMI events, please read manager.txt. Do not re-use
+existing headers for new purposes, but please re-use existing headers
+for the same type of data.
+
+Manager events that signal a status are required to have one
+event name, with a status header that shows the status.
+The old style, with one event named "ThisEventOn" and another named
+"ThisEventOff", is no longer approved.
+
+Check manager.txt for more information on manager and existing
+headers. Please update this file if you add new headers.
+
+ ------------------------------------
+ == PART TWO: BUILD ARCHITECTURE ==
+ ------------------------------------
+
+The asterisk build architecture relies on autoconf to detect the
+system configuration, and on a locally developed tool (menuselect) to
+select build options and modules list, and on gmake to do the build.
+
+The first step, usually to be done soon after a checkout, is running
+"./configure", which will store its findings in two files:
+
+ + include/asterisk/autoconfig.h
+ contains C macros, normally #define HAVE_FOO or HAVE_FOO_H ,
+ for all functions and headers that have been detected at build time.
+ These are meant to be used by C or C++ source files.
+
+ + makeopts
+ contains variables that can be used by Makefiles.
+ In addition to the usual CC, LD, ... variables pointing to
+ the various build tools, and prefix, includedir ... which are
+ useful for generic compiler flags, there are variables
+ for each package detected.
+ These are normally of the form FOO_INCLUDE=... FOO_LIB=...
+ FOO_DIR=... indicating, for each package, the useful libraries
+ and header files.
+
+The next step is to run "make menuselect", to extract the dependencies existing
+between files and modules, and to store build options.
+menuselect produces two files, both to be read by the Makefile:
+
+ + menuselect.makeopts
+ Contains for each subdirectory a list of modules that must be
+ excluded from the build, plus some additional informatiom.
+ + menuselect.makedeps
+ Contains, for each module, a list of packages it depends on.
+ For each of these packages, we can collect the relevant INCLUDE
+ and LIB files from makeopts. This file is based on information
+ in the .c source code files for each module.
+
+The top level Makefile is in charge of setting up the build environment,
+creating header files with build options, and recursively invoking the
+subdir Makefiles to produce modules and the main executable.
+
+The sources are split in multiple directories, more or less divided by
+module type (apps/ channels/ funcs/ res/ ...) or by function, for the main
+binary (main/ pbx/).
+
+
+TO BE COMPLETED
+
+
+-----------------------------------------------
+Welcome to the Asterisk development community!
+Meet you on the asterisk-dev mailing list.
+Subscribe at http://lists.digium.com!
+
+Mark Spencer, Kevin P. Fleming and
+the Asterisk.org Development Team
diff --git a/trunk/doc/India-CID.txt b/trunk/doc/India-CID.txt
new file mode 100644
index 000000000..5961bb555
--- /dev/null
+++ b/trunk/doc/India-CID.txt
@@ -0,0 +1,75 @@
+India finds itself in a unique situation (hopefully). It has several
+telephone line providers, and they are not all using the same CID
+signalling; and the CID signalling is not like other countries.
+
+In order to help those in India quickly find to the CID signalling
+system that their carrier uses (or range of them), and get the
+configs right with a minimal amount of experimentation, this file
+is provided. Not all carriers are covered, and not all mentioned
+below are complete. Those with updates to this table should post
+the new information on bug 6683 of the asterisk bug tracker.
+
+
+---------------------------------------------------------
+Provider: Bharti (is this BSNL?)
+Config: cidstart=polarity_in
+ cidsignalling=dtmf
+Results: ? (this should work), but needs to be tested?
+tested by:
+--------------------------------------------------------
+
+Provider: VSNL
+Config:
+
+Results: ?
+tested by:
+--------------------------------------------------------
+
+Provider: BSNL
+Config: cid_start=ring
+ cid_signalling=dtmf
+
+Results: ?
+tested by: (abhi)
+--------------------------------------------------------
+
+Provider: MTNL, old BSNL
+Config: cidsignalling = v23
+ cidstart=ring
+
+Results: works
+tested by: (enterux)
+--------------------------------------------------------
+
+Provider: MTNL (Delhi)
+Config: cidsignalling = v23
+ cidstart = ring
+
+cidsignalling = dtmf
+cidstart = polarity_IN
+
+cidsignalling = dtmf
+cidstart = polarity
+
+Results: fails
+tested by: brealer
+--------------------------------------------------------
+
+Provider: TATA
+Config: cidsignalling = dtmf
+ cidstart=polarity_IN
+
+Results: works
+tested by: brealer
+---------------------------------------------------------
+
+Asterisk still doesn't work with some of the CID scenarios in India.
+If you are in India, and not able to make CID work with any of the
+permutations of cidsignalling and cidstart, it could be that this
+particular situation is not covered by Asterisk. A good course of
+action would be to get in touch with the provider, and find out from
+them exactly how their CID signalling works. Describe this to us,
+and perhaps someone will be able to extend the code to cover their
+signalling.
+
+
diff --git a/trunk/doc/PEERING b/trunk/doc/PEERING
new file mode 100644
index 000000000..1a1a25c74
--- /dev/null
+++ b/trunk/doc/PEERING
@@ -0,0 +1,503 @@
+\begin{verbatim}
+
+ DIGIUM GENERAL PEERING AGREEMENT (TM)
+ Version 1.0.0, September 2004
+ Copyright (C) 2004 Digium, Inc.
+ 445 Jan Davis Drive, Huntsville, AL 35806 USA
+
+ Everyone is permitted to copy and distribute complete verbatim copies
+ of this General Peering Agreement provided it is not modified in any
+ manner.
+
+ ------------------------------------------------------
+
+ DIGIUM GENERAL PEERING AGREEMENT
+
+ PREAMBLE
+
+ For most of the history of telecommunications, the power of being able
+to locate and communicate with another person in a system, be it across
+a hall or around the world, has always centered around a centralized
+authority -- from a local PBX administrator to regional and national
+RBOCs, generally requiring fees, taxes or regulation. By contrast,
+DUNDi is a technology developed to provide users the freedom to
+communicate with each other without the necessity of any centralized
+authority. This General Peering Agreement ("GPA") is used by individual
+parties (each, a "Participant") to allow them to build the E164 trust
+group for the DUNDi protocol.
+
+ To protect the usefulness of the E164 trust group for those who use
+it, while keeping the system wholly decentralized, it is necessary to
+replace many of the responsibilities generally afforded to a company or
+government agency, with a set of responsibilities implemented by the
+parties who use the system, themselves. It is the goal of this document
+to provide all the protections necessary to keep the DUNDi E164 trust
+group useful and reliable.
+
+ The Participants wish to protect competition, promote innovation and
+value added services and make this service valuable both commercially
+and non-commercially. To that end, this GPA provides special terms and
+conditions outlining some permissible and non-permissible revenue
+sources.
+
+ This GPA is independent of any software license or other license
+agreement for a program or technology employing the DUNDi protocol. For
+example, the implementation of DUNDi used by Asterisk is covered under a
+separate license. Each Participant is responsible for compliance with
+any licenses or other agreements governing use of such program or
+technology that they use to peer.
+
+ You do not have to execute this GPA to use a program or technology
+employing the DUNDi protocol, however if you do not execute this GPA,
+you will not be able to peer using DUNDi and the E164 context with
+anyone who is a member of the trust group by virtue of their having
+executed this GPA with another member.
+
+The parties to this GPA agree as follows:
+
+ 0. DEFINITIONS. As used herein, certain terms shall be defined as
+follows:
+
+ (a) The term "DUNDi" means the DUNDi protocol as published by
+ Digium, Inc. or its successor in interest with respect to the
+ DUNDi protocol specification.
+
+ (b) The terms "E.164" and "E164" mean ITU-T specification E.164 as
+ published by the International Telecommunications Union (ITU) in
+ May, 1997.
+
+ (c) The term "Service" refers to any communication facility (e.g.,
+ telephone, fax, modem, etc.), identified by an E.164-compatible
+ number, and assigned by the appropriate authority in that
+ jurisdiction.
+
+ (d) The term "Egress Gateway" refers an Internet facility that
+ provides a communications path to a Service or Services that may
+ not be directly addressable via the Internet.
+
+ (e) The term "Route" refers to an Internet address, policies, and
+ other characteristics defined by the DUNDi protocol and
+ associated with the Service, or the Egress Gateway which
+ provides access to the specified Service.
+
+ (f) The term "Propagate" means to accept or transmit Service and/or
+ Egress Gateway Routes only using the DUNDi protocol and the
+ DUNDi context "e164" without regard to case, and does not apply
+ to the exchange of information using any other protocol or
+ context.
+
+ (g) The term "Peering System" means the network of systems that
+ Propagate Routes.
+
+ (h) The term "Subscriber" means the owner of, or someone who
+ contracts to receive, the services identified by an E.164
+ number.
+
+ (i) The term "Authorizing Individual" means the Subscriber to a
+ number who has authorized a Participant to provide Routes
+ regarding their services via this Peering System.
+
+ (j) The term "Route Authority" refers to a Participant that provides
+ an original source of said Route within the Peering System.
+ Routes are propagated from the Route Authorities through the
+ Peering System and may be cached at intermediate points. There
+ may be multiple Route Authorities for any Service.
+
+ (k) The term "Participant" (introduced above) refers to any member
+ of the Peering System.
+
+ (l) The term "Service Provider" refers to the carrier (e.g.,
+ exchange carrier, Internet Telephony Service Provider, or other
+ reseller) that provides communication facilities for a
+ particular Service to a Subscriber, Customer or other End User.
+
+ (m) The term "Weight" refers to a numeric quality assigned to a
+ Route as per the DUNDi protocol specification. The current
+ Weight definitions are shown in Exhibit A.
+
+ 1. PEERING. The undersigned Participants agree to Propagate Routes
+with each other and any other member of the Peering System and further
+agree not to Propagate DUNDi Routes with a third party unless they have
+first have executed this GPA (in its unmodified form) with such third
+party. The Participants further agree only to Propagate Routes with
+Participants whom they reasonably believe to be honoring the terms of
+the GPA. Participants may not insert, remove, amend, or otherwise
+modify any of the terms of the GPA.
+
+ 2. ACCEPTABLE USE POLICY. The DUNDi protocol contains information
+that reflect a Subscriber's or Egress Gateway's decisions to receive
+calls. In addition to the terms and conditions set forth in this GPA,
+the Participants agree to honor the intent of restrictions encoded in
+the DUNDi protocol. To that end, Participants agree to the following:
+
+ (a) A Participant may not utilize or permit the utilization of
+ Routes for which the Subscriber or Egress Gateway provider has
+ indicated that they do not wish to receive "Unsolicited Calls"
+ for the purpose of making an unsolicited phone call on behalf of
+ any party or organization.
+
+ (b) A Participant may not utilize or permit the utilization of
+ Routes which have indicated that they do not wish to receive
+ "Unsolicited Commercial Calls" for the purpose of making an
+ unsolicited phone call on behalf of a commercial organization.
+
+ (c) A Participant may never utilize or permit the utilization of any
+ DUNDi route for the purpose of making harassing phone calls.
+
+ (d) A Party may not utilize or permit the utilization of DUNDi
+ provided Routes for any systematic or random calling of numbers
+ (e.g., for the purpose of locating facsimile, modem services, or
+ systematic telemarketing).
+
+ (e) Initial control signaling for all communication sessions that
+ utilize Routes obtained from the Peering System must be sent
+ from a member of the Peering System to the Service or Egress
+ Gateway identified in the selected Route. For example, 'SIP
+ INVITES' and IAX2 "NEW" commands must be sent from the
+ requesting DUNDi node to the terminating Service.
+
+ (f) A Participant may not disclose any specific Route, Service or
+ Participant contact information obtained from the Peering System
+ to any party outside of the Peering System except as a
+ by-product of facilitating communication in accordance with
+ section 2e (e.g., phone books or other databases may not be
+ published, but the Internet addresses of the Egress Gateway or
+ Service does not need to be obfuscated.)
+
+ (g) The DUNDi Protocol requires that each Participant include valid
+ contact information about itself (including information about
+ nodes connected to each Participant). Participants may use or
+ disclose the contact information only to ensure enforcement of
+ legal furtherance of this Agreement.
+
+ 3. ROUTES. The Participants shall only propagate valid Routes, as
+defined herein, through the Peering System, regardless of the original
+source. The Participants may only provide Routes as set forth below,
+and then only if such Participant has no good faith reason to believe
+such Route to be invalid or unauthorized.
+
+ (a) A Participant may provide Routes if each Route has as its
+ original source another member of the Peering System who has
+ duly executed the GPA and such Routes are provided in accordance
+ with this Agreement; provided that the Routes are not modified
+ (e.g., with regards to existence, destination, technology or
+ Weight); or
+
+ (b) A Participant may provide Routes for Services with any Weight
+ for which it is the Subscriber; or
+
+ (c) A Participant may provide Routes for those Services whose
+ Subscriber has authorized the Participant to do so, provided
+ that the Participant is able to confirm that the Authorizing
+ Individual is the Subscriber through:
+
+ i. a written statement of ownership from the Authorizing
+ Individual, which the Participant believes in good faith
+ to be accurate (e.g., a phone bill with the name of the
+ Authorizing Individual and the number in question); or
+
+ ii. the Participant's own direct personal knowledge that the
+ Authorizing Individual is the Subscriber.
+
+ (d) A Participant may provide Routes for Services, with Weight in
+ accordance with the Current DUNDi Specification, if it can in
+ good faith provide an Egress Gateway to that Service on the
+ traditional telephone network without cost to the calling party.
+
+ 4. REVOCATION. A Participant must provide a free, easily accessible
+mechanism by which a Subscriber may revoke permission to act as a Route
+Authority for his Service. A Participant must stop acting as a Route
+Authority for that Service within 7 days after:
+
+ (a) receipt of a revocation request;
+
+ (b) receiving other notice that the Service is no longer valid; or
+
+ (c) determination that the Subscriber's information is no longer
+ accurate (including that the Subscriber is no longer the service
+ owner or the service owner's authorized delegate).
+
+ 5. SERVICE FEES. A Participant may charge a fee to act as a Route
+Authority for a Service, with any Weight, provided that no Participant
+may charge a fee to propagate the Route received through the Peering
+System.
+
+ 6. TOLL SERVICES. No Participant may provide Routes for any Services
+that require payment from the calling party or their customer for
+communication with the Service. Nothing in this section shall prohibit
+a Participant from providing routes for Services where the calling party
+may later enter into a financial transaction with the called party
+(e.g., a Participant may provide Routes for calling cards services).
+
+ 7. QUALITY. A Participant may not intentionally impair communication
+using a Route provided to the Peering System (e.g. by adding delay,
+advertisements, reduced quality). If for any reason a Participant is
+unable to deliver a call via a Route provided to the Peering System,
+that Participant shall return out-of-band Network Congestion
+notification (e.g. "503 Service Unavailable" with SIP protocol or
+"CONGESTION" with IAX protocol).
+
+ 8. PROTOCOL COMPLIANCE. Participants agree to Propagate Routes in
+strict compliance with current DUNDi protocol specifications.
+
+ 9. ADMINISTRATIVE FEES. A Participant may charge (but is not required
+to charge) another Participant a reasonable fee to cover administrative
+expenses incurred in the execution of this Agreement. A Participant may
+not charge any fee to continue the relationship or to provide Routes to
+another Participant in the Peering System.
+
+ 10. CALLER IDENTIFICATION. A Participant will make a good faith effort
+to ensure the accuracy and appropriate nature of any caller
+identification that it transmits via any Route obtained from the Peering
+System. Caller identification shall at least be provided as a valid
+E.164 number.
+
+ 11. COMPLIANCE WITH LAWS. The Participants are solely responsible for
+determining to what extent, if any, the obligations set forth in this
+GPA conflict with any laws or regulations their region. A Participant
+may not provide any service or otherwise use DUNDi under this GPA if
+doing so is prohibited by law or regulation, or if any law or regulation
+imposes requirements on the Participant that are inconsistent with the
+terms of this GPA or the Acceptable Use Policy.
+
+ 12. WARRANTY. EACH PARTICIPANT WARRANTS TO THE OTHER PARTICIPANTS THAT
+IT MADE, AND WILL CONTINUE TO MAKE, A GOOD FAITH EFFORT TO AUTHENTICATE
+OTHERS IN THE PEERING SYSTEM AND TO PROVIDE ACCURATE INFORMATION IN
+ACCORDANCE WITH THE TERMS OF THIS GPA. THIS WARRANTY IS MADE BETWEEN
+THE PARTICIPANTS, AND THE PARTICIPANTS MAY NOT EXTEND THIS WARRANTY TO
+ANY NON-PARTICIPANT INCLUDING END-USERS.
+
+ 13. DISCLAIMER OF WARRANTIES. THE PARTICIPANTS UNDERSTAND AND AGREE
+THAT ANY SERVICE PROVIDED AS A RESULT OF THIS GPA IS "AS IS." EXCEPT FOR
+THOSE WARRANTIES OTHERWISE EXPRESSLY SET FORTH HEREIN, THE PARTICIPANTS
+DISCLAIM ANY REPRESENTATIONS OR WARRANTIES OF ANY KIND OR NATURE,
+EXPRESS OR IMPLIED, AS TO THE CONDITION, VALUE OR QUALITIES OF THE
+SERVICES PROVIDED HEREUNDER, AND SPECIFICALLY DISCLAIM ANY
+REPRESENTATION OR WARRANTY OF MERCHANTABILITY, SUITABILITY OR FITNESS
+FOR A PARTICULAR PURPOSE OR AS TO THE CONDITION OR WORKMANSHIP THEREOF,
+OR THE ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT,
+INCLUDING ANY WARRANTIES ARISING FROM A COURSE OF DEALING, USAGE OR
+TRADE PRACTICE. EXCEPT AS EXPRESSLY PROVIDED HEREIN, THE PARTICIPANTS
+EXPRESSLY DISCLAIM ANY REPRESENTATIONS OR WARRANTIES THAT THE PEERING
+SERVICE WILL BE CONTINUOUS, UNINTERRUPTED OR ERROR-FREE, THAT ANY DATA
+SHARED OR OTHERWISE MADE AVAILABLE WILL BE ACCURATE OR COMPLETE OR
+OTHERWISE COMPLETELY SECURE FROM UNAUTHORIZED ACCESS.
+
+ 14. LIMITATION OF LIABILITIES. NO PARTICIPANT SHALL BE LIABLE TO ANY
+OTHER PARTICIPANT FOR INCIDENTAL, INDIRECT, CONSEQUENTIAL, SPECIAL,
+PUNITIVE OR EXEMPLARY DAMAGES OF ANY KIND (INCLUDING LOST REVENUES OR
+PROFITS, LOSS OF BUSINESS OR LOSS OF DATA) IN ANY WAY RELATED TO THIS
+GPA, WHETHER IN CONTRACT OR IN TORT, REGARDLESS OF WHETHER SUCH
+PARTICIPANT WAS ADVISED OF THE POSSIBILITY THEREOF.
+
+ 15. END-USER AGREEMENTS. The Participants may independently enter
+into agreements with end-users to provide certain services (e.g., fees
+to a Subscriber to originate Routes for that Service). To the extent
+that provision of these services employs the Peering System, the Parties
+will include in their agreements with their end-users terms and
+conditions consistent with the terms of this GPA with respect to the
+exclusion of warranties, limitation of liability and Acceptable Use
+Policy. In no event may a Participant extend the warranty described in
+Section 12 in this GPA to any end-users.
+
+ 16. INDEMNIFICATION. Each Participant agrees to defend, indemnify and
+hold harmless the other Participant or third-party beneficiaries to this
+GPA (including their affiliates, successors, assigns, agents and
+representatives and their respective officers, directors and employees)
+from and against any and all actions, suits, proceedings,
+investigations, demands, claims, judgments, liabilities, obligations,
+liens, losses, damages, expenses (including, without limitation,
+attorneys' fees) and any other fees arising out of or relating to (i)
+personal injury or property damage caused by that Participant, its
+employees, agents, servants, or other representatives; (ii) any act or
+omission by the Participant, its employees, agents, servants or other
+representatives, including, but not limited to, unauthorized
+representations or warranties made by the Participant; or (iii) any
+breach by the Participant of any of the terms or conditions of this GPA.
+
+ 17. THIRD PARTY BENEFICIARIES. This GPA is intended to benefit those
+Participants who have executed the GPA and who are in the Peering
+System. It is the intent of the Parties to this GPA to give to those
+Participants who are in the Peering System standing to bring any
+necessary legal action to enforce the terms of this GPA.
+
+ 18. TERMINATION. Any Participant may terminate this GPA at any time,
+with or without cause. A Participant that terminates must immediately
+cease to Propagate.
+
+ 19. CHOICE OF LAW. This GPA and the rights and duties of the Parties
+hereto shall be construed and determined in accordance with the internal
+laws of the State of New York, United States of America, without regard
+to its conflict of laws principles and without application of the United
+Nations Convention on Contracts for the International Sale of Goods.
+
+ 20. DISPUTE RESOLUTION. Unless otherwise agreed in writing, the
+exclusive procedure for handling disputes shall be as set forth herein.
+Notwithstanding such procedures, any Participant may, at any time, seek
+injunctive relief in addition to the process described below.
+
+ (a) Prior to mediation or arbitration the disputing Participants
+ shall seek informal resolution of disputes. The process shall be
+ initiated with written notice of one Participant to the other
+ describing the dispute with reasonable particularity followed
+ with a written response within ten (10) days of receipt of
+ notice. Each Participant shall promptly designate an executive
+ with requisite authority to resolve the dispute. The informal
+ procedure shall commence within ten (10) days of the date of
+ response. All reasonable requests for non-privileged information
+ reasonably related to the dispute shall be honored. If the
+ dispute is not resolved within thirty (30) days of commencement
+ of the procedure either Participant may proceed to mediation or
+ arbitration pursuant to the rules set forth in (b) or (c) below.
+
+ (b) If the dispute has not been resolved pursuant to (a) above or,
+ if the disputing Participants fail to commence informal dispute
+ resolution pursuant to (a) above, either Participant may, in
+ writing and within twenty (20) days of the response date noted
+ in (a) above, ask the other Participant to participate in a one
+ (1) day mediation with an impartial mediator, and the other
+ Participant shall do so. Each Participant will bear its own
+ expenses and an equal share of the fees of the mediator. If the
+ mediation is not successful the Participants may proceed with
+ arbitration pursuant to (c) below.
+
+ (c) If the dispute has not been resolved pursuant to (a) or (b)
+ above, the dispute shall be promptly referred, no later than one
+ (1) year from the date of original notice and subject to
+ applicable statute of limitations, to binding arbitration in
+ accordance with the UNCITRAL Arbitration Rules in effect on the
+ date of this contract. The appointing authority shall be the
+ International Centre for Dispute Resolution. The case shall be
+ administered by the International Centre for Dispute Resolution
+ under its Procedures for Cases under the UNCITRAL Arbitration
+ Rules. Each Participant shall bear its own expenses and shall
+ share equally in fees of the arbitrator. All arbitrators shall
+ have substantial experience in information technology and/or in
+ the telecommunications business and shall be selected by the
+ disputing participants in accordance with UNCITRAL Arbitration
+ Rules. If any arbitrator, once selected is unable or unwilling
+ to continue for any reason, replacement shall be filled via the
+ process described above and a re-hearing shall be conducted. The
+ disputing Participants will provide each other with all
+ requested documents and records reasonably related to the
+ dispute in a manner that will minimize the expense and
+ inconvenience of both parties. Discovery will not include
+ depositions or interrogatories except as the arbitrators
+ expressly allow upon a showing of need. If disputes arise
+ concerning discovery requests, the arbitrators shall have sole
+ and complete discretion to resolve the disputes. The parties and
+ arbitrator shall be guided in resolving discovery disputes by
+ the Federal Rules of Civil Procedure. The Participants agree
+ that time of the essence principles shall guide the hearing and
+ that the arbitrator shall have the right and authority to issue
+ monetary sanctions in the event of unreasonable delay. The
+ arbitrator shall deliver a written opinion setting forth
+ findings of fact and the rationale for the award within thirty
+ (30) days following conclusion of the hearing. The award of the
+ arbitrator, which may include legal and equitable relief, but
+ which may not include punitive damages, will be final and
+ binding upon the disputing Participants, and judgment may be
+ entered upon it in accordance with applicable law in any court
+ having jurisdiction thereof. In addition to award the
+ arbitrator shall have the discretion to award the prevailing
+ Participant all or part of its attorneys' fees and costs,
+ including fees associated with arbitrator, if the arbitrator
+ determines that the positions taken by the other Participant on
+ material issues of the dispute were without substantial
+ foundation. Any conflict between the UNCITRAL Arbitration Rules
+ and the provisions of this GPA shall be controlled by this GPA.
+
+ 21. INTEGRATED AGREEMENT. This GPA, constitutes the complete
+integrated agreement between the parties concerning the subject matter
+hereof. All prior and contemporaneous agreements, understandings,
+negotiations or representations, whether oral or in writing, relating to
+the subject matter of this GPA are superseded and canceled in their
+entirety.
+
+ 22. WAIVER. No waiver of any of the provisions of this GPA shall be
+deemed or shall constitute a waiver of any other provision of this GPA,
+whether or not similar, nor shall such waiver constitute a continuing
+waiver unless otherwise expressly so provided in writing. The failure
+of either party to enforce at any time any of the provisions of this
+GPA, or the failure to require at any time performance by either party
+of any of the provisions of this GPA, shall in no way be construed to be
+a present or future waiver of such provisions, nor in any way affect the
+ability of a Participant to enforce each and every such provision
+thereafter.
+
+ 23. INDEPENDENT CONTRACTORS. Nothing in this GPA shall make the
+Parties partners, joint venturers, or otherwise associated in or with
+the business of the other. Parties are, and shall always remain,
+independent contractors. No Participant shall be liable for any debts,
+accounts, obligations, or other liabilities of the other Participant,
+its agents or employees. No party is authorized to incur debts or other
+obligations of any kind on the part of or as agent for the other. This
+GPA is not a franchise agreement and does not create a franchise
+relationship between the parties, and if any provision of this GPA is
+deemed to create a franchise between the parties, then this GPA shall
+automatically terminate.
+
+ 24. CAPTIONS AND HEADINGS. The captions and headings used in this GPA
+are used for convenience only and are not to be given any legal effect.
+
+ 25. EXECUTION. This GPA may be executed in counterparts, each of which
+so executed will be deemed to be an original and such counterparts
+together will constitute one and the same Agreement. The Parties shall
+transmit to each other a signed copy of the GPA by any means that
+faithfully reproduces the GPA along with the Signature. For purposes of
+this GPA, the term "signature" shall include digital signatures as
+defined by the jurisdiction of the Participant signing the GPA.
+
+ Exhibit A
+
+Weight Range Requirements
+
+0-99 May only be used under authorization of Owner
+
+100-199 May only be used by the Owner's service
+ provider, regardless of authorization.
+
+200-299 Reserved -- do not use for e164 context.
+
+300-399 May only be used by the owner of the code under
+ which the Owner's number is a part of.
+
+400-499 May be used by any entity providing access via
+ direct connectivity to the Public Switched
+ Telephone Network.
+
+500-599 May be used by any entity providing access via
+ indirect connectivity to the Public Switched
+ Telephone Network (e.g. Via another VoIP
+ provider)
+
+600- Reserved-- do not use for e164 context.
+
+ Participant Participant
+
+Company:
+
+Address:
+
+Email:
+
+
+ _________________________ _________________________
+ Authorized Signature Authorized Signature
+
+Name:
+
+
+END OF GENERAL PEERING AGREEMENT
+
+------------------------------------------------
+
+How to Peer using this GPA If you wish to exchange routing information
+with parties using the e164 DUNDi context, all you must do is execute
+this GPA with any member of the Peering System and you will become a
+member of the Peering System and be able to make Routes available in
+accordance with this GPA.
+
+DUNDi, IAX, Asterisk and GPA are trademarks of Digium, Inc.
+
+\end{verbatim}
diff --git a/trunk/doc/asterisk-mib.txt b/trunk/doc/asterisk-mib.txt
new file mode 100644
index 000000000..9f62cf673
--- /dev/null
+++ b/trunk/doc/asterisk-mib.txt
@@ -0,0 +1,769 @@
+ASTERISK-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ OBJECT-TYPE, MODULE-IDENTITY, Integer32, Counter32, TimeTicks
+ FROM SNMPv2-SMI
+
+ TEXTUAL-CONVENTION, DisplayString, TruthValue
+ FROM SNMPv2-TC
+
+ digium
+ FROM DIGIUM-MIB;
+
+asterisk MODULE-IDENTITY
+ LAST-UPDATED "200708211450Z"
+ ORGANIZATION "Digium, Inc."
+ CONTACT-INFO
+ "Mark A. Spencer
+ Postal: Digium, Inc.
+ 445 Jan Davis Drive
+ Huntsville, AL 35806
+ USA
+ Tel: +1 256 428 6000
+ Email: markster@digium.com
+
+ Thorsten Lockert
+ Postal: Voop AS
+ Boehmergaten 42
+ NO-5057 Bergen
+ Norway
+ Tel: +47 5598 7200
+ Email: tholo@voop.no"
+ DESCRIPTION
+ "Add total and current call counter statistics."
+ REVISION "200708211450Z"
+ DESCRIPTION
+ "Asterisk is an Open Source PBX. This MIB defined
+ objects for managing Asterisk instances."
+ REVISION "200603061840Z"
+ DESCRIPTION
+ "Change audio codec identification from 3kAudio to
+ Audio3k to conform better with specification.
+
+ Expand on contact information."
+ REVISION "200602041900Z"
+ DESCRIPTION
+ "Initial published revision."
+ ::= { digium 1 }
+
+asteriskVersion OBJECT IDENTIFIER ::= { asterisk 1 }
+asteriskConfiguration OBJECT IDENTIFIER ::= { asterisk 2 }
+asteriskModules OBJECT IDENTIFIER ::= { asterisk 3 }
+asteriskIndications OBJECT IDENTIFIER ::= { asterisk 4 }
+asteriskChannels OBJECT IDENTIFIER ::= { asterisk 5 }
+
+-- asteriskVersion
+
+astVersionString OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Text version string of the version of Asterisk that
+ the SNMP Agent was compiled to run against."
+ ::= { asteriskVersion 1 }
+
+astVersionTag OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "SubVersion revision of the version of Asterisk that
+ the SNMP Agent was compiled to run against -- this is
+ typically 0 for release-versions of Asterisk."
+ ::= { asteriskVersion 2 }
+
+-- asteriskConfiguration
+
+astConfigUpTime OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Time ticks since Asterisk was started."
+ ::= { asteriskConfiguration 1 }
+
+astConfigReloadTime OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Time ticks since Asterisk was last reloaded."
+ ::= { asteriskConfiguration 2 }
+
+astConfigPid OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The process id of the running Asterisk process."
+ ::= { asteriskConfiguration 3 }
+
+astConfigSocket OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The control socket for giving Asterisk commands."
+ ::= { asteriskConfiguration 4 }
+
+astConfigCallsActive OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of calls currently active on the Asterisk PBX."
+ ::= { asteriskConfiguration 5 }
+
+astConfigCallsProcessed OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number of calls processed through the Asterisk PBX since last
+ restart."
+ ::= { asteriskConfiguration 6 }
+
+-- asteriskModules
+
+astNumModules OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of modules currently loaded into Asterisk."
+ ::= { asteriskModules 1 }
+
+-- asteriskIndications
+
+astNumIndications OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of indications currently defined in Asterisk."
+ ::= { asteriskIndications 1 }
+
+astCurrentIndication OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Default indication zone to use."
+ ::= { asteriskIndications 2 }
+
+astIndicationsTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF AstIndicationsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table with all the indication zones currently know to
+ the running Asterisk instance."
+ ::= { asteriskIndications 3 }
+
+astIndicationsEntry OBJECT-TYPE
+ SYNTAX AstIndicationsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Information about a single indication zone."
+ INDEX { astIndIndex }
+ ::= { astIndicationsTable 1 }
+
+AstIndicationsEntry ::= SEQUENCE {
+ astIndIndex Integer32,
+ astIndCountry DisplayString,
+ astIndAlias DisplayString,
+ astIndDescription DisplayString
+}
+
+astIndIndex OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Numerical index into the table of indication zones."
+ ::= { astIndicationsEntry 1 }
+
+astIndCountry OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Country for which the indication zone is valid,
+ typically this is the ISO 2-letter code of the country."
+ ::= { astIndicationsEntry 2 }
+
+astIndAlias OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ ""
+ ::= { astIndicationsEntry 3 }
+
+astIndDescription OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Description of the indication zone, usually the full
+ name of the country it is valid for."
+ ::= { astIndicationsEntry 4 }
+
+-- asteriskChannels
+
+astNumChannels OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current number of active channels."
+ ::= { asteriskChannels 1 }
+
+astChanTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF AstChanEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table with details of the currently active channels
+ in the Asterisk instance."
+ ::= { asteriskChannels 2 }
+
+astChanEntry OBJECT-TYPE
+ SYNTAX AstChanEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Details of a single channel."
+ INDEX { astChanIndex }
+ ::= { astChanTable 1 }
+
+AstChanEntry ::= SEQUENCE {
+ astChanIndex Integer32,
+ astChanName DisplayString,
+ astChanLanguage DisplayString,
+ astChanType DisplayString,
+ astChanMusicClass DisplayString,
+ astChanBridge DisplayString,
+ astChanMasq DisplayString,
+ astChanMasqr DisplayString,
+ astChanWhenHangup TimeTicks,
+ astChanApp DisplayString,
+ astChanData DisplayString,
+ astChanContext DisplayString,
+ astChanMacroContext DisplayString,
+ astChanMacroExten DisplayString,
+ astChanMacroPri Integer32,
+ astChanExten DisplayString,
+ astChanPri Integer32,
+ astChanAccountCode DisplayString,
+ astChanForwardTo DisplayString,
+ astChanUniqueId DisplayString,
+ astChanCallGroup Unsigned32,
+ astChanPickupGroup Unsigned32,
+ astChanState INTEGER,
+ astChanMuted TruthValue,
+ astChanRings Integer32,
+ astChanCidDNID DisplayString,
+ astChanCidNum DisplayString,
+ astChanCidName DisplayString,
+ astChanCidANI DisplayString,
+ astChanCidRDNIS DisplayString,
+ astChanCidPresentation DisplayString,
+ astChanCidANI2 Integer32,
+ astChanCidTON Integer32,
+ astChanCidTNS Integer32,
+ astChanAMAFlags INTEGER,
+ astChanADSI INTEGER,
+ astChanToneZone DisplayString,
+ astChanHangupCause INTEGER,
+ astChanVariables DisplayString,
+ astChanFlags BITS,
+ astChanTransferCap INTEGER
+}
+
+astChanIndex OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Index into the channel table."
+ ::= { astChanEntry 1 }
+
+astChanName OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Name of the current channel."
+ ::= { astChanEntry 2 }
+
+astChanLanguage OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Which language the current channel is configured to
+ use -- used mainly for prompts."
+ ::= { astChanEntry 3 }
+
+astChanType OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Underlying technology for the current channel."
+ ::= { astChanEntry 4 }
+
+astChanMusicClass OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Music class to be used for Music on Hold for this
+ channel."
+ ::= { astChanEntry 5 }
+
+astChanBridge OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Which channel this channel is currently bridged (in a
+ conversation) with."
+ ::= { astChanEntry 6 }
+
+astChanMasq OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Channel masquerading for us."
+ ::= { astChanEntry 7 }
+
+astChanMasqr OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Channel we are masquerading for."
+ ::= { astChanEntry 8 }
+
+astChanWhenHangup OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "How long until this channel will be hung up."
+ ::= { astChanEntry 9 }
+
+astChanApp OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current application for the channel."
+ ::= { astChanEntry 10 }
+
+astChanData OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Arguments passed to the current application."
+ ::= { astChanEntry 11 }
+
+astChanContext OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current extension context."
+ ::= { astChanEntry 12 }
+
+astChanMacroContext OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current macro context."
+ ::= { astChanEntry 13 }
+
+astChanMacroExten OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current macro extension."
+ ::= { astChanEntry 14 }
+
+astChanMacroPri OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current macro priority."
+ ::= { astChanEntry 15 }
+
+astChanExten OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current extension."
+ ::= { astChanEntry 16 }
+
+astChanPri OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current priority."
+ ::= { astChanEntry 17 }
+
+astChanAccountCode OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Account Code for billing."
+ ::= { astChanEntry 18 }
+
+astChanForwardTo OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Where to forward to if asked to dial on this
+ interface."
+ ::= { astChanEntry 19 }
+
+astChanUniqueId OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Unique Channel Identifier."
+ ::= { astChanEntry 20 }
+
+astChanCallGroup OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Call Group."
+ ::= { astChanEntry 21 }
+
+astChanPickupGroup OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Pickup Group."
+ ::= { astChanEntry 22 }
+
+astChanState OBJECT-TYPE
+ SYNTAX INTEGER {
+ stateDown(0),
+ stateReserved(1),
+ stateOffHook(2),
+ stateDialing(3),
+ stateRing(4),
+ stateRinging(5),
+ stateUp(6),
+ stateBusy(7),
+ stateDialingOffHook(8),
+ statePreRing(9)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Channel state."
+ ::= { astChanEntry 23 }
+
+astChanMuted OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Transmission of voice data has been muted."
+ ::= { astChanEntry 24 }
+
+astChanRings OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of rings so far."
+ ::= { astChanEntry 25 }
+
+astChanCidDNID OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Dialled Number ID."
+ ::= { astChanEntry 26 }
+
+astChanCidNum OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Caller Number."
+ ::= { astChanEntry 27 }
+
+astChanCidName OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Caller Name."
+ ::= { astChanEntry 28 }
+
+astCanCidANI OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "ANI"
+ ::= { astChanEntry 29 }
+
+astChanCidRDNIS OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Redirected Dialled Number Service."
+ ::= { astChanEntry 30 }
+
+astChanCidPresentation OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number Presentation/Screening."
+ ::= { astChanEntry 31 }
+
+astChanCidANI2 OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "ANI 2 (info digit)."
+ ::= { astChanEntry 32 }
+
+astChanCidTON OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Type of Number."
+ ::= { astChanEntry 33 }
+
+astChanCidTNS OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Transit Network Select."
+ ::= { astChanEntry 34 }
+
+astChanAMAFlags OBJECT-TYPE
+ SYNTAX INTEGER {
+ Default(0),
+ Omit(1),
+ Billing(2),
+ Documentation(3)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "AMA Flags."
+ ::= { astChanEntry 35 }
+
+astChanADSI OBJECT-TYPE
+ SYNTAX INTEGER {
+ Unknown(0),
+ Available(1),
+ Unavailable(2),
+ OffHookOnly(3)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Whether or not ADSI is detected on CPE."
+ ::= { astChanEntry 36 }
+
+astChanToneZone OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indication zone to use for channel."
+ ::= { astChanEntry 37 }
+
+astChanHangupCause OBJECT-TYPE
+ SYNTAX INTEGER {
+ NotDefined(0),
+ Unregistered(3),
+ Normal(16),
+ Busy(17),
+ NoAnswer(19),
+ Congestion(34),
+ Failure(38),
+ NoSuchDriver(66)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Why is the channel hung up."
+ ::= { astChanEntry 38 }
+
+astChanVariables OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Channel Variables defined for this channel."
+ ::= { astChanEntry 39 }
+
+astChanFlags OBJECT-TYPE
+ SYNTAX BITS {
+ WantsJitter(0),
+ DeferDTMF(1),
+ WriteInterrupt(2),
+ Blocking(3),
+ Zombie(4),
+ Exception(5),
+ MusicOnHold(6),
+ Spying(7),
+ NativeBridge(8),
+ AutoIncrementingLoop(9)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Flags set on this channel."
+ ::= { astChanEntry 40 }
+
+astChanTransferCap OBJECT-TYPE
+ SYNTAX INTEGER {
+ Speech(0),
+ Digital(8),
+ RestrictedDigital(9),
+ Audio3k(16),
+ DigitalWithTones(17),
+ Video(24)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Transfer Capabilities for this channel."
+ ::= { astChanEntry 41 }
+
+astNumChanTypes OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of channel types (technologies) supported."
+ ::= { asteriskChannels 3 }
+
+astChanTypeTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF AstChanTypeEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table with details of the supported channel types."
+ ::= { asteriskChannels 4 }
+
+astChanTypeEntry OBJECT-TYPE
+ SYNTAX AstChanTypeEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Information about a technology we support, including
+ how many channels are currently using this technology."
+ INDEX { astChanTypeIndex }
+ ::= { astChanTypeTable 1 }
+
+AstChanTypeEntry ::= SEQUENCE {
+ astChanTypeIndex Integer32,
+ astChanTypeName DisplayString,
+ astChanTypeDesc DisplayString,
+ astChanTypeDeviceState Integer32,
+ astChanTypeIndications Integer32,
+ astChanTypeTransfer Integer32,
+ astChanTypeChannels Gauge32
+}
+
+astChanTypeIndex OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Index into the table of channel types."
+ ::= { astChanTypeEntry 1 }
+
+astChanTypeName OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Unique name of the technology we are describing."
+ ::= { astChanTypeEntry 2 }
+
+astChanTypeDesc OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Description of the channel type (technology)."
+ ::= { astChanTypeEntry 3 }
+
+astChanTypeDeviceState OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Whether the current technology can hold device states."
+ ::= { astChanTypeEntry 4 }
+
+astChanTypeIndications OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Whether the current technology supports progress indication."
+ ::= { astChanTypeEntry 5 }
+
+astChanTypeTransfer OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Whether the current technology supports transfers, where
+ Asterisk can get out from inbetween two bridged channels."
+ ::= { astChanTypeEntry 6 }
+
+astChanTypeChannels OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of active channels using the current technology."
+ ::= { astChanTypeEntry 7 }
+
+astChanScalars OBJECT IDENTIFIER ::= { asteriskChannels 5 }
+
+astNumChanBridge OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of channels currently in a bridged state."
+ ::= { astChanScalars 1 }
+
+END
diff --git a/trunk/doc/asterisk.8 b/trunk/doc/asterisk.8
new file mode 100644
index 000000000..876721a93
--- /dev/null
+++ b/trunk/doc/asterisk.8
@@ -0,0 +1,199 @@
+.\" This manpage has been automatically generated by docbook2man
+.\" from a DocBook document. This tool can be found at:
+.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
+.\" Please send any bug reports, improvements, comments, patches,
+.\" etc. to Steve Cheng <steve@ggi-project.org>.
+.TH "ASTERISK" "8" "25 October 2005" "asterisk 1.2" ""
+
+.SH NAME
+asterisk \- All-purpose telephony server.
+.SH SYNOPSIS
+
+\fBasterisk\fR [ \fB-tThfdvVqpRgciIn\fR ] [ \fB-C \fIfile\fB\fR ] [ \fB-U \fIuser\fB\fR ] [ \fB-G \fIgroup\fB\fR ] [ \fB-x \fIcommand\fB\fR ] [ \fB-M \fIvalue\fB\fR ]
+
+
+\fBasterisk -r\fR [ \fB-v\fR ] [ \fB-x \fIcommand\fB\fR ] [ \fB-s \fIsocket\fB\fR ]
+
+.SH "DESCRIPTION"
+.PP
+\fBasterisk\fR is a full-featured telephony server which
+provides Private Branch eXchange (PBX), Interactive Voice Response (IVR),
+Automated Call Distribution (ACD), Voice over IP (VoIP) gatewaying,
+Conferencing, and a plethora of other telephony applications to a broad
+range of telephony devices including packet voice (SIP, IAX2, MGCP, Skinny,
+H.323) devices (both endpoints and proxies), as well as traditional TDM
+hardware including T1, E1, ISDN PRI, GR-303, RBS, Loopstart, Groundstart,
+ISDN BRI, and many more.
+.PP
+At start, Asterisk reads the /etc/asterisk/asterisk.conf main configuration
+file and locates the rest of the configuration files from the configuration
+in that file. The -C option specifies an alternate main configuration file.
+Virtually all aspects of the operation of asterisk's configuration files
+can be found in the sample configuration files. The format for those files
+is generally beyond the scope of this man page.
+.PP
+When running with \fB-c\fR, \fB-r\fR or \fB-R\fR
+options, Asterisk supplies a powerful command line, including command
+completion, which may be used to monitors its status, perform a variety
+of administrative actions and even explore the applications that are
+currently loaded into the system.
+.PP
+Asterisk is a trademark of Digium, Inc.
+.SH "OPTIONS"
+.TP
+\fB-C \fIfile\fB\fR
+Use \fIfile\fR as master configuration file
+instead of the default, /etc/asterisk/asterisk.conf
+.TP
+\fB-c\fR
+Provide a control console on the calling terminal.
+Specifying this option implies \fB-f\fR and will cause
+asterisk to no longer fork or detach from the controlling terminal.
+.TP
+\fB-d\fR
+Enable extra debugging statements.
+
+Note: This always sets the debug level in the asterisk process,
+even if it is running in the background. This will affect the size
+of your log files.
+.TP
+\fB-f\fR
+Do not fork or detach from controlling terminal.
+.TP
+\fB-g\fR
+Remove resource limit on core size, thus forcing Asterisk to dump
+core in the unlikely event of a segmentation fault or abort signal.
+\fBNOTE:\fR in some cases this may be incompatible
+with the \fB-U\fR or \fB-G\fR flags.
+.TP
+\fB-G \fIgroup\fB\fR
+Run as group \fIgroup\fR instead of the
+calling group. \fBNOTE:\fR this requires substantial work
+to be sure that Asterisk's environment has permission to write
+the files required for its operation, including logs, its comm
+socket, the asterisk database, etc.
+.TP
+\fB-h\fR
+Provide brief summary of command line arguments and terminate.
+.TP
+\fB-i\fR
+Prompt user to initialize any encrypted private keys for IAX2
+secure authentication during startup.
+.TP
+\fB-L \fIloadaverage\fB\fR
+Limits the maximum load average before rejecting new calls. This can
+be useful to prevent a system from being brought down by terminating
+too many simultaneous calls.
+.TP
+\fB-m\fR
+Disable log and verbose output to remote (-r) consoles.
+.TP
+\fB-M \fIvalue\fB\fR
+Limits the maximum number of calls to the specified value. This can
+be useful to prevent a system from being brought down by terminating
+too many simultaneous calls.
+.TP
+\fB-n\fR
+Disable ANSI colors even on terminals capable of displaying them.
+.TP
+\fB-p\fR
+If supported by the operating system (and executing as root),
+attempt to run with realtime priority for increased performance and
+responsiveness within the Asterisk process, at the expense of other
+programs running on the same machine.
+.TP
+\fB-q\fR
+Reduce default console output when running in conjunction with
+console mode (\fB-c\fR).
+.TP
+\fB-r\fR
+Instead of running a new Asterisk process, attempt to connect
+to a running Asterisk process and provide a console interface
+for controlling it.
+.TP
+\fB-R\fR
+Much like \fB-r\fR\&. Instead of running a new Asterisk process, attempt to connect
+to a running Asterisk process and provide a console interface
+for controlling it. Additionally, if connection to the Asterisk
+process is lost, attempt to reconnect for as long as 30 seconds.
+.TP
+\fB-s \fIsocket\fB\fR
+Allows to specify the socket file to be used to connect to the
+Asterisk console. Used in conjunction with \fB-r\fR or \fB-R\fR.
+.TP
+\fB-I\fR
+Enable internal timing if Zaptel timer is available
+The default behaviour is that outbound packets are phase locked
+to inbound packets. Enabling this switch causes them to be
+locked to the internal Zaptel timer instead.
+.TP
+\fB-t\fR
+When recording files, write them first into a temporary holding directory,
+then move them into the final location when done.
+.TP
+\fB-T\fR
+Add timestamp to all non-command related output going to the console
+when running with verbose and/or logging to the console.
+.TP
+\fB-U \fIuser\fB\fR
+Run as user \fIuser\fR instead of the
+calling user. \fBNOTE:\fR this requires substantial work
+to be sure that Asterisk's environment has permission to write
+the files required for its operation, including logs, its comm
+socket, the asterisk database, etc.
+.TP
+\fB-v\fR
+Increase the level of verboseness on the console. The more times
+\fB-v\fR is specified, the more verbose the output is.
+Specifying this option implies \fB-f\fR and will cause
+asterisk to no longer fork or detach from the controlling terminal.
+This option may also be used in conjunction with \fB-r\fR
+and \fB-R\fR\&.
+
+Note: This always sets the verbose level in the asterisk process,
+even if it is running in the background. This will affect the size
+of your log files.
+.TP
+\fB-V\fR
+Display version information and exit immediately.
+.TP
+\fB-x \fIcommand\fB\fR
+Connect to a running Asterisk process and execute a command on
+a command line, passing any output through to standard out and
+then terminating when the command execution completes. Implies
+\fB-r\fR when \fB-R\fR is not explicitly
+supplied.
+.SH "EXAMPLES"
+.PP
+\fBasterisk\fR - Begin Asterisk as a daemon
+.PP
+\fBasterisk -vvvgc\fR - Run on controlling terminal
+.PP
+\fBasterisk -rx "core show channels"\fR - Display channels on running server
+.SH "BUGS"
+.PP
+Bug reports and feature requests may be filed at http://bugs.digium.com
+.SH "SEE ALSO"
+.PP
+*CLI> \fBhelp\fR - Help on Asterisk CLI
+.PP
+*CLI> \fBcore show applications\fR - Show loaded dialplan applications
+.PP
+*CLI> \fBcore show functions\fR - Show loaded dialplan functions
+.PP
+*CLI> \fBdialplan show\fR - Show current dialplan
+.PP
+http://www.asterisk.org - The Asterisk Home Page
+.PP
+http://www.asteriskdocs.org - The Asterisk Documentation Project
+.PP
+http://www.voip-info.org/wiki-Asterisk - The Asterisk Wiki
+.PP
+http://www.digium.com/ - Asterisk sponsor and hardware supplier
+.PP
+http://www.markocam.com/ - Asterisk author's web cam
+.SH "AUTHOR"
+.PP
+Mark Spencer <markster@digium.com>
+.PP
+Countless other contributors, see CREDITS with distribution for more information
diff --git a/trunk/doc/asterisk.sgml b/trunk/doc/asterisk.sgml
new file mode 100644
index 000000000..ebcecfc06
--- /dev/null
+++ b/trunk/doc/asterisk.sgml
@@ -0,0 +1,373 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+<refentry>
+<refentryinfo>
+ <date>2005-10-18</date>
+</refentryinfo>
+<refmeta>
+ <refentrytitle>
+ <application>asterisk</application>
+ </refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo>asterisk 1.6</refmiscinfo>
+</refmeta>
+<refnamediv>
+ <refname>
+ <application>asterisk</application>
+ </refname>
+ <refpurpose>
+ All-purpose telephony server.
+ </refpurpose>
+</refnamediv>
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>asterisk</command>
+<arg><option>-tThfdvVqpRgciIns</option></arg>
+<arg><option>-C </option><replaceable class="parameter">file</replaceable></arg>
+<arg><option>-U </option><replaceable class="parameter">user</replaceable></arg>
+<arg><option>-G </option><replaceable class="parameter">group</replaceable></arg>
+<arg><option>-x </option><replaceable class="parameter">command</replaceable></arg>
+<arg><option>-M </option><replaceable class="parameter">value</replaceable></arg>
+<arg><option>-L </option><replaceable class="parameter">loadaverage</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+
+ <command>asterisk -r</command>
+ <arg><option>-v</option></arg>
+<arg><option>-x </option><replaceable class="parameter">command</replaceable></arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+<refsect1>
+ <refsect1info>
+ <date>2007-12-19</date>
+ </refsect1info>
+ <title>DESCRIPTION</title>
+ <para>
+ <command>asterisk</command> is a full-featured telephony server which
+ provides Private Branch eXchange (PBX), Interactive Voice Response (IVR),
+ Automated Call Distribution (ACD), Voice over IP (VoIP) gatewaying,
+ Conferencing, and a plethora of other telephony applications to a broad
+ range of telephony devices including packet voice (SIP, IAX2, MGCP, Skinny,
+ H.323, Unistim) devices (both endpoints and proxies), as well as traditional TDM
+ hardware including T1, E1, ISDN PRI, GR-303, RBS, Loopstart, Groundstart,
+ ISDN BRI and many more.
+ </para>
+ <para>
+ At start, Asterisk reads the /etc/asterisk/asterisk.conf main configuration
+ file and locates the rest of the configuration files from the configuration
+ in that file. The -C option specifies an alternate main configuration file.
+ Virtually all aspects of the operation of asterisk's configuration files
+ can be found in the sample configuration files. The format for those files
+ is generally beyond the scope of this man page.
+ </para>
+ <para>
+ When running with <command>-c</command>, <command>-r</command> or <command>-R</command>
+ options, Asterisk supplies a powerful command line, including command
+ completion, which may be used to monitors its status, perform a variety
+ of administrative actions and even explore the applications that are
+ currently loaded into the system.
+ </para>
+ <para>
+ Asterisk is a trademark of Digium, Inc.
+ </para>
+</refsect1>
+<refsect1>
+ <title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term>-C <replaceable class="parameter">file</replaceable></term>
+ <listitem>
+ <para>
+ Use <filename>file</filename> as master configuration file
+ instead of the default, /etc/asterisk/asterisk.conf
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-c</term>
+ <listitem>
+ <para>
+ Provide a control console on the calling terminal.
+ Specifying this option implies <command>-f</command> and will cause
+ asterisk to no longer fork or detach from the controlling terminal.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-d</term>
+ <listitem>
+ <para>
+ Enable extra debugging statements.
+ </para>
+ <para>
+ Note: This always sets the debug level in the asterisk process,
+ even if it is running in the background. This will affect the size
+ of your log files.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-f</term>
+ <listitem>
+ <para>
+ Do not fork or detach from controlling terminal.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-g</term>
+ <listitem>
+ <para>
+ Remove resource limit on core size, thus forcing Asterisk to dump
+ core in the unlikely event of a segmentation fault or abort signal.
+ <command>NOTE:</command> in some cases this may be incompatible
+ with the <command>-U</command> or <command>-G</command> flags.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-G <replaceable class="parameter">group</replaceable></term>
+ <listitem>
+ <para>
+ Run as group <replaceable>group</replaceable> instead of the
+ calling group. <command>NOTE:</command> this requires substantial work
+ to be sure that Asterisk's environment has permission to write
+ the files required for its operation, including logs, its comm
+ socket, the asterisk database, etc.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-h</term>
+ <listitem>
+ <para>
+ Provide brief summary of command line arguments and terminate.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-i</term>
+ <listitem>
+ <para>
+ Prompt user to intialize any encrypted private keys for IAX2
+ secure authentication during startup.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-I</term>
+ <listitem>
+ <para>
+ Enable internal timing if Zaptel timing is available.
+ The default behaviour is that outbound packets are phase locked
+ to inbound packets. Enabling this switch causes them to be
+ locked to the internal Zaptel timer instead.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-L <replaceable class="parameter">loadaverage</replaceable></term>
+ <listitem>
+ <para>
+ Limits the maximum load average before rejecting new calls. This can
+ be useful to prevent a system from being brought down by terminating
+ too many simultaneous calls.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-M <replaceable class="parameter">value</replaceable></term>
+ <listitem>
+ <para>
+ Limits the maximum number of calls to the specified value. This can
+ be useful to prevent a system from being brought down by terminating
+ too many simultaneous calls.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-n</term>
+ <listitem>
+ <para>
+ Disable ANSI colors even on terminals capable of displaying them.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-p</term>
+ <listitem>
+ <para>
+ If supported by the operating system (and executing as root),
+ attempt to run with realtime priority for increased performance and
+ responsiveness within the Asterisk process, at the expense of other
+ programs running on the same machine.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-q</term>
+ <listitem>
+ <para>
+ Reduce default console output when running in conjunction with
+ console mode (<command>-c</command>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-r</term>
+ <listitem>
+ <para>
+ Instead of running a new Asterisk process, attempt to connect
+ to a running Asterisk process and provide a console interface
+ for controlling it.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-R</term>
+ <listitem>
+ <para>
+ Much like <command>-r</command>. Instead of running a new Asterisk process, attempt to connect
+ to a running Asterisk process and provide a console interface
+ for controlling it. Additionally, if connection to the Asterisk
+ process is lost, attempt to reconnect for as long as 30 seconds.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-s <replaceable class="parameter">socket file name</replaceable></term>
+ <listitem>
+ <para>
+ In combination with <command>-r</command>, connect directly to a specified
+ Asterisk server socket.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-t</term>
+ <listitem>
+ <para>
+ When recording files, write them first into a temporary holding directory,
+ then move them into the final location when done.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-T</term>
+ <listitem>
+ <para>
+ Add timestamp to all non-command related output going to the console
+ when running with verbose and/or logging to the console.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-U <replaceable class="parameter">user</replaceable></term>
+ <listitem>
+ <para>
+ Run as user <replaceable>user</replaceable> instead of the
+ calling user. <command>NOTE:</command> this requires substantial work
+ to be sure that Asterisk's environment has permission to write
+ the files required for its operation, including logs, its comm
+ socket, the asterisk database, etc.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-v</term>
+ <listitem>
+ <para>
+ Increase the level of verboseness on the console. The more times
+ <command>-v</command> is specified, the more verbose the output is.
+ Specifying this option implies <command>-f</command> and will cause
+ asterisk to no longer fork or detach from the controlling terminal.
+ This option may also be used in conjunction with <command>-r</command>
+ and <command>-R</command>.
+ </para>
+ <para>
+ Note: This always sets the verbose level in the asterisk process,
+ even if it is running in the background. This will affect the size
+ of your log files.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-V</term>
+ <listitem>
+ <para>
+ Display version information and exit immediately.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-x <replaceable class="parameter">command</replaceable></term>
+ <listitem>
+ <para>
+ Connect to a running Asterisk process and execute a command on
+ a command line, passing any output through to standard out and
+ then terminating when the command execution completes. Implies
+ <command>-r</command> when <command>-R</command> is not explicitly
+ supplied.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+<refsect1>
+ <title>EXAMPLES</title>
+ <para>
+ <command>asterisk</command> - Begin Asterisk as a daemon
+ </para>
+ <para>
+ <command>asterisk -vvvgc</command> - Run on controlling terminal
+ </para>
+ <para>
+ <command>asterisk -rx "show channels"</command> - Display channels on running server
+ </para>
+</refsect1>
+<refsect1>
+ <title>BUGS</title>
+ <para>
+ Bug reports and feature requests may be filed at http://bugs.digium.com
+ </para>
+</refsect1>
+<refsect1>
+ <title>SEE ALSO</title>
+ <para>
+ *CLI&gt; <command>help</command> - Help on Asterisk CLI
+ </para>
+ <para>
+ *CLI&gt; <command>show applications</command> - Show loaded dialplan applications
+ </para>
+ <para>
+ *CLI&gt; <command>show functions</command> - Show loaded dialplan functions
+ </para>
+ <para>
+ http://www.asterisk.org - The Asterisk Home Page
+ </para>
+ <para>
+ http://www.asteriskdocs.org - The Asterisk Documentation Project
+ </para>
+ <para>
+ http://www.voip-info.org/wiki-Asterisk - The Asterisk Wiki
+ </para>
+ <para>
+ http://www.digium.com/ - Asterisk sponsor and hardware supplier
+ </para>
+ <para>
+ http://www.markocam.com/ - Asterisk author's web cam
+ </para>
+</refsect1>
+<refsect1>
+ <title>AUTHOR</title>
+ <para>
+ <author>
+ <firstname>Mark Spencer &lt;markster@digium.com&gt;</firstname>
+ </author>
+ </para>
+ <para>
+ <author>
+ <firstname>Countless other contributers, see CREDITS with distribution for more information</firstname>
+ </author>
+ </para>
+</refsect1>
+</refentry>
diff --git a/trunk/doc/backtrace.txt b/trunk/doc/backtrace.txt
new file mode 100644
index 000000000..d4e13c863
--- /dev/null
+++ b/trunk/doc/backtrace.txt
@@ -0,0 +1,191 @@
+This document is intended to provide information on how to obtain the
+backtraces required on the asterisk bug tracker, available at
+http://bugs.digium.com. The information is required by developers to
+help fix problem with bugs of any kind. Backtraces provide information
+about what was wrong when a program crashed; in our case,
+Asterisk. There are two kind of backtraces (aka 'bt') which are
+useful: bt and bt full.
+
+First of all, when you start Asterisk, you MUST start it with option
+-g. This tells Asterisk to produce a core file if it crashes.
+
+If you start Asterisk with the safe_asterisk script, it automatically
+starts using the option -g.
+
+If you're not sure if Asterisk is running with the -g option, type the
+following command in your shell:
+
+debian:/tmp# ps aux | grep asterisk
+root 17832 0.0 1.2 2348 788 pts/1 S Aug12 0:00 /bin/sh /usr/sbin/safe_asterisk
+root 26686 0.0 2.8 15544 1744 pts/1 S Aug13 0:02 asterisk -vvvg -c
+[...]
+
+The interesting information is located in the last column.
+
+Second, your copy of Asterisk must have been built without
+optimization or the backtrace will be (nearly) unusable. This can be
+done by selecting the 'DONT_OPTIMIZE' option in the Compiler Flags
+submenu in the 'make menuselect' tree before building Asterisk.
+
+After Asterisk crashes, a core file will be "dumped" in your /tmp/
+directory. To make sure it's really there, you can just type the
+following command in your shell:
+
+debian:/tmp# ls -l /tmp/core.*
+-rw------- 1 root root 10592256 Aug 12 19:40 /tmp/core.26252
+-rw------- 1 root root 9924608 Aug 12 20:12 /tmp/core.26340
+-rw------- 1 root root 10862592 Aug 12 20:14 /tmp/core.26374
+-rw------- 1 root root 9105408 Aug 12 20:19 /tmp/core.26426
+-rw------- 1 root root 9441280 Aug 12 20:20 /tmp/core.26462
+-rw------- 1 root root 8331264 Aug 13 00:32 /tmp/core.26647
+debian:/tmp#
+
+In the event that there are multiple core files present (as in the
+above example), it is important to look at the file timestamps in
+order to determine which one you really intend to look at.
+
+Now that we've verified the core file has been written to disk, the
+final part is to extract 'bt' from the core file. Core files are
+pretty big, don't be scared, it's normal.
+
+*** NOTE: Don't attach core files on the bug tracker, we only need the bt and bt full. ***
+
+For extraction, we use a really nice tool, called gdb. To verify that
+you have gdb installed on your system:
+
+debian:/tmp# gdb -v
+GNU gdb 6.3-debian
+Copyright 2004 Free Software Foundation, Inc.
+GDB is free software, covered by the GNU General Public License, and you are
+welcome to change it and/or distribute copies of it under certain conditions.
+Type "show copying" to see the conditions.
+There is absolutely no warranty for GDB. Type "show warranty" for details.
+This GDB was configured as "i386-linux".
+debian:/tmp#
+
+Which is great, we can continue. If you don't have gdb installed, go install gdb.
+
+Now load the core file in gdb, as follows:
+
+debian:/tmp# gdb asterisk /tmp/core.26252
+[...]
+(You would see a lot of output here.)
+[...]
+Reading symbols from /usr/lib/asterisk/modules/app_externalivr.so...done.
+Loaded symbols for /usr/lib/asterisk/modules/app_externalivr.so
+#0 0x29b45d7e in ?? ()
+(gdb)
+
+Now at the gdb prompt, type: bt
+You would see output similar to:
+(gdb) bt
+#0 0x29b45d7e in ?? ()
+#1 0x08180bf8 in ?? ()
+#2 0xbcdffa58 in ?? ()
+#3 0x08180bf8 in ?? ()
+#4 0xbcdffa60 in ?? ()
+#5 0x08180bf8 in ?? ()
+#6 0x180bf894 in ?? ()
+#7 0x0bf80008 in ?? ()
+#8 0x180b0818 in ?? ()
+#9 0x08068008 in ast_stopstream (tmp=0x40758d38) at file.c:180
+#10 0x000000a0 in ?? ()
+#11 0x000000a0 in ?? ()
+#12 0x00000000 in ?? ()
+#13 0x407513c3 in confcall_careful_stream (conf=0x8180bf8, filename=0x8181de8 "Zap/pseudo-1324221520") at app_meetme.c:262
+#14 0x40751332 in streamconfthread (args=0x8180bf8) at app_meetme.c:1965
+#15 0xbcdffbe0 in ?? ()
+#16 0x40028e51 in pthread_start_thread () from /lib/libpthread.so.0
+#17 0x401ec92a in clone () from /lib/libc.so.6
+(gdb)
+
+
+The bt's output is the information that we need on the bug tracker.
+
+Now do a bt full as follows:
+(gdb) bt full
+#0 0x29b45d7e in ?? ()
+No symbol table info available.
+#1 0x08180bf8 in ?? ()
+No symbol table info available.
+#2 0xbcdffa58 in ?? ()
+No symbol table info available.
+#3 0x08180bf8 in ?? ()
+No symbol table info available.
+#4 0xbcdffa60 in ?? ()
+No symbol table info available.
+#5 0x08180bf8 in ?? ()
+No symbol table info available.
+#6 0x180bf894 in ?? ()
+No symbol table info available.
+#7 0x0bf80008 in ?? ()
+No symbol table info available.
+#8 0x180b0818 in ?? ()
+No symbol table info available.
+#9 0x08068008 in ast_stopstream (tmp=0x40758d38) at file.c:180
+No locals.
+#10 0x000000a0 in ?? ()
+No symbol table info available.
+#11 0x000000a0 in ?? ()
+No symbol table info available.
+#12 0x00000000 in ?? ()
+No symbol table info available.
+#13 0x407513c3 in confcall_careful_stream (conf=0x8180bf8, filename=0x8181de8 "Zap/pseudo-1324221520") at app_meetme.c:262
+ f = (struct ast_frame *) 0x8180bf8
+ trans = (struct ast_trans_pvt *) 0x0
+#14 0x40751332 in streamconfthread (args=0x8180bf8) at app_meetme.c:1965
+No locals.
+#15 0xbcdffbe0 in ?? ()
+No symbol table info available.
+#16 0x40028e51 in pthread_start_thread () from /lib/libpthread.so.0
+No symbol table info available.
+#17 0x401ec92a in clone () from /lib/libc.so.6
+No symbol table info available.
+(gdb)
+
+We also need gdb's output. That output gives more details compared to
+the simple "bt". So we recommend that you use bt full instead of bt.
+But, if you could include both, we appreciate that.
+
+The final "extraction" would be to know all traces by all
+threads. Even if asterisk runs on the same thread for each call, it
+could have created some new threads.
+
+To make sure we have the correct information, just do:
+(gdb) thread apply all bt
+
+Thread 1 (process 26252):
+#0 0x29b45d7e in ?? ()
+#1 0x08180bf8 in ?? ()
+#2 0xbcdffa58 in ?? ()
+#3 0x08180bf8 in ?? ()
+#4 0xbcdffa60 in ?? ()
+#5 0x08180bf8 in ?? ()
+#6 0x180bf894 in ?? ()
+#7 0x0bf80008 in ?? ()
+#8 0x180b0818 in ?? ()
+#9 0x08068008 in ast_stopstream (tmp=0x40758d38) at file.c:180
+#10 0x000000a0 in ?? ()
+#11 0x000000a0 in ?? ()
+#12 0x00000000 in ?? ()
+#13 0x407513c3 in confcall_careful_stream (conf=0x8180bf8, filename=0x8181de8 "Zap/pseudo-1324221520") at app_meetme.c:262
+#14 0x40751332 in streamconfthread (args=0x8180bf8) at app_meetme.c:1965
+#15 0xbcdffbe0 in ?? ()
+#16 0x40028e51 in pthread_start_thread () from /lib/libpthread.so.0
+#17 0x401ec92a in clone () from /lib/libc.so.6
+(gdb)
+
+
+That output tells us crucial information about each thread.
+
+Now, just create an output.txt file and dump your "bt full"
+(and/or "bt") ALONG WITH "thread apply all bt" into it.
+
+Note: Please ATTACH your output, DO NOT paste it as a note.
+
+And you're ready for upload on the bug tracker.
+
+
+If you have questions or comments regarding this documentation, feel
+free to pass by the #asterisk-bugs channel on irc.freenode.net.
+
diff --git a/trunk/doc/callfiles.txt b/trunk/doc/callfiles.txt
new file mode 100644
index 000000000..3fe6cb09e
--- /dev/null
+++ b/trunk/doc/callfiles.txt
@@ -0,0 +1,139 @@
+Asterisk call files
+===================
+
+Asterisk has the ability to initiate a call from outside of the normal
+methods such as the dialplan, manager interface, or spooling interface.
+
+Using the call file method, you must give Asterisk the following information:
+
+* How to perform the call, similar to the Dial() application
+* What to do when the call is answered
+
+With call files you submit this information simply by creating a file with
+the required syntax and placing it in the outgoing spooling directory, located
+by default in /var/spool/asterisk/outgoing/ (configurable in asterisk.conf).
+
+The pbx_spool module aggressively examines the directory contents every second,
+creating a new call for every call file it finds. Do NOT write or create
+the call file directly in the outgoing directory, but always create the file
+in another directory of the same filesystem and then move the file to the
+/var/spool/asterisk/outgoing directory, or Asterisk may read just a partial
+file.
+
+
+The call file syntax
+====================
+
+The call file consists of <Key>: <value> pairs; one per line.
+
+Comments are indicated by a '#' character that begins a line, or follows a space
+or tab character. To be consistent with the configuration files in Asterisk,
+comments can also be indicated by a semicolon. However, the multiline comments
+(;-- --;) used in Asterisk configuration files are not supported. Semicolons can
+be escaped by a backslash.
+
+
+The following keys-value pairs are used to specify how setup a call:
+
+Channel: <channel> the channel to use for the new call, in the form
+ technology/resource as in the Dial application. This
+ value is required.
+
+Callerid: <callerid> the caller id to use.
+
+WaitTime: <number> how many seconds to wait for an answer before the call
+ fails (ring cycle). Default 45 seconds.
+
+Maxretries: <number> number of retries before failing, not including the
+ initial attempt. Default = 0 e.g. don't retry if fails.
+
+RetryTime: <number> how many seconds to wait before retry. The default is
+ 300 (5 minutes).
+
+Account: <account> the account code for the call. This value will be
+ assigned to CDR(accountcode)
+
+
+
+When the call answers there are two choices:
+* Execute a single application, or
+* Execute the dialplan at the specified context/extension/priority.
+
+
+To execute an application:
+--------------------------
+
+Application: <appname> the application to execute
+
+Data: <args> the application arguments
+
+
+To start executing applications in the dialplan:
+------------------------------------------------
+
+Context: <context> the context in the dialplan
+
+Extension: <exten> the extension in the specified context
+
+Priority: <priority> the priority of the specified extension
+ (numeric or label)
+
+
+
+Setvar: <var=value> you may also assign values to variables that will be
+ available to the channel, as if you had performed a
+ Set(var=value) in the dialplan. More than one Setvar:
+ maybe specified.
+
+
+The processing of the call file ends when the call is answered and terminated; when
+the call was not answered in the initial attempt and subsequent retries; or if
+the call file can't be successfully read and parsed.
+
+To specify what to do with the call file at the end of processing:
+
+Archive: <yes|no> if "no" the call file is deleted. If set to "yes" the
+ call file is moved to the "outgoing_done" subdirectory
+ of the Asterisk spool directory. The default is to
+ delete the call file.
+
+
+If the call file is archived, Asterisk will append to the call file:
+
+Status: <exitstatus> can be "Expired", "Completed" or "Failed"
+
+
+
+Other lines generated by Asterisk:
+
+Asterisk keep track of how many retries the call has already attempted,
+appending to the call file the following key-pairs in the form:
+
+StartRetry: <pid> <retrycount> (<time>)
+EndRetry: <pid> <retrycount> (<time>)
+
+With the main process ID (pid) of the Asterisk process, the retry number, and
+the attempts start and end times in time_t format.
+
+
+
+Directory locations
+===================
+
+<astspooldir>/outgoing the outgoing dir, where call files are put
+ for processing
+
+<astspooldir>/outgoing_done the archive dir
+
+
+<astspooldir> is specified in asterisk.conf, usually /var/spool/asterisk
+
+
+
+How to schedule a call
+======================
+
+Call files that have the time of the last modification in the future are ignored
+by Asterisk. This makes it possible to modify the time of a call file to the
+wanted time, move to the outgoing directory, and Asterisk will attempt to
+create the call at that time.
diff --git a/trunk/doc/datastores.txt b/trunk/doc/datastores.txt
new file mode 100644
index 000000000..64b5d35cc
--- /dev/null
+++ b/trunk/doc/datastores.txt
@@ -0,0 +1,63 @@
+Asterisk Channel Data Stores
+============================
+
+* What is a data store?
+
+A data store is a way of storing complex data (such as a structure) on a channel
+so it can be retrieved at a later time by another application, or the same application.
+
+If the data store is not freed by said application though, a callback to a destroy function
+occurs which frees the memory used by the data in the data store so no memory loss occurs.
+
+* A datastore info structure
+static const struct example_datastore {
+ .type = "example",
+ .destroy = callback_destroy
+};
+
+This is a needed structure that contains information about a datastore, it's used by many API calls.
+
+* How do you create a data store?
+
+1. Use ast_channel_datastore_alloc function to return a pre-allocated structure
+ Ex: datastore = ast_channel_datastore_alloc(&example_datastore, "uid");
+ This function takes two arguments: (datastore info structure, uid)
+2. Attach data to pre-allocated structure.
+ Ex: datastore->data = mysillydata;
+3. Add datastore to the channel
+ Ex: ast_channel_datastore_add(chan, datastore);
+ This function takes two arguments: (pointer to channel, pointer to data store)
+
+Full Example:
+
+void callback_destroy(void *data)
+{
+ ast_free(data);
+}
+
+struct ast_datastore *datastore = NULL;
+datastore = ast_channel_datastore_alloc(&example_datastore, NULL);
+datastore->data = mysillydata;
+ast_channel_datastore_add(chan, datastore);
+
+NOTE: Because you're passing a pointer to a function in your module, you'll want to include
+this in your use count. When allocated increment, when destroyed decrement.
+
+* How do you remove a data store?
+
+1. Find the data store
+ Ex: datastore = ast_channel_datastore_find(chan, &example_datastore, NULL);
+ This function takes three arguments: (pointer to channel, datastore info structure, uid)
+2. Remove the data store from the channel
+ Ex: ast_channel_datastore_remove(chan, datastore);
+ This function takes two arguments: (pointer to channel, pointer to data store)
+3. If we want to now do stuff to the data on the data store
+4. Free the data store (this will call the destroy call back)
+ Ex: ast_channel_datastore_free(datastore);
+ This function takes one argument: (pointer to data store)
+
+* How do you find a data store?
+
+1. Find the data store
+ Ex: datastore = ast_channel_datastore_find(chan, &example_datastore, NULL);
+ This function takes three arguments: (pointer to channel, datastore info structure, uid)
diff --git a/trunk/doc/digium-mib.txt b/trunk/doc/digium-mib.txt
new file mode 100644
index 000000000..018a080dd
--- /dev/null
+++ b/trunk/doc/digium-mib.txt
@@ -0,0 +1,17 @@
+DIGIUM-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ enterprises
+ FROM SNMPv2-SMI;
+
+digium MODULE-IDENTITY
+ LAST-UPDATED "200602041900Z"
+ ORGANIZATION "Digium, Inc."
+ CONTACT-INFO
+ "Mark Spencer
+ Email: markster@digium.com"
+ DESCRIPTION
+ ""
+ ::= { enterprises 22736 }
+
+END
diff --git a/trunk/doc/externalivr.txt b/trunk/doc/externalivr.txt
new file mode 100644
index 000000000..73fb5820f
--- /dev/null
+++ b/trunk/doc/externalivr.txt
@@ -0,0 +1,117 @@
+Asterisk External IVR Interface
+-------------------------------
+
+If you load app_externalivr.so in your Asterisk instance, you will
+have an ExternalIVR() application available in your dialplan. This
+application implements a simple protocol for bidirectional
+communication with an external process, while simultaneous playing
+audio files to the connected channel (without interruption or
+blocking).
+
+The arguments to ExternalIVR() consist of the command to execute and
+any arguments to pass to it, the same as the System() application
+accepts. The external command will be executed in a child process,
+with its standard file handles connected to the Asterisk process as
+follows:
+
+stdin (0) - DTMF and hangup events will be received on this handle
+stdout (1) - Playback and hangup commands can be sent on this handle
+stderr (2) - Error messages can be sent on this handle
+
+The application will also create an audio generator to play audio to
+the channel, and will start playing silence. When your application
+wants to send audio to the channel, it can send a command (see below)
+to add file(s) to the generator's playlist. The generator will then
+work its way through the list, playing each file in turn until it
+either runs out of files to play, the channel is hung up, or a command
+is received to clear the list and start with a new file. At any time,
+more files can be added to the list and the generator will play them
+in sequence.
+
+While the generator is playing audio (or silence), any DTMF events
+received on the channel will be sent to the child process (see
+below). Note that this can happen at any time, since the generator,
+the child process and the channel thread are all executing
+independently. It is very important that your external application be
+ready to receive events from Asterisk at all times (without blocking),
+or you could cause the channel to become non-responsive.
+
+If the child process dies, ExternalIVR() will notice this and hang up
+the channel immediately (and also send a message to the log).
+
+DTMF (and other) events
+-----------------------
+
+All events will be newline-terminated strings.
+
+Events send to the child's stdin will be in the following format:
+
+tag,timestamp[,data]
+
+The tag can be one of the following characters:
+
+0-9: DTMF event for keys 0 through 9
+A-D: DTMF event for keys A through D
+*: DTMF event for key *
+#: DTMF event for key #
+H: the channel was hung up by the connected party
+E: the script requested an exit
+Z: the previous command was unable to be executed (file does not
+exist, etc.)
+T: the play list was interrupted (see below)
+D: a file was dropped from the play list due to interruption (the
+data element will be the dropped file name)
+F: a file has finished playing (the data element will be the file
+name)
+
+The timestamp will be 10 digits long, and will be a decimal
+representation of a standard Unix epoch-based timestamp.
+
+Commands
+--------
+
+All commands must be newline-terminated strings.
+
+The child process can send commands on stdout in the following formats:
+
+S,filename
+A,filename
+H,message
+E,message
+O,option
+V,name=value
+
+The 'S' command checks to see if there is a playable audio file with
+the specified name, and if so, clear's the generator's playlist and
+places the file onto the list. Note that the playability check does
+not take into account transcoding requirements, so it is possible for
+the file to not be played even though it was found. If the file cannot
+be found, a 'Z' event (see above) will be sent to the child. If the
+generator is not currently playing silence, then T and D events will
+be sent to the child to signal the playlist interruption and notify
+it of the files that will not be played.
+
+The 'A' command checks to see if there is a playable audio file with
+the specified name, and if so, adds it to the generator's
+playlist. The same playability and exception rules apply as for the
+'S' command.
+
+The 'E' command stops the generator and continues execution in the dialplan,
+and logs the supplied message to the Asterisk log.
+
+The 'H' command stops the generator and hangs up the channel, and logs
+the supplied message to the Asterisk log.
+
+The 'O' command allows the child to set/clear options in the
+ExternalIVR() application. The supported options are:
+ autoclear/noautoclear:
+ Automatically interrupt and clear the playlist upon reception
+ of DTMF input.
+
+The 'V' command sets the specified channel variable to the specified value.
+
+Errors
+------
+
+Any newline-terminated output generated by the child process on its
+stderr handle will be copied into the Asterisk log.
diff --git a/trunk/doc/jabber.txt b/trunk/doc/jabber.txt
new file mode 100644
index 000000000..ca3e0f528
--- /dev/null
+++ b/trunk/doc/jabber.txt
@@ -0,0 +1,15 @@
+(res_jabber is very experimental!)
+
+Jabber(xmpp) is an xml based protocol primarily for presence and messaging.
+It is an open standard and there are several open server implementations,
+ejabberd, jabberd(2), wildfire, and many others, as well as several open source
+clients, Psi, gajim, gaim etc. Jabber differs from other IM applications as it
+is immensly extendable. This allows us to easily integrate Asterisk with
+jabber. The Asterisk Jabber Interface is provided by res_jabber.so. res_jabber
+allows for Asterisk to connect to any jabber server via the standard client
+protocol or also as a simple client. Several simple functions are exposed to
+the dial plan, jabberstatus, jabbersend, and soon jabberrecv. res_jabber is also used
+to provide the connection interface for chan_jingle.
+
+The maintainer of res_jabber is Matthew O'Gorman <mogorman@digium.com> or
+mog_work on irc or (preferred) mogorman@astjab.org over jabber.
diff --git a/trunk/doc/jingle.txt b/trunk/doc/jingle.txt
new file mode 100644
index 000000000..b1f20a639
--- /dev/null
+++ b/trunk/doc/jingle.txt
@@ -0,0 +1,10 @@
+(Jingle support in asterisk is experimental)
+Jingle is an xmpp based protocol for signalling the transfer of media.
+Currently asterisk supports the proprietary GoogleTalk protocol that is
+very similar to jingle, and hopes to soon support true jingle specs
+(JEP-166,167,176,177,180,181 etc) as more clients support the true standard.
+Jingle's configuration is very similar to sip.conf only as we are not the
+jabber server in this case you must provide a connection for the peer to
+travel out on.
+chan_gtalk is for supporting the non-jingle google/libjingle spec and
+chan_jingle will continue to move in the direction of the correct spec.
diff --git a/trunk/doc/macroexclusive.txt b/trunk/doc/macroexclusive.txt
new file mode 100644
index 000000000..3a3111493
--- /dev/null
+++ b/trunk/doc/macroexclusive.txt
@@ -0,0 +1,78 @@
+About the MacroExclusive application
+------------------------------------
+
+Steve Davies <steve@connection-telecom.com
+
+
+The MacroExclusive application was added to solve the problem of
+synchronisation between calls running at the same time.
+
+This is usually an issue when you have calls manipulating global
+variables or the Asterisk database, but may be useful elsewhere.
+
+Consider this example macro, intended to return a "next" number -
+each caller is intended to get a different number:
+
+[macro-next]
+exten => s,1,Set(RESULT=${COUNT})
+exten => s,n,SetGlobalVar(COUNT=$[${COUNT} + 1])
+
+The problem is that in a box with high activity, you can be sure
+that two calls will come along together - both will get the same
+"RESULT", or the "COUNT" value will get mangled.
+
+Calling this Macro via MacroExclusive will use a mutex to make sure
+that only one call executes in the Macro at a time. This ensures
+that the two lines execute as a unit.
+
+Note that even the s,2 line above has its own race problem. Two
+calls running that line at once will step on each other and
+the count will end up as +1 rather than +2.
+
+I've also been able to use MacroExclusive where I have two Macros
+that need to be mutually exclusive.
+
+Here's the example:
+
+[macro-push]
+; push value ${ARG2} onto stack ${ARG1}
+exten => s,1,Set(DB(STACK/${ARG1})=${ARG2}^${DB(STACK/${ARG1})})
+
+[macro-pop]
+; pop top value from stack ${ARG1}
+exten => s,1,Set(RESULT=${DB(STACK/${ARG1})})
+exten => s,n,Set(DB(STACK/${ARG1})=${CUT(RESULT,^,2)})
+exten => s,n,Set(RESULT=${CUT(RESULT,^,1)})
+
+All that futzing with the STACK/${ARG1} in the astdb needs protecting
+if this is to work. But neither push nor pop can run together.
+
+So add this "pattern":
+
+[macro-stack]
+exten => Macro(${ARG1},${ARG2},${ARG3})
+
+... and use it like so:
+
+exten => s,1,MacroExclusive(stack,push,MYSTACK,bananas)
+exten => s,n,MacroExclusive(stack,push,MYSTACK,apples)
+exten => s,n,MacroExclusive(stack,push,MYSTACK,guavas)
+exten => s,n,MacroExclusive(stack,push,MYSTACK,pawpaws)
+exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets pawpaws (yum)
+exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets guavas
+exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets apples
+exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets bananas
+
+We get to the push and pop macros "via" the stack macro. But only one call
+can execute the stack macro at a time; ergo, only one of push OR pop can
+run at a time.
+
+Hope people find this useful.
+
+Lastly, its worth pointing out that only Macros that access shared data
+will require this MacroExclusive protection. And Macro's that you call
+with macroExclusive should run quickly or you will clog up your Asterisk
+system.
+
+Regards,
+Steve
diff --git a/trunk/doc/manager_1_1.txt b/trunk/doc/manager_1_1.txt
new file mode 100644
index 000000000..b2a0ba030
--- /dev/null
+++ b/trunk/doc/manager_1_1.txt
@@ -0,0 +1,286 @@
+Changes to manager version 1.1:
+-------------------------------
+
+
+* SYNTAX CLEANUPS
+-----------------
+
+- Response: headers are now either
+ "Success" - Action OK, this message contains response
+ "Error" - Action failed, reason in Message: header
+ "Follows" - Action OK, response follows in following Events.
+
+- Manager version changed to 1.1
+
+* CHANGED EVENTS AND ACTIONS
+----------------------------
+- The Hold/Unhold events
+ - Both are now "Hold" events
+ For hold, there's a "Status: On" header, for unhold, status is off
+ - Modules chan_sip/chan_iax2
+
+- The Ping Action
+ - Now use Response: success
+ - New header "Ping: pong" :-)
+
+- The Events action
+ - Now use Response: Success
+ - The new status is reported as "Events: On" or "Events: Off"
+
+- The JabberSend action
+ - The Response: header is now the first header in the response
+ - now sends "Response: Error" instead of "Failure"
+
+- Newstate and Newchannel events
+ - these have changed headers
+ "State" -> ChannelStateDesc Text based channel state
+ -> ChannelState Numeric channel state
+ - The events does not send "<unknown>" for unknown caller IDs just an empty field
+
+- Newchannel event
+ - Now includes "AccountCode"
+
+- Newstate event
+ - Now has "CalleridNum" for numeric caller id, like Newchannel
+ - The event does not send "<unknown>" for unknown caller IDs just an empty field
+
+- Newexten and VarSet events
+ - Now are part of the new Dialplan privilege class, instead of the Call class
+
+- Dial event
+ - Event Dial has new headers, to comply with other events
+ - Source -> Channel Channel name (caller)
+ - SrcUniqueID -> UniqueID Uniqueid
+ (new) -> Dialstring Dialstring in app data
+
+- Link and Unlink events
+ - The "Link" and "Unlink" bridge events in channel.c are now renamed to "Bridge"
+ - The link state is in the bridgestate: header as "Link" or "Unlink"
+ - For channel.c bridges, "Bridgetype: core" is added. This opens up for
+ bridge events in rtp.c
+ - The RTP channel also reports Bridge: events with bridgetypes
+ - rtp-native RTP native bridge
+ - rtp-direct RTP peer-2-peer bridge (NAT support only)
+ - rtp-remote Remote (re-invite) bridge. (Not reported yet)
+
+- The "Rename" manager event has a renamed header, to use the same
+ terminology for the current channel as other events
+ - Oldname -> Channel
+
+- The "NewCallerID" manager event has a renamed header
+ - CallerID -> CallerIDnum
+ - The event does not send "<unknown>" for unknown caller IDs just an empty field
+
+- Reload event
+ - The "Reload" event sent at manager reload now has a new header and is now implemented
+ in more modules than manager to alert a reload. For channels, there's a CHANNELRELOAD
+ event to use.
+ (new) -> Module: manager | CDR | DNSmgr | RTP | ENUM
+ (new) -> Status: enabled | disabled
+ - To support reload events from other modules too
+ - cdr module added
+
+- Status action replies (Event: Status)
+ Header changes
+ - link -> BridgedChannel
+ - Account -> AccountCode
+ - (new) -> BridgedUniqueid
+
+- StatusComplete Event
+ New header
+ - (new) -> Items Number of channels reported
+
+
+- The ExtensionStatus manager command now has a "StatusDesc" field with text description of the state
+
+- The Registry and Peerstatus events in chan_sip and chan_iax now use "ChannelType" instead of "ChannelDriver"
+
+- The Response to Action: IAXpeers now have a Response: Success header
+
+- The MeetmeJoin now has caller ID name and Caller ID number fields (like MeetMeLeave)
+
+- Action ZapShowChannels
+ Header changes
+ - Channel: -> ZapChannel
+ For active channels, the Channel: and Uniqueid: headers are added
+ You can now add a "ZapChannel: " argument to zapshowchannels actions
+ to only get information about one channel.
+
+- Event ZapShowChannelsComplete
+ New header
+ - (new) -> Items: Reports number of channels reported
+
+- Action VoicemailUsersList
+ Added new headers for SayEnvelope, SayCID, AttachMessage, CanReview
+ and CallOperator voicemail configuration settings.
+
+* NEW ACTIONS
+-------------
+- Action: ModuleLoad
+ Modules: loader.c
+ Purpose:
+ To be able to unload, reload and unload modules from AMI.
+ Variables:
+ ActionID: <id> Action ID for this transaction. Will be returned.
+ Module: <name> Asterisk module name (including .so extension)
+ or subsystem identifier:
+ cdr, enum, dnsmgr, extconfig, manager, rtp, http
+ LoadType: load | unload | reload
+ The operation to be done on module
+ If no module is specified for a reload loadtype, all modules are reloaded
+
+- Action: ModuleCheck
+ Modules: loader.c
+ Purpose:
+ To check version of a module - if it's loaded
+ Variables:
+ ActionID: <id> Action ID for this transaction. Will be returned.
+ Module: <name> Asterisk module name (not including extension)
+ Returns:
+ If module is loaded, returns version number of the module
+
+ Note: This will have to change. I don't like sending Response: failure
+ on both command not found (trying this command in earlier versions of
+ Asterisk) and module not found.
+ Also, check if other manager actions behave that way.
+
+- Action: QueueSummary
+ Modules: app_queue
+ Purpose:
+ To request that the manager send a QueueSummary event (see the NEW EVENTS
+ section for more details).
+ Variables:
+ ActionID: <id> Action ID for this transaction. Will be returned.
+ Queue: <name> Queue for which the summary is desired
+
+- Action: QueuePenalty
+ Modules: app_queue
+ Purpose:
+ To change the penalty of a queue member from AMI
+ Variables:
+ Interface: <tech/name> The interface of the member whose penalty you wish to change
+ Penalty: <number> The new penalty for the member. Must be nonnegative.
+ Queue: <name> If specified, only set the penalty for the member for this queue;
+ Otherwise, set the penalty for the member in all queues to which
+ he belongs.
+
+- Action: QueueRule
+ Modules: app_queue
+ Purpose:
+ To list queue rules defined in queuerules.conf
+ Variables:
+ Rule: <name> The name of the rule whose contents you wish to list. If this variable
+ is not present, all rules in queuerules.conf will be listed.
+
+
+* NEW EVENTS
+------------
+
+- Event: Transfer
+ Modules: res_features, chan_sip
+ Purpose:
+ Inform about call transfer, linking transferer with transfer target
+ You should be able to trace the call flow with this missing piece
+ of information. If it works out well, the "Transfer" event should
+ be followed by a "Bridge" event
+ The transfermethod: header informs if this is a pbx core transfer
+ or something done on channel driver level. For SIP, check the example:
+ Example:
+
+ Event: Transfer
+ Privilege: call,all
+ TransferMethod: SIP
+ TransferType: Blind
+ Channel: SIP/device1-01849800
+ SIP-Callid: 091386f505842c87016c4d93195ec67d@127.0.0.1
+ TargetChannel: SIP/device2-01841200
+ TransferExten: 100
+ TransferContext: default
+
+- Event: ChannelUpdate
+ Modules: chan_sip.c, chan_iax2.c
+ Purpose:
+ Updates channel information with ID of PVT in channel driver, to
+ be able to link events on channel driver level.
+ * Integrated in SVN trunk as of May 4th, 2007
+
+ Example:
+
+ Event: ChannelUpdate
+ Privilege: system,all
+ Uniqueid: 1177271625.27
+ Channel: SIP/olle-01843c00
+ Channeltype: SIP
+ SIPcallid: NTQzYWFiOWM4NmE0MWRkZjExMzU2YzQ3OWQwNzg3ZmI.
+ SIPfullcontact: sip:olle@127.0.0.1:49054
+
+- Action: CoreSettings
+ Modules: manager.c
+ Purpose: To report core settings, like AMI and Asterisk version,
+ maxcalls and maxload settings.
+ * Integrated in SVN trunk as of May 4th, 2007
+ Example:
+ Response: Success
+ ActionID: 1681692777
+ AMIversion: 1.1
+ AsteriskVersion: SVN-oej-moremanager-r61756M
+ SystemName: EDVINA-node-a
+ CoreMaxCalls: 120
+ CoreMaxLoadAvg: 0.000000
+ CoreRunUser: edvina
+ CoreRunGroup: edvina
+
+- Action: CoreStatus
+ Modules: manager.c
+ Purpose: To report current PBX core status flags, like
+ number of concurrent calls, startup and reload time.
+ * Integrated in SVN trunk as of May 4th, 2007
+ Example:
+ Response: Success
+ ActionID: 1649760492
+ CoreStartupTime: 22:35:17
+ CoreReloadTime: 22:35:17
+ CoreCurrentCalls: 20
+
+- Event: NewAccountCode
+ Modules: cdr.c
+ Purpose: To report a change in account code for a live channel
+ Example:
+ Event: NewAccountCode
+ Privilege: call,all
+ Channel: SIP/olle-01844600
+ Uniqueid: 1177530895.2
+ AccountCode: Stinas account 1234848484
+ OldAccountCode: OllesAccount 12345
+
+- Event: ModuleLoadReport
+ Modules: loader.c
+ Purpose: To report that module loading is complete. Some aggressive
+ clients connect very quickly to AMI and needs to know when
+ all manager events embedded in modules are loaded
+ Also, if this does not happen, something is seriously wrong.
+ This could happen to chan_sip and other modules using DNS.
+ Example:
+ Event: ModuleLoad
+ ModuleLoadStatus: Done
+ ModuleSelection: All
+ ModuleCount: 24
+
+- Event: QueueSummary
+ Modules: app_queue
+ Purpose: To report a summary of queue information. This event is generated by
+ issuing a QueueSummary AMI action.
+ Example:
+ Event: QueueSummary
+ Queue: Sales
+ LoggedIn: 12
+ Available: 5
+ Callers: 10
+ HoldTime: 47
+ If an actionID was specified for the QueueSummary action, it will be appended as the
+ last line of the QueueSummary event.
+
+
+* TODO
+------
+
diff --git a/trunk/doc/modules.txt b/trunk/doc/modules.txt
new file mode 100644
index 000000000..f6d004718
--- /dev/null
+++ b/trunk/doc/modules.txt
@@ -0,0 +1,25 @@
+All modules must have at least the following:
+
+static int load_module():
+
+ Do what you need to do when you get started. This function can return
+AST_MODULE_LOAD_FAILURE if an action fails and the module is prevented from loading,
+AST_MODULE_LOAD_DECLINE if the module can not load because of a non-critical failure
+(the configuration file was not found), or AST_MODULE_LOAD_SUCCESS if the module
+loaded fine.
+
+static int unload_module():
+
+ The module will soon be unloaded. If any channels are using your
+features, you should give them a softhangup in an effort to keep the
+program from crashing. Generally, unload_module is only called when the
+usecount is 0 or less, but the user can force unloading at their
+discretion, and thus a module should do its best to comply (although in
+some cases there may be no way to avoid a crash). This function should
+return 0 on success and non-zero on failure (i.e. it cannot yet be
+unloaded).
+
+AST_MODULE_INFO_STANDARD(keystr, desc);
+
+keystr: Applicable license for module. In most cases this is ASTERISK_GPL_KEY.
+desc: Description of module.
diff --git a/trunk/doc/osp.txt b/trunk/doc/osp.txt
new file mode 100644
index 000000000..763bb3871
--- /dev/null
+++ b/trunk/doc/osp.txt
@@ -0,0 +1,747 @@
+
+
+
+
+
+
+
+OSP User Guide for Asterisk V1.6
+9 February 2007
+
+Table of Contents
+
+Revision History 3
+1 Introduction 4
+2 OSP Toolkit 4
+2.1 Build OSP Toolkit 4
+2.1.1 Unpacking the Toolkit 4
+2.1.2 Preparing to build the OSP Toolkit 5
+2.1.3 Building the OSP Toolkit 5
+2.1.4 Installing the OSP Toolkit 6
+2.1.5 Building the Enrollment Utility 6
+2.2 Obtain Crypto Files 6
+3 Asterisk 8
+3.1 Configure for OSP Support 8
+3.1.1 Build Asterisk with OSP Toolkit 8
+3.1.2 osp.conf 8
+3.1.3 extensions.conf 10
+3.1.4 zapata/sip/iax/h323/ooh323.conf 13
+3.2 OSP Dial Plan Functions 13
+3.2.1 OSPAuth 13
+3.2.2 OSPLookup 14
+3.2.3 OSPNext 14
+3.2.4 OSPFinish 15
+3.3 extensions.conf Examples 15
+3.3.1 Source Gateway 15
+3.3.2 Destination Gateway 17
+3.3.3 Proxy 18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Asterisk is a trademark of Digium, Inc.
+TransNexus and OSP Secures are trademarks of TransNexus, Inc.
+
+Revision History
+Revision Date of Issue Description
+
+1 26 Jul 2005 OSP Module User Guide for Asterisk V1.2
+1.4 16 Jun 2006 OSP Module User Guide for Asterisk V1.4
+1.6.0 13 Dec 2006 OSP Module User Guide for Asterisk V1.6
+1.6.1 4 Jan 2007 Clarifying edits, add revision history, add general
+ purpose extensions.conf example
+1.6.2 9 Feb 2007 Replace OSP Toolkit site from SIPfoundry with
+ SourceForge
+
+
+1 Introduction
+This document provides instructions on how to build and configure Asterisk V1.6 with the OSP Toolkit to enable secure, multi-lateral peering. This document is also available in the Asterisk source package as doc/osp.txt. The OSP Toolkit is an open source implementation of the OSP peering protocol and is freely available from https://sourceforge.net/projects/osp-toolkit. The OSP standard defined by the European Telecommunications Standards Institute (ETSI TS 101 321) www.etsi.org. If you have questions or need help, building Asterisk with the OSP Toolkit, please post your question on the OSP mailing list at https://lists.sourceforge.net/lists/listinfo/osp-toolkit-client.
+
+2 OSP Toolkit
+Please reference the OSP Toolkit document "How to Build and Test the OSP Toolkit” available from https://sourceforge.net/projects/osp-toolkit.
+
+2.1 Build OSP Toolkit
+The software listed below is required to build and use the OSP Toolkit:
+* OpenSSL (required for building) - Open Source SSL protocol and Cryptographic Algorithms (version 0.9.7g recommended) from www.openssl.org. Pre-compiled OpenSSL binary packages are not recommended because of the binary compatibility issue.
+* Perl (required for building) - A programming language used by OpenSSL for compilation. Any version of Perl should work. One version of Perl is available from www.activestate.com/Products/ActivePer. If pre-compiled OpenSSL packages are used, Perl package is not required.
+* C compiler (required for building) - Any C compiler should work. The GNU Compiler Collection from www.gnu.org is routinely used for building the OSP Toolkit for testing.
+* OSP Server (required for testing) - Access to any OSP server should work. An open source reference OSP server developed by Cisco System is available at http://www.vovida.org/applications/downloads/openosp/. RAMS, a java based open source OSP server is available at https://sourceforge.net/projects/rams. A free version of the TransNexus commercial OSP server may be downloaded from http://www.transnexus.com/OSP%20Toolkit/Peering_Server/VoIP_Peering_Server.htm.
+
+2.1.1 Unpacking the Toolkit
+After downloading the OSP Toolkit (version 3.3.6 or later release) from www.sourceforge.net, perform the following steps in order:
+
+1) Copy the OSP Toolkit distribution into the directory where it will reside. The default directory for the OSP Toolkit is /usr/src.
+
+2) Un-package the distribution file by executing the following command:
+gunzip –c OSPToolkit-###.tar.gz | tar xvf –
+Where ### is the version number separated by underlines. For example, if the version is 3.3.6, then the above command would be:
+gunzip –c OSPToolkit-3_3_6.tar.gz | tar xvf –
+A new directory (TK-3_3_6-20060303) will be created within the same directory as the tar file.
+
+3) Go to the TK-3_3_6-20060303 directory by running this command:
+cd TK-3_3_6-20060303
+Within this directory, you will find directories and files similar to what is listed below if the command "ls -F" is executed):
+ls -F
+enroll/
+RelNotes.txt lib/
+README.txt license.txt
+bin/ src/
+crypto/ test/
+include/
+
+2.1.2 Preparing to build the OSP Toolkit
+4) Compile OpenSSL according to the instructions provided with the OpenSSL distribution (You would need to do this only if you don’t have openssl already).
+
+5) Copy the OpenSSL header files (the *.h files) into the crypto/openssl directory within the osptoolkit directory. The OpenSSL header files are located under the openssl/include/openssl directory.
+
+6) Copy the OpenSSL library files (libcrypto.a and libssl.a) into the lib directory within the osptoolkit directory. The OpenSSL library files are located under the openssl directory.
+Note: Since the Asterisk requires the OpenSSL package. If the OpenSSL package has been installed, steps 4 through 6 are not necessary.
+
+7) Optionally, change the install directory of the OSP Toolkit. Open the Makefile in the /usr/src/TK-3_3_6-20060303/src directory, look for the install path variable – INSTALL_PATH, and edit it to be anywhere you want (defaults /usr/local).
+Note: Please change the install path variable only if you are familiar with both the OSP Toolkit and the Asterisk.
+
+2.1.3 Building the OSP Toolkit
+8) From within the OSP Toolkit directory (/usr/src/TK-3_3_6-20060303), start the compilation script by executing the following commands:
+cd src
+make clean; make build
+
+2.1.4 Installing the OSP Toolkit
+The header files and the library of the OSP Toolkit should be installed. Otherwise, you must specify the OSP Toolkit path for the Asterisk.
+
+9) Use the make script to install the Toolkit.
+make install
+The make script is also used to install the OSP Toolkit header files and the library into the INSTALL_PATH specified in the Makefile.
+
+Note: Please make sure you have the rights to access the INSTALL_PATH directory. For example, in order to access /usr/local directory, root privileges are required.
+2.1.5 Building the Enrollment Utility
+Device enrollment is the process of establishing a trusted cryptographic relationship between the VoIP device and the OSP Server. The Enroll program is a utility application for establishing a trusted relationship between an OSP client and an OSP server. Please see the document "Device Enrollment" at http://www.transnexus.com/OSP%20Toolkit/OSP%20Toolkit%20Documents/Device_Enrollment.pdf for more information about the enroll application.
+
+10) From within the OSP Toolkit directory (example: /usr/src/TK-3_3_6-20060303), execute the following commands at the command prompt:
+cd enroll
+make clean; make linux
+Compilation is successful if there are no errors in the compiler output. The enroll program is now located in the OSP Toolkit/bin directory (example: /usr/src/ TK-3_3_6-20060303/bin).
+
+2.2 Obtain Crypto Files
+The OSP module in Asterisk requires three crypto files containing a local certificate (localcert.pem), private key (pkey.pem), and CA certificate (cacert_0.pem). Asterisk will try to load the files from the Asterisk public/private key directory - /var/lib/asterisk/keys. If the files are not present, the OSP module will not start and the Asterisk will not support the OSP protocol. Use the enroll.sh script from the toolkit distribution to enroll Asterisk with an OSP server and obtain the crypto files. Documentation explaining how to use the enroll.sh script (Device Enrollment) to enroll with an OSP server is available at http://www.transnexus.com/OSP%20Toolkit/OSP%20Toolkit%20Documents/Device_Enrollment.pdf. Copy the files generated by the enrollment process to the Asterisk /var/lib/asterisk/keys directory.
+
+Note: The osptestserver.transnexus.com is configured only for sending and receiving non-SSL messages, and issuing signed tokens. If you need help, post a message on the OSP mailing list at https://lists.sourceforge.net/lists/listinfo/osp-toolkit-client..
+
+The enroll.sh script takes the domain name or IP addresses of the OSP servers that the OSP Toolkit needs to enroll with as arguments, and then generates pem files – cacert_#.pem, certreq.pem, localcert.pem, and pkey.pem. The ‘#’ in the cacert file name is used to differentiate the ca certificate file names for the various SP’s (OSP servers). If only one address is provided at the command line, cacert_0.pem will be generated. If 2 addresses are provided at the command line, 2 files will be generated – cacert_0.pem and cacert_1.pem, one for each SP (OSP server). The example below shows the usage when the client is registering with osptestserver.transnexus.com.
+./enroll.sh osptestserver.transnexus.com
+Generating a 512 bit RSA private key
+........................++++++++++++
+.........++++++++++++
+writing new private key to 'pkey.pem'
+-----
+You are about to be asked to enter information that will be incorporated
+into your certificate request.
+What you are about to enter is what is called a Distinguished Name or a DN.
+There are quite a few fields but you can leave some blank
+For some fields there will be a default value,
+If you enter '.', the field will be left blank.
+-----
+Country Name (2 letter code) [AU]: _______
+State or Province Name (full name) [Some-State]: _______
+Locality Name (eg, city) []:_______
+Organization Name (eg, company) [Internet Widgits Pty Ltd]: _______
+Organizational Unit Name (eg, section) []:_______
+Common Name (eg, YOUR name) []:_______
+Email Address []:_______
+
+Please enter the following 'extra' attributes
+to be sent with your certificate request
+A challenge password []:_______
+An optional company name []:_______
+
+Error Code returned from openssl command : 0
+
+CA certificate received
+[SP: osptestserver.transnexus.com]Error Code returned from getcacert command : 0
+
+output buffer after operation: operation=request
+output buffer after nonce: operation=request&nonce=1655976791184458
+X509 CertInfo context is null pointer
+Unable to get Local Certificate
+depth=0 /CN=osptestserver.transnexus.com/O=OSPServer
+verify error:num=18:self signed certificate
+verify return:1
+depth=0 /CN=osptestserver.transnexus.com/O=OSPServer
+verify return:1
+The certificate request was successful.
+Error Code returned from localcert command : 0
+The files generated should be copied to the /var/lib/asterisk/keys directory.
+Note: The script enroll.sh requires AT&T korn shell (ksh) or any of its compatible variants. The /usr/src/TK-3_3_6-20060303/bin directory should be in the PATH variable. Otherwise, enroll.sh cannot find the enroll file.
+
+3 Asterisk
+In Asterisk, all OSP support is implemented as dial plan functions. In Asterisk V1.6, all combinations of routing between OSP and non-OSP enabled networks using any combination of SIP, H.323 and IAX protocols are fully supported. Section
+3.1 describes the three easy steps to add OSP support to Asterisk:
+1. Build Asterisk with OSP Toolkit
+2. Configure osp.conf file
+3. Cut and paste to extensions.conf
+Sections 3.2 and 3.3 provide a detailed explanation of OSP dial plan functions and configuration examples. The detailed information provided in Sections 3.2 and 3.3 is not required for operating Asterisk with OSP, but may be helpful to developers who want to customize their Asterisk OSP implementation.
+
+3.1 Configure for OSP Support
+3.1.1 Build Asterisk with OSP Toolkit
+The first step is to build Asterisk with the OSP Toolkit. If the OSP Toolkit is installed in the default install directory, /usr/local, no additional configuration is required. Compile Asterisk according to the instructions provided with the Asterisk distribution.
+If the OSP Toolkit is installed in another directory, such as /myosp, Asterisk must be configured with the location of the OSP Toolkit. See the example below.
+--with-osptk=/myosp
+Note: Please change the install path only if you familiar with both the OSP Toolkit and the Asterisk. Otherwise, the change may result in Asterisk not supporting the OSP protocol.
+
+3.1.2 osp.conf
+The /etc/asterisk/osp.conf file, shown below, contains configuration parameters for using OSP. Two parameters, servicepoint and source must be configured. The default values for all other parameters will work well for standard OSP implementations.
+;
+; Open Settlement Protocol Sample Configuration File
+;
+; This file contains configuration of OSP server providers that
+; are used by the Asterisk OSP module. The section "general" is
+; reserved for global options. All other sections describe specific
+; OSP Providers. The provider "default" is used when no provider is
+; otherwise specified.
+:
+: The "servicepoint" and "source" parameters must be configured. For
+; most implementations the other parameters in this file can be left
+; unchanged.
+;
+[general]
+;
+; Enable cryptographic acceleration hardware.
+;
+accelerate=no
+;
+; Defines the status of tokens that Asterisk will validate.
+; 0 - signed tokens only
+; 1 - unsigned tokens only
+; 2 - both signed and unsigned
+; The default value is 0, i.e. the Asterisk will only validate signed
+; tokens.
+;
+tokenformat=0
+;
+[default]
+;
+; List all service points (OSP servers) for this provider. Use
+; either domain name or IP address. Most OSP servers use port 1080.
+;
+;servicepoint=http://osptestserver.transnexus.com:1080/osp
+servicepoint=http://OSP server IP:1080/osp
+;
+; Define the "source" device for requesting OSP authorization.
+: This value is usually the domain name or IP address of the
+: the Asterisk server.
+;
+;source=domain name or [IP address in brackets]
+source=[host IP]
+;
+; Define path and file name of crypto files.
+; The default path for crypto file is /var/lib/asterisk/keys. If no
+; path is defined, crypto files should be in
+; /var/lib/asterisk/keys directory.
+;
+; Specify the private key file name.
+; If this parameter is unspecified or not present, the default name
+; will be the osp.conf section name followed by "-privatekey.pem"
+; (for example: default-privatekey.pem)
+;
+privatekey=pkey.pem
+;
+; Specify the local certificate file.
+; If this parameter is unspecified or not present, the default name
+; will be the osp.conf section name followed by "- localcert.pem "
+; (for example: default-localcert.pem)
+;
+localcert=localcert.pem
+;
+; Specify one or more Certificate Authority key file names. If none
+; are listed, a single Certificate Authority key file name is added
+; with the default name of the osp.conf section name followed by
+; "-cacert_0.pem " (for example: default-cacert_0.pem)
+;
+cacert=cacert_0.pem
+;
+; Configure parameters for OSP communication between Asterisk OSP
+; client and OSP servers.
+;
+; maxconnections: Max number of simultaneous connections to the
+; provider OSP server (default=20)
+; retrydelay: Extra delay between retries (default=0)
+; retrylimit: Max number of retries before giving up (default=2)
+; timeout: Timeout for response in milliseconds (default=500)
+;
+maxconnections=20
+retrydelay=0
+retrylimit=2
+timeout=500
+;
+; Set the authentication policy.
+; 0 - NO - Accept all calls.
+; 1 – YES - Accept calls with valid token or no token.
+; Block calls with invalid token.
+; 2 – EXCLUSIVE – Accept calls with valid token.
+; Block calls with invalid token or no token.
+; Default is 1,
+;
+authpolicy=1
+;
+; Set the default destination protocol. The OSP module supports
+; SIP, H323, and IAX protocols. The default protocol is set to SIP.
+;
+defaultprotocol=SIP
+
+3.1.3 extensions.conf
+OSP functions are implemented as dial plan functions in the extensions.conf file. To add OSP support to your Asterisk server, simply copy and paste the text box below to your extensions.conf file. These functions will enable your Asterisk server to support all OSP call scenarios. Configuration of your Asterisk server for OSP is now complete.
+[globals]
+DIALOUT=Zap/1
+
+[SrcGW] ; OSP Source Gateway
+exten => _XXXX.,1,NoOp(OSPSrcGW)
+; Set calling number if necessary
+exten => _XXXX.,n,Set(CALLERID(numner)=1234567890)
+; OSP lookup using default provider, if fail/error jump to lookup+101
+exten => _XXXX.,n(lookup),OSPLookup(${EXTEN}||j)
+; Deal with outbound call according to protocol
+exten => _XXXX.,n,Macro(outbound)
+; Dial to destination, 60 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},60,oL($[${OSPOUTTIMELIMIT}*1000]))
+; Wait 1 second
+exten => _XXXX.,n,Wait,1
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPLookup fail/error
+exten => _XXXX.,lookup+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+
+[DstGW] ; OSP Destination Gateway
+exten => _XXXX.,1,NoOp(OSPDstGW)
+; Deal with inbound call according to protocol
+exten => _XXXX.,n,Macro(inbound)
+; Validate token using default provider, if fail/error jump to auth+101
+exten => _XXXX.,n(auth),OSPAuth(|j)
+; Ringing
+exten => _XXXX.,n,Ringing
+; Wait 1 second
+exten => _XXXX.,n,Wait,1
+; Check inbound call duration limit
+exten => _XXXX.,n,GoToIf($[${OSPINTIMELIMIT}=0]?100:200)
+; Without duration limit
+exten => _XXXX.,100,Dial(${DIALOUT},15,o)
+exten => _XXXX.,n,Goto(1000)
+; With duration limit
+exten => _XXXX.,200,Dial(${DIALOUT},15,oL($[${OSPINTIMELIMIT}*1000]))
+exten => _XXXX.,n,Goto(1000)
+; Wait 1 second
+exten => _XXXX.,1000,Wait,1
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPAuth fail/error
+exten => _XXXX.,auth+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+
+[GeneralProxy] ; Proxy
+exten => _XXXX.,1,NoOp(OSP-GeneralProxy)
+; Deal with inbound call according to protocol
+exten => _XXXX.,n,Macro(inbound)
+; Validate token using default provider, if fail/error jump to auth+101
+exten => _XXXX.,n(auth),OSPAuth(|j)
+; OSP lookup using default provider, if fail/error jump to lookup+101
+exten => _XXXX.,n(lookup),OSPLookup(${EXTEN}||j)
+; Deal with outbound call according to protocol
+exten => _XXXX.,n,Macro(outbound)
+; Dial to destination, 14 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},14,oL($[${OSPOUTTIMELIMIT}*1000]))
+; OSP lookup next destination using default provider, if fail/error jump to next1+101
+exten => _XXXX.,n(next1),OSPNext(${HANGUPCAUSE}||j)
+; Deal with outbound call according to protocol
+exten => _XXXX.,n,Macro(outbound)
+; Dial to destination, 15 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},15,oL($[${OSPOUTTIMELIMIT}*1000]))
+; OSP lookup next destination using default provider, if fail/error jump to next2+101
+exten => _XXXX.,n(next2),OSPNext(${HANGUPCAUSE}||j)
+; Deal with outbound call according to protocol
+exten => _XXXX.,n,Macro(outbound)
+; Dial to destination, 16 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},16,oL($[${OSPOUTTIMELIMIT}*1000]))
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPAuth fail/error
+exten => _XXXX.,auth+101,Hangup
+; Deal with OSPLookup fail/error
+exten => _XXXX.,lookup+101,Hangup
+; Deal with OSPNext fail/error
+exten => _XXXX.,next1+101,Hangup
+; Deal with OSPNext fail/error
+exten => _XXXX.,next2+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+
+[macro-inbound]
+exten => s,1,NoOp(inbound)
+; Get inbound protocol
+exten => s,n,Set(CHANTECH=${CUT(CHANNEL,/,1)})
+exten => s,n,GoToIf($["${CHANTECH}"="H323"]?100)
+exten => s,n,GoToIf($["${CHANTECH}"="IAX2"]?200)
+exten => s,n,GoToIf($["${CHANTECH}"="SIP"]?300)
+exten => s,n,GoTo(1000)
+; H323 --------------------------------------------------------
+; Get peer IP
+exten => s,100,Set(OSPPEERIP=${H323CHANINFO(peerip)})
+; Get OSP token
+exten => s,n,Set(OSPINTOKEN=${H323CHANINFO(osptoken)})
+exten => s,n,GoTo(1000)
+; IAX ----------------------------------------------------------
+; Get peer IP
+exten => s,200,Set(OSPPEERIP=${IAXPEER(CURRENTCHANNEL)})
+; Get OSP token
+exten => s,n,Set(OSPINTOKEN=${IAXCHANINFO(osptoken)})
+exten => s,n,GoTo(1000)
+; SIP ----------------------------------------------------------
+; Get peer IP
+exten => s,300,Set(OSPPEERIP=${SIPCHANINFO(peerip)})
+; Get OSP token
+exten => s,n,Set(OSPINTOKEN=${SIP_HEADER(P-OSP-Auth-Token)})
+exten => s,n,GoTo(1000)
+; --------------------------------------------------------------
+exten => s,1000,MacroExit
+
+[macro-outbound]
+exten => s,1,NoOp(outbound)
+; Set calling number which may be translated
+exten => s,n,Set(CALLERID(num)=${OSPCALLING})
+; Check destinatio protocol
+exten => s,n,GoToIf($["${OSPTECH}"="H323"]?100)
+exten => s,n,GoToIf($["${OSPTECH}"="IAX2"]?200)
+exten => s,n,GoToIf($["${OSPTECH}"="SIP"]?300)
+; Something wrong
+exten => s,n,Hangup
+exten => s,n,GoTo(1000)
+; H323 --------------------------------------------------------
+; Set call id
+exten => s,100,Set(H323CHANINFO(callid)=${OSPOUTCALLID})
+; Set OSP token
+exten => s,n,Set(H323CHANINFO(osptoken)=${OSPOUTTOKEN})
+exten => s,n,GoTo(1000)
+; IAX ----------------------------------------------------------
+; Set OSP token
+exten => s,200,Set(IAXCHANINFO(osptoken)=${OSPOUTTOKEN})
+exten => s,n,GoTo(1000)
+; SIP ----------------------------------------------------------
+exten => s,300,GoTo(1000)
+; --------------------------------------------------------------
+exten => s,1000,MacroExit
+
+3.1.4 zapata/sip/iax/h323/ooh323.conf
+There is no configuration required for OSP.
+
+3.2 OSP Dial Plan Functions
+This section provides a description of each OSP dial plan function.
+3.2.1 OSPAuth
+OSP token validation function.
+Input:
+* OSPPEERIP: last hop IP address
+* OSPINTOKEN: inbound OSP token
+* provider: OSP service provider configured in osp.conf. If it is empty, default provider is used.
+* priority jump
+Output:
+* OSPINHANDLE: inbound OSP transaction handle
+* OSPINTIMELIMIT: inbound call duration limit
+* OSPAUTHSTATUS: OSPAuth return value. SUCCESS/FAILED/ERROR
+
+3.2.2 OSPLookup
+OSP lookup function.
+Input:
+* OSPPEERIP: last hop IP address
+* OSPINHANDLE: inbound OSP transaction handle
+* OSPINTIMELIMIT: inbound call duration limit
+* exten: called number
+* provider: OSP service provider configured in osp.conf. If it is empty, default provider is used.
+* priority jump
+* callidtypes: Generate call ID for the outbound call. h: H.323; s: SIP; i: IAX. Only h, H.323, has been implemented.
+Output:
+* OSPOUTHANDLE: outbound transaction handle
+* OSPTECH: outbound protocol
+* OSPDEST: outbound destination IP address
+* OSPCALLED: outbound called nummber
+* OSPCALLING: outbound calling number
+* OSPOUTTOKEN: outbound OSP token
+* OSPRESULTS: number of remaining destinations
+* OSPOUTTIMELIMIT: outbound call duration limit
+* OSPOUTCALLIDTYPES: same as input callidtypes
+* OSPOUTCALLID: outbound call ID. Only for H.323
+* OSPDIALSTR: outbound dial string
+* OSPLOOKUPSTATUS: OSPLookup return value. SUCCESS/FAILED/ERROR
+
+3.2.3 OSPNext
+OSP lookup next function.
+Input:
+* OSPINHANDLE: inbound transaction handle
+* OSPOUTHANDLE: outbound transaction handle
+* OSPINTIMELIMIT: inbound call duration limit
+* OSPOUTCALLIDTYPES: types of call ID generated by Asterisk.
+* OSPRESULTS: number of remain destinations
+* cause: last destination disconnect cause
+* priority jump
+Output:
+* OSPTECH: outbound protocol
+* OSPDEST: outbound destination IP address
+* OSPCALLED: outbound called number
+* OSPCALLING: outbound calling number
+* OSPOUTTOKEN: outbound OSP token
+* OSPRESULTS: number of remain destinations
+* OSPOUTTIMELIMIT: outbound call duration limit
+* OSPOUTCALLID: outbound call ID. Only for H.323
+* OSPDIALSTR: outbound dial string
+* OSPNEXTSTATUS: OSPLookup return value. SUCCESS/FAILED/ERROR
+
+3.2.4 OSPFinish
+OSP report usage function.
+Input:
+* OSPINHANDLE: inbound transaction handle
+* OSPOUTHANDLE: outbound transaction handle
+* OSPAUTHSTATUS: OSPAuth return value
+* OSPLOOKUPTSTATUS: OSPLookup return value
+* OSPNEXTSTATUS: OSPNext return value
+* cause: last destination disconnect cause
+* priority jump
+Output:
+* OSPFINISHSTATUS: OSPLookup return value. SUCCESS/FAILED/ERROR
+
+3.3 extensions.conf Examples
+The extensions.conf file example provided in Section 3.1 is designed to handle all OSP call scenarios when Asterisk is used as a source or destination gateway to the PSTN or as a proxy between VoIP networks. The extenstion.conf examples in this section are designed for specific use cases only.
+3.3.1 Source Gateway
+The examples in this section apply when the Asterisk server is being used as a TDM to VoIP gateway. Calls originate on the TDM network and are converted to VoIP by Asterisk. In these cases, the Asterisk server queries an OSP server to find a route to a VoIP destination. When the call ends, Asterisk sends a CDR to the OSP server.
+For SIP protocol.
+[SIPSrcGW]
+exten => _XXXX.,1,NoOp(SIPSrcGW)
+; Set calling number if necessary
+exten => _XXXX.,n,Set(CALLERID(numner)=CallingNumber)
+; OSP lookup using default provider, if fail/error jump to lookup+101
+exten => _XXXX.,n(lookup),OSPLookup(${EXTEN}||j)
+; Set calling number which may be translated
+exten => _XXXX.,n,Set(CALLERID(num)=${OSPCALLING})
+; Dial to destination, 60 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},60,oL($[${OSPOUTTIMELIMIT}*1000]))
+; Wait 3 seconds
+exten => _XXXX.,n,Wait,3
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPLookup fail/error
+exten => _XXXX.,lookup+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+For IAX protocol.
+[IAXSrcGW]
+exten => _XXXX.,1,NoOp(IAXSrcGW)
+; Set calling number if necessary
+exten => _XXXX.,n,Set(CALLERID(numner)=CallingNumber)
+; OSP lookup using default provider, if fail/error jump to lookup+101
+exten => _XXXX.,n(lookup),OSPLookup(${EXTEN}||j)
+; Set outbound OSP token
+exten => _XXXX.,n,Set(IAXCHANINFO(osptoken)=${OSPOUTTOKEN})
+; Set calling number which may be translated
+exten => _XXXX.,n,Set(CALLERID(num)=${OSPCALLING})
+; Dial to destination, 60 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},60,oL($[${OSPOUTTIMELIMIT}*1000]))
+; Wait 3 seconds
+exten => _XXXX.,n,Wait,3
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPLookup fail/error
+exten => _XXXX.,lookup+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+For H.323 protocol.
+[H323SrcGW]
+exten => _XXXX.,1,NoOp(H323SrcGW)
+; Set calling number if necessary
+exten => _XXXX.,n,Set(CALLERID(numner)=CallingNumber)
+; OSP lookup using default provider, if fail/error jump to lookup+101
+; “h” parameter is used to generate a call id
+; Cisco OSP gateways use this call id to validate OSP token
+exten => _XXXX.,n(lookup),OSPLookup(${EXTEN}||jh)
+; Set outbound call id
+exten => _XXXX.,n,Set(OH323CHANINFO(callid)=${OSPOUTCALLID})
+; Set outbound OSP token
+exten => _XXXX.,n,Set(OH323CHANINFO(osptoken)=${OSPOUTTOKEN})
+; Set calling number which may be translated
+exten => _XXXX.,n,Set(CALLERID(num)=${OSPCALLING})
+; Dial to destination, 60 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},60,oL($[${OSPOUTTIMELIMIT}*1000]))
+; Wait 3 seconds
+exten => _XXXX.,n,Wait,3
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPLookup fail/error
+exten => _XXXX.,lookup+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+
+3.3.2 Destination Gateway
+The examples in this section apply when Asterisk is being used as a VoIP to TDM gateway. VoIP calls are received by Asterisk which validates the OSP peering token and completes to the TDM network. After the call ends, Asterisk sends a CDR to the OSP server.
+For SIP protocol
+[SIPDstGW]
+exten => _XXXX.,1,NoOp(SIPDstGW)
+; Get peer IP
+exten => _XXXX.,n,Set(OSPPEERIP=${SIPCHANINFO(peerip)})
+; Get OSP token
+exten => _XXXX.,n,Set(OSPINTOKEN=${SIP_HEADER(P-OSP-Auth-Token)})
+; Validate token using default provider, if fail/error jump to auth+101
+exten => _XXXX.,n(auth),OSPAuth(|j)
+; Ringing
+exten => _XXXX.,n,Ringing
+; Wait 1 second
+exten => _XXXX.,n,Wait,1
+; Dial phone, timeout 15 seconds, with call duration limit
+exten => _XXXX.,n,Dial(${DIALOUTANALOG}/${EXTEN:1},15,oL($[${OSPINTIMELIMIT}*1000]))
+; Wait 3 seconds
+exten => _XXXX.,n,Wait,3
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPAuth fail/error
+exten => _XXXX.,auth+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+For IAX protocol
+[IAXDstGW]
+exten => _XXXX.,1,NoOp(IAXDstGW)
+; Get peer IP
+exten => _XXXX.,n,Set(OSPPEERIP=${IAXPEER(CURRENTCHANNEL)})
+; Get OSP token
+exten => _XXXX.,n,Set(OSPINTOKEN=${IAXCHANINFO(osptoken)})
+; Validate token using default provider, if fail/error jump to auth+101
+exten => _XXXX.,n(auth),OSPAuth(|j)
+; Ringing
+exten => _XXXX.,n,Ringing
+; Wait 1 second
+exten => _XXXX.,n,Wait,1
+; Dial phone, timeout 15 seconds, with call duration limit
+exten => _XXXX.,n,Dial(${DIALOUTANALOG}/${EXTEN:1},15,oL($[${OSPINTIMELIMIT}*1000]))
+; Wait 3 seconds
+exten => _XXXX.,n,Wait,3
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPAuth fail/error
+exten => _XXXX.,auth+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+For H.323 protocol
+[H323DstGW]
+exten => _XXXX.,1,NoOp(H323DstGW)
+; Get peer IP
+exten => _XXXX.,n,Set(OSPPEERIP=${H323CHANINFO(peerip)})
+; Get OSP token
+exten => _XXXX.,n,Set(OSPINTOKEN=${H323CHANINFO(osptoken)})
+; Validate token using default provider, if fail/error jump to auth+101
+exten => _XXXX.,n(auth),OSPAuth(|j)
+; Ringing
+exten => _XXXX.,n,Ringing
+; Wait 1 second
+exten => _XXXX.,n,Wait,1
+; Dial phone, timeout 15 seconds, with call duration limit
+exten => _XXXX.,n,Dial(${DIALOUTANALOG}/${EXTEN:1},15,oL($[${OSPINTIMELIMIT}*1000]))
+; Wait 3 seconds
+exten => _XXXX.,n,Wait,3
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPAuth fail/error
+exten => _XXXX.,auth+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+
+3.3.3 Proxy
+The example in this section applies when Asterisk is a proxy between two VoIP networks.
+[GeneralProxy]
+exten => _XXXX.,1,NoOp(GeneralProxy)
+; Get peer IP and inbound OSP token
+; SIP, un-comment the following two lines.
+;exten => _XXXX.,n,Set(OSPPEERIP=${SIPCHANINFO(peerip)})
+;exten => _XXXX.,n,Set(OSPINTOKEN=${SIP_HEADER(P-OSP-Auth-Token)})
+; IAX, un-comment the following 2 lines
+;exten => _XXXX.,n,Set(OSPPEERIP=${IAXPEER(CURRENTCHANNEL)})
+;exten => _XXXX.,n,Set(OSPINTOKEN=${IAXCHANINFO(osptoken)})
+; H323, un-comment the following two lines.
+;exten => _XXXX.,n,Set(OSPPEERIP=${OH323CHANINFO(peerip)})
+;exten => _XXXX.,n,Set(OSPINTOKEN=${OH323CHANINFO(osptoken)})
+;---------------------------------------------------------------
+; Validate token using default provider, if fail/error jump to auth+101
+exten => _XXXX.,n(auth),OSPAuth(|j)
+; OSP lookup using default provider, if fail/error jump to lookup+101
+; “h” parameter is used to generate a call id for H.323 destinations
+; Cisco OSP gateways use this call id to validate OSP token
+exten => _XXXX.,n(lookup),OSPLookup(${EXTEN}||jh)
+; Set outbound call id and OSP token
+; IAX, un-comment the following line.
+;exten => _XXXX.,n,Set(IAXCHANINFO(osptoken)=${OSPOUTTOKEN})
+; H323, un-comment the following two lines.
+;exten => _XXXX.,n,Set(OH323CHANINFO(callid)=${OSPOUTCALLID})
+;exten => _XXXX.,n,Set(OH323CHANINFO(osptoken)=${OSPOUTTOKEN})
+;---------------------------------------------------------------
+; Set calling number which may be translated
+exten => _XXXX.,n,Set(CALLERID(num)=${OSPCALLING})
+; Dial to destination, 14 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},14,oL($[${OSPOUTTIMELIMIT}*1000]))
+; OSP lookup next destination using default provider, if fail/error jump to next1+101
+exten => _XXXX.,n(next1),OSPNext(${HANGUPCAUSE}||j)
+; Set outbound call id and OSP token
+; IAX, un-comment the following line.
+;exten => _XXXX.,n,Set(IAXCHANINFO(osptoken)=${OSPOUTTOKEN})
+; H323, un-comment the following two lines.
+;exten => _XXXX.,n,Set(OH323CHANINFO(callid)=${OSPOUTCALLID})
+;exten => _XXXX.,n,Set(OH323CHANINFO(osptoken)=${OSPOUTTOKEN})
+;---------------------------------------------------------------
+; Set calling number which may be translated
+exten => _XXXX.,n,Set(CALLERID(num)=${OSPCALLING})
+; Dial to destination, 15 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},15,oL($[${OSPOUTTIMELIMIT}*1000]))
+; OSP lookup next destination using default provider, if fail/error jump to next2+101
+exten => _XXXX.,n(next2),OSPNext(${HANGUPCAUSE}||j)
+; Set outbound call id and OSP token
+; IAX, un-comment the following line.
+;exten => _XXXX.,n,Set(IAXCHANINFO(osptoken)=${OSPOUTTOKEN})
+; H323, un-comment the following two lines.
+;exten => _XXXX.,n,Set(OH323CHANINFO(callid)=${OSPOUTCALLID})
+;exten => _XXXX.,n,Set(OH323CHANINFO(osptoken)=${OSPOUTTOKEN})
+;---------------------------------------------------------------
+; Set calling number which may be translated
+exten => _XXXX.,n,Set(CALLERID(num)=${OSPCALLING})
+; Dial to destination, 16 timeout, with call duration limit
+exten => _XXXX.,n,Dial(${OSPDIALSTR},16,oL($[${OSPOUTTIMELIMIT}*1000]))
+; Hangup
+exten => _XXXX.,n,Hangup
+; Deal with OSPAuth fail/error
+exten => _XXXX.,auth+101,Hangup
+; Deal with OSPLookup fail/error
+exten => _XXXX.,lookup+101,Hangup
+; Deal with 1st OSPNext fail/error
+exten => _XXXX.,next1+101,Hangup
+; Deal with 2nd OSPNext fail/error
+exten => _XXXX.,next2+101,Hangup
+exten => h,1,NoOp()
+; OSP report usage
+exten => h,n,OSPFinish(${HANGUPCAUSE})
+
+19
+
diff --git a/trunk/doc/queue.txt b/trunk/doc/queue.txt
new file mode 100644
index 000000000..11047f83f
--- /dev/null
+++ b/trunk/doc/queue.txt
@@ -0,0 +1,39 @@
+Asterisk Call Queues
+--------------------
+
+<template holder while we wait for input on a good README
+ for call queues. Please open a bug report and add text to this
+ document>
+
+* General advice on the agent channel
+-------------------------------------
+
+* Using dynamic queue members
+-----------------------------
+
+* SIP channel configuration
+---------------------------
+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
+
+
+* Other references
+-------------------
+
+* queuelog.txt
+* queues-with-callback-members.txt
+
+(Should we merge those documents into this?)
diff --git a/trunk/doc/res_config_sqlite.txt b/trunk/doc/res_config_sqlite.txt
new file mode 100644
index 000000000..39d31521a
--- /dev/null
+++ b/trunk/doc/res_config_sqlite.txt
@@ -0,0 +1,124 @@
+/*
+ * res_config_sqlite - SQLite 2 support for Asterisk
+ *
+ * This module can be used as a static/RealTime configuration module, and a CDR
+ * handler. See the Doxygen documentation for a detailed description of the
+ * module, and the configs/ directory for the sample configuration file.
+ */
+
+/*
+ * Tables for res_config_sqlite.so.
+ */
+
+/*
+ * RealTime static table.
+ */
+CREATE TABLE ast_config (
+ id INTEGER,
+ cat_metric INT(11) NOT NULL DEFAULT 0,
+ var_metric INT(11) NOT NULL DEFAULT 0,
+ commented TINYINT(1) NOT NULL DEFAULT 0,
+ filename VARCHAR(128) NOT NULL DEFAULT '',
+ category VARCHAR(128) NOT NULL DEFAULT 'default',
+ var_name VARCHAR(128) NOT NULL DEFAULT '',
+ var_val TEXT NOT NULL DEFAULT '',
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX ast_config__idx__cat_metric ON ast_config(cat_metric);
+CREATE INDEX ast_config__idx__var_metric ON ast_config(var_metric);
+CREATE INDEX ast_config__idx__filename_commented ON ast_config(filename, commented);
+
+/*
+ * CDR table (this table is automatically created if non existent).
+ */
+CREATE TABLE ast_cdr (
+ id INTEGER,
+ clid VARCHAR(80) NOT NULL DEFAULT '',
+ src VARCHAR(80) NOT NULL DEFAULT '',
+ dst VARCHAR(80) NOT NULL DEFAULT '',
+ dcontext VARCHAR(80) NOT NULL DEFAULT '',
+ channel VARCHAR(80) NOT NULL DEFAULT '',
+ dstchannel VARCHAR(80) NOT NULL DEFAULT '',
+ lastapp VARCHAR(80) NOT NULL DEFAULT '',
+ lastdata VARCHAR(80) NOT NULL DEFAULT '',
+ start DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
+ answer DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
+ end DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
+ duration INT(11) NOT NULL DEFAULT 0,
+ billsec INT(11) NOT NULL DEFAULT 0,
+ disposition VARCHAR(45) NOT NULL DEFAULT '',
+ amaflags INT(11) NOT NULL DEFAULT 0,
+ accountcode VARCHAR(20) NOT NULL DEFAULT '',
+ uniqueid VARCHAR(32) NOT NULL DEFAULT '',
+ userfield VARCHAR(255) NOT NULL DEFAULT '',
+ PRIMARY KEY (id)
+);
+
+/*
+ * SIP RealTime table.
+ */
+CREATE TABLE ast_sip (
+ id INTEGER,
+ commented TINYINT(1) NOT NULL DEFAULT 0,
+ name VARCHAR(80) NOT NULL DEFAULT '',
+ host VARCHAR(31) NOT NULL DEFAULT '',
+ nat VARCHAR(5) NOT NULL DEFAULT 'no',
+ type VARCHAR(6) NOT NULL DEFAULT 'friend',
+ accountcode VARCHAR(20) DEFAULT NULL,
+ amaflags VARCHAR(13) DEFAULT NULL,
+ callgroup VARCHAR(10) DEFAULT NULL,
+ callerid VARCHAR(80) DEFAULT NULL,
+ cancallforward CHAR(3) DEFAULT 'yes',
+ canreinvite CHAR(3) DEFAULT 'yes',
+ context VARCHAR(80) DEFAULT NULL,
+ defaultip VARCHAR(15) DEFAULT NULL,
+ dtmfmode VARCHAR(7) DEFAULT NULL,
+ fromuser VARCHAR(80) DEFAULT NULL,
+ fromdomain VARCHAR(80) DEFAULT NULL,
+ insecure VARCHAR(4) DEFAULT NULL,
+ language CHAR(2) DEFAULT NULL,
+ mailbox VARCHAR(50) DEFAULT NULL,
+ md5secret VARCHAR(80) DEFAULT NULL,
+ deny VARCHAR(95) DEFAULT NULL,
+ permit VARCHAR(95) DEFAULT NULL,
+ mask VARCHAR(95) DEFAULT NULL,
+ musiconhold VARCHAR(100) DEFAULT NULL,
+ pickupgroup VARCHAR(10) DEFAULT NULL,
+ qualify CHAR(3) DEFAULT NULL,
+ regexten VARCHAR(80) DEFAULT NULL,
+ restrictcid CHAR(3) DEFAULT NULL,
+ rtptimeout CHAR(3) DEFAULT NULL,
+ rtpholdtimeout CHAR(3) DEFAULT NULL,
+ secret VARCHAR(80) DEFAULT NULL,
+ setvar VARCHAR(100) DEFAULT NULL,
+ disallow VARCHAR(100) DEFAULT 'all',
+ allow VARCHAR(100) DEFAULT 'g729,ilbc,gsm,ulaw,alaw',
+ fullcontact VARCHAR(80) NOT NULL DEFAULT '',
+ ipaddr VARCHAR(15) NOT NULL DEFAULT '',
+ port INT(11) NOT NULL DEFAULT 0,
+ regserver VARCHAR(100) DEFAULT NULL,
+ regseconds INT(11) NOT NULL DEFAULT 0,
+ username VARCHAR(80) NOT NULL DEFAULT '',
+ PRIMARY KEY (id)
+ UNIQUE (name)
+);
+
+CREATE INDEX ast_sip__idx__commented ON ast_sip(commented);
+
+/*
+ * Dialplan RealTime table.
+ */
+CREATE TABLE ast_exten (
+ id INTEGER,
+ commented TINYINT(1) NOT NULL DEFAULT 0,
+ context VARCHAR(80) NOT NULL DEFAULT '',
+ exten VARCHAR(40) NOT NULL DEFAULT '',
+ priority INT(11) NOT NULL DEFAULT 0,
+ app VARCHAR(128) NOT NULL DEFAULT '',
+ appdata VARCHAR(128) NOT NULL DEFAULT '',
+ PRIMARY KEY (id)
+);
+
+CREATE INDEX ast_exten__idx__commented ON ast_exten(commented);
+CREATE INDEX ast_exten__idx__context_exten_priority ON ast_exten(context, exten, priority);
diff --git a/trunk/doc/rtp-packetization.txt b/trunk/doc/rtp-packetization.txt
new file mode 100644
index 000000000..c558a538e
--- /dev/null
+++ b/trunk/doc/rtp-packetization.txt
@@ -0,0 +1,75 @@
+Overview
+-------
+Asterisk currently supports configurable RTP packetization per codec for
+select RTP-based channels.
+
+Channels
+-------
+These channel drivers allow RTP packetization on a user/peer/friend
+or global level:
+ chan_sip
+ chan_skinny
+ chan_h323
+ chan_ooh323 (Asterisk-Addons)
+ chan_gtalk
+ chan_jingle
+
+Configuration
+-------
+To set a desired packetization interval on a specific codec,
+append that inteval to the allow= statement.
+
+Example:
+allow=ulaw:30,alaw,g729:60
+
+No packetization is specified in the case of alaw in this example,
+so the default of 20ms is used.
+
+Autoframing
+-------
+In addition, chan_sip has the ability to negotiate the desired
+framing at call establishment.
+
+In sip.conf if autoframing=yes is set in the global section, then
+all calls will try to set the packetization based on the remote
+endpoint's preferences. This behaviour depends on the endpoints
+ability to present the desired packetization (ptime:) in the SDP.
+If the endpoint does not include a ptime attribute, the call will
+be established with 20ms packetization.
+
+Autoframing can be set at the global level or on a user/peer/friend
+basis. If it is enabled at the global level, it applies to all
+users/peers/friends regardless of their prefered codec packetization.
+
+Codec framing options
+-------
+The following table lists the minimum and maximum values that are
+valid per codec, as well as the increment value used for each.
+Please note that the maximum values here are only recommended
+maximums, and should not exceed the RTP MTU.
+
+Name Min Max Default Increment
+g723 30 300 30 30
+gsm 20 300 20 20
+ulaw 10 150 20 10
+alaw 10 150 20 10
+g726 10 300 20 10
+ADPCM 10 300 20 10
+SLIN 10 70 20 10
+lpc10 20 20 20 20
+g729 10 230 20 10
+speex 10 60 20 10
+ilbc 30 30 30 30
+g726_aal2 10 300 20 10
+
+Invalid framing options are handled based on the following rules:
+ 1. If the specified framing is less than the codec's minimum, then
+ the minimum value is used.
+ 2. If the specific framing is greater than the codec's maximum, then
+ the maximum value is used
+ 3. If the specificed framing does not meet the increment requirement,
+ the specified framing is rounded down to the closest valid
+ framing options.
+ example allow=ulaw:33 will set the codec to 30ms framing
+ 4. If no framing is specified in the allow= directive, then the
+ codec default is used.
diff --git a/trunk/doc/siptls.txt b/trunk/doc/siptls.txt
new file mode 100644
index 000000000..3a54bf095
--- /dev/null
+++ b/trunk/doc/siptls.txt
@@ -0,0 +1,94 @@
+Asterisk SIP/TLS Transport
+==========================
+
+When using TLS the client will typically check the validity of the
+certificate chain. So that means you either need a certificate that is
+signed by one of the larger CAs, or if you use a self signed certificate
+you must install a copy of your CA on the client.
+
+So far this code has been test with:
+Asterisk as client and server (TLS and TCP)
+Polycom Soundpoint IP Phones (TLS and TCP)
+ Polycom phones require that the host (ip or hostname) that is
+ configured match the 'common name' in the certificate
+Minisip Softphone (TLS and TCP)
+Cisco IOS Gateways (TCP only)
+SNOM 360 (TLS only)
+Zoiper Biz Softphone (TLS and TCP)
+
+
+sip.conf options
+----------------
+tlsenable=[yes|no]
+ Enable TLS server, default is no
+
+tlsbindaddr=<ip address>
+ Specify IP address to bind TLS server to, default is 0.0.0.0
+
+tlscertfile=</path/to/certificate>
+ The server's certificate file. Should include the key and
+ certificate. This is mandatory if your going to run a TLS server.
+
+tlscafile=</path/to/certificate>
+ If the server your connecting to uses a self signed certificate
+ you should have their certificate installed here so the code can
+ verify the authenticity of their certificate.
+
+tlscadir=</path/to/ca/dir>
+ A directory full of CA certificates. The files must be named with
+ the CA subject name hash value.
+ (see man SSL_CTX_load_verify_locations for more info)
+
+tlsdontverifyserver=[yes|no]
+ If set to yes, don't verify the servers certificate when acting as
+ a client. If you don't have the server's CA certificate you can
+ set this and it will connect without requiring tlscafile to be set.
+ Default is no.
+
+tlscipher=<SSL cipher string>
+ A string specifying which SSL ciphers to use or not use
+
+
+Sample config
+-------------
+
+Here are the relevant bits of config for setting up TLS between 2
+asterisk servers. With server_a registering to server_b
+
+On server_a:
+[general]
+tlsenable=yes
+tlscertfgile=/etc/asterisk/asterisk.pem
+tlscafile=/etc/ssl/ca.pem ; This is the CA file used to generate both certificates
+register => tls://100:test@192.168.0.100:5061
+
+[101]
+type=friend
+context=internal
+host=192.168.0.100 ; The host should be either IP or hostname and should
+ ; match the 'common name' field in the servers certificate
+secret=test
+dtmfmode=rfc2833
+disallow=all
+allow=ulaw
+transport=tls
+port=5061
+
+On server_b:
+[general]
+tlsenable=yes
+tlscertfgile=/etc/asterisk/asterisk.pem
+
+[100]
+type=friend
+context=internal
+host=dynamic
+secret=test
+dtmfmode=rfc2833
+disallow=all
+allow=ulaw
+;You can specify transport= and port=5061 for TLS, but its not necessary in
+;the server configuration, any type of SIP transport will work
+;transport=tls
+;port=5061
+
diff --git a/trunk/doc/smdi.txt b/trunk/doc/smdi.txt
new file mode 100644
index 000000000..a4aa6bbd6
--- /dev/null
+++ b/trunk/doc/smdi.txt
@@ -0,0 +1,25 @@
+Asterisk SMDI (Simple Message Desk Interface) integration
+---------------------------------------------------------
+
+SMDI integration is configured in smdi.conf, zaptel.conf, and voicemail.conf.
+Various characteristics of the SMDI interfaces to be used (serial ports) are
+defined in smdi.conf. SMDI integration for callerid and MWI are defined in
+zaptel.conf and voicemail.conf respectively. SMDI only works with Zaptel
+interfaces configured for FXS signalling.
+
+When SMDI is enabled and a call comes into Asterisk, the forwarding station
+number is used as the destination for the call and any callerid information
+present is used. This way you can configure your extensions.conf as follows to
+behave as a message desk.
+
+[default]
+
+exten => _XXXXXXX,1,VoiceMail(${EXTEN}|${SMDI_VM_TYPE})
+exten => _XXXXXXX,n,Hangup
+
+exten => s,1,VoiceMailMain(${CALLERID(num)})
+exten => s,n,Hangup
+
+The ${SMDI_VM_TYPE} variable will be set to u, b, or nothing depending on the
+contents of the type of SMDI message received.
+
diff --git a/trunk/doc/sms.txt b/trunk/doc/sms.txt
new file mode 100644
index 000000000..fe0ec8d85
--- /dev/null
+++ b/trunk/doc/sms.txt
@@ -0,0 +1,147 @@
+* The SMS application
+---------------------
+SMS() is an application to handles calls to/from text message capable phones and
+message centres using ETSI ES 201 912 protocol 1 FSK messaging over analog calls.
+
+Basically it allows sending and receiving of text messages over the PSTN. It is
+compatible with BT Text service in the UK and works on ISDN and PSTN lines. It is
+designed to connect to an ISDN or zap interface directly and uses FSK so would
+probably not work over any sort of compressed link (like a VoIP call using GSM codec).
+
+Typical applications include:-
+
+1. Connection to a message centre to send text messages - probably initiated via the
+ manager interface or "outgoing" directory
+2. Connection to an POTS line with an SMS capable phone to send messages - probably
+ initiated via the manager interface or "outgoing" directory
+3. Acceptance of calls from the message centre (based on CLI) and storage of
+ received messages
+4. Acceptance of calls from a POTS line with an SMS capable phone and storage of
+ received messages
+
+* Arguments to sms():
+
+- First argument is queue name
+- Second is options:
+ a: SMS() is to act as the answering side, and so send the initial FSK frame
+ s: SMS() is to act as a service centre side rather than as terminal equipment
+
+- If a third argument is specified, then SMS does not handle the call at all,
+ but takes the third argument as a destination number to send an SMS to
+- The forth argument onward is a message to be queued to the number in the
+ third argument. All this does is create the file in the me-sc directory.
+ If 's' is set then the number is the source
+ address and the message placed in the sc-me directory.
+
+All text messages are stored in /var/spool/asterisk/sms
+A log is recorded in /var/log/asterisk/sms
+
+There are two subdirectories called sc-me.<queuename> holding all
+messages from service centre to phone, and me-sc.<queuename> holding all
+messages from phone to service centre.
+
+In each directory are messages in files, one per file, using any filename not
+starting with a dot.
+
+When connected as a service centre, SMS(s) will send all messages waiting in
+the sc-me-<queuename> directory, deleting the files as it goes. Any
+received in this mode are placed in the me-sc-<queuename> directory.
+
+When connected as a client, SMS() will send all messages waiting in the
+me-sc-<queuename> directory, deleting the files as it goes. Any received in
+this mode are placed in the sc-me-<queuename> directory.
+
+Message files created by SMS() use a time stamp/reference based filename.
+
+The format of the sms file is lines that have the form of key=value
+Keys are :
+
+oa Originating Address
+ Telephone number, national number if just digits
+ Telephone number starting with + then digits for international
+ Ignored on sending messages to service centre (CLI used)
+da Destination Address
+ Telephone number, national number if just digits
+ Telephone number starting with + then digits for international
+scts Service Centre Time Stamp
+ In the format YYYY-MM-DD HH:MM:SS
+pid Protocol Identifier (decimal octet value)
+dcs Data coding scheme (decimal octet value)
+mr Message reference (decimal octet value)
+ud The message (see escaping below)
+srr 0/1 Status Report Request
+rp 0/1 Return Path
+vp mins validity period
+
+Omitted fields have default values.
+
+Note that there is special format for ud, ud# instead of ud= which is followed
+by raw hex (2 characters per octet). This is used in output where characters
+other than 10,13,32-126,128-255 are included in the data. In this case a comment (line
+starting ;) is added showing the printable characters
+
+When generating files to send to a service centre, only da and ud need be
+specified. oa is ignored.
+
+When generating files to send to a phone, only oa and ud need be specified. da is ignored.
+
+When receiving a message as a service centre, only the destination address is
+sent, so the originating address is set to the callerid.
+
+EXAMPLES
+
+The following are examples of use within the UK using BT Text SMS/landline
+service.
+
+This is a context to use with a manager script.
+
+[smsdial]
+; create and send a text message, expects number+message and
+; connect to 17094009
+exten => _X.,1,SMS(${CALLERIDNUM},,${EXTEN},${CALLERIDNAME})
+exten => _X.,n,SMS(${CALLERIDNUM})
+exten => _X.,n,Hangup
+
+The script sends
+
+ action: originate
+ callerid: message <from>
+ exten: to
+ channel: Local/17094009
+ context: smsdial
+ priority: 1
+
+You put the message as the name of the caller ID (messy, I know), the
+originating number and hence queue name as the number of the caller ID and the
+exten as the number to which the sms is to be sent. The context uses SMS to
+create the message in the queue and then SMS to communicate with 17094009 to
+actually send the message.
+
+Note that the 9 on the end of 17094009 is the sub address 9 meaning no sub
+address (BT specific). If a different digit is used then that is the sub
+address for the sending message source address (appended to the outgoing CLI
+by BT).
+
+For incoming calls you can use a context like this :-
+
+[incoming]
+exten => _XXXXXX/_8005875290,1,SMS(${EXTEN:3},a)
+exten => _XXXXXX/_8005875290,n,System(/usr/lib/asterisk/smsin ${EXTEN:3})
+exten => _XXXXXX/_80058752[0-8]0,1,SMS(${EXTEN:3}${CALLERIDNUM:8:1},a)
+exten => _XXXXXX/_80058752[0-8]0,n,System(/usr/lib/asterisk/smsin ${EXTEN>:3}${CALLERIDNUM:8:1})
+exten => _XXXXXX/_80058752[0-8]0,n,Hangup
+
+
+In this case the called number we get from BT is 6 digits (XXXXXX) and we are
+using the last 3 digits as the queue name.
+
+Priority 1 causes the SMS to be received and processed for the incoming call.
+It is from 080058752X0. The two versions handle the queue name as 3 digits (no
+sub address) or 4 digits (with sub address). In both cases, after the call a
+script (smsin) is run - this is optional, but is useful to actually processed
+the received queued SMS. In our case we email them based on the target number.
+Priority 3 hangs up.
+
+If using the CAPI drivers they send the right CLI and so the _800... would be
+_0800...
+
diff --git a/trunk/doc/snmp.txt b/trunk/doc/snmp.txt
new file mode 100644
index 000000000..f1667ee15
--- /dev/null
+++ b/trunk/doc/snmp.txt
@@ -0,0 +1,39 @@
+Asterisk SNMP Support
+---------------------
+
+Rudimentary support for SNMP access to Asterisk is available. To build
+this, one needs to have Net-SNMP development headers and libraries on
+the build system, including any libraries Net-SNMP depends on.
+
+Note that on some (many?) Linux-distributions the dependency list in
+the net-snmp-devel list is not complete, and additional RPMs will need
+to be installed. This is typically seen as attempts to build res_snmp
+as net-snmp-devel is available, but then fails to find certain
+libraries. The packages may include the following:
+ * bzip2-devel
+ * lm_sensors-devel
+ * newt-devel
+
+SNMP support comes in two varieties -- as a sub-agent to a running SNMP
+daemon using the AgentX protocol, or as a full standalone agent. If
+you wish to run a full standalone agent, Asterisk must run as root in
+order to bind to port 161.
+
+Configuring access when running as a full agent is something that is
+left as an exercise to the reader.
+
+To enable access to the Asterisk SNMP subagent from a master SNMP
+daemon, one will need to enable AgentX support, and also make sure that
+Asterisk will be able to access the Unix domain socket. One way of
+doing this is to add the following to /etc/snmp/snmpd.conf:
+
+ # Enable AgentX support
+ master agentx
+
+ # Set permissions on AgentX socket and containing
+ # directory such that process in group 'asterisk'
+ # will be able to connect
+ agentXPerms 0660 0550 nobody asterisk
+
+This assumes that you run Asterisk under group 'asterisk' (and does
+not care what user you run as).
diff --git a/trunk/doc/speechrec.txt b/trunk/doc/speechrec.txt
new file mode 100644
index 000000000..1e5bf6f49
--- /dev/null
+++ b/trunk/doc/speechrec.txt
@@ -0,0 +1,295 @@
+The Asterisk Speech Recognition API
+===================================
+
+The generic speech recognition engine is implemented in the res_speech.so module.
+This module connects through the API to speech recognition software, that is
+not included in the module.
+
+To use the API, you must load the res_speech.so module before any connectors.
+For your convenience, there is a preload line commented out in the modules.conf
+sample file.
+
+* Dialplan Applications:
+------------------------
+
+The dialplan API is based around a single speech utilities application file,
+which exports many applications to be used for speech recognition. These include an
+application to prepare for speech recognition, activate a grammar, and play back a
+sound file while waiting for the person to speak. Using a combination of these applications
+you can easily make a dialplan use speech recognition without worrying about what
+speech recognition engine is being used.
+
+- SpeechCreate(Engine Name):
+
+This application creates information to be used by all the other applications.
+It must be called before doing any speech recognition activities such as activating a
+grammar. It takes the engine name to use as the argument, if not specified the default
+engine will be used.
+
+If an error occurs are you are not able to create an object, the variable ERROR will be
+set to 1. You can then exit your speech recognition specific context and play back an
+error message, or resort to a DTMF based IVR.
+
+- SpeechLoadGrammar(Grammar Name|Path):
+
+Loads grammar locally on a channel. Note that the grammar is only available as long as the
+channel exists, and you must call SpeechUnloadGrammar before all is done or you may cause a
+memory leak. First argument is the grammar name that it will be loaded as and second
+argument is the path to the grammar.
+
+- SpeechUnloadGrammar(Grammar Name):
+
+Unloads a locally loaded grammar and frees any memory used by it. The only argument is the
+name of the grammar to unload.
+
+- SpeechActivateGrammar(Grammar Name):
+
+This activates the specified grammar to be recognized by the engine. A grammar tells the
+speech recognition engine what to recognize, and how to portray it back to you in the
+dialplan. The grammar name is the only argument to this application.
+
+- SpeechStart():
+
+Tell the speech recognition engine that it should start trying to get results from audio
+being fed to it. This has no arguments.
+
+- SpeechBackground(Sound File|Timeout):
+
+This application plays a sound file and waits for the person to speak. Once they start
+speaking playback of the file stops, and silence is heard. Once they stop talking the
+processing sound is played to indicate the speech recognition engine is working. Note it is
+possible to have more then one result. The first argument is the sound file and the second is the
+timeout. Note the timeout will only start once the sound file has stopped playing.
+
+- SpeechDeactivateGrammar(Grammar Name):
+
+This deactivates the specified grammar so that it is no longer recognized. The
+only argument is the grammar name to deactivate.
+
+- SpeechProcessingSound(Sound File):
+
+This changes the processing sound that SpeechBackground plays back when the speech
+recognition engine is processing and working to get results. It takes the sound file as the
+only argument.
+
+- SpeechDestroy():
+
+This destroys the information used by all the other speech recognition applications.
+If you call this application but end up wanting to recognize more speech, you must call
+SpeechCreate again before calling any other application. It takes no arguments.
+
+* Getting Result Information:
+-----------------------------
+
+The speech recognition utilities module exports several dialplan functions that you can use to
+examine results.
+
+- ${SPEECH(status)}:
+
+Returns 1 if SpeechCreate has been called. This uses the same check that applications do to see if a
+speech object is setup. If it returns 0 then you know you can not use other speech applications.
+
+- ${SPEECH(spoke)}:
+
+Returns 1 if the speaker spoke something, or 0 if they were silent.
+
+- ${SPEECH(results)}:
+
+Returns the number of results that are available.
+
+- ${SPEECH_SCORE(result number)}:
+
+Returns the score of a result.
+
+- ${SPEECH_TEXT(result number)}:
+
+Returns the recognized text of a result.
+
+- ${SPEECH_GRAMMAR(result number)}:
+
+Returns the matched grammar of the result.
+
+- SPEECH_ENGINE(name)=value
+
+Sets a speech engine specific attribute.
+
+* Dialplan Flow:
+-----------------
+
+1. Create a speech recognition object using SpeechCreate()
+2. Activate your grammars using SpeechActivateGrammar(Grammar Name)
+3. Call SpeechStart() to indicate you are going to do speech recognition immediately
+4. Play back your audio and wait for recognition using SpeechBackground(Sound File|Timeout)
+5. Check the results and do things based on them
+6. Deactivate your grammars using SpeechDeactivateGrammar(Grammar Name)
+7. Destroy your speech recognition object using SpeechDestroy()
+
+* Dialplan Examples:
+
+This is pretty cheeky in that it does not confirmation of results. As well the way the
+grammar is written it returns the person's extension instead of their name so we can
+just do a Goto based on the result text.
+
+- Grammar: company-directory.gram
+
+#ABNF 1.0;
+language en-US;
+mode voice;
+tag-format <lumenvox/1.0>;
+root $company_directory;
+
+$josh = ((Joshua | Josh) [Colp]):"6066";
+$mark = (Mark [Spencer] | Markster):"4569";
+$kevin = (Kevin [Fleming]):"2567";
+
+$company_directory = ($josh | $mark | $kevin) { $ = $$ };
+
+- Dialplan logic
+
+ [dial-by-name]
+ exten => s,1,SpeechCreate()
+ exten => s,2,SpeechActivateGrammar(company-directory)
+ exten => s,3,SpeechStart()
+ exten => s,4,SpeechBackground(who-would-you-like-to-dial)
+ exten => s,5,SpeechDeactivateGrammar(company-directory)
+ exten => s,6,Goto(internal-extensions-${SPEECH_TEXT(0)})
+
+- Useful Dialplan Tidbits:
+
+A simple macro that can be used for confirm of a result. Requires some sound files.
+ARG1 is equal to the file to play back after "I heard..." is played.
+
+ [macro-speech-confirm]
+ exten => s,1,SpeechActivateGrammar(yes_no)
+ exten => s,2,Set(OLDTEXT0=${SPEECH_TEXT(0)})
+ exten => s,3,Playback(heard)
+ exten => s,4,Playback(${ARG1})
+ exten => s,5,SpeechStart()
+ exten => s,6,SpeechBackground(correct)
+ exten => s,7,Set(CONFIRM=${SPEECH_TEXT(0)})
+ exten => s,8,GotoIf($["${SPEECH_TEXT(0)}" = "1"]?9:10)
+ exten => s,9,Set(CONFIRM=yes)
+ exten => s,10,Set(CONFIRMED=${OLDTEXT0})
+ exten => s,11,SpeechDeactivateGrammar(yes_no)
+
+* The Asterisk Speech Recognition C API
+---------------------------------------
+
+The module res_speech.so exports a C based API that any developer can use to speech
+recognize enable their application. The API gives greater control, but requires the
+developer to do more on their end in comparison to the dialplan speech utilities.
+
+For all API calls that return an integer value, a non-zero value indicates an error has occurred.
+
+- Creating a speech structure:
+
+ struct ast_speech *ast_speech_new(char *engine_name, int format)
+
+ struct ast_speech *speech = ast_speech_new(NULL, AST_FORMAT_SLINEAR);
+
+This will create a new speech structure that will be returned to you. The speech recognition
+engine name is optional and if NULL the default one will be used. As well for now format should
+always be AST_FORMAT_SLINEAR.
+
+- Activating a grammar:
+
+ int ast_speech_grammar_activate(struct ast_speech *speech, char *grammar_name)
+
+ res = ast_speech_grammar_activate(speech, "yes_no");
+
+This activates the specified grammar on the speech structure passed to it.
+
+- Start recognizing audio:
+
+ void ast_speech_start(struct ast_speech *speech)
+
+ ast_speech_start(speech);
+
+This essentially tells the speech recognition engine that you will be feeding audio to it from
+then on. It MUST be called every time before you start feeding audio to the speech structure.
+
+- Send audio to be recognized:
+
+ int ast_speech_write(struct ast_speech *speech, void *data, int len)
+
+ res = ast_speech_write(speech, fr->data, fr->datalen);
+
+This writes audio to the speech structure that will then be recognized. It must be written
+signed linear only at this time. In the future other formats may be supported.
+
+- Checking for results:
+
+The way the generic speech recognition API is written is that the speech structure will
+undergo state changes to indicate progress of recognition. The states are outlined below:
+
+ AST_SPEECH_STATE_NOT_READY - The speech structure is not ready to accept audio
+ AST_SPEECH_STATE_READY - You may write audio to the speech structure
+ AST_SPEECH_STATE_WAIT - No more audio should be written, and results will be available soon.
+ AST_SPEECH_STATE_DONE - Results are available and the speech structure can only be used again by
+ calling ast_speech_start
+
+It is up to you to monitor these states. Current state is available via a variable on the speech
+structure. (state)
+
+- Knowing when to stop playback:
+
+If you are playing back a sound file to the user and you want to know when to stop play back because the
+individual started talking use the following.
+
+ ast_test_flag(speech, AST_SPEECH_QUIET) - This will return a positive value when the person has started talking.
+
+- Getting results:
+
+ struct ast_speech_result *ast_speech_results_get(struct ast_speech *speech)
+
+ struct ast_speech_result *results = ast_speech_results_get(speech);
+
+This will return a linked list of result structures. A result structure looks like the following:
+
+ struct ast_speech_result {
+ char *text; /*!< Recognized text */
+ int score; /*!< Result score */
+ char *grammar; /*!< Matched grammar */
+ struct ast_speech_result *next; /*!< List information */
+ };
+
+- Freeing a set of results:
+
+ int ast_speech_results_free(struct ast_speech_result *result)
+
+ res = ast_speech_results_free(results);
+
+This will free all results on a linked list. Results MAY NOT be used as the memory will have been freed.
+
+- Deactivating a grammar:
+
+ int ast_speech_grammar_deactivate(struct ast_speech *speech, char *grammar_name)
+
+ res = ast_speech_grammar_deactivate(speech, "yes_no");
+
+This deactivates the specified grammar on the speech structure.
+
+- Destroying a speech structure:
+
+ int ast_speech_destroy(struct ast_speech *speech)
+
+ res = ast_speech_destroy(speech);
+
+This will free all associated memory with the speech structure and destroy it with the speech recognition engine.
+
+- Loading a grammar on a speech structure:
+
+ int ast_speech_grammar_load(struct ast_speech *speech, char *grammar_name, char *grammar)
+
+ res = ast_speech_grammar_load(speech, "builtin:yes_no", "yes_no");
+
+- Unloading a grammar on a speech structure:
+
+If you load a grammar on a speech structure it is preferred that you unload it as well,
+or you may cause a memory leak. Don't say I didn't warn you.
+
+ int ast_speech_grammar_unload(struct ast_speech *speech, char *grammar_name)
+
+ res = ast_speech_grammar_unload(speech, "yes_no");
+
+This unloads the specified grammar from the speech structure.
diff --git a/trunk/doc/ss7.txt b/trunk/doc/ss7.txt
new file mode 100644
index 000000000..632035df0
--- /dev/null
+++ b/trunk/doc/ss7.txt
@@ -0,0 +1,113 @@
+("Taken from the README in libss7")
+Tested Switches:
+================
+Siemens EWSD - (ITU style) MTP2 and MTP3 comes up, ISUP inbound and outbound calls work as well.
+DTI DXC 4K - (ANSI style) 56kbps link, MTP2 and MTP3 come up, ISUP inbound and outbound calls work as well.
+Huawei M800 - (ITU style) MTP2 and MTP3 comes up, ISUP National, International inbound and outbound calls work as well, CallerID presentation&screening work.
+and MORE~!
+
+Thanks:
+=======
+Mark Spencer, for writing Asterisk and libpri and being such a great friend and boss.
+
+Luciano Ramos, for donating a link in getting the first "real" ITU switch working.
+
+Collin Rose and John Lodden, John for introducing me to Collin, and Collin for the first
+"real" ANSI link and for holding my hand through the remaining changes that had to be
+done for ANSI switches.
+
+To Use:
+=======
+In order to use libss7, you must get at least the following versions of Zaptel and Asterisk:
+Zaptel: 1.4.x
+libss7: trunk (currently, there *only* is a trunk release).
+Asterisk: trunk
+
+You must then do a `make; make install` in each of the directories that you installed
+in the given order (Zaptel first, libss7 second, and Asterisk last).
+
+NOTE: In order to check out the code, you must have the subversion client installed. This
+is how to check them out from the public subversion server.
+
+These are the commands you would type to install them:
+
+`svn co http://svn.digium.com/svn/zaptel/branches/1.4 zaptel-1.4`
+`cd zaptel-1.4`
+`make; make install`
+
+`svn co http://svn.digium.com/svn/libss7/trunk libss7-trunk`
+`cd libss7-trunk`
+`make; make install`
+
+`svn co http://svn.digium.com/svn/asterisk/trunk asterisk-trunk`
+`cd asterisk-trunk`
+`./configure; make; make install;`
+
+This should build Zaptel, libss7, and Asterisk with SS7 support.
+
+In the past, there was a special asterisk-ss7 branch to use which contained the SS7 code.
+That code has been merged back into the trunk version of Asterisk, and the old asterisk-ss7
+branch has been deprecated and removed. If you are still using the asterisk-ss7 branch, it
+will not work against the current version of libss7, and you should switch to asterisk-trunk
+instead.
+
+CONFIGURATION:
+In zaptel.conf, your signalling channel(s) should be a "dchan" and your bearers should
+be set as "bchan".
+
+In the asterisk-ss7 branch, there is a sample zapata.conf that is installed which
+contains sample configuration for setting up an E1 link.
+
+In brief, here is a simple ss7 linkset setup:
+
+signalling = ss7
+ss7type = itu ; or ansi if you are using an ANSI link
+
+linkset = 1 ; Pick a number for your linkset identifier in zapata.conf
+
+pointcode = 28 ; The decimal form of your point code. If you are using an
+ ; ANSI linkset, you can use the xxx-xxx-xxx notation for
+ ; specifying your linkset pointcode.
+adjpointcode = 2 ; The point code of the switch adjacent to your linkset
+
+defaultdpc = 3 ; The point code of the switch you want to send your ISUP
+ ; traffic to. A lot of the time, this is the same as your
+ ; adjpointcode.
+
+; Now we configure our Bearer channels (CICs)
+
+cicbeginswith = 1 ; Number to start counting the CICs from. So if Zap/1 to
+ ; Zap/15 are CICs 1-15, you would set this to 1 before you
+ ; declare channel=1-15
+
+channel=1-15 ; Use Zap/1-15 and assign them to CICs 1-15
+
+cicbeginswith = 17 ; Now for Zap/17 to Zap/31, they are CICs 17-31 so we initialize
+ ; cicbeginswith to 17 before we declare those channels
+
+channel = 17-31 ; This assigns CICs 17-31 to channels 17-31
+
+sigchan = 16 ; This is where you declare which Zap channel is your signalling
+ ; channel. In our case it is Zap/16. You can add redundant
+ ; signalling channels by adding additional sigchan= lines.
+
+; If we want an alternate redundant signalling channel add this
+
+sigchan = 48 ; This would put two signalling channels in our linkset, one at
+ ; Zap/16 and one at Zap/48 which both would be used to send/receive
+ ; ISUP traffic.
+
+; End of zapata.conf
+
+This is how a basic linkset is setup. For more detailed zapata.conf SS7 config information
+as well as other options available for that file, see the default zapata.conf that comes
+with the samples in asterisk. If you would like, you can do a `make samples` in your
+asterisk-trunk directory and it will install a sample zapata.conf for you that contains
+more information about SS7 setup.
+
+For more information, please use the Asterisk-ss7 or Asterisk-dev mailing
+lists (I monitor them regularly) or email me directly.
+
+Matthew Fredrickson
+creslin@digium.com
+
diff --git a/trunk/doc/tex/Makefile b/trunk/doc/tex/Makefile
new file mode 100644
index 000000000..70ad5f14a
--- /dev/null
+++ b/trunk/doc/tex/Makefile
@@ -0,0 +1,44 @@
+include ../../makeopts
+
+pdf: asterisk.pdf
+
+asterisk.pdf: $(wildcard *.tex)
+ifeq ($(findstring rubber,$(RUBBER)),)
+ @echo "**********************************************"
+ @echo "** You must install the \"rubber\" tool ***"
+ @echo "** to generate the Asterisk reference PDF. ***"
+ @echo "**********************************************"
+else
+ @echo "**********************************************"
+ @echo "** The Asterisk reference PDF will now be ***"
+ @echo "** generated. When complete, it will be ***"
+ @echo "** located at asterisk.pdf. ***"
+ @echo "**********************************************"
+ifneq ($(findstring kpsewhich,$(KPATHSEA)),)
+ifeq ($(findstring fncychap.sty,$(shell find `$(KPATHSEA) --expand-braces='$$(TEXMF)'| tr -d \! | sed 's/:/ /g'` -name fncychap.sty -print)),)
+ @echo
+ @echo "WARNING: The fncychap.sty document was not found"
+ @echo "On Ubuntu, install the texlive-latex-extra package."
+ @echo
+ @exit
+endif
+endif
+ @cp asterisk.tex asterisk.tex.orig
+ sed -i -e 's/ASTERISKVERSION/$(shell echo $(ASTERISKVERSION) | sed -e 's/\//\\\//g')/' asterisk.tex
+ @$(RUBBER) --pdf asterisk.tex
+ @mv asterisk.tex.orig asterisk.tex
+endif
+
+html:
+ @echo "**********************************************"
+ @echo "** The Asterisk reference HTML will now be ***"
+ @echo "** generated. When complete, it will be ***"
+ @echo "** located in the asterisk/ directory. ***"
+ @echo "** Note that the latex2html tool is ***"
+ @echo "** required for this to work. ***"
+ @echo "**********************************************"
+ @cp asterisk.tex asterisk.tex.orig
+ @sed -i -e 's/ASTERISKVERSION/$(ASTERISKVERSION)/' asterisk.tex
+ @latex2html asterisk.tex
+ @mv asterisk.tex.orig asterisk.tex
+
diff --git a/trunk/doc/tex/README.txt b/trunk/doc/tex/README.txt
new file mode 100644
index 000000000..460d330a0
--- /dev/null
+++ b/trunk/doc/tex/README.txt
@@ -0,0 +1,24 @@
+Asterisk Reference Documentation
+--------------------------------
+
+1) To generate a PDF from this documentation, you will need the rubber tool,
+ and all of its dependencies. The web site for this tool is:
+
+ http://www.pps.jussieu.fr/~beffara/soft/rubber/
+
+ Then, once this tool is installed, running "make pdf" will generate
+ the PDF automatically using this tool. The result will be asterisk.pdf.
+
+ NOTE: After installing rubber, you will need to re-run the top level
+ configure script. It checks to see if rubber is installed, so that the
+ asterisk.pdf Makefile target can produce a useful error message when it is
+ not installed.
+
+2) To generate HTML from this documentation, you will need the latex2html tool,
+ and all of its dependencies. The web site for this tool is:
+
+ http://www.latex2html.org/
+
+ Then, once this tool is installed, running "make html" will generate the
+ HTML documentation. The result will be an asterisk directory full of
+ HTML files.
diff --git a/trunk/doc/tex/ael.tex b/trunk/doc/tex/ael.tex
new file mode 100644
index 000000000..4d9fa2433
--- /dev/null
+++ b/trunk/doc/tex/ael.tex
@@ -0,0 +1,1305 @@
+\section{Introduction}
+
+AEL is a specialized language intended purely for
+describing Asterisk dial plans.
+
+The current version was written by Steve Murphy, and is a rewrite of
+the original version.
+
+This new version further extends AEL, and
+provides more flexible syntax, better error messages, and some missing
+functionality.
+
+AEL is really the merger of 4 different 'languages', or syntaxes:
+
+\begin{itemize}
+ \item The first and most obvious is the AEL syntax itself. A BNF is
+ provided near the end of this document.
+
+ \item The second syntax is the Expression Syntax, which is normally
+ handled by Asterisk extension engine, as expressions enclosed in
+ \$[...]. The right hand side of assignments are wrapped in \$[ ... ]
+ by AEL, and so are the if and while expressions, among others.
+
+ \item The third syntax is the Variable Reference Syntax, the stuff
+ enclosed in \$\{..\} curly braces. It's a bit more involved than just
+ putting a variable name in there. You can include one of dozens of
+ 'functions', and their arguments, and there are even some string
+ manipulation notation in there.
+
+ \item The last syntax that underlies AEL, and is not used
+ directly in AEL, is the Extension Language Syntax. The
+ extension language is what you see in extensions.conf, and AEL
+ compiles the higher level AEL language into extensions and
+ priorities, and passes them via function calls into
+ Asterisk. Embedded in this language is the Application/AGI
+ commands, of which one application call per step, or priority
+ can be made. You can think of this as a "macro assembler"
+ language, that AEL will compile into.
+\end{itemize}
+
+Any programmer of AEL should be familiar with it's syntax, of course,
+as well as the Expression syntax, and the Variable syntax.
+
+
+\section{Asterisk in a Nutshell}
+
+Asterisk acts as a server. Devices involved in telephony, like Zapata
+cards, or Voip phones, all indicate some context that should be
+activated in their behalf. See the config file formats for IAX, SIP,
+zapata.conf, etc. They all help describe a device, and they all
+specify a context to activate when somebody picks up a phone, or a
+call comes in from the phone company, or a voip phone, etc.
+
+\subsection{Contexts}
+
+Contexts are a grouping of extensions.
+
+Contexts can also include other contexts. Think of it as a sort of
+merge operation at runtime, whereby the included context's extensions
+are added to the contexts making the inclusion.
+
+\subsection{Extensions and priorities}
+
+A Context contains zero or more Extensions. There are several
+predefined extensions. The "s" extension is the "start" extension, and
+when a device activates a context the "s" extension is the one that is
+going to be run. Other extensions are the timeout "t" extension, the
+invalid response, or "i" extension, and there's a "fax" extension. For
+instance, a normal call will activate the "s" extension, but an
+incoming FAX call will come into the "fax" extension, if it
+exists. (BTW, asterisk can tell it's a fax call by the little "beep"
+that the calling fax machine emits every so many seconds.).
+
+Extensions contain several priorities, which are individual
+instructions to perform. Some are as simple as setting a variable to a
+value. Others are as complex as initiating the Voicemail application,
+for instance. Priorities are executed in order.
+
+When the 's" extension completes, asterisk waits until the timeout for
+a response. If the response matches an extension's pattern in the
+context, then control is transferred to that extension. Usually the
+responses are tones emitted when a user presses a button on their
+phone. For instance, a context associated with a desk phone might not
+have any "s" extension. It just plays a dialtone until someone starts
+hitting numbers on the keypad, gather the number, find a matching
+extension, and begin executing it. That extension might Dial out over
+a connected telephone line for the user, and then connect the two
+lines together.
+
+The extensions can also contain "goto" or "jump" commands to skip to
+extensions in other contexts. Conditionals provide the ability to
+react to different stimuli, and there you have it.
+
+\subsection{Macros}
+
+Think of a macro as a combination of a context with one nameless
+extension, and a subroutine. It has arguments like a subroutine
+might. A macro call can be made within an extension, and the
+individual statements there are executed until it ends. At this point,
+execution returns to the next statement after the macro call. Macros
+can call other macros. And they work just like function calls.
+
+\subsection{Applications}
+
+Application calls, like "Dial()", or "Hangup()", or "Answer()", are
+available for users to use to accomplish the work of the
+dialplan. There are over 145 of them at the moment this was written,
+and the list grows as new needs and wants are uncovered. Some
+applications do fairly simple things, some provide amazingly complex
+services.
+
+Hopefully, the above objects will allow you do anything you need to in
+the Asterisk environment!
+
+\section{Getting Started}
+
+The AEL parser (pbx\_ael.so) is completely separate from the module
+that parses extensions.conf (pbx\_config.so). To use AEL, the only
+thing that has to be done is the module pbx\_ael.so must be loaded by
+Asterisk. This will be done automatically if using 'autoload=yes' in
+\path{/etc/asterisk/modules.conf}. When the module is loaded, it will look
+for 'extensions.ael' in \path{/etc/asterisk/}. extensions.conf and
+extensions.ael can be used in conjunction with
+each other if that is what is desired. Some users may want to keep
+extensions.conf for the features that are configured in the 'general'
+section of extensions.conf.
+
+To reload extensions.ael, the following command can be issued at the
+CLI:
+
+ *CLI> ael reload
+
+\section{Debugging}
+
+Right at this moment, the following commands are available, but do
+nothing:
+
+Enable AEL contexts debug
+
+ *CLI$>$ ael debug contexts
+
+Enable AEL macros debug
+
+ *CLI$>$ ael debug macros
+
+Enable AEL read debug
+
+ *CLI$>$ ael debug read
+
+Enable AEL tokens debug
+
+ *CLI$>$ ael debug tokens
+
+Disable AEL debug messages
+
+ *CLI$>$ ael no debug
+
+If things are going wrong in your dialplan, you can use the following
+facilities to debug your file:
+
+1. The messages log in \path{/var/log/asterisk}. (from the checks done at load time).
+2. the "show dialplan" command in asterisk
+3. the standalone executable, "aelparse" built in the utils/ dir in the source.
+
+
+\section{About "aelparse"}
+
+You can use the "aelparse" program to check your extensions.ael
+file before feeding it to asterisk. Wouldn't it be nice to eliminate
+most errors before giving the file to asterisk?
+
+aelparse is compiled in the utils directory of the asterisk release.
+It isn't installed anywhere (yet). You can copy it to your favorite
+spot in your PATH.
+
+aelparse has two optional arguments:
+
+\begin{itemize}
+ \item -d
+ \begin{itemize}
+ \item Override the normal location of the config file dir, (usually
+ \path{/etc/asterisk}), and use the current directory instead as the
+ config file dir. Aelparse will then expect to find the file
+ "./extensions.ael" in the current directory, and any included
+ files in the current directory as well.
+ \end{itemize}
+ \item -n
+ \begin{itemize}
+ \item don't show all the function calls to set priorities and contexts
+ within asterisk. It will just show the errors and warnings from
+ the parsing and semantic checking phases.
+ \end{itemize}
+\end{itemize}
+
+\section{General Notes about Syntax}
+
+Note that the syntax and style are now a little more free-form. The
+opening '{' (curly-braces) do not have to be on the same line as the
+keyword that precedes them. Statements can be split across lines, as
+long as tokens are not broken by doing so. More than one statement can
+be included on a single line. Whatever you think is best!
+
+You can just as easily say,
+
+\begin{astlisting}
+\begin{verbatim}
+if(${x}=1) { NoOp(hello!); goto s,3; } else { NoOp(Goodbye!); goto s,12; }
+\end{verbatim}
+\end{astlisting}
+as you can say:
+\begin{astlisting}
+\begin{verbatim}
+if(${x}=1)
+{
+ NoOp(hello!);
+ goto s,3;
+}
+else
+{
+ NoOp(Goodbye!);
+ goto s,12;
+}
+\end{verbatim}
+\end{astlisting}
+
+or:
+
+\begin{astlisting}
+\begin{verbatim}
+if(${x}=1) {
+ NoOp(hello!);
+ goto s,3;
+} else {
+ NoOp(Goodbye!);
+ goto s,12;
+}
+\end{verbatim}
+\end{astlisting}
+
+or:
+
+\begin{astlisting}
+\begin{verbatim}
+if (${x}=1) {
+ NoOp(hello!); goto s,3;
+} else {
+ NoOp(Goodbye!); goto s,12;
+}
+\end{verbatim}
+\end{astlisting}
+
+\section{Keywords}
+
+The AEL keywords are case-sensitive. If an application name and a
+keyword overlap, there is probably good reason, and you should
+consider replacing the application call with an AEL statement. If you
+do not wish to do so, you can still use the application, by using a
+capitalized letter somewhere in its name. In the Asterisk extension
+language, application names are NOT case-sensitive.
+
+The following are keywords in the AEL language:
+\begin{itemize}
+ \item abstract
+ \item context
+ \item macro
+ \item globals
+ \item ignorepat
+ \item switch
+ \item if
+ \item ifTime
+ \item else
+ \item random
+ \item goto
+ \item jump
+ \item local
+ \item return
+ \item break
+ \item continue
+ \item regexten
+ \item hint
+ \item for
+ \item while
+ \item case
+ \item pattern
+ \item default NOTE: the "default" keyword can be used as a context name,
+ for those who would like to do so.
+ \item catch
+ \item switches
+ \item eswitches
+ \item includes
+\end{itemize}
+
+
+\section{Procedural Interface and Internals}
+
+AEL first parses the extensions.ael file into a memory structure representing the file.
+The entire file is represented by a tree of "pval" structures linked together.
+
+This tree is then handed to the semantic check routine.
+
+Then the tree is handed to the compiler.
+
+After that, it is freed from memory.
+
+A program could be written that could build a tree of pval structures, and
+a pretty printing function is provided, that would dump the data to a file,
+or the tree could be handed to the compiler to merge the data into the
+asterisk dialplan. The modularity of the design offers several opportunities
+for developers to simplify apps to generate dialplan data.
+
+
+\subsection{AEL version 2 BNF}
+
+(hopefully, something close to bnf).
+
+First, some basic objects
+
+\begin{astlisting}
+\begin{verbatim}
+------------------------
+<word> a lexical token consisting of characters matching this pattern: [-a-zA-Z0-9"_/.\<\>\*\+!$#\[\]][-a-zA-Z0-9"_/.!\*\+\<\>\{\}$#\[\]]*
+
+<word3-list> a concatenation of up to 3 <word>s.
+
+<collected-word> all characters encountered until the character that follows the <collected-word> in the grammar.
+-------------------------
+
+<file> :== <objects>
+
+<objects> :== <object>
+ | <objects> <object>
+
+
+<object> :== <context>
+ | <macro>
+ | <globals>
+ | ';'
+
+
+<context> :== 'context' <word> '{' <elements> '}'
+ | 'context' <word> '{' '}'
+ | 'context' 'default' '{' <elements> '}'
+ | 'context' 'default' '{' '}'
+ | 'abstract' 'context' <word> '{' <elements> '}'
+ | 'abstract' 'context' <word> '{' '}'
+ | 'abstract' 'context' 'default' '{' <elements> '}'
+ | 'abstract' 'context' 'default' '{' '}'
+
+
+<macro> :== 'macro' <word> '(' <arglist> ')' '{' <macro_statements> '}'
+ | 'macro' <word> '(' <arglist> ')' '{' '}'
+ | 'macro' <word> '(' ')' '{' <macro_statements> '}'
+ | 'macro' <word> '(' ')' '{' '}'
+
+
+<globals> :== 'globals' '{' <global_statements> '}'
+ | 'globals' '{' '}'
+
+
+<global_statements> :== <global_statement>
+ | <global_statements> <global_statement>
+
+
+<global_statement> :== <word> '=' <collected-word> ';'
+
+
+<arglist> :== <word>
+ | <arglist> ',' <word>
+
+
+<elements> :== <element>
+ | <elements> <element>
+
+
+<element> :== <extension>
+ | <includes>
+ | <switches>
+ | <eswitches>
+ | <ignorepat>
+ | <word> '=' <collected-word> ';'
+ | 'local' <word> '=' <collected-word> ';'
+ | ';'
+
+
+<ignorepat> :== 'ignorepat' '=>' <word> ';'
+
+
+<extension> :== <word> '=>' <statement>
+ | 'regexten' <word> '=>' <statement>
+ | 'hint' '(' <word3-list> ')' <word> '=>' <statement>
+ | 'regexten' 'hint' '(' <word3-list> ')' <word> '=>' <statement>
+
+
+<statements> :== <statement>
+ | <statements> <statement>
+
+<if_head> :== 'if' '(' <collected-word> ')'
+
+<random_head> :== 'random' '(' <collected-word> ')'
+
+<ifTime_head> :== 'ifTime' '(' <word3-list> ':' <word3-list> ':' <word3-list> '|' <word3-list> '|' <word3-list> '|' <word3-list> ')'
+ | 'ifTime' '(' <word> '|' <word3-list> '|' <word3-list> '|' <word3-list> ')'
+
+
+<word3-list> :== <word>
+ | <word> <word>
+ | <word> <word> <word>
+
+<switch_head> :== 'switch' '(' <collected-word> ')' '{'
+
+
+<statement> :== '{' <statements> '}'
+ | <word> '=' <collected-word> ';'
+ | 'local' <word> '=' <collected-word> ';'
+ | 'goto' <target> ';'
+ | 'jump' <jumptarget> ';'
+ | <word> ':'
+ | 'for' '(' <collected-word> ';' <collected-word> ';' <collected-word> ')' <statement>
+ | 'while' '(' <collected-word> ')' <statement>
+ | <switch_head> '}'
+ | <switch_head> <case_statements> '}'
+ | '&' macro_call ';'
+ | <application_call> ';'
+ | <application_call> '=' <collected-word> ';'
+ | 'break' ';'
+ | 'return' ';'
+ | 'continue' ';'
+ | <random_head> <statement>
+ | <random_head> <statement> 'else' <statement>
+ | <if_head> <statement>
+ | <if_head> <statement> 'else' <statement>
+ | <ifTime_head> <statement>
+ | <ifTime_head> <statement> 'else' <statement>
+ | ';'
+
+<target> :== <word>
+ | <word> '|' <word>
+ | <word> '|' <word> '|' <word>
+ | 'default' '|' <word> '|' <word>
+ | <word> ',' <word>
+ | <word> ',' <word> ',' <word>
+ | 'default' ',' <word> ',' <word>
+
+<jumptarget> :== <word>
+ | <word> ',' <word>
+ | <word> ',' <word> '@' <word>
+ | <word> '@' <word>
+ | <word> ',' <word> '@' 'default'
+ | <word> '@' 'default'
+
+<macro_call> :== <word> '(' <eval_arglist> ')'
+ | <word> '(' ')'
+
+<application_call_head> :== <word> '('
+
+<application_call> :== <application_call_head> <eval_arglist> ')'
+ | <application_call_head> ')'
+
+<eval_arglist> :== <collected-word>
+ | <eval_arglist> ',' <collected-word>
+ | /* nothing */
+ | <eval_arglist> ',' /* nothing */
+
+<case_statements> :== <case_statement>
+ | <case_statements> <case_statement>
+
+
+<case_statement> :== 'case' <word> ':' <statements>
+ | 'default' ':' <statements>
+ | 'pattern' <word> ':' <statements>
+ | 'case' <word> ':'
+ | 'default' ':'
+ | 'pattern' <word> ':'
+
+<macro_statements> :== <macro_statement>
+ | <macro_statements> <macro_statement>
+
+<macro_statement> :== <statement>
+ | 'catch' <word> '{' <statements> '}'
+
+<switches> :== 'switches' '{' <switchlist> '}'
+ | 'switches' '{' '}'
+
+<eswitches> :== 'eswitches' '{' <switchlist> '}'
+ | 'eswitches' '{' '}'
+
+<switchlist> :== <word> ';'
+ | <switchlist> <word> ';'
+
+<includeslist> :== <includedname> ';'
+ | <includedname> '|' <word3-list> ':' <word3-list> ':' <word3-list> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+ | <includedname> '|' <word> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+ | <includeslist> <includedname> ';'
+ | <includeslist> <includedname> '|' <word3-list> ':' <word3-list> ':' <word3-list> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+ | <includeslist> <includedname> '|' <word> '|' <word3-list> '|' <word3-list> '|' <word3-list> ';'
+
+<includedname> :== <word>
+ | 'default'
+
+<includes> :== 'includes' '{' <includeslist> '}'
+ | 'includes' '{' '}'
+\end{verbatim}
+\end{astlisting}
+
+\section{AEL Example USAGE}
+
+\subsection{Comments}
+
+Comments begin with // and end with the end of the line.
+
+Comments are removed by the lexical scanner, and will not be
+recognized in places where it is busy gathering expressions to wrap in
+\$[] , or inside application call argument lists. The safest place to put
+comments is after terminating semicolons, or on otherwise empty lines.
+
+
+\subsection{Context}
+
+Contexts in AEL represent a set of extensions in the same way that
+they do in extensions.conf.
+\begin{astlisting}
+\begin{verbatim}
+context default {
+
+}
+\end{verbatim}
+\end{astlisting}
+
+A context can be declared to be "abstract", in which case, this
+declaration expresses the intent of the writer, that this context will
+only be included by another context, and not "stand on its own". The
+current effect of this keyword is to prevent "goto " statements from
+being checked.
+\begin{astlisting}
+\begin{verbatim}
+abstract context longdist {
+ _1NXXNXXXXXX => NoOp(generic long distance dialing actions in the US);
+}
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Extensions}
+
+To specify an extension in a context, the following syntax is used. If
+more than one application is be called in an extension, they can be
+listed in order inside of a block.
+\begin{astlisting}
+\begin{verbatim}
+context default {
+ 1234 => Playback(tt-monkeys);
+ 8000 => {
+ NoOp(one);
+ NoOp(two);
+ NoOp(three);
+ };
+ _5XXX => NoOp(it's a pattern!);
+}
+\end{verbatim}
+\end{astlisting}
+
+Two optional items have been added to the AEL syntax, that allow the
+specification of hints, and a keyword, regexten, that will force the
+numbering of priorities to start at 2.
+
+The ability to make extensions match by CID is preserved in
+AEL; just use '/' and the CID number in the specification. See below.
+\begin{astlisting}
+\begin{verbatim}
+context default {
+
+ regexten _5XXX => NoOp(it's a pattern!);
+}
+\end{verbatim}
+\end{astlisting}
+
+\begin{astlisting}
+\begin{verbatim}
+context default {
+
+ hint(Sip/1) _5XXX => NoOp(it's a pattern!);
+}
+\end{verbatim}
+\end{astlisting}
+
+\begin{astlisting}
+\begin{verbatim}
+context default {
+
+ regexten hint(Sip/1) _5XXX => NoOp(it's a pattern!);
+}
+\end{verbatim}
+\end{astlisting}
+
+The regexten must come before the hint if they are both present.
+
+CID matching is done as with the extensions.conf file. Follow the extension
+name/number with a slash (/) and the number to match against the Caller ID:
+\begin{astlisting}
+\begin{verbatim}
+context zoombo
+{
+ 819/7079953345 => { NoOp(hello, 3345); }
+}
+\end{verbatim}
+\end{astlisting}
+
+In the above, the 819/7079953345 extension will only be matched if the
+CallerID is 7079953345, and the dialed number is 819. Hopefully you have
+another 819 extension defined for all those who wish 819, that are not so lucky
+as to have 7079953345 as their CallerID!
+
+
+\subsection{Includes}
+
+Contexts can be included in other contexts. All included contexts are
+listed within a single block.
+
+\begin{astlisting}
+\begin{verbatim}
+context default {
+ includes {
+ local;
+ longdistance;
+ international;
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+Time-limited inclusions can be specified, as in extensions.conf
+format, with the fields described in the wiki page Asterisk cmd
+GotoIfTime.
+
+\begin{astlisting}
+\begin{verbatim}
+context default {
+ includes {
+ local;
+ longdistance|16:00-23:59|mon-fri|*|*;
+ international;
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+\subsection{\#include}
+
+You can include other files with the \#include "filepath" construct.
+
+\begin{astlisting}
+\begin{verbatim}
+ #include "/etc/asterisk/testfor.ael"
+\end{verbatim}
+\end{astlisting}
+
+An interesting property of the \#include, is that you can use it almost
+anywhere in the .ael file. It is possible to include the contents of
+a file in a macro, context, or even extension. The \#include does not
+have to occur at the beginning of a line. Included files can include
+other files, up to 50 levels deep. If the path provided in quotes is a
+relative path, the parser looks in the config file directory for the
+file (usually \path{/etc/asterisk}).
+
+
+
+\subsection{Dialplan Switches}
+
+Switches are listed in their own block within a context. For clues as
+to what these are used for, see Asterisk - dual servers, and Asterisk
+config extensions.conf.
+
+\begin{astlisting}
+\begin{verbatim}
+context default {
+ switches {
+ DUNDi/e164;
+ IAX2/box5;
+ };
+ eswitches {
+ IAX2/context@${CURSERVER};
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Ignorepat}
+
+ignorepat can be used to instruct channel drivers to not cancel
+dialtone upon receipt of a particular pattern. The most commonly used
+example is '9'.
+\begin{astlisting}
+\begin{verbatim}
+context outgoing {
+ ignorepat => 9;
+}
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Variables}
+
+Variables in Asterisk do not have a type, so to define a variable, it
+just has to be specified with a value.
+
+Global variables are set in their own block.
+
+\begin{astlisting}
+\begin{verbatim}
+globals {
+ CONSOLE=Console/dsp;
+ TRUNK=Zap/g2;
+}
+\end{verbatim}
+\end{astlisting}
+
+Variables can be set within extensions as well.
+
+\begin{astlisting}
+\begin{verbatim}
+context foo {
+ 555 => {
+ x=5;
+ y=blah;
+ divexample=10/2
+ NoOp(x is ${x} and y is ${y} !);
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+NOTE: AEL wraps the right hand side of an assignment with \$[ ] to allow
+expressions to be used If this is unwanted, you can protect the right hand
+side from being wrapped by using the Set() application.
+Read the README.variables about the requirements and behavior
+of \$[ ] expressions.
+
+NOTE: These things are wrapped up in a \$[ ] expression: The while() test;
+the if() test; the middle expression in the for( x; y; z) statement
+(the y expression); Assignments - the right hand side, so a = b -> Set(a=\$[b])
+
+Writing to a dialplan function is treated the same as writing to a variable.
+
+\begin{astlisting}
+\begin{verbatim}
+context blah {
+ s => {
+ CALLERID(name)=ChickenMan;
+ NoOp(My name is ${CALLERID(name)} !);
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+You can declare variables in Macros, as so:
+
+\begin{astlisting}
+\begin{verbatim}
+Macro myroutine(firstarg, secondarg)
+{
+ Myvar=1;
+ NoOp(Myvar is set to ${myvar});
+}
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Local Variables}
+
+In 1.2, and 1.4, ALL VARIABLES are CHANNEL variables, including the function
+arguments and associated ARG1, ARG2, etc variables. Sorry.
+
+In trunk (1.6 and higher), we have made all arguments local variables to
+a macro call. They will not affect channel variables of the same name.
+This includes the ARG1, ARG2, etc variables.
+
+Users can declare their own local variables by using the keyword 'local'
+before setting them to a value;
+
+\begin{astlisting}
+\begin{verbatim}
+Macro myroutine(firstarg, secondarg)
+{
+ local Myvar=1;
+ NoOp(Myvar is set to ${Myvar}, and firstarg is ${firstarg}, and secondarg is ${secondarg});
+}
+\end{verbatim}
+\end{astlisting}
+
+In the above example, Myvar, firstarg, and secondarg are all local variables,
+and will not be visible to the calling code, be it an extension, or another Macro.
+
+If you need to make a local variable within the Set() application, you can do it this way:
+\begin{astlisting}
+\begin{verbatim}
+Macro myroutine(firstarg, secondarg)
+{
+ Set(LOCAL(Myvar)=1);
+ NoOp(Myvar is set to ${Myvar}, and firstarg is ${firstarg}, and secondarg is ${secondarg});
+}
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Loops}
+
+AEL has implementations of 'for' and 'while' loops.
+\begin{astlisting}
+\begin{verbatim}
+context loops {
+ 1 => {
+ for (x=0; ${x} < 3; x=${x} + 1) {
+ Verbose(x is ${x} !);
+ }
+ }
+ 2 => {
+ y=10;
+ while (${y} >= 0) {
+ Verbose(y is ${y} !);
+ y=${y}-1;
+ }
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+NOTE: The conditional expression (the "\$\{y\} $>$= 0" above) is wrapped in
+ \$[ ] so it can be evaluated. NOTE: The for loop test expression
+ (the "\${x} $<$ 3" above) is wrapped in \$[ ] so it can be evaluated.
+
+
+
+\subsection{Conditionals}
+
+AEL supports if and switch statements, like AEL, but adds ifTime, and
+random. Unlike the original AEL, though, you do NOT need to put curly
+braces around a single statement in the "true" branch of an if(), the
+random(), or an ifTime() statement. The if(), ifTime(), and random()
+statements allow optional else clause.
+
+\begin{astlisting}
+\begin{verbatim}
+context conditional {
+ _8XXX => {
+ Dial(SIP/${EXTEN});
+ if ("${DIALSTATUS}" = "BUSY")
+ {
+ NoOp(yessir);
+ Voicemail(${EXTEN},b);
+ }
+ else
+ Voicemail(${EXTEN},u);
+ ifTime (14:00-25:00,sat-sun,*,*)
+ Voicemail(${EXTEN},b);
+ else
+ {
+ Voicemail(${EXTEN},u);
+ NoOp(hi, there!);
+ }
+ random(51) NoOp(This should appear 51% of the time);
+
+ random( 60 )
+ {
+ NoOp( This should appear 60% of the time );
+ }
+ else
+ {
+ random(75)
+ {
+ NoOp( This should appear 30% of the time! );
+ }
+ else
+ {
+ NoOp( This should appear 10% of the time! );
+ }
+ }
+ }
+ _777X => {
+ switch (${EXTEN}) {
+ case 7771:
+ NoOp(You called 7771!);
+ break;
+ case 7772:
+ NoOp(You called 7772!);
+ break;
+ case 7773:
+ NoOp(You called 7773!);
+ // fall thru-
+ pattern 777[4-9]:
+ NoOp(You called 777 something!);
+ default:
+ NoOp(In the default clause!);
+ }
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+NOTE: The conditional expression in if() statements (the
+ "\$\{DIALSTATUS\}" = "BUSY" above) is wrapped by the compiler in
+ \$[] for evaluation.
+
+NOTE: Neither the switch nor case values are wrapped in \$[ ]; they can
+ be constants, or \$\{var\} type references only.
+
+NOTE: AEL generates each case as a separate extension. case clauses
+ with no terminating 'break', or 'goto', have a goto inserted, to
+ the next clause, which creates a 'fall thru' effect.
+
+NOTE: AEL introduces the ifTime keyword/statement, which works just
+ like the if() statement, but the expression is a time value,
+ exactly like that used by the application GotoIfTime(). See
+ Asterisk cmd GotoIfTime
+
+NOTE: The pattern statement makes sure the new extension that is
+ created has an '\_' preceding it to make sure asterisk recognizes
+ the extension name as a pattern.
+
+NOTE: Every character enclosed by the switch expression's parenthesis
+ are included verbatim in the labels generated. So watch out for
+ spaces!
+
+NOTE: NEW: Previous to version 0.13, the random statement used the
+ "Random()" application, which has been deprecated. It now uses
+ the RAND() function instead, in the GotoIf application.
+
+
+\subsection{Break, Continue, and Return}
+
+Three keywords, break, continue, and return, are included in the
+syntax to provide flow of control to loops, and switches.
+
+The break can be used in switches and loops, to jump to the end of the
+loop or switch.
+
+The continue can be used in loops (while and for) to immediately jump
+to the end of the loop. In the case of a for loop, the increment and
+test will then be performed. In the case of the while loop, the
+continue will jump to the test at the top of the loop.
+
+The return keyword will cause an immediate jump to the end of the
+context, or macro, and can be used anywhere.
+
+
+
+\subsection{goto, jump, and labels}
+
+This is an example of how to do a goto in AEL.
+
+\begin{astlisting}
+\begin{verbatim}
+context gotoexample {
+ s => {
+begin:
+ NoOp(Infinite Loop! yay!);
+ Wait(1);
+ goto begin; // go to label in same extension
+ }
+ 3 => {
+ goto s,begin; // go to label in different extension
+ }
+ 4 => {
+ goto gotoexample,s,begin; // overkill go to label in same context
+ }
+}
+
+context gotoexample2 {
+ s => {
+ end:
+ goto gotoexample,s,begin; // go to label in different context
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+You can use the special label of "1" in the goto and jump
+statements. It means the "first" statement in the extension. I would
+not advise trying to use numeric labels other than "1" in goto's or
+jumps, nor would I advise declaring a "1" label anywhere! As a matter
+of fact, it would be bad form to declare a numeric label, and it might
+conflict with the priority numbers used internally by asterisk.
+
+The syntax of the jump statement is: jump
+extension[,priority][@context] If priority is absent, it defaults to
+"1". If context is not present, it is assumed to be the same as that
+which contains the "jump".
+
+\begin{astlisting}
+\begin{verbatim}
+context gotoexample {
+ s => {
+begin:
+ NoOp(Infinite Loop! yay!);
+ Wait(1);
+ jump s; // go to first extension in same extension
+ }
+ 3 => {
+ jump s,begin; // go to label in different extension
+ }
+ 4 => {
+ jump s,begin@gotoexample; // overkill go to label in same context
+ }
+}
+
+context gotoexample2 {
+ s => {
+ end:
+ jump s@gotoexample; // go to label in different context
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+NOTE: goto labels follow the same requirements as the Goto()
+ application, except the last value has to be a label. If the
+ label does not exist, you will have run-time errors. If the
+ label exists, but in a different extension, you have to specify
+ both the extension name and label in the goto, as in: goto s,z;
+ if the label is in a different context, you specify
+ context,extension,label. There is a note about using goto's in a
+ switch statement below...
+
+NOTE AEL introduces the special label "1", which is the beginning
+ context number for most extensions.
+
+
+\subsection{Macros}
+
+A macro is defined in its own block like this. The arguments to the
+macro are specified with the name of the macro. They are then referred
+to by that same name. A catch block can be specified to catch special
+extensions.
+
+\begin{astlisting}
+\begin{verbatim}
+macro std-exten( ext , dev ) {
+ Dial(${dev}/${ext},20);
+ switch(${DIALSTATUS) {
+ case BUSY:
+ Voicemail(${ext},b);
+ break;
+ default:
+ Voicemail(${ext},u);
+
+ }
+ catch a {
+ VoiceMailMain(${ext});
+ return;
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+A macro is then called by preceding the macro name with an
+ampersand. Empty arguments can be passed simply with nothing between
+comments(0.11).
+
+\begin{astlisting}
+\begin{verbatim}
+context example {
+ _5XXX => &std-exten(${EXTEN}, "IAX2");
+ _6XXX => &std-exten(, "IAX2");
+ _7XXX => &std-exten(${EXTEN},);
+ _8XXX => &std-exten(,);
+}
+\end{verbatim}
+\end{astlisting}
+
+
+\section{Examples}
+
+\begin{astlisting}
+\begin{verbatim}
+context demo {
+ s => {
+ Wait(1);
+ Answer();
+ TIMEOUT(digit)=5;
+ TIMEOUT(response)=10;
+restart:
+ Background(demo-congrats);
+instructions:
+ for (x=0; ${x} < 3; x=${x} + 1) {
+ Background(demo-instruct);
+ WaitExten();
+ }
+ }
+ 2 => {
+ Background(demo-moreinfo);
+ goto s,instructions;
+ }
+ 3 => {
+ LANGUAGE()=fr;
+ goto s,restart;
+ }
+
+ 500 => {
+ Playback(demo-abouttotry);
+ Dial(IAX2/guest@misery.digium.com);
+ Playback(demo-nogo);
+ goto s,instructions;
+ }
+ 600 => {
+ Playback(demo-echotest);
+ Echo();
+ Playback(demo-echodone);
+ goto s,instructions;
+ }
+ # => {
+hangup:
+ Playback(demo-thanks);
+ Hangup();
+ }
+ t => goto #,hangup;
+ i => Playback(invalid);
+}
+\end{verbatim}
+\end{astlisting}
+
+
+\section{Semantic Checks}
+
+
+AEL, after parsing, but before compiling, traverses the dialplan
+tree, and makes several checks:
+
+\begin{itemize}
+ \item Macro calls to non-existent macros.
+ \item Macro calls to contexts.
+ \item Macro calls with argument count not matching the definition.
+ \item application call to macro. (missing the '\&')
+ \item application calls to "GotoIf", "GotoIfTime", "while",
+ "endwhile", "Random", and "execIf", will generate a message to
+ consider converting the call to AEL goto, while, etc. constructs.
+ \item goto a label in an empty extension.
+ \item goto a non-existent label, either a within-extension,
+ within-context, or in a different context, or in any included
+ contexts. Will even check "sister" context references.
+ \item All the checks done on the time values in the dial plan, are
+ done on the time values in the ifTime() and includes times:
+ o the time range has to have two times separated by a dash;
+ o the times have to be in range of 0 to 24 hours.
+ o The weekdays have to match the internal list, if they are provided;
+ o the day of the month, if provided, must be in range of 1 to 31;
+ o the month name or names have to match those in the internal list.
+ \item (0.5) If an expression is wrapped in \$[ ... ], and the compiler
+ will wrap it again, a warning is issued.
+ \item (0.5) If an expression had operators (you know,
+ +,-,*,/,%,!,etc), but no \${ } variables, a warning is
+ issued. Maybe someone forgot to wrap a variable name?
+ \item (0.12) check for duplicate context names.
+ \item (0.12) check for abstract contexts that are not included by any context.
+ \item (0.13) Issue a warning if a label is a numeric value.
+\end{itemize}
+
+There are a subset of checks that have been removed until the proposed
+AAL (Asterisk Argument Language) is developed and incorporated into Asterisk.
+These checks will be:
+
+\begin{itemize}
+ \item (if the application argument analyzer is working: the presence
+ of the 'j' option is reported as error.
+ \item if options are specified, that are not available in an
+ application.
+ \item if you specify too many arguments to an application.
+ \item a required argument is not present in an application call.
+ \item Switch-case using "known" variables that applications set, that
+ does not cover all the possible values. (a "default" case will
+ solve this problem. Each "unhandled" value is listed.
+ \item a Switch construct is used, which is uses a known variable, and
+ the application that would set that variable is not called in
+ the same extension. This is a warning only...
+ \item Calls to applications not in the "applist" database (installed
+ in \path{/var/lib/asterisk/applist}" on most systems).
+ \item In an assignment statement, if the assignment is to a function,
+ the function name used is checked to see if it one of the
+ currently known functions. A warning is issued if it is not.
+\end{itemize}
+
+\section{Differences with the original version of AEL}
+
+\begin{enumerate}
+ \item The \$[...] expressions have been enhanced to include the ==, $|$$|$,
+ and \&\& operators. These operators are exactly equivalent to the
+ =, $|$, and \& operators, respectively. Why? So the C, Java, C++
+ hackers feel at home here.
+ \item It is more free-form. The newline character means very little,
+ and is pulled out of the white-space only for line numbers in
+ error messages.
+ \item It generates more error messages -- by this I mean that any
+ difference between the input and the grammar are reported, by
+ file, line number, and column.
+ \item It checks the contents of \$[ ] expressions (or what will end up
+ being \$[ ] expressions!) for syntax errors. It also does
+ matching paren/bracket counts.
+ \item It runs several semantic checks after the parsing is over, but
+ before the compiling begins, see the list above.
+ \item It handles \#include "filepath" directives. -- ALMOST
+ anywhere, in fact. You could easily include a file in a context,
+ in an extension, or at the root level. Files can be included in
+ files that are included in files, down to 50 levels of hierarchy...
+ \item Local Goto's inside Switch statements automatically have the
+ extension of the location of the switch statement appended to them.
+ \item A pretty printer function is available within pbx\_ael.so.
+ \item In the utils directory, two standalone programs are supplied for
+ debugging AEL files. One is called "aelparse", and it reads in
+ the \path{/etc/asterisk/extensions.ael} file, and shows the results of
+ syntax and semantic checking on stdout, and also shows the
+ results of compilation to stdout. The other is "aelparse1",
+ which uses the original ael compiler to do the same work,
+ reading in "\path{/etc/asterisk/extensions.ael}", using the original
+ 'pbx\_ael.so' instead.
+ \item AEL supports the "jump" statement, and the "pattern" statement
+ in switch constructs. Hopefully these will be documented in the
+ AEL README.
+ \item Added the "return" keyword, which will jump to the end of an
+ extension/Macro.
+ \item Added the ifTime ($<$time range$>$$|$$<$days of week$>$$|$$<$days of
+ month$>$$|$$<$months$>$ ) {} [else {}] construct, which executes much
+ like an if () statement, but the decision is based on the
+ current time, and the time spec provided in the ifTime. See the
+ example above. (Note: all the other time-dependent Applications
+ can be used via ifTime)
+ \item Added the optional time spec to the contexts in the includes
+ construct. See examples above.
+ \item You don't have to wrap a single "true" statement in curly
+ braces, as in the original AEL. An "else" is attached to the
+ closest if. As usual, be careful about nested if statements!
+ When in doubt, use curlies!
+ \item Added the syntax [regexten] [hint(channel)] to precede an
+ extension declaration. See examples above, under
+ "Extension". The regexten keyword will cause the priorities in
+ the extension to begin with 2 instead of 1. The hint keyword
+ will cause its arguments to be inserted in the extension under
+ the hint priority. They are both optional, of course, but the
+ order is fixed at the moment-- the regexten must come before the
+ hint, if they are both present.
+ \item Empty case/default/pattern statements will "fall thru" as
+ expected. (0.6)
+ \item A trailing label in an extension, will automatically have a
+ NoOp() added, to make sure the label exists in the extension on
+ Asterisk. (0.6)
+ \item (0.9) the semicolon is no longer required after a closing brace!
+ (i.e. "];" ===$>$ "\}". You can have them there if you like, but
+ they are not necessary. Someday they may be rejected as a syntax
+ error, maybe.
+ \item (0.9) the // comments are not recognized and removed in the
+ spots where expressions are gathered, nor in application call
+ arguments. You may have to move a comment if you get errors in
+ existing files.
+ \item (0.10) the random statement has been added. Syntax: random (
+ $<$expr$>$ ) $<$lucky-statement$>$ [ else $<$unlucky-statement$>$ ]. The
+ probability of the lucky-statement getting executed is $<$expr$>$,
+ which should evaluate to an integer between 0 and 100. If the
+ $<$lucky-statement$>$ isn't so lucky this time around, then the
+ $<$unlucky-statement$>$ gets executed, if it is present.
+\end{enumerate}
+
+
+\section{Hints and Bugs}
+
+ The safest way to check for a null strings is to say \$[ "\$\{x\}" =
+ "" ] The old way would do as shell scripts often do, and append
+ something on both sides, like this: \$[ \$\{x\}foo = foo ]. The
+ trouble with the old way, is that, if x contains any spaces, then
+ problems occur, usually syntax errors. It is better practice and
+ safer wrap all such tests with double quotes! Also, there are now
+ some functions that can be used in a variable reference,
+ ISNULL(), and LEN(), that can be used to test for an empty string:
+ \$\{ISNULL(\$\{x\})\} or \$[ \$\{LEN(\$\{x\})\} = 0 ].
+
+ Assignment vs. Set(). Keep in mind that setting a variable to
+ value can be done two different ways. If you choose say 'x=y;',
+ keep in mind that AEL will wrap the right-hand-side with
+ \$[]. So, when compiled into extension language format, the end
+ result will be 'Set(x=\$[y])'. If you don't want this effect,
+ then say "Set(x=y);" instead.
+
+
+\section{The Full Power of AEL}
+
+A newcomer to Asterisk will look at the above constructs and
+descriptions, and ask, "Where's the string manipulation functions?",
+"Where's all the cool operators that other languages have to offer?",
+etc.
+
+The answer is that the rich capabilities of Asterisk are made
+available through AEL, via:
+
+\begin{itemize}
+ \item Applications: See Asterisk - documentation of application
+ commands
+
+ \item Functions: Functions were implemented inside \$\{ .. \} variable
+ references, and supply many useful capabilities.
+
+ \item Expressions: An expression evaluation engine handles items
+ wrapped inside \$[...]. This includes some string manipulation
+ facilities, arithmetic expressions, etc.
+
+ \item Application Gateway Interface: Asterisk can fork external
+ processes that communicate via pipe. AGI applications can be
+ written in any language. Very powerful applications can be added
+ this way.
+
+ \item Variables: Channels of communication have variables associated
+ with them, and asterisk provides some global variables. These can be
+ manipulated and/or consulted by the above mechanisms.
+\end{itemize}
diff --git a/trunk/doc/tex/ajam.tex b/trunk/doc/tex/ajam.tex
new file mode 100644
index 000000000..3421cc0ed
--- /dev/null
+++ b/trunk/doc/tex/ajam.tex
@@ -0,0 +1,97 @@
+\section{Asynchronous Javascript Asterisk Manger (AJAM)}
+
+AJAM is a new technology which allows web browsers or other HTTP enabled
+applications and web pages to directly access the Asterisk Manger
+Interface (AMI) via HTTP. Setting up your server to process AJAM
+involves a few steps:
+
+\subsection{Setup the Asterisk HTTP server}
+
+\begin{enumerate}
+\item Uncomment the line "enabled=yes" in \path{/etc/asterisk/http.conf} to enable
+ Asterisk's builtin micro HTTP server.
+
+\item If you want Asterisk to actually deliver simple HTML pages, CSS,
+ javascript, etc. you should uncomment "enablestatic=yes"
+
+\item Adjust your "bindaddr" and "bindport" settings as appropriate for
+ your desired accessibility
+
+\item Adjust your "prefix" if appropriate, which must be the beginning of
+ any URI on the server to match. The default is "asterisk" and the
+ rest of these instructions assume that value.
+\end{enumerate}
+
+\subsection{Allow Manager Access via HTTP}
+
+\begin{enumerate}
+\item Make sure you have both "enabled = yes" and "webenabled = yes" setup
+ in \path{/etc/asterisk/manager.conf}
+
+\item You may also use "httptimeout" to set a default timeout for HTTP
+ connections.
+
+\item Make sure you have a manager username/secret
+\end{enumerate}
+
+Once those configurations are complete you can reload or restart
+Asterisk and you should be able to point your web browser to specific
+URI's which will allow you to access various web functions. A complete
+list can be found by typing "http show status" at the Asterisk CLI.
+
+examples:
+\begin{astlisting}
+\begin{verbatim}
+http://localhost:8088/asterisk/manager?action=login&username=foo&secret=bar
+\end{verbatim}
+\end{astlisting}
+This logs you into the manager interface's "HTML" view. Once you're
+logged in, Asterisk stores a cookie on your browser (valid for the
+length of httptimeout) which is used to connect to the same session.
+\begin{astlisting}
+\begin{verbatim}
+http://localhost:8088/asterisk/rawman?action=status
+\end{verbatim}
+\end{astlisting}
+Assuming you've already logged into manager, this URI will give you a
+"raw" manager output for the "status" command.
+\begin{astlisting}
+\begin{verbatim}
+http://localhost:8088/asterisk/mxml?action=status
+\end{verbatim}
+\end{astlisting}
+This will give you the same status view but represented as AJAX data,
+theoretically compatible with RICO (\url{http://www.openrico.org}).
+\begin{astlisting}
+\begin{verbatim}
+http://localhost:8088/asterisk/static/ajamdemo.html
+\end{verbatim}
+\end{astlisting}
+If you have enabled static content support and have done a make install,
+Asterisk will serve up a demo page which presents a live, but very
+basic, "astman" like interface. You can login with your username/secret
+for manager and have a basic view of channels as well as transfer and
+hangup calls. It's only tested in Firefox, but could probably be made
+to run in other browsers as well.
+
+A sample library (astman.js) is included to help ease the creation of
+manager HTML interfaces.
+
+Note that for the demo, there is no need for *any* external web server.
+
+\subsection{Integration with other web servers}
+
+Asterisk's micro HTTP server is *not* designed to replace a general
+purpose web server and it is intentionally created to provide only the
+minimal interfaces required. Even without the addition of an external
+web server, one can use Asterisk's interfaces to implement screen pops
+and similar tools pulling data from other web servers using iframes,
+div's etc. If you want to integrate CGI's, databases, PHP, etc. you
+will likely need to use a more traditional web server like Apache and
+link in your Asterisk micro HTTP server with something like this:
+\begin{astlisting}
+\begin{verbatim}
+ProxyPass /asterisk http://localhost:8088/asterisk
+\end{verbatim}
+\end{astlisting}
+
diff --git a/trunk/doc/tex/app-sms.tex b/trunk/doc/tex/app-sms.tex
new file mode 100644
index 000000000..aa515f61a
--- /dev/null
+++ b/trunk/doc/tex/app-sms.tex
@@ -0,0 +1,518 @@
+\section{Introduction}
+
+ The SMS module for Asterisk was developed by Adrian Kennard, and is an
+ implementation of the ETSI specification for landline SMS, ETSI ES 201
+ 912, which is available from \url{www.etsi.org}. Landline SMS is starting to
+ be available in various parts of Europe, and is available from BT in
+ the UK. However, Asterisk would allow gateways to be created in other
+ locations such as the US, and use of SMS capable phones such as the
+ Magic Messenger. SMS works using analogue or ISDN lines.
+
+\section{Background}
+
+ Short Message Service (SMS), or texting is very popular between mobile
+ phones. A message can be sent between two phones, and normally
+ contains 160 characters. There are ways in which various types of data
+ can be encoded in a text message such as ring tones, and small
+ graphic, etc. Text messaging is being used for voting and
+ competitions, and also SPAM...
+
+ Sending a message involves the mobile phone contacting a message
+ centre (SMSC) and passing the message to it. The message centre then
+ contacts the destination mobile to deliver the message. The SMSC is
+ responsible for storing the message and trying to send it until the
+ destination mobile is available, or a timeout.
+
+ Landline SMS works in basically the same way. You would normally have
+ a suitable text capable landline phone, or a separate texting box such
+ as a Magic Messenger on your phone line. This sends a message to a
+ message centre your telco provides by making a normal call and sending
+ the data using 1200 Baud FSK signaling according to the ETSI spec. To
+ receive a message the message centre calls the line with a specific
+ calling number, and the text capable phone answers the call and
+ receives the data using 1200 Baud FSK signaling. This works
+ particularly well in the UK as the calling line identity is sent
+ before the first ring, so no phones in the house would ring when a
+ message arrives.
+
+\section{Typical use with Asterisk}
+
+ Sending messages from an Asterisk box can be used for a variety of
+ reasons, including notification from any monitoring systems, email
+ subject lines, etc.
+
+ Receiving messages to an Asterisk box is typically used just to email
+ the messages to someone appropriate - we email and texts that are
+ received to our direct numbers to the appropriate person. Received
+ messages could also be used to control applications, manage
+ competitions, votes, post items to IRC, anything.
+
+ Using a terminal such as a magic messenger, an Asterisk box could ask
+ as a message centre sending messages to the terminal, which will beep
+ and pop up the message (and remember 100 or so messages in its
+ memory).
+
+\section{Terminology}
+
+\begin{itemize}
+ \item SMS -
+ Short Message Service
+ i.e. text messages
+
+ \item SMSC -
+ Short Message Service Centre
+ The system responsible for storing and forwarding messages
+
+ \item MO -
+ Mobile Originated
+ A message on its way from a mobile or landline device to the SMSC
+
+ \item MT -
+ Mobile Terminated
+ A message on its way from the SMSC to the mobile or landline device
+
+ \item RX -
+ Receive
+ A message coming in to the Asterisk box
+
+ \item TX -
+ Transmit
+ A message going out of the Asterisk box
+\end{itemize}
+
+\section{Sub address}
+
+ When sending a message to a landline, you simply send to the landline
+ number. In the UK, all of the mobile operators (bar one) understand
+ sending messages to landlines and pass the messages to the BTText
+ system for delivery to the landline.
+
+ The specification for landline SMS allows for the possibility of more
+ than one device on a single landline. These can be configured with Sub
+ addresses which are a single digit. To send a message to a specific
+ device the message is sent to the landline number with an extra digit
+ appended to the end. The telco can define a default sub address (9 in
+ the UK) which is used when the extra digit is not appended to the end.
+ When the call comes in, part of the calling line ID is the sub
+ address, so that only one device on the line answers the call and
+ receives the message.
+
+ Sub addresses also work for outgoing messages. Part of the number
+ called by the device to send a message is its sub address. Sending
+ from the default sub address (9 in the UK) means the message is
+ delivered with the sender being the normal landline number. Sending
+ from any other sub address makes the sender the landline number with
+ an extra digit on the end.
+
+ Using Asterisk, you can make use of the sub addresses for sending and
+ receiving messages. Using DDI (DID, i.e. multiple numbers on the line
+ on ISDN) you can also make use of many different numbers for SMS.
+
+\section{extensions.conf}
+
+ The following contexts are recommended.
+
+\begin{astlisting}
+\begin{verbatim}
+; Mobile Terminated, RX. This is used when an incoming call from the SMS arrive
+s, with the queue (called number and sub address) in ${EXTEN}
+; Running an app after receipt of the text allows the app to find all messages
+in the queue and handle them, e.g. email them.
+; The app may be something like smsq --process=somecommand --queue=${EXTEN}
+to run a command for each received message
+; See below for usage
+[smsmtrx]
+exten = _X.,1, SMS(${EXTEN},a)
+exten = _X.,2,System("someapptohandleincomingsms ${EXTEN}")
+exten = _X.,3,Hangup
+; Mobile originated, RX. This is receiving a message from a device, e.g.
+; a Magic Messenger on a sip extension
+; Running an app after receipt of the text allows the app to find all messages
+; in the queue and handle then, e.g. sending them to the public SMSC
+; The app may be something like smsq --process=somecommand --queue=${EXTEN}
+; to run a command for each received message
+; See below for example usage
+[smsmorx]
+exten = _X.,1, SMS(${EXTEN},sa)
+exten = _X.,2,System("someapptohandlelocalsms ${EXTEN}")
+exten = _X.,3,Hangup
+\end{verbatim}
+\end{astlisting}
+
+ smsmtrx is normally accessed by an incoming call from the SMSC. In the
+ UK this call is from a CLI of 080058752X0 where X is the sub address.
+ As such a typical usage in the extensions.conf at the point of
+ handling an incoming call is:
+\begin{astlisting}
+\begin{verbatim}
+exten = _X./8005875290,1,Goto(smsmtrx,${EXTEN},1)
+exten = _X./_80058752[0-8]0,1,Goto(smsmtrx,${EXTEN}-${CALLERID(num):8:1},1)
+\end{verbatim}
+\end{astlisting}
+
+ Alternatively, if you have the correct national prefix on incoming
+ CLI, e.g. using zaphfc, you might use:
+\begin{astlisting}
+\begin{verbatim}
+exten = _X./08005875290,1,Goto(smsmtrx,${EXTEN},1)
+exten = _X./_080058752[0-8]0,1,Goto(smsmtrx,${EXTEN}-${CALLERID(num):9:1},1)
+\end{verbatim}
+\end{astlisting}
+
+ smsmorx is normally accessed by a call from a local sip device
+ connected to a Magic Messenger. It could however by that you are
+ operating Asterisk as a message centre for calls from outside. Either
+ way, you look at the called number and goto smsmorx. In the UK, the
+ SMSC number that would be dialed is 1709400X where X is the caller sub
+ address. As such typical usage in extension.config at the point of
+ handling a call from a sip phone is:
+\begin{astlisting}
+\begin{verbatim}
+exten = 17094009,1,Goto(smsmorx,${CALLERID(num)},1)
+exten = _1709400[0-8],1,Goto(smsmorx,${CALLERID(num)}-{EXTEN:7:1},1)
+\end{verbatim}
+\end{astlisting}
+
+\section{Using smsq}
+
+ smsq is a simple helper application designed to make it easy to send
+ messages from a command line. it is intended to run on the Asterisk
+ box and have direct access to the queue directories for SMS and for
+ Asterisk.
+
+ In its simplest form you can send an SMS by a command such as
+ smsq 0123456789 This is a test to 0123456789
+ This would create a queue file for a mobile originated TX message in
+ queue 0 to send the text "This is a test to 0123456789" to 0123456789.
+ It would then place a file in the \path{/var/spool/asterisk/outgoing}
+ directory to initiate a call to 17094009 (the default message centre
+ in smsq) attached to application SMS with argument of the queue name
+ (0).
+
+ Normally smsq will queue a message ready to send, and will then create
+ a file in the Asterisk outgoing directory causing Asterisk to actually
+ connect to the message centre or device and actually send the pending
+ message(s).
+
+ Using \verb!--process!, smsq can however be used on received queues to run a
+ command for each file (matching the queue if specified) with various
+ environment variables set based on the message (see below);
+ smsq options:
+\begin{verbatim}
+ --help
+ Show help text
+ --usage
+ Show usage
+ --queue
+ -q
+ Specify a specific queue
+ In no specified, messages are queued under queue "0"
+ --da
+ -d
+ Specify destination address
+ --oa
+ -o
+ Specify originating address
+ This also implies that we are generating a mobile terminated message
+ --ud
+ -m
+ Specify the actual message
+ --ud-file
+ -f
+ Specify a file to be read for the context of the message
+ A blank filename (e.g. --ud-file= on its own) means read stdin. Very
+ useful when using via ssh where command line parsing could mess up the
+ message.
+ --mt
+ -t
+ Mobile terminated message to be generated
+ --mo
+ Mobile originated message to be generated
+ Default
+ --tx
+ Transmit message
+ Default
+ --rx
+ -r
+ Generate a message in the receive queue
+ --UTF-8
+ Treat the file as UTF-8 encoded (default)
+ --UCS-1
+ Treat the file as raw 8 bit UCS-1 data, not UTF-8 encoded
+ --UCS-2
+ Treat the file as raw 16 bit bigendian USC-2 data
+ --process
+ Specific a command to process for each file in the queue
+ Implies --rx and --mt if not otherwise specified.
+ Sets environment variables for every possible variable, and also ud,
+ ud8 (USC-1 hex), and ud16 (USC-2 hex) for each call. Removes files.
+ --motx-channel
+ Specify the channel for motx calls
+ May contain X to use sub address based on queue name or may be full
+ number
+ Default is Local/1709400X
+ --motx-callerid
+ Specify the caller ID for motx calls
+ The default is the queue name without -X suffix
+ --motx-wait
+ Wait time for motx call
+ Default 10
+ --motx-delay
+ Retry time for motx call
+ Default 1
+ --motx-retries
+ Retries for motx call
+ Default 10
+ --mttx-channel
+ Specify the channel for mttx calls
+ Default is Local/ and the queue name without -X suffix
+ --mtttx-callerid
+ Specify the callerid for mttx calls
+ May include X to use sub address based on queue name or may be full
+ number
+ Default is 080058752X0
+ --mttx-wait
+ Wait time for mttx call
+ Default 10
+ --mttx-delay
+ Retry time for mttx call
+ Default 30
+ --mttx-retries
+ Retries for mttx call
+ Default 100
+ --default-sub-address
+ The default sub address assumed (e.g. for X in CLI and dialled numbers
+ as above) when none added (-X) to queue
+ Default 9
+ --no-dial
+ -x
+ Create queue, but do not dial to send message
+ --no-wait
+ Do not wait if a call appears to be in progress
+ This could have a small window where a message is queued but not
+ sent, so regular calls to smsq should be done to pick up any missed
+ messages
+ --concurrent
+ How many concurrent calls to allow (per queue), default 1
+ --mr
+ -n
+ Message reference
+ --pid
+ -p
+ Protocol ID
+ --dcs
+ Data coding scheme
+ --udh
+ Specific hex string of user data header specified (not including the
+ initial length byte)
+ May be a blank string to indicate header is included in the user data
+ already but user data header indication to be set.
+ --srr
+ Status report requested
+ --rp
+ Return path requested
+ --vp
+ Specify validity period (seconds)
+ --scts
+ Specify timestamp (YYYY-MM-DDTHH:MM:SS)
+ --spool-dir
+ Spool dir (in which sms and outgoing are found)
+ Default /var/spool/asterisk
+\end{verbatim}
+
+ Other arguments starting '-' or '\verb!--!' are invalid and will cause an
+ error. Any trailing arguments are processed as follows:-
+
+\begin{itemize}
+
+ \item If the message is mobile originating and no destination address
+ has been specified, then the first argument is assumed to be a
+ destination address
+
+ \item If the message is mobile terminating and no destination address
+ has been specified, then the first argument is assumed to be the
+ queue name
+
+ \item If there is no user data, or user data file specified, then any
+ following arguments are assumed to be the message, which are
+ concatenated.
+
+ \item If no user data is specified, then no message is sent. However,
+ unless \verb!--no-dial! is specified, smsq checks for pending messages
+ and generates an outgoing anyway
+\end{itemize}
+
+
+ Note that when smsq attempts to make a file in
+ \path{/var/spool/asterisk/outgoing}, it checks if there is already a call
+ queued for that queue. It will try several filenames, up to the
+ \verb!--concurrent! setting. If these files exist, then this means Asterisk
+ is already queued to send all messages for that queue, and so Asterisk
+ should pick up the message just queued. However, this alone could
+ create a race condition, so if the files exist then smsq will wait up
+ to 3 seconds to confirm it still exists or if the queued messages have
+ been sent already. The \verb!--no-wait! turns off this behaviour. Basically,
+ this means that if you have a lot of messages to send all at once,
+ Asterisk will not make unlimited concurrent calls to the same message
+ centre or device for the same queue. This is because it is generally
+ more efficient to make one call and send all of the messages one after
+ the other.
+
+ smsq can be used with no arguments, or with a queue name only, and it
+ will check for any pending messages and cause an outgoing if there are
+ any. It only sets up one outgoing call at a time based on the first
+ queued message it finds. A outgoing call will normally send all queued
+ messages for that queue. One way to use smsq would be to run with no
+ queue name (so any queue) every minute or every few seconds to send
+ pending message. This is not normally necessary unless \verb!--no-dial! is
+ selected. Note that smsq does only check motx or mttx depending on the
+ options selected, so it would need to be called twice as a general
+ check.
+
+ UTF-8 is used to parse command line arguments for user data, and is
+ the default when reading a file. If an invalid UTF-8 sequence is
+ found, it is treated as UCS-1 data (i.e, as is).
+ The \verb!--process! option causes smsq to scan the specified queue (default
+ is mtrx) for messages (matching the queue specified, or any if queue
+ not specified) and run a command and delete the file. The command is
+ run with a number of environment variables set as follows. Note that
+ these are unset if not needed and not just taken from the calling
+ environment. This allows simple processing of incoming messages
+\begin{verbatim}
+ $queue
+ Set if a queue specified
+ $?srr
+ srr is set (to blank) if srr defined and has value 1.
+ $?rp
+ rp is set (to blank) if rp defined and has value 1.
+ $ud
+ User data, UTF-8 encoding, including any control characters, but with
+ nulls stripped out
+ Useful for the content of emails, for example, as it includes any
+ newlines, etc.
+ $ude
+ User data, escaped UTF-8, including all characters, but control
+ characters \n, \r, \t, \f, \xxx and \ is escaped as \\
+ Useful guaranteed one line printable text, so useful in Subject lines
+ of emails, etc
+ $ud8
+ Hex UCS-1 coding of user data (2 hex digits per character)
+ Present only if all user data is in range U+0000 to U+00FF
+ $ud16
+ Hex UCS-2 coding of user data (4 hex digits per character)
+ other
+ Other fields set using their field name, e.g. mr, pid, dcs, etc. udh
+ is a hex byte string
+\end{verbatim}
+
+\section{File formats}
+
+ By default all queues are held in a director \path{/var/spool/asterisk/sms}.
+ Within this directory are sub directories mtrx, mttx, morx, motx which
+ hold the received messages and the messages ready to send. Also,
+ \path{/var/log/asterisk/sms} is a log file of all messages handled.
+
+ The file name in each queue directory starts with the queue parameter
+ to SMS which is normally the CLI used for an outgoing message or the
+ called number on an incoming message, and may have -X (X being sub
+ address) appended. If no queue ID is known, then 0 is used by smsq by
+ default. After this is a dot, and then any text. Files are scanned for
+ matching queue ID and a dot at the start. This means temporary files
+ being created can be given a different name not starting with a queue
+ (we recommend a . on the start of the file name for temp files).
+ Files in these queues are in the form of a simple text file where each
+ line starts with a keyword and an = and then data. udh and ud have
+ options for hex encoding, see below.
+
+ UTF-8. The user data (ud) field is treated as being UTF-8 encoded
+ unless the DCS is specified indicating 8 bit format. If 8 bit format
+ is specified then the user data is sent as is.
+ The keywords are as follows:
+\begin{verbatim}
+ oa Originating address
+ The phone number from which the message came
+ Present on mobile terminated messages and is the CLI for morx messages
+ da
+ Destination Address
+ The phone number to which the message is sent
+ Present on mobile originated messages
+ scts
+ The service centre time stamp
+ Format YYYY-MM-DDTHH:MM:SS
+ Present on mobile terminated messages
+ pid
+ One byte decimal protocol ID
+ See GSM specs for more details
+ Normally 0 or absent
+ dcs
+ One byte decimal data coding scheme
+ If omitted, a sensible default is used (see below)
+ See GSM specs for more details
+ mr
+ One byte decimal message reference
+ Present on mobile originated messages, added by default if absent
+ srr
+ 0 or 1 for status report request
+ Does not work in UK yet, not implemented in app_sms yet
+ rp
+ 0 or 1 return path
+ See GSM specs for details
+ vp
+ Validity period in seconds
+ Does not work in UK yet
+ udh
+ Hex string of user data header prepended to the SMS contents,
+ excluding initial length byte.
+ Consistent with ud, this is specified as udh# rather than udh=
+ If blank, this means that the udhi flag will be set but any user data
+ header must be in the ud field
+ ud
+ User data, may be text, or hex, see below
+\end{verbatim}
+
+ udh is specified as as udh\# followed by hex (2 hex digits per byte).
+ If present, then the user data header indicator bit is set, and the
+ length plus the user data header is added to the start of the user
+ data, with padding if necessary (to septet boundary in 7 bit format).
+ User data can hold an USC character codes U+0000 to U+FFFF. Any other
+ characters are coded as U+FEFF
+
+ ud can be specified as ud= followed by UTF-8 encoded text if it
+ contains no control characters, i.e. only (U+0020 to U+FFFF). Any
+ invalid UTF-8 sequences are treated as is (U+0080-U+00FF).
+
+ ud can also be specified as ud\# followed by hex (2 hex digits per
+ byte) containing characters U+0000 to U+00FF only.
+
+ ud can also be specified as ud\#\# followed by hex (4 hex digits per
+ byte) containing UCS-2 characters.
+
+ When written by app\_sms (e.g. incoming messages), the file is written
+ with ud= if it can be (no control characters). If it cannot, the a
+ comment line ;ud= is used to show the user data for human readability
+ and ud\# or ud\#\# is used.
+
+\section{Delivery reports}
+
+ The SMS specification allows for delivery reports. These are requested
+ using the srr bit. However, as these do not work in the UK yet they
+ are not fully implemented in this application. If anyone has a telco
+ that does implement these, please let me know. BT in the UK have a non
+ standard way to do this by starting the message with *0\#, and so this
+ application may have a UK specific bodge in the near future to handle
+ these.
+
+ The main changes that are proposed for delivery report handling are :
+
+\begin{itemize}
+ \item New queues for sent messages, one file for each destination
+ address and message reference.
+
+ \item New field in message format, user reference, allowing applications
+ to tie up their original message with a report.
+
+ \item Handling of the delivery confirmation/rejection and connecting to
+ the outgoing message - the received message file would then have
+ fields for the original outgoing message and user reference
+ allowing applications to handle confirmations better.
+\end{itemize}
diff --git a/trunk/doc/tex/asterisk-conf.tex b/trunk/doc/tex/asterisk-conf.tex
new file mode 100644
index 000000000..e25bc996f
--- /dev/null
+++ b/trunk/doc/tex/asterisk-conf.tex
@@ -0,0 +1,141 @@
+\subsubsection{Asterisk Main Configuration File}
+
+Below is a sample of the main Asterisk configuration file,
+asterisk.conf. Note that this file is not provided in
+sample form, because the Makefile creates it when needed
+and does not touch it when it already exists.
+
+\begin{astlisting}
+\begin{verbatim}
+[directories]
+; Make sure these directories have the right permissions if not
+; running Asterisk as root
+
+; Where the configuration files (except for this one) are located
+astetcdir => /etc/asterisk
+
+; Where the Asterisk loadable modules are located
+astmoddir => /usr/lib/asterisk/modules
+
+; Where additional 'library' elements (scripts, etc.) are located
+astvarlibdir => /var/lib/asterisk
+
+; Where AGI scripts/programs are located
+astagidir => /var/lib/asterisk/agi-bin
+
+; Where spool directories are located
+; Voicemail, monitor, dictation and other apps will create files here
+; and outgoing call files (used with pbx_spool) must be placed here
+astspooldir => /var/spool/asterisk
+
+; Where the Asterisk process ID (pid) file should be created
+astrundir => /var/run/asterisk
+
+; Where the Asterisk log files should be created
+astlogdir => /var/log/asterisk
+
+
+[options]
+;Under "options" you can enter configuration options
+;that you also can set with command line options
+
+; Verbosity level for logging (-v)
+verbose = 0
+
+; Debug: "No" or value (1-4)
+debug = 3
+
+; Background execution disabled (-f)
+nofork=yes | no
+
+; Always background, even with -v or -d (-F)
+alwaysfork=yes | no
+
+; Console mode (-c)
+console= yes | no
+
+; Execute with high priority (-p)
+highpriority = yes | no
+
+; Initialize crypto at startup (-i)
+initcrypto = yes | no
+
+; Disable ANSI colors (-n)
+nocolor = yes | no
+
+; Dump core on failure (-g)
+dumpcore = yes | no
+
+; Run quietly (-q)
+quiet = yes | no
+
+; Force timestamping in CLI verbose output (-T)
+timestamp = yes | no
+
+; User to run asterisk as (-U) NOTE: will require changes to
+; directory and device permissions
+runuser = asterisk
+
+; Group to run asterisk as (-G)
+rungroup = asterisk
+
+; Enable internal timing support (-I)
+internal_timing = yes | no
+
+; These options have no command line equivalent
+
+; Cache record() files in another directory until completion
+cache_record_files = yes | no
+record_cache_dir = <dir>
+
+; Build transcode paths via SLINEAR
+transcode_via_sln = yes | no
+
+; send SLINEAR silence while channel is being recorded
+transmit_silence_during_record = yes | no
+
+; The maximum load average we accept calls for
+maxload = 1.0
+
+; The maximum number of concurrent calls you want to allow
+maxcalls = 255
+
+; Stop accepting calls when free memory falls below this amount specified in MB
+minmemfree = 256
+
+; Allow #exec entries in configuration files
+execincludes = yes | no
+
+; Don't over-inform the Asterisk sysadm, he's a guru
+dontwarn = yes | no
+
+; System name. Used to prefix CDR uniqueid and to fill \${SYSTEMNAME}
+systemname = <a_string>
+
+; Should language code be last component of sound file name or first?
+; when off, sound files are searched as <path>/<lang>/<file>
+; when on, sound files are search as <lang>/<path>/<file>
+; (only affects relative paths for sound files)
+languageprefix = yes | no
+
+; Locking mode for voicemail
+; - lockfile: default, for normal use
+; - flock: for where the lockfile locking method doesn't work
+; eh. on SMB/CIFS mounts
+lockmode = lockfile | flock
+
+
+[files]
+; Changing the following lines may compromise your security
+; Asterisk.ctl is the pipe that is used to connect the remote CLI
+; (asterisk -r) to Asterisk. Changing these settings change the
+; permissions and ownership of this file.
+; The file is created when Asterisk starts, in the "astrundir" above.
+
+;astctlpermissions = 0660
+;astctlowner = root
+;astctlgroup = asterisk
+;astctl = asterisk.ctl
+
+\end{verbatim}
+\end{astlisting}
diff --git a/trunk/doc/tex/asterisk.tex b/trunk/doc/tex/asterisk.tex
new file mode 100644
index 000000000..0feb3d7d9
--- /dev/null
+++ b/trunk/doc/tex/asterisk.tex
@@ -0,0 +1,162 @@
+% To generate a PDF from this, install the "rubber" tool, and the LaTeX
+% dependencies for it. Then, run:
+%
+% rubber asterisk.tex
+%
+% http://www.pps.jussieu.fr/~beffara/soft/rubber/
+
+\documentclass[12pt,a4]{report}
+
+\usepackage{hyperref}
+
+\usepackage{url}
+\makeatletter
+\def\url@aststyle{%
+ \@ifundefined{selectfont}{\def\UrlFont{\sf}}{\def\UrlFont{\small\ttfamily}}}
+\makeatother
+\urlstyle{ast}
+
+\usepackage[titles]{tocloft}
+\renewcommand{\cftchapfont}{%
+ \fontsize{11}{13}\usefont{OT1}{phv}{bc}{n}\selectfont
+}
+
+\newenvironment{astlisting}
+{\begin{list}{}{\setlength{\leftmargin}{1em}}\item\scriptsize\bfseries}
+{\end{list}}
+
+\usepackage{sectsty}
+\allsectionsfont{\usefont{OT1}{phv}{bc}{n}\selectfont}
+
+\usepackage[Lenny]{fncychap}
+
+
+\author{Asterisk Development Team \\ Asterisk.org}
+\title{Asterisk Reference Information \\ Version }
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+
+\chapter{Introduction}
+
+This document contains various pieces of information that are useful for
+reference purposes.
+
+ \section{License Information}
+ \input{../../LICENSE}
+ \subsection{Hold Music}
+ Digium has licensed the music included with
+ the Asterisk distribution From FreePlayMusic
+ for use and distribution with Asterisk. It
+ is licensed ONLY for use as hold music within
+ an Asterisk based PBX.
+ \section{Security}
+ \input{security.tex}
+ \section{Hardware}
+ \input{hardware.tex}
+
+\chapter{Configuration}
+ \section{General Configuration Information}
+ \subsection{Configuration Parser}
+ \input{configuration.tex}
+ \subsection{Asterisk.conf}
+ \input{asterisk-conf.tex}
+ \subsection{CLI Prompt}
+ \input{cliprompt.tex}
+ \subsection{Extensions}
+ \input{extensions.tex}
+ \subsection{IP Quality of Service}
+ \input{qos.tex}
+ \subsection{MP3 Support}
+ \input{mp3.tex}
+ \subsection{ICES}
+ \input{ices.tex}
+ \section{Database Support}
+ \subsection{Realtime Database Configuration}
+ \input{realtime.tex}
+ \subsection{FreeTDS}
+ \input{freetds.tex}
+ \section{Privacy}
+ \input{privacy.tex}
+
+\chapter{Channel Variables}
+\input{channelvariables.tex}
+
+\chapter{AEL: Asterisk Extension Language}
+\input{ael.tex}
+
+\chapter{SLA: Shared Line Appearances}
+\input{sla.tex}
+
+\chapter{Channel Drivers}
+ \section{IAX2}
+ \input{chaniax.tex}
+ \subsection{IAX2 Jitterbuffer}
+ \input{jitterbuffer.tex}
+ \section{mISDN}
+ \input{misdn.tex}
+ \section{Local}
+ \input{localchannel.tex}
+
+\chapter{Distributed Universal Number Discovery (DUNDi)}
+ \section{Introduction}
+ \input{dundi.tex}
+ \section{Peering Agreement}
+ \input{../PEERING}
+
+\chapter{ENUM}
+\input{enum.tex}
+
+\chapter{AMI: Asterisk Manager Interface}
+ \input{manager.tex}
+ \input{ajam.tex}
+
+\chapter{CDR: Call Detail Records}
+\input{billing.tex}
+\input{cdrdriver.tex}
+
+\chapter{Voicemail}
+ \section{ODBC Storage}
+ \label{odbcstorage}
+ \input{odbcstorage.tex}
+ \section{IMAP Storage}
+ \input{imapstorage.tex}
+
+\chapter{SMS}
+\input{app-sms.tex}
+
+\chapter{Queues}
+ \input{queues-with-callback-members.tex}
+ \section{Queue Logs}
+ \input{queuelog.tex}
+
+\chapter{Phone Provisioning}
+ \input{phoneprov.tex}
+
+\chapter{Development}
+ \section{Backtrace}
+ \input{backtrace.tex}
+
+
+
+% This is a list of files not yet integrated into this document:
+%
+%Misc
+%----
+%asterisk-mib.txt SNMP mib for Asterisk (net-snmp)
+%digium-mib.txt SNMP mib for Asterisk (net-snmp)
+%
+%For developers
+%--------------
+%See http://www.asterisk.org/developers for more information
+%
+%callfiles.txt Asterisk callfiles using instruction
+%CODING-GUIDELINES Guidelines for developers
+%externalivr.txt Documentation of the protocol used in externalivr()
+%modules.txt How Asterisk modules work
+%datastores.txt About channel data stores
+%speechrec.txt The Generic Speech Recognition API
+
+\end{document}
diff --git a/trunk/doc/tex/backtrace.tex b/trunk/doc/tex/backtrace.tex
new file mode 100644
index 000000000..f43f42327
--- /dev/null
+++ b/trunk/doc/tex/backtrace.tex
@@ -0,0 +1,217 @@
+This document is intended to provide information on how to obtain the
+backtraces required on the asterisk bug tracker, available at
+\url{http://bugs.digium.com}. The information is required by developers to
+help fix problem with bugs of any kind. Backtraces provide information
+about what was wrong when a program crashed; in our case,
+Asterisk. There are two kind of backtraces (aka 'bt') which are
+useful: bt and bt full.
+
+First of all, when you start Asterisk, you MUST start it with option
+-g. This tells Asterisk to produce a core file if it crashes.
+
+If you start Asterisk with the safe\_asterisk script, it automatically
+starts using the option -g.
+
+If you're not sure if Asterisk is running with the -g option, type the
+following command in your shell:
+
+\begin{astlisting}
+\begin{verbatim}
+debian:/tmp# ps aux | grep asterisk
+root 17832 0.0 1.2 2348 788 pts/1 S Aug12 0:00 /bin/sh /usr/sbin/safe_asterisk
+root 26686 0.0 2.8 15544 1744 pts/1 S Aug13 0:02 asterisk -vvvg -c
+[...]
+\end{verbatim}
+\end{astlisting}
+
+The interesting information is located in the last column.
+
+Second, your copy of Asterisk must have been built without
+optimization or the backtrace will be (nearly) unusable. This can be
+done by selecting the 'DONT\_OPTIMIZE' option in the Compiler Flags
+submenu in the 'make menuselect' tree before building Asterisk.
+
+After Asterisk crashes, a core file will be "dumped" in your \path{/tmp/}
+directory. To make sure it's really there, you can just type the
+following command in your shell:
+
+\begin{astlisting}
+\begin{verbatim}
+debian:/tmp# ls -l /tmp/core.*
+-rw------- 1 root root 10592256 Aug 12 19:40 /tmp/core.26252
+-rw------- 1 root root 9924608 Aug 12 20:12 /tmp/core.26340
+-rw------- 1 root root 10862592 Aug 12 20:14 /tmp/core.26374
+-rw------- 1 root root 9105408 Aug 12 20:19 /tmp/core.26426
+-rw------- 1 root root 9441280 Aug 12 20:20 /tmp/core.26462
+-rw------- 1 root root 8331264 Aug 13 00:32 /tmp/core.26647
+debian:/tmp#
+\end{verbatim}
+\end{astlisting}
+
+In the event that there are multiple core files present (as in the
+above example), it is important to look at the file timestamps in
+order to determine which one you really intend to look at.
+
+Now that we've verified the core file has been written to disk, the
+final part is to extract 'bt' from the core file. Core files are
+pretty big, don't be scared, it's normal.
+
+\textbf{NOTE: Don't attach core files on the bug tracker, we only need the bt and bt full.}
+
+For extraction, we use a really nice tool, called gdb. To verify that
+you have gdb installed on your system:
+
+\begin{astlisting}
+\begin{verbatim}
+debian:/tmp# gdb -v
+GNU gdb 6.3-debian
+Copyright 2004 Free Software Foundation, Inc.
+GDB is free software, covered by the GNU General Public License, and you are
+welcome to change it and/or distribute copies of it under certain conditions.
+Type "show copying" to see the conditions.
+There is absolutely no warranty for GDB. Type "show warranty" for details.
+This GDB was configured as "i386-linux".
+debian:/tmp#
+\end{verbatim}
+\end{astlisting}
+
+Which is great, we can continue. If you don't have gdb installed, go install gdb.
+
+Now load the core file in gdb, as follows:
+
+\begin{astlisting}
+\begin{verbatim}
+debian:/tmp# gdb asterisk /tmp/core.26252
+[...]
+(You would see a lot of output here.)
+[...]
+Reading symbols from /usr/lib/asterisk/modules/app_externalivr.so...done.
+Loaded symbols for /usr/lib/asterisk/modules/app_externalivr.so
+#0 0x29b45d7e in ?? ()
+(gdb)
+\end{verbatim}
+\end{astlisting}
+
+Now at the gdb prompt, type: bt
+You would see output similar to:
+
+\begin{astlisting}
+\begin{verbatim}
+(gdb) bt
+#0 0x29b45d7e in ?? ()
+#1 0x08180bf8 in ?? ()
+#2 0xbcdffa58 in ?? ()
+#3 0x08180bf8 in ?? ()
+#4 0xbcdffa60 in ?? ()
+#5 0x08180bf8 in ?? ()
+#6 0x180bf894 in ?? ()
+#7 0x0bf80008 in ?? ()
+#8 0x180b0818 in ?? ()
+#9 0x08068008 in ast_stopstream (tmp=0x40758d38) at file.c:180
+#10 0x000000a0 in ?? ()
+#11 0x000000a0 in ?? ()
+#12 0x00000000 in ?? ()
+#13 0x407513c3 in confcall_careful_stream (conf=0x8180bf8, filename=0x8181de8 "Zap/pseudo-1324221520") at app_meetme.c:262
+#14 0x40751332 in streamconfthread (args=0x8180bf8) at app_meetme.c:1965
+#15 0xbcdffbe0 in ?? ()
+#16 0x40028e51 in pthread_start_thread () from /lib/libpthread.so.0
+#17 0x401ec92a in clone () from /lib/libc.so.6
+(gdb)
+\end{verbatim}
+\end{astlisting}
+
+The bt's output is the information that we need on the bug tracker.
+
+\begin{astlisting}
+\begin{verbatim}
+Now do a bt full as follows:
+(gdb) bt full
+#0 0x29b45d7e in ?? ()
+No symbol table info available.
+#1 0x08180bf8 in ?? ()
+No symbol table info available.
+#2 0xbcdffa58 in ?? ()
+No symbol table info available.
+#3 0x08180bf8 in ?? ()
+No symbol table info available.
+#4 0xbcdffa60 in ?? ()
+No symbol table info available.
+#5 0x08180bf8 in ?? ()
+No symbol table info available.
+#6 0x180bf894 in ?? ()
+No symbol table info available.
+#7 0x0bf80008 in ?? ()
+No symbol table info available.
+#8 0x180b0818 in ?? ()
+No symbol table info available.
+#9 0x08068008 in ast_stopstream (tmp=0x40758d38) at file.c:180
+No locals.
+#10 0x000000a0 in ?? ()
+No symbol table info available.
+#11 0x000000a0 in ?? ()
+No symbol table info available.
+#12 0x00000000 in ?? ()
+No symbol table info available.
+#13 0x407513c3 in confcall_careful_stream (conf=0x8180bf8, filename=0x8181de8 "Zap/pseudo-1324221520") at app_meetme.c:262
+ f = (struct ast_frame *) 0x8180bf8
+ trans = (struct ast_trans_pvt *) 0x0
+#14 0x40751332 in streamconfthread (args=0x8180bf8) at app_meetme.c:1965
+No locals.
+#15 0xbcdffbe0 in ?? ()
+No symbol table info available.
+#16 0x40028e51 in pthread_start_thread () from /lib/libpthread.so.0
+No symbol table info available.
+#17 0x401ec92a in clone () from /lib/libc.so.6
+No symbol table info available.
+(gdb)
+\end{verbatim}
+\end{astlisting}
+
+We also need gdb's output. That output gives more details compared to
+the simple "bt". So we recommend that you use bt full instead of bt.
+But, if you could include both, we appreciate that.
+
+The final "extraction" would be to know all traces by all
+threads. Even if asterisk runs on the same thread for each call, it
+could have created some new threads.
+
+To make sure we have the correct information, just do:
+(gdb) thread apply all bt
+
+\begin{astlisting}
+\begin{verbatim}
+Thread 1 (process 26252):
+#0 0x29b45d7e in ?? ()
+#1 0x08180bf8 in ?? ()
+#2 0xbcdffa58 in ?? ()
+#3 0x08180bf8 in ?? ()
+#4 0xbcdffa60 in ?? ()
+#5 0x08180bf8 in ?? ()
+#6 0x180bf894 in ?? ()
+#7 0x0bf80008 in ?? ()
+#8 0x180b0818 in ?? ()
+#9 0x08068008 in ast_stopstream (tmp=0x40758d38) at file.c:180
+#10 0x000000a0 in ?? ()
+#11 0x000000a0 in ?? ()
+#12 0x00000000 in ?? ()
+#13 0x407513c3 in confcall_careful_stream (conf=0x8180bf8, filename=0x8181de8 "Zap/pseudo-1324221520") at app_meetme.c:262
+#14 0x40751332 in streamconfthread (args=0x8180bf8) at app_meetme.c:1965
+#15 0xbcdffbe0 in ?? ()
+#16 0x40028e51 in pthread_start_thread () from /lib/libpthread.so.0
+#17 0x401ec92a in clone () from /lib/libc.so.6
+(gdb)
+\end{verbatim}
+\end{astlisting}
+
+That output tells us crucial information about each thread.
+
+Now, just create an output.txt file and dump your "bt full"
+(and/or "bt") ALONG WITH "thread apply all bt" into it.
+
+Note: Please ATTACH your output, DO NOT paste it as a note.
+
+And you're ready for upload on the bug tracker.
+
+If you have questions or comments regarding this documentation, feel
+free to pass by the \#asterisk-bugs channel on irc.freenode.net.
+
diff --git a/trunk/doc/tex/billing.tex b/trunk/doc/tex/billing.tex
new file mode 100644
index 000000000..9fae40be8
--- /dev/null
+++ b/trunk/doc/tex/billing.tex
@@ -0,0 +1,86 @@
+\section{Applications}
+
+\begin{itemize}
+ \item SetAccount - Set account code for billing
+ \item SetAMAFlags - Sets AMA flags
+ \item NoCDR - Make sure no CDR is saved for a specific call
+ \item ResetCDR - Reset CDR
+ \item ForkCDR - Save current CDR and start a new CDR for this call
+ \item Authenticate - Authenticates and sets the account code
+ \item SetCDRUserField - Set CDR user field
+ \item AppendCDRUserField - Append data to CDR User field
+\end{itemize}
+
+For more information, use the "core show application $<$application$>$" command.
+You can set default account codes and AMA flags for devices in
+channel configuration files, like sip.conf, iax.conf etc.
+
+\section{Fields of the CDR in Asterisk}
+
+\begin{itemize}
+ \item accountcode: What account number to use, (string, 20 characters)
+ \item src: Caller*ID number (string, 80 characters)
+ \item dst: Destination extension (string, 80 characters)
+ \item dcontext: Destination context (string, 80 characters)
+ \item clid: Caller*ID with text (80 characters)
+ \item channel: Channel used (80 characters)
+ \item dstchannel: Destination channel if appropriate (80 characters)
+ \item lastapp: Last application if appropriate (80 characters)
+ \item lastdata: Last application data (arguments) (80 characters)
+ \item start: Start of call (date/time)
+ \item answer: Answer of call (date/time)
+ \item end: End of call (date/time)
+ \item duration: Total time in system, in seconds (integer), from dial to hangup
+ \item billsec: Total time call is up, in seconds (integer), from answer to hangup
+ \item disposition: What happened to the call: ANSWERED, NO ANSWER, BUSY
+ \item amaflags: What flags to use: DOCUMENTATION, BILL, IGNORE etc,
+ specified on a per channel basis like accountcode.
+ \item user field: A user-defined field, maximum 255 characters
+\end{itemize}
+
+In some cases, uniqueid is appended:
+
+\begin{itemize}
+ \item uniqueid: Unique Channel Identifier (32 characters)
+ This needs to be enabled in the source code at compile time
+\end{itemize}
+
+NOTE: If you use IAX2 channels for your calls, and allow 'full' transfers
+(not media-only transfers), then when the calls is transferred the server
+in the middle will no longer be involved in the signaling path, and thus
+will not generate accurate CDRs for that call. If you can, use media-only
+transfers with IAX2 to avoid this problem, or turn off transfers completely
+(although this can result in a media latency increase since the media packets
+have to traverse the middle server(s) in the call).
+
+\section{CDR Variables}
+
+If the channel has a cdr, that cdr record has its own set of variables which
+can be accessed just like channel variables. The following builtin variables
+are available.
+
+\begin{verbatim}
+${CDR(clid)} Caller ID
+${CDR(src)} Source
+${CDR(dst)} Destination
+${CDR(dcontext)} Destination context
+${CDR(channel)} Channel name
+${CDR(dstchannel)} Destination channel
+${CDR(lastapp)} Last app executed
+${CDR(lastdata)} Last app's arguments
+${CDR(start)} Time the call started.
+${CDR(answer)} Time the call was answered.
+${CDR(end)} Time the call ended.
+${CDR(duration)} Duration of the call.
+${CDR(billsec)} Duration of the call once it was answered.
+${CDR(disposition)} ANSWERED, NO ANSWER, BUSY
+${CDR(amaflags)} DOCUMENTATION, BILL, IGNORE etc
+${CDR(accountcode)} The channel's account code.
+${CDR(uniqueid)} The channel's unique id.
+${CDR(userfield)} The channels uses specified field.
+\end{verbatim}
+
+In addition, you can set your own extra variables by using Set(CDR(name)=value).
+These variables can be output into a text-format CDR by using the cdr\_custom
+CDR driver; see the cdr\_custom.conf.sample file in the configs directory for
+an example of how to do this.
diff --git a/trunk/doc/tex/cdrdriver.tex b/trunk/doc/tex/cdrdriver.tex
new file mode 100644
index 000000000..174df5b68
--- /dev/null
+++ b/trunk/doc/tex/cdrdriver.tex
@@ -0,0 +1,458 @@
+Call data records can be stored in many different databases or even CSV text.
+
+\section{MSSQL}
+
+ Asterisk can currently store CDRs into an MSSQL database in
+ two different ways: cdr\_odbc or cdr\_tds
+
+ Call Data Records can be stored using unixODBC (which requires
+ the FreeTDS package) [cdr\_odbc] or directly by using just the
+ FreeTDS package [cdr\_tds] The following provide some
+ examples known to get asterisk working with mssql.
+
+ NOTE: Only choose one db connector.
+
+\subsection{ODBC using cdr\_odbc}
+ Compile, configure, and install the latest unixODBC package:
+\begin{astlisting}
+\begin{verbatim}
+ tar -zxvf unixODBC-2.2.9.tar.gz &&
+ cd unixODBC-2.2.9 &&
+ ./configure --sysconfdir=/etc --prefix=/usr --disable-gui &&
+ make &&
+ make install
+\end{verbatim}
+\end{astlisting}
+
+ Compile, configure, and install the latest FreeTDS package:
+\begin{astlisting}
+\begin{verbatim}
+ tar -zxvf freetds-0.62.4.tar.gz &&
+ cd freetds-0.62.4 &&
+ ./configure --prefix=/usr --with-tdsver=7.0 \
+ --with-unixodbc=/usr/lib &&
+ make && make install
+\end{verbatim}
+\end{astlisting}
+
+ Compile, or recompile, asterisk so that it will now add support
+ for cdr\_odbc.
+\begin{astlisting}
+\begin{verbatim}
+ make clean && ./configure --with-odbc &&
+ make update &&
+ make &&
+ make install
+\end{verbatim}
+\end{astlisting}
+
+ Setup odbc configuration files. These are working examples
+ from my system. You will need to modify for your setup.
+ You are not required to store usernames or passwords here.
+\begin{astlisting}
+\begin{verbatim}
+ /etc/odbcinst.ini
+ [FreeTDS]
+ Description = FreeTDS ODBC driver for MSSQL
+ Driver = /usr/lib/libtdsodbc.so
+ Setup = /usr/lib/libtdsS.so
+ FileUsage = 1
+
+ /etc/odbc.ini
+ [MSSQL-asterisk]
+ description = Asterisk ODBC for MSSQL
+ driver = FreeTDS
+ server = 192.168.1.25
+ port = 1433
+ database = voipdb
+ tds_version = 7.0
+ language = us_english
+\end{verbatim}
+\end{astlisting}
+
+ Only install one database connector. Do not confuse asterisk
+ by using both ODBC (cdr\_odbc) and FreeTDS (cdr\_tds).
+ This command will erase the contents of cdr\_tds.conf
+\begin{astlisting}
+\begin{verbatim}
+ [ -f /etc/asterisk/cdr_tds.conf ] > /etc/asterisk/cdr_tds.conf
+\end{verbatim}
+\end{astlisting}
+ NOTE: unixODBC requires the freeTDS package, but asterisk does
+ not call freeTDS directly.
+
+ Now set up cdr\_odbc configuration files. These are working samples
+ from my system. You will need to modify for your setup. Define
+ your usernames and passwords here, secure file as well.
+\begin{astlisting}
+\begin{verbatim}
+ /etc/asterisk/cdr_odbc.conf
+ [global]
+ dsn=MSSQL-asterisk
+ username=voipdbuser
+ password=voipdbpass
+ loguniqueid=yes
+\end{verbatim}
+\end{astlisting}
+ And finally, create the 'cdr' table in your mssql database.
+\begin{astlisting}
+\begin{verbatim}
+ CREATE TABLE cdr (
+ [calldate] [datetime] NOT NULL ,
+ [clid] [varchar] (80) NOT NULL ,
+ [src] [varchar] (80) NOT NULL ,
+ [dst] [varchar] (80) NOT NULL ,
+ [dcontext] [varchar] (80) NOT NULL ,
+ [channel] [varchar] (80) NOT NULL ,
+ [dstchannel] [varchar] (80) NOT NULL ,
+ [lastapp] [varchar] (80) NOT NULL ,
+ [lastdata] [varchar] (80) NOT NULL ,
+ [duration] [int] NOT NULL ,
+ [billsec] [int] NOT NULL ,
+ [disposition] [varchar] (45) NOT NULL ,
+ [amaflags] [int] NOT NULL ,
+ [accountcode] [varchar] (20) NOT NULL ,
+ [uniqueid] [varchar] (32) NOT NULL ,
+ [userfield] [varchar] (255) NOT NULL
+ )
+\end{verbatim}
+\end{astlisting}
+ Start asterisk in verbose mode, you should see that asterisk
+ logs a connection to the database and will now record every
+ call to the database when it's complete.
+
+\subsection{TDS, using cdr\_tds}
+ Compile, configure, and install the latest FreeTDS package:
+\begin{astlisting}
+\begin{verbatim}
+ tar -zxvf freetds-0.62.4.tar.gz &&
+ cd freetds-0.62.4 &&
+ ./configure --prefix=/usr --with-tdsver=7.0
+ make &&
+ make install
+\end{verbatim}
+\end{astlisting}
+ Compile, or recompile, asterisk so that it will now add support
+ for cdr\_tds.
+\begin{astlisting}
+\begin{verbatim}
+ make clean && ./configure --with-tds &&
+ make update &&
+ make &&
+ make install
+\end{verbatim}
+\end{astlisting}
+ Only install one database connector. Do not confuse asterisk
+ by using both ODBC (cdr\_odbc) and FreeTDS (cdr\_tds).
+ This command will erase the contents of cdr\_odbc.conf
+\begin{astlisting}
+\begin{verbatim}
+ [ -f /etc/asterisk/cdr_odbc.conf ] > /etc/asterisk/cdr_odbc.conf
+\end{verbatim}
+\end{astlisting}
+ Setup cdr\_tds configuration files. These are working samples
+ from my system. You will need to modify for your setup. Define
+ your usernames and passwords here, secure file as well.
+\begin{astlisting}
+\begin{verbatim}
+ /etc/asterisk/cdr_tds.conf
+ [global]
+ hostname=192.168.1.25
+ port=1433
+ dbname=voipdb
+ user=voipdbuser
+ password=voipdpass
+ charset=BIG5
+\end{verbatim}
+\end{astlisting}
+ And finally, create the 'cdr' table in your mssql database.
+\begin{astlisting}
+\begin{verbatim}
+ CREATE TABLE cdr (
+ [accountcode] [varchar] (20) NULL ,
+ [src] [varchar] (80) NULL ,
+ [dst] [varchar] (80) NULL ,
+ [dcontext] [varchar] (80) NULL ,
+ [clid] [varchar] (80) NULL ,
+ [channel] [varchar] (80) NULL ,
+ [dstchannel] [varchar] (80) NULL ,
+ [lastapp] [varchar] (80) NULL ,
+ [lastdata] [varchar] (80) NULL ,
+ [start] [datetime] NULL ,
+ [answer] [datetime] NULL ,
+ [end] [datetime] NULL ,
+ [duration] [int] NULL ,
+ [billsec] [int] NULL ,
+ [disposition] [varchar] (20) NULL ,
+ [amaflags] [varchar] (16) NULL ,
+ [uniqueid] [varchar] (32) NULL
+ )
+\end{verbatim}
+\end{astlisting}
+ Start asterisk in verbose mode, you should see that asterisk
+ logs a connection to the database and will now record every
+ call to the database when it's complete.
+
+
+\section{MYSQL}
+
+Using MySQL for CDR records is supported by using ODBC and the cdr\_odbc module.
+
+\section{PGSQL}
+ If you want to go directly to postgresql database, and have the cdr\_pgsql.so
+ compiled you can use the following sample setup.
+ On Debian, before compiling asterisk, just install libpqxx-dev.
+ Other distros will likely have a similiar package.
+
+ Once you have the compile done,
+ copy the sample cdr\_pgsql.conf file or create your own.
+
+ Here is a sample:
+\begin{astlisting}
+\begin{verbatim}
+ /etc/asterisk/cdr_pgsql.conf
+ ; Sample Asterisk config file for CDR logging to PostgresSQL
+ [global]
+ hostname=localhost
+ port=5432
+ dbname=asterisk
+ password=password
+ user=postgres
+ table=cdr
+\end{verbatim}
+\end{astlisting}
+ Now create a table in postgresql for your cdrs
+\begin{astlisting}
+\begin{verbatim}
+ CREATE TABLE cdr (
+ calldate time NOT NULL ,
+ clid varchar (80) NOT NULL ,
+ src varchar (80) NOT NULL ,
+ dst varchar (80) NOT NULL ,
+ dcontext varchar (80) NOT NULL ,
+ channel varchar (80) NOT NULL ,
+ dstchannel varchar (80) NOT NULL ,
+ lastapp varchar (80) NOT NULL ,
+ lastdata varchar (80) NOT NULL ,
+ duration int NOT NULL ,
+ billsec int NOT NULL ,
+ disposition varchar (45) NOT NULL ,
+ amaflags int NOT NULL ,
+ accountcode varchar (20) NOT NULL ,
+ uniqueid varchar (32) NOT NULL ,
+ userfield varchar (255) NOT NULL
+ );
+\end{verbatim}
+\end{astlisting}
+
+\section{SQLLITE}
+
+SQLite version 2 is supported in cdr\_sqlite.
+
+\section{RADIUS}
+
+\subsection{What is needed}
+
+\begin{itemize}
+ \item FreeRADIUS server
+ \item Radiusclient-ng library
+ \item Asterisk PBX
+\end{itemize}
+
+\begin{verbatim}
+ +--------------------+
+ | Asterisk PBX |
+ | |
+ |********************|
+ | | +---------------+
+ | RADIUS client |------->| RADIUS server |
+ | |<-------| (FreeRADIUS) |
+ +--------------------+ +---------------+
+\end{verbatim}
+
+
+
+\subsection{Steps to follow in order to have RADIUS support}
+
+\subsubsection{Installation of the Radiusclient library}
+
+ Download the sources from
+ \url{http://developer.berlios.de/projects/radiusclient-ng/}
+
+ Untar the source tarball:
+
+\begin{verbatim}
+ root@localhost:/usr/local/src# tar xvfz radiusclient-ng-0.5.2.tar.gz
+\end{verbatim}
+
+ Compile and install the library:
+
+\begin{verbatim}
+ root@localhost:/usr/local/src# cd radiusclient-ng-0.5.2
+ root@localhost:/usr/local/src/radiusclient-ng-0.5.2# ./configure
+ root@localhost:/usr/local/src/radiusclient-ng-0.5.2# make
+ root@localhost:/usr/local/src/radiusclient-ng-0.5.2# make install
+\end{verbatim}
+
+\subsubsection{Configuration of the Radiusclient library}
+
+ By default all the configuration files of the radiusclient library will
+ be in \path{/usr/local/etc/radiusclient-ng} directory.
+
+ File "radiusclient.conf"
+ Open the file and find lines containing the following:
+
+ authserver localhost
+
+ This is the hostname or IP address of the RADIUS server used for
+ authentication. You will have to change this unless the server is
+ running on the same host as your Asterisk PBX.
+
+ acctserver localhost
+
+ This is the hostname or IP address of the RADIUS server used for
+ accounting. You will have to change this unless the server is running
+ on the same host as your Asterisk PBX.
+
+ \textbf{File "servers"}
+
+ RADIUS protocol uses simple access control mechanism based on shared
+ secrets that allows RADIUS servers to limit access from RADIUS clients.
+
+ A RADIUS server is configured with a secret string and only RADIUS
+ clients that have the same secret will be accepted.
+
+ You need to configure a shared secret for each server you have
+ configured in radiusclient.conf file in the previous step. The shared
+ secrets are stored in \path{/usr/local/etc/radiusclient-ng/servers} file.
+
+ Each line contains hostname of a RADIUS server and shared secret
+ used in communication with that server. The two values are separated
+ by white spaces. Configure shared secrets for every RADIUS server you
+ are going to use.
+
+ \textbf{File "dictionary"}
+
+ Asterisk uses some attributes that are not included in the
+ dictionary of radiusclient library, therefore it is necessary to add
+ them. A file called dictionary.digium (kept in the contrib dir)
+ was created to list all new attributes used by Asterisk.
+ Add to the end of the main dictionary file
+ \path{/usr/local/etc/radiusclient-ng/dictionary} the line:
+
+ \$INCLUDE /path/to/dictionary.digium
+
+\subsubsection{Install FreeRADIUS Server (Version 1.1.1)}
+
+ Download sources tarball from:
+
+ \url{http://freeradius.org/}
+
+ Untar, configure, build, and install the server:
+
+\begin{verbatim}
+ root@localhost:/usr/local/src# tar xvfz freeradius-1.1.1.tar.gz
+ root@localhost:/usr/local/src# cd freeradius-1.1.1
+ root@localhost"/usr/local/src/freeradius-1.1.1# ./configure
+ root@localhost"/usr/local/src/freeradius-1.1.1# make
+ root@localhost"/usr/local/src/freeradius-1.1.1# make install
+\end{verbatim}
+
+ All the configuration files of FreeRADIUS server will be in
+ /usr/local/etc/raddb directory.
+
+
+\subsubsection{Configuration of the FreeRADIUS Server}
+
+ There are several files that have to be modified to configure the
+ RADIUS server. These are presented next.
+
+ File "clients.conf"
+
+ File \path{/usr/local/etc/raddb/clients.conf} contains description of
+ RADIUS clients that are allowed to use the server. For each of the
+ clients you need to specify its hostname or IP address and also a
+ shared secret. The shared secret must be the same string you configured
+ in radiusclient library.
+
+ Example:
+\begin{verbatim}
+ client myhost {
+ secret = mysecret
+ shortname = foo
+ }
+\end{verbatim}
+
+ This fragment allows access from RADIUS clients on "myhost" if they use
+ "mysecret" as the shared secret.
+ The file already contains an entry for localhost (127.0.0.1), so if you
+ are running the RADIUS server on the same host as your Asterisk server,
+ then modify the existing entry instead, replacing the default password.
+
+ File "dictionary"
+
+ Note: as of version 1.1.2, the dictionary.digium file ships with FreeRADIUS.
+ The following procedure brings the dictionary.digium file to previous versions
+ of FreeRADIUS.
+
+ File \path{/usr/local/etc/raddb/dictionary} contains the dictionary of
+ FreeRADIUS server. You have to add the same dictionary file
+ (dictionary.digium), which you added to the dictionary of radiusclient-ng
+ library. You can include it into the main file, adding the following line at the
+ end of file \path{/usr/local/etc/raddb/dictionary}:
+
+ \$INCLUDE /path/to/dictionary.digium
+
+ That will include the same new attribute definitions that are used
+ in radiusclient-ng library so the client and server will understand each
+ other.
+
+
+\subsubsection{Asterisk Accounting Configuration}
+
+ Compilation and installation:
+
+ The module will be compiled as long as the radiusclient-ng
+ library has been detected on your system.
+
+ By default FreeRADIUS server will log all accounting requests into
+ \path{/usr/local/var/log/radius/radacct} directory in form of plain text files.
+ The server will create one file for each hostname in the directory. The
+ following example shows how the log files look like.
+
+ Asterisk now generates Call Detail Records. See \path{/include/asterisk/cdr.h}
+ for all the fields which are recorded. By default, records in comma
+ separated values will be created in \path{/var/log/asterisk/cdr-csv}.
+
+ The configuration file for cdr\_radius.so module is \path{/etc/asterisk/cdr.conf}
+
+ This is where you can set CDR related parameters as well as the path to
+ the radiusclient-ng library configuration file.
+
+
+\section{Logged Values}
+\begin{verbatim}
+ "Asterisk-Acc-Code", The account name of detail records
+ "Asterisk-Src",
+ "Asterisk-Dst",
+ "Asterisk-Dst-Ctx", The destination context
+ "Asterisk-Clid",
+ "Asterisk-Chan", The channel
+ "Asterisk-Dst-Chan", (if applicable)
+ "Asterisk-Last-App", Last application run on the channel
+ "Asterisk-Last-Data", Argument to the last channel
+ "Asterisk-Start-Time",
+ "Asterisk-Answer-Time",
+ "Asterisk-End-Time",
+ "Asterisk-Duration", Duration is the whole length that the entire
+ call lasted. ie. call rx'd to hangup
+ "end time" minus "start time"
+ "Asterisk-Bill-Sec", The duration that a call was up after other
+ end answered which will be <= to duration
+ "end time" minus "answer time"
+ "Asterisk-Disposition", ANSWERED, NO ANSWER, BUSY
+ "Asterisk-AMA-Flags", DOCUMENTATION, BILL, IGNORE etc, specified on
+ a per channel basis like accountcode.
+ "Asterisk-Unique-ID", Unique call identifier
+ "Asterisk-User-Field" User field set via SetCDRUserField
+\end{verbatim}
diff --git a/trunk/doc/tex/chaniax.tex b/trunk/doc/tex/chaniax.tex
new file mode 100644
index 000000000..954e068b0
--- /dev/null
+++ b/trunk/doc/tex/chaniax.tex
@@ -0,0 +1,84 @@
+\subsection{Introduction}
+
+This section is intended as an introduction to the Inter-Asterisk
+eXchange v2 (or simply IAX2) protocol. It provides both a theoretical
+background and practical information on its use.
+
+\subsection{Why IAX2?}
+
+The first question most people are thinking at this point is "Why do you
+need another VoIP protocol? Why didn't you just use SIP or H.323?"
+
+Well, the answer is a fairly complicated one, but in a nutshell it's like
+this... Asterisk is intended as a very flexible and powerful
+communications tool. As such, the primary feature we need from a VoIP
+protocol is the ability to meet our own goals with Asterisk, and one with
+enough flexibility that we could use it as a kind of laboratory for
+inventing and implementing new concepts in the field. Neither H.323 or
+SIP fit the roles we needed, so we developed our own protocol, which,
+while not standards based, provides a number of advantages over both SIP
+and H.323, some of which are:
+
+\begin{itemize}
+ \item Interoperability with NAT/PAT/Masquerade firewalls
+ \begin{itemize}
+ \item IAX seamlessly interoperates through all sorts of NAT and PAT
+ and other firewalls, including the ability to place and
+ receive calls, and transfer calls to other stations.
+ \end{itemize}
+ \item High performance, low overhead protocol
+ \begin{itemize}
+ \item When running on low-bandwidth connections, or when running
+ large numbers of calls, optimized bandwidth utilization is
+ imperative. IAX uses only 4 bytes of overhead
+ \end{itemize}
+ \item Internationalization support
+ \begin{itemize}
+ \item IAX transmits language information, so that remote PBX
+ content can be delivered in the native language of the
+ calling party.
+ \end{itemize}
+ \item Remote dialplan polling
+ \begin{itemize}
+ \item IAX allows a PBX or IP phone to poll the availability of a
+ number from a remote server. This allows PBX dialplans to
+ be centralized.
+ \end{itemize}
+ \item Flexible authentication
+ \begin{itemize}
+ \item IAX supports cleartext, md5, and RSA authentication,
+ providing flexible security models for outgoing calls and
+ registration services.
+ \end{itemize}
+ \item Multimedia protocol
+ \begin{itemize}
+ \item IAX supports the transmission of voice, video, images, text,
+ HTML, DTMF, and URL's. Voice menus can be presented in both
+ audibly and visually.
+ \end{itemize}
+ \item Call statistic gathering
+ \begin{itemize}
+ \item IAX gathers statistics about network performance (including
+ latency and jitter, as well as providing end-to-end latency
+ measurement.
+ \end{itemize}
+ \item Call parameter communication
+ \begin{itemize}
+ \item Caller*ID, requested extension, requested context, etc are
+ all communicated through the call.
+ \end{itemize}
+ \item Single socket design
+ \begin{itemize}
+ \item IAX's single socket design allows up to 32768 calls to be
+ multiplexed.
+ \end{itemize}
+\end{itemize}
+
+While we value the importance of standards based (i.e. SIP) call handling,
+hopefully this will provide a reasonable explanation of why we developed
+IAX rather than starting with SIP.
+
+\subsection{Configuration}
+
+For examples of a configuration, please see the iax.conf.sample in
+your the /configs directory of you source code distribution.
diff --git a/trunk/doc/tex/channelvariables.tex b/trunk/doc/tex/channelvariables.tex
new file mode 100644
index 000000000..d6981ec0b
--- /dev/null
+++ b/trunk/doc/tex/channelvariables.tex
@@ -0,0 +1,974 @@
+\section{Introduction}
+
+There are two levels of parameter evaluation done in the Asterisk
+dial plan in extensions.conf.
+\begin{enumerate}
+\item The first, and most frequently used, is the substitution of variable
+ references with their values.
+\item Then there are the evaluations of expressions done in \$[ .. ].
+ This will be discussed below.
+\end{enumerate}
+Asterisk has user-defined variables and standard variables set
+by various modules in Asterisk. These standard variables are
+listed at the end of this document.
+
+\section{Parameter Quoting}
+\begin{astlisting}
+\begin{verbatim}
+exten => s,5,BackGround,blabla
+\end{verbatim}
+\end{astlisting}
+The parameter (blabla) can be quoted ("blabla"). In this case, a
+comma does not terminate the field. However, the double quotes
+will be passed down to the Background command, in this example.
+
+Also, characters special to variable substitution, expression evaluation, etc
+(see below), can be quoted. For example, to literally use a \$ on the
+string "\$1231", quote it with a preceding \textbackslash. Special characters that must
+be quoted to be used, are [ ] \$ " \textbackslash. (to write \textbackslash itself, use \textbackslash).
+
+These Double quotes and escapes are evaluated at the level of the
+asterisk config file parser.
+
+Double quotes can also be used inside expressions, as discussed below.
+
+\section{Variables}
+
+Parameter strings can include variables. Variable names are arbitrary strings.
+They are stored in the respective channel structure.
+
+To set a variable to a particular value, do:
+\begin{astlisting}
+\begin{verbatim}
+ exten => 1,2,Set(varname=value)
+\end{verbatim}
+\end{astlisting}
+You can substitute the value of a variable everywhere using \$\{variablename\}.
+For example, to stringwise append \$lala to \$blabla and store result in \$koko,
+do:
+\begin{astlisting}
+\begin{verbatim}
+ exten => 1,2,Set(koko=${blabla}${lala})
+\end{verbatim}
+\end{astlisting}
+
+There are two reference modes - reference by value and reference by name.
+To refer to a variable with its name (as an argument to a function that
+requires a variable), just write the name. To refer to the variable's value,
+enclose it inside \$\{\}. For example, Set takes as the first argument
+(before the =) a variable name, so:
+\begin{astlisting}
+\begin{verbatim}
+ exten => 1,2,Set(koko=lala)
+ exten => 1,3,Set(${koko}=blabla)
+\end{verbatim}
+\end{astlisting}
+stores to the variable "koko" the value "lala" and to variable "lala" the
+value "blabla".
+
+In fact, everything contained \$\{here\} is just replaced with the value of
+the variable "here".
+
+\section{Variable Inheritance}
+
+Variable names which are prefixed by "\_" will be inherited to channels
+that are created in the process of servicing the original channel in
+which the variable was set. When the inheritance takes place, the
+prefix will be removed in the channel inheriting the variable. If the
+name is prefixed by "\_\_" in the channel, then the variable is
+inherited and the "\_\_" will remain intact in the new channel.
+
+In the dialplan, all references to these variables refer to the same
+variable, regardless of having a prefix or not. Note that setting any
+version of the variable removes any other version of the variable,
+regardless of prefix.
+
+\subsection{Example}
+\begin{astlisting}
+\begin{verbatim}
+Set(__FOO=bar) ; Sets an inherited version of "FOO" variable
+Set(FOO=bar) ; Removes the inherited version and sets a local
+ ; variable.
+\end{verbatim}
+\end{astlisting}
+
+However, NoOp(\$\{\_\_FOO\}) is identical to NoOp(\$\{FOO\})
+
+\section{Selecting Characters from Variables}
+
+The format for selecting characters from a variable can be expressed as:
+\begin{astlisting}
+\begin{verbatim}
+ ${variable_name[:offset[:length]]}
+\end{verbatim}
+\end{astlisting}
+If you want to select the first N characters from the string assigned
+to a variable, simply append a colon and the number of characters to
+skip from the beginning of the string to the variable name.
+\begin{astlisting}
+\begin{verbatim}
+ ; Remove the first character of extension, save in "number" variable
+ exten => _9X.,1,Set(number=${EXTEN:1})
+\end{verbatim}
+\end{astlisting}
+Assuming we've dialed 918005551234, the value saved to the 'number' variable
+would be 18005551234. This is useful in situations when we require users to
+dial a number to access an outside line, but do not wish to pass the first
+digit.
+
+If you use a negative offset number, Asterisk starts counting from the end
+of the string and then selects everything after the new position. The following
+example will save the numbers 1234 to the 'number' variable, still assuming
+we've dialed 918005551234.
+\begin{astlisting}
+\begin{verbatim}
+ ; Remove everything before the last four digits of the dialed string
+ exten => _9X.,1,Set(number=${EXTEN:-4})
+\end{verbatim}
+\end{astlisting}
+We can also limit the number of characters from our offset position that we
+wish to use. This is done by appending a second colon and length value to the
+variable name. The following example will save the numbers 555 to the 'number'
+variable.
+\begin{astlisting}
+\begin{verbatim}
+ ; Only save the middle numbers 555 from the string 918005551234
+ exten => _9X.,1,Set(number=${EXTEN:5:3})
+\end{verbatim}
+\end{astlisting}
+The length value can also be used in conjunction with a negative offset. This
+may be useful if the length of the string is unknown, but the trailing digits
+are. The following example will save the numbers 555 to the 'number' variable,
+even if the string starts with more characters than expected (unlike the
+previous example).
+\begin{astlisting}
+\begin{verbatim}
+ ; Save the numbers 555 to the 'number' variable
+ exten => _9X.,1,Set(number=${EXTEN:-7:3})
+\end{verbatim}
+\end{astlisting}
+If a negative length value is entered, Asterisk will remove that many characters
+from the end of the string.
+\begin{astlisting}
+\begin{verbatim}
+ ; Set pin to everything but the trailing #.
+ exten => _XXXX#,1,Set(pin=${EXTEN:0:-1})
+\end{verbatim}
+\end{astlisting}
+
+\section{Expressions}
+
+Everything contained inside a bracket pair prefixed by a \$ (like \$[this]) is
+considered as an expression and it is evaluated. Evaluation works similar to
+(but is done on a later stage than) variable substitution: the expression
+(including the square brackets) is replaced by the result of the expression
+evaluation.
+
+For example, after the sequence:
+\begin{astlisting}
+\begin{verbatim}
+exten => 1,1,Set(lala=$[1 + 2])
+exten => 1,2,Set(koko=$[2 * ${lala}])
+\end{verbatim}
+\end{astlisting}
+the value of variable koko is "6".
+
+and, further:
+\begin{astlisting}
+\begin{verbatim}
+exten => 1,1,Set,(lala=$[ 1 + 2 ]);
+\end{verbatim}
+\end{astlisting}
+will parse as intended. Extra spaces are ignored.
+
+
+\subsection{Spaces Inside Variables Values}
+
+If the variable being evaluated contains spaces, there can be problems.
+
+For these cases, double quotes around text that may contain spaces
+will force the surrounded text to be evaluated as a single token.
+The double quotes will be counted as part of that lexical token.
+
+As an example:
+
+\begin{astlisting}
+\begin{verbatim}
+exten => s,6,GotoIf($[ "${CALLERID(name)}" : "Privacy Manager" ]?callerid-liar,s,1:s,7)
+\end{verbatim}
+\end{astlisting}
+
+The variable CALLERID(name) could evaluate to "DELOREAN MOTORS" (with a space)
+but the above will evaluate to:
+
+\begin{verbatim}
+"DELOREAN MOTORS" : "Privacy Manager"
+\end{verbatim}
+
+and will evaluate to 0.
+
+The above without double quotes would have evaluated to:
+
+\begin{verbatim}
+DELOREAN MOTORS : Privacy Manager
+\end{verbatim}
+
+and will result in syntax errors, because token DELOREAN is immediately
+followed by token MOTORS and the expression parser will not know how to
+evaluate this expression, because it does not match its grammar.
+
+\subsection{Operators}
+
+Operators are listed below in order of increasing precedence. Operators
+with equal precedence are grouped within \{ \} symbols.
+
+\begin{itemize}
+ \item \verb!expr1 | expr2!
+
+ Return the evaluation of expr1 if it is neither an empty string
+ nor zero; otherwise, returns the evaluation of expr2.
+
+ \item \verb!expr1 & expr2!
+
+ Return the evaluation of expr1 if neither expression evaluates to
+ an empty string or zero; otherwise, returns zero.
+
+ \item \verb+expr1 {=, >, >=, <, <=, !=} expr2+
+
+ Return the results of floating point comparison if both arguments are
+ numbers; otherwise, returns the results of string comparison
+ using the locale-specific collation sequence. The result of each
+ comparison is 1 if the specified relation is true, or 0 if the
+ relation is false.
+
+ \item \verb!expr1 {+, -} expr2!
+
+ Return the results of addition or subtraction of floating point-valued
+ arguments.
+
+ \item \verb!expr1 {*, /, %} expr2!
+
+ Return the results of multiplication, floating point division, or
+ remainder of arguments.
+
+ \item \verb!- expr1!
+
+ Return the result of subtracting expr1 from 0.
+ This, the unary minus operator, is right associative, and
+ has the same precedence as the ! operator.
+
+ \item \verb+! expr1+
+
+ Return the result of a logical complement of expr1.
+ In other words, if expr1 is null, 0, an empty string,
+ or the string "0", return a 1. Otherwise, return a 0.
+ It has the same precedence as the unary minus operator, and
+ is also right associative.
+
+ \item \verb!expr1 : expr2!
+
+ The `:' operator matches expr1 against expr2, which must be a
+ regular expression. The regular expression is anchored to the
+ beginning of the string with an implicit `\^'.
+
+ If the match succeeds and the pattern contains at least one regular
+ expression subexpression `\(...\)', the string corresponing
+ to `\textbackslash1' is returned; otherwise the matching operator
+ returns the number of characters matched. If the match fails and
+ the pattern contains a regular expression subexpression the null
+ string is returned; otherwise 0.
+
+ Normally, the double quotes wrapping a string are left as part
+ of the string. This is disastrous to the : operator. Therefore,
+ before the regex match is made, beginning and ending double quote
+ characters are stripped from both the pattern and the string.
+
+ \item \verb!expr1 =~ expr2!
+
+ Exactly the same as the ':' operator, except that the match is
+ not anchored to the beginning of the string. Pardon any similarity
+ to seemingly similar operators in other programming languages!
+ The ":" and "=\~" operators share the same precedence.
+
+ \item \verb!expr1 ? expr2 :: expr3!
+
+ Traditional Conditional operator. If expr1 is a number
+ that evaluates to 0 (false), expr3 is result of the this
+ expression evaluation. Otherwise, expr2 is the result.
+ If expr1 is a string, and evaluates to an empty string,
+ or the two characters (""), then expr3 is the
+ result. Otherwise, expr2 is the result. In Asterisk, all
+ 3 exprs will be "evaluated"; if expr1 is "true", expr2
+ will be the result of the "evaluation" of this
+ expression. expr3 will be the result otherwise. This
+ operator has the lowest precedence.
+\end{itemize}
+
+Parentheses are used for grouping in the usual manner.
+
+Operator precedence is applied as one would expect in any of the C
+or C derived languages.
+
+\subsection{Floating Point Numbers}
+
+In 1.6 and above, we shifted the \$[...] expressions to be calculated
+via floating point numbers instead of integers. We use 'long double' numbers
+when possible, which provide around 16 digits of precision with 12 byte numbers.
+
+To specify a floating point constant, the number has to have this format: D.D, where D is
+a string of base 10 digits. So, you can say 0.10, but you can't say .10 or 20.-- we hope
+this is not an excessive restriction!
+
+Floating point numbers are turned into strings via the '\%g'/'\%Lg' format of the printf
+function set. This allows numbers to still 'look' like integers to those counting
+on integer behavior. If you were counting on 1/4 evaluating to 0, you need to now say
+TRUNC(1/4). For a list of all the truncation/rounding capabilities, see the next section.
+
+
+\subsection{Functions}
+
+In 1.6 and above, we upgraded the \$[] expressions to handle floating point numbers.
+Because of this, folks counting on integer behavior would be disrupted. To make
+the same results possible, some rounding and integer truncation functions have been
+added to the core of the Expr2 parser. Indeed, dialplan functions can be called from
+\$[..] expressions without the \$\{...\} operators. The only trouble might be in the fact that
+the arguments to these functions must be specified with a comma. If you try to call
+the MATH function, for example, and try to say 3 + MATH(7*8), the expression parser will
+evaluate 7*8 for you into 56, and the MATH function will most likely complain that its
+input doesn't make any sense.
+
+We also provide access to most of the floating point functions in the C library. (but not all of them).
+
+While we don't expect someone to want to do Fourier analysis in the dialplan, we
+don't want to preclude it, either.
+
+Here is a list of the 'builtin' functions in Expr2. All other dialplan functions
+are available by simply calling them (read-only). In other words, you don't need to
+surround function calls in \$[...] expressions with \$\{...\}. Don't jump to conclusions,
+though! -- you still need to wrap variable names in curly braces!
+
+\begin{enumerate}
+\item COS(x) x is in radians. Results vary from -1 to 1.
+\item SIN(x) x is in radians. Results vary from -1 to 1.
+\item TAN(x) x is in radians.
+\item ACOS(x) x should be a value between -1 and 1.
+\item ASIN(x) x should be a value between -1 and 1.
+\item ATAN(x) returns the arc tangent in radians; between -PI/2 and PI/2.
+\item ATAN2(x,y) returns a result resembling y/x, except that the signs of both args are used to determine the quadrant of the result. Its result is in radians, between -PI and PI.
+\item POW(x,y) returns the value of x raised to the power of y.
+\item SQRT(x) returns the square root of x.
+\item FLOOR(x) rounds x down to the nearest integer.
+\item CEIL(x) rounds x up to the nearest integer.
+\item ROUND(x) rounds x to the nearest integer, but round halfway cases away from zero.
+\item RINT(x) rounds x to the nearest integer, rounding halfway cases to the nearest even integer.
+\item TRUNC(x) rounds x to the nearest integer not larger in absolute value.
+\item REMAINDER(x,y) computes the remainder of dividing x by y. The return value is x - n*y, where n is the value x/y, rounded to the nearest integer. If this quotient is 1/2, it is rounded to the nearest even number.
+\item EXP(x) returns e to the x power.
+\item EXP2(x) returns 2 to the x power.
+\item LOG(x) returns the natural logarithm of x.
+\item LOG2(x) returns the base 2 log of x.
+\item LOG10(x) returns the base 10 log of x.
+\end{enumerate}
+
+\subsection{Examples}
+
+\begin{astlisting}
+\begin{verbatim}
+ "One Thousand Five Hundred" =~ "(T[^ ]+)"
+ returns: Thousand
+
+ "One Thousand Five Hundred" =~ "T[^ ]+"
+ returns: 8
+
+ "One Thousand Five Hundred" : "T[^ ]+"
+ returns: 0
+
+ "8015551212" : "(...)"
+ returns: 801
+
+ "3075551212":"...(...)"
+ returns: 555
+
+ ! "One Thousand Five Hundred" =~ "T[^ ]+"
+ returns: 0 (because it applies to the string, which is non-null,
+ which it turns to "0", and then looks for the pattern
+ in the "0", and doesn't find it)
+
+ !( "One Thousand Five Hundred" : "T[^ ]+" )
+ returns: 1 (because the string doesn't start with a word starting
+ with T, so the match evals to 0, and the ! operator
+ inverts it to 1 ).
+
+ 2 + 8 / 2
+ returns 6. (because of operator precedence; the division is done first, then the addition).
+
+ 2+8/2
+ returns 6. Spaces aren't necessary.
+
+(2+8)/2
+ returns 5, of course.
+
+(3+8)/2
+ returns 5.5 now.
+
+TRUNC((3+8)/2)
+ returns 5.
+
+FLOOR(2.5)
+ returns 2
+
+FLOOR(-2.5)
+ returns -3
+
+CEIL(2.5)
+ returns 3.
+
+CEIL(-2.5)
+ returns -2.
+
+ROUND(2.5)
+ returns 3.
+
+ROUND(3.5)
+ returns 4.
+
+ROUND(-2.5)
+ returns -3
+
+RINT(2.5)
+ returns 2.
+
+RINT(3.5)
+ returns 4.
+
+RINT(-2.5)
+ returns -2.
+
+RINT(-3.5)
+ returns -4.
+
+TRUNC(2.5)
+ returns 2.
+
+TRUNC(3.5)
+ returns 3.
+
+TRUNC(-3.5)
+ returns -3.
+\end{verbatim}
+\end{astlisting}
+
+Of course, all of the above examples use constants, but would work the
+same if any of the numeric or string constants were replaced with a
+variable reference \$\{CALLERID(num)\}, for instance.
+
+
+\subsection{Numbers Vs. Strings}
+
+Tokens consisting only of numbers are converted to 'long double' if possible, which
+are from 80 bits to 128 bits depending on the OS, compiler, and hardware.
+This means that overflows can occur when the
+numbers get above 18 digits (depending on the number of bits involved). Warnings will appear in the logs in this
+case.
+
+\subsection{Conditionals}
+
+There is one conditional application - the conditional goto :
+\begin{astlisting}
+\begin{verbatim}
+ exten => 1,2,GotoIf(condition?label1:label2)
+\end{verbatim}
+\end{astlisting}
+
+If condition is true go to label1, else go to label2. Labels are interpreted
+exactly as in the normal goto command.
+
+"condition" is just a string. If the string is empty or "0", the condition
+is considered to be false, if it's anything else, the condition is true.
+This is designed to be used together with the expression syntax described
+above, eg :
+
+\begin{astlisting}
+\begin{verbatim}
+ exten => 1,2,GotoIf($[${CALLERID(all)} = 123456]?2,1:3,1)
+\end{verbatim}
+\end{astlisting}
+
+Example of use :
+\begin{astlisting}
+\begin{verbatim}
+exten => s,2,Set(vara=1)
+exten => s,3,Set(varb=$[${vara} + 2])
+exten => s,4,Set(varc=$[${varb} * 2])
+exten => s,5,GotoIf($[${varc} = 6]?99,1:s,6)
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Parse Errors}
+
+Syntax errors are now output with 3 lines.
+
+If the extensions.conf file contains a line like:
+
+\begin{astlisting}
+\begin{verbatim}
+exten => s,6,GotoIf($[ "${CALLERID(num)}" = "3071234567" & & "${CALLERID(name)}" : "Privacy Manager" ]?callerid-liar,s,1:s,7)
+\end{verbatim}
+\end{astlisting}
+
+You may see an error in \path{/var/log/asterisk/messages} like this:
+\begin{astlisting}
+\begin{verbatim}
+Jul 15 21:27:49 WARNING[1251240752]: ast_yyerror(): syntax error: parse error, unexpected TOK_AND, expecting TOK_MINUS or TOK_LP or TOKEN; Input:
+"3072312154" = "3071234567" & & "Steves Extension" : "Privacy Manager"
+ ^
+\end{verbatim}
+\end{astlisting}
+
+The log line tells you that a syntax error was encountered. It now
+also tells you (in grand standard bison format) that it hit an "AND"
+(\&) token unexpectedly, and that was hoping for for a MINUS (-), LP
+(left parenthesis), or a plain token (a string or number).
+
+The next line shows the evaluated expression, and the line after
+that, the position of the parser in the expression when it became confused,
+marked with the "\^" character.
+
+\subsection{NULL Strings}
+Testing to see if a string is null can be done in one of two different ways:
+\begin{astlisting}
+\begin{verbatim}
+ exten => _XX.,1,GotoIf($["${calledid}" != ""]?3)
+ or
+ exten => _XX.,1,GotoIf($[foo${calledid} != foo]?3)
+\end{verbatim}
+\end{astlisting}
+
+The second example above is the way suggested by the WIKI. It will
+work as long as there are no spaces in the evaluated value.
+
+The first way should work in all cases, and indeed, might now
+be the safest way to handle this situation.
+
+\subsection{Warning}
+
+If you need to do complicated things with strings, asterisk expressions
+is most likely NOT the best way to go about it. AGI scripts are an
+excellent option to this need, and make available the full power of
+whatever language you desire, be it Perl, C, C++, Cobol, RPG, Java,
+Snobol, PL/I, Scheme, Common Lisp, Shell scripts, Tcl, Forth, Modula,
+Pascal, APL, assembler, etc.
+
+\subsection{Incompatabilities}
+
+The asterisk expression parser has undergone some evolution. It is hoped
+that the changes will be viewed as positive.
+
+The "original" expression parser had a simple, hand-written scanner,
+and a simple bison grammar. This was upgraded to a more involved bison
+grammar, and a hand-written scanner upgraded to allow extra spaces,
+and to generate better error diagnostics. This upgrade required bison
+1.85, and part of the user community felt the pain of having to
+upgrade their bison version.
+
+The next upgrade included new bison and flex input files, and the makefile
+was upgraded to detect current version of both flex and bison, conditionally
+compiling and linking the new files if the versions of flex and bison would
+allow it.
+
+If you have not touched your extensions.conf files in a year or so, the
+above upgrades may cause you some heartburn in certain circumstances, as
+several changes have been made, and these will affect asterisk's behavior on
+legacy extension.conf constructs. The changes have been engineered
+to minimize these conflicts, but there are bound to be problems.
+
+The following list gives some (and most likely, not all) of areas
+of possible concern with "legacy" extension.conf files:
+
+\begin{enumerate}
+\item Tokens separated by space(s).
+ Previously, tokens were separated by spaces. Thus, ' 1 + 1 ' would evaluate
+ to the value '2', but '1+1' would evaluate to the string '1+1'. If this
+ behavior was depended on, then the expression evaluation will break. '1+1'
+ will now evaluate to '2', and something is not going to work right.
+ To keep such strings from being evaluated, simply wrap them in double
+ quotes: ' "1+1" '
+
+\item The colon operator. In versions previous to double quoting, the
+ colon operator takes the right hand string, and using it as a
+ regex pattern, looks for it in the left hand string. It is given
+ an implicit \^ operator at the beginning, meaning the pattern
+ will match only at the beginning of the left hand string.
+ If the pattern or the matching string had double quotes around
+ them, these could get in the way of the pattern match. Now,
+ the wrapping double quotes are stripped from both the pattern
+ and the left hand string before applying the pattern. This
+ was done because it recognized that the new way of
+ scanning the expression doesn't use spaces to separate tokens,
+ and the average regex expression is full of operators that
+ the scanner will recognize as expression operators. Thus, unless
+ the pattern is wrapped in double quotes, there will be trouble.
+ For instance, \$\{VAR1\} : (Who$|$What*)+
+ may have have worked before, but unless you wrap the pattern
+ in double quotes now, look out for trouble! This is better:
+ "\$\{VAR1\}" : "(Who$|$What*)+"
+ and should work as previous.
+
+\item Variables and Double Quotes
+ Before these changes, if a variable's value contained one or more double
+ quotes, it was no reason for concern. It is now!
+
+\item LE, GE, NE operators removed. The code supported these operators,
+ but they were not documented. The symbolic operators, $<$=, $>$=, and !=
+ should be used instead.
+
+\item Added the unary '-' operator. So you can 3+ -4 and get -1.
+
+\item Added the unary '!' operator, which is a logical complement.
+ Basically, if the string or number is null, empty, or '0',
+ a '1' is returned. Otherwise a '0' is returned.
+
+\item Added the '=~' operator, just in case someone is just looking for
+ match anywhere in the string. The only diff with the ':' is that
+ match doesn't have to be anchored to the beginning of the string.
+
+\item Added the conditional operator 'expr1 ? true\_expr : false\_expr'
+ First, all 3 exprs are evaluated, and if expr1 is false, the 'false\_expr'
+ is returned as the result. See above for details.
+
+\item Unary operators '-' and '!' were made right associative.
+\end{enumerate}
+
+\subsection{Debugging Hints}
+
+There are two utilities you can build to help debug the \$[ ] in
+your extensions.conf file.
+
+The first, and most simplistic, is to issue the command:
+\begin{astlisting}
+\begin{verbatim}
+make testexpr2
+\end{verbatim}
+\end{astlisting}
+in the top level asterisk source directory. This will build a small
+executable, that is able to take the first command line argument, and
+run it thru the expression parser. No variable substitutions will be
+performed. It might be safest to wrap the expression in single
+quotes...
+\begin{astlisting}
+\begin{verbatim}
+testexpr2 '2*2+2/2'
+\end{verbatim}
+\end{astlisting}
+is an example.
+
+And, in the utils directory, you can say:
+\begin{astlisting}
+\begin{verbatim}
+make check_expr
+\end{verbatim}
+\end{astlisting}
+and a small program will be built, that will check the file mentioned
+in the first command line argument, for any expressions that might be
+have problems when you move to flex-2.5.31. It was originally
+designed to help spot possible incompatibilities when moving from the
+pre-2.5.31 world to the upgraded version of the lexer.
+
+But one more capability has been added to check\_expr, that might make
+it more generally useful. It now does a simple minded evaluation of
+all variables, and then passes the \$[] exprs to the parser. If there
+are any parse errors, they will be reported in the log file. You can
+use check\_expr to do a quick sanity check of the expressions in your
+extensions.conf file, to see if they pass a crude syntax check.
+
+The "simple-minded" variable substitution replaces \$\{varname\} variable
+references with '555'. You can override the 555 for variable values,
+by entering in var=val arguments after the filename on the command
+line. So...
+\begin{astlisting}
+\begin{verbatim}
+ check_expr /etc/asterisk/extensions.conf CALLERID(num)=3075551212 DIALSTATUS=TORTURE EXTEN=121
+\end{verbatim}
+\end{astlisting}
+will substitute any \$\{CALLERID(num)\} variable references with
+3075551212, any \$\{DIALSTATUS\} variable references with 'TORTURE', and
+any \$\{EXTEN\} references with '121'. If there is any fancy stuff
+going on in the reference, like \$\{EXTEN:2\}, then the override will
+not work. Everything in the \$\{...\} has to match. So, to substitute
+\$\{EXTEN:2\} references, you'd best say:
+\begin{astlisting}
+\begin{verbatim}
+ check_expr /etc/asterisk/extensions.conf CALLERID(num)=3075551212 DIALSTATUS=TORTURE EXTEN:2=121
+\end{verbatim}
+\end{astlisting}
+on stdout, you will see something like:
+
+\begin{astlisting}
+\begin{verbatim}
+ OK -- $[ "${DIALSTATUS}" = "TORTURE" | "${DIALSTATUS}" = "DONTCALL" ] at line 416
+\end{verbatim}
+\end{astlisting}
+
+In the expr2\_log file that is generated, you will see:
+
+\begin{astlisting}
+\begin{verbatim}
+ line 416, evaluation of $[ "TORTURE" = "TORTURE" | "TORTURE" = "DONTCALL" ] result: 1
+\end{verbatim}
+\end{astlisting}
+
+check\_expr is a very simplistic algorithm, and it is far from being
+guaranteed to work in all cases, but it is hoped that it will be
+useful.
+
+\section{Asterisk standard channel variables}
+
+There are a number of variables that are defined or read
+by Asterisk. Here is a list of them. More information is
+available in each application's help text. All these variables
+are in UPPER CASE only.
+
+Variables marked with a * are builtin functions and can't be set,
+only read in the dialplan. Writes to such variables are silently
+ignored.
+
+\begin{verbatim}
+${CDR(accountcode)} * Account code (if specified)
+${BLINDTRANSFER} The name of the channel on the other side of a blind transfer
+${BRIDGEPEER} Bridged peer
+${CALLERID(ani)} * Caller ANI (PRI channels)
+${CALLERID(ani2)} * ANI2 (Info digits) also called Originating line information or OLI
+${CALLERID(all)} * Caller ID
+${CALLERID(dnid)} * Dialed Number Identifier
+${CALLERID(name)} * Caller ID Name only
+${CALLERID(num)} * Caller ID Number only
+${CALLERID(rdnis)} * Redirected Dial Number ID Service
+${CALLINGANI2} * Caller ANI2 (PRI channels)
+${CALLINGPRES} * Caller ID presentation for incoming calls (PRI channels)
+${CALLINGTNS} * Transit Network Selector (PRI channels)
+${CALLINGTON} * Caller Type of Number (PRI channels)
+${CHANNEL} * Current channel name
+${CONTEXT} * Current context
+${DATETIME} * Current date time in the format: DDMMYYYY-HH:MM:SS
+ (Deprecated; use ${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)})
+${DB_RESULT} Result value of DB_EXISTS() dial plan function
+${EPOCH} * Current unix style epoch
+${EXTEN} * Current extension
+${ENV(VAR)} Environmental variable VAR
+${GOTO_ON_BLINDXFR} Transfer to the specified context/extension/priority
+ after a blind transfer (use ^ characters in place of
+ | to separate context/extension/priority when setting
+ this variable from the dialplan)
+${HANGUPCAUSE} * Asterisk cause of hangup (inbound/outbound)
+${HINT} * Channel hints for this extension
+${HINTNAME} * Suggested Caller*ID name for this extension
+${INVALID_EXTEN} The invalid called extension (used in the "i" extension)
+${LANGUAGE} * Current language (Deprecated; use ${LANGUAGE()})
+${LEN(VAR)} * String length of VAR (integer)
+${PRIORITY} * Current priority in the dialplan
+${PRIREDIRECTREASON} Reason for redirect on PRI, if a call was directed
+${TIMESTAMP} * Current date time in the format: YYYYMMDD-HHMMSS
+ (Deprecated; use ${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)})
+${TRANSFER_CONTEXT} Context for transferred calls
+${FORWARD_CONTEXT} Context for forwarded calls
+${UNIQUEID} * Current call unique identifier
+${SYSTEMNAME} * value of the systemname option of asterisk.conf
+\end{verbatim}
+
+\subsection{Application return values}
+
+In Asterisk 1.2, many applications return the result in a variable
+instead of, as in Asterisk 1.0, changing the dial plan priority (+101).
+For the various status values, see each application's help text.
+\begin{verbatim}
+${AGISTATUS} * agi()
+${AQMSTATUS} * addqueuemember()
+${AVAILSTATUS} * chanisavail()
+${CHECKGROUPSTATUS} * checkgroup()
+${CHECKMD5STATUS} * checkmd5()
+${CPLAYBACKSTATUS} * controlplayback()
+${DIALSTATUS} * dial()
+${DBGETSTATUS} * dbget()
+${ENUMSTATUS} * enumlookup()
+${HASVMSTATUS} * hasnewvoicemail()
+${LOOKUPBLSTATUS} * lookupblacklist()
+${OSPAUTHSTATUS} * ospauth()
+${OSPLOOKUPSTATUS} * osplookup()
+${OSPNEXTSTATUS} * ospnext()
+${OSPFINISHSTATUS} * ospfinish()
+${PARKEDAT} * parkandannounce()
+${PLAYBACKSTATUS} * playback()
+${PQMSTATUS} * pausequeuemember()
+${PRIVACYMGRSTATUS} * privacymanager()
+${QUEUESTATUS} * queue()
+${RQMSTATUS} * removequeuemember()
+${SENDIMAGESTATUS} * sendimage()
+${SENDTEXTSTATUS} * sendtext()
+${SENDURLSTATUS} * sendurl()
+${SYSTEMSTATUS} * system()
+${TRANSFERSTATUS} * transfer()
+${TXTCIDNAMESTATUS} * txtcidname()
+${UPQMSTATUS} * unpausequeuemember()
+${VMSTATUS} * voicmail()
+${VMBOXEXISTSSTATUS} * vmboxexists()
+${WAITSTATUS} * waitforsilence()
+\end{verbatim}
+
+\subsection{Various application variables}
+\begin{verbatim}
+${CURL} * Resulting page content for curl()
+${ENUM} * Result of application EnumLookup
+${EXITCONTEXT} Context to exit to in IVR menu (app background())
+ or in the RetryDial() application
+${MONITOR} * Set to "TRUE" if the channel is/has been monitored (app monitor())
+${MONITOR_EXEC} Application to execute after monitoring a call
+${MONITOR_EXEC_ARGS} Arguments to application
+${MONITOR_FILENAME} File for monitoring (recording) calls in queue
+${QUEUE_PRIO} Queue priority
+${QUEUE_MAX_PENALTY} Maximum member penalty allowed to answer caller
+${QUEUE_MIN_PENALTY} Minimum member penalty allowed to answer caller
+${QUEUESTATUS} Status of the call, one of:
+ (TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL)
+${RECORDED_FILE} * Recorded file in record()
+${TALK_DETECTED} * Result from talkdetect()
+${TOUCH_MONITOR} The filename base to use with Touch Monitor (auto record)
+${TOUCH_MONITOR_PREF} * The prefix for automonitor recording filenames.
+${TOUCH_MONITOR_FORMAT} The audio format to use with Touch Monitor (auto record)
+${TOUCH_MONITOR_OUTPUT} * Recorded file from Touch Monitor (auto record)
+${TXTCIDNAME} * Result of application TXTCIDName
+${VPB_GETDTMF} chan_vpb
+\end{verbatim}
+
+\subsection{The MeetMe Conference Bridge}
+\begin{verbatim}
+${MEETME_RECORDINGFILE} Name of file for recording a conference with
+ the "r" option
+${MEETME_RECORDINGFORMAT} Format of file to be recorded
+${MEETME_EXIT_CONTEXT} Context for exit out of meetme meeting
+${MEETME_AGI_BACKGROUND} AGI script for Meetme (zap only)
+${MEETMESECS} * Number of seconds a user participated in a MeetMe conference
+${CONF_LIMIT_TIMEOUT_FILE} File to play when time is up. Used with the L() option.
+${CONF_LIMIT_WARNING_FILE} File to play as warning if 'y' is defined.
+ The default is to say the time remaining. Used with the L() option.
+\end{verbatim}
+
+\subsection{The VoiceMail() application}
+\begin{verbatim}
+${VM_CATEGORY} Sets voicemail category
+${VM_NAME} * Full name in voicemail
+${VM_DUR} * Voicemail duration
+${VM_MSGNUM} * Number of voicemail message in mailbox
+${VM_CALLERID} * Voicemail Caller ID (Person leaving vm)
+${VM_CIDNAME} * Voicemail Caller ID Name
+${VM_CIDNUM} * Voicemail Caller ID Number
+${VM_DATE} * Voicemail Date
+${VM_MESSAGEFILE} * Path to message left by caller
+\end{verbatim}
+
+\subsection{The VMAuthenticate() application}
+\begin{verbatim}
+${AUTH_MAILBOX} * Authenticated mailbox
+${AUTH_CONTEXT} * Authenticated mailbox context
+\end{verbatim}
+
+\subsection{DUNDiLookup()}
+\begin{verbatim}
+${DUNDTECH} * The Technology of the result from a call to DUNDiLookup()
+${DUNDDEST} * The Destination of the result from a call to DUNDiLookup()
+\end{verbatim}
+
+\subsection{chan\_zap}
+\begin{verbatim}
+${ANI2} * The ANI2 Code provided by the network on the incoming call.
+ (ie, Code 29 identifies call as a Prison/Inmate Call)
+${CALLTYPE} * Type of call (Speech, Digital, etc)
+${CALLEDTON} * Type of number for incoming PRI extension
+ i.e. 0=unknown, 1=international, 2=domestic, 3=net_specific,
+ 4=subscriber, 6=abbreviated, 7=reserved
+${CALLINGSUBADDR} * Called PRI Subaddress
+${FAXEXTEN} * The extension called before being redirected to "fax"
+${PRIREDIRECTREASON} * Reason for redirect, if a call was directed
+${SMDI_VM_TYPE} * When an call is received with an SMDI message, the 'type'
+ of message 'b' or 'u'
+\end{verbatim}
+
+\subsection{chan\_sip}
+\begin{verbatim}
+${SIPCALLID} * SIP Call-ID: header verbatim (for logging or CDR matching)
+${SIPDOMAIN} * SIP destination domain of an inbound call (if appropriate)
+${SIPUSERAGENT} * SIP user agent (deprecated)
+${SIPURI} * SIP uri
+${SIP_CODEC} Set the SIP codec for a call
+${SIP_URI_OPTIONS} * additional options to add to the URI for an outgoing call
+${RTPAUDIOQOS} RTCP QoS report for the audio of this call
+${RTPVIDEOQOS} RTCP QoS report for the video of this call
+\end{verbatim}
+
+\subsection{chan\_agent}
+\begin{verbatim}
+${AGENTMAXLOGINTRIES} Set the maximum number of failed logins
+${AGENTUPDATECDR} Whether to update the CDR record with Agent channel data
+${AGENTGOODBYE} Sound file to use for "Good Bye" when agent logs out
+${AGENTACKCALL} Whether the agent should acknowledge the incoming call
+${AGENTAUTOLOGOFF} Auto logging off for an agent
+${AGENTWRAPUPTIME} Setting the time for wrapup between incoming calls
+${AGENTNUMBER} * Agent number (username) set at login
+${AGENTSTATUS} * Status of login ( fail | on | off )
+${AGENTEXTEN} * Extension for logged in agent
+\end{verbatim}
+
+
+\subsection{The Dial() application}
+\begin{verbatim}
+${DIALEDPEERNAME} * Dialed peer name
+${DIALEDPEERNUMBER} * Dialed peer number
+${DIALEDTIME} * Time for the call (seconds)
+${ANSWEREDTIME} * Time from dial to answer (seconds)
+${DIALSTATUS} * Status of the call, one of:
+ (CHANUNAVAIL | CONGESTION | BUSY | NOANSWER
+ | ANSWER | CANCEL | DONTCALL | TORTURE)
+${DYNAMIC_FEATURES} * The list of features (from the [applicationmap] section of
+ features.conf) to activate during the call, with feature
+ names separated by '#' characters
+${LIMIT_PLAYAUDIO_CALLER} Soundfile for call limits
+${LIMIT_PLAYAUDIO_CALLEE} Soundfile for call limits
+${LIMIT_WARNING_FILE} Soundfile for call limits
+${LIMIT_TIMEOUT_FILE} Soundfile for call limits
+${LIMIT_CONNECT_FILE} Soundfile for call limits
+${OUTBOUND_GROUP} Default groups for peer channels (as in SetGroup)
+ * See "show application dial" for more information
+\end{verbatim}
+
+\subsection{The chanisavail() application}
+\begin{verbatim}
+${AVAILCHAN} * the name of the available channel if one was found
+${AVAILORIGCHAN} * the canonical channel name that was used to create the channel
+${AVAILSTATUS} * Status of requested channel
+\end{verbatim}
+
+\subsection{Dialplan Macros}
+\begin{verbatim}
+${MACRO_EXTEN} * The calling extensions
+${MACRO_CONTEXT} * The calling context
+${MACRO_PRIORITY} * The calling priority
+${MACRO_OFFSET} Offset to add to priority at return from macro
+\end{verbatim}
+
+\subsection{The ChanSpy() application}
+\begin{verbatim}
+${SPYGROUP} * A ':' (colon) separated list of group names.
+ (To be set on spied on channel and matched against the g(grp) option)
+\end{verbatim}
+
+\subsection{OSP}
+\begin{verbatim}
+${OSPINHANDLE} OSP handle of in_bound call
+${OSPINTIMELIMIT} Duration limit for in_bound call
+${OSPOUTHANDLE} OSP handle of out_bound call
+${OSPTECH} OSP technology
+${OSPDEST} OSP destination
+${OSPCALLING} OSP calling number
+${OSPOUTTOKEN} OSP token to use for out_bound call
+${OSPOUTTIMELIMIT} Duration limit for out_bound call
+${OSPRESULTS} Number of remained destinations
+\end{verbatim}
diff --git a/trunk/doc/tex/cliprompt.tex b/trunk/doc/tex/cliprompt.tex
new file mode 100644
index 000000000..42e6b4bd1
--- /dev/null
+++ b/trunk/doc/tex/cliprompt.tex
@@ -0,0 +1,29 @@
+\subsubsection{Changing the CLI Prompt}
+
+The CLI prompt is set with the ASTERISK\_PROMPT UNIX environment variable that
+you set from the Unix shell before starting Asterisk
+
+You may include the following variables, that will be replaced by
+the current value by Asterisk:
+
+\begin{itemize}
+ \item \%d - Date (year-month-date)
+ \item \%s - Asterisk system name (from asterisk.conf)
+ \item \%h - Full hostname
+ \item \%H - Short hostname
+ \item \%t - Time
+ \item \%\% - Percent sign
+ \item \%\# - '\#' if Asterisk is run in console mode, '$>$' if running as remote console
+ \item \%Cn[;n] - Change terminal foreground (and optional background) color to specified
+ A full list of colors may be found in \path{include/asterisk/term.h}
+\end{itemize}
+
+On Linux systems, you may also use:
+
+\begin{itemize}
+ \item \%l1 - Load average over past minute
+ \item \%l2 - Load average over past 5 minutes
+ \item \%l3 - Load average over past 15 minutes
+ \item \%l4 - Process fraction (processes running / total processes)
+ \item \%l5 - The most recently allocated pid
+\end{itemize}
diff --git a/trunk/doc/tex/configuration.tex b/trunk/doc/tex/configuration.tex
new file mode 100644
index 000000000..9257a86ba
--- /dev/null
+++ b/trunk/doc/tex/configuration.tex
@@ -0,0 +1,225 @@
+\subsubsection{Introduction}
+
+The Asterisk configuration parser in the 1.2 version
+and beyond series has been improved in a number of ways. In
+addition to the realtime architecture, we now have the ability to create
+templates in configuration files, and use these as templates when we
+configure phones, voicemail accounts and queues.
+
+These changes are general to the configuration parser, and works in
+all configuration files.
+
+\subsubsection{General syntax}
+Asterisk configuration files are defined as follows:
+
+\begin{astlisting}
+\begin{verbatim}
+ [section]
+ label = value
+ label2 = value
+\end{verbatim}
+\end{astlisting}
+
+In some files, (e.g. mgcp.conf, zapata.conf and agents.conf), the syntax
+is a bit different. In these files the syntax is as follows:
+
+\begin{astlisting}
+\begin{verbatim}
+ [section]
+ label1 = value1
+ label2 = value2
+ object => name
+
+ label3 = value3
+ label2 = value4
+ object2 => name2
+\end{verbatim}
+\end{astlisting}
+
+In this syntax, we create objects with the settings defined above the object
+creation. Note that settings are inherited from the top, so in the example
+above object2 has inherited the setting for "label1" from the first object.
+
+For template configurations, the syntax for defining a section is changed
+to:
+\begin{astlisting}
+\begin{verbatim}
+ [section](options)
+ label = value
+\end{verbatim}
+\end{astlisting}
+
+The options field is used to define templates, refer to templates and hide
+templates. Any object can be used as a template.
+
+No whitespace is allowed between the closing "]" and the parenthesis "(".
+
+\subsubsection{Comments}
+
+All lines that starts with semi-colon ";" is treated as comments
+and is not parsed.
+
+The "\verb!;--!" is a marker for a multi-line comment. Everything after
+that marker will be treated as a comment until the end-marker "\verb!--;!"
+is found. Parsing begins directly after the end-marker.
+
+\begin{astlisting}
+\begin{verbatim}
+ ;This is a comment
+ label = value
+ ;-- This is
+ a comment --;
+
+ ;-- Comment --; exten=> 1000,1,dial(SIP/lisa)
+\end{verbatim}
+\end{astlisting}
+
+\subsubsection{Including other files}
+In all of the configuration files, you may include the content of another
+file with the \#include statement. The content of the other file will be
+included at the row that the \#include statement occurred.
+
+\begin{astlisting}
+\begin{verbatim}
+ #include myusers.conf
+\end{verbatim}
+\end{astlisting}
+
+You may also include the output of a program with the \#exec directive,
+if you enable it in asterisk.conf
+
+In asterisk.conf, add the execincludes = yes statement in the options
+section:
+\begin{astlisting}
+\begin{verbatim}
+ [options]
+ execincludes=yes
+\end{verbatim}
+\end{astlisting}
+
+The exec directive is used like this:
+\begin{astlisting}
+\begin{verbatim}
+ #exec /usr/local/bin/myasteriskconfigurator.sh
+\end{verbatim}
+\end{astlisting}
+
+\subsubsection{Adding to an existing section}
+\begin{astlisting}
+\begin{verbatim}
+ [section]
+ label = value
+
+ [section](+)
+ label2 = value2
+\end{verbatim}
+\end{astlisting}
+
+In this case, the plus sign indicates that the second section (with the
+same name) is an addition to the first section. The second section can
+be in another file (by using the \#include statement). If the section
+name referred to before the plus is missing, the configuration will fail
+to load.
+
+\subsubsection{Defining a template-only section}
+\begin{astlisting}
+\begin{verbatim}
+ [section](!)
+ label = value
+\end{verbatim}
+\end{astlisting}
+
+The exclamation mark indicates to the config parser that this is a only
+a template and should not itself be used by the Asterisk module for
+configuration. The section can be inherited by other sections (see
+section "Using templates" below) but is not used by itself.
+
+\subsubsection{Using templates (or other configuration sections)}
+\begin{astlisting}
+\begin{verbatim}
+ [section](name[,name])
+ label = value
+\end{verbatim}
+\end{astlisting}
+
+The name within the parenthesis refers to other sections, either
+templates or standard sections. The referred sections are included
+before the configuration engine parses the local settings within the
+section as though their entire contents (and anything they were
+previously based upon) were included in the new section. For example
+consider the following:
+
+\begin{astlisting}
+\begin{verbatim}
+[foo]
+permit=192.168.0.2
+host=asdf
+deny=192.168.0.1
+
+[bar]
+permit=192.168.1.2
+host=jkl
+deny=192.168.1.1
+
+[baz](foo,bar)
+permit=192.168.3.1
+host=bnm
+\end{verbatim}
+\end{astlisting}
+
+The [baz] section will be processed as though it had been written in the
+following way:
+
+\begin{astlisting}
+\begin{verbatim}
+[baz]
+permit=192.168.0.2
+host=asdf
+deny=192.168.0.1
+permit=192.168.1.2
+host=jkl
+deny=192.168.1.1
+permit=192.168.3.1
+host=bnm
+\end{verbatim}
+\end{astlisting}
+
+\subsubsection{Additional Examples}
+
+(in top-level sip.conf)
+
+\begin{astlisting}
+\begin{verbatim}
+[defaults](!)
+type=friend
+nat=yes
+qualify=on
+dtmfmode=rfc2833
+disallow=all
+allow=alaw
+
+#include accounts/*/sip.conf
+\end{verbatim}
+\end{astlisting}
+
+(in \path{accounts/customer1/sip.conf})
+
+\begin{astlisting}
+\begin{verbatim}
+[def-customer1](!,defaults)
+secret=this_is_not_secret
+context=from-customer1
+callerid=Customer 1 <300>
+accountcode=0001
+
+[phone1](def-customer1)
+mailbox=phone1@customer1
+
+[phone2](def-customer1)
+mailbox=phone2@customer1
+\end{verbatim}
+\end{astlisting}
+
+This example defines two phones - phone1 and phone2 with settings
+inherited from "def-customer1". The "def-customer1" is a template that
+inherits from "defaults", which also is a template.
diff --git a/trunk/doc/tex/dundi.tex b/trunk/doc/tex/dundi.tex
new file mode 100644
index 000000000..aa2fbb24c
--- /dev/null
+++ b/trunk/doc/tex/dundi.tex
@@ -0,0 +1,41 @@
+\url{http://www.dundi.com}
+
+Mark Spencer, Digium, Inc.
+
+DUNDi is essentially a trusted, peer-to-peer system for being able to
+call any phone number from the Internet. DUNDi works by creating a
+network of nodes called the "DUNDi E.164 Trust Group" which are bound by
+a common peering agreement known as the General Peering Agreement or
+GPA. The GPA legally binds the members of the Trust Group to provide
+good-faith accurate information to the other nodes on the network, and
+provides standards by which the community can insure the integrity of
+the information on the nodes themselves. Unlike ENUM or similar
+systems, DUNDi is explicitly designed to preclude any necessity for a
+single centralized system which could be a source of fees, regulation,
+etc.
+
+Much less dramatically, DUNDi can also be used within a private
+enterprise to share a dialplan efficiently between multiple nodes,
+without incurring a risk of a single point of failure. In this way,
+administrators can locally add extensions which become immediately
+available to the other nodes in the system.
+
+For more information visit \url{http://www.dundi.com}
+
+\section{DUNDIQUERY and DUNDIRESULT}
+
+The DUNDIQUERY and DUNDIRESULT dialplan functions will let you initiate
+a DUNDi query from the dialplan, see how many results there are, and access
+each one. Here is some example usage:
+\begin{astlisting}
+\begin{verbatim}
+exten => 1,1,Set(ID=${DUNDIQUERY(1,dundi_test,b)})
+exten => 1,n,Set(NUM=${DUNDIRESULT(${ID},getnum)})
+exten => 1,n,NoOp(There are ${NUM} results)
+exten => 1,n,Set(X=1)
+exten => 1,n,While($[${X} <= ${NUM}])
+exten => 1,n,NoOp(Result ${X} is ${DUNDIRESULT(${ID},${X})})
+exten => 1,n,Set(X=$[${X} + 1])
+exten => 1,n,EndWhile
+\end{verbatim}
+\end{astlisting}
diff --git a/trunk/doc/tex/enum.tex b/trunk/doc/tex/enum.tex
new file mode 100644
index 000000000..9a3384d46
--- /dev/null
+++ b/trunk/doc/tex/enum.tex
@@ -0,0 +1,355 @@
+\section{The ENUMLOOKUP dialplan function}
+
+The ENUMLOOKUP function is more complex than it first may appear, and
+this guide is to give a general overview and set of examples that may
+be well-suited for the advanced user to evaluate in their
+consideration of ENUM or ENUM-like lookup strategies. This document
+assumes a familiarity with ENUM (RFC3761) or ENUM-like methods, as
+well as familiarity with NAPTR DNS records (RFC2915, RFC3401-3404).
+For an overview of NAPTR records, and the use of NAPTRs in the ENUM
+global phone-number-to-DNS mapping scheme, please see
+\url{http://www.voip-info.org/tiki-index.php?page=ENUM} for more detail.
+
+Using ENUM within Asterisk can be simple or complex, depending on how
+many failover methods and redundancy procedures you wish to utilize.
+Implementation of ENUM paths is supposedly defined by the person
+creating the NAPTR records, but the local administrator may choose to
+ignore certain NAPTR response methods (URI types) or prefer some over
+others, which is in contradiction to the RFC. The ENUMLOOKUP method
+simply provides administrators a method for determining NAPTR results
+in either the globally unique ENUM (e164.arpa) DNS tree, or in other
+ENUM-like DNS trees which are not globally unique. The methods to
+actually create channels ("dial") results given by the ENUMLOOKUP
+function is then up to the administrator to implement in a way that
+best suits their environment.
+
+\begin{verbatim}
+Function: ENUMLOOKUP(number[,Method-type[,options[,record#[,zone-suffix]]]])
+\end{verbatim}
+
+ Performs an ENUM tree lookup on the specified number, method type, and
+ ordinal record offset, and returns one of four different values:
+
+\begin{enumerate}
+ \item post-parsed NAPTR of one method (URI) type
+ \item count of elements of one method (URI) type
+ \item count of all method types
+ \item full URI of method at a particular point in the list of all possible methods
+\end{enumerate}
+
+\subsection{Arguments}
+
+\begin{itemize}
+ \item number
+ \begin{itemize}
+ \item telephone number or search string. Only numeric values
+ within this string are parsed; all other digits are ignored for
+ search, but are re-written during NAPTR regexp expansion.
+ \end{itemize}
+
+ \item service\_type
+ \begin{itemize}
+ \item tel, sip, h323, iax2, mailto, ...[any other string],
+ ALL. Default type is "sip".
+ Special name of "ALL" will create a list of method types across
+ all NAPTR records for the search number, and then put the results
+ in an ordinal list starting with 1. The position <number>
+ specified will then be returned, starting with 1 as the first
+ record (lowest value) in the list. The service types are not
+ hardcoded in Asterisk except for the default (sip) if no other
+ service type specified; any method type string (IANA-approved or
+ not) may be used except for the string "ALL".
+ \end{itemize}
+
+ \item options
+ \begin{itemize}
+ \item c
+ \begin{itemize}
+ \item count. Returns the number of records of this type are returned
+ (regardless of order or priority.) If "ALL" is the specified
+ service\_type, then a count of all methods will be returned for the
+ DNS record.
+ \end{itemize}
+ \end{itemize}
+
+ \item record\#
+ \begin{itemize}
+ \item which record to present if multiple answers are returned
+ <integer> = The record in priority/order sequence based on the
+ total count of records passed back by the query. If a service\_type
+ is specified, all entries of that type will be sorted into an
+ ordinal list starting with 1 (by order first, then priority).
+ The default of <options> is "1"
+ \end{itemize}
+
+ \item zone\_suffix
+ \begin{itemize}
+ \item allows customization of the ENUM zone. Default is e164.arpa.
+ \end{itemize}
+\end{itemize}
+
+\subsection{Examples}
+
+Let's use this ENUM list as an example (note that these examples exist
+in the DNS, and will hopefully remain in place as example
+destinations, but they may change or become invalid over time. The
+end result URIs are not guaranteed to actually work, since some of
+these hostnames or SIP proxies are imaginary. Of course, the tel:
+replies go to directory assistance for New York City and San
+Francisco...) Also note that the complex SIP NAPTR at weight 30 will
+strip off the leading "+" from the dialed string if it exists. This
+is probably a better NAPTR than hard-coding the number into the NAPTR,
+and it is included as a more complex regexp example, though other
+simpler NAPTRs will work just as well.
+
+\begin{verbatim}
+0.2.0.1.1.6.5.1.0.3.1.loligo.com. 3600 IN NAPTR 10 100 "u"
+ "E2U+tel" "!^\\+13015611020$!tel:+12125551212!" .
+0.2.0.1.1.6.5.1.0.3.1.loligo.com. 3600 IN NAPTR 21 100 "u"
+ "E2U+tel" "!^\\+13015611020$!tel:+14155551212!" .
+0.2.0.1.1.6.5.1.0.3.1.loligo.com. 3600 IN NAPTR 25 100 "u"
+ "E2U+sip" "!^\\+13015611020$!sip:2203@sip.fox-den.com!" .
+0.2.0.1.1.6.5.1.0.3.1.loligo.com. 3600 IN NAPTR 26 100 "u"
+ "E2U+sip" "!^\\+13015611020$!sip:1234@sip-2.fox-den.com!" .
+0.2.0.1.1.6.5.1.0.3.1.loligo.com. 3600 IN NAPTR 30 100 "u"
+ "E2U+sip" "!^\\+*([^\\*]*)!sip:\\1@sip-3.fox-den.com!" .
+0.2.0.1.1.6.5.1.0.3.1.loligo.com. 3600 IN NAPTR 55 100 "u"
+ "E2U+mailto" "!^\\+13015611020$!mailto:jtodd@fox-den.com!" .
+\end{verbatim}
+
+Example 1: Simplest case, using first SIP return (use all defaults
+except for domain name)
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(+13015611020,,,,loligo.com)})
+ returns: ${foo}="2203@sip.fox-den.com"
+\end{verbatim}
+
+Example 2: What is the first "tel" pointer type for this number?
+(after sorting by order/preference; default of "1" is assumed in
+options field)
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(+13015611020,tel,,,loligo.com)})
+ returns: ${foo}="+12125551212"
+\end{verbatim}
+
+Example 3: How many "sip" pointer type entries are there for this number?
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(+13015611020,sip,c,,loligo.com)})
+ returns: ${foo}=3
+\end{verbatim}
+
+Example 4: For all the "tel" pointer type entries, what is the second
+one in the list? (after sorting by preference)
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(+13015611020,tel,,2,loligo.com)})
+ returns: ${foo}="+14155551212"
+\end{verbatim}
+
+Example 5: How many NAPTRs (tel, sip, mailto, etc.) are in the list for this number?
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(+13015611020,ALL,c,,loligo.com)})
+ returns: ${foo}=6
+\end{verbatim}
+
+Example 6: Give back the second full URI in the sorted list of all NAPTR URIs:
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(+13015611020,ALL,,2,loligo.com)})
+ returns: ${foo}="tel:+14155551212" [note the "tel:" prefix in the string]
+\end{verbatim}
+
+Example 7: Look up first SIP entry for the number in the e164.arpa zone (all defaults)
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(+437203001721)})
+ returns: ${foo}="enum-test@sip.nemox.net" [note: this result is
+ subject to change as it is "live" DNS and not under my control]
+\end{verbatim}
+
+Example 8: Look up the ISN mapping in freenum.org alpha test zone
+\begin{verbatim}
+exten => 100,1,Set(foo=${ENUMLOOKUP(1234*256,,,,freenum.org)})
+ returns: ${foo}="1234@204.91.156.10" [note: this result is subject
+ to change as it is "live" DNS]
+\end{verbatim}
+
+Example 9: Give back the first SIP pointer for a number in the
+\begin{verbatim}
+enum.yoydynelabs.com zone (invalid lookup)
+exten => 100,1,Set(foo=${ENUMLOOKUP(1234567890,sip,,1,enum.yoyodynelabs.com)})
+ returns: ${foo}=""
+\end{verbatim}
+
+\subsection{Usage notes and subtle features}
+\begin{itemize}
+ \item The use of "+" in lookups is confusing, and warrants further
+ explanation. All E.164 numbers ("global phone numbers") by
+ definition need a leading "+" during ENUM lookup. If you neglect to
+ add a leading "+", you may discover that numbers that seem to exist
+ in the DNS aren't getting matched by the system or are returned with
+ a null string result. This is due to the NAPTR reply requiring a
+ "+" in the regular expression matching sequence. Older versions of
+ Asterisk add a "+" from within the code, which may confuse
+ administrators converting to the new function. Please ensure that
+ all ENUM (e164.arpa) lookups contain a leading "+" before lookup, so
+ ensure your lookup includes the leading plus sign. Other DNS trees
+ may or may not require a leading "+" - check before using those
+ trees, as it is possible the parsed NAPTRs will not provide correct
+ results unless you have the correct dialed string. If you get
+ console messages like "WARNING[24907]: enum.c:222 parse\_naptr: NAPTR
+ Regex match failed." then it is very possible that the returned
+ NAPTR expects a leading "+" in the search string (or the returned
+ NAPTR is mis-formed.)
+
+ \item If a query is performed of type "c" ("count") and let's say you
+ get back 5 records and then some seconds later a query is made
+ against record 5 in the list, it may not be the case that the DNS
+ resolver has the same answers as it did a second or two ago - maybe
+ there are only 4 records in the list in the newest query. The
+ resolver should be the canonical storage location for DNS records,
+ since that is the intent of ENUM. However, some obscure future
+ cases may have wildly changing NAPTR records within several seconds.
+ This is a corner case, and probably only worth noting as a very rare
+ circumstance. (note: I do not object to Asterisk's dnsmgr method of
+ locally caching DNS replies, but this method needs to honor the TTL
+ given by the remote zone master. Currently, the ENUMLOOKUP function
+ does not use the dnsmgr method of caching local DNS replies.)
+
+ \item If you want strict NAPTR value ordering, then it will be
+ necessary to use the "ALL" method to incrementally step through the
+ different returned NAPTR pointers. You will need to use string
+ manipulation to strip off the returned method types, since the
+ results will look like "sip:12125551212" in the returned value.
+ This is a non-trivial task, though it is required in order to have
+ strict RFC compliance and to comply with the desires of the remote
+ party who is presenting NAPTRs in a particular order for a reason.
+
+ \item Default behavior for the function (even in event of an error) is
+ to move to the next priority, and the result is a null value. Most
+ ENUM lookups are going to be failures, and it is the responsibility
+ of the dialplan administrator to manage error conditions within
+ their dialplan. This is a change from the old app\_enumlookup method
+ and it's arbitrary priority jumping based on result type or failure.
+
+ \item Anything other than digits will be ignored in lookup strings.
+ Example: a search string of "+4372030blah01721" will turn into
+ 1.2.7.1.0.0.3.0.2.7.3.4.e164.arpa. for the lookup. The NAPTR
+ parsing may cause unexpected results if there are strings inside
+ your NAPTR lookups.
+
+ \item If there exist multiple records with the same weight and order as
+ a result of your query, the function will RANDOMLY select a single
+ NAPTR from those equal results.
+
+ \item Currently, the function ignores the settings in enum.conf as the
+ search zone name is now specified within the function, and the H323
+ driver can be chosen by the user via the dialplan. There were no
+ other values in this file, and so it becomes deprecated.
+
+ \item The function will digest and return NAPTRs which use older
+ (deprecated) style, reversed method strings such as "sip+E2U"
+ instead of the more modern "E2U+sip"
+
+ \item There is no provision for multi-part methods at this time. If
+ there are multiple NAPTRs with (as an example) a method of
+ "E2U+voice:sip" and then another NAPTR in the same DNS record with a
+ method of ""E2U+sip", the system will treat these both as method
+ "sip" and they will be separate records from the perspective of the
+ function. Of course, if both records point to the same URI and have
+ equal priority/weight (as is often the case) then this will cause no
+ serious difficulty, but it bears mentioning.
+
+ \item ISN (ITAD Subscriber Number) usage: If the search number is of
+ the form ABC*DEF (where ABC and DEF are at least one numeric digit)
+ then perform an ISN-style lookup where the lookup is manipulated to
+ C.B.A.DEF.domain.tld (all other settings and options apply.) See
+ \url{http://www.freenum.org/} for more details on ISN lookups. In the
+ unlikely event you wish to avoid ISN re-writes, put an "n" as the
+ first digit of the search string - the "n" will be ignored for the search.
+\end{itemize}
+
+\subsection{Some more Examples}
+
+All examples below except where noted use "e164.arpa" as the
+referenced domain, which is the default domain name for ENUMLOOKUP.
+All numbers are assumed to not have a leading "+" as dialed by the
+inbound channel, so that character is added where necessary during
+ENUMLOOKUP function calls.
+
+\begin{astlisting}
+\begin{verbatim}
+; example 1
+;
+; Assumes North American international dialing (011) prefix.
+; Look up the first SIP result and send the call there, otherwise
+; send the call out a PRI. This is the most simple possible
+; ENUM example, but only uses the first SIP reply in the list of
+; NAPTR(s).
+;
+exten => _011.,1,Set(enumresult=${ENUMLOOKUP(+${EXTEN:3})})
+exten => _011.,n,Dial(SIP/${enumresult})
+exten => _011.,n,Dial(Zap/g1/${EXTEN})
+;
+; end example 1
+
+; example 2
+;
+; Assumes North American international dialing (011) prefix.
+; Check to see if there are multiple SIP NAPTRs returned by
+; the lookup, and dial each in order. If none work (or none
+; exist) then send the call out a PRI, group 1.
+;
+exten => _011.,1,Set(sipcount=${ENUMLOOKUP(${EXTEN:3},sip,c)}|counter=0)
+exten => _011.,n,While($["${counter}"<"${sipcount}"])
+exten => _011.,n,Set(counter=$[${counter}+1])
+exten => _011.,n,Dial(SIP/${ENUMLOOKUP(+${EXTEN:3},sip,,${counter})})
+exten => _011.,n,EndWhile
+exten => _011.,n,Dial(Zap/g1/${EXTEN})
+;
+; end example 2
+
+; example 3
+;
+; This example expects an ${EXTEN} that is an e.164 number (like
+; 14102241145 or 437203001721)
+; Search through e164.arpa and then also search through e164.org
+; to see if there are any valid SIP or IAX termination capabilities.
+; If none, send call out via Zap channel 1.
+;
+; Start first with e164.arpa zone...
+;
+exten => _X.,1,Set(sipcount=${ENUMLOOKUP(+${EXTEN},sip,c)}|counter=0)
+exten => _X.,2,GotoIf($["${counter}"<"${sipcount}"]?3:6)
+exten => _X.,3,Set(counter=$[${counter}+1])
+exten => _X.,4,Dial(SIP/${ENUMLOOKUP(+${EXTEN},sip,,${counter})})
+exten => _X.,5,GotoIf($["${counter}"<"${sipcount}"]?3:6)
+;
+exten => _X.,6,Set(iaxcount=${ENUMLOOKUP(+${EXTEN},iax2,c)}|counter=0)
+exten => _X.,7,GotoIf($["${counter}"<"${iaxcount}"]?8:11)
+exten => _X.,8,Set(counter=$[${counter}+1])
+exten => _X.,9,Dial(IAX2/${ENUMLOOKUP(+${EXTEN},iax2,,${counter})})
+exten => _X.,10,GotoIf($["${counter}"<"${iaxcount}"]?8:11)
+;
+exten => _X.,11,NoOp("No valid entries in e164.arpa for ${EXTEN} - checking in e164.org")
+;
+; ...then also try e164.org, and look for SIP and IAX NAPTRs...
+;
+exten => _X.,12,Set(sipcount=${ENUMLOOKUP(+${EXTEN},sip,c,,e164.org)}|counter=0)
+exten => _X.,13,GotoIf($["${counter}"<"${sipcount}"]?14:17)
+exten => _X.,14,Set(counter=$[${counter}+1])
+exten => _X.,15,Dial(SIP/${ENUMLOOKUP(+${EXTEN},sip,,${counter},e164.org)})
+exten => _X.,16,GotoIf($["${counter}"<"${sipcount}"]?14:17)
+;
+exten => _X.,17,Set(iaxcount=${ENUMLOOKUP(+${EXTEN},iax2,c,,e164.org)}|counter=0)
+exten => _X.,18,GotoIf($["${counter}"<"${iaxcount}"]?19:22)
+exten => _X.,19,Set(counter=$[${counter}+1])
+exten => _X.,20,Dial(IAX2/${ENUMLOOKUP(+${EXTEN},iax2,,${counter},e164.org)})
+exten => _X.,21,GotoIf($["${counter}"<"${iaxcount}"]?19:22)
+;
+; ...then send out PRI.
+;
+exten => _X.,22,NoOp("No valid entries in e164.org for ${EXTEN} - sending out via Zap")
+exten => _X.,23,Dial(Zap/g1/${EXTEN})
+;
+; end example 3
+
+\end{verbatim}
+\end{astlisting}
diff --git a/trunk/doc/tex/extensions.tex b/trunk/doc/tex/extensions.tex
new file mode 100644
index 000000000..262d14ecd
--- /dev/null
+++ b/trunk/doc/tex/extensions.tex
@@ -0,0 +1,82 @@
+\subsubsection{The Asterisk dialplan}
+
+The Asterisk dialplan is divided into contexts. A context is simply a group
+of extensions. For each "line" that should be able to be called, an extension
+must be added to a context. Then, you configure the calling "line" to have
+access to this context.
+
+If you change the dialplan, you can use the Asterisk CLI command
+"extensions reload" to load the new dialplan without disrupting
+service in your PBX.
+
+Extensions are routed according to priority and may be based on any set
+of characters (a-z), digits, \#, and *. Please note that when matching a
+pattern, "N", "X", and "Z" are interpreted as classes of digits.
+
+For each extension, several actions may be listed and must be given a unique
+priority. When each action completes, the call continues at the next priority
+(except for some modules which use explicitly GOTO's).
+
+When each action completes, it generally moves to the next priority (except for
+some modules which use explicitly GOTO's.
+
+Extensions frequently have data they pass to the executing application
+(most frequently a string). You can see the available dialplan applications
+by entering the "core show applications" command in the CLI.
+
+In this version of Asterisk, dialplan functions are added. These can
+be used as arguments to any application. For a list of the installed
+functions in your Asterisk, use the "core show functions" command.
+
+\subsubsection{Example dialplan}
+
+The example dial plan, in the \path{configs/extensions.conf.sample} file
+is installed as extensions.conf if you run "make samples" after
+installation of Asterisk. This file includes many more instructions
+and examples than this file, so it's worthwhile to read it.
+
+\subsubsection{Special extensions}
+
+There are some extensions with important meanings:
+
+\begin{itemize}
+ \item s
+ \begin{itemize}
+ \item What to do when an extension context is entered (unless
+ overridden by the low level channel interface)
+ This is used in macros, and some special cases.
+ "s" is not a generic catch-all wildcard extension.
+ \end{itemize}
+ \item i
+ \begin{itemize}
+ \item What to do if an invalid extension is entered
+ \end{itemize}
+ \item h
+ \begin{itemize}
+ \item The hangup extension, executed at hangup
+ \end{itemize}
+ \item t
+ \begin{itemize}
+ \item What to do if nothing is entered in the requisite amount
+ of time.
+ \end{itemize}
+ \item T
+ \begin{itemize}
+ \item This is the extension that is executed when the 'absolute'
+ timeout is reached. See "core show function TIMEOUT" for more
+ information on setting timeouts.
+ \end{itemize}
+ \item e
+ \begin{itemize}
+ \item This extension will substitute as a catchall for any of the
+ 'i', 't', or 'T' extensions, if any of them do not exist and
+ catching the error in a single routine is desired. The
+ function EXCEPTION may be used to query the type of exception
+ or the location where it occurred.
+ \end{itemize}
+\end{itemize}
+
+And finally, the extension context "default" is used when either a) an
+extension context is deleted while an extension is in use, or b) a specific
+starting extension handler has not been defined (unless overridden by the
+low level channel interface).
diff --git a/trunk/doc/tex/freetds.tex b/trunk/doc/tex/freetds.tex
new file mode 100644
index 000000000..8dcbec29a
--- /dev/null
+++ b/trunk/doc/tex/freetds.tex
@@ -0,0 +1,16 @@
+The cdr\_tds module is NOT compatible with version 0.63 of FreeTDS.
+
+The cdr\_tds module is known to work with FreeTDS version 0.62.1;
+it should also work with 0.62.2, 0.62.3 and 0.62.4, which are bug
+fix releases.
+
+The cdr\_tds module uses the raw "libtds" API of FreeTDS. It appears
+that from 0.63 onwards, this is not considered a published API
+of FreeTDS and is subject to change without notice.
+
+Between 0.62.x and 0.63 of FreeTDS, many incompatible changes
+have been made to the libtds API.
+
+For newer versions of FreeTDS, it is recommended that you use the
+ODBC driver.
+
diff --git a/trunk/doc/tex/hardware.tex b/trunk/doc/tex/hardware.tex
new file mode 100644
index 000000000..30fa587aa
--- /dev/null
+++ b/trunk/doc/tex/hardware.tex
@@ -0,0 +1,100 @@
+\subsection{Introduction}
+
+A PBX is only really useful if you can get calls into it. Of course, you
+can use Asterisk with VoIP calls (SIP, H.323, IAX, etc.), but you can also
+talk to the real PSTN through various cards.
+
+Supported Hardware is divided into two general groups: Zaptel devices and
+non-zaptel devices. The Zaptel compatible hardware supports pseudo-TDM
+conferencing and all call features through chan\_zap, whereas non-zaptel
+compatible hardware may have different features.
+
+\subsection{Zaptel compatible hardware}
+
+\begin{itemize}
+\item Digium, Inc. (Primary Developer of Asterisk)
+ \url{http://www.digium.com}
+ \begin{itemize}
+ \item Analog Interfaces
+ \begin{itemize}
+ \item TDM400P - The TDM400P is a half-length PCI 2.2-compliant card that supports FXS and FXO station interfaces for connecting analog telephones and analog POTS lines through a PC.
+ \item TDM800P - The TDM800P is a half-length PCI 2.2-compliant, 8 port card using Digium's VoiceBus technology that supports FXS and FXO station interfaces for connecting analog telephones and analog POTS lines through a PC.
+ \item TDM2400P - The TDM2400P is a full-length PCI 2.2-compliant card for connecting analog telephones and analog POTS lines through a PC. It supports a combination of up to 6 FXS and/or FXO modules for a total of 24 lines.
+ \end{itemize}
+ \item Digital Interfaces
+ \begin{itemize}
+ \item TE412P - The TE412P offers an on-board DSP-based echo cancellation module. It supports E1, T1, and J1 environments and is selectable on a per-card or per-port basis.
+ \item TE410P - The TE410P improves performance and scalability through bus mastering architecture. It supports E1, T1, and J1 environments and is selectable on a per-card or per-port basis.
+ \item TE407P - The TE407P offers an on-board DSP-based echo cancellation module. It supports E1, T1, and J1 environments and is selectable on a per-card or per-port basis.
+ \item TE405P - The TE405P improves performance and scalability through bus mastering architecture. It supports both E1, T1, J1 environments and is selectable on a per-card or per-port basis.
+ \item TE212P - The TE212P offers an on-board DSP-based echo cancellation module. It supports E1, T1, and J1 environments and is selectable on a per-card or per-port basis.
+ \item TE210P - The TE210P improves performance and scalability through bus mastering architecture. It supports E1, T1, and J1 environments and is selectable on a per-card or per-port basis.
+ \item TE207P - The TE207P offers an on-board DSP-based echo cancellation module. It supports E1, T1, and J1 environments and is selectable on a per-card or per-port basis.
+ \item TE205P - The TE205P improves performance and scalability through bus mastering architecture. It supports both E1 and T1/J1 environments and is selectable on a per-card or per-port basis.
+ \item TE120P - The TE120P is a single span, selectable T1, E1, or J1 card and utilizes Digium's VoiceBus\texttrademark technology. It supports both voice and data modes.
+ \item TE110P - The TE110P brings a high-performance, cost-effective, and flexible single span togglable T1, E1, J1 interface to the Digium line-up of telephony interface devices.
+ \end{itemize}
+ \end{itemize}
+\end{itemize}
+
+\subsection{Non-zaptel compatible hardware}
+
+\begin{itemize}
+ \item QuickNet, Inc.
+ \url{http://www.quicknet.net}
+ \begin{itemize}
+ \item Internet PhoneJack - Single FXS interface. Supports Linux telephony
+ interface. DSP compression built-in.
+
+ \item Internet LineJack - Single FXS or FXO interface. Supports Linux
+ telephony interface.
+ \end{itemize}
+\end{itemize}
+
+\subsection{mISDN compatible hardware}
+
+mISDN homepage: \url{http://www.misdn.org/}
+
+Any adapter with an mISDN driver should be compatible with
+chan\_misdn. See the mISDN section for more information.
+
+\begin{itemize}
+ \item Digium, Inc. (Primary Developer of Asterisk)
+ \url{http://www.digium.com}
+ \begin{itemize}
+ \item B410P - 4 Port BRI card (TE/NT)
+ \end{itemize}
+\end{itemize}
+
+\begin{itemize}
+ \item beroNet
+ \url{http://www.beronet.com}
+ \begin{itemize}
+ \item BN4S0 - 4 Port BRI card (TE/NT)
+
+ \item BN8S0 - 8 Port BRI card (TE/NT)
+
+ \item Billion Card - Single Port BRI card (TE (/NT with crossed cable))
+ \end{itemize}
+\end{itemize}
+
+\subsection{Miscellaneous other interfaces}
+
+\begin{itemize}
+ \item Digium, Inc. (Primary Developer of Asterisk)
+ \begin{itemize}
+ \item TC400B - The TC400B is a half-length, low-profile PCI 2.2-compliant card for transforming complex VoIP codecs (G.729) into simple codecs.
+ \end{itemize}
+
+ \item ALSA
+ \url{http://www.alsa-project.org}
+ \begin{itemize}
+ \item Any ALSA compatible full-duplex sound card
+ \end{itemize}
+
+ \item OSS
+ \url{http://www.opensound.com}
+ \begin{itemize}
+ \item Any OSS compatible full-duplex sound card
+ \end{itemize}
+\end{itemize}
diff --git a/trunk/doc/tex/ices.tex b/trunk/doc/tex/ices.tex
new file mode 100644
index 000000000..39872be57
--- /dev/null
+++ b/trunk/doc/tex/ices.tex
@@ -0,0 +1,7 @@
+The advent of icecast into Asterisk allows you to do neat things like have
+a caller stream right into an ice-cast stream as well as using chan\_local
+to place things like conferences, music on hold, etc. into the stream.
+
+You'll need to specify a config file for the ices encoder. An example is
+included in \path{contrib/asterisk-ices.xml}.
+
diff --git a/trunk/doc/tex/imapstorage.tex b/trunk/doc/tex/imapstorage.tex
new file mode 100644
index 000000000..3a952ce54
--- /dev/null
+++ b/trunk/doc/tex/imapstorage.tex
@@ -0,0 +1,196 @@
+By enabling IMAP Storage, Asterisk will use native IMAP as the storage
+mechanism for voicemail messages instead of using the standard file structure.
+
+Tighter integration of Asterisk voicemail and IMAP email services allows
+additional voicemail functionality, including:
+
+\begin{itemize}
+ \item Listening to a voicemail on the phone will set its state to "read" in
+ a user's mailbox automatically.
+ \item Deleting a voicemail on the phone will delete it from the user's
+ mailbox automatically.
+ \item Accessing a voicemail recording email message will turn off the message
+ waiting indicator (MWI) on the user's phone.
+ \item Deleting a voicemail recording email will also turn off the message
+ waiting indicator, and delete the message from the voicemail system.
+\end{itemize}
+
+\subsection{Installation Notes}
+
+\subsubsection{University of Washington IMAP C-Client}
+
+If you do not have the University of Washington's IMAP c-client
+installed on your system, you will need to download the c-client
+source distribution (\url{http://www.washington.edu/imap/}) and compile it.
+Asterisk supports both the 2004 and 2006 versions of c-client, however
+mail\_expunge\_full is enabled in the 2006 version.
+
+Note that Asterisk only uses the 'client' portion of the UW IMAP toolkit,
+but building it also builds an IMAP server and various other utilities.
+Because of this, the build instructions for the IMAP toolkit are somewhat
+complicated and can lead to confusion about what is needed.
+
+If you are going to be connecting Asterisk to an existing IMAP server,
+then you don't need to care about the server or utilities in the IMAP
+toolkit at all. If you want to also install the UW IMAPD server, that
+is outside the scope of this document.
+
+Building the c-client library is fairly straightforward; for example, on a
+Debian system there are two possibilities:
+
+\begin{enumerate}
+ \item If you will not be using SSL to connect to the IMAP server:
+ \begin{verbatim}
+ $ make slx SSLTYPE=none!
+ \end{verbatim}
+ \item If you will be using SSL to connect to the IMAP server:
+ \begin{verbatim}
+ $ make slx EXTRACFLAGS="-I/usr/include/openssl"
+ \end{verbatim}
+\end{enumerate}
+
+Once this completes you can proceed with the Asterisk build; there is no
+need to run 'make install'.
+
+\subsubsection{Compiling Asterisk}
+
+To use the system c-client library, configure Asterisk with
+./configure --with-imap=system. If you downloaded the c-client source
+and compiled it according to the above instructions, configure
+Asterisk with with ./configure --with-imap=/usr/src/imap or where ever
+you built the UWashington IMAP Toolkit. When you run 'make
+menuselect', choose 'Voicemail Build Options' and the IMAP\_STORAGE
+option should be available for selection.
+
+After selecting the IMAP\_STORAGE option, use the 'x' key to exit
+menuselect and save your changes, and the build/install Asterisk
+normally.
+
+\subsection{Modify voicemail.conf}
+
+The following directives have been added to voicemail.conf:
+\begin{astlisting}
+\begin{verbatim}
+imapserver=<name or IP address of IMAP mail server>
+imapport=<IMAP port, defaults to 143>
+imapflags=<IMAP flags, "novalidate-cert" for example>
+imapfolder=<IMAP folder to store messages to>
+imapgreetings=<yes or no>
+greetingsfolder=<IMAP folder to store greetings in if imapgreetings is enabled>
+expungeonhangup=<yes or no>
+authuser=<username>
+authpassword=<password>
+opentimeout=<TCP open timeout in seconds>
+closetimeout=<TCP close timeout in seconds>
+readtimeout=<TCP read timeout in seconds>
+writetimeout=<TCP write timeout in seconds>
+\end{verbatim}
+\end{astlisting}
+
+The "imapfolder" can be used to specify an alternative folder on your IMAP server
+to store voicemails in. If not specified, the default folder 'INBOX' will be used.
+
+The "imapgreetings" parameter can be enabled in order to store voicemail greetings
+on the IMAP server. If disabled, then they will be stored on the local file system
+as normal.
+
+The "greetingsfolder" can be set to store greetings on the IMAP server when
+"imapgreetings" is enabled in an alternative folder than that set by "imapfolder"
+or the default folder for voicemails.
+
+The "expungeonhangup" flag is used to determine if the voicemail system should
+expunge all messages marked for deletion when the user hangs up the phone.
+
+Each mailbox definition should also have imapuser=$<$imap username$>$.
+For example:
+\begin{astlisting}
+\begin{verbatim}
+4123=>4123,James Rothenberger,jar@onebiztone.com,,attach=yes|imapuser=jar
+\end{verbatim}
+\end{astlisting}
+
+The directives "authuser" and "authpassword" are not needed when using
+Kerberos. They are defined to allow Asterisk to authenticate as a single
+user that has access to all mailboxes as an alternative to Kerberos.
+
+
+\subsection{IMAP Folders}
+
+Besides INBOX, users should create "Old", "Work", "Family" and "Friends"
+IMAP folders at the same level of hierarchy as the INBOX. These will be
+used as alternate folders for storing voicemail messages to mimic the
+behavior of the current (file-based) voicemail system.
+
+
+\subsection{Separate vs. Shared Email Accounts}
+
+As administrator you will have to decide if you want to send the voicemail
+messages to a separate IMAP account or use each user's existing IMAP mailbox
+for voicemail storage. The IMAP storage mechanism will work either way.
+
+By implementing a single IMAP mailbox, the user will see voicemail messages
+appear in the same INBOX as other messages. The disadvantage of this method
+is that if the IMAP server does NOT support UIDPLUS, Asterisk voicemail will
+expunge ALL messages marked for deletion when the user exits the voicemail
+system, not just the VOICEMAIL messages marked for deletion.
+
+By implementing separate IMAP mailboxes for voicemail and email, voicemail
+expunges will not remove regular email flagged for deletion.
+
+
+\subsection{IMAP Server Implementations}
+
+There are various IMAP server implementations, each supports a potentially
+different set of features.
+
+
+\subsubsection{UW IMAP-2005 or earlier}
+
+UIDPLUS is currently NOT supported on these versions of UW-IMAP. Please note
+that without UID\_EXPUNGE, Asterisk voicemail will expunge ALL messages marked
+for deletion when a user exits the voicemail system (hangs up the phone).
+
+\subsubsection{UW IMAP-2006 Development Branch}
+
+This version supports UIDPLUS, which allows UID\_EXPUNGE capabilities. This
+feature allow the system to expunge ONLY pertinent messages, instead of the
+default behavior, which is to expunge ALL messages marked for deletion when
+EXPUNGE is called. The IMAP storage mechanism is this version of Asterisk
+will check if the UID\_EXPUNGE feature is supported by the server, and use it
+if possible.
+
+\subsubsection{Cyrus IMAP}
+
+Cyrus IMAP server v2.3.3 has been tested using a hierarchy delimiter of '/'.
+
+
+\subsection{Quota Support}
+
+If the IMAP server supports quotas, Asterisk will check the quota when
+accessing voicemail. Currently only a warning is given to the user that
+their quota is exceeded.
+
+
+\subsection{Application Notes}
+
+Since the primary storage mechanism is IMAP, all message information that
+was previously stored in an associated text file, AND the recording itself,
+is now stored in a single email message. This means that the .gsm recording
+will ALWAYS be attached to the message (along with the user's preference of
+recording format if different - ie. .WAV). The voicemail message information
+is stored in the email message headers. These headers include:
+
+\begin{verbatim}
+X-Asterisk-VM-Message-Num
+X-Asterisk-VM-Server-Name
+X-Asterisk-VM-Context
+X-Asterisk-VM-Extension
+X-Asterisk-VM-Priority
+X-Asterisk-VM-Caller-channel
+X-Asterisk-VM-Caller-ID-Num
+X-Asterisk-VM-Caller-ID-Name
+X-Asterisk-VM-Duration
+X-Asterisk-VM-Category
+X-Asterisk-VM-Orig-date
+X-Asterisk-VM-Orig-time
+\end{verbatim}
diff --git a/trunk/doc/tex/jitterbuffer.tex b/trunk/doc/tex/jitterbuffer.tex
new file mode 100644
index 000000000..a29cf811a
--- /dev/null
+++ b/trunk/doc/tex/jitterbuffer.tex
@@ -0,0 +1,98 @@
+\subsubsection{The new jitterbuffer}
+
+You must add "jitterbuffer=yes" to either the [general] part of
+iax.conf, or to a peer or a user. (just like the old jitterbuffer).
+Also, you can set "maxjitterbuffer=n", which puts a hard-limit on the size of the
+jitterbuffer of "n milliseconds". It is not necessary to have the new jitterbuffer
+on both sides of a call; it works on the receive side only.
+
+\subsubsection{PLC}
+
+The new jitterbuffer detects packet loss. PLC is done to try to recreate these
+lost packets in the codec decoding stage, as the encoded audio is translated to slinear.
+PLC is also used to mask jitterbuffer growth.
+
+This facility is enabled by default in iLBC and speex, as it has no additional cost.
+This facility can be enabled in adpcm, alaw, g726, gsm, lpc10, and ulaw by setting
+genericplc =$>$ true in the [plc] section of codecs.conf.
+
+\subsubsection{Trunktimestamps}
+
+To use this, both sides must be using Asterisk v1.2 or later.
+Setting "trunktimestamps=yes" in iax.conf will cause your box to send 16-bit timestamps
+for each trunked frame inside of a trunk frame. This will enable you to use jitterbuffer
+for an IAX2 trunk, something that was not possible in the old architecture.
+
+The other side must also support this functionality, or else, well, bad things will happen.
+If you don't use trunktimestamps, there's lots of ways the jitterbuffer can get confused because
+timestamps aren't necessarily sent through the trunk correctly.
+
+\subsubsection{Communication with Asterisk v1.0.x systems}
+
+You can set up communication with v1.0.x systems with the new jitterbuffer, but
+you can't use trunks with trunktimestamps in this communication.
+
+If you are connecting to an Asterisk server with earlier versions of the software (1.0.x),
+do not enable both jitterbuffer and trunking for the involved peers/users
+in order to be able to communicate. Earlier systems will not support trunktimestamps.
+
+You may also compile chan\_iax2.c without the new jitterbuffer, enabling the old
+backwards compatible architecture. Look in the source code for instructions.
+
+
+\subsubsection{Testing and monitoring}
+
+You can test the effectiveness of PLC and the new jitterbuffer's detection of loss by using
+the new CLI command "iax2 test losspct $<$n$>$". This will simulate n percent packet loss
+coming \_in\_ to chan\_iax2. You should find that with PLC and the new JB, 10 percent packet
+loss should lead to just a tiny amount of distortion, while without PLC, it would lead to
+silent gaps in your audio.
+
+"iax2 show netstats" shows you statistics for each iax2 call you have up.
+The columns are "RTT" which is the round-trip time for the last PING, and then a bunch of s
+tats for both the local side (what you're receiving), and the remote side (what the other
+end is telling us they are seeing). The remote stats may not be complete if the remote
+end isn't using the new jitterbuffer.
+
+The stats shown are:
+\begin{itemize}
+\item Jit: The jitter we have measured (milliseconds)
+\item Del: The maximum delay imposed by the jitterbuffer (milliseconds)
+\item Lost: The number of packets we've detected as lost.
+\item \%: The percentage of packets we've detected as lost recently.
+\item Drop: The number of packets we've purposely dropped (to lower latency).
+\item OOO: The number of packets we've received out-of-order
+\item Kpkts: The number of packets we've received / 1000.
+\end{itemize}
+
+\subsubsection{Reporting problems}
+
+There's a couple of things that can make calls sound bad using the jitterbuffer:
+
+\begin{enumerate}
+\item The JB and PLC can make your calls sound better, but they can't fix everything.
+If you lost 10 frames in a row, it can't possibly fix that. It really can't help much
+more than one or two consecutive frames.
+
+\item Bad timestamps: If whatever is generating timestamps to be sent to you generates
+nonsensical timestamps, it can confuse the jitterbuffer. In particular, discontinuities
+in timestamps will really upset it: Things like timestamps sequences which go 0, 20, 40,
+60, 80, 34000, 34020, 34040, 34060... It's going to think you've got about 34 seconds
+of jitter in this case, etc..
+The right solution to this is to find out what's causing the sender to send us such nonsense,
+and fix that. But we should also figure out how to make the receiver more robust in
+cases like this.
+
+chan\_iax2 will actually help fix this a bit if it's more than 3 seconds or so, but at
+some point we should try to think of a better way to detect this kind of thing and
+resynchronize.
+
+Different clock rates are handled very gracefully though; it will actually deal with a
+sender sending 20\% faster or slower than you expect just fine.
+
+\item Really strange network delays: If your network "pauses" for like 5 seconds, and then
+when it restarts, you are sent some packets that are 5 seconds old, we are going to see
+that as a lot of jitter. We already throw away up to the worst 20 frames like this,
+though, and the "maxjitterbuffer" parameter should put a limit on what we do in this case.
+
+\end{enumerate}
diff --git a/trunk/doc/tex/localchannel.tex b/trunk/doc/tex/localchannel.tex
new file mode 100644
index 000000000..ab42606f7
--- /dev/null
+++ b/trunk/doc/tex/localchannel.tex
@@ -0,0 +1,80 @@
+\subsection{Introduction}
+
+chan\_local is a pseudo-channel. Use of this channel simply loops calls back
+into the dialplan in a different context. Useful for recursive routing.
+
+\subsection{Syntax}
+\begin{verbatim}
+ Local/extension@context[/{n|j}]
+\end{verbatim}
+
+Adding "/n" at the end of the string will make the Local channel not do a
+native transfer (the "n" stands for "n"o release) upon the remote end answering
+the line. This is an esoteric, but important feature if you expect the Local
+channel to handle calls exactly like a normal channel. If you do not have the
+"no release" feature set, then as soon as the destination (inside of the Local
+channel) answers the line and one audio frame passes, the variables and dial plan
+will revert back to that of the original call, and the Local channel will become a
+zombie and be removed from the active channels list. This is desirable in some
+circumstances, but can result in unexpected dialplan behavior if you are doing
+fancy things with variables in your call handling.
+
+There is another option that can be used with local channels, which is the "j"
+option. The "j" option must be used with the "n" option to make sure that the
+local channel does not get optimized out of the call. This option will enable
+a jitterbuffer on the local channel. The jitterbuffer will be used to de-jitter
+audio that it receives from the channel that called the local channel. This is
+especially in the case of putting chan\_local in between an incoming SIP call
+and Asterisk applications, so that the incoming audio will be de-jittered.
+
+\subsection{Purpose}
+
+The Local channel construct can be used to establish dialing into any part of
+the dialplan.
+
+Imagine you have a TE410P in your box. You want to do something for which you
+must use a Dial statement (for instance when dropping files in
+\path{/var/spool/outgoing}) but you do want to be able to use your dialplans
+least-cost-routes or other intelligent stuff. What you could do before we had
+chan\_local was create a cross-link between two ports of the TE410P and then
+Dial out one port and in the other. This way you could control where the call
+was going.
+
+Of course, this was a nasty hack, and to make it more sensible, chan\_local was
+built.
+
+The "Local" channel driver allows you to convert an arbitrary extension into a
+channel. It is used in a variety of places, including agents, etc.
+
+This also allows us to hop to contexts like a GoSub routine; See examples below.
+
+\subsection{Examples}
+\begin{astlisting}
+\begin{verbatim}
+[inbound] ; here falls all incoming calls
+exten => s,1,Answer
+exten => s,2,Dial(local/200@internal,30,r)
+exten => s,3,Playback(sorrynoanswer)
+exten => s,4,Hangup
+
+[internal] ; here where our phones falls for default
+exten => 200,1,Dial(sip/blah)
+exten => 200,102,VoiceMail(${EXTEN}@default)
+
+exten => 201,1,Dial(zap/1)
+exten => 201,102,VoiceMail(${EXTEN}@default)
+
+exten => _0.,1,Dial(Zap/g1/${EXTEN:1}) ; outgoing calls with 0+number
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Caveats}
+
+If you use chan\_local from a call-file and you want to pass channel variables
+into your context, make sure you append the '/n', because otherwise
+chan\_local will 'optimize' itself out of the call-path, and the variables will
+get lost. i.e.
+
+\begin{verbatim}
+ Local/00531234567@pbx becomes Local/00531234567@pbx/n
+\end{verbatim}
diff --git a/trunk/doc/tex/manager.tex b/trunk/doc/tex/manager.tex
new file mode 100644
index 000000000..c3b567bd4
--- /dev/null
+++ b/trunk/doc/tex/manager.tex
@@ -0,0 +1,258 @@
+\section{The Asterisk Manager TCP/IP API}
+
+The manager is a client/server model over TCP. With the manager interface,
+you'll be able to control the PBX, originate calls, check mailbox status,
+monitor channels and queues as well as execute Asterisk commands.
+
+AMI is the standard management interface into your Asterisk server.
+You configure AMI in manager.conf. By default, AMI is available on
+TCP port 5038 if you enable it in manager.conf.
+
+AMI receive commands, called "actions". These generate a "response"
+from Asterisk. Asterisk will also send "Events" containing various
+information messages about changes within Asterisk. Some actions
+generate an initial response and data in the form list of events.
+This format is created to make sure that extensive reports do not
+block the manager interface fully.
+
+Management users are configured in the configuration file manager.conf and are
+given permissions for read and write, where write represents their ability
+to perform this class of "action", and read represents their ability to
+receive this class of "event".
+
+If you develop AMI applications, treat the headers
+in Actions, Events and Responses as local to that particular
+message. There is no cross-message standardization of headers.
+
+If you develop applications, please try to reuse existing manager
+headers and their interpretation. If you are unsure, discuss on
+the asterisk-dev mailing list.
+
+\section{Device status reports}
+
+Manager subscribes to extension status reports from all channels,
+to be able to generate events when an extension or device changes
+state. The level of details in these events may depend on the channel
+and device configuration. Please check each channel configuration
+file for more information. (in sip.conf, check the section on
+subscriptions and call limits)
+
+
+\section{Command Syntax}
+
+Management communication consists of tags of the form "header: value",
+terminated with an empty newline (\textbackslash r\textbackslash n) in
+the style of SMTP, HTTP, and other headers.
+
+The first tag MUST be one of the following:
+
+\begin{itemize}
+ \item Action: An action requested by the CLIENT to the Asterisk SERVER.
+ Only one "Action" may be outstanding at any time.
+ \item Response: A response to an action from the Asterisk SERVER to the CLIENT.
+ \item Event: An event reported by the Asterisk SERVER to the CLIENT
+\end{itemize}
+
+\section{Manager commands}
+
+To see all of the available manager commands, use the "manager show commands"
+CLI command.
+
+You can get more information about a manager command
+with the "manager show command $<$command$>$" CLI command in Asterisk.
+
+\section{Examples}
+
+Login - Log a user into the manager interface.
+
+\begin{verbatim}
+ Action: Login
+ Username: testuser
+ Secret: testsecret
+\end{verbatim}
+
+Originate - Originate a call from a channel to an extension.
+
+\begin{verbatim}
+ Action: Originate
+ Channel: sip/12345
+ Exten: 1234
+ Context: default
+\end{verbatim}
+
+Originate - Originate a call from a channel to an extension without waiting
+for call to complete.
+
+\begin{verbatim}
+ Action: Originate
+ Channel: sip/12345
+ Exten: 1234
+ Context: default
+ Async: yes
+\end{verbatim}
+
+Redirect with ExtraChannel:
+
+ Attempted goal:
+ Have a 'robot' program Redirect both ends of an already-connected call
+ to a meetme room using the ExtraChannel feature through the management interface.
+
+\begin{verbatim}
+ Action: Redirect
+ Channel: Zap/1-1
+ ExtraChannel: SIP/3064-7e00 (varies)
+ Exten: 680
+ Priority: 1
+\end{verbatim}
+
+Where 680 is an extension that sends you to a MeetMe room.
+
+There are a number of GUI tools that use the manager interface, please search
+the mailing list archives and the documentation page on the
+\url{http://www.asterisk.org} web site for more information.
+
+
+\section{Some standard AMI headers}
+\begin{verbatim}
+ Account: -- Account Code (Status)
+ AccountCode: -- Account Code (cdr_manager)
+ ACL: <Y | N> -- Does ACL exist for object ?
+ Action: <action> -- Request or notification of a particular action
+ Address-IP: -- IPaddress
+ Address-Port: -- IP port number
+ Agent: <string> -- Agent name
+ AMAflags: -- AMA flag (cdr_manager, sippeers)
+ AnswerTime: -- Time of answer (cdr_manager)
+ Append: <bool> -- CDR userfield Append flag
+ Application: -- Application to use
+ Async: -- Whether or not to use fast setup
+ AuthType: -- Authentication type (for login or challenge)
+ "md5"
+ BillableSeconds: -- Billable seconds for call (cdr_manager)
+ CallerID: -- Caller id (name and number in Originate & cdr_manager)
+ CallerID: -- CallerID number
+ Number or "<unknown>" or "unknown"
+ (should change to "<unknown>" in app_queue)
+ CallerID1: -- Channel 1 CallerID (Link event)
+ CallerID2: -- Channel 2 CallerID (Link event)
+ CallerIDName: -- CallerID name
+ Name or "<unknown>" or "unknown"
+ (should change to "<unknown>" in app_queue)
+ Callgroup: -- Call group for peer/user
+ CallsTaken: <num> -- Queue status variable
+ Cause: <value> -- Event change cause - "Expired"
+ Cause: <value> -- Hangupcause (channel.c)
+ CID-CallingPres: -- Caller ID calling presentation
+ Channel: <channel> -- Channel specifier
+ Channel: <dialstring> -- Dialstring in Originate
+ Channel: <tech/[peer/username]> -- Channel in Registry events (SIP, IAX2)
+ Channel: <tech> -- Technology (SIP/IAX2 etc) in Registry events
+ ChannelType: -- Tech: SIP, IAX2, ZAP, MGCP etc
+ Channel1: -- Link channel 1
+ Channel2: -- Link channel 2
+ ChanObjectType: -- "peer", "user"
+ Codecs: -- Codec list
+ CodecOrder: -- Codec order, separated with comma ","
+ Command: -- Cli command to run
+ Context: -- Context
+ Count: <num> -- Number of callers in queue
+ Data: -- Application data
+ Default-addr-IP: -- IP address to use before registration
+ Default-Username: -- Username part of URI to use before registration
+ Destination: -- Destination for call (Dialstring ) (dial, cdr_manager)
+ DestinationContext: -- Destination context (cdr_manager)
+ DestinationChannel: -- Destination channel (cdr_manager)
+ DestUniqueID: -- UniqueID of destination (dial event)
+ Disposition: -- Call disposition (CDR manager)
+ Domain: <domain> -- DNS domain
+ Duration: <secs> -- Duration of call (cdr_manager)
+ Dynamic: <Y | N> -- Device registration supported?
+ Endtime: -- End time stamp of call (cdr_manager)
+ EventList: <flag> -- Flag being "Start", "End", "Cancelled" or "ListObject"
+ Events: <eventmask> -- Eventmask filter ("on", "off", "system", "call", "log")
+ Exten: -- Extension (Redirect command)
+ Extension: -- Extension (Status)
+ Family: <string> -- ASTdb key family
+ File: <filename> -- Filename (monitor)
+ Format: <format> -- Format of sound file (monitor)
+ From: <time> -- Parking time (ParkedCall event)
+ Hint: -- Extension hint
+ Incominglimit: -- SIP Peer incoming limit
+ Key:
+ Key: -- ASTdb Database key
+ LastApplication: -- Last application executed (cdr_manager)
+ LastCall: <num> -- Last call in queue
+ LastData: -- Data for last application (cdr_manager)
+ Link: -- (Status)
+ ListItems: <number> -- Number of items in Eventlist (Optionally sent in "end" packet)
+ Location: -- Interface (whatever that is -maybe tech/name in app_queue )
+ Loginchan: -- Login channel for agent
+ Logintime: <number> -- Login time for agent
+ Mailbox: -- VM Mailbox (id@vmcontext) (mailboxstatus, mailboxcount)
+ MD5SecretExist: <Y | N> -- Whether secret exists in MD5 format
+ Membership: <string> -- "Dynamic" or "static" member in queue
+ Message: <text> -- Text message in ACKs, errors (explanation)
+ Mix: <bool> -- Boolean parameter (monitor)
+ NewMessages: <count> -- Count of new Mailbox messages (mailboxcount)
+ Newname:
+ ObjectName: -- Name of object in list
+ OldName: -- Something in Rename (channel.c)
+ OldMessages: <count> -- Count of old mailbox messages (mailboxcount)
+ Outgoinglimit: -- SIP Peer outgoing limit
+ Paused: <num> -- Queue member paused status
+ Peer: <tech/name> -- "channel" specifier :-)
+ PeerStatus: <tech/name> -- Peer status code
+ "Unregistered", "Registered", "Lagged", "Reachable"
+ Penalty: <num> -- Queue penalty
+ Priority: -- Extension priority
+ Privilege: <privilege> -- AMI authorization class (system, call, log, verbose, command, agent, user)
+ Pickupgroup: -- Pickup group for peer
+ Position: <num> -- Position in Queue
+ Queue: -- Queue name
+ Reason: -- "Autologoff"
+ Reason: -- "Chanunavail"
+ Response: <response> -- response code, like "200 OK"
+ "Success", "Error", "Follows"
+ Restart: -- "True", "False"
+ RegExpire: -- SIP registry expire
+ RegExpiry: -- SIP registry expiry
+ Reason: -- Originate reason code
+ Seconds: -- Seconds (Status)
+ Secret: <password> -- Authentication secret (for login)
+ SecretExist: <Y | N> -- Whether secret exists
+ Shutdown: -- "Uncleanly", "Cleanly"
+ SIP-AuthInsecure:
+ SIP-FromDomain: -- Peer FromDomain
+ SIP-FromUser: -- Peer FromUser
+ SIP-NatSupport:
+ SIPLastMsg:
+ Source: -- Source of call (dial event, cdr_manager)
+ SrcUniqueID: -- UniqueID of source (dial event)
+ StartTime: -- Start time of call (cdr_manager)
+ State: -- Channel state
+ Status: -- Registration status (Registry events SIP)
+ Status: -- Extension status (Extensionstate)
+ Status: -- Peer status (if monitored) ** Will change name **
+ "unknown", "lagged", "ok"
+ Status: <num> -- Queue Status
+ Status: -- DND status (DNDState)
+ Time: <sec> -- Roundtrip time (latency)
+ Timeout: -- Parking timeout time
+ Timeout: -- Timeout for call setup (Originate)
+ Timeout: <seconds> -- Timeout for call
+ Uniqueid: -- Channel Unique ID
+ Uniqueid1: -- Channel 1 Unique ID (Link event)
+ Uniqueid2: -- Channel 2 Unique ID (Link event)
+ User: -- Username (SIP registry)
+ UserField: -- CDR userfield (cdr_manager)
+ Val: -- Value to set/read in ASTdb
+ Variable: -- Variable AND value to set (multiple separated with | in Originate)
+ Variable: <name> -- For channel variables
+ Value: <value> -- Value to set
+ VoiceMailbox: -- VM Mailbox in SIPpeers
+ Waiting: -- Count of mailbox messages (mailboxstatus)
+\end{verbatim}
+
+ ** Please try to re-use existing headers to simplify manager message parsing in clients.
+
+Read the CODING-GUIDELINES if you develop new manager commands or events.
diff --git a/trunk/doc/tex/misdn.tex b/trunk/doc/tex/misdn.tex
new file mode 100644
index 000000000..84dbb7aaa
--- /dev/null
+++ b/trunk/doc/tex/misdn.tex
@@ -0,0 +1,272 @@
+\subsection{Introduction}
+
+This package contains the mISDN Channel Driver for the Asterisk PBX. It
+supports every mISDN Hardware and provides an interface for asterisk.
+
+\subsection{Features}
+
+\begin{itemize}
+\item NT and TE mode
+\item PP and PMP mode
+\item BRI and PRI (with BNE1 and BN2E1 Cards)
+\item Hardware Bridging
+\item DTMF Detection in HW+mISDNdsp
+\item Display Messages on Phones (on those that support display msg)
+\item app\_SendText
+\item HOLD/RETRIEVE/TRANSFER on ISDN Phones : )
+\item Screen/ Not Screen User Number
+\item EchoCancellation
+\item Volume Control
+\item Crypting with mISDNdsp (Blowfish)
+\item Data (HDLC) callthrough
+\item Data Calling (with app\_ptyfork +pppd)
+\item Echo cancellation
+\item CallDeflection
+\item Some other
+\end{itemize}
+
+\subsection{Fast Installation Guide}
+
+It is easy to install mISDN and mISDNuser. This can be done by:
+\begin{itemize}
+ \item You can download latest stable releases from \url{http://www.misdn.org/downloads/}
+
+ \item Just fetch the newest head of the GIT (mISDN provect moved from CVS)
+ In details this process described here: \url{http://www.misdn.org/index.php/GIT}
+\end{itemize}
+
+
+then compile and install both with:
+\begin{astlisting}
+\begin{verbatim}
+cd mISDN ;
+make && make install
+\end{verbatim}
+\end{astlisting}
+(you will need at least your kernel headers to compile mISDN).
+\begin{astlisting}
+\begin{verbatim}
+cd mISDNuser ;
+make && make install
+\end{verbatim}
+\end{astlisting}
+Now you can compile chan\_misdn, just by making asterisk:
+\begin{astlisting}
+\begin{verbatim}
+cd asterisk ;
+./configure && make && make install
+\end{verbatim}
+\end{astlisting}
+That's all!
+
+Follow the instructions in the mISDN Package for how to load the Kernel
+Modules. Also install process described in \url{http://www.misdn.org/index.php/Installing_mISDN}
+
+\subsection{Pre-Requisites}
+
+To compile and install this driver, you'll need at least one mISDN Driver and
+the mISDNuser package. Chan\_misdn works with both, the current release version
+and the development (svn trunk) version of Asterisk. mISDNuser and mISDN must
+be fetched from cvs.isdn4linux.de.
+
+You should use Kernels $>$= 2.6.9
+
+
+\subsection{Configuration}
+
+First of all you must configure the mISDN drivers, please follow the
+instructions in the mISDN package to do that, the main config file and config
+script is:
+\begin{astlisting}
+\begin{verbatim}
+/etc/init.d/misdn-init and
+/etc/misdn-init.conf
+\end{verbatim}
+\end{astlisting}
+Now you will want to configure the misdn.conf file which resides in the
+asterisk config directory (normally /etc/asterisk).
+
+\subsubsection{misdn.conf: [general]}
+The misdn.conf file contains a "general" subsection, and user subsections which
+contain misdn port settings and different Asterisk contexts.
+
+In the general subsection you can set options that are not directly port
+related. There is for example the very important debug variable which you can
+set from the Asterisk cli (command line interface) or in this configuration
+file, bigger numbers will lead to more debug output. There's also a tracefile
+option, which takes a path+filename where debug output is written to.
+
+\subsubsection{misdn.conf: [default] subsection}
+
+The default subsection is another special subsection which can contain all the
+options available in the user/port subsections. the user/port subsection inherit
+their parameters from the default subsection.
+
+\subsubsection{misdn.conf: user/port subsections}
+
+The user subsections have names which are unequal to "general". Those subsections
+contain the ports variable which mean the mISDN Ports. Here you can add
+multiple ports, comma separated.
+
+Espacially for TE-Mode Ports there is a msns option. This option tells the
+chan\_misdn driver to listen for incoming calls with the given msns, you can
+insert a '*' as single msn, which leads in getting every incoming call (if you
+want to share on PMP TE S0 with a asterisk and a phone or isdn card you should
+insert here the msns which you'll like to give the Asterisk). Finally a
+context variable resides in the user subsections, which tells chan\_misdn where to
+send incoming calls to in the Asterisk dial plan (extension.conf).
+
+
+\subsubsection{Dial and Options String}
+
+The dial string of chan\_misdn got more complex, because we added more features,
+so the generic dial string looks like:
+
+\begin{astlisting}
+\begin{verbatim}
+mISDN/<port>|g:<group>/<extension>[/<OPTIONSSTRING>]
+
+The Optionsstring looks Like:
+:<optchar1><OptParam1>:<optchar2><OptParam2>
+
+the ":" character is the delimiter.
+
+The available Optchars are:
+ d - Send display text on called phone, text is the optparam
+ n - don't detect dtmf tones on called channel
+ h - make digital outgoing call
+ c - make crypted outgoing call, param is keyindex
+ e - perform echo cancellation on this channel,
+ takes taps as arguments (32,64,128,256)
+ s - send Non Inband DTMF as inband
+ vr - rxgain control
+ vt - txgain control
+\end{verbatim}
+\end{astlisting}
+
+chan\_misdn registers a new dial plan application "misdn\_set\_opt" when
+loaded. This application takes the Optionsstring as argument. The Syntax is:
+
+\begin{verbatim}
+misdn_set_opt(<OPTIONSSTRING>)
+\end{verbatim}
+
+When you set options in the dialstring, the options are set in the external
+channel. When you set options with misdn\_set\_opt, they are set in the current
+incoming channel. So if you like to use static encryption, the scenario looks
+as follows:
+
+\begin{verbatim}
+Phone1 --> * Box 1 --> PSTN_TE
+PSTN_TE --> * Box 2 --> Phone2
+\end{verbatim}
+
+The Encryption must be done on the PSTN sides, so the dialplan on the boxes
+are:
+
+\begin{verbatim}
+* Box 1:
+exten => _${CRYPT_PREFIX}X.,1,Dial(mISDN/g:outbound/:c1)
+
+* Box 2:
+exten => ${CRYPT_MSN},1,misdn_set_opt(:c1)
+exten => ${CRYPT_MSN},2,dial(${PHONE2})
+\end{verbatim}
+
+
+\subsection{mISDN CLI commands}
+
+At the Asterisk cli you can try to type in:
+
+\begin{verbatim}
+misdn <tab> <tab>
+\end{verbatim}
+
+Now you should see the misdn cli commands:
+
+\begin{astlisting}
+\begin{verbatim}
+- clean
+ -> pid (cleans a broken call, use with care, leads often
+ to a segmentation fault)
+- send
+ -> display (sends a Text Message to a Asterisk channel,
+ this channel must be an misdn channel)
+- set
+ -> debug (sets debug level)
+- show
+ -> config (shows the configuration options)
+ -> channels (shows the current active misdn channels)
+ -> channel (shows details about the given misdn channels)
+ -> stacks (shows the current ports, their protocols and states)
+ -> fullstacks (shows the current active and inactive misdn channels)
+
+- restart
+ -> port (restarts given port (L2 Restart) )
+
+- reload (reloads misdn.conf)
+\end{verbatim}
+\end{astlisting}
+
+You can only use "misdn send display" when an Asterisk channel is created and
+isdn is in the correct state. "correct state" means that you have established a
+call to another phone (mustn't be isdn though).
+
+Then you use it like this:
+
+misdn send display mISDN/1/101 "Hello World!"
+
+where 1 is the Port of the Card where the phone is plugged in, and 101 is the
+msn (callerid) of the Phone to send the text to.
+
+\subsection{mISDN Variables}
+
+mISDN Exports/Imports a few Variables:
+
+\begin{verbatim}
+- MISDN_ADDRESS_COMPLETE : Is either set to 1 from the Provider, or you
+ can set it to 1 to force a sending complete.
+\end{verbatim}
+
+
+\subsection{Debugging and sending bug reports}
+
+If you encounter problems, you should set up the debugging flag, usually
+debug=2 should be enough. the messages are divided in asterisk and misdn
+parts. Misdn Debug messages begin with an 'I', asterisk messages begin with
+an '*', the rest is clear I think.
+
+Please take a trace of the problem and open a report in the Asterisk issue
+tracker at \url{http://bugs.digium.com} in the "channel drivers" project,
+"chan\_misdn" category. Read the bug guidelines to make sure you
+provide all the information needed.
+
+
+\subsection{Examples}
+
+Here are some examples of how to use chan\_misdn in the dialplan
+(extensions.conf):
+
+\begin{astlisting}
+\begin{verbatim}
+[globals]
+OUT_PORT=1 ; The physical Port of the Card
+OUT_GROUP=ExternE1 ; The Group of Ports defined in misdn.conf
+
+[misdnIn]
+exten => _X.,1,Dial(mISDN/${OUT_PORT}/${EXTEN})
+exten => _0X.,1,Dial(mISDN/g:${OUT_GROUP}/${EXTEN:1})
+exten => _1X.,1,Dial(mISDN/g:${OUT_GROUP}/${EXTEN:1}/:dHello)
+exten => _1X.,1,Dial(mISDN/g:${OUT_GROUP}/${EXTEN:1}/:dHello Test:n)
+\end{verbatim}
+\end{astlisting}
+
+On the last line, you will notice the last argument (Hello); this is sent
+as Display Message to the Phone.
+
+\subsection{Known Problems}
+
+Q: I cannot hear any tone after a successful CONNECT to the other end
+
+A: You forgot to load mISDNdsp, which is now needed by chan\_misdn for switching
+and dtmf tone detection
diff --git a/trunk/doc/tex/mp3.tex b/trunk/doc/tex/mp3.tex
new file mode 100644
index 000000000..aeb02a0ea
--- /dev/null
+++ b/trunk/doc/tex/mp3.tex
@@ -0,0 +1,11 @@
+\subsubsection{MP3 Music On Hold}
+
+Use of the mpg123 for your music on hold is no longer recommended and is now
+officially deprecated. You should now use one of the native formats for your
+music on hold selections.
+
+However, if you still need to use mp3 as your music on hold format, a format
+driver for reading MP3 audio files is available in the asterisk-addons SVN
+repository on svn.digium.com or in the asterisk-addons release at
+\url{http://downloads.digium.com/pub/telephony/asterisk/}.
+
diff --git a/trunk/doc/tex/odbcstorage.tex b/trunk/doc/tex/odbcstorage.tex
new file mode 100644
index 000000000..fed96e9d1
--- /dev/null
+++ b/trunk/doc/tex/odbcstorage.tex
@@ -0,0 +1,31 @@
+
+
+ODBC Storage allows you to store voicemail messages within a database
+instead of using a file. This is \textbf{not} a full realtime engine and
+\textbf{only} supports ODBC. The table description for the "voicemessages"
+table is as follows:
+
+\begin{verbatim}
++----------------+-------------+------+-----+---------+-------+
+| Field | Type | Null | Key | Default | Extra |
++----------------+-------------+------+-----+---------+-------+
+| msgnum | int(11) | YES | | NULL | |
+| dir | varchar(80) | YES | MUL | NULL | |
+| context | varchar(80) | YES | | NULL | |
+| macrocontext | varchar(80) | YES | | NULL | |
+| callerid | varchar(40) | YES | | NULL | |
+| origtime | varchar(40) | YES | | NULL | |
+| duration | varchar(20) | YES | | NULL | |
+| mailboxuser | varchar(80) | YES | | NULL | |
+| mailboxcontext | varchar(80) | YES | | NULL | |
+| recording | longblob | YES | | NULL | |
++----------------+-------------+------+-----+---------+-------+
+\end{verbatim}
+
+The database name (from \path{/etc/asterisk/res_odbc.conf}) is in the
+"odbcstorage" variable in the general section of voicemail.conf.
+
+You may modify the voicemessages table name by using
+odbctable=??? in voicemail.conf.
+
+
diff --git a/trunk/doc/tex/phoneprov.tex b/trunk/doc/tex/phoneprov.tex
new file mode 100644
index 000000000..cb236a89a
--- /dev/null
+++ b/trunk/doc/tex/phoneprov.tex
@@ -0,0 +1,307 @@
+\section{Introduction}
+
+Asterisk includes basic phone provisioning support through the res\_phoneprov module. The
+current implementation is based on a templating system using Asterisk dialplan function
+and variable substitution and obtains information to substitute into those templates from
+\path{phoneprov.conf} and \path{users.conf}. A profile and set of templates is provided
+for provisioning Polycom phones. Note that res\_phoneprov is currently limited to
+provisioning a single user per device.
+
+\section{Configuration of phoneprov.conf}
+
+The configuration file, \path{phoneprov.conf}, is used to set up the built-in variables
+SEVER and SERVER\_PORT, to define a default phone profile to use, and to define different
+phone profiles available for provisioning.
+
+\subsection{The [general] section}
+
+Below is a sample of the general section of \path{phoneprov.conf}:
+
+\begin{astlisting}
+\begin{verbatim}
+[general]
+;serveriface=eth0
+;serveraddr=192.168.1.1
+;serverport=5060
+default_profile=polycom
+\end{verbatim}
+\end{astlisting}
+
+By default, res\_phoneprov will set the SERVER variable to the IP address on the server
+that the requesting phone uses to contact the asterisk HTTP server. The SERVER\_PORT
+variable will default to the \textbf{bindport} setting in sip.conf.
+
+Should the defaults be insufficient, there are two choices for overriding the default
+setting of the SERVER variable. If the IP address of the server is known, or the hostname
+resolvable by the phones, the appropriate \textbf{serveraddr} value should be set.
+Alternatively, the network interface that the server listens on can be set by specifying a
+\textbf{serveriface} and SERVER will be set to the IP address of that interface. Only one
+of these options should be set.
+
+The default SERVER\_PORT variable can be overridden by setting the \textbf{serverport}.
+If \textbf{bindport} is not set in \path{sip.conf} and serverport is not specified, it
+is set to a default value of 5060.
+
+Any user set for auto-provisioning in users.conf without a specified profile will be
+assumed to belong to the profile set with \textbf{default\_profile}.
+
+\subsection{Creating phone profiles}
+
+A phone profile is basically a list of files that a particular group of phones needs to
+function. For most phone types there are files that are identical for all phones
+(firmware, for instance) as well as a configuration file that is specific to individual
+phones. res\_phoneprov breaks these two groups of files into static files and dynamic
+files, respectively. A sample profile:
+
+\begin{astlisting}
+\begin{verbatim}
+[polycom]
+staticdir => configs/
+mime_type => text/xml
+setvar => CUSTOM_CONFIG=/var/lib/asterisk/phoneprov/configs/custom.cfg
+static_file => bootrom.ld,application/octet-stream
+static_file => bootrom.ver,plain/text
+static_file => sip.ld,application/octet-stream
+static_file => sip.ver,plain/text
+static_file => sip.cfg
+static_file => custom.cfg
+${TOLOWER(${MAC})}.cfg => 000000000000.cfg
+${TOLOWER(${MAC})}-phone.cfg => 000000000000-phone.cfg
+config/${TOLOWER(${MAC})} => polycom.xml
+${TOLOWER(${MAC})}-directory.xml => 000000000000-directory.xml
+\end{verbatim}
+\end{astlisting}
+
+A \textbf{static\_file} is set by specifying the file name, relative to
+\path{AST\_DATA\_DIR/phoneprov}. The mime-type of the file can optionally be specified
+after a comma. If \textbf{staticdir} is set, all static files will be relative to the
+subdirectory of AST\_DATA\_DIR/phoneprov specified.
+
+Since phone-specific config files generally have file names based on phone-specifc data,
+dynamic filenames in res\_phoneprov can be defined with Asterisk dialplan function and
+variable substitution. In the above example, \$\{TOLOWER(\$\{MAC\})\}.cfg $\Rightarrow$
+000000000000.cfg would define a relative URI to be served that matches the format of
+MACADDRESS.cfg, all lower case. A request for that file would then point to the template
+found at AST\_DATA\_DIR/phoneprov/000000000000.cfg. The template can be followed by a
+comma and mime-type. Notice that the dynamic filename (URI) can contain contain
+directories. Since these files are dynamically generated, the config file itself does not
+reside on the filesystem--only the template. To view the generated config file, open it
+in a web browser. If the config file is XML, Firefox should display it. Some browsers
+will require viewing the source of the page requested.
+
+A default mime-type for the profile can be defined by setting \textbf{mime-type}. If a
+custom variable is required for a template, it can be specified with \textbf{setvar}.
+Variable substitution on this value is done while building the route list, so
+\$\{USERNAME\} would expand to the username of the users.conf user that registers the
+dynamic filename.
+
+NOTE: Any dialplan function that is used for generation of dynamic file names MUST be
+loaded before res\_phoneprov. Add "preload $\Rightarrow$ modulename.so" to
+\path{modules.conf} for required functions. In the example above, "preload $\Rightarrow$
+func\_strings.so" would be required.
+
+\section{Configuration of users.conf}
+
+The asterisk-gui sets up extensions, SIP/IAX2 peers, and a host of other settings.
+User-specific settings are stored in users.conf. If the asterisk-gui is not being used,
+manual entries to users.conf can be made.
+
+\subsection{The [general] section}
+
+There are only two settings in the general section of \path{users.conf} that apply to
+phone provisioning: localextenlength which maps to template variable EXTENSION\_LENGTH
+and \textbf{vmexten} which maps to the VOICEMAIL\_EXTEN variable.
+
+\subsection{Invdividual Users}
+
+To enable auto-provisioning of a phone, the user in \path{users.conf} needs to have:
+
+\begin{astlisting}
+\begin{verbatim}
+...
+autoprov=yes
+macaddress=deadbeef4dad
+profile=polycom
+\end{verbatim}
+\end{astlisting}
+
+The profile is optional if a \textbf{default\_profile} is set in \path{phoneprov.conf}.
+The following is a sample users.conf entry, with the template variables commented next to
+the settings:
+
+\begin{astlisting}
+\begin{verbatim}
+[6001]
+callwaiting = yes
+context = numberplan-custom-1
+hasagent = no
+hasdirectory = yes
+hasiax = no
+hasmanager = no
+hassip = yes
+hasvoicemail = yes
+host = dynamic
+mailbox = 6001
+threewaycalling = yes
+deletevoicemail = no
+autoprov = yes
+profile = polycom
+canreinvite = no
+nat = no
+fullname = User Two ; ${DISPLAY_NAME}
+secret = test ; ${SECRET}
+username = 6001 ; ${USERNAME}
+macaddress = deadbeef4dad ; ${MAC}
+label = 6001 ; ${LABEL}
+cid_number = 6001 ; ${CALLERID}
+\end{verbatim}
+\end{astlisting}
+
+The variables above, are the user-specfic variables that can be substituted into dynamic
+filenames and config templates.
+
+\section{Templates}
+
+Configuration templates are a generic way to configure phones with text-based
+configuration files. Templates can use any loaded dialplan function and all of the
+variables created by \path{phoneprov.conf} and \path{users.conf}. A short example is the
+included 000000000000.cfg Polycom template:
+
+\begin{astlisting}
+\begin{verbatim}
+<?xml version="1.0" standalone="yes"?>
+ <APPLICATION
+ APP_FILE_PATH="sip.ld"
+ CONFIG_FILES="${IF($[${STAT(e|${CUSTOM_CONFIG})}] ? "custom.cfg,
+")}config/${TOLOWER(${MAC})}, sip.cfg"
+ MISC_FILES="" LOG_FILE_DIRECTORY=""
+ />
+\end{verbatim}
+\end{astlisting}
+
+This template uses dialplan functions, expressions, and a couple of variables to generate
+a config file to instruct the Polycom where to pull other needed config files. If a phone
+with MAC address 0xDEADBEEF4DAD requests this config file, and the filename that is
+stored in variable CUSTOM\_CONFIG does not exist, then the generated output would be:
+
+\begin{astlisting}
+\begin{verbatim}
+<?xml version="1.0" standalone="yes"?>
+ <APPLICATION
+ APP_FILE_PATH="sip.ld"
+ CONFIG_FILES="config/deadbeef4dad, sip.cfg"
+ MISC_FILES="" LOG_FILE_DIRECTORY=""
+ />
+\end{verbatim}
+\end{astlisting}
+
+The Polycom phone would then download both sip.cfg (which would be registered in
+\path{phoneprov.conf} as a static file) and config/deadbeef4dad (which would be
+registered as a dynamic file pointing to another template, polycom.xml).
+
+res\_phoneprov also registers its own dialplan function: PP\_EACH\_USER. This function
+was designed to be able to print out a particular string for each user that
+res\_phoneprov knows about. An example use of this function is the template for a Polycom
+contact directory:
+
+\begin{astlisting}
+\begin{verbatim}
+<?xml version="1.0" standalone="yes"?>
+<directory>
+ <item_list>
+ ${PP_EACH_USER(<item><fn>%{DISPLAY_NAME}</fn><ct>%{CALLERID}</ct><bw>1</bw></item>|${MAC})}
+ </item_list>
+</directory>
+\end{verbatim}
+\end{astlisting}
+
+PP\_EACH\_USER takes two arguments. The first is the string to be printed for each user.
+Any variables that are to be substituted need to be in the format \%\{VARNAME\} so that
+Asterisk doesn't try to substitute the variable immediately before it is passed to
+PP\_EACH\_USER. The second, optional, argument is a MAC address to exclude from the list
+iterated over (so, in this case, a phone won't be listed in its own contact directory).
+
+\section{Putting it all together}
+
+Make sure that \path{manager.conf} has:
+
+\begin{astlisting}
+\begin{verbatim}
+[general]
+enabled = yes
+webenabled = yes
+\end{verbatim}
+\end{astlisting}
+
+and that \path{http.conf} has:
+
+\begin{astlisting}
+\begin{verbatim}
+[general]
+enabled = yes
+bindaddr = 192.168.1.1 ; Your IP here ;-)
+bindport = 8088 ; Or port 80 if it is the only http server running on the machine
+\end{verbatim}
+\end{astlisting}
+
+With \path{phoneprov.conf} and \path{users.conf} in place, start Astersik. From the CLI,
+type "http show status". An example output:
+\begin{astlisting}
+\begin{verbatim}
+HTTP Server Status:
+Prefix: /asterisk
+Server Enabled and Bound to 192.168.1.1:8088
+
+Enabled URI's:
+/asterisk/httpstatus => Asterisk HTTP General Status
+/asterisk/phoneprov/... => Asterisk HTTP Phone Provisioning Tool
+/asterisk/manager => HTML Manager Event Interface
+/asterisk/rawman => Raw HTTP Manager Event Interface
+/asterisk/static/... => Asterisk HTTP Static Delivery
+/asterisk/mxml => XML Manager Event Interface
+
+Enabled Redirects:
+ None.
+
+POST mappings:
+None.
+\end{verbatim}
+\end{astlisting}
+
+There should be a phoneprov URI listed. Next, from the CLI, type "phoneprov show routes"
+and verify that the information there is correct. An example output for Polycom phones
+woud look like:
+
+\begin{astlisting}
+\begin{verbatim}
+Static routes
+
+Relative URI Physical location
+sip.ver configs/sip.ver
+sip.ld configs/sip.ld
+bootrom.ver configs/bootrom.ver
+sip.cfg configs/sip.cfg
+bootrom.ld configs/bootrom.ld
+custom.cfg configs/custom.cfg
+
+Dynamic routes
+
+Relative URI Template
+deadbeef4dad.cfg 000000000000.cfg
+deadbeef4dad-directory.xml 000000000000-directory.xml
+deadbeef4dad-phone.cfg 000000000000-phone.cfg
+config/deadbeef4dad polycom.xml
+\end{verbatim}
+\end{astlisting}
+
+With the above examples, the phones would be pointed to
+\url{http://192.168.1.1:8080/asterisk/phoneprov} for pulling config files. Templates
+would all be placed in AST\_DATA\_DIR/phoneprov and static files would be placed in
+AST\_DATA\_DIR/phoneprov/configs. Examples of valid URIs would be:
+
+\begin{itemize}
+\item http://192.168.1.1:8080/asterisk/phoneprov/sip.cfg
+\item http://192.168.1.1:8080/asterisk/phoneprov/deadbeef4dad.cfg
+\item http://192.168.1.1:8080/asterisk/phoneprov/config/deadbeef4dad
+\end{itemize}
+
diff --git a/trunk/doc/tex/privacy.tex b/trunk/doc/tex/privacy.tex
new file mode 100644
index 000000000..f8bf698f6
--- /dev/null
+++ b/trunk/doc/tex/privacy.tex
@@ -0,0 +1,354 @@
+So, you want to avoid talking to pesky telemarketers/charity
+seekers/poll takers/magazine renewers/etc?
+
+\subsection{First of all}
+
+ the FTC "Don't call" database, this alone will reduce your
+telemarketing call volume considerably. (see:
+\url{https://www.donotcall.gov/default.aspx} ) But, this list won't protect
+from the Charities, previous business relationships, etc.
+
+\subsection{Next, Fight against autodialers!!}
+
+Zapateller detects if callerid is present, and if not, plays the
+da-da-da tones that immediately precede messages like, "I'm sorry,
+the number you have called is no longer in service."
+
+Most humans, even those with unlisted/callerid-blocked numbers, will
+not immediately slam the handset down on the hook the moment they hear
+the three tones. But autodialers seem pretty quick to do this.
+
+I just counted 40 hangups in Zapateller over the last year in my
+CDR's. So, that is possibly 40 different telemarketers/charities that have
+hopefully slashed my back-waters, out-of-the-way, humble home phone
+number from their lists.
+
+I highly advise Zapateller for those seeking the nirvana of "privacy".
+
+
+\subsection{Next, Fight against the empty CALLERID!}
+
+A considerable percentage of the calls you don't want, come from
+sites that do not provide CallerID.
+
+Null callerid's are a fact of life, and could be a friend with an
+unlisted number, or some charity looking for a handout. The
+PrivacyManager application can help here. It will ask the caller to
+enter a 10-digit phone number. They get 3 tries(configurable), and this is
+configurable, with control being passed to priority+101 if they won't
+supply one.
+
+PrivacyManager can't guarantee that the number they supply is any
+good, tho, as there is no way to find out, short of hanging up and
+calling them back. But some answers are obviously wrong. For instance,
+it seems a common practice for telemarketers to use your own number
+instead of giving you theirs. A simple test can detect this. More
+advanced tests would be to look for -555- numbers, numbers that count
+up or down, numbers of all the same digit, etc.
+
+My logs show that 39 have hung up in the PrivacyManager script over
+the last year.
+
+(Note: Demanding all unlisted incoming callers to enter their CID may
+not always be appropriate for all users. Another option might be to
+use call screening. See below.)
+
+
+\subsection{Next, use a WELCOME MENU !}
+
+Experience has shown that simply presenting incoming callers with
+a set of options, no matter how simple, will deter them from calling
+you. In the vast majority of situations, a telemarketer will simply
+hang up rather than make a choice and press a key.
+
+This will also immediately foil all autodialers that simply belch a
+message in your ear and hang up.
+
+\subsubsection{Example usage of Zapateller and PrivacyManager}
+
+\begin{astlisting}
+\begin{verbatim}
+[homeline]
+exten => s,1,Answer
+exten => s,2,SetVar,repeatcount=0
+exten => s,3,Zapateller,nocallerid
+exten => s,4,PrivacyManager
+ ;; do this if they don't enter a number to Privacy Manager
+exten => s,105,Background(tt-allbusy)
+exten => s,106,Background(tt-somethingwrong)
+exten => s,107,Background(tt-monkeysintro)
+exten => s,108,Background(tt-monkeys)
+exten => s,109,Background(tt-weasels)
+exten => s,110,Hangup
+exten => s,5,GotoIf($[ "${CALLERID(num)}" = "7773334444" & "${CALLERID(name)}" : "Privacy Manager" ]?callerid-liar,s,1:s,7)
+\end{verbatim}
+\end{astlisting}
+
+I suggest using Zapateller at the beginning of the context, before
+anything else, on incoming calls.This can be followed by the
+PrivacyManager App.
+
+Make sure, if you do the PrivacyManager app, that you take care of the
+error condition! or their non-compliance will be rewarded with access
+to the system. In the above, if they can't enter a 10-digit number in
+3 tries, they get the humorous "I'm sorry, but all household members
+are currently helping other telemarketers...", "something is terribly
+wrong", "monkeys have carried them away...", various loud monkey
+screechings, "weasels have...", and a hangup. There are plenty of
+other paths to my torture scripts, I wanted to have some fun.
+
+In nearly all cases now, the telemarketers/charity-seekers that
+usually get thru to my main intro, hang up. I guess they can see it's
+pointless, or the average telemarketer/charity-seeker is instructed
+not to enter options when encountering such systems. Don't know.
+
+
+\subsection{Next: Torture Them!}
+
+I have developed an elaborate script to torture Telemarketers, and
+entertain friends. (See
+\url{http://www.voip-info.org/wiki-Asterisk+Telemarketer+Torture} )
+
+While mostly those that call in and traverse my teletorture scripts
+are those we know, and are doing so out of curiosity, there have been
+these others from Jan 1st,2004 thru June 1st, 2004:
+(the numbers may or may not be correct.)
+
+\begin{itemize}
+ \item 603890zzzz -- hung up telemarket options.
+ \item "Integrated Sale" -- called a couple times. hung up in telemarket options
+ \item "UNITED STATES GOV" -- maybe a military recruiter, trying to lure one of my sons.
+ \item 800349zzzz -- hung up in charity intro
+ \item 800349zzzz -- hung up in charity choices, intro, about the only one who actually travelled to the bitter bottom of the scripts!
+ \item 216377zzzz -- hung up the magazine section
+ \item 626757zzzz = "LIR " (pronounced "Liar"?) hung up in telemarket intro, then choices
+ \item 757821zzzz -- hung up in new magazine subscription options.
+\end{itemize}
+
+That averages out to maybe 1 a month. That puts into question whether
+the ratio of the amount of labor it took to make the scripts versus
+the benefits of lower call volumes was worth it, but, well, I had fun,
+so what the heck.
+
+but, that's about it. Not a whole lot. But I haven't had to say "NO"
+or "GO AWAY" to any of these folks for about a year now ...!
+
+\subsection{Using Call Screening}
+
+Another option is to use call screening in the Dial command. It has
+two main privacy modes, one that remembers the CID of the caller, and
+how the callee wants the call handled, and the other, which does not
+have a "memory".
+
+Turning on these modes in the dial command results in this sequence of
+events, when someone calls you at an extension:
+
+\begin{enumerate}
+\item The caller calls the Asterisk system, and at some point, selects an
+option or enters an extension number that would dial your extension.
+
+\item Before ringing your extension, the caller is asked to supply an
+introduction. The application asks them: "After the tone, say your
+name". They are allowed 4 seconds of introduction.
+
+\item After that, they are told "Hang on, we will attempt to connect you
+to your party. Depending on your dial options, they will hear ringing
+indications, or get music on hold. I suggest music on hold.
+
+\item Your extension is then dialed. When (and if) you pick up, you are
+told that a caller presenting themselves as $<$their recorded intro is
+played$>$ is calling, and you have options, like being connected,
+sending them to voicemail, torture, etc.
+
+\item You make your selection, and the call is handled as you chose.
+\end{enumerate}
+
+There are some variations, and these will be explained in due course.
+
+
+To use these options, set your Dial to something like:
+\begin{astlisting}
+\begin{verbatim}
+exten => 3,3,Dial(Zap/5r3&Zap/6r3,35,tmPA(beep))
+ or
+exten => 3,3,Dial(Zap/5r3&Zap/6r3,35,tmP(something)A(beep))
+ or
+exten => 3,3,Dial(Zap/5r3&Zap/6r3,35,tmpA(beep))
+\end{verbatim}
+\end{astlisting}
+
+The 't' allows the dialed party to transfer the call using '\#'. It's
+optional.
+
+The 'm' is for music on hold. I suggest it. Otherwise, the calling
+party gets to hear all the ringing, and lack thereof. It is generally
+better to use Music On Hold. Lots of folks hang up after the 3rd or
+4th ring, and you might lose the call before you can enter an option!
+
+The 'P' option alone will database everything using the extension as a
+default 'tree'. To get multiple extensions sharing the same database, use
+P(some-shared-key). Also, if the same person has multiple extensions,
+use P(unique-id) on all their dial commands.
+
+Use little 'p' for screening. Every incoming call will include a
+prompt for the callee's choice.
+
+the A(beep), will generate a 'beep' that the callee will hear if they
+choose to talk to the caller. It's kind of a prompt to let the callee
+know that he has to say 'hi'. It's not required, but I find it
+helpful.
+
+When there is no CallerID, P and p options will always record an intro
+for the incoming caller. This intro will be stored temporarily in the
+\path{/var/lib/asterisk/sounds/priv-callerintros} dir, under the name
+NOCALLERID\_$<$extension$>$ $<$channelname$>$ and will be erased after the
+callee decides what to do with the call.
+
+Of course, NOCALLERID is not stored in the database. All those with no
+CALLERID will be considered "Unknown".
+
+\subsection{The 'N' and 'n' options}
+
+Two other options exist, that act as modifiers to the privacy options
+'P' and 'p'. They are 'N' and 'n'. You can enter them as dialing
+options, but they only affect things if P or p are also in the
+options.
+
+'N' says, "Only screen the call if no CallerID is present". So, if a
+callerID were supplied, it will come straight thru to your extension.
+
+'n' says, "Don't save any introductions". Folks will be asked to
+supply an introduction ("At the tone, say your name") every time they
+call. Their introductions will be removed after the callee makes a
+choice on how to handle the call. Whether the P option or the p option
+is used, the incoming caller will have to supply their intro every
+time they call.
+
+
+\subsection{Recorded Introductions}
+
+\subsubsection{Philosophical Side Note}
+The 'P' option stores the CALLERID in the database, along with the
+callee's choice of actions, as a convenience to the CALLEE, whereas
+introductions are stored and re-used for the convenience of the CALLER.
+
+\subsubsection{Introductions}
+Unless instructed to not save introductions (see the 'n' option above),
+the screening modes will save the recordings of the caller's names in
+the directory \path{/var/lib/asterisk/sounds/priv-callerintros}, if they have
+a CallerID. Just the 10-digit callerid numbers are used as filenames,
+with a ".gsm" at the end.
+
+Having these recordings around can be very useful, however...
+
+First of all, if a callerid is supplied, and a recorded intro for that
+number is already present, the caller is spared the inconvenience of
+having to supply their name, which shortens their call a bit.
+
+Next of all, these intros can be used in voicemail, played over
+loudspeakers, and perhaps other nifty things. For instance:
+
+\begin{astlisting}
+\begin{verbatim}
+exten => s,6,Set(PATH=/var/lib/asterisk/sounds/priv-callerintros)
+exten => s,7,System(/usr/bin/play ${PATH}/${CALLERID(num)}.gsm&,0)
+\end{verbatim}
+\end{astlisting}
+
+When a call comes in at the house, the above priority gets executed,
+and the callers intro is played over the phone systems speakers. This
+gives us a hint who is calling.
+
+(Note: the ,0 option at the end of the System command above, is a
+local mod I made to the System command. It forces a 0 result code to
+be returned, whether the play command successfully completed or
+not. Therefore, I don't have to ensure that the file exists or
+not. While I've turned this mod into the developers, it hasn't been
+incorporated yet. You might want to write an AGI or shell script to
+handle it a little more intelligently)
+
+And one other thing. You can easily supply your callers with an option
+to listen to, and re-record their introductions. Here's what I did in
+the home system's extensions.conf. (assume that a
+Goto(home-introduction,s,1) exists somewhere in your main menu as an
+option):
+
+\begin{astlisting}
+\begin{verbatim}
+[home-introduction]
+exten => s,1,Background(intro-options) ;; Script:
+ ;; To hear your Introduction, dial 1.
+ ;; to record a new introduction, dial 2.
+ ;; to return to the main menu, dial 3.
+ ;; to hear what this is all about, dial 4.
+exten => 1,1,Playback,priv-callerintros/${CALLERID(num)}
+exten => 1,2,Goto(s,1)
+exten => 2,1,Goto(home-introduction-record,s,1)
+exten => 3,1,Goto(homeline,s,7)
+exten => 4,1,Playback(intro-intro)
+ ;; Script:
+ ;; This may seem a little strange, but it really is a neat
+ ;; thing, both for you and for us. I've taped a short introduction
+ ;; for many of the folks who normally call us. Using the Caller ID
+ ;; from each incoming call, the system plays the introduction
+ ;; for that phone number over a speaker, just as the call comes in.
+ ;; This helps the folks
+ ;; here in the house more quickly determine who is calling.
+ ;; and gets the right ones to gravitate to the phone.
+ ;; You can listen to, and record a new intro for your phone number
+ ;; using this menu.
+exten => 4,2,Goto(s,1)
+exten => t,1,Goto(s,1)
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s,1)
+exten => o,1,Goto(s,1)
+
+[home-introduction-record]
+exten => s,1,Background(intro-record-choices) ;; Script:
+ ;; If you want some advice about recording your
+ ;; introduction, dial 1.
+ ;; otherwise, dial 2, and introduce yourself after
+ ;; the beep.
+exten => 1,1,Playback(intro-record)
+ ;; Your introduction should be short and sweet and crisp.
+ ;; Your introduction will be limited to 4 seconds.
+ ;; This is NOT meant to be a voice mail message, so
+ ;; please, don't say anything about why you are calling.
+ ;; After we are done making the recording, your introduction
+ ;; will be saved for playback.
+ ;; If you are the only person that would call from this number,
+ ;; please state your name. Otherwise, state your business
+ ;; or residence name instead. For instance, if you are
+ ;; friend of the family, say, Olie McPherson, and both
+ ;; you and your kids might call here a lot, you might
+ ;; say: "This is the distinguished Olie McPherson Residence!"
+ ;; If you are the only person calling, you might say this:
+ ;; "This is the illustrious Kermit McFrog! Pick up the Phone, someone!!"
+ ;; If you are calling from a business, you might pronounce a more sedate introduction,like,
+ ;; "Fritz from McDonalds calling.", or perhaps the more original introduction:
+ ;; "John, from the Park County Morgue. You stab 'em, we slab 'em!".
+ ;; Just one caution: the kids will hear what you record every time
+ ;; you call. So watch your language!
+ ;; I will begin recording after the tone.
+ ;; When you are done, hit the # key. Gather your thoughts and get
+ ;; ready. Remember, the # key will end the recording, and play back
+ ;; your intro. Good Luck, and Thank you!"
+exten => 1,2,Goto(2,1)
+exten => 2,1,Background(intro-start)
+ ;; OK, here we go! After the beep, please give your introduction.
+exten => 2,2,Background(beep)
+exten => 2,3,Record(priv-callerintros/${CALLERID(num)}:gsm,4)
+exten => 2,4,Background(priv-callerintros/${CALLERID(num)})
+exten => 2,5,Goto(home-introduction,s,1)
+exten => t,1,Goto(s,1)
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s,1)
+exten => o,1,Goto(s,1)
+\end{verbatim}
+\end{astlisting}
+
+In the above, you'd most likely reword the messages to your liking,
+and maybe do more advanced things with the 'error' conditions (i,o,t priorities),
+but I hope it conveys the idea.
+
+
diff --git a/trunk/doc/tex/qos.tex b/trunk/doc/tex/qos.tex
new file mode 100644
index 000000000..acbf7f443
--- /dev/null
+++ b/trunk/doc/tex/qos.tex
@@ -0,0 +1,135 @@
+\subsubsection{Introduction}
+
+Asterisk support different QoS settings on application level on various protocol
+on any of signaling and media. Type of Service (TOS) byte can be set on
+outgoing IP packets for various protocols. The TOS byte is used by the network
+to provide some level of Quality of Service (QoS) even if the network is
+congested with other traffic.
+
+Also asterisk running on Linux can set 802.1p CoS marks in VLAN packets for all
+used VoIP protocols. It is useful when you are working in switched environment.
+In fact asterisk only set priority for Linux socket. For mapping this priority
+and VLAN CoS mark you need to use this command:
+
+\begin{verbatim}
+vconfig set_egress_map [vlan-device] [skb-priority] [vlan-qos]
+\end{verbatim}
+
+In table behind shown all voice channels and other modules of asterisk, that
+support QoS settings for network traffic and type of traffic which can have
+QoS settings.
+
+\begin{verbatim}
+ Channel Drivers
++==============+===========+=====+=====+=====+
+| | Signaling |Audio|Video| Text|
++==============+===========+=====+=====+=====+
+|chan_sip | + | + | + | + |
+|--------------+-----------+-----+-----+-----+
+|chan_skinny | + | + | + | |
+|--------------+-----------+-----+-----+-----+
+|chan_mgcp | + | + | | |
+|--------------+-----------+-----+-----+-----+
+|chan_unistim | + | + | | |
+|--------------+-----------+-----+-----+-----+
+|chan_h323 | | + | | |
+|--------------+-----------+-----+-----+-----+
+|chan_iax2 | + |
++==============+=============================+
+ Other
++==============+=============================+
+| dundi.conf | + (tos setting) |
+|--------------+-----------------------------+
+| iaxprov.conf | + (tos setting) |
++==============+=============================+
+\end{verbatim}
+
+
+\subsubsection{IP TOS values}
+
+The allowable values for any of the tos* parameters are:
+CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, AF11, AF12, AF13, AF21, AF22, AF23,
+AF31, AF32, AF33, AF41, AF42, AF43 and ef (expedited forwarding),
+
+The tos* parameters also take numeric values.
+
+Note, that on Linux system you can use ef value in case your asterisk is running
+from a user other then root only when you have compiled asterisk with libcap.
+
+The lowdelay, throughput, reliability, mincost, and none values are removed
+in current releases.
+
+\subsubsection{802.1p CoS values}
+
+As far as 802.1p uses 3 bites from VLAN header, there are parameter can take
+integer values from 0 to 7.
+
+\subsubsection{Recommended values}
+Recommended values shown above and also included in sample configuration files:
+\begin{verbatim}
++============+=========+======+
+| | tos | cos |
++============+=========+======+
+|Signaling | cs3 | 3 |
+|Audio | ef | 5 |
+|Video | af41 | 4 |
+|Text | af41 | 3 |
+|Other | ef | |
++============+=========+======+
+\end{verbatim}
+
+\subsubsection{IAX2}
+
+In iax.conf, there is a "tos" parameter that sets the global default TOS
+for IAX packets generated by chan\_iax2. Since IAX connections combine
+signalling, audio, and video into one UDP stream, it is not possible
+to set the TOS separately for the different types of traffic.
+
+In iaxprov.conf, there is a "tos" parameter that tells the IAXy what TOS
+to set on packets it generates. As with the parameter in iax.conf,
+IAX packets generated by an IAXy cannot have different TOS settings
+based upon the type of packet. However different IAXy devices can
+have different TOS settings.
+
+\subsubsection{SIP}
+
+In sip.conf, there are three parameters that control the TOS settings:
+"tos\_sip", "tos\_audio", "tos\_video" and "tos\_text". tos\_sip controls
+what TOS SIP call signaling packets are set to. tos\_audio, tos\_video
+and tos\_text controls what TOS RTP audio, video or text accordingly
+packets are set to.
+
+There are four parameters to control 802.1p CoS: "cos\_sip", "cos\_audio",
+"cos\_video" and "cos\_text". It behavior the same as written above.
+
+\subsubsection{Other RTP channels}
+
+chan\_mgcp, chan\_h323, chan\_skinny and chan\_unistim also support TOS and
+CoS via setting tos and cos parameters in correspond to module config
+files. Naming style and behavior same as for chan\_sip.
+
+\subsubsection{Reference}
+
+IEEE 802.1Q Standard:
+\url{http://standards.ieee.org/getieee802/download/802.1Q-1998.pdf}
+Related protocols: IEEE 802.3, 802.2, 802.1D, 802.1Q
+
+RFC 2474 - "Definition of the Differentiated Services Field
+(DS field) in the IPv4 and IPv6 Headers", Nichols, K., et al,
+December 1998.
+
+IANA Assignments, DSCP registry
+Differentiated Services Field Codepoints
+\url{http://www.iana.org/assignments/dscp-registry}
+
+To get the most out of setting the TOS on packets generated by
+Asterisk, you will need to ensure that your network handles packets
+with a TOS properly. For Cisco devices, see the previously mentioned
+"Enterprise QoS Solution Reference Network Design Guide". For Linux
+systems see the "Linux Advanced Routing \& Traffic Control HOWTO" at
+\url{http://www.lartc.org/}.
+
+For more information on Quality of
+Service for VoIP networks see the "Enterprise QoS Solution Reference
+Network Design Guide" version 3.3 from Cisco at:
+\url{http://www.cisco.com/application/pdf/en/us/guest/netsol/ns432/c649/ccmigration\_09186a008049b062.pdf}
diff --git a/trunk/doc/tex/queuelog.tex b/trunk/doc/tex/queuelog.tex
new file mode 100644
index 000000000..daf650a26
--- /dev/null
+++ b/trunk/doc/tex/queuelog.tex
@@ -0,0 +1,118 @@
+In order to properly manage ACD queues, it is important to be able to
+keep track of details of call setups and teardowns in much greater detail
+than traditional call detail records provide. In order to support this,
+extensive and detailed tracing of every queued call is stored in the
+queue log, located (by default) in \path{/var/log/asterisk/queue_log}.
+
+These are the events (and associated information) in the queue log:
+
+\textbf{ABANDON(position$|$origposition$|$waittime)}
+
+The caller abandoned their position in the queue. The position is the
+caller's position in the queue when they hungup, the origposition is
+the original position the caller was when they first entered the
+queue, and the waittime is how long the call had been waiting in the
+queue at the time of disconnect.
+
+\textbf{AGENTDUMP}
+
+The agent dumped the caller while listening to the queue announcement.
+
+\textbf{AGENTLOGIN(channel)}
+
+The agent logged in. The channel is recorded.
+
+\textbf{AGENTCALLBACKLOGIN(exten@context)}
+
+The callback agent logged in. The login extension and context is recorded.
+
+\textbf{AGENTLOGOFF(channel$|$logintime)}
+
+The agent logged off. The channel is recorded, along with the total time
+the agent was logged in.
+
+\textbf{AGENTCALLBACKLOGOFF(exten@context$|$logintime$|$reason)}
+
+The callback agent logged off. The last login extension and context is
+recorded, along with the total time the agent was logged in, and the
+reason for the logoff if it was not a normal logoff
+(e.g., Autologoff, Chanunavail)
+
+\textbf{COMPLETEAGENT(holdtime$|$calltime$|$origposition)}
+
+The caller was connected to an agent, and the call was terminated normally
+by the *agent*. The caller's hold time and the length of the call are both
+recorded. The caller's original position in the queue is recorded in
+origposition.
+
+\textbf{COMPLETECALLER(holdtime$|$calltime$|$origposition)}
+
+The caller was connected to an agent, and the call was terminated normally
+by the *caller*. The caller's hold time and the length of the call are both
+recorded. The caller's original position in the queue is recorded in
+origposition.
+
+\textbf{CONFIGRELOAD}
+
+The configuration has been reloaded (e.g. with asterisk -rx reload)
+
+\textbf{CONNECT(holdtime$|$bridgedchanneluniqueid$|$ringtime)}
+
+The caller was connected to an agent. Hold time represents the amount
+of time the caller was on hold. The bridged channel unique ID contains
+the unique ID of the queue member channel that is taking the call. This
+is useful when trying to link recording filenames to a particular
+call in the queue. Ringtime represents the time the queue members phone
+was ringing prior to being answered.
+
+\textbf{ENTERQUEUE(url$|$callerid)}
+
+A call has entered the queue. URL (if specified) and Caller*ID are placed
+in the log.
+
+\textbf{EXITEMPTY(position$|$origposition$|$waittime)}
+
+The caller was exited from the queue forcefully because the queue had no
+reachable members and it's configured to do that to callers when there
+are no reachable members. The position is the caller's position in the
+queue when they hungup, the origposition is the original position the
+caller was when they first entered the queue, and the waittime is how
+long the call had been waiting in the queue at the time of disconnect.
+
+\textbf{EXITWITHKEY(key$|$position$|$origposition$|$waittime)}
+
+The caller elected to use a menu key to exit the queue. The key and
+the caller's position in the queue are recorded. The caller's entry
+position and amoutn of time waited is also recorded.
+
+\textbf{EXITWITHTIMEOUT(position$|$origposition$|$waittime)}
+
+The caller was on hold too long and the timeout expired. The position in the
+queue when the timeout occurred, the entry position, and the amount of time
+waited are logged.
+
+\textbf{QUEUESTART}
+
+The queueing system has been started for the first time this session.
+
+\textbf{RINGNOANSWER(ringtime)}
+
+After trying for ringtime ms to connect to the available queue member,
+the attempt ended without the member picking up the call. Bad queue
+member!
+
+\textbf{SYSCOMPAT}
+
+A call was answered by an agent, but the call was dropped because the
+channels were not compatible.
+
+\textbf{TRANSFER(extension$|$context$|$holdtime$|$calltime)}
+
+Caller was transferred to a different extension. Context and extension
+are recorded. The caller's hold time and the length of the call are both
+recorded. PLEASE remember that transfers performed by SIP UA's by way
+of a reinvite may not always be caught by Asterisk and trigger off this
+event. The only way to be 100\% sure that you will get this event when
+a transfer is performed by a queue member is to use the built-in transfer
+functionality of Asterisk.
+
diff --git a/trunk/doc/tex/queues-with-callback-members.tex b/trunk/doc/tex/queues-with-callback-members.tex
new file mode 100644
index 000000000..36e642845
--- /dev/null
+++ b/trunk/doc/tex/queues-with-callback-members.tex
@@ -0,0 +1,551 @@
+
+\section{Introduction}
+
+Pardon, but the dialplan in this tutorial will be expressed
+in AEL, the new Asterisk Extension Language. If you are
+not used to its syntax, we hope you will find it to some
+degree intuitive. If not, there are documents explaining
+its syntax and constructs.
+
+
+\section{Configuring Call Queues}
+
+\subsection{queues.conf}
+First of all, set up call queues in queue.conf
+
+Here is an example:
+
+\begin{astlisting}
+\begin{verbatim}
+ =========== queues.conf ===========
+ | ; Cool Digium Queues |
+ | [general] |
+ | persistentmembers = yes |
+ | |
+ | ; General sales queue |
+ | [sales-general] |
+ | music=default |
+ | context=sales |
+ | strategy=ringall |
+ | joinempty=strict |
+ | leavewhenempty=strict |
+ | |
+ | ; Customer service queue |
+ | [customerservice] |
+ | music=default |
+ | context=customerservice |
+ | strategy=ringall |
+ | joinempty=strict |
+ | leavewhenempty=strict |
+ | |
+ | ; Support dispatch queue |
+ | [dispatch] |
+ | music=default |
+ | context=dispatch |
+ | strategy=ringall |
+ | joinempty=strict |
+ | leavewhenempty=strict |
+ ===================================
+\end{verbatim}
+\end{astlisting}
+
+In the above, we have defined 3 separate calling queues:
+sales-general, customerservice, and dispatch.
+
+Please note that the sales-general queue specifies a
+context of "sales", and that customerservice specifies the
+context of "customerservice", and the dispatch
+queue specifies the context "dispatch". These three
+contexts must be defined somewhere in your dialplan.
+We will show them after the main menu below.
+
+In the [general] section, specifying the persistentmembers=yes,
+will cause the agent lists to be stored in astdb, and
+recalled on startup.
+
+The strategy=ringall will cause all agents to be dialed
+together, the first to answer is then assigned the incoming
+call.
+
+"joinempty" set to "strict" will keep incoming callers from
+being placed in queues where there are no agents to take calls.
+The Queue() application will return, and the dial plan can
+determine what to do next.
+
+If there are calls queued, and the last agent logs out, the
+remaining incoming callers will immediately be removed from
+the queue, and the Queue() call will return, IF the "leavewhenempty" is
+set to "strict".
+
+\subsection{Routing incoming Calls to Queues}
+
+
+Then in extensions.ael, you can do these things:
+
+\subsubsection{The Main Menu}
+
+At Digium, incoming callers are sent to the "mainmenu" context, where they
+are greeted, and directed to the numbers they choose...
+
+\begin{astlisting}
+\begin{verbatim}
+context mainmenu {
+
+ includes {
+ digium;
+ queues-loginout;
+ }
+
+ 0 => goto dispatch,s,1;
+ 2 => goto sales,s,1;
+ 3 => goto customerservice,s,1;
+ 4 => goto dispatch,s,1;
+
+ s => {
+ Ringing();
+ Wait(1);
+ Set(attempts=0);
+ Answer();
+ Wait(1);
+ Background(digium/ThankYouForCallingDigium);
+ Background(digium/YourOpenSourceTelecommunicationsSupplier);
+ WaitExten(0.3);
+ repeat:
+ Set(attempts=$[${attempts} + 1]);
+ Background(digium/IfYouKnowYourPartysExtensionYouMayDialItAtAnyTime);
+ WaitExten(0.1);
+ Background(digium/Otherwise);
+ WaitExten(0.1);
+ Background(digium/ForSalesPleasePress2);
+ WaitExten(0.2);
+ Background(digium/ForCustomerServicePleasePress3);
+ WaitExten(0.2);
+ Background(digium/ForAllOtherDepartmentsPleasePress4);
+ WaitExten(0.2);
+ Background(digium/ToSpeakWithAnOperatorPleasePress0AtAnyTime);
+ if( ${attempts} < 2 ) {
+ WaitExten(0.3);
+ Background(digium/ToHearTheseOptionsRepeatedPleaseHold);
+ }
+ WaitExten(5);
+ if( ${attempts} < 2 ) goto repeat;
+ Background(digium/YouHaveMadeNoSelection);
+ Background(digium/ThisCallWillBeEnded);
+ Background(goodbye);
+ Hangup();
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+\subsubsection{The Contexts referenced from the queues.conf file}
+
+\begin{astlisting}
+\begin{verbatim}
+context sales {
+
+ 0 => goto dispatch,s,1;
+ 8 => Voicemail(${SALESVM});
+
+ s => {
+ Ringing();
+ Wait(2);
+ Background(digium/ThankYouForContactingTheDigiumSalesDepartment);
+ WaitExten(0.3);
+ Background(digium/PleaseHoldAndYourCallWillBeAnsweredByOurNextAvailableSalesRepresentative);
+ WaitExten(0.3);
+ Background(digium/AtAnyTimeYouMayPress0ToSpeakWithAnOperatorOr8ToLeaveAMessage);
+ Set(CALLERID(name)=Sales);
+ Queue(sales-general,t);
+ Set(CALLERID(name)=EmptySalQ);
+ goto dispatch,s,1;
+ Playback(goodbye);
+ Hangup();
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+Please note that there is only one attempt to queue a call in the sales queue. All sales agents that
+are logged in will be rung.
+
+\begin{astlisting}
+\begin{verbatim}
+context customerservice {
+
+ 0 => {
+ SetCIDName(CSVTrans);
+ goto dispatch|s|1;
+ }
+ 8 => Voicemail(${CUSTSERVVM});
+
+ s => {
+ Ringing();
+ Wait(2);
+ Background(digium/ThankYouForCallingDigiumCustomerService);
+ WaitExten(0.3);
+ notracking:
+ Background(digium/PleaseWaitForTheNextAvailableCustomerServiceRepresentative);
+ WaitExten(0.3);
+ Background(digium/AtAnyTimeYouMayPress0ToSpeakWithAnOperatorOr8ToLeaveAMessage);
+ Set(CALLERID(name)=Cust Svc);
+ Set(QUEUE_MAX_PENALTY=10);
+ Queue(customerservice,t);
+ Set(QUEUE_MAX_PENALTY=0);
+ Queue(customerservice,t);
+ Set(CALLERID(name)=EmptyCSVQ);
+ goto dispatch,s,1;
+ Background(digium/NoCustomerServiceRepresentativesAreAvailableAtThisTime);
+ Background(digium/PleaseLeaveAMessageInTheCustomerServiceVoiceMailBox);
+ Voicemail(${CUSTSERVVM});
+ Playback(goodbye);
+ Hangup();
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+Note that calls coming into customerservice will first be try to queue
+calls to those agents with a QUEUE\_MAX\_PENALTY of 10, and if none are available,
+then all agents are rung.
+
+\begin{astlisting}
+\begin{verbatim}
+context dispatch
+{
+
+ s => {
+ Ringing();
+ Wait(2);
+ Background(digium/ThankYouForCallingDigium);
+ WaitExten(0.3);
+ Background(digium/YourCallWillBeAnsweredByOurNextAvailableOperator);
+ Background(digium/PleaseHold);
+ Set(QUEUE_MAX_PENALTY=10);
+ Queue(dispatch|t);
+ Set(QUEUE_MAX_PENALTY=20);
+ Queue(dispatch|t);
+ Set(QUEUE_MAX_PENALTY=0);
+ Queue(dispatch|t);
+ Background(digium/NoOneIsAvailableToTakeYourCall);
+ Background(digium/PleaseLeaveAMessageInOurGeneralVoiceMailBox);
+ Voicemail(${DISPATCHVM});
+ Playback(goodbye);
+ Hangup();
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+And in the dispatch context, first agents of priority 10 are tried, then
+20, and if none are available, all agents are tried.
+
+Notice that a common pattern is followed in each of the three queue contexts:
+
+First, you set QUEUE\_MAX\_PENALTY to a value, then you call
+Queue($<$queue-name$>$,option,...) (see the Queue application documetation for details)
+
+In the above, note that the "t" option is specified, and this allows the
+agent picking up the incoming call the luxury of transferring the call to
+other parties.
+
+The purpose of specifying the QUEUE\_MAX\_PENALTY is to develop a set of priorities
+amongst agents. By the above usage, agents with lower number priorities will
+be given the calls first, and then, if no-one picks up the call, the QUEUE\_MAX\_PENALTY
+will be incremented, and the queue tried again. Hopefully, along the line, someone
+will pick up the call, and the Queue application will end with a hangup.
+
+The final attempt to queue in most of our examples sets the QUEUE\_MAX\_PENALTY
+to zero, which means to try all available agents.
+
+
+\subsection{Assigning agents to Queues}
+
+In this example dialplan, we want to be able to add and remove agents to
+handle incoming calls, as they feel they are available. As they log in,
+they are added to the queue's agent list, and as they log out, they are
+removed. If no agents are available, the queue command will terminate, and
+it is the duty of the dialplan to do something appropriate, be it sending
+the incoming caller to voicemail, or trying the queue again with a higher
+QUEUE\_MAX\_PENALTY.
+
+Because a single agent can make themselves available to more than one queue,
+the process of joining multiple queues can be handled automatically by the
+dialplan.
+
+\subsubsection{Agents Log In and Out}
+
+\begin{astlisting}
+\begin{verbatim}
+context queues-loginout
+{
+ 6092 => {
+ Answer();
+ Read(AGENT_NUMBER,agent-enternum);
+ VMAuthenticate(${AGENT_NUMBER}@default,s);
+ Set(queue-announce-success=1);
+ goto queues-manip,I${AGENT_NUMBER},1;
+ }
+
+ 6093 => {
+ Answer();
+ Read(AGENT_NUMBER,agent-enternum);
+ Set(queue-announce-success=1);
+ goto queues-manip,O${AGENT_NUMBER},1;
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+In the above contexts, the agents dial 6092 to log into their queues,
+and they dial 6093 to log out of their queues. The agent is prompted
+for their agent number, and if they are logging in, their passcode,
+and then they are transferred to the proper extension in the
+queues-manip context. The queues-manip context does all the
+actual work:
+
+\begin{astlisting}
+\begin{verbatim}
+context queues-manip {
+
+ // Raquel Squelch
+ _[IO]6121 => {
+ &queue-addremove(dispatch,10,${EXTEN});
+ &queue-success(${EXTEN});
+ }
+
+ // Brittanica Spears
+ _[IO]6165 => {
+ &queue-addremove(dispatch,20,${EXTEN});
+ &queue-success(${EXTEN});
+ }
+
+ // Rock Hudson
+ _[IO]6170 => {
+ &queue-addremove(sales-general,10,${EXTEN});
+ &queue-addremove(customerservice,20,${EXTEN});
+ &queue-addremove(dispatch,30,${EXTEN});
+ &queue-success(${EXTEN});
+ }
+
+ // Saline Dye-on
+ _[IO]6070 => {
+ &queue-addremove(sales-general,20,${EXTEN});
+ &queue-addremove(customerservice,30,${EXTEN});
+ &queue-addremove(dispatch,30,${EXTEN});
+ &queue-success(${EXTEN});
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+In the above extensions, note that the queue-addremove macro is used
+to actually add or remove the agent from the applicable queue,
+with the applicable priority level. Note that agents with a
+priority level of 10 will be called before agents with levels
+of 20 or 30.
+
+In the above example, Raquel will be dialed first in the dispatch
+queue, if she has logged in. If she is not, then the second call of
+Queue() with priority of 20 will dial Brittanica if she is present,
+otherwise the third call of Queue() with MAX\_PENALTY of 0 will
+dial Rock and Saline simultaneously.
+
+Also note that Rock will be among the first to be called in the sales-general
+queue, and among the last in the dispatch queue. As you can see in
+main menu, the callerID is set in the main menu so they can tell
+which queue incoming calls are coming from.
+
+The call to queue-success() gives some feedback to the agent
+as they log in and out, that the process has completed.
+
+\begin{astlisting}
+\begin{verbatim}
+macro queue-success(exten)
+{
+ if( ${queue-announce-success} > 0 )
+ {
+ switch(${exten:0:1})
+ {
+ case I:
+ Playback(agent-loginok);
+ Hangup();
+ break;
+ case O:
+ Playback(agent-loggedoff);
+ Hangup();
+ break;
+ }
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+The queue-addremove macro is defined in this manner:
+
+\begin{astlisting}
+\begin{verbatim}
+macro queue-addremove(queuename,penalty,exten)
+{
+ switch(${exten:0:1})
+ {
+ case I: // Login
+ AddQueueMember(${queuename},Local/${exten:1}@agents,${penalty});
+ break;
+ case O: // Logout
+ RemoveQueueMember(${queuename},Local/${exten:1}@agents);
+ break;
+ case P: // Pause
+ PauseQueueMember(${queuename},Local/${exten:1}@agents);
+ break;
+ case U: // Unpause
+ UnpauseQueueMember(${queuename},Local/${exten:1}@agents);
+ break;
+ default: // Invalid
+ Playback(invalid);
+ break;
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+Basically, it uses the first character of the exten variable, to determine the
+proper actions to take. In the above dial plan code, only the cases I or O are used,
+which correspond to the Login and Logout actions.
+
+
+\subsection{Controlling The Way Queues Call the Agents}
+
+Notice in the above, that the commands to manipulate agents in queues have
+"@agents" in their arguments. This is a reference to the agents context:
+
+\begin{astlisting}
+\begin{verbatim}
+context agents
+{
+ // General sales queue
+ 8010 =>
+ {
+ Set(QUEUE_MAX_PENALTY=10);
+ Queue(sales-general,t);
+ Set(QUEUE_MAX_PENALTY=0);
+ Queue(sales-general,t);
+ Set(CALLERID(name)=EmptySalQ);
+ goto dispatch,s,1;
+ }
+ // Customer Service queue
+ 8011 =>
+ {
+ Set(QUEUE_MAX_PENALTY=10);
+ Queue(customerservice,t);
+ Set(QUEUE_MAX_PENALTY=0);
+ Queue(customerservice,t);
+ Set(CALLERID(name)=EMptyCSVQ);
+ goto dispatch,s,1;
+ }
+ 8013 =>
+ {
+ Dial(iax2/sweatshop/9456@from-ecstacy);
+
+ Set(CALLERID(name)=EmptySupQ);
+ Set(QUEUE_MAX_PENALTY=10);
+ Queue(support-dispatch,t);
+ Set(QUEUE_MAX_PENALTY=20);
+ Queue(support-dispatch,t);
+ Set(QUEUE_MAX_PENALTY=0); // means no max
+ Queue(support-dispatch,t);
+ goto dispatch,s,1;
+ }
+ 6121 => &callagent(${RAQUEL},${EXTEN});
+ 6165 => &callagent(${SPEARS},${EXTEN});
+ 6170 => &callagent(${ROCK},${EXTEN});
+ 6070 => &callagent(${SALINE},${EXTEN});
+}
+\end{verbatim}
+\end{astlisting}
+
+In the above, the variables \$\{RAQUEL\}, etc stand for
+actual devices to ring that person's
+phone (like Zap/37).
+
+The 8010, 8011, and 8013 extensions are purely for transferring
+incoming callers to queues. For instance, a customer service
+agent might want to transfer the caller to talk to sales. The
+agent only has to transfer to extension 8010, in this case.
+
+Here is the callagent macro, note that if a person in the
+queue is called, but does not answer, then they are automatically
+removed from the queue.
+
+\begin{astlisting}
+\begin{verbatim}
+macro callagent(device,exten)
+{
+ if( ${GROUP_COUNT(${exten}@agents)}=0 )
+ {
+ Set(OUTBOUND_GROUP=${exten}@agents);
+ Dial(${device},300,t);
+ switch(${DIALSTATUS})
+ {
+ case BUSY:
+ Busy();
+ break;
+ case NOANSWER:
+ Set(queue-announce-success=0);
+ goto queues-manip,O${exten},1;
+ default:
+ Hangup();
+ break;
+ }
+ }
+ else
+ {
+ Busy();
+ }
+}
+\end{verbatim}
+\end{astlisting}
+
+In the callagent macro above, the \$\{exten\} will
+be 6121, or 6165, etc, which is the extension of the agent.
+
+The use of the GROUP\_COUNT, and OUTBOUND\_GROUP follow this line
+of thinking. Incoming calls can be queued to ring all agents in the
+current priority. If some of those agents are already talking, they
+would get bothersome call-waiting tones. To avoid this inconvenience,
+when an agent gets a call, the OUTBOUND\_GROUP assigns that
+conversation to the group specified, for instance 6171@agents.
+The \$\{GROUP\_COUNT()\} variable on a subsequent call should return
+"1" for that group. If GROUP\_COUNT returns 1, then the busy()
+is returned without actually trying to dial the agent.
+
+\subsection{Pre Acknowledgement Message}
+
+If you would like to have a pre acknowledge message with option to reject the message
+you can use the following dialplan Macro as a base with the 'M' dial argument.
+
+\begin{astlisting}
+\begin{verbatim}
+[macro-screen]
+exten=>s,1,Wait(.25)
+exten=>s,2,Read(ACCEPT,screen-callee-options,1)
+exten=>s,3,Gotoif($[${ACCEPT} = 1] ?50)
+exten=>s,4,Gotoif($[${ACCEPT} = 2] ?30)
+exten=>s,5,Gotoif($[${ACCEPT} = 3] ?40)
+exten=>s,6,Gotoif($[${ACCEPT} = 4] ?30:30)
+exten=>s,30,Set(MACRO_RESULT=CONTINUE)
+exten=>s,40,Read(TEXTEN,custom/screen-exten,)
+exten=>s,41,Gotoif($[${LEN(${TEXTEN})} = 3]?42:45)
+exten=>s,42,Set(MACRO_RESULT=GOTO:from-internal^${TEXTEN}^1)
+exten=>s,45,Gotoif($[${TEXTEN} = 0] ?46:4)
+exten=>s,46,Set(MACRO_RESULT=CONTINUE)
+exten=>s,50,Playback(after-the-tone)
+exten=>s,51,Playback(connected)
+exten=>s,52,Playback(beep)
+\end{verbatim}
+\end{astlisting}
+
+\subsection{Caveats}
+
+In the above examples, some of the possible error checking has been omitted,
+to reduce clutter and make the examples clearer.
diff --git a/trunk/doc/tex/realtime.tex b/trunk/doc/tex/realtime.tex
new file mode 100644
index 000000000..62349f60c
--- /dev/null
+++ b/trunk/doc/tex/realtime.tex
@@ -0,0 +1,127 @@
+\subsubsection{Introduction}
+
+The Asterisk Realtime Architecture is a new set of drivers and
+functions implemented in Asterisk.
+
+The benefits of this architecture are many, both from a code management
+standpoint and from an installation perspective.
+
+The ARA is designed to be independent of storage. Currently, most
+drivers are based on SQL, but the architecture should be able to handle
+other storage methods in the future, like LDAP.
+
+The main benefit comes in the database support. In Asterisk v1.0 some
+functions supported MySQL database, some PostgreSQL and other ODBC.
+With the ARA, we have a unified database interface internally in Asterisk,
+so if one function supports database integration, all databases that has a
+realtime driver will be supported in that function.
+
+Currently there are three realtime database drivers:
+
+\begin{itemize}
+ \item ODBC: Support for UnixODBC, integrated into Asterisk
+ The UnixODBC subsystem supports many different databases,
+ please check \url{www.unixodbc.org} for more information.
+ \item MySQL: Found in the asterisk-addons subversion repository on \url{svn.digium.com}
+ \item PostgreSQL: Native support for Postgres, integrated into Asterisk
+\end{itemize}
+
+\subsubsection{Two modes: Static and Realtime}
+
+The ARA realtime mode is used to dynamically load and update objects.
+This mode is used in the SIP and IAX2 channels, as well as in the voicemail
+system. For SIP and IAX2 this is similar to the v1.0 MYSQL\_FRIENDS
+functionality. With the ARA, we now support many more databases for
+dynamic configuration of phones.
+
+The ARA static mode is used to load configuration files. For the Asterisk
+modules that read configurations, there's no difference between a static
+file in the file system, like extensions.conf, and a configuration loaded
+from a database.
+
+\subsubsection{Realtime SIP friends}
+
+The SIP realtime objects are users and peers that are loaded in memory
+when needed, then deleted. This means that Asterisk currently can't handle
+voicemail notification and NAT keepalives for these peers. Other than that,
+most of the functionality works the same way for realtime friends as for
+the ones in static configuration.
+
+With caching, the device stays in memory for a specified time. More
+information about this is to be found in the sip.conf sample file.
+
+If you specify a separate family called "sipregs" SIP registration
+data will be stored in that table and not in the "sippeers" table.
+
+\subsubsection{Realtime H.323 friends}
+
+Like SIP realtime friends, H.323 friends also can be configured using
+dynamic realtime objects.
+
+\subsubsection{New function in the dial plan: The Realtime Switch}
+
+The realtime switch is more than a port of functionality in v1.0 to the
+new architecture, this is a new feature of Asterisk based on the
+ARA. The realtime switch lets your Asterisk server do database lookups
+of extensions in realtime from your dial plan. You can have many Asterisk
+servers sharing a dynamically updated dial plan in real time with this
+solution.
+
+Note that this switch does NOT support Caller ID matching, only
+extension name or pattern matching.
+
+\subsubsection{Capabilities}
+
+The realtime Architecture lets you store all of your configuration in
+databases and reload it whenever you want. You can force a reload over
+the AMI, Asterisk Manager Interface or by calling Asterisk from a
+shell script with
+
+ asterisk -rx "reload"
+
+You may also dynamically add SIP and IAX devices and extensions
+and making them available without a reload, by using the realtime
+objects and the realtime switch.
+
+\subsubsection{Configuration in extconfig.conf}
+
+You configure the ARA in extconfig.conf (yes, it's a strange name, but
+is was defined in the early days of the realtime architecture and kind
+of stuck).
+
+The part of Asterisk that connects to the ARA use a well defined family
+name to find the proper database driver. The syntax is easy:
+
+\begin{verbatim}
+ <family> => <realtime driver>,<db name>[,<table>]
+\end{verbatim}
+
+The options following the realtime driver identified depends on the
+driver.
+
+Defined well-known family names are:
+
+\begin{itemize}
+ \item sippeers, sipusers - SIP peers and users
+ \item iaxpeers, iaxusers - IAX2 peers and users
+ \item voicemail - Voicemail accounts
+ \item queues - Queues
+ \item queue\_members - Queue members
+ \item extensions - Realtime extensions (switch)
+\end{itemize}
+
+Voicemail storage with the support of ODBC described in file
+\path{docs/odbcstorage.tex} (\ref{odbcstorage}).
+
+\subsubsection{Limitations}
+
+Currently, realtime extensions do not support realtime hints. There is
+a workaround available by using func\_odbc. See the sample func\_odbc.conf
+for more information.
+
+\subsubsection{FreeTDS supported with connection pooling}
+
+In order to use a FreeTDS-based database with realtime, you need to turn
+connection pooling on in res\_odbc.conf. This is due to a limitation within
+the FreeTDS protocol itself. Please note that this includes databases such
+as MS SQL Server and Sybase. This support is new in the current release.
diff --git a/trunk/doc/tex/security.tex b/trunk/doc/tex/security.tex
new file mode 100644
index 000000000..4eb4e1095
--- /dev/null
+++ b/trunk/doc/tex/security.tex
@@ -0,0 +1,80 @@
+\subsection{Introduction}
+
+PLEASE READ THE FOLLOWING IMPORTANT SECURITY RELATED INFORMATION.
+IMPROPER CONFIGURATION OF ASTERISK COULD ALLOW UNAUTHORIZED USE OF YOUR
+FACILITIES, POTENTIALLY INCURRING SUBSTANTIAL CHARGES.
+
+Asterisk security involves both network security (encryption, authentication)
+as well as dialplan security (authorization - who can access services in
+your pbx). If you are setting up Asterisk in production use, please make
+sure you understand the issues involved.
+
+\subsection{Network Security}
+
+If you install Asterisk and use the "make samples" command to install
+a demonstration configuration, Asterisk will open a few ports for accepting
+VoIP calls. Check the channel configuration files for the ports and IP addresses.
+
+If you enable the manager interface in manager.conf, please make sure that
+you access manager in a safe environment or protect it with SSH or other
+VPN solutions.
+
+For all TCP/IP connections in Asterisk, you can set ACL lists that
+will permit or deny network access to Asterisk services. Please check
+the "permit" and "deny" configuration options in manager.conf and
+the VoIP channel configurations - i.e. sip.conf and iax.conf.
+
+The IAX2 protocol supports strong RSA key authentication as well as
+AES encryption of voice and signalling. The SIP channel does not
+support encryption in this version of Asterisk.
+
+\subsection{Dialplan Security}
+
+First and foremost remember this:
+
+USE THE EXTENSION CONTEXTS TO ISOLATE OUTGOING OR TOLL SERVICES FROM ANY
+INCOMING CONNECTIONS.
+
+You should consider that if any channel, incoming line, etc can enter an
+extension context that it has the capability of accessing any extension
+within that context.
+
+Therefore, you should NOT allow access to outgoing or toll services in
+contexts that are accessible (especially without a password) from incoming
+channels, be they IAX channels, FX or other trunks, or even untrusted
+stations within you network. In particular, never ever put outgoing toll
+services in the "default" context. To make things easier, you can include
+the "default" context within other private contexts by using:
+
+\begin{astlisting}
+\begin{verbatim}
+ include => default
+\end{verbatim}
+\end{astlisting}
+
+in the appropriate section. A well designed PBX might look like this:
+
+\begin{astlisting}
+\begin{verbatim}
+[longdistance]
+exten => _91NXXNXXXXXX,1,Dial(Zap/g2/${EXTEN:1})
+include => local
+
+[local]
+exten => _9NXXNXXX,1,Dial(Zap/g2/${EXTEN:1})
+include => default
+
+[default]
+exten => 6123,Dial(Zap/1)
+\end{verbatim}
+\end{astlisting}
+
+DON'T FORGET TO TAKE THE DEMO CONTEXT OUT OF YOUR DEFAULT CONTEXT. There
+isn't really a security reason, it just will keep people from wanting to
+play with your Asterisk setup remotely.
+
+\subsection{Log Security}
+
+Please note that the Asterisk log files, as well as information printed to the
+Asterisk CLI, may contain sensitive information such as passwords and call
+history. Keep this in mind when providing access to these resources.
diff --git a/trunk/doc/tex/sla.tex b/trunk/doc/tex/sla.tex
new file mode 100644
index 000000000..afafd2ae4
--- /dev/null
+++ b/trunk/doc/tex/sla.tex
@@ -0,0 +1,387 @@
+%\documentclass[12pt,a4]{article}
+%\usepackage{hyperref}
+
+%\author{Russell Bryant \\ Software Engineer \\ Digium, Inc.}
+%\title{Shared Line Appearances}
+
+%\begin{document}
+%\maketitle
+
+%\tableofcontents
+
+\section{Introduction}
+
+The "SLA" functionality in Asterisk is intended to allow a setup that emulates
+a simple key system. It uses the various abstraction layers already built into
+Asterisk to emulate key system functionality across various devices, including
+IP channels.
+
+\section{Configuration}
+
+\subsection{Summary}
+
+An SLA system is built up of virtual trunks and stations mapped to real
+Asterisk devices. The configuration for all of this is done in three
+different files: extensions.conf, sla.conf, and the channel specific
+configuration file such as sip.conf or zapata.conf.
+
+\subsection{Dialplan}
+
+The SLA implementation can automatically generate the dialplan necessary for
+basic operation if the "autocontext" option is set for trunks and stations in
+sla.conf. However, for reference, here is an automatically generated dialplan
+to help with custom building of the dialplan to include other features, such as
+voicemail (\ref{voicemail}).
+
+However, note that there is a little bit of additional configuration needed if
+the trunk is an IP channel. This is discussed in the section on trunks (\ref{trunks}).
+
+There are extensions for incoming calls on a specific trunk, which execute the SLATrunk
+application, as well as incoming calls from a station, which execute SLAStation.
+Note that there are multiple extensions for incoming calls from a station. This is
+because the SLA system has to know whether the phone just went off hook, or if the
+user pressed a specific line button.
+
+Also note that there is a hint for every line on every station. This lets the SLA
+system control each individual light on every phone to ensure that it shows the
+correct state of the line. The phones must subscribe to the state of each of their
+line appearances.
+
+Please refer to the examples section for full dialplan samples for SLA.
+
+\subsection{Trunks}
+\label{trunks}
+
+An SLA trunk is a mapping between a virtual trunk and a real Asterisk device.
+This device may be an analog FXO line, or something like a SIP trunk. A trunk
+must be configured in two places. First, configure the device itself in the
+channel specific configuration file such as zapata.conf or sip.conf. Once the
+trunk is configured, then map it to an SLA trunk in sla.conf.
+\begin{astlisting}
+\begin{verbatim}
+[line1]
+type=trunk
+device=Zap/1
+\end{verbatim}
+\end{astlisting}
+
+Be sure to configure the trunk's context to be the same one that is set for the
+"autocontext" option in sla.conf if automatic dialplan configuration is used.
+This would be done in the regular device entry in zapata.conf, sip.conf, etc.
+Note that the automatic dialplan generation creates the SLATrunk() extension
+at extension 's'. This is perfect for Zap channels that are FXO trunks, for
+example. However, it may not be good enough for an IP trunk, since the call
+coming in over the trunk may specify an actual number.
+
+If the dialplan is being built manually, ensure that calls coming in on a trunk
+execute the SLATrunk() application with an argument of the trunk name, as shown
+in the dialplan example before.
+
+IP trunks can be used, but they require some additional configuration to work.
+
+For this example, let's say we have a SIP trunk called "mytrunk" that is going
+to be used as line4. Furthermore, when calls come in on this trunk, they are
+going to say that they are calling the number "12564286000". Also, let's say
+that the numbers that are valid for calling out this trunk are NANP numbers,
+of the form \_1NXXNXXXXXX.
+
+In sip.conf, there would be an entry for [mytrunk]. For [mytrunk],
+set context=line4.
+
+\begin{astlisting}
+\begin{verbatim}
+[line4]
+type=trunk
+device=Local/disa@line4_outbound
+\end{verbatim}
+\end{astlisting}
+
+\begin{astlisting}
+\begin{verbatim}
+[line4]
+exten => 12564286000,1,SLATrunk(line4)
+
+[line4_outbound]
+exten => disa,1,Disa(no-password,line4_outbound)
+exten => _1NXXNXXXXXX,1,Dial(SIP/${EXTEN}@mytrunk)
+\end{verbatim}
+\end{astlisting}
+
+So, when a station picks up their phone and connects to line 4, they are
+connected to the local dialplan. The Disa application plays dialtone to the
+phone and collects digits until it matches an extension. In this case, once
+the phone dials a number like 12565551212, the call will proceed out the
+SIP trunk.
+
+\subsection{Stations}
+
+An SLA station is a mapping between a virtual station and a real Asterisk device.
+Currently, the only channel driver that has all of the features necessary to
+support an SLA environment is chan\_sip. So, to configure a SIP phone to use
+as a station, you must configure sla.conf and sip.conf.
+
+\begin{astlisting}
+\begin{verbatim}
+[station1]
+type=station
+device=SIP/station1
+trunk=line1
+trunk=line2
+\end{verbatim}
+\end{astlisting}
+
+Here are some hints on configuring a SIP phone for use with SLA:
+
+\begin{enumerate}
+\item Add the SIP channel as a [station] in sla.conf.
+
+\item Configure the phone in sip.conf. If automatic dialplan configuration was
+ used by enabling the "autocontext" option in sla.conf, then this entry in
+ sip.conf should have the same context setting.
+
+\item On the phone itself, there are various things that must be configured to
+ make everything work correctly:
+
+ Let's say this phone is called "station1" in sla.conf, and it uses trunks
+ named "line1" and line2".
+ \begin{enumerate}
+
+ \item Two line buttons must be configured to subscribe to the state of the
+ following extensions:
+ - station1\_line1
+ - station1\_line2
+
+ \item The line appearance buttons should be configured to dial the extensions
+ that they are subscribed to when they are pressed.
+
+ \item If you would like the phone to automatically connect to a trunk when it
+ is taken off hook, then the phone should be automatically configured to
+ dial "station1" when it is taken off hook.
+
+ \end{enumerate}
+\end{enumerate}
+
+
+\section{Configuration Examples}
+\subsection{Basic SLA}
+
+This is an example of the most basic SLA setup. It uses the automatic
+dialplan generation so the configuration is minimal.
+
+sla.conf:
+\begin{astlisting}
+\begin{verbatim}
+[line1]
+type=trunk
+device=Zap/1
+autocontext=line1
+
+[line2]
+type=trunk
+device=Zap/2
+autocontext=line2
+
+[station](!)
+type=station
+trunk=line1
+trunk=line2
+autocontext=sla_stations
+
+[station1](station)
+device=SIP/station1
+
+[station2](station)
+device=SIP/station2
+
+[station3](station)
+device=SIP/station3
+\end{verbatim}
+\end{astlisting}
+
+With this configuration, the dialplan is generated automatically. The first
+zap channel should have its context set to "line1" and the second should be
+set to "line2" in zapata.conf. In sip.conf, station1, station2, and station3
+should all have their context set to "sla\_stations".
+
+For reference, here is the automatically generated dialplan for this situation:
+\begin{astlisting}
+\begin{verbatim}
+[line1]
+exten => s,1,SLATrunk(line1)
+
+[line2]
+exten => s,2,SLATrunk(line2)
+
+[sla_stations]
+exten => station1,1,SLAStation(station1)
+exten => station1_line1,hint,SLA:station1_line1
+exten => station1_line1,1,SLAStation(station1_line1)
+exten => station1_line2,hint,SLA:station1_line2
+exten => station1_line2,1,SLAStation(station1_line2)
+
+exten => station2,1,SLAStation(station2)
+exten => station2_line1,hint,SLA:station2_line1
+exten => station2_line1,1,SLAStation(station2_line1)
+exten => station2_line2,hint,SLA:station2_line2
+exten => station2_line2,1,SLAStation(station2_line2)
+
+exten => station3,1,SLAStation(station3)
+exten => station3_line1,hint,SLA:station3_line1
+exten => station3_line1,1,SLAStation(station3_line1)
+exten => station3_line2,hint,SLA:station3_line2
+exten => station3_line2,1,SLAStation(station3_line2)
+\end{verbatim}
+\end{astlisting}
+
+\subsection{SLA and Voicemail}
+\label{voicemail}
+
+This is an example of how you could set up a single voicemail box for the
+phone system. The voicemail box number used in this example is 1234, which
+would be configured in voicemail.conf.
+
+For this example, assume that there are 2 trunks and 3 stations. The trunks
+are Zap/1 and Zap/2. The stations are SIP/station1, SIP/station2, and
+SIP/station3.
+
+In zapata.conf, channel 1 has context=line1 and channel 2 has context=line2.
+
+In sip.conf, all three stations are configured with context=sla\_stations.
+
+When the stations pick up their phones to dial, they are allowed to dial
+NANP numbers for outbound calls, or 8500 for checking voicemail.
+
+
+sla.conf:
+\begin{astlisting}
+\begin{verbatim}
+[line1]
+type=trunk
+device=Local/disa@line1_outbound
+
+[line2]
+type=trunk
+device=Local/disa@line2_outbound
+
+[station](!)
+type=station
+trunk=line1
+trunk=line2
+
+[station1](station)
+device=SIP/station1
+
+[station2](station)
+device=SIP/station2
+
+[station3](station)
+device=SIP/station3
+
+\end{verbatim}
+\end{astlisting}
+
+extensions.conf:
+\begin{astlisting}
+\begin{verbatim}
+[macro-slaline]
+exten => s,1,SLATrunk(${ARG1})
+exten => s,n,Goto(s-${SLATRUNK_STATUS},1)
+exten => s-FAILURE,1,Voicemail(1234,u)
+exten => s-UNANSWERED,1,Voicemail(1234,u)
+
+[line1]
+exten => s,1,Macro(slaline,line1)
+
+[line2]
+exten => s,2,Macro(slaline,line2)
+
+[line1_outbound]
+exten => disa,1,Disa(no-password,line1_outbound)
+exten => _1NXXNXXXXXX,1,Dial(Zap/1/${EXTEN})
+exten => 8500,1,VoicemailMain(1234)
+
+[line2_outbound]
+exten => disa,1,Disa(no-password|line2_outbound)
+exten => _1NXXNXXXXXX,1,Dial(Zap/2/${EXTEN})
+exten => 8500,1,VoicemailMain(1234)
+
+[sla_stations]
+
+exten => station1,1,SLAStation(station1)
+exten => station1_line1,hint,SLA:station1_line1
+exten => station1_line1,1,SLAStation(station1_line1)
+exten => station1_line2,hint,SLA:station1_line2
+exten => station1_line2,1,SLAStation(station1_line2)
+
+exten => station2,1,SLAStation(station2)
+exten => station2_line1,hint,SLA:station2_line1
+exten => station2_line1,1,SLAStation(station2_line1)
+exten => station2_line2,hint,SLA:station2_line2
+exten => station2_line2,1,SLAStation(station2_line2)
+
+exten => station3,1,SLAStation(station3)
+exten => station3_line1,hint,SLA:station3_line1
+exten => station3_line1,1,SLAStation(station3_line1)
+exten => station3_line2,hint,SLA:station3_line2
+exten => station3_line2,1,SLAStation(station3_line2)
+
+\end{verbatim}
+\end{astlisting}
+
+\section{Call Handling}
+\subsection{Summary}
+
+This section is intended to describe how Asterisk handles calls inside of the
+SLA system so that it is clear what behavior is expected.
+
+\subsection{Station goes off hook (not ringing)}
+
+When a station goes off hook, it should initiate a call to Asterisk with the
+extension that indicates that the phone went off hook without specifying a
+specific line. In the examples in this document, for the station named
+"station1", this extension is simply named, "station1".
+
+Asterisk will attempt to connect this station to the first available trunk
+that is not in use. Asterisk will check the trunks in the order that they
+were specified in the station entry in sla.conf. If all trunks are in use,
+the call will be denied.
+
+If Asterisk is able to acquire an idle trunk for this station, then trunk
+is connected to the station and the station will hear dialtone. The station
+can then proceed to dial a number to call. As soon as a trunk is acquired,
+all appearances of this line on stations will show that the line is in use.
+
+\subsection{Station goes off hook (ringing)}
+
+When a station goes off hook while it is ringing, it should simply answer
+the call that had been initiated to it to make it ring. Once the station
+has answered, Asterisk will figure out which trunk to connect it to. It
+will connect it to the highest priority trunk that is currently ringing.
+Trunk priority is determined by the order that the trunks are listed in
+the station entry in sla.conf.
+
+\subsection{Line button on a station is pressed}
+
+When a line button is pressed on a station, the station should initiate a
+call to Asterisk with the extension that indicates which line button was
+pressed. In the examples given in this document, for a station named
+"station1" and a trunk named "line1", the extension would be "station1\_line1".
+
+If the specified trunk is not in use, then the station will be connected to it and
+will hear dialtone. All appearances of this trunk will then show that it
+is now in use.
+
+If the specified trunk is on hold by this station, then this station will be
+reconnected to the trunk. The line appearance for this trunk on this station
+will now show in use. If this was the only station that had the call on hold,
+then all appearances of this trunk will now show that it is in use. Otherwise,
+all stations that are not currently connected to this trunk will show it
+on hold.
+
+If the specified trunk is on hold by a different station, then this station
+will be connected to the trunk only if the trunk itself and the station(s) that
+have it on hold do not have private hold enabled. If connected, the appeareance
+of this trunk on this station will then show in use. All stations that are not
+currently connected to this trunk will show it on hold.
+
+%\end{document}
diff --git a/trunk/doc/unistim.txt b/trunk/doc/unistim.txt
new file mode 100644
index 000000000..f0bfe12bb
--- /dev/null
+++ b/trunk/doc/unistim.txt
@@ -0,0 +1,127 @@
+This is a channel driver for Unistim protocol. You can use a least a Nortel i2002, i2004 and i2050.
+Following features are supported : Send/Receive CallerID, Redial, SoftKeys, SendText(), Music On Hold, Message Waiting Indication (MWI), Distinctive ring, Transfer, Threeway call, History, Forward, Dynamic SoftKeys.
+
+How to configure the i2004 :
+- Power on the phone
+- Wait for message "Nortel Networks"
+- Press quickly the four buttons just below the LCD screen, in sequence from left to right
+- If you see "Locating server", power off or reboot the phone and try again
+- DHCP : 0
+- SET IP : a free ip of your network
+- NETMSK / DEF GW : netmask and default gateway
+- S1 IP : ip of the asterisk server
+- S1 PORT : 5000
+- S1 ACTION : 1
+- S1 RETRY COUNT : 10
+- S2 : same as S1
+
+How to place a call :
+- The line=> entry in unistim.conf does not add an extension in asterisk by default.
+ If you want to do that, add extension=line in your phone context.
+- if you have this entry on unistim.conf :
+ [violet]
+ device=006038abcdef
+ line => 102
+
+ then use exten => 2100,1,Dial(USTM/102@violet)
+
+- You can display a text with :
+ exten => 555,1,SendText(Sends text to client. Greetings)
+
+Rebooting a Nortel phone:
+- Press mute,up,down,up,down,up,mute,9,release(red button)
+
+Distinctive ring :
+- You need to append /r to the dial string.
+- The first digit must be from 0 to 7 (inclusive). It's the 'melody' selection.
+- The second digit (optional) must be from 0 to 3 (inclusive). It's the ring volume. 0 still produce a sound.
+ Select the ring style #1 and the default volume :
+ exten => 2100,1,Dial(USTM/102@violet/r1)
+ Select the ring style #4 with a very loud volume :
+ exten => 2100,1,Dial(USTM/102@violet/r43)
+
+Country code :
+- You can use the following codes for country= (used for dial tone)
+ us fr au nl uk fi es jp no at nz tw cl se be sg il br hu lt pl za pt ee mx in de ch dk cn
+- If you want a correct ring, busy and congestion tone, you also need a valid entry in
+ indications.conf and check if res_indications.so is loaded.
+ language= is also supported but it's only used by Asterisk (for more informations
+ see http://www.voip-info.org/wiki/view/Asterisk+multi-language ). The end user interface of the phone
+ will stay in english.
+
+Bookmarks, Softkeys
+ - Layout :
+ |--------------------|
+ | 5 2 |
+ | 4 1 |
+ | 3 0 |
+ - When the second letter of bookmark= is @, then the first character is used for positioning this entry
+ - If this option is omitted, the bookmark will be added to the next available sofkey
+ - Also work for linelabel (example : linelabel="5@Line 123")
+ - You can change a softkey programmatically with SendText(@position@icon@label@extension) ex: SendText(@1@55@Stop Forwd@908)
+
+Autoprovisioning :
+- This feature must only be used on a trusted network. It's very insecure : all unistim phones
+ will be able to use your asterisk pbx.
+- You must add an entry called [template]. Each new phones will be based on this profile.
+- You must set a least line=>. This value will be incremented when a new phone is registred.
+ device= must not be specified. By default, the phone will asks for a number. It will be added into
+ the dialplan. Add extension=line for using the generated line number instead.
+ Example :
+ [general]
+ port=5000
+ autoprovisioning=yes
+
+ [template]
+ line => 100
+ bookmark=Support@123 ; Every phone will have a softkey Support
+
+- If a first phone have a mac = 006038abcdef, a new device named USTM/100@006038abcdef will be created.
+- If a second phone have a mac = 006038000000, it will be named USTM/101@006038000000 and so on.
+
+- When autoprovisioning=tn, new phones will ask for a tn, if this number match a tn= entry in a device,
+ this phone will be mapped into.
+ Example:
+ [black]
+ tn=1234
+ line => 100
+
+- If a user enter TN 1234, the phone will be known as USTM/100@black.
+
+History :
+- Use the two keys located in the middle of the Fixed feature keys row (on the bottom of the phone)
+ to enter call history.
+- By default, chan_unistim add any incoming and outgoing calls in files (/var/log/asterisk/unistimHistory)
+ It can be a privacy issue, you can disable this feature by adding callhistory=0. If history files were created,
+ you also need to delete them. callhistory=0 will NOT disable normal asterisk CDR logs.
+
+Forward :
+- This feature requires chan_local (loaded by default)
+
+Generic asterisk features :
+ You can use the following entries in unistim.conf
+ - Billing : accountcode amaflags
+ - Call Group : callgroup pickupgroup (untested)
+ - Music On Hold : musiconhold
+ - Language : language (see section Coutry Code)
+ - RTP NAT : nat (control ast_rtp_setnat, default = 0. Obscure behaviour)
+
+Trunking :
+- It's not possible to connect a Nortel Succession/Meridian/BCM to Asterisk via chan_unistim. Use either E1/T1 trunks, or buy UTPS (UNISTIM Terminal Proxy Server) from Nortel.
+
+Wiki, Additional infos, Comments :
+- http://www.voip-info.org/wiki-Asterisk+UNISTIM+channels
+
+*BSD :
+- Comment #define HAVE_IP_PKTINFO in chan_unistim.c
+- Set public_ip with an IP of your computer
+- Check if unistim.conf is in the correct directory
+
+Issues :
+- As always, NAT can be tricky. If a phone is behind a NAT, you should port forward UDP 5000 (or change [general] port= in unistim.conf) and UDP 10000 (or change [yourphone] rtp_port=)
+- Only one phone per public IP (multiple phones behind the same NAT don't work). You can either :
+ - Setup a VPN
+ - Install asterisk inside your NAT. You can use IAX2 trunking if you're master asterisk is outside.
+- If asterisk is behind a NAT, you must set [general] public_ip= with your public IP. If you don't do that or the bindaddr is invalid (or no longer valid, eg dynamic IP), phones should be able to display messages but will be unable to send/receive RTP packets (no sound)
+- Don't forget : this work is based entirely on a reverse engineering, so you may encounter compatibility issues. At this time, I know three ways to establish a RTP session. You can modify [yourphone] rtp_method= with 0, 1, 2 or 3. 0 is the default method, should work. 1 can be used on new firmware (black i2004) and 2 on old violet i2004. 3 can be used on black i2004 with chrome.
+- If you have difficulties, try unistim debug and set verbose 3 on the asterisk CLI. For extra debug, uncomment #define DUMP_PACKET 1 and recompile chan_unistim.
diff --git a/trunk/doc/valgrind.txt b/trunk/doc/valgrind.txt
new file mode 100644
index 000000000..1ac4b2bd7
--- /dev/null
+++ b/trunk/doc/valgrind.txt
@@ -0,0 +1,19 @@
+If you're having certain types of crashes, such as those associated with
+memory corruption, a bug marshal may ask you to run Asterisk under valgrind.
+You should follow these steps, to give the bug marshal the maximum amount
+of information about the crash.
+
+1. Run 'make menuselect' and in the Compiler Options, enable MALLOC_DEBUG
+ and DONT_OPTIMIZE. A bug marshal may also ask you to enable additional
+ compiler flags, such as DEBUG_THREADS, depending upon the nature of the
+ issue.
+
+2. Rebuild and install Asterisk.
+
+3. Run Asterisk as follows:
+ valgrind --log-file-exactly=valgrind.txt asterisk -vvvvcg 2>malloc_debug.txt
+
+4. Reproduce the issue. Following the manifestation of the issue (or when
+ the process crashes), upload the two files, valgrind.txt and
+ malloc_debug.txt to the issue tracker.
+
diff --git a/trunk/doc/video.txt b/trunk/doc/video.txt
new file mode 100644
index 000000000..d7bd282f9
--- /dev/null
+++ b/trunk/doc/video.txt
@@ -0,0 +1,46 @@
+Asterisk and Video telephony
+----------------------------
+
+Asterisk supports video telephony in the core infrastructure. Internally, it's one audio stream
+and one video stream in the same call. Some channel drivers and applications has video support,
+but not all.
+
+Codecs and formats
+------------------
+Asterisk supports the following video codecs and file formats. There's no video
+transcoding so you have to make sure that both ends support the same video format.
+
+ Codec Format
+ ----- ----------
+ H.263 read/write
+ H.264 read/write
+ H.261 - Passthrough only
+
+Note that the file produced by Asterisk video format drivers is in no generic
+video format. Gstreamer has support for producing these files and converting from
+various video files to Asterisk video+audio files.
+
+Note that H.264 is not enabled by default. You need to add that in the channel
+configuration file.
+
+Channel drivers
+---------------
+SIP The SIP channel driver (chan_sip.so) has support for video
+IAX2 Supports video calls (over trunks too)
+Local Forwards video calls as a proxy channel
+Agent Forwards video calls as a proxy channel
+
+Applications
+------------
+This is not yet a complete list. These dialplan applications are known to handle video:
+
+voicemail Video voicemail storage (does not attach video to e-mail)
+record Records audio and video files (give audio format as argument)
+playback Plays a video while being instructed to play audio
+echo Echos audio and video back to the user
+
+There is a development group working on enhancing video support for Asterisk.
+If you want to participate, join the asterisk-video mailing list on http://lists.digium.com
+
+---
+Updates to this file are more than welcome!
diff --git a/trunk/doc/voicemail_odbc_postgresql.txt b/trunk/doc/voicemail_odbc_postgresql.txt
new file mode 100644
index 000000000..722e60774
--- /dev/null
+++ b/trunk/doc/voicemail_odbc_postgresql.txt
@@ -0,0 +1,427 @@
+GETTING ODBC STORAGE WITH POSTGRESQL WORKING WITH VOICEMAIL
+
+
+1) Install PostgreSQL, PostgreSQL-devel, unixODBC, and unixODBC-devel, and
+PostgreSQL-ODBC. Make sure PostgreSQL is listening on a TCP socket, and that
+you are using md5 authentication for the database user. The line in my
+pg_hba.conf looks like:
+
+# "local" is for Unix domain socket connections only
+local jsmith2 jsmith2 md5
+local all all ident sameuser
+# IPv4 local connections:
+host all all 127.0.0.1/32 md5
+
+
+2) Make sure you have the PostgreSQL odbc driver setup in /etc/odbcinst.ini.
+Mine looks like:
+
+[PostgreSQL]
+Description = ODBC for PostgreSQL
+Driver = /usr/lib/libodbcpsql.so
+Setup = /usr/lib/libodbcpsqlS.so
+FileUsage = 1
+
+You can confirm that unixODBC is seeing the driver by typing:
+
+[jsmith2@localhost tmp]$ odbcinst -q -d
+[PostgreSQL]
+
+
+3) Setup a DSN in /etc/odbc.ini, pointing at the PostgreSQL database and
+driver. Mine looks like:
+
+[testing]
+Description = ODBC Testing
+Driver = PostgreSQL
+Trace = No
+TraceFile = sql.log
+Database = jsmith2
+Servername = 127.0.0.1
+UserName = jsmith2
+Password = supersecret
+Port = 5432
+ReadOnly = No
+RowVersioning = No
+ShowSystemTables = No
+ShowOidColumn = No
+FakeOidIndex = No
+ConnSettings =
+
+You can confirm that unixODBC sees your DSN by typing:
+
+[jsmith2@localhost tmp]$ odbcinst -q -s
+[testing]
+
+
+4) Test your database connectivity through ODBC. If this doesn't work,
+something is wrong with your ODBC setup.
+
+[jsmith2@localhost tmp]$ echo "select 1" | isql -v testing
++---------------------------------------+
+| Connected! |
+| |
+| sql-statement |
+| help [tablename] |
+| quit |
+| |
++---------------------------------------+
+SQL> +------------+
+| ?column? |
++------------+
+| 1 |
++------------+
+SQLRowCount returns 1
+1 rows fetched
+
+If your ODBC connectivity to PostgreSQL isn't working, you'll see an error
+message instead, like this:
+
+[jsmith2@localhost tmp]$ echo "select 1" | isql -v testing
+[S1000][unixODBC]Could not connect to the server;
+Could not connect to remote socket.
+[ISQL]ERROR: Could not SQLConnect
+bash: echo: write error: Broken pipe
+
+5) Compile Asterisk with support for ODBC voicemail. Go to your Asterisk
+source directory and run `make menuselect`. Under "Voicemail Build Options",
+enable "ODBC_STORAGE".
+# See doc/README.odbcstorage for more information
+
+Recompile Asterisk and install the new version.
+
+
+6) Once you've recompiled and re-installed Asterisk, check to make sure
+res_odbc.so has been compiled.
+
+localhost*CLI> show modules like res_odbc.so
+Module Description Use Count
+res_odbc.so ODBC Resource 0
+1 modules loaded
+
+
+7) Now it's time to get Asterisk configured. First, we need to tell Asterisk
+about our ODBC setup. Open /etc/asterisk/res_odbc.conf and add the following:
+
+[postgres]
+enabled => yes
+dsn => testing
+pre-connect => yes
+
+8) At the Asterisk CLI, unload and then load the res_odbc.so module. (You
+could restart Asterisk as well, but this way makes it easier to tell what's
+happening.) Notice how it says it's connected to "postgres", which is our ODBC
+connection as defined in res_odbc.conf, which points to the "testing" DSN in
+ODBC.
+
+localhost*CLI> unload res_odbc.so
+Jan 2 21:19:36 WARNING[8130]: res_odbc.c:498 odbc_obj_disconnect: res_odbc: disconnected 0 from postgres [testing]
+Jan 2 21:19:36 NOTICE[8130]: res_odbc.c:589 unload_module: res_odbc unloaded.
+localhost*CLI> load res_odbc.so
+ Loaded /usr/lib/asterisk/modules/res_odbc.so => (ODBC Resource)
+ == Parsing '/etc/asterisk/res_odbc.conf': Found
+Jan 2 21:19:40 NOTICE[8130]: res_odbc.c:266 load_odbc_config: Adding ENV var: INFORMIXSERVER=my_special_database
+Jan 2 21:19:40 NOTICE[8130]: res_odbc.c:266 load_odbc_config: Adding ENV var: INFORMIXDIR=/opt/informix
+Jan 2 21:19:40 NOTICE[8130]: res_odbc.c:295 load_odbc_config: registered database handle 'postgres' dsn->[testing]
+Jan 2 21:19:40 NOTICE[8130]: res_odbc.c:555 odbc_obj_connect: Connecting postgres
+Jan 2 21:19:40 NOTICE[8130]: res_odbc.c:570 odbc_obj_connect: res_odbc: Connected to postgres [testing]
+Jan 2 21:19:40 NOTICE[8130]: res_odbc.c:600 load_module: res_odbc loaded.
+
+You can also check the status of your ODBC connection at any time from the
+Asterisk CLI:
+
+localhost*CLI> odbc show
+Name: postgres
+DSN: testing
+Connected: yes
+
+9) Now we can setup our voicemail table in PostgreSQL. Log into PostgreSQL and
+type (or copy and paste) the following:
+
+--
+-- First, let's create our large object type, called "lo"
+--
+CREATE FUNCTION loin (cstring) RETURNS lo AS 'oidin' LANGUAGE internal IMMUTABLE STRICT;
+CREATE FUNCTION loout (lo) RETURNS cstring AS 'oidout' LANGUAGE internal IMMUTABLE STRICT;
+CREATE FUNCTION lorecv (internal) RETURNS lo AS 'oidrecv' LANGUAGE internal IMMUTABLE STRICT;
+CREATE FUNCTION losend (lo) RETURNS bytea AS 'oidrecv' LANGUAGE internal IMMUTABLE STRICT;
+
+CREATE TYPE lo ( INPUT = loin, OUTPUT = loout, RECEIVE = lorecv, SEND = losend, INTERNALLENGTH = 4, PASSEDBYVALUE );
+CREATE CAST (lo AS oid) WITHOUT FUNCTION AS IMPLICIT;
+CREATE CAST (oid AS lo) WITHOUT FUNCTION AS IMPLICIT;
+
+--
+-- If we're not already using plpgsql, then let's use it!
+--
+CREATE TRUSTED LANGUAGE plpgsql;
+
+--
+-- Next, let's create a trigger to cleanup the large object table
+-- whenever we update or delete a row from the voicemessages table
+--
+
+CREATE FUNCTION vm_lo_cleanup() RETURNS "trigger"
+ AS $$
+ declare
+ msgcount INTEGER;
+ begin
+ -- raise notice 'Starting lo_cleanup function for large object with oid %',old.recording;
+ -- If it is an update action but the BLOB (lo) field was not changed, dont do anything
+ if (TG_OP = 'UPDATE') then
+ if ((old.recording = new.recording) or (old.recording is NULL)) then
+ raise notice 'Not cleaning up the large object table, as recording has not changed';
+ return new;
+ end if;
+ end if;
+ if (old.recording IS NOT NULL) then
+ SELECT INTO msgcount COUNT(*) AS COUNT FROM voicemessages WHERE recording = old.recording;
+ if (msgcount > 0) then
+ raise notice 'Not deleting record from the large object table, as object is still referenced';
+ return new;
+ else
+ perform lo_unlink(old.recording);
+ if found then
+ raise notice 'Cleaning up the large object table';
+ return new;
+ else
+ raise exception 'Failed to cleanup the large object table';
+ return old;
+ end if;
+ end if;
+ else
+ raise notice 'No need to cleanup the large object table, no recording on old row';
+ return new;
+ end if;
+ end$$
+ LANGUAGE plpgsql;
+
+--
+-- Now, let's create our voicemessages table
+-- This is what holds the voicemail from Asterisk
+--
+
+CREATE TABLE voicemessages
+(
+ uniqueid serial PRIMARY KEY,
+ msgnum int4,
+ dir varchar(80),
+ context varchar(80),
+ macrocontext varchar(80),
+ callerid varchar(40),
+ origtime varchar(40),
+ duration varchar(20),
+ mailboxuser varchar(80),
+ mailboxcontext varchar(80),
+ recording lo,
+ label varchar(30),
+ "read" bool DEFAULT false
+);
+
+--
+-- Let's not forget to make the voicemessages table use the trigger
+--
+
+CREATE TRIGGER vm_cleanup AFTER DELETE OR UPDATE ON voicemessages FOR EACH ROW EXECUTE PROCEDURE vm_lo_cleanup();
+
+
+10) Just as a sanity check, make sure you check the voicemessages table via the
+isql utility.
+
+[jsmith2@localhost ODBC]$ echo "SELECT id, msgnum, dir, duration FROM voicemessages WHERE msgnum = 1" | isql testing
++---------------------------------------+
+| Connected! |
+| |
+| sql-statement |
+| help [tablename] |
+| quit |
+| |
++---------------------------------------+
+SQL> +------------+------------+---------------------------------------------------------------------------------+---------------------+
+| id | msgnum | dir | duration |
++------------+------------+---------------------------------------------------------------------------------+---------------------+
++------------+------------+---------------------------------------------------------------------------------+---------------------+
+SQLRowCount returns 0
+
+
+11) Now we can finally configure voicemail in Asterisk to use our database.
+Open /etc/asterisk/voicemail.conf, and look in the [general] section. I've
+changed the format to gsm (as I can't seem to get WAV or wav working), and
+specify both the odbc connection and database table to use.
+
+[general]
+; Default formats for writing Voicemail
+;format=g723sf|wav49|wav
+format=gsm
+odbcstorage=postgres
+odbctable=voicemessages
+
+You'll also want to create a new voicemail context called "odbctest" to do some
+testing, and create a sample mailbox inside that context. Add the following to
+the very bottom of voicemail.conf:
+
+[odbctest]
+101 => 5555,Example Mailbox
+
+
+12) Once you've updated voicemail.conf, let's make the changes take effect:
+
+localhost*CLI> unload app_voicemail.so
+ == Unregistered application 'VoiceMail'
+ == Unregistered application 'VoiceMailMain'
+ == Unregistered application 'MailboxExists'
+ == Unregistered application 'VMAuthenticate'
+localhost*CLI> load app_voicemail.so
+ Loaded /usr/lib/asterisk/modules/app_voicemail.so => (Comedian Mail (Voicemail System))
+ == Registered application 'VoiceMail'
+ == Registered application 'VoiceMailMain'
+ == Registered application 'MailboxExists'
+ == Registered application 'VMAuthenticate'
+ == Parsing '/etc/asterisk/voicemail.conf': Found
+
+You can check to make sure your new mailbox exists by typing:
+
+localhost*CLI> show voicemail users for odbctest
+Context Mbox User Zone NewMsg
+odbctest 101 Example Mailbox 0
+
+
+13) Now, let's add a new context called "odbc" to extensions.conf. We'll use
+these extensions to do some testing:
+
+[odbc]
+exten => 100,1,Voicemail(101@odbctest)
+exten => 200,1,VoicemailMain(101@odbctest)
+
+
+14) Next, we need to point a phone at the odbc context. In my case, I've got a
+SIP phone called "linksys" that is registering to Asterisk, so I'm setting its
+context to the [odbc] context we created in the previous step. The relevant
+section of my sip.conf file looks like:
+
+[linksys]
+type=friend
+secret=verysecret
+disallow=all
+allow=ulaw
+allow=gsm
+context=odbc
+host=dynamic
+qualify=yes
+
+I can check to see that my linksys phone is registered with Asterisk correctly:
+
+localhost*CLI> sip show peers like linksys
+Name/username Host Dyn Nat ACL Port Status
+linksys/linksys 192.168.0.103 D 5060 OK (9 ms)
+1 sip peers [1 online , 0 offline]
+
+
+15) At last, we're finally ready to leave a voicemail message and have it
+stored in our database! (Who'd have guessed it would be this much trouble?!?)
+Pick up the phone, dial extension 100, and leave yourself a voicemail message.
+In my case, this is what appeared on the Asterisk CLI:
+
+localhost*CLI>
+ -- Executing VoiceMail("SIP/linksys-10228cac", "101@odbctest") in new stack
+ -- Playing 'vm-intro' (language 'en')
+ -- Playing 'beep' (language 'en')
+ -- Recording the message
+ -- x=0, open writing: /var/spool/asterisk/voicemail/odbctest/101/tmp/dlZunm format: gsm, 0x101f6534
+ -- User ended message by pressing #
+ -- Playing 'auth-thankyou' (language 'en')
+ == Parsing '/var/spool/asterisk/voicemail/odbctest/101/INBOX/msg0000.txt': Found
+
+Now, we can check the database and make sure the record actually made it into
+PostgreSQL, from within the psql utility.
+
+[jsmith2@localhost ~]$ psql
+Password:
+Welcome to psql 8.1.4, the PostgreSQL interactive terminal.
+
+Type: \copyright for distribution terms
+ \h for help with SQL commands
+ \? for help with psql commands
+ \g or terminate with semicolon to execute query
+ \q to quit
+
+jsmith2=# SELECT * FROM voicemessages;
+ id | msgnum | dir | context | macrocontext | callerid | origtime | duration | mailboxuser | mailboxcontext | recording | label | read | sip_id | pabx_id | iax_id
+----+--------+--------------------------------------------------+---------+--------------+-----------------------+------------+----------+-------------+----------------+-----------+-------+------+--------+---------+--------
+ 26 | 0 | /var/spool/asterisk/voicemail/odbctest/101/INBOX | odbc | | "linksys" <linksys> | 1167794179 | 7 | 101 | odbctest | 16599 | | f | | |
+(1 row)
+
+Did you notice the the recording column is just a number? When a recording
+gets stuck in the database, the audio isn't actually stored in the
+voicemessages table. It's stored in a system table called the large object
+table. We can look in the large object table and verify that the object
+actually exists there:
+
+jsmith2=# \lo_list
+ Large objects
+ ID | Description
+-------+-------------
+ 16599 |
+(1 row)
+
+In my case, the OID is 16599. Your OID will almost surely be different. Just
+make sure the OID number in the recording column in the voicemessages table
+corresponds with a record in the large object table. (The trigger we added to
+our voicemessages table was designed to make sure this is always the case.)
+
+We can also pull a copy of the voicemail message back out of the database and
+write it to a file, to help us as we debug things:
+
+jsmith2=# \lo_export 16599 /tmp/odcb-16599.gsm
+lo_export
+
+We can even listen to the file from the Linux command line:
+
+[jsmith2@localhost tmp]$ play /tmp/odcb-16599.gsm
+
+Input Filename : /tmp/odcb-16599.gsm
+Sample Size : 8-bits
+Sample Encoding: gsm
+Channels : 1
+Sample Rate : 8000
+
+Time: 00:06.22 [00:00.00] of 00:00.00 ( 0.0%) Output Buffer: 298.36K
+
+Done.
+
+
+16) Last but not least, we can pull the voicemail message back out of the
+database by dialing extension 200 and entering "5555" at the password prompt.
+You should see something like this on the Asterisk CLI:
+
+localhost*CLI>
+ -- Executing VoiceMailMain("SIP/linksys-10228cac", "101@odbctest") in new stack
+ -- Playing 'vm-password' (language 'en')
+ -- Playing 'vm-youhave' (language 'en')
+ -- Playing 'digits/1' (language 'en')
+ -- Playing 'vm-INBOX' (language 'en')
+ -- Playing 'vm-message' (language 'en')
+ -- Playing 'vm-onefor' (language 'en')
+ -- Playing 'vm-INBOX' (language 'en')
+ -- Playing 'vm-messages' (language 'en')
+ -- Playing 'vm-opts' (language 'en')
+ -- Playing 'vm-first' (language 'en')
+ -- Playing 'vm-message' (language 'en')
+ == Parsing '/var/spool/asterisk/voicemail/odbctest/101/INBOX/msg0000.txt': Found
+ -- Playing 'vm-received' (language 'en')
+ -- Playing 'digits/at' (language 'en')
+ -- Playing 'digits/10' (language 'en')
+ -- Playing 'digits/16' (language 'en')
+ -- Playing 'digits/p-m' (language 'en')
+ -- Playing '/var/spool/asterisk/voicemail/odbctest/101/INBOX/msg0000' (language 'en')
+ -- Playing 'vm-advopts' (language 'en')
+ -- Playing 'vm-repeat' (language 'en')
+ -- Playing 'vm-delete' (language 'en')
+ -- Playing 'vm-toforward' (language 'en')
+ -- Playing 'vm-savemessage' (language 'en')
+ -- Playing 'vm-helpexit' (language 'en')
+ -- Playing 'vm-goodbye' (language 'en')
+
+That's it!
+
+Jared Smith
+2 Jan 2006
diff --git a/trunk/formats/Makefile b/trunk/formats/Makefile
new file mode 100644
index 000000000..483470103
--- /dev/null
+++ b/trunk/formats/Makefile
@@ -0,0 +1,20 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for file format modules
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=format
+MENUSELECT_CATEGORY=FORMATS
+MENUSELECT_DESCRIPTION=Format Interpreters
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/trunk/formats/format_g723.c b/trunk/formats/format_g723.c
new file mode 100644
index 000000000..6e57b4fa8
--- /dev/null
+++ b/trunk/formats/format_g723.c
@@ -0,0 +1,152 @@
+/*
+ * 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 Old-style G.723.1 frame/timestamp format.
+ *
+ * \arg Extensions: g723, g723sf
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+
+#define G723_MAX_SIZE 1024
+
+static struct ast_frame *g723_read(struct ast_filestream *s, int *whennext)
+{
+ unsigned short size;
+ int res;
+ int delay;
+ /* Read the delay for the next packet, and schedule again if necessary */
+ /* XXX is this ignored ? */
+ if (fread(&delay, 1, 4, s->f) == 4)
+ delay = ntohl(delay);
+ else
+ delay = -1;
+ if (fread(&size, 1, 2, s->f) != 2) {
+ /* Out of data, or the file is no longer valid. In any case
+ go ahead and stop the stream */
+ return NULL;
+ }
+ /* Looks like we have a frame to read from here */
+ size = ntohs(size);
+ if (size > G723_MAX_SIZE) {
+ ast_log(LOG_WARNING, "Size %d is invalid\n", size);
+ /* The file is apparently no longer any good, as we
+ shouldn't ever get frames even close to this
+ size. */
+ return NULL;
+ }
+ /* Read the data into the buffer */
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_G723_1;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, size);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != size) {
+ ast_log(LOG_WARNING, "Short read (%d of %d bytes) (%s)!\n", res, size, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples = 240;
+ return &s->fr;
+}
+
+static int g723_write(struct ast_filestream *s, struct ast_frame *f)
+{
+ uint32_t delay;
+ uint16_t size;
+ int res;
+ /* XXX there used to be a check s->fr means a read stream */
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_G723_1) {
+ ast_log(LOG_WARNING, "Asked to write non-g723 frame!\n");
+ return -1;
+ }
+ delay = 0;
+ if (f->datalen <= 0) {
+ ast_log(LOG_WARNING, "Short frame ignored (%d bytes long?)\n", f->datalen);
+ return 0;
+ }
+ if ((res = fwrite(&delay, 1, 4, s->f)) != 4) {
+ ast_log(LOG_WARNING, "Unable to write delay: res=%d (%s)\n", res, strerror(errno));
+ return -1;
+ }
+ size = htons(f->datalen);
+ if ((res = fwrite(&size, 1, 2, s->f)) != 2) {
+ ast_log(LOG_WARNING, "Unable to write size: res=%d (%s)\n", res, strerror(errno));
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Unable to write frame: res=%d (%s)\n", res, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int g723_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ return -1;
+}
+
+static int g723_trunc(struct ast_filestream *fs)
+{
+ /* Truncate file to current length */
+ if (ftruncate(fileno(fs->f), ftello(fs->f)) < 0)
+ return -1;
+ return 0;
+}
+
+static off_t g723_tell(struct ast_filestream *fs)
+{
+ return -1;
+}
+
+static const struct ast_format g723_1_f = {
+ .name = "g723sf",
+ .exts = "g723|g723sf",
+ .format = AST_FORMAT_G723_1,
+ .write = g723_write,
+ .seek = g723_seek,
+ .trunc = g723_trunc,
+ .tell = g723_tell,
+ .read = g723_read,
+ .buf_size = G723_MAX_SIZE + AST_FRIENDLY_OFFSET,
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&g723_1_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(g723_1_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "G.723.1 Simple Timestamp File Format");
diff --git a/trunk/formats/format_g726.c b/trunk/formats/format_g726.c
new file mode 100644
index 000000000..e27476fed
--- /dev/null
+++ b/trunk/formats/format_g726.c
@@ -0,0 +1,261 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2004 - 2005, inAccess Networks
+ *
+ * Michael Manousos <manousos@inaccessnetworks.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 Headerless G.726 (16/24/32/40kbps) data format for Asterisk.
+ *
+ * File name extensions:
+ * \arg 40 kbps: g726-40
+ * \arg 32 kbps: g726-32
+ * \arg 24 kbps: g726-24
+ * \arg 16 kbps: g726-16
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+#define RATE_40 0
+#define RATE_32 1
+#define RATE_24 2
+#define RATE_16 3
+
+/* We can only read/write chunks of FRAME_TIME ms G.726 data */
+#define FRAME_TIME 10 /* 10 ms size */
+
+#define BUF_SIZE (5*FRAME_TIME) /* max frame size in bytes ? */
+/* Frame sizes in bytes */
+static int frame_size[4] = {
+ FRAME_TIME * 5,
+ FRAME_TIME * 4,
+ FRAME_TIME * 3,
+ FRAME_TIME * 2
+};
+
+struct g726_desc {
+ int rate; /* RATE_* defines */
+};
+
+/*
+ * Rate dependant format functions (open, rewrite)
+ */
+static int g726_open(struct ast_filestream *tmp, int rate)
+{
+ struct g726_desc *s = (struct g726_desc *)tmp->_private;
+ s->rate = rate;
+ ast_debug(1, "Created filestream G.726-%dk.\n", 40 - s->rate * 8);
+ return 0;
+}
+
+static int g726_40_open(struct ast_filestream *s)
+{
+ return g726_open(s, RATE_40);
+}
+
+static int g726_32_open(struct ast_filestream *s)
+{
+ return g726_open(s, RATE_32);
+}
+
+static int g726_24_open(struct ast_filestream *s)
+{
+ return g726_open(s, RATE_24);
+}
+
+static int g726_16_open(struct ast_filestream *s)
+{
+ return g726_open(s, RATE_16);
+}
+
+static int g726_40_rewrite(struct ast_filestream *s, const char *comment)
+{
+ return g726_open(s, RATE_40);
+}
+
+static int g726_32_rewrite(struct ast_filestream *s, const char *comment)
+{
+ return g726_open(s, RATE_32);
+}
+
+static int g726_24_rewrite(struct ast_filestream *s, const char *comment)
+{
+ return g726_open(s, RATE_24);
+}
+
+static int g726_16_rewrite(struct ast_filestream *s, const char *comment)
+{
+ return g726_open(s, RATE_16);
+}
+
+/*
+ * Rate independent format functions (read, write)
+ */
+
+static struct ast_frame *g726_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ struct g726_desc *fs = (struct g726_desc *)s->_private;
+
+ /* Send a frame from the file to the appropriate channel */
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_G726;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, frame_size[fs->rate]);
+ s->fr.samples = 8 * FRAME_TIME;
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples;
+ return &s->fr;
+}
+
+static int g726_write(struct ast_filestream *s, struct ast_frame *f)
+{
+ int res;
+ struct g726_desc *fs = (struct g726_desc *)s->_private;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_G726) {
+ ast_log(LOG_WARNING, "Asked to write non-G726 frame (%d)!\n",
+ f->subclass);
+ return -1;
+ }
+ if (f->datalen % frame_size[fs->rate]) {
+ ast_log(LOG_WARNING, "Invalid data length %d, should be multiple of %d\n",
+ f->datalen, frame_size[fs->rate]);
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n",
+ res, frame_size[fs->rate], strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int g726_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ return -1;
+}
+
+static int g726_trunc(struct ast_filestream *fs)
+{
+ return -1;
+}
+
+static off_t g726_tell(struct ast_filestream *fs)
+{
+ return -1;
+}
+
+static const struct ast_format f[] = {
+ {
+ .name = "g726-40",
+ .exts = "g726-40",
+ .format = AST_FORMAT_G726,
+ .open = g726_40_open,
+ .rewrite = g726_40_rewrite,
+ .write = g726_write,
+ .seek = g726_seek,
+ .trunc = g726_trunc,
+ .tell = g726_tell,
+ .read = g726_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct g726_desc),
+ },
+ {
+ .name = "g726-32",
+ .exts = "g726-32",
+ .format = AST_FORMAT_G726,
+ .open = g726_32_open,
+ .rewrite = g726_32_rewrite,
+ .write = g726_write,
+ .seek = g726_seek,
+ .trunc = g726_trunc,
+ .tell = g726_tell,
+ .read = g726_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct g726_desc),
+ },
+ {
+ .name = "g726-24",
+ .exts = "g726-24",
+ .format = AST_FORMAT_G726,
+ .open = g726_24_open,
+ .rewrite = g726_24_rewrite,
+ .write = g726_write,
+ .seek = g726_seek,
+ .trunc = g726_trunc,
+ .tell = g726_tell,
+ .read = g726_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct g726_desc),
+ },
+ {
+ .name = "g726-16",
+ .exts = "g726-16",
+ .format = AST_FORMAT_G726,
+ .open = g726_16_open,
+ .rewrite = g726_16_rewrite,
+ .write = g726_write,
+ .seek = g726_seek,
+ .trunc = g726_trunc,
+ .tell = g726_tell,
+ .read = g726_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct g726_desc),
+ },
+ { .format = 0 } /* terminator */
+};
+
+static int load_module(void)
+{
+ int i;
+
+ for (i = 0; f[i].format ; i++) {
+ if (ast_format_register(&f[i])) { /* errors are fatal */
+ ast_log(LOG_WARNING, "Failed to register format %s.\n", f[i].name);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int i;
+
+ for (i = 0; f[i].format ; i++) {
+ if (ast_format_unregister(f[i].name))
+ ast_log(LOG_WARNING, "Failed to unregister format %s.\n", f[i].name);
+ }
+ return(0);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw G.726 (16/24/32/40kbps) data");
diff --git a/trunk/formats/format_g729.c b/trunk/formats/format_g729.c
new file mode 100644
index 000000000..8df463d81
--- /dev/null
+++ b/trunk/formats/format_g729.c
@@ -0,0 +1,148 @@
+/*
+ * 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 Save to raw, headerless G729 data.
+ * \note This is not an encoder/decoder. The codec fo g729 is only
+ * available with a commercial license from Digium, due to patent
+ * restrictions. Check http://www.digium.com for information.
+ * \arg Extensions: g729
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+/* Some Ideas for this code came from makeg729e.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+
+#define BUF_SIZE 20 /* two G729 frames */
+#define G729A_SAMPLES 160
+
+static struct ast_frame *g729_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ /* Send a frame from the file to the appropriate channel */
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_G729A;
+ s->fr.mallocd = 0;
+ s->fr.samples = G729A_SAMPLES;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
+ if (res && (res != 10)) /* XXX what for ? */
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples;
+ return &s->fr;
+}
+
+static int g729_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res;
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_G729A) {
+ ast_log(LOG_WARNING, "Asked to write non-G729 frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ if (f->datalen % 10) {
+ ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 10\n", f->datalen);
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/10): %s\n", res, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int g729_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ long bytes;
+ off_t min,cur,max,offset=0;
+ min = 0;
+ cur = ftello(fs->f);
+ fseeko(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+
+ bytes = BUF_SIZE * (sample_offset / G729A_SAMPLES);
+ if (whence == SEEK_SET)
+ offset = bytes;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = cur + bytes;
+ else if (whence == SEEK_END)
+ offset = max - bytes;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max)?max:offset;
+ }
+ /* protect against seeking beyond begining. */
+ offset = (offset < min)?min:offset;
+ if (fseeko(fs->f, offset, SEEK_SET) < 0)
+ return -1;
+ return 0;
+}
+
+static int g729_trunc(struct ast_filestream *fs)
+{
+ /* Truncate file to current length */
+ if (ftruncate(fileno(fs->f), ftello(fs->f)) < 0)
+ return -1;
+ return 0;
+}
+
+static off_t g729_tell(struct ast_filestream *fs)
+{
+ off_t offset = ftello(fs->f);
+ return (offset/BUF_SIZE)*G729A_SAMPLES;
+}
+
+static const struct ast_format g729_f = {
+ .name = "g729",
+ .exts = "g729",
+ .format = AST_FORMAT_G729A,
+ .write = g729_write,
+ .seek = g729_seek,
+ .trunc = g729_trunc,
+ .tell = g729_tell,
+ .read = g729_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&g729_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(g729_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw G729 data");
diff --git a/trunk/formats/format_gsm.c b/trunk/formats/format_gsm.c
new file mode 100644
index 000000000..d43844e64
--- /dev/null
+++ b/trunk/formats/format_gsm.c
@@ -0,0 +1,170 @@
+/*
+ * 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 Save to raw, headerless GSM data.
+ * \arg File name extension: gsm
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+#include "msgsm.h"
+
+/* Some Ideas for this code came from makegsme.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+
+#define GSM_FRAME_SIZE 33
+#define GSM_SAMPLES 160
+
+/* silent gsm frame */
+/* begin binary data: */
+char gsm_silence[] = /* 33 */
+{0xD8,0x20,0xA2,0xE1,0x5A,0x50,0x00,0x49,0x24,0x92,0x49,0x24,0x50,0x00,0x49
+,0x24,0x92,0x49,0x24,0x50,0x00,0x49,0x24,0x92,0x49,0x24,0x50,0x00,0x49,0x24
+,0x92,0x49,0x24};
+/* end binary data. size = 33 bytes */
+
+static struct ast_frame *gsm_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_GSM;
+ AST_FRAME_SET_BUFFER(&(s->fr), s->buf, AST_FRIENDLY_OFFSET, GSM_FRAME_SIZE)
+ s->fr.mallocd = 0;
+ if ((res = fread(s->fr.data, 1, GSM_FRAME_SIZE, s->f)) != GSM_FRAME_SIZE) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples = GSM_SAMPLES;
+ return &s->fr;
+}
+
+static int gsm_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res;
+ unsigned char gsm[2*GSM_FRAME_SIZE];
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_GSM) {
+ ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ if (!(f->datalen % 65)) {
+ /* This is in MSGSM format, need to be converted */
+ int len=0;
+ while(len < f->datalen) {
+ conv65(f->data + len, gsm);
+ if ((res = fwrite(gsm, 1, 2*GSM_FRAME_SIZE, fs->f)) != 2*GSM_FRAME_SIZE) {
+ ast_log(LOG_WARNING, "Bad write (%d/66): %s\n", res, strerror(errno));
+ return -1;
+ }
+ len += 65;
+ }
+ } else {
+ if (f->datalen % GSM_FRAME_SIZE) {
+ ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 33\n", f->datalen);
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/33): %s\n", res, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int gsm_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t offset=0,min,cur,max,distance;
+
+ min = 0;
+ cur = ftello(fs->f);
+ fseeko(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+ /* have to fudge to frame here, so not fully to sample */
+ distance = (sample_offset/GSM_SAMPLES) * GSM_FRAME_SIZE;
+ if(whence == SEEK_SET)
+ offset = distance;
+ else if(whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = distance + cur;
+ else if(whence == SEEK_END)
+ offset = max - distance;
+ /* Always protect against seeking past the begining. */
+ offset = (offset < min)?min:offset;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max)?max:offset;
+ } else if (offset > max) {
+ int i;
+ fseeko(fs->f, 0, SEEK_END);
+ for (i=0; i< (offset - max) / GSM_FRAME_SIZE; i++) {
+ fwrite(gsm_silence, 1, GSM_FRAME_SIZE, fs->f);
+ }
+ }
+ return fseeko(fs->f, offset, SEEK_SET);
+}
+
+static int gsm_trunc(struct ast_filestream *fs)
+{
+ return ftruncate(fileno(fs->f), ftello(fs->f));
+}
+
+static off_t gsm_tell(struct ast_filestream *fs)
+{
+ off_t offset = ftello(fs->f);
+ return (offset/GSM_FRAME_SIZE)*GSM_SAMPLES;
+}
+
+static const struct ast_format gsm_f = {
+ .name = "gsm",
+ .exts = "gsm",
+ .format = AST_FORMAT_GSM,
+ .write = gsm_write,
+ .seek = gsm_seek,
+ .trunc = gsm_trunc,
+ .tell = gsm_tell,
+ .read = gsm_read,
+ .buf_size = 2*GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET, /* 2 gsm frames */
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&gsm_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(gsm_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw GSM data");
diff --git a/trunk/formats/format_h263.c b/trunk/formats/format_h263.c
new file mode 100644
index 000000000..b0b5cb27d
--- /dev/null
+++ b/trunk/formats/format_h263.c
@@ -0,0 +1,186 @@
+/*
+ * 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 Save to raw, headerless h263 data.
+ * \arg File name extension: h263
+ * \ingroup formats
+ * \arg See \ref AstVideo
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+/* Some Ideas for this code came from makeh263e.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+
+/* According to:
+ * http://lists.mpegif.org/pipermail/mp4-tech/2005-July/005741.html
+ * the maximum actual frame size is not 2048, but 8192. Since the maximum
+ * theoretical limit is not much larger (32k = 15bits), we'll go for that
+ * size to ensure we don't corrupt frames sent to us (unless they're
+ * ridiculously large). */
+#define BUF_SIZE 32768 /* Four real h.263 Frames */
+
+struct h263_desc {
+ unsigned int lastts;
+};
+
+
+static int h263_open(struct ast_filestream *s)
+{
+ unsigned int ts;
+ int res;
+
+ if ((res = fread(&ts, 1, sizeof(ts), s->f)) < sizeof(ts)) {
+ ast_log(LOG_WARNING, "Empty file!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static struct ast_frame *h263_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ int mark;
+ unsigned short len;
+ unsigned int ts;
+ struct h263_desc *fs = (struct h263_desc *)s->_private;
+
+ /* Send a frame from the file to the appropriate channel */
+ if ((res = fread(&len, 1, sizeof(len), s->f)) < 1)
+ return NULL;
+ len = ntohs(len);
+ mark = (len & 0x8000) ? 1 : 0;
+ len &= 0x7fff;
+ if (len > BUF_SIZE) {
+ ast_log(LOG_WARNING, "Length %d is too long\n", len);
+ return NULL;
+ }
+ s->fr.frametype = AST_FRAME_VIDEO;
+ s->fr.subclass = AST_FORMAT_H263;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ s->fr.samples = fs->lastts; /* XXX what ? */
+ s->fr.datalen = len;
+ s->fr.subclass |= mark;
+ s->fr.delivery.tv_sec = 0;
+ s->fr.delivery.tv_usec = 0;
+ if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) {
+ fs->lastts = ntohl(ts);
+ *whennext = fs->lastts * 4/45;
+ } else
+ *whennext = 0;
+ return &s->fr;
+}
+
+static int h263_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res;
+ unsigned int ts;
+ unsigned short len;
+ int subclass;
+ int mark=0;
+ if (f->frametype != AST_FRAME_VIDEO) {
+ ast_log(LOG_WARNING, "Asked to write non-video frame!\n");
+ return -1;
+ }
+ subclass = f->subclass;
+ if (subclass & 0x1)
+ mark=0x8000;
+ subclass &= ~0x1;
+ if (subclass != AST_FORMAT_H263) {
+ ast_log(LOG_WARNING, "Asked to write non-h263 frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ ts = htonl(f->samples);
+ if ((res = fwrite(&ts, 1, sizeof(ts), fs->f)) != sizeof(ts)) {
+ ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno));
+ return -1;
+ }
+ len = htons(f->datalen | mark);
+ if ((res = fwrite(&len, 1, sizeof(len), fs->f)) != sizeof(len)) {
+ ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno));
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int h263_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ /* No way Jose */
+ return -1;
+}
+
+static int h263_trunc(struct ast_filestream *fs)
+{
+ /* Truncate file to current length */
+ if (ftruncate(fileno(fs->f), ftello(fs->f)) < 0)
+ return -1;
+ return 0;
+}
+
+static off_t h263_tell(struct ast_filestream *fs)
+{
+ off_t offset = ftello(fs->f);
+ return offset; /* XXX totally bogus, needs fixing */
+}
+
+static const struct ast_format h263_f = {
+ .name = "h263",
+ .exts = "h263",
+ .format = AST_FORMAT_H263,
+ .open = h263_open,
+ .write = h263_write,
+ .seek = h263_seek,
+ .trunc = h263_trunc,
+ .tell = h263_tell,
+ .read = h263_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct h263_desc),
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&h263_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(h263_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw H.263 data");
diff --git a/trunk/formats/format_h264.c b/trunk/formats/format_h264.c
new file mode 100644
index 000000000..06def313c
--- /dev/null
+++ b/trunk/formats/format_h264.c
@@ -0,0 +1,175 @@
+/*
+ * 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 Save to raw, headerless h264 data.
+ * \arg File name extension: h264
+ * \ingroup formats
+ * \arg See \ref AstVideo
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+/* Some Ideas for this code came from makeh264e.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+/*! \todo Check this buf size estimate, it may be totally wrong for large frame video */
+
+#define BUF_SIZE 4096 /* Two Real h264 Frames */
+struct h264_desc {
+ unsigned int lastts;
+};
+
+static int h264_open(struct ast_filestream *s)
+{
+ unsigned int ts;
+ int res;
+ if ((res = fread(&ts, 1, sizeof(ts), s->f)) < sizeof(ts)) {
+ ast_log(LOG_WARNING, "Empty file!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static struct ast_frame *h264_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ int mark=0;
+ unsigned short len;
+ unsigned int ts;
+ struct h264_desc *fs = (struct h264_desc *)s->_private;
+
+ /* Send a frame from the file to the appropriate channel */
+ if ((res = fread(&len, 1, sizeof(len), s->f)) < 1)
+ return NULL;
+ len = ntohs(len);
+ mark = (len & 0x8000) ? 1 : 0;
+ len &= 0x7fff;
+ if (len > BUF_SIZE) {
+ ast_log(LOG_WARNING, "Length %d is too long\n", len);
+ len = BUF_SIZE; /* XXX truncate */
+ }
+ s->fr.frametype = AST_FRAME_VIDEO;
+ s->fr.subclass = AST_FORMAT_H264;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d of %d) (%s)!\n", res, len, strerror(errno));
+ return NULL;
+ }
+ s->fr.samples = fs->lastts;
+ s->fr.datalen = len;
+ s->fr.subclass |= mark;
+ s->fr.delivery.tv_sec = 0;
+ s->fr.delivery.tv_usec = 0;
+ if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) {
+ fs->lastts = ntohl(ts);
+ *whennext = fs->lastts * 4/45;
+ } else
+ *whennext = 0;
+ return &s->fr;
+}
+
+static int h264_write(struct ast_filestream *s, struct ast_frame *f)
+{
+ int res;
+ unsigned int ts;
+ unsigned short len;
+ int mark;
+
+ if (f->frametype != AST_FRAME_VIDEO) {
+ ast_log(LOG_WARNING, "Asked to write non-video frame!\n");
+ return -1;
+ }
+ mark = (f->subclass & 0x1) ? 0x8000 : 0;
+ if ((f->subclass & ~0x1) != AST_FORMAT_H264) {
+ ast_log(LOG_WARNING, "Asked to write non-h264 frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ ts = htonl(f->samples);
+ if ((res = fwrite(&ts, 1, sizeof(ts), s->f)) != sizeof(ts)) {
+ ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno));
+ return -1;
+ }
+ len = htons(f->datalen | mark);
+ if ((res = fwrite(&len, 1, sizeof(len), s->f)) != sizeof(len)) {
+ ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno));
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int h264_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ /* No way Jose */
+ return -1;
+}
+
+static int h264_trunc(struct ast_filestream *fs)
+{
+ /* Truncate file to current length */
+ if (ftruncate(fileno(fs->f), ftell(fs->f)) < 0)
+ return -1;
+ return 0;
+}
+
+static off_t h264_tell(struct ast_filestream *fs)
+{
+ off_t offset = ftell(fs->f);
+ return offset; /* XXX totally bogus, needs fixing */
+}
+
+static const struct ast_format h264_f = {
+ .name = "h264",
+ .exts = "h264",
+ .format = AST_FORMAT_H264,
+ .open = h264_open,
+ .write = h264_write,
+ .seek = h264_seek,
+ .trunc = h264_trunc,
+ .tell = h264_tell,
+ .read = h264_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct h264_desc),
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&h264_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(h264_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw H.264 data");
diff --git a/trunk/formats/format_ilbc.c b/trunk/formats/format_ilbc.c
new file mode 100644
index 000000000..aaddc6c38
--- /dev/null
+++ b/trunk/formats/format_ilbc.c
@@ -0,0 +1,146 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Brian K. West <brian@bkw.org>
+ *
+ * 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 Save to raw, headerless iLBC data.
+ * \arg File name extension: ilbc
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+/* Some Ideas for this code came from makeg729e.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+
+#define ILBC_BUF_SIZE 50 /* One Real iLBC Frame */
+#define ILBC_SAMPLES 240
+
+static struct ast_frame *ilbc_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ /* Send a frame from the file to the appropriate channel */
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_ILBC;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, ILBC_BUF_SIZE);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples = ILBC_SAMPLES;
+ return &s->fr;
+}
+
+static int ilbc_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res;
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_ILBC) {
+ ast_log(LOG_WARNING, "Asked to write non-iLBC frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ if (f->datalen % 50) {
+ ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 50\n", f->datalen);
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/50): %s\n", res, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int ilbc_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ long bytes;
+ off_t min,cur,max,offset=0;
+ min = 0;
+ cur = ftello(fs->f);
+ fseeko(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+
+ bytes = ILBC_BUF_SIZE * (sample_offset / ILBC_SAMPLES);
+ if (whence == SEEK_SET)
+ offset = bytes;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = cur + bytes;
+ else if (whence == SEEK_END)
+ offset = max - bytes;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max)?max:offset;
+ }
+ /* protect against seeking beyond begining. */
+ offset = (offset < min)?min:offset;
+ if (fseeko(fs->f, offset, SEEK_SET) < 0)
+ return -1;
+ return 0;
+}
+
+static int ilbc_trunc(struct ast_filestream *fs)
+{
+ /* Truncate file to current length */
+ if (ftruncate(fileno(fs->f), ftello(fs->f)) < 0)
+ return -1;
+ return 0;
+}
+
+static off_t ilbc_tell(struct ast_filestream *fs)
+{
+ off_t offset = ftello(fs->f);
+ return (offset/ILBC_BUF_SIZE)*ILBC_SAMPLES;
+}
+
+static const struct ast_format ilbc_f = {
+ .name = "iLBC",
+ .exts = "ilbc",
+ .format = AST_FORMAT_ILBC,
+ .write = ilbc_write,
+ .seek = ilbc_seek,
+ .trunc = ilbc_trunc,
+ .tell = ilbc_tell,
+ .read = ilbc_read,
+ .buf_size = ILBC_BUF_SIZE + AST_FRIENDLY_OFFSET,
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&ilbc_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(ilbc_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw iLBC data");
diff --git a/trunk/formats/format_jpeg.c b/trunk/formats/format_jpeg.c
new file mode 100644
index 000000000..4d8d7855d
--- /dev/null
+++ b/trunk/formats/format_jpeg.c
@@ -0,0 +1,115 @@
+/*
+ * 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 JPEG File format support.
+ *
+ * \arg File name extension: jpeg, jpg
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/image.h"
+#include "asterisk/endian.h"
+
+static struct ast_frame *jpeg_read_image(int fd, int len)
+{
+ struct ast_frame fr;
+ int res;
+ char buf[65536];
+ if (len > sizeof(buf) || len < 0) {
+ ast_log(LOG_WARNING, "JPEG image too large to read\n");
+ return NULL;
+ }
+ res = read(fd, buf, len);
+ if (res < len) {
+ ast_log(LOG_WARNING, "Only read %d of %d bytes: %s\n", res, len, strerror(errno));
+ }
+ memset(&fr, 0, sizeof(fr));
+ fr.frametype = AST_FRAME_IMAGE;
+ fr.subclass = AST_FORMAT_JPEG;
+ fr.data = buf;
+ fr.src = "JPEG Read";
+ fr.datalen = len;
+ return ast_frisolate(&fr);
+}
+
+static int jpeg_identify(int fd)
+{
+ char buf[10];
+ int res;
+ res = read(fd, buf, sizeof(buf));
+ if (res < sizeof(buf))
+ return 0;
+ if (memcmp(buf + 6, "JFIF", 4))
+ return 0;
+ return 1;
+}
+
+static int jpeg_write_image(int fd, struct ast_frame *fr)
+{
+ int res=0;
+ if (fr->frametype != AST_FRAME_IMAGE) {
+ ast_log(LOG_WARNING, "Not an image\n");
+ return -1;
+ }
+ if (fr->subclass != AST_FORMAT_JPEG) {
+ ast_log(LOG_WARNING, "Not a jpeg image\n");
+ return -1;
+ }
+ if (fr->datalen) {
+ res = write(fd, fr->data, fr->datalen);
+ if (res != fr->datalen) {
+ ast_log(LOG_WARNING, "Only wrote %d of %d bytes: %s\n", res, fr->datalen, strerror(errno));
+ return -1;
+ }
+ }
+ return res;
+}
+
+static struct ast_imager jpeg_format = {
+ .name = "jpg",
+ .desc = "JPEG (Joint Picture Experts Group)",
+ .exts = "jpg|jpeg",
+ .format = AST_FORMAT_JPEG,
+ .read_image = jpeg_read_image,
+ .identify = jpeg_identify,
+ .write_image = jpeg_write_image,
+};
+
+static int load_module(void)
+{
+ if (ast_image_register(&jpeg_format))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_image_unregister(&jpeg_format);
+
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "JPEG (Joint Picture Experts Group) Image Format");
diff --git a/trunk/formats/format_ogg_vorbis.c b/trunk/formats/format_ogg_vorbis.c
new file mode 100644
index 000000000..669e96a7d
--- /dev/null
+++ b/trunk/formats/format_ogg_vorbis.c
@@ -0,0 +1,552 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Jeff Ollie
+ *
+ * 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 OGG/Vorbis streams.
+ * \arg File name extension: ogg
+ * \ingroup formats
+ */
+
+/* the order of these dependencies is important... it also specifies
+ the link order of the libraries during linking
+*/
+
+/*** MODULEINFO
+ <depend>vorbis</depend>
+ <depend>ogg</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <vorbis/codec.h>
+#include <vorbis/vorbisenc.h>
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+
+/*
+ * this is the number of samples we deal with. Samples are converted
+ * to SLINEAR so each one uses 2 bytes in the buffer.
+ */
+#define SAMPLES_MAX 160
+#define BUF_SIZE (2*SAMPLES_MAX)
+
+#define BLOCK_SIZE 4096 /* used internally in the vorbis routines */
+
+struct vorbis_desc { /* format specific parameters */
+ /* structures for handling the Ogg container */
+ ogg_sync_state oy;
+ ogg_stream_state os;
+ ogg_page og;
+ ogg_packet op;
+
+ /* structures for handling Vorbis audio data */
+ vorbis_info vi;
+ vorbis_comment vc;
+ vorbis_dsp_state vd;
+ vorbis_block vb;
+
+ /*! \brief Indicates whether this filestream is set up for reading or writing. */
+ int writing;
+
+ /*! \brief Indicates whether an End of Stream condition has been detected. */
+ int eos;
+};
+
+/*!
+ * \brief Create a new OGG/Vorbis filestream and set it up for reading.
+ * \param s File that points to on disk storage of the OGG/Vorbis data.
+ * \return The new filestream.
+ */
+static int ogg_vorbis_open(struct ast_filestream *s)
+{
+ int i;
+ int bytes;
+ int result;
+ char **ptr;
+ char *buffer;
+ struct vorbis_desc *tmp = (struct vorbis_desc *)s->_private;
+
+ tmp->writing = 0;
+
+ ogg_sync_init(&tmp->oy);
+
+ buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE);
+ bytes = fread(buffer, 1, BLOCK_SIZE, s->f);
+ ogg_sync_wrote(&tmp->oy, bytes);
+
+ result = ogg_sync_pageout(&tmp->oy, &tmp->og);
+ if (result != 1) {
+ if(bytes < BLOCK_SIZE) {
+ ast_log(LOG_ERROR, "Run out of data...\n");
+ } else {
+ ast_log(LOG_ERROR, "Input does not appear to be an Ogg bitstream.\n");
+ }
+ ogg_sync_clear(&tmp->oy);
+ return -1;
+ }
+
+ ogg_stream_init(&tmp->os, ogg_page_serialno(&tmp->og));
+ vorbis_info_init(&tmp->vi);
+ vorbis_comment_init(&tmp->vc);
+
+ if (ogg_stream_pagein(&tmp->os, &tmp->og) < 0) {
+ ast_log(LOG_ERROR, "Error reading first page of Ogg bitstream data.\n");
+error:
+ ogg_stream_clear(&tmp->os);
+ vorbis_comment_clear(&tmp->vc);
+ vorbis_info_clear(&tmp->vi);
+ ogg_sync_clear(&tmp->oy);
+ return -1;
+ }
+
+ if (ogg_stream_packetout(&tmp->os, &tmp->op) != 1) {
+ ast_log(LOG_ERROR, "Error reading initial header packet.\n");
+ goto error;
+ }
+
+ if (vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op) < 0) {
+ ast_log(LOG_ERROR, "This Ogg bitstream does not contain Vorbis audio data.\n");
+ goto error;
+ }
+
+ for (i = 0; i < 2 ; ) {
+ while (i < 2) {
+ result = ogg_sync_pageout(&tmp->oy, &tmp->og);
+ if (result == 0)
+ break;
+ if (result == 1) {
+ ogg_stream_pagein(&tmp->os, &tmp->og);
+ while(i < 2) {
+ result = ogg_stream_packetout(&tmp->os,&tmp->op);
+ if(result == 0)
+ break;
+ if(result < 0) {
+ ast_log(LOG_ERROR, "Corrupt secondary header. Exiting.\n");
+ goto error;
+ }
+ vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op);
+ i++;
+ }
+ }
+ }
+
+ buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE);
+ bytes = fread(buffer, 1, BLOCK_SIZE, s->f);
+ if (bytes == 0 && i < 2) {
+ ast_log(LOG_ERROR, "End of file before finding all Vorbis headers!\n");
+ goto error;
+ }
+ ogg_sync_wrote(&tmp->oy, bytes);
+ }
+
+ for (ptr = tmp->vc.user_comments; *ptr; ptr++)
+ ast_debug(1, "OGG/Vorbis comment: %s\n", *ptr);
+ ast_debug(1, "OGG/Vorbis bitstream is %d channel, %ldHz\n", tmp->vi.channels, tmp->vi.rate);
+ ast_debug(1, "OGG/Vorbis file encoded by: %s\n", tmp->vc.vendor);
+
+ if (tmp->vi.channels != 1) {
+ ast_log(LOG_ERROR, "Only monophonic OGG/Vorbis files are currently supported!\n");
+ goto error;
+ }
+
+ if (tmp->vi.rate != DEFAULT_SAMPLE_RATE) {
+ ast_log(LOG_ERROR, "Only 8000Hz OGG/Vorbis files are currently supported!\n");
+ vorbis_block_clear(&tmp->vb);
+ vorbis_dsp_clear(&tmp->vd);
+ goto error;
+ }
+
+ vorbis_synthesis_init(&tmp->vd, &tmp->vi);
+ vorbis_block_init(&tmp->vd, &tmp->vb);
+
+ return 0;
+}
+
+/*!
+ * \brief Create a new OGG/Vorbis filestream and set it up for writing.
+ * \param s File pointer that points to on-disk storage.
+ * \param comment Comment that should be embedded in the OGG/Vorbis file.
+ * \return A new filestream.
+ */
+static int ogg_vorbis_rewrite(struct ast_filestream *s,
+ const char *comment)
+{
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+ struct vorbis_desc *tmp = (struct vorbis_desc *)s->_private;
+
+ tmp->writing = 1;
+
+ vorbis_info_init(&tmp->vi);
+
+ if (vorbis_encode_init_vbr(&tmp->vi, 1, DEFAULT_SAMPLE_RATE, 0.4)) {
+ ast_log(LOG_ERROR, "Unable to initialize Vorbis encoder!\n");
+ return -1;
+ }
+
+ vorbis_comment_init(&tmp->vc);
+ vorbis_comment_add_tag(&tmp->vc, "ENCODER", "Asterisk PBX");
+ if (comment)
+ vorbis_comment_add_tag(&tmp->vc, "COMMENT", (char *) comment);
+
+ vorbis_analysis_init(&tmp->vd, &tmp->vi);
+ vorbis_block_init(&tmp->vd, &tmp->vb);
+
+ ogg_stream_init(&tmp->os, ast_random());
+
+ vorbis_analysis_headerout(&tmp->vd, &tmp->vc, &header, &header_comm,
+ &header_code);
+ ogg_stream_packetin(&tmp->os, &header);
+ ogg_stream_packetin(&tmp->os, &header_comm);
+ ogg_stream_packetin(&tmp->os, &header_code);
+
+ while (!tmp->eos) {
+ if (ogg_stream_flush(&tmp->os, &tmp->og) == 0)
+ break;
+ fwrite(tmp->og.header, 1, tmp->og.header_len, s->f);
+ fwrite(tmp->og.body, 1, tmp->og.body_len, s->f);
+ if (ogg_page_eos(&tmp->og))
+ tmp->eos = 1;
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Write out any pending encoded data.
+ * \param s An OGG/Vorbis filestream.
+ * \param f The file to write to.
+ */
+static void write_stream(struct vorbis_desc *s, FILE *f)
+{
+ while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
+ vorbis_analysis(&s->vb, NULL);
+ vorbis_bitrate_addblock(&s->vb);
+
+ while (vorbis_bitrate_flushpacket(&s->vd, &s->op)) {
+ ogg_stream_packetin(&s->os, &s->op);
+ while (!s->eos) {
+ if (ogg_stream_pageout(&s->os, &s->og) == 0) {
+ break;
+ }
+ fwrite(s->og.header, 1, s->og.header_len, f);
+ fwrite(s->og.body, 1, s->og.body_len, f);
+ if (ogg_page_eos(&s->og)) {
+ s->eos = 1;
+ }
+ }
+ }
+ }
+}
+
+/*!
+ * \brief Write audio data from a frame to an OGG/Vorbis filestream.
+ * \param fs An OGG/Vorbis filestream.
+ * \param f A frame containing audio to be written to the filestream.
+ * \return -1 if there was an error, 0 on success.
+ */
+static int ogg_vorbis_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int i;
+ float **buffer;
+ short *data;
+ struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
+
+ if (!s->writing) {
+ ast_log(LOG_ERROR, "This stream is not set up for writing!\n");
+ return -1;
+ }
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n",
+ f->subclass);
+ return -1;
+ }
+ if (!f->datalen)
+ return -1;
+
+ data = (short *) f->data;
+
+ buffer = vorbis_analysis_buffer(&s->vd, f->samples);
+
+ for (i = 0; i < f->samples; i++)
+ buffer[0][i] = (double)data[i] / 32768.0;
+
+ vorbis_analysis_wrote(&s->vd, f->samples);
+
+ write_stream(s, fs->f);
+
+ return 0;
+}
+
+/*!
+ * \brief Close a OGG/Vorbis filestream.
+ * \param fs A OGG/Vorbis filestream.
+ */
+static void ogg_vorbis_close(struct ast_filestream *fs)
+{
+ struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
+
+ if (s->writing) {
+ /* Tell the Vorbis encoder that the stream is finished
+ * and write out the rest of the data */
+ vorbis_analysis_wrote(&s->vd, 0);
+ write_stream(s, fs->f);
+ }
+
+ ogg_stream_clear(&s->os);
+ vorbis_block_clear(&s->vb);
+ vorbis_dsp_clear(&s->vd);
+ vorbis_comment_clear(&s->vc);
+ vorbis_info_clear(&s->vi);
+
+ if (s->writing) {
+ ogg_sync_clear(&s->oy);
+ }
+}
+
+/*!
+ * \brief Get audio data.
+ * \param fs An OGG/Vorbis filestream.
+ * \param pcm Pointer to a buffere to store audio data in.
+ */
+
+static int read_samples(struct ast_filestream *fs, float ***pcm)
+{
+ int samples_in;
+ int result;
+ char *buffer;
+ int bytes;
+ struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
+
+ while (1) {
+ samples_in = vorbis_synthesis_pcmout(&s->vd, pcm);
+ if (samples_in > 0) {
+ return samples_in;
+ }
+
+ /* The Vorbis decoder needs more data... */
+ /* See ifOGG has any packets in the current page for the Vorbis decoder. */
+ result = ogg_stream_packetout(&s->os, &s->op);
+ if (result > 0) {
+ /* Yes OGG had some more packets for the Vorbis decoder. */
+ if (vorbis_synthesis(&s->vb, &s->op) == 0) {
+ vorbis_synthesis_blockin(&s->vd, &s->vb);
+ }
+
+ continue;
+ }
+
+ if (result < 0)
+ ast_log(LOG_WARNING,
+ "Corrupt or missing data at this page position; continuing...\n");
+
+ /* No more packets left in the current page... */
+
+ if (s->eos) {
+ /* No more pages left in the stream */
+ return -1;
+ }
+
+ while (!s->eos) {
+ /* See ifOGG has any pages in it's internal buffers */
+ result = ogg_sync_pageout(&s->oy, &s->og);
+ if (result > 0) {
+ /* Yes, OGG has more pages in it's internal buffers,
+ add the page to the stream state */
+ result = ogg_stream_pagein(&s->os, &s->og);
+ if (result == 0) {
+ /* Yes, got a new,valid page */
+ if (ogg_page_eos(&s->og)) {
+ s->eos = 1;
+ }
+ break;
+ }
+ ast_log(LOG_WARNING,
+ "Invalid page in the bitstream; continuing...\n");
+ }
+
+ if (result < 0)
+ ast_log(LOG_WARNING,
+ "Corrupt or missing data in bitstream; continuing...\n");
+
+ /* No, we need to read more data from the file descrptor */
+ /* get a buffer from OGG to read the data into */
+ buffer = ogg_sync_buffer(&s->oy, BLOCK_SIZE);
+ /* read more data from the file descriptor */
+ bytes = fread(buffer, 1, BLOCK_SIZE, fs->f);
+ /* Tell OGG how many bytes we actually read into the buffer */
+ ogg_sync_wrote(&s->oy, bytes);
+ if (bytes == 0) {
+ s->eos = 1;
+ }
+ }
+ }
+}
+
+/*!
+ * \brief Read a frame full of audio data from the filestream.
+ * \param fs The filestream.
+ * \param whennext Number of sample times to schedule the next call.
+ * \return A pointer to a frame containing audio data or NULL ifthere is no more audio data.
+ */
+static struct ast_frame *ogg_vorbis_read(struct ast_filestream *fs,
+ int *whennext)
+{
+ int clipflag = 0;
+ int i;
+ int j;
+ double accumulator[SAMPLES_MAX];
+ int val;
+ int samples_in;
+ int samples_out = 0;
+ struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
+ short *buf; /* SLIN data buffer */
+
+ fs->fr.frametype = AST_FRAME_VOICE;
+ fs->fr.subclass = AST_FORMAT_SLINEAR;
+ fs->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
+ buf = (short *)(fs->fr.data); /* SLIN data buffer */
+
+ while (samples_out != SAMPLES_MAX) {
+ float **pcm;
+ int len = SAMPLES_MAX - samples_out;
+
+ /* See ifVorbis decoder has some audio data for us ... */
+ samples_in = read_samples(fs, &pcm);
+ if (samples_in <= 0)
+ break;
+
+ /* Got some audio data from Vorbis... */
+ /* Convert the float audio data to 16-bit signed linear */
+
+ clipflag = 0;
+ if (samples_in > len)
+ samples_in = len;
+ for (j = 0; j < samples_in; j++)
+ accumulator[j] = 0.0;
+
+ for (i = 0; i < s->vi.channels; i++) {
+ float *mono = pcm[i];
+ for (j = 0; j < samples_in; j++)
+ accumulator[j] += mono[j];
+ }
+
+ for (j = 0; j < samples_in; j++) {
+ val = accumulator[j] * 32767.0 / s->vi.channels;
+ if (val > 32767) {
+ val = 32767;
+ clipflag = 1;
+ } else if (val < -32768) {
+ val = -32768;
+ clipflag = 1;
+ }
+ buf[samples_out + j] = val;
+ }
+
+ if (clipflag)
+ ast_log(LOG_WARNING, "Clipping in frame %ld\n", (long) (s->vd.sequence));
+ /* Tell the Vorbis decoder how many samples we actually used. */
+ vorbis_synthesis_read(&s->vd, samples_in);
+ samples_out += samples_in;
+ }
+
+ if (samples_out > 0) {
+ fs->fr.datalen = samples_out * 2;
+ fs->fr.samples = samples_out;
+ *whennext = samples_out;
+
+ return &fs->fr;
+ } else {
+ return NULL;
+ }
+}
+
+/*!
+ * \brief Trucate an OGG/Vorbis filestream.
+ * \param s The filestream to truncate.
+ * \return 0 on success, -1 on failure.
+ */
+
+static int ogg_vorbis_trunc(struct ast_filestream *s)
+{
+ ast_log(LOG_WARNING, "Truncation is not supported on OGG/Vorbis streams!\n");
+ return -1;
+}
+
+/*!
+ * \brief Seek to a specific position in an OGG/Vorbis filestream.
+ * \param s The filestream to truncate.
+ * \param sample_offset New position for the filestream, measured in 8KHz samples.
+ * \param whence Location to measure
+ * \return 0 on success, -1 on failure.
+ */
+static int ogg_vorbis_seek(struct ast_filestream *s, off_t sample_offset, int whence)
+{
+ ast_log(LOG_WARNING, "Seeking is not supported on OGG/Vorbis streams!\n");
+ return -1;
+}
+
+static off_t ogg_vorbis_tell(struct ast_filestream *s)
+{
+ ast_log(LOG_WARNING, "Telling is not supported on OGG/Vorbis streams!\n");
+ return -1;
+}
+
+static const struct ast_format vorbis_f = {
+ .name = "ogg_vorbis",
+ .exts = "ogg",
+ .format = AST_FORMAT_SLINEAR,
+ .open = ogg_vorbis_open,
+ .rewrite = ogg_vorbis_rewrite,
+ .write = ogg_vorbis_write,
+ .seek = ogg_vorbis_seek,
+ .trunc = ogg_vorbis_trunc,
+ .tell = ogg_vorbis_tell,
+ .read = ogg_vorbis_read,
+ .close = ogg_vorbis_close,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct vorbis_desc),
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&vorbis_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(vorbis_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OGG/Vorbis audio");
+
diff --git a/trunk/formats/format_pcm.c b/trunk/formats/format_pcm.c
new file mode 100644
index 000000000..c514b5863
--- /dev/null
+++ b/trunk/formats/format_pcm.c
@@ -0,0 +1,485 @@
+/*
+ * 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 Flat, binary, ulaw PCM file format.
+ * \arg File name extension: pcm, ulaw, ul, mu
+ *
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+
+#define BUF_SIZE 160 /* 160 bytes, and same number of samples */
+
+static char ulaw_silence[BUF_SIZE];
+static char alaw_silence[BUF_SIZE];
+
+/* #define REALTIME_WRITE */ /* XXX does it work at all ? */
+
+#ifdef REALTIME_WRITE
+struct pcm_desc {
+ unsigned long start_time;
+};
+
+/* Returns time in msec since system boot. */
+static unsigned long get_time(void)
+{
+ struct tms buf;
+ clock_t cur;
+
+ cur = times( &buf );
+ if( cur < 0 ) {
+ ast_log( LOG_WARNING, "Cannot get current time\n" );
+ return 0;
+ }
+ return cur * 1000 / sysconf( _SC_CLK_TCK );
+}
+
+static int pcma_open(struct ast_filestream *s)
+{
+ if (s->fmt->format == AST_FORMAT_ALAW)
+ pd->starttime = get_time();
+ return 0;
+}
+
+static int pcma_rewrite(struct ast_filestream *s, const char *comment)
+{
+ return pcma_open(s);
+}
+#endif
+
+static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+
+ /* Send a frame from the file to the appropriate channel */
+
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = s->fmt->format;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ s->fr.datalen = res;
+ *whennext = s->fr.samples = res;
+ return &s->fr;
+}
+
+static int pcm_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t cur, max, offset = 0;
+ int ret = -1; /* assume error */
+
+ cur = ftello(fs->f);
+ fseeko(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+
+ switch (whence) {
+ case SEEK_SET:
+ offset = sample_offset;
+ break;
+ case SEEK_END:
+ offset = max - sample_offset;
+ break;
+ case SEEK_CUR:
+ case SEEK_FORCECUR:
+ offset = cur + sample_offset;
+ break;
+ default:
+ ast_log(LOG_WARNING, "invalid whence %d, assuming SEEK_SET\n", whence);
+ offset = sample_offset;
+ }
+ if (offset < 0) {
+ ast_log(LOG_WARNING, "negative offset %ld, resetting to 0\n", (long) offset);
+ offset = 0;
+ }
+ if (whence == SEEK_FORCECUR && offset > max) { /* extend the file */
+ size_t left = offset - max;
+ const char *src = (fs->fmt->format == AST_FORMAT_ALAW) ? alaw_silence : ulaw_silence;
+
+ while (left) {
+ size_t written = fwrite(src, 1, (left > BUF_SIZE) ? BUF_SIZE : left, fs->f);
+ if (written == -1)
+ break; /* error */
+ left -= written;
+ }
+ ret = 0; /* successful */
+ } else {
+ if (offset > max) {
+ ast_log(LOG_WARNING, "offset too large %ld, truncating to %ld\n", (long) offset, (long) max);
+ offset = max;
+ }
+ ret = fseeko(fs->f, offset, SEEK_SET);
+ }
+ return ret;
+}
+
+static int pcm_trunc(struct ast_filestream *fs)
+{
+ return ftruncate(fileno(fs->f), ftello(fs->f));
+}
+
+static off_t pcm_tell(struct ast_filestream *fs)
+{
+ return ftello(fs->f);
+}
+
+static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != fs->fmt->format) {
+ ast_log(LOG_WARNING, "Asked to write incompatible format frame (%d)!\n", f->subclass);
+ return -1;
+ }
+
+#ifdef REALTIME_WRITE
+ if (s->fmt->format == AST_FORMAT_ALAW) {
+ struct pcm_desc *pd = (struct pcm_desc *)fs->_private;
+ struct stat stat_buf;
+ unsigned long cur_time = get_time();
+ unsigned long fpos = ( cur_time - pd->start_time ) * 8; /* 8 bytes per msec */
+ /* Check if we have written to this position yet. If we have, then increment pos by one frame
+ * for some degree of protection against receiving packets in the same clock tick.
+ */
+
+ fstat(fileno(fs->f), &stat_buf );
+ if (stat_buf.st_size > fpos )
+ fpos += f->datalen; /* Incrementing with the size of this current frame */
+
+ if (stat_buf.st_size < fpos) {
+ /* fill the gap with 0x55 rather than 0. */
+ char buf[1024];
+ unsigned long cur, to_write;
+
+ cur = stat_buf.st_size;
+ if (fseek(fs->f, cur, SEEK_SET) < 0) {
+ ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) );
+ return -1;
+ }
+ memset(buf, 0x55, 512);
+ while (cur < fpos) {
+ to_write = fpos - cur;
+ if (to_write > sizeof(buf))
+ to_write = sizeof(buf);
+ fwrite(buf, 1, to_write, fs->f);
+ cur += to_write;
+ }
+ }
+
+ if (fseek(s->f, fpos, SEEK_SET) < 0) {
+ ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) );
+ return -1;
+ }
+ }
+#endif /* REALTIME_WRITE */
+
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+/* SUN .au support routines */
+
+#define AU_HEADER_SIZE 24
+#define AU_HEADER(var) uint32_t var[6]
+
+#define AU_HDR_MAGIC_OFF 0
+#define AU_HDR_HDR_SIZE_OFF 1
+#define AU_HDR_DATA_SIZE_OFF 2
+#define AU_HDR_ENCODING_OFF 3
+#define AU_HDR_SAMPLE_RATE_OFF 4
+#define AU_HDR_CHANNELS_OFF 5
+
+#define AU_ENC_8BIT_ULAW 1
+
+#define AU_MAGIC 0x2e736e64
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define htoll(b) (b)
+#define htols(b) (b)
+#define ltohl(b) (b)
+#define ltohs(b) (b)
+#else
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htoll(b) \
+ (((((b) ) & 0xFF) << 24) | \
+ ((((b) >> 8) & 0xFF) << 16) | \
+ ((((b) >> 16) & 0xFF) << 8) | \
+ ((((b) >> 24) & 0xFF) ))
+#define htols(b) \
+ (((((b) ) & 0xFF) << 8) | \
+ ((((b) >> 8) & 0xFF) ))
+#define ltohl(b) htoll(b)
+#define ltohs(b) htols(b)
+#else
+#error "Endianess not defined"
+#endif
+#endif
+
+static int check_header(FILE *f)
+{
+ AU_HEADER(header);
+ uint32_t magic;
+ uint32_t hdr_size;
+ uint32_t data_size;
+ uint32_t encoding;
+ uint32_t sample_rate;
+ uint32_t channels;
+
+ if (fread(header, 1, AU_HEADER_SIZE, f) != AU_HEADER_SIZE) {
+ ast_log(LOG_WARNING, "Read failed (header)\n");
+ return -1;
+ }
+ magic = ltohl(header[AU_HDR_MAGIC_OFF]);
+ if (magic != (uint32_t) AU_MAGIC) {
+ ast_log(LOG_WARNING, "Bad magic: 0x%x\n", magic);
+ }
+/* hdr_size = ltohl(header[AU_HDR_HDR_SIZE_OFF]);
+ if (hdr_size < AU_HEADER_SIZE)*/
+ hdr_size = AU_HEADER_SIZE;
+/* data_size = ltohl(header[AU_HDR_DATA_SIZE_OFF]); */
+ encoding = ltohl(header[AU_HDR_ENCODING_OFF]);
+ if (encoding != AU_ENC_8BIT_ULAW) {
+ ast_log(LOG_WARNING, "Unexpected format: %d. Only 8bit ULAW allowed (%d)\n", encoding, AU_ENC_8BIT_ULAW);
+ return -1;
+ }
+ sample_rate = ltohl(header[AU_HDR_SAMPLE_RATE_OFF]);
+ if (sample_rate != DEFAULT_SAMPLE_RATE) {
+ ast_log(LOG_WARNING, "Sample rate can only be 8000 not %d\n", sample_rate);
+ return -1;
+ }
+ channels = ltohl(header[AU_HDR_CHANNELS_OFF]);
+ if (channels != 1) {
+ ast_log(LOG_WARNING, "Not in mono: channels=%d\n", channels);
+ return -1;
+ }
+ /* Skip to data */
+ fseek(f, 0, SEEK_END);
+ data_size = ftell(f) - hdr_size;
+ if (fseek(f, hdr_size, SEEK_SET) == -1 ) {
+ ast_log(LOG_WARNING, "Failed to skip to data: %d\n", hdr_size);
+ return -1;
+ }
+ return data_size;
+}
+
+static int update_header(FILE *f)
+{
+ off_t cur, end;
+ uint32_t datalen;
+ int bytes;
+
+ cur = ftell(f);
+ fseek(f, 0, SEEK_END);
+ end = ftell(f);
+ /* data starts 24 bytes in */
+ bytes = end - AU_HEADER_SIZE;
+ datalen = htoll(bytes);
+
+ if (cur < 0) {
+ ast_log(LOG_WARNING, "Unable to find our position\n");
+ return -1;
+ }
+ if (fseek(f, AU_HDR_DATA_SIZE_OFF * sizeof(uint32_t), SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to set our position\n");
+ return -1;
+ }
+ if (fwrite(&datalen, 1, sizeof(datalen), f) != sizeof(datalen)) {
+ ast_log(LOG_WARNING, "Unable to set write file size\n");
+ return -1;
+ }
+ if (fseek(f, cur, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to return to position\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int write_header(FILE *f)
+{
+ AU_HEADER(header);
+
+ header[AU_HDR_MAGIC_OFF] = htoll((uint32_t) AU_MAGIC);
+ header[AU_HDR_HDR_SIZE_OFF] = htoll(AU_HEADER_SIZE);
+ header[AU_HDR_DATA_SIZE_OFF] = 0;
+ header[AU_HDR_ENCODING_OFF] = htoll(AU_ENC_8BIT_ULAW);
+ header[AU_HDR_SAMPLE_RATE_OFF] = htoll(DEFAULT_SAMPLE_RATE);
+ header[AU_HDR_CHANNELS_OFF] = htoll(1);
+
+ /* Write an au header, ignoring sizes which will be filled in later */
+ fseek(f, 0, SEEK_SET);
+ if (fwrite(header, 1, AU_HEADER_SIZE, f) != AU_HEADER_SIZE) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int au_open(struct ast_filestream *s)
+{
+ if (check_header(s->f) < 0)
+ return -1;
+ return 0;
+}
+
+static int au_rewrite(struct ast_filestream *s, const char *comment)
+{
+ if (write_header(s->f))
+ return -1;
+ return 0;
+}
+
+/* XXX check this, probably incorrect */
+static int au_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t min, max, cur;
+ long offset = 0, samples;
+
+ samples = sample_offset;
+ min = AU_HEADER_SIZE;
+ cur = ftello(fs->f);
+ fseek(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+ if (whence == SEEK_SET)
+ offset = samples + min;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = samples + cur;
+ else if (whence == SEEK_END)
+ offset = max - samples;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max) ? max : offset;
+ }
+ /* always protect the header space. */
+ offset = (offset < min) ? min : offset;
+ return fseeko(fs->f, offset, SEEK_SET);
+}
+
+static int au_trunc(struct ast_filestream *fs)
+{
+ if (ftruncate(fileno(fs->f), ftell(fs->f)))
+ return -1;
+ return update_header(fs->f);
+}
+
+static off_t au_tell(struct ast_filestream *fs)
+{
+ off_t offset = ftello(fs->f);
+ return offset - AU_HEADER_SIZE;
+}
+
+static const struct ast_format alaw_f = {
+ .name = "alaw",
+ .exts = "alaw|al",
+ .format = AST_FORMAT_ALAW,
+ .write = pcm_write,
+ .seek = pcm_seek,
+ .trunc = pcm_trunc,
+ .tell = pcm_tell,
+ .read = pcm_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+#ifdef REALTIME_WRITE
+ .open = pcma_open,
+ .rewrite = pcma_rewrite,
+ .desc_size = sizeof(struct pcm_desc),
+#endif
+};
+
+static const struct ast_format pcm_f = {
+ .name = "pcm",
+ .exts = "pcm|ulaw|ul|mu",
+ .format = AST_FORMAT_ULAW,
+ .write = pcm_write,
+ .seek = pcm_seek,
+ .trunc = pcm_trunc,
+ .tell = pcm_tell,
+ .read = pcm_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+};
+
+static const struct ast_format g722_f = {
+ .name = "g722",
+ .exts = "g722",
+ .format = AST_FORMAT_G722,
+ .write = pcm_write,
+ .seek = pcm_seek,
+ .trunc = pcm_trunc,
+ .tell = pcm_tell,
+ .read = pcm_read,
+ .buf_size = (BUF_SIZE * 2) + AST_FRIENDLY_OFFSET,
+};
+
+static const struct ast_format au_f = {
+ .name = "au",
+ .exts = "au",
+ .format = AST_FORMAT_ULAW,
+ .open = au_open,
+ .rewrite = au_rewrite,
+ .write = pcm_write,
+ .seek = au_seek,
+ .trunc = au_trunc,
+ .tell = au_tell,
+ .read = pcm_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, /* this many shorts */
+};
+
+static int load_module(void)
+{
+ int index;
+
+ /* XXX better init ? */
+ for (index = 0; index < (sizeof(ulaw_silence) / sizeof(ulaw_silence[0])); index++)
+ ulaw_silence[index] = AST_LIN2MU(0);
+ for (index = 0; index < (sizeof(alaw_silence) / sizeof(alaw_silence[0])); index++)
+ alaw_silence[index] = AST_LIN2A(0);
+
+ if ( ast_format_register(&pcm_f)
+ || ast_format_register(&alaw_f)
+ || ast_format_register(&au_f)
+ || ast_format_register(&g722_f) )
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(pcm_f.name)
+ || ast_format_unregister(alaw_f.name)
+ || ast_format_unregister(au_f.name)
+ || ast_format_unregister(g722_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw/Sun uLaw/ALaw 8KHz (PCM,PCMA,AU), G.722 16Khz");
diff --git a/trunk/formats/format_sln.c b/trunk/formats/format_sln.c
new file mode 100644
index 000000000..51f796271
--- /dev/null
+++ b/trunk/formats/format_sln.c
@@ -0,0 +1,130 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Anthony Minessale
+ * Anthony Minessale (anthmct@yahoo.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 RAW SLINEAR Format
+ * \arg File name extensions: sln, raw
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+#define BUF_SIZE 320 /* 320 bytes, 160 samples */
+#define SLIN_SAMPLES 160
+
+static struct ast_frame *slinear_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ /* Send a frame from the file to the appropriate channel */
+
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_SLINEAR;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples = res/2;
+ s->fr.datalen = res;
+ return &s->fr;
+}
+
+static int slinear_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res;
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Asked to write non-slinear frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int slinear_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t offset=0,min,cur,max;
+
+ min = 0;
+ sample_offset <<= 1;
+ cur = ftello(fs->f);
+ fseeko(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+ if (whence == SEEK_SET)
+ offset = sample_offset;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = sample_offset + cur;
+ else if (whence == SEEK_END)
+ offset = max - sample_offset;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max)?max:offset;
+ }
+ /* always protect against seeking past begining. */
+ offset = (offset < min)?min:offset;
+ return fseeko(fs->f, offset, SEEK_SET);
+}
+
+static int slinear_trunc(struct ast_filestream *fs)
+{
+ return ftruncate(fileno(fs->f), ftello(fs->f));
+}
+
+static off_t slinear_tell(struct ast_filestream *fs)
+{
+ return ftello(fs->f) / 2;
+}
+
+static const struct ast_format slin_f = {
+ .name = "sln",
+ .exts = "sln|raw",
+ .format = AST_FORMAT_SLINEAR,
+ .write = slinear_write,
+ .seek = slinear_seek,
+ .trunc = slinear_trunc,
+ .tell = slinear_tell,
+ .read = slinear_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&slin_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(slin_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw Signed Linear Audio support (SLN)");
diff --git a/trunk/formats/format_sln16.c b/trunk/formats/format_sln16.c
new file mode 100644
index 000000000..50349f2dd
--- /dev/null
+++ b/trunk/formats/format_sln16.c
@@ -0,0 +1,138 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, Anthony Minessale and Digium, Inc.
+ * Anthony Minessale (anthmct@yahoo.com)
+ * Kevin P. Fleming <kpfleming@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 RAW SLINEAR 16 Format
+ * \arg File name extensions: sln16
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+#define BUF_SIZE 640 /* 640 bytes, 320 samples */
+#define SLIN_SAMPLES 320
+
+static struct ast_frame *slinear_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ /* Send a frame from the file to the appropriate channel */
+
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_SLINEAR16;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples = res/2;
+ s->fr.datalen = res;
+ return &s->fr;
+}
+
+static int slinear_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_SLINEAR16) {
+ ast_log(LOG_WARNING, "Asked to write non-slinear16 frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int slinear_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t offset = 0, min = 0, cur, max;
+
+ sample_offset <<= 1;
+
+ cur = ftello(fs->f);
+
+ fseeko(fs->f, 0, SEEK_END);
+
+ max = ftello(fs->f);
+
+ if (whence == SEEK_SET)
+ offset = sample_offset;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = sample_offset + cur;
+ else if (whence == SEEK_END)
+ offset = max - sample_offset;
+
+ if (whence != SEEK_FORCECUR)
+ offset = (offset > max) ? max : offset;
+
+ /* always protect against seeking past begining. */
+ offset = (offset < min) ? min : offset;
+
+ return fseeko(fs->f, offset, SEEK_SET);
+}
+
+static int slinear_trunc(struct ast_filestream *fs)
+{
+ return ftruncate(fileno(fs->f), ftello(fs->f));
+}
+
+static off_t slinear_tell(struct ast_filestream *fs)
+{
+ return ftello(fs->f) / 2;
+}
+
+static const struct ast_format slin_f = {
+ .name = "sln16",
+ .exts = "sln16",
+ .format = AST_FORMAT_SLINEAR16,
+ .write = slinear_write,
+ .seek = slinear_seek,
+ .trunc = slinear_trunc,
+ .tell = slinear_tell,
+ .read = slinear_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&slin_f))
+ return AST_MODULE_LOAD_FAILURE;
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(slin_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw Signed Linear 16KHz Audio support (SLN16)");
diff --git a/trunk/formats/format_vox.c b/trunk/formats/format_vox.c
new file mode 100644
index 000000000..f22b4881a
--- /dev/null
+++ b/trunk/formats/format_vox.c
@@ -0,0 +1,135 @@
+/*
+ * 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 Flat, binary, ADPCM vox file format.
+ * \arg File name extensions: vox
+ *
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+#define BUF_SIZE 80 /* 80 bytes, 160 samples */
+#define VOX_SAMPLES 160
+
+static struct ast_frame *vox_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+
+ /* Send a frame from the file to the appropriate channel */
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_ADPCM;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
+ if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ *whennext = s->fr.samples = res * 2;
+ s->fr.datalen = res;
+ return &s->fr;
+}
+
+static int vox_write(struct ast_filestream *s, struct ast_frame *f)
+{
+ int res;
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_ADPCM) {
+ ast_log(LOG_WARNING, "Asked to write non-ADPCM frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) {
+ ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int vox_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t offset=0,min,cur,max,distance;
+
+ min = 0;
+ cur = ftello(fs->f);
+ fseeko(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+
+ /* have to fudge to frame here, so not fully to sample */
+ distance = sample_offset/2;
+ if(whence == SEEK_SET)
+ offset = distance;
+ else if(whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = distance + cur;
+ else if(whence == SEEK_END)
+ offset = max - distance;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max)?max:offset;
+ offset = (offset < min)?min:offset;
+ }
+ return fseeko(fs->f, offset, SEEK_SET);
+}
+
+static int vox_trunc(struct ast_filestream *fs)
+{
+ return ftruncate(fileno(fs->f), ftello(fs->f));
+}
+
+static off_t vox_tell(struct ast_filestream *fs)
+{
+ off_t offset;
+ offset = ftello(fs->f) << 1;
+ return offset;
+}
+
+static const struct ast_format vox_f = {
+ .name = "vox",
+ .exts = "vox",
+ .format = AST_FORMAT_ADPCM,
+ .write = vox_write,
+ .seek = vox_seek,
+ .trunc = vox_trunc,
+ .tell = vox_tell,
+ .read = vox_read,
+ .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&vox_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(vox_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialogic VOX (ADPCM) File Format");
diff --git a/trunk/formats/format_wav.c b/trunk/formats/format_wav.c
new file mode 100644
index 000000000..2a40dedbd
--- /dev/null
+++ b/trunk/formats/format_wav.c
@@ -0,0 +1,491 @@
+/*
+ * 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 Work with WAV in the proprietary Microsoft format.
+ * Microsoft WAV format (8000hz Signed Linear)
+ * \arg File name extension: wav (lower case)
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+/* Some Ideas for this code came from makewave.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+
+#define WAV_BUF_SIZE 320
+
+struct wav_desc { /* format-specific parameters */
+ int bytes;
+ int lasttimeout;
+ int maxlen;
+ struct timeval last;
+};
+
+#define BLOCKSIZE 160
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htoll(b) (b)
+#define htols(b) (b)
+#define ltohl(b) (b)
+#define ltohs(b) (b)
+#else
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define htoll(b) \
+ (((((b) ) & 0xFF) << 24) | \
+ ((((b) >> 8) & 0xFF) << 16) | \
+ ((((b) >> 16) & 0xFF) << 8) | \
+ ((((b) >> 24) & 0xFF) ))
+#define htols(b) \
+ (((((b) ) & 0xFF) << 8) | \
+ ((((b) >> 8) & 0xFF) ))
+#define ltohl(b) htoll(b)
+#define ltohs(b) htols(b)
+#else
+#error "Endianess not defined"
+#endif
+#endif
+
+
+static int check_header(FILE *f)
+{
+ int type, size, formtype;
+ int fmt, hsize;
+ short format, chans, bysam, bisam;
+ int bysec;
+ int freq;
+ int data;
+ if (fread(&type, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (type)\n");
+ return -1;
+ }
+ if (fread(&size, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (size)\n");
+ return -1;
+ }
+ size = ltohl(size);
+ if (fread(&formtype, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (formtype)\n");
+ return -1;
+ }
+ if (memcmp(&type, "RIFF", 4)) {
+ ast_log(LOG_WARNING, "Does not begin with RIFF\n");
+ return -1;
+ }
+ if (memcmp(&formtype, "WAVE", 4)) {
+ ast_log(LOG_WARNING, "Does not contain WAVE\n");
+ return -1;
+ }
+ if (fread(&fmt, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (fmt)\n");
+ return -1;
+ }
+ if (memcmp(&fmt, "fmt ", 4)) {
+ ast_log(LOG_WARNING, "Does not say fmt\n");
+ return -1;
+ }
+ if (fread(&hsize, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (formtype)\n");
+ return -1;
+ }
+ if (ltohl(hsize) < 16) {
+ ast_log(LOG_WARNING, "Unexpected header size %d\n", ltohl(hsize));
+ return -1;
+ }
+ if (fread(&format, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Read failed (format)\n");
+ return -1;
+ }
+ if (ltohs(format) != 1) {
+ ast_log(LOG_WARNING, "Not a wav file %d\n", ltohs(format));
+ return -1;
+ }
+ if (fread(&chans, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Read failed (format)\n");
+ return -1;
+ }
+ if (ltohs(chans) != 1) {
+ ast_log(LOG_WARNING, "Not in mono %d\n", ltohs(chans));
+ return -1;
+ }
+ if (fread(&freq, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (freq)\n");
+ return -1;
+ }
+ if (ltohl(freq) != DEFAULT_SAMPLE_RATE) {
+ ast_log(LOG_WARNING, "Unexpected freqency %d\n", ltohl(freq));
+ return -1;
+ }
+ /* Ignore the byte frequency */
+ if (fread(&bysec, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (BYTES_PER_SECOND)\n");
+ return -1;
+ }
+ /* Check bytes per sample */
+ if (fread(&bysam, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Read failed (BYTES_PER_SAMPLE)\n");
+ return -1;
+ }
+ if (ltohs(bysam) != 2) {
+ ast_log(LOG_WARNING, "Can only handle 16bits per sample: %d\n", ltohs(bysam));
+ return -1;
+ }
+ if (fread(&bisam, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Read failed (Bits Per Sample): %d\n", ltohs(bisam));
+ return -1;
+ }
+ /* Skip any additional header */
+ if (fseek(f,ltohl(hsize)-16,SEEK_CUR) == -1 ) {
+ ast_log(LOG_WARNING, "Failed to skip remaining header bytes: %d\n", ltohl(hsize)-16 );
+ return -1;
+ }
+ /* Skip any facts and get the first data block */
+ for(;;)
+ {
+ char buf[4];
+
+ /* Begin data chunk */
+ if (fread(&buf, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (data)\n");
+ return -1;
+ }
+ /* Data has the actual length of data in it */
+ if (fread(&data, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (data)\n");
+ return -1;
+ }
+ data = ltohl(data);
+ if(memcmp(buf, "data", 4) == 0 )
+ break;
+ if(memcmp(buf, "fact", 4) != 0 ) {
+ ast_log(LOG_WARNING, "Unknown block - not fact or data\n");
+ return -1;
+ }
+ if (fseek(f,data,SEEK_CUR) == -1 ) {
+ ast_log(LOG_WARNING, "Failed to skip fact block: %d\n", data );
+ return -1;
+ }
+ }
+#if 0
+ curpos = lseek(fd, 0, SEEK_CUR);
+ truelength = lseek(fd, 0, SEEK_END);
+ lseek(fd, curpos, SEEK_SET);
+ truelength -= curpos;
+#endif
+ return data;
+}
+
+static int update_header(FILE *f)
+{
+ off_t cur,end;
+ int datalen,filelen,bytes;
+
+ cur = ftello(f);
+ fseek(f, 0, SEEK_END);
+ end = ftello(f);
+ /* data starts 44 bytes in */
+ bytes = end - 44;
+ datalen = htoll(bytes);
+ /* chunk size is bytes of data plus 36 bytes of header */
+ filelen = htoll(36 + bytes);
+
+ if (cur < 0) {
+ ast_log(LOG_WARNING, "Unable to find our position\n");
+ return -1;
+ }
+ if (fseek(f, 4, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to set our position\n");
+ return -1;
+ }
+ if (fwrite(&filelen, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to set write file size\n");
+ return -1;
+ }
+ if (fseek(f, 40, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to set our position\n");
+ return -1;
+ }
+ if (fwrite(&datalen, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to set write datalen\n");
+ return -1;
+ }
+ if (fseeko(f, cur, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to return to position\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int write_header(FILE *f)
+{
+ unsigned int hz=htoll(8000);
+ unsigned int bhz = htoll(16000);
+ unsigned int hs = htoll(16);
+ unsigned short fmt = htols(1);
+ unsigned short chans = htols(1);
+ unsigned short bysam = htols(2);
+ unsigned short bisam = htols(16);
+ unsigned int size = htoll(0);
+ /* Write a wav header, ignoring sizes which will be filled in later */
+ fseek(f,0,SEEK_SET);
+ if (fwrite("RIFF", 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&size, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite("WAVEfmt ", 1, 8, f) != 8) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&hs, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&fmt, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&chans, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&hz, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&bhz, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&bysam, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&bisam, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite("data", 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ if (fwrite(&size, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int wav_open(struct ast_filestream *s)
+{
+ /* We don't have any header to read or anything really, but
+ if we did, it would go here. We also might want to check
+ and be sure it's a valid file. */
+ struct wav_desc *tmp = (struct wav_desc *)s->_private;
+ if ((tmp->maxlen = check_header(s->f)) < 0)
+ return -1;
+ return 0;
+}
+
+static int wav_rewrite(struct ast_filestream *s, const char *comment)
+{
+ /* We don't have any header to read or anything really, but
+ if we did, it would go here. We also might want to check
+ and be sure it's a valid file. */
+
+ if (write_header(s->f))
+ return -1;
+ return 0;
+}
+
+static void wav_close(struct ast_filestream *s)
+{
+ char zero = 0;
+ struct wav_desc *fs = (struct wav_desc *)s->_private;
+ /* Pad to even length */
+ if (fs->bytes & 0x1)
+ fwrite(&zero, 1, 1, s->f);
+}
+
+static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
+{
+ int res;
+ int samples; /* actual samples read */
+#if __BYTE_ORDER == __BIG_ENDIAN
+ int x;
+#endif
+ short *tmp;
+ int bytes = WAV_BUF_SIZE; /* in bytes */
+ off_t here;
+ /* Send a frame from the file to the appropriate channel */
+ struct wav_desc *fs = (struct wav_desc *)s->_private;
+
+ here = ftello(s->f);
+ if (fs->maxlen - here < bytes) /* truncate if necessary */
+ bytes = fs->maxlen - here;
+ if (bytes < 0)
+ bytes = 0;
+/* ast_debug(1, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_SLINEAR;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, bytes);
+
+ if ( (res = fread(s->fr.data, 1, s->fr.datalen, s->f)) <= 0 ) {
+ if (res)
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ s->fr.datalen = res;
+ s->fr.samples = samples = res / 2;
+
+ tmp = (short *)(s->fr.data);
+#if __BYTE_ORDER == __BIG_ENDIAN
+ /* file format is little endian so we need to swap */
+ for( x = 0; x < samples; x++)
+ tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
+#endif
+
+ *whennext = samples;
+ return &s->fr;
+}
+
+static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+ int x;
+ short tmp[8000], *tmpi;
+#endif
+ struct wav_desc *s = (struct wav_desc *)fs->_private;
+ int res;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ if (!f->datalen)
+ return -1;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ /* swap and write */
+ if (f->datalen > sizeof(tmp)) {
+ ast_log(LOG_WARNING, "Data length is too long\n");
+ return -1;
+ }
+ tmpi = f->data;
+ for (x=0; x < f->datalen/2; x++)
+ tmp[x] = (tmpi[x] << 8) | ((tmpi[x] & 0xff00) >> 8);
+
+ if ((res = fwrite(tmp, 1, f->datalen, fs->f)) != f->datalen ) {
+#else
+ /* just write */
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen ) {
+#endif
+ ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno));
+ return -1;
+ }
+
+ s->bytes += f->datalen;
+ update_header(fs->f);
+
+ return 0;
+
+}
+
+static int wav_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t min, max, cur, offset = 0, samples;
+
+ samples = sample_offset * 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */
+ min = 44; /* wav header is 44 bytes */
+ cur = ftello(fs->f);
+ fseeko(fs->f, 0, SEEK_END);
+ max = ftello(fs->f);
+ if (whence == SEEK_SET)
+ offset = samples + min;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = samples + cur;
+ else if (whence == SEEK_END)
+ offset = max - samples;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max)?max:offset;
+ }
+ /* always protect the header space. */
+ offset = (offset < min)?min:offset;
+ return fseeko(fs->f, offset, SEEK_SET);
+}
+
+static int wav_trunc(struct ast_filestream *fs)
+{
+ if (ftruncate(fileno(fs->f), ftello(fs->f)))
+ return -1;
+ return update_header(fs->f);
+}
+
+static off_t wav_tell(struct ast_filestream *fs)
+{
+ off_t offset;
+ offset = ftello(fs->f);
+ /* subtract header size to get samples, then divide by 2 for 16 bit samples */
+ return (offset - 44)/2;
+}
+
+static const struct ast_format wav_f = {
+ .name = "wav",
+ .exts = "wav",
+ .format = AST_FORMAT_SLINEAR,
+ .open = wav_open,
+ .rewrite = wav_rewrite,
+ .write = wav_write,
+ .seek = wav_seek,
+ .trunc = wav_trunc,
+ .tell = wav_tell,
+ .read = wav_read,
+ .close = wav_close,
+ .buf_size = WAV_BUF_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct wav_desc),
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&wav_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(wav_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Microsoft WAV format (8000Hz Signed Linear)");
diff --git a/trunk/formats/format_wav_gsm.c b/trunk/formats/format_wav_gsm.c
new file mode 100644
index 000000000..cf40560de
--- /dev/null
+++ b/trunk/formats/format_wav_gsm.c
@@ -0,0 +1,559 @@
+/*
+ * 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 Save GSM in the proprietary Microsoft format.
+ *
+ * Microsoft WAV format (Proprietary GSM)
+ * \arg File name extension: WAV,wav49 (Upper case WAV, lower case is another format)
+ * This format can be played on Windows systems, used for
+ * e-mail attachments mainly.
+ * \ingroup formats
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/mod_format.h"
+#include "asterisk/module.h"
+#include "asterisk/endian.h"
+
+#include "msgsm.h"
+
+/* Some Ideas for this code came from makewave.c by Jeffrey Chilton */
+
+/* Portions of the conversion code are by guido@sienanet.it */
+
+#define GSM_FRAME_SIZE 33
+#define MSGSM_FRAME_SIZE 65
+#define MSGSM_DATA_OFFSET 60 /* offset of data bytes */
+#define GSM_SAMPLES 160 /* samples in a GSM block */
+#define MSGSM_SAMPLES (2*GSM_SAMPLES) /* samples in an MSGSM block */
+
+/* begin binary data: */
+char msgsm_silence[] = /* 65 */
+{0x48,0x17,0xD6,0x84,0x02,0x80,0x24,0x49,0x92,0x24,0x89,0x02,0x80,0x24,0x49
+,0x92,0x24,0x89,0x02,0x80,0x24,0x49,0x92,0x24,0x89,0x02,0x80,0x24,0x49,0x92
+,0x24,0x09,0x82,0x74,0x61,0x4D,0x28,0x00,0x48,0x92,0x24,0x49,0x92,0x28,0x00
+,0x48,0x92,0x24,0x49,0x92,0x28,0x00,0x48,0x92,0x24,0x49,0x92,0x28,0x00,0x48
+,0x92,0x24,0x49,0x92,0x00};
+/* end binary data. size = 65 bytes */
+
+struct wavg_desc {
+ /* Believe it or not, we must decode/recode to account for the
+ weird MS format */
+ int secondhalf; /* Are we on the second half */
+};
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htoll(b) (b)
+#define htols(b) (b)
+#define ltohl(b) (b)
+#define ltohs(b) (b)
+#else
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define htoll(b) \
+ (((((b) ) & 0xFF) << 24) | \
+ ((((b) >> 8) & 0xFF) << 16) | \
+ ((((b) >> 16) & 0xFF) << 8) | \
+ ((((b) >> 24) & 0xFF) ))
+#define htols(b) \
+ (((((b) ) & 0xFF) << 8) | \
+ ((((b) >> 8) & 0xFF) ))
+#define ltohl(b) htoll(b)
+#define ltohs(b) htols(b)
+#else
+#error "Endianess not defined"
+#endif
+#endif
+
+
+static int check_header(FILE *f)
+{
+ int type, size, formtype;
+ int fmt, hsize, fact;
+ short format, chans;
+ int freq;
+ int data;
+ if (fread(&type, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (type)\n");
+ return -1;
+ }
+ if (fread(&size, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (size)\n");
+ return -1;
+ }
+ size = ltohl(size);
+ if (fread(&formtype, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (formtype)\n");
+ return -1;
+ }
+ if (memcmp(&type, "RIFF", 4)) {
+ ast_log(LOG_WARNING, "Does not begin with RIFF\n");
+ return -1;
+ }
+ if (memcmp(&formtype, "WAVE", 4)) {
+ ast_log(LOG_WARNING, "Does not contain WAVE\n");
+ return -1;
+ }
+ if (fread(&fmt, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (fmt)\n");
+ return -1;
+ }
+ if (memcmp(&fmt, "fmt ", 4)) {
+ ast_log(LOG_WARNING, "Does not say fmt\n");
+ return -1;
+ }
+ if (fread(&hsize, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (formtype)\n");
+ return -1;
+ }
+ if (ltohl(hsize) != 20) {
+ ast_log(LOG_WARNING, "Unexpected header size %d\n", ltohl(hsize));
+ return -1;
+ }
+ if (fread(&format, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Read failed (format)\n");
+ return -1;
+ }
+ if (ltohs(format) != 49) {
+ ast_log(LOG_WARNING, "Not a GSM file %d\n", ltohs(format));
+ return -1;
+ }
+ if (fread(&chans, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Read failed (format)\n");
+ return -1;
+ }
+ if (ltohs(chans) != 1) {
+ ast_log(LOG_WARNING, "Not in mono %d\n", ltohs(chans));
+ return -1;
+ }
+ if (fread(&freq, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (freq)\n");
+ return -1;
+ }
+ if (ltohl(freq) != DEFAULT_SAMPLE_RATE) {
+ ast_log(LOG_WARNING, "Unexpected freqency %d\n", ltohl(freq));
+ return -1;
+ }
+ /* Ignore the byte frequency */
+ if (fread(&freq, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (X_1)\n");
+ return -1;
+ }
+ /* Ignore the two weird fields */
+ if (fread(&freq, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (X_2/X_3)\n");
+ return -1;
+ }
+ /* Ignore the byte frequency */
+ if (fread(&freq, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (Y_1)\n");
+ return -1;
+ }
+ /* Check for the word fact */
+ if (fread(&fact, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (fact)\n");
+ return -1;
+ }
+ if (memcmp(&fact, "fact", 4)) {
+ ast_log(LOG_WARNING, "Does not say fact\n");
+ return -1;
+ }
+ /* Ignore the "fact value" */
+ if (fread(&fact, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (fact header)\n");
+ return -1;
+ }
+ if (fread(&fact, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (fact value)\n");
+ return -1;
+ }
+ /* Check for the word data */
+ if (fread(&data, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (data)\n");
+ return -1;
+ }
+ if (memcmp(&data, "data", 4)) {
+ ast_log(LOG_WARNING, "Does not say data\n");
+ return -1;
+ }
+ /* Ignore the data length */
+ if (fread(&data, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Read failed (data)\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int update_header(FILE *f)
+{
+ off_t cur,end,bytes;
+ int datalen, filelen, samples;
+
+ cur = ftello(f);
+ fseek(f, 0, SEEK_END);
+ end = ftello(f);
+ /* in a gsm WAV, data starts 60 bytes in */
+ bytes = end - MSGSM_DATA_OFFSET;
+ samples = htoll(bytes / MSGSM_FRAME_SIZE * MSGSM_SAMPLES);
+ datalen = htoll((bytes + 1) & ~0x1);
+ filelen = htoll(MSGSM_DATA_OFFSET - 8 + ((bytes + 1) & ~0x1));
+ if (cur < 0) {
+ ast_log(LOG_WARNING, "Unable to find our position\n");
+ return -1;
+ }
+ if (fseek(f, 4, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to set our position\n");
+ return -1;
+ }
+ if (fwrite(&filelen, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write file size\n");
+ return -1;
+ }
+ if (fseek(f, 48, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to set our position\n");
+ return -1;
+ }
+ if (fwrite(&samples, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write samples\n");
+ return -1;
+ }
+ if (fseek(f, 56, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to set our position\n");
+ return -1;
+ }
+ if (fwrite(&datalen, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write datalen\n");
+ return -1;
+ }
+ if (fseeko(f, cur, SEEK_SET)) {
+ ast_log(LOG_WARNING, "Unable to return to position\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int write_header(FILE *f)
+{
+ /* Samples per second (always 8000 for this format). */
+ unsigned int sample_rate = htoll(8000);
+ /* Bytes per second (always 1625 for this format). */
+ unsigned int byte_sample_rate = htoll(1625);
+ /* This is the size of the "fmt " subchunk */
+ unsigned int fmtsize = htoll(20);
+ /* WAV #49 */
+ unsigned short fmt = htols(49);
+ /* Mono = 1 channel */
+ unsigned short chans = htols(1);
+ /* Each block of data is exactly 65 bytes in size. */
+ unsigned int block_align = htoll(MSGSM_FRAME_SIZE);
+ /* Not actually 2, but rounded up to the nearest bit */
+ unsigned short bits_per_sample = htols(2);
+ /* Needed for compressed formats */
+ unsigned short extra_format = htols(MSGSM_SAMPLES);
+ /* This is the size of the "fact" subchunk */
+ unsigned int factsize = htoll(4);
+ /* Number of samples in the data chunk */
+ unsigned int num_samples = htoll(0);
+ /* Number of bytes in the data chunk */
+ unsigned int size = htoll(0);
+ /* Write a GSM header, ignoring sizes which will be filled in later */
+
+ /* 0: Chunk ID */
+ if (fwrite("RIFF", 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 4: Chunk Size */
+ if (fwrite(&size, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 8: Chunk Format */
+ if (fwrite("WAVE", 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 12: Subchunk 1: ID */
+ if (fwrite("fmt ", 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 16: Subchunk 1: Size (minus 8) */
+ if (fwrite(&fmtsize, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 20: Subchunk 1: Audio format (49) */
+ if (fwrite(&fmt, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 22: Subchunk 1: Number of channels */
+ if (fwrite(&chans, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 24: Subchunk 1: Sample rate */
+ if (fwrite(&sample_rate, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 28: Subchunk 1: Byte rate */
+ if (fwrite(&byte_sample_rate, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 32: Subchunk 1: Block align */
+ if (fwrite(&block_align, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 36: Subchunk 1: Bits per sample */
+ if (fwrite(&bits_per_sample, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 38: Subchunk 1: Extra format bytes */
+ if (fwrite(&extra_format, 1, 2, f) != 2) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 40: Subchunk 2: ID */
+ if (fwrite("fact", 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 44: Subchunk 2: Size (minus 8) */
+ if (fwrite(&factsize, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 48: Subchunk 2: Number of samples */
+ if (fwrite(&num_samples, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 52: Subchunk 3: ID */
+ if (fwrite("data", 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ /* 56: Subchunk 3: Size */
+ if (fwrite(&size, 1, 4, f) != 4) {
+ ast_log(LOG_WARNING, "Unable to write header\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int wav_open(struct ast_filestream *s)
+{
+ /* We don't have any header to read or anything really, but
+ if we did, it would go here. We also might want to check
+ and be sure it's a valid file. */
+ struct wavg_desc *fs = (struct wavg_desc *)s->_private;
+
+ if (check_header(s->f))
+ return -1;
+ fs->secondhalf = 0; /* not strictly necessary */
+ return 0;
+}
+
+static int wav_rewrite(struct ast_filestream *s, const char *comment)
+{
+ /* We don't have any header to read or anything really, but
+ if we did, it would go here. We also might want to check
+ and be sure it's a valid file. */
+
+ if (write_header(s->f))
+ return -1;
+ return 0;
+}
+
+static void wav_close(struct ast_filestream *s)
+{
+ char zero = 0;
+ /* Pad to even length */
+ fseek(s->f, 0, SEEK_END);
+ if (ftello(s->f) & 0x1)
+ fwrite(&zero, 1, 1, s->f);
+}
+
+static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
+{
+ /* Send a frame from the file to the appropriate channel */
+ struct wavg_desc *fs = (struct wavg_desc *)s->_private;
+
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_GSM;
+ s->fr.offset = AST_FRIENDLY_OFFSET;
+ s->fr.samples = GSM_SAMPLES;
+ s->fr.mallocd = 0;
+ AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, GSM_FRAME_SIZE);
+ if (fs->secondhalf) {
+ /* Just return a frame based on the second GSM frame */
+ s->fr.data = (char *)s->fr.data + GSM_FRAME_SIZE;
+ s->fr.offset += GSM_FRAME_SIZE;
+ } else {
+ /* read and convert */
+ unsigned char msdata[MSGSM_FRAME_SIZE];
+ int res;
+
+ if ((res = fread(msdata, 1, MSGSM_FRAME_SIZE, s->f)) != MSGSM_FRAME_SIZE) {
+ if (res && (res != 1))
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
+ return NULL;
+ }
+ /* Convert from MS format to two real GSM frames */
+ conv65(msdata, s->fr.data);
+ }
+ fs->secondhalf = !fs->secondhalf;
+ *whennext = GSM_SAMPLES;
+ return &s->fr;
+}
+
+static int wav_write(struct ast_filestream *s, struct ast_frame *f)
+{
+ int len;
+ int size;
+ struct wavg_desc *fs = (struct wavg_desc *)s->_private;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
+ return -1;
+ }
+ if (f->subclass != AST_FORMAT_GSM) {
+ ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
+ return -1;
+ }
+ /* XXX this might fail... if the input is a multiple of MSGSM_FRAME_SIZE
+ * we assume it is already in the correct format.
+ */
+ if (!(f->datalen % MSGSM_FRAME_SIZE)) {
+ size = MSGSM_FRAME_SIZE;
+ fs->secondhalf = 0;
+ } else {
+ size = GSM_FRAME_SIZE;
+ }
+ for (len = 0; len < f->datalen ; len += size) {
+ int res;
+ unsigned char *src, msdata[MSGSM_FRAME_SIZE];
+ if (fs->secondhalf) { /* second half of raw gsm to be converted */
+ memcpy(s->buf + GSM_FRAME_SIZE, f->data + len, GSM_FRAME_SIZE);
+ conv66((unsigned char *) s->buf, msdata);
+ src = msdata;
+ fs->secondhalf = 0;
+ } else if (size == GSM_FRAME_SIZE) { /* first half of raw gsm */
+ memcpy(s->buf, f->data + len, GSM_FRAME_SIZE);
+ src = NULL; /* nothing to write */
+ fs->secondhalf = 1;
+ } else { /* raw msgsm data */
+ src = f->data + len;
+ }
+ if (src && (res = fwrite(src, 1, MSGSM_FRAME_SIZE, s->f)) != MSGSM_FRAME_SIZE) {
+ ast_log(LOG_WARNING, "Bad write (%d/65): %s\n", res, strerror(errno));
+ return -1;
+ }
+ update_header(s->f); /* XXX inefficient! */
+ }
+ return 0;
+}
+
+static int wav_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ off_t offset=0, distance, max;
+ struct wavg_desc *s = (struct wavg_desc *)fs->_private;
+
+ off_t min = MSGSM_DATA_OFFSET;
+ off_t cur = ftello(fs->f);
+ fseek(fs->f, 0, SEEK_END);
+ max = ftello(fs->f); /* XXX ideally, should round correctly */
+ /* Compute the distance in bytes, rounded to the block size */
+ distance = (sample_offset/MSGSM_SAMPLES) * MSGSM_FRAME_SIZE;
+ if (whence == SEEK_SET)
+ offset = distance + min;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = distance + cur;
+ else if (whence == SEEK_END)
+ offset = max - distance;
+ /* always protect against seeking past end of header */
+ if (offset < min)
+ offset = min;
+ if (whence != SEEK_FORCECUR) {
+ if (offset > max)
+ offset = max;
+ } else if (offset > max) {
+ int i;
+ fseek(fs->f, 0, SEEK_END);
+ for (i=0; i< (offset - max) / MSGSM_FRAME_SIZE; i++) {
+ fwrite(msgsm_silence, 1, MSGSM_FRAME_SIZE, fs->f);
+ }
+ }
+ s->secondhalf = 0;
+ return fseeko(fs->f, offset, SEEK_SET);
+}
+
+static int wav_trunc(struct ast_filestream *fs)
+{
+ if (ftruncate(fileno(fs->f), ftello(fs->f)))
+ return -1;
+ return update_header(fs->f);
+}
+
+static off_t wav_tell(struct ast_filestream *fs)
+{
+ off_t offset;
+ offset = ftello(fs->f);
+ /* since this will most likely be used later in play or record, lets stick
+ * to that level of resolution, just even frames boundaries */
+ return (offset - MSGSM_DATA_OFFSET)/MSGSM_FRAME_SIZE*MSGSM_SAMPLES;
+}
+
+static const struct ast_format wav49_f = {
+ .name = "wav49",
+ .exts = "WAV|wav49",
+ .format = AST_FORMAT_GSM,
+ .open = wav_open,
+ .rewrite = wav_rewrite,
+ .write = wav_write,
+ .seek = wav_seek,
+ .trunc = wav_trunc,
+ .tell = wav_tell,
+ .read = wav_read,
+ .close = wav_close,
+ .buf_size = 2*GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct wavg_desc),
+};
+
+static int load_module(void)
+{
+ if (ast_format_register(&wav49_f))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(wav49_f.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Microsoft WAV format (Proprietary GSM)");
diff --git a/trunk/formats/msgsm.h b/trunk/formats/msgsm.h
new file mode 100644
index 000000000..f951cc271
--- /dev/null
+++ b/trunk/formats/msgsm.h
@@ -0,0 +1,689 @@
+/* Conversion routines derived from code by guido@sienanet.it */
+
+#define GSM_MAGIC 0xD
+
+#ifndef GSM_H
+typedef unsigned char gsm_byte;
+#endif
+typedef unsigned char wav_byte;
+typedef unsigned int uword;
+
+#define readGSM_33(c1) { \
+ gsm_byte *c = (c1); \
+ LARc[0] = (*c++ & 0xF) << 2; /* 1 */ \
+ LARc[0] |= (*c >> 6) & 0x3; \
+ LARc[1] = *c++ & 0x3F; \
+ LARc[2] = (*c >> 3) & 0x1F; \
+ LARc[3] = (*c++ & 0x7) << 2; \
+ LARc[3] |= (*c >> 6) & 0x3; \
+ LARc[4] = (*c >> 2) & 0xF; \
+ LARc[5] = (*c++ & 0x3) << 2; \
+ LARc[5] |= (*c >> 6) & 0x3; \
+ LARc[6] = (*c >> 3) & 0x7; \
+ LARc[7] = *c++ & 0x7; \
+ Nc[0] = (*c >> 1) & 0x7F; \
+ bc[0] = (*c++ & 0x1) << 1; \
+ bc[0] |= (*c >> 7) & 0x1; \
+ Mc[0] = (*c >> 5) & 0x3; \
+ xmaxc[0] = (*c++ & 0x1F) << 1; \
+ xmaxc[0] |= (*c >> 7) & 0x1; \
+ xmc[0] = (*c >> 4) & 0x7; \
+ xmc[1] = (*c >> 1) & 0x7; \
+ xmc[2] = (*c++ & 0x1) << 2; \
+ xmc[2] |= (*c >> 6) & 0x3; \
+ xmc[3] = (*c >> 3) & 0x7; \
+ xmc[4] = *c++ & 0x7; \
+ xmc[5] = (*c >> 5) & 0x7; \
+ xmc[6] = (*c >> 2) & 0x7; \
+ xmc[7] = (*c++ & 0x3) << 1; /* 10 */ \
+ xmc[7] |= (*c >> 7) & 0x1; \
+ xmc[8] = (*c >> 4) & 0x7; \
+ xmc[9] = (*c >> 1) & 0x7; \
+ xmc[10] = (*c++ & 0x1) << 2; \
+ xmc[10] |= (*c >> 6) & 0x3; \
+ xmc[11] = (*c >> 3) & 0x7; \
+ xmc[12] = *c++ & 0x7; \
+ Nc[1] = (*c >> 1) & 0x7F; \
+ bc[1] = (*c++ & 0x1) << 1; \
+ bc[1] |= (*c >> 7) & 0x1; \
+ Mc[1] = (*c >> 5) & 0x3; \
+ xmaxc[1] = (*c++ & 0x1F) << 1; \
+ xmaxc[1] |= (*c >> 7) & 0x1; \
+ xmc[13] = (*c >> 4) & 0x7; \
+ xmc[14] = (*c >> 1) & 0x7; \
+ xmc[15] = (*c++ & 0x1) << 2; \
+ xmc[15] |= (*c >> 6) & 0x3; \
+ xmc[16] = (*c >> 3) & 0x7; \
+ xmc[17] = *c++ & 0x7; \
+ xmc[18] = (*c >> 5) & 0x7; \
+ xmc[19] = (*c >> 2) & 0x7; \
+ xmc[20] = (*c++ & 0x3) << 1; \
+ xmc[20] |= (*c >> 7) & 0x1; \
+ xmc[21] = (*c >> 4) & 0x7; \
+ xmc[22] = (*c >> 1) & 0x7; \
+ xmc[23] = (*c++ & 0x1) << 2; \
+ xmc[23] |= (*c >> 6) & 0x3; \
+ xmc[24] = (*c >> 3) & 0x7; \
+ xmc[25] = *c++ & 0x7; \
+ Nc[2] = (*c >> 1) & 0x7F; \
+ bc[2] = (*c++ & 0x1) << 1; /* 20 */ \
+ bc[2] |= (*c >> 7) & 0x1; \
+ Mc[2] = (*c >> 5) & 0x3; \
+ xmaxc[2] = (*c++ & 0x1F) << 1; \
+ xmaxc[2] |= (*c >> 7) & 0x1; \
+ xmc[26] = (*c >> 4) & 0x7; \
+ xmc[27] = (*c >> 1) & 0x7; \
+ xmc[28] = (*c++ & 0x1) << 2; \
+ xmc[28] |= (*c >> 6) & 0x3; \
+ xmc[29] = (*c >> 3) & 0x7; \
+ xmc[30] = *c++ & 0x7; \
+ xmc[31] = (*c >> 5) & 0x7; \
+ xmc[32] = (*c >> 2) & 0x7; \
+ xmc[33] = (*c++ & 0x3) << 1; \
+ xmc[33] |= (*c >> 7) & 0x1; \
+ xmc[34] = (*c >> 4) & 0x7; \
+ xmc[35] = (*c >> 1) & 0x7; \
+ xmc[36] = (*c++ & 0x1) << 2; \
+ xmc[36] |= (*c >> 6) & 0x3; \
+ xmc[37] = (*c >> 3) & 0x7; \
+ xmc[38] = *c++ & 0x7; \
+ Nc[3] = (*c >> 1) & 0x7F; \
+ bc[3] = (*c++ & 0x1) << 1; \
+ bc[3] |= (*c >> 7) & 0x1; \
+ Mc[3] = (*c >> 5) & 0x3; \
+ xmaxc[3] = (*c++ & 0x1F) << 1; \
+ xmaxc[3] |= (*c >> 7) & 0x1; \
+ xmc[39] = (*c >> 4) & 0x7; \
+ xmc[40] = (*c >> 1) & 0x7; \
+ xmc[41] = (*c++ & 0x1) << 2; \
+ xmc[41] |= (*c >> 6) & 0x3; \
+ xmc[42] = (*c >> 3) & 0x7; \
+ xmc[43] = *c++ & 0x7; /* 30 */ \
+ xmc[44] = (*c >> 5) & 0x7; \
+ xmc[45] = (*c >> 2) & 0x7; \
+ xmc[46] = (*c++ & 0x3) << 1; \
+ xmc[46] |= (*c >> 7) & 0x1; \
+ xmc[47] = (*c >> 4) & 0x7; \
+ xmc[48] = (*c >> 1) & 0x7; \
+ xmc[49] = (*c++ & 0x1) << 2; \
+ xmc[49] |= (*c >> 6) & 0x3; \
+ xmc[50] = (*c >> 3) & 0x7; \
+ xmc[51] = *c & 0x7; /* 33 */ \
+}
+
+static inline void conv66(gsm_byte * d, wav_byte * c) {
+ gsm_byte frame_chain;
+ unsigned int sr;
+ unsigned int LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+ readGSM_33(d);
+ sr = 0;
+ sr = (sr >> 6) | (LARc[0] << 10);
+ sr = (sr >> 6) | (LARc[1] << 10);
+ *c++ = sr >> 4;
+ sr = (sr >> 5) | (LARc[2] << 11);
+ *c++ = sr >> 7;
+ sr = (sr >> 5) | (LARc[3] << 11);
+ sr = (sr >> 4) | (LARc[4] << 12);
+ *c++ = sr >> 6;
+ sr = (sr >> 4) | (LARc[5] << 12);
+ sr = (sr >> 3) | (LARc[6] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3) | (LARc[7] << 13);
+ sr = (sr >> 7) | (Nc[0] << 9);
+ *c++ = sr >> 5;
+ sr = (sr >> 2) | (bc[0] << 14);
+ sr = (sr >> 2) | (Mc[0] << 14);
+ sr = (sr >> 6) | (xmaxc[0] << 10);
+ *c++ = sr >> 3;
+ sr = (sr >> 3 )|( xmc[0] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[1] << 13);
+ sr = (sr >> 3 )|( xmc[2] << 13);
+ sr = (sr >> 3 )|( xmc[3] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[4] << 13);
+ sr = (sr >> 3 )|( xmc[5] << 13);
+ sr = (sr >> 3 )|( xmc[6] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[7] << 13);
+ sr = (sr >> 3 )|( xmc[8] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[9] << 13);
+ sr = (sr >> 3 )|( xmc[10] << 13);
+ sr = (sr >> 3 )|( xmc[11] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[12] << 13);
+ sr = (sr >> 7 )|( Nc[1] << 9);
+ *c++ = sr >> 5;
+ sr = (sr >> 2 )|( bc[1] << 14);
+ sr = (sr >> 2 )|( Mc[1] << 14);
+ sr = (sr >> 6 )|( xmaxc[1] << 10);
+ *c++ = sr >> 3;
+ sr = (sr >> 3 )|( xmc[13] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[14] << 13);
+ sr = (sr >> 3 )|( xmc[15] << 13);
+ sr = (sr >> 3 )|( xmc[16] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[17] << 13);
+ sr = (sr >> 3 )|( xmc[18] << 13);
+ sr = (sr >> 3 )|( xmc[19] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[20] << 13);
+ sr = (sr >> 3 )|( xmc[21] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[22] << 13);
+ sr = (sr >> 3 )|( xmc[23] << 13);
+ sr = (sr >> 3 )|( xmc[24] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[25] << 13);
+ sr = (sr >> 7 )|( Nc[2] << 9);
+ *c++ = sr >> 5;
+ sr = (sr >> 2 )|( bc[2] << 14);
+ sr = (sr >> 2 )|( Mc[2] << 14);
+ sr = (sr >> 6 )|( xmaxc[2] << 10);
+ *c++ = sr >> 3;
+ sr = (sr >> 3 )|( xmc[26] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[27] << 13);
+ sr = (sr >> 3 )|( xmc[28] << 13);
+ sr = (sr >> 3 )|( xmc[29] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[30] << 13);
+ sr = (sr >> 3 )|( xmc[31] << 13);
+ sr = (sr >> 3 )|( xmc[32] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[33] << 13);
+ sr = (sr >> 3 )|( xmc[34] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[35] << 13);
+ sr = (sr >> 3 )|( xmc[36] << 13);
+ sr = (sr >> 3 )|( xmc[37] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[38] << 13);
+ sr = (sr >> 7 )|( Nc[3] << 9);
+ *c++ = sr >> 5;
+ sr = (sr >> 2 )|( bc[3] << 14);
+ sr = (sr >> 2 )|( Mc[3] << 14);
+ sr = (sr >> 6 )|( xmaxc[3] << 10);
+ *c++ = sr >> 3;
+ sr = (sr >> 3 )|( xmc[39] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[40] << 13);
+ sr = (sr >> 3 )|( xmc[41] << 13);
+ sr = (sr >> 3 )|( xmc[42] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[43] << 13);
+ sr = (sr >> 3 )|( xmc[44] << 13);
+ sr = (sr >> 3 )|( xmc[45] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[46] << 13);
+ sr = (sr >> 3 )|( xmc[47] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[48] << 13);
+ sr = (sr >> 3 )|( xmc[49] << 13);
+ sr = (sr >> 3 )|( xmc[50] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[51] << 13);
+ sr = sr >> 4;
+ *c = sr >> 8;
+ frame_chain = *c;
+ readGSM_33(d+33); /* puts all the parameters into LARc etc. */
+
+
+ sr = 0;
+/* sr = (sr >> 4 )|( s->frame_chain << 12); */
+ sr = (sr >> 4 )|( frame_chain << 12);
+
+ sr = (sr >> 6 )|( LARc[0] << 10);
+ *c++ = sr >> 6;
+ sr = (sr >> 6 )|( LARc[1] << 10);
+ *c++ = sr >> 8;
+ sr = (sr >> 5 )|( LARc[2] << 11);
+ sr = (sr >> 5 )|( LARc[3] << 11);
+ *c++ = sr >> 6;
+ sr = (sr >> 4 )|( LARc[4] << 12);
+ sr = (sr >> 4 )|( LARc[5] << 12);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( LARc[6] << 13);
+ sr = (sr >> 3 )|( LARc[7] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 7 )|( Nc[0] << 9);
+ sr = (sr >> 2 )|( bc[0] << 14);
+ *c++ = sr >> 7;
+ sr = (sr >> 2 )|( Mc[0] << 14);
+ sr = (sr >> 6 )|( xmaxc[0] << 10);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[0] << 13);
+ sr = (sr >> 3 )|( xmc[1] << 13);
+ sr = (sr >> 3 )|( xmc[2] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[3] << 13);
+ sr = (sr >> 3 )|( xmc[4] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[5] << 13);
+ sr = (sr >> 3 )|( xmc[6] << 13);
+ sr = (sr >> 3 )|( xmc[7] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[8] << 13);
+ sr = (sr >> 3 )|( xmc[9] << 13);
+ sr = (sr >> 3 )|( xmc[10] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[11] << 13);
+ sr = (sr >> 3 )|( xmc[12] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 7 )|( Nc[1] << 9);
+ sr = (sr >> 2 )|( bc[1] << 14);
+ *c++ = sr >> 7;
+ sr = (sr >> 2 )|( Mc[1] << 14);
+ sr = (sr >> 6 )|( xmaxc[1] << 10);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[13] << 13);
+ sr = (sr >> 3 )|( xmc[14] << 13);
+ sr = (sr >> 3 )|( xmc[15] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[16] << 13);
+ sr = (sr >> 3 )|( xmc[17] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[18] << 13);
+ sr = (sr >> 3 )|( xmc[19] << 13);
+ sr = (sr >> 3 )|( xmc[20] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[21] << 13);
+ sr = (sr >> 3 )|( xmc[22] << 13);
+ sr = (sr >> 3 )|( xmc[23] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[24] << 13);
+ sr = (sr >> 3 )|( xmc[25] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 7 )|( Nc[2] << 9);
+ sr = (sr >> 2 )|( bc[2] << 14);
+ *c++ = sr >> 7;
+ sr = (sr >> 2 )|( Mc[2] << 14);
+ sr = (sr >> 6 )|( xmaxc[2] << 10);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[26] << 13);
+ sr = (sr >> 3 )|( xmc[27] << 13);
+ sr = (sr >> 3 )|( xmc[28] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[29] << 13);
+ sr = (sr >> 3 )|( xmc[30] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[31] << 13);
+ sr = (sr >> 3 )|( xmc[32] << 13);
+ sr = (sr >> 3 )|( xmc[33] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[34] << 13);
+ sr = (sr >> 3 )|( xmc[35] << 13);
+ sr = (sr >> 3 )|( xmc[36] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[37] << 13);
+ sr = (sr >> 3 )|( xmc[38] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 7 )|( Nc[3] << 9);
+ sr = (sr >> 2 )|( bc[3] << 14);
+ *c++ = sr >> 7;
+ sr = (sr >> 2 )|( Mc[3] << 14);
+ sr = (sr >> 6 )|( xmaxc[3] << 10);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[39] << 13);
+ sr = (sr >> 3 )|( xmc[40] << 13);
+ sr = (sr >> 3 )|( xmc[41] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[42] << 13);
+ sr = (sr >> 3 )|( xmc[43] << 13);
+ *c++ = sr >> 8;
+ sr = (sr >> 3 )|( xmc[44] << 13);
+ sr = (sr >> 3 )|( xmc[45] << 13);
+ sr = (sr >> 3 )|( xmc[46] << 13);
+ *c++ = sr >> 7;
+ sr = (sr >> 3 )|( xmc[47] << 13);
+ sr = (sr >> 3 )|( xmc[48] << 13);
+ sr = (sr >> 3 )|( xmc[49] << 13);
+ *c++ = sr >> 6;
+ sr = (sr >> 3 )|( xmc[50] << 13);
+ sr = (sr >> 3 )|( xmc[51] << 13);
+ *c++ = sr >> 8;
+
+}
+
+#define writeGSM_33(c1) { \
+ gsm_byte *c = (c1); \
+ *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */ \
+ | ((LARc[0] >> 2) & 0xF); \
+ *c++ = ((LARc[0] & 0x3) << 6) \
+ | (LARc[1] & 0x3F); \
+ *c++ = ((LARc[2] & 0x1F) << 3) \
+ | ((LARc[3] >> 2) & 0x7); \
+ *c++ = ((LARc[3] & 0x3) << 6) \
+ | ((LARc[4] & 0xF) << 2) \
+ | ((LARc[5] >> 2) & 0x3); \
+ *c++ = ((LARc[5] & 0x3) << 6) \
+ | ((LARc[6] & 0x7) << 3) \
+ | (LARc[7] & 0x7); \
+ *c++ = ((Nc[0] & 0x7F) << 1) \
+ | ((bc[0] >> 1) & 0x1); \
+ *c++ = ((bc[0] & 0x1) << 7) \
+ | ((Mc[0] & 0x3) << 5) \
+ | ((xmaxc[0] >> 1) & 0x1F); \
+ *c++ = ((xmaxc[0] & 0x1) << 7) \
+ | ((xmc[0] & 0x7) << 4) \
+ | ((xmc[1] & 0x7) << 1) \
+ | ((xmc[2] >> 2) & 0x1); \
+ *c++ = ((xmc[2] & 0x3) << 6) \
+ | ((xmc[3] & 0x7) << 3) \
+ | (xmc[4] & 0x7); \
+ *c++ = ((xmc[5] & 0x7) << 5) /* 10 */ \
+ | ((xmc[6] & 0x7) << 2) \
+ | ((xmc[7] >> 1) & 0x3); \
+ *c++ = ((xmc[7] & 0x1) << 7) \
+ | ((xmc[8] & 0x7) << 4) \
+ | ((xmc[9] & 0x7) << 1) \
+ | ((xmc[10] >> 2) & 0x1); \
+ *c++ = ((xmc[10] & 0x3) << 6) \
+ | ((xmc[11] & 0x7) << 3) \
+ | (xmc[12] & 0x7); \
+ *c++ = ((Nc[1] & 0x7F) << 1) \
+ | ((bc[1] >> 1) & 0x1); \
+ *c++ = ((bc[1] & 0x1) << 7) \
+ | ((Mc[1] & 0x3) << 5) \
+ | ((xmaxc[1] >> 1) & 0x1F); \
+ *c++ = ((xmaxc[1] & 0x1) << 7) \
+ | ((xmc[13] & 0x7) << 4) \
+ | ((xmc[14] & 0x7) << 1) \
+ | ((xmc[15] >> 2) & 0x1); \
+ *c++ = ((xmc[15] & 0x3) << 6) \
+ | ((xmc[16] & 0x7) << 3) \
+ | (xmc[17] & 0x7); \
+ *c++ = ((xmc[18] & 0x7) << 5) \
+ | ((xmc[19] & 0x7) << 2) \
+ | ((xmc[20] >> 1) & 0x3); \
+ *c++ = ((xmc[20] & 0x1) << 7) \
+ | ((xmc[21] & 0x7) << 4) \
+ | ((xmc[22] & 0x7) << 1) \
+ | ((xmc[23] >> 2) & 0x1); \
+ *c++ = ((xmc[23] & 0x3) << 6) \
+ | ((xmc[24] & 0x7) << 3) \
+ | (xmc[25] & 0x7); \
+ *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */ \
+ | ((bc[2] >> 1) & 0x1); \
+ *c++ = ((bc[2] & 0x1) << 7) \
+ | ((Mc[2] & 0x3) << 5) \
+ | ((xmaxc[2] >> 1) & 0x1F); \
+ *c++ = ((xmaxc[2] & 0x1) << 7) \
+ | ((xmc[26] & 0x7) << 4) \
+ | ((xmc[27] & 0x7) << 1) \
+ | ((xmc[28] >> 2) & 0x1); \
+ *c++ = ((xmc[28] & 0x3) << 6) \
+ | ((xmc[29] & 0x7) << 3) \
+ | (xmc[30] & 0x7); \
+ *c++ = ((xmc[31] & 0x7) << 5) \
+ | ((xmc[32] & 0x7) << 2) \
+ | ((xmc[33] >> 1) & 0x3); \
+ *c++ = ((xmc[33] & 0x1) << 7) \
+ | ((xmc[34] & 0x7) << 4) \
+ | ((xmc[35] & 0x7) << 1) \
+ | ((xmc[36] >> 2) & 0x1); \
+ *c++ = ((xmc[36] & 0x3) << 6) \
+ | ((xmc[37] & 0x7) << 3) \
+ | (xmc[38] & 0x7); \
+ *c++ = ((Nc[3] & 0x7F) << 1) \
+ | ((bc[3] >> 1) & 0x1); \
+ *c++ = ((bc[3] & 0x1) << 7) \
+ | ((Mc[3] & 0x3) << 5) \
+ | ((xmaxc[3] >> 1) & 0x1F); \
+ *c++ = ((xmaxc[3] & 0x1) << 7) \
+ | ((xmc[39] & 0x7) << 4) \
+ | ((xmc[40] & 0x7) << 1) \
+ | ((xmc[41] >> 2) & 0x1); \
+ *c++ = ((xmc[41] & 0x3) << 6) /* 30 */ \
+ | ((xmc[42] & 0x7) << 3) \
+ | (xmc[43] & 0x7); \
+ *c++ = ((xmc[44] & 0x7) << 5) \
+ | ((xmc[45] & 0x7) << 2) \
+ | ((xmc[46] >> 1) & 0x3); \
+ *c++ = ((xmc[46] & 0x1) << 7) \
+ | ((xmc[47] & 0x7) << 4) \
+ | ((xmc[48] & 0x7) << 1) \
+ | ((xmc[49] >> 2) & 0x1); \
+ *c++ = ((xmc[49] & 0x3) << 6) \
+ | ((xmc[50] & 0x7) << 3) \
+ | (xmc[51] & 0x7); \
+}
+
+static inline void conv65( wav_byte * c, gsm_byte * d){
+
+ unsigned int sr = 0;
+ unsigned int frame_chain;
+ unsigned int LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4];
+ /* silence bogus compiler warning */
+ unsigned int xmc[13*4] = { 0, };
+
+ sr = *c++;
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 2;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr |= (uword)*c++ << 4;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 2;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2; /* 5 */
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+ xmc[0] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 10 */
+ xmc[6] = sr & 0x7; sr >>= 3;
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+ xmc[13] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 15 */
+ xmc[14] = sr & 0x7; sr >>= 3;
+ xmc[15] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4; /* 20 */
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+ xmc[26] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 25 */
+ xmc[35] = sr & 0x7; sr >>= 3;
+ xmc[36] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 4;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 1;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+ xmc[39] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 30 */
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ xmc[44] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+
+ frame_chain = sr & 0xf;
+
+
+ writeGSM_33(d);/* LARc etc. -> array of 33 GSM bytes */
+
+
+ sr = frame_chain;
+ sr |= (uword)*c++ << 4; /* 1 */
+ LARc[0] = sr & 0x3f; sr >>= 6;
+ LARc[1] = sr & 0x3f; sr >>= 6;
+ sr = *c++;
+ LARc[2] = sr & 0x1f; sr >>= 5;
+ sr |= (uword)*c++ << 3;
+ LARc[3] = sr & 0x1f; sr >>= 5;
+ LARc[4] = sr & 0xf; sr >>= 4;
+ sr |= (uword)*c++ << 2;
+ LARc[5] = sr & 0xf; sr >>= 4;
+ LARc[6] = sr & 0x7; sr >>= 3;
+ LARc[7] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 5 */
+ Nc[0] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[0] = sr & 0x3; sr >>= 2;
+ Mc[0] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[0] = sr & 0x3f; sr >>= 6;
+ xmc[0] = sr & 0x7; sr >>= 3;
+ xmc[1] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[2] = sr & 0x7; sr >>= 3;
+ xmc[3] = sr & 0x7; sr >>= 3;
+ xmc[4] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[5] = sr & 0x7; sr >>= 3;
+ xmc[6] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2; /* 10 */
+ xmc[7] = sr & 0x7; sr >>= 3;
+ xmc[8] = sr & 0x7; sr >>= 3;
+ xmc[9] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[10] = sr & 0x7; sr >>= 3;
+ xmc[11] = sr & 0x7; sr >>= 3;
+ xmc[12] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[1] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[1] = sr & 0x3; sr >>= 2;
+ Mc[1] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[1] = sr & 0x3f; sr >>= 6;
+ xmc[13] = sr & 0x7; sr >>= 3;
+ xmc[14] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 15 */
+ xmc[15] = sr & 0x7; sr >>= 3;
+ xmc[16] = sr & 0x7; sr >>= 3;
+ xmc[17] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[18] = sr & 0x7; sr >>= 3;
+ xmc[19] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[20] = sr & 0x7; sr >>= 3;
+ xmc[21] = sr & 0x7; sr >>= 3;
+ xmc[22] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[23] = sr & 0x7; sr >>= 3;
+ xmc[24] = sr & 0x7; sr >>= 3;
+ xmc[25] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[2] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1; /* 20 */
+ bc[2] = sr & 0x3; sr >>= 2;
+ Mc[2] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[2] = sr & 0x3f; sr >>= 6;
+ xmc[26] = sr & 0x7; sr >>= 3;
+ xmc[27] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[28] = sr & 0x7; sr >>= 3;
+ xmc[29] = sr & 0x7; sr >>= 3;
+ xmc[30] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ xmc[31] = sr & 0x7; sr >>= 3;
+ xmc[32] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[33] = sr & 0x7; sr >>= 3;
+ xmc[34] = sr & 0x7; sr >>= 3;
+ xmc[35] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1; /* 25 */
+ xmc[36] = sr & 0x7; sr >>= 3;
+ xmc[37] = sr & 0x7; sr >>= 3;
+ xmc[38] = sr & 0x7; sr >>= 3;
+ sr = *c++;
+ Nc[3] = sr & 0x7f; sr >>= 7;
+ sr |= (uword)*c++ << 1;
+ bc[3] = sr & 0x3; sr >>= 2;
+ Mc[3] = sr & 0x3; sr >>= 2;
+ sr |= (uword)*c++ << 5;
+ xmaxc[3] = sr & 0x3f; sr >>= 6;
+ xmc[39] = sr & 0x7; sr >>= 3;
+ xmc[40] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[41] = sr & 0x7; sr >>= 3;
+ xmc[42] = sr & 0x7; sr >>= 3;
+ xmc[43] = sr & 0x7; sr >>= 3;
+ sr = *c++; /* 30 */
+ xmc[44] = sr & 0x7; sr >>= 3;
+ xmc[45] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 2;
+ xmc[46] = sr & 0x7; sr >>= 3;
+ xmc[47] = sr & 0x7; sr >>= 3;
+ xmc[48] = sr & 0x7; sr >>= 3;
+ sr |= (uword)*c++ << 1;
+ xmc[49] = sr & 0x7; sr >>= 3;
+ xmc[50] = sr & 0x7; sr >>= 3;
+ xmc[51] = sr & 0x7; sr >>= 3;
+ writeGSM_33(d+33);
+
+}
diff --git a/trunk/funcs/Makefile b/trunk/funcs/Makefile
new file mode 100644
index 000000000..9b13b17a0
--- /dev/null
+++ b/trunk/funcs/Makefile
@@ -0,0 +1,20 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for dialplan functions
+#
+# Copyright (C) 2005-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=func
+MENUSELECT_CATEGORY=FUNCS
+MENUSELECT_DESCRIPTION=Dialplan Functions
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/trunk/funcs/func_base64.c b/trunk/funcs/func_base64.c
new file mode 100644
index 000000000..0849e9539
--- /dev/null
+++ b/trunk/funcs/func_base64.c
@@ -0,0 +1,87 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005 - 2006, Digium, Inc.
+ * Copyright (C) 2005, Claude Patry
+ *
+ * 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 Use the base64 as functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h" /* function register/unregister */
+#include "asterisk/utils.h"
+
+static int base64_encode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: BASE64_ENCODE(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_base64encode(buf, (unsigned char *) data, strlen(data), len);
+
+ return 0;
+}
+
+static int base64_decode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: BASE64_DECODE(<base_64 string>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_base64decode((unsigned char *) buf, data, len);
+
+ return 0;
+}
+
+static struct ast_custom_function base64_encode_function = {
+ .name = "BASE64_ENCODE",
+ .synopsis = "Encode a string in base64",
+ .desc = "Returns the base64 string\n",
+ .syntax = "BASE64_ENCODE(<string>)",
+ .read = base64_encode,
+};
+
+static struct ast_custom_function base64_decode_function = {
+ .name = "BASE64_DECODE",
+ .synopsis = "Decode a base64 string",
+ .desc = "Returns the plain text string\n",
+ .syntax = "BASE64_DECODE(<base64_string>)",
+ .read = base64_decode,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&base64_encode_function) |
+ ast_custom_function_unregister(&base64_decode_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&base64_encode_function) |
+ ast_custom_function_register(&base64_decode_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "base64 encode/decode dialplan functions");
diff --git a/trunk/funcs/func_blacklist.c b/trunk/funcs/func_blacklist.c
new file mode 100644
index 000000000..2a2fa78c7
--- /dev/null
+++ b/trunk/funcs/func_blacklist.c
@@ -0,0 +1,74 @@
+/*
+ * 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 Function to lookup the callerid number, and see if it is blacklisted
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup functions
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/astdb.h"
+
+static int blacklist_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char blacklist[1];
+ int bl = 0;
+
+ if (chan->cid.cid_num) {
+ if (!ast_db_get("blacklist", chan->cid.cid_num, blacklist, sizeof (blacklist)))
+ bl = 1;
+ }
+ if (chan->cid.cid_name) {
+ if (!ast_db_get("blacklist", chan->cid.cid_name, blacklist, sizeof (blacklist)))
+ bl = 1;
+ }
+
+ snprintf(buf, len, "%d", bl);
+ return 0;
+}
+
+static struct ast_custom_function blacklist_function = {
+ .name = "BLACKLIST",
+ .synopsis = "Check if the callerid is on the blacklist",
+ .desc = "Uses astdb to check if the Caller*ID is in family 'blacklist'. Returns 1 or 0.\n",
+ .syntax = "BLACKLIST()",
+ .read = blacklist_read,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&blacklist_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&blacklist_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Look up Caller*ID name/number from blacklist database");
diff --git a/trunk/funcs/func_callerid.c b/trunk/funcs/func_callerid.c
new file mode 100644
index 000000000..9dfe0d8c7
--- /dev/null
+++ b/trunk/funcs/func_callerid.c
@@ -0,0 +1,233 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999-2006, Digium, Inc.
+ *
+ * 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 Caller ID related dialplan functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/callerid.h"
+
+static int callerpres_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ ast_copy_string(buf, ast_named_caller_presentation(chan->cid.cid_pres), len);
+ return 0;
+}
+
+static int callerpres_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int pres = ast_parse_caller_presentation(value);
+ if (pres < 0)
+ ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show function CALLERPRES')\n", value);
+ else
+ chan->cid.cid_pres = pres;
+ return 0;
+}
+
+static int callerid_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *opt = data;
+
+ if (!chan)
+ return -1;
+
+ if (strchr(opt, ',')) {
+ char name[80], num[80];
+
+ data = strsep(&opt, ",");
+ ast_callerid_split(opt, name, sizeof(name), num, sizeof(num));
+
+ if (!strncasecmp("all", data, 3)) {
+ snprintf(buf, len, "\"%s\" <%s>", name, num);
+ } else if (!strncasecmp("name", data, 4)) {
+ ast_copy_string(buf, name, len);
+ } else if (!strncasecmp("num", data, 3)) {
+ ast_copy_string(buf, num, len);
+ } else {
+ ast_log(LOG_ERROR, "Unknown callerid data type '%s'.\n", data);
+ }
+ } else {
+ ast_channel_lock(chan);
+
+ if (!strncasecmp("all", data, 3)) {
+ snprintf(buf, len, "\"%s\" <%s>",
+ S_OR(chan->cid.cid_name, ""),
+ S_OR(chan->cid.cid_num, ""));
+ } else if (!strncasecmp("name", data, 4)) {
+ if (chan->cid.cid_name) {
+ ast_copy_string(buf, chan->cid.cid_name, len);
+ }
+ } else if (!strncasecmp("num", data, 3)) {
+ if (chan->cid.cid_num) {
+ ast_copy_string(buf, chan->cid.cid_num, len);
+ }
+ } else if (!strncasecmp("ani", data, 3)) {
+ if (!strncasecmp(data + 3, "2", 1)) {
+ snprintf(buf, len, "%d", chan->cid.cid_ani2);
+ } else if (chan->cid.cid_ani) {
+ ast_copy_string(buf, chan->cid.cid_ani, len);
+ }
+ } else if (!strncasecmp("dnid", data, 4)) {
+ if (chan->cid.cid_dnid) {
+ ast_copy_string(buf, chan->cid.cid_dnid, len);
+ }
+ } else if (!strncasecmp("rdnis", data, 5)) {
+ if (chan->cid.cid_rdnis) {
+ ast_copy_string(buf, chan->cid.cid_rdnis, len);
+ }
+ } else if (!strncasecmp("pres", data, 4)) {
+ ast_copy_string(buf, ast_named_caller_presentation(chan->cid.cid_pres), len);
+ } else if (!strncasecmp("ton", data, 3)) {
+ snprintf(buf, len, "%d", chan->cid.cid_ton);
+ } else {
+ ast_log(LOG_ERROR, "Unknown callerid data type '%s'.\n", data);
+ }
+
+ ast_channel_unlock(chan);
+ }
+
+ return 0;
+}
+
+static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ if (!value || !chan)
+ return -1;
+
+ if (!strncasecmp("all", data, 3)) {
+ char name[256];
+ char num[256];
+
+ if (!ast_callerid_split(value, name, sizeof(name), num, sizeof(num)))
+ ast_set_callerid(chan, num, name, num);
+ } else if (!strncasecmp("name", data, 4)) {
+ ast_set_callerid(chan, NULL, value, NULL);
+ } else if (!strncasecmp("num", data, 3)) {
+ ast_set_callerid(chan, value, NULL, NULL);
+ } else if (!strncasecmp("ani", data, 3)) {
+ if (!strncasecmp(data + 3, "2", 1)) {
+ int i = atoi(value);
+ chan->cid.cid_ani2 = i;
+ } else
+ ast_set_callerid(chan, NULL, NULL, value);
+ } else if (!strncasecmp("dnid", data, 4)) {
+ ast_channel_lock(chan);
+ if (chan->cid.cid_dnid)
+ ast_free(chan->cid.cid_dnid);
+ chan->cid.cid_dnid = ast_strdup(value);
+ ast_channel_unlock(chan);
+ } else if (!strncasecmp("rdnis", data, 5)) {
+ ast_channel_lock(chan);
+ if (chan->cid.cid_rdnis)
+ ast_free(chan->cid.cid_rdnis);
+ chan->cid.cid_rdnis = ast_strdup(value);
+ ast_channel_unlock(chan);
+ } else if (!strncasecmp("pres", data, 4)) {
+ int i;
+ char *s, *val;
+
+ /* Strip leading spaces */
+ while ((value[0] == '\t') || (value[0] == ' '))
+ ++value;
+
+ val = ast_strdupa(value);
+
+ /* Strip trailing spaces */
+ s = val + strlen(val);
+ while ((s != val) && ((s[-1] == '\t') || (s[-1] == ' ')))
+ --s;
+ *s = '\0';
+
+ if ((val[0] >= '0') && (val[0] <= '9'))
+ i = atoi(val);
+ else
+ i = ast_parse_caller_presentation(val);
+
+ if (i < 0)
+ ast_log(LOG_ERROR, "Unknown calling number presentation '%s', value unchanged\n", val);
+ else
+ chan->cid.cid_pres = i;
+ } else if (!strncasecmp("ton", data, 3)) {
+ int i = atoi(value);
+ chan->cid.cid_ton = i;
+ } else {
+ ast_log(LOG_ERROR, "Unknown callerid data type '%s'.\n", data);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function callerid_function = {
+ .name = "CALLERID",
+ .synopsis = "Gets or sets Caller*ID data on the channel.",
+ .syntax = "CALLERID(datatype[,<optional-CID>])",
+ .desc =
+ "Gets or sets Caller*ID data on the channel. The allowable datatypes\n"
+ "are \"all\", \"name\", \"num\", \"ANI\", \"DNID\", \"RDNIS\", \"pres\",\n"
+ "and \"ton\".\n"
+ "Uses channel callerid by default or optional callerid, if specified.\n",
+ .read = callerid_read,
+ .write = callerid_write,
+};
+
+static struct ast_custom_function callerpres_function = {
+ .name = "CALLERPRES",
+ .synopsis = "Gets or sets Caller*ID presentation on the channel.",
+ .syntax = "CALLERPRES()",
+ .desc =
+"Gets or sets Caller*ID presentation on the channel. The following values\n"
+"are valid:\n"
+" allowed_not_screened : Presentation Allowed, Not Screened\n"
+" allowed_passed_screen : Presentation Allowed, Passed Screen\n"
+" allowed_failed_screen : Presentation Allowed, Failed Screen\n"
+" allowed : Presentation Allowed, Network Number\n"
+" prohib_not_screened : Presentation Prohibited, Not Screened\n"
+" prohib_passed_screen : Presentation Prohibited, Passed Screen\n"
+" prohib_failed_screen : Presentation Prohibited, Failed Screen\n"
+" prohib : Presentation Prohibited, Network Number\n"
+" unavailable : Number Unavailable\n",
+ .read = callerpres_read,
+ .write = callerpres_write,
+};
+
+static int unload_module(void)
+{
+ int res = ast_custom_function_unregister(&callerpres_function);
+ res |= ast_custom_function_unregister(&callerid_function);
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = ast_custom_function_register(&callerpres_function);
+ res |= ast_custom_function_register(&callerid_function);
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Caller ID related dialplan functions");
diff --git a/trunk/funcs/func_cdr.c b/trunk/funcs/func_cdr.c
new file mode 100644
index 000000000..63d49fd42
--- /dev/null
+++ b/trunk/funcs/func_cdr.c
@@ -0,0 +1,162 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999-2006, Digium, Inc.
+ *
+ * Portions Copyright (C) 2005, Anthony Minessale II
+ *
+ * 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 Call Detail Record related dialplan functions
+ *
+ * \author Anthony Minessale II
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/cdr.h"
+
+enum {
+ OPT_RECURSIVE = (1 << 0),
+ OPT_UNPARSED = (1 << 1),
+ OPT_LAST = (1 << 2),
+} cdr_option_flags;
+
+AST_APP_OPTIONS(cdr_func_options, {
+ AST_APP_OPTION('l', OPT_LAST),
+ AST_APP_OPTION('r', OPT_RECURSIVE),
+ AST_APP_OPTION('u', OPT_UNPARSED),
+});
+
+static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
+ char *buf, size_t len)
+{
+ char *ret;
+ struct ast_flags flags = { 0 };
+ struct ast_cdr *cdr = chan ? chan->cdr : NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(variable);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(parse))
+ return -1;
+
+ if (!cdr)
+ return -1;
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
+
+ if (ast_test_flag(&flags, OPT_LAST))
+ while (cdr->next)
+ cdr = cdr->next;
+
+ ast_cdr_getvar(cdr, args.variable, &ret, buf, len,
+ ast_test_flag(&flags, OPT_RECURSIVE),
+ ast_test_flag(&flags, OPT_UNPARSED));
+
+ return 0;
+}
+
+static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
+ const char *value)
+{
+ struct ast_flags flags = { 0 };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(variable);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(parse) || !value || !chan)
+ return -1;
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
+
+ if (!strcasecmp(args.variable, "accountcode"))
+ ast_cdr_setaccount(chan, value);
+ else if (!strcasecmp(args.variable, "userfield"))
+ ast_cdr_setuserfield(chan, value);
+ else if (!strcasecmp(args.variable, "amaflags"))
+ ast_cdr_setamaflags(chan, value);
+ else if (chan->cdr)
+ ast_cdr_setvar(chan->cdr, args.variable, value, ast_test_flag(&flags, OPT_RECURSIVE));
+ /* No need to worry about the u flag, as all fields for which setting
+ * 'u' would do anything are marked as readonly. */
+
+ return 0;
+}
+
+static struct ast_custom_function cdr_function = {
+ .name = "CDR",
+ .synopsis = "Gets or sets a CDR variable",
+ .syntax = "CDR(<name>[,options])",
+ .read = cdr_read,
+ .write = cdr_write,
+ .desc =
+"Options:\n"
+" 'r' searches the entire stack of CDRs on the channel\n"
+" 'u' retrieves the raw, unprocessed value\n"
+" For example, 'start', 'answer', and 'end' will be retrieved as epoch\n"
+" values, when the 'u' option is passed, but formatted as YYYY-MM-DD HH:MM:SS\n"
+" otherwise. Similarly, disposition and amaflags will return their raw\n"
+" integral values.\n"
+" Here is a list of all the available cdr field names:\n"
+" clid lastdata disposition\n"
+" src start amaflags\n"
+" dst answer accountcode\n"
+" dcontext end uniqueid\n"
+" dstchannel duration userfield\n"
+" lastapp billsec channel\n"
+" All of the above variables are read-only, except for accountcode,\n"
+" userfield, and amaflags. You may, however, supply\n"
+" a name not on the above list, and create your own\n"
+" variable, whose value can be changed with this function,\n"
+" and this variable will be stored on the cdr.\n"
+" raw values for disposition:\n"
+" 1 = NO ANSWER\n"
+" 2 = BUSY\n"
+" 3 = FAILED\n"
+" 4 = ANSWERED\n"
+" raw values for amaflags:\n"
+" 1 = OMIT\n"
+" 2 = BILLING\n"
+" 3 = DOCUMENTATION\n",
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&cdr_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&cdr_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Detail Record (CDR) dialplan function");
diff --git a/trunk/funcs/func_channel.c b/trunk/funcs/func_channel.c
new file mode 100644
index 000000000..d920ac21f
--- /dev/null
+++ b/trunk/funcs/func_channel.c
@@ -0,0 +1,206 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * 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 Channel info dialplan function
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/indications.h"
+#include "asterisk/stringfields.h"
+
+#define locked_copy_string(chan, dest, source, len) \
+ do { \
+ ast_channel_lock(chan); \
+ ast_copy_string(dest, source, len); \
+ ast_channel_unlock(chan); \
+ } while (0)
+#define locked_string_field_set(chan, field, source) \
+ do { \
+ ast_channel_lock(chan); \
+ ast_string_field_set(chan, field, source); \
+ ast_channel_unlock(chan); \
+ } while (0)
+
+char *transfercapability_table[0x20] = {
+ "SPEECH", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK",
+ "DIGITAL", "RESTRICTED_DIGITAL", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK",
+ "3K1AUDIO", "DIGITAL_W_TONES", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK",
+ "VIDEO", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK", "UNK", };
+
+static int func_channel_read(struct ast_channel *chan, const char *function,
+ char *data, char *buf, size_t len)
+{
+ int ret = 0;
+
+ if (!strcasecmp(data, "audionativeformat"))
+ /* use the _multiple version when chan->nativeformats holds multiple formats */
+ /* ast_getformatname_multiple(buf, len, chan->nativeformats & AST_FORMAT_AUDIO_MASK); */
+ ast_copy_string(buf, ast_getformatname(chan->nativeformats & AST_FORMAT_AUDIO_MASK), len);
+ else if (!strcasecmp(data, "videonativeformat"))
+ /* use the _multiple version when chan->nativeformats holds multiple formats */
+ /* ast_getformatname_multiple(buf, len, chan->nativeformats & AST_FORMAT_VIDEO_MASK); */
+ ast_copy_string(buf, ast_getformatname(chan->nativeformats & AST_FORMAT_VIDEO_MASK), len);
+ else if (!strcasecmp(data, "audioreadformat"))
+ ast_copy_string(buf, ast_getformatname(chan->readformat), len);
+ else if (!strcasecmp(data, "audiowriteformat"))
+ ast_copy_string(buf, ast_getformatname(chan->writeformat), len);
+ else if (!strcasecmp(data, "tonezone") && chan->zone)
+ locked_copy_string(chan, buf, chan->zone->country, len);
+ else if (!strcasecmp(data, "language"))
+ locked_copy_string(chan, buf, chan->language, len);
+ else if (!strcasecmp(data, "musicclass"))
+ locked_copy_string(chan, buf, chan->musicclass, len);
+ else if (!strcasecmp(data, "state"))
+ locked_copy_string(chan, buf, ast_state2str(chan->_state), len);
+ else if (!strcasecmp(data, "channeltype"))
+ locked_copy_string(chan, buf, chan->tech->type, len);
+ else if (!strcasecmp(data, "transfercapability"))
+ locked_copy_string(chan, buf, transfercapability_table[chan->transfercapability & 0x1f], len);
+ else if (!strcasecmp(data, "callgroup")) {
+ char groupbuf[256];
+ locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), chan->callgroup), len);
+ } else if (!chan->tech->func_channel_read
+ || chan->tech->func_channel_read(chan, function, data, buf, len)) {
+ ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int func_channel_write(struct ast_channel *chan, const char *function,
+ char *data, const char *value)
+{
+ int ret = 0;
+ signed char gainset;
+
+ if (!strcasecmp(data, "language"))
+ locked_string_field_set(chan, language, value);
+ else if (!strcasecmp(data, "musicclass"))
+ locked_string_field_set(chan, musicclass, value);
+ else if (!strcasecmp(data, "tonezone")) {
+ struct ind_tone_zone *new_zone;
+ if (!(new_zone = ast_get_indication_zone(value))) {
+ ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone. Check indications.conf for available country codes.\n", value);
+ ret = -1;
+ } else
+ chan->zone = new_zone;
+ } else if (!strcasecmp(data, "callgroup"))
+ chan->callgroup = ast_get_group(value);
+ else if (!strcasecmp(data, "txgain")) {
+ sscanf(value, "%hhd", &gainset);
+ ast_channel_setoption(chan, AST_OPTION_TXGAIN, &gainset, sizeof(gainset), 0);
+ } else if (!strcasecmp(data, "rxgain")) {
+ sscanf(value, "%hhd", &gainset);
+ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &gainset, sizeof(gainset), 0);
+ } else if (!strcasecmp(data, "transfercapability")) {
+ unsigned short i;
+ for (i = 0; i < 0x20; i++) {
+ if (!strcasecmp(transfercapability_table[i], value) && strcmp(value, "UNK")) {
+ chan->transfercapability = i;
+ break;
+ }
+ }
+ } else if (!chan->tech->func_channel_write
+ || chan->tech->func_channel_write(chan, function, data, value)) {
+ ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n",
+ data);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static struct ast_custom_function channel_function = {
+ .name = "CHANNEL",
+ .synopsis = "Gets/sets various pieces of information about the channel.",
+ .syntax = "CHANNEL(item)",
+ .desc = "Gets/set various pieces of information about the channel.\n"
+ "Standard items (provided by all channel technologies) are:\n"
+ "R/O audioreadformat format currently being read\n"
+ "R/O audionativeformat format used natively for audio\n"
+ "R/O audiowriteformat format currently being written\n"
+ "R/W callgroup call groups for call pickup\n"
+ "R/O channeltype technology used for channel\n"
+ "R/W language language for sounds played\n"
+ "R/W musicclass class (from musiconhold.conf) for hold music\n"
+ "R/W rxgain set rxgain level on channel drivers that support it\n"
+ "R/O state state for channel\n"
+ "R/W tonezone zone for indications played\n"
+ "R/W txgain set txgain level on channel drivers that support it\n"
+ "R/O videonativeformat format used natively for video\n"
+ "\n"
+ "chan_sip provides the following additional options:\n"
+ "R/O rtpqos Get QOS information about the RTP stream\n"
+ " This option takes two additional arguments:\n"
+ " Argument 1:\n"
+ " audio Get data about the audio stream\n"
+ " video Get data about the video stream\n"
+ " text Get data about the text stream\n"
+ " Argument 2:\n"
+ " local_ssrc Local SSRC (stream ID)\n"
+ " local_lostpackets Local lost packets\n"
+ " local_jitter Local calculated jitter\n"
+ " local_count Number of received packets\n"
+ " remote_ssrc Remote SSRC (stream ID)\n"
+ " remote_lostpackets Remote lost packets\n"
+ " remote_jitter Remote reported jitter\n"
+ " remote_count Number of transmitted packets\n"
+ " rtt Round trip time\n"
+ " all All statistics (in a form suited to logging, but not for parsing)\n"
+ "R/O rtpdest Get remote RTP destination information\n"
+ " This option takes one additional argument:\n"
+ " Argument 1:\n"
+ " audio Get audio destination\n"
+ " video Get video destination\n"
+ "\n"
+ "chan_iax2 provides the following additional options:\n"
+ "R/W osptoken Get or set the OSP token information for a call\n"
+ "\n"
+ "Additional items may be available from the channel driver providing\n"
+ "the channel; see its documentation for details.\n"
+ "\n"
+ "Any item requested that is not available on the current channel will\n"
+ "return an empty string.\n",
+ .read = func_channel_read,
+ .write = func_channel_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&channel_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&channel_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel information dialplan function");
diff --git a/trunk/funcs/func_curl.c b/trunk/funcs/func_curl.c
new file mode 100644
index 000000000..32dbf7686
--- /dev/null
+++ b/trunk/funcs/func_curl.c
@@ -0,0 +1,204 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2006, Tilghman Lesher
+ *
+ * Tilghman Lesher <curl-20050919@the-tilghman.com>
+ * and Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
+ *
+ * app_curl.c is distributed with no restrictions on usage or
+ * redistribution.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Curl - Load a URL
+ *
+ * \author Tilghman Lesher <curl-20050919@the-tilghman.com>
+ *
+ * \note Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
+ *
+ * \extref Depends on the CURL library - http://curl.haxx.se/
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+ <depend>curl</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <curl/curl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cli.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+/* There might be a realloc() out there that doesn't like reallocing
+ * NULL pointers, so we take care of it here
+ */
+static void *myrealloc(void *ptr, size_t size)
+{
+ return (ptr ? ast_realloc(ptr, size) : ast_malloc(size));
+}
+
+static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ register int realsize = size * nmemb;
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ if ((mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1))) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+
+ return realsize;
+}
+
+static const char *global_useragent = "asterisk-libcurl-agent/1.0";
+
+static int curl_instance_init(void *data)
+{
+ CURL **curl = data;
+
+ if (!(*curl = curl_easy_init()))
+ return -1;
+
+ curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
+ curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
+
+ return 0;
+}
+
+static void curl_instance_cleanup(void *data)
+{
+ CURL **curl = data;
+
+ curl_easy_cleanup(*curl);
+}
+
+AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
+
+static int curl_internal(struct MemoryStruct *chunk, char *url, char *post)
+{
+ CURL **curl;
+
+ if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl))))
+ return -1;
+
+ curl_easy_setopt(*curl, CURLOPT_URL, url);
+ curl_easy_setopt(*curl, CURLOPT_WRITEDATA, (void *) chunk);
+
+ if (post) {
+ curl_easy_setopt(*curl, CURLOPT_POST, 1);
+ curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, post);
+ }
+
+ curl_easy_perform(*curl);
+
+ if (post)
+ curl_easy_setopt(*curl, CURLOPT_POST, 0);
+
+ return 0;
+}
+
+static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
+{
+ struct MemoryStruct chunk = { NULL, 0 };
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(url);
+ AST_APP_ARG(postdata);
+ );
+
+ *buf = '\0';
+
+ if (ast_strlen_zero(info)) {
+ ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, info);
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ if (!curl_internal(&chunk, args.url, args.postdata)) {
+ if (chunk.memory) {
+ chunk.memory[chunk.size] = '\0';
+ if (chunk.memory[chunk.size - 1] == 10)
+ chunk.memory[chunk.size - 1] = '\0';
+
+ ast_copy_string(buf, chunk.memory, len);
+ ast_free(chunk.memory);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+struct ast_custom_function acf_curl = {
+ .name = "CURL",
+ .synopsis = "Retrieves the contents of a URL",
+ .syntax = "CURL(url[,post-data])",
+ .desc =
+ " url - URL to retrieve\n"
+ " post-data - Optional data to send as a POST (GET is default action)\n",
+ .read = acf_curl_exec,
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_custom_function_unregister(&acf_curl);
+
+ curl_global_cleanup();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if (curl_global_init(CURL_GLOBAL_ALL)) {
+ ast_log(LOG_ERROR, "Unable to initialize the CURL library. Cannot load func_curl\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ res = ast_custom_function_register(&acf_curl);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Load external URL");
+
diff --git a/trunk/funcs/func_cut.c b/trunk/funcs/func_cut.c
new file mode 100644
index 000000000..169fed6b5
--- /dev/null
+++ b/trunk/funcs/func_cut.c
@@ -0,0 +1,300 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2003-2006 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <app_cut__v003@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief CUT function
+ *
+ * \author Tilghman Lesher <app_cut__v003@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+
+/* Maximum length of any variable */
+#define MAXRESULT 1024
+
+struct sortable_keys {
+ char *key;
+ float value;
+};
+
+static int sort_subroutine(const void *arg1, const void *arg2)
+{
+ const struct sortable_keys *one=arg1, *two=arg2;
+ if (one->value < two->value)
+ return -1;
+ else if (one->value == two->value)
+ return 0;
+ else
+ return 1;
+}
+
+#define ERROR_NOARG (-1)
+#define ERROR_NOMEM (-2)
+#define ERROR_USAGE (-3)
+
+static int sort_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
+{
+ char *strings, *ptrkey, *ptrvalue;
+ int count=1, count2, element_count=0;
+ struct sortable_keys *sortable_keys;
+
+ *buffer = '\0';
+
+ if (!data)
+ return ERROR_NOARG;
+
+ strings = ast_strdupa(data);
+
+ for (ptrkey = strings; *ptrkey; ptrkey++) {
+ if (*ptrkey == ',')
+ count++;
+ }
+
+ sortable_keys = alloca(count * sizeof(struct sortable_keys));
+
+ memset(sortable_keys, 0, count * sizeof(struct sortable_keys));
+
+ /* Parse each into a struct */
+ count2 = 0;
+ while ((ptrkey = strsep(&strings, ","))) {
+ ptrvalue = index(ptrkey, ':');
+ if (!ptrvalue) {
+ count--;
+ continue;
+ }
+ *ptrvalue++ = '\0';
+ sortable_keys[count2].key = ptrkey;
+ sscanf(ptrvalue, "%f", &sortable_keys[count2].value);
+ count2++;
+ }
+
+ /* Sort the structs */
+ qsort(sortable_keys, count, sizeof(struct sortable_keys), sort_subroutine);
+
+ for (count2 = 0; count2 < count; count2++) {
+ int blen = strlen(buffer);
+ if (element_count++) {
+ strncat(buffer + blen, ",", buflen - blen - 1);
+ blen++;
+ }
+ strncat(buffer + blen, sortable_keys[count2].key, buflen - blen - 1);
+ }
+
+ return 0;
+}
+
+static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
+{
+ char *parse;
+ size_t delim_consumed;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(varname);
+ AST_APP_ARG(delimiter);
+ AST_APP_ARG(field);
+ );
+
+ *buffer = '\0';
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ /* Check and parse arguments */
+ if (args.argc < 3) {
+ return ERROR_NOARG;
+ } else {
+ char d, ds[2] = "";
+ char *tmp = alloca(strlen(args.varname) + 4);
+ char varvalue[MAXRESULT], *tmp2=varvalue;
+
+ if (tmp) {
+ snprintf(tmp, strlen(args.varname) + 4, "${%s}", args.varname);
+ } else {
+ return ERROR_NOMEM;
+ }
+
+ if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed))
+ ast_copy_string(ds, "-", sizeof(ds));
+
+ /* String form of the delimiter, for use with strsep(3) */
+ d = *ds;
+
+ pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
+
+ if (tmp2) {
+ int curfieldnum = 1;
+ while (tmp2 != NULL && args.field != NULL) {
+ char *nextgroup = strsep(&(args.field), "&");
+ int num1 = 0, num2 = MAXRESULT;
+ char trashchar;
+
+ if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) {
+ /* range with both start and end */
+ } else if (sscanf(nextgroup, "-%d", &num2) == 1) {
+ /* range with end */
+ num1 = 0;
+ } else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) {
+ /* range with start */
+ num2 = MAXRESULT;
+ } else if (sscanf(nextgroup, "%d", &num1) == 1) {
+ /* single number */
+ num2 = num1;
+ } else {
+ return ERROR_USAGE;
+ }
+
+ /* Get to start, if any */
+ if (num1 > 0) {
+ while (tmp2 != (char *)NULL + 1 && curfieldnum < num1) {
+ tmp2 = index(tmp2, d) + 1;
+ curfieldnum++;
+ }
+ }
+
+ /* Most frequent problem is the expectation of reordering fields */
+ if ((num1 > 0) && (curfieldnum > num1))
+ ast_log(LOG_WARNING, "We're already past the field you wanted?\n");
+
+ /* Re-null tmp2 if we added 1 to NULL */
+ if (tmp2 == (char *)NULL + 1)
+ tmp2 = NULL;
+
+ /* Output fields until we either run out of fields or num2 is reached */
+ while (tmp2 != NULL && curfieldnum <= num2) {
+ char *tmp3 = strsep(&tmp2, ds);
+ int curlen = strlen(buffer);
+
+ if (curlen)
+ snprintf(buffer + curlen, buflen - curlen, "%c%s", d, tmp3);
+ else
+ snprintf(buffer, buflen, "%s", tmp3);
+
+ curfieldnum++;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int acf_sort_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int ret = -1;
+
+ switch (sort_internal(chan, data, buf, len)) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "SORT() requires an argument\n");
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ break;
+ case 0:
+ ret = 0;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ }
+
+ return ret;
+}
+
+static int acf_cut_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ int ret = -1;
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ switch (cut_internal(chan, data, buf, len)) {
+ case ERROR_NOARG:
+ ast_log(LOG_ERROR, "Syntax: CUT(<varname>,<char-delim>,<range-spec>) - missing argument!\n");
+ break;
+ case ERROR_NOMEM:
+ ast_log(LOG_ERROR, "Out of memory\n");
+ break;
+ case ERROR_USAGE:
+ ast_log(LOG_ERROR, "Usage: CUT(<varname>,<char-delim>,<range-spec>)\n");
+ break;
+ case 0:
+ ret = 0;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown internal error\n");
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return ret;
+}
+
+struct ast_custom_function acf_sort = {
+ .name = "SORT",
+ .synopsis = "Sorts a list of key/vals into a list of keys, based upon the vals",
+ .syntax = "SORT(key1:val1[...][,keyN:valN])",
+ .desc =
+"Takes a comma-separated list of keys and values, each separated by a colon, and returns a\n"
+"comma-separated list of the keys, sorted by their values. Values will be evaluated as\n"
+"floating-point numbers.\n",
+ .read = acf_sort_exec,
+};
+
+struct ast_custom_function acf_cut = {
+ .name = "CUT",
+ .synopsis = "Slices and dices strings, based upon a named delimiter.",
+ .syntax = "CUT(<varname>,<char-delim>,<range-spec>)",
+ .desc =
+" varname - variable you want cut\n"
+" char-delim - defaults to '-'\n"
+" range-spec - number of the field you want (1-based offset)\n"
+" may also be specified as a range (with -)\n"
+" or group of ranges and fields (with &)\n",
+ .read = acf_cut_exec,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&acf_cut);
+ res |= ast_custom_function_unregister(&acf_sort);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&acf_cut);
+ res |= ast_custom_function_register(&acf_sort);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Cut out information from a string");
diff --git a/trunk/funcs/func_db.c b/trunk/funcs/func_db.c
new file mode 100644
index 000000000..1c15e5df8
--- /dev/null
+++ b/trunk/funcs/func_db.c
@@ -0,0 +1,225 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Russell Bryant <russelb@clemson.edu>
+ *
+ * func_db.c adapted from the old app_db.c, copyright by the following people
+ * Copyright (C) 2005, Mark Spencer <markster@digium.com>
+ * Copyright (C) 2003, 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 Functions for interaction with the Asterisk database
+ *
+ * \author Russell Bryant <russelb@clemson.edu>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <regex.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/astdb.h"
+
+static int function_db_read(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ if (ast_db_get(args.family, args.key, buf, len - 1)) {
+ ast_debug(1, "DB: %s/%s not found in database.\n", args.family, args.key);
+ } else
+ pbx_builtin_setvar_helper(chan, "DB_RESULT", buf);
+
+ return 0;
+}
+
+static int function_db_write(struct ast_channel *chan, const char *cmd, char *parse,
+ const char *value)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)=<value>\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB requires an argument, DB(<family>/<key>)=value\n");
+ return -1;
+ }
+
+ if (ast_db_put(args.family, args.key, (char *) value))
+ ast_log(LOG_WARNING, "DB: Error writing value to database.\n");
+
+ return 0;
+}
+
+static struct ast_custom_function db_function = {
+ .name = "DB",
+ .synopsis = "Read from or write to the Asterisk database",
+ .syntax = "DB(<family>/<key>)",
+ .desc =
+"This function will read from or write a value to the Asterisk database. On a\n"
+"read, this function returns the corresponding value from the database, or blank\n"
+"if it does not exist. Reading a database value will also set the variable\n"
+"DB_RESULT. If you wish to find out if an entry exists, use the DB_EXISTS\n"
+"function.\n",
+ .read = function_db_read,
+ .write = function_db_write,
+};
+
+static int function_db_exists(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB_EXISTS requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB_EXISTS requires an argument, DB(<family>/<key>)\n");
+ return -1;
+ }
+
+ if (ast_db_get(args.family, args.key, buf, len - 1))
+ strcpy(buf, "0");
+ else {
+ pbx_builtin_setvar_helper(chan, "DB_RESULT", buf);
+ strcpy(buf, "1");
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function db_exists_function = {
+ .name = "DB_EXISTS",
+ .synopsis = "Check to see if a key exists in the Asterisk database",
+ .syntax = "DB_EXISTS(<family>/<key>)",
+ .desc =
+ "This function will check to see if a key exists in the Asterisk\n"
+ "database. If it exists, the function will return \"1\". If not,\n"
+ "it will return \"0\". Checking for existence of a database key will\n"
+ "also set the variable DB_RESULT to the key's value if it exists.\n",
+ .read = function_db_exists,
+};
+
+static int function_db_delete(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(key);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "DB_DELETE requires an argument, DB_DELETE(<family>/<key>)\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (args.argc < 2) {
+ ast_log(LOG_WARNING, "DB_DELETE requires an argument, DB_DELETE(<family>/<key>)\n");
+ return -1;
+ }
+
+ if (ast_db_get(args.family, args.key, buf, len - 1)) {
+ ast_debug(1, "DB_DELETE: %s/%s not found in database.\n", args.family, args.key);
+ } else {
+ if (ast_db_del(args.family, args.key)) {
+ ast_debug(1, "DB_DELETE: %s/%s could not be deleted from the database\n", args.family, args.key);
+ }
+ }
+ pbx_builtin_setvar_helper(chan, "DB_RESULT", buf);
+
+ return 0;
+}
+
+
+static struct ast_custom_function db_delete_function = {
+ .name = "DB_DELETE",
+ .synopsis = "Return a value from the database and delete it",
+ .syntax = "DB_DELETE(<family>/<key>)",
+ .desc =
+ "This function will retrieve a value from the Asterisk database\n"
+ " and then remove that key from the database. DB_RESULT\n"
+ "will be set to the key's value if it exists.\n",
+ .read = function_db_delete,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&db_function);
+ res |= ast_custom_function_unregister(&db_exists_function);
+ res |= ast_custom_function_unregister(&db_delete_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&db_function);
+ res |= ast_custom_function_register(&db_exists_function);
+ res |= ast_custom_function_register(&db_delete_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Database (astdb) related dialplan functions");
diff --git a/trunk/funcs/func_devstate.c b/trunk/funcs/func_devstate.c
new file mode 100644
index 000000000..950208be5
--- /dev/null
+++ b/trunk/funcs/func_devstate.c
@@ -0,0 +1,255 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Russell Bryant <russell@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 Manually controlled blinky lights
+ *
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \ingroup functions
+ *
+ * \note Props go out to Ahrimanes in \#asterisk for requesting this at 4:30 AM
+ * when I couldn't sleep. :)
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/cli.h"
+#include "asterisk/astdb.h"
+#include "asterisk/app.h"
+
+static const char astdb_family[] = "CustomDevstate";
+
+static int devstate_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ ast_copy_string(buf, ast_devstate_str(ast_device_state(data)), len);
+
+ return 0;
+}
+
+static int devstate_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ size_t len = strlen("Custom:");
+
+ if (strncasecmp(data, "Custom:", len)) {
+ ast_log(LOG_WARNING, "The DEVICE_STATE function can only be used to set 'Custom:' device state!\n");
+ return -1;
+ }
+ data += len;
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "DEVICE_STATE function called with no custom device name!\n");
+ return -1;
+ }
+
+ ast_db_put(astdb_family, data, value);
+
+ ast_devstate_changed(ast_devstate_val(value), "Custom:%s", data);
+
+ return 0;
+}
+
+enum {
+ HINT_OPT_NAME = (1 << 0),
+};
+
+AST_APP_OPTIONS(hint_options, BEGIN_OPTIONS
+ AST_APP_OPTION('n', HINT_OPT_NAME),
+END_OPTIONS );
+
+static int hint_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *exten, *context;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(exten);
+ AST_APP_ARG(options);
+ );
+ struct ast_flags opts = { 0, };
+ int res;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "The HINT function requires an extension\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (ast_strlen_zero(args.exten)) {
+ ast_log(LOG_WARNING, "The HINT function requires an extension\n");
+ return -1;
+ }
+
+ context = exten = args.exten;
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(hint_options, &opts, NULL, args.options);
+
+ if (ast_test_flag(&opts, HINT_OPT_NAME))
+ res = ast_get_hint(NULL, 0, buf, len, chan, context, exten);
+ else
+ res = ast_get_hint(buf, len, NULL, 0, chan, context, exten);
+
+ return !res; /* ast_get_hint returns non-zero on success */
+}
+
+static enum ast_device_state custom_devstate_callback(const char *data)
+{
+ char buf[256] = "";
+
+ ast_db_get(astdb_family, data, buf, sizeof(buf));
+
+ return ast_devstate_val(buf);
+}
+
+static char *cli_funcdevstate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_db_entry *db_entry, *db_tree;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "funcdevstate list";
+ e->usage =
+ "Usage: funcdevstate list\n"
+ " List all custom device states that have been set by using\n"
+ " the DEVICE_STATE dialplan function.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "\n"
+ "---------------------------------------------------------------------\n"
+ "--- Custom Device States --------------------------------------------\n"
+ "---------------------------------------------------------------------\n"
+ "---\n");
+
+ db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
+ for (; db_entry; db_entry = db_entry->next) {
+ const char *dev_name = strrchr(db_entry->key, '/') + 1;
+ if (dev_name <= (const char *) 1)
+ continue;
+ ast_cli(a->fd, "--- Name: 'Custom:%s' State: '%s'\n"
+ "---\n", dev_name, db_entry->data);
+ }
+ ast_db_freetree(db_tree);
+ db_tree = NULL;
+
+ ast_cli(a->fd,
+ "---------------------------------------------------------------------\n"
+ "---------------------------------------------------------------------\n"
+ "\n");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_funcdevstate[] = {
+ AST_CLI_DEFINE(cli_funcdevstate_list, "List currently known custom device states"),
+};
+
+static struct ast_custom_function devstate_function = {
+ .name = "DEVICE_STATE",
+ .synopsis = "Get or Set a device state",
+ .syntax = "DEVICE_STATE(device)",
+ .desc =
+ " The DEVICE_STATE function can be used to retrieve the device state from any\n"
+ "device state provider. For example:\n"
+ " NoOp(SIP/mypeer has state ${DEVICE_STATE(SIP/mypeer)})\n"
+ " NoOp(Conference number 1234 has state ${DEVICE_STATE(MeetMe:1234)})\n"
+ "\n"
+ " The DEVICE_STATE function can also be used to set custom device state from\n"
+ "the dialplan. The \"Custom:\" prefix must be used. For example:\n"
+ " Set(DEVICE_STATE(Custom:lamp1)=BUSY)\n"
+ " Set(DEVICE_STATE(Custom:lamp2)=NOT_INUSE)\n"
+ "You can subscribe to the status of a custom device state using a hint in\n"
+ "the dialplan:\n"
+ " exten => 1234,hint,Custom:lamp1\n"
+ "\n"
+ " The possible values for both uses of this function are:\n"
+ "UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
+ "RINGINUSE | ONHOLD\n",
+ .read = devstate_read,
+ .write = devstate_write,
+};
+
+static struct ast_custom_function hint_function = {
+ .name = "HINT",
+ .synopsis = "Get the devices set for a dialplan hint",
+ .syntax = "HINT(extension[@context][|options])",
+ .desc =
+ " The HINT function can be used to retrieve the list of devices that are\n"
+ "mapped to a dialplan hint. For example:\n"
+ " NoOp(Hint for Extension 1234 is ${HINT(1234)})\n"
+ "Options:\n"
+ " 'n' - Retrieve name on the hint instead of list of devices\n"
+ "",
+ .read = hint_read,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&devstate_function);
+ res |= ast_custom_function_unregister(&hint_function);
+ res |= ast_devstate_prov_del("Custom");
+ res |= ast_cli_unregister_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+ struct ast_db_entry *db_entry, *db_tree;
+
+ /* Populate the device state cache on the system with all of the currently
+ * known custom device states. */
+ db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
+ for (; db_entry; db_entry = db_entry->next) {
+ const char *dev_name = strrchr(db_entry->key, '/') + 1;
+ if (dev_name <= (const char *) 1)
+ continue;
+ ast_devstate_changed(ast_devstate_val(db_entry->data),
+ "Custom:%s\n", dev_name);
+ }
+ ast_db_freetree(db_tree);
+ db_tree = NULL;
+
+ res |= ast_custom_function_register(&devstate_function);
+ res |= ast_custom_function_register(&hint_function);
+ res |= ast_devstate_prov_add("Custom", custom_devstate_callback);
+ res |= ast_cli_register_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Gets or sets a device state in the dialplan");
diff --git a/trunk/funcs/func_dialgroup.c b/trunk/funcs/func_dialgroup.c
new file mode 100644
index 000000000..746784ba9
--- /dev/null
+++ b/trunk/funcs/func_dialgroup.c
@@ -0,0 +1,220 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Tilghman Lesher
+ *
+ * Tilghman Lesher <func_dialgroup__200709@the-tilghman.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 group dialplan function
+ *
+ * \author Tilghman Lesher <func_dialgroup__200709@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
+
+static struct ao2_container *group_container = NULL;
+
+struct group_entry {
+ char name[AST_CHANNEL_NAME];
+};
+
+struct group {
+ char name[AST_MAX_EXTENSION];
+ struct ao2_container *entries;
+};
+
+static void group_destroy(void *vgroup)
+{
+ struct group *group = vgroup;
+ ao2_ref(group->entries, -1);
+}
+
+static int group_hash_fn(const void *obj, const int flags)
+{
+ const struct group *g = obj;
+ return ast_str_hash(g->name);
+}
+
+static int group_cmp_fn(void *obj1, void *name2, int flags)
+{
+ struct group *g1 = obj1, *g2 = name2;
+ char *name = name2;
+ if (flags & OBJ_POINTER)
+ return strcmp(g1->name, g2->name) ? 0 : CMP_MATCH;
+ else
+ return strcmp(g1->name, name) ? 0 : CMP_MATCH;
+}
+
+static int entry_hash_fn(const void *obj, const int flags)
+{
+ const struct group_entry *e = obj;
+ return ast_str_hash(e->name);
+}
+
+static int entry_cmp_fn(void *obj1, void *name2, int flags)
+{
+ struct group_entry *e1 = obj1, *e2 = name2;
+ char *name = name2;
+ if (flags & OBJ_POINTER)
+ return strcmp(e1->name, e2->name) ? 0 : CMP_MATCH;
+ else
+ return strcmp(e1->name, name) ? 0 : CMP_MATCH;
+}
+
+static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ao2_iterator i;
+ struct group *grhead = ao2_find(group_container, data, 0);
+ struct group_entry *entry;
+ size_t bufused = 0;
+ int trunc_warning = 0;
+
+ if (!grhead) {
+ ast_log(LOG_WARNING, "No such dialgroup '%s'\n", data);
+ return -1;
+ }
+
+ i = ao2_iterator_init(grhead->entries, 0);
+ while ((entry = ao2_iterator_next(&i))) {
+ int tmp = strlen(entry->name);
+ /* Ensure that we copy only complete names, not partials */
+ if (len - bufused > tmp + 2) {
+ if (bufused != 0)
+ buf[bufused++] = '&';
+ ast_copy_string(buf + bufused, entry->name, len - bufused);
+ bufused += tmp;
+ } else if (trunc_warning++ == 0)
+ ast_log(LOG_WARNING, "Dialgroup '%s' is too large. Truncating list.\n", data);
+ }
+
+ return 0;
+}
+
+static int dialgroup_write(struct ast_channel *chan, const char *cmd, char *data, const char *cvalue)
+{
+ struct group *grhead;
+ struct group_entry *entry;
+ int j;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(group);
+ AST_APP_ARG(op);
+ );
+ AST_DECLARE_APP_ARGS(inter,
+ AST_APP_ARG(faces)[100];
+ );
+ char *value = ast_strdupa(cvalue);
+
+ AST_STANDARD_APP_ARGS(args, data);
+ AST_NONSTANDARD_APP_ARGS(inter, value, '&');
+
+ if (!(grhead = ao2_find(group_container, data, 0))) {
+ /* Create group */
+ grhead = ao2_alloc(sizeof(*grhead), group_destroy);
+ if (!grhead)
+ return -1;
+ grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn);
+ if (!grhead->entries) {
+ ao2_ref(grhead, -1);
+ return -1;
+ }
+ ast_copy_string(grhead->name, args.group, sizeof(grhead->name));
+ ao2_link(group_container, grhead);
+ }
+
+ if (ast_strlen_zero(args.op)) {
+ /* Wholesale replacement of the group */
+ args.op = "add";
+
+ /* Remove all existing */
+ ao2_ref(grhead->entries, -1);
+ if (!(grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn)))
+ return -1;
+ }
+
+ if (strcasecmp(args.op, "add") == 0) {
+ for (j = 0; j < inter.argc; j++) {
+ if ((entry = ao2_alloc(sizeof(*entry), NULL))) {
+ ast_copy_string(entry->name, inter.faces[j], sizeof(entry->name));
+ ao2_link(grhead->entries, entry);
+ } else
+ ast_log(LOG_WARNING, "Unable to add '%s' to dialgroup '%s'\n", inter.faces[j], grhead->name);
+ }
+ } else if (strncasecmp(args.op, "del", 3) == 0) {
+ for (j = 0; j < inter.argc; j++) {
+ if ((entry = ao2_find(grhead->entries, inter.faces[j], OBJ_UNLINK)))
+ ao2_ref(entry, -1);
+ else
+ ast_log(LOG_WARNING, "Interface '%s' not found in dialgroup '%s'\n", inter.faces[j], grhead->name);
+ }
+ } else
+ ast_log(LOG_ERROR, "Unrecognized operation: %s\n", args.op);
+
+ return 0;
+}
+
+static struct ast_custom_function dialgroup_function = {
+ .name = "DIALGROUP",
+ .synopsis = "Manages a group of users for dialing",
+ .syntax = "DIALGROUP(<group>[,op])",
+ .desc =
+" DIALGROUP presents an interface meant to be used in concert with the Dial\n"
+"application, by presenting a list of channels which should be dialled when\n"
+"referenced.\n"
+" When DIALGROUP is read from, the argument is interpreted as the particular\n"
+"group for which a dial should be attempted. When DIALGROUP is written to\n"
+"with no arguments, the entire list is replaced with the argument specified.\n"
+"Other operations are as follows:\n"
+" add - add a channel name or interface (write-only)\n"
+" del - remove a channel name or interface (write-only)\n\n"
+"Functionality is similar to a queue, except that when no interfaces are\n"
+"available, execution may continue in the dialplan. This is useful when\n"
+"you want certain people to be the first to answer any calls, with immediate\n"
+"fallback to a queue when the front line people are busy or unavailable, but\n"
+"you still want front line people to log in and out of that group, just like\n"
+"a queue.\n",
+ .read = dialgroup_read,
+ .write = dialgroup_write,
+};
+
+static int unload_module(void)
+{
+ int res = ast_custom_function_unregister(&dialgroup_function);
+ ao2_ref(group_container, -1);
+ return res;
+}
+
+static int load_module(void)
+{
+ if ((group_container = ao2_container_alloc(37, group_hash_fn, group_cmp_fn)))
+ return ast_custom_function_register(&dialgroup_function);
+ else
+ return AST_MODULE_LOAD_DECLINE;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialgroup dialplan function");
diff --git a/trunk/funcs/func_dialplan.c b/trunk/funcs/func_dialplan.c
new file mode 100644
index 000000000..6d4c488f6
--- /dev/null
+++ b/trunk/funcs/func_dialplan.c
@@ -0,0 +1,106 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * 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 Dialplan group functions check if a dialplan entry exists
+ *
+ * \author Gregory Nietsky AKA irroot <gregory@networksentry.co.za>
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+
+static int isexten_function_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(context);
+ AST_APP_ARG(exten);
+ AST_APP_ARG(priority);
+ );
+
+ strcpy(buf, "0");
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "DIALPLAN_EXISTS() requires an argument\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.priority)) {
+ int priority_num;
+ if (sscanf(args.priority, "%d", &priority_num) == 1 && priority_num > 0) {
+ int res;
+ res = ast_exists_extension(chan, args.context, args.exten, priority_num,
+ chan->cid.cid_num);
+ if (res)
+ strcpy(buf, "1");
+ } else {
+ int res;
+ res = ast_findlabel_extension(chan, args.context, args.exten,
+ args.priority, chan->cid.cid_num);
+ if (res > 0)
+ strcpy(buf, "1");
+ }
+ } else if (!ast_strlen_zero(args.exten)) {
+ int res;
+ res = ast_exists_extension(chan, args.context, args.exten, 1,
+ chan->cid.cid_num);
+ if (res)
+ strcpy(buf, "1");
+ } else if (!ast_strlen_zero(args.context)) {
+ if (ast_context_find(args.context))
+ strcpy(buf, "1");
+ } else {
+ ast_log(LOG_ERROR, "Invalid arguments provided to DIALPLAN_EXISTS\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function isexten_function = {
+ .name = "DIALPLAN_EXISTS",
+ .syntax = "DIALPLAN_EXISTS(context[,extension[,priority]])",
+ .synopsis = "Checks the existence of a dialplan target.",
+ .desc = "This function returns 1 if the target exits. Otherwise, it returns 0.\n",
+ .read = isexten_function_read,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&isexten_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&isexten_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Context/Extension/Priority Checking Functions");
diff --git a/trunk/funcs/func_enum.c b/trunk/funcs/func_enum.c
new file mode 100644
index 000000000..d69881955
--- /dev/null
+++ b/trunk/funcs/func_enum.c
@@ -0,0 +1,391 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006
+ *
+ * Mark Spencer <markster@digium.com>
+ * Oleksiy Krivoshey <oleksiyk@gmail.com>
+ * Russell Bryant <russelb@clemson.edu>
+ * Brett Bryant <bbryant@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 ENUM Functions
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Oleksiy Krivoshey <oleksiyk@gmail.com>
+ * \author Russell Bryant <russelb@clemson.edu>
+ * \author Brett Bryant <bbryant@digium.com>
+ *
+ * \arg See also AstENUM
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/enum.h"
+#include "asterisk/app.h"
+
+static char *synopsis = "Syntax: ENUMLOOKUP(number[,Method-type[,options[,record#[,zone-suffix]]]])\n";
+
+static int function_enum(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(number);
+ AST_APP_ARG(tech);
+ AST_APP_ARG(options);
+ AST_APP_ARG(record);
+ AST_APP_ARG(zone);
+ );
+ int res = 0;
+ char tech[80];
+ char dest[256] = "", tmp[2] = "", num[AST_MAX_EXTENSION] = "";
+ char *s, *p;
+ unsigned int record = 1;
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, synopsis);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.argc < 1) {
+ ast_log(LOG_WARNING, synopsis);
+ return -1;
+ }
+
+ ast_copy_string(tech, args.tech ? args.tech : "sip", sizeof(tech));
+
+ if (!args.zone)
+ args.zone = "e164.arpa";
+
+ if (!args.options)
+ args.options = "";
+
+ if (args.record)
+ record = atoi(args.record);
+
+ /* strip any '-' signs from number */
+ for (s = p = args.number; *s; s++) {
+ if (*s != '-') {
+ snprintf(tmp, sizeof(tmp), "%c", *s);
+ strncat(num, tmp, sizeof(num));
+ }
+
+ }
+
+ res = ast_get_enum(chan, num, dest, sizeof(dest), tech, sizeof(tech), args.zone, args.options, 1, NULL);
+
+ p = strchr(dest, ':');
+ if (p && strcasecmp(tech, "ALL"))
+ ast_copy_string(buf, p + 1, len);
+ else
+ ast_copy_string(buf, dest, len);
+
+ return 0;
+}
+
+unsigned int enum_datastore_id;
+
+struct enum_result_datastore {
+ struct enum_context *context;
+ unsigned int id;
+};
+
+static void erds_destroy(struct enum_result_datastore *data)
+{
+ int k;
+
+ for (k = 0; k < data->context->naptr_rrs_count; k++) {
+ ast_free(data->context->naptr_rrs[k].result);
+ ast_free(data->context->naptr_rrs[k].tech);
+ }
+
+ ast_free(data->context->naptr_rrs);
+ ast_free(data->context);
+ ast_free(data);
+}
+
+static void erds_destroy_cb(void *data)
+{
+ struct enum_result_datastore *erds = data;
+ erds_destroy(erds);
+}
+
+const struct ast_datastore_info enum_result_datastore_info = {
+ .type = "ENUMQUERY",
+ .destroy = erds_destroy_cb,
+};
+
+static int enum_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct enum_result_datastore *erds;
+ struct ast_datastore *datastore;
+ char *parse, tech[128], dest[128];
+ int res = -1;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(number);
+ AST_APP_ARG(tech);
+ AST_APP_ARG(zone);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ENUMQUERY requires at least a number as an argument...\n");
+ goto finish;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "ENUMQUERY cannot be used without a channel!\n");
+ goto finish;
+ }
+
+ if (!args.zone)
+ args.zone = "e164.zone";
+
+ ast_copy_string(tech, args.tech ? args.tech : "sip", sizeof(tech));
+
+ if (!(erds = ast_calloc(1, sizeof(*erds))))
+ goto finish;
+
+ if (!(erds->context = ast_calloc(1, sizeof(*erds->context)))) {
+ ast_free(erds);
+ goto finish;
+ }
+
+ erds->id = ast_atomic_fetchadd_int((int *) &enum_datastore_id, 1);
+
+ snprintf(buf, len, "%u", erds->id);
+
+ if (!(datastore = ast_channel_datastore_alloc(&enum_result_datastore_info, buf))) {
+ ast_free(erds->context);
+ ast_free(erds);
+ goto finish;
+ }
+
+ ast_get_enum(chan, args.number, dest, sizeof(dest), tech, sizeof(tech), args.zone, "", 1, &erds->context);
+
+ datastore->data = erds;
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+
+ res = 0;
+
+finish:
+
+ return res;
+}
+
+static int enum_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct enum_result_datastore *erds;
+ struct ast_datastore *datastore;
+ char *parse, *p;
+ unsigned int num;
+ int res = -1, k;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(id);
+ AST_APP_ARG(resultnum);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ENUMRESULT requires two arguments (id and resultnum)\n");
+ goto finish;
+ }
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "ENUMRESULT can not be used without a channel!\n");
+ goto finish;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.id)) {
+ ast_log(LOG_ERROR, "A result ID must be provided to ENUMRESULT\n");
+ goto finish;
+ }
+
+ if (ast_strlen_zero(args.resultnum)) {
+ ast_log(LOG_ERROR, "A result number must be given to ENUMRESULT!\n");
+ goto finish;
+ }
+
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &enum_result_datastore_info, args.id);
+ ast_channel_unlock(chan);
+ if (!datastore) {
+ ast_log(LOG_WARNING, "No ENUM results found for query id!\n");
+ goto finish;
+ }
+
+ erds = datastore->data;
+
+ if (!strcasecmp(args.resultnum, "getnum")) {
+ snprintf(buf, len, "%u", erds->context->naptr_rrs_count);
+ res = 0;
+ goto finish;
+ }
+
+ if (sscanf(args.resultnum, "%u", &num) != 1) {
+ ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to ENUMRESULT!\n", args.resultnum);
+ goto finish;
+ }
+
+ if (!num || num > erds->context->naptr_rrs_count) {
+ ast_log(LOG_WARNING, "Result number %u is not valid for ENUM query results for ID %s!\n", num, args.id);
+ goto finish;
+ }
+
+ for (k = 0; k < erds->context->naptr_rrs_count; k++) {
+ if (num - 1 != erds->context->naptr_rrs[k].sort_pos)
+ continue;
+
+ p = strchr(erds->context->naptr_rrs[k].result, ':');
+
+ if (p && strcasecmp(erds->context->naptr_rrs[k].tech, "ALL"))
+ ast_copy_string(buf, p + 1, len);
+ else
+ ast_copy_string(buf, erds->context->naptr_rrs[k].result, len);
+
+ break;
+ }
+
+ res = 0;
+
+finish:
+
+ return res;
+}
+
+static struct ast_custom_function enum_query_function = {
+ .name = "ENUMQUERY",
+ .synopsis = "Initiate an ENUM query",
+ .syntax = "ENUMQUERY(number[,Method-type[,zone-suffix]])",
+ .desc = "This will do a ENUM lookup of the given phone number.\n"
+ "If no method-tpye is given, the default will be sip. If no\n"
+ "zone-suffix is given, the default will be \"e164.arpa\".\n"
+ "The result of this function will be a numeric ID that can\n"
+ "be used to retrieve the results using the ENUMRESULT function.\n",
+ .read = enum_query_read,
+};
+
+static struct ast_custom_function enum_result_function = {
+ .name = "ENUMRESULT",
+ .synopsis = "Retrieve results from a ENUMQUERY",
+ .syntax = "ENUMRESULT(id,resultnum)",
+ .desc = "This function will retrieve results from a previous use\n"
+ "of the ENUMQUERY function.\n"
+ " id - This argument is the identifier returned by the ENUMQUERY function.\n"
+ " resultnum - This is the number of the result that you want to retrieve.\n"
+ " Results start at 1. If this argument is specified as \"getnum\",\n"
+ " then it will return the total number of results that are available.\n",
+ .read = enum_result_read,
+};
+
+static struct ast_custom_function enum_function = {
+ .name = "ENUMLOOKUP",
+ .synopsis =
+ "General or specific querying of NAPTR records for ENUM or ENUM-like DNS pointers",
+ .syntax =
+ "ENUMLOOKUP(number[,Method-type[,options[,record#[,zone-suffix]]]])",
+ .desc =
+ "Option 'c' returns an integer count of the number of NAPTRs of a certain RR type.\n"
+ "Combination of 'c' and Method-type of 'ALL' will return a count of all NAPTRs for the record.\n"
+ "Defaults are: Method-type=sip, no options, record=1, zone-suffix=e164.arpa\n\n"
+ "For more information, see doc/asterisk.pdf",
+ .read = function_enum,
+};
+
+static int function_txtcidname(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ int res;
+ char tech[80];
+ char txt[256] = "";
+ char dest[80];
+
+ buf[0] = '\0';
+
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "TXTCIDNAME requires an argument (number)\n");
+ return -1;
+ }
+
+ res = ast_get_txt(chan, data, dest, sizeof(dest), tech, sizeof(tech), txt,
+ sizeof(txt));
+
+ if (!ast_strlen_zero(txt))
+ ast_copy_string(buf, txt, len);
+
+ return 0;
+}
+
+static struct ast_custom_function txtcidname_function = {
+ .name = "TXTCIDNAME",
+ .synopsis = "TXTCIDNAME looks up a caller name via DNS",
+ .syntax = "TXTCIDNAME(<number>)",
+ .desc =
+ "This function looks up the given phone number in DNS to retrieve\n"
+ "the caller id name. The result will either be blank or be the value\n"
+ "found in the TXT record in DNS.\n",
+ .read = function_txtcidname,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&enum_result_function);
+ res |= ast_custom_function_unregister(&enum_query_function);
+ res |= ast_custom_function_unregister(&enum_function);
+ res |= ast_custom_function_unregister(&txtcidname_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&enum_result_function);
+ res |= ast_custom_function_register(&enum_query_function);
+ res |= ast_custom_function_register(&enum_function);
+ res |= ast_custom_function_register(&txtcidname_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ENUM related dialplan functions");
diff --git a/trunk/funcs/func_env.c b/trunk/funcs/func_env.c
new file mode 100644
index 000000000..4eba211bb
--- /dev/null
+++ b/trunk/funcs/func_env.c
@@ -0,0 +1,215 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Environment related dialplan functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int env_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *ret = NULL;
+
+ *buf = '\0';
+
+ if (data)
+ ret = getenv(data);
+
+ if (ret)
+ ast_copy_string(buf, ret, len);
+
+ return 0;
+}
+
+static int env_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ if (!ast_strlen_zero(data)) {
+ if (!ast_strlen_zero(value)) {
+ setenv(data, value, 1);
+ } else {
+ unsetenv(data);
+ }
+ }
+
+ return 0;
+}
+
+static int stat_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *action;
+ struct stat s;
+
+ ast_copy_string(buf, "0", len);
+
+ action = strsep(&data, ",");
+ if (stat(data, &s)) {
+ return 0;
+ } else {
+ switch (*action) {
+ case 'e':
+ strcpy(buf, "1");
+ break;
+ case 's':
+ snprintf(buf, len, "%d", (unsigned int) s.st_size);
+ break;
+ case 'f':
+ snprintf(buf, len, "%d", S_ISREG(s.st_mode) ? 1 : 0);
+ break;
+ case 'd':
+ snprintf(buf, len, "%d", S_ISDIR(s.st_mode) ? 1 : 0);
+ break;
+ case 'M':
+ snprintf(buf, len, "%d", (int) s.st_mtime);
+ break;
+ case 'A':
+ snprintf(buf, len, "%d", (int) s.st_mtime);
+ break;
+ case 'C':
+ snprintf(buf, len, "%d", (int) s.st_ctime);
+ break;
+ case 'm':
+ snprintf(buf, len, "%o", (int) s.st_mode);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int file_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(offset);
+ AST_APP_ARG(length);
+ );
+ int offset = 0, length;
+ char *contents;
+
+ AST_STANDARD_APP_ARGS(args, data);
+ if (args.argc > 1)
+ offset = atoi(args.offset);
+
+ if (args.argc > 2) {
+ if ((length = atoi(args.length)) < 1) {
+ ast_log(LOG_WARNING, "Invalid length '%s'. Returning the max (%d)\n", args.length, (int)len);
+ length = len;
+ } else if (length > len) {
+ ast_log(LOG_WARNING, "Length %d is greater than the max (%d). Truncating output.\n", length, (int)len);
+ length = len;
+ }
+ } else
+ length = len;
+
+ if (!(contents = ast_read_textfile(args.filename)))
+ return -1;
+
+ if (offset >= 0)
+ ast_copy_string(buf, &contents[offset], length);
+ else {
+ size_t tmp = strlen(contents);
+ if (offset * -1 > tmp) {
+ ast_log(LOG_WARNING, "Offset is larger than the file size.\n");
+ offset = tmp * -1;
+ }
+ ast_copy_string(buf, &contents[tmp + offset], length);
+ }
+ ast_free(contents);
+
+ return 0;
+}
+
+static struct ast_custom_function env_function = {
+ .name = "ENV",
+ .synopsis = "Gets or sets the environment variable specified",
+ .syntax = "ENV(<envname>)",
+ .read = env_read,
+ .write = env_write,
+};
+
+static struct ast_custom_function stat_function = {
+ .name = "STAT",
+ .synopsis = "Does a check on the specified file",
+ .syntax = "STAT(<flag>,<filename>)",
+ .read = stat_read,
+ .desc =
+ "flag may be one of the following:\n"
+ " d - Checks if the file is a directory\n"
+ " e - Checks if the file exists\n"
+ " f - Checks if the file is a regular file\n"
+ " m - Returns the file mode (in octal)\n"
+ " s - Returns the size (in bytes) of the file\n"
+ " A - Returns the epoch at which the file was last accessed\n"
+ " C - Returns the epoch at which the inode was last changed\n"
+ " M - Returns the epoch at which the file was last modified\n",
+};
+
+static struct ast_custom_function file_function = {
+ .name = "FILE",
+ .synopsis = "Obtains the contents of a file",
+ .syntax = "FILE(<filename>,<offset>,<length>)",
+ .read = file_read,
+ /*
+ * Some enterprising programmer could probably add write functionality
+ * to FILE(), although I'm not sure how useful it would be. Hence why
+ * it's called FILE and not READFILE (like the app was).
+ */
+ .desc =
+"<offset> may be specified as any number. If negative, <offset> specifies\n"
+" the number of bytes back from the end of the file.\n"
+"<length>, if specified, will limit the length of the data read to that size.\n",
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&env_function);
+ res |= ast_custom_function_unregister(&stat_function);
+ res |= ast_custom_function_unregister(&file_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&env_function);
+ res |= ast_custom_function_register(&stat_function);
+ res |= ast_custom_function_register(&file_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Environment/filesystem dialplan functions");
diff --git a/trunk/funcs/func_extstate.c b/trunk/funcs/func_extstate.c
new file mode 100644
index 000000000..8866d89b9
--- /dev/null
+++ b/trunk/funcs/func_extstate.c
@@ -0,0 +1,133 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Modified from func_devstate.c by Russell Bryant <russell@digium.com>
+ * Adam Gundy <adam@starsilk.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 Get the state of a hinted extension for dialplan control
+ *
+ * \author Adam Gundy <adam@starsilk.net>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/devicestate.h"
+
+static const char *ast_extstate_str(int state)
+{
+ const char *res = "UNKNOWN";
+
+ switch (state) {
+ case AST_EXTENSION_NOT_INUSE:
+ res = "NOT_INUSE";
+ break;
+ case AST_EXTENSION_INUSE:
+ res = "INUSE";
+ break;
+ case AST_EXTENSION_BUSY:
+ res = "BUSY";
+ break;
+ case AST_EXTENSION_UNAVAILABLE:
+ res = "UNAVAILABLE";
+ break;
+ case AST_EXTENSION_RINGING:
+ res = "RINGING";
+ break;
+ case AST_EXTENSION_INUSE | AST_EXTENSION_RINGING:
+ res = "RINGINUSE";
+ break;
+ case AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD:
+ res = "HOLDINUSE";
+ break;
+ case AST_EXTENSION_ONHOLD:
+ res = "ONHOLD";
+ break;
+ }
+
+ return res;
+}
+
+static int extstate_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *exten, *context;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "EXTENSION_STATE requires an extension\n");
+ return -1;
+ }
+
+ context = exten = data;
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ if (ast_strlen_zero(exten)) {
+ ast_log(LOG_WARNING, "EXTENSION_STATE requires an extension\n");
+ return -1;
+ }
+
+ ast_copy_string(buf,
+ ast_extstate_str(ast_extension_state(chan, context, exten)), len);
+
+ return 0;
+}
+
+static struct ast_custom_function extstate_function = {
+ .name = "EXTENSION_STATE",
+ .synopsis = "Get an extension's state",
+ .syntax = "EXTENSION_STATE(extension[@context])",
+ .desc =
+ " The EXTENSION_STATE function can be used to retrieve the state from any\n"
+ "hinted extension. For example:\n"
+ " NoOp(1234@default has state ${EXTENSION_STATE(1234)})\n"
+ " NoOp(4567@home has state ${EXTENSION_STATE(4567@home)})\n"
+ "\n"
+ " The possible values returned by this function are:\n"
+ "UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
+ "RINGINUSE | HOLDINUSE | ONHOLD\n",
+ .read = extstate_read,
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_custom_function_unregister(&extstate_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_custom_function_register(&extstate_function);
+
+ return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Gets an extension's state in the dialplan");
diff --git a/trunk/funcs/func_global.c b/trunk/funcs/func_global.c
new file mode 100644
index 000000000..1f0053d94
--- /dev/null
+++ b/trunk/funcs/func_global.c
@@ -0,0 +1,82 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Tilghman Lesher
+ *
+ * Tilghman Lesher <func_global__200605@the-tilghman.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 Global variable dialplan functions
+ *
+ * \author Tilghman Lesher <func_global__200605@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int global_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ const char *var = pbx_builtin_getvar_helper(NULL, data);
+
+ *buf = '\0';
+
+ if (var)
+ ast_copy_string(buf, var, len);
+
+ return 0;
+}
+
+static int global_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ pbx_builtin_setvar_helper(NULL, data, value);
+
+ return 0;
+}
+
+static struct ast_custom_function global_function = {
+ .name = "GLOBAL",
+ .synopsis = "Gets or sets the global variable specified",
+ .syntax = "GLOBAL(<varname>)",
+ .read = global_read,
+ .write = global_write,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&global_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&global_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Global variable dialplan functions");
diff --git a/trunk/funcs/func_groupcount.c b/trunk/funcs/func_groupcount.c
new file mode 100644
index 000000000..f0d7df1b5
--- /dev/null
+++ b/trunk/funcs/func_groupcount.c
@@ -0,0 +1,231 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Channel group related dialplan functions
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int group_count_function_read(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ int count = -1;
+ char group[80] = "", category[80] = "";
+
+ ast_app_group_split_group(data, group, sizeof(group), category,
+ sizeof(category));
+
+ /* If no group has been provided let's find one */
+ if (ast_strlen_zero(group)) {
+ struct ast_group_info *gi = NULL;
+
+ ast_app_group_list_rdlock();
+ for (gi = ast_app_group_list_head(); gi; gi = AST_LIST_NEXT(gi, list)) {
+ if (gi->chan != chan)
+ continue;
+ if (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))
+ break;
+ }
+ if (gi) {
+ ast_copy_string(group, gi->group, sizeof(group));
+ if (!ast_strlen_zero(gi->category))
+ ast_copy_string(category, gi->category, sizeof(category));
+ }
+ ast_app_group_list_unlock();
+ }
+
+ if ((count = ast_app_group_get_count(group, category)) == -1)
+ ast_log(LOG_NOTICE, "No group could be found for channel '%s'\n", chan->name);
+ else
+ snprintf(buf, len, "%d", count);
+
+ return 0;
+}
+
+static struct ast_custom_function group_count_function = {
+ .name = "GROUP_COUNT",
+ .syntax = "GROUP_COUNT([groupname][@category])",
+ .synopsis = "Counts the number of channels in the specified group",
+ .desc =
+ "Calculates the group count for the specified group, or uses the\n"
+ "channel's current group if not specifed (and non-empty).\n",
+ .read = group_count_function_read,
+};
+
+static int group_match_count_function_read(struct ast_channel *chan,
+ const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ int count;
+ char group[80] = "";
+ char category[80] = "";
+
+ ast_app_group_split_group(data, group, sizeof(group), category,
+ sizeof(category));
+
+ if (!ast_strlen_zero(group)) {
+ count = ast_app_group_match_get_count(group, category);
+ snprintf(buf, len, "%d", count);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function group_match_count_function = {
+ .name = "GROUP_MATCH_COUNT",
+ .syntax = "GROUP_MATCH_COUNT(groupmatch[@category])",
+ .synopsis =
+ "Counts the number of channels in the groups matching the specified pattern",
+ .desc =
+ "Calculates the group count for all groups that match the specified pattern.\n"
+ "Uses standard regular expression matching (see regex(7)).\n",
+ .read = group_match_count_function_read,
+ .write = NULL,
+};
+
+static int group_function_read(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ struct ast_group_info *gi = NULL;
+
+ ast_app_group_list_rdlock();
+
+ for (gi = ast_app_group_list_head(); gi; gi = AST_LIST_NEXT(gi, list)) {
+ if (gi->chan != chan)
+ continue;
+ if (ast_strlen_zero(data))
+ break;
+ if (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, data))
+ break;
+ }
+
+ if (gi)
+ ast_copy_string(buf, gi->group, len);
+
+ ast_app_group_list_unlock();
+
+ return 0;
+}
+
+static int group_function_write(struct ast_channel *chan, const char *cmd,
+ char *data, const char *value)
+{
+ char grpcat[256];
+
+ if (!ast_strlen_zero(data)) {
+ snprintf(grpcat, sizeof(grpcat), "%s@%s", value, data);
+ } else {
+ ast_copy_string(grpcat, value, sizeof(grpcat));
+ }
+
+ if (ast_app_group_set_channel(chan, grpcat))
+ ast_log(LOG_WARNING,
+ "Setting a group requires an argument (group name)\n");
+
+ return 0;
+}
+
+static struct ast_custom_function group_function = {
+ .name = "GROUP",
+ .syntax = "GROUP([category])",
+ .synopsis = "Gets or sets the channel group.",
+ .desc = "Gets or sets the channel group.\n",
+ .read = group_function_read,
+ .write = group_function_write,
+};
+
+static int group_list_function_read(struct ast_channel *chan, const char *cmd,
+ char *data, char *buf, size_t len)
+{
+ struct ast_group_info *gi = NULL;
+ char tmp1[1024] = "";
+ char tmp2[1024] = "";
+
+ if (!chan)
+ return -1;
+
+ ast_app_group_list_rdlock();
+
+ for (gi = ast_app_group_list_head(); gi; gi = AST_LIST_NEXT(gi, list)) {
+ if (gi->chan != chan)
+ continue;
+ if (!ast_strlen_zero(tmp1)) {
+ ast_copy_string(tmp2, tmp1, sizeof(tmp2));
+ if (!ast_strlen_zero(gi->category))
+ snprintf(tmp1, sizeof(tmp1), "%s %s@%s", tmp2, gi->group, gi->category);
+ else
+ snprintf(tmp1, sizeof(tmp1), "%s %s", tmp2, gi->group);
+ } else {
+ if (!ast_strlen_zero(gi->category))
+ snprintf(tmp1, sizeof(tmp1), "%s@%s", gi->group, gi->category);
+ else
+ snprintf(tmp1, sizeof(tmp1), "%s", gi->group);
+ }
+ }
+
+ ast_app_group_list_unlock();
+
+ ast_copy_string(buf, tmp1, len);
+
+ return 0;
+}
+
+static struct ast_custom_function group_list_function = {
+ .name = "GROUP_LIST",
+ .syntax = "GROUP_LIST()",
+ .synopsis = "Gets a list of the groups set on a channel.",
+ .desc = "Gets a list of the groups set on a channel.\n",
+ .read = group_list_function_read,
+ .write = NULL,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&group_count_function);
+ res |= ast_custom_function_unregister(&group_match_count_function);
+ res |= ast_custom_function_unregister(&group_list_function);
+ res |= ast_custom_function_unregister(&group_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&group_count_function);
+ res |= ast_custom_function_register(&group_match_count_function);
+ res |= ast_custom_function_register(&group_list_function);
+ res |= ast_custom_function_register(&group_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel group dialplan functions");
diff --git a/trunk/funcs/func_iconv.c b/trunk/funcs/func_iconv.c
new file mode 100644
index 000000000..74f32412b
--- /dev/null
+++ b/trunk/funcs/func_iconv.c
@@ -0,0 +1,125 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2005,2006,2007 Sven Slezak <sunny@mezzo.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 Charset conversions
+ *
+ * \author Sven Slezak <sunny@mezzo.net>
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+ <depend>iconv</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <iconv.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+/*!
+ * Some systems define the second arg to iconv() as (const char *),
+ * while others define it as (char *). Cast it to a (void *) to
+ * suppress compiler warnings about it.
+ */
+#define AST_ICONV_CAST void *
+
+static int iconv_read(struct ast_channel *chan, const char *cmd, char *arguments, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(in_charset);
+ AST_APP_ARG(out_charset);
+ AST_APP_ARG(text);
+ );
+ iconv_t cd;
+ size_t incount, outcount = len;
+ char *parse;
+
+ if (ast_strlen_zero(arguments)) {
+ ast_log(LOG_WARNING, "Syntax: ICONV(<in-charset>,<out-charset>,<text>) - missing arguments!\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(arguments);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc < 3) {
+ ast_log(LOG_WARNING, "Syntax: ICONV(<in-charset>,<out-charset>,<text>) %d\n", args.argc);
+ return -1;
+ }
+
+ incount = strlen(args.text);
+
+ ast_debug(1, "Iconv: \"%s\" %s -> %s\n", args.text, args.in_charset, args.out_charset);
+
+ cd = iconv_open(args.out_charset, args.in_charset);
+
+ if (cd == (iconv_t) -1) {
+ ast_log(LOG_ERROR, "conversion from '%s' to '%s' not available. type 'iconv -l' in a shell to list the supported charsets.\n", args.in_charset, args.out_charset);
+ return -1;
+ }
+
+ if (iconv(cd, (AST_ICONV_CAST) &args.text, &incount, &buf, &outcount) == (size_t) -1) {
+ if (errno == E2BIG)
+ ast_log(LOG_WARNING, "Iconv: output buffer too small.\n");
+ else if (errno == EILSEQ)
+ ast_log(LOG_WARNING, "Iconv: illegal character.\n");
+ else if (errno == EINVAL)
+ ast_log(LOG_WARNING, "Iconv: incomplete character sequence.\n");
+ else
+ ast_log(LOG_WARNING, "Iconv: error %d: %s.\n", errno, strerror(errno));
+ }
+ iconv_close(cd);
+
+ return 0;
+}
+
+
+static struct ast_custom_function iconv_function = {
+ .name = "ICONV",
+ .synopsis = "Converts charsets of strings.",
+ .desc =
+"Converts string from in-charset into out-charset. For available charsets,\n"
+"use 'iconv -l' on your shell command line.\n"
+"Note: due to limitations within the API, ICONV will not currently work with\n"
+"charsets with embedded NULLs. If found, the string will terminate.\n",
+ .syntax = "ICONV(in-charset,out-charset,string)",
+ .read = iconv_read,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&iconv_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&iconv_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Charset conversions");
+
diff --git a/trunk/funcs/func_lock.c b/trunk/funcs/func_lock.c
new file mode 100644
index 000000000..53b05a3e8
--- /dev/null
+++ b/trunk/funcs/func_lock.c
@@ -0,0 +1,350 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Tilghman Lesher
+ *
+ * Tilghman Lesher <func_lock_2007@the-tilghman.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 Dialplan mutexes
+ *
+ * \author Tilghman Lesher <func_lock_2007@the-tilghman.com>
+ *
+ * \ingroup functions
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/linkedlists.h"
+
+AST_LIST_HEAD_STATIC(locklist, lock_frame);
+
+static void lock_free(void *data);
+static int unloading = 0;
+
+static struct ast_datastore_info lock_info = {
+ .type = "MUTEX",
+ .destroy = lock_free,
+};
+
+struct lock_frame {
+ AST_LIST_ENTRY(lock_frame) entries;
+ ast_mutex_t mutex;
+ /*! count is needed so if a recursive mutex exits early, we know how many times to unlock it. */
+ unsigned int count;
+ /*! who owns us */
+ struct ast_channel *channel;
+ /*! name of the lock */
+ char name[0];
+};
+
+struct channel_lock_frame {
+ AST_LIST_ENTRY(channel_lock_frame) list;
+ /*! Need to save channel pointer here, because during destruction, we won't have it. */
+ struct ast_channel *channel;
+ struct lock_frame *lock_frame;
+};
+
+static void lock_free(void *data)
+{
+ AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
+ struct channel_lock_frame *clframe;
+ AST_LIST_LOCK(oldlist);
+ while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
+ /* Only unlock if we own the lock */
+ if (clframe->channel == clframe->lock_frame->channel) {
+ clframe->lock_frame->channel = NULL;
+ while (clframe->lock_frame->count > 0) {
+ clframe->lock_frame->count--;
+ ast_mutex_unlock(&clframe->lock_frame->mutex);
+ }
+ }
+ ast_free(clframe);
+ }
+ AST_LIST_UNLOCK(oldlist);
+ AST_LIST_HEAD_DESTROY(oldlist);
+ ast_free(oldlist);
+}
+
+static int get_lock(struct ast_channel *chan, char *lockname, int try)
+{
+ struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
+ struct lock_frame *current;
+ struct channel_lock_frame *clframe = NULL, *save_clframe = NULL;
+ AST_LIST_HEAD(, channel_lock_frame) *list;
+ int res, count_channel_locks = 0;
+
+ if (!lock_store) {
+ ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
+ lock_store = ast_channel_datastore_alloc(&lock_info, NULL);
+ if (!lock_store) {
+ ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
+ return -1;
+ }
+
+ list = ast_calloc(1, sizeof(*list));
+ if (!list) {
+ ast_log(LOG_ERROR, "Unable to allocate datastore list head. %sLOCK will fail.\n", try ? "TRY" : "");
+ ast_channel_datastore_free(lock_store);
+ return -1;
+ }
+
+ lock_store->data = list;
+ AST_LIST_HEAD_INIT(list);
+ ast_channel_datastore_add(chan, lock_store);
+ } else
+ list = lock_store->data;
+
+ /* Lock already exists? */
+ AST_LIST_LOCK(&locklist);
+ AST_LIST_TRAVERSE(&locklist, current, entries) {
+ if (strcmp(current->name, lockname) == 0) {
+ break;
+ }
+ }
+
+ if (!current) {
+ if (unloading) {
+ /* Don't bother */
+ AST_LIST_UNLOCK(&locklist);
+ return -1;
+ }
+
+ /* Create new lock entry */
+ current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
+ if (!current) {
+ AST_LIST_UNLOCK(&locklist);
+ return -1;
+ }
+
+ strcpy((char *)current + sizeof(*current), lockname);
+ ast_mutex_init(&current->mutex);
+ AST_LIST_INSERT_TAIL(&locklist, current, entries);
+ }
+ AST_LIST_UNLOCK(&locklist);
+
+ /* Found lock or created one - now find or create the corresponding link in the channel */
+ AST_LIST_LOCK(list);
+ AST_LIST_TRAVERSE(list, clframe, list) {
+ if (clframe->lock_frame == current)
+ save_clframe = clframe;
+
+ /* Only count mutexes that we currently hold */
+ if (clframe->lock_frame->channel == chan)
+ count_channel_locks++;
+ }
+
+ if (save_clframe) {
+ clframe = save_clframe;
+ } else {
+ if (unloading) {
+ /* Don't bother */
+ AST_LIST_UNLOCK(list);
+ return -1;
+ }
+
+ clframe = ast_calloc(1, sizeof(*clframe));
+ if (!clframe) {
+ ast_log(LOG_ERROR, "Unable to allocate channel lock frame. %sLOCK will fail.\n", try ? "TRY" : "");
+ AST_LIST_UNLOCK(list);
+ return -1;
+ }
+
+ clframe->lock_frame = current;
+ clframe->channel = chan;
+ /* Count the lock just created */
+ count_channel_locks++;
+ AST_LIST_INSERT_TAIL(list, clframe, list);
+ }
+ AST_LIST_UNLOCK(list);
+
+ /* Okay, we have both frames, so now we need to try to lock the mutex. */
+ if (count_channel_locks > 1) {
+ struct timeval start = ast_tvnow();
+ for (;;) {
+ if ((res = ast_mutex_trylock(&current->mutex)) == 0)
+ break;
+ if (ast_tvdiff_ms(ast_tvnow(), start) > 3000)
+ break; /* bail after 3 seconds of waiting */
+ usleep(1);
+ }
+ } else {
+ /* If the channel doesn't have any locks so far, then there's no possible deadlock. */
+ res = try ? ast_mutex_trylock(&current->mutex) : ast_mutex_lock(&current->mutex);
+ }
+
+ if (res == 0) {
+ current->count++;
+ current->channel = chan;
+ }
+
+ return res;
+}
+
+static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
+ struct channel_lock_frame *clframe;
+ AST_LIST_HEAD(, channel_lock_frame) *list;
+
+ if (!lock_store) {
+ ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
+ ast_copy_string(buf, "0", len);
+ return 0;
+ }
+
+ if (!(list = lock_store->data)) {
+ ast_debug(1, "This should NEVER happen\n");
+ ast_copy_string(buf, "0", len);
+ return 0;
+ }
+
+ /* Find item in the channel list */
+ AST_LIST_LOCK(list);
+ AST_LIST_TRAVERSE(list, clframe, list) {
+ if (clframe->lock_frame && clframe->lock_frame->channel == chan && strcmp(clframe->lock_frame->name, data) == 0) {
+ break;
+ }
+ }
+ /* We never destroy anything until channel destruction, which will never
+ * happen while this routine is executing, so we don't need to hold the
+ * lock beyond this point. */
+ AST_LIST_UNLOCK(list);
+
+ if (!clframe) {
+ /* We didn't have this lock in the first place */
+ ast_copy_string(buf, "0", len);
+ return 0;
+ }
+
+ /* Decrement before we release, because if a channel is waiting on the
+ * mutex, there's otherwise a race to alter count. */
+ clframe->lock_frame->count--;
+ /* If we get another lock, this one shouldn't count against us for deadlock avoidance. */
+ clframe->lock_frame->channel = NULL;
+ ast_mutex_unlock(&clframe->lock_frame->mutex);
+
+ ast_copy_string(buf, "1", len);
+ return 0;
+}
+
+static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ if (chan)
+ ast_autoservice_start(chan);
+
+ ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ if (chan)
+ ast_autoservice_start(chan);
+
+ ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function lock_function = {
+ .name = "LOCK",
+ .synopsis = "Attempt to obtain a named mutex",
+ .desc =
+"Attempts to grab a named lock exclusively, and prevents other channels from\n"
+"obtaining the same lock. LOCK will wait for the lock to become available.\n"
+"Returns 1 if the lock was obtained or 0 on error.\n\n"
+"Note: to avoid the possibility of a deadlock, LOCK will only attempt to\n"
+"obtain the lock for 3 seconds if the channel already has another lock.\n",
+ .syntax = "LOCK(<lockname>)",
+ .read = lock_read,
+};
+
+static struct ast_custom_function trylock_function = {
+ .name = "TRYLOCK",
+ .synopsis = "Attempt to obtain a named mutex",
+ .desc =
+"Attempts to grab a named lock exclusively, and prevents other channels\n"
+"from obtaining the same lock. Returns 1 if the lock was available or 0\n"
+"otherwise.\n",
+ .syntax = "TRYLOCK(<lockname>)",
+ .read = trylock_read,
+};
+
+static struct ast_custom_function unlock_function = {
+ .name = "UNLOCK",
+ .synopsis = "Unlocks a named mutex",
+ .desc =
+"Unlocks a previously locked mutex. Note that it is generally unnecessary to\n"
+"unlock in a hangup routine, as any locks held are automatically freed when the\n"
+"channel is destroyed. Returns 1 if the channel had a lock or 0 otherwise.\n",
+ .syntax = "UNLOCK(<lockname>)",
+ .read = unlock_read,
+};
+
+static int unload_module(void)
+{
+ struct lock_frame *current;
+
+ /* Module flag */
+ unloading = 1;
+
+ AST_LIST_LOCK(&locklist);
+ while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
+ /* If any locks are currently in use, then we cannot unload this module */
+ if (current->channel) {
+ /* Put it back */
+ AST_LIST_INSERT_HEAD(&locklist, current, entries);
+ AST_LIST_UNLOCK(&locklist);
+ unloading = 0;
+ return -1;
+ }
+ ast_mutex_destroy(&current->mutex);
+ ast_free(current);
+ }
+
+ /* No locks left, unregister functions */
+ ast_custom_function_unregister(&lock_function);
+ ast_custom_function_unregister(&trylock_function);
+ ast_custom_function_unregister(&unlock_function);
+
+ AST_LIST_UNLOCK(&locklist);
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res = ast_custom_function_register(&lock_function);
+ res |= ast_custom_function_register(&trylock_function);
+ res |= ast_custom_function_register(&unlock_function);
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");
diff --git a/trunk/funcs/func_logic.c b/trunk/funcs/func_logic.c
new file mode 100644
index 000000000..7fca070aa
--- /dev/null
+++ b/trunk/funcs/func_logic.c
@@ -0,0 +1,242 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Portions Copyright (C) 2005, Anthony Minessale II
+ *
+ * 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 Conditional logic dialplan functions
+ *
+ * \author Anthony Minessale II
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int isnull(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ strcpy(buf, data && *data ? "0" : "1");
+
+ return 0;
+}
+
+static int exists(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ strcpy(buf, data && *data ? "1" : "0");
+
+ return 0;
+}
+
+static int iftime(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ struct ast_timing timing;
+ char *expr;
+ char *iftrue;
+ char *iffalse;
+
+ data = ast_strip_quoted(data, "\"", "\"");
+ expr = strsep(&data, "?");
+ iftrue = strsep(&data, ":");
+ iffalse = data;
+
+ if (ast_strlen_zero(expr) || !(iftrue || iffalse)) {
+ ast_log(LOG_WARNING,
+ "Syntax IFTIME(<timespec>?[<true>][:<false>])\n");
+ return -1;
+ }
+
+ if (!ast_build_timing(&timing, expr)) {
+ ast_log(LOG_WARNING, "Invalid Time Spec.\n");
+ return -1;
+ }
+
+ if (iftrue)
+ iftrue = ast_strip_quoted(iftrue, "\"", "\"");
+ if (iffalse)
+ iffalse = ast_strip_quoted(iffalse, "\"", "\"");
+
+ ast_copy_string(buf, ast_check_timing(&timing) ? iftrue : iffalse, len);
+
+ return 0;
+}
+
+static int acf_if(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ AST_DECLARE_APP_ARGS(args1,
+ AST_APP_ARG(expr);
+ AST_APP_ARG(remainder);
+ );
+ AST_DECLARE_APP_ARGS(args2,
+ AST_APP_ARG(iftrue);
+ AST_APP_ARG(iffalse);
+ );
+ args2.iftrue = args2.iffalse = NULL; /* you have to set these, because if there is nothing after the '?',
+ then args1.remainder will be NULL, not a pointer to a null string, and
+ then any garbage in args2.iffalse will not be cleared, and you'll crash.
+ -- and if you mod the ast_app_separate_args func instead, you'll really
+ mess things up badly, because the rest of everything depends on null args
+ for non-specified stuff. */
+
+ AST_NONSTANDARD_APP_ARGS(args1, data, '?');
+ AST_NONSTANDARD_APP_ARGS(args2, args1.remainder, ':');
+
+ if (ast_strlen_zero(args1.expr) || !(args2.iftrue || args2.iffalse)) {
+ ast_log(LOG_WARNING, "Syntax IF(<expr>?[<true>][:<false>]) (expr must be non-null, and either <true> or <false> must be non-null)\n");
+ ast_log(LOG_WARNING, " In this case, <expr>='%s', <true>='%s', and <false>='%s'\n", args1.expr, args2.iftrue, args2.iffalse);
+ return -1;
+ }
+
+ args1.expr = ast_strip(args1.expr);
+ if (args2.iftrue)
+ args2.iftrue = ast_strip(args2.iftrue);
+ if (args2.iffalse)
+ args2.iffalse = ast_strip(args2.iffalse);
+
+ ast_copy_string(buf, pbx_checkcondition(args1.expr) ? (S_OR(args2.iftrue, "")) : (S_OR(args2.iffalse, "")), len);
+
+ return 0;
+}
+
+static int set(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ char *varname;
+ char *val;
+
+ varname = strsep(&data, "=");
+ val = data;
+
+ if (ast_strlen_zero(varname) || !val) {
+ ast_log(LOG_WARNING, "Syntax SET(<varname>=[<value>])\n");
+ return -1;
+ }
+
+ varname = ast_strip(varname);
+ val = ast_strip(val);
+ pbx_builtin_setvar_helper(chan, varname, val);
+ ast_copy_string(buf, val, len);
+
+ return 0;
+}
+
+static int acf_import(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(channel);
+ AST_APP_ARG(varname);
+ );
+ AST_STANDARD_APP_ARGS(args, data);
+ buf[0] = 0;
+ if (!ast_strlen_zero(args.varname)) {
+ struct ast_channel *chan2 = ast_get_channel_by_name_locked(args.channel);
+ if (chan2) {
+ char *s = alloca(strlen(args.varname) + 4);
+ if (s) {
+ sprintf(s, "${%s}", args.varname);
+ pbx_substitute_variables_helper(chan2, s, buf, len);
+ }
+ ast_channel_unlock(chan2);
+ }
+ }
+ return 0;
+}
+
+static struct ast_custom_function isnull_function = {
+ .name = "ISNULL",
+ .synopsis = "NULL Test: Returns 1 if NULL or 0 otherwise",
+ .syntax = "ISNULL(<data>)",
+ .read = isnull,
+};
+
+static struct ast_custom_function set_function = {
+ .name = "SET",
+ .synopsis = "SET assigns a value to a channel variable",
+ .syntax = "SET(<varname>=[<value>])",
+ .read = set,
+};
+
+static struct ast_custom_function exists_function = {
+ .name = "EXISTS",
+ .synopsis = "Existence Test: Returns 1 if exists, 0 otherwise",
+ .syntax = "EXISTS(<data>)",
+ .read = exists,
+};
+
+static struct ast_custom_function if_function = {
+ .name = "IF",
+ .synopsis =
+ "Conditional: Returns the data following '?' if true, else the data following ':'",
+ .syntax = "IF(<expr>?[<true>][:<false>])",
+ .read = acf_if,
+};
+
+static struct ast_custom_function if_time_function = {
+ .name = "IFTIME",
+ .synopsis =
+ "Temporal Conditional: Returns the data following '?' if true, else the data following ':'",
+ .syntax = "IFTIME(<timespec>?[<true>][:<false>])",
+ .read = iftime,
+};
+
+static struct ast_custom_function import_function = {
+ .name = "IMPORT",
+ .synopsis =
+ "Retrieve the value of a variable from another channel\n",
+ .syntax = "IMPORT(channel,variable)",
+ .read = acf_import,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&isnull_function);
+ res |= ast_custom_function_unregister(&set_function);
+ res |= ast_custom_function_unregister(&exists_function);
+ res |= ast_custom_function_unregister(&if_function);
+ res |= ast_custom_function_unregister(&if_time_function);
+ res |= ast_custom_function_unregister(&import_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&isnull_function);
+ res |= ast_custom_function_register(&set_function);
+ res |= ast_custom_function_register(&exists_function);
+ res |= ast_custom_function_register(&if_function);
+ res |= ast_custom_function_register(&if_time_function);
+ res |= ast_custom_function_register(&import_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Logical dialplan functions");
diff --git a/trunk/funcs/func_math.c b/trunk/funcs/func_math.c
new file mode 100644
index 000000000..45036d5ad
--- /dev/null
+++ b/trunk/funcs/func_math.c
@@ -0,0 +1,333 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2006, Andy Powell
+ *
+ * Updated by 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 Math related dialplan function
+ *
+ * \author Andy Powell
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+
+enum TypeOfFunctions {
+ ADDFUNCTION,
+ DIVIDEFUNCTION,
+ MULTIPLYFUNCTION,
+ SUBTRACTFUNCTION,
+ MODULUSFUNCTION,
+ POWFUNCTION,
+ SHLEFTFUNCTION,
+ SHRIGHTFUNCTION,
+ BITWISEANDFUNCTION,
+ BITWISEXORFUNCTION,
+ BITWISEORFUNCTION,
+ GTFUNCTION,
+ LTFUNCTION,
+ GTEFUNCTION,
+ LTEFUNCTION,
+ EQFUNCTION
+};
+
+enum TypeOfResult {
+ FLOAT_RESULT,
+ INT_RESULT,
+ HEX_RESULT,
+ CHAR_RESULT
+};
+
+static int math(struct ast_channel *chan, const char *cmd, char *parse,
+ char *buf, size_t len)
+{
+ double fnum1;
+ double fnum2;
+ double ftmp = 0;
+ char *op;
+ int iaction = -1;
+ int type_of_result = FLOAT_RESULT;
+ char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
+ int negvalue1 = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(argv0);
+ AST_APP_ARG(argv1);
+ );
+
+ if (ast_strlen_zero(parse)) {
+ ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc < 1) {
+ ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
+ return -1;
+ }
+
+ mvalue1 = args.argv0;
+
+ if (mvalue1[0] == '-') {
+ negvalue1 = 1;
+ mvalue1++;
+ }
+
+ if ((op = strchr(mvalue1, '*'))) {
+ iaction = MULTIPLYFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '/'))) {
+ iaction = DIVIDEFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '%'))) {
+ iaction = MODULUSFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '^'))) {
+ iaction = POWFUNCTION;
+ *op = '\0';
+ } else if ((op = strstr(mvalue1, "AND"))) {
+ iaction = BITWISEANDFUNCTION;
+ op += 3;
+ *op = '\0';
+ } else if ((op = strstr(mvalue1, "XOR"))) {
+ iaction = BITWISEXORFUNCTION;
+ op += 3;
+ *op = '\0';
+ } else if ((op = strstr(mvalue1, "OR"))) {
+ iaction = BITWISEORFUNCTION;
+ op += 2;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '>'))) {
+ iaction = GTFUNCTION;
+ *op = '\0';
+ if (*(op + 1) == '=') {
+ *++op = '\0';
+ iaction = GTEFUNCTION;
+ } else if (*(op + 1) == '>') {
+ *++op = '\0';
+ iaction = SHRIGHTFUNCTION;
+ }
+ } else if ((op = strchr(mvalue1, '<'))) {
+ iaction = LTFUNCTION;
+ *op = '\0';
+ if (*(op + 1) == '=') {
+ *++op = '\0';
+ iaction = LTEFUNCTION;
+ } else if (*(op + 1) == '<') {
+ *++op = '\0';
+ iaction = SHLEFTFUNCTION;
+ }
+ } else if ((op = strchr(mvalue1, '='))) {
+ *op = '\0';
+ if (*(op + 1) == '=') {
+ *++op = '\0';
+ iaction = EQFUNCTION;
+ } else
+ op = NULL;
+ } else if ((op = strchr(mvalue1, '+'))) {
+ iaction = ADDFUNCTION;
+ *op = '\0';
+ } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative first number */
+ iaction = SUBTRACTFUNCTION;
+ *op = '\0';
+ }
+
+ if (op)
+ mvalue2 = op + 1;
+
+ /* detect wanted type of result */
+ mtype_of_result = args.argv1;
+ if (mtype_of_result) {
+ if (!strcasecmp(mtype_of_result, "float")
+ || !strcasecmp(mtype_of_result, "f"))
+ type_of_result = FLOAT_RESULT;
+ else if (!strcasecmp(mtype_of_result, "int")
+ || !strcasecmp(mtype_of_result, "i"))
+ type_of_result = INT_RESULT;
+ else if (!strcasecmp(mtype_of_result, "hex")
+ || !strcasecmp(mtype_of_result, "h"))
+ type_of_result = HEX_RESULT;
+ else if (!strcasecmp(mtype_of_result, "char")
+ || !strcasecmp(mtype_of_result, "c"))
+ type_of_result = CHAR_RESULT;
+ else {
+ ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
+ mtype_of_result);
+ return -1;
+ }
+ }
+
+ if (!mvalue1 || !mvalue2) {
+ ast_log(LOG_WARNING,
+ "Supply all the parameters - just this once, please\n");
+ return -1;
+ }
+
+ if (sscanf(mvalue1, "%lf", &fnum1) != 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
+ return -1;
+ }
+
+ if (sscanf(mvalue2, "%lf", &fnum2) != 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
+ return -1;
+ }
+
+ if (negvalue1)
+ fnum1 = 0 - fnum1;
+
+ switch (iaction) {
+ case ADDFUNCTION:
+ ftmp = fnum1 + fnum2;
+ break;
+ case DIVIDEFUNCTION:
+ if (fnum2 <= 0)
+ ftmp = 0; /* can't do a divide by 0 */
+ else
+ ftmp = (fnum1 / fnum2);
+ break;
+ case MULTIPLYFUNCTION:
+ ftmp = (fnum1 * fnum2);
+ break;
+ case SUBTRACTFUNCTION:
+ ftmp = (fnum1 - fnum2);
+ break;
+ case MODULUSFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+
+ ftmp = (inum1 % inum2);
+
+ break;
+ }
+ case POWFUNCTION:
+ ftmp = pow(fnum1, fnum2);
+ break;
+ case SHLEFTFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+
+ ftmp = (inum1 << inum2);
+ break;
+ }
+ case SHRIGHTFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+
+ ftmp = (inum1 >> inum2);
+ break;
+ }
+ case BITWISEANDFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+ ftmp = (inum1 & inum2);
+ break;
+ }
+ case BITWISEXORFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+ ftmp = (inum1 ^ inum2);
+ break;
+ }
+ case BITWISEORFUNCTION:
+ {
+ int inum1 = fnum1;
+ int inum2 = fnum2;
+ ftmp = (inum1 | inum2);
+ break;
+ }
+ case GTFUNCTION:
+ ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case LTFUNCTION:
+ ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case GTEFUNCTION:
+ ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case LTEFUNCTION:
+ ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ case EQFUNCTION:
+ ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
+ break;
+ default:
+ ast_log(LOG_WARNING,
+ "Something happened that neither of us should be proud of %d\n",
+ iaction);
+ return -1;
+ }
+
+ if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
+ if (type_of_result == FLOAT_RESULT)
+ snprintf(buf, len, "%f", ftmp);
+ else if (type_of_result == INT_RESULT)
+ snprintf(buf, len, "%i", (int) ftmp);
+ else if (type_of_result == HEX_RESULT)
+ snprintf(buf, len, "%x", (unsigned int) ftmp);
+ else if (type_of_result == CHAR_RESULT)
+ snprintf(buf, len, "%c", (unsigned char) ftmp);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function math_function = {
+ .name = "MATH",
+ .synopsis = "Performs Mathematical Functions",
+ .syntax = "MATH(<number1><op><number2>[,<type_of_result>])",
+ .desc = "Perform calculation on number1 to number2. Valid ops are: \n"
+ " +,-,/,*,%,<<,>>,^,AND,OR,XOR,<,>,>=,<=,==\n"
+ "and behave as their C equivalents.\n"
+ "<type_of_result> - wanted type of result:\n"
+ " f, float - float(default)\n"
+ " i, int - integer,\n"
+ " h, hex - hex,\n"
+ " c, char - char\n"
+ "Example: Set(i=${MATH(123%16,int)}) - sets var i=11",
+ .read = math
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&math_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&math_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");
diff --git a/trunk/funcs/func_md5.c b/trunk/funcs/func_md5.c
new file mode 100644
index 000000000..b376e8ae3
--- /dev/null
+++ b/trunk/funcs/func_md5.c
@@ -0,0 +1,67 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Digium, Inc.
+ * Copyright (C) 2005, Olle E. Johansson, Edvina.net
+ * Copyright (C) 2005, Russell Bryant <russelb@clemson.edu>
+ *
+ * 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 MD5 digest related dialplan functions
+ *
+ * \author Olle E. Johansson <oej@edvina.net>
+ * \author Russell Bryant <russelb@clemson.edu>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int md5(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: MD5(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_md5_hash(buf, data);
+ buf[32] = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function md5_function = {
+ .name = "MD5",
+ .synopsis = "Computes an MD5 digest",
+ .syntax = "MD5(<data>)",
+ .read = md5,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&md5_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&md5_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MD5 digest dialplan functions");
diff --git a/trunk/funcs/func_module.c b/trunk/funcs/func_module.c
new file mode 100644
index 000000000..8b2326386
--- /dev/null
+++ b/trunk/funcs/func_module.c
@@ -0,0 +1,68 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Simple module check function
+ * \author Olle E. Johansson, Edvina.net
+ *
+ * \ingroup functions
+ */
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int ifmodule_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ char *ret = "0";
+
+ *buf = '\0';
+
+ if (data)
+ if (ast_module_check(data))
+ ret = "1";
+
+ ast_copy_string(buf, ret, len);
+
+ return 0;
+}
+
+static struct ast_custom_function ifmodule_function = {
+ .name = "IFMODULE",
+ .synopsis = "Checks if an Asterisk module is loaded in memory",
+ .syntax = "IFMODULE(<modulename.so>)",
+ .read = ifmodule_read,
+ .desc = "Checks if a module is loaded. Use the full module name\n"
+ "as shown by the list in \"module list\". \n"
+ "Returns \"1\" if module exists in memory, otherwise \"0\".\n",
+};
+
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&ifmodule_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&ifmodule_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Checks if Asterisk module is loaded in memory");
diff --git a/trunk/funcs/func_odbc.c b/trunk/funcs/func_odbc.c
new file mode 100644
index 000000000..af4bab069
--- /dev/null
+++ b/trunk/funcs/func_odbc.c
@@ -0,0 +1,900 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2005, 2006 Tilghman Lesher
+ *
+ * Tilghman Lesher <func_odbc__200508@the-tilghman.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 ODBC lookups
+ *
+ * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+ <depend>unixodbc</depend>
+ <depend>ltdl</depend>
+ <depend>res_odbc</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/res_odbc.h"
+#include "asterisk/app.h"
+
+static char *config = "func_odbc.conf";
+
+enum {
+ OPT_ESCAPECOMMAS = (1 << 0),
+ OPT_MULTIROW = (1 << 1),
+} odbc_option_flags;
+
+struct acf_odbc_query {
+ AST_LIST_ENTRY(acf_odbc_query) list;
+ char readhandle[5][30];
+ char writehandle[5][30];
+ char sql_read[2048];
+ char sql_write[2048];
+ unsigned int flags;
+ int rowlimit;
+ struct ast_custom_function *acf;
+};
+
+static void odbc_datastore_free(void *data);
+
+struct ast_datastore_info odbc_info = {
+ .type = "FUNC_ODBC",
+ .destroy = odbc_datastore_free,
+};
+
+/* For storing each result row */
+struct odbc_datastore_row {
+ AST_LIST_ENTRY(odbc_datastore_row) list;
+ char data[0];
+};
+
+/* For storing each result set */
+struct odbc_datastore {
+ AST_LIST_HEAD(, odbc_datastore_row);
+ char names[0];
+};
+
+AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
+
+static int resultcount = 0;
+
+static void odbc_datastore_free(void *data)
+{
+ struct odbc_datastore *result = data;
+ struct odbc_datastore_row *row;
+ AST_LIST_LOCK(result);
+ while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
+ ast_free(row);
+ }
+ AST_LIST_UNLOCK(result);
+ AST_LIST_HEAD_DESTROY(result);
+ ast_free(result);
+}
+
+static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
+{
+ int res;
+ char *sql = data;
+ SQLHSTMT stmt;
+
+ res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ return NULL;
+ }
+
+ res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Exec Direct failed![%s]\n", sql);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
+/*
+ * Master control routine
+ */
+static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
+{
+ struct odbc_obj *obj = NULL;
+ struct acf_odbc_query *query;
+ char *t, buf[2048], varname[15];
+ int i, dsn, bogus_chan = 0;
+ AST_DECLARE_APP_ARGS(values,
+ AST_APP_ARG(field)[100];
+ );
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(field)[100];
+ );
+ SQLHSTMT stmt = NULL;
+ SQLLEN rows=0;
+
+ AST_LIST_LOCK(&queries);
+ AST_LIST_TRAVERSE(&queries, query, list) {
+ if (!strcmp(query->acf->name, cmd)) {
+ break;
+ }
+ }
+
+ if (!query) {
+ ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
+ AST_LIST_UNLOCK(&queries);
+ return -1;
+ }
+
+ if (!chan) {
+ if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
+ bogus_chan = 1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ /* Parse our arguments */
+ t = value ? ast_strdupa(value) : "";
+
+ if (!s || !t) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ AST_LIST_UNLOCK(&queries);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, s);
+ for (i = 0; i < args.argc; i++) {
+ snprintf(varname, sizeof(varname), "ARG%d", i + 1);
+ pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
+ }
+
+ /* Parse values, just like arguments */
+ AST_STANDARD_APP_ARGS(values, t);
+ for (i = 0; i < values.argc; i++) {
+ snprintf(varname, sizeof(varname), "VAL%d", i + 1);
+ pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
+ }
+
+ /* Additionally set the value as a whole (but push an empty string if value is NULL) */
+ pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
+
+ pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
+
+ /* Restore prior values */
+ for (i = 0; i < args.argc; i++) {
+ snprintf(varname, sizeof(varname), "ARG%d", i + 1);
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+
+ for (i = 0; i < values.argc; i++) {
+ snprintf(varname, sizeof(varname), "VAL%d", i + 1);
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+ pbx_builtin_setvar_helper(chan, "VALUE", NULL);
+
+ AST_LIST_UNLOCK(&queries);
+
+ for (dsn = 0; dsn < 5; dsn++) {
+ if (!ast_strlen_zero(query->writehandle[dsn])) {
+ obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
+ if (obj)
+ stmt = ast_odbc_direct_execute(obj, generic_execute, buf);
+ }
+ if (stmt)
+ break;
+ }
+
+ if (stmt) {
+ /* Rows affected */
+ SQLRowCount(stmt, &rows);
+ }
+
+ /* Output the affected rows, for all cases. In the event of failure, we
+ * flag this as -1 rows. Note that this is different from 0 affected rows
+ * which would be the case if we succeeded in our query, but the values did
+ * not change. */
+ snprintf(varname, sizeof(varname), "%d", (int)rows);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
+
+ if (stmt) {
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ }
+ if (obj)
+ ast_odbc_release_obj(obj);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+
+ return 0;
+}
+
+static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
+{
+ struct odbc_obj *obj = NULL;
+ struct acf_odbc_query *query;
+ char sql[2048], varname[15], colnames[2048] = "", rowcount[12] = "-1";
+ int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(field)[100];
+ );
+ SQLHSTMT stmt = NULL;
+ SQLSMALLINT colcount=0;
+ SQLLEN indicator;
+ SQLSMALLINT collength;
+ struct odbc_datastore *resultset = NULL;
+ struct odbc_datastore_row *row = NULL;
+
+ AST_LIST_LOCK(&queries);
+ AST_LIST_TRAVERSE(&queries, query, list) {
+ if (!strcmp(query->acf->name, cmd)) {
+ break;
+ }
+ }
+
+ if (!query) {
+ ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
+ AST_LIST_UNLOCK(&queries);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ return -1;
+ }
+
+ if (!chan) {
+ if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
+ bogus_chan = 1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ AST_STANDARD_APP_ARGS(args, s);
+ for (x = 0; x < args.argc; x++) {
+ snprintf(varname, sizeof(varname), "ARG%d", x + 1);
+ pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
+ }
+
+ pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
+
+ /* Restore prior values */
+ for (x = 0; x < args.argc; x++) {
+ snprintf(varname, sizeof(varname), "ARG%d", x + 1);
+ pbx_builtin_setvar_helper(chan, varname, NULL);
+ }
+
+ /* Save these flags, so we can release the lock */
+ escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
+ if (ast_test_flag(query, OPT_MULTIROW)) {
+ resultset = ast_calloc(1, sizeof(*resultset));
+ AST_LIST_HEAD_INIT(resultset);
+ if (query->rowlimit)
+ rowlimit = query->rowlimit;
+ else
+ rowlimit = INT_MAX;
+ }
+ AST_LIST_UNLOCK(&queries);
+
+ for (dsn = 0; dsn < 5; dsn++) {
+ if (!ast_strlen_zero(query->writehandle[dsn])) {
+ obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
+ if (obj)
+ stmt = ast_odbc_direct_execute(obj, generic_execute, sql);
+ }
+ if (stmt)
+ break;
+ }
+
+ if (!stmt) {
+ ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql);
+ if (obj)
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+
+ res = SQLNumResultCols(stmt, &colcount);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ int res1 = -1;
+ if (res == SQL_NO_DATA) {
+ ast_verb(4, "Found no rows [%s]\n", sql);
+ res1 = 0;
+ ast_copy_string(rowcount, "0", sizeof(rowcount));
+ } else {
+ ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
+ }
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return res1;
+ }
+
+ for (y = 0; y < rowlimit; y++) {
+ *buf = '\0';
+ for (x = 0; x < colcount; x++) {
+ int i;
+ char coldata[256];
+
+ if (y == 0) {
+ char colname[256];
+ int namelen;
+
+ res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
+ if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
+ snprintf(colname, sizeof(colname), "field%d", x);
+ }
+
+ if (!ast_strlen_zero(colnames))
+ strncat(colnames, ",", sizeof(colnames) - 1);
+ namelen = strlen(colnames);
+
+ /* Copy data, encoding '\' and ',' for the argument parser */
+ for (i = 0; i < sizeof(colname); i++) {
+ if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
+ colnames[namelen++] = '\\';
+ }
+ colnames[namelen++] = colname[i];
+
+ if (namelen >= sizeof(colnames) - 2) {
+ colnames[namelen >= sizeof(colnames) ? sizeof(colnames) - 1 : namelen] = '\0';
+ break;
+ }
+
+ if (colname[i] == '\0')
+ break;
+ }
+
+ if (resultset) {
+ void *tmp = ast_realloc(resultset, sizeof(*resultset) + strlen(colnames) + 1);
+ if (!tmp) {
+ ast_log(LOG_ERROR, "No space for a new resultset?\n");
+ ast_free(resultset);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+ resultset = tmp;
+ strcpy((char *)resultset + sizeof(*resultset), colnames);
+ }
+ }
+
+ buflen = strlen(buf);
+ res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
+ if (indicator == SQL_NULL_DATA) {
+ coldata[0] = '\0';
+ res = SQL_SUCCESS;
+ }
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ y = -1;
+ goto end_acf_read;
+ }
+
+ /* Copy data, encoding '\' and ',' for the argument parser */
+ for (i = 0; i < sizeof(coldata); i++) {
+ if (escapecommas && (coldata[i] == '\\' || coldata[i] == ',')) {
+ buf[buflen++] = '\\';
+ }
+ buf[buflen++] = coldata[i];
+
+ if (buflen >= len - 2)
+ break;
+
+ if (coldata[i] == '\0')
+ break;
+ }
+
+ buf[buflen - 1] = ',';
+ buf[buflen] = '\0';
+ }
+ /* Trim trailing comma */
+ buf[buflen - 1] = '\0';
+
+ if (resultset) {
+ row = ast_calloc(1, sizeof(*row) + buflen);
+ if (!row) {
+ ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
+ goto end_acf_read;
+ }
+ strcpy((char *)row + sizeof(*row), buf);
+ AST_LIST_INSERT_TAIL(resultset, row, list);
+
+ /* Get next row */
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ if (res != SQL_NO_DATA)
+ ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
+ y++;
+ break;
+ }
+ }
+ }
+
+end_acf_read:
+ snprintf(rowcount, sizeof(rowcount), "%d", y);
+ pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
+ pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
+ if (resultset) {
+ int uid;
+ struct ast_datastore *odbc_store;
+ uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
+ snprintf(buf, len, "%d", uid);
+ odbc_store = ast_channel_datastore_alloc(&odbc_info, buf);
+ if (!odbc_store) {
+ ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
+ odbc_datastore_free(resultset);
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return -1;
+ }
+ odbc_store->data = resultset;
+ ast_channel_datastore_add(chan, odbc_store);
+ }
+ SQLCloseCursor(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ if (chan)
+ ast_autoservice_stop(chan);
+ if (bogus_chan)
+ ast_channel_free(chan);
+ return 0;
+}
+
+static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *out = buf;
+
+ for (; *data && out - buf < len; data++) {
+ if (*data == '\'') {
+ *out = '\'';
+ out++;
+ }
+ *out++ = *data;
+ }
+ *out = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function escape_function = {
+ .name = "SQL_ESC",
+ .synopsis = "Escapes single ticks for use in SQL statements",
+ .syntax = "SQL_ESC(<string>)",
+ .desc =
+"Used in SQL templates to escape data which may contain single ticks (') which\n"
+"are otherwise used to delimit data. For example:\n"
+"SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
+ .read = acf_escape,
+ .write = NULL,
+};
+
+static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_datastore *store;
+ struct odbc_datastore *resultset;
+ struct odbc_datastore_row *row;
+ store = ast_channel_datastore_find(chan, &odbc_info, data);
+ if (!store) {
+ return -1;
+ }
+ resultset = store->data;
+ AST_LIST_LOCK(resultset);
+ row = AST_LIST_REMOVE_HEAD(resultset, list);
+ AST_LIST_UNLOCK(resultset);
+ if (!row) {
+ /* Cleanup datastore */
+ ast_channel_datastore_remove(chan, store);
+ ast_channel_datastore_free(store);
+ return -1;
+ }
+ pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
+ ast_copy_string(buf, row->data, len);
+ ast_free(row);
+ return 0;
+}
+
+static struct ast_custom_function fetch_function = {
+ .name = "ODBC_FETCH",
+ .synopsis = "Fetch a row from a multirow query",
+ .syntax = "ODBC_FETCH(<result-id>)",
+ .desc =
+"For queries which are marked as mode=multirow, the original query returns a\n"
+"result-id from which results may be fetched. This function implements the\n"
+"actual fetch of the results.\n",
+ .read = acf_fetch,
+ .write = NULL,
+};
+
+static char *app_odbcfinish = "ODBCFinish";
+static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
+static char *desc_odbcfinish =
+"ODBCFinish(<result-id>)\n"
+" Clears any remaining rows of the specified resultset\n";
+
+
+static int exec_odbcfinish(struct ast_channel *chan, void *data)
+{
+ struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
+ if (!store) /* Already freed; no big deal. */
+ return 0;
+ ast_channel_datastore_remove(chan, store);
+ ast_channel_datastore_free(store);
+ return 0;
+}
+
+static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
+{
+ const char *tmp;
+ int i;
+
+ if (!cfg || !catg) {
+ return EINVAL;
+ }
+
+ *query = ast_calloc(1, sizeof(struct acf_odbc_query));
+ if (! (*query))
+ return ENOMEM;
+
+ if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
+ char *tmp2 = ast_strdupa(tmp);
+ AST_DECLARE_APP_ARGS(write,
+ AST_APP_ARG(dsn)[5];
+ );
+ AST_STANDARD_APP_ARGS(write, tmp2);
+ for (i = 0; i < 5; i++) {
+ if (!ast_strlen_zero(write.dsn[i]))
+ ast_copy_string((*query)->writehandle[i], write.dsn[i], sizeof((*query)->writehandle[i]));
+ }
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
+ char *tmp2 = ast_strdupa(tmp);
+ AST_DECLARE_APP_ARGS(read,
+ AST_APP_ARG(dsn)[5];
+ );
+ AST_STANDARD_APP_ARGS(read, tmp2);
+ for (i = 0; i < 5; i++) {
+ if (!ast_strlen_zero(read.dsn[i]))
+ ast_copy_string((*query)->readhandle[i], read.dsn[i], sizeof((*query)->readhandle[i]));
+ }
+ } else {
+ /* If no separate readhandle, then use the writehandle for reading */
+ for (i = 0; i < 5; i++) {
+ if (!ast_strlen_zero((*query)->writehandle[i]))
+ ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
+ }
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
+ ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
+ else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
+ ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
+ ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
+ }
+
+ if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
+ ast_free(*query);
+ *query = NULL;
+ ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
+ return EINVAL;
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
+ ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
+ else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
+ ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
+ ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
+ }
+
+ if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
+ ast_free(*query);
+ *query = NULL;
+ ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
+ return EINVAL;
+ }
+
+ /* Allow escaping of embedded commas in fields to be turned off */
+ ast_set_flag((*query), OPT_ESCAPECOMMAS);
+ if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
+ if (ast_false(tmp))
+ ast_clear_flag((*query), OPT_ESCAPECOMMAS);
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
+ if (strcasecmp(tmp, "multirow") == 0)
+ ast_set_flag((*query), OPT_MULTIROW);
+ if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
+ sscanf(tmp, "%d", &((*query)->rowlimit));
+ }
+
+ (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
+ if (! (*query)->acf) {
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
+ asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
+ } else {
+ asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
+ }
+
+ if (!((*query)->acf->name)) {
+ ast_free((*query)->acf);
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
+
+ if (!((*query)->acf->syntax)) {
+ ast_free((char *)(*query)->acf->name);
+ ast_free((*query)->acf);
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
+ if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
+ asprintf((char **)&((*query)->acf->desc),
+ "Runs the following query, as defined in func_odbc.conf, performing\n"
+ "substitution of the arguments into the query as specified by ${ARG1},\n"
+ "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
+ "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
+ "\nRead:\n%s\n\nWrite:\n%s\n",
+ (*query)->sql_read,
+ (*query)->sql_write);
+ } else if (!ast_strlen_zero((*query)->sql_read)) {
+ asprintf((char **)&((*query)->acf->desc),
+ "Runs the following query, as defined in func_odbc.conf, performing\n"
+ "substitution of the arguments into the query as specified by ${ARG1},\n"
+ "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
+ (*query)->sql_read);
+ } else if (!ast_strlen_zero((*query)->sql_write)) {
+ asprintf((char **)&((*query)->acf->desc),
+ "Runs the following query, as defined in func_odbc.conf, performing\n"
+ "substitution of the arguments into the query as specified by ${ARG1},\n"
+ "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
+ "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
+ "This function may only be set.\nSQL:\n%s\n",
+ (*query)->sql_write);
+ } else {
+ ast_free((char *)(*query)->acf->syntax);
+ ast_free((char *)(*query)->acf->name);
+ ast_free((*query)->acf);
+ ast_free(*query);
+ ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute. Ignoring.\n", catg);
+ return EINVAL;
+ }
+
+ if (! ((*query)->acf->desc)) {
+ ast_free((char *)(*query)->acf->syntax);
+ ast_free((char *)(*query)->acf->name);
+ ast_free((*query)->acf);
+ ast_free(*query);
+ *query = NULL;
+ return ENOMEM;
+ }
+
+ if (ast_strlen_zero((*query)->sql_read)) {
+ (*query)->acf->read = NULL;
+ } else {
+ (*query)->acf->read = acf_odbc_read;
+ }
+
+ if (ast_strlen_zero((*query)->sql_write)) {
+ (*query)->acf->write = NULL;
+ } else {
+ (*query)->acf->write = acf_odbc_write;
+ }
+
+ return 0;
+}
+
+static int free_acf_query(struct acf_odbc_query *query)
+{
+ if (query) {
+ if (query->acf) {
+ if (query->acf->name)
+ ast_free((char *)query->acf->name);
+ if (query->acf->syntax)
+ ast_free((char *)query->acf->syntax);
+ if (query->acf->desc)
+ ast_free((char *)query->acf->desc);
+ ast_free(query->acf);
+ }
+ ast_free(query);
+ }
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+ struct ast_config *cfg;
+ char *catg;
+ struct ast_flags config_flags = { 0 };
+
+ res |= ast_custom_function_register(&fetch_function);
+ res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
+ AST_LIST_LOCK(&queries);
+
+ cfg = ast_config_load(config, config_flags);
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
+ AST_LIST_UNLOCK(&queries);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ for (catg = ast_category_browse(cfg, NULL);
+ catg;
+ catg = ast_category_browse(cfg, catg)) {
+ struct acf_odbc_query *query = NULL;
+ int err;
+
+ if ((err = init_acf_query(cfg, catg, &query))) {
+ if (err == ENOMEM)
+ ast_log(LOG_ERROR, "Out of memory\n");
+ else if (err == EINVAL)
+ ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
+ else
+ ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
+ } else {
+ AST_LIST_INSERT_HEAD(&queries, query, list);
+ ast_custom_function_register(query->acf);
+ }
+ }
+
+ ast_config_destroy(cfg);
+ res |= ast_custom_function_register(&escape_function);
+
+ AST_LIST_UNLOCK(&queries);
+ return res;
+}
+
+static int unload_module(void)
+{
+ struct acf_odbc_query *query;
+ int res = 0;
+
+ AST_LIST_LOCK(&queries);
+ while (!AST_LIST_EMPTY(&queries)) {
+ query = AST_LIST_REMOVE_HEAD(&queries, list);
+ ast_custom_function_unregister(query->acf);
+ free_acf_query(query);
+ }
+
+ res |= ast_custom_function_unregister(&escape_function);
+ res |= ast_custom_function_unregister(&fetch_function);
+ res |= ast_unregister_application(app_odbcfinish);
+
+ /* Allow any threads waiting for this lock to pass (avoids a race) */
+ AST_LIST_UNLOCK(&queries);
+ usleep(1);
+ AST_LIST_LOCK(&queries);
+
+ AST_LIST_UNLOCK(&queries);
+ return 0;
+}
+
+static int reload(void)
+{
+ int res = 0;
+ struct ast_config *cfg;
+ struct acf_odbc_query *oldquery;
+ char *catg;
+ struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
+
+ cfg = ast_config_load(config, config_flags);
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ AST_LIST_LOCK(&queries);
+
+ while (!AST_LIST_EMPTY(&queries)) {
+ oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
+ ast_custom_function_unregister(oldquery->acf);
+ free_acf_query(oldquery);
+ }
+
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
+ goto reload_out;
+ }
+
+ for (catg = ast_category_browse(cfg, NULL);
+ catg;
+ catg = ast_category_browse(cfg, catg)) {
+ struct acf_odbc_query *query = NULL;
+
+ if (init_acf_query(cfg, catg, &query)) {
+ ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
+ } else {
+ AST_LIST_INSERT_HEAD(&queries, query, list);
+ ast_custom_function_register(query->acf);
+ }
+ }
+
+ ast_config_destroy(cfg);
+reload_out:
+ AST_LIST_UNLOCK(&queries);
+ return res;
+}
+
+/* XXX need to revise usecount - set if query_lock is set */
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/trunk/funcs/func_rand.c b/trunk/funcs/func_rand.c
new file mode 100644
index 000000000..a3db21d26
--- /dev/null
+++ b/trunk/funcs/func_rand.c
@@ -0,0 +1,93 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ * Copyright (C) 2006, Claude Patry
+ *
+ * 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 Generate Random Number
+ *
+ * \author Claude Patry <cpatry@gmail.com>
+ * \author Tilghman Lesher ( http://asterisk.drunkcoder.com/ )
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int acf_rand_exec(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buffer, size_t buflen)
+{
+ int min_int, response_int, max_int;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(min);
+ AST_APP_ARG(max);
+ );
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.min) || sscanf(args.min, "%d", &min_int) != 1)
+ min_int = 0;
+
+ if (ast_strlen_zero(args.max) || sscanf(args.max, "%d", &max_int) != 1)
+ max_int = RAND_MAX;
+
+ if (max_int < min_int) {
+ int tmp = max_int;
+
+ max_int = min_int;
+ min_int = tmp;
+ ast_debug(1, "max<min\n");
+ }
+
+ response_int = min_int + (ast_random() % (max_int - min_int + 1));
+ ast_debug(1, "%d was the lucky number in range [%d,%d]\n", response_int, min_int, max_int);
+ snprintf(buffer, buflen, "%d", response_int);
+
+ return 0;
+}
+
+static struct ast_custom_function acf_rand = {
+ .name = "RAND",
+ .synopsis = "Choose a random number in a range",
+ .syntax = "RAND([min][,max])",
+ .desc =
+ "Choose a random number between min and max. Min defaults to 0, if not\n"
+ "specified, while max defaults to RAND_MAX (2147483647 on many systems).\n"
+ " Example: Set(junky=${RAND(1,8)}); \n"
+ " Sets junky to a random number between 1 and 8, inclusive.\n",
+ .read = acf_rand_exec,
+};
+
+static int unload_module(void)
+{
+ ast_custom_function_unregister(&acf_rand);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&acf_rand);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Random number dialplan function");
diff --git a/trunk/funcs/func_realtime.c b/trunk/funcs/func_realtime.c
new file mode 100644
index 000000000..7b269d91c
--- /dev/null
+++ b/trunk/funcs/func_realtime.c
@@ -0,0 +1,154 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, BJ Weschke. All rights reserved.
+ *
+ * BJ Weschke <bweschke@btwtech.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ * 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.
+ *
+ */
+
+/*! \file
+ *
+ * \brief REALTIME dialplan function
+ *
+ * \author BJ Weschke <bweschke@btwtech.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_variable *var, *head;
+ struct ast_str *out;
+ size_t resultslen;
+ int n;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(fieldmatch);
+ AST_APP_ARG(value);
+ AST_APP_ARG(delim1);
+ AST_APP_ARG(delim2);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch[,value[,delim1[,delim2]]]) - missing argument!\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (!args.delim1)
+ args.delim1 = ",";
+ if (!args.delim2)
+ args.delim2 = "=";
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, NULL);
+
+ if (!head)
+ if (chan)
+ ast_autoservice_stop(chan);
+ return -1;
+
+ resultslen = 0;
+ n = 0;
+ for (var = head; var; n++, var = var->next)
+ resultslen += strlen(var->name) + strlen(var->value);
+ /* add space for delimiters and final '\0' */
+ resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+
+ out = ast_str_alloca(resultslen);
+ for (var = head; var; var = var->next)
+ ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+ ast_copy_string(buf, out->str, len);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static int function_realtime_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int res = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(family);
+ AST_APP_ARG(fieldmatch);
+ AST_APP_ARG(value);
+ AST_APP_ARG(field);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch,value,newcol) - missing argument!\n");
+ return -1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ res = ast_update_realtime(args.family, args.fieldmatch, args.value, args.field, (char *)value, NULL);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to update. Check the debug log for possible data repository related entries.\n");
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+struct ast_custom_function realtime_function = {
+ .name = "REALTIME",
+ .synopsis = "RealTime Read/Write Functions",
+ .syntax = "REALTIME(family,fieldmatch[,value[,delim1[,delim2]]]) on read\n"
+ "REALTIME(family,fieldmatch,value,field) on write",
+ .desc = "This function will read or write values from/to a RealTime repository.\n"
+ "REALTIME(....) will read names/values from the repository, and \n"
+ "REALTIME(....)= will write a new value/field to the repository. On a\n"
+ "read, this function returns a delimited text string. The name/value \n"
+ "pairs are delimited by delim1, and the name and value are delimited \n"
+ "between each other with delim2. The default for delim1 is ',' and \n"
+ "the default for delim2 is '='. If there is no match, NULL will be \n"
+ "returned by the function. On a write, this function will always \n"
+ "return NULL. \n",
+ .read = function_realtime_read,
+ .write = function_realtime_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&realtime_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&realtime_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read/Write values from a RealTime repository");
diff --git a/trunk/funcs/func_sha1.c b/trunk/funcs/func_sha1.c
new file mode 100644
index 000000000..6474140dd
--- /dev/null
+++ b/trunk/funcs/func_sha1.c
@@ -0,0 +1,76 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ * Copyright (C) 2006, Claude Patry
+ *
+ * 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 SHA1 digest related dialplan functions
+ *
+ * \author Claude Patry <cpatry@gmail.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int sha1(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ *buf = '\0';
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: SHA1(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ if (len >= 41)
+ ast_sha1_hash(buf, data);
+ else {
+ ast_log(LOG_ERROR,
+ "Insufficient space to produce SHA1 hash result (%d < 41)\n",
+ (int) len);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function sha1_function = {
+ .name = "SHA1",
+ .synopsis = "Computes a SHA1 digest",
+ .syntax = "SHA1(<data>)",
+ .read = sha1,
+ .desc = "Generate a SHA1 digest via the SHA1 algorythm.\n"
+ " Example: Set(sha1hash=${SHA1(junky)})\n"
+ " Sets the asterisk variable sha1hash to the string '60fa5675b9303eb62f99a9cd47f9f5837d18f9a0'\n"
+ " which is known as his hash\n",
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&sha1_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&sha1_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SHA-1 computation dialplan function");
diff --git a/trunk/funcs/func_shell.c b/trunk/funcs/func_shell.c
new file mode 100644
index 000000000..57a9819cd
--- /dev/null
+++ b/trunk/funcs/func_shell.c
@@ -0,0 +1,92 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * 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
+ *
+ * SHELL function to return the value of a system call.
+ *
+ * \note Inspiration and Guidance from Russell! Thank You!
+ *
+ * \author Brandon Kruse <bkruse@digium.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int shell_helper(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Missing Argument! Example: Set(foo=${SHELL(echo \"bar\")})\n");
+ return -1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ if (len >= 1) {
+ FILE *ptr;
+ char plbuff[4096];
+
+ ptr = popen(data, "r");
+ while (fgets(plbuff, sizeof(plbuff), ptr)) {
+ strncat(buf, plbuff, len - strlen(buf) - 1);
+ }
+ pclose(ptr);
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function shell_function = {
+ .name = "SHELL",
+ .synopsis = "Executes a command as if you were at a shell.",
+ .syntax = "SHELL(<command>)",
+ .read = shell_helper,
+ .desc =
+"Returns the value from a system command\n"
+" Example: Set(foo=${SHELL(echo \"bar\")})\n"
+" Note: When using the SHELL() dialplan function, your \"SHELL\" is /bin/sh,\n"
+" which may differ as to the underlying shell, depending upon your production\n"
+" platform. Also keep in mind that if you are using a common path, you should\n"
+" be mindful of race conditions that could result from two calls running\n"
+" SHELL() simultaneously.\n",
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&shell_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&shell_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Returns the output of a shell command");
+
diff --git a/trunk/funcs/func_strings.c b/trunk/funcs/func_strings.c
new file mode 100644
index 000000000..54730e108
--- /dev/null
+++ b/trunk/funcs/func_strings.c
@@ -0,0 +1,886 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Digium, Inc.
+ * Portions Copyright (C) 2005, Tilghman Lesher. All rights reserved.
+ * Portions Copyright (C) 2005, Anthony Minessale II
+ *
+ * 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 String manipulation dialplan functions
+ *
+ * \author Tilghman Lesher
+ * \author Anothony Minessale II
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <regex.h>
+#include <ctype.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/localtime.h"
+
+static int function_fieldqty(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buf, size_t len)
+{
+ char *varsubst, varval[8192], *varval2 = varval;
+ int fieldcount = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(varname);
+ AST_APP_ARG(delim);
+ );
+ char delim[2] = "";
+ size_t delim_used;
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (args.delim) {
+ ast_get_encoded_char(args.delim, delim, &delim_used);
+
+ varsubst = alloca(strlen(args.varname) + 4);
+
+ sprintf(varsubst, "${%s}", args.varname);
+ pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
+ if (ast_strlen_zero(varval2))
+ fieldcount = 0;
+ else {
+ while (strsep(&varval2, delim))
+ fieldcount++;
+ }
+ } else {
+ fieldcount = 1;
+ }
+ snprintf(buf, len, "%d", fieldcount);
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function fieldqty_function = {
+ .name = "FIELDQTY",
+ .synopsis = "Count the fields, with an arbitrary delimiter",
+ .syntax = "FIELDQTY(<varname>,<delim>)",
+ .read = function_fieldqty,
+};
+
+static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
+ size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(allowed);
+ AST_APP_ARG(string);
+ );
+ char *outbuf = buf, ac;
+ char allowed[256] = "";
+ size_t allowedlen = 0;
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!args.string) {
+ ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
+ return -1;
+ }
+
+ /* Expand ranges */
+ for (; *(args.allowed) && allowedlen < sizeof(allowed); (args.allowed)++) {
+ char c1 = 0, c2 = 0;
+ size_t consumed = 0;
+
+ if (ast_get_encoded_char(args.allowed, &c1, &consumed))
+ return -1;
+ args.allowed += consumed;
+
+ if (*(args.allowed) == '-') {
+ if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
+ c2 = -1;
+ args.allowed += consumed + 1;
+
+ /*!\note
+ * Looks a little strange, until you realize that we can overflow
+ * the size of a char.
+ */
+ for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
+ allowed[allowedlen++] = ac;
+ allowed[allowedlen++] = ac;
+
+ ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
+
+ /* Decrement before the loop increment */
+ (args.allowed)--;
+ } else
+ allowed[allowedlen++] = c1;
+ }
+
+ ast_debug(1, "Allowed: %s\n", allowed);
+
+ for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
+ if (strchr(allowed, *(args.string)))
+ *outbuf++ = *(args.string);
+ }
+ *outbuf = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function filter_function = {
+ .name = "FILTER",
+ .synopsis = "Filter the string to include only the allowed characters",
+ .syntax = "FILTER(<allowed-chars>,<string>)",
+ .read = filter,
+ .desc =
+"Permits all characters listed in <allowed-chars>, filtering all others out.\n"
+"In addition to literally listing the characters, you may also use ranges of\n"
+"characters (delimited by a '-'), as well as hexadecimal characters started\n"
+"with a \\x (i.e. \\x20) and octal characters started with \\0 (i.e. \\040).\n"
+"Also, \\t, \\n, and \\r are recognized. If you want a literal '-' character,\n"
+"simply prefix it with a '\\'\n",
+};
+
+static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
+ size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(null);
+ AST_APP_ARG(reg);
+ AST_APP_ARG(str);
+ );
+ int errcode;
+ regex_t regexbuf;
+
+ buf[0] = '\0';
+
+ AST_NONSTANDARD_APP_ARGS(args, parse, '"');
+
+ if (args.argc != 3) {
+ ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
+ return -1;
+ }
+ if ((*args.str == ' ') || (*args.str == '\t'))
+ args.str++;
+
+ ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
+
+ if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
+ regerror(errcode, &regexbuf, buf, len);
+ ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
+ return -1;
+ }
+
+ strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
+
+ regfree(&regexbuf);
+
+ return 0;
+}
+
+static struct ast_custom_function regex_function = {
+ .name = "REGEX",
+ .synopsis = "Regular Expression",
+ .desc =
+ "Returns 1 if data matches regular expression, or 0 otherwise.\n"
+ "Please note that the space following the double quotes separating the regex from the data\n"
+ "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
+ "then put two spaces there; the second will not be skipped.\n",
+ .syntax = "REGEX(\"<regular expression>\" <data>)",
+ .read = regex,
+};
+
+#define HASH_PREFIX "~HASH~%s~"
+#define HASH_FORMAT HASH_PREFIX "%s~"
+
+static char *app_clearhash = "ClearHash";
+static char *syn_clearhash = "Clear the keys from a specified hashname";
+static char *desc_clearhash =
+"ClearHash(<hashname>)\n"
+" Clears all keys out of the specified hashname\n";
+
+/* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
+static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
+{
+ struct ast_var_t *var;
+ int len = strlen(prefix);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
+ if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
+ AST_LIST_REMOVE_CURRENT(entries);
+ ast_free(var);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+}
+
+static int exec_clearhash(struct ast_channel *chan, void *data)
+{
+ char prefix[80];
+ snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
+ clearvar_prefix(chan, prefix);
+ return 0;
+}
+
+static int array(struct ast_channel *chan, const char *cmd, char *var,
+ const char *value)
+{
+ AST_DECLARE_APP_ARGS(arg1,
+ AST_APP_ARG(var)[100];
+ );
+ AST_DECLARE_APP_ARGS(arg2,
+ AST_APP_ARG(val)[100];
+ );
+ char *origvar = "", *value2, varname[256];
+ int i, ishash = 0;
+
+ value2 = ast_strdupa(value);
+ if (!var || !value2)
+ return -1;
+
+ if (chan)
+ ast_autoservice_start(chan);
+
+ if (!strcmp(cmd, "HASH")) {
+ const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
+ origvar = var;
+ if (var2)
+ var = ast_strdupa(var2);
+ else {
+ if (chan)
+ ast_autoservice_stop(chan);
+ return -1;
+ }
+ ishash = 1;
+ }
+
+ /* The functions this will generally be used with are SORT and ODBC_*, which
+ * both return comma-delimited lists. However, if somebody uses literal lists,
+ * their commas will be translated to vertical bars by the load, and I don't
+ * want them to be surprised by the result. Hence, we prefer commas as the
+ * delimiter, but we'll fall back to vertical bars if commas aren't found.
+ */
+ ast_debug(1, "array (%s=%s)\n", var, value2);
+ AST_STANDARD_APP_ARGS(arg1, var);
+
+ AST_STANDARD_APP_ARGS(arg2, value2);
+
+ for (i = 0; i < arg1.argc; i++) {
+ ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
+ arg2.val[i]);
+ if (i < arg2.argc) {
+ if (ishash) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
+ pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
+ } else {
+ pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
+ }
+ } else {
+ /* We could unset the variable, by passing a NULL, but due to
+ * pushvar semantics, that could create some undesired behavior. */
+ if (ishash) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
+ pbx_builtin_setvar_helper(chan, varname, "");
+ } else {
+ pbx_builtin_setvar_helper(chan, arg1.var[i], "");
+ }
+ }
+ }
+
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_var_t *newvar;
+ int plen;
+ char prefix[80];
+ snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
+ plen = strlen(prefix);
+
+ memset(buf, 0, len);
+ AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
+ if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
+ /* Copy everything after the prefix */
+ strncat(buf, ast_var_name(newvar) + plen, len);
+ /* Trim the trailing ~ */
+ buf[strlen(buf) - 1] = ',';
+ }
+ }
+ /* Trim the trailing comma */
+ buf[strlen(buf) - 1] = '\0';
+ return 0;
+}
+
+static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
+{
+ char varname[256];
+ AST_DECLARE_APP_ARGS(arg,
+ AST_APP_ARG(hashname);
+ AST_APP_ARG(hashkey);
+ );
+
+ if (!strchr(var, ',')) {
+ /* Single argument version */
+ return array(chan, "HASH", var, value);
+ }
+
+ AST_STANDARD_APP_ARGS(arg, var);
+ snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
+ pbx_builtin_setvar_helper(chan, varname, value);
+
+ return 0;
+}
+
+static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char varname[256];
+ const char *varvalue;
+ AST_DECLARE_APP_ARGS(arg,
+ AST_APP_ARG(hashname);
+ AST_APP_ARG(hashkey);
+ );
+
+ AST_STANDARD_APP_ARGS(arg, data);
+ if (arg.argc == 2) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
+ varvalue = pbx_builtin_getvar_helper(chan, varname);
+ if (varvalue)
+ ast_copy_string(buf, varvalue, len);
+ else
+ *buf = '\0';
+ } else if (arg.argc == 1) {
+ char colnames[4096];
+ int i;
+ AST_DECLARE_APP_ARGS(arg2,
+ AST_APP_ARG(col)[100];
+ );
+
+ /* Get column names, in no particular order */
+ hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
+ pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
+
+ AST_STANDARD_APP_ARGS(arg2, colnames);
+ *buf = '\0';
+
+ /* Now get the corresponding column values, in exactly the same order */
+ for (i = 0; i < arg2.argc; i++) {
+ snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
+ varvalue = pbx_builtin_getvar_helper(chan, varname);
+ strncat(buf, varvalue, len);
+ strncat(buf, ",", len);
+ }
+
+ /* Strip trailing comma */
+ buf[strlen(buf) - 1] = '\0';
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function hash_function = {
+ .name = "HASH",
+ .synopsis = "Implementation of a dialplan associative array",
+ .syntax = "HASH(hashname[,hashkey])",
+ .write = hash_write,
+ .read = hash_read,
+ .desc =
+ "In two argument mode, gets and sets values to corresponding keys within a named\n"
+ "associative array. The single-argument mode will only work when assigned to from\n"
+ "a function defined by func_odbc.so.\n",
+};
+
+static struct ast_custom_function hashkeys_function = {
+ .name = "HASHKEYS",
+ .synopsis = "Retrieve the keys of a HASH()",
+ .syntax = "HASHKEYS(<hashname>)",
+ .read = hashkeys_read,
+ .desc =
+ "Returns a comma-delimited list of the current keys of an associative array\n"
+ "defined by the HASH() function. Note that if you iterate over the keys of\n"
+ "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
+ "function to change.\n",
+};
+
+static struct ast_custom_function array_function = {
+ .name = "ARRAY",
+ .synopsis = "Allows setting multiple variables at once",
+ .syntax = "ARRAY(var1[,var2[...][,varN]])",
+ .write = array,
+ .desc =
+ "The comma-separated list passed as a value to which the function is set will\n"
+ "be interpreted as a set of values to which the comma-separated list of\n"
+ "variable names in the argument should be set.\n"
+ "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2.\n",
+};
+
+static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+#define SPRINTF_FLAG 0
+#define SPRINTF_WIDTH 1
+#define SPRINTF_PRECISION 2
+#define SPRINTF_LENGTH 3
+#define SPRINTF_CONVERSION 4
+ int i, state = -1, argcount = 0;
+ char *formatstart = NULL, *bufptr = buf;
+ char formatbuf[256] = "";
+ int tmpi;
+ double tmpd;
+ AST_DECLARE_APP_ARGS(arg,
+ AST_APP_ARG(format);
+ AST_APP_ARG(var)[100];
+ );
+
+ AST_STANDARD_APP_ARGS(arg, data);
+
+ /* Scan the format, converting each argument into the requisite format type. */
+ for (i = 0; arg.format[i]; i++) {
+ switch (state) {
+ case SPRINTF_FLAG:
+ if (strchr("#0- +'I", arg.format[i]))
+ break;
+ state = SPRINTF_WIDTH;
+ case SPRINTF_WIDTH:
+ if (arg.format[i] >= '0' && arg.format[i] <= '9')
+ break;
+
+ /* Next character must be a period to go into a precision */
+ if (arg.format[i] == '.') {
+ state = SPRINTF_PRECISION;
+ } else {
+ state = SPRINTF_LENGTH;
+ i--;
+ }
+ break;
+ case SPRINTF_PRECISION:
+ if (arg.format[i] >= '0' && arg.format[i] <= '9')
+ break;
+ state = SPRINTF_LENGTH;
+ case SPRINTF_LENGTH:
+ if (strchr("hl", arg.format[i])) {
+ if (arg.format[i + 1] == arg.format[i])
+ i++;
+ state = SPRINTF_CONVERSION;
+ break;
+ } else if (strchr("Lqjzt", arg.format[i]))
+ state = SPRINTF_CONVERSION;
+ break;
+ state = SPRINTF_CONVERSION;
+ case SPRINTF_CONVERSION:
+ if (strchr("diouxXc", arg.format[i])) {
+ /* Integer */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ /* Convert the argument into the required type */
+ if (sscanf(arg.var[argcount++], "%d", &tmpi) != 1) {
+ ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
+ goto sprintf_fail;
+ }
+
+ /* Format the argument */
+ snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
+
+ /* Update the position of the next parameter to print */
+ bufptr = strchr(buf, '\0');
+ } else if (strchr("eEfFgGaA", arg.format[i])) {
+ /* Double */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ /* Convert the argument into the required type */
+ if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
+ ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
+ goto sprintf_fail;
+ }
+
+ /* Format the argument */
+ snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
+
+ /* Update the position of the next parameter to print */
+ bufptr = strchr(buf, '\0');
+ } else if (arg.format[i] == 's') {
+ /* String */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ /* Format the argument */
+ snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
+
+ /* Update the position of the next parameter to print */
+ bufptr = strchr(buf, '\0');
+ } else if (arg.format[i] == '%') {
+ /* Literal data to copy */
+ *bufptr++ = arg.format[i];
+ } else {
+ /* Not supported */
+
+ /* Isolate this format alone */
+ ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
+ formatbuf[&arg.format[i] - formatstart + 1] = '\0';
+
+ ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
+ goto sprintf_fail;
+ }
+ state = -1;
+ break;
+ default:
+ if (arg.format[i] == '%') {
+ state = SPRINTF_FLAG;
+ formatstart = &arg.format[i];
+ break;
+ } else {
+ /* Literal data to copy */
+ *bufptr++ = arg.format[i];
+ }
+ }
+ }
+ return 0;
+sprintf_fail:
+ return -1;
+}
+
+static struct ast_custom_function sprintf_function = {
+ .name = "SPRINTF",
+ .synopsis = "Format a variable according to a format string",
+ .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
+ .read = acf_sprintf,
+ .desc =
+"Parses the format string specified and returns a string matching that format.\n"
+"Supports most options supported by sprintf(3). Returns a shortened string if\n"
+"a format specifier is not recognized.\n",
+};
+
+static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr = buf, *dataptr = data;
+ *bufptr++ = '"';
+ for (; bufptr < buf + len - 1; dataptr++) {
+ if (*dataptr == '\\') {
+ *bufptr++ = '\\';
+ *bufptr++ = '\\';
+ } else if (*dataptr == '"') {
+ *bufptr++ = '\\';
+ *bufptr++ = '"';
+ } else if (*dataptr == '\0') {
+ break;
+ } else {
+ *bufptr++ = *dataptr;
+ }
+ }
+ *bufptr++ = '"';
+ *bufptr = '\0';
+ return 0;
+}
+
+static struct ast_custom_function quote_function = {
+ .name = "QUOTE",
+ .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
+ .syntax = "QUOTE(<string>)",
+ .read = quote,
+};
+
+
+static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
+ size_t len)
+{
+ int length = 0;
+
+ if (data)
+ length = strlen(data);
+
+ snprintf(buf, len, "%d", length);
+
+ return 0;
+}
+
+static struct ast_custom_function len_function = {
+ .name = "LEN",
+ .synopsis = "Returns the length of the argument given",
+ .syntax = "LEN(<string>)",
+ .read = len,
+};
+
+static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
+ char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(epoch);
+ AST_APP_ARG(timezone);
+ AST_APP_ARG(format);
+ );
+ struct timeval tv;
+ struct ast_tm tm;
+
+ buf[0] = '\0';
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ ast_get_timeval(args.epoch, &tv, ast_tvnow(), NULL);
+ ast_localtime(&tv, &tm, args.timezone);
+
+ if (!args.format)
+ args.format = "%c";
+
+ if (ast_strftime(buf, len, args.format, &tm) <= 0)
+ ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
+
+ buf[len - 1] = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function strftime_function = {
+ .name = "STRFTIME",
+ .synopsis = "Returns the current date/time in a specified format.",
+ .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
+ .desc =
+"STRFTIME sports all of the same formats as the underlying C function\n"
+"strftime(3) - see the man page for details. It also supports the\n"
+"following format:\n"
+" %[n]q - fractions of a second, with leading zeroes. For example, %3q will\n"
+" give milliseconds and %1q will give tenths of a second. The default\n"
+" is to output milliseconds (n=3). The common case is to use it in\n"
+" combination with %S, as in \"%S.%3q\".\n",
+ .read = acf_strftime,
+};
+
+static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(timestring);
+ AST_APP_ARG(timezone);
+ AST_APP_ARG(format);
+ );
+ union {
+ struct ast_tm atm;
+ struct tm time;
+ } t = { { 0, }, };
+
+ buf[0] = '\0';
+
+ if (!data) {
+ ast_log(LOG_ERROR,
+ "Asterisk function STRPTIME() requires an argument.\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (ast_strlen_zero(args.format)) {
+ ast_log(LOG_ERROR,
+ "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
+ return -1;
+ }
+
+ if (!strptime(args.timestring, args.format, &t.time)) {
+ ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
+ } else {
+ struct timeval tv = ast_mktime(&t.atm, args.timezone);
+ snprintf(buf, len, "%d", (int) tv.tv_sec);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function strptime_function = {
+ .name = "STRPTIME",
+ .synopsis =
+ "Returns the epoch of the arbitrary date/time string structured as described in the format.",
+ .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
+ .desc =
+ "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
+ "an application like SayUnixTime or to calculate the difference between two\n"
+ "date strings.\n"
+ "\n"
+ "Example:\n"
+ " ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
+ .read = acf_strptime,
+};
+
+static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
+ return -1;
+ }
+
+ if (chan)
+ ast_autoservice_start(chan);
+ pbx_substitute_variables_helper(chan, data, buf, len - 1);
+ if (chan)
+ ast_autoservice_stop(chan);
+
+ return 0;
+}
+
+static struct ast_custom_function eval_function = {
+ .name = "EVAL",
+ .synopsis = "Evaluate stored variables.",
+ .syntax = "EVAL(<variable>)",
+ .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
+ "When a variable or expression is in the dialplan, it will be\n"
+ "evaluated at runtime. However, if the result of the evaluation\n"
+ "is in fact a variable or expression, using EVAL will have it\n"
+ "evaluated a second time. For example, if the variable ${MYVAR}\n"
+ "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
+ "in the dialplan will be the contents of the variable, OTHERVAR.\n"
+ "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
+ "left with \"${OTHERVAR}\".\n",
+ .read = function_eval,
+};
+
+static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr, *dataptr;
+
+ for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
+ if (*dataptr == '1') {
+ *bufptr++ = '1';
+ } else if (strchr("AaBbCc2", *dataptr)) {
+ *bufptr++ = '2';
+ } else if (strchr("DdEeFf3", *dataptr)) {
+ *bufptr++ = '3';
+ } else if (strchr("GgHhIi4", *dataptr)) {
+ *bufptr++ = '4';
+ } else if (strchr("JjKkLl5", *dataptr)) {
+ *bufptr++ = '5';
+ } else if (strchr("MmNnOo6", *dataptr)) {
+ *bufptr++ = '6';
+ } else if (strchr("PpQqRrSs7", *dataptr)) {
+ *bufptr++ = '7';
+ } else if (strchr("TtUuVv8", *dataptr)) {
+ *bufptr++ = '8';
+ } else if (strchr("WwXxYyZz9", *dataptr)) {
+ *bufptr++ = '9';
+ } else if (*dataptr == '0') {
+ *bufptr++ = '0';
+ } else if (*dataptr == '\0') {
+ *bufptr++ = '\0';
+ break;
+ }
+ }
+ buf[len - 1] = '\0';
+
+ return 0;
+}
+
+static struct ast_custom_function keypadhash_function = {
+ .name = "KEYPADHASH",
+ .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
+ .syntax = "KEYPADHASH(<string>)",
+ .read = keypadhash,
+ .desc = "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
+};
+
+static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr = buf, *dataptr = data;
+
+ while ((bufptr < buf + len - 1) && (*bufptr++ = toupper(*dataptr++)));
+
+ return 0;
+}
+
+static struct ast_custom_function toupper_function = {
+ .name = "TOUPPER",
+ .synopsis = "Convert the string to upper case.",
+ .syntax = "TOUPPER(<string>)",
+ .read = string_toupper,
+ .desc = "Example: ${TOUPPER(Example)} returns \"EXAMPLE\"\n",
+};
+
+static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr = buf, *dataptr = data;
+
+ while ((bufptr < buf + len - 1) && (*bufptr++ = tolower(*dataptr++)));
+
+ return 0;
+}
+
+static struct ast_custom_function tolower_function = {
+ .name = "TOLOWER",
+ .synopsis = "Convert the string to lower case.",
+ .syntax = "TOLOWER(<string>)",
+ .read = string_tolower,
+ .desc = "Example: ${TOLOWER(Example)} returns \"example\"\n",
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&fieldqty_function);
+ res |= ast_custom_function_unregister(&filter_function);
+ res |= ast_custom_function_unregister(&regex_function);
+ res |= ast_custom_function_unregister(&array_function);
+ res |= ast_custom_function_unregister(&quote_function);
+ res |= ast_custom_function_unregister(&len_function);
+ res |= ast_custom_function_unregister(&strftime_function);
+ res |= ast_custom_function_unregister(&strptime_function);
+ res |= ast_custom_function_unregister(&eval_function);
+ res |= ast_custom_function_unregister(&keypadhash_function);
+ res |= ast_custom_function_unregister(&sprintf_function);
+ res |= ast_custom_function_unregister(&hashkeys_function);
+ res |= ast_custom_function_unregister(&hash_function);
+ res |= ast_unregister_application(app_clearhash);
+ res |= ast_custom_function_unregister(&toupper_function);
+ res |= ast_custom_function_unregister(&tolower_function);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_custom_function_register(&fieldqty_function);
+ res |= ast_custom_function_register(&filter_function);
+ res |= ast_custom_function_register(&regex_function);
+ res |= ast_custom_function_register(&array_function);
+ res |= ast_custom_function_register(&quote_function);
+ res |= ast_custom_function_register(&len_function);
+ res |= ast_custom_function_register(&strftime_function);
+ res |= ast_custom_function_register(&strptime_function);
+ res |= ast_custom_function_register(&eval_function);
+ res |= ast_custom_function_register(&keypadhash_function);
+ res |= ast_custom_function_register(&sprintf_function);
+ res |= ast_custom_function_register(&hashkeys_function);
+ res |= ast_custom_function_register(&hash_function);
+ res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
+ res |= ast_custom_function_register(&toupper_function);
+ res |= ast_custom_function_register(&tolower_function);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");
diff --git a/trunk/funcs/func_sysinfo.c b/trunk/funcs/func_sysinfo.c
new file mode 100644
index 000000000..b386b9fb4
--- /dev/null
+++ b/trunk/funcs/func_sysinfo.c
@@ -0,0 +1,116 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * 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
+ *
+ * SYSINFO function to return various system data.
+ *
+ * \note Inspiration and Guidance from Russell
+ *
+ * \author Jeff Peeler
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 87233 $")
+
+#if defined(HAVE_SYSINFO)
+#include <sys/sysinfo.h>
+#endif
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static int sysinfo_helper(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+#if defined(HAVE_SYSINFO)
+ struct sysinfo sys_info;
+ if (sysinfo(&sys_info)) {
+ ast_log(LOG_ERROR, "FAILED to retrieve system information\n");
+ return -1;
+ }
+#endif
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: ${SYSINFO(<parameter>)} - missing argument!)\n");
+ return -1;
+ } else if (!strcasecmp("loadavg", data)) {
+ double curloadavg;
+ getloadavg(&curloadavg, 1);
+ snprintf(buf, len, "%f", curloadavg);
+ } else if (!strcasecmp("numcalls", data)) {
+ snprintf(buf, len, "%d", ast_active_calls());
+ }
+#if defined(HAVE_SYSINFO)
+ else if (!strcasecmp("uptime", data)) { /* in hours */
+ snprintf(buf, len, "%ld", sys_info.uptime/3600);
+ } else if (!strcasecmp("totalram", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.totalram / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("freeram", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.freeram / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("bufferram", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.bufferram / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("totalswap", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.totalswap / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("freeswap", data)) { /* in KiB */
+ snprintf(buf, len, "%ld",(sys_info.freeswap / sys_info.mem_unit)/1024);
+ } else if (!strcasecmp("numprocs", data)) {
+ snprintf(buf, len, "%d", sys_info.procs);
+ }
+#endif
+ else {
+ ast_log(LOG_ERROR, "Unknown sysinfo parameter type '%s'.\n", data);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function sysinfo_function = {
+ .name = "SYSINFO",
+ .synopsis = "Returns system information specified by parameter.",
+ .syntax = "SYSINFO(<parameter>)",
+ .read = sysinfo_helper,
+ .desc =
+"Returns information from a given parameter\n"
+" Options:\n"
+" loadavg - system load average from past minute\n"
+" numcalls - number of active calls currently in progress\n"
+#if defined(HAVE_SYSINFO)
+" uptime - system uptime in hours\n"
+" totalram - total usable main memory size in KiB\n"
+" freeram - available memory size in KiB\n"
+" bufferram - memory used by buffers in KiB\n"
+" totalswap - total swap space size in KiB\n"
+" freeswap - free swap space still available in KiB\n"
+" numprocs - number of current processes\n",
+#endif /* HAVE_SYSINFO */
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&sysinfo_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&sysinfo_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "System information related functions");
+
diff --git a/trunk/funcs/func_timeout.c b/trunk/funcs/func_timeout.c
new file mode 100644
index 000000000..a42718a6d
--- /dev/null
+++ b/trunk/funcs/func_timeout.c
@@ -0,0 +1,184 @@
+/*
+ * 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 Channel timeout related dialplan functions
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int timeout_read(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ time_t myt;
+
+ if (!chan)
+ return -1;
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Must specify type of timeout to get.\n");
+ return -1;
+ }
+
+ switch (*data) {
+ case 'a':
+ case 'A':
+ if (chan->whentohangup == 0) {
+ ast_copy_string(buf, "0", len);
+ } else {
+ time(&myt);
+ snprintf(buf, len, "%d", (int) (chan->whentohangup - myt));
+ }
+ break;
+
+ case 'r':
+ case 'R':
+ if (chan->pbx) {
+ snprintf(buf, len, "%d", chan->pbx->rtimeout);
+ }
+ break;
+
+ case 'd':
+ case 'D':
+ if (chan->pbx) {
+ snprintf(buf, len, "%d", chan->pbx->dtimeout);
+ }
+ break;
+
+ default:
+ ast_log(LOG_ERROR, "Unknown timeout type specified.\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int timeout_write(struct ast_channel *chan, const char *cmd, char *data,
+ const char *value)
+{
+ int x;
+ char timestr[64];
+ struct ast_tm myt;
+
+ if (!chan)
+ return -1;
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Must specify type of timeout to set.\n");
+ return -1;
+ }
+
+ if (!value)
+ return -1;
+
+ x = atoi(value);
+ if (x < 0)
+ x = 0;
+
+ switch (*data) {
+ case 'a':
+ case 'A':
+ ast_channel_setwhentohangup(chan, x);
+ if (VERBOSITY_ATLEAST(3)) {
+ if (chan->whentohangup) {
+ struct timeval tv = { chan->whentohangup, 0 };
+ ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S.%3q %Z",
+ ast_localtime(&tv, &myt, NULL));
+ ast_verbose("Channel will hangup at %s.\n", timestr);
+ } else {
+ ast_verbose("Channel hangup cancelled.\n");
+ }
+ }
+ break;
+
+ case 'r':
+ case 'R':
+ if (chan->pbx) {
+ chan->pbx->rtimeout = x;
+ ast_verb(3, "Response timeout set to %d\n", chan->pbx->rtimeout);
+ }
+ break;
+
+ case 'd':
+ case 'D':
+ if (chan->pbx) {
+ chan->pbx->dtimeout = x;
+ ast_verb(3, "Digit timeout set to %d\n", chan->pbx->dtimeout);
+ }
+ break;
+
+ default:
+ ast_log(LOG_ERROR, "Unknown timeout type specified.\n");
+ break;
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function timeout_function = {
+ .name = "TIMEOUT",
+ .synopsis = "Gets or sets timeouts on the channel. Timeout values are in seconds.",
+ .syntax = "TIMEOUT(timeouttype)",
+ .desc =
+ "Gets or sets various channel timeouts. The timeouts that can be\n"
+ "manipulated are:\n" "\n"
+ "absolute: The absolute maximum amount of time permitted for a call. A\n"
+ " setting of 0 disables the timeout.\n" "\n"
+ "digit: The maximum amount of time permitted between digits when the\n"
+ " user is typing in an extension. When this timeout expires,\n"
+ " after the user has started to type in an extension, the\n"
+ " extension will be considered complete, and will be\n"
+ " interpreted. Note that if an extension typed in is valid,\n"
+ " it will not have to timeout to be tested, so typically at\n"
+ " the expiry of this timeout, the extension will be considered\n"
+ " invalid (and thus control would be passed to the 'i'\n"
+ " extension, or if it doesn't exist the call would be\n"
+ " terminated). The default timeout is 5 seconds.\n" "\n"
+ "response: The maximum amount of time permitted after falling through a\n"
+ " series of priorities for a channel in which the user may\n"
+ " begin typing an extension. If the user does not type an\n"
+ " extension in this amount of time, control will pass to the\n"
+ " 't' extension if it exists, and if not the call would be\n"
+ " terminated. The default timeout is 10 seconds.\n",
+ .read = timeout_read,
+ .write = timeout_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&timeout_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&timeout_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel timeout dialplan functions");
diff --git a/trunk/funcs/func_uri.c b/trunk/funcs/func_uri.c
new file mode 100644
index 000000000..94ae220b6
--- /dev/null
+++ b/trunk/funcs/func_uri.c
@@ -0,0 +1,96 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Created by Olle E. Johansson, Edvina.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 URI encoding / decoding
+ *
+ * \author Olle E. Johansson <oej@edvina.net>
+ *
+ * \note For now this code only supports 8 bit characters, not unicode,
+ which we ultimately will need to support.
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+/*! \brief uriencode: Encode URL according to RFC 2396 */
+static int uriencode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: URIENCODE(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_uri_encode(data, buf, len, 1);
+
+ return 0;
+}
+
+/*!\brief uridecode: Decode URI according to RFC 2396 */
+static int uridecode(struct ast_channel *chan, const char *cmd, char *data,
+ char *buf, size_t len)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Syntax: URIDECODE(<data>) - missing argument!\n");
+ return -1;
+ }
+
+ ast_copy_string(buf, data, len);
+ ast_uri_decode(buf);
+
+ return 0;
+}
+
+static struct ast_custom_function urldecode_function = {
+ .name = "URIDECODE",
+ .synopsis = "Decodes a URI-encoded string according to RFC 2396.",
+ .syntax = "URIDECODE(<data>)",
+ .read = uridecode,
+};
+
+static struct ast_custom_function urlencode_function = {
+ .name = "URIENCODE",
+ .synopsis = "Encodes a string to URI-safe encoding according to RFC 2396.",
+ .syntax = "URIENCODE(<data>)",
+ .read = uriencode,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&urldecode_function)
+ || ast_custom_function_unregister(&urlencode_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&urldecode_function)
+ || ast_custom_function_register(&urlencode_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "URI encode/decode dialplan functions");
diff --git a/trunk/funcs/func_version.c b/trunk/funcs/func_version.c
new file mode 100644
index 000000000..8d1250375
--- /dev/null
+++ b/trunk/funcs/func_version.c
@@ -0,0 +1,101 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * 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 Return the current Version strings
+ *
+ * \author Steve Murphy (murf@digium.com)
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/version.h"
+#include "asterisk/build.h"
+
+static int acf_version_exec(struct ast_channel *chan, const char *cmd,
+ char *parse, char *buffer, size_t buflen)
+{
+ const char *response_char = ast_get_version();
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(info);
+ );
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.info) ) {
+ if (!strcasecmp(args.info,"ASTERISK_VERSION_NUM"))
+ response_char = ast_get_version_num();
+ else if (!strcasecmp(args.info,"BUILD_USER"))
+ response_char = BUILD_USER;
+ else if (!strcasecmp(args.info,"BUILD_HOSTNAME"))
+ response_char = BUILD_HOSTNAME;
+ else if (!strcasecmp(args.info,"BUILD_MACHINE"))
+ response_char = BUILD_MACHINE;
+ else if (!strcasecmp(args.info,"BUILD_KERNEL"))
+ response_char = BUILD_KERNEL;
+ else if (!strcasecmp(args.info,"BUILD_OS"))
+ response_char = BUILD_OS;
+ else if (!strcasecmp(args.info,"BUILD_DATE"))
+ response_char = BUILD_DATE;
+ }
+
+ ast_debug(1, "VERSION returns %s result, given %s argument\n", response_char, args.info);
+
+ snprintf(buffer, buflen, "%s", response_char);
+
+ return 0;
+}
+
+static struct ast_custom_function acf_version = {
+ .name = "VERSION",
+ .synopsis = "Return the Version info for this Asterisk",
+ .syntax = "VERSION([info])",
+ .desc =
+ "If there are no arguments, return the version of Asterisk in this format: SVN-branch-1.4-r44830M\n"
+ "If the argument is 'ASTERISK_VERSION_NUM', a string of digits is returned (right now fixed at 999999).\n"
+ "If the argument is 'BUILD_USER', the string representing the user's name whose account was used to configure Asterisk, is returned.\n"
+ "If the argument is 'BUILD_HOSTNAME', the string representing the name of the host on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_MACHINE', the string representing the type of machine on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_OS', the string representing the OS of the machine on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_DATE', the string representing the date on which Asterisk was configured, is returned.\n"
+ "If the argument is 'BUILD_KERNEL', the string representing the kernel version of the machine on which Asterisk was configured, is returned .\n"
+ " Example: Set(junky=${VERSION()}; \n"
+ " Sets junky to the string 'SVN-branch-1.6-r74830M', or possibly, 'SVN-trunk-r45126M'.\n",
+ .read = acf_version_exec,
+};
+
+static int unload_module(void)
+{
+ ast_custom_function_unregister(&acf_version);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&acf_version);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get Asterisk Version/Build Info");
diff --git a/trunk/funcs/func_vmcount.c b/trunk/funcs/func_vmcount.c
new file mode 100644
index 000000000..24d83140d
--- /dev/null
+++ b/trunk/funcs/func_vmcount.c
@@ -0,0 +1,93 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (c) 2006 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <asterisk-vmcount-func@the-tilghman.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 VMCOUNT dialplan function
+ *
+ * \author Tilghman Lesher <asterisk-vmcount-func@the-tilghman.com>
+ *
+ * \ingroup functions
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <dirent.h>
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+static int acf_vmcount_exec(struct ast_channel *chan, const char *cmd, char *argsstr, char *buf, size_t len)
+{
+ char *context;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(vmbox);
+ AST_APP_ARG(folder);
+ );
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(argsstr))
+ return -1;
+
+ AST_STANDARD_APP_ARGS(args, argsstr);
+
+ if (strchr(args.vmbox, '@')) {
+ context = args.vmbox;
+ args.vmbox = strsep(&context, "@");
+ } else {
+ context = "default";
+ }
+
+ if (ast_strlen_zero(args.folder)) {
+ args.folder = "INBOX";
+ }
+
+ snprintf(buf, len, "%d", ast_app_messagecount(context, args.vmbox, args.folder));
+
+ return 0;
+}
+
+struct ast_custom_function acf_vmcount = {
+ .name = "VMCOUNT",
+ .synopsis = "Counts the voicemail in a specified mailbox",
+ .syntax = "VMCOUNT(vmbox[@context][,folder])",
+ .desc =
+ " context - defaults to \"default\"\n"
+ " folder - defaults to \"INBOX\"\n",
+ .read = acf_vmcount_exec,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&acf_vmcount);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&acf_vmcount);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Indicator for whether a voice mailbox has messages in a given folder.");
diff --git a/trunk/funcs/func_volume.c b/trunk/funcs/func_volume.c
new file mode 100644
index 000000000..9a5247f54
--- /dev/null
+++ b/trunk/funcs/func_volume.c
@@ -0,0 +1,160 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Technology independent volume control
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup functions
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/audiohook.h"
+
+struct volume_information {
+ struct ast_audiohook audiohook;
+ int tx_gain;
+ int rx_gain;
+};
+
+static void destroy_callback(void *data)
+{
+ struct volume_information *vi = data;
+
+ /* Destroy the audiohook, and destroy ourselves */
+ ast_audiohook_destroy(&vi->audiohook);
+ free(vi);
+
+ return;
+}
+
+/*! \brief Static structure for datastore information */
+static const struct ast_datastore_info volume_datastore = {
+ .type = "volume",
+ .destroy = destroy_callback
+};
+
+static int volume_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
+{
+ struct ast_datastore *datastore = NULL;
+ struct volume_information *vi = NULL;
+ int *gain = NULL;
+
+ /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
+ if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
+ return 0;
+
+ /* Grab datastore which contains our gain information */
+ if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL)))
+ return 0;
+
+ vi = datastore->data;
+
+ /* If this is DTMF then allow them to increase/decrease the gains */
+ if (frame->frametype == AST_FRAME_DTMF) {
+ /* Only use DTMF coming from the source... not going to it */
+ if (direction != AST_AUDIOHOOK_DIRECTION_READ)
+ return 0;
+ if (frame->subclass == '*') {
+ vi->tx_gain += 1;
+ vi->rx_gain += 1;
+ } else if (frame->subclass == '#') {
+ vi->tx_gain -= 1;
+ vi->rx_gain -= 1;
+ }
+ } else if (frame->frametype == AST_FRAME_VOICE) {
+ /* Based on direction of frame grab the gain, and confirm it is applicable */
+ if (!(gain = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? &vi->rx_gain : &vi->tx_gain) || !*gain)
+ return 0;
+ /* Apply gain to frame... easy as pi */
+ ast_frame_adjust_volume(frame, *gain);
+ }
+
+ return 0;
+}
+
+static int volume_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ struct ast_datastore *datastore = NULL;
+ struct volume_information *vi = NULL;
+ int is_new = 0;
+
+ if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL))) {
+ /* Allocate a new datastore to hold the reference to this volume and audiohook information */
+ if (!(datastore = ast_channel_datastore_alloc(&volume_datastore, NULL)))
+ return 0;
+ if (!(vi = ast_calloc(1, sizeof(*vi)))) {
+ ast_channel_datastore_free(datastore);
+ return 0;
+ }
+ ast_audiohook_init(&vi->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume");
+ vi->audiohook.manipulate_callback = volume_callback;
+ ast_set_flag(&vi->audiohook, AST_AUDIOHOOK_WANTS_DTMF);
+ is_new = 1;
+ } else {
+ vi = datastore->data;
+ }
+
+ /* Adjust gain on volume information structure */
+ if (!strcasecmp(data, "tx"))
+ vi->tx_gain = atoi(value);
+ else if (!strcasecmp(data, "rx"))
+ vi->rx_gain = atoi(value);
+
+ if (is_new) {
+ datastore->data = vi;
+ ast_channel_datastore_add(chan, datastore);
+ ast_audiohook_attach(chan, &vi->audiohook);
+ }
+
+ return 0;
+}
+
+static struct ast_custom_function volume_function = {
+ .name = "VOLUME",
+ .synopsis = "Set the TX or RX volume of a channel",
+ .syntax = "VOLUME(TX|RX)",
+ .desc =
+ " The VOLUME function can be used to increase or decrease the tx or\n"
+ "rx gain of any channel. For example:\n"
+ " Set(VOLUME(TX)=3)\n"
+ " Set(VOLUME(RX)=2)\n",
+ .write = volume_write,
+};
+
+static int unload_module(void)
+{
+ return ast_custom_function_unregister(&volume_function);
+}
+
+static int load_module(void)
+{
+ return ast_custom_function_register(&volume_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Technology independent volume control");
diff --git a/trunk/images/animlogo.gif b/trunk/images/animlogo.gif
new file mode 100644
index 000000000..9b75989fc
--- /dev/null
+++ b/trunk/images/animlogo.gif
Binary files differ
diff --git a/trunk/images/asterisk-intro.jpg b/trunk/images/asterisk-intro.jpg
new file mode 100644
index 000000000..278f018ce
--- /dev/null
+++ b/trunk/images/asterisk-intro.jpg
Binary files differ
diff --git a/trunk/images/font.png b/trunk/images/font.png
new file mode 100644
index 000000000..7b94d00ab
--- /dev/null
+++ b/trunk/images/font.png
Binary files differ
diff --git a/trunk/images/kpad2.jpg b/trunk/images/kpad2.jpg
new file mode 100644
index 000000000..b03a7596a
--- /dev/null
+++ b/trunk/images/kpad2.jpg
Binary files differ
diff --git a/trunk/images/play.gif b/trunk/images/play.gif
new file mode 100644
index 000000000..4d457b19d
--- /dev/null
+++ b/trunk/images/play.gif
Binary files differ
diff --git a/trunk/include/asterisk.h b/trunk/include/asterisk.h
new file mode 100644
index 000000000..9d5c1ae8a
--- /dev/null
+++ b/trunk/include/asterisk.h
@@ -0,0 +1,184 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * General Definitions for Asterisk top level program
+ *
+ * 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
+ */
+
+/*! \file
+ * \brief Asterisk main include file. File version handling, generic pbx functions.
+ */
+
+#ifndef _ASTERISK_H
+#define _ASTERISK_H
+
+/* The include of 'autoconfig.h' is not necessary for any modules that
+ are part of the Asterisk source tree, because the top-level Makefile
+ will forcibly include that header in all compilations before all
+ other headers (even system headers). However, leaving this here will
+ help out-of-tree module builders, and doesn't cause any harm for the
+ in-tree modules.
+*/
+#include "asterisk/autoconfig.h"
+
+#include "asterisk/compat.h"
+
+#include "asterisk/logger.h"
+
+/* Default to allowing the umask or filesystem ACLs to determine actual file
+ * creation permissions
+ */
+#ifndef AST_DIR_MODE
+#define AST_DIR_MODE 0777
+#endif
+#ifndef AST_FILE_MODE
+#define AST_FILE_MODE 0666
+#endif
+
+#define DEFAULT_LANGUAGE "en"
+
+#define DEFAULT_SAMPLE_RATE 8000
+#define DEFAULT_SAMPLES_PER_MS ((DEFAULT_SAMPLE_RATE)/1000)
+#define setpriority __PLEASE_USE_ast_set_priority_INSTEAD_OF_setpriority__
+#define sched_setscheduler __PLEASE_USE_ast_set_priority_INSTEAD_OF_sched_setscheduler__
+
+int ast_set_priority(int); /*!< Provided by asterisk.c */
+
+/*!
+ * \brief Register a function to be executed before Asterisk exits.
+ * \param func The callback function to use.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_register_atexit(void (*func)(void));
+
+/*!
+ * \brief Unregister a function registered with ast_register_atexit().
+ * \param func The callback function to unregister.
+ */
+void ast_unregister_atexit(void (*func)(void));
+
+#if !defined(LOW_MEMORY)
+/*!
+ * \brief Register the version of a source code file with the core.
+ * \param file the source file name
+ * \param version the version string (typically a CVS revision keyword string)
+ * \return nothing
+ *
+ * This function should not be called directly, but instead the
+ * ASTERISK_FILE_VERSION macro should be used to register a file with the core.
+ */
+void ast_register_file_version(const char *file, const char *version);
+
+/*!
+ * \brief Unregister a source code file from the core.
+ * \param file the source file name
+ * \return nothing
+ *
+ * This function should not be called directly, but instead the
+ * ASTERISK_FILE_VERSION macro should be used to automatically unregister
+ * the file when the module is unloaded.
+ */
+void ast_unregister_file_version(const char *file);
+
+/*! \brief Find version for given module name
+ * \param file Module name (i.e. chan_sip.so)
+ * \return version string or NULL if the module is not found
+ */
+const char *ast_file_version_find(const char *file);
+
+
+/*!
+ * \brief Register/unregister a source code file with the core.
+ * \param file the source file name
+ * \param version the version string (typically a CVS revision keyword string)
+ *
+ * This macro will place a file-scope constructor and destructor into the
+ * source of the module using it; this will cause the version of this file
+ * to registered with the Asterisk core (and unregistered) at the appropriate
+ * times.
+ *
+ * Example:
+ *
+ * \code
+ * ASTERISK_FILE_VERSION(__FILE__, "\$Revision\$")
+ * \endcode
+ *
+ * \note The dollar signs above have been protected with backslashes to keep
+ * CVS from modifying them in this file; under normal circumstances they would
+ * not be present and CVS would expand the Revision keyword into the file's
+ * revision number.
+ */
+#ifdef MTX_PROFILE
+#define HAVE_MTX_PROFILE /* used in lock.h */
+#define ASTERISK_FILE_VERSION(file, version) \
+ static int mtx_prof = -1; /* profile mutex */ \
+ static void __attribute__((constructor)) __register_file_version(void) \
+ { \
+ mtx_prof = ast_add_profile("mtx_lock_" file, 0); \
+ ast_register_file_version(file, version); \
+ } \
+ static void __attribute__((destructor)) __unregister_file_version(void) \
+ { \
+ ast_unregister_file_version(file); \
+ }
+#else /* !MTX_PROFILE */
+#define ASTERISK_FILE_VERSION(file, version) \
+ static void __attribute__((constructor)) __register_file_version(void) \
+ { \
+ ast_register_file_version(file, version); \
+ } \
+ static void __attribute__((destructor)) __unregister_file_version(void) \
+ { \
+ ast_unregister_file_version(file); \
+ }
+#endif /* !MTX_PROFILE */
+#else /* LOW_MEMORY */
+#define ASTERISK_FILE_VERSION(file, x)
+#endif /* LOW_MEMORY */
+
+#if !defined(LOW_MEMORY)
+/*!
+ * \brief support for event profiling
+ *
+ * (note, this must be documented a lot more)
+ * ast_add_profile allocates a generic 'counter' with a given name,
+ * which can be shown with the command 'show profile <name>'
+ *
+ * The counter accumulates positive or negative values supplied by
+ * ast_add_profile(), dividing them by the 'scale' value passed in the
+ * create call, and also counts the number of 'events'.
+ * Values can also be taked by the TSC counter on ia32 architectures,
+ * in which case you can mark the start of an event calling ast_mark(id, 1)
+ * and then the end of the event with ast_mark(id, 0).
+ * For non-i386 architectures, these two calls return 0.
+ */
+int ast_add_profile(const char *, uint64_t scale);
+int64_t ast_profile(int, int64_t);
+int64_t ast_mark(int, int start1_stop0);
+#else /* LOW_MEMORY */
+#define ast_add_profile(a, b) 0
+#define ast_profile(a, b) do { } while (0)
+#define ast_mark(a, b) do { } while (0)
+#endif /* LOW_MEMORY */
+
+/*! \brief
+ * Definition of various structures that many asterisk files need,
+ * but only because they need to know that the type exists.
+ *
+ */
+
+struct ast_channel;
+struct ast_frame;
+struct ast_module;
+struct ast_variable;
+struct ast_str;
+
+#endif /* _ASTERISK_H */
diff --git a/trunk/include/asterisk/_private.h b/trunk/include/asterisk/_private.h
new file mode 100644
index 000000000..06163cd53
--- /dev/null
+++ b/trunk/include/asterisk/_private.h
@@ -0,0 +1,53 @@
+/*
+ * Prototypes for public functions only of internal interest,
+ * normally not used by modules.
+ * What goes here are typically *_init() routines.
+ */
+
+/*! \file
+ *
+ * \brief
+ * Prototypes for public functions only of internal interest,
+ *
+ */
+
+
+#ifndef _ASTERISK__PRIVATE_H
+#define _ASTERISK__PRIVATE_H
+
+int load_modules(unsigned int); /*!< Provided by loader.c */
+int load_pbx(void); /*!< Provided by pbx.c */
+int init_logger(void); /*!< Provided by logger.c */
+void close_logger(void); /*!< Provided by logger.c */
+int init_framer(void); /*!< Provided by frame.c */
+int ast_term_init(void); /*!< Provided by term.c */
+int astdb_init(void); /*!< Provided by db.c */
+void ast_channels_init(void); /*!< Provided by channel.c */
+void ast_builtins_init(void); /*!< Provided by cli.c */
+int dnsmgr_init(void); /*!< Provided by dnsmgr.c */
+void dnsmgr_start_refresh(void); /*!< Provided by dnsmgr.c */
+int dnsmgr_reload(void); /*!< Provided by dnsmgr.c */
+void threadstorage_init(void); /*!< Provided by threadstorage.c */
+void ast_event_init(void); /*!< Provided by event.c */
+int ast_device_state_engine_init(void); /*!< Provided by devicestate.c */
+int astobj2_init(void); /*!< Provided by astobj2.c */
+int ast_file_init(void); /*!< Provided by file.c */
+
+/*!
+ * \brief Reload asterisk modules.
+ * \param name the name of the module to reload
+ *
+ * This function reloads the specified module, or if no modules are specified,
+ * it will reload all loaded modules.
+ *
+ * \note Modules are reloaded using their reload() functions, not unloading
+ * them and loading them again.
+ *
+ * \return 0 if the specified module was not found.
+ * \retval 1 if the module was found but cannot be reloaded.
+ * \retval -1 if a reload operation is already in progress.
+ * \retval 2 if the specfied module was found and reloaded.
+ */
+int ast_module_reload(const char *name);
+
+#endif /* _ASTERISK__PRIVATE_H */
diff --git a/trunk/include/asterisk/abstract_jb.h b/trunk/include/asterisk/abstract_jb.h
new file mode 100644
index 000000000..4a41c8c2c
--- /dev/null
+++ b/trunk/include/asterisk/abstract_jb.h
@@ -0,0 +1,220 @@
+/*
+ * abstract_jb: common implementation-independent jitterbuffer stuff
+ *
+ * Copyright (C) 2005, Attractel OOD
+ *
+ * Contributors:
+ * Slav Klenov <slav@securax.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.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ *
+ * \brief Common implementation-independent jitterbuffer stuff.
+ *
+ * \author Slav Klenov <slav@securax.org>
+ */
+
+#ifndef _ABSTRACT_JB_H_
+#define _ABSTRACT_JB_H_
+
+#include <sys/time.h>
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct ast_frame;
+
+/* Configuration flags */
+enum {
+ AST_JB_ENABLED = (1 << 0),
+ AST_JB_FORCED = (1 << 1),
+ AST_JB_LOG = (1 << 2)
+};
+
+#define AST_JB_IMPL_NAME_SIZE 12
+
+/*!
+ * \brief General jitterbuffer configuration.
+ */
+struct ast_jb_conf
+{
+ /*! \brief Combination of the AST_JB_ENABLED, AST_JB_FORCED and AST_JB_LOG flags. */
+ unsigned int flags;
+ /*! \brief Max size of the jitterbuffer implementation. */
+ long max_size;
+ /*! \brief Resynchronization threshold of the jitterbuffer implementation. */
+ long resync_threshold;
+ /*! \brief Name of the jitterbuffer implementation to be used. */
+ char impl[AST_JB_IMPL_NAME_SIZE];
+};
+
+
+/* Jitterbuffer configuration property names */
+#define AST_JB_CONF_PREFIX "jb"
+#define AST_JB_CONF_ENABLE "enable"
+#define AST_JB_CONF_FORCE "force"
+#define AST_JB_CONF_MAX_SIZE "maxsize"
+#define AST_JB_CONF_RESYNCH_THRESHOLD "resyncthreshold"
+#define AST_JB_CONF_IMPL "impl"
+#define AST_JB_CONF_LOG "log"
+
+
+struct ast_jb_impl;
+
+
+/*!
+ * \brief General jitterbuffer state.
+ */
+struct ast_jb
+{
+ /*! \brief Jitterbuffer configuration. */
+ struct ast_jb_conf conf;
+ /*! \brief Jitterbuffer implementation to be used. */
+ struct ast_jb_impl *impl;
+ /*! \brief Jitterbuffer object, passed to the implementation. */
+ void *jbobj;
+ /*! \brief The time the jitterbuffer was created. */
+ struct timeval timebase;
+ /*! \brief The time the next frame should be played. */
+ long next;
+ /*! \brief Voice format of the last frame in. */
+ int last_format;
+ /*! \brief File for frame timestamp tracing. */
+ FILE *logfile;
+ /*! \brief Jitterbuffer internal state flags. */
+ unsigned int flags;
+};
+
+
+/*!
+ * \brief Checks the need of a jb use in a generic bridge.
+ * \param c0 first bridged channel.
+ * \param c1 second bridged channel.
+ *
+ * Called from ast_generic_bridge() when two channels are entering in a bridge.
+ * The function checks the need of a jitterbuffer, depending on both channel's
+ * configuration and technology properties. As a result, this function sets
+ * appropriate internal jb flags to the channels, determining further behaviour
+ * of the bridged jitterbuffers.
+ *
+ * \retval zero if there are no jitter buffers in use
+ * \retval non-zero if there are
+ */
+int ast_jb_do_usecheck(struct ast_channel *c0, struct ast_channel *c1);
+
+
+/*!
+ * \brief Calculates the time, left to the closest delivery moment in a bridge.
+ * \param c0 first bridged channel.
+ * \param c1 second bridged channel.
+ * \param time_left bridge time limit, or -1 if not set.
+ *
+ * Called from ast_generic_bridge() to determine the maximum time to wait for
+ * activity in ast_waitfor_n() call. If neihter of the channels is using jb,
+ * this function returns the time limit passed.
+ *
+ * \return maximum time to wait.
+ */
+int ast_jb_get_when_to_wakeup(struct ast_channel *c0, struct ast_channel *c1, int time_left);
+
+
+/*!
+ * \brief Puts a frame into a channel jitterbuffer.
+ * \param chan channel.
+ * \param f frame.
+ *
+ * Called from ast_generic_bridge() to put a frame into a channel's jitterbuffer.
+ * The function will successfuly enqueue a frame if and only if:
+ * 1. the channel is using a jitterbuffer (as determined by ast_jb_do_usecheck()),
+ * 2. the frame's type is AST_FRAME_VOICE,
+ * 3. the frame has timing info set and has length >= 2 ms,
+ * 4. there is no some internal error happened (like failed memory allocation).
+ * Frames, successfuly queued, should be delivered by the channel's jitterbuffer,
+ * when their delivery time has came.
+ * Frames, not successfuly queued, should be delivered immediately.
+ * Dropped by the jb implementation frames are considered successfuly enqueued as
+ * far as they should not be delivered at all.
+ *
+ * \retval 0 if the frame was queued
+ * \retval -1 if not
+ */
+int ast_jb_put(struct ast_channel *chan, struct ast_frame *f);
+
+
+/*!
+ * \brief Deliver the queued frames that should be delivered now for both channels.
+ * \param c0 first bridged channel.
+ * \param c1 second bridged channel.
+ *
+ * Called from ast_generic_bridge() to deliver any frames, that should be delivered
+ * for the moment of invocation. Does nothing if neihter of the channels is using jb
+ * or has any frames currently queued in. The function delivers frames usig ast_write()
+ * each of the channels.
+ */
+void ast_jb_get_and_deliver(struct ast_channel *c0, struct ast_channel *c1);
+
+
+/*!
+ * \brief Destroys jitterbuffer on a channel.
+ * \param chan channel.
+ *
+ * Called from ast_channel_free() when a channel is destroyed.
+ */
+void ast_jb_destroy(struct ast_channel *chan);
+
+
+/*!
+ * \brief Sets jitterbuffer configuration property.
+ * \param conf configuration to store the property in.
+ * \param varname property name.
+ * \param value property value.
+ *
+ * Called from a channel driver to build a jitterbuffer configuration tipically when
+ * reading a configuration file. It is not neccessary for a channel driver to know
+ * each of the jb configuration property names. The jitterbuffer itself knows them.
+ * The channel driver can pass each config var it reads through this function. It will
+ * return 0 if the variable was consumed from the jb conf.
+ *
+ * \return zero if the property was set to the configuration, -1 if not.
+ */
+int ast_jb_read_conf(struct ast_jb_conf *conf, const char *varname, const char *value);
+
+
+/*!
+ * \brief Configures a jitterbuffer on a channel.
+ * \param chan channel to configure.
+ * \param conf configuration to apply.
+ *
+ * Called from a channel driver when a channel is created and its jitterbuffer needs
+ * to be configured.
+ */
+void ast_jb_configure(struct ast_channel *chan, const struct ast_jb_conf *conf);
+
+
+/*!
+ * \brief Copies a channel's jitterbuffer configuration.
+ * \param chan channel.
+ * \param conf destination.
+ */
+void ast_jb_get_config(const struct ast_channel *chan, struct ast_jb_conf *conf);
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ABSTRACT_JB_H_ */
diff --git a/trunk/include/asterisk/acl.h b/trunk/include/asterisk/acl.h
new file mode 100644
index 000000000..79b787d70
--- /dev/null
+++ b/trunk/include/asterisk/acl.h
@@ -0,0 +1,84 @@
+/*
+ * 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 Access Control of various sorts
+ */
+
+#ifndef _ASTERISK_ACL_H
+#define _ASTERISK_ACL_H
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/network.h"
+#include "asterisk/io.h"
+
+#define AST_SENSE_DENY 0
+#define AST_SENSE_ALLOW 1
+
+/* Host based access control */
+
+/*! \brief internal representation of acl entries
+ * In principle user applications would have no need for this,
+ * but there is sometimes a need to extract individual items,
+ * e.g. to print them, and rather than defining iterators to
+ * navigate the list, and an externally visible 'struct ast_ha_entry',
+ * at least in the short term it is more convenient to make the whole
+ * thing public and let users play with them.
+ */
+struct ast_ha {
+ /* Host access rule */
+ struct in_addr netaddr;
+ struct in_addr netmask;
+ int sense;
+ struct ast_ha *next;
+};
+
+/*! \brief Free host access list */
+void ast_free_ha(struct ast_ha *ha);
+
+/*! \brief Append ACL entry to host access list. */
+struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error);
+
+/*! \brief Check IP address with host access list */
+int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin);
+
+/*! \brief Copy host access list */
+struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original);
+
+int ast_get_ip(struct sockaddr_in *sin, const char *value);
+
+int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service);
+
+int ast_ouraddrfor(struct in_addr *them, struct in_addr *us);
+
+int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr);
+
+int ast_str2cos(const char *value, unsigned int *cos);
+
+int ast_str2tos(const char *value, unsigned int *tos);
+const char *ast_tos2str(unsigned int tos);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_ACL_H */
diff --git a/trunk/include/asterisk/adsi.h b/trunk/include/asterisk/adsi.h
new file mode 100644
index 000000000..3c31574a5
--- /dev/null
+++ b/trunk/include/asterisk/adsi.h
@@ -0,0 +1,353 @@
+/*
+ * 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 ADSI Support (built upon Caller*ID)
+ */
+
+#ifndef _ASTERISK_ADSI_H
+#define _ASTERISK_ADSI_H
+
+#include "asterisk/callerid.h"
+/*! \name ADSI parameters */
+/*@{ */
+
+/* ADSI Message types */
+#define ADSI_MSG_DISPLAY 132
+#define ADSI_MSG_DOWNLOAD 133
+
+/* ADSI Parameters (display) */
+#define ADSI_LOAD_SOFTKEY 128
+#define ADSI_INIT_SOFTKEY_LINE 129
+#define ADSI_LOAD_VIRTUAL_DISP 130
+#define ADSI_LINE_CONTROL 131
+#define ADSI_INFORMATION 132
+#define ADSI_DISC_SESSION 133
+#define ADSI_SWITCH_TO_DATA 134
+#define ADSI_SWITCH_TO_VOICE 135
+#define ADSI_CLEAR_SOFTKEY 136
+#define ADSI_INPUT_CONTROL 137
+#define ADSI_INPUT_FORMAT 138
+#define ADSI_SWITCH_TO_PERIPH 139
+#define ADSI_MOVE_DATA 140
+#define ADSI_LOAD_DEFAULT 141
+#define ADSI_CONNECT_SESSION 142
+#define ADSI_CLEAR_TYPE_AHEAD 143
+#define ADSI_DISPLAY_CALL_BUF 144
+#define ADSI_CLEAR_CALL_BUF 145
+#define ADSI_SWITCH_TO_ALT 146
+#define ADSI_SWITCH_TO_GRAPHICS 147
+#define ADSI_CLEAR_SCREEN 148
+#define ADSI_QUERY_CONFIG 149
+#define ADSI_QUERY_CPEID 150
+#define ADSI_SWITCH_TO_APP 151
+
+/* Feature download messages */
+#define ADSI_LOAD_SOFTKEY_TABLE 128 /* Conveniently identical to the soft version */
+#define ADSI_LOAD_PREDEF_DISP 129 /* Load predefined display */
+#define ADSI_LOAD_SCRIPT 130
+#define ADSI_DOWNLOAD_CONNECT 131
+#define ADSI_DOWNLOAD_DISC 132
+
+/* Special return string codes */
+#define ADSI_ENCODED_DTMF 0x80 /* Transmit following chars with encoded dtmf */
+#define ADSI_ON_HOOK 0x81 /* Open switch-hook */
+#define ADSI_OFF_HOOK 0x82 /* Close switch-hook */
+#define ADSI_FLASH 0x83 /* Flash switch-hook */
+#define ADSI_DIAL_TONE_DETECT 0x84 /* Wait for dialtone */
+#define ADSI_LINE_NUMBER 0x85 /* Send current line number using DTMF/encoded DTMF */
+#define ADSI_BLANK 0x86 /* Blank (does nothing) */
+#define ADSI_SEND_CHARS 0x87 /* Send collected digits/characters */
+#define ADSI_CLEAR_CHARS 0x88 /* Clear characters/digits collected */
+#define ADSI_BACKSPACE 0x89 /* Erase last collected digit */
+#define ADSI_TAB_COLUMN 0x8A /* Display specified display column of current line */
+#define ADSI_GOTO_LINE 0x8B /* Go to given page and line number */
+#define ADSI_GOTO_LINE_REL 0x8C /* Go to given line (relative to current) */
+#define ADSI_PAGE_UP 0x8D /* Go up one page */
+#define ADSI_PAGE_DOWN 0x8E /* Go down one page */
+#define ADSI_EXTENDED_DTMF 0x8F /* Send DTMF tones for 250ms instead of 60 ms */
+#define ADSI_DELAY 0x90 /* Delay for given # (times 10) of ms */
+#define ADSI_DIAL_PULSE_ONE 0x91 /* Send a dial pulse "1" */
+#define ADSI_SWITCH_TO_DATA2 0x92 /* Switch CPE to data mode */
+#define ADSI_SWITCH_TO_VOICE2 0x93 /* Switch CPE to voice mode */
+#define ADSI_DISP_CALL_BUF 0x94 /* Display specified call buffer */
+#define ADSI_CLEAR_CALL_B 0x95 /* Clear specified call buffer */
+
+#ifdef __ADSI_CPE
+/* These messages are reserved for the ADSI CPE only */
+#define ADSI_DISPLAY_CONTROL 0x98 /* Store predefined display identified next / Display status display page */
+#define ADSI_DISPLAY_SOFT_KEYS 0x99 /* Display the script soft keys identified next */
+#define ADSI_CHANGE_STATE 0x9A /* Change state of service script */
+#define ADSI_START_CLEAR_TIMER 0x9B /* Start / Clear timer */
+#define ADSI_SET_SCRIPT_FLAG 0x9C /* Set / clear a script flag */
+#define ADSI_JUMP_TO_SUBSCRIPT 0x9D /* Jump to specified subscript */
+#define ADSI_EVENT_22_TRIGGER 0x9E /* Trigger an occurance of event 22 */
+#define ADSI_EVENT_23_TRIGGER 0x9f /* Trigger an occurance of event 23 */
+#define ADSI_EXIT 0xA0 /* Exit the service script interpreter */
+#endif
+
+/* Display pages */
+#define ADSI_INFO_PAGE 0x0
+#define ADSI_COMM_PAGE 0x1
+
+#define ADSI_KEY_APPS 16 /* 16 to 33 reserved for applications */
+
+/* Justification */
+#define ADSI_JUST_LEFT 0x2
+#define ADSI_JUST_RIGHT 0x1
+#define ADSI_JUST_CENT 0x0 /* Center */
+#define ADSI_JUST_IND 0x3 /* Indent */
+
+#define ADSI_KEY_SKT 0x80 /* Load from SKT */
+#define ADSI_KEY_HILITE 0x40 /* Highlight key */
+
+#define ADSI_DIR_FROM_LEFT (0)
+#define ADSI_DIR_FROM_RIGHT (1)
+
+/*@} */
+
+/*! Perform Asterisk ADSI initialization (for channel drivers that want
+ * to support ADSI when the handset is first lifted)
+ * \param chan Channel to initialize for ADSI (if supported)
+ *
+ * \retval 0 on success (or adsi unavailable.
+ * \retval -1 on hangup.
+ */
+extern int (*ast_adsi_channel_init)(struct ast_channel *chan);
+
+extern int (*ast_adsi_begin_download)(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version);
+
+extern int (*ast_adsi_end_download)(struct ast_channel *chan);
+
+/*! Restore ADSI initialization (for applications that play with ADSI
+ * and want to restore it to normal. If you touch "INFO" then you
+ * have to use the ast_adsi_channel_init again instead.
+ * \param chan Channel to restore
+ *
+ * \retval 0 on success (or adsi unavailable)
+ * \retval -1 on hangup
+ */
+extern int (*ast_adsi_channel_restore)(struct ast_channel *chan);
+
+/*!
+ * \brief Display some stuff on the screen
+ * \param chan Channel to display on
+ * \param lines NULL-terminated list of things to print (no more than 4 recommended)
+ * \param align list of alignments to use (ADSI_JUST_LEFT, ADSI_JUST_RIGHT, ADSI_JUST_CEN, etc..)
+ * \param voice whether to jump into voice mode when finished
+ *
+ * \retval 0 on success (or adsi unavailable)
+ * \retval -1 on hangup
+ */
+extern int (*ast_adsi_print)(struct ast_channel *chan, char **lines, int *align, int voice);
+
+/*!
+ * \brief Check if scripts for a given app are already loaded.
+ * Version may be -1, if any version is okay, or 0-255 for a specific version.
+ * \param chan Channel to test for loaded app
+ * \param app Four character app name (must be unique to your application)
+ * \param ver optional version number
+ * \param data Non-zero if you want to be put in data mode
+ *
+ * \retval 0 if scripts is not loaded or not an ADSI CPE
+ * \retval -1 on hangup
+ * \retval 1 if script already loaded.
+ */
+extern int (*ast_adsi_load_session)(struct ast_channel *chan, unsigned char *app, int ver, int data);
+extern int (*ast_adsi_unload_session)(struct ast_channel *chan);
+
+/* ADSI Layer 2 transmission functions */
+extern int (*ast_adsi_transmit_messages)(struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype);
+extern int (*ast_adsi_transmit_message)(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype);
+extern int (*ast_adsi_transmit_message_full)(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype, int dowait);
+/*! Read some encoded DTMF data.
+ * Returns number of bytes received
+ */
+extern int (*ast_adsi_read_encoded_dtmf)(struct ast_channel *chan, unsigned char *buf, int maxlen);
+
+/* ADSI Layer 3 creation functions */
+
+/*!
+ * \brief Connects an ADSI Display Session
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param fdn Optional 4 byte Feature Download Number (for loading soft keys)
+ * \param ver Optional version number (0-255, or -1 to omit)
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+
+extern int (*ast_adsi_connect_session)(unsigned char *buf, unsigned char *fdn, int ver);
+
+/*! Build Query CPE ID of equipment.
+ * Returns number of bytes added to message
+ */
+extern int (*ast_adsi_query_cpeid)(unsigned char *buf);
+extern int (*ast_adsi_query_cpeinfo)(unsigned char *buf);
+
+/*! Get CPE ID from an attached ADSI compatible CPE.
+ * Returns 1 on success, storing 4 bytes of CPE ID at buf
+ * or -1 on hangup, or 0 if there was no hangup but it failed to find the
+ * device ID. Returns to voice mode if "voice" is non-zero.
+ */
+extern int (*ast_adsi_get_cpeid)(struct ast_channel *chan, unsigned char *cpeid, int voice);
+
+extern int (*ast_adsi_get_cpeinfo)(struct ast_channel *chan, int *width, int *height, int *buttons, int voice);
+
+/*!
+ * \brief Begin an ADSI script download
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param service a 1-18 byte name of the feature
+ * \param fdn 4 byte Feature Download Number (for loading soft keys)
+ * \param sec 4 byte vendor security code
+ * \param ver version number (0-255, or -1 to omit)
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+
+extern int (*ast_adsi_download_connect)(unsigned char *buf, char *service, unsigned char *fdn, unsigned char *sec, int ver);
+
+/*!
+ * \brief Disconnects a running session.
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_disconnect_session)(unsigned char *buf);
+
+/*!
+ * \brief Disconnects (and hopefully saves) a downloaded script
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_download_disconnect)(unsigned char *buf);
+
+/*!
+ * \brief Puts CPE in data mode.
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_data_mode)(unsigned char *buf);
+extern int (*ast_adsi_clear_soft_keys)(unsigned char *buf);
+extern int (*ast_adsi_clear_screen)(unsigned char *buf);
+
+/*!
+ * \brief Puts CPE in voice mode.
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param when (a time in seconds) to make the switch
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_voice_mode)(unsigned char *buf, int when);
+
+/*!
+ * \brief Returns non-zero if Channel does or might support ADSI
+ * \param chan Channel to check
+ */
+extern int (*ast_adsi_available)(struct ast_channel *chan);
+
+/*!
+ * \brief Loads a line of info into the display.
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param page Page to load (ADSI_COMM_PAGE or ADSI_INFO_PAGE)
+ * \param line Line number to load (1-4 for Comm page, 1-33 for info page)
+ * \param just Line justification (ADSI_JUST_LEFT, ADSI_JUST_RIGHT, ADSI_JUST_CENT, ADSI_JUST_IND)
+ * \param wrap Wrap (1 = yes, 0 = no)
+ * \param col1 Text to place in first column
+ * \param col2 Text to place in second column
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+
+extern int (*ast_adsi_display)(unsigned char *buf, int page, int line, int just, int wrap, char *col1, char *col2);
+
+/*!
+ * \brief Sets the current line and page.
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param page Which page (ADSI_COMM_PAGE or ADSI_INFO_PAGE)
+ * \param line Line number (1-33 for info page, 1-4 for comm page)
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+
+extern int (*ast_adsi_set_line)(unsigned char *buf, int page, int line);
+
+/*!
+ * \brief Creates "load soft key" parameters
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param key Key code from 2 to 33, for which key we are loading
+ * \param llabel Long label for key (1-18 bytes)
+ * \param slabel Short label for key (1-7 bytes)
+ * \param ret Optional return sequence (NULL for none)
+ * \param data whether to put CPE in data mode before sending digits
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_load_soft_key)(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data);
+
+/*!
+ * \brief Set which soft keys should be displayed
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param keys Array of 8 unsigned chars with the key numbers, may be OR'd with ADSI_KEY_HILITE
+ * But remember, the last two keys aren't real keys, they're for scrolling
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_set_keys)(unsigned char *buf, unsigned char *keys);
+
+/*!
+ * \brief Set input information
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param page Which page to input on (ADSI_COMM_PAGE or ADSI_INFO_PAGE)
+ * \param line Line number to input on
+ * \param display Set to zero to obscure input, or 1 to leave visible
+ * \param format Format number to use (0-7)
+ * \param just Justification (left, right center, indent)
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_input_control)(unsigned char *buf, int page, int line, int display, int format, int just);
+
+/*!
+ * \brief Set input format
+ * \param buf Character buffer to create parameter in (must have at least 256 free)
+ * \param num Which format we are setting
+ * \param dir Which direction (ADSI_DIR_FROM_LEFT or ADSI_DIR_FROM_RIGHT)
+ * \param wrap Set to 1 to permit line wrap, or 0 if not
+ * \param format1 Format for column 1
+ * \param format2 Format for column 2
+ *
+ * \retval number of bytes added to buffer
+ * \retval -1 on error.
+ */
+extern int (*ast_adsi_input_format)(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2);
+
+#endif /* _ASTERISK_ADSI_H */
+
diff --git a/trunk/include/asterisk/ael_structs.h b/trunk/include/asterisk/ael_structs.h
new file mode 100644
index 000000000..0a58ed378
--- /dev/null
+++ b/trunk/include/asterisk/ael_structs.h
@@ -0,0 +1,123 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, 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 Structures for AEL - the Asterisk extension language
+ *
+ * \ref pbx_ael.c
+ * \todo document this file (ael.h)
+ */
+
+#ifndef _ASTERISK_AEL_STRUCTS_H
+#define _ASTERISK_AEL_STRUCTS_H
+
+/*
+ * We include asterisk/paths.h here because it is a convenient place
+ * that doesn't require us to rebuild ael files from .fl/.y
+ */
+#include "asterisk/paths.h"
+
+#include "pval.h"
+
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+/* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+
+#if defined(LONG_LONG_MIN) && !defined(QUAD_MIN)
+#define QUAD_MIN LONG_LONG_MIN
+#endif
+#if defined(LONG_LONG_MAX) && !defined(QUAD_MAX)
+#define QUAD_MAX LONG_LONG_MAX
+#endif
+
+# if ! defined(QUAD_MIN)
+# define QUAD_MIN (-0x7fffffffffffffffLL-1)
+# endif
+# if ! defined(QUAD_MAX)
+# define QUAD_MAX (0x7fffffffffffffffLL)
+# endif
+
+
+#if 0
+#endif
+void ael2_semantic_check(pval *item, int *errs, int *warns, int *notes);
+pval *npval(pvaltype type, int first_line, int last_line, int first_column, int last_column);
+pval *linku1(pval *head, pval *tail);
+void ael2_print(char *fname, pval *tree);
+struct pval *ael2_parse(char *fname, int *errs); /* in ael.flex */
+void destroy_pval(pval *item);
+
+extern char *prev_word; /* in ael.flex */
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* for passing info into and out of yyparse */
+struct parse_io
+{
+ struct pval *pval; /* yyparse will set this to point to the parse tree */
+ yyscan_t scanner; /* yylex needs a scanner. Set it up, and pass it in */
+ int syntax_error_count; /* the count of syntax errors encountered */
+};
+
+/* for CODE GENERATION */
+
+typedef enum { AEL_APPCALL, AEL_CONTROL1, AEL_FOR_CONTROL, AEL_IF_CONTROL, AEL_IFTIME_CONTROL, AEL_RAND_CONTROL, AEL_LABEL, AEL_RETURN } ael_priority_type;
+
+
+struct ael_priority
+{
+ int priority_num;
+ ael_priority_type type;
+
+ char *app;
+ char *appargs;
+
+ struct pval *origin;
+ struct ael_extension *exten;
+
+ struct ael_priority *goto_true;
+ struct ael_priority *goto_false;
+ struct ael_priority *next;
+};
+
+struct ael_extension
+{
+ char *name;
+ char *cidmatch;
+ char *hints;
+ int regexten;
+ int is_switch;
+
+ struct ast_context *context;
+
+ struct ael_priority *plist;
+ struct ael_priority *plist_last;
+ struct ael_extension *next_exten;
+
+ struct ael_priority *loop_break; /*!< set by latest loop for breaks */
+ struct ael_priority *loop_continue; /*!< set by lastest loop for continuing */
+ struct ael_priority *return_target;
+ int return_needed;
+};
+
+#endif /* _ASTERISK_AEL_STRUCTS_H */
diff --git a/trunk/include/asterisk/aes.h b/trunk/include/asterisk/aes.h
new file mode 100644
index 000000000..25e76153f
--- /dev/null
+++ b/trunk/include/asterisk/aes.h
@@ -0,0 +1,67 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 20075, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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
+ * Wrappers for AES encryption/decryption
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * These wrappers provided a generic interface to either the
+ * AES methods provided by OpenSSL's crypto library, or the
+ * AES implementation included with Asterisk.
+ */
+
+#ifndef _ASTERISK_AES_H
+#define _ASTERISK_AES_H
+
+#ifdef HAVE_CRYPTO
+
+/* Use the OpenSSL crypto library */
+#include "openssl/aes.h"
+
+typedef AES_KEY ast_aes_encrypt_key;
+typedef AES_KEY ast_aes_decrypt_key;
+
+#define ast_aes_encrypt_key(key, context) AES_set_encrypt_key(key, 1024, context)
+
+#define ast_aes_decrypt_key(key, context) AES_set_decrypt_key(key, 1024, context)
+
+#define ast_aes_encrypt(in, out, context) AES_encrypt(in, out, context)
+
+#define ast_aes_decrypt(in, out, context) AES_decrypt(in, out, context)
+
+#else /* !HAVE_CRYPTO */
+
+/* Use the included AES implementation */
+
+#include "aes_internal.h"
+
+typedef aes_encrypt_ctx ast_aes_encrypt_key;
+typedef aes_decrypt_ctx ast_aes_decrypt_key;
+
+#define ast_aes_encrypt_key(key, context) aes_encrypt_key128(key, context)
+
+#define ast_aes_decrypt_key(key, context) aes_decrypt_key128(key, context)
+
+#define ast_aes_encrypt(in, out, context) aes_encrypt(in, out, context)
+
+#define ast_aes_decrypt(in, out, context) aes_decrypt(in, out, context)
+
+#endif /* !HAVE_CRYPTO */
+
+#endif /* _ASTERISK_AES_H */
diff --git a/trunk/include/asterisk/aes_internal.h b/trunk/include/asterisk/aes_internal.h
new file mode 100644
index 000000000..18c27a6d4
--- /dev/null
+++ b/trunk/include/asterisk/aes_internal.h
@@ -0,0 +1,170 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * 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.
+ */
+
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+*/
+/*!\file
+
+ \brief This file contains the definitions required to use AES in C. See aesopt.h
+ for optimisation details.
+*/
+
+#ifndef _AES_INTERNAL_H
+#define _AES_INTERNAL_H
+
+/* This include is used to find 8 & 32 bit unsigned integer types */
+#include "limits.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#define AES_128 /* define if AES with 128 bit keys is needed */
+#undef AES_192 /* define if AES with 192 bit keys is needed */
+#undef AES_256 /* define if AES with 256 bit keys is needed */
+#undef AES_VAR /* define if a variable key size is needed */
+
+/* The following must also be set in assembler files if being used */
+
+#define AES_ENCRYPT /* if support for encryption is needed */
+#define AES_DECRYPT /* if support for decryption is needed */
+#define AES_ERR_CHK /* for parameter checks & error return codes */
+
+#if UCHAR_MAX == 0xff /* an unsigned 8 bit type */
+ typedef unsigned char aes_08t;
+#else
+#error Please define aes_08t as an 8-bit unsigned integer type in aes.h
+#endif
+
+#if UINT_MAX == 0xffffffff /* an unsigned 32 bit type */
+ typedef unsigned int aes_32t;
+#elif ULONG_MAX == 0xffffffff
+ typedef unsigned long aes_32t;
+#else
+#error Please define aes_32t as a 32-bit unsigned integer type in aes.h
+#endif
+
+#define AES_BLOCK_SIZE 16 /* the AES block size in bytes */
+#define N_COLS 4 /* the number of columns in the state */
+
+/* a maximum of 60 32-bit words are needed for the key schedule but */
+/* 64 are claimed to allow space at the top for a CBC xor buffer. */
+/* If this is not needed, this value can be reduced to 60. A value */
+/* of 64 may also help in maintaining alignment in some situations */
+#define KS_LENGTH 64
+
+#ifdef AES_ERR_CHK
+#define aes_ret int
+#define aes_good 0
+#define aes_error -1
+#else
+#define aes_ret void
+#endif
+
+#ifndef AES_DLL /* implement normal/DLL functions */
+#define aes_rval aes_ret
+#else
+#define aes_rval aes_ret __declspec(dllexport) _stdcall
+#endif
+
+/* This routine must be called before first use if non-static */
+/* tables are being used */
+
+void gen_tabs(void);
+
+/* The key length (klen) is input in bytes when it is in the range */
+/* 16 <= klen <= 32 or in bits when in the range 128 <= klen <= 256 */
+
+#ifdef AES_ENCRYPT
+
+typedef struct
+{ aes_32t ks[KS_LENGTH];
+} aes_encrypt_ctx;
+
+#if defined(AES_128) || defined(AES_VAR)
+aes_rval aes_encrypt_key128(const void *in_key, aes_encrypt_ctx cx[1]);
+#endif
+
+#if defined(AES_192) || defined(AES_VAR)
+aes_rval aes_encrypt_key192(const void *in_key, aes_encrypt_ctx cx[1]);
+#endif
+
+#if defined(AES_256) || defined(AES_VAR)
+aes_rval aes_encrypt_key256(const void *in_key, aes_encrypt_ctx cx[1]);
+#endif
+
+#if defined(AES_VAR)
+aes_rval aes_encrypt_key(const void *in_key, int key_len, aes_encrypt_ctx cx[1]);
+#endif
+
+aes_rval aes_encrypt(const void *in_blk, void *out_blk, const aes_encrypt_ctx cx[1]);
+#endif
+
+#ifdef AES_DECRYPT
+
+typedef struct
+{ aes_32t ks[KS_LENGTH];
+} aes_decrypt_ctx;
+
+#if defined(AES_128) || defined(AES_VAR)
+aes_rval aes_decrypt_key128(const void *in_key, aes_decrypt_ctx cx[1]);
+#endif
+
+#if defined(AES_192) || defined(AES_VAR)
+aes_rval aes_decrypt_key192(const void *in_key, aes_decrypt_ctx cx[1]);
+#endif
+
+#if defined(AES_256) || defined(AES_VAR)
+aes_rval aes_decrypt_key256(const void *in_key, aes_decrypt_ctx cx[1]);
+#endif
+
+#if defined(AES_VAR)
+aes_rval aes_decrypt_key(const void *in_key, int key_len, aes_decrypt_ctx cx[1]);
+#endif
+
+aes_rval aes_decrypt(const void *in_blk, void *out_blk, const aes_decrypt_ctx cx[1]);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/trunk/include/asterisk/agi.h b/trunk/include/asterisk/agi.h
new file mode 100644
index 000000000..5085f05df
--- /dev/null
+++ b/trunk/include/asterisk/agi.h
@@ -0,0 +1,66 @@
+/*
+ * 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 AGI Extension interfaces - Asterisk Gateway Interface
+ */
+
+#ifndef _ASTERISK_AGI_H
+#define _ASTERISK_AGI_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct agi_state {
+ int fd; /*!< FD for general output */
+ int audio; /*!< FD for audio output */
+ int ctrl; /*!< FD for input control */
+ unsigned int fast:1; /*!< flag for fast agi or not */
+ struct ast_speech *speech; /*!< Speech structure for speech recognition */
+} AGI;
+
+typedef struct agi_command {
+ /* Null terminated list of the words of the command */
+ char *cmda[AST_MAX_CMD_LEN];
+ /* Handler for the command (channel, AGI state, # of arguments, argument list).
+ Returns RESULT_SHOWUSAGE for improper arguments */
+ int (*handler)(struct ast_channel *chan, AGI *agi, int argc, char *argv[]);
+ /* Summary of the command (< 60 characters) */
+ char *summary;
+ /* Detailed usage information */
+ char *usage;
+ /* Does this application run dead */
+ int dead;
+ /* Pointer to module that registered the agi command */
+ struct ast_module *mod;
+ /* Linked list pointer */
+ AST_LIST_ENTRY(agi_command) list;
+} agi_command;
+
+int ast_agi_fdprintf(struct ast_channel *chan, int fd, char *fmt, ...);
+int ast_agi_register(struct ast_module *mod, agi_command *cmd);
+int ast_agi_unregister(struct ast_module *mod, agi_command *cmd);
+void ast_agi_register_multiple(struct ast_module *mod, agi_command *cmd, int len);
+void ast_agi_unregister_multiple(struct ast_module *mod, agi_command *cmd, int len);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_AGI_H */
diff --git a/trunk/include/asterisk/alaw.h b/trunk/include/asterisk/alaw.h
new file mode 100644
index 000000000..0ef42becc
--- /dev/null
+++ b/trunk/include/asterisk/alaw.h
@@ -0,0 +1,86 @@
+/*
+ * 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 A-Law to Signed linear conversion
+ */
+
+#ifndef _ASTERISK_ALAW_H
+#define _ASTERISK_ALAW_H
+
+
+/*! \brief
+ * To init the alaw to slinear conversion stuff, this needs to be run.
+ */
+void ast_alaw_init(void);
+
+#define AST_ALAW_BIT_LOSS 4
+#define AST_ALAW_STEP (1 << AST_ALAW_BIT_LOSS)
+#define AST_ALAW_TAB_SIZE (32768 / AST_ALAW_STEP + 1)
+#define AST_ALAW_SIGN_BIT 0x80
+#define AST_ALAW_AMI_MASK 0x55
+
+
+/*! \brief converts signed linear to alaw */
+#ifndef G711_NEW_ALGORITHM
+extern unsigned char __ast_lin2a[8192];
+#else
+extern unsigned char __ast_lin2a[AST_ALAW_TAB_SIZE];
+#endif
+
+/*! help */
+extern short __ast_alaw[256];
+
+#ifndef G711_NEW_ALGORITHM
+#define AST_LIN2A(a) (__ast_lin2a[((unsigned short)(a)) >> 3])
+#else
+#define AST_LIN2A_LOOKUP(mag) \
+ __ast_lin2a[(mag) >> AST_ALAW_BIT_LOSS]
+
+/*! \brief Convert signed linear sample to sign-magnitude pair for a-Law */
+static inline void ast_alaw_get_sign_mag(short sample, unsigned *sign, unsigned *mag)
+{
+ /* It may look illogical to retrive the sign this way in both cases,
+ * but this helps gcc eliminate the branch below and produces
+ * faster code */
+ *sign = ((unsigned short)sample >> 8) & AST_ALAW_SIGN_BIT;
+#if defined(G711_REDUCED_BRANCHING)
+ {
+ unsigned dual_mag = (-sample << 16) | (unsigned short)sample;
+ *mag = (dual_mag >> (*sign >> 3)) & 0xffffU;
+ }
+#else
+ if (sample < 0)
+ *mag = -sample;
+ else
+ *mag = sample;
+#endif /* G711_REDUCED_BRANCHING */
+ *sign ^= AST_ALAW_SIGN_BIT;
+}
+
+static inline unsigned char AST_LIN2A(short sample)
+{
+ unsigned mag, sign;
+ ast_alaw_get_sign_mag(sample, &sign, &mag);
+ return (sign | AST_LIN2A_LOOKUP(mag)) ^ AST_ALAW_AMI_MASK;
+}
+#endif
+
+#define AST_ALAW(a) (__ast_alaw[(int)(a)])
+
+#endif /* _ASTERISK_ALAW_H */
diff --git a/trunk/include/asterisk/app.h b/trunk/include/asterisk/app.h
new file mode 100644
index 000000000..5be236608
--- /dev/null
+++ b/trunk/include/asterisk/app.h
@@ -0,0 +1,463 @@
+/*
+ * 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 Application convenience functions, designed to give consistent
+ * look and feel to Asterisk apps.
+ */
+
+#ifndef _ASTERISK_APP_H
+#define _ASTERISK_APP_H
+
+struct ast_flags64;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/* IVR stuff */
+
+/*! \brief Callback function for IVR
+ \return returns 0 on completion, -1 on hangup or digit if interrupted
+ */
+typedef int (*ast_ivr_callback)(struct ast_channel *chan, char *option, void *cbdata);
+
+typedef enum {
+ AST_ACTION_UPONE, /*!< adata is unused */
+ AST_ACTION_EXIT, /*!< adata is the return value for ast_ivr_menu_run if channel was not hungup */
+ AST_ACTION_CALLBACK, /*!< adata is an ast_ivr_callback */
+ AST_ACTION_PLAYBACK, /*!< adata is file to play */
+ AST_ACTION_BACKGROUND, /*!< adata is file to play */
+ AST_ACTION_PLAYLIST, /*!< adata is list of files, separated by ; to play */
+ AST_ACTION_MENU, /*!< adata is a pointer to an ast_ivr_menu */
+ AST_ACTION_REPEAT, /*!< adata is max # of repeats, cast to a pointer */
+ AST_ACTION_RESTART, /*!< adata is like repeat, but resets repeats to 0 */
+ AST_ACTION_TRANSFER, /*!< adata is a string with exten\verbatim[@context]\endverbatim */
+ AST_ACTION_WAITOPTION, /*!< adata is a timeout, or 0 for defaults */
+ AST_ACTION_NOOP, /*!< adata is unused */
+ AST_ACTION_BACKLIST, /*!< adata is list of files separated by ; allows interruption */
+} ast_ivr_action;
+
+/*!
+ Special "options" are:
+ \arg "s" - "start here (one time greeting)"
+ \arg "g" - "greeting/instructions"
+ \arg "t" - "timeout"
+ \arg "h" - "hangup"
+ \arg "i" - "invalid selection"
+
+*/
+struct ast_ivr_option {
+ char *option;
+ ast_ivr_action action;
+ void *adata;
+};
+
+struct ast_ivr_menu {
+ char *title; /*!< Title of menu */
+ unsigned int flags; /*!< Flags */
+ struct ast_ivr_option *options; /*!< All options */
+};
+
+#define AST_IVR_FLAG_AUTORESTART (1 << 0)
+
+#define AST_IVR_DECLARE_MENU(holder, title, flags, foo...) \
+ static struct ast_ivr_option __options_##holder[] = foo;\
+ static struct ast_ivr_menu holder = { title, flags, __options_##holder }
+
+
+/*! \brief Runs an IVR menu
+ \return returns 0 on successful completion, -1 on hangup, or -2 on user error in menu */
+int ast_ivr_menu_run(struct ast_channel *c, struct ast_ivr_menu *menu, void *cbdata);
+
+/*! \brief Plays a stream and gets DTMF data from a channel
+ * \param c Which channel one is interacting with
+ * \param prompt File to pass to ast_streamfile (the one that you wish to play).
+ * It is also valid for this to be multiple files concatenated by "&".
+ * For example, "file1&file2&file3".
+ * \param s The location where the DTMF data will be stored
+ * \param maxlen Max Length of the data
+ * \param timeout Timeout length waiting for data(in milliseconds). Set to 0 for standard timeout(six seconds), or -1 for no time out.
+ *
+ * This function was designed for application programmers for situations where they need
+ * to play a message and then get some DTMF data in response to the message. If a digit
+ * is pressed during playback, it will immediately break out of the message and continue
+ * execution of your code.
+ */
+int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout);
+
+/*! \brief Full version with audiofd and controlfd. NOTE: returns '2' on ctrlfd available, not '1' like other full functions */
+int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);
+
+void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
+ int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
+ int (*messagecount_func)(const char *context, const char *mailbox, const char *folder));
+
+void ast_uninstall_vm_functions(void);
+
+/*! \brief Determine if a given mailbox has any voicemail */
+int ast_app_has_voicemail(const char *mailbox, const char *folder);
+
+/*! \brief Determine number of new/old messages in a mailbox */
+int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs);
+
+/*! \brief Determine number of messages in a given mailbox and folder */
+int ast_app_messagecount(const char *context, const char *mailbox, const char *folder);
+
+/*! \brief Safely spawn an external program while closing file descriptors
+ \note This replaces the \b system call in all Asterisk modules
+*/
+int ast_safe_system(const char *s);
+
+/*!
+ * \brief Replace the SIGCHLD handler
+ *
+ * Normally, Asterisk has a SIGCHLD handler that is cleaning up all zombie
+ * processes from forking elsewhere in Asterisk. However, if you want to
+ * wait*() on the process to retrieve information about it's exit status,
+ * then this signal handler needs to be temporarily replaced.
+ *
+ * Code that executes this function *must* call ast_unreplace_sigchld()
+ * after it is finished doing the wait*().
+ */
+void ast_replace_sigchld(void);
+
+/*!
+ * \brief Restore the SIGCHLD handler
+ *
+ * This function is called after a call to ast_replace_sigchld. It restores
+ * the SIGCHLD handler that cleans up any zombie processes.
+ */
+void ast_unreplace_sigchld(void);
+
+/*!
+ \brief Send DTMF to a channel
+
+ \param chan The channel that will receive the DTMF frames
+ \param peer (optional) Peer channel that will be autoserviced while the
+ primary channel is receiving DTMF
+ \param digits This is a string of characters representing the DTMF digits
+ to be sent to the channel. Valid characters are
+ "0123456789*#abcdABCD". Note: You can pass arguments 'f' or
+ 'F', if you want to Flash the channel (if supported by the
+ channel), or 'w' to add a 500 millisecond pause to the DTMF
+ sequence.
+ \param between This is the number of milliseconds to wait in between each
+ DTMF digit. If zero milliseconds is specified, then the
+ default value of 100 will be used.
+ \param duration This is the duration that each DTMF digit should have.
+*/
+int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration);
+
+/*! \brief Stream a filename (or file descriptor) as a generator. */
+int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride);
+
+/*!
+ * \brief Stream a file with fast forward, pause, reverse, restart.
+ * \param chan
+ * \param file filename
+ * \param fwd, rev, stop, pause, restart, skipms, offsetms
+ *
+ * Before calling this function, set this to be the number
+ * of ms to start from the beginning of the file. When the function
+ * returns, it will be the number of ms from the beginning where the
+ * playback stopped. Pass NULL if you don't care.
+ */
+int ast_control_streamfile(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *pause, const char *restart, int skipms, long *offsetms);
+
+/*! \brief Play a stream and wait for a digit, returning the digit that was pressed */
+int ast_play_and_wait(struct ast_channel *chan, const char *fn);
+
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf);
+
+/*! \brief Record a file for a max amount of time (in seconds), in a given list of formats separated by '|', outputting the duration of the recording, and with a maximum
+ \n
+ permitted silence time in milliseconds of 'maxsilence' under 'silencethreshold' or use '-1' for either or both parameters for defaults.
+ calls ast_unlock_path() on 'path' if passed */
+int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int silencethreshold, int maxsilence_ms, const char *path);
+
+/*! \brief Record a message and prepend the message to the given record file after
+ playing the optional playfile (or a beep), storing the duration in
+ 'duration' and with a maximum permitted silence time in milliseconds of 'maxsilence' under
+ 'silencethreshold' or use '-1' for either or both parameters for defaults. */
+int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime_sec, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence_ms);
+
+enum AST_LOCK_RESULT {
+ AST_LOCK_SUCCESS = 0,
+ AST_LOCK_TIMEOUT = -1,
+ AST_LOCK_PATH_NOT_FOUND = -2,
+ AST_LOCK_FAILURE = -3,
+};
+
+/*! \brief Type of locking to use in ast_lock_path / ast_unlock_path */
+enum AST_LOCK_TYPE {
+ AST_LOCK_TYPE_LOCKFILE = 0,
+ AST_LOCK_TYPE_FLOCK = 1,
+};
+
+/*!
+ * \brief Set the type of locks used by ast_lock_path()
+ * \param type the locking type to use
+ */
+void ast_set_lock_type(enum AST_LOCK_TYPE type);
+
+/*!
+ * \brief Lock a filesystem path.
+ * \param path the path to be locked
+ * \return one of \ref AST_LOCK_RESULT values
+ */
+enum AST_LOCK_RESULT ast_lock_path(const char *path);
+
+/*! \brief Unlock a path */
+int ast_unlock_path(const char *path);
+
+/*! \brief Read a file into asterisk*/
+char *ast_read_textfile(const char *file);
+
+struct ast_group_info;
+
+/*! \brief Split a group string into group and category, returning a default category if none is provided. */
+int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max);
+
+/*! \brief Set the group for a channel, splitting the provided data into group and category, if specified. */
+int ast_app_group_set_channel(struct ast_channel *chan, const char *data);
+
+/*! \brief Get the current channel count of the specified group and category. */
+int ast_app_group_get_count(const char *group, const char *category);
+
+/*! \brief Get the current channel count of all groups that match the specified pattern and category. */
+int ast_app_group_match_get_count(const char *groupmatch, const char *category);
+
+/*! \brief Discard all group counting for a channel */
+int ast_app_group_discard(struct ast_channel *chan);
+
+/*! \brief Update all group counting for a channel to a new one */
+int ast_app_group_update(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+/*! \brief Write Lock the group count list */
+int ast_app_group_list_wrlock(void);
+
+/*! \brief Read Lock the group count list */
+int ast_app_group_list_rdlock(void);
+
+/*! \brief Get the head of the group count list */
+struct ast_group_info *ast_app_group_list_head(void);
+
+/*! \brief Unlock the group count list */
+int ast_app_group_list_unlock(void);
+
+/*!
+ \brief Define an application argument
+ \param name The name of the argument
+*/
+#define AST_APP_ARG(name) char *name
+
+/*!
+ \brief Declare a structure to hold the application's arguments.
+ \param name The name of the structure
+ \param arglist The list of arguments, defined using AST_APP_ARG
+
+ This macro defines a structure intended to be used in a call
+ to ast_app_separate_args(). The structure includes all the
+ arguments specified, plus an argv array that overlays them and an
+ argc argument counter. The arguments must be declared using AST_APP_ARG,
+ and they will all be character pointers (strings).
+
+ \note The structure is <b>not</b> initialized, as the call to
+ ast_app_separate_args() will perform that function before parsing
+ the arguments.
+ */
+#define AST_DECLARE_APP_ARGS(name, arglist) \
+ struct { \
+ unsigned int argc; \
+ char *argv[0]; \
+ arglist \
+ } name
+
+/*!
+ \brief Performs the 'standard' argument separation process for an application.
+ \param args An argument structure defined using AST_DECLARE_APP_ARGS
+ \param parse A modifiable buffer containing the input to be parsed
+
+ This function will separate the input string using the standard argument
+ separator character ',' and fill in the provided structure, including
+ the argc argument counter field.
+ */
+#define AST_STANDARD_APP_ARGS(args, parse) \
+ args.argc = ast_app_separate_args(parse, ',', args.argv, (sizeof(args) - sizeof(args.argc)) / sizeof(args.argv[0]))
+
+/*!
+ \brief Performs the 'nonstandard' argument separation process for an application.
+ \param args An argument structure defined using AST_DECLARE_APP_ARGS
+ \param parse A modifiable buffer containing the input to be parsed
+ \param sep A nonstandard separator character
+
+ This function will separate the input string using the nonstandard argument
+ separator character and fill in the provided structure, including
+ the argc argument counter field.
+ */
+#define AST_NONSTANDARD_APP_ARGS(args, parse, sep) \
+ args.argc = ast_app_separate_args(parse, sep, args.argv, (sizeof(args) - sizeof(args.argc)) / sizeof(args.argv[0]))
+
+/*!
+ \brief Separate a string into arguments in an array
+ \param buf The string to be parsed (this must be a writable copy, as it will be modified)
+ \param delim The character to be used to delimit arguments
+ \param array An array of 'char *' to be filled in with pointers to the found arguments
+ \param arraylen The number of elements in the array (i.e. the number of arguments you will accept)
+
+ Note: if there are more arguments in the string than the array will hold, the last element of
+ the array will contain the remaining arguments, not separated.
+
+ The array will be completely zeroed by this function before it populates any entries.
+
+ \return The number of arguments found, or zero if the function arguments are not valid.
+*/
+unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen);
+
+/*!
+ \brief A structure to hold the description of an application 'option'.
+
+ Application 'options' are single-character flags that can be supplied
+ to the application to affect its behavior; they can also optionally
+ accept arguments enclosed in parenthesis.
+
+ These structures are used by the ast_app_parse_options function, uses
+ this data to fill in a flags structure (to indicate which options were
+ supplied) and array of argument pointers (for those options that had
+ arguments supplied).
+ */
+struct ast_app_option {
+ /*! \brief The flag bit that represents this option. */
+ uint64_t flag;
+ /*! \brief The index of the entry in the arguments array
+ that should be used for this option's argument. */
+ unsigned int arg_index;
+};
+
+#define BEGIN_OPTIONS {
+#define END_OPTIONS }
+
+/*!
+ \brief Declares an array of options for an application.
+ \param holder The name of the array to be created
+ \param options The actual options to be placed into the array
+ \sa ast_app_parse_options
+
+ This macro declares a 'static const' array of \c struct \c ast_option
+ elements to hold the list of available options for an application.
+ Each option must be declared using either the AST_APP_OPTION()
+ or AST_APP_OPTION_ARG() macros.
+
+ Example usage:
+ \code
+ enum {
+ OPT_JUMP = (1 << 0),
+ OPT_BLAH = (1 << 1),
+ OPT_BLORT = (1 << 2),
+ } my_app_option_flags;
+
+ enum {
+ OPT_ARG_BLAH = 0,
+ OPT_ARG_BLORT,
+ !! this entry tells how many possible arguments there are,
+ and must be the last entry in the list
+ OPT_ARG_ARRAY_SIZE,
+ } my_app_option_args;
+
+ AST_APP_OPTIONS(my_app_options, {
+ AST_APP_OPTION('j', OPT_JUMP),
+ AST_APP_OPTION_ARG('b', OPT_BLAH, OPT_ARG_BLAH),
+ AST_APP_OPTION_BLORT('B', OPT_BLORT, OPT_ARG_BLORT),
+ });
+
+ static int my_app_exec(struct ast_channel *chan, void *data)
+ {
+ char *options;
+ struct ast_flags opts = { 0, };
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+
+ ... do any argument parsing here ...
+
+ if (ast_parseoptions(my_app_options, &opts, opt_args, options)) {
+ ast_module_user_remove(u);
+ return -1;
+ }
+ }
+ \endcode
+ */
+#define AST_APP_OPTIONS(holder, options...) \
+ static const struct ast_app_option holder[128] = options
+
+/*!
+ \brief Declares an application option that does not accept an argument.
+ \param option The single character representing the option
+ \param flagno The flag index to be set if this option is present
+ \sa AST_APP_OPTIONS, ast_app_parse_options
+ */
+#define AST_APP_OPTION(option, flagno) \
+ [option] = { .flag = flagno }
+
+/*!
+ \brief Declares an application option that accepts an argument.
+ \param option The single character representing the option
+ \param flagno The flag index to be set if this option is present
+ \param argno The index into the argument array where the argument should
+ be placed
+ \sa AST_APP_OPTIONS, ast_app_parse_options
+ */
+#define AST_APP_OPTION_ARG(option, flagno, argno) \
+ [option] = { .flag = flagno, .arg_index = argno + 1 }
+
+/*!
+ \brief Parses a string containing application options and sets flags/arguments.
+ \param options The array of possible options declared with AST_APP_OPTIONS
+ \param flags The flag structure to have option flags set
+ \param args The array of argument pointers to hold arguments found
+ \param optstr The string containing the options to be parsed
+ \return zero for success, non-zero if an error occurs
+ \sa AST_APP_OPTIONS
+ */
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr);
+
+ /*!
+ \brief Parses a string containing application options and sets flags/arguments.
+ \param options The array of possible options declared with AST_APP_OPTIONS
+ \param flags The 64-bit flag structure to have option flags set
+ \param args The array of argument pointers to hold arguments found
+ \param optstr The string containing the options to be parsed
+ \return zero for success, non-zero if an error occurs
+ \sa AST_APP_OPTIONS
+ */
+int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr);
+
+/*! \brief Present a dialtone and collect a certain length extension.
+ \return Returns 1 on valid extension entered, -1 on hangup, or 0 on invalid extension.
+\note Note that if 'collect' holds digits already, new digits will be appended, so be sure it's initialized properly */
+int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout);
+
+/*! \brief Allow to record message and have a review option */
+int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path);
+
+/*! \brief Decode an encoded control or extended ASCII character */
+int ast_get_encoded_char(const char *stream, char *result, size_t *consumed);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_APP_H */
diff --git a/trunk/include/asterisk/ast_expr.h b/trunk/include/asterisk/ast_expr.h
new file mode 100644
index 000000000..a89b7b9a1
--- /dev/null
+++ b/trunk/include/asterisk/ast_expr.h
@@ -0,0 +1,40 @@
+/*
+ * 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
+ *
+ * ???????
+ * \todo Explain this file!
+ */
+
+
+#ifndef _ASTERISK_EXPR_H
+#define _ASTERISK_EXPR_H
+#ifndef STANDALONE
+#endif
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_EXPR_H */
diff --git a/trunk/include/asterisk/astdb.h b/trunk/include/asterisk/astdb.h
new file mode 100644
index 000000000..e19784bad
--- /dev/null
+++ b/trunk/include/asterisk/astdb.h
@@ -0,0 +1,58 @@
+/*
+ * 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 Persistant data storage (akin to *doze registry)
+ */
+
+#ifndef _ASTERISK_ASTDB_H
+#define _ASTERISK_ASTDB_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct ast_db_entry {
+ struct ast_db_entry *next;
+ char *key;
+ char data[0];
+};
+
+/*! \brief Get key value specified by family/key */
+int ast_db_get(const char *family, const char *key, char *out, int outlen);
+
+/*! \brief Store value addressed by family/key*/
+int ast_db_put(const char *family, const char *key, const char *value);
+
+/*! \brief Delete entry in astdb */
+int ast_db_del(const char *family, const char *key);
+
+/*! \brief Delete a whole family (for some reason also called "tree" */
+int ast_db_deltree(const char *family, const char *keytree);
+
+/*! \brief Get a whole family */
+struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree);
+
+/*! \brief Free in-memory data */
+void ast_db_freetree(struct ast_db_entry *entry);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_ASTDB_H */
diff --git a/trunk/include/asterisk/astmm.h b/trunk/include/asterisk/astmm.h
new file mode 100644
index 000000000..4487c17aa
--- /dev/null
+++ b/trunk/include/asterisk/astmm.h
@@ -0,0 +1,82 @@
+/*
+ * 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 Asterisk memory usage debugging
+ */
+
+#ifndef _ASTERISK_ASTMM_H
+#define _ASTERISK_ASTMM_H
+
+#define __AST_DEBUG_MALLOC
+
+#include "asterisk.h"
+
+/* Undefine any macros */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef strdup
+#undef strndup
+#undef asprintf
+#undef vasprintf
+
+void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func);
+void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func);
+void *__ast_malloc(size_t size, const char *file, int lineno, const char *func);
+void __ast_free(void *ptr, const char *file, int lineno, const char *func);
+void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func);
+char *__ast_strdup(const char *s, const char *file, int lineno, const char *func);
+char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func);
+int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *format, ...);
+int __ast_vasprintf(char **strp, const char *format, va_list ap, const char *file, int lineno, const char *func);
+
+void __ast_mm_init(void);
+
+
+/* Provide our own definitions */
+#define calloc(a,b) \
+ __ast_calloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define ast_calloc_cache(a,b) \
+ __ast_calloc_cache(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define malloc(a) \
+ __ast_malloc(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define free(a) \
+ __ast_free(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define realloc(a,b) \
+ __ast_realloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define strdup(a) \
+ __ast_strdup(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define strndup(a,b) \
+ __ast_strndup(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#define asprintf(a, b, c...) \
+ __ast_asprintf(__FILE__, __LINE__, __PRETTY_FUNCTION__, a, b, c)
+
+#define vasprintf(a,b,c) \
+ __ast_vasprintf(a,b,c,__FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#else
+#error "NEVER INCLUDE astmm.h DIRECTLY!!"
+#endif /* _ASTERISK_ASTMM_H */
diff --git a/trunk/include/asterisk/astobj.h b/trunk/include/asterisk/astobj.h
new file mode 100644
index 000000000..cca463f42
--- /dev/null
+++ b/trunk/include/asterisk/astobj.h
@@ -0,0 +1,819 @@
+/*
+ * 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.
+ */
+
+/*
+ * Object Model for Asterisk
+ */
+
+#ifndef _ASTERISK_ASTOBJ_H
+#define _ASTERISK_ASTOBJ_H
+
+#include "asterisk/lock.h"
+
+/*! \file
+ * \brief A set of macros implementing objects and containers.
+ * Macros are used for maximum performance, to support multiple inheritance,
+ * and to be easily integrated into existing structures without additional
+ * malloc calls, etc.
+ *
+ * These macros expect to operate on two different object types, ASTOBJs and
+ * ASTOBJ_CONTAINERs. These are not actual types, as any struct can be
+ * converted into an ASTOBJ compatible object or container using the supplied
+ * macros.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_object {
+ * ASTOBJ_COMPONENTS(struct sample_object);
+ * };
+ *
+ * struct sample_container {
+ * ASTOBJ_CONTAINER_COMPONENTS(struct sample_object);
+ * } super_container;
+ *
+ * void sample_object_destroy(struct sample_object *obj)
+ * {
+ * free(obj);
+ * }
+ *
+ * int init_stuff()
+ * {
+ * struct sample_object *obj1;
+ * struct sample_object *found_obj;
+ *
+ * obj1 = malloc(sizeof(struct sample_object));
+ *
+ * ASTOBJ_CONTAINER_INIT(&super_container);
+ *
+ * ASTOBJ_INIT(obj1);
+ * ASTOBJ_WRLOCK(obj1);
+ * ast_copy_string(obj1->name, "obj1", sizeof(obj1->name));
+ * ASTOBJ_UNLOCK(obj1);
+ *
+ * ASTOBJ_CONTAINER_LINK(&super_container, obj1);
+ *
+ * found_obj = ASTOBJ_CONTAINER_FIND(&super_container, "obj1");
+ *
+ * if(found_obj) {
+ * printf("Found object: %s", found_obj->name);
+ * ASTOBJ_UNREF(found_obj,sample_object_destroy);
+ * }
+ *
+ * ASTOBJ_CONTAINER_DESTROYALL(&super_container,sample_object_destroy);
+ * ASTOBJ_CONTAINER_DESTROY(&super_container);
+ *
+ * return 0;
+ * }
+ * \endcode
+ */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define ASTOBJ_DEFAULT_NAMELEN 80
+#define ASTOBJ_DEFAULT_BUCKETS 256
+#define ASTOBJ_DEFAULT_HASH ast_strhash
+
+#define ASTOBJ_FLAG_MARKED (1 << 0) /* Object has been marked for future operation */
+
+/* C++ is simply a syntactic crutch for those who cannot think for themselves
+ in an object oriented way. */
+
+/*! \brief Lock an ASTOBJ for reading.
+ */
+#define ASTOBJ_RDLOCK(object) ast_mutex_lock(&(object)->_lock)
+
+/*! \brief Lock an ASTOBJ for writing.
+ */
+#define ASTOBJ_WRLOCK(object) ast_mutex_lock(&(object)->_lock)
+
+/*! \brief Unlock a locked object. */
+#define ASTOBJ_UNLOCK(object) ast_mutex_unlock(&(object)->_lock)
+
+#ifdef ASTOBJ_CONTAINER_HASHMODEL
+#define __ASTOBJ_HASH(type,hashes) \
+ type *next[hashes]
+#else
+#define __ASTOBJ_HASH(type,hashes) \
+ type *next[1]
+#endif
+
+/*! \brief Add ASTOBJ components to a struct (without locking support).
+ *
+ * \param type The datatype of the object.
+ * \param namelen The length to make the name char array.
+ * \param hashes The number of containers the object can be present in.
+ *
+ * This macro adds components to a struct to make it an ASTOBJ. This macro
+ * differs from ASTOBJ_COMPONENTS_FULL in that it does not create a mutex for
+ * locking.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct {
+ * ASTOBJ_COMPONENTS_NOLOCK_FULL(struct sample_struct,1,1);
+ * };
+ * \endcode
+ */
+#define ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes) \
+ char name[namelen]; \
+ unsigned int refcount; \
+ unsigned int objflags; \
+ __ASTOBJ_HASH(type,hashes)
+
+/*! \brief Add ASTOBJ components to a struct (without locking support).
+ *
+ * \param type The datatype of the object.
+ *
+ * This macro works like #ASTOBJ_COMPONENTS_NOLOCK_FULL() except it only accepts a
+ * type and uses default values for namelen and hashes.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct_componets {
+ * ASTOBJ_COMPONENTS_NOLOCK(struct sample_struct);
+ * };
+ * \endcode
+ */
+#define ASTOBJ_COMPONENTS_NOLOCK(type) \
+ ASTOBJ_COMPONENTS_NOLOCK_FULL(type,ASTOBJ_DEFAULT_NAMELEN,1)
+
+/*! \brief Add ASTOBJ components to a struct (with locking support).
+ *
+ * \param type The datatype of the object.
+ *
+ * This macro works like #ASTOBJ_COMPONENTS_NOLOCK() except it includes locking
+ * support.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct {
+ * ASTOBJ_COMPONENTS(struct sample_struct);
+ * };
+ * \endcode
+ */
+#define ASTOBJ_COMPONENTS(type) \
+ ASTOBJ_COMPONENTS_NOLOCK(type); \
+ ast_mutex_t _lock;
+
+/*! \brief Add ASTOBJ components to a struct (with locking support).
+ *
+ * \param type The datatype of the object.
+ * \param namelen The length to make the name char array.
+ * \param hashes The number of containers the object can be present in.
+ *
+ * This macro adds components to a struct to make it an ASTOBJ and includes
+ * support for locking.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct {
+ * ASTOBJ_COMPONENTS_FULL(struct sample_struct,1,1);
+ * };
+ * \endcode
+ */
+#define ASTOBJ_COMPONENTS_FULL(type,namelen,hashes) \
+ ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes); \
+ ast_mutex_t _lock;
+
+/*! \brief Increment an object reference count.
+ * \param object A pointer to the object to operate on.
+ * \return The object.
+ */
+#define ASTOBJ_REF(object) \
+ ({ \
+ ASTOBJ_WRLOCK(object); \
+ (object)->refcount++; \
+ ASTOBJ_UNLOCK(object); \
+ (object); \
+ })
+
+/*! \brief Decrement the reference count on an object.
+ *
+ * \param object A pointer the object to operate on.
+ * \param destructor The destructor to call if the object is no longer referenced. It will be passed the pointer as an argument.
+ *
+ * This macro unreferences an object and calls the specfied destructor if the
+ * object is no longer referenced. The destructor should free the object if it
+ * was dynamically allocated.
+ */
+#define ASTOBJ_UNREF(object,destructor) \
+ do { \
+ int newcount = 0; \
+ ASTOBJ_WRLOCK(object); \
+ if (__builtin_expect((object)->refcount > 0, 1)) \
+ newcount = --((object)->refcount); \
+ else \
+ ast_log(LOG_WARNING, "Unreferencing unreferenced (object)!\n"); \
+ ASTOBJ_UNLOCK(object); \
+ if (newcount == 0) { \
+ ast_mutex_destroy(&(object)->_lock); \
+ destructor((object)); \
+ } \
+ (object) = NULL; \
+ } while(0)
+
+/*! \brief Mark an ASTOBJ by adding the #ASTOBJ_FLAG_MARKED flag to its objflags mask.
+ * \param object A pointer to the object to operate on.
+ *
+ * This macro "marks" an object. Marked objects can later be unlinked from a container using
+ * #ASTOBJ_CONTAINER_PRUNE_MARKED().
+ *
+ */
+#define ASTOBJ_MARK(object) \
+ do { \
+ ASTOBJ_WRLOCK(object); \
+ (object)->objflags |= ASTOBJ_FLAG_MARKED; \
+ ASTOBJ_UNLOCK(object); \
+ } while(0)
+
+/*! \brief Unmark an ASTOBJ by subtracting the #ASTOBJ_FLAG_MARKED flag from its objflags mask.
+ * \param object A pointer to the object to operate on.
+ */
+#define ASTOBJ_UNMARK(object) \
+ do { \
+ ASTOBJ_WRLOCK(object); \
+ (object)->objflags &= ~ASTOBJ_FLAG_MARKED; \
+ ASTOBJ_UNLOCK(object); \
+ } while(0)
+
+/*! \brief Initialize an object.
+ * \param object A pointer to the object to operate on.
+ *
+ * \note This should only be used on objects that support locking (objects
+ * created with #ASTOBJ_COMPONENTS() or #ASTOBJ_COMPONENTS_FULL())
+ */
+#define ASTOBJ_INIT(object) \
+ do { \
+ ast_mutex_init(&(object)->_lock); \
+ object->name[0] = '\0'; \
+ object->refcount = 1; \
+ } while(0)
+
+/* Containers for objects -- current implementation is linked lists, but
+ should be able to be converted to hashes relatively easily */
+
+/*! \brief Lock an ASTOBJ_CONTAINER for reading.
+ */
+#define ASTOBJ_CONTAINER_RDLOCK(container) ast_mutex_lock(&(container)->_lock)
+
+/*! \brief Lock an ASTOBJ_CONTAINER for writing.
+ */
+#define ASTOBJ_CONTAINER_WRLOCK(container) ast_mutex_lock(&(container)->_lock)
+
+/*! \brief Unlock an ASTOBJ_CONTAINER. */
+#define ASTOBJ_CONTAINER_UNLOCK(container) ast_mutex_unlock(&(container)->_lock)
+
+#ifdef ASTOBJ_CONTAINER_HASHMODEL
+#error "Hash model for object containers not yet implemented!"
+#else
+/* Linked lists */
+
+/*! \brief Create a container for ASTOBJs (without locking support).
+ *
+ * \param type The type of objects the container will hold.
+ * \param hashes Currently unused.
+ * \param buckets Currently unused.
+ *
+ * This macro is used to create a container for ASTOBJs without locking
+ * support.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct_nolock_container {
+ * ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(struct sample_struct,1,1);
+ * };
+ * \endcode
+ */
+#define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,hashes,buckets) \
+ type *head
+
+/*! \brief Initialize a container.
+ *
+ * \param container A pointer to the container to initialize.
+ * \param hashes Currently unused.
+ * \param buckets Currently unused.
+ *
+ * This macro initializes a container. It should only be used on containers
+ * that support locking.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct_container {
+ * ASTOBJ_CONTAINER_COMPONENTS_FULL(struct sample_struct,1,1);
+ * } container;
+ *
+ * int func()
+ * {
+ * ASTOBJ_CONTAINER_INIT_FULL(&container,1,1);
+ * }
+ * \endcode
+ */
+#define ASTOBJ_CONTAINER_INIT_FULL(container,hashes,buckets) \
+ do { \
+ ast_mutex_init(&(container)->_lock); \
+ } while(0)
+
+/*! \brief Destroy a container.
+ *
+ * \param container A pointer to the container to destroy.
+ * \param hashes Currently unused.
+ * \param buckets Currently unused.
+ *
+ * This macro frees up resources used by a container. It does not operate on
+ * the objects in the container. To unlink the objects from the container use
+ * #ASTOBJ_CONTAINER_DESTROYALL().
+ *
+ * \note This macro should only be used on containers with locking support.
+ */
+#define ASTOBJ_CONTAINER_DESTROY_FULL(container,hashes,buckets) \
+ do { \
+ ast_mutex_destroy(&(container)->_lock); \
+ } while(0)
+
+/*! \brief Iterate through the objects in a container.
+ *
+ * \param container A pointer to the container to traverse.
+ * \param continue A condition to allow the traversal to continue.
+ * \param eval A statement to evaluate in the iteration loop.
+ *
+ * This is macro is a little complicated, but it may help to think of it as a
+ * loop. Basically it iterates through the specfied containter as long as the
+ * condition is met. Two variables, iterator and next, are provided for use in
+ * your \p eval statement. See the sample code for an example.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * ASTOBJ_CONTAINER_TRAVERSE(&sample_container,1, {
+ * ASTOBJ_RDLOCK(iterator);
+ * printf("Currently iterating over '%s'\n", iterator->name);
+ * ASTOBJ_UNLOCK(iterator);
+ * } );
+ * \endcode
+ *
+ * \code
+ * ASTOBJ_CONTAINER_TRAVERSE(&sample_container,1, sample_func(iterator));
+ * \endcode
+ */
+#define ASTOBJ_CONTAINER_TRAVERSE(container,continue,eval) \
+ do { \
+ typeof((container)->head) iterator; \
+ typeof((container)->head) next; \
+ ASTOBJ_CONTAINER_RDLOCK(container); \
+ next = (container)->head; \
+ while((continue) && (iterator = next)) { \
+ next = iterator->next[0]; \
+ eval; \
+ } \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ } while(0)
+
+/*! \brief Find an object in a container.
+ *
+ * \param container A pointer to the container to search.
+ * \param namestr The name to search for.
+ *
+ * Use this function to find an object with the specfied name in a container.
+ *
+ * \note When the returned object is no longer in use, #ASTOBJ_UNREF() should
+ * be used to free the additional reference created by this macro.
+ *
+ * \return A new reference to the object located or NULL if nothing is found.
+ */
+#define ASTOBJ_CONTAINER_FIND(container,namestr) \
+ ({ \
+ typeof((container)->head) found = NULL; \
+ ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
+ if (!(strcasecmp(iterator->name, (namestr)))) \
+ found = ASTOBJ_REF(iterator); \
+ } while (0)); \
+ found; \
+ })
+
+/*! \brief Find an object in a container.
+ *
+ * \param container A pointer to the container to search.
+ * \param data The data to search for.
+ * \param field The field/member of the container's objects to search.
+ * \param hashfunc The hash function to use, currently not implemented.
+ * \param hashoffset The hash offset to use, currently not implemented.
+ * \param comparefunc The function used to compare the field and data values.
+ *
+ * This macro iterates through a container passing the specified field and data
+ * elements to the specified comparefunc. The function should return 0 when a match is found.
+ *
+ * \note When the returned object is no longer in use, #ASTOBJ_UNREF() should
+ * be used to free the additional reference created by this macro.
+ *
+ * \return A pointer to the object located or NULL if nothing is found.
+ */
+#define ASTOBJ_CONTAINER_FIND_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
+ ({ \
+ typeof((container)->head) found = NULL; \
+ ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
+ ASTOBJ_RDLOCK(iterator); \
+ if (!(comparefunc(iterator->field, (data)))) { \
+ found = ASTOBJ_REF(iterator); \
+ } \
+ ASTOBJ_UNLOCK(iterator); \
+ } while (0)); \
+ found; \
+ })
+
+/*! \brief Empty a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param destructor A destructor function to call on each object.
+ *
+ * This macro loops through a container removing all the items from it using
+ * #ASTOBJ_UNREF(). This does not destroy the container itself, use
+ * #ASTOBJ_CONTAINER_DESTROY() for that.
+ *
+ * \note If any object in the container is only referenced by the container,
+ * the destructor will be called for that object once it has been removed.
+ */
+#define ASTOBJ_CONTAINER_DESTROYALL(container,destructor) \
+ do { \
+ typeof((container)->head) iterator; \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ while((iterator = (container)->head)) { \
+ (container)->head = (iterator)->next[0]; \
+ ASTOBJ_UNREF(iterator,destructor); \
+ } \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ } while(0)
+
+/*! \brief Remove an object from a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param obj A pointer to the object to remove.
+ *
+ * This macro iterates through a container and removes the specfied object if
+ * it exists in the container.
+ *
+ * \note This macro does not destroy any objects, it simply unlinks
+ * them from the list. No destructors are called.
+ *
+ * \return The container's reference to the removed object or NULL if no
+ * matching object was found.
+ */
+#define ASTOBJ_CONTAINER_UNLINK(container,obj) \
+ ({ \
+ typeof((container)->head) found = NULL; \
+ typeof((container)->head) prev = NULL; \
+ ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
+ if (iterator == obj) { \
+ found = iterator; \
+ found->next[0] = NULL; \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ if (prev) \
+ prev->next[0] = next; \
+ else \
+ (container)->head = next; \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ } \
+ prev = iterator; \
+ } while (0)); \
+ found; \
+ })
+
+/*! \brief Find and remove an object from a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param namestr The name of the object to remove.
+ *
+ * This macro iterates through a container and removes the first object with
+ * the specfied name from the container.
+ *
+ * \note This macro does not destroy any objects, it simply unlinks
+ * them. No destructors are called.
+ *
+ * \return The container's reference to the removed object or NULL if no
+ * matching object was found.
+ */
+#define ASTOBJ_CONTAINER_FIND_UNLINK(container,namestr) \
+ ({ \
+ typeof((container)->head) found = NULL; \
+ typeof((container)->head) prev = NULL; \
+ ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
+ if (!(strcasecmp(iterator->name, (namestr)))) { \
+ found = iterator; \
+ found->next[0] = NULL; \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ if (prev) \
+ prev->next[0] = next; \
+ else \
+ (container)->head = next; \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ } \
+ prev = iterator; \
+ } while (0)); \
+ found; \
+ })
+
+/*! \brief Find and remove an object in a container.
+ *
+ * \param container A pointer to the container to search.
+ * \param data The data to search for.
+ * \param field The field/member of the container's objects to search.
+ * \param hashfunc The hash function to use, currently not implemented.
+ * \param hashoffset The hash offset to use, currently not implemented.
+ * \param comparefunc The function used to compare the field and data values.
+ *
+ * This macro iterates through a container passing the specified field and data
+ * elements to the specified comparefunc. The function should return 0 when a match is found.
+ * If a match is found it is removed from the list.
+ *
+ * \note This macro does not destroy any objects, it simply unlinks
+ * them. No destructors are called.
+ *
+ * \return The container's reference to the removed object or NULL if no match
+ * was found.
+ */
+#define ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
+ ({ \
+ typeof((container)->head) found = NULL; \
+ typeof((container)->head) prev = NULL; \
+ ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
+ ASTOBJ_RDLOCK(iterator); \
+ if (!(comparefunc(iterator->field, (data)))) { \
+ found = iterator; \
+ found->next[0] = NULL; \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ if (prev) \
+ prev->next[0] = next; \
+ else \
+ (container)->head = next; \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ } \
+ ASTOBJ_UNLOCK(iterator); \
+ prev = iterator; \
+ } while (0)); \
+ found; \
+ })
+
+/*! \brief Add an object to the end of a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param newobj A pointer to the object to be added.
+ *
+ * This macro adds an object to the end of a container.
+ */
+#define ASTOBJ_CONTAINER_LINK_END(container,newobj) \
+ do { \
+ typeof((container)->head) iterator; \
+ typeof((container)->head) next; \
+ typeof((container)->head) prev; \
+ ASTOBJ_CONTAINER_RDLOCK(container); \
+ prev = NULL; \
+ next = (container)->head; \
+ while((iterator = next)) { \
+ next = iterator->next[0]; \
+ prev = iterator; \
+ } \
+ if(prev) { \
+ ASTOBJ_CONTAINER_WRLOCK((container)); \
+ prev->next[0] = ASTOBJ_REF(newobj); \
+ (newobj)->next[0] = NULL; \
+ ASTOBJ_CONTAINER_UNLOCK((container)); \
+ } else { \
+ ASTOBJ_CONTAINER_LINK_START((container),(newobj)); \
+ } \
+ ASTOBJ_CONTAINER_UNLOCK((container)); \
+ } while(0)
+
+/*! \brief Add an object to the front of a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param newobj A pointer to the object to be added.
+ *
+ * This macro adds an object to the start of a container.
+ */
+#define ASTOBJ_CONTAINER_LINK_START(container,newobj) \
+ do { \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ (newobj)->next[0] = (container)->head; \
+ (container)->head = ASTOBJ_REF(newobj); \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ } while(0)
+
+/*! \brief Remove an object from the front of a container.
+ *
+ * \param container A pointer to the container to operate on.
+ *
+ * This macro removes the first object in a container.
+ *
+ * \note This macro does not destroy any objects, it simply unlinks
+ * them from the list. No destructors are called.
+ *
+ * \return The container's reference to the removed object or NULL if no
+ * matching object was found.
+ */
+#define ASTOBJ_CONTAINER_UNLINK_START(container) \
+ ({ \
+ typeof((container)->head) found = NULL; \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ if((container)->head) { \
+ found = (container)->head; \
+ (container)->head = (container)->head->next[0]; \
+ found->next[0] = NULL; \
+ } \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ found; \
+ })
+
+/*! \brief Prune marked objects from a container.
+ *
+ * \param container A pointer to the container to prune.
+ * \param destructor A destructor function to call on each marked object.
+ *
+ * This macro iterates through the specfied container and prunes any marked
+ * objects executing the specfied destructor if necessary.
+ */
+#define ASTOBJ_CONTAINER_PRUNE_MARKED(container,destructor) \
+ do { \
+ typeof((container)->head) prev = NULL; \
+ ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { \
+ ASTOBJ_RDLOCK(iterator); \
+ if (iterator->objflags & ASTOBJ_FLAG_MARKED) { \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ if (prev) \
+ prev->next[0] = next; \
+ else \
+ (container)->head = next; \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ ASTOBJ_UNLOCK(iterator); \
+ ASTOBJ_UNREF(iterator,destructor); \
+ continue; \
+ } \
+ ASTOBJ_UNLOCK(iterator); \
+ prev = iterator; \
+ } while (0)); \
+ } while(0)
+
+/*! \brief Add an object to a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param newobj A pointer to the object to be added.
+ * \param data Currently unused.
+ * \param field Currently unused.
+ * \param hashfunc Currently unused.
+ * \param hashoffset Currently unused.
+ * \param comparefunc Currently unused.
+ *
+ * Currently this function adds an object to the head of the list. One day it
+ * will support adding objects atthe position specified using the various
+ * options this macro offers.
+ */
+#define ASTOBJ_CONTAINER_LINK_FULL(container,newobj,data,field,hashfunc,hashoffset,comparefunc) \
+ do { \
+ ASTOBJ_CONTAINER_WRLOCK(container); \
+ (newobj)->next[0] = (container)->head; \
+ (container)->head = ASTOBJ_REF(newobj); \
+ ASTOBJ_CONTAINER_UNLOCK(container); \
+ } while(0)
+
+#endif /* List model */
+
+/* Common to hash and linked list models */
+
+/*! \brief Create a container for ASTOBJs (without locking support).
+ *
+ * \param type The type of objects the container will hold.
+ *
+ * This macro is used to create a container for ASTOBJs without locking
+ * support.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct_nolock_container {
+ * ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(struct sample_struct);
+ * };
+ * \endcode
+ */
+#define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type) \
+ ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,1,ASTOBJ_DEFAULT_BUCKETS)
+
+
+/*! \brief Create a container for ASTOBJs (with locking support).
+ *
+ * \param type The type of objects the container will hold.
+ *
+ * This macro is used to create a container for ASTOBJs with locking support.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct_container {
+ * ASTOBJ_CONTAINER_COMPONENTS(struct sample_struct);
+ * };
+ * \endcode
+ */
+#define ASTOBJ_CONTAINER_COMPONENTS(type) \
+ ast_mutex_t _lock; \
+ ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type)
+
+/*! \brief Initialize a container.
+ *
+ * \param container A pointer to the container to initialize.
+ *
+ * This macro initializes a container. It should only be used on containers
+ * that support locking.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ * struct sample_struct_container {
+ * ASTOBJ_CONTAINER_COMPONENTS(struct sample_struct);
+ * } container;
+ *
+ * int func()
+ * {
+ * ASTOBJ_CONTAINER_INIT(&container);
+ * }
+ * \endcode
+ */
+#define ASTOBJ_CONTAINER_INIT(container) \
+ ASTOBJ_CONTAINER_INIT_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
+
+/*! \brief Destroy a container.
+ *
+ * \param container A pointer to the container to destory.
+ *
+ * This macro frees up resources used by a container. It does not operate on
+ * the objects in the container. To unlink the objects from the container use
+ * #ASTOBJ_CONTAINER_DESTROYALL().
+ *
+ * \note This macro should only be used on containers with locking support.
+ */
+#define ASTOBJ_CONTAINER_DESTROY(container) \
+ ASTOBJ_CONTAINER_DESTROY_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
+
+/*! \brief Add an object to a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param newobj A pointer to the object to be added.
+ *
+ * Currently this macro adds an object to the head of a container. One day it
+ * should add an object in alphabetical order.
+ */
+#define ASTOBJ_CONTAINER_LINK(container,newobj) \
+ ASTOBJ_CONTAINER_LINK_FULL(container,newobj,(newobj)->name,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp)
+
+/*! \brief Mark all the objects in a container.
+ * \param container A pointer to the container to operate on.
+ */
+#define ASTOBJ_CONTAINER_MARKALL(container) \
+ ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_MARK(iterator))
+
+/*! \brief Unmark all the objects in a container.
+ * \param container A pointer to the container to operate on.
+ */
+#define ASTOBJ_CONTAINER_UNMARKALL(container) \
+ ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_UNMARK(iterator))
+
+/*! \brief Dump information about an object into a string.
+ *
+ * \param s A pointer to the string buffer to use.
+ * \param slen The length of s.
+ * \param obj A pointer to the object to dump.
+ *
+ * This macro dumps a text representation of the name, objectflags, and
+ * refcount fields of an object to the specfied string buffer.
+ */
+#define ASTOBJ_DUMP(s,slen,obj) \
+ snprintf((s),(slen),"name: %s\nobjflags: %d\nrefcount: %d\n\n", (obj)->name, (obj)->objflags, (obj)->refcount);
+
+/*! \brief Dump information about all the objects in a container to a file descriptor.
+ *
+ * \param fd The file descriptor to write to.
+ * \param s A string buffer, same as #ASTOBJ_DUMP().
+ * \param slen The length of s, same as #ASTOBJ_DUMP().
+ * \param container A pointer to the container to dump.
+ *
+ * This macro dumps a text representation of the name, objectflags, and
+ * refcount fields of all the objects in a container to the specified file
+ * descriptor.
+ */
+#define ASTOBJ_CONTAINER_DUMP(fd,s,slen,container) \
+ ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { ASTOBJ_DUMP(s,slen,iterator); ast_cli(fd, s); } while(0))
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_ASTOBJ_H */
diff --git a/trunk/include/asterisk/astobj2.h b/trunk/include/asterisk/astobj2.h
new file mode 100644
index 000000000..b02b6cba8
--- /dev/null
+++ b/trunk/include/asterisk/astobj2.h
@@ -0,0 +1,568 @@
+/*
+ * astobj2 - replacement containers for asterisk data structures.
+ *
+ * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
+ *
+ * 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.
+ */
+
+#ifndef _ASTERISK_ASTOBJ2_H
+#define _ASTERISK_ASTOBJ2_H
+
+/*! \file
+ * \ref AstObj2
+ *
+ * \page AstObj2 Object Model implementing objects and containers.
+
+This module implements an abstraction for objects (with locks and
+reference counts), and containers for these user-defined objects,
+also supporting locking, reference counting and callbacks.
+
+The internal implementation of objects and containers is opaque to the user,
+so we can use different data structures as needs arise.
+
+\section AstObj2_UsageObjects USAGE - OBJECTS
+
+An ao2 object is a block of memory that the user code can access,
+and for which the system keeps track (with a bit of help from the
+programmer) of the number of references around. When an object has
+no more references (refcount == 0), it is destroyed, by first
+invoking whatever 'destructor' function the programmer specifies
+(it can be NULL if none is necessary), and then freeing the memory.
+This way objects can be shared without worrying who is in charge
+of freeing them.
+As an additional feature, ao2 objects are associated to individual
+locks.
+
+Creating an object requires the size of the object and
+and a pointer to the destructor function:
+
+ struct foo *o;
+
+ o = ao2_alloc(sizeof(struct foo), my_destructor_fn);
+
+The value returned points to the user-visible portion of the objects
+(user-data), but is also used as an identifier for all object-related
+operations such as refcount and lock manipulations.
+
+On return from ao2_alloc():
+
+ - the object has a refcount = 1;
+ - the memory for the object is allocated dynamically and zeroed;
+ - we cannot realloc() the object itself;
+ - we cannot call free(o) to dispose of the object. Rather, we
+ tell the system that we do not need the reference anymore:
+
+ ao2_ref(o, -1)
+
+ causing the destructor to be called (and then memory freed) when
+ the refcount goes to 0. This is also available as ao2_unref(o),
+ and returns NULL as a convenience, so you can do things like
+
+ o = ao2_unref(o);
+
+ and clean the original pointer to prevent errors.
+
+- ao2_ref(o, +1) can be used to modify the refcount on the
+ object in case we want to pass it around.
+
+- ao2_lock(obj), ao2_unlock(obj), ao2_trylock(obj) can be used
+ to manipulate the lock associated with the object.
+
+
+\section AstObj2_UsageContainers USAGE - CONTAINERS
+
+An ao2 container is an abstract data structure where we can store
+ao2 objects, search them (hopefully in an efficient way), and iterate
+or apply a callback function to them. A container is just an ao2 object
+itself.
+
+A container must first be allocated, specifying the initial
+parameters. At the moment, this is done as follows:
+
+ <b>Sample Usage:</b>
+ \code
+
+ struct ao2_container *c;
+
+ c = ao2_container_alloc(MAX_BUCKETS, my_hash_fn, my_cmp_fn);
+ \endcode
+
+where
+
+- MAX_BUCKETS is the number of buckets in the hash table,
+- my_hash_fn() is the (user-supplied) function that returns a
+ hash key for the object (further reduced modulo MAX_BUCKETS
+ by the container's code);
+- my_cmp_fn() is the default comparison function used when doing
+ searches on the container,
+
+A container knows little or nothing about the objects it stores,
+other than the fact that they have been created by ao2_alloc().
+All knowledge of the (user-defined) internals of the objects
+is left to the (user-supplied) functions passed as arguments
+to ao2_container_alloc().
+
+If we want to insert an object in a container, we should
+initialize its fields -- especially, those used by my_hash_fn() --
+to compute the bucket to use.
+Once done, we can link an object to a container with
+
+ ao2_link(c, o);
+
+The function returns NULL in case of errors (and the object
+is not inserted in the container). Other values mean success
+(we are not supposed to use the value as a pointer to anything).
+
+\note While an object o is in a container, we expect that
+my_hash_fn(o) will always return the same value. The function
+does not lock the object to be computed, so modifications of
+those fields that affect the computation of the hash should
+be done by extracting the object from the container, and
+reinserting it after the change (this is not terribly expensive).
+
+\note A container with a single buckets is effectively a linked
+list. However there is no ordering among elements.
+
+- \ref AstObj2_Containers
+- \ref astobj2.h All documentation for functions and data structures
+
+ */
+
+/*! \brief
+ * Typedef for an object destructor. This is called just before freeing
+ * the memory for the object. It is passed a pointer to the user-defined
+ * data of the object.
+ */
+typedef void (*ao2_destructor_fn)(void *);
+
+
+/*! \brief
+ * Allocate and initialize an object.
+ *
+ * \param data_size The sizeof() of the user-defined structure.
+ * \param destructor_fn The destructor function (can be NULL)
+ * \return A pointer to user-data.
+ *
+ * Allocates a struct astobj2 with sufficient space for the
+ * user-defined structure.
+ * \note
+ * - storage is zeroed; XXX maybe we want a flag to enable/disable this.
+ * - the refcount of the object just created is 1
+ * - the returned pointer cannot be free()'d or realloc()'ed;
+ * rather, we just call ao2_ref(o, -1);
+ */
+void *ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn);
+
+/*! \brief
+ * Reference/unreference an object and return the old refcount.
+ *
+ * \param o A pointer to the object
+ * \param delta Value to add to the reference counter.
+ * \return The value of the reference counter before the operation.
+ *
+ * Increase/decrease the reference counter according
+ * the value of delta.
+ *
+ * If the refcount goes to zero, the object is destroyed.
+ *
+ * \note The object must not be locked by the caller of this function, as
+ * it is invalid to try to unlock it after releasing the reference.
+ *
+ * \note if we know the pointer to an object, it is because we
+ * have a reference count to it, so the only case when the object
+ * can go away is when we release our reference, and it is
+ * the last one in existence.
+ */
+int ao2_ref(void *o, int delta);
+
+/*! \brief
+ * Lock an object.
+ *
+ * \param a A pointer to the object we want lock.
+ * \return 0 on success, other values on error.
+ */
+int ao2_lock(void *a);
+
+/*! \brief
+ * Unlock an object.
+ *
+ * \param a A pointer to the object we want unlock.
+ * \return 0 on success, other values on error.
+ */
+int ao2_unlock(void *a);
+
+/*!
+ *
+ \page AstObj2_Containers AstObj2 Containers
+
+Containers are data structures meant to store several objects,
+and perform various operations on them.
+Internally, objects are stored in lists, hash tables or other
+data structures depending on the needs.
+
+\note NOTA BENE: at the moment the only container we support is the
+ hash table and its degenerate form, the list.
+
+Operations on container include:
+
+ - c = \b ao2_container_alloc(size, cmp_fn, hash_fn)
+ allocate a container with desired size and default compare
+ and hash function
+
+ - \b ao2_find(c, arg, flags)
+ returns zero or more element matching a given criteria
+ (specified as arg). Flags indicate how many results we
+ want (only one or all matching entries), and whether we
+ should unlink the object from the container.
+
+ - \b ao2_callback(c, flags, fn, arg)
+ apply fn(obj, arg) to all objects in the container.
+ Similar to find. fn() can tell when to stop, and
+ do anything with the object including unlinking it.
+ Note that the entire operation is run with the container
+ locked, so noone else can change its content while we work on it.
+ However, we pay this with the fact that doing
+ anything blocking in the callback keeps the container
+ blocked.
+ The mechanism is very flexible because the callback function fn()
+ can do basically anything e.g. counting, deleting records, etc.
+ possibly using arg to store the results.
+
+ - \b iterate on a container
+ this is done with the following sequence
+
+\code
+
+ struct ao2_container *c = ... // our container
+ struct ao2_iterator i;
+ void *o;
+
+ i = ao2_iterator_init(c, flags);
+
+ while ( (o = ao2_iterator_next(&i)) ) {
+ ... do something on o ...
+ ao2_ref(o, -1);
+ }
+\endcode
+
+ The difference with the callback is that the control
+ on how to iterate is left to us.
+
+ - \b ao2_ref(c, -1)
+ dropping a reference to a container destroys it, very simple!
+
+Containers are ao2 objects themselves, and this is why their
+implementation is simple too.
+
+Before declaring containers, we need to declare the types of the
+arguments passed to the constructor - in turn, this requires
+to define callback and hash functions and their arguments.
+
+- \ref AstObj2
+- \ref astobj2.h
+ */
+
+/*! \brief
+ * Type of a generic callback function
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * The return values are a combination of enum _cb_results.
+ * Callback functions are used to search or manipulate objects in a container,
+ */
+typedef int (ao2_callback_fn)(void *obj, void *arg, int flags);
+
+/*! \brief a very common callback is one that matches by address. */
+ao2_callback_fn ao2_match_by_addr;
+
+/*! \brief
+ * A callback function will return a combination of CMP_MATCH and CMP_STOP.
+ * The latter will terminate the search in a container.
+ */
+enum _cb_results {
+ CMP_MATCH = 0x1, /*!< the object matches the request */
+ CMP_STOP = 0x2, /*!< stop the search now */
+};
+
+/*! \brief
+ * Flags passed to ao2_callback() and ao2_hash_fn() to modify its behaviour.
+ */
+enum search_flags {
+ /*! Unlink the object for which the callback function
+ * returned CMP_MATCH . This is the only way to extract
+ * objects from a container. */
+ OBJ_UNLINK = (1 << 0),
+ /*! On match, don't return the object hence do not increase
+ * its refcount. */
+ OBJ_NODATA = (1 << 1),
+ /*! Don't stop at the first match in ao2_callback()
+ * \note This is not fully implemented. */
+ OBJ_MULTIPLE = (1 << 2),
+ /*! obj is an object of the same type as the one being searched for,
+ * so use the object's hash function for optimized searching.
+ * The search function is unaffected (i.e. use the one passed as
+ * argument, or match_by_addr if none specified). */
+ OBJ_POINTER = (1 << 3),
+};
+
+/*!
+ * Type of a generic function to generate a hash value from an object.
+ * flags is ignored at the moment. Eventually, it will include the
+ * value of OBJ_POINTER passed to ao2_callback().
+ */
+typedef int (ao2_hash_fn)(const void *obj, const int flags);
+
+/*! \name Object Containers
+ * Here start declarations of containers.
+ */
+/*@{ */
+struct ao2_container;
+
+/*! \brief
+ * Allocate and initialize a container
+ * with the desired number of buckets.
+ *
+ * We allocate space for a struct astobj_container, struct container
+ * and the buckets[] array.
+ *
+ * \param n_buckets Number of buckets for hash
+ * \param hash_fn Pointer to a function computing a hash value.
+ * \param cmp_fn Pointer to a function comparating key-value
+ * with a string. (can be NULL)
+ * \return A pointer to a struct container.
+ *
+ * destructor is set implicitly.
+ */
+struct ao2_container *ao2_container_alloc(const uint n_buckets,
+ ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn);
+
+/*! \brief
+ * Returns the number of elements in a container.
+ */
+int ao2_container_count(struct ao2_container *c);
+/*@} */
+
+/*! \name Object Management
+ * Here we have functions to manage objects.
+ *
+ * We can use the functions below on any kind of
+ * object defined by the user.
+ */
+/*@{ */
+
+/*!
+ * \brief Add an object to a container.
+ *
+ * \param c the container to operate on.
+ * \param newobj the object to be added.
+ *
+ * \retval NULL on errors
+ * \retval newobj on success.
+ *
+ * This function inserts an object in a container according its key.
+ *
+ * \note Remember to set the key before calling this function.
+ *
+ * \note This function automatically increases the reference count to account
+ * for the reference that the container now holds to the object.
+ */
+void *ao2_link(struct ao2_container *c, void *newobj);
+
+/*!
+ * \brief Remove an object from the container
+ *
+ * \arg c the container
+ * \arg obj the object to unlink
+ *
+ * \retval NULL, always
+ *
+ * \note The object requested to be unlinked must be valid. However, if it turns
+ * out that it is not in the container, this function is still safe to
+ * be called.
+ *
+ * \note If the object gets unlinked from the container, the container's
+ * reference to the object will be automatically released.
+ */
+void *ao2_unlink(struct ao2_container *c, void *obj);
+
+/*! \brief Used as return value if the flag OBJ_MULTIPLE is set */
+struct ao2_list {
+ struct ao2_list *next;
+ void *obj; /* pointer to the user portion of the object */
+};
+/*@} */
+
+/*! \brief
+ * ao2_callback() is a generic function that applies cb_fn() to all objects
+ * in a container, as described below.
+ *
+ * \param c A pointer to the container to operate on.
+ * \param arg passed to the callback.
+ * \param flags A set of flags specifying the operation to perform,
+ partially used by the container code, but also passed to
+ the callback.
+ * \return A pointer to the object found/marked,
+ * a pointer to a list of objects matching comparison function,
+ * NULL if not found.
+ *
+ * If the function returns any objects, their refcount is incremented,
+ * and the caller is in charge of decrementing them once done.
+ * Also, in case of multiple values returned, the list used
+ * to store the objects must be freed by the caller.
+ *
+ * Typically, ao2_callback() is used for two purposes:
+ * - to perform some action (including removal from the container) on one
+ * or more objects; in this case, cb_fn() can modify the object itself,
+ * and to perform deletion should set CMP_MATCH on the matching objects,
+ * and have OBJ_UNLINK set in flags.
+ * - to look for a specific object in a container; in this case, cb_fn()
+ * should not modify the object, but just return a combination of
+ * CMP_MATCH and CMP_STOP on the desired object.
+ * Other usages are also possible, of course.
+
+ * This function searches through a container and performs operations
+ * on objects according on flags passed.
+ * XXX describe better
+ * The comparison is done calling the compare function set implicitly.
+ * The p pointer can be a pointer to an object or to a key,
+ * we can say this looking at flags value.
+ * If p points to an object we will search for the object pointed
+ * by this value, otherwise we serch for a key value.
+ * If the key is not uniq we only find the first matching valued.
+ * If we use the OBJ_MARK flags, we mark all the objects matching
+ * the condition.
+ *
+ * The use of flags argument is the follow:
+ *
+ * OBJ_UNLINK unlinks the object found
+ * OBJ_NODATA on match, do return an object
+ * Callbacks use OBJ_NODATA as a default
+ * functions such as find() do
+ * OBJ_MULTIPLE return multiple matches
+ * Default for _find() is no.
+ * to a key (not yet supported)
+ * OBJ_POINTER the pointer is an object pointer
+ *
+ * In case we return a list, the callee must take care to destroy
+ * that list when no longer used.
+ *
+ * \note When the returned object is no longer in use, ao2_ref() should
+ * be used to free the additional reference possibly created by this function.
+ */
+void *ao2_callback(struct ao2_container *c,
+ enum search_flags flags,
+ ao2_callback_fn *cb_fn, void *arg);
+
+/*! ao2_find() is a short hand for ao2_callback(c, flags, c->cmp_fn, arg)
+ * XXX possibly change order of arguments ?
+ */
+void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags);
+
+/*! \brief
+ *
+ *
+ * When we need to walk through a container, we use
+ * ao2_iterator to keep track of the current position.
+ *
+ * Because the navigation is typically done without holding the
+ * lock on the container across the loop,
+ * objects can be inserted or deleted or moved
+ * while we work. As a consequence, there is no guarantee that
+ * the we manage to touch all the elements on the list, or it
+ * is possible that we touch the same object multiple times.
+ * However, within the current hash table container, the following is true:
+ * - It is not possible to miss an object in the container while iterating
+ * unless it gets added after the iteration begins and is added to a bucket
+ * that is before the one the current object is in. In this case, even if
+ * you locked the container around the entire iteration loop, you still would
+ * not see this object, because it would still be waiting on the container
+ * lock so that it can be added.
+ * - It would be extremely rare to see an object twice. The only way this can
+ * happen is if an object got unlinked from the container and added again
+ * during the same iteration. Furthermore, when the object gets added back,
+ * it has to be in the current or later bucket for it to be seen again.
+ *
+ * An iterator must be first initialized with ao2_iterator_init(),
+ * then we can use o = ao2_iterator_next() to move from one
+ * element to the next. Remember that the object returned by
+ * ao2_iterator_next() has its refcount incremented,
+ * and the reference must be explicitly released when done with it.
+ *
+ * Example:
+ *
+ * \code
+ *
+ * struct ao2_container *c = ... // the container we want to iterate on
+ * struct ao2_iterator i;
+ * struct my_obj *o;
+ *
+ * i = ao2_iterator_init(c, flags);
+ *
+ * while ( (o = ao2_iterator_next(&i)) ) {
+ * ... do something on o ...
+ * ao2_ref(o, -1);
+ * }
+ *
+ * \endcode
+ *
+ */
+
+/*! \brief
+ * The Astobj2 iterator
+ *
+ * \note You are not supposed to know the internals of an iterator!
+ * We would like the iterator to be opaque, unfortunately
+ * its size needs to be known if we want to store it around
+ * without too much trouble.
+ * Anyways...
+ * The iterator has a pointer to the container, and a flags
+ * field specifying various things e.g. whether the container
+ * should be locked or not while navigating on it.
+ * The iterator "points" to the current object, which is identified
+ * by three values:
+ *
+ * - a bucket number;
+ * - the object_id, which is also the container version number
+ * when the object was inserted. This identifies the object
+ * univoquely, however reaching the desired object requires
+ * scanning a list.
+ * - a pointer, and a container version when we saved the pointer.
+ * If the container has not changed its version number, then we
+ * can safely follow the pointer to reach the object in constant time.
+ *
+ * Details are in the implementation of ao2_iterator_next()
+ * A freshly-initialized iterator has bucket=0, version = 0.
+ */
+struct ao2_iterator {
+ /*! the container */
+ struct ao2_container *c;
+ /*! operation flags */
+ int flags;
+#define F_AO2I_DONTLOCK 1 /*!< don't lock when iterating */
+ /*! current bucket */
+ int bucket;
+ /*! container version */
+ uint c_version;
+ /*! pointer to the current object */
+ void *obj;
+ /*! container version when the object was created */
+ uint version;
+};
+
+struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags);
+
+void *ao2_iterator_next(struct ao2_iterator *a);
+
+/* extra functions */
+void ao2_bt(void); /* backtrace */
+#endif /* _ASTERISK_ASTOBJ2_H */
diff --git a/trunk/include/asterisk/astosp.h b/trunk/include/asterisk/astosp.h
new file mode 100644
index 000000000..d5c7da1a7
--- /dev/null
+++ b/trunk/include/asterisk/astosp.h
@@ -0,0 +1,31 @@
+/*
+ * 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 Open Settlement Protocol (OSP)
+ */
+
+#ifndef _ASTERISK_OSP_H
+#define _ASTERISK_OSP_H
+
+#define AST_OSP_SUCCESS ((char*)"SUCCESS") /* Return status, success */
+#define AST_OSP_FAILED ((char*)"FAILED") /* Return status, failed */
+#define AST_OSP_ERROR ((char*)"ERROR") /* Return status, error */
+
+#endif /* _ASTERISK_OSP_H */
diff --git a/trunk/include/asterisk/audiohook.h b/trunk/include/asterisk/audiohook.h
new file mode 100644
index 000000000..5ef0ac294
--- /dev/null
+++ b/trunk/include/asterisk/audiohook.h
@@ -0,0 +1,210 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Audiohooks Architecture
+ */
+
+#ifndef _ASTERISK_AUDIOHOOK_H
+#define _ASTERISK_AUDIOHOOK_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/* these two are used in struct ast_audiohook */
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+
+#include "asterisk/slinfactory.h"
+
+enum ast_audiohook_type {
+ AST_AUDIOHOOK_TYPE_SPY = 0, /*!< Audiohook wants to receive audio */
+ AST_AUDIOHOOK_TYPE_WHISPER, /*!< Audiohook wants to provide audio to be mixed with existing audio */
+ AST_AUDIOHOOK_TYPE_MANIPULATE, /*!< Audiohook wants to manipulate the audio */
+};
+
+enum ast_audiohook_status {
+ AST_AUDIOHOOK_STATUS_NEW = 0, /*!< Audiohook was just created, not in use yet */
+ AST_AUDIOHOOK_STATUS_RUNNING, /*!< Audiohook is running on a channel */
+ AST_AUDIOHOOK_STATUS_SHUTDOWN, /*!< Audiohook is being shutdown */
+ AST_AUDIOHOOK_STATUS_DONE, /*!< Audiohook has shutdown and is not running on a channel any longer */
+};
+
+enum ast_audiohook_direction {
+ AST_AUDIOHOOK_DIRECTION_READ = 0, /*!< Reading audio in */
+ AST_AUDIOHOOK_DIRECTION_WRITE, /*!< Writing audio out */
+ AST_AUDIOHOOK_DIRECTION_BOTH, /*!< Both reading audio in and writing audio out */
+};
+
+enum ast_audiohook_flags {
+ AST_AUDIOHOOK_TRIGGER_MODE = (3 << 0), /*!< When audiohook should be triggered to do something */
+ AST_AUDIOHOOK_TRIGGER_READ = (1 << 0), /*!< Audiohook wants to be triggered when reading audio in */
+ AST_AUDIOHOOK_TRIGGER_WRITE = (2 << 0), /*!< Audiohook wants to be triggered when writing audio out */
+ AST_AUDIOHOOK_WANTS_DTMF = (1 << 1), /*!< Audiohook also wants to receive DTMF frames */
+};
+
+struct ast_audiohook;
+
+/*! \brief Callback function for manipulate audiohook type
+ * \param audiohook Audiohook structure
+ * \param chan Channel
+ * \param frame Frame of audio to manipulate
+ * \param direction Direction frame came from
+ * \return Returns 0 on success, -1 on failure
+ * \note An audiohook does not have any reference to a private data structure for manipulate types. It is up to the manipulate callback to store this data
+ * via it's own method. An example would be datastores.
+ */
+typedef int (*ast_audiohook_manipulate_callback)(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction);
+
+struct ast_audiohook_options {
+ int read_volume; /*!< Volume adjustment on frames read from the channel the hook is on */
+ int write_volume; /*!< Volume adjustment on frames written to the channel the hook is on */
+};
+
+struct ast_audiohook {
+ ast_mutex_t lock; /*!< Lock that protects the audiohook structure */
+ ast_cond_t trigger; /*!< Trigger condition (if enabled) */
+ enum ast_audiohook_type type; /*!< Type of audiohook */
+ enum ast_audiohook_status status; /*!< Status of the audiohook */
+ const char *source; /*!< Who this audiohook ultimately belongs to */
+ unsigned int flags; /*!< Flags on the audiohook */
+ struct ast_slinfactory read_factory; /*!< Factory where frames read from the channel, or read from the whisper source will go through */
+ struct ast_slinfactory write_factory; /*!< Factory where frames written to the channel will go through */
+ int format; /*!< Format translation path is setup as */
+ struct ast_trans_pvt *trans_pvt; /*!< Translation path for reading frames */
+ ast_audiohook_manipulate_callback manipulate_callback; /*!< Manipulation callback */
+ struct ast_audiohook_options options; /*!< Applicable options */
+ AST_LIST_ENTRY(ast_audiohook) list; /*!< Linked list information */
+};
+
+struct ast_audiohook_list;
+
+/*! \brief Initialize an audiohook structure
+ * \param audiohook Audiohook structure
+ * \param type Type of audiohook to initialize this as
+ * \param source Who is initializing this audiohook
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source);
+
+/*! \brief Destroys an audiohook structure
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_destroy(struct ast_audiohook *audiohook);
+
+/*! \brief Writes a frame into the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param direction Direction the audio frame came from
+ * \param frame Frame to write in
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohook_direction direction, struct ast_frame *frame);
+
+/*! \brief Reads a frame in from the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param samples Number of samples wanted
+ * \param direction Direction the audio frame came from
+ * \param format Format of frame remote side wants back
+ * \return Returns frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, int format);
+
+/*! \brief Attach audiohook to channel
+ * \param chan Channel
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook);
+
+/*! \brief Detach audiohook from channel
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach(struct ast_audiohook *audiohook);
+
+/*! \brief Detach audiohooks from list and destroy said list
+ * \param audiohook_list List of audiohooks
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list);
+
+/*!
+ * \brief Detach specified source audiohook from channel
+ *
+ * \param chan Channel to detach from
+ * \param source Name of source to detach
+ *
+ * \return Returns 0 on success, -1 on failure
+ *
+ * \note The channel does not need to be locked before calling this function.
+ */
+int ast_audiohook_detach_source(struct ast_channel *chan, const char *source);
+
+/*! \brief Pass a frame off to be handled by the audiohook core
+ * \param chan Channel that the list is coming off of
+ * \param audiohook_list List of audiohooks
+ * \param direction Direction frame is coming in from
+ * \param frame The frame itself
+ * \return Return frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame);
+
+/*! \brief Wait for audiohook trigger to be triggered
+ * \param audiohook Audiohook to wait on
+ */
+void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook);
+
+/*!
+ \brief Find out how many audiohooks from a certain source exist on a given channel, regardless of status.
+ \param chan The channel on which to find the spies
+ \param source The audiohook's source
+ \param type The type of audiohook
+ \return Return the number of audiohooks which are from the source specified
+
+ Note: Function performs nlocking.
+*/
+int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type);
+
+/*!
+ \brief Find out how many spies of a certain type exist on a given channel, and are in state running.
+ \param chan The channel on which to find the spies
+ \param source The source of the audiohook
+ \param type The type of spy to look for
+ \return Return the number of running audiohooks which are from the source specified
+
+ Note: Function performs no locking.
+*/
+int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type);
+
+/*! \brief Lock an audiohook
+ * \param ah Audiohook structure
+ */
+#define ast_audiohook_lock(ah) ast_mutex_lock(&(ah)->lock)
+
+/*! \brief Unlock an audiohook
+ * \param ah Audiohook structure
+ */
+#define ast_audiohook_unlock(ah) ast_mutex_unlock(&(ah)->lock)
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_AUDIOHOOK_H */
diff --git a/trunk/include/asterisk/autoconfig.h.in b/trunk/include/asterisk/autoconfig.h.in
new file mode 100644
index 000000000..3e9217689
--- /dev/null
+++ b/trunk/include/asterisk/autoconfig.h.in
@@ -0,0 +1,1205 @@
+/* include/asterisk/autoconfig.h.in. Generated from configure.ac by autoheader. */
+
+#ifndef ASTERISK_AUTOCONFIG_H
+#define ASTERISK_AUTOCONFIG_H
+
+#include "asterisk/buildopts.h"
+
+
+
+/* Define to 1 if the `closedir' function returns void instead of `int'. */
+#undef CLOSEDIR_VOID
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Define this to indicate the ${ACOS_DESCRIP} library */
+#undef HAVE_ACOS
+
+/* Define this to indicate the ${ACOSL_DESCRIP} library */
+#undef HAVE_ACOSL
+
+/* Define to indicate the ${ACOSL_DESCRIP} library version */
+#undef HAVE_ACOSL_VERSION
+
+/* Define to indicate the ${ACOS_DESCRIP} library version */
+#undef HAVE_ACOS_VERSION
+
+/* Define to 1 if you have the `alarm' function. */
+#undef HAVE_ALARM
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+#undef HAVE_ALLOCA_H
+
+/* Define this to indicate the ${ALSA_DESCRIP} library */
+#undef HAVE_ALSA
+
+/* Define to indicate the ${ALSA_DESCRIP} library version */
+#undef HAVE_ALSA_VERSION
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define this to indicate the ${ASIN_DESCRIP} library */
+#undef HAVE_ASIN
+
+/* Define this to indicate the ${ASINL_DESCRIP} library */
+#undef HAVE_ASINL
+
+/* Define to indicate the ${ASINL_DESCRIP} library version */
+#undef HAVE_ASINL_VERSION
+
+/* Define to indicate the ${ASIN_DESCRIP} library version */
+#undef HAVE_ASIN_VERSION
+
+/* Define to 1 if you have the `asprintf' function. */
+#undef HAVE_ASPRINTF
+
+/* Define this to indicate the ${ATAN_DESCRIP} library */
+#undef HAVE_ATAN
+
+/* Define this to indicate the ${ATAN2_DESCRIP} library */
+#undef HAVE_ATAN2
+
+/* Define this to indicate the ${ATAN2L_DESCRIP} library */
+#undef HAVE_ATAN2L
+
+/* Define to indicate the ${ATAN2L_DESCRIP} library version */
+#undef HAVE_ATAN2L_VERSION
+
+/* Define to indicate the ${ATAN2_DESCRIP} library version */
+#undef HAVE_ATAN2_VERSION
+
+/* Define this to indicate the ${ATANL_DESCRIP} library */
+#undef HAVE_ATANL
+
+/* Define to indicate the ${ATANL_DESCRIP} library version */
+#undef HAVE_ATANL_VERSION
+
+/* Define to indicate the ${ATAN_DESCRIP} library version */
+#undef HAVE_ATAN_VERSION
+
+/* Define to 1 if you have the `atexit' function. */
+#undef HAVE_ATEXIT
+
+/* Define to 1 if your GCC C compiler supports the 'always_inline' attribute.
+ */
+#undef HAVE_ATTRIBUTE_always_inline
+
+/* Define to 1 if your GCC C compiler supports the 'const' attribute. */
+#undef HAVE_ATTRIBUTE_const
+
+/* Define to 1 if your GCC C compiler supports the 'deprecated' attribute. */
+#undef HAVE_ATTRIBUTE_deprecated
+
+/* Define to 1 if your GCC C compiler supports the 'malloc' attribute. */
+#undef HAVE_ATTRIBUTE_malloc
+
+/* Define to 1 if your GCC C compiler supports the 'pure' attribute. */
+#undef HAVE_ATTRIBUTE_pure
+
+/* Define to 1 if your GCC C compiler supports the 'unused' attribute. */
+#undef HAVE_ATTRIBUTE_unused
+
+/* Define this to indicate the ${BKTR_DESCRIP} library */
+#undef HAVE_BKTR
+
+/* Define to indicate the ${BKTR_DESCRIP} library version */
+#undef HAVE_BKTR_VERSION
+
+/* Define to 1 if byteswap.h macros are available. */
+#undef HAVE_BYTESWAP_H
+
+/* Define to 1 if you have the `bzero' function. */
+#undef HAVE_BZERO
+
+/* Define this to indicate the ${CAP_DESCRIP} library */
+#undef HAVE_CAP
+
+/* Define to indicate the ${CAP_DESCRIP} library version */
+#undef HAVE_CAP_VERSION
+
+/* Define this to indicate the ${CEIL_DESCRIP} library */
+#undef HAVE_CEIL
+
+/* Define this to indicate the ${CEILL_DESCRIP} library */
+#undef HAVE_CEILL
+
+/* Define to indicate the ${CEILL_DESCRIP} library version */
+#undef HAVE_CEILL_VERSION
+
+/* Define to indicate the ${CEIL_DESCRIP} library version */
+#undef HAVE_CEIL_VERSION
+
+/* Define to 1 if your system has a working `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define this to indicate the ${COS_DESCRIP} library */
+#undef HAVE_COS
+
+/* Define this to indicate the ${COSL_DESCRIP} library */
+#undef HAVE_COSL
+
+/* Define to indicate the ${COSL_DESCRIP} library version */
+#undef HAVE_COSL_VERSION
+
+/* Define to indicate the ${COS_DESCRIP} library version */
+#undef HAVE_COS_VERSION
+
+/* Define this to indicate the ${CRYPTO_DESCRIP} library */
+#undef HAVE_CRYPTO
+
+/* Define to indicate the ${CRYPTO_DESCRIP} library version */
+#undef HAVE_CRYPTO_VERSION
+
+/* Define if your system has the curl libraries. */
+#undef HAVE_CURL
+
+/* Define this to indicate the ${CURSES_DESCRIP} library */
+#undef HAVE_CURSES
+
+/* Define to indicate the ${CURSES_DESCRIP} library version */
+#undef HAVE_CURSES_VERSION
+
+/* Define to 1 if your system has /dev/urandom. */
+#undef HAVE_DEV_URANDOM
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+#undef HAVE_DOPRNT
+
+/* Define to 1 if you have the `dup2' function. */
+#undef HAVE_DUP2
+
+/* Define to 1 if you have the `endpwent' function. */
+#undef HAVE_ENDPWENT
+
+/* Define to 1 if your system has working epoll support. */
+#undef HAVE_EPOLL
+
+/* Define this to indicate the ${EXP_DESCRIP} library */
+#undef HAVE_EXP
+
+/* Define this to indicate the ${EXP10_DESCRIP} library */
+#undef HAVE_EXP10
+
+/* Define this to indicate the ${EXP10L_DESCRIP} library */
+#undef HAVE_EXP10L
+
+/* Define to indicate the ${EXP10L_DESCRIP} library version */
+#undef HAVE_EXP10L_VERSION
+
+/* Define to indicate the ${EXP10_DESCRIP} library version */
+#undef HAVE_EXP10_VERSION
+
+/* Define this to indicate the ${EXP2_DESCRIP} library */
+#undef HAVE_EXP2
+
+/* Define this to indicate the ${EXP2L_DESCRIP} library */
+#undef HAVE_EXP2L
+
+/* Define to indicate the ${EXP2L_DESCRIP} library version */
+#undef HAVE_EXP2L_VERSION
+
+/* Define to indicate the ${EXP2_DESCRIP} library version */
+#undef HAVE_EXP2_VERSION
+
+/* Define this to indicate the ${EXPL_DESCRIP} library */
+#undef HAVE_EXPL
+
+/* Define to indicate the ${EXPL_DESCRIP} library version */
+#undef HAVE_EXPL_VERSION
+
+/* Define to indicate the ${EXP_DESCRIP} library version */
+#undef HAVE_EXP_VERSION
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define this to indicate the ${FFMPEG_DESCRIP} library */
+#undef HAVE_FFMPEG
+
+/* Define to indicate the ${FFMPEG_DESCRIP} library version */
+#undef HAVE_FFMPEG_VERSION
+
+/* Define this to indicate the ${FLOOR_DESCRIP} library */
+#undef HAVE_FLOOR
+
+/* Define this to indicate the ${FLOORL_DESCRIP} library */
+#undef HAVE_FLOORL
+
+/* Define to indicate the ${FLOORL_DESCRIP} library version */
+#undef HAVE_FLOORL_VERSION
+
+/* Define to indicate the ${FLOOR_DESCRIP} library version */
+#undef HAVE_FLOOR_VERSION
+
+/* Define this to indicate the ${FMOD_DESCRIP} library */
+#undef HAVE_FMOD
+
+/* Define this to indicate the ${FMODL_DESCRIP} library */
+#undef HAVE_FMODL
+
+/* Define to indicate the ${FMODL_DESCRIP} library version */
+#undef HAVE_FMODL_VERSION
+
+/* Define to indicate the ${FMOD_DESCRIP} library version */
+#undef HAVE_FMOD_VERSION
+
+/* Define to 1 if you have the `fopencookie' function. */
+#undef HAVE_FOPENCOOKIE
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define this to indicate the ${FREETDS_DESCRIP} library */
+#undef HAVE_FREETDS
+
+/* Define to indicate the ${FREETDS_DESCRIP} library version */
+#undef HAVE_FREETDS_VERSION
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#undef HAVE_FSEEKO
+
+/* Define to 1 if you have the `ftruncate' function. */
+#undef HAVE_FTRUNCATE
+
+/* Define to 1 if you have the `funopen' function. */
+#undef HAVE_FUNOPEN
+
+/* Define to 1 if your GCC C compiler provides atomic operations. */
+#undef HAVE_GCC_ATOMICS
+
+/* Define to 1 if you have the `getcwd' function. */
+#undef HAVE_GETCWD
+
+/* Define to 1 if you have the `gethostbyname' function. */
+#undef HAVE_GETHOSTBYNAME
+
+/* Define to 1 if your system has gethostbyname_r with 5 arguments. */
+#undef HAVE_GETHOSTBYNAME_R_5
+
+/* Define to 1 if your system has gethostbyname_r with 6 arguments. */
+#undef HAVE_GETHOSTBYNAME_R_6
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if you have the `getloadavg' function. */
+#undef HAVE_GETLOADAVG
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the `glob' function. */
+#undef HAVE_GLOB
+
+/* Define to indicate the GSM library */
+#undef HAVE_GSM
+
+/* Define to indicate that gsm.h is in gsm/gsm.h */
+#undef HAVE_GSM_GSM_HEADER
+
+/* Define to indicate that gsm.h has no prefix for its location */
+#undef HAVE_GSM_HEADER
+
+/* Define if your system has the GTK libraries. */
+#undef HAVE_GTK
+
+/* Define if your system has the GTK2 libraries. */
+#undef HAVE_GTK2
+
+/* Define this to indicate the ${ICONV_DESCRIP} library */
+#undef HAVE_ICONV
+
+/* Define to indicate the ${ICONV_DESCRIP} library version */
+#undef HAVE_ICONV_VERSION
+
+/* Define this to indicate the ${IKSEMEL_DESCRIP} library */
+#undef HAVE_IKSEMEL
+
+/* Define to indicate the ${IKSEMEL_DESCRIP} library version */
+#undef HAVE_IKSEMEL_VERSION
+
+/* Define if your system has the UW IMAP Toolkit c-client library. */
+#undef HAVE_IMAP_TK
+
+/* Define if your system has the UW IMAP Toolkit c-client library version 2006
+ or greater. */
+#undef HAVE_IMAP_TK2006
+
+/* Define to 1 if you have the `inet_aton' function. */
+#undef HAVE_INET_ATON
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#undef HAVE_INET_NTOA
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define if your system has the IP_MTU_DISCOVER headers. */
+#undef HAVE_IP_MTU_DISCOVER
+
+/* Define IP_MTU_DISCOVER headers version */
+#undef HAVE_IP_MTU_DISCOVER_VERSION
+
+/* Define to 1 if you have the `isascii' function. */
+#undef HAVE_ISASCII
+
+/* Define this to indicate the ${ISDNNET_DESCRIP} library */
+#undef HAVE_ISDNNET
+
+/* Define to indicate the ${ISDNNET_DESCRIP} library version */
+#undef HAVE_ISDNNET_VERSION
+
+/* Define this to indicate the ${JACK_DESCRIP} library */
+#undef HAVE_JACK
+
+/* Define to indicate the ${JACK_DESCRIP} library version */
+#undef HAVE_JACK_VERSION
+
+/* Define to 1 if you have the <libintl.h> header file. */
+#undef HAVE_LIBINTL_H
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if your system has linux/compiler.h. */
+#undef HAVE_LINUX_COMPILER_H
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if you have the `localtime_r' function. */
+#undef HAVE_LOCALTIME_R
+
+/* Define this to indicate the ${LOG_DESCRIP} library */
+#undef HAVE_LOG
+
+/* Define this to indicate the ${LOG10_DESCRIP} library */
+#undef HAVE_LOG10
+
+/* Define this to indicate the ${LOG10L_DESCRIP} library */
+#undef HAVE_LOG10L
+
+/* Define to indicate the ${LOG10L_DESCRIP} library version */
+#undef HAVE_LOG10L_VERSION
+
+/* Define to indicate the ${LOG10_DESCRIP} library version */
+#undef HAVE_LOG10_VERSION
+
+/* Define this to indicate the ${LOG2_DESCRIP} library */
+#undef HAVE_LOG2
+
+/* Define this to indicate the ${LOG2L_DESCRIP} library */
+#undef HAVE_LOG2L
+
+/* Define to indicate the ${LOG2L_DESCRIP} library version */
+#undef HAVE_LOG2L_VERSION
+
+/* Define to indicate the ${LOG2_DESCRIP} library version */
+#undef HAVE_LOG2_VERSION
+
+/* Define this to indicate the ${LOGL_DESCRIP} library */
+#undef HAVE_LOGL
+
+/* Define to indicate the ${LOGL_DESCRIP} library version */
+#undef HAVE_LOGL_VERSION
+
+/* Define to indicate the ${LOG_DESCRIP} library version */
+#undef HAVE_LOG_VERSION
+
+/* Define this to indicate the ${LTDL_DESCRIP} library */
+#undef HAVE_LTDL
+
+/* Define to indicate the ${LTDL_DESCRIP} library version */
+#undef HAVE_LTDL_VERSION
+
+/* Define this to indicate the ${LUA_DESCRIP} library */
+#undef HAVE_LUA
+
+/* Define to indicate the ${LUA_DESCRIP} library version */
+#undef HAVE_LUA_VERSION
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the `memchr' function. */
+#undef HAVE_MEMCHR
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define this to indicate the ${MISDN_DESCRIP} library */
+#undef HAVE_MISDN
+
+/* Define if your system has the MISDN_FAC_ERROR headers. */
+#undef HAVE_MISDN_FAC_ERROR
+
+/* Define MISDN_FAC_ERROR headers version */
+#undef HAVE_MISDN_FAC_ERROR_VERSION
+
+/* Define if your system has the MISDN_FAC_RESULT headers. */
+#undef HAVE_MISDN_FAC_RESULT
+
+/* Define MISDN_FAC_RESULT headers version */
+#undef HAVE_MISDN_FAC_RESULT_VERSION
+
+/* Define to indicate the ${MISDN_DESCRIP} library version */
+#undef HAVE_MISDN_VERSION
+
+/* Define to 1 if you have the `mkdir' function. */
+#undef HAVE_MKDIR
+
+/* Define to 1 if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define to 1 if you have the `munmap' function. */
+#undef HAVE_MUNMAP
+
+/* Define this to indicate the ${NBS_DESCRIP} library */
+#undef HAVE_NBS
+
+/* Define to indicate the ${NBS_DESCRIP} library version */
+#undef HAVE_NBS_VERSION
+
+/* Define this to indicate the ${NCURSES_DESCRIP} library */
+#undef HAVE_NCURSES
+
+/* Define to indicate the ${NCURSES_DESCRIP} library version */
+#undef HAVE_NCURSES_VERSION
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define if your system has the NETSNMP libraries. */
+#undef HAVE_NETSNMP
+
+/* Define this to indicate the ${NEWT_DESCRIP} library */
+#undef HAVE_NEWT
+
+/* Define to indicate the ${NEWT_DESCRIP} library version */
+#undef HAVE_NEWT_VERSION
+
+/* Define this to indicate the ${OGG_DESCRIP} library */
+#undef HAVE_OGG
+
+/* Define to indicate the ${OGG_DESCRIP} library version */
+#undef HAVE_OGG_VERSION
+
+/* Define if your system has the OpenH323 libraries. */
+#undef HAVE_OPENH323
+
+/* Define this to indicate the ${OPENSSL_DESCRIP} library */
+#undef HAVE_OPENSSL
+
+/* Define to indicate the ${OPENSSL_DESCRIP} library version */
+#undef HAVE_OPENSSL_VERSION
+
+/* Define this to indicate the ${OSPTK_DESCRIP} library */
+#undef HAVE_OSPTK
+
+/* Define to indicate the ${OSPTK_DESCRIP} library version */
+#undef HAVE_OSPTK_VERSION
+
+/* Define this to indicate the ${OSS_DESCRIP} library */
+#undef HAVE_OSS
+
+/* Define to indicate the ${OSS_DESCRIP} library version */
+#undef HAVE_OSS_VERSION
+
+/* Define to 1 if OSX atomic operations are supported. */
+#undef HAVE_OSX_ATOMICS
+
+/* Define to indicate the PostgreSQL library */
+#undef HAVE_PGSQL
+
+/* Define to 1 if your system defines IP_PKTINFO. */
+#undef HAVE_PKTINFO
+
+/* Define this to indicate the ${POPT_DESCRIP} library */
+#undef HAVE_POPT
+
+/* Define to indicate the ${POPT_DESCRIP} library version */
+#undef HAVE_POPT_VERSION
+
+/* Define this to indicate the ${PORTAUDIO_DESCRIP} library */
+#undef HAVE_PORTAUDIO
+
+/* Define to indicate the ${PORTAUDIO_DESCRIP} library version */
+#undef HAVE_PORTAUDIO_VERSION
+
+/* Define this to indicate the ${POW_DESCRIP} library */
+#undef HAVE_POW
+
+/* Define this to indicate the ${POWL_DESCRIP} library */
+#undef HAVE_POWL
+
+/* Define to indicate the ${POWL_DESCRIP} library version */
+#undef HAVE_POWL_VERSION
+
+/* Define to indicate the ${POW_DESCRIP} library version */
+#undef HAVE_POW_VERSION
+
+/* Define this to indicate the ${PRI_DESCRIP} library */
+#undef HAVE_PRI
+
+/* Define to indicate the ${PRI_DESCRIP} library version */
+#undef HAVE_PRI_VERSION
+
+/* Define if you have POSIX threads libraries and header files. */
+#undef HAVE_PTHREAD
+
+/* Define to 1 if your system defines PTHREAD_MUTEX_RECURSIVE_NP in pthread.h
+ */
+#undef HAVE_PTHREAD_MUTEX_RECURSIVE_NP
+
+/* Define if your system has the PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+ headers. */
+#undef HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+
+/* Define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP headers version */
+#undef HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP_VERSION
+
+/* Define if your system has the PTHREAD_RWLOCK_INITIALIZER headers. */
+#undef HAVE_PTHREAD_RWLOCK_INITIALIZER
+
+/* Define PTHREAD_RWLOCK_INITIALIZER headers version */
+#undef HAVE_PTHREAD_RWLOCK_INITIALIZER_VERSION
+
+/* Define to 1 if your system defines PTHREAD_RWLOCK_PREFER_WRITER_NP in
+ pthread.h */
+#undef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
+
+/* Define to 1 if the system has the type `ptrdiff_t'. */
+#undef HAVE_PTRDIFF_T
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define if your system has the PWLib libraries. */
+#undef HAVE_PWLIB
+
+/* Define this to indicate the ${RADIUS_DESCRIP} library */
+#undef HAVE_RADIUS
+
+/* Define to indicate the ${RADIUS_DESCRIP} library version */
+#undef HAVE_RADIUS_VERSION
+
+/* Define to 1 if you have the `regcomp' function. */
+#undef HAVE_REGCOMP
+
+/* Define this to indicate the ${REMAINDER_DESCRIP} library */
+#undef HAVE_REMAINDER
+
+/* Define this to indicate the ${REMAINDERL_DESCRIP} library */
+#undef HAVE_REMAINDERL
+
+/* Define to indicate the ${REMAINDERL_DESCRIP} library version */
+#undef HAVE_REMAINDERL_VERSION
+
+/* Define to indicate the ${REMAINDER_DESCRIP} library version */
+#undef HAVE_REMAINDER_VERSION
+
+/* Define to 1 if your system has the ndestroy resolver function. */
+#undef HAVE_RES_NDESTROY
+
+/* Define to 1 if your system has the re-entrant resolver functions. */
+#undef HAVE_RES_NINIT
+
+/* Define to 1 if you have the `re_comp' function. */
+#undef HAVE_RE_COMP
+
+/* Define this to indicate the ${RINT_DESCRIP} library */
+#undef HAVE_RINT
+
+/* Define this to indicate the ${RINTL_DESCRIP} library */
+#undef HAVE_RINTL
+
+/* Define to indicate the ${RINTL_DESCRIP} library version */
+#undef HAVE_RINTL_VERSION
+
+/* Define to indicate the ${RINT_DESCRIP} library version */
+#undef HAVE_RINT_VERSION
+
+/* Define this to indicate the ${ROUND_DESCRIP} library */
+#undef HAVE_ROUND
+
+/* Define this to indicate the ${ROUNDL_DESCRIP} library */
+#undef HAVE_ROUNDL
+
+/* Define to indicate the ${ROUNDL_DESCRIP} library version */
+#undef HAVE_ROUNDL_VERSION
+
+/* Define to indicate the ${ROUND_DESCRIP} library version */
+#undef HAVE_ROUND_VERSION
+
+/* Define if your system has the RTLD_NOLOAD headers. */
+#undef HAVE_RTLD_NOLOAD
+
+/* Define RTLD_NOLOAD headers version */
+#undef HAVE_RTLD_NOLOAD_VERSION
+
+/* Define if your system has the SDL libraries. */
+#undef HAVE_SDL
+
+/* Define this to indicate the ${SDL_IMAGE_DESCRIP} library */
+#undef HAVE_SDL_IMAGE
+
+/* Define to indicate the ${SDL_IMAGE_DESCRIP} library version */
+#undef HAVE_SDL_IMAGE_VERSION
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define this to indicate the ${SIN_DESCRIP} library */
+#undef HAVE_SIN
+
+/* Define this to indicate the ${SINL_DESCRIP} library */
+#undef HAVE_SINL
+
+/* Define to indicate the ${SINL_DESCRIP} library version */
+#undef HAVE_SINL_VERSION
+
+/* Define to indicate the ${SIN_DESCRIP} library version */
+#undef HAVE_SIN_VERSION
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if your system has soxmix application. */
+#undef HAVE_SOXMIX
+
+/* Define this to indicate the ${SPEEX_DESCRIP} library */
+#undef HAVE_SPEEX
+
+/* Define this to indicate the ${SPEEXDSP_DESCRIP} library */
+#undef HAVE_SPEEXDSP
+
+/* Define to indicate the ${SPEEXDSP_DESCRIP} library version */
+#undef HAVE_SPEEXDSP_VERSION
+
+/* Define to indicate the ${SPEEX_DESCRIP} library version */
+#undef HAVE_SPEEX_VERSION
+
+/* Define this to indicate the ${SQLITE_DESCRIP} library */
+#undef HAVE_SQLITE
+
+/* Define this to indicate the ${SQLITE3_DESCRIP} library */
+#undef HAVE_SQLITE3
+
+/* Define to indicate the ${SQLITE3_DESCRIP} library version */
+#undef HAVE_SQLITE3_VERSION
+
+/* Define to indicate the ${SQLITE_DESCRIP} library version */
+#undef HAVE_SQLITE_VERSION
+
+/* Define this to indicate the ${SQRT_DESCRIP} library */
+#undef HAVE_SQRT
+
+/* Define this to indicate the ${SQRTL_DESCRIP} library */
+#undef HAVE_SQRTL
+
+/* Define to indicate the ${SQRTL_DESCRIP} library version */
+#undef HAVE_SQRTL_VERSION
+
+/* Define to indicate the ${SQRT_DESCRIP} library version */
+#undef HAVE_SQRT_VERSION
+
+/* Define this to indicate the ${SS7_DESCRIP} library */
+#undef HAVE_SS7
+
+/* Define to indicate the ${SS7_DESCRIP} library version */
+#undef HAVE_SS7_VERSION
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+#undef HAVE_STAT_EMPTY_STRING_BUG
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#undef HAVE_STDBOOL_H
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#undef HAVE_STDDEF_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strcasestr' function. */
+#undef HAVE_STRCASESTR
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strcoll' function and it is properly defined.
+ */
+#undef HAVE_STRCOLL
+
+/* Define to 1 if you have the `strcspn' function. */
+#undef HAVE_STRCSPN
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strndup' function. */
+#undef HAVE_STRNDUP
+
+/* Define to 1 if you have the `strnlen' function. */
+#undef HAVE_STRNLEN
+
+/* Define to 1 if you have the `strrchr' function. */
+#undef HAVE_STRRCHR
+
+/* Define to 1 if you have the `strsep' function. */
+#undef HAVE_STRSEP
+
+/* Define to 1 if you have the `strspn' function. */
+#undef HAVE_STRSPN
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define this to indicate the ${STRTOD_DESCRIP} library */
+#undef HAVE_STRTOD
+
+/* Define to indicate the ${STRTOD_DESCRIP} library version */
+#undef HAVE_STRTOD_VERSION
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define this to indicate the ${STRTOLD_DESCRIP} library */
+#undef HAVE_STRTOLD
+
+/* Define to indicate the ${STRTOLD_DESCRIP} library version */
+#undef HAVE_STRTOLD_VERSION
+
+/* Define to 1 if you have the `strtoq' function. */
+#undef HAVE_STRTOQ
+
+/* Define to 1 if `st_blksize' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_BLKSIZE
+
+/* Define this to indicate the ${SUPPSERV_DESCRIP} library */
+#undef HAVE_SUPPSERV
+
+/* Define to indicate the ${SUPPSERV_DESCRIP} library version */
+#undef HAVE_SUPPSERV_VERSION
+
+/* Define to 1 if your system has sysinfo support */
+#undef HAVE_SYSINFO
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if your sys/endian.h header file provides the bswap16 macro. */
+#undef HAVE_SYS_ENDIAN_BSWAP16
+
+/* Define to 1 if your sys/endian.h header file provides the __swap16 macro.
+ */
+#undef HAVE_SYS_ENDIAN_SWAP16
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if your system has working sys/poll.h */
+#undef HAVE_SYS_POLL_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define this to indicate the ${TAN_DESCRIP} library */
+#undef HAVE_TAN
+
+/* Define this to indicate the ${TANL_DESCRIP} library */
+#undef HAVE_TANL
+
+/* Define to indicate the ${TANL_DESCRIP} library version */
+#undef HAVE_TANL_VERSION
+
+/* Define to indicate the ${TAN_DESCRIP} library version */
+#undef HAVE_TAN_VERSION
+
+/* Define this to indicate the ${TERMCAP_DESCRIP} library */
+#undef HAVE_TERMCAP
+
+/* Define to indicate the ${TERMCAP_DESCRIP} library version */
+#undef HAVE_TERMCAP_VERSION
+
+/* Define to 1 if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define to 1 if your system defines timersub. */
+#undef HAVE_TIMERSUB
+
+/* Define this to indicate the ${TINFO_DESCRIP} library */
+#undef HAVE_TINFO
+
+/* Define to indicate the ${TINFO_DESCRIP} library version */
+#undef HAVE_TINFO_VERSION
+
+/* Define this to indicate the ${TONEZONE_DESCRIP} library */
+#undef HAVE_TONEZONE
+
+/* Define to indicate the ${TONEZONE_DESCRIP} library version */
+#undef HAVE_TONEZONE_VERSION
+
+/* Define this to indicate the ${TRUNC_DESCRIP} library */
+#undef HAVE_TRUNC
+
+/* Define this to indicate the ${TRUNCL_DESCRIP} library */
+#undef HAVE_TRUNCL
+
+/* Define to indicate the ${TRUNCL_DESCRIP} library version */
+#undef HAVE_TRUNCL_VERSION
+
+/* Define to indicate the ${TRUNC_DESCRIP} library version */
+#undef HAVE_TRUNC_VERSION
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define this to indicate the ${UNIXODBC_DESCRIP} library */
+#undef HAVE_UNIXODBC
+
+/* Define to indicate the ${UNIXODBC_DESCRIP} library version */
+#undef HAVE_UNIXODBC_VERSION
+
+/* Define to 1 if you have the `unsetenv' function. */
+#undef HAVE_UNSETENV
+
+/* Define this to indicate the ${USB_DESCRIP} library */
+#undef HAVE_USB
+
+/* Define to indicate the ${USB_DESCRIP} library version */
+#undef HAVE_USB_VERSION
+
+/* Define to 1 if you have the `utime' function. */
+#undef HAVE_UTIME
+
+/* Define to 1 if you have the <utime.h> header file. */
+#undef HAVE_UTIME_H
+
+/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
+#undef HAVE_UTIME_NULL
+
+/* Define to 1 if you have the `vasprintf' function. */
+#undef HAVE_VASPRINTF
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if your system has linux/videodev.h. */
+#undef HAVE_VIDEODEV_H
+
+/* Define this to indicate the ${VORBIS_DESCRIP} library */
+#undef HAVE_VORBIS
+
+/* Define to indicate the ${VORBIS_DESCRIP} library version */
+#undef HAVE_VORBIS_VERSION
+
+/* Define if your system has the VoiceTronix API libraries. */
+#undef HAVE_VPB
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+#undef HAVE_WINSOCK2_H
+
+/* Define to 1 if you have the <winsock.h> header file. */
+#undef HAVE_WINSOCK_H
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define this to indicate the ${X11_DESCRIP} library */
+#undef HAVE_X11
+
+/* Define to indicate the ${X11_DESCRIP} library version */
+#undef HAVE_X11_VERSION
+
+/* Define this to indicate the ${ZAPTEL_DESCRIP} library */
+#undef HAVE_ZAPTEL
+
+/* Define if your system has the ZAPTEL_CHANALARMS headers. */
+#undef HAVE_ZAPTEL_CHANALARMS
+
+/* Define ZAPTEL_CHANALARMS headers version */
+#undef HAVE_ZAPTEL_CHANALARMS_VERSION
+
+/* Define if your system has the ZAPTEL_ECHOCANPARAMS headers. */
+#undef HAVE_ZAPTEL_ECHOCANPARAMS
+
+/* Define ZAPTEL_ECHOCANPARAMS headers version */
+#undef HAVE_ZAPTEL_ECHOCANPARAMS_VERSION
+
+/* Define if your system has the ZAPTEL_HWGAIN headers. */
+#undef HAVE_ZAPTEL_HWGAIN
+
+/* Define ZAPTEL_HWGAIN headers version */
+#undef HAVE_ZAPTEL_HWGAIN_VERSION
+
+/* Define if your system has the ZAPTEL_TRANSCODE headers. */
+#undef HAVE_ZAPTEL_TRANSCODE
+
+/* Define ZAPTEL_TRANSCODE headers version */
+#undef HAVE_ZAPTEL_TRANSCODE_VERSION
+
+/* Define to indicate the ${ZAPTEL_DESCRIP} library version */
+#undef HAVE_ZAPTEL_VERSION
+
+/* Define this to indicate the ${ZAPTEL_VLDTMF_DESCRIP} library */
+#undef HAVE_ZAPTEL_VLDTMF
+
+/* Define to indicate the ${ZAPTEL_VLDTMF_DESCRIP} library version */
+#undef HAVE_ZAPTEL_VLDTMF_VERSION
+
+/* Define this to indicate the ${ZLIB_DESCRIP} library */
+#undef HAVE_ZLIB
+
+/* Define to indicate the ${ZLIB_DESCRIP} library version */
+#undef HAVE_ZLIB_VERSION
+
+/* Define to 1 if the system has the type `_Bool'. */
+#undef HAVE__BOOL
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+ slash. */
+#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
+
+/* Build chan_misdn for mISDN 1.2 or later. */
+#undef MISDN_1_2
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if the C compiler supports function prototypes. */
+#undef PROTOTYPES
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+#undef PTHREAD_CREATE_JOINABLE
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to the type of arg 1 for `select'. */
+#undef SELECT_TYPE_ARG1
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#undef SELECT_TYPE_ARG234
+
+/* Define to the type of arg 5 for `select'. */
+#undef SELECT_TYPE_ARG5
+
+/* Define to 1 if the `setvbuf' function takes the buffering type as its
+ second argument and the buffer pointer as the third, as on System V before
+ release 3. */
+#undef SETVBUF_REVERSED
+
+/* The size of `int', as computed by sizeof. */
+#undef SIZEOF_INT
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#undef _LARGEFILE_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Enable extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+
+/* Define like PROTOTYPES; this can be used by system headers. */
+#undef __PROTOTYPES
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef mode_t
+
+/* Define to `long int' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+ code using `volatile' can become incorrect without. Disable with care. */
+#undef volatile
+
+#endif
+
diff --git a/trunk/include/asterisk/callerid.h b/trunk/include/asterisk/callerid.h
new file mode 100644
index 000000000..8bf27158b
--- /dev/null
+++ b/trunk/include/asterisk/callerid.h
@@ -0,0 +1,345 @@
+/*
+ * 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 CallerID (and other GR30) management and generation
+ * Includes code and algorithms from the Zapata library.
+ *
+ * \ref CID
+ *
+ */
+
+/*!
+ * \page CID Caller ID names and numbers
+ *
+ * Caller ID names are currently 8 bit characters, propably
+ * ISO8859-1, depending on what your channel drivers handle.
+ *
+ * IAX2 and SIP caller ID names are UTF8
+ * On ISDN Caller ID names are 7 bit, Almost ASCII
+ * (See http://www.zytrax.com/tech/ia5.html )
+ *
+ * \note Asterisk does not currently support SIP utf8 caller ID names or caller ID's.
+ *
+ * \par See also
+ * \arg \ref callerid.c
+ * \arg \ref callerid.h
+ * \arg \ref Def_CallerPres
+ */
+
+#ifndef _ASTERISK_CALLERID_H
+#define _ASTERISK_CALLERID_H
+
+#define MAX_CALLERID_SIZE 32000
+
+#define CID_PRIVATE_NAME (1 << 0)
+#define CID_PRIVATE_NUMBER (1 << 1)
+#define CID_UNKNOWN_NAME (1 << 2)
+#define CID_UNKNOWN_NUMBER (1 << 3)
+#define CID_MSGWAITING (1 << 4)
+#define CID_NOMSGWAITING (1 << 5)
+
+#define CID_SIG_BELL 1
+#define CID_SIG_V23 2
+#define CID_SIG_DTMF 3
+#define CID_SIG_V23_JP 4
+#define CID_SIG_SMDI 5
+
+#define CID_START_RING 1
+#define CID_START_POLARITY 2
+#define CID_START_POLARITY_IN 3
+
+
+#define AST_LIN2X(a) ((codec == AST_FORMAT_ALAW) ? (AST_LIN2A(a)) : (AST_LIN2MU(a)))
+#define AST_XLAW(a) ((codec == AST_FORMAT_ALAW) ? (AST_ALAW(a)) : (AST_MULAW(a)))
+
+
+struct callerid_state;
+typedef struct callerid_state CIDSTATE;
+
+/*! \brief CallerID Initialization
+ * \par
+ * Initializes the callerid system. Mostly stuff for inverse FFT
+ */
+void callerid_init(void);
+
+/*! \brief Generates a CallerID FSK stream in ulaw format suitable for transmission.
+ * \param buf Buffer to use. If "buf" is supplied, it will use that buffer instead of allocating its own. "buf" must be at least 32000 bytes in size of you want to be sure you don't have an overrun.
+ * \param number Use NULL for no number or "P" for "private"
+ * \param name name to be used
+ * \param flags passed flags
+ * \param callwaiting callwaiting flag
+ * \param codec -- either AST_FORMAT_ULAW or AST_FORMAT_ALAW
+ * This function creates a stream of callerid (a callerid spill) data in ulaw format.
+ * \return It returns the size
+ * (in bytes) of the data (if it returns a size of 0, there is probably an error)
+*/
+int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, int codec);
+
+/*! \brief Create a callerID state machine
+ * \param cid_signalling Type of signalling in use
+ *
+ * This function returns a malloc'd instance of the callerid_state data structure.
+ * \return Returns a pointer to a malloc'd callerid_state structure, or NULL on error.
+ */
+struct callerid_state *callerid_new(int cid_signalling);
+
+/*! \brief Read samples into the state machine.
+ * \param cid Which state machine to act upon
+ * \param ubuf containing your samples
+ * \param samples number of samples contained within the buffer.
+ * \param codec which codec (AST_FORMAT_ALAW or AST_FORMAT_ULAW)
+ *
+ * Send received audio to the Caller*ID demodulator.
+ * \return Returns -1 on error, 0 for "needs more samples",
+ * and 1 if the CallerID spill reception is complete.
+ */
+int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int samples, int codec);
+
+/*! \brief Read samples into the state machine.
+ * \param cid Which state machine to act upon
+ * \param ubuf containing your samples
+ * \param samples number of samples contained within the buffer.
+ * \param codec which codec (AST_FORMAT_ALAW or AST_FORMAT_ULAW)
+ *
+ * Send received audio to the Caller*ID demodulator (for japanese style lines).
+ * \return Returns -1 on error, 0 for "needs more samples",
+ * and 1 if the CallerID spill reception is complete.
+ */
+int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int samples, int codec);
+
+/*! \brief Extract info out of callerID state machine. Flags are listed above
+ * \param cid Callerid state machine to act upon
+ * \param number Pass the address of a pointer-to-char (will contain the phone number)
+ * \param name Pass the address of a pointer-to-char (will contain the name)
+ * \param flags Pass the address of an int variable(will contain the various callerid flags)
+ *
+ * This function extracts a callerid string out of a callerid_state state machine.
+ * If no number is found, *number will be set to NULL. Likewise for the name.
+ * Flags can contain any of the following:
+ *
+ * \return Returns nothing.
+ */
+void callerid_get(struct callerid_state *cid, char **number, char **name, int *flags);
+
+/*! Get and parse DTMF-based callerid */
+/*!
+ * \param cidstring The actual transmitted string.
+ * \param number The cid number is returned here.
+ * \param flags The cid flags are returned here.
+ * This function parses DTMF callerid.
+ */
+void callerid_get_dtmf(char *cidstring, char *number, int *flags);
+
+/*! \brief Free a callerID state
+ * \param cid This is the callerid_state state machine to free
+ * This function frees callerid_state cid.
+ */
+void callerid_free(struct callerid_state *cid);
+
+/*! \brief Generate Caller-ID spill from the "callerid" field of asterisk (in e-mail address like format)
+ * \param buf buffer for output samples. See callerid_generate() for details regarding buffer.
+ * \param name Caller-ID Name
+ * \param number Caller-ID Number
+ * \param codec Asterisk codec (either AST_FORMAT_ALAW or AST_FORMAT_ULAW)
+ *
+ * Acts like callerid_generate except uses an asterisk format callerid string.
+ */
+int ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int codec);
+
+/*! \brief Generate message waiting indicator (stutter tone) */
+int vmwi_generate(unsigned char *buf, int active, int mdmf, int codec);
+
+/*! \brief Generate Caller-ID spill but in a format suitable for Call Waiting(tm)'s Caller*ID(tm)
+ * See ast_callerid_generate() for other details
+ */
+int ast_callerid_callwaiting_generate(unsigned char *buf, const char *name, const char *number, int codec);
+
+/*! \brief Destructively parse inbuf into name and location (or number)
+ * Parses callerid stream from inbuf and changes into useable form, outputed in name and location.
+ * \param instr buffer of callerid stream (in audio form) to be parsed. Warning, data in buffer is changed.
+ * \param name address of a pointer-to-char for the name value of the stream.
+ * \param location address of a pointer-to-char for the phone number value of the stream.
+ * \return Returns 0 on success, -1 on failure.
+ */
+int ast_callerid_parse(char *instr, char **name, char **location);
+
+/*! Generate a CAS (CPE Alert Signal) tone for 'n' samples */
+/*!
+ * \param outbuf Allocated buffer for data. Must be at least 2400 bytes unless no SAS is desired
+ * \param sas Non-zero if CAS should be preceeded by SAS
+ * \param len How many samples to generate.
+ * \param codec Which codec (AST_FORMAT_ALAW or AST_FORMAT_ULAW)
+ * \return Returns -1 on error (if len is less than 2400), 0 on success.
+ */
+int ast_gen_cas(unsigned char *outbuf, int sas, int len, int codec);
+
+/*! \brief Shrink a phone number in place to just digits (more accurately it just removes ()'s, .'s, and -'s... */
+/*!
+ * \param n The number to be stripped/shrunk
+ * \return Returns nothing important
+ */
+void ast_shrink_phone_number(char *n);
+
+/*! \brief Check if a string consists only of digits and + \#
+ \param n number to be checked.
+ \return Returns 0 if n is a number, 1 if it's not.
+ */
+int ast_isphonenumber(const char *n);
+
+/*! \brief Check if a string consists only of digits and and + \# ( ) - .
+ (meaning it can be cleaned with ast_shrink_phone_number)
+ \param exten The extension (or URI) to be checked.
+ \return Returns 0 if n is a number, 1 if it's not.
+ */
+int ast_is_shrinkable_phonenumber(const char *exten);
+
+int ast_callerid_split(const char *src, char *name, int namelen, char *num, int numlen);
+
+char *ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown);
+
+/*
+ * Caller*ID and other GR-30 compatible generation
+ * routines (used by ADSI for example)
+ */
+
+extern float cid_dr[4];
+extern float cid_di[4];
+extern float clidsb;
+
+static inline float callerid_getcarrier(float *cr, float *ci, int bit)
+{
+ /* Move along. There's nothing to see here... */
+ float t;
+ t = *cr * cid_dr[bit] - *ci * cid_di[bit];
+ *ci = *cr * cid_di[bit] + *ci * cid_dr[bit];
+ *cr = t;
+
+ t = 2.0 - (*cr * *cr + *ci * *ci);
+ *cr *= t;
+ *ci *= t;
+ return *cr;
+}
+
+#define PUT_BYTE(a) do { \
+ *(buf++) = (a); \
+ bytes++; \
+} while(0)
+
+#define PUT_AUDIO_SAMPLE(y) do { \
+ int index = (short)(rint(8192.0 * (y))); \
+ *(buf++) = AST_LIN2X(index); \
+ bytes++; \
+} while(0)
+
+#define PUT_CLID_MARKMS do { \
+ int x; \
+ for (x=0;x<8;x++) \
+ PUT_AUDIO_SAMPLE(callerid_getcarrier(&cr, &ci, 1)); \
+} while(0)
+
+#define PUT_CLID_BAUD(bit) do { \
+ while(scont < clidsb) { \
+ PUT_AUDIO_SAMPLE(callerid_getcarrier(&cr, &ci, bit)); \
+ scont += 1.0; \
+ } \
+ scont -= clidsb; \
+} while(0)
+
+
+#define PUT_CLID(byte) do { \
+ int z; \
+ unsigned char b = (byte); \
+ PUT_CLID_BAUD(0); /* Start bit */ \
+ for (z=0;z<8;z++) { \
+ PUT_CLID_BAUD(b & 1); \
+ b >>= 1; \
+ } \
+ PUT_CLID_BAUD(1); /* Stop bit */ \
+} while(0)
+
+/* Various defines and bits for handling PRI- and SS7-type restriction */
+
+#define AST_PRES_NUMBER_TYPE 0x03
+#define AST_PRES_USER_NUMBER_UNSCREENED 0x00
+#define AST_PRES_USER_NUMBER_PASSED_SCREEN 0x01
+#define AST_PRES_USER_NUMBER_FAILED_SCREEN 0x02
+#define AST_PRES_NETWORK_NUMBER 0x03
+
+#define AST_PRES_RESTRICTION 0x60
+#define AST_PRES_ALLOWED 0x00
+#define AST_PRES_RESTRICTED 0x20
+#define AST_PRES_UNAVAILABLE 0x40
+#define AST_PRES_RESERVED 0x60
+
+#define AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED \
+ AST_PRES_USER_NUMBER_UNSCREENED + AST_PRES_ALLOWED
+
+#define AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN \
+ AST_PRES_USER_NUMBER_PASSED_SCREEN + AST_PRES_ALLOWED
+
+#define AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN \
+ AST_PRES_USER_NUMBER_FAILED_SCREEN + AST_PRES_ALLOWED
+
+#define AST_PRES_ALLOWED_NETWORK_NUMBER \
+ AST_PRES_NETWORK_NUMBER + AST_PRES_ALLOWED
+
+#define AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED \
+ AST_PRES_USER_NUMBER_UNSCREENED + AST_PRES_RESTRICTED
+
+#define AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN \
+ AST_PRES_USER_NUMBER_PASSED_SCREEN + AST_PRES_RESTRICTED
+
+#define AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN \
+ AST_PRES_USER_NUMBER_FAILED_SCREEN + AST_PRES_RESTRICTED
+
+#define AST_PRES_PROHIB_NETWORK_NUMBER \
+ AST_PRES_NETWORK_NUMBER + AST_PRES_RESTRICTED
+
+#define AST_PRES_NUMBER_NOT_AVAILABLE \
+ AST_PRES_NETWORK_NUMBER + AST_PRES_UNAVAILABLE
+
+int ast_parse_caller_presentation(const char *data);
+const char *ast_describe_caller_presentation(int data);
+const char *ast_named_caller_presentation(int data);
+
+/*! \page Def_CallerPres Caller ID Presentation
+
+ Caller ID presentation values are used to set properties to a
+ caller ID in PSTN networks, and as RPID value in SIP transactions.
+
+ The following values are available to use:
+ \arg \b Defined value, text string in config file, explanation
+
+ \arg \b AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "allowed_not_screened", Presentation Allowed, Not Screened,
+ \arg \b AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen", Presentation Allowed, Passed Screen,
+ \arg \b AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen", Presentation Allowed, Failed Screen,
+ \arg \b AST_PRES_ALLOWED_NETWORK_NUMBER, "allowed", Presentation Allowed, Network Number,
+ \arg \b AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "prohib_not_screened", Presentation Prohibited, Not Screened,
+ \arg \b AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen", Presentation Prohibited, Passed Screen,
+ \arg \b AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen", Presentation Prohibited, Failed Screen,
+ \arg \b AST_PRES_PROHIB_NETWORK_NUMBER, "prohib", Presentation Prohibited, Network Number,
+
+ \par References
+ \arg \ref callerid.h Definitions
+ \arg \ref callerid.c Functions
+ \arg \ref CID Caller ID names and numbers
+*/
+
+
+#endif /* _ASTERISK_CALLERID_H */
diff --git a/trunk/include/asterisk/causes.h b/trunk/include/asterisk/causes.h
new file mode 100644
index 000000000..fb4f3c115
--- /dev/null
+++ b/trunk/include/asterisk/causes.h
@@ -0,0 +1,149 @@
+/*
+ * 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 Internal Asterisk hangup causes
+ */
+
+#ifndef _ASTERISK_CAUSES_H
+#define _ASTERISK_CAUSES_H
+
+/*! \page AstCauses Hangup Causes for Asterisk
+
+The Asterisk hangup causes are delivered to the dialplan in the
+${HANGUPCAUSE} channel variable after a call (after execution
+of "dial").
+
+In SIP, we have a conversion table to convert between SIP
+return codes and Q.931 both ways. This is to improve SIP/ISDN
+compatibility.
+
+These are the current codes, based on the Q.931
+specification:
+
+ - AST_CAUSE_UNALLOCATED 1
+ - AST_CAUSE_NO_ROUTE_TRANSIT_NET 2
+ - AST_CAUSE_NO_ROUTE_DESTINATION 3
+ - AST_CAUSE_CHANNEL_UNACCEPTABLE 6
+ - AST_CAUSE_CALL_AWARDED_DELIVERED 7
+ - AST_CAUSE_NORMAL_CLEARING 16
+ - AST_CAUSE_USER_BUSY 17
+ - AST_CAUSE_NO_USER_RESPONSE 18
+ - AST_CAUSE_NO_ANSWER 19
+ - AST_CAUSE_CALL_REJECTED 21
+ - AST_CAUSE_NUMBER_CHANGED 22
+ - AST_CAUSE_DESTINATION_OUT_OF_ORDER 27
+ - AST_CAUSE_INVALID_NUMBER_FORMAT 28
+ - AST_CAUSE_FACILITY_REJECTED 29
+ - AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY 30
+ - AST_CAUSE_NORMAL_UNSPECIFIED 31
+ - AST_CAUSE_NORMAL_CIRCUIT_CONGESTION 34
+ - AST_CAUSE_NETWORK_OUT_OF_ORDER 38
+ - AST_CAUSE_NORMAL_TEMPORARY_FAILURE 41
+ - AST_CAUSE_SWITCH_CONGESTION 42
+ - AST_CAUSE_ACCESS_INFO_DISCARDED 43
+ - AST_CAUSE_REQUESTED_CHAN_UNAVAIL 44
+ - AST_CAUSE_PRE_EMPTED 45
+ - AST_CAUSE_FACILITY_NOT_SUBSCRIBED 50
+ - AST_CAUSE_OUTGOING_CALL_BARRED 52
+ - AST_CAUSE_INCOMING_CALL_BARRED 54
+ - AST_CAUSE_BEARERCAPABILITY_NOTAUTH 57
+ - AST_CAUSE_BEARERCAPABILITY_NOTAVAIL 58
+ - AST_CAUSE_BEARERCAPABILITY_NOTIMPL 65
+ - AST_CAUSE_CHAN_NOT_IMPLEMENTED 66
+ - AST_CAUSE_FACILITY_NOT_IMPLEMENTED 69
+ - AST_CAUSE_INVALID_CALL_REFERENCE 81
+ - AST_CAUSE_INCOMPATIBLE_DESTINATION 88
+ - AST_CAUSE_INVALID_MSG_UNSPECIFIED 95
+ - AST_CAUSE_MANDATORY_IE_MISSING 96
+ - AST_CAUSE_MESSAGE_TYPE_NONEXIST 97
+ - AST_CAUSE_WRONG_MESSAGE 98
+ - AST_CAUSE_IE_NONEXIST 99
+ - AST_CAUSE_INVALID_IE_CONTENTS 100
+ - AST_CAUSE_WRONG_CALL_STATE 101
+ - AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE 102
+ - AST_CAUSE_MANDATORY_IE_LENGTH_ERROR 103
+ - AST_CAUSE_PROTOCOL_ERROR 111
+ - AST_CAUSE_INTERWORKING 127
+
+For more information:
+- \ref app_dial.c
+*/
+
+/*! \name Causes for disconnection (from Q.931)
+ These are the internal cause codes used in Asterisk.
+ \ref AstCauses
+*/
+/*@{ */
+#define AST_CAUSE_UNALLOCATED 1
+#define AST_CAUSE_NO_ROUTE_TRANSIT_NET 2
+#define AST_CAUSE_NO_ROUTE_DESTINATION 3
+#define AST_CAUSE_CHANNEL_UNACCEPTABLE 6
+#define AST_CAUSE_CALL_AWARDED_DELIVERED 7
+#define AST_CAUSE_NORMAL_CLEARING 16
+#define AST_CAUSE_USER_BUSY 17
+#define AST_CAUSE_NO_USER_RESPONSE 18
+#define AST_CAUSE_NO_ANSWER 19
+#define AST_CAUSE_CALL_REJECTED 21
+#define AST_CAUSE_NUMBER_CHANGED 22
+#define AST_CAUSE_DESTINATION_OUT_OF_ORDER 27
+#define AST_CAUSE_INVALID_NUMBER_FORMAT 28
+#define AST_CAUSE_FACILITY_REJECTED 29
+#define AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY 30
+#define AST_CAUSE_NORMAL_UNSPECIFIED 31
+#define AST_CAUSE_NORMAL_CIRCUIT_CONGESTION 34
+#define AST_CAUSE_NETWORK_OUT_OF_ORDER 38
+#define AST_CAUSE_NORMAL_TEMPORARY_FAILURE 41
+#define AST_CAUSE_SWITCH_CONGESTION 42
+#define AST_CAUSE_ACCESS_INFO_DISCARDED 43
+#define AST_CAUSE_REQUESTED_CHAN_UNAVAIL 44
+#define AST_CAUSE_PRE_EMPTED 45
+#define AST_CAUSE_FACILITY_NOT_SUBSCRIBED 50
+#define AST_CAUSE_OUTGOING_CALL_BARRED 52
+#define AST_CAUSE_INCOMING_CALL_BARRED 54
+#define AST_CAUSE_BEARERCAPABILITY_NOTAUTH 57
+#define AST_CAUSE_BEARERCAPABILITY_NOTAVAIL 58
+#define AST_CAUSE_BEARERCAPABILITY_NOTIMPL 65
+#define AST_CAUSE_CHAN_NOT_IMPLEMENTED 66
+#define AST_CAUSE_FACILITY_NOT_IMPLEMENTED 69
+#define AST_CAUSE_INVALID_CALL_REFERENCE 81
+#define AST_CAUSE_INCOMPATIBLE_DESTINATION 88
+#define AST_CAUSE_INVALID_MSG_UNSPECIFIED 95
+#define AST_CAUSE_MANDATORY_IE_MISSING 96
+#define AST_CAUSE_MESSAGE_TYPE_NONEXIST 97
+#define AST_CAUSE_WRONG_MESSAGE 98
+#define AST_CAUSE_IE_NONEXIST 99
+#define AST_CAUSE_INVALID_IE_CONTENTS 100
+#define AST_CAUSE_WRONG_CALL_STATE 101
+#define AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE 102
+#define AST_CAUSE_MANDATORY_IE_LENGTH_ERROR 103
+#define AST_CAUSE_PROTOCOL_ERROR 111
+#define AST_CAUSE_INTERWORKING 127
+
+/* Special Asterisk aliases */
+#define AST_CAUSE_BUSY AST_CAUSE_USER_BUSY
+#define AST_CAUSE_FAILURE AST_CAUSE_NETWORK_OUT_OF_ORDER
+#define AST_CAUSE_NORMAL AST_CAUSE_NORMAL_CLEARING
+#define AST_CAUSE_NOANSWER AST_CAUSE_NO_ANSWER
+#define AST_CAUSE_CONGESTION AST_CAUSE_NORMAL_CIRCUIT_CONGESTION
+#define AST_CAUSE_UNREGISTERED AST_CAUSE_NO_ROUTE_DESTINATION
+#define AST_CAUSE_NOTDEFINED 0
+#define AST_CAUSE_NOSUCHDRIVER AST_CAUSE_CHAN_NOT_IMPLEMENTED
+/*@{ */
+
+#endif /* _ASTERISK_CAUSES_H */
diff --git a/trunk/include/asterisk/cdr.h b/trunk/include/asterisk/cdr.h
new file mode 100644
index 000000000..96a5dc497
--- /dev/null
+++ b/trunk/include/asterisk/cdr.h
@@ -0,0 +1,337 @@
+/*
+ * 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 Call Detail Record API
+ */
+
+#ifndef _ASTERISK_CDR_H
+#define _ASTERISK_CDR_H
+
+#include <sys/time.h>
+#define AST_CDR_FLAG_KEEP_VARS (1 << 0)
+#define AST_CDR_FLAG_POSTED (1 << 1)
+#define AST_CDR_FLAG_LOCKED (1 << 2)
+#define AST_CDR_FLAG_CHILD (1 << 3)
+#define AST_CDR_FLAG_POST_DISABLED (1 << 4)
+
+/*! \name CDR Flags */
+/*@{ */
+#define AST_CDR_NULL 0
+#define AST_CDR_FAILED (1 << 0)
+#define AST_CDR_BUSY (1 << 1)
+#define AST_CDR_NOANSWER (1 << 2)
+#define AST_CDR_ANSWERED (1 << 3)
+/*@} */
+
+/*! \name CDR AMA Flags */
+/*@{ */
+#define AST_CDR_OMIT (1)
+#define AST_CDR_BILLING (2)
+#define AST_CDR_DOCUMENTATION (3)
+/*@} */
+
+#define AST_MAX_USER_FIELD 256
+#define AST_MAX_ACCOUNT_CODE 20
+
+/* Include channel.h after relevant declarations it will need */
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+
+/*! \brief Responsible for call detail data */
+struct ast_cdr {
+ /*! Caller*ID with text */
+ char clid[AST_MAX_EXTENSION];
+ /*! Caller*ID number */
+ char src[AST_MAX_EXTENSION];
+ /*! Destination extension */
+ char dst[AST_MAX_EXTENSION];
+ /*! Destination context */
+ char dcontext[AST_MAX_EXTENSION];
+
+ char channel[AST_MAX_EXTENSION];
+ /*! Destination channel if appropriate */
+ char dstchannel[AST_MAX_EXTENSION];
+ /*! Last application if appropriate */
+ char lastapp[AST_MAX_EXTENSION];
+ /*! Last application data */
+ char lastdata[AST_MAX_EXTENSION];
+
+ struct timeval start;
+
+ struct timeval answer;
+
+ struct timeval end;
+ /*! Total time in system, in seconds */
+ long int duration;
+ /*! Total time call is up, in seconds */
+ long int billsec;
+ /*! What happened to the call */
+ long int disposition;
+ /*! What flags to use */
+ long int amaflags;
+ /*! What account number to use */
+ char accountcode[AST_MAX_ACCOUNT_CODE];
+ /*! flags */
+ unsigned int flags;
+ /*! Unique Channel Identifier */
+ char uniqueid[32];
+ /*! User field */
+ char userfield[AST_MAX_USER_FIELD];
+
+ /*! A linked list for variables */
+ struct varshead varshead;
+
+ struct ast_cdr *next;
+};
+
+void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw);
+int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur);
+int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur);
+void ast_cdr_free_vars(struct ast_cdr *cdr, int recur);
+int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr);
+int ast_cdr_log_unanswered(void);
+
+typedef int (*ast_cdrbe)(struct ast_cdr *cdr);
+
+/*! \brief Return TRUE if CDR subsystem is enabled */
+int check_cdr_enabled(void);
+
+/*!
+ * \brief Allocate a CDR record
+ * \retval a malloc'd ast_cdr structure
+ * \retval NULL on error (malloc failure)
+ */
+struct ast_cdr *ast_cdr_alloc(void);
+
+/*!
+ * \brief Duplicate a record
+ * \retval a malloc'd ast_cdr structure,
+ * \retval NULL on error (malloc failure)
+ */
+struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr);
+
+/*!
+ * \brief Free a CDR record
+ * \param cdr ast_cdr structure to free
+ * Returns nothing
+ */
+void ast_cdr_free(struct ast_cdr *cdr);
+
+/*!
+ * \brief Discard and free a CDR record
+ * \param cdr ast_cdr structure to free
+ * Returns nothing -- same as free, but no checks or complaints
+ */
+void ast_cdr_discard(struct ast_cdr *cdr);
+
+/*!
+ * \brief Initialize based on a channel
+ * \param cdr Call Detail Record to use for channel
+ * \param chan Channel to bind CDR with
+ * Initializes a CDR and associates it with a particular channel
+ * \return 0 by default
+ */
+int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *chan);
+
+/*!
+ * \brief Initialize based on a channel
+ * \param cdr Call Detail Record to use for channel
+ * \param chan Channel to bind CDR with
+ * Initializes a CDR and associates it with a particular channel
+ * \return 0 by default
+ */
+int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *chan);
+
+/*!
+ * \brief Register a CDR handling engine
+ * \param name name associated with the particular CDR handler
+ * \param desc description of the CDR handler
+ * \param be function pointer to a CDR handler
+ * Used to register a Call Detail Record handler.
+ * \retval 0 on success.
+ * \retval -1 on error
+ */
+int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be);
+
+/*!
+ * \brief Unregister a CDR handling engine
+ * \param name name of CDR handler to unregister
+ * Unregisters a CDR by it's name
+ */
+void ast_cdr_unregister(const char *name);
+
+/*!
+ * \brief Start a call
+ * \param cdr the cdr you wish to associate with the call
+ * Starts all CDR stuff necessary for monitoring a call
+ * Returns nothing
+ */
+void ast_cdr_start(struct ast_cdr *cdr);
+
+/*! \brief Answer a call
+ * \param cdr the cdr you wish to associate with the call
+ * Starts all CDR stuff necessary for doing CDR when answering a call
+ * \note NULL argument is just fine.
+ */
+void ast_cdr_answer(struct ast_cdr *cdr);
+
+/*!
+ * \brief A call wasn't answered
+ * \param cdr the cdr you wish to associate with the call
+ * Marks the channel disposition as "NO ANSWER"
+ */
+extern void ast_cdr_noanswer(struct ast_cdr *cdr);
+
+/*!
+ * \brief Busy a call
+ * \param cdr the cdr you wish to associate with the call
+ * Returns nothing
+ */
+void ast_cdr_busy(struct ast_cdr *cdr);
+
+/*!
+ * \brief Fail a call
+ * \param cdr the cdr you wish to associate with the call
+ * Returns nothing
+ */
+void ast_cdr_failed(struct ast_cdr *cdr);
+
+/*!
+ * \brief Save the result of the call based on the AST_CAUSE_*
+ * \param cdr the cdr you wish to associate with the call
+ * \param cause the AST_CAUSE_*
+ * Returns nothing
+ */
+int ast_cdr_disposition(struct ast_cdr *cdr, int cause);
+
+/*!
+ * \brief End a call
+ * \param cdr the cdr you have associated the call with
+ * Registers the end of call time in the cdr structure.
+ * Returns nothing
+ */
+void ast_cdr_end(struct ast_cdr *cdr);
+
+/*!
+ * \brief Detaches the detail record for posting (and freeing) either now or at a
+ * later time in bulk with other records during batch mode operation.
+ * \param cdr Which CDR to detach from the channel thread
+ * Prevents the channel thread from blocking on the CDR handling
+ * Returns nothing
+ */
+void ast_cdr_detach(struct ast_cdr *cdr);
+
+/*!
+ * \brief Spawns (possibly) a new thread to submit a batch of CDRs to the backend engines
+ * \param shutdown Whether or not we are shutting down
+ * Blocks the asterisk shutdown procedures until the CDR data is submitted.
+ * Returns nothing
+ */
+void ast_cdr_submit_batch(int shutdown);
+
+/*!
+ * \brief Set the destination channel, if there was one
+ * \param cdr Which cdr it's applied to
+ * \param chan Channel to which dest will be
+ * Sets the destination channel the CDR is applied to
+ * Returns nothing
+ */
+void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chan);
+
+/*!
+ * \brief Set the last executed application
+ * \param cdr which cdr to act upon
+ * \param app the name of the app you wish to change it to
+ * \param data the data you want in the data field of app you set it to
+ * Changes the value of the last executed app
+ * Returns nothing
+ */
+void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data);
+
+/*!
+ * \brief Convert a string to a detail record AMA flag
+ * \param flag string form of flag
+ * Converts the string form of the flag to the binary form.
+ * \return the binary form of the flag
+ */
+int ast_cdr_amaflags2int(const char *flag);
+
+/*!
+ * \brief Disposition to a string
+ * \param disposition input binary form
+ * Converts the binary form of a disposition to string form.
+ * \return a pointer to the string form
+ */
+char *ast_cdr_disp2str(int disposition);
+
+/*!
+ * \brief Reset the detail record, optionally posting it first
+ * \param cdr which cdr to act upon
+ * \param flags |AST_CDR_FLAG_POSTED whether or not to post the cdr first before resetting it
+ * |AST_CDR_FLAG_LOCKED whether or not to reset locked CDR's
+ */
+void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *flags);
+
+/*!
+ * \brief Flags to a string
+ * \param flags binary flag
+ * Converts binary flags to string flags
+ * Returns string with flag name
+ */
+char *ast_cdr_flags2str(int flags);
+
+/*!
+ * \brief Move the non-null data from the "from" cdr to the "to" cdr
+ * \param to the cdr to get the goodies
+ * \param from the cdr to give the goodies
+ */
+void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from);
+
+/*! \brief Set account code, will generate AMI event */
+int ast_cdr_setaccount(struct ast_channel *chan, const char *account);
+
+/*! \brief Set AMA flags for channel */
+int ast_cdr_setamaflags(struct ast_channel *chan, const char *amaflags);
+
+/*! \brief Set CDR user field for channel (stored in CDR) */
+int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield);
+/*! \brief Append to CDR user field for channel (stored in CDR) */
+int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield);
+
+
+/*! Update CDR on a channel */
+int ast_cdr_update(struct ast_channel *chan);
+
+
+extern int ast_default_amaflags;
+
+extern char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
+
+struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr);
+
+/*! \brief Reload the configuration file cdr.conf and start/stop CDR scheduling thread */
+int ast_cdr_engine_reload(void);
+
+/*! \brief Load the configuration file cdr.conf and possibly start the CDR scheduling thread */
+int ast_cdr_engine_init(void);
+
+/*! Submit any remaining CDRs and prepare for shutdown */
+void ast_cdr_engine_term(void);
+
+#endif /* _ASTERISK_CDR_H */
diff --git a/trunk/include/asterisk/channel.h b/trunk/include/asterisk/channel.h
new file mode 100644
index 000000000..efb87fde6
--- /dev/null
+++ b/trunk/include/asterisk/channel.h
@@ -0,0 +1,1577 @@
+/*
+ * 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 General Asterisk PBX channel definitions.
+ * \par See also:
+ * \arg \ref Def_Channel
+ * \arg \ref channel_drivers
+ */
+
+/*! \page Def_Channel Asterisk Channels
+ \par What is a Channel?
+ A phone call through Asterisk consists of an incoming
+ connection and an outbound connection. Each call comes
+ in through a channel driver that supports one technology,
+ like SIP, ZAP, IAX2 etc.
+ \par
+ Each channel driver, technology, has it's own private
+ channel or dialog structure, that is technology-dependent.
+ Each private structure is "owned" by a generic Asterisk
+ channel structure, defined in channel.h and handled by
+ channel.c .
+ \par Call scenario
+ This happens when an incoming call arrives to Asterisk
+ -# Call arrives on a channel driver interface
+ -# Channel driver creates a PBX channel and starts a
+ pbx thread on the channel
+ -# The dial plan is executed
+ -# At this point at least two things can happen:
+ -# The call is answered by Asterisk and
+ Asterisk plays a media stream or reads media
+ -# The dial plan forces Asterisk to create an outbound
+ call somewhere with the dial (see \ref app_dial.c)
+ application
+ .
+
+ \par Bridging channels
+ If Asterisk dials out this happens:
+ -# Dial creates an outbound PBX channel and asks one of the
+ channel drivers to create a call
+ -# When the call is answered, Asterisk bridges the media streams
+ so the caller on the first channel can speak with the callee
+ on the second, outbound channel
+ -# In some cases where we have the same technology on both
+ channels and compatible codecs, a native bridge is used.
+ In a native bridge, the channel driver handles forwarding
+ of incoming audio to the outbound stream internally, without
+ sending audio frames through the PBX.
+ -# In SIP, theres an "external native bridge" where Asterisk
+ redirects the endpoint, so audio flows directly between the
+ caller's phone and the callee's phone. Signalling stays in
+ Asterisk in order to be able to provide a proper CDR record
+ for the call.
+
+
+ \par Masquerading channels
+ In some cases, a channel can masquerade itself into another
+ channel. This happens frequently in call transfers, where
+ a new channel takes over a channel that is already involved
+ in a call. The new channel sneaks in and takes over the bridge
+ and the old channel, now a zombie, is hung up.
+
+ \par Reference
+ \arg channel.c - generic functions
+ \arg channel.h - declarations of functions, flags and structures
+ \arg translate.h - Transcoding support functions
+ \arg \ref channel_drivers - Implemented channel drivers
+ \arg \ref Def_Frame Asterisk Multimedia Frames
+ \arg \ref Def_Bridge
+
+*/
+/*! \page Def_Bridge Asterisk Channel Bridges
+
+ In Asterisk, there's several media bridges.
+
+ The Core bridge handles two channels (a "phone call") and bridge
+ them together.
+
+ The conference bridge (meetme) handles several channels simultaneously
+ with the support of an external timer (zaptel timer). This is used
+ not only by the Conference application (meetme) but also by the
+ page application and the SLA system introduced in 1.4.
+ The conference bridge does not handle video.
+
+ When two channels of the same type connect, the channel driver
+ or the media subsystem used by the channel driver (i.e. RTP)
+ can create a native bridge without sending media through the
+ core.
+
+ Native briding can be disabled by a number of reasons,
+ like DTMF being needed by the core or codecs being incompatible
+ so a transcoding module is needed.
+
+References:
+ \li \see ast_channel_early_bridge()
+ \li \see ast_channel_bridge()
+ \li \see app_meetme.c
+ \li \ref AstRTPbridge
+ \li \see ast_rtp_bridge()
+ \li \ref Def_Channel
+*/
+
+/*! \page AstFileDesc File descriptors
+ Asterisk File descriptors are connected to each channel (see \ref Def_Channel)
+ in the \ref ast_channel structure.
+*/
+
+#ifndef _ASTERISK_CHANNEL_H
+#define _ASTERISK_CHANNEL_H
+
+#include "asterisk/abstract_jb.h"
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include "asterisk/poll-compat.h"
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define AST_MAX_EXTENSION 80 /*!< Max length of an extension */
+#define AST_MAX_CONTEXT 80 /*!< Max length of a context */
+#define AST_CHANNEL_NAME 80 /*!< Max length of an ast_channel name */
+#define MAX_LANGUAGE 20 /*!< Max length of the language setting */
+#define MAX_MUSICCLASS 80 /*!< Max length of the music class setting */
+
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/cdr.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/stringfields.h"
+
+#define DATASTORE_INHERIT_FOREVER INT_MAX
+
+#define AST_MAX_FDS 10
+/*
+ * We have AST_MAX_FDS file descriptors in a channel.
+ * Some of them have a fixed use:
+ */
+#define AST_ALERT_FD (AST_MAX_FDS-1) /*!< used for alertpipe */
+#define AST_TIMING_FD (AST_MAX_FDS-2) /*!< used for timingfd */
+#define AST_AGENT_FD (AST_MAX_FDS-3) /*!< used by agents for pass through */
+#define AST_GENERATOR_FD (AST_MAX_FDS-4) /*!< used by generator */
+
+enum ast_bridge_result {
+ AST_BRIDGE_COMPLETE = 0,
+ AST_BRIDGE_FAILED = -1,
+ AST_BRIDGE_FAILED_NOWARN = -2,
+ AST_BRIDGE_RETRY = -3,
+};
+
+typedef unsigned long long ast_group_t;
+
+/*! \todo Add an explanation of an Asterisk generator
+*/
+struct ast_generator {
+ void *(*alloc)(struct ast_channel *chan, void *params);
+ void (*release)(struct ast_channel *chan, void *data);
+ /*! This function gets called with the channel locked */
+ int (*generate)(struct ast_channel *chan, void *data, int len, int samples);
+ /*! This gets called when DTMF_END frames are read from the channel */
+ void (*digit)(struct ast_channel *chan, char digit);
+};
+
+/*! \brief Structure for a data store type */
+struct ast_datastore_info {
+ const char *type; /*!< Type of data store */
+ void *(*duplicate)(void *data); /*!< Duplicate item data (used for inheritance) */
+ void (*destroy)(void *data); /*!< Destroy function */
+};
+
+/*! \brief Structure for a channel data store */
+struct ast_datastore {
+ const char *uid; /*!< Unique data store identifier */
+ void *data; /*!< Contained data */
+ const struct ast_datastore_info *info; /*!< Data store type information */
+ unsigned int inheritance; /*!< Number of levels this item will continue to be inherited */
+ AST_LIST_ENTRY(ast_datastore) entry; /*!< Used for easy linking */
+};
+
+/*! \brief Structure for all kinds of caller ID identifications.
+ * \note All string fields here are malloc'ed, so they need to be
+ * freed when the structure is deleted.
+ * Also, NULL and "" must be considered equivalent.
+ *
+ * SIP and IAX2 has utf8 encoded Unicode caller ID names.
+ * In some cases, we also have an alternative (RPID) E.164 number that can be used
+ * as caller ID on numeric E.164 phone networks (zaptel or SIP/IAX2 to pstn gateway).
+ *
+ * \todo Implement settings for transliteration between UTF8 caller ID names in
+ * to Ascii Caller ID's (Zaptel). Östen Åsklund might be transliterated into
+ * Osten Asklund or Oesten Aasklund depending upon language and person...
+ * We need automatic routines for incoming calls and static settings for
+ * our own accounts.
+ */
+struct ast_callerid {
+ char *cid_dnid; /*!< Malloc'd Dialed Number Identifier */
+ char *cid_num; /*!< Malloc'd Caller Number */
+ char *cid_name; /*!< Malloc'd Caller Name (ASCII) */
+ char *cid_ani; /*!< Malloc'd ANI */
+ char *cid_rdnis; /*!< Malloc'd RDNIS */
+ int cid_pres; /*!< Callerid presentation/screening */
+ int cid_ani2; /*!< Callerid ANI 2 (Info digits) */
+ int cid_ton; /*!< Callerid Type of Number */
+ int cid_tns; /*!< Callerid Transit Network Select */
+};
+
+/*! \brief
+ Structure to describe a channel "technology", ie a channel driver
+ See for examples:
+ \arg chan_iax2.c - The Inter-Asterisk exchange protocol
+ \arg chan_sip.c - The SIP channel driver
+ \arg chan_zap.c - PSTN connectivity (TDM, PRI, T1/E1, FXO, FXS)
+
+ If you develop your own channel driver, this is where you
+ tell the PBX at registration of your driver what properties
+ this driver supports and where different callbacks are
+ implemented.
+*/
+struct ast_channel_tech {
+ const char * const type;
+ const char * const description;
+
+ int capabilities; /*!< Bitmap of formats this channel can handle */
+
+ int properties; /*!< Technology Properties */
+
+ /*! \brief Requester - to set up call data structures (pvt's) */
+ struct ast_channel *(* const requester)(const char *type, int format, void *data, int *cause);
+
+ int (* const devicestate)(void *data); /*!< Devicestate call back */
+
+ /*!
+ * \brief Start sending a literal DTMF digit
+ *
+ * \note The channel is not locked when this function gets called.
+ */
+ int (* const send_digit_begin)(struct ast_channel *chan, char digit);
+
+ /*!
+ * \brief Stop sending a literal DTMF digit
+ *
+ * \note The channel is not locked when this function gets called.
+ */
+ int (* const send_digit_end)(struct ast_channel *chan, char digit, unsigned int duration);
+
+ /*! \brief Call a given phone number (address, etc), but don't
+ take longer than timeout seconds to do so. */
+ int (* const call)(struct ast_channel *chan, char *addr, int timeout);
+
+ /*! \brief Hangup (and possibly destroy) the channel */
+ int (* const hangup)(struct ast_channel *chan);
+
+ /*! \brief Answer the channel */
+ int (* const answer)(struct ast_channel *chan);
+
+ /*! \brief Read a frame, in standard format (see frame.h) */
+ struct ast_frame * (* const read)(struct ast_channel *chan);
+
+ /*! \brief Write a frame, in standard format (see frame.h) */
+ int (* const write)(struct ast_channel *chan, struct ast_frame *frame);
+
+ /*! \brief Display or transmit text */
+ int (* const send_text)(struct ast_channel *chan, const char *text);
+
+ /*! \brief Display or send an image */
+ int (* const send_image)(struct ast_channel *chan, struct ast_frame *frame);
+
+ /*! \brief Send HTML data */
+ int (* const send_html)(struct ast_channel *chan, int subclass, const char *data, int len);
+
+ /*! \brief Handle an exception, reading a frame */
+ struct ast_frame * (* const exception)(struct ast_channel *chan);
+
+ /*! \brief Bridge two channels of the same type together */
+ enum ast_bridge_result (* const bridge)(struct ast_channel *c0, struct ast_channel *c1, int flags,
+ struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
+
+ /*! \brief Bridge two channels of the same type together (early) */
+ enum ast_bridge_result (* const early_bridge)(struct ast_channel *c0, struct ast_channel *c1);
+
+ /*! \brief Indicate a particular condition (e.g. AST_CONTROL_BUSY or AST_CONTROL_RINGING or AST_CONTROL_CONGESTION */
+ int (* const indicate)(struct ast_channel *c, int condition, const void *data, size_t datalen);
+
+ /*! \brief Fix up a channel: If a channel is consumed, this is called. Basically update any ->owner links */
+ int (* const fixup)(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+ /*! \brief Set a given option */
+ int (* const setoption)(struct ast_channel *chan, int option, void *data, int datalen);
+
+ /*! \brief Query a given option */
+ int (* const queryoption)(struct ast_channel *chan, int option, void *data, int *datalen);
+
+ /*! \brief Blind transfer other side (see app_transfer.c and ast_transfer() */
+ int (* const transfer)(struct ast_channel *chan, const char *newdest);
+
+ /*! \brief Write a frame, in standard format */
+ int (* const write_video)(struct ast_channel *chan, struct ast_frame *frame);
+
+ /*! \brief Write a text frame, in standard format */
+ int (* const write_text)(struct ast_channel *chan, struct ast_frame *frame);
+
+ /*! \brief Find bridged channel */
+ struct ast_channel *(* const bridged_channel)(struct ast_channel *chan, struct ast_channel *bridge);
+
+ /*! \brief Provide additional read items for CHANNEL() dialplan function */
+ int (* func_channel_read)(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len);
+
+ /*! \brief Provide additional write items for CHANNEL() dialplan function */
+ int (* func_channel_write)(struct ast_channel *chan, const char *function, char *data, const char *value);
+
+ /*! \brief Retrieve base channel (agent and local) */
+ struct ast_channel* (* get_base_channel)(struct ast_channel *chan);
+
+ /*! \brief Set base channel (agent and local) */
+ int (* set_base_channel)(struct ast_channel *chan, struct ast_channel *base);
+};
+
+struct ast_epoll_data;
+
+/*!
+ * The high bit of the frame count is used as a debug marker, so
+ * increments of the counters must be done with care.
+ * Please use c->fin = FRAMECOUNT_INC(c->fin) and the same for c->fout.
+ */
+#define DEBUGCHAN_FLAG 0x80000000
+
+/* XXX not ideal to evaluate x twice... */
+#define FRAMECOUNT_INC(x) ( ((x) & DEBUGCHAN_FLAG) | (((x)+1) & ~DEBUGCHAN_FLAG) )
+
+/*!
+ * The current value of the debug flags is stored in the two
+ * variables global_fin and global_fout (declared in main/channel.c)
+ */
+extern unsigned long global_fin, global_fout;
+
+enum ast_channel_adsicpe {
+ AST_ADSI_UNKNOWN,
+ AST_ADSI_AVAILABLE,
+ AST_ADSI_UNAVAILABLE,
+ AST_ADSI_OFFHOOKONLY,
+};
+
+/*!
+ * \brief ast_channel states
+ *
+ * \note Bits 0-15 of state are reserved for the state (up/down) of the line
+ * Bits 16-32 of state are reserved for flags
+ */
+enum ast_channel_state {
+ AST_STATE_DOWN, /*!< Channel is down and available */
+ AST_STATE_RESERVED, /*!< Channel is down, but reserved */
+ AST_STATE_OFFHOOK, /*!< Channel is off hook */
+ AST_STATE_DIALING, /*!< Digits (or equivalent) have been dialed */
+ AST_STATE_RING, /*!< Line is ringing */
+ AST_STATE_RINGING, /*!< Remote end is ringing */
+ AST_STATE_UP, /*!< Line is up */
+ AST_STATE_BUSY, /*!< Line is busy */
+ AST_STATE_DIALING_OFFHOOK, /*!< Digits (or equivalent) have been dialed while offhook */
+ AST_STATE_PRERING, /*!< Channel has detected an incoming call and is waiting for ring */
+
+ AST_STATE_MUTE = (1 << 16), /*!< Do not transmit voice data */
+};
+
+/*! \brief Main Channel structure associated with a channel.
+ * This is the side of it mostly used by the pbx and call management.
+ *
+ * \note XXX It is important to remember to increment .cleancount each time
+ * this structure is changed. XXX
+ */
+
+struct ast_channel {
+ const struct ast_channel_tech *tech; /*!< Technology (point to channel driver) */
+
+ void *tech_pvt; /*!< Private data used by the technology driver */
+
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name); /*!< ASCII unique channel name */
+ AST_STRING_FIELD(language); /*!< Language requested for voice prompts */
+ AST_STRING_FIELD(musicclass); /*!< Default music class */
+ AST_STRING_FIELD(accountcode); /*!< Account code for billing */
+ AST_STRING_FIELD(call_forward); /*!< Where to forward to if asked to dial on this interface */
+ AST_STRING_FIELD(uniqueid); /*!< Unique Channel Identifier */
+ );
+
+ int fds[AST_MAX_FDS]; /*!< File descriptors for channel -- Drivers will poll on
+ these file descriptors, so at least one must be non -1.
+ See \arg \ref AstFileDesc */
+
+ void *music_state; /*!< Music State*/
+ void *generatordata; /*!< Current generator data if there is any */
+ struct ast_generator *generator; /*!< Current active data generator */
+
+ struct ast_channel *_bridge; /*!< Who are we bridged to, if we're bridged.
+ Who is proxying for us, if we are proxied (i.e. chan_agent).
+ Do not access directly, use ast_bridged_channel(chan) */
+
+ struct ast_channel *masq; /*!< Channel that will masquerade as us */
+ struct ast_channel *masqr; /*!< Who we are masquerading as */
+ int cdrflags; /*!< Call Detail Record Flags */
+
+ int _softhangup; /*!< Whether or not we have been hung up... Do not set this value
+ directly, use ast_softhangup() */
+ time_t whentohangup; /*!< Non-zero, set to actual time when channel is to be hung up */
+ pthread_t blocker; /*!< If anyone is blocking, this is them */
+ ast_mutex_t lock_dont_use; /*!< Lock a channel for some operations. See ast_channel_lock() */
+ const char *blockproc; /*!< Procedure causing blocking */
+
+ const char *appl; /*!< Current application */
+ const char *data; /*!< Data passed to current application */
+ int fdno; /*!< Which fd had an event detected on */
+ struct sched_context *sched; /*!< Schedule context */
+ int streamid; /*!< For streaming playback, the schedule ID */
+ struct ast_filestream *stream; /*!< Stream itself. */
+ int vstreamid; /*!< For streaming video playback, the schedule ID */
+ struct ast_filestream *vstream; /*!< Video Stream itself. */
+ int oldwriteformat; /*!< Original writer format */
+
+ int timingfd; /*!< Timing fd */
+ int (*timingfunc)(const void *data);
+ void *timingdata;
+
+ enum ast_channel_state _state; /*!< State of line -- Don't write directly, use ast_setstate() */
+ int rings; /*!< Number of rings so far */
+ struct ast_callerid cid; /*!< Caller ID, name, presentation etc */
+ char dtmfq[AST_MAX_EXTENSION]; /*!< Any/all queued DTMF characters */
+ struct ast_frame dtmff; /*!< DTMF frame */
+
+ char context[AST_MAX_CONTEXT]; /*!< Dialplan: Current extension context */
+ char exten[AST_MAX_EXTENSION]; /*!< Dialplan: Current extension number */
+ int priority; /*!< Dialplan: Current extension priority */
+ char macrocontext[AST_MAX_CONTEXT]; /*!< Macro: Current non-macro context. See app_macro.c */
+ char macroexten[AST_MAX_EXTENSION]; /*!< Macro: Current non-macro extension. See app_macro.c */
+ int macropriority; /*!< Macro: Current non-macro priority. See app_macro.c */
+ char dialcontext[AST_MAX_CONTEXT]; /*!< Dial: Extension context that we were called from */
+
+ struct ast_pbx *pbx; /*!< PBX private structure for this channel */
+ int amaflags; /*!< Set BEFORE PBX is started to determine AMA flags */
+ struct ast_cdr *cdr; /*!< Call Detail Record */
+ enum ast_channel_adsicpe adsicpe; /*!< Whether or not ADSI is detected on CPE */
+
+ struct ind_tone_zone *zone; /*!< Tone zone as set in indications.conf or
+ in the CHANNEL dialplan function */
+
+ struct ast_channel_monitor *monitor; /*!< Channel monitoring */
+
+ unsigned long insmpl; /*!< Track the read/written samples for monitor use */
+ unsigned long outsmpl; /*!< Track the read/written samples for monitor use */
+
+ unsigned int fin; /*!< Frames in counters. The high bit is a debug mask, so
+ the counter is only in the remaining bits */
+ unsigned int fout; /*!< Frames out counters. The high bit is a debug mask, so
+ the counter is only in the remaining bits */
+ int hangupcause; /*!< Why is the channel hanged up. See causes.h */
+ struct varshead varshead; /*!< A linked list for channel variables. See \ref AstChanVar */
+ ast_group_t callgroup; /*!< Call group for call pickups */
+ ast_group_t pickupgroup; /*!< Pickup group - which calls groups can be picked up? */
+ unsigned int flags; /*!< channel flags of AST_FLAG_ type */
+ unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */
+ AST_LIST_HEAD_NOLOCK(, ast_frame) readq;
+ int alertpipe[2];
+
+ int nativeformats; /*!< Kinds of data this channel can natively handle */
+ int readformat; /*!< Requested read format */
+ int writeformat; /*!< Requested write format */
+ struct ast_trans_pvt *writetrans; /*!< Write translation path */
+ struct ast_trans_pvt *readtrans; /*!< Read translation path */
+ int rawreadformat; /*!< Raw read format */
+ int rawwriteformat; /*!< Raw write format */
+
+ struct ast_audiohook_list *audiohooks;
+
+ AST_LIST_ENTRY(ast_channel) chan_list; /*!< For easy linking */
+
+ struct ast_jb jb; /*!< The jitterbuffer state */
+
+ char emulate_dtmf_digit; /*!< Digit being emulated */
+ unsigned int emulate_dtmf_duration; /*!< Number of ms left to emulate DTMF for */
+ struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */
+
+ AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores; /*!< Data stores on the channel */
+
+#ifdef HAVE_EPOLL
+ int epfd;
+ struct ast_epoll_data *epfd_data[AST_MAX_FDS];
+#endif
+ int visible_indication; /*!< Indication currently playing on the channel */
+};
+
+/*! \brief ast_channel_tech Properties */
+enum {
+ /*! \brief Channels have this property if they can accept input with jitter;
+ * i.e. most VoIP channels */
+ AST_CHAN_TP_WANTSJITTER = (1 << 0),
+ /*! \brief Channels have this property if they can create jitter;
+ * i.e. most VoIP channels */
+ AST_CHAN_TP_CREATESJITTER = (1 << 1),
+};
+
+/*! \brief ast_channel flags */
+enum {
+ /*! Queue incoming dtmf, to be released when this flag is turned off */
+ AST_FLAG_DEFER_DTMF = (1 << 1),
+ /*! write should be interrupt generator */
+ AST_FLAG_WRITE_INT = (1 << 2),
+ /*! a thread is blocking on this channel */
+ AST_FLAG_BLOCKING = (1 << 3),
+ /*! This is a zombie channel */
+ AST_FLAG_ZOMBIE = (1 << 4),
+ /*! There is an exception pending */
+ AST_FLAG_EXCEPTION = (1 << 5),
+ /*! Listening to moh XXX anthm promises me this will disappear XXX */
+ AST_FLAG_MOH = (1 << 6),
+ /*! This channel is spying on another channel */
+ AST_FLAG_SPYING = (1 << 7),
+ /*! This channel is in a native bridge */
+ AST_FLAG_NBRIDGE = (1 << 8),
+ /*! the channel is in an auto-incrementing dialplan processor,
+ * so when ->priority is set, it will get incremented before
+ * finding the next priority to run */
+ AST_FLAG_IN_AUTOLOOP = (1 << 9),
+ /*! This is an outgoing call */
+ AST_FLAG_OUTGOING = (1 << 10),
+ /*! A DTMF_BEGIN frame has been read from this channel, but not yet an END */
+ AST_FLAG_IN_DTMF = (1 << 12),
+ /*! A DTMF_END was received when not IN_DTMF, so the length of the digit is
+ * currently being emulated */
+ AST_FLAG_EMULATE_DTMF = (1 << 13),
+ /*! This is set to tell the channel not to generate DTMF begin frames, and
+ * to instead only generate END frames. */
+ AST_FLAG_END_DTMF_ONLY = (1 << 14),
+ /*! Flag to show channels that this call is hangup due to the fact that the call
+ was indeed anwered, but in another channel */
+ AST_FLAG_ANSWERED_ELSEWHERE = (1 << 15),
+ /*! This flag indicates that on a masquerade, an active stream should not
+ * be carried over */
+ AST_FLAG_MASQ_NOSTREAM = (1 << 16),
+};
+
+/*! \brief ast_bridge_config flags */
+enum {
+ AST_FEATURE_PLAY_WARNING = (1 << 0),
+ AST_FEATURE_REDIRECT = (1 << 1),
+ AST_FEATURE_DISCONNECT = (1 << 2),
+ AST_FEATURE_ATXFER = (1 << 3),
+ AST_FEATURE_AUTOMON = (1 << 4),
+ AST_FEATURE_PARKCALL = (1 << 5),
+ AST_FEATURE_AUTOMIXMON = (1 << 6),
+};
+
+/*! \brief bridge configuration */
+struct ast_bridge_config {
+ struct ast_flags features_caller;
+ struct ast_flags features_callee;
+ struct timeval start_time;
+ long feature_timer;
+ long timelimit;
+ long play_warning;
+ long warning_freq;
+ const char *warning_sound;
+ const char *end_sound;
+ const char *start_sound;
+ int firstpass;
+ unsigned int flags;
+};
+
+struct chanmon;
+
+struct outgoing_helper {
+ const char *context;
+ const char *exten;
+ int priority;
+ const char *cid_num;
+ const char *cid_name;
+ const char *account;
+ struct ast_variable *vars;
+ struct ast_channel *parent_channel;
+};
+
+enum {
+ AST_CDR_TRANSFER = (1 << 0),
+ AST_CDR_FORWARD = (1 << 1),
+ AST_CDR_CALLWAIT = (1 << 2),
+ AST_CDR_CONFERENCE = (1 << 3),
+};
+
+enum {
+ /*! Soft hangup by device */
+ AST_SOFTHANGUP_DEV = (1 << 0),
+ /*! Soft hangup for async goto */
+ AST_SOFTHANGUP_ASYNCGOTO = (1 << 1),
+ AST_SOFTHANGUP_SHUTDOWN = (1 << 2),
+ AST_SOFTHANGUP_TIMEOUT = (1 << 3),
+ AST_SOFTHANGUP_APPUNLOAD = (1 << 4),
+ AST_SOFTHANGUP_EXPLICIT = (1 << 5),
+ AST_SOFTHANGUP_UNBRIDGE = (1 << 6),
+};
+
+
+/*! \brief Channel reload reasons for manager events at load or reload of configuration */
+enum channelreloadreason {
+ CHANNEL_MODULE_LOAD,
+ CHANNEL_MODULE_RELOAD,
+ CHANNEL_CLI_RELOAD,
+ CHANNEL_MANAGER_RELOAD,
+};
+
+/*!
+ * \brief Create a channel datastore structure
+ *
+ * \note None of the datastore API calls lock the ast_channel they are using.
+ * So, the channel should be locked before calling the functions that
+ * take a channel argument.
+ */
+struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, const char *uid);
+
+/*! \brief Free a channel datastore structure */
+int ast_channel_datastore_free(struct ast_datastore *datastore);
+
+/*! \brief Inherit datastores from a parent to a child. */
+int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to);
+
+/*!
+ * \brief Add a datastore to a channel
+ *
+ * \note The channel should be locked before calling this function.
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ */
+
+int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore);
+
+/*!
+ * \brief Remove a datastore from a channel
+ *
+ * \note The channel should be locked before calling this function.
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ */
+int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore);
+
+/*!
+ * \brief Find a datastore on a channel
+ *
+ * \note The channel should be locked before calling this function.
+ *
+ * \note The datastore returned from this function must not be used if the
+ * reference to the channel is released.
+ */
+struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid);
+
+/*! \brief Change the state of a channel */
+int ast_setstate(struct ast_channel *chan, enum ast_channel_state);
+
+/*!
+ * \brief Create a channel structure
+ *
+ * \retval NULL failure
+ * \retval non-NULL successfully allocated channel
+ *
+ * \note By default, new channels are set to the "s" extension
+ * and "default" context.
+ */
+struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...);
+
+/*!
+ * \brief Queue an outgoing frame
+ *
+ * \note The channel does not need to be locked before calling this function.
+ */
+int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f);
+
+/*!
+ * \brief Queue a hangup frame
+ *
+ * \note The channel does not need to be locked before calling this function.
+ */
+int ast_queue_hangup(struct ast_channel *chan);
+
+/*!
+ * \brief Queue a control frame with payload
+ *
+ * \param chan channel to queue frame onto
+ * \param control type of control frame
+ *
+ * \note The channel does not need to be locked before calling this function.
+ *
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type control);
+
+/*!
+ * \brief Queue a control frame with payload
+ *
+ * \param chan channel to queue frame onto
+ * \param control type of control frame
+ * \param data pointer to payload data to be included in frame
+ * \param datalen number of bytes of payload data
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ *
+ * The supplied payload data is copied into the frame, so the caller's copy
+ * is not modified nor freed, and the resulting frame will retain a copy of
+ * the data even if the caller frees their local copy.
+ *
+ * \note This method should be treated as a 'network transport'; in other
+ * words, your frames may be transferred across an IAX2 channel to another
+ * system, which may be a different endianness than yours. Because of this,
+ * you should ensure that either your frames will never be expected to work
+ * across systems, or that you always put your payload data into 'network byte
+ * order' before calling this function.
+ *
+ * \note The channel does not need to be locked before calling this function.
+ */
+int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type control,
+ const void *data, size_t datalen);
+
+/*!
+ * \brief Change channel name
+ *
+ * \note The channel must be locked before calling this function.
+ */
+void ast_change_name(struct ast_channel *chan, char *newname);
+
+/*! \brief Free a channel structure */
+void ast_channel_free(struct ast_channel *);
+
+/*!
+ * \brief Requests a channel
+ *
+ * \param type type of channel to request
+ * \param format requested channel format (codec)
+ * \param data data to pass to the channel requester
+ * \param status status
+ *
+ * Request a channel of a given type, with data as optional information used
+ * by the low level module
+ *
+ * \retval NULL failure
+ * \retval non-NULL channel on success
+ */
+struct ast_channel *ast_request(const char *type, int format, void *data, int *status);
+
+/*!
+ * \brief Request a channel of a given type, with data as optional information used
+ * by the low level module and attempt to place a call on it
+ *
+ * \param type type of channel to request
+ * \param format requested channel format
+ * \param data data to pass to the channel requester
+ * \param timeout maximum amount of time to wait for an answer
+ * \param reason why unsuccessful (if unsuccessful)
+ * \param cid_num Caller-ID Number
+ * \param cid_name Caller-ID Name (ascii)
+ *
+ * \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state
+ * to know if the call was answered or not.
+ */
+struct ast_channel *ast_request_and_dial(const char *type, int format, void *data,
+ int timeout, int *reason, const char *cid_num, const char *cid_name);
+
+/*!
+ * \brief Request a channel of a given type, with data as optional information used
+ * by the low level module and attempt to place a call on it
+ * \param type type of channel to request
+ * \param format requested channel format
+ * \param data data to pass to the channel requester
+ * \param timeout maximum amount of time to wait for an answer
+ * \param reason why unsuccessful (if unsuccessful)
+ * \param cid_num Caller-ID Number
+ * \param cid_name Caller-ID Name (ascii)
+ * \param oh Outgoing helper
+ * \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state
+ * to know if the call was answered or not.
+ */
+struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data,
+ int timeout, int *reason, const char *cid_num, const char *cid_name, struct outgoing_helper *oh);
+
+/*!\brief Register a channel technology (a new channel driver)
+ * Called by a channel module to register the kind of channels it supports.
+ * \param tech Structure defining channel technology or "type"
+ * \return Returns 0 on success, -1 on failure.
+ */
+int ast_channel_register(const struct ast_channel_tech *tech);
+
+/*! \brief Unregister a channel technology
+ * \param tech Structure defining channel technology or "type" that was previously registered
+ * \return No return value.
+ */
+void ast_channel_unregister(const struct ast_channel_tech *tech);
+
+/*! \brief Get a channel technology structure by name
+ * \param name name of technology to find
+ * \return a pointer to the structure, or NULL if no matching technology found
+ */
+const struct ast_channel_tech *ast_get_channel_tech(const char *name);
+
+/*! \brief Hang up a channel
+ * \note This function performs a hard hangup on a channel. Unlike the soft-hangup, this function
+ * performs all stream stopping, etc, on the channel that needs to end.
+ * chan is no longer valid after this call.
+ * \param chan channel to hang up
+ * \return Returns 0 on success, -1 on failure.
+ */
+int ast_hangup(struct ast_channel *chan);
+
+/*!
+ * \brief Softly hangup up a channel
+ *
+ * \param chan channel to be soft-hung-up
+ * \param cause Ast hangupcause for hangup
+ *
+ * Call the protocol layer, but don't destroy the channel structure
+ * (use this if you are trying to
+ * safely hangup a channel managed by another thread.
+ *
+ * \note The channel passed to this function does not need to be locked.
+ *
+ * \return Returns 0 regardless
+ */
+int ast_softhangup(struct ast_channel *chan, int cause);
+
+/*! \brief Softly hangup up a channel (no channel lock)
+ * \param chan channel to be soft-hung-up
+ * \param cause Ast hangupcause for hangup (see cause.h) */
+int ast_softhangup_nolock(struct ast_channel *chan, int cause);
+
+/*! \brief Check to see if a channel is needing hang up
+ * \param chan channel on which to check for hang up
+ * This function determines if the channel is being requested to be hung up.
+ * \return Returns 0 if not, or 1 if hang up is requested (including time-out).
+ */
+int ast_check_hangup(struct ast_channel *chan);
+
+/*! \brief Compare a offset with the settings of when to hang a channel up
+ * \param chan channel on which to check for hang up
+ * \param offset offset in seconds from current time
+ * \return 1, 0, or -1
+ * This function compares a offset from current time with the absolute time
+ * out on a channel (when to hang up). If the absolute time out on a channel
+ * is earlier than current time plus the offset, it returns 1, if the two
+ * time values are equal, it return 0, otherwise, it return -1.
+ */
+int ast_channel_cmpwhentohangup(struct ast_channel *chan, time_t offset);
+
+/*! \brief Set when to hang a channel up
+ *
+ * \param chan channel on which to check for hang up
+ * \param offset offset in seconds from current time of when to hang up
+ *
+ * This function sets the absolute time out on a channel (when to hang up).
+ *
+ * \note This function does not require that the channel is locked before
+ * calling it.
+ *
+ * \return Nothing
+ */
+void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset);
+
+/*!
+ * \brief Answer a channel
+ *
+ * \param chan channel to answer
+ *
+ * This function answers a channel and handles all necessary call
+ * setup functions.
+ *
+ * \note The channel passed does not need to be locked.
+ *
+ * \retval 0 on success
+ * \retval non-zero on failure
+ */
+int ast_answer(struct ast_channel *chan);
+int __ast_answer(struct ast_channel *chan, unsigned int delay);
+
+/*! \brief Make a call
+ * \param chan which channel to make the call on
+ * \param addr destination of the call
+ * \param timeout time to wait on for connect
+ * Place a call, take no longer than timeout ms.
+ \return Returns -1 on failure, 0 on not enough time
+ (does not automatically stop ringing), and
+ the number of seconds the connect took otherwise.
+ */
+int ast_call(struct ast_channel *chan, char *addr, int timeout);
+
+/*! \brief Indicates condition of channel
+ * \note Indicate a condition such as AST_CONTROL_BUSY, AST_CONTROL_RINGING, or AST_CONTROL_CONGESTION on a channel
+ * \param chan channel to change the indication
+ * \param condition which condition to indicate on the channel
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_indicate(struct ast_channel *chan, int condition);
+
+/*! \brief Indicates condition of channel, with payload
+ * \note Indicate a condition such as AST_CONTROL_HOLD with payload being music on hold class
+ * \param chan channel to change the indication
+ * \param condition which condition to indicate on the channel
+ * \param data pointer to payload data
+ * \param datalen size of payload data
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen);
+
+/* Misc stuff ------------------------------------------------ */
+
+/*! \brief Wait for input on a channel
+ * \param chan channel to wait on
+ * \param ms length of time to wait on the channel
+ * Wait for input on a channel for a given # of milliseconds (<0 for indefinite).
+ \return Returns < 0 on failure, 0 if nothing ever arrived, and the # of ms remaining otherwise */
+int ast_waitfor(struct ast_channel *chan, int ms);
+
+/*! \brief Wait for a specified amount of time, looking for hangups
+ * \param chan channel to wait for
+ * \param ms length of time in milliseconds to sleep
+ * Waits for a specified amount of time, servicing the channel as required.
+ * \return returns -1 on hangup, otherwise 0.
+ */
+int ast_safe_sleep(struct ast_channel *chan, int ms);
+
+/*! \brief Wait for a specified amount of time, looking for hangups and a condition argument
+ * \param chan channel to wait for
+ * \param ms length of time in milliseconds to sleep
+ * \param cond a function pointer for testing continue condition
+ * \param data argument to be passed to the condition test function
+ * \return returns -1 on hangup, otherwise 0.
+ * Waits for a specified amount of time, servicing the channel as required. If cond
+ * returns 0, this function returns.
+ */
+int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data );
+
+/*! \brief Waits for activity on a group of channels
+ * \param chan an array of pointers to channels
+ * \param n number of channels that are to be waited upon
+ * \param fds an array of fds to wait upon
+ * \param nfds the number of fds to wait upon
+ * \param exception exception flag
+ * \param outfd fd that had activity on it
+ * \param ms how long the wait was
+ * Big momma function here. Wait for activity on any of the n channels, or any of the nfds
+ file descriptors.
+ \return Returns the channel with activity, or NULL on error or if an FD
+ came first. If the FD came first, it will be returned in outfd, otherwise, outfd
+ will be -1 */
+struct ast_channel *ast_waitfor_nandfds(struct ast_channel **chan, int n,
+ int *fds, int nfds, int *exception, int *outfd, int *ms);
+
+/*! \brief Waits for input on a group of channels
+ Wait for input on an array of channels for a given # of milliseconds.
+ \return Return channel with activity, or NULL if none has activity.
+ \param chan an array of pointers to channels
+ \param n number of channels that are to be waited upon
+ \param ms time "ms" is modified in-place, if applicable */
+struct ast_channel *ast_waitfor_n(struct ast_channel **chan, int n, int *ms);
+
+/*! \brief Waits for input on an fd
+ This version works on fd's only. Be careful with it. */
+int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception);
+
+
+/*! \brief Reads a frame
+ * \param chan channel to read a frame from
+ * \return Returns a frame, or NULL on error. If it returns NULL, you
+ best just stop reading frames and assume the channel has been
+ disconnected. */
+struct ast_frame *ast_read(struct ast_channel *chan);
+
+/*! \brief Reads a frame, returning AST_FRAME_NULL frame if audio.
+ \param chan channel to read a frame from
+ \return Returns a frame, or NULL on error. If it returns NULL, you
+ best just stop reading frames and assume the channel has been
+ disconnected.
+ \note Audio is replaced with AST_FRAME_NULL to avoid
+ transcode when the resulting audio is not necessary. */
+struct ast_frame *ast_read_noaudio(struct ast_channel *chan);
+
+/*! \brief Write a frame to a channel
+ * This function writes the given frame to the indicated channel.
+ * \param chan destination channel of the frame
+ * \param frame frame that will be written
+ * \return It returns 0 on success, -1 on failure.
+ */
+int ast_write(struct ast_channel *chan, struct ast_frame *frame);
+
+/*! \brief Write video frame to a channel
+ * This function writes the given frame to the indicated channel.
+ * \param chan destination channel of the frame
+ * \param frame frame that will be written
+ * \return It returns 1 on success, 0 if not implemented, and -1 on failure.
+ */
+int ast_write_video(struct ast_channel *chan, struct ast_frame *frame);
+
+/*! \brief Write text frame to a channel
+ * This function writes the given frame to the indicated channel.
+ * \param chan destination channel of the frame
+ * \param frame frame that will be written
+ * \return It returns 1 on success, 0 if not implemented, and -1 on failure.
+ */
+int ast_write_text(struct ast_channel *chan, struct ast_frame *frame);
+
+/*! \brief Send empty audio to prime a channel driver */
+int ast_prod(struct ast_channel *chan);
+
+/*! \brief Sets read format on channel chan
+ * Set read format for channel to whichever component of "format" is best.
+ * \param chan channel to change
+ * \param format format to change to
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_set_read_format(struct ast_channel *chan, int format);
+
+/*! \brief Sets write format on channel chan
+ * Set write format for channel to whichever component of "format" is best.
+ * \param chan channel to change
+ * \param format new format for writing
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_set_write_format(struct ast_channel *chan, int format);
+
+/*!
+ * \brief Sends text to a channel
+ *
+ * \param chan channel to act upon
+ * \param text string of text to send on the channel
+ *
+ * Write text to a display on a channel
+ *
+ * \note The channel does not need to be locked before calling this function.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_sendtext(struct ast_channel *chan, const char *text);
+
+/*! \brief Receives a text character from a channel
+ * \param chan channel to act upon
+ * \param timeout timeout in milliseconds (0 for infinite wait)
+ * Read a char of text from a channel
+ * Returns 0 on success, -1 on failure
+ */
+int ast_recvchar(struct ast_channel *chan, int timeout);
+
+/*! \brief Send a DTMF digit to a channel
+ * Send a DTMF digit to a channel.
+ * \param chan channel to act upon
+ * \param digit the DTMF digit to send, encoded in ASCII
+ * \param duration the duration of the digit ending in ms
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration);
+
+/*! \brief Send a DTMF digit to a channel
+ * Send a DTMF digit to a channel.
+ * \param chan channel to act upon
+ * \param digit the DTMF digit to send, encoded in ASCII
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_senddigit_begin(struct ast_channel *chan, char digit);
+
+/*! \brief Send a DTMF digit to a channel
+
+ * Send a DTMF digit to a channel.
+ * \param chan channel to act upon
+ * \param digit the DTMF digit to send, encoded in ASCII
+ * \param duration the duration of the digit ending in ms
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration);
+
+/*! \brief Receives a text string from a channel
+ * Read a string of text from a channel
+ * \param chan channel to act upon
+ * \param timeout timeout in milliseconds (0 for infinite wait)
+ * \return the received text, or NULL to signify failure.
+ */
+char *ast_recvtext(struct ast_channel *chan, int timeout);
+
+/*! \brief Browse channels in use
+ * Browse the channels currently in use
+ * \param prev where you want to start in the channel list
+ * \return Returns the next channel in the list, NULL on end.
+ * If it returns a channel, that channel *has been locked*!
+ */
+struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev);
+
+/*! \brief Get channel by name or uniqueid (locks channel) */
+struct ast_channel *ast_get_channel_by_name_locked(const char *chan);
+
+/*! \brief Get channel by name or uniqueid prefix (locks channel) */
+struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen);
+
+/*! \brief Get channel by name or uniqueid prefix (locks channel) */
+struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name, const int namelen);
+
+/*! \brief Get channel by exten (and optionally context) and lock it */
+struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context);
+
+/*! \brief Get next channel by exten (and optionally context) and lock it */
+struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
+ const char *context);
+
+/*! ! \brief Waits for a digit
+ * \param c channel to wait for a digit on
+ * \param ms how many milliseconds to wait
+ * \return Returns <0 on error, 0 on no entry, and the digit on success. */
+int ast_waitfordigit(struct ast_channel *c, int ms);
+
+/*! \brief Wait for a digit
+ Same as ast_waitfordigit() with audio fd for outputting read audio and ctrlfd to monitor for reading.
+ * \param c channel to wait for a digit on
+ * \param ms how many milliseconds to wait
+ * \param audiofd audio file descriptor to write to if audio frames are received
+ * \param ctrlfd control file descriptor to monitor for reading
+ * \return Returns 1 if ctrlfd becomes available */
+int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int ctrlfd);
+
+/*! Reads multiple digits
+ * \param c channel to read from
+ * \param s string to read in to. Must be at least the size of your length
+ * \param len how many digits to read (maximum)
+ * \param timeout how long to timeout between digits
+ * \param rtimeout timeout to wait on the first digit
+ * \param enders digits to end the string
+ * Read in a digit string "s", max length "len", maximum timeout between
+ digits "timeout" (-1 for none), terminated by anything in "enders". Give them rtimeout
+ for the first digit. Returns 0 on normal return, or 1 on a timeout. In the case of
+ a timeout, any digits that were read before the timeout will still be available in s.
+ RETURNS 2 in full version when ctrlfd is available, NOT 1*/
+int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders);
+int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders, int audiofd, int ctrlfd);
+
+/*! \brief Report DTMF on channel 0 */
+#define AST_BRIDGE_DTMF_CHANNEL_0 (1 << 0)
+/*! \brief Report DTMF on channel 1 */
+#define AST_BRIDGE_DTMF_CHANNEL_1 (1 << 1)
+/*! \brief Return all voice frames on channel 0 */
+#define AST_BRIDGE_REC_CHANNEL_0 (1 << 2)
+/*! \brief Return all voice frames on channel 1 */
+#define AST_BRIDGE_REC_CHANNEL_1 (1 << 3)
+/*! \brief Ignore all signal frames except NULL */
+#define AST_BRIDGE_IGNORE_SIGS (1 << 4)
+
+
+/*! \brief Makes two channel formats compatible
+ * \param c0 first channel to make compatible
+ * \param c1 other channel to make compatible
+ * Set two channels to compatible formats -- call before ast_channel_bridge in general .
+ * \return Returns 0 on success and -1 if it could not be done */
+int ast_channel_make_compatible(struct ast_channel *c0, struct ast_channel *c1);
+
+/*! Bridge two channels together (early)
+ * \param c0 first channel to bridge
+ * \param c1 second channel to bridge
+ * Bridge two channels (c0 and c1) together early. This implies either side may not be answered yet.
+ * \return Returns 0 on success and -1 if it could not be done */
+int ast_channel_early_bridge(struct ast_channel *c0, struct ast_channel *c1);
+
+/*! Bridge two channels together
+ * \param c0 first channel to bridge
+ * \param c1 second channel to bridge
+ * \param config config for the channels
+ * \param fo destination frame(?)
+ * \param rc destination channel(?)
+ * Bridge two channels (c0 and c1) together. If an important frame occurs, we return that frame in
+ *rf (remember, it could be NULL) and which channel (0 or 1) in rc */
+/* int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc); */
+int ast_channel_bridge(struct ast_channel *c0,struct ast_channel *c1,
+ struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc);
+
+/*!
+ * \brief Weird function made for call transfers
+ *
+ * \param original channel to make a copy of
+ * \param clone copy of the original channel
+ *
+ * This is a very strange and freaky function used primarily for transfer. Suppose that
+ * "original" and "clone" are two channels in random situations. This function takes
+ * the guts out of "clone" and puts them into the "original" channel, then alerts the
+ * channel driver of the change, asking it to fixup any private information (like the
+ * p->owner pointer) that is affected by the change. The physical layer of the original
+ * channel is hung up.
+ *
+ * \note Neither channel passed here needs to be locked before calling this function.
+ */
+int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone);
+
+/*! Gives the string form of a given cause code */
+/*!
+ * \param state cause to get the description of
+ * Give a name to a cause code
+ * Returns the text form of the binary cause code given
+ */
+const char *ast_cause2str(int state) attribute_pure;
+
+/*! Convert the string form of a cause code to a number */
+/*!
+ * \param name string form of the cause
+ * Returns the cause code
+ */
+int ast_str2cause(const char *name) attribute_pure;
+
+/*! Gives the string form of a given channel state */
+/*!
+ * \param ast_channel_state state to get the name of
+ * Give a name to a state
+ * Returns the text form of the binary state given
+ */
+const char *ast_state2str(enum ast_channel_state);
+
+/*! Gives the string form of a given transfer capability */
+/*!
+ * \param transfercapability transfercapabilty to get the name of
+ * Give a name to a transfercapbility
+ * See above
+ * Returns the text form of the binary transfer capability
+ */
+char *ast_transfercapability2str(int transfercapability) attribute_const;
+
+/* Options: Some low-level drivers may implement "options" allowing fine tuning of the
+ low level channel. See frame.h for options. Note that many channel drivers may support
+ none or a subset of those features, and you should not count on this if you want your
+ asterisk application to be portable. They're mainly useful for tweaking performance */
+
+/*! Sets an option on a channel */
+/*!
+ * \param channel channel to set options on
+ * \param option option to change
+ * \param data data specific to option
+ * \param datalen length of the data
+ * \param block blocking or not
+ * Set an option on a channel (see frame.h), optionally blocking awaiting the reply
+ * Returns 0 on success and -1 on failure
+ */
+int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block);
+
+/*! Pick the best codec */
+/* Choose the best codec... Uhhh... Yah. */
+int ast_best_codec(int fmts);
+
+
+/*! Checks the value of an option */
+/*!
+ * Query the value of an option, optionally blocking until a reply is received
+ * Works similarly to setoption except only reads the options.
+ */
+struct ast_frame *ast_channel_queryoption(struct ast_channel *channel, int option, void *data, int *datalen, int block);
+
+/*! Checks for HTML support on a channel */
+/*! Returns 0 if channel does not support HTML or non-zero if it does */
+int ast_channel_supports_html(struct ast_channel *channel);
+
+/*! Sends HTML on given channel */
+/*! Send HTML or URL on link. Returns 0 on success or -1 on failure */
+int ast_channel_sendhtml(struct ast_channel *channel, int subclass, const char *data, int datalen);
+
+/*! Sends a URL on a given link */
+/*! Send URL on link. Returns 0 on success or -1 on failure */
+int ast_channel_sendurl(struct ast_channel *channel, const char *url);
+
+/*! Defers DTMF */
+/*! Defer DTMF so that you only read things like hangups and audio. Returns
+ non-zero if channel was already DTMF-deferred or 0 if channel is just now
+ being DTMF-deferred */
+int ast_channel_defer_dtmf(struct ast_channel *chan);
+
+/*! Undo defer. ast_read will return any dtmf characters that were queued */
+void ast_channel_undefer_dtmf(struct ast_channel *chan);
+
+/*! Initiate system shutdown -- prevents new channels from being allocated.
+ If "hangup" is non-zero, all existing channels will receive soft
+ hangups */
+void ast_begin_shutdown(int hangup);
+
+/*! Cancels an existing shutdown and returns to normal operation */
+void ast_cancel_shutdown(void);
+
+/*! Returns number of active/allocated channels */
+int ast_active_channels(void);
+
+/*! Returns non-zero if Asterisk is being shut down */
+int ast_shutting_down(void);
+
+/*! Activate a given generator */
+int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params);
+
+/*! Deactivate an active generator */
+void ast_deactivate_generator(struct ast_channel *chan);
+
+/*!
+ * \brief Set caller ID number, name and ANI
+ *
+ * \note The channel does not need to be locked before calling this function.
+ */
+void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani);
+
+/*! Set the file descriptor on the channel */
+void ast_channel_set_fd(struct ast_channel *chan, int which, int fd);
+
+/*! Add a channel to an optimized waitfor */
+void ast_poll_channel_add(struct ast_channel *chan0, struct ast_channel *chan1);
+
+/*! Delete a channel from an optimized waitfor */
+void ast_poll_channel_del(struct ast_channel *chan0, struct ast_channel *chan1);
+
+/*! Start a tone going */
+int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol);
+/*! Stop a tone from playing */
+void ast_tonepair_stop(struct ast_channel *chan);
+/*! Play a tone pair for a given amount of time */
+int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, int vol);
+
+/*!
+ * \brief Automatically service a channel for us...
+ *
+ * \retval 0 success
+ * \retval -1 failure, or the channel is already being autoserviced
+ */
+int ast_autoservice_start(struct ast_channel *chan);
+
+/*!
+ * \brief Stop servicing a channel for us...
+ *
+ * \retval 0 success
+ * \retval -1 error, or the channel has been hungup
+ */
+int ast_autoservice_stop(struct ast_channel *chan);
+
+/* If built with zaptel optimizations, force a scheduled expiration on the
+ timer fd, at which point we call the callback function / data */
+int ast_settimeout(struct ast_channel *c, int samples, int (*func)(const void *data), void *data);
+
+/*! \brief Transfer a channel (if supported). Returns -1 on error, 0 if not supported
+ and 1 if supported and requested
+ \param chan current channel
+ \param dest destination extension for transfer
+*/
+int ast_transfer(struct ast_channel *chan, char *dest);
+
+/*! \brief Start masquerading a channel
+ XXX This is a seriously whacked out operation. We're essentially putting the guts of
+ the clone channel into the original channel. Start by killing off the original
+ channel's backend. I'm not sure we're going to keep this function, because
+ while the features are nice, the cost is very high in terms of pure nastiness. XXX
+ \param chan Channel to masquerade
+*/
+int ast_do_masquerade(struct ast_channel *chan);
+
+/*! \brief Find bridged channel
+ \param chan Current channel
+*/
+struct ast_channel *ast_bridged_channel(struct ast_channel *chan);
+
+/*!
+ \brief Inherits channel variable from parent to child channel
+ \param parent Parent channel
+ \param child Child channel
+
+ Scans all channel variables in the parent channel, looking for those
+ that should be copied into the child channel.
+ Variables whose names begin with a single '_' are copied into the
+ child channel with the prefix removed.
+ Variables whose names begin with '__' are copied into the child
+ channel with their names unchanged.
+*/
+void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child);
+
+/*!
+ \brief adds a list of channel variables to a channel
+ \param chan the channel
+ \param vars a linked list of variables
+
+ Variable names can be for a regular channel variable or a dialplan function
+ that has the ability to be written to.
+*/
+void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars);
+
+/*!
+ \brief An opaque 'object' structure use by silence generators on channels.
+ */
+struct ast_silence_generator;
+
+/*!
+ \brief Starts a silence generator on the given channel.
+ \param chan The channel to generate silence on
+ \return An ast_silence_generator pointer, or NULL if an error occurs
+
+ This function will cause SLINEAR silence to be generated on the supplied
+ channel until it is disabled; if the channel cannot be put into SLINEAR
+ mode then the function will fail.
+
+ The pointer returned by this function must be preserved and passed to
+ ast_channel_stop_silence_generator when you wish to stop the silence
+ generation.
+ */
+struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan);
+
+/*!
+ \brief Stops a previously-started silence generator on the given channel.
+ \param chan The channel to operate on
+ \param state The ast_silence_generator pointer return by a previous call to
+ ast_channel_start_silence_generator.
+ \return nothing
+
+ This function will stop the operating silence generator and return the channel
+ to its previous write format.
+ */
+void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state);
+
+/*!
+ \brief Check if the channel can run in internal timing mode.
+ \param chan The channel to check
+ \return boolean
+
+ This function will return 1 if internal timing is enabled and the timing
+ device is available.
+ */
+int ast_internal_timing_enabled(struct ast_channel *chan);
+
+/* Misc. functions below */
+
+/*! \brief if fd is a valid descriptor, set *pfd with the descriptor
+ * \return Return 1 (not -1!) if added, 0 otherwise (so we can add the
+ * return value to the index into the array)
+ */
+static inline int ast_add_fd(struct pollfd *pfd, int fd)
+{
+ pfd->fd = fd;
+ pfd->events = POLLIN | POLLPRI;
+ return fd >= 0;
+}
+
+/*! \brief Helper function for migrating select to poll */
+static inline int ast_fdisset(struct pollfd *pfds, int fd, int max, int *start)
+{
+ int x;
+ int dummy=0;
+
+ if (fd < 0)
+ return 0;
+ if (!start)
+ start = &dummy;
+ for (x = *start; x<max; x++)
+ if (pfds[x].fd == fd) {
+ if (x == *start)
+ (*start)++;
+ return pfds[x].revents;
+ }
+ return 0;
+}
+
+#ifndef HAVE_TIMERSUB
+static inline void timersub(struct timeval *tvend, struct timeval *tvstart, struct timeval *tvdiff)
+{
+ tvdiff->tv_sec = tvend->tv_sec - tvstart->tv_sec;
+ tvdiff->tv_usec = tvend->tv_usec - tvstart->tv_usec;
+ if (tvdiff->tv_usec < 0) {
+ tvdiff->tv_sec --;
+ tvdiff->tv_usec += 1000000;
+ }
+
+}
+#endif
+
+/*! \brief Waits for activity on a group of channels
+ * \param nfds the maximum number of file descriptors in the sets
+ * \param rfds file descriptors to check for read availability
+ * \param wfds file descriptors to check for write availability
+ * \param efds file descriptors to check for exceptions (OOB data)
+ * \param tvp timeout while waiting for events
+ * This is the same as a standard select(), except it guarantees the
+ * behaviour where the passed struct timeval is updated with how much
+ * time was not slept while waiting for the specified events
+ */
+static inline int ast_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tvp)
+{
+#ifdef __linux__
+ return select(nfds, rfds, wfds, efds, tvp);
+#else
+ if (tvp) {
+ struct timeval tv, tvstart, tvend, tvlen;
+ int res;
+
+ tv = *tvp;
+ gettimeofday(&tvstart, NULL);
+ res = select(nfds, rfds, wfds, efds, tvp);
+ gettimeofday(&tvend, NULL);
+ timersub(&tvend, &tvstart, &tvlen);
+ timersub(&tv, &tvlen, tvp);
+ if (tvp->tv_sec < 0 || (tvp->tv_sec == 0 && tvp->tv_usec < 0)) {
+ tvp->tv_sec = 0;
+ tvp->tv_usec = 0;
+ }
+ return res;
+ }
+ else
+ return select(nfds, rfds, wfds, efds, NULL);
+#endif
+}
+
+#ifdef DO_CRASH
+#define CRASH do { fprintf(stderr, "!! Forcing immediate crash a-la abort !!\n"); *((int *)0) = 0; } while(0)
+#else
+#define CRASH do { } while(0)
+#endif
+
+#define CHECK_BLOCKING(c) do { \
+ if (ast_test_flag(c, AST_FLAG_BLOCKING)) {\
+ if (option_debug) \
+ ast_log(LOG_DEBUG, "Thread %ld Blocking '%s', already blocked by thread %ld in procedure %s\n", (long) pthread_self(), (c)->name, (long) (c)->blocker, (c)->blockproc); \
+ CRASH; \
+ } else { \
+ (c)->blocker = pthread_self(); \
+ (c)->blockproc = __PRETTY_FUNCTION__; \
+ ast_set_flag(c, AST_FLAG_BLOCKING); \
+ } } while (0)
+
+ast_group_t ast_get_group(const char *s);
+
+/*! \brief print call- and pickup groups into buffer */
+char *ast_print_group(char *buf, int buflen, ast_group_t group);
+
+/*! \brief Convert enum channelreloadreason to text string for manager event
+ \param reason Enum channelreloadreason - reason for reload (manager, cli, start etc)
+*/
+const char *channelreloadreason2txt(enum channelreloadreason reason);
+
+/*! \brief return an ast_variable list of channeltypes */
+struct ast_variable *ast_channeltype_list(void);
+
+/*!
+ \brief return an english explanation of the code returned thru __ast_request_and_dial's 'outstate' argument
+ \param reason The integer argument, usually taken from AST_CONTROL_ macros
+ \return char pointer explaining the code
+ */
+const char *ast_channel_reason2str(int reason);
+
+/*! \brief channel group info
+ */
+struct ast_group_info {
+ struct ast_channel *chan;
+ char *category;
+ char *group;
+ AST_LIST_ENTRY(ast_group_info) list;
+};
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_CHANNEL_H */
diff --git a/trunk/include/asterisk/chanvars.h b/trunk/include/asterisk/chanvars.h
new file mode 100644
index 000000000..63de58429
--- /dev/null
+++ b/trunk/include/asterisk/chanvars.h
@@ -0,0 +1,42 @@
+/*
+ * 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 Channel Variables
+ */
+
+#ifndef _ASTERISK_CHANVARS_H
+#define _ASTERISK_CHANVARS_H
+
+#include "asterisk/linkedlists.h"
+
+struct ast_var_t {
+ AST_LIST_ENTRY(ast_var_t) entries;
+ char *value;
+ char name[0];
+};
+
+AST_LIST_HEAD_NOLOCK(varshead, ast_var_t);
+
+struct ast_var_t *ast_var_assign(const char *name, const char *value);
+void ast_var_delete(struct ast_var_t *var);
+const char *ast_var_name(const struct ast_var_t *var);
+const char *ast_var_full_name(const struct ast_var_t *var);
+const char *ast_var_value(const struct ast_var_t *var);
+
+#endif /* _ASTERISK_CHANVARS_H */
diff --git a/trunk/include/asterisk/cli.h b/trunk/include/asterisk/cli.h
new file mode 100644
index 000000000..854c189ff
--- /dev/null
+++ b/trunk/include/asterisk/cli.h
@@ -0,0 +1,285 @@
+/*
+ * 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 Standard Command Line Interface
+ */
+
+#ifndef _ASTERISK_CLI_H
+#define _ASTERISK_CLI_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/linkedlists.h"
+
+void ast_cli(int fd, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+#define RESULT_SUCCESS 0
+#define RESULT_SHOWUSAGE 1
+#define RESULT_FAILURE 2
+
+#define CLI_SUCCESS (char *)RESULT_SUCCESS
+#define CLI_SHOWUSAGE (char *)RESULT_SHOWUSAGE
+#define CLI_FAILURE (char *)RESULT_FAILURE
+
+#define AST_MAX_CMD_LEN 16
+
+#define AST_MAX_ARGS 64
+
+#define AST_CLI_COMPLETE_EOF "_EOF_"
+
+/*!
+ * In many cases we need to print singular or plural
+ * words depending on a count. This macro helps us e.g.
+ * printf("we have %d object%s", n, ESS(n));
+ */
+#define ESS(x) ((x) == 1 ? "" : "s")
+
+/*! \page CLI_command_API CLI command API
+
+ CLI commands are described by a struct ast_cli_entry that contains
+ all the components for their implementation.
+
+ In the "old-style" format, the record must contain:
+ - a NULL-terminated array of words constituting the command, e.g.
+ { "set", "debug", "on", NULL },
+ - a summary string (short) and a usage string (longer);
+ - a handler which implements the command itself, invoked with
+ a file descriptor and argc/argv as typed by the user
+ - a 'generator' function which, given a partial string, can
+ generate legal completions for it.
+ An example is
+
+ int old_setdebug(int fd, int argc, char *argv[]);
+ char *dbg_complete(const char *line, const char *word, int pos, int n);
+
+ { { "set", "debug", "on", NULL }, do_setdebug, "Enable debugging",
+ set_debug_usage, dbg_complete },
+
+ In the "new-style" format, all the above functionalities are implemented
+ by a single function, and the arguments tell which output is required.
+ The prototype is the following:
+
+ char *new_setdebug(const struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+ ...
+ // this is how we create the entry to register
+ AST_CLI_DEFINE(new_setdebug, "short description")
+ ...
+
+ To help the transition, we make the pointer to the struct ast_cli_entry
+ available to old-style handlers via argv[-1].
+
+ An example of new-style handler is the following
+
+\code
+static char *test_new_cli(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ static char *choices = { "one", "two", "three", NULL };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "do this well";
+ e->usage =
+ "Usage: do this well <arg>\n"
+ " typically multiline with body indented\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ if (a->pos > e->args)
+ return NULL;
+ return ast_cli_complete(a->word, choices, a->n);
+
+ default:
+ // we are guaranteed to be called with argc >= e->args;
+ if (a->argc > e->args + 1) // we accept one extra argument
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "done this well for %s\n", e->args[argc-1]);
+ return CLI_SUCCESS;
+ }
+}
+
+\endcode
+
+ */
+
+/*! \brief calling arguments for new-style handlers.
+* \arg \ref CLI_command_API
+*/
+enum ast_cli_fn {
+ CLI_INIT = -2, /* return the usage string */
+ CLI_GENERATE = -3, /* behave as 'generator', remap argv to struct ast_cli_args */
+ CLI_HANDLER = -4, /* run the normal handler */
+};
+
+/* argument for new-style CLI handler */
+struct ast_cli_args {
+ int fd;
+ int argc;
+ char **argv;
+ const char *line; /* the current input line */
+ const char *word; /* the word we want to complete */
+ int pos; /* position of the word to complete */
+ int n; /* the iteration count (n-th entry we generate) */
+};
+
+struct ast_cli_entry;
+typedef char *(*cli_fn)(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+/*! \brief descriptor for a cli entry.
+ * \arg \ref CLI_command_API
+ */
+struct ast_cli_entry {
+ char * const cmda[AST_MAX_CMD_LEN]; /*!< words making up the command.
+ * set the first entry to NULL for a new-style entry. */
+
+ const char *summary; /*!< Summary of the command (< 60 characters) */
+ const char *usage; /*!< Detailed usage information */
+
+ struct ast_cli_entry *deprecate_cmd;
+
+ int inuse; /*!< For keeping track of usage */
+ struct module *module; /*!< module this belongs to */
+ char *_full_cmd; /*!< built at load time from cmda[] */
+ int cmdlen; /*!< len up to the first invalid char [<{% */
+ /*! \brief This gets set in ast_cli_register()
+ It then gets set to something different when the deprecated command
+ is run for the first time (ie; after we warn the user that it's deprecated)
+ */
+ int args; /*!< number of non-null entries in cmda */
+ char *command; /*!< command, non-null for new-style entries */
+ int deprecated;
+ cli_fn handler;
+ char *_deprecated_by; /*!< copied from the "parent" _full_cmd, on deprecated commands */
+ /*! For linking */
+ AST_LIST_ENTRY(ast_cli_entry) list;
+};
+
+/* XXX the parser in gcc 2.95 gets confused if you don't put a space
+ * between the last arg before VA_ARGS and the comma */
+#define AST_CLI_DEFINE(fn, txt , ... ) { .handler = fn, .summary = txt, ## __VA_ARGS__ }
+
+/*!
+ * Helper function to generate cli entries from a NULL-terminated array.
+ * Returns the n-th matching entry from the array, or NULL if not found.
+ * Can be used to implement generate() for static entries as below
+ * (in this example we complete the word in position 2):
+ \code
+ char *my_generate(const char *line, const char *word, int pos, int n)
+ {
+ static char *choices = { "one", "two", "three", NULL };
+ if (pos == 2)
+ return ast_cli_complete(word, choices, n);
+ else
+ return NULL;
+ }
+ \endcode
+ */
+char *ast_cli_complete(const char *word, char *const choices[], int pos);
+
+/*!
+ * \brief Interprets a command
+ * Interpret a command s, sending output to fd
+ * \param fd pipe
+ * \param s incoming string
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_cli_command(int fd, const char *s);
+
+/*!
+ * \brief Executes multiple CLI commands
+ * Interpret strings separated by NULL and execute each one, sending output to fd
+ * \param fd pipe
+ * \param size is the total size of the string
+ * \param s incoming string
+ * \retval number of commands executed
+ */
+int ast_cli_command_multiple(int fd, size_t size, const char *s);
+
+/*! \brief Registers a command or an array of commands
+ * \param e which cli entry to register.
+ * Register your own command
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_cli_register(struct ast_cli_entry *e);
+
+/*!
+ * \brief Register multiple commands
+ * \param e pointer to first cli entry to register
+ * \param len number of entries to register
+ */
+int ast_cli_register_multiple(struct ast_cli_entry *e, int len);
+
+/*!
+ * \brief Unregisters a command or an array of commands
+ * \param e which cli entry to unregister
+ * Unregister your own command. You must pass a completed ast_cli_entry structure
+ * \return 0
+ */
+int ast_cli_unregister(struct ast_cli_entry *e);
+
+/*!
+ * \brief Unregister multiple commands
+ * \param e pointer to first cli entry to unregister
+ * \param len number of entries to unregister
+ */
+int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len);
+
+/*!
+ * \brief Readline madness
+ * Useful for readline, that's about it
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+char *ast_cli_generator(const char *, const char *, int);
+
+int ast_cli_generatornummatches(const char *, const char *);
+
+/*!
+ * \brief Generates a NULL-terminated array of strings that
+ * 1) begin with the string in the second parameter, and
+ * 2) are valid in a command after the string in the first parameter.
+ *
+ * The first entry (offset 0) of the result is the longest common substring
+ * in the results, useful to extend the string that has been completed.
+ * Subsequent entries are all possible values, followed by a NULL.
+ * All strings and the array itself are malloc'ed and must be freed
+ * by the caller.
+ */
+char **ast_cli_completion_matches(const char *, const char *);
+
+/*!
+ * \brief Command completion for the list of active channels.
+ *
+ * This can be called from a CLI command completion function that wants to
+ * complete from the list of active channels. 'rpos' is the required
+ * position in the command. This function will return NULL immediately if
+ * 'rpos' is not the same as the current position, 'pos'.
+ */
+char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_CLI_H */
diff --git a/trunk/include/asterisk/compat.h b/trunk/include/asterisk/compat.h
new file mode 100644
index 000000000..43da49bc0
--- /dev/null
+++ b/trunk/include/asterisk/compat.h
@@ -0,0 +1,184 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * 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
+ */
+
+/*! \file
+ * \brief General Definitions for Asterisk top level program
+ * Included by asterisk.h to handle platform-specific issues
+ * especially those related to header files.
+ */
+
+#include "asterisk/compiler.h"
+
+#ifndef _COMPAT_H
+#define _COMPAT_H
+
+#ifndef __STDC_VERSION__
+/* flex output wants to find this defined. */
+#define __STDC_VERSION__ 0
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdarg.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h> /* not necessarily present - could be in stdlib */
+#elif defined(HAVE_ALLOCA) && defined(__MINGW32__)
+#include <malloc.h> /* see if it is here... */
+#endif
+
+#include <stdio.h> /* this is always present */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include "asterisk/poll-compat.h"
+#endif
+
+#if !defined(HAVE_ASPRINTF) && !defined(__AST_DEBUG_MALLOC)
+int asprintf(char **str, const char *fmt, ...);
+#endif
+
+#ifndef HAVE_GETLOADAVG
+int getloadavg(double *list, int nelem);
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite);
+#endif
+
+#ifndef HAVE_STRCASESTR
+char *strcasestr(const char *, const char *);
+#endif
+
+#if !defined(HAVE_STRNDUP) && !defined(__AST_DEBUG_MALLOC)
+char *strndup(const char *, size_t);
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *, size_t);
+#endif
+
+#ifndef HAVE_STRSEP
+char* strsep(char** str, const char* delims);
+#endif
+
+#ifndef HAVE_STRTOQ
+uint64_t strtoq(const char *nptr, char **endptr, int base);
+#endif
+
+#ifndef HAVE_UNSETENV
+int unsetenv(const char *name);
+#endif
+
+#if !defined(HAVE_VASPRINTF) && !defined(__AST_DEBUG_MALLOC)
+int vasprintf(char **strp, const char *fmt, va_list ap);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+
+#include <errno.h>
+
+#ifdef SOLARIS
+#define __BEGIN_DECLS
+#define __END_DECLS
+
+#ifndef __P
+#define __P(p) p
+#endif
+
+#include <alloca.h>
+#include <strings.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <sys/loadavg.h>
+#include <dat/dat_platform_specific.h>
+
+#ifndef BYTE_ORDER
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+
+#ifdef __sparc__
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
+
+#ifndef __BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __BYTE_ORDER BYTE_ORDER
+#endif
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int u_int32_t;
+#endif
+
+#endif /* SOLARIS */
+
+#ifdef __CYGWIN__
+#define _WIN32_WINNT 0x0500
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+#endif /* __CYGWIN__ */
+
+#ifdef __CYGWIN__
+typedef unsigned long long uint64_t;
+#endif
+
+#endif
diff --git a/trunk/include/asterisk/compiler.h b/trunk/include/asterisk/compiler.h
new file mode 100644
index 000000000..8ac441463
--- /dev/null
+++ b/trunk/include/asterisk/compiler.h
@@ -0,0 +1,56 @@
+/*
+ * 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 Compiler-specific macros and other items
+ */
+
+#ifndef _ASTERISK_COMPILER_H
+#define _ASTERISK_COMPILER_H
+
+#if HAVE_ATTRIBUTE_always_inline
+#define force_inline __attribute__((always_inline)) inline
+#else
+#define force_inline inline
+#endif
+
+#if HAVE_ATTRIBUTE_pure
+#define attribute_pure __attribute__((pure))
+#else
+#define attribute_pure
+#endif
+
+#if HAVE_ATTRIBUTE_const
+#define attribute_const __attribute__((const))
+#else
+#define attribute_const
+#endif
+
+#if HAVE_ATTRIBUTE_unused
+#define attribute_unused __attribute__((unused))
+#else
+#define attribute_unused
+#endif
+
+#if HAVE_ATTRIBUTE_malloc
+#define attribute_malloc __attribute__((malloc))
+#else
+#define attribute_malloc
+#endif
+
+#endif /* _ASTERISK_COMPILER_H */
diff --git a/trunk/include/asterisk/config.h b/trunk/include/asterisk/config.h
new file mode 100644
index 000000000..fe9d49d30
--- /dev/null
+++ b/trunk/include/asterisk/config.h
@@ -0,0 +1,406 @@
+/*
+ * 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 Configuration File Parser
+ */
+
+#ifndef _ASTERISK_CONFIG_H
+#define _ASTERISK_CONFIG_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/utils.h"
+
+struct ast_config;
+
+struct ast_category;
+
+/*! Options for ast_config_load()
+ */
+enum {
+ /*! Load the configuration, including comments */
+ CONFIG_FLAG_WITHCOMMENTS = (1 << 0),
+ /*! On a reload, give us a -1 if the file hasn't changed. */
+ CONFIG_FLAG_FILEUNCHANGED = (1 << 1),
+ /*! Don't attempt to cache mtime on this config file. */
+ CONFIG_FLAG_NOCACHE = (1 << 2),
+};
+
+#define CONFIG_STATUS_FILEUNCHANGED (void *)-1
+
+/*! \brief Structure for variables, used for configurations and for channel variables
+*/
+struct ast_variable {
+ const char *name;
+ const char *value;
+ struct ast_variable *next;
+
+ char *file;
+
+ int lineno;
+ int object; /*!< 0 for variable, 1 for object */
+ int blanklines; /*!< Number of blanklines following entry */
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
+ char stuff[0];
+};
+
+typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, struct ast_flags flags, const char *suggested_include_file);
+typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
+typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
+typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+typedef int realtime_store(const char *database, const char *table, va_list ap);
+typedef int realtime_destroy(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+
+/*! \brief Configuration engine structure, used to define realtime drivers */
+struct ast_config_engine {
+ char *name;
+ config_load_func *load_func;
+ realtime_var_get *realtime_func;
+ realtime_multi_get *realtime_multi_func;
+ realtime_update *update_func;
+ realtime_store *store_func;
+ realtime_destroy *destroy_func;
+ struct ast_config_engine *next;
+};
+
+/*! \brief Load a config file
+ * \param filename path of file to open. If no preceding '/' character, path is considered relative to AST_CONFIG_DIR
+ * Create a config structure from a given configuration file.
+ * \param flags Optional flags:
+ * CONFIG_FLAG_WITHCOMMENTS - load the file with comments intact;
+ * CONFIG_FLAG_FILEUNCHANGED - check the file mtime and return CONFIG_STATUS_FILEUNCHANGED if the mtime is the same; or
+ * CONFIG_FLAG_NOCACHE - don't cache file mtime (main purpose of this option is to save memory on temporary files).
+ *
+ * \retval an ast_config data structure on success
+ * \retval NULL on error
+ */
+struct ast_config *ast_config_load(const char *filename, struct ast_flags flags);
+
+/*! \brief Destroys a config
+ * \param config pointer to config data structure
+ * Free memory associated with a given config
+ *
+ */
+void ast_config_destroy(struct ast_config *config);
+
+/*! \brief returns the root ast_variable of a config
+ * \param config pointer to an ast_config data structure
+ * \param cat name of the category for which you want the root
+ *
+ * Returns the category specified
+ */
+struct ast_variable *ast_category_root(struct ast_config *config, char *cat);
+
+/*! \brief Goes through categories
+ * \param config Which config structure you wish to "browse"
+ * \param prev A pointer to a previous category.
+ * This function is kind of non-intuitive in it's use. To begin, one passes NULL as the second argument. It will return a pointer to the string of the first category in the file. From here on after, one must then pass the previous usage's return value as the second pointer, and it will return a pointer to the category name afterwards.
+ *
+ * \retval a category on success
+ * \retval NULL on failure/no-more-categories
+ */
+char *ast_category_browse(struct ast_config *config, const char *prev);
+
+/*!
+ * \brief Goes through variables
+ * Somewhat similar in intent as the ast_category_browse.
+ * List variables of config file category
+ *
+ * \retval ast_variable list on success
+ * \retval NULL on failure
+ */
+struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
+
+/*!
+ * \brief given a pointer to a category, return the root variable.
+ * This is equivalent to ast_variable_browse(), but more efficient if we
+ * already have the struct ast_category * (e.g. from ast_category_get())
+ */
+struct ast_variable *ast_category_first(struct ast_category *cat);
+
+/*!
+ * \brief Gets a variable
+ * \param config which (opened) config to use
+ * \param category category under which the variable lies
+ * \param variable which variable you wish to get the data for
+ * Goes through a given config file in the given category and searches for the given variable
+ *
+ * \retval The variable value on success
+ * \retval NULL if unable to find it.
+ */
+const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
+
+/*!
+ * \brief Retrieve a category if it exists
+ * \param config which config to use
+ * \param category_name name of the category you're looking for
+ * This will search through the categories within a given config file for a match.
+ *
+ * \retval pointer to category if found
+ * \retval NULL if not.
+ */
+struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name);
+
+/*!
+ * \brief Check for category duplicates
+ * \param config which config to use
+ * \param category_name name of the category you're looking for
+ * This will search through the categories within a given config file for a match.
+ *
+ * \return non-zero if found
+ */
+int ast_category_exist(const struct ast_config *config, const char *category_name);
+
+/*!
+ * \brief Retrieve realtime configuration
+ * \param family which family/config to lookup
+ * This will use builtin configuration backends to look up a particular
+ * entity in realtime and return a variable list of its parameters. Note
+ * that unlike the variables in ast_config, the resulting list of variables
+ * MUST be freed with ast_variables_destroy() as there is no container.
+ */
+struct ast_variable *ast_load_realtime(const char *family, ...);
+struct ast_variable *ast_load_realtime_all(const char *family, ...);
+
+/*!
+ * \brief Retrieve realtime configuration
+ * \param family which family/config to lookup
+ * This will use builtin configuration backends to look up a particular
+ * entity in realtime and return a variable list of its parameters. Unlike
+ * the ast_load_realtime, this function can return more than one entry and
+ * is thus stored inside a taditional ast_config structure rather than
+ * just returning a linked list of variables.
+ */
+struct ast_config *ast_load_realtime_multientry(const char *family, ...);
+
+/*!
+ * \brief Update realtime configuration
+ * \param family which family/config to be updated
+ * \param keyfield which field to use as the key
+ * \param lookup which value to look for in the key field to match the entry.
+ * This function is used to update a parameter in realtime configuration space.
+ *
+ */
+int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...);
+
+/*!
+ * \brief Create realtime configuration
+ * \param family which family/config to be created
+ * This function is used to create a parameter in realtime configuration space.
+ *
+ */
+int ast_store_realtime(const char *family, ...);
+
+/*!
+ * \brief Destroy realtime configuration
+ * \param family which family/config to be destroyed
+ * \param keyfield which field to use as the key
+ * \param lookup which value to look for in the key field to match the entry.
+ * This function is used to destroy an entry in realtime configuration space.
+ * Additional params are used as keys.
+ *
+ */
+int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...);
+
+/*!
+ * \brief Check if realtime engine is configured for family
+ * \param family which family/config to be checked
+ * \return 1 if family is configured in realtime and engine exists
+*/
+int ast_check_realtime(const char *family);
+
+/*! \brief Check if there's any realtime engines loaded */
+int ast_realtime_enabled(void);
+
+/*! \brief Free variable list
+ * \param var the linked list of variables to free
+ * This function frees a list of variables.
+ */
+void ast_variables_destroy(struct ast_variable *var);
+
+/*! \brief Register config engine */
+int ast_config_engine_register(struct ast_config_engine *newconfig);
+
+/*! \brief Deegister config engine */
+int ast_config_engine_deregister(struct ast_config_engine *del);
+
+int register_config_cli(void);
+int read_config_maps(void);
+
+struct ast_config *ast_config_new(void);
+struct ast_category *ast_config_get_current_category(const struct ast_config *cfg);
+void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat);
+const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var);
+
+struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
+void ast_category_append(struct ast_config *config, struct ast_category *cat);
+int ast_category_delete(struct ast_config *cfg, const char *category);
+void ast_category_destroy(struct ast_category *cat);
+struct ast_variable *ast_category_detach_variables(struct ast_category *cat);
+void ast_category_rename(struct ast_category *cat, const char *name);
+
+struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename);
+struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size);
+struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file);
+void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file);
+void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
+int ast_variable_delete(struct ast_category *category, const char *variable, const char *match);
+int ast_variable_update(struct ast_category *category, const char *variable,
+ const char *value, const char *match, unsigned int object);
+
+int config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator);
+
+struct ast_config *ast_config_internal_load(const char *configfile, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl_file);
+
+/*! \brief Support code to parse config file arguments
+ *
+ * The function ast_parse_arg() provides a generic interface to parse
+ * strings (e.g. numbers, network addresses and so on) in a flexible
+ * way, e.g. by doing proper error and bound checks, provide default
+ * values, and so on.
+ * The function (described later) takes a string as an argument,
+ * a set of flags to specify the result format and checks to perform,
+ * a pointer to the result, and optionally some additional arguments.
+ * It returns 0 on success, != 0 otherwise.
+ *
+ */
+enum ast_parse_flags {
+ /* low 4 bits of flags are used for the operand type */
+ PARSE_TYPE = 0x000f,
+ /* numeric types, with optional default value and bound checks.
+ * Additional arguments are passed by value.
+ */
+ PARSE_INT32 = 0x0001,
+ PARSE_UINT32 = 0x0002,
+ PARSE_DOUBLE = 0x0003,
+#if 0 /* not supported yet */
+ PARSE_INT16 = 0x0004,
+ PARSE_UINT16 = 0x0005,
+#endif
+ /* Returns a struct sockaddr_in, with optional default value
+ * (passed by reference) and port handling (accept, ignore,
+ * require, forbid). The format is 'host.name[:port]'
+ */
+ PARSE_INADDR = 0x000f,
+
+ /* Other data types can be added as needed */
+
+ /* If PARSE_DEFAULT is set, next argument is a default value
+ * which is returned in case of error. The argument is passed
+ * by value in case of numeric types, by reference in other cases.
+ */
+ PARSE_DEFAULT = 0x0010, /* assign default on error */
+
+ /* Request a range check, applicable to numbers. Two additional
+ * arguments are passed by value, specifying the low-high end of
+ * the range (inclusive). An error is returned if the value
+ * is outside or inside the range, respectively.
+ */
+ PARSE_IN_RANGE = 0x0020, /* accept values inside a range */
+ PARSE_OUT_RANGE = 0x0040, /* accept values outside a range */
+
+ /* Port handling, for sockaddr_in. accept/ignore/require/forbid
+ * port number after the hostname or address.
+ */
+ PARSE_PORT_MASK = 0x0300, /* 0x000: accept port if present */
+ PARSE_PORT_IGNORE = 0x0100, /* 0x100: ignore port if present */
+ PARSE_PORT_REQUIRE = 0x0200, /* 0x200: require port number */
+ PARSE_PORT_FORBID = 0x0300, /* 0x100: forbid port number */
+};
+
+/*! \brief The argument parsing routine.
+ * \param arg the string to parse. It is not modified.
+ * \param flags combination of ast_parse_flags to specify the
+ * return type and additional checks.
+ * \param result pointer to the result. NULL is valid here, and can
+ * be used to perform only the validity checks.
+ * \param ... extra arguments are required according to flags.
+ * \retval 0 in case of success, != 0 otherwise.
+ * \retval result returns the parsed value in case of success,
+ * the default value in case of error, or it is left unchanged
+ * in case of error and no default specified. Note that in certain
+ * cases (e.g. sockaddr_in, with multi-field return values) some
+ * of the fields in result may be changed even if an error occurs.
+ *
+ * Examples of use:
+ * ast_parse_arg("223", PARSE_INT32|PARSE_IN_RANGE,
+ * &a, -1000, 1000);
+ * returns 0, a = 223
+ * ast_parse_arg("22345", PARSE_INT32|PARSE_IN_RANGE|PARSE_DEFAULT,
+ * &a, 9999, 10, 100);
+ * returns 1, a = 9999
+ * ast_parse_arg("22345ssf", PARSE_UINT32|PARSE_IN_RANGE, &b, 10, 100);
+ * returns 1, b unchanged
+ * ast_parse_arg("www.foo.biz:44", PARSE_INADDR, &sa);
+ * returns 0, sa contains address and port
+ * ast_parse_arg("www.foo.biz", PARSE_INADDR|PARSE_PORT_REQUIRE, &sa);
+ * returns 1 because port is missing, sa contains address
+ */
+int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
+ void *result, ...);
+
+/*
+ * Parsing config file options in C is slightly annoying because we cannot use
+ * string in a switch() statement, yet we need a similar behaviour, with many
+ * branches and a break on a matching one.
+ * The following somehow simplifies the job: we create a block using
+ * the CV_START and CV_END macros, and then within the block we can run
+ * actions such as "if (condition) { body; break; }"
+ * Additional macros are present to run simple functions (e.g. ast_copy_string)
+ * or to pass arguments to ast_parse_arg()
+ *
+ * As an example:
+
+ CV_START(v->name, v->value); // start the block
+ CV_STR("foo", x_foo); // static string
+ CV_DSTR("bar", y_bar); // malloc'ed string
+ CV_F("bar", ...); // call a generic function
+ CV_END; // end the block
+ */
+
+/*! \brief the macro to open a block for variable parsing */
+#define CV_START(__in_var, __in_val) \
+ do { \
+ const char *__var = __in_var; \
+ const char *__val = __in_val;
+
+/*! \brief close a variable parsing block */
+#define CV_END } while (0)
+
+/*! \brief call a generic function if the name matches. */
+#define CV_F(__pattern, __body) if (!strcasecmp((__var), __pattern)) { __body; break; }
+
+/*! \brief helper macros to assign the value to a BOOL, UINT, static string and
+ * dynamic string
+ */
+#define CV_BOOL(__x, __dst) CV_F(__x, (__dst) = ast_true(__val) )
+#define CV_UINT(__x, __dst) CV_F(__x, (__dst) = strtoul(__val, NULL, 0) )
+#define CV_STR(__x, __dst) CV_F(__x, ast_copy_string(__dst, __val, sizeof(__dst)))
+#define CV_DSTR(__x, __dst) CV_F(__x, if (__dst) ast_free(__dst); __dst = ast_strdup(__val))
+#define CV_STRFIELD(__x, __obj, __field) CV_F(__x, ast_string_field_set(__obj, __field, __val))
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_CONFIG_H */
diff --git a/trunk/include/asterisk/crypto.h b/trunk/include/asterisk/crypto.h
new file mode 100644
index 000000000..427c5ea51
--- /dev/null
+++ b/trunk/include/asterisk/crypto.h
@@ -0,0 +1,126 @@
+/*
+ * 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 Provide cryptographic signature routines
+ */
+
+#ifndef _ASTERISK_CRYPTO_H
+#define _ASTERISK_CRYPTO_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define AST_KEY_PUBLIC (1 << 0)
+#define AST_KEY_PRIVATE (1 << 1)
+
+struct ast_key;
+
+/*!
+ * \brief Retrieve a key
+ * \param name of the key we are retrieving
+ * \param int type of key (AST_KEY_PUBLIC or AST_KEY_PRIVATE)
+ *
+ * \retval the key on success.
+ * \retval NULL on failure.
+ */
+struct ast_key *(*ast_key_get)(const char *key, int type);
+
+/*!
+ * \brief Check the authenticity of a message signature using a given public key
+ * \param key a public key to use to verify
+ * \param msg the message that has been signed
+ * \param sig the proposed valid signature in mime64-like encoding
+ *
+ * \retval 0 if the signature is valid.
+ * \retval -1 otherwise.
+ *
+ */
+int (*ast_check_signature)(struct ast_key *key, const char *msg, const char *sig);
+
+/*!
+ * \brief Check the authenticity of a message signature using a given public key
+ * \param key a public key to use to verify
+ * \param msg the message that has been signed
+ * \param sig the proposed valid signature in raw binary representation
+ *
+ * \retval 0 if the signature is valid.
+ * \retval -1 otherwise.
+ *
+ */
+int (*ast_check_signature_bin)(struct ast_key *key, const char *msg, int msglen, const unsigned char *sig);
+
+/*!
+ * \brief Sign a message signature using a given private key
+ * \param key a private key to use to create the signature
+ * \param msg the message to sign
+ * \param sig a pointer to a buffer of at least 256 bytes in which the
+ * mime64-like encoded signature will be stored
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ *
+ */
+int (*ast_sign)(struct ast_key *key, char *msg, char *sig);
+
+/*!
+ * \brief Sign a message signature using a given private key
+ * \param key a private key to use to create the signature
+ * \param msg the message to sign
+ * \param sig a pointer to a buffer of at least 128 bytes in which the
+ * raw encoded signature will be stored
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ *
+ */
+int (*ast_sign_bin)(struct ast_key *key, const char *msg, int msglen, unsigned char *sig);
+
+/*!
+ * \brief Encrypt a message using a given private key
+ * \param key a private key to use to encrypt
+ * \param src the message to encrypt
+ * \param srclen the length of the message to encrypt
+ * \param dst a pointer to a buffer of at least srclen * 1.5 bytes in which the encrypted
+ * answer will be stored
+ *
+ * \retval length of encrypted data on success.
+ * \retval -1 on failure.
+ *
+ */
+int (*ast_encrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key);
+
+/*!
+ * \brief Decrypt a message using a given private key
+ * \param key a private key to use to decrypt
+ * \param src the message to decrypt
+ * \param srclen the length of the message to decrypt
+ * \param dst a pointer to a buffer of at least srclen bytes in which the decrypted
+ * answer will be stored
+ *
+ * \retval length of dencrypted data on success.
+ * \retval -1 on failure.
+ *
+ */
+int (*ast_decrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key);
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_CRYPTO_H */
diff --git a/trunk/include/asterisk/devicestate.h b/trunk/include/asterisk/devicestate.h
new file mode 100644
index 000000000..d52430301
--- /dev/null
+++ b/trunk/include/asterisk/devicestate.h
@@ -0,0 +1,203 @@
+/*
+ * 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 Device state management
+ *
+ * To subscribe to device state changes, use the generic ast_event_subscribe
+ * method. For an example, see apps/app_queue.c.
+ *
+ * \todo Currently, when the state of a device changes, the device state provider
+ * calls one of the functions defined here to queue an object to say that the
+ * state of a device has changed. However, this does not include the new state.
+ * Another thread processes these device state change objects and calls the
+ * device state provider's callback to figure out what the new state is. It
+ * would make a lot more sense for the new state to be included in the original
+ * function call that says the state of a device has changed. However, it
+ * will take a lot of work to change this.
+ *
+ * \arg See \ref AstExtState
+ */
+
+#ifndef _ASTERISK_DEVICESTATE_H
+#define _ASTERISK_DEVICESTATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief Device States
+ * \note The order of these states may not change because they are included
+ * in Asterisk events which may be transmitted across the network to
+ * other servers.
+ */
+enum ast_device_state {
+ AST_DEVICE_UNKNOWN, /*!< Device is valid but channel didn't know state */
+ AST_DEVICE_NOT_INUSE, /*!< Device is not used */
+ AST_DEVICE_INUSE, /*!< Device is in use */
+ AST_DEVICE_BUSY, /*!< Device is busy */
+ AST_DEVICE_INVALID, /*!< Device is invalid */
+ AST_DEVICE_UNAVAILABLE, /*!< Device is unavailable */
+ AST_DEVICE_RINGING, /*!< Device is ringing */
+ AST_DEVICE_RINGINUSE, /*!< Device is ringing *and* in use */
+ AST_DEVICE_ONHOLD, /*!< Device is on hold */
+};
+
+/*! \brief Devicestate provider call back */
+typedef enum ast_device_state (*ast_devstate_prov_cb_type)(const char *data);
+
+/*!
+ * \brief Convert device state to text string for output
+ *
+ * \param devstate Current device state
+ */
+const char *devstate2str(enum ast_device_state devstate);
+
+/*!
+ * \brief Convert device state to text string that is easier to parse
+ *
+ * \param devstate Current device state
+ */
+const char *ast_devstate_str(enum ast_device_state devstate);
+
+/*!
+ * \brief Convert device state from text to integer value
+ *
+ * \param val The text representing the device state. Valid values are anything
+ * that comes after AST_DEVICE_ in one of the defined values.
+ *
+ * \return The AST_DEVICE_ integer value
+ */
+enum ast_device_state ast_devstate_val(const char *val);
+
+/*!
+ * \brief Search the Channels by Name
+ *
+ * \param device like a dial string
+ *
+ * Search the Device in active channels by compare the channel name against
+ * the device name. Compared are only the first chars to the first '-' char.
+ *
+ * \retval AST_DEVICE_UNKNOWN if no channel found
+ * \retval AST_DEVICE_INUSE if a channel is found
+ */
+enum ast_device_state ast_parse_device_state(const char *device);
+
+/*!
+ * \brief Asks a channel for device state
+ *
+ * \param device like a dial string
+ *
+ * Asks a channel for device state, data is normally a number from a dial string
+ * used by the low level module
+ * Tries the channel device state callback if not supported search in the
+ * active channels list for the device.
+ *
+ * \retval an AST_DEVICE_??? state
+ * \retval -1 on failure
+ */
+enum ast_device_state ast_device_state(const char *device);
+
+/*!
+ * \brief Tells Asterisk the State for Device is changed
+ *
+ * \param state the new state of the device
+ * \param fmt device name like a dial string with format parameters
+ *
+ * The new state of the device will be sent off to any subscribers
+ * of device states. It will also be stored in the internal event
+ * cache.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+/*!
+ * \brief Tells Asterisk the State for Device is changed
+ *
+ * \param state the new state of the device
+ * \param device device name like a dial string with format parameters
+ *
+ * The new state of the device will be sent off to any subscribers
+ * of device states. It will also be stored in the internal event
+ * cache.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_devstate_changed_literal(enum ast_device_state state, const char *device);
+
+/*!
+ * \brief Tells Asterisk the State for Device is changed
+ *
+ * \param fmt device name like a dial string with format parameters
+ *
+ * Asterisk polls the new extension states and calls the registered
+ * callbacks for the changed extensions
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * \note This is deprecated in favor of ast_devstate_changed()
+ */
+int ast_device_state_changed(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+/*!
+ * \brief Tells Asterisk the State for Device is changed
+ *
+ * \param device device name like a dial string
+ *
+ * Asterisk polls the new extension states and calls the registered
+ * callbacks for the changed extensions
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * \note This is deprecated in favor of ast_devstate_changed_literal()
+ */
+int ast_device_state_changed_literal(const char *device);
+
+/*!
+ * \brief Add device state provider
+ *
+ * \param label to use in hint, like label:object
+ * \param callback Callback
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback);
+
+/*!
+ * \brief Remove device state provider
+ *
+ * \param label to use in hint, like label:object
+ *
+ * \retval -1 on failure
+ * \retval 0 on success
+ */
+int ast_devstate_prov_del(const char *label);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_DEVICESTATE_H */
diff --git a/trunk/include/asterisk/dial.h b/trunk/include/asterisk/dial.h
new file mode 100644
index 000000000..9a2478070
--- /dev/null
+++ b/trunk/include/asterisk/dial.h
@@ -0,0 +1,168 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Dialing API
+ */
+
+#ifndef _ASTERISK_DIAL_H
+#define _ASTERISK_DIAL_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief Main dialing structure. Contains global options, channels being dialed, and more! */
+struct ast_dial;
+
+/*! \brief Dialing channel structure. Contains per-channel dialing options, asterisk channel, and more! */
+struct ast_dial_channel;
+
+typedef void (*ast_dial_state_callback)(struct ast_dial *);
+
+/*! \brief List of options that are applicable either globally or per dialed channel */
+enum ast_dial_option {
+ AST_DIAL_OPTION_RINGING, /*!< Always indicate ringing to caller */
+ AST_DIAL_OPTION_ANSWER_EXEC, /*!< Execute application upon answer in async mode */
+ AST_DIAL_OPTION_MUSIC, /*!< Play music on hold instead of ringing to the calling channel */
+ AST_DIAL_OPTION_DISABLE_CALL_FORWARDING, /*!< Disable call forwarding on channels */
+ AST_DIAL_OPTION_MAX, /*!< End terminator -- must always remain last */
+};
+
+/*! \brief List of return codes for dial run API calls */
+enum ast_dial_result {
+ AST_DIAL_RESULT_INVALID, /*!< Invalid options were passed to run function */
+ AST_DIAL_RESULT_FAILED, /*!< Attempts to dial failed before reaching critical state */
+ AST_DIAL_RESULT_TRYING, /*!< Currently trying to dial */
+ AST_DIAL_RESULT_RINGING, /*!< Dial is presently ringing */
+ AST_DIAL_RESULT_PROGRESS, /*!< Dial is presently progressing */
+ AST_DIAL_RESULT_PROCEEDING, /*!< Dial is presently proceeding */
+ AST_DIAL_RESULT_ANSWERED, /*!< A channel was answered */
+ AST_DIAL_RESULT_TIMEOUT, /*!< Timeout was tripped, nobody answered */
+ AST_DIAL_RESULT_HANGUP, /*!< Caller hung up */
+ AST_DIAL_RESULT_UNANSWERED, /*!< Nobody answered */
+};
+
+/*! \brief New dialing structure
+ * \note Create a dialing structure
+ * \return Returns a calloc'd ast_dial structure, NULL on failure
+ */
+struct ast_dial *ast_dial_create(void);
+
+/*! \brief Append a channel
+ * \note Appends a channel to a dialing structure
+ * \return Returns channel reference number on success, -1 on failure
+ */
+int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device);
+
+/*! \brief Execute dialing synchronously or asynchronously
+ * \note Dials channels in a dial structure.
+ * \return Returns dial result code. (TRYING/INVALID/FAILED/ANSWERED/TIMEOUT/UNANSWERED).
+ */
+enum ast_dial_result ast_dial_run(struct ast_dial *dial, struct ast_channel *chan, int async);
+
+/*! \brief Return channel that answered
+ * \note Returns the Asterisk channel that answered
+ * \param dial Dialing structure
+ */
+struct ast_channel *ast_dial_answered(struct ast_dial *dial);
+
+/*! \brief Return state of dial
+ * \note Returns the state of the dial attempt
+ * \param dial Dialing structure
+ */
+enum ast_dial_result ast_dial_state(struct ast_dial *dial);
+
+/*! \brief Cancel async thread
+ * \note Cancel a running async thread
+ * \param dial Dialing structure
+ */
+enum ast_dial_result ast_dial_join(struct ast_dial *dial);
+
+/*! \brief Hangup channels
+ * \note Hangup all active channels
+ * \param dial Dialing structure
+ */
+void ast_dial_hangup(struct ast_dial *dial);
+
+/*! \brief Destroys a dialing structure
+ * \note Cancels dialing and destroys (free's) the given ast_dial structure
+ * \param dial Dialing structure to free
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_destroy(struct ast_dial *dial);
+
+/*! \brief Enables an option globally
+ * \param dial Dial structure to enable option on
+ * \param option Option to enable
+ * \param data Data to pass to this option (not always needed)
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_global_enable(struct ast_dial *dial, enum ast_dial_option option, void *data);
+
+/*! \brief Enables an option per channel
+ * \param dial Dial structure
+ * \param num Channel number to enable option on
+ * \param option Option to enable
+ * \param data Data to pass to this option (not always needed)
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_enable(struct ast_dial *dial, int num, enum ast_dial_option option, void *data);
+
+/*! \brief Disables an option globally
+ * \param dial Dial structure to disable option on
+ * \param option Option to disable
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_global_disable(struct ast_dial *dial, enum ast_dial_option option);
+
+/*! \brief Disables an option per channel
+ * \param dial Dial structure
+ * \param num Channel number to disable option on
+ * \param option Option to disable
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_disable(struct ast_dial *dial, int num, enum ast_dial_option option);
+
+/*! \brief Set a callback for state changes
+ * \param dial The dial structure to watch for state changes
+ * \param callback the callback
+ * \return nothing
+ */
+void ast_dial_set_state_callback(struct ast_dial *dial, ast_dial_state_callback callback);
+
+/*! \brief Set the maximum time (globally) allowed for trying to ring phones
+ * \param dial The dial structure to apply the time limit to
+ * \param timeout Maximum time allowed
+ * \return nothing
+ */
+void ast_dial_set_global_timeout(struct ast_dial *dial, int timeout);
+
+/*! \brief Set the maximum time (per channel) allowed for trying to ring the phone
+ * \param dial The dial structure the channel belongs to
+ * \param num Channel number to set timeout on
+ * \param timeout Maximum time allowed
+ * \return nothing
+ */
+void ast_dial_set_timeout(struct ast_dial *dial, int num, int timeout);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_DIAL_H */
diff --git a/trunk/include/asterisk/dlfcn-compat.h b/trunk/include/asterisk/dlfcn-compat.h
new file mode 100644
index 000000000..2ce827a6a
--- /dev/null
+++ b/trunk/include/asterisk/dlfcn-compat.h
@@ -0,0 +1,88 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * 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.
+ */
+
+/*
+Copyright (c) 2002 Jorge Acereda <jacereda@users.sourceforge.net> &
+ Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Portions may be copyright others, see the AUTHORS file included with this
+distribution.
+
+Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef _DLFCN_H_
+#define _DLFCN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__GNUC__) && __GNUC__ > 3
+#define dl_restrict __restrict
+#else
+#define dl_restrict
+#endif
+/*
+ * Structure filled in by dladdr().
+ */
+
+typedef struct dl_info {
+ const char *dli_fname; /* Pathname of shared object */
+ void *dli_fbase; /* Base address of shared object */
+ const char *dli_sname; /* Name of nearest symbol */
+ void *dli_saddr; /* Address of nearest symbol */
+} Dl_info;
+
+extern void * dlopen(const char *path, int mode);
+extern void * dlsym(void * dl_restrict handle, const char * dl_restrict symbol);
+extern const char * dlerror(void);
+extern int dlclose(void * handle);
+extern int dladdr(const void * dl_restrict, Dl_info * dl_restrict);
+
+#define RTLD_LAZY 0x1
+#define RTLD_NOW 0x2
+#define RTLD_LOCAL 0x4
+#define RTLD_GLOBAL 0x8
+#define RTLD_NOLOAD 0x10
+#define RTLD_NODELETE 0x80
+
+/*
+ * Special handle arguments for dlsym().
+ */
+#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */
+#define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DLFCN_H_ */
diff --git a/trunk/include/asterisk/dns.h b/trunk/include/asterisk/dns.h
new file mode 100644
index 000000000..64cf68c10
--- /dev/null
+++ b/trunk/include/asterisk/dns.h
@@ -0,0 +1,39 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Written by Thorsten Lockert <tholo@trollphone.org>
+ *
+ * Funding provided by Troll Phone Networks AS
+ *
+ * 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 DNS support for Asterisk
+ * \author Thorsten Lockert <tholo@trollphone.org>
+ */
+
+#ifndef _ASTERISK_DNS_H
+#define _ASTERISK_DNS_H
+
+/*! \brief Perform DNS lookup (used by DNS, enum and SRV lookups)
+ \param context
+ \param dname Domain name to lookup (host, SRV domain, TXT record name)
+ \param class Record Class (see "man res_search")
+ \param type Record type (see "man res_search")
+ \param callback Callback function for handling DNS result
+ \note Asterisk DNS is synchronus at this time. This means that if your DNS
+ services does not work, Asterisk may lock while waiting for response.
+*/
+int ast_search_dns(void *context, const char *dname, int class, int type,
+ int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer));
+
+#endif /* _ASTERISK_DNS_H */
diff --git a/trunk/include/asterisk/dnsmgr.h b/trunk/include/asterisk/dnsmgr.h
new file mode 100644
index 000000000..33b9556e7
--- /dev/null
+++ b/trunk/include/asterisk/dnsmgr.h
@@ -0,0 +1,62 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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 Background DNS update manager
+ */
+
+#ifndef _ASTERISK_DNSMGR_H
+#define _ASTERISK_DNSMGR_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/network.h"
+
+struct ast_dnsmgr_entry;
+
+struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result);
+
+void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry);
+
+int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr);
+
+/*!
+ * \brief Force a refresh of a dnsmgr entry
+ *
+ * \retval non-zero if the result is different than the previous result
+ * \retval zero if the result is the same as the previous result
+ */
+int ast_dnsmgr_refresh(struct ast_dnsmgr_entry *entry);
+
+/*!
+ * \brief Check is see if a dnsmgr entry has changed
+ *
+ * \retval non-zero if the dnsmgr entry has changed since the last call to
+ * this function
+ * \retval zero if the dnsmgr entry has not changed since the last call to
+ * this function
+ */
+int ast_dnsmgr_changed(struct ast_dnsmgr_entry *entry);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif /* c_plusplus */
+
+#endif /* ASTERISK_DNSMGR_H */
diff --git a/trunk/include/asterisk/doxyref.h b/trunk/include/asterisk/doxyref.h
new file mode 100644
index 000000000..6fbe462ec
--- /dev/null
+++ b/trunk/include/asterisk/doxyref.h
@@ -0,0 +1,563 @@
+/*
+ * 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 This file generates Doxygen pages from files in the /doc
+ directory of the Asterisk source code tree
+ */
+
+/* The following is for Doxygen Developer's documentation generated
+ * by running "make progdocs" with doxygen installed on your
+ * system.
+ */
+/*! \page DevDoc Asterisk Developer's Documentation - appendices
+ * \arg \ref CodeGuide : The must-read document for all developer's
+ * \arg \ref AstAPI
+ * \arg \ref Def_Channel : What's a channel, anyway?
+ * \arg \ref channel_drivers : Existing channel drivers
+ * \arg \ref AstDebug : Hints on debugging
+ * \arg \ref AstAMI : The Call management socket API
+ * \arg \ref AstARA : A generic data storage and retrieval API for Asterisk
+ * \arg \ref AstDUNDi : A way to find phone services dynamically by using the DUNDi protocol
+ * \arg \ref AJI_intro : The Asterisk Jabber Interface
+ * \arg \ref AstCDR
+ * \arg \ref AstREADME
+ * \arg \ref AstVar
+ * \arg \ref AstVideo
+ * \arg \ref AstENUM : The IETF way to redirect from phone numbers to VoIP calls
+ * \arg \ref AstHTTP
+ * \arg \ref AstSpeech
+ * \arg \ref ConfigFiles
+ * \arg \ref SoundFiles included in the Asterisk distribution
+ * \arg \ref AstCREDITS : A Thank You to contributors
+ * \arg \ref extref
+ \n\n
+ * \section weblinks Web sites
+ * \arg \b Main: Asterisk Developer's website http://www.asterisk.org/developers/
+ * \arg \b Bugs: The Issue tracker http://bugs.digium.com
+ * \arg \b Lists: List server http://lists.digium.com
+ * \arg \b Wiki: The Asterisk Wiki http://www.voip-info.org
+ * \arg \b Docs: The Asterisk Documentation Project http://www.asteriskdocs.org
+ * \arg \b Digium: The Asterisk company http://www.digium.com
+ *
+ */
+
+/*! \page CodeGuide Coding Guidelines
+ * \section Coding Guidelines
+ * This file is in the /doc directory in your Asterisk source tree.
+ * Make sure to stay up to date with the latest guidelines.
+ * \verbinclude CODING-GUIDELINES
+ */
+
+/*! \page AstAPI Asterisk API
+ * \section Asteriskapi Asterisk API
+ * Some generic documents on the Asterisk architecture
+ *
+ * \arg \ref AstThreadStorage
+ * \arg \ref DataStores
+ * \arg \ref AstExtState
+ *
+ * \subsection model_txt Generic Model
+ * \verbinclude model.txt
+ * \subsection channel_txt Channels
+ * \arg See \ref Def_Channel
+ */
+
+/*! \page AstDebug Debugging
+ * \section debug Debugging
+ * \verbinclude backtrace.txt
+ */
+
+/*! \page AstSpeech The Generic Speech Recognition API
+ * \section debug The Generic Speech Recognition API
+ * \verbinclude speechrec.txt
+ */
+
+/*! \page DataStores Channel Data Stores
+ * \section debug Channel Data Stores
+ * \verbinclude datastores.txt
+ */
+
+/*! \page AstAMI AMI - The Manager Interface
+ * \section ami AMI - The manager Interface
+ * \arg \link Config_ami Configuration file \endlink
+ * \arg \ref manager.c
+ * \verbinclude manager.txt
+ */
+
+/*! \page AstARA ARA - The Asterisk Realtime Interface
+ * \section realtime ARA - a generic API to storage and retrieval
+ * Implemented in \ref config.c
+ * Implemented in \ref pbx_realtime.c
+ * \verbinclude realtime.txt
+ * \verbinclude extconfig.txt
+ */
+
+/*! \page AstDUNDi DUNDi
+DUNDi is a peer-to-peer system for locating Internet gateways to telephony services. Unlike traditional centralized services (such as the remarkably simple and concise ENUM standard), DUNDi is fully-distributed with no centralized authority whatsoever.
+
+DUNDi is not itself a Voice-over IP signaling or media protocol. Instead, it publishes routes which are in turn accessed via industry standard protocols such as IAX, SIP and H.323.
+
+ \par References
+ \arg DUNDi is documented at http://www.dundi.com
+ \arg Implemented in \ref pbx_dundi.c and \ref dundi-parser.c
+ \arg Configuration in \link Config_dun dundi.conf \endlink
+ */
+
+/*! \page AstCDR CDR - Call Data Records and billing
+ * \section cdr Call Data Records
+ * \par See also
+ * \arg \ref cdr.c
+ * \arg \ref cdr_drivers
+ * \arg \ref Config_cdr CDR configuration files
+ *
+ * \verbinclude cdrdriver.txt
+ */
+
+/*! \page AstREADME README - the general administrator introduction
+ * \verbinclude README
+ */
+
+/*! \page AstCREDITS CREDITS
+ * \verbinclude CREDITS
+ */
+
+/*! \page AstVideo Video support in Asterisk
+ * \section sectAstVideo Video support in Asterisk
+ * \verbinclude video.txt
+ */
+
+/*! \page AstVar Globally predefined channel variables
+ * \section globchan Globally predefined channel variables
+ *
+ * More and more of these variables are being replaced by dialplan functions.
+ * Some still exist though and some that does still exist needs to move to
+ * dialplan functions.
+ *
+ * See also
+ * - \ref pbx_retrieve_variable()
+ * - \ref AstChanVar
+ *
+ * \verbinclude channelvariables.txt
+
+ */
+
+/*! \page AstChanVar Asterisk Dialplan Variables
+ * Asterisk Dialplan variables are divided into three groups:
+ * - Predefined global variables, handled by the PBX core
+ * - Global variables, that exist for the duration of the pbx execution
+ * - Channel variables, that exist during a channel
+ *
+ * Global variables are reachable in all channels, all of the time.
+ * Channel variables are only reachable within the channel.
+ *
+ * For more information on the predefined variables, see \ref AstVar
+ *
+ * Global and Channel variables:
+ * - Names are Case insensitive
+ * - Names that start with a character, but are alphanumeric
+ * - Global variables are defined and reached with the GLOBAL() dialplan function
+ * and the set application, like
+ *
+ * exten => 1234,1,set(GLOBAL(myvariable)=tomteluva)
+ *
+ * - \ref func_global.c
+ *
+ * - Channel variables are defined with the set() dialplan application
+ *
+ * exten => 1234,1,set(xmasattribute=tomtegröt)
+ *
+ * - Some channels also supports setting channel variables with the \b setvar=
+ * configuraiton option for a device or line.
+ *
+ * \section AstChanVar_globalvars Global Variables
+ * Global variables can also be set in the [globals] section of extensions.conf. The
+ * setting \b clearglobalvars in extensions.conf [general] section affects whether
+ * or not the global variables defined in \b globals are reset at dialplan reload.
+ *
+ * There are CLI commands to change and read global variables. This can be handy
+ * to reset counters at midnight from an external script.
+ *
+ * \section AstChanVar_devnotes Developer notes
+ * Variable handling is managed within \ref pbx.c
+ * You need to include pbx.h to reach these functions.
+ * - \ref pbx_builtin_setvar_helper()
+ * - \ref pbx_builtin_getvar_helper()
+ *
+ * The variables is a linked list stored in the channel data structure
+ * with the list starting at varshead in struct ast_channel
+ *
+ *
+ */
+
+/*! \page AstENUM ENUM
+ * \section enumreadme ENUM
+ * \arg Configuration: \ref Config_enum
+ * \arg \ref enum.c
+ * \arg \ref func_enum.c
+ *
+ * \verbinclude enum.txt
+ */
+
+/*! \page ConfigFiles Configuration files
+ * \section config Main configuration files
+ * \arg \link Config_ast asterisk.conf - the main configuration file \endlink
+ * \arg \link Config_ext extensions.conf - The Dial Plan \endlink
+ * \arg \link Config_mod modules.conf - which modules to load and not to load \endlink
+ * \arg \link Config_fea features.conf - call features (transfer, parking, etc) \endlink
+ * \section chanconf Channel configuration files
+ * \arg \link Config_iax IAX2 configuration \endlink
+ * \arg \link Config_sip SIP configuration \endlink
+ * \arg \link Config_mgcp MGCP configuration \endlink
+ * \arg \link Config_rtp RTP configuration \endlink
+ * \arg \link Config_zap Zaptel configuration \endlink
+ * \arg \link Config_oss OSS (sound card) configuration \endlink
+ * \arg \link Config_alsa ALSA (sound card) configuration \endlink
+ * \arg \link Config_agent Agent (proxy channel) configuration \endlink
+ * \arg \link Config_misdn MISDN Experimental ISDN BRI channel configuration \endlink
+ * \arg \link Config_h323 H.323 configuration \endlink
+ * \section appconf Application configuration files
+ * \arg \link Config_mm Meetme (conference bridge) configuration \endlink
+ * \arg \link Config_qu Queue system configuration \endlink
+ * \arg \link Config_vm Voicemail configuration \endlink
+ * \arg \link Config_followme Followme configuration \endlink
+ * \section cdrconf CDR configuration files
+ * \arg \link Config_cdr CDR configuration \endlink
+ * \arg \link cdr_custom Custom CDR driver configuration \endlink
+ * \arg \link cdr_ami Manager CDR driver configuration \endlink
+ * \arg \link cdr_odbc ODBC CDR driver configuration \endlink
+ * \arg \link cdr_pgsql PostgreSQL CDR driver configuration \endlink
+ * \arg \link cdr_sqlite SQLite CDR driver configuration \endlink
+ * \arg \link cdr_tds FreeTDS CDR driver configuration (Microsoft SQL Server) \endlink
+ * \section miscconf Miscellenaous configuration files
+ * \arg \link Config_adsi ADSI configuration \endlink
+ * \arg \link Config_ami AMI - Manager configuration \endlink
+ * \arg \link Config_ara Realtime configuration \endlink
+ * \arg \link Config_codec Codec configuration \endlink
+ * \arg \link Config_dun DUNDi configuration \endlink
+ * \arg \link Config_enum ENUM configuration \endlink
+ * \arg \link Config_moh Music on Hold configuration \endlink
+ * \arg \link Config_vm Voicemail configuration \endlink
+ * \arg \link res_config_sqlite SQLite Resource driver configuration \endlink
+ */
+
+/*! \page Config_ast Asterisk.conf
+ * \verbinclude asterisk-conf.txt
+ */
+/*! \page Config_mod Modules configuration
+ * All res_ resource modules are loaded with globals on, which means
+ * that non-static functions are callable from other modules.
+ *
+ * If you want your non res_* module to export functions to other modules
+ * you have to include it in the [global] section.
+ * \verbinclude modules.conf.sample
+ */
+
+/*! \page Config_fea Call features configuration
+ * \par See also
+ * \arg \ref res_features.c : Call feature implementation
+ * \section featconf features.conf
+ * \verbinclude features.conf.sample
+ */
+
+/*! \page Config_followme Followme: An application for simple follow-me calls
+ * \section followmeconf Followme.conf
+ * - See app_followme.c
+ * \verbinclude followme.conf.sample
+ */
+
+/*! \page Config_ext Extensions.conf - the Dial Plan
+ * \section dialplan Extensions.conf
+ * \verbinclude extensions.conf.sample
+ */
+
+/*! \page Config_iax IAX2 configuration
+ * IAX2 is implemented in \ref chan_iax2.c
+ * \arg \link Config_iax iax.conf Configuration file example \endlink
+ * \section iaxreadme IAX readme file
+ * \verbinclude iax.txt
+ * \section Config_iax IAX Configuration example
+ * \verbinclude iax.conf.sample
+ * \section iaxjitter IAX Jitterbuffer information
+ * \verbinclude jitterbuffer.txt
+ */
+
+/*! \page Config_iax IAX configuration
+ * \arg Implemented in \ref chan_iax2.c
+ * \section iaxconf iax.conf
+ * \verbinclude iax.conf.sample
+ */
+
+/*! \page Config_sip SIP configuration
+ * Also see \ref Config_rtp RTP configuration
+ * \arg Implemented in \ref chan_sip.c
+ * \section sipconf sip.conf
+ * \verbinclude sip.conf.sample
+ *
+ * \arg \b Back \ref chanconf
+ */
+
+/*! \page Config_mgcp MGCP configuration
+ * Also see \ref Config_rtp RTP configuration
+ * \arg Implemented in \ref chan_mgcp.c
+ * \section mgcpconf mgcp.conf
+ * \verbinclude mgcp.conf.sample
+ */
+
+/*! \page README_misdn MISDN documentation
+ * \arg See \ref Config_misdn
+ * \section mISDN configuration
+ * \verbinclude misdn.txt
+ */
+
+/*! \page Config_misdn MISDN configuration
+ * \arg Implemented in \ref chan_misdn.c
+ * \arg \ref README_misdn
+ * \arg See the mISDN home page: http://www.isdn4linux.de/mISDN/
+ * \section misdnconf misdn.conf
+ * \verbinclude misdn.conf.sample
+ */
+
+/*! \page Config_vm VoiceMail configuration
+ * \section vmconf voicemail.conf
+ * \arg Implemented in \ref app_voicemail.c
+ * \verbinclude voicemail.conf.sample
+ */
+
+/*! \page Config_zap Zaptel configuration
+ * \section zapconf zapata.conf
+ * \arg Implemented in \ref chan_zap.c
+ * \verbinclude zapata.conf.sample
+ */
+
+/*! \page Config_h323 H.323 channel driver information
+ * This is the configuration of the H.323 channel driver within the Asterisk
+ * distribution. There's another one, called OH323, in asterisk-addons
+ * \arg Implemented in \ref chan_h323.c
+ * \section h323conf h323.conf
+ * \ref chan_h323.c
+ */
+
+/*! \page Config_oss OSS configuration
+ * \section ossconf oss.conf
+ * \arg Implemented in \ref chan_oss.c
+ * \verbinclude oss.conf.sample
+ */
+
+/*! \page Config_alsa ALSA configuration
+ * \section alsaconf alsa.conf
+ * \arg Implemented in \ref chan_alsa.c
+ * \verbinclude alsa.conf.sample
+ */
+
+/*! \page Config_agent Agent configuration
+ * \section agentconf agents.conf
+ * The agent channel is a proxy channel for queues
+ * \arg Implemented in \ref chan_agent.c
+ * \verbinclude agents.conf.sample
+ */
+
+/*! \page Config_rtp RTP configuration
+ * \arg Implemented in \ref rtp.c
+ * Used in \ref chan_sip.c and \ref chan_mgcp.c (and various H.323 channels)
+ * \section rtpconf rtp.conf
+ * \verbinclude rtp.conf.sample
+ */
+
+/*! \page Config_dun DUNDi Configuration
+ * \arg See also \ref AstDUNDi
+ * \section dundiconf dundi.conf
+ * \verbinclude dundi.conf.sample
+ */
+
+/*! \page Config_enum ENUM Configuration
+ * \section enumconf enum.conf
+ * \arg See also \ref enumreadme
+ * \arg Implemented in \ref func_enum.c and \ref enum.c
+ * \verbinclude enum.conf.sample
+ */
+
+/*! \page cdr_custom Custom CDR Configuration
+ * \par See also
+ * \arg \ref cdrconf
+ * \arg \ref cdr_custom.c
+ * \verbinclude cdr_custom.conf.sample
+ */
+
+/*! \page cdr_ami Manager CDR driver configuration
+ * \par See also
+ * \arg \ref cdrconf
+ * \arg \ref AstAMI
+ * \arg \ref cdr_manager.c
+ * \verbinclude cdr_manager.conf.sample
+ */
+
+/*! \page cdr_odbc ODBC CDR driver configuration
+ * \arg See also \ref cdrconf
+ * \arg \ref cdr_odbc.c
+ * \verbinclude cdr_odbc.conf.sample
+ * See also:
+ * \arg http://www.unixodbc.org
+ */
+
+/*! \page cdr_pgsql PostgreSQL CDR driver configuration
+ * \arg See also \ref cdrconf
+ * \arg \ref cdr_pgsql.c
+ * See also:
+ * \arg http://www.postgresql.org
+ * \verbinclude cdr_pgsql.conf.sample
+ */
+
+/*! \page cdr_sqlite SQLite CDR driver configuration
+ * \arg See also \ref cdrconf
+ * \arg \ref cdr_sqlite.c
+ * See also:
+ * \arg http://www.sqlite.org
+ */
+
+/*! \page cdr_tds FreeTDS CDR driver configuration
+ * \arg See also \ref cdrconf
+ * See also:
+ * \arg http://www.freetds.org
+ * \verbinclude cdr_tds.conf.sample
+ */
+
+/*! \page Config_cdr CDR configuration
+ * \par See also
+ * \arg \ref cdr_drivers
+ * \arg \link Config_cdr CDR configuration \endlink
+ * \arg \link cdr_custom Custom CDR driver configuration \endlink
+ * \arg \link cdr_ami Manager CDR driver configuration \endlink
+ * \arg \link cdr_odbc ODBC CDR driver configuration \endlink
+ * \arg \link cdr_pgsql PostgreSQL CDR driver configuration \endlink
+ * \arg \link cdr_sqlite SQLite CDR driver configuration \endlink
+ * \arg \link cdr_tds FreeTDS CDR driver configuration (Microsoft SQL Server) \endlink
+ * \verbinclude cdr.conf.sample
+ */
+
+/*! \page Config_moh Music on Hold Configuration
+ * \arg Implemented in \ref res_musiconhold.c
+ * \section mohconf musiconhold.conf
+ * \verbinclude musiconhold.conf.sample
+ */
+
+/*! \page Config_adsi ADSI Configuration
+ * \section adsiconf adsi.conf
+ * \verbinclude adsi.conf.sample
+ */
+
+/*! \page Config_codec CODEC Configuration
+ * \section codecsconf codecs.conf
+ * \verbinclude codecs.conf.sample
+ */
+
+/*! \page Config_ara REALTIME Configuration
+ * \arg See also: \arg \link AstARA \endlink
+ * \section extconf extconfig.conf
+ * \verbinclude extconfig.conf.sample
+ */
+
+/*! \page Config_ami AMI configuration
+ * \arg See also: \arg \link AstAMI \endlink
+ * \section amiconf manager.conf
+ * \verbinclude manager.conf.sample
+ */
+
+/*! \page Config_qu ACD - Queue system configuration
+ * \arg Implemented in \ref app_queue.c
+ * \section quconf queues.conf
+ * \verbinclude queues.conf.sample
+ */
+
+/*! \page Config_mm Meetme - The conference bridge configuration
+ * \arg Implemented in \ref app_meetme.c
+ * \section mmconf meetme.conf
+ * \verbinclude meetme.conf.sample
+ */
+
+/*! \page SoundFiles Sound files
+ * \section SecSound Asterisk Sound files
+ * Asterisk includes a large number of sound files. Many of these
+ * are used by applications and demo scripts within asterisk.
+ *
+ * Additional sound files are available in the asterisk-addons
+ * repository on svn.digium.com
+ */
+
+/*! \addtogroup cdr_drivers Module: CDR Drivers
+ * \section CDR_generic Asterisk CDR Drivers
+ * \brief CDR drivers are loaded dynamically, each loaded CDR driver produce a billing record for each call.
+ * \arg \ref Config_mod "Modules Configuration"
+ * \arg \ref Config_cdr "CDR Configuration"
+ */
+
+
+/*! \addtogroup channel_drivers Module: Asterisk Channel Drivers
+ * \section channel_generic Asterisk Channel Drivers
+ * \brief Channel drivers are loaded dynamically.
+ * \arg \ref Config_mod "Modules Configuration"
+ */
+
+/*! \addtogroup applications Module: Dial plan applications
+ * \section app_generic Asterisk Dial Plan Applications
+ * \brief Applications support the dialplan. They register dynamically with \see ast_register_application() and unregister with \see ast_unregister_application()
+ * \par See also
+ * \arg \ref functions
+ *
+ */
+
+/*! \addtogroup functions Module: Dial plan functions
+ * \section func_generic Asterisk Dial Plan Functions
+ * \brief Functions support the dialplan. They do not change any property of a channel
+ * or touch a channel in any way.
+ * \par See also
+ * \arg \ref applications
+ *
+ */
+
+/*! \addtogroup codecs Module: Codecs
+ * \section codec_generic Asterisk Codec Modules
+ * Codecs are referenced in configuration files by name
+ * \par See also
+ * \arg \ref formats
+ *
+ */
+
+/*! \addtogroup formats Module: Media File Formats
+ * \section codec_generic Asterisk Format drivers
+ * Formats are modules that read or write media files to disk.
+ * \par See also
+ * \arg \ref codecs
+ */
+
+/*! \page AstHTTP AMI over HTTP support
+ * The http.c file includes support for manager transactions over
+ * http.
+ * \section ami AMI - The manager Interface
+ * \arg \link Config_ami Configuration file \endlink
+ */
+
+/*! \page res_config_sqlite SQLite Resource driver configuration
+ * \arg Implemented in \ref res_config_sqlite.c
+ * \arg Configuration file:
+ * \verbinclude res_config_sqlite.conf
+ * \arg SQL tables:
+ * \verbinclude res_config_sqlite.txt
+ * \arg See also:
+ * http://www.sqlite.org
+ */
diff --git a/trunk/include/asterisk/dsp.h b/trunk/include/asterisk/dsp.h
new file mode 100644
index 000000000..2664b9b3e
--- /dev/null
+++ b/trunk/include/asterisk/dsp.h
@@ -0,0 +1,111 @@
+/*
+ * 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 Convenient Signal Processing routines
+ */
+
+#ifndef _ASTERISK_DSP_H
+#define _ASTERISK_DSP_H
+
+#define DSP_FEATURE_SILENCE_SUPPRESS (1 << 0)
+#define DSP_FEATURE_BUSY_DETECT (1 << 1)
+#define DSP_FEATURE_DTMF_DETECT (1 << 3)
+#define DSP_FEATURE_FAX_DETECT (1 << 4)
+
+#define DSP_DIGITMODE_DTMF 0 /*!< Detect DTMF digits */
+#define DSP_DIGITMODE_MF 1 /*!< Detect MF digits */
+
+#define DSP_DIGITMODE_NOQUELCH (1 << 8) /*!< Do not quelch DTMF from in-band */
+#define DSP_DIGITMODE_MUTECONF (1 << 9) /*!< Mute conference */
+#define DSP_DIGITMODE_MUTEMAX (1 << 10) /*!< Delay audio by a frame to try to extra quelch */
+#define DSP_DIGITMODE_RELAXDTMF (1 << 11) /*!< "Radio" mode (relaxed DTMF) */
+
+#define DSP_PROGRESS_TALK (1 << 16) /*!< Enable talk detection */
+#define DSP_PROGRESS_RINGING (1 << 17) /*!< Enable calling tone detection */
+#define DSP_PROGRESS_BUSY (1 << 18) /*!< Enable busy tone detection */
+#define DSP_PROGRESS_CONGESTION (1 << 19) /*!< Enable congestion tone detection */
+#define DSP_FEATURE_CALL_PROGRESS (DSP_PROGRESS_TALK | DSP_PROGRESS_RINGING | DSP_PROGRESS_BUSY | DSP_PROGRESS_CONGESTION)
+
+#define DSP_TONE_STATE_SILENCE 0
+#define DSP_TONE_STATE_RINGING 1
+#define DSP_TONE_STATE_DIALTONE 2
+#define DSP_TONE_STATE_TALKING 3
+#define DSP_TONE_STATE_BUSY 4
+#define DSP_TONE_STATE_SPECIAL1 5
+#define DSP_TONE_STATE_SPECIAL2 6
+#define DSP_TONE_STATE_SPECIAL3 7
+#define DSP_TONE_STATE_HUNGUP 8
+
+struct ast_dsp;
+
+struct ast_dsp *ast_dsp_new(void);
+void ast_dsp_free(struct ast_dsp *dsp);
+
+/*! \brief Set threshold value for silence */
+void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold);
+
+/*! \brief Set number of required cadences for busy */
+void ast_dsp_set_busy_count(struct ast_dsp *dsp, int cadences);
+
+/*! \brief Set expected lengths of the busy tone */
+void ast_dsp_set_busy_pattern(struct ast_dsp *dsp, int tonelength, int quietlength);
+
+/*! \brief Scans for progress indication in audio */
+int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf);
+
+/*! \brief Set zone for doing progress detection */
+int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone);
+
+/*! \brief Return AST_FRAME_NULL frames when there is silence, AST_FRAME_BUSY on
+ busies, and call progress, all dependent upon which features are enabled */
+struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *inf);
+
+/*! \brief Return non-zero if this is silence. Updates "totalsilence" with the total
+ number of seconds of silence */
+int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence);
+
+/*! \brief Return non-zero if historically this should be a busy, request that
+ ast_dsp_silence has already been called */
+int ast_dsp_busydetect(struct ast_dsp *dsp);
+
+/*! \brief Return non-zero if DTMF hit was found */
+int ast_dsp_digitdetect(struct ast_dsp *dsp, struct ast_frame *f);
+
+/*! \brief Reset total silence count */
+void ast_dsp_reset(struct ast_dsp *dsp);
+
+/*! \brief Reset DTMF detector */
+void ast_dsp_digitreset(struct ast_dsp *dsp);
+
+/*! \brief Select feature set */
+void ast_dsp_set_features(struct ast_dsp *dsp, int features);
+
+/*! \brief Get pending DTMF/MF digits */
+int ast_dsp_getdigits(struct ast_dsp *dsp, char *buf, int max);
+
+/*! \brief Set digit mode */
+int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode);
+
+/*! \brief Get tstate (Tone State) */
+int ast_dsp_get_tstate(struct ast_dsp *dsp);
+
+/*! \brief Get tcount (Threshold counter) */
+int ast_dsp_get_tcount(struct ast_dsp *dsp);
+
+#endif /* _ASTERISK_DSP_H */
diff --git a/trunk/include/asterisk/dundi.h b/trunk/include/asterisk/dundi.h
new file mode 100644
index 000000000..e588338ae
--- /dev/null
+++ b/trunk/include/asterisk/dundi.h
@@ -0,0 +1,231 @@
+/*
+ * 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 Distributed Universal Number Discovery (DUNDi)
+ * See also \arg \ref AstDUNDi
+ */
+
+#ifndef _ASTERISK_DUNDI_H
+#define _ASTERISK_DUNDI_H
+
+#include "asterisk/channel.h"
+
+#define DUNDI_PORT 4520
+
+/*!\brief A DUNDi Entity ID is essentially a MAC address, brief and unique */
+struct _dundi_eid {
+ unsigned char eid[6];
+} __attribute__ ((__packed__));
+
+typedef struct _dundi_eid dundi_eid;
+
+struct dundi_hdr {
+ unsigned short strans; /*!< Source transaction */
+ unsigned short dtrans; /*!< Destination transaction */
+ unsigned char iseqno; /*!< Next expected incoming sequence number */
+ unsigned char oseqno; /*!< Outgoing sequence number */
+ unsigned char cmdresp; /*!< Command / Response */
+ unsigned char cmdflags; /*!< Command / Response specific flags*/
+ unsigned char ies[0];
+} __attribute__ ((__packed__));
+
+struct dundi_ie_hdr {
+ unsigned char ie;
+ unsigned char len;
+ unsigned char iedata[0];
+} __attribute__ ((__packed__));
+
+#define DUNDI_FLAG_RETRANS (1 << 16) /*!< Applies to dtrans */
+#define DUNDI_FLAG_RESERVED (1 << 16) /*!< Applies to strans */
+
+#define DUNDI_PROTO_NONE 0 /*!< No answer yet */
+#define DUNDI_PROTO_IAX 1 /*!< IAX version 2 */
+#define DUNDI_PROTO_SIP 2 /*!< Session Initiation Protocol */
+#define DUNDI_PROTO_H323 3 /*!< ITU H.323 */
+
+#define DUNDI_FLAG_NONEXISTENT (0) /*!< Isn't and can't be a valid number */
+#define DUNDI_FLAG_EXISTS (1 << 0) /*!< Is a valid number */
+#define DUNDI_FLAG_MATCHMORE (1 << 1) /*!< Might be valid if you add more digits */
+#define DUNDI_FLAG_CANMATCH (1 << 2) /*!< Might be a match */
+#define DUNDI_FLAG_IGNOREPAT (1 << 3) /*!< Keep dialtone */
+#define DUNDI_FLAG_RESIDENTIAL (1 << 4) /*!< Destination known to be residential */
+#define DUNDI_FLAG_COMMERCIAL (1 << 5) /*!< Destination known to be commercial */
+#define DUNDI_FLAG_MOBILE (1 << 6) /*!< Destination known to be cellular/mobile */
+#define DUNDI_FLAG_NOUNSOLICITED (1 << 7) /*!< No unsolicited calls of any kind through this route */
+#define DUNDI_FLAG_NOCOMUNSOLICIT (1 << 8) /*!< No commercial unsolicited calls through this route */
+
+#define DUNDI_HINT_NONE (0)
+#define DUNDI_HINT_TTL_EXPIRED (1 << 0) /*!< TTL Expired */
+#define DUNDI_HINT_DONT_ASK (1 << 1) /*!< Don't ask for anything beginning with data */
+#define DUNDI_HINT_UNAFFECTED (1 << 2) /*!< Answer not affected by entity list */
+
+struct dundi_encblock { /*!< AES-128 encrypted block */
+ unsigned char iv[16]; /*!< Initialization vector of random data */
+ unsigned char encdata[0]; /*!< Encrypted / compressed data */
+} __attribute__ ((__packed__));
+
+struct dundi_answer {
+ dundi_eid eid; /*!< Original source of answer */
+ unsigned char protocol; /*!< Protocol (DUNDI_PROTO_*) */
+ unsigned short flags; /*!< Flags relating to answer */
+ unsigned short weight; /*!< Weight of answers */
+ unsigned char data[0]; /*!< Protocol specific URI */
+} __attribute__ ((__packed__));
+
+struct dundi_hint {
+ unsigned short flags; /*!< Flags relating to answer */
+ unsigned char data[0]; /*!< For data for hint */
+} __attribute__ ((__packed__));
+
+#define DUNDI_CAUSE_SUCCESS 0 /*!< Success */
+#define DUNDI_CAUSE_GENERAL 1 /*!< General unspecified failure */
+#define DUNDI_CAUSE_DYNAMIC 2 /*!< Requested entity is dynamic */
+#define DUNDI_CAUSE_NOAUTH 3 /*!< No or improper authorization */
+#define DUNDI_CAUSE_DUPLICATE 4 /*!< Duplicate request */
+#define DUNDI_CAUSE_TTL_EXPIRED 5 /*!< Expired TTL */
+#define DUNDI_CAUSE_NEEDKEY 6 /*!< Need new session key to decode */
+#define DUNDI_CAUSE_BADENCRYPT 7 /*!< Badly encrypted data */
+
+struct dundi_cause {
+ unsigned char causecode; /*!< Numerical cause (DUNDI_CAUSE_*) */
+ char desc[0]; /*!< Textual description */
+} __attribute__ ((__packed__));
+
+struct dundi_peer_status {
+ unsigned int flags;
+ unsigned short netlag;
+ unsigned short querylag;
+ dundi_eid peereid;
+} __attribute__ ((__packed__));
+
+#define DUNDI_PEER_PRIMARY (1 << 0)
+#define DUNDI_PEER_SECONDARY (1 << 1)
+#define DUNDI_PEER_UNAVAILABLE (1 << 2)
+#define DUNDI_PEER_REGISTERED (1 << 3)
+#define DUNDI_PEER_MOD_OUTBOUND (1 << 4)
+#define DUNDI_PEER_MOD_INBOUND (1 << 5)
+#define DUNDI_PEER_PCMOD_OUTBOUND (1 << 6)
+#define DUNDI_PEER_PCMOD_INBOUND (1 << 7)
+
+#define DUNDI_COMMAND_FINAL (0x80) /*!< Or'd with other flags */
+
+#define DUNDI_COMMAND_ACK (0 | 0x40) /*!< Ack a message */
+#define DUNDI_COMMAND_DPDISCOVER 1 /*!< Request discovery */
+#define DUNDI_COMMAND_DPRESPONSE (2 | 0x40) /*!< Respond to a discovery request */
+#define DUNDI_COMMAND_EIDQUERY 3 /*!< Request information for a peer */
+#define DUNDI_COMMAND_EIDRESPONSE (4 | 0x40) /*!< Response to a peer query */
+#define DUNDI_COMMAND_PRECACHERQ 5 /*!< Pre-cache Request */
+#define DUNDI_COMMAND_PRECACHERP (6 | 0x40) /*!< Pre-cache Response */
+#define DUNDI_COMMAND_INVALID (7 | 0x40) /*!< Invalid dialog state (does not require ack) */
+#define DUNDI_COMMAND_UNKNOWN (8 | 0x40) /*!< Unknown command */
+#define DUNDI_COMMAND_NULL 9 /*!< No-op */
+#define DUNDI_COMMAND_REGREQ (10) /*!< Register Request */
+#define DUNDI_COMMAND_REGRESPONSE (11 | 0x40) /*!< Register Response */
+#define DUNDI_COMMAND_CANCEL (12) /*!< Cancel transaction entirely */
+#define DUNDI_COMMAND_ENCRYPT (13) /*!< Send an encrypted message */
+#define DUNDI_COMMAND_ENCREJ (14 | 0x40) /*!< Reject an encrypted message */
+
+#define DUNDI_COMMAND_STATUS 15 /*!< Status command */
+
+/*
+ * Remember that some information elements may occur
+ * more than one time within a message
+ */
+
+#define DUNDI_IE_EID 1 /*!< Entity identifier (dundi_eid) */
+#define DUNDI_IE_CALLED_CONTEXT 2 /*!< DUNDi Context (string) */
+#define DUNDI_IE_CALLED_NUMBER 3 /*!< Number of equivalent (string) */
+#define DUNDI_IE_EID_DIRECT 4 /*!< Entity identifier (dundi_eid), direct connect */
+#define DUNDI_IE_ANSWER 5 /*!< An answer (struct dundi_answer) */
+#define DUNDI_IE_TTL 6 /*!< Max TTL for this request / Remaining TTL for the response (short)*/
+#define DUNDI_IE_VERSION 10 /*!< DUNDi version (should be 1) (short) */
+#define DUNDI_IE_EXPIRATION 11 /*!< Recommended expiration (short) */
+#define DUNDI_IE_UNKNOWN 12 /*!< Unknown command (byte) */
+#define DUNDI_IE_CAUSE 14 /*!< Success or cause of failure */
+#define DUNDI_IE_REQEID 15 /*!< EID being requested for EIDQUERY*/
+#define DUNDI_IE_ENCDATA 16 /*!< AES-128 encrypted data */
+#define DUNDI_IE_SHAREDKEY 17 /*!< RSA encrypted AES-128 key */
+#define DUNDI_IE_SIGNATURE 18 /*!< RSA Signature of encrypted shared key */
+#define DUNDI_IE_KEYCRC32 19 /*!< CRC32 of encrypted key (int) */
+#define DUNDI_IE_HINT 20 /*!< Answer hints (struct ast_hint) */
+
+#define DUNDI_IE_DEPARTMENT 21 /*!< Department, for EIDQUERY (string) */
+#define DUNDI_IE_ORGANIZATION 22 /*!< Organization, for EIDQUERY (string) */
+#define DUNDI_IE_LOCALITY 23 /*!< City/Locality, for EIDQUERY (string) */
+#define DUNDI_IE_STATE_PROV 24 /*!< State/Province, for EIDQUERY (string) */
+#define DUNDI_IE_COUNTRY 25 /*!< Country, for EIDQUERY (string) */
+#define DUNDI_IE_EMAIL 26 /*!< E-mail addy, for EIDQUERY (string) */
+#define DUNDI_IE_PHONE 27 /*!< Contact Phone, for EIDQUERY (string) */
+#define DUNDI_IE_IPADDR 28 /*!< IP Address, for EIDQUERY (string) */
+#define DUNDI_IE_CACHEBYPASS 29 /*!< Bypass cache (empty) */
+
+#define DUNDI_IE_PEERSTATUS 30 /*!< Peer/peer status (struct dundi_peer_status) */
+
+#define DUNDI_FLUFF_TIME 2000 /*!< Amount of time for answer */
+#define DUNDI_TTL_TIME 200 /*!< Incremental average time */
+
+#define DUNDI_DEFAULT_RETRANS 5
+#define DUNDI_DEFAULT_RETRANS_TIMER 1000
+#define DUNDI_DEFAULT_TTL 120 /*!< In seconds/hops like TTL */
+#define DUNDI_DEFAULT_VERSION 1
+#define DUNDI_DEFAULT_CACHE_TIME 3600 /*!< In seconds */
+#define DUNDI_DEFAULT_KEY_EXPIRE 3600 /*!< Life of shared key In seconds */
+#define DUNDI_DEF_EMPTY_CACHE_TIME 60 /*!< In seconds, cache of empty answer */
+#define DUNDI_WINDOW 1 /*!< Max 1 message in window */
+
+#define DEFAULT_MAXMS 2000
+
+struct dundi_result {
+ unsigned int flags;
+ int weight;
+ int expiration;
+ int techint;
+ dundi_eid eid;
+ char eid_str[20];
+ char tech[10];
+ char dest[256];
+};
+
+struct dundi_entity_info {
+ char country[80];
+ char stateprov[80];
+ char locality[80];
+ char org[80];
+ char orgunit[80];
+ char email[80];
+ char phone[80];
+ char ipaddr[80];
+};
+
+/*!
+ * \brief Lookup the given number in the given dundi context.
+ * Lookup number in a given dundi context (if unspecified use e164), the given callerid (if specified)
+ * and return up to maxret results in the array specified.
+ * \retval the number of results found.
+ * \retval -1 on a hangup of the channel.
+*/
+int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int nocache);
+
+/*! \brief Retrieve information on a specific EID */
+int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid);
+
+/*! \brief Pre-cache to push upstream peers */
+int dundi_precache(const char *dcontext, const char *number);
+
+#endif /* _ASTERISK_DUNDI_H */
diff --git a/trunk/include/asterisk/endian.h b/trunk/include/asterisk/endian.h
new file mode 100644
index 000000000..302a0aa7d
--- /dev/null
+++ b/trunk/include/asterisk/endian.h
@@ -0,0 +1,69 @@
+/*
+ * 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 Asterisk architecture endianess compatibility definitions
+ */
+
+#ifndef _ASTERISK_ENDIAN_H
+#define _ASTERISK_ENDIAN_H
+
+/*
+ * Autodetect system endianess
+ */
+
+#ifndef __BYTE_ORDER
+#ifdef __linux__
+#include <endian.h>
+#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#include <machine/endian.h>
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#else
+
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#endif
+
+#ifndef __BIG_ENDIAN
+#define __BIG_ENDIAN 4321
+#endif
+
+#ifdef __LITTLE_ENDIAN__
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif /* __LITTLE_ENDIAN */
+
+#if defined(i386) || defined(__i386__)
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif /* defined i386 */
+
+#if defined(sun) && defined(unix) && defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif /* sun unix sparc */
+
+#endif /* linux */
+
+#endif /* __BYTE_ORDER */
+
+#ifndef __BYTE_ORDER
+#error Need to know endianess
+#endif /* __BYTE_ORDER */
+
+#endif /* _ASTERISK_ENDIAN_H */
+
diff --git a/trunk/include/asterisk/enum.h b/trunk/include/asterisk/enum.h
new file mode 100644
index 000000000..d6bbea294
--- /dev/null
+++ b/trunk/include/asterisk/enum.h
@@ -0,0 +1,90 @@
+/*
+ * 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 enum.h
+ \brief DNS and ENUM functions
+*/
+
+#ifndef _ASTERISK_ENUM_H
+#define _ASTERISK_ENUM_H
+
+#include "asterisk/channel.h"
+
+struct naptr {
+ unsigned short order;
+ unsigned short pref;
+} __attribute__ ((__packed__));
+
+struct enum_naptr_rr {
+ struct naptr naptr; /*!< order and preference of RR */
+ char *result; /*!< result of naptr parsing,e.g.: tel:+5553 */
+ char *tech; /*!< Technology (from URL scheme) */
+ int sort_pos; /*!< sort position */
+};
+
+struct enum_context {
+ char *dst; /*!< Destination part of URL from ENUM */
+ int dstlen; /*!< Length */
+ char *tech; /*!< Technology (from URL scheme) */
+ int techlen; /*!< Length */
+ char *txt; /*!< TXT record in TXT lookup */
+ int txtlen; /*!< Length */
+ char *naptrinput; /*!< The number to lookup */
+ int position; /*!< used as counter for RRs or specifies position of required RR */
+ int options; /*!< options , see ENUMLOOKUP_OPTIONS_* defined above */
+ struct enum_naptr_rr *naptr_rrs; /*!< array of parsed NAPTR RRs */
+ int naptr_rrs_count; /*!< Size of array naptr_rrs */
+};
+
+
+/*! \brief Lookup entry in ENUM
+ \param chan Channel
+ \param number E164 number with or without the leading +
+ \param location Number returned (or SIP uri)
+ \param maxloc Max length
+ \param technology Technology (from url scheme in response)
+ You can set it to get particular answer RR, if there are many techs in DNS response, example: "sip"
+ If you need any record, then set it to empty string
+ \param maxtech Max length
+ \param suffix Zone suffix (if is NULL then use enum.conf 'search' variable)
+ \param options Options ('c' to count number of NAPTR RR)
+ \param record The position of required RR in the answer list
+ \param argcontext Argument for caching results into an enum_context pointer (NULL is used for not caching)
+ \retval 1 if found
+ \retval 0 if not found
+ \retval -1 on hangup
+*/
+int ast_get_enum(struct ast_channel *chan, const char *number, char *location, int maxloc, char *technology,
+ int maxtech, char* suffix, char* options, unsigned int record, struct enum_context **argcontext);
+
+/*! \brief Lookup DNS TXT record (used by app TXTCIDnum
+ \param chan Channel
+ \param number E164 number with or without the leading +
+ \param location Number returned (or SIP uri)
+ \param maxloc Max length of number
+ \param technology Technology (not used in TXT records)
+ \param maxtech Max length
+ \param txt Text string (return value)
+ \param maxtxt Max length of "txt"
+*/
+int ast_get_txt(struct ast_channel *chan, const char *number, char *location, int maxloc, char *technology, int maxtech, char *txt, int maxtxt);
+
+int ast_enum_init(void);
+int ast_enum_reload(void);
+
+#endif /* _ASTERISK_ENUM_H */
diff --git a/trunk/include/asterisk/event.h b/trunk/include/asterisk/event.h
new file mode 100644
index 000000000..6946b54e1
--- /dev/null
+++ b/trunk/include/asterisk/event.h
@@ -0,0 +1,482 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Russell Bryant <russell@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
+ * \author Russell Bryant <russell@digium.com>
+ * \ref AstGenericEvents
+ * \page AstGenericEvents Generic event system
+ *
+ * The purpose of this API is to provide a generic way to share events between
+ * Asterisk modules. Code can generate events, and other code can subscribe to
+ * them.
+ *
+ * Events have an associated event type, as well as information elements. The
+ * information elements are the meta data that go along with each event. For
+ * example, in the case of message waiting indication, the event type is MWI,
+ * and each MWI event contains at least three information elements: the
+ * mailbox, the number of new messages, and the number of old messages.
+ *
+ * Subscriptions to events consist of an event type and information elements,
+ * as well. Subscriptions can be to all events, or a certain subset of events.
+ * If an event type is provided, only events of that type will be sent to this
+ * subscriber. Furthermore, if information elements are supplied with the
+ * subscription, only events that contain the specified information elements
+ * with specified values will be sent to the subscriber. For example, when a
+ * SIP phone subscribes to MWI for mailbox 1234, then chan_sip can subscribe
+ * to internal Asterisk MWI events with the MAILBOX information element with
+ * a value of "1234".
+ *
+ * Another key feature of this event system is the ability to cache events.
+ * It is useful for some types of events to be able to remember the last known
+ * value. These are usually events that indicate some kind of state change.
+ * In the example of MWI, app_voicemail can instruct the event core to cache
+ * these events based on the mailbox. So, the last known MWI state of each
+ * mailbox will be cached, and other modules can retrieve this information
+ * on demand without having to poll the mailbox directly.
+ */
+
+#ifndef AST_EVENT_H
+#define AST_EVENT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/event_defs.h"
+
+/*!
+ * \brief Subscriber event callback type
+ *
+ * \param event the event being passed to the subscriber
+ * \param userdata the data provider in the call to ast_event_subscribe()
+ *
+ * \return The event callbacks do not return anything.
+ */
+typedef void (*ast_event_cb_t)(const struct ast_event *event, void *userdata);
+
+/*!
+ * \brief Subscribe to events
+ *
+ * \param event_type The type of events to subscribe to
+ * \param cb The function to be called with events
+ * \param userdata data to be passed to the event callback
+ *
+ * The rest of the arguments to this function specify additional parameters for
+ * the subscription to filter which events are passed to this subscriber. The
+ * arguments must be in sets of:
+ * \code
+ * <enum ast_event_ie_type>, [enum ast_event_ie_pltype, [payload] ]
+ * \endcode
+ * and must end with AST_EVENT_IE_END.
+ *
+ * If the ie_type specified is *not* AST_EVENT_IE_END, then it must be followed
+ * by a valid IE payload type. If the payload type specified is
+ * AST_EVENT_IE_PLTYPE_EXISTS, then the 3rd argument should not be provided.
+ * Otherwise, a payload must also be specified.
+ *
+ * \return This returns a reference to the subscription for use with
+ * un-subscribing later. If there is a failure in creating the
+ * subscription, NULL will be returned.
+ *
+ * Example usage:
+ *
+ * \code
+ * peer->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, peer,
+ * AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, peer->mailbox,
+ * AST_EVENT_IE_END);
+ * \endcode
+ *
+ * This creates a subscription to AST_EVENT_MWI events that contain an
+ * information element, AST_EVENT_IE_MAILBOX, with the same string value
+ * contained in peer->mailbox. Also, the event callback will be passed a
+ * pointer to the peer.
+ */
+struct ast_event_sub *ast_event_subscribe(enum ast_event_type event_type,
+ ast_event_cb_t cb, void *userdata, ...);
+
+/*!
+ * \brief Un-subscribe from events
+ *
+ * \param event_sub This is the reference to the subscription returned by
+ * ast_event_subscribe.
+ *
+ * \return Nothing
+ */
+void ast_event_unsubscribe(struct ast_event_sub *event_sub);
+
+/*!
+ * \brief Check if subscribers exist
+ *
+ * \param event_type This is the type of event that the caller would like to
+ * check for subscribers to.
+ *
+ * The rest of the arguments to this function specify additional parameters for
+ * checking for subscriptions to subsets of an event type. The arguments must
+ * in sets of:
+ * \code
+ * <enum ast_event_ie_type>, [enum ast_event_ie_pltype, [payload] ]
+ * \endcode
+ * and must end with AST_EVENT_IE_END.
+ *
+ * If the ie_type specified is *not* AST_EVENT_IE_END, then it must be followed
+ * by a valid IE payload type. If the payload type specified is
+ * AST_EVENT_IE_PLTYPE_EXISTS, then the 3rd argument should not be provided.
+ * Otherwise, a payload must also be specified.
+ *
+ * \return This returns one of the values defined in the ast_event_subscriber_res
+ * enum which will indicate if subscribers exist that match the given
+ * criteria.
+ *
+ * Example usage:
+ *
+ * \code
+ * if (ast_event_check_subscriber(AST_EVENT_MWI,
+ * AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ * AST_EVENT_IE_END) == AST_EVENT_SUB_NONE) {
+ * return;
+ * }
+ * \endcode
+ *
+ * This example will check if there are any subscribers to MWI events for the
+ * mailbox defined in the "mailbox" variable.
+ */
+enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type event_type, ...);
+
+/*!
+ * \brief Report current subscriptions to a subscription subscriber
+ *
+ * \arg sub the subscription subscriber
+ *
+ * \return nothing
+ *
+ * This reports all of the current subscribers to a subscriber of
+ * subscribers to a specific event type. (Try saying that a few times fast).
+ *
+ * The idea here is that it is sometimes very useful for a module to know when
+ * someone subscribes to events. However, when they first subscribe, this
+ * provides that module the ability to request the event core report to them
+ * all of the subscriptions to that event type that already exist.
+ */
+void ast_event_report_subs(const struct ast_event_sub *sub);
+
+/*!
+ * \brief Create a new event
+ *
+ * \param event_type The type of event to create
+ *
+ * The rest of the arguments to this function specify information elements to
+ * add to the event. They are specified in the form:
+ * \code
+ * <enum ast_event_ie_type>, [enum ast_event_ie_pltype, [payload] ]
+ * \endcode
+ * and must end with AST_EVENT_IE_END.
+ *
+ * If the ie_type specified is *not* AST_EVENT_IE_END, then it must be followed
+ * by a valid IE payload type. The payload type, EXISTS, should not be used here
+ * because it makes no sense to do so. So, a payload must also be specified
+ * after the IE payload type.
+ *
+ * \return This returns the event that has been created. If there is an error
+ * creating the event, NULL will be returned.
+ *
+ * Example usage:
+ *
+ * \code
+ * if (!(event = ast_event_new(AST_EVENT_MWI,
+ * AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ * AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, new,
+ * AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
+ * AST_EVENT_IE_END))) {
+ * return;
+ * }
+ * \endcode
+ *
+ * This creates a MWI event with 3 information elements, a mailbox which is
+ * a string, and the number of new and old messages, specified as integers.
+ */
+struct ast_event *ast_event_new(enum ast_event_type event_type, ...);
+
+/*!
+ * \brief Destroy an event
+ *
+ * \param event the event to destroy
+ *
+ * \return Nothing
+ *
+ * \note Events that have been queued should *not* be destroyed by the code that
+ * created the event. It will be automatically destroyed after being
+ * dispatched to the appropriate subscribers.
+ */
+void ast_event_destroy(struct ast_event *event);
+
+/*!
+ * \brief Queue an event
+ *
+ * \param event the event to be queued
+ *
+ * \retval zero success
+ * \retval non-zero failure
+ *
+ * This function queues an event to be dispatched to all of the appropriate
+ * subscribers. This function will not block while the event is being
+ * dispatched because a pool of event dispatching threads handle the event
+ * queue.
+ */
+int ast_event_queue(struct ast_event *event);
+
+/*!
+ * \brief Queue and cache an event
+ *
+ * \param event the event to be queued and cached
+ *
+ * The rest of the arguments to this function specify information elements to
+ * use for determining which events in the cache that this event should replace.
+ * All events in the cache that match the specified criteria will be removed from
+ * the cache and then this one will be added. The arguments are specified in
+ * the form:
+ *
+ * \code
+ * <enum ast_event_ie_type>, [enum ast_event_ie_pltype]
+ * \endcode
+ * and must end with AST_EVENT_IE_END.
+ *
+ * If the ie_type specified is *not* AST_EVENT_IE_END, then it must be followed
+ * by a valid IE payload type. If the payload type given is EXISTS, then all
+ * events that contain that information element will be removed from the cache.
+ * Otherwise, all events in the cache that contain an information element with
+ * the same value as the new event will be removed.
+ *
+ * \note If more than one IE parameter is specified, they *all* must match for
+ * the event to be removed from the cache.
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_event_queue_and_cache(event,
+ * AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
+ * AST_EVENT_IE_END);
+ * \endcode
+ *
+ * This example queues and caches an event. Any events in the cache that have
+ * the same MAILBOX information element as this event will be removed.
+ *
+ * The purpose of caching events is so that the core can retain the last known
+ * information for events that represent some sort of state. That way, when
+ * code needs to find out the current state, it can query the cache.
+ */
+int ast_event_queue_and_cache(struct ast_event *event, ...);
+
+/*!
+ * \brief Retrieve an event from the cache
+ *
+ * \param ast_event_type The type of event to retrieve from the cache
+ *
+ * The rest of the arguments to this function specify information elements to
+ * match for retrieving events from the cache. They are specified in the form:
+ * \code
+ * <enum ast_event_ie_type>, [enum ast_event_ie_pltype, [payload] ]
+ * \endcode
+ * and must end with AST_EVENT_IE_END.
+ *
+ * If the ie_type specified is *not* AST_EVENT_IE_END, then it must be followed
+ * by a valid IE payload type. If the payload type specified is
+ * AST_EVENT_IE_PLTYPE_EXISTS, then the 3rd argument should not be provided.
+ * Otherwise, a payload must also be specified.
+ *
+ * \return A reference to an event retrieved from the cache. If no event was
+ * found that matches the specified criteria, then NULL will be returned.
+ *
+ * \note If more than one event in the cache matches the specified criteria, only
+ * one will be returned, and it is undefined which one it will be.
+ *
+ * \note The caller of this function *must* call ast_event_destroy() on the
+ * returned event after it is done using it.
+ *
+ * Example Usage:
+ *
+ * \code
+ * event = ast_event_get_cached(AST_EVENT_MWI,
+ * AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+ * AST_EVENT_IE_END);
+ * \endcode
+ *
+ * This example will check for an MWI event in the cache that matches the
+ * specified mailbox. This would be the way to find out the last known state
+ * of a mailbox without having to poll the mailbox directly.
+ */
+struct ast_event *ast_event_get_cached(enum ast_event_type, ...);
+
+/*!
+ * \brief Append an information element that has a string payload
+ *
+ * \param event the event that the IE will be appended to
+ * \param ie_type the type of IE to append
+ * \param str The string for the payload of the IE
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * The pointer to the event will get updated with the new location for the event
+ * that now contains the appended information element. If the re-allocation of
+ * the memory for this event fails, it will be set to NULL.
+ */
+int ast_event_append_ie_str(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const char *str);
+
+/*!
+ * \brief Append an information element that has an integer payload
+ *
+ * \param event the event that the IE will be appended to
+ * \param ie_type the type of IE to append
+ * \param data The integer for the payload of the IE
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * The pointer to the event will get updated with the new location for the event
+ * that now contains the appended information element. If the re-allocation of
+ * the memory for this event fails, it will be set to NULL.
+ */
+int ast_event_append_ie_uint(struct ast_event **event, enum ast_event_ie_type ie_type,
+ uint32_t data);
+
+/*!
+ * \brief Append an information element that has a raw payload
+ *
+ * \param event the event that the IE will be appended to
+ * \param ie_type the type of IE to append
+ * \param data A pointer to the raw data for the payload of the IE
+ * \param data_len The amount of data to copy into the payload
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * The pointer to the event will get updated with the new location for the event
+ * that now contains the appended information element. If the re-allocation of
+ * the memory for this event fails, it will be set to NULL.
+ */
+int ast_event_append_ie_raw(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const void *data, size_t data_len);
+
+/*!
+ * \brief Get the value of an information element that has an integer payload
+ *
+ * \param event The event to get the IE from
+ * \param ie_type the type of information element to retrieve
+ *
+ * \return This returns the payload of the information element with the given type.
+ * However, an IE with a payload of 0, and the case where no IE is found
+ * yield the same return value.
+ */
+uint32_t ast_event_get_ie_uint(const struct ast_event *event, enum ast_event_ie_type ie_type);
+
+/*!
+ * \brief Get the value of an information element that has a string payload
+ *
+ * \param event The event to get the IE from
+ * \param ie_type the type of information element to retrieve
+ *
+ * \return This returns the payload of the information element with the given type.
+ * If the information element isn't found, NULL will be returned.
+ */
+const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type);
+
+/*!
+ * \brief Get the value of an information element that has a raw payload
+ *
+ * \param event The event to get the IE from
+ * \param ie_type the type of information element to retrieve
+ *
+ * \return This returns the payload of the information element with the given type.
+ * If the information element isn't found, NULL will be returned.
+ */
+const void *ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type);
+
+/*!
+ * \brief Get the type for an event
+ *
+ * \param event the event to get the type for
+ *
+ * \return the event type as represented by one of the values in the
+ * ast_event_type enum
+ */
+enum ast_event_type ast_event_get_type(const struct ast_event *event);
+
+/*!
+ * \brief Initialize an event iterator instance
+ *
+ * \param iterator The iterator instance to initialize
+ * \param event The event that will be iterated through
+ *
+ * \return Nothing
+ */
+void ast_event_iterator_init(struct ast_event_iterator *iterator, const struct ast_event *event);
+
+/*!
+ * \brief Move iterator instance to next IE
+ *
+ * \param iterator The iterator instance
+ *
+ * \retval 0 on success
+ * \retval -1 if end is reached
+ */
+int ast_event_iterator_next(struct ast_event_iterator *iterator);
+
+/*!
+ * \brief Get the type of the current IE in the iterator instance
+ *
+ * \param iterator The iterator instance
+ *
+ * \return the ie type as represented by one of the value sin the
+ * ast_event_ie_type enum
+ */
+enum ast_event_ie_type ast_event_iterator_get_ie_type(struct ast_event_iterator *iterator);
+
+/*!
+ * \brief Get the value of the current IE in the ierator as an integer payload
+ *
+ * \param iterator The iterator instance
+ *
+ * \return This returns the payload of the information element as a uint.
+ */
+uint32_t ast_event_iterator_get_ie_uint(struct ast_event_iterator *iterator);
+
+/*!
+ * \brief Get the value of the current IE in the iterator as a string payload
+ *
+ * \param iterator The iterator instance
+ *
+ * \return This returns the payload of the information element as a string.
+ */
+const char *ast_event_iterator_get_ie_str(struct ast_event_iterator *iterator);
+
+/*!
+ * \brief Get the value of the current IE in the iterator instance that has a raw payload
+ *
+ * \param iterator The iterator instance
+ *
+ * \return This returns the payload of the information element as type raw.
+ */
+void *ast_event_iterator_get_ie_raw(struct ast_event_iterator *iterator);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* AST_EVENT_H */
diff --git a/trunk/include/asterisk/event_defs.h b/trunk/include/asterisk/event_defs.h
new file mode 100644
index 000000000..b664ac14b
--- /dev/null
+++ b/trunk/include/asterisk/event_defs.h
@@ -0,0 +1,143 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Russell Bryant <russell@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
+ * \author Russell Bryant <russell@digium.com>
+ * \brief Generic event system
+ */
+
+#ifndef AST_EVENT_DEFS_H
+#define AST_EVENT_DEFS_H
+
+/*! \brief Event types
+ * \note These values can *never* change. */
+enum ast_event_type {
+ /*! Reserved to provide the ability to subscribe to all events. A specific
+ event should never have a payload of 0. */
+ AST_EVENT_ALL = 0x00,
+ /*! This event type is reserved for use by third-party modules to create
+ custom events without having to modify this file.
+ \note There are no "custom" IE types, because IEs only have to be
+ unique to the event itself, not necessarily across all events. */
+ AST_EVENT_CUSTOM = 0x01,
+ /*! Voicemail message waiting indication */
+ AST_EVENT_MWI = 0x02,
+ /*! Someone has subscribed to events */
+ AST_EVENT_SUB = 0x03,
+ /*! Someone has unsubscribed from events */
+ AST_EVENT_UNSUB = 0x04,
+ /*! The state of a device has changed */
+ AST_EVENT_DEVICE_STATE = 0x05,
+ /*! Number of event types. This should be the last event type + 1 */
+ AST_EVENT_TOTAL = 0x06,
+};
+
+/*! \brief Event Information Element types */
+enum ast_event_ie_type {
+ /*! Used to terminate the arguments to event functions */
+ AST_EVENT_IE_END = -1,
+
+ /*!
+ * \brief Number of new messages
+ * Used by: AST_EVENT_MWI
+ * Payload type: UINT
+ */
+ AST_EVENT_IE_NEWMSGS = 0x01,
+ /*!
+ * \brief Number of
+ * Used by: AST_EVENT_MWI
+ * Payload type: UINT
+ */
+ AST_EVENT_IE_OLDMSGS = 0x02,
+ /*!
+ * \brief Mailbox name \verbatim (mailbox[@context]) \endverbatim
+ * Used by: AST_EVENT_MWI
+ * Payload type: STR
+ */
+ AST_EVENT_IE_MAILBOX = 0x03,
+ /*!
+ * \brief Unique ID
+ * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
+ * Payload type: UINT
+ */
+ AST_EVENT_IE_UNIQUEID = 0x04,
+ /*!
+ * \brief Event type
+ * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
+ * Payload type: UINT
+ */
+ AST_EVENT_IE_EVENTTYPE = 0x05,
+ /*!
+ * \brief Hint that someone cares that an IE exists
+ * Used by: AST_EVENT_SUB
+ * Payload type: UINT (ast_event_ie_type)
+ */
+ AST_EVENT_IE_EXISTS = 0x06,
+ /*!
+ * \brief Device Name
+ * Used by AST_EVENT_DEVICE_STATE
+ * Payload type: STR
+ */
+ AST_EVENT_IE_DEVICE = 0x07,
+ /*!
+ * \brief Generic State IE
+ * Used by AST_EVENT_DEVICE_STATE
+ * Payload type: UINT
+ * The actual state values depend on the event which
+ * this IE is a part of.
+ */
+ AST_EVENT_IE_STATE = 0x08,
+ /*!
+ * \brief Context IE
+ * Used by AST_EVENT_MWI
+ * Payload type: str
+ */
+ AST_EVENT_IE_CONTEXT = 0x09,
+};
+
+/*!
+ * \brief Payload types for event information elements
+ */
+enum ast_event_ie_pltype {
+ /*! Just check if it exists, not the value */
+ AST_EVENT_IE_PLTYPE_EXISTS,
+ /*! Unsigned Integer (Can be used for signed, too ...) */
+ AST_EVENT_IE_PLTYPE_UINT,
+ /*! String */
+ AST_EVENT_IE_PLTYPE_STR,
+};
+
+/*!
+ * \brief Results for checking for subscribers
+ *
+ * \ref ast_event_check_subscriber()
+ */
+enum ast_event_subscriber_res {
+ /*! No subscribers exist */
+ AST_EVENT_SUB_NONE,
+ /*! At least one subscriber exists */
+ AST_EVENT_SUB_EXISTS,
+};
+
+struct ast_event;
+struct ast_event_ie;
+struct ast_event_sub;
+struct ast_event_iterator;
+
+#endif /* AST_EVENT_DEFS_H */
diff --git a/trunk/include/asterisk/extconf.h b/trunk/include/asterisk/extconf.h
new file mode 100644
index 000000000..943945031
--- /dev/null
+++ b/trunk/include/asterisk/extconf.h
@@ -0,0 +1,248 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Steve Murphy <murf@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 External configuration handlers (realtime and static configuration)
+ * \author Steve Murphy <murf@digium.com>
+ *
+ */
+
+#ifndef _ASTERISK_EXTCONF_H
+#define _ASTERISK_EXTCONF_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#ifdef NOTYET
+/* I'm going to define all the structs mentioned below, to avoid
+ possible conflicts in declarations that might be introduced,
+ if we just include the files that define them-- this may be
+ unnecessary */
+
+struct ast_comment {
+ struct ast_comment *next;
+ char cmt[0];
+};
+
+struct ast_variable {
+ char *name;
+ char *value;
+ int lineno;
+ int object; /*!< 0 for variable, 1 for object */
+ int blanklines; /*!< Number of blanklines following entry */
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *next;
+ char stuff[0];
+};
+
+struct ast_category {
+ char name[80];
+ int ignored; /*!< do not let user of the config see this category */
+ int include_level;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *root;
+ struct ast_variable *last;
+ struct ast_category *next;
+};
+
+struct ast_config {
+ struct ast_category *root;
+ struct ast_category *last;
+ struct ast_category *current;
+ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
+ int include_level;
+ int max_include_level;
+};
+
+/* ================== above: the config world; below, the dialplan world */
+
+/*! \brief A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ void *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+/*!
+ \brief An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+/* from pbx.h */
+typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
+struct ast_timing {
+ int hastime; /*!< If time construct exists */
+ unsigned int monthmask; /*!< Mask for month */
+ unsigned int daymask; /*!< Mask for date */
+ unsigned int dowmask; /*!< Mask for day of week (mon-sun) */
+ unsigned int minmask[24]; /*!< Mask for minute */
+};
+/*! \brief include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+*! \brief Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief An extension context */
+struct ast_context {
+ ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
+ struct ast_exten *root; /*!< The root of the list of extensions */
+ struct ast_context *next; /*!< Link them together */
+ struct ast_include *includes; /*!< Include other contexts */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+#endif
+
+struct ast_config *localized_config_load(const char *filename);
+struct ast_config *localized_config_load_with_comments(const char *filename);
+struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name);
+int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator);
+struct ast_context *localized_walk_contexts(struct ast_context *con);
+struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten);
+struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority);
+struct ast_include *localized_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc);
+struct ast_sw *localized_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw);
+
+void localized_context_destroy(struct ast_context *con, const char *registrar);
+int localized_pbx_load_module(void);
+
+struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar);
+int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data);
+int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
+int localized_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar);
+int localized_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar);
+int localized_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar);
+void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar);
+int localized_context_verify_includes(struct ast_context *con);
+void localized_use_conf_dir(void);
+void localized_use_local_dir(void);
+
+
+#ifndef _ASTERISK_PBX_H
+/*!
+ * When looking up extensions, we can have different requests
+ * identified by the 'action' argument, as follows.
+ * Note that the coding is such that the low 4 bits are the
+ * third argument to extension_match_core.
+ */
+enum ext_match_t {
+ E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
+ E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
+ E_MATCH = 0x02, /* extension is an exact match */
+ E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
+ E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
+ E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
+};
+#define AST_PBX_MAX_STACK 128
+
+/* request and result for pbx_find_extension */
+struct pbx_find_info {
+#if 0
+ const char *context;
+ const char *exten;
+ int priority;
+#endif
+
+ char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
+ int stacklen; /* modified during the search */
+ int status; /* set on return */
+ struct ast_switch *swo; /* set on return */
+ const char *data; /* set on return */
+ const char *foundcontext; /* set on return */
+};
+
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+#endif
+
+struct ast_exten *localized_find_extension(struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_PBX_H */
diff --git a/trunk/include/asterisk/features.h b/trunk/include/asterisk/features.h
new file mode 100644
index 000000000..aa145a961
--- /dev/null
+++ b/trunk/include/asterisk/features.h
@@ -0,0 +1,112 @@
+/*
+ * 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 Call Parking and Pickup API
+ * Includes code and algorithms from the Zapata library.
+ */
+
+#ifndef _AST_FEATURES_H
+#define _AST_FEATURES_H
+
+#define FEATURE_MAX_LEN 11
+#define FEATURE_APP_LEN 64
+#define FEATURE_APP_ARGS_LEN 256
+#define FEATURE_SNAME_LEN 32
+#define FEATURE_EXTEN_LEN 32
+#define FEATURE_MOH_LEN 80 /* same as MAX_MUSICCLASS from channel.h */
+
+/*! \brief main call feature structure */
+struct ast_call_feature {
+ int feature_mask;
+ char *fname;
+ char sname[FEATURE_SNAME_LEN];
+ char exten[FEATURE_MAX_LEN];
+ char default_exten[FEATURE_MAX_LEN];
+ int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data);
+ unsigned int flags;
+ char app[FEATURE_APP_LEN];
+ char app_args[FEATURE_APP_ARGS_LEN];
+ char moh_class[FEATURE_MOH_LEN];
+ AST_LIST_ENTRY(ast_call_feature) feature_entry;
+};
+
+
+
+/*!
+ * \brief Park a call and read back parked location
+ * \param chan the channel to actually be parked
+ * \param host the channel which will have the parked location read to.
+ * \param timeout is a timeout in milliseconds
+ * \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
+ *
+ * Park the channel chan, and read back the parked location to the host.
+ * If the call is not picked up within a specified period of time,
+ * then the call will return to the last step that it was in
+ * (in terms of exten, priority and context)
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+int ast_park_call(struct ast_channel *chan, struct ast_channel *host, int timeout, int *extout);
+
+/*!
+ * \brief Park a call via a masqueraded channel
+ * \param rchan the real channel to be parked
+ * \param host the channel to have the parking read to.
+ * \param timeout is a timeout in milliseconds
+ * \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
+ *
+ * Masquerade the channel rchan into a new, empty channel which is then parked with ast_park_call
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout);
+
+/*!
+ * \brief Determine system parking extension
+ * \returns the call parking extension for drivers that provide special call parking help
+*/
+const char *ast_parking_ext(void);
+
+/*! \brief Determine system call pickup extension */
+const char *ast_pickup_ext(void);
+
+/*! \brief Bridge a call, optionally allowing redirection */
+int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
+
+/*! \brief Pickup a call */
+int ast_pickup_call(struct ast_channel *chan);
+
+/*! \brief register new feature into feature_set
+ \param feature an ast_call_feature object which contains a keysequence
+ and a callback function which is called when this keysequence is pressed
+ during a call. */
+void ast_register_feature(struct ast_call_feature *feature);
+
+/*! \brief unregister feature from feature_set
+ \param feature the ast_call_feature object which was registered before*/
+void ast_unregister_feature(struct ast_call_feature *feature);
+
+/*! \brief look for a call feature entry by its sname
+ \param name a string ptr, should match "automon", "blindxfer", "atxfer", etc. */
+struct ast_call_feature *ast_find_call_feature(const char *name);
+
+void ast_rdlock_call_features(void);
+void ast_unlock_call_features(void);
+
+#endif /* _AST_FEATURES_H */
diff --git a/trunk/include/asterisk/file.h b/trunk/include/asterisk/file.h
new file mode 100644
index 000000000..0f33b5340
--- /dev/null
+++ b/trunk/include/asterisk/file.h
@@ -0,0 +1,322 @@
+/*
+ * 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 Generic File Format Support.
+ * Should be included by clients of the file handling routines.
+ * File service providers should instead include mod_format.h
+ */
+
+#ifndef _ASTERISK_FILE_H
+#define _ASTERISK_FILE_H
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct ast_filestream;
+struct ast_format;
+
+/*! Convenient for waiting */
+#define AST_DIGIT_ANY "0123456789#*ABCD"
+#define AST_DIGIT_ANYNUM "0123456789"
+
+#define SEEK_FORCECUR 10
+
+/*!
+ * \brief Streams a file
+ * \param c channel to stream the file to
+ * \param filename the name of the file you wish to stream, minus the extension
+ * \param preflang the preferred language you wish to have the file streamed to you in
+ * Prepares a channel for the streaming of a file. To start the stream, afterward do a ast_waitstream() on the channel
+ * Also, it will stop any existing streams on the channel.
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang);
+
+/*!
+ * \brief stream file until digit
+ * If the file name is non-empty, try to play it.
+ * \note If digits == "" then we can simply check for non-zero.
+ * \return 0 if success.
+ * \retval -1 if error.
+ * \retval digit if interrupted by a digit.
+ */
+int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits);
+
+/*!
+ * \brief Stops a stream
+ *
+ * \param c The channel you wish to stop playback on
+ *
+ * Stop playback of a stream
+ *
+ * \retval 0 always
+ *
+ * \note The channel does not need to be locked before calling this function.
+ */
+int ast_stopstream(struct ast_channel *c);
+
+/*!
+ * \brief Checks for the existence of a given file
+ * \param filename name of the file you wish to check, minus the extension
+ * \param fmt the format you wish to check (the extension)
+ * \param preflang (the preferred language you wisht to find the file in)
+ * See if a given file exists in a given format. If fmt is NULL, any format is accepted.
+ * \return -1 if file does not exist, non-zero positive otherwise.
+ */
+int ast_fileexists(const char *filename, const char *fmt, const char *preflang);
+
+/*!
+ * \brief Renames a file
+ * \param oldname the name of the file you wish to act upon (minus the extension)
+ * \param newname the name you wish to rename the file to (minus the extension)
+ * \param fmt the format of the file
+ * Rename a given file in a given format, or if fmt is NULL, then do so for all
+ * \return -1 on failure
+ */
+int ast_filerename(const char *oldname, const char *newname, const char *fmt);
+
+/*!
+ * \brief Deletes a file
+ * \param filename name of the file you wish to delete (minus the extension)
+ * \param fmt of the file
+ * Delete a given file in a given format, or if fmt is NULL, then do so for all
+ */
+int ast_filedelete(const char *filename, const char *fmt);
+
+/*!
+ * \brief Copies a file
+ * \param oldname name of the file you wish to copy (minus extension)
+ * \param newname name you wish the file to be copied to (minus extension)
+ * \param fmt the format of the file
+ * Copy a given file in a given format, or if fmt is NULL, then do so for all
+ */
+int ast_filecopy(const char *oldname, const char *newname, const char *fmt);
+
+/*!
+ * \brief Waits for a stream to stop or digit to be pressed
+ * \param c channel to waitstream on
+ * \param breakon string of DTMF digits to break upon
+ * Begins playback of a stream...
+ * Wait for a stream to stop or for any one of a given digit to arrive,
+ * \retval 0 if the stream finishes
+ * \retval the character if it was interrupted,
+ * \retval -1 on error
+ */
+int ast_waitstream(struct ast_channel *c, const char *breakon);
+
+/*!
+ * \brief Waits for a stream to stop or digit matching a valid one digit exten to be pressed
+ * \param c channel to waitstream on
+ * \param context string of context to match digits to break upon
+ * Begins playback of a stream...
+ * Wait for a stream to stop or for any one of a valid extension digit to arrive,
+ * \retval 0 if the stream finishes.
+ * \retval the character if it was interrupted.
+ * \retval -1 on error.
+ */
+int ast_waitstream_exten(struct ast_channel *c, const char *context);
+
+/*!
+ * \brief Same as waitstream but allows stream to be forwarded or rewound
+ * \param c channel to waitstream on
+ * \param breakon string of DTMF digits to break upon
+ * \param forward DTMF digit to fast forward upon
+ * \param rewind DTMF digit to rewind upon
+ * \param ms How many miliseconds to skip forward/back
+ * Begins playback of a stream...
+ * Wait for a stream to stop or for any one of a given digit to arrive,
+ * \retval 0 if the stream finishes.
+ * \retval the character if it was interrupted.
+ * \retval -1 on error.
+ */
+int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms);
+
+/*!
+ * Same as waitstream, but with audio output to fd and monitored fd checking.
+ *
+ * \return 1 if monfd is ready for reading
+ */
+int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int monfd);
+
+/*!
+ * \brief Starts reading from a file
+ * \param filename the name of the file to read from
+ * \param type format of file you wish to read from
+ * \param comment comment to go with
+ * \param flags file flags
+ * \param check (unimplemented, hence negligible)
+ * \param mode Open mode
+ * Open an incoming file stream. flags are flags for the open() command, and
+ * if check is non-zero, then it will not read a file if there are any files that
+ * start with that name and have an extension
+ * Please note, this is a blocking function. Program execution will not return until ast_waitstream completes it's execution.
+ * \retval a struct ast_filestream on success.
+ * \retval NULL on failure.
+ */
+struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode);
+
+/*!
+ * \brief Starts writing a file
+ * \param filename the name of the file to write to
+ * \param type format of file you wish to write out to
+ * \param comment comment to go with
+ * \param flags output file flags
+ * \param check (unimplemented, hence negligible)
+ * \param mode Open mode
+ * Create an outgoing file stream. oflags are flags for the open() command, and
+ * if check is non-zero, then it will not write a file if there are any files that
+ * start with that name and have an extension
+ * Please note, this is a blocking function. Program execution will not return until ast_waitstream completes it's execution.
+ * \retval a struct ast_filestream on success.
+ * \retval NULL on failure.
+ */
+struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode);
+
+/*!
+ * \brief Writes a frame to a stream
+ * \param fs filestream to write to
+ * \param f frame to write to the filestream
+ * Send a frame to a filestream -- note: does NOT free the frame, call ast_frfree manually
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_writestream(struct ast_filestream *fs, struct ast_frame *f);
+
+/*!
+ * \brief Closes a stream
+ * \param f filestream to close
+ * Close a playback or recording stream
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_closestream(struct ast_filestream *f);
+
+/*!
+ * \brief Opens stream for use in seeking, playing
+ * \param chan channel to work with
+ * \param filename to use
+ * \param preflang prefered language to use
+ * \retval a ast_filestream pointer if it opens the file.
+ * \retval NULL on error.
+ */
+struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang);
+
+/*!
+ * \brief Opens stream for use in seeking, playing
+ * \param chan channel to work with
+ * \param filename to use
+ * \param preflang prefered language to use
+ * \param asis if set, don't clear generators
+ * \retval a ast_filestream pointer if it opens the file.
+ * \retval NULL on error.
+ */
+struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis);
+/*!
+ * \brief Opens stream for use in seeking, playing
+ * \param chan channel to work with
+ * \param filename to use
+ * \param preflang prefered language to use
+ * \retval a ast_filestream pointer if it opens the file.
+ * \retval NULL on error.
+ */
+struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang);
+
+/*!
+ * \brief Applys a open stream to a channel.
+ * \param chan channel to work
+ * \param s ast_filestream to apply
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_applystream(struct ast_channel *chan, struct ast_filestream *s);
+
+/*!
+ * \brief Play a open stream on a channel.
+ * \param s filestream to play
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_playstream(struct ast_filestream *s);
+
+/*!
+ * \brief Seeks into stream
+ * \param fs ast_filestream to perform seek on
+ * \param sample_offset numbers of samples to seek
+ * \param whence SEEK_SET, SEEK_CUR, SEEK_END
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence);
+
+/*!
+ * \brief Trunc stream at current location
+ * \param fs filestream to act on
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_truncstream(struct ast_filestream *fs);
+
+/*!
+ * \brief Fast forward stream ms
+ * \param fs filestream to act on
+ * \param ms milliseconds to move
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_stream_fastforward(struct ast_filestream *fs, off_t ms);
+
+/*!
+ * \brief Rewind stream ms
+ * \param fs filestream to act on
+ * \param ms milliseconds to move
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_stream_rewind(struct ast_filestream *fs, off_t ms);
+
+/*!
+ * \brief Tell where we are in a stream
+ * \param fs fs to act on
+ * \return a long as a sample offset into stream
+ */
+off_t ast_tellstream(struct ast_filestream *fs);
+
+/*!
+ * \brief Read a frame from a filestream
+ * \param s ast_filestream to act on
+ * \return a frame.
+ * \retval NULL if read failed.
+ */
+struct ast_frame *ast_readframe(struct ast_filestream *s);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_FILE_H */
diff --git a/trunk/include/asterisk/frame.h b/trunk/include/asterisk/frame.h
new file mode 100644
index 000000000..3983c84cf
--- /dev/null
+++ b/trunk/include/asterisk/frame.h
@@ -0,0 +1,603 @@
+/*
+ * 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 Asterisk internal frame definitions.
+ * \arg For an explanation of frames, see \ref Def_Frame
+ * \arg Frames are send of Asterisk channels, see \ref Def_Channel
+ */
+
+#ifndef _ASTERISK_FRAME_H
+#define _ASTERISK_FRAME_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <sys/time.h>
+
+#include "asterisk/endian.h"
+#include "asterisk/linkedlists.h"
+
+struct ast_codec_pref {
+ char order[32];
+ char framing[32];
+};
+
+/*! \page Def_Frame AST Multimedia and signalling frames
+ \section Def_AstFrame What is an ast_frame ?
+ A frame of data read used to communicate between
+ between channels and applications.
+ Frames are divided into frame types and subclasses.
+
+ \par Frame types
+ \arg \b VOICE: Voice data, subclass is codec (AST_FORMAT_*)
+ \arg \b VIDEO: Video data, subclass is codec (AST_FORMAT_*)
+ \arg \b DTMF: A DTMF digit, subclass is the digit
+ \arg \b IMAGE: Image transport, mostly used in IAX
+ \arg \b TEXT: Text messages and character by character (real time text)
+ \arg \b HTML: URL's and web pages
+ \arg \b MODEM: Modulated data encodings, such as T.38 and V.150
+ \arg \b IAX: Private frame type for the IAX protocol
+ \arg \b CNG: Comfort noice frames
+ \arg \b CONTROL: A control frame, subclass defined as AST_CONTROL_
+ \arg \b NULL: Empty, useless frame
+
+ \par Files
+ \arg frame.h Definitions
+ \arg frame.c Function library
+ \arg \ref Def_Channel Asterisk channels
+ \section Def_ControlFrame Control Frames
+ Control frames send signalling information between channels
+ and devices. They are prefixed with AST_CONTROL_, like AST_CONTROL_FRAME_HANGUP
+ \arg \b HANGUP The other end has hungup
+ \arg \b RING Local ring
+ \arg \b RINGING The other end is ringing
+ \arg \b ANSWER The other end has answered
+ \arg \b BUSY Remote end is busy
+ \arg \b TAKEOFFHOOK Make it go off hook (what's "it" ? )
+ \arg \b OFFHOOK Line is off hook
+ \arg \b CONGESTION Congestion (circuit is busy, not available)
+ \arg \b FLASH Other end sends flash hook
+ \arg \b WINK Other end sends wink
+ \arg \b OPTION Send low-level option
+ \arg \b RADIO_KEY Key radio (see app_rpt.c)
+ \arg \b RADIO_UNKEY Un-key radio (see app_rpt.c)
+ \arg \b PROGRESS Other end indicates call progress
+ \arg \b PROCEEDING Indicates proceeding
+ \arg \b HOLD Call is placed on hold
+ \arg \b UNHOLD Call is back from hold
+ \arg \b VIDUPDATE Video update requested
+
+*/
+
+/*!
+ * \brief Frame types
+ *
+ * \note It is important that the values of each frame type are never changed,
+ * because it will break backwards compatability with older versions.
+ * This is because these constants are transmitted directly over IAX2.
+ */
+enum ast_frame_type {
+ /*! DTMF end event, subclass is the digit */
+ AST_FRAME_DTMF_END = 1,
+ /*! Voice data, subclass is AST_FORMAT_* */
+ AST_FRAME_VOICE,
+ /*! Video frame, maybe?? :) */
+ AST_FRAME_VIDEO,
+ /*! A control frame, subclass is AST_CONTROL_* */
+ AST_FRAME_CONTROL,
+ /*! An empty, useless frame */
+ AST_FRAME_NULL,
+ /*! Inter Asterisk Exchange private frame type */
+ AST_FRAME_IAX,
+ /*! Text messages */
+ AST_FRAME_TEXT,
+ /*! Image Frames */
+ AST_FRAME_IMAGE,
+ /*! HTML Frame */
+ AST_FRAME_HTML,
+ /*! Comfort Noise frame (subclass is level of CNG in -dBov),
+ body may include zero or more 8-bit quantization coefficients */
+ AST_FRAME_CNG,
+ /*! Modem-over-IP data streams */
+ AST_FRAME_MODEM,
+ /*! DTMF begin event, subclass is the digit */
+ AST_FRAME_DTMF_BEGIN,
+};
+#define AST_FRAME_DTMF AST_FRAME_DTMF_END
+
+enum {
+ /*! This frame contains valid timing information */
+ AST_FRFLAG_HAS_TIMING_INFO = (1 << 0),
+ /*! This frame came from a translator and is still the original frame.
+ * The translator can not be free'd if the frame inside of it still has
+ * this flag set. */
+ AST_FRFLAG_FROM_TRANSLATOR = (1 << 1),
+};
+
+/*! \brief Data structure associated with a single frame of data
+ */
+struct ast_frame {
+ /*! Kind of frame */
+ enum ast_frame_type frametype;
+ /*! Subclass, frame dependent */
+ int subclass;
+ /*! Length of data */
+ int datalen;
+ /*! Number of 8khz samples in this frame */
+ int samples;
+ /*! Was the data malloc'd? i.e. should we free it when we discard the frame? */
+ int mallocd;
+ /*! The number of bytes allocated for a malloc'd frame header */
+ size_t mallocd_hdr_len;
+ /*! How many bytes exist _before_ "data" that can be used if needed */
+ int offset;
+ /*! Optional source of frame for debugging */
+ const char *src;
+ /*! Pointer to actual data */
+ void *data;
+ /*! Global delivery time */
+ struct timeval delivery;
+ /*! For placing in a linked list */
+ AST_LIST_ENTRY(ast_frame) frame_list;
+ /*! Misc. frame flags */
+ unsigned int flags;
+ /*! Timestamp in milliseconds */
+ long ts;
+ /*! Length in milliseconds */
+ long len;
+ /*! Sequence number */
+ int seqno;
+};
+
+/*!
+ * Set the various field of a frame to point to a buffer.
+ * Typically you set the base address of the buffer, the offset as
+ * AST_FRIENDLY_OFFSET, and the datalen as the amount of bytes queued.
+ * The remaining things (to be done manually) is set the number of
+ * samples, which cannot be derived from the datalen unless you know
+ * the number of bits per sample.
+ */
+#define AST_FRAME_SET_BUFFER(fr, _base, _ofs, _datalen) \
+ { \
+ (fr)->data = (char *)_base + (_ofs); \
+ (fr)->offset = (_ofs); \
+ (fr)->datalen = (_datalen); \
+ }
+
+/*! Queueing a null frame is fairly common, so we declare a global null frame object
+ for this purpose instead of having to declare one on the stack */
+extern struct ast_frame ast_null_frame;
+
+#define AST_FRIENDLY_OFFSET 64 /*! It's polite for a a new frame to
+ have this number of bytes for additional
+ headers. */
+#define AST_MIN_OFFSET 32 /*! Make sure we keep at least this much handy */
+
+/*! Need the header be free'd? */
+#define AST_MALLOCD_HDR (1 << 0)
+/*! Need the data be free'd? */
+#define AST_MALLOCD_DATA (1 << 1)
+/*! Need the source be free'd? (haha!) */
+#define AST_MALLOCD_SRC (1 << 2)
+
+/* MODEM subclasses */
+/*! T.38 Fax-over-IP */
+#define AST_MODEM_T38 1
+/*! V.150 Modem-over-IP */
+#define AST_MODEM_V150 2
+
+/* HTML subclasses */
+/*! Sending a URL */
+#define AST_HTML_URL 1
+/*! Data frame */
+#define AST_HTML_DATA 2
+/*! Beginning frame */
+#define AST_HTML_BEGIN 4
+/*! End frame */
+#define AST_HTML_END 8
+/*! Load is complete */
+#define AST_HTML_LDCOMPLETE 16
+/*! Peer is unable to support HTML */
+#define AST_HTML_NOSUPPORT 17
+/*! Send URL, and track */
+#define AST_HTML_LINKURL 18
+/*! No more HTML linkage */
+#define AST_HTML_UNLINK 19
+/*! Reject link request */
+#define AST_HTML_LINKREJECT 20
+
+/* Data formats for capabilities and frames alike */
+/*! G.723.1 compression */
+#define AST_FORMAT_G723_1 (1 << 0)
+/*! GSM compression */
+#define AST_FORMAT_GSM (1 << 1)
+/*! Raw mu-law data (G.711) */
+#define AST_FORMAT_ULAW (1 << 2)
+/*! Raw A-law data (G.711) */
+#define AST_FORMAT_ALAW (1 << 3)
+/*! ADPCM (G.726, 32kbps, AAL2 codeword packing) */
+#define AST_FORMAT_G726_AAL2 (1 << 4)
+/*! ADPCM (IMA) */
+#define AST_FORMAT_ADPCM (1 << 5)
+/*! Raw 16-bit Signed Linear (8000 Hz) PCM */
+#define AST_FORMAT_SLINEAR (1 << 6)
+/*! LPC10, 180 samples/frame */
+#define AST_FORMAT_LPC10 (1 << 7)
+/*! G.729A audio */
+#define AST_FORMAT_G729A (1 << 8)
+/*! SpeeX Free Compression */
+#define AST_FORMAT_SPEEX (1 << 9)
+/*! iLBC Free Compression */
+#define AST_FORMAT_ILBC (1 << 10)
+/*! ADPCM (G.726, 32kbps, RFC3551 codeword packing) */
+#define AST_FORMAT_G726 (1 << 11)
+/*! G.722 */
+#define AST_FORMAT_G722 (1 << 12)
+/*! Raw 16-bit Signed Linear (16000 Hz) PCM */
+#define AST_FORMAT_SLINEAR16 (1 << 15)
+/*! Maximum audio mask */
+#define AST_FORMAT_AUDIO_MASK ((1 << 16)-1)
+/*! JPEG Images */
+#define AST_FORMAT_JPEG (1 << 16)
+/*! PNG Images */
+#define AST_FORMAT_PNG (1 << 17)
+/*! H.261 Video */
+#define AST_FORMAT_H261 (1 << 18)
+/*! H.263 Video */
+#define AST_FORMAT_H263 (1 << 19)
+/*! H.263+ Video */
+#define AST_FORMAT_H263_PLUS (1 << 20)
+/*! H.264 Video */
+#define AST_FORMAT_H264 (1 << 21)
+/*! MPEG4 Video */
+#define AST_FORMAT_MP4_VIDEO (1 << 22)
+#define AST_FORMAT_VIDEO_MASK (((1 << 25)-1) & ~(AST_FORMAT_AUDIO_MASK))
+/*! T.140 Text format - ITU T.140, RFC 4351*/
+#define AST_FORMAT_T140 (1 << 25)
+#define AST_FORMAT_TEXT_MASK (((1 << 30)-1) & ~(AST_FORMAT_AUDIO_MASK) & ~(AST_FORMAT_VIDEO_MASK))
+
+enum ast_control_frame_type {
+ AST_CONTROL_HANGUP = 1, /*!< Other end has hungup */
+ AST_CONTROL_RING = 2, /*!< Local ring */
+ AST_CONTROL_RINGING = 3, /*!< Remote end is ringing */
+ AST_CONTROL_ANSWER = 4, /*!< Remote end has answered */
+ AST_CONTROL_BUSY = 5, /*!< Remote end is busy */
+ AST_CONTROL_TAKEOFFHOOK = 6, /*!< Make it go off hook */
+ AST_CONTROL_OFFHOOK = 7, /*!< Line is off hook */
+ AST_CONTROL_CONGESTION = 8, /*!< Congestion (circuits busy) */
+ AST_CONTROL_FLASH = 9, /*!< Flash hook */
+ AST_CONTROL_WINK = 10, /*!< Wink */
+ AST_CONTROL_OPTION = 11, /*!< Set a low-level option */
+ AST_CONTROL_RADIO_KEY = 12, /*!< Key Radio */
+ AST_CONTROL_RADIO_UNKEY = 13, /*!< Un-Key Radio */
+ AST_CONTROL_PROGRESS = 14, /*!< Indicate PROGRESS */
+ AST_CONTROL_PROCEEDING = 15, /*!< Indicate CALL PROCEEDING */
+ AST_CONTROL_HOLD = 16, /*!< Indicate call is placed on hold */
+ AST_CONTROL_UNHOLD = 17, /*!< Indicate call is left from hold */
+ AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */
+};
+
+#define AST_SMOOTHER_FLAG_G729 (1 << 0)
+#define AST_SMOOTHER_FLAG_BE (1 << 1)
+
+/* Option identifiers and flags */
+#define AST_OPTION_FLAG_REQUEST 0
+#define AST_OPTION_FLAG_ACCEPT 1
+#define AST_OPTION_FLAG_REJECT 2
+#define AST_OPTION_FLAG_QUERY 4
+#define AST_OPTION_FLAG_ANSWER 5
+#define AST_OPTION_FLAG_WTF 6
+
+/*! Verify touchtones by muting audio transmission
+ (and reception) and verify the tone is still present */
+#define AST_OPTION_TONE_VERIFY 1
+
+/*! Put a compatible channel into TDD (TTY for the hearing-impared) mode */
+#define AST_OPTION_TDD 2
+
+/*! Relax the parameters for DTMF reception (mainly for radio use) */
+#define AST_OPTION_RELAXDTMF 3
+
+/*! Set (or clear) Audio (Not-Clear) Mode */
+#define AST_OPTION_AUDIO_MODE 4
+
+/*! Set channel transmit gain
+ * Option data is a single signed char
+ representing number of decibels (dB)
+ to set gain to (on top of any gain
+ specified in channel driver)
+*/
+#define AST_OPTION_TXGAIN 5
+
+/*! Set channel receive gain
+ * Option data is a single signed char
+ representing number of decibels (dB)
+ to set gain to (on top of any gain
+ specified in channel driver)
+*/
+#define AST_OPTION_RXGAIN 6
+
+/* set channel into "Operator Services" mode */
+#define AST_OPTION_OPRMODE 7
+
+/*! Explicitly enable or disable echo cancelation for the given channel */
+#define AST_OPTION_ECHOCAN 8
+
+struct oprmode {
+ struct ast_channel *peer;
+ int mode;
+} ;
+
+struct ast_option_header {
+ /* Always keep in network byte order */
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t flag:3;
+ uint16_t option:13;
+#else
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint16_t option:13;
+ uint16_t flag:3;
+#else
+#error Byte order not defined
+#endif
+#endif
+ uint8_t data[0];
+};
+
+
+/*! \brief Definition of supported media formats (codecs) */
+struct ast_format_list {
+ int bits; /*!< bitmask value */
+ char *name; /*!< short name */
+ int samplespersecond; /*!< Number of samples per second (8000/16000) */
+ char *desc; /*!< Description */
+ int fr_len; /*!< Single frame length in bytes */
+ int min_ms; /*!< Min value */
+ int max_ms; /*!< Max value */
+ int inc_ms; /*!< Increment */
+ int def_ms; /*!< Default value */
+ unsigned int flags; /*!< Smoother flags */
+ int cur_ms; /*!< Current value */
+};
+
+
+/*! \brief Requests a frame to be allocated
+ *
+ * \param source
+ * Request a frame be allocated. source is an optional source of the frame,
+ * len is the requested length, or "0" if the caller will supply the buffer
+ */
+#if 0 /* Unimplemented */
+struct ast_frame *ast_fralloc(char *source, int len);
+#endif
+
+/*!
+ * \brief Frees a frame
+ *
+ * \param fr Frame to free
+ * \param cache Whether to consider this frame for frame caching
+ */
+void ast_frame_free(struct ast_frame *fr, int cache);
+
+#define ast_frfree(fr) ast_frame_free(fr, 1)
+
+/*! \brief Makes a frame independent of any static storage
+ * \param fr frame to act upon
+ * Take a frame, and if it's not been malloc'd, make a malloc'd copy
+ * and if the data hasn't been malloced then make the
+ * data malloc'd. If you need to store frames, say for queueing, then
+ * you should call this function.
+ * \return Returns a frame on success, NULL on error
+ */
+struct ast_frame *ast_frisolate(struct ast_frame *fr);
+
+/*! \brief Copies a frame
+ * \param fr frame to copy
+ * Duplicates a frame -- should only rarely be used, typically frisolate is good enough
+ * \return Returns a frame on success, NULL on error
+ */
+struct ast_frame *ast_frdup(const struct ast_frame *fr);
+
+void ast_swapcopy_samples(void *dst, const void *src, int samples);
+
+/* Helpers for byteswapping native samples to/from
+ little-endian and big-endian. */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ast_frame_byteswap_le(fr) do { ; } while(0)
+#define ast_frame_byteswap_be(fr) do { struct ast_frame *__f = (fr); ast_swapcopy_samples(__f->data, __f->data, __f->samples); } while(0)
+#else
+#define ast_frame_byteswap_le(fr) do { struct ast_frame *__f = (fr); ast_swapcopy_samples(__f->data, __f->data, __f->samples); } while(0)
+#define ast_frame_byteswap_be(fr) do { ; } while(0)
+#endif
+
+
+/*! \brief Get the name of a format
+ * \param format id of format
+ * \return A static string containing the name of the format or "unknown" if unknown.
+ */
+char* ast_getformatname(int format);
+
+/*! \brief Get the names of a set of formats
+ * \param buf a buffer for the output string
+ * \param size size of buf (bytes)
+ * \param format the format (combined IDs of codecs)
+ * Prints a list of readable codec names corresponding to "format".
+ * ex: for format=AST_FORMAT_GSM|AST_FORMAT_SPEEX|AST_FORMAT_ILBC it will return "0x602 (GSM|SPEEX|ILBC)"
+ * \return The return value is buf.
+ */
+char* ast_getformatname_multiple(char *buf, size_t size, int format);
+
+/*!
+ * \brief Gets a format from a name.
+ * \param name string of format
+ * \return This returns the form of the format in binary on success, 0 on error.
+ */
+int ast_getformatbyname(const char *name);
+
+/*! \brief Get a name from a format
+ * Gets a name from a format
+ * \param codec codec number (1,2,4,8,16,etc.)
+ * \return This returns a static string identifying the format on success, 0 on error.
+ */
+char *ast_codec2str(int codec);
+
+/*! \name AST_Smoother
+*/
+/*@{ */
+/*! \page ast_smooth The AST Frame Smoother
+The ast_smoother interface was designed specifically
+to take frames of variant sizes and produce frames of a single expected
+size, precisely what you want to do.
+
+The basic interface is:
+
+- Initialize with ast_smoother_new()
+- Queue input frames with ast_smoother_feed()
+- Get output frames with ast_smoother_read()
+- when you're done, free the structure with ast_smoother_free()
+- Also see ast_smoother_test_flag(), ast_smoother_set_flags(), ast_smoother_get_flags(), ast_smoother_reset()
+*/
+struct ast_smoother;
+
+struct ast_smoother *ast_smoother_new(int bytes);
+void ast_smoother_set_flags(struct ast_smoother *smoother, int flags);
+int ast_smoother_get_flags(struct ast_smoother *smoother);
+int ast_smoother_test_flag(struct ast_smoother *s, int flag);
+void ast_smoother_free(struct ast_smoother *s);
+void ast_smoother_reset(struct ast_smoother *s, int bytes);
+int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap);
+struct ast_frame *ast_smoother_read(struct ast_smoother *s);
+#define ast_smoother_feed(s,f) __ast_smoother_feed(s, f, 0)
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ast_smoother_feed_be(s,f) __ast_smoother_feed(s, f, 1)
+#define ast_smoother_feed_le(s,f) __ast_smoother_feed(s, f, 0)
+#else
+#define ast_smoother_feed_be(s,f) __ast_smoother_feed(s, f, 0)
+#define ast_smoother_feed_le(s,f) __ast_smoother_feed(s, f, 1)
+#endif
+/*@} Doxygen marker */
+
+struct ast_format_list *ast_get_format_list_index(int index);
+struct ast_format_list *ast_get_format_list(size_t *size);
+void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix);
+
+/*! \page AudioCodecPref Audio Codec Preferences
+
+ In order to negotiate audio codecs in the order they are configured
+ in \<channel\>.conf for a device, we set up codec preference lists
+ in addition to the codec capabilities setting. The capabilities
+ setting is a bitmask of audio and video codecs with no internal
+ order. This will reflect the offer given to the other side, where
+ the prefered codecs will be added to the top of the list in the
+ order indicated by the "allow" lines in the device configuration.
+
+ Video codecs are not included in the preference lists since they
+ can't be transcoded and we just have to pick whatever is supported
+*/
+
+/*!
+ *\brief Initialize an audio codec preference to "no preference".
+ * \arg \ref AudioCodecPref
+*/
+void ast_codec_pref_init(struct ast_codec_pref *pref);
+
+/*!
+ * \brief Codec located at a particular place in the preference index.
+ * \arg \ref AudioCodecPref
+*/
+int ast_codec_pref_index(struct ast_codec_pref *pref, int index);
+
+/*! \brief Remove audio a codec from a preference list */
+void ast_codec_pref_remove(struct ast_codec_pref *pref, int format);
+
+/*! \brief Append a audio codec to a preference list, removing it first if it was already there
+*/
+int ast_codec_pref_append(struct ast_codec_pref *pref, int format);
+
+/*! \brief Prepend an audio codec to a preference list, removing it first if it was already there
+*/
+void ast_codec_pref_prepend(struct ast_codec_pref *pref, int format, int only_if_existing);
+
+/*! \brief Select the best audio format according to preference list from supplied options.
+ If "find_best" is non-zero then if nothing is found, the "Best" format of
+ the format list is selected, otherwise 0 is returned. */
+int ast_codec_choose(struct ast_codec_pref *pref, int formats, int find_best);
+
+/*! \brief Set packet size for codec
+*/
+int ast_codec_pref_setsize(struct ast_codec_pref *pref, int format, int framems);
+
+/*! \brief Get packet size for codec
+*/
+struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, int format);
+
+/*! \brief Parse an "allow" or "deny" line in a channel or device configuration
+ and update the capabilities mask and pref if provided.
+ Video codecs are not added to codec preference lists, since we can not transcode
+ \return Returns number of errors encountered during parsing
+ */
+int ast_parse_allow_disallow(struct ast_codec_pref *pref, int *mask, const char *list, int allowing);
+
+/*! \brief Dump audio codec preference list into a string */
+int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size);
+
+/*! \brief Shift an audio codec preference list up or down 65 bytes so that it becomes an ASCII string */
+void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right);
+
+/*! \brief Returns the number of samples contained in the frame */
+int ast_codec_get_samples(struct ast_frame *f);
+
+/*! \brief Returns the number of bytes for the number of samples of the given format */
+int ast_codec_get_len(int format, int samples);
+
+/*! \brief Appends a frame to the end of a list of frames, truncating the maximum length of the list */
+struct ast_frame *ast_frame_enqueue(struct ast_frame *head, struct ast_frame *f, int maxlen, int dupe);
+
+
+/*! \brief Gets duration in ms of interpolation frame for a format */
+static inline int ast_codec_interp_len(int format)
+{
+ return (format == AST_FORMAT_ILBC) ? 30 : 20;
+}
+
+/*!
+ \brief Adjusts the volume of the audio samples contained in a frame.
+ \param f The frame containing the samples (must be AST_FRAME_VOICE and AST_FORMAT_SLINEAR)
+ \param adjustment The number of dB to adjust up or down.
+ \return 0 for success, non-zero for an error
+ */
+int ast_frame_adjust_volume(struct ast_frame *f, int adjustment);
+
+/*!
+ \brief Sums two frames of audio samples.
+ \param f1 The first frame (which will contain the result)
+ \param f2 The second frame
+ \return 0 for success, non-zero for an error
+
+ The frames must be AST_FRAME_VOICE and must contain AST_FORMAT_SLINEAR samples,
+ and must contain the same number of samples.
+ */
+int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_FRAME_H */
diff --git a/trunk/include/asterisk/fskmodem.h b/trunk/include/asterisk/fskmodem.h
new file mode 100644
index 000000000..d50e8abde
--- /dev/null
+++ b/trunk/include/asterisk/fskmodem.h
@@ -0,0 +1,81 @@
+/*
+ * 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 FSK Modem Support
+ * \note Includes code and algorithms from the Zapata library.
+ */
+
+#ifndef _ASTERISK_FSKMODEM_H
+#define _ASTERISK_FSKMODEM_H
+
+#define PARITY_NONE 0
+#define PARITY_EVEN 1
+#define PARITY_ODD 2
+
+
+#define NCOLA 0x4000
+
+/* new filter structure */
+struct filter_struct {
+
+ int icoefs[8];
+ int ip;
+ int ixv[8];
+ int iyv[8];
+};
+
+typedef struct {
+ int nbit; /*!< Number of Data Bits (5,7,8) */
+ int parity; /*!< Parity 0=none 1=even 2=odd */
+ int instop; /*!< Number of Stop Bits */
+ int hdlc; /*!< Modo Packet */
+ int xi0;
+ int xi1;
+ int xi2;
+
+ int ispb;
+ int icont;
+ int bw; /*!< Band Selector*/
+ int f_mark_idx; /*!< Mark Frequency Index (f_M-500)/5 */
+ int f_space_idx; /*!< Space Frequency Index (f_S-500)/5 */
+ int state;
+
+ int pllispb; /*!<Pll autosense */
+ int pllids;
+ int pllispb2;
+
+ struct filter_struct mark_filter;
+ struct filter_struct space_filter;
+ struct filter_struct demod_filter;
+
+} fsk_data;
+
+/* \brief Retrieve a serial byte into outbyte.
+ Buffer is a pointer into a series of
+ shorts and len records the number of bytes in the buffer. len will be
+ overwritten with the number of bytes left that were not consumed.
+ \return return value is as follows:
+ \arg 0: Still looking for something...
+ \arg 1: An output byte was received and stored in outbyte
+ \arg -1: An error occured in the transmission
+ This must be called with at least 80 bytes of buffer. */
+int fsk_serial(fsk_data *fskd, short *buffer, int *len, int *outbyte);
+int fskmodem_init(fsk_data *fskd);
+
+#endif /* _ASTERISK_FSKMODEM_H */
diff --git a/trunk/include/asterisk/global_datastores.h b/trunk/include/asterisk/global_datastores.h
new file mode 100644
index 000000000..72edabac5
--- /dev/null
+++ b/trunk/include/asterisk/global_datastores.h
@@ -0,0 +1,36 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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 globally accessible channel datastores
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#ifndef _ASTERISK_GLOBAL_DATASTORE_H
+#define _ASTERISK_GLOBAL_DATASTORE_H
+
+#include "asterisk/channel.h"
+
+extern const struct ast_datastore_info dialed_interface_info;
+
+struct ast_dialed_interface {
+ AST_LIST_ENTRY(ast_dialed_interface) list;
+ char interface[1];
+};
+
+#endif
diff --git a/trunk/include/asterisk/hashtab.h b/trunk/include/asterisk/hashtab.h
new file mode 100644
index 000000000..ed9a95e84
--- /dev/null
+++ b/trunk/include/asterisk/hashtab.h
@@ -0,0 +1,324 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@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.
+ */
+#ifndef _ASTERISK_HASHTAB_H_
+#define _ASTERISK_HASHTAB_H_
+#define __USE_UNIX98 1 /* to get the MUTEX_RECURSIVE stuff */
+
+/*! \file
+ * \brief Generic (perhaps overly so) hashtable implementation
+ * \ref AstHash
+ */
+/*! \page AstHash Hash Table support in Asterisk
+
+A hash table is a structure that allows for an exact-match search
+in O(1) (or close to that) time.
+
+The method: given: a set of {key,val} pairs. (at a minimum).
+ given: a hash function, which, given a key,
+ will return an integer. Ideally, each key in the
+ set will have its own unique associated hash value.
+ This hash number will index into an array. "buckets"
+ are what the elements of this array are called. To
+ handle possible collisions in hash values, buckets can form a list.
+
+The key for a value must be contained in the value, or we won't
+be able to find it in the bucket list.
+
+This implementation is pretty generic, because:
+
+ 1. The value and key are expected to be in a structure
+ (along with other data, perhaps) and it's address is a "void *".
+ 2. The pointer to a compare function must be passed in at the
+ time of creation, and is stored in the hashtable.
+ 3. The pointer to a resize function, which returns 1 if the
+ hash table is to be grown. A default routine is provided
+ if the pointer is NULL, and uses the java hashtable metric
+ of a 75% load factor.
+ 4. The pointer to a "new size" function, which returns a preferable
+ new size for the hash table bucket array. By default, a function
+ is supplied which roughly doubles the size of the array, is provided.
+ This size should ideally be a prime number.
+ 5. The hashing function pointer must also be supplied. This function
+ must be written by the user to access the keys in the objects being
+ stored. Some helper functions that use a simple "mult by prime, add
+ the next char", sort of string hash, or a simple modulus of the hash
+ table size for ints, is provided; the user can use these simple
+ algorithms to generate a hash, or implement any other algorithms they
+ wish.
+ 6. Recently updated the hash routines to use Doubly-linked lists for buckets,
+ and added a doubly-linked list that threads thru every bucket in the table.
+ The list of all buckets is on the HashTab struct. The Traversal was modified
+ to go thru this list instead of searching the bucket array for buckets.
+ This also should make it safe to remove a bucket during the traversal.
+ Removal and destruction routines will work faster.
+*/
+
+struct ast_hashtab_bucket
+{
+ const void *object; /*!< whatever it is we are storing in this table */
+ struct ast_hashtab_bucket *next; /*!< a DLL of buckets in hash collision */
+ struct ast_hashtab_bucket *prev; /*!< a DLL of buckets in hash collision */
+ struct ast_hashtab_bucket *tnext; /*!< a DLL of all the hash buckets for traversal */
+ struct ast_hashtab_bucket *tprev; /*!< a DLL of all the hash buckets for traversal */
+};
+
+struct ast_hashtab
+{
+ struct ast_hashtab_bucket **array;
+ struct ast_hashtab_bucket *tlist; /*!< the head of a DLList of all the hashbuckets in the table (for traversal). */
+
+ int (*compare) (const void *a, const void *b); /*!< a ptr to func that returns int, and take two void* ptrs, compares them,
+ rets -1 if a < b; rets 0 if a==b; rets 1 if a>b */
+ int (*newsize) (struct ast_hashtab *tab); /*!< a ptr to func that returns int, a new size for hash tab, based on curr_size */
+ int (*resize) (struct ast_hashtab *tab); /*!< a function to decide whether this hashtable should be resized now */
+ unsigned int (*hash) (const void *obj); /*!< a hash func ptr for this table. Given a raw ptr to an obj,
+ it calcs a hash.*/
+ int hash_tab_size; /*!< the size of the bucket array */
+ int hash_tab_elements; /*!< the number of objects currently stored in the table */
+ int largest_bucket_size; /*!< a stat on the health of the table */
+ int resize_count; /*!< a count of the number of times this table has been
+ resized */
+ int do_locking; /*!< if 1 use locks to guarantee safety of insertions/deletions */
+ /* this spot reserved for the proper lock storage */
+ ast_rwlock_t lock; /* is this as good as it sounds? */
+};
+
+/*! \brief an iterator for traversing the buckets */
+struct ast_hashtab_iter
+{
+ struct ast_hashtab *tab;
+ struct ast_hashtab_bucket *next;
+};
+
+
+/* some standard, default routines for general use */
+
+/*! \brief For sizing the hash table, tells if num is prime or not */
+int ast_is_prime(int num);
+
+/*!
+ * \brief assumes a and b are char *
+ * \return 0 if they match
+*/
+int ast_hashtab_compare_strings(const void *a, const void *b);
+
+/*!
+ * \brief assumes a & b are strings
+ * \return 0 if they match (strcasecmp)
+*/
+int ast_hashtab_compare_strings_nocase(const void *a, const void *b);
+
+/*!
+ * \brief assumes a & b are int *
+ * \retval 0 if match
+ * \retval 1 a > b
+ * \retval -1 a < b
+*/
+int ast_hashtab_compare_ints(const void *a, const void *b);
+
+/*!
+ * \brief assumes a & b are short *
+ * \retval 0 if match
+ * \retval 1 a > b
+ * \retval -1 a < b
+*/
+int ast_hashtab_compare_shorts(const void *a, const void *b);
+
+/*!
+ * \brief determine if resize should occur
+ * \returns 1 if the table is 75% full or more
+*/
+int ast_hashtab_resize_java(struct ast_hashtab *tab);
+
+/*! \brief no resizing; always return 0 */
+int ast_hashtab_resize_tight(struct ast_hashtab *tab);
+
+/*! \brief no resizing; always return 0 */
+int ast_hashtab_resize_none(struct ast_hashtab *tab);
+
+/*! \brief Create a prime number roughly 2x the current table size */
+int ast_hashtab_newsize_java(struct ast_hashtab *tab);
+
+/* not yet specified, probably will return 1.5x the current table size */
+int ast_hashtab_newsize_tight(struct ast_hashtab *tab);
+
+/*! \brief always return current size -- no resizing */
+int ast_hashtab_newsize_none(struct ast_hashtab *tab);
+
+/*!
+ * \brief Hashes a string to a number
+ * \param obj
+ * \note A modulus is applied so it in the range 0 to mod-1
+*/
+unsigned int ast_hashtab_hash_string(const void *obj);
+
+/*! \brief Upperases each char before using them for a hash */
+unsigned int ast_hashtab_hash_string_nocase(const void *obj);
+
+
+unsigned int ast_hashtab_hash_string_sax(const void *obj); /* from Josh */
+
+
+unsigned int ast_hashtab_hash_int(const int num); /* right now, both these funcs are just result = num%modulus; */
+
+
+unsigned int ast_hashtab_hash_short(const short num);
+
+
+/*!
+ * \brief Create the hashtable list
+ * \param initial_buckets starting number of buckets
+ * \param compare a func ptr to compare two elements in the hash -- cannot be null
+ * \param resize a func ptr to decide if the table needs to be resized, a NULL ptr here will cause a default to be used
+ * \param newsize a func ptr that returns a new size of the array. A NULL will cause a default to be used
+ * \param hash a func ptr to do the hashing
+ * \param do_locking use locks to guarantee safety of iterators/insertion/deletion -- real simpleminded right now
+*/
+struct ast_hashtab * ast_hashtab_create(int initial_buckets,
+ int (*compare)(const void *a, const void *b),
+ int (*resize)(struct ast_hashtab *),
+ int (*newsize)(struct ast_hashtab *tab),
+ unsigned int (*hash)(const void *obj),
+ int do_locking );
+
+/*!
+ * \brief This func will free the hash table and all its memory.
+ * \note It doesn't touch the objects stored in it
+ * \param tab
+ * \param objdestroyfunc
+*/
+void ast_hashtab_destroy( struct ast_hashtab *tab, void (*objdestroyfunc)(void *obj));
+
+
+/*!
+ * \brief Insert without checking
+ * \param tab
+ * \param obj
+ *
+ * Normally, you'd insert "safely" by checking to see if the element is
+ * already there; in this case, you must already have checked. If an element
+ * is already in the hashtable, that matches this one, most likely this one
+ * will be found first.
+ * \note will force a resize if the resize func returns 1
+ * \retval 1 on success
+ * \retval 0 if there's a problem
+*/
+int ast_hashtab_insert_immediate(struct ast_hashtab *tab, const void *obj);
+
+/*!
+ * \brief Insert without checking, hashing or locking
+ * \param tab
+ * \param obj
+ * \param h hashed index value
+ *
+ * \note Will force a resize if the resize func returns 1
+ * \retval 1 on success
+ * \retval 0 if there's a problem
+*/
+int ast_hashtab_insert_immediate_bucket(struct ast_hashtab *tab, const void *obj, unsigned int h);
+
+/*!
+ * \brief Check and insert new object only if it is not there.
+ * \note Will force a resize if the resize func returns 1
+ * \retval 1 on success
+ * \retval 0 if there's a problem, or it's already there.
+*/
+int ast_hashtab_insert_safe(struct ast_hashtab *tab, const void *obj);
+
+/*!
+ * \brief Lookup this object in the hash table.
+ * \param tab
+ * \param obj
+ * \retval a ptr if found
+ * \retval NULL if not found
+*/
+void * ast_hashtab_lookup(struct ast_hashtab *tab, const void *obj);
+
+/*!
+ * \brief Use this if have the hash val for the object
+ * \note This and avoid the recalc of the hash (the modulus (table_size) is not applied)
+*/
+void * ast_hashtab_lookup_with_hash(struct ast_hashtab *tab, const void *obj, unsigned int hashval);
+
+/*!
+ * \brief Similar to ast_hashtab_lookup but sets h to the key hash value if the lookup fails.
+ * \note This has the modulus applied, and will not be useful for long term storage if the table is resizable.
+*/
+void * ast_hashtab_lookup_bucket(struct ast_hashtab *tab, const void *obj, unsigned int *h);
+
+/*! \brief Returns key stats for the table */
+void ast_hashtab_get_stats( struct ast_hashtab *tab, int *biggest_bucket_size, int *resize_count, int *num_objects, int *num_buckets);
+
+/*! \brief Returns the number of elements stored in the hashtab */
+int ast_hashtab_size( struct ast_hashtab *tab);
+
+/*! \brief Returns the size of the bucket array in the hashtab */
+int ast_hashtab_capacity( struct ast_hashtab *tab);
+
+/*! \brief Return a copy of the hash table */
+struct ast_hashtab *ast_hashtab_dup(struct ast_hashtab *tab, void *(*obj_dup_func)(const void *obj));
+
+/*! \brief Gives an iterator to hastable */
+struct ast_hashtab_iter *ast_hashtab_start_traversal(struct ast_hashtab *tab);
+
+/*! \brief end the traversal, free the iterator, unlock if necc. */
+void ast_hashtab_end_traversal(struct ast_hashtab_iter *it);
+
+/*! \brief Gets the next object in the list, advances iter one step returns null on end of traversal */
+void *ast_hashtab_next(struct ast_hashtab_iter *it);
+
+/*! \brief Looks up the object, removes the corresponding bucket */
+void *ast_hashtab_remove_object_via_lookup(struct ast_hashtab *tab, void *obj);
+
+/*! \brief Hash the object and then compare ptrs in bucket list instead of
+ calling the compare routine, will remove the bucket */
+void *ast_hashtab_remove_this_object(struct ast_hashtab *tab, void *obj);
+
+/* ------------------ */
+/* for lock-enabled traversals with ability to remove an object during the traversal*/
+/* ------------------ */
+
+/*! \brief Gives an iterator to hastable */
+struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *tab);
+
+/*! \brief Looks up the object, removes the corresponding bucket */
+void *ast_hashtab_remove_object_via_lookup_nolock(struct ast_hashtab *tab, void *obj);
+
+/*! \brief Hash the object and then compare ptrs in bucket list instead of
+ calling the compare routine, will remove the bucket */
+void *ast_hashtab_remove_this_object_nolock(struct ast_hashtab *tab, void *obj);
+
+/* ------------------ */
+/* ------------------ */
+
+/* user-controlled hashtab locking. Create a hashtab without locking, then call the
+ following locking routines yourself to lock the table between threads. */
+
+/*! \brief Call this after you create the table to init the lock */
+void ast_hashtab_initlock(struct ast_hashtab *tab);
+/*! \brief Request a write-lock on the table. */
+void ast_hashtab_wrlock(struct ast_hashtab *tab);
+/*! \brief Request a read-lock on the table -- don't change anything! */
+void ast_hashtab_rdlock(struct ast_hashtab *tab);
+/*! \brief release a read- or write- lock. */
+void ast_hashtab_unlock(struct ast_hashtab *tab);
+/*! \brief Call this before you destroy the table. */
+void ast_hashtab_destroylock(struct ast_hashtab *tab);
+
+
+#endif
diff --git a/trunk/include/asterisk/http.h b/trunk/include/asterisk/http.h
new file mode 100644
index 000000000..4cda5cacc
--- /dev/null
+++ b/trunk/include/asterisk/http.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef _ASTERISK_HTTP_H
+#define _ASTERISK_HTTP_H
+
+#include "asterisk/config.h"
+#include "asterisk/tcptls.h"
+#include "asterisk/linkedlists.h"
+
+/*!
+ * \file http.h
+ * \brief Support for Private Asterisk HTTP Servers.
+ * \note Note: The Asterisk HTTP servers are extremely simple and minimal and
+ * only support the "GET" method.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note In order to have TLS/SSL support, we need the openssl libraries.
+ * Still we can decide whether or not to use them by commenting
+ * in or out the DO_SSL macro.
+ * TLS/SSL support is basically implemented by reading from a config file
+ * (currently http.conf) the names of the certificate and cipher to use,
+ * and then run ssl_setup() to create an appropriate SSL_CTX (ssl_ctx)
+ * If we support multiple domains, presumably we need to read multiple
+ * certificates.
+ * When we are requested to open a TLS socket, we run make_file_from_fd()
+ * on the socket, to do the necessary setup. At the moment the context's name
+ * is hardwired in the function, but we can certainly make it into an extra
+ * parameter to the function.
+ * We declare most of ssl support variables unconditionally,
+ * because their number is small and this simplifies the code.
+ *
+ * \note: the ssl-support variables (ssl_ctx, do_ssl, certfile, cipher)
+ * and their setup should be moved to a more central place, e.g. asterisk.conf
+ * and the source files that processes it. Similarly, ssl_setup() should
+ * be run earlier in the startup process so modules have it available.
+ */
+
+
+/*! \brief HTTP Callbacks take the socket
+
+ \note The method and the path as arguments and should
+ return the content, allocated with malloc(). Status should be changed to reflect
+ the status of the request if it isn't 200 and title may be set to a malloc()'d string
+ to an appropriate title for non-200 responses. Content length may also be specified.
+\verbatim
+ The return value may include additional headers at the front and MUST include a blank
+ line with \r\n to provide separation between user headers and content (even if no
+ content is specified)
+\endverbatim
+*/
+typedef struct ast_str *(*ast_http_callback)(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength);
+
+/*! \brief Definition of a URI reachable in the embedded HTTP server */
+struct ast_http_uri {
+ AST_LIST_ENTRY(ast_http_uri) entry;
+ const char *description;
+ const char *uri;
+ unsigned int has_subtree:1;
+ /*! This URI mapping serves static content */
+ unsigned int static_content:1;
+ ast_http_callback callback;
+};
+
+/*! \brief Link into the Asterisk HTTP server */
+int ast_http_uri_link(struct ast_http_uri *urihandler);
+
+/*! \brief Return an ast_str malloc()'d string containing an HTTP error message */
+struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text);
+
+/*! \brief Destroy an HTTP server */
+void ast_http_uri_unlink(struct ast_http_uri *urihandler);
+
+int ast_http_init(void);
+int ast_http_reload(void);
+
+#endif /* _ASTERISK_SRV_H */
diff --git a/trunk/include/asterisk/image.h b/trunk/include/asterisk/image.h
new file mode 100644
index 000000000..cfe511904
--- /dev/null
+++ b/trunk/include/asterisk/image.h
@@ -0,0 +1,90 @@
+/*
+ * 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 General Asterisk channel definitions for image handling
+ */
+
+#ifndef _ASTERISK_IMAGE_H
+#define _ASTERISK_IMAGE_H
+
+/*! \brief structure associated with registering an image format */
+struct ast_imager {
+ char *name; /*!< Name */
+ char *desc; /*!< Description */
+ char *exts; /*!< Extension(s) (separated by '|' ) */
+ int format; /*!< Image format */
+ struct ast_frame *(*read_image)(int fd, int len); /*!< Read an image from a file descriptor */
+ int (*identify)(int fd); /*!< Identify if this is that type of file */
+ int (*write_image)(int fd, struct ast_frame *frame); /*!< Returns length written */
+ AST_LIST_ENTRY(ast_imager) list; /*!< For linked list */
+};
+
+/*!
+ * \brief Check for image support on a channel
+ * \param chan channel to check
+ * Checks the channel to see if it supports the transmission of images
+ * \return non-zero if image transmission is supported
+ */
+int ast_supports_images(struct ast_channel *chan);
+
+/*!
+ * \brief Sends an image
+ * \param chan channel to send image on
+ * \param filename filename of image to send (minus extension)
+ * Sends an image on the given channel.
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+int ast_send_image(struct ast_channel *chan, char *filename);
+
+/*!
+ * \brief Make an image
+ * \param filename filename of image to prepare
+ * \param preflang preferred language to get the image...?
+ * \param format the format of the file
+ * Make an image from a filename ??? No estoy positivo
+ * \retval an ast_frame on success
+ * \retval NULL on failure
+ */
+struct ast_frame *ast_read_image(char *filename, const char *preflang, int format);
+
+/*!
+ * \brief Register image format
+ * \param imgdrv Populated ast_imager structure with info to register
+ * Registers an image format
+ * \return 0 regardless
+ */
+int ast_image_register(struct ast_imager *imgdrv);
+
+/*!
+ * \brief Unregister an image format
+ * \param imgdrv pointer to the ast_imager structure you wish to unregister
+ * Unregisters the image format passed in.
+ * Returns nothing
+ */
+void ast_image_unregister(struct ast_imager *imgdrv);
+
+/*!
+ * \brief Initialize image stuff
+ * Initializes all the various image stuff. Basically just registers the cli stuff
+ * \return 0 all the time
+ */
+int ast_image_init(void);
+
+#endif /* _ASTERISK_IMAGE_H */
diff --git a/trunk/include/asterisk/indications.h b/trunk/include/asterisk/indications.h
new file mode 100644
index 000000000..2841a9fdf
--- /dev/null
+++ b/trunk/include/asterisk/indications.h
@@ -0,0 +1,89 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * 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.
+ */
+
+/*! \file
+ * \brief BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Primary Author: Pauline Middelink <middelink@polyware.nl>
+ *
+ */
+
+#ifndef _ASTERISK_INDICATIONS_H
+#define _ASTERISK_INDICATIONS_H
+
+#include "asterisk/lock.h"
+
+/*! \brief Description is a series of tones of the format:
+ [!]freq1[+freq2][/duration] separated by commas. There
+ are no spaces. The sequence is repeated back to the
+ first tone description not preceeded by !. Duration is
+ specified in milliseconds */
+struct ind_tone_zone_sound {
+ struct ind_tone_zone_sound *next; /*!< next element */
+ const char *name; /*!< Identifing name */
+ const char *data; /*!< Actual zone description */
+};
+
+struct ind_tone_zone {
+ AST_RWLIST_ENTRY(ind_tone_zone) list;
+ char country[5]; /*!< Country code */
+ char alias[5]; /*!< is this an alias? */
+ char description[40]; /*!< Description */
+ int nrringcadence; /*!< # registered ringcadence elements */
+ int *ringcadence; /*!< Ring cadence */
+ struct ind_tone_zone_sound *tones; /*!< The known tones for this zone */
+};
+
+/*! \brief set the default tone country */
+int ast_set_indication_country(const char *country);
+
+/*! \brief locate tone_zone, given the country. if country == NULL, use the default country */
+struct ind_tone_zone *ast_get_indication_zone(const char *country);
+/*! \brief locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
+struct ind_tone_zone_sound *ast_get_indication_tone(const struct ind_tone_zone *zone, const char *indication);
+
+/*! \brief add a new country, if country exists, it will be replaced. */
+int ast_register_indication_country(struct ind_tone_zone *country);
+/*! \brief remove an existing country and all its indications, country must exist */
+int ast_unregister_indication_country(const char *country);
+/*! \brief add a new indication to a tone_zone. tone_zone must exist. if the indication already
+ * exists, it will be replaced. */
+int ast_register_indication(struct ind_tone_zone *zone, const char *indication, const char *tonelist);
+/*! \brief remove an existing tone_zone's indication. tone_zone must exist */
+int ast_unregister_indication(struct ind_tone_zone *zone, const char *indication);
+
+/*! \brief Start a tone-list going */
+int ast_playtones_start(struct ast_channel *chan, int vol, const char* tonelist, int interruptible);
+/*! \brief Stop the tones from playing */
+void ast_playtones_stop(struct ast_channel *chan);
+
+/*! \brief support for walking through a list of indications */
+struct ind_tone_zone *ast_walk_indications(const struct ind_tone_zone *cur);
+
+#if 0
+extern struct ind_tone_zone *tone_zones;
+extern ast_mutex_t tzlock;
+#endif
+
+#endif /* _ASTERISK_INDICATIONS_H */
diff --git a/trunk/include/asterisk/inline_api.h b/trunk/include/asterisk/inline_api.h
new file mode 100644
index 000000000..2347d09d7
--- /dev/null
+++ b/trunk/include/asterisk/inline_api.h
@@ -0,0 +1,66 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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.
+ */
+
+#ifndef __ASTERISK_INLINEAPI_H
+#define __ASTERISK_INLINEAPI_H
+
+/*! \file
+ * \brief Inlinable API function macro
+
+ Small API functions that are candidates for inlining need to be specially
+ declared and defined, to ensure that the 'right thing' always happens.
+ For example:
+ - there must _always_ be a non-inlined version of the function
+ available for modules compiled out of the tree to link to
+ - references to a function that cannot be inlined (for any
+ reason that the compiler deems proper) must devolve into an
+ 'extern' reference, instead of 'static', so that multiple
+ copies of the function body are not built in different modules
+ - when LOW_MEMORY is defined, inlining should be disabled
+ completely, even if the compiler is configured to support it
+
+ The AST_INLINE_API macro allows this to happen automatically, when
+ used to define your function. Proper usage is as follows:
+ - define your function one place, in a header file, using the macro
+ to wrap the function (see strings.h or time.h for examples)
+ - choose a module to 'host' the function body for non-inline
+ usages, and in that module _only_, define AST_API_MODULE before
+ including the header file
+ */
+
+#if !defined(LOW_MEMORY)
+
+#if !defined(AST_API_MODULE)
+#define AST_INLINE_API(hdr, body) hdr; extern inline hdr body
+#else
+#define AST_INLINE_API(hdr, body) hdr; hdr body
+#endif
+
+#else /* defined(LOW_MEMORY) */
+
+#if !defined(AST_API_MODULE)
+#define AST_INLINE_API(hdr, body) hdr;
+#else
+#define AST_INLINE_API(hdr, body) hdr; hdr body
+#endif
+
+#endif
+
+#undef AST_API_MODULE
+
+#endif /* __ASTERISK_INLINEAPI_H */
diff --git a/trunk/include/asterisk/io.h b/trunk/include/asterisk/io.h
new file mode 100644
index 000000000..ee22994a4
--- /dev/null
+++ b/trunk/include/asterisk/io.h
@@ -0,0 +1,150 @@
+/*
+ * 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 I/O Management (derived from Cheops-NG)
+ */
+
+#ifndef _ASTERISK_IO_H
+#define _ASTERISK_IO_H
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h> /* For POLL* constants */
+#else
+#include "asterisk/poll-compat.h"
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! Input ready */
+#define AST_IO_IN POLLIN
+/*! Output ready */
+#define AST_IO_OUT POLLOUT
+/*! Priority input ready */
+#define AST_IO_PRI POLLPRI
+
+/* Implicitly polled for */
+/*! Error condition (errno or getsockopt) */
+#define AST_IO_ERR POLLERR
+/*! Hangup */
+#define AST_IO_HUP POLLHUP
+/*! Invalid fd */
+#define AST_IO_NVAL POLLNVAL
+
+/*! \brief
+ * An Asterisk IO callback takes its id, a file descriptor, list of events, and
+ * callback data as arguments and returns 0 if it should not be
+ * run again, or non-zero if it should be run again.
+ */
+
+struct io_context;
+
+/*!
+ * \brief Creates a context
+ * Create a context for I/O operations
+ * Basically mallocs an IO structure and sets up some default values.
+ * \return an allocated io_context structure
+ */
+struct io_context *io_context_create(void);
+
+/*!
+ * \brief Destroys a context
+ * \param ioc structure to destroy
+ * Destroy a context for I/O operations
+ * Frees all memory associated with the given io_context structure along with the structure itself
+ */
+void io_context_destroy(struct io_context *ioc);
+
+typedef int (*ast_io_cb)(int *id, int fd, short events, void *cbdata);
+#define AST_IO_CB(a) ((ast_io_cb)(a))
+
+/*!
+ * \brief Adds an IO context
+ * \param ioc which context to use
+ * \param fd which fd to monitor
+ * \param callback callback function to run
+ * \param events event mask of events to wait for
+ * \param data data to pass to the callback
+ * Watch for any of revents activites on fd, calling callback with data as
+ * callback data.
+ * \retval a pointer to ID of the IO event
+ * \retval NULL on failure
+ */
+int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data);
+
+/*!
+ * \brief Changes an IO handler
+ * \param ioc which context to use
+ * \param id
+ * \param fd the fd you wish it to contain now
+ * \param callback new callback function
+ * \param events event mask to wait for
+ * \param data data to pass to the callback function
+ * Change an I/O handler, updating fd if > -1, callback if non-null,
+ * and revents if >-1, and data if non-null.
+ * \retval a pointer to the ID of the IO event
+ * \retval NULL on failure
+ */
+int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data);
+
+/*!
+ * \brief Removes an IO context
+ * \param ioc which io_context to remove it from
+ * \param id which ID to remove
+ * Remove an I/O id from consideration
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_io_remove(struct io_context *ioc, int *id);
+
+/*!
+ * \brief Waits for IO
+ * \param ioc which context to act upon
+ * \param howlong how many milliseconds to wait
+ * Wait for I/O to happen, returning after
+ * howlong milliseconds, and after processing
+ * any necessary I/O.
+ * \return he number of I/O events which took place.
+ */
+int ast_io_wait(struct io_context *ioc, int howlong);
+
+/*!
+ * \brief Dumps the IO array.
+ * Debugging: Dump everything in the I/O array
+ */
+void ast_io_dump(struct io_context *ioc);
+
+/*! Set fd into non-echoing mode (if fd is a tty) */
+
+int ast_hide_password(int fd);
+
+/*!
+ * \brief Restores TTY mode.
+ * Call with result from previous ast_hide_password
+ */
+int ast_restore_tty(int fd, int oldstatus);
+
+int ast_get_termcols(int fd);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_IO_H */
diff --git a/trunk/include/asterisk/jabber.h b/trunk/include/asterisk/jabber.h
new file mode 100644
index 000000000..4bb8b9130
--- /dev/null
+++ b/trunk/include/asterisk/jabber.h
@@ -0,0 +1,201 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matt O'Gorman <mogorman@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 AJI - The Asterisk Jabber Interface
+ * \arg \ref AJI_intro
+ * \ref res_jabber.c
+ * \author Matt O'Gorman <mogorman@digium.com>
+ * \extref IKSEMEL http://iksemel.jabberstudio.org
+ *
+ * \page AJI_intro AJI - The Asterisk Jabber Interface
+ *
+ * The Asterisk Jabber Interface, AJI, publishes an API for
+ * modules to use jabber communication. res_jabber.c implements
+ * a Jabber client and a component that can connect as a service
+ * to Jabber servers.
+ *
+ * \section External dependencies
+ * AJI use the IKSEMEL library found at http://iksemel.jabberstudio.org/
+ *
+ * \section Files
+ * - res_jabber.c
+ * - jabber.h
+ * - chan_gtalk.c
+ *
+ */
+
+#ifndef _ASTERISK_JABBER_H
+#define _ASTERISK_JABBER_H
+
+#ifdef HAVE_OPENSSL
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#define TRY_SECURE 2
+#define SECURE 4
+/* file is read by blocks with this size */
+#define NET_IO_BUF_SIZE 4096
+/* Return value for timeout connection expiration */
+#define IKS_NET_EXPIRED 12
+
+#endif /* HAVE_OPENSSL */
+
+#include <iksemel.h>
+#include "asterisk/astobj.h"
+#include "asterisk/linkedlists.h"
+
+/*
+ * As per RFC 3920 - section 3.1, the maximum length for a full Jabber ID
+ * is 3071 bytes.
+ * The ABNF syntax for jid :
+ * jid = [node "@" ] domain [ "/" resource ]
+ * Each allowable portion of a JID (node identifier, domain identifier,
+ * and resource identifier) MUST NOT be more than 1023 bytes in length,
+ * resulting in a maximum total size (including the '@' and '/' separators)
+ * of 3071 bytes.
+ */
+#define AJI_MAX_JIDLEN 3071
+#define AJI_MAX_RESJIDLEN 1023
+
+enum aji_state {
+ AJI_DISCONNECTING,
+ AJI_DISCONNECTED,
+ AJI_CONNECTING,
+ AJI_CONNECTED
+};
+
+enum {
+ AJI_AUTOPRUNE = (1 << 0),
+ AJI_AUTOREGISTER = (1 << 1)
+};
+
+enum aji_btype {
+ AJI_USER=0,
+ AJI_TRANS=1,
+ AJI_UTRANS=2
+};
+
+struct aji_version {
+ char version[50];
+ int jingle;
+ struct aji_capabilities *parent;
+ struct aji_version *next;
+};
+
+struct aji_capabilities {
+ char node[200];
+ struct aji_version *versions;
+ struct aji_capabilities *next;
+};
+
+struct aji_resource {
+ int status;
+ char resource[AJI_MAX_RESJIDLEN];
+ char *description;
+ struct aji_version *cap;
+ int priority;
+ struct aji_resource *next;
+};
+
+struct aji_message {
+ char *from;
+ char *message;
+ char id[25];
+ time_t arrived;
+ AST_LIST_ENTRY(aji_message) list;
+};
+
+struct aji_buddy {
+ ASTOBJ_COMPONENTS_FULL(struct aji_buddy, AJI_MAX_JIDLEN, 1);
+ char channel[160];
+ struct aji_resource *resources;
+ enum aji_btype btype;
+ unsigned int flags;
+};
+
+struct aji_buddy_container {
+ ASTOBJ_CONTAINER_COMPONENTS(struct aji_buddy);
+};
+
+struct aji_transport_container {
+ ASTOBJ_CONTAINER_COMPONENTS(struct aji_transport);
+};
+
+struct aji_client {
+ ASTOBJ_COMPONENTS(struct aji_client);
+ char password[160];
+ char user[AJI_MAX_JIDLEN];
+ char serverhost[AJI_MAX_RESJIDLEN];
+ char statusmessage[256];
+ char name_space[256];
+ char sid[10]; /* Session ID */
+ char mid[6]; /* Message ID */
+ iksid *jid;
+ iksparser *p;
+ iksfilter *f;
+ ikstack *stack;
+#ifdef HAVE_OPENSSL
+ SSL_CTX *ssl_context;
+ SSL *ssl_session;
+ SSL_METHOD *ssl_method;
+ unsigned int stream_flags;
+#endif /* HAVE_OPENSSL */
+ enum aji_state state;
+ int port;
+ int debug;
+ int usetls;
+ int forcessl;
+ int usesasl;
+ int keepalive;
+ int allowguest;
+ int timeout;
+ int message_timeout;
+ int authorized;
+ unsigned int flags;
+ int component; /* 0 client, 1 component */
+ struct aji_buddy_container buddies;
+ AST_LIST_HEAD(messages,aji_message) messages;
+ void *jingle;
+ pthread_t thread;
+ int priority;
+ enum ikshowtype status;
+};
+
+struct aji_client_container{
+ ASTOBJ_CONTAINER_COMPONENTS(struct aji_client);
+};
+
+/* !Send XML stanza over the established XMPP connection */
+int ast_aji_send(struct aji_client *client, iks *x);
+/*! Send jabber chat message from connected client to jabber URI */
+int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message);
+/*! Disconnect jabber client */
+int ast_aji_disconnect(struct aji_client *client);
+int ast_aji_check_roster(void);
+void ast_aji_increment_mid(char *mid);
+/*! Open Chat session */
+int ast_aji_create_chat(struct aji_client *client,char *room, char *server, char *topic);
+/*! Invite to opened Chat session */
+int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message);
+/*! Join existing Chat session */
+int ast_aji_join_chat(struct aji_client *client,char *room);
+struct aji_client *ast_aji_get_client(const char *name);
+struct aji_client_container *ast_aji_get_clients(void);
+
+#endif
diff --git a/trunk/include/asterisk/jingle.h b/trunk/include/asterisk/jingle.h
new file mode 100644
index 000000000..bc3b0f217
--- /dev/null
+++ b/trunk/include/asterisk/jingle.h
@@ -0,0 +1,61 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matt O'Gorman <mogorman@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 Jingle definitions for chan_jingle
+ *
+ * \ref chan_jingle.c
+ *
+ * \author Matt O'Gorman <mogorman@digium.com>
+ */
+
+
+#ifndef _ASTERISK_JINGLE_H
+#define _ASTERISK_JINGLE_H
+
+#include <iksemel.h>
+#include "asterisk/astobj.h"
+
+
+/* Jingle Constants */
+
+#define JINGLE_NODE "jingle"
+#define GOOGLE_NODE "session"
+
+#define JINGLE_NS "http://www.xmpp.org/extensions/xep-0166.html#ns"
+#define JINGLE_AUDIO_RTP_NS "http://www.xmpp.org/extensions/xep-0167.html#ns"
+#define JINGLE_ICE_UDP_NS "http://www.xmpp.org/extensions/xep-0176.html#ns-udp"
+#define JINGLE_DTMF_NS "http://www.xmpp.org/extensions/xep-0181.html#ns"
+#define JINGLE_DTMF_NS_ERRORS "http://www.xmpp.org/extensions/xep-0181.html#ns-errors"
+#define GOOGLE_NS "http://www.google.com/session"
+
+#define JINGLE_SID "sid"
+#define GOOGLE_SID "id"
+
+#define JINGLE_INITIATE "session-initiate"
+
+#define JINGLE_ACCEPT "session-accept"
+#define GOOGLE_ACCEPT "accept"
+
+#define JINGLE_NEGOTIATE "transport-info"
+#define GOOGLE_NEGOTIATE "candidates"
+
+#define JINGLE_INFO "session-info"
+#define JINGLE_TERMINATE "session-terminate"
+
+#endif
diff --git a/trunk/include/asterisk/libresample.h b/trunk/include/asterisk/libresample.h
new file mode 120000
index 000000000..d71269da1
--- /dev/null
+++ b/trunk/include/asterisk/libresample.h
@@ -0,0 +1 @@
+../../main/libresample/include/libresample.h \ No newline at end of file
diff --git a/trunk/include/asterisk/linkedlists.h b/trunk/include/asterisk/linkedlists.h
new file mode 100644
index 000000000..1f3285dfa
--- /dev/null
+++ b/trunk/include/asterisk/linkedlists.h
@@ -0,0 +1,775 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@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.
+ */
+
+#ifndef ASTERISK_LINKEDLISTS_H
+#define ASTERISK_LINKEDLISTS_H
+
+#include "asterisk/lock.h"
+
+/*!
+ \file linkedlists.h
+ \brief A set of macros to manage forward-linked lists.
+*/
+
+/*!
+ \brief Locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_LIST_LOCK(head) \
+ ast_mutex_lock(&(head)->lock)
+
+/*!
+ \brief Write locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWLIST_WRLOCK(head) \
+ ast_rwlock_wrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWLIST_RDLOCK(head) \
+ ast_rwlock_rdlock(&(head)->lock)
+
+/*!
+ \brief Locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_LIST_TRYLOCK(head) \
+ ast_mutex_trylock(&(head)->lock)
+
+/*!
+ \brief Write locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWLIST_TRYWRLOCK(head) \
+ ast_rwlock_trywrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWLIST_TRYRDLOCK(head) \
+ ast_rwlock_tryrdlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove an exclusive lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_LIST_UNLOCK(head) \
+ ast_mutex_unlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a read/write based list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove a read or write lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_RWLIST_UNLOCK(head) \
+ ast_rwlock_unlock(&(head)->lock)
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_RWLIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_RWLIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type (with no lock).
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD_NOLOCK(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD_NOLOCK(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+}
+
+/*!
+ \brief Defines initial values for a declaration of AST_LIST_HEAD
+*/
+#define AST_LIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_MUTEX_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_RWLIST_HEAD
+*/
+#define AST_RWLIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_RWLOCK_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK
+*/
+#define AST_LIST_HEAD_NOLOCK_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ }
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+#define AST_LIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name; \
+static void __attribute__ ((constructor)) __init_##name(void) \
+{ \
+ AST_LIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) __fini_##name(void) \
+{ \
+ AST_LIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_LIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name = AST_LIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_RWLIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#ifndef AST_RWLOCK_INIT_VALUE
+#define AST_RWLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name; \
+static void __attribute__ ((constructor)) __init_##name(void) \
+{ \
+ AST_RWLIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) __fini_##name(void) \
+{ \
+ AST_RWLIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_RWLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name = AST_RWLIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+
+ This is the same as AST_LIST_HEAD_STATIC, except without the lock included.
+*/
+#define AST_LIST_HEAD_NOLOCK_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+} name = AST_LIST_HEAD_NOLOCK_INIT_VALUE
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_mutex_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes an rwlist head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_RWLIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_rwlock_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value.
+*/
+#define AST_LIST_HEAD_SET_NOLOCK(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+} while (0)
+
+/*!
+ \brief Declare a forward link structure inside a list entry.
+ \param type This is the type of each list entry.
+
+ This macro declares a structure to be used to link list entries together.
+ It must be used inside the definition of the structure named in
+ \a type, as follows:
+
+ \code
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ \endcode
+
+ The field name \a list here is arbitrary, and can be anything you wish.
+*/
+#define AST_LIST_ENTRY(type) \
+struct { \
+ struct type *next; \
+}
+
+#define AST_RWLIST_ENTRY AST_LIST_ENTRY
+
+/*!
+ \brief Returns the first entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_LIST_FIRST(head) ((head)->first)
+
+#define AST_RWLIST_FIRST AST_LIST_FIRST
+
+/*!
+ \brief Returns the last entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_LIST_LAST(head) ((head)->last)
+
+#define AST_RWLIST_LAST AST_LIST_LAST
+
+/*!
+ \brief Returns the next entry in the list after the given entry.
+ \param elm This is a pointer to the current entry.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+*/
+#define AST_LIST_NEXT(elm, field) ((elm)->field.next)
+
+#define AST_RWLIST_NEXT AST_LIST_NEXT
+
+/*!
+ \brief Checks whether the specified list contains any entries.
+ \param head This is a pointer to the list head structure
+
+ \return non-zero if the list has entries
+ \return zero if not.
+ */
+#define AST_LIST_EMPTY(head) (AST_LIST_FIRST(head) == NULL)
+
+#define AST_RWLIST_EMPTY AST_LIST_EMPTY
+
+/*!
+ \brief Loops over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is use to loop over (traverse) the entries in a list. It uses a
+ \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+ \code
+ static AST_LIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_LIST_TRAVERSE(&entries, current, list) {
+ (do something with current here)
+ }
+ \endcode
+ \warning If you modify the forward-link pointer contained in the \a current entry while
+ inside the loop, the behavior will be unpredictable. At a minimum, the following
+ macros will modify the forward-link pointer, and should not be used inside
+ AST_LIST_TRAVERSE() against the entry pointed to by the \a current pointer without
+ careful consideration of their consequences:
+ \li AST_LIST_NEXT() (when used as an lvalue)
+ \li AST_LIST_INSERT_AFTER()
+ \li AST_LIST_INSERT_HEAD()
+ \li AST_LIST_INSERT_TAIL()
+*/
+#define AST_LIST_TRAVERSE(head,var,field) \
+ for((var) = (head)->first; (var); (var) = (var)->field.next)
+
+#define AST_RWLIST_TRAVERSE AST_LIST_TRAVERSE
+
+/*!
+ \brief Loops safely over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is used to safely loop over (traverse) the entries in a list. It
+ uses a \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+
+ \code
+ static AST_LIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&entries, current, list) {
+ (do something with current here)
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ \endcode
+
+ It differs from AST_LIST_TRAVERSE() in that the code inside the loop can modify
+ (or even free, after calling AST_LIST_REMOVE_CURRENT()) the entry pointed to by
+ the \a current pointer without affecting the loop traversal.
+*/
+#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field) { \
+ typeof((head)) __list_head = head; \
+ typeof(__list_head->first) __list_next; \
+ typeof(__list_head->first) __list_prev = NULL; \
+ typeof(__list_head->first) __new_prev = NULL; \
+ for ((var) = __list_head->first, __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL; \
+ (var); \
+ __list_prev = __new_prev, (var) = __list_next, \
+ __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL \
+ )
+
+#define AST_RWLIST_TRAVERSE_SAFE_BEGIN AST_LIST_TRAVERSE_SAFE_BEGIN
+
+/*!
+ \brief Removes the \a current entry from a list during a traversal.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+ block; it is used to unlink the current entry from the list without affecting
+ the list traversal (and without having to re-traverse the list to modify the
+ previous entry, if any).
+ */
+#define AST_LIST_REMOVE_CURRENT(field) do { \
+ __new_prev->field.next = NULL; \
+ __new_prev = __list_prev; \
+ if (__list_prev) \
+ __list_prev->field.next = __list_next; \
+ else \
+ __list_head->first = __list_next; \
+ if (!__list_next) \
+ __list_head->last = __list_prev; \
+ } while (0)
+
+#define AST_RWLIST_REMOVE_CURRENT AST_LIST_REMOVE_CURRENT
+
+#define AST_LIST_MOVE_CURRENT(newhead, field) do { \
+ typeof ((newhead)->first) __list_cur = __new_prev; \
+ AST_LIST_REMOVE_CURRENT(field); \
+ AST_LIST_INSERT_TAIL((newhead), __list_cur, field); \
+ } while (0)
+
+#define AST_RWLIST_MOVE_CURRENT AST_LIST_MOVE_CURRENT
+
+/*!
+ \brief Inserts a list entry before the current entry during a traversal.
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+ block.
+ */
+#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field) do { \
+ if (__list_prev) { \
+ (elm)->field.next = __list_prev->field.next; \
+ __list_prev->field.next = elm; \
+ } else { \
+ (elm)->field.next = __list_head->first; \
+ __list_head->first = (elm); \
+ } \
+ __new_prev = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_BEFORE_CURRENT AST_LIST_INSERT_BEFORE_CURRENT
+
+/*!
+ \brief Closes a safe loop traversal block.
+ */
+#define AST_LIST_TRAVERSE_SAFE_END }
+
+#define AST_RWLIST_TRAVERSE_SAFE_END AST_LIST_TRAVERSE_SAFE_END
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_init(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_RWLIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_init(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_LIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_RWLIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list). There is no embedded lock handling
+ with this macro.
+*/
+#define AST_LIST_HEAD_INIT_NOLOCK(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+}
+
+/*!
+ \brief Inserts a list entry after a given entry.
+ \param head This is a pointer to the list head structure
+ \param listelm This is a pointer to the entry after which the new entry should
+ be inserted.
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.next = (listelm)->field.next; \
+ (listelm)->field.next = (elm); \
+ if ((head)->last == (listelm)) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_AFTER AST_LIST_INSERT_AFTER
+
+/*!
+ \brief Inserts a list entry at the head of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.next = (head)->first; \
+ (head)->first = (elm); \
+ if (!(head)->last) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_HEAD AST_LIST_INSERT_HEAD
+
+/*!
+ \brief Appends a list entry to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be appended.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ Note: The link field in the appended entry is \b not modified, so if it is
+ actually the head of a list itself, the entire list will be appended
+ temporarily (until the next AST_LIST_INSERT_TAIL is performed).
+ */
+#define AST_LIST_INSERT_TAIL(head, elm, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (elm); \
+ (head)->last = (elm); \
+ } else { \
+ (head)->last->field.next = (elm); \
+ (head)->last = (elm); \
+ } \
+} while (0)
+
+#define AST_RWLIST_INSERT_TAIL AST_LIST_INSERT_TAIL
+
+/*!
+ \brief Appends a whole list to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param list This is a pointer to the list to be appended.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ Note: The source list (the \a list parameter) will be empty after
+ calling this macro (the list entries are \b moved to the target list).
+ */
+#define AST_LIST_APPEND_LIST(head, list, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (list)->first; \
+ (head)->last = (list)->last; \
+ } else { \
+ (head)->last->field.next = (list)->first; \
+ (head)->last = (list)->last; \
+ } \
+ (list)->first = NULL; \
+ (list)->last = NULL; \
+} while (0)
+
+#define AST_RWLIST_APPEND_LIST AST_LIST_APPEND_LIST
+
+/*!
+ \brief Removes and returns the head entry from a list.
+ \param head This is a pointer to the list head structure
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ Removes the head entry from the list, and returns a pointer to it.
+ This macro is safe to call on an empty list.
+ */
+#define AST_LIST_REMOVE_HEAD(head, field) ({ \
+ typeof((head)->first) cur = (head)->first; \
+ if (cur) { \
+ (head)->first = cur->field.next; \
+ cur->field.next = NULL; \
+ if ((head)->last == cur) \
+ (head)->last = NULL; \
+ } \
+ cur; \
+ })
+
+#define AST_RWLIST_REMOVE_HEAD AST_LIST_REMOVE_HEAD
+
+/*!
+ \brief Removes a specific entry from a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be removed.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ \warning The removed entry is \b not freed nor modified in any way.
+ */
+#define AST_LIST_REMOVE(head, elm, field) ({ \
+ __typeof(elm) __res = NULL; \
+ if ((head)->first == (elm)) { \
+ __res = (head)->first; \
+ (head)->first = (elm)->field.next; \
+ if ((head)->last == (elm)) \
+ (head)->last = NULL; \
+ } else { \
+ typeof(elm) curelm = (head)->first; \
+ while (curelm && (curelm->field.next != (elm))) \
+ curelm = curelm->field.next; \
+ if (curelm) { \
+ __res = (elm); \
+ curelm->field.next = (elm)->field.next; \
+ if ((head)->last == (elm)) \
+ (head)->last = curelm; \
+ } \
+ } \
+ (elm)->field.next = NULL; \
+ (__res); \
+})
+
+#define AST_RWLIST_REMOVE AST_LIST_REMOVE
+
+#endif /* _ASTERISK_LINKEDLISTS_H */
diff --git a/trunk/include/asterisk/localtime.h b/trunk/include/asterisk/localtime.h
new file mode 100644
index 000000000..dd92871e9
--- /dev/null
+++ b/trunk/include/asterisk/localtime.h
@@ -0,0 +1,48 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Tilghman Lesher <tlesher@vcch.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 Custom localtime functions for multiple timezones
+ */
+
+#ifndef _ASTERISK_LOCALTIME_H
+#define _ASTERISK_LOCALTIME_H
+
+struct ast_tm {
+ int tm_sec; /*!< Seconds. [0-60] (1 leap second) */
+ int tm_min; /*!< Minutes. [0-59] */
+ int tm_hour; /*!< Hours. [0-23] */
+ int tm_mday; /*!< Day. [1-31] */
+ int tm_mon; /*!< Month. [0-11] */
+ int tm_year; /*!< Year - 1900. */
+ int tm_wday; /*!< Day of week. [0-6] */
+ int tm_yday; /*!< Days in year.[0-365] */
+ int tm_isdst; /*!< DST. [-1/0/1]*/
+ long int tm_gmtoff; /*!< Seconds east of UTC. */
+ char *tm_zone; /*!< Timezone abbreviation. */
+ /* NOTE: do NOT reorder this final item. The order needs to remain compatible with struct tm */
+ int tm_usec; /*!< microseconds */
+};
+
+struct ast_tm *ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone);
+void ast_get_dst_info(const time_t * const timep, int *dst_enabled, time_t *dst_start, time_t *dst_end, int *gmt_off, const char * const zone);
+struct timeval ast_mktime(struct ast_tm * const tmp, const char *zone);
+int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm);
+
+#endif /* _ASTERISK_LOCALTIME_H */
diff --git a/trunk/include/asterisk/lock.h b/trunk/include/asterisk/lock.h
new file mode 100644
index 000000000..f9a6a5831
--- /dev/null
+++ b/trunk/include/asterisk/lock.h
@@ -0,0 +1,1205 @@
+/*
+ * 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 Asterisk locking-related definitions:
+ * - ast_mutext_t, ast_rwlock_t and related functions;
+ * - atomic arithmetic instructions;
+ * - wrappers for channel locking.
+ *
+ * - See \ref LockDef
+ */
+
+/*! \page LockDef Asterisk thread locking models
+ *
+ * This file provides different implementation of the functions,
+ * depending on the platform, the use of DEBUG_THREADS, and the way
+ * module-level mutexes are initialized.
+ *
+ * - \b static: the mutex is assigned the value AST_MUTEX_INIT_VALUE
+ * this is done at compile time, and is the way used on Linux.
+ * This method is not applicable to all platforms e.g. when the
+ * initialization needs that some code is run.
+ *
+ * - \b through constructors: for each mutex, a constructor function is
+ * defined, which then runs when the program (or the module)
+ * starts. The problem with this approach is that there is a
+ * lot of code duplication (a new block of code is created for
+ * each mutex). Also, it does not prevent a user from declaring
+ * a global mutex without going through the wrapper macros,
+ * so sane programming practices are still required.
+ */
+
+#ifndef _ASTERISK_LOCK_H
+#define _ASTERISK_LOCK_H
+
+#include <pthread.h>
+#include <sys/param.h>
+
+#include "asterisk/logger.h"
+
+/* internal macro to profile mutexes. Only computes the delay on
+ * non-blocking calls.
+ */
+#ifndef HAVE_MTX_PROFILE
+#define __MTX_PROF(a) return pthread_mutex_lock((a))
+#else
+#define __MTX_PROF(a) do { \
+ int i; \
+ /* profile only non-blocking events */ \
+ ast_mark(mtx_prof, 1); \
+ i = pthread_mutex_trylock((a)); \
+ ast_mark(mtx_prof, 0); \
+ if (!i) \
+ return i; \
+ else \
+ return pthread_mutex_lock((a)); \
+ } while (0)
+#endif /* HAVE_MTX_PROFILE */
+
+#define AST_PTHREADT_NULL (pthread_t) -1
+#define AST_PTHREADT_STOP (pthread_t) -2
+
+#if defined(SOLARIS) || defined(BSD)
+#define AST_MUTEX_INIT_W_CONSTRUCTORS
+#endif /* SOLARIS || BSD */
+
+/* Asterisk REQUIRES recursive (not error checking) mutexes
+ and will not run without them. */
+#if defined(HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) && defined(HAVE_PTHREAD_MUTEX_RECURSIVE_NP)
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE_NP
+#else
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_MUTEX_INITIALIZER
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE
+#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */
+
+/*
+ * Definition of ast_mutex_t, ast_cont_d and related functions with/without debugging
+ * (search for DEBUG_THREADS to find the start/end of the sections).
+ *
+ * The non-debug code contains just wrappers for the corresponding pthread functions.
+ * The debug code tracks usage and tries to identify deadlock situations.
+ */
+#ifdef DEBUG_THREADS
+
+#define __ast_mutex_logger(...) do { if (canlog) ast_log(LOG_ERROR, __VA_ARGS__); else fprintf(stderr, __VA_ARGS__); } while (0)
+
+#ifdef THREAD_CRASH
+#define DO_THREAD_CRASH do { *((int *)(0)) = 1; } while(0)
+#else
+#define DO_THREAD_CRASH do { } while (0)
+#endif
+
+#include <errno.h>
+
+#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, 1, { NULL }, { 0 }, 0, { NULL }, { 0 }, PTHREAD_MUTEX_INIT_VALUE }
+#define AST_MUTEX_INIT_VALUE_NOTRACKING \
+ { PTHREAD_MUTEX_INIT_VALUE, 0, { NULL }, { 0 }, 0, { NULL }, { 0 }, PTHREAD_MUTEX_INIT_VALUE }
+
+#define AST_MAX_REENTRANCY 10
+
+struct ast_channel;
+
+struct ast_mutex_info {
+ pthread_mutex_t mutex;
+ /*! Track which thread holds this lock */
+ unsigned int track:1;
+ const char *file[AST_MAX_REENTRANCY];
+ int lineno[AST_MAX_REENTRANCY];
+ int reentrancy;
+ const char *func[AST_MAX_REENTRANCY];
+ pthread_t thread[AST_MAX_REENTRANCY];
+ pthread_mutex_t reentr_mutex;
+};
+
+typedef struct ast_mutex_info ast_mutex_t;
+
+typedef pthread_cond_t ast_cond_t;
+
+static pthread_mutex_t empty_mutex;
+
+enum ast_lock_type {
+ AST_MUTEX,
+ AST_RDLOCK,
+ AST_WRLOCK,
+};
+
+/*!
+ * \brief Store lock info for the current thread
+ *
+ * This function gets called in ast_mutex_lock() and ast_mutex_trylock() so
+ * that information about this lock can be stored in this thread's
+ * lock info struct. The lock is marked as pending as the thread is waiting
+ * on the lock. ast_mark_lock_acquired() will mark it as held by this thread.
+ */
+void ast_store_lock_info(enum ast_lock_type type, const char *filename,
+ int line_num, const char *func, const char *lock_name, void *lock_addr);
+
+/*!
+ * \brief Mark the last lock as acquired
+ */
+void ast_mark_lock_acquired(void);
+
+/*!
+ * \brief Mark the last lock as failed (trylock)
+ */
+void ast_mark_lock_failed(void);
+
+/*!
+ * \brief remove lock info for the current thread
+ *
+ * this gets called by ast_mutex_unlock so that information on the lock can
+ * be removed from the current thread's lock info struct.
+ */
+void ast_remove_lock_info(void *lock_addr);
+
+static void __attribute__((constructor)) init_empty_mutex(void)
+{
+ memset(&empty_mutex, 0, sizeof(empty_mutex));
+}
+
+static inline void ast_reentrancy_lock(ast_mutex_t *p_ast_mutex)
+{
+ pthread_mutex_lock(&p_ast_mutex->reentr_mutex);
+}
+
+static inline void ast_reentrancy_unlock(ast_mutex_t *p_ast_mutex)
+{
+ pthread_mutex_unlock(&p_ast_mutex->reentr_mutex);
+}
+
+static inline void ast_reentrancy_init(ast_mutex_t *p_ast_mutex)
+{
+ int i;
+ pthread_mutexattr_t reentr_attr;
+
+ for (i = 0; i < AST_MAX_REENTRANCY; i++) {
+ p_ast_mutex->file[i] = NULL;
+ p_ast_mutex->lineno[i] = 0;
+ p_ast_mutex->func[i] = NULL;
+ p_ast_mutex->thread[i] = 0;
+ }
+
+ p_ast_mutex->reentrancy = 0;
+
+ pthread_mutexattr_init(&reentr_attr);
+ pthread_mutexattr_settype(&reentr_attr, AST_MUTEX_KIND);
+ pthread_mutex_init(&p_ast_mutex->reentr_mutex, &reentr_attr);
+ pthread_mutexattr_destroy(&reentr_attr);
+}
+
+static inline void delete_reentrancy_cs(ast_mutex_t * p_ast_mutex)
+{
+ pthread_mutex_destroy(&p_ast_mutex->reentr_mutex);
+}
+
+static inline int __ast_pthread_mutex_init(int track, const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ pthread_mutexattr_t attr;
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+
+ if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+/*
+ int canlog = strcmp(filename, "logger.c") & track;
+ __ast_mutex_logger("%s line %d (%s): NOTICE: mutex '%s' is already initialized.\n",
+ filename, lineno, func, mutex_name);
+ DO_THREAD_CRASH;
+*/
+ return 0;
+ }
+
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_reentrancy_init(t);
+ t->track = track;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+
+ res = pthread_mutex_init(&t->mutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ return res;
+}
+
+#define ast_mutex_init(pmutex) __ast_pthread_mutex_init(1, __FILE__, __LINE__, __PRETTY_FUNCTION__, #pmutex, pmutex)
+#define ast_mutex_init_notracking(pmutex) \
+ __ast_pthread_mutex_init(0, __FILE__, __LINE__, __PRETTY_FUNCTION__, #pmutex, pmutex)
+
+static inline int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c") & t->track;
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ /* Don't try to uninitialize non initialized mutex
+ * This may no effect on linux
+ * And always ganerate core on *BSD with
+ * linked libpthread
+ * This not error condition if the mutex created on the fly.
+ */
+ __ast_mutex_logger("%s line %d (%s): NOTICE: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ return 0;
+ }
+#endif
+
+ res = pthread_mutex_trylock(&t->mutex);
+ switch (res) {
+ case 0:
+ pthread_mutex_unlock(&t->mutex);
+ break;
+ case EINVAL:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy invalid mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ break;
+ case EBUSY:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ ast_reentrancy_lock(t);
+ __ast_mutex_logger("%s line %d (%s): Error: '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ ast_reentrancy_unlock(t);
+ break;
+ }
+
+ if ((res = pthread_mutex_destroy(&t->mutex)))
+ __ast_mutex_logger("%s line %d (%s): Error destroying mutex %s: %s\n",
+ filename, lineno, func, mutex_name, strerror(res));
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+ else
+ t->mutex = PTHREAD_MUTEX_INIT_VALUE;
+#endif
+ ast_reentrancy_lock(t);
+ t->file[0] = filename;
+ t->lineno[0] = lineno;
+ t->func[0] = func;
+ t->reentrancy = 0;
+ t->thread[0] = 0;
+ ast_reentrancy_unlock(t);
+ delete_reentrancy_cs(t);
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c") & t->track;
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ /* Don't warn abount uninitialized mutex.
+ * Simple try to initialize it.
+ * May be not needed in linux system.
+ */
+ res = __ast_pthread_mutex_init(t->track, filename, lineno, func, mutex_name, t);
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
+ filename, lineno, func, mutex_name);
+ return res;
+ }
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ if (t->track)
+ ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, &t->mutex);
+
+#ifdef DETECT_DEADLOCKS
+ {
+ time_t seconds = time(NULL);
+ time_t wait_time, reported_wait = 0;
+ do {
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+#endif
+ res = pthread_mutex_trylock(&t->mutex);
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 0);
+#endif
+ if (res == EBUSY) {
+ wait_time = time(NULL) - seconds;
+ if (wait_time > reported_wait && (wait_time % 5) == 0) {
+ __ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for mutex '%s'?\n",
+ filename, lineno, func, (int) wait_time, mutex_name);
+ ast_reentrancy_lock(t);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1],
+ t->func[t->reentrancy-1], mutex_name);
+ ast_reentrancy_unlock(t);
+ reported_wait = wait_time;
+ }
+ usleep(200);
+ }
+ } while (res == EBUSY);
+ }
+#else
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+ res = pthread_mutex_trylock(&t->mutex);
+ ast_mark(mtx_prof, 0);
+ if (res)
+#endif
+ res = pthread_mutex_lock(&t->mutex);
+#endif /* DETECT_DEADLOCKS */
+
+ if (!res) {
+ ast_reentrancy_lock(t);
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ ast_reentrancy_unlock(t);
+ if (t->track)
+ ast_mark_lock_acquired();
+ } else {
+ if (t->track)
+ ast_remove_lock_info(&t->mutex);
+ __ast_mutex_logger("%s line %d (%s): Error obtaining mutex: %s\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ }
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c") & t->track;
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ /* Don't warn abount uninitialized mutex.
+ * Simple try to initialize it.
+ * May be not needed in linux system.
+ */
+ res = __ast_pthread_mutex_init(t->track, filename, lineno, func, mutex_name, t);
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
+ filename, lineno, func, mutex_name);
+ return res;
+ }
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ if (t->track)
+ ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, &t->mutex);
+
+ if (!(res = pthread_mutex_trylock(&t->mutex))) {
+ ast_reentrancy_lock(t);
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ ast_reentrancy_unlock(t);
+ if (t->track)
+ ast_mark_lock_acquired();
+ } else if (t->track) {
+ ast_mark_lock_failed();
+ }
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c") & t->track;
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ res = __ast_pthread_mutex_init(t->track, filename, lineno, func, mutex_name, t);
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
+ filename, lineno, func, mutex_name);
+ }
+ return res;
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_reentrancy_lock(t);
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+ ast_reentrancy_unlock(t);
+
+ if (t->track)
+ ast_remove_lock_info(&t->mutex);
+
+ if ((res = pthread_mutex_unlock(&t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ }
+
+ return res;
+}
+
+static inline int __ast_cond_init(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr)
+{
+ return pthread_cond_init(cond, cond_attr);
+}
+
+static inline int __ast_cond_signal(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_signal(cond);
+}
+
+static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_broadcast(cond);
+}
+
+static inline int __ast_cond_destroy(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_destroy(cond);
+}
+
+static inline int __ast_cond_wait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name,
+ ast_cond_t *cond, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c") & t->track;
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ res = __ast_pthread_mutex_init(t->track, filename, lineno, func, mutex_name, t);
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
+ filename, lineno, func, mutex_name);
+ }
+ return res;
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_reentrancy_lock(t);
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+ ast_reentrancy_unlock(t);
+
+ if (t->track)
+ ast_remove_lock_info(&t->mutex);
+
+ if ((res = pthread_cond_wait(cond, &t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ ast_reentrancy_lock(t);
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ ast_reentrancy_unlock(t);
+
+ if (t->track)
+ ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, &t->mutex);
+ }
+
+ return res;
+}
+
+static inline int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name, ast_cond_t *cond,
+ ast_mutex_t *t, const struct timespec *abstime)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c") & t->track;
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ res = __ast_pthread_mutex_init(t->track, filename, lineno, func, mutex_name, t);
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
+ filename, lineno, func, mutex_name);
+ }
+ return res;
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_reentrancy_lock(t);
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+ ast_reentrancy_unlock(t);
+
+ if (t->track)
+ ast_remove_lock_info(&t->mutex);
+
+ if ((res = pthread_cond_timedwait(cond, &t->mutex, abstime)) && (res != ETIMEDOUT)) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ ast_reentrancy_lock(t);
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ ast_reentrancy_unlock(t);
+
+ if (t->track)
+ ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, &t->mutex);
+ }
+
+ return res;
+}
+
+#define ast_mutex_destroy(a) __ast_pthread_mutex_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_trylock(a) __ast_pthread_mutex_trylock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_cond_init(cond, attr) __ast_cond_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond, attr)
+#define ast_cond_destroy(cond) __ast_cond_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_signal(cond) __ast_cond_signal(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_broadcast(cond) __ast_cond_broadcast(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_wait(cond, mutex) __ast_cond_wait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex)
+#define ast_cond_timedwait(cond, mutex, time) __ast_cond_timedwait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex, time)
+
+#else /* !DEBUG_THREADS */
+
+
+typedef pthread_mutex_t ast_mutex_t;
+
+#define AST_MUTEX_INIT_VALUE ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE)
+#define AST_MUTEX_INIT_VALUE_NOTRACKING ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE)
+
+#define ast_mutex_init_notracking(m) ast_mutex_init(m)
+
+static inline int ast_mutex_init(ast_mutex_t *pmutex)
+{
+ int res;
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+
+ res = pthread_mutex_init(pmutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ return res;
+}
+
+#define ast_pthread_mutex_init(pmutex,a) pthread_mutex_init(pmutex,a)
+
+static inline int ast_mutex_unlock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_unlock(pmutex);
+}
+
+static inline int ast_mutex_destroy(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_destroy(pmutex);
+}
+
+static inline int ast_mutex_lock(ast_mutex_t *pmutex)
+{
+ __MTX_PROF(pmutex);
+}
+
+static inline int ast_mutex_trylock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_trylock(pmutex);
+}
+
+typedef pthread_cond_t ast_cond_t;
+
+static inline int ast_cond_init(ast_cond_t *cond, pthread_condattr_t *cond_attr)
+{
+ return pthread_cond_init(cond, cond_attr);
+}
+
+static inline int ast_cond_signal(ast_cond_t *cond)
+{
+ return pthread_cond_signal(cond);
+}
+
+static inline int ast_cond_broadcast(ast_cond_t *cond)
+{
+ return pthread_cond_broadcast(cond);
+}
+
+static inline int ast_cond_destroy(ast_cond_t *cond)
+{
+ return pthread_cond_destroy(cond);
+}
+
+static inline int ast_cond_wait(ast_cond_t *cond, ast_mutex_t *t)
+{
+ return pthread_cond_wait(cond, t);
+}
+
+static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const struct timespec *abstime)
+{
+ return pthread_cond_timedwait(cond, t, abstime);
+}
+
+#endif /* !DEBUG_THREADS */
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+/*
+ * If AST_MUTEX_INIT_W_CONSTRUCTORS is defined, use file scope constructors
+ * and destructors to create/destroy global mutexes.
+ */
+#define __AST_MUTEX_DEFINE(scope, mutex, init_val, track) \
+ scope ast_mutex_t mutex = init_val; \
+static void __attribute__ ((constructor)) init_##mutex(void) \
+{ \
+ if (track) \
+ ast_mutex_init(&mutex); \
+ else \
+ ast_mutex_init_notracking(&mutex); \
+} \
+ \
+static void __attribute__ ((destructor)) fini_##mutex(void) \
+{ \
+ ast_mutex_destroy(&mutex); \
+}
+#else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */
+/* By default, use static initialization of mutexes. */
+#define __AST_MUTEX_DEFINE(scope, mutex, init_val, track) scope ast_mutex_t mutex = init_val
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+#ifndef __CYGWIN__ /* temporary disabled for cygwin */
+#define pthread_mutex_t use_ast_mutex_t_instead_of_pthread_mutex_t
+#define pthread_cond_t use_ast_cond_t_instead_of_pthread_cond_t
+#endif
+#define pthread_mutex_lock use_ast_mutex_lock_instead_of_pthread_mutex_lock
+#define pthread_mutex_unlock use_ast_mutex_unlock_instead_of_pthread_mutex_unlock
+#define pthread_mutex_trylock use_ast_mutex_trylock_instead_of_pthread_mutex_trylock
+#define pthread_mutex_init use_ast_mutex_init_instead_of_pthread_mutex_init
+#define pthread_mutex_destroy use_ast_mutex_destroy_instead_of_pthread_mutex_destroy
+#define pthread_cond_init use_ast_cond_init_instead_of_pthread_cond_init
+#define pthread_cond_destroy use_ast_cond_destroy_instead_of_pthread_cond_destroy
+#define pthread_cond_signal use_ast_cond_signal_instead_of_pthread_cond_signal
+#define pthread_cond_broadcast use_ast_cond_broadcast_instead_of_pthread_cond_broadcast
+#define pthread_cond_wait use_ast_cond_wait_instead_of_pthread_cond_wait
+#define pthread_cond_timedwait use_ast_cond_timedwait_instead_of_pthread_cond_timedwait
+
+#define AST_MUTEX_DEFINE_STATIC(mutex) __AST_MUTEX_DEFINE(static, mutex, AST_MUTEX_INIT_VALUE, 1)
+#define AST_MUTEX_DEFINE_STATIC_NOTRACKING(mutex) __AST_MUTEX_DEFINE(static, mutex, AST_MUTEX_INIT_VALUE_NOTRACKING, 0)
+
+#define AST_MUTEX_INITIALIZER __use_AST_MUTEX_DEFINE_STATIC_rather_than_AST_MUTEX_INITIALIZER__
+
+#define gethostbyname __gethostbyname__is__not__reentrant__use__ast_gethostbyname__instead__
+
+#ifndef __linux__
+#define pthread_create __use_ast_pthread_create_instead__
+#endif
+
+/*
+ * Same as above, definitions of ast_rwlock_t for the various cases:
+ * simple wrappers for the pthread equivalent in the non-debug case,
+ * more sophisticated tracking in the debug case.
+ */
+
+typedef pthread_rwlock_t ast_rwlock_t;
+
+#ifdef HAVE_PTHREAD_RWLOCK_INITIALIZER
+#define AST_RWLOCK_INIT_VALUE PTHREAD_RWLOCK_INITIALIZER
+#else
+#define AST_RWLOCK_INIT_VALUE { 0 }
+#endif
+
+#ifdef DEBUG_THREADS
+
+#define ast_rwlock_init(rwlock) __ast_rwlock_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #rwlock, rwlock)
+#define ast_rwlock_destroy(rwlock) __ast_rwlock_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #rwlock, rwlock)
+#define ast_rwlock_unlock(a) _ast_rwlock_unlock(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_rwlock_rdlock(a) _ast_rwlock_rdlock(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_rwlock_wrlock(a) _ast_rwlock_wrlock(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_rwlock_tryrdlock(a) _ast_rwlock_tryrdlock(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_rwlock_trywrlock(a) _ast_rwlock_trywrlock(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+
+static inline int __ast_rwlock_init(const char *filename, int lineno, const char *func, const char *rwlock_name, ast_rwlock_t *prwlock)
+{
+ int res;
+ pthread_rwlockattr_t attr;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(filename, "logger.c");
+
+ if (*prwlock != ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Warning: rwlock '%s' is already initialized.\n",
+ filename, lineno, func, rwlock_name);
+ return 0;
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+ pthread_rwlockattr_init(&attr);
+
+#ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
+ pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
+#endif
+
+ res = pthread_rwlock_init(prwlock, &attr);
+ pthread_rwlockattr_destroy(&attr);
+ return res;
+}
+
+
+static inline int __ast_rwlock_destroy(const char *filename, int lineno, const char *func, const char *rwlock_name, ast_rwlock_t *prwlock)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if (*prwlock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Warning: rwlock '%s' is uninitialized.\n",
+ filename, lineno, func, rwlock_name);
+ return 0;
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ if ((res = pthread_rwlock_destroy(prwlock)))
+ __ast_mutex_logger("%s line %d (%s): Error destroying rwlock %s: %s\n",
+ filename, lineno, func, rwlock_name, strerror(res));
+
+ return res;
+}
+
+
+static inline int _ast_rwlock_unlock(ast_rwlock_t *lock, const char *name,
+ const char *file, int line, const char *func)
+{
+ int res;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(file, "logger.c");
+
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Warning: rwlock '%s' is uninitialized.\n",
+ file, line, func, name);
+ res = __ast_rwlock_init(file, line, func, name, lock);
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
+ file, line, func, name);
+ }
+ return res;
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ res = pthread_rwlock_unlock(lock);
+ ast_remove_lock_info(lock);
+ return res;
+}
+
+
+static inline int _ast_rwlock_rdlock(ast_rwlock_t *lock, const char *name,
+ const char *file, int line, const char *func)
+{
+ int res;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(file, "logger.c");
+
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ /* Don't warn abount uninitialized lock.
+ * Simple try to initialize it.
+ * May be not needed in linux system.
+ */
+ res = __ast_rwlock_init(file, line, func, name, lock);
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
+ file, line, func, name);
+ return res;
+ }
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_store_lock_info(AST_RDLOCK, file, line, func, name, lock);
+ res = pthread_rwlock_rdlock(lock);
+ if (!res)
+ ast_mark_lock_acquired();
+ else
+ ast_remove_lock_info(lock);
+ return res;
+}
+
+
+static inline int _ast_rwlock_wrlock(ast_rwlock_t *lock, const char *name,
+ const char *file, int line, const char *func)
+{
+ int res;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(file, "logger.c");
+
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ /* Don't warn abount uninitialized lock.
+ * Simple try to initialize it.
+ * May be not needed in linux system.
+ */
+ res = __ast_rwlock_init(file, line, func, name, lock);
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
+ file, line, func, name);
+ return res;
+ }
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_store_lock_info(AST_WRLOCK, file, line, func, name, lock);
+ res = pthread_rwlock_wrlock(lock);
+ if (!res)
+ ast_mark_lock_acquired();
+ else
+ ast_remove_lock_info(lock);
+ return res;
+}
+
+
+static inline int _ast_rwlock_tryrdlock(ast_rwlock_t *lock, const char *name,
+ const char *file, int line, const char *func)
+{
+ int res;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(file, "logger.c");
+
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ /* Don't warn abount uninitialized lock.
+ * Simple try to initialize it.
+ * May be not needed in linux system.
+ */
+ res = __ast_rwlock_init(file, line, func, name, lock);
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
+ file, line, func, name);
+ return res;
+ }
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_store_lock_info(AST_RDLOCK, file, line, func, name, lock);
+ res = pthread_rwlock_tryrdlock(lock);
+ if (!res)
+ ast_mark_lock_acquired();
+ else
+ ast_remove_lock_info(lock);
+ return res;
+}
+
+
+static inline int _ast_rwlock_trywrlock(ast_rwlock_t *lock, const char *name,
+ const char *file, int line, const char *func)
+{
+ int res;
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(file, "logger.c");
+
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ /* Don't warn abount uninitialized lock.
+ * Simple try to initialize it.
+ * May be not needed in linux system.
+ */
+ res = __ast_rwlock_init(file, line, func, name, lock);
+ if (*lock == ((ast_rwlock_t) AST_RWLOCK_INIT_VALUE)) {
+ __ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
+ file, line, func, name);
+ return res;
+ }
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ ast_store_lock_info(AST_WRLOCK, file, line, func, name, lock);
+ res = pthread_rwlock_trywrlock(lock);
+ if (!res)
+ ast_mark_lock_acquired();
+ else
+ ast_remove_lock_info(lock);
+ return res;
+}
+
+#else /* !DEBUG_THREADS */
+
+static inline int ast_rwlock_init(ast_rwlock_t *prwlock)
+{
+ int res;
+ pthread_rwlockattr_t attr;
+
+ pthread_rwlockattr_init(&attr);
+
+#ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
+ pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
+#endif
+
+ res = pthread_rwlock_init(prwlock, &attr);
+ pthread_rwlockattr_destroy(&attr);
+ return res;
+}
+
+static inline int ast_rwlock_destroy(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_destroy(prwlock);
+}
+
+static inline int ast_rwlock_unlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_unlock(prwlock);
+}
+
+static inline int ast_rwlock_rdlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_rdlock(prwlock);
+}
+
+static inline int ast_rwlock_tryrdlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_tryrdlock(prwlock);
+}
+
+static inline int ast_rwlock_wrlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_wrlock(prwlock);
+}
+
+static inline int ast_rwlock_trywrlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_trywrlock(prwlock);
+}
+#endif /* !DEBUG_THREADS */
+
+/* Statically declared read/write locks */
+
+#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
+#define __AST_RWLOCK_DEFINE(scope, rwlock) \
+ scope ast_rwlock_t rwlock; \
+static void __attribute__ ((constructor)) init_##rwlock(void) \
+{ \
+ ast_rwlock_init(&rwlock); \
+} \
+ \
+static void __attribute__ ((destructor)) fini_##rwlock(void) \
+{ \
+ ast_rwlock_destroy(&rwlock); \
+}
+#else
+#define __AST_RWLOCK_DEFINE(scope, rwlock) scope ast_rwlock_t rwlock = AST_RWLOCK_INIT_VALUE
+#endif
+
+#define AST_RWLOCK_DEFINE_STATIC(rwlock) __AST_RWLOCK_DEFINE(static, rwlock)
+
+/*
+ * Support for atomic instructions.
+ * For platforms that have it, use the native cpu instruction to
+ * implement them. For other platforms, resort to a 'slow' version
+ * (defined in utils.c) that protects the atomic instruction with
+ * a single lock.
+ * The slow versions is always available, for testing purposes,
+ * as ast_atomic_fetchadd_int_slow()
+ */
+
+int ast_atomic_fetchadd_int_slow(volatile int *p, int v);
+
+#include "asterisk/inline_api.h"
+
+#if defined(HAVE_OSX_ATOMICS)
+#include "libkern/OSAtomic.h"
+#endif
+
+/*! \brief Atomically add v to *p and return * the previous value of *p.
+ * This can be used to handle reference counts, and the return value
+ * can be used to generate unique identifiers.
+ */
+
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return __sync_fetch_and_add(p, v);
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd32(v, (int32_t *) p) - v;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd64(v, (int64_t *) p) - v;
+#elif defined (__i386__) || defined(__x86_64__)
+#ifdef sun
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ __asm __volatile (
+ " lock; xaddl %0, %1 ; "
+ : "+r" (v), /* 0 (result) */
+ "=m" (*p) /* 1 */
+ : "m" (*p)); /* 2 */
+ return (v);
+})
+#else /* ifndef sun */
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ __asm __volatile (
+ " lock xaddl %0, %1 ; "
+ : "+r" (v), /* 0 (result) */
+ "=m" (*p) /* 1 */
+ : "m" (*p)); /* 2 */
+ return (v);
+})
+#endif
+#else /* low performance version in utils.c */
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return ast_atomic_fetchadd_int_slow(p, v);
+})
+#endif
+
+/*! \brief decrement *p by 1 and return true if the variable has reached 0.
+ * Useful e.g. to check if a refcount has reached 0.
+ */
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return __sync_sub_and_fetch(p, 1) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd32( -1, (int32_t *) p) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd64( -1, (int64_t *) p) == 0;
+#else
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ int a = ast_atomic_fetchadd_int(p, -1);
+ return a == 1; /* true if the value is 0 now (so it was 1 previously) */
+})
+#endif
+
+#ifndef DEBUG_CHANNEL_LOCKS
+/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_lock(x) ast_mutex_lock(&x->lock_dont_use)
+/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_unlock(x) ast_mutex_unlock(&x->lock_dont_use)
+/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_trylock(x) ast_mutex_trylock(&x->lock_dont_use)
+#else
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_lock(struct ast_channel *chan);
+
+/*! \brief Unlock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function
+*/
+int ast_channel_unlock(struct ast_channel *chan);
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_trylock(struct ast_channel *chan);
+#endif
+
+#endif /* _ASTERISK_LOCK_H */
diff --git a/trunk/include/asterisk/logger.h b/trunk/include/asterisk/logger.h
new file mode 100644
index 000000000..d76aa932f
--- /dev/null
+++ b/trunk/include/asterisk/logger.h
@@ -0,0 +1,178 @@
+/*
+ * 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 logger.h
+ \brief Support for logging to various files, console and syslog
+ Configuration in file logger.conf
+*/
+
+#ifndef _ASTERISK_LOGGER_H
+#define _ASTERISK_LOGGER_H
+
+#include "asterisk/options.h" /* need option_debug */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define EVENTLOG "event_log"
+#define QUEUELOG "queue_log"
+
+#define DEBUG_M(a) { \
+ a; \
+}
+
+#define VERBOSE_PREFIX_1 " "
+#define VERBOSE_PREFIX_2 " == "
+#define VERBOSE_PREFIX_3 " -- "
+#define VERBOSE_PREFIX_4 " > "
+
+/*! \brief Used for sending a log message
+ This is the standard logger function. Probably the only way you will invoke it would be something like this:
+ ast_log(LOG_WHATEVER, "Problem with the %s Captain. We should get some more. Will %d be enough?\n", "flux capacitor", 10);
+ where WHATEVER is one of ERROR, DEBUG, EVENT, NOTICE, or WARNING depending
+ on which log you wish to output to. These are implemented as macros, that
+ will provide the function with the needed arguments.
+
+ \param level Type of log event
+ \param file Will be provided by the LOG_* macro
+ \param line Will be provided by the LOG_* macro
+ \param function Will be provided by the LOG_* macro
+ \param fmt This is what is important. The format is the same as your favorite breed of printf. You know how that works, right? :-)
+ */
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+ __attribute__ ((format (printf, 5, 6)));
+
+void ast_backtrace(void);
+
+/*! \brief Reload logger without rotating log files */
+int logger_reload(void);
+
+void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
+ __attribute__ ((format (printf, 5, 6)));
+
+/*! Send a verbose message (based on verbose level)
+ \brief This works like ast_log, but prints verbose messages to the console depending on verbosity level set.
+ ast_verbose(VERBOSE_PREFIX_3 "Whatever %s is happening\n", "nothing");
+ This will print the message to the console if the verbose level is set to a level >= 3
+ Note the abscence of a comma after the VERBOSE_PREFIX_3. This is important.
+ VERBOSE_PREFIX_1 through VERBOSE_PREFIX_3 are defined.
+ */
+void ast_verbose(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+int ast_register_verbose(void (*verboser)(const char *string));
+int ast_unregister_verbose(void (*verboser)(const char *string));
+
+void ast_console_puts(const char *string);
+
+void ast_console_puts_mutable(const char *string);
+void ast_console_toggle_mute(int fd, int silent);
+
+#define _A_ __FILE__, __LINE__, __PRETTY_FUNCTION__
+
+#ifdef LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+#define __LOG_DEBUG 0
+#define LOG_DEBUG __LOG_DEBUG, _A_
+
+#ifdef LOG_EVENT
+#undef LOG_EVENT
+#endif
+#define __LOG_EVENT 1
+#define LOG_EVENT __LOG_EVENT, _A_
+
+#ifdef LOG_NOTICE
+#undef LOG_NOTICE
+#endif
+#define __LOG_NOTICE 2
+#define LOG_NOTICE __LOG_NOTICE, _A_
+
+#ifdef LOG_WARNING
+#undef LOG_WARNING
+#endif
+#define __LOG_WARNING 3
+#define LOG_WARNING __LOG_WARNING, _A_
+
+#ifdef LOG_ERROR
+#undef LOG_ERROR
+#endif
+#define __LOG_ERROR 4
+#define LOG_ERROR __LOG_ERROR, _A_
+
+#ifdef LOG_VERBOSE
+#undef LOG_VERBOSE
+#endif
+#define __LOG_VERBOSE 5
+#define LOG_VERBOSE __LOG_VERBOSE, _A_
+
+#ifdef LOG_DTMF
+#undef LOG_DTMF
+#endif
+#define __LOG_DTMF 6
+#define LOG_DTMF __LOG_DTMF, _A_
+
+/*!
+ * \brief Get the debug level for a file
+ * \arg file the filename
+ * \return the debug level
+ */
+unsigned int ast_debug_get_by_file(const char *file);
+
+/*!
+ * \brief Get the debug level for a file
+ * \arg file the filename
+ * \return the debug level
+ */
+unsigned int ast_verbose_get_by_file(const char *file);
+
+/*!
+ * \brief Log a DEBUG message
+ * \param level The minimum value of option_debug for this message
+ * to get logged
+ */
+#define ast_debug(level, ...) do { \
+ if (option_debug >= (level) || (ast_opt_dbg_file && ast_debug_get_by_file(__FILE__) >= (level)) ) \
+ ast_log(LOG_DEBUG, __VA_ARGS__); \
+} while (0)
+
+#define VERBOSITY_ATLEAST(level) (option_verbose >= (level) || (ast_opt_verb_file && ast_verbose_get_by_file(__FILE__) >= (level)))
+
+#define ast_verb(level, ...) do { \
+ if (VERBOSITY_ATLEAST((level)) ) { \
+ if (level >= 4) \
+ ast_verbose(VERBOSE_PREFIX_4 __VA_ARGS__); \
+ else if (level == 3) \
+ ast_verbose(VERBOSE_PREFIX_3 __VA_ARGS__); \
+ else if (level == 2) \
+ ast_verbose(VERBOSE_PREFIX_2 __VA_ARGS__); \
+ else if (level == 1) \
+ ast_verbose(VERBOSE_PREFIX_1 __VA_ARGS__); \
+ else \
+ ast_verbose(__VA_ARGS__); \
+ } \
+} while (0)
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_LOGGER_H */
diff --git a/trunk/include/asterisk/manager.h b/trunk/include/asterisk/manager.h
new file mode 100644
index 000000000..327f674f8
--- /dev/null
+++ b/trunk/include/asterisk/manager.h
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+#ifndef _ASTERISK_MANAGER_H
+#define _ASTERISK_MANAGER_H
+
+#include "asterisk/network.h"
+#include "asterisk/lock.h"
+
+/*!
+ \file
+ \brief The AMI - Asterisk Manager Interface - is a TCP protocol created to
+ manage Asterisk with third-party software.
+
+ Manager protocol packages are text fields of the form a: b. There is
+ always exactly one space after the colon.
+
+\verbatim
+
+ For Actions replies, the first line of the reply is a "Response:" header with
+ values "success", "error" or "follows". "Follows" implies that the
+ response is coming as separate events with the same ActionID. If the
+ Action request has no ActionID, it will be hard matching events
+ to the Action request in the manager client.
+
+ The first header type is the "Event" header. Other headers vary from
+ event to event. Headers end with standard \\r\\n termination.
+ The last line of the manager response or event is an empty line.
+ (\\r\\n)
+
+\endverbatim
+
+ \note Please try to \b re-use \b existing \b headers to simplify manager message parsing in clients.
+ Don't re-use an existing header with a new meaning, please.
+ You can find a reference of standard headers in doc/manager.txt
+
+- \ref manager.c Main manager code file
+ */
+
+#define AMI_VERSION "1.1"
+#define DEFAULT_MANAGER_PORT 5038 /* Default port for Asterisk management via TCP */
+
+/*! \name Manager event classes */
+/*@{ */
+#define EVENT_FLAG_SYSTEM (1 << 0) /* System events such as module load/unload */
+#define EVENT_FLAG_CALL (1 << 1) /* Call event, such as state change, etc */
+#define EVENT_FLAG_LOG (1 << 2) /* Log events */
+#define EVENT_FLAG_VERBOSE (1 << 3) /* Verbose messages */
+#define EVENT_FLAG_COMMAND (1 << 4) /* Ability to read/set commands */
+#define EVENT_FLAG_AGENT (1 << 5) /* Ability to read/set agent info */
+#define EVENT_FLAG_USER (1 << 6) /* Ability to read/set user info */
+#define EVENT_FLAG_CONFIG (1 << 7) /* Ability to modify configurations */
+#define EVENT_FLAG_DTMF (1 << 8) /* Ability to read DTMF events */
+#define EVENT_FLAG_REPORTING (1 << 9) /* Reporting events such as rtcp sent */
+#define EVENT_FLAG_CDR (1 << 10) /* CDR events */
+#define EVENT_FLAG_DIALPLAN (1 << 11) /* Dialplan events (VarSet, NewExten) */
+/*@} */
+
+/*! \brief Export manager structures */
+#define AST_MAX_MANHEADERS 128
+
+/*! \brief Manager Helper Function */
+typedef int (*manager_hook_t)(int, const char *, char *);
+
+
+struct manager_custom_hook {
+ /*! Identifier */
+ char *file;
+ /*! helper function */
+ manager_hook_t helper;
+ /*! Linked list information */
+ AST_RWLIST_ENTRY(manager_custom_hook) list;
+};
+
+/*! \brief Check if AMI is enabled */
+int check_manager_enabled(void);
+
+/*! \brief Check if AMI/HTTP is enabled */
+int check_webmanager_enabled(void);
+
+/*! Add a custom hook to be called when an event is fired
+ \param hook struct manager_custom_hook object to add
+*/
+void ast_manager_register_hook(struct manager_custom_hook *hook);
+
+/*! Delete a custom hook to be called when an event is fired
+ \param hook struct manager_custom_hook object to delete
+*/
+void ast_manager_unregister_hook(struct manager_custom_hook *hook);
+
+struct mansession;
+
+struct message {
+ unsigned int hdrcount;
+ const char *headers[AST_MAX_MANHEADERS];
+};
+
+struct manager_action {
+ /*! Name of the action */
+ const char *action;
+ /*! Short description of the action */
+ const char *synopsis;
+ /*! Detailed description of the action */
+ const char *description;
+ /*! Permission required for action. EVENT_FLAG_* */
+ int authority;
+ /*! Function to be called */
+ int (*func)(struct mansession *s, const struct message *m);
+ /*! For easy linking */
+ AST_RWLIST_ENTRY(manager_action) list;
+};
+
+/*! \brief External routines may register/unregister manager callbacks this way
+ * \note Use ast_manager_register2() to register with help text for new manager commands */
+#define ast_manager_register(a, b, c, d) ast_manager_register2(a, b, c, d, NULL)
+
+
+/*! \brief Register a manager command with the manager interface
+ \param action Name of the requested Action:
+ \param authority Required authority for this command
+ \param func Function to call for this command
+ \param synopsis Help text (one line, up to 30 chars) for CLI manager show commands
+ \param description Help text, several lines
+*/
+int ast_manager_register2(
+ const char *action,
+ int authority,
+ int (*func)(struct mansession *s, const struct message *m),
+ const char *synopsis,
+ const char *description);
+
+/*! \brief Unregister a registred manager command
+ \param action Name of registred Action:
+*/
+int ast_manager_unregister( char *action );
+
+/*!
+ * \brief Verify a session's read permissions against a permission mask.
+ * \param ident session identity
+ * \param perm permission mask to verify
+ * \retval 1 if the session has the permission mask capabilities
+ * \retval 0 otherwise
+ */
+int astman_verify_session_readpermissions(unsigned long ident, int perm);
+
+/*!
+ * \brief Verify a session's write permissions against a permission mask.
+ * \param ident session identity
+ * \param perm permission mask to verify
+ * \retval 1 if the session has the permission mask capabilities, otherwise 0
+ * \retval 0 otherwise
+ */
+int astman_verify_session_writepermissions(unsigned long ident, int perm);
+
+/*! \brief External routines may send asterisk manager events this way
+ * \param category Event category, matches manager authorization
+ \param event Event name
+ \param contents Contents of event
+*/
+
+/* XXX the parser in gcc 2.95 gets confused if you don't put a space
+ * between the last arg before VA_ARGS and the comma */
+#define manager_event(category, event, contents , ...) \
+ __manager_event(category, event, __FILE__, __LINE__, __PRETTY_FUNCTION__, contents , ## __VA_ARGS__)
+
+int __attribute__ ((format(printf, 6, 7))) __manager_event(int category, const char *event,
+ const char *file, int line, const char *func,
+ const char *contents, ...);
+
+/*! \brief Get header from mananger transaction */
+const char *astman_get_header(const struct message *m, char *var);
+
+/*! \brief Get a linked list of the Variable: headers */
+struct ast_variable *astman_get_variables(const struct message *m);
+
+/*! \brief Send error in manager transaction */
+void astman_send_error(struct mansession *s, const struct message *m, char *error);
+
+/*! \brief Send response in manager transaction */
+void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg);
+
+/*! \brief Send ack in manager transaction */
+void astman_send_ack(struct mansession *s, const struct message *m, char *msg);
+
+/*! \brief Send ack in manager list transaction */
+void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag);
+
+void __attribute__ ((format (printf, 2, 3))) astman_append(struct mansession *s, const char *fmt, ...);
+
+/*! \brief Called by Asterisk initialization */
+int init_manager(void);
+
+/*! \brief Called by Asterisk module functions and the CLI command */
+int reload_manager(void);
+
+#endif /* _ASTERISK_MANAGER_H */
diff --git a/trunk/include/asterisk/md5.h b/trunk/include/asterisk/md5.h
new file mode 100644
index 000000000..714267da6
--- /dev/null
+++ b/trunk/include/asterisk/md5.h
@@ -0,0 +1,38 @@
+/*
+ * 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 MD5 digest functions
+ */
+
+#ifndef _ASTERISK_MD5_H
+#define _ASTERISK_MD5_H
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+#endif /* _ASTERISK_MD5_H */
diff --git a/trunk/include/asterisk/mod_format.h b/trunk/include/asterisk/mod_format.h
new file mode 100644
index 000000000..c51bf681c
--- /dev/null
+++ b/trunk/include/asterisk/mod_format.h
@@ -0,0 +1,144 @@
+/*
+ * 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 Header for providers of file and format handling routines.
+ * Clients of these routines should include "asterisk/file.h" instead.
+ */
+
+#ifndef _ASTERISK_MOD_FORMAT_H
+#define _ASTERISK_MOD_FORMAT_H
+
+#include "asterisk/file.h"
+#include "asterisk/frame.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief
+ * Each supported file format is described by the following structure.
+ *
+ * Not all are necessary, the support routine implement default
+ * values for some of them.
+ * A handler typically fills a structure initializing the desired
+ * fields, and then calls ast_format_register() with the (readonly)
+ * structure as an argument.
+ */
+struct ast_format {
+ char name[80]; /*!< Name of format */
+ char exts[80]; /*!< Extensions (separated by | if more than one)
+ this format can read. First is assumed for writing (e.g. .mp3) */
+ int format; /*!< Format of frames it uses/provides (one only) */
+ /*!
+ * \brief Prepare an input stream for playback.
+ * \return 0 on success, -1 on error.
+ * The FILE is already open (in s->f) so this function only needs to perform
+ * any applicable validity checks on the file. If none is required, the
+ * function can be omitted.
+ */
+ int (*open)(struct ast_filestream *s);
+ /*!
+ * \brief Prepare a stream for output, and comment it appropriately if applicable.
+ * \return 0 on success, -1 on error.
+ * Same as the open, the FILE is already open so the function just needs to
+ * prepare any header and other fields, if any.
+ * The function can be omitted if nothing is needed.
+ */
+ int (*rewrite)(struct ast_filestream *s, const char *comment);
+ /*! Write a frame to a channel */
+ int (*write)(struct ast_filestream *, struct ast_frame *);
+ /*! seek num samples into file, whence - like a normal seek but with offset in samples */
+ int (*seek)(struct ast_filestream *, off_t, int);
+ int (*trunc)(struct ast_filestream *fs); /*!< trunc file to current position */
+ off_t (*tell)(struct ast_filestream *fs); /*!< tell current position */
+ /*! Read the next frame from the filestream (if available) and report
+ * when to get next frame (in samples)
+ */
+ struct ast_frame * (*read)(struct ast_filestream *, int *whennext);
+ /*! Do any closing actions, if any. The descriptor and structure are closed
+ * and destroyed by the generic routines, so they must not be done here. */
+ void (*close)(struct ast_filestream *);
+ char * (*getcomment)(struct ast_filestream *); /*!< Retrieve file comment */
+
+ AST_LIST_ENTRY(ast_format) list; /*!< Link */
+
+ /*!
+ * If the handler needs a buffer (for read, typically)
+ * and/or a private descriptor, put here the
+ * required size (in bytes) and the support routine will allocate them
+ * for you, pointed by s->buf and s->private, respectively.
+ * When allocating a buffer, remember to leave AST_FRIENDLY_OFFSET
+ * spare bytes at the bginning.
+ */
+ int buf_size; /*!< size of frame buffer, if any, aligned to 8 bytes. */
+ int desc_size; /*!< size of private descriptor, if any */
+
+ struct ast_module *module;
+};
+
+/*! \brief
+ * This structure is allocated by file.c in one chunk,
+ * together with buf_size and desc_size bytes of memory
+ * to be used for private purposes (e.g. buffers etc.)
+ */
+struct ast_filestream {
+ /*! Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
+ struct ast_format *fmt; /* need to write to the lock and usecnt */
+ int flags;
+ mode_t mode;
+ char *filename;
+ char *realfilename;
+ /*! Video file stream */
+ struct ast_filestream *vfs;
+ /*! Transparently translate from another format -- just once */
+ struct ast_trans_pvt *trans;
+ struct ast_tranlator_pvt *tr;
+ int lastwriteformat;
+ int lasttimeout;
+ struct ast_channel *owner;
+ FILE *f;
+ struct ast_frame fr; /*!< frame produced by read, typically */
+ char *buf; /*!< buffer pointed to by ast_frame; */
+ void *_private; /*!< pointer to private buffer */
+ const char *orig_chan_name;
+};
+
+/*!
+ * \brief Register a new file format capability.
+ * Adds a format to Asterisk's format abilities.
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int __ast_format_register(const struct ast_format *f, struct ast_module *mod);
+#define ast_format_register(f) __ast_format_register(f, ast_module_info->self)
+
+/*!
+ * \brief Unregisters a file format
+ * \param name the name of the format you wish to unregister
+ * Unregisters a format based on the name of the format.
+ * \retval 0 on success
+ * \retval -1 on failure to unregister
+ */
+int ast_format_unregister(const char *name);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_MOD_FORMAT_H */
diff --git a/trunk/include/asterisk/module.h b/trunk/include/asterisk/module.h
new file mode 100644
index 000000000..2344fe825
--- /dev/null
+++ b/trunk/include/asterisk/module.h
@@ -0,0 +1,421 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@digium.com>
+ * Luigi Rizzo <rizzo@icir.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 Asterisk module definitions.
+ *
+ * This file contains the definitons for functions Asterisk modules should
+ * provide and some other module related functions.
+ */
+
+#ifndef _ASTERISK_MODULE_H
+#define _ASTERISK_MODULE_H
+
+#include "asterisk/utils.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief The text the key() function should return. */
+#define ASTERISK_GPL_KEY \
+"This paragraph is copyright (c) 2006 by Digium, Inc. \
+In order for your module to load, it must return this \
+key via a function called \"key\". Any code which \
+includes this paragraph must be licensed under the GNU \
+General Public License version 2 or later (at your \
+option). In addition to Digium's general reservations \
+of rights, Digium expressly reserves the right to \
+allow other parties to license this paragraph under \
+different terms. Any use of Digium, Inc. trademarks or \
+logos (including \"Asterisk\" or \"Digium\") without \
+express written permission of Digium, Inc. is prohibited.\n"
+
+#define AST_MODULE_CONFIG "modules.conf" /*!< \brief Module configuration file */
+
+enum ast_module_unload_mode {
+ AST_FORCE_SOFT = 0, /*!< Softly unload a module, only if not in use */
+ AST_FORCE_FIRM = 1, /*!< Firmly unload a module, even if in use */
+ AST_FORCE_HARD = 2, /*!< as FIRM, plus dlclose() on the module. Not recommended
+ as it may cause crashes */
+};
+
+enum ast_module_load_result {
+ AST_MODULE_LOAD_SUCCESS = 0, /*!< Module loaded and configured */
+ AST_MODULE_LOAD_DECLINE = 1, /*!< Module is not configured */
+ AST_MODULE_LOAD_SKIP = 2, /*!< Module was skipped for some reason */
+ AST_MODULE_LOAD_FAILURE = -1, /*!< Module could not be loaded properly */
+};
+
+/*!
+ * \brief Load a module.
+ * \param resource_name The name of the module to load.
+ *
+ * This function is run by the PBX to load the modules. It performs
+ * all loading and initilization tasks. Basically, to load a module, just
+ * give it the name of the module and it will do the rest.
+ *
+ * \return See possible enum values for ast_module_load_result.
+ */
+enum ast_module_load_result ast_load_resource(const char *resource_name);
+
+/*!
+ * \brief Unload a module.
+ * \param resource_name The name of the module to unload.
+ * \param ast_module_unload_mode The force flag. This should be set using one of the AST_FORCE flags.
+ *
+ * This function unloads a module. It will only unload modules that are not in
+ * use (usecount not zero), unless #AST_FORCE_FIRM or #AST_FORCE_HARD is
+ * specified. Setting #AST_FORCE_FIRM or #AST_FORCE_HARD will unload the
+ * module regardless of consequences (NOT RECOMMENDED).
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode);
+
+/*!
+ * \brief Notify when usecount has been changed.
+ *
+ * This function calulates use counts and notifies anyone trying to keep track
+ * of them. It should be called whenever your module's usecount changes.
+ *
+ * \note The ast_module_user_* functions take care of calling this function for you.
+ */
+void ast_update_use_count(void);
+
+/*!
+ * \brief Ask for a list of modules, descriptions, and use counts.
+ * \param modentry A callback to an updater function.
+ * \param like
+ *
+ * For each of the modules loaded, modentry will be executed with the resource,
+ * description, and usecount values of each particular module.
+ *
+ * \return the number of modules loaded
+ */
+int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
+ const char *like);
+
+/*!
+ * \brief Check if module with the name given is loaded
+ * \param name Module name, like "chan_sip.so"
+ * \retval 1 if true
+ * \retval 0 if false
+ */
+int ast_module_check(const char *name);
+
+/*!
+ * \brief Add a procedure to be run when modules have been updated.
+ * \param updater The function to run when modules have been updated.
+ *
+ * This function adds the given function to a linked list of functions to be
+ * run when the modules are updated.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int ast_loader_register(int (*updater)(void));
+
+/*!
+ * \brief Remove a procedure to be run when modules are updated.
+ * \param updater The updater function to unregister.
+ *
+ * This removes the given function from the updater list.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int ast_loader_unregister(int (*updater)(void));
+
+/*!
+ * \brief Run the unload() callback for all loaded modules
+ *
+ * This function should be called when Asterisk is shutting down gracefully.
+ */
+void ast_module_shutdown(void);
+
+/*!
+ * \brief Match modules names for the Asterisk cli.
+ * \param line Unused by this function, but this should be the line we are
+ * matching.
+ * \param word The partial name to match.
+ * \param pos The position the word we are completing is in.
+ * \param state The possible match to return.
+ * \param rpos The position we should be matching. This should be the same as
+ * pos.
+ * \param needsreload This should be 1 if we need to reload this module and 0
+ * otherwise. This function will only return modules that are reloadble
+ * if this is 1.
+ *
+ * \retval A possible completion of the partial match.
+ * \retval NULL if no matches were found.
+ */
+char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload);
+
+/* Opaque type for module handles generated by the loader */
+
+struct ast_module;
+
+/* User count routines keep track of which channels are using a given module
+ resource. They can help make removing modules safer, particularly if
+ they're in use at the time they have been requested to be removed */
+
+struct ast_module_user;
+struct ast_module_user_list;
+
+/*! \page ModMngmnt The Asterisk Module management interface
+ *
+ * All modules must implement the module API (load, unload...)
+ */
+
+enum ast_module_flags {
+ AST_MODFLAG_DEFAULT = 0,
+ AST_MODFLAG_GLOBAL_SYMBOLS = (1 << 0),
+};
+
+struct ast_module_info {
+
+ /*!
+ * The 'self' pointer for a module; it will be set by the loader before
+ * it calls the module's load_module() entrypoint, and used by various
+ * other macros that need to identify the module.
+ */
+
+ struct ast_module *self;
+ enum ast_module_load_result (*load)(void); /*!< register stuff etc. Optional. */
+ int (*reload)(void); /*!< config etc. Optional. */
+ int (*unload)(void); /*!< unload. called with the module locked */
+ int (*backup_globals)(void); /*!< for embedded modules, backup global data */
+ void (*restore_globals)(void); /*!< for embedded modules, restore global data */
+ const char *name; /*!< name of the module for loader reference and CLI commands */
+ const char *description; /*!< user friendly description of the module. */
+
+ /*!
+ * This holds the ASTERISK_GPL_KEY, signifiying that you agree to the terms of
+ * the Asterisk license as stated in the ASTERISK_GPL_KEY. Your module will not
+ * load if it does not return the EXACT key string.
+ */
+
+ const char *key;
+ unsigned int flags;
+
+ /*! The value of AST_BUILDOPT_SUM when this module was compiled */
+ const char buildopt_sum[33];
+};
+
+void ast_module_register(const struct ast_module_info *);
+void ast_module_unregister(const struct ast_module_info *);
+
+struct ast_module_user *__ast_module_user_add(struct ast_module *, struct ast_channel *);
+void __ast_module_user_remove(struct ast_module *, struct ast_module_user *);
+void __ast_module_user_hangup_all(struct ast_module *);
+
+#define ast_module_user_add(chan) __ast_module_user_add(ast_module_info->self, chan)
+#define ast_module_user_remove(user) __ast_module_user_remove(ast_module_info->self, user)
+#define ast_module_user_hangup_all() __ast_module_user_hangup_all(ast_module_info->self)
+
+struct ast_module *ast_module_ref(struct ast_module *);
+void ast_module_unref(struct ast_module *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+#define AST_MODULE_INFO(keystr, flags_to_set, desc, load_func, unload_func, reload_func) \
+ static struct ast_module_info __mod_info = { \
+ NULL, \
+ load_func, \
+ reload_func, \
+ unload_func, \
+ AST_MODULE, \
+ NULL, \
+ NULL, \
+ desc, \
+ keystr, \
+ flags_to_set, \
+ AST_BUILDOPT_SUM, \
+ }; \
+ static void __attribute__ ((constructor)) __reg_module(void) \
+ { \
+ ast_module_register(&__mod_info); \
+ } \
+ static void __attribute__ ((destructor)) __unreg_module(void) \
+ { \
+ ast_module_unregister(&__mod_info); \
+ } \
+ const static __attribute__((unused)) struct ast_module_info *ast_module_info = &__mod_info
+
+#define AST_MODULE_INFO_STANDARD(keystr, desc) \
+ AST_MODULE_INFO(keystr, AST_MODFLAG_DEFAULT, desc, \
+ load_module, \
+ unload_module, \
+ NULL \
+ )
+#else /* plain C */
+
+/* forward declare this pointer in modules, so that macro/function
+ calls that need it can get it, since it will actually be declared
+ and populated at the end of the module's source file... */
+const static __attribute__((unused)) struct ast_module_info *ast_module_info;
+
+#if !defined(EMBEDDED_MODULE)
+#define __MODULE_INFO_SECTION
+#define __MODULE_INFO_GLOBALS
+#else
+/*
+ * For embedded modules we need additional information to backup and
+ * restore the global variables in the module itself, so we can unload
+ * reload the module.
+ * EMBEDDED_MODULE is defined as the module name, so the calls to make_var()
+ * below will actually define different symbols for each module.
+ */
+#define __MODULE_INFO_SECTION __attribute__((section(".embed_module")))
+#define __MODULE_INFO_GLOBALS .backup_globals = __backup_globals, .restore_globals = __restore_globals,
+
+#define make_var_sub(mod, type) __ ## mod ## _ ## type
+#define make_var(mod, type) make_var_sub(mod, type)
+
+extern void make_var(EMBEDDED_MODULE, bss_start);
+extern void make_var(EMBEDDED_MODULE, bss_end);
+extern void make_var(EMBEDDED_MODULE, data_start);
+extern void make_var(EMBEDDED_MODULE, data_end);
+
+static void * __attribute__((section(".embed_module"))) __global_backup;
+
+static int __backup_globals(void)
+{
+ size_t data_size = & make_var(EMBEDDED_MODULE, data_end) - & make_var(EMBEDDED_MODULE, data_start);
+
+ if (__global_backup)
+ return 0;
+
+ if (!data_size)
+ return 0;
+
+ if (!(__global_backup = ast_malloc(data_size)))
+ return -1;
+
+ memcpy(__global_backup, & make_var(EMBEDDED_MODULE, data_start), data_size);
+
+ return 0;
+}
+
+static void __restore_globals(void)
+{
+ size_t data_size = & make_var(EMBEDDED_MODULE, data_end) - & make_var(EMBEDDED_MODULE, data_start);
+ size_t bss_size = & make_var(EMBEDDED_MODULE, bss_end) - & make_var(EMBEDDED_MODULE, bss_start);
+
+ if (bss_size)
+ memset(& make_var(EMBEDDED_MODULE, bss_start), 0, bss_size);
+
+ if (!data_size || !__global_backup)
+ return;
+
+ memcpy(& make_var(EMBEDDED_MODULE, data_start), __global_backup, data_size);
+}
+#undef make_var
+#undef make_var_sub
+#endif /* EMBEDDED_MODULE */
+
+#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...) \
+ static struct ast_module_info \
+ __MODULE_INFO_SECTION \
+ __mod_info = { \
+ __MODULE_INFO_GLOBALS \
+ .name = AST_MODULE, \
+ .flags = flags_to_set, \
+ .description = desc, \
+ .key = keystr, \
+ .buildopt_sum = AST_BUILDOPT_SUM, \
+ fields \
+ }; \
+ static void __attribute__ ((constructor)) __reg_module(void) \
+ { \
+ ast_module_register(&__mod_info); \
+ } \
+ static void __attribute__ ((destructor)) __unreg_module(void) \
+ { \
+ ast_module_unregister(&__mod_info); \
+ } \
+ const static struct ast_module_info *ast_module_info = &__mod_info
+
+#define AST_MODULE_INFO_STANDARD(keystr, desc) \
+ AST_MODULE_INFO(keystr, AST_MODFLAG_DEFAULT, desc, \
+ .load = load_module, \
+ .unload = unload_module, \
+ )
+#endif /* plain C */
+
+/*!
+ * \brief Register an application.
+ *
+ * \param app Short name of the application
+ * \param execute a function callback to execute the application. It should return
+ * non-zero if the channel needs to be hung up.
+ * \param synopsis a short description (one line synopsis) of the application
+ * \param description long description with all of the details about the use of
+ * the application
+ *
+ * This registers an application with Asterisk's internal application list.
+ * \note The individual applications themselves are responsible for registering and unregistering
+ * and unregistering their own CLI commands.
+ *
+ * \retval 0 success
+ * \retval -1 failure.
+ */
+#define ast_register_application(app, execute, synopsis, description) ast_register_application2(app, execute, synopsis, description, ast_module_info->self)
+
+/*!
+ * \brief Register an application.
+ *
+ * \param app Short name of the application
+ * \param execute a function callback to execute the application. It should return
+ * non-zero if the channel needs to be hung up.
+ * \param synopsis a short description (one line synopsis) of the application
+ * \param description long description with all of the details about the use of
+ * the application
+ * \param mod module this application belongs to
+ *
+ * This registers an application with Asterisk's internal application list.
+ * \note The individual applications themselves are responsible for registering and unregistering
+ * and unregistering their own CLI commands.
+ *
+ * \retval 0 success
+ * \retval -1 failure.
+ */
+int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, void *),
+ const char *synopsis, const char *description, void *mod);
+
+/*!
+ * \brief Unregister an application
+ *
+ * \param app name of the application (does not have to be the same string as the one that was registered)
+ *
+ * This unregisters an application from Asterisk's internal application list.
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_unregister_application(const char *app);
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_MODULE_H */
diff --git a/trunk/include/asterisk/monitor.h b/trunk/include/asterisk/monitor.h
new file mode 100644
index 000000000..7f32b6994
--- /dev/null
+++ b/trunk/include/asterisk/monitor.h
@@ -0,0 +1,71 @@
+/*
+ * 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 Channel monitoring
+ */
+
+#ifndef _ASTERISK_MONITOR_H
+#define _ASTERISK_MONITOR_H
+
+#include "asterisk/channel.h"
+
+enum AST_MONITORING_STATE {
+ AST_MONITOR_RUNNING,
+ AST_MONITOR_PAUSED
+};
+
+/* Streams recording control */
+#define X_REC_IN 1
+#define X_REC_OUT 2
+#define X_JOIN 4
+
+/*! Responsible for channel monitoring data */
+struct ast_channel_monitor {
+ struct ast_filestream *read_stream;
+ struct ast_filestream *write_stream;
+ char read_filename[FILENAME_MAX];
+ char write_filename[FILENAME_MAX];
+ char filename_base[FILENAME_MAX];
+ int filename_changed;
+ char *format;
+ int joinfiles;
+ enum AST_MONITORING_STATE state;
+ int (*stop)(struct ast_channel *chan, int need_lock);
+};
+
+/* Start monitoring a channel */
+int ast_monitor_start(struct ast_channel *chan, const char *format_spec,
+ const char *fname_base, int need_lock, int stream_action);
+
+/* Stop monitoring a channel */
+int ast_monitor_stop(struct ast_channel *chan, int need_lock);
+
+/* Change monitoring filename of a channel */
+int ast_monitor_change_fname(struct ast_channel *chan,
+ const char *fname_base, int need_lock);
+
+void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon);
+
+/* Pause monitoring of a channel */
+int ast_monitor_pause(struct ast_channel *chan);
+
+/* Unpause monitoring of a channel */
+int ast_monitor_unpause(struct ast_channel *chan);
+
+#endif /* _ASTERISK_MONITOR_H */
diff --git a/trunk/include/asterisk/musiconhold.h b/trunk/include/asterisk/musiconhold.h
new file mode 100644
index 000000000..480f2ff8e
--- /dev/null
+++ b/trunk/include/asterisk/musiconhold.h
@@ -0,0 +1,59 @@
+/*
+ * 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 Music on hold handling
+ */
+
+#ifndef _ASTERISK_MOH_H
+#define _ASTERISK_MOH_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*!
+ * \brief Turn on music on hold on a given channel
+ *
+ * \param chan The channel structure that will get music on hold
+ * \param mclass The class to use if the musicclass is not currently set on
+ * the channel structure.
+ * \param interpclass The class to use if the musicclass is not currently set on
+ * the channel structure or in the mclass argument.
+ *
+ * \retval Zero on success
+ * \retval non-zero on failure
+ */
+int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass);
+
+/*! Turn off music on hold on a given channel */
+void ast_moh_stop(struct ast_channel *chan);
+
+void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, const char *, const char *),
+ void (*stop_ptr)(struct ast_channel *),
+ void (*cleanup_ptr)(struct ast_channel *));
+
+void ast_uninstall_music_functions(void);
+
+void ast_moh_cleanup(struct ast_channel *chan);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_MOH_H */
diff --git a/trunk/include/asterisk/netsock.h b/trunk/include/asterisk/netsock.h
new file mode 100644
index 000000000..2aeb803b4
--- /dev/null
+++ b/trunk/include/asterisk/netsock.h
@@ -0,0 +1,70 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@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 Network socket handling
+ */
+
+#ifndef _ASTERISK_NETSOCK_H
+#define _ASTERISK_NETSOCK_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/network.h"
+#include "asterisk/io.h"
+#include "asterisk/astobj.h"
+
+struct ast_netsock;
+
+struct ast_netsock_list;
+
+struct ast_netsock_list *ast_netsock_list_alloc(void);
+
+int ast_netsock_init(struct ast_netsock_list *list);
+
+struct ast_netsock *ast_netsock_bind(struct ast_netsock_list *list, struct io_context *ioc,
+ const char *bindinfo, int defaultport, int tos, int cos, ast_io_cb callback, void *data);
+
+struct ast_netsock *ast_netsock_bindaddr(struct ast_netsock_list *list, struct io_context *ioc,
+ struct sockaddr_in *bindaddr, int tos, int cos, ast_io_cb callback, void *data);
+
+int ast_netsock_free(struct ast_netsock_list *list, struct ast_netsock *netsock);
+
+int ast_netsock_release(struct ast_netsock_list *list);
+
+struct ast_netsock *ast_netsock_find(struct ast_netsock_list *list,
+ struct sockaddr_in *sa);
+
+int ast_netsock_set_qos(int netsocket, int tos, int cos, const char *desc);
+
+int ast_netsock_sockfd(const struct ast_netsock *ns);
+
+const struct sockaddr_in *ast_netsock_boundaddr(const struct ast_netsock *ns);
+
+void *ast_netsock_data(const struct ast_netsock *ns);
+
+void ast_netsock_unref(struct ast_netsock *ns);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_NETSOCK_H */
diff --git a/trunk/include/asterisk/network.h b/trunk/include/asterisk/network.h
new file mode 100644
index 000000000..00aebe45f
--- /dev/null
+++ b/trunk/include/asterisk/network.h
@@ -0,0 +1,98 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Luigi Rizzo
+ *
+ * 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 Wrapper for network related headers,
+ * masking differences between various operating systems.
+ * On passing, we also provide here trivial functions or
+ * other simple wrappers to network-related functions.
+ */
+
+#ifndef _ASTERISK_NETWORK_H
+#define _ASTERISK_NETWORK_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*
+ * Include relevant network headers.
+ * Our preferred choice are the standard BSD/linux/unix headers.
+ * Missing them (e.g. for solaris or various windows environments),
+ * we resort to whatever we find around, and provide local definitions
+ * for the missing bits.
+ */
+#ifdef HAVE_ARPA_INET_H
+#include <netinet/in.h>
+#include <arpa/inet.h> /* include early to override inet_ntoa */
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#elif defined(HAVE_WINSOCK_H)
+#include <winsock.h>
+typedef int socklen_t;
+#elif defined(HAVE_WINSOCK2_H)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#error "don't know how to handle network functions here."
+#endif
+
+#ifndef HAVE_INET_ATON
+int inet_aton(const char *cp, struct in_addr *pin);
+#endif
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+/*!
+ * \brief thread-safe replacement for inet_ntoa().
+ *
+ * \note It is very important to note that even though this is a thread-safe
+ * replacement for inet_ntoa(), it is *not* reentrant. In a single
+ * thread, the result from a previous call to this function is no longer
+ * valid once it is called again. If the result from multiple calls to
+ * this function need to be kept or used at once, then the result must be
+ * copied to a local buffer before calling this function again.
+ */
+const char *ast_inet_ntoa(struct in_addr ia);
+
+#ifdef inet_ntoa
+#undef inet_ntoa
+#endif
+#define inet_ntoa __dont__use__inet_ntoa__use__ast_inet_ntoa__instead__
+
+/*! \brief Compares the source address and port of two sockaddr_in */
+static force_inline int inaddrcmp(const struct sockaddr_in *sin1, const struct sockaddr_in *sin2)
+{
+ return ((sin1->sin_addr.s_addr != sin2->sin_addr.s_addr)
+ || (sin1->sin_port != sin2->sin_port));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_NETWORK_H */
diff --git a/trunk/include/asterisk/options.h b/trunk/include/asterisk/options.h
new file mode 100644
index 000000000..f974ca2c9
--- /dev/null
+++ b/trunk/include/asterisk/options.h
@@ -0,0 +1,137 @@
+/*
+ * 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 Options provided by main asterisk program
+ */
+
+#ifndef _ASTERISK_OPTIONS_H
+#define _ASTERISK_OPTIONS_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define AST_CACHE_DIR_LEN 512
+#define AST_FILENAME_MAX 80
+
+/*! \ingroup main_options */
+enum ast_option_flags {
+ /*! Allow \#exec in config files */
+ AST_OPT_FLAG_EXEC_INCLUDES = (1 << 0),
+ /*! Do not fork() */
+ AST_OPT_FLAG_NO_FORK = (1 << 1),
+ /*! Keep quiet */
+ AST_OPT_FLAG_QUIET = (1 << 2),
+ /*! Console mode */
+ AST_OPT_FLAG_CONSOLE = (1 << 3),
+ /*! Run in realtime Linux priority */
+ AST_OPT_FLAG_HIGH_PRIORITY = (1 << 4),
+ /*! Initialize keys for RSA authentication */
+ AST_OPT_FLAG_INIT_KEYS = (1 << 5),
+ /*! Remote console */
+ AST_OPT_FLAG_REMOTE = (1 << 6),
+ /*! Execute an asterisk CLI command upon startup */
+ AST_OPT_FLAG_EXEC = (1 << 7),
+ /*! Don't use termcap colors */
+ AST_OPT_FLAG_NO_COLOR = (1 << 8),
+ /*! Are we fully started yet? */
+ AST_OPT_FLAG_FULLY_BOOTED = (1 << 9),
+ /*! Trascode via signed linear */
+ AST_OPT_FLAG_TRANSCODE_VIA_SLIN = (1 << 10),
+ /*! Dump core on a seg fault */
+ AST_OPT_FLAG_DUMP_CORE = (1 << 12),
+ /*! Cache sound files */
+ AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13),
+ /*! Display timestamp in CLI verbose output */
+ AST_OPT_FLAG_TIMESTAMP = (1 << 14),
+ /*! Override config */
+ AST_OPT_FLAG_OVERRIDE_CONFIG = (1 << 15),
+ /*! Reconnect */
+ AST_OPT_FLAG_RECONNECT = (1 << 16),
+ /*! Transmit Silence during Record() */
+ AST_OPT_FLAG_TRANSMIT_SILENCE = (1 << 17),
+ /*! Suppress some warnings */
+ AST_OPT_FLAG_DONT_WARN = (1 << 18),
+ /*! End CDRs before the 'h' extension */
+ AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN = (1 << 19),
+ /*! Use Zaptel Timing for generators if available */
+ AST_OPT_FLAG_INTERNAL_TIMING = (1 << 20),
+ /*! Always fork, even if verbose or debug settings are non-zero */
+ AST_OPT_FLAG_ALWAYS_FORK = (1 << 21),
+ /*! Disable log/verbose output to remote consoles */
+ AST_OPT_FLAG_MUTE = (1 << 22),
+ /*! There is a per-file debug setting */
+ AST_OPT_FLAG_DEBUG_FILE = (1 << 23),
+ /*! There is a per-file verbose setting */
+ AST_OPT_FLAG_VERBOSE_FILE = (1 << 24),
+};
+
+/*! These are the options that set by default when Asterisk starts */
+#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN
+
+#define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES)
+#define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK)
+#define ast_opt_quiet ast_test_flag(&ast_options, AST_OPT_FLAG_QUIET)
+#define ast_opt_console ast_test_flag(&ast_options, AST_OPT_FLAG_CONSOLE)
+#define ast_opt_high_priority ast_test_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY)
+#define ast_opt_init_keys ast_test_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS)
+#define ast_opt_remote ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)
+#define ast_opt_exec ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC)
+#define ast_opt_no_color ast_test_flag(&ast_options, AST_OPT_FLAG_NO_COLOR)
+#define ast_fully_booted ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)
+#define ast_opt_transcode_via_slin ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN)
+#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE)
+#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES)
+#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP)
+#define ast_opt_override_config ast_test_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG)
+#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT)
+#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE)
+#define ast_opt_dont_warn ast_test_flag(&ast_options, AST_OPT_FLAG_DONT_WARN)
+#define ast_opt_end_cdr_before_h_exten ast_test_flag(&ast_options, AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN)
+#define ast_opt_internal_timing ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING)
+#define ast_opt_always_fork ast_test_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK)
+#define ast_opt_mute ast_test_flag(&ast_options, AST_OPT_FLAG_MUTE)
+#define ast_opt_dbg_file ast_test_flag(&ast_options, AST_OPT_FLAG_DEBUG_FILE)
+#define ast_opt_verb_file ast_test_flag(&ast_options, AST_OPT_FLAG_VERBOSE_FILE)
+
+extern struct ast_flags ast_options;
+
+extern int option_verbose;
+extern int option_maxfiles; /*!< Max number of open file handles (files, sockets) */
+extern int option_debug; /*!< Debugging */
+extern int option_maxcalls; /*!< Maximum number of simultaneous channels */
+extern double option_maxload;
+#if defined(HAVE_SYSINFO)
+extern long option_minmemfree; /*!< Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
+#endif
+extern char defaultlanguage[];
+
+extern struct timeval ast_startuptime;
+extern struct timeval ast_lastreloadtime;
+extern pid_t ast_mainpid;
+
+extern char record_cache_dir[AST_CACHE_DIR_LEN];
+
+extern int ast_language_is_prefix;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_OPTIONS_H */
diff --git a/trunk/include/asterisk/paths.h b/trunk/include/asterisk/paths.h
new file mode 100644
index 000000000..c161c284d
--- /dev/null
+++ b/trunk/include/asterisk/paths.h
@@ -0,0 +1,39 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Paths to configurable Asterisk directories
+ *
+ * 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
+ */
+
+/*! \file
+ * \brief Asterisk file paths, configured in asterisk.conf
+ */
+
+#ifndef _ASTERISK_PATHS_H
+#define _ASTERISK_PATHS_H
+
+extern const char *ast_config_AST_CONFIG_DIR;
+extern const char *ast_config_AST_CONFIG_FILE;
+extern const char *ast_config_AST_MODULE_DIR;
+extern const char *ast_config_AST_SPOOL_DIR;
+extern const char *ast_config_AST_MONITOR_DIR;
+extern const char *ast_config_AST_VAR_DIR;
+extern const char *ast_config_AST_DATA_DIR;
+extern const char *ast_config_AST_LOG_DIR;
+extern const char *ast_config_AST_AGI_DIR;
+extern const char *ast_config_AST_DB;
+extern const char *ast_config_AST_KEY_DIR;
+extern const char *ast_config_AST_PID;
+extern const char *ast_config_AST_SOCKET;
+extern const char *ast_config_AST_RUN_DIR;
+extern const char *ast_config_AST_RUN_GROUP;
+extern const char *ast_config_AST_RUN_USER;
+extern const char *ast_config_AST_SYSTEM_NAME;
+
+#endif /* _ASTERISK_PATHS_H */
diff --git a/trunk/include/asterisk/pbx.h b/trunk/include/asterisk/pbx.h
new file mode 100644
index 000000000..1082c74b2
--- /dev/null
+++ b/trunk/include/asterisk/pbx.h
@@ -0,0 +1,978 @@
+/*
+ * 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 Core PBX routines and definitions.
+ */
+
+#ifndef _ASTERISK_PBX_H
+#define _ASTERISK_PBX_H
+
+#include "asterisk/sched.h"
+#include "asterisk/chanvars.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define AST_MAX_APP 32 /*!< Max length of an application */
+
+#define AST_PBX_KEEP 0
+#define AST_PBX_REPLACE 1
+
+/*! \brief Special return values from applications to the PBX { */
+#define AST_PBX_HANGUP -1 /*!< Jump to the 'h' exten */
+#define AST_PBX_OK 0 /*!< No errors */
+#define AST_PBX_ERROR 1 /*!< Jump to the 'e' exten */
+#define AST_PBX_KEEPALIVE 10 /*!< Destroy the thread, but don't hang up the channel */
+#define AST_PBX_NO_HANGUP_PEER 11
+/*! } */
+
+#define PRIORITY_HINT -1 /*!< Special Priority for a hint */
+
+/*! \brief Extension states
+ \note States can be combined
+ - \ref AstExtState
+*/
+enum ast_extension_states {
+ AST_EXTENSION_REMOVED = -2, /*!< Extension removed */
+ AST_EXTENSION_DEACTIVATED = -1, /*!< Extension hint removed */
+ AST_EXTENSION_NOT_INUSE = 0, /*!< No device INUSE or BUSY */
+ AST_EXTENSION_INUSE = 1 << 0, /*!< One or more devices INUSE */
+ AST_EXTENSION_BUSY = 1 << 1, /*!< All devices BUSY */
+ AST_EXTENSION_UNAVAILABLE = 1 << 2, /*!< All devices UNAVAILABLE/UNREGISTERED */
+ AST_EXTENSION_RINGING = 1 << 3, /*!< All devices RINGING */
+ AST_EXTENSION_ONHOLD = 1 << 4, /*!< All devices ONHOLD */
+};
+
+
+struct ast_context;
+struct ast_exten;
+struct ast_include;
+struct ast_ignorepat;
+struct ast_sw;
+
+/*! \brief Typedef for devicestate and hint callbacks */
+typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
+
+/*! \brief Data structure associated with a custom dialplan function */
+struct ast_custom_function {
+ const char *name; /*!< Name */
+ const char *synopsis; /*!< Short description for "show functions" */
+ const char *desc; /*!< Help text that explains it all */
+ const char *syntax; /*!< Syntax description */
+ int (*read)(struct ast_channel *, const char *, char *, char *, size_t); /*!< Read function, if read is supported */
+ int (*write)(struct ast_channel *, const char *, char *, const char *); /*!< Write function, if write is supported */
+ struct ast_module *mod; /*!< Module this custom function belongs to */
+ AST_RWLIST_ENTRY(ast_custom_function) acflist;
+};
+
+/*! \brief All switch functions have the same interface, so define a type for them */
+typedef int (ast_switch_f)(struct ast_channel *chan, const char *context,
+ const char *exten, int priority, const char *callerid, const char *data);
+
+/*!< Data structure associated with an Asterisk switch */
+struct ast_switch {
+ AST_LIST_ENTRY(ast_switch) list;
+ const char *name; /*!< Name of the switch */
+ const char *description; /*!< Description of the switch */
+
+ ast_switch_f *exists;
+ ast_switch_f *canmatch;
+ ast_switch_f *exec;
+ ast_switch_f *matchmore;
+};
+
+struct ast_timing {
+ int hastime; /*!< If time construct exists */
+ unsigned int monthmask; /*!< Mask for month */
+ unsigned int daymask; /*!< Mask for date */
+ unsigned int dowmask; /*!< Mask for day of week (mon-sun) */
+ unsigned int minmask[24]; /*!< Mask for minute */
+};
+
+int ast_build_timing(struct ast_timing *i, const char *info);
+int ast_check_timing(const struct ast_timing *i);
+
+struct ast_pbx {
+ int dtimeout; /*!< Timeout between digits (seconds) */
+ int rtimeout; /*!< Timeout for response (seconds) */
+};
+
+
+/*!
+ * \brief Register an alternative dialplan switch
+ *
+ * \param sw switch to register
+ *
+ * This function registers a populated ast_switch structure with the
+ * asterisk switching architecture.
+ *
+ * \return 0 on success, and other than 0 on failure
+ */
+int ast_register_switch(struct ast_switch *sw);
+
+/*!
+ * \brief Unregister an alternative switch
+ *
+ * \param sw switch to unregister
+ *
+ * Unregisters a switch from asterisk.
+ *
+ * \return nothing
+ */
+void ast_unregister_switch(struct ast_switch *sw);
+
+/*!
+ * \brief Look up an application
+ *
+ * \param app name of the app
+ *
+ * This function searches for the ast_app structure within
+ * the apps that are registered for the one with the name
+ * you passed in.
+ *
+ * \return the ast_app structure that matches on success, or NULL on failure
+ */
+struct ast_app *pbx_findapp(const char *app);
+
+/*!
+ * \brief Execute an application
+ *
+ * \param c channel to execute on
+ * \param app which app to execute
+ * \param data the data passed into the app
+ *
+ * This application executes an application on a given channel. It
+ * saves the stack and executes the given application passing in
+ * the given data.
+ *
+ * \return 0 on success, and -1 on failure
+ */
+int pbx_exec(struct ast_channel *c, struct ast_app *app, void *data);
+
+/*!
+ * \brief Register a new context
+ *
+ * \param extcontexts pointer to the ast_context structure pointer
+ * \param name name of the new context
+ * \param registrar registrar of the context
+ *
+ * This will first search for a context with your name. If it exists already, it will not
+ * create a new one. If it does not exist, it will create a new one with the given name
+ * and registrar.
+ *
+ * \return NULL on failure, and an ast_context structure on success
+ */
+struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar);
+
+/*!
+ * \brief Register a new context or find an existing one
+ *
+ * \param extcontexts pointer to the ast_context structure pointer
+ * \param name name of the new context
+ * \param registrar registrar of the context
+ *
+ * This will first search for a context with your name. If it exists already, it will not
+ * create a new one. If it does not exist, it will create a new one with the given name
+ * and registrar.
+ *
+ * \return NULL on failure, and an ast_context structure on success
+ */
+struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar);
+
+/*!
+ * \brief Merge the temporary contexts into a global contexts list and delete from the
+ * global list the ones that are being added
+ *
+ * \param extcontexts pointer to the ast_context structure pointer
+ * \param registrar of the context; if it's set the routine will delete all contexts
+ * that belong to that registrar; if NULL only the contexts that are specified
+ * in extcontexts
+ */
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar);
+
+/*!
+ * \brief Destroy a context (matches the specified context (or ANY context if NULL)
+ *
+ * \param con context to destroy
+ * \param registrar who registered it
+ *
+ * You can optionally leave out either parameter. It will find it
+ * based on either the ast_context or the registrar name.
+ *
+ * \return nothing
+ */
+void ast_context_destroy(struct ast_context *con, const char *registrar);
+
+/*!
+ * \brief Find a context
+ *
+ * \param name name of the context to find
+ *
+ * Will search for the context with the given name.
+ *
+ * \return the ast_context on success, NULL on failure.
+ */
+struct ast_context *ast_context_find(const char *name);
+
+/*! \brief The result codes when starting the PBX on a channelwith \see ast_pbx_start.
+ AST_PBX_CALL_LIMIT refers to the maxcalls call limit in asterisk.conf
+ */
+enum ast_pbx_result {
+ AST_PBX_SUCCESS = 0,
+ AST_PBX_FAILED = -1,
+ AST_PBX_CALL_LIMIT = -2,
+};
+
+/*!
+ * \brief Create a new thread and start the PBX
+ *
+ * \param c channel to start the pbx on
+ *
+ * \see ast_pbx_run for a synchronous function to run the PBX in the
+ * current thread, as opposed to starting a new one.
+ *
+ * \retval Zero on success
+ * \retval non-zero on failure
+ */
+enum ast_pbx_result ast_pbx_start(struct ast_channel *c);
+
+/*!
+ * \brief Execute the PBX in the current thread
+ *
+ * \param c channel to run the pbx on
+ *
+ * This executes the PBX on a given channel. It allocates a new
+ * PBX structure for the channel, and provides all PBX functionality.
+ * See ast_pbx_start for an asynchronous function to run the PBX in a
+ * new thread as opposed to the current one.
+ *
+ * \retval Zero on success
+ * \retval non-zero on failure
+ */
+enum ast_pbx_result ast_pbx_run(struct ast_channel *c);
+
+/*!
+ * \brief Add and extension to an extension context.
+ *
+ * \param context context to add the extension to
+ * \param replace
+ * \param extension extension to add
+ * \param priority priority level of extension addition
+ * \param label extension label
+ * \param callerid pattern to match CallerID, or NULL to match any CallerID
+ * \param application application to run on the extension with that priority level
+ * \param data data to pass to the application
+ * \param datad
+ * \param registrar who registered the extension
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_add_extension(const char *context, int replace, const char *extension,
+ int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *), const char *registrar);
+
+/*!
+ * \brief Add an extension to an extension context, this time with an ast_context *.
+ *
+ * \note For details about the arguments, check ast_add_extension()
+ */
+int ast_add_extension2(struct ast_context *con, int replace, const char *extension,
+ int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *), const char *registrar);
+
+
+/*!
+ * \brief Uses hint and devicestate callback to get the state of an extension
+ *
+ * \param c this is not important
+ * \param context which context to look in
+ * \param exten which extension to get state
+ *
+ * \return extension state as defined in the ast_extension_states enum
+ */
+int ast_extension_state(struct ast_channel *c, const char *context, const char *exten);
+
+/*!
+ * \brief Return string representation of the state of an extension
+ *
+ * \param extension_state is the numerical state delivered by ast_extension_state
+ *
+ * \return the state of an extension as string
+ */
+const char *ast_extension_state2str(int extension_state);
+
+/*!
+ * \brief Registers a state change callback
+ *
+ * \param context which context to look in
+ * \param exten which extension to get state
+ * \param callback callback to call if state changed
+ * \param data to pass to callback
+ *
+ * The callback is called if the state of an extension is changed.
+ *
+ * \retval -1 on failure
+ * \retval ID on success
+ */
+int ast_extension_state_add(const char *context, const char *exten,
+ ast_state_cb_type callback, void *data);
+
+/*!
+ * \brief Deletes a registered state change callback by ID
+ *
+ * \param id of the callback to delete
+ * \param callback callback
+ *
+ * Removes the callback from list of callbacks
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_extension_state_del(int id, ast_state_cb_type callback);
+
+/*!
+ * \brief If an extension hint exists, return non-zero
+ *
+ * \param hint buffer for hint
+ * \param maxlen size of hint buffer
+ * \param name buffer for name portion of hint
+ * \param maxnamelen size of name buffer
+ * \param c this is not important
+ * \param context which context to look in
+ * \param exten which extension to search for
+ *
+ * \return If an extension within the given context with the priority PRIORITY_HINT
+ * is found a non zero value will be returned.
+ * Otherwise, 0 is returned.
+ */
+int ast_get_hint(char *hint, int maxlen, char *name, int maxnamelen,
+ struct ast_channel *c, const char *context, const char *exten);
+
+/*!
+ * \brief Determine whether an extension exists
+ *
+ * \param c this is not important
+ * \param context which context to look in
+ * \param exten which extension to search for
+ * \param priority priority of the action within the extension
+ * \param callerid callerid to search for
+ *
+ * \return If an extension within the given context(or callerid) with the given priority
+ * is found a non zero value will be returned. Otherwise, 0 is returned.
+ */
+int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten,
+ int priority, const char *callerid);
+
+/*!
+ * \brief Find the priority of an extension that has the specified label
+ *
+ * \param c this is not important
+ * \param context which context to look in
+ * \param exten which extension to search for
+ * \param label label of the action within the extension to match to priority
+ * \param callerid callerid to search for
+ *
+ * \retval the priority which matches the given label in the extension
+ * \retval -1 if not found.
+ */
+int ast_findlabel_extension(struct ast_channel *c, const char *context,
+ const char *exten, const char *label, const char *callerid);
+
+/*!
+ * \brief Find the priority of an extension that has the specified label
+ *
+ * \note This function is the same as ast_findlabel_extension, except that it accepts
+ * a pointer to an ast_context structure to specify the context instead of the
+ * name of the context. Otherwise, the functions behave the same.
+ */
+int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con,
+ const char *exten, const char *label, const char *callerid);
+
+/*!
+ * \brief Looks for a valid matching extension
+ *
+ * \param c not really important
+ * \param context context to serach within
+ * \param exten extension to check
+ * \param priority priority of extension path
+ * \param callerid callerid of extension being searched for
+ *
+ * \return If "exten" *could be* a valid extension in this context with or without
+ * some more digits, return non-zero. Basically, when this returns 0, no matter
+ * what you add to exten, it's not going to be a valid extension anymore
+ */
+int ast_canmatch_extension(struct ast_channel *c, const char *context,
+ const char *exten, int priority, const char *callerid);
+
+/*!
+ * \brief Looks to see if adding anything to this extension might match something. (exists ^ canmatch)
+ *
+ * \param c not really important XXX
+ * \param context context to serach within
+ * \param exten extension to check
+ * \param priority priority of extension path
+ * \param callerid callerid of extension being searched for
+ *
+ * \return If "exten" *could match* a valid extension in this context with
+ * some more digits, return non-zero. Does NOT return non-zero if this is
+ * an exact-match only. Basically, when this returns 0, no matter
+ * what you add to exten, it's not going to be a valid extension anymore
+ */
+int ast_matchmore_extension(struct ast_channel *c, const char *context,
+ const char *exten, int priority, const char *callerid);
+
+/*!
+ * \brief Determine if a given extension matches a given pattern (in NXX format)
+ *
+ * \param pattern pattern to match
+ * \param extension extension to check against the pattern.
+ *
+ * Checks whether or not the given extension matches the given pattern.
+ *
+ * \retval 1 on match
+ * \retval 0 on failure
+ */
+int ast_extension_match(const char *pattern, const char *extension);
+
+int ast_extension_close(const char *pattern, const char *data, int needmore);
+
+/*!
+ * \brief Determine if one extension should match before another
+ *
+ * \param a extension to compare with b
+ * \param b extension to compare with a
+ *
+ * Checks whether or extension a should match before extension b
+ *
+ * \retval 0 if the two extensions have equal matching priority
+ * \retval 1 on a > b
+ * \retval -1 on a < b
+ */
+int ast_extension_cmp(const char *a, const char *b);
+
+/*!
+ * \brief Launch a new extension (i.e. new stack)
+ *
+ * \param c not important
+ * \param context which context to generate the extension within
+ * \param exten new extension to add
+ * \param priority priority of new extension
+ * \param callerid callerid of extension
+ * \param found
+ * \param combined_find_spawn
+ *
+ * This adds a new extension to the asterisk extension list.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int ast_spawn_extension(struct ast_channel *c, const char *context,
+ const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn);
+
+/*!
+ * \brief Add a context include
+ *
+ * \param context context to add include to
+ * \param include new include to add
+ * \param registrar who's registering it
+ *
+ * Adds an include taking a char * string as the context parameter
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+*/
+int ast_context_add_include(const char *context, const char *include,
+ const char *registrar);
+
+/*!
+ * \brief Add a context include
+ *
+ * \param con context to add the include to
+ * \param include include to add
+ * \param registrar who registered the context
+ *
+ * Adds an include taking a struct ast_context as the first parameter
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_add_include2(struct ast_context *con, const char *include,
+ const char *registrar);
+
+/*!
+ * \brief Remove a context include
+ *
+ * \note See ast_context_add_include for information on arguments
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_remove_include(const char *context, const char *include,
+ const char *registrar);
+
+/*!
+ * \brief Removes an include by an ast_context structure
+ *
+ * \note See ast_context_add_include2 for information on arguments
+ *
+ * \retval 0 on success
+ * \retval -1 on success
+ */
+int ast_context_remove_include2(struct ast_context *con, const char *include,
+ const char *registrar);
+
+/*!
+ * \brief Verifies includes in an ast_contect structure
+ *
+ * \param con context in which to verify the includes
+ *
+ * \retval 0 if no problems found
+ * \retval -1 if there were any missing context
+ */
+int ast_context_verify_includes(struct ast_context *con);
+
+/*!
+ * \brief Add a switch
+ *
+ * \param context context to which to add the switch
+ * \param sw switch to add
+ * \param data data to pass to switch
+ * \param eval whether to evaluate variables when running switch
+ * \param registrar whoever registered the switch
+ *
+ * This function registers a switch with the asterisk switch architecture
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_add_switch(const char *context, const char *sw, const char *data,
+ int eval, const char *registrar);
+
+/*!
+ * \brief Adds a switch (first param is a ast_context)
+ *
+ * \note See ast_context_add_switch() for argument information, with the exception of
+ * the first argument. In this case, it's a pointer to an ast_context structure
+ * as opposed to the name.
+ */
+int ast_context_add_switch2(struct ast_context *con, const char *sw, const char *data,
+ int eval, const char *registrar);
+
+/*!
+ * \brief Remove a switch
+ *
+ * Removes a switch with the given parameters
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_remove_switch(const char *context, const char *sw,
+ const char *data, const char *registrar);
+
+int ast_context_remove_switch2(struct ast_context *con, const char *sw,
+ const char *data, const char *registrar);
+
+/*!
+ * \brief Simply remove extension from context
+ *
+ * \param context context to remove extension from
+ * \param extension which extension to remove
+ * \param priority priority of extension to remove
+ * \param registrar registrar of the extension
+ *
+ * This function removes an extension from a given context.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_remove_extension(const char *context, const char *extension, int priority,
+ const char *registrar);
+
+int ast_context_remove_extension2(struct ast_context *con, const char *extension,
+ int priority, const char *registrar);
+
+/*!
+ * \brief Add an ignorepat
+ *
+ * \param context which context to add the ignorpattern to
+ * \param ignorepat ignorepattern to set up for the extension
+ * \param registrar registrar of the ignore pattern
+ *
+ * Adds an ignore pattern to a particular context.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_add_ignorepat(const char *context, const char *ignorepat, const char *registrar);
+
+int ast_context_add_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar);
+
+/*
+ * \brief Remove an ignorepat
+ *
+ * \param context context from which to remove the pattern
+ * \param ignorepat the pattern to remove
+ * \param registrar the registrar of the ignore pattern
+ *
+ * This removes the given ignorepattern
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar);
+
+int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar);
+
+/*!
+ * \brief Checks to see if a number should be ignored
+ *
+ * \param context context to search within
+ * \param pattern to check whether it should be ignored or not
+ *
+ * Check if a number should be ignored with respect to dialtone cancellation.
+ *
+ * \retval 0 if the pattern should not be ignored
+ * \retval non-zero if the pattern should be ignored
+ */
+int ast_ignore_pattern(const char *context, const char *pattern);
+
+/* Locking functions for outer modules, especially for completion functions */
+
+/*!
+ * \brief Write locks the context list
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+int ast_wrlock_contexts(void);
+
+/*!
+ * \brief Read locks the context list
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+int ast_rdlock_contexts(void);
+
+/*!
+ * \brief Unlocks contexts
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_unlock_contexts(void);
+
+/*!
+ * \brief Write locks a given context
+ *
+ * \param con context to lock
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_wrlock_context(struct ast_context *con);
+
+/*!
+ * \brief Read locks a given context
+ *
+ * \param con context to lock
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_rdlock_context(struct ast_context *con);
+
+/*!
+ * \retval Unlocks the given context
+ *
+ * \param con context to unlock
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_unlock_context(struct ast_context *con);
+
+/*!
+ * \brief locks the macrolock in the given given context
+ *
+ * \param macrocontext name of the macro-context to lock
+ *
+ * Locks the given macro-context to ensure only one thread (call) can execute it at a time
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_lockmacro(const char *macrocontext);
+
+/*!
+ * \brief Unlocks the macrolock in the given context
+ *
+ * \param macrocontext name of the macro-context to unlock
+ *
+ * Unlocks the given macro-context so that another thread (call) can execute it
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_unlockmacro(const char *macrocontext);
+
+int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+int ast_async_goto_by_name(const char *chan, const char *context, const char *exten, int priority);
+
+/*! Synchronously or asynchronously make an outbound call and send it to a
+ particular extension */
+int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel);
+
+/*! Synchronously or asynchronously make an outbound call and send it to a
+ particular application with given extension */
+int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel);
+
+/*!
+ * \brief Evaluate a condition
+ *
+ * \retval 0 if the condition is NULL or of zero length
+ * \retval int If the string is an integer, the integer representation of
+ * the integer is returned
+ * \retval 1 Any other non-empty string
+ */
+int pbx_checkcondition(const char *condition);
+
+/*! @name
+ * Functions for returning values from structures */
+/*! @{ */
+const char *ast_get_context_name(struct ast_context *con);
+const char *ast_get_extension_name(struct ast_exten *exten);
+struct ast_context *ast_get_extension_context(struct ast_exten *exten);
+const char *ast_get_include_name(struct ast_include *include);
+const char *ast_get_ignorepat_name(struct ast_ignorepat *ip);
+const char *ast_get_switch_name(struct ast_sw *sw);
+const char *ast_get_switch_data(struct ast_sw *sw);
+/*! @} */
+
+/*! @name Other Extension stuff */
+/*! @{ */
+int ast_get_extension_priority(struct ast_exten *exten);
+int ast_get_extension_matchcid(struct ast_exten *e);
+const char *ast_get_extension_cidmatch(struct ast_exten *e);
+const char *ast_get_extension_app(struct ast_exten *e);
+const char *ast_get_extension_label(struct ast_exten *e);
+void *ast_get_extension_app_data(struct ast_exten *e);
+/*! @} */
+
+/*! @name Registrar info functions ... */
+/*! @{ */
+const char *ast_get_context_registrar(struct ast_context *c);
+const char *ast_get_extension_registrar(struct ast_exten *e);
+const char *ast_get_include_registrar(struct ast_include *i);
+const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip);
+const char *ast_get_switch_registrar(struct ast_sw *sw);
+/*! @} */
+
+/* Walking functions ... */
+struct ast_context *ast_walk_contexts(struct ast_context *con);
+struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *priority);
+struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority);
+struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc);
+struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
+ struct ast_ignorepat *ip);
+struct ast_sw *ast_walk_context_switches(struct ast_context *con, struct ast_sw *sw);
+
+/*!
+ * \note Will lock the channel.
+ */
+int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf);
+
+/*!
+ * \note Will lock the channel.
+ */
+const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name);
+
+/*!
+ * \note Will lock the channel.
+ */
+void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value);
+
+/*!
+ * \note Will lock the channel.
+ */
+void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value);
+
+/*!
+ * \note Will lock the channel.
+ */
+void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp);
+void pbx_builtin_clear_globals(void);
+
+/*!
+ * \note Will lock the channel.
+ */
+int pbx_builtin_setvar(struct ast_channel *chan, void *data);
+
+int pbx_builtin_raise_exception(struct ast_channel *chan, void *data);
+
+void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
+void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count);
+
+int ast_extension_patmatch(const char *pattern, const char *data);
+
+/*! Set "autofallthrough" flag, if newval is <0, does not acutally set. If
+ set to 1, sets to auto fall through. If newval set to 0, sets to no auto
+ fall through (reads extension instead). Returns previous value. */
+int pbx_set_autofallthrough(int newval);
+
+/*! Set "extenpatternmatchnew" flag, if newval is <0, does not acutally set. If
+ set to 1, sets to use the new Trie-based pattern matcher. If newval set to 0, sets to use
+ the old linear-search algorithm. Returns previous value. */
+int pbx_set_extenpatternmatchnew(int newval);
+
+/*!
+ * \note This function will handle locking the channel as needed.
+ */
+int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+/*!
+ * \note I can find neither parsable nor parseable at dictionary.com,
+ * but google gives me 169000 hits for parseable and only 49,800
+ * for parsable
+ *
+ * \note This function will handle locking the channel as needed.
+ */
+int ast_parseable_goto(struct ast_channel *chan, const char *goto_string);
+
+/*!
+ * \note This function will handle locking the channel as needed.
+ */
+int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+/*!
+ * \note This function will handle locking the channel as needed.
+ */
+int ast_async_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+struct ast_custom_function* ast_custom_function_find(const char *name);
+
+/*!
+ * \brief Unregister a custom function
+ */
+int ast_custom_function_unregister(struct ast_custom_function *acf);
+
+/*!
+ * \brief Register a custom function
+ */
+#define ast_custom_function_register(acf) __ast_custom_function_register(acf, ast_module_info->self)
+
+/*!
+ * \brief Register a custom function
+ */
+int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod);
+
+/*!
+ * \brief Retrieve the number of active calls
+ */
+int ast_active_calls(void);
+
+/*!
+ * \brief Retrieve the total number of calls processed through the PBX since last restart
+ */
+int ast_processed_calls(void);
+
+/*!
+ * \brief executes a read operation on a function
+ *
+ * \param chan Channel to execute on
+ * \param function Data containing the function call string (will be modified)
+ * \param workspace A pointer to safe memory to use for a return value
+ * \param len the number of bytes in workspace
+ *
+ * This application executes a function in read mode on a given channel.
+ *
+ * \return zero on success, non-zero on failure
+ */
+int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len);
+
+/*!
+ * \brief executes a write operation on a function
+ *
+ * \param chan Channel to execute on
+ * \param function Data containing the function call string (will be modified)
+ * \param value A value parameter to pass for writing
+ *
+ * This application executes a function in write mode on a given channel.
+ *
+ * \return zero on success, non-zero on failure
+ */
+int ast_func_write(struct ast_channel *chan, const char *function, const char *value);
+
+/*!
+ * When looking up extensions, we can have different requests
+ * identified by the 'action' argument, as follows.
+ * Note that the coding is such that the low 4 bits are the
+ * third argument to extension_match_core.
+ */
+
+enum ext_match_t {
+ E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
+ E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
+ E_MATCH = 0x02, /* extension is an exact match */
+ E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
+ E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
+ E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
+};
+
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+#define AST_PBX_MAX_STACK 128
+
+/* request and result for pbx_find_extension */
+struct pbx_find_info {
+#if 0
+ const char *context;
+ const char *exten;
+ int priority;
+#endif
+
+ char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
+ int stacklen; /* modified during the search */
+ int status; /* set on return */
+ struct ast_switch *swo; /* set on return */
+ const char *data; /* set on return */
+ const char *foundcontext; /* set on return */
+};
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass, struct pbx_find_info *q,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action);
+
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_PBX_H */
diff --git a/trunk/include/asterisk/plc.h b/trunk/include/asterisk/plc.h
new file mode 100644
index 000000000..cd650012c
--- /dev/null
+++ b/trunk/include/asterisk/plc.h
@@ -0,0 +1,153 @@
+/*! \file
+ * \brief SpanDSP - a series of DSP components for telephony
+ *
+ * plc.h
+ *
+ * \author Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2004 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This version may be optionally licenced under the GNU LGPL licence.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+
+#if !defined(_PLC_H_)
+#define _PLC_H_
+
+/* solaris used to #include <sys/int_types.h> */
+
+/*! \page plc_page Packet loss concealment
+\section plc_page_sec_1 What does it do?
+The packet loss concealment module provides a suitable synthetic fill-in signal,
+to minimise the audible effect of lost packets in VoIP applications. It is not
+tied to any particular codec, and could be used with almost any codec which does not
+specify its own procedure for packet loss concealment.
+
+Where a codec specific concealment procedure exists, the algorithm is usually built
+around knowledge of the characteristics of the particular codec. It will, therefore,
+generally give better results for that particular codec than this generic concealer will.
+
+\section plc_page_sec_2 How does it work?
+While good packets are being received, the plc_rx() routine keeps a record of the trailing
+section of the known speech signal. If a packet is missed, plc_fillin() is called to produce
+a synthetic replacement for the real speech signal. The average mean difference function
+(AMDF) is applied to the last known good signal, to determine its effective pitch.
+Based on this, the last pitch period of signal is saved. Essentially, this cycle of speech
+will be repeated over and over until the real speech resumes. However, several refinements
+are needed to obtain smooth pleasant sounding results.
+
+- The two ends of the stored cycle of speech will not always fit together smoothly. This can
+ cause roughness, or even clicks, at the joins between cycles. To soften this, the
+ 1/4 pitch period of real speech preceeding the cycle to be repeated is blended with the last
+ 1/4 pitch period of the cycle to be repeated, using an overlap-add (OLA) technique (i.e.
+ in total, the last 5/4 pitch periods of real speech are used).
+
+- The start of the synthetic speech will not always fit together smoothly with the tail of
+ real speech passed on before the erasure was identified. Ideally, we would like to modify
+ the last 1/4 pitch period of the real speech, to blend it into the synthetic speech. However,
+ it is too late for that. We could have delayed the real speech a little, but that would
+ require more buffer manipulation, and hurt the efficiency of the no-lost-packets case
+ (which we hope is the dominant case). Instead we use a degenerate form of OLA to modify
+ the start of the synthetic data. The last 1/4 pitch period of real speech is time reversed,
+ and OLA is used to blend it with the first 1/4 pitch period of synthetic speech. The result
+ seems quite acceptable.
+
+- As we progress into the erasure, the chances of the synthetic signal being anything like
+ correct steadily fall. Therefore, the volume of the synthesized signal is made to decay
+ linearly, such that after 50ms of missing audio it is reduced to silence.
+
+- When real speech resumes, an extra 1/4 pitch period of sythetic speech is blended with the
+ start of the real speech. If the erasure is small, this smoothes the transition. If the erasure
+ is long, and the synthetic signal has faded to zero, the blending softens the start up of the
+ real signal, avoiding a kind of "click" or "pop" effect that might occur with a sudden onset.
+
+\section plc_page_sec_3 How do I use it?
+Before audio is processed, call plc_init() to create an instance of the packet loss
+concealer. For each received audio packet that is acceptable (i.e. not including those being
+dropped for being too late) call plc_rx() to record the content of the packet. Note this may
+modify the packet a little after a period of packet loss, to blend real synthetic data smoothly.
+When a real packet is not available in time, call plc_fillin() to create a sythetic substitute.
+That's it!
+*/
+
+/*! Minimum allowed pitch (66 Hz) */
+#define PLC_PITCH_MIN 120
+/*! Maximum allowed pitch (200 Hz) */
+#define PLC_PITCH_MAX 40
+/*! Maximum pitch OLA window */
+#define PLC_PITCH_OVERLAP_MAX (PLC_PITCH_MIN >> 2)
+/*! The length over which the AMDF function looks for similarity (20 ms) */
+#define CORRELATION_SPAN 160
+/*! History buffer length. The buffer much also be at leat 1.25 times
+ PLC_PITCH_MIN, but that is much smaller than the buffer needs to be for
+ the pitch assessment. */
+#define PLC_HISTORY_LEN (CORRELATION_SPAN + PLC_PITCH_MIN)
+
+typedef struct
+{
+ /*! Consecutive erased samples */
+ int missing_samples;
+ /*! Current offset into pitch period */
+ int pitch_offset;
+ /*! Pitch estimate */
+ int pitch;
+ /*! Buffer for a cycle of speech */
+ float pitchbuf[PLC_PITCH_MIN];
+ /*! History buffer */
+ int16_t history[PLC_HISTORY_LEN];
+ /*! Current pointer into the history buffer */
+ int buf_ptr;
+} plc_state_t;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! Process a block of received audio samples.
+ \brief Process a block of received audio samples.
+ \param s The packet loss concealer context.
+ \param amp The audio sample buffer.
+ \param len The number of samples in the buffer.
+ \return The number of samples in the buffer. */
+int plc_rx(plc_state_t *s, int16_t amp[], int len);
+
+/*! Fill-in a block of missing audio samples.
+ \brief Fill-in a block of missing audio samples.
+ \param s The packet loss concealer context.
+ \param amp The audio sample buffer.
+ \param len The number of samples to be synthesised.
+ \return The number of samples synthesized. */
+int plc_fillin(plc_state_t *s, int16_t amp[], int len);
+
+/*! Process a block of received V.29 modem audio samples.
+ \brief Process a block of received V.29 modem audio samples.
+ \param s The packet loss concealer context.
+ \return A pointer to the he packet loss concealer context. */
+plc_state_t *plc_init(plc_state_t *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/trunk/include/asterisk/poll-compat.h b/trunk/include/asterisk/poll-compat.h
new file mode 100644
index 000000000..5f795a894
--- /dev/null
+++ b/trunk/include/asterisk/poll-compat.h
@@ -0,0 +1,111 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * 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.
+ */
+
+/*---------------------------------------------------------------------------*\
+ $Id$
+
+ NAME
+
+ poll - select(2)-based poll() emulation function for BSD systems.
+
+ SYNOPSIS
+ #include "poll.h"
+
+ struct pollfd
+ {
+ int fd;
+ short events;
+ short revents;
+ }
+
+ int poll (struct pollfd *pArray, unsigned long n_fds, int timeout)
+
+ DESCRIPTION
+
+ This file, and the accompanying "poll.c", implement the System V
+ poll(2) system call for BSD systems (which typically do not provide
+ poll()). Poll() provides a method for multiplexing input and output
+ on multiple open file descriptors; in traditional BSD systems, that
+ capability is provided by select(). While the semantics of select()
+ differ from those of poll(), poll() can be readily emulated in terms
+ of select() -- which is how this function is implemented.
+
+ REFERENCES
+ Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990.
+
+ NOTES
+ 1. This software requires an ANSI C compiler.
+
+ LICENSE
+
+ This software is released under the following license:
+
+ Copyright (c) 1995-2002 Brian M. Clapper
+ All rights reserved.
+
+ Redistribution and use in source and binary forms are
+ permitted provided that: (1) source distributions retain
+ this entire copyright notice and comment; (2) modifications
+ made to the software are prominently mentioned, and a copy
+ of the original software (or a pointer to its location) are
+ included; and (3) distributions including binaries display
+ the following acknowledgement: "This product includes
+ software developed by Brian M. Clapper <bmc@clapper.org>"
+ in the documentation or other materials provided with the
+ distribution. The name of the author may not be used to
+ endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE.
+
+ Effectively, this means you can do what you want with the software
+ except remove this notice or take advantage of the author's name.
+ If you modify the software and redistribute your modified version,
+ you must indicate that your version is a modification of the
+ original, and you must provide either a pointer to or a copy of the
+ original.
+\*---------------------------------------------------------------------------*/
+
+#ifndef _POLL_EMUL_H_
+#define _POLL_EMUL_H_
+
+#define POLLIN 0x01
+#define POLLPRI 0x02
+#define POLLOUT 0x04
+#define POLLERR 0x08
+#define POLLHUP 0x10
+#define POLLNVAL 0x20
+
+struct pollfd
+{
+ int fd;
+ short events;
+ short revents;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if (__STDC__ > 0) || defined(__cplusplus)
+extern int poll (struct pollfd *pArray, unsigned long n_fds, int timeout);
+#else
+extern int poll();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _POLL_EMUL_H_ */
diff --git a/trunk/include/asterisk/privacy.h b/trunk/include/asterisk/privacy.h
new file mode 100644
index 000000000..686a14d75
--- /dev/null
+++ b/trunk/include/asterisk/privacy.h
@@ -0,0 +1,46 @@
+/*
+ * 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 Persistant data storage (akin to *doze registry)
+ */
+
+#ifndef _ASTERISK_PRIVACY_H
+#define _ASTERISK_PRIVACY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define AST_PRIVACY_DENY (1 << 0) /* Don't bother ringing, send to voicemail */
+#define AST_PRIVACY_ALLOW (1 << 1) /* Pass directly to me */
+#define AST_PRIVACY_KILL (1 << 2) /* Play anti-telemarketer message and hangup */
+#define AST_PRIVACY_TORTURE (1 << 3) /* Send directly to tele-torture */
+#define AST_PRIVACY_UNKNOWN (1 << 16)
+
+int ast_privacy_check(char *dest, char *cid);
+
+int ast_privacy_set(char *dest, char *cid, int status);
+
+int ast_privacy_reset(char *dest);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_PRIVACY_H */
diff --git a/trunk/include/asterisk/pval.h b/trunk/include/asterisk/pval.h
new file mode 100644
index 000000000..edfd647b3
--- /dev/null
+++ b/trunk/include/asterisk/pval.h
@@ -0,0 +1,273 @@
+#ifndef _ASTERISK_PVAL_H
+#define _ASTERISK_PVAL_H
+
+
+typedef enum
+{
+ PV_WORD, /* an ident, string, name, label, etc. A user-supplied string. */ /* 0 */
+ PV_MACRO, /* 1 */
+ PV_CONTEXT, /* 2 */
+ PV_MACRO_CALL, /* 3 */
+ PV_APPLICATION_CALL, /* 4 */
+ PV_CASE, /* 5 */
+ PV_PATTERN, /* 6 */
+ PV_DEFAULT, /* 7 */
+ PV_CATCH, /* 8 */
+ PV_SWITCHES, /* 9 */
+ PV_ESWITCHES, /* 10 */
+ PV_INCLUDES, /* 11 */
+ PV_STATEMENTBLOCK, /* 12 */
+ PV_VARDEC, /* you know, var=val; */ /* 13 */
+ PV_GOTO, /* 14 */
+ PV_LABEL, /* 15 */
+ PV_FOR, /* 16 */
+ PV_WHILE, /* 17 */
+ PV_BREAK, /* 18 */
+ PV_RETURN, /* 19 */
+ PV_CONTINUE, /* 20 */
+ PV_IF, /* 21 */
+ PV_IFTIME, /* 22 */
+ PV_RANDOM, /* 23 */
+ PV_SWITCH, /* 24 */
+ PV_EXTENSION, /* 25 */
+ PV_IGNOREPAT, /* 26 */
+ PV_GLOBALS, /* 27 */
+ PV_LOCALVARDEC, /* 28 */
+} pvaltype;
+
+/* why this horrible mess? It's always been a tradeoff-- tons of structs,
+ each storing it's specific lists of goodies, or a 'simple' single struct,
+ with lots of fields, that catches all uses at once. Either you have a long
+ list of struct names and subnames, or you have a long list of field names,
+ and where/how they are used. I'm going with a single struct, using unions
+ to reduce storage. Some simple generalizations, and a long list of types,
+ and a book about what is used with what types.... Sorry!
+*/
+
+struct pval
+{
+ pvaltype type;
+ int startline;
+ int endline;
+ int startcol;
+ int endcol;
+ char *filename;
+
+ union
+ {
+ char *str; /* wow, used almost everywhere! */
+ struct pval *list; /* used in SWITCHES, ESWITCHES, INCLUDES, STATEMENTBLOCK, GOTO */
+ struct pval *statements;/* used in EXTENSION */
+ char *for_init; /* used in FOR */
+ } u1;
+ struct pval *u1_last; /* to build in-order lists -- looks like we only need one */
+
+ union
+ {
+ struct pval *arglist; /* used in macro_call, application_call, MACRO def, also attached to PWORD, the 4 timevals for includes */
+ struct pval *statements; /* used in case, default, catch, while's statement, CONTEXT elements, GLOBALS */
+ char *val; /* used in VARDEC */
+ char *for_test; /* used in FOR */
+ int label_in_case; /* a boolean for LABELs */
+ struct pval *goto_target; /* used in GOTO */
+ } u2;
+
+ union
+ {
+ char *for_inc; /* used in FOR */
+ struct pval *else_statements; /* used in IF */
+ struct pval *macro_statements; /* used in MACRO */
+ int abstract; /* used for context 1=abstract; 2=extend; 3=both */
+ char *hints; /* used in EXTENSION */
+ int goto_target_in_case; /* used in GOTO */
+ struct ael_extension *compiled_label;
+ struct pval *extend; /* to link extended contexts to the 'original' */
+ } u3;
+
+ union
+ {
+ struct pval *for_statements; /* used in PV_FOR */
+ int regexten; /* used in EXTENSION */
+ } u4;
+
+ struct pval *next; /* the pval at the end of this ptr will ALWAYS be of the same type as this one!
+ EXCEPT for objects of the different types, that are in the same list, like contexts & macros, etc */
+
+ struct pval *dad; /* the 'container' of this struct instance */
+ struct pval *prev; /* the opposite of the 'next' pointer */
+} ;
+
+
+typedef struct pval pval;
+
+#ifndef AAL_ARGCHECK
+/* for the time being, short circuit all the AAL related structures
+ without permanently removing the code; after/during the AAL
+ development, this code can be properly re-instated
+*/
+
+/* null definitions for structs passed down the infrastructure */
+struct argapp
+{
+ struct argapp *next;
+};
+
+#endif
+
+struct ast_context;
+
+#ifdef AAL_ARGCHECK
+int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
+int option_matches( struct argdesc *should, pval *is, struct argapp *app);
+int ael_is_funcname(char *name);
+#endif
+
+int do_pbx_load_module(void);
+int count_labels_in_current_context(char *label);
+int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
+void check_pval(pval *item, struct argapp *apps, int in_globals);
+void check_pval_item(pval *item, struct argapp *apps, int in_globals);
+void check_switch_expr(pval *item, struct argapp *apps);
+void ast_expr_register_extra_error_info(char *errmsg);
+void ast_expr_clear_extra_error_info(void);
+int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan);
+struct pval *find_macro(char *name);
+struct pval *find_context(char *name);
+struct pval *find_context(char *name);
+struct pval *find_macro(char *name);
+struct ael_priority *new_prio(void);
+struct ael_extension *new_exten(void);
+void linkprio(struct ael_extension *exten, struct ael_priority *prio);
+void destroy_extensions(struct ael_extension *exten);
+/* static void linkexten(struct ael_extension *exten, struct ael_extension *add);
+ static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *context ); */
+void set_priorities(struct ael_extension *exten);
+void add_extensions(struct ael_extension *exten);
+void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
+void destroy_pval(pval *item);
+void destroy_pval_item(pval *item);
+int is_float(char *arg );
+int is_int(char *arg );
+int is_empty(char *arg);
+
+/* PVAL PI */
+
+
+pval *pvalCreateNode( pvaltype type );
+pvaltype pvalObjectGetType( pval *p );
+
+void pvalWordSetString( pval *p, char *str);
+char *pvalWordGetString( pval *p );
+
+void pvalMacroSetName( pval *p, char *name);
+char *pvalMacroGetName( pval *p );
+void pvalMacroSetArglist( pval *p, pval *arglist );
+void pvalMacroAddArg( pval *p, pval *arg );
+pval *pvalMacroWalkArgs( pval *p, pval **arg );
+void pvalMacroAddStatement( pval *p, pval *statement );
+pval *pvalMacroWalkStatements( pval *p, pval **next_statement );
+
+void pvalContextSetName( pval *p, char *name);
+char *pvalContextGetName( pval *p );
+void pvalContextSetAbstract( pval *p );
+void pvalContextUnsetAbstract( pval *p );
+int pvalContextGetAbstract( pval *p );
+void pvalContextAddStatement( pval *p, pval *statement);
+pval *pvalContextWalkStatements( pval *p, pval **statements );
+
+void pvalMacroCallSetMacroName( pval *p, char *name );
+char* pvalMacroCallGetMacroName( pval *p );
+void pvalMacroCallSetArglist( pval *p, pval *arglist );
+void pvalMacroCallAddArg( pval *p, pval *arg );
+pval *pvalMacroCallWalkArgs( pval *p, pval **args );
+
+void pvalAppCallSetAppName( pval *p, char *name );
+char* pvalAppCallGetAppName( pval *p );
+void pvalAppCallSetArglist( pval *p, pval *arglist );
+void pvalAppCallAddArg( pval *p, pval *arg );
+pval *pvalAppCallWalkArgs( pval *p, pval **args );
+
+void pvalCasePatSetVal( pval *p, char *val );
+char* pvalCasePatGetVal( pval *p );
+void pvalCasePatDefAddStatement( pval *p, pval *statement );
+pval *pvalCasePatDefWalkStatements( pval *p, pval **statement );
+
+void pvalCatchSetExtName( pval *p, char *name );
+char* pvalCatchGetExtName( pval *p );
+void pvalCatchSetStatement( pval *p, pval *statement );
+pval *pvalCatchGetStatement( pval *p );
+
+void pvalSwitchesAddSwitch( pval *p, char *name );
+char* pvalSwitchesWalkNames( pval *p, pval **next_item );
+void pvalESwitchesAddSwitch( pval *p, char *name );
+char* pvalESwitchesWalkNames( pval *p, pval **next_item );
+
+void pvalIncludesAddInclude( pval *p, const char *include );
+
+void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range );
+void pvalIncludeGetTimeConstraints( pval *p, char **hour_range, char **dom_range, char **dow_range, char **month_range );
+char* pvalIncludesWalk( pval *p, pval **next_item );
+
+void pvalStatementBlockAddStatement( pval *p, pval *statement);
+pval *pvalStatementBlockWalkStatements( pval *p, pval **next_statement);
+
+void pvalVarDecSetVarname( pval *p, char *name );
+void pvalVarDecSetValue( pval *p, char *value );
+char* pvalVarDecGetVarname( pval *p );
+char* pvalVarDecGetValue( pval *p );
+
+void pvalGotoSetTarget( pval *p, char *context, char *exten, char *label );
+void pvalGotoGetTarget( pval *p, char **context, char **exten, char **label );
+
+void pvalLabelSetName( pval *p, char *name );
+char* pvalLabelGetName( pval *p );
+
+void pvalForSetInit( pval *p, char *init );
+void pvalForSetTest( pval *p, char *test );
+void pvalForSetInc( pval *p, char *inc );
+void pvalForSetStatement( pval *p, pval *statement );
+char* pvalForGetInit( pval *p );
+char* pvalForGetTest( pval *p );
+char* pvalForGetInc( pval *p );
+pval* pvalForGetStatement( pval *p );
+
+
+void pvalIfSetCondition( pval *p, char *expr );
+char* pvalIfGetCondition( pval *p );
+void pvalIfTimeSetCondition( pval *p, char *hour_range, char *dow_range, char *dom_range, char *mon_range ); /* time range format: 24-hour format begin-end|dow range|dom range|month range */
+void pvalIfTimeGetCondition( pval *p, char **hour_range, char **dow_range, char **dom_range, char **month_range );
+void pvalRandomSetCondition( pval *p, char *percent );
+char* pvalRandomGetCondition( pval *p );
+void pvalConditionalSetThenStatement( pval *p, pval *statement );
+void pvalConditionalSetElseStatement( pval *p, pval *statement );
+pval* pvalConditionalGetThenStatement( pval *p );
+pval* pvalConditionalGetElseStatement( pval *p );
+
+void pvalSwitchSetTestexpr( pval *p, char *expr );
+char* pvalSwitchGetTestexpr( pval *p );
+void pvalSwitchAddCase( pval *p, pval *Case );
+pval* pvalSwitchWalkCases( pval *p, pval **next_case );
+
+void pvalExtenSetName( pval *p, char *name );
+char *pvalExtenGetName( pval *p );
+void pvalExtenSetRegexten( pval *p );
+void pvalExtenUnSetRegexten( pval *p );
+int pvalExtenGetRegexten( pval *p );
+void pvalExtenSetHints( pval *p, char *hints );
+char* pvalExtenGetHints( pval *p );
+void pvalExtenSetStatement( pval *p, pval *statement );
+pval* pvalExtenGetStatement( pval *p );
+
+void pvalIgnorePatSetPattern( pval *p, char *pat );
+char* pvalIgnorePatGetPattern( pval *p );
+
+void pvalGlobalsAddStatement( pval *p, pval *statement );
+pval* pvalGlobalsWalkStatements( pval *p, pval **next_statement );
+
+void pvalTopLevAddObject( pval *p, pval *contextOrObj );
+pval* pvalTopLevWalkObjects( pval *p, pval **next_obj );
+
+int pvalCheckType( pval *p, char *funcname, pvaltype type );
+
+
+#endif
diff --git a/trunk/include/asterisk/res_odbc.h b/trunk/include/asterisk/res_odbc.h
new file mode 100644
index 000000000..fcb1581e4
--- /dev/null
+++ b/trunk/include/asterisk/res_odbc.h
@@ -0,0 +1,122 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ * Copyright (C) 2004 - 2005, Anthony Minessale II
+ * Copyright (C) 2006, Tilghman Lesher
+ *
+ * Mark Spencer <markster@digium.com>
+ * Anthony Minessale <anthmct@yahoo.com>
+ * Tilghman Lesher <res_odbc_200603@the-tilghman.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 ODBC resource manager
+ */
+
+#ifndef _ASTERISK_RES_ODBC_H
+#define _ASTERISK_RES_ODBC_H
+
+#include <sql.h>
+#include <sqlext.h>
+#include <sqltypes.h>
+
+typedef enum { ODBC_SUCCESS=0, ODBC_FAIL=-1} odbc_status;
+
+/*! \brief ODBC container */
+struct odbc_obj {
+ ast_mutex_t lock;
+ SQLHDBC con; /* ODBC Connection Handle */
+ struct odbc_class *parent; /* Information about the connection is protected */
+ unsigned int used:1;
+ unsigned int up:1;
+ AST_LIST_ENTRY(odbc_obj) list;
+};
+
+/* functions */
+
+/*!
+ * \brief Executes a prepared statement handle
+ * \param obj The non-NULL result of odbc_request_obj()
+ * \param stmt The prepared statement handle
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * This function was originally designed simply to execute a prepared
+ * statement handle and to retry if the initial execution failed.
+ * Unfortunately, it did this by disconnecting and reconnecting the database
+ * handle which on most databases causes the statement handle to become
+ * invalid. Therefore, this method has been deprecated in favor of
+ * odbc_prepare_and_execute() which allows the statement to be prepared
+ * multiple times, if necessary, in case of a loss of connection.
+ *
+ * This function really only ever worked with MySQL, where the statement handle is
+ * not prepared on the server. If you are not using MySQL, you should avoid it.
+ */
+int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt); /* DEPRECATED */
+
+/*!
+ * \brief Retrieves a connected ODBC object
+ * \param name The name of the ODBC class for which a connection is needed.
+ * \param check Whether to ensure that a connection is valid before returning the handle. Usually unnecessary.
+ * \retval ODBC object
+ * \retval NULL if there is no connection available with the requested name.
+ *
+ * Connection classes may, in fact, contain multiple connection handles. If
+ * the connection is pooled, then each connection will be dedicated to the
+ * thread which requests it. Note that all connections should be released
+ * when the thread is done by calling odbc_release_obj(), below.
+ */
+struct odbc_obj *ast_odbc_request_obj(const char *name, int check);
+
+/*!
+ * \brief Releases an ODBC object previously allocated by odbc_request_obj()
+ * \param obj The ODBC object
+ */
+void ast_odbc_release_obj(struct odbc_obj *obj);
+
+/*!
+ * \brief Checks an ODBC object to ensure it is still connected
+ * \param obj The ODBC object
+ * \retval 0 if connected
+ * \retval -1 otherwise.
+ */
+int ast_odbc_sanity_check(struct odbc_obj *obj);
+
+/*! \brief Checks if the database natively supports backslash as an escape character.
+ * \param obj The ODBC object
+ * \return Returns 1 if an ESCAPE clause is needed to support '\', 0 otherwise
+ */
+int ast_odbc_backslash_is_escape(struct odbc_obj *obj);
+
+/*! \brief Executes an non prepared statement and returns the resulting
+ * statement handle.
+ * \param obj The ODBC object
+ * \param exec_cb A function callback, which, when called, should return a statement handle with result columns bound.
+ * \param data A parameter to be passed to the exec_cb parameter function, indicating which statement handle is to be prepared.
+ * \retval a statement handle
+ * \retval NULL on error
+ */
+SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data);
+
+/*!
+ * \brief Prepares, executes, and returns the resulting statement handle.
+ * \param obj The ODBC object
+ * \param prepare_cb A function callback, which, when called, should return a statement handle prepared, with any necessary parameters or result columns bound.
+ * \param data A parameter to be passed to the prepare_cb parameter function, indicating which statement handle is to be prepared.
+ * \retval a statement handle
+ * \retval NULL on error
+ */
+SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data);
+
+#endif /* _ASTERISK_RES_ODBC_H */
diff --git a/trunk/include/asterisk/rtp.h b/trunk/include/asterisk/rtp.h
new file mode 100644
index 000000000..003ff268f
--- /dev/null
+++ b/trunk/include/asterisk/rtp.h
@@ -0,0 +1,293 @@
+/*
+ * 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 rtp.h
+ * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal.
+ *
+ * RTP is defined in RFC 3550.
+ */
+
+#ifndef _ASTERISK_RTP_H
+#define _ASTERISK_RTP_H
+
+#include "asterisk/network.h"
+
+#include "asterisk/frame.h"
+#include "asterisk/io.h"
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/linkedlists.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/* Codes for RTP-specific data - not defined by our AST_FORMAT codes */
+/*! DTMF (RFC2833) */
+#define AST_RTP_DTMF (1 << 0)
+/*! 'Comfort Noise' (RFC3389) */
+#define AST_RTP_CN (1 << 1)
+/*! DTMF (Cisco Proprietary) */
+#define AST_RTP_CISCO_DTMF (1 << 2)
+/*! Maximum RTP-specific code */
+#define AST_RTP_MAX AST_RTP_CISCO_DTMF
+
+/*! Maxmum number of payload defintions for a RTP session */
+#define MAX_RTP_PT 256
+
+#define FLAG_3389_WARNING (1 << 0)
+
+enum ast_rtp_options {
+ AST_RTP_OPT_G726_NONSTANDARD = (1 << 0),
+};
+
+enum ast_rtp_get_result {
+ /*! Failed to find the RTP structure */
+ AST_RTP_GET_FAILED = 0,
+ /*! RTP structure exists but true native bridge can not occur so try partial */
+ AST_RTP_TRY_PARTIAL,
+ /*! RTP structure exists and native bridge can occur */
+ AST_RTP_TRY_NATIVE,
+};
+
+struct ast_rtp;
+
+/*! \brief This is the structure that binds a channel (SIP/Jingle/H.323) to the RTP subsystem
+*/
+struct ast_rtp_protocol {
+ /*! Get RTP struct, or NULL if unwilling to transfer */
+ enum ast_rtp_get_result (* const get_rtp_info)(struct ast_channel *chan, struct ast_rtp **rtp);
+ /*! Get RTP struct, or NULL if unwilling to transfer */
+ enum ast_rtp_get_result (* const get_vrtp_info)(struct ast_channel *chan, struct ast_rtp **rtp);
+ /*! Get RTP struct, or NULL if unwilling to transfer */
+ enum ast_rtp_get_result (* const get_trtp_info)(struct ast_channel *chan, struct ast_rtp **rtp);
+ /*! Set RTP peer */
+ int (* const set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer, struct ast_rtp *tpeer, int codecs, int nat_active);
+ int (* const get_codec)(struct ast_channel *chan);
+ const char * const type;
+ AST_LIST_ENTRY(ast_rtp_protocol) list;
+};
+
+/*! \brief RTCP quality report storage */
+struct ast_rtp_quality {
+ unsigned int local_ssrc; /*!< Our SSRC */
+ unsigned int local_lostpackets; /*!< Our lost packets */
+ double local_jitter; /*!< Our calculated jitter */
+ unsigned int local_count; /*!< Number of received packets */
+ unsigned int remote_ssrc; /*!< Their SSRC */
+ unsigned int remote_lostpackets; /*!< Their lost packets */
+ double remote_jitter; /*!< Their reported jitter */
+ unsigned int remote_count; /*!< Number of transmitted packets */
+ double rtt; /*!< Round trip time */
+};
+
+/*! RTP callback structure */
+typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data);
+
+/*!
+ * \brief Get the amount of space required to hold an RTP session
+ * \return number of bytes required
+ */
+size_t ast_rtp_alloc_size(void);
+
+/*!
+ * \brief Initializate a RTP session.
+ *
+ * \param sched
+ * \param io
+ * \param rtcpenable
+ * \param callbackmode
+ * \returns A representation (structure) of an RTP session.
+ */
+struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode);
+
+/*!
+ * \brief Initializate a RTP session using an in_addr structure.
+ *
+ * This fuction gets called by ast_rtp_new().
+ *
+ * \param sched
+ * \param io
+ * \param rtcpenable
+ * \param callbackmode
+ * \param in
+ * \returns A representation (structure) of an RTP session.
+ */
+struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr in);
+
+void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
+
+/* Copies from rtp to them and returns 1 if there was a change or 0 if it was already the same */
+int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
+
+void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us);
+
+struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp);
+
+/*! Destroy RTP session */
+void ast_rtp_destroy(struct ast_rtp *rtp);
+
+void ast_rtp_reset(struct ast_rtp *rtp);
+
+/*! Stop RTP session, do not destroy structure */
+void ast_rtp_stop(struct ast_rtp *rtp);
+
+void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback);
+
+void ast_rtp_set_data(struct ast_rtp *rtp, void *data);
+
+int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *f);
+
+struct ast_frame *ast_rtp_read(struct ast_rtp *rtp);
+
+struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp);
+
+int ast_rtp_fd(struct ast_rtp *rtp);
+
+int ast_rtcp_fd(struct ast_rtp *rtp);
+
+int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit);
+
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit);
+
+int ast_rtp_sendcng(struct ast_rtp *rtp, int level);
+
+int ast_rtp_setqos(struct ast_rtp *rtp, int tos, int cos, char *desc);
+
+/*! \brief Setting RTP payload types from lines in a SDP description: */
+void ast_rtp_pt_clear(struct ast_rtp* rtp);
+/*! \brief Set payload types to defaults */
+void ast_rtp_pt_default(struct ast_rtp* rtp);
+
+/*! \brief Copy payload types between RTP structures */
+void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src);
+
+/*! \brief Activate payload type */
+void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt);
+
+/*! \brief clear payload type */
+void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt);
+
+/*! \brief Initiate payload type to a known MIME media type for a codec */
+int ast_rtp_set_rtpmap_type(struct ast_rtp* rtp, int pt,
+ char *mimeType, char *mimeSubtype,
+ enum ast_rtp_options options);
+
+/*! \brief Mapping between RTP payload format codes and Asterisk codes: */
+struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt);
+int ast_rtp_lookup_code(struct ast_rtp* rtp, int isAstFormat, int code);
+
+void ast_rtp_get_current_formats(struct ast_rtp* rtp,
+ int* astFormats, int* nonAstFormats);
+
+/*! \brief Mapping an Asterisk code into a MIME subtype (string): */
+const char *ast_rtp_lookup_mime_subtype(int isAstFormat, int code,
+ enum ast_rtp_options options);
+
+/*! \brief Build a string of MIME subtype names from a capability list */
+char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability,
+ const int isAstFormat, enum ast_rtp_options options);
+
+void ast_rtp_setnat(struct ast_rtp *rtp, int nat);
+
+int ast_rtp_getnat(struct ast_rtp *rtp);
+
+/*! \brief Indicate whether this RTP session is carrying DTMF or not */
+void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf);
+
+/*! \brief Compensate for devices that send RFC2833 packets all at once */
+void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate);
+
+/*! \brief Enable STUN capability */
+void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable);
+
+/*! \brief Generic STUN request
+ * send a generic stun request to the server specified.
+ * \param s the socket used to send the request
+ * \param dst the address of the STUN server
+ * \param username if non null, add the username in the request
+ * \param answer if non null, the function waits for a response and
+ * puts here the externally visible address.
+ * \return 0 on success, other values on error.
+ * The interface it may change in the future.
+ */
+int ast_stun_request(int s, struct sockaddr_in *dst,
+ const char *username, struct sockaddr_in *answer);
+
+/*! \brief Send STUN request for an RTP socket
+ * Deprecated, this is just a wrapper for ast_rtp_stun_request()
+ */
+void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username);
+
+/*! \brief The RTP bridge.
+ \arg \ref AstRTPbridge
+*/
+int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
+
+/*! \brief Register an RTP channel client */
+int ast_rtp_proto_register(struct ast_rtp_protocol *proto);
+
+/*! \brief Unregister an RTP channel client */
+void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto);
+
+int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media);
+
+/*! \brief If possible, create an early bridge directly between the devices without
+ having to send a re-invite later */
+int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1);
+
+/*! \brief Return RTCP quality string */
+char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual);
+
+/*! \brief Send an H.261 fast update request. Some devices need this rather than the XML message in SIP */
+int ast_rtcp_send_h261fur(void *data);
+
+void ast_rtp_init(void); /*! Initialize RTP subsystem */
+int ast_rtp_reload(void); /*! reload rtp configuration */
+void ast_rtp_new_init(struct ast_rtp *rtp);
+
+/*! Set codec preference */
+int ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs);
+
+/*! Get codec preference */
+struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp);
+
+/*! get format from predefined dynamic payload format */
+int ast_rtp_codec_getformat(int pt);
+
+/*! \brief Set rtp timeout */
+void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout);
+/*! \brief Set rtp hold timeout */
+void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout);
+/*! \brief set RTP keepalive interval */
+void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period);
+/*! \brief Get RTP keepalive interval */
+int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp);
+/*! \brief Get rtp hold timeout */
+int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp);
+/*! \brief Get rtp timeout */
+int ast_rtp_get_rtptimeout(struct ast_rtp *rtp);
+/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */
+void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_RTP_H */
diff --git a/trunk/include/asterisk/say.h b/trunk/include/asterisk/say.h
new file mode 100644
index 000000000..37098402c
--- /dev/null
+++ b/trunk/include/asterisk/say.h
@@ -0,0 +1,170 @@
+/*
+ * 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 Say numbers and dates (maybe words one day too)
+ */
+
+#ifndef _ASTERISK_SAY_H
+#define _ASTERISK_SAY_H
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+
+#include <time.h>
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief
+ * The basic ast_say_* functions are implemented as function pointers,
+ * initialized to the function say_stub() which simply returns an error.
+ * Other interfaces, declared here as regular functions, are simply
+ * wrappers around the basic functions.
+ *
+ * An implementation of the basic ast_say functions (e.g. from say.c or from
+ * a dynamically loaded module) will just have to reassign the pointers
+ * to the relevant functions to override the previous implementation.
+ *
+ * \todo XXX
+ * As the conversion from the old implementation of say.c to the new
+ * implementation will be completed, and the API suitably reworked by
+ * removing redundant functions and/or arguments, this mechanism may be
+ * reverted back to pure static functions, if needed.
+ */
+#if defined(SAY_STUBS)
+/* provide declarations for the *say*() functions
+ * and initialize them to the stub function
+ */
+static int say_stub(struct ast_channel *chan, ...)
+{
+ ast_log(LOG_WARNING, "no implementation for the say() functions\n");
+ return -1;
+};
+
+#undef SAY_STUBS
+#define SAY_INIT(x) = (typeof (x))say_stub
+#define SAY_EXTERN
+#else
+#define SAY_INIT(x)
+#define SAY_EXTERN extern
+#endif
+
+/*
+ * \brief says a number
+ * \param chan channel to say them number on
+ * \param num number to say on the channel
+ * \param ints which dtmf to interrupt on
+ * \param lang language to speak the number
+ * \param options set to 'f' for female, 'm' for male, 'c' for commune, 'n' for neuter, 'p' for plural
+ * Vocally says a number on a given channel
+ * \retval 0 on success
+ * \retval DTMF digit on interrupt
+ * \retval -1 on failure
+ */
+int ast_say_number(struct ast_channel *chan, int num,
+ const char *ints, const char *lang, const char *options);
+
+/* Same as above with audiofd for received audio and returns 1 on ctrlfd being readable */
+SAY_EXTERN int (* ast_say_number_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_number_full);
+
+/*
+ * \brief says an enumeration
+ * \param chan channel to say them enumeration on
+ * \param num number to say on the channel
+ * \param ints which dtmf to interrupt on
+ * \param lang language to speak the enumeration
+ * \param options set to 'f' for female, 'm' for male, 'c' for commune, 'n' for neuter, 'p' for plural
+ * Vocally says a enumeration on a given channel (first, sencond, third, forth, thirtyfirst, hundredth, ....)
+ * especially useful for dates and messages. says 'last' if num equals to INT_MAX
+ * \retval 0 on success
+ * \retval DTMF digit on interrupt
+ * \retval -1 on failure
+ */
+int ast_say_enumeration(struct ast_channel *chan, int num,
+ const char *ints, const char *lang, const char *options);
+
+SAY_EXTERN int (* ast_say_enumeration_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_enumeration_full);
+
+/*
+ * \brief says digits
+ * \param chan channel to act upon
+ * \param num number to speak
+ * \param ints which dtmf to interrupt on
+ * \param lang language to speak
+ * Vocally says digits of a given number
+ * \retval 0 on success
+ * \retval DTMF if interrupted
+ * \retval -1 on failure
+ */
+int ast_say_digits(struct ast_channel *chan, int num,
+ const char *ints, const char *lang);
+
+int ast_say_digits_full(struct ast_channel *chan, int num,
+ const char *ints, const char *lang, int audiofd, int ctrlfd);
+
+/*
+ * \brief says digits of a string
+ * \param chan channel to act upon
+ * \param num string to speak
+ * \param ints which dtmf to interrupt on
+ * \param lang language to speak in
+ * Vocally says the digits of a given string
+ * \retval 0 on succes
+ * \retval DTMF if interrupted
+ * \retval -1 on failure
+ */
+int ast_say_digit_str(struct ast_channel *chan, const char *num,
+ const char *ints, const char *lang);
+
+SAY_EXTERN int (* ast_say_digit_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_digit_str_full);
+
+/*
+ * the generic 'say' routine, with the first chars in the string
+ * defining the format to use
+ */
+SAY_EXTERN int (* ast_say_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_full);
+
+/*
+ * other function to pronounce character and phonetic strings
+ */
+int ast_say_character_str(struct ast_channel *chan, const char *num,
+ const char *ints, const char *lang);
+
+SAY_EXTERN int (* ast_say_character_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_character_str_full);
+
+int ast_say_phonetic_str(struct ast_channel *chan, const char *num,
+ const char *ints, const char *lang);
+
+SAY_EXTERN int (* ast_say_phonetic_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_phonetic_str_full);
+
+SAY_EXTERN int (* ast_say_datetime)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_datetime);
+SAY_EXTERN int (* ast_say_time)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_time);
+
+SAY_EXTERN int (* ast_say_date)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_date);
+
+SAY_EXTERN int (* ast_say_datetime_from_now)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_datetime_from_now);
+
+SAY_EXTERN int (* ast_say_date_with_format)(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *timezone) SAY_INIT(ast_say_date_with_format);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_SAY_H */
diff --git a/trunk/include/asterisk/sched.h b/trunk/include/asterisk/sched.h
new file mode 100644
index 000000000..f074518c5
--- /dev/null
+++ b/trunk/include/asterisk/sched.h
@@ -0,0 +1,176 @@
+/*
+ * 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 Scheduler Routines (derived from cheops)
+ */
+
+#ifndef _ASTERISK_SCHED_H
+#define _ASTERISK_SCHED_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief Max num of schedule structs
+ * \note The max number of schedule structs to keep around
+ * for use. Undefine to disable schedule structure
+ * caching. (Only disable this on very low memory
+ * machines)
+ */
+#define SCHED_MAX_CACHE 128
+
+struct sched_context;
+
+/*! \brief New schedule context
+ * \note Create a scheduling context
+ * \return Returns a malloc'd sched_context structure, NULL on failure
+ */
+struct sched_context *sched_context_create(void);
+
+/*! \brief destroys a schedule context
+ * Destroys (free's) the given sched_context structure
+ * \param c Context to free
+ * \return Returns 0 on success, -1 on failure
+ */
+void sched_context_destroy(struct sched_context *c);
+
+/*! \brief callback for a cheops scheduler
+ * A cheops scheduler callback takes a pointer with callback data and
+ * \return returns a 0 if it should not be run again, or non-zero if it should be
+ * rescheduled to run again
+ */
+typedef int (*ast_sched_cb)(const void *data);
+#define AST_SCHED_CB(a) ((ast_sched_cb)(a))
+
+/*! \brief Adds a scheduled event
+ * Schedule an event to take place at some point in the future. callback
+ * will be called with data as the argument, when milliseconds into the
+ * future (approximately)
+ * If callback returns 0, no further events will be re-scheduled
+ * \param con Scheduler context to add
+ * \param when how many milliseconds to wait for event to occur
+ * \param callback function to call when the amount of time expires
+ * \param data data to pass to the callback
+ * \return Returns a schedule item ID on success, -1 on failure
+ */
+int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data);
+
+/*!
+ * \brief replace a scheduler entry
+ *
+ * This deletes the scheduler entry for old_id if it exists, and then
+ * calls ast_sched_add to create a new entry. A negative old_id will
+ * be ignored.
+ *
+ * \retval -1 failure
+ * \retval otherwise, returns scheduled item ID
+ */
+int ast_sched_replace(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data);
+
+/*!Adds a scheduled event with rescheduling support
+ * \param con Scheduler context to add
+ * \param when how many milliseconds to wait for event to occur
+ * \param callback function to call when the amount of time expires
+ * \param data data to pass to the callback
+ * \param variable If true, the result value of callback function will be
+ * used for rescheduling
+ * Schedule an event to take place at some point in the future. Callback
+ * will be called with data as the argument, when milliseconds into the
+ * future (approximately)
+ * If callback returns 0, no further events will be re-scheduled
+ * \return Returns a schedule item ID on success, -1 on failure
+ */
+int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable);
+
+/*!
+ * \brief replace a scheduler entry
+ *
+ * This deletes the scheduler entry for old_id if it exists, and then
+ * calls ast_sched_add to create a new entry. A negative old_id will
+ * be ignored.
+ *
+ * \retval -1 failure
+ * \retval otherwise, returns scheduled item ID
+ */
+int ast_sched_replace_variable(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable);
+
+/*! \brief Deletes a scheduled event
+ * Remove this event from being run. A procedure should not remove its
+ * own event, but return 0 instead.
+ * \param con scheduling context to delete item from
+ * \param id ID of the scheduled item to delete
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_sched_del(struct sched_context *con, int id);
+
+/*! \brief Determines number of seconds until the next outstanding event to take place
+ * Determine the number of seconds until the next outstanding event
+ * should take place, and return the number of milliseconds until
+ * it needs to be run. This value is perfect for passing to the poll
+ * call.
+ * \param con context to act upon
+ * \return Returns "-1" if there is nothing there are no scheduled events
+ * (and thus the poll should not timeout)
+ */
+int ast_sched_wait(struct sched_context *con);
+
+/*! \brief Runs the queue
+ * \param con Scheduling context to run
+ * Run the queue, executing all callbacks which need to be performed
+ * at this time.
+ * \param con context to act upon
+ * \return Returns the number of events processed.
+ */
+int ast_sched_runq(struct sched_context *con);
+
+/*! \brief Dumps the scheduler contents
+ * Debugging: Dump the contents of the scheduler to stderr
+ * \param con Context to dump
+ */
+void ast_sched_dump(const struct sched_context *con);
+
+/*! \brief Returns the number of seconds before an event takes place
+ * \param con Context to use
+ * \param id Id to dump
+ */
+long ast_sched_when(struct sched_context *con,int id);
+
+/*!
+ * \brief Convenience macro for objects and reference (add)
+ *
+ */
+#define ast_sched_add_object(obj,con,when,callback) ast_sched_add((con),(when),(callback), ASTOBJ_REF((obj)))
+
+/*!
+ * \brief Convenience macro for objects and reference (del)
+ *
+ */
+#define ast_sched_del_object(obj,destructor,con,id) do { \
+ if ((id) > -1) { \
+ ast_sched_del((con),(id)); \
+ (id) = -1; \
+ ASTOBJ_UNREF((obj),(destructor)); \
+ } \
+} while(0)
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_SCHED_H */
diff --git a/trunk/include/asterisk/sha1.h b/trunk/include/asterisk/sha1.h
new file mode 100644
index 000000000..d530c3206
--- /dev/null
+++ b/trunk/include/asterisk/sha1.h
@@ -0,0 +1,73 @@
+/*
+ * sha1.h
+ *
+ * Description:
+ * This is the header file for code which implements the Secure
+ * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ * April 17, 1995.
+ *
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the names
+ * used in the publication.
+ *
+ * Please read the file sha1.c for more information.
+ *
+ */
+
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+/*
+ * We assume that the standard asterisk headers have been included before this one.
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ * name meaning
+ * uint32_t unsigned 32 bit integer
+ * uint8_t unsigned 8 bit integer (i.e., unsigned char)
+ *
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+enum
+{
+ shaSuccess = 0,
+ shaNull, /* Null pointer parameter */
+ shaInputTooLong, /* input data too long */
+ shaStateError /* called Input after Result */
+};
+#endif
+#define SHA1HashSize 20
+
+/*!
+ * \brief This structure will hold context information for the SHA-1 hashing operation
+*/
+typedef struct SHA1Context
+{
+ uint32_t Intermediate_Hash[SHA1HashSize/4]; /*! Message Digest */
+
+ uint32_t Length_Low; /*!< Message length in bits */
+ uint32_t Length_High; /*!< Message length in bits */
+
+ /* Index into message block array */
+ uint32_t Message_Block_Index; /*!< 8 bits actually suffice */
+ uint8_t Message_Block[64]; /*!< 512-bit message blocks */
+
+ int Computed; /*!< Is the digest computed? */
+ int Corrupted; /*!< Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ * Function Prototypes
+ */
+
+
+int SHA1Reset( SHA1Context *);
+int SHA1Input( SHA1Context *,
+ const uint8_t *,
+ unsigned int);
+int SHA1Result( SHA1Context *,
+ uint8_t Message_Digest[SHA1HashSize]);
+
+#endif
diff --git a/trunk/include/asterisk/slinfactory.h b/trunk/include/asterisk/slinfactory.h
new file mode 100644
index 000000000..603c648c7
--- /dev/null
+++ b/trunk/include/asterisk/slinfactory.h
@@ -0,0 +1,65 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Anthony Minessale II
+ *
+ * Anthony Minessale <anthmct@yahoo.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 A machine to gather up arbitrary frames and convert them
+ * to raw slinear on demand.
+ */
+
+#ifndef _ASTERISK_SLINFACTORY_H
+#define _ASTERISK_SLINFACTORY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct ast_slinfactory {
+ AST_LIST_HEAD_NOLOCK(, ast_frame) queue;
+ struct ast_trans_pvt *trans;
+ short hold[1280];
+ short *offset;
+ size_t holdlen; /*!< in samples */
+ unsigned int size; /*!< in samples */
+ unsigned int format;
+};
+
+void ast_slinfactory_init(struct ast_slinfactory *sf);
+
+/*!
+ * \brief Destroy the contents of a slinfactory
+ *
+ * \arg sf the slinfactory that is no longer needed
+ *
+ * This function will free any memory allocated for the contents of the
+ * slinfactory. It does not free the slinfactory itself. If the sf is
+ * malloc'd, then it must be explicitly free'd after calling this function.
+ *
+ * \return nothing
+ */
+void ast_slinfactory_destroy(struct ast_slinfactory *sf);
+
+int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f);
+int ast_slinfactory_read(struct ast_slinfactory *sf, short *buf, size_t samples);
+unsigned int ast_slinfactory_available(const struct ast_slinfactory *sf);
+void ast_slinfactory_flush(struct ast_slinfactory *sf);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_SLINFACTORY_H */
diff --git a/trunk/include/asterisk/smdi.h b/trunk/include/asterisk/smdi.h
new file mode 100644
index 000000000..6260d9d2d
--- /dev/null
+++ b/trunk/include/asterisk/smdi.h
@@ -0,0 +1,127 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (C) 2005-2006, Digium, Inc.
+ *
+ * Matthew A. Nicholson <mnicholson@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 SMDI support for Asterisk.
+ * \author Matthew A. Nicholson <mnicholson@digium.com>
+ */
+
+
+/* C is simply a ego booster for those who want to do objects the hard way. */
+
+
+#ifndef ASTERISK_SMDI_H
+#define ASTERISK_SMDI_H
+
+#include <termios.h>
+#include <time.h>
+
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/astobj.h"
+
+#define SMDI_MESG_DESK_NUM_LEN 3
+#define SMDI_MESG_DESK_TERM_LEN 4
+#define SMDI_MWI_FAIL_CAUSE_LEN 3
+#define SMDI_MAX_STATION_NUM_LEN 10
+#define SMDI_MAX_FILENAME_LEN 256
+
+/*!
+ * \brief An SMDI message waiting indicator message.
+ *
+ * The ast_smdi_mwi_message structure contains the parsed out parts of an smdi
+ * message. Each ast_smdi_interface structure has a message queue consisting
+ * ast_smdi_mwi_message structures.
+ */
+struct ast_smdi_mwi_message {
+ ASTOBJ_COMPONENTS(struct ast_smdi_mwi_message);
+ char fwd_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* forwarding station number */
+ char cause[SMDI_MWI_FAIL_CAUSE_LEN + 1]; /* the type of failure */
+ struct timeval timestamp; /* a timestamp for the message */
+};
+
+/*!
+ * \brief An SMDI message desk message.
+ *
+ * The ast_smdi_md_message structure contains the parsed out parts of an smdi
+ * message. Each ast_smdi_interface structure has a message queue consisting
+ * ast_smdi_md_message structures.
+ */
+struct ast_smdi_md_message {
+ ASTOBJ_COMPONENTS(struct ast_smdi_md_message);
+ char mesg_desk_num[SMDI_MESG_DESK_NUM_LEN + 1]; /* message desk number */
+ char mesg_desk_term[SMDI_MESG_DESK_TERM_LEN + 1]; /* message desk terminal */
+ char fwd_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* forwarding station number */
+ char calling_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* calling station number */
+ char type; /* the type of the call */
+ struct timeval timestamp; /* a timestamp for the message */
+};
+
+/*! \brief SMDI message desk message queue. */
+struct ast_smdi_md_queue {
+ ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
+};
+
+/*! \brief SMDI message waiting indicator message queue. */
+struct ast_smdi_mwi_queue {
+ ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
+};
+
+/*!
+ * \brief SMDI interface structure.
+ *
+ * The ast_smdi_interface structure holds information on a serial port that
+ * should be monitored for SMDI activity. The structure contains a message
+ * queue of messages that have been received on the interface.
+ */
+struct ast_smdi_interface {
+ ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
+ struct ast_smdi_md_queue md_q;
+ struct ast_smdi_mwi_queue mwi_q;
+ FILE *file;
+ int fd;
+ pthread_t thread;
+ struct termios mode;
+ int msdstrip;
+ long msg_expiry;
+};
+
+
+/* MD message queue functions */
+struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface);
+struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout);
+void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
+
+/* MWI message queue functions */
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface);
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout);
+void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
+
+struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name);
+
+/* MWI functions */
+int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox);
+int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox);
+
+void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg);
+void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg);
+
+void ast_smdi_interface_destroy(struct ast_smdi_interface *iface);
+
+#endif /* !ASTERISK_SMDI_H */
diff --git a/trunk/include/asterisk/speech.h b/trunk/include/asterisk/speech.h
new file mode 100644
index 000000000..54e9c69ac
--- /dev/null
+++ b/trunk/include/asterisk/speech.h
@@ -0,0 +1,156 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Generic Speech Recognition API
+ */
+
+#ifndef _ASTERISK_SPEECH_H
+#define _ASTERISK_SPEECH_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/* Speech structure flags */
+enum ast_speech_flags {
+ AST_SPEECH_QUIET = (1 << 0), /* Quiet down output... they are talking */
+ AST_SPEECH_SPOKE = (1 << 1), /* Speaker spoke! */
+ AST_SPEECH_HAVE_RESULTS = (1 << 2), /* Results are present */
+};
+
+/* Speech structure states - in order of expected change */
+enum ast_speech_states {
+ AST_SPEECH_STATE_NOT_READY = 0, /* Not ready to accept audio */
+ AST_SPEECH_STATE_READY, /* Accepting audio */
+ AST_SPEECH_STATE_WAIT, /* Wait for results to become available */
+ AST_SPEECH_STATE_DONE, /* Processing is all done */
+};
+
+enum ast_speech_results_type {
+ AST_SPEECH_RESULTS_TYPE_NORMAL = 0,
+ AST_SPEECH_RESULTS_TYPE_NBEST,
+};
+
+/* Speech structure */
+struct ast_speech {
+ /*! Structure lock */
+ ast_mutex_t lock;
+ /*! Set flags */
+ unsigned int flags;
+ /*! Processing sound (used when engine is processing audio and getting results) */
+ char *processing_sound;
+ /*! Current state of structure */
+ int state;
+ /*! Expected write format */
+ int format;
+ /*! Data for speech engine */
+ void *data;
+ /*! Cached results */
+ struct ast_speech_result *results;
+ /*! Type of results we want */
+ enum ast_speech_results_type results_type;
+ /*! Pointer to the engine used by this speech structure */
+ struct ast_speech_engine *engine;
+};
+
+/* Speech recognition engine structure */
+struct ast_speech_engine {
+ /*! Name of speech engine */
+ char *name;
+ /*! Set up the speech structure within the engine */
+ int (*create)(struct ast_speech *speech, int format);
+ /*! Destroy any data set on the speech structure by the engine */
+ int (*destroy)(struct ast_speech *speech);
+ /*! Load a local grammar on the speech structure */
+ int (*load)(struct ast_speech *speech, char *grammar_name, char *grammar);
+ /*! Unload a local grammar */
+ int (*unload)(struct ast_speech *speech, char *grammar_name);
+ /*! Activate a loaded grammar */
+ int (*activate)(struct ast_speech *speech, char *grammar_name);
+ /*! Deactivate a loaded grammar */
+ int (*deactivate)(struct ast_speech *speech, char *grammar_name);
+ /*! Write audio to the speech engine */
+ int (*write)(struct ast_speech *speech, void *data, int len);
+ /*! Signal DTMF was received */
+ int (*dtmf)(struct ast_speech *speech, const char *dtmf);
+ /*! Prepare engine to accept audio */
+ int (*start)(struct ast_speech *speech);
+ /*! Change an engine specific setting */
+ int (*change)(struct ast_speech *speech, char *name, const char *value);
+ /*! Change the type of results we want back */
+ int (*change_results_type)(struct ast_speech *speech, enum ast_speech_results_type results_type);
+ /*! Try to get results */
+ struct ast_speech_result *(*get)(struct ast_speech *speech);
+ /*! Accepted formats by the engine */
+ int formats;
+ AST_LIST_ENTRY(ast_speech_engine) list;
+};
+
+/* Result structure */
+struct ast_speech_result {
+ /*! Recognized text */
+ char *text;
+ /*! Result score */
+ int score;
+ /*! NBest Alternative number if in NBest results type */
+ int nbest_num;
+ /*! Matched grammar */
+ char *grammar;
+ /*! List information */
+ AST_LIST_ENTRY(ast_speech_result) list;
+};
+
+/*! \brief Activate a grammar on a speech structure */
+int ast_speech_grammar_activate(struct ast_speech *speech, char *grammar_name);
+/*! \brief Deactivate a grammar on a speech structure */
+int ast_speech_grammar_deactivate(struct ast_speech *speech, char *grammar_name);
+/*! \brief Load a grammar on a speech structure (not globally) */
+int ast_speech_grammar_load(struct ast_speech *speech, char *grammar_name, char *grammar);
+/*! \brief Unload a grammar */
+int ast_speech_grammar_unload(struct ast_speech *speech, char *grammar_name);
+/*! \brief Get speech recognition results */
+struct ast_speech_result *ast_speech_results_get(struct ast_speech *speech);
+/*! \brief Free a set of results */
+int ast_speech_results_free(struct ast_speech_result *result);
+/*! \brief Indicate to the speech engine that audio is now going to start being written */
+void ast_speech_start(struct ast_speech *speech);
+/*! \brief Create a new speech structure */
+struct ast_speech *ast_speech_new(char *engine_name, int formats);
+/*! \brief Destroy a speech structure */
+int ast_speech_destroy(struct ast_speech *speech);
+/*! \brief Write audio to the speech engine */
+int ast_speech_write(struct ast_speech *speech, void *data, int len);
+/*! \brief Signal to the engine that DTMF was received */
+int ast_speech_dtmf(struct ast_speech *speech, const char *dtmf);
+/*! \brief Change an engine specific attribute */
+int ast_speech_change(struct ast_speech *speech, char *name, const char *value);
+/*! \brief Change the type of results we want */
+int ast_speech_change_results_type(struct ast_speech *speech, enum ast_speech_results_type results_type);
+/*! \brief Change state of a speech structure */
+int ast_speech_change_state(struct ast_speech *speech, int state);
+/*! \brief Register a speech recognition engine */
+int ast_speech_register(struct ast_speech_engine *engine);
+/*! \brief Unregister a speech recognition engine */
+int ast_speech_unregister(char *engine_name);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_SPEECH_H */
diff --git a/trunk/include/asterisk/srv.h b/trunk/include/asterisk/srv.h
new file mode 100644
index 000000000..567b40844
--- /dev/null
+++ b/trunk/include/asterisk/srv.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*
+ * DNS SRV record support
+ */
+
+#ifndef _ASTERISK_SRV_H
+#define _ASTERISK_SRV_H
+
+/*!
+ \file srv.h
+ \brief Support for DNS SRV records, used in to locate SIP services.
+ \note Note: This SRV record support will respect the priority and
+ weight elements of the records that are returned, but there are
+ no provisions for retrying or failover between records.
+*/
+
+/*! Lookup entry in SRV records Returns 1 if found, 0 if not found, -1 on hangup
+ Only do SRV record lookup if you get a domain without a port. If you get a port #, it's a DNS host name.
+*/
+/*! \param chan Ast channel
+ \param host host name (return value)
+ \param hostlen Length of string "host"
+ \param port Port number (return value)
+ \param service Service tag for SRV lookup (like "_sip._udp" or "_stun._udp"
+*/
+extern int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service);
+
+#endif /* _ASTERISK_SRV_H */
diff --git a/trunk/include/asterisk/stringfields.h b/trunk/include/asterisk/stringfields.h
new file mode 100644
index 000000000..65f1b53f9
--- /dev/null
+++ b/trunk/include/asterisk/stringfields.h
@@ -0,0 +1,307 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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 String fields in structures
+
+ This file contains objects and macros used to manage string
+ fields in structures without requiring them to be allocated
+ as fixed-size buffers or requiring individual allocations for
+ for each field.
+
+ Using this functionality is quite simple. An example structure
+ with three fields is defined like this:
+
+ \code
+ struct sample_fields {
+ int x1;
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(foo);
+ AST_STRING_FIELD(bar);
+ AST_STRING_FIELD(blah);
+ );
+ long x2;
+ };
+ \endcode
+
+ When an instance of this structure is allocated (either statically or
+ dynamically), the fields and the pool of storage for them must be
+ initialized:
+
+ \code
+ struct sample_fields *x;
+
+ x = ast_calloc(1, sizeof(*x));
+ if (x == NULL || ast_string_field_init(x, 252)) {
+ if (x)
+ ast_free(x);
+ x = NULL;
+ ... handle error
+ }
+ \endcode
+
+ Fields will default to pointing to an empty string, and will revert to
+ that when ast_string_field_set() is called with a NULL argument.
+ A string field will \b never contain NULL (this feature is not used
+ in this code, but comes from external requirements).
+
+ ast_string_field_init(x, 0) will reset fields to the
+ initial value while keeping the pool allocated.
+
+ Reading the fields is much like using 'const char * const' fields in the
+ structure: you cannot write to the field or to the memory it points to
+ (XXX perhaps the latter is too much of a restriction since values
+ are not shared).
+
+ Writing to the fields must be done using the wrapper macros listed below;
+ and assignments are always by value (i.e. strings are copied):
+ * ast_string_field_set() stores a simple value;
+ * ast_string_field_build() builds the string using a printf-style;
+ * ast_string_field_build_va() is the varargs version of the above (for
+ portability reasons it uses two vararg);
+ * variants of these function allow passing a pointer to the field
+ as an argument.
+ \code
+ ast_string_field_set(x, foo, "infinite loop");
+ ast_string_field_set(x, foo, NULL); // set to an empty string
+ ast_string_field_ptr_set(x, &x->bar, "right way");
+
+ ast_string_field_build(x, blah, "%d %s", zipcode, city);
+ ast_string_field_ptr_build(x, &x->blah, "%d %s", zipcode, city);
+
+ ast_string_field_build_va(x, bar, fmt, args1, args2)
+ ast_string_field_ptr_build_va(x, &x->bar, fmt, args1, args2)
+ \endcode
+
+ When the structure instance is no longer needed, the fields
+ and their storage pool must be freed:
+
+ \code
+ ast_string_field_free_memory(x);
+ ast_free(x);
+ \endcode
+
+ This completes the API description.
+*/
+
+#ifndef _ASTERISK_STRINGFIELDS_H
+#define _ASTERISK_STRINGFIELDS_H
+
+#include "asterisk/inline_api.h"
+
+/*!
+ \internal
+ \brief An opaque type for managed string fields in structures
+
+ Don't declare instances of this type directly; use the AST_STRING_FIELD()
+ macro instead.
+*/
+typedef const char * ast_string_field;
+
+/*!
+ \internal
+ \brief A constant empty string used for fields that have no other value
+*/
+extern const char __ast_string_field_empty[];
+
+/*!
+ \internal
+ \brief Structure used to hold a pool of space for string fields
+*/
+struct ast_string_field_pool {
+ struct ast_string_field_pool *prev; /*!< pointer to the previous pool, if any */
+ char base[0]; /*!< storage space for the fields */
+};
+
+/*!
+ \internal
+ \brief Structure used to manage the storage for a set of string fields.
+ Because of the way pools are managed, we can only allocate from the topmost
+ pool, so the numbers here reflect just that.
+*/
+struct ast_string_field_mgr {
+ size_t size; /*!< the total size of the current pool */
+ size_t used; /*!< the space used in the current pool */
+};
+
+/*!
+ \internal
+ \brief Allocate space for a field
+ \param mgr Pointer to the pool manager structure
+ \param needed Amount of space needed for this field
+ \param fields Pointer to the first entry of the field array
+ \return NULL on failure, an address for the field on success.
+
+ This function will allocate the requested amount of space from
+ the field pool. If the requested amount of space is not available,
+ an additional pool will be allocated.
+*/
+ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t needed);
+
+/*!
+ \internal
+ \brief Set a field to a complex (built) value
+ \param mgr Pointer to the pool manager structure
+ \param fields Pointer to the first entry of the field array
+ \param ptr Pointer to a field within the structure
+ \param format printf-style format string
+ \return nothing
+*/
+void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head,
+ const ast_string_field *ptr, const char *format, ...);
+
+/*!
+ \internal
+ \brief Set a field to a complex (built) value
+ \param mgr Pointer to the pool manager structure
+ \param fields Pointer to the first entry of the field array
+ \param ptr Pointer to a field within the structure
+ \param format printf-style format string
+ \param args va_list of the args for the format_string
+ \param args_again a copy of the first va_list for the sake of bsd not having a copy routine
+ \return nothing
+*/
+void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head,
+ const ast_string_field *ptr, const char *format, va_list a1, va_list a2);
+
+/*!
+ \brief Declare a string field
+ \param name The field name
+*/
+#define AST_STRING_FIELD(name) const ast_string_field name
+
+/*!
+ \brief Declare the fields needed in a structure
+ \param field_list The list of fields to declare, using AST_STRING_FIELD() for each one.
+ Internally, string fields are stored as a pointer to the head of the pool,
+ followed by individual string fields, and then a struct ast_string_field_mgr
+ which describes the space allocated.
+ We split the two variables so they can be used as markers around the
+ field_list, and this allows us to determine how many entries are in
+ the field, and play with them.
+ In particular, for writing to the fields, we rely on __field_mgr_pool to be
+ a non-const pointer, so we know it has the same size as ast_string_field,
+ and we can use it to locate the fields.
+*/
+#define AST_DECLARE_STRING_FIELDS(field_list) \
+ struct ast_string_field_pool *__field_mgr_pool; \
+ field_list \
+ struct ast_string_field_mgr __field_mgr
+
+/*!
+ \brief Initialize a field pool and fields
+ \param x Pointer to a structure containing fields
+ \param size Amount of storage to allocate.
+ Use 0 to reset fields to the default value,
+ and release all but the most recent pool.
+ size<0 (used internally) means free all pools.
+ \return 0 on success, non-zero on failure
+*/
+#define ast_string_field_init(x, size) \
+ __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, size)
+
+/*! \brief free all memory - to be called before destroying the object */
+#define ast_string_field_free_memory(x) \
+ __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, -1)
+
+/*! \internal \brief internal version of ast_string_field_init */
+int __ast_string_field_init(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, int needed);
+
+/*!
+ \brief Set a field to a simple string value
+ \param x Pointer to a structure containing fields
+ \param ptr Pointer to a field within the structure
+ \param data String value to be copied into the field
+ \return nothing
+*/
+
+#define ast_string_field_ptr_set(x, ptr, data) do { \
+ const char *__d__ = (data); \
+ size_t __dlen__ = (__d__) ? strlen(__d__) : 0; \
+ const char **__p__ = (const char **)(ptr); \
+ if (__dlen__ == 0) \
+ *__p__ = __ast_string_field_empty; \
+ else if (__dlen__ <= strlen(*__p__)) \
+ strcpy((char *)*__p__, __d__); \
+ else if ( (*__p__ = __ast_string_field_alloc_space(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__ + 1) ) ) \
+ strcpy((char *)*__p__, __d__); \
+ } while (0)
+
+/*!
+ \brief Set a field to a simple string value
+ \param x Pointer to a structure containing fields
+ \param field Name of the field to set
+ \param data String value to be copied into the field
+ \return nothing
+*/
+#define ast_string_field_set(x, field, data) do { \
+ ast_string_field_ptr_set(x, &(x)->field, data); \
+ } while (0)
+
+
+/*!
+ \brief Set a field to a complex (built) value
+ \param x Pointer to a structure containing fields
+ \param ptr Pointer to a field within the structure
+ \param fmt printf-style format string
+ \param args Arguments for format string
+ \return nothing
+*/
+#define ast_string_field_ptr_build(x, ptr, fmt, args...) \
+ __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, ptr, fmt, args)
+
+/*!
+ \brief Set a field to a complex (built) value
+ \param x Pointer to a structure containing fields
+ \param field Name of the field to set
+ \param fmt printf-style format string
+ \param args Arguments for format string
+ \return nothing
+*/
+#define ast_string_field_build(x, field, fmt, args...) \
+ __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, &(x)->field, fmt, args)
+
+/*!
+ \brief Set a field to a complex (built) value with prebuilt va_lists.
+ \param x Pointer to a structure containing fields
+ \param ptr Pointer to a field within the structure
+ \param fmt printf-style format string
+ \param args1 Arguments for format string in va_list format
+ \param args2 a second copy of the va_list for the sake of bsd, with no va_list copy operation
+ \return nothing
+*/
+#define ast_string_field_ptr_build_va(x, ptr, fmt, args1, args2) \
+ __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, ptr, fmt, args1, args2)
+
+/*!
+ \brief Set a field to a complex (built) value
+ \param x Pointer to a structure containing fields
+ \param field Name of the field to set
+ \param fmt printf-style format string
+ \param args1 argument one
+ \param args2 argument two
+ \return nothing
+*/
+#define ast_string_field_build_va(x, field, fmt, args1, args2) \
+ __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, &(x)->field, fmt, args1, args2)
+
+#endif /* _ASTERISK_STRINGFIELDS_H */
diff --git a/trunk/include/asterisk/strings.h b/trunk/include/asterisk/strings.h
new file mode 100644
index 000000000..9646e94c2
--- /dev/null
+++ b/trunk/include/asterisk/strings.h
@@ -0,0 +1,691 @@
+/*
+ * 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 String manipulation functions
+ */
+
+#ifndef _ASTERISK_STRINGS_H
+#define _ASTERISK_STRINGS_H
+
+#include "asterisk/inline_api.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+
+/* You may see casts in this header that may seem useless but they ensure this file is C++ clean */
+
+static force_inline int ast_strlen_zero(const char *s)
+{
+ return (!s || (*s == '\0'));
+}
+
+/*! \brief returns the equivalent of logic or for strings:
+ * first one if not empty, otherwise second one.
+ */
+#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b))
+
+/*! \brief returns the equivalent of logic or for strings, with an additional boolean check:
+ * second one if not empty and first one is true, otherwise third one.
+ * example: S_COR(usewidget, widget, "<no widget>")
+ */
+#define S_COR(a, b, c) ((a && !ast_strlen_zero(b)) ? (b) : (c))
+
+/*!
+ \brief Gets a pointer to the first non-whitespace character in a string.
+ \param ast_skip_blanks function being used
+ \arg str the input string
+ \return a pointer to the first non-whitespace character
+ */
+AST_INLINE_API(
+char *ast_skip_blanks(const char *str),
+{
+ while (*str && ((unsigned char) *str) < 33)
+ str++;
+ return (char *)str;
+}
+)
+
+/*!
+ \brief Trims trailing whitespace characters from a string.
+ \param ast_skip_blanks function being used
+ \arg str the input string
+ \return a pointer to the modified string
+ */
+AST_INLINE_API(
+char *ast_trim_blanks(char *str),
+{
+ char *work = str;
+
+ if (work) {
+ work += strlen(work) - 1;
+ /* It's tempting to only want to erase after we exit this loop,
+ but since ast_trim_blanks *could* receive a constant string
+ (which we presumably wouldn't have to touch), we shouldn't
+ actually set anything unless we must, and it's easier just
+ to set each position to \0 than to keep track of a variable
+ for it */
+ while ((work >= str) && ((unsigned char) *work) < 33)
+ *(work--) = '\0';
+ }
+ return str;
+}
+)
+
+/*!
+ \brief Gets a pointer to first whitespace character in a string.
+ \param ast_skip_noblanks function being used
+ \arg str the input string
+ \return a pointer to the first whitespace character
+ */
+AST_INLINE_API(
+char *ast_skip_nonblanks(char *str),
+{
+ while (*str && ((unsigned char) *str) > 32)
+ str++;
+ return str;
+}
+)
+
+/*!
+ \brief Strip leading/trailing whitespace from a string.
+ \param ast_strip function ast_strip being used.
+ \arg s The string to be stripped (will be modified).
+ \return The stripped string.
+
+ This functions strips all leading and trailing whitespace
+ characters from the input string, and returns a pointer to
+ the resulting string. The string is modified in place.
+*/
+AST_INLINE_API(
+char *ast_strip(char *s),
+{
+ s = ast_skip_blanks(s);
+ if (s)
+ ast_trim_blanks(s);
+ return s;
+}
+)
+
+/*!
+ \brief Strip leading/trailing whitespace and quotes from a string.
+ \param s The string to be stripped (will be modified).
+ \param beg_quotes The list of possible beginning quote characters.
+ \param end_quotes The list of matching ending quote characters.
+ \return The stripped string.
+
+ This functions strips all leading and trailing whitespace
+ characters from the input string, and returns a pointer to
+ the resulting string. The string is modified in place.
+
+ It can also remove beginning and ending quote (or quote-like)
+ characters, in matching pairs. If the first character of the
+ string matches any character in beg_quotes, and the last
+ character of the string is the matching character in
+ end_quotes, then they are removed from the string.
+
+ Examples:
+ \code
+ ast_strip_quoted(buf, "\"", "\"");
+ ast_strip_quoted(buf, "'", "'");
+ ast_strip_quoted(buf, "[{(", "]})");
+ \endcode
+ */
+char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes);
+
+/*!
+ \brief Strip backslash for "escaped" semicolons.
+ \brief s The string to be stripped (will be modified).
+ \return The stripped string.
+ */
+char *ast_unescape_semicolon(char *s);
+
+/*!
+ \brief Convert some C escape sequences (\b\f\n\r\t) into the
+ equivalent characters.
+ \brief s The string to be converted (will be modified).
+ \return The converted string.
+ */
+char *ast_unescape_c(char *s);
+
+/*!
+ \brief Size-limited null-terminating string copy.
+ \arg dst The destination buffer.
+ \arg src The source string
+ \arg size The size of the destination buffer
+ \return Nothing.
+
+ This is similar to \a strncpy, with two important differences:
+ - the destination buffer will \b always be null-terminated
+ - the destination buffer is not filled with zeros past the copied string length
+ These differences make it slightly more efficient, and safer to use since it will
+ not leave the destination buffer unterminated. There is no need to pass an artificially
+ reduced buffer size to this function (unlike \a strncpy), and the buffer does not need
+ to be initialized to zeroes prior to calling this function.
+*/
+AST_INLINE_API(
+void ast_copy_string(char *dst, const char *src, size_t size),
+{
+ while (*src && size) {
+ *dst++ = *src++;
+ size--;
+ }
+ if (__builtin_expect(!size, 0))
+ dst--;
+ *dst = '\0';
+}
+)
+
+
+/*!
+ \brief Build a string in a buffer, designed to be called repeatedly
+
+ \note This method is not recommended. New code should use ast_str_*() instead.
+
+ This is a wrapper for snprintf, that properly handles the buffer pointer
+ and buffer space available.
+
+ \arg buffer current position in buffer to place string into (will be updated on return)
+ \arg space remaining space in buffer (will be updated on return)
+ \arg fmt printf-style format string
+ \retval 0 on success
+ \retval non-zero on failure.
+*/
+int ast_build_string(char **buffer, size_t *space, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
+
+/*!
+ \brief Build a string in a buffer, designed to be called repeatedly
+
+ This is a wrapper for snprintf, that properly handles the buffer pointer
+ and buffer space available.
+
+ \return 0 on success, non-zero on failure.
+ \param buffer current position in buffer to place string into (will be updated on return)
+ \param space remaining space in buffer (will be updated on return)
+ \param fmt printf-style format string
+ \param ap varargs list of arguments for format
+*/
+int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap);
+
+/*!
+ * \brief Make sure something is true.
+ * Determine if a string containing a boolean value is "true".
+ * This function checks to see whether a string passed to it is an indication of an "true" value.
+ * It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
+ *
+ * \retval 0 if val is a NULL pointer.
+ * \retval -1 if "true".
+ * \retval 0 otherwise.
+ */
+int ast_true(const char *val);
+
+/*!
+ * \brief Make sure something is false.
+ * Determine if a string containing a boolean value is "false".
+ * This function checks to see whether a string passed to it is an indication of an "false" value.
+ * It checks to see if the string is "no", "false", "n", "f", "off" or "0".
+ *
+ * \retval 0 if val is a NULL pointer.
+ * \retval -1 if "true".
+ * \retval 0 otherwise.
+ */
+int ast_false(const char *val);
+
+/*
+ * \brief Join an array of strings into a single string.
+ * \param s the resulting string buffer
+ * \param len the length of the result buffer, s
+ * \param w an array of strings to join.
+ *
+ * This function will join all of the strings in the array 'w' into a single
+ * string. It will also place a space in the result buffer in between each
+ * string from 'w'.
+*/
+void ast_join(char *s, size_t len, char * const w[]);
+
+/*
+ \brief Parse a time (integer) string.
+ \param src String to parse
+ \param dst Destination
+ \param _default Value to use if the string does not contain a valid time
+ \param consumed The number of characters 'consumed' in the string by the parse (see 'man sscanf' for details)
+ \retval 0 on success
+ \retval non-zero on failure.
+*/
+int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed);
+
+/*
+ \brief Parse a time (float) string.
+ \param src String to parse
+ \param dst Destination
+ \param _default Value to use if the string does not contain a valid time
+ \param consumed The number of characters 'consumed' in the string by the parse (see 'man sscanf' for details)
+ \return zero on success, non-zero on failure
+*/
+int ast_get_timeval(const char *src, struct timeval *tv, struct timeval _default, int *consumed);
+
+/*!
+ * Support for dynamic strings.
+ *
+ * A dynamic string is just a C string prefixed by a few control fields
+ * that help setting/appending/extending it using a printf-like syntax.
+ *
+ * One should never declare a variable with this type, but only a pointer
+ * to it, e.g.
+ *
+ * struct ast_str *ds;
+ *
+ * The pointer can be initialized with the following:
+ *
+ * ds = ast_str_create(init_len);
+ * creates a malloc()'ed dynamic string;
+ *
+ * ds = ast_str_alloca(init_len);
+ * creates a string on the stack (not very dynamic!).
+ *
+ * ds = ast_str_thread_get(ts, init_len)
+ * creates a malloc()'ed dynamic string associated to
+ * the thread-local storage key ts
+ *
+ * Finally, the string can be manipulated with the following:
+ *
+ * ast_str_set(&buf, max_len, fmt, ...)
+ * ast_str_append(&buf, max_len, fmt, ...)
+ *
+ * and their varargs variant
+ *
+ * ast_str_set_va(&buf, max_len, ap)
+ * ast_str_append_va(&buf, max_len, ap)
+ *
+ * \arg max_len The maximum allowed length, reallocating if needed.
+ * 0 means unlimited, -1 means "at most the available space"
+ *
+ * \return All the functions return <0 in case of error, or the
+ * length of the string added to the buffer otherwise.
+ */
+
+/*! \brief The descriptor of a dynamic string
+ * XXX storage will be optimized later if needed
+ * We use the ts field to indicate the type of storage.
+ * Three special constants indicate malloc, alloca() or static
+ * variables, all other values indicate a
+ * struct ast_threadstorage pointer.
+ */
+struct ast_str {
+ size_t len; /*!< The current maximum length of the string */
+ size_t used; /*!< Amount of space used */
+ struct ast_threadstorage *ts; /*!< What kind of storage is this ? */
+#define DS_MALLOC ((struct ast_threadstorage *)1)
+#define DS_ALLOCA ((struct ast_threadstorage *)2)
+#define DS_STATIC ((struct ast_threadstorage *)3) /* not supported yet */
+ char str[0]; /*!< The string buffer */
+};
+
+/*!
+ * \brief Create a malloc'ed dynamic length string
+ *
+ * \arg init_len This is the initial length of the string buffer
+ *
+ * \return This function returns a pointer to the dynamic string length. The
+ * result will be NULL in the case of a memory allocation error.
+ *
+ * \note The result of this function is dynamically allocated memory, and must
+ * be free()'d after it is no longer needed.
+ */
+AST_INLINE_API(
+struct ast_str * attribute_malloc ast_str_create(size_t init_len),
+{
+ struct ast_str *buf;
+
+ buf = (struct ast_str *)ast_calloc(1, sizeof(*buf) + init_len);
+ if (buf == NULL)
+ return NULL;
+
+ buf->len = init_len;
+ buf->used = 0;
+ buf->ts = DS_MALLOC;
+
+ return buf;
+}
+)
+
+/*! \brief Reset the content of a dynamic string.
+ * Useful before a series of ast_str_append.
+ */
+AST_INLINE_API(
+void ast_str_reset(struct ast_str *buf),
+{
+ if (buf) {
+ buf->used = 0;
+ if (buf->len)
+ buf->str[0] = '\0';
+ }
+}
+)
+
+/*
+ * AST_INLINE_API() is a macro that takes a block of code as an argument.
+ * Using preprocessor #directives in the argument is not supported by all
+ * compilers, and it is a bit of an obfuscation anyways, so avoid it.
+ * As a workaround, define a macro that produces either its argument
+ * or nothing, and use that instead of #ifdef/#endif within the
+ * argument to AST_INLINE_API().
+ */
+#if defined(DEBUG_THREADLOCALS)
+#define _DB1(x) x
+#else
+#define _DB1(x)
+#endif
+
+/*!
+ * Make space in a new string (e.g. to read in data from a file)
+ */
+AST_INLINE_API(
+int ast_str_make_space(struct ast_str **buf, size_t new_len),
+{
+ _DB1(struct ast_str *old_buf = *buf;)
+
+ if (new_len <= (*buf)->len)
+ return 0; /* success */
+ if ((*buf)->ts == DS_ALLOCA || (*buf)->ts == DS_STATIC)
+ return -1; /* cannot extend */
+ *buf = (struct ast_str *)ast_realloc(*buf, new_len + sizeof(struct ast_str));
+ if (*buf == NULL) /* XXX watch out, we leak memory here */
+ return -1;
+ if ((*buf)->ts != DS_MALLOC) {
+ pthread_setspecific((*buf)->ts->key, *buf);
+ _DB1(__ast_threadstorage_object_replace(old_buf, *buf, new_len + sizeof(struct ast_str));)
+ }
+
+ (*buf)->len = new_len;
+ return 0;
+}
+)
+
+#define ast_str_alloca(init_len) \
+ ({ \
+ struct ast_str *buf; \
+ buf = alloca(sizeof(*buf) + init_len); \
+ buf->len = init_len; \
+ buf->used = 0; \
+ buf->ts = DS_ALLOCA; \
+ buf->str[0] = '\0'; \
+ (buf); \
+ })
+
+/*!
+ * \brief Retrieve a thread locally stored dynamic string
+ *
+ * \arg ts This is a pointer to the thread storage structure declared by using
+ * the AST_THREADSTORAGE macro. If declared with
+ * AST_THREADSTORAGE(my_buf, my_buf_init), then this argument would be
+ * (&my_buf).
+ * \arg init_len This is the initial length of the thread's dynamic string. The
+ * current length may be bigger if previous operations in this thread have
+ * caused it to increase.
+ *
+ * \return This function will return the thread locally stored dynamic string
+ * associated with the thread storage management variable passed as the
+ * first argument.
+ * The result will be NULL in the case of a memory allocation error.
+ *
+ * Example usage:
+ * \code
+ * AST_THREADSTORAGE(my_str, my_str_init);
+ * #define MY_STR_INIT_SIZE 128
+ * ...
+ * void my_func(const char *fmt, ...)
+ * {
+ * struct ast_str *buf;
+ *
+ * if (!(buf = ast_str_thread_get(&my_str, MY_STR_INIT_SIZE)))
+ * return;
+ * ...
+ * }
+ * \endcode
+ */
+#if !defined(DEBUG_THREADLOCALS)
+AST_INLINE_API(
+struct ast_str *ast_str_thread_get(struct ast_threadstorage *ts,
+ size_t init_len),
+{
+ struct ast_str *buf;
+
+ buf = (struct ast_str *)ast_threadstorage_get(ts, sizeof(*buf) + init_len);
+ if (buf == NULL)
+ return NULL;
+
+ if (!buf->len) {
+ buf->len = init_len;
+ buf->used = 0;
+ buf->ts = ts;
+ }
+
+ return buf;
+}
+)
+#else /* defined(DEBUG_THREADLOCALS) */
+AST_INLINE_API(
+struct ast_str *__ast_str_thread_get(struct ast_threadstorage *ts,
+ size_t init_len, const char *file, const char *function, unsigned int line),
+{
+ struct ast_str *buf;
+
+ buf = (struct ast_str *)__ast_threadstorage_get(ts, sizeof(*buf) + init_len, file, function, line);
+ if (buf == NULL)
+ return NULL;
+
+ if (!buf->len) {
+ buf->len = init_len;
+ buf->used = 0;
+ buf->ts = ts;
+ }
+
+ return buf;
+}
+)
+
+#define ast_str_thread_get(ts, init_len) __ast_str_thread_get(ts, init_len, __FILE__, __PRETTY_FUNCTION__, __LINE__)
+#endif /* defined(DEBUG_THREADLOCALS) */
+
+/*!
+ * \brief Error codes from __ast_str_helper()
+ * The undelying processing to manipulate dynamic string is done
+ * by __ast_str_helper(), which can return a success, a
+ * permanent failure (e.g. no memory), or a temporary one (when
+ * the string needs to be reallocated, and we must run va_start()
+ * again; XXX this convoluted interface is only here because
+ * FreeBSD 4 lacks va_copy, but this will be fixed and the
+ * interface simplified).
+ */
+enum {
+ /*! An error has occured and the contents of the dynamic string
+ * are undefined */
+ AST_DYNSTR_BUILD_FAILED = -1,
+ /*! The buffer size for the dynamic string had to be increased, and
+ * __ast_str_helper() needs to be called again after
+ * a va_end() and va_start().
+ */
+ AST_DYNSTR_BUILD_RETRY = -2
+};
+
+/*!
+ * \brief Set a dynamic string from a va_list
+ *
+ * \arg buf This is the address of a pointer to a struct ast_str.
+ * If it is retrieved using ast_str_thread_get, the
+ struct ast_threadstorage pointer will need to
+ * be updated in the case that the buffer has to be reallocated to
+ * accommodate a longer string than what it currently has space for.
+ * \arg max_len This is the maximum length to allow the string buffer to grow
+ * to. If this is set to 0, then there is no maximum length.
+ * \arg fmt This is the format string (printf style)
+ * \arg ap This is the va_list
+ *
+ * \return The return value of this function is the same as that of the printf
+ * family of functions.
+ *
+ * Example usage (the first part is only for thread-local storage)
+ * \code
+ * AST_THREADSTORAGE(my_str, my_str_init);
+ * #define MY_STR_INIT_SIZE 128
+ * ...
+ * void my_func(const char *fmt, ...)
+ * {
+ * struct ast_str *buf;
+ * va_list ap;
+ *
+ * if (!(buf = ast_str_thread_get(&my_str, MY_STR_INIT_SIZE)))
+ * return;
+ * ...
+ * va_start(fmt, ap);
+ * ast_str_set_va(&buf, 0, fmt, ap);
+ * va_end(ap);
+ *
+ * printf("This is the string we just built: %s\n", buf->str);
+ * ...
+ * }
+ * \endcode
+ *
+ * \note: the following two functions must be implemented as macros
+ * because we must do va_end()/va_start() on the original arguments.
+ */
+#define ast_str_set_va(buf, max_len, fmt, ap) \
+ ({ \
+ int __res; \
+ while ((__res = __ast_str_helper(buf, max_len, \
+ 0, fmt, ap)) == AST_DYNSTR_BUILD_RETRY) { \
+ va_end(ap); \
+ va_start(ap, fmt); \
+ } \
+ (__res); \
+ })
+
+/*!
+ * \brief Append to a dynamic string using a va_list
+ *
+ * Same as ast_str_set_va(), but append to the current content.
+ */
+#define ast_str_append_va(buf, max_len, fmt, ap) \
+ ({ \
+ int __res; \
+ while ((__res = __ast_str_helper(buf, max_len, \
+ 1, fmt, ap)) == AST_DYNSTR_BUILD_RETRY) { \
+ va_end(ap); \
+ va_start(ap, fmt); \
+ } \
+ (__res); \
+ })
+
+/*!
+ * \brief Core functionality of ast_str_(set|append)_va
+ *
+ * The arguments to this function are the same as those described for
+ * ast_str_set_va except for an addition argument, append.
+ * If append is non-zero, this will append to the current string instead of
+ * writing over it.
+ *
+ * In the case that this function is called and the buffer was not large enough
+ * to hold the result, the partial write will be truncated, and the result
+ * AST_DYNSTR_BUILD_RETRY will be returned to indicate that the buffer size
+ * was increased, and the function should be called a second time.
+ *
+ * A return of AST_DYNSTR_BUILD_FAILED indicates a memory allocation error.
+ *
+ * A return value greater than or equal to zero indicates the number of
+ * characters that have been written, not including the terminating '\0'.
+ * In the append case, this only includes the number of characters appended.
+ *
+ * \note This function should never need to be called directly. It should
+ * through calling one of the other functions or macros defined in this
+ * file.
+ */
+int __ast_str_helper(struct ast_str **buf, size_t max_len,
+ int append, const char *fmt, va_list ap);
+
+/*!
+ * \brief Set a dynamic string using variable arguments
+ *
+ * \arg buf This is the address of a pointer to a struct ast_str which should
+ * have been retrieved using ast_str_thread_get. It will need to
+ * be updated in the case that the buffer has to be reallocated to
+ * accomodate a longer string than what it currently has space for.
+ * \arg max_len This is the maximum length to allow the string buffer to grow
+ * to. If this is set to 0, then there is no maximum length.
+ * If set to -1, we are bound to the current maximum length.
+ * \arg fmt This is the format string (printf style)
+ *
+ * \return The return value of this function is the same as that of the printf
+ * family of functions.
+ *
+ * All the rest is the same as ast_str_set_va()
+ */
+AST_INLINE_API(
+int __attribute__ ((format (printf, 3, 4))) ast_str_set(
+ struct ast_str **buf, size_t max_len, const char *fmt, ...),
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ res = ast_str_set_va(buf, max_len, fmt, ap);
+ va_end(ap);
+
+ return res;
+}
+)
+
+/*!
+ * \brief Append to a thread local dynamic string
+ *
+ * The arguments, return values, and usage of this function are the same as
+ * ast_str_set(), but the new data is appended to the current value.
+ */
+AST_INLINE_API(
+int __attribute__ ((format (printf, 3, 4))) ast_str_append(
+ struct ast_str **buf, size_t max_len, const char *fmt, ...),
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ res = ast_str_append_va(buf, max_len, fmt, ap);
+ va_end(ap);
+
+ return res;
+}
+)
+
+/*!
+ * \brief Compute a hash value on a string
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * http://www.cse.yorku.ca/~oz/hash.html
+ */
+static force_inline int ast_str_hash(const char *str)
+{
+ int hash = 5381;
+
+ while (*str)
+ hash = hash * 33 ^ *str++;
+
+ return abs(hash);
+}
+
+#endif /* _ASTERISK_STRINGS_H */
diff --git a/trunk/include/asterisk/tcptls.h b/trunk/include/asterisk/tcptls.h
new file mode 100644
index 000000000..fe4743f33
--- /dev/null
+++ b/trunk/include/asterisk/tcptls.h
@@ -0,0 +1,166 @@
+/*
+ * 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 server.h
+ *
+ * \brief Generic support for tcp/tls servers in Asterisk.
+ * \note In order to have TLS/SSL support, we need the openssl libraries.
+ * Still we can decide whether or not to use them by commenting
+ * in or out the DO_SSL macro.
+ * TLS/SSL support is basically implemented by reading from a config file
+ * (currently http.conf) the names of the certificate and cipher to use,
+ * and then run ssl_setup() to create an appropriate SSL_CTX (ssl_ctx)
+ * If we support multiple domains, presumably we need to read multiple
+ * certificates.
+ * When we are requested to open a TLS socket, we run make_file_from_fd()
+ * on the socket, to do the necessary setup. At the moment the context's name
+ * is hardwired in the function, but we can certainly make it into an extra
+ * parameter to the function.
+ * We declare most of ssl support variables unconditionally,
+ * because their number is small and this simplifies the code.
+ *
+ * \note: the ssl-support variables (ssl_ctx, do_ssl, certfile, cipher)
+ * and their setup should be moved to a more central place, e.g. asterisk.conf
+ * and the source files that processes it. Similarly, ssl_setup() should
+ * be run earlier in the startup process so modules have it available.
+ *
+ */
+
+
+#ifndef _ASTERISK_SERVER_H
+#define _ASTERISK_SERVER_H
+
+#include "asterisk/utils.h"
+
+#if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
+#define DO_SSL /* comment in/out if you want to support ssl */
+#endif
+
+#ifdef DO_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#else
+/* declare dummy types so we can define a pointer to them */
+typedef struct {} SSL;
+typedef struct {} SSL_CTX;
+#endif /* DO_SSL */
+
+/*! SSL support */
+#define AST_CERTFILE "asterisk.pem"
+
+enum ast_ssl_flags {
+ /*! Verify certificate when acting as server */
+ AST_SSL_VERIFY_CLIENT = (1 << 0),
+ /*! Don't verify certificate when connecting to a server */
+ AST_SSL_DONT_VERIFY_SERVER = (1 << 1),
+ /*! Don't compare "Common Name" against IP or hostname */
+ AST_SSL_IGNORE_COMMON_NAME = (1 << 2)
+};
+
+struct ast_tls_config {
+ int enabled;
+ char *certfile;
+ char *cipher;
+ char *cafile;
+ char *capath;
+ struct ast_flags flags;
+ SSL_CTX *ssl_ctx;
+};
+
+/*!
+ * The following code implements a generic mechanism for starting
+ * services on a TCP or TLS socket.
+ * The service is configured in the struct server_args, and
+ * then started by calling server_start(desc) on the descriptor.
+ * server_start() first verifies if an instance of the service is active,
+ * and in case shuts it down. Then, if the service must be started, creates
+ * a socket and a thread in charge of doing the accept().
+ *
+ * The body of the thread is desc->accept_fn(desc), which the user can define
+ * freely. We supply a sample implementation, server_root(), structured as an
+ * infinite loop. At the beginning of each iteration it runs periodic_fn()
+ * if defined (e.g. to perform some cleanup etc.) then issues a poll()
+ * or equivalent with a timeout of 'poll_timeout' milliseconds, and if the
+ * following accept() is successful it creates a thread in charge of
+ * running the session, whose body is desc->worker_fn(). The argument of
+ * worker_fn() is a struct server_instance, which contains the address
+ * of the other party, a pointer to desc, the file descriptors (fd) on which
+ * we can do a select/poll (but NOT IO/, and a FILE *on which we can do I/O.
+ * We have both because we want to support plain and SSL sockets, and
+ * going through a FILE *lets us provide the encryption/decryption
+ * on the stream without using an auxiliary thread.
+ *
+ * NOTE: in order to let other parts of asterisk use these services,
+ * we need to do the following:
+ * + move struct server_instance and struct server_args to
+ * a common header file, together with prototypes for
+ * server_start() and server_root().
+ */
+
+/*!
+ * describes a server instance
+ */
+struct server_instance {
+ FILE *f; /* fopen/funopen result */
+ int fd; /* the socket returned by accept() */
+ SSL *ssl; /* ssl state */
+// iint (*ssl_setup)(SSL *);
+ int client;
+ struct sockaddr_in requestor;
+ struct server_args *parent;
+};
+
+/*!
+ * arguments for the accepting thread
+ */
+struct server_args {
+ struct sockaddr_in sin;
+ struct sockaddr_in oldsin;
+ char hostname[MAXHOSTNAMELEN]; /* only necessary for SSL clients so we can compare to common name */
+ struct ast_tls_config *tls_cfg; /* points to the SSL configuration if any */
+ int accept_fd;
+ int poll_timeout;
+ pthread_t master;
+ void *(*accept_fn)(void *); /* the function in charge of doing the accept */
+ void (*periodic_fn)(void *);/* something we may want to run before after select on the accept socket */
+ void *(*worker_fn)(void *); /* the function in charge of doing the actual work */
+ const char *name;
+};
+
+#if defined(HAVE_FUNOPEN)
+#define HOOK_T int
+#define LEN_T int
+#else
+#define HOOK_T ssize_t
+#define LEN_T size_t
+#endif
+
+struct server_instance *client_start(struct server_args *desc);
+
+void *server_root(void *);
+void server_start(struct server_args *desc);
+void server_stop(struct server_args *desc);
+int ssl_setup(struct ast_tls_config *cfg);
+
+void *ast_make_file_from_fd(void *data);
+
+HOOK_T server_read(struct server_instance *ser, void *buf, size_t count);
+HOOK_T server_write(struct server_instance *ser, void *buf, size_t count);
+
+#endif /* _ASTERISK_SERVER_H */
diff --git a/trunk/include/asterisk/tdd.h b/trunk/include/asterisk/tdd.h
new file mode 100644
index 000000000..6fb7e3e27
--- /dev/null
+++ b/trunk/include/asterisk/tdd.h
@@ -0,0 +1,82 @@
+/*
+ * 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 TTY/TDD Generation support
+ * \note Includes code and algorithms from the Zapata library.
+ */
+
+#ifndef _ASTERISK_TDD_H
+#define _ASTERISK_TDD_H
+
+#define TDD_BYTES_PER_CHAR 2700
+
+struct tdd_state;
+typedef struct tdd_state TDDSTATE;
+
+/*! CallerID Initialization
+ * Initializes the TDD system. Mostly stuff for inverse FFT
+ */
+void tdd_init(void);
+
+/*! Generates a CallerID FSK stream in ulaw format suitable for transmission.
+ * \param tdd tdd structure
+ * \param buf Buffer to use. This needs to be large enough to accomodate all the generated samples.
+ * \param string This is the string to send.
+ * This function creates a stream of TDD data in ulaw format. It returns the size
+ * (in bytes) of the data (if it returns a size of 0, there is probably an error)
+*/
+int tdd_generate(struct tdd_state *tdd, unsigned char *buf, const char *string);
+
+/*! Create a TDD state machine
+ * This function returns a malloc'd instance of the tdd_state data structure.
+ * Returns a pointer to a malloc'd tdd_state structure, or NULL on error.
+ */
+struct tdd_state *tdd_new(void);
+
+/*! Read samples into the state machine, and return character (if any).
+ * \param tdd Which state machine to act upon
+ * \param ubuf containing your samples
+ * \param samples number of samples contained within the buffer.
+ *
+ * Send received audio to the TDD demodulator.
+ * Returns -1 on error, 0 for "needs more samples",
+ * and > 0 (the character) if reception of a character is complete.
+ */
+int tdd_feed(struct tdd_state *tdd, unsigned char *ubuf, int samples);
+
+/*! Free a TDD state machine
+ * \param tdd This is the tdd_state state machine to free
+ * This function frees tdd_state tdd.
+ */
+void tdd_free(struct tdd_state *tdd);
+
+/*! Generate Echo Canceller disable tone (2100HZ)
+ * \param outbuf This is the buffer to receive the tone data
+ * \param len This is the length (in samples) of the tone data to generate
+ * Returns 0 if no error, and -1 if error.
+ */
+int ast_tdd_gen_ecdisa(unsigned char *outbuf, int len);
+
+
+/*! Generate hold tone
+ * \param outbuf This is the buffer to receive the tone data
+*/
+int tdd_gen_holdtone(unsigned char* outbuf);
+
+#endif /* _ASTERISK_TDD_H */
diff --git a/trunk/include/asterisk/term.h b/trunk/include/asterisk/term.h
new file mode 100644
index 000000000..3277f0042
--- /dev/null
+++ b/trunk/include/asterisk/term.h
@@ -0,0 +1,85 @@
+/*
+ * 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 Handy terminal functions for vt* terms
+ */
+
+#ifndef _ASTERISK_TERM_H
+#define _ASTERISK_TERM_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define ESC 0x1b
+
+/*! \name Terminal Attributes
+*/
+/*@{ */
+#define ATTR_RESET 0
+#define ATTR_BRIGHT 1
+#define ATTR_DIM 2
+#define ATTR_UNDER 4
+#define ATTR_BLINK 5
+#define ATTR_REVER 7
+#define ATTR_HIDDEN 8
+/*@} */
+
+/*! \name Terminal Colors
+*/
+/*@{ */
+#define COLOR_BLACK 30
+#define COLOR_GRAY (30 | 128)
+#define COLOR_RED 31
+#define COLOR_BRRED (31 | 128)
+#define COLOR_GREEN 32
+#define COLOR_BRGREEN (32 | 128)
+#define COLOR_BROWN 33
+#define COLOR_YELLOW (33 | 128)
+#define COLOR_BLUE 34
+#define COLOR_BRBLUE (34 | 128)
+#define COLOR_MAGENTA 35
+#define COLOR_BRMAGENTA (35 | 128)
+#define COLOR_CYAN 36
+#define COLOR_BRCYAN (36 | 128)
+#define COLOR_WHITE 37
+#define COLOR_BRWHITE (37 | 128)
+/*@} */
+
+char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout);
+
+char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout);
+
+char *term_strip(char *outbuf, char *inbuf, int maxout);
+
+void term_filter_escapes(char *line);
+
+char *term_prompt(char *outbuf, const char *inbuf, int maxout);
+
+char *term_prep(void);
+
+char *term_end(void);
+
+char *term_quit(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_TERM_H */
diff --git a/trunk/include/asterisk/threadstorage.h b/trunk/include/asterisk/threadstorage.h
new file mode 100644
index 000000000..5e6b3c348
--- /dev/null
+++ b/trunk/include/asterisk/threadstorage.h
@@ -0,0 +1,212 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Russell Bryant <russell@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 threadstorage.h
+ * \author Russell Bryant <russell@digium.com>
+ * \brief Definitions to aid in the use of thread local storage
+ *
+ * \arg \ref AstThreadStorage
+ */
+
+/*!
+ * \page AstThreadStorage The Asterisk Thread Storage API
+ *
+ *
+ * The POSIX threads (pthreads) API provides the ability to define thread
+ * specific data. The functions and structures defined here are intended
+ * to centralize the code that is commonly used when using thread local
+ * storage.
+ *
+ * The motivation for using this code in Asterisk is for situations where
+ * storing data on a thread-specific basis can provide some amount of
+ * performance benefit. For example, there are some call types in Asterisk
+ * where ast_frame structures must be allocated very rapidly (easily 50, 100,
+ * 200 times a second). Instead of doing the equivalent of that many calls
+ * to malloc() and free() per second, thread local storage is used to keep a
+ * list of unused frame structures so that they can be continuously reused.
+ *
+ * - \ref threadstorage.h
+ */
+
+#ifndef ASTERISK_THREADSTORAGE_H
+#define ASTERISK_THREADSTORAGE_H
+
+#include "asterisk/utils.h"
+#include "asterisk/inline_api.h"
+
+/*!
+ * \brief data for a thread locally stored variable
+ */
+struct ast_threadstorage {
+ pthread_once_t once; /*!< Ensure that the key is only initialized by one thread */
+ pthread_key_t key; /*!< The key used to retrieve this thread's data */
+ void (*key_init)(void); /*!< The function that initializes the key */
+ int (*custom_init)(void *); /*!< Custom initialization function specific to the object */
+};
+
+#ifdef SOLARIS
+#define THREADSTORAGE_ONCE_INIT {PTHREAD_ONCE_INIT}
+#else
+#define THREADSTORAGE_ONCE_INIT PTHREAD_ONCE_INIT
+#endif
+
+#if defined(DEBUG_THREADLOCALS)
+void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line);
+void __ast_threadstorage_object_remove(void *key);
+void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len);
+#endif /* defined(DEBUG_THREADLOCALS) */
+
+/*!
+ * \brief Define a thread storage variable
+ *
+ * \arg name The name of the thread storage object
+ *
+ * This macro would be used to declare an instance of thread storage in a file.
+ *
+ * Example usage:
+ * \code
+ * AST_THREADSTORAGE(my_buf);
+ * \endcode
+ */
+#define AST_THREADSTORAGE(name) \
+ AST_THREADSTORAGE_CUSTOM(name, NULL, ast_free_ptr)
+
+/*!
+ * \brief Define a thread storage variable, with custom initialization and cleanup
+ *
+ * \arg name The name of the thread storage object
+ * \arg init This is a custom function that will be called after each thread specific
+ * object is allocated, with the allocated block of memory passed
+ * as the argument.
+ * \arg cleanup This is a custom function that will be called instead of ast_free
+ * when the thread goes away. Note that if this is used, it *MUST*
+ * call free on the allocated memory.
+ *
+ * Example usage:
+ * \code
+ * AST_THREADSTORAGE_CUSTOM(my_buf, my_init, my_cleanup);
+ * \endcode
+ */
+#if !defined(DEBUG_THREADLOCALS)
+#define AST_THREADSTORAGE_CUSTOM(name, c_init, c_cleanup) \
+static void __init_##name(void); \
+static struct ast_threadstorage name = { \
+ .once = THREADSTORAGE_ONCE_INIT, \
+ .key_init = __init_##name, \
+ .custom_init = c_init, \
+}; \
+static void __init_##name(void) \
+{ \
+ pthread_key_create(&(name).key, c_cleanup); \
+}
+#else /* defined(DEBUG_THREADLOCALS) */
+#define AST_THREADSTORAGE_CUSTOM(name, c_init, c_cleanup) \
+static void __init_##name(void); \
+static struct ast_threadstorage name = { \
+ .once = THREADSTORAGE_ONCE_INIT, \
+ .key_init = __init_##name, \
+ .custom_init = c_init, \
+}; \
+static void __cleanup_##name(void *data) \
+{ \
+ __ast_threadstorage_object_remove(data); \
+ c_cleanup(data); \
+} \
+static void __init_##name(void) \
+{ \
+ pthread_key_create(&(name).key, __cleanup_##name); \
+}
+#endif /* defined(DEBUG_THREADLOCALS) */
+
+/*!
+ * \brief Retrieve thread storage
+ *
+ * \arg ts This is a pointer to the thread storage structure declared by using
+ * the AST_THREADSTORAGE macro. If declared with
+ * AST_THREADSTORAGE(my_buf, my_buf_init), then this argument would be
+ * (&my_buf).
+ * \arg init_size This is the amount of space to be allocated the first time
+ * this thread requests its data. Thus, this should be the size that the
+ * code accessing this thread storage is assuming the size to be.
+ *
+ * \return This function will return the thread local storage associated with
+ * the thread storage management variable passed as the first argument.
+ * The result will be NULL in the case of a memory allocation error.
+ *
+ * Example usage:
+ * \code
+ * AST_THREADSTORAGE(my_buf, my_buf_init);
+ * #define MY_BUF_SIZE 128
+ * ...
+ * void my_func(const char *fmt, ...)
+ * {
+ * void *buf;
+ *
+ * if (!(buf = ast_threadstorage_get(&my_buf, MY_BUF_SIZE)))
+ * return;
+ * ...
+ * }
+ * \endcode
+ */
+#if !defined(DEBUG_THREADLOCALS)
+AST_INLINE_API(
+void *ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size),
+{
+ void *buf;
+
+ pthread_once(&ts->once, ts->key_init);
+ if (!(buf = pthread_getspecific(ts->key))) {
+ if (!(buf = ast_calloc(1, init_size)))
+ return NULL;
+ if (ts->custom_init && ts->custom_init(buf)) {
+ free(buf);
+ return NULL;
+ }
+ pthread_setspecific(ts->key, buf);
+ }
+
+ return buf;
+}
+)
+#else /* defined(DEBUG_THREADLOCALS) */
+AST_INLINE_API(
+void *__ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size, const char *file, const char *function, unsigned int line),
+{
+ void *buf;
+
+ pthread_once(&ts->once, ts->key_init);
+ if (!(buf = pthread_getspecific(ts->key))) {
+ if (!(buf = ast_calloc(1, init_size)))
+ return NULL;
+ if (ts->custom_init && ts->custom_init(buf)) {
+ free(buf);
+ return NULL;
+ }
+ pthread_setspecific(ts->key, buf);
+ __ast_threadstorage_object_add(buf, init_size, file, function, line);
+ }
+
+ return buf;
+}
+)
+
+#define ast_threadstorage_get(ts, init_size) __ast_threadstorage_get(ts, init_size, __FILE__, __PRETTY_FUNCTION__, __LINE__)
+#endif /* defined(DEBUG_THREADLOCALS) */
+
+#endif /* ASTERISK_THREADSTORAGE_H */
diff --git a/trunk/include/asterisk/time.h b/trunk/include/asterisk/time.h
new file mode 100644
index 000000000..4b7ca00ff
--- /dev/null
+++ b/trunk/include/asterisk/time.h
@@ -0,0 +1,178 @@
+/*
+ * 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 Time-related functions and macros
+ */
+
+#ifndef _ASTERISK_TIME_H
+#define _ASTERISK_TIME_H
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include "asterisk/inline_api.h"
+
+/* We have to let the compiler learn what types to use for the elements of a
+ struct timeval since on linux, it's time_t and suseconds_t, but on *BSD,
+ they are just a long. */
+extern struct timeval tv;
+typedef typeof(tv.tv_sec) ast_time_t;
+typedef typeof(tv.tv_usec) ast_suseconds_t;
+
+/*!
+ * \brief Computes the difference (in seconds) between two \c struct \c timeval instances.
+ * \param end the end of the time period
+ * \param start the beginning of the time period
+ * \return the difference in seconds
+ */
+AST_INLINE_API(
+int ast_tvdiff_sec(struct timeval end, struct timeval start),
+{
+ int result = end.tv_sec - start.tv_sec;
+ if (result > 0 && end.tv_usec < start.tv_usec)
+ result--;
+ else if (result < 0 && end.tv_usec > start.tv_usec)
+ result++;
+
+ return result;
+}
+)
+
+/*!
+ * \brief Computes the difference (in microseconds) between two \c struct \c timeval instances.
+ * \param end the end of the time period
+ * \param start the beginning of the time period
+ * \return the difference in microseconds
+ */
+AST_INLINE_API(
+int64_t ast_tvdiff_us(struct timeval end, struct timeval start),
+{
+ return (end.tv_sec - start.tv_sec) * (int64_t) 1000000 +
+ end.tv_usec - start.tv_usec;
+}
+)
+
+/*!
+ * \brief Computes the difference (in milliseconds) between two \c struct \c timeval instances.
+ * \param end end of the time period
+ * \param start beginning of the time period
+ * \return the difference in milliseconds
+ */
+AST_INLINE_API(
+int ast_tvdiff_ms(struct timeval end, struct timeval start),
+{
+ /* the offset by 1,000,000 below is intentional...
+ it avoids differences in the way that division
+ is handled for positive and negative numbers, by ensuring
+ that the divisor is always positive
+ */
+ return ((end.tv_sec - start.tv_sec) * 1000) +
+ (((1000000 + end.tv_usec - start.tv_usec) / 1000) - 1000);
+}
+)
+
+/*!
+ * \brief Returns true if the argument is 0,0
+ */
+AST_INLINE_API(
+int ast_tvzero(const struct timeval t),
+{
+ return (t.tv_sec == 0 && t.tv_usec == 0);
+}
+)
+
+/*!
+ * \brief Compres two \c struct \c timeval instances returning
+ * -1, 0, 1 if the first arg is smaller, equal or greater to the second.
+ */
+AST_INLINE_API(
+int ast_tvcmp(struct timeval _a, struct timeval _b),
+{
+ if (_a.tv_sec < _b.tv_sec)
+ return -1;
+ if (_a.tv_sec > _b.tv_sec)
+ return 1;
+ /* now seconds are equal */
+ if (_a.tv_usec < _b.tv_usec)
+ return -1;
+ if (_a.tv_usec > _b.tv_usec)
+ return 1;
+ return 0;
+}
+)
+
+/*!
+ * \brief Returns true if the two \c struct \c timeval arguments are equal.
+ */
+AST_INLINE_API(
+int ast_tveq(struct timeval _a, struct timeval _b),
+{
+ return (_a.tv_sec == _b.tv_sec && _a.tv_usec == _b.tv_usec);
+}
+)
+
+/*!
+ * \brief Returns current timeval. Meant to replace calls to gettimeofday().
+ */
+AST_INLINE_API(
+struct timeval ast_tvnow(void),
+{
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ return t;
+}
+)
+
+/*!
+ * \brief Returns the sum of two timevals a + b
+ */
+struct timeval ast_tvadd(struct timeval a, struct timeval b);
+
+/*!
+ * \brief Returns the difference of two timevals a - b
+ */
+struct timeval ast_tvsub(struct timeval a, struct timeval b);
+
+/*!
+ * \brief Returns a timeval from sec, usec
+ */
+AST_INLINE_API(
+struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec),
+{
+ struct timeval t;
+ t.tv_sec = sec;
+ t.tv_usec = usec;
+ return t;
+}
+)
+
+/*!
+ * \brief Returns a timeval corresponding to the duration of n samples at rate r.
+ * Useful to convert samples to timevals, or even milliseconds to timevals
+ * in the form ast_samp2tv(milliseconds, 1000)
+ */
+AST_INLINE_API(
+struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate),
+{
+ return ast_tv(_nsamp / _rate, (_nsamp % _rate) * (1000000 / _rate));
+}
+)
+
+#endif /* _ASTERISK_TIME_H */
diff --git a/trunk/include/asterisk/transcap.h b/trunk/include/asterisk/transcap.h
new file mode 100644
index 000000000..1eca28d6d
--- /dev/null
+++ b/trunk/include/asterisk/transcap.h
@@ -0,0 +1,46 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Matthew Fredrickson <creslin@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 General Asterisk channel transcoding definitions.
+ */
+
+#ifndef _ASTERISK_TRANSCAP_H
+#define _ASTERISK_TRANSCAP_H
+
+/* These definitions are taken directly out of libpri.h and used here.
+ * DO NOT change them as it will cause unexpected behavior in channels
+ * that utilize these fields.
+ */
+
+/*! \name AstTranscode General Asterisk channel transcoding definitions.
+*/
+/*@{ */
+#define AST_TRANS_CAP_SPEECH 0x0
+#define AST_TRANS_CAP_DIGITAL 0x08
+#define AST_TRANS_CAP_RESTRICTED_DIGITAL 0x09
+#define AST_TRANS_CAP_3_1K_AUDIO 0x10
+#define AST_TRANS_CAP_7K_AUDIO 0x11 /* Depriciated ITU Q.931 (05/1998)*/
+#define AST_TRANS_CAP_DIGITAL_W_TONES 0x11
+#define AST_TRANS_CAP_VIDEO 0x18
+/*@} */
+
+#define IS_DIGITAL(cap)\
+ (cap) & AST_TRANS_CAP_DIGITAL ? 1 : 0
+
+#endif /* _ASTERISK_TRANSCAP_H */
diff --git a/trunk/include/asterisk/translate.h b/trunk/include/asterisk/translate.h
new file mode 100644
index 000000000..97f10283f
--- /dev/null
+++ b/trunk/include/asterisk/translate.h
@@ -0,0 +1,273 @@
+/*
+ * 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 Support for translation of data formats.
+ * \ref translate.c
+ */
+
+#ifndef _ASTERISK_TRANSLATE_H
+#define _ASTERISK_TRANSLATE_H
+
+#define MAX_AUDIO_FORMAT 15 /* Do not include video here */
+#define MAX_FORMAT 32 /* Do include video here */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if 1 /* need lots of stuff... */
+#include "asterisk/frame.h"
+#include "asterisk/plc.h"
+#include "asterisk/linkedlists.h"
+// XXX #include "asterisk/module.h"
+#endif
+
+struct ast_trans_pvt; /* declared below */
+
+/*! \brief
+ * Descriptor of a translator.
+ *
+ * Name, callbacks, and various options
+ * related to run-time operation (size of buffers, auxiliary
+ * descriptors, etc).
+ *
+ * A codec registers itself by filling the relevant fields
+ * of a structure and passing it as an argument to
+ * ast_register_translator(). The structure should not be
+ * modified after a successful registration, and its address
+ * must be used as an argument to ast_unregister_translator().
+ *
+ * As a minimum, a translator should supply name, srcfmt and dstfmt,
+ * the required buf_size (in bytes) and buffer_samples (in samples),
+ * and a few callbacks (framein, frameout, sample).
+ * The outbuf is automatically prepended by AST_FRIENDLY_OFFSET
+ * spare bytes so generic routines can place data in there.
+ *
+ * Note, the translator is not supposed to do any memory allocation
+ * or deallocation, nor any locking, because all of this is done in
+ * the generic code.
+ *
+ * Translators using generic plc (packet loss concealment) should
+ * supply a non-zero plc_samples indicating the size (in samples)
+ * of artificially generated frames and incoming data.
+ * Generic plc is only available for dstfmt = SLINEAR
+ */
+struct ast_translator {
+ const char name[80]; /*!< Name of translator */
+ int srcfmt; /*!< Source format (note: bit position,
+ converted to index during registration) */
+ int dstfmt; /*!< Destination format (note: bit position,
+ converted to index during registration) */
+
+ int (*newpvt)(struct ast_trans_pvt *); /*!< initialize private data
+ associated with the translator */
+
+ int (*framein)(struct ast_trans_pvt *pvt, struct ast_frame *in);
+ /*!< Input frame callback. Store
+ (and possibly convert) input frame. */
+
+ struct ast_frame * (*frameout)(struct ast_trans_pvt *pvt);
+ /*!< Output frame callback. Generate a frame
+ with outbuf content. */
+
+ void (*destroy)(struct ast_trans_pvt *pvt);
+ /*!< cleanup private data, if needed
+ (often unnecessary). */
+
+ struct ast_frame * (*sample)(void); /*!< Generate an example frame */
+
+ /*! \brief size of outbuf, in samples. Leave it 0 if you want the framein
+ * callback deal with the frame. Set it appropriately if you
+ * want the code to checks if the incoming frame fits the
+ * outbuf (this is e.g. required for plc).
+ */
+ int buffer_samples; /*< size of outbuf, in samples */
+
+ /*! \brief size of outbuf, in bytes. Mandatory. The wrapper code will also
+ * allocate an AST_FRIENDLY_OFFSET space before.
+ */
+ int buf_size;
+
+ int desc_size; /*!< size of private descriptor in pvt->pvt, if any */
+ int plc_samples; /*!< set to the plc block size if used, 0 otherwise */
+ int useplc; /*!< current status of plc, changed at runtime */
+ int native_plc; /*!< true if the translator can do native plc */
+
+ struct ast_module *module; /* opaque reference to the parent module */
+
+ int cost; /*!< Cost in milliseconds for encoding/decoding 1 second of sound */
+ int active; /*!< Whether this translator should be used or not */
+ AST_LIST_ENTRY(ast_translator) list; /*!< link field */
+};
+
+/*! \brief
+ * Default structure for translators, with the basic fields and buffers,
+ * all allocated as part of the same chunk of memory. The buffer is
+ * preceded by \ref AST_FRIENDLY_OFFSET bytes in front of the user portion.
+ * 'buf' points right after this space.
+ *
+ * *_framein() routines operate in two ways:
+ * 1. some convert on the fly and place the data directly in outbuf;
+ * in this case 'samples' and 'datalen' contain the number of samples
+ * and number of bytes available in the buffer.
+ * In this case we can use a generic *_frameout() routine that simply
+ * takes whatever is there and places it into the output frame.
+ * 2. others simply store the (unconverted) samples into a working
+ * buffer, and leave the conversion task to *_frameout().
+ * In this case, the intermediate buffer must be in the private
+ * descriptor, 'datalen' is left to 0, while 'samples' is still
+ * updated with the number of samples received.
+ */
+struct ast_trans_pvt {
+ struct ast_translator *t;
+ struct ast_frame f; /*!< used in frameout */
+ int samples; /*!< samples available in outbuf */
+ /*! \brief actual space used in outbuf */
+ int datalen;
+ void *pvt; /*!< more private data, if any */
+ char *outbuf; /*!< the useful portion of the buffer */
+ plc_state_t *plc; /*!< optional plc pointer */
+ struct ast_trans_pvt *next; /*!< next in translator chain */
+ struct timeval nextin;
+ struct timeval nextout;
+ unsigned int destroy:1;
+};
+
+/*! \brief generic frameout function */
+struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt,
+ int datalen, int samples);
+
+struct ast_trans_pvt;
+
+/*!
+ * \brief Register a translator
+ * This registers a codec translator with asterisk
+ * \param t populated ast_translator structure
+ * \param module handle to the module that owns this translator
+ * \return 0 on success, -1 on failure
+ */
+int __ast_register_translator(struct ast_translator *t, struct ast_module *module);
+
+/*! \brief See \ref __ast_register_translator() */
+#define ast_register_translator(t) __ast_register_translator(t, ast_module_info->self)
+
+/*!
+ * \brief Unregister a translator
+ * Unregisters the given tranlator
+ * \param t translator to unregister
+ * \return 0 on success, -1 on failure
+ */
+int ast_unregister_translator(struct ast_translator *t);
+
+/*!
+ * \brief Activate a previously deactivated translator
+ * \param t translator to activate
+ * \return nothing
+ *
+ * Enables the specified translator for use.
+ */
+void ast_translator_activate(struct ast_translator *t);
+
+/*!
+ * \brief Deactivate a translator
+ * \param t translator to deactivate
+ * \return nothing
+ *
+ * Disables the specified translator from being used.
+ */
+void ast_translator_deactivate(struct ast_translator *t);
+
+/*!
+ * \brief Chooses the best translation path
+ *
+ * Given a list of sources, and a designed destination format, which should
+ * I choose?
+ * \return Returns 0 on success, -1 if no path could be found.
+ * \note Modifies dests and srcs in place
+ */
+int ast_translator_best_choice(int *dsts, int *srcs);
+
+/*!
+ * \brief Builds a translator path
+ * Build a path (possibly NULL) from source to dest
+ * \param dest destination format
+ * \param source source format
+ * \return ast_trans_pvt on success, NULL on failure
+ * */
+struct ast_trans_pvt *ast_translator_build_path(int dest, int source);
+
+/*!
+ * \brief Frees a translator path
+ * Frees the given translator path structure
+ * \param tr translator path to get rid of
+ */
+void ast_translator_free_path(struct ast_trans_pvt *tr);
+
+/*!
+ * \brief translates one or more frames
+ * Apply an input frame into the translator and receive zero or one output frames. Consume
+ * determines whether the original frame should be freed
+ * \param tr translator structure to use for translation
+ * \param f frame to translate
+ * \param consume Whether or not to free the original frame
+ * \return an ast_frame of the new translation format on success, NULL on failure
+ */
+struct ast_frame *ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, int consume);
+
+/*!
+ * \brief Returns the number of steps required to convert from 'src' to 'dest'.
+ * \param dest destination format
+ * \param src source format
+ * \return the number of translation steps required, or -1 if no path is available
+ */
+unsigned int ast_translate_path_steps(unsigned int dest, unsigned int src);
+
+/*!
+ * \brief Mask off unavailable formats from a format bitmask
+ * \param dest possible destination formats
+ * \param src source formats
+ * \return the destination formats that are available in the source or translatable
+ *
+ * The result will include all formats from 'dest' that are either present
+ * in 'src' or translatable from a format present in 'src'.
+ *
+ * \note Only a single audio format and a single video format can be
+ * present in 'src', or the function will produce unexpected results.
+ */
+unsigned int ast_translate_available_formats(unsigned int dest, unsigned int src);
+
+/*!
+ * \brief Hint that a frame from a translator has been freed
+ *
+ * This is sort of a hack. This function gets called when ast_frame_free() gets
+ * called on a frame that has the AST_FRFLAG_FROM_TRANSLATOR flag set. This is
+ * because it is possible for a translation path to be destroyed while a frame
+ * from a translator is still in use. Specifically, this happens if a masquerade
+ * happens after a call to ast_read() but before the frame is done being processed,
+ * since the frame processing is generally done without the channel lock held.
+ *
+ * \return nothing
+ */
+void ast_translate_frame_freed(struct ast_frame *fr);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_TRANSLATE_H */
diff --git a/trunk/include/asterisk/udptl.h b/trunk/include/asterisk/udptl.h
new file mode 100644
index 000000000..74fcf357d
--- /dev/null
+++ b/trunk/include/asterisk/udptl.h
@@ -0,0 +1,127 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * UDPTL support for T.38
+ *
+ * Copyright (C) 2005, Steve Underwood, partly based on RTP code which is
+ * Copyright (C) 1999-2004, Digium, Inc.
+ *
+ * Steve Underwood <steveu@coppice.org>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ * \brief UDPTL support for T.38
+ * \author Steve Underwood <steveu@coppice.org>
+ * \ref udptl.c
+ * \todo add doxygen documentation to this file!
+ */
+
+
+#ifndef _ASTERISK_UDPTL_H
+#define _ASTERISK_UDPTL_H
+
+#include "asterisk/network.h"
+#include "asterisk/frame.h"
+#include "asterisk/io.h"
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+
+
+enum {
+ UDPTL_ERROR_CORRECTION_NONE,
+ UDPTL_ERROR_CORRECTION_FEC,
+ UDPTL_ERROR_CORRECTION_REDUNDANCY
+};
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct ast_udptl_protocol {
+ /*! \brief Get UDPTL struct, or NULL if unwilling to transfer */
+ struct ast_udptl *(*get_udptl_info)(struct ast_channel *chan);
+ /*! \brief Set UDPTL peer */
+ int (* const set_udptl_peer)(struct ast_channel *chan, struct ast_udptl *peer);
+ const char * const type;
+ AST_RWLIST_ENTRY(ast_udptl_protocol) list;
+};
+
+struct ast_udptl;
+
+typedef int (*ast_udptl_callback)(struct ast_udptl *udptl, struct ast_frame *f, void *data);
+
+struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context *io, int callbackmode);
+
+struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr in);
+
+void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them);
+
+void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them);
+
+void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us);
+
+void ast_udptl_destroy(struct ast_udptl *udptl);
+
+void ast_udptl_reset(struct ast_udptl *udptl);
+
+void ast_udptl_set_callback(struct ast_udptl *udptl, ast_udptl_callback callback);
+
+void ast_udptl_set_data(struct ast_udptl *udptl, void *data);
+
+int ast_udptl_write(struct ast_udptl *udptl, struct ast_frame *f);
+
+struct ast_frame *ast_udptl_read(struct ast_udptl *udptl);
+
+int ast_udptl_fd(struct ast_udptl *udptl);
+
+int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos);
+
+void ast_udptl_set_m_type(struct ast_udptl* udptl, int pt);
+
+void ast_udptl_set_udptlmap_type(struct ast_udptl* udptl, int pt,
+ char* mimeType, char* mimeSubtype);
+
+int ast_udptl_lookup_code(struct ast_udptl* udptl, int isAstFormat, int code);
+
+void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local);
+
+int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl);
+
+void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec);
+
+int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl);
+
+void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram);
+
+int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl);
+
+void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram);
+
+void ast_udptl_get_current_formats(struct ast_udptl* udptl,
+ int* astFormats, int* nonAstFormats);
+
+void ast_udptl_setnat(struct ast_udptl *udptl, int nat);
+
+int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc);
+
+int ast_udptl_proto_register(struct ast_udptl_protocol *proto);
+
+void ast_udptl_proto_unregister(struct ast_udptl_protocol *proto);
+
+void ast_udptl_stop(struct ast_udptl *udptl);
+
+void ast_udptl_init(void);
+
+void ast_udptl_reload(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/trunk/include/asterisk/ulaw.h b/trunk/include/asterisk/ulaw.h
new file mode 100644
index 000000000..0f3eae58b
--- /dev/null
+++ b/trunk/include/asterisk/ulaw.h
@@ -0,0 +1,87 @@
+/*
+ * 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 u-Law to Signed linear conversion
+ */
+
+#ifndef _ASTERISK_ULAW_H
+#define _ASTERISK_ULAW_H
+
+
+/*!
+ * To init the ulaw to slinear conversion stuff, this needs to be run.
+ */
+void ast_ulaw_init(void);
+
+#define AST_ULAW_BIT_LOSS 3
+#define AST_ULAW_STEP (1 << AST_ULAW_BIT_LOSS)
+#define AST_ULAW_TAB_SIZE (32768 / AST_ULAW_STEP + 1)
+#define AST_ULAW_SIGN_BIT 0x80
+
+/*! \brief converts signed linear to mulaw */
+#ifndef G711_NEW_ALGORITHM
+extern unsigned char __ast_lin2mu[16384];
+#else
+extern unsigned char __ast_lin2mu[AST_ULAW_TAB_SIZE];
+#endif
+
+/*! help */
+extern short __ast_mulaw[256];
+
+#ifndef G711_NEW_ALGORITHM
+
+#define AST_LIN2MU(a) (__ast_lin2mu[((unsigned short)(a)) >> 2])
+
+#else
+
+#define AST_LIN2MU_LOOKUP(mag) \
+ __ast_lin2mu[((mag) + AST_ULAW_STEP / 2) >> AST_ULAW_BIT_LOSS]
+
+
+/*! \brief convert signed linear sample to sign-magnitude pair for u-Law */
+static inline void ast_ulaw_get_sign_mag(short sample, unsigned *sign, unsigned *mag)
+{
+ /* It may look illogical to retrive the sign this way in both cases,
+ * but this helps gcc eliminate the branch below and produces
+ * faster code */
+ *sign = ((unsigned short)sample >> 8) & AST_ULAW_SIGN_BIT;
+#if defined(G711_REDUCED_BRANCHING)
+ {
+ unsigned dual_mag = (-sample << 16) | (unsigned short)sample;
+ *mag = (dual_mag >> (*sign >> 3)) & 0xffffU;
+ }
+#else
+ if (sample < 0)
+ *mag = -sample;
+ else
+ *mag = sample;
+#endif /* G711_REDUCED_BRANCHING */
+}
+
+static inline unsigned char AST_LIN2MU(short sample)
+{
+ unsigned mag, sign;
+ ast_ulaw_get_sign_mag(sample, &sign, &mag);
+ return ~(sign | AST_LIN2MU_LOOKUP(mag));
+}
+#endif
+
+#define AST_MULAW(a) (__ast_mulaw[(a)])
+
+#endif /* _ASTERISK_ULAW_H */
diff --git a/trunk/include/asterisk/unaligned.h b/trunk/include/asterisk/unaligned.h
new file mode 100644
index 000000000..c502efede
--- /dev/null
+++ b/trunk/include/asterisk/unaligned.h
@@ -0,0 +1,102 @@
+/*
+ * 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 Handle unaligned data access
+ */
+
+#ifndef _ASTERISK_UNALIGNED_H
+#define _ASTERISK_UNALIGNED_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#ifdef __GNUC__
+/* If we just tell GCC what's going on, we can trust it to behave optimally */
+static inline unsigned int get_unaligned_uint32(const void *p)
+{
+ const struct { unsigned int d; } __attribute__((packed)) *pp = p;
+
+ return pp->d;
+}
+static inline unsigned short get_unaligned_uint16(const void *p)
+{
+ const struct { unsigned short d; } __attribute__((packed)) *pp = p;
+
+ return pp->d;
+}
+
+static inline void put_unaligned_uint32(void *p, unsigned int datum)
+{
+ struct { unsigned int d; } __attribute__((packed)) *pp = p;
+
+ pp->d = datum;
+}
+
+static inline void put_unaligned_uint16(void *p, unsigned short datum)
+{
+ struct { unsigned short d; } __attribute__((packed)) *pp = p;
+
+ pp->d = datum;
+}
+#elif defined(SOLARIS) && defined(__sparc__)
+static inline unsigned int get_unaligned_uint32(const void *p)
+{
+ const unsigned char *cp = p;
+
+ return (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | cp[3];
+}
+
+static inline unsigned short get_unaligned_uint16(const void *p)
+{
+ const unsigned char *cp = p;
+
+ return (cp[0] << 8) | cp[1] ;
+}
+
+static inline void put_unaligned_uint32(void *p, unsigned int datum)
+{
+ unsigned char *cp = p;
+
+ cp[0] = datum >> 24;
+ cp[1] = datum >> 16;
+ cp[2] = datum >> 8;
+ cp[3] = datum;
+}
+
+static inline void put_unaligned_uint16(void *p, unsigned int datum)
+{
+ unsigned char *cp = p;
+
+ cp[0] = datum >> 8;
+ cp[1] = datum;
+}
+#else /* Not GCC, not Solaris/SPARC. Assume we can handle direct load/store. */
+#define get_unaligned_uint32(p) (*((unsigned int *)(p)))
+#define get_unaligned_uint16(p) (*((unsigned short *)(p)))
+#define put_unaligned_uint32(p,d) do { unsigned int *__P = (p); *__P = d; } while(0)
+#define put_unaligned_uint16(p,d) do { unsigned short *__P = (p); *__P = d; } while(0)
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+
+#endif /* _ASTERISK_UNALIGNED_H */
diff --git a/trunk/include/asterisk/utils.h b/trunk/include/asterisk/utils.h
new file mode 100644
index 000000000..a9c8d5f0c
--- /dev/null
+++ b/trunk/include/asterisk/utils.h
@@ -0,0 +1,657 @@
+/*
+ * 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 Utility functions
+ */
+
+#ifndef _ASTERISK_UTILS_H
+#define _ASTERISK_UTILS_H
+
+#include "asterisk/network.h"
+
+#include <time.h> /* we want to override localtime_r */
+
+#include "asterisk/lock.h"
+#include "asterisk/time.h"
+#include "asterisk/logger.h"
+#include "asterisk/localtime.h"
+
+/*!
+\note \verbatim
+ Note:
+ It is very important to use only unsigned variables to hold
+ bit flags, as otherwise you can fall prey to the compiler's
+ sign-extension antics if you try to use the top two bits in
+ your variable.
+
+ The flag macros below use a set of compiler tricks to verify
+ that the caller is using an "unsigned int" variable to hold
+ the flags, and nothing else. If the caller uses any other
+ type of variable, a warning message similar to this:
+
+ warning: comparison of distinct pointer types lacks cast
+ will be generated.
+
+ The "dummy" variable below is used to make these comparisons.
+
+ Also note that at -O2 or above, this type-safety checking
+ does _not_ produce any additional object code at all.
+ \endverbatim
+*/
+
+extern unsigned int __unsigned_int_flags_dummy;
+
+#define ast_test_flag(p,flag) ({ \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags & (flag)); \
+ })
+
+#define ast_set_flag(p,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags |= (flag)); \
+ } while(0)
+
+#define ast_clear_flag(p,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags &= ~(flag)); \
+ } while(0)
+
+#define ast_copy_flags(dest,src,flagz) do { \
+ typeof ((dest)->flags) __d = (dest)->flags; \
+ typeof ((src)->flags) __s = (src)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__d == &__x); \
+ (void) (&__s == &__x); \
+ (dest)->flags &= ~(flagz); \
+ (dest)->flags |= ((src)->flags & (flagz)); \
+ } while (0)
+
+#define ast_set2_flag(p,value,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ if (value) \
+ (p)->flags |= (flag); \
+ else \
+ (p)->flags &= ~(flag); \
+ } while (0)
+
+#define ast_set_flags_to(p,flag,value) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ (p)->flags &= ~(flag); \
+ (p)->flags |= (value); \
+ } while (0)
+
+
+/* The following 64-bit flag code can most likely be erased after app_dial
+ is reorganized to either reduce the large number of options, or handle
+ them in some other way. At the time of this writing, app_dial would be
+ the only user of 64-bit option flags */
+
+extern uint64_t __unsigned_int_flags_dummy64;
+
+#define ast_test_flag64(p,flag) ({ \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy64) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags & (flag)); \
+ })
+
+#define ast_set_flag64(p,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy64) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags |= (flag)); \
+ } while(0)
+
+#define ast_clear_flag64(p,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy64) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags &= ~(flag)); \
+ } while(0)
+
+#define ast_copy_flags64(dest,src,flagz) do { \
+ typeof ((dest)->flags) __d = (dest)->flags; \
+ typeof ((src)->flags) __s = (src)->flags; \
+ typeof (__unsigned_int_flags_dummy64) __x = 0; \
+ (void) (&__d == &__x); \
+ (void) (&__s == &__x); \
+ (dest)->flags &= ~(flagz); \
+ (dest)->flags |= ((src)->flags & (flagz)); \
+ } while (0)
+
+#define ast_set2_flag64(p,value,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy64) __x = 0; \
+ (void) (&__p == &__x); \
+ if (value) \
+ (p)->flags |= (flag); \
+ else \
+ (p)->flags &= ~(flag); \
+ } while (0)
+
+#define ast_set_flags_to64(p,flag,value) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy64) __x = 0; \
+ (void) (&__p == &__x); \
+ (p)->flags &= ~(flag); \
+ (p)->flags |= (value); \
+ } while (0)
+
+
+/* Non-type checking variations for non-unsigned int flags. You
+ should only use non-unsigned int flags where required by
+ protocol etc and if you know what you're doing :) */
+#define ast_test_flag_nonstd(p,flag) \
+ ((p)->flags & (flag))
+
+#define ast_set_flag_nonstd(p,flag) do { \
+ ((p)->flags |= (flag)); \
+ } while(0)
+
+#define ast_clear_flag_nonstd(p,flag) do { \
+ ((p)->flags &= ~(flag)); \
+ } while(0)
+
+#define ast_copy_flags_nonstd(dest,src,flagz) do { \
+ (dest)->flags &= ~(flagz); \
+ (dest)->flags |= ((src)->flags & (flagz)); \
+ } while (0)
+
+#define ast_set2_flag_nonstd(p,value,flag) do { \
+ if (value) \
+ (p)->flags |= (flag); \
+ else \
+ (p)->flags &= ~(flag); \
+ } while (0)
+
+#define AST_FLAGS_ALL UINT_MAX
+
+/*! \brief Structure used to handle boolean flags
+*/
+struct ast_flags {
+ unsigned int flags;
+};
+
+/*! \brief Structure used to handle a large number of boolean flags == used only in app_dial?
+*/
+struct ast_flags64 {
+ uint64_t flags;
+};
+
+struct ast_hostent {
+ struct hostent hp;
+ char buf[1024];
+};
+
+/*! \brief Thread-safe gethostbyname function to use in Asterisk */
+struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp);
+
+/*! \brief Produces MD5 hash based on input string */
+void ast_md5_hash(char *output, char *input);
+/*! \brief Produces SHA1 hash based on input string */
+void ast_sha1_hash(char *output, char *input);
+
+int ast_base64encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks);
+
+/*!
+ * \brief Encode data in base64
+ * \param dst the destination buffer
+ * \param src the source data to be encoded
+ * \param srclen the number of bytes present in the source buffer
+ * \param max the maximum number of bytes to write into the destination
+ * buffer, *including* the terminating NULL character.
+ */
+int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max);
+
+/*!
+ * \brief Decode data from base64
+ * \param dst the destination buffer
+ * \param src the source buffer
+ * \param max The maximum number of bytes to write into the destination
+ * buffer. Note that this function will not ensure that the
+ * destination buffer is NULL terminated. So, in general,
+ * this parameter should be sizeof(dst) - 1.
+ */
+int ast_base64decode(unsigned char *dst, const char *src, int max);
+
+/*! \brief Turn text string to URI-encoded %XX version
+
+\note At this point, we're converting from ISO-8859-x (8-bit), not UTF8
+ as in the SIP protocol spec
+ If doreserved == 1 we will convert reserved characters also.
+ RFC 2396, section 2.4
+ outbuf needs to have more memory allocated than the instring
+ to have room for the expansion. Every char that is converted
+ is replaced by three ASCII characters.
+ \param string String to be converted
+ \param outbuf Resulting encoded string
+ \param buflen Size of output buffer
+ \param doreserved Convert reserved characters
+*/
+
+char *ast_uri_encode(const char *string, char *outbuf, int buflen, int doreserved);
+
+/*! \brief Decode URI, URN, URL (overwrite string)
+ \param s String to be decoded
+ */
+void ast_uri_decode(char *s);
+
+static force_inline void ast_slinear_saturated_add(short *input, short *value)
+{
+ int res;
+
+ res = (int) *input + *value;
+ if (res > 32767)
+ *input = 32767;
+ else if (res < -32767)
+ *input = -32767;
+ else
+ *input = (short) res;
+}
+
+static force_inline void ast_slinear_saturated_subtract(short *input, short *value)
+{
+ int res;
+
+ res = (int) *input - *value;
+ if (res > 32767)
+ *input = 32767;
+ else if (res < -32767)
+ *input = -32767;
+ else
+ *input = (short) res;
+}
+
+static force_inline void ast_slinear_saturated_multiply(short *input, short *value)
+{
+ int res;
+
+ res = (int) *input * *value;
+ if (res > 32767)
+ *input = 32767;
+ else if (res < -32767)
+ *input = -32767;
+ else
+ *input = (short) res;
+}
+
+static force_inline void ast_slinear_saturated_divide(short *input, short *value)
+{
+ *input /= *value;
+}
+
+int test_for_thread_safety(void);
+
+#ifdef localtime_r
+#undef localtime_r
+#endif
+#define localtime_r __dont_use_localtime_r_use_ast_localtime_instead__
+
+int ast_utils_init(void);
+int ast_wait_for_input(int fd, int ms);
+
+/*!
+ \brief Try to write string, but wait no more than ms milliseconds
+ before timing out.
+
+ \note If you are calling ast_carefulwrite, it is assumed that you are calling
+ it on a file descriptor that _DOES_ have NONBLOCK set. This way,
+ there is only one system call made to do a write, unless we actually
+ have a need to wait. This way, we get better performance.
+*/
+int ast_carefulwrite(int fd, char *s, int len, int timeoutms);
+
+/*
+ * Thread management support (should be moved to lock.h or a different header)
+ */
+
+#define AST_STACKSIZE 240 * 1024
+
+#if defined(LOW_MEMORY)
+#define AST_BACKGROUND_STACKSIZE 48 * 1024
+#else
+#define AST_BACKGROUND_STACKSIZE 240 * 1024
+#endif
+
+void ast_register_thread(char *name);
+void ast_unregister_thread(void *id);
+
+int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *data, size_t stacksize, const char *file, const char *caller,
+ int line, const char *start_fn);
+
+int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, void*(*start_routine)(void *),
+ void *data, size_t stacksize, const char *file, const char *caller,
+ int line, const char *start_fn);
+
+#define ast_pthread_create(a, b, c, d) \
+ ast_pthread_create_stack(a, b, c, d, \
+ 0, __FILE__, __FUNCTION__, __LINE__, #c)
+
+#define ast_pthread_create_detached(a, b, c, d) \
+ ast_pthread_create_detached_stack(a, b, c, d, \
+ 0, __FILE__, __FUNCTION__, __LINE__, #c)
+
+#define ast_pthread_create_background(a, b, c, d) \
+ ast_pthread_create_stack(a, b, c, d, \
+ AST_BACKGROUND_STACKSIZE, \
+ __FILE__, __FUNCTION__, __LINE__, #c)
+
+#define ast_pthread_create_detached_background(a, b, c, d) \
+ ast_pthread_create_detached_stack(a, b, c, d, \
+ AST_BACKGROUND_STACKSIZE, \
+ __FILE__, __FUNCTION__, __LINE__, #c)
+
+/* End of thread management support */
+
+/*!
+ \brief Process a string to find and replace characters
+ \param start The string to analyze
+ \param find The character to find
+ \param replace_with The character that will replace the one we are looking for
+*/
+char *ast_process_quotes_and_slashes(char *start, char find, char replace_with);
+
+long int ast_random(void);
+
+#define ast_free free
+
+/*!
+ * \brief free() wrapper
+ *
+ * ast_free_ptr should be used when a function pointer for free() needs to be passed
+ * as the argument to a function. Otherwise, astmm will cause seg faults.
+ */
+#ifdef __AST_DEBUG_MALLOC
+static void ast_free_ptr(void *ptr) attribute_unused;
+static void ast_free_ptr(void *ptr)
+{
+ ast_free(ptr);
+}
+#else
+#define ast_free_ptr ast_free
+#endif
+
+#ifndef __AST_DEBUG_MALLOC
+
+#define MALLOC_FAILURE_MSG \
+ ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file);
+/*!
+ * \brief A wrapper for malloc()
+ *
+ * ast_malloc() is a wrapper for malloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The argument and return value are the same as malloc()
+ */
+#define ast_malloc(len) \
+ _ast_malloc((len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_malloc(size_t len, const char *file, int lineno, const char *func),
+{
+ void *p;
+
+ if (!(p = malloc(len)))
+ MALLOC_FAILURE_MSG;
+
+ return p;
+}
+)
+
+/*!
+ * \brief A wrapper for calloc()
+ *
+ * ast_calloc() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_calloc(size_t num, size_t len, const char *file, int lineno, const char *func),
+{
+ void *p;
+
+ if (!(p = calloc(num, len)))
+ MALLOC_FAILURE_MSG;
+
+ return p;
+}
+)
+
+/*!
+ * \brief A wrapper for calloc() for use in cache pools
+ *
+ * ast_calloc_cache() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails. When memory debugging is in use,
+ * the memory allocated by this function will be marked as 'cache' so it can be
+ * distinguished from normal memory allocations.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc_cache(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+/*!
+ * \brief A wrapper for realloc()
+ *
+ * ast_realloc() is a wrapper for realloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as realloc()
+ */
+#define ast_realloc(p, len) \
+ _ast_realloc((p), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_realloc(void *p, size_t len, const char *file, int lineno, const char *func),
+{
+ void *newp;
+
+ if (!(newp = realloc(p, len)))
+ MALLOC_FAILURE_MSG;
+
+ return newp;
+}
+)
+
+/*!
+ * \brief A wrapper for strdup()
+ *
+ * ast_strdup() is a wrapper for strdup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strdup(), unlike strdup(), can safely accept a NULL argument. If a NULL
+ * argument is provided, ast_strdup will return NULL without generating any
+ * kind of error log message.
+ *
+ * The argument and return value are the same as strdup()
+ */
+#define ast_strdup(str) \
+ _ast_strdup((str), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+char * attribute_malloc _ast_strdup(const char *str, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
+
+ if (str) {
+ if (!(newstr = strdup(str)))
+ MALLOC_FAILURE_MSG;
+ }
+
+ return newstr;
+}
+)
+
+/*!
+ * \brief A wrapper for strndup()
+ *
+ * ast_strndup() is a wrapper for strndup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strndup(), unlike strndup(), can safely accept a NULL argument for the
+ * string to duplicate. If a NULL argument is provided, ast_strdup will return
+ * NULL without generating any kind of error log message.
+ *
+ * The arguments and return value are the same as strndup()
+ */
+#define ast_strndup(str, len) \
+ _ast_strndup((str), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+char * attribute_malloc _ast_strndup(const char *str, size_t len, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
+
+ if (str) {
+ if (!(newstr = strndup(str, len)))
+ MALLOC_FAILURE_MSG;
+ }
+
+ return newstr;
+}
+)
+
+/*!
+ * \brief A wrapper for asprintf()
+ *
+ * ast_asprintf() is a wrapper for asprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as asprintf()
+ */
+#define ast_asprintf(ret, fmt, ...) \
+ _ast_asprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, __VA_ARGS__)
+
+AST_INLINE_API(
+int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...),
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+ va_end(ap);
+
+ return res;
+}
+)
+
+/*!
+ * \brief A wrapper for vasprintf()
+ *
+ * ast_vasprintf() is a wrapper for vasprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as vasprintf()
+ */
+#define ast_vasprintf(ret, fmt, ap) \
+ _ast_vasprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, (fmt), (ap))
+
+AST_INLINE_API(
+int _ast_vasprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, va_list ap),
+{
+ int res;
+
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+
+ return res;
+}
+)
+
+#else
+
+/* If astmm is in use, let it handle these. Otherwise, it will report that
+ all allocations are coming from this header file */
+
+#define ast_malloc(a) malloc(a)
+#define ast_calloc(a,b) calloc(a,b)
+#define ast_realloc(a,b) realloc(a,b)
+#define ast_strdup(a) strdup(a)
+#define ast_strndup(a,b) strndup(a,b)
+#define ast_asprintf(a,b,c) asprintf(a,b,c)
+#define ast_vasprintf(a,b,c) vasprintf(a,b,c)
+
+#endif /* AST_DEBUG_MALLOC */
+
+#if !defined(ast_strdupa) && defined(__GNUC__)
+/*!
+ \brief duplicate a string in memory from the stack
+ \param s The string to duplicate
+
+ This macro will duplicate the given string. It returns a pointer to the stack
+ allocatted memory for the new string.
+*/
+#define ast_strdupa(s) \
+ (__extension__ \
+ ({ \
+ const char *__old = (s); \
+ size_t __len = strlen(__old) + 1; \
+ char *__new = __builtin_alloca(__len); \
+ memcpy (__new, __old, __len); \
+ __new; \
+ }))
+#endif
+
+/*!
+ \brief Disable PMTU discovery on a socket
+ \param sock The socket to manipulate
+ \return Nothing
+
+ On Linux, UDP sockets default to sending packets with the Dont Fragment (DF)
+ bit set. This is supposedly done to allow the application to do PMTU
+ discovery, but Asterisk does not do this.
+
+ Because of this, UDP packets sent by Asterisk that are larger than the MTU
+ of any hop in the path will be lost. This function can be called on a socket
+ to ensure that the DF bit will not be set.
+ */
+void ast_enable_packet_fragmentation(int sock);
+
+/*!
+ \brief Recursively create directory path
+ \param path The directory path to create
+ \param mode The permissions with which to try to create the directory
+ \return 0 on success or an error code otherwise
+
+ Creates a directory path, creating parent directories as needed.
+ */
+int ast_mkdir(const char *path, int mode);
+
+#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
+
+#include "asterisk/strings.h"
+
+#endif /* _ASTERISK_UTILS_H */
diff --git a/trunk/include/asterisk/version.h b/trunk/include/asterisk/version.h
new file mode 100644
index 000000000..51ff48102
--- /dev/null
+++ b/trunk/include/asterisk/version.h
@@ -0,0 +1,44 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2008, Digium, Inc.
+ *
+ * Russell Bryant <russell@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 Asterisk version information
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#ifndef __AST_VERSION_H
+#define __AST_VERSION_H
+
+/*!
+ * \brief Retrieve the Asterisk version string.
+ */
+const char *ast_get_version(void);
+
+/*!
+ * \brief Retrieve the numeric Asterisk version
+ *
+ * Format ABBCC
+ * AABB - Major version (1.4 would be 104)
+ * CC - Minor version
+ *
+ * 1.4.17 would be 10417.
+ */
+const char *ast_get_version_num(void);
+
+#endif /* __AST_VERSION_H */
diff --git a/trunk/include/asterisk/zapata.h b/trunk/include/asterisk/zapata.h
new file mode 100644
index 000000000..50df73a58
--- /dev/null
+++ b/trunk/include/asterisk/zapata.h
@@ -0,0 +1,48 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * 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.
+ *
+ * Sometimes one really wonders why we need a copyright
+ * for less than ten lines of preprocessor directives...
+ */
+
+/*! \file
+ * \brief Stub to find zaptel headers
+*
+ * Stub to find the zaptel headers. The configure script will
+ * define HAVE_ZAPTEL_VERSION according to what it has found.
+ * Applications should include "zapata.h" and not (directly)
+ * <foo/zaptel.h> or <foo/tonezone.h>.
+ * For the mapping of version numbers to location see below.
+ *
+ */
+#ifndef _AST_ZAPATA_H
+#define _AST_ZAPATA_H
+
+#ifdef HAVE_ZAPTEL
+#include <sys/ioctl.h>
+
+#if defined(HAVE_ZAPTEL_VERSION) && HAVE_ZAPTEL_VERSION < 100
+/* Very old versions of zaptel drivers on FreeBSD install in ${PREFIX} */
+#include <zaptel.h>
+#include <tonezone.h>
+#else
+/* newer versions install in ${PREFIX}/zaptel */
+#include <zaptel/zaptel.h>
+#include <zaptel/tonezone.h>
+#endif /* HAVE_ZAPTEL_VERSION < 100 */
+
+#endif /* HAVE_ZAPTEL */
+
+#endif /* _AST_ZAPATA_H */
diff --git a/trunk/include/jitterbuf.h b/trunk/include/jitterbuf.h
new file mode 100644
index 000000000..2a6045f05
--- /dev/null
+++ b/trunk/include/jitterbuf.h
@@ -0,0 +1,174 @@
+/*
+ * jitterbuf: an application-independent jitterbuffer
+ *
+ * Copyrights:
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.
+ *
+ * Contributors:
+ * Steve Kann <stevek@stevek.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU Lesser (Library) General Public License
+ *
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
+ */
+
+/*! \file
+ * \brief
+ * jitterbuf: an application-independent jitterbuffer
+ * \ref jitterbuf.c
+ */
+
+
+#ifndef _JITTERBUF_H_
+#define _JITTERBUF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \name configuration constants */
+/*@{ */
+ /*! Number of historical timestamps to use in calculating jitter and drift */
+#define JB_HISTORY_SZ 500
+ /*! what percentage of timestamps should we drop from the history when we examine it;
+ * this might eventually be something made configurable */
+#define JB_HISTORY_DROPPCT 3
+ /*! the maximum droppct we can handle (say it was configurable). */
+#define JB_HISTORY_DROPPCT_MAX 4
+ /*! the size of the buffer we use to keep the top and botton timestamps for dropping */
+#define JB_HISTORY_MAXBUF_SZ JB_HISTORY_SZ * JB_HISTORY_DROPPCT_MAX / 100
+ /*! amount of additional jitterbuffer adjustment */
+#define JB_TARGET_EXTRA 40
+ /*! ms between growing and shrinking; may not be honored if jitterbuffer runs out of space */
+#define JB_ADJUST_DELAY 40
+/*@} */
+
+enum jb_return_code {
+ /* return codes */
+ JB_OK, /* 0 */
+ JB_EMPTY, /* 1 */
+ JB_NOFRAME, /* 2 */
+ JB_INTERP, /* 3 */
+ JB_DROP, /* 4 */
+ JB_SCHED /* 5 */
+};
+
+enum jb_frame_type {
+ /* frame types */
+ JB_TYPE_CONTROL, /*!< 0 */
+ JB_TYPE_VOICE, /*!< 1 */
+ JB_TYPE_VIDEO, /*!< 2 - reserved */
+ JB_TYPE_SILENCE /*!< 3 */
+};
+
+typedef struct jb_conf {
+ /* settings */
+ long max_jitterbuf; /*!< defines a hard clamp to use in setting the jitter buffer delay */
+ long resync_threshold; /*!< the jb will resync when delay increases to (2 * jitter) + this param */
+ long max_contig_interp; /*!< the max interp frames to return in a row */
+ long target_extra ; /*!< amount of additional jitterbuffer adjustment, overrides JB_TARGET_EXTRA */
+} jb_conf;
+
+typedef struct jb_info {
+ jb_conf conf;
+
+ /* statistics */
+ long frames_in; /*!< number of frames input to the jitterbuffer.*/
+ long frames_out; /*!< number of frames output from the jitterbuffer.*/
+ long frames_late; /*!< number of frames which were too late, and dropped.*/
+ long frames_lost; /*!< number of missing frames.*/
+ long frames_dropped; /*!< number of frames dropped (shrinkage) */
+ long frames_ooo; /*!< number of frames received out-of-order */
+ long frames_cur; /*!< number of frames presently in jb, awaiting delivery.*/
+ long jitter; /*!< jitter measured within current history interval*/
+ long min; /*!< minimum lateness within current history interval */
+ long current; /*!< the present jitterbuffer adjustment */
+ long target; /*!< the target jitterbuffer adjustment */
+ long losspct; /*!< recent lost frame percentage (* 1000) */
+ long next_voice_ts; /*!< the ts of the next frame to be read from the jb - in receiver's time */
+ long last_voice_ms; /*!< the duration of the last voice frame */
+ long silence_begin_ts; /*!< the time of the last CNG frame, when in silence */
+ long last_adjustment; /*!< the time of the last adjustment */
+ long last_delay; /*!< the last now added to history */
+ long cnt_delay_discont; /*!< the count of discontinuous delays */
+ long resync_offset; /*!< the amount to offset ts to support resyncs */
+ long cnt_contig_interp; /*!< the number of contiguous interp frames returned */
+} jb_info;
+
+typedef struct jb_frame {
+ void *data; /* the frame data */
+ long ts; /* the relative delivery time expected */
+ long ms; /* the time covered by this frame, in sec/8000 */
+ enum jb_frame_type type; /* the type of frame */
+ struct jb_frame *next, *prev;
+} jb_frame;
+
+typedef struct jitterbuf {
+ jb_info info;
+
+ /* history */
+ long history[JB_HISTORY_SZ]; /*!< history */
+ int hist_ptr; /*!< points to index in history for next entry */
+ long hist_maxbuf[JB_HISTORY_MAXBUF_SZ]; /*!< a sorted buffer of the max delays (highest first) */
+ long hist_minbuf[JB_HISTORY_MAXBUF_SZ]; /*!< a sorted buffer of the min delays (lowest first) */
+ int hist_maxbuf_valid; /*!< are the "maxbuf"/minbuf valid? */
+ unsigned int dropem:1; /*!< flag to indicate dropping frames (overload) */
+
+ jb_frame *frames; /*!< queued frames */
+ jb_frame *free; /*!< free frames (avoid malloc?) */
+} jitterbuf;
+
+
+/*! \brief new jitterbuf */
+jitterbuf * jb_new(void);
+
+/*! \brief destroy jitterbuf */
+void jb_destroy(jitterbuf *jb);
+
+/*! \brief reset jitterbuf
+ * \note The jitterbuffer should be empty before you call this, otherwise
+ * you will leak queued frames, and some internal structures */
+void jb_reset(jitterbuf *jb);
+
+/*!\brief queue a frame
+ *
+ * data=frame data, timings (in ms): ms=length of frame (for voice), ts=ts (sender's time)
+ * now=now (in receiver's time) return value is one of
+ * JB_OK: Frame added. Last call to jb_next() still valid
+ * JB_DROP: Drop this frame immediately
+ * JB_SCHED: Frame added. Call jb_next() to get a new time for the next frame
+ */
+enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now);
+
+/*! \brief get a frame for time now (receiver's time) return value is one of
+ * JB_OK: You've got frame!
+ * JB_DROP: Here's an audio frame you should just drop. Ask me again for this time..
+ * JB_NOFRAME: There's no frame scheduled for this time.
+ * JB_INTERP: Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame)
+ * JB_EMPTY: The jb is empty.
+ */
+enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frame, long now, long interpl);
+
+/*! \brief unconditionally get frames from jitterbuf until empty */
+enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout);
+
+/*! \brief when is the next frame due out, in receiver's time (0=EMPTY)
+ * This value may change as frames are added (esp non-audio frames) */
+long jb_next(jitterbuf *jb);
+
+/*! \brief get jitterbuf info: only "statistics" may be valid */
+enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats);
+
+/*! \brief set jitterbuf conf */
+enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf);
+
+typedef void (*jb_output_function_t)(const char *fmt, ...);
+void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/trunk/include/solaris-compat/compat.h b/trunk/include/solaris-compat/compat.h
new file mode 100644
index 000000000..b34cf11f4
--- /dev/null
+++ b/trunk/include/solaris-compat/compat.h
@@ -0,0 +1,46 @@
+#ifndef _SOLARIS_COMPAT_H
+#define _SOLARIS_COMPAT_H
+
+#define __BEGIN_DECLS
+#define __END_DECLS
+
+#ifndef __P
+#define __P(p) p
+#endif
+
+#include <alloca.h>
+#include <strings.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <netinet/in.h>
+
+#ifndef BYTE_ORDER
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+
+#ifdef __sparc__
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
+
+#ifndef __BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __BYTE_ORDER BYTE_ORDER
+#endif
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int u_int32_t;
+#endif
+
+char* strsep(char** str, const char* delims);
+int setenv(const char *name, const char *value, int overwrite);
+int unsetenv(const char *name);
+#endif
diff --git a/trunk/include/solaris-compat/sys/cdefs.h b/trunk/include/solaris-compat/sys/cdefs.h
new file mode 100644
index 000000000..40f76af87
--- /dev/null
+++ b/trunk/include/solaris-compat/sys/cdefs.h
@@ -0,0 +1,10 @@
+#ifndef __SYS_CDEFS_H_
+#define __SYS_CDEFS_H_
+
+#define __BEGIN_DECLS
+#define __END_DECLS
+
+#define __P(p) p
+
+
+#endif
diff --git a/trunk/include/solaris-compat/sys/queue.h b/trunk/include/solaris-compat/sys/queue.h
new file mode 100644
index 000000000..ac273dfe3
--- /dev/null
+++ b/trunk/include/solaris-compat/sys/queue.h
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.24.2.4 2000/05/05 01:41:41 archie Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ CIRCLEQ
+ * _HEAD + + + + +
+ * _ENTRY + + + + +
+ * _INIT + + + + +
+ * _EMPTY + + + + +
+ * _FIRST + + + + +
+ * _NEXT + + + + +
+ * _PREV - - - + +
+ * _LAST - - + + +
+ * _FOREACH + + + + +
+ * _FOREACH_REVERSE - - - + +
+ * _INSERT_HEAD + + + + +
+ * _INSERT_BEFORE - + - + +
+ * _INSERT_AFTER + + + + +
+ * _INSERT_TAIL - - + + +
+ * _REMOVE_HEAD + - + - -
+ * _REMOVE + + + + +
+ *
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+#define SLIST_INIT(head) { \
+ (head)->slh_first = NULL; \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * Singly-linked Tail queue definitions.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (0)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+#define STAILQ_LAST(head) (*(head)->stqh_last)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = (head)->stqh_first; (var); (var) = (var)->field.stqe_next)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (tqelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (tqelm)->field.stqe_next = (elm); \
+} while (0)
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = \
+ (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
+ if (((head)->stqh_first = (elm)->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ STAILQ_REMOVE_HEAD(head, field); \
+ } \
+ else { \
+ struct type *curelm = (head)->stqh_first; \
+ while( curelm->field.stqe_next != (elm) ) \
+ curelm = curelm->field.stqe_next; \
+ if((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = (head)->lh_first; (var); (var) = (var)->field.le_next)
+
+#define LIST_INIT(head) do { \
+ (head)->lh_first = NULL; \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = (head)->cqh_first; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_next)
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = (head)->cqh_last; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_prev)
+
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+
+#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#ifdef KERNEL
+
+/*
+ * XXX insque() and remque() are an old way of handling certain queues.
+ * They bogusly assumes that all queue heads look alike.
+ */
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+#ifdef __GNUC__
+
+static __inline void
+insque(void *a, void *b)
+{
+ struct quehead *element = a, *head = b;
+
+ element->qh_link = head->qh_link;
+ element->qh_rlink = head;
+ head->qh_link = element;
+ element->qh_link->qh_rlink = element;
+}
+
+static __inline void
+remque(void *a)
+{
+ struct quehead *element = a;
+
+ element->qh_link->qh_rlink = element->qh_rlink;
+ element->qh_rlink->qh_link = element->qh_link;
+ element->qh_rlink = 0;
+}
+
+#else /* !__GNUC__ */
+
+void insque __P((void *a, void *b));
+void remque __P((void *a));
+
+#endif /* __GNUC__ */
+
+#endif /* KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
+
diff --git a/trunk/install-sh b/trunk/install-sh
new file mode 100755
index 000000000..d4744f0c7
--- /dev/null
+++ b/trunk/install-sh
@@ -0,0 +1,269 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+#
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/trunk/keys/freeworlddialup.pub b/trunk/keys/freeworlddialup.pub
new file mode 100644
index 000000000..4ba3faf66
--- /dev/null
+++ b/trunk/keys/freeworlddialup.pub
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcE53oHNoe3sBSkvW3JaO8v5Z1
+CC+Cm+JgocGwmUek0hlQST1NUsFWfYIMd5z/Iunnd1GziXLqDYzCQeZUtJ6Y9J4A
+cA9wNv1eYWrlH7ozKWOv592+Y5xF0kqQ1jFt+5zFTP5myL9N439Evu/BWALHw0B4
+aML+CsGHg0uIe5ZjNwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/trunk/keys/iaxtel.pub b/trunk/keys/iaxtel.pub
new file mode 100644
index 000000000..d7c8f6cc8
--- /dev/null
+++ b/trunk/keys/iaxtel.pub
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3+joshldCUFgjj5DeYOgRLSPS
+0t7gxTHiy1BTfynadPzgn447dy9iIQfBykE0pEdIPgaPMEt+ZPqPtln1P4dX3Ynx
+I+RYKgtjsmYYnyJRMGHfHuLXwkbFkxyiEg0KDvbdBWbz7GUNZlp49BLXewWGY9DJ
+MauE7FXVyCVDchn0YQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/trunk/main/Makefile b/trunk/main/Makefile
new file mode 100644
index 000000000..3504b547b
--- /dev/null
+++ b/trunk/main/Makefile
@@ -0,0 +1,174 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile to build main Asterisk binary
+#
+# 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
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps $(ASTTOPDIR)/makeopts.embed_rules
+
+all: asterisk
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+RESAMPLE_OBJS:=libresample/src/resample.o libresample/src/resamplesubs.o libresample/src/filterkit.o
+
+OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
+ translate.o file.o pbx.o cli.o md5.o term.o \
+ ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
+ cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \
+ dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
+ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
+ utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
+ netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
+ cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
+ strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
+ astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o
+
+# we need to link in the objects statically, not as a library, because
+# otherwise modules will not have them available if none of the static
+# objects use it.
+OBJS+=stdtime/localtime.o
+
+# At the moment say.o is an optional component which can be overridden
+# by a module.
+OBJS+=say.o
+
+AST_LIBS += $(SSL_LIB)
+AST_LIBS += $(BKTR_LIB)
+
+ifeq ($(POLL_AVAILABLE),)
+ OBJS+=poll.o
+endif
+
+ifeq ($(wildcard /usr/include/dlfcn.h),)
+ OBJS+=dlfcn.o
+endif
+
+ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc ),)
+ ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ AST_LIBS+=-ldl
+ endif
+ ifneq (x$(CAP_LIB),x)
+ AST_LIBS+=$(CAP_LIB)
+ endif
+ AST_LIBS+=-lpthread $(EDITLINE_LIB) -lm -lresolv
+else
+ AST_LIBS+=$(EDITLINE_LIB) -lm
+endif
+
+ifneq ($(findstring darwin,$(OSARCH)),)
+ AST_LIBS+=-lresolv
+ ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ ASTLINK=-Wl,-dynamic
+ endif
+else
+# These are used for all but Darwin
+ ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ ASTLINK+=-Wl,--export-dynamic
+ else
+ ASTLINK+=${GC_LDFLAGS}
+ endif
+ ifneq ($(findstring BSD,$(OSARCH)),)
+ LDFLAGS+=-L/usr/local/lib
+ endif
+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)
+ AST_LIBS+=$(shell if test $(BSDVERSION) -lt 502102 ; then echo "-lc_r"; else echo "-pthread"; fi)
+ AST_LIBS+=-lcrypto
+endif
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ AST_LIBS+=-lminires -ldl
+ ASTLINK+= -shared -Wl,--out-implib,libasterisk.a
+endif
+ifeq ($(OSARCH),NetBSD)
+ AST_LIBS+=-lpthread -lcrypto -lm -L/usr/pkg/lib $(EDITLINE_LIB)
+endif
+
+ifeq ($(OSARCH),OpenBSD)
+ AST_LIBS+=-lcrypto -lpthread -lm $(EDITLINE_LIB)
+endif
+
+ifeq ($(OSARCH),SunOS)
+ AST_LIBS+=-lpthread -ldl -lnsl -lsocket -lresolv -L/opt/ssl/lib -L/usr/local/ssl/lib
+ ASTLINK=
+endif
+
+CHECK_SUBDIR: # do nothing, just make sure that we recurse in the subdir/
+
+editline/libedit.a: CHECK_SUBDIR
+ cd editline && test -f config.h || CFLAGS="$(PTHREAD_CFLAGS) $(ASTCFLAGS:-Werror=)" LDFLAGS="$(ASTLDFLAGS)" ./configure --build=$(BUILD_PLATFORM) --host=$(HOST_PLATFORM) --with-ncurses=$(NCURSES_DIR) --with-curses=$(CURSES_DIR) --with-termcap=$(TERMCAP_DIR) --with-tinfo=$(TINFO_DIR)
+ $(MAKE) -C editline libedit.a
+
+db1-ast/libdb1.a: CHECK_SUBDIR
+ CFLAGS="$(ASTCFLAGS)" LDFLAGS="$(ASTLDFLAGS)" $(MAKE) -C db1-ast libdb1.a
+
+ast_expr2.c ast_expr2.h:
+ bison -o $@ -d --name-prefix=ast_yy ast_expr2.y
+
+ast_expr2f.c:
+ flex -o $@ --full ast_expr2.fl # moved the correction of yyfree into the flex input file itself.
+ sed 's@#if __STDC_VERSION__ >= 199901L@#if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L@' ast_expr2f.c > zz
+ mv zz ast_expr2f.c
+
+testexpr2: ast_expr2f.c ast_expr2.c ast_expr2.h
+ $(CC) -g -c -Iinclude -DSTANDALONE ast_expr2f.c
+ $(CC) -g -c -Iinclude -DSTANDALONE ast_expr2.c
+ $(CC) -g -o testexpr2 ast_expr2f.o ast_expr2.o -lm
+ rm ast_expr2.o ast_expr2f.o
+
+channel.o: ASTCFLAGS+=$(ZAPTEL_INCLUDE)
+
+AST_EMBED_LDSCRIPTS:=$(sort $(EMBED_LDSCRIPTS))
+AST_EMBED_LDFLAGS:=$(foreach dep,$(EMBED_LDFLAGS),$(value $(dep)))
+AST_EMBED_LIBS:=$(foreach dep,$(EMBED_LIBS),$(value $(dep)))
+OBJS:=$(sort $(OBJS))
+
+ifneq ($(wildcard ../channels/h323/Makefile.ast),)
+ include ../channels/h323/Makefile.ast
+else
+ H323LDFLAGS=
+ H323LDLIBS=
+endif
+
+minimime/libmmime.a: CHECK_SUBDIR
+ @cd minimime && $(MAKE) libmmime.a
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+MAIN_TGT:=asterisk.dll
+asterisk: cygload
+ mv cygload.exe asterisk.exe
+
+cygload: asterisk.dll
+else
+MAIN_TGT:=asterisk
+endif
+
+$(MAIN_TGT): $(OBJS) editline/libedit.a db1-ast/libdb1.a minimime/libmmime.a $(AST_EMBED_LDSCRIPTS)
+ @$(CC) -c -o buildinfo.o $(ASTCFLAGS) buildinfo.c
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ifneq ($(findstring chan_h323,$(MENUSELECT_CHANNELS)),)
+ $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS)
+else
+ $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $(H323LDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(H323LDLIBS)
+endif
+ @$(ASTTOPDIR)/build_tools/strip_nonapi $@
+
+clean::
+ rm -f asterisk
+ rm -f db1-ast/.*.d
+ @if [ -f editline/Makefile ]; then $(MAKE) -C editline distclean ; fi
+ @$(MAKE) -C db1-ast clean
+ @$(MAKE) -C stdtime clean
+ @$(MAKE) -C minimime clean
+ rm -f libresample/src/*.o
diff --git a/trunk/main/abstract_jb.c b/trunk/main/abstract_jb.c
new file mode 100644
index 000000000..4f464796d
--- /dev/null
+++ b/trunk/main/abstract_jb.c
@@ -0,0 +1,774 @@
+/*
+ * abstract_jb: common implementation-independent jitterbuffer stuff
+ *
+ * Copyright (C) 2005, Attractel OOD
+ *
+ * Contributors:
+ * Slav Klenov <slav@securax.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.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ *
+ * \brief Common implementation-independent jitterbuffer stuff.
+ *
+ * \author Slav Klenov <slav@securax.org>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+
+#include "asterisk/abstract_jb.h"
+#include "fixedjitterbuf.h"
+#include "jitterbuf.h"
+
+/*! Internal jb flags */
+enum {
+ JB_USE = (1 << 0),
+ JB_TIMEBASE_INITIALIZED = (1 << 1),
+ JB_CREATED = (1 << 2)
+};
+
+/* Hooks for the abstract jb implementation */
+
+/*! \brief Create */
+typedef void * (*jb_create_impl)(struct ast_jb_conf *general_config, long resynch_threshold);
+/*! \brief Destroy */
+typedef void (*jb_destroy_impl)(void *jb);
+/*! \brief Put first frame */
+typedef int (*jb_put_first_impl)(void *jb, struct ast_frame *fin, long now);
+/*! \brief Put frame */
+typedef int (*jb_put_impl)(void *jb, struct ast_frame *fin, long now);
+/*! \brief Get frame for now */
+typedef int (*jb_get_impl)(void *jb, struct ast_frame **fout, long now, long interpl);
+/*! \brief Get next */
+typedef long (*jb_next_impl)(void *jb);
+/*! \brief Remove first frame */
+typedef int (*jb_remove_impl)(void *jb, struct ast_frame **fout);
+/*! \brief Force resynch */
+typedef void (*jb_force_resynch_impl)(void *jb);
+
+
+/*!
+ * \brief Jitterbuffer implementation private struct.
+ */
+struct ast_jb_impl
+{
+ char name[AST_JB_IMPL_NAME_SIZE];
+ jb_create_impl create;
+ jb_destroy_impl destroy;
+ jb_put_first_impl put_first;
+ jb_put_impl put;
+ jb_get_impl get;
+ jb_next_impl next;
+ jb_remove_impl remove;
+ jb_force_resynch_impl force_resync;
+};
+
+/* Implementation functions */
+/* fixed */
+static void * jb_create_fixed(struct ast_jb_conf *general_config, long resynch_threshold);
+static void jb_destroy_fixed(void *jb);
+static int jb_put_first_fixed(void *jb, struct ast_frame *fin, long now);
+static int jb_put_fixed(void *jb, struct ast_frame *fin, long now);
+static int jb_get_fixed(void *jb, struct ast_frame **fout, long now, long interpl);
+static long jb_next_fixed(void *jb);
+static int jb_remove_fixed(void *jb, struct ast_frame **fout);
+static void jb_force_resynch_fixed(void *jb);
+/* adaptive */
+static void * jb_create_adaptive(struct ast_jb_conf *general_config, long resynch_threshold);
+static void jb_destroy_adaptive(void *jb);
+static int jb_put_first_adaptive(void *jb, struct ast_frame *fin, long now);
+static int jb_put_adaptive(void *jb, struct ast_frame *fin, long now);
+static int jb_get_adaptive(void *jb, struct ast_frame **fout, long now, long interpl);
+static long jb_next_adaptive(void *jb);
+static int jb_remove_adaptive(void *jb, struct ast_frame **fout);
+static void jb_force_resynch_adaptive(void *jb);
+
+/* Available jb implementations */
+static struct ast_jb_impl avail_impl[] =
+{
+ {
+ .name = "fixed",
+ .create = jb_create_fixed,
+ .destroy = jb_destroy_fixed,
+ .put_first = jb_put_first_fixed,
+ .put = jb_put_fixed,
+ .get = jb_get_fixed,
+ .next = jb_next_fixed,
+ .remove = jb_remove_fixed,
+ .force_resync = jb_force_resynch_fixed
+ },
+ {
+ .name = "adaptive",
+ .create = jb_create_adaptive,
+ .destroy = jb_destroy_adaptive,
+ .put_first = jb_put_first_adaptive,
+ .put = jb_put_adaptive,
+ .get = jb_get_adaptive,
+ .next = jb_next_adaptive,
+ .remove = jb_remove_adaptive,
+ .force_resync = jb_force_resynch_adaptive
+ }
+};
+
+static int default_impl = 0;
+
+
+/*! Abstract return codes */
+enum {
+ JB_IMPL_OK,
+ JB_IMPL_DROP,
+ JB_IMPL_INTERP,
+ JB_IMPL_NOFRAME
+};
+
+/* Translations between impl and abstract return codes */
+static int fixed_to_abstract_code[] =
+ {JB_IMPL_OK, JB_IMPL_DROP, JB_IMPL_INTERP, JB_IMPL_NOFRAME};
+static int adaptive_to_abstract_code[] =
+ {JB_IMPL_OK, JB_IMPL_NOFRAME, JB_IMPL_NOFRAME, JB_IMPL_INTERP, JB_IMPL_DROP, JB_IMPL_OK};
+
+/* JB_GET actions (used only for the frames log) */
+static char *jb_get_actions[] = {"Delivered", "Dropped", "Interpolated", "No"};
+
+/*! \brief Macros for the frame log files */
+#define jb_framelog(...) do { \
+ if (jb->logfile) { \
+ fprintf(jb->logfile, __VA_ARGS__); \
+ fflush(jb->logfile); \
+ } \
+} while (0)
+
+
+/* Internal utility functions */
+static void jb_choose_impl(struct ast_channel *chan);
+static void jb_get_and_deliver(struct ast_channel *chan);
+static int create_jb(struct ast_channel *chan, struct ast_frame *first_frame);
+static long get_now(struct ast_jb *jb, struct timeval *tv);
+
+
+/* Interface ast jb functions impl */
+
+
+static void jb_choose_impl(struct ast_channel *chan)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_conf *jbconf = &jb->conf;
+ struct ast_jb_impl *test_impl;
+ int i, avail_impl_count = sizeof(avail_impl) / sizeof(avail_impl[0]);
+
+ jb->impl = &avail_impl[default_impl];
+
+ if (ast_strlen_zero(jbconf->impl))
+ return;
+
+ for (i = 0; i < avail_impl_count; i++) {
+ test_impl = &avail_impl[i];
+ if (!strcasecmp(jbconf->impl, test_impl->name)) {
+ jb->impl = test_impl;
+ return;
+ }
+ }
+}
+
+int ast_jb_do_usecheck(struct ast_channel *c0, struct ast_channel *c1)
+{
+ struct ast_jb *jb0 = &c0->jb;
+ struct ast_jb *jb1 = &c1->jb;
+ struct ast_jb_conf *conf0 = &jb0->conf;
+ struct ast_jb_conf *conf1 = &jb1->conf;
+ int c0_wants_jitter = c0->tech->properties & AST_CHAN_TP_WANTSJITTER;
+ int c0_creates_jitter = c0->tech->properties & AST_CHAN_TP_CREATESJITTER;
+ int c0_jb_enabled = ast_test_flag(conf0, AST_JB_ENABLED);
+ int c0_force_jb = ast_test_flag(conf0, AST_JB_FORCED);
+ int c0_jb_timebase_initialized = ast_test_flag(jb0, JB_TIMEBASE_INITIALIZED);
+ int c0_jb_created = ast_test_flag(jb0, JB_CREATED);
+ int c1_wants_jitter = c1->tech->properties & AST_CHAN_TP_WANTSJITTER;
+ int c1_creates_jitter = c1->tech->properties & AST_CHAN_TP_CREATESJITTER;
+ int c1_jb_enabled = ast_test_flag(conf1, AST_JB_ENABLED);
+ int c1_force_jb = ast_test_flag(conf1, AST_JB_FORCED);
+ int c1_jb_timebase_initialized = ast_test_flag(jb1, JB_TIMEBASE_INITIALIZED);
+ int c1_jb_created = ast_test_flag(jb1, JB_CREATED);
+ int inuse = 0;
+
+ /* Determine whether audio going to c0 needs a jitter buffer */
+ if (((!c0_wants_jitter && c1_creates_jitter) || (c0_force_jb && c1_creates_jitter)) && c0_jb_enabled) {
+ ast_set_flag(jb0, JB_USE);
+ if (!c0_jb_timebase_initialized) {
+ if (c1_jb_timebase_initialized) {
+ memcpy(&jb0->timebase, &jb1->timebase, sizeof(struct timeval));
+ } else {
+ gettimeofday(&jb0->timebase, NULL);
+ }
+ ast_set_flag(jb0, JB_TIMEBASE_INITIALIZED);
+ }
+
+ if (!c0_jb_created) {
+ jb_choose_impl(c0);
+ }
+
+ inuse = 1;
+ }
+
+ /* Determine whether audio going to c1 needs a jitter buffer */
+ if (((!c1_wants_jitter && c0_creates_jitter) || (c1_force_jb && c0_creates_jitter)) && c1_jb_enabled) {
+ ast_set_flag(jb1, JB_USE);
+ if (!c1_jb_timebase_initialized) {
+ if (c0_jb_timebase_initialized) {
+ memcpy(&jb1->timebase, &jb0->timebase, sizeof(struct timeval));
+ } else {
+ gettimeofday(&jb1->timebase, NULL);
+ }
+ ast_set_flag(jb1, JB_TIMEBASE_INITIALIZED);
+ }
+
+ if (!c1_jb_created) {
+ jb_choose_impl(c1);
+ }
+
+ inuse = 1;
+ }
+
+ return inuse;
+}
+
+int ast_jb_get_when_to_wakeup(struct ast_channel *c0, struct ast_channel *c1, int time_left)
+{
+ struct ast_jb *jb0 = &c0->jb;
+ struct ast_jb *jb1 = &c1->jb;
+ int c0_use_jb = ast_test_flag(jb0, JB_USE);
+ int c0_jb_is_created = ast_test_flag(jb0, JB_CREATED);
+ int c1_use_jb = ast_test_flag(jb1, JB_USE);
+ int c1_jb_is_created = ast_test_flag(jb1, JB_CREATED);
+ int wait, wait0, wait1;
+ struct timeval tv_now;
+
+ if (time_left == 0) {
+ /* No time left - the bridge will be retried */
+ /* TODO: Test disable this */
+ /*return 0;*/
+ }
+
+ if (time_left < 0) {
+ time_left = INT_MAX;
+ }
+
+ gettimeofday(&tv_now, NULL);
+
+ wait0 = (c0_use_jb && c0_jb_is_created) ? jb0->next - get_now(jb0, &tv_now) : time_left;
+ wait1 = (c1_use_jb && c1_jb_is_created) ? jb1->next - get_now(jb1, &tv_now) : time_left;
+
+ wait = wait0 < wait1 ? wait0 : wait1;
+ wait = wait < time_left ? wait : time_left;
+
+ if (wait == INT_MAX) {
+ wait = -1;
+ } else if (wait < 1) {
+ /* don't let wait=0, because this can cause the pbx thread to loop without any sleeping at all */
+ wait = 1;
+ }
+
+ return wait;
+}
+
+
+int ast_jb_put(struct ast_channel *chan, struct ast_frame *f)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj = jb->jbobj;
+ struct ast_frame *frr;
+ long now = 0;
+
+ if (!ast_test_flag(jb, JB_USE))
+ return -1;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ if (f->frametype == AST_FRAME_DTMF && ast_test_flag(jb, JB_CREATED)) {
+ jb_framelog("JB_PUT {now=%ld}: Received DTMF frame. Force resynching jb...\n", now);
+ jbimpl->force_resync(jbobj);
+ }
+
+ return -1;
+ }
+
+ /* We consider an enabled jitterbuffer should receive frames with valid timing info. */
+ if (!ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO) || f->len < 2 || f->ts < 0) {
+ ast_log(LOG_WARNING, "%s received frame with invalid timing info: "
+ "has_timing_info=%d, len=%ld, ts=%ld, src=%s\n",
+ chan->name, ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO), f->len, f->ts, f->src);
+ return -1;
+ }
+
+ frr = ast_frdup(f);
+
+ if (!frr) {
+ ast_log(LOG_ERROR, "Failed to isolate frame for the jitterbuffer on channel '%s'\n", chan->name);
+ return -1;
+ }
+
+ if (!ast_test_flag(jb, JB_CREATED)) {
+ if (create_jb(chan, frr)) {
+ ast_frfree(frr);
+ /* Disable the jitterbuffer */
+ ast_clear_flag(jb, JB_USE);
+ return -1;
+ }
+
+ ast_set_flag(jb, JB_CREATED);
+ return 0;
+ } else {
+ now = get_now(jb, NULL);
+ if (jbimpl->put(jbobj, frr, now) != JB_IMPL_OK) {
+ jb_framelog("JB_PUT {now=%ld}: Dropped frame with ts=%ld and len=%ld\n", now, frr->ts, frr->len);
+ ast_frfree(frr);
+ /*return -1;*/
+ /* TODO: Check this fix - should return 0 here, because the dropped frame shouldn't
+ be delivered at all */
+ return 0;
+ }
+
+ jb->next = jbimpl->next(jbobj);
+
+ jb_framelog("JB_PUT {now=%ld}: Queued frame with ts=%ld and len=%ld\n", now, frr->ts, frr->len);
+
+ return 0;
+ }
+}
+
+
+void ast_jb_get_and_deliver(struct ast_channel *c0, struct ast_channel *c1)
+{
+ struct ast_jb *jb0 = &c0->jb;
+ struct ast_jb *jb1 = &c1->jb;
+ int c0_use_jb = ast_test_flag(jb0, JB_USE);
+ int c0_jb_is_created = ast_test_flag(jb0, JB_CREATED);
+ int c1_use_jb = ast_test_flag(jb1, JB_USE);
+ int c1_jb_is_created = ast_test_flag(jb1, JB_CREATED);
+
+ if (c0_use_jb && c0_jb_is_created)
+ jb_get_and_deliver(c0);
+
+ if (c1_use_jb && c1_jb_is_created)
+ jb_get_and_deliver(c1);
+}
+
+
+static void jb_get_and_deliver(struct ast_channel *chan)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj = jb->jbobj;
+ struct ast_frame *f, finterp;
+ long now;
+ int interpolation_len, res;
+
+ now = get_now(jb, NULL);
+ jb->next = jbimpl->next(jbobj);
+ if (now < jb->next) {
+ jb_framelog("\tJB_GET {now=%ld}: now < next=%ld\n", now, jb->next);
+ return;
+ }
+
+ while (now >= jb->next) {
+ interpolation_len = ast_codec_interp_len(jb->last_format);
+
+ res = jbimpl->get(jbobj, &f, now, interpolation_len);
+
+ switch (res) {
+ case JB_IMPL_OK:
+ /* deliver the frame */
+ ast_write(chan, f);
+ case JB_IMPL_DROP:
+ jb_framelog("\tJB_GET {now=%ld}: %s frame with ts=%ld and len=%ld\n",
+ now, jb_get_actions[res], f->ts, f->len);
+ jb->last_format = f->subclass;
+ ast_frfree(f);
+ break;
+ case JB_IMPL_INTERP:
+ /* interpolate a frame */
+ f = &finterp;
+ f->frametype = AST_FRAME_VOICE;
+ f->subclass = jb->last_format;
+ f->datalen = 0;
+ f->samples = interpolation_len * 8;
+ f->mallocd = 0;
+ f->src = "JB interpolation";
+ f->data = NULL;
+ f->delivery = ast_tvadd(jb->timebase, ast_samp2tv(jb->next, 1000));
+ f->offset = AST_FRIENDLY_OFFSET;
+ /* deliver the interpolated frame */
+ ast_write(chan, f);
+ jb_framelog("\tJB_GET {now=%ld}: Interpolated frame with len=%d\n", now, interpolation_len);
+ break;
+ case JB_IMPL_NOFRAME:
+ ast_log(LOG_WARNING,
+ "JB_IMPL_NOFRAME is retuned from the %s jb when now=%ld >= next=%ld, jbnext=%ld!\n",
+ jbimpl->name, now, jb->next, jbimpl->next(jbobj));
+ jb_framelog("\tJB_GET {now=%ld}: No frame for now!?\n", now);
+ return;
+ default:
+ ast_log(LOG_ERROR, "This should never happen!\n");
+ CRASH;
+ break;
+ }
+
+ jb->next = jbimpl->next(jbobj);
+ }
+}
+
+
+static int create_jb(struct ast_channel *chan, struct ast_frame *frr)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_conf *jbconf = &jb->conf;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj;
+ struct ast_channel *bridged;
+ long now;
+ char logfile_pathname[20 + AST_JB_IMPL_NAME_SIZE + 2*AST_CHANNEL_NAME + 1];
+ char name1[AST_CHANNEL_NAME], name2[AST_CHANNEL_NAME], *tmp;
+ int res;
+
+ jbobj = jb->jbobj = jbimpl->create(jbconf, jbconf->resync_threshold);
+ if (!jbobj) {
+ ast_log(LOG_WARNING, "Failed to create jitterbuffer on channel '%s'\n", chan->name);
+ return -1;
+ }
+
+ now = get_now(jb, NULL);
+ res = jbimpl->put_first(jbobj, frr, now);
+
+ /* The result of putting the first frame should not differ from OK. However, its possible
+ some implementations (i.e. adaptive's when resynch_threshold is specified) to drop it. */
+ if (res != JB_IMPL_OK) {
+ ast_log(LOG_WARNING, "Failed to put first frame in the jitterbuffer on channel '%s'\n", chan->name);
+ /*
+ jbimpl->destroy(jbobj);
+ return -1;
+ */
+ }
+
+ /* Init next */
+ jb->next = jbimpl->next(jbobj);
+
+ /* Init last format for a first time. */
+ jb->last_format = frr->subclass;
+
+ /* Create a frame log file */
+ if (ast_test_flag(jbconf, AST_JB_LOG)) {
+ snprintf(name2, sizeof(name2), "%s", chan->name);
+ tmp = strchr(name2, '/');
+ if (tmp)
+ *tmp = '#';
+
+ bridged = ast_bridged_channel(chan);
+ if (!bridged) {
+ /* We should always have bridged chan if a jitterbuffer is in use */
+ CRASH;
+ }
+ snprintf(name1, sizeof(name1), "%s", bridged->name);
+ tmp = strchr(name1, '/');
+ if (tmp)
+ *tmp = '#';
+
+ snprintf(logfile_pathname, sizeof(logfile_pathname),
+ "/tmp/ast_%s_jb_%s--%s.log", jbimpl->name, name1, name2);
+ jb->logfile = fopen(logfile_pathname, "w+b");
+
+ if (!jb->logfile)
+ ast_log(LOG_WARNING, "Failed to create frame log file with pathname '%s'\n", logfile_pathname);
+
+ if (res == JB_IMPL_OK)
+ jb_framelog("JB_PUT_FIRST {now=%ld}: Queued frame with ts=%ld and len=%ld\n",
+ now, frr->ts, frr->len);
+ else
+ jb_framelog("JB_PUT_FIRST {now=%ld}: Dropped frame with ts=%ld and len=%ld\n",
+ now, frr->ts, frr->len);
+ }
+
+ ast_verb(3, "%s jitterbuffer created on channel %s\n", jbimpl->name, chan->name);
+
+ /* Free the frame if it has not been queued in the jb */
+ if (res != JB_IMPL_OK)
+ ast_frfree(frr);
+
+ return 0;
+}
+
+
+void ast_jb_destroy(struct ast_channel *chan)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj = jb->jbobj;
+ struct ast_frame *f;
+
+ if (jb->logfile) {
+ fclose(jb->logfile);
+ jb->logfile = NULL;
+ }
+
+ if (ast_test_flag(jb, JB_CREATED)) {
+ /* Remove and free all frames still queued in jb */
+ while (jbimpl->remove(jbobj, &f) == JB_IMPL_OK) {
+ ast_frfree(f);
+ }
+
+ jbimpl->destroy(jbobj);
+ jb->jbobj = NULL;
+
+ ast_clear_flag(jb, JB_CREATED);
+
+ ast_verb(3, "%s jitterbuffer destroyed on channel %s\n", jbimpl->name, chan->name);
+ }
+}
+
+
+static long get_now(struct ast_jb *jb, struct timeval *tv)
+{
+ struct timeval now;
+
+ if (!tv) {
+ tv = &now;
+ gettimeofday(tv, NULL);
+ }
+
+ return ast_tvdiff_ms(*tv, jb->timebase);
+}
+
+
+int ast_jb_read_conf(struct ast_jb_conf *conf, const char *varname, const char *value)
+{
+ int prefixlen = sizeof(AST_JB_CONF_PREFIX) - 1;
+ const char *name;
+ int tmp;
+
+ if (strncasecmp(AST_JB_CONF_PREFIX, varname, prefixlen))
+ return -1;
+
+ name = varname + prefixlen;
+
+ if (!strcasecmp(name, AST_JB_CONF_ENABLE)) {
+ ast_set2_flag(conf, ast_true(value), AST_JB_ENABLED);
+ } else if (!strcasecmp(name, AST_JB_CONF_FORCE)) {
+ ast_set2_flag(conf, ast_true(value), AST_JB_FORCED);
+ } else if (!strcasecmp(name, AST_JB_CONF_MAX_SIZE)) {
+ if ((tmp = atoi(value)) > 0)
+ conf->max_size = tmp;
+ } else if (!strcasecmp(name, AST_JB_CONF_RESYNCH_THRESHOLD)) {
+ if ((tmp = atoi(value)) > 0)
+ conf->resync_threshold = tmp;
+ } else if (!strcasecmp(name, AST_JB_CONF_IMPL)) {
+ if (!ast_strlen_zero(value))
+ snprintf(conf->impl, sizeof(conf->impl), "%s", value);
+ } else if (!strcasecmp(name, AST_JB_CONF_LOG)) {
+ ast_set2_flag(conf, ast_true(value), AST_JB_LOG);
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ast_jb_configure(struct ast_channel *chan, const struct ast_jb_conf *conf)
+{
+ memcpy(&chan->jb.conf, conf, sizeof(*conf));
+}
+
+
+void ast_jb_get_config(const struct ast_channel *chan, struct ast_jb_conf *conf)
+{
+ memcpy(conf, &chan->jb.conf, sizeof(*conf));
+}
+
+
+/* Implementation functions */
+
+/* fixed */
+
+static void * jb_create_fixed(struct ast_jb_conf *general_config, long resynch_threshold)
+{
+ struct fixed_jb_conf conf;
+
+ conf.jbsize = general_config->max_size;
+ conf.resync_threshold = resynch_threshold;
+
+ return fixed_jb_new(&conf);
+}
+
+
+static void jb_destroy_fixed(void *jb)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+
+ /* destroy the jb */
+ fixed_jb_destroy(fixedjb);
+}
+
+
+static int jb_put_first_fixed(void *jb, struct ast_frame *fin, long now)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ int res;
+
+ res = fixed_jb_put_first(fixedjb, fin, fin->len, fin->ts, now);
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static int jb_put_fixed(void *jb, struct ast_frame *fin, long now)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ int res;
+
+ res = fixed_jb_put(fixedjb, fin, fin->len, fin->ts, now);
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static int jb_get_fixed(void *jb, struct ast_frame **fout, long now, long interpl)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ struct fixed_jb_frame frame;
+ int res;
+
+ res = fixed_jb_get(fixedjb, &frame, now, interpl);
+ *fout = frame.data;
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static long jb_next_fixed(void *jb)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+
+ return fixed_jb_next(fixedjb);
+}
+
+
+static int jb_remove_fixed(void *jb, struct ast_frame **fout)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ struct fixed_jb_frame frame;
+ int res;
+
+ res = fixed_jb_remove(fixedjb, &frame);
+ *fout = frame.data;
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static void jb_force_resynch_fixed(void *jb)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+
+ fixed_jb_set_force_resynch(fixedjb);
+}
+
+
+/* adaptive */
+
+static void *jb_create_adaptive(struct ast_jb_conf *general_config, long resynch_threshold)
+{
+ jb_conf jbconf;
+ jitterbuf *adaptivejb;
+
+ adaptivejb = jb_new();
+ if (adaptivejb) {
+ jbconf.max_jitterbuf = general_config->max_size;
+ jbconf.resync_threshold = general_config->resync_threshold;
+ jbconf.max_contig_interp = 10;
+ jb_setconf(adaptivejb, &jbconf);
+ }
+
+ return adaptivejb;
+}
+
+
+static void jb_destroy_adaptive(void *jb)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+
+ jb_destroy(adaptivejb);
+}
+
+
+static int jb_put_first_adaptive(void *jb, struct ast_frame *fin, long now)
+{
+ return jb_put_adaptive(jb, fin, now);
+}
+
+
+static int jb_put_adaptive(void *jb, struct ast_frame *fin, long now)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+ int res;
+
+ res = jb_put(adaptivejb, fin, JB_TYPE_VOICE, fin->len, fin->ts, now);
+
+ return adaptive_to_abstract_code[res];
+}
+
+
+static int jb_get_adaptive(void *jb, struct ast_frame **fout, long now, long interpl)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+ jb_frame frame;
+ int res;
+
+ res = jb_get(adaptivejb, &frame, now, interpl);
+ *fout = frame.data;
+
+ return adaptive_to_abstract_code[res];
+}
+
+
+static long jb_next_adaptive(void *jb)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+
+ return jb_next(adaptivejb);
+}
+
+
+static int jb_remove_adaptive(void *jb, struct ast_frame **fout)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+ jb_frame frame;
+ int res;
+
+ res = jb_getall(adaptivejb, &frame);
+ *fout = frame.data;
+
+ return adaptive_to_abstract_code[res];
+}
+
+
+static void jb_force_resynch_adaptive(void *jb)
+{
+}
diff --git a/trunk/main/acl.c b/trunk/main/acl.c
new file mode 100644
index 000000000..ed149b963
--- /dev/null
+++ b/trunk/main/acl.c
@@ -0,0 +1,357 @@
+/*
+ * 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 Various sorts of access control
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/network.h"
+
+#if defined(SOLARIS)
+#include <sys/sockio.h>
+#endif
+
+#include "asterisk/acl.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/srv.h"
+
+/* Free HA structure */
+void ast_free_ha(struct ast_ha *ha)
+{
+ struct ast_ha *hal;
+ while (ha) {
+ hal = ha;
+ ha = ha->next;
+ ast_free(hal);
+ }
+}
+
+/* Copy HA structure */
+static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to)
+{
+ memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
+ memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
+ to->sense = from->sense;
+}
+
+/* Create duplicate of ha structure */
+static struct ast_ha *ast_duplicate_ha(struct ast_ha *original)
+{
+ struct ast_ha *new_ha;
+
+ if ((new_ha = ast_malloc(sizeof(*new_ha)))) {
+ /* Copy from original to new object */
+ ast_copy_ha(original, new_ha);
+ }
+
+ return new_ha;
+}
+
+/* Create duplicate HA link list */
+/* Used in chan_sip2 templates */
+struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
+{
+ struct ast_ha *start = original;
+ struct ast_ha *ret = NULL;
+ struct ast_ha *link, *prev = NULL;
+
+ while (start) {
+ link = ast_duplicate_ha(start); /* Create copy of this object */
+ if (prev)
+ prev->next = link; /* Link previous to this object */
+
+ if (!ret)
+ ret = link; /* Save starting point */
+
+ start = start->next; /* Go to next object */
+ prev = link; /* Save pointer to this object */
+ }
+ return ret; /* Return start of list */
+}
+
+struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+{
+ struct ast_ha *ha;
+ char *nm;
+ struct ast_ha *prev = NULL;
+ struct ast_ha *ret;
+ int x;
+ char *tmp = ast_strdupa(stuff);
+
+ ret = path;
+ while (path) {
+ prev = path;
+ path = path->next;
+ }
+
+ ha = ast_malloc(sizeof(*ha));
+ if (!ha)
+ return ret;
+
+ nm = strchr(tmp, '/');
+ if (!nm) {
+ /* assume /32. Yes, htonl does not do anything for this particular mask
+ but we better use it to show we remember about byte order */
+ ha->netmask.s_addr = htonl(0xFFFFFFFF);
+ } else {
+ *nm = '\0';
+ nm++;
+
+ if (!strchr(nm, '.')) {
+ if ((sscanf(nm, "%d", &x) == 1) && (x >= 0) && (x <= 32))
+ ha->netmask.s_addr = htonl(0xFFFFFFFF << (32 - x));
+ else {
+ ast_log(LOG_WARNING, "Invalid CIDR in %s\n", stuff);
+ ast_free(ha);
+ if (error)
+ *error = 1;
+ return ret;
+ }
+ } else if (!inet_aton(nm, &ha->netmask)) {
+ ast_log(LOG_WARNING, "Invalid mask in %s\n", stuff);
+ ast_free(ha);
+ if (error)
+ *error = 1;
+ return ret;
+ }
+ }
+
+ if (!inet_aton(tmp, &ha->netaddr)) {
+ ast_log(LOG_WARNING, "Invalid IP address in %s\n", stuff);
+ ast_free(ha);
+ if (error)
+ *error = 1;
+ return ret;
+ }
+
+ ha->netaddr.s_addr &= ha->netmask.s_addr;
+
+ ha->sense = strncasecmp(sense, "p", 1) ? AST_SENSE_DENY : AST_SENSE_ALLOW;
+
+ ha->next = NULL;
+ if (prev) {
+ prev->next = ha;
+ } else {
+ ret = ha;
+ }
+
+ ast_debug(1, "%s/%s sense %d appended to acl for peer\n", ast_strdupa(ast_inet_ntoa(ha->netaddr)), ast_strdupa(ast_inet_ntoa(ha->netmask)), ha->sense);
+
+ return ret;
+}
+
+int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
+{
+ /* Start optimistic */
+ int res = AST_SENSE_ALLOW;
+ while (ha) {
+#if 0 /* debugging code */
+ char iabuf[INET_ADDRSTRLEN];
+ char iabuf2[INET_ADDRSTRLEN];
+ /* DEBUG */
+ ast_copy_string(iabuf, ast_inet_ntoa(sin->sin_addr), sizeof(iabuf));
+ ast_copy_string(iabuf2, ast_inet_ntoa(ha->netaddr), sizeof(iabuf2));
+ ast_debug(1, "##### Testing %s with %s\n", iabuf, iabuf2);
+#endif
+ /* For each rule, if this address and the netmask = the net address
+ apply the current rule */
+ if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == ha->netaddr.s_addr)
+ res = ha->sense;
+ ha = ha->next;
+ }
+ return res;
+}
+
+int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ char srv[256];
+ char host[256];
+ int tportno = ntohs(sin->sin_port);
+ if (service) {
+ snprintf(srv, sizeof(srv), "%s.%s", service, value);
+ if (ast_get_srv(NULL, host, sizeof(host), &tportno, srv) > 0) {
+ sin->sin_port = htons(tportno);
+ value = host;
+ }
+ }
+ hp = ast_gethostbyname(value, &ahp);
+ if (hp) {
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
+ } else {
+ ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
+ return -1;
+ }
+ return 0;
+}
+
+struct dscp_codepoint {
+ char *name;
+ unsigned int space;
+};
+
+/* IANA registered DSCP codepoints */
+
+static const struct dscp_codepoint dscp_pool1[] = {
+ { "CS0", 0x00 },
+ { "CS1", 0x08 },
+ { "CS2", 0x10 },
+ { "CS3", 0x18 },
+ { "CS4", 0x20 },
+ { "CS5", 0x28 },
+ { "CS6", 0x30 },
+ { "CS7", 0x38 },
+ { "AF11", 0x0A },
+ { "AF12", 0x0C },
+ { "AF13", 0x0E },
+ { "AF21", 0x12 },
+ { "AF22", 0x14 },
+ { "AF23", 0x16 },
+ { "AF31", 0x1A },
+ { "AF32", 0x1C },
+ { "AF33", 0x1E },
+ { "AF41", 0x22 },
+ { "AF42", 0x24 },
+ { "AF43", 0x26 },
+ { "EF", 0x2E },
+};
+
+int ast_str2cos(const char *value, unsigned int *cos)
+{
+ int fval;
+
+ if (sscanf(value, "%d", &fval) == 1) {
+ if (fval < 8) {
+ *cos = fval;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int ast_str2tos(const char *value, unsigned int *tos)
+{
+ int fval;
+ unsigned int x;
+
+ if (sscanf(value, "%i", &fval) == 1) {
+ *tos = fval & 0xFF;
+ return 0;
+ }
+
+ for (x = 0; x < sizeof(dscp_pool1) / sizeof(dscp_pool1[0]); x++) {
+ if (!strcasecmp(value, dscp_pool1[x].name)) {
+ *tos = dscp_pool1[x].space << 2;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+const char *ast_tos2str(unsigned int tos)
+{
+ unsigned int x;
+
+ for (x = 0; x < sizeof(dscp_pool1) / sizeof(dscp_pool1[0]); x++) {
+ if (dscp_pool1[x].space == (tos >> 2))
+ return dscp_pool1[x].name;
+ }
+
+ return "unknown";
+}
+
+int ast_get_ip(struct sockaddr_in *sin, const char *value)
+{
+ return ast_get_ip_or_srv(sin, value, NULL);
+}
+
+int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
+{
+ int s;
+ struct sockaddr_in sin;
+ socklen_t slen;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ ast_log(LOG_ERROR, "Cannot create socket\n");
+ return -1;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_port = 5060;
+ sin.sin_addr = *them;
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin))) {
+ ast_log(LOG_WARNING, "Cannot connect\n");
+ close(s);
+ return -1;
+ }
+ slen = sizeof(sin);
+ if (getsockname(s, (struct sockaddr *)&sin, &slen)) {
+ ast_log(LOG_WARNING, "Cannot get socket name\n");
+ close(s);
+ return -1;
+ }
+ close(s);
+ ast_debug(3, "Found IP address for this socket\n");
+ *us = sin.sin_addr;
+ return 0;
+}
+
+int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr)
+{
+ char ourhost[MAXHOSTNAMELEN] = "";
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ struct in_addr saddr;
+
+ /* just use the bind address if it is nonzero */
+ if (ntohl(bindaddr.sin_addr.s_addr)) {
+ memcpy(ourip, &bindaddr.sin_addr, sizeof(*ourip));
+ ast_debug(3, "Attached to given IP address\n");
+ return 0;
+ }
+ /* try to use our hostname */
+ if (gethostname(ourhost, sizeof(ourhost) - 1)) {
+ ast_log(LOG_WARNING, "Unable to get hostname\n");
+ } else {
+ hp = ast_gethostbyname(ourhost, &ahp);
+ if (hp) {
+ memcpy(ourip, hp->h_addr, sizeof(*ourip));
+ ast_debug(3, "Found one IP address based on local hostname %s.\n", ourhost);
+ return 0;
+ }
+ }
+ ast_debug(3, "Trying to check A.ROOT-SERVERS.NET and get our IP address for that connection\n");
+ /* A.ROOT-SERVERS.NET. */
+ if (inet_aton("198.41.0.4", &saddr) && !ast_ouraddrfor(&saddr, ourip))
+ return 0;
+ ast_debug(3, "Failed to find any IP address for us\n");
+ return -1;
+}
+
diff --git a/trunk/main/adsistub.c b/trunk/main/adsistub.c
new file mode 100644
index 000000000..8f2908225
--- /dev/null
+++ b/trunk/main/adsistub.c
@@ -0,0 +1,77 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Christopher L. Wade <wade.christopher@gmail.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.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/adsi.h"
+
+#ifdef SKREP
+#define build_stub(func_name,...) \
+static int stub_ ## func_name(__VA_ARGS__) \
+{ \
+ if (option_debug > 4) \
+ ast_log(LOG_NOTICE, "ADSI support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_ ## func_name;
+#endif
+#define build_stub(func_name,...) \
+static int stub_##func_name(__VA_ARGS__) \
+{ \
+ ast_debug(5, "ADSI support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_##func_name;
+
+build_stub(ast_adsi_channel_init, struct ast_channel *chan)
+build_stub(ast_adsi_begin_download, struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
+build_stub(ast_adsi_end_download, struct ast_channel *chan)
+build_stub(ast_adsi_channel_restore, struct ast_channel *chan)
+build_stub(ast_adsi_print, struct ast_channel *chan, char **lines, int *align, int voice)
+build_stub(ast_adsi_load_session, struct ast_channel *chan, unsigned char *app, int ver, int data)
+build_stub(ast_adsi_unload_session, struct ast_channel *chan)
+build_stub(ast_adsi_transmit_messages, struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype)
+build_stub(ast_adsi_transmit_message, struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
+build_stub(ast_adsi_transmit_message_full, struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype, int dowait)
+build_stub(ast_adsi_read_encoded_dtmf, struct ast_channel *chan, unsigned char *buf, int maxlen)
+build_stub(ast_adsi_connect_session, unsigned char *buf, unsigned char *fdn, int ver)
+build_stub(ast_adsi_query_cpeid, unsigned char *buf)
+build_stub(ast_adsi_query_cpeinfo, unsigned char *buf)
+build_stub(ast_adsi_get_cpeid, struct ast_channel *chan, unsigned char *cpeid, int voice)
+build_stub(ast_adsi_get_cpeinfo, struct ast_channel *chan, int *width, int *height, int *buttons, int voice)
+build_stub(ast_adsi_download_connect, unsigned char *buf, char *service, unsigned char *fdn, unsigned char *sec, int ver)
+build_stub(ast_adsi_disconnect_session, unsigned char *buf)
+build_stub(ast_adsi_download_disconnect, unsigned char *buf)
+build_stub(ast_adsi_data_mode, unsigned char *buf)
+build_stub(ast_adsi_clear_soft_keys, unsigned char *buf)
+build_stub(ast_adsi_clear_screen, unsigned char *buf)
+build_stub(ast_adsi_voice_mode, unsigned char *buf, int when)
+build_stub(ast_adsi_available, struct ast_channel *chan)
+build_stub(ast_adsi_display, unsigned char *buf, int page, int line, int just, int wrap, char *col1, char *col2)
+build_stub(ast_adsi_set_line, unsigned char *buf, int page, int line)
+build_stub(ast_adsi_load_soft_key, unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
+build_stub(ast_adsi_set_keys, unsigned char *buf, unsigned char *keys)
+build_stub(ast_adsi_input_control, unsigned char *buf, int page, int line, int display, int format, int just)
+build_stub(ast_adsi_input_format, unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
diff --git a/trunk/main/aescrypt.c b/trunk/main/aescrypt.c
new file mode 100644
index 000000000..7b34809bb
--- /dev/null
+++ b/trunk/main/aescrypt.c
@@ -0,0 +1,321 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+*/
+
+/*! \file
+ *
+ * \brief This file contains the code for implementing encryption and decryption
+ * for AES (Rijndael) for block and key sizes of 16, 24 and 32 bytes. It
+ * can optionally be replaced by code written in assembler using NASM. For
+ * further details see the file aesopt.h
+ *
+ * \author Dr Brian Gladman <brg@gladman.me.uk>
+ */
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#ifndef HAVE_CRYPTO
+
+#include "aesopt.h"
+
+#define si(y,x,k,c) (s(y,c) = word_in(x, c) ^ (k)[c])
+#define so(y,x,c) word_out(y, c, s(x,c))
+
+#if defined(ARRAYS)
+#define locals(y,x) x[4],y[4]
+#else
+#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3
+#endif
+
+#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
+ s(y,2) = s(x,2); s(y,3) = s(x,3);
+#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3)
+#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3)
+#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3)
+
+#if defined(ENCRYPTION) && !defined(AES_ASM)
+
+/* Visual C++ .Net v7.1 provides the fastest encryption code when using
+ Pentium optimiation with small code but this is poor for decryption
+ so we need to control this with the following VC++ pragmas
+*/
+
+#if defined(_MSC_VER)
+#pragma optimize( "s", on )
+#endif
+
+/* Given the column (c) of the output state variable, the following
+ macros give the input state variables which are needed in its
+ computation for each row (r) of the state. All the alternative
+ macros give the same end values but expand into different ways
+ of calculating these values. In particular the complex macro
+ used for dynamically variable block sizes is designed to expand
+ to a compile time constant whenever possible but will expand to
+ conditional clauses on some branches (I am grateful to Frank
+ Yellin for this construction)
+*/
+
+#define fwd_var(x,r,c)\
+ ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
+ : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\
+ : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
+ : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2)))
+
+#if defined(FT4_SET)
+#undef dec_fmvars
+#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c))
+#elif defined(FT1_SET)
+#undef dec_fmvars
+#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(f,n),fwd_var,rf1,c))
+#else
+#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ fwd_mcol(no_table(x,t_use(s,box),fwd_var,rf1,c)))
+#endif
+
+#if defined(FL4_SET)
+#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,l),fwd_var,rf1,c))
+#elif defined(FL1_SET)
+#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(f,l),fwd_var,rf1,c))
+#else
+#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(s,box),fwd_var,rf1,c))
+#endif
+
+aes_rval aes_encrypt(const void *in_blk, void *out_blk, const aes_encrypt_ctx cx[1])
+{ aes_32t locals(b0, b1);
+ const aes_32t *kp = cx->ks;
+#ifdef dec_fmvars
+ dec_fmvars; /* declare variables for fwd_mcol() if needed */
+#endif
+
+ aes_32t nr = (kp[45] ^ kp[52] ^ kp[53] ? kp[52] : 14);
+
+#ifdef AES_ERR_CHK
+ if( (nr != 10 || !(kp[0] | kp[3] | kp[4]))
+ && (nr != 12 || !(kp[0] | kp[5] | kp[6]))
+ && (nr != 14 || !(kp[0] | kp[7] | kp[8])) )
+ return aes_error;
+#endif
+
+ state_in(b0, in_blk, kp);
+
+#if (ENC_UNROLL == FULL)
+
+ switch(nr)
+ {
+ case 14:
+ round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
+ kp += 2 * N_COLS;
+ case 12:
+ round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
+ kp += 2 * N_COLS;
+ case 10:
+ round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 3 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 4 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 5 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 6 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 7 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 8 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 9 * N_COLS);
+ round(fwd_lrnd, b0, b1, kp +10 * N_COLS);
+ }
+
+#else
+
+#if (ENC_UNROLL == PARTIAL)
+ { aes_32t rnd;
+ for(rnd = 0; rnd < (nr >> 1) - 1; ++rnd)
+ {
+ kp += N_COLS;
+ round(fwd_rnd, b1, b0, kp);
+ kp += N_COLS;
+ round(fwd_rnd, b0, b1, kp);
+ }
+ kp += N_COLS;
+ round(fwd_rnd, b1, b0, kp);
+#else
+ { aes_32t rnd;
+ for(rnd = 0; rnd < nr - 1; ++rnd)
+ {
+ kp += N_COLS;
+ round(fwd_rnd, b1, b0, kp);
+ l_copy(b0, b1);
+ }
+#endif
+ kp += N_COLS;
+ round(fwd_lrnd, b0, b1, kp);
+ }
+#endif
+
+ state_out(out_blk, b0);
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(DECRYPTION) && !defined(AES_ASM)
+
+/* Visual C++ .Net v7.1 provides the fastest encryption code when using
+ Pentium optimiation with small code but this is poor for decryption
+ so we need to control this with the following VC++ pragmas
+*/
+
+#if defined(_MSC_VER)
+#pragma optimize( "t", on )
+#endif
+
+/* Given the column (c) of the output state variable, the following
+ macros give the input state variables which are needed in its
+ computation for each row (r) of the state. All the alternative
+ macros give the same end values but expand into different ways
+ of calculating these values. In particular the complex macro
+ used for dynamically variable block sizes is designed to expand
+ to a compile time constant whenever possible but will expand to
+ conditional clauses on some branches (I am grateful to Frank
+ Yellin for this construction)
+*/
+
+#define inv_var(x,r,c)\
+ ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
+ : r == 1 ? ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))\
+ : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
+ : ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0)))
+
+#if defined(IT4_SET)
+#undef dec_imvars
+#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,n),inv_var,rf1,c))
+#elif defined(IT1_SET)
+#undef dec_imvars
+#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(i,n),inv_var,rf1,c))
+#else
+#define inv_rnd(y,x,k,c) (s(y,c) = inv_mcol((k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c)))
+#endif
+
+#if defined(IL4_SET)
+#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,l),inv_var,rf1,c))
+#elif defined(IL1_SET)
+#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(i,l),inv_var,rf1,c))
+#else
+#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c))
+#endif
+
+aes_rval aes_decrypt(const void *in_blk, void *out_blk, const aes_decrypt_ctx cx[1])
+{ aes_32t locals(b0, b1);
+#ifdef dec_imvars
+ dec_imvars; /* declare variables for inv_mcol() if needed */
+#endif
+
+ aes_32t nr = (cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] ? cx->ks[52] : 14);
+ const aes_32t *kp = cx->ks + nr * N_COLS;
+
+#ifdef AES_ERR_CHK
+ if( (nr != 10 || !(cx->ks[0] | cx->ks[3] | cx->ks[4]))
+ && (nr != 12 || !(cx->ks[0] | cx->ks[5] | cx->ks[6]))
+ && (nr != 14 || !(cx->ks[0] | cx->ks[7] | cx->ks[8])) )
+ return aes_error;
+#endif
+
+ state_in(b0, in_blk, kp);
+
+#if (DEC_UNROLL == FULL)
+
+ switch(nr)
+ {
+ case 14:
+ round(inv_rnd, b1, b0, kp - 1 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 2 * N_COLS);
+ kp -= 2 * N_COLS;
+ case 12:
+ round(inv_rnd, b1, b0, kp - 1 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 2 * N_COLS);
+ kp -= 2 * N_COLS;
+ case 10:
+ round(inv_rnd, b1, b0, kp - 1 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 2 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 3 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 4 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 5 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 6 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 7 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 8 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 9 * N_COLS);
+ round(inv_lrnd, b0, b1, kp - 10 * N_COLS);
+ }
+
+#else
+
+#if (DEC_UNROLL == PARTIAL)
+ { aes_32t rnd;
+ for(rnd = 0; rnd < (nr >> 1) - 1; ++rnd)
+ {
+ kp -= N_COLS;
+ round(inv_rnd, b1, b0, kp);
+ kp -= N_COLS;
+ round(inv_rnd, b0, b1, kp);
+ }
+ kp -= N_COLS;
+ round(inv_rnd, b1, b0, kp);
+#else
+ { aes_32t rnd;
+ for(rnd = 0; rnd < nr - 1; ++rnd)
+ {
+ kp -= N_COLS;
+ round(inv_rnd, b1, b0, kp);
+ l_copy(b0, b1);
+ }
+#endif
+ kp -= N_COLS;
+ round(inv_lrnd, b0, b1, kp);
+ }
+#endif
+
+ state_out(out_blk, b0);
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#endif /* !HAVE_CRYPTO */
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/trunk/main/aeskey.c b/trunk/main/aeskey.c
new file mode 100644
index 000000000..cd0c7faf8
--- /dev/null
+++ b/trunk/main/aeskey.c
@@ -0,0 +1,473 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+*/
+
+/*! \file
+ *
+ * \brief This file contains the code for implementing the key schedule for AES
+ * (Rijndael) for block and key sizes of 16, 24, and 32 bytes. See aesopt.h
+ * for further details including optimisation.
+ *
+ * \author Dr Brian Gladman <brg@gladman.me.uk>
+ */
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#ifndef HAVE_CRYPTO
+
+#include "aesopt.h"
+
+/* Initialise the key schedule from the user supplied key. The key
+ length can be specified in bytes, with legal values of 16, 24
+ and 32, or in bits, with legal values of 128, 192 and 256. These
+ values correspond with Nk values of 4, 6 and 8 respectively.
+
+ The following macros implement a single cycle in the key
+ schedule generation process. The number of cycles needed
+ for each cx->n_col and nk value is:
+
+ nk = 4 5 6 7 8
+ ------------------------------
+ cx->n_col = 4 10 9 8 7 7
+ cx->n_col = 5 14 11 10 9 9
+ cx->n_col = 6 19 15 12 11 11
+ cx->n_col = 7 21 19 16 13 14
+ cx->n_col = 8 29 23 19 17 14
+*/
+
+#define ke4(k,i) \
+{ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+5] = ss[1] ^= ss[0]; \
+ k[4*(i)+6] = ss[2] ^= ss[1]; k[4*(i)+7] = ss[3] ^= ss[2]; \
+}
+#define kel4(k,i) \
+{ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+5] = ss[1] ^= ss[0]; \
+ k[4*(i)+6] = ss[2] ^= ss[1]; k[4*(i)+7] = ss[3] ^= ss[2]; \
+}
+
+#define ke6(k,i) \
+{ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 7] = ss[1] ^= ss[0]; \
+ k[6*(i)+ 8] = ss[2] ^= ss[1]; k[6*(i)+ 9] = ss[3] ^= ss[2]; \
+ k[6*(i)+10] = ss[4] ^= ss[3]; k[6*(i)+11] = ss[5] ^= ss[4]; \
+}
+#define kel6(k,i) \
+{ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 7] = ss[1] ^= ss[0]; \
+ k[6*(i)+ 8] = ss[2] ^= ss[1]; k[6*(i)+ 9] = ss[3] ^= ss[2]; \
+}
+
+#define ke8(k,i) \
+{ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 9] = ss[1] ^= ss[0]; \
+ k[8*(i)+10] = ss[2] ^= ss[1]; k[8*(i)+11] = ss[3] ^= ss[2]; \
+ k[8*(i)+12] = ss[4] ^= ls_box(ss[3],0); k[8*(i)+13] = ss[5] ^= ss[4]; \
+ k[8*(i)+14] = ss[6] ^= ss[5]; k[8*(i)+15] = ss[7] ^= ss[6]; \
+}
+#define kel8(k,i) \
+{ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 9] = ss[1] ^= ss[0]; \
+ k[8*(i)+10] = ss[2] ^= ss[1]; k[8*(i)+11] = ss[3] ^= ss[2]; \
+}
+
+#if defined(ENCRYPTION_KEY_SCHEDULE)
+
+#if defined(AES_128) || defined(AES_VAR)
+
+aes_rval aes_encrypt_key128(const void *in_key, aes_encrypt_ctx cx[1])
+{ aes_32t ss[4];
+
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if ENC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < ((11 * N_COLS - 1) / 4); ++i)
+ ke4(cx->ks, i);
+ }
+#else
+ ke4(cx->ks, 0); ke4(cx->ks, 1);
+ ke4(cx->ks, 2); ke4(cx->ks, 3);
+ ke4(cx->ks, 4); ke4(cx->ks, 5);
+ ke4(cx->ks, 6); ke4(cx->ks, 7);
+ ke4(cx->ks, 8); kel4(cx->ks, 9);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45] = 0;
+ cx->ks[52] = 10;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_192) || defined(AES_VAR)
+
+aes_rval aes_encrypt_key192(const void *in_key, aes_encrypt_ctx cx[1])
+{ aes_32t ss[6];
+
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+
+#if ENC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < (13 * N_COLS - 1) / 6; ++i)
+ ke6(cx->ks, i);
+ }
+#else
+ ke6(cx->ks, 0); ke6(cx->ks, 1);
+ ke6(cx->ks, 2); ke6(cx->ks, 3);
+ ke6(cx->ks, 4); ke6(cx->ks, 5);
+ ke6(cx->ks, 6); kel6(cx->ks, 7);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45];
+ cx->ks[52] = 12;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_256) || defined(AES_VAR)
+
+aes_rval aes_encrypt_key256(const void *in_key, aes_encrypt_ctx cx[1])
+{ aes_32t ss[8];
+
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+ cx->ks[6] = ss[6] = word_in(in_key, 6);
+ cx->ks[7] = ss[7] = word_in(in_key, 7);
+
+#if ENC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < (15 * N_COLS - 1) / 8; ++i)
+ ke8(cx->ks, i);
+ }
+#else
+ ke8(cx->ks, 0); ke8(cx->ks, 1);
+ ke8(cx->ks, 2); ke8(cx->ks, 3);
+ ke8(cx->ks, 4); ke8(cx->ks, 5);
+ kel8(cx->ks, 6);
+#endif
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_VAR)
+
+aes_rval aes_encrypt_key(const void *in_key, int key_len, aes_encrypt_ctx cx[1])
+{
+ switch(key_len)
+ {
+#ifdef AES_ERR_CHK
+ case 16: case 128: return aes_encrypt_key128(in_key, cx);
+ case 24: case 192: return aes_encrypt_key192(in_key, cx);
+ case 32: case 256: return aes_encrypt_key256(in_key, cx);
+ default: return aes_error;
+#else
+ case 16: case 128: aes_encrypt_key128(in_key, cx); return;
+ case 24: case 192: aes_encrypt_key192(in_key, cx); return;
+ case 32: case 256: aes_encrypt_key256(in_key, cx); return;
+#endif
+ }
+}
+
+#endif
+
+#endif
+
+#if defined(DECRYPTION_KEY_SCHEDULE)
+
+#if DEC_ROUND == NO_TABLES
+#define ff(x) (x)
+#else
+#define ff(x) inv_mcol(x)
+#ifdef dec_imvars
+#define d_vars dec_imvars
+#endif
+#endif
+
+#if 1
+#define kdf4(k,i) \
+{ ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; ss[1] = ss[1] ^ ss[3]; ss[2] = ss[2] ^ ss[3]; ss[3] = ss[3]; \
+ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; \
+ ss[4] ^= k[4*(i)]; k[4*(i)+4] = ff(ss[4]); ss[4] ^= k[4*(i)+1]; k[4*(i)+5] = ff(ss[4]); \
+ ss[4] ^= k[4*(i)+2]; k[4*(i)+6] = ff(ss[4]); ss[4] ^= k[4*(i)+3]; k[4*(i)+7] = ff(ss[4]); \
+}
+#define kd4(k,i) \
+{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; ss[4] = ff(ss[4]); \
+ k[4*(i)+4] = ss[4] ^= k[4*(i)]; k[4*(i)+5] = ss[4] ^= k[4*(i)+1]; \
+ k[4*(i)+6] = ss[4] ^= k[4*(i)+2]; k[4*(i)+7] = ss[4] ^= k[4*(i)+3]; \
+}
+#define kdl4(k,i) \
+{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; \
+ k[4*(i)+4] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; k[4*(i)+5] = ss[1] ^ ss[3]; \
+ k[4*(i)+6] = ss[0]; k[4*(i)+7] = ss[1]; \
+}
+#else
+#define kdf4(k,i) \
+{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+ 4] = ff(ss[0]); ss[1] ^= ss[0]; k[4*(i)+ 5] = ff(ss[1]); \
+ ss[2] ^= ss[1]; k[4*(i)+ 6] = ff(ss[2]); ss[3] ^= ss[2]; k[4*(i)+ 7] = ff(ss[3]); \
+}
+#define kd4(k,i) \
+{ ss[4] = ls_box(ss[3],3) ^ t_use(r,c)[i]; \
+ ss[0] ^= ss[4]; ss[4] = ff(ss[4]); k[4*(i)+ 4] = ss[4] ^= k[4*(i)]; \
+ ss[1] ^= ss[0]; k[4*(i)+ 5] = ss[4] ^= k[4*(i)+ 1]; \
+ ss[2] ^= ss[1]; k[4*(i)+ 6] = ss[4] ^= k[4*(i)+ 2]; \
+ ss[3] ^= ss[2]; k[4*(i)+ 7] = ss[4] ^= k[4*(i)+ 3]; \
+}
+#define kdl4(k,i) \
+{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+ 4] = ss[0]; ss[1] ^= ss[0]; k[4*(i)+ 5] = ss[1]; \
+ ss[2] ^= ss[1]; k[4*(i)+ 6] = ss[2]; ss[3] ^= ss[2]; k[4*(i)+ 7] = ss[3]; \
+}
+#endif
+
+#define kdf6(k,i) \
+{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 6] = ff(ss[0]); ss[1] ^= ss[0]; k[6*(i)+ 7] = ff(ss[1]); \
+ ss[2] ^= ss[1]; k[6*(i)+ 8] = ff(ss[2]); ss[3] ^= ss[2]; k[6*(i)+ 9] = ff(ss[3]); \
+ ss[4] ^= ss[3]; k[6*(i)+10] = ff(ss[4]); ss[5] ^= ss[4]; k[6*(i)+11] = ff(ss[5]); \
+}
+#define kd6(k,i) \
+{ ss[6] = ls_box(ss[5],3) ^ t_use(r,c)[i]; \
+ ss[0] ^= ss[6]; ss[6] = ff(ss[6]); k[6*(i)+ 6] = ss[6] ^= k[6*(i)]; \
+ ss[1] ^= ss[0]; k[6*(i)+ 7] = ss[6] ^= k[6*(i)+ 1]; \
+ ss[2] ^= ss[1]; k[6*(i)+ 8] = ss[6] ^= k[6*(i)+ 2]; \
+ ss[3] ^= ss[2]; k[6*(i)+ 9] = ss[6] ^= k[6*(i)+ 3]; \
+ ss[4] ^= ss[3]; k[6*(i)+10] = ss[6] ^= k[6*(i)+ 4]; \
+ ss[5] ^= ss[4]; k[6*(i)+11] = ss[6] ^= k[6*(i)+ 5]; \
+}
+#define kdl6(k,i) \
+{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 6] = ss[0]; ss[1] ^= ss[0]; k[6*(i)+ 7] = ss[1]; \
+ ss[2] ^= ss[1]; k[6*(i)+ 8] = ss[2]; ss[3] ^= ss[2]; k[6*(i)+ 9] = ss[3]; \
+}
+
+#define kdf8(k,i) \
+{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 8] = ff(ss[0]); ss[1] ^= ss[0]; k[8*(i)+ 9] = ff(ss[1]); \
+ ss[2] ^= ss[1]; k[8*(i)+10] = ff(ss[2]); ss[3] ^= ss[2]; k[8*(i)+11] = ff(ss[3]); \
+ ss[4] ^= ls_box(ss[3],0); k[8*(i)+12] = ff(ss[4]); ss[5] ^= ss[4]; k[8*(i)+13] = ff(ss[5]); \
+ ss[6] ^= ss[5]; k[8*(i)+14] = ff(ss[6]); ss[7] ^= ss[6]; k[8*(i)+15] = ff(ss[7]); \
+}
+#define kd8(k,i) \
+{ aes_32t g = ls_box(ss[7],3) ^ t_use(r,c)[i]; \
+ ss[0] ^= g; g = ff(g); k[8*(i)+ 8] = g ^= k[8*(i)]; \
+ ss[1] ^= ss[0]; k[8*(i)+ 9] = g ^= k[8*(i)+ 1]; \
+ ss[2] ^= ss[1]; k[8*(i)+10] = g ^= k[8*(i)+ 2]; \
+ ss[3] ^= ss[2]; k[8*(i)+11] = g ^= k[8*(i)+ 3]; \
+ g = ls_box(ss[3],0); \
+ ss[4] ^= g; g = ff(g); k[8*(i)+12] = g ^= k[8*(i)+ 4]; \
+ ss[5] ^= ss[4]; k[8*(i)+13] = g ^= k[8*(i)+ 5]; \
+ ss[6] ^= ss[5]; k[8*(i)+14] = g ^= k[8*(i)+ 6]; \
+ ss[7] ^= ss[6]; k[8*(i)+15] = g ^= k[8*(i)+ 7]; \
+}
+#define kdl8(k,i) \
+{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 8] = ss[0]; ss[1] ^= ss[0]; k[8*(i)+ 9] = ss[1]; \
+ ss[2] ^= ss[1]; k[8*(i)+10] = ss[2]; ss[3] ^= ss[2]; k[8*(i)+11] = ss[3]; \
+}
+
+#if defined(AES_128) || defined(AES_VAR)
+
+aes_rval aes_decrypt_key128(const void *in_key, aes_decrypt_ctx cx[1])
+{ aes_32t ss[5];
+#ifdef d_vars
+ d_vars;
+#endif
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if DEC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < (11 * N_COLS - 1) / 4; ++i)
+ ke4(cx->ks, i);
+#if !(DEC_ROUND == NO_TABLES)
+ for(i = N_COLS; i < 10 * N_COLS; ++i)
+ cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+ }
+#else
+ kdf4(cx->ks, 0); kd4(cx->ks, 1);
+ kd4(cx->ks, 2); kd4(cx->ks, 3);
+ kd4(cx->ks, 4); kd4(cx->ks, 5);
+ kd4(cx->ks, 6); kd4(cx->ks, 7);
+ kd4(cx->ks, 8); kdl4(cx->ks, 9);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45] = 0;
+ cx->ks[52] = 10;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_192) || defined(AES_VAR)
+
+aes_rval aes_decrypt_key192(const void *in_key, aes_decrypt_ctx cx[1])
+{ aes_32t ss[7];
+#ifdef d_vars
+ d_vars;
+#endif
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if DEC_UNROLL == NONE
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+ { aes_32t i;
+
+ for(i = 0; i < (13 * N_COLS - 1) / 6; ++i)
+ ke6(cx->ks, i);
+#if !(DEC_ROUND == NO_TABLES)
+ for(i = N_COLS; i < 12 * N_COLS; ++i)
+ cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+ }
+#else
+ cx->ks[4] = ff(ss[4] = word_in(in_key, 4));
+ cx->ks[5] = ff(ss[5] = word_in(in_key, 5));
+ kdf6(cx->ks, 0); kd6(cx->ks, 1);
+ kd6(cx->ks, 2); kd6(cx->ks, 3);
+ kd6(cx->ks, 4); kd6(cx->ks, 5);
+ kd6(cx->ks, 6); kdl6(cx->ks, 7);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45];
+ cx->ks[52] = 12;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_256) || defined(AES_VAR)
+
+aes_rval aes_decrypt_key256(const void *in_key, aes_decrypt_ctx cx[1])
+{ aes_32t ss[8];
+#ifdef d_vars
+ d_vars;
+#endif
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if DEC_UNROLL == NONE
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+ cx->ks[6] = ss[6] = word_in(in_key, 6);
+ cx->ks[7] = ss[7] = word_in(in_key, 7);
+ { aes_32t i;
+
+ for(i = 0; i < (15 * N_COLS - 1) / 8; ++i)
+ ke8(cx->ks, i);
+#if !(DEC_ROUND == NO_TABLES)
+ for(i = N_COLS; i < 14 * N_COLS; ++i)
+ cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+ }
+#else
+ cx->ks[4] = ff(ss[4] = word_in(in_key, 4));
+ cx->ks[5] = ff(ss[5] = word_in(in_key, 5));
+ cx->ks[6] = ff(ss[6] = word_in(in_key, 6));
+ cx->ks[7] = ff(ss[7] = word_in(in_key, 7));
+ kdf8(cx->ks, 0); kd8(cx->ks, 1);
+ kd8(cx->ks, 2); kd8(cx->ks, 3);
+ kd8(cx->ks, 4); kd8(cx->ks, 5);
+ kdl8(cx->ks, 6);
+#endif
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_VAR)
+
+aes_rval aes_decrypt_key(const void *in_key, int key_len, aes_decrypt_ctx cx[1])
+{
+ switch(key_len)
+ {
+#ifdef AES_ERR_CHK
+ case 16: case 128: return aes_decrypt_key128(in_key, cx);
+ case 24: case 192: return aes_decrypt_key192(in_key, cx);
+ case 32: case 256: return aes_decrypt_key256(in_key, cx);
+ default: return aes_error;
+#else
+ case 16: case 128: aes_decrypt_key128(in_key, cx); return;
+ case 24: case 192: aes_decrypt_key192(in_key, cx); return;
+ case 32: case 256: aes_decrypt_key256(in_key, cx); return;
+#endif
+ }
+}
+
+#endif
+
+#endif
+
+#endif /* !HAVE_CRYPTO */
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/trunk/main/aesopt.h b/trunk/main/aesopt.h
new file mode 100644
index 000000000..bb4f05a0b
--- /dev/null
+++ b/trunk/main/aesopt.h
@@ -0,0 +1,1029 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+ My thanks go to Dag Arne Osvik for devising the schemes used here for key
+ length derivation from the form of the key schedule
+
+ This file contains the compilation options for AES (Rijndael) and code
+ that is common across encryption, key scheduling and table generation.
+
+ OPERATION
+
+ These source code files implement the AES algorithm Rijndael designed by
+ Joan Daemen and Vincent Rijmen. This version is designed for the standard
+ block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24
+ and 32 bytes).
+
+ This version is designed for flexibility and speed using operations on
+ 32-bit words rather than operations on bytes. It can be compiled with
+ either big or little endian internal byte order but is faster when the
+ native byte order for the processor is used.
+
+ THE CIPHER INTERFACE
+
+ The cipher interface is implemented as an array of bytes in which lower
+ AES bit sequence indexes map to higher numeric significance within bytes.
+
+ aes_08t (an unsigned 8-bit type)
+ aes_32t (an unsigned 32-bit type)
+ struct aes_encrypt_ctx (structure for the cipher encryption context)
+ struct aes_decrypt_ctx (structure for the cipher decryption context)
+ aes_rval the function return type
+
+ C subroutine calls:
+
+ aes_rval aes_encrypt_key128(const void *in_key, aes_encrypt_ctx cx[1]);
+ aes_rval aes_encrypt_key192(const void *in_key, aes_encrypt_ctx cx[1]);
+ aes_rval aes_encrypt_key256(const void *in_key, aes_encrypt_ctx cx[1]);
+ aes_rval aes_encrypt(const void *in_blk,
+ void *out_blk, const aes_encrypt_ctx cx[1]);
+
+ aes_rval aes_decrypt_key128(const void *in_key, aes_decrypt_ctx cx[1]);
+ aes_rval aes_decrypt_key192(const void *in_key, aes_decrypt_ctx cx[1]);
+ aes_rval aes_decrypt_key256(const void *in_key, aes_decrypt_ctx cx[1]);
+ aes_rval aes_decrypt(const void *in_blk,
+ void *out_blk, const aes_decrypt_ctx cx[1]);
+
+ IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that
+ you call genTabs() before AES is used so that the tables are initialised.
+
+ C++ aes class subroutines:
+
+ Class AESencrypt for encryption
+
+ Construtors:
+ AESencrypt(void)
+ AESencrypt(const void *in_key) - 128 bit key
+ Members:
+ void key128(const void *in_key)
+ void key192(const void *in_key)
+ void key256(const void *in_key)
+ void encrypt(const void *in_blk, void *out_blk) const
+
+ Class AESdecrypt for encryption
+ Construtors:
+ AESdecrypt(void)
+ AESdecrypt(const void *in_key) - 128 bit key
+ Members:
+ void key128(const void *in_key)
+ void key192(const void *in_key)
+ void key256(const void *in_key)
+ void decrypt(const void *in_blk, void *out_blk) const
+
+ COMPILATION
+
+ The files used to provide AES (Rijndael) are
+
+ a. aes.h for the definitions needed for use in C.
+ b. aescpp.h for the definitions needed for use in C++.
+ c. aesopt.h for setting compilation options (also includes common code).
+ d. aescrypt.c for encryption and decrytpion, or
+ e. aeskey.c for key scheduling.
+ f. aestab.c for table loading or generation.
+ g. aescrypt.asm for encryption and decryption using assembler code.
+ h. aescrypt.mmx.asm for encryption and decryption using MMX assembler.
+
+ To compile AES (Rijndael) for use in C code use aes.h and set the
+ defines here for the facilities you need (key lengths, encryption
+ and/or decryption). Do not define AES_DLL or AES_CPP. Set the options
+ for optimisations and table sizes here.
+
+ To compile AES (Rijndael) for use in in C++ code use aescpp.h but do
+ not define AES_DLL
+
+ To compile AES (Rijndael) in C as a Dynamic Link Library DLL) use
+ aes.h and include the AES_DLL define.
+
+ CONFIGURATION OPTIONS (here and in aes.h)
+
+ a. set AES_DLL in aes.h if AES (Rijndael) is to be compiled as a DLL
+ b. You may need to set PLATFORM_BYTE_ORDER to define the byte order.
+ c. If you want the code to run in a specific internal byte order, then
+ ALGORITHM_BYTE_ORDER must be set accordingly.
+ d. set other configuration options decribed below.
+*/
+
+#ifndef _AESOPT_H
+#define _AESOPT_H
+
+#include "asterisk/aes.h"
+#include "asterisk/endian.h"
+
+/* CONFIGURATION - USE OF DEFINES
+
+ Later in this section there are a number of defines that control the
+ operation of the code. In each section, the purpose of each define is
+ explained so that the relevant form can be included or excluded by
+ setting either 1's or 0's respectively on the branches of the related
+ #if clauses.
+*/
+
+/* BYTE ORDER IN 32-BIT WORDS
+
+ To obtain the highest speed on processors with 32-bit words, this code
+ needs to determine the byte order of the target machine. The following
+ block of code is an attempt to capture the most obvious ways in which
+ various environemnts define byte order. It may well fail, in which case
+ the definitions will need to be set by editing at the points marked
+ **** EDIT HERE IF NECESSARY **** below. My thanks to Peter Gutmann for
+ some of these defines (from cryptlib).
+*/
+
+#define BRG_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */
+#define BRG_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */
+
+#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \
+ defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \
+ defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \
+ defined( vax ) || defined( vms ) || defined( VMS ) || \
+ defined( __VMS )
+
+#define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+
+#endif
+
+#if defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \
+ defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \
+ defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \
+ defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \
+ defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \
+ defined( __TANDEM ) || defined( THINK_C ) || defined( __VMCMS__ )
+
+#define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+
+#endif
+
+/* if the platform is still not known, try to find its byte order */
+/* from commonly used definitions in the headers included earlier */
+
+#if !defined(PLATFORM_BYTE_ORDER)
+
+#if defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN)
+# if defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif !defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# elif defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# endif
+
+#elif defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN)
+# if defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif !defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _LITTLE_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# endif
+
+#elif defined(__LITTLE_ENDIAN__) || defined(__BIG_ENDIAN__)
+# if defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif !defined(__LITTLE_ENDIAN__) && defined(__BIG_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __LITTLE_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# endif
+
+#elif 0 /* **** EDIT HERE IF NECESSARY **** */
+#define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+
+#elif 0 /* **** EDIT HERE IF NECESSARY **** */
+#define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+
+#else
+#error Please edit aesopt.h (line 235 or 238) to set the platform byte order
+#endif
+
+#endif
+
+/* SOME LOCAL DEFINITIONS */
+
+#define NO_TABLES 0
+#define ONE_TABLE 1
+#define FOUR_TABLES 4
+#define NONE 0
+#define PARTIAL 1
+#define FULL 2
+
+#if defined(bswap32)
+#define aes_sw32 bswap32
+#elif defined(bswap_32)
+#define aes_sw32 bswap_32
+#else
+#define brot(x,n) (((aes_32t)(x) << n) | ((aes_32t)(x) >> (32 - n)))
+#define aes_sw32(x) ((brot((x),8) & 0x00ff00ff) | (brot((x),24) & 0xff00ff00))
+#endif
+
+/* 1. FUNCTIONS REQUIRED
+
+ This implementation provides subroutines for encryption, decryption
+ and for setting the three key lengths (separately) for encryption
+ and decryption. When the assembler code is not being used the following
+ definition blocks allow the selection of the routines that are to be
+ included in the compilation.
+*/
+#ifdef AES_ENCRYPT
+#define ENCRYPTION
+#define ENCRYPTION_KEY_SCHEDULE
+#endif
+
+#ifdef AES_DECRYPT
+#define DECRYPTION
+#define DECRYPTION_KEY_SCHEDULE
+#endif
+
+/* 2. ASSEMBLER SUPPORT
+
+ This define (which can be on the command line) enables the use of the
+ assembler code routines for encryption and decryption with the C code
+ only providing key scheduling
+*/
+#if 0
+#define AES_ASM
+#endif
+
+/* 3. BYTE ORDER WITHIN 32 BIT WORDS
+
+ The fundamental data processing units in Rijndael are 8-bit bytes. The
+ input, output and key input are all enumerated arrays of bytes in which
+ bytes are numbered starting at zero and increasing to one less than the
+ number of bytes in the array in question. This enumeration is only used
+ for naming bytes and does not imply any adjacency or order relationship
+ from one byte to another. When these inputs and outputs are considered
+ as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to
+ byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte.
+ In this implementation bits are numbered from 0 to 7 starting at the
+ numerically least significant end of each byte (bit n represents 2^n).
+
+ However, Rijndael can be implemented more efficiently using 32-bit
+ words by packing bytes into words so that bytes 4*n to 4*n+3 are placed
+ into word[n]. While in principle these bytes can be assembled into words
+ in any positions, this implementation only supports the two formats in
+ which bytes in adjacent positions within words also have adjacent byte
+ numbers. This order is called big-endian if the lowest numbered bytes
+ in words have the highest numeric significance and little-endian if the
+ opposite applies.
+
+ This code can work in either order irrespective of the order used by the
+ machine on which it runs. Normally the internal byte order will be set
+ to the order of the processor on which the code is to be run but this
+ define can be used to reverse this in special situations
+
+ NOTE: Assembler code versions rely on PLATFORM_BYTE_ORDER being set
+*/
+#if 1 || defined(AES_ASM)
+#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER
+#elif 0
+#define ALGORITHM_BYTE_ORDER BRG_LITTLE_ENDIAN
+#elif 0
+#define ALGORITHM_BYTE_ORDER BRG_BIG_ENDIAN
+#else
+#error The algorithm byte order is not defined
+#endif
+
+/* 4. FAST INPUT/OUTPUT OPERATIONS.
+
+ On some machines it is possible to improve speed by transferring the
+ bytes in the input and output arrays to and from the internal 32-bit
+ variables by addressing these arrays as if they are arrays of 32-bit
+ words. On some machines this will always be possible but there may
+ be a large performance penalty if the byte arrays are not aligned on
+ the normal word boundaries. On other machines this technique will
+ lead to memory access errors when such 32-bit word accesses are not
+ properly aligned. The option SAFE_IO avoids such problems but will
+ often be slower on those machines that support misaligned access
+ (especially so if care is taken to align the input and output byte
+ arrays on 32-bit word boundaries). If SAFE_IO is not defined it is
+ assumed that access to byte arrays as if they are arrays of 32-bit
+ words will not cause problems when such accesses are misaligned.
+*/
+#if 1 && !defined(_MSC_VER)
+#define SAFE_IO
+#endif
+
+/* 5. LOOP UNROLLING
+
+ The code for encryption and decrytpion cycles through a number of rounds
+ that can be implemented either in a loop or by expanding the code into a
+ long sequence of instructions, the latter producing a larger program but
+ one that will often be much faster. The latter is called loop unrolling.
+ There are also potential speed advantages in expanding two iterations in
+ a loop with half the number of iterations, which is called partial loop
+ unrolling. The following options allow partial or full loop unrolling
+ to be set independently for encryption and decryption
+*/
+#if 1
+#define ENC_UNROLL FULL
+#elif 0
+#define ENC_UNROLL PARTIAL
+#else
+#define ENC_UNROLL NONE
+#endif
+
+#if 1
+#define DEC_UNROLL FULL
+#elif 0
+#define DEC_UNROLL PARTIAL
+#else
+#define DEC_UNROLL NONE
+#endif
+
+/* 6. FAST FINITE FIELD OPERATIONS
+
+ If this section is included, tables are used to provide faster finite
+ field arithmetic (this has no effect if FIXED_TABLES is defined).
+*/
+#if 1
+#define FF_TABLES
+#endif
+
+/* 7. INTERNAL STATE VARIABLE FORMAT
+
+ The internal state of Rijndael is stored in a number of local 32-bit
+ word varaibles which can be defined either as an array or as individual
+ names variables. Include this section if you want to store these local
+ varaibles in arrays. Otherwise individual local variables will be used.
+*/
+#if 1
+#define ARRAYS
+#endif
+
+/* In this implementation the columns of the state array are each held in
+ 32-bit words. The state array can be held in various ways: in an array
+ of words, in a number of individual word variables or in a number of
+ processor registers. The following define maps a variable name x and
+ a column number c to the way the state array variable is to be held.
+ The first define below maps the state into an array x[c] whereas the
+ second form maps the state into a number of individual variables x0,
+ x1, etc. Another form could map individual state colums to machine
+ register names.
+*/
+
+#if defined(ARRAYS)
+#define s(x,c) x[c]
+#else
+#define s(x,c) x##c
+#endif
+
+/* 8. FIXED OR DYNAMIC TABLES
+
+ When this section is included the tables used by the code are compiled
+ statically into the binary file. Otherwise the subroutine gen_tabs()
+ must be called to compute them before the code is first used.
+*/
+#if 1
+#define FIXED_TABLES
+#endif
+
+/* 9. TABLE ALIGNMENT
+
+ On some sytsems speed will be improved by aligning the AES large lookup
+ tables on particular boundaries. This define should be set to a power of
+ two giving the desired alignment. It can be left undefined if alignment
+ is not needed. This option is specific to the Microsft VC++ compiler -
+ it seems to sometimes cause trouble for the VC++ version 6 compiler.
+*/
+
+#if 0 && defined(_MSC_VER) && (_MSC_VER >= 1300)
+#define TABLE_ALIGN 64
+#endif
+
+/* 10. INTERNAL TABLE CONFIGURATION
+
+ This cipher proceeds by repeating in a number of cycles known as 'rounds'
+ which are implemented by a round function which can optionally be speeded
+ up using tables. The basic tables are each 256 32-bit words, with either
+ one or four tables being required for each round function depending on
+ how much speed is required. The encryption and decryption round functions
+ are different and the last encryption and decrytpion round functions are
+ different again making four different round functions in all.
+
+ This means that:
+ 1. Normal encryption and decryption rounds can each use either 0, 1
+ or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
+ 2. The last encryption and decryption rounds can also use either 0, 1
+ or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
+
+ Include or exclude the appropriate definitions below to set the number
+ of tables used by this implementation.
+*/
+
+#if 1 /* set tables for the normal encryption round */
+#define ENC_ROUND FOUR_TABLES
+#elif 0
+#define ENC_ROUND ONE_TABLE
+#else
+#define ENC_ROUND NO_TABLES
+#endif
+
+#if 1 /* set tables for the last encryption round */
+#define LAST_ENC_ROUND FOUR_TABLES
+#elif 0
+#define LAST_ENC_ROUND ONE_TABLE
+#else
+#define LAST_ENC_ROUND NO_TABLES
+#endif
+
+#if 1 /* set tables for the normal decryption round */
+#define DEC_ROUND FOUR_TABLES
+#elif 0
+#define DEC_ROUND ONE_TABLE
+#else
+#define DEC_ROUND NO_TABLES
+#endif
+
+#if 1 /* set tables for the last decryption round */
+#define LAST_DEC_ROUND FOUR_TABLES
+#elif 0
+#define LAST_DEC_ROUND ONE_TABLE
+#else
+#define LAST_DEC_ROUND NO_TABLES
+#endif
+
+/* The decryption key schedule can be speeded up with tables in the same
+ way that the round functions can. Include or exclude the following
+ defines to set this requirement.
+*/
+#if 1
+#define KEY_SCHED FOUR_TABLES
+#elif 0
+#define KEY_SCHED ONE_TABLE
+#else
+#define KEY_SCHED NO_TABLES
+#endif
+
+/* END OF CONFIGURATION OPTIONS */
+
+#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2))
+
+/* Disable or report errors on some combinations of options */
+
+#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES
+#undef LAST_ENC_ROUND
+#define LAST_ENC_ROUND NO_TABLES
+#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES
+#undef LAST_ENC_ROUND
+#define LAST_ENC_ROUND ONE_TABLE
+#endif
+
+#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE
+#undef ENC_UNROLL
+#define ENC_UNROLL NONE
+#endif
+
+#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES
+#undef LAST_DEC_ROUND
+#define LAST_DEC_ROUND NO_TABLES
+#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES
+#undef LAST_DEC_ROUND
+#define LAST_DEC_ROUND ONE_TABLE
+#endif
+
+#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE
+#undef DEC_UNROLL
+#define DEC_UNROLL NONE
+#endif
+
+/* upr(x,n): rotates bytes within words by n positions, moving bytes to
+ higher index positions with wrap around into low positions
+ ups(x,n): moves bytes by n positions to higher index positions in
+ words but without wrap around
+ bval(x,n): extracts a byte from a word
+
+ NOTE: The definitions given here are intended only for use with
+ unsigned variables and with shift counts that are compile
+ time constants
+*/
+
+#if (ALGORITHM_BYTE_ORDER == BRG_LITTLE_ENDIAN)
+#define upr(x,n) (((aes_32t)(x) << (8 * (n))) | ((aes_32t)(x) >> (32 - 8 * (n))))
+#define ups(x,n) ((aes_32t) (x) << (8 * (n)))
+#define bval(x,n) ((aes_08t)((x) >> (8 * (n))))
+#define bytes2word(b0, b1, b2, b3) \
+ (((aes_32t)(b3) << 24) | ((aes_32t)(b2) << 16) | ((aes_32t)(b1) << 8) | (b0))
+#endif
+
+#if (ALGORITHM_BYTE_ORDER == BRG_BIG_ENDIAN)
+#define upr(x,n) (((aes_32t)(x) >> (8 * (n))) | ((aes_32t)(x) << (32 - 8 * (n))))
+#define ups(x,n) ((aes_32t) (x) >> (8 * (n))))
+#define bval(x,n) ((aes_08t)((x) >> (24 - 8 * (n))))
+#define bytes2word(b0, b1, b2, b3) \
+ (((aes_32t)(b0) << 24) | ((aes_32t)(b1) << 16) | ((aes_32t)(b2) << 8) | (b3))
+#endif
+
+#if defined(SAFE_IO)
+
+#define word_in(x,c) bytes2word(((aes_08t*)(x)+4*c)[0], ((aes_08t*)(x)+4*c)[1], \
+ ((aes_08t*)(x)+4*c)[2], ((aes_08t*)(x)+4*c)[3])
+#define word_out(x,c,v) { ((aes_08t*)(x)+4*c)[0] = bval(v,0); ((aes_08t*)(x)+4*c)[1] = bval(v,1); \
+ ((aes_08t*)(x)+4*c)[2] = bval(v,2); ((aes_08t*)(x)+4*c)[3] = bval(v,3); }
+
+#elif (ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER)
+
+#define word_in(x,c) (*((aes_32t*)(x)+(c)))
+#define word_out(x,c,v) (*((aes_32t*)(x)+(c)) = (v))
+
+#else
+
+#define word_in(x,c) aes_sw32(*((aes_32t*)(x)+(c)))
+#define word_out(x,c,v) (*((aes_32t*)(x)+(c)) = aes_sw32(v))
+
+#endif
+
+/* the finite field modular polynomial and elements */
+
+#define WPOLY 0x011b
+#define BPOLY 0x1b
+
+/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+#define m1 0x80808080
+#define m2 0x7f7f7f7f
+#define gf_mulx(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * BPOLY))
+
+/* The following defines provide alternative definitions of gf_mulx that might
+ give improved performance if a fast 32-bit multiply is not available. Note
+ that a temporary variable u needs to be defined where gf_mulx is used.
+
+#define gf_mulx(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6))
+#define m4 (0x01010101 * BPOLY)
+#define gf_mulx(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4)
+*/
+
+/* Work out which tables are needed for the different options */
+
+#ifdef AES_ASM
+#ifdef ENC_ROUND
+#undef ENC_ROUND
+#endif
+#define ENC_ROUND FOUR_TABLES
+#ifdef LAST_ENC_ROUND
+#undef LAST_ENC_ROUND
+#endif
+#define LAST_ENC_ROUND FOUR_TABLES
+#ifdef DEC_ROUND
+#undef DEC_ROUND
+#endif
+#define DEC_ROUND FOUR_TABLES
+#ifdef LAST_DEC_ROUND
+#undef LAST_DEC_ROUND
+#endif
+#define LAST_DEC_ROUND FOUR_TABLES
+#ifdef KEY_SCHED
+#undef KEY_SCHED
+#define KEY_SCHED FOUR_TABLES
+#endif
+#endif
+
+#if defined(ENCRYPTION) || defined(AES_ASM)
+#if ENC_ROUND == ONE_TABLE
+#define FT1_SET
+#elif ENC_ROUND == FOUR_TABLES
+#define FT4_SET
+#else
+#define SBX_SET
+#endif
+#if LAST_ENC_ROUND == ONE_TABLE
+#define FL1_SET
+#elif LAST_ENC_ROUND == FOUR_TABLES
+#define FL4_SET
+#elif !defined(SBX_SET)
+#define SBX_SET
+#endif
+#endif
+
+#if defined(DECRYPTION) || defined(AES_ASM)
+#if DEC_ROUND == ONE_TABLE
+#define IT1_SET
+#elif DEC_ROUND == FOUR_TABLES
+#define IT4_SET
+#else
+#define ISB_SET
+#endif
+#if LAST_DEC_ROUND == ONE_TABLE
+#define IL1_SET
+#elif LAST_DEC_ROUND == FOUR_TABLES
+#define IL4_SET
+#elif !defined(ISB_SET)
+#define ISB_SET
+#endif
+#endif
+
+#if defined(ENCRYPTION_KEY_SCHEDULE) || defined(DECRYPTION_KEY_SCHEDULE)
+#if KEY_SCHED == ONE_TABLE
+#define LS1_SET
+#define IM1_SET
+#elif KEY_SCHED == FOUR_TABLES
+#define LS4_SET
+#define IM4_SET
+#elif !defined(SBX_SET)
+#define SBX_SET
+#endif
+#endif
+
+/* generic definitions of Rijndael macros that use tables */
+
+#define no_table(x,box,vf,rf,c) bytes2word( \
+ box[bval(vf(x,0,c),rf(0,c))], \
+ box[bval(vf(x,1,c),rf(1,c))], \
+ box[bval(vf(x,2,c),rf(2,c))], \
+ box[bval(vf(x,3,c),rf(3,c))])
+
+#define one_table(x,op,tab,vf,rf,c) \
+ ( tab[bval(vf(x,0,c),rf(0,c))] \
+ ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \
+ ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \
+ ^ op(tab[bval(vf(x,3,c),rf(3,c))],3))
+
+#define four_tables(x,tab,vf,rf,c) \
+ ( tab[0][bval(vf(x,0,c),rf(0,c))] \
+ ^ tab[1][bval(vf(x,1,c),rf(1,c))] \
+ ^ tab[2][bval(vf(x,2,c),rf(2,c))] \
+ ^ tab[3][bval(vf(x,3,c),rf(3,c))])
+
+#define vf1(x,r,c) (x)
+#define rf1(r,c) (r)
+#define rf2(r,c) ((8+r-c)&3)
+
+/* perform forward and inverse column mix operation on four bytes in long word x in */
+/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros. */
+
+#if defined(FM4_SET) /* not currently used */
+#define fwd_mcol(x) four_tables(x,t_use(f,m),vf1,rf1,0)
+#elif defined(FM1_SET) /* not currently used */
+#define fwd_mcol(x) one_table(x,upr,t_use(f,m),vf1,rf1,0)
+#else
+#define dec_fmvars aes_32t g2
+#define fwd_mcol(x) (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1))
+#endif
+
+#if defined(IM4_SET)
+#define inv_mcol(x) four_tables(x,t_use(i,m),vf1,rf1,0)
+#elif defined(IM1_SET)
+#define inv_mcol(x) one_table(x,upr,t_use(i,m),vf1,rf1,0)
+#else
+#define dec_imvars aes_32t g2, g4, g9
+#define inv_mcol(x) (g2 = gf_mulx(x), g4 = gf_mulx(g2), g9 = (x) ^ gf_mulx(g4), g4 ^= g9, \
+ (x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1))
+#endif
+
+#if defined(FL4_SET)
+#define ls_box(x,c) four_tables(x,t_use(f,l),vf1,rf2,c)
+#elif defined(LS4_SET)
+#define ls_box(x,c) four_tables(x,t_use(l,s),vf1,rf2,c)
+#elif defined(FL1_SET)
+#define ls_box(x,c) one_table(x,upr,t_use(f,l),vf1,rf2,c)
+#elif defined(LS1_SET)
+#define ls_box(x,c) one_table(x,upr,t_use(l,s),vf1,rf2,c)
+#else
+#define ls_box(x,c) no_table(x,t_use(s,box),vf1,rf2,c)
+#endif
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* If there are no global variables, the definitions here can be
+ used to put the AES tables in a structure so that a pointer
+ can then be added to the AES context to pass them to the AES
+ routines that need them. If this facility is used, the calling
+ program has to ensure that this pointer is managed appropriately.
+ In particular, the value of the t_dec(in,it) item in the table
+ structure must be set to zero in order to ensure that the tables
+ are initialised. In practice the three code sequences in aeskey.c
+ that control the calls to gen_tabs() and the gen_tabs() routine
+ itself will have to be changed for a specific implementation. If
+ global variables are available it will generally be preferable to
+ use them with the precomputed FIXED_TABLES option that uses static
+ global tables.
+
+ The following defines can be used to control the way the tables
+ are defined, initialised and used in embedded environments that
+ require special features for these purposes
+
+ the 't_dec' construction is used to declare fixed table arrays
+ the 't_set' construction is used to set fixed table values
+ the 't_use' construction is used to access fixed table values
+
+ 256 byte tables:
+
+ t_xxx(s,box) => forward S box
+ t_xxx(i,box) => inverse S box
+
+ 256 32-bit word OR 4 x 256 32-bit word tables:
+
+ t_xxx(f,n) => forward normal round
+ t_xxx(f,l) => forward last round
+ t_xxx(i,n) => inverse normal round
+ t_xxx(i,l) => inverse last round
+ t_xxx(l,s) => key schedule table
+ t_xxx(i,m) => key schedule table
+
+ Other variables and tables:
+
+ t_xxx(r,c) => the rcon table
+*/
+
+#define t_dec(m,n) t_##m##n
+#define t_set(m,n) t_##m##n
+#define t_use(m,n) t_##m##n
+
+#if defined(DO_TABLES) /* declare and instantiate tables */
+
+/* finite field arithmetic operations for table generation */
+
+#if defined(FIXED_TABLES) || !defined(FF_TABLES)
+
+#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY))
+#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY))
+#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) \
+ ^ (((x>>5) & 4) * WPOLY))
+#define f3(x) (f2(x) ^ x)
+#define f9(x) (f8(x) ^ x)
+#define fb(x) (f8(x) ^ f2(x) ^ x)
+#define fd(x) (f8(x) ^ f4(x) ^ x)
+#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
+
+#else
+
+#define f2(x) ((x) ? pow[log[x] + 0x19] : 0)
+#define f3(x) ((x) ? pow[log[x] + 0x01] : 0)
+#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0)
+#define fb(x) ((x) ? pow[log[x] + 0x68] : 0)
+#define fd(x) ((x) ? pow[log[x] + 0xee] : 0)
+#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0)
+#define fi(x) ((x) ? pow[ 255 - log[x]] : 0)
+
+#endif
+
+#if defined(FIXED_TABLES) /* declare and set values for static tables */
+
+#define sb_data(w) \
+ w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\
+ w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\
+ w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\
+ w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\
+ w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\
+ w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\
+ w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\
+ w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\
+ w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\
+ w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\
+ w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\
+ w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\
+ w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\
+ w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\
+ w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\
+ w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\
+ w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\
+ w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\
+ w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\
+ w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\
+ w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\
+ w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\
+ w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\
+ w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\
+ w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\
+ w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\
+ w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\
+ w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\
+ w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\
+ w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\
+ w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\
+ w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16)
+
+#define isb_data(w) \
+ w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\
+ w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\
+ w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\
+ w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\
+ w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\
+ w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\
+ w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\
+ w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\
+ w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\
+ w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\
+ w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\
+ w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\
+ w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\
+ w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\
+ w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\
+ w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\
+ w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\
+ w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\
+ w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\
+ w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\
+ w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\
+ w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\
+ w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\
+ w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\
+ w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\
+ w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\
+ w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\
+ w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\
+ w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\
+ w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\
+ w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\
+ w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d),
+
+#define mm_data(w) \
+ w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\
+ w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\
+ w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\
+ w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\
+ w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\
+ w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\
+ w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\
+ w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\
+ w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\
+ w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\
+ w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\
+ w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\
+ w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\
+ w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\
+ w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\
+ w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\
+ w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\
+ w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\
+ w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\
+ w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\
+ w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\
+ w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\
+ w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\
+ w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\
+ w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\
+ w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\
+ w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\
+ w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\
+ w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\
+ w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\
+ w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\
+ w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff)
+
+#define h0(x) (x)
+
+/* These defines are used to ensure tables are generated in the
+ right format depending on the internal byte order required
+*/
+
+#define w0(p) bytes2word(p, 0, 0, 0)
+#define w1(p) bytes2word(0, p, 0, 0)
+#define w2(p) bytes2word(0, 0, p, 0)
+#define w3(p) bytes2word(0, 0, 0, p)
+
+#define u0(p) bytes2word(f2(p), p, p, f3(p))
+#define u1(p) bytes2word(f3(p), f2(p), p, p)
+#define u2(p) bytes2word(p, f3(p), f2(p), p)
+#define u3(p) bytes2word(p, p, f3(p), f2(p))
+
+#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p))
+#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p))
+#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p))
+#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p))
+
+const aes_32t t_dec(r,c)[RC_LENGTH] =
+{
+ w0(0x01), w0(0x02), w0(0x04), w0(0x08), w0(0x10),
+ w0(0x20), w0(0x40), w0(0x80), w0(0x1b), w0(0x36)
+};
+
+#define d_1(t,n,b,v) const t n[256] = { b(v##0) }
+#define d_4(t,n,b,v) const t n[4][256] = { { b(v##0) }, { b(v##1) }, { b(v##2) }, { b(v##3) } }
+
+#else /* declare and instantiate tables for dynamic value generation in in tab.c */
+
+aes_32t t_dec(r,c)[RC_LENGTH];
+
+#define d_1(t,n,b,v) t n[256]
+#define d_4(t,n,b,v) t n[4][256]
+
+#endif
+
+#else /* declare tables without instantiation */
+
+#if defined(FIXED_TABLES)
+
+extern const aes_32t t_dec(r,c)[RC_LENGTH];
+
+#if defined(_MSC_VER) && defined(TABLE_ALIGN)
+#define d_1(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) const t n[256]
+#define d_4(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) const t n[4][256]
+#else
+#define d_1(t,n,b,v) extern const t n[256]
+#define d_4(t,n,b,v) extern const t n[4][256]
+#endif
+#else
+
+extern aes_32t t_dec(r,c)[RC_LENGTH];
+
+#if defined(_MSC_VER) && defined(TABLE_ALIGN)
+#define d_1(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) t n[256]
+#define d_4(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) t n[4][256]
+#else
+#define d_1(t,n,b,v) extern t n[256]
+#define d_4(t,n,b,v) extern t n[4][256]
+#endif
+#endif
+
+#endif
+
+#ifdef SBX_SET
+ d_1(aes_08t, t_dec(s,box), sb_data, h);
+#endif
+#ifdef ISB_SET
+ d_1(aes_08t, t_dec(i,box), isb_data, h);
+#endif
+
+#ifdef FT1_SET
+ d_1(aes_32t, t_dec(f,n), sb_data, u);
+#endif
+#ifdef FT4_SET
+ d_4(aes_32t, t_dec(f,n), sb_data, u);
+#endif
+
+#ifdef FL1_SET
+ d_1(aes_32t, t_dec(f,l), sb_data, w);
+#endif
+#ifdef FL4_SET
+ d_4(aes_32t, t_dec(f,l), sb_data, w);
+#endif
+
+#ifdef IT1_SET
+ d_1(aes_32t, t_dec(i,n), isb_data, v);
+#endif
+#ifdef IT4_SET
+ d_4(aes_32t, t_dec(i,n), isb_data, v);
+#endif
+
+#ifdef IL1_SET
+ d_1(aes_32t, t_dec(i,l), isb_data, w);
+#endif
+#ifdef IL4_SET
+ d_4(aes_32t, t_dec(i,l), isb_data, w);
+#endif
+
+#ifdef LS1_SET
+#ifdef FL1_SET
+#undef LS1_SET
+#else
+ d_1(aes_32t, t_dec(l,s), sb_data, w);
+#endif
+#endif
+
+#ifdef LS4_SET
+#ifdef FL4_SET
+#undef LS4_SET
+#else
+ d_4(aes_32t, t_dec(l,s), sb_data, w);
+#endif
+#endif
+
+#ifdef IM1_SET
+ d_1(aes_32t, t_dec(i,m), mm_data, v);
+#endif
+#ifdef IM4_SET
+ d_4(aes_32t, t_dec(i,m), mm_data, v);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/trunk/main/aestab.c b/trunk/main/aestab.c
new file mode 100644
index 000000000..b134d22d8
--- /dev/null
+++ b/trunk/main/aestab.c
@@ -0,0 +1,236 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+*/
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#ifndef HAVE_CRYPTO
+
+#define DO_TABLES
+
+#include "aesopt.h"
+
+#if defined(FIXED_TABLES)
+
+/* implemented in case of wrong call for fixed tables */
+
+void gen_tabs(void)
+{
+}
+
+#else /* dynamic table generation */
+
+#if !defined(FF_TABLES)
+
+/* Generate the tables for the dynamic table option
+
+ It will generally be sensible to use tables to compute finite
+ field multiplies and inverses but where memory is scarse this
+ code might sometimes be better. But it only has effect during
+ initialisation so its pretty unimportant in overall terms.
+*/
+
+/* return 2 ^ (n - 1) where n is the bit number of the highest bit
+ set in x with x in the range 1 < x < 0x00000200. This form is
+ used so that locals within fi can be bytes rather than words
+*/
+
+static aes_08t hibit(const aes_32t x)
+{ aes_08t r = (aes_08t)((x >> 1) | (x >> 2));
+
+ r |= (r >> 2);
+ r |= (r >> 4);
+ return (r + 1) >> 1;
+}
+
+/* return the inverse of the finite field element x */
+
+static aes_08t fi(const aes_08t x)
+{ aes_08t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
+
+ if(x < 2) return x;
+
+ for(;;)
+ {
+ if(!n1) return v1;
+
+ while(n2 >= n1)
+ {
+ n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2);
+ }
+
+ if(!n2) return v2;
+
+ while(n1 >= n2)
+ {
+ n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1);
+ }
+ }
+}
+
+#endif
+
+/* The forward and inverse affine transformations used in the S-box */
+
+#define fwd_affine(x) \
+ (w = (aes_32t)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(aes_08t)(w^(w>>8)))
+
+#define inv_affine(x) \
+ (w = (aes_32t)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(aes_08t)(w^(w>>8)))
+
+static int init = 0;
+
+void gen_tabs(void)
+{ aes_32t i, w;
+
+#if defined(FF_TABLES)
+
+ aes_08t pow[512], log[256];
+
+ if(init) return;
+ /* log and power tables for GF(2^8) finite field with
+ WPOLY as modular polynomial - the simplest primitive
+ root is 0x03, used here to generate the tables
+ */
+
+ i = 0; w = 1;
+ do
+ {
+ pow[i] = (aes_08t)w;
+ pow[i + 255] = (aes_08t)w;
+ log[w] = (aes_08t)i++;
+ w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0);
+ }
+ while (w != 1);
+
+#else
+ if(init) return;
+#endif
+
+ for(i = 0, w = 1; i < RC_LENGTH; ++i)
+ {
+ t_set(r,c)[i] = bytes2word(w, 0, 0, 0);
+ w = f2(w);
+ }
+
+ for(i = 0; i < 256; ++i)
+ { aes_08t b;
+
+ b = fwd_affine(fi((aes_08t)i));
+ w = bytes2word(f2(b), b, b, f3(b));
+
+#ifdef SBX_SET
+ t_set(s,box)[i] = b;
+#endif
+
+#ifdef FT1_SET /* tables for a normal encryption round */
+ t_set(f,n)[i] = w;
+#endif
+#ifdef FT4_SET
+ t_set(f,n)[0][i] = w;
+ t_set(f,n)[1][i] = upr(w,1);
+ t_set(f,n)[2][i] = upr(w,2);
+ t_set(f,n)[3][i] = upr(w,3);
+#endif
+ w = bytes2word(b, 0, 0, 0);
+
+#ifdef FL1_SET /* tables for last encryption round (may also */
+ t_set(f,l)[i] = w; /* be used in the key schedule) */
+#endif
+#ifdef FL4_SET
+ t_set(f,l)[0][i] = w;
+ t_set(f,l)[1][i] = upr(w,1);
+ t_set(f,l)[2][i] = upr(w,2);
+ t_set(f,l)[3][i] = upr(w,3);
+#endif
+
+#ifdef LS1_SET /* table for key schedule if t_set(f,l) above is */
+ t_set(l,s)[i] = w; /* not of the required form */
+#endif
+#ifdef LS4_SET
+ t_set(l,s)[0][i] = w;
+ t_set(l,s)[1][i] = upr(w,1);
+ t_set(l,s)[2][i] = upr(w,2);
+ t_set(l,s)[3][i] = upr(w,3);
+#endif
+
+ b = fi(inv_affine((aes_08t)i));
+ w = bytes2word(fe(b), f9(b), fd(b), fb(b));
+
+#ifdef IM1_SET /* tables for the inverse mix column operation */
+ t_set(i,m)[b] = w;
+#endif
+#ifdef IM4_SET
+ t_set(i,m)[0][b] = w;
+ t_set(i,m)[1][b] = upr(w,1);
+ t_set(i,m)[2][b] = upr(w,2);
+ t_set(i,m)[3][b] = upr(w,3);
+#endif
+
+#ifdef ISB_SET
+ t_set(i,box)[i] = b;
+#endif
+#ifdef IT1_SET /* tables for a normal decryption round */
+ t_set(i,n)[i] = w;
+#endif
+#ifdef IT4_SET
+ t_set(i,n)[0][i] = w;
+ t_set(i,n)[1][i] = upr(w,1);
+ t_set(i,n)[2][i] = upr(w,2);
+ t_set(i,n)[3][i] = upr(w,3);
+#endif
+ w = bytes2word(b, 0, 0, 0);
+#ifdef IL1_SET /* tables for last decryption round */
+ t_set(i,l)[i] = w;
+#endif
+#ifdef IL4_SET
+ t_set(i,l)[0][i] = w;
+ t_set(i,l)[1][i] = upr(w,1);
+ t_set(i,l)[2][i] = upr(w,2);
+ t_set(i,l)[3][i] = upr(w,3);
+#endif
+ }
+ init = 1;
+}
+
+#endif
+
+#endif /* !HAVE_CRYPTO */
+
+#if defined(__cplusplus)
+}
+#endif
+
diff --git a/trunk/main/alaw.c b/trunk/main/alaw.c
new file mode 100644
index 000000000..8d3595206
--- /dev/null
+++ b/trunk/main/alaw.c
@@ -0,0 +1,210 @@
+/*
+ * 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 a-Law to Signed linear conversion
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/alaw.h"
+
+#ifndef G711_NEW_ALGORITHM
+#define AMI_MASK 0x55
+
+static inline unsigned char linear2alaw(short int linear)
+{
+ int mask;
+ int seg;
+ int pcm_val;
+ static int seg_end[8] =
+ {
+ 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+ };
+
+ pcm_val = linear;
+ if (pcm_val >= 0) {
+ /* Sign (7th) bit = 1 */
+ mask = AMI_MASK | 0x80;
+ } else {
+ /* Sign bit = 0 */
+ mask = AMI_MASK;
+ pcm_val = -pcm_val;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ for (seg = 0; seg < 8; seg++) {
+ if (pcm_val <= seg_end[seg])
+ break;
+ }
+ /* Combine the sign, segment, and quantization bits. */
+ return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
+}
+#else
+static unsigned char linear2alaw(short sample, int full_coding)
+{
+ static const unsigned exp_lut[128] = {
+ 1,1,2,2,3,3,3,3,
+ 4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7 };
+ unsigned sign, exponent, mantissa, mag;
+ unsigned char alawbyte;
+
+ ast_alaw_get_sign_mag(sample, &sign, &mag);
+ if (mag > 32767)
+ mag = 32767; /* clip the magnitude for -32768 */
+
+ exponent = exp_lut[(mag >> 8) & 0x7f];
+ mantissa = (mag >> (exponent + 3)) & 0x0f;
+ if (mag < 0x100)
+ exponent = 0;
+
+ if (full_coding) {
+ /* full encoding, with sign and xform */
+ alawbyte = (unsigned char)(sign | (exponent << 4) | mantissa);
+ alawbyte ^= AST_ALAW_AMI_MASK;
+ } else {
+ /* half-cooked coding -- mantissa+exponent only (for lookup tab) */
+ alawbyte = (exponent << 4) | mantissa;
+ }
+ return alawbyte;
+}
+#endif
+
+#ifndef G711_NEW_ALGORITHM
+static inline short int alaw2linear (unsigned char alaw)
+{
+ int i;
+ int seg;
+
+ alaw ^= AMI_MASK;
+ i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
+ seg = (((int) alaw & 0x70) >> 4);
+ if (seg)
+ i = (i + 0x100) << (seg - 1);
+ return (short int) ((alaw & 0x80) ? i : -i);
+}
+#else
+static inline short alaw2linear(unsigned char alawbyte)
+{
+ unsigned exponent, mantissa;
+ short sample;
+
+ alawbyte ^= AST_ALAW_AMI_MASK;
+ exponent = (alawbyte & 0x70) >> 4;
+ mantissa = alawbyte & 0x0f;
+ sample = (mantissa << 4) + 8 /* rounding error */;
+ if (exponent)
+ sample = (sample + 0x100) << (exponent - 1);
+ if (!(alawbyte & 0x80))
+ sample = -sample;
+ return sample;
+}
+#endif
+
+
+
+#ifndef G711_NEW_ALGORITHM
+unsigned char __ast_lin2a[8192];
+#else
+unsigned char __ast_lin2a[AST_ALAW_TAB_SIZE];
+#endif
+short __ast_alaw[256];
+
+void ast_alaw_init(void)
+{
+ int i;
+ /*
+ * Set up mu-law conversion table
+ */
+#ifndef G711_NEW_ALGORITHM
+ for (i = 0; i < 256; i++) {
+ __ast_alaw[i] = alaw2linear(i);
+ }
+ /* set up the reverse (mu-law) conversion table */
+ for (i = -32768; i < 32768; i++) {
+ __ast_lin2a[((unsigned short)i) >> 3] = linear2alaw(i);
+ }
+#else
+ for (i = 0; i < 256; i++) {
+ __ast_alaw[i] = alaw2linear(i);
+ }
+ /* set up the reverse (a-law) conversion table */
+ for (i = 0; i <= 32768; i += AST_ALAW_STEP) {
+ AST_LIN2A_LOOKUP(i) = linear2alaw(i, 0 /* half-cooked */);
+ }
+#endif
+
+#ifdef TEST_CODING_TABLES
+ for (i = -32768; i < 32768; ++i) {
+#ifndef G711_NEW_ALGORITHM
+ unsigned char e1 = linear2alaw(i);
+#else
+ unsigned char e1 = linear2alaw(i, 1);
+#endif
+ short d1 = alaw2linear(e1);
+ unsigned char e2 = AST_LIN2A(i);
+ short d2 = alaw2linear(e2);
+ short d3 = AST_ALAW(e1);
+
+ if (e1 != e2 || d1 != d3 || d2 != d3) {
+ ast_log(LOG_WARNING, "a-Law coding tables test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2);
+ }
+ }
+ ast_log(LOG_NOTICE, "a-Law coding tables test complete.\n");
+#endif /* TEST_CODING_TABLES */
+
+#ifdef TEST_TANDEM_TRANSCODING
+ /* tandem transcoding test */
+ for (i = -32768; i < 32768; ++i) {
+ unsigned char e1 = AST_LIN2A(i);
+ short d1 = AST_ALAW(e1);
+ unsigned char e2 = AST_LIN2A(d1);
+ short d2 = AST_ALAW(e2);
+ unsigned char e3 = AST_LIN2A(d2);
+ short d3 = AST_ALAW(e3);
+
+ if (e1 != e2 || e2 != e3 || d1 != d2 || d2 != d3) {
+ ast_log(LOG_WARNING, "a-Law tandem transcoding test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d, d3=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2, (int)d3);
+ }
+ }
+ ast_log(LOG_NOTICE, "a-Law tandem transcoding test complete.\n");
+#endif /* TEST_TANDEM_TRANSCODING */
+
+}
+
diff --git a/trunk/main/app.c b/trunk/main/app.c
new file mode 100644
index 000000000..696b22c6f
--- /dev/null
+++ b/trunk/main/app.c
@@ -0,0 +1,1739 @@
+/*
+ * 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 Convenient Application Routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <regex.h>
+#include <sys/file.h> /* added this to allow to compile, sorry! */
+
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/indications.h"
+#include "asterisk/linkedlists.h"
+
+#define MAX_OTHER_FORMATS 10
+
+static AST_RWLIST_HEAD_STATIC(groups, ast_group_info);
+
+/*!
+ * \brief This function presents a dialtone and reads an extension into 'collect'
+ * which must be a pointer to a **pre-initialized** array of char having a
+ * size of 'size' suitable for writing to. It will collect no more than the smaller
+ * of 'maxlen' or 'size' minus the original strlen() of collect digits.
+ * \param chan struct.
+ * \param context
+ * \param collect
+ * \param size
+ * \param maxlen
+ * \param timeout timeout in seconds
+ *
+ * \return 0 if extension does not exist, 1 if extension exists
+*/
+int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout)
+{
+ struct ind_tone_zone_sound *ts;
+ int res = 0, x = 0;
+
+ if (maxlen > size)
+ maxlen = size;
+
+ if (!timeout && chan->pbx)
+ timeout = chan->pbx->dtimeout;
+ else if (!timeout)
+ timeout = 5;
+
+ if ((ts = ast_get_indication_tone(chan->zone, "dial")) && ts->data[0])
+ res = ast_playtones_start(chan, 0, ts->data, 0);
+ else
+ ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
+
+ for (x = strlen(collect); x < maxlen; ) {
+ res = ast_waitfordigit(chan, timeout);
+ if (!ast_ignore_pattern(context, collect))
+ ast_playtones_stop(chan);
+ if (res < 1)
+ break;
+ if (res == '#')
+ break;
+ collect[x++] = res;
+ if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num))
+ break;
+ }
+
+ if (res >= 0)
+ res = ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num) ? 1 : 0;
+
+ return res;
+}
+
+/*!
+ * \brief ast_app_getdata
+ * \param c The channel to read from
+ * \param prompt The file to stream to the channel
+ * \param s The string to read in to. Must be at least the size of your length
+ * \param maxlen How many digits to read (maximum)
+ * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
+ * "ludicrous time" (essentially never times out) */
+int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
+{
+ int res = 0, to, fto;
+ char *front, *filename;
+
+ /* XXX Merge with full version? XXX */
+
+ if (maxlen)
+ s[0] = '\0';
+
+ if (!prompt)
+ prompt="";
+
+ filename = ast_strdupa(prompt);
+ while ((front = strsep(&filename, "&"))) {
+ if (!ast_strlen_zero(front)) {
+ res = ast_streamfile(c, front, c->language);
+ if (res)
+ continue;
+ }
+ if (ast_strlen_zero(filename)) {
+ /* set timeouts for the last prompt */
+ fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
+ to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+
+ if (timeout > 0)
+ fto = to = timeout;
+ if (timeout < 0)
+ fto = to = 1000000000;
+ } else {
+ /* there is more than one prompt, so
+ get rid of the long timeout between
+ prompts, and make it 50ms */
+ fto = 50;
+ to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+ }
+ res = ast_readstring(c, s, maxlen, to, fto, "#");
+ if (!ast_strlen_zero(s))
+ return res;
+ }
+
+ return res;
+}
+
+/* The lock type used by ast_lock_path() / ast_unlock_path() */
+static enum AST_LOCK_TYPE ast_lock_type = AST_LOCK_TYPE_LOCKFILE;
+
+int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
+{
+ int res, to = 2000, fto = 6000;
+
+ if (!ast_strlen_zero(prompt)) {
+ res = ast_streamfile(c, prompt, c->language);
+ if (res < 0)
+ return res;
+ }
+
+ if (timeout > 0)
+ fto = to = timeout;
+ if (timeout < 0)
+ fto = to = 1000000000;
+
+ res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
+
+ return res;
+}
+
+static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
+static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
+static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
+
+void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
+ int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
+ int (*messagecount_func)(const char *context, const char *mailbox, const char *folder))
+{
+ ast_has_voicemail_func = has_voicemail_func;
+ ast_inboxcount_func = inboxcount_func;
+ ast_messagecount_func = messagecount_func;
+}
+
+void ast_uninstall_vm_functions(void)
+{
+ ast_has_voicemail_func = NULL;
+ ast_inboxcount_func = NULL;
+ ast_messagecount_func = NULL;
+}
+
+int ast_app_has_voicemail(const char *mailbox, const char *folder)
+{
+ static int warned = 0;
+ if (ast_has_voicemail_func)
+ return ast_has_voicemail_func(mailbox, folder);
+
+ if (!warned) {
+ ast_verb(3, "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX");
+ warned++;
+ }
+ return 0;
+}
+
+
+int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
+{
+ static int warned = 0;
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+ if (ast_inboxcount_func)
+ return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
+
+ if (!warned) {
+ warned++;
+ ast_verb(3, "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
+ }
+
+ return 0;
+}
+
+int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
+{
+ static int warned = 0;
+ if (ast_messagecount_func)
+ return ast_messagecount_func(context, mailbox, folder);
+
+ if (!warned) {
+ warned++;
+ ast_verb(3, "Message count requested for mailbox %s@%s/%s but voicemail not loaded.\n", mailbox, context, folder);
+ }
+
+ return 0;
+}
+
+int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
+{
+ const char *ptr;
+ int res = 0;
+
+ if (!between)
+ between = 100;
+
+ if (peer)
+ res = ast_autoservice_start(peer);
+
+ if (!res)
+ res = ast_waitfor(chan, 100);
+
+ /* ast_waitfor will return the number of remaining ms on success */
+ if (res < 0)
+ return res;
+
+ for (ptr = digits; *ptr; ptr++) {
+ if (*ptr == 'w') {
+ /* 'w' -- wait half a second */
+ if ((res = ast_safe_sleep(chan, 500)))
+ break;
+ } else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
+ /* Character represents valid DTMF */
+ if (*ptr == 'f' || *ptr == 'F') {
+ /* ignore return values if not supported by channel */
+ ast_indicate(chan, AST_CONTROL_FLASH);
+ } else
+ ast_senddigit(chan, *ptr, duration);
+ /* pause between digits */
+ if ((res = ast_safe_sleep(chan, between)))
+ break;
+ } else
+ ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
+ }
+
+ if (peer) {
+ /* Stop autoservice on the peer channel, but don't overwrite any error condition
+ that has occurred previously while acting on the primary channel */
+ if (ast_autoservice_stop(peer) && !res)
+ res = -1;
+ }
+
+ return res;
+}
+
+struct linear_state {
+ int fd;
+ int autoclose;
+ int allowoverride;
+ int origwfmt;
+};
+
+static void linear_release(struct ast_channel *chan, void *params)
+{
+ struct linear_state *ls = params;
+
+ if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt))
+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
+
+ if (ls->autoclose)
+ close(ls->fd);
+
+ ast_free(params);
+}
+
+static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ short buf[2048 + AST_FRIENDLY_OFFSET / 2];
+ struct linear_state *ls = data;
+ struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = buf + AST_FRIENDLY_OFFSET / 2,
+ .offset = AST_FRIENDLY_OFFSET,
+ };
+ int res;
+
+ len = samples * 2;
+ if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
+ ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
+ len = sizeof(buf) - AST_FRIENDLY_OFFSET;
+ }
+ res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
+ if (res > 0) {
+ f.datalen = res;
+ f.samples = res / 2;
+ ast_write(chan, &f);
+ if (res == len)
+ return 0;
+ }
+ return -1;
+}
+
+static void *linear_alloc(struct ast_channel *chan, void *params)
+{
+ struct linear_state *ls = params;
+
+ if (!params)
+ return NULL;
+
+ /* In this case, params is already malloc'd */
+ if (ls->allowoverride)
+ ast_set_flag(chan, AST_FLAG_WRITE_INT);
+ else
+ ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+
+ ls->origwfmt = chan->writeformat;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
+ ast_free(ls);
+ ls = params = NULL;
+ }
+
+ return params;
+}
+
+static struct ast_generator linearstream =
+{
+ alloc: linear_alloc,
+ release: linear_release,
+ generate: linear_generator,
+};
+
+int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
+{
+ struct linear_state *lin;
+ char tmpf[256];
+ int res = -1;
+ int autoclose = 0;
+ if (fd < 0) {
+ if (ast_strlen_zero(filename))
+ return -1;
+ autoclose = 1;
+ if (filename[0] == '/')
+ ast_copy_string(tmpf, filename, sizeof(tmpf));
+ else
+ snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", ast_config_AST_DATA_DIR, "sounds", filename);
+ if ((fd = open(tmpf, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
+ return -1;
+ }
+ }
+ if ((lin = ast_calloc(1, sizeof(*lin)))) {
+ lin->fd = fd;
+ lin->allowoverride = allowoverride;
+ lin->autoclose = autoclose;
+ res = ast_activate_generator(chan, &linearstream, lin);
+ }
+ return res;
+}
+
+int ast_control_streamfile(struct ast_channel *chan, const char *file,
+ const char *fwd, const char *rev,
+ const char *stop, const char *pause,
+ const char *restart, int skipms, long *offsetms)
+{
+ char *breaks = NULL;
+ char *end = NULL;
+ int blen = 2;
+ int res;
+ long pause_restart_point = 0;
+ long offset = 0;
+
+ if (offsetms)
+ offset = *offsetms * 8; /* XXX Assumes 8kHz */
+
+ if (stop)
+ blen += strlen(stop);
+ if (pause)
+ blen += strlen(pause);
+ if (restart)
+ blen += strlen(restart);
+
+ if (blen > 2) {
+ breaks = alloca(blen + 1);
+ breaks[0] = '\0';
+ if (stop)
+ strcat(breaks, stop);
+ if (pause)
+ strcat(breaks, pause);
+ if (restart)
+ strcat(breaks, restart);
+ }
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ if (file) {
+ if ((end = strchr(file,':'))) {
+ if (!strcasecmp(end, ":end")) {
+ *end = '\0';
+ end++;
+ }
+ }
+ }
+
+ for (;;) {
+ ast_stopstream(chan);
+ res = ast_streamfile(chan, file, chan->language);
+ if (!res) {
+ if (pause_restart_point) {
+ ast_seekstream(chan->stream, pause_restart_point, SEEK_SET);
+ pause_restart_point = 0;
+ }
+ else if (end || offset < 0) {
+ if (offset == -8)
+ offset = 0;
+ ast_verb(3, "ControlPlayback seek to offset %ld from end\n", offset);
+
+ ast_seekstream(chan->stream, offset, SEEK_END);
+ end = NULL;
+ offset = 0;
+ } else if (offset) {
+ ast_verb(3, "ControlPlayback seek to offset %ld\n", offset);
+ ast_seekstream(chan->stream, offset, SEEK_SET);
+ offset = 0;
+ };
+ res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
+ }
+
+ if (res < 1)
+ break;
+
+ /* We go at next loop if we got the restart char */
+ if (restart && strchr(restart, res)) {
+ ast_debug(1, "we'll restart the stream here at next loop\n");
+ pause_restart_point = 0;
+ continue;
+ }
+
+ if (pause && strchr(pause, res)) {
+ pause_restart_point = ast_tellstream(chan->stream);
+ for (;;) {
+ ast_stopstream(chan);
+ res = ast_waitfordigit(chan, 1000);
+ if (!res)
+ continue;
+ else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
+ break;
+ }
+ if (res == *pause) {
+ res = 0;
+ continue;
+ }
+ }
+
+ if (res == -1)
+ break;
+
+ /* if we get one of our stop chars, return it to the calling function */
+ if (stop && strchr(stop, res))
+ break;
+ }
+
+ if (pause_restart_point) {
+ offset = pause_restart_point;
+ } else {
+ if (chan->stream) {
+ offset = ast_tellstream(chan->stream);
+ } else {
+ offset = -8; /* indicate end of file */
+ }
+ }
+
+ if (offsetms)
+ *offsetms = offset / 8; /* samples --> ms ... XXX Assumes 8 kHz */
+
+ /* If we are returning a digit cast it as char */
+ if (res > 0 || chan->stream)
+ res = (char)res;
+
+ ast_stopstream(chan);
+
+ return res;
+}
+
+int ast_play_and_wait(struct ast_channel *chan, const char *fn)
+{
+ int d = 0;
+
+ if ((d = ast_streamfile(chan, fn, chan->language)))
+ return d;
+
+ d = ast_waitstream(chan, AST_DIGIT_ANY);
+
+ ast_stopstream(chan);
+
+ return d;
+}
+
+static int global_silence_threshold = 128;
+static int global_maxsilence = 0;
+
+/*! Optionally play a sound file or a beep, then record audio and video from the channel.
+ * \param chan Channel to playback to/record from.
+ * \param playfile Filename of sound to play before recording begins.
+ * \param recordfile Filename to record to.
+ * \param maxtime Maximum length of recording (in milliseconds).
+ * \param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
+ * \param duration Where to store actual length of the recorded message (in milliseconds).
+ * \param beep Whether to play a beep before starting to record.
+ * \param silencethreshold
+ * \param maxsilence Length of silence that will end a recording (in milliseconds).
+ * \param path Optional filesystem path to unlock.
+ * \param prepend If true, prepend the recorded audio to an existing file.
+ * \param acceptdtmf DTMF digits that will end the recording.
+ * \param canceldtmf DTMF digits that will cancel the recording.
+ */
+
+static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf)
+{
+ int d = 0;
+ char *fmts;
+ char comment[256];
+ int x, fmtcnt = 1, res = -1, outmsg = 0;
+ struct ast_filestream *others[MAX_OTHER_FORMATS];
+ char *sfmt[MAX_OTHER_FORMATS];
+ char *stringp = NULL;
+ time_t start, end;
+ struct ast_dsp *sildet = NULL; /* silence detector dsp */
+ int totalsilence = 0;
+ int rfmt = 0;
+ struct ast_silence_generator *silgen = NULL;
+ char prependfile[80];
+
+ if (silencethreshold < 0)
+ silencethreshold = global_silence_threshold;
+
+ if (maxsilence < 0)
+ maxsilence = global_maxsilence;
+
+ /* barf if no pointer passed to store duration in */
+ if (!duration) {
+ ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
+ return -1;
+ }
+
+ ast_debug(1, "play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
+ snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
+
+ if (playfile || beep) {
+ if (!beep)
+ d = ast_play_and_wait(chan, playfile);
+ if (d > -1)
+ d = ast_stream_and_wait(chan, "beep", "");
+ if (d < 0)
+ return -1;
+ }
+
+ if (prepend) {
+ ast_copy_string(prependfile, recordfile, sizeof(prependfile));
+ strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
+ }
+
+ fmts = ast_strdupa(fmt);
+
+ stringp = fmts;
+ strsep(&stringp, "|");
+ ast_debug(1, "Recording Formats: sfmts=%s\n", fmts);
+ sfmt[0] = ast_strdupa(fmts);
+
+ while ((fmt = strsep(&stringp, "|"))) {
+ if (fmtcnt > MAX_OTHER_FORMATS - 1) {
+ ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
+ break;
+ }
+ sfmt[fmtcnt++] = ast_strdupa(fmt);
+ }
+
+ end = start = time(NULL); /* pre-initialize end to be same as start in case we never get into loop */
+ for (x = 0; x < fmtcnt; x++) {
+ others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, AST_FILE_MODE);
+ ast_verb(3, "x=%d, open writing: %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
+
+ if (!others[x])
+ break;
+ }
+
+ if (path)
+ ast_unlock_path(path);
+
+ if (maxsilence > 0) {
+ sildet = ast_dsp_new(); /* Create the silence detector */
+ if (!sildet) {
+ ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+ return -1;
+ }
+ ast_dsp_set_threshold(sildet, silencethreshold);
+ rfmt = chan->readformat;
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+ ast_dsp_free(sildet);
+ return -1;
+ }
+ }
+
+ if (!prepend) {
+ /* Request a video update */
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+
+ if (ast_opt_transmit_silence)
+ silgen = ast_channel_start_silence_generator(chan);
+ }
+
+ if (x == fmtcnt) {
+ /* Loop forever, writing the packets we read to the writer(s), until
+ we read a digit or get a hangup */
+ struct ast_frame *f;
+ for (;;) {
+ res = ast_waitfor(chan, 2000);
+ if (!res) {
+ ast_debug(1, "One waitfor failed, trying another\n");
+ /* Try one more time in case of masq */
+ res = ast_waitfor(chan, 2000);
+ if (!res) {
+ ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
+ res = -1;
+ }
+ }
+
+ if (res < 0) {
+ f = NULL;
+ break;
+ }
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_VOICE) {
+ /* write each format */
+ for (x = 0; x < fmtcnt; x++) {
+ if (prepend && !others[x])
+ break;
+ res = ast_writestream(others[x], f);
+ }
+
+ /* Silence Detection */
+ if (maxsilence > 0) {
+ int dspsilence = 0;
+ ast_dsp_silence(sildet, f, &dspsilence);
+ if (dspsilence)
+ totalsilence = dspsilence;
+ else
+ totalsilence = 0;
+
+ if (totalsilence > maxsilence) {
+ /* Ended happily with silence */
+ ast_verb(3, "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
+ res = 'S';
+ outmsg = 2;
+ break;
+ }
+ }
+ /* Exit on any error */
+ if (res) {
+ ast_log(LOG_WARNING, "Error writing frame\n");
+ break;
+ }
+ } else if (f->frametype == AST_FRAME_VIDEO) {
+ /* Write only once */
+ ast_writestream(others[0], f);
+ } else if (f->frametype == AST_FRAME_DTMF) {
+ if (prepend) {
+ /* stop recording with any digit */
+ ast_verb(3, "User ended message by pressing %c\n", f->subclass);
+ res = 't';
+ outmsg = 2;
+ break;
+ }
+ if (strchr(acceptdtmf, f->subclass)) {
+ ast_verb(3, "User ended message by pressing %c\n", f->subclass);
+ res = f->subclass;
+ outmsg = 2;
+ break;
+ }
+ if (strchr(canceldtmf, f->subclass)) {
+ ast_verb(3, "User cancelled message by pressing %c\n", f->subclass);
+ res = f->subclass;
+ outmsg = 0;
+ break;
+ }
+ }
+ if (maxtime) {
+ end = time(NULL);
+ if (maxtime < (end - start)) {
+ ast_verb(3, "Took too long, cutting it short...\n");
+ res = 't';
+ outmsg = 2;
+ break;
+ }
+ }
+ ast_frfree(f);
+ }
+ if (!f) {
+ ast_verb(3, "User hung up\n");
+ res = -1;
+ outmsg = 1;
+ } else {
+ ast_frfree(f);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
+ }
+
+ if (!prepend) {
+ if (silgen)
+ ast_channel_stop_silence_generator(chan, silgen);
+ }
+
+ /*!\note
+ * Instead of asking how much time passed (end - start), calculate the number
+ * of seconds of audio which actually went into the file. This fixes a
+ * problem where audio is stopped up on the network and never gets to us.
+ *
+ * Note that we still want to use the number of seconds passed for the max
+ * message, otherwise we could get a situation where this stream is never
+ * closed (which would create a resource leak).
+ */
+ *duration = ast_tellstream(others[0]) / 8000;
+
+ if (!prepend) {
+ for (x = 0; x < fmtcnt; x++) {
+ if (!others[x])
+ break;
+ /*!\note
+ * If we ended with silence, trim all but the first 200ms of silence
+ * off the recording. However, if we ended with '#', we don't want
+ * to trim ANY part of the recording.
+ */
+ if (res > 0 && totalsilence)
+ ast_stream_rewind(others[x], totalsilence - 200);
+ ast_truncstream(others[x]);
+ ast_closestream(others[x]);
+ }
+ }
+
+ if (prepend && outmsg) {
+ struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
+ struct ast_frame *fr;
+
+ for (x = 0; x < fmtcnt; x++) {
+ snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
+ realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
+ if (!others[x] || !realfiles[x])
+ break;
+ /*!\note Same logic as above. */
+ if (totalsilence)
+ ast_stream_rewind(others[x], totalsilence - 200);
+ ast_truncstream(others[x]);
+ /* add the original file too */
+ while ((fr = ast_readframe(realfiles[x]))) {
+ ast_writestream(others[x], fr);
+ ast_frfree(fr);
+ }
+ ast_closestream(others[x]);
+ ast_closestream(realfiles[x]);
+ ast_filerename(prependfile, recordfile, sfmt[x]);
+ ast_verb(4, "Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x], prependfile, recordfile);
+ ast_filedelete(prependfile, sfmt[x]);
+ }
+ }
+ if (rfmt && ast_set_read_format(chan, rfmt)) {
+ ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
+ }
+ if (outmsg == 2) {
+ ast_stream_and_wait(chan, "auth-thankyou", "");
+ }
+ if (sildet)
+ ast_dsp_free(sildet);
+ return res;
+}
+
+static char default_acceptdtmf[] = "#";
+static char default_canceldtmf[] = "";
+
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
+{
+ return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf));
+}
+
+int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
+{
+ return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf);
+}
+
+int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
+{
+ return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf);
+}
+
+/* Channel group core functions */
+
+int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max)
+{
+ int res = 0;
+ char tmp[256];
+ char *grp = NULL, *cat = NULL;
+
+ if (!ast_strlen_zero(data)) {
+ ast_copy_string(tmp, data, sizeof(tmp));
+ grp = tmp;
+ cat = strchr(tmp, '@');
+ if (cat) {
+ *cat = '\0';
+ cat++;
+ }
+ }
+
+ if (!ast_strlen_zero(grp))
+ ast_copy_string(group, grp, group_max);
+ else
+ *group = '\0';
+
+ if (!ast_strlen_zero(cat))
+ ast_copy_string(category, cat, category_max);
+
+ return res;
+}
+
+int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
+{
+ int res = 0;
+ char group[80] = "", category[80] = "";
+ struct ast_group_info *gi = NULL;
+ size_t len = 0;
+
+ if (ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category)))
+ return -1;
+
+ /* Calculate memory we will need if this is new */
+ len = sizeof(*gi) + strlen(group) + 1;
+ if (!ast_strlen_zero(category))
+ len += strlen(category) + 1;
+
+ AST_RWLIST_WRLOCK(&groups);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
+ if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ free(gi);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (ast_strlen_zero(group)) {
+ /* Enable unsetting the group */
+ } else if ((gi = calloc(1, len))) {
+ gi->chan = chan;
+ gi->group = (char *) gi + sizeof(*gi);
+ strcpy(gi->group, group);
+ if (!ast_strlen_zero(category)) {
+ gi->category = (char *) gi + sizeof(*gi) + strlen(group) + 1;
+ strcpy(gi->category, category);
+ }
+ AST_RWLIST_INSERT_TAIL(&groups, gi, list);
+ } else {
+ res = -1;
+ }
+
+ AST_RWLIST_UNLOCK(&groups);
+
+ return res;
+}
+
+int ast_app_group_get_count(const char *group, const char *category)
+{
+ struct ast_group_info *gi = NULL;
+ int count = 0;
+
+ if (ast_strlen_zero(group))
+ return 0;
+
+ AST_RWLIST_RDLOCK(&groups);
+ AST_RWLIST_TRAVERSE(&groups, gi, list) {
+ if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
+ count++;
+ }
+ AST_RWLIST_UNLOCK(&groups);
+
+ return count;
+}
+
+int ast_app_group_match_get_count(const char *groupmatch, const char *category)
+{
+ struct ast_group_info *gi = NULL;
+ regex_t regexbuf;
+ int count = 0;
+
+ if (ast_strlen_zero(groupmatch))
+ return 0;
+
+ /* if regex compilation fails, return zero matches */
+ if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
+ return 0;
+
+ AST_RWLIST_RDLOCK(&groups);
+ AST_RWLIST_TRAVERSE(&groups, gi, list) {
+ if (!regexec(&regexbuf, gi->group, 0, NULL, 0) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
+ count++;
+ }
+ AST_RWLIST_UNLOCK(&groups);
+
+ regfree(&regexbuf);
+
+ return count;
+}
+
+int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)
+{
+ struct ast_group_info *gi = NULL;
+
+ AST_RWLIST_WRLOCK(&groups);
+ AST_RWLIST_TRAVERSE(&groups, gi, list) {
+ if (gi->chan == old)
+ gi->chan = new;
+ }
+ AST_RWLIST_UNLOCK(&groups);
+
+ return 0;
+}
+
+int ast_app_group_discard(struct ast_channel *chan)
+{
+ struct ast_group_info *gi = NULL;
+
+ AST_RWLIST_WRLOCK(&groups);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
+ if (gi->chan == chan) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(gi);
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&groups);
+
+ return 0;
+}
+
+int ast_app_group_list_wrlock(void)
+{
+ return AST_RWLIST_WRLOCK(&groups);
+}
+
+int ast_app_group_list_rdlock(void)
+{
+ return AST_RWLIST_RDLOCK(&groups);
+}
+
+struct ast_group_info *ast_app_group_list_head(void)
+{
+ return AST_RWLIST_FIRST(&groups);
+}
+
+int ast_app_group_list_unlock(void)
+{
+ return AST_RWLIST_UNLOCK(&groups);
+}
+
+unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
+{
+ int argc;
+ char *scan;
+ int paren = 0, quote = 0;
+
+ if (!buf || !array || !arraylen)
+ return 0;
+
+ memset(array, 0, arraylen * sizeof(*array));
+
+ scan = buf;
+
+ for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
+ array[argc] = scan;
+ for (; *scan; scan++) {
+ if (*scan == '(')
+ paren++;
+ else if (*scan == ')') {
+ if (paren)
+ paren--;
+ } else if (*scan == '"' && delim != '"') {
+ quote = quote ? 0 : 1;
+ /* Remove quote character from argument */
+ memmove(scan, scan + 1, strlen(scan));
+ scan--;
+ } else if (*scan == '\\') {
+ /* Literal character, don't parse */
+ memmove(scan, scan + 1, strlen(scan));
+ } else if ((*scan == delim) && !paren && !quote) {
+ *scan++ = '\0';
+ break;
+ }
+ }
+ }
+
+ if (*scan)
+ array[argc++] = scan;
+
+ return argc;
+}
+
+static enum AST_LOCK_RESULT ast_lock_path_lockfile(const char *path)
+{
+ char *s;
+ char *fs;
+ int res;
+ int fd;
+ int lp = strlen(path);
+ time_t start;
+
+ s = alloca(lp + 10);
+ fs = alloca(lp + 20);
+
+ snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
+ fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, AST_FILE_MODE);
+ if (fd < 0) {
+ ast_log(LOG_ERROR, "Unable to create lock file '%s': %s\n", path, strerror(errno));
+ return AST_LOCK_PATH_NOT_FOUND;
+ }
+ close(fd);
+
+ snprintf(s, strlen(path) + 9, "%s/.lock", path);
+ start = time(NULL);
+ while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
+ usleep(1);
+
+ unlink(fs);
+
+ if (res) {
+ ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
+ return AST_LOCK_TIMEOUT;
+ } else {
+ ast_debug(1, "Locked path '%s'\n", path);
+ return AST_LOCK_SUCCESS;
+ }
+}
+
+static int ast_unlock_path_lockfile(const char *path)
+{
+ char *s;
+ int res;
+
+ s = alloca(strlen(path) + 10);
+
+ snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
+
+ if ((res = unlink(s)))
+ ast_log(LOG_ERROR, "Could not unlock path '%s': %s\n", path, strerror(errno));
+ else {
+ ast_debug(1, "Unlocked path '%s'\n", path);
+ }
+
+ return res;
+}
+
+struct path_lock {
+ AST_LIST_ENTRY(path_lock) le;
+ int fd;
+ char *path;
+};
+
+static AST_LIST_HEAD_STATIC(path_lock_list, path_lock);
+
+static void path_lock_destroy(struct path_lock *obj)
+{
+ if (obj->fd >= 0)
+ close(obj->fd);
+ if (obj->path)
+ free(obj->path);
+ free(obj);
+}
+
+static enum AST_LOCK_RESULT ast_lock_path_flock(const char *path)
+{
+ char *fs;
+ int res;
+ int fd;
+ time_t start;
+ struct path_lock *pl;
+ struct stat st, ost;
+
+ fs = alloca(strlen(path) + 20);
+
+ snprintf(fs, strlen(path) + 19, "%s/lock", path);
+ if (lstat(fs, &st) == 0) {
+ if ((st.st_mode & S_IFMT) == S_IFLNK) {
+ ast_log(LOG_WARNING, "Unable to create lock file "
+ "'%s': it's already a symbolic link\n",
+ fs);
+ return AST_LOCK_FAILURE;
+ }
+ if (st.st_nlink > 1) {
+ ast_log(LOG_WARNING, "Unable to create lock file "
+ "'%s': %u hard links exist\n",
+ fs, (unsigned int) st.st_nlink);
+ return AST_LOCK_FAILURE;
+ }
+ }
+ fd = open(fs, O_WRONLY | O_CREAT, 0600);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to create lock file '%s': %s\n",
+ fs, strerror(errno));
+ return AST_LOCK_PATH_NOT_FOUND;
+ }
+ pl = ast_calloc(1, sizeof(*pl));
+ if (!pl) {
+ /* We don't unlink the lock file here, on the possibility that
+ * someone else created it - better to leave a little mess
+ * than create a big one by destroying someone else's lock
+ * and causing something to be corrupted.
+ */
+ close(fd);
+ return AST_LOCK_FAILURE;
+ }
+ pl->fd = fd;
+ pl->path = strdup(path);
+
+ time(&start);
+ while ((
+ #ifdef SOLARIS
+ (res = fcntl(pl->fd, F_SETLK, fcntl(pl->fd,F_GETFL)|O_NONBLOCK)) < 0) &&
+ #else
+ (res = flock(pl->fd, LOCK_EX | LOCK_NB)) < 0) &&
+ #endif
+ (errno == EWOULDBLOCK) &&
+ (time(NULL) - start < 5))
+ usleep(1000);
+ if (res) {
+ ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n",
+ path, strerror(errno));
+ /* No unlinking of lock done, since we tried and failed to
+ * flock() it.
+ */
+ path_lock_destroy(pl);
+ return AST_LOCK_TIMEOUT;
+ }
+
+ /* Check for the race where the file is recreated or deleted out from
+ * underneath us.
+ */
+ if (lstat(fs, &st) != 0 && fstat(pl->fd, &ost) != 0 &&
+ st.st_dev != ost.st_dev &&
+ st.st_ino != ost.st_ino) {
+ ast_log(LOG_WARNING, "Unable to create lock file '%s': "
+ "file changed underneath us\n", fs);
+ path_lock_destroy(pl);
+ return AST_LOCK_FAILURE;
+ }
+
+ /* Success: file created, flocked, and is the one we started with */
+ AST_LIST_LOCK(&path_lock_list);
+ AST_LIST_INSERT_TAIL(&path_lock_list, pl, le);
+ AST_LIST_UNLOCK(&path_lock_list);
+
+ ast_debug(1, "Locked path '%s'\n", path);
+
+ return AST_LOCK_SUCCESS;
+}
+
+static int ast_unlock_path_flock(const char *path)
+{
+ char *s;
+ struct path_lock *p;
+
+ s = alloca(strlen(path) + 20);
+
+ AST_LIST_LOCK(&path_lock_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&path_lock_list, p, le) {
+ if (!strcmp(p->path, path)) {
+ AST_LIST_REMOVE_CURRENT(le);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&path_lock_list);
+
+ if (p) {
+ snprintf(s, strlen(path) + 19, "%s/lock", path);
+ unlink(s);
+ path_lock_destroy(p);
+ ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
+ } else
+ ast_log(LOG_DEBUG, "Failed to unlock path '%s': "
+ "lock not found\n", path);
+
+ return 0;
+}
+
+void ast_set_lock_type(enum AST_LOCK_TYPE type)
+{
+ ast_lock_type = type;
+}
+
+enum AST_LOCK_RESULT ast_lock_path(const char *path)
+{
+ enum AST_LOCK_RESULT r = AST_LOCK_FAILURE;
+
+ switch (ast_lock_type) {
+ case AST_LOCK_TYPE_LOCKFILE:
+ r = ast_lock_path_lockfile(path);
+ break;
+ case AST_LOCK_TYPE_FLOCK:
+ r = ast_lock_path_flock(path);
+ break;
+ }
+
+ return r;
+}
+
+int ast_unlock_path(const char *path)
+{
+ int r = 0;
+
+ switch (ast_lock_type) {
+ case AST_LOCK_TYPE_LOCKFILE:
+ r = ast_unlock_path_lockfile(path);
+ break;
+ case AST_LOCK_TYPE_FLOCK:
+ r = ast_unlock_path_flock(path);
+ break;
+ }
+
+ return r;
+}
+
+int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
+{
+ int silencethreshold = 128;
+ int maxsilence = 0;
+ int res = 0;
+ int cmd = 0;
+ int max_attempts = 3;
+ int attempts = 0;
+ int recorded = 0;
+ int message_exists = 0;
+ /* Note that urgent and private are for flagging messages as such in the future */
+
+ /* barf if no pointer passed to store duration in */
+ if (!duration) {
+ ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
+ return -1;
+ }
+
+ cmd = '3'; /* Want to start by recording */
+
+ while ((cmd >= 0) && (cmd != 't')) {
+ switch (cmd) {
+ case '1':
+ if (!message_exists) {
+ /* In this case, 1 is to record a message */
+ cmd = '3';
+ break;
+ } else {
+ ast_stream_and_wait(chan, "vm-msgsaved", "");
+ cmd = 't';
+ return res;
+ }
+ case '2':
+ /* Review */
+ ast_verb(3, "Reviewing the recording\n");
+ cmd = ast_stream_and_wait(chan, recordfile, AST_DIGIT_ANY);
+ break;
+ case '3':
+ message_exists = 0;
+ /* Record */
+ if (recorded == 1)
+ ast_verb(3, "Re-recording\n");
+ else
+ ast_verb(3, "Recording\n");
+ recorded = 1;
+ cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path);
+ if (cmd == -1) {
+ /* User has hung up, no options to give */
+ return cmd;
+ }
+ if (cmd == '0') {
+ break;
+ } else if (cmd == '*') {
+ break;
+ }
+ else {
+ /* If all is well, a message exists */
+ message_exists = 1;
+ cmd = 0;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '*':
+ case '#':
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ break;
+ default:
+ if (message_exists) {
+ cmd = ast_play_and_wait(chan, "vm-review");
+ }
+ else {
+ cmd = ast_play_and_wait(chan, "vm-torerecord");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 6000);
+ if (!cmd) {
+ attempts++;
+ }
+ if (attempts > max_attempts) {
+ cmd = 't';
+ }
+ }
+ }
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+#define RES_UPONE (1 << 16)
+#define RES_EXIT (1 << 17)
+#define RES_REPEAT (1 << 18)
+#define RES_RESTART ((1 << 19) | RES_REPEAT)
+
+static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
+
+static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
+{
+ int res;
+ int (*ivr_func)(struct ast_channel *, void *);
+ char *c;
+ char *n;
+
+ switch (option->action) {
+ case AST_ACTION_UPONE:
+ return RES_UPONE;
+ case AST_ACTION_EXIT:
+ return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
+ case AST_ACTION_REPEAT:
+ return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
+ case AST_ACTION_RESTART:
+ return RES_RESTART ;
+ case AST_ACTION_NOOP:
+ return 0;
+ case AST_ACTION_BACKGROUND:
+ res = ast_stream_and_wait(chan, (char *)option->adata, AST_DIGIT_ANY);
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
+ res = 0;
+ }
+ return res;
+ case AST_ACTION_PLAYBACK:
+ res = ast_stream_and_wait(chan, (char *)option->adata, "");
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
+ res = 0;
+ }
+ return res;
+ case AST_ACTION_MENU:
+ res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
+ /* Do not pass entry errors back up, treat as though it was an "UPONE" */
+ if (res == -2)
+ res = 0;
+ return res;
+ case AST_ACTION_WAITOPTION:
+ res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
+ if (!res)
+ return 't';
+ return res;
+ case AST_ACTION_CALLBACK:
+ ivr_func = option->adata;
+ res = ivr_func(chan, cbdata);
+ return res;
+ case AST_ACTION_TRANSFER:
+ res = ast_parseable_goto(chan, option->adata);
+ return 0;
+ case AST_ACTION_PLAYLIST:
+ case AST_ACTION_BACKLIST:
+ res = 0;
+ c = ast_strdupa(option->adata);
+ while ((n = strsep(&c, ";"))) {
+ if ((res = ast_stream_and_wait(chan, n,
+ (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
+ break;
+ }
+ ast_stopstream(chan);
+ return res;
+ default:
+ ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
+ return 0;
+ };
+ return -1;
+}
+
+static int option_exists(struct ast_ivr_menu *menu, char *option)
+{
+ int x;
+ for (x = 0; menu->options[x].option; x++)
+ if (!strcasecmp(menu->options[x].option, option))
+ return x;
+ return -1;
+}
+
+static int option_matchmore(struct ast_ivr_menu *menu, char *option)
+{
+ int x;
+ for (x = 0; menu->options[x].option; x++)
+ if ((!strncasecmp(menu->options[x].option, option, strlen(option))) &&
+ (menu->options[x].option[strlen(option)]))
+ return x;
+ return -1;
+}
+
+static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
+{
+ int res=0;
+ int ms;
+ while (option_matchmore(menu, exten)) {
+ ms = chan->pbx ? chan->pbx->dtimeout : 5000;
+ if (strlen(exten) >= maxexten - 1)
+ break;
+ res = ast_waitfordigit(chan, ms);
+ if (res < 1)
+ break;
+ exten[strlen(exten) + 1] = '\0';
+ exten[strlen(exten)] = res;
+ }
+ return res > 0 ? 0 : res;
+}
+
+static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
+{
+ /* Execute an IVR menu structure */
+ int res=0;
+ int pos = 0;
+ int retries = 0;
+ char exten[AST_MAX_EXTENSION] = "s";
+ if (option_exists(menu, "s") < 0) {
+ strcpy(exten, "g");
+ if (option_exists(menu, "g") < 0) {
+ ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
+ return -1;
+ }
+ }
+ while (!res) {
+ while (menu->options[pos].option) {
+ if (!strcasecmp(menu->options[pos].option, exten)) {
+ res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
+ ast_debug(1, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
+ if (res < 0)
+ break;
+ else if (res & RES_UPONE)
+ return 0;
+ else if (res & RES_EXIT)
+ return res;
+ else if (res & RES_REPEAT) {
+ int maxretries = res & 0xffff;
+ if ((res & RES_RESTART) == RES_RESTART) {
+ retries = 0;
+ } else
+ retries++;
+ if (!maxretries)
+ maxretries = 3;
+ if ((maxretries > 0) && (retries >= maxretries)) {
+ ast_debug(1, "Max retries %d exceeded\n", maxretries);
+ return -2;
+ } else {
+ if (option_exists(menu, "g") > -1)
+ strcpy(exten, "g");
+ else if (option_exists(menu, "s") > -1)
+ strcpy(exten, "s");
+ }
+ pos = 0;
+ continue;
+ } else if (res && strchr(AST_DIGIT_ANY, res)) {
+ ast_debug(1, "Got start of extension, %c\n", res);
+ exten[1] = '\0';
+ exten[0] = res;
+ if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
+ break;
+ if (option_exists(menu, exten) < 0) {
+ if (option_exists(menu, "i")) {
+ ast_debug(1, "Invalid extension entered, going to 'i'!\n");
+ strcpy(exten, "i");
+ pos = 0;
+ continue;
+ } else {
+ ast_debug(1, "Aborting on invalid entry, with no 'i' option!\n");
+ res = -2;
+ break;
+ }
+ } else {
+ ast_debug(1, "New existing extension: %s\n", exten);
+ pos = 0;
+ continue;
+ }
+ }
+ }
+ pos++;
+ }
+ ast_debug(1, "Stopping option '%s', res is %d\n", exten, res);
+ pos = 0;
+ if (!strcasecmp(exten, "s"))
+ strcpy(exten, "g");
+ else
+ break;
+ }
+ return res;
+}
+
+int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
+{
+ int res = ast_ivr_menu_run_internal(chan, menu, cbdata);
+ /* Hide internal coding */
+ return res > 0 ? 0 : res;
+}
+
+char *ast_read_textfile(const char *filename)
+{
+ int fd, count = 0, res;
+ char *output = NULL;
+ struct stat filesize;
+
+ if (stat(filename, &filesize) == -1) {
+ ast_log(LOG_WARNING, "Error can't stat %s\n", filename);
+ return NULL;
+ }
+
+ count = filesize.st_size + 1;
+
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ if ((output = ast_malloc(count))) {
+ res = read(fd, output, count - 1);
+ if (res == count - 1) {
+ output[res] = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Short read of %s (%d of %d): %s\n", filename, res, count - 1, strerror(errno));
+ ast_free(output);
+ output = NULL;
+ }
+ }
+
+ close(fd);
+
+ return output;
+}
+
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
+{
+ char *s, *arg;
+ int curarg, res = 0;
+ unsigned int argloc;
+
+ ast_clear_flag(flags, AST_FLAGS_ALL);
+
+ if (!optstr)
+ return 0;
+
+ s = optstr;
+ while (*s) {
+ curarg = *s++ & 0x7f; /* the array (in app.h) has 128 entries */
+ argloc = options[curarg].arg_index;
+ if (*s == '(') {
+ /* Has argument */
+ arg = ++s;
+ if ((s = strchr(s, ')'))) {
+ if (argloc)
+ args[argloc - 1] = arg;
+ *s++ = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
+ res = -1;
+ break;
+ }
+ } else if (argloc) {
+ args[argloc - 1] = "";
+ }
+ ast_set_flag(flags, options[curarg].flag);
+ }
+
+ return res;
+}
+
+/* the following function will probably only be used in app_dial, until app_dial is reorganized to
+ better handle the large number of options it provides. After it is, you need to get rid of this variant
+ -- unless, of course, someone else digs up some use for large flag fields. */
+
+int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr)
+{
+ char *s, *arg;
+ int curarg, res = 0;
+ unsigned int argloc;
+
+ flags->flags = 0;
+
+ if (!optstr)
+ return 0;
+
+ s = optstr;
+ while (*s) {
+ curarg = *s++ & 0x7f; /* the array (in app.h) has 128 entries */
+ ast_set_flag64(flags, options[curarg].flag);
+ argloc = options[curarg].arg_index;
+ if (*s == '(') {
+ /* Has argument */
+ arg = ++s;
+ if ((s = strchr(s, ')'))) {
+ if (argloc)
+ args[argloc - 1] = arg;
+ *s++ = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
+ res = -1;
+ break;
+ }
+ } else if (argloc) {
+ args[argloc - 1] = NULL;
+ }
+ }
+
+ return res;
+}
+
+int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
+{
+ int i;
+ *consumed = 1;
+ *result = 0;
+ if (*stream == '\\') {
+ *consumed = 2;
+ switch (*(stream + 1)) {
+ case 'n':
+ *result = '\n';
+ break;
+ case 'r':
+ *result = '\r';
+ break;
+ case 't':
+ *result = '\t';
+ break;
+ case 'x':
+ /* Hexadecimal */
+ if (strchr("0123456789ABCDEFabcdef", *(stream + 2)) && *(stream + 2) != '\0') {
+ *consumed = 3;
+ if (*(stream + 2) <= '9')
+ *result = *(stream + 2) - '0';
+ else if (*(stream + 2) <= 'F')
+ *result = *(stream + 2) - 'A' + 10;
+ else
+ *result = *(stream + 2) - 'a' + 10;
+ } else {
+ ast_log(LOG_ERROR, "Illegal character '%c' in hexadecimal string\n", *(stream + 2));
+ return -1;
+ }
+
+ if (strchr("0123456789ABCDEFabcdef", *(stream + 3)) && *(stream + 3) != '\0') {
+ *consumed = 4;
+ *result <<= 4;
+ if (*(stream + 3) <= '9')
+ *result += *(stream + 3) - '0';
+ else if (*(stream + 3) <= 'F')
+ *result += *(stream + 3) - 'A' + 10;
+ else
+ *result += *(stream + 3) - 'a' + 10;
+ }
+ break;
+ case '0':
+ /* Octal */
+ *consumed = 2;
+ for (i = 2; ; i++) {
+ if (strchr("01234567", *(stream + i)) && *(stream + i) != '\0') {
+ (*consumed)++;
+ ast_debug(5, "result was %d, ", *result);
+ *result <<= 3;
+ *result += *(stream + i) - '0';
+ ast_debug(5, "is now %d\n", *result);
+ } else
+ break;
+ }
+ break;
+ default:
+ *result = *(stream + 1);
+ }
+ } else {
+ *result = *stream;
+ *consumed = 1;
+ }
+ return 0;
+}
+
diff --git a/trunk/main/ast_expr2.c b/trunk/main/ast_expr2.c
new file mode 100644
index 000000000..693a7896a
--- /dev/null
+++ b/trunk/main/ast_expr2.c
@@ -0,0 +1,3448 @@
+/* A Bison parser, made by GNU Bison 2.1a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ 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, 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.1a"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names. */
+#define yyparse ast_yyparse
+#define yylex ast_yylex
+#define yyerror ast_yyerror
+#define yylval ast_yylval
+#define yychar ast_yychar
+#define yydebug ast_yydebug
+#define yynerrs ast_yynerrs
+#define yylloc ast_yylloc
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ TOK_COMMA = 258,
+ TOK_COLONCOLON = 259,
+ TOK_COND = 260,
+ TOK_OR = 261,
+ TOK_AND = 262,
+ TOK_NE = 263,
+ TOK_LE = 264,
+ TOK_GE = 265,
+ TOK_LT = 266,
+ TOK_GT = 267,
+ TOK_EQ = 268,
+ TOK_MINUS = 269,
+ TOK_PLUS = 270,
+ TOK_MOD = 271,
+ TOK_DIV = 272,
+ TOK_MULT = 273,
+ TOK_COMPL = 274,
+ TOK_EQTILDE = 275,
+ TOK_COLON = 276,
+ TOK_LP = 277,
+ TOK_RP = 278,
+ TOKEN = 279
+ };
+#endif
+/* Tokens. */
+#define TOK_COMMA 258
+#define TOK_COLONCOLON 259
+#define TOK_COND 260
+#define TOK_OR 261
+#define TOK_AND 262
+#define TOK_NE 263
+#define TOK_LE 264
+#define TOK_GE 265
+#define TOK_LT 266
+#define TOK_GT 267
+#define TOK_EQ 268
+#define TOK_MINUS 269
+#define TOK_PLUS 270
+#define TOK_MOD 271
+#define TOK_DIV 272
+#define TOK_MULT 273
+#define TOK_COMPL 274
+#define TOK_EQTILDE 275
+#define TOK_COLON 276
+#define TOK_LP 277
+#define TOK_RP 278
+#define TOKEN 279
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "ast_expr2.y"
+
+/* Written by Pace Willisson (pace@blitz.com)
+ * and placed in the public domain.
+ *
+ * Largely rewritten by J.T. Conklin (jtc@wimsey.com)
+ *
+ * And then overhauled twice by Steve Murphy (murf@digium.com)
+ * to add double-quoted strings, allow mult. spaces, improve
+ * error messages, and then to fold in a flex scanner for the
+ * yylex operation.
+ *
+ * $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#ifdef STANDALONE
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___TYPE double
+#endif
+
+#ifdef HAVE_COSL
+#define FUNC_COS cosl
+#elif defined(HAVE_COS)
+#define FUNC_COS (long double)cos
+#endif
+
+#ifdef HAVE_SINL
+#define FUNC_SIN sinl
+#elif defined(HAVE_SIN)
+#define FUNC_SIN (long double)sin
+#endif
+
+#ifdef HAVE_TANL
+#define FUNC_TAN tanl
+#elif defined(HAVE_TAN)
+#define FUNC_TAN (long double)tan
+#endif
+
+#ifdef HAVE_ACOSL
+#define FUNC_ACOS acosl
+#elif defined(HAVE_ACOS)
+#define FUNC_ACOS (long double)acos
+#endif
+
+#ifdef HAVE_ASINL
+#define FUNC_ASIN asinl
+#elif defined(HAVE_ASIN)
+#define FUNC_ASIN (long double)asin
+#endif
+
+#ifdef HAVE_ATANL
+#define FUNC_ATAN atanl
+#elif defined(HAVE_ATAN)
+#define FUNC_ATAN (long double)atan
+#endif
+
+#ifdef HAVE_ATAN2L
+#define FUNC_ATAN2 atan2l
+#elif defined(HAVE_ATAN2)
+#define FUNC_ATAN2 (long double)atan2
+#endif
+
+#ifdef HAVE_POWL
+#define FUNC_POW powl
+#elif defined(HAVE_POW)
+#define FUNC_POW (long double)pow
+#endif
+
+#ifdef HAVE_SQRTL
+#define FUNC_SQRT sqrtl
+#elif defined(HAVE_SQRT)
+#define FUNC_SQRT (long double)sqrt
+#endif
+
+#ifdef HAVE_RINTL
+#define FUNC_RINT rintl
+#elif defined(HAVE_RINT)
+#define FUNC_RINT (long double)rint
+#endif
+
+#ifdef HAVE_EXPL
+#define FUNC_EXP expl
+#elif defined(HAVE_EXP)
+#define FUNC_EXP (long double)exp
+#endif
+
+#ifdef HAVE_LOGL
+#define FUNC_LOG logl
+#elif defined(HAVE_LOG)
+#define FUNC_LOG (long double)log
+#endif
+
+#ifdef HAVE_REMINDERL
+#define FUNC_REMINDER reminderl
+#elif defined(HAVE_REMINDER)
+#define FUNC_REMINDER (long double)reminder
+#endif
+
+#ifdef HAVE_FMODL
+#define FUNC_FMOD fmodl
+#elif defined(HAVE_FMOD)
+#define FUNC_FMOD (long double)fmod
+#endif
+
+#ifdef HAVE_STRTOLD
+#define FUNC_STRTOD strtold
+#elif defined(HAVE_STRTOD)
+#define FUNC_STRTOD (long double)strtod
+#endif
+
+#ifdef HAVE_FLOORL
+#define FUNC_FLOOR floorl
+#elif defined(HAVE_FLOOR)
+#define FUNC_FLOOR (long double)floor
+#endif
+
+#ifdef HAVE_CEILL
+#define FUNC_CEIL ceill
+#elif defined(HAVE_CEIL)
+#define FUNC_CEIL (long double)ceil
+#endif
+
+#ifdef HAVE_ROUNDL
+#define FUNC_ROUND roundl
+#elif defined(HAVE_ROUND)
+#define FUNC_ROUND (long double)round
+#endif
+
+#ifdef HAVE_TRUNCL
+#define FUNC_TRUNC truncl
+#elif defined(HAVE_TRUNC)
+#define FUNC_TRUNC (long double)trunc
+#endif
+
+/*! \note
+ * Oddly enough, some platforms have some ISO C99 functions, but not others, so
+ * we define the missing functions in terms of their mathematical identities.
+ */
+#ifdef HAVE_EXP2L
+#define FUNC_EXP2 exp2l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP2(x) expl((x) * logl(2.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP2(x) (long double)exp((x) * log(2.0))
+#endif
+
+#ifdef HAVE_EXP10L
+#define FUNC_EXP10 exp10l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP10(x) expl((x) * logl(10.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP10(x) (long double)exp((x) * log(10.0))
+#endif
+
+#ifdef HAVE_LOG2L
+#define FUNC_LOG2 log2l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG2(x) (logl(x) / logl(2.0))
+#elif defined(HAVE_LOG10L)
+#define FUNC_LOG2(x) (log10l(x) / log10l(2.0))
+#elif defined(HAVE_LOG2)
+#define FUNC_LOG2 (long double)log2
+#elif defined(HAVE_LOG)
+#define FUNC_LOG2(x) ((long double)log(x) / log(2.0))
+#endif
+
+#ifdef HAVE_LOG10L
+#define FUNC_LOG10 log10l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG10(x) (logl(x) / logl(10.0))
+#elif defined(HAVE_LOG2L)
+#define FUNC_LOG10(x) (log2l(x) / log2l(10.0))
+#elif defined(HAVE_LOG10)
+#define FUNC_LOG10(x) (long double)log10(x)
+#elif defined(HAVE_LOG)
+#define FUNC_LOG10(x) ((long double)log(x) / log(10.0))
+#endif
+
+
+#include <stdlib.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <string.h>
+#include <math.h>
+#include <locale.h>
+#include <unistd.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+ /* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/pbx.h"
+#endif
+
+#if defined(LONG_LONG_MIN) && !defined(QUAD_MIN)
+#define QUAD_MIN LONG_LONG_MIN
+#endif
+#if defined(LONG_LONG_MAX) && !defined(QUAD_MAX)
+#define QUAD_MAX LONG_LONG_MAX
+#endif
+
+# if ! defined(QUAD_MIN)
+# define QUAD_MIN (-0x7fffffffffffffffLL-1)
+# endif
+# if ! defined(QUAD_MAX)
+# define QUAD_MAX (0x7fffffffffffffffLL)
+# endif
+#define YYENABLE_NLS 0
+#define YYPARSE_PARAM parseio
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+extern char extra_error_message[4095];
+extern int extra_error_message_supplied;
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+#ifdef STANDALONE
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+#endif
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* either long double, or just double, on a bad day */
+ } u;
+} ;
+
+enum node_type {
+ AST_EXPR_NODE_COMMA, AST_EXPR_NODE_STRING, AST_EXPR_NODE_VAL
+} ;
+
+struct expr_node
+{
+ enum node_type type;
+ struct val *val;
+ struct expr_node *left;
+ struct expr_node *right;
+};
+
+
+typedef void *yyscan_t;
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+static int chk_div __P((FP___TYPE, FP___TYPE));
+static int chk_minus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_plus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_times __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static void free_value __P((struct val *));
+static int is_zero_or_null __P((struct val *));
+static int isstring __P((struct val *));
+static struct val *make_number __P((FP___TYPE));
+static struct val *make_str __P((const char *));
+static struct val *op_and __P((struct val *, struct val *));
+static struct val *op_colon __P((struct val *, struct val *));
+static struct val *op_eqtilde __P((struct val *, struct val *));
+static struct val *op_div __P((struct val *, struct val *));
+static struct val *op_eq __P((struct val *, struct val *));
+static struct val *op_ge __P((struct val *, struct val *));
+static struct val *op_gt __P((struct val *, struct val *));
+static struct val *op_le __P((struct val *, struct val *));
+static struct val *op_lt __P((struct val *, struct val *));
+static struct val *op_cond __P((struct val *, struct val *, struct val *));
+static struct val *op_minus __P((struct val *, struct val *));
+static struct val *op_negate __P((struct val *));
+static struct val *op_compl __P((struct val *));
+static struct val *op_ne __P((struct val *, struct val *));
+static struct val *op_or __P((struct val *, struct val *));
+static struct val *op_plus __P((struct val *, struct val *));
+static struct val *op_rem __P((struct val *, struct val *));
+static struct val *op_times __P((struct val *, struct val *));
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan);
+static int to_number __P((struct val *));
+static void to_string __P((struct val *));
+static struct expr_node *alloc_expr_node(enum node_type);
+static void destroy_arglist(struct expr_node *arglist);
+static int is_really_num(char *str);
+
+/* uh, if I want to predeclare yylex with a YYLTYPE, I have to predeclare the yyltype... sigh */
+typedef struct yyltype
+{
+ int first_line;
+ int first_column;
+
+ int last_line;
+ int last_column;
+} yyltype;
+
+# define YYLTYPE yyltype
+# define YYLTYPE_IS_TRIVIAL 1
+
+/* we will get warning about no prototype for yylex! But we can't
+ define it here, we have no definition yet for YYSTYPE. */
+
+int ast_yyerror(const char *,YYLTYPE *, struct parse_io *);
+
+/* I wanted to add args to the yyerror routine, so I could print out
+ some useful info about the error. Not as easy as it looks, but it
+ is possible. */
+#define ast_yyerror(x) ast_yyerror(x,&yyloc,parseio)
+#define DESTROY(x) {if((x)->type == AST_EXPR_numeric_string || (x)->type == AST_EXPR_string) free((x)->u.s); (x)->u.s = 0; free(x);}
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 341 "ast_expr2.y"
+{
+ struct val *val;
+ struct expr_node *arglist;
+}
+/* Line 198 of yacc.c. */
+#line 480 "ast_expr2.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+#line 346 "ast_expr2.y"
+
+extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
+
+
+/* Line 221 of yacc.c. */
+#line 508 "ast_expr2.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# ifdef __cplusplus
+extern "C" {
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifdef __cplusplus
+}
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+ + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 11
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 150
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 25
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 4
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 26
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 52
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 279
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 6, 8, 12, 17, 19, 23,
+ 27, 31, 35, 39, 43, 47, 51, 55, 59, 63,
+ 66, 69, 73, 77, 81, 85, 89
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 26, 0, -1, 28, -1, -1, 28, -1, 27, 3,
+ 28, -1, 24, 22, 27, 23, -1, 24, -1, 22,
+ 28, 23, -1, 28, 6, 28, -1, 28, 7, 28,
+ -1, 28, 13, 28, -1, 28, 12, 28, -1, 28,
+ 11, 28, -1, 28, 10, 28, -1, 28, 9, 28,
+ -1, 28, 8, 28, -1, 28, 15, 28, -1, 28,
+ 14, 28, -1, 14, 28, -1, 19, 28, -1, 28,
+ 18, 28, -1, 28, 17, 28, -1, 28, 16, 28,
+ -1, 28, 21, 28, -1, 28, 20, 28, -1, 28,
+ 5, 28, 4, 28, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 372, 372, 380, 387, 388, 397, 403, 404, 408,
+ 412, 416, 420, 424, 428, 432, 436, 440, 444, 448,
+ 452, 456, 460, 464, 468, 472, 476
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "TOK_COMMA", "TOK_COLONCOLON",
+ "TOK_COND", "TOK_OR", "TOK_AND", "TOK_NE", "TOK_LE", "TOK_GE", "TOK_LT",
+ "TOK_GT", "TOK_EQ", "TOK_MINUS", "TOK_PLUS", "TOK_MOD", "TOK_DIV",
+ "TOK_MULT", "TOK_COMPL", "TOK_EQTILDE", "TOK_COLON", "TOK_LP", "TOK_RP",
+ "TOKEN", "$accept", "start", "arglist", "expr", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 25, 26, 26, 27, 27, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 0, 1, 3, 4, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 2,
+ 2, 3, 3, 3, 3, 3, 5
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 3, 0, 0, 0, 7, 0, 2, 19, 20, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 0,
+ 4, 0, 9, 10, 16, 15, 14, 13, 12, 11,
+ 18, 17, 23, 22, 21, 25, 24, 0, 6, 0,
+ 5, 26
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 5, 29, 6
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -18
+static const yytype_int16 yypact[] =
+{
+ 112, 112, 112, 112, -16, 5, 62, -17, -17, 24,
+ 112, -18, 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112, -18, 4,
+ 62, 45, 93, 107, 123, 123, 123, 123, 123, 123,
+ 129, 129, -17, -17, -17, -18, -18, 112, -18, 112,
+ 62, 78
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -18, -18, -18, -1
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 7, 8, 9, 26, 27, 11, 10, 47, 0, 30,
+ 0, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 48, 0, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 0, 26, 27, 50, 28, 51, 49,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 0, 26, 27, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 0, 26, 27, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 0, 26, 27,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 0, 26, 27, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 1, 26, 27, 0,
+ 0, 2, 0, 0, 3, 0, 4, 21, 22, 23,
+ 24, 25, 0, 26, 27, 23, 24, 25, 0, 26,
+ 27
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 1, 2, 3, 20, 21, 0, 22, 3, -1, 10,
+ -1, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 23, -1, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, -1, 20, 21, 47, 23, 49, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, -1, 20, 21, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, -1, 20, 21, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, -1, 20, 21,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, -1, 20, 21, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 14, 20, 21, -1,
+ -1, 19, -1, -1, 22, -1, 24, 14, 15, 16,
+ 17, 18, -1, 20, 21, 16, 17, 18, -1, 20,
+ 21
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 14, 19, 22, 24, 26, 28, 28, 28, 28,
+ 22, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 20, 21, 23, 27,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 3, 23, 4,
+ 28, 28
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, Location); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp)
+ FILE *yyoutput;
+ int yytype;
+ const YYSTYPE * const yyvaluep;
+ const YYLTYPE * const yylocationp;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (yylocationp);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp)
+ FILE *yyoutput;
+ int yytype;
+ const YYSTYPE * const yyvaluep;
+ const YYLTYPE * const yylocationp;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ YY_LOCATION_PRINT (yyoutput, *yylocationp);
+ YYFPRINTF (yyoutput, ": ");
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule
+ )
+ YYSTYPE *yyvsp;
+ YYLTYPE *yylsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , &(yylsp[(yyi + 1) - (yynrhs)]) );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, yylsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ size_t yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn < YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ YYLTYPE *yylocationp;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (yylocationp);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+ case 4: /* "TOK_COLONCOLON" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1460 "ast_expr2.c"
+ break;
+ case 5: /* "TOK_COND" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1465 "ast_expr2.c"
+ break;
+ case 6: /* "TOK_OR" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1470 "ast_expr2.c"
+ break;
+ case 7: /* "TOK_AND" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1475 "ast_expr2.c"
+ break;
+ case 8: /* "TOK_NE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1480 "ast_expr2.c"
+ break;
+ case 9: /* "TOK_LE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1485 "ast_expr2.c"
+ break;
+ case 10: /* "TOK_GE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1490 "ast_expr2.c"
+ break;
+ case 11: /* "TOK_LT" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1495 "ast_expr2.c"
+ break;
+ case 12: /* "TOK_GT" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1500 "ast_expr2.c"
+ break;
+ case 13: /* "TOK_EQ" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1505 "ast_expr2.c"
+ break;
+ case 14: /* "TOK_MINUS" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1510 "ast_expr2.c"
+ break;
+ case 15: /* "TOK_PLUS" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1515 "ast_expr2.c"
+ break;
+ case 16: /* "TOK_MOD" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1520 "ast_expr2.c"
+ break;
+ case 17: /* "TOK_DIV" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1525 "ast_expr2.c"
+ break;
+ case 18: /* "TOK_MULT" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1530 "ast_expr2.c"
+ break;
+ case 19: /* "TOK_COMPL" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1535 "ast_expr2.c"
+ break;
+ case 20: /* "TOK_EQTILDE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1540 "ast_expr2.c"
+ break;
+ case 21: /* "TOK_COLON" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1545 "ast_expr2.c"
+ break;
+ case 22: /* "TOK_LP" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1550 "ast_expr2.c"
+ break;
+ case 23: /* "TOK_RP" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1555 "ast_expr2.c"
+ break;
+ case 24: /* "TOKEN" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1560 "ast_expr2.c"
+ break;
+ case 28: /* "expr" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1565 "ast_expr2.c"
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ /* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+/* Location data for the look-ahead symbol. */
+YYLTYPE yylloc;
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+ /* The location stack. */
+ YYLTYPE yylsa[YYINITDEPTH];
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ /* The locations where the error started and ended. */
+ YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+ YYLTYPE yyloc;
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ yylsp = yyls;
+#if YYLTYPE_IS_TRIVIAL
+ /* Initialize the default location before parsing starts. */
+ yylloc.first_line = yylloc.last_line = 1;
+ yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+ YYLTYPE *yyls1 = yyls;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yyls1, yysize * sizeof (*yylsp),
+ &yystacksize);
+ yyls = yyls1;
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+ YYSTACK_RELOCATE (yyls);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+ yylsp = yyls + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+ *++yylsp = yylloc;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+ /* Default location. */
+ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 372 "ast_expr2.y"
+ { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = (yyvsp[(1) - (1)].val)->type;
+ if( (yyvsp[(1) - (1)].val)->type == AST_EXPR_number )
+ ((struct parse_io *)parseio)->val->u.i = (yyvsp[(1) - (1)].val)->u.i;
+ else
+ ((struct parse_io *)parseio)->val->u.s = (yyvsp[(1) - (1)].val)->u.s;
+ free((yyvsp[(1) - (1)].val));
+ ;}
+ break;
+
+ case 3:
+#line 380 "ast_expr2.y"
+ {/* nothing */ ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = AST_EXPR_string;
+ ((struct parse_io *)parseio)->val->u.s = strdup("");
+ ;}
+ break;
+
+ case 4:
+#line 387 "ast_expr2.y"
+ { (yyval.arglist) = alloc_expr_node(AST_EXPR_NODE_VAL); (yyval.arglist)->val = (yyvsp[(1) - (1)].val);;}
+ break;
+
+ case 5:
+#line 388 "ast_expr2.y"
+ {struct expr_node *x = alloc_expr_node(AST_EXPR_NODE_VAL);
+ struct expr_node *t;
+ DESTROY((yyvsp[(2) - (3)].val));
+ for (t=(yyvsp[(1) - (3)].arglist);t->right;t=t->right)
+ ;
+ (yyval.arglist) = (yyvsp[(1) - (3)].arglist); t->right = x; x->val = (yyvsp[(3) - (3)].val);;}
+ break;
+
+ case 6:
+#line 397 "ast_expr2.y"
+ { (yyval.val) = op_func((yyvsp[(1) - (4)].val),(yyvsp[(3) - (4)].arglist), ((struct parse_io *)parseio)->chan);
+ DESTROY((yyvsp[(2) - (4)].val));
+ DESTROY((yyvsp[(4) - (4)].val));
+ DESTROY((yyvsp[(1) - (4)].val));
+ destroy_arglist((yyvsp[(3) - (4)].arglist));
+ ;}
+ break;
+
+ case 7:
+#line 403 "ast_expr2.y"
+ {(yyval.val) = (yyvsp[(1) - (1)].val);;}
+ break;
+
+ case 8:
+#line 404 "ast_expr2.y"
+ { (yyval.val) = (yyvsp[(2) - (3)].val);
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;
+ DESTROY((yyvsp[(1) - (3)].val)); DESTROY((yyvsp[(3) - (3)].val)); ;}
+ break;
+
+ case 9:
+#line 408 "ast_expr2.y"
+ { (yyval.val) = op_or ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 10:
+#line 412 "ast_expr2.y"
+ { (yyval.val) = op_and ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 11:
+#line 416 "ast_expr2.y"
+ { (yyval.val) = op_eq ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 12:
+#line 420 "ast_expr2.y"
+ { (yyval.val) = op_gt ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 13:
+#line 424 "ast_expr2.y"
+ { (yyval.val) = op_lt ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 14:
+#line 428 "ast_expr2.y"
+ { (yyval.val) = op_ge ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 15:
+#line 432 "ast_expr2.y"
+ { (yyval.val) = op_le ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 16:
+#line 436 "ast_expr2.y"
+ { (yyval.val) = op_ne ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 17:
+#line 440 "ast_expr2.y"
+ { (yyval.val) = op_plus ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 18:
+#line 444 "ast_expr2.y"
+ { (yyval.val) = op_minus ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 19:
+#line 448 "ast_expr2.y"
+ { (yyval.val) = op_negate ((yyvsp[(2) - (2)].val));
+ DESTROY((yyvsp[(1) - (2)].val));
+ (yyloc).first_column = (yylsp[(1) - (2)]).first_column; (yyloc).last_column = (yylsp[(2) - (2)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 20:
+#line 452 "ast_expr2.y"
+ { (yyval.val) = op_compl ((yyvsp[(2) - (2)].val));
+ DESTROY((yyvsp[(1) - (2)].val));
+ (yyloc).first_column = (yylsp[(1) - (2)]).first_column; (yyloc).last_column = (yylsp[(2) - (2)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 21:
+#line 456 "ast_expr2.y"
+ { (yyval.val) = op_times ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 22:
+#line 460 "ast_expr2.y"
+ { (yyval.val) = op_div ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 23:
+#line 464 "ast_expr2.y"
+ { (yyval.val) = op_rem ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 24:
+#line 468 "ast_expr2.y"
+ { (yyval.val) = op_colon ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 25:
+#line 472 "ast_expr2.y"
+ { (yyval.val) = op_eqtilde ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 26:
+#line 476 "ast_expr2.y"
+ { (yyval.val) = op_cond ((yyvsp[(1) - (5)].val), (yyvsp[(3) - (5)].val), (yyvsp[(5) - (5)].val));
+ DESTROY((yyvsp[(2) - (5)].val));
+ DESTROY((yyvsp[(4) - (5)].val));
+ (yyloc).first_column = (yylsp[(1) - (5)]).first_column; (yyloc).last_column = (yylsp[(3) - (5)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+
+/* Line 1270 of yacc.c. */
+#line 2092 "ast_expr2.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+ *++yylsp = yyloc;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+ yyerror_range[0] = yylloc;
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, &yylloc);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ yyerror_range[0] = yylsp[1-yylen];
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ yyerror_range[0] = *yylsp;
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, yylsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+ yyerror_range[1] = yylloc;
+ /* Using YYLLOC is tempting, but would change the location of
+ the look-ahead. YYLOC is available though. */
+ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+ *++yylsp = yyloc;
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, &yylloc);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, yylsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+
+
+#line 483 "ast_expr2.y"
+
+
+static struct expr_node *alloc_expr_node(enum node_type nt)
+{
+ struct expr_node *x = calloc(1,sizeof(struct expr_node));
+ if (!x) {
+ ast_log(LOG_ERROR, "Allocation for expr_node FAILED!!\n");
+ return 0;
+ }
+ x->type = nt;
+ return x;
+}
+
+
+
+static struct val *
+make_number (FP___TYPE i)
+{
+ struct val *vp;
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ return(NULL);
+ }
+
+ vp->type = AST_EXPR_number;
+ vp->u.i = i;
+ return vp;
+}
+
+static struct val *
+make_str (const char *s)
+{
+ struct val *vp;
+ size_t i;
+ int isint; /* this started out being a test for an integer, but then ended up being a test for a float */
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return(NULL);
+ }
+
+ for (i = 0, isint = (isdigit(s[0]) || s[0] == '-' || s[0]=='.'); isint && i < strlen(s); i++)
+ {
+ if (!isdigit(s[i]) && s[i] != '.') {
+ isint = 0;
+ break;
+ }
+ }
+ if (isint)
+ vp->type = AST_EXPR_numeric_string;
+ else
+ vp->type = AST_EXPR_string;
+
+ return vp;
+}
+
+
+static void
+free_value (struct val *vp)
+{
+ if (vp==NULL) {
+ return;
+ }
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ free (vp->u.s);
+ free(vp);
+}
+
+
+static int
+to_number (struct val *vp)
+{
+ FP___TYPE i;
+
+ if (vp == NULL) {
+ ast_log(LOG_WARNING,"vp==NULL in to_number()\n");
+ return(0);
+ }
+
+ if (vp->type == AST_EXPR_number)
+ return 1;
+
+ if (vp->type == AST_EXPR_string)
+ return 0;
+
+ /* vp->type == AST_EXPR_numeric_string, make it numeric */
+ errno = 0;
+ i = FUNC_STRTOD(vp->u.s, (char**)0); /* either strtod, or strtold on a good day */
+ if (errno != 0) {
+ ast_log(LOG_WARNING,"Conversion of %s to number under/overflowed!\n", vp->u.s);
+ free(vp->u.s);
+ vp->u.s = 0;
+ return(0);
+ }
+ free (vp->u.s);
+ vp->u.i = i;
+ vp->type = AST_EXPR_number;
+ return 1;
+}
+
+static void
+strip_quotes(struct val *vp)
+{
+ if (vp->type != AST_EXPR_string && vp->type != AST_EXPR_numeric_string)
+ return;
+
+ if( vp->u.s[0] == '"' && vp->u.s[strlen(vp->u.s)-1] == '"' )
+ {
+ char *f, *t;
+ f = vp->u.s;
+ t = vp->u.s;
+
+ while( *f )
+ {
+ if( *f && *f != '"' )
+ *t++ = *f++;
+ else
+ f++;
+ }
+ *t = *f;
+ }
+}
+
+static void
+to_string (struct val *vp)
+{
+ char *tmp;
+
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ return;
+
+ tmp = malloc ((size_t)25);
+ if (tmp == NULL) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return;
+ }
+
+ sprintf(tmp, FP___PRINTF, vp->u.i);
+ vp->type = AST_EXPR_string;
+ vp->u.s = tmp;
+}
+
+
+static int
+isstring (struct val *vp)
+{
+ /* only TRUE if this string is not a valid number */
+ return (vp->type == AST_EXPR_string);
+}
+
+
+static int
+is_zero_or_null (struct val *vp)
+{
+ if (vp->type == AST_EXPR_number) {
+ return (vp->u.i == 0);
+ } else {
+ return (*vp->u.s == 0 || (to_number(vp) && vp->u.i == 0));
+ }
+ /* NOTREACHED */
+}
+
+#ifdef STANDALONE
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+
+int main(int argc,char **argv) {
+ char s[4096];
+ char out[4096];
+ FILE *infile;
+
+ if( !argv[1] )
+ exit(20);
+
+ if( access(argv[1],F_OK)== 0 )
+ {
+ int ret;
+
+ infile = fopen(argv[1],"r");
+ if( !infile )
+ {
+ printf("Sorry, couldn't open %s for reading!\n", argv[1]);
+ exit(10);
+ }
+ while( fgets(s,sizeof(s),infile) )
+ {
+ if( s[strlen(s)-1] == '\n' )
+ s[strlen(s)-1] = 0;
+
+ ret = ast_expr(s, out, sizeof(out),NULL);
+ printf("Expression: %s Result: [%d] '%s'\n",
+ s, ret, out);
+ }
+ fclose(infile);
+ }
+ else
+ {
+ if (ast_expr(argv[1], s, sizeof(s), NULL))
+ printf("=====%s======\n",s);
+ else
+ printf("No result\n");
+ }
+}
+
+#endif
+
+#undef ast_yyerror
+#define ast_yyerror(x) ast_yyerror(x, YYLTYPE *yylloc, struct parse_io *parseio)
+
+/* I put the ast_yyerror func in the flex input file,
+ because it refers to the buffer state. Best to
+ let it access the BUFFER stuff there and not trying
+ define all the structs, macros etc. in this file! */
+
+static void destroy_arglist(struct expr_node *arglist)
+{
+ struct expr_node *arglist_next;
+
+ while (arglist)
+ {
+ arglist_next = arglist->right;
+ if (arglist->val)
+ free_value(arglist->val);
+ arglist->val = 0;
+ arglist->right = 0;
+ free(arglist);
+ arglist = arglist_next;
+ }
+}
+
+static char *compose_func_args(struct expr_node *arglist)
+{
+ struct expr_node *t = arglist;
+ char *argbuf;
+ int total_len = 0;
+
+ while (t) {
+ if (t != arglist)
+ total_len += 1; /* for the sep */
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number)
+ total_len += 25; /* worst case */
+ else
+ total_len += strlen(t->val->u.s);
+ }
+
+ t = t->right;
+ }
+ total_len++; /* for the null */
+ ast_log(LOG_NOTICE,"argbuf allocated %d bytes;\n", total_len);
+ argbuf = malloc(total_len);
+ argbuf[0] = 0;
+ t = arglist;
+ while (t) {
+ char numbuf[30];
+
+ if (t != arglist)
+ strcat(argbuf,"|");
+
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number) {
+ sprintf(numbuf,FP___PRINTF,t->val->u.i);
+ strcat(argbuf,numbuf);
+ } else
+ strcat(argbuf,t->val->u.s);
+ }
+ t = t->right;
+ }
+ ast_log(LOG_NOTICE,"argbuf uses %d bytes;\n", (int) strlen(argbuf));
+ return argbuf;
+}
+
+static int is_really_num(char *str)
+{
+ if ( strspn(str,"-0123456789. ") == strlen(str))
+ return 1;
+ else
+ return 0;
+}
+
+
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan)
+{
+ if (strspn(funcname->u.s,"ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") == strlen(funcname->u.s))
+ {
+ struct val *result;
+ if (0) {
+#ifdef FUNC_COS
+ } else if (strcmp(funcname->u.s,"COS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_COS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SIN
+ } else if (strcmp(funcname->u.s,"SIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TAN
+ } else if (strcmp(funcname->u.s,"TAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ACOS
+ } else if (strcmp(funcname->u.s,"ACOS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ACOS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ASIN
+ } else if (strcmp(funcname->u.s,"ASIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ASIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN
+ } else if (strcmp(funcname->u.s,"ATAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ATAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN2
+ } else if (strcmp(funcname->u.s,"ATAN2") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_ATAN2(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_POW
+ } else if (strcmp(funcname->u.s,"POW") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_POW(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SQRT
+ } else if (strcmp(funcname->u.s,"SQRT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SQRT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_FLOOR
+ } else if (strcmp(funcname->u.s,"FLOOR") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_FLOOR(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_CEIL
+ } else if (strcmp(funcname->u.s,"CEIL") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_CEIL(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ROUND
+ } else if (strcmp(funcname->u.s,"ROUND") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ROUND(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_ROUND) */
+#ifdef FUNC_RINT
+ } else if (strcmp(funcname->u.s,"RINT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_RINT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TRUNC
+ } else if (strcmp(funcname->u.s,"TRUNC") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TRUNC(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_TRUNC) */
+#ifdef FUNC_EXP
+ } else if (strcmp(funcname->u.s,"EXP") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP2
+ } else if (strcmp(funcname->u.s,"EXP2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP10
+ } else if (strcmp(funcname->u.s,"EXP10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG
+ } else if (strcmp(funcname->u.s,"LOG") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG2
+ } else if (strcmp(funcname->u.s,"LOG2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG10
+ } else if (strcmp(funcname->u.s,"LOG10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_REMAINDER
+ } else if (strcmp(funcname->u.s,"REMAINDER") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_REMAINDER(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+ } else {
+ /* is this a custom function we should execute and collect the results of? */
+#ifndef STANDALONE
+ struct ast_custom_function *f = ast_custom_function_find(funcname->u.s);
+ if (!chan)
+ ast_log(LOG_WARNING,"Hey! chan is NULL.\n");
+ if (!f)
+ ast_log(LOG_WARNING,"Hey! could not find func %s.\n", funcname->u.s);
+
+ if (f && chan) {
+ if (f->read) {
+ char workspace[512];
+ char *argbuf = compose_func_args(arglist);
+ f->read(chan, funcname->u.s, argbuf, workspace, sizeof(workspace));
+ free(argbuf);
+ if (is_really_num(workspace))
+ return make_number(FUNC_STRTOD(workspace,(char **)NULL));
+ else
+ return make_str(workspace);
+ } else {
+ ast_log(LOG_ERROR,"Error! Function '%s' cannot be read!\n", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+
+ } else {
+ ast_log(LOG_ERROR,"Error! '%s' doesn't appear to be an available function!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+#else
+ ast_log(LOG_ERROR,"Error! '%s' is not available in the standalone version!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+#endif
+ }
+ }
+ else
+ {
+ ast_log(LOG_ERROR,"Error! '%s' is not possibly a function name!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+ return (make_number ((FP___TYPE)0.0));
+}
+
+
+static struct val *
+op_or (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a)) {
+ free_value (a);
+ return (b);
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_and (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a) || is_zero_or_null (b)) {
+ free_value (a);
+ free_value (b);
+ return (make_number ((FP___TYPE)0.0));
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_eq (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) == 0));
+ } else {
+#ifdef DEBUG_FOR_CONVERSIONS
+ char buffer[2000];
+ sprintf(buffer,"Converting '%s' and '%s' ", a->u.s, b->u.s);
+#endif
+ (void)to_number(a);
+ (void)to_number(b);
+#ifdef DEBUG_FOR_CONVERSIONS
+ ast_log(LOG_WARNING,"%s to '%lld' and '%lld'\n", buffer, a->u.i, b->u.i);
+#endif
+ r = make_number ((FP___TYPE)(a->u.i == b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_gt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) > 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i > b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_lt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) < 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i < b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_ge (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) >= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i >= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_le (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) <= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i <= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_cond (struct val *a, struct val *b, struct val *c)
+{
+ struct val *r;
+
+ if( isstring(a) )
+ {
+ if( strlen(a->u.s) && strcmp(a->u.s, "\"\"") != 0 && strcmp(a->u.s,"0") != 0 )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ else
+ {
+ (void)to_number(a);
+ if( a->u.i )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ return r;
+}
+
+static struct val *
+op_ne (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) != 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i != b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_plus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* sum of two positive numbers must be positive */
+ if (a > 0 && b > 0 && r <= 0)
+ return 1;
+ /* sum of two negative numbers must be negative */
+ if (a < 0 && b < 0 && r >= 0)
+ return 1;
+ /* all other cases are OK */
+ return 0;
+}
+
+static struct val *
+op_plus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING,"non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ free_value(a);
+ return (b);
+ }
+ } else if (!to_number(b)) {
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i + b->u.i);
+ if (chk_plus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING,"overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_minus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case subtraction of QUAD_MIN */
+ if (b == QUAD_MIN) {
+ if (a >= 0)
+ return 1;
+ else
+ return 0;
+ }
+ /* this is allowed for b != QUAD_MIN */
+ return chk_plus (a, -b, r);
+}
+
+static struct val *
+op_minus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ r = make_number(0 - b->u.i);
+ free_value(a);
+ free_value(b);
+ return (r);
+ }
+ } else if (!to_number(b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i - b->u.i);
+ if (chk_minus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_negate (struct val *a)
+{
+ struct val *r;
+
+ if (!to_number (a) ) {
+ free_value(a);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ }
+
+ r = make_number (- a->u.i);
+ if (chk_minus (0, a->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ return r;
+}
+
+static struct val *
+op_compl (struct val *a)
+{
+ int v1 = 1;
+ struct val *r;
+
+ if( !a )
+ {
+ v1 = 0;
+ }
+ else
+ {
+ switch( a->type )
+ {
+ case AST_EXPR_number:
+ if( a->u.i == 0 )
+ v1 = 0;
+ break;
+
+ case AST_EXPR_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+
+ case AST_EXPR_numeric_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+ }
+ }
+
+ r = make_number (!v1);
+ free_value (a);
+ return r;
+}
+
+static int
+chk_times (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case: first operand is 0, no overflow possible */
+ if (a == 0)
+ return 0;
+ /* cerify that result of division matches second operand */
+ if (r / a != b)
+ return 1;
+ return 0;
+}
+
+static struct val *
+op_times (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return(make_number(0));
+ }
+
+ r = make_number (a->u.i * b->u.i);
+ if (chk_times (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return (r);
+}
+
+static int
+chk_div (FP___TYPE a, FP___TYPE b)
+{
+ /* div by zero has been taken care of before */
+ /* only QUAD_MIN / -1 causes overflow */
+ if (a == QUAD_MIN && b == -1)
+ return 1;
+ /* everything else is OK */
+ return 0;
+}
+
+static struct val *
+op_div (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ } else if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(INT_MAX);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "division by zero\n");
+ free_value(a);
+ free_value(b);
+ return make_number(INT_MAX);
+ }
+
+ r = make_number (a->u.i / b->u.i);
+ if (chk_div (a->u.i, b->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_rem (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "div by zero\n");
+ free_value(a);
+ return(b);
+ }
+
+ r = make_number (FUNC_FMOD(a->u.i, b->u.i)); /* either fmod or fmodl if FP___TYPE is available */
+ /* chk_rem necessary ??? */
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+
+static struct val *
+op_colon (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
+
+
+static struct val *
+op_eqtilde (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 ) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0.0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
+
diff --git a/trunk/main/ast_expr2.fl b/trunk/main/ast_expr2.fl
new file mode 100644
index 000000000..723eebf5a
--- /dev/null
+++ b/trunk/main/ast_expr2.fl
@@ -0,0 +1,444 @@
+%{
+/*
+ * 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 Dialplan Expression Lexical Scanner
+ */
+
+#include "asterisk.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifndef STANDALONE
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#else
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___FMOD fmodl
+#define FP___STRTOD strtold
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___FMOD fmod
+#define FP___STRTOD strtod
+#define FP___TYPE double
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+/* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/strings.h"
+#include "asterisk/channel.h"
+#endif
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* long double or just double if it's a bad day */
+ } u;
+} ;
+
+#include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
+
+#define SET_COLUMNS do { \
+ yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf); \
+ yylloc_param->last_column += yyleng - 1; \
+ yylloc_param->first_line = yylloc_param->last_line = 1; \
+ } while (0)
+
+#define SET_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+#define SET_NUMERIC_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_numeric_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+void ast_yyset_column(int column_no, yyscan_t yyscanner);
+int ast_yyget_column(yyscan_t yyscanner);
+static int curlycount = 0;
+static char *expr2_token_subst(const char *mess);
+%}
+
+%option prefix="ast_yy"
+%option batch
+%option 8bit
+%option outfile="ast_expr2f.c"
+%option reentrant
+%option bison-bridge
+%option bison-locations
+%option noyywrap
+%option noyyfree
+%x var trail
+
+%%
+
+\| { SET_COLUMNS; SET_STRING; return TOK_OR;}
+\& { SET_COLUMNS; SET_STRING; return TOK_AND;}
+\= { SET_COLUMNS; SET_STRING; return TOK_EQ;}
+\|\| { SET_COLUMNS; SET_STRING; return TOK_OR;}
+\&\& { SET_COLUMNS; SET_STRING; return TOK_AND;}
+\=\= { SET_COLUMNS; SET_STRING; return TOK_EQ;}
+\=~ { SET_COLUMNS; SET_STRING; return TOK_EQTILDE;}
+\> { SET_COLUMNS; SET_STRING; return TOK_GT;}
+\< { SET_COLUMNS; SET_STRING; return TOK_LT;}
+\>\= { SET_COLUMNS; SET_STRING; return TOK_GE;}
+\<\= { SET_COLUMNS; SET_STRING; return TOK_LE;}
+\!\= { SET_COLUMNS; SET_STRING; return TOK_NE;}
+\+ { SET_COLUMNS; SET_STRING; return TOK_PLUS;}
+\, { SET_COLUMNS; SET_STRING; return TOK_COMMA;}
+\- { SET_COLUMNS; SET_STRING; return TOK_MINUS;}
+\* { SET_COLUMNS; SET_STRING; return TOK_MULT;}
+\/ { SET_COLUMNS; SET_STRING; return TOK_DIV;}
+\% { SET_COLUMNS; SET_STRING; return TOK_MOD;}
+\? { SET_COLUMNS; SET_STRING; return TOK_COND;}
+\! { SET_COLUMNS; SET_STRING; return TOK_COMPL;}
+\: { SET_COLUMNS; SET_STRING; return TOK_COLON;}
+\:\: { SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
+\( { SET_COLUMNS; SET_STRING; return TOK_LP;}
+\) { SET_COLUMNS; SET_STRING; return TOK_RP;}
+\$\{ {
+ /* gather the contents of ${} expressions, with trailing stuff,
+ * into a single TOKEN.
+ * They are much more complex now than they used to be
+ */
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+
+[ \t\r] {}
+\"[^"]*\" {SET_COLUMNS; SET_STRING; return TOKEN;}
+
+[\n] {/* what to do with eol */}
+[0-9]+(\.[0-9]+)? {
+ SET_COLUMNS;
+ /* the original behavior of the expression parser was
+ * to bring in numbers as a numeric string
+ */
+ SET_NUMERIC_STRING;
+ return TOKEN;
+ }
+
+([a-zA-Z0-9\.';\\_^$#@]|[\x80-\xff])+ {
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+
+
+<var>[^{}]*\} {
+ curlycount--;
+ if (curlycount < 0) {
+ BEGIN(trail);
+ yymore();
+ } else {
+ yymore();
+ }
+ }
+
+<var>[^{}]*\{ {
+ curlycount++;
+ yymore();
+ }
+
+
+<trail>[^-\t\r \n$():?%/+=*<>!|&]* {
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+
+<trail>[-\t\r \n$():?%/+=*<>!|&] {
+ char c = yytext[yyleng-1];
+ BEGIN(0);
+ unput(c);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+
+<trail>\$\{ {
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+
+<trail><<EOF>> {
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ /*actually, if an expr is only a variable ref, this could happen a LOT */
+ }
+
+%%
+
+/* I'm putting the interface routine to the whole parse here in the flexer input file
+ mainly because of all the flexer initialization that has to be done. Shouldn't matter
+ where it is, as long as it's somewhere. I didn't want to define a prototype for the
+ ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
+ UGH! that would be inappropriate. */
+
+int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
+int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
+
+void ast_yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr) /* the normal generated yyfree func just frees its first arg;
+ this get complaints on some systems, as sometimes this
+ arg is a nil ptr! It's usually not fatal, but is irritating! */
+ free( (char *) ptr );
+}
+
+int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
+{
+ struct parse_io io;
+ int return_value = 0;
+
+ memset(&io, 0, sizeof(io));
+ io.string = expr; /* to pass to the error routine */
+ io.chan = chan;
+
+ ast_yylex_init(&io.scanner);
+
+ ast_yy_scan_string(expr, io.scanner);
+
+ ast_yyparse ((void *) &io);
+
+ ast_yylex_destroy(io.scanner);
+
+ if (!io.val) {
+ if (length > 1) {
+ strcpy(buf, "0");
+ return_value = 1;
+ }
+ } else {
+ if (io.val->type == AST_EXPR_number) {
+ int res_length;
+
+ res_length = snprintf(buf, length, FP___PRINTF, io.val->u.i);
+ return_value = (res_length <= length) ? res_length : length;
+ } else {
+ if (io.val->u.s)
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(buf, io.val->u.s, length - 1);
+#else /* !STANDALONE && !LOW_MEMORY */
+ ast_copy_string(buf, io.val->u.s, length);
+#endif /* STANDALONE || LOW_MEMORY */
+ else
+ buf[0] = 0;
+ return_value = strlen(buf);
+ if (io.val->u.s)
+ free(io.val->u.s);
+ }
+ free(io.val);
+ }
+ return return_value;
+}
+
+
+char extra_error_message[4095];
+int extra_error_message_supplied = 0;
+void ast_expr_register_extra_error_info(char *message);
+void ast_expr_clear_extra_error_info(void);
+
+void ast_expr_register_extra_error_info(char *message)
+{
+ extra_error_message_supplied=1;
+ strcpy(extra_error_message, message);
+}
+
+void ast_expr_clear_extra_error_info(void)
+{
+ extra_error_message_supplied=0;
+ extra_error_message[0] = 0;
+}
+
+static char *expr2_token_equivs1[] =
+{
+ "TOKEN",
+ "TOK_COND",
+ "TOK_COLONCOLON",
+ "TOK_OR",
+ "TOK_AND",
+ "TOK_EQ",
+ "TOK_GT",
+ "TOK_LT",
+ "TOK_GE",
+ "TOK_LE",
+ "TOK_NE",
+ "TOK_PLUS",
+ "TOK_MINUS",
+ "TOK_MULT",
+ "TOK_DIV",
+ "TOK_MOD",
+ "TOK_COMPL",
+ "TOK_COLON",
+ "TOK_EQTILDE",
+ "TOK_COMMA",
+ "TOK_RP",
+ "TOK_LP"
+};
+
+static char *expr2_token_equivs2[] =
+{
+ "<token>",
+ "?",
+ "::",
+ "|",
+ "&",
+ "=",
+ ">",
+ "<",
+ ">=",
+ "<=",
+ "!=",
+ "+",
+ "-",
+ "*",
+ "/",
+ "%",
+ "!",
+ ":",
+ "=~",
+ ",",
+ ")",
+ "("
+};
+
+
+static char *expr2_token_subst(const char *mess)
+{
+ /* calc a length, malloc, fill, and return; yyerror had better free it! */
+ int len=0,i;
+ const char *p;
+ char *res, *s,*t;
+ int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
+
+ for (p=mess; *p; p++) {
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
+ {
+ len+=strlen(expr2_token_equivs2[i])+2;
+ p += strlen(expr2_token_equivs1[i])-1;
+ break;
+ }
+ }
+ len++;
+ }
+ res = (char*)malloc(len+1);
+ res[0] = 0;
+ s = res;
+ for (p=mess; *p;) {
+ int found = 0;
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
+ *s++ = '\'';
+ for (t=expr2_token_equivs2[i]; *t;) {
+ *s++ = *t++;
+ }
+ *s++ = '\'';
+ p += strlen(expr2_token_equivs1[i]);
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ *s++ = *p++;
+ }
+ *s++ = 0;
+ return res;
+}
+
+int ast_yyerror (const char *s, yyltype *loc, struct parse_io *parseio )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
+ char spacebuf[8000]; /* best safe than sorry */
+ char spacebuf2[8000]; /* best safe than sorry */
+ int i=0;
+ char *s2 = expr2_token_subst(s);
+ spacebuf[0] = 0;
+
+ for(i=0;i< (int)(yytext - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);i++) spacebuf2[i] = ' '; /* uh... assuming yyg is defined, then I can use the yycolumn macro,
+ which is the same thing as... get this:
+ yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
+ I was tempted to just use yy_buf_pos in the STATE, but..., well:
+ a. the yy_buf_pos is the current position in the buffer, which
+ may not relate to the entire string/buffer because of the
+ buffering.
+ b. but, analysis of the situation is that when you use the
+ yy_scan_string func, it creates a single buffer the size of
+ string, so the two would be the same...
+ so, in the end, the yycolumn macro is available, shorter, therefore easier. */
+ spacebuf2[i++]='^';
+ spacebuf2[i]= 0;
+
+#ifdef STANDALONE3
+ /* easier to read in the standalone version */
+ printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#else
+ ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#endif
+#ifndef STANDALONE
+ ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n");
+#endif
+ free(s2);
+ return(0);
+}
diff --git a/trunk/main/ast_expr2.h b/trunk/main/ast_expr2.h
new file mode 100644
index 000000000..cd2077149
--- /dev/null
+++ b/trunk/main/ast_expr2.h
@@ -0,0 +1,115 @@
+/* A Bison parser, made by GNU Bison 2.1a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ 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, 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ TOK_COMMA = 258,
+ TOK_COLONCOLON = 259,
+ TOK_COND = 260,
+ TOK_OR = 261,
+ TOK_AND = 262,
+ TOK_NE = 263,
+ TOK_LE = 264,
+ TOK_GE = 265,
+ TOK_LT = 266,
+ TOK_GT = 267,
+ TOK_EQ = 268,
+ TOK_MINUS = 269,
+ TOK_PLUS = 270,
+ TOK_MOD = 271,
+ TOK_DIV = 272,
+ TOK_MULT = 273,
+ TOK_COMPL = 274,
+ TOK_EQTILDE = 275,
+ TOK_COLON = 276,
+ TOK_LP = 277,
+ TOK_RP = 278,
+ TOKEN = 279
+ };
+#endif
+/* Tokens. */
+#define TOK_COMMA 258
+#define TOK_COLONCOLON 259
+#define TOK_COND 260
+#define TOK_OR 261
+#define TOK_AND 262
+#define TOK_NE 263
+#define TOK_LE 264
+#define TOK_GE 265
+#define TOK_LT 266
+#define TOK_GT 267
+#define TOK_EQ 268
+#define TOK_MINUS 269
+#define TOK_PLUS 270
+#define TOK_MOD 271
+#define TOK_DIV 272
+#define TOK_MULT 273
+#define TOK_COMPL 274
+#define TOK_EQTILDE 275
+#define TOK_COLON 276
+#define TOK_LP 277
+#define TOK_RP 278
+#define TOKEN 279
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 341 "ast_expr2.y"
+{
+ struct val *val;
+ struct expr_node *arglist;
+}
+/* Line 1536 of yacc.c. */
+#line 92 "ast_expr2.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+
diff --git a/trunk/main/ast_expr2.y b/trunk/main/ast_expr2.y
new file mode 100644
index 000000000..7eba6d165
--- /dev/null
+++ b/trunk/main/ast_expr2.y
@@ -0,0 +1,1619 @@
+%{
+/* Written by Pace Willisson (pace@blitz.com)
+ * and placed in the public domain.
+ *
+ * Largely rewritten by J.T. Conklin (jtc@wimsey.com)
+ *
+ * And then overhauled twice by Steve Murphy (murf@digium.com)
+ * to add double-quoted strings, allow mult. spaces, improve
+ * error messages, and then to fold in a flex scanner for the
+ * yylex operation.
+ *
+ * $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#ifdef STANDALONE
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___TYPE double
+#endif
+
+#ifdef HAVE_COSL
+#define FUNC_COS cosl
+#elif defined(HAVE_COS)
+#define FUNC_COS (long double)cos
+#endif
+
+#ifdef HAVE_SINL
+#define FUNC_SIN sinl
+#elif defined(HAVE_SIN)
+#define FUNC_SIN (long double)sin
+#endif
+
+#ifdef HAVE_TANL
+#define FUNC_TAN tanl
+#elif defined(HAVE_TAN)
+#define FUNC_TAN (long double)tan
+#endif
+
+#ifdef HAVE_ACOSL
+#define FUNC_ACOS acosl
+#elif defined(HAVE_ACOS)
+#define FUNC_ACOS (long double)acos
+#endif
+
+#ifdef HAVE_ASINL
+#define FUNC_ASIN asinl
+#elif defined(HAVE_ASIN)
+#define FUNC_ASIN (long double)asin
+#endif
+
+#ifdef HAVE_ATANL
+#define FUNC_ATAN atanl
+#elif defined(HAVE_ATAN)
+#define FUNC_ATAN (long double)atan
+#endif
+
+#ifdef HAVE_ATAN2L
+#define FUNC_ATAN2 atan2l
+#elif defined(HAVE_ATAN2)
+#define FUNC_ATAN2 (long double)atan2
+#endif
+
+#ifdef HAVE_POWL
+#define FUNC_POW powl
+#elif defined(HAVE_POW)
+#define FUNC_POW (long double)pow
+#endif
+
+#ifdef HAVE_SQRTL
+#define FUNC_SQRT sqrtl
+#elif defined(HAVE_SQRT)
+#define FUNC_SQRT (long double)sqrt
+#endif
+
+#ifdef HAVE_RINTL
+#define FUNC_RINT rintl
+#elif defined(HAVE_RINT)
+#define FUNC_RINT (long double)rint
+#endif
+
+#ifdef HAVE_EXPL
+#define FUNC_EXP expl
+#elif defined(HAVE_EXP)
+#define FUNC_EXP (long double)exp
+#endif
+
+#ifdef HAVE_LOGL
+#define FUNC_LOG logl
+#elif defined(HAVE_LOG)
+#define FUNC_LOG (long double)log
+#endif
+
+#ifdef HAVE_REMINDERL
+#define FUNC_REMINDER reminderl
+#elif defined(HAVE_REMINDER)
+#define FUNC_REMINDER (long double)reminder
+#endif
+
+#ifdef HAVE_FMODL
+#define FUNC_FMOD fmodl
+#elif defined(HAVE_FMOD)
+#define FUNC_FMOD (long double)fmod
+#endif
+
+#ifdef HAVE_STRTOLD
+#define FUNC_STRTOD strtold
+#elif defined(HAVE_STRTOD)
+#define FUNC_STRTOD (long double)strtod
+#endif
+
+#ifdef HAVE_FLOORL
+#define FUNC_FLOOR floorl
+#elif defined(HAVE_FLOOR)
+#define FUNC_FLOOR (long double)floor
+#endif
+
+#ifdef HAVE_CEILL
+#define FUNC_CEIL ceill
+#elif defined(HAVE_CEIL)
+#define FUNC_CEIL (long double)ceil
+#endif
+
+#ifdef HAVE_ROUNDL
+#define FUNC_ROUND roundl
+#elif defined(HAVE_ROUND)
+#define FUNC_ROUND (long double)round
+#endif
+
+#ifdef HAVE_TRUNCL
+#define FUNC_TRUNC truncl
+#elif defined(HAVE_TRUNC)
+#define FUNC_TRUNC (long double)trunc
+#endif
+
+/*! \note
+ * Oddly enough, some platforms have some ISO C99 functions, but not others, so
+ * we define the missing functions in terms of their mathematical identities.
+ */
+#ifdef HAVE_EXP2L
+#define FUNC_EXP2 exp2l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP2(x) expl((x) * logl(2.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP2(x) (long double)exp((x) * log(2.0))
+#endif
+
+#ifdef HAVE_EXP10L
+#define FUNC_EXP10 exp10l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP10(x) expl((x) * logl(10.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP10(x) (long double)exp((x) * log(10.0))
+#endif
+
+#ifdef HAVE_LOG2L
+#define FUNC_LOG2 log2l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG2(x) (logl(x) / logl(2.0))
+#elif defined(HAVE_LOG10L)
+#define FUNC_LOG2(x) (log10l(x) / log10l(2.0))
+#elif defined(HAVE_LOG2)
+#define FUNC_LOG2 (long double)log2
+#elif defined(HAVE_LOG)
+#define FUNC_LOG2(x) ((long double)log(x) / log(2.0))
+#endif
+
+#ifdef HAVE_LOG10L
+#define FUNC_LOG10 log10l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG10(x) (logl(x) / logl(10.0))
+#elif defined(HAVE_LOG2L)
+#define FUNC_LOG10(x) (log2l(x) / log2l(10.0))
+#elif defined(HAVE_LOG10)
+#define FUNC_LOG10(x) (long double)log10(x)
+#elif defined(HAVE_LOG)
+#define FUNC_LOG10(x) ((long double)log(x) / log(10.0))
+#endif
+
+
+#include <stdlib.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <string.h>
+#include <math.h>
+#include <locale.h>
+#include <unistd.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+ /* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/pbx.h"
+#endif
+
+#if defined(LONG_LONG_MIN) && !defined(QUAD_MIN)
+#define QUAD_MIN LONG_LONG_MIN
+#endif
+#if defined(LONG_LONG_MAX) && !defined(QUAD_MAX)
+#define QUAD_MAX LONG_LONG_MAX
+#endif
+
+# if ! defined(QUAD_MIN)
+# define QUAD_MIN (-0x7fffffffffffffffLL-1)
+# endif
+# if ! defined(QUAD_MAX)
+# define QUAD_MAX (0x7fffffffffffffffLL)
+# endif
+#define YYENABLE_NLS 0
+#define YYPARSE_PARAM parseio
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+extern char extra_error_message[4095];
+extern int extra_error_message_supplied;
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+#ifdef STANDALONE
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+#endif
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* either long double, or just double, on a bad day */
+ } u;
+} ;
+
+enum node_type {
+ AST_EXPR_NODE_COMMA, AST_EXPR_NODE_STRING, AST_EXPR_NODE_VAL
+} ;
+
+struct expr_node
+{
+ enum node_type type;
+ struct val *val;
+ struct expr_node *left;
+ struct expr_node *right;
+};
+
+
+typedef void *yyscan_t;
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+static int chk_div __P((FP___TYPE, FP___TYPE));
+static int chk_minus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_plus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_times __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static void free_value __P((struct val *));
+static int is_zero_or_null __P((struct val *));
+static int isstring __P((struct val *));
+static struct val *make_number __P((FP___TYPE));
+static struct val *make_str __P((const char *));
+static struct val *op_and __P((struct val *, struct val *));
+static struct val *op_colon __P((struct val *, struct val *));
+static struct val *op_eqtilde __P((struct val *, struct val *));
+static struct val *op_div __P((struct val *, struct val *));
+static struct val *op_eq __P((struct val *, struct val *));
+static struct val *op_ge __P((struct val *, struct val *));
+static struct val *op_gt __P((struct val *, struct val *));
+static struct val *op_le __P((struct val *, struct val *));
+static struct val *op_lt __P((struct val *, struct val *));
+static struct val *op_cond __P((struct val *, struct val *, struct val *));
+static struct val *op_minus __P((struct val *, struct val *));
+static struct val *op_negate __P((struct val *));
+static struct val *op_compl __P((struct val *));
+static struct val *op_ne __P((struct val *, struct val *));
+static struct val *op_or __P((struct val *, struct val *));
+static struct val *op_plus __P((struct val *, struct val *));
+static struct val *op_rem __P((struct val *, struct val *));
+static struct val *op_times __P((struct val *, struct val *));
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan);
+static int to_number __P((struct val *));
+static void to_string __P((struct val *));
+static struct expr_node *alloc_expr_node(enum node_type);
+static void destroy_arglist(struct expr_node *arglist);
+static int is_really_num(char *str);
+
+/* uh, if I want to predeclare yylex with a YYLTYPE, I have to predeclare the yyltype... sigh */
+typedef struct yyltype
+{
+ int first_line;
+ int first_column;
+
+ int last_line;
+ int last_column;
+} yyltype;
+
+# define YYLTYPE yyltype
+# define YYLTYPE_IS_TRIVIAL 1
+
+/* we will get warning about no prototype for yylex! But we can't
+ define it here, we have no definition yet for YYSTYPE. */
+
+int ast_yyerror(const char *,YYLTYPE *, struct parse_io *);
+
+/* I wanted to add args to the yyerror routine, so I could print out
+ some useful info about the error. Not as easy as it looks, but it
+ is possible. */
+#define ast_yyerror(x) ast_yyerror(x,&yyloc,parseio)
+#define DESTROY(x) {if((x)->type == AST_EXPR_numeric_string || (x)->type == AST_EXPR_string) free((x)->u.s); (x)->u.s = 0; free(x);}
+%}
+
+%pure-parser
+%locations
+/* %debug for when you are having big problems */
+
+/* %name-prefix="ast_yy" */
+
+%union
+{
+ struct val *val;
+ struct expr_node *arglist;
+}
+
+%{
+extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
+%}
+%left <val> TOK_COMMA
+%left <val> TOK_COND TOK_COLONCOLON
+%left <val> TOK_OR
+%left <val> TOK_AND
+%left <val> TOK_EQ TOK_GT TOK_LT TOK_GE TOK_LE TOK_NE
+%left <val> TOK_PLUS TOK_MINUS
+%left <val> TOK_MULT TOK_DIV TOK_MOD
+%right <val> TOK_COMPL
+%left <val> TOK_COLON TOK_EQTILDE
+%left <val> TOK_RP TOK_LP
+
+
+%token <val> TOKEN
+%type <arglist> arglist
+%type <val> start expr
+
+
+%destructor { free_value($$); } expr TOKEN TOK_COND TOK_COLONCOLON TOK_OR TOK_AND TOK_EQ
+ TOK_GT TOK_LT TOK_GE TOK_LE TOK_NE TOK_PLUS TOK_MINUS TOK_MULT TOK_DIV TOK_MOD TOK_COMPL TOK_COLON TOK_EQTILDE
+ TOK_RP TOK_LP
+
+%%
+
+start: expr { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = $1->type;
+ if( $1->type == AST_EXPR_number )
+ ((struct parse_io *)parseio)->val->u.i = $1->u.i;
+ else
+ ((struct parse_io *)parseio)->val->u.s = $1->u.s;
+ free($1);
+ }
+ | {/* nothing */ ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = AST_EXPR_string;
+ ((struct parse_io *)parseio)->val->u.s = strdup("");
+ }
+
+ ;
+
+arglist: expr { $$ = alloc_expr_node(AST_EXPR_NODE_VAL); $$->val = $1;}
+ | arglist TOK_COMMA expr %prec TOK_RP{struct expr_node *x = alloc_expr_node(AST_EXPR_NODE_VAL);
+ struct expr_node *t;
+ DESTROY($2);
+ for (t=$1;t->right;t=t->right)
+ ;
+ $$ = $1; t->right = x; x->val = $3;}
+ ;
+
+expr:
+ TOKEN TOK_LP arglist TOK_RP { $$ = op_func($1,$3, ((struct parse_io *)parseio)->chan);
+ DESTROY($2);
+ DESTROY($4);
+ DESTROY($1);
+ destroy_arglist($3);
+ }
+ | TOKEN {$$ = $1;}
+ | TOK_LP expr TOK_RP { $$ = $2;
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;
+ DESTROY($1); DESTROY($3); }
+ | expr TOK_OR expr { $$ = op_or ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_AND expr { $$ = op_and ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_EQ expr { $$ = op_eq ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_GT expr { $$ = op_gt ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_LT expr { $$ = op_lt ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_GE expr { $$ = op_ge ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_LE expr { $$ = op_le ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_NE expr { $$ = op_ne ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_PLUS expr { $$ = op_plus ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_MINUS expr { $$ = op_minus ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | TOK_MINUS expr %prec TOK_COMPL { $$ = op_negate ($2);
+ DESTROY($1);
+ @$.first_column = @1.first_column; @$.last_column = @2.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | TOK_COMPL expr { $$ = op_compl ($2);
+ DESTROY($1);
+ @$.first_column = @1.first_column; @$.last_column = @2.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_MULT expr { $$ = op_times ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_DIV expr { $$ = op_div ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_MOD expr { $$ = op_rem ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_COLON expr { $$ = op_colon ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_EQTILDE expr { $$ = op_eqtilde ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_COND expr TOK_COLONCOLON expr { $$ = op_cond ($1, $3, $5);
+ DESTROY($2);
+ DESTROY($4);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ ;
+
+%%
+
+static struct expr_node *alloc_expr_node(enum node_type nt)
+{
+ struct expr_node *x = calloc(1,sizeof(struct expr_node));
+ if (!x) {
+ ast_log(LOG_ERROR, "Allocation for expr_node FAILED!!\n");
+ return 0;
+ }
+ x->type = nt;
+ return x;
+}
+
+
+
+static struct val *
+make_number (FP___TYPE i)
+{
+ struct val *vp;
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ return(NULL);
+ }
+
+ vp->type = AST_EXPR_number;
+ vp->u.i = i;
+ return vp;
+}
+
+static struct val *
+make_str (const char *s)
+{
+ struct val *vp;
+ size_t i;
+ int isint; /* this started out being a test for an integer, but then ended up being a test for a float */
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return(NULL);
+ }
+
+ for (i = 0, isint = (isdigit(s[0]) || s[0] == '-' || s[0]=='.'); isint && i < strlen(s); i++)
+ {
+ if (!isdigit(s[i]) && s[i] != '.') {
+ isint = 0;
+ break;
+ }
+ }
+ if (isint)
+ vp->type = AST_EXPR_numeric_string;
+ else
+ vp->type = AST_EXPR_string;
+
+ return vp;
+}
+
+
+static void
+free_value (struct val *vp)
+{
+ if (vp==NULL) {
+ return;
+ }
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ free (vp->u.s);
+ free(vp);
+}
+
+
+static int
+to_number (struct val *vp)
+{
+ FP___TYPE i;
+
+ if (vp == NULL) {
+ ast_log(LOG_WARNING,"vp==NULL in to_number()\n");
+ return(0);
+ }
+
+ if (vp->type == AST_EXPR_number)
+ return 1;
+
+ if (vp->type == AST_EXPR_string)
+ return 0;
+
+ /* vp->type == AST_EXPR_numeric_string, make it numeric */
+ errno = 0;
+ i = FUNC_STRTOD(vp->u.s, (char**)0); /* either strtod, or strtold on a good day */
+ if (errno != 0) {
+ ast_log(LOG_WARNING,"Conversion of %s to number under/overflowed!\n", vp->u.s);
+ free(vp->u.s);
+ vp->u.s = 0;
+ return(0);
+ }
+ free (vp->u.s);
+ vp->u.i = i;
+ vp->type = AST_EXPR_number;
+ return 1;
+}
+
+static void
+strip_quotes(struct val *vp)
+{
+ if (vp->type != AST_EXPR_string && vp->type != AST_EXPR_numeric_string)
+ return;
+
+ if( vp->u.s[0] == '"' && vp->u.s[strlen(vp->u.s)-1] == '"' )
+ {
+ char *f, *t;
+ f = vp->u.s;
+ t = vp->u.s;
+
+ while( *f )
+ {
+ if( *f && *f != '"' )
+ *t++ = *f++;
+ else
+ f++;
+ }
+ *t = *f;
+ }
+}
+
+static void
+to_string (struct val *vp)
+{
+ char *tmp;
+
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ return;
+
+ tmp = malloc ((size_t)25);
+ if (tmp == NULL) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return;
+ }
+
+ sprintf(tmp, FP___PRINTF, vp->u.i);
+ vp->type = AST_EXPR_string;
+ vp->u.s = tmp;
+}
+
+
+static int
+isstring (struct val *vp)
+{
+ /* only TRUE if this string is not a valid number */
+ return (vp->type == AST_EXPR_string);
+}
+
+
+static int
+is_zero_or_null (struct val *vp)
+{
+ if (vp->type == AST_EXPR_number) {
+ return (vp->u.i == 0);
+ } else {
+ return (*vp->u.s == 0 || (to_number(vp) && vp->u.i == 0));
+ }
+ /* NOTREACHED */
+}
+
+#ifdef STANDALONE
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+
+int main(int argc,char **argv) {
+ char s[4096];
+ char out[4096];
+ FILE *infile;
+
+ if( !argv[1] )
+ exit(20);
+
+ if( access(argv[1],F_OK)== 0 )
+ {
+ int ret;
+
+ infile = fopen(argv[1],"r");
+ if( !infile )
+ {
+ printf("Sorry, couldn't open %s for reading!\n", argv[1]);
+ exit(10);
+ }
+ while( fgets(s,sizeof(s),infile) )
+ {
+ if( s[strlen(s)-1] == '\n' )
+ s[strlen(s)-1] = 0;
+
+ ret = ast_expr(s, out, sizeof(out),NULL);
+ printf("Expression: %s Result: [%d] '%s'\n",
+ s, ret, out);
+ }
+ fclose(infile);
+ }
+ else
+ {
+ if (ast_expr(argv[1], s, sizeof(s), NULL))
+ printf("=====%s======\n",s);
+ else
+ printf("No result\n");
+ }
+}
+
+#endif
+
+#undef ast_yyerror
+#define ast_yyerror(x) ast_yyerror(x, YYLTYPE *yylloc, struct parse_io *parseio)
+
+/* I put the ast_yyerror func in the flex input file,
+ because it refers to the buffer state. Best to
+ let it access the BUFFER stuff there and not trying
+ define all the structs, macros etc. in this file! */
+
+static void destroy_arglist(struct expr_node *arglist)
+{
+ struct expr_node *arglist_next;
+
+ while (arglist)
+ {
+ arglist_next = arglist->right;
+ if (arglist->val)
+ free_value(arglist->val);
+ arglist->val = 0;
+ arglist->right = 0;
+ free(arglist);
+ arglist = arglist_next;
+ }
+}
+
+static char *compose_func_args(struct expr_node *arglist)
+{
+ struct expr_node *t = arglist;
+ char *argbuf;
+ int total_len = 0;
+
+ while (t) {
+ if (t != arglist)
+ total_len += 1; /* for the sep */
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number)
+ total_len += 25; /* worst case */
+ else
+ total_len += strlen(t->val->u.s);
+ }
+
+ t = t->right;
+ }
+ total_len++; /* for the null */
+ ast_log(LOG_NOTICE,"argbuf allocated %d bytes;\n", total_len);
+ argbuf = malloc(total_len);
+ argbuf[0] = 0;
+ t = arglist;
+ while (t) {
+ char numbuf[30];
+
+ if (t != arglist)
+ strcat(argbuf,"|");
+
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number) {
+ sprintf(numbuf,FP___PRINTF,t->val->u.i);
+ strcat(argbuf,numbuf);
+ } else
+ strcat(argbuf,t->val->u.s);
+ }
+ t = t->right;
+ }
+ ast_log(LOG_NOTICE,"argbuf uses %d bytes;\n", (int) strlen(argbuf));
+ return argbuf;
+}
+
+static int is_really_num(char *str)
+{
+ if ( strspn(str,"-0123456789. ") == strlen(str))
+ return 1;
+ else
+ return 0;
+}
+
+
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan)
+{
+ if (strspn(funcname->u.s,"ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") == strlen(funcname->u.s))
+ {
+ struct val *result;
+ if (0) {
+#ifdef FUNC_COS
+ } else if (strcmp(funcname->u.s,"COS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_COS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SIN
+ } else if (strcmp(funcname->u.s,"SIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TAN
+ } else if (strcmp(funcname->u.s,"TAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ACOS
+ } else if (strcmp(funcname->u.s,"ACOS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ACOS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ASIN
+ } else if (strcmp(funcname->u.s,"ASIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ASIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN
+ } else if (strcmp(funcname->u.s,"ATAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ATAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN2
+ } else if (strcmp(funcname->u.s,"ATAN2") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_ATAN2(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_POW
+ } else if (strcmp(funcname->u.s,"POW") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_POW(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SQRT
+ } else if (strcmp(funcname->u.s,"SQRT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SQRT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_FLOOR
+ } else if (strcmp(funcname->u.s,"FLOOR") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_FLOOR(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_CEIL
+ } else if (strcmp(funcname->u.s,"CEIL") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_CEIL(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ROUND
+ } else if (strcmp(funcname->u.s,"ROUND") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ROUND(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_ROUND) */
+#ifdef FUNC_RINT
+ } else if (strcmp(funcname->u.s,"RINT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_RINT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TRUNC
+ } else if (strcmp(funcname->u.s,"TRUNC") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TRUNC(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_TRUNC) */
+#ifdef FUNC_EXP
+ } else if (strcmp(funcname->u.s,"EXP") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP2
+ } else if (strcmp(funcname->u.s,"EXP2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP10
+ } else if (strcmp(funcname->u.s,"EXP10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG
+ } else if (strcmp(funcname->u.s,"LOG") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG2
+ } else if (strcmp(funcname->u.s,"LOG2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG10
+ } else if (strcmp(funcname->u.s,"LOG10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_REMAINDER
+ } else if (strcmp(funcname->u.s,"REMAINDER") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_REMAINDER(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+ } else {
+ /* is this a custom function we should execute and collect the results of? */
+#ifndef STANDALONE
+ struct ast_custom_function *f = ast_custom_function_find(funcname->u.s);
+ if (!chan)
+ ast_log(LOG_WARNING,"Hey! chan is NULL.\n");
+ if (!f)
+ ast_log(LOG_WARNING,"Hey! could not find func %s.\n", funcname->u.s);
+
+ if (f && chan) {
+ if (f->read) {
+ char workspace[512];
+ char *argbuf = compose_func_args(arglist);
+ f->read(chan, funcname->u.s, argbuf, workspace, sizeof(workspace));
+ free(argbuf);
+ if (is_really_num(workspace))
+ return make_number(FUNC_STRTOD(workspace,(char **)NULL));
+ else
+ return make_str(workspace);
+ } else {
+ ast_log(LOG_ERROR,"Error! Function '%s' cannot be read!\n", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+
+ } else {
+ ast_log(LOG_ERROR,"Error! '%s' doesn't appear to be an available function!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+#else
+ ast_log(LOG_ERROR,"Error! '%s' is not available in the standalone version!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+#endif
+ }
+ }
+ else
+ {
+ ast_log(LOG_ERROR,"Error! '%s' is not possibly a function name!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+ return (make_number ((FP___TYPE)0.0));
+}
+
+
+static struct val *
+op_or (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a)) {
+ free_value (a);
+ return (b);
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_and (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a) || is_zero_or_null (b)) {
+ free_value (a);
+ free_value (b);
+ return (make_number ((FP___TYPE)0.0));
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_eq (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) == 0));
+ } else {
+#ifdef DEBUG_FOR_CONVERSIONS
+ char buffer[2000];
+ sprintf(buffer,"Converting '%s' and '%s' ", a->u.s, b->u.s);
+#endif
+ (void)to_number(a);
+ (void)to_number(b);
+#ifdef DEBUG_FOR_CONVERSIONS
+ ast_log(LOG_WARNING,"%s to '%lld' and '%lld'\n", buffer, a->u.i, b->u.i);
+#endif
+ r = make_number ((FP___TYPE)(a->u.i == b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_gt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) > 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i > b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_lt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) < 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i < b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_ge (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) >= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i >= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_le (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) <= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i <= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_cond (struct val *a, struct val *b, struct val *c)
+{
+ struct val *r;
+
+ if( isstring(a) )
+ {
+ if( strlen(a->u.s) && strcmp(a->u.s, "\"\"") != 0 && strcmp(a->u.s,"0") != 0 )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ else
+ {
+ (void)to_number(a);
+ if( a->u.i )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ return r;
+}
+
+static struct val *
+op_ne (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) != 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i != b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_plus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* sum of two positive numbers must be positive */
+ if (a > 0 && b > 0 && r <= 0)
+ return 1;
+ /* sum of two negative numbers must be negative */
+ if (a < 0 && b < 0 && r >= 0)
+ return 1;
+ /* all other cases are OK */
+ return 0;
+}
+
+static struct val *
+op_plus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING,"non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ free_value(a);
+ return (b);
+ }
+ } else if (!to_number(b)) {
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i + b->u.i);
+ if (chk_plus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING,"overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_minus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case subtraction of QUAD_MIN */
+ if (b == QUAD_MIN) {
+ if (a >= 0)
+ return 1;
+ else
+ return 0;
+ }
+ /* this is allowed for b != QUAD_MIN */
+ return chk_plus (a, -b, r);
+}
+
+static struct val *
+op_minus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ r = make_number(0 - b->u.i);
+ free_value(a);
+ free_value(b);
+ return (r);
+ }
+ } else if (!to_number(b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i - b->u.i);
+ if (chk_minus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_negate (struct val *a)
+{
+ struct val *r;
+
+ if (!to_number (a) ) {
+ free_value(a);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ }
+
+ r = make_number (- a->u.i);
+ if (chk_minus (0, a->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ return r;
+}
+
+static struct val *
+op_compl (struct val *a)
+{
+ int v1 = 1;
+ struct val *r;
+
+ if( !a )
+ {
+ v1 = 0;
+ }
+ else
+ {
+ switch( a->type )
+ {
+ case AST_EXPR_number:
+ if( a->u.i == 0 )
+ v1 = 0;
+ break;
+
+ case AST_EXPR_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+
+ case AST_EXPR_numeric_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+ }
+ }
+
+ r = make_number (!v1);
+ free_value (a);
+ return r;
+}
+
+static int
+chk_times (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case: first operand is 0, no overflow possible */
+ if (a == 0)
+ return 0;
+ /* cerify that result of division matches second operand */
+ if (r / a != b)
+ return 1;
+ return 0;
+}
+
+static struct val *
+op_times (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return(make_number(0));
+ }
+
+ r = make_number (a->u.i * b->u.i);
+ if (chk_times (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return (r);
+}
+
+static int
+chk_div (FP___TYPE a, FP___TYPE b)
+{
+ /* div by zero has been taken care of before */
+ /* only QUAD_MIN / -1 causes overflow */
+ if (a == QUAD_MIN && b == -1)
+ return 1;
+ /* everything else is OK */
+ return 0;
+}
+
+static struct val *
+op_div (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ } else if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(INT_MAX);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "division by zero\n");
+ free_value(a);
+ free_value(b);
+ return make_number(INT_MAX);
+ }
+
+ r = make_number (a->u.i / b->u.i);
+ if (chk_div (a->u.i, b->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_rem (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "div by zero\n");
+ free_value(a);
+ return(b);
+ }
+
+ r = make_number (FUNC_FMOD(a->u.i, b->u.i)); /* either fmod or fmodl if FP___TYPE is available */
+ /* chk_rem necessary ??? */
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+
+static struct val *
+op_colon (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
+
+
+static struct val *
+op_eqtilde (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 ) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0.0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
diff --git a/trunk/main/ast_expr2f.c b/trunk/main/ast_expr2f.c
new file mode 100644
index 000000000..770b8d1a5
--- /dev/null
+++ b/trunk/main/ast_expr2f.c
@@ -0,0 +1,2515 @@
+#line 2 "ast_expr2f.c"
+
+#line 4 "ast_expr2f.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 33
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+int ast_yylex_init (yyscan_t* scanner);
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE ast_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via ast_yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void ast_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void ast_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE ast_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void ast_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ast_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ast_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void ast_yypop_buffer_state (yyscan_t yyscanner );
+
+static void ast_yyensure_buffer_stack (yyscan_t yyscanner );
+static void ast_yy_load_buffer_state (yyscan_t yyscanner );
+static void ast_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER ast_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE ast_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE ast_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE ast_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *ast_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *ast_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void ast_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer ast_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ ast_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ ast_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define ast_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyg->yytext_ptr -= yyg->yy_more_len; \
+ yyleng = (size_t) (yy_cp - yyg->yytext_ptr); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 36
+#define YY_END_OF_BUFFER 37
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[58] =
+ { 0,
+ 0, 0, 0, 0, 33, 33, 37, 36, 26, 28,
+ 20, 36, 30, 30, 18, 2, 23, 24, 16, 13,
+ 14, 15, 17, 29, 21, 9, 3, 8, 19, 1,
+ 36, 32, 31, 33, 34, 34, 12, 0, 27, 30,
+ 25, 5, 30, 29, 22, 11, 6, 7, 10, 4,
+ 0, 32, 31, 33, 35, 29, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 7, 8, 9, 6, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 19, 6, 20,
+ 21, 22, 23, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 1, 6, 1, 6, 6, 1, 6, 6, 6, 6,
+
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 24, 25, 26, 27, 1, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28
+ } ;
+
+static yyconst flex_int32_t yy_meta[29] =
+ { 0,
+ 1, 2, 2, 2, 1, 3, 4, 2, 2, 2,
+ 2, 2, 2, 1, 2, 3, 2, 3, 2, 2,
+ 2, 2, 2, 1, 2, 1, 1, 3
+ } ;
+
+static yyconst flex_int16_t yy_base[64] =
+ { 0,
+ 0, 0, 5, 6, 32, 60, 64, 110, 110, 110,
+ 42, 57, 0, 33, 110, 46, 110, 110, 110, 110,
+ 110, 110, 110, 18, 35, 32, 14, 31, 110, 26,
+ 16, 110, 110, 0, 110, 25, 110, 42, 110, 0,
+ 110, 110, 26, 0, 110, 110, 110, 110, 110, 110,
+ 19, 110, 110, 0, 110, 0, 110, 88, 92, 96,
+ 98, 102, 106
+ } ;
+
+static yyconst flex_int16_t yy_def[64] =
+ { 0,
+ 57, 1, 58, 58, 59, 59, 57, 57, 57, 57,
+ 57, 60, 61, 61, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 61, 57, 57, 57, 57, 57, 57,
+ 62, 57, 57, 63, 57, 57, 57, 60, 57, 61,
+ 57, 57, 61, 24, 57, 57, 57, 57, 57, 57,
+ 62, 57, 57, 63, 57, 43, 0, 57, 57, 57,
+ 57, 57, 57
+ } ;
+
+static yyconst flex_int16_t yy_nxt[139] =
+ { 0,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 13, 23, 24, 25, 26,
+ 27, 28, 29, 8, 30, 8, 8, 13, 32, 32,
+ 33, 33, 34, 43, 47, 44, 34, 34, 36, 52,
+ 48, 53, 52, 56, 53, 34, 39, 34, 55, 34,
+ 50, 49, 46, 45, 42, 34, 41, 34, 34, 34,
+ 34, 39, 37, 57, 34, 34, 36, 57, 57, 57,
+ 57, 57, 57, 34, 57, 34, 57, 34, 57, 57,
+ 57, 57, 57, 34, 57, 34, 34, 34, 31, 31,
+ 31, 31, 35, 35, 35, 35, 38, 38, 38, 38,
+
+ 40, 40, 51, 51, 51, 51, 54, 57, 54, 7,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57
+ } ;
+
+static yyconst flex_int16_t yy_chk[139] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 4,
+ 3, 4, 5, 24, 27, 24, 5, 5, 5, 31,
+ 27, 31, 51, 43, 51, 5, 38, 5, 36, 5,
+ 30, 28, 26, 25, 16, 5, 14, 5, 5, 5,
+ 6, 12, 11, 7, 6, 6, 6, 0, 0, 0,
+ 0, 0, 0, 6, 0, 6, 0, 6, 0, 0,
+ 0, 0, 0, 6, 0, 6, 6, 6, 58, 58,
+ 58, 58, 59, 59, 59, 59, 60, 60, 60, 60,
+
+ 61, 61, 62, 62, 62, 62, 63, 0, 63, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() (yyg->yy_more_flag = 1)
+#define YY_MORE_ADJ yyg->yy_more_len
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "ast_expr2.fl"
+#line 2 "ast_expr2.fl"
+/*
+ * 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 Dialplan Expression Lexical Scanner
+ */
+
+#include "asterisk.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifndef STANDALONE
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#else
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___FMOD fmodl
+#define FP___STRTOD strtold
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___FMOD fmod
+#define FP___STRTOD strtod
+#define FP___TYPE double
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+/* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/strings.h"
+#include "asterisk/channel.h"
+#endif
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* long double or just double if it's a bad day */
+ } u;
+} ;
+
+#include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
+
+#define SET_COLUMNS do { \
+ yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf); \
+ yylloc_param->last_column += yyleng - 1; \
+ yylloc_param->first_line = yylloc_param->last_line = 1; \
+ } while (0)
+
+#define SET_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+#define SET_NUMERIC_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_numeric_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+void ast_yyset_column(int column_no, yyscan_t yyscanner);
+int ast_yyget_column(yyscan_t yyscanner);
+static int curlycount = 0;
+static char *expr2_token_subst(const char *mess);
+
+#line 600 "ast_expr2f.c"
+
+#define INITIAL 0
+#define var 1
+#define trail 2
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ YYLTYPE * yylloc_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+ # define yylloc yyg->yylloc_r
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int ast_yylex_destroy (yyscan_t yyscanner );
+
+int ast_yyget_debug (yyscan_t yyscanner );
+
+void ast_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE ast_yyget_extra (yyscan_t yyscanner );
+
+void ast_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *ast_yyget_in (yyscan_t yyscanner );
+
+void ast_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *ast_yyget_out (yyscan_t yyscanner );
+
+void ast_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int ast_yyget_leng (yyscan_t yyscanner );
+
+char *ast_yyget_text (yyscan_t yyscanner );
+
+int ast_yyget_lineno (yyscan_t yyscanner );
+
+void ast_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * ast_yyget_lval (yyscan_t yyscanner );
+
+void ast_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+ YYLTYPE *ast_yyget_lloc (yyscan_t yyscanner );
+
+ void ast_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int ast_yywrap (yyscan_t yyscanner );
+#else
+extern int ast_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int ast_yylex (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int ast_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 127 "ast_expr2.fl"
+
+
+#line 840 "ast_expr2f.c"
+
+ yylval = yylval_param;
+
+ yylloc = yylloc_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ ast_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ast_yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yyg->yy_more_len = 0;
+ if ( yyg->yy_more_flag )
+ {
+ yyg->yy_more_len = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ yyg->yy_more_flag = 0;
+ }
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 58 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 57 );
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 129 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_OR;}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 130 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_AND;}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 131 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQ;}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 132 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_OR;}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 133 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_AND;}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 134 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQ;}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 135 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQTILDE;}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 136 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_GT;}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 137 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LT;}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 138 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_GE;}
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 139 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LE;}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 140 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_NE;}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 141 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_PLUS;}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 142 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COMMA;}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 143 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MINUS;}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 144 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MULT;}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 145 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_DIV;}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 146 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MOD;}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 147 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COND;}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 148 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COMPL;}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 149 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COLON;}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 150 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 151 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LP;}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 152 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_RP;}
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 153 "ast_expr2.fl"
+{
+ /* gather the contents of ${} expressions, with trailing stuff,
+ * into a single TOKEN.
+ * They are much more complex now than they used to be
+ */
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 163 "ast_expr2.fl"
+{}
+ YY_BREAK
+case 27:
+/* rule 27 can match eol */
+YY_RULE_SETUP
+#line 164 "ast_expr2.fl"
+{SET_COLUMNS; SET_STRING; return TOKEN;}
+ YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+#line 166 "ast_expr2.fl"
+{/* what to do with eol */}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 167 "ast_expr2.fl"
+{
+ SET_COLUMNS;
+ /* the original behavior of the expression parser was
+ * to bring in numbers as a numeric string
+ */
+ SET_NUMERIC_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 176 "ast_expr2.fl"
+{
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 31:
+/* rule 31 can match eol */
+YY_RULE_SETUP
+#line 183 "ast_expr2.fl"
+{
+ curlycount--;
+ if (curlycount < 0) {
+ BEGIN(trail);
+ yymore();
+ } else {
+ yymore();
+ }
+ }
+ YY_BREAK
+case 32:
+/* rule 32 can match eol */
+YY_RULE_SETUP
+#line 193 "ast_expr2.fl"
+{
+ curlycount++;
+ yymore();
+ }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 199 "ast_expr2.fl"
+{
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 34:
+/* rule 34 can match eol */
+YY_RULE_SETUP
+#line 206 "ast_expr2.fl"
+{
+ char c = yytext[yyleng-1];
+ BEGIN(0);
+ unput(c);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 215 "ast_expr2.fl"
+{
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+ YY_BREAK
+case YY_STATE_EOF(trail):
+#line 221 "ast_expr2.fl"
+{
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ /*actually, if an expr is only a variable ref, this could happen a LOT */
+ }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 229 "ast_expr2.fl"
+ECHO;
+ YY_BREAK
+#line 1170 "ast_expr2f.c"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(var):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * ast_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( ast_yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of ast_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ ast_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ ast_yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 58 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ register char *yy_cp = yyg->yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 58 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 57);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ ast_yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( ast_yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void ast_yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ ast_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ast_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ ast_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void ast_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * ast_yypop_buffer_state();
+ * ast_yypush_buffer_state(new_buffer);
+ */
+ ast_yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ ast_yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (ast_yywrap()) processing, but the only time this flag
+ * is looked at is after ast_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void ast_yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE ast_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) ast_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) ast_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ ast_yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with ast_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void ast_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ ast_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ ast_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a ast_yyrestart() or at EOF.
+ */
+ static void ast_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ ast_yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then ast_yy_init_buffer was _probably_
+ * called from ast_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void ast_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ ast_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void ast_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ ast_yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from ast_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from ast_yy_switch_to_buffer. */
+ ast_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void ast_yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ ast_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ ast_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void ast_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ast_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ast_yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ast_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) ast_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ ast_yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to ast_yylex() will
+ * scan from a @e copy of @a str.
+ * @param str a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * ast_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE ast_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return ast_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to ast_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ast_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) ast_yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = ast_yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in ast_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE ast_yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int ast_yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int ast_yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ast_yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ast_yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int ast_yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *ast_yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void ast_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void ast_yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "ast_yyset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void ast_yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "ast_yyset_column called with no buffer" , yyscanner);
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see ast_yy_switch_to_buffer
+ */
+void ast_yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void ast_yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int ast_yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void ast_yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * ast_yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void ast_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+YYLTYPE *ast_yyget_lloc (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylloc;
+}
+
+void ast_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylloc = yylloc_param;
+}
+
+/* User-visible API */
+
+/* ast_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int ast_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ast_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from ast_yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * ast_yylex_init()
+ */
+ return 0;
+}
+
+/* ast_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int ast_yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ ast_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ ast_yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ ast_yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ ast_yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * ast_yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ ast_yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *ast_yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *ast_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 229 "ast_expr2.fl"
+
+
+
+/* I'm putting the interface routine to the whole parse here in the flexer input file
+ mainly because of all the flexer initialization that has to be done. Shouldn't matter
+ where it is, as long as it's somewhere. I didn't want to define a prototype for the
+ ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
+ UGH! that would be inappropriate. */
+
+int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
+int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
+
+void ast_yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr) /* the normal generated ast_yyfree func just frees its first arg;
+ this get complaints on some systems, as sometimes this
+ arg is a nil ptr! It's usually not fatal, but is irritating! */
+ free( (char *) ptr );
+}
+
+int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
+{
+ struct parse_io io;
+ int return_value = 0;
+
+ memset(&io, 0, sizeof(io));
+ io.string = expr; /* to pass to the error routine */
+ io.chan = chan;
+
+ ast_yylex_init(&io.scanner);
+
+ ast_yy_scan_string(expr, io.scanner);
+
+ ast_yyparse ((void *) &io);
+
+ ast_yylex_destroy(io.scanner);
+
+ if (!io.val) {
+ if (length > 1) {
+ strcpy(buf, "0");
+ return_value = 1;
+ }
+ } else {
+ if (io.val->type == AST_EXPR_number) {
+ int res_length;
+
+ res_length = snprintf(buf, length, FP___PRINTF, io.val->u.i);
+ return_value = (res_length <= length) ? res_length : length;
+ } else {
+ if (io.val->u.s)
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(buf, io.val->u.s, length - 1);
+#else /* !STANDALONE && !LOW_MEMORY */
+ ast_copy_string(buf, io.val->u.s, length);
+#endif /* STANDALONE || LOW_MEMORY */
+ else
+ buf[0] = 0;
+ return_value = strlen(buf);
+ if (io.val->u.s)
+ free(io.val->u.s);
+ }
+ free(io.val);
+ }
+ return return_value;
+}
+
+
+char extra_error_message[4095];
+int extra_error_message_supplied = 0;
+void ast_expr_register_extra_error_info(char *message);
+void ast_expr_clear_extra_error_info(void);
+
+void ast_expr_register_extra_error_info(char *message)
+{
+ extra_error_message_supplied=1;
+ strcpy(extra_error_message, message);
+}
+
+void ast_expr_clear_extra_error_info(void)
+{
+ extra_error_message_supplied=0;
+ extra_error_message[0] = 0;
+}
+
+static char *expr2_token_equivs1[] =
+{
+ "TOKEN",
+ "TOK_COND",
+ "TOK_COLONCOLON",
+ "TOK_OR",
+ "TOK_AND",
+ "TOK_EQ",
+ "TOK_GT",
+ "TOK_LT",
+ "TOK_GE",
+ "TOK_LE",
+ "TOK_NE",
+ "TOK_PLUS",
+ "TOK_MINUS",
+ "TOK_MULT",
+ "TOK_DIV",
+ "TOK_MOD",
+ "TOK_COMPL",
+ "TOK_COLON",
+ "TOK_EQTILDE",
+ "TOK_COMMA",
+ "TOK_RP",
+ "TOK_LP"
+};
+
+static char *expr2_token_equivs2[] =
+{
+ "<token>",
+ "?",
+ "::",
+ "|",
+ "&",
+ "=",
+ ">",
+ "<",
+ ">=",
+ "<=",
+ "!=",
+ "+",
+ "-",
+ "*",
+ "/",
+ "%",
+ "!",
+ ":",
+ "=~",
+ ",",
+ ")",
+ "("
+};
+
+
+static char *expr2_token_subst(const char *mess)
+{
+ /* calc a length, malloc, fill, and return; yyerror had better free it! */
+ int len=0,i;
+ const char *p;
+ char *res, *s,*t;
+ int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
+
+ for (p=mess; *p; p++) {
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
+ {
+ len+=strlen(expr2_token_equivs2[i])+2;
+ p += strlen(expr2_token_equivs1[i])-1;
+ break;
+ }
+ }
+ len++;
+ }
+ res = (char*)malloc(len+1);
+ res[0] = 0;
+ s = res;
+ for (p=mess; *p;) {
+ int found = 0;
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
+ *s++ = '\'';
+ for (t=expr2_token_equivs2[i]; *t;) {
+ *s++ = *t++;
+ }
+ *s++ = '\'';
+ p += strlen(expr2_token_equivs1[i]);
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ *s++ = *p++;
+ }
+ *s++ = 0;
+ return res;
+}
+
+int ast_yyerror (const char *s, yyltype *loc, struct parse_io *parseio )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
+ char spacebuf[8000]; /* best safe than sorry */
+ char spacebuf2[8000]; /* best safe than sorry */
+ int i=0;
+ char *s2 = expr2_token_subst(s);
+ spacebuf[0] = 0;
+
+ for(i=0;i< (int)(yytext - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);i++) spacebuf2[i] = ' '; /* uh... assuming yyg is defined, then I can use the yycolumn macro,
+ which is the same thing as... get this:
+ yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
+ I was tempted to just use yy_buf_pos in the STATE, but..., well:
+ a. the yy_buf_pos is the current position in the buffer, which
+ may not relate to the entire string/buffer because of the
+ buffering.
+ b. but, analysis of the situation is that when you use the
+ ast_yy_scan_string func, it creates a single buffer the size of
+ string, so the two would be the same...
+ so, in the end, the yycolumn macro is available, shorter, therefore easier. */
+ spacebuf2[i++]='^';
+ spacebuf2[i]= 0;
+
+#ifdef STANDALONE3
+ /* easier to read in the standalone version */
+ printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#else
+ ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#endif
+#ifndef STANDALONE
+ ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n");
+#endif
+ free(s2);
+ return(0);
+}
+
diff --git a/trunk/main/asterisk.c b/trunk/main/asterisk.c
new file mode 100644
index 000000000..3b3c2cb2a
--- /dev/null
+++ b/trunk/main/asterisk.c
@@ -0,0 +1,3267 @@
+/*
+ * 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.
+ */
+
+
+/* Doxygenified Copyright Header */
+/*!
+ * \mainpage Asterisk -- An Open Source Telephony Toolkit
+ *
+ * \par Developer Documentation for Asterisk
+ * This is the main developer documentation for Asterisk. It is
+ * generated by running "make progdocs".
+ * \par Additional documentation
+ * \arg \ref DevDoc
+ * \arg \ref ConfigFiles
+ *
+ * \section copyright Copyright and author
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Asterisk is a trade mark registered by Digium, Inc.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * Also see \ref AstCREDITS
+ *
+ * \section license License
+ * 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.
+ *
+ * \verbinclude LICENSE
+ *
+ */
+
+/*! \file
+ \brief Top level source file for Asterisk - the Open Source PBX. Implementation
+ of PBX core functions and CLI interface.
+
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+
+#undef sched_setscheduler
+#undef setpriority
+#include <sys/time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sched.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <sys/resource.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#if defined(HAVE_SYSINFO)
+#include <sys/sysinfo.h>
+#endif
+#ifdef linux
+#include <sys/prctl.h>
+#ifdef HAVE_CAP
+#include <sys/capability.h>
+#endif /* HAVE_CAP */
+#endif /* linux */
+#include <regex.h>
+
+#if defined(SOLARIS)
+int daemon(int, int); /* defined in libresolv of all places */
+#include <sys/loadavg.h>
+#endif
+
+#include "asterisk/paths.h" /* we define here the variables so better agree on the prototype */
+#include "asterisk/network.h"
+#include "asterisk/cli.h"
+#include "asterisk/channel.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/callerid.h"
+#include "asterisk/image.h"
+#include "asterisk/tdd.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/cdr.h"
+#include "asterisk/pbx.h"
+#include "asterisk/enum.h"
+#include "asterisk/rtp.h"
+#include "asterisk/http.h"
+#include "asterisk/udptl.h"
+#include "asterisk/app.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/file.h"
+#include "asterisk/io.h"
+#include "editline/histedit.h"
+#include "asterisk/config.h"
+#include "asterisk/version.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/module.h"
+
+#include "asterisk/doxyref.h" /* Doxygen documentation */
+
+#include "../defaults.h"
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#define PF_LOCAL PF_UNIX
+#endif
+
+#define AST_MAX_CONNECTS 128
+#define NUM_MSGS 64
+
+/*! \brief Welcome message when starting a CLI interface */
+#define WELCOME_MESSAGE \
+ ast_verbose("Asterisk %s, Copyright (C) 1999 - 2007 Digium, Inc. and others.\n" \
+ "Created by Mark Spencer <markster@digium.com>\n" \
+ "Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.\n" \
+ "This is free software, with components licensed under the GNU General Public\n" \
+ "License version 2 and other licenses; you are welcome to redistribute it under\n" \
+ "certain conditions. Type 'core show license' for details.\n" \
+ "=========================================================================\n" \
+ "NOTE: This is a development version of Asterisk, and should not be used in\n" \
+ "production installations.\n", ast_get_version());
+
+/*! \defgroup main_options Main Configuration Options
+ * \brief Main configuration options from asterisk.conf or OS command line on starting Asterisk.
+ * \arg \ref Config_ast "asterisk.conf"
+ * \note Some of them can be changed in the CLI
+ */
+/*! @{ */
+
+struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
+
+int option_verbose; /*!< Verbosity level */
+int option_debug; /*!< Debug level */
+double option_maxload; /*!< Max load avg on system */
+int option_maxcalls; /*!< Max number of active calls */
+int option_maxfiles; /*!< Max number of open file handles (files, sockets) */
+#if defined(HAVE_SYSINFO)
+long option_minmemfree; /*!< Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
+#endif
+
+/*! @} */
+
+/* XXX tmpdir is a subdir of the spool directory, and no way to remap it */
+char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
+
+static int ast_socket = -1; /*!< UNIX Socket for allowing remote control */
+static int ast_consock = -1; /*!< UNIX Socket for controlling another asterisk */
+pid_t ast_mainpid;
+struct console {
+ int fd; /*!< File descriptor */
+ int p[2]; /*!< Pipe */
+ pthread_t t; /*!< Thread of handler */
+ int mute; /*!< Is the console muted for logs */
+};
+
+struct ast_atexit {
+ void (*func)(void);
+ AST_RWLIST_ENTRY(ast_atexit) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(atexits, ast_atexit);
+
+struct timeval ast_startuptime;
+struct timeval ast_lastreloadtime;
+
+static History *el_hist;
+static EditLine *el;
+static char *remotehostname;
+
+struct console consoles[AST_MAX_CONNECTS];
+
+char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
+
+static int ast_el_add_history(char *);
+static int ast_el_read_history(char *);
+static int ast_el_write_history(char *);
+
+struct _cfg_paths {
+ char config_dir[PATH_MAX];
+ char module_dir[PATH_MAX];
+ char spool_dir[PATH_MAX];
+ char monitor_dir[PATH_MAX];
+ char var_dir[PATH_MAX];
+ char data_dir[PATH_MAX];
+ char log_dir[PATH_MAX];
+ char agi_dir[PATH_MAX];
+ char run_dir[PATH_MAX];
+ char key_dir[PATH_MAX];
+
+ char config_file[PATH_MAX];
+ char db_path[PATH_MAX];
+ char pid_path[PATH_MAX];
+ char socket_path[PATH_MAX];
+ char run_user[PATH_MAX];
+ char run_group[PATH_MAX];
+ char system_name[128];
+};
+
+static struct _cfg_paths cfg_paths;
+
+const char *ast_config_AST_CONFIG_DIR = cfg_paths.config_dir;
+const char *ast_config_AST_CONFIG_FILE = cfg_paths.config_file;
+const char *ast_config_AST_MODULE_DIR = cfg_paths.module_dir;
+const char *ast_config_AST_SPOOL_DIR = cfg_paths.spool_dir;
+const char *ast_config_AST_MONITOR_DIR = cfg_paths.monitor_dir;
+const char *ast_config_AST_VAR_DIR = cfg_paths.var_dir;
+const char *ast_config_AST_DATA_DIR = cfg_paths.data_dir;
+const char *ast_config_AST_LOG_DIR = cfg_paths.log_dir;
+const char *ast_config_AST_AGI_DIR = cfg_paths.agi_dir;
+const char *ast_config_AST_KEY_DIR = cfg_paths.key_dir;
+const char *ast_config_AST_RUN_DIR = cfg_paths.run_dir;
+
+const char *ast_config_AST_DB = cfg_paths.db_path;
+const char *ast_config_AST_PID = cfg_paths.pid_path;
+const char *ast_config_AST_SOCKET = cfg_paths.socket_path;
+const char *ast_config_AST_RUN_USER = cfg_paths.run_user;
+const char *ast_config_AST_RUN_GROUP = cfg_paths.run_group;
+const char *ast_config_AST_SYSTEM_NAME = cfg_paths.system_name;
+
+static char ast_config_AST_CTL_PERMISSIONS[PATH_MAX];
+static char ast_config_AST_CTL_OWNER[PATH_MAX] = "\0";
+static char ast_config_AST_CTL_GROUP[PATH_MAX] = "\0";
+static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl";
+
+extern const char *ast_build_hostname;
+extern const char *ast_build_kernel;
+extern const char *ast_build_machine;
+extern const char *ast_build_os;
+extern const char *ast_build_date;
+extern const char *ast_build_user;
+
+static char *_argv[256];
+static int shuttingdown;
+static int restartnow;
+static pthread_t consolethread = AST_PTHREADT_NULL;
+static int canary_pid = 0;
+static char canary_filename[128];
+
+static char randompool[256];
+
+static int sig_alert_pipe[2] = { -1, -1 };
+static struct {
+ unsigned int need_reload:1;
+ unsigned int need_quit:1;
+} sig_flags;
+
+#if !defined(LOW_MEMORY)
+struct file_version {
+ AST_RWLIST_ENTRY(file_version) list;
+ const char *file;
+ char *version;
+};
+
+static AST_RWLIST_HEAD_STATIC(file_versions, file_version);
+
+void ast_register_file_version(const char *file, const char *version)
+{
+ struct file_version *new;
+ char *work;
+ size_t version_length;
+
+ work = ast_strdupa(version);
+ work = ast_strip(ast_strip_quoted(work, "$", "$"));
+ version_length = strlen(work) + 1;
+
+ if (!(new = ast_calloc(1, sizeof(*new) + version_length)))
+ return;
+
+ new->file = file;
+ new->version = (char *) new + sizeof(*new);
+ memcpy(new->version, work, version_length);
+ AST_RWLIST_WRLOCK(&file_versions);
+ AST_RWLIST_INSERT_HEAD(&file_versions, new, list);
+ AST_RWLIST_UNLOCK(&file_versions);
+}
+
+void ast_unregister_file_version(const char *file)
+{
+ struct file_version *find;
+
+ AST_RWLIST_WRLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&file_versions, find, list) {
+ if (!strcasecmp(find->file, file)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&file_versions);
+
+ if (find)
+ ast_free(find);
+}
+
+/*! \brief Find version for given module name */
+const char *ast_file_version_find(const char *file)
+{
+ struct file_version *iterator;
+
+ AST_RWLIST_WRLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&file_versions, iterator, list) {
+ if (!strcasecmp(iterator->file, file))
+ break;
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&file_versions);
+ if (iterator)
+ return iterator->version;
+ return NULL;
+}
+
+
+
+struct thread_list_t {
+ AST_RWLIST_ENTRY(thread_list_t) list;
+ char *name;
+ pthread_t id;
+};
+
+static AST_RWLIST_HEAD_STATIC(thread_list, thread_list_t);
+
+void ast_register_thread(char *name)
+{
+ struct thread_list_t *new = ast_calloc(1, sizeof(*new));
+
+ if (!new)
+ return;
+ new->id = pthread_self();
+ new->name = name; /* steal the allocated memory for the thread name */
+ AST_RWLIST_WRLOCK(&thread_list);
+ AST_RWLIST_INSERT_HEAD(&thread_list, new, list);
+ AST_RWLIST_UNLOCK(&thread_list);
+}
+
+void ast_unregister_thread(void *id)
+{
+ struct thread_list_t *x;
+
+ AST_RWLIST_WRLOCK(&thread_list);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&thread_list, x, list) {
+ if ((void *) x->id == id) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&thread_list);
+ if (x) {
+ ast_free(x->name);
+ ast_free(x);
+ }
+}
+
+/*! \brief Give an overview of core settings */
+static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char buf[BUFSIZ];
+ struct ast_tm tm;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show settings";
+ e->usage = "Usage: core show settings\n"
+ " Show core misc settings";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "\nPBX Core settings\n");
+ ast_cli(a->fd, "-----------------\n");
+ ast_cli(a->fd, " Version: %s\n", ast_get_version());
+ if (option_maxcalls)
+ ast_cli(a->fd, " Max. calls: %d (Current %d)\n", option_maxcalls, ast_active_channels());
+ else
+ ast_cli(a->fd, " Max. calls: Not set\n");
+ if (option_maxfiles)
+ ast_cli(a->fd, " Max. open file handles: %d\n", option_maxfiles);
+ else
+ ast_cli(a->fd, " Max. open file handles: Not set\n");
+ ast_cli(a->fd, " Verbosity: %d\n", option_verbose);
+ ast_cli(a->fd, " Debug level: %d\n", option_debug);
+ ast_cli(a->fd, " Max load avg: %lf\n", option_maxload);
+#if defined(HAVE_SYSINFO)
+ ast_cli(a->fd, " Min Free Memory: %ld MB\n", option_minmemfree);
+#endif
+ if (ast_localtime(&ast_startuptime, &tm, NULL)) {
+ ast_strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
+ ast_cli(a->fd, " Startup time: %s\n", buf);
+ }
+ if (ast_localtime(&ast_lastreloadtime, &tm, NULL)) {
+ ast_strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
+ ast_cli(a->fd, " Last reload time: %s\n", buf);
+ }
+ ast_cli(a->fd, " System: %s/%s built by %s on %s %s\n", ast_build_os, ast_build_kernel, ast_build_user, ast_build_machine, ast_build_date);
+ ast_cli(a->fd, " System name: %s\n", ast_config_AST_SYSTEM_NAME);
+ ast_cli(a->fd, " Default language: %s\n", defaultlanguage);
+ ast_cli(a->fd, " Language prefix: %s\n", ast_language_is_prefix ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " User name and group: %s/%s\n", ast_config_AST_RUN_USER, ast_config_AST_RUN_GROUP);
+ ast_cli(a->fd, " Executable includes: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Transcode via SLIN: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Internal timing: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Transmit silence during rec: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING) ? "Enabled" : "Disabled");
+
+ ast_cli(a->fd, "\n* Subsystems\n");
+ ast_cli(a->fd, " -------------\n");
+ ast_cli(a->fd, " Manager (AMI): %s\n", check_manager_enabled() ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Web Manager (AMI/HTTP): %s\n", check_webmanager_enabled() ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Call data records: %s\n", check_cdr_enabled() ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Realtime Architecture (ARA): %s\n", ast_realtime_enabled() ? "Enabled" : "Disabled");
+
+ /*! \todo we could check musiconhold, voicemail, smdi, adsi, queues */
+
+ ast_cli(a->fd, "\n* Directories\n");
+ ast_cli(a->fd, " -------------\n");
+ ast_cli(a->fd, " Configuration file: %s\n", ast_config_AST_CONFIG_FILE);
+ ast_cli(a->fd, " Configuration directory: %s\n", ast_config_AST_CONFIG_DIR);
+ ast_cli(a->fd, " Module directory: %s\n", ast_config_AST_MODULE_DIR);
+ ast_cli(a->fd, " Spool directory: %s\n", ast_config_AST_SPOOL_DIR);
+ ast_cli(a->fd, " Log directory: %s\n", ast_config_AST_LOG_DIR);
+ ast_cli(a->fd, "\n\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_show_threads(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int count = 0;
+ struct thread_list_t *cur;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show threads";
+ e->usage =
+ "Usage: core show threads\n"
+ " List threads currently active in the system.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&thread_list);
+ AST_RWLIST_TRAVERSE(&thread_list, cur, list) {
+ ast_cli(a->fd, "%p %s\n", (void *)cur->id, cur->name);
+ count++;
+ }
+ AST_RWLIST_UNLOCK(&thread_list);
+ ast_cli(a->fd, "%d threads listed.\n", count);
+ return CLI_SUCCESS;
+}
+
+#if defined(HAVE_SYSINFO)
+/*! \brief Give an overview of system statistics */
+static char *handle_show_sysinfo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sysinfo sys_info;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show sysinfo";
+ e->usage =
+ "Usage: core show sysinfo\n"
+ " List current system information.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (sysinfo(&sys_info)) {
+ ast_cli(a->fd, "FAILED to retrieve system information\n\n");
+ return CLI_FAILURE;
+ }
+ ast_cli(a->fd, "\nSystem Statistics\n");
+ ast_cli(a->fd, "-----------------\n");
+ ast_cli(a->fd, " System Uptime: %ld hours\n", sys_info.uptime/3600);
+ ast_cli(a->fd, " Total RAM: %ld KiB\n", (sys_info.totalram / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Free RAM: %ld KiB\n", (sys_info.freeram / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Buffer RAM: %ld KiB\n", (sys_info.bufferram / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Total Swap Space: %ld KiB\n", (sys_info.totalswap / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Free Swap Space: %ld KiB\n\n", (sys_info.freeswap / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Number of Processes: %d \n\n", sys_info.procs);
+ return CLI_SUCCESS;
+}
+#endif
+
+struct profile_entry {
+ const char *name;
+ uint64_t scale; /* if non-zero, values are scaled by this */
+ int64_t mark;
+ int64_t value;
+ int64_t events;
+};
+
+struct profile_data {
+ int entries;
+ int max_size;
+ struct profile_entry e[0];
+};
+
+static struct profile_data *prof_data;
+
+/*! \brief allocates a counter with a given name and scale.
+ * \return Returns the identifier of the counter.
+ */
+int ast_add_profile(const char *name, uint64_t scale)
+{
+ int l = sizeof(struct profile_data);
+ int n = 10; /* default entries */
+
+ if (prof_data == NULL) {
+ prof_data = ast_calloc(1, l + n*sizeof(struct profile_entry));
+ if (prof_data == NULL)
+ return -1;
+ prof_data->entries = 0;
+ prof_data->max_size = n;
+ }
+ if (prof_data->entries >= prof_data->max_size) {
+ void *p;
+ n = prof_data->max_size + 20;
+ p = ast_realloc(prof_data, l + n*sizeof(struct profile_entry));
+ if (p == NULL)
+ return -1;
+ prof_data = p;
+ prof_data->max_size = n;
+ }
+ n = prof_data->entries++;
+ prof_data->e[n].name = ast_strdup(name);
+ prof_data->e[n].value = 0;
+ prof_data->e[n].events = 0;
+ prof_data->e[n].mark = 0;
+ prof_data->e[n].scale = scale;
+ return n;
+}
+
+int64_t ast_profile(int i, int64_t delta)
+{
+ if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
+ return 0;
+ if (prof_data->e[i].scale > 1)
+ delta /= prof_data->e[i].scale;
+ prof_data->e[i].value += delta;
+ prof_data->e[i].events++;
+ return prof_data->e[i].value;
+}
+
+#if defined ( __i386__) && (defined(__FreeBSD__) || defined(linux))
+#if defined(__FreeBSD__)
+#include <machine/cpufunc.h>
+#elif defined(linux)
+static __inline uint64_t
+rdtsc(void)
+{
+ uint64_t rv;
+
+ __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
+ return (rv);
+}
+#endif
+#else /* supply a dummy function on other platforms */
+static __inline uint64_t
+rdtsc(void)
+{
+ return 0;
+}
+#endif
+
+int64_t ast_mark(int i, int startstop)
+{
+ if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
+ return 0;
+ if (startstop == 1)
+ prof_data->e[i].mark = rdtsc();
+ else {
+ prof_data->e[i].mark = (rdtsc() - prof_data->e[i].mark);
+ if (prof_data->e[i].scale > 1)
+ prof_data->e[i].mark /= prof_data->e[i].scale;
+ prof_data->e[i].value += prof_data->e[i].mark;
+ prof_data->e[i].events++;
+ }
+ return prof_data->e[i].mark;
+}
+
+#define DEFINE_PROFILE_MIN_MAX_VALUES min = 0; \
+ max = prof_data->entries;\
+ if (a->argc > 3) { /* specific entries */ \
+ if (isdigit(a->argv[3][0])) { \
+ min = atoi(a->argv[3]); \
+ if (a->argc == 5 && strcmp(a->argv[4], "-")) \
+ max = atoi(a->argv[4]); \
+ } else \
+ search = a->argv[3]; \
+ } \
+ if (max > prof_data->entries) \
+ max = prof_data->entries;
+
+static char *handle_show_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, min, max;
+ char *search = NULL;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show profile";
+ e->usage = "Usage: core show profile\n"
+ " show profile information";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (prof_data == NULL)
+ return 0;
+
+ DEFINE_PROFILE_MIN_MAX_VALUES;
+ ast_cli(a->fd, "profile values (%d, allocated %d)\n-------------------\n",
+ prof_data->entries, prof_data->max_size);
+ ast_cli(a->fd, "%6s %8s %10s %12s %12s %s\n", "ID", "Scale", "Events",
+ "Value", "Average", "Name");
+ for (i = min; i < max; i++) {
+ struct profile_entry *e = &prof_data->e[i];
+ if (!search || strstr(prof_data->e[i].name, search))
+ ast_cli(a->fd, "%6d: [%8ld] %10ld %12lld %12lld %s\n",
+ i,
+ (long)e->scale,
+ (long)e->events, (long long)e->value,
+ (long long)(e->events ? e->value / e->events : e->value),
+ e->name);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_clear_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, min, max;
+ char *search = NULL;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core clear profile";
+ e->usage = "Usage: core clear profile\n"
+ " clear profile information";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (prof_data == NULL)
+ return 0;
+
+ DEFINE_PROFILE_MIN_MAX_VALUES;
+ for (i= min; i < max; i++) {
+ if (!search || strstr(prof_data->e[i].name, search)) {
+ prof_data->e[i].value = 0;
+ prof_data->e[i].events = 0;
+ }
+ }
+ return CLI_SUCCESS;
+}
+#undef DEFINE_PROFILE_MIN_MAX_VALUES
+
+/*! \brief CLI command to list module versions */
+static char *handle_show_version_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-25.25s %-40.40s\n"
+ struct file_version *iterator;
+ regex_t regexbuf;
+ int havepattern = 0;
+ int havename = 0;
+ int count_files = 0;
+ char *ret = NULL;
+ int matchlen, which = 0;
+ struct file_version *find;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show file version [like]";
+ e->usage =
+ "Usage: core show file version [like <pattern>]\n"
+ " Lists the revision numbers of the files used to build this copy of Asterisk.\n"
+ " Optional regular expression pattern is used to filter the file list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ matchlen = strlen(a->word);
+ if (a->pos != 3)
+ return NULL;
+ AST_RWLIST_RDLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE(&file_versions, find, list) {
+ if (!strncasecmp(a->word, find->file, matchlen) && ++which > a->n) {
+ ret = ast_strdup(find->file);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&file_versions);
+ return ret;
+ }
+
+
+ switch (a->argc) {
+ case 6:
+ if (!strcasecmp(a->argv[4], "like")) {
+ if (regcomp(&regexbuf, a->argv[5], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = 1;
+ } else
+ return CLI_SHOWUSAGE;
+ break;
+ case 5:
+ havename = 1;
+ break;
+ case 4:
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, FORMAT, "File", "Revision");
+ ast_cli(a->fd, FORMAT, "----", "--------");
+ AST_RWLIST_RDLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE(&file_versions, iterator, list) {
+ if (havename && strcasecmp(iterator->file, a->argv[4]))
+ continue;
+
+ if (havepattern && regexec(&regexbuf, iterator->file, 0, NULL, 0))
+ continue;
+
+ ast_cli(a->fd, FORMAT, iterator->file, iterator->version);
+ count_files++;
+ if (havename)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&file_versions);
+ if (!havename) {
+ ast_cli(a->fd, "%d files listed.\n", count_files);
+ }
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+}
+
+#endif /* ! LOW_MEMORY */
+
+int ast_register_atexit(void (*func)(void))
+{
+ struct ast_atexit *ae;
+
+ if (!(ae = ast_calloc(1, sizeof(*ae))))
+ return -1;
+
+ ae->func = func;
+
+ ast_unregister_atexit(func);
+
+ AST_RWLIST_WRLOCK(&atexits);
+ AST_RWLIST_INSERT_HEAD(&atexits, ae, list);
+ AST_RWLIST_UNLOCK(&atexits);
+
+ return 0;
+}
+
+void ast_unregister_atexit(void (*func)(void))
+{
+ struct ast_atexit *ae = NULL;
+
+ AST_RWLIST_WRLOCK(&atexits);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
+ if (ae->func == func) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&atexits);
+
+ if (ae)
+ free(ae);
+}
+
+static int fdprint(int fd, const char *s)
+{
+ return write(fd, s, strlen(s) + 1);
+}
+
+/*! \brief NULL handler so we can collect the child exit status */
+static void null_sig_handler(int signal)
+{
+
+}
+
+AST_MUTEX_DEFINE_STATIC(safe_system_lock);
+/*! \brief Keep track of how many threads are currently trying to wait*() on
+ * a child process */
+static unsigned int safe_system_level = 0;
+static void *safe_system_prev_handler;
+
+void ast_replace_sigchld(void)
+{
+ unsigned int level;
+
+ ast_mutex_lock(&safe_system_lock);
+ level = safe_system_level++;
+
+ /* only replace the handler if it has not already been done */
+ if (level == 0)
+ safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
+
+ ast_mutex_unlock(&safe_system_lock);
+}
+
+void ast_unreplace_sigchld(void)
+{
+ unsigned int level;
+
+ ast_mutex_lock(&safe_system_lock);
+ level = --safe_system_level;
+
+ /* only restore the handler if we are the last one */
+ if (level == 0)
+ signal(SIGCHLD, safe_system_prev_handler);
+
+ ast_mutex_unlock(&safe_system_lock);
+}
+
+int ast_safe_system(const char *s)
+{
+ pid_t pid;
+#ifdef HAVE_WORKING_FORK
+ int x;
+#endif
+ int res;
+ struct rusage rusage;
+ int status;
+
+#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
+ ast_replace_sigchld();
+
+#ifdef HAVE_WORKING_FORK
+ pid = fork();
+#else
+ pid = vfork();
+#endif
+
+ if (pid == 0) {
+#ifdef HAVE_WORKING_FORK
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+ /* Close file descriptors and launch system command */
+ for (x = STDERR_FILENO + 1; x < 4096; x++)
+ close(x);
+#endif
+ execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
+ _exit(1);
+ } else if (pid > 0) {
+ for (;;) {
+ res = wait4(pid, &status, 0, &rusage);
+ if (res > -1) {
+ res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ break;
+ } else if (errno != EINTR)
+ break;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+ res = -1;
+ }
+
+ ast_unreplace_sigchld();
+#else
+ res = -1;
+#endif
+
+ return res;
+}
+
+/*!
+ * \brief mute or unmute a console from logging
+ */
+void ast_console_toggle_mute(int fd, int silent) {
+ int x;
+ for (x = 0;x < AST_MAX_CONNECTS; x++) {
+ if (fd == consoles[x].fd) {
+ if (consoles[x].mute) {
+ consoles[x].mute = 0;
+ if (!silent)
+ ast_cli(fd, "Console is not muted anymore.\n");
+ } else {
+ consoles[x].mute = 1;
+ if (!silent)
+ ast_cli(fd, "Console is muted.\n");
+ }
+ return;
+ }
+ }
+ ast_cli(fd, "Couldn't find remote console.\n");
+}
+
+/*!
+ * \brief log the string to all attached console clients
+ */
+static void ast_network_puts_mutable(const char *string)
+{
+ int x;
+ for (x = 0;x < AST_MAX_CONNECTS; x++) {
+ if (consoles[x].mute)
+ continue;
+ if (consoles[x].fd > -1)
+ fdprint(consoles[x].p[1], string);
+ }
+}
+
+/*!
+ * \brief log the string to the console, and all attached
+ * console clients
+ */
+void ast_console_puts_mutable(const char *string)
+{
+ fputs(string, stdout);
+ fflush(stdout);
+ ast_network_puts_mutable(string);
+}
+
+/*!
+ * \brief write the string to all attached console clients
+ */
+static void ast_network_puts(const char *string)
+{
+ int x;
+ for (x=0; x < AST_MAX_CONNECTS; x++) {
+ if (consoles[x].fd > -1)
+ fdprint(consoles[x].p[1], string);
+ }
+}
+
+/*!
+ * write the string to the console, and all attached
+ * console clients
+ */
+void ast_console_puts(const char *string)
+{
+ fputs(string, stdout);
+ fflush(stdout);
+ ast_network_puts(string);
+}
+
+static void network_verboser(const char *s)
+{
+ ast_network_puts_mutable(s);
+}
+
+static pthread_t lthread;
+
+static void *netconsole(void *vconsole)
+{
+ struct console *con = vconsole;
+ char hostname[MAXHOSTNAMELEN] = "";
+ char tmp[512];
+ int res;
+ struct pollfd fds[2];
+
+ if (gethostname(hostname, sizeof(hostname)-1))
+ ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
+ snprintf(tmp, sizeof(tmp), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version());
+ fdprint(con->fd, tmp);
+ for (;;) {
+ fds[0].fd = con->fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ fds[1].fd = con->p[0];
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+
+ res = poll(fds, 2, -1);
+ if (res < 0) {
+ if (errno != EINTR)
+ ast_log(LOG_WARNING, "poll returned < 0: %s\n", strerror(errno));
+ continue;
+ }
+ if (fds[0].revents) {
+ res = read(con->fd, tmp, sizeof(tmp));
+ if (res < 1) {
+ break;
+ }
+ tmp[res] = 0;
+ ast_cli_command_multiple(con->fd, res, tmp);
+ }
+ if (fds[1].revents) {
+ res = read(con->p[0], tmp, sizeof(tmp));
+ if (res < 1) {
+ ast_log(LOG_ERROR, "read returned %d\n", res);
+ break;
+ }
+ res = write(con->fd, tmp, res);
+ if (res < 1)
+ break;
+ }
+ }
+ ast_verb(3, "Remote UNIX connection disconnected\n");
+ close(con->fd);
+ close(con->p[0]);
+ close(con->p[1]);
+ con->fd = -1;
+
+ return NULL;
+}
+
+static void *listener(void *unused)
+{
+ struct sockaddr_un sunaddr;
+ int s;
+ socklen_t len;
+ int x;
+ int flags;
+ struct pollfd fds[1];
+ for (;;) {
+ if (ast_socket < 0)
+ return NULL;
+ fds[0].fd = ast_socket;
+ fds[0].events = POLLIN;
+ s = poll(fds, 1, -1);
+ pthread_testcancel();
+ if (s < 0) {
+ if (errno != EINTR)
+ ast_log(LOG_WARNING, "poll returned error: %s\n", strerror(errno));
+ continue;
+ }
+ len = sizeof(sunaddr);
+ s = accept(ast_socket, (struct sockaddr *)&sunaddr, &len);
+ if (s < 0) {
+ if (errno != EINTR)
+ ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
+ } else {
+ for (x = 0; x < AST_MAX_CONNECTS; x++) {
+ if (consoles[x].fd < 0) {
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
+ ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
+ consoles[x].fd = -1;
+ fdprint(s, "Server failed to create pipe\n");
+ close(s);
+ break;
+ }
+ flags = fcntl(consoles[x].p[1], F_GETFL);
+ fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
+ consoles[x].fd = s;
+ consoles[x].mute = 1; /* Default is muted, we will un-mute if necessary */
+ if (ast_pthread_create_detached_background(&consoles[x].t, NULL, netconsole, &consoles[x])) {
+ ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
+ close(consoles[x].p[0]);
+ close(consoles[x].p[1]);
+ consoles[x].fd = -1;
+ fdprint(s, "Server failed to spawn thread\n");
+ close(s);
+ }
+ break;
+ }
+ }
+ if (x >= AST_MAX_CONNECTS) {
+ fdprint(s, "No more connections allowed\n");
+ ast_log(LOG_WARNING, "No more connections allowed\n");
+ close(s);
+ } else if (consoles[x].fd > -1)
+ ast_verb(3, "Remote UNIX connection\n");
+ }
+ }
+ return NULL;
+}
+
+static int ast_makesocket(void)
+{
+ struct sockaddr_un sunaddr;
+ int res;
+ int x;
+ uid_t uid = -1;
+ gid_t gid = -1;
+
+ for (x = 0; x < AST_MAX_CONNECTS; x++)
+ consoles[x].fd = -1;
+ unlink(ast_config_AST_SOCKET);
+ ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (ast_socket < 0) {
+ ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
+ return -1;
+ }
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ sunaddr.sun_family = AF_LOCAL;
+ ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
+ res = bind(ast_socket, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+ close(ast_socket);
+ ast_socket = -1;
+ return -1;
+ }
+ res = listen(ast_socket, 2);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+ close(ast_socket);
+ ast_socket = -1;
+ return -1;
+ }
+ ast_register_verbose(network_verboser);
+ ast_pthread_create_background(&lthread, NULL, listener, NULL);
+
+ if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
+ struct passwd *pw;
+ if ((pw = getpwnam(ast_config_AST_CTL_OWNER)) == NULL)
+ ast_log(LOG_WARNING, "Unable to find uid of user %s\n", ast_config_AST_CTL_OWNER);
+ else
+ uid = pw->pw_uid;
+ }
+
+ if (!ast_strlen_zero(ast_config_AST_CTL_GROUP)) {
+ struct group *grp;
+ if ((grp = getgrnam(ast_config_AST_CTL_GROUP)) == NULL)
+ ast_log(LOG_WARNING, "Unable to find gid of group %s\n", ast_config_AST_CTL_GROUP);
+ else
+ gid = grp->gr_gid;
+ }
+
+ if (chown(ast_config_AST_SOCKET, uid, gid) < 0)
+ ast_log(LOG_WARNING, "Unable to change ownership of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+
+ if (!ast_strlen_zero(ast_config_AST_CTL_PERMISSIONS)) {
+ int p1;
+ mode_t p;
+ sscanf(ast_config_AST_CTL_PERMISSIONS, "%o", &p1);
+ p = p1;
+ if ((chmod(ast_config_AST_SOCKET, p)) < 0)
+ ast_log(LOG_WARNING, "Unable to change file permissions of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+ }
+
+ return 0;
+}
+
+static int ast_tryconnect(void)
+{
+ struct sockaddr_un sunaddr;
+ int res;
+ ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (ast_consock < 0) {
+ ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
+ return 0;
+ }
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ sunaddr.sun_family = AF_LOCAL;
+ ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
+ res = connect(ast_consock, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
+ if (res) {
+ close(ast_consock);
+ ast_consock = -1;
+ return 0;
+ } else
+ return 1;
+}
+
+/*! \brief Urgent handler
+
+ Called by soft_hangup to interrupt the poll, read, or other
+ system call. We don't actually need to do anything though.
+ Remember: Cannot EVER ast_log from within a signal handler
+ */
+static void urg_handler(int num)
+{
+ signal(num, urg_handler);
+ return;
+}
+
+static void hup_handler(int num)
+{
+ int a = 0;
+ if (option_verbose > 1)
+ printf("Received HUP signal -- Reloading configs\n");
+ if (restartnow)
+ execvp(_argv[0], _argv);
+ sig_flags.need_reload = 1;
+ if (sig_alert_pipe[1] != -1)
+ write(sig_alert_pipe[1], &a, sizeof(a));
+ signal(num, hup_handler);
+}
+
+static void child_handler(int sig)
+{
+ /* Must not ever ast_log or ast_verbose within signal handler */
+ int n, status;
+
+ /*
+ * Reap all dead children -- not just one
+ */
+ for (n = 0; wait4(-1, &status, WNOHANG, NULL) > 0; n++)
+ ;
+ if (n == 0 && option_debug)
+ printf("Huh? Child handler, but nobody there?\n");
+ signal(sig, child_handler);
+}
+
+/*! \brief Set maximum open files */
+static void set_ulimit(int value)
+{
+ struct rlimit l = {0, 0};
+
+ if (value <= 0) {
+ ast_log(LOG_WARNING, "Unable to change max files open to invalid value %i\n",value);
+ return;
+ }
+
+ l.rlim_cur = value;
+ l.rlim_max = value;
+
+ if (setrlimit(RLIMIT_NOFILE, &l)) {
+ ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n",strerror(errno));
+ return;
+ }
+
+ ast_log(LOG_NOTICE, "Setting max files open to %d\n",value);
+
+ return;
+}
+
+/*! \brief Set an X-term or screen title */
+static void set_title(char *text)
+{
+ if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
+ fprintf(stdout, "\033]2;%s\007", text);
+}
+
+static void set_icon(char *text)
+{
+ if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
+ fprintf(stdout, "\033]1;%s\007", text);
+}
+
+/*! \brief We set ourselves to a high priority, that we might pre-empt everything
+ else. If your PBX has heavy activity on it, this is a good thing. */
+int ast_set_priority(int pri)
+{
+ struct sched_param sched;
+ memset(&sched, 0, sizeof(sched));
+#ifdef __linux__
+ if (pri) {
+ sched.sched_priority = 10;
+ if (sched_setscheduler(0, SCHED_RR, &sched)) {
+ ast_log(LOG_WARNING, "Unable to set high priority\n");
+ return -1;
+ } else
+ if (option_verbose)
+ ast_verbose("Set to realtime thread\n");
+ } else {
+ sched.sched_priority = 0;
+ /* According to the manpage, these parameters can never fail. */
+ sched_setscheduler(0, SCHED_OTHER, &sched);
+ }
+#else
+ if (pri) {
+ if (setpriority(PRIO_PROCESS, 0, -10) == -1) {
+ ast_log(LOG_WARNING, "Unable to set high priority\n");
+ return -1;
+ } else
+ if (option_verbose)
+ ast_verbose("Set to high priority\n");
+ } else {
+ /* According to the manpage, these parameters can never fail. */
+ setpriority(PRIO_PROCESS, 0, 0);
+ }
+#endif
+ return 0;
+}
+
+static void ast_run_atexits(void)
+{
+ struct ast_atexit *ae;
+ AST_RWLIST_RDLOCK(&atexits);
+ AST_RWLIST_TRAVERSE(&atexits, ae, list) {
+ if (ae->func)
+ ae->func();
+ }
+ AST_RWLIST_UNLOCK(&atexits);
+}
+
+static void quit_handler(int num, int nice, int safeshutdown, int restart)
+{
+ char filename[80] = "";
+ time_t s,e;
+ int x;
+ /* Try to get as many CDRs as possible submitted to the backend engines (if in batch mode) */
+ ast_cdr_engine_term();
+ if (safeshutdown) {
+ shuttingdown = 1;
+ if (!nice) {
+ /* Begin shutdown routine, hanging up active channels */
+ ast_begin_shutdown(1);
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
+ time(&s);
+ for (;;) {
+ time(&e);
+ /* Wait up to 15 seconds for all channels to go away */
+ if ((e - s) > 15)
+ break;
+ if (!ast_active_channels())
+ break;
+ if (!shuttingdown)
+ break;
+ /* Sleep 1/10 of a second */
+ usleep(100000);
+ }
+ } else {
+ if (nice < 2)
+ ast_begin_shutdown(0);
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
+ for (;;) {
+ if (!ast_active_channels())
+ break;
+ if (!shuttingdown)
+ break;
+ sleep(1);
+ }
+ }
+
+ if (!shuttingdown) {
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
+ return;
+ }
+
+ if (nice)
+ ast_module_shutdown();
+ }
+ if (ast_opt_console || ast_opt_remote) {
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ if (!ast_strlen_zero(filename))
+ ast_el_write_history(filename);
+ if (el != NULL)
+ el_end(el);
+ if (el_hist != NULL)
+ history_end(el_hist);
+ }
+ if (option_verbose)
+ ast_verbose("Executing last minute cleanups\n");
+ ast_run_atexits();
+ /* Called on exit */
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
+ ast_debug(1, "Asterisk ending (%d).\n", num);
+ manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\nRestart: %s\r\n", ast_active_channels() ? "Uncleanly" : "Cleanly", restart ? "True" : "False");
+ if (ast_socket > -1) {
+ pthread_cancel(lthread);
+ close(ast_socket);
+ ast_socket = -1;
+ unlink(ast_config_AST_SOCKET);
+ }
+ if (ast_consock > -1)
+ close(ast_consock);
+ if (!ast_opt_remote)
+ unlink(ast_config_AST_PID);
+ printf(term_quit());
+ if (restart) {
+ if (option_verbose || ast_opt_console)
+ ast_verbose("Preparing for Asterisk restart...\n");
+ /* Mark all FD's for closing on exec */
+ for (x=3; x < 32768; x++) {
+ fcntl(x, F_SETFD, FD_CLOEXEC);
+ }
+ if (option_verbose || ast_opt_console)
+ ast_verbose("Asterisk is now restarting...\n");
+ restartnow = 1;
+
+ /* close logger */
+ close_logger();
+
+ /* If there is a consolethread running send it a SIGHUP
+ so it can execvp, otherwise we can do it ourselves */
+ if ((consolethread != AST_PTHREADT_NULL) && (consolethread != pthread_self())) {
+ pthread_kill(consolethread, SIGHUP);
+ /* Give the signal handler some time to complete */
+ sleep(2);
+ } else
+ execvp(_argv[0], _argv);
+
+ } else {
+ /* close logger */
+ close_logger();
+ }
+ exit(0);
+}
+
+static void __quit_handler(int num)
+{
+ int a = 0;
+ sig_flags.need_quit = 1;
+ if (sig_alert_pipe[1] != -1)
+ write(sig_alert_pipe[1], &a, sizeof(a));
+ /* There is no need to restore the signal handler here, since the app
+ * is going to exit */
+}
+
+static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
+{
+ const char *c;
+ if (!strncmp(s, cmp, strlen(cmp))) {
+ c = s + strlen(cmp);
+ term_color(outbuf, cmp, COLOR_GRAY, 0, maxout);
+ return c;
+ }
+ return NULL;
+}
+
+static void console_verboser(const char *s)
+{
+ char tmp[80];
+ const char *c = NULL;
+
+ if ((c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_4)) ||
+ (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_3)) ||
+ (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_2)) ||
+ (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_1))) {
+ fputs(tmp, stdout);
+ fputs(c, stdout);
+ } else
+ fputs(s, stdout);
+
+ fflush(stdout);
+
+ /* Wake up a poll()ing console */
+ if (ast_opt_console && consolethread != AST_PTHREADT_NULL)
+ pthread_kill(consolethread, SIGURG);
+}
+
+static int ast_all_zeros(char *s)
+{
+ while (*s) {
+ if (*s > 32)
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
+static void consolehandler(char *s)
+{
+ printf(term_end());
+ fflush(stdout);
+
+ /* Called when readline data is available */
+ if (!ast_all_zeros(s))
+ ast_el_add_history(s);
+ /* The real handler for bang */
+ if (s[0] == '!') {
+ if (s[1])
+ ast_safe_system(s+1);
+ else
+ ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+ } else
+ ast_cli_command(STDOUT_FILENO, s);
+}
+
+static int remoteconsolehandler(char *s)
+{
+ int ret = 0;
+
+ /* Called when readline data is available */
+ if (!ast_all_zeros(s))
+ ast_el_add_history(s);
+ /* The real handler for bang */
+ if (s[0] == '!') {
+ if (s[1])
+ ast_safe_system(s+1);
+ else
+ ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+ ret = 1;
+ }
+ if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
+ (s[4] == '\0' || isspace(s[4]))) {
+ quit_handler(0, 0, 0, 0);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static char *handle_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show version";
+ e->usage =
+ "Usage: core show version\n"
+ " Shows Asterisk version information.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "Asterisk %s built by %s @ %s on a %s running %s on %s\n",
+ ast_get_version(), ast_build_user, ast_build_hostname,
+ ast_build_machine, ast_build_os, ast_build_date);
+ return CLI_SUCCESS;
+}
+
+#if 0
+static int handle_quit(int fd, int argc, char *argv[])
+{
+ if (argc != 1)
+ return RESULT_SHOWUSAGE;
+ quit_handler(0, 0, 1, 0);
+ return RESULT_SUCCESS;
+}
+#endif
+
+static char *handle_stop_now(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stop now";
+ e->usage =
+ "Usage: stop now\n"
+ " Shuts down a running Asterisk immediately, hanging up all active calls .\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 0 /* Not nice */, 1 /* safely */, 0 /* not restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_stop_gracefully(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stop gracefully";
+ e->usage =
+ "Usage: stop gracefully\n"
+ " Causes Asterisk to not accept new calls, and exit when all\n"
+ " active calls have terminated normally.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 1 /* nicely */, 1 /* safely */, 0 /* no restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_stop_when_convenient(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stop when convenient";
+ e->usage =
+ "Usage: stop when convenient\n"
+ " Causes Asterisk to perform a shutdown when all active calls have ended.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "Waiting for inactivity to perform halt\n");
+ quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_restart_now(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "restart now";
+ e->usage =
+ "Usage: restart now\n"
+ " Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
+ " restart.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 0 /* not nicely */, 1 /* safely */, 1 /* restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_restart_gracefully(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "restart gracefully";
+ e->usage =
+ "Usage: restart gracefully\n"
+ " Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n"
+ " restart when all active calls have ended.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 1 /* nicely */, 1 /* safely */, 1 /* restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "restart when convenient";
+ e->usage =
+ "Usage: restart when convenient\n"
+ " Causes Asterisk to perform a cold restart when all active calls have ended.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "Waiting for inactivity to perform restart\n");
+ quit_handler(0, 2 /* really nicely */, 1 /* safely */, 1 /* restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "abort shutdown";
+ e->usage =
+ "Usage: abort shutdown\n"
+ " Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
+ " call operations.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ ast_cancel_shutdown();
+ shuttingdown = 0;
+ return CLI_SUCCESS;
+}
+
+static char *handle_bang(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "!";
+ e->usage =
+ "Usage: !<command>\n"
+ " Executes a given shell command\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ return CLI_SUCCESS;
+}
+static const char warranty_lines[] = {
+ "\n"
+ " NO WARRANTY\n"
+ "\n"
+ "BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n"
+ "FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n"
+ "OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n"
+ "PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n"
+ "OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n"
+ "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n"
+ "TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n"
+ "PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n"
+ "REPAIR OR CORRECTION.\n"
+ "\n"
+ "IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n"
+ "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n"
+ "REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n"
+ "INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n"
+ "OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n"
+ "TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n"
+ "YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n"
+ "PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n"
+ "POSSIBILITY OF SUCH DAMAGES.\n"
+};
+
+static char *show_warranty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show warranty";
+ e->usage =
+ "Usage: core show warranty\n"
+ " Shows the warranty (if any) for this copy of Asterisk.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, warranty_lines);
+
+ return CLI_SUCCESS;
+}
+
+static const char license_lines[] = {
+ "\n"
+ "This program is free software; you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License version 2 as\n"
+ "published by the Free Software Foundation.\n"
+ "\n"
+ "This program also contains components licensed under other licenses.\n"
+ "They include:\n"
+ "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n"
+ "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with this program; if not, write to the Free Software\n"
+ "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
+};
+
+static char *show_license(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show license";
+ e->usage =
+ "Usage: core show license\n"
+ " Shows the license(s) for this copy of Asterisk.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, license_lines);
+
+ return CLI_SUCCESS;
+}
+
+#define ASTERISK_PROMPT "*CLI> "
+
+#define ASTERISK_PROMPT2 "%s*CLI> "
+
+static struct ast_cli_entry cli_asterisk[] = {
+ AST_CLI_DEFINE(handle_abort_shutdown, "Cancel a running shutdown"),
+ AST_CLI_DEFINE(handle_stop_now, "Shut down Asterisk immediately"),
+ AST_CLI_DEFINE(handle_stop_gracefully, "Gracefully shut down Asterisk"),
+ AST_CLI_DEFINE(handle_stop_when_convenient, "Shut down Asterisk at empty call volume"),
+ AST_CLI_DEFINE(handle_restart_now, "Restart Asterisk immediately"),
+ AST_CLI_DEFINE(handle_restart_gracefully, "Restart Asterisk gracefully"),
+ AST_CLI_DEFINE(handle_restart_when_convenient, "Restart Asterisk at empty call volume"),
+ AST_CLI_DEFINE(show_warranty, "Show the warranty (if any) for this copy of Asterisk"),
+ AST_CLI_DEFINE(show_license, "Show the license(s) for this copy of Asterisk"),
+ AST_CLI_DEFINE(handle_version, "Display version info"),
+ AST_CLI_DEFINE(handle_bang, "Execute a shell command"),
+#if !defined(LOW_MEMORY)
+ AST_CLI_DEFINE(handle_show_version_files, "List versions of files used to build Asterisk"),
+ AST_CLI_DEFINE(handle_show_threads, "Show running threads"),
+#if defined(HAVE_SYSINFO)
+ AST_CLI_DEFINE(handle_show_sysinfo, "Show System Information"),
+#endif
+ AST_CLI_DEFINE(handle_show_profile, "Display profiling info"),
+ AST_CLI_DEFINE(handle_show_settings, "Show some core settings"),
+ AST_CLI_DEFINE(handle_clear_profile, "Clear profiling info"),
+#endif /* ! LOW_MEMORY */
+};
+
+static int ast_el_read_char(EditLine *el, char *cp)
+{
+ int num_read = 0;
+ int lastpos = 0;
+ struct pollfd fds[2];
+ int res;
+ int max;
+#define EL_BUF_SIZE 512
+ char buf[EL_BUF_SIZE];
+
+ for (;;) {
+ max = 1;
+ fds[0].fd = ast_consock;
+ fds[0].events = POLLIN;
+ if (!ast_opt_exec) {
+ fds[1].fd = STDIN_FILENO;
+ fds[1].events = POLLIN;
+ max++;
+ }
+ res = poll(fds, max, -1);
+ if (res < 0) {
+ if (errno == EINTR)
+ continue;
+ ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
+ break;
+ }
+
+ if (!ast_opt_exec && fds[1].revents) {
+ num_read = read(STDIN_FILENO, cp, 1);
+ if (num_read < 1) {
+ break;
+ } else
+ return (num_read);
+ }
+ if (fds[0].revents) {
+ res = read(ast_consock, buf, sizeof(buf) - 1);
+ /* if the remote side disappears exit */
+ if (res < 1) {
+ fprintf(stderr, "\nDisconnected from Asterisk server\n");
+ if (!ast_opt_reconnect) {
+ quit_handler(0, 0, 0, 0);
+ } else {
+ int tries;
+ int reconnects_per_second = 20;
+ fprintf(stderr, "Attempting to reconnect for 30 seconds\n");
+ for (tries=0; tries < 30 * reconnects_per_second; tries++) {
+ if (ast_tryconnect()) {
+ fprintf(stderr, "Reconnect succeeded after %.3f seconds\n", 1.0 / reconnects_per_second * tries);
+ printf(term_quit());
+ WELCOME_MESSAGE;
+ if (!ast_opt_mute)
+ fdprint(ast_consock, "logger mute silent");
+ else
+ printf("log and verbose output currently muted ('logger mute' to unmute)\n");
+ break;
+ } else
+ usleep(1000000 / reconnects_per_second);
+ }
+ if (tries >= 30 * reconnects_per_second) {
+ fprintf(stderr, "Failed to reconnect for 30 seconds. Quitting.\n");
+ quit_handler(0, 0, 0, 0);
+ }
+ }
+ }
+
+ buf[res] = '\0';
+
+ if (!ast_opt_exec && !lastpos)
+ write(STDOUT_FILENO, "\r", 1);
+ write(STDOUT_FILENO, buf, res);
+ if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (buf[res-2] == '\n'))) {
+ *cp = CC_REFRESH;
+ return(1);
+ } else
+ lastpos = 1;
+ }
+ }
+
+ *cp = '\0';
+ return (0);
+}
+
+static char *cli_prompt(EditLine *el)
+{
+ static char prompt[200];
+ char *pfmt;
+ int color_used = 0;
+ char term_code[20];
+
+ if ((pfmt = getenv("ASTERISK_PROMPT"))) {
+ char *t = pfmt, *p = prompt;
+ memset(prompt, 0, sizeof(prompt));
+ while (*t != '\0' && *p < sizeof(prompt)) {
+ if (*t == '%') {
+ char hostname[MAXHOSTNAMELEN]="";
+ int i;
+ struct timeval ts = ast_tvnow();
+ struct ast_tm tm = { 0, };
+#ifdef linux
+ FILE *LOADAVG;
+#endif
+ int fgcolor = COLOR_WHITE, bgcolor = COLOR_BLACK;
+
+ t++;
+ switch (*t) {
+ case 'C': /* color */
+ t++;
+ if (sscanf(t, "%d;%d%n", &fgcolor, &bgcolor, &i) == 2) {
+ strncat(p, term_color_code(term_code, fgcolor, bgcolor, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
+ t += i - 1;
+ } else if (sscanf(t, "%d%n", &fgcolor, &i) == 1) {
+ strncat(p, term_color_code(term_code, fgcolor, 0, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
+ t += i - 1;
+ }
+
+ /* If the color has been reset correctly, then there's no need to reset it later */
+ color_used = ((fgcolor == COLOR_WHITE) && (bgcolor == COLOR_BLACK)) ? 0 : 1;
+ break;
+ case 'd': /* date */
+ if (ast_localtime(&ts, &tm, NULL))
+ ast_strftime(p, sizeof(prompt) - strlen(prompt), "%Y-%m-%d", &tm);
+ break;
+ case 'h': /* hostname */
+ if (!gethostname(hostname, sizeof(hostname) - 1))
+ strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
+ else
+ strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case 'H': /* short hostname */
+ if (!gethostname(hostname, sizeof(hostname) - 1)) {
+ for (i = 0; i < sizeof(hostname); i++) {
+ if (hostname[i] == '.') {
+ hostname[i] = '\0';
+ break;
+ }
+ }
+ strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
+ } else
+ strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+#ifdef linux
+ case 'l': /* load avg */
+ t++;
+ if ((LOADAVG = fopen("/proc/loadavg", "r"))) {
+ float avg1, avg2, avg3;
+ int actproc, totproc, npid, which;
+ fscanf(LOADAVG, "%f %f %f %d/%d %d",
+ &avg1, &avg2, &avg3, &actproc, &totproc, &npid);
+ if (sscanf(t, "%d", &which) == 1) {
+ switch (which) {
+ case 1:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg1);
+ break;
+ case 2:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg2);
+ break;
+ case 3:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg3);
+ break;
+ case 4:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%d/%d", actproc, totproc);
+ break;
+ case 5:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%d", npid);
+ break;
+ }
+ }
+ }
+ break;
+#endif
+ case 's': /* Asterisk system name (from asterisk.conf) */
+ strncat(p, ast_config_AST_SYSTEM_NAME, sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case 't': /* time */
+ if (ast_localtime(&ts, &tm, NULL))
+ ast_strftime(p, sizeof(prompt) - strlen(prompt), "%H:%M:%S", &tm);
+ break;
+ case '#': /* process console or remote? */
+ if (!ast_opt_remote)
+ strncat(p, "#", sizeof(prompt) - strlen(prompt) - 1);
+ else
+ strncat(p, ">", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case '%': /* literal % */
+ strncat(p, "%", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case '\0': /* % is last character - prevent bug */
+ t--;
+ break;
+ }
+ while (*p != '\0')
+ p++;
+ t++;
+ } else {
+ *p = *t;
+ p++;
+ t++;
+ }
+ }
+ if (color_used) {
+ /* Force colors back to normal at end */
+ term_color_code(term_code, COLOR_WHITE, COLOR_BLACK, sizeof(term_code));
+ if (strlen(term_code) > sizeof(prompt) - strlen(prompt))
+ strncat(prompt + sizeof(prompt) - strlen(term_code) - 1, term_code, strlen(term_code));
+ else
+ strncat(p, term_code, sizeof(term_code));
+ }
+ } else if (remotehostname)
+ snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname);
+ else
+ ast_copy_string(prompt, ASTERISK_PROMPT, sizeof(prompt));
+
+ return(prompt);
+}
+
+static char **ast_el_strtoarr(char *buf)
+{
+ char **match_list = NULL, **match_list_tmp, *retstr;
+ size_t match_list_len;
+ int matches = 0;
+
+ match_list_len = 1;
+ while ( (retstr = strsep(&buf, " ")) != NULL) {
+
+ if (!strcmp(retstr, AST_CLI_COMPLETE_EOF))
+ break;
+ if (matches + 1 >= match_list_len) {
+ match_list_len <<= 1;
+ if ((match_list_tmp = ast_realloc(match_list, match_list_len * sizeof(char *)))) {
+ match_list = match_list_tmp;
+ } else {
+ if (match_list)
+ ast_free(match_list);
+ return (char **) NULL;
+ }
+ }
+
+ match_list[matches++] = ast_strdup(retstr);
+ }
+
+ if (!match_list)
+ return (char **) NULL;
+
+ if (matches >= match_list_len) {
+ if ((match_list_tmp = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *)))) {
+ match_list = match_list_tmp;
+ } else {
+ if (match_list)
+ ast_free(match_list);
+ return (char **) NULL;
+ }
+ }
+
+ match_list[matches] = (char *) NULL;
+
+ return match_list;
+}
+
+static int ast_el_sort_compare(const void *i1, const void *i2)
+{
+ char *s1, *s2;
+
+ s1 = ((char **)i1)[0];
+ s2 = ((char **)i2)[0];
+
+ return strcasecmp(s1, s2);
+}
+
+static int ast_cli_display_match_list(char **matches, int len, int max)
+{
+ int i, idx, limit, count;
+ int screenwidth = 0;
+ int numoutput = 0, numoutputline = 0;
+
+ screenwidth = ast_get_termcols(STDOUT_FILENO);
+
+ /* find out how many entries can be put on one line, with two spaces between strings */
+ limit = screenwidth / (max + 2);
+ if (limit == 0)
+ limit = 1;
+
+ /* how many lines of output */
+ count = len / limit;
+ if (count * limit < len)
+ count++;
+
+ idx = 1;
+
+ qsort(&matches[0], (size_t)(len), sizeof(char *), ast_el_sort_compare);
+
+ for (; count > 0; count--) {
+ numoutputline = 0;
+ for (i=0; i < limit && matches[idx]; i++, idx++) {
+
+ /* Don't print dupes */
+ if ( (matches[idx+1] != NULL && strcmp(matches[idx], matches[idx+1]) == 0 ) ) {
+ i--;
+ ast_free(matches[idx]);
+ matches[idx] = NULL;
+ continue;
+ }
+
+ numoutput++;
+ numoutputline++;
+ fprintf(stdout, "%-*s ", max, matches[idx]);
+ ast_free(matches[idx]);
+ matches[idx] = NULL;
+ }
+ if (numoutputline > 0)
+ fprintf(stdout, "\n");
+ }
+
+ return numoutput;
+}
+
+
+static char *cli_complete(EditLine *el, int ch)
+{
+ int len = 0;
+ char *ptr;
+ int nummatches = 0;
+ char **matches;
+ int retval = CC_ERROR;
+ char buf[2048];
+ int res;
+
+ LineInfo *lf = (LineInfo *)el_line(el);
+
+ *(char *)lf->cursor = '\0';
+ ptr = (char *)lf->cursor;
+ if (ptr) {
+ while (ptr > lf->buffer) {
+ if (isspace(*ptr)) {
+ ptr++;
+ break;
+ }
+ ptr--;
+ }
+ }
+
+ len = lf->cursor - ptr;
+
+ if (ast_opt_remote) {
+ snprintf(buf, sizeof(buf),"_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr);
+ fdprint(ast_consock, buf);
+ res = read(ast_consock, buf, sizeof(buf));
+ buf[res] = '\0';
+ nummatches = atoi(buf);
+
+ if (nummatches > 0) {
+ char *mbuf;
+ int mlen = 0, maxmbuf = 2048;
+ /* Start with a 2048 byte buffer */
+ if (!(mbuf = ast_malloc(maxmbuf)))
+ return (char *)(CC_ERROR);
+ snprintf(buf, sizeof(buf),"_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr);
+ fdprint(ast_consock, buf);
+ res = 0;
+ mbuf[0] = '\0';
+ while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) {
+ if (mlen + 1024 > maxmbuf) {
+ /* Every step increment buffer 1024 bytes */
+ maxmbuf += 1024;
+ if (!(mbuf = ast_realloc(mbuf, maxmbuf)))
+ return (char *)(CC_ERROR);
+ }
+ /* Only read 1024 bytes at a time */
+ res = read(ast_consock, mbuf + mlen, 1024);
+ if (res > 0)
+ mlen += res;
+ }
+ mbuf[mlen] = '\0';
+
+ matches = ast_el_strtoarr(mbuf);
+ ast_free(mbuf);
+ } else
+ matches = (char **) NULL;
+ } else {
+ char **p, *oldbuf=NULL;
+ nummatches = 0;
+ matches = ast_cli_completion_matches((char *)lf->buffer,ptr);
+ for (p = matches; p && *p; p++) {
+ if (!oldbuf || strcmp(*p,oldbuf))
+ nummatches++;
+ oldbuf = *p;
+ }
+ }
+
+ if (matches) {
+ int i;
+ int matches_num, maxlen, match_len;
+
+ if (matches[0][0] != '\0') {
+ el_deletestr(el, (int) len);
+ el_insertstr(el, matches[0]);
+ retval = CC_REFRESH;
+ }
+
+ if (nummatches == 1) {
+ /* Found an exact match */
+ el_insertstr(el, " ");
+ retval = CC_REFRESH;
+ } else {
+ /* Must be more than one match */
+ for (i=1, maxlen=0; matches[i]; i++) {
+ match_len = strlen(matches[i]);
+ if (match_len > maxlen)
+ maxlen = match_len;
+ }
+ matches_num = i - 1;
+ if (matches_num >1) {
+ fprintf(stdout, "\n");
+ ast_cli_display_match_list(matches, nummatches, maxlen);
+ retval = CC_REDISPLAY;
+ } else {
+ el_insertstr(el," ");
+ retval = CC_REFRESH;
+ }
+ }
+ for (i = 0; matches[i]; i++)
+ ast_free(matches[i]);
+ ast_free(matches);
+ }
+
+ return (char *)(long)retval;
+}
+
+static int ast_el_initialize(void)
+{
+ HistEvent ev;
+ char *editor = getenv("AST_EDITOR");
+
+ if (el != NULL)
+ el_end(el);
+ if (el_hist != NULL)
+ history_end(el_hist);
+
+ el = el_init("asterisk", stdin, stdout, stderr);
+ el_set(el, EL_PROMPT, cli_prompt);
+
+ el_set(el, EL_EDITMODE, 1);
+ el_set(el, EL_EDITOR, editor ? editor : "emacs");
+ el_hist = history_init();
+ if (!el || !el_hist)
+ return -1;
+
+ /* setup history with 100 entries */
+ history(el_hist, &ev, H_SETSIZE, 100);
+
+ el_set(el, EL_HIST, history, el_hist);
+
+ el_set(el, EL_ADDFN, "ed-complete", "Complete argument", cli_complete);
+ /* Bind <tab> to command completion */
+ el_set(el, EL_BIND, "^I", "ed-complete", NULL);
+ /* Bind ? to command completion */
+ el_set(el, EL_BIND, "?", "ed-complete", NULL);
+ /* Bind ^D to redisplay */
+ el_set(el, EL_BIND, "^D", "ed-redisplay", NULL);
+
+ return 0;
+}
+
+static int ast_el_add_history(char *buf)
+{
+ HistEvent ev;
+
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+ if (strlen(buf) > 256)
+ return 0;
+ return (history(el_hist, &ev, H_ENTER, buf));
+}
+
+static int ast_el_write_history(char *filename)
+{
+ HistEvent ev;
+
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ return (history(el_hist, &ev, H_SAVE, filename));
+}
+
+static int ast_el_read_history(char *filename)
+{
+ char buf[256];
+ FILE *f;
+ int ret = -1;
+
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ if ((f = fopen(filename, "r")) == NULL)
+ return ret;
+
+ while (!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!strcmp(buf, "_HiStOrY_V2_\n"))
+ continue;
+ if (ast_all_zeros(buf))
+ continue;
+ if ((ret = ast_el_add_history(buf)) == -1)
+ break;
+ }
+ fclose(f);
+
+ return ret;
+}
+
+static void ast_remotecontrol(char * data)
+{
+ char buf[80];
+ int res;
+ char filename[80] = "";
+ char *hostname;
+ char *cpid;
+ char *version;
+ int pid;
+ char tmp[80];
+ char *stringp = NULL;
+
+ char *ebuf;
+ int num = 0;
+
+ read(ast_consock, buf, sizeof(buf));
+ if (data)
+ write(ast_consock, data, strlen(data) + 1);
+ stringp = buf;
+ hostname = strsep(&stringp, "/");
+ cpid = strsep(&stringp, "/");
+ version = strsep(&stringp, "\n");
+ if (!version)
+ version = "<Version Unknown>";
+ stringp = hostname;
+ strsep(&stringp, ".");
+ if (cpid)
+ pid = atoi(cpid);
+ else
+ pid = -1;
+ if (!data) {
+ snprintf(tmp, sizeof(tmp), "core set verbose atleast %d", option_verbose);
+ fdprint(ast_consock, tmp);
+ snprintf(tmp, sizeof(tmp), "core set debug atleast %d", option_debug);
+ fdprint(ast_consock, tmp);
+ if (!ast_opt_mute)
+ fdprint(ast_consock, "logger mute silent");
+ else
+ printf("log and verbose output currently muted ('logger mute' to unmute)\n");
+ }
+ ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
+ remotehostname = hostname;
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ el_set(el, EL_GETCFN, ast_el_read_char);
+
+ if (!ast_strlen_zero(filename))
+ ast_el_read_history(filename);
+
+ if (ast_opt_exec && data) { /* hack to print output then exit if asterisk -rx is used */
+ char tempchar;
+ struct pollfd fds;
+ fds.fd = ast_consock;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ while (poll(&fds, 1, 100) > 0)
+ ast_el_read_char(el, &tempchar);
+ return;
+ }
+ for (;;) {
+ ebuf = (char *)el_gets(el, &num);
+
+ if (!ebuf && write(1, "", 1) < 0)
+ break;
+
+ if (!ast_strlen_zero(ebuf)) {
+ if (ebuf[strlen(ebuf)-1] == '\n')
+ ebuf[strlen(ebuf)-1] = '\0';
+ if (!remoteconsolehandler(ebuf)) {
+ res = write(ast_consock, ebuf, strlen(ebuf) + 1);
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
+ break;
+ }
+ }
+ }
+ }
+ printf("\nDisconnected from Asterisk server\n");
+}
+
+static int show_version(void)
+{
+ printf("Asterisk %s\n", ast_get_version());
+ return 0;
+}
+
+static int show_cli_help(void) {
+ printf("Asterisk %s, Copyright (C) 1999 - 2007, Digium, Inc. and others.\n", ast_get_version());
+ printf("Usage: asterisk [OPTIONS]\n");
+ printf("Valid Options:\n");
+ printf(" -V Display version number and exit\n");
+ printf(" -C <configfile> Use an alternate configuration file\n");
+ printf(" -G <group> Run as a group other than the caller\n");
+ printf(" -U <user> Run as a user other than the caller\n");
+ printf(" -c Provide console CLI\n");
+ printf(" -d Enable extra debugging\n");
+#if HAVE_WORKING_FORK
+ printf(" -f Do not fork\n");
+ printf(" -F Always fork\n");
+#endif
+ printf(" -g Dump core in case of a crash\n");
+ printf(" -h This help screen\n");
+ printf(" -i Initialize crypto keys at startup\n");
+ printf(" -I Enable internal timing if Zaptel timer is available\n");
+ printf(" -L <load> Limit the maximum load average before rejecting new calls\n");
+ printf(" -M <value> Limit the maximum number of calls to the specified value\n");
+ printf(" -m Mute debugging and console output on the console\n");
+ printf(" -n Disable console colorization\n");
+ printf(" -p Run as pseudo-realtime thread\n");
+ printf(" -q Quiet mode (suppress output)\n");
+ printf(" -r Connect to Asterisk on this machine\n");
+ printf(" -R Same as -r, except attempt to reconnect if disconnected\n");
+ printf(" -t Record soundfiles in /var/tmp and move them where they\n");
+ printf(" belong after they are done\n");
+ printf(" -T Display the time in [Mmm dd hh:mm:ss] format for each line\n");
+ printf(" of output to the CLI\n");
+ printf(" -v Increase verbosity (multiple v's = more verbose)\n");
+ printf(" -x <cmd> Execute command <cmd> (only valid with -r)\n");
+ printf(" -s <socket> Connect to Asterisk via socket <socket> (only valid with -r)\n");
+ printf("\n");
+ return 0;
+}
+
+static void ast_readconfig(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ char *config = DEFAULT_CONFIG_FILE;
+ char hostname[MAXHOSTNAMELEN] = "";
+ struct ast_flags config_flags = { 0 };
+ struct {
+ unsigned int dbdir:1;
+ unsigned int keydir:1;
+ } found = { 0, 0 };
+
+ if (ast_opt_override_config) {
+ cfg = ast_config_load(ast_config_AST_CONFIG_FILE, config_flags);
+ if (!cfg)
+ ast_log(LOG_WARNING, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
+ } else
+ cfg = ast_config_load(config, config_flags);
+
+ /* init with buildtime config */
+ ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
+ ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
+ ast_copy_string(cfg_paths.module_dir, DEFAULT_MODULE_DIR, sizeof(cfg_paths.module_dir));
+ snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir) - 1, "%s/monitor", cfg_paths.spool_dir);
+ ast_copy_string(cfg_paths.var_dir, DEFAULT_VAR_DIR, sizeof(cfg_paths.var_dir));
+ ast_copy_string(cfg_paths.data_dir, DEFAULT_DATA_DIR, sizeof(cfg_paths.data_dir));
+ ast_copy_string(cfg_paths.log_dir, DEFAULT_LOG_DIR, sizeof(cfg_paths.log_dir));
+ ast_copy_string(cfg_paths.agi_dir, DEFAULT_AGI_DIR, sizeof(cfg_paths.agi_dir));
+ ast_copy_string(cfg_paths.db_path, DEFAULT_DB, sizeof(cfg_paths.db_path));
+ ast_copy_string(cfg_paths.key_dir, DEFAULT_KEY_DIR, sizeof(cfg_paths.key_dir));
+ ast_copy_string(cfg_paths.pid_path, DEFAULT_PID, sizeof(cfg_paths.pid_path));
+ ast_copy_string(cfg_paths.socket_path, DEFAULT_SOCKET, sizeof(cfg_paths.socket_path));
+ ast_copy_string(cfg_paths.run_dir, DEFAULT_RUN_DIR, sizeof(cfg_paths.run_dir));
+
+ /* no asterisk.conf? no problem, use buildtime config! */
+ if (!cfg) {
+ return;
+ }
+
+ for (v = ast_variable_browse(cfg, "files"); v; v = v->next) {
+ if (!strcasecmp(v->name, "astctlpermissions"))
+ ast_copy_string(ast_config_AST_CTL_PERMISSIONS, v->value, sizeof(ast_config_AST_CTL_PERMISSIONS));
+ else if (!strcasecmp(v->name, "astctlowner"))
+ ast_copy_string(ast_config_AST_CTL_OWNER, v->value, sizeof(ast_config_AST_CTL_OWNER));
+ else if (!strcasecmp(v->name, "astctlgroup"))
+ ast_copy_string(ast_config_AST_CTL_GROUP, v->value, sizeof(ast_config_AST_CTL_GROUP));
+ else if (!strcasecmp(v->name, "astctl"))
+ ast_copy_string(ast_config_AST_CTL, v->value, sizeof(ast_config_AST_CTL));
+ }
+
+ for (v = ast_variable_browse(cfg, "directories"); v; v = v->next) {
+ if (!strcasecmp(v->name, "astetcdir")) {
+ ast_copy_string(cfg_paths.config_dir, v->value, sizeof(cfg_paths.config_dir));
+ } else if (!strcasecmp(v->name, "astspooldir")) {
+ ast_copy_string(cfg_paths.spool_dir, v->value, sizeof(cfg_paths.spool_dir));
+ snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir) - 1, "%s/monitor", v->value);
+ } else if (!strcasecmp(v->name, "astvarlibdir")) {
+ ast_copy_string(cfg_paths.var_dir, v->value, sizeof(cfg_paths.var_dir));
+ if (!found.dbdir)
+ snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
+ } else if (!strcasecmp(v->name, "astdbdir")) {
+ snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
+ found.dbdir = 1;
+ } else if (!strcasecmp(v->name, "astdatadir")) {
+ ast_copy_string(cfg_paths.data_dir, v->value, sizeof(cfg_paths.data_dir));
+ if (!found.keydir)
+ snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
+ } else if (!strcasecmp(v->name, "astkeydir")) {
+ snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
+ found.keydir = 1;
+ } else if (!strcasecmp(v->name, "astlogdir")) {
+ ast_copy_string(cfg_paths.log_dir, v->value, sizeof(cfg_paths.log_dir));
+ } else if (!strcasecmp(v->name, "astagidir")) {
+ ast_copy_string(cfg_paths.agi_dir, v->value, sizeof(cfg_paths.agi_dir));
+ } else if (!strcasecmp(v->name, "astrundir")) {
+ snprintf(cfg_paths.pid_path, sizeof(cfg_paths.pid_path), "%s/%s", v->value, "asterisk.pid");
+ snprintf(cfg_paths.socket_path, sizeof(cfg_paths.socket_path), "%s/%s", v->value, ast_config_AST_CTL);
+ ast_copy_string(cfg_paths.run_dir, v->value, sizeof(cfg_paths.run_dir));
+ } else if (!strcasecmp(v->name, "astmoddir")) {
+ ast_copy_string(cfg_paths.module_dir, v->value, sizeof(cfg_paths.module_dir));
+ }
+ }
+
+ for (v = ast_variable_browse(cfg, "options"); v; v = v->next) {
+ /* verbose level (-v at startup) */
+ if (!strcasecmp(v->name, "verbose")) {
+ option_verbose = atoi(v->value);
+ /* whether or not to force timestamping in CLI verbose output. (-T at startup) */
+ } else if (!strcasecmp(v->name, "timestamp")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TIMESTAMP);
+ /* whether or not to support #exec in config files */
+ } else if (!strcasecmp(v->name, "execincludes")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_EXEC_INCLUDES);
+ /* debug level (-d at startup) */
+ } else if (!strcasecmp(v->name, "debug")) {
+ option_debug = 0;
+ if (sscanf(v->value, "%d", &option_debug) != 1) {
+ option_debug = ast_true(v->value);
+ }
+#if HAVE_WORKING_FORK
+ /* Disable forking (-f at startup) */
+ } else if (!strcasecmp(v->name, "nofork")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_FORK);
+ /* Always fork, even if verbose or debug are enabled (-F at startup) */
+ } else if (!strcasecmp(v->name, "alwaysfork")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_ALWAYS_FORK);
+#endif
+ /* Run quietly (-q at startup ) */
+ } else if (!strcasecmp(v->name, "quiet")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_QUIET);
+ /* Run as console (-c at startup, implies nofork) */
+ } else if (!strcasecmp(v->name, "console")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CONSOLE);
+ /* Run with high priority if the O/S permits (-p at startup) */
+ } else if (!strcasecmp(v->name, "highpriority")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIGH_PRIORITY);
+ /* Initialize RSA auth keys (IAX2) (-i at startup) */
+ } else if (!strcasecmp(v->name, "initcrypto")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INIT_KEYS);
+ /* Disable ANSI colors for console (-c at startup) */
+ } else if (!strcasecmp(v->name, "nocolor")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_COLOR);
+ /* Disable some usage warnings for picky people :p */
+ } else if (!strcasecmp(v->name, "dontwarn")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DONT_WARN);
+ /* Dump core in case of crash (-g) */
+ } else if (!strcasecmp(v->name, "dumpcore")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DUMP_CORE);
+ /* Cache recorded sound files to another directory during recording */
+ } else if (!strcasecmp(v->name, "cache_record_files")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_RECORD_FILES);
+ /* Specify cache directory */
+ } else if (!strcasecmp(v->name, "record_cache_dir")) {
+ ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN);
+ /* Build transcode paths via SLINEAR, instead of directly */
+ } else if (!strcasecmp(v->name, "transcode_via_sln")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSCODE_VIA_SLIN);
+ /* Transmit SLINEAR silence while a channel is being recorded */
+ } else if (!strcasecmp(v->name, "transmit_silence_during_record")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSMIT_SILENCE);
+ /* Enable internal timing */
+ } else if (!strcasecmp(v->name, "internal_timing")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INTERNAL_TIMING);
+ } else if (!strcasecmp(v->name, "maxcalls")) {
+ if ((sscanf(v->value, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0)) {
+ option_maxcalls = 0;
+ }
+ } else if (!strcasecmp(v->name, "maxload")) {
+ double test[1];
+
+ if (getloadavg(test, 1) == -1) {
+ ast_log(LOG_ERROR, "Cannot obtain load average on this system. 'maxload' option disabled.\n");
+ option_maxload = 0.0;
+ } else if ((sscanf(v->value, "%lf", &option_maxload) != 1) || (option_maxload < 0.0)) {
+ option_maxload = 0.0;
+ }
+ /* Set the maximum amount of open files */
+ } else if (!strcasecmp(v->name, "maxfiles")) {
+ option_maxfiles = atoi(v->value);
+ set_ulimit(option_maxfiles);
+ /* What user to run as */
+ } else if (!strcasecmp(v->name, "runuser")) {
+ ast_copy_string(cfg_paths.run_user, v->value, sizeof(cfg_paths.run_user));
+ /* What group to run as */
+ } else if (!strcasecmp(v->name, "rungroup")) {
+ ast_copy_string(cfg_paths.run_group, v->value, sizeof(cfg_paths.run_group));
+ } else if (!strcasecmp(v->name, "systemname")) {
+ ast_copy_string(cfg_paths.system_name, v->value, sizeof(cfg_paths.system_name));
+ } else if (!strcasecmp(v->name, "autosystemname")) {
+ if (ast_true(v->value)) {
+ if (!gethostname(hostname, sizeof(hostname) - 1))
+ ast_copy_string(cfg_paths.system_name, hostname, sizeof(cfg_paths.system_name));
+ else {
+ if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)){
+ ast_copy_string(cfg_paths.system_name, "localhost", sizeof(cfg_paths.system_name));
+ }
+ ast_log(LOG_ERROR, "Cannot obtain hostname for this system. Using '%s' instead.\n", ast_config_AST_SYSTEM_NAME);
+ }
+ }
+ } else if (!strcasecmp(v->name, "languageprefix")) {
+ ast_language_is_prefix = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "lockmode")) {
+ if (!strcasecmp(v->value, "lockfile")) {
+ ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+ } else if (!strcasecmp(v->value, "flock")) {
+ ast_set_lock_type(AST_LOCK_TYPE_FLOCK);
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a valid setting for the lockmode option, "
+ "defaulting to 'lockfile'\n", v->value);
+ ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+ }
+#if defined(HAVE_SYSINFO)
+ } else if (!strcasecmp(v->name, "minmemfree")) {
+ /* specify the minimum amount of free memory to retain. Asterisk should stop accepting new calls
+ * if the amount of free memory falls below this watermark */
+ if ((sscanf(v->value, "%ld", &option_minmemfree) != 1) || (option_minmemfree < 0)) {
+ option_minmemfree = 0;
+ }
+#endif
+ }
+ }
+ ast_config_destroy(cfg);
+}
+
+static void *monitor_sig_flags(void *unused)
+{
+ for (;;) {
+ struct pollfd p = { sig_alert_pipe[0], POLLIN, 0 };
+ int a;
+ poll(&p, 1, -1);
+ if (sig_flags.need_reload) {
+ sig_flags.need_reload = 0;
+ ast_module_reload(NULL);
+ }
+ if (sig_flags.need_quit) {
+ sig_flags.need_quit = 0;
+ quit_handler(0, 0, 1, 0);
+ }
+ read(sig_alert_pipe[0], &a, sizeof(a));
+ }
+
+ return NULL;
+}
+
+static void *canary_thread(void *unused)
+{
+ struct stat canary_stat;
+ struct timeval tv;
+
+ /* Give the canary time to sing */
+ sleep(120);
+
+ for (;;) {
+ stat(canary_filename, &canary_stat);
+ tv = ast_tvnow();
+ if (tv.tv_sec > canary_stat.st_mtime + 60) {
+ ast_log(LOG_WARNING, "The canary is no more. He has ceased to be! He's expired and gone to meet his maker! He's a stiff! Bereft of life, he rests in peace. His metabolic processes are now history! He's off the twig! He's kicked the bucket. He's shuffled off his mortal coil, run down the curtain, and joined the bleeding choir invisible!! THIS is an EX-CANARY. (Reducing priority)\n");
+ ast_set_priority(0);
+ pthread_exit(NULL);
+ }
+
+ /* Check the canary once a minute */
+ sleep(60);
+ }
+}
+
+/* Used by libc's atexit(3) function */
+static void canary_exit(void)
+{
+ if (canary_pid > 0)
+ kill(canary_pid, SIGKILL);
+}
+
+static void run_startup_commands(void)
+{
+ char filename[PATH_MAX];
+ char buf[256];
+ FILE *f;
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ return;
+
+ snprintf(filename, sizeof(filename), "%s/startup_commands", ast_config_AST_CONFIG_DIR);
+
+ if (!(f = fopen(filename, "r"))) {
+ close(fd);
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ size_t res = strlen(buf);
+
+ if (!res)
+ continue;
+
+ if (buf[res - 1] == '\n')
+ buf[res - 1] = '\0';
+
+ ast_cli_command(fd, buf);
+ }
+
+ fclose(f);
+ close(fd);
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ char filename[80] = "";
+ char hostname[MAXHOSTNAMELEN] = "";
+ char tmp[80];
+ char * xarg = NULL;
+ int x;
+ FILE *f;
+ sigset_t sigs;
+ int num;
+ int isroot = 1;
+ char *buf;
+ const char *runuser = NULL, *rungroup = NULL;
+ char *remotesock = NULL;
+
+ /* Remember original args for restart */
+ if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) {
+ fprintf(stderr, "Truncating argument size to %d\n", (int)(sizeof(_argv) / sizeof(_argv[0])) - 1);
+ argc = sizeof(_argv) / sizeof(_argv[0]) - 1;
+ }
+ for (x=0; x<argc; x++)
+ _argv[x] = argv[x];
+ _argv[x] = NULL;
+
+ if (geteuid() != 0)
+ isroot = 0;
+
+ /* if the progname is rasterisk consider it a remote console */
+ if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) {
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
+ }
+ if (gethostname(hostname, sizeof(hostname)-1))
+ ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
+ ast_mainpid = getpid();
+ ast_ulaw_init();
+ ast_alaw_init();
+ callerid_init();
+ ast_builtins_init();
+ ast_utils_init();
+ tdd_init();
+
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ /* Check for options */
+ while ((c = getopt(argc, argv, "mtThfFdvVqprRgciInx:U:G:C:L:M:e:s:")) != -1) {
+ switch (c) {
+#if defined(HAVE_SYSINFO)
+ case 'e':
+ if ((sscanf(&optarg[1], "%ld", &option_minmemfree) != 1) || (option_minmemfree < 0)) {
+ option_minmemfree = 0;
+ }
+ break;
+#endif
+#if HAVE_WORKING_FORK
+ case 'F':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);
+ break;
+ case 'f':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ break;
+#endif
+ case 'd':
+ option_debug++;
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ break;
+ case 'c':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_CONSOLE);
+ break;
+ case 'n':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_COLOR);
+ break;
+ case 'r':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
+ break;
+ case 'R':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE | AST_OPT_FLAG_RECONNECT);
+ break;
+ case 'p':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY);
+ break;
+ case 'v':
+ option_verbose++;
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ break;
+ case 'm':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_MUTE);
+ break;
+ case 'M':
+ if ((sscanf(optarg, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0))
+ option_maxcalls = 0;
+ break;
+ case 'L':
+ if ((sscanf(optarg, "%lf", &option_maxload) != 1) || (option_maxload < 0.0))
+ option_maxload = 0.0;
+ break;
+ case 'q':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_QUIET);
+ break;
+ case 't':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES);
+ break;
+ case 'T':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP);
+ break;
+ case 'x':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC);
+ xarg = ast_strdupa(optarg);
+ break;
+ case 'C':
+ ast_copy_string(cfg_paths.config_file, optarg, sizeof(cfg_paths.config_file));
+ ast_set_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG);
+ break;
+ case 'I':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING);
+ break;
+ case 'i':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS);
+ break;
+ case 'g':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE);
+ break;
+ case 'h':
+ show_cli_help();
+ exit(0);
+ case 'V':
+ show_version();
+ exit(0);
+ case 'U':
+ runuser = ast_strdupa(optarg);
+ break;
+ case 'G':
+ rungroup = ast_strdupa(optarg);
+ break;
+ case 's':
+ remotesock = ast_strdupa(optarg);
+ break;
+ case '?':
+ exit(1);
+ }
+ }
+
+ if (ast_opt_console || option_verbose || (ast_opt_remote && !ast_opt_exec)) {
+ ast_register_verbose(console_verboser);
+ WELCOME_MESSAGE;
+ }
+
+ if (ast_opt_console && !option_verbose)
+ ast_verbose("[ Booting...\n");
+
+ if (ast_opt_always_fork && (ast_opt_remote || ast_opt_console)) {
+ ast_log(LOG_WARNING, "'alwaysfork' is not compatible with console or remote console mode; ignored\n");
+ ast_clear_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);
+ }
+
+ /* For remote connections, change the name of the remote connection.
+ * We do this for the benefit of init scripts (which need to know if/when
+ * the main asterisk process has died yet). */
+ if (ast_opt_remote) {
+ strcpy(argv[0], "rasterisk");
+ for (x = 1; x < argc; x++) {
+ argv[x] = argv[0] + 10;
+ }
+ }
+
+ if (ast_opt_console && !option_verbose)
+ ast_verbose("[ Reading Master Configuration ]\n");
+ ast_readconfig();
+
+ if (ast_opt_remote && remotesock != NULL)
+ ast_copy_string((char *) cfg_paths.socket_path, remotesock, sizeof(cfg_paths.socket_path));
+
+ if (!ast_language_is_prefix && !ast_opt_remote)
+ ast_log(LOG_WARNING, "The 'languageprefix' option in asterisk.conf is deprecated; in a future release it will be removed, and your sound files will need to be organized in the 'new style' language layout.\n");
+
+ if (ast_opt_dump_core) {
+ struct rlimit l;
+ memset(&l, 0, sizeof(l));
+ l.rlim_cur = RLIM_INFINITY;
+ l.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &l)) {
+ ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n", strerror(errno));
+ }
+ }
+
+ if ((!rungroup) && !ast_strlen_zero(ast_config_AST_RUN_GROUP))
+ rungroup = ast_config_AST_RUN_GROUP;
+ if ((!runuser) && !ast_strlen_zero(ast_config_AST_RUN_USER))
+ runuser = ast_config_AST_RUN_USER;
+
+ /* Must install this signal handler up here to ensure that if the canary
+ * fails to execute that it doesn't kill the Asterisk process.
+ */
+ signal(SIGCHLD, child_handler);
+
+#ifndef __CYGWIN__
+
+ if (isroot) {
+ ast_set_priority(ast_opt_high_priority);
+ if (ast_opt_high_priority) {
+ snprintf(canary_filename, sizeof(canary_filename), "%s/alt.asterisk.canary.tweet.tweet.tweet", ast_config_AST_RUN_DIR);
+
+ canary_pid = fork();
+ if (canary_pid == 0) {
+ char canary_binary[128], *lastslash;
+ int fd;
+
+ /* Reset signal handler */
+ signal(SIGCHLD, SIG_DFL);
+
+ for (fd = 0; fd < 100; fd++)
+ close(fd);
+
+ execlp("astcanary", "astcanary", canary_filename, (char *)NULL);
+
+ /* If not found, try the same path as used to execute asterisk */
+ ast_copy_string(canary_binary, argv[0], sizeof(canary_binary));
+ if ((lastslash = strrchr(canary_binary, '/'))) {
+ ast_copy_string(lastslash + 1, "astcanary", sizeof(canary_binary) + canary_binary - (lastslash + 1));
+ execl(canary_binary, "astcanary", canary_filename, (char *)NULL);
+ }
+
+ /* Should never happen */
+ _exit(1);
+ } else if (canary_pid > 0) {
+ pthread_t dont_care;
+ ast_pthread_create_detached(&dont_care, NULL, canary_thread, NULL);
+ }
+
+ /* Kill the canary when we exit */
+ atexit(canary_exit);
+ }
+ }
+
+ if (isroot && rungroup) {
+ struct group *gr;
+ gr = getgrnam(rungroup);
+ if (!gr) {
+ ast_log(LOG_WARNING, "No such group '%s'!\n", rungroup);
+ exit(1);
+ }
+ if (setgid(gr->gr_gid)) {
+ ast_log(LOG_WARNING, "Unable to setgid to %d (%s)\n", (int)gr->gr_gid, rungroup);
+ exit(1);
+ }
+ if (setgroups(0, NULL)) {
+ ast_log(LOG_WARNING, "Unable to drop unneeded groups\n");
+ exit(1);
+ }
+ if (option_verbose)
+ ast_verbose("Running as group '%s'\n", rungroup);
+ }
+
+ if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) {
+#ifdef HAVE_CAP
+ int has_cap = 1;
+#endif /* HAVE_CAP */
+ struct passwd *pw;
+ pw = getpwnam(runuser);
+ if (!pw) {
+ ast_log(LOG_WARNING, "No such user '%s'!\n", runuser);
+ exit(1);
+ }
+#ifdef HAVE_CAP
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
+ ast_log(LOG_WARNING, "Unable to keep capabilities.\n");
+ has_cap = 0;
+ }
+#endif /* HAVE_CAP */
+ if (!isroot && pw->pw_uid != geteuid()) {
+ ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser);
+ exit(1);
+ }
+ if (!rungroup) {
+ if (setgid(pw->pw_gid)) {
+ ast_log(LOG_WARNING, "Unable to setgid to %d!\n", (int)pw->pw_gid);
+ exit(1);
+ }
+ if (isroot && initgroups(pw->pw_name, pw->pw_gid)) {
+ ast_log(LOG_WARNING, "Unable to init groups for '%s'\n", runuser);
+ exit(1);
+ }
+ }
+ if (setuid(pw->pw_uid)) {
+ ast_log(LOG_WARNING, "Unable to setuid to %d (%s)\n", (int)pw->pw_uid, runuser);
+ exit(1);
+ }
+ if (option_verbose)
+ ast_verbose("Running as user '%s'\n", runuser);
+#ifdef HAVE_CAP
+ if (has_cap) {
+ cap_t cap;
+
+ cap = cap_from_text("cap_net_admin=ep");
+
+ if (cap_set_proc(cap))
+ ast_log(LOG_WARNING, "Unable to install capabilities.\n");
+
+ if (cap_free(cap))
+ ast_log(LOG_WARNING, "Unable to drop capabilities.\n");
+ }
+#endif /* HAVE_CAP */
+ }
+
+#endif /* __CYGWIN__ */
+
+#ifdef linux
+ if (geteuid() && ast_opt_dump_core) {
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+ ast_log(LOG_WARNING, "Unable to set the process for core dumps after changing to a non-root user. %s\n", strerror(errno));
+ }
+ }
+#endif
+
+ ast_term_init();
+ printf(term_end());
+ fflush(stdout);
+
+ if (ast_opt_console && !option_verbose)
+ ast_verbose("[ Initializing Custom Configuration Options ]\n");
+ /* custom config setup */
+ register_config_cli();
+ read_config_maps();
+
+ if (ast_opt_console) {
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ if (!ast_strlen_zero(filename))
+ ast_el_read_history(filename);
+ }
+
+ if (ast_tryconnect()) {
+ /* One is already running */
+ if (ast_opt_remote) {
+ if (ast_opt_exec) {
+ ast_remotecontrol(xarg);
+ quit_handler(0, 0, 0, 0);
+ exit(0);
+ }
+ printf(term_quit());
+ ast_remotecontrol(NULL);
+ quit_handler(0, 0, 0, 0);
+ exit(0);
+ } else {
+ ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect.\n", ast_config_AST_SOCKET);
+ printf(term_quit());
+ exit(1);
+ }
+ } else if (ast_opt_remote || ast_opt_exec) {
+ ast_log(LOG_ERROR, "Unable to connect to remote asterisk (does %s exist?)\n", ast_config_AST_SOCKET);
+ printf(term_quit());
+ exit(1);
+ }
+ /* Blindly write pid file since we couldn't connect */
+ unlink(ast_config_AST_PID);
+ f = fopen(ast_config_AST_PID, "w");
+ if (f) {
+ fprintf(f, "%ld\n", (long)getpid());
+ fclose(f);
+ } else
+ ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
+
+#if HAVE_WORKING_FORK
+ if (ast_opt_always_fork || !ast_opt_no_fork) {
+ daemon(1, 0);
+ ast_mainpid = getpid();
+ /* Blindly re-write pid file since we are forking */
+ unlink(ast_config_AST_PID);
+ f = fopen(ast_config_AST_PID, "w");
+ if (f) {
+ fprintf(f, "%ld\n", (long)ast_mainpid);
+ fclose(f);
+ } else
+ ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
+ }
+#endif
+
+ /* Test recursive mutex locking. */
+ if (test_for_thread_safety())
+ ast_verbose("Warning! Asterisk is not thread safe.\n");
+
+ ast_event_init();
+
+ ast_makesocket();
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGHUP);
+ sigaddset(&sigs, SIGTERM);
+ sigaddset(&sigs, SIGINT);
+ sigaddset(&sigs, SIGPIPE);
+ sigaddset(&sigs, SIGWINCH);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+ signal(SIGURG, urg_handler);
+ signal(SIGINT, __quit_handler);
+ signal(SIGTERM, __quit_handler);
+ signal(SIGHUP, hup_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* ensure that the random number generators are seeded with a different value every time
+ Asterisk is started
+ */
+ srand((unsigned int) getpid() + (unsigned int) time(NULL));
+ initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool));
+
+ if (init_logger()) { /* Start logging subsystem */
+ printf(term_quit());
+ exit(1);
+ }
+
+ threadstorage_init();
+
+ astobj2_init();
+
+ if (load_modules(1)) { /* Load modules, pre-load only */
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (dnsmgr_init()) { /* Initialize the DNS manager */
+ printf(term_quit());
+ exit(1);
+ }
+
+ ast_http_init(); /* Start the HTTP server, if needed */
+
+ ast_channels_init();
+
+ if (init_manager()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_cdr_engine_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_device_state_engine_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ ast_rtp_init();
+
+ ast_udptl_init();
+
+ if (ast_image_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_file_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (load_pbx()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (init_framer()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (astdb_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_enum_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (load_modules(0)) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ dnsmgr_start_refresh();
+
+ /* We might have the option of showing a console, but for now just
+ do nothing... */
+ if (ast_opt_console && !option_verbose)
+ ast_verbose(" ]\n");
+ if (option_verbose || ast_opt_console)
+ ast_verbose(term_color(tmp, "Asterisk Ready.\n", COLOR_BRWHITE, COLOR_BLACK, sizeof(tmp)));
+ if (ast_opt_no_fork)
+ consolethread = pthread_self();
+
+ if (pipe(sig_alert_pipe))
+ sig_alert_pipe[0] = sig_alert_pipe[1] = -1;
+
+ ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED);
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+
+#ifdef __AST_DEBUG_MALLOC
+ __ast_mm_init();
+#endif
+
+ ast_lastreloadtime = ast_startuptime = ast_tvnow();
+ ast_cli_register_multiple(cli_asterisk, sizeof(cli_asterisk) / sizeof(struct ast_cli_entry));
+
+ run_startup_commands();
+
+ if (ast_opt_console) {
+ /* Console stuff now... */
+ /* Register our quit function */
+ char title[256];
+ pthread_t dont_care;
+
+ ast_pthread_create_detached(&dont_care, NULL, monitor_sig_flags, NULL);
+
+ set_icon("Asterisk");
+ snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %ld)", hostname, (long)ast_mainpid);
+ set_title(title);
+
+ for (;;) {
+ buf = (char *) el_gets(el, &num);
+
+ if (!buf && write(1, "", 1) < 0)
+ goto lostterm;
+
+ if (buf) {
+ if (buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = '\0';
+
+ consolehandler((char *)buf);
+ } else if (ast_opt_remote && (write(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n",
+ strlen("\nUse EXIT or QUIT to exit the asterisk console\n")) < 0)) {
+ /* Whoa, stdout disappeared from under us... Make /dev/null's */
+ int fd;
+ fd = open("/dev/null", O_RDWR);
+ if (fd > -1) {
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDIN_FILENO);
+ } else
+ ast_log(LOG_WARNING, "Failed to open /dev/null to recover from dead console. Bad things will happen!\n");
+ break;
+ }
+ }
+ }
+
+ monitor_sig_flags(NULL);
+
+lostterm:
+ return 0;
+}
diff --git a/trunk/main/astmm.c b/trunk/main/astmm.c
new file mode 100644
index 000000000..3d180acec
--- /dev/null
+++ b/trunk/main/astmm.c
@@ -0,0 +1,479 @@
+/*
+ * 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 Memory Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#ifdef __AST_DEBUG_MALLOC
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include <time.h>
+
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/strings.h"
+#include "asterisk/unaligned.h"
+
+#define SOME_PRIME 563
+
+enum func_type {
+ FUNC_CALLOC = 1,
+ FUNC_MALLOC,
+ FUNC_REALLOC,
+ FUNC_STRDUP,
+ FUNC_STRNDUP,
+ FUNC_VASPRINTF,
+ FUNC_ASPRINTF
+};
+
+/* Undefine all our macros */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef strdup
+#undef strndup
+#undef free
+#undef vasprintf
+#undef asprintf
+
+#define FENCE_MAGIC 0xdeadbeef
+
+static FILE *mmlog;
+
+static struct ast_region {
+ struct ast_region *next;
+ char file[40];
+ char func[40];
+ unsigned int lineno;
+ enum func_type which;
+ unsigned int cache; /* region was allocated as part of a cache pool */
+ size_t len;
+ unsigned int fence;
+ unsigned char data[0];
+} *regions[SOME_PRIME];
+
+#define HASH(a) \
+ (((unsigned long)(a)) % SOME_PRIME)
+
+/*! Tracking this mutex will cause infinite recursion, as the mutex tracking
+ * code allocates memory */
+AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
+
+#define astmm_log(...) \
+ do { \
+ fprintf(stderr, __VA_ARGS__); \
+ if (mmlog) { \
+ fprintf(mmlog, __VA_ARGS__); \
+ fflush(mmlog); \
+ } \
+ } while (0)
+
+static inline void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
+{
+ struct ast_region *reg;
+ void *ptr = NULL;
+ unsigned int *fence;
+ int hash;
+
+ if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
+ astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
+ "at line %d of %s\n", (int) size, func, lineno, file);
+ }
+
+ ast_copy_string(reg->file, file, sizeof(reg->file));
+ ast_copy_string(reg->func, func, sizeof(reg->func));
+ reg->lineno = lineno;
+ reg->len = size;
+ reg->which = which;
+ reg->cache = cache;
+ ptr = reg->data;
+ hash = HASH(ptr);
+ reg->fence = FENCE_MAGIC;
+ fence = (ptr + reg->len);
+ put_unaligned_uint32(fence, FENCE_MAGIC);
+
+ ast_mutex_lock(&reglock);
+ reg->next = regions[hash];
+ regions[hash] = reg;
+ ast_mutex_unlock(&reglock);
+
+ return ptr;
+}
+
+static inline size_t __ast_sizeof_region(void *ptr)
+{
+ int hash = HASH(ptr);
+ struct ast_region *reg;
+ size_t len = 0;
+
+ ast_mutex_lock(&reglock);
+ for (reg = regions[hash]; reg; reg = reg->next) {
+ if (reg->data == ptr) {
+ len = reg->len;
+ break;
+ }
+ }
+ ast_mutex_unlock(&reglock);
+
+ return len;
+}
+
+static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
+{
+ int hash = HASH(ptr);
+ struct ast_region *reg, *prev = NULL;
+ unsigned int *fence;
+
+ ast_mutex_lock(&reglock);
+ for (reg = regions[hash]; reg; reg = reg->next) {
+ if (reg->data == ptr) {
+ if (prev)
+ prev->next = reg->next;
+ else
+ regions[hash] = reg->next;
+ break;
+ }
+ prev = reg;
+ }
+ ast_mutex_unlock(&reglock);
+
+ if (reg) {
+ fence = (unsigned int *)(reg->data + reg->len);
+ if (reg->fence != FENCE_MAGIC) {
+ astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+ }
+ if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
+ astmm_log("WARNING: High fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+ }
+ free(reg);
+ } else {
+ astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
+ ptr, func, file, lineno);
+ }
+}
+
+void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
+ memset(ptr, 0, size * nmemb);
+
+ return ptr;
+}
+
+void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
+ memset(ptr, 0, size * nmemb);
+
+ return ptr;
+}
+
+void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
+{
+ return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
+}
+
+void __ast_free(void *ptr, const char *file, int lineno, const char *func)
+{
+ __ast_free_region(ptr, file, lineno, func);
+}
+
+void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
+{
+ void *tmp;
+ size_t len = 0;
+
+ if (ptr && !(len = __ast_sizeof_region(ptr))) {
+ astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
+ "line %d\n", ptr, func, file, lineno);
+ return NULL;
+ }
+
+ if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
+ return NULL;
+
+ if (len > size)
+ len = size;
+ if (ptr) {
+ memcpy(tmp, ptr, len);
+ __ast_free_region(ptr, file, lineno, func);
+ }
+
+ return tmp;
+}
+
+char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
+{
+ size_t len;
+ void *ptr;
+
+ if (!s)
+ return NULL;
+
+ len = strlen(s) + 1;
+ if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
+ strcpy(ptr, s);
+
+ return ptr;
+}
+
+char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
+{
+ size_t len;
+ void *ptr;
+
+ if (!s)
+ return NULL;
+
+ len = strlen(s) + 1;
+ if (len > n)
+ len = n;
+ if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
+ strcpy(ptr, s);
+
+ return ptr;
+}
+
+int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
+{
+ int size;
+ va_list ap, ap2;
+ char s;
+
+ *strp = NULL;
+ va_start(ap, fmt);
+ va_copy(ap2, ap);
+ size = vsnprintf(&s, 1, fmt, ap2);
+ va_end(ap2);
+ if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
+ va_end(ap);
+ return -1;
+ }
+ vsnprintf(*strp, size + 1, fmt, ap);
+ va_end(ap);
+
+ return size;
+}
+
+int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
+{
+ int size;
+ va_list ap2;
+ char s;
+
+ *strp = NULL;
+ va_copy(ap2, ap);
+ size = vsnprintf(&s, 1, fmt, ap2);
+ va_end(ap2);
+ if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
+ va_end(ap);
+ return -1;
+ }
+ vsnprintf(*strp, size + 1, fmt, ap);
+
+ return size;
+}
+
+static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ struct ast_region *reg;
+ unsigned int x;
+ unsigned int len = 0;
+ unsigned int cache_len = 0;
+ unsigned int count = 0;
+ unsigned int *fence;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "memory show allocations";
+ e->usage =
+ "Usage: memory show allocations [<file>]\n"
+ " Dumps a list of all segments of allocated memory, optionally\n"
+ " limited to those from a specific file\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&reglock);
+ for (x = 0; x < SOME_PRIME; x++) {
+ for (reg = regions[x]; reg; reg = reg->next) {
+ if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
+ fence = (unsigned int *)(reg->data + reg->len);
+ if (reg->fence != FENCE_MAGIC) {
+ astmm_log("WARNING: Low fence violation at %p, "
+ "in %s of %s, line %d\n", reg->data,
+ reg->func, reg->file, reg->lineno);
+ }
+ if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
+ astmm_log("WARNING: High fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+ }
+ }
+ if (!fn || !strcasecmp(fn, reg->file)) {
+ ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
+ (int) reg->len, reg->cache ? " (cache)" : "",
+ reg->func, reg->lineno, reg->file);
+ len += reg->len;
+ if (reg->cache)
+ cache_len += reg->len;
+ count++;
+ }
+ }
+ }
+ ast_mutex_unlock(&reglock);
+
+ if (cache_len)
+ ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ int x;
+ struct ast_region *reg;
+ unsigned int len = 0;
+ unsigned int cache_len = 0;
+ int count = 0;
+ struct file_summary {
+ char fn[80];
+ int len;
+ int cache_len;
+ int count;
+ struct file_summary *next;
+ } *list = NULL, *cur;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "memory show summary";
+ e->usage =
+ "Usage: memory show summary [<file>]\n"
+ " Summarizes heap memory allocations by file, or optionally\n"
+ "by function, if a file is specified\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&reglock);
+ for (x = 0; x < SOME_PRIME; x++) {
+ for (reg = regions[x]; reg; reg = reg->next) {
+ if (fn && strcasecmp(fn, reg->file))
+ continue;
+
+ for (cur = list; cur; cur = cur->next) {
+ if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
+ break;
+ }
+ if (!cur) {
+ cur = alloca(sizeof(*cur));
+ memset(cur, 0, sizeof(*cur));
+ ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
+ cur->next = list;
+ list = cur;
+ }
+
+ cur->len += reg->len;
+ if (reg->cache)
+ cur->cache_len += reg->len;
+ cur->count++;
+ }
+ }
+ ast_mutex_unlock(&reglock);
+
+ /* Dump the whole list */
+ for (cur = list; cur; cur = cur->next) {
+ len += cur->len;
+ cache_len += cur->cache_len;
+ count += cur->count;
+ if (cur->cache_len) {
+ if (fn) {
+ ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
+ cur->len, cur->cache_len, cur->count, cur->fn, fn);
+ } else {
+ ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
+ cur->len, cur->cache_len, cur->count, cur->fn);
+ }
+ } else {
+ if (fn) {
+ ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
+ cur->len, cur->count, cur->fn, fn);
+ } else {
+ ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n",
+ cur->len, cur->count, cur->fn);
+ }
+ }
+ }
+
+ if (cache_len)
+ ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_memory[] = {
+ AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
+ AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
+};
+
+void __ast_mm_init(void)
+{
+ char filename[PATH_MAX];
+
+ ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
+
+ snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
+
+ if (option_verbose)
+ ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
+
+ if ((mmlog = fopen(filename, "a+"))) {
+ fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
+ fflush(mmlog);
+ }
+}
+
+#endif
diff --git a/trunk/main/astobj2.c b/trunk/main/astobj2.c
new file mode 100644
index 000000000..2d3611889
--- /dev/null
+++ b/trunk/main/astobj2.c
@@ -0,0 +1,732 @@
+/*
+ * astobj2 - replacement containers for asterisk data structures.
+ *
+ * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
+ *
+ * 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.
+ */
+
+/*
+ * Function implementing astobj2 objects.
+ */
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+
+/*!
+ * astobj2 objects are always preceded by this data structure,
+ * which contains a lock, a reference counter,
+ * the flags and a pointer to a destructor.
+ * The refcount is used to decide when it is time to
+ * invoke the destructor.
+ * The magic number is used for consistency check.
+ * XXX the lock is not always needed, and its initialization may be
+ * expensive. Consider making it external.
+ */
+struct __priv_data {
+ ast_mutex_t lock;
+ int ref_counter;
+ ao2_destructor_fn destructor_fn;
+ /*! for stats */
+ size_t data_size;
+ /*! magic number. This is used to verify that a pointer passed in is a
+ * valid astobj2 */
+ uint32_t magic;
+};
+
+#define AO2_MAGIC 0xa570b123
+
+/*!
+ * What an astobj2 object looks like: fixed-size private data
+ * followed by variable-size user data.
+ */
+struct astobj2 {
+ struct __priv_data priv_data;
+ void *user_data[0];
+};
+
+#ifdef AST_DEVMODE
+#define AO2_DEBUG 1
+#endif
+
+#ifdef AO2_DEBUG
+struct ao2_stats {
+ volatile int total_objects;
+ volatile int total_mem;
+ volatile int total_containers;
+ volatile int total_refs;
+ volatile int total_locked;
+};
+
+static struct ao2_stats ao2;
+#endif
+
+#ifndef HAVE_BKTR /* backtrace support */
+void ao2_bt(void) {}
+#else
+#include <execinfo.h> /* for backtrace */
+
+void ao2_bt(void)
+{
+ int c, i;
+#define N1 20
+ void *addresses[N1];
+ char **strings;
+
+ c = backtrace(addresses, N1);
+ strings = backtrace_symbols(addresses,c);
+ ast_verbose("backtrace returned: %d\n", c);
+ for(i = 0; i < c; i++) {
+ ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
+ }
+ free(strings);
+}
+#endif
+
+/*!
+ * \brief convert from a pointer _p to a user-defined object
+ *
+ * \return the pointer to the astobj2 structure
+ */
+static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
+{
+ struct astobj2 *p;
+
+ if (!user_data) {
+ ast_log(LOG_ERROR, "user_data is NULL\n");
+ return NULL;
+ }
+
+ p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
+ if (AO2_MAGIC != (p->priv_data.magic) ) {
+ ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
+ p = NULL;
+ }
+
+ return p;
+}
+
+/*!
+ * \brief convert from a pointer _p to an astobj2 object
+ *
+ * \return the pointer to the user-defined portion.
+ */
+#define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
+
+int ao2_lock(void *user_data)
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+
+ return ast_mutex_lock(&p->priv_data.lock);
+}
+
+int ao2_unlock(void *user_data)
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+#endif
+
+ return ast_mutex_unlock(&p->priv_data.lock);
+}
+
+/*
+ * The argument is a pointer to the user portion.
+ */
+int ao2_ref(void *user_data, const int delta)
+{
+ int current_value;
+ int ret;
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (obj == NULL)
+ return -1;
+
+ /* if delta is 0, just return the refcount */
+ if (delta == 0)
+ return (obj->priv_data.ref_counter);
+
+ /* we modify with an atomic operation the reference counter */
+ ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
+ current_value = ret + delta;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_refs, delta);
+#endif
+
+ /* this case must never happen */
+ if (current_value < 0)
+ ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
+
+ if (current_value <= 0) { /* last reference, destroy the object */
+ if (obj->priv_data.destructor_fn != NULL)
+ obj->priv_data.destructor_fn(user_data);
+
+ ast_mutex_destroy(&obj->priv_data.lock);
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
+ ast_atomic_fetchadd_int(&ao2.total_objects, -1);
+#endif
+ /* for safety, zero-out the astobj2 header and also the
+ * first word of the user-data, which we make sure is always
+ * allocated. */
+ bzero(obj, sizeof(struct astobj2 *) + sizeof(void *) );
+ free(obj);
+ }
+
+ return ret;
+}
+
+/*
+ * We always alloc at least the size of a void *,
+ * for debugging purposes.
+ */
+void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
+{
+ /* allocation */
+ struct astobj2 *obj;
+
+ if (data_size < sizeof(void *))
+ data_size = sizeof(void *);
+
+ obj = ast_calloc(1, sizeof(*obj) + data_size);
+
+ if (obj == NULL)
+ return NULL;
+
+ ast_mutex_init(&obj->priv_data.lock);
+ obj->priv_data.magic = AO2_MAGIC;
+ obj->priv_data.data_size = data_size;
+ obj->priv_data.ref_counter = 1;
+ obj->priv_data.destructor_fn = destructor_fn; /* can be NULL */
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_objects, 1);
+ ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
+ ast_atomic_fetchadd_int(&ao2.total_refs, 1);
+#endif
+
+ /* return a pointer to the user data */
+ return EXTERNAL_OBJ(obj);
+}
+
+/* internal callback to destroy a container. */
+static void container_destruct(void *c);
+
+/* each bucket in the container is a tailq. */
+AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
+
+/*!
+ * A container; stores the hash and callback functions, information on
+ * the size, the hash bucket heads, and a version number, starting at 0
+ * (for a newly created, empty container)
+ * and incremented every time an object is inserted or deleted.
+ * The assumption is that an object is never moved in a container,
+ * but removed and readded with the new number.
+ * The version number is especially useful when implementing iterators.
+ * In fact, we can associate a unique, monotonically increasing number to
+ * each object, which means that, within an iterator, we can store the
+ * version number of the current object, and easily look for the next one,
+ * which is the next one in the list with a higher number.
+ * Since all objects have a version >0, we can use 0 as a marker for
+ * 'we need the first object in the bucket'.
+ *
+ * \todo Linking and unlink objects is typically expensive, as it
+ * involves a malloc() of a small object which is very inefficient.
+ * To optimize this, we allocate larger arrays of bucket_list's
+ * when we run out of them, and then manage our own freelist.
+ * This will be more efficient as we can do the freelist management while
+ * we hold the lock (that we need anyways).
+ */
+struct ao2_container {
+ ao2_hash_fn *hash_fn;
+ ao2_callback_fn *cmp_fn;
+ int n_buckets;
+ /*! Number of elements in the container */
+ int elements;
+ /*! described above */
+ int version;
+ /*! variable size */
+ struct bucket buckets[0];
+};
+
+/*!
+ * \brief always zero hash function
+ *
+ * it is convenient to have a hash function that always returns 0.
+ * This is basically used when we want to have a container that is
+ * a simple linked list.
+ *
+ * \returns 0
+ */
+static int hash_zero(const void *user_obj, const int flags)
+{
+ return 0;
+}
+
+/*
+ * A container is just an object, after all!
+ */
+struct ao2_container *
+ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn,
+ ao2_callback_fn *cmp_fn)
+{
+ /* XXX maybe consistency check on arguments ? */
+ /* compute the container size */
+ size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+
+ struct ao2_container *c = ao2_alloc(container_size, container_destruct);
+
+ if (!c)
+ return NULL;
+
+ c->version = 1; /* 0 is a reserved value here */
+ c->n_buckets = n_buckets;
+ c->hash_fn = hash_fn ? hash_fn : hash_zero;
+ c->cmp_fn = cmp_fn;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_containers, 1);
+#endif
+
+ return c;
+}
+
+/*!
+ * return the number of elements in the container
+ */
+int ao2_container_count(struct ao2_container *c)
+{
+ return c->elements;
+}
+
+/*!
+ * A structure to create a linked list of entries,
+ * used within a bucket.
+ * XXX \todo this should be private to the container code
+ */
+struct bucket_list {
+ AST_LIST_ENTRY(bucket_list) entry;
+ int version;
+ struct astobj2 *astobj; /* pointer to internal data */
+};
+
+/*
+ * link an object to a container
+ */
+void *ao2_link(struct ao2_container *c, void *user_data)
+{
+ int i;
+ /* create a new list entry */
+ struct bucket_list *p;
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (!obj)
+ return NULL;
+
+ if (INTERNAL_OBJ(c) == NULL)
+ return NULL;
+
+ p = ast_calloc(1, sizeof(*p));
+ if (!p)
+ return NULL;
+
+ i = c->hash_fn(user_data, OBJ_POINTER);
+
+ ao2_lock(c);
+ i %= c->n_buckets;
+ p->astobj = obj;
+ p->version = ast_atomic_fetchadd_int(&c->version, 1);
+ AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
+ ast_atomic_fetchadd_int(&c->elements, 1);
+ ao2_ref(user_data, +1);
+ ao2_unlock(c);
+
+ return p;
+}
+
+/*!
+ * \brief another convenience function is a callback that matches on address
+ */
+int ao2_match_by_addr(void *user_data, void *arg, int flags)
+{
+ return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
+}
+
+/*
+ * Unlink an object from the container
+ * and destroy the associated * ao2_bucket_list structure.
+ */
+void *ao2_unlink(struct ao2_container *c, void *user_data)
+{
+ if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */
+ return NULL;
+
+ ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
+
+ return NULL;
+}
+
+/*!
+ * \brief special callback that matches all
+ */
+static int cb_true(void *user_data, void *arg, int flags)
+{
+ return CMP_MATCH;
+}
+
+/*!
+ * Browse the container using different stategies accoding the flags.
+ * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is
+ * specified.
+ */
+void *ao2_callback(struct ao2_container *c,
+ const enum search_flags flags,
+ ao2_callback_fn *cb_fn, void *arg)
+{
+ int i, last; /* search boundaries */
+ void *ret = NULL;
+
+ if (INTERNAL_OBJ(c) == NULL) /* safety check on the argument */
+ return NULL;
+
+ if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
+ ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
+ return NULL;
+ }
+
+ /* override the match function if necessary */
+ if (cb_fn == NULL) /* if NULL, match everything */
+ cb_fn = cb_true;
+ /*
+ * XXX this can be optimized.
+ * If we have a hash function and lookup by pointer,
+ * run the hash function. Otherwise, scan the whole container
+ * (this only for the time being. We need to optimize this.)
+ */
+ if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
+ i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
+ else /* don't know, let's scan all buckets */
+ i = -1; /* XXX this must be fixed later. */
+
+ /* determine the search boundaries: i..last-1 */
+ if (i < 0) {
+ i = 0;
+ last = c->n_buckets;
+ } else {
+ last = i + 1;
+ }
+
+ ao2_lock(c); /* avoid modifications to the content */
+
+ for (; i < last ; i++) {
+ /* scan the list with prev-cur pointers */
+ struct bucket_list *cur;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
+ int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
+
+ /* we found the object, performing operations according flags */
+ if (match == 0) { /* no match, no stop, continue */
+ continue;
+ } else if (match == CMP_STOP) { /* no match but stop, we are done */
+ i = last;
+ break;
+ }
+ /* we have a match (CMP_MATCH) here */
+ if (!(flags & OBJ_NODATA)) { /* if must return the object, record the value */
+ /* it is important to handle this case before the unlink */
+ ret = EXTERNAL_OBJ(cur->astobj);
+ ao2_ref(ret, 1);
+ }
+
+ if (flags & OBJ_UNLINK) { /* must unlink */
+ struct bucket_list *x = cur;
+
+ /* we are going to modify the container, so update version */
+ ast_atomic_fetchadd_int(&c->version, 1);
+ AST_LIST_REMOVE_CURRENT(entry);
+ /* update number of elements and version */
+ ast_atomic_fetchadd_int(&c->elements, -1);
+ ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
+ free(x); /* free the link record */
+ }
+
+ if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
+ /* We found the only match we need */
+ i = last; /* force exit from outer loop */
+ break;
+ }
+ if (!(flags & OBJ_NODATA)) {
+#if 0 /* XXX to be completed */
+ /*
+ * This is the multiple-return case. We need to link
+ * the object in a list. The refcount is already increased.
+ */
+#endif
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+ ao2_unlock(c);
+ return ret;
+}
+
+/*!
+ * the find function just invokes the default callback with some reasonable flags.
+ */
+void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
+{
+ return ao2_callback(c, flags, c->cmp_fn, arg);
+}
+
+/*!
+ * initialize an iterator so we start from the first object
+ */
+struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
+{
+ struct ao2_iterator a = {
+ .c = c,
+ .flags = flags
+ };
+
+ return a;
+}
+
+/*
+ * move to the next element in the container.
+ */
+void * ao2_iterator_next(struct ao2_iterator *a)
+{
+ int lim;
+ struct bucket_list *p = NULL;
+ void *ret = NULL;
+
+ if (INTERNAL_OBJ(a->c) == NULL)
+ return NULL;
+
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_lock(a->c);
+
+ /* optimization. If the container is unchanged and
+ * we have a pointer, try follow it
+ */
+ if (a->c->version == a->c_version && (p = a->obj) ) {
+ if ( (p = AST_LIST_NEXT(p, entry)) )
+ goto found;
+ /* nope, start from the next bucket */
+ a->bucket++;
+ a->version = 0;
+ a->obj = NULL;
+ }
+
+ lim = a->c->n_buckets;
+
+ /* Browse the buckets array, moving to the next
+ * buckets if we don't find the entry in the current one.
+ * Stop when we find an element with version number greater
+ * than the current one (we reset the version to 0 when we
+ * switch buckets).
+ */
+ for (; a->bucket < lim; a->bucket++, a->version = 0) {
+ /* scan the current bucket */
+ AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
+ if (p->version > a->version)
+ goto found;
+ }
+ }
+
+found:
+ if (p) {
+ a->version = p->version;
+ a->obj = p;
+ a->c_version = a->c->version;
+ ret = EXTERNAL_OBJ(p->astobj);
+ /* inc refcount of returned object */
+ ao2_ref(ret, 1);
+ }
+
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_unlock(a->c);
+
+ return ret;
+}
+
+/* callback for destroying container.
+ * we can make it simple as we know what it does
+ */
+static int cd_cb(void *obj, void *arg, int flag)
+{
+ ao2_ref(obj, -1);
+ return 0;
+}
+
+static void container_destruct(void *_c)
+{
+ struct ao2_container *c = _c;
+
+ ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_containers, -1);
+#endif
+}
+
+#ifdef AO2_DEBUG
+static int print_cb(void *obj, void *arg, int flag)
+{
+ int *fd = arg;
+ char *s = (char *)obj;
+
+ ast_cli(*fd, "string <%s>\n", s);
+ return 0;
+}
+
+/*
+ * Print stats
+ */
+static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "astobj2 stats";
+ e->usage = "Usage: astobj2 stats\n"
+ " Show astobj2 stats\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ ast_cli(a->fd, "Objects : %d\n", ao2.total_objects);
+ ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
+ ast_cli(a->fd, "Memory : %d\n", ao2.total_mem);
+ ast_cli(a->fd, "Locked : %d\n", ao2.total_locked);
+ ast_cli(a->fd, "Refs : %d\n", ao2.total_refs);
+ return CLI_SUCCESS;
+}
+
+/*
+ * This is testing code for astobj
+ */
+static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ao2_container *c1;
+ int i, lim;
+ char *obj;
+ static int prof_id = -1;
+ struct ast_cli_args fake_args = { a->fd, 0, NULL };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "astobj2 test";
+ e->usage = "Usage: astobj2 test <num>\n"
+ " Runs astobj2 test. Creates 'num' objects,\n"
+ " and test iterators, callbacks and may be other stuff\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (prof_id == -1)
+ prof_id = ast_add_profile("ao2_alloc", 0);
+
+ ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
+ lim = atoi(a->argv[2]);
+ ast_cli(a->fd, "called astobj_test\n");
+
+ handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
+ /*
+ * allocate a container with no default callback, and no hash function.
+ * No hash means everything goes in the same bucket.
+ */
+ c1 = ao2_container_alloc(100, NULL /* no callback */, NULL /* no hash */);
+ ast_cli(a->fd, "container allocated as %p\n", c1);
+
+ /*
+ * fill the container with objects.
+ * ao2_alloc() gives us a reference which we pass to the
+ * container when we do the insert.
+ */
+ for (i = 0; i < lim; i++) {
+ ast_mark(prof_id, 1 /* start */);
+ obj = ao2_alloc(80, NULL);
+ ast_mark(prof_id, 0 /* stop */);
+ ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
+ sprintf(obj, "-- this is obj %d --", i);
+ ao2_link(c1, obj);
+ }
+ ast_cli(a->fd, "testing callbacks\n");
+ ao2_callback(c1, 0, print_cb, &a->fd);
+
+ ast_cli(a->fd, "testing iterators, remove every second object\n");
+ {
+ struct ao2_iterator ai;
+ int x = 0;
+
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(a->fd, "iterator on <%s>\n", obj);
+ if (x++ & 1)
+ ao2_unlink(c1, obj);
+ ao2_ref(obj, -1);
+ }
+ ast_cli(a->fd, "testing iterators again\n");
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(a->fd, "iterator on <%s>\n", obj);
+ ao2_ref(obj, -1);
+ }
+ }
+ ast_cli(a->fd, "testing callbacks again\n");
+ ao2_callback(c1, 0, print_cb, &a->fd);
+
+ ast_verbose("now you should see an error message:\n");
+ ao2_ref(&i, -1); /* i is not a valid object so we print an error here */
+
+ ast_cli(a->fd, "destroy container\n");
+ ao2_ref(c1, -1); /* destroy container */
+ handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_astobj2[] = {
+ AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
+ AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
+};
+#endif /* AO2_DEBUG */
+
+int astobj2_init(void)
+{
+#ifdef AO2_DEBUG
+ ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
+#endif
+
+ return 0;
+}
diff --git a/trunk/main/audiohook.c b/trunk/main/audiohook.c
new file mode 100644
index 000000000..c50e6c476
--- /dev/null
+++ b/trunk/main/audiohook.c
@@ -0,0 +1,693 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Audiohooks Architecture
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/audiohook.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/frame.h"
+#include "asterisk/translate.h"
+
+struct ast_audiohook_translate {
+ struct ast_trans_pvt *trans_pvt;
+ int format;
+};
+
+struct ast_audiohook_list {
+ struct ast_audiohook_translate in_translate[2];
+ struct ast_audiohook_translate out_translate[2];
+ AST_LIST_HEAD_NOLOCK(, ast_audiohook) spy_list;
+ AST_LIST_HEAD_NOLOCK(, ast_audiohook) whisper_list;
+ AST_LIST_HEAD_NOLOCK(, ast_audiohook) manipulate_list;
+};
+
+/*! \brief Initialize an audiohook structure
+ * \param audiohook Audiohook structure
+ * \param type
+ * \param source
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source)
+{
+ /* Need to keep the type and source */
+ audiohook->type = type;
+ audiohook->source = source;
+
+ /* Initialize lock that protects our audiohook */
+ ast_mutex_init(&audiohook->lock);
+ ast_cond_init(&audiohook->trigger, NULL);
+
+ /* Setup the factories that are needed for this audiohook type */
+ switch (type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ ast_slinfactory_init(&audiohook->read_factory);
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ ast_slinfactory_init(&audiohook->write_factory);
+ break;
+ default:
+ break;
+ }
+
+ /* Since we are just starting out... this audiohook is new */
+ audiohook->status = AST_AUDIOHOOK_STATUS_NEW;
+
+ return 0;
+}
+
+/*! \brief Destroys an audiohook structure
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_destroy(struct ast_audiohook *audiohook)
+{
+ /* Drop the factories used by this audiohook type */
+ switch (audiohook->type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ ast_slinfactory_destroy(&audiohook->read_factory);
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ ast_slinfactory_destroy(&audiohook->write_factory);
+ break;
+ default:
+ break;
+ }
+
+ /* Destroy translation path if present */
+ if (audiohook->trans_pvt)
+ ast_translator_free_path(audiohook->trans_pvt);
+
+ /* Lock and trigger be gone! */
+ ast_cond_destroy(&audiohook->trigger);
+ ast_mutex_destroy(&audiohook->lock);
+
+ return 0;
+}
+
+/*! \brief Writes a frame into the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param direction Direction the audio frame came from
+ * \param frame Frame to write in
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ struct ast_slinfactory *factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_factory : &audiohook->write_factory);
+
+ /* Write frame out to respective factory */
+ ast_slinfactory_feed(factory, frame);
+
+ /* If we need to notify the respective handler of this audiohook, do so */
+ switch (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_MODE)) {
+ case AST_AUDIOHOOK_TRIGGER_READ:
+ if (direction == AST_AUDIOHOOK_DIRECTION_READ)
+ ast_cond_signal(&audiohook->trigger);
+ break;
+ case AST_AUDIOHOOK_TRIGGER_WRITE:
+ if (direction == AST_AUDIOHOOK_DIRECTION_WRITE)
+ ast_cond_signal(&audiohook->trigger);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction)
+{
+ struct ast_slinfactory *factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_factory : &audiohook->write_factory);
+ int vol = (direction == AST_AUDIOHOOK_DIRECTION_READ ? audiohook->options.read_volume : audiohook->options.write_volume);
+ short buf[samples];
+ struct ast_frame frame = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = buf,
+ .datalen = sizeof(buf),
+ .samples = samples,
+ };
+
+ /* Ensure the factory is able to give us the samples we want */
+ if (samples > ast_slinfactory_available(factory))
+ return NULL;
+
+ /* Read data in from factory */
+ if (!ast_slinfactory_read(factory, buf, samples))
+ return NULL;
+
+ /* If a volume adjustment needs to be applied apply it */
+ if (vol)
+ ast_frame_adjust_volume(&frame, vol);
+
+ return ast_frdup(&frame);
+}
+
+static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audiohook, size_t samples)
+{
+ int i = 0;
+ short buf1[samples], buf2[samples], *read_buf = NULL, *write_buf = NULL, *final_buf = NULL, *data1 = NULL, *data2 = NULL;
+ struct ast_frame frame = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = NULL,
+ .datalen = sizeof(buf1),
+ .samples = samples,
+ };
+
+ /* Start with the read factory... if there are enough samples, read them in */
+ if (ast_slinfactory_available(&audiohook->read_factory) >= samples) {
+ if (ast_slinfactory_read(&audiohook->read_factory, buf1, samples)) {
+ read_buf = buf1;
+ /* Adjust read volume if need be */
+ if (audiohook->options.read_volume) {
+ int count = 0;
+ short adjust_value = abs(audiohook->options.read_volume);
+ for (count = 0; count < samples; count++) {
+ if (audiohook->options.read_volume > 0)
+ ast_slinear_saturated_multiply(&buf1[count], &adjust_value);
+ else if (audiohook->options.read_volume < 0)
+ ast_slinear_saturated_divide(&buf1[count], &adjust_value);
+ }
+ }
+ }
+ } else if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to get %d samples from read factory %p\n", (int)samples, &audiohook->read_factory);
+
+ /* Move on to the write factory... if there are enough samples, read them in */
+ if (ast_slinfactory_available(&audiohook->write_factory) >= samples) {
+ if (ast_slinfactory_read(&audiohook->write_factory, buf2, samples)) {
+ write_buf = buf2;
+ /* Adjust write volume if need be */
+ if (audiohook->options.write_volume) {
+ int count = 0;
+ short adjust_value = abs(audiohook->options.write_volume);
+ for (count = 0; count < samples; count++) {
+ if (audiohook->options.write_volume > 0)
+ ast_slinear_saturated_multiply(&buf2[count], &adjust_value);
+ else if (audiohook->options.write_volume < 0)
+ ast_slinear_saturated_divide(&buf2[count], &adjust_value);
+ }
+ }
+ }
+ } else if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to get %d samples from write factory %p\n", (int)samples, &audiohook->write_factory);
+
+ /* Basically we figure out which buffer to use... and if mixing can be done here */
+ if (!read_buf && !write_buf)
+ return NULL;
+ else if (read_buf && write_buf) {
+ for (i = 0, data1 = read_buf, data2 = write_buf; i < samples; i++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+ final_buf = buf1;
+ } else if (read_buf)
+ final_buf = buf1;
+ else if (write_buf)
+ final_buf = buf2;
+
+ /* Make the final buffer part of the frame, so it gets duplicated fine */
+ frame.data = final_buf;
+
+ /* Yahoo, a combined copy of the audio! */
+ return ast_frdup(&frame);
+}
+
+/*! \brief Reads a frame in from the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param samples Number of samples wanted
+ * \param direction Direction the audio frame came from
+ * \param format Format of frame remote side wants back
+ * \return Returns frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, int format)
+{
+ struct ast_frame *read_frame = NULL, *final_frame = NULL;
+
+ if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? audiohook_read_frame_both(audiohook, samples) : audiohook_read_frame_single(audiohook, samples, direction))))
+ return NULL;
+
+ /* If they don't want signed linear back out, we'll have to send it through the translation path */
+ if (format != AST_FORMAT_SLINEAR) {
+ /* Rebuild translation path if different format then previously */
+ if (audiohook->format != format) {
+ if (audiohook->trans_pvt) {
+ ast_translator_free_path(audiohook->trans_pvt);
+ audiohook->trans_pvt = NULL;
+ }
+ /* Setup new translation path for this format... if we fail we can't very well return signed linear so free the frame and return nothing */
+ if (!(audiohook->trans_pvt = ast_translator_build_path(format, AST_FORMAT_SLINEAR))) {
+ ast_frfree(read_frame);
+ return NULL;
+ }
+ }
+ /* Convert to requested format, and allow the read in frame to be freed */
+ final_frame = ast_translate(audiohook->trans_pvt, read_frame, 1);
+ } else {
+ final_frame = read_frame;
+ }
+
+ return final_frame;
+}
+
+/*! \brief Attach audiohook to channel
+ * \param chan Channel
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
+{
+ ast_channel_lock(chan);
+
+ if (!chan->audiohooks) {
+ /* Whoops... allocate a new structure */
+ if (!(chan->audiohooks = ast_calloc(1, sizeof(*chan->audiohooks)))) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->spy_list);
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->whisper_list);
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->manipulate_list);
+ }
+
+ /* Drop into respective list */
+ if (audiohook->type == AST_AUDIOHOOK_TYPE_SPY)
+ AST_LIST_INSERT_TAIL(&chan->audiohooks->spy_list, audiohook, list);
+ else if (audiohook->type == AST_AUDIOHOOK_TYPE_WHISPER)
+ AST_LIST_INSERT_TAIL(&chan->audiohooks->whisper_list, audiohook, list);
+ else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE)
+ AST_LIST_INSERT_TAIL(&chan->audiohooks->manipulate_list, audiohook, list);
+
+ /* Change status over to running since it is now attached */
+ audiohook->status = AST_AUDIOHOOK_STATUS_RUNNING;
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+/*! \brief Detach audiohook from channel
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach(struct ast_audiohook *audiohook)
+{
+ if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
+ return 0;
+
+ audiohook->status = AST_AUDIOHOOK_STATUS_SHUTDOWN;
+
+ while (audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
+ ast_audiohook_trigger_wait(audiohook);
+
+ return 0;
+}
+
+/*! \brief Detach audiohooks from list and destroy said list
+ * \param audiohook_list List of audiohooks
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list)
+{
+ int i = 0;
+ struct ast_audiohook *audiohook = NULL;
+
+ /* Drop any spies */
+ while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->spy_list, list))) {
+ ast_audiohook_lock(audiohook);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ }
+
+ /* Drop any whispering sources */
+ while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->whisper_list, list))) {
+ ast_audiohook_lock(audiohook);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ }
+
+ /* Drop any manipulaters */
+ while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->manipulate_list, list))) {
+ ast_audiohook_lock(audiohook);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_audiohook_unlock(audiohook);
+ audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
+ }
+
+ /* Drop translation paths if present */
+ for (i = 0; i < 2; i++) {
+ if (audiohook_list->in_translate[i].trans_pvt)
+ ast_translator_free_path(audiohook_list->in_translate[i].trans_pvt);
+ if (audiohook_list->out_translate[i].trans_pvt)
+ ast_translator_free_path(audiohook_list->out_translate[i].trans_pvt);
+ }
+
+ /* Free ourselves */
+ ast_free(audiohook_list);
+
+ return 0;
+}
+
+static struct ast_audiohook *find_audiohook_by_source(struct ast_audiohook_list *audiohook_list, const char *source)
+{
+ struct ast_audiohook *audiohook = NULL;
+
+ AST_LIST_TRAVERSE(&audiohook_list->spy_list, audiohook, list) {
+ if (!strcasecmp(audiohook->source, source))
+ return audiohook;
+ }
+
+ AST_LIST_TRAVERSE(&audiohook_list->whisper_list, audiohook, list) {
+ if (!strcasecmp(audiohook->source, source))
+ return audiohook;
+ }
+
+ AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, audiohook, list) {
+ if (!strcasecmp(audiohook->source, source))
+ return audiohook;
+ }
+
+ return NULL;
+}
+
+/*! \brief Detach specified source audiohook from channel
+ * \param chan Channel to detach from
+ * \param source Name of source to detach
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach_source(struct ast_channel *chan, const char *source)
+{
+ struct ast_audiohook *audiohook = NULL;
+
+ ast_channel_lock(chan);
+
+ /* Ensure the channel has audiohooks on it */
+ if (!chan->audiohooks) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ audiohook = find_audiohook_by_source(chan->audiohooks, source);
+
+ ast_channel_unlock(chan);
+
+ if (audiohook && audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
+ audiohook->status = AST_AUDIOHOOK_STATUS_SHUTDOWN;
+
+ return (audiohook ? 0 : -1);
+}
+
+/*! \brief Pass a DTMF frame off to be handled by the audiohook core
+ * \param chan Channel that the list is coming off of
+ * \param audiohook_list List of audiohooks
+ * \param direction Direction frame is coming in from
+ * \param frame The frame itself
+ * \return Return frame on success, NULL on failure
+ */
+static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ struct ast_audiohook *audiohook = NULL;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_audiohook_unlock(audiohook);
+ audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
+ continue;
+ }
+ if (ast_test_flag(audiohook, AST_AUDIOHOOK_WANTS_DTMF))
+ audiohook->manipulate_callback(audiohook, chan, frame, direction);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ return frame;
+}
+
+/*! \brief Pass an AUDIO frame off to be handled by the audiohook core
+ * \param chan Channel that the list is coming off of
+ * \param audiohook_list List of audiohooks
+ * \param direction Direction frame is coming in from
+ * \param frame The frame itself
+ * \return Return frame on success, NULL on failure
+ */
+static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ struct ast_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]);
+ struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]);
+ struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame;
+ struct ast_audiohook *audiohook = NULL;
+ int samples = frame->samples;
+
+ /* If the frame coming in is not signed linear we have to send it through the in_translate path */
+ if (frame->subclass != AST_FORMAT_SLINEAR) {
+ if (in_translate->format != frame->subclass) {
+ if (in_translate->trans_pvt)
+ ast_translator_free_path(in_translate->trans_pvt);
+ if (!(in_translate->trans_pvt = ast_translator_build_path(AST_FORMAT_SLINEAR, frame->subclass)))
+ return frame;
+ in_translate->format = frame->subclass;
+ }
+ if (!(middle_frame = ast_translate(in_translate->trans_pvt, frame, 0)))
+ return frame;
+ }
+
+ /* Queue up signed linear frame to each spy */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ continue;
+ }
+ ast_audiohook_write_frame(audiohook, direction, middle_frame);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ /* If this frame is being written out to the channel then we need to use whisper sources */
+ if (direction == AST_AUDIOHOOK_DIRECTION_WRITE && !AST_LIST_EMPTY(&audiohook_list->whisper_list)) {
+ int i = 0;
+ short read_buf[samples], combine_buf[samples], *data1 = NULL, *data2 = NULL;
+ memset(&combine_buf, 0, sizeof(combine_buf));
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->whisper_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ continue;
+ }
+ if (ast_slinfactory_available(&audiohook->write_factory) >= samples && ast_slinfactory_read(&audiohook->write_factory, read_buf, samples)) {
+ /* Take audio from this whisper source and combine it into our main buffer */
+ for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+ }
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ /* We take all of the combined whisper sources and combine them into the audio being written out */
+ for (i = 0, data1 = middle_frame->data, data2 = combine_buf; i < samples; i++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+ end_frame = middle_frame;
+ }
+
+ /* Pass off frame to manipulate audiohooks */
+ if (!AST_LIST_EMPTY(&audiohook_list->manipulate_list)) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_audiohook_unlock(audiohook);
+ /* We basically drop all of our links to the manipulate audiohook and prod it to do it's own destructive things */
+ audiohook->manipulate_callback(audiohook, chan, NULL, direction);
+ continue;
+ }
+ /* Feed in frame to manipulation */
+ audiohook->manipulate_callback(audiohook, chan, middle_frame, direction);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ end_frame = middle_frame;
+ }
+
+ /* Now we figure out what to do with our end frame (whether to transcode or not) */
+ if (middle_frame == end_frame) {
+ /* Middle frame was modified and became the end frame... let's see if we need to transcode */
+ if (end_frame->subclass != start_frame->subclass) {
+ if (out_translate->format != start_frame->subclass) {
+ if (out_translate->trans_pvt)
+ ast_translator_free_path(out_translate->trans_pvt);
+ if (!(out_translate->trans_pvt = ast_translator_build_path(start_frame->subclass, AST_FORMAT_SLINEAR))) {
+ /* We can't transcode this... drop our middle frame and return the original */
+ ast_frfree(middle_frame);
+ return start_frame;
+ }
+ out_translate->format = start_frame->subclass;
+ }
+ /* Transcode from our middle (signed linear) frame to new format of the frame that came in */
+ if (!(end_frame = ast_translate(out_translate->trans_pvt, middle_frame, 0))) {
+ /* Failed to transcode the frame... drop it and return the original */
+ ast_frfree(middle_frame);
+ return start_frame;
+ }
+ /* Here's the scoop... middle frame is no longer of use to us */
+ ast_frfree(middle_frame);
+ }
+ } else {
+ /* No frame was modified, we can just drop our middle frame and pass the frame we got in out */
+ ast_frfree(middle_frame);
+ }
+
+ return end_frame;
+}
+
+/*! \brief Pass a frame off to be handled by the audiohook core
+ * \param chan Channel that the list is coming off of
+ * \param audiohook_list List of audiohooks
+ * \param direction Direction frame is coming in from
+ * \param frame The frame itself
+ * \return Return frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ /* Pass off frame to it's respective list write function */
+ if (frame->frametype == AST_FRAME_VOICE)
+ return audio_audiohook_write_list(chan, audiohook_list, direction, frame);
+ else if (frame->frametype == AST_FRAME_DTMF)
+ return dtmf_audiohook_write_list(chan, audiohook_list, direction, frame);
+ else
+ return frame;
+}
+
+
+/*! \brief Wait for audiohook trigger to be triggered
+ * \param audiohook Audiohook to wait on
+ */
+void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
+{
+ struct timeval tv;
+ struct timespec ts;
+
+ tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+
+ ast_cond_timedwait(&audiohook->trigger, &audiohook->lock, &ts);
+
+ return;
+}
+
+/* Count number of channel audiohooks by type, regardless of type */
+int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
+{
+ int count = 0;
+ struct ast_audiohook *ah = NULL;
+
+ if (!chan->audiohooks)
+ return -1;
+
+ switch (type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) {
+ if (!strcmp(ah->source, source)) {
+ count++;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) {
+ if (!strcmp(ah->source, source)) {
+ count++;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_MANIPULATE:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) {
+ if (!strcmp(ah->source, source)) {
+ count++;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type);
+ return -1;
+ }
+
+ return count;
+}
+
+/* Count number of channel audiohooks by type that are running */
+int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
+{
+ int count = 0;
+ struct ast_audiohook *ah = NULL;
+ if (!chan->audiohooks)
+ return -1;
+
+ switch (type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) {
+ if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+ count++;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) {
+ if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+ count++;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_MANIPULATE:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) {
+ if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+ count++;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type);
+ return -1;
+ }
+ return count;
+}
+
diff --git a/trunk/main/autoservice.c b/trunk/main/autoservice.c
new file mode 100644
index 000000000..fcc42911f
--- /dev/null
+++ b/trunk/main/autoservice.c
@@ -0,0 +1,252 @@
+/*
+ * 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 Automatic channel service routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/translate.h"
+#include "asterisk/manager.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/indications.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+
+#define MAX_AUTOMONS 1500
+
+struct asent {
+ struct ast_channel *chan;
+ /*! This gets incremented each time autoservice gets started on the same
+ * channel. It will ensure that it doesn't actually get stopped until
+ * it gets stopped for the last time. */
+ unsigned int use_count;
+ unsigned int orig_end_dtmf_flag:1;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
+ AST_LIST_ENTRY(asent) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(aslist, asent);
+
+static pthread_t asthread = AST_PTHREADT_NULL;
+
+static void defer_frame(struct ast_channel *chan, struct ast_frame *f)
+{
+ struct ast_frame *dup_f;
+ struct asent *as;
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_TRAVERSE(&aslist, as, list) {
+ if (as->chan != chan)
+ continue;
+ if ((dup_f = ast_frdup(f)))
+ AST_LIST_INSERT_TAIL(&as->dtmf_frames, dup_f, frame_list);
+ }
+ AST_RWLIST_UNLOCK(&aslist);
+}
+
+static void *autoservice_run(void *ign)
+{
+ for (;;) {
+ struct ast_channel *mons[MAX_AUTOMONS], *chan;
+ struct asent *as;
+ int x = 0, ms = 500;
+
+ AST_RWLIST_RDLOCK(&aslist);
+ AST_RWLIST_TRAVERSE(&aslist, as, list) {
+ if (!ast_check_hangup(as->chan)) {
+ if (x < MAX_AUTOMONS)
+ mons[x++] = as->chan;
+ else
+ ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
+ }
+ }
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if ((chan = ast_waitfor_n(mons, x, &ms))) {
+ struct ast_frame *f = ast_read(chan);
+
+ if (!f) {
+ struct ast_frame hangup_frame = { 0, };
+ /* No frame means the channel has been hung up.
+ * A hangup frame needs to be queued here as ast_waitfor() may
+ * never return again for the condition to be detected outside
+ * of autoservice. So, we'll leave a HANGUP queued up so the
+ * thread in charge of this channel will know. */
+
+ hangup_frame.frametype = AST_FRAME_CONTROL;
+ hangup_frame.subclass = AST_CONTROL_HANGUP;
+
+ defer_frame(chan, &hangup_frame);
+
+ continue;
+ }
+
+ /* Do not add a default entry in this switch statement. Each new
+ * frame type should be addressed directly as to whether it should
+ * be queued up or not. */
+ switch (f->frametype) {
+ /* Save these frames */
+ case AST_FRAME_DTMF_END:
+ case AST_FRAME_CONTROL:
+ case AST_FRAME_TEXT:
+ case AST_FRAME_IMAGE:
+ case AST_FRAME_HTML:
+ defer_frame(chan, f);
+ break;
+
+ /* Throw these frames away */
+ case AST_FRAME_DTMF_BEGIN:
+ case AST_FRAME_VOICE:
+ case AST_FRAME_VIDEO:
+ case AST_FRAME_NULL:
+ case AST_FRAME_IAX:
+ case AST_FRAME_CNG:
+ case AST_FRAME_MODEM:
+ break;
+ }
+
+ if (f)
+ ast_frfree(f);
+ }
+ }
+
+ asthread = AST_PTHREADT_NULL;
+
+ return NULL;
+}
+
+int ast_autoservice_start(struct ast_channel *chan)
+{
+ int res = 0;
+ struct asent *as;
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_TRAVERSE(&aslist, as, list) {
+ if (as->chan == chan) {
+ as->use_count++;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if (as) {
+ /* Entry exists, autoservice is already handling this channel */
+ return 0;
+ }
+
+ if (!(as = ast_calloc(1, sizeof(*as))))
+ return -1;
+
+ /* New entry created */
+ as->chan = chan;
+ as->use_count = 1;
+
+ ast_channel_lock(chan);
+ as->orig_end_dtmf_flag = ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
+ if (!as->orig_end_dtmf_flag)
+ ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
+ ast_channel_unlock(chan);
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_INSERT_HEAD(&aslist, as, list);
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
+ if (ast_pthread_create_background(&asthread, NULL, autoservice_run, NULL)) {
+ ast_log(LOG_WARNING, "Unable to create autoservice thread :(\n");
+ /* There will only be a single member in the list at this point,
+ the one we just added. */
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_REMOVE(&aslist, as, list);
+ AST_RWLIST_UNLOCK(&aslist);
+ free(as);
+ res = -1;
+ } else
+ pthread_kill(asthread, SIGURG);
+ }
+
+ return res;
+}
+
+int ast_autoservice_stop(struct ast_channel *chan)
+{
+ int res = -1;
+ struct asent *as;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
+ struct ast_frame *f;
+ int removed = 0;
+ int orig_end_dtmf_flag = 0;
+
+ AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames);
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
+ if (as->chan == chan) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ as->use_count--;
+ if (as->use_count)
+ break;
+ AST_LIST_APPEND_LIST(&dtmf_frames, &as->dtmf_frames, frame_list);
+ orig_end_dtmf_flag = as->orig_end_dtmf_flag;
+ ast_free(as);
+ removed = 1;
+ if (!ast_check_hangup(chan))
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (removed && asthread != AST_PTHREADT_NULL)
+ pthread_kill(asthread, SIGURG);
+
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if (!removed)
+ return 0;
+
+ if (!orig_end_dtmf_flag)
+ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+
+ /* Wait for it to un-block */
+ while (ast_test_flag(chan, AST_FLAG_BLOCKING))
+ usleep(1000);
+
+ while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) {
+ ast_queue_frame(chan, f);
+ ast_frfree(f);
+ }
+
+ return res;
+}
diff --git a/trunk/main/buildinfo.c b/trunk/main/buildinfo.c
new file mode 100644
index 000000000..964e06eb3
--- /dev/null
+++ b/trunk/main/buildinfo.c
@@ -0,0 +1,33 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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 Build timestamp variables
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk/build.h"
+
+const char *ast_build_hostname = BUILD_HOSTNAME;
+const char *ast_build_kernel = BUILD_KERNEL;
+const char *ast_build_machine = BUILD_MACHINE;
+const char *ast_build_os = BUILD_OS;
+const char *ast_build_date = BUILD_DATE;
+const char *ast_build_user = BUILD_USER;
diff --git a/trunk/main/callerid.c b/trunk/main/callerid.c
new file mode 100644
index 000000000..c1d5e80b9
--- /dev/null
+++ b/trunk/main/callerid.c
@@ -0,0 +1,1115 @@
+/*
+ * 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 CallerID Generation support
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/callerid.h"
+#include "asterisk/fskmodem.h"
+#include "asterisk/utils.h"
+
+struct callerid_state {
+ fsk_data fskd;
+ char rawdata[256];
+ short oldstuff[160];
+ int oldlen;
+ int pos;
+ int type;
+ int cksum;
+ char name[64];
+ char number[64];
+ int flags;
+ int sawflag;
+ int len;
+
+ int skipflag;
+ unsigned short crc;
+};
+
+
+float cid_dr[4], cid_di[4];
+float clidsb = 8000.0 / 1200.0;
+float sasdr, sasdi;
+float casdr1, casdi1, casdr2, casdi2;
+
+#define CALLERID_SPACE 2200.0 /*!< 2200 hz for "0" */
+#define CALLERID_MARK 1200.0 /*!< 1200 hz for "1" */
+#define SAS_FREQ 440.0
+#define CAS_FREQ1 2130.0
+#define CAS_FREQ2 2750.0
+
+#define AST_CALLERID_UNKNOWN "<unknown>"
+
+static inline void gen_tones(unsigned char *buf, int len, int codec, float ddr1, float ddi1, float ddr2, float ddi2, float *cr1, float *ci1, float *cr2, float *ci2)
+{
+ int x;
+ float t;
+ for (x = 0; x < len; x++) {
+ t = *cr1 * ddr1 - *ci1 * ddi1;
+ *ci1 = *cr1 * ddi1 + *ci1 * ddr1;
+ *cr1 = t;
+ t = 2.0 - (*cr1 * *cr1 + *ci1 * *ci1);
+ *cr1 *= t;
+ *ci1 *= t;
+
+ t = *cr2 * ddr2 - *ci2 * ddi2;
+ *ci2 = *cr2 * ddi2 + *ci2 * ddr2;
+ *cr2 = t;
+ t = 2.0 - (*cr2 * *cr2 + *ci2 * *ci2);
+ *cr2 *= t;
+ *ci2 *= t;
+ buf[x] = AST_LIN2X((*cr1 + *cr2) * 2048.0);
+ }
+}
+
+static inline void gen_tone(unsigned char *buf, int len, int codec, float ddr1, float ddi1, float *cr1, float *ci1)
+{
+ int x;
+ float t;
+ for (x = 0; x < len; x++) {
+ t = *cr1 * ddr1 - *ci1 * ddi1;
+ *ci1 = *cr1 * ddi1 + *ci1 * ddr1;
+ *cr1 = t;
+ t = 2.0 - (*cr1 * *cr1 + *ci1 * *ci1);
+ *cr1 *= t;
+ *ci1 *= t;
+ buf[x] = AST_LIN2X(*cr1 * 8192.0);
+ }
+}
+
+/*! \brief Initialize stuff for inverse FFT */
+void callerid_init(void)
+{
+ cid_dr[0] = cos(CALLERID_SPACE * 2.0 * M_PI / 8000.0);
+ cid_di[0] = sin(CALLERID_SPACE * 2.0 * M_PI / 8000.0);
+ cid_dr[1] = cos(CALLERID_MARK * 2.0 * M_PI / 8000.0);
+ cid_di[1] = sin(CALLERID_MARK * 2.0 * M_PI / 8000.0);
+ sasdr = cos(SAS_FREQ * 2.0 * M_PI / 8000.0);
+ sasdi = sin(SAS_FREQ * 2.0 * M_PI / 8000.0);
+ casdr1 = cos(CAS_FREQ1 * 2.0 * M_PI / 8000.0);
+ casdi1 = sin(CAS_FREQ1 * 2.0 * M_PI / 8000.0);
+ casdr2 = cos(CAS_FREQ2 * 2.0 * M_PI / 8000.0);
+ casdi2 = sin(CAS_FREQ2 * 2.0 * M_PI / 8000.0);
+}
+
+struct callerid_state *callerid_new(int cid_signalling)
+{
+ struct callerid_state *cid;
+
+ if ((cid = ast_calloc(1, sizeof(*cid)))) {
+ cid->fskd.ispb = 7; /* 1200 baud */
+ /* Set up for 1200 / 8000 freq *32 to allow ints */
+ cid->fskd.pllispb = (int)(8000 * 32 / 1200);
+ cid->fskd.pllids = cid->fskd.pllispb/32;
+ cid->fskd.pllispb2 = cid->fskd.pllispb/2;
+
+ cid->fskd.icont = 0; /* PLL REset */
+ /* cid->fskd.hdlc = 0; */ /* Async */
+ cid->fskd.nbit = 8; /* 8 bits */
+ cid->fskd.instop = 1; /* 1 stop bit */
+ /* cid->fskd.paridad = 0; */ /* No parity */
+ cid->fskd.bw = 1; /* Filter 800 Hz */
+ if (cid_signalling == 2) { /* v23 signalling */
+ cid->fskd.f_mark_idx = 4; /* 1300 Hz */
+ cid->fskd.f_space_idx = 5; /* 2100 Hz */
+ } else { /* Bell 202 signalling as default */
+ cid->fskd.f_mark_idx = 2; /* 1200 Hz */
+ cid->fskd.f_space_idx = 3; /* 2200 Hz */
+ }
+ /* cid->fskd.pcola = 0; */ /* No clue */
+ /* cid->fskd.cont = 0.0; */ /* Digital PLL reset */
+ /* cid->fskd.x0 = 0.0; */
+ /* cid->fskd.state = 0; */
+ cid->flags = CID_UNKNOWN_NAME | CID_UNKNOWN_NUMBER;
+ /* cid->pos = 0; */
+
+ fskmodem_init(&cid->fskd);
+ }
+
+ return cid;
+}
+
+void callerid_get(struct callerid_state *cid, char **name, char **number, int *flags)
+{
+ *flags = cid->flags;
+ if (cid->flags & (CID_UNKNOWN_NAME | CID_PRIVATE_NAME))
+ *name = NULL;
+ else
+ *name = cid->name;
+ if (cid->flags & (CID_UNKNOWN_NUMBER | CID_PRIVATE_NUMBER))
+ *number = NULL;
+ else
+ *number = cid->number;
+}
+
+void callerid_get_dtmf(char *cidstring, char *number, int *flags)
+{
+ int i;
+ int code;
+
+ /* "Clear" the number-buffer. */
+ number[0] = 0;
+
+ if (strlen(cidstring) < 2) {
+ ast_debug(1, "No cid detected\n");
+ *flags = CID_UNKNOWN_NUMBER;
+ return;
+ }
+
+ /* Detect protocol and special types */
+ if (cidstring[0] == 'B') {
+ /* Handle special codes */
+ code = atoi(&cidstring[1]);
+ if (code == 0)
+ *flags = CID_UNKNOWN_NUMBER;
+ else if (code == 10)
+ *flags = CID_PRIVATE_NUMBER;
+ else
+ ast_debug(1, "Unknown DTMF code %d\n", code);
+ } else if (cidstring[0] == 'D' && cidstring[2] == '#') {
+ /* .DK special code */
+ if (cidstring[1] == '1')
+ *flags = CID_PRIVATE_NUMBER;
+ if (cidstring[1] == '2' || cidstring[1] == '3')
+ *flags = CID_UNKNOWN_NUMBER;
+ } else if (cidstring[0] == 'D' || cidstring[0] == 'A') {
+ /* "Standard" callerid */
+ for (i = 1; i < strlen(cidstring); i++) {
+ if (cidstring[i] == 'C' || cidstring[i] == '#')
+ break;
+ if (isdigit(cidstring[i]))
+ number[i-1] = cidstring[i];
+ else
+ ast_debug(1, "Unknown CID digit '%c'\n",
+ cidstring[i]);
+ }
+ number[i-1] = 0;
+ } else if (isdigit(cidstring[0])) {
+ /* It begins with a digit, so we parse it as a number and hope
+ * for the best */
+ ast_log(LOG_WARNING, "Couldn't detect start-character. CID "
+ "parsing might be unreliable\n");
+ for (i = 0; i < strlen(cidstring); i++) {
+ if (isdigit(cidstring[i]))
+ number[i] = cidstring[i];
+ else
+ break;
+ }
+ number[i] = 0;
+ } else {
+ ast_debug(1, "Unknown CID protocol, start digit '%c'\n", cidstring[0]);
+ *flags = CID_UNKNOWN_NUMBER;
+ }
+}
+
+int ast_gen_cas(unsigned char *outbuf, int sendsas, int len, int codec)
+{
+ int pos = 0;
+ int saslen = 2400;
+ float cr1 = 1.0;
+ float ci1 = 0.0;
+ float cr2 = 1.0;
+ float ci2 = 0.0;
+
+ if (sendsas) {
+ if (len < saslen)
+ return -1;
+ gen_tone(outbuf, saslen, codec, sasdr, sasdi, &cr1, &ci1);
+ len -= saslen;
+ pos += saslen;
+ cr2 = cr1;
+ ci2 = ci1;
+ }
+ gen_tones(outbuf + pos, len, codec, casdr1, casdi1, casdr2, casdi2, &cr1, &ci1, &cr2, &ci2);
+ return 0;
+}
+
+static unsigned short calc_crc(unsigned short crc, unsigned char data)
+{
+ unsigned int i, j, org, dst;
+ org = data;
+ dst = 0;
+
+ for (i = 0; i < CHAR_BIT; i++) {
+ org <<= 1;
+ dst >>= 1;
+ if (org & 0x100)
+ dst |= 0x80;
+ }
+ data = (unsigned char) dst;
+ crc ^= (unsigned int) data << (16 - CHAR_BIT);
+ for (j = 0; j < CHAR_BIT; j++) {
+ if (crc & 0x8000U)
+ crc = (crc << 1) ^ 0x1021U ;
+ else
+ crc <<= 1 ;
+ }
+ return crc;
+}
+
+int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int len, int codec)
+{
+ int mylen = len;
+ int olen;
+ int b = 'X';
+ int b2;
+ int res;
+ int x;
+ short *buf;
+
+ buf = alloca(2 * len + cid->oldlen);
+
+ memcpy(buf, cid->oldstuff, cid->oldlen);
+ mylen += cid->oldlen / 2;
+
+ for (x = 0; x < len; x++)
+ buf[x+cid->oldlen/2] = AST_XLAW(ubuf[x]);
+
+ while (mylen >= 160) {
+ b = b2 = 0;
+ olen = mylen;
+ res = fsk_serial(&cid->fskd, buf, &mylen, &b);
+
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "fsk_serial made mylen < 0 (%d)\n", mylen);
+ return -1;
+ }
+
+ buf += (olen - mylen);
+
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serial failed\n");
+ return -1;
+ }
+
+ if (res == 1) {
+ b2 = b;
+ b &= 0x7f;
+
+ /* crc checksum calculation */
+ if (cid->sawflag > 1)
+ cid->crc = calc_crc(cid->crc, (unsigned char) b2);
+
+ /* Ignore invalid bytes */
+ if (b > 0xff)
+ continue;
+
+ /* skip DLE if needed */
+ if (cid->sawflag > 0) {
+ if (cid->sawflag != 5 && cid->skipflag == 0 && b == 0x10) {
+ cid->skipflag = 1 ;
+ continue ;
+ }
+ }
+ if (cid->skipflag == 1)
+ cid->skipflag = 0 ;
+
+ /* caller id retrieval */
+ switch (cid->sawflag) {
+ case 0: /* DLE */
+ if (b == 0x10) {
+ cid->sawflag = 1;
+ cid->skipflag = 0;
+ cid->crc = 0;
+ }
+ break;
+ case 1: /* SOH */
+ if (b == 0x01)
+ cid->sawflag = 2;
+ break ;
+ case 2: /* HEADER */
+ if (b == 0x07)
+ cid->sawflag = 3;
+ break;
+ case 3: /* STX */
+ if (b == 0x02)
+ cid->sawflag = 4;
+ break;
+ case 4: /* SERVICE TYPE */
+ if (b == 0x40)
+ cid->sawflag = 5;
+ break;
+ case 5: /* Frame Length */
+ cid->sawflag = 6;
+ break;
+ case 6: /* NUMBER TYPE */
+ cid->sawflag = 7;
+ cid->pos = 0;
+ cid->rawdata[cid->pos++] = b;
+ break;
+ case 7: /* NUMBER LENGTH */
+ cid->sawflag = 8;
+ cid->len = b;
+ if ((cid->len+2) >= sizeof(cid->rawdata)) {
+ ast_log(LOG_WARNING, "too long caller id string\n") ;
+ return -1;
+ }
+ cid->rawdata[cid->pos++] = b;
+ break;
+ case 8: /* Retrieve message */
+ cid->rawdata[cid->pos++] = b;
+ cid->len--;
+ if (cid->len<=0) {
+ cid->rawdata[cid->pos] = '\0';
+ cid->sawflag = 9;
+ }
+ break;
+ case 9: /* ETX */
+ cid->sawflag = 10;
+ break;
+ case 10: /* CRC Checksum 1 */
+ cid->sawflag = 11;
+ break;
+ case 11: /* CRC Checksum 2 */
+ cid->sawflag = 12;
+ if (cid->crc != 0) {
+ ast_log(LOG_WARNING, "crc checksum error\n") ;
+ return -1;
+ }
+ /* extract caller id data */
+ for (x = 0; x < cid->pos;) {
+ switch (cid->rawdata[x++]) {
+ case 0x02: /* caller id number */
+ cid->number[0] = '\0';
+ cid->name[0] = '\0';
+ cid->flags = 0;
+ res = cid->rawdata[x++];
+ ast_copy_string(cid->number, &cid->rawdata[x], res+1);
+ x += res;
+ break;
+ case 0x21: /* additional information */
+ /* length */
+ x++;
+ /* number type */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* international number */
+ case 0x02: /* domestic number */
+ case 0x03: /* network */
+ case 0x04: /* local call */
+ case 0x06: /* short dial number */
+ case 0x07: /* reserved */
+ default: /* reserved */
+ ast_debug(2, "cid info:#1=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ /* numbering plan octed 4 */
+ x++;
+ /* numbering plan octed 5 */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* recommendation E.164 ISDN */
+ case 0x03: /* recommendation X.121 */
+ case 0x04: /* telex dial plan */
+ case 0x08: /* domestic dial plan */
+ case 0x09: /* private dial plan */
+ case 0x05: /* reserved */
+ default: /* reserved */
+ ast_debug(2, "cid info:#2=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ break ;
+ case 0x04: /* no callerid reason */
+ /* length */
+ x++;
+ /* no callerid reason code */
+ switch (cid->rawdata[x]) {
+ case 'P': /* caller id denied by user */
+ case 'O': /* service not available */
+ case 'C': /* pay phone */
+ case 'S': /* service congested */
+ cid->flags |= CID_UNKNOWN_NUMBER;
+ ast_debug(2, "no cid reason:%c\n",cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ break ;
+ case 0x09: /* dialed number */
+ /* length */
+ res = cid->rawdata[x++];
+ /* dialed number */
+ x += res;
+ break ;
+ case 0x22: /* dialed number additional information */
+ /* length */
+ x++;
+ /* number type */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* international number */
+ case 0x02: /* domestic number */
+ case 0x03: /* network */
+ case 0x04: /* local call */
+ case 0x06: /* short dial number */
+ case 0x07: /* reserved */
+ default: /* reserved */
+ if (option_debug > 1)
+ ast_log(LOG_NOTICE, "did info:#1=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ /* numbering plan octed 4 */
+ x++;
+ /* numbering plan octed 5 */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* recommendation E.164 ISDN */
+ case 0x03: /* recommendation X.121 */
+ case 0x04: /* telex dial plan */
+ case 0x08: /* domestic dial plan */
+ case 0x09: /* private dial plan */
+ case 0x05: /* reserved */
+ default: /* reserved */
+ ast_debug(2, "did info:#2=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ break ;
+ }
+ }
+ return 1;
+ break;
+ default:
+ ast_log(LOG_ERROR, "invalid value in sawflag %d\n", cid->sawflag);
+ }
+ }
+ }
+ if (mylen) {
+ memcpy(cid->oldstuff, buf, mylen * 2);
+ cid->oldlen = mylen * 2;
+ } else
+ cid->oldlen = 0;
+
+ return 0;
+}
+
+
+int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, int codec)
+{
+ int mylen = len;
+ int olen;
+ int b = 'X';
+ int res;
+ int x;
+ short *buf;
+
+ buf = alloca(2 * len + cid->oldlen);
+
+ memcpy(buf, cid->oldstuff, cid->oldlen);
+ mylen += cid->oldlen/2;
+
+ for (x = 0; x < len; x++)
+ buf[x+cid->oldlen/2] = AST_XLAW(ubuf[x]);
+ while (mylen >= 160) {
+ olen = mylen;
+ res = fsk_serial(&cid->fskd, buf, &mylen, &b);
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "fsk_serial made mylen < 0 (%d)\n", mylen);
+ return -1;
+ }
+ buf += (olen - mylen);
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serial failed\n");
+ return -1;
+ }
+ if (res == 1) {
+ /* Ignore invalid bytes */
+ if (b > 0xff)
+ continue;
+ switch (cid->sawflag) {
+ case 0: /* Look for flag */
+ if (b == 'U')
+ cid->sawflag = 2;
+ break;
+ case 2: /* Get lead-in */
+ if ((b == 0x04) || (b == 0x80) || (b == 0x06) || (b == 0x82)) {
+ cid->type = b;
+ cid->sawflag = 3;
+ cid->cksum = b;
+ }
+ break;
+ case 3: /* Get length */
+ /* Not a lead in. We're ready */
+ cid->sawflag = 4;
+ cid->len = b;
+ cid->pos = 0;
+ cid->cksum += b;
+ break;
+ case 4: /* Retrieve message */
+ if (cid->pos >= 128) {
+ ast_log(LOG_WARNING, "Caller ID too long???\n");
+ return -1;
+ }
+ cid->rawdata[cid->pos++] = b;
+ cid->len--;
+ cid->cksum += b;
+ if (!cid->len) {
+ cid->rawdata[cid->pos] = '\0';
+ cid->sawflag = 5;
+ }
+ break;
+ case 5: /* Check checksum */
+ if (b != (256 - (cid->cksum & 0xff))) {
+ ast_log(LOG_NOTICE, "Caller*ID failed checksum\n");
+ /* Try again */
+ cid->sawflag = 0;
+ break;
+ }
+
+ cid->number[0] = '\0';
+ cid->name[0] = '\0';
+ /* Update flags */
+ cid->flags = 0;
+ /* If we get this far we're fine. */
+ if ((cid->type == 0x80) || (cid->type == 0x82)) {
+ /* MDMF */
+ /* Go through each element and process */
+ for (x = 0; x < cid->pos;) {
+ switch (cid->rawdata[x++]) {
+ case 1:
+ /* Date */
+ break;
+ case 2: /* Number */
+ case 3: /* Number (for Zebble) */
+ case 4: /* Number */
+ res = cid->rawdata[x];
+ if (res > 32) {
+ ast_log(LOG_NOTICE, "Truncating long caller ID number from %d bytes to 32\n", cid->rawdata[x]);
+ res = 32;
+ }
+ if (ast_strlen_zero(cid->number)) {
+ memcpy(cid->number, cid->rawdata + x + 1, res);
+ /* Null terminate */
+ cid->number[res] = '\0';
+ }
+ break;
+ case 6: /* Stentor Call Qualifier (ie. Long Distance call) */
+ break;
+ case 7: /* Name */
+ case 8: /* Name */
+ res = cid->rawdata[x];
+ if (res > 32) {
+ ast_log(LOG_NOTICE, "Truncating long caller ID name from %d bytes to 32\n", cid->rawdata[x]);
+ res = 32;
+ }
+ memcpy(cid->name, cid->rawdata + x + 1, res);
+ cid->name[res] = '\0';
+ break;
+ case 11: /* Message Waiting */
+ res = cid->rawdata[x + 1];
+ if (res)
+ cid->flags |= CID_MSGWAITING;
+ else
+ cid->flags |= CID_NOMSGWAITING;
+ break;
+ case 17: /* UK: Call type, 1=Voice Call, 2=Ringback when free, 129=Message waiting */
+ case 19: /* UK: Network message system status (Number of messages waiting) */
+ case 22: /* Something French */
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Unknown IE %d\n", cid->rawdata[x - 1]);
+ }
+ res = cid->rawdata[x];
+ if (0 > res){ /* Negative offset in the CID Spill */
+ ast_log(LOG_NOTICE, "IE %d has bad field length of %d at offset %d\n", cid->rawdata[x-1], cid->rawdata[x], x);
+ /* Try again */
+ cid->sawflag = 0;
+ break; /* Exit the loop */
+ }
+ x += cid->rawdata[x];
+ x++;
+ }
+ } else if (cid->type == 0x6) {
+ /* VMWI SDMF */
+ if (cid->rawdata[2] == 0x42) {
+ cid->flags |= CID_MSGWAITING;
+ } else if (cid->rawdata[2] == 0x6f) {
+ cid->flags |= CID_NOMSGWAITING;
+ }
+ } else {
+ /* SDMF */
+ ast_copy_string(cid->number, cid->rawdata + 8, sizeof(cid->number));
+ }
+ if (!strcmp(cid->number, "P")) {
+ strcpy(cid->number, "");
+ cid->flags |= CID_PRIVATE_NUMBER;
+ } else if (!strcmp(cid->number, "O") || ast_strlen_zero(cid->number)) {
+ strcpy(cid->number, "");
+ cid->flags |= CID_UNKNOWN_NUMBER;
+ }
+ if (!strcmp(cid->name, "P")) {
+ strcpy(cid->name, "");
+ cid->flags |= CID_PRIVATE_NAME;
+ } else if (!strcmp(cid->name, "O") || ast_strlen_zero(cid->name)) {
+ strcpy(cid->name, "");
+ cid->flags |= CID_UNKNOWN_NAME;
+ }
+ return 1;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Dunno what to do with a digit in sawflag %d\n", cid->sawflag);
+ }
+ }
+ }
+ if (mylen) {
+ memcpy(cid->oldstuff, buf, mylen * 2);
+ cid->oldlen = mylen * 2;
+ } else
+ cid->oldlen = 0;
+
+ return 0;
+}
+
+void callerid_free(struct callerid_state *cid)
+{
+ ast_free(cid);
+}
+
+static int callerid_genmsg(char *msg, int size, const char *number, const char *name, int flags)
+{
+ struct timeval tv = ast_tvnow();
+ struct ast_tm tm;
+ char *ptr;
+ int res;
+ int i, x;
+
+ /* Get the time */
+ ast_localtime(&tv, &tm, NULL);
+
+ ptr = msg;
+
+ /* Format time and message header */
+ res = snprintf(ptr, size, "\001\010%02d%02d%02d%02d", tm.tm_mon + 1,
+ tm.tm_mday, tm.tm_hour, tm.tm_min);
+ size -= res;
+ ptr += res;
+ if (ast_strlen_zero(number) || (flags & CID_UNKNOWN_NUMBER)) {
+ /* Indicate number not known */
+ res = snprintf(ptr, size, "\004\001O");
+ size -= res;
+ ptr += res;
+ } else if (flags & CID_PRIVATE_NUMBER) {
+ /* Indicate number is private */
+ res = snprintf(ptr, size, "\004\001P");
+ size -= res;
+ ptr += res;
+ } else {
+ /* Send up to 16 digits of number MAX */
+ i = strlen(number);
+ if (i > 16)
+ i = 16;
+ res = snprintf(ptr, size, "\002%c", i);
+ size -= res;
+ ptr += res;
+ for (x = 0; x < i; x++)
+ ptr[x] = number[x];
+ ptr[i] = '\0';
+ ptr += i;
+ size -= i;
+ }
+
+ if (ast_strlen_zero(name) || (flags & CID_UNKNOWN_NAME)) {
+ /* Indicate name not known */
+ res = snprintf(ptr, size, "\010\001O");
+ size -= res;
+ ptr += res;
+ } else if (flags & CID_PRIVATE_NAME) {
+ /* Indicate name is private */
+ res = snprintf(ptr, size, "\010\001P");
+ size -= res;
+ ptr += res;
+ } else {
+ /* Send up to 16 digits of name MAX */
+ i = strlen(name);
+ if (i > 16)
+ i = 16;
+ res = snprintf(ptr, size, "\007%c", i);
+ size -= res;
+ ptr += res;
+ for (x = 0; x < i; x++)
+ ptr[x] = name[x];
+ ptr[i] = '\0';
+ ptr += i;
+ size -= i;
+ }
+ return (ptr - msg);
+
+}
+
+int vmwi_generate(unsigned char *buf, int active, int mdmf, int codec)
+{
+ unsigned char msg[256];
+ int len=0;
+ int sum;
+ int x;
+ int bytes = 0;
+ float cr = 1.0;
+ float ci = 0.0;
+ float scont = 0.0;
+
+ if (mdmf) {
+ /* MDMF Message waiting */
+ msg[len++] = 0x82;
+ /* Length is 3 */
+ msg[len++] = 3;
+ /* IE is "Message Waiting Parameter" */
+ msg[len++] = 0xb;
+ /* Length of IE is one */
+ msg[len++] = 1;
+ /* Active or not */
+ if (active)
+ msg[len++] = 0xff;
+ else
+ msg[len++] = 0x00;
+ } else {
+ /* SDMF Message waiting */
+ msg[len++] = 0x6;
+ /* Length is 3 */
+ msg[len++] = 3;
+ if (active) {
+ msg[len++] = 0x42;
+ msg[len++] = 0x42;
+ msg[len++] = 0x42;
+ } else {
+ msg[len++] = 0x6f;
+ msg[len++] = 0x6f;
+ msg[len++] = 0x6f;
+ }
+ }
+ sum = 0;
+ for (x = 0; x < len; x++)
+ sum += msg[x];
+ sum = (256 - (sum & 255));
+ msg[len++] = sum;
+ /* Wait a half a second */
+ for (x = 0; x < 4000; x++)
+ PUT_BYTE(0x7f);
+ /* Transmit 30 0x55's (looks like a square wave) for channel seizure */
+ for (x = 0; x < 30; x++)
+ PUT_CLID(0x55);
+ /* Send 170ms of callerid marks */
+ for (x = 0; x < 170; x++)
+ PUT_CLID_MARKMS;
+ for (x = 0; x < len; x++) {
+ PUT_CLID(msg[x]);
+ }
+ /* Send 50 more ms of marks */
+ for (x = 0; x < 50; x++)
+ PUT_CLID_MARKMS;
+ return bytes;
+}
+
+int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, int codec)
+{
+ int bytes=0;
+ int x, sum;
+ int len;
+
+ /* Initial carriers (real/imaginary) */
+ float cr = 1.0;
+ float ci = 0.0;
+ float scont = 0.0;
+ char msg[256];
+ len = callerid_genmsg(msg, sizeof(msg), number, name, flags);
+ if (!callwaiting) {
+ /* Wait a half a second */
+ for (x = 0; x < 4000; x++)
+ PUT_BYTE(0x7f);
+ /* Transmit 30 0x55's (looks like a square wave) for channel seizure */
+ for (x = 0; x < 30; x++)
+ PUT_CLID(0x55);
+ }
+ /* Send 150ms of callerid marks */
+ for (x = 0; x < 150; x++)
+ PUT_CLID_MARKMS;
+ /* Send 0x80 indicating MDMF format */
+ PUT_CLID(0x80);
+ /* Put length of whole message */
+ PUT_CLID(len);
+ sum = 0x80 + strlen(msg);
+ /* Put each character of message and update checksum */
+ for (x = 0; x < len; x++) {
+ PUT_CLID(msg[x]);
+ sum += msg[x];
+ }
+ /* Send 2's compliment of sum */
+ PUT_CLID(256 - (sum & 255));
+
+ /* Send 50 more ms of marks */
+ for (x = 0; x < 50; x++)
+ PUT_CLID_MARKMS;
+
+ return bytes;
+}
+
+/*! \brief Clean up phone string
+ * remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets.
+ * Basically, remove anything that could be invalid in a pattern.
+ */
+void ast_shrink_phone_number(char *n)
+{
+ int x, y=0;
+ int bracketed = 0;
+
+ for (x = 0; n[x]; x++) {
+ switch (n[x]) {
+ case '[':
+ bracketed++;
+ n[y++] = n[x];
+ break;
+ case ']':
+ bracketed--;
+ n[y++] = n[x];
+ break;
+ case '-':
+ if (bracketed)
+ n[y++] = n[x];
+ break;
+ case '.':
+ if (!n[x+1])
+ n[y++] = n[x];
+ break;
+ default:
+ /* ignore parenthesis and whitespace */
+ if (!strchr("( )", n[x]))
+ n[y++] = n[x];
+ }
+ }
+ n[y] = '\0';
+}
+
+/*! \brief Checks if phone number consists of valid characters
+ \param exten String that needs to be checked
+ \param valid Valid characters in string
+ \return 1 if valid string, 0 if string contains invalid characters
+*/
+static int ast_is_valid_string(const char *exten, const char *valid)
+{
+ int x;
+
+ if (ast_strlen_zero(exten))
+ return 0;
+ for (x = 0; exten[x]; x++)
+ if (!strchr(valid, exten[x]))
+ return 0;
+ return 1;
+}
+
+/*! \brief checks if string consists only of digits and * \# and +
+ \return 1 if string is valid AST phone number
+ \return 0 if not
+*/
+int ast_isphonenumber(const char *n)
+{
+ return ast_is_valid_string(n, "0123456789*#+");
+}
+
+/*! \brief checks if string consists only of digits and ( ) - * \# and +
+ Pre-qualifies the string for ast_shrink_phone_number()
+ \return 1 if string is valid AST shrinkable phone number
+ \return 0 if not
+*/
+int ast_is_shrinkable_phonenumber(const char *exten)
+{
+ return ast_is_valid_string(exten, "0123456789*#+()-.");
+}
+
+/*! \brief parse string for caller id information
+ \return always returns 0, as the code always returns something.
+ XXX note that 'name' is not parsed consistently e.g. we have
+
+ input location name
+ " foo bar " <123> 123 ' foo bar ' (with spaces around)
+ " foo bar " NULL 'foo bar' (without spaces around)
+ " foo bar <123>" 123 '" foo bar'
+ The parsing of leading and trailing space/quotes should be more consistent.
+*/
+int ast_callerid_parse(char *instr, char **name, char **location)
+{
+ char *ns, *ne, *ls, *le;
+
+ /* Try "name" <location> format or name <location> format */
+ if ((ls = strchr(instr, '<')) && (le = strchr(ls, '>'))) {
+ *ls = *le = '\0'; /* location found, trim off the brackets */
+ *location = ls + 1; /* and this is the result */
+ if ((ns = strchr(instr, '"')) && (ne = strchr(ns + 1, '"'))) {
+ *ns = *ne = '\0'; /* trim off the quotes */
+ *name = ns + 1; /* and this is the name */
+ } else { /* no quotes, trim off leading and trailing spaces */
+ *name = ast_skip_blanks(instr);
+ ast_trim_blanks(*name);
+ }
+ } else { /* no valid brackets */
+ char tmp[256];
+
+ ast_copy_string(tmp, instr, sizeof(tmp));
+ ast_shrink_phone_number(tmp);
+ if (ast_isphonenumber(tmp)) { /* Assume it's just a location */
+ *name = NULL;
+ strcpy(instr, tmp); /* safe, because tmp will always be the same size or smaller than instr */
+ *location = instr;
+ } else { /* Assume it's just a name. */
+ *location = NULL;
+ if ((ns = strchr(instr, '"')) && (ne = strchr(ns + 1, '"'))) {
+ *ns = *ne = '\0'; /* trim off the quotes */
+ *name = ns + 1; /* and this is the name */
+ } else { /* no quotes, trim off leading and trailing spaces */
+ *name = ast_skip_blanks(instr);
+ ast_trim_blanks(*name);
+ }
+ }
+ }
+ return 0;
+}
+
+static int __ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int callwaiting, int codec)
+{
+ if (ast_strlen_zero(name))
+ name = NULL;
+ if (ast_strlen_zero(number))
+ number = NULL;
+ return callerid_generate(buf, number, name, 0, callwaiting, codec);
+}
+
+int ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int codec)
+{
+ return __ast_callerid_generate(buf, name, number, 0, codec);
+}
+
+int ast_callerid_callwaiting_generate(unsigned char *buf, const char *name, const char *number, int codec)
+{
+ return __ast_callerid_generate(buf, name, number, 1, codec);
+}
+
+char *ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
+{
+ if (!unknown)
+ unknown = "<unknown>";
+ if (name && num)
+ snprintf(buf, bufsiz, "\"%s\" <%s>", name, num);
+ else if (name)
+ ast_copy_string(buf, name, bufsiz);
+ else if (num)
+ ast_copy_string(buf, num, bufsiz);
+ else
+ ast_copy_string(buf, unknown, bufsiz);
+ return buf;
+}
+
+int ast_callerid_split(const char *buf, char *name, int namelen, char *num, int numlen)
+{
+ char *tmp;
+ char *l = NULL, *n = NULL;
+
+ tmp = ast_strdupa(buf);
+ ast_callerid_parse(tmp, &n, &l);
+ if (n)
+ ast_copy_string(name, n, namelen);
+ else
+ name[0] = '\0';
+ if (l) {
+ ast_shrink_phone_number(l);
+ ast_copy_string(num, l, numlen);
+ } else
+ num[0] = '\0';
+ return 0;
+}
+
+/*! \brief Translation table for Caller ID Presentation settings */
+static struct {
+ int val;
+ const char *name;
+ const char *description;
+} pres_types[] = {
+ { AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "allowed_not_screened", "Presentation Allowed, Not Screened"},
+ { AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen", "Presentation Allowed, Passed Screen"},
+ { AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen", "Presentation Allowed, Failed Screen"},
+ { AST_PRES_ALLOWED_NETWORK_NUMBER, "allowed", "Presentation Allowed, Network Number"},
+ { AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "prohib_not_screened", "Presentation Prohibited, Not Screened"},
+ { AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen", "Presentation Prohibited, Passed Screen"},
+ { AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen", "Presentation Prohibited, Failed Screen"},
+ { AST_PRES_PROHIB_NETWORK_NUMBER, "prohib", "Presentation Prohibited, Network Number"},
+ { AST_PRES_NUMBER_NOT_AVAILABLE, "unavailable", "Number Unavailable"},
+};
+
+/*! \brief Convert caller ID text code to value
+ used in config file parsing
+ \param data text string
+ \return value AST_PRES_ from callerid.h
+*/
+int ast_parse_caller_presentation(const char *data)
+{
+ int i;
+
+ for (i = 0; i < ((sizeof(pres_types) / sizeof(pres_types[0]))); i++) {
+ if (!strcasecmp(pres_types[i].name, data))
+ return pres_types[i].val;
+ }
+
+ return -1;
+}
+
+/*! \brief Convert caller ID pres value to explanatory string
+ \param data value (see callerid.h AST_PRES_ )
+ \return string for human presentation
+*/
+const char *ast_describe_caller_presentation(int data)
+{
+ int i;
+
+ for (i = 0; i < ((sizeof(pres_types) / sizeof(pres_types[0]))); i++) {
+ if (pres_types[i].val == data)
+ return pres_types[i].description;
+ }
+
+ return "unknown";
+}
+
+/*! \brief Convert caller ID pres value to text code
+ \param data text string
+ \return string for config file
+*/
+const char *ast_named_caller_presentation(int data)
+{
+ int i;
+
+ for (i = 0; i < ((sizeof(pres_types) / sizeof(pres_types[0]))); i++) {
+ if (pres_types[i].val == data)
+ return pres_types[i].name;
+ }
+
+ return "unknown";
+}
diff --git a/trunk/main/cdr.c b/trunk/main/cdr.c
new file mode 100644
index 000000000..028e0eb14
--- /dev/null
+++ b/trunk/main/cdr.c
@@ -0,0 +1,1456 @@
+/*
+ * 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 Call Detail Record API
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note Includes code and algorithms from the Zapata library.
+ *
+ * \note We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
+ * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally
+ * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
+ * isn't properly generated and posted.
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/callerid.h"
+#include "asterisk/manager.h"
+#include "asterisk/causes.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/utils.h"
+#include "asterisk/sched.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/stringfields.h"
+
+/*! Default AMA flag for billing records (CDR's) */
+int ast_default_amaflags = AST_CDR_DOCUMENTATION;
+char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
+
+struct ast_cdr_beitem {
+ char name[20];
+ char desc[80];
+ ast_cdrbe be;
+ AST_RWLIST_ENTRY(ast_cdr_beitem) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(be_list, ast_cdr_beitem);
+
+struct ast_cdr_batch_item {
+ struct ast_cdr *cdr;
+ struct ast_cdr_batch_item *next;
+};
+
+static struct ast_cdr_batch {
+ int size;
+ struct ast_cdr_batch_item *head;
+ struct ast_cdr_batch_item *tail;
+} *batch = NULL;
+
+static struct sched_context *sched;
+static int cdr_sched = -1;
+static pthread_t cdr_thread = AST_PTHREADT_NULL;
+
+#define BATCH_SIZE_DEFAULT 100
+#define BATCH_TIME_DEFAULT 300
+#define BATCH_SCHEDULER_ONLY_DEFAULT 0
+#define BATCH_SAFE_SHUTDOWN_DEFAULT 1
+
+static int enabled; /*! Is the CDR subsystem enabled ? */
+static int unanswered;
+static int batchmode;
+static int batchsize;
+static int batchtime;
+static int batchscheduleronly;
+static int batchsafeshutdown;
+
+AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
+
+/* these are used to wake up the CDR thread when there's work to do */
+AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
+static ast_cond_t cdr_pending_cond;
+
+int check_cdr_enabled()
+{
+ return enabled;
+}
+
+int ast_cdr_log_unanswered(void)
+{
+ return unanswered;
+}
+
+/*! Register a CDR driver. Each registered CDR driver generates a CDR
+ \return 0 on success, -1 on failure
+*/
+int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
+{
+ struct ast_cdr_beitem *i = NULL;
+
+ if (!name)
+ return -1;
+
+ if (!be) {
+ ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
+ return -1;
+ }
+
+ AST_RWLIST_WRLOCK(&be_list);
+ AST_RWLIST_TRAVERSE(&be_list, i, list) {
+ if (!strcasecmp(name, i->name)) {
+ ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
+ AST_RWLIST_UNLOCK(&be_list);
+ return -1;
+ }
+ }
+
+ if (!(i = ast_calloc(1, sizeof(*i))))
+ return -1;
+
+ i->be = be;
+ ast_copy_string(i->name, name, sizeof(i->name));
+ ast_copy_string(i->desc, desc, sizeof(i->desc));
+
+ AST_RWLIST_INSERT_HEAD(&be_list, i, list);
+ AST_RWLIST_UNLOCK(&be_list);
+
+ return 0;
+}
+
+/*! unregister a CDR driver */
+void ast_cdr_unregister(const char *name)
+{
+ struct ast_cdr_beitem *i = NULL;
+
+ AST_RWLIST_WRLOCK(&be_list);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
+ if (!strcasecmp(name, i->name)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(2, "Unregistered '%s' CDR backend\n", name);
+ ast_free(i);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&be_list);
+}
+
+/*! Duplicate a CDR record
+ \returns Pointer to new CDR record
+*/
+struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
+{
+ struct ast_cdr *newcdr;
+
+ if (!cdr) /* don't die if we get a null cdr pointer */
+ return NULL;
+ newcdr = ast_cdr_alloc();
+ if (!newcdr)
+ return NULL;
+
+ memcpy(newcdr, cdr, sizeof(*newcdr));
+ /* The varshead is unusable, volatile even, after the memcpy so we take care of that here */
+ memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
+ ast_cdr_copy_vars(newcdr, cdr);
+ newcdr->next = NULL;
+
+ return newcdr;
+}
+
+static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
+{
+ if (ast_strlen_zero(name))
+ return NULL;
+
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ struct ast_var_t *variables;
+ struct varshead *headp = &cdr->varshead;
+ AST_LIST_TRAVERSE(headp, variables, entries) {
+ if (!strcasecmp(name, ast_var_name(variables)))
+ return ast_var_value(variables);
+ }
+ }
+
+ return NULL;
+}
+
+static void cdr_get_tv(struct timeval tv, const char *fmt, char *buf, int bufsize)
+{
+ if (fmt == NULL) { /* raw mode */
+ snprintf(buf, bufsize, "%ld.%06ld", (long)tv.tv_sec, (long)tv.tv_usec);
+ } else {
+ struct ast_tm tm;
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(buf, bufsize, fmt, &tm);
+ }
+}
+
+/*! CDR channel variable retrieval */
+void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
+{
+ const char *fmt = "%Y-%m-%d %T";
+ const char *varbuf;
+
+ if (!cdr) /* don't die if the cdr is null */
+ return;
+
+ *ret = NULL;
+ /* special vars (the ones from the struct ast_cdr when requested by name)
+ I'd almost say we should convert all the stringed vals to vars */
+
+ if (!strcasecmp(name, "clid"))
+ ast_copy_string(workspace, cdr->clid, workspacelen);
+ else if (!strcasecmp(name, "src"))
+ ast_copy_string(workspace, cdr->src, workspacelen);
+ else if (!strcasecmp(name, "dst"))
+ ast_copy_string(workspace, cdr->dst, workspacelen);
+ else if (!strcasecmp(name, "dcontext"))
+ ast_copy_string(workspace, cdr->dcontext, workspacelen);
+ else if (!strcasecmp(name, "channel"))
+ ast_copy_string(workspace, cdr->channel, workspacelen);
+ else if (!strcasecmp(name, "dstchannel"))
+ ast_copy_string(workspace, cdr->dstchannel, workspacelen);
+ else if (!strcasecmp(name, "lastapp"))
+ ast_copy_string(workspace, cdr->lastapp, workspacelen);
+ else if (!strcasecmp(name, "lastdata"))
+ ast_copy_string(workspace, cdr->lastdata, workspacelen);
+ else if (!strcasecmp(name, "start"))
+ cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
+ else if (!strcasecmp(name, "answer"))
+ cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
+ else if (!strcasecmp(name, "end"))
+ cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
+ else if (!strcasecmp(name, "duration"))
+ snprintf(workspace, workspacelen, "%ld", cdr->duration ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
+ else if (!strcasecmp(name, "billsec"))
+ snprintf(workspace, workspacelen, "%ld", cdr->billsec || cdr->answer.tv_sec == 0 ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);
+ else if (!strcasecmp(name, "disposition")) {
+ if (raw) {
+ snprintf(workspace, workspacelen, "%ld", cdr->disposition);
+ } else {
+ ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
+ }
+ } else if (!strcasecmp(name, "amaflags")) {
+ if (raw) {
+ snprintf(workspace, workspacelen, "%ld", cdr->amaflags);
+ } else {
+ ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
+ }
+ } else if (!strcasecmp(name, "accountcode"))
+ ast_copy_string(workspace, cdr->accountcode, workspacelen);
+ else if (!strcasecmp(name, "uniqueid"))
+ ast_copy_string(workspace, cdr->uniqueid, workspacelen);
+ else if (!strcasecmp(name, "userfield"))
+ ast_copy_string(workspace, cdr->userfield, workspacelen);
+ else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
+ ast_copy_string(workspace, varbuf, workspacelen);
+ else
+ workspace[0] = '\0';
+
+ if (!ast_strlen_zero(workspace))
+ *ret = workspace;
+}
+
+/* readonly cdr variables */
+static const char *cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
+ "lastapp", "lastdata", "start", "answer", "end", "duration",
+ "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
+ "userfield", NULL };
+/*! Set a CDR channel variable
+ \note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
+*/
+int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ int x;
+
+ if (!cdr) /* don't die if the cdr is null */
+ return -1;
+
+ for (x = 0; cdr_readonly_vars[x]; x++) {
+ if (!strcasecmp(name, cdr_readonly_vars[x])) {
+ ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
+ return -1;
+ }
+ }
+
+ if (!cdr) {
+ ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
+ return -1;
+ }
+
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ headp = &cdr->varshead;
+ AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
+ if (!strcasecmp(ast_var_name(newvariable), name)) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE_CURRENT(entries);
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (value) {
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
+{
+ struct ast_var_t *variables, *newvariable = NULL;
+ struct varshead *headpa, *headpb;
+ const char *var, *val;
+ int x = 0;
+
+ if (!to_cdr || !from_cdr) /* don't die if one of the pointers is null */
+ return 0;
+
+ headpa = &from_cdr->varshead;
+ headpb = &to_cdr->varshead;
+
+ AST_LIST_TRAVERSE(headpa,variables,entries) {
+ if (variables &&
+ (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
+ !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
+ newvariable = ast_var_assign(var, val);
+ AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
+ x++;
+ }
+ }
+
+ return x;
+}
+
+int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
+{
+ struct ast_var_t *variables;
+ const char *var, *val;
+ char *tmp;
+ char workspace[256];
+ int total = 0, x = 0, i;
+
+ (*buf)->used = 0;
+ (*buf)->str[0] = '\0';
+
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ if (++x > 1)
+ ast_str_append(buf, 0, "\n");
+
+ AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
+ if (variables &&
+ (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
+ !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
+ if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, val, sep) < 0) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ } else
+ break;
+ }
+
+ for (i = 0; cdr_readonly_vars[i]; i++) {
+ ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
+ if (!tmp)
+ continue;
+
+ if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ }
+ }
+
+ return total;
+}
+
+
+void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
+{
+
+ /* clear variables */
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ struct ast_var_t *vardata;
+ struct varshead *headp = &cdr->varshead;
+ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
+ ast_var_delete(vardata);
+ }
+}
+
+/*! \brief print a warning if cdr already posted */
+static void check_post(struct ast_cdr *cdr)
+{
+ if (!cdr)
+ return;
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
+}
+
+void ast_cdr_free(struct ast_cdr *cdr)
+{
+
+ while (cdr) {
+ struct ast_cdr *next = cdr->next;
+ char *chan = S_OR(cdr->channel, "<unknown>");
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_POSTED) && !ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' not posted\n", chan);
+ if (ast_tvzero(cdr->end))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' lacks end\n", chan);
+ if (ast_tvzero(cdr->start))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' lacks start\n", chan);
+
+ ast_cdr_free_vars(cdr, 0);
+ ast_free(cdr);
+ cdr = next;
+ }
+}
+
+/*! \brief the same as a cdr_free call, only with no checks; just get rid of it */
+void ast_cdr_discard(struct ast_cdr *cdr)
+{
+ while (cdr) {
+ struct ast_cdr *next = cdr->next;
+
+ ast_cdr_free_vars(cdr, 0);
+ ast_free(cdr);
+ cdr = next;
+ }
+}
+
+struct ast_cdr *ast_cdr_alloc(void)
+{
+ struct ast_cdr *x;
+ x = ast_calloc(1, sizeof(*x));
+ if (!x)
+ ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
+ return x;
+}
+
+static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
+{
+ struct ast_var_t *variablesfrom,*variablesto;
+ struct varshead *headpfrom = &to->varshead;
+ struct varshead *headpto = &from->varshead;
+ AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
+ /* for every var in from, stick it in to */
+ const char *fromvarname, *fromvarval;
+ const char *tovarname = NULL, *tovarval = NULL;
+ fromvarname = ast_var_name(variablesfrom);
+ fromvarval = ast_var_value(variablesfrom);
+ tovarname = 0;
+
+ /* now, quick see if that var is in the 'to' cdr already */
+ AST_LIST_TRAVERSE(headpto, variablesto, entries) {
+
+ /* now, quick see if that var is in the 'to' cdr already */
+ if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
+ tovarname = ast_var_name(variablesto);
+ tovarval = ast_var_value(variablesto);
+ break;
+ }
+ }
+ if (tovarname && strcasecmp(fromvarval,tovarval) != 0) { /* this message here to see how irritating the userbase finds it */
+ ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
+ continue;
+ } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0) /* if they are the same, the job is done */
+ continue;
+
+ /* rip this var out of the from cdr, and stick it in the to cdr */
+ AST_LIST_MOVE_CURRENT(headpto, entries);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+}
+
+void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
+{
+ struct ast_cdr *zcdr;
+ struct ast_cdr *lto = NULL;
+ struct ast_cdr *lfrom = NULL;
+ int discard_from = 0;
+
+ if (!to || !from)
+ return;
+
+ /* don't merge into locked CDR's -- it's bad business */
+ if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
+ zcdr = to; /* safety valve? */
+ while (to->next) {
+ lto = to;
+ to = to->next;
+ }
+
+ if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
+ ast_log(LOG_WARNING, "Merging into locked CDR... no choice.");
+ to = zcdr; /* safety-- if all there are is locked CDR's, then.... ?? */
+ lto = NULL;
+ }
+ }
+
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
+ struct ast_cdr *llfrom = NULL;
+ discard_from = 1;
+ if (lto) {
+ /* insert the from stuff after lto */
+ lto->next = from;
+ lfrom = from;
+ while (lfrom && lfrom->next) {
+ if (!lfrom->next->next)
+ llfrom = lfrom;
+ lfrom = lfrom->next;
+ }
+ /* rip off the last entry and put a copy of the to at the end */
+ llfrom->next = to;
+ from = lfrom;
+ } else {
+ /* save copy of the current *to cdr */
+ struct ast_cdr tcdr;
+ memcpy(&tcdr, to, sizeof(tcdr));
+ /* copy in the locked from cdr */
+ memcpy(to, from, sizeof(*to));
+ lfrom = from;
+ while (lfrom && lfrom->next) {
+ if (!lfrom->next->next)
+ llfrom = lfrom;
+ lfrom = lfrom->next;
+ }
+ from->next = NULL;
+ /* rip off the last entry and put a copy of the to at the end */
+ if (llfrom == from)
+ to = to->next = ast_cdr_dup(&tcdr);
+ else
+ to = llfrom->next = ast_cdr_dup(&tcdr);
+ from = lfrom;
+ }
+ }
+
+ if (!ast_tvzero(from->start)) {
+ if (!ast_tvzero(to->start)) {
+ if (ast_tvcmp(to->start, from->start) > 0 ) {
+ to->start = from->start; /* use the earliest time */
+ from->start = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ /* else nothing to do */
+ } else {
+ to->start = from->start;
+ from->start = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ }
+ if (!ast_tvzero(from->answer)) {
+ if (!ast_tvzero(to->answer)) {
+ if (ast_tvcmp(to->answer, from->answer) > 0 ) {
+ to->answer = from->answer; /* use the earliest time */
+ from->answer = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ /* we got the earliest answer time, so we'll settle for that? */
+ } else {
+ to->answer = from->answer;
+ from->answer = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ }
+ if (!ast_tvzero(from->end)) {
+ if (!ast_tvzero(to->end)) {
+ if (ast_tvcmp(to->end, from->end) < 0 ) {
+ to->end = from->end; /* use the latest time */
+ from->end = ast_tv(0,0); /* we actively "steal" these values */
+ to->duration = to->end.tv_sec - to->start.tv_sec; /* don't forget to update the duration, billsec, when we set end */
+ to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
+ }
+ /* else, nothing to do */
+ } else {
+ to->end = from->end;
+ from->end = ast_tv(0,0); /* we actively "steal" these values */
+ to->duration = to->end.tv_sec - to->start.tv_sec;
+ to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
+ }
+ }
+ if (to->disposition < from->disposition) {
+ to->disposition = from->disposition;
+ from->disposition = AST_CDR_NOANSWER;
+ }
+ if (ast_strlen_zero(to->lastapp) && !ast_strlen_zero(from->lastapp)) {
+ ast_copy_string(to->lastapp, from->lastapp, sizeof(to->lastapp));
+ from->lastapp[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->lastdata) && !ast_strlen_zero(from->lastdata)) {
+ ast_copy_string(to->lastdata, from->lastdata, sizeof(to->lastdata));
+ from->lastdata[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->dcontext) && !ast_strlen_zero(from->dcontext)) {
+ ast_copy_string(to->dcontext, from->dcontext, sizeof(to->dcontext));
+ from->dcontext[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->dstchannel) && !ast_strlen_zero(from->dstchannel)) {
+ ast_copy_string(to->dstchannel, from->dstchannel, sizeof(to->dstchannel));
+ from->dstchannel[0] = 0; /* theft */
+ }
+ if (!ast_strlen_zero(from->channel) && (ast_strlen_zero(to->channel) || !strncasecmp(from->channel, "Agent/", 6))) {
+ ast_copy_string(to->channel, from->channel, sizeof(to->channel));
+ from->channel[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->src) && !ast_strlen_zero(from->src)) {
+ ast_copy_string(to->src, from->src, sizeof(to->src));
+ from->src[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->clid) && !ast_strlen_zero(from->clid)) {
+ ast_copy_string(to->clid, from->clid, sizeof(to->clid));
+ from->clid[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->dst) && !ast_strlen_zero(from->dst)) {
+ ast_copy_string(to->dst, from->dst, sizeof(to->dst));
+ from->dst[0] = 0; /* theft */
+ }
+ if (!to->amaflags)
+ to->amaflags = AST_CDR_DOCUMENTATION;
+ if (!from->amaflags)
+ from->amaflags = AST_CDR_DOCUMENTATION; /* make sure both amaflags are set to something (DOC is default) */
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (to->amaflags == AST_CDR_DOCUMENTATION && from->amaflags != AST_CDR_DOCUMENTATION)) {
+ to->amaflags = from->amaflags;
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) {
+ ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) {
+ ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
+ }
+ /* flags, varsead, ? */
+ cdr_merge_vars(from, to);
+
+ if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS))
+ ast_set_flag(to, AST_CDR_FLAG_KEEP_VARS);
+ if (ast_test_flag(from, AST_CDR_FLAG_POSTED))
+ ast_set_flag(to, AST_CDR_FLAG_POSTED);
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED))
+ ast_set_flag(to, AST_CDR_FLAG_LOCKED);
+ if (ast_test_flag(from, AST_CDR_FLAG_CHILD))
+ ast_set_flag(to, AST_CDR_FLAG_CHILD);
+ if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED))
+ ast_set_flag(to, AST_CDR_FLAG_POST_DISABLED);
+
+ /* last, but not least, we need to merge any forked CDRs to the 'to' cdr */
+ while (from->next) {
+ /* just rip 'em off the 'from' and insert them on the 'to' */
+ zcdr = from->next;
+ from->next = zcdr->next;
+ zcdr->next = NULL;
+ /* zcdr is now ripped from the current list; */
+ ast_cdr_append(to, zcdr);
+ }
+ if (discard_from)
+ ast_cdr_discard(from);
+}
+
+void ast_cdr_start(struct ast_cdr *cdr)
+{
+ char *chan;
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ chan = S_OR(cdr->channel, "<unknown>");
+ check_post(cdr);
+ cdr->start = ast_tvnow();
+ }
+ }
+}
+
+void ast_cdr_answer(struct ast_cdr *cdr)
+{
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (cdr->disposition < AST_CDR_ANSWERED)
+ cdr->disposition = AST_CDR_ANSWERED;
+ if (ast_tvzero(cdr->answer))
+ cdr->answer = ast_tvnow();
+ }
+ }
+}
+
+void ast_cdr_busy(struct ast_cdr *cdr)
+{
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (cdr->disposition < AST_CDR_BUSY)
+ cdr->disposition = AST_CDR_BUSY;
+ }
+ }
+}
+
+void ast_cdr_failed(struct ast_cdr *cdr)
+{
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (cdr->disposition < AST_CDR_FAILED)
+ cdr->disposition = AST_CDR_FAILED;
+ }
+ }
+}
+
+void ast_cdr_noanswer(struct ast_cdr *cdr)
+{
+ char *chan;
+
+ while (cdr) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
+ ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
+ if (cdr->disposition < AST_CDR_NOANSWER)
+ cdr->disposition = AST_CDR_NOANSWER;
+ }
+ cdr = cdr->next;
+ }
+}
+
+/* everywhere ast_cdr_disposition is called, it will call ast_cdr_failed()
+ if ast_cdr_disposition returns a non-zero value */
+
+int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
+{
+ int res = 0;
+
+ for (; cdr; cdr = cdr->next) {
+ switch (cause) { /* handle all the non failure, busy cases, return 0 not to set disposition,
+ return -1 to set disposition to FAILED */
+ case AST_CAUSE_BUSY:
+ ast_cdr_busy(cdr);
+ break;
+ case AST_CAUSE_NORMAL:
+ break;
+ default:
+ res = -1;
+ }
+ }
+ return res;
+}
+
+void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
+{
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
+ }
+ }
+}
+
+void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
+{
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (!app)
+ app = "";
+ ast_copy_string(cdr->lastapp, app, sizeof(cdr->lastapp));
+ if (!data)
+ data = "";
+ ast_copy_string(cdr->lastdata, data, sizeof(cdr->lastdata));
+ }
+ }
+}
+
+/* set cid info for one record */
+static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
+{
+ /* Grab source from ANI or normal Caller*ID */
+ const char *num = S_OR(c->cid.cid_ani, c->cid.cid_num);
+ if (!cdr)
+ return;
+ if (!ast_strlen_zero(c->cid.cid_name)) {
+ if (!ast_strlen_zero(num)) /* both name and number */
+ snprintf(cdr->clid, sizeof(cdr->clid), "\"%s\" <%s>", c->cid.cid_name, num);
+ else /* only name */
+ ast_copy_string(cdr->clid, c->cid.cid_name, sizeof(cdr->clid));
+ } else if (!ast_strlen_zero(num)) { /* only number */
+ ast_copy_string(cdr->clid, num, sizeof(cdr->clid));
+ } else { /* nothing known */
+ cdr->clid[0] = '\0';
+ }
+ ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
+
+}
+int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
+{
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ set_one_cid(cdr, c);
+ }
+ return 0;
+}
+
+int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
+{
+ char *chan;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ chan = S_OR(cdr->channel, "<unknown>");
+ ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
+ set_one_cid(cdr, c);
+
+ cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NULL;
+ cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
+ ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
+ /* Destination information */
+ ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
+ ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
+ /* Unique call identifier */
+ ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
+ }
+ }
+ return 0;
+}
+
+void ast_cdr_end(struct ast_cdr *cdr)
+{
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (ast_tvzero(cdr->end))
+ cdr->end = ast_tvnow();
+ if (ast_tvzero(cdr->start)) {
+ ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
+ cdr->disposition = AST_CDR_FAILED;
+ } else
+ cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
+ cdr->billsec = ast_tvzero(cdr->answer) ? 0 : cdr->end.tv_sec - cdr->answer.tv_sec;
+ }
+ }
+}
+
+char *ast_cdr_disp2str(int disposition)
+{
+ switch (disposition) {
+ case AST_CDR_NULL:
+ return "NO ANSWER"; /* by default, for backward compatibility */
+ case AST_CDR_NOANSWER:
+ return "NO ANSWER";
+ case AST_CDR_FAILED:
+ return "FAILED";
+ case AST_CDR_BUSY:
+ return "BUSY";
+ case AST_CDR_ANSWERED:
+ return "ANSWERED";
+ }
+ return "UNKNOWN";
+}
+
+/*! Converts AMA flag to printable string */
+char *ast_cdr_flags2str(int flag)
+{
+ switch (flag) {
+ case AST_CDR_OMIT:
+ return "OMIT";
+ case AST_CDR_BILLING:
+ return "BILLING";
+ case AST_CDR_DOCUMENTATION:
+ return "DOCUMENTATION";
+ }
+ return "Unknown";
+}
+
+int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
+{
+ struct ast_cdr *cdr = chan->cdr;
+ char buf[BUFSIZ/2] = "";
+ if (!ast_strlen_zero(chan->accountcode))
+ ast_copy_string(buf, chan->accountcode, sizeof(buf));
+
+ ast_string_field_set(chan, accountcode, account);
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
+ }
+ }
+
+ /* Signal change of account code to manager */
+ manager_event(EVENT_FLAG_CALL, "NewAccountCode", "Channel: %s\r\nUniqueid: %s\r\nAccountCode: %s\r\nOldAccountCode: %s\r\n", chan->name, chan->uniqueid, chan->accountcode, buf);
+ return 0;
+}
+
+int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
+{
+ struct ast_cdr *cdr;
+ int newflag = ast_cdr_amaflags2int(flag);
+ if (newflag) {
+ for (cdr = chan->cdr; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ cdr->amaflags = newflag;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
+{
+ struct ast_cdr *cdr = chan->cdr;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
+ }
+
+ return 0;
+}
+
+int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
+{
+ struct ast_cdr *cdr = chan->cdr;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ int len = strlen(cdr->userfield);
+
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
+ }
+
+ return 0;
+}
+
+int ast_cdr_update(struct ast_channel *c)
+{
+ struct ast_cdr *cdr = c->cdr;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ set_one_cid(cdr, c);
+
+ /* Copy account code et-al */
+ ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
+
+ /* Destination information */ /* XXX privilege macro* ? */
+ ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
+ ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
+ }
+ }
+
+ return 0;
+}
+
+int ast_cdr_amaflags2int(const char *flag)
+{
+ if (!strcasecmp(flag, "default"))
+ return 0;
+ if (!strcasecmp(flag, "omit"))
+ return AST_CDR_OMIT;
+ if (!strcasecmp(flag, "billing"))
+ return AST_CDR_BILLING;
+ if (!strcasecmp(flag, "documentation"))
+ return AST_CDR_DOCUMENTATION;
+ return -1;
+}
+
+static void post_cdr(struct ast_cdr *cdr)
+{
+ char *chan;
+ struct ast_cdr_beitem *i;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ chan = S_OR(cdr->channel, "<unknown>");
+ check_post(cdr);
+ if (ast_tvzero(cdr->end))
+ ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
+ if (ast_tvzero(cdr->start))
+ ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
+ ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
+ continue;
+ AST_RWLIST_RDLOCK(&be_list);
+ AST_RWLIST_TRAVERSE(&be_list, i, list) {
+ i->be(cdr);
+ }
+ AST_RWLIST_UNLOCK(&be_list);
+ }
+}
+
+void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
+{
+ struct ast_cdr *dup;
+ struct ast_flags flags = { 0 };
+
+ if (_flags)
+ ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
+
+ for ( ; cdr ; cdr = cdr->next) {
+ /* Detach if post is requested */
+ if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
+ ast_cdr_end(cdr);
+ if ((dup = ast_cdr_dup(cdr))) {
+ ast_cdr_detach(dup);
+ }
+ ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
+ }
+
+ /* clear variables */
+ if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
+ ast_cdr_free_vars(cdr, 0);
+ }
+
+ /* Reset to initial state */
+ ast_clear_flag(cdr, AST_FLAGS_ALL);
+ memset(&cdr->start, 0, sizeof(cdr->start));
+ memset(&cdr->end, 0, sizeof(cdr->end));
+ memset(&cdr->answer, 0, sizeof(cdr->answer));
+ cdr->billsec = 0;
+ cdr->duration = 0;
+ ast_cdr_start(cdr);
+ cdr->disposition = AST_CDR_NULL;
+ }
+ }
+}
+
+struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
+{
+ struct ast_cdr *ret;
+
+ if (cdr) {
+ ret = cdr;
+
+ while (cdr->next)
+ cdr = cdr->next;
+ cdr->next = newcdr;
+ } else {
+ ret = newcdr;
+ }
+
+ return ret;
+}
+
+/*! \note Don't call without cdr_batch_lock */
+static void reset_batch(void)
+{
+ batch->size = 0;
+ batch->head = NULL;
+ batch->tail = NULL;
+}
+
+/*! \note Don't call without cdr_batch_lock */
+static int init_batch(void)
+{
+ /* This is the single meta-batch used to keep track of all CDRs during the entire life of the program */
+ if (!(batch = ast_malloc(sizeof(*batch))))
+ return -1;
+
+ reset_batch();
+
+ return 0;
+}
+
+static void *do_batch_backend_process(void *data)
+{
+ struct ast_cdr_batch_item *processeditem;
+ struct ast_cdr_batch_item *batchitem = data;
+
+ /* Push each CDR into storage mechanism(s) and free all the memory */
+ while (batchitem) {
+ post_cdr(batchitem->cdr);
+ ast_cdr_free(batchitem->cdr);
+ processeditem = batchitem;
+ batchitem = batchitem->next;
+ ast_free(processeditem);
+ }
+
+ return NULL;
+}
+
+void ast_cdr_submit_batch(int shutdown)
+{
+ struct ast_cdr_batch_item *oldbatchitems = NULL;
+ pthread_t batch_post_thread = AST_PTHREADT_NULL;
+
+ /* if there's no batch, or no CDRs in the batch, then there's nothing to do */
+ if (!batch || !batch->head)
+ return;
+
+ /* move the old CDRs aside, and prepare a new CDR batch */
+ ast_mutex_lock(&cdr_batch_lock);
+ oldbatchitems = batch->head;
+ reset_batch();
+ ast_mutex_unlock(&cdr_batch_lock);
+
+ /* if configured, spawn a new thread to post these CDRs,
+ also try to save as much as possible if we are shutting down safely */
+ if (batchscheduleronly || shutdown) {
+ ast_debug(1, "CDR single-threaded batch processing begins now\n");
+ do_batch_backend_process(oldbatchitems);
+ } else {
+ if (ast_pthread_create_detached_background(&batch_post_thread, NULL, do_batch_backend_process, oldbatchitems)) {
+ ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
+ do_batch_backend_process(oldbatchitems);
+ } else {
+ ast_debug(1, "CDR multi-threaded batch processing begins now\n");
+ }
+ }
+}
+
+static int submit_scheduled_batch(const void *data)
+{
+ ast_cdr_submit_batch(0);
+ /* manually reschedule from this point in time */
+ cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
+ /* returning zero so the scheduler does not automatically reschedule */
+ return 0;
+}
+
+static void submit_unscheduled_batch(void)
+{
+ /* this is okay since we are not being called from within the scheduler */
+ if (cdr_sched > -1)
+ ast_sched_del(sched, cdr_sched);
+ /* schedule the submission to occur ASAP (1 ms) */
+ cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
+ /* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */
+ ast_mutex_lock(&cdr_pending_lock);
+ ast_cond_signal(&cdr_pending_cond);
+ ast_mutex_unlock(&cdr_pending_lock);
+}
+
+void ast_cdr_detach(struct ast_cdr *cdr)
+{
+ struct ast_cdr_batch_item *newtail;
+ int curr;
+
+ if (!cdr)
+ return;
+
+ /* maybe they disabled CDR stuff completely, so just drop it */
+ if (!enabled) {
+ ast_debug(1, "Dropping CDR !\n");
+ ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
+ ast_cdr_free(cdr);
+ return;
+ }
+
+ /* post stuff immediately if we are not in batch mode, this is legacy behaviour */
+ if (!batchmode) {
+ post_cdr(cdr);
+ ast_cdr_free(cdr);
+ return;
+ }
+
+ /* otherwise, each CDR gets put into a batch list (at the end) */
+ ast_debug(1, "CDR detaching from this thread\n");
+
+ /* we'll need a new tail for every CDR */
+ if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
+ post_cdr(cdr);
+ ast_cdr_free(cdr);
+ return;
+ }
+
+ /* don't traverse a whole list (just keep track of the tail) */
+ ast_mutex_lock(&cdr_batch_lock);
+ if (!batch)
+ init_batch();
+ if (!batch->head) {
+ /* new batch is empty, so point the head at the new tail */
+ batch->head = newtail;
+ } else {
+ /* already got a batch with something in it, so just append a new tail */
+ batch->tail->next = newtail;
+ }
+ newtail->cdr = cdr;
+ batch->tail = newtail;
+ curr = batch->size++;
+ ast_mutex_unlock(&cdr_batch_lock);
+
+ /* if we have enough stuff to post, then do it */
+ if (curr >= (batchsize - 1))
+ submit_unscheduled_batch();
+}
+
+static void *do_cdr(void *data)
+{
+ struct timespec timeout;
+ int schedms;
+ int numevents = 0;
+
+ for (;;) {
+ struct timeval now;
+ schedms = ast_sched_wait(sched);
+ /* this shouldn't happen, but provide a 1 second default just in case */
+ if (schedms <= 0)
+ schedms = 1000;
+ now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
+ timeout.tv_sec = now.tv_sec;
+ timeout.tv_nsec = now.tv_usec * 1000;
+ /* prevent stuff from clobbering cdr_pending_cond, then wait on signals sent to it until the timeout expires */
+ ast_mutex_lock(&cdr_pending_lock);
+ ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
+ numevents = ast_sched_runq(sched);
+ ast_mutex_unlock(&cdr_pending_lock);
+ ast_debug(2, "Processed %d scheduled CDR batches from the run queue\n", numevents);
+ }
+
+ return NULL;
+}
+
+static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_cdr_beitem *beitem=NULL;
+ int cnt=0;
+ long nextbatchtime=0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cdr status";
+ e->usage =
+ "Usage: cdr status\n"
+ " Displays the Call Detail Record engine system status.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
+ ast_cli(a->fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
+ if (enabled) {
+ ast_cli(a->fd, "CDR output unanswered calls: %s\n", unanswered ? "yes" : "no");
+ if (batchmode) {
+ if (batch)
+ cnt = batch->size;
+ if (cdr_sched > -1)
+ nextbatchtime = ast_sched_when(sched, cdr_sched);
+ ast_cli(a->fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
+ ast_cli(a->fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
+ ast_cli(a->fd, "CDR current batch size: %d record%s\n", cnt, ESS(cnt));
+ ast_cli(a->fd, "CDR maximum batch size: %d record%s\n", batchsize, ESS(batchsize));
+ ast_cli(a->fd, "CDR maximum batch time: %d second%s\n", batchtime, ESS(batchtime));
+ ast_cli(a->fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, ESS(nextbatchtime));
+ }
+ AST_RWLIST_RDLOCK(&be_list);
+ AST_RWLIST_TRAVERSE(&be_list, beitem, list) {
+ ast_cli(a->fd, "CDR registered backend: %s\n", beitem->name);
+ }
+ AST_RWLIST_UNLOCK(&be_list);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cdr submit";
+ e->usage =
+ "Usage: cdr submit\n"
+ " Posts all pending batched CDR data to the configured CDR backend engine modules.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ submit_unscheduled_batch();
+ ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
+static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
+
+static int do_reload(int reload)
+{
+ struct ast_config *config;
+ const char *enabled_value;
+ const char *unanswered_value;
+ const char *batched_value;
+ const char *scheduleronly_value;
+ const char *batchsafeshutdown_value;
+ const char *size_value;
+ const char *time_value;
+ const char *end_before_h_value;
+ int cfg_size;
+ int cfg_time;
+ int was_enabled;
+ int was_batchmode;
+ int res=0;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((config = ast_config_load("cdr.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ ast_mutex_lock(&cdr_batch_lock);
+
+ batchsize = BATCH_SIZE_DEFAULT;
+ batchtime = BATCH_TIME_DEFAULT;
+ batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
+ batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
+ was_enabled = enabled;
+ was_batchmode = batchmode;
+ enabled = 1;
+ batchmode = 0;
+
+ /* don't run the next scheduled CDR posting while reloading */
+ if (cdr_sched > -1)
+ ast_sched_del(sched, cdr_sched);
+
+ if (config) {
+ if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
+ enabled = ast_true(enabled_value);
+ }
+ if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
+ unanswered = ast_true(unanswered_value);
+ }
+ if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
+ batchmode = ast_true(batched_value);
+ }
+ if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
+ batchscheduleronly = ast_true(scheduleronly_value);
+ }
+ if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
+ batchsafeshutdown = ast_true(batchsafeshutdown_value);
+ }
+ if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
+ if (sscanf(size_value, "%d", &cfg_size) < 1)
+ ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
+ else if (size_value < 0)
+ ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
+ else
+ batchsize = cfg_size;
+ }
+ if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
+ if (sscanf(time_value, "%d", &cfg_time) < 1)
+ ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
+ else if (time_value < 0)
+ ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
+ else
+ batchtime = cfg_time;
+ }
+ if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
+ ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
+ }
+
+ if (enabled && !batchmode) {
+ ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
+ } else if (enabled && batchmode) {
+ cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
+ ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
+ } else {
+ ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
+ }
+
+ /* if this reload enabled the CDR batch mode, create the background thread
+ if it does not exist */
+ if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
+ ast_cond_init(&cdr_pending_cond, NULL);
+ if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
+ ast_sched_del(sched, cdr_sched);
+ } else {
+ ast_cli_register(&cli_submit);
+ ast_register_atexit(ast_cdr_engine_term);
+ res = 0;
+ }
+ /* if this reload disabled the CDR and/or batch mode and there is a background thread,
+ kill it */
+ } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
+ /* wake up the thread so it will exit */
+ pthread_cancel(cdr_thread);
+ pthread_kill(cdr_thread, SIGURG);
+ pthread_join(cdr_thread, NULL);
+ cdr_thread = AST_PTHREADT_NULL;
+ ast_cond_destroy(&cdr_pending_cond);
+ ast_cli_unregister(&cli_submit);
+ ast_unregister_atexit(ast_cdr_engine_term);
+ res = 0;
+ /* if leaving batch mode, then post the CDRs in the batch,
+ and don't reschedule, since we are stopping CDR logging */
+ if (!batchmode && was_batchmode) {
+ ast_cdr_engine_term();
+ }
+ } else {
+ res = 0;
+ }
+
+ ast_mutex_unlock(&cdr_batch_lock);
+ ast_config_destroy(config);
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
+
+ return res;
+}
+
+int ast_cdr_engine_init(void)
+{
+ int res;
+
+ sched = sched_context_create();
+ if (!sched) {
+ ast_log(LOG_ERROR, "Unable to create schedule context.\n");
+ return -1;
+ }
+
+ ast_cli_register(&cli_status);
+
+ res = do_reload(0);
+ if (res) {
+ ast_mutex_lock(&cdr_batch_lock);
+ res = init_batch();
+ ast_mutex_unlock(&cdr_batch_lock);
+ }
+
+ return res;
+}
+
+/* \note This actually gets called a couple of times at shutdown. Once, before we start
+ hanging up channels, and then again, after the channel hangup timeout expires */
+void ast_cdr_engine_term(void)
+{
+ ast_cdr_submit_batch(batchsafeshutdown);
+}
+
+int ast_cdr_engine_reload(void)
+{
+ return do_reload(1);
+}
+
diff --git a/trunk/main/channel.c b/trunk/main/channel.c
new file mode 100644
index 000000000..d9e018310
--- /dev/null
+++ b/trunk/main/channel.c
@@ -0,0 +1,4843 @@
+/*
+ * 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 Channel Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+
+#include <sys/time.h>
+#include <signal.h>
+#include <math.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */
+#include "asterisk/zapata.h"
+
+#include "asterisk/pbx.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/say.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/translate.h"
+#include "asterisk/manager.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/indications.h"
+#include "asterisk/monitor.h"
+#include "asterisk/causes.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/transcap.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/sha1.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/audiohook.h"
+
+#ifdef HAVE_EPOLL
+#include <sys/epoll.h>
+#endif
+
+struct ast_epoll_data {
+ struct ast_channel *chan;
+ int which;
+};
+
+/* uncomment if you have problems with 'monitoring' synchronized files */
+#if 0
+#define MONITOR_CONSTANT_DELAY
+#define MONITOR_DELAY 150 * 8 /*!< 150 ms of MONITORING DELAY */
+#endif
+
+/*! \brief Prevent new channel allocation if shutting down. */
+static int shutting_down;
+
+static int uniqueint;
+
+unsigned long global_fin, global_fout;
+
+AST_THREADSTORAGE(state2str_threadbuf);
+#define STATE2STR_BUFSIZE 32
+
+/*! Default amount of time to use when emulating a digit as a begin and end
+ * 100ms */
+#define AST_DEFAULT_EMULATE_DTMF_DURATION 100
+
+/*! Minimum allowed digit length - 80ms */
+#define AST_MIN_DTMF_DURATION 80
+
+/*! Minimum amount of time between the end of the last digit and the beginning
+ * of a new one - 45ms */
+#define AST_MIN_DTMF_GAP 45
+
+/*! \brief List of channel drivers */
+struct chanlist {
+ const struct ast_channel_tech *tech;
+ AST_LIST_ENTRY(chanlist) list;
+};
+
+/*! \brief the list of registered channel types */
+static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist);
+
+/*! \brief the list of channels we have. Note that the lock for this list is used for
+ both the channels list and the backends list. */
+static AST_RWLIST_HEAD_STATIC(channels, ast_channel);
+
+/*! \brief map AST_CAUSE's to readable string representations
+ *
+ * \ref causes.h
+*/
+const struct ast_cause {
+ int cause;
+ const char *name;
+ const char *desc;
+} causes[] = {
+ { AST_CAUSE_UNALLOCATED, "UNALLOCATED", "Unallocated (unassigned) number" },
+ { AST_CAUSE_NO_ROUTE_TRANSIT_NET, "NO_ROUTE_TRANSIT_NET", "No route to specified transmit network" },
+ { AST_CAUSE_NO_ROUTE_DESTINATION, "NO_ROUTE_DESTINATION", "No route to destination" },
+ { AST_CAUSE_CHANNEL_UNACCEPTABLE, "CHANNEL_UNACCEPTABLE", "Channel unacceptable" },
+ { AST_CAUSE_CALL_AWARDED_DELIVERED, "CALL_AWARDED_DELIVERED", "Call awarded and being delivered in an established channel" },
+ { AST_CAUSE_NORMAL_CLEARING, "NORMAL_CLEARING", "Normal Clearing" },
+ { AST_CAUSE_USER_BUSY, "USER_BUSY", "User busy" },
+ { AST_CAUSE_NO_USER_RESPONSE, "NO_USER_RESPONSE", "No user responding" },
+ { AST_CAUSE_NO_ANSWER, "NO_ANSWER", "User alerting, no answer" },
+ { AST_CAUSE_CALL_REJECTED, "CALL_REJECTED", "Call Rejected" },
+ { AST_CAUSE_NUMBER_CHANGED, "NUMBER_CHANGED", "Number changed" },
+ { AST_CAUSE_DESTINATION_OUT_OF_ORDER, "DESTINATION_OUT_OF_ORDER", "Destination out of order" },
+ { AST_CAUSE_INVALID_NUMBER_FORMAT, "INVALID_NUMBER_FORMAT", "Invalid number format" },
+ { AST_CAUSE_FACILITY_REJECTED, "FACILITY_REJECTED", "Facility rejected" },
+ { AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "RESPONSE_TO_STATUS_ENQUIRY", "Response to STATus ENQuiry" },
+ { AST_CAUSE_NORMAL_UNSPECIFIED, "NORMAL_UNSPECIFIED", "Normal, unspecified" },
+ { AST_CAUSE_NORMAL_CIRCUIT_CONGESTION, "NORMAL_CIRCUIT_CONGESTION", "Circuit/channel congestion" },
+ { AST_CAUSE_NETWORK_OUT_OF_ORDER, "NETWORK_OUT_OF_ORDER", "Network out of order" },
+ { AST_CAUSE_NORMAL_TEMPORARY_FAILURE, "NORMAL_TEMPORARY_FAILURE", "Temporary failure" },
+ { AST_CAUSE_SWITCH_CONGESTION, "SWITCH_CONGESTION", "Switching equipment congestion" },
+ { AST_CAUSE_ACCESS_INFO_DISCARDED, "ACCESS_INFO_DISCARDED", "Access information discarded" },
+ { AST_CAUSE_REQUESTED_CHAN_UNAVAIL, "REQUESTED_CHAN_UNAVAIL", "Requested channel not available" },
+ { AST_CAUSE_PRE_EMPTED, "PRE_EMPTED", "Pre-empted" },
+ { AST_CAUSE_FACILITY_NOT_SUBSCRIBED, "FACILITY_NOT_SUBSCRIBED", "Facility not subscribed" },
+ { AST_CAUSE_OUTGOING_CALL_BARRED, "OUTGOING_CALL_BARRED", "Outgoing call barred" },
+ { AST_CAUSE_INCOMING_CALL_BARRED, "INCOMING_CALL_BARRED", "Incoming call barred" },
+ { AST_CAUSE_BEARERCAPABILITY_NOTAUTH, "BEARERCAPABILITY_NOTAUTH", "Bearer capability not authorized" },
+ { AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, "BEARERCAPABILITY_NOTAVAIL", "Bearer capability not available" },
+ { AST_CAUSE_BEARERCAPABILITY_NOTIMPL, "BEARERCAPABILITY_NOTIMPL", "Bearer capability not implemented" },
+ { AST_CAUSE_CHAN_NOT_IMPLEMENTED, "CHAN_NOT_IMPLEMENTED", "Channel not implemented" },
+ { AST_CAUSE_FACILITY_NOT_IMPLEMENTED, "FACILITY_NOT_IMPLEMENTED", "Facility not implemented" },
+ { AST_CAUSE_INVALID_CALL_REFERENCE, "INVALID_CALL_REFERENCE", "Invalid call reference value" },
+ { AST_CAUSE_INCOMPATIBLE_DESTINATION, "INCOMPATIBLE_DESTINATION", "Incompatible destination" },
+ { AST_CAUSE_INVALID_MSG_UNSPECIFIED, "INVALID_MSG_UNSPECIFIED", "Invalid message unspecified" },
+ { AST_CAUSE_MANDATORY_IE_MISSING, "MANDATORY_IE_MISSING", "Mandatory information element is missing" },
+ { AST_CAUSE_MESSAGE_TYPE_NONEXIST, "MESSAGE_TYPE_NONEXIST", "Message type nonexist." },
+ { AST_CAUSE_WRONG_MESSAGE, "WRONG_MESSAGE", "Wrong message" },
+ { AST_CAUSE_IE_NONEXIST, "IE_NONEXIST", "Info. element nonexist or not implemented" },
+ { AST_CAUSE_INVALID_IE_CONTENTS, "INVALID_IE_CONTENTS", "Invalid information element contents" },
+ { AST_CAUSE_WRONG_CALL_STATE, "WRONG_CALL_STATE", "Message not compatible with call state" },
+ { AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "RECOVERY_ON_TIMER_EXPIRE", "Recover on timer expiry" },
+ { AST_CAUSE_MANDATORY_IE_LENGTH_ERROR, "MANDATORY_IE_LENGTH_ERROR", "Mandatory IE length error" },
+ { AST_CAUSE_PROTOCOL_ERROR, "PROTOCOL_ERROR", "Protocol error, unspecified" },
+ { AST_CAUSE_INTERWORKING, "INTERWORKING", "Interworking, unspecified" },
+};
+
+struct ast_variable *ast_channeltype_list(void)
+{
+ struct chanlist *cl;
+ struct ast_variable *var=NULL, *prev = NULL;
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (prev) {
+ if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description, "")))
+ prev = prev->next;
+ } else {
+ var = ast_variable_new(cl->tech->type, cl->tech->description, "");
+ prev = var;
+ }
+ }
+ return var;
+}
+
+/*! \brief Show channel types - CLI command */
+static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-10.10s %-40.40s %-12.12s %-12.12s %-12.12s\n"
+ struct chanlist *cl;
+ int count_chan = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channeltypes";
+ e->usage =
+ "Usage: core show channeltypes\n"
+ " Lists available channel types registered in your\n"
+ " Asterisk server.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
+ ast_cli(a->fd, FORMAT, "----------", "-----------", "-----------", "-----------", "--------");
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return CLI_FAILURE;
+ }
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
+ (cl->tech->devicestate) ? "yes" : "no",
+ (cl->tech->indicate) ? "yes" : "no",
+ (cl->tech->transfer) ? "yes" : "no");
+ count_chan++;
+ }
+ AST_RWLIST_UNLOCK(&channels);
+ ast_cli(a->fd, "----------\n%d channel drivers registered.\n", count_chan);
+ return CLI_SUCCESS;
+
+#undef FORMAT
+}
+
+static char *complete_channeltypes(struct ast_cli_args *a)
+{
+ struct chanlist *cl;
+ int which = 0;
+ int wordlen;
+ char *ret = NULL;
+
+ if (a->pos != 3)
+ return NULL;
+
+ wordlen = strlen(a->word);
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(a->word, cl->tech->type, wordlen) && ++which > a->n) {
+ ret = ast_strdup(cl->tech->type);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*! \brief Show details about a channel driver - CLI command */
+static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chanlist *cl = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channeltype";
+ e->usage =
+ "Usage: core show channeltype <name>\n"
+ " Show details about the specified channel type, <name>.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_channeltypes(a);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return CLI_FAILURE;
+ }
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(cl->tech->type, a->argv[3], strlen(cl->tech->type)))
+ break;
+ }
+
+
+ if (!cl) {
+ ast_cli(a->fd, "\n%s is not a registered channel driver.\n", a->argv[3]);
+ AST_RWLIST_UNLOCK(&channels);
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd,
+ "-- Info about channel driver: %s --\n"
+ " Device State: %s\n"
+ " Indication: %s\n"
+ " Transfer : %s\n"
+ " Capabilities: %d\n"
+ " Digit Begin: %s\n"
+ " Digit End: %s\n"
+ " Send HTML : %s\n"
+ " Image Support: %s\n"
+ " Text Support: %s\n",
+ cl->tech->type,
+ (cl->tech->devicestate) ? "yes" : "no",
+ (cl->tech->indicate) ? "yes" : "no",
+ (cl->tech->transfer) ? "yes" : "no",
+ (cl->tech->capabilities) ? cl->tech->capabilities : -1,
+ (cl->tech->send_digit_begin) ? "yes" : "no",
+ (cl->tech->send_digit_end) ? "yes" : "no",
+ (cl->tech->send_html) ? "yes" : "no",
+ (cl->tech->send_image) ? "yes" : "no",
+ (cl->tech->send_text) ? "yes" : "no"
+
+ );
+
+ AST_RWLIST_UNLOCK(&channels);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_channel[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_channeltypes, "List available channel types"),
+ AST_CLI_DEFINE(handle_cli_core_show_channeltype, "Give more details on that channel type")
+};
+
+/*! \brief Checks to see if a channel is needing hang up */
+int ast_check_hangup(struct ast_channel *chan)
+{
+ if (chan->_softhangup) /* yes if soft hangup flag set */
+ return 1;
+ if (!chan->tech_pvt) /* yes if no technology private data */
+ return 1;
+ if (!chan->whentohangup) /* no if no hangup scheduled */
+ return 0;
+ if (chan->whentohangup > time(NULL)) /* no if hangup time has not come yet. */
+ return 0;
+ chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT; /* record event */
+ return 1;
+}
+
+static int ast_check_hangup_locked(struct ast_channel *chan)
+{
+ int res;
+ ast_channel_lock(chan);
+ res = ast_check_hangup(chan);
+ ast_channel_unlock(chan);
+ return res;
+}
+
+/*! \brief Initiate system shutdown */
+void ast_begin_shutdown(int hangup)
+{
+ struct ast_channel *c;
+ shutting_down = 1;
+ if (hangup) {
+ AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_TRAVERSE(&channels, c, chan_list)
+ ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
+ AST_RWLIST_UNLOCK(&channels);
+ }
+}
+
+/*! \brief returns number of active/allocated channels */
+int ast_active_channels(void)
+{
+ struct ast_channel *c;
+ int cnt = 0;
+ AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_TRAVERSE(&channels, c, chan_list)
+ cnt++;
+ AST_RWLIST_UNLOCK(&channels);
+ return cnt;
+}
+
+/*! \brief Cancel a shutdown in progress */
+void ast_cancel_shutdown(void)
+{
+ shutting_down = 0;
+}
+
+/*! \brief Returns non-zero if Asterisk is being shut down */
+int ast_shutting_down(void)
+{
+ return shutting_down;
+}
+
+/*! \brief Set when to hangup channel */
+void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
+{
+ chan->whentohangup = offset ? time(NULL) + offset : 0;
+ ast_queue_frame(chan, &ast_null_frame);
+ return;
+}
+
+/*! \brief Compare a offset with when to hangup channel */
+int ast_channel_cmpwhentohangup(struct ast_channel *chan, time_t offset)
+{
+ time_t whentohangup;
+
+ if (!chan->whentohangup)
+ return (offset == 0) ? 0 : -1;
+
+ if (!offset) /* XXX why is this special? */
+ return 1;
+
+ whentohangup = offset + time(NULL);
+
+ if (chan->whentohangup < whentohangup)
+ return 1;
+ else if (chan->whentohangup == whentohangup)
+ return 0;
+ else
+ return -1;
+}
+
+/*! \brief Register a new telephony channel in Asterisk */
+int ast_channel_register(const struct ast_channel_tech *tech)
+{
+ struct chanlist *chan;
+
+ AST_RWLIST_WRLOCK(&channels);
+
+ AST_LIST_TRAVERSE(&backends, chan, list) {
+ if (!strcasecmp(tech->type, chan->tech->type)) {
+ ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", tech->type);
+ AST_RWLIST_UNLOCK(&channels);
+ return -1;
+ }
+ }
+
+ if (!(chan = ast_calloc(1, sizeof(*chan)))) {
+ AST_RWLIST_UNLOCK(&channels);
+ return -1;
+ }
+ chan->tech = tech;
+ AST_LIST_INSERT_HEAD(&backends, chan, list);
+
+ ast_debug(1, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
+
+ ast_verb(2, "Registered channel type '%s' (%s)\n", chan->tech->type, chan->tech->description);
+
+ AST_RWLIST_UNLOCK(&channels);
+ return 0;
+}
+
+/*! \brief Unregister channel driver */
+void ast_channel_unregister(const struct ast_channel_tech *tech)
+{
+ struct chanlist *chan;
+
+ ast_debug(1, "Unregistering channel type '%s'\n", tech->type);
+
+ AST_RWLIST_WRLOCK(&channels);
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
+ if (chan->tech == tech) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(chan);
+ ast_verb(2, "Unregistered channel type '%s'\n", tech->type);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ AST_RWLIST_UNLOCK(&channels);
+}
+
+/*! \brief Get handle to channel driver based on name */
+const struct ast_channel_tech *ast_get_channel_tech(const char *name)
+{
+ struct chanlist *chanls;
+ const struct ast_channel_tech *ret = NULL;
+
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel tech list\n");
+ return NULL;
+ }
+
+ AST_LIST_TRAVERSE(&backends, chanls, list) {
+ if (!strcasecmp(name, chanls->tech->type)) {
+ ret = chanls->tech;
+ break;
+ }
+ }
+
+ AST_RWLIST_UNLOCK(&channels);
+
+ return ret;
+}
+
+/*! \brief Gives the string form of a given hangup cause */
+const char *ast_cause2str(int cause)
+{
+ int x;
+
+ for (x=0; x < sizeof(causes) / sizeof(causes[0]); x++) {
+ if (causes[x].cause == cause)
+ return causes[x].desc;
+ }
+
+ return "Unknown";
+}
+
+/*! \brief Convert a symbolic hangup cause to number */
+int ast_str2cause(const char *name)
+{
+ int x;
+
+ for (x = 0; x < sizeof(causes) / sizeof(causes[0]); x++)
+ if (strncasecmp(causes[x].name, name, strlen(causes[x].name)) == 0)
+ return causes[x].cause;
+
+ return -1;
+}
+
+/*! \brief Gives the string form of a given channel state.
+ \note This function is not reentrant.
+ */
+const char *ast_state2str(enum ast_channel_state state)
+{
+ char *buf;
+
+ switch (state) {
+ case AST_STATE_DOWN:
+ return "Down";
+ case AST_STATE_RESERVED:
+ return "Rsrvd";
+ case AST_STATE_OFFHOOK:
+ return "OffHook";
+ case AST_STATE_DIALING:
+ return "Dialing";
+ case AST_STATE_RING:
+ return "Ring";
+ case AST_STATE_RINGING:
+ return "Ringing";
+ case AST_STATE_UP:
+ return "Up";
+ case AST_STATE_BUSY:
+ return "Busy";
+ case AST_STATE_DIALING_OFFHOOK:
+ return "Dialing Offhook";
+ case AST_STATE_PRERING:
+ return "Pre-ring";
+ default:
+ if (!(buf = ast_threadstorage_get(&state2str_threadbuf, STATE2STR_BUFSIZE)))
+ return "Unknown";
+ snprintf(buf, STATE2STR_BUFSIZE, "Unknown (%d)", state);
+ return buf;
+ }
+}
+
+/*! \brief Gives the string form of a given transfer capability */
+char *ast_transfercapability2str(int transfercapability)
+{
+ switch (transfercapability) {
+ case AST_TRANS_CAP_SPEECH:
+ return "SPEECH";
+ case AST_TRANS_CAP_DIGITAL:
+ return "DIGITAL";
+ case AST_TRANS_CAP_RESTRICTED_DIGITAL:
+ return "RESTRICTED_DIGITAL";
+ case AST_TRANS_CAP_3_1K_AUDIO:
+ return "3K1AUDIO";
+ case AST_TRANS_CAP_DIGITAL_W_TONES:
+ return "DIGITAL_W_TONES";
+ case AST_TRANS_CAP_VIDEO:
+ return "VIDEO";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*! \brief Pick the best audio codec */
+int ast_best_codec(int fmts)
+{
+ /* This just our opinion, expressed in code. We are asked to choose
+ the best codec to use, given no information */
+ int x;
+ static const int prefs[] =
+ {
+ /*! Okay, ulaw is used by all telephony equipment, so start with it */
+ AST_FORMAT_ULAW,
+ /*! Unless of course, you're a silly European, so then prefer ALAW */
+ AST_FORMAT_ALAW,
+ /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */
+ AST_FORMAT_G722,
+ /*! Okay, well, signed linear is easy to translate into other stuff */
+ AST_FORMAT_SLINEAR16,
+ AST_FORMAT_SLINEAR,
+ /*! G.726 is standard ADPCM, in RFC3551 packing order */
+ AST_FORMAT_G726,
+ /*! G.726 is standard ADPCM, in AAL2 packing order */
+ AST_FORMAT_G726_AAL2,
+ /*! ADPCM has great sound quality and is still pretty easy to translate */
+ AST_FORMAT_ADPCM,
+ /*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to
+ translate and sounds pretty good */
+ AST_FORMAT_GSM,
+ /*! iLBC is not too bad */
+ AST_FORMAT_ILBC,
+ /*! Speex is free, but computationally more expensive than GSM */
+ AST_FORMAT_SPEEX,
+ /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
+ to use it */
+ AST_FORMAT_LPC10,
+ /*! G.729a is faster than 723 and slightly less expensive */
+ AST_FORMAT_G729A,
+ /*! Down to G.723.1 which is proprietary but at least designed for voice */
+ AST_FORMAT_G723_1,
+ };
+
+ /* Strip out video */
+ fmts &= AST_FORMAT_AUDIO_MASK;
+
+ /* Find the first preferred codec in the format given */
+ for (x=0; x < (sizeof(prefs) / sizeof(prefs[0]) ); x++)
+ if (fmts & prefs[x])
+ return prefs[x];
+ ast_log(LOG_WARNING, "Don't know any of 0x%x formats\n", fmts);
+ return 0;
+}
+
+static const struct ast_channel_tech null_tech = {
+ .type = "NULL",
+ .description = "Null channel (should not see this)",
+};
+
+/*! \brief Create a new channel structure */
+struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...)
+{
+ struct ast_channel *tmp;
+ int x;
+ int flags;
+ struct varshead *headp;
+ va_list ap1, ap2;
+
+ /* If shutting down, don't allocate any new channels */
+ if (shutting_down) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Refusing due to active shutdown\n");
+ return NULL;
+ }
+
+ if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ return NULL;
+
+ if (!(tmp->sched = sched_context_create())) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
+ ast_free(tmp);
+ return NULL;
+ }
+
+ if ((ast_string_field_init(tmp, 128))) {
+ sched_context_destroy(tmp->sched);
+ ast_free(tmp);
+ return NULL;
+ }
+
+#ifdef HAVE_EPOLL
+ tmp->epfd = epoll_create(25);
+#endif
+
+ for (x = 0; x < AST_MAX_FDS; x++) {
+ tmp->fds[x] = -1;
+#ifdef HAVE_EPOLL
+ tmp->epfd_data[x] = NULL;
+#endif
+ }
+
+#ifdef HAVE_ZAPTEL
+ tmp->timingfd = open("/dev/zap/timer", O_RDWR);
+ if (tmp->timingfd > -1) {
+ /* Check if timing interface supports new
+ ping/pong scheme */
+ flags = 1;
+ if (!ioctl(tmp->timingfd, ZT_TIMERPONG, &flags))
+ needqueue = 0;
+ }
+#else
+ tmp->timingfd = -1;
+#endif
+
+ if (needqueue) {
+ if (pipe(tmp->alertpipe)) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe!\n");
+#ifdef HAVE_ZAPTEL
+ if (tmp->timingfd > -1)
+ close(tmp->timingfd);
+#endif
+ sched_context_destroy(tmp->sched);
+ ast_string_field_free_memory(tmp);
+ ast_free(tmp);
+ return NULL;
+ } else {
+ flags = fcntl(tmp->alertpipe[0], F_GETFL);
+ fcntl(tmp->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl(tmp->alertpipe[1], F_GETFL);
+ fcntl(tmp->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
+ }
+ } else /* Make sure we've got it done right if they don't */
+ tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
+
+ /* Always watch the alertpipe */
+ ast_channel_set_fd(tmp, AST_ALERT_FD, tmp->alertpipe[0]);
+ /* And timing pipe */
+ ast_channel_set_fd(tmp, AST_TIMING_FD, tmp->timingfd);
+ ast_string_field_set(tmp, name, "**Unknown**");
+
+ /* Initial state */
+ tmp->_state = state;
+
+ tmp->streamid = -1;
+
+ tmp->fin = global_fin;
+ tmp->fout = global_fout;
+
+ if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
+ ast_string_field_build(tmp, uniqueid, "%li.%d", (long) time(NULL),
+ ast_atomic_fetchadd_int(&uniqueint, 1));
+ } else {
+ ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME,
+ (long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1));
+ }
+
+ tmp->cid.cid_name = ast_strdup(cid_name);
+ tmp->cid.cid_num = ast_strdup(cid_num);
+
+ if (!ast_strlen_zero(name_fmt)) {
+ /* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call.
+ * And they all use slightly different formats for their name string.
+ * This means, to set the name here, we have to accept variable args, and call the string_field_build from here.
+ * This means, that the stringfields must have a routine that takes the va_lists directly, and
+ * uses them to build the string, instead of forming the va_lists internally from the vararg ... list.
+ * This new function was written so this can be accomplished.
+ */
+ va_start(ap1, name_fmt);
+ va_start(ap2, name_fmt);
+ ast_string_field_build_va(tmp, name, name_fmt, ap1, ap2);
+ va_end(ap1);
+ va_end(ap2);
+ }
+
+ /* Reminder for the future: under what conditions do we NOT want to track cdrs on channels? */
+
+ /* These 4 variables need to be set up for the cdr_init() to work right */
+ if (amaflag)
+ tmp->amaflags = amaflag;
+ else
+ tmp->amaflags = ast_default_amaflags;
+
+ if (!ast_strlen_zero(acctcode))
+ ast_string_field_set(tmp, accountcode, acctcode);
+ else
+ ast_string_field_set(tmp, accountcode, ast_default_accountcode);
+
+ if (!ast_strlen_zero(context))
+ ast_copy_string(tmp->context, context, sizeof(tmp->context));
+ else
+ strcpy(tmp->context, "default");
+
+ if (!ast_strlen_zero(exten))
+ ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
+ else
+ strcpy(tmp->exten, "s");
+
+ tmp->priority = 1;
+
+ tmp->cdr = ast_cdr_alloc();
+ ast_cdr_init(tmp->cdr, tmp);
+ ast_cdr_start(tmp->cdr);
+
+ headp = &tmp->varshead;
+ AST_LIST_HEAD_INIT_NOLOCK(headp);
+
+ ast_mutex_init(&tmp->lock_dont_use);
+
+ AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);
+
+ ast_string_field_set(tmp, language, defaultlanguage);
+
+ tmp->tech = &null_tech;
+
+ AST_RWLIST_WRLOCK(&channels);
+ AST_RWLIST_INSERT_HEAD(&channels, tmp, chan_list);
+ AST_RWLIST_UNLOCK(&channels);
+
+ /*\!note
+ * and now, since the channel structure is built, and has its name, let's
+ * call the manager event generator with this Newchannel event. This is the
+ * proper and correct place to make this call, but you sure do have to pass
+ * a lot of data into this func to do it here!
+ */
+ if (!ast_strlen_zero(name_fmt)) {
+ manager_event(EVENT_FLAG_CALL, "Newchannel",
+ "Channel: %s\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "AccountCode: %s\r\n"
+ "Uniqueid: %s\r\n",
+ tmp->name,
+ state,
+ ast_state2str(state),
+ S_OR(cid_num, ""),
+ S_OR(cid_name, ""),
+ tmp->accountcode,
+ tmp->uniqueid);
+ }
+
+ return tmp;
+}
+
+/*! \brief Queue an outgoing media frame */
+int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
+{
+ struct ast_frame *f;
+ struct ast_frame *cur;
+ int blah = 1;
+ int qlen = 0;
+
+ /* Build us a copy and free the original one */
+ if (!(f = ast_frdup(fin))) {
+ ast_log(LOG_WARNING, "Unable to duplicate frame\n");
+ return -1;
+ }
+ ast_channel_lock(chan);
+
+ /* See if the last frame on the queue is a hangup, if so don't queue anything */
+ if ((cur = AST_LIST_LAST(&chan->readq)) && (cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
+ ast_frfree(f);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ /* Count how many frames exist on the queue */
+ AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) {
+ qlen++;
+ }
+
+ /* Allow up to 96 voice frames outstanding, and up to 128 total frames */
+ if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen > 128)) {
+ if (fin->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
+ CRASH;
+ } else {
+ ast_debug(1, "Dropping voice to exceptionally long queue on %s\n", chan->name);
+ ast_frfree(f);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ }
+ AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list);
+ if (chan->alertpipe[1] > -1) {
+ if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
+ ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
+ chan->name, f->frametype, f->subclass, qlen, strerror(errno));
+#ifdef HAVE_ZAPTEL
+ } else if (chan->timingfd > -1) {
+ ioctl(chan->timingfd, ZT_TIMERPING, &blah);
+#endif
+ } else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
+ pthread_kill(chan->blocker, SIGURG);
+ }
+ ast_channel_unlock(chan);
+ return 0;
+}
+
+/*! \brief Queue a hangup frame for channel */
+int ast_queue_hangup(struct ast_channel *chan)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
+ /* Yeah, let's not change a lock-critical value without locking */
+ if (!ast_channel_trylock(chan)) {
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_channel_unlock(chan);
+ }
+ return ast_queue_frame(chan, &f);
+}
+
+/*! \brief Queue a control frame */
+int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type control)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+
+ f.subclass = control;
+
+ return ast_queue_frame(chan, &f);
+}
+
+/*! \brief Queue a control frame with payload */
+int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type control,
+ const void *data, size_t datalen)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+
+ f.subclass = control;
+ f.data = (void *) data;
+ f.datalen = datalen;
+
+ return ast_queue_frame(chan, &f);
+}
+
+/*! \brief Set defer DTMF flag on channel */
+int ast_channel_defer_dtmf(struct ast_channel *chan)
+{
+ int pre = 0;
+
+ if (chan) {
+ pre = ast_test_flag(chan, AST_FLAG_DEFER_DTMF);
+ ast_set_flag(chan, AST_FLAG_DEFER_DTMF);
+ }
+ return pre;
+}
+
+/*! \brief Unset defer DTMF flag on channel */
+void ast_channel_undefer_dtmf(struct ast_channel *chan)
+{
+ if (chan)
+ ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
+}
+
+/*!
+ * \brief Helper function to find channels.
+ *
+ * It supports these modes:
+ *
+ * prev != NULL : get channel next in list after prev
+ * name != NULL : get channel with matching name
+ * name != NULL && namelen != 0 : get channel whose name starts with prefix
+ * exten != NULL : get channel whose exten or macroexten matches
+ * context != NULL && exten != NULL : get channel whose context or macrocontext
+ *
+ * It returns with the channel's lock held. If getting the individual lock fails,
+ * unlock and retry quickly up to 10 times, then give up.
+ *
+ * \note XXX Note that this code has cost O(N) because of the need to verify
+ * that the object is still on the global list.
+ *
+ * \note XXX also note that accessing fields (e.g. c->name in ast_log())
+ * can only be done with the lock held or someone could delete the
+ * object while we work on it. This causes some ugliness in the code.
+ * Note that removing the first ast_log() may be harmful, as it would
+ * shorten the retry period and possibly cause failures.
+ * We should definitely go for a better scheme that is deadlock-free.
+ */
+static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
+ const char *name, const int namelen,
+ const char *context, const char *exten)
+{
+ const char *msg = prev ? "deadlock" : "initial deadlock";
+ int retries;
+ struct ast_channel *c;
+ const struct ast_channel *_prev = prev;
+
+ for (retries = 0; retries < 10; retries++) {
+ int done;
+ AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_TRAVERSE(&channels, c, chan_list) {
+ prev = _prev;
+ if (prev) { /* look for next item */
+ if (c != prev) /* not this one */
+ continue;
+ /* found, prepare to return c->next */
+ if ((c = AST_RWLIST_NEXT(c, chan_list)) == NULL) break;
+ /* If prev was the last item on the channel list, then we just
+ * want to return NULL, instead of trying to deref NULL in the
+ * next section.
+ */
+ prev = NULL;
+ /* We want prev to be NULL in case we end up doing more searching through
+ * the channel list to find the channel (ie: name searching). If we didn't
+ * set this to NULL the logic would just blow up
+ * XXX Need a better explanation for this ...
+ */
+ }
+ if (name) { /* want match by name */
+ if ((!namelen && strcasecmp(c->name, name) && strcmp(c->uniqueid, name)) ||
+ (namelen && strncasecmp(c->name, name, namelen)))
+ continue; /* name match failed */
+ } else if (exten) {
+ if (context && strcasecmp(c->context, context) &&
+ strcasecmp(c->macrocontext, context))
+ continue; /* context match failed */
+ if (strcasecmp(c->exten, exten) &&
+ strcasecmp(c->macroexten, exten))
+ continue; /* exten match failed */
+ }
+ /* if we get here, c points to the desired record */
+ break;
+ }
+ /* exit if chan not found or mutex acquired successfully */
+ /* this is slightly unsafe, as we _should_ hold the lock to access c->name */
+ done = c == NULL || ast_channel_trylock(c) == 0;
+ if (!done) {
+ ast_debug(1, "Avoiding %s for channel '%p'\n", msg, c);
+ if (retries == 9) {
+ /* We are about to fail due to a deadlock, so report this
+ * while we still have the list lock.
+ */
+ ast_debug(1, "Failure, could not lock '%p' after %d retries!\n", c, retries);
+ /* As we have deadlocked, we will skip this channel and
+ * see if there is another match.
+ * NOTE: No point doing this for a full-name match,
+ * as there can be no more matches.
+ */
+ if (!(name && !namelen)) {
+ prev = c;
+ retries = -1;
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&channels);
+ if (done)
+ return c;
+ /* If we reach this point we basically tried to lock a channel and failed. Instead of
+ * starting from the beginning of the list we can restore our saved pointer to the previous
+ * channel and start from there.
+ */
+ prev = _prev;
+ usleep(1); /* give other threads a chance before retrying */
+ }
+
+ return NULL;
+}
+
+/*! \brief Browse channels in use */
+struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
+{
+ return channel_find_locked(prev, NULL, 0, NULL, NULL);
+}
+
+/*! \brief Get channel by name and lock it */
+struct ast_channel *ast_get_channel_by_name_locked(const char *name)
+{
+ return channel_find_locked(NULL, name, 0, NULL, NULL);
+}
+
+/*! \brief Get channel by name prefix and lock it */
+struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen)
+{
+ return channel_find_locked(NULL, name, namelen, NULL, NULL);
+}
+
+/*! \brief Get next channel by name prefix and lock it */
+struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name,
+ const int namelen)
+{
+ return channel_find_locked(chan, name, namelen, NULL, NULL);
+}
+
+/*! \brief Get channel by exten (and optionally context) and lock it */
+struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context)
+{
+ return channel_find_locked(NULL, NULL, 0, context, exten);
+}
+
+/*! \brief Get next channel by exten (and optionally context) and lock it */
+struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
+ const char *context)
+{
+ return channel_find_locked(chan, NULL, 0, context, exten);
+}
+
+/*! \brief Wait, look for hangups and condition arg */
+int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data)
+{
+ struct ast_frame *f;
+
+ while (ms > 0) {
+ if (cond && ((*cond)(data) == 0))
+ return 0;
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0)
+ return -1;
+ if (ms > 0) {
+ f = ast_read(chan);
+ if (!f)
+ return -1;
+ ast_frfree(f);
+ }
+ }
+ return 0;
+}
+
+/*! \brief Wait, look for hangups */
+int ast_safe_sleep(struct ast_channel *chan, int ms)
+{
+ return ast_safe_sleep_conditional(chan, ms, NULL, NULL);
+}
+
+static void free_cid(struct ast_callerid *cid)
+{
+ if (cid->cid_dnid)
+ ast_free(cid->cid_dnid);
+ if (cid->cid_num)
+ ast_free(cid->cid_num);
+ if (cid->cid_name)
+ ast_free(cid->cid_name);
+ if (cid->cid_ani)
+ ast_free(cid->cid_ani);
+ if (cid->cid_rdnis)
+ ast_free(cid->cid_rdnis);
+ cid->cid_dnid = cid->cid_num = cid->cid_name = cid->cid_ani = cid->cid_rdnis = NULL;
+}
+
+/*! \brief Free a channel structure */
+void ast_channel_free(struct ast_channel *chan)
+{
+ int fd;
+#ifdef HAVE_EPOLL
+ int i;
+#endif
+ struct ast_var_t *vardata;
+ struct ast_frame *f;
+ struct varshead *headp;
+ struct ast_datastore *datastore = NULL;
+ char name[AST_CHANNEL_NAME];
+
+ headp=&chan->varshead;
+
+ AST_RWLIST_WRLOCK(&channels);
+ if (!AST_RWLIST_REMOVE(&channels, chan, chan_list)) {
+ AST_RWLIST_UNLOCK(&channels);
+ ast_log(LOG_ERROR, "Unable to find channel in list to free. Assuming it has already been done.\n");
+ }
+ /* Lock and unlock the channel just to be sure nobody
+ has it locked still */
+ ast_channel_lock(chan);
+ ast_channel_unlock(chan);
+ if (chan->tech_pvt) {
+ ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
+ ast_free(chan->tech_pvt);
+ }
+
+ if (chan->sched)
+ sched_context_destroy(chan->sched);
+
+ ast_copy_string(name, chan->name, sizeof(name));
+
+ /* Stop monitoring */
+ if (chan->monitor)
+ chan->monitor->stop( chan, 0 );
+
+ /* If there is native format music-on-hold state, free it */
+ if (chan->music_state)
+ ast_moh_cleanup(chan);
+
+ /* Free translators */
+ if (chan->readtrans)
+ ast_translator_free_path(chan->readtrans);
+ if (chan->writetrans)
+ ast_translator_free_path(chan->writetrans);
+ if (chan->pbx)
+ ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
+ free_cid(&chan->cid);
+ ast_mutex_destroy(&chan->lock_dont_use);
+ /* Close pipes if appropriate */
+ if ((fd = chan->alertpipe[0]) > -1)
+ close(fd);
+ if ((fd = chan->alertpipe[1]) > -1)
+ close(fd);
+ if ((fd = chan->timingfd) > -1)
+ close(fd);
+#ifdef HAVE_EPOLL
+ for (i = 0; i < AST_MAX_FDS; i++) {
+ if (chan->epfd_data[i])
+ free(chan->epfd_data[i]);
+ }
+ close(chan->epfd);
+#endif
+ while ((f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list)))
+ ast_frfree(f);
+
+ /* Get rid of each of the data stores on the channel */
+ while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
+ /* Free the data store */
+ ast_channel_datastore_free(datastore);
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->datastores);
+
+ /* loop over the variables list, freeing all data and deleting list items */
+ /* no need to lock the list, as the channel is already locked */
+
+ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
+ ast_var_delete(vardata);
+
+ ast_app_group_discard(chan);
+
+ /* Destroy the jitterbuffer */
+ ast_jb_destroy(chan);
+
+ ast_string_field_free_memory(chan);
+ ast_free(chan);
+ AST_RWLIST_UNLOCK(&channels);
+
+ ast_device_state_changed_literal(name);
+}
+
+struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
+{
+ struct ast_datastore *datastore = NULL;
+
+ /* Make sure we at least have type so we can identify this */
+ if (!info) {
+ return NULL;
+ }
+
+ /* Allocate memory for datastore and clear it */
+ datastore = ast_calloc(1, sizeof(*datastore));
+ if (!datastore) {
+ return NULL;
+ }
+
+ datastore->info = info;
+
+ datastore->uid = ast_strdup(uid);
+
+ return datastore;
+}
+
+int ast_channel_datastore_free(struct ast_datastore *datastore)
+{
+ int res = 0;
+
+ /* Using the destroy function (if present) destroy the data */
+ if (datastore->info->destroy != NULL && datastore->data != NULL) {
+ datastore->info->destroy(datastore->data);
+ datastore->data = NULL;
+ }
+
+ /* Free allocated UID memory */
+ if (datastore->uid != NULL) {
+ ast_free((void *) datastore->uid);
+ datastore->uid = NULL;
+ }
+
+ /* Finally free memory used by ourselves */
+ ast_free(datastore);
+
+ return res;
+}
+
+int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
+{
+ struct ast_datastore *datastore = NULL, *datastore2;
+
+ AST_LIST_TRAVERSE(&from->datastores, datastore, entry) {
+ if (datastore->inheritance > 0) {
+ datastore2 = ast_channel_datastore_alloc(datastore->info, datastore->uid);
+ if (datastore2) {
+ datastore2->data = datastore->info->duplicate(datastore->data);
+ datastore2->inheritance = datastore->inheritance == DATASTORE_INHERIT_FOREVER ? DATASTORE_INHERIT_FOREVER : datastore->inheritance - 1;
+ AST_LIST_INSERT_TAIL(&to->datastores, datastore2, entry);
+ }
+ }
+ }
+ return 0;
+}
+
+int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
+{
+ int res = 0;
+
+ AST_LIST_INSERT_HEAD(&chan->datastores, datastore, entry);
+
+ return res;
+}
+
+int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
+{
+ return AST_LIST_REMOVE(&chan->datastores, datastore, entry) ? 0 : -1;
+}
+
+struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
+{
+ struct ast_datastore *datastore = NULL;
+
+ if (info == NULL)
+ return NULL;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore, entry) {
+ if (datastore->info == info) {
+ if (uid != NULL && datastore->uid != NULL) {
+ if (!strcasecmp(uid, datastore->uid)) {
+ /* Matched by type AND uid */
+ break;
+ }
+ } else {
+ /* Matched by type at least */
+ break;
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ return datastore;
+}
+
+/*! Set the file descriptor on the channel */
+void ast_channel_set_fd(struct ast_channel *chan, int which, int fd)
+{
+#ifdef HAVE_EPOLL
+ struct epoll_event ev;
+ struct ast_epoll_data *aed = NULL;
+
+ if (chan->fds[which] > -1) {
+ epoll_ctl(chan->epfd, EPOLL_CTL_DEL, chan->fds[which], &ev);
+ aed = chan->epfd_data[which];
+ }
+
+ /* If this new fd is valid, add it to the epoll */
+ if (fd > -1) {
+ if (!aed && (!(aed = ast_calloc(1, sizeof(*aed)))))
+ return;
+
+ chan->epfd_data[which] = aed;
+ aed->chan = chan;
+ aed->which = which;
+
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
+ ev.data.ptr = aed;
+ epoll_ctl(chan->epfd, EPOLL_CTL_ADD, fd, &ev);
+ } else if (aed) {
+ /* We don't have to keep around this epoll data structure now */
+ free(aed);
+ chan->epfd_data[which] = NULL;
+ }
+#endif
+ chan->fds[which] = fd;
+ return;
+}
+
+/*! Add a channel to an optimized waitfor */
+void ast_poll_channel_add(struct ast_channel *chan0, struct ast_channel *chan1)
+{
+#ifdef HAVE_EPOLL
+ struct epoll_event ev;
+ int i = 0;
+
+ if (chan0->epfd == -1)
+ return;
+
+ /* Iterate through the file descriptors on chan1, adding them to chan0 */
+ for (i = 0; i < AST_MAX_FDS; i++) {
+ if (chan1->fds[i] == -1)
+ continue;
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
+ ev.data.ptr = chan1->epfd_data[i];
+ epoll_ctl(chan0->epfd, EPOLL_CTL_ADD, chan1->fds[i], &ev);
+ }
+
+#endif
+ return;
+}
+
+/*! Delete a channel from an optimized waitfor */
+void ast_poll_channel_del(struct ast_channel *chan0, struct ast_channel *chan1)
+{
+#ifdef HAVE_EPOLL
+ struct epoll_event ev;
+ int i = 0;
+
+ if (chan0->epfd == -1)
+ return;
+
+ for (i = 0; i < AST_MAX_FDS; i++) {
+ if (chan1->fds[i] == -1)
+ continue;
+ epoll_ctl(chan0->epfd, EPOLL_CTL_DEL, chan1->fds[i], &ev);
+ }
+
+#endif
+ return;
+}
+
+/*! \brief Softly hangup a channel, don't lock */
+int ast_softhangup_nolock(struct ast_channel *chan, int cause)
+{
+ ast_debug(1, "Soft-Hanging up channel '%s'\n", chan->name);
+ /* Inform channel driver that we need to be hung up, if it cares */
+ chan->_softhangup |= cause;
+ ast_queue_frame(chan, &ast_null_frame);
+ /* Interrupt any poll call or such */
+ if (ast_test_flag(chan, AST_FLAG_BLOCKING))
+ pthread_kill(chan->blocker, SIGURG);
+ return 0;
+}
+
+/*! \brief Softly hangup a channel, lock */
+int ast_softhangup(struct ast_channel *chan, int cause)
+{
+ int res;
+ ast_channel_lock(chan);
+ res = ast_softhangup_nolock(chan, cause);
+ ast_channel_unlock(chan);
+ return res;
+}
+
+static void free_translation(struct ast_channel *clone)
+{
+ if (clone->writetrans)
+ ast_translator_free_path(clone->writetrans);
+ if (clone->readtrans)
+ ast_translator_free_path(clone->readtrans);
+ clone->writetrans = NULL;
+ clone->readtrans = NULL;
+ clone->rawwriteformat = clone->nativeformats;
+ clone->rawreadformat = clone->nativeformats;
+}
+
+/*! \brief Hangup a channel */
+int ast_hangup(struct ast_channel *chan)
+{
+ int res = 0;
+ struct ast_cdr *cdr = NULL;
+
+ /* Don't actually hang up a channel that will masquerade as someone else, or
+ if someone is going to masquerade as us */
+ ast_channel_lock(chan);
+
+ if (chan->audiohooks) {
+ ast_audiohook_detach_list(chan->audiohooks);
+ chan->audiohooks = NULL;
+ }
+
+ ast_autoservice_stop(chan);
+
+ if (chan->masq) {
+ if (ast_do_masquerade(chan))
+ ast_log(LOG_WARNING, "Failed to perform masquerade\n");
+ }
+
+ if (chan->masq) {
+ ast_log(LOG_WARNING, "%s getting hung up, but someone is trying to masq into us?!?\n", chan->name);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ /* If this channel is one which will be masqueraded into something,
+ mark it as a zombie already, so we know to free it later */
+ if (chan->masqr) {
+ ast_set_flag(chan, AST_FLAG_ZOMBIE);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ free_translation(chan);
+ /* Close audio stream */
+ if (chan->stream) {
+ ast_closestream(chan->stream);
+ chan->stream = NULL;
+ }
+ /* Close video stream */
+ if (chan->vstream) {
+ ast_closestream(chan->vstream);
+ chan->vstream = NULL;
+ }
+ if (chan->sched) {
+ sched_context_destroy(chan->sched);
+ chan->sched = NULL;
+ }
+
+ if (chan->generatordata) /* Clear any tone stuff remaining */
+ if (chan->generator && chan->generator->release)
+ chan->generator->release(chan, chan->generatordata);
+ chan->generatordata = NULL;
+ chan->generator = NULL;
+ if (chan->cdr) { /* End the CDR if it hasn't already */
+ ast_cdr_end(chan->cdr);
+ cdr = chan->cdr;
+ chan->cdr = NULL;
+ }
+ if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
+ ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd "
+ "is blocked by thread %ld in procedure %s! Expect a failure\n",
+ (long)pthread_self(), chan->name, (long)chan->blocker, chan->blockproc);
+ CRASH;
+ }
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
+ ast_debug(1, "Hanging up channel '%s'\n", chan->name);
+ if (chan->tech->hangup)
+ res = chan->tech->hangup(chan);
+ } else {
+ ast_debug(1, "Hanging up zombie '%s'\n", chan->name);
+ }
+
+ ast_channel_unlock(chan);
+ manager_event(EVENT_FLAG_CALL, "Hangup",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Cause: %d\r\n"
+ "Cause-txt: %s\r\n",
+ chan->name,
+ chan->uniqueid,
+ S_OR(chan->cid.cid_num, "<unknown>"),
+ S_OR(chan->cid.cid_name, "<unknown>"),
+ chan->hangupcause,
+ ast_cause2str(chan->hangupcause)
+ );
+ ast_channel_free(chan);
+
+ if (cdr)
+ ast_cdr_detach(cdr);
+
+ return res;
+}
+
+int __ast_answer(struct ast_channel *chan, unsigned int delay)
+{
+ int res = 0;
+
+ ast_channel_lock(chan);
+
+ /* You can't answer an outbound call */
+ if (ast_test_flag(chan, AST_FLAG_OUTGOING)) {
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ switch (chan->_state) {
+ case AST_STATE_RINGING:
+ case AST_STATE_RING:
+ if (chan->tech->answer)
+ res = chan->tech->answer(chan);
+ ast_setstate(chan, AST_STATE_UP);
+ ast_cdr_answer(chan->cdr);
+ ast_channel_unlock(chan);
+ if (delay)
+ ast_safe_sleep(chan, delay);
+ return res;
+ break;
+ case AST_STATE_UP:
+ ast_cdr_answer(chan->cdr);
+ break;
+ default:
+ break;
+ }
+ chan->visible_indication = 0;
+ ast_channel_unlock(chan);
+
+ return res;
+}
+
+int ast_answer(struct ast_channel *chan)
+{
+ return __ast_answer(chan, 500);
+}
+
+void ast_deactivate_generator(struct ast_channel *chan)
+{
+ ast_channel_lock(chan);
+ if (chan->generatordata) {
+ if (chan->generator && chan->generator->release)
+ chan->generator->release(chan, chan->generatordata);
+ chan->generatordata = NULL;
+ chan->generator = NULL;
+ ast_channel_set_fd(chan, AST_GENERATOR_FD, -1);
+ ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+ ast_settimeout(chan, 0, NULL, NULL);
+ }
+ ast_channel_unlock(chan);
+}
+
+static int generator_force(const void *data)
+{
+ /* Called if generator doesn't have data */
+ void *tmp;
+ int res;
+ int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
+ struct ast_channel *chan = (struct ast_channel *)data;
+ tmp = chan->generatordata;
+ chan->generatordata = NULL;
+ generate = chan->generator->generate;
+ res = generate(chan, tmp, 0, 160);
+ chan->generatordata = tmp;
+ if (res) {
+ ast_debug(1, "Auto-deactivating generator\n");
+ ast_deactivate_generator(chan);
+ }
+ return 0;
+}
+
+int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
+{
+ int res = 0;
+
+ ast_channel_lock(chan);
+
+ if (chan->generatordata) {
+ if (chan->generator && chan->generator->release)
+ chan->generator->release(chan, chan->generatordata);
+ chan->generatordata = NULL;
+ }
+
+ ast_prod(chan);
+ if (gen->alloc && !(chan->generatordata = gen->alloc(chan, params))) {
+ res = -1;
+ }
+
+ if (!res) {
+ ast_settimeout(chan, 160, generator_force, chan);
+ chan->generator = gen;
+ }
+
+ ast_channel_unlock(chan);
+
+ return res;
+}
+
+/*! \brief Wait for x amount of time on a file descriptor to have input. */
+int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
+{
+ int winner = -1;
+ ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
+ return winner;
+}
+
+/*! \brief Wait for x amount of time on a file descriptor to have input. */
+#ifdef HAVE_EPOLL
+static struct ast_channel *ast_waitfor_nandfds_classic(struct ast_channel **c, int n, int *fds, int nfds,
+ int *exception, int *outfd, int *ms)
+#else
+struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
+ int *exception, int *outfd, int *ms)
+#endif
+{
+ struct timeval start = { 0 , 0 };
+ struct pollfd *pfds;
+ int res;
+ long rms;
+ int x, y, max;
+ int sz;
+ time_t now = 0;
+ long whentohangup = 0, diff;
+ struct ast_channel *winner = NULL;
+ struct fdmap {
+ int chan;
+ int fdno;
+ } *fdmap;
+
+ sz = n * AST_MAX_FDS + nfds;
+ pfds = alloca(sizeof(*pfds) * sz);
+ fdmap = alloca(sizeof(*fdmap) * sz);
+
+ if (outfd)
+ *outfd = -99999;
+ if (exception)
+ *exception = 0;
+
+ /* Perform any pending masquerades */
+ for (x = 0; x < n; x++) {
+ ast_channel_lock(c[x]);
+ if (c[x]->masq && ast_do_masquerade(c[x])) {
+ ast_log(LOG_WARNING, "Masquerade failed\n");
+ *ms = -1;
+ ast_channel_unlock(c[x]);
+ return NULL;
+ }
+ if (c[x]->whentohangup) {
+ if (!whentohangup)
+ time(&now);
+ diff = c[x]->whentohangup - now;
+ if (diff < 1) {
+ /* Should already be hungup */
+ c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ ast_channel_unlock(c[x]);
+ return c[x];
+ }
+ if (!whentohangup || (diff < whentohangup))
+ whentohangup = diff;
+ }
+ ast_channel_unlock(c[x]);
+ }
+ /* Wait full interval */
+ rms = *ms;
+ if (whentohangup) {
+ rms = whentohangup * 1000; /* timeout in milliseconds */
+ if (*ms >= 0 && *ms < rms) /* original *ms still smaller */
+ rms = *ms;
+ }
+ /*
+ * Build the pollfd array, putting the channels' fds first,
+ * followed by individual fds. Order is important because
+ * individual fd's must have priority over channel fds.
+ */
+ max = 0;
+ for (x = 0; x < n; x++) {
+ for (y = 0; y < AST_MAX_FDS; y++) {
+ fdmap[max].fdno = y; /* fd y is linked to this pfds */
+ fdmap[max].chan = x; /* channel x is linked to this pfds */
+ max += ast_add_fd(&pfds[max], c[x]->fds[y]);
+ }
+ CHECK_BLOCKING(c[x]);
+ }
+ /* Add the individual fds */
+ for (x = 0; x < nfds; x++) {
+ fdmap[max].chan = -1;
+ max += ast_add_fd(&pfds[max], fds[x]);
+ }
+
+ if (*ms > 0)
+ start = ast_tvnow();
+
+ if (sizeof(int) == 4) { /* XXX fix timeout > 600000 on linux x86-32 */
+ do {
+ int kbrms = rms;
+ if (kbrms > 600000)
+ kbrms = 600000;
+ res = poll(pfds, max, kbrms);
+ if (!res)
+ rms -= kbrms;
+ } while (!res && (rms > 0));
+ } else {
+ res = poll(pfds, max, rms);
+ }
+ for (x = 0; x < n; x++)
+ ast_clear_flag(c[x], AST_FLAG_BLOCKING);
+ if (res < 0) { /* Simulate a timeout if we were interrupted */
+ if (errno != EINTR)
+ *ms = -1;
+ return NULL;
+ }
+ if (whentohangup) { /* if we have a timeout, check who expired */
+ time(&now);
+ for (x = 0; x < n; x++) {
+ if (c[x]->whentohangup && now >= c[x]->whentohangup) {
+ c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ if (winner == NULL)
+ winner = c[x];
+ }
+ }
+ }
+ if (res == 0) { /* no fd ready, reset timeout and done */
+ *ms = 0; /* XXX use 0 since we may not have an exact timeout. */
+ return winner;
+ }
+ /*
+ * Then check if any channel or fd has a pending event.
+ * Remember to check channels first and fds last, as they
+ * must have priority on setting 'winner'
+ */
+ for (x = 0; x < max; x++) {
+ res = pfds[x].revents;
+ if (res == 0)
+ continue;
+ if (fdmap[x].chan >= 0) { /* this is a channel */
+ winner = c[fdmap[x].chan]; /* override previous winners */
+ if (res & POLLPRI)
+ ast_set_flag(winner, AST_FLAG_EXCEPTION);
+ else
+ ast_clear_flag(winner, AST_FLAG_EXCEPTION);
+ winner->fdno = fdmap[x].fdno;
+ } else { /* this is an fd */
+ if (outfd)
+ *outfd = pfds[x].fd;
+ if (exception)
+ *exception = (res & POLLPRI) ? -1 : 0;
+ winner = NULL;
+ }
+ }
+ if (*ms > 0) {
+ *ms -= ast_tvdiff_ms(ast_tvnow(), start);
+ if (*ms < 0)
+ *ms = 0;
+ }
+ return winner;
+}
+
+#ifdef HAVE_EPOLL
+static struct ast_channel *ast_waitfor_nandfds_simple(struct ast_channel *chan, int *ms)
+{
+ struct timeval start = { 0 , 0 };
+ int res = 0;
+ struct epoll_event ev[1];
+ long whentohangup = 0, rms = *ms;
+ time_t now;
+ struct ast_channel *winner = NULL;
+ struct ast_epoll_data *aed = NULL;
+
+ ast_channel_lock(chan);
+
+ /* See if this channel needs to be masqueraded */
+ if (chan->masq && ast_do_masquerade(chan)) {
+ ast_log(LOG_WARNING, "Failed to perform masquerade on %s\n", chan->name);
+ *ms = -1;
+ ast_channel_unlock(chan);
+ return NULL;
+ }
+
+ /* Figure out their timeout */
+ if (chan->whentohangup) {
+ time(&now);
+ if ((whentohangup = chan->whentohangup - now) < 1) {
+ /* They should already be hungup! */
+ chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ ast_channel_unlock(chan);
+ return NULL;
+ }
+ /* If this value is smaller then the current one... make it priority */
+ whentohangup *= 1000;
+ if (rms > whentohangup)
+ rms = whentohangup;
+ }
+
+ ast_channel_unlock(chan);
+
+ /* Time to make this channel block... */
+ CHECK_BLOCKING(chan);
+
+ if (*ms > 0)
+ start = ast_tvnow();
+
+ /* We don't have to add any file descriptors... they are already added, we just have to wait! */
+ res = epoll_wait(chan->epfd, ev, 1, rms);
+
+ /* Stop blocking */
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+
+ /* Simulate a timeout if we were interrupted */
+ if (res < 0) {
+ if (errno != EINTR)
+ *ms = -1;
+ return NULL;
+ }
+
+ /* If this channel has a timeout see if it expired */
+ if (chan->whentohangup) {
+ time(&now);
+ if (now >= chan->whentohangup) {
+ chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ winner = chan;
+ }
+ }
+
+ /* No fd ready, reset timeout and be done for now */
+ if (!res) {
+ *ms = 0;
+ return winner;
+ }
+
+ /* See what events are pending */
+ aed = ev[0].data.ptr;
+ chan->fdno = aed->which;
+ if (ev[0].events & EPOLLPRI)
+ ast_set_flag(chan, AST_FLAG_EXCEPTION);
+ else
+ ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+
+ if (*ms > 0) {
+ *ms -= ast_tvdiff_ms(ast_tvnow(), start);
+ if (*ms < 0)
+ *ms = 0;
+ }
+
+ return chan;
+}
+
+static struct ast_channel *ast_waitfor_nandfds_complex(struct ast_channel **c, int n, int *ms)
+{
+ struct timeval start = { 0 , 0 };
+ int res = 0, i;
+ struct epoll_event ev[25] = { { 0, } };
+ long whentohangup = 0, diff, rms = *ms;
+ time_t now;
+ struct ast_channel *winner = NULL;
+
+ for (i = 0; i < n; i++) {
+ ast_channel_lock(c[i]);
+ if (c[i]->masq && ast_do_masquerade(c[i])) {
+ ast_log(LOG_WARNING, "Masquerade failed\n");
+ *ms = -1;
+ ast_channel_unlock(c[i]);
+ return NULL;
+ }
+ if (c[i]->whentohangup) {
+ if (!whentohangup)
+ time(&now);
+ if ((diff = c[i]->whentohangup - now) < 1) {
+ c[i]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ ast_channel_unlock(c[i]);
+ return c[i];
+ }
+ if (!whentohangup || (diff < whentohangup))
+ whentohangup = diff;
+ }
+ ast_channel_unlock(c[i]);
+ CHECK_BLOCKING(c[i]);
+ }
+
+ rms = *ms;
+ if (whentohangup) {
+ rms = whentohangup * 1000;
+ if (*ms >= 0 && *ms < rms)
+ rms = *ms;
+ }
+
+ if (*ms > 0)
+ start = ast_tvnow();
+
+ res = epoll_wait(c[0]->epfd, ev, 25, rms);
+
+ for (i = 0; i < n; i++)
+ ast_clear_flag(c[i], AST_FLAG_BLOCKING);
+
+ if (res < 0) {
+ if (errno != EINTR)
+ *ms = -1;
+ return NULL;
+ }
+
+ if (whentohangup) {
+ time(&now);
+ for (i = 0; i < n; i++) {
+ if (c[i]->whentohangup && now >= c[i]->whentohangup) {
+ c[i]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ if (!winner)
+ winner = c[i];
+ }
+ }
+ }
+
+ if (!res) {
+ *ms = 0;
+ return winner;
+ }
+
+ for (i = 0; i < res; i++) {
+ struct ast_epoll_data *aed = ev[i].data.ptr;
+
+ if (!ev[i].events || !aed)
+ continue;
+
+ winner = aed->chan;
+ if (ev[i].events & EPOLLPRI)
+ ast_set_flag(winner, AST_FLAG_EXCEPTION);
+ else
+ ast_clear_flag(winner, AST_FLAG_EXCEPTION);
+ winner->fdno = aed->which;
+ }
+
+ if (*ms > 0) {
+ *ms -= ast_tvdiff_ms(ast_tvnow(), start);
+ if (*ms < 0)
+ *ms = 0;
+ }
+
+ return winner;
+}
+
+struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
+ int *exception, int *outfd, int *ms)
+{
+ /* Clear all provided values in one place. */
+ if (outfd)
+ *outfd = -99999;
+ if (exception)
+ *exception = 0;
+
+ /* If no epoll file descriptor is available resort to classic nandfds */
+ if (!n || nfds || c[0]->epfd == -1)
+ return ast_waitfor_nandfds_classic(c, n, fds, nfds, exception, outfd, ms);
+ else if (!nfds && n == 1)
+ return ast_waitfor_nandfds_simple(c[0], ms);
+ else
+ return ast_waitfor_nandfds_complex(c, n, ms);
+}
+#endif
+
+struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
+{
+ return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, ms);
+}
+
+int ast_waitfor(struct ast_channel *c, int ms)
+{
+ int oldms = ms; /* -1 if no timeout */
+
+ ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
+ if ((ms < 0) && (oldms < 0))
+ ms = 0;
+ return ms;
+}
+
+/* XXX never to be called with ms = -1 */
+int ast_waitfordigit(struct ast_channel *c, int ms)
+{
+ return ast_waitfordigit_full(c, ms, -1, -1);
+}
+
+int ast_settimeout(struct ast_channel *c, int samples, int (*func)(const void *data), void *data)
+{
+ int res = -1;
+#ifdef HAVE_ZAPTEL
+ if (c->timingfd > -1) {
+ if (!func) {
+ samples = 0;
+ data = 0;
+ }
+ ast_debug(1, "Scheduling timer at %d sample intervals\n", samples);
+ res = ioctl(c->timingfd, ZT_TIMERCONFIG, &samples);
+ c->timingfunc = func;
+ c->timingdata = data;
+ }
+#endif
+ return res;
+}
+
+int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
+{
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
+ return -1;
+
+ /* Only look for the end of DTMF, don't bother with the beginning and don't emulate things */
+ ast_set_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ /* Wait for a digit, no more than ms milliseconds total. */
+
+ while (ms) {
+ struct ast_channel *rchan;
+ int outfd=-1;
+
+ errno = 0;
+ rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
+
+ if (!rchan && outfd < 0 && ms) {
+ if (errno == 0 || errno == EINTR)
+ continue;
+ ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ } else if (outfd > -1) {
+ /* The FD we were watching has something waiting */
+ ast_log(LOG_WARNING, "The FD we were waiting for has something waiting. Waitfordigit returning numeric 1\n");
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return 1;
+ } else if (rchan) {
+ int res;
+ struct ast_frame *f = ast_read(c);
+ if (!f)
+ return -1;
+
+ switch (f->frametype) {
+ case AST_FRAME_DTMF_BEGIN:
+ break;
+ case AST_FRAME_DTMF_END:
+ res = f->subclass;
+ ast_frfree(f);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ case AST_FRAME_CONTROL:
+ switch (f->subclass) {
+ case AST_CONTROL_HANGUP:
+ ast_frfree(f);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_ANSWER:
+ /* Unimportant */
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", f->subclass);
+ break;
+ }
+ break;
+ case AST_FRAME_VOICE:
+ /* Write audio if appropriate */
+ if (audiofd > -1)
+ write(audiofd, f->data, f->datalen);
+ default:
+ /* Ignore */
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ return 0; /* Time is up */
+}
+
+static void send_dtmf_event(const struct ast_channel *chan, const char *direction, const char digit, const char *begin, const char *end)
+{
+ manager_event(EVENT_FLAG_DTMF,
+ "DTMF",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Digit: %c\r\n"
+ "Direction: %s\r\n"
+ "Begin: %s\r\n"
+ "End: %s\r\n",
+ chan->name, chan->uniqueid, digit, direction, begin, end);
+}
+
+static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f)
+{
+ if (chan->generatordata && !ast_internal_timing_enabled(chan)) {
+ void *tmp = chan->generatordata;
+ int res;
+
+ if (chan->timingfunc) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
+ ast_settimeout(chan, 0, NULL, NULL);
+ }
+
+ chan->generatordata = NULL; /* reset, to let writes go through */
+ res = chan->generator->generate(chan, tmp, f->datalen, f->samples);
+ chan->generatordata = tmp;
+ if (res) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
+ ast_deactivate_generator(chan);
+ }
+
+ } else if (f->frametype == AST_FRAME_CNG) {
+ if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Generator got CNG, switching to timed mode\n");
+ ast_settimeout(chan, 160, generator_force, chan);
+ }
+ }
+}
+
+static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
+{
+ struct ast_frame *f = NULL; /* the return value */
+ int blah;
+ int prestate;
+ int count = 0;
+
+ /* this function is very long so make sure there is only one return
+ * point at the end (there are only two exceptions to this).
+ */
+ while(ast_channel_trylock(chan)) {
+ if(count++ > 10)
+ /*cannot goto done since the channel is not locked*/
+ return &ast_null_frame;
+ usleep(1);
+ }
+
+ if (chan->masq) {
+ if (ast_do_masquerade(chan))
+ ast_log(LOG_WARNING, "Failed to perform masquerade\n");
+ else
+ f = &ast_null_frame;
+ goto done;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+ if (chan->generator)
+ ast_deactivate_generator(chan);
+ goto done;
+ }
+ prestate = chan->_state;
+
+ if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) &&
+ !ast_strlen_zero(chan->dtmfq) &&
+ (ast_tvzero(chan->dtmf_tv) || ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) > AST_MIN_DTMF_GAP) ) {
+ /* We have DTMF that has been deferred. Return it now */
+ chan->dtmff.subclass = chan->dtmfq[0];
+ /* Drop first digit from the buffer */
+ memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
+ f = &chan->dtmff;
+ if (ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) {
+ ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name);
+ chan->dtmff.frametype = AST_FRAME_DTMF_END;
+ } else {
+ ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %d queued on %s\n", f->subclass, AST_DEFAULT_EMULATE_DTMF_DURATION, chan->name);
+ chan->dtmff.frametype = AST_FRAME_DTMF_BEGIN;
+ ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = f->subclass;
+ chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION;
+ }
+ chan->dtmf_tv = ast_tvnow();
+ goto done;
+ }
+
+ /* Read and ignore anything on the alertpipe, but read only
+ one sizeof(blah) per frame that we send from it */
+ if (chan->alertpipe[0] > -1)
+ read(chan->alertpipe[0], &blah, sizeof(blah));
+
+#ifdef HAVE_ZAPTEL
+ if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
+ int res;
+
+ ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+ blah = -1;
+ /* IF we can't get event, assume it's an expired as-per the old interface */
+ res = ioctl(chan->timingfd, ZT_GETEVENT, &blah);
+ if (res)
+ blah = ZT_EVENT_TIMER_EXPIRED;
+
+ if (blah == ZT_EVENT_TIMER_PING) {
+ if (AST_LIST_EMPTY(&chan->readq) || !AST_LIST_NEXT(AST_LIST_FIRST(&chan->readq), frame_list)) {
+ /* Acknowledge PONG unless we need it again */
+ if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
+ ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
+ }
+ }
+ } else if (blah == ZT_EVENT_TIMER_EXPIRED) {
+ ioctl(chan->timingfd, ZT_TIMERACK, &blah);
+ if (chan->timingfunc) {
+ chan->timingfunc(chan->timingdata);
+ } else {
+ blah = 0;
+ ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
+ chan->timingdata = NULL;
+ }
+ ast_channel_unlock(chan);
+ /* cannot 'goto done' because the channel is already unlocked */
+ return &ast_null_frame;
+ } else
+ ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
+ } else
+#endif
+ if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
+ /* if the AST_GENERATOR_FD is set, call the generator with args
+ * set to -1 so it can do whatever it needs to.
+ */
+ void *tmp = chan->generatordata;
+ chan->generatordata = NULL; /* reset to let ast_write get through */
+ chan->generator->generate(chan, tmp, -1, -1);
+ chan->generatordata = tmp;
+ f = &ast_null_frame;
+ goto done;
+ }
+
+ /* Check for pending read queue */
+ if (!AST_LIST_EMPTY(&chan->readq)) {
+ f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list);
+ /* Interpret hangup and return NULL */
+ /* XXX why not the same for frames from the channel ? */
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
+ ast_frfree(f);
+ f = NULL;
+ }
+ } else {
+ chan->blocker = pthread_self();
+ if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
+ if (chan->tech->exception)
+ f = chan->tech->exception(chan);
+ else {
+ ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
+ f = &ast_null_frame;
+ }
+ /* Clear the exception flag */
+ ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+ } else if (chan->tech->read)
+ f = chan->tech->read(chan);
+ else
+ ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
+ }
+
+ if (f) {
+ /* if the channel driver returned more than one frame, stuff the excess
+ into the readq for the next ast_read call (note that we can safely assume
+ that the readq is empty, because otherwise we would not have called into
+ the channel driver and f would be only a single frame)
+ */
+ if (AST_LIST_NEXT(f, frame_list)) {
+ AST_LIST_HEAD_SET_NOLOCK(&chan->readq, AST_LIST_NEXT(f, frame_list));
+ AST_LIST_NEXT(f, frame_list) = NULL;
+ }
+
+ switch (f->frametype) {
+ case AST_FRAME_CONTROL:
+ if (f->subclass == AST_CONTROL_ANSWER) {
+ if (!ast_test_flag(chan, AST_FLAG_OUTGOING)) {
+ ast_debug(1, "Ignoring answer on an inbound call!\n");
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if (prestate == AST_STATE_UP) {
+ ast_debug(1, "Dropping duplicate answer!\n");
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ /* Answer the CDR */
+ ast_setstate(chan, AST_STATE_UP);
+ if (!chan->cdr) { /* up till now, this insertion hasn't been done. Therefore,
+ to keep from throwing off the basic order of the universe,
+ we will try to keep this cdr from getting posted. */
+ chan->cdr = ast_cdr_alloc();
+ ast_cdr_init(chan->cdr, chan);
+ ast_cdr_start(chan->cdr);
+ }
+
+ ast_cdr_answer(chan->cdr);
+ }
+ }
+ break;
+ case AST_FRAME_DTMF_END:
+ send_dtmf_event(chan, "Received", f->subclass, "No", "Yes");
+ ast_log(LOG_DTMF, "DTMF end '%c' received on %s, duration %ld ms\n", f->subclass, chan->name, f->len);
+ /* Queue it up if DTMF is deffered, or if DTMF emulation is forced.
+ * However, only let emulation be forced if the other end cares about BEGIN frames */
+ if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF) ||
+ (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) ) {
+ if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) {
+ ast_log(LOG_DTMF, "DTMF end '%c' put into dtmf queue on %s\n", f->subclass, chan->name);
+ chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
+ } else
+ ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF | AST_FLAG_END_DTMF_ONLY)) {
+ if (!ast_tvzero(chan->dtmf_tv) &&
+ ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) {
+ /* If it hasn't been long enough, defer this digit */
+ if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) {
+ ast_log(LOG_DTMF, "DTMF end '%c' put into dtmf queue on %s\n", f->subclass, chan->name);
+ chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
+ } else
+ ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ /* There was no begin, turn this into a begin and send the end later */
+ f->frametype = AST_FRAME_DTMF_BEGIN;
+ ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = f->subclass;
+ chan->dtmf_tv = ast_tvnow();
+ if (f->len) {
+ if (f->len > AST_MIN_DTMF_DURATION)
+ chan->emulate_dtmf_duration = f->len;
+ else
+ chan->emulate_dtmf_duration = AST_MIN_DTMF_DURATION;
+ } else
+ chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION;
+ ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %u queued on %s\n", f->subclass, chan->emulate_dtmf_duration, chan->name);
+ }
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ /*!
+ * \todo XXX It is possible to write a digit to the audiohook twice
+ * if the digit was originally read while the channel was in autoservice. */
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ } else {
+ struct timeval now = ast_tvnow();
+ if (ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+ ast_log(LOG_DTMF, "DTMF end accepted with begin '%c' on %s\n", f->subclass, chan->name);
+ ast_clear_flag(chan, AST_FLAG_IN_DTMF);
+ if (!f->len)
+ f->len = ast_tvdiff_ms(now, chan->dtmf_tv);
+ } else if (!f->len) {
+ ast_log(LOG_DTMF, "DTMF end accepted without begin '%c' on %s\n", f->subclass, chan->name);
+ f->len = AST_MIN_DTMF_DURATION;
+ }
+ if (f->len < AST_MIN_DTMF_DURATION) {
+ ast_log(LOG_DTMF, "DTMF end '%c' has duration %ld but want minimum %d, emulating on %s\n", f->subclass, f->len, AST_MIN_DTMF_DURATION, chan->name);
+ ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = f->subclass;
+ chan->emulate_dtmf_duration = AST_MIN_DTMF_DURATION - f->len;
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ ast_log(LOG_DTMF, "DTMF end passthrough '%c' on %s\n", f->subclass, chan->name);
+ chan->dtmf_tv = now;
+ }
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ }
+ break;
+ case AST_FRAME_DTMF_BEGIN:
+ send_dtmf_event(chan, "Received", f->subclass, "Yes", "No");
+ ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
+ if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY) ||
+ (!ast_tvzero(chan->dtmf_tv) &&
+ ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) ) {
+ ast_log(LOG_DTMF, "DTMF begin ignored '%c' on %s\n", f->subclass, chan->name);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ ast_set_flag(chan, AST_FLAG_IN_DTMF);
+ chan->dtmf_tv = ast_tvnow();
+ ast_log(LOG_DTMF, "DTMF begin passthrough '%c' on %s\n", f->subclass, chan->name);
+ }
+ break;
+ case AST_FRAME_NULL:
+ if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) {
+ struct timeval now = ast_tvnow();
+ if (ast_tvdiff_ms(now, chan->dtmf_tv) >= chan->emulate_dtmf_duration) {
+ chan->emulate_dtmf_duration = 0;
+ ast_frfree(f);
+ f = &chan->dtmff;
+ f->frametype = AST_FRAME_DTMF_END;
+ f->subclass = chan->emulate_dtmf_digit;
+ f->len = ast_tvdiff_ms(now, chan->dtmf_tv);
+ chan->dtmf_tv = now;
+ ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = 0;
+ ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name);
+ }
+ }
+ break;
+ case AST_FRAME_VOICE:
+ /* The EMULATE_DTMF flag must be cleared here as opposed to when the duration
+ * is reached , because we want to make sure we pass at least one
+ * voice frame through before starting the next digit, to ensure a gap
+ * between DTMF digits. */
+ if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !chan->emulate_dtmf_duration) {
+ ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = 0;
+ }
+
+ if (dropaudio || ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+ if (dropaudio)
+ ast_read_generator_actions(chan, f);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ }
+
+ if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+ struct timeval now = ast_tvnow();
+ if (ast_tvdiff_ms(now, chan->dtmf_tv) >= chan->emulate_dtmf_duration) {
+ chan->emulate_dtmf_duration = 0;
+ ast_frfree(f);
+ f = &chan->dtmff;
+ f->frametype = AST_FRAME_DTMF_END;
+ f->subclass = chan->emulate_dtmf_digit;
+ f->len = ast_tvdiff_ms(now, chan->dtmf_tv);
+ chan->dtmf_tv = now;
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name);
+ } else {
+ /* Drop voice frames while we're still in the middle of the digit */
+ ast_frfree(f);
+ f = &ast_null_frame;
+ }
+ } else if ((f->frametype == AST_FRAME_VOICE) && !(f->subclass & chan->nativeformats)) {
+ /* This frame can't be from the current native formats -- drop it on the
+ floor */
+ ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
+ chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if ((f->frametype == AST_FRAME_VOICE)) {
+ /* Send frame to audiohooks if present */
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ if (chan->monitor && chan->monitor->read_stream ) {
+ /* XXX what does this do ? */
+#ifndef MONITOR_CONSTANT_DELAY
+ int jump = chan->outsmpl - chan->insmpl - 4 * f->samples;
+ if (jump >= 0) {
+ jump = chan->outsmpl - chan->insmpl;
+ if (ast_seekstream(chan->monitor->read_stream, jump, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
+ chan->insmpl += jump + f->samples;
+ } else
+ chan->insmpl+= f->samples;
+#else
+ int jump = chan->outsmpl - chan->insmpl;
+ if (jump - MONITOR_DELAY >= 0) {
+ if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
+ chan->insmpl += jump;
+ } else
+ chan->insmpl += f->samples;
+#endif
+ if (chan->monitor->state == AST_MONITOR_RUNNING) {
+ if (ast_writestream(chan->monitor->read_stream, f) < 0)
+ ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+ }
+ }
+
+ if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL)
+ f = &ast_null_frame;
+ else
+ /* Run generator sitting on the line if timing device not available
+ * and synchronous generation of outgoing frames is necessary */
+ ast_read_generator_actions(chan, f);
+ }
+ default:
+ /* Just pass it on! */
+ break;
+ }
+ } else {
+ /* Make sure we always return NULL in the future */
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ if (chan->generator)
+ ast_deactivate_generator(chan);
+ /* End the CDR if appropriate */
+ if (chan->cdr)
+ ast_cdr_end(chan->cdr);
+ }
+
+ /* High bit prints debugging */
+ if (chan->fin & DEBUGCHAN_FLAG)
+ ast_frame_dump(chan->name, f, "<<");
+ chan->fin = FRAMECOUNT_INC(chan->fin);
+
+done:
+ if (chan->music_state && chan->generator && chan->generator->digit && f && f->frametype == AST_FRAME_DTMF_END)
+ chan->generator->digit(chan, f->subclass);
+
+ ast_channel_unlock(chan);
+ return f;
+}
+
+int ast_internal_timing_enabled(struct ast_channel *chan)
+{
+ int ret = ast_opt_internal_timing && chan->timingfd > -1;
+ ast_debug(5, "Internal timing is %s (option_internal_timing=%d chan->timingfd=%d)\n", ret? "enabled": "disabled", ast_opt_internal_timing, chan->timingfd);
+ return ret;
+}
+
+struct ast_frame *ast_read(struct ast_channel *chan)
+{
+ return __ast_read(chan, 0);
+}
+
+struct ast_frame *ast_read_noaudio(struct ast_channel *chan)
+{
+ return __ast_read(chan, 1);
+}
+
+int ast_indicate(struct ast_channel *chan, int condition)
+{
+ return ast_indicate_data(chan, condition, NULL, 0);
+}
+
+int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
+{
+ int res = -1;
+
+ ast_channel_lock(chan);
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ if (chan->tech->indicate)
+ res = chan->tech->indicate(chan, condition, data, datalen);
+ ast_channel_unlock(chan);
+ if (!chan->tech->indicate || res) {
+ /*
+ * Device does not support (that) indication, lets fake
+ * it by doing our own tone generation. (PM2002)
+ */
+ if (condition < 0)
+ ast_playtones_stop(chan);
+ else {
+ const struct ind_tone_zone_sound *ts = NULL;
+ switch (condition) {
+ case AST_CONTROL_RINGING:
+ ts = ast_get_indication_tone(chan->zone, "ring");
+ break;
+ case AST_CONTROL_BUSY:
+ ts = ast_get_indication_tone(chan->zone, "busy");
+ break;
+ case AST_CONTROL_CONGESTION:
+ ts = ast_get_indication_tone(chan->zone, "congestion");
+ break;
+ }
+ if (ts && ts->data[0]) {
+ ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
+ ast_playtones_start(chan,0,ts->data, 1);
+ res = 0;
+ chan->visible_indication = condition;
+ } else if (condition == AST_CONTROL_PROGRESS) {
+ /* ast_playtones_stop(chan); */
+ } else if (condition == AST_CONTROL_PROCEEDING) {
+ /* Do nothing, really */
+ } else if (condition == AST_CONTROL_HOLD) {
+ /* Do nothing.... */
+ } else if (condition == AST_CONTROL_UNHOLD) {
+ /* Do nothing.... */
+ } else if (condition == AST_CONTROL_VIDUPDATE) {
+ /* Do nothing.... */
+ } else {
+ /* not handled */
+ ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
+ res = -1;
+ }
+ }
+ } else
+ chan->visible_indication = condition;
+
+ return res;
+}
+
+int ast_recvchar(struct ast_channel *chan, int timeout)
+{
+ int c;
+ char *buf = ast_recvtext(chan, timeout);
+ if (buf == NULL)
+ return -1; /* error or timeout */
+ c = *(unsigned char *)buf;
+ ast_free(buf);
+ return c;
+}
+
+char *ast_recvtext(struct ast_channel *chan, int timeout)
+{
+ int res, done = 0;
+ char *buf = NULL;
+
+ while (!done) {
+ struct ast_frame *f;
+ if (ast_check_hangup(chan))
+ break;
+ res = ast_waitfor(chan, timeout);
+ if (res <= 0) /* timeout or error */
+ break;
+ timeout = res; /* update timeout */
+ f = ast_read(chan);
+ if (f == NULL)
+ break; /* no frame */
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)
+ done = 1; /* force a break */
+ else if (f->frametype == AST_FRAME_TEXT) { /* what we want */
+ buf = ast_strndup((char *) f->data, f->datalen); /* dup and break */
+ done = 1;
+ }
+ ast_frfree(f);
+ }
+ return buf;
+}
+
+int ast_sendtext(struct ast_channel *chan, const char *text)
+{
+ int res = 0;
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
+ return -1;
+ CHECK_BLOCKING(chan);
+ if (chan->tech->send_text)
+ res = chan->tech->send_text(chan, text);
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ return res;
+}
+
+int ast_senddigit_begin(struct ast_channel *chan, char digit)
+{
+ /* Device does not support DTMF tones, lets fake
+ * it by doing our own generation. */
+ static const char* dtmf_tones[] = {
+ "941+1336", /* 0 */
+ "697+1209", /* 1 */
+ "697+1336", /* 2 */
+ "697+1477", /* 3 */
+ "770+1209", /* 4 */
+ "770+1336", /* 5 */
+ "770+1477", /* 6 */
+ "852+1209", /* 7 */
+ "852+1336", /* 8 */
+ "852+1477", /* 9 */
+ "697+1633", /* A */
+ "770+1633", /* B */
+ "852+1633", /* C */
+ "941+1633", /* D */
+ "941+1209", /* * */
+ "941+1477" /* # */
+ };
+
+ if (!chan->tech->send_digit_begin)
+ return 0;
+
+ if (!chan->tech->send_digit_begin(chan, digit))
+ return 0;
+
+ if (digit >= '0' && digit <='9')
+ ast_playtones_start(chan, 0, dtmf_tones[digit-'0'], 0);
+ else if (digit >= 'A' && digit <= 'D')
+ ast_playtones_start(chan, 0, dtmf_tones[digit-'A'+10], 0);
+ else if (digit == '*')
+ ast_playtones_start(chan, 0, dtmf_tones[14], 0);
+ else if (digit == '#')
+ ast_playtones_start(chan, 0, dtmf_tones[15], 0);
+ else {
+ /* not handled */
+ ast_debug(1, "Unable to generate DTMF tone '%c' for '%s'\n", digit, chan->name);
+ }
+
+ return 0;
+}
+
+int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration)
+{
+ int res = -1;
+
+ if (chan->tech->send_digit_end)
+ res = chan->tech->send_digit_end(chan, digit, duration);
+
+ if (res && chan->generator)
+ ast_playtones_stop(chan);
+
+ return 0;
+}
+
+int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration)
+{
+ if (chan->tech->send_digit_begin) {
+ ast_senddigit_begin(chan, digit);
+ ast_safe_sleep(chan, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
+ }
+
+ return ast_senddigit_end(chan, digit, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
+}
+
+int ast_prod(struct ast_channel *chan)
+{
+ struct ast_frame a = { AST_FRAME_VOICE };
+ char nothing[128];
+
+ /* Send an empty audio frame to get things moving */
+ if (chan->_state != AST_STATE_UP) {
+ ast_debug(1, "Prodding channel '%s'\n", chan->name);
+ a.subclass = chan->rawwriteformat;
+ a.data = nothing + AST_FRIENDLY_OFFSET;
+ a.src = "ast_prod";
+ if (ast_write(chan, &a))
+ ast_log(LOG_WARNING, "Prodding channel '%s' failed\n", chan->name);
+ }
+ return 0;
+}
+
+int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
+{
+ int res;
+ if (!chan->tech->write_video)
+ return 0;
+ res = ast_write(chan, fr);
+ if (!res)
+ res = 1;
+ return res;
+}
+
+int ast_write(struct ast_channel *chan, struct ast_frame *fr)
+{
+ int res = -1;
+ struct ast_frame *f = NULL, *f2 = NULL;
+ int count = 0;
+
+ /*Deadlock avoidance*/
+ while(ast_channel_trylock(chan)) {
+ /*cannot goto done since the channel is not locked*/
+ if(count++ > 10) {
+ ast_debug(1, "Deadlock avoided for write to channel '%s'\n", chan->name);
+ return 0;
+ }
+ usleep(1);
+ }
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
+ goto done;
+
+ /* Handle any pending masquerades */
+ if (chan->masq && ast_do_masquerade(chan)) {
+ ast_log(LOG_WARNING, "Failed to perform masquerade\n");
+ goto done;
+ }
+ if (chan->masqr) {
+ res = 0; /* XXX explain, why 0 ? */
+ goto done;
+ }
+ if (chan->generatordata) {
+ if (ast_test_flag(chan, AST_FLAG_WRITE_INT))
+ ast_deactivate_generator(chan);
+ else {
+ if (fr->frametype == AST_FRAME_DTMF_END) {
+ /* There is a generator running while we're in the middle of a digit.
+ * It's probably inband DTMF, so go ahead and pass it so it can
+ * stop the generator */
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ ast_channel_unlock(chan);
+ res = ast_senddigit_end(chan, fr->subclass, fr->len);
+ ast_channel_lock(chan);
+ CHECK_BLOCKING(chan);
+ } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_UNHOLD) {
+ /* This is a side case where Echo is basically being called and the person put themselves on hold and took themselves off hold */
+ res = (chan->tech->indicate == NULL) ? 0 :
+ chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen);
+ }
+ res = 0; /* XXX explain, why 0 ? */
+ goto done;
+ }
+ }
+ /* High bit prints debugging */
+ if (chan->fout & DEBUGCHAN_FLAG)
+ ast_frame_dump(chan->name, fr, ">>");
+ CHECK_BLOCKING(chan);
+ switch (fr->frametype) {
+ case AST_FRAME_CONTROL:
+ res = (chan->tech->indicate == NULL) ? 0 :
+ chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen);
+ break;
+ case AST_FRAME_DTMF_BEGIN:
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = fr;
+ fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+ if (old_frame != fr)
+ f = fr;
+ }
+ send_dtmf_event(chan, "Sent", fr->subclass, "Yes", "No");
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ ast_channel_unlock(chan);
+ res = ast_senddigit_begin(chan, fr->subclass);
+ ast_channel_lock(chan);
+ CHECK_BLOCKING(chan);
+ break;
+ case AST_FRAME_DTMF_END:
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = fr;
+ fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+ if (old_frame != fr)
+ f = fr;
+ }
+ send_dtmf_event(chan, "Sent", fr->subclass, "No", "Yes");
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ ast_channel_unlock(chan);
+ res = ast_senddigit_end(chan, fr->subclass, fr->len);
+ ast_channel_lock(chan);
+ CHECK_BLOCKING(chan);
+ break;
+ case AST_FRAME_TEXT:
+ if (fr->subclass == AST_FORMAT_T140) {
+ res = (chan->tech->write_text == NULL) ? 0 :
+ chan->tech->write_text(chan, fr);
+ } else {
+ res = (chan->tech->send_text == NULL) ? 0 :
+ chan->tech->send_text(chan, (char *) fr->data);
+ }
+ break;
+ case AST_FRAME_HTML:
+ res = (chan->tech->send_html == NULL) ? 0 :
+ chan->tech->send_html(chan, fr->subclass, (char *) fr->data, fr->datalen);
+ break;
+ case AST_FRAME_VIDEO:
+ /* XXX Handle translation of video codecs one day XXX */
+ res = (chan->tech->write_video == NULL) ? 0 :
+ chan->tech->write_video(chan, fr);
+ break;
+ case AST_FRAME_MODEM:
+ res = (chan->tech->write == NULL) ? 0 :
+ chan->tech->write(chan, fr);
+ break;
+ case AST_FRAME_VOICE:
+ if (chan->tech->write == NULL)
+ break; /*! \todo XXX should return 0 maybe ? */
+
+ /* If audiohooks are present, write the frame out */
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = fr;
+ fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+ if (old_frame != fr)
+ f2 = fr;
+ }
+
+ /* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
+ if (fr->subclass == chan->rawwriteformat)
+ f = fr;
+ else
+ f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
+
+ if (!f) {
+ res = 0;
+ break;
+ }
+
+ /* If Monitor is running on this channel, then we have to write frames out there too */
+ if (chan->monitor && chan->monitor->write_stream) {
+ /* XXX must explain this code */
+#ifndef MONITOR_CONSTANT_DELAY
+ int jump = chan->insmpl - chan->outsmpl - 4 * f->samples;
+ if (jump >= 0) {
+ jump = chan->insmpl - chan->outsmpl;
+ if (ast_seekstream(chan->monitor->write_stream, jump, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
+ chan->outsmpl += jump + f->samples;
+ } else
+ chan->outsmpl += f->samples;
+#else
+ int jump = chan->insmpl - chan->outsmpl;
+ if (jump - MONITOR_DELAY >= 0) {
+ if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
+ chan->outsmpl += jump;
+ } else
+ chan->outsmpl += f->samples;
+#endif
+ if (chan->monitor->state == AST_MONITOR_RUNNING) {
+ if (ast_writestream(chan->monitor->write_stream, f) < 0)
+ ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
+ }
+ }
+
+ if (f)
+ res = chan->tech->write(chan,f);
+ else
+ res = 0;
+ break;
+ case AST_FRAME_NULL:
+ case AST_FRAME_IAX:
+ /* Ignore these */
+ res = 0;
+ break;
+ default:
+ /* At this point, fr is the incoming frame and f is NULL. Channels do
+ * not expect to get NULL as a frame pointer and will segfault. Hence,
+ * we output the original frame passed in. */
+ res = chan->tech->write(chan, fr);
+ break;
+ }
+
+ if (f && f != fr)
+ ast_frfree(f);
+ if (f2)
+ ast_frfree(f2);
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ /* Consider a write failure to force a soft hangup */
+ if (res < 0)
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ else {
+ chan->fout = FRAMECOUNT_INC(chan->fout);
+ }
+done:
+ ast_channel_unlock(chan);
+ return res;
+}
+
+static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *format,
+ struct ast_trans_pvt **trans, const int direction)
+{
+ int native;
+ int res;
+
+ /* Make sure we only consider audio */
+ fmt &= AST_FORMAT_AUDIO_MASK;
+
+ native = chan->nativeformats;
+ /* Find a translation path from the native format to one of the desired formats */
+ if (!direction)
+ /* reading */
+ res = ast_translator_best_choice(&fmt, &native);
+ else
+ /* writing */
+ res = ast_translator_best_choice(&native, &fmt);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n",
+ ast_getformatname(native), ast_getformatname(fmt));
+ return -1;
+ }
+
+ /* Now we have a good choice for both. */
+ ast_channel_lock(chan);
+
+ if ((*rawformat == native) && (*format == fmt) && ((*rawformat == *format) || (*trans))) {
+ /* the channel is already in these formats, so nothing to do */
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ *rawformat = native;
+ /* User perspective is fmt */
+ *format = fmt;
+ /* Free any read translation we have right now */
+ if (*trans)
+ ast_translator_free_path(*trans);
+ /* Build a translation path from the raw format to the desired format */
+ if (!direction)
+ /* reading */
+ *trans = ast_translator_build_path(*format, *rawformat);
+ else
+ /* writing */
+ *trans = ast_translator_build_path(*rawformat, *format);
+ ast_channel_unlock(chan);
+ ast_debug(1, "Set channel %s to %s format %s\n", chan->name,
+ direction ? "write" : "read", ast_getformatname(fmt));
+ return 0;
+}
+
+int ast_set_read_format(struct ast_channel *chan, int fmt)
+{
+ return set_format(chan, fmt, &chan->rawreadformat, &chan->readformat,
+ &chan->readtrans, 0);
+}
+
+int ast_set_write_format(struct ast_channel *chan, int fmt)
+{
+ return set_format(chan, fmt, &chan->rawwriteformat, &chan->writeformat,
+ &chan->writetrans, 1);
+}
+
+const char *ast_channel_reason2str(int reason)
+{
+ switch (reason) /* the following appear to be the only ones actually returned by request_and_dial */
+ {
+ case 0:
+ return "Call Failure (not BUSY, and not NO_ANSWER, maybe Circuit busy or down?)";
+ case AST_CONTROL_HANGUP:
+ return "Hangup";
+ case AST_CONTROL_RING:
+ return "Local Ring";
+ case AST_CONTROL_RINGING:
+ return "Remote end Ringing";
+ case AST_CONTROL_ANSWER:
+ return "Remote end has Answered";
+ case AST_CONTROL_BUSY:
+ return "Remote end is Busy";
+ case AST_CONTROL_CONGESTION:
+ return "Congestion (circuits busy)";
+ default:
+ return "Unknown Reason!!";
+ }
+}
+
+struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
+{
+ int dummy_outstate;
+ int cause = 0;
+ struct ast_channel *chan;
+ int res = 0;
+ int last_subclass = 0;
+
+ if (outstate)
+ *outstate = 0;
+ else
+ outstate = &dummy_outstate; /* make outstate always a valid pointer */
+
+ chan = ast_request(type, format, data, &cause);
+ if (!chan) {
+ ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
+ /* compute error and return */
+ if (cause == AST_CAUSE_BUSY)
+ *outstate = AST_CONTROL_BUSY;
+ else if (cause == AST_CAUSE_CONGESTION)
+ *outstate = AST_CONTROL_CONGESTION;
+ return NULL;
+ }
+
+ if (oh) {
+ if (oh->vars)
+ ast_set_variables(chan, oh->vars);
+ /* XXX why is this necessary, for the parent_channel perhaps ? */
+ if (!ast_strlen_zero(oh->cid_num) && !ast_strlen_zero(oh->cid_name))
+ ast_set_callerid(chan, oh->cid_num, oh->cid_name, oh->cid_num);
+ if (oh->parent_channel) {
+ ast_channel_inherit_variables(oh->parent_channel, chan);
+ ast_channel_datastore_inherit(oh->parent_channel, chan);
+ }
+ if (oh->account)
+ ast_cdr_setaccount(chan, oh->account);
+ }
+ ast_set_callerid(chan, cid_num, cid_name, cid_num);
+
+
+
+ if (!chan->cdr) { /* up till now, this insertion hasn't been done. Therefore,
+ to keep from throwing off the basic order of the universe,
+ we will try to keep this cdr from getting posted. */
+ chan->cdr = ast_cdr_alloc();
+ ast_cdr_init(chan->cdr, chan);
+ ast_cdr_start(chan->cdr);
+ }
+ if (ast_call(chan, data, 0)) { /* ast_call failed... */
+ ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
+ } else {
+ res = 1; /* mark success in case chan->_state is already AST_STATE_UP */
+ while (timeout && chan->_state != AST_STATE_UP) {
+ struct ast_frame *f;
+ res = ast_waitfor(chan, timeout);
+ if (res <= 0) /* error, timeout, or done */
+ break;
+ if (timeout > -1)
+ timeout = res;
+ f = ast_read(chan);
+ if (!f) {
+ *outstate = AST_CONTROL_HANGUP;
+ res = 0;
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch (f->subclass) {
+ case AST_CONTROL_RINGING: /* record but keep going */
+ *outstate = f->subclass;
+ break;
+
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_ANSWER:
+ *outstate = f->subclass;
+ timeout = 0; /* trick to force exit from the while() */
+ break;
+
+ /* Ignore these */
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ case AST_CONTROL_VIDUPDATE:
+ case -1: /* Ignore -- just stopping indications */
+ break;
+
+ default:
+ ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
+ }
+ last_subclass = f->subclass;
+ }
+ ast_frfree(f);
+ }
+ }
+
+ /* Final fixups */
+ if (oh) {
+ if (!ast_strlen_zero(oh->context))
+ ast_copy_string(chan->context, oh->context, sizeof(chan->context));
+ if (!ast_strlen_zero(oh->exten))
+ ast_copy_string(chan->exten, oh->exten, sizeof(chan->exten));
+ if (oh->priority)
+ chan->priority = oh->priority;
+ }
+ if (chan->_state == AST_STATE_UP)
+ *outstate = AST_CONTROL_ANSWER;
+
+ if (res <= 0) {
+ if ( AST_CONTROL_RINGING == last_subclass )
+ chan->hangupcause = AST_CAUSE_NO_ANSWER;
+ if (!chan->cdr && (chan->cdr = ast_cdr_alloc()))
+ ast_cdr_init(chan->cdr, chan);
+ if (chan->cdr) {
+ char tmp[256];
+ snprintf(tmp, sizeof(tmp), "%s/%s", type, (char *)data);
+ ast_cdr_setapp(chan->cdr,"Dial",tmp);
+ ast_cdr_update(chan);
+ ast_cdr_start(chan->cdr);
+ ast_cdr_end(chan->cdr);
+ /* If the cause wasn't handled properly */
+ if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+ ast_hangup(chan);
+ chan = NULL;
+ }
+ return chan;
+}
+
+struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname)
+{
+ return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
+}
+
+struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
+{
+ struct chanlist *chan;
+ struct ast_channel *c;
+ int capabilities;
+ int fmt;
+ int res;
+ int foo;
+ int videoformat = format & AST_FORMAT_VIDEO_MASK;
+ int textformat = format & AST_FORMAT_TEXT_MASK;
+
+ if (!cause)
+ cause = &foo;
+ *cause = AST_CAUSE_NOTDEFINED;
+
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return NULL;
+ }
+
+ AST_LIST_TRAVERSE(&backends, chan, list) {
+ if (strcasecmp(type, chan->tech->type))
+ continue;
+
+ capabilities = chan->tech->capabilities;
+ fmt = format & AST_FORMAT_AUDIO_MASK;
+ res = ast_translator_best_choice(&fmt, &capabilities);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "No translator path exists for channel type %s (native 0x%x) to 0x%x\n", type, chan->tech->capabilities, format);
+ *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+ AST_RWLIST_UNLOCK(&channels);
+ return NULL;
+ }
+ AST_RWLIST_UNLOCK(&channels);
+ if (!chan->tech->requester)
+ return NULL;
+
+ if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, data, cause)))
+ return NULL;
+
+ /* no need to generate a Newchannel event here; it is done in the channel_alloc call */
+ return c;
+ }
+
+ ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+ *cause = AST_CAUSE_NOSUCHDRIVER;
+ AST_RWLIST_UNLOCK(&channels);
+
+ return NULL;
+}
+
+int ast_call(struct ast_channel *chan, char *addr, int timeout)
+{
+ /* Place an outgoing call, but don't wait any longer than timeout ms before returning.
+ If the remote end does not answer within the timeout, then do NOT hang up, but
+ return anyway. */
+ int res = -1;
+ /* Stop if we're a zombie or need a soft hangup */
+ ast_channel_lock(chan);
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
+ if (chan->tech->call)
+ res = chan->tech->call(chan, addr, timeout);
+ ast_set_flag(chan, AST_FLAG_OUTGOING);
+ }
+ ast_channel_unlock(chan);
+ return res;
+}
+
+/*!
+ \brief Transfer a call to dest, if the channel supports transfer
+
+ Called by:
+ \arg app_transfer
+ \arg the manager interface
+*/
+int ast_transfer(struct ast_channel *chan, char *dest)
+{
+ int res = -1;
+
+ /* Stop if we're a zombie or need a soft hangup */
+ ast_channel_lock(chan);
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
+ if (chan->tech->transfer) {
+ res = chan->tech->transfer(chan, dest);
+ if (!res)
+ res = 1;
+ } else
+ res = 0;
+ }
+ ast_channel_unlock(chan);
+ return res;
+}
+
+int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
+{
+ return ast_readstring_full(c, s, len, timeout, ftimeout, enders, -1, -1);
+}
+
+int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd)
+{
+ int pos = 0; /* index in the buffer where we accumulate digits */
+ int to = ftimeout;
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
+ return -1;
+ if (!len)
+ return -1;
+ for (;;) {
+ int d;
+ if (c->stream) {
+ d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd);
+ ast_stopstream(c);
+ usleep(1000);
+ if (!d)
+ d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
+ } else {
+ d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
+ }
+ if (d < 0)
+ return -1;
+ if (d == 0) {
+ s[pos]='\0';
+ return 1;
+ }
+ if (d == 1) {
+ s[pos]='\0';
+ return 2;
+ }
+ if (!strchr(enders, d))
+ s[pos++] = d;
+ if (strchr(enders, d) || (pos >= len)) {
+ s[pos]='\0';
+ return 0;
+ }
+ to = timeout;
+ }
+ /* Never reached */
+ return 0;
+}
+
+int ast_channel_supports_html(struct ast_channel *chan)
+{
+ return (chan->tech->send_html) ? 1 : 0;
+}
+
+int ast_channel_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
+{
+ if (chan->tech->send_html)
+ return chan->tech->send_html(chan, subclass, data, datalen);
+ return -1;
+}
+
+int ast_channel_sendurl(struct ast_channel *chan, const char *url)
+{
+ return ast_channel_sendhtml(chan, AST_HTML_URL, url, strlen(url) + 1);
+}
+
+/*! \brief Set up translation from one channel to another */
+static int ast_channel_make_compatible_helper(struct ast_channel *from, struct ast_channel *to)
+{
+ int src;
+ int dst;
+
+ if (from->readformat == to->writeformat && from->writeformat == to->readformat) {
+ /* Already compatible! Moving on ... */
+ return 0;
+ }
+
+ /* Set up translation from the 'from' channel to the 'to' channel */
+ src = from->nativeformats;
+ dst = to->nativeformats;
+ if (ast_translator_best_choice(&dst, &src) < 0) {
+ ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", from->name, src, to->name, dst);
+ return -1;
+ }
+
+ /* if the best path is not 'pass through', then
+ transcoding is needed; if desired, force transcode path
+ to use SLINEAR between channels, but only if there is
+ no direct conversion available */
+ if ((src != dst) && ast_opt_transcode_via_slin &&
+ (ast_translate_path_steps(dst, src) != 1))
+ dst = AST_FORMAT_SLINEAR;
+ if (ast_set_read_format(from, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", from->name, dst);
+ return -1;
+ }
+ if (ast_set_write_format(to, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", to->name, dst);
+ return -1;
+ }
+ return 0;
+}
+
+int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
+{
+ /* Some callers do not check return code, and we must try to set all call legs correctly */
+ int rc = 0;
+
+ /* Set up translation from the chan to the peer */
+ rc = ast_channel_make_compatible_helper(chan, peer);
+
+ if (rc < 0)
+ return rc;
+
+ /* Set up translation from the peer to the chan */
+ rc = ast_channel_make_compatible_helper(peer, chan);
+
+ return rc;
+}
+
+int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
+{
+ int res = -1;
+ struct ast_channel *final_orig, *final_clone, *base;
+
+retrymasq:
+ final_orig = original;
+ final_clone = clone;
+
+ ast_channel_lock(original);
+ while (ast_channel_trylock(clone)) {
+ ast_channel_unlock(original);
+ usleep(1);
+ ast_channel_lock(original);
+ }
+
+ /* each of these channels may be sitting behind a channel proxy (i.e. chan_agent)
+ and if so, we don't really want to masquerade it, but its proxy */
+ if (original->_bridge && (original->_bridge != ast_bridged_channel(original)) && (original->_bridge->_bridge != original))
+ final_orig = original->_bridge;
+
+ if (clone->_bridge && (clone->_bridge != ast_bridged_channel(clone)) && (clone->_bridge->_bridge != clone))
+ final_clone = clone->_bridge;
+
+ if (final_clone->tech->get_base_channel && (base = final_clone->tech->get_base_channel(final_clone))) {
+ final_clone = base;
+ }
+
+ if ((final_orig != original) || (final_clone != clone)) {
+ /* Lots and lots of deadlock avoidance. The main one we're competing with
+ * is ast_write(), which locks channels recursively, when working with a
+ * proxy channel. */
+ if (ast_channel_trylock(final_orig)) {
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ goto retrymasq;
+ }
+ if (ast_channel_trylock(final_clone)) {
+ ast_channel_unlock(final_orig);
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ goto retrymasq;
+ }
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ original = final_orig;
+ clone = final_clone;
+ }
+
+ if (original == clone) {
+ ast_log(LOG_WARNING, "Can't masquerade channel '%s' into itself!\n", original->name);
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ return -1;
+ }
+
+ ast_debug(1, "Planning to masquerade channel %s into the structure of %s\n",
+ clone->name, original->name);
+ if (original->masq) {
+ ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
+ original->masq->name, original->name);
+ } else if (clone->masqr) {
+ ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
+ clone->name, clone->masqr->name);
+ } else {
+ original->masq = clone;
+ clone->masqr = original;
+ ast_queue_frame(original, &ast_null_frame);
+ ast_queue_frame(clone, &ast_null_frame);
+ ast_debug(1, "Done planning to masquerade channel %s into the structure of %s\n", clone->name, original->name);
+ res = 0;
+ }
+
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+
+ return res;
+}
+
+void ast_change_name(struct ast_channel *chan, char *newname)
+{
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", chan->name, newname, chan->uniqueid);
+ ast_string_field_set(chan, name, newname);
+}
+
+void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
+{
+ struct ast_var_t *current, *newvar;
+ const char *varname;
+
+ AST_LIST_TRAVERSE(&parent->varshead, current, entries) {
+ int vartype = 0;
+
+ varname = ast_var_full_name(current);
+ if (!varname)
+ continue;
+
+ if (varname[0] == '_') {
+ vartype = 1;
+ if (varname[1] == '_')
+ vartype = 2;
+ }
+
+ switch (vartype) {
+ case 1:
+ newvar = ast_var_assign(&varname[1], ast_var_value(current));
+ if (newvar) {
+ AST_LIST_INSERT_TAIL(&child->varshead, newvar, entries);
+ ast_debug(1, "Copying soft-transferable variable %s.\n", ast_var_name(newvar));
+ }
+ break;
+ case 2:
+ newvar = ast_var_assign(varname, ast_var_value(current));
+ if (newvar) {
+ AST_LIST_INSERT_TAIL(&child->varshead, newvar, entries);
+ ast_debug(1, "Copying hard-transferable variable %s.\n", ast_var_name(newvar));
+ }
+ break;
+ default:
+ ast_debug(1, "Not copying variable %s.\n", ast_var_name(current));
+ break;
+ }
+ }
+}
+
+/*!
+ \brief Clone channel variables from 'clone' channel into 'original' channel
+
+ All variables except those related to app_groupcount are cloned.
+ Variables are actually _removed_ from 'clone' channel, presumably
+ because it will subsequently be destroyed.
+
+ \note Assumes locks will be in place on both channels when called.
+*/
+static void clone_variables(struct ast_channel *original, struct ast_channel *clone)
+{
+ struct ast_var_t *current, *newvar;
+ /* Append variables from clone channel into original channel */
+ /* XXX Is this always correct? We have to in order to keep MACROS working XXX */
+ if (AST_LIST_FIRST(&clone->varshead))
+ AST_LIST_APPEND_LIST(&original->varshead, &clone->varshead, entries);
+
+ /* then, dup the varshead list into the clone */
+
+ AST_LIST_TRAVERSE(&original->varshead, current, entries) {
+ newvar = ast_var_assign(current->name, current->value);
+ if (newvar)
+ AST_LIST_INSERT_TAIL(&clone->varshead, newvar, entries);
+ }
+}
+
+/*!
+ \brief Masquerade a channel
+
+ \note Assumes channel will be locked when called
+*/
+int ast_do_masquerade(struct ast_channel *original)
+{
+ int x,i;
+ int res=0;
+ int origstate;
+ struct ast_frame *cur;
+ const struct ast_channel_tech *t;
+ void *t_pvt;
+ struct ast_callerid tmpcid;
+ struct ast_channel *clone = original->masq;
+ struct ast_cdr *cdr;
+ int rformat = original->readformat;
+ int wformat = original->writeformat;
+ char newn[AST_CHANNEL_NAME];
+ char orig[AST_CHANNEL_NAME];
+ char masqn[AST_CHANNEL_NAME];
+ char zombn[AST_CHANNEL_NAME];
+
+ ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
+ clone->name, clone->_state, original->name, original->_state);
+
+ manager_event(EVENT_FLAG_CALL, "Masquerade", "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n",
+ clone->name, ast_state2str(clone->_state), original->name, ast_state2str(original->_state));
+
+ /* XXX This is a seriously wacked out operation. We're essentially putting the guts of
+ the clone channel into the original channel. Start by killing off the original
+ channel's backend. I'm not sure we're going to keep this function, because
+ while the features are nice, the cost is very high in terms of pure nastiness. XXX */
+
+ /* We need the clone's lock, too */
+ ast_channel_lock(clone);
+
+ ast_debug(2, "Got clone lock for masquerade on '%s' at %p\n", clone->name, &clone->lock_dont_use);
+
+ /* Having remembered the original read/write formats, we turn off any translation on either
+ one */
+ free_translation(clone);
+ free_translation(original);
+
+
+ /* Unlink the masquerade */
+ original->masq = NULL;
+ clone->masqr = NULL;
+
+ /* Save the original name */
+ ast_copy_string(orig, original->name, sizeof(orig));
+ /* Save the new name */
+ ast_copy_string(newn, clone->name, sizeof(newn));
+ /* Create the masq name */
+ snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
+
+ /* Copy the name from the clone channel */
+ ast_string_field_set(original, name, newn);
+
+ /* Mangle the name of the clone channel */
+ ast_string_field_set(clone, name, masqn);
+
+ /* Notify any managers of the change, first the masq then the other */
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clone->uniqueid);
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid);
+
+ /* Swap the technologies */
+ t = original->tech;
+ original->tech = clone->tech;
+ clone->tech = t;
+
+ /* Swap the cdrs */
+ cdr = original->cdr;
+ original->cdr = clone->cdr;
+ clone->cdr = cdr;
+
+ t_pvt = original->tech_pvt;
+ original->tech_pvt = clone->tech_pvt;
+ clone->tech_pvt = t_pvt;
+
+ /* Swap the alertpipes */
+ for (i = 0; i < 2; i++) {
+ x = original->alertpipe[i];
+ original->alertpipe[i] = clone->alertpipe[i];
+ clone->alertpipe[i] = x;
+ }
+
+ /*
+ * Swap the readq's. The end result should be this:
+ *
+ * 1) All frames should be on the new (original) channel.
+ * 2) Any frames that were already on the new channel before this
+ * masquerade need to be at the end of the readq, after all of the
+ * frames on the old (clone) channel.
+ * 3) The alertpipe needs to get poked for every frame that was already
+ * on the new channel, since we are now using the alert pipe from the
+ * old (clone) channel.
+ */
+ {
+ AST_LIST_HEAD_NOLOCK(, ast_frame) tmp_readq;
+ AST_LIST_HEAD_SET_NOLOCK(&tmp_readq, NULL);
+
+ AST_LIST_APPEND_LIST(&tmp_readq, &original->readq, frame_list);
+ AST_LIST_APPEND_LIST(&original->readq, &clone->readq, frame_list);
+
+ while ((cur = AST_LIST_REMOVE_HEAD(&tmp_readq, frame_list))) {
+ AST_LIST_INSERT_TAIL(&original->readq, cur, frame_list);
+ if (original->alertpipe[1] > -1) {
+ int poke = 0;
+ write(original->alertpipe[1], &poke, sizeof(poke));
+ }
+ }
+ }
+
+ /* Swap the raw formats */
+ x = original->rawreadformat;
+ original->rawreadformat = clone->rawreadformat;
+ clone->rawreadformat = x;
+ x = original->rawwriteformat;
+ original->rawwriteformat = clone->rawwriteformat;
+ clone->rawwriteformat = x;
+
+ clone->_softhangup = AST_SOFTHANGUP_DEV;
+
+ /* And of course, so does our current state. Note we need not
+ call ast_setstate since the event manager doesn't really consider
+ these separate. We do this early so that the clone has the proper
+ state of the original channel. */
+ origstate = original->_state;
+ original->_state = clone->_state;
+ clone->_state = origstate;
+
+ if (clone->tech->fixup){
+ res = clone->tech->fixup(original, clone);
+ if (res)
+ ast_log(LOG_WARNING, "Fixup failed on channel %s, strange things may happen.\n", clone->name);
+ }
+
+ /* Start by disconnecting the original's physical side */
+ if (clone->tech->hangup)
+ res = clone->tech->hangup(clone);
+ if (res) {
+ ast_log(LOG_WARNING, "Hangup failed! Strange things may happen!\n");
+ ast_channel_unlock(clone);
+ return -1;
+ }
+
+ snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
+ /* Mangle the name of the clone channel */
+ ast_string_field_set(clone, name, zombn);
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", masqn, zombn, clone->uniqueid);
+
+ /* Update the type. */
+ t_pvt = original->monitor;
+ original->monitor = clone->monitor;
+ clone->monitor = t_pvt;
+
+ /* Keep the same language. */
+ ast_string_field_set(original, language, clone->language);
+ /* Copy the FD's other than the generator fd */
+ for (x = 0; x < AST_MAX_FDS; x++) {
+ if (x != AST_GENERATOR_FD)
+ ast_channel_set_fd(original, x, clone->fds[x]);
+ }
+
+ ast_app_group_update(clone, original);
+
+ /* Move data stores over */
+ if (AST_LIST_FIRST(&clone->datastores))
+ AST_LIST_APPEND_LIST(&original->datastores, &clone->datastores, entry);
+
+ clone_variables(original, clone);
+ /* Presense of ADSI capable CPE follows clone */
+ original->adsicpe = clone->adsicpe;
+ /* Bridge remains the same */
+ /* CDR fields remain the same */
+ /* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
+ /* Application and data remain the same */
+ /* Clone exception becomes real one, as with fdno */
+ ast_copy_flags(original, clone, AST_FLAG_EXCEPTION);
+ original->fdno = clone->fdno;
+ /* Schedule context remains the same */
+ /* Stream stuff stays the same */
+ /* Keep the original state. The fixup code will need to work with it most likely */
+
+ /* Just swap the whole structures, nevermind the allocations, they'll work themselves
+ out. */
+ tmpcid = original->cid;
+ original->cid = clone->cid;
+ clone->cid = tmpcid;
+
+ /* Restore original timing file descriptor */
+ ast_channel_set_fd(original, AST_TIMING_FD, original->timingfd);
+
+ /* Our native formats are different now */
+ original->nativeformats = clone->nativeformats;
+
+ /* Context, extension, priority, app data, jump table, remain the same */
+ /* pvt switches. pbx stays the same, as does next */
+
+ /* Set the write format */
+ ast_set_write_format(original, wformat);
+
+ /* Set the read format */
+ ast_set_read_format(original, rformat);
+
+ /* Copy the music class */
+ ast_string_field_set(original, musicclass, clone->musicclass);
+
+ ast_debug(1, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat);
+
+ /* Okay. Last thing is to let the channel driver know about all this mess, so he
+ can fix up everything as best as possible */
+ if (original->tech->fixup) {
+ res = original->tech->fixup(clone, original);
+ if (res) {
+ ast_log(LOG_WARNING, "Channel for type '%s' could not fixup channel %s\n",
+ original->tech->type, original->name);
+ ast_channel_unlock(clone);
+ return -1;
+ }
+ } else
+ ast_log(LOG_WARNING, "Channel type '%s' does not have a fixup routine (for %s)! Bad things may happen.\n",
+ original->tech->type, original->name);
+
+ /* If an indication is currently playing maintain it on the channel that is taking the place of original */
+ if (original->visible_indication)
+ ast_indicate(original, original->visible_indication);
+
+ /* Now, at this point, the "clone" channel is totally F'd up. We mark it as
+ a zombie so nothing tries to touch it. If it's already been marked as a
+ zombie, then free it now (since it already is considered invalid). */
+ if (ast_test_flag(clone, AST_FLAG_ZOMBIE)) {
+ ast_debug(1, "Destroying channel clone '%s'\n", clone->name);
+ ast_channel_unlock(clone);
+ manager_event(EVENT_FLAG_CALL, "Hangup",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Cause: %d\r\n"
+ "Cause-txt: %s\r\n",
+ clone->name,
+ clone->uniqueid,
+ clone->hangupcause,
+ ast_cause2str(clone->hangupcause)
+ );
+ ast_channel_free(clone);
+ } else {
+ ast_debug(1, "Released clone lock on '%s'\n", clone->name);
+ ast_set_flag(clone, AST_FLAG_ZOMBIE);
+ ast_queue_frame(clone, &ast_null_frame);
+ ast_channel_unlock(clone);
+ }
+
+ /* Signal any blocker */
+ if (ast_test_flag(original, AST_FLAG_BLOCKING))
+ pthread_kill(original->blocker, SIGURG);
+ ast_debug(1, "Done Masquerading %s (%d)\n", original->name, original->_state);
+ return 0;
+}
+
+void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
+{
+ ast_channel_lock(chan);
+
+ if (cid_num) {
+ if (chan->cid.cid_num)
+ ast_free(chan->cid.cid_num);
+ chan->cid.cid_num = ast_strdup(cid_num);
+ }
+ if (cid_name) {
+ if (chan->cid.cid_name)
+ ast_free(chan->cid.cid_name);
+ chan->cid.cid_name = ast_strdup(cid_name);
+ }
+ if (cid_ani) {
+ if (chan->cid.cid_ani)
+ ast_free(chan->cid.cid_ani);
+ chan->cid.cid_ani = ast_strdup(cid_ani);
+ }
+ if (chan->cdr)
+ ast_cdr_setcid(chan->cdr, chan);
+ manager_event(EVENT_FLAG_CALL, "NewCallerid",
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "CID-CallingPres: %d (%s)\r\n",
+ chan->name,
+ S_OR(chan->cid.cid_num, ""),
+ S_OR(chan->cid.cid_name, ""),
+ chan->uniqueid,
+ chan->cid.cid_pres,
+ ast_describe_caller_presentation(chan->cid.cid_pres)
+ );
+
+ ast_channel_unlock(chan);
+}
+
+int ast_setstate(struct ast_channel *chan, enum ast_channel_state state)
+{
+ int oldstate = chan->_state;
+
+ if (oldstate == state)
+ return 0;
+
+ chan->_state = state;
+ ast_device_state_changed_literal(chan->name);
+ /* setstate used to conditionally report Newchannel; this is no more */
+ manager_event(EVENT_FLAG_CALL,
+ "Newstate",
+ "Channel: %s\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan->name, chan->_state, ast_state2str(chan->_state),
+ S_OR(chan->cid.cid_num, ""),
+ S_OR(chan->cid.cid_name, ""),
+ chan->uniqueid);
+
+ return 0;
+}
+
+/*! \brief Find bridged channel */
+struct ast_channel *ast_bridged_channel(struct ast_channel *chan)
+{
+ struct ast_channel *bridged;
+ bridged = chan->_bridge;
+ if (bridged && bridged->tech->bridged_channel)
+ bridged = bridged->tech->bridged_channel(chan, bridged);
+ return bridged;
+}
+
+static void bridge_playfile(struct ast_channel *chan, struct ast_channel *peer, const char *sound, int remain)
+{
+ int min = 0, sec = 0, check;
+
+ check = ast_autoservice_start(peer);
+ if (check)
+ return;
+
+ if (remain > 0) {
+ if (remain / 60 > 1) {
+ min = remain / 60;
+ sec = remain % 60;
+ } else {
+ sec = remain;
+ }
+ }
+
+ if (!strcmp(sound,"timeleft")) { /* Queue support */
+ ast_stream_and_wait(chan, "vm-youhave", "");
+ if (min) {
+ ast_say_number(chan, min, AST_DIGIT_ANY, chan->language, NULL);
+ ast_stream_and_wait(chan, "queue-minutes", "");
+ }
+ if (sec) {
+ ast_say_number(chan, sec, AST_DIGIT_ANY, chan->language, NULL);
+ ast_stream_and_wait(chan, "queue-seconds", "");
+ }
+ } else {
+ ast_stream_and_wait(chan, sound, "");
+ }
+
+ ast_autoservice_stop(peer);
+}
+
+static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_channel *c1,
+ struct ast_bridge_config *config, struct ast_frame **fo,
+ struct ast_channel **rc, struct timeval bridge_end)
+{
+ /* Copy voice back and forth between the two channels. */
+ struct ast_channel *cs[3];
+ struct ast_frame *f;
+ enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
+ int o0nativeformats;
+ int o1nativeformats;
+ int watch_c0_dtmf;
+ int watch_c1_dtmf;
+ void *pvt0, *pvt1;
+ /* Indicates whether a frame was queued into a jitterbuffer */
+ int frame_put_in_jb = 0;
+ int jb_in_use;
+ int to;
+
+ cs[0] = c0;
+ cs[1] = c1;
+ pvt0 = c0->tech_pvt;
+ pvt1 = c1->tech_pvt;
+ o0nativeformats = c0->nativeformats;
+ o1nativeformats = c1->nativeformats;
+ watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0;
+ watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1;
+
+ /* Check the need of a jitterbuffer for each channel */
+ jb_in_use = ast_jb_do_usecheck(c0, c1);
+
+ ast_poll_channel_add(c0, c1);
+
+ for (;;) {
+ struct ast_channel *who, *other;
+
+ if ((c0->tech_pvt != pvt0) || (c1->tech_pvt != pvt1) ||
+ (o0nativeformats != c0->nativeformats) ||
+ (o1nativeformats != c1->nativeformats)) {
+ /* Check for Masquerade, codec changes, etc */
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ if (bridge_end.tv_sec) {
+ to = ast_tvdiff_ms(bridge_end, ast_tvnow());
+ if (to <= 0) {
+ if (config->timelimit)
+ res = AST_BRIDGE_RETRY;
+ else
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ } else
+ to = -1;
+ /* Calculate the appropriate max sleep interval - in general, this is the time,
+ left to the closest jb delivery moment */
+ if (jb_in_use)
+ to = ast_jb_get_when_to_wakeup(c0, c1, to);
+ who = ast_waitfor_n(cs, 2, &to);
+ if (!who) {
+ /* No frame received within the specified timeout - check if we have to deliver now */
+ if (jb_in_use)
+ ast_jb_get_and_deliver(c0, c1);
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c0->_softhangup = 0;
+ if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c1->_softhangup = 0;
+ c0->_bridge = c1;
+ c1->_bridge = c0;
+ }
+ continue;
+ }
+ f = ast_read(who);
+ if (!f) {
+ *fo = NULL;
+ *rc = who;
+ ast_debug(1, "Didn't get a frame from channel: %s\n",who->name);
+ break;
+ }
+
+ other = (who == c0) ? c1 : c0; /* the 'other' channel */
+ /* Try add the frame info the who's bridged channel jitterbuff */
+ if (jb_in_use)
+ frame_put_in_jb = !ast_jb_put(other, f);
+
+ if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
+ int bridge_exit = 0;
+
+ switch (f->subclass) {
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ case AST_CONTROL_VIDUPDATE:
+ ast_indicate_data(other, f->subclass, f->data, f->datalen);
+ break;
+ default:
+ *fo = f;
+ *rc = who;
+ bridge_exit = 1;
+ ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
+ break;
+ }
+ if (bridge_exit)
+ break;
+ }
+ if ((f->frametype == AST_FRAME_VOICE) ||
+ (f->frametype == AST_FRAME_DTMF_BEGIN) ||
+ (f->frametype == AST_FRAME_DTMF) ||
+ (f->frametype == AST_FRAME_VIDEO) ||
+ (f->frametype == AST_FRAME_IMAGE) ||
+ (f->frametype == AST_FRAME_HTML) ||
+ (f->frametype == AST_FRAME_MODEM) ||
+ (f->frametype == AST_FRAME_TEXT)) {
+ /* monitored dtmf causes exit from bridge */
+ int monitored_source = (who == c0) ? watch_c0_dtmf : watch_c1_dtmf;
+
+ if (monitored_source &&
+ (f->frametype == AST_FRAME_DTMF_END ||
+ f->frametype == AST_FRAME_DTMF_BEGIN)) {
+ *fo = f;
+ *rc = who;
+ ast_debug(1, "Got DTMF %s on channel (%s)\n",
+ f->frametype == AST_FRAME_DTMF_END ? "end" : "begin",
+ who->name);
+ break;
+ }
+ /* Write immediately frames, not passed through jb */
+ if (!frame_put_in_jb)
+ ast_write(other, f);
+
+ /* Check if we have to deliver now */
+ if (jb_in_use)
+ ast_jb_get_and_deliver(c0, c1);
+ }
+ /* XXX do we want to pass on also frames not matched above ? */
+ ast_frfree(f);
+
+#ifndef HAVE_EPOLL
+ /* Swap who gets priority */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+#endif
+ }
+
+ ast_poll_channel_del(c0, c1);
+
+ return res;
+}
+
+/*! \brief Bridge two channels together (early) */
+int ast_channel_early_bridge(struct ast_channel *c0, struct ast_channel *c1)
+{
+ /* Make sure we can early bridge, if not error out */
+ if (!c0->tech->early_bridge || (c1 && (!c1->tech->early_bridge || c0->tech->early_bridge != c1->tech->early_bridge)))
+ return -1;
+
+ return c0->tech->early_bridge(c0, c1);
+}
+
+/*! \brief Send manager event for bridge link and unlink events.
+ \param type 1 for core, 2 for native
+*/
+static void manager_bridge_event(int onoff, int type, struct ast_channel *c0, struct ast_channel *c1)
+{
+ manager_event(EVENT_FLAG_CALL, "Bridge",
+ "Bridgestate: %s\r\n"
+ "Bridgetype: %s\r\n"
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n"
+ "Uniqueid1: %s\r\n"
+ "Uniqueid2: %s\r\n"
+ "CallerID1: %s\r\n"
+ "CallerID2: %s\r\n",
+ onoff ? "Link" : "Unlink",
+ type == 1 ? "core" : "native",
+ c0->name, c1->name, c0->uniqueid, c1->uniqueid,
+ S_OR(c0->cid.cid_num, ""),
+ S_OR(c1->cid.cid_num, ""));
+}
+
+/*! \brief Bridge two channels together */
+enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1,
+ struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc)
+{
+ struct ast_channel *who = NULL;
+ enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
+ int nativefailed=0;
+ int firstpass;
+ int o0nativeformats;
+ int o1nativeformats;
+ long time_left_ms=0;
+ struct timeval nexteventts = { 0, };
+ char caller_warning = 0;
+ char callee_warning = 0;
+
+ if (c0->_bridge) {
+ ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
+ c0->name, c0->_bridge->name);
+ return -1;
+ }
+ if (c1->_bridge) {
+ ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
+ c1->name, c1->_bridge->name);
+ return -1;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
+ ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1))
+ return -1;
+
+ *fo = NULL;
+ firstpass = config->firstpass;
+ config->firstpass = 0;
+
+ if (ast_tvzero(config->start_time))
+ config->start_time = ast_tvnow();
+ time_left_ms = config->timelimit;
+
+ caller_warning = ast_test_flag(&config->features_caller, AST_FEATURE_PLAY_WARNING);
+ callee_warning = ast_test_flag(&config->features_callee, AST_FEATURE_PLAY_WARNING);
+
+ if (config->start_sound && firstpass) {
+ if (caller_warning)
+ bridge_playfile(c0, c1, config->start_sound, time_left_ms / 1000);
+ if (callee_warning)
+ bridge_playfile(c1, c0, config->start_sound, time_left_ms / 1000);
+ }
+
+ /* Keep track of bridge */
+ c0->_bridge = c1;
+ c1->_bridge = c0;
+
+
+ o0nativeformats = c0->nativeformats;
+ o1nativeformats = c1->nativeformats;
+
+ if (config->feature_timer) {
+ nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->feature_timer, 1000));
+ } else if (config->timelimit) {
+ nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
+ if (caller_warning || callee_warning)
+ nexteventts = ast_tvsub(nexteventts, ast_samp2tv(config->play_warning, 1000));
+ }
+
+ if (!c0->tech->send_digit_begin)
+ ast_set_flag(c1, AST_FLAG_END_DTMF_ONLY);
+ if (!c1->tech->send_digit_begin)
+ ast_set_flag(c0, AST_FLAG_END_DTMF_ONLY);
+ manager_bridge_event(1, 1, c0, c1);
+
+ for (/* ever */;;) {
+ struct timeval now = { 0, };
+ int to;
+
+ to = -1;
+
+ if (!ast_tvzero(nexteventts)) {
+ now = ast_tvnow();
+ to = ast_tvdiff_ms(nexteventts, now);
+ if (to <= 0) {
+ if (!config->timelimit) {
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ to = 0;
+ }
+ }
+
+ if (config->timelimit) {
+ time_left_ms = config->timelimit - ast_tvdiff_ms(now, config->start_time);
+ if (time_left_ms < to)
+ to = time_left_ms;
+
+ if (time_left_ms <= 0) {
+ if (caller_warning && config->end_sound)
+ bridge_playfile(c0, c1, config->end_sound, 0);
+ if (callee_warning && config->end_sound)
+ bridge_playfile(c1, c0, config->end_sound, 0);
+ *fo = NULL;
+ if (who)
+ *rc = who;
+ res = 0;
+ break;
+ }
+
+ if (!to) {
+ if (time_left_ms >= 5000 && config->warning_sound && config->play_warning) {
+ int t = (time_left_ms + 500) / 1000; /* round to nearest second */
+ if (caller_warning)
+ bridge_playfile(c0, c1, config->warning_sound, t);
+ if (callee_warning)
+ bridge_playfile(c1, c0, config->warning_sound, t);
+ }
+ if (config->warning_freq && (time_left_ms > (config->warning_freq + 5000)))
+ nexteventts = ast_tvadd(nexteventts, ast_samp2tv(config->warning_freq, 1000));
+ else
+ nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
+ }
+ }
+
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c0->_softhangup = 0;
+ if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c1->_softhangup = 0;
+ c0->_bridge = c1;
+ c1->_bridge = c0;
+ ast_debug(1, "Unbridge signal received. Ending native bridge.\n");
+ continue;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
+ ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) {
+ *fo = NULL;
+ if (who)
+ *rc = who;
+ res = 0;
+ ast_debug(1, "Bridge stops because we're zombie or need a soft hangup: c0=%s, c1=%s, flags: %s,%s,%s,%s\n",
+ c0->name, c1->name,
+ ast_test_flag(c0, AST_FLAG_ZOMBIE) ? "Yes" : "No",
+ ast_check_hangup(c0) ? "Yes" : "No",
+ ast_test_flag(c1, AST_FLAG_ZOMBIE) ? "Yes" : "No",
+ ast_check_hangup(c1) ? "Yes" : "No");
+ break;
+ }
+
+ /* See if the BRIDGEPEER variable needs to be updated */
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(c0, "BRIDGEPEER")))
+ pbx_builtin_setvar_helper(c0, "BRIDGEPEER", c1->name);
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(c1, "BRIDGEPEER")))
+ pbx_builtin_setvar_helper(c1, "BRIDGEPEER", c0->name);
+
+ if (c0->tech->bridge &&
+ (config->timelimit == 0) &&
+ (c0->tech->bridge == c1->tech->bridge) &&
+ !nativefailed && !c0->monitor && !c1->monitor &&
+ !c0->audiohooks && !c1->audiohooks &&
+ !c0->masq && !c0->masqr && !c1->masq && !c1->masqr) {
+ /* Looks like they share a bridge method and nothing else is in the way */
+ ast_set_flag(c0, AST_FLAG_NBRIDGE);
+ ast_set_flag(c1, AST_FLAG_NBRIDGE);
+ if ((res = c0->tech->bridge(c0, c1, config->flags, fo, rc, to)) == AST_BRIDGE_COMPLETE) {
+ /* \todo XXX here should check that cid_num is not NULL */
+ manager_event(EVENT_FLAG_CALL, "Unlink",
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n"
+ "Uniqueid1: %s\r\n"
+ "Uniqueid2: %s\r\n"
+ "CallerID1: %s\r\n"
+ "CallerID2: %s\r\n",
+ c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
+ ast_debug(1, "Returning from native bridge, channels: %s, %s\n", c0->name, c1->name);
+
+ ast_clear_flag(c0, AST_FLAG_NBRIDGE);
+ ast_clear_flag(c1, AST_FLAG_NBRIDGE);
+
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ continue;
+
+ c0->_bridge = NULL;
+ c1->_bridge = NULL;
+
+ return res;
+ } else {
+ ast_clear_flag(c0, AST_FLAG_NBRIDGE);
+ ast_clear_flag(c1, AST_FLAG_NBRIDGE);
+ }
+ switch (res) {
+ case AST_BRIDGE_RETRY:
+ continue;
+ default:
+ ast_verb(3, "Native bridging %s and %s ended\n", c0->name, c1->name);
+ /* fallthrough */
+ case AST_BRIDGE_FAILED_NOWARN:
+ nativefailed++;
+ break;
+ }
+ }
+
+ if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat) ||
+ (c0->nativeformats != o0nativeformats) || (c1->nativeformats != o1nativeformats)) &&
+ !(c0->generator || c1->generator)) {
+ if (ast_channel_make_compatible(c0, c1)) {
+ ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name);
+ manager_bridge_event(0, 1, c0, c1);
+ return AST_BRIDGE_FAILED;
+ }
+ o0nativeformats = c0->nativeformats;
+ o1nativeformats = c1->nativeformats;
+ }
+ res = ast_generic_bridge(c0, c1, config, fo, rc, nexteventts);
+ if (res != AST_BRIDGE_RETRY)
+ break;
+ }
+
+ ast_clear_flag(c0, AST_FLAG_END_DTMF_ONLY);
+ ast_clear_flag(c1, AST_FLAG_END_DTMF_ONLY);
+
+ c0->_bridge = NULL;
+ c1->_bridge = NULL;
+
+ /* \todo XXX here should check that cid_num is not NULL */
+ manager_event(EVENT_FLAG_CALL, "Unlink",
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n"
+ "Uniqueid1: %s\r\n"
+ "Uniqueid2: %s\r\n"
+ "CallerID1: %s\r\n"
+ "CallerID2: %s\r\n",
+ c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
+ ast_debug(1, "Bridge stops bridging channels %s and %s\n", c0->name, c1->name);
+
+ return res;
+}
+
+/*! \brief Sets an option on a channel */
+int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
+{
+ int res;
+
+ if (chan->tech->setoption) {
+ res = chan->tech->setoption(chan, option, data, datalen);
+ if (res < 0)
+ return res;
+ } else {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (block) {
+ /* XXX Implement blocking -- just wait for our option frame reply, discarding
+ intermediate packets. XXX */
+ ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
+ return -1;
+ }
+ return 0;
+}
+
+struct tonepair_def {
+ int freq1;
+ int freq2;
+ int duration;
+ int vol;
+};
+
+struct tonepair_state {
+ int fac1;
+ int fac2;
+ int v1_1;
+ int v2_1;
+ int v3_1;
+ int v1_2;
+ int v2_2;
+ int v3_2;
+ int origwfmt;
+ int pos;
+ int duration;
+ int modulate;
+ struct ast_frame f;
+ unsigned char offset[AST_FRIENDLY_OFFSET];
+ short data[4000];
+};
+
+static void tonepair_release(struct ast_channel *chan, void *params)
+{
+ struct tonepair_state *ts = params;
+
+ if (chan)
+ ast_set_write_format(chan, ts->origwfmt);
+ ast_free(ts);
+}
+
+static void *tonepair_alloc(struct ast_channel *chan, void *params)
+{
+ struct tonepair_state *ts;
+ struct tonepair_def *td = params;
+
+ if (!(ts = ast_calloc(1, sizeof(*ts))))
+ return NULL;
+ ts->origwfmt = chan->writeformat;
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
+ tonepair_release(NULL, ts);
+ ts = NULL;
+ } else {
+ ts->fac1 = 2.0 * cos(2.0 * M_PI * (td->freq1 / 8000.0)) * 32768.0;
+ ts->v1_1 = 0;
+ ts->v2_1 = sin(-4.0 * M_PI * (td->freq1 / 8000.0)) * td->vol;
+ ts->v3_1 = sin(-2.0 * M_PI * (td->freq1 / 8000.0)) * td->vol;
+ ts->v2_1 = 0;
+ ts->fac2 = 2.0 * cos(2.0 * M_PI * (td->freq2 / 8000.0)) * 32768.0;
+ ts->v2_2 = sin(-4.0 * M_PI * (td->freq2 / 8000.0)) * td->vol;
+ ts->v3_2 = sin(-2.0 * M_PI * (td->freq2 / 8000.0)) * td->vol;
+ ts->duration = td->duration;
+ ts->modulate = 0;
+ }
+ /* Let interrupts interrupt :) */
+ ast_set_flag(chan, AST_FLAG_WRITE_INT);
+ return ts;
+}
+
+static int tonepair_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct tonepair_state *ts = data;
+ int x;
+
+ /* we need to prepare a frame with 16 * timelen samples as we're
+ * generating SLIN audio
+ */
+ len = samples * 2;
+
+ if (len > sizeof(ts->data) / 2 - 1) {
+ ast_log(LOG_WARNING, "Can't generate that much data!\n");
+ return -1;
+ }
+ memset(&ts->f, 0, sizeof(ts->f));
+ for (x=0;x<len/2;x++) {
+ ts->v1_1 = ts->v2_1;
+ ts->v2_1 = ts->v3_1;
+ ts->v3_1 = (ts->fac1 * ts->v2_1 >> 15) - ts->v1_1;
+
+ ts->v1_2 = ts->v2_2;
+ ts->v2_2 = ts->v3_2;
+ ts->v3_2 = (ts->fac2 * ts->v2_2 >> 15) - ts->v1_2;
+ if (ts->modulate) {
+ int p;
+ p = ts->v3_2 - 32768;
+ if (p < 0) p = -p;
+ p = ((p * 9) / 10) + 1;
+ ts->data[x] = (ts->v3_1 * p) >> 15;
+ } else
+ ts->data[x] = ts->v3_1 + ts->v3_2;
+ }
+ ts->f.frametype = AST_FRAME_VOICE;
+ ts->f.subclass = AST_FORMAT_SLINEAR;
+ ts->f.datalen = len;
+ ts->f.samples = samples;
+ ts->f.offset = AST_FRIENDLY_OFFSET;
+ ts->f.data = ts->data;
+ ast_write(chan, &ts->f);
+ ts->pos += x;
+ if (ts->duration > 0) {
+ if (ts->pos >= ts->duration * 8)
+ return -1;
+ }
+ return 0;
+}
+
+static struct ast_generator tonepair = {
+ alloc: tonepair_alloc,
+ release: tonepair_release,
+ generate: tonepair_generator,
+};
+
+int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
+{
+ struct tonepair_def d = { 0, };
+
+ d.freq1 = freq1;
+ d.freq2 = freq2;
+ d.duration = duration;
+ d.vol = (vol < 1) ? 8192 : vol; /* force invalid to 8192 */
+ if (ast_activate_generator(chan, &tonepair, &d))
+ return -1;
+ return 0;
+}
+
+void ast_tonepair_stop(struct ast_channel *chan)
+{
+ ast_deactivate_generator(chan);
+}
+
+int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
+{
+ int res;
+
+ if ((res = ast_tonepair_start(chan, freq1, freq2, duration, vol)))
+ return res;
+
+ /* Give us some wiggle room */
+ while (chan->generatordata && ast_waitfor(chan, 100) >= 0) {
+ struct ast_frame *f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+ else
+ return -1;
+ }
+ return 0;
+}
+
+ast_group_t ast_get_group(const char *s)
+{
+ char *piece;
+ char *c;
+ int start=0, finish=0, x;
+ ast_group_t group = 0;
+
+ if (ast_strlen_zero(s))
+ return 0;
+
+ c = ast_strdupa(s);
+
+ while ((piece = strsep(&c, ","))) {
+ if (sscanf(piece, "%d-%d", &start, &finish) == 2) {
+ /* Range */
+ } else if (sscanf(piece, "%d", &start)) {
+ /* Just one */
+ finish = start;
+ } else {
+ ast_log(LOG_ERROR, "Syntax error parsing group configuration '%s' at '%s'. Ignoring.\n", s, piece);
+ continue;
+ }
+ for (x = start; x <= finish; x++) {
+ if ((x > 63) || (x < 0)) {
+ ast_log(LOG_WARNING, "Ignoring invalid group %d (maximum group is 63)\n", x);
+ } else
+ group |= ((ast_group_t) 1 << x);
+ }
+ }
+ return group;
+}
+
+static int (*ast_moh_start_ptr)(struct ast_channel *, const char *, const char *) = NULL;
+static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL;
+static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL;
+
+void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, const char *, const char *),
+ void (*stop_ptr)(struct ast_channel *),
+ void (*cleanup_ptr)(struct ast_channel *))
+{
+ ast_moh_start_ptr = start_ptr;
+ ast_moh_stop_ptr = stop_ptr;
+ ast_moh_cleanup_ptr = cleanup_ptr;
+}
+
+void ast_uninstall_music_functions(void)
+{
+ ast_moh_start_ptr = NULL;
+ ast_moh_stop_ptr = NULL;
+ ast_moh_cleanup_ptr = NULL;
+}
+
+/*! \brief Turn on music on hold on a given channel */
+int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
+{
+ if (ast_moh_start_ptr)
+ return ast_moh_start_ptr(chan, mclass, interpclass);
+
+ ast_verb(3, "Music class %s requested but no musiconhold loaded.\n", mclass ? mclass : (interpclass ? interpclass : "default"));
+
+ return 0;
+}
+
+/*! \brief Turn off music on hold on a given channel */
+void ast_moh_stop(struct ast_channel *chan)
+{
+ if (ast_moh_stop_ptr)
+ ast_moh_stop_ptr(chan);
+}
+
+void ast_moh_cleanup(struct ast_channel *chan)
+{
+ if (ast_moh_cleanup_ptr)
+ ast_moh_cleanup_ptr(chan);
+}
+
+void ast_channels_init(void)
+{
+ ast_cli_register_multiple(cli_channel, sizeof(cli_channel) / sizeof(struct ast_cli_entry));
+}
+
+/*! \brief Print call group and pickup group ---*/
+char *ast_print_group(char *buf, int buflen, ast_group_t group)
+{
+ unsigned int i;
+ int first=1;
+ char num[3];
+
+ buf[0] = '\0';
+
+ if (!group) /* Return empty string if no group */
+ return buf;
+
+ for (i = 0; i <= 63; i++) { /* Max group is 63 */
+ if (group & ((ast_group_t) 1 << i)) {
+ if (!first) {
+ strncat(buf, ", ", buflen);
+ } else {
+ first=0;
+ }
+ snprintf(num, sizeof(num), "%u", i);
+ strncat(buf, num, buflen);
+ }
+ }
+ return buf;
+}
+
+void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
+{
+ struct ast_variable *cur;
+
+ for (cur = vars; cur; cur = cur->next)
+ pbx_builtin_setvar_helper(chan, cur->name, cur->value);
+}
+
+static void *silence_generator_alloc(struct ast_channel *chan, void *data)
+{
+ /* just store the data pointer in the channel structure */
+ return data;
+}
+
+static void silence_generator_release(struct ast_channel *chan, void *data)
+{
+ /* nothing to do */
+}
+
+static int silence_generator_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ short buf[samples];
+ struct ast_frame frame = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = buf,
+ .samples = samples,
+ .datalen = sizeof(buf),
+ };
+ memset(buf, 0, sizeof(buf));
+ if (ast_write(chan, &frame))
+ return -1;
+ return 0;
+}
+
+static struct ast_generator silence_generator = {
+ .alloc = silence_generator_alloc,
+ .release = silence_generator_release,
+ .generate = silence_generator_generate,
+};
+
+struct ast_silence_generator {
+ int old_write_format;
+};
+
+struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan)
+{
+ struct ast_silence_generator *state;
+
+ if (!(state = ast_calloc(1, sizeof(*state)))) {
+ return NULL;
+ }
+
+ state->old_write_format = chan->writeformat;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_ERROR, "Could not set write format to SLINEAR\n");
+ ast_free(state);
+ return NULL;
+ }
+
+ ast_activate_generator(chan, &silence_generator, state);
+
+ ast_debug(1, "Started silence generator on '%s'\n", chan->name);
+
+ return state;
+}
+
+void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state)
+{
+ if (!state)
+ return;
+
+ ast_deactivate_generator(chan);
+
+ ast_debug(1, "Stopped silence generator on '%s'\n", chan->name);
+
+ if (ast_set_write_format(chan, state->old_write_format) < 0)
+ ast_log(LOG_ERROR, "Could not return write format to its original state\n");
+
+ ast_free(state);
+}
+
+
+/*! \ brief Convert channel reloadreason (ENUM) to text string for manager event */
+const char *channelreloadreason2txt(enum channelreloadreason reason)
+{
+ switch (reason) {
+ case CHANNEL_MODULE_LOAD:
+ return "LOAD (Channel module load)";
+
+ case CHANNEL_MODULE_RELOAD:
+ return "RELOAD (Channel module reload)";
+
+ case CHANNEL_CLI_RELOAD:
+ return "CLIRELOAD (Channel module reload by CLI command)";
+
+ default:
+ return "MANAGERRELOAD (Channel module reload by manager)";
+ }
+};
+
+#ifdef DEBUG_CHANNEL_LOCKS
+
+/*! \brief Unlock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function
+*/
+int ast_channel_unlock(struct ast_channel *chan)
+{
+ int res = 0;
+ ast_debug(3, "::::==== Unlocking AST channel %s\n", chan->name);
+
+ if (!chan) {
+ ast_debug(1, "::::==== Unlocking non-existing channel \n");
+ return 0;
+ }
+
+ res = ast_mutex_unlock(&chan->lock_dont_use);
+
+ if (option_debug > 2) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock_dont_use.reentrancy))
+ ast_debug(3, ":::=== Still have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_debug(3, "::::==== Channel %s was unlocked\n", chan->name);
+ if (res == EINVAL) {
+ ast_debug(3, "::::==== Channel %s had no lock by this thread. Failed unlocking\n", chan->name);
+ }
+ }
+ if (res == EPERM) {
+ /* We had no lock, so okay any way*/
+ ast_debug(4, "::::==== Channel %s was not locked at all \n", chan->name);
+ res = 0;
+ }
+ return res;
+}
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_lock(struct ast_channel *chan)
+{
+ int res;
+
+ ast_debug(4, "====:::: Locking AST channel %s\n", chan->name);
+
+ res = ast_mutex_lock(&chan->lock_dont_use);
+
+ if (option_debug > 3) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock_dont_use.reentrancy))
+ ast_debug(4, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_debug(4, "::::==== Channel %s was locked\n", chan->name);
+ if (res == EDEADLK) {
+ /* We had no lock, so okey any way */
+ ast_debug(4, "::::==== Channel %s was not locked by us. Lock would cause deadlock.\n", chan->name);
+ }
+ if (res == EINVAL) {
+ ast_debug(4, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
+ }
+ }
+ return res;
+}
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_trylock(struct ast_channel *chan)
+{
+ int res;
+
+ ast_debug(3, "====:::: Trying to lock AST channel %s\n", chan->name);
+
+ res = ast_mutex_trylock(&chan->lock_dont_use);
+
+ if (option_debug > 2) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock_dont_use.reentrancy))
+ ast_debug(3, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_debug(3, "::::==== Channel %s was locked\n", chan->name);
+ if (res == EBUSY) {
+ /* We failed to lock */
+ ast_debug(3, "::::==== Channel %s failed to lock. Not waiting around...\n", chan->name);
+ }
+ if (res == EDEADLK) {
+ /* We had no lock, so okey any way*/
+ ast_debug(3, "::::==== Channel %s was not locked. Lock would cause deadlock.\n", chan->name);
+ }
+ if (res == EINVAL)
+ ast_debug(3, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
+ }
+ return res;
+}
+
+#endif
+
+/*
+ * Wrappers for various ast_say_*() functions that call the full version
+ * of the same functions.
+ * The proper place would be say.c, but that file is optional and one
+ * must be able to build asterisk even without it (using a loadable 'say'
+ * implementation that only supplies the 'full' version of the functions.
+ */
+
+int ast_say_number(struct ast_channel *chan, int num,
+ const char *ints, const char *language, const char *options)
+{
+ return ast_say_number_full(chan, num, ints, language, options, -1, -1);
+}
+
+int ast_say_enumeration(struct ast_channel *chan, int num,
+ const char *ints, const char *language, const char *options)
+{
+ return ast_say_enumeration_full(chan, num, ints, language, options, -1, -1);
+}
+
+int ast_say_digits(struct ast_channel *chan, int num,
+ const char *ints, const char *lang)
+{
+ return ast_say_digits_full(chan, num, ints, lang, -1, -1);
+}
+
+int ast_say_digit_str(struct ast_channel *chan, const char *str,
+ const char *ints, const char *lang)
+{
+ return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);
+}
+
+int ast_say_character_str(struct ast_channel *chan, const char *str,
+ const char *ints, const char *lang)
+{
+ return ast_say_character_str_full(chan, str, ints, lang, -1, -1);
+}
+
+int ast_say_phonetic_str(struct ast_channel *chan, const char *str,
+ const char *ints, const char *lang)
+{
+ return ast_say_phonetic_str_full(chan, str, ints, lang, -1, -1);
+}
+
+int ast_say_digits_full(struct ast_channel *chan, int num,
+ const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%d", num);
+ return ast_say_digit_str_full(chan, buf, ints, lang, audiofd, ctrlfd);
+}
diff --git a/trunk/main/chanvars.c b/trunk/main/chanvars.c
new file mode 100644
index 000000000..14a89f767
--- /dev/null
+++ b/trunk/main/chanvars.c
@@ -0,0 +1,82 @@
+/*
+ * 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 Channel Variables
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/chanvars.h"
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+
+struct ast_var_t *ast_var_assign(const char *name, const char *value)
+{
+ struct ast_var_t *var;
+ int name_len = strlen(name) + 1;
+ int value_len = strlen(value) + 1;
+
+ if (!(var = ast_calloc(sizeof(*var) + name_len + value_len, sizeof(char)))) {
+ return NULL;
+ }
+
+ ast_copy_string(var->name, name, name_len);
+ var->value = var->name + name_len;
+ ast_copy_string(var->value, value, value_len);
+
+ return var;
+}
+
+void ast_var_delete(struct ast_var_t *var)
+{
+ if (var)
+ ast_free(var);
+}
+
+const char *ast_var_name(const struct ast_var_t *var)
+{
+ const char *name;
+
+ if (var == NULL || (name = var->name) == NULL)
+ return NULL;
+ /* Return the name without the initial underscores */
+ if (name[0] == '_') {
+ name++;
+ if (name[0] == '_')
+ name++;
+ }
+ return name;
+}
+
+const char *ast_var_full_name(const struct ast_var_t *var)
+{
+ return (var ? var->name : NULL);
+}
+
+const char *ast_var_value(const struct ast_var_t *var)
+{
+ return (var ? var->value : NULL);
+}
+
+
diff --git a/trunk/main/cli.c b/trunk/main/cli.c
new file mode 100644
index 000000000..8813c6a3f
--- /dev/null
+++ b/trunk/main/cli.c
@@ -0,0 +1,1918 @@
+/*
+ * 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 Standard Command Line Interface
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
+#include <sys/signal.h>
+#include <signal.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/lock.h"
+#include "editline/readline/readline.h"
+#include "asterisk/threadstorage.h"
+
+/*!
+ * \brief map a debug or verbose value to a filename
+ */
+struct ast_debug_file {
+ unsigned int level;
+ AST_RWLIST_ENTRY(ast_debug_file) entry;
+ char filename[0];
+};
+
+AST_RWLIST_HEAD(debug_file_list, ast_debug_file);
+
+/*! list of filenames and their debug settings */
+static struct debug_file_list debug_files;
+/*! list of filenames and their verbose settings */
+static struct debug_file_list verbose_files;
+
+AST_THREADSTORAGE(ast_cli_buf);
+
+/*! \brief Initial buffer size for resulting strings in ast_cli() */
+#define AST_CLI_INITLEN 256
+
+void ast_cli(int fd, const char *fmt, ...)
+{
+ int res;
+ struct ast_str *buf;
+ va_list ap;
+
+ if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
+ return;
+
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ if (res != AST_DYNSTR_BUILD_FAILED)
+ ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
+}
+
+unsigned int ast_debug_get_by_file(const char *file)
+{
+ struct ast_debug_file *adf;
+ unsigned int res = 0;
+
+ AST_RWLIST_RDLOCK(&debug_files);
+ AST_LIST_TRAVERSE(&debug_files, adf, entry) {
+ if (!strncasecmp(adf->filename, file, strlen(adf->filename))) {
+ res = adf->level;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&debug_files);
+
+ return res;
+}
+
+unsigned int ast_verbose_get_by_file(const char *file)
+{
+ struct ast_debug_file *adf;
+ unsigned int res = 0;
+
+ AST_RWLIST_RDLOCK(&verbose_files);
+ AST_LIST_TRAVERSE(&verbose_files, adf, entry) {
+ if (!strncasecmp(adf->filename, file, strlen(file))) {
+ res = adf->level;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&verbose_files);
+
+ return res;
+}
+
+static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
+
+static char *complete_fn(const char *word, int state)
+{
+ char *c, *d;
+ char filename[256];
+
+ if (word[0] == '/')
+ ast_copy_string(filename, word, sizeof(filename));
+ else
+ snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
+
+ c = d = filename_completion_function(filename, state);
+
+ if (c && word[0] != '/')
+ c += (strlen(ast_config_AST_MODULE_DIR) + 1);
+ if (c)
+ c = ast_strdup(c);
+ if (d)
+ free(d);
+
+ return c;
+}
+
+static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ /* "module load <mod>" */
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module load";
+ e->usage =
+ "Usage: module load <module name>\n"
+ " Loads the specified module into Asterisk.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ if (a->pos != e->args)
+ return NULL;
+ return complete_fn(a->word, a->n);
+ }
+ if (a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+ if (ast_load_resource(a->argv[e->args])) {
+ ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = handle_load(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "load";
+ return res;
+}
+
+static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int x;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module reload";
+ e->usage =
+ "Usage: module reload [module ...]\n"
+ " Reloads configuration files for all listed modules which support\n"
+ " reloading, or for all supported modules if none are listed.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
+ }
+ if (a->argc == e->args) {
+ ast_module_reload(NULL);
+ return CLI_SUCCESS;
+ }
+ for (x = e->args; x < a->argc; x++) {
+ int res = ast_module_reload(a->argv[x]);
+ /* XXX reload has multiple error returns, including -1 on error and 2 on success */
+ switch (res) {
+ case 0:
+ ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
+ break;
+ case 1:
+ ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
+ break;
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *s = handle_reload(e, cmd, a);
+ if (cmd == CLI_INIT) /* override command name */
+ e->command = "reload";
+ return s;
+}
+
+/*!
+ * \brief Find the debug or verbose file setting
+ * \arg debug 1 for debug, 0 for verbose
+ */
+static struct ast_debug_file *find_debug_file(const char *fn, unsigned int debug)
+{
+ struct ast_debug_file *df = NULL;
+ struct debug_file_list *dfl = debug ? &debug_files : &verbose_files;
+
+ AST_LIST_TRAVERSE(dfl, df, entry) {
+ if (!strcasecmp(df->filename, fn))
+ break;
+ }
+
+ return df;
+}
+
+static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval;
+ int newlevel;
+ int atleast = 0;
+ int fd = a->fd;
+ int argc = a->argc;
+ char **argv = a->argv;
+ int *dst;
+ char *what;
+ struct debug_file_list *dfl;
+ struct ast_debug_file *adf;
+ char *fn;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set {debug|verbose} [off|atleast]";
+ e->usage =
+ "Usage: core set {debug|verbose} [atleast] <level> [filename]\n"
+ " core set {debug|verbose} off\n"
+ " Sets level of debug or verbose messages to be displayed or \n"
+ " sets a filename to display debug messages from.\n"
+ " 0 or off means no messages should be displayed.\n"
+ " Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ /* all the above return, so we proceed with the handler.
+ * we are guaranteed to be called with argc >= e->args;
+ */
+
+ if (argc < e->args)
+ return CLI_SHOWUSAGE;
+ if (!strcasecmp(argv[e->args - 2], "debug")) {
+ dst = &option_debug;
+ oldval = option_debug;
+ what = "Core debug";
+ } else {
+ dst = &option_verbose;
+ oldval = option_verbose;
+ what = "Verbosity";
+ }
+ if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
+ unsigned int debug = (*what == 'C');
+ newlevel = 0;
+
+ dfl = debug ? &debug_files : &verbose_files;
+
+ AST_RWLIST_WRLOCK(dfl);
+ while ((adf = AST_RWLIST_REMOVE_HEAD(dfl, entry)))
+ ast_free(adf);
+ ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
+ AST_RWLIST_UNLOCK(dfl);
+
+ goto done;
+ }
+ if (!strcasecmp(argv[e->args-1], "atleast"))
+ atleast = 1;
+ if (argc != e->args + atleast && argc != e->args + atleast + 1)
+ return CLI_SHOWUSAGE;
+ if (sscanf(argv[e->args + atleast - 1], "%d", &newlevel) != 1)
+ return CLI_SHOWUSAGE;
+ if (argc == e->args + atleast + 1) {
+ unsigned int debug = (*what == 'C');
+ dfl = debug ? &debug_files : &verbose_files;
+
+ fn = argv[e->args + atleast];
+
+ AST_RWLIST_WRLOCK(dfl);
+
+ if ((adf = find_debug_file(fn, debug)) && !newlevel) {
+ AST_RWLIST_REMOVE(dfl, adf, entry);
+ if (AST_RWLIST_EMPTY(dfl))
+ ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
+ AST_RWLIST_UNLOCK(dfl);
+ ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, adf->level, fn);
+ ast_free(adf);
+ return CLI_SUCCESS;
+ }
+
+ if (adf) {
+ if ((atleast && newlevel < adf->level) || adf->level == newlevel) {
+ ast_cli(fd, "%s is %d for '%s'\n", what, adf->level, fn);
+ AST_RWLIST_UNLOCK(dfl);
+ return CLI_SUCCESS;
+ }
+ } else if (!(adf = ast_calloc(1, sizeof(*adf) + strlen(fn) + 1))) {
+ AST_RWLIST_UNLOCK(dfl);
+ return CLI_FAILURE;
+ }
+
+ oldval = adf->level;
+ adf->level = newlevel;
+ strcpy(adf->filename, fn);
+
+ ast_set_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
+
+ AST_RWLIST_INSERT_TAIL(dfl, adf, entry);
+ AST_RWLIST_UNLOCK(dfl);
+
+ ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, adf->level, adf->filename);
+
+ return CLI_SUCCESS;
+ }
+
+done:
+ if (!atleast || newlevel > *dst)
+ *dst = newlevel;
+ if (oldval > 0 && *dst == 0)
+ ast_cli(fd, "%s is now OFF\n", what);
+ else if (*dst > 0) {
+ if (oldval == *dst)
+ ast_cli(fd, "%s is at least %d\n", what, *dst);
+ else
+ ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger mute";
+ e->usage =
+ "Usage: logger mute\n"
+ " Disables logging output to the current console, making it possible to\n"
+ " gather information without being disturbed by scrolling lines.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
+ ast_console_toggle_mute(a->fd, 1);
+ else
+ ast_console_toggle_mute(a->fd, 0);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ /* "module unload mod_1 [mod_2 .. mod_N]" */
+ int x;
+ int force = AST_FORCE_SOFT;
+ char *s;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module unload";
+ e->usage =
+ "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
+ " Unloads the specified module from Asterisk. The -f\n"
+ " option causes the module to be unloaded even if it is\n"
+ " in use (may cause a crash) and the -h module causes the\n"
+ " module to be unloaded even if the module says it cannot, \n"
+ " which almost always will cause a crash.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
+ }
+ if (a->argc < e->args + 1)
+ return CLI_SHOWUSAGE;
+ x = e->args; /* first argument */
+ s = a->argv[x];
+ if (s[0] == '-') {
+ if (s[1] == 'f')
+ force = AST_FORCE_FIRM;
+ else if (s[1] == 'h')
+ force = AST_FORCE_HARD;
+ else
+ return CLI_SHOWUSAGE;
+ if (a->argc < e->args + 2) /* need at least one module name */
+ return CLI_SHOWUSAGE;
+ x++; /* skip this argument */
+ }
+
+ for (; x < a->argc; x++) {
+ if (ast_unload_resource(a->argv[x], force)) {
+ ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
+ return CLI_FAILURE;
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = handle_unload(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "unload"; /* XXX override */
+ return res;
+}
+
+#define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
+#define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
+
+AST_MUTEX_DEFINE_STATIC(climodentrylock);
+static int climodentryfd = -1;
+
+static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
+{
+ /* Comparing the like with the module */
+ if (strcasestr(module, like) ) {
+ ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
+ return 1;
+ }
+ return 0;
+}
+
+static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
+{
+ int x; /* the main part - years, weeks, etc. */
+ struct ast_str *out;
+
+#define SECOND (1)
+#define MINUTE (SECOND*60)
+#define HOUR (MINUTE*60)
+#define DAY (HOUR*24)
+#define WEEK (DAY*7)
+#define YEAR (DAY*365)
+#define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
+ if (timeval.tv_sec < 0) /* invalid, nothing to show */
+ return;
+
+ if (printsec) { /* plain seconds output */
+ ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
+ return;
+ }
+ out = ast_str_alloca(256);
+ if (timeval.tv_sec > YEAR) {
+ x = (timeval.tv_sec / YEAR);
+ timeval.tv_sec -= (x * YEAR);
+ ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > WEEK) {
+ x = (timeval.tv_sec / WEEK);
+ timeval.tv_sec -= (x * WEEK);
+ ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > DAY) {
+ x = (timeval.tv_sec / DAY);
+ timeval.tv_sec -= (x * DAY);
+ ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > HOUR) {
+ x = (timeval.tv_sec / HOUR);
+ timeval.tv_sec -= (x * HOUR);
+ ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > MINUTE) {
+ x = (timeval.tv_sec / MINUTE);
+ timeval.tv_sec -= (x * MINUTE);
+ ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ x = timeval.tv_sec;
+ if (x > 0 || out->used == 0) /* if there is nothing, print 0 seconds */
+ ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
+ ast_cli(fd, "%s: %s\n", prefix, out->str);
+}
+
+static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct timeval curtime = ast_tvnow();
+ int printsec;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show uptime [seconds]";
+ e->usage =
+ "Usage: core show uptime [seconds]\n"
+ " Shows Asterisk uptime information.\n"
+ " The seconds word returns the uptime in seconds only.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ /* regular handler */
+ if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
+ printsec = 1;
+ else if (a->argc == e->args-1)
+ printsec = 0;
+ else
+ return CLI_SHOWUSAGE;
+ if (ast_startuptime.tv_sec)
+ print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
+ if (ast_lastreloadtime.tv_sec)
+ print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload", printsec);
+ return CLI_SUCCESS;
+}
+
+static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *like;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module show [like]";
+ e->usage =
+ "Usage: module show [like keyword]\n"
+ " Shows Asterisk modules currently in use, and usage statistics.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ if (a->pos == e->args)
+ return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
+ else
+ return NULL;
+ }
+ /* all the above return, so we proceed with the handler.
+ * we are guaranteed to have argc >= e->args
+ */
+ if (a->argc == e->args - 1)
+ like = "";
+ else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
+ like = a->argv[e->args];
+ else
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&climodentrylock);
+ climodentryfd = a->fd; /* global, protected by climodentrylock */
+ ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
+ ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
+ climodentryfd = -1;
+ ast_mutex_unlock(&climodentrylock);
+ return CLI_SUCCESS;
+}
+#undef MODLIST_FORMAT
+#undef MODLIST_FORMAT2
+
+static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct timeval curtime = ast_tvnow();
+ int showuptime, printsec;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show calls [uptime]";
+ e->usage =
+ "Usage: core show calls [uptime] [seconds]\n"
+ " Lists number of currently active calls and total number of calls\n"
+ " processed through PBX since last restart. If 'uptime' is specified\n"
+ " the system uptime is also displayed. If 'seconds' is specified in\n"
+ " addition to 'uptime', the system uptime is displayed in seconds.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ if (a->pos != e->args)
+ return NULL;
+ return a->n == 0 ? ast_strdup("seconds") : NULL;
+ }
+
+ /* regular handler */
+ if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
+ showuptime = 1;
+
+ if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
+ printsec = 1;
+ else if (a->argc == e->args)
+ printsec = 0;
+ else
+ return CLI_SHOWUSAGE;
+ } else if (a->argc == e->args-1) {
+ showuptime = 0;
+ printsec = 0;
+ } else
+ return CLI_SHOWUSAGE;
+
+ if (option_maxcalls) {
+ ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
+ ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
+ ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
+ } else {
+ ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
+ }
+
+ ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
+
+ if (ast_startuptime.tv_sec && showuptime) {
+ print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
+#define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
+#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
+#define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
+#define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
+
+ struct ast_channel *c = NULL;
+ int numchans = 0, concise = 0, verbose = 0, count = 0;
+ int fd, argc;
+ char **argv;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channels [concise|verbose|count]";
+ e->usage =
+ "Usage: core show channels [concise|verbose|count]\n"
+ " Lists currently defined channels and some information about them. If\n"
+ " 'concise' is specified, the format is abridged and in a more easily\n"
+ " machine parsable format. If 'verbose' is specified, the output includes\n"
+ " more and longer fields. If 'count' is specified only the channel and call\n"
+ " count is output.\n"
+ " The 'concise' option is deprecated and will be removed from future versions\n"
+ " of Asterisk.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ fd = a->fd;
+ argc = a->argc;
+ argv = a->argv;
+
+ if (a->argc == e->args) {
+ if (!strcasecmp(argv[e->args-1],"concise"))
+ concise = 1;
+ else if (!strcasecmp(argv[e->args-1],"verbose"))
+ verbose = 1;
+ else if (!strcasecmp(argv[e->args-1],"count"))
+ count = 1;
+ else
+ return CLI_SHOWUSAGE;
+ } else if (a->argc != e->args - 1)
+ return CLI_SHOWUSAGE;
+
+ if (!count) {
+ if (!concise && !verbose)
+ ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
+ else if (verbose)
+ ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
+ "CallerID", "Duration", "Accountcode", "BridgedTo");
+ }
+
+ while ((c = ast_channel_walk_locked(c)) != NULL) {
+ struct ast_channel *bc = ast_bridged_channel(c);
+ char durbuf[10] = "-";
+
+ if (!count) {
+ if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
+ int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
+ if (verbose) {
+ int durh = duration / 3600;
+ int durm = (duration % 3600) / 60;
+ int durs = duration % 60;
+ snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
+ } else {
+ snprintf(durbuf, sizeof(durbuf), "%d", duration);
+ }
+ }
+ if (concise) {
+ ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
+ c->appl ? c->appl : "(None)",
+ S_OR(c->data, ""), /* XXX different from verbose ? */
+ S_OR(c->cid.cid_num, ""),
+ S_OR(c->accountcode, ""),
+ c->amaflags,
+ durbuf,
+ bc ? bc->name : "(None)",
+ c->uniqueid);
+ } else if (verbose) {
+ ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
+ c->appl ? c->appl : "(None)",
+ c->data ? S_OR(c->data, "(Empty)" ): "(None)",
+ S_OR(c->cid.cid_num, ""),
+ durbuf,
+ S_OR(c->accountcode, ""),
+ bc ? bc->name : "(None)");
+ } else {
+ char locbuf[40] = "(None)";
+ char appdata[40] = "(None)";
+
+ if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten))
+ snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
+ if (c->appl)
+ snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, ""));
+ ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
+ }
+ }
+ numchans++;
+ ast_channel_unlock(c);
+ }
+ if (!concise) {
+ ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
+ if (option_maxcalls)
+ ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
+ ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
+ ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
+ else
+ ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
+
+ ast_cli(fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
+ }
+ return CLI_SUCCESS;
+
+#undef FORMAT_STRING
+#undef FORMAT_STRING2
+#undef CONCISE_FORMAT_STRING
+#undef VERBOSE_FORMAT_STRING
+#undef VERBOSE_FORMAT_STRING2
+}
+
+static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *c=NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "soft hangup";
+ e->usage =
+ "Usage: soft hangup <channel>\n"
+ " Request that a channel be hung up. The hangup takes effect\n"
+ " the next time the driver reads or writes from the channel\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ c = ast_get_channel_by_name_locked(a->argv[2]);
+ if (c) {
+ ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(c);
+ } else
+ ast_cli(a->fd, "%s is not a known channel\n", a->argv[2]);
+ return CLI_SUCCESS;
+}
+
+static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
+
+static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *buf, *obuf;
+ int buflen = 2048;
+ int len = 0;
+ char **matches;
+ int x, matchlen;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "_command matchesarray";
+ e->usage =
+ "Usage: _command matchesarray \"<line>\" text \n"
+ " This function is used internally to help with command completion and should.\n"
+ " never be called by the user directly.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ if (!(buf = ast_malloc(buflen)))
+ return CLI_FAILURE;
+ buf[len] = '\0';
+ matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
+ if (matches) {
+ for (x=0; matches[x]; x++) {
+ matchlen = strlen(matches[x]) + 1;
+ if (len + matchlen >= buflen) {
+ buflen += matchlen * 3;
+ obuf = buf;
+ if (!(buf = ast_realloc(obuf, buflen)))
+ /* Memory allocation failure... Just free old buffer and be done */
+ ast_free(obuf);
+ }
+ if (buf)
+ len += sprintf( buf + len, "%s ", matches[x]);
+ ast_free(matches[x]);
+ matches[x] = NULL;
+ }
+ ast_free(matches);
+ }
+
+ if (buf) {
+ ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
+ ast_free(buf);
+ } else
+ ast_cli(a->fd, "NULL\n");
+
+ return CLI_SUCCESS;
+}
+
+
+
+static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int matches = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "_command nummatches";
+ e->usage =
+ "Usage: _command nummatches \"<line>\" text \n"
+ " This function is used internally to help with command completion and should.\n"
+ " never be called by the user directly.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
+
+ ast_cli(a->fd, "%d", matches);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *buf;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "_command complete";
+ e->usage =
+ "Usage: _command complete \"<line>\" text state\n"
+ " This function is used internally to help with command completion and should.\n"
+ " never be called by the user directly.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+ buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
+ if (buf) {
+ ast_cli(a->fd, buf);
+ ast_free(buf);
+ } else
+ ast_cli(a->fd, "NULL\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *c = NULL;
+ int is_all, is_off = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set debug channel";
+ e->usage =
+ "Usage: core set debug channel <all|channel> [off]\n"
+ " Enables/disables debugging on all or on a specific channel.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ /* XXX remember to handle the optional "off" */
+ if (a->pos != e->args)
+ return NULL;
+ return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
+ }
+ /* 'core set debug channel {all|chan_id}' */
+ if (a->argc == e->args + 2) {
+ if (!strcasecmp(a->argv[e->args + 1], "off"))
+ is_off = 1;
+ else
+ return CLI_SHOWUSAGE;
+ } else if (a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+
+ is_all = !strcasecmp("all", a->argv[e->args]);
+ if (is_all) {
+ if (is_off) {
+ global_fin &= ~DEBUGCHAN_FLAG;
+ global_fout &= ~DEBUGCHAN_FLAG;
+ } else {
+ global_fin |= DEBUGCHAN_FLAG;
+ global_fout |= DEBUGCHAN_FLAG;
+ }
+ c = ast_channel_walk_locked(NULL);
+ } else {
+ c = ast_get_channel_by_name_locked(a->argv[e->args]);
+ if (c == NULL)
+ ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
+ }
+ while (c) {
+ if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
+ if (is_off) {
+ c->fin &= ~DEBUGCHAN_FLAG;
+ c->fout &= ~DEBUGCHAN_FLAG;
+ } else {
+ c->fin |= DEBUGCHAN_FLAG;
+ c->fout |= DEBUGCHAN_FLAG;
+ }
+ ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
+ }
+ ast_channel_unlock(c);
+ if (!is_all)
+ break;
+ c = ast_channel_walk_locked(c);
+ }
+ ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
+ return CLI_SUCCESS;
+}
+
+static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res;
+
+ if (cmd == CLI_HANDLER && a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+ res = handle_core_set_debug_channel(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "debug channel";
+ return res;
+}
+
+static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res;
+ if (cmd == CLI_HANDLER) {
+ if (a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+ /* pretend we have an extra "off" at the end. We can do this as the array
+ * is NULL terminated so we overwrite that entry.
+ */
+ a->argv[e->args+1] = "off";
+ a->argc++;
+ }
+ res = handle_core_set_debug_channel(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "no debug channel";
+ return res;
+}
+
+static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *c=NULL;
+ struct timeval now;
+ struct ast_str *out = ast_str_alloca(2048);
+ char cdrtime[256];
+ char nf[256], wf[256], rf[256];
+ long elapsed_seconds=0;
+ int hour=0, min=0, sec=0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channel";
+ e->usage =
+ "Usage: core show channel <channel>\n"
+ " Shows lots of information about the specified channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ now = ast_tvnow();
+ c = ast_get_channel_by_name_locked(a->argv[3]);
+ if (!c) {
+ ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
+ return CLI_SUCCESS;
+ }
+ if (c->cdr) {
+ elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
+ hour = elapsed_seconds / 3600;
+ min = (elapsed_seconds % 3600) / 60;
+ sec = elapsed_seconds % 60;
+ snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
+ } else
+ strcpy(cdrtime, "N/A");
+ ast_cli(a->fd,
+ " -- General --\n"
+ " Name: %s\n"
+ " Type: %s\n"
+ " UniqueID: %s\n"
+ " Caller ID: %s\n"
+ " Caller ID Name: %s\n"
+ " DNID Digits: %s\n"
+ " Language: %s\n"
+ " State: %s (%d)\n"
+ " Rings: %d\n"
+ " NativeFormats: %s\n"
+ " WriteFormat: %s\n"
+ " ReadFormat: %s\n"
+ " WriteTranscode: %s\n"
+ " ReadTranscode: %s\n"
+ "1st File Descriptor: %d\n"
+ " Frames in: %d%s\n"
+ " Frames out: %d%s\n"
+ " Time to Hangup: %ld\n"
+ " Elapsed Time: %s\n"
+ " Direct Bridge: %s\n"
+ "Indirect Bridge: %s\n"
+ " -- PBX --\n"
+ " Context: %s\n"
+ " Extension: %s\n"
+ " Priority: %d\n"
+ " Call Group: %llu\n"
+ " Pickup Group: %llu\n"
+ " Application: %s\n"
+ " Data: %s\n"
+ " Blocking in: %s\n",
+ c->name, c->tech->type, c->uniqueid,
+ S_OR(c->cid.cid_num, "(N/A)"),
+ S_OR(c->cid.cid_name, "(N/A)"),
+ S_OR(c->cid.cid_dnid, "(N/A)"),
+ c->language,
+ ast_state2str(c->_state), c->_state, c->rings,
+ ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats),
+ ast_getformatname_multiple(wf, sizeof(wf), c->writeformat),
+ ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
+ c->writetrans ? "Yes" : "No",
+ c->readtrans ? "Yes" : "No",
+ c->fds[0],
+ c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
+ c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
+ (long)c->whentohangup,
+ cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>",
+ c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
+ ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
+ (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
+
+ if (pbx_builtin_serialize_variables(c, &out))
+ ast_cli(a->fd," Variables:\n%s\n", out->str);
+ if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
+ ast_cli(a->fd," CDR Variables:\n%s\n", out->str);
+
+ ast_channel_unlock(c);
+ return CLI_SUCCESS;
+}
+
+/*
+ * helper function to generate CLI matches from a fixed set of values.
+ * A NULL word is acceptable.
+ */
+char *ast_cli_complete(const char *word, char *const choices[], int state)
+{
+ int i, which = 0, len;
+ len = ast_strlen_zero(word) ? 0 : strlen(word);
+
+ for (i = 0; choices[i]; i++) {
+ if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
+ return ast_strdup(choices[i]);
+ }
+ return NULL;
+}
+
+char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
+{
+ struct ast_channel *c = NULL;
+ int which = 0;
+ int wordlen;
+ char notfound = '\0';
+ char *ret = &notfound; /* so NULL can break the loop */
+
+ if (pos != rpos)
+ return NULL;
+
+ wordlen = strlen(word);
+
+ while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
+ if (!strncasecmp(word, c->name, wordlen) && ++which > state)
+ ret = ast_strdup(c->name);
+ ast_channel_unlock(c);
+ }
+ return ret == &notfound ? NULL : ret;
+}
+
+static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT_STRING "%-25s %-20s %-20s\n"
+
+ struct ast_group_info *gi = NULL;
+ int numchans = 0;
+ regex_t regexbuf;
+ int havepattern = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "group show channels";
+ e->usage =
+ "Usage: group show channels [pattern]\n"
+ " Lists all currently active channels with channel group(s) specified.\n"
+ " Optional regular expression pattern is matched to group names for each\n"
+ " channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 4) {
+ if (regcomp(&regexbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = 1;
+ }
+
+ ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
+
+ ast_app_group_list_rdlock();
+
+ gi = ast_app_group_list_head();
+ while (gi) {
+ if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
+ ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
+ numchans++;
+ }
+ gi = AST_LIST_NEXT(gi, list);
+ }
+
+ ast_app_group_list_unlock();
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
+ return CLI_SUCCESS;
+#undef FORMAT_STRING
+}
+
+static struct ast_cli_entry cli_debug_channel_deprecated = AST_CLI_DEFINE(handle_debugchan_deprecated, "Enable debugging on channel");
+static struct ast_cli_entry cli_module_load_deprecated = AST_CLI_DEFINE(handle_load_deprecated, "Load a module");
+static struct ast_cli_entry cli_module_reload_deprecated = AST_CLI_DEFINE(handle_reload_deprecated, "reload modules by name");
+static struct ast_cli_entry cli_module_unload_deprecated = AST_CLI_DEFINE(handle_unload_deprecated, "unload modules by name");
+
+static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry cli_cli[] = {
+ /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
+ AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
+ AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
+ AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
+
+ AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
+
+ AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
+
+ AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
+
+ AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
+
+ AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
+ .deprecate_cmd = &cli_debug_channel_deprecated),
+
+ AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
+
+ AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
+
+ AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
+
+ AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
+
+ AST_CLI_DEFINE(handle_modlist, "List modules and info"),
+
+ AST_CLI_DEFINE(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated),
+
+ AST_CLI_DEFINE(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated),
+
+ AST_CLI_DEFINE(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ),
+
+ AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
+
+ AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
+};
+
+/*!
+ * Some regexp characters in cli arguments are reserved and used as separators.
+ */
+static const char cli_rsvd[] = "[]{}|*%";
+
+/*!
+ * initialize the _full_cmd string and related parameters,
+ * return 0 on success, -1 on error.
+ */
+static int set_full_cmd(struct ast_cli_entry *e)
+{
+ int i;
+ char buf[80];
+
+ ast_join(buf, sizeof(buf), e->cmda);
+ e->_full_cmd = ast_strdup(buf);
+ if (!e->_full_cmd) {
+ ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
+ return -1;
+ }
+ e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
+ for (i = 0; e->cmda[i]; i++)
+ ;
+ e->args = i;
+ return 0;
+}
+
+/*! \brief initialize the _full_cmd string in * each of the builtins. */
+void ast_builtins_init(void)
+{
+ ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
+}
+
+static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
+{
+ if (e == NULL)
+ e = AST_LIST_FIRST(&helpers);
+ if (e)
+ e = AST_LIST_NEXT(e, list);
+ return e;
+}
+
+/*!
+ * match a word in the CLI entry.
+ * returns -1 on mismatch, 0 on match of an optional word,
+ * 1 on match of a full word.
+ *
+ * The pattern can be
+ * any_word match for equal
+ * [foo|bar|baz] optionally, one of these words
+ * {foo|bar|baz} exactly, one of these words
+ * % any word
+ */
+static int word_match(const char *cmd, const char *cli_word)
+{
+ int l;
+ char *pos;
+
+ if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
+ return -1;
+ if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
+ return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
+ /* regexp match, takes [foo|bar] or {foo|bar} */
+ l = strlen(cmd);
+ /* wildcard match - will extend in the future */
+ if (l > 0 && cli_word[0] == '%') {
+ return 1; /* wildcard */
+ }
+ pos = strcasestr(cli_word, cmd);
+ if (pos == NULL) /* not found, say ok if optional */
+ return cli_word[0] == '[' ? 0 : -1;
+ if (pos == cli_word) /* no valid match at the beginning */
+ return -1;
+ if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
+ return 1; /* valid match */
+ return -1; /* not found */
+}
+
+/*! \brief if word is a valid prefix for token, returns the pos-th
+ * match as a malloced string, or NULL otherwise.
+ * Always tell in *actual how many matches we got.
+ */
+static char *is_prefix(const char *word, const char *token,
+ int pos, int *actual)
+{
+ int lw;
+ char *s, *t1;
+
+ *actual = 0;
+ if (ast_strlen_zero(token))
+ return NULL;
+ if (ast_strlen_zero(word))
+ word = ""; /* dummy */
+ lw = strlen(word);
+ if (strcspn(word, cli_rsvd) != lw)
+ return NULL; /* no match if word has reserved chars */
+ if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */
+ if (strncasecmp(token, word, lw)) /* no match */
+ return NULL;
+ *actual = 1;
+ return (pos != 0) ? NULL : ast_strdup(token);
+ }
+ /* now handle regexp match */
+
+ /* Wildcard always matches, so we never do is_prefix on them */
+
+ t1 = ast_strdupa(token + 1); /* copy, skipping first char */
+ while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
+ if (*s == '%') /* wildcard */
+ continue;
+ if (strncasecmp(s, word, lw)) /* no match */
+ continue;
+ (*actual)++;
+ if (pos-- == 0)
+ return ast_strdup(s);
+ }
+ return NULL;
+}
+
+/*!
+ * \brief locate a cli command in the 'helpers' list (which must be locked).
+ * exact has 3 values:
+ * 0 returns if the search key is equal or longer than the entry.
+ * note that trailing optional arguments are skipped.
+ * -1 true if the mismatch is on the last word XXX not true!
+ * 1 true only on complete, exact match.
+ *
+ * The search compares word by word taking care of regexps in e->cmda
+ */
+static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
+{
+ int matchlen = -1; /* length of longest match so far */
+ struct ast_cli_entry *cand = NULL, *e=NULL;
+
+ while ( (e = cli_next(e)) ) {
+ /* word-by word regexp comparison */
+ char * const *src = cmds;
+ char * const *dst = e->cmda;
+ int n = 0;
+ for (;; dst++, src += n) {
+ n = word_match(*src, *dst);
+ if (n < 0)
+ break;
+ }
+ if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
+ /* no more words in 'e' */
+ if (ast_strlen_zero(*src)) /* exact match, cannot do better */
+ break;
+ /* Here, cmds has more words than the entry 'e' */
+ if (match_type != 0) /* but we look for almost exact match... */
+ continue; /* so we skip this one. */
+ /* otherwise we like it (case 0) */
+ } else { /* still words in 'e' */
+ if (ast_strlen_zero(*src))
+ continue; /* cmds is shorter than 'e', not good */
+ /* Here we have leftover words in cmds and 'e',
+ * but there is a mismatch. We only accept this one if match_type == -1
+ * and this is the last word for both.
+ */
+ if (match_type != -1 || !ast_strlen_zero(src[1]) ||
+ !ast_strlen_zero(dst[1])) /* not the one we look for */
+ continue;
+ /* good, we are in case match_type == -1 and mismatch on last word */
+ }
+ if (src - cmds > matchlen) { /* remember the candidate */
+ matchlen = src - cmds;
+ cand = e;
+ }
+ }
+ return e ? e : cand;
+}
+
+static char *find_best(char *argv[])
+{
+ static char cmdline[80];
+ int x;
+ /* See how close we get, then print the candidate */
+ char *myargv[AST_MAX_CMD_LEN];
+ for (x=0;x<AST_MAX_CMD_LEN;x++)
+ myargv[x]=NULL;
+ AST_RWLIST_RDLOCK(&helpers);
+ for (x=0;argv[x];x++) {
+ myargv[x] = argv[x];
+ if (!find_cli(myargv, -1))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&helpers);
+ ast_join(cmdline, sizeof(cmdline), myargv);
+ return cmdline;
+}
+
+static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
+{
+ if (e->deprecate_cmd) {
+ __ast_cli_unregister(e->deprecate_cmd, e);
+ }
+ if (e->inuse) {
+ ast_log(LOG_WARNING, "Can't remove command that is in use\n");
+ } else {
+ AST_RWLIST_WRLOCK(&helpers);
+ AST_RWLIST_REMOVE(&helpers, e, list);
+ AST_RWLIST_UNLOCK(&helpers);
+ ast_free(e->_full_cmd);
+ e->_full_cmd = NULL;
+ if (e->handler) {
+ /* this is a new-style entry. Reset fields and free memory. */
+ bzero((char **)(e->cmda), sizeof(e->cmda));
+ ast_free(e->command);
+ e->command = NULL;
+ e->usage = NULL;
+ }
+ }
+ return 0;
+}
+
+static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
+{
+ struct ast_cli_entry *cur;
+ int i, lf, ret = -1;
+
+ struct ast_cli_args a; /* fake argument */
+ char **dst = (char **)e->cmda; /* need to cast as the entry is readonly */
+ char *s;
+
+ bzero (&a, sizeof(a));
+ e->handler(e, CLI_INIT, &a);
+ /* XXX check that usage and command are filled up */
+ s = ast_skip_blanks(e->command);
+ s = e->command = ast_strdup(s);
+ for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
+ *dst++ = s; /* store string */
+ s = ast_skip_nonblanks(s);
+ if (*s == '\0') /* we are done */
+ break;
+ *s++ = '\0';
+ s = ast_skip_blanks(s);
+ }
+ *dst++ = NULL;
+
+ AST_RWLIST_WRLOCK(&helpers);
+
+ if (find_cli(e->cmda, 1)) {
+ ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd);
+ goto done;
+ }
+ if (set_full_cmd(e))
+ goto done;
+ if (!ed) {
+ e->deprecated = 0;
+ } else {
+ e->deprecated = 1;
+ e->summary = ed->summary;
+ e->usage = ed->usage;
+ /* XXX If command A deprecates command B, and command B deprecates command C...
+ Do we want to show command A or command B when telling the user to use new syntax?
+ This currently would show command A.
+ To show command B, you just need to always use ed->_full_cmd.
+ */
+ e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
+ }
+
+ lf = e->cmdlen;
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
+ int len = cur->cmdlen;
+ if (lf < len)
+ len = lf;
+ if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(e, list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (!cur)
+ AST_RWLIST_INSERT_TAIL(&helpers, e, list);
+ ret = 0; /* success */
+
+done:
+ AST_RWLIST_UNLOCK(&helpers);
+
+ if (e->deprecate_cmd) {
+ /* This command deprecates another command. Register that one also. */
+ __ast_cli_register(e->deprecate_cmd, e);
+ }
+
+ return ret;
+}
+
+/* wrapper function, so we can unregister deprecated commands recursively */
+int ast_cli_unregister(struct ast_cli_entry *e)
+{
+ return __ast_cli_unregister(e, NULL);
+}
+
+/* wrapper function, so we can register deprecated commands recursively */
+int ast_cli_register(struct ast_cli_entry *e)
+{
+ return __ast_cli_register(e, NULL);
+}
+
+/*
+ * register/unregister an array of entries.
+ */
+int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
+{
+ int i, res = 0;
+
+ for (i = 0; i < len; i++)
+ res |= ast_cli_register(e + i);
+
+ return res;
+}
+
+int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
+{
+ int i, res = 0;
+
+ for (i = 0; i < len; i++)
+ res |= ast_cli_unregister(e + i);
+
+ return res;
+}
+
+
+/*! \brief helper for final part of handle_help
+ * if locked = 1, assume the list is already locked
+ */
+static char *help1(int fd, char *match[], int locked)
+{
+ char matchstr[80] = "";
+ struct ast_cli_entry *e = NULL;
+ int len = 0;
+ int found = 0;
+
+ if (match) {
+ ast_join(matchstr, sizeof(matchstr), match);
+ len = strlen(matchstr);
+ }
+ if (!locked)
+ AST_RWLIST_RDLOCK(&helpers);
+ while ( (e = cli_next(e)) ) {
+ /* Hide commands that start with '_' */
+ if (e->_full_cmd[0] == '_')
+ continue;
+ /* Hide commands that are marked as deprecated. */
+ if (e->deprecated)
+ continue;
+ if (match && strncasecmp(matchstr, e->_full_cmd, len))
+ continue;
+ ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
+ found++;
+ }
+ if (!locked)
+ AST_RWLIST_UNLOCK(&helpers);
+ if (!found && matchstr[0])
+ ast_cli(fd, "No such command '%s'.\n", matchstr);
+ return CLI_SUCCESS;
+}
+
+static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char fullcmd[80];
+ struct ast_cli_entry *my_e;
+ char *res = CLI_SUCCESS;
+
+ if (cmd == CLI_INIT) {
+ e->command = "help";
+ e->usage =
+ "Usage: help [topic]\n"
+ " When called with a topic as an argument, displays usage\n"
+ " information on the given command. If called without a\n"
+ " topic, it provides a list of commands.\n";
+ return NULL;
+
+ } else if (cmd == CLI_GENERATE) {
+ /* skip first 4 or 5 chars, "help " */
+ int l = strlen(a->line);
+
+ if (l > 5)
+ l = 5;
+ /* XXX watch out, should stop to the non-generator parts */
+ return __ast_cli_generator(a->line + l, a->word, a->n, 0);
+ }
+ if (a->argc == 1)
+ return help1(a->fd, NULL, 0);
+
+ AST_RWLIST_RDLOCK(&helpers);
+ my_e = find_cli(a->argv + 1, 1); /* try exact match first */
+ if (!my_e) {
+ res = help1(a->fd, a->argv + 1, 1 /* locked */);
+ AST_RWLIST_UNLOCK(&helpers);
+ return res;
+ }
+ if (my_e->usage)
+ ast_cli(a->fd, "%s", my_e->usage);
+ else {
+ ast_join(fullcmd, sizeof(fullcmd), a->argv+1);
+ ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
+ }
+ AST_RWLIST_UNLOCK(&helpers);
+ return res;
+}
+
+static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
+{
+ char *dup, *cur;
+ int x = 0;
+ int quoted = 0;
+ int escaped = 0;
+ int whitespace = 1;
+ int dummy = 0;
+
+ if (trailingwhitespace == NULL)
+ trailingwhitespace = &dummy;
+ *trailingwhitespace = 0;
+ if (s == NULL) /* invalid, though! */
+ return NULL;
+ /* make a copy to store the parsed string */
+ if (!(dup = ast_strdup(s)))
+ return NULL;
+
+ cur = dup;
+ /* scan the original string copying into cur when needed */
+ for (; *s ; s++) {
+ if (x >= max - 1) {
+ ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
+ break;
+ }
+ if (*s == '"' && !escaped) {
+ quoted = !quoted;
+ if (quoted && whitespace) {
+ /* start a quoted string from previous whitespace: new argument */
+ argv[x++] = cur;
+ whitespace = 0;
+ }
+ } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
+ /* If we are not already in whitespace, and not in a quoted string or
+ processing an escape sequence, and just entered whitespace, then
+ finalize the previous argument and remember that we are in whitespace
+ */
+ if (!whitespace) {
+ *cur++ = '\0';
+ whitespace = 1;
+ }
+ } else if (*s == '\\' && !escaped) {
+ escaped = 1;
+ } else {
+ if (whitespace) {
+ /* we leave whitespace, and are not quoted. So it's a new argument */
+ argv[x++] = cur;
+ whitespace = 0;
+ }
+ *cur++ = *s;
+ escaped = 0;
+ }
+ }
+ /* Null terminate */
+ *cur++ = '\0';
+ /* XXX put a NULL in the last argument, because some functions that take
+ * the array may want a null-terminated array.
+ * argc still reflects the number of non-NULL entries.
+ */
+ argv[x] = NULL;
+ *argc = x;
+ *trailingwhitespace = whitespace;
+ return dup;
+}
+
+/*! \brief Return the number of unique matches for the generator */
+int ast_cli_generatornummatches(const char *text, const char *word)
+{
+ int matches = 0, i = 0;
+ char *buf = NULL, *oldbuf = NULL;
+
+ while ((buf = ast_cli_generator(text, word, i++))) {
+ if (!oldbuf || strcmp(buf,oldbuf))
+ matches++;
+ if (oldbuf)
+ ast_free(oldbuf);
+ oldbuf = buf;
+ }
+ if (oldbuf)
+ ast_free(oldbuf);
+ return matches;
+}
+
+char **ast_cli_completion_matches(const char *text, const char *word)
+{
+ char **match_list = NULL, *retstr, *prevstr;
+ size_t match_list_len, max_equal, which, i;
+ int matches = 0;
+
+ /* leave entry 0 free for the longest common substring */
+ match_list_len = 1;
+ while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
+ if (matches + 1 >= match_list_len) {
+ match_list_len <<= 1;
+ if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
+ return NULL;
+ }
+ match_list[++matches] = retstr;
+ }
+
+ if (!match_list)
+ return match_list; /* NULL */
+
+ /* Find the longest substring that is common to all results
+ * (it is a candidate for completion), and store a copy in entry 0.
+ */
+ prevstr = match_list[1];
+ max_equal = strlen(prevstr);
+ for (which = 2; which <= matches; which++) {
+ for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
+ continue;
+ max_equal = i;
+ }
+
+ if (!(retstr = ast_malloc(max_equal + 1)))
+ return NULL;
+
+ ast_copy_string(retstr, match_list[1], max_equal + 1);
+ match_list[0] = retstr;
+
+ /* ensure that the array is NULL terminated */
+ if (matches + 1 >= match_list_len) {
+ if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
+ return NULL;
+ }
+ match_list[matches + 1] = NULL;
+
+ return match_list;
+}
+
+/*! \brief returns true if there are more words to match */
+static int more_words (char * const *dst)
+{
+ int i;
+ for (i = 0; dst[i]; i++) {
+ if (dst[i][0] != '[')
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * generate the entry at position 'state'
+ */
+static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
+{
+ char *argv[AST_MAX_ARGS];
+ struct ast_cli_entry *e = NULL;
+ int x = 0, argindex, matchlen;
+ int matchnum=0;
+ char *ret = NULL;
+ char matchstr[80] = "";
+ int tws = 0;
+ /* Split the argument into an array of words */
+ char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
+
+ if (!dup) /* malloc error */
+ return NULL;
+
+ /* Compute the index of the last argument (could be an empty string) */
+ argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
+
+ /* rebuild the command, ignore terminating white space and flatten space */
+ ast_join(matchstr, sizeof(matchstr)-1, argv);
+ matchlen = strlen(matchstr);
+ if (tws) {
+ strcat(matchstr, " "); /* XXX */
+ if (matchlen)
+ matchlen++;
+ }
+ if (lock)
+ AST_RWLIST_RDLOCK(&helpers);
+ while ( (e = cli_next(e)) ) {
+ /* XXX repeated code */
+ int src = 0, dst = 0, n = 0;
+
+ if (e->command[0] == '_')
+ continue;
+
+ /*
+ * Try to match words, up to and excluding the last word, which
+ * is either a blank or something that we want to extend.
+ */
+ for (;src < argindex; dst++, src += n) {
+ n = word_match(argv[src], e->cmda[dst]);
+ if (n < 0)
+ break;
+ }
+
+ if (src != argindex && more_words(e->cmda + dst)) /* not a match */
+ continue;
+ ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
+ matchnum += n; /* this many matches here */
+ if (ret) {
+ /*
+ * argv[src] is a valid prefix of the next word in this
+ * command. If this is also the correct entry, return it.
+ */
+ if (matchnum > state)
+ break;
+ ast_free(ret);
+ ret = NULL;
+ } else if (ast_strlen_zero(e->cmda[dst])) {
+ /*
+ * This entry is a prefix of the command string entered
+ * (only one entry in the list should have this property).
+ * Run the generator if one is available. In any case we are done.
+ */
+ if (e->handler) { /* new style command */
+ struct ast_cli_args a = {
+ .line = matchstr, .word = word,
+ .pos = argindex,
+ .n = state - matchnum };
+ ret = e->handler(e, CLI_GENERATE, &a);
+ }
+ if (ret)
+ break;
+ }
+ }
+ if (lock)
+ AST_RWLIST_UNLOCK(&helpers);
+ ast_free(dup);
+ return ret;
+}
+
+char *ast_cli_generator(const char *text, const char *word, int state)
+{
+ return __ast_cli_generator(text, word, state, 1);
+}
+
+int ast_cli_command(int fd, const char *s)
+{
+ char *args[AST_MAX_ARGS + 1];
+ struct ast_cli_entry *e;
+ int x;
+ char *dup = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
+ char *retval = NULL;
+ struct ast_cli_args a = {
+ .fd = fd, .argc = x, .argv = args+1 };
+
+ if (dup == NULL)
+ return -1;
+
+ if (x < 1) /* We need at least one entry, otherwise ignore */
+ goto done;
+
+ AST_RWLIST_RDLOCK(&helpers);
+ e = find_cli(args + 1, 0);
+ if (e)
+ ast_atomic_fetchadd_int(&e->inuse, 1);
+ AST_RWLIST_UNLOCK(&helpers);
+ if (e == NULL) {
+ ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(args + 1));
+ goto done;
+ }
+ /*
+ * Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
+ * Remember that the array returned by parse_args is NULL-terminated.
+ */
+ args[0] = (char *)e;
+
+ retval = e->handler(e, CLI_HANDLER, &a);
+
+ if (retval == CLI_SHOWUSAGE) {
+ ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
+ AST_RWLIST_RDLOCK(&helpers);
+ if (e->deprecated)
+ ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
+ AST_RWLIST_UNLOCK(&helpers);
+ } else {
+ if (retval == CLI_FAILURE)
+ ast_cli(fd, "Command '%s' failed.\n", s);
+ AST_RWLIST_RDLOCK(&helpers);
+ if (e->deprecated == 1) {
+ ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
+ e->deprecated = 2;
+ }
+ AST_RWLIST_UNLOCK(&helpers);
+ }
+ ast_atomic_fetchadd_int(&e->inuse, -1);
+done:
+ ast_free(dup);
+ return 0;
+}
+
+int ast_cli_command_multiple(int fd, size_t size, const char *s)
+{
+ char cmd[512];
+ int x, y = 0, count = 0;
+
+ for (x = 0; x < size; x++) {
+ cmd[y] = s[x];
+ y++;
+ if (s[x] == '\0') {
+ ast_cli_command(fd, cmd);
+ y = 0;
+ count++;
+ }
+ }
+ return count;
+}
diff --git a/trunk/main/config.c b/trunk/main/config.c
new file mode 100644
index 000000000..d5b0738fc
--- /dev/null
+++ b/trunk/main/config.c
@@ -0,0 +1,2281 @@
+/*
+ * 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 Configuration File Parser
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * Includes the Asterisk Realtime API - ARA
+ * See doc/realtime.txt and doc/extconfig.txt
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
+#include "asterisk/network.h" /* we do some sockaddr manipulation here */
+#include <time.h>
+#include <sys/stat.h>
+
+#include <math.h> /* HUGE_VAL */
+
+#define AST_INCLUDE_GLOB 1
+
+#ifdef AST_INCLUDE_GLOB
+/* glob compat stuff - eventually this should go in compat.h or some
+ * header in include/asterisk/
+ */
+#if defined(__Darwin__) || defined(__CYGWIN__)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+
+#include <glob.h>
+
+#ifdef SOLARIS
+#define MY_GLOB_FLAGS GLOB_NOCHECK
+#else
+#define MY_GLOB_FLAGS (GLOB_NOMAGIC|GLOB_BRACE)
+#endif
+
+#endif
+
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/strings.h" /* for the ast_str_*() API */
+
+#define MAX_NESTED_COMMENTS 128
+#define COMMENT_START ";--"
+#define COMMENT_END "--;"
+#define COMMENT_META ';'
+#define COMMENT_TAG '-'
+
+static char *extconfig_conf = "extconfig.conf";
+
+
+/*! \brief Structure to keep comments for rewriting configuration files */
+struct ast_comment {
+ struct ast_comment *next;
+ char cmt[0];
+};
+
+/*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
+struct cache_file_include {
+ AST_LIST_ENTRY(cache_file_include) list;
+ char include[0];
+};
+
+struct cache_file_mtime {
+ AST_LIST_ENTRY(cache_file_mtime) list;
+ AST_LIST_HEAD(includes, cache_file_include) includes;
+ unsigned int has_exec:1;
+ time_t mtime;
+ char filename[0];
+};
+
+static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
+
+
+/* comment buffers are better implemented using the ast_str_*() API */
+#define CB_SIZE 250 /* initial size of comment buffers */
+
+static void CB_ADD(struct ast_str **cb, const char *str)
+{
+ ast_str_append(cb, 0, "%s", str);
+}
+
+static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
+{
+ char *s = alloca(len + 1);
+ ast_copy_string(s, str, len);
+ ast_str_append(cb, 0, "%s", str);
+}
+
+static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
+{
+ if (cb)
+ cb->used = 0;
+ if (llb)
+ llb->used = 0;
+}
+
+static struct ast_comment *ALLOC_COMMENT(const struct ast_str *buffer)
+{
+ struct ast_comment *x = NULL;
+ if (buffer && buffer->used)
+ x = ast_calloc(1, sizeof(*x) + buffer->used + 1);
+ if (x)
+ strcpy(x->cmt, buffer->str);
+ return x;
+}
+
+/* I need to keep track of each config file, and all its inclusions,
+ so that we can track blank lines in each */
+
+struct inclfile {
+ char *fname;
+ int lineno;
+};
+
+static int hash_string(const void *obj, const int flags)
+{
+ char *str = ((struct inclfile*)obj)->fname;
+ int total;
+
+ for (total=0; *str; str++) {
+ unsigned int tmp = total;
+ total <<= 1; /* multiply by 2 */
+ total += tmp; /* multiply by 3 */
+ total <<= 2; /* multiply by 12 */
+ total += tmp; /* multiply by 13 */
+
+ total += ((unsigned int)(*str));
+ }
+ if (total < 0)
+ total = -total;
+ return total;
+}
+
+static int hashtab_compare_strings(void *a, void *b, int flags)
+{
+ const struct inclfile *ae = a, *be = b;
+ return !strcmp(ae->fname, be->fname) ? CMP_MATCH : 0;
+}
+
+static struct ast_config_map {
+ struct ast_config_map *next;
+ char *name;
+ char *driver;
+ char *database;
+ char *table;
+ char stuff[0];
+} *config_maps = NULL;
+
+AST_MUTEX_DEFINE_STATIC(config_lock);
+static struct ast_config_engine *config_engine_list;
+
+#define MAX_INCLUDE_LEVEL 10
+
+struct ast_category_template_instance {
+ char name[80]; /* redundant? */
+ const struct ast_category *inst;
+ AST_LIST_ENTRY(ast_category_template_instance) next;
+};
+
+struct ast_category {
+ char name[80];
+ int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
+ int include_level;
+ char *file; /*!< the file name from whence this declaration was read */
+ int lineno;
+ AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
+ struct ast_variable *root;
+ struct ast_variable *last;
+ struct ast_category *next;
+};
+
+struct ast_config {
+ struct ast_category *root;
+ struct ast_category *last;
+ struct ast_category *current;
+ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
+ int include_level;
+ int max_include_level;
+ struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
+};
+
+struct ast_config_include {
+ char *include_location_file; /*!< file name in which the include occurs */
+ int include_location_lineno; /*!< lineno where include occurred */
+ int exec; /*!< set to non-zero if itsa #exec statement */
+ char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
+ char *included_file; /*!< file name included */
+ int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
+ we explode the instances and will include those-- so all entries will be unique */
+ int output; /*!< a flag to indicate if the inclusion has been output */
+ struct ast_config_include *next; /*!< ptr to next inclusion in the list */
+};
+
+struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
+{
+ struct ast_variable *variable;
+ int name_len = strlen(name) + 1;
+ int val_len = strlen(value) + 1;
+ int fn_len = strlen(filename) + 1;
+
+ if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
+ char *dst = variable->stuff; /* writable space starts here */
+ variable->name = strcpy(dst, name);
+ dst += name_len;
+ variable->value = strcpy(dst, value);
+ dst += val_len;
+ variable->file = strcpy(dst, filename);
+ }
+ return variable;
+}
+
+struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
+{
+ /* a file should be included ONCE. Otherwise, if one of the instances is changed,
+ then all be changed. -- how do we know to include it? -- Handling modified
+ instances is possible, I'd have
+ to create a new master for each instance. */
+ struct ast_config_include *inc;
+ struct stat statbuf;
+
+ inc = ast_include_find(conf, included_file);
+ if (inc) {
+ do {
+ inc->inclusion_count++;
+ snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
+ } while (stat(real_included_file_name, &statbuf) == 0);
+ ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
+ } else
+ *real_included_file_name = 0;
+
+ inc = ast_calloc(1,sizeof(struct ast_config_include));
+ inc->include_location_file = ast_strdup(from_file);
+ inc->include_location_lineno = from_lineno;
+ if (!ast_strlen_zero(real_included_file_name))
+ inc->included_file = ast_strdup(real_included_file_name);
+ else
+ inc->included_file = ast_strdup(included_file);
+
+ inc->exec = is_exec;
+ if (is_exec)
+ inc->exec_file = ast_strdup(exec_file);
+
+ /* attach this new struct to the conf struct */
+ inc->next = conf->includes;
+ conf->includes = inc;
+
+ return inc;
+}
+
+void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
+{
+ struct ast_config_include *incl;
+ struct ast_category *cat;
+ struct ast_variable *v;
+
+ int from_len = strlen(from_file);
+ int to_len = strlen(to_file);
+
+ if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
+ return;
+
+ /* the manager code allows you to read in one config file, then
+ write it back out under a different name. But, the new arrangement
+ ties output lines to the file name. So, before you try to write
+ the config file to disk, better riffle thru the data and make sure
+ the file names are changed.
+ */
+ /* file names are on categories, includes (of course), and on variables. So,
+ traverse all this and swap names */
+
+ for (incl = conf->includes; incl; incl=incl->next) {
+ if (strcmp(incl->include_location_file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(incl->include_location_file, to_file);
+ else {
+ free(incl->include_location_file);
+ incl->include_location_file = strdup(to_file);
+ }
+ }
+ }
+ for (cat = conf->root; cat; cat = cat->next) {
+ if (strcmp(cat->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(cat->file, to_file);
+ else {
+ free(cat->file);
+ cat->file = strdup(to_file);
+ }
+ }
+ for (v = cat->root; v; v = v->next) {
+ if (strcmp(v->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(v->file, to_file);
+ else {
+ free(v->file);
+ v->file = strdup(to_file);
+ }
+ }
+ }
+ }
+}
+
+struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
+{
+ struct ast_config_include *x;
+ for (x=conf->includes;x;x=x->next) {
+ if (strcmp(x->included_file,included_file) == 0)
+ return x;
+ }
+ return 0;
+}
+
+
+void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
+{
+ if (!variable)
+ return;
+ if (category->last)
+ category->last->next = variable;
+ else
+ category->root = variable;
+ category->last = variable;
+ while (category->last->next)
+ category->last = category->last->next;
+}
+
+void ast_variables_destroy(struct ast_variable *v)
+{
+ struct ast_variable *vn;
+
+ while (v) {
+ vn = v;
+ v = v->next;
+ ast_free(vn);
+ }
+}
+
+struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
+{
+ struct ast_category *cat = NULL;
+
+ if (category && config->last_browse && (config->last_browse->name == category))
+ cat = config->last_browse;
+ else
+ cat = ast_category_get(config, category);
+
+ return (cat) ? cat->root : NULL;
+}
+
+const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
+{
+ const char *tmp;
+ tmp = ast_variable_retrieve(cfg, cat, var);
+ if (!tmp)
+ tmp = ast_variable_retrieve(cfg, "general", var);
+ return tmp;
+}
+
+
+const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
+{
+ struct ast_variable *v;
+
+ if (category) {
+ for (v = ast_variable_browse(config, category); v; v = v->next) {
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
+ } else {
+ struct ast_category *cat;
+
+ for (cat = config->root; cat; cat = cat->next)
+ for (v = cat->root; v; v = v->next)
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
+
+ return NULL;
+}
+
+static struct ast_variable *variable_clone(const struct ast_variable *old)
+{
+ struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
+
+ if (new) {
+ new->lineno = old->lineno;
+ new->object = old->object;
+ new->blanklines = old->blanklines;
+ /* TODO: clone comments? */
+ }
+
+ return new;
+}
+
+static void move_variables(struct ast_category *old, struct ast_category *new)
+{
+ struct ast_variable *var = old->root;
+
+ old->root = NULL;
+ /* we can just move the entire list in a single op */
+ ast_variable_append(new, var);
+}
+
+struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
+{
+ struct ast_category *category;
+
+ if ((category = ast_calloc(1, sizeof(*category))))
+ ast_copy_string(category->name, name, sizeof(category->name));
+ category->file = strdup(in_file);
+ category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
+ return category;
+}
+
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
+{
+ struct ast_category *cat;
+
+ /* try exact match first, then case-insensitive match */
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == category_name && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ return NULL;
+}
+
+struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+{
+ return category_get(config, category_name, 0);
+}
+
+int ast_category_exist(const struct ast_config *config, const char *category_name)
+{
+ return !!ast_category_get(config, category_name);
+}
+
+void ast_category_append(struct ast_config *config, struct ast_category *category)
+{
+ if (config->last)
+ config->last->next = category;
+ else
+ config->root = category;
+ category->include_level = config->include_level;
+ config->last = category;
+ config->current = category;
+}
+
+static void ast_destroy_comments(struct ast_category *cat)
+{
+ struct ast_comment *n, *p;
+
+ for (p=cat->precomments; p; p=n) {
+ n = p->next;
+ free(p);
+ }
+ for (p=cat->sameline; p; p=n) {
+ n = p->next;
+ free(p);
+ }
+ for (p=cat->trailing; p; p=n) {
+ n = p->next;
+ free(p);
+ }
+ cat->precomments = NULL;
+ cat->sameline = NULL;
+ cat->trailing = NULL;
+}
+
+static void ast_destroy_template_list(struct ast_category *cat)
+{
+ struct ast_category_template_instance *x;
+
+ while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
+ free(x);
+}
+
+void ast_category_destroy(struct ast_category *cat)
+{
+ ast_variables_destroy(cat->root);
+ if (cat->file) {
+ free(cat->file);
+ cat->file = 0;
+ }
+ ast_destroy_comments(cat);
+ ast_destroy_template_list(cat);
+ ast_free(cat);
+}
+
+static void ast_includes_destroy(struct ast_config_include *incls)
+{
+ struct ast_config_include *incl,*inclnext;
+
+ for (incl=incls; incl; incl = inclnext) {
+ inclnext = incl->next;
+ if (incl->include_location_file)
+ free(incl->include_location_file);
+ if (incl->exec_file)
+ free(incl->exec_file);
+ if (incl->included_file)
+ free(incl->included_file);
+ free(incl);
+ }
+}
+
+static struct ast_category *next_available_category(struct ast_category *cat)
+{
+ for (; cat && cat->ignored; cat = cat->next);
+
+ return cat;
+}
+
+/*! return the first var of a category */
+struct ast_variable *ast_category_first(struct ast_category *cat)
+{
+ return (cat) ? cat->root : NULL;
+}
+
+struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
+{
+ struct ast_category *category = ast_category_get(config, cat);
+
+ if (category)
+ return category->root;
+ return NULL;
+}
+
+char *ast_category_browse(struct ast_config *config, const char *prev)
+{
+ struct ast_category *cat = NULL;
+
+ if (prev && config->last_browse && (config->last_browse->name == prev))
+ cat = config->last_browse->next;
+ else if (!prev && config->root)
+ cat = config->root;
+ else if (prev) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == prev) {
+ cat = cat->next;
+ break;
+ }
+ }
+ if (!cat) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, prev)) {
+ cat = cat->next;
+ break;
+ }
+ }
+ }
+ }
+
+ if (cat)
+ cat = next_available_category(cat);
+
+ config->last_browse = cat;
+ return (cat) ? cat->name : NULL;
+}
+
+struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
+{
+ struct ast_variable *v;
+
+ v = cat->root;
+ cat->root = NULL;
+ cat->last = NULL;
+
+ return v;
+}
+
+void ast_category_rename(struct ast_category *cat, const char *name)
+{
+ ast_copy_string(cat->name, name, sizeof(cat->name));
+}
+
+static void inherit_category(struct ast_category *new, const struct ast_category *base)
+{
+ struct ast_variable *var;
+ struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
+
+ strcpy(x->name, base->name);
+ x->inst = base;
+ AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
+ for (var = base->root; var; var = var->next)
+ ast_variable_append(new, variable_clone(var));
+}
+
+struct ast_config *ast_config_new(void)
+{
+ struct ast_config *config;
+
+ if ((config = ast_calloc(1, sizeof(*config))))
+ config->max_include_level = MAX_INCLUDE_LEVEL;
+ return config;
+}
+
+int ast_variable_delete(struct ast_category *category, const char *variable, const char *match)
+{
+ struct ast_variable *cur, *prev=NULL, *curn;
+ int res = -1;
+
+ cur = category->root;
+ while (cur) {
+ if (cur->name == variable) {
+ if (prev) {
+ prev->next = cur->next;
+ if (cur == category->last)
+ category->last = prev;
+ } else {
+ category->root = cur->next;
+ if (cur == category->last)
+ category->last = NULL;
+ }
+ cur->next = NULL;
+ ast_variables_destroy(cur);
+ return 0;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ prev = NULL;
+ cur = category->root;
+ while (cur) {
+ curn = cur->next;
+ if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) {
+ if (prev) {
+ prev->next = cur->next;
+ if (cur == category->last)
+ category->last = prev;
+ } else {
+ category->root = cur->next;
+ if (cur == category->last)
+ category->last = NULL;
+ }
+ cur->next = NULL;
+ ast_variables_destroy(cur);
+ res = 0;
+ } else
+ prev = cur;
+
+ cur = curn;
+ }
+ return res;
+}
+
+int ast_variable_update(struct ast_category *category, const char *variable,
+ const char *value, const char *match, unsigned int object)
+{
+ struct ast_variable *cur, *prev=NULL, *newer=NULL;
+
+ for (cur = category->root; cur; prev = cur, cur = cur->next) {
+ if (strcasecmp(cur->name, variable) ||
+ (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
+ continue;
+
+ if (!(newer = ast_variable_new(variable, value, cur->file)))
+ return -1;
+
+ newer->next = cur->next;
+ newer->object = cur->object || object;
+ if (prev)
+ prev->next = newer;
+ else
+ category->root = newer;
+ if (category->last == cur)
+ category->last = newer;
+
+ cur->next = NULL;
+ ast_variables_destroy(cur);
+
+ return 0;
+ }
+
+ if (prev)
+ prev->next = newer;
+ else
+ category->root = newer;
+
+ return 0;
+}
+
+int ast_category_delete(struct ast_config *cfg, const char *category)
+{
+ struct ast_category *prev=NULL, *cat;
+
+ cat = cfg->root;
+ while (cat) {
+ if (cat->name == category) {
+ if (prev) {
+ prev->next = cat->next;
+ if (cat == cfg->last)
+ cfg->last = prev;
+ } else {
+ cfg->root = cat->next;
+ if (cat == cfg->last)
+ cfg->last = NULL;
+ }
+ ast_category_destroy(cat);
+ return 0;
+ }
+ prev = cat;
+ cat = cat->next;
+ }
+
+ prev = NULL;
+ cat = cfg->root;
+ while (cat) {
+ if (!strcasecmp(cat->name, category)) {
+ if (prev) {
+ prev->next = cat->next;
+ if (cat == cfg->last)
+ cfg->last = prev;
+ } else {
+ cfg->root = cat->next;
+ if (cat == cfg->last)
+ cfg->last = NULL;
+ }
+ ast_category_destroy(cat);
+ return 0;
+ }
+ prev = cat;
+ cat = cat->next;
+ }
+ return -1;
+}
+
+void ast_config_destroy(struct ast_config *cfg)
+{
+ struct ast_category *cat, *catn;
+
+ if (!cfg)
+ return;
+
+ ast_includes_destroy(cfg->includes);
+
+ cat = cfg->root;
+ while (cat) {
+ catn = cat;
+ cat = cat->next;
+ ast_category_destroy(catn);
+ }
+ ast_free(cfg);
+}
+
+struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
+{
+ return cfg->current;
+}
+
+void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
+{
+ /* cast below is just to silence compiler warning about dropping "const" */
+ cfg->current = (struct ast_category *) cat;
+}
+
+enum config_cache_attribute_enum {
+ ATTRIBUTE_INCLUDE = 0,
+ ATTRIBUTE_EXEC = 1,
+};
+
+static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename)
+{
+ struct cache_file_mtime *cfmtime;
+ struct cache_file_include *cfinclude;
+ struct stat statbuf = { 0, };
+
+ /* Find our cached entry for this configuration file */
+ AST_LIST_LOCK(&cfmtime_head);
+ AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
+ if (!strcmp(cfmtime->filename, configfile))
+ break;
+ }
+ if (!cfmtime) {
+ cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1);
+ if (!cfmtime) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ AST_LIST_HEAD_INIT(&cfmtime->includes);
+ strcpy(cfmtime->filename, configfile);
+ /* Note that the file mtime is initialized to 0, i.e. 1970 */
+ AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
+ }
+
+ if (!stat(configfile, &statbuf))
+ cfmtime->mtime = 0;
+ else
+ cfmtime->mtime = statbuf.st_mtime;
+
+ switch (attrtype) {
+ case ATTRIBUTE_INCLUDE:
+ AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
+ if (!strcmp(cfinclude->include, filename)) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ }
+ cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
+ if (!cfinclude) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ strcpy(cfinclude->include, filename);
+ AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
+ break;
+ case ATTRIBUTE_EXEC:
+ cfmtime->has_exec = 1;
+ break;
+ }
+ AST_LIST_UNLOCK(&cfmtime_head);
+}
+
+/*! \brief parse one line in the configuration.
+ * We can have a category header [foo](...)
+ * a directive #include / #exec
+ * or a regular line name = value
+ */
+static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
+ char *buf, int lineno, const char *configfile, struct ast_flags flags,
+ struct ast_str *comment_buffer,
+ struct ast_str *lline_buffer,
+ const char *suggested_include_file,
+ struct ast_category **last_cat, struct ast_variable **last_var)
+{
+ char *c;
+ char *cur = buf;
+ struct ast_variable *v;
+ char cmd[512], exec_file[512];
+
+ /* Actually parse the entry */
+ if (cur[0] == '[') { /* A category header */
+ /* format is one of the following:
+ * [foo] define a new category named 'foo'
+ * [foo](!) define a new template category named 'foo'
+ * [foo](+) append to category 'foo', error if foo does not exist.
+ * [foo](a) define a new category and inherit from template a.
+ * You can put a comma-separated list of templates and '!' and '+'
+ * between parentheses, with obvious meaning.
+ */
+ struct ast_category *newcat = NULL;
+ char *catname;
+
+ c = strchr(cur, ']');
+ if (!c) {
+ ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *c++ = '\0';
+ cur++;
+ if (*c++ != '(')
+ c = NULL;
+ catname = cur;
+ if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
+ return -1;
+ }
+ (*cat)->lineno = lineno;
+ *last_var = 0;
+ *last_cat = newcat;
+
+ /* add comments */
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ newcat->precomments = ALLOC_COMMENT(comment_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ newcat->sameline = ALLOC_COMMENT(lline_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ CB_RESET(comment_buffer, lline_buffer);
+
+ /* If there are options or categories to inherit from, process them now */
+ if (c) {
+ if (!(cur = strchr(c, ')'))) {
+ ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *cur = '\0';
+ while ((cur = strsep(&c, ","))) {
+ if (!strcasecmp(cur, "!")) {
+ (*cat)->ignored = 1;
+ } else if (!strcasecmp(cur, "+")) {
+ *cat = category_get(cfg, catname, 1);
+ if (!(*cat)) {
+ if (newcat)
+ ast_category_destroy(newcat);
+ ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
+ return -1;
+ }
+ if (newcat) {
+ move_variables(newcat, *cat);
+ ast_category_destroy(newcat);
+ newcat = NULL;
+ }
+ } else {
+ struct ast_category *base;
+
+ base = category_get(cfg, cur, 1);
+ if (!base) {
+ ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
+ return -1;
+ }
+ inherit_category(*cat, base);
+ }
+ }
+ }
+ if (newcat)
+ ast_category_append(cfg, *cat);
+ } else if (cur[0] == '#') { /* A directive - #include or #exec */
+ char *cur2;
+ char real_inclusion_name[256];
+ struct ast_config_include *inclu;
+ int do_include = 0; /* otherwise, it is exec */
+
+ cur++;
+ c = cur;
+ while (*c && (*c > 32)) c++;
+ if (*c) {
+ *c = '\0';
+ /* Find real argument */
+ c = ast_skip_blanks(c + 1);
+ if (!(*c))
+ c = NULL;
+ } else
+ c = NULL;
+ if (!strcasecmp(cur, "include")) {
+ do_include = 1;
+ } else if (!strcasecmp(cur, "exec")) {
+ if (!ast_opt_exec_includes) {
+ ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
+ return 0; /* XXX is this correct ? or we should return -1 ? */
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
+ return 0; /* XXX is this correct ? or we should return -1 ? */
+ }
+
+ if (c == NULL) {
+ ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
+ do_include ? "include" : "exec",
+ do_include ? "filename" : "/path/to/executable",
+ lineno,
+ configfile);
+ return 0; /* XXX is this correct ? or we should return -1 ? */
+ }
+
+ /* Strip off leading and trailing "'s and <>'s */
+ while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
+ /* Get rid of leading mess */
+ cur = c;
+ cur2 = cur;
+ while (!ast_strlen_zero(cur)) {
+ c = cur + strlen(cur) - 1;
+ if ((*c == '>') || (*c == '<') || (*c == '\"'))
+ *c = '\0';
+ else
+ break;
+ }
+ /* #exec </path/to/executable>
+ We create a tmp file, then we #include it, then we delete it. */
+ if (!do_include) {
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL);
+ snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
+ snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
+ ast_safe_system(cmd);
+ cur = exec_file;
+ } else {
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur);
+ exec_file[0] = '\0';
+ }
+ /* A #include */
+ /* record this inclusion */
+ inclu = ast_include_new(cfg, configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
+
+ do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name) ? 1 : 0;
+ if (!ast_strlen_zero(exec_file))
+ unlink(exec_file);
+ if (!do_include) {
+ ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
+ return -1;
+ }
+ /* XXX otherwise what ? the default return is 0 anyways */
+
+ } else {
+ /* Just a line (variable = value) */
+ if (!(*cat)) {
+ ast_log(LOG_WARNING,
+ "parse error: No category context for line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ c = strchr(cur, '=');
+ if (c) {
+ int object;
+ *c = 0;
+ c++;
+ /* Ignore > in => */
+ if (*c== '>') {
+ object = 1;
+ c++;
+ } else
+ object = 0;
+ if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
+ v->lineno = lineno;
+ v->object = object;
+ *last_cat = 0;
+ *last_var = v;
+ /* Put and reset comments */
+ v->blanklines = 0;
+ ast_variable_append(*cat, v);
+ /* add comments */
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ v->precomments = ALLOC_COMMENT(comment_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ v->sameline = ALLOC_COMMENT(lline_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ CB_RESET(comment_buffer, lline_buffer);
+
+ } else {
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
+ }
+ }
+ return 0;
+}
+
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
+{
+ char fn[256];
+ char buf[8192];
+ char *new_buf, *comment_p, *process_buf;
+ FILE *f;
+ int lineno=0;
+ int comment = 0, nest[MAX_NESTED_COMMENTS];
+ struct ast_category *cat = NULL;
+ int count = 0;
+ struct stat statbuf;
+ struct cache_file_mtime *cfmtime = NULL;
+ struct cache_file_include *cfinclude;
+ struct ast_variable *last_var = 0;
+ struct ast_category *last_cat = 0;
+ /*! Growable string buffer */
+ struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
+ struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
+
+ if (cfg)
+ cat = ast_config_get_current_category(cfg);
+
+ if (filename[0] == '/') {
+ ast_copy_string(fn, filename, sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
+ }
+
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ comment_buffer = ast_str_create(CB_SIZE);
+ if (comment_buffer)
+ lline_buffer = ast_str_create(CB_SIZE);
+ if (!lline_buffer) {
+ if (comment_buffer)
+ ast_free(comment_buffer);
+ ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
+ return NULL;
+ }
+ }
+#ifdef AST_INCLUDE_GLOB
+ {
+ int glob_ret;
+ glob_t globbuf;
+ globbuf.gl_offs = 0; /* initialize it to silence gcc */
+ glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
+ if (glob_ret == GLOB_NOSPACE)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
+ else if (glob_ret == GLOB_ABORTED)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Read error\n", fn);
+ else {
+ /* loop over expanded files */
+ int i;
+ for (i=0; i<globbuf.gl_pathc; i++) {
+ ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
+#endif
+ /*
+ * The following is not a loop, but just a convenient way to define a block
+ * (using do { } while(0) ), and be able to exit from it with 'continue'
+ * or 'break' in case of errors. Nice trick.
+ */
+ do {
+ if (stat(fn, &statbuf))
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode)) {
+ ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
+ continue;
+ }
+
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
+ /* Find our cached entry for this configuration file */
+ AST_LIST_LOCK(&cfmtime_head);
+ AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
+ if (!strcmp(cfmtime->filename, fn))
+ break;
+ }
+ if (!cfmtime) {
+ cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
+ if (!cfmtime)
+ continue;
+ AST_LIST_HEAD_INIT(&cfmtime->includes);
+ strcpy(cfmtime->filename, fn);
+ /* Note that the file mtime is initialized to 0, i.e. 1970 */
+ AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
+ }
+ }
+
+ if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
+ /* File is unchanged, what about the (cached) includes (if any)? */
+ int unchanged = 1;
+ AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
+ /* We must glob here, because if we did not, then adding a file to globbed directory would
+ * incorrectly cause no reload to be necessary. */
+ char fn2[256];
+#ifdef AST_INCLUDE_GLOB
+ int glob_ret;
+ glob_t globbuf = { .gl_offs = 0 };
+ glob_ret = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &globbuf);
+ /* On error, we reparse */
+ if (glob_ret == GLOB_NOSPACE || glob_ret == GLOB_ABORTED)
+ unchanged = 0;
+ else {
+ /* loop over expanded files */
+ int j;
+ for (j = 0; j < globbuf.gl_pathc; j++) {
+ ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
+#else
+ ast_copy_string(fn2, cfinclude->include);
+#endif
+ if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
+ unchanged = 0;
+ /* One change is enough to short-circuit and reload the whole shebang */
+ break;
+ }
+#ifdef AST_INCLUDE_GLOB
+ }
+ }
+#endif
+ }
+
+ if (unchanged) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return CONFIG_STATUS_FILEUNCHANGED;
+ }
+ }
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ AST_LIST_UNLOCK(&cfmtime_head);
+
+ /* If cfg is NULL, then we just want an answer */
+ if (cfg == NULL)
+ return NULL;
+
+ if (cfmtime)
+ cfmtime->mtime = statbuf.st_mtime;
+
+ ast_verb(2, "Parsing '%s': ", fn);
+ fflush(stdout);
+ if (!(f = fopen(fn, "r"))) {
+ ast_debug(1, "No file to parse: %s\n", fn);
+ ast_verb(2, "Not found (%s)\n", strerror(errno));
+ continue;
+ }
+ count++;
+ /* If we get to this point, then we're loading regardless */
+ ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
+ ast_debug(1, "Parsing %s\n", fn);
+ ast_verb(2, "Found\n");
+ while (!feof(f)) {
+ lineno++;
+ if (fgets(buf, sizeof(buf), f)) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
+ CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
+ lline_buffer->used = 0; /* erase the lline buffer */
+ }
+
+ new_buf = buf;
+ if (comment)
+ process_buf = NULL;
+ else
+ process_buf = buf;
+
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
+ /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
+ CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
+ continue; /* go get a new line, then */
+ }
+
+ while ((comment_p = strchr(new_buf, COMMENT_META))) {
+ if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
+ /* Escaped semicolons aren't comments. */
+ new_buf = comment_p + 1;
+ } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
+ /* Meta-Comment start detected ";--" */
+ if (comment < MAX_NESTED_COMMENTS) {
+ *comment_p = '\0';
+ new_buf = comment_p + 3;
+ comment++;
+ nest[comment-1] = lineno;
+ } else {
+ ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
+ }
+ } else if ((comment_p >= new_buf + 2) &&
+ (*(comment_p - 1) == COMMENT_TAG) &&
+ (*(comment_p - 2) == COMMENT_TAG)) {
+ /* Meta-Comment end detected */
+ comment--;
+ new_buf = comment_p + 1;
+ if (!comment) {
+ /* Back to non-comment now */
+ if (process_buf) {
+ /* Actually have to move what's left over the top, then continue */
+ char *oldptr;
+ oldptr = process_buf + strlen(process_buf);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ CB_ADD(&comment_buffer, ";");
+ CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
+ }
+
+ memmove(oldptr, new_buf, strlen(new_buf) + 1);
+ new_buf = oldptr;
+ } else
+ process_buf = new_buf;
+ }
+ } else {
+ if (!comment) {
+ /* If ; is found, and we are not nested in a comment,
+ we immediately stop all comment processing */
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ CB_ADD(&lline_buffer, comment_p);
+ }
+ *comment_p = '\0';
+ new_buf = comment_p;
+ } else
+ new_buf = comment_p + 1;
+ }
+ }
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
+ CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
+ }
+
+ if (process_buf) {
+ char *buf = ast_strip(process_buf);
+ if (!ast_strlen_zero(buf)) {
+ if (process_text_line(cfg, &cat, buf, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var)) {
+ cfg = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* end of file-- anything in a comment buffer? */
+ if (last_cat) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
+ if (lline_buffer && lline_buffer->used) {
+ CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
+ lline_buffer->used = 0; /* erase the lline buffer */
+ }
+ last_cat->trailing = ALLOC_COMMENT(comment_buffer);
+ }
+ } else if (last_var) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
+ if (lline_buffer && lline_buffer->used) {
+ CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
+ lline_buffer->used = 0; /* erase the lline buffer */
+ }
+ last_var->trailing = ALLOC_COMMENT(comment_buffer);
+ }
+ } else {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
+ ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
+ }
+ }
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ CB_RESET(comment_buffer, lline_buffer);
+
+ fclose(f);
+ } while (0);
+ if (comment) {
+ ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
+ }
+#ifdef AST_INCLUDE_GLOB
+ if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
+ break;
+ }
+ globfree(&globbuf);
+ }
+ }
+#endif
+
+ if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ if (comment_buffer)
+ ast_free(comment_buffer);
+ if (lline_buffer)
+ ast_free(lline_buffer);
+ comment_buffer = NULL;
+ lline_buffer = NULL;
+ }
+
+ if (count == 0)
+ return NULL;
+
+ return cfg;
+}
+
+
+/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
+ which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
+ recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
+ be shocked and mystified as to why things are not showing up in the files!
+
+ Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
+ and line number are stored for each include, plus the name of the file included, so that these statements may be
+ included in the output files on a file_save operation.
+
+ The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
+ are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
+ the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
+ and a header gets added.
+
+ vars and category heads are output in the order they are stored in the config file. So, if the software
+ shuffles these at all, then the placement of #include directives might get a little mixed up, because the
+ file/lineno data probably won't get changed.
+
+*/
+
+static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
+{
+ char date[256]="";
+ time_t t;
+
+ time(&t);
+ ast_copy_string(date, ctime(&t), sizeof(date));
+
+ fprintf(f1, ";!\n");
+ fprintf(f1, ";! Automatically generated configuration file\n");
+ if (strcmp(configfile, fn))
+ fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
+ else
+ fprintf(f1, ";! Filename: %s\n", configfile);
+ fprintf(f1, ";! Generator: %s\n", generator);
+ fprintf(f1, ";! Creation Date: %s", date);
+ fprintf(f1, ";!\n");
+}
+
+static void inclfile_destroy(void *obj)
+{
+ const struct inclfile *o = obj;
+
+ if (o->fname)
+ free(o->fname);
+}
+
+
+static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
+{
+ struct inclfile lookup;
+
+ if (!file || file[0] == 0) {
+ if (configfile[0] == '/')
+ ast_copy_string(fn, configfile, fn_size);
+ else
+ snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
+ } else if (file[0] == '/')
+ ast_copy_string(fn, file, fn_size);
+ else
+ snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
+ lookup.fname = fn;
+ *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
+ if (!(*fi)) {
+ /* set up a file scratch pad */
+ struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
+ fx->fname = ast_strdup(fn);
+ fx->lineno = 1;
+ *fi = fx;
+ ao2_link(fileset, fx);
+ }
+}
+
+static int count_linefeeds(char *str)
+{
+ int count = 0;
+
+ while (*str) {
+ if (*str =='\n')
+ count++;
+ str++;
+ }
+ return count;
+}
+
+static int count_linefeeds_in_comments(struct ast_comment *x)
+{
+ int count = 0;
+
+ while (x) {
+ count += count_linefeeds(x->cmt);
+ x = x->next;
+ }
+ return count;
+}
+
+static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
+{
+ int precomment_lines = count_linefeeds_in_comments(precomments);
+ int i;
+
+ /* I don't have to worry about those ;! comments, they are
+ stored in the precomments, but not printed back out.
+ I did have to make sure that comments following
+ the ;! header comments were not also deleted in the process */
+ for (i=fi->lineno;i<lineno - precomment_lines; i++) {
+ fprintf(fp,"\n");
+ }
+ fi->lineno = lineno+1; /* Advance the file lineno */
+}
+
+int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
+{
+ FILE *f;
+ char fn[256];
+ struct ast_variable *var;
+ struct ast_category *cat;
+ struct ast_comment *cmt;
+ struct ast_config_include *incl;
+ int blanklines = 0;
+ struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
+ struct inclfile *fi = 0;
+
+ /* reset all the output flags, in case this isn't our first time saving this data */
+
+ for (incl=cfg->includes; incl; incl = incl->next)
+ incl->output = 0;
+
+ /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
+ are all truncated to zero bytes and have that nice header*/
+
+ for (incl=cfg->includes; incl; incl = incl->next)
+ {
+ if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
+ FILE *f1;
+
+ set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
+ f1 = fopen(fn,"w");
+ if (f1) {
+ gen_header(f1, configfile, fn, generator);
+ fclose(f1); /* this should zero out the file */
+ } else {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ }
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ }
+ }
+
+ set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
+#ifdef __CYGWIN__
+ if ((f = fopen(fn, "w+"))) {
+#else
+ if ((f = fopen(fn, "w"))) {
+#endif
+ ast_verb(2, "Saving '%s': ", fn);
+ gen_header(f, configfile, fn, generator);
+ cat = cfg->root;
+ fclose(f);
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+
+ /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
+ /* since each var, cat, and associated comments can come from any file, we have to be
+ mobile, and open each file, print, and close it on an entry-by-entry basis */
+
+ while (cat) {
+ set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* dump any includes that happen before this category header */
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (strcmp(incl->include_location_file, cat->file) == 0){
+ if (cat->lineno > incl->include_location_lineno && !incl->output) {
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ incl->output = 1;
+ }
+ }
+ }
+
+ insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
+ /* Dump section with any appropriate comment */
+ for (cmt = cat->precomments; cmt; cmt=cmt->next) {
+ char *cmtp = cmt->cmt;
+ while (*cmtp == ';' && *(cmtp+1) == '!') {
+ char *cmtp2 = strchr(cmtp+1, '\n');
+ if (cmtp2)
+ cmtp = cmtp2+1;
+ else cmtp = 0;
+ }
+ if (cmtp)
+ fprintf(f,"%s", cmtp);
+ }
+ if (!cat->precomments)
+ fprintf(f,"\n");
+ fprintf(f, "[%s]", cat->name);
+ if (cat->ignored)
+ fprintf(f, "(!)");
+ if (!AST_LIST_EMPTY(&cat->template_instances)) {
+ struct ast_category_template_instance *x;
+ fprintf(f, "(");
+ AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
+ fprintf(f,"%s",x->name);
+ if (x != AST_LIST_LAST(&cat->template_instances))
+ fprintf(f,",");
+ }
+ fprintf(f, ")");
+ }
+ for(cmt = cat->sameline; cmt; cmt=cmt->next)
+ {
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (!cat->sameline)
+ fprintf(f,"\n");
+ for (cmt = cat->trailing; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ fclose(f);
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+
+ var = cat->root;
+ while (var) {
+ struct ast_category_template_instance *x;
+ int found = 0;
+ AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
+ struct ast_variable *v;
+ for (v = x->inst->root; v; v = v->next) {
+ if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (found) {
+ var = var->next;
+ continue;
+ }
+ set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* dump any includes that happen before this category header */
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (strcmp(incl->include_location_file, var->file) == 0){
+ if (var->lineno > incl->include_location_lineno && !incl->output) {
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ incl->output = 1;
+ }
+ }
+ }
+
+ insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
+ for (cmt = var->precomments; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (var->sameline)
+ fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
+ else
+ fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
+ for (cmt = var->trailing; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (var->blanklines) {
+ blanklines = var->blanklines;
+ while (blanklines--)
+ fprintf(f, "\n");
+ }
+
+ fclose(f);
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+
+ var = var->next;
+ }
+ cat = cat->next;
+ }
+ if (!option_debug)
+ ast_verb(2, "Saved\n");
+ } else {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write (%s)", strerror(errno));
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* Now, for files with trailing #include/#exec statements,
+ we have to make sure every entry is output */
+
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (!incl->output) {
+ /* open the respective file */
+ set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* output the respective include */
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ fclose(f);
+ incl->output = 1;
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ }
+ }
+ ao2_ref(fileset, -1); /* this should destroy the hash container */
+
+ return 0;
+}
+
+static void clear_config_maps(void)
+{
+ struct ast_config_map *map;
+
+ ast_mutex_lock(&config_lock);
+
+ while (config_maps) {
+ map = config_maps;
+ config_maps = config_maps->next;
+ ast_free(map);
+ }
+
+ ast_mutex_unlock(&config_lock);
+}
+
+static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
+{
+ struct ast_config_map *map;
+ int length;
+
+ length = sizeof(*map);
+ length += strlen(name) + 1;
+ length += strlen(driver) + 1;
+ length += strlen(database) + 1;
+ if (table)
+ length += strlen(table) + 1;
+
+ if (!(map = ast_calloc(1, length)))
+ return -1;
+
+ map->name = map->stuff;
+ strcpy(map->name, name);
+ map->driver = map->name + strlen(map->name) + 1;
+ strcpy(map->driver, driver);
+ map->database = map->driver + strlen(map->driver) + 1;
+ strcpy(map->database, database);
+ if (table) {
+ map->table = map->database + strlen(map->database) + 1;
+ strcpy(map->table, table);
+ }
+ map->next = config_maps;
+
+ ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
+
+ config_maps = map;
+ return 0;
+}
+
+int read_config_maps(void)
+{
+ struct ast_config *config, *configtmp;
+ struct ast_variable *v;
+ char *driver, *table, *database, *stringp, *tmp;
+ struct ast_flags flags = { 0 };
+
+ clear_config_maps();
+
+ configtmp = ast_config_new();
+ configtmp->max_include_level = 1;
+ config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
+ if (!config) {
+ ast_config_destroy(configtmp);
+ return 0;
+ }
+
+ for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
+ char buf[512];
+ ast_copy_string(buf, v->value, sizeof(buf));
+ stringp = buf;
+ driver = strsep(&stringp, ",");
+
+ if ((tmp = strchr(stringp, '\"')))
+ stringp = tmp;
+
+ /* check if the database text starts with a double quote */
+ if (*stringp == '"') {
+ stringp++;
+ database = strsep(&stringp, "\"");
+ strsep(&stringp, ",");
+ } else {
+ /* apparently this text has no quotes */
+ database = strsep(&stringp, ",");
+ }
+
+ table = strsep(&stringp, ",");
+
+ if (!strcmp(v->name, extconfig_conf)) {
+ ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
+ continue;
+ }
+
+ if (!strcmp(v->name, "asterisk.conf")) {
+ ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
+ continue;
+ }
+
+ if (!strcmp(v->name, "logger.conf")) {
+ ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
+ continue;
+ }
+
+ if (!driver || !database)
+ continue;
+ if (!strcasecmp(v->name, "sipfriends")) {
+ ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
+ append_mapping("sipusers", driver, database, table ? table : "sipfriends");
+ append_mapping("sippeers", driver, database, table ? table : "sipfriends");
+ } else if (!strcasecmp(v->name, "iaxfriends")) {
+ ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
+ append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
+ append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
+ } else
+ append_mapping(v->name, driver, database, table);
+ }
+
+ ast_config_destroy(config);
+ return 0;
+}
+
+int ast_config_engine_register(struct ast_config_engine *new)
+{
+ struct ast_config_engine *ptr;
+
+ ast_mutex_lock(&config_lock);
+
+ if (!config_engine_list) {
+ config_engine_list = new;
+ } else {
+ for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
+ ptr->next = new;
+ }
+
+ ast_mutex_unlock(&config_lock);
+ ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
+
+ return 1;
+}
+
+int ast_config_engine_deregister(struct ast_config_engine *del)
+{
+ struct ast_config_engine *ptr, *last=NULL;
+
+ ast_mutex_lock(&config_lock);
+
+ for (ptr = config_engine_list; ptr; ptr=ptr->next) {
+ if (ptr == del) {
+ if (last)
+ last->next = ptr->next;
+ else
+ config_engine_list = ptr->next;
+ break;
+ }
+ last = ptr;
+ }
+
+ ast_mutex_unlock(&config_lock);
+
+ return 0;
+}
+
+/*! \brief Find realtime engine for realtime family */
+static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
+{
+ struct ast_config_engine *eng, *ret = NULL;
+ struct ast_config_map *map;
+
+ ast_mutex_lock(&config_lock);
+
+ for (map = config_maps; map; map = map->next) {
+ if (!strcasecmp(family, map->name)) {
+ if (database)
+ ast_copy_string(database, map->database, dbsiz);
+ if (table)
+ ast_copy_string(table, map->table ? map->table : family, tabsiz);
+ break;
+ }
+ }
+
+ /* Check if the required driver (engine) exist */
+ if (map) {
+ for (eng = config_engine_list; !ret && eng; eng = eng->next) {
+ if (!strcasecmp(eng->name, map->driver))
+ ret = eng;
+ }
+ }
+
+ ast_mutex_unlock(&config_lock);
+
+ /* if we found a mapping, but the engine is not available, then issue a warning */
+ if (map && !ret)
+ ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
+
+ return ret;
+}
+
+static struct ast_config_engine text_file_engine = {
+ .name = "text",
+ .load_func = config_text_file_load,
+};
+
+struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
+{
+ char db[256];
+ char table[256];
+ struct ast_config_engine *loader = &text_file_engine;
+ struct ast_config *result;
+
+ if (cfg->include_level == cfg->max_include_level) {
+ ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
+ return NULL;
+ }
+
+ cfg->include_level++;
+
+ if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
+ struct ast_config_engine *eng;
+
+ eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
+
+
+ if (eng && eng->load_func) {
+ loader = eng;
+ } else {
+ eng = find_engine("global", db, sizeof(db), table, sizeof(table));
+ if (eng && eng->load_func)
+ loader = eng;
+ }
+ }
+
+ result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
+
+ if (result && result != CONFIG_STATUS_FILEUNCHANGED)
+ result->include_level--;
+ else
+ cfg->include_level--;
+
+ return result;
+}
+
+struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, flags, "");
+ if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
+{
+ struct ast_config_engine *eng;
+ char db[256]="";
+ char table[256]="";
+ struct ast_variable *res=NULL;
+
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->realtime_func)
+ res = eng->realtime_func(db, table, ap);
+
+ return res;
+}
+
+struct ast_variable *ast_load_realtime_all(const char *family, ...)
+{
+ struct ast_variable *res;
+ va_list ap;
+
+ va_start(ap, family);
+ res = ast_load_realtime_helper(family, ap);
+ va_end(ap);
+
+ return res;
+}
+
+struct ast_variable *ast_load_realtime(const char *family, ...)
+{
+ struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
+ va_list ap;
+
+ va_start(ap, family);
+ res = ast_load_realtime_helper(family, ap);
+ va_end(ap);
+
+ /* Eliminate blank entries */
+ for (cur = res; cur; cur = cur->next) {
+ if (freeme) {
+ ast_free(freeme);
+ freeme = NULL;
+ }
+
+ if (ast_strlen_zero(cur->value)) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ res = cur->next;
+ freeme = cur;
+ } else {
+ prev = cur;
+ }
+ }
+ return res;
+}
+
+/*! \brief Check if realtime engine is configured for family */
+int ast_check_realtime(const char *family)
+{
+ struct ast_config_engine *eng;
+
+ eng = find_engine(family, NULL, 0, NULL, 0);
+ if (eng)
+ return 1;
+ return 0;
+}
+
+/*! \brief Check if there's any realtime engines loaded */
+int ast_realtime_enabled()
+{
+ return config_maps ? 1 : 0;
+}
+
+struct ast_config *ast_load_realtime_multientry(const char *family, ...)
+{
+ struct ast_config_engine *eng;
+ char db[256]="";
+ char table[256]="";
+ struct ast_config *res=NULL;
+ va_list ap;
+
+ va_start(ap, family);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->realtime_multi_func)
+ res = eng->realtime_multi_func(db, table, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
+{
+ struct ast_config_engine *eng;
+ int res = -1;
+ char db[256]="";
+ char table[256]="";
+ va_list ap;
+
+ va_start(ap, lookup);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->update_func)
+ res = eng->update_func(db, table, keyfield, lookup, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int ast_store_realtime(const char *family, ...) {
+ struct ast_config_engine *eng;
+ int res = -1;
+ char db[256]="";
+ char table[256]="";
+ va_list ap;
+
+ va_start(ap, family);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->store_func)
+ res = eng->store_func(db, table, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
+ struct ast_config_engine *eng;
+ int res = -1;
+ char db[256]="";
+ char table[256]="";
+ va_list ap;
+
+ va_start(ap, lookup);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->destroy_func)
+ res = eng->destroy_func(db, table, keyfield, lookup, ap);
+ va_end(ap);
+
+ return res;
+}
+
+/*! \brief Helper function to parse arguments
+ * See documentation in config.h
+ */
+int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
+ void *p_result, ...)
+{
+ va_list ap;
+ int error = 0;
+
+ va_start(ap, p_result);
+ switch (flags & PARSE_TYPE) {
+ case PARSE_INT32:
+ {
+ int32_t *result = p_result;
+ int32_t x, def = result ? *result : 0,
+ high = (int32_t)0x7fffffff,
+ low = (int32_t)0x80000000;
+ /* optional argument: first default value, then range */
+ if (flags & PARSE_DEFAULT)
+ def = va_arg(ap, int32_t);
+ if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
+ /* range requested, update bounds */
+ low = va_arg(ap, int32_t);
+ high = va_arg(ap, int32_t);
+ }
+ x = strtol(arg, NULL, 0);
+ error = (x < low) || (x > high);
+ if (flags & PARSE_OUT_RANGE)
+ error = !error;
+ if (result)
+ *result = error ? def : x;
+ ast_debug(3,
+ "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
+ arg, low, high,
+ result ? *result : x, error);
+ break;
+ }
+
+ case PARSE_UINT32:
+ {
+ uint32_t *result = p_result;
+ uint32_t x, def = result ? *result : 0,
+ low = 0, high = (uint32_t)~0;
+ /* optional argument: first default value, then range */
+ if (flags & PARSE_DEFAULT)
+ def = va_arg(ap, uint32_t);
+ if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
+ /* range requested, update bounds */
+ low = va_arg(ap, uint32_t);
+ high = va_arg(ap, uint32_t);
+ }
+ x = strtoul(arg, NULL, 0);
+ error = (x < low) || (x > high);
+ if (flags & PARSE_OUT_RANGE)
+ error = !error;
+ if (result)
+ *result = error ? def : x;
+ ast_debug(3,
+ "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
+ arg, low, high,
+ result ? *result : x, error);
+ break;
+ }
+
+ case PARSE_DOUBLE:
+ {
+ double *result = p_result;
+ double x, def = result ? *result : 0,
+ low = -HUGE_VAL, high = HUGE_VAL;
+
+ /* optional argument: first default value, then range */
+ if (flags & PARSE_DEFAULT)
+ def = va_arg(ap, double);
+ if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
+ /* range requested, update bounds */
+ low = va_arg(ap, double);
+ high = va_arg(ap, double);
+ }
+ x = strtod(arg, NULL);
+ error = (x < low) || (x > high);
+ if (flags & PARSE_OUT_RANGE)
+ error = !error;
+ if (result)
+ *result = error ? def : x;
+ ast_debug(3,
+ "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
+ arg, low, high,
+ result ? *result : x, error);
+ break;
+ }
+ case PARSE_INADDR:
+ {
+ char *port, *buf;
+ struct sockaddr_in _sa_buf; /* buffer for the result */
+ struct sockaddr_in *sa = p_result ?
+ (struct sockaddr_in *)p_result : &_sa_buf;
+ /* default is either the supplied value or the result itself */
+ struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
+ va_arg(ap, struct sockaddr_in *) : sa;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+
+ bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
+ /* duplicate the string to strip away the :port */
+ port = ast_strdupa(arg);
+ buf = strsep(&port, ":");
+ sa->sin_family = AF_INET; /* assign family */
+ /*
+ * honor the ports flag setting, assign default value
+ * in case of errors or field unset.
+ */
+ flags &= PARSE_PORT_MASK; /* the only flags left to process */
+ if (port) {
+ if (flags == PARSE_PORT_FORBID) {
+ error = 1; /* port was forbidden */
+ sa->sin_port = def->sin_port;
+ } else if (flags == PARSE_PORT_IGNORE)
+ sa->sin_port = def->sin_port;
+ else /* accept or require */
+ sa->sin_port = htons(strtol(port, NULL, 0));
+ } else {
+ sa->sin_port = def->sin_port;
+ if (flags == PARSE_PORT_REQUIRE)
+ error = 1;
+ }
+ /* Now deal with host part, even if we have errors before. */
+ hp = ast_gethostbyname(buf, &ahp);
+ if (hp) /* resolved successfully */
+ memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
+ else {
+ error = 1;
+ sa->sin_addr = def->sin_addr;
+ }
+ ast_debug(3,
+ "extract inaddr from [%s] gives [%s:%d](%d)\n",
+ arg, ast_inet_ntoa(sa->sin_addr),
+ ntohs(sa->sin_port), error);
+ break;
+ }
+ }
+ va_end(ap);
+ return error;
+}
+
+static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_config_engine *eng;
+ struct ast_config_map *map;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show config mappings";
+ e->usage =
+ "Usage: core show config mappings\n"
+ " Shows the filenames to config engines.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_mutex_lock(&config_lock);
+
+ if (!config_engine_list) {
+ ast_cli(a->fd, "No config mappings found.\n");
+ } else {
+ ast_cli(a->fd, "\n\n");
+ for (eng = config_engine_list; eng; eng = eng->next) {
+ ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
+ for (map = config_maps; map; map = map->next) {
+ if (!strcasecmp(map->driver, eng->name)) {
+ ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
+ map->table ? map->table : map->name);
+ }
+ }
+ }
+ ast_cli(a->fd,"\n\n");
+ }
+
+ ast_mutex_unlock(&config_lock);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_config[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
+};
+
+int register_config_cli()
+{
+ ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
+ return 0;
+}
diff --git a/trunk/main/cryptostub.c b/trunk/main/cryptostub.c
new file mode 100644
index 000000000..c3b59c7b3
--- /dev/null
+++ b/trunk/main/cryptostub.c
@@ -0,0 +1,67 @@
+/*
+ * 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 Stubs for res_crypto routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/crypto.h"
+
+static struct ast_key *stub_ast_key_get(const char *kname, int ktype)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return NULL;
+}
+
+#ifdef SKREP
+#define build_stub(func_name,...) \
+static int stub_ ## func_name(__VA_ARGS__) \
+{ \
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_ ## func_name;
+#endif
+#define build_stub(func_name,...) \
+static int stub_##func_name(__VA_ARGS__) \
+{ \
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_##func_name;
+
+struct ast_key *(*ast_key_get)(const char *key, int type) =
+stub_ast_key_get;
+
+build_stub(ast_check_signature, struct ast_key *key, const char *msg, const char *sig);
+build_stub(ast_check_signature_bin, struct ast_key *key, const char *msg, int msglen, const unsigned char *sig);
+build_stub(ast_sign, struct ast_key *key, char *msg, char *sig);
+build_stub(ast_sign_bin, struct ast_key *key, const char *msg, int msglen, unsigned char *sig);
+build_stub(ast_encrypt_bin, unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key);
+build_stub(ast_decrypt_bin, unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key);
diff --git a/trunk/main/cygload.c b/trunk/main/cygload.c
new file mode 100644
index 000000000..fd8f3d5b6
--- /dev/null
+++ b/trunk/main/cygload.c
@@ -0,0 +1,39 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * 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
+ * Loader for Asterisk under Cygwin/windows.
+ * Open the dll, locate main, run.
+ */
+
+#include <unistd.h>
+#include <dlfcn.h>
+#include <stdio.h>
+
+typedef int (*main_f)(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+ main_f ast_main = NULL;
+ void *handle = dlopen("asterisk.dll", 0);
+ if (handle)
+ ast_main = (main_f)dlsym(handle, "main");
+ if (ast_main)
+ return ast_main(argc, argv);
+ fprintf(stderr, "could not load Asterisk, %s\n", dlerror());
+ return 1; /* there was an error */
+}
diff --git a/trunk/main/db.c b/trunk/main/db.c
new file mode 100644
index 000000000..94bf4f001
--- /dev/null
+++ b/trunk/main/db.c
@@ -0,0 +1,671 @@
+/*
+ * 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 ASTdb Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note DB3 is licensed under Sleepycat Public License and is thus incompatible
+ * with GPL. To avoid having to make another exception (and complicate
+ * licensing even further) we elect to use DB1 which is BSD licensed
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_DB */
+#include <sys/time.h>
+#include <signal.h>
+#include <dirent.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/astdb.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/manager.h"
+#include "db1-ast/include/db.h"
+
+static DB *astdb;
+AST_MUTEX_DEFINE_STATIC(dblock);
+
+static int dbinit(void)
+{
+ if (!astdb && !(astdb = dbopen(ast_config_AST_DB, O_CREAT | O_RDWR, AST_FILE_MODE, DB_BTREE, NULL))) {
+ ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", ast_config_AST_DB, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+
+static inline int keymatch(const char *key, const char *prefix)
+{
+ int preflen = strlen(prefix);
+ if (!preflen)
+ return 1;
+ if (!strcasecmp(key, prefix))
+ return 1;
+ if ((strlen(key) > preflen) && !strncasecmp(key, prefix, preflen)) {
+ if (key[preflen] == '/')
+ return 1;
+ }
+ return 0;
+}
+
+static inline int subkeymatch(const char *key, const char *suffix)
+{
+ int suffixlen = strlen(suffix);
+ if (suffixlen) {
+ const char *subkey = key + strlen(key) - suffixlen;
+ if (subkey < key)
+ return 0;
+ if (!strcasecmp(subkey, suffix))
+ return 1;
+ }
+ return 0;
+}
+
+int ast_db_deltree(const char *family, const char *keytree)
+{
+ char prefix[256];
+ DBT key, data;
+ char *keys;
+ int res;
+ int pass;
+ int counter = 0;
+
+ if (family) {
+ if (keytree) {
+ snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
+ } else {
+ snprintf(prefix, sizeof(prefix), "/%s", family);
+ }
+ } else if (keytree) {
+ return -1;
+ } else {
+ prefix[0] = '\0';
+ }
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (keymatch(keys, prefix)) {
+ astdb->del(astdb, &key, 0);
+ counter++;
+ }
+ }
+ astdb->sync(astdb, 0);
+ ast_mutex_unlock(&dblock);
+ return counter;
+}
+
+int ast_db_put(const char *family, const char *keys, const char *value)
+{
+ char fullkey[256];
+ DBT key, data;
+ int res, fullkeylen;
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = fullkey;
+ key.size = fullkeylen + 1;
+ data.data = (char *) value;
+ data.size = strlen(value) + 1;
+ res = astdb->put(astdb, &key, &data, 0);
+ astdb->sync(astdb, 0);
+ ast_mutex_unlock(&dblock);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
+ return res;
+}
+
+int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
+{
+ char fullkey[256] = "";
+ DBT key, data;
+ int res, fullkeylen;
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ memset(value, 0, valuelen);
+ key.data = fullkey;
+ key.size = fullkeylen + 1;
+
+ res = astdb->get(astdb, &key, &data, 0);
+
+ ast_mutex_unlock(&dblock);
+
+ /* Be sure to NULL terminate our data either way */
+ if (res) {
+ ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
+ } else {
+#if 0
+ printf("Got value of size %d\n", data.size);
+#endif
+ if (data.size) {
+ ((char *)data.data)[data.size - 1] = '\0';
+ /* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */
+ ast_copy_string(value, data.data, (valuelen > data.size) ? data.size : valuelen);
+ } else {
+ ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
+ }
+ }
+ return res;
+}
+
+int ast_db_del(const char *family, const char *keys)
+{
+ char fullkey[256];
+ DBT key;
+ int res, fullkeylen;
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
+ memset(&key, 0, sizeof(key));
+ key.data = fullkey;
+ key.size = fullkeylen + 1;
+
+ res = astdb->del(astdb, &key, 0);
+ astdb->sync(astdb, 0);
+
+ ast_mutex_unlock(&dblock);
+
+ if (res) {
+ ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
+ }
+ return res;
+}
+
+static char *handle_cli_database_put(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database put";
+ e->usage =
+ "Usage: database put <family> <key> <value>\n"
+ " Adds or updates an entry in the Asterisk database for\n"
+ " a given family, key, and value.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+ res = ast_db_put(a->argv[2], a->argv[3], a->argv[4]);
+ if (res) {
+ ast_cli(a->fd, "Failed to update entry\n");
+ } else {
+ ast_cli(a->fd, "Updated database successfully\n");
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_get(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+ char tmp[256];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database get";
+ e->usage =
+ "Usage: database get <family> <key>\n"
+ " Retrieves an entry in the Asterisk database for a given\n"
+ " family and key.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ res = ast_db_get(a->argv[2], a->argv[3], tmp, sizeof(tmp));
+ if (res) {
+ ast_cli(a->fd, "Database entry not found.\n");
+ } else {
+ ast_cli(a->fd, "Value: %s\n", tmp);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_del(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database del";
+ e->usage =
+ "Usage: database del <family> <key>\n"
+ " Deletes an entry in the Asterisk database for a given\n"
+ " family and key.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ res = ast_db_del(a->argv[2], a->argv[3]);
+ if (res) {
+ ast_cli(a->fd, "Database entry does not exist.\n");
+ } else {
+ ast_cli(a->fd, "Database entry removed.\n");
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_deltree(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database deltree";
+ e->usage =
+ "Usage: database deltree <family> [keytree]\n"
+ " Deletes a family or specific keytree within a family\n"
+ " in the Asterisk database.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc < 3) || (a->argc > 4))
+ return CLI_SHOWUSAGE;
+ if (a->argc == 4) {
+ res = ast_db_deltree(a->argv[2], a->argv[3]);
+ } else {
+ res = ast_db_deltree(a->argv[2], NULL);
+ }
+ if (res < 0) {
+ ast_cli(a->fd, "Database entries do not exist.\n");
+ } else {
+ ast_cli(a->fd, "%d database entries removed.\n",res);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char prefix[256];
+ DBT key, data;
+ char *keys, *values;
+ int res;
+ int pass;
+ int counter = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database show";
+ e->usage =
+ "Usage: database show [family [keytree]]\n"
+ " Shows Asterisk database contents, optionally restricted\n"
+ " to a given family, or family and keytree.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 4) {
+ /* Family and key tree */
+ snprintf(prefix, sizeof(prefix), "/%s/%s", a->argv[2], a->argv[3]);
+ } else if (a->argc == 3) {
+ /* Family only */
+ snprintf(prefix, sizeof(prefix), "/%s", a->argv[2]);
+ } else if (a->argc == 2) {
+ /* Neither */
+ prefix[0] = '\0';
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "Database unavailable\n");
+ return CLI_SUCCESS;
+ }
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (data.size) {
+ values = data.data;
+ values[data.size - 1]='\0';
+ } else {
+ values = "<bad value>";
+ }
+ if (keymatch(keys, prefix)) {
+ ast_cli(a->fd, "%-50s: %-25s\n", keys, values);
+ counter++;
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "%d results found.\n", counter);
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char suffix[256];
+ DBT key, data;
+ char *keys, *values;
+ int res;
+ int pass;
+ int counter = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database showkey";
+ e->usage =
+ "Usage: database showkey <keytree>\n"
+ " Shows Asterisk database contents, restricted to a given key.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 3) {
+ /* Key only */
+ snprintf(suffix, sizeof(suffix), "/%s", a->argv[2]);
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "Database unavailable\n");
+ return CLI_SUCCESS;
+ }
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (data.size) {
+ values = data.data;
+ values[data.size - 1]='\0';
+ } else {
+ values = "<bad value>";
+ }
+ if (subkeymatch(keys, suffix)) {
+ ast_cli(a->fd, "%-50s: %-25s\n", keys, values);
+ counter++;
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "%d results found.\n", counter);
+ return CLI_SUCCESS;
+}
+
+struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
+{
+ char prefix[256];
+ DBT key, data;
+ char *keys, *values;
+ int values_len;
+ int res;
+ int pass;
+ struct ast_db_entry *last = NULL;
+ struct ast_db_entry *cur, *ret=NULL;
+
+ if (!ast_strlen_zero(family)) {
+ if (!ast_strlen_zero(keytree)) {
+ /* Family and key tree */
+ snprintf(prefix, sizeof(prefix), "/%s/%s", family, prefix);
+ } else {
+ /* Family only */
+ snprintf(prefix, sizeof(prefix), "/%s", family);
+ }
+ } else {
+ prefix[0] = '\0';
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_log(LOG_WARNING, "Database unavailable\n");
+ return NULL;
+ }
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (data.size) {
+ values = data.data;
+ values[data.size - 1] = '\0';
+ } else {
+ values = "<bad value>";
+ }
+ values_len = strlen(values) + 1;
+ if (keymatch(keys, prefix) && (cur = ast_malloc(sizeof(*cur) + strlen(keys) + 1 + values_len))) {
+ cur->next = NULL;
+ cur->key = cur->data + values_len;
+ strcpy(cur->data, values);
+ strcpy(cur->key, keys);
+ if (last) {
+ last->next = cur;
+ } else {
+ ret = cur;
+ }
+ last = cur;
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ return ret;
+}
+
+void ast_db_freetree(struct ast_db_entry *dbe)
+{
+ struct ast_db_entry *last;
+ while (dbe) {
+ last = dbe;
+ dbe = dbe->next;
+ ast_free(last);
+ }
+}
+
+struct ast_cli_entry cli_database[] = {
+ AST_CLI_DEFINE(handle_cli_database_show, "Shows database contents"),
+ AST_CLI_DEFINE(handle_cli_database_showkey, "Shows database contents"),
+ AST_CLI_DEFINE(handle_cli_database_get, "Gets database value"),
+ AST_CLI_DEFINE(handle_cli_database_put, "Adds/updates database value"),
+ AST_CLI_DEFINE(handle_cli_database_del, "Removes database key/value"),
+ AST_CLI_DEFINE(handle_cli_database_deltree, "Removes database keytree/values")
+};
+
+static int manager_dbput(struct mansession *s, const struct message *m)
+{
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ const char *val = astman_get_header(m, "Val");
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified");
+ return 0;
+ }
+ if (ast_strlen_zero(key)) {
+ astman_send_error(s, m, "No key specified");
+ return 0;
+ }
+
+ res = ast_db_put(family, key, S_OR(val, ""));
+ if (res) {
+ astman_send_error(s, m, "Failed to update entry");
+ } else {
+ astman_send_ack(s, m, "Updated database successfully");
+ }
+ return 0;
+}
+
+static int manager_dbget(struct mansession *s, const struct message *m)
+{
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ char tmp[256];
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified.");
+ return 0;
+ }
+ if (ast_strlen_zero(key)) {
+ astman_send_error(s, m, "No key specified.");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
+
+ res = ast_db_get(family, key, tmp, sizeof(tmp));
+ if (res) {
+ astman_send_error(s, m, "Database entry not found");
+ } else {
+ astman_send_ack(s, m, "Result will follow");
+ astman_append(s, "Event: DBGetResponse\r\n"
+ "Family: %s\r\n"
+ "Key: %s\r\n"
+ "Val: %s\r\n"
+ "%s"
+ "\r\n",
+ family, key, tmp, idText);
+ }
+ return 0;
+}
+
+static int manager_dbdel(struct mansession *s, const struct message *m)
+{
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified.");
+ return 0;
+ }
+
+ if (ast_strlen_zero(key)) {
+ astman_send_error(s, m, "No key specified.");
+ return 0;
+ }
+
+ res = ast_db_del(family, key);
+ if (res)
+ astman_send_error(s, m, "Database entry not found");
+ else
+ astman_send_ack(s, m, "Key deleted successfully");
+
+ return 0;
+}
+
+static int manager_dbdeltree(struct mansession *s, const struct message *m)
+{
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified.");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(key))
+ res = ast_db_deltree(family, key);
+ else
+ res = ast_db_deltree(family, NULL);
+
+ if (res < 0)
+ astman_send_error(s, m, "Database entry not found");
+ else
+ astman_send_ack(s, m, "Key tree deleted successfully");
+
+ return 0;
+}
+
+int astdb_init(void)
+{
+ dbinit();
+ ast_cli_register_multiple(cli_database, sizeof(cli_database) / sizeof(struct ast_cli_entry));
+ ast_manager_register("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget, "Get DB Entry");
+ ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry");
+ ast_manager_register("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel, "Delete DB Entry");
+ ast_manager_register("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree, "Delete DB Tree");
+ return 0;
+}
diff --git a/trunk/main/db1-ast/Makefile b/trunk/main/db1-ast/Makefile
new file mode 100644
index 000000000..56657f88f
--- /dev/null
+++ b/trunk/main/db1-ast/Makefile
@@ -0,0 +1,72 @@
+# @(#)Makefile 8.9 (Berkeley) 7/14/94
+
+LIBDB= libdb1.a
+ARCH=$(shell uname -m)
+ifeq ($(ARCH),alpha)
+SOVER=2.1
+else
+SOVER=2
+endif
+
+ifeq ($(OSARCH),Darwin)
+ OSARCH_DEFINE+=-D__Darwin__
+endif
+
+LIBDBSO=libdb.so.$(SOVER)
+PROG= db_dump185
+OBJ1= hash/hash.o hash/hash_bigkey.o hash/hash_buf.o hash/hash_func.o hash/hash_log2.o hash/hash_page.o \
+ hash/ndbm.o
+OBJ2= btree/bt_close.o btree/bt_conv.o btree/bt_debug.o btree/bt_delete.o btree/bt_get.o btree/bt_open.o \
+ btree/bt_overflow.o btree/bt_page.o btree/bt_put.o btree/bt_search.o btree/bt_seq.o btree/bt_split.o \
+ btree/bt_utils.o
+OBJ3= db/db.o
+OBJ4= mpool/mpool.o
+OBJ5= recno/rec_close.o recno/rec_delete.o recno/rec_get.o recno/rec_open.o recno/rec_put.o recno/rec_search.o \
+ recno/rec_seq.o recno/rec_utils.o
+MISC=
+OBJS= $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ4) $(OBJ5) $(MISC)
+SHOBJS= $(patsubst %.o,%.os,$(OBJS))
+
+include $(ASTTOPDIR)/Makefile.rules
+
+all: $(LIBDB) #$(LIBDBSO) $(PROG)
+
+$(LIBDB): $(OBJS)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+$(LIBDBSO): $(SHOBJS)
+ $(CC) -Wl,-O1 -Wl,--version-script=libdb.map -Wl,-soname=$(LIBDBSO) -shared -o $@ $^
+ ln -sf $@ libdb.so
+
+$(PROG): db_dump185.o $(LIBDBSO)
+ $(CC) -o $@ db_dump185.o -L. -ldb
+
+clean-depend:
+
+clean:
+ rm -f $(LIBDB) $(LIBDBSO) $(OBJS) $(SHOBJS)
+ rm -f *.s *.i
+
+ASTCFLAGS:=-Wall -D__DBINTERFACE_PRIVATE -I. -I.. -Iinclude -Ihash -Ibtree -Irecno $(ASTCFLAGS)
+
+OSTYPE=$(shell uname -s)
+ifeq ($(OSTYPE),SunOS)
+ASTCFLAGS+=-I../../include -I../../include/solaris-compat -DSOLARIS
+endif
+
+db_dump185.o: db_dump185.c
+ $(CL) -o $@ $<
+x%.o: hash/%.c
+ $(CL) -Ihash $(OSARCH_DEFINE) -o $@ $<
+%.os: hash/%.c
+ $(CL) -Ihash -fPIC -o $@ $<
+x%.o: btree/%.c
+ $(CL) -Ibtree -o $@ $<
+%.os: btree/%.c
+ $(CL) -Ibtree -fPIC -o $@ $<
+x%.o: recno/%.c
+ $(CL) -Irecno -o $@ $<
+%.os: recno/%.c
+ $(CL) -Irecno -fPIC -o $@ $<
diff --git a/trunk/main/db1-ast/btree/bt_close.c b/trunk/main/db1-ast/btree/bt_close.c
new file mode 100644
index 000000000..67a6e5340
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_close.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_close.c 8.7 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int bt_meta __P((BTREE *));
+
+/*
+ * BT_CLOSE -- Close a btree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__bt_close(dbp)
+ DB *dbp;
+{
+ BTREE *t;
+ int fd;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Sync the tree. */
+ if (__bt_sync(dbp, 0) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Close the memory pool. */
+ if (mpool_close(t->bt_mp) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Free random memory. */
+ if (t->bt_cursor.key.data != NULL) {
+ free(t->bt_cursor.key.data);
+ t->bt_cursor.key.size = 0;
+ t->bt_cursor.key.data = NULL;
+ }
+ if (t->bt_rkey.data) {
+ free(t->bt_rkey.data);
+ t->bt_rkey.size = 0;
+ t->bt_rkey.data = NULL;
+ }
+ if (t->bt_rdata.data) {
+ free(t->bt_rdata.data);
+ t->bt_rdata.size = 0;
+ t->bt_rdata.data = NULL;
+ }
+
+ fd = t->bt_fd;
+ free(t);
+ free(dbp);
+ return (close(fd) ? RET_ERROR : RET_SUCCESS);
+}
+
+/*
+ * BT_SYNC -- sync the btree to disk.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__bt_sync(dbp, flags)
+ const DB *dbp;
+ u_int flags;
+{
+ BTREE *t;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Sync doesn't currently take any flags. */
+ if (flags != 0) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (F_ISSET(t, B_INMEM | B_RDONLY) || !F_ISSET(t, B_MODIFIED))
+ return (RET_SUCCESS);
+
+ if (F_ISSET(t, B_METADIRTY) && bt_meta(t) == RET_ERROR)
+ return (RET_ERROR);
+
+ if ((status = mpool_sync(t->bt_mp)) == RET_SUCCESS)
+ F_CLR(t, B_MODIFIED);
+
+ return (status);
+}
+
+/*
+ * BT_META -- write the tree meta data to disk.
+ *
+ * Parameters:
+ * t: tree
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+bt_meta(t)
+ BTREE *t;
+{
+ BTMETA m;
+ void *p;
+
+ if ((p = mpool_get(t->bt_mp, P_META, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Fill in metadata. */
+ m.magic = BTREEMAGIC;
+ m.version = BTREEVERSION;
+ m.psize = t->bt_psize;
+ m.free = t->bt_free;
+ m.nrecs = t->bt_nrecs;
+ m.flags = F_ISSET(t, SAVEMETA);
+
+ memmove(p, &m, sizeof(BTMETA));
+ mpool_put(t->bt_mp, p, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/btree/bt_conv.c b/trunk/main/db1-ast/btree/bt_conv.c
new file mode 100644
index 000000000..d2ebdc57b
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_conv.c
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_conv.c 8.5 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static void mswap __P((PAGE *));
+
+/*
+ * __BT_BPGIN, __BT_BPGOUT --
+ * Convert host-specific number layout to/from the host-independent
+ * format stored on disk.
+ *
+ * Parameters:
+ * t: tree
+ * pg: page number
+ * h: page to convert
+ */
+void
+__bt_pgin(t, pg, pp)
+ void *t;
+ pgno_t pg;
+ void *pp;
+{
+ PAGE *h;
+ indx_t i, top;
+ u_char flags;
+ char *p;
+
+ if (!F_ISSET(((BTREE *)t), B_NEEDSWAP))
+ return;
+ if (pg == P_META) {
+ mswap(pp);
+ return;
+ }
+
+ h = pp;
+ M_32_SWAP(h->pgno);
+ M_32_SWAP(h->prevpg);
+ M_32_SWAP(h->nextpg);
+ M_32_SWAP(h->flags);
+ M_16_SWAP(h->lower);
+ M_16_SWAP(h->upper);
+
+ top = NEXTINDEX(h);
+ if ((h->flags & P_TYPE) == P_BINTERNAL)
+ for (i = 0; i < top; i++) {
+ M_16_SWAP(h->linp[i]);
+ p = (char *)GETBINTERNAL(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ if (*(u_char *)p & P_BIGKEY) {
+ p += sizeof(u_char);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ }
+ else if ((h->flags & P_TYPE) == P_BLEAF)
+ for (i = 0; i < top; i++) {
+ M_16_SWAP(h->linp[i]);
+ p = (char *)GETBLEAF(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ flags = *(u_char *)p;
+ if (flags & (P_BIGKEY | P_BIGDATA)) {
+ p += sizeof(u_char);
+ if (flags & P_BIGKEY) {
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ if (flags & P_BIGDATA) {
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ }
+ }
+}
+
+void
+__bt_pgout(t, pg, pp)
+ void *t;
+ pgno_t pg;
+ void *pp;
+{
+ PAGE *h;
+ indx_t i, top;
+ u_char flags;
+ char *p;
+
+ if (!F_ISSET(((BTREE *)t), B_NEEDSWAP))
+ return;
+ if (pg == P_META) {
+ mswap(pp);
+ return;
+ }
+
+ h = pp;
+ top = NEXTINDEX(h);
+ if ((h->flags & P_TYPE) == P_BINTERNAL)
+ for (i = 0; i < top; i++) {
+ p = (char *)GETBINTERNAL(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ if (*(u_char *)p & P_BIGKEY) {
+ p += sizeof(u_char);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ M_16_SWAP(h->linp[i]);
+ }
+ else if ((h->flags & P_TYPE) == P_BLEAF)
+ for (i = 0; i < top; i++) {
+ p = (char *)GETBLEAF(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ flags = *(u_char *)p;
+ if (flags & (P_BIGKEY | P_BIGDATA)) {
+ p += sizeof(u_char);
+ if (flags & P_BIGKEY) {
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ if (flags & P_BIGDATA) {
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ }
+ M_16_SWAP(h->linp[i]);
+ }
+
+ M_32_SWAP(h->pgno);
+ M_32_SWAP(h->prevpg);
+ M_32_SWAP(h->nextpg);
+ M_32_SWAP(h->flags);
+ M_16_SWAP(h->lower);
+ M_16_SWAP(h->upper);
+}
+
+/*
+ * MSWAP -- Actually swap the bytes on the meta page.
+ *
+ * Parameters:
+ * p: page to convert
+ */
+static void
+mswap(pg)
+ PAGE *pg;
+{
+ char *p;
+
+ p = (char *)pg;
+ P_32_SWAP(p); /* magic */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* version */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* psize */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* free */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* nrecs */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* flags */
+ p += sizeof(u_int32_t);
+}
diff --git a/trunk/main/db1-ast/btree/bt_debug.c b/trunk/main/db1-ast/btree/bt_debug.c
new file mode 100644
index 000000000..e035851a8
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_debug.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_debug.c 8.5 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef DEBUG
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * BT_DUMP -- Dump the tree
+ *
+ * Parameters:
+ * dbp: pointer to the DB
+ */
+void
+__bt_dump(dbp)
+ DB *dbp;
+{
+ BTREE *t;
+ PAGE *h;
+ pgno_t i;
+ char *sep;
+
+ t = dbp->internal;
+ (void)fprintf(stderr, "%s: pgsz %d",
+ F_ISSET(t, B_INMEM) ? "memory" : "disk", t->bt_psize);
+ if (F_ISSET(t, R_RECNO))
+ (void)fprintf(stderr, " keys %lu", t->bt_nrecs);
+#undef X
+#define X(flag, name) \
+ if (F_ISSET(t, flag)) { \
+ (void)fprintf(stderr, "%s%s", sep, name); \
+ sep = ", "; \
+ }
+ if (t->flags != 0) {
+ sep = " flags (";
+ X(R_FIXLEN, "FIXLEN");
+ X(B_INMEM, "INMEM");
+ X(B_NODUPS, "NODUPS");
+ X(B_RDONLY, "RDONLY");
+ X(R_RECNO, "RECNO");
+ X(B_METADIRTY,"METADIRTY");
+ (void)fprintf(stderr, ")\n");
+ }
+#undef X
+
+ for (i = P_ROOT; (h = mpool_get(t->bt_mp, i, 0)) != NULL; ++i) {
+ __bt_dpage(h);
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+}
+
+/*
+ * BT_DMPAGE -- Dump the meta page
+ *
+ * Parameters:
+ * h: pointer to the PAGE
+ */
+void
+__bt_dmpage(h)
+ PAGE *h;
+{
+ BTMETA *m;
+ char *sep;
+
+ m = (BTMETA *)h;
+ (void)fprintf(stderr, "magic %lx\n", m->magic);
+ (void)fprintf(stderr, "version %lu\n", m->version);
+ (void)fprintf(stderr, "psize %lu\n", m->psize);
+ (void)fprintf(stderr, "free %lu\n", m->free);
+ (void)fprintf(stderr, "nrecs %lu\n", m->nrecs);
+ (void)fprintf(stderr, "flags %lu", m->flags);
+#undef X
+#define X(flag, name) \
+ if (m->flags & flag) { \
+ (void)fprintf(stderr, "%s%s", sep, name); \
+ sep = ", "; \
+ }
+ if (m->flags) {
+ sep = " (";
+ X(B_NODUPS, "NODUPS");
+ X(R_RECNO, "RECNO");
+ (void)fprintf(stderr, ")");
+ }
+}
+
+/*
+ * BT_DNPAGE -- Dump the page
+ *
+ * Parameters:
+ * n: page number to dump.
+ */
+void
+__bt_dnpage(dbp, pgno)
+ DB *dbp;
+ pgno_t pgno;
+{
+ BTREE *t;
+ PAGE *h;
+
+ t = dbp->internal;
+ if ((h = mpool_get(t->bt_mp, pgno, 0)) != NULL) {
+ __bt_dpage(h);
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+}
+
+/*
+ * BT_DPAGE -- Dump the page
+ *
+ * Parameters:
+ * h: pointer to the PAGE
+ */
+void
+__bt_dpage(h)
+ PAGE *h;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ RINTERNAL *ri;
+ RLEAF *rl;
+ indx_t cur, top;
+ char *sep;
+
+ (void)fprintf(stderr, " page %d: (", h->pgno);
+#undef X
+#define X(flag, name) \
+ if (h->flags & flag) { \
+ (void)fprintf(stderr, "%s%s", sep, name); \
+ sep = ", "; \
+ }
+ sep = "";
+ X(P_BINTERNAL, "BINTERNAL") /* types */
+ X(P_BLEAF, "BLEAF")
+ X(P_RINTERNAL, "RINTERNAL") /* types */
+ X(P_RLEAF, "RLEAF")
+ X(P_OVERFLOW, "OVERFLOW")
+ X(P_PRESERVE, "PRESERVE");
+ (void)fprintf(stderr, ")\n");
+#undef X
+
+ (void)fprintf(stderr, "\tprev %2d next %2d", h->prevpg, h->nextpg);
+ if (h->flags & P_OVERFLOW)
+ return;
+
+ top = NEXTINDEX(h);
+ (void)fprintf(stderr, " lower %3d upper %3d nextind %d\n",
+ h->lower, h->upper, top);
+ for (cur = 0; cur < top; cur++) {
+ (void)fprintf(stderr, "\t[%03d] %4d ", cur, h->linp[cur]);
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ bi = GETBINTERNAL(h, cur);
+ (void)fprintf(stderr,
+ "size %03d pgno %03d", bi->ksize, bi->pgno);
+ if (bi->flags & P_BIGKEY)
+ (void)fprintf(stderr, " (indirect)");
+ else if (bi->ksize)
+ (void)fprintf(stderr,
+ " {%.*s}", (int)bi->ksize, bi->bytes);
+ break;
+ case P_RINTERNAL:
+ ri = GETRINTERNAL(h, cur);
+ (void)fprintf(stderr, "entries %03d pgno %03d",
+ ri->nrecs, ri->pgno);
+ break;
+ case P_BLEAF:
+ bl = GETBLEAF(h, cur);
+ if (bl->flags & P_BIGKEY)
+ (void)fprintf(stderr,
+ "big key page %lu size %u/",
+ *(pgno_t *)bl->bytes,
+ *(u_int32_t *)(bl->bytes + sizeof(pgno_t)));
+ else if (bl->ksize)
+ (void)fprintf(stderr, "%s/", bl->bytes);
+ if (bl->flags & P_BIGDATA)
+ (void)fprintf(stderr,
+ "big data page %lu size %u",
+ *(pgno_t *)(bl->bytes + bl->ksize),
+ *(u_int32_t *)(bl->bytes + bl->ksize +
+ sizeof(pgno_t)));
+ else if (bl->dsize)
+ (void)fprintf(stderr, "%.*s",
+ (int)bl->dsize, bl->bytes + bl->ksize);
+ break;
+ case P_RLEAF:
+ rl = GETRLEAF(h, cur);
+ if (rl->flags & P_BIGDATA)
+ (void)fprintf(stderr,
+ "big data page %lu size %u",
+ *(pgno_t *)rl->bytes,
+ *(u_int32_t *)(rl->bytes + sizeof(pgno_t)));
+ else if (rl->dsize)
+ (void)fprintf(stderr,
+ "%.*s", (int)rl->dsize, rl->bytes);
+ break;
+ }
+ (void)fprintf(stderr, "\n");
+ }
+}
+#endif
+
+#ifdef STATISTICS
+/*
+ * BT_STAT -- Gather/print the tree statistics
+ *
+ * Parameters:
+ * dbp: pointer to the DB
+ */
+void
+__bt_stat(dbp)
+ DB *dbp;
+{
+ extern u_long bt_cache_hit, bt_cache_miss, bt_pfxsaved, bt_rootsplit;
+ extern u_long bt_sortsplit, bt_split;
+ BTREE *t;
+ PAGE *h;
+ pgno_t i, pcont, pinternal, pleaf;
+ u_long ifree, lfree, nkeys;
+ int levels;
+
+ t = dbp->internal;
+ pcont = pinternal = pleaf = 0;
+ nkeys = ifree = lfree = 0;
+ for (i = P_ROOT; (h = mpool_get(t->bt_mp, i, 0)) != NULL; ++i) {
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ case P_RINTERNAL:
+ ++pinternal;
+ ifree += h->upper - h->lower;
+ break;
+ case P_BLEAF:
+ case P_RLEAF:
+ ++pleaf;
+ lfree += h->upper - h->lower;
+ nkeys += NEXTINDEX(h);
+ break;
+ case P_OVERFLOW:
+ ++pcont;
+ break;
+ }
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+
+ /* Count the levels of the tree. */
+ for (i = P_ROOT, levels = 0 ;; ++levels) {
+ h = mpool_get(t->bt_mp, i, 0);
+ if (h->flags & (P_BLEAF|P_RLEAF)) {
+ if (levels == 0)
+ levels = 1;
+ (void)mpool_put(t->bt_mp, h, 0);
+ break;
+ }
+ i = F_ISSET(t, R_RECNO) ?
+ GETRINTERNAL(h, 0)->pgno :
+ GETBINTERNAL(h, 0)->pgno;
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+
+ (void)fprintf(stderr, "%d level%s with %ld keys",
+ levels, levels == 1 ? "" : "s", nkeys);
+ if (F_ISSET(t, R_RECNO))
+ (void)fprintf(stderr, " (%ld header count)", t->bt_nrecs);
+ (void)fprintf(stderr,
+ "\n%lu pages (leaf %ld, internal %ld, overflow %ld)\n",
+ pinternal + pleaf + pcont, pleaf, pinternal, pcont);
+ (void)fprintf(stderr, "%ld cache hits, %ld cache misses\n",
+ bt_cache_hit, bt_cache_miss);
+ (void)fprintf(stderr, "%ld splits (%ld root splits, %ld sort splits)\n",
+ bt_split, bt_rootsplit, bt_sortsplit);
+ pleaf *= t->bt_psize - BTDATAOFF;
+ if (pleaf)
+ (void)fprintf(stderr,
+ "%.0f%% leaf fill (%ld bytes used, %ld bytes free)\n",
+ ((double)(pleaf - lfree) / pleaf) * 100,
+ pleaf - lfree, lfree);
+ pinternal *= t->bt_psize - BTDATAOFF;
+ if (pinternal)
+ (void)fprintf(stderr,
+ "%.0f%% internal fill (%ld bytes used, %ld bytes free\n",
+ ((double)(pinternal - ifree) / pinternal) * 100,
+ pinternal - ifree, ifree);
+ if (bt_pfxsaved)
+ (void)fprintf(stderr, "prefix checking removed %lu bytes.\n",
+ bt_pfxsaved);
+}
+#endif
diff --git a/trunk/main/db1-ast/btree/bt_delete.c b/trunk/main/db1-ast/btree/bt_delete.c
new file mode 100644
index 000000000..e816c432a
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_delete.c
@@ -0,0 +1,657 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_delete.c 8.13 (Berkeley) 7/28/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int __bt_bdelete __P((BTREE *, const DBT *));
+static int __bt_curdel __P((BTREE *, const DBT *, PAGE *, u_int));
+static int __bt_pdelete __P((BTREE *, PAGE *));
+static int __bt_relink __P((BTREE *, PAGE *));
+static int __bt_stkacq __P((BTREE *, PAGE **, CURSOR *));
+
+/*
+ * __bt_delete
+ * Delete the item(s) referenced by a key.
+ *
+ * Return RET_SPECIAL if the key is not found.
+ */
+int
+__bt_delete(dbp, key, flags)
+ const DB *dbp;
+ const DBT *key;
+ u_int flags;
+{
+ BTREE *t;
+ CURSOR *c;
+ PAGE *h;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Check for change to a read-only tree. */
+ if (F_ISSET(t, B_RDONLY)) {
+ errno = EPERM;
+ return (RET_ERROR);
+ }
+
+ switch (flags) {
+ case 0:
+ status = __bt_bdelete(t, key);
+ break;
+ case R_CURSOR:
+ /*
+ * If flags is R_CURSOR, delete the cursor. Must already
+ * have started a scan and not have already deleted it.
+ */
+ c = &t->bt_cursor;
+ if (F_ISSET(c, CURS_INIT)) {
+ if (F_ISSET(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE))
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
+ return (RET_ERROR);
+
+ /*
+ * If the page is about to be emptied, we'll need to
+ * delete it, which means we have to acquire a stack.
+ */
+ if (NEXTINDEX(h) == 1)
+ if (__bt_stkacq(t, &h, &t->bt_cursor))
+ return (RET_ERROR);
+
+ status = __bt_dleaf(t, NULL, h, c->pg.index);
+
+ if (NEXTINDEX(h) == 0 && status == RET_SUCCESS) {
+ if (__bt_pdelete(t, h))
+ return (RET_ERROR);
+ } else
+ mpool_put(t->bt_mp,
+ h, status == RET_SUCCESS ? MPOOL_DIRTY : 0);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+ if (status == RET_SUCCESS)
+ F_SET(t, B_MODIFIED);
+ return (status);
+}
+
+/*
+ * __bt_stkacq --
+ * Acquire a stack so we can delete a cursor entry.
+ *
+ * Parameters:
+ * t: tree
+ * hp: pointer to current, pinned PAGE pointer
+ * c: pointer to the cursor
+ *
+ * Returns:
+ * 0 on success, 1 on failure
+ */
+static int
+__bt_stkacq(t, hp, c)
+ BTREE *t;
+ PAGE **hp;
+ CURSOR *c;
+{
+ BINTERNAL *bi;
+ EPG *e;
+ EPGNO *parent;
+ PAGE *h;
+ indx_t index = 0;
+ pgno_t pgno;
+ recno_t nextpg, prevpg;
+ int exact, level;
+
+ /*
+ * Find the first occurrence of the key in the tree. Toss the
+ * currently locked page so we don't hit an already-locked page.
+ */
+ h = *hp;
+ mpool_put(t->bt_mp, h, 0);
+ if ((e = __bt_search(t, &c->key, &exact)) == NULL)
+ return (1);
+ h = e->page;
+
+ /* See if we got it in one shot. */
+ if (h->pgno == c->pg.pgno)
+ goto ret;
+
+ /*
+ * Move right, looking for the page. At each move we have to move
+ * up the stack until we don't have to move to the next page. If
+ * we have to change pages at an internal level, we have to fix the
+ * stack back up.
+ */
+ while (h->pgno != c->pg.pgno) {
+ if ((nextpg = h->nextpg) == P_INVALID)
+ break;
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Move up the stack. */
+ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) {
+ /* Get the parent page. */
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ return (1);
+
+ /* Move to the next index. */
+ if (parent->index != NEXTINDEX(h) - 1) {
+ index = parent->index + 1;
+ BT_PUSH(t, h->pgno, index);
+ break;
+ }
+ mpool_put(t->bt_mp, h, 0);
+ }
+
+ /* Restore the stack. */
+ while (level--) {
+ /* Push the next level down onto the stack. */
+ bi = GETBINTERNAL(h, index);
+ pgno = bi->pgno;
+ BT_PUSH(t, pgno, 0);
+
+ /* Lose the currently pinned page. */
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Get the next level down. */
+ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL)
+ return (1);
+ index = 0;
+ }
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp, nextpg, 0)) == NULL)
+ return (1);
+ }
+
+ if (h->pgno == c->pg.pgno)
+ goto ret;
+
+ /* Reacquire the original stack. */
+ mpool_put(t->bt_mp, h, 0);
+ if ((e = __bt_search(t, &c->key, &exact)) == NULL)
+ return (1);
+ h = e->page;
+
+ /*
+ * Move left, looking for the page. At each move we have to move
+ * up the stack until we don't have to change pages to move to the
+ * next page. If we have to change pages at an internal level, we
+ * have to fix the stack back up.
+ */
+ while (h->pgno != c->pg.pgno) {
+ if ((prevpg = h->prevpg) == P_INVALID)
+ break;
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Move up the stack. */
+ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) {
+ /* Get the parent page. */
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ return (1);
+
+ /* Move to the next index. */
+ if (parent->index != 0) {
+ index = parent->index - 1;
+ BT_PUSH(t, h->pgno, index);
+ break;
+ }
+ mpool_put(t->bt_mp, h, 0);
+ }
+
+ /* Restore the stack. */
+ while (level--) {
+ /* Push the next level down onto the stack. */
+ bi = GETBINTERNAL(h, index);
+ pgno = bi->pgno;
+
+ /* Lose the currently pinned page. */
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Get the next level down. */
+ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL)
+ return (1);
+
+ index = NEXTINDEX(h) - 1;
+ BT_PUSH(t, pgno, index);
+ }
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp, prevpg, 0)) == NULL)
+ return (1);
+ }
+
+
+ret: mpool_put(t->bt_mp, h, 0);
+ return ((*hp = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL);
+}
+
+/*
+ * __bt_bdelete --
+ * Delete all key/data pairs matching the specified key.
+ *
+ * Parameters:
+ * t: tree
+ * key: key to delete
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+static int
+__bt_bdelete(t, key)
+ BTREE *t;
+ const DBT *key;
+{
+ EPG *e;
+ PAGE *h;
+ int deleted, exact, redo;
+
+ deleted = 0;
+
+ /* Find any matching record; __bt_search pins the page. */
+loop: if ((e = __bt_search(t, key, &exact)) == NULL)
+ return (deleted ? RET_SUCCESS : RET_ERROR);
+ if (!exact) {
+ mpool_put(t->bt_mp, e->page, 0);
+ return (deleted ? RET_SUCCESS : RET_SPECIAL);
+ }
+
+ /*
+ * Delete forward, then delete backward, from the found key. If
+ * there are duplicates and we reach either side of the page, do
+ * the key search again, so that we get them all.
+ */
+ redo = 0;
+ h = e->page;
+ do {
+ if (__bt_dleaf(t, key, h, e->index)) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ if (F_ISSET(t, B_NODUPS)) {
+ if (NEXTINDEX(h) == 0) {
+ if (__bt_pdelete(t, h))
+ return (RET_ERROR);
+ } else
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+ }
+ deleted = 1;
+ } while (e->index < NEXTINDEX(h) && __bt_cmp(t, key, e) == 0);
+
+ /* Check for right-hand edge of the page. */
+ if (e->index == NEXTINDEX(h))
+ redo = 1;
+
+ /* Delete from the key to the beginning of the page. */
+ while (e->index-- > 0) {
+ if (__bt_cmp(t, key, e) != 0)
+ break;
+ if (__bt_dleaf(t, key, h, e->index) == RET_ERROR) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ if (e->index == 0)
+ redo = 1;
+ }
+
+ /* Check for an empty page. */
+ if (NEXTINDEX(h) == 0) {
+ if (__bt_pdelete(t, h))
+ return (RET_ERROR);
+ goto loop;
+ }
+
+ /* Put the page. */
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ if (redo)
+ goto loop;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_pdelete --
+ * Delete a single page from the tree.
+ *
+ * Parameters:
+ * t: tree
+ * h: leaf page
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ *
+ * Side-effects:
+ * mpool_put's the page
+ */
+static int
+__bt_pdelete(t, h)
+ BTREE *t;
+ PAGE *h;
+{
+ BINTERNAL *bi;
+ PAGE *pg;
+ EPGNO *parent;
+ indx_t cnt, index, *ip, offset;
+ u_int32_t nksize;
+ char *from;
+
+ /*
+ * Walk the parent page stack -- a LIFO stack of the pages that were
+ * traversed when we searched for the page where the delete occurred.
+ * Each stack entry is a page number and a page index offset. The
+ * offset is for the page traversed on the search. We've just deleted
+ * a page, so we have to delete the key from the parent page.
+ *
+ * If the delete from the parent page makes it empty, this process may
+ * continue all the way up the tree. We stop if we reach the root page
+ * (which is never deleted, it's just not worth the effort) or if the
+ * delete does not empty the page.
+ */
+ while ((parent = BT_POP(t)) != NULL) {
+ /* Get the parent page. */
+ if ((pg = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ return (RET_ERROR);
+
+ index = parent->index;
+ bi = GETBINTERNAL(pg, index);
+
+ /* Free any overflow pages. */
+ if (bi->flags & P_BIGKEY &&
+ __ovfl_delete(t, bi->bytes) == RET_ERROR) {
+ mpool_put(t->bt_mp, pg, 0);
+ return (RET_ERROR);
+ }
+
+ /*
+ * Free the parent if it has only the one key and it's not the
+ * root page. If it's the rootpage, turn it back into an empty
+ * leaf page.
+ */
+ if (NEXTINDEX(pg) == 1) {
+ if (pg->pgno == P_ROOT) {
+ pg->lower = BTDATAOFF;
+ pg->upper = t->bt_psize;
+ pg->flags = P_BLEAF;
+ } else {
+ if (__bt_relink(t, pg) || __bt_free(t, pg))
+ return (RET_ERROR);
+ continue;
+ }
+ } else {
+ /* Pack remaining key items at the end of the page. */
+ nksize = NBINTERNAL(bi->ksize);
+ from = (char *)pg + pg->upper;
+ memmove(from + nksize, from, (char *)bi - from);
+ pg->upper += nksize;
+
+ /* Adjust indices' offsets, shift the indices down. */
+ offset = pg->linp[index];
+ for (cnt = index, ip = &pg->linp[0]; cnt--; ++ip)
+ if (ip[0] < offset)
+ ip[0] += nksize;
+ for (cnt = NEXTINDEX(pg) - index; --cnt; ++ip)
+ ip[0] = ip[1] < offset ? ip[1] + nksize : ip[1];
+ pg->lower -= sizeof(indx_t);
+ }
+
+ mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
+ break;
+ }
+
+ /* Free the leaf page, as long as it wasn't the root. */
+ if (h->pgno == P_ROOT) {
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+ }
+ return (__bt_relink(t, h) || __bt_free(t, h));
+}
+
+/*
+ * __bt_dleaf --
+ * Delete a single record from a leaf page.
+ *
+ * Parameters:
+ * t: tree
+ * key: referenced key
+ * h: page
+ * index: index on page to delete
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__bt_dleaf(t, key, h, index)
+ BTREE *t;
+ const DBT *key;
+ PAGE *h;
+ u_int index;
+{
+ BLEAF *bl;
+ indx_t cnt, *ip, offset;
+ u_int32_t nbytes;
+ void *to;
+ char *from;
+
+ /* If this record is referenced by the cursor, delete the cursor. */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
+ t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index == index &&
+ __bt_curdel(t, key, h, index))
+ return (RET_ERROR);
+
+ /* If the entry uses overflow pages, make them available for reuse. */
+ to = bl = GETBLEAF(h, index);
+ if (bl->flags & P_BIGKEY && __ovfl_delete(t, bl->bytes) == RET_ERROR)
+ return (RET_ERROR);
+ if (bl->flags & P_BIGDATA &&
+ __ovfl_delete(t, bl->bytes + bl->ksize) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Pack the remaining key/data items at the end of the page. */
+ nbytes = NBLEAF(bl);
+ from = (char *)h + h->upper;
+ memmove(from + nbytes, from, (char *)to - from);
+ h->upper += nbytes;
+
+ /* Adjust the indices' offsets, shift the indices down. */
+ offset = h->linp[index];
+ for (cnt = index, ip = &h->linp[0]; cnt--; ++ip)
+ if (ip[0] < offset)
+ ip[0] += nbytes;
+ for (cnt = NEXTINDEX(h) - index; --cnt; ++ip)
+ ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1];
+ h->lower -= sizeof(indx_t);
+
+ /* If the cursor is on this page, adjust it as necessary. */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
+ t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index > index)
+ --t->bt_cursor.pg.index;
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_curdel --
+ * Delete the cursor.
+ *
+ * Parameters:
+ * t: tree
+ * key: referenced key (or NULL)
+ * h: page
+ * index: index on page to delete
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+static int
+__bt_curdel(t, key, h, index)
+ BTREE *t;
+ const DBT *key;
+ PAGE *h;
+ u_int index;
+{
+ CURSOR *c;
+ EPG e;
+ PAGE *pg;
+ int curcopy, status;
+
+ /*
+ * If there are duplicates, move forward or backward to one.
+ * Otherwise, copy the key into the cursor area.
+ */
+ c = &t->bt_cursor;
+ F_CLR(c, CURS_AFTER | CURS_BEFORE | CURS_ACQUIRE);
+
+ curcopy = 0;
+ if (!F_ISSET(t, B_NODUPS)) {
+ /*
+ * We're going to have to do comparisons. If we weren't
+ * provided a copy of the key, i.e. the user is deleting
+ * the current cursor position, get one.
+ */
+ if (key == NULL) {
+ e.page = h;
+ e.index = index;
+ if ((status = __bt_ret(t, &e,
+ &c->key, &c->key, NULL, NULL, 1)) != RET_SUCCESS)
+ return (status);
+ curcopy = 1;
+ key = &c->key;
+ }
+ /* Check previous key, if not at the beginning of the page. */
+ if (index > 0) {
+ e.page = h;
+ e.index = index - 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_BEFORE);
+ goto dup2;
+ }
+ }
+ /* Check next key, if not at the end of the page. */
+ if (index < NEXTINDEX(h) - 1) {
+ e.page = h;
+ e.index = index + 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_AFTER);
+ goto dup2;
+ }
+ }
+ /* Check previous key if at the beginning of the page. */
+ if (index == 0 && h->prevpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
+ return (RET_ERROR);
+ e.page = pg;
+ e.index = NEXTINDEX(pg) - 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_BEFORE);
+ goto dup1;
+ }
+ mpool_put(t->bt_mp, pg, 0);
+ }
+ /* Check next key if at the end of the page. */
+ if (index == NEXTINDEX(h) - 1 && h->nextpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
+ return (RET_ERROR);
+ e.page = pg;
+ e.index = 0;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_AFTER);
+dup1: mpool_put(t->bt_mp, pg, 0);
+dup2: c->pg.pgno = e.page->pgno;
+ c->pg.index = e.index;
+ return (RET_SUCCESS);
+ }
+ mpool_put(t->bt_mp, pg, 0);
+ }
+ }
+ e.page = h;
+ e.index = index;
+ if (curcopy || (status =
+ __bt_ret(t, &e, &c->key, &c->key, NULL, NULL, 1)) == RET_SUCCESS) {
+ F_SET(c, CURS_ACQUIRE);
+ return (RET_SUCCESS);
+ }
+ return (status);
+}
+
+/*
+ * __bt_relink --
+ * Link around a deleted page.
+ *
+ * Parameters:
+ * t: tree
+ * h: page to be deleted
+ */
+static int
+__bt_relink(t, h)
+ BTREE *t;
+ PAGE *h;
+{
+ PAGE *pg;
+
+ if (h->nextpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
+ return (RET_ERROR);
+ pg->prevpg = h->prevpg;
+ mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
+ }
+ if (h->prevpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
+ return (RET_ERROR);
+ pg->nextpg = h->nextpg;
+ mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
+ }
+ return (0);
+}
diff --git a/trunk/main/db1-ast/btree/bt_get.c b/trunk/main/db1-ast/btree/bt_get.c
new file mode 100644
index 000000000..b5e18022c
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_get.c
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_get.c 8.6 (Berkeley) 7/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * __BT_GET -- Get a record from the btree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key to find
+ * data: data to return
+ * flag: currently unused
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+int
+__bt_get(dbp, key, data, flags)
+ const DB *dbp;
+ const DBT *key;
+ DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG *e;
+ int exact, status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Get currently doesn't take any flags. */
+ if (flags) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if ((e = __bt_search(t, key, &exact)) == NULL)
+ return (RET_ERROR);
+ if (!exact) {
+ mpool_put(t->bt_mp, e->page, 0);
+ return (RET_SPECIAL);
+ }
+
+ status = __bt_ret(t, e, NULL, NULL, data, &t->bt_rdata, 0);
+
+ /*
+ * If the user is doing concurrent access, we copied the
+ * key/data, toss the page.
+ */
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e->page, 0);
+ else
+ t->bt_pinned = e->page;
+ return (status);
+}
diff --git a/trunk/main/db1-ast/btree/bt_open.c b/trunk/main/db1-ast/btree/bt_open.c
new file mode 100644
index 000000000..5d40e4593
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_open.c
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_open.c 8.10 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Implementation of btree access method for 4.4BSD.
+ *
+ * The design here was originally based on that of the btree access method
+ * used in the Postgres database system at UC Berkeley. This implementation
+ * is wholly independent of the Postgres code.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+#ifdef DEBUG
+#undef MINPSIZE
+#define MINPSIZE 128
+#endif
+
+static int byteorder __P((void));
+static int nroot __P((BTREE *));
+static int tmp __P((void));
+
+/*
+ * __BT_OPEN -- Open a btree.
+ *
+ * Creates and fills a DB struct, and calls the routine that actually
+ * opens the btree.
+ *
+ * Parameters:
+ * fname: filename (NULL for in-memory trees)
+ * flags: open flag bits
+ * mode: open permission bits
+ * b: BTREEINFO pointer
+ *
+ * Returns:
+ * NULL on failure, pointer to DB on success.
+ *
+ */
+DB *
+__bt_open(fname, flags, mode, openinfo, dflags)
+ const char *fname;
+ int flags, mode, dflags;
+ const BTREEINFO *openinfo;
+{
+ struct stat sb;
+ BTMETA m;
+ BTREE *t;
+ BTREEINFO b;
+ DB *dbp;
+ pgno_t ncache;
+ ssize_t nr;
+ int machine_lorder;
+
+ t = NULL;
+
+ /*
+ * Intention is to make sure all of the user's selections are okay
+ * here and then use them without checking. Can't be complete, since
+ * we don't know the right page size, lorder or flags until the backing
+ * file is opened. Also, the file's page size can cause the cachesize
+ * to change.
+ */
+ machine_lorder = byteorder();
+ if (openinfo) {
+ b = *openinfo;
+
+ /* Flags: R_DUP. */
+ if (b.flags & ~(R_DUP))
+ goto einval;
+
+ /*
+ * Page size must be indx_t aligned and >= MINPSIZE. Default
+ * page size is set farther on, based on the underlying file
+ * transfer size.
+ */
+ if (b.psize &&
+ (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 ||
+ b.psize & (sizeof(indx_t) - 1)))
+ goto einval;
+
+ /* Minimum number of keys per page; absolute minimum is 2. */
+ if (b.minkeypage) {
+ if (b.minkeypage < 2)
+ goto einval;
+ } else
+ b.minkeypage = DEFMINKEYPAGE;
+
+ /* If no comparison, use default comparison and prefix. */
+ if (b.compare == NULL) {
+ b.compare = __bt_defcmp;
+ if (b.prefix == NULL)
+ b.prefix = __bt_defpfx;
+ }
+
+ if (b.lorder == 0)
+ b.lorder = machine_lorder;
+ } else {
+ b.compare = __bt_defcmp;
+ b.cachesize = 0;
+ b.flags = 0;
+ b.lorder = machine_lorder;
+ b.minkeypage = DEFMINKEYPAGE;
+ b.prefix = __bt_defpfx;
+ b.psize = 0;
+ }
+
+ /* Check for the ubiquitous PDP-11. */
+ if (b.lorder != BIG_ENDIAN && b.lorder != LITTLE_ENDIAN)
+ goto einval;
+
+ /* Allocate and initialize DB and BTREE structures. */
+ if ((t = (BTREE *)malloc(sizeof(BTREE))) == NULL)
+ goto err;
+ memset(t, 0, sizeof(BTREE));
+ t->bt_fd = -1; /* Don't close unopened fd on error. */
+ t->bt_lorder = b.lorder;
+ t->bt_order = NOT;
+ t->bt_cmp = b.compare;
+ t->bt_pfx = b.prefix;
+ t->bt_rfd = -1;
+
+ if ((t->bt_dbp = dbp = (DB *)malloc(sizeof(DB))) == NULL)
+ goto err;
+ memset(t->bt_dbp, 0, sizeof(DB));
+ if (t->bt_lorder != machine_lorder)
+ F_SET(t, B_NEEDSWAP);
+
+ dbp->type = DB_BTREE;
+ dbp->internal = t;
+ dbp->close = __bt_close;
+ dbp->del = __bt_delete;
+ dbp->fd = __bt_fd;
+ dbp->get = __bt_get;
+ dbp->put = __bt_put;
+ dbp->seq = __bt_seq;
+ dbp->sync = __bt_sync;
+
+ /*
+ * If no file name was supplied, this is an in-memory btree and we
+ * open a backing temporary file. Otherwise, it's a disk-based tree.
+ */
+ if (fname) {
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ F_SET(t, B_RDONLY);
+ break;
+ case O_RDWR:
+ break;
+ case O_WRONLY:
+ default:
+ goto einval;
+ }
+
+ if ((t->bt_fd = open(fname, flags, mode)) < 0)
+ goto err;
+
+ } else {
+ if ((flags & O_ACCMODE) != O_RDWR)
+ goto einval;
+ if ((t->bt_fd = tmp()) == -1)
+ goto err;
+ F_SET(t, B_INMEM);
+ }
+
+ if (fcntl(t->bt_fd, F_SETFD, 1) == -1)
+ goto err;
+
+ if (fstat(t->bt_fd, &sb))
+ goto err;
+ if (sb.st_size) {
+ if ((nr = read(t->bt_fd, &m, sizeof(BTMETA))) < 0)
+ goto err;
+ if (nr != sizeof(BTMETA))
+ goto eftype;
+
+ /*
+ * Read in the meta-data. This can change the notion of what
+ * the lorder, page size and flags are, and, when the page size
+ * changes, the cachesize value can change too. If the user
+ * specified the wrong byte order for an existing database, we
+ * don't bother to return an error, we just clear the NEEDSWAP
+ * bit.
+ */
+ if (m.magic == BTREEMAGIC)
+ F_CLR(t, B_NEEDSWAP);
+ else {
+ F_SET(t, B_NEEDSWAP);
+ M_32_SWAP(m.magic);
+ M_32_SWAP(m.version);
+ M_32_SWAP(m.psize);
+ M_32_SWAP(m.free);
+ M_32_SWAP(m.nrecs);
+ M_32_SWAP(m.flags);
+ }
+ if (m.magic != BTREEMAGIC || m.version != BTREEVERSION)
+ goto eftype;
+ if (m.psize < MINPSIZE || m.psize > MAX_PAGE_OFFSET + 1 ||
+ m.psize & (sizeof(indx_t) - 1))
+ goto eftype;
+ if (m.flags & ~SAVEMETA)
+ goto eftype;
+ b.psize = m.psize;
+ F_SET(t, m.flags);
+ t->bt_free = m.free;
+ t->bt_nrecs = m.nrecs;
+ } else {
+ /*
+ * Set the page size to the best value for I/O to this file.
+ * Don't overflow the page offset type.
+ */
+ if (b.psize == 0) {
+#ifdef _STATBUF_ST_BLKSIZE
+ b.psize = sb.st_blksize;
+#endif
+ if (b.psize < MINPSIZE)
+ b.psize = MINPSIZE;
+ if (b.psize > MAX_PAGE_OFFSET + 1)
+ b.psize = MAX_PAGE_OFFSET + 1;
+ }
+
+ /* Set flag if duplicates permitted. */
+ if (!(b.flags & R_DUP))
+ F_SET(t, B_NODUPS);
+
+ t->bt_free = P_INVALID;
+ t->bt_nrecs = 0;
+ F_SET(t, B_METADIRTY);
+ }
+
+ t->bt_psize = b.psize;
+
+ /* Set the cache size; must be a multiple of the page size. */
+ if (b.cachesize && b.cachesize & (b.psize - 1))
+ b.cachesize += (~b.cachesize & (b.psize - 1)) + 1;
+ if (b.cachesize < b.psize * MINCACHE)
+ b.cachesize = b.psize * MINCACHE;
+
+ /* Calculate number of pages to cache. */
+ ncache = (b.cachesize + t->bt_psize - 1) / t->bt_psize;
+
+ /*
+ * The btree data structure requires that at least two keys can fit on
+ * a page, but other than that there's no fixed requirement. The user
+ * specified a minimum number per page, and we translated that into the
+ * number of bytes a key/data pair can use before being placed on an
+ * overflow page. This calculation includes the page header, the size
+ * of the index referencing the leaf item and the size of the leaf item
+ * structure. Also, don't let the user specify a minkeypage such that
+ * a key/data pair won't fit even if both key and data are on overflow
+ * pages.
+ */
+ t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage -
+ (sizeof(indx_t) + NBLEAFDBT(0, 0));
+ if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t))
+ t->bt_ovflsize =
+ NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t);
+
+ /* Initialize the buffer pool. */
+ if ((t->bt_mp =
+ mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL)
+ goto err;
+ if (!F_ISSET(t, B_INMEM))
+ mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t);
+
+ /* Create a root page if new tree. */
+ if (nroot(t) == RET_ERROR)
+ goto err;
+
+ /* Global flags. */
+ if (dflags & DB_LOCK)
+ F_SET(t, B_DB_LOCK);
+ if (dflags & DB_SHMEM)
+ F_SET(t, B_DB_SHMEM);
+ if (dflags & DB_TXN)
+ F_SET(t, B_DB_TXN);
+
+ return (dbp);
+
+einval: errno = EINVAL;
+ goto err;
+
+eftype: errno = EFTYPE;
+ goto err;
+
+err: if (t) {
+ if (t->bt_dbp)
+ free(t->bt_dbp);
+ if (t->bt_fd != -1)
+ (void)close(t->bt_fd);
+ free(t);
+ }
+ return (NULL);
+}
+
+/*
+ * NROOT -- Create the root of a new tree.
+ *
+ * Parameters:
+ * t: tree
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+nroot(t)
+ BTREE *t;
+{
+ PAGE *meta, *root;
+ pgno_t npg;
+
+ if ((meta = mpool_get(t->bt_mp, 0, 0)) != NULL) {
+ mpool_put(t->bt_mp, meta, 0);
+ return (RET_SUCCESS);
+ }
+ if (errno != EINVAL) /* It's OK to not exist. */
+ return (RET_ERROR);
+ errno = 0;
+
+ if ((meta = mpool_new(t->bt_mp, &npg)) == NULL)
+ return (RET_ERROR);
+
+ if ((root = mpool_new(t->bt_mp, &npg)) == NULL)
+ return (RET_ERROR);
+
+ if (npg != P_ROOT)
+ return (RET_ERROR);
+ root->pgno = npg;
+ root->prevpg = root->nextpg = P_INVALID;
+ root->lower = BTDATAOFF;
+ root->upper = t->bt_psize;
+ root->flags = P_BLEAF;
+ memset(meta, 0, t->bt_psize);
+ mpool_put(t->bt_mp, meta, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, root, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
+
+static int
+tmp()
+{
+ sigset_t set, oset;
+ int fd;
+ const char *envtmp;
+ char *path;
+ static const char fmt[] = "%s/bt.XXXXXX";
+ size_t n;
+
+ envtmp = getenv("TMPDIR");
+ if (!envtmp)
+ envtmp = "/tmp";
+ n = strlen (envtmp) + sizeof fmt;
+#ifdef __GNUC__
+ path = __builtin_alloca(n);
+#else
+ path = malloc(n);
+#endif
+ (void)snprintf(path, n, fmt, envtmp ? envtmp : "/tmp");
+
+ (void)sigfillset(&set);
+ (void)sigprocmask(SIG_BLOCK, &set, &oset);
+ if ((fd = mkstemp(path)) != -1)
+ (void)unlink(path);
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+#ifndef __GNUC__
+ free(path);
+#endif
+ return(fd);
+}
+
+static int
+byteorder()
+{
+ u_int32_t x;
+ u_char *p;
+
+ x = 0x01020304;
+ p = (u_char *)&x;
+ switch (*p) {
+ case 1:
+ return (BIG_ENDIAN);
+ case 4:
+ return (LITTLE_ENDIAN);
+ default:
+ return (0);
+ }
+}
+
+int
+__bt_fd(dbp)
+ const DB *dbp;
+{
+ BTREE *t;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* In-memory database can't have a file descriptor. */
+ if (F_ISSET(t, B_INMEM)) {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (t->bt_fd);
+}
diff --git a/trunk/main/db1-ast/btree/bt_overflow.c b/trunk/main/db1-ast/btree/bt_overflow.c
new file mode 100644
index 000000000..d8f310d91
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_overflow.c
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_overflow.c 8.5 (Berkeley) 7/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * Big key/data code.
+ *
+ * Big key and data entries are stored on linked lists of pages. The initial
+ * reference is byte string stored with the key or data and is the page number
+ * and size. The actual record is stored in a chain of pages linked by the
+ * nextpg field of the PAGE header.
+ *
+ * The first page of the chain has a special property. If the record is used
+ * by an internal page, it cannot be deleted and the P_PRESERVE bit will be set
+ * in the header.
+ *
+ * XXX
+ * A single DBT is written to each chain, so a lot of space on the last page
+ * is wasted. This is a fairly major bug for some data sets.
+ */
+
+/*
+ * __OVFL_GET -- Get an overflow key/data item.
+ *
+ * Parameters:
+ * t: tree
+ * p: pointer to { pgno_t, u_int32_t }
+ * buf: storage address
+ * bufsz: storage size
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__ovfl_get(t, p, ssz, buf, bufsz)
+ BTREE *t;
+ void *p;
+ size_t *ssz;
+ void **buf;
+ size_t *bufsz;
+{
+ PAGE *h;
+ pgno_t pg;
+ size_t nb, plen;
+ u_int32_t sz;
+
+ memmove(&pg, p, sizeof(pgno_t));
+ memmove(&sz, (char *)p + sizeof(pgno_t), sizeof(u_int32_t));
+ *ssz = sz;
+
+#ifdef DEBUG
+ if (pg == P_INVALID || sz == 0)
+ abort();
+#endif
+ /* Make the buffer bigger as necessary. */
+ if (*bufsz < sz) {
+ *buf = (char *)(*buf == NULL ? malloc(sz) : realloc(*buf, sz));
+ if (*buf == NULL)
+ return (RET_ERROR);
+ *bufsz = sz;
+ }
+
+ /*
+ * Step through the linked list of pages, copying the data on each one
+ * into the buffer. Never copy more than the data's length.
+ */
+ plen = t->bt_psize - BTDATAOFF;
+ for (p = *buf;; p = (char *)p + nb, pg = h->nextpg) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ nb = MIN(sz, plen);
+ memmove(p, (char *)h + BTDATAOFF, nb);
+ mpool_put(t->bt_mp, h, 0);
+
+ if ((sz -= nb) == 0)
+ break;
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __OVFL_PUT -- Store an overflow key/data item.
+ *
+ * Parameters:
+ * t: tree
+ * data: DBT to store
+ * pgno: storage page number
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__ovfl_put(t, dbt, pg)
+ BTREE *t;
+ const DBT *dbt;
+ pgno_t *pg;
+{
+ PAGE *h, *last;
+ void *p;
+ pgno_t npg;
+ size_t nb, plen;
+ u_int32_t sz;
+
+ /*
+ * Allocate pages and copy the key/data record into them. Store the
+ * number of the first page in the chain.
+ */
+ plen = t->bt_psize - BTDATAOFF;
+ for (last = NULL, p = dbt->data, sz = dbt->size;;
+ p = (char *)p + plen, last = h) {
+ if ((h = __bt_new(t, &npg)) == NULL)
+ return (RET_ERROR);
+
+ h->pgno = npg;
+ h->nextpg = h->prevpg = P_INVALID;
+ h->flags = P_OVERFLOW;
+ h->lower = h->upper = 0;
+
+ nb = MIN(sz, plen);
+ memmove((char *)h + BTDATAOFF, p, nb);
+
+ if (last) {
+ last->nextpg = h->pgno;
+ mpool_put(t->bt_mp, last, MPOOL_DIRTY);
+ } else
+ *pg = h->pgno;
+
+ if ((sz -= nb) == 0) {
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ }
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __OVFL_DELETE -- Delete an overflow chain.
+ *
+ * Parameters:
+ * t: tree
+ * p: pointer to { pgno_t, u_int32_t }
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__ovfl_delete(t, p)
+ BTREE *t;
+ void *p;
+{
+ PAGE *h;
+ pgno_t pg;
+ size_t plen;
+ u_int32_t sz;
+
+ memmove(&pg, p, sizeof(pgno_t));
+ memmove(&sz, (char *)p + sizeof(pgno_t), sizeof(u_int32_t));
+
+#ifdef DEBUG
+ if (pg == P_INVALID || sz == 0)
+ abort();
+#endif
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Don't delete chains used by internal pages. */
+ if (h->flags & P_PRESERVE) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SUCCESS);
+ }
+
+ /* Step through the chain, calling the free routine for each page. */
+ for (plen = t->bt_psize - BTDATAOFF;; sz -= plen) {
+ pg = h->nextpg;
+ __bt_free(t, h);
+ if (sz <= plen)
+ break;
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ }
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/btree/bt_page.c b/trunk/main/db1-ast/btree/bt_page.c
new file mode 100644
index 000000000..e77a1d6b5
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_page.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_page.c 8.3 (Berkeley) 7/14/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * __bt_free --
+ * Put a page on the freelist.
+ *
+ * Parameters:
+ * t: tree
+ * h: page to free
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ *
+ * Side-effect:
+ * mpool_put's the page.
+ */
+int
+__bt_free(t, h)
+ BTREE *t;
+ PAGE *h;
+{
+ /* Insert the page at the head of the free list. */
+ h->prevpg = P_INVALID;
+ h->nextpg = t->bt_free;
+ t->bt_free = h->pgno;
+ F_SET(t, B_METADIRTY);
+
+ /* Make sure the page gets written back. */
+ return (mpool_put(t->bt_mp, h, MPOOL_DIRTY));
+}
+
+/*
+ * __bt_new --
+ * Get a new page, preferably from the freelist.
+ *
+ * Parameters:
+ * t: tree
+ * npg: storage for page number.
+ *
+ * Returns:
+ * Pointer to a page, NULL on error.
+ */
+PAGE *
+__bt_new(t, npg)
+ BTREE *t;
+ pgno_t *npg;
+{
+ PAGE *h;
+
+ if (t->bt_free != P_INVALID &&
+ (h = mpool_get(t->bt_mp, t->bt_free, 0)) != NULL) {
+ *npg = t->bt_free;
+ t->bt_free = h->nextpg;
+ F_SET(t, B_METADIRTY);
+ return (h);
+ }
+ return (mpool_new(t->bt_mp, npg));
+}
diff --git a/trunk/main/db1-ast/btree/bt_put.c b/trunk/main/db1-ast/btree/bt_put.c
new file mode 100644
index 000000000..aeb0bb16c
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_put.c
@@ -0,0 +1,321 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_put.c 8.8 (Berkeley) 7/26/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static EPG *bt_fast __P((BTREE *, const DBT *, const DBT *, int *));
+
+/*
+ * __BT_PUT -- Add a btree item to the tree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key
+ * data: data
+ * flag: R_NOOVERWRITE
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is already in the
+ * tree and R_NOOVERWRITE specified.
+ */
+int
+__bt_put(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key;
+ const DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ DBT tkey, tdata;
+ EPG *e = 0;
+ PAGE *h;
+ indx_t index, nxtindex;
+ pgno_t pg;
+ u_int32_t nbytes;
+ int dflags, exact, status;
+ char *dest, db[NOVFLSIZE], kb[NOVFLSIZE];
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Check for change to a read-only tree. */
+ if (F_ISSET(t, B_RDONLY)) {
+ errno = EPERM;
+ return (RET_ERROR);
+ }
+
+ switch (flags) {
+ case 0:
+ case R_NOOVERWRITE:
+ break;
+ case R_CURSOR:
+ /*
+ * If flags is R_CURSOR, put the cursor. Must already
+ * have started a scan and not have already deleted it.
+ */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor,
+ CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE))
+ break;
+ /* FALLTHROUGH */
+ default:
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ /*
+ * If the key/data pair won't fit on a page, store it on overflow
+ * pages. Only put the key on the overflow page if the pair are
+ * still too big after moving the data to an overflow page.
+ *
+ * XXX
+ * If the insert fails later on, the overflow pages aren't recovered.
+ */
+ dflags = 0;
+ if (key->size + data->size > t->bt_ovflsize) {
+ if (key->size > t->bt_ovflsize) {
+storekey: if (__ovfl_put(t, key, &pg) == RET_ERROR)
+ return (RET_ERROR);
+ tkey.data = kb;
+ tkey.size = NOVFLSIZE;
+ memmove(kb, &pg, sizeof(pgno_t));
+ memmove(kb + sizeof(pgno_t),
+ &key->size, sizeof(u_int32_t));
+ dflags |= P_BIGKEY;
+ key = &tkey;
+ }
+ if (key->size + data->size > t->bt_ovflsize) {
+ if (__ovfl_put(t, data, &pg) == RET_ERROR)
+ return (RET_ERROR);
+ tdata.data = db;
+ tdata.size = NOVFLSIZE;
+ memmove(db, &pg, sizeof(pgno_t));
+ memmove(db + sizeof(pgno_t),
+ &data->size, sizeof(u_int32_t));
+ dflags |= P_BIGDATA;
+ data = &tdata;
+ }
+ if (key->size + data->size > t->bt_ovflsize)
+ goto storekey;
+ }
+
+ /* Replace the cursor. */
+ if (flags == R_CURSOR) {
+ if ((h = mpool_get(t->bt_mp, t->bt_cursor.pg.pgno, 0)) == NULL)
+ return (RET_ERROR);
+ index = t->bt_cursor.pg.index;
+ goto delete;
+ }
+
+ /*
+ * Find the key to delete, or, the location at which to insert.
+ * Bt_fast and __bt_search both pin the returned page.
+ */
+ if (t->bt_order == NOT || (e = bt_fast(t, key, data, &exact)) == NULL)
+ if ((e = __bt_search(t, key, &exact)) == NULL)
+ return (RET_ERROR);
+ h = e->page;
+ index = e->index;
+
+ /*
+ * Add the key/data pair to the tree. If an identical key is already
+ * in the tree, and R_NOOVERWRITE is set, an error is returned. If
+ * R_NOOVERWRITE is not set, the key is either added (if duplicates are
+ * permitted) or an error is returned.
+ */
+ switch (flags) {
+ case R_NOOVERWRITE:
+ if (!exact)
+ break;
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SPECIAL);
+ default:
+ if (!exact || !F_ISSET(t, B_NODUPS))
+ break;
+ /*
+ * !!!
+ * Note, the delete may empty the page, so we need to put a
+ * new entry into the page immediately.
+ */
+delete: if (__bt_dleaf(t, key, h, index) == RET_ERROR) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ break;
+ }
+
+ /*
+ * If not enough room, or the user has put a ceiling on the number of
+ * keys permitted in the page, split the page. The split code will
+ * insert the key and data and unpin the current page. If inserting
+ * into the offset array, shift the pointers up.
+ */
+ nbytes = NBLEAFDBT(key->size, data->size);
+ if ((u_int32_t) (h->upper - h->lower) < nbytes + sizeof(indx_t)) {
+ if ((status = __bt_split(t, h, key,
+ data, dflags, nbytes, index)) != RET_SUCCESS)
+ return (status);
+ goto success;
+ }
+
+ if (index < (nxtindex = NEXTINDEX(h)))
+ memmove(h->linp + index + 1, h->linp + index,
+ (nxtindex - index) * sizeof(indx_t));
+ h->lower += sizeof(indx_t);
+
+ h->linp[index] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ WR_BLEAF(dest, key, data, dflags);
+
+ /* If the cursor is on this page, adjust it as necessary. */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
+ t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index >= index)
+ ++t->bt_cursor.pg.index;
+
+ if (t->bt_order == NOT) {
+ if (h->nextpg == P_INVALID) {
+ if (index == NEXTINDEX(h) - 1) {
+ t->bt_order = FORWARD;
+ t->bt_last.index = index;
+ t->bt_last.pgno = h->pgno;
+ }
+ } else if (h->prevpg == P_INVALID) {
+ if (index == 0) {
+ t->bt_order = BACK;
+ t->bt_last.index = 0;
+ t->bt_last.pgno = h->pgno;
+ }
+ }
+ }
+
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+success:
+ if (flags == R_SETCURSOR)
+ __bt_setcur(t, e->page->pgno, e->index);
+
+ F_SET(t, B_MODIFIED);
+ return (RET_SUCCESS);
+}
+
+#ifdef STATISTICS
+u_long bt_cache_hit, bt_cache_miss;
+#endif
+
+/*
+ * BT_FAST -- Do a quick check for sorted data.
+ *
+ * Parameters:
+ * t: tree
+ * key: key to insert
+ *
+ * Returns:
+ * EPG for new record or NULL if not found.
+ */
+static EPG *
+bt_fast(t, key, data, exactp)
+ BTREE *t;
+ const DBT *key, *data;
+ int *exactp;
+{
+ PAGE *h;
+ u_int32_t nbytes;
+ int cmp;
+
+ if ((h = mpool_get(t->bt_mp, t->bt_last.pgno, 0)) == NULL) {
+ t->bt_order = NOT;
+ return (NULL);
+ }
+ t->bt_cur.page = h;
+ t->bt_cur.index = t->bt_last.index;
+
+ /*
+ * If won't fit in this page or have too many keys in this page,
+ * have to search to get split stack.
+ */
+ nbytes = NBLEAFDBT(key->size, data->size);
+ if ((u_int32_t) (h->upper - h->lower) < nbytes + sizeof(indx_t))
+ goto miss;
+
+ if (t->bt_order == FORWARD) {
+ if (t->bt_cur.page->nextpg != P_INVALID)
+ goto miss;
+ if (t->bt_cur.index != NEXTINDEX(h) - 1)
+ goto miss;
+ if ((cmp = __bt_cmp(t, key, &t->bt_cur)) < 0)
+ goto miss;
+ t->bt_last.index = cmp ? ++t->bt_cur.index : t->bt_cur.index;
+ } else {
+ if (t->bt_cur.page->prevpg != P_INVALID)
+ goto miss;
+ if (t->bt_cur.index != 0)
+ goto miss;
+ if ((cmp = __bt_cmp(t, key, &t->bt_cur)) > 0)
+ goto miss;
+ t->bt_last.index = 0;
+ }
+ *exactp = cmp == 0;
+#ifdef STATISTICS
+ ++bt_cache_hit;
+#endif
+ return (&t->bt_cur);
+
+miss:
+#ifdef STATISTICS
+ ++bt_cache_miss;
+#endif
+ t->bt_order = NOT;
+ mpool_put(t->bt_mp, h, 0);
+ return (NULL);
+}
diff --git a/trunk/main/db1-ast/btree/bt_search.c b/trunk/main/db1-ast/btree/bt_search.c
new file mode 100644
index 000000000..623f43949
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_search.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_search.c 8.8 (Berkeley) 7/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int __bt_snext __P((BTREE *, PAGE *, const DBT *, int *));
+static int __bt_sprev __P((BTREE *, PAGE *, const DBT *, int *));
+
+/*
+ * __bt_search --
+ * Search a btree for a key.
+ *
+ * Parameters:
+ * t: tree to search
+ * key: key to find
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * The EPG for matching record, if any, or the EPG for the location
+ * of the key, if it were inserted into the tree, is entered into
+ * the bt_cur field of the tree. A pointer to the field is returned.
+ */
+EPG *
+__bt_search(t, key, exactp)
+ BTREE *t;
+ const DBT *key;
+ int *exactp;
+{
+ PAGE *h;
+ indx_t base, index, lim;
+ pgno_t pg;
+ int cmp;
+
+ BT_CLR(t);
+ for (pg = P_ROOT;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (NULL);
+
+ /* Do a binary search on the current page. */
+ t->bt_cur.page = h;
+ for (base = 0, lim = NEXTINDEX(h); lim; lim >>= 1) {
+ t->bt_cur.index = index = base + (lim >> 1);
+ if ((cmp = __bt_cmp(t, key, &t->bt_cur)) == 0) {
+ if (h->flags & P_BLEAF) {
+ *exactp = 1;
+ return (&t->bt_cur);
+ }
+ goto next;
+ }
+ if (cmp > 0) {
+ base = index + 1;
+ --lim;
+ }
+ }
+
+ /*
+ * If it's a leaf page, we're almost done. If no duplicates
+ * are allowed, or we have an exact match, we're done. Else,
+ * it's possible that there were matching keys on this page,
+ * which later deleted, and we're on a page with no matches
+ * while there are matches on other pages. If at the start or
+ * end of a page, check the adjacent page.
+ */
+ if (h->flags & P_BLEAF) {
+ if (!F_ISSET(t, B_NODUPS)) {
+ if (base == 0 &&
+ h->prevpg != P_INVALID &&
+ __bt_sprev(t, h, key, exactp))
+ return (&t->bt_cur);
+ if (base == NEXTINDEX(h) &&
+ h->nextpg != P_INVALID &&
+ __bt_snext(t, h, key, exactp))
+ return (&t->bt_cur);
+ }
+ *exactp = 0;
+ t->bt_cur.index = base;
+ return (&t->bt_cur);
+ }
+
+ /*
+ * No match found. Base is the smallest index greater than
+ * key and may be zero or a last + 1 index. If it's non-zero,
+ * decrement by one, and record the internal page which should
+ * be a parent page for the key. If a split later occurs, the
+ * inserted page will be to the right of the saved page.
+ */
+ index = base ? base - 1 : base;
+
+next: BT_PUSH(t, h->pgno, index);
+ pg = GETBINTERNAL(h, index)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ }
+}
+
+/*
+ * __bt_snext --
+ * Check for an exact match after the key.
+ *
+ * Parameters:
+ * t: tree
+ * h: current page
+ * key: key
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * If an exact match found.
+ */
+static int
+__bt_snext(t, h, key, exactp)
+ BTREE *t;
+ PAGE *h;
+ const DBT *key;
+ int *exactp;
+{
+ EPG e;
+
+ /*
+ * Get the next page. The key is either an exact
+ * match, or not as good as the one we already have.
+ */
+ if ((e.page = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
+ return (0);
+ e.index = 0;
+ if (__bt_cmp(t, key, &e) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ t->bt_cur = e;
+ *exactp = 1;
+ return (1);
+ }
+ mpool_put(t->bt_mp, e.page, 0);
+ return (0);
+}
+
+/*
+ * __bt_sprev --
+ * Check for an exact match before the key.
+ *
+ * Parameters:
+ * t: tree
+ * h: current page
+ * key: key
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * If an exact match found.
+ */
+static int
+__bt_sprev(t, h, key, exactp)
+ BTREE *t;
+ PAGE *h;
+ const DBT *key;
+ int *exactp;
+{
+ EPG e;
+
+ /*
+ * Get the previous page. The key is either an exact
+ * match, or not as good as the one we already have.
+ */
+ if ((e.page = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
+ return (0);
+ e.index = NEXTINDEX(e.page) - 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ t->bt_cur = e;
+ *exactp = 1;
+ return (1);
+ }
+ mpool_put(t->bt_mp, e.page, 0);
+ return (0);
+}
diff --git a/trunk/main/db1-ast/btree/bt_seq.c b/trunk/main/db1-ast/btree/bt_seq.c
new file mode 100644
index 000000000..3f1724274
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_seq.c
@@ -0,0 +1,460 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_seq.c 8.7 (Berkeley) 7/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int __bt_first __P((BTREE *, const DBT *, EPG *, int *));
+static int __bt_seqadv __P((BTREE *, EPG *, int));
+static int __bt_seqset __P((BTREE *, EPG *, DBT *, int));
+
+/*
+ * Sequential scan support.
+ *
+ * The tree can be scanned sequentially, starting from either end of the
+ * tree or from any specific key. A scan request before any scanning is
+ * done is initialized as starting from the least node.
+ */
+
+/*
+ * __bt_seq --
+ * Btree sequential scan interface.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key for positioning and return value
+ * data: data return value
+ * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+int
+__bt_seq(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key, *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG e;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /*
+ * If scan uninitialized as yet, or starting at a specific record, set
+ * the scan to a specific key. Both __bt_seqset and __bt_seqadv pin
+ * the page the cursor references if they're successful.
+ */
+ switch (flags) {
+ case R_NEXT:
+ case R_PREV:
+ if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
+ status = __bt_seqadv(t, &e, flags);
+ break;
+ }
+ /* FALLTHROUGH */
+ case R_FIRST:
+ case R_LAST:
+ case R_CURSOR:
+ status = __bt_seqset(t, &e, key, flags);
+ break;
+ default:
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (status == RET_SUCCESS) {
+ __bt_setcur(t, e.page->pgno, e.index);
+
+ status =
+ __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0);
+
+ /*
+ * If the user is doing concurrent access, we copied the
+ * key/data, toss the page.
+ */
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e.page, 0);
+ else
+ t->bt_pinned = e.page;
+ }
+ return (status);
+}
+
+/*
+ * __bt_seqset --
+ * Set the sequential scan to a specific key.
+ *
+ * Parameters:
+ * t: tree
+ * ep: storage for returned key
+ * key: key for initial scan position
+ * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
+ *
+ * Side effects:
+ * Pins the page the cursor references.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+static int
+__bt_seqset(t, ep, key, flags)
+ BTREE *t;
+ EPG *ep;
+ DBT *key;
+ int flags;
+{
+ PAGE *h;
+ pgno_t pg;
+ int exact;
+
+ /*
+ * Find the first, last or specific key in the tree and point the
+ * cursor at it. The cursor may not be moved until a new key has
+ * been found.
+ */
+ switch (flags) {
+ case R_CURSOR: /* Keyed scan. */
+ /*
+ * Find the first instance of the key or the smallest key
+ * which is greater than or equal to the specified key.
+ */
+ if (key->data == NULL || key->size == 0) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+ return (__bt_first(t, key, ep, &exact));
+ case R_FIRST: /* First record. */
+ case R_NEXT:
+ /* Walk down the left-hand side of the tree. */
+ for (pg = P_ROOT;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Check for an empty tree. */
+ if (NEXTINDEX(h) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SPECIAL);
+ }
+
+ if (h->flags & (P_BLEAF | P_RLEAF))
+ break;
+ pg = GETBINTERNAL(h, 0)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ }
+ ep->page = h;
+ ep->index = 0;
+ break;
+ case R_LAST: /* Last record. */
+ case R_PREV:
+ /* Walk down the right-hand side of the tree. */
+ for (pg = P_ROOT;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Check for an empty tree. */
+ if (NEXTINDEX(h) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SPECIAL);
+ }
+
+ if (h->flags & (P_BLEAF | P_RLEAF))
+ break;
+ pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ }
+
+ ep->page = h;
+ ep->index = NEXTINDEX(h) - 1;
+ break;
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_seqadvance --
+ * Advance the sequential scan.
+ *
+ * Parameters:
+ * t: tree
+ * flags: R_NEXT, R_PREV
+ *
+ * Side effects:
+ * Pins the page the new key/data record is on.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+static int
+__bt_seqadv(t, ep, flags)
+ BTREE *t;
+ EPG *ep;
+ int flags;
+{
+ CURSOR *c;
+ PAGE *h;
+ indx_t index = 0;
+ pgno_t pg;
+ int exact;
+
+ /*
+ * There are a couple of states that we can be in. The cursor has
+ * been initialized by the time we get here, but that's all we know.
+ */
+ c = &t->bt_cursor;
+
+ /*
+ * The cursor was deleted where there weren't any duplicate records,
+ * so the key was saved. Find out where that key would go in the
+ * current tree. It doesn't matter if the returned key is an exact
+ * match or not -- if it's an exact match, the record was added after
+ * the delete so we can just return it. If not, as long as there's
+ * a record there, return it.
+ */
+ if (F_ISSET(c, CURS_ACQUIRE))
+ return (__bt_first(t, &c->key, ep, &exact));
+
+ /* Get the page referenced by the cursor. */
+ if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
+ return (RET_ERROR);
+
+ /*
+ * Find the next/previous record in the tree and point the cursor at
+ * it. The cursor may not be moved until a new key has been found.
+ */
+ switch (flags) {
+ case R_NEXT: /* Next record. */
+ /*
+ * The cursor was deleted in duplicate records, and moved
+ * forward to a record that has yet to be returned. Clear
+ * that flag, and return the record.
+ */
+ if (F_ISSET(c, CURS_AFTER))
+ goto usecurrent;
+ index = c->pg.index;
+ if (++index == NEXTINDEX(h)) {
+ pg = h->nextpg;
+ mpool_put(t->bt_mp, h, 0);
+ if (pg == P_INVALID)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ index = 0;
+ }
+ break;
+ case R_PREV: /* Previous record. */
+ /*
+ * The cursor was deleted in duplicate records, and moved
+ * backward to a record that has yet to be returned. Clear
+ * that flag, and return the record.
+ */
+ if (F_ISSET(c, CURS_BEFORE)) {
+usecurrent: F_CLR(c, CURS_AFTER | CURS_BEFORE);
+ ep->page = h;
+ ep->index = c->pg.index;
+ return (RET_SUCCESS);
+ }
+ index = c->pg.index;
+ if (index == 0) {
+ pg = h->prevpg;
+ mpool_put(t->bt_mp, h, 0);
+ if (pg == P_INVALID)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ index = NEXTINDEX(h) - 1;
+ } else
+ --index;
+ break;
+ }
+
+ ep->page = h;
+ ep->index = index;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_first --
+ * Find the first entry.
+ *
+ * Parameters:
+ * t: the tree
+ * key: the key
+ * erval: return EPG
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * The first entry in the tree greater than or equal to key,
+ * or RET_SPECIAL if no such key exists.
+ */
+static int
+__bt_first(t, key, erval, exactp)
+ BTREE *t;
+ const DBT *key;
+ EPG *erval;
+ int *exactp;
+{
+ PAGE *h;
+ EPG *ep, save;
+ pgno_t pg;
+
+ /*
+ * Find any matching record; __bt_search pins the page.
+ *
+ * If it's an exact match and duplicates are possible, walk backwards
+ * in the tree until we find the first one. Otherwise, make sure it's
+ * a valid key (__bt_search may return an index just past the end of a
+ * page) and return it.
+ */
+ if ((ep = __bt_search(t, key, exactp)) == NULL)
+ return (RET_SPECIAL);
+ if (*exactp) {
+ if (F_ISSET(t, B_NODUPS)) {
+ *erval = *ep;
+ return (RET_SUCCESS);
+ }
+
+ /*
+ * Walk backwards, as long as the entry matches and there are
+ * keys left in the tree. Save a copy of each match in case
+ * we go too far.
+ */
+ save = *ep;
+ h = ep->page;
+ do {
+ if (save.page->pgno != ep->page->pgno) {
+ mpool_put(t->bt_mp, save.page, 0);
+ save = *ep;
+ } else
+ save.index = ep->index;
+
+ /*
+ * Don't unpin the page the last (or original) match
+ * was on, but make sure it's unpinned if an error
+ * occurs.
+ */
+ if (ep->index == 0) {
+ if (h->prevpg == P_INVALID)
+ break;
+ if (h->pgno != save.page->pgno)
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp,
+ h->prevpg, 0)) == NULL) {
+ if (h->pgno == save.page->pgno)
+ mpool_put(t->bt_mp,
+ save.page, 0);
+ return (RET_ERROR);
+ }
+ ep->page = h;
+ ep->index = NEXTINDEX(h);
+ }
+ --ep->index;
+ } while (__bt_cmp(t, key, ep) == 0);
+
+ /*
+ * Reach here with the last page that was looked at pinned,
+ * which may or may not be the same as the last (or original)
+ * match page. If it's not useful, release it.
+ */
+ if (h->pgno != save.page->pgno)
+ mpool_put(t->bt_mp, h, 0);
+
+ *erval = save;
+ return (RET_SUCCESS);
+ }
+
+ /* If at the end of a page, find the next entry. */
+ if (ep->index == NEXTINDEX(ep->page)) {
+ h = ep->page;
+ pg = h->nextpg;
+ mpool_put(t->bt_mp, h, 0);
+ if (pg == P_INVALID)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ ep->index = 0;
+ ep->page = h;
+ }
+ *erval = *ep;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_setcur --
+ * Set the cursor to an entry in the tree.
+ *
+ * Parameters:
+ * t: the tree
+ * pgno: page number
+ * index: page index
+ */
+void
+__bt_setcur(t, pgno, index)
+ BTREE *t;
+ pgno_t pgno;
+ u_int index;
+{
+ /* Lose any already deleted key. */
+ if (t->bt_cursor.key.data != NULL) {
+ free(t->bt_cursor.key.data);
+ t->bt_cursor.key.size = 0;
+ t->bt_cursor.key.data = NULL;
+ }
+ F_CLR(&t->bt_cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE);
+
+ /* Update the cursor. */
+ t->bt_cursor.pg.pgno = pgno;
+ t->bt_cursor.pg.index = index;
+ F_SET(&t->bt_cursor, CURS_INIT);
+}
diff --git a/trunk/main/db1-ast/btree/bt_split.c b/trunk/main/db1-ast/btree/bt_split.c
new file mode 100644
index 000000000..8fede1e45
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_split.c
@@ -0,0 +1,829 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_split.c 8.9 (Berkeley) 7/26/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int bt_broot __P((BTREE *, PAGE *, PAGE *, PAGE *));
+static PAGE *bt_page
+ __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t));
+static int bt_preserve __P((BTREE *, pgno_t));
+static PAGE *bt_psplit
+ __P((BTREE *, PAGE *, PAGE *, PAGE *, indx_t *, size_t));
+static PAGE *bt_root
+ __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t));
+static int bt_rroot __P((BTREE *, PAGE *, PAGE *, PAGE *));
+static recno_t rec_total __P((PAGE *));
+
+#ifdef STATISTICS
+u_long bt_rootsplit, bt_split, bt_sortsplit, bt_pfxsaved;
+#endif
+
+/*
+ * __BT_SPLIT -- Split the tree.
+ *
+ * Parameters:
+ * t: tree
+ * sp: page to split
+ * key: key to insert
+ * data: data to insert
+ * flags: BIGKEY/BIGDATA flags
+ * ilen: insert length
+ * skip: index to leave open
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__bt_split(t, sp, key, data, flags, ilen, argskip)
+ BTREE *t;
+ PAGE *sp;
+ const DBT *key, *data;
+ int flags;
+ size_t ilen;
+ u_int32_t argskip;
+{
+ BINTERNAL *bi = 0;
+ BLEAF *bl = 0, *tbl;
+ DBT a, b;
+ EPGNO *parent;
+ PAGE *h, *l, *r, *lchild, *rchild;
+ indx_t nxtindex;
+ u_int16_t skip;
+ u_int32_t n, nbytes, nksize = 0;
+ int parentsplit;
+ char *dest;
+
+ /*
+ * Split the page into two pages, l and r. The split routines return
+ * a pointer to the page into which the key should be inserted and with
+ * skip set to the offset which should be used. Additionally, l and r
+ * are pinned.
+ */
+ skip = argskip;
+ h = sp->pgno == P_ROOT ?
+ bt_root(t, sp, &l, &r, &skip, ilen) :
+ bt_page(t, sp, &l, &r, &skip, ilen);
+ if (h == NULL)
+ return (RET_ERROR);
+
+ /*
+ * Insert the new key/data pair into the leaf page. (Key inserts
+ * always cause a leaf page to split first.)
+ */
+ h->linp[skip] = h->upper -= ilen;
+ dest = (char *)h + h->upper;
+ if (F_ISSET(t, R_RECNO))
+ WR_RLEAF(dest, data, flags)
+ else
+ WR_BLEAF(dest, key, data, flags)
+
+ /* If the root page was split, make it look right. */
+ if (sp->pgno == P_ROOT &&
+ (F_ISSET(t, R_RECNO) ?
+ bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR)
+ goto err2;
+
+ /*
+ * Now we walk the parent page stack -- a LIFO stack of the pages that
+ * were traversed when we searched for the page that split. Each stack
+ * entry is a page number and a page index offset. The offset is for
+ * the page traversed on the search. We've just split a page, so we
+ * have to insert a new key into the parent page.
+ *
+ * If the insert into the parent page causes it to split, may have to
+ * continue splitting all the way up the tree. We stop if the root
+ * splits or the page inserted into didn't have to split to hold the
+ * new key. Some algorithms replace the key for the old page as well
+ * as the new page. We don't, as there's no reason to believe that the
+ * first key on the old page is any better than the key we have, and,
+ * in the case of a key being placed at index 0 causing the split, the
+ * key is unavailable.
+ *
+ * There are a maximum of 5 pages pinned at any time. We keep the left
+ * and right pages pinned while working on the parent. The 5 are the
+ * two children, left parent and right parent (when the parent splits)
+ * and the root page or the overflow key page when calling bt_preserve.
+ * This code must make sure that all pins are released other than the
+ * root page or overflow page which is unlocked elsewhere.
+ */
+ while ((parent = BT_POP(t)) != NULL) {
+ lchild = l;
+ rchild = r;
+
+ /* Get the parent page. */
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ goto err2;
+
+ /*
+ * The new key goes ONE AFTER the index, because the split
+ * was to the right.
+ */
+ skip = parent->index + 1;
+
+ /*
+ * Calculate the space needed on the parent page.
+ *
+ * Prefix trees: space hack when inserting into BINTERNAL
+ * pages. Retain only what's needed to distinguish between
+ * the new entry and the LAST entry on the page to its left.
+ * If the keys compare equal, retain the entire key. Note,
+ * we don't touch overflow keys, and the entire key must be
+ * retained for the next-to-left most key on the leftmost
+ * page of each level, or the search will fail. Applicable
+ * ONLY to internal pages that have leaf pages as children.
+ * Further reduction of the key between pairs of internal
+ * pages loses too much information.
+ */
+ switch (rchild->flags & P_TYPE) {
+ case P_BINTERNAL:
+ bi = GETBINTERNAL(rchild, 0);
+ nbytes = NBINTERNAL(bi->ksize);
+ break;
+ case P_BLEAF:
+ bl = GETBLEAF(rchild, 0);
+ nbytes = NBINTERNAL(bl->ksize);
+ if (t->bt_pfx && !(bl->flags & P_BIGKEY) &&
+ (h->prevpg != P_INVALID || skip > 1)) {
+ tbl = GETBLEAF(lchild, NEXTINDEX(lchild) - 1);
+ a.size = tbl->ksize;
+ a.data = tbl->bytes;
+ b.size = bl->ksize;
+ b.data = bl->bytes;
+ nksize = t->bt_pfx(&a, &b);
+ n = NBINTERNAL(nksize);
+ if (n < nbytes) {
+#ifdef STATISTICS
+ bt_pfxsaved += nbytes - n;
+#endif
+ nbytes = n;
+ } else
+ nksize = 0;
+ } else
+ nksize = 0;
+ break;
+ case P_RINTERNAL:
+ case P_RLEAF:
+ nbytes = NRINTERNAL;
+ break;
+ default:
+ abort();
+ }
+
+ /* Split the parent page if necessary or shift the indices. */
+ if ((u_int32_t) (h->upper - h->lower)
+ < nbytes + sizeof(indx_t)) {
+ sp = h;
+ h = h->pgno == P_ROOT ?
+ bt_root(t, h, &l, &r, &skip, nbytes) :
+ bt_page(t, h, &l, &r, &skip, nbytes);
+ if (h == NULL)
+ goto err1;
+ parentsplit = 1;
+ } else {
+ if (skip < (nxtindex = NEXTINDEX(h)))
+ memmove(h->linp + skip + 1, h->linp + skip,
+ (nxtindex - skip) * sizeof(indx_t));
+ h->lower += sizeof(indx_t);
+ parentsplit = 0;
+ }
+
+ /* Insert the key into the parent page. */
+ switch (rchild->flags & P_TYPE) {
+ case P_BINTERNAL:
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ memmove(dest, bi, nbytes);
+ ((BINTERNAL *)dest)->pgno = rchild->pgno;
+ break;
+ case P_BLEAF:
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ WR_BINTERNAL(dest, nksize ? nksize : bl->ksize,
+ rchild->pgno, bl->flags & P_BIGKEY);
+ memmove(dest, bl->bytes, nksize ? nksize : bl->ksize);
+ if (bl->flags & P_BIGKEY &&
+ bt_preserve(t, *(pgno_t *)bl->bytes) == RET_ERROR)
+ goto err1;
+ break;
+ case P_RINTERNAL:
+ /*
+ * Update the left page count. If split
+ * added at index 0, fix the correct page.
+ */
+ if (skip > 0)
+ dest = (char *)h + h->linp[skip - 1];
+ else
+ dest = (char *)l + l->linp[NEXTINDEX(l) - 1];
+ ((RINTERNAL *)dest)->nrecs = rec_total(lchild);
+ ((RINTERNAL *)dest)->pgno = lchild->pgno;
+
+ /* Update the right page count. */
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ ((RINTERNAL *)dest)->nrecs = rec_total(rchild);
+ ((RINTERNAL *)dest)->pgno = rchild->pgno;
+ break;
+ case P_RLEAF:
+ /*
+ * Update the left page count. If split
+ * added at index 0, fix the correct page.
+ */
+ if (skip > 0)
+ dest = (char *)h + h->linp[skip - 1];
+ else
+ dest = (char *)l + l->linp[NEXTINDEX(l) - 1];
+ ((RINTERNAL *)dest)->nrecs = NEXTINDEX(lchild);
+ ((RINTERNAL *)dest)->pgno = lchild->pgno;
+
+ /* Update the right page count. */
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ ((RINTERNAL *)dest)->nrecs = NEXTINDEX(rchild);
+ ((RINTERNAL *)dest)->pgno = rchild->pgno;
+ break;
+ default:
+ abort();
+ }
+
+ /* Unpin the held pages. */
+ if (!parentsplit) {
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ }
+
+ /* If the root page was split, make it look right. */
+ if (sp->pgno == P_ROOT &&
+ (F_ISSET(t, R_RECNO) ?
+ bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR)
+ goto err1;
+
+ mpool_put(t->bt_mp, lchild, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, rchild, MPOOL_DIRTY);
+ }
+
+ /* Unpin the held pages. */
+ mpool_put(t->bt_mp, l, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, r, MPOOL_DIRTY);
+
+ /* Clear any pages left on the stack. */
+ return (RET_SUCCESS);
+
+ /*
+ * If something fails in the above loop we were already walking back
+ * up the tree and the tree is now inconsistent. Nothing much we can
+ * do about it but release any memory we're holding.
+ */
+err1: mpool_put(t->bt_mp, lchild, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, rchild, MPOOL_DIRTY);
+
+err2: mpool_put(t->bt_mp, l, 0);
+ mpool_put(t->bt_mp, r, 0);
+ __dbpanic(t->bt_dbp);
+ return (RET_ERROR);
+}
+
+/*
+ * BT_PAGE -- Split a non-root page of a btree.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * lp: pointer to left page pointer
+ * rp: pointer to right page pointer
+ * skip: pointer to index to leave open
+ * ilen: insert length
+ *
+ * Returns:
+ * Pointer to page in which to insert or NULL on error.
+ */
+static PAGE *
+bt_page(t, h, lp, rp, skip, ilen)
+ BTREE *t;
+ PAGE *h, **lp, **rp;
+ indx_t *skip;
+ size_t ilen;
+{
+ PAGE *l, *r, *tp;
+ pgno_t npg;
+
+#ifdef STATISTICS
+ ++bt_split;
+#endif
+ /* Put the new right page for the split into place. */
+ if ((r = __bt_new(t, &npg)) == NULL)
+ return (NULL);
+ r->pgno = npg;
+ r->lower = BTDATAOFF;
+ r->upper = t->bt_psize;
+ r->nextpg = h->nextpg;
+ r->prevpg = h->pgno;
+ r->flags = h->flags & P_TYPE;
+
+ /*
+ * If we're splitting the last page on a level because we're appending
+ * a key to it (skip is NEXTINDEX()), it's likely that the data is
+ * sorted. Adding an empty page on the side of the level is less work
+ * and can push the fill factor much higher than normal. If we're
+ * wrong it's no big deal, we'll just do the split the right way next
+ * time. It may look like it's equally easy to do a similar hack for
+ * reverse sorted data, that is, split the tree left, but it's not.
+ * Don't even try.
+ */
+ if (h->nextpg == P_INVALID && *skip == NEXTINDEX(h)) {
+#ifdef STATISTICS
+ ++bt_sortsplit;
+#endif
+ h->nextpg = r->pgno;
+ r->lower = BTDATAOFF + sizeof(indx_t);
+ *skip = 0;
+ *lp = h;
+ *rp = r;
+ return (r);
+ }
+
+ /* Put the new left page for the split into place. */
+ if ((l = (PAGE *)malloc(t->bt_psize)) == NULL) {
+ mpool_put(t->bt_mp, r, 0);
+ return (NULL);
+ }
+#ifdef PURIFY
+ memset(l, 0xff, t->bt_psize);
+#endif
+ l->pgno = h->pgno;
+ l->nextpg = r->pgno;
+ l->prevpg = h->prevpg;
+ l->lower = BTDATAOFF;
+ l->upper = t->bt_psize;
+ l->flags = h->flags & P_TYPE;
+
+ /* Fix up the previous pointer of the page after the split page. */
+ if (h->nextpg != P_INVALID) {
+ if ((tp = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) {
+ free(l);
+ /* XXX mpool_free(t->bt_mp, r->pgno); */
+ return (NULL);
+ }
+ tp->prevpg = r->pgno;
+ mpool_put(t->bt_mp, tp, MPOOL_DIRTY);
+ }
+
+ /*
+ * Split right. The key/data pairs aren't sorted in the btree page so
+ * it's simpler to copy the data from the split page onto two new pages
+ * instead of copying half the data to the right page and compacting
+ * the left page in place. Since the left page can't change, we have
+ * to swap the original and the allocated left page after the split.
+ */
+ tp = bt_psplit(t, h, l, r, skip, ilen);
+
+ /* Move the new left page onto the old left page. */
+ memmove(h, l, t->bt_psize);
+ if (tp == l)
+ tp = h;
+ free(l);
+
+ *lp = h;
+ *rp = r;
+ return (tp);
+}
+
+/*
+ * BT_ROOT -- Split the root page of a btree.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * lp: pointer to left page pointer
+ * rp: pointer to right page pointer
+ * skip: pointer to index to leave open
+ * ilen: insert length
+ *
+ * Returns:
+ * Pointer to page in which to insert or NULL on error.
+ */
+static PAGE *
+bt_root(t, h, lp, rp, skip, ilen)
+ BTREE *t;
+ PAGE *h, **lp, **rp;
+ indx_t *skip;
+ size_t ilen;
+{
+ PAGE *l, *r, *tp;
+ pgno_t lnpg, rnpg;
+
+#ifdef STATISTICS
+ ++bt_split;
+ ++bt_rootsplit;
+#endif
+ /* Put the new left and right pages for the split into place. */
+ if ((l = __bt_new(t, &lnpg)) == NULL ||
+ (r = __bt_new(t, &rnpg)) == NULL)
+ return (NULL);
+ l->pgno = lnpg;
+ r->pgno = rnpg;
+ l->nextpg = r->pgno;
+ r->prevpg = l->pgno;
+ l->prevpg = r->nextpg = P_INVALID;
+ l->lower = r->lower = BTDATAOFF;
+ l->upper = r->upper = t->bt_psize;
+ l->flags = r->flags = h->flags & P_TYPE;
+
+ /* Split the root page. */
+ tp = bt_psplit(t, h, l, r, skip, ilen);
+
+ *lp = l;
+ *rp = r;
+ return (tp);
+}
+
+/*
+ * BT_RROOT -- Fix up the recno root page after it has been split.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * l: left page
+ * r: right page
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+bt_rroot(t, h, l, r)
+ BTREE *t;
+ PAGE *h, *l, *r;
+{
+ char *dest;
+
+ /* Insert the left and right keys, set the header information. */
+ h->linp[0] = h->upper = t->bt_psize - NRINTERNAL;
+ dest = (char *)h + h->upper;
+ WR_RINTERNAL(dest,
+ l->flags & P_RLEAF ? NEXTINDEX(l) : rec_total(l), l->pgno);
+
+ h->linp[1] = h->upper -= NRINTERNAL;
+ dest = (char *)h + h->upper;
+ WR_RINTERNAL(dest,
+ r->flags & P_RLEAF ? NEXTINDEX(r) : rec_total(r), r->pgno);
+
+ h->lower = BTDATAOFF + 2 * sizeof(indx_t);
+
+ /* Unpin the root page, set to recno internal page. */
+ h->flags &= ~P_TYPE;
+ h->flags |= P_RINTERNAL;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * BT_BROOT -- Fix up the btree root page after it has been split.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * l: left page
+ * r: right page
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+bt_broot(t, h, l, r)
+ BTREE *t;
+ PAGE *h, *l, *r;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ u_int32_t nbytes;
+ char *dest;
+
+ /*
+ * If the root page was a leaf page, change it into an internal page.
+ * We copy the key we split on (but not the key's data, in the case of
+ * a leaf page) to the new root page.
+ *
+ * The btree comparison code guarantees that the left-most key on any
+ * level of the tree is never used, so it doesn't need to be filled in.
+ */
+ nbytes = NBINTERNAL(0);
+ h->linp[0] = h->upper = t->bt_psize - nbytes;
+ dest = (char *)h + h->upper;
+ WR_BINTERNAL(dest, 0, l->pgno, 0);
+
+ switch (h->flags & P_TYPE) {
+ case P_BLEAF:
+ bl = GETBLEAF(r, 0);
+ nbytes = NBINTERNAL(bl->ksize);
+ h->linp[1] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ WR_BINTERNAL(dest, bl->ksize, r->pgno, 0);
+ memmove(dest, bl->bytes, bl->ksize);
+
+ /*
+ * If the key is on an overflow page, mark the overflow chain
+ * so it isn't deleted when the leaf copy of the key is deleted.
+ */
+ if (bl->flags & P_BIGKEY &&
+ bt_preserve(t, *(pgno_t *)bl->bytes) == RET_ERROR)
+ return (RET_ERROR);
+ break;
+ case P_BINTERNAL:
+ bi = GETBINTERNAL(r, 0);
+ nbytes = NBINTERNAL(bi->ksize);
+ h->linp[1] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ memmove(dest, bi, nbytes);
+ ((BINTERNAL *)dest)->pgno = r->pgno;
+ break;
+ default:
+ abort();
+ }
+
+ /* There are two keys on the page. */
+ h->lower = BTDATAOFF + 2 * sizeof(indx_t);
+
+ /* Unpin the root page, set to btree internal page. */
+ h->flags &= ~P_TYPE;
+ h->flags |= P_BINTERNAL;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * BT_PSPLIT -- Do the real work of splitting the page.
+ *
+ * Parameters:
+ * t: tree
+ * h: page to be split
+ * l: page to put lower half of data
+ * r: page to put upper half of data
+ * pskip: pointer to index to leave open
+ * ilen: insert length
+ *
+ * Returns:
+ * Pointer to page in which to insert.
+ */
+static PAGE *
+bt_psplit(t, h, l, r, pskip, ilen)
+ BTREE *t;
+ PAGE *h, *l, *r;
+ indx_t *pskip;
+ size_t ilen;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ CURSOR *c;
+ RLEAF *rl;
+ PAGE *rval;
+ void *src = 0;
+ indx_t full, half, nxt, off, skip, top, used;
+ u_int32_t nbytes;
+ int bigkeycnt, isbigkey;
+
+ /*
+ * Split the data to the left and right pages. Leave the skip index
+ * open. Additionally, make some effort not to split on an overflow
+ * key. This makes internal page processing faster and can save
+ * space as overflow keys used by internal pages are never deleted.
+ */
+ bigkeycnt = 0;
+ skip = *pskip;
+ full = t->bt_psize - BTDATAOFF;
+ half = full / 2;
+ used = 0;
+ for (nxt = off = 0, top = NEXTINDEX(h); nxt < top; ++off) {
+ if (skip == off) {
+ nbytes = ilen;
+ isbigkey = 0; /* XXX: not really known. */
+ } else
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ src = bi = GETBINTERNAL(h, nxt);
+ nbytes = NBINTERNAL(bi->ksize);
+ isbigkey = bi->flags & P_BIGKEY;
+ break;
+ case P_BLEAF:
+ src = bl = GETBLEAF(h, nxt);
+ nbytes = NBLEAF(bl);
+ isbigkey = bl->flags & P_BIGKEY;
+ break;
+ case P_RINTERNAL:
+ src = GETRINTERNAL(h, nxt);
+ nbytes = NRINTERNAL;
+ isbigkey = 0;
+ break;
+ case P_RLEAF:
+ src = rl = GETRLEAF(h, nxt);
+ nbytes = NRLEAF(rl);
+ isbigkey = 0;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * If the key/data pairs are substantial fractions of the max
+ * possible size for the page, it's possible to get situations
+ * where we decide to try and copy too much onto the left page.
+ * Make sure that doesn't happen.
+ */
+ if ((skip <= off && used + nbytes + sizeof(indx_t) >= full)
+ || nxt == top - 1) {
+ --off;
+ break;
+ }
+
+ /* Copy the key/data pair, if not the skipped index. */
+ if (skip != off) {
+ ++nxt;
+
+ l->linp[off] = l->upper -= nbytes;
+ memmove((char *)l + l->upper, src, nbytes);
+ }
+
+ used += nbytes + sizeof(indx_t);
+ if (used >= half) {
+ if (!isbigkey || bigkeycnt == 3)
+ break;
+ else
+ ++bigkeycnt;
+ }
+ }
+
+ /*
+ * Off is the last offset that's valid for the left page.
+ * Nxt is the first offset to be placed on the right page.
+ */
+ l->lower += (off + 1) * sizeof(indx_t);
+
+ /*
+ * If splitting the page that the cursor was on, the cursor has to be
+ * adjusted to point to the same record as before the split. If the
+ * cursor is at or past the skipped slot, the cursor is incremented by
+ * one. If the cursor is on the right page, it is decremented by the
+ * number of records split to the left page.
+ */
+ c = &t->bt_cursor;
+ if (F_ISSET(c, CURS_INIT) && c->pg.pgno == h->pgno) {
+ if (c->pg.index >= skip)
+ ++c->pg.index;
+ if (c->pg.index < nxt) /* Left page. */
+ c->pg.pgno = l->pgno;
+ else { /* Right page. */
+ c->pg.pgno = r->pgno;
+ c->pg.index -= nxt;
+ }
+ }
+
+ /*
+ * If the skipped index was on the left page, just return that page.
+ * Otherwise, adjust the skip index to reflect the new position on
+ * the right page.
+ */
+ if (skip <= off) {
+ skip = 0;
+ rval = l;
+ } else {
+ rval = r;
+ *pskip -= nxt;
+ }
+
+ for (off = 0; nxt < top; ++off) {
+ if (skip == nxt) {
+ ++off;
+ skip = 0;
+ }
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ src = bi = GETBINTERNAL(h, nxt);
+ nbytes = NBINTERNAL(bi->ksize);
+ break;
+ case P_BLEAF:
+ src = bl = GETBLEAF(h, nxt);
+ nbytes = NBLEAF(bl);
+ break;
+ case P_RINTERNAL:
+ src = GETRINTERNAL(h, nxt);
+ nbytes = NRINTERNAL;
+ break;
+ case P_RLEAF:
+ src = rl = GETRLEAF(h, nxt);
+ nbytes = NRLEAF(rl);
+ break;
+ default:
+ abort();
+ }
+ ++nxt;
+ r->linp[off] = r->upper -= nbytes;
+ memmove((char *)r + r->upper, src, nbytes);
+ }
+ r->lower += off * sizeof(indx_t);
+
+ /* If the key is being appended to the page, adjust the index. */
+ if (skip == top)
+ r->lower += sizeof(indx_t);
+
+ return (rval);
+}
+
+/*
+ * BT_PRESERVE -- Mark a chain of pages as used by an internal node.
+ *
+ * Chains of indirect blocks pointed to by leaf nodes get reclaimed when the
+ * record that references them gets deleted. Chains pointed to by internal
+ * pages never get deleted. This routine marks a chain as pointed to by an
+ * internal page.
+ *
+ * Parameters:
+ * t: tree
+ * pg: page number of first page in the chain.
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+static int
+bt_preserve(t, pg)
+ BTREE *t;
+ pgno_t pg;
+{
+ PAGE *h;
+
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ h->flags |= P_PRESERVE;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
+
+/*
+ * REC_TOTAL -- Return the number of recno entries below a page.
+ *
+ * Parameters:
+ * h: page
+ *
+ * Returns:
+ * The number of recno entries below a page.
+ *
+ * XXX
+ * These values could be set by the bt_psplit routine. The problem is that the
+ * entry has to be popped off of the stack etc. or the values have to be passed
+ * all the way back to bt_split/bt_rroot and it's not very clean.
+ */
+static recno_t
+rec_total(h)
+ PAGE *h;
+{
+ recno_t recs;
+ indx_t nxt, top;
+
+ for (recs = 0, nxt = 0, top = NEXTINDEX(h); nxt < top; ++nxt)
+ recs += GETRINTERNAL(h, nxt)->nrecs;
+ return (recs);
+}
diff --git a/trunk/main/db1-ast/btree/bt_utils.c b/trunk/main/db1-ast/btree/bt_utils.c
new file mode 100644
index 000000000..2ecb5e678
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_utils.c
@@ -0,0 +1,260 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_utils.c 8.8 (Berkeley) 7/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * __bt_ret --
+ * Build return key/data pair.
+ *
+ * Parameters:
+ * t: tree
+ * e: key/data pair to be returned
+ * key: user's key structure (NULL if not to be filled in)
+ * rkey: memory area to hold key
+ * data: user's data structure (NULL if not to be filled in)
+ * rdata: memory area to hold data
+ * copy: always copy the key/data item
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__bt_ret(t, e, key, rkey, data, rdata, copy)
+ BTREE *t;
+ EPG *e;
+ DBT *key, *rkey, *data, *rdata;
+ int copy;
+{
+ BLEAF *bl;
+ void *p;
+
+ bl = GETBLEAF(e->page, e->index);
+
+ /*
+ * We must copy big keys/data to make them contiguous. Otherwise,
+ * leave the page pinned and don't copy unless the user specified
+ * concurrent access.
+ */
+ if (key == NULL)
+ goto dataonly;
+
+ if (bl->flags & P_BIGKEY) {
+ if (__ovfl_get(t, bl->bytes,
+ &key->size, &rkey->data, &rkey->size))
+ return (RET_ERROR);
+ key->data = rkey->data;
+ } else if (copy || F_ISSET(t, B_DB_LOCK)) {
+ if (bl->ksize > rkey->size) {
+ p = (void *)(rkey->data == NULL ?
+ malloc(bl->ksize) : realloc(rkey->data, bl->ksize));
+ if (p == NULL)
+ return (RET_ERROR);
+ rkey->data = p;
+ rkey->size = bl->ksize;
+ }
+ memmove(rkey->data, bl->bytes, bl->ksize);
+ key->size = bl->ksize;
+ key->data = rkey->data;
+ } else {
+ key->size = bl->ksize;
+ key->data = bl->bytes;
+ }
+
+dataonly:
+ if (data == NULL)
+ return (RET_SUCCESS);
+
+ if (bl->flags & P_BIGDATA) {
+ if (__ovfl_get(t, bl->bytes + bl->ksize,
+ &data->size, &rdata->data, &rdata->size))
+ return (RET_ERROR);
+ data->data = rdata->data;
+ } else if (copy || F_ISSET(t, B_DB_LOCK)) {
+ /* Use +1 in case the first record retrieved is 0 length. */
+ if (bl->dsize + 1 > rdata->size) {
+ p = (void *)(rdata->data == NULL ?
+ malloc(bl->dsize + 1) :
+ realloc(rdata->data, bl->dsize + 1));
+ if (p == NULL)
+ return (RET_ERROR);
+ rdata->data = p;
+ rdata->size = bl->dsize + 1;
+ }
+ memmove(rdata->data, bl->bytes + bl->ksize, bl->dsize);
+ data->size = bl->dsize;
+ data->data = rdata->data;
+ } else {
+ data->size = bl->dsize;
+ data->data = bl->bytes + bl->ksize;
+ }
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * __BT_CMP -- Compare a key to a given record.
+ *
+ * Parameters:
+ * t: tree
+ * k1: DBT pointer of first arg to comparison
+ * e: pointer to EPG for comparison
+ *
+ * Returns:
+ * < 0 if k1 is < record
+ * = 0 if k1 is = record
+ * > 0 if k1 is > record
+ */
+int
+__bt_cmp(t, k1, e)
+ BTREE *t;
+ const DBT *k1;
+ EPG *e;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ DBT k2;
+ PAGE *h;
+ void *bigkey;
+
+ /*
+ * The left-most key on internal pages, at any level of the tree, is
+ * guaranteed by the following code to be less than any user key.
+ * This saves us from having to update the leftmost key on an internal
+ * page when the user inserts a new key in the tree smaller than
+ * anything we've yet seen.
+ */
+ h = e->page;
+ if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & P_BLEAF))
+ return (1);
+
+ bigkey = NULL;
+ if (h->flags & P_BLEAF) {
+ bl = GETBLEAF(h, e->index);
+ if (bl->flags & P_BIGKEY)
+ bigkey = bl->bytes;
+ else {
+ k2.data = bl->bytes;
+ k2.size = bl->ksize;
+ }
+ } else {
+ bi = GETBINTERNAL(h, e->index);
+ if (bi->flags & P_BIGKEY)
+ bigkey = bi->bytes;
+ else {
+ k2.data = bi->bytes;
+ k2.size = bi->ksize;
+ }
+ }
+
+ if (bigkey) {
+ if (__ovfl_get(t, bigkey,
+ &k2.size, &t->bt_rdata.data, &t->bt_rdata.size))
+ return (RET_ERROR);
+ k2.data = t->bt_rdata.data;
+ }
+ return ((*t->bt_cmp)(k1, &k2));
+}
+
+/*
+ * __BT_DEFCMP -- Default comparison routine.
+ *
+ * Parameters:
+ * a: DBT #1
+ * b: DBT #2
+ *
+ * Returns:
+ * < 0 if a is < b
+ * = 0 if a is = b
+ * > 0 if a is > b
+ */
+int
+__bt_defcmp(a, b)
+ const DBT *a, *b;
+{
+ register size_t len;
+ register u_char *p1, *p2;
+
+ /*
+ * XXX
+ * If a size_t doesn't fit in an int, this routine can lose.
+ * What we need is a integral type which is guaranteed to be
+ * larger than a size_t, and there is no such thing.
+ */
+ len = MIN(a->size, b->size);
+ for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2)
+ if (*p1 != *p2)
+ return ((int)*p1 - (int)*p2);
+ return ((int)a->size - (int)b->size);
+}
+
+/*
+ * __BT_DEFPFX -- Default prefix routine.
+ *
+ * Parameters:
+ * a: DBT #1
+ * b: DBT #2
+ *
+ * Returns:
+ * Number of bytes needed to distinguish b from a.
+ */
+size_t
+__bt_defpfx(a, b)
+ const DBT *a, *b;
+{
+ register u_char *p1, *p2;
+ register size_t cnt, len;
+
+ cnt = 1;
+ len = MIN(a->size, b->size);
+ for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2, ++cnt)
+ if (*p1 != *p2)
+ return (cnt);
+
+ /* a->size must be <= b->size, or they wouldn't be in this order. */
+ return (a->size < b->size ? a->size + 1 : a->size);
+}
diff --git a/trunk/main/db1-ast/btree/btree.h b/trunk/main/db1-ast/btree/btree.h
new file mode 100644
index 000000000..1f4a9ec91
--- /dev/null
+++ b/trunk/main/db1-ast/btree/btree.h
@@ -0,0 +1,391 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)btree.h 8.11 (Berkeley) 8/17/94
+ */
+
+/* Macros to set/clear/test flags. */
+#define F_SET(p, f) (p)->flags |= (f)
+#define F_CLR(p, f) (p)->flags &= ~(f)
+#define F_ISSET(p, f) ((p)->flags & (f))
+
+#include <mpool.h>
+
+#define mpool_open __mpool_open
+#define mpool_filter __mpool_filter
+#define mpool_new __mpool_new
+#define mpool_get __mpool_get
+#define mpool_put __mpool_put
+#define mpool_sync __mpool_sync
+#define mpool_close __mpool_close
+
+#define DEFMINKEYPAGE (2) /* Minimum keys per page */
+#define MINCACHE (5) /* Minimum cached pages */
+#define MINPSIZE (512) /* Minimum page size */
+
+/*
+ * Page 0 of a btree file contains a copy of the meta-data. This page is also
+ * used as an out-of-band page, i.e. page pointers that point to nowhere point
+ * to page 0. Page 1 is the root of the btree.
+ */
+#define P_INVALID 0 /* Invalid tree page number. */
+#define P_META 0 /* Tree metadata page number. */
+#define P_ROOT 1 /* Tree root page number. */
+
+/*
+ * There are five page layouts in the btree: btree internal pages (BINTERNAL),
+ * btree leaf pages (BLEAF), recno internal pages (RINTERNAL), recno leaf pages
+ * (RLEAF) and overflow pages. All five page types have a page header (PAGE).
+ * This implementation requires that values within structures NOT be padded.
+ * (ANSI C permits random padding.) If your compiler pads randomly you'll have
+ * to do some work to get this package to run.
+ */
+typedef struct _page {
+ pgno_t pgno; /* this page's page number */
+ pgno_t prevpg; /* left sibling */
+ pgno_t nextpg; /* right sibling */
+
+#define P_BINTERNAL 0x01 /* btree internal page */
+#define P_BLEAF 0x02 /* leaf page */
+#define P_OVERFLOW 0x04 /* overflow page */
+#define P_RINTERNAL 0x08 /* recno internal page */
+#define P_RLEAF 0x10 /* leaf page */
+#define P_TYPE 0x1f /* type mask */
+#define P_PRESERVE 0x20 /* never delete this chain of pages */
+ u_int32_t flags;
+
+ indx_t lower; /* lower bound of free space on page */
+ indx_t upper; /* upper bound of free space on page */
+ indx_t linp[1]; /* indx_t-aligned VAR. LENGTH DATA */
+} PAGE;
+
+/* First and next index. */
+#define BTDATAOFF \
+ (sizeof(pgno_t) + sizeof(pgno_t) + sizeof(pgno_t) + \
+ sizeof(u_int32_t) + sizeof(indx_t) + sizeof(indx_t))
+#define NEXTINDEX(p) (((p)->lower - BTDATAOFF) / sizeof(indx_t))
+
+/*
+ * For pages other than overflow pages, there is an array of offsets into the
+ * rest of the page immediately following the page header. Each offset is to
+ * an item which is unique to the type of page. The h_lower offset is just
+ * past the last filled-in index. The h_upper offset is the first item on the
+ * page. Offsets are from the beginning of the page.
+ *
+ * If an item is too big to store on a single page, a flag is set and the item
+ * is a { page, size } pair such that the page is the first page of an overflow
+ * chain with size bytes of item. Overflow pages are simply bytes without any
+ * external structure.
+ *
+ * The page number and size fields in the items are pgno_t-aligned so they can
+ * be manipulated without copying. (This presumes that 32 bit items can be
+ * manipulated on this system.)
+ */
+#define LALIGN(n) (((n) + sizeof(pgno_t) - 1) & ~(sizeof(pgno_t) - 1))
+#define NOVFLSIZE (sizeof(pgno_t) + sizeof(u_int32_t))
+
+/*
+ * For the btree internal pages, the item is a key. BINTERNALs are {key, pgno}
+ * pairs, such that the key compares less than or equal to all of the records
+ * on that page. For a tree without duplicate keys, an internal page with two
+ * consecutive keys, a and b, will have all records greater than or equal to a
+ * and less than b stored on the page associated with a. Duplicate keys are
+ * somewhat special and can cause duplicate internal and leaf page records and
+ * some minor modifications of the above rule.
+ */
+typedef struct _binternal {
+ u_int32_t ksize; /* key size */
+ pgno_t pgno; /* page number stored on */
+#define P_BIGDATA 0x01 /* overflow data */
+#define P_BIGKEY 0x02 /* overflow key */
+ u_char flags;
+ char bytes[1]; /* data */
+} BINTERNAL;
+
+/* Get the page's BINTERNAL structure at index indx. */
+#define GETBINTERNAL(pg, indx) \
+ ((BINTERNAL *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NBINTERNAL(len) \
+ LALIGN(sizeof(u_int32_t) + sizeof(pgno_t) + sizeof(u_char) + (len))
+
+/* Copy a BINTERNAL entry to the page. */
+#define WR_BINTERNAL(p, size, pgno, flags) { \
+ *(u_int32_t *)p = size; \
+ p += sizeof(u_int32_t); \
+ *(pgno_t *)p = pgno; \
+ p += sizeof(pgno_t); \
+ *(u_char *)p = flags; \
+ p += sizeof(u_char); \
+}
+
+/*
+ * For the recno internal pages, the item is a page number with the number of
+ * keys found on that page and below.
+ */
+typedef struct _rinternal {
+ recno_t nrecs; /* number of records */
+ pgno_t pgno; /* page number stored below */
+} RINTERNAL;
+
+/* Get the page's RINTERNAL structure at index indx. */
+#define GETRINTERNAL(pg, indx) \
+ ((RINTERNAL *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NRINTERNAL \
+ LALIGN(sizeof(recno_t) + sizeof(pgno_t))
+
+/* Copy a RINTERNAL entry to the page. */
+#define WR_RINTERNAL(p, nrecs, pgno) { \
+ *(recno_t *)p = nrecs; \
+ p += sizeof(recno_t); \
+ *(pgno_t *)p = pgno; \
+}
+
+/* For the btree leaf pages, the item is a key and data pair. */
+typedef struct _bleaf {
+ u_int32_t ksize; /* size of key */
+ u_int32_t dsize; /* size of data */
+ u_char flags; /* P_BIGDATA, P_BIGKEY */
+ char bytes[1]; /* data */
+} BLEAF;
+
+/* Get the page's BLEAF structure at index indx. */
+#define GETBLEAF(pg, indx) \
+ ((BLEAF *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NBLEAF(p) NBLEAFDBT((p)->ksize, (p)->dsize)
+
+/* Get the number of bytes in the user's key/data pair. */
+#define NBLEAFDBT(ksize, dsize) \
+ LALIGN(sizeof(u_int32_t) + sizeof(u_int32_t) + sizeof(u_char) + \
+ (ksize) + (dsize))
+
+/* Copy a BLEAF entry to the page. */
+#define WR_BLEAF(p, key, data, flags) { \
+ *(u_int32_t *)p = key->size; \
+ p += sizeof(u_int32_t); \
+ *(u_int32_t *)p = data->size; \
+ p += sizeof(u_int32_t); \
+ *(u_char *)p = flags; \
+ p += sizeof(u_char); \
+ memmove(p, key->data, key->size); \
+ p += key->size; \
+ memmove(p, data->data, data->size); \
+}
+
+/* For the recno leaf pages, the item is a data entry. */
+typedef struct _rleaf {
+ u_int32_t dsize; /* size of data */
+ u_char flags; /* P_BIGDATA */
+ char bytes[1];
+} RLEAF;
+
+/* Get the page's RLEAF structure at index indx. */
+#define GETRLEAF(pg, indx) \
+ ((RLEAF *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NRLEAF(p) NRLEAFDBT((p)->dsize)
+
+/* Get the number of bytes from the user's data. */
+#define NRLEAFDBT(dsize) \
+ LALIGN(sizeof(u_int32_t) + sizeof(u_char) + (dsize))
+
+/* Copy a RLEAF entry to the page. */
+#define WR_RLEAF(p, data, flags) { \
+ *(u_int32_t *)p = data->size; \
+ p += sizeof(u_int32_t); \
+ *(u_char *)p = flags; \
+ p += sizeof(u_char); \
+ memmove(p, data->data, data->size); \
+}
+
+/*
+ * A record in the tree is either a pointer to a page and an index in the page
+ * or a page number and an index. These structures are used as a cursor, stack
+ * entry and search returns as well as to pass records to other routines.
+ *
+ * One comment about searches. Internal page searches must find the largest
+ * record less than key in the tree so that descents work. Leaf page searches
+ * must find the smallest record greater than key so that the returned index
+ * is the record's correct position for insertion.
+ */
+typedef struct _epgno {
+ pgno_t pgno; /* the page number */
+ indx_t index; /* the index on the page */
+} EPGNO;
+
+typedef struct _epg {
+ PAGE *page; /* the (pinned) page */
+ indx_t index; /* the index on the page */
+} EPG;
+
+/*
+ * About cursors. The cursor (and the page that contained the key/data pair
+ * that it referenced) can be deleted, which makes things a bit tricky. If
+ * there are no duplicates of the cursor key in the tree (i.e. B_NODUPS is set
+ * or there simply aren't any duplicates of the key) we copy the key that it
+ * referenced when it's deleted, and reacquire a new cursor key if the cursor
+ * is used again. If there are duplicates keys, we move to the next/previous
+ * key, and set a flag so that we know what happened. NOTE: if duplicate (to
+ * the cursor) keys are added to the tree during this process, it is undefined
+ * if they will be returned or not in a cursor scan.
+ *
+ * The flags determine the possible states of the cursor:
+ *
+ * CURS_INIT The cursor references *something*.
+ * CURS_ACQUIRE The cursor was deleted, and a key has been saved so that
+ * we can reacquire the right position in the tree.
+ * CURS_AFTER, CURS_BEFORE
+ * The cursor was deleted, and now references a key/data pair
+ * that has not yet been returned, either before or after the
+ * deleted key/data pair.
+ * XXX
+ * This structure is broken out so that we can eventually offer multiple
+ * cursors as part of the DB interface.
+ */
+typedef struct _cursor {
+ EPGNO pg; /* B: Saved tree reference. */
+ DBT key; /* B: Saved key, or key.data == NULL. */
+ recno_t rcursor; /* R: recno cursor (1-based) */
+
+#define CURS_ACQUIRE 0x01 /* B: Cursor needs to be reacquired. */
+#define CURS_AFTER 0x02 /* B: Unreturned cursor after key. */
+#define CURS_BEFORE 0x04 /* B: Unreturned cursor before key. */
+#define CURS_INIT 0x08 /* RB: Cursor initialized. */
+ u_int8_t flags;
+} CURSOR;
+
+/*
+ * The metadata of the tree. The nrecs field is used only by the RECNO code.
+ * This is because the btree doesn't really need it and it requires that every
+ * put or delete call modify the metadata.
+ */
+typedef struct _btmeta {
+ u_int32_t magic; /* magic number */
+ u_int32_t version; /* version */
+ u_int32_t psize; /* page size */
+ u_int32_t free; /* page number of first free page */
+ u_int32_t nrecs; /* R: number of records */
+
+#define SAVEMETA (B_NODUPS | R_RECNO)
+ u_int32_t flags; /* bt_flags & SAVEMETA */
+} BTMETA;
+
+/* The in-memory btree/recno data structure. */
+typedef struct _btree {
+ MPOOL *bt_mp; /* memory pool cookie */
+
+ DB *bt_dbp; /* pointer to enclosing DB */
+
+ EPG bt_cur; /* current (pinned) page */
+ PAGE *bt_pinned; /* page pinned across calls */
+
+ CURSOR bt_cursor; /* cursor */
+
+#define BT_PUSH(t, p, i) { \
+ t->bt_sp->pgno = p; \
+ t->bt_sp->index = i; \
+ ++t->bt_sp; \
+}
+#define BT_POP(t) (t->bt_sp == t->bt_stack ? NULL : --t->bt_sp)
+#define BT_CLR(t) (t->bt_sp = t->bt_stack)
+ EPGNO bt_stack[50]; /* stack of parent pages */
+ EPGNO *bt_sp; /* current stack pointer */
+
+ DBT bt_rkey; /* returned key */
+ DBT bt_rdata; /* returned data */
+
+ int bt_fd; /* tree file descriptor */
+
+ pgno_t bt_free; /* next free page */
+ u_int32_t bt_psize; /* page size */
+ indx_t bt_ovflsize; /* cut-off for key/data overflow */
+ int bt_lorder; /* byte order */
+ /* sorted order */
+ enum { NOT, BACK, FORWARD } bt_order;
+ EPGNO bt_last; /* last insert */
+
+ /* B: key comparison function */
+ int (*bt_cmp) __P((const DBT *, const DBT *));
+ /* B: prefix comparison function */
+ size_t (*bt_pfx) __P((const DBT *, const DBT *));
+ /* R: recno input function */
+ int (*bt_irec) __P((struct _btree *, recno_t));
+
+ FILE *bt_rfp; /* R: record FILE pointer */
+ int bt_rfd; /* R: record file descriptor */
+
+ caddr_t bt_cmap; /* R: current point in mapped space */
+ caddr_t bt_smap; /* R: start of mapped space */
+ caddr_t bt_emap; /* R: end of mapped space */
+ size_t bt_msize; /* R: size of mapped region. */
+
+ recno_t bt_nrecs; /* R: number of records */
+ size_t bt_reclen; /* R: fixed record length */
+ u_char bt_bval; /* R: delimiting byte/pad character */
+
+/*
+ * NB:
+ * B_NODUPS and R_RECNO are stored on disk, and may not be changed.
+ */
+#define B_INMEM 0x00001 /* in-memory tree */
+#define B_METADIRTY 0x00002 /* need to write metadata */
+#define B_MODIFIED 0x00004 /* tree modified */
+#define B_NEEDSWAP 0x00008 /* if byte order requires swapping */
+#define B_RDONLY 0x00010 /* read-only tree */
+
+#define B_NODUPS 0x00020 /* no duplicate keys permitted */
+#define R_RECNO 0x00080 /* record oriented tree */
+
+#define R_CLOSEFP 0x00040 /* opened a file pointer */
+#define R_EOF 0x00100 /* end of input file reached. */
+#define R_FIXLEN 0x00200 /* fixed length records */
+#define R_MEMMAPPED 0x00400 /* memory mapped file. */
+#define R_INMEM 0x00800 /* in-memory file */
+#define R_MODIFIED 0x01000 /* modified file */
+#define R_RDONLY 0x02000 /* read-only file */
+
+#define B_DB_LOCK 0x04000 /* DB_LOCK specified. */
+#define B_DB_SHMEM 0x08000 /* DB_SHMEM specified. */
+#define B_DB_TXN 0x10000 /* DB_TXN specified. */
+ u_int32_t flags;
+} BTREE;
+
+#include "extern.h"
diff --git a/trunk/main/db1-ast/btree/extern.h b/trunk/main/db1-ast/btree/extern.h
new file mode 100644
index 000000000..ebd9c5492
--- /dev/null
+++ b/trunk/main/db1-ast/btree/extern.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.10 (Berkeley) 7/20/94
+ */
+
+int __bt_close __P((DB *));
+int __bt_cmp __P((BTREE *, const DBT *, EPG *));
+int __bt_crsrdel __P((BTREE *, EPGNO *));
+int __bt_defcmp __P((const DBT *, const DBT *));
+size_t __bt_defpfx __P((const DBT *, const DBT *));
+int __bt_delete __P((const DB *, const DBT *, u_int));
+int __bt_dleaf __P((BTREE *, const DBT *, PAGE *, u_int));
+int __bt_fd __P((const DB *));
+int __bt_free __P((BTREE *, PAGE *));
+int __bt_get __P((const DB *, const DBT *, DBT *, u_int));
+PAGE *__bt_new __P((BTREE *, pgno_t *));
+void __bt_pgin __P((void *, pgno_t, void *));
+void __bt_pgout __P((void *, pgno_t, void *));
+int __bt_push __P((BTREE *, pgno_t, int));
+int __bt_put __P((const DB *dbp, DBT *, const DBT *, u_int));
+int __bt_ret __P((BTREE *, EPG *, DBT *, DBT *, DBT *, DBT *, int));
+EPG *__bt_search __P((BTREE *, const DBT *, int *));
+int __bt_seq __P((const DB *, DBT *, DBT *, u_int));
+void __bt_setcur __P((BTREE *, pgno_t, u_int));
+int __bt_split __P((BTREE *, PAGE *,
+ const DBT *, const DBT *, int, size_t, u_int32_t));
+int __bt_sync __P((const DB *, u_int));
+
+int __ovfl_delete __P((BTREE *, void *));
+int __ovfl_get __P((BTREE *, void *, size_t *, void **, size_t *));
+int __ovfl_put __P((BTREE *, const DBT *, pgno_t *));
+
+#ifdef DEBUG
+void __bt_dnpage __P((DB *, pgno_t));
+void __bt_dpage __P((PAGE *));
+void __bt_dump __P((DB *));
+#endif
+#ifdef STATISTICS
+void __bt_stat __P((DB *));
+#endif
diff --git a/trunk/main/db1-ast/db/db.c b/trunk/main/db1-ast/db/db.c
new file mode 100644
index 000000000..8c0584d4c
--- /dev/null
+++ b/trunk/main/db1-ast/db/db.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)db.c 8.4 (Berkeley) 2/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "../include/db.h"
+
+DB *
+dbopen(fname, flags, mode, type, openinfo)
+ const char *fname;
+ int flags, mode;
+ DBTYPE type;
+ const void *openinfo;
+{
+
+#define DB_FLAGS (DB_LOCK | DB_SHMEM | DB_TXN)
+#define USE_OPEN_FLAGS \
+ (O_CREAT | O_EXCL | O_EXLOCK | O_NONBLOCK | O_RDONLY | \
+ O_RDWR | O_SHLOCK | O_TRUNC)
+
+ if ((flags & ~(USE_OPEN_FLAGS | DB_FLAGS)) == 0)
+ switch (type) {
+ case DB_BTREE:
+ return (__bt_open(fname, flags & USE_OPEN_FLAGS,
+ mode, openinfo, flags & DB_FLAGS));
+ case DB_HASH:
+ return (__hash_open(fname, flags & USE_OPEN_FLAGS,
+ mode, openinfo, flags & DB_FLAGS));
+ case DB_RECNO:
+ return (__rec_open(fname, flags & USE_OPEN_FLAGS,
+ mode, openinfo, flags & DB_FLAGS));
+ }
+ errno = EINVAL;
+ return (NULL);
+}
+
+static int
+__dberr __P((void))
+{
+ return (RET_ERROR);
+}
+
+/*
+ * __DBPANIC -- Stop.
+ *
+ * Parameters:
+ * dbp: pointer to the DB structure.
+ */
+void
+__dbpanic(dbp)
+ DB *dbp;
+{
+ /* The only thing that can succeed is a close. */
+ dbp->del = (int (*)__P((const struct __db *,
+ const DBT *, u_int))) __dberr;
+ dbp->get = (int (*)__P((const struct __db *,
+ const DBT *, DBT *, u_int))) __dberr;
+ dbp->put = (int (*)__P((const struct __db *,
+ DBT *, const DBT *, u_int))) __dberr;
+ dbp->seq = (int (*)__P((const struct __db *,
+ DBT *, DBT *, u_int))) __dberr;
+ dbp->sync = (int (*)__P((const struct __db *, u_int))) __dberr;
+ dbp->fd = (int (*)__P((const struct __db *))) __dberr;
+}
diff --git a/trunk/main/db1-ast/hash/README b/trunk/main/db1-ast/hash/README
new file mode 100644
index 000000000..f29ccf7e1
--- /dev/null
+++ b/trunk/main/db1-ast/hash/README
@@ -0,0 +1,72 @@
+# @(#)README 8.1 (Berkeley) 6/4/93
+
+This package implements a superset of the hsearch and dbm/ndbm libraries.
+
+Test Programs:
+ All test programs which need key/data pairs expect them entered
+ with key and data on separate lines
+
+ tcreat3.c
+ Takes
+ bucketsize (bsize),
+ fill factor (ffactor), and
+ initial number of elements (nelem).
+ Creates a hash table named hashtest containing the
+ keys/data pairs entered from standard in.
+ thash4.c
+ Takes
+ bucketsize (bsize),
+ fill factor (ffactor),
+ initial number of elements (nelem)
+ bytes of cache (ncached), and
+ file from which to read data (fname)
+ Creates a table from the key/data pairs on standard in and
+ then does a read of each key/data in fname
+ tdel.c
+ Takes
+ bucketsize (bsize), and
+ fill factor (ffactor).
+ file from which to read data (fname)
+ Reads each key/data pair from fname and deletes the
+ key from the hash table hashtest
+ tseq.c
+ Reads the key/data pairs in the file hashtest and writes them
+ to standard out.
+ tread2.c
+ Takes
+ butes of cache (ncached).
+ Reads key/data pairs from standard in and looks them up
+ in the file hashtest.
+ tverify.c
+ Reads key/data pairs from standard in, looks them up
+ in the file hashtest, and verifies that the data is
+ correct.
+
+NOTES:
+
+The file search.h is provided for using the hsearch compatible interface
+on BSD systems. On System V derived systems, search.h should appear in
+/usr/include.
+
+The man page ../man/db.3 explains the interface to the hashing system.
+The file hash.ps is a postscript copy of a paper explaining
+the history, implementation, and performance of the hash package.
+
+"bugs" or idiosyncracies
+
+If you have a lot of overflows, it is possible to run out of overflow
+pages. Currently, this will cause a message to be printed on stderr.
+Eventually, this will be indicated by a return error code.
+
+If you are using the ndbm interface and exit without flushing or closing the
+file, you may lose updates since the package buffers all writes. Also,
+the db interface only creates a single database file. To avoid overwriting
+the user's original file, the suffix ".db" is appended to the file name
+passed to dbm_open. Additionally, if your code "knows" about the historic
+.dir and .pag files, it will break.
+
+There is a fundamental difference between this package and the old hsearch.
+Hsearch requires the user to maintain the keys and data in the application's
+allocated memory while hash takes care of all storage management. The down
+side is that the byte strings passed in the ENTRY structure must be null
+terminated (both the keys and the data).
diff --git a/trunk/main/db1-ast/hash/extern.h b/trunk/main/db1-ast/hash/extern.h
new file mode 100644
index 000000000..4f1f23d67
--- /dev/null
+++ b/trunk/main/db1-ast/hash/extern.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.4 (Berkeley) 6/16/94
+ */
+
+BUFHEAD *__add_ovflpage __P((HTAB *, BUFHEAD *));
+int __addel __P((HTAB *, BUFHEAD *, const DBT *, const DBT *));
+int __big_delete __P((HTAB *, BUFHEAD *));
+int __big_insert __P((HTAB *, BUFHEAD *, const DBT *, const DBT *));
+int __big_keydata __P((HTAB *, BUFHEAD *, DBT *, DBT *, int));
+int __big_return __P((HTAB *, BUFHEAD *, int, DBT *, int));
+int __big_split __P((HTAB *, BUFHEAD *, BUFHEAD *, BUFHEAD *,
+ int, u_int32_t, SPLIT_RETURN *));
+int __buf_free __P((HTAB *, int, int));
+void __buf_init __P((HTAB *, int));
+u_int32_t __call_hash __P((HTAB *, char *, int));
+int __delpair __P((HTAB *, BUFHEAD *, int));
+int __expand_table __P((HTAB *));
+int __find_bigpair __P((HTAB *, BUFHEAD *, int, char *, int));
+u_int16_t __find_last_page __P((HTAB *, BUFHEAD **));
+void __free_ovflpage __P((HTAB *, BUFHEAD *));
+BUFHEAD *__get_buf __P((HTAB *, u_int32_t, BUFHEAD *, int));
+int __get_page __P((HTAB *, char *, u_int32_t, int, int, int));
+int __ibitmap __P((HTAB *, int, int, int));
+u_int32_t __hash_log2 __P((u_int32_t));
+int __put_page __P((HTAB *, char *, u_int32_t, int, int));
+void __reclaim_buf __P((HTAB *, BUFHEAD *));
+int __split_page __P((HTAB *, u_int32_t, u_int32_t));
+
+/* Default hash routine. */
+extern u_int32_t (*__default_hash) __P((const void *, size_t));
+
+#ifdef HASH_STATISTICS
+extern int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
+#endif
diff --git a/trunk/main/db1-ast/hash/hash.c b/trunk/main/db1-ast/hash/hash.c
new file mode 100644
index 000000000..47dc52a0e
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash.c
@@ -0,0 +1,999 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash.c 8.9 (Berkeley) 6/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static int alloc_segs __P((HTAB *, int));
+static int flush_meta __P((HTAB *));
+static int hash_access __P((HTAB *, ACTION, DBT *, DBT *));
+static int hash_close __P((DB *));
+static int hash_delete __P((const DB *, const DBT *, u_int32_t));
+static int hash_fd __P((const DB *));
+static int hash_get __P((const DB *, const DBT *, DBT *, u_int32_t));
+static int hash_put __P((const DB *, DBT *, const DBT *, u_int32_t));
+static void *hash_realloc __P((SEGMENT **, int, int));
+static int hash_seq __P((const DB *, DBT *, DBT *, u_int32_t));
+static int hash_sync __P((const DB *, u_int32_t));
+static int hdestroy __P((HTAB *));
+static HTAB *init_hash __P((HTAB *, const char *, const HASHINFO *));
+static int init_htab __P((HTAB *, int));
+#if BYTE_ORDER == LITTLE_ENDIAN
+static void swap_header __P((HTAB *));
+static void swap_header_copy __P((HASHHDR *, HASHHDR *));
+#endif
+
+/* Fast arithmetic, relying on powers of 2, */
+#define MOD(x, y) ((x) & ((y) - 1))
+
+#define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; }
+
+/* Return values */
+#define SUCCESS (0)
+#define ERROR (-1)
+#define ABNORMAL (1)
+
+#ifdef HASH_STATISTICS
+int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
+#endif
+
+/************************** INTERFACE ROUTINES ***************************/
+/* OPEN/CLOSE */
+
+extern DB *
+__hash_open(file, flags, mode, info, dflags)
+ const char *file;
+ int flags, mode, dflags;
+ const HASHINFO *info; /* Special directives for create */
+{
+ HTAB *hashp;
+ struct stat statbuf;
+ DB *dbp;
+ int bpages, hdrsize, new_table, nsegs, save_errno;
+
+ if ((flags & O_ACCMODE) == O_WRONLY) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB))))
+ return (NULL);
+ hashp->fp = -1;
+
+ /*
+ * Even if user wants write only, we need to be able to read
+ * the actual file, so we need to open it read/write. But, the
+ * field in the hashp structure needs to be accurate so that
+ * we can check accesses.
+ */
+ hashp->flags = flags;
+
+ new_table = 0;
+ if (!file || (flags & O_TRUNC) ||
+ (stat(file, &statbuf) && (errno == ENOENT))) {
+ if (errno == ENOENT)
+ errno = 0; /* Just in case someone looks at errno */
+ new_table = 1;
+ }
+ if (file) {
+ if ((hashp->fp = open(file, flags, mode)) == -1)
+ RETURN_ERROR(errno, error0);
+ (void)fcntl(hashp->fp, F_SETFD, 1);
+ }
+ if (new_table) {
+ if (!(hashp = init_hash(hashp, file, info)))
+ RETURN_ERROR(errno, error1);
+ } else {
+ /* Table already exists */
+ if (info && info->hash)
+ hashp->hash = info->hash;
+ else
+ hashp->hash = __default_hash;
+
+ hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
+#if BYTE_ORDER == LITTLE_ENDIAN
+ swap_header(hashp);
+#endif
+ if (hdrsize == -1)
+ RETURN_ERROR(errno, error1);
+ if (hdrsize != sizeof(HASHHDR))
+ RETURN_ERROR(EFTYPE, error1);
+ /* Verify file type, versions and hash function */
+ if (hashp->MAGIC != HASHMAGIC)
+ RETURN_ERROR(EFTYPE, error1);
+#define OLDHASHVERSION 1
+ if (hashp->VERSION != HASHVERSION &&
+ hashp->VERSION != OLDHASHVERSION)
+ RETURN_ERROR(EFTYPE, error1);
+ if (hashp->hash(CHARKEY, sizeof(CHARKEY))
+ != (u_int32_t) hashp->H_CHARKEY)
+ RETURN_ERROR(EFTYPE, error1);
+ /*
+ * Figure out how many segments we need. Max_Bucket is the
+ * maximum bucket number, so the number of buckets is
+ * max_bucket + 1.
+ */
+ nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
+ hashp->SGSIZE;
+ hashp->nsegs = 0;
+ if (alloc_segs(hashp, nsegs))
+ /*
+ * If alloc_segs fails, table will have been destroyed
+ * and errno will have been set.
+ */
+ return (NULL);
+ /* Read in bitmaps */
+ bpages = (hashp->SPARES[hashp->OVFL_POINT] +
+ (hashp->BSIZE << BYTE_SHIFT) - 1) >>
+ (hashp->BSHIFT + BYTE_SHIFT);
+
+ hashp->nmaps = bpages;
+ (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *));
+ }
+
+ /* Initialize Buffer Manager */
+ if (info && info->cachesize)
+ __buf_init(hashp, info->cachesize);
+ else
+ __buf_init(hashp, DEF_BUFSIZE);
+
+ hashp->new_file = new_table;
+ hashp->save_file = file && (hashp->flags & O_ACCMODE) != O_RDONLY;
+ hashp->cbucket = -1;
+ if (!(dbp = (DB *)malloc(sizeof(DB)))) {
+ save_errno = errno;
+ hdestroy(hashp);
+ errno = save_errno;
+ return (NULL);
+ }
+ dbp->internal = hashp;
+ dbp->close = hash_close;
+ dbp->del = hash_delete;
+ dbp->fd = hash_fd;
+ dbp->get = hash_get;
+ dbp->put = hash_put;
+ dbp->seq = hash_seq;
+ dbp->sync = hash_sync;
+ dbp->type = DB_HASH;
+
+#ifdef DEBUG
+ (void)fprintf(stderr,
+"%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
+ "init_htab:",
+ "TABLE POINTER ", hashp,
+ "BUCKET SIZE ", hashp->BSIZE,
+ "BUCKET SHIFT ", hashp->BSHIFT,
+ "DIRECTORY SIZE ", hashp->DSIZE,
+ "SEGMENT SIZE ", hashp->SGSIZE,
+ "SEGMENT SHIFT ", hashp->SSHIFT,
+ "FILL FACTOR ", hashp->FFACTOR,
+ "MAX BUCKET ", hashp->MAX_BUCKET,
+ "OVFL POINT ", hashp->OVFL_POINT,
+ "LAST FREED ", hashp->LAST_FREED,
+ "HIGH MASK ", hashp->HIGH_MASK,
+ "LOW MASK ", hashp->LOW_MASK,
+ "NSEGS ", hashp->nsegs,
+ "NKEYS ", hashp->NKEYS);
+#endif
+#ifdef HASH_STATISTICS
+ hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
+#endif
+ return (dbp);
+
+error1:
+ if (hashp != NULL)
+ (void)close(hashp->fp);
+
+error0:
+ free(hashp);
+ errno = save_errno;
+ return (NULL);
+}
+
+static int
+hash_close(dbp)
+ DB *dbp;
+{
+ HTAB *hashp;
+ int retval;
+
+ if (!dbp)
+ return (ERROR);
+
+ hashp = (HTAB *)dbp->internal;
+ retval = hdestroy(hashp);
+ free(dbp);
+ return (retval);
+}
+
+static int
+hash_fd(dbp)
+ const DB *dbp;
+{
+ HTAB *hashp;
+
+ if (!dbp)
+ return (ERROR);
+
+ hashp = (HTAB *)dbp->internal;
+ if (hashp->fp == -1) {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (hashp->fp);
+}
+
+/************************** LOCAL CREATION ROUTINES **********************/
+static HTAB *
+init_hash(hashp, file, info)
+ HTAB *hashp;
+ const char *file;
+ const HASHINFO *info;
+{
+#ifdef _STATBUF_ST_BLKSIZE
+ struct stat statbuf;
+#endif
+ int nelem;
+
+ nelem = 1;
+ hashp->NKEYS = 0;
+ hashp->LORDER = BYTE_ORDER;
+ hashp->BSIZE = DEF_BUCKET_SIZE;
+ hashp->BSHIFT = DEF_BUCKET_SHIFT;
+ hashp->SGSIZE = DEF_SEGSIZE;
+ hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
+ hashp->DSIZE = DEF_DIRSIZE;
+ hashp->FFACTOR = DEF_FFACTOR;
+ hashp->hash = __default_hash;
+ memset(hashp->SPARES, 0, sizeof(hashp->SPARES));
+ memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS));
+
+ /* Fix bucket size to be optimal for file system */
+#ifdef _STATBUF_ST_BLKSIZE
+ if (file != NULL) {
+ if (stat(file, &statbuf))
+ return (NULL);
+ hashp->BSIZE = statbuf.st_blksize;
+ hashp->BSHIFT = __hash_log2(hashp->BSIZE);
+ }
+#endif
+
+ if (info) {
+ if (info->bsize) {
+ /* Round pagesize up to power of 2 */
+ hashp->BSHIFT = __hash_log2(info->bsize);
+ hashp->BSIZE = 1 << hashp->BSHIFT;
+ if (hashp->BSIZE > MAX_BSIZE) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ }
+ if (info->ffactor)
+ hashp->FFACTOR = info->ffactor;
+ if (info->hash)
+ hashp->hash = info->hash;
+ if (info->nelem)
+ nelem = info->nelem;
+ if (info->lorder) {
+ if (info->lorder != BIG_ENDIAN &&
+ info->lorder != LITTLE_ENDIAN) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ hashp->LORDER = info->lorder;
+ }
+ }
+ /* init_htab should destroy the table and set errno if it fails */
+ if (init_htab(hashp, nelem))
+ return (NULL);
+ else
+ return (hashp);
+}
+/*
+ * This calls alloc_segs which may run out of memory. Alloc_segs will destroy
+ * the table and set errno, so we just pass the error information along.
+ *
+ * Returns 0 on No Error
+ */
+static int
+init_htab(hashp, nelem)
+ HTAB *hashp;
+ int nelem;
+{
+ register int nbuckets, nsegs;
+ int l2;
+
+ /*
+ * Divide number of elements by the fill factor and determine a
+ * desired number of buckets. Allocate space for the next greater
+ * power of two number of buckets.
+ */
+ nelem = (nelem - 1) / hashp->FFACTOR + 1;
+
+ l2 = __hash_log2(MAX(nelem, 2));
+ nbuckets = 1 << l2;
+
+ hashp->SPARES[l2] = l2 + 1;
+ hashp->SPARES[l2 + 1] = l2 + 1;
+ hashp->OVFL_POINT = l2;
+ hashp->LAST_FREED = 2;
+
+ /* First bitmap page is at: splitpoint l2 page offset 1 */
+ if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0))
+ return (-1);
+
+ hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
+ hashp->HIGH_MASK = (nbuckets << 1) - 1;
+ hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
+ hashp->BSHIFT) + 1;
+
+ nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
+ nsegs = 1 << __hash_log2(nsegs);
+
+ if (nsegs > hashp->DSIZE)
+ hashp->DSIZE = nsegs;
+ return (alloc_segs(hashp, nsegs));
+}
+
+/********************** DESTROY/CLOSE ROUTINES ************************/
+
+/*
+ * Flushes any changes to the file if necessary and destroys the hashp
+ * structure, freeing all allocated space.
+ */
+static int
+hdestroy(hashp)
+ HTAB *hashp;
+{
+ int i, save_errno;
+
+ save_errno = 0;
+
+#ifdef HASH_STATISTICS
+ (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
+ hash_accesses, hash_collisions);
+ (void)fprintf(stderr, "hdestroy: expansions %ld\n",
+ hash_expansions);
+ (void)fprintf(stderr, "hdestroy: overflows %ld\n",
+ hash_overflows);
+ (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
+ hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
+
+ for (i = 0; i < NCACHED; i++)
+ (void)fprintf(stderr,
+ "spares[%d] = %d\n", i, hashp->SPARES[i]);
+#endif
+ /*
+ * Call on buffer manager to free buffers, and if required,
+ * write them to disk.
+ */
+ if (__buf_free(hashp, 1, hashp->save_file))
+ save_errno = errno;
+ if (hashp->dir) {
+ free(*hashp->dir); /* Free initial segments */
+ /* Free extra segments */
+ while (hashp->exsegs--)
+ free(hashp->dir[--hashp->nsegs]);
+ free(hashp->dir);
+ }
+ if (flush_meta(hashp) && !save_errno)
+ save_errno = errno;
+ /* Free Bigmaps */
+ for (i = 0; i < hashp->nmaps; i++)
+ if (hashp->mapp[i])
+ free(hashp->mapp[i]);
+
+ if (hashp->fp != -1)
+ (void)close(hashp->fp);
+
+ free(hashp);
+
+ if (save_errno) {
+ errno = save_errno;
+ return (ERROR);
+ }
+ return (SUCCESS);
+}
+/*
+ * Write modified pages to disk
+ *
+ * Returns:
+ * 0 == OK
+ * -1 ERROR
+ */
+static int
+hash_sync(dbp, flags)
+ const DB *dbp;
+ u_int32_t flags;
+{
+ HTAB *hashp;
+
+ if (flags != 0) {
+ errno = EINVAL;
+ return (ERROR);
+ }
+
+ if (!dbp)
+ return (ERROR);
+
+ hashp = (HTAB *)dbp->internal;
+ if (!hashp->save_file)
+ return (0);
+ if (__buf_free(hashp, 0, 1) || flush_meta(hashp))
+ return (ERROR);
+ hashp->new_file = 0;
+ return (0);
+}
+
+/*
+ * Returns:
+ * 0 == OK
+ * -1 indicates that errno should be set
+ */
+static int
+flush_meta(hashp)
+ HTAB *hashp;
+{
+ HASHHDR *whdrp;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ HASHHDR whdr;
+#endif
+ int fp, i, wsize;
+
+ if (!hashp->save_file)
+ return (0);
+ hashp->MAGIC = HASHMAGIC;
+ hashp->VERSION = HASHVERSION;
+ hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
+
+ fp = hashp->fp;
+ whdrp = &hashp->hdr;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ whdrp = &whdr;
+ swap_header_copy(&hashp->hdr, whdrp);
+#endif
+ if ((lseek(fp, (off_t)0, SEEK_SET) == -1) ||
+ ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
+ return (-1);
+ else
+ if (wsize != sizeof(HASHHDR)) {
+ errno = EFTYPE;
+ hashp->errnum = errno;
+ return (-1);
+ }
+ for (i = 0; i < NCACHED; i++)
+ if (hashp->mapp[i])
+ if (__put_page(hashp, (char *)hashp->mapp[i],
+ hashp->BITMAPS[i], 0, 1))
+ return (-1);
+ return (0);
+}
+
+/*******************************SEARCH ROUTINES *****************************/
+/*
+ * All the access routines return
+ *
+ * Returns:
+ * 0 on SUCCESS
+ * 1 to indicate an external ERROR (i.e. key not found, etc)
+ * -1 to indicate an internal ERROR (i.e. out of memory, etc)
+ */
+static int
+hash_get(dbp, key, data, flag)
+ const DB *dbp;
+ const DBT *key;
+ DBT *data;
+ u_int32_t flag;
+{
+ HTAB *hashp;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+ return (hash_access(hashp, HASH_GET, (DBT *)key, data));
+}
+
+static int
+hash_put(dbp, key, data, flag)
+ const DB *dbp;
+ DBT *key;
+ const DBT *data;
+ u_int32_t flag;
+{
+ HTAB *hashp;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag && flag != R_NOOVERWRITE) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+ if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
+ hashp->errnum = errno = EPERM;
+ return (ERROR);
+ }
+ return (hash_access(hashp, flag == R_NOOVERWRITE ?
+ HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
+}
+
+static int
+hash_delete(dbp, key, flag)
+ const DB *dbp;
+ const DBT *key;
+ u_int32_t flag; /* Ignored */
+{
+ HTAB *hashp;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag && flag != R_CURSOR) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+ if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
+ hashp->errnum = errno = EPERM;
+ return (ERROR);
+ }
+ return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL));
+}
+
+/*
+ * Assume that hashp has been set in wrapper routine.
+ */
+static int
+hash_access(hashp, action, key, val)
+ HTAB *hashp;
+ ACTION action;
+ DBT *key, *val;
+{
+ register BUFHEAD *rbufp;
+ BUFHEAD *bufp, *save_bufp;
+ register u_int16_t *bp;
+ register int n, ndx, off, size;
+ register char *kp;
+ u_int16_t pageno;
+
+#ifdef HASH_STATISTICS
+ hash_accesses++;
+#endif
+
+ off = hashp->BSIZE;
+ size = key->size;
+ kp = (char *)key->data;
+ rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
+ if (!rbufp)
+ return (ERROR);
+ save_bufp = rbufp;
+
+ /* Pin the bucket chain */
+ rbufp->flags |= BUF_PIN;
+ for (bp = (u_int16_t *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
+ if (bp[1] >= REAL_KEY) {
+ /* Real key/data pair */
+ if (size == off - *bp &&
+ memcmp(kp, rbufp->page + *bp, size) == 0)
+ goto found;
+ off = bp[1];
+#ifdef HASH_STATISTICS
+ hash_collisions++;
+#endif
+ bp += 2;
+ ndx += 2;
+ } else if (bp[1] == OVFLPAGE) {
+ rbufp = __get_buf(hashp, *bp, rbufp, 0);
+ if (!rbufp) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ /* FOR LOOP INIT */
+ bp = (u_int16_t *)rbufp->page;
+ n = *bp++;
+ ndx = 1;
+ off = hashp->BSIZE;
+ } else if (bp[1] < REAL_KEY) {
+ if ((ndx =
+ __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0)
+ goto found;
+ if (ndx == -2) {
+ bufp = rbufp;
+ if (!(pageno =
+ __find_last_page(hashp, &bufp))) {
+ ndx = 0;
+ rbufp = bufp;
+ break; /* FOR */
+ }
+ rbufp = __get_buf(hashp, pageno, bufp, 0);
+ if (!rbufp) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ /* FOR LOOP INIT */
+ bp = (u_int16_t *)rbufp->page;
+ n = *bp++;
+ ndx = 1;
+ off = hashp->BSIZE;
+ } else {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ }
+
+ /* Not found */
+ switch (action) {
+ case HASH_PUT:
+ case HASH_PUTNEW:
+ if (__addel(hashp, rbufp, key, val)) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ } else {
+ save_bufp->flags &= ~BUF_PIN;
+ return (SUCCESS);
+ }
+ case HASH_GET:
+ case HASH_DELETE:
+ default:
+ save_bufp->flags &= ~BUF_PIN;
+ return (ABNORMAL);
+ }
+
+found:
+ switch (action) {
+ case HASH_PUTNEW:
+ save_bufp->flags &= ~BUF_PIN;
+ return (ABNORMAL);
+ case HASH_GET:
+ bp = (u_int16_t *)rbufp->page;
+ if (bp[ndx + 1] < REAL_KEY) {
+ if (__big_return(hashp, rbufp, ndx, val, 0))
+ return (ERROR);
+ } else {
+ val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
+ val->size = bp[ndx] - bp[ndx + 1];
+ }
+ break;
+ case HASH_PUT:
+ if ((__delpair(hashp, rbufp, ndx)) ||
+ (__addel(hashp, rbufp, key, val))) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ break;
+ case HASH_DELETE:
+ if (__delpair(hashp, rbufp, ndx))
+ return (ERROR);
+ break;
+ default:
+ abort();
+ }
+ save_bufp->flags &= ~BUF_PIN;
+ return (SUCCESS);
+}
+
+static int
+hash_seq(dbp, key, data, flag)
+ const DB *dbp;
+ DBT *key, *data;
+ u_int32_t flag;
+{
+ register u_int32_t bucket;
+ register BUFHEAD *bufp = NULL;
+ HTAB *hashp;
+ u_int16_t *bp, ndx;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag && flag != R_FIRST && flag != R_NEXT) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+#ifdef HASH_STATISTICS
+ hash_accesses++;
+#endif
+ if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
+ hashp->cbucket = 0;
+ hashp->cndx = 1;
+ hashp->cpage = NULL;
+ }
+
+ for (bp = NULL; !bp || !bp[0]; ) {
+ if (!(bufp = hashp->cpage)) {
+ for (bucket = hashp->cbucket;
+ bucket <= (u_int32_t) hashp->MAX_BUCKET;
+ bucket++, hashp->cndx = 1) {
+ bufp = __get_buf(hashp, bucket, NULL, 0);
+ if (!bufp)
+ return (ERROR);
+ hashp->cpage = bufp;
+ bp = (u_int16_t *)bufp->page;
+ if (bp[0])
+ break;
+ }
+ hashp->cbucket = bucket;
+ if (hashp->cbucket > hashp->MAX_BUCKET) {
+ hashp->cbucket = -1;
+ return (ABNORMAL);
+ }
+ } else
+ bp = (u_int16_t *)hashp->cpage->page;
+
+#ifdef DEBUG
+ assert(bp);
+ assert(bufp);
+#endif
+ while (bp[hashp->cndx + 1] == OVFLPAGE) {
+ bufp = hashp->cpage =
+ __get_buf(hashp, bp[hashp->cndx], bufp, 0);
+ if (!bufp)
+ return (ERROR);
+ bp = (u_int16_t *)(bufp->page);
+ hashp->cndx = 1;
+ }
+ if (!bp[0]) {
+ hashp->cpage = NULL;
+ ++hashp->cbucket;
+ }
+ }
+ ndx = hashp->cndx;
+ if (bp[ndx + 1] < REAL_KEY) {
+ if (__big_keydata(hashp, bufp, key, data, 1))
+ return (ERROR);
+ } else {
+ key->data = (u_char *)hashp->cpage->page + bp[ndx];
+ key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
+ data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
+ data->size = bp[ndx] - bp[ndx + 1];
+ ndx += 2;
+ if (ndx > bp[0]) {
+ hashp->cpage = NULL;
+ hashp->cbucket++;
+ hashp->cndx = 1;
+ } else
+ hashp->cndx = ndx;
+ }
+ return (SUCCESS);
+}
+
+/********************************* UTILITIES ************************/
+
+/*
+ * Returns:
+ * 0 ==> OK
+ * -1 ==> Error
+ */
+extern int
+__expand_table(hashp)
+ HTAB *hashp;
+{
+ u_int32_t old_bucket, new_bucket;
+ int dirsize, new_segnum, spare_ndx;
+
+#ifdef HASH_STATISTICS
+ hash_expansions++;
+#endif
+ new_bucket = ++hashp->MAX_BUCKET;
+ old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
+
+ new_segnum = new_bucket >> hashp->SSHIFT;
+
+ /* Check if we need a new segment */
+ if (new_segnum >= hashp->nsegs) {
+ /* Check if we need to expand directory */
+ if (new_segnum >= hashp->DSIZE) {
+ /* Reallocate directory */
+ dirsize = hashp->DSIZE * sizeof(SEGMENT *);
+ if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
+ return (-1);
+ hashp->DSIZE = dirsize << 1;
+ }
+ if ((hashp->dir[new_segnum] =
+ (SEGMENT)calloc(hashp->SGSIZE, sizeof(SEGMENT))) == NULL)
+ return (-1);
+ hashp->exsegs++;
+ hashp->nsegs++;
+ }
+ /*
+ * If the split point is increasing (MAX_BUCKET's log base 2
+ * * increases), we need to copy the current contents of the spare
+ * split bucket to the next bucket.
+ */
+ spare_ndx = __hash_log2(hashp->MAX_BUCKET + 1);
+ if (spare_ndx > hashp->OVFL_POINT) {
+ hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
+ hashp->OVFL_POINT = spare_ndx;
+ }
+
+ if (new_bucket > (u_int32_t) hashp->HIGH_MASK) {
+ /* Starting a new doubling */
+ hashp->LOW_MASK = hashp->HIGH_MASK;
+ hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
+ }
+ /* Relocate records to the new bucket */
+ return (__split_page(hashp, old_bucket, new_bucket));
+}
+
+/*
+ * If realloc guarantees that the pointer is not destroyed if the realloc
+ * fails, then this routine can go away.
+ */
+static void *
+hash_realloc(p_ptr, oldsize, newsize)
+ SEGMENT **p_ptr;
+ int oldsize, newsize;
+{
+ register void *p;
+
+ if ((p = malloc(newsize))) {
+ memmove(p, *p_ptr, oldsize);
+ memset((char *)p + oldsize, 0, newsize - oldsize);
+ free(*p_ptr);
+ *p_ptr = p;
+ }
+ return (p);
+}
+
+extern u_int32_t
+__call_hash(hashp, k, len)
+ HTAB *hashp;
+ char *k;
+ int len;
+{
+ int n, bucket;
+
+ n = hashp->hash(k, len);
+ bucket = n & hashp->HIGH_MASK;
+ if (bucket > hashp->MAX_BUCKET)
+ bucket = bucket & hashp->LOW_MASK;
+ return (bucket);
+}
+
+/*
+ * Allocate segment table. On error, destroy the table and set errno.
+ *
+ * Returns 0 on success
+ */
+static int
+alloc_segs(hashp, nsegs)
+ HTAB *hashp;
+ int nsegs;
+{
+ register int i;
+ register SEGMENT store;
+
+ int save_errno;
+
+ if ((hashp->dir =
+ (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *))) == NULL) {
+ save_errno = errno;
+ (void)hdestroy(hashp);
+ errno = save_errno;
+ return (-1);
+ }
+ /* Allocate segments */
+ if ((store =
+ (SEGMENT)calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT))) == NULL) {
+ save_errno = errno;
+ (void)hdestroy(hashp);
+ errno = save_errno;
+ return (-1);
+ }
+ for (i = 0; i < nsegs; i++, hashp->nsegs++)
+ hashp->dir[i] = &store[i << hashp->SSHIFT];
+ return (0);
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+/*
+ * Hashp->hdr needs to be byteswapped.
+ */
+static void
+swap_header_copy(srcp, destp)
+ HASHHDR *srcp, *destp;
+{
+ int i;
+
+ P_32_COPY(srcp->magic, destp->magic);
+ P_32_COPY(srcp->version, destp->version);
+ P_32_COPY(srcp->lorder, destp->lorder);
+ P_32_COPY(srcp->bsize, destp->bsize);
+ P_32_COPY(srcp->bshift, destp->bshift);
+ P_32_COPY(srcp->dsize, destp->dsize);
+ P_32_COPY(srcp->ssize, destp->ssize);
+ P_32_COPY(srcp->sshift, destp->sshift);
+ P_32_COPY(srcp->ovfl_point, destp->ovfl_point);
+ P_32_COPY(srcp->last_freed, destp->last_freed);
+ P_32_COPY(srcp->max_bucket, destp->max_bucket);
+ P_32_COPY(srcp->high_mask, destp->high_mask);
+ P_32_COPY(srcp->low_mask, destp->low_mask);
+ P_32_COPY(srcp->ffactor, destp->ffactor);
+ P_32_COPY(srcp->nkeys, destp->nkeys);
+ P_32_COPY(srcp->hdrpages, destp->hdrpages);
+ P_32_COPY(srcp->h_charkey, destp->h_charkey);
+ for (i = 0; i < NCACHED; i++) {
+ P_32_COPY(srcp->spares[i], destp->spares[i]);
+ P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
+ }
+}
+
+static void
+swap_header(hashp)
+ HTAB *hashp;
+{
+ HASHHDR *hdrp;
+ int i;
+
+ hdrp = &hashp->hdr;
+
+ M_32_SWAP(hdrp->magic);
+ M_32_SWAP(hdrp->version);
+ M_32_SWAP(hdrp->lorder);
+ M_32_SWAP(hdrp->bsize);
+ M_32_SWAP(hdrp->bshift);
+ M_32_SWAP(hdrp->dsize);
+ M_32_SWAP(hdrp->ssize);
+ M_32_SWAP(hdrp->sshift);
+ M_32_SWAP(hdrp->ovfl_point);
+ M_32_SWAP(hdrp->last_freed);
+ M_32_SWAP(hdrp->max_bucket);
+ M_32_SWAP(hdrp->high_mask);
+ M_32_SWAP(hdrp->low_mask);
+ M_32_SWAP(hdrp->ffactor);
+ M_32_SWAP(hdrp->nkeys);
+ M_32_SWAP(hdrp->hdrpages);
+ M_32_SWAP(hdrp->h_charkey);
+ for (i = 0; i < NCACHED; i++) {
+ M_32_SWAP(hdrp->spares[i]);
+ M_16_SWAP(hdrp->bitmaps[i]);
+ }
+}
+#endif
diff --git a/trunk/main/db1-ast/hash/hash.h b/trunk/main/db1-ast/hash/hash.h
new file mode 100644
index 000000000..d07db6f07
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash.h
@@ -0,0 +1,293 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hash.h 8.3 (Berkeley) 5/31/94
+ */
+
+/* Operations */
+typedef enum {
+ HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT
+} ACTION;
+
+/* Buffer Management structures */
+typedef struct _bufhead BUFHEAD;
+
+struct _bufhead {
+ BUFHEAD *prev; /* LRU links */
+ BUFHEAD *next; /* LRU links */
+ BUFHEAD *ovfl; /* Overflow page buffer header */
+ u_int32_t addr; /* Address of this page */
+ char *page; /* Actual page data */
+ char flags;
+#define BUF_MOD 0x0001
+#define BUF_DISK 0x0002
+#define BUF_BUCKET 0x0004
+#define BUF_PIN 0x0008
+};
+
+#define IS_BUCKET(X) ((X) & BUF_BUCKET)
+
+typedef BUFHEAD **SEGMENT;
+
+/* Hash Table Information */
+typedef struct hashhdr { /* Disk resident portion */
+ int magic; /* Magic NO for hash tables */
+ int version; /* Version ID */
+ u_int32_t lorder; /* Byte Order */
+ int bsize; /* Bucket/Page Size */
+ int bshift; /* Bucket shift */
+ int dsize; /* Directory Size */
+ int ssize; /* Segment Size */
+ int sshift; /* Segment shift */
+ int ovfl_point; /* Where overflow pages are being
+ * allocated */
+ int last_freed; /* Last overflow page freed */
+ int max_bucket; /* ID of Maximum bucket in use */
+ int high_mask; /* Mask to modulo into entire table */
+ int low_mask; /* Mask to modulo into lower half of
+ * table */
+ int ffactor; /* Fill factor */
+ int nkeys; /* Number of keys in hash table */
+ int hdrpages; /* Size of table header */
+ int h_charkey; /* value of hash(CHARKEY) */
+#define NCACHED 32 /* number of bit maps and spare
+ * points */
+ int spares[NCACHED];/* spare pages for overflow */
+ u_int16_t bitmaps[NCACHED]; /* address of overflow page
+ * bitmaps */
+} HASHHDR;
+
+typedef struct htab { /* Memory resident data structure */
+ HASHHDR hdr; /* Header */
+ int nsegs; /* Number of allocated segments */
+ int exsegs; /* Number of extra allocated
+ * segments */
+ u_int32_t /* Hash function */
+ (*hash)__P((const void *, size_t));
+ int flags; /* Flag values */
+ int fp; /* File pointer */
+ char *tmp_buf; /* Temporary Buffer for BIG data */
+ char *tmp_key; /* Temporary Buffer for BIG keys */
+ BUFHEAD *cpage; /* Current page */
+ int cbucket; /* Current bucket */
+ int cndx; /* Index of next item on cpage */
+ int errnum; /* Error Number -- for DBM
+ * compatibility */
+ int new_file; /* Indicates if fd is backing store
+ * or no */
+ int save_file; /* Indicates whether we need to flush
+ * file at
+ * exit */
+ u_int32_t *mapp[NCACHED]; /* Pointers to page maps */
+ int nmaps; /* Initial number of bitmaps */
+ int nbufs; /* Number of buffers left to
+ * allocate */
+ BUFHEAD bufhead; /* Header of buffer lru list */
+ SEGMENT *dir; /* Hash Bucket directory */
+} HTAB;
+
+/*
+ * Constants
+ */
+#define MAX_BSIZE 65536 /* 2^16 */
+#define MIN_BUFFERS 6
+#define MINHDRSIZE 512
+#define DEF_BUFSIZE 65536 /* 64 K */
+#define DEF_BUCKET_SIZE 4096
+#define DEF_BUCKET_SHIFT 12 /* log2(BUCKET) */
+#define DEF_SEGSIZE 256
+#define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */
+#define DEF_DIRSIZE 256
+#define DEF_FFACTOR 65536
+#define MIN_FFACTOR 4
+#define SPLTMAX 8
+#define CHARKEY "%$sniglet^&"
+#define NUMKEY 1038583
+#define BYTE_SHIFT 3
+#define INT_TO_BYTE 2
+#define INT_BYTE_SHIFT 5
+#define ALL_SET ((u_int32_t)0xFFFFFFFF)
+#define ALL_CLEAR 0
+
+#define PTROF(X) ((BUFHEAD *)((ptrdiff_t)(X)&~0x3))
+#define ISMOD(X) ((u_int32_t)(ptrdiff_t)(X)&0x1)
+#define DOMOD(X) ((X) = (char *)((ptrdiff_t)(X)|0x1))
+#define ISDISK(X) ((u_int32_t)(ptrdiff_t)(X)&0x2)
+#define DODISK(X) ((X) = (char *)((ptrdiff_t)(X)|0x2))
+
+#define BITS_PER_MAP 32
+
+/* Given the address of the beginning of a big map, clear/set the nth bit */
+#define CLRBIT(A, N) ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP)))
+#define SETBIT(A, N) ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP)))
+#define ISSET(A, N) ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP)))
+
+/* Overflow management */
+/*
+ * Overflow page numbers are allocated per split point. At each doubling of
+ * the table, we can allocate extra pages. So, an overflow page number has
+ * the top 5 bits indicate which split point and the lower 11 bits indicate
+ * which page at that split point is indicated (pages within split points are
+ * numberered starting with 1).
+ */
+
+#define SPLITSHIFT 11
+#define SPLITMASK 0x7FF
+#define SPLITNUM(N) (((u_int32_t)(N)) >> SPLITSHIFT)
+#define OPAGENUM(N) ((N) & SPLITMASK)
+#define OADDR_OF(S,O) ((u_int32_t)((u_int32_t)(S) << SPLITSHIFT) + (O))
+
+#define BUCKET_TO_PAGE(B) \
+ (B) + hashp->HDRPAGES + ((B) ? hashp->SPARES[__hash_log2((B)+1)-1] : 0)
+#define OADDR_TO_PAGE(B) \
+ BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B));
+
+/*
+ * page.h contains a detailed description of the page format.
+ *
+ * Normally, keys and data are accessed from offset tables in the top of
+ * each page which point to the beginning of the key and data. There are
+ * four flag values which may be stored in these offset tables which indicate
+ * the following:
+ *
+ *
+ * OVFLPAGE Rather than a key data pair, this pair contains
+ * the address of an overflow page. The format of
+ * the pair is:
+ * OVERFLOW_PAGE_NUMBER OVFLPAGE
+ *
+ * PARTIAL_KEY This must be the first key/data pair on a page
+ * and implies that page contains only a partial key.
+ * That is, the key is too big to fit on a single page
+ * so it starts on this page and continues on the next.
+ * The format of the page is:
+ * KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE
+ *
+ * KEY_OFF -- offset of the beginning of the key
+ * PARTIAL_KEY -- 1
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * FULL_KEY This must be the first key/data pair on the page. It
+ * is used in two cases.
+ *
+ * Case 1:
+ * There is a complete key on the page but no data
+ * (because it wouldn't fit). The next page contains
+ * the data.
+ *
+ * Page format it:
+ * KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
+ *
+ * KEY_OFF -- offset of the beginning of the key
+ * FULL_KEY -- 2
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * Case 2:
+ * This page contains no key, but part of a large
+ * data field, which is continued on the next page.
+ *
+ * Page format it:
+ * DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
+ *
+ * KEY_OFF -- offset of the beginning of the data on
+ * this page
+ * FULL_KEY -- 2
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * FULL_KEY_DATA
+ * This must be the first key/data pair on the page.
+ * There are two cases:
+ *
+ * Case 1:
+ * This page contains a key and the beginning of the
+ * data field, but the data field is continued on the
+ * next page.
+ *
+ * Page format is:
+ * KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF
+ *
+ * KEY_OFF -- offset of the beginning of the key
+ * FULL_KEY_DATA -- 3
+ * OVFL_PAGENO - page number of the next overflow page
+ * DATA_OFF -- offset of the beginning of the data
+ *
+ * Case 2:
+ * This page contains the last page of a big data pair.
+ * There is no key, only the tail end of the data
+ * on this page.
+ *
+ * Page format is:
+ * DATA_OFF FULL_KEY_DATA <OVFL_PAGENO> <OVFLPAGE>
+ *
+ * DATA_OFF -- offset of the beginning of the data on
+ * this page
+ * FULL_KEY_DATA -- 3
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * OVFL_PAGENO and OVFLPAGE are optional (they are
+ * not present if there is no next page).
+ */
+
+#define OVFLPAGE 0
+#define PARTIAL_KEY 1
+#define FULL_KEY 2
+#define FULL_KEY_DATA 3
+#define REAL_KEY 4
+
+/* Short hands for accessing structure */
+#define BSIZE hdr.bsize
+#define BSHIFT hdr.bshift
+#define DSIZE hdr.dsize
+#define SGSIZE hdr.ssize
+#define SSHIFT hdr.sshift
+#define LORDER hdr.lorder
+#define OVFL_POINT hdr.ovfl_point
+#define LAST_FREED hdr.last_freed
+#define MAX_BUCKET hdr.max_bucket
+#define FFACTOR hdr.ffactor
+#define HIGH_MASK hdr.high_mask
+#define LOW_MASK hdr.low_mask
+#define NKEYS hdr.nkeys
+#define HDRPAGES hdr.hdrpages
+#define SPARES hdr.spares
+#define BITMAPS hdr.bitmaps
+#define VERSION hdr.version
+#define MAGIC hdr.magic
+#define NEXT_FREE hdr.next_free
+#define H_CHARKEY hdr.h_charkey
diff --git a/trunk/main/db1-ast/hash/hash_bigkey.c b/trunk/main/db1-ast/hash/hash_bigkey.c
new file mode 100644
index 000000000..daa8c1f17
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_bigkey.c
@@ -0,0 +1,668 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_bigkey.c 8.3 (Berkeley) 5/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hash
+ * DESCRIPTION:
+ * Big key/data handling for the hashing package.
+ *
+ * ROUTINES:
+ * External
+ * __big_keydata
+ * __big_split
+ * __big_insert
+ * __big_return
+ * __big_delete
+ * __find_last_page
+ * Internal
+ * collect_key
+ * collect_data
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static int collect_key __P((HTAB *, BUFHEAD *, int, DBT *, int));
+static int collect_data __P((HTAB *, BUFHEAD *, int, int));
+
+/*
+ * Big_insert
+ *
+ * You need to do an insert and the key/data pair is too big
+ *
+ * Returns:
+ * 0 ==> OK
+ *-1 ==> ERROR
+ */
+extern int
+__big_insert(hashp, bufp, key, val)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ const DBT *key, *val;
+{
+ register u_int16_t *p;
+ int key_size, n, val_size;
+ u_int16_t space, move_bytes, off;
+ char *cp, *key_data, *val_data;
+
+ cp = bufp->page; /* Character pointer of p. */
+ p = (u_int16_t *)cp;
+
+ key_data = (char *)key->data;
+ key_size = key->size;
+ val_data = (char *)val->data;
+ val_size = val->size;
+
+ /* First move the Key */
+ for (space = FREESPACE(p) - BIGOVERHEAD; key_size;
+ space = FREESPACE(p) - BIGOVERHEAD) {
+ move_bytes = MIN(space, key_size);
+ off = OFFSET(p) - move_bytes;
+ memmove(cp + off, key_data, move_bytes);
+ key_size -= move_bytes;
+ key_data += move_bytes;
+ n = p[0];
+ p[++n] = off;
+ p[0] = ++n;
+ FREESPACE(p) = off - PAGE_META(n);
+ OFFSET(p) = off;
+ p[n] = PARTIAL_KEY;
+ bufp = __add_ovflpage(hashp, bufp);
+ if (!bufp)
+ return (-1);
+ n = p[0];
+ if (!key_size) {
+ if (FREESPACE(p)) {
+ move_bytes = MIN(FREESPACE(p), val_size);
+ off = OFFSET(p) - move_bytes;
+ p[n] = off;
+ memmove(cp + off, val_data, move_bytes);
+ val_data += move_bytes;
+ val_size -= move_bytes;
+ p[n - 2] = FULL_KEY_DATA;
+ FREESPACE(p) = FREESPACE(p) - move_bytes;
+ OFFSET(p) = off;
+ } else
+ p[n - 2] = FULL_KEY;
+ }
+ p = (u_int16_t *)bufp->page;
+ cp = bufp->page;
+ bufp->flags |= BUF_MOD;
+ }
+
+ /* Now move the data */
+ for (space = FREESPACE(p) - BIGOVERHEAD; val_size;
+ space = FREESPACE(p) - BIGOVERHEAD) {
+ move_bytes = MIN(space, val_size);
+ /*
+ * Here's the hack to make sure that if the data ends on the
+ * same page as the key ends, FREESPACE is at least one.
+ */
+ if ((int) space == val_size && (size_t) val_size == val->size)
+ move_bytes--;
+ off = OFFSET(p) - move_bytes;
+ memmove(cp + off, val_data, move_bytes);
+ val_size -= move_bytes;
+ val_data += move_bytes;
+ n = p[0];
+ p[++n] = off;
+ p[0] = ++n;
+ FREESPACE(p) = off - PAGE_META(n);
+ OFFSET(p) = off;
+ if (val_size) {
+ p[n] = FULL_KEY;
+ bufp = __add_ovflpage(hashp, bufp);
+ if (!bufp)
+ return (-1);
+ cp = bufp->page;
+ p = (u_int16_t *)cp;
+ } else
+ p[n] = FULL_KEY_DATA;
+ bufp->flags |= BUF_MOD;
+ }
+ return (0);
+}
+
+/*
+ * Called when bufp's page contains a partial key (index should be 1)
+ *
+ * All pages in the big key/data pair except bufp are freed. We cannot
+ * free bufp because the page pointing to it is lost and we can't get rid
+ * of its pointer.
+ *
+ * Returns:
+ * 0 => OK
+ *-1 => ERROR
+ */
+extern int
+__big_delete(hashp, bufp)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+{
+ register BUFHEAD *last_bfp, *rbufp;
+ u_int16_t *bp, pageno;
+ int key_done, n;
+
+ rbufp = bufp;
+ last_bfp = NULL;
+ bp = (u_int16_t *)bufp->page;
+ pageno = 0;
+ key_done = 0;
+
+ while (!key_done || (bp[2] != FULL_KEY_DATA)) {
+ if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA)
+ key_done = 1;
+
+ /*
+ * If there is freespace left on a FULL_KEY_DATA page, then
+ * the data is short and fits entirely on this page, and this
+ * is the last page.
+ */
+ if (bp[2] == FULL_KEY_DATA && FREESPACE(bp))
+ break;
+ pageno = bp[bp[0] - 1];
+ rbufp->flags |= BUF_MOD;
+ rbufp = __get_buf(hashp, pageno, rbufp, 0);
+ if (last_bfp)
+ __free_ovflpage(hashp, last_bfp);
+ last_bfp = rbufp;
+ if (!rbufp)
+ return (-1); /* Error. */
+ bp = (u_int16_t *)rbufp->page;
+ }
+
+ /*
+ * If we get here then rbufp points to the last page of the big
+ * key/data pair. Bufp points to the first one -- it should now be
+ * empty pointing to the next page after this pair. Can't free it
+ * because we don't have the page pointing to it.
+ */
+
+ /* This is information from the last page of the pair. */
+ n = bp[0];
+ pageno = bp[n - 1];
+
+ /* Now, bp is the first page of the pair. */
+ bp = (u_int16_t *)bufp->page;
+ if (n > 2) {
+ /* There is an overflow page. */
+ bp[1] = pageno;
+ bp[2] = OVFLPAGE;
+ bufp->ovfl = rbufp->ovfl;
+ } else
+ /* This is the last page. */
+ bufp->ovfl = NULL;
+ n -= 2;
+ bp[0] = n;
+ FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
+ OFFSET(bp) = hashp->BSIZE - 1;
+
+ bufp->flags |= BUF_MOD;
+ if (rbufp)
+ __free_ovflpage(hashp, rbufp);
+ if (last_bfp && last_bfp != rbufp)
+ __free_ovflpage(hashp, last_bfp);
+
+ hashp->NKEYS--;
+ return (0);
+}
+/*
+ * Returns:
+ * 0 = key not found
+ * -1 = get next overflow page
+ * -2 means key not found and this is big key/data
+ * -3 error
+ */
+extern int
+__find_bigpair(hashp, bufp, ndx, key, size)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int ndx;
+ char *key;
+ int size;
+{
+ register u_int16_t *bp;
+ register char *p;
+ int ksize;
+ u_int16_t bytes;
+ char *kkey;
+
+ bp = (u_int16_t *)bufp->page;
+ p = bufp->page;
+ ksize = size;
+ kkey = key;
+
+ for (bytes = hashp->BSIZE - bp[ndx];
+ bytes <= size && bp[ndx + 1] == PARTIAL_KEY;
+ bytes = hashp->BSIZE - bp[ndx]) {
+ if (memcmp(p + bp[ndx], kkey, bytes))
+ return (-2);
+ kkey += bytes;
+ ksize -= bytes;
+ bufp = __get_buf(hashp, bp[ndx + 2], bufp, 0);
+ if (!bufp)
+ return (-3);
+ p = bufp->page;
+ bp = (u_int16_t *)p;
+ ndx = 1;
+ }
+
+ if (bytes != ksize || memcmp(p + bp[ndx], kkey, bytes)) {
+#ifdef HASH_STATISTICS
+ ++hash_collisions;
+#endif
+ return (-2);
+ } else
+ return (ndx);
+}
+
+/*
+ * Given the buffer pointer of the first overflow page of a big pair,
+ * find the end of the big pair
+ *
+ * This will set bpp to the buffer header of the last page of the big pair.
+ * It will return the pageno of the overflow page following the last page
+ * of the pair; 0 if there isn't any (i.e. big pair is the last key in the
+ * bucket)
+ */
+extern u_int16_t
+__find_last_page(hashp, bpp)
+ HTAB *hashp;
+ BUFHEAD **bpp;
+{
+ BUFHEAD *bufp;
+ u_int16_t *bp, pageno;
+ int n;
+
+ bufp = *bpp;
+ bp = (u_int16_t *)bufp->page;
+ for (;;) {
+ n = bp[0];
+
+ /*
+ * This is the last page if: the tag is FULL_KEY_DATA and
+ * either only 2 entries OVFLPAGE marker is explicit there
+ * is freespace on the page.
+ */
+ if (bp[2] == FULL_KEY_DATA &&
+ ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp))))
+ break;
+
+ pageno = bp[n - 1];
+ bufp = __get_buf(hashp, pageno, bufp, 0);
+ if (!bufp)
+ return (0); /* Need to indicate an error! */
+ bp = (u_int16_t *)bufp->page;
+ }
+
+ *bpp = bufp;
+ if (bp[0] > 2)
+ return (bp[3]);
+ else
+ return (0);
+}
+
+/*
+ * Return the data for the key/data pair that begins on this page at this
+ * index (index should always be 1).
+ */
+extern int
+__big_return(hashp, bufp, ndx, val, set_current)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int ndx;
+ DBT *val;
+ int set_current;
+{
+ BUFHEAD *save_p;
+ u_int16_t *bp, len, off, save_addr;
+ char *tp;
+
+ bp = (u_int16_t *)bufp->page;
+ while (bp[ndx + 1] == PARTIAL_KEY) {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ ndx = 1;
+ }
+
+ if (bp[ndx + 1] == FULL_KEY) {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ save_p = bufp;
+ save_addr = save_p->addr;
+ off = bp[1];
+ len = 0;
+ } else
+ if (!FREESPACE(bp)) {
+ /*
+ * This is a hack. We can't distinguish between
+ * FULL_KEY_DATA that contains complete data or
+ * incomplete data, so we require that if the data
+ * is complete, there is at least 1 byte of free
+ * space left.
+ */
+ off = bp[bp[0]];
+ len = bp[1] - off;
+ save_p = bufp;
+ save_addr = bufp->addr;
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ } else {
+ /* The data is all on one page. */
+ tp = (char *)bp;
+ off = bp[bp[0]];
+ val->data = (u_char *)tp + off;
+ val->size = bp[1] - off;
+ if (set_current) {
+ if (bp[0] == 2) { /* No more buckets in
+ * chain */
+ hashp->cpage = NULL;
+ hashp->cbucket++;
+ hashp->cndx = 1;
+ } else {
+ hashp->cpage = __get_buf(hashp,
+ bp[bp[0] - 1], bufp, 0);
+ if (!hashp->cpage)
+ return (-1);
+ hashp->cndx = 1;
+ if (!((u_int16_t *)
+ hashp->cpage->page)[0]) {
+ hashp->cbucket++;
+ hashp->cpage = NULL;
+ }
+ }
+ }
+ return (0);
+ }
+
+ val->size = collect_data(hashp, bufp, (int)len, set_current);
+ if (val->size == (size_t) -1)
+ return (-1);
+ if (save_p->addr != save_addr) {
+ /* We are pretty short on buffers. */
+ errno = EINVAL; /* OUT OF BUFFERS */
+ return (-1);
+ }
+ memmove(hashp->tmp_buf, (save_p->page) + off, len);
+ val->data = (u_char *)hashp->tmp_buf;
+ return (0);
+}
+/*
+ * Count how big the total datasize is by recursing through the pages. Then
+ * allocate a buffer and copy the data as you recurse up.
+ */
+static int
+collect_data(hashp, bufp, len, set)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int len, set;
+{
+ register u_int16_t *bp;
+ register char *p;
+ BUFHEAD *xbp;
+ u_int16_t save_addr;
+ int mylen, totlen;
+
+ p = bufp->page;
+ bp = (u_int16_t *)p;
+ mylen = hashp->BSIZE - bp[1];
+ save_addr = bufp->addr;
+
+ if (bp[2] == FULL_KEY_DATA) { /* End of Data */
+ totlen = len + mylen;
+ if (hashp->tmp_buf)
+ free(hashp->tmp_buf);
+ if ((hashp->tmp_buf = (char *)malloc(totlen)) == NULL)
+ return (-1);
+ if (set) {
+ hashp->cndx = 1;
+ if (bp[0] == 2) { /* No more buckets in chain */
+ hashp->cpage = NULL;
+ hashp->cbucket++;
+ } else {
+ hashp->cpage =
+ __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!hashp->cpage)
+ return (-1);
+ else if (!((u_int16_t *)hashp->cpage->page)[0]) {
+ hashp->cbucket++;
+ hashp->cpage = NULL;
+ }
+ }
+ }
+ } else {
+ xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!xbp || ((totlen =
+ collect_data(hashp, xbp, len + mylen, set)) < 1))
+ return (-1);
+ }
+ if (bufp->addr != save_addr) {
+ errno = EINVAL; /* Out of buffers. */
+ return (-1);
+ }
+ memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], mylen);
+ return (totlen);
+}
+
+/*
+ * Fill in the key and data for this big pair.
+ */
+extern int
+__big_keydata(hashp, bufp, key, val, set)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ DBT *key, *val;
+ int set;
+{
+ key->size = collect_key(hashp, bufp, 0, val, set);
+ if (key->size == (size_t) -1)
+ return (-1);
+ key->data = (u_char *)hashp->tmp_key;
+ return (0);
+}
+
+/*
+ * Count how big the total key size is by recursing through the pages. Then
+ * collect the data, allocate a buffer and copy the key as you recurse up.
+ */
+static int
+collect_key(hashp, bufp, len, val, set)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int len;
+ DBT *val;
+ int set;
+{
+ BUFHEAD *xbp;
+ char *p;
+ int mylen, totlen;
+ u_int16_t *bp, save_addr;
+
+ p = bufp->page;
+ bp = (u_int16_t *)p;
+ mylen = hashp->BSIZE - bp[1];
+
+ save_addr = bufp->addr;
+ totlen = len + mylen;
+ if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) { /* End of Key. */
+ if (hashp->tmp_key != NULL)
+ free(hashp->tmp_key);
+ if ((hashp->tmp_key = (char *)malloc(totlen)) == NULL)
+ return (-1);
+ if (__big_return(hashp, bufp, 1, val, set))
+ return (-1);
+ } else {
+ xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!xbp || ((totlen =
+ collect_key(hashp, xbp, totlen, val, set)) < 1))
+ return (-1);
+ }
+ if (bufp->addr != save_addr) {
+ errno = EINVAL; /* MIS -- OUT OF BUFFERS */
+ return (-1);
+ }
+ memmove(&hashp->tmp_key[len], (bufp->page) + bp[1], mylen);
+ return (totlen);
+}
+
+/*
+ * Returns:
+ * 0 => OK
+ * -1 => error
+ */
+extern int
+__big_split(hashp, op, np, big_keyp, addr, obucket, ret)
+ HTAB *hashp;
+ BUFHEAD *op; /* Pointer to where to put keys that go in old bucket */
+ BUFHEAD *np; /* Pointer to new bucket page */
+ /* Pointer to first page containing the big key/data */
+ BUFHEAD *big_keyp;
+ int addr; /* Address of big_keyp */
+ u_int32_t obucket;/* Old Bucket */
+ SPLIT_RETURN *ret;
+{
+ register BUFHEAD *tmpp;
+ register u_int16_t *tp;
+ BUFHEAD *bp;
+ DBT key, val;
+ u_int32_t change;
+ u_int16_t free_space, n, off;
+
+ bp = big_keyp;
+
+ /* Now figure out where the big key/data goes */
+ if (__big_keydata(hashp, big_keyp, &key, &val, 0))
+ return (-1);
+ change = (__call_hash(hashp, key.data, key.size) != obucket);
+
+ if ((ret->next_addr = __find_last_page(hashp, &big_keyp))) {
+ if (!(ret->nextp =
+ __get_buf(hashp, ret->next_addr, big_keyp, 0)))
+ return (-1);;
+ } else
+ ret->nextp = NULL;
+
+ /* Now make one of np/op point to the big key/data pair */
+#ifdef DEBUG
+ assert(np->ovfl == NULL);
+#endif
+ if (change)
+ tmpp = np;
+ else
+ tmpp = op;
+
+ tmpp->flags |= BUF_MOD;
+#ifdef DEBUG1
+ (void)fprintf(stderr,
+ "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
+ (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0));
+#endif
+ tmpp->ovfl = bp; /* one of op/np point to big_keyp */
+ tp = (u_int16_t *)tmpp->page;
+#ifdef DEBUG
+ assert(FREESPACE(tp) >= OVFLSIZE);
+#endif
+ n = tp[0];
+ off = OFFSET(tp);
+ free_space = FREESPACE(tp);
+ tp[++n] = (u_int16_t)addr;
+ tp[++n] = OVFLPAGE;
+ tp[0] = n;
+ OFFSET(tp) = off;
+ FREESPACE(tp) = free_space - OVFLSIZE;
+
+ /*
+ * Finally, set the new and old return values. BIG_KEYP contains a
+ * pointer to the last page of the big key_data pair. Make sure that
+ * big_keyp has no following page (2 elements) or create an empty
+ * following page.
+ */
+
+ ret->newp = np;
+ ret->oldp = op;
+
+ tp = (u_int16_t *)big_keyp->page;
+ big_keyp->flags |= BUF_MOD;
+ if (tp[0] > 2) {
+ /*
+ * There may be either one or two offsets on this page. If
+ * there is one, then the overflow page is linked on normally
+ * and tp[4] is OVFLPAGE. If there are two, tp[4] contains
+ * the second offset and needs to get stuffed in after the
+ * next overflow page is added.
+ */
+ n = tp[4];
+ free_space = FREESPACE(tp);
+ off = OFFSET(tp);
+ tp[0] -= 2;
+ FREESPACE(tp) = free_space + OVFLSIZE;
+ OFFSET(tp) = off;
+ tmpp = __add_ovflpage(hashp, big_keyp);
+ if (!tmpp)
+ return (-1);
+ tp[4] = n;
+ } else
+ tmpp = big_keyp;
+
+ if (change)
+ ret->newp = tmpp;
+ else
+ ret->oldp = tmpp;
+ return (0);
+}
diff --git a/trunk/main/db1-ast/hash/hash_buf.c b/trunk/main/db1-ast/hash/hash_buf.c
new file mode 100644
index 000000000..063dd8229
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_buf.c
@@ -0,0 +1,355 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_buf.c 8.5 (Berkeley) 7/15/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hash
+ *
+ * DESCRIPTION:
+ * Contains buffer management
+ *
+ * ROUTINES:
+ * External
+ * __buf_init
+ * __get_buf
+ * __buf_free
+ * __reclaim_buf
+ * Internal
+ * newbuf
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static BUFHEAD *newbuf __P((HTAB *, u_int32_t, BUFHEAD *));
+
+/* Unlink B from its place in the lru */
+#define BUF_REMOVE(B) { \
+ (B)->prev->next = (B)->next; \
+ (B)->next->prev = (B)->prev; \
+}
+
+/* Insert B after P */
+#define BUF_INSERT(B, P) { \
+ (B)->next = (P)->next; \
+ (B)->prev = (P); \
+ (P)->next = (B); \
+ (B)->next->prev = (B); \
+}
+
+#define MRU hashp->bufhead.next
+#define LRU hashp->bufhead.prev
+
+#define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead)
+#define LRU_INSERT(B) BUF_INSERT((B), LRU)
+
+/*
+ * We are looking for a buffer with address "addr". If prev_bp is NULL, then
+ * address is a bucket index. If prev_bp is not NULL, then it points to the
+ * page previous to an overflow page that we are trying to find.
+ *
+ * CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer
+ * be valid. Therefore, you must always verify that its address matches the
+ * address you are seeking.
+ */
+extern BUFHEAD *
+__get_buf(hashp, addr, prev_bp, newpage)
+ HTAB *hashp;
+ u_int32_t addr;
+ BUFHEAD *prev_bp;
+ int newpage; /* If prev_bp set, indicates a new overflow page. */
+{
+ register BUFHEAD *bp;
+ register u_int32_t is_disk_mask;
+ register int is_disk, segment_ndx = 0;
+ SEGMENT segp = 0;
+
+ is_disk = 0;
+ is_disk_mask = 0;
+ if (prev_bp) {
+ bp = prev_bp->ovfl;
+ if (!bp || (bp->addr != addr))
+ bp = NULL;
+ if (!newpage)
+ is_disk = BUF_DISK;
+ } else {
+ /* Grab buffer out of directory */
+ segment_ndx = addr & (hashp->SGSIZE - 1);
+
+ /* valid segment ensured by __call_hash() */
+ segp = hashp->dir[addr >> hashp->SSHIFT];
+#ifdef DEBUG
+ assert(segp != NULL);
+#endif
+ bp = PTROF(segp[segment_ndx]);
+ is_disk_mask = ISDISK(segp[segment_ndx]);
+ is_disk = is_disk_mask || !hashp->new_file;
+ }
+
+ if (!bp) {
+ bp = newbuf(hashp, addr, prev_bp);
+ if (!bp ||
+ __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
+ return (NULL);
+ if (!prev_bp)
+ segp[segment_ndx] =
+ (BUFHEAD *)((ptrdiff_t)bp | is_disk_mask);
+ } else {
+ BUF_REMOVE(bp);
+ MRU_INSERT(bp);
+ }
+ return (bp);
+}
+
+/*
+ * We need a buffer for this page. Either allocate one, or evict a resident
+ * one (if we have as many buffers as we're allowed) and put this one in.
+ *
+ * If newbuf finds an error (returning NULL), it also sets errno.
+ */
+static BUFHEAD *
+newbuf(hashp, addr, prev_bp)
+ HTAB *hashp;
+ u_int32_t addr;
+ BUFHEAD *prev_bp;
+{
+ register BUFHEAD *bp; /* The buffer we're going to use */
+ register BUFHEAD *xbp; /* Temp pointer */
+ register BUFHEAD *next_xbp;
+ SEGMENT segp;
+ int segment_ndx;
+ u_int16_t oaddr, *shortp;
+
+ oaddr = 0;
+ bp = LRU;
+ /*
+ * If LRU buffer is pinned, the buffer pool is too small. We need to
+ * allocate more buffers.
+ */
+ if (hashp->nbufs || (bp->flags & BUF_PIN)) {
+ /* Allocate a new one */
+ if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL)
+ return (NULL);
+#ifdef PURIFY
+ memset(bp, 0xff, sizeof(BUFHEAD));
+#endif
+ if ((bp->page = (char *)malloc(hashp->BSIZE)) == NULL) {
+ free(bp);
+ return (NULL);
+ }
+#ifdef PURIFY
+ memset(bp->page, 0xff, hashp->BSIZE);
+#endif
+ if (hashp->nbufs)
+ hashp->nbufs--;
+ } else {
+ /* Kick someone out */
+ BUF_REMOVE(bp);
+ /*
+ * If this is an overflow page with addr 0, it's already been
+ * flushed back in an overflow chain and initialized.
+ */
+ if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
+ /*
+ * Set oaddr before __put_page so that you get it
+ * before bytes are swapped.
+ */
+ shortp = (u_int16_t *)bp->page;
+ if (shortp[0])
+ oaddr = shortp[shortp[0] - 1];
+ if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
+ bp->addr, (int)IS_BUCKET(bp->flags), 0))
+ return (NULL);
+ /*
+ * Update the pointer to this page (i.e. invalidate it).
+ *
+ * If this is a new file (i.e. we created it at open
+ * time), make sure that we mark pages which have been
+ * written to disk so we retrieve them from disk later,
+ * rather than allocating new pages.
+ */
+ if (IS_BUCKET(bp->flags)) {
+ segment_ndx = bp->addr & (hashp->SGSIZE - 1);
+ segp = hashp->dir[bp->addr >> hashp->SSHIFT];
+#ifdef DEBUG
+ assert(segp != NULL);
+#endif
+
+ if (hashp->new_file &&
+ ((bp->flags & BUF_MOD) ||
+ ISDISK(segp[segment_ndx])))
+ segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
+ else
+ segp[segment_ndx] = NULL;
+ }
+ /*
+ * Since overflow pages can only be access by means of
+ * their bucket, free overflow pages associated with
+ * this bucket.
+ */
+ for (xbp = bp; xbp->ovfl;) {
+ next_xbp = xbp->ovfl;
+ xbp->ovfl = 0;
+ xbp = next_xbp;
+
+ /* Check that ovfl pointer is up date. */
+ if (IS_BUCKET(xbp->flags) ||
+ (oaddr != xbp->addr))
+ break;
+
+ shortp = (u_int16_t *)xbp->page;
+ if (shortp[0])
+ /* set before __put_page */
+ oaddr = shortp[shortp[0] - 1];
+ if ((xbp->flags & BUF_MOD) && __put_page(hashp,
+ xbp->page, xbp->addr, 0, 0))
+ return (NULL);
+ xbp->addr = 0;
+ xbp->flags = 0;
+ BUF_REMOVE(xbp);
+ LRU_INSERT(xbp);
+ }
+ }
+ }
+
+ /* Now assign this buffer */
+ bp->addr = addr;
+#ifdef DEBUG1
+ (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
+ bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
+#endif
+ bp->ovfl = NULL;
+ if (prev_bp) {
+ /*
+ * If prev_bp is set, this is an overflow page, hook it in to
+ * the buffer overflow links.
+ */
+#ifdef DEBUG1
+ (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
+ prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
+ (bp ? bp->addr : 0));
+#endif
+ prev_bp->ovfl = bp;
+ bp->flags = 0;
+ } else
+ bp->flags = BUF_BUCKET;
+ MRU_INSERT(bp);
+ return (bp);
+}
+
+extern void
+__buf_init(hashp, nbytes)
+ HTAB *hashp;
+ int nbytes;
+{
+ BUFHEAD *bfp;
+ int npages;
+
+ bfp = &(hashp->bufhead);
+ npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
+ npages = MAX(npages, MIN_BUFFERS);
+
+ hashp->nbufs = npages;
+ bfp->next = bfp;
+ bfp->prev = bfp;
+ /*
+ * This space is calloc'd so these are already null.
+ *
+ * bfp->ovfl = NULL;
+ * bfp->flags = 0;
+ * bfp->page = NULL;
+ * bfp->addr = 0;
+ */
+}
+
+extern int
+__buf_free(hashp, do_free, to_disk)
+ HTAB *hashp;
+ int do_free, to_disk;
+{
+ BUFHEAD *bp;
+
+ /* Need to make sure that buffer manager has been initialized */
+ if (!LRU)
+ return (0);
+ for (bp = LRU; bp != &hashp->bufhead;) {
+ /* Check that the buffer is valid */
+ if (bp->addr || IS_BUCKET(bp->flags)) {
+ if (to_disk && (bp->flags & BUF_MOD) &&
+ __put_page(hashp, bp->page,
+ bp->addr, IS_BUCKET(bp->flags), 0))
+ return (-1);
+ }
+ /* Check if we are freeing stuff */
+ if (do_free) {
+ if (bp->page)
+ free(bp->page);
+ BUF_REMOVE(bp);
+ free(bp);
+ bp = LRU;
+ } else
+ bp = bp->prev;
+ }
+ return (0);
+}
+
+extern void
+__reclaim_buf(hashp, bp)
+ HTAB *hashp;
+ BUFHEAD *bp;
+{
+ bp->ovfl = 0;
+ bp->addr = 0;
+ bp->flags = 0;
+ BUF_REMOVE(bp);
+ LRU_INSERT(bp);
+}
diff --git a/trunk/main/db1-ast/hash/hash_func.c b/trunk/main/db1-ast/hash/hash_func.c
new file mode 100644
index 000000000..4d7907b57
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_func.c
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_func.c 8.2 (Berkeley) 2/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+/* only one of these can be defined */
+/* #define HASH1_EJB 1 */
+/* #define HASH2_PHONG 1 */
+/* #define HASH3_SDBM 1 */
+#define HASH4_TOREK 1
+
+static u_int32_t hashfunc __P((const void *, size_t));
+
+/* Global default hash function */
+u_int32_t (*__default_hash) __P((const void *, size_t)) = hashfunc;
+
+/*
+ * HASH FUNCTIONS
+ *
+ * Assume that we've already split the bucket to which this key hashes,
+ * calculate that bucket, and check that in fact we did already split it.
+ *
+ * This came from ejb's hsearch.
+ */
+
+#ifdef HASH1_EJB
+
+#define PRIME1 37
+#define PRIME2 1048583
+
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ register size_t len;
+{
+ register const u_char *key;
+ register u_int32_t h;
+
+ /* Convert string to integer */
+ for (key = keyarg, h = 0; len--;)
+ h = h * PRIME1 ^ (*key++ - ' ');
+ h %= PRIME2;
+ return (h);
+}
+
+#endif
+
+#ifdef HASH2_PHONG
+/*
+ * Phong's linear congruential hash
+ */
+#define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c))
+
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ size_t len;
+{
+ register const u_char *e, *key;
+ register u_int32_t h;
+ register u_char c;
+
+ key = keyarg;
+ e = key + len;
+ for (h = 0; key != e;) {
+ c = *key++;
+ if (!c && key > e)
+ break;
+ dcharhash(h, c);
+ }
+ return (h);
+}
+#endif
+
+#ifdef HASH3_SDBM
+/*
+ * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte
+ * units. On the first time through the loop we get the "leftover bytes"
+ * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If
+ * this routine is heavily used enough, it's worth the ugly coding.
+ *
+ * OZ's original sdbm hash
+ */
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ register size_t len;
+{
+ register const u_char *key;
+ register size_t loop;
+ register u_int32_t h;
+
+#define HASHC h = *key++ + 65599 * h
+
+ h = 0;
+ key = keyarg;
+ if (len > 0) {
+ loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do {
+ HASHC;
+ /* FALLTHROUGH */
+ case 7:
+ HASHC;
+ /* FALLTHROUGH */
+ case 6:
+ HASHC;
+ /* FALLTHROUGH */
+ case 5:
+ HASHC;
+ /* FALLTHROUGH */
+ case 4:
+ HASHC;
+ /* FALLTHROUGH */
+ case 3:
+ HASHC;
+ /* FALLTHROUGH */
+ case 2:
+ HASHC;
+ /* FALLTHROUGH */
+ case 1:
+ HASHC;
+ } while (--loop);
+ }
+ }
+ return (h);
+}
+#endif
+
+#ifdef HASH4_TOREK
+/* Hash function from Chris Torek. */
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ register size_t len;
+{
+ register const u_char *key;
+ register size_t loop;
+ register u_int32_t h;
+
+#define HASH4a h = (h << 5) - h + *key++;
+#define HASH4b h = (h << 5) + h + *key++;
+#define HASH4 HASH4b
+
+ h = 0;
+ key = keyarg;
+ if (len > 0) {
+ loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do {
+ HASH4;
+ /* FALLTHROUGH */
+ case 7:
+ HASH4;
+ /* FALLTHROUGH */
+ case 6:
+ HASH4;
+ /* FALLTHROUGH */
+ case 5:
+ HASH4;
+ /* FALLTHROUGH */
+ case 4:
+ HASH4;
+ /* FALLTHROUGH */
+ case 3:
+ HASH4;
+ /* FALLTHROUGH */
+ case 2:
+ HASH4;
+ /* FALLTHROUGH */
+ case 1:
+ HASH4;
+ } while (--loop);
+ }
+ }
+ return (h);
+}
+#endif
diff --git a/trunk/main/db1-ast/hash/hash_log2.c b/trunk/main/db1-ast/hash/hash_log2.c
new file mode 100644
index 000000000..b86655d47
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_log2.c
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_log2.c 8.2 (Berkeley) 5/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include "../include/db.h"
+
+u_int32_t __hash_log2 __P((u_int32_t));
+
+u_int32_t
+__hash_log2(num)
+ u_int32_t num;
+{
+ register u_int32_t i, limit;
+
+ limit = 1;
+ for (i = 0; limit < num; limit = limit << 1, i++);
+ return (i);
+}
diff --git a/trunk/main/db1-ast/hash/hash_page.c b/trunk/main/db1-ast/hash/hash_page.c
new file mode 100644
index 000000000..737b97d32
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_page.c
@@ -0,0 +1,944 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_page.c 8.7 (Berkeley) 8/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hashing
+ *
+ * DESCRIPTION:
+ * Page manipulation for hashing package.
+ *
+ * ROUTINES:
+ *
+ * External
+ * __get_page
+ * __add_ovflpage
+ * Internal
+ * overflow_page
+ * open_temp
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static u_int32_t *fetch_bitmap __P((HTAB *, int));
+static u_int32_t first_free __P((u_int32_t));
+static int open_temp __P((HTAB *));
+static u_int16_t overflow_page __P((HTAB *));
+static void putpair __P((char *, const DBT *, const DBT *));
+static void squeeze_key __P((u_int16_t *, const DBT *, const DBT *));
+static int ugly_split
+ __P((HTAB *, u_int32_t, BUFHEAD *, BUFHEAD *, int, int));
+
+#define PAGE_INIT(P) { \
+ ((u_int16_t *)(P))[0] = 0; \
+ ((u_int16_t *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_int16_t); \
+ ((u_int16_t *)(P))[2] = hashp->BSIZE; \
+}
+
+/*
+ * This is called AFTER we have verified that there is room on the page for
+ * the pair (PAIRFITS has returned true) so we go right ahead and start moving
+ * stuff on.
+ */
+static void
+putpair(p, key, val)
+ char *p;
+ const DBT *key, *val;
+{
+ register u_int16_t *bp, n, off;
+
+ bp = (u_int16_t *)p;
+
+ /* Enter the key first. */
+ n = bp[0];
+
+ off = OFFSET(bp) - key->size;
+ memmove(p + off, key->data, key->size);
+ bp[++n] = off;
+
+ /* Now the data. */
+ off -= val->size;
+ memmove(p + off, val->data, val->size);
+ bp[++n] = off;
+
+ /* Adjust page info. */
+ bp[0] = n;
+ bp[n + 1] = off - ((n + 3) * sizeof(u_int16_t));
+ bp[n + 2] = off;
+}
+
+/*
+ * Returns:
+ * 0 OK
+ * -1 error
+ */
+extern int
+__delpair(hashp, bufp, ndx)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ register int ndx;
+{
+ register u_int16_t *bp, newoff;
+ register int n;
+ u_int16_t pairlen;
+
+ bp = (u_int16_t *)bufp->page;
+ n = bp[0];
+
+ if (bp[ndx + 1] < REAL_KEY)
+ return (__big_delete(hashp, bufp));
+ if (ndx != 1)
+ newoff = bp[ndx - 1];
+ else
+ newoff = hashp->BSIZE;
+ pairlen = newoff - bp[ndx + 1];
+
+ if (ndx != (n - 1)) {
+ /* Hard Case -- need to shuffle keys */
+ register int i;
+ register char *src = bufp->page + (int)OFFSET(bp);
+ register char *dst = src + (int)pairlen;
+ memmove(dst, src, bp[ndx + 1] - OFFSET(bp));
+
+ /* Now adjust the pointers */
+ for (i = ndx + 2; i <= n; i += 2) {
+ if (bp[i + 1] == OVFLPAGE) {
+ bp[i - 2] = bp[i];
+ bp[i - 1] = bp[i + 1];
+ } else {
+ bp[i - 2] = bp[i] + pairlen;
+ bp[i - 1] = bp[i + 1] + pairlen;
+ }
+ }
+ }
+ /* Finally adjust the page data */
+ bp[n] = OFFSET(bp) + pairlen;
+ bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_int16_t);
+ bp[0] = n - 2;
+ hashp->NKEYS--;
+
+ bufp->flags |= BUF_MOD;
+ return (0);
+}
+/*
+ * Returns:
+ * 0 ==> OK
+ * -1 ==> Error
+ */
+extern int
+__split_page(hashp, obucket, nbucket)
+ HTAB *hashp;
+ u_int32_t obucket, nbucket;
+{
+ register BUFHEAD *new_bufp, *old_bufp;
+ register u_int16_t *ino;
+ register char *np;
+ DBT key, val;
+ int n, ndx, retval;
+ u_int16_t copyto, diff, off, moved;
+ char *op;
+
+ copyto = (u_int16_t)hashp->BSIZE;
+ off = (u_int16_t)hashp->BSIZE;
+ old_bufp = __get_buf(hashp, obucket, NULL, 0);
+ if (old_bufp == NULL)
+ return (-1);
+ new_bufp = __get_buf(hashp, nbucket, NULL, 0);
+ if (new_bufp == NULL)
+ return (-1);
+
+ old_bufp->flags |= (BUF_MOD | BUF_PIN);
+ new_bufp->flags |= (BUF_MOD | BUF_PIN);
+
+ ino = (u_int16_t *)(op = old_bufp->page);
+ np = new_bufp->page;
+
+ moved = 0;
+
+ for (n = 1, ndx = 1; n < ino[0]; n += 2) {
+ if (ino[n + 1] < REAL_KEY) {
+ retval = ugly_split(hashp, obucket, old_bufp, new_bufp,
+ (int)copyto, (int)moved);
+ old_bufp->flags &= ~BUF_PIN;
+ new_bufp->flags &= ~BUF_PIN;
+ return (retval);
+
+ }
+ key.data = (u_char *)op + ino[n];
+ key.size = off - ino[n];
+
+ if (__call_hash(hashp, key.data, key.size) == obucket) {
+ /* Don't switch page */
+ diff = copyto - off;
+ if (diff) {
+ copyto = ino[n + 1] + diff;
+ memmove(op + copyto, op + ino[n + 1],
+ off - ino[n + 1]);
+ ino[ndx] = copyto + ino[n] - ino[n + 1];
+ ino[ndx + 1] = copyto;
+ } else
+ copyto = ino[n + 1];
+ ndx += 2;
+ } else {
+ /* Switch page */
+ val.data = (u_char *)op + ino[n + 1];
+ val.size = ino[n] - ino[n + 1];
+ putpair(np, &key, &val);
+ moved += 2;
+ }
+
+ off = ino[n + 1];
+ }
+
+ /* Now clean up the page */
+ ino[0] -= moved;
+ FREESPACE(ino) = copyto - sizeof(u_int16_t) * (ino[0] + 3);
+ OFFSET(ino) = copyto;
+
+#ifdef DEBUG3
+ (void)fprintf(stderr, "split %d/%d\n",
+ ((u_int16_t *)np)[0] / 2,
+ ((u_int16_t *)op)[0] / 2);
+#endif
+ /* unpin both pages */
+ old_bufp->flags &= ~BUF_PIN;
+ new_bufp->flags &= ~BUF_PIN;
+ return (0);
+}
+
+/*
+ * Called when we encounter an overflow or big key/data page during split
+ * handling. This is special cased since we have to begin checking whether
+ * the key/data pairs fit on their respective pages and because we may need
+ * overflow pages for both the old and new pages.
+ *
+ * The first page might be a page with regular key/data pairs in which case
+ * we have a regular overflow condition and just need to go on to the next
+ * page or it might be a big key/data pair in which case we need to fix the
+ * big key/data pair.
+ *
+ * Returns:
+ * 0 ==> success
+ * -1 ==> failure
+ */
+static int
+ugly_split(hashp, obucket, old_bufp, new_bufp, copyto, moved)
+ HTAB *hashp;
+ u_int32_t obucket; /* Same as __split_page. */
+ BUFHEAD *old_bufp, *new_bufp;
+ int copyto; /* First byte on page which contains key/data values. */
+ int moved; /* Number of pairs moved to new page. */
+{
+ register BUFHEAD *bufp; /* Buffer header for ino */
+ register u_int16_t *ino; /* Page keys come off of */
+ register u_int16_t *np; /* New page */
+ register u_int16_t *op; /* Page keys go on to if they aren't moving */
+
+ BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */
+ DBT key, val;
+ SPLIT_RETURN ret;
+ u_int16_t n, off, ov_addr, scopyto;
+ char *cino; /* Character value of ino */
+
+ bufp = old_bufp;
+ ino = (u_int16_t *)old_bufp->page;
+ np = (u_int16_t *)new_bufp->page;
+ op = (u_int16_t *)old_bufp->page;
+ last_bfp = NULL;
+ scopyto = (u_int16_t)copyto; /* ANSI */
+
+ n = ino[0] - 1;
+ while (n < ino[0]) {
+ if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) {
+ if (__big_split(hashp, old_bufp,
+ new_bufp, bufp, bufp->addr, obucket, &ret))
+ return (-1);
+ old_bufp = ret.oldp;
+ if (!old_bufp)
+ return (-1);
+ op = (u_int16_t *)old_bufp->page;
+ new_bufp = ret.newp;
+ if (!new_bufp)
+ return (-1);
+ np = (u_int16_t *)new_bufp->page;
+ bufp = ret.nextp;
+ if (!bufp)
+ return (0);
+ cino = (char *)bufp->page;
+ ino = (u_int16_t *)cino;
+ last_bfp = ret.nextp;
+ } else if (ino[n + 1] == OVFLPAGE) {
+ ov_addr = ino[n];
+ /*
+ * Fix up the old page -- the extra 2 are the fields
+ * which contained the overflow information.
+ */
+ ino[0] -= (moved + 2);
+ FREESPACE(ino) =
+ scopyto - sizeof(u_int16_t) * (ino[0] + 3);
+ OFFSET(ino) = scopyto;
+
+ bufp = __get_buf(hashp, ov_addr, bufp, 0);
+ if (!bufp)
+ return (-1);
+
+ ino = (u_int16_t *)bufp->page;
+ n = 1;
+ scopyto = hashp->BSIZE;
+ moved = 0;
+
+ if (last_bfp)
+ __free_ovflpage(hashp, last_bfp);
+ last_bfp = bufp;
+ }
+ /* Move regular sized pairs of there are any */
+ off = hashp->BSIZE;
+ for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) {
+ cino = (char *)ino;
+ key.data = (u_char *)cino + ino[n];
+ key.size = off - ino[n];
+ val.data = (u_char *)cino + ino[n + 1];
+ val.size = ino[n] - ino[n + 1];
+ off = ino[n + 1];
+
+ if (__call_hash(hashp, key.data, key.size) == obucket) {
+ /* Keep on old page */
+ if (PAIRFITS(op, (&key), (&val)))
+ putpair((char *)op, &key, &val);
+ else {
+ old_bufp =
+ __add_ovflpage(hashp, old_bufp);
+ if (!old_bufp)
+ return (-1);
+ op = (u_int16_t *)old_bufp->page;
+ putpair((char *)op, &key, &val);
+ }
+ old_bufp->flags |= BUF_MOD;
+ } else {
+ /* Move to new page */
+ if (PAIRFITS(np, (&key), (&val)))
+ putpair((char *)np, &key, &val);
+ else {
+ new_bufp =
+ __add_ovflpage(hashp, new_bufp);
+ if (!new_bufp)
+ return (-1);
+ np = (u_int16_t *)new_bufp->page;
+ putpair((char *)np, &key, &val);
+ }
+ new_bufp->flags |= BUF_MOD;
+ }
+ }
+ }
+ if (last_bfp)
+ __free_ovflpage(hashp, last_bfp);
+ return (0);
+}
+
+/*
+ * Add the given pair to the page
+ *
+ * Returns:
+ * 0 ==> OK
+ * 1 ==> failure
+ */
+extern int
+__addel(hashp, bufp, key, val)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ const DBT *key, *val;
+{
+ register u_int16_t *bp, *sop;
+ int do_expand;
+
+ bp = (u_int16_t *)bufp->page;
+ do_expand = 0;
+ while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY))
+ /* Exception case */
+ if (bp[2] == FULL_KEY_DATA && bp[0] == 2)
+ /* This is the last page of a big key/data pair
+ and we need to add another page */
+ break;
+ else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ } else
+ /* Try to squeeze key on this page */
+ if (FREESPACE(bp) > PAIRSIZE(key, val)) {
+ squeeze_key(bp, key, val);
+ return (0);
+ } else {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ }
+
+ if (PAIRFITS(bp, key, val))
+ putpair(bufp->page, key, val);
+ else {
+ do_expand = 1;
+ bufp = __add_ovflpage(hashp, bufp);
+ if (!bufp)
+ return (-1);
+ sop = (u_int16_t *)bufp->page;
+
+ if (PAIRFITS(sop, key, val))
+ putpair((char *)sop, key, val);
+ else
+ if (__big_insert(hashp, bufp, key, val))
+ return (-1);
+ }
+ bufp->flags |= BUF_MOD;
+ /*
+ * If the average number of keys per bucket exceeds the fill factor,
+ * expand the table.
+ */
+ hashp->NKEYS++;
+ if (do_expand ||
+ (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR))
+ return (__expand_table(hashp));
+ return (0);
+}
+
+/*
+ *
+ * Returns:
+ * pointer on success
+ * NULL on error
+ */
+extern BUFHEAD *
+__add_ovflpage(hashp, bufp)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+{
+ register u_int16_t *sp;
+ u_int16_t ndx, ovfl_num;
+#ifdef DEBUG1
+ int tmp1, tmp2;
+#endif
+ sp = (u_int16_t *)bufp->page;
+
+ /* Check if we are dynamically determining the fill factor */
+ if (hashp->FFACTOR == DEF_FFACTOR) {
+ hashp->FFACTOR = sp[0] >> 1;
+ if (hashp->FFACTOR < MIN_FFACTOR)
+ hashp->FFACTOR = MIN_FFACTOR;
+ }
+ bufp->flags |= BUF_MOD;
+ ovfl_num = overflow_page(hashp);
+#ifdef DEBUG1
+ tmp1 = bufp->addr;
+ tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0;
+#endif
+ if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1)))
+ return (NULL);
+ bufp->ovfl->flags |= BUF_MOD;
+#ifdef DEBUG1
+ (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n",
+ tmp1, tmp2, bufp->ovfl->addr);
+#endif
+ ndx = sp[0];
+ /*
+ * Since a pair is allocated on a page only if there's room to add
+ * an overflow page, we know that the OVFL information will fit on
+ * the page.
+ */
+ sp[ndx + 4] = OFFSET(sp);
+ sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE;
+ sp[ndx + 1] = ovfl_num;
+ sp[ndx + 2] = OVFLPAGE;
+ sp[0] = ndx + 2;
+#ifdef HASH_STATISTICS
+ hash_overflows++;
+#endif
+ return (bufp->ovfl);
+}
+
+/*
+ * Returns:
+ * 0 indicates SUCCESS
+ * -1 indicates FAILURE
+ */
+extern int
+__get_page(hashp, p, bucket, is_bucket, is_disk, is_bitmap)
+ HTAB *hashp;
+ char *p;
+ u_int32_t bucket;
+ int is_bucket, is_disk, is_bitmap;
+{
+ register int fd, page, size;
+ int rsize;
+ u_int16_t *bp;
+
+ fd = hashp->fp;
+ size = hashp->BSIZE;
+
+ if ((fd == -1) || !is_disk) {
+ PAGE_INIT(p);
+ return (0);
+ }
+ if (is_bucket)
+ page = BUCKET_TO_PAGE(bucket);
+ else
+ page = OADDR_TO_PAGE(bucket);
+ if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) ||
+ ((rsize = read(fd, p, size)) == -1))
+ return (-1);
+ bp = (u_int16_t *)p;
+ if (!rsize)
+ bp[0] = 0; /* We hit the EOF, so initialize a new page */
+ else
+ if (rsize != size) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ if (!is_bitmap && !bp[0]) {
+ PAGE_INIT(p);
+ } else
+ if (hashp->LORDER != BYTE_ORDER) {
+ register int i, max;
+
+ if (is_bitmap) {
+ max = hashp->BSIZE >> 2; /* divide by 4 */
+ for (i = 0; i < max; i++)
+ M_32_SWAP(((int *)p)[i]);
+ } else {
+ M_16_SWAP(bp[0]);
+ max = bp[0] + 2;
+ for (i = 1; i <= max; i++)
+ M_16_SWAP(bp[i]);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Write page p to disk
+ *
+ * Returns:
+ * 0 ==> OK
+ * -1 ==>failure
+ */
+extern int
+__put_page(hashp, p, bucket, is_bucket, is_bitmap)
+ HTAB *hashp;
+ char *p;
+ u_int32_t bucket;
+ int is_bucket, is_bitmap;
+{
+ register int fd, page, size;
+ int wsize;
+
+ size = hashp->BSIZE;
+ if ((hashp->fp == -1) && open_temp(hashp))
+ return (-1);
+ fd = hashp->fp;
+
+ if (hashp->LORDER != BYTE_ORDER) {
+ register int i;
+ register int max;
+
+ if (is_bitmap) {
+ max = hashp->BSIZE >> 2; /* divide by 4 */
+ for (i = 0; i < max; i++)
+ M_32_SWAP(((int *)p)[i]);
+ } else {
+ max = ((u_int16_t *)p)[0] + 2;
+ for (i = 0; i <= max; i++)
+ M_16_SWAP(((u_int16_t *)p)[i]);
+ }
+ }
+ if (is_bucket)
+ page = BUCKET_TO_PAGE(bucket);
+ else
+ page = OADDR_TO_PAGE(bucket);
+ if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) ||
+ ((wsize = write(fd, p, size)) == -1))
+ /* Errno is set */
+ return (-1);
+ if (wsize != size) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ return (0);
+}
+
+#define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1)
+/*
+ * Initialize a new bitmap page. Bitmap pages are left in memory
+ * once they are read in.
+ */
+extern int
+__ibitmap(hashp, pnum, nbits, ndx)
+ HTAB *hashp;
+ int pnum, nbits, ndx;
+{
+ u_int32_t *ip;
+ int clearbytes, clearints;
+
+ if ((ip = (u_int32_t *)malloc(hashp->BSIZE)) == NULL)
+ return (1);
+ hashp->nmaps++;
+ clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1;
+ clearbytes = clearints << INT_TO_BYTE;
+ (void)memset((char *)ip, 0, clearbytes);
+ (void)memset(((char *)ip) + clearbytes, 0xFF,
+ hashp->BSIZE - clearbytes);
+ ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK);
+ SETBIT(ip, 0);
+ hashp->BITMAPS[ndx] = (u_int16_t)pnum;
+ hashp->mapp[ndx] = ip;
+ return (0);
+}
+
+static u_int32_t
+first_free(map)
+ u_int32_t map;
+{
+ register u_int32_t i, mask;
+
+ mask = 0x1;
+ for (i = 0; i < BITS_PER_MAP; i++) {
+ if (!(mask & map))
+ return (i);
+ mask = mask << 1;
+ }
+ return (i);
+}
+
+static u_int16_t
+overflow_page(hashp)
+ HTAB *hashp;
+{
+ register u_int32_t *freep = 0;
+ register int max_free, offset, splitnum;
+ u_int16_t addr;
+ int bit, first_page, free_bit, free_page, i, in_use_bits, j;
+#ifdef DEBUG2
+ int tmp1, tmp2;
+#endif
+ splitnum = hashp->OVFL_POINT;
+ max_free = hashp->SPARES[splitnum];
+
+ free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT);
+ free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1);
+
+ /* Look through all the free maps to find the first free block */
+ first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT);
+ for ( i = first_page; i <= free_page; i++ ) {
+ if (!(freep = (u_int32_t *)hashp->mapp[i]) &&
+ !(freep = fetch_bitmap(hashp, i)))
+ return (0);
+ if (i == free_page)
+ in_use_bits = free_bit;
+ else
+ in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1;
+
+ if (i == first_page) {
+ bit = hashp->LAST_FREED &
+ ((hashp->BSIZE << BYTE_SHIFT) - 1);
+ j = bit / BITS_PER_MAP;
+ bit = bit & ~(BITS_PER_MAP - 1);
+ } else {
+ bit = 0;
+ j = 0;
+ }
+ for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP)
+ if (freep[j] != ALL_SET)
+ goto found;
+ }
+
+ /* No Free Page Found */
+ hashp->LAST_FREED = hashp->SPARES[splitnum];
+ hashp->SPARES[splitnum]++;
+ offset = hashp->SPARES[splitnum] -
+ (splitnum ? hashp->SPARES[splitnum - 1] : 0);
+
+#define OVMSG "HASH: Out of overflow pages. Increase page size\n"
+ if (offset > SPLITMASK) {
+ if (++splitnum >= NCACHED) {
+ (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
+ return (0);
+ }
+ hashp->OVFL_POINT = splitnum;
+ hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1];
+ hashp->SPARES[splitnum-1]--;
+ offset = 1;
+ }
+
+ /* Check if we need to allocate a new bitmap page */
+ if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) {
+ free_page++;
+ if (free_page >= NCACHED) {
+ (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
+ return (0);
+ }
+ /*
+ * This is tricky. The 1 indicates that you want the new page
+ * allocated with 1 clear bit. Actually, you are going to
+ * allocate 2 pages from this map. The first is going to be
+ * the map page, the second is the overflow page we were
+ * looking for. The init_bitmap routine automatically, sets
+ * the first bit of itself to indicate that the bitmap itself
+ * is in use. We would explicitly set the second bit, but
+ * don't have to if we tell init_bitmap not to leave it clear
+ * in the first place.
+ */
+ if (__ibitmap(hashp,
+ (int)OADDR_OF(splitnum, offset), 1, free_page))
+ return (0);
+ hashp->SPARES[splitnum]++;
+#ifdef DEBUG2
+ free_bit = 2;
+#endif
+ offset++;
+ if (offset > SPLITMASK) {
+ if (++splitnum >= NCACHED) {
+ (void)write(STDERR_FILENO, OVMSG,
+ sizeof(OVMSG) - 1);
+ return (0);
+ }
+ hashp->OVFL_POINT = splitnum;
+ hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1];
+ hashp->SPARES[splitnum-1]--;
+ offset = 0;
+ }
+ } else {
+ /*
+ * Free_bit addresses the last used bit. Bump it to address
+ * the first available bit.
+ */
+ free_bit++;
+ SETBIT(freep, free_bit);
+ }
+
+ /* Calculate address of the new overflow page */
+ addr = OADDR_OF(splitnum, offset);
+#ifdef DEBUG2
+ (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
+ addr, free_bit, free_page);
+#endif
+ return (addr);
+
+found:
+ bit = bit + first_free(freep[j]);
+ SETBIT(freep, bit);
+#ifdef DEBUG2
+ tmp1 = bit;
+ tmp2 = i;
+#endif
+ /*
+ * Bits are addressed starting with 0, but overflow pages are addressed
+ * beginning at 1. Bit is a bit addressnumber, so we need to increment
+ * it to convert it to a page number.
+ */
+ bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT));
+ if (bit >= hashp->LAST_FREED)
+ hashp->LAST_FREED = bit - 1;
+
+ /* Calculate the split number for this page */
+ for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++);
+ offset = (i ? bit - hashp->SPARES[i - 1] : bit);
+ if (offset >= SPLITMASK)
+ return (0); /* Out of overflow pages */
+ addr = OADDR_OF(i, offset);
+#ifdef DEBUG2
+ (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
+ addr, tmp1, tmp2);
+#endif
+
+ /* Allocate and return the overflow page */
+ return (addr);
+}
+
+/*
+ * Mark this overflow page as free.
+ */
+extern void
+__free_ovflpage(hashp, obufp)
+ HTAB *hashp;
+ BUFHEAD *obufp;
+{
+ register u_int16_t addr;
+ u_int32_t *freep;
+ int bit_address, free_page, free_bit;
+ u_int16_t ndx;
+
+ addr = obufp->addr;
+#ifdef DEBUG1
+ (void)fprintf(stderr, "Freeing %d\n", addr);
+#endif
+ ndx = (((u_int16_t)addr) >> SPLITSHIFT);
+ bit_address =
+ (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1;
+ if (bit_address < hashp->LAST_FREED)
+ hashp->LAST_FREED = bit_address;
+ free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT));
+ free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1);
+
+ if (!(freep = hashp->mapp[free_page]))
+ freep = fetch_bitmap(hashp, free_page);
+#ifdef DEBUG
+ /*
+ * This had better never happen. It means we tried to read a bitmap
+ * that has already had overflow pages allocated off it, and we
+ * failed to read it from the file.
+ */
+ if (!freep)
+ assert(0);
+#endif
+ CLRBIT(freep, free_bit);
+#ifdef DEBUG2
+ (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n",
+ obufp->addr, free_bit, free_page);
+#endif
+ __reclaim_buf(hashp, obufp);
+}
+
+/*
+ * Returns:
+ * 0 success
+ * -1 failure
+ */
+static int
+open_temp(hashp)
+ HTAB *hashp;
+{
+ sigset_t set, oset;
+ static char namestr[] = "_hashXXXXXX";
+
+ /* Block signals; make sure file goes away at process exit. */
+ (void)sigfillset(&set);
+ (void)sigprocmask(SIG_BLOCK, &set, &oset);
+ if ((hashp->fp = mkstemp(namestr)) != -1) {
+ (void)unlink(namestr);
+ (void)fcntl(hashp->fp, F_SETFD, 1);
+ }
+ (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
+ return (hashp->fp != -1 ? 0 : -1);
+}
+
+/*
+ * We have to know that the key will fit, but the last entry on the page is
+ * an overflow pair, so we need to shift things.
+ */
+static void
+squeeze_key(sp, key, val)
+ u_int16_t *sp;
+ const DBT *key, *val;
+{
+ register char *p;
+ u_int16_t free_space, n, off, pageno;
+
+ p = (char *)sp;
+ n = sp[0];
+ free_space = FREESPACE(sp);
+ off = OFFSET(sp);
+
+ pageno = sp[n - 1];
+ off -= key->size;
+ sp[n - 1] = off;
+ memmove(p + off, key->data, key->size);
+ off -= val->size;
+ sp[n] = off;
+ memmove(p + off, val->data, val->size);
+ sp[0] = n + 2;
+ sp[n + 1] = pageno;
+ sp[n + 2] = OVFLPAGE;
+ FREESPACE(sp) = free_space - PAIRSIZE(key, val);
+ OFFSET(sp) = off;
+}
+
+static u_int32_t *
+fetch_bitmap(hashp, ndx)
+ HTAB *hashp;
+ int ndx;
+{
+ if (ndx >= hashp->nmaps)
+ return (NULL);
+ if ((hashp->mapp[ndx] = (u_int32_t *)malloc(hashp->BSIZE)) == NULL)
+ return (NULL);
+ if (__get_page(hashp,
+ (char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) {
+ free(hashp->mapp[ndx]);
+ return (NULL);
+ }
+ return (hashp->mapp[ndx]);
+}
+
+#ifdef DEBUG4
+int
+print_chain(addr)
+ int addr;
+{
+ BUFHEAD *bufp;
+ short *bp, oaddr;
+
+ (void)fprintf(stderr, "%d ", addr);
+ bufp = __get_buf(hashp, addr, NULL, 0);
+ bp = (short *)bufp->page;
+ while (bp[0] && ((bp[bp[0]] == OVFLPAGE) ||
+ ((bp[0] > 2) && bp[2] < REAL_KEY))) {
+ oaddr = bp[bp[0] - 1];
+ (void)fprintf(stderr, "%d ", (int)oaddr);
+ bufp = __get_buf(hashp, (int)oaddr, bufp, 0);
+ bp = (short *)bufp->page;
+ }
+ (void)fprintf(stderr, "\n");
+}
+#endif
diff --git a/trunk/main/db1-ast/hash/hsearch.c b/trunk/main/db1-ast/hash/hsearch.c
new file mode 100644
index 000000000..2971f9308
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hsearch.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hsearch.c 8.4 (Berkeley) 7/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "search.h"
+
+static DB *dbp = NULL;
+static ENTRY retval;
+
+extern int
+hcreate(nel)
+ u_int nel;
+{
+ HASHINFO info;
+
+ info.nelem = nel;
+ info.bsize = 256;
+ info.ffactor = 8;
+ info.cachesize = 0;
+ info.hash = NULL;
+ info.lorder = 0;
+ dbp = (DB *)__hash_open(NULL, O_CREAT | O_RDWR, 0600, &info, 0);
+ return ((int)dbp);
+}
+
+extern ENTRY *
+hsearch(item, action)
+ ENTRY item;
+ ACTION action;
+{
+ DBT key, val;
+ int status;
+
+ if (!dbp)
+ return (NULL);
+ key.data = (u_char *)item.key;
+ key.size = strlen(item.key) + 1;
+
+ if (action == ENTER) {
+ val.data = (u_char *)item.data;
+ val.size = strlen(item.data) + 1;
+ status = (dbp->put)(dbp, &key, &val, R_NOOVERWRITE);
+ if (status)
+ return (NULL);
+ } else {
+ /* FIND */
+ status = (dbp->get)(dbp, &key, &val, 0);
+ if (status)
+ return (NULL);
+ else
+ item.data = (char *)val.data;
+ }
+ retval.key = item.key;
+ retval.data = item.data;
+ return (&retval);
+}
+
+extern void
+hdestroy()
+{
+ if (dbp) {
+ (void)(dbp->close)(dbp);
+ dbp = NULL;
+ }
+}
diff --git a/trunk/main/db1-ast/hash/ndbm.c b/trunk/main/db1-ast/hash/ndbm.c
new file mode 100644
index 000000000..d702f737a
--- /dev/null
+++ b/trunk/main/db1-ast/hash/ndbm.c
@@ -0,0 +1,235 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)ndbm.c 8.4 (Berkeley) 7/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This package provides a dbm compatible interface to the new hashing
+ * package described in db(3).
+ */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <ndbm.h>
+#include "hash.h"
+
+/*
+ * Returns:
+ * *DBM on success
+ * NULL on failure
+ */
+extern DBM *
+dbm_open(file, flags, mode)
+ const char *file;
+ int flags, mode;
+{
+ DBM *db;
+ HASHINFO info;
+ const size_t len = strlen(file) + sizeof (DBM_SUFFIX);
+#ifdef __GNUC__
+ char path[len];
+#else
+ char *path = malloc(len);
+ if (path == NULL)
+ return NULL;
+#endif
+
+ info.bsize = 4096;
+ info.ffactor = 40;
+ info.nelem = 1;
+ info.cachesize = 0;
+ info.hash = NULL;
+ info.lorder = 0;
+ (void)strncpy(path, file, len - 1);
+ (void)strncat(path, DBM_SUFFIX, len - strlen(path) - 1);
+ db = (DBM *)__hash_open(path, flags, mode, &info, 0);
+#ifndef __GNUC__
+ free(path);
+#endif
+ return db;
+}
+
+extern void
+dbm_close(db)
+ DBM *db;
+{
+ (void)(db->close)(db);
+}
+
+/*
+ * Returns:
+ * DATUM on success
+ * NULL on failure
+ */
+extern datum
+dbm_fetch(db, key)
+ DBM *db;
+ datum key;
+{
+ datum retdata;
+ int status;
+ DBT dbtkey, dbtretdata;
+
+ dbtkey.data = key.dptr;
+ dbtkey.size = key.dsize;
+ status = (db->get)(db, &dbtkey, &dbtretdata, 0);
+ if (status) {
+ dbtretdata.data = NULL;
+ dbtretdata.size = 0;
+ }
+ retdata.dptr = dbtretdata.data;
+ retdata.dsize = dbtretdata.size;
+ return (retdata);
+}
+
+/*
+ * Returns:
+ * DATUM on success
+ * NULL on failure
+ */
+extern datum
+dbm_firstkey(db)
+ DBM *db;
+{
+ int status;
+ datum retkey;
+ DBT dbtretkey, dbtretdata;
+
+ status = (db->seq)(db, &dbtretkey, &dbtretdata, R_FIRST);
+ if (status)
+ dbtretkey.data = NULL;
+ retkey.dptr = dbtretkey.data;
+ retkey.dsize = dbtretkey.size;
+ return (retkey);
+}
+
+/*
+ * Returns:
+ * DATUM on success
+ * NULL on failure
+ */
+extern datum
+dbm_nextkey(db)
+ DBM *db;
+{
+ int status;
+ datum retkey;
+ DBT dbtretkey, dbtretdata;
+
+ status = (db->seq)(db, &dbtretkey, &dbtretdata, R_NEXT);
+ if (status)
+ dbtretkey.data = NULL;
+ retkey.dptr = dbtretkey.data;
+ retkey.dsize = dbtretkey.size;
+ return (retkey);
+}
+/*
+ * Returns:
+ * 0 on success
+ * <0 failure
+ */
+extern int
+dbm_delete(db, key)
+ DBM *db;
+ datum key;
+{
+ int status;
+ DBT dbtkey;
+
+ dbtkey.data = key.dptr;
+ dbtkey.size = key.dsize;
+ status = (db->del)(db, &dbtkey, 0);
+ if (status)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Returns:
+ * 0 on success
+ * <0 failure
+ * 1 if DBM_INSERT and entry exists
+ */
+extern int
+dbm_store(db, key, data, flags)
+ DBM *db;
+ datum key, data;
+ int flags;
+{
+ DBT dbtkey, dbtdata;
+
+ dbtkey.data = key.dptr;
+ dbtkey.size = key.dsize;
+ dbtdata.data = data.dptr;
+ dbtdata.size = data.dsize;
+ return ((db->put)(db, &dbtkey, &dbtdata,
+ (flags == DBM_INSERT) ? R_NOOVERWRITE : 0));
+}
+
+extern int
+dbm_error(db)
+ DBM *db;
+{
+ HTAB *hp;
+
+ hp = (HTAB *)db->internal;
+ return (hp->errnum);
+}
+
+extern int
+dbm_clearerr(db)
+ DBM *db;
+{
+ HTAB *hp;
+
+ hp = (HTAB *)db->internal;
+ hp->errnum = 0;
+ return (0);
+}
+
+extern int
+dbm_dirfno(db)
+ DBM *db;
+{
+ return(((HTAB *)db->internal)->fp);
+}
diff --git a/trunk/main/db1-ast/hash/page.h b/trunk/main/db1-ast/hash/page.h
new file mode 100644
index 000000000..0fc0d5a3e
--- /dev/null
+++ b/trunk/main/db1-ast/hash/page.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)page.h 8.2 (Berkeley) 5/31/94
+ */
+
+/*
+ * Definitions for hashing page file format.
+ */
+
+/*
+ * routines dealing with a data page
+ *
+ * page format:
+ * +------------------------------+
+ * p | n | keyoff | datoff | keyoff |
+ * +------------+--------+--------+
+ * | datoff | free | ptr | --> |
+ * +--------+---------------------+
+ * | F R E E A R E A |
+ * +--------------+---------------+
+ * | <---- - - - | data |
+ * +--------+-----+----+----------+
+ * | key | data | key |
+ * +--------+----------+----------+
+ *
+ * Pointer to the free space is always: p[p[0] + 2]
+ * Amount of free space on the page is: p[p[0] + 1]
+ */
+
+/*
+ * How many bytes required for this pair?
+ * 2 shorts in the table at the top of the page + room for the
+ * key and room for the data
+ *
+ * We prohibit entering a pair on a page unless there is also room to append
+ * an overflow page. The reason for this it that you can get in a situation
+ * where a single key/data pair fits on a page, but you can't append an
+ * overflow page and later you'd have to split the key/data and handle like
+ * a big pair.
+ * You might as well do this up front.
+ */
+
+#define PAIRSIZE(K,D) (2*sizeof(u_int16_t) + (K)->size + (D)->size)
+#define BIGOVERHEAD (4*sizeof(u_int16_t))
+#define KEYSIZE(K) (4*sizeof(u_int16_t) + (K)->size);
+#define OVFLSIZE (2*sizeof(u_int16_t))
+#define FREESPACE(P) ((P)[(P)[0]+1])
+#define OFFSET(P) ((P)[(P)[0]+2])
+#define PAIRFITS(P,K,D) \
+ (((P)[2] >= REAL_KEY) && \
+ (PAIRSIZE((K),(D)) + OVFLSIZE) <= FREESPACE((P)))
+#define PAGE_META(N) (((N)+3) * sizeof(u_int16_t))
+
+typedef struct {
+ BUFHEAD *newp;
+ BUFHEAD *oldp;
+ BUFHEAD *nextp;
+ u_int16_t next_addr;
+} SPLIT_RETURN;
diff --git a/trunk/main/db1-ast/hash/search.h b/trunk/main/db1-ast/hash/search.h
new file mode 100644
index 000000000..4d3b9143e
--- /dev/null
+++ b/trunk/main/db1-ast/hash/search.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)search.h 8.1 (Berkeley) 6/4/93
+ */
+
+/* Backward compatibility to hsearch interface. */
+typedef struct entry {
+ char *key;
+ char *data;
+} ENTRY;
+
+typedef enum {
+ FIND, ENTER
+} ACTION;
+
+int hcreate __P((unsigned int));
+void hdestroy __P((void));
+ENTRY *hsearch __P((ENTRY, ACTION));
diff --git a/trunk/main/db1-ast/include/circ-queue.h b/trunk/main/db1-ast/include/circ-queue.h
new file mode 100644
index 000000000..33ba9115b
--- /dev/null
+++ b/trunk/main/db1-ast/include/circ-queue.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: ports/misc/44bsd-more/files/queue.h,v 1.1 2001/01/06 03:41:36 hoek Exp $
+ */
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = (head)->cqh_first; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_next)
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = (head)->cqh_last; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_prev)
+
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+
+#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
diff --git a/trunk/main/db1-ast/include/compat.h b/trunk/main/db1-ast/include/compat.h
new file mode 100644
index 000000000..706e58265
--- /dev/null
+++ b/trunk/main/db1-ast/include/compat.h
@@ -0,0 +1,49 @@
+/* Values for building 4.4 BSD db routines in the GNU C library. */
+
+#ifndef _compat_h_
+#define _compat_h_
+
+#include <fcntl.h>
+
+/*
+ * If you can't provide lock values in the open(2) call. Note, this
+ * allows races to happen.
+ */
+#ifndef O_EXLOCK /* 4.4BSD extension. */
+#define O_EXLOCK 0
+#endif
+
+#ifndef O_SHLOCK /* 4.4BSD extension. */
+#define O_SHLOCK 0
+#endif
+
+#include <errno.h>
+
+#ifndef EFTYPE
+#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */
+#endif
+
+#include <unistd.h>
+#include <limits.h>
+
+#ifndef _POSIX_VDISABLE /* POSIX 1003.1 disabling char. */
+#define _POSIX_VDISABLE 0 /* Some systems used 0. */
+#endif
+
+#include <termios.h>
+
+#ifndef TCSASOFT /* 4.4BSD extension. */
+#define TCSASOFT 0
+#endif
+
+#include <sys/param.h>
+
+#ifndef MAX /* Usually found in <sys/param.h>. */
+#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef MIN /* Usually found in <sys/param.h>. */
+#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
+#endif
+
+
+#endif /* compat.h */
diff --git a/trunk/main/db1-ast/include/db.h b/trunk/main/db1-ast/include/db.h
new file mode 100644
index 000000000..a58724bdd
--- /dev/null
+++ b/trunk/main/db1-ast/include/db.h
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)db.h 8.7 (Berkeley) 6/16/94
+ */
+
+#ifndef _DB_H
+#define _DB_H 1
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+#include <limits.h>
+
+#ifdef __DBINTERFACE_PRIVATE
+#include <compat.h>
+#endif
+
+#ifdef SOLARIS
+#include "solaris-compat/compat.h"
+#endif
+
+#define RET_ERROR -1 /* Return values. */
+#define RET_SUCCESS 0
+#define RET_SPECIAL 1
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+#if (!defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__APPLE__))
+typedef __signed char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int u_int32_t;
+#ifdef WE_DONT_NEED_QUADS
+typedef long long int64_t;
+typedef unsigned long long u_int64_t;
+#endif
+#endif /* __FreeBSD__ */
+#endif
+
+#ifdef SOLARIS
+#define __P(p) p
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+
+#define MAX_PAGE_NUMBER 0xffffffff /* >= # of pages in a file */
+typedef u_int32_t pgno_t;
+#define MAX_PAGE_OFFSET 65535 /* >= # of bytes in a page */
+typedef u_int16_t indx_t;
+#define MAX_REC_NUMBER 0xffffffff /* >= # of records in a tree */
+typedef u_int32_t recno_t;
+
+/* Key/data structure -- a Data-Base Thang. */
+typedef struct {
+ void *data; /* data */
+ size_t size; /* data length */
+} DBT;
+
+/* Routine flags. */
+#define R_CURSOR 1 /* del, put, seq */
+#define __R_UNUSED 2 /* UNUSED */
+#define R_FIRST 3 /* seq */
+#define R_IAFTER 4 /* put (RECNO) */
+#define R_IBEFORE 5 /* put (RECNO) */
+#define R_LAST 6 /* seq (BTREE, RECNO) */
+#define R_NEXT 7 /* seq */
+#define R_NOOVERWRITE 8 /* put */
+#define R_PREV 9 /* seq (BTREE, RECNO) */
+#define R_SETCURSOR 10 /* put (RECNO) */
+#define R_RECNOSYNC 11 /* sync (RECNO) */
+
+typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE;
+
+/*
+ * !!!
+ * The following flags are included in the dbopen(3) call as part of the
+ * open(2) flags. In order to avoid conflicts with the open flags, start
+ * at the top of the 16 or 32-bit number space and work our way down. If
+ * the open flags were significantly expanded in the future, it could be
+ * a problem. Wish I'd left another flags word in the dbopen call.
+ *
+ * !!!
+ * None of this stuff is implemented yet. The only reason that it's here
+ * is so that the access methods can skip copying the key/data pair when
+ * the DB_LOCK flag isn't set.
+ */
+#if UINT_MAX > 65535
+#define DB_LOCK 0x20000000 /* Do locking. */
+#define DB_SHMEM 0x40000000 /* Use shared memory. */
+#define DB_TXN 0x80000000 /* Do transactions. */
+#else
+#define DB_LOCK 0x2000 /* Do locking. */
+#define DB_SHMEM 0x4000 /* Use shared memory. */
+#define DB_TXN 0x8000 /* Do transactions. */
+#endif
+
+/* Access method description structure. */
+typedef struct __db {
+ DBTYPE type; /* Underlying db type. */
+ int (*close) __P((struct __db *));
+ int (*del) __P((const struct __db *, const DBT *, u_int));
+ int (*get) __P((const struct __db *, const DBT *, DBT *, u_int));
+ int (*put) __P((const struct __db *, DBT *, const DBT *, u_int));
+ int (*seq) __P((const struct __db *, DBT *, DBT *, u_int));
+ int (*sync) __P((const struct __db *, u_int));
+ void *internal; /* Access method private. */
+ int (*fd) __P((const struct __db *));
+} DB;
+
+#define BTREEMAGIC 0x053162
+#define BTREEVERSION 3
+
+/* Structure used to pass parameters to the btree routines. */
+typedef struct {
+#define R_DUP 0x01 /* duplicate keys */
+ u_long flags;
+ u_int cachesize; /* bytes to cache */
+ int maxkeypage; /* maximum keys per page */
+ int minkeypage; /* minimum keys per page */
+ u_int psize; /* page size */
+ int (*compare) /* comparison function */
+ __P((const DBT *, const DBT *));
+ size_t (*prefix) /* prefix function */
+ __P((const DBT *, const DBT *));
+ int lorder; /* byte order */
+} BTREEINFO;
+
+#define HASHMAGIC 0x061561
+#define HASHVERSION 2
+
+/* Structure used to pass parameters to the hashing routines. */
+typedef struct {
+ u_int bsize; /* bucket size */
+ u_int ffactor; /* fill factor */
+ u_int nelem; /* number of elements */
+ u_int cachesize; /* bytes to cache */
+ u_int32_t /* hash function */
+ (*hash) __P((const void *, size_t));
+ int lorder; /* byte order */
+} HASHINFO;
+
+/* Structure used to pass parameters to the record routines. */
+typedef struct {
+#define R_FIXEDLEN 0x01 /* fixed-length records */
+#define R_NOKEY 0x02 /* key not required */
+#define R_SNAPSHOT 0x04 /* snapshot the input */
+ u_long flags;
+ u_int cachesize; /* bytes to cache */
+ u_int psize; /* page size */
+ int lorder; /* byte order */
+ size_t reclen; /* record length (fixed-length records) */
+ u_char bval; /* delimiting byte (variable-length records */
+ char *bfname; /* btree file name */
+} RECNOINFO;
+
+#ifdef __DBINTERFACE_PRIVATE
+/*
+ * Little endian <==> big endian 32-bit swap macros.
+ * M_32_SWAP swap a memory location
+ * P_32_SWAP swap a referenced memory location
+ * P_32_COPY swap from one location to another
+ */
+#define M_32_SWAP(a) { \
+ u_int32_t _tmp = a; \
+ ((char *)&a)[0] = ((char *)&_tmp)[3]; \
+ ((char *)&a)[1] = ((char *)&_tmp)[2]; \
+ ((char *)&a)[2] = ((char *)&_tmp)[1]; \
+ ((char *)&a)[3] = ((char *)&_tmp)[0]; \
+}
+#define P_32_SWAP(a) { \
+ u_int32_t _tmp = *(u_int32_t *)a; \
+ ((char *)a)[0] = ((char *)&_tmp)[3]; \
+ ((char *)a)[1] = ((char *)&_tmp)[2]; \
+ ((char *)a)[2] = ((char *)&_tmp)[1]; \
+ ((char *)a)[3] = ((char *)&_tmp)[0]; \
+}
+#define P_32_COPY(a, b) { \
+ ((char *)&(b))[0] = ((char *)&(a))[3]; \
+ ((char *)&(b))[1] = ((char *)&(a))[2]; \
+ ((char *)&(b))[2] = ((char *)&(a))[1]; \
+ ((char *)&(b))[3] = ((char *)&(a))[0]; \
+}
+
+/*
+ * Little endian <==> big endian 16-bit swap macros.
+ * M_16_SWAP swap a memory location
+ * P_16_SWAP swap a referenced memory location
+ * P_16_COPY swap from one location to another
+ */
+#define M_16_SWAP(a) { \
+ u_int16_t _tmp = a; \
+ ((char *)&a)[0] = ((char *)&_tmp)[1]; \
+ ((char *)&a)[1] = ((char *)&_tmp)[0]; \
+}
+#define P_16_SWAP(a) { \
+ u_int16_t _tmp = *(u_int16_t *)a; \
+ ((char *)a)[0] = ((char *)&_tmp)[1]; \
+ ((char *)a)[1] = ((char *)&_tmp)[0]; \
+}
+#define P_16_COPY(a, b) { \
+ ((char *)&(b))[0] = ((char *)&(a))[1]; \
+ ((char *)&(b))[1] = ((char *)&(a))[0]; \
+}
+#endif
+
+__BEGIN_DECLS
+DB *__dbopen __P((const char *, int, int, DBTYPE, const void *));
+DB *dbopen __P((const char *, int, int, DBTYPE, const void *));
+
+#ifdef __DBINTERFACE_PRIVATE
+DB *__bt_open __P((const char *, int, int, const BTREEINFO *, int));
+DB *__hash_open __P((const char *, int, int, const HASHINFO *, int));
+DB *__rec_open __P((const char *, int, int, const RECNOINFO *, int));
+void __dbpanic __P((DB *dbp));
+#endif
+__END_DECLS
+
+#endif /* db.h */
diff --git a/trunk/main/db1-ast/include/mpool.h b/trunk/main/db1-ast/include/mpool.h
new file mode 100644
index 000000000..0cfc5741c
--- /dev/null
+++ b/trunk/main/db1-ast/include/mpool.h
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mpool.h 8.2 (Berkeley) 7/14/94
+ */
+
+#ifndef _MPOOL_H
+#define _MPOOL_H 1
+
+#include <sys/queue.h>
+#ifndef CIRCLEQ_HEAD
+#include <circ-queue.h>
+#endif
+
+/*
+ * The memory pool scheme is a simple one. Each in-memory page is referenced
+ * by a bucket which is threaded in up to two of three ways. All active pages
+ * are threaded on a hash chain (hashed by page number) and an lru chain.
+ * Inactive pages are threaded on a free chain. Each reference to a memory
+ * pool is handed an opaque MPOOL cookie which stores all of this information.
+ */
+#define HASHSIZE 128
+#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE)
+
+/* The BKT structures are the elements of the queues. */
+typedef struct _bkt {
+ CIRCLEQ_ENTRY(_bkt) hq; /* hash queue */
+ CIRCLEQ_ENTRY(_bkt) q; /* lru queue */
+ void *page; /* page */
+ pgno_t pgno; /* page number */
+
+#define MPOOL_DIRTY 0x01 /* page needs to be written */
+#define MPOOL_PINNED 0x02 /* page is pinned into memory */
+ u_int8_t flags; /* flags */
+} BKT;
+
+typedef struct MPOOL {
+ CIRCLEQ_HEAD(_lqh, _bkt) lqh; /* lru queue head */
+ /* hash queue array */
+ CIRCLEQ_HEAD(_hqh, _bkt) hqh[HASHSIZE];
+ pgno_t curcache; /* current number of cached pages */
+ pgno_t maxcache; /* max number of cached pages */
+ pgno_t npages; /* number of pages in the file */
+ u_long pagesize; /* file page size */
+ int fd; /* file descriptor */
+ /* page in conversion routine */
+ void (*pgin) __P((void *, pgno_t, void *));
+ /* page out conversion routine */
+ void (*pgout) __P((void *, pgno_t, void *));
+ void *pgcookie; /* cookie for page in/out routines */
+#ifdef STATISTICS
+ u_long cachehit;
+ u_long cachemiss;
+ u_long pagealloc;
+ u_long pageflush;
+ u_long pageget;
+ u_long pagenew;
+ u_long pageput;
+ u_long pageread;
+ u_long pagewrite;
+#endif
+} MPOOL;
+
+__BEGIN_DECLS
+MPOOL *__mpool_open __P((void *, int, pgno_t, pgno_t));
+MPOOL *mpool_open __P((void *, int, pgno_t, pgno_t));
+void __mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *),
+ void (*)(void *, pgno_t, void *), void *));
+void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *),
+ void (*)(void *, pgno_t, void *), void *));
+void *__mpool_new __P((MPOOL *, pgno_t *));
+void *mpool_new __P((MPOOL *, pgno_t *));
+void *__mpool_get __P((MPOOL *, pgno_t, u_int));
+void *mpool_get __P((MPOOL *, pgno_t, u_int));
+int __mpool_put __P((MPOOL *, void *, u_int));
+int mpool_put __P((MPOOL *, void *, u_int));
+int __mpool_sync __P((MPOOL *));
+int mpool_sync __P((MPOOL *));
+int __mpool_close __P((MPOOL *));
+int mpool_close __P((MPOOL *));
+#ifdef STATISTICS
+void mpool_stat __P((MPOOL *));
+#endif
+__END_DECLS
+
+#endif /* mpool.h */
diff --git a/trunk/main/db1-ast/include/ndbm.h b/trunk/main/db1-ast/include/ndbm.h
new file mode 100644
index 000000000..d2079b0a5
--- /dev/null
+++ b/trunk/main/db1-ast/include/ndbm.h
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ndbm.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _NDBM_H
+#define _NDBM_H 1
+
+#include "db.h"
+
+/* Map dbm interface onto db(3). */
+#define DBM_RDONLY O_RDONLY
+
+/* Flags to dbm_store(). */
+#define DBM_INSERT 0
+#define DBM_REPLACE 1
+
+/*
+ * The db(3) support for ndbm(3) always appends this suffix to the
+ * file name to avoid overwriting the user's original database.
+ */
+#define DBM_SUFFIX ".db"
+
+typedef struct {
+ char *dptr;
+ int dsize;
+} datum;
+
+typedef DB DBM;
+#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE
+
+__BEGIN_DECLS
+void dbm_close __P((DBM *));
+int dbm_delete __P((DBM *, datum));
+datum dbm_fetch __P((DBM *, datum));
+datum dbm_firstkey __P((DBM *));
+long dbm_forder __P((DBM *, datum));
+datum dbm_nextkey __P((DBM *));
+DBM *dbm_open __P((const char *, int, int));
+int dbm_store __P((DBM *, datum, datum, int));
+int dbm_dirfno __P((DBM *));
+int dbm_error __P((DBM *));
+int dbm_clearerr __P((DBM *));
+__END_DECLS
+
+#endif /* ndbm.h */
diff --git a/trunk/main/db1-ast/libdb.map b/trunk/main/db1-ast/libdb.map
new file mode 100644
index 000000000..87e34c430
--- /dev/null
+++ b/trunk/main/db1-ast/libdb.map
@@ -0,0 +1,11 @@
+GLIBC_2.0 {
+ global:
+ # the real DB entry point.
+ dbopen; __dbopen;
+
+ # The compatibility functions.
+ dbm_clearerr; dbm_close; dbm_delete; dbm_dirfno; dbm_error;
+ dbm_fetch; dbm_firstkey; dbm_nextkey; dbm_open; dbm_store;
+ local:
+ *;
+};
diff --git a/trunk/main/db1-ast/mpool/README b/trunk/main/db1-ast/mpool/README
new file mode 100644
index 000000000..0f01fbcdb
--- /dev/null
+++ b/trunk/main/db1-ast/mpool/README
@@ -0,0 +1,7 @@
+# @(#)README 8.1 (Berkeley) 6/4/93
+
+These are the current memory pool routines.
+They aren't ready for prime time, yet, and
+the interface is expected to change.
+
+--keith
diff --git a/trunk/main/db1-ast/mpool/mpool.c b/trunk/main/db1-ast/mpool/mpool.c
new file mode 100644
index 000000000..b5b7c86d6
--- /dev/null
+++ b/trunk/main/db1-ast/mpool/mpool.c
@@ -0,0 +1,498 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mpool.c 8.5 (Berkeley) 7/26/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+
+#define __MPOOLINTERFACE_PRIVATE
+#include <mpool.h>
+
+#undef __APPLE__
+
+#ifndef __APPLE__
+#define mpool_open __mpool_open
+#define mpool_filter __mpool_filter
+#define mpool_new __mpool_new
+#define mpool_get __mpool_get
+#define mpool_put __mpool_put
+#define mpool_sync __mpool_sync
+#define mpool_close __mpool_close
+#endif
+
+static BKT *mpool_bkt __P((MPOOL *));
+static BKT *mpool_look __P((MPOOL *, pgno_t));
+static int mpool_write __P((MPOOL *, BKT *));
+
+/*
+ * mpool_open --
+ * Initialize a memory pool.
+ */
+MPOOL *
+mpool_open(key, fd, pagesize, maxcache)
+ void *key;
+ int fd;
+ pgno_t pagesize, maxcache;
+{
+ struct stat sb;
+ MPOOL *mp;
+ int entry;
+
+ /*
+ * Get information about the file.
+ *
+ * XXX
+ * We don't currently handle pipes, although we should.
+ */
+ if (fstat(fd, &sb))
+ return (NULL);
+ if (!S_ISREG(sb.st_mode)) {
+ errno = ESPIPE;
+ return (NULL);
+ }
+
+ /* Allocate and initialize the MPOOL cookie. */
+ if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL)
+ return (NULL);
+ CIRCLEQ_INIT(&mp->lqh);
+ for (entry = 0; entry < HASHSIZE; ++entry)
+ CIRCLEQ_INIT(&mp->hqh[entry]);
+ mp->maxcache = maxcache;
+ mp->npages = sb.st_size / pagesize;
+ mp->pagesize = pagesize;
+ mp->fd = fd;
+ return (mp);
+}
+
+/*
+ * mpool_filter --
+ * Initialize input/output filters.
+ */
+void
+mpool_filter(mp, pgin, pgout, pgcookie)
+ MPOOL *mp;
+ void (*pgin) __P((void *, pgno_t, void *));
+ void (*pgout) __P((void *, pgno_t, void *));
+ void *pgcookie;
+{
+ mp->pgin = pgin;
+ mp->pgout = pgout;
+ mp->pgcookie = pgcookie;
+}
+
+/*
+ * mpool_new --
+ * Get a new page of memory.
+ */
+void *
+mpool_new(mp, pgnoaddr)
+ MPOOL *mp;
+ pgno_t *pgnoaddr;
+{
+ struct _hqh *head;
+ BKT *bp;
+
+ if (mp->npages == MAX_PAGE_NUMBER) {
+ (void)fprintf(stderr, "mpool_new: page allocation overflow.\n");
+ abort();
+ }
+#ifdef STATISTICS
+ ++mp->pagenew;
+#endif
+ /*
+ * Get a BKT from the cache. Assign a new page number, attach
+ * it to the head of the hash chain, the tail of the lru chain,
+ * and return.
+ */
+ if ((bp = mpool_bkt(mp)) == NULL)
+ return (NULL);
+ *pgnoaddr = bp->pgno = mp->npages++;
+ bp->flags = MPOOL_PINNED;
+
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_INSERT_HEAD(head, bp, hq);
+ CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
+ return (bp->page);
+}
+
+/*
+ * mpool_get
+ * Get a page.
+ */
+void *
+mpool_get(mp, pgno, flags)
+ MPOOL *mp;
+ pgno_t pgno;
+ u_int flags; /* XXX not used? */
+{
+ struct _hqh *head;
+ BKT *bp;
+ off_t off;
+ int nr;
+
+ /* Check for attempt to retrieve a non-existent page. */
+ if (pgno >= mp->npages) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+#ifdef STATISTICS
+ ++mp->pageget;
+#endif
+
+ /* Check for a page that is cached. */
+ if ((bp = mpool_look(mp, pgno)) != NULL) {
+#ifdef DEBUG
+ if (bp->flags & MPOOL_PINNED) {
+ (void)fprintf(stderr,
+ "mpool_get: page %d already pinned\n", bp->pgno);
+ abort();
+ }
+#endif
+ /*
+ * Move the page to the head of the hash chain and the tail
+ * of the lru chain.
+ */
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_REMOVE(head, bp, hq);
+ CIRCLEQ_INSERT_HEAD(head, bp, hq);
+ CIRCLEQ_REMOVE(&mp->lqh, bp, q);
+ CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
+
+ /* Return a pinned page. */
+ bp->flags |= MPOOL_PINNED;
+ return (bp->page);
+ }
+
+ /* Get a page from the cache. */
+ if ((bp = mpool_bkt(mp)) == NULL)
+ return (NULL);
+
+ /* Read in the contents. */
+#ifdef STATISTICS
+ ++mp->pageread;
+#endif
+ off = mp->pagesize * pgno;
+ if (lseek(mp->fd, off, SEEK_SET) != off)
+ return (NULL);
+ if ((u_long) (nr = read(mp->fd, bp->page, mp->pagesize))
+ != mp->pagesize) {
+ if (nr >= 0)
+ errno = EFTYPE;
+ return (NULL);
+ }
+
+ /* Set the page number, pin the page. */
+ bp->pgno = pgno;
+ bp->flags = MPOOL_PINNED;
+
+ /*
+ * Add the page to the head of the hash chain and the tail
+ * of the lru chain.
+ */
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_INSERT_HEAD(head, bp, hq);
+ CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
+
+ /* Run through the user's filter. */
+ if (mp->pgin != NULL)
+ (mp->pgin)(mp->pgcookie, bp->pgno, bp->page);
+
+ return (bp->page);
+}
+
+/*
+ * mpool_put
+ * Return a page.
+ */
+int
+mpool_put(mp, page, flags)
+ MPOOL *mp;
+ void *page;
+ u_int flags;
+{
+ BKT *bp;
+
+#ifdef STATISTICS
+ ++mp->pageput;
+#endif
+ bp = (BKT *)((char *)page - sizeof(BKT));
+#ifdef DEBUG
+ if (!(bp->flags & MPOOL_PINNED)) {
+ (void)fprintf(stderr,
+ "mpool_put: page %d not pinned\n", bp->pgno);
+ abort();
+ }
+#endif
+ bp->flags &= ~MPOOL_PINNED;
+ bp->flags |= flags & MPOOL_DIRTY;
+ return (RET_SUCCESS);
+}
+
+/*
+ * mpool_close
+ * Close the buffer pool.
+ */
+int
+mpool_close(mp)
+ MPOOL *mp;
+{
+ BKT *bp;
+
+ /* Free up any space allocated to the lru pages. */
+ while ((bp = mp->lqh.cqh_first) != (void *)&mp->lqh) {
+ CIRCLEQ_REMOVE(&mp->lqh, mp->lqh.cqh_first, q);
+ free(bp);
+ }
+
+ /* Free the MPOOL cookie. */
+ free(mp);
+ return (RET_SUCCESS);
+}
+
+/*
+ * mpool_sync
+ * Sync the pool to disk.
+ */
+int
+mpool_sync(mp)
+ MPOOL *mp;
+{
+ BKT *bp;
+
+ /* Walk the lru chain, flushing any dirty pages to disk. */
+ for (bp = mp->lqh.cqh_first;
+ bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
+ if (bp->flags & MPOOL_DIRTY &&
+ mpool_write(mp, bp) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Sync the file descriptor. */
+ return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);
+}
+
+#define __APPLE__
+
+#ifndef __APPLE__
+#undef mpool_open
+#undef mpool_filter
+#undef mpool_new
+#undef mpool_get
+#undef mpool_put
+#undef mpool_close
+#undef mpool_sync
+
+#define weak_alias(original, alias) \
+ asm (".weak " #alias "\n" #alias " = " #original);
+weak_alias (__mpool_open, mpool_open)
+weak_alias (__mpool_filter, mpool_filter)
+weak_alias (__mpool_new, mpool_new)
+weak_alias (__mpool_get, mpool_get)
+weak_alias (__mpool_put, mpool_put)
+weak_alias (__mpool_close, mpool_close)
+weak_alias (__mpool_sync, mpool_sync)
+#endif
+
+/*
+ * mpool_bkt
+ * Get a page from the cache (or create one).
+ */
+static BKT *
+mpool_bkt(mp)
+ MPOOL *mp;
+{
+ struct _hqh *head;
+ BKT *bp;
+
+ /* If under the max cached, always create a new page. */
+ if (mp->curcache < mp->maxcache)
+ goto new;
+
+ /*
+ * If the cache is max'd out, walk the lru list for a buffer we
+ * can flush. If we find one, write it (if necessary) and take it
+ * off any lists. If we don't find anything we grow the cache anyway.
+ * The cache never shrinks.
+ */
+ for (bp = mp->lqh.cqh_first;
+ bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
+ if (!(bp->flags & MPOOL_PINNED)) {
+ /* Flush if dirty. */
+ if (bp->flags & MPOOL_DIRTY &&
+ mpool_write(mp, bp) == RET_ERROR)
+ return (NULL);
+#ifdef STATISTICS
+ ++mp->pageflush;
+#endif
+ /* Remove from the hash and lru queues. */
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_REMOVE(head, bp, hq);
+ CIRCLEQ_REMOVE(&mp->lqh, bp, q);
+#ifdef DEBUG
+ { void *spage;
+ spage = bp->page;
+ memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
+ bp->page = spage;
+ }
+#endif
+ return (bp);
+ }
+
+new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
+ return (NULL);
+#ifdef STATISTICS
+ ++mp->pagealloc;
+#endif
+#if defined(DEBUG) || defined(PURIFY)
+ memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
+#endif
+ bp->page = (char *)bp + sizeof(BKT);
+ ++mp->curcache;
+ return (bp);
+}
+
+/*
+ * mpool_write
+ * Write a page to disk.
+ */
+static int
+mpool_write(mp, bp)
+ MPOOL *mp;
+ BKT *bp;
+{
+ off_t off;
+
+#ifdef STATISTICS
+ ++mp->pagewrite;
+#endif
+
+ /* Run through the user's filter. */
+ if (mp->pgout)
+ (mp->pgout)(mp->pgcookie, bp->pgno, bp->page);
+
+ off = mp->pagesize * bp->pgno;
+ if (lseek(mp->fd, off, SEEK_SET) != off)
+ return (RET_ERROR);
+ if ((u_long) write(mp->fd, bp->page, mp->pagesize) != mp->pagesize)
+ return (RET_ERROR);
+
+ bp->flags &= ~MPOOL_DIRTY;
+ return (RET_SUCCESS);
+}
+
+/*
+ * mpool_look
+ * Lookup a page in the cache.
+ */
+static BKT *
+mpool_look(mp, pgno)
+ MPOOL *mp;
+ pgno_t pgno;
+{
+ struct _hqh *head;
+ BKT *bp;
+
+ head = &mp->hqh[HASHKEY(pgno)];
+ for (bp = head->cqh_first; bp != (void *)head; bp = bp->hq.cqe_next)
+ if (bp->pgno == pgno) {
+#ifdef STATISTICS
+ ++mp->cachehit;
+#endif
+ return (bp);
+ }
+#ifdef STATISTICS
+ ++mp->cachemiss;
+#endif
+ return (NULL);
+}
+
+#ifdef STATISTICS
+/*
+ * mpool_stat
+ * Print out cache statistics.
+ */
+void
+mpool_stat(mp)
+ MPOOL *mp;
+{
+ BKT *bp;
+ int cnt;
+ char *sep;
+
+ (void)fprintf(stderr, "%lu pages in the file\n", mp->npages);
+ (void)fprintf(stderr,
+ "page size %lu, cacheing %lu pages of %lu page max cache\n",
+ mp->pagesize, mp->curcache, mp->maxcache);
+ (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
+ mp->pageput, mp->pageget, mp->pagenew);
+ (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
+ mp->pagealloc, mp->pageflush);
+ if (mp->cachehit + mp->cachemiss)
+ (void)fprintf(stderr,
+ "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
+ ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
+ * 100, mp->cachehit, mp->cachemiss);
+ (void)fprintf(stderr, "%lu page reads, %lu page writes\n",
+ mp->pageread, mp->pagewrite);
+
+ sep = "";
+ cnt = 0;
+ for (bp = mp->lqh.cqh_first;
+ bp != (void *)&mp->lqh; bp = bp->q.cqe_next) {
+ (void)fprintf(stderr, "%s%d", sep, bp->pgno);
+ if (bp->flags & MPOOL_DIRTY)
+ (void)fprintf(stderr, "d");
+ if (bp->flags & MPOOL_PINNED)
+ (void)fprintf(stderr, "P");
+ if (++cnt == 10) {
+ sep = "\n";
+ cnt = 0;
+ } else
+ sep = ", ";
+
+ }
+ (void)fprintf(stderr, "\n");
+}
+#endif
diff --git a/trunk/main/db1-ast/recno/extern.h b/trunk/main/db1-ast/recno/extern.h
new file mode 100644
index 000000000..feed43445
--- /dev/null
+++ b/trunk/main/db1-ast/recno/extern.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 6/4/94
+ */
+
+#include "../btree/extern.h"
+
+int __rec_close __P((DB *));
+int __rec_delete __P((const DB *, const DBT *, u_int));
+int __rec_dleaf __P((BTREE *, PAGE *, u_int32_t));
+int __rec_fd __P((const DB *));
+int __rec_fmap __P((BTREE *, recno_t));
+int __rec_fout __P((BTREE *));
+int __rec_fpipe __P((BTREE *, recno_t));
+int __rec_get __P((const DB *, const DBT *, DBT *, u_int));
+int __rec_iput __P((BTREE *, recno_t, const DBT *, u_int));
+int __rec_put __P((const DB *dbp, DBT *, const DBT *, u_int));
+int __rec_ret __P((BTREE *, EPG *, recno_t, DBT *, DBT *));
+EPG *__rec_search __P((BTREE *, recno_t, enum SRCHOP));
+int __rec_seq __P((const DB *, DBT *, DBT *, u_int));
+int __rec_sync __P((const DB *, u_int));
+int __rec_vmap __P((BTREE *, recno_t));
+int __rec_vout __P((BTREE *));
+int __rec_vpipe __P((BTREE *, recno_t));
diff --git a/trunk/main/db1-ast/recno/rec_close.c b/trunk/main/db1-ast/recno/rec_close.c
new file mode 100644
index 000000000..20b00f52c
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_close.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_close.c 8.6 (Berkeley) 8/18/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_CLOSE -- Close a recno tree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_close(dbp)
+ DB *dbp;
+{
+ BTREE *t;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ if (__rec_sync(dbp, 0) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Committed to closing. */
+ status = RET_SUCCESS;
+ if (F_ISSET(t, R_MEMMAPPED) && munmap(t->bt_smap, t->bt_msize))
+ status = RET_ERROR;
+
+ if (!F_ISSET(t, R_INMEM)) {
+ if (F_ISSET(t, R_CLOSEFP)) {
+ if (fclose(t->bt_rfp))
+ status = RET_ERROR;
+ } else
+ if (close(t->bt_rfd))
+ status = RET_ERROR;
+ }
+
+ if (__bt_close(dbp) == RET_ERROR)
+ status = RET_ERROR;
+
+ return (status);
+}
+
+/*
+ * __REC_SYNC -- sync the recno tree to disk.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__rec_sync(dbp, flags)
+ const DB *dbp;
+ u_int flags;
+{
+ struct iovec iov[2];
+ BTREE *t;
+ DBT data, key;
+ off_t off;
+ recno_t scursor, trec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ if (flags == R_RECNOSYNC)
+ return (__bt_sync(dbp, 0));
+
+ if (F_ISSET(t, R_RDONLY | R_INMEM) || !F_ISSET(t, R_MODIFIED))
+ return (RET_SUCCESS);
+
+ /* Read any remaining records into the tree. */
+ if (!F_ISSET(t, R_EOF) && t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Rewind the file descriptor. */
+ if (lseek(t->bt_rfd, (off_t)0, SEEK_SET) != 0)
+ return (RET_ERROR);
+
+ /* Save the cursor. */
+ scursor = t->bt_cursor.rcursor;
+
+ key.size = sizeof(recno_t);
+ key.data = &trec;
+
+ if (F_ISSET(t, R_FIXLEN)) {
+ /*
+ * We assume that fixed length records are all fixed length.
+ * Any that aren't are either EINVAL'd or corrected by the
+ * record put code.
+ */
+ status = (dbp->seq)(dbp, &key, &data, R_FIRST);
+ while (status == RET_SUCCESS) {
+ if ((size_t) write(t->bt_rfd, data.data, data.size) != data.size)
+ return (RET_ERROR);
+ status = (dbp->seq)(dbp, &key, &data, R_NEXT);
+ }
+ } else {
+ iov[1].iov_base = &t->bt_bval;
+ iov[1].iov_len = 1;
+
+ status = (dbp->seq)(dbp, &key, &data, R_FIRST);
+ while (status == RET_SUCCESS) {
+ iov[0].iov_base = data.data;
+ iov[0].iov_len = data.size;
+ if ((size_t) writev(t->bt_rfd, iov, 2) != data.size + 1)
+ return (RET_ERROR);
+ status = (dbp->seq)(dbp, &key, &data, R_NEXT);
+ }
+ }
+
+ /* Restore the cursor. */
+ t->bt_cursor.rcursor = scursor;
+
+ if (status == RET_ERROR)
+ return (RET_ERROR);
+ if ((off = lseek(t->bt_rfd, (off_t)0, SEEK_CUR)) == -1)
+ return (RET_ERROR);
+ if (ftruncate(t->bt_rfd, off))
+ return (RET_ERROR);
+ F_CLR(t, R_MODIFIED);
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_delete.c b/trunk/main/db1-ast/recno/rec_delete.c
new file mode 100644
index 000000000..fc2047226
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_delete.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_delete.c 8.7 (Berkeley) 7/14/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+static int rec_rdelete __P((BTREE *, recno_t));
+
+/*
+ * __REC_DELETE -- Delete the item(s) referenced by a key.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key to delete
+ * flags: R_CURSOR if deleting what the cursor references
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+int
+__rec_delete(dbp, key, flags)
+ const DB *dbp;
+ const DBT *key;
+ u_int flags;
+{
+ BTREE *t;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ switch(flags) {
+ case 0:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ if (nrec > t->bt_nrecs)
+ return (RET_SPECIAL);
+ --nrec;
+ status = rec_rdelete(t, nrec);
+ break;
+ case R_CURSOR:
+ if (!F_ISSET(&t->bt_cursor, CURS_INIT))
+ goto einval;
+ if (t->bt_nrecs == 0)
+ return (RET_SPECIAL);
+ status = rec_rdelete(t, t->bt_cursor.rcursor - 1);
+ if (status == RET_SUCCESS)
+ --t->bt_cursor.rcursor;
+ break;
+ default:
+einval: errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (status == RET_SUCCESS)
+ F_SET(t, B_MODIFIED | R_MODIFIED);
+ return (status);
+}
+
+/*
+ * REC_RDELETE -- Delete the data matching the specified key.
+ *
+ * Parameters:
+ * tree: tree
+ * nrec: record to delete
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+static int
+rec_rdelete(t, nrec)
+ BTREE *t;
+ recno_t nrec;
+{
+ EPG *e;
+ PAGE *h;
+ int status;
+
+ /* Find the record; __rec_search pins the page. */
+ if ((e = __rec_search(t, nrec, SDELETE)) == NULL)
+ return (RET_ERROR);
+
+ /* Delete the record. */
+ h = e->page;
+ status = __rec_dleaf(t, h, e->index);
+ if (status != RET_SUCCESS) {
+ mpool_put(t->bt_mp, h, 0);
+ return (status);
+ }
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_DLEAF -- Delete a single record from a recno leaf page.
+ *
+ * Parameters:
+ * t: tree
+ * index: index on current page to delete
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__rec_dleaf(t, h, index)
+ BTREE *t;
+ PAGE *h;
+ u_int32_t index;
+{
+ RLEAF *rl;
+ indx_t *ip, cnt, offset;
+ u_int32_t nbytes;
+ char *from;
+ void *to;
+
+ /*
+ * Delete a record from a recno leaf page. Internal records are never
+ * deleted from internal pages, regardless of the records that caused
+ * them to be added being deleted. Pages made empty by deletion are
+ * not reclaimed. They are, however, made available for reuse.
+ *
+ * Pack the remaining entries at the end of the page, shift the indices
+ * down, overwriting the deleted record and its index. If the record
+ * uses overflow pages, make them available for reuse.
+ */
+ to = rl = GETRLEAF(h, index);
+ if (rl->flags & P_BIGDATA && __ovfl_delete(t, rl->bytes) == RET_ERROR)
+ return (RET_ERROR);
+ nbytes = NRLEAF(rl);
+
+ /*
+ * Compress the key/data pairs. Compress and adjust the [BR]LEAF
+ * offsets. Reset the headers.
+ */
+ from = (char *)h + h->upper;
+ memmove(from + nbytes, from, (char *)to - from);
+ h->upper += nbytes;
+
+ offset = h->linp[index];
+ for (cnt = &h->linp[index] - (ip = &h->linp[0]); cnt--; ++ip)
+ if (ip[0] < offset)
+ ip[0] += nbytes;
+ for (cnt = &h->linp[NEXTINDEX(h)] - ip; --cnt; ++ip)
+ ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1];
+ h->lower -= sizeof(indx_t);
+ --t->bt_nrecs;
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_get.c b/trunk/main/db1-ast/recno/rec_get.c
new file mode 100644
index 000000000..7038cc81a
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_get.c
@@ -0,0 +1,311 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_get.c 8.9 (Berkeley) 8/18/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_GET -- Get a record from the btree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key to find
+ * data: data to return
+ * flag: currently unused
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+int
+__rec_get(dbp, key, data, flags)
+ const DB *dbp;
+ const DBT *key;
+ DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG *e;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Get currently doesn't take any flags, and keys of 0 are illegal. */
+ if (flags || (nrec = *(recno_t *)key->data) == 0) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ /*
+ * If we haven't seen this record yet, try to find it in the
+ * original file.
+ */
+ if (nrec > t->bt_nrecs) {
+ if (F_ISSET(t, R_EOF | R_INMEM))
+ return (RET_SPECIAL);
+ if ((status = t->bt_irec(t, nrec)) != RET_SUCCESS)
+ return (status);
+ }
+
+ --nrec;
+ if ((e = __rec_search(t, nrec, SEARCH)) == NULL)
+ return (RET_ERROR);
+
+ status = __rec_ret(t, e, 0, NULL, data);
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e->page, 0);
+ else
+ t->bt_pinned = e->page;
+ return (status);
+}
+
+/*
+ * __REC_FPIPE -- Get fixed length records from a pipe.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_fpipe(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ recno_t nrec;
+ size_t len;
+ int ch;
+ u_char *p;
+
+ if (t->bt_rdata.size < t->bt_reclen) {
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_reclen) :
+ realloc(t->bt_rdata.data, t->bt_reclen);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.size = t->bt_reclen;
+ }
+ data.data = t->bt_rdata.data;
+ data.size = t->bt_reclen;
+
+ for (nrec = t->bt_nrecs; nrec < top;) {
+ len = t->bt_reclen;
+ for (p = t->bt_rdata.data;; *p++ = ch)
+ if ((ch = getc(t->bt_rfp)) == EOF || !--len) {
+ if (ch != EOF)
+ *p = ch;
+ if (len != 0)
+ memset(p, t->bt_bval, len);
+ if (__rec_iput(t,
+ nrec, &data, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ ++nrec;
+ break;
+ }
+ if (ch == EOF)
+ break;
+ }
+ if (nrec < top) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_VPIPE -- Get variable length records from a pipe.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_vpipe(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ recno_t nrec;
+ indx_t len;
+ size_t sz;
+ int bval, ch;
+ u_char *p;
+
+ bval = t->bt_bval;
+ for (nrec = t->bt_nrecs; nrec < top; ++nrec) {
+ for (p = t->bt_rdata.data,
+ sz = t->bt_rdata.size;; *p++ = ch, --sz) {
+ if ((ch = getc(t->bt_rfp)) == EOF || ch == bval) {
+ data.data = t->bt_rdata.data;
+ data.size = p - (u_char *)t->bt_rdata.data;
+ if (ch == EOF && data.size == 0)
+ break;
+ if (__rec_iput(t, nrec, &data, 0)
+ != RET_SUCCESS)
+ return (RET_ERROR);
+ break;
+ }
+ if (sz == 0) {
+ len = p - (u_char *)t->bt_rdata.data;
+ t->bt_rdata.size += (sz = 256);
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_rdata.size) :
+ realloc(t->bt_rdata.data, t->bt_rdata.size);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ p = (u_char *)t->bt_rdata.data + len;
+ }
+ }
+ if (ch == EOF)
+ break;
+ }
+ if (nrec < top) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_FMAP -- Get fixed length records from a file.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_fmap(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ recno_t nrec;
+ u_char *sp, *ep, *p;
+ size_t len;
+
+ if (t->bt_rdata.size < t->bt_reclen) {
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_reclen) :
+ realloc(t->bt_rdata.data, t->bt_reclen);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.size = t->bt_reclen;
+ }
+ data.data = t->bt_rdata.data;
+ data.size = t->bt_reclen;
+
+ sp = (u_char *)t->bt_cmap;
+ ep = (u_char *)t->bt_emap;
+ for (nrec = t->bt_nrecs; nrec < top; ++nrec) {
+ if (sp >= ep) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ len = t->bt_reclen;
+ for (p = t->bt_rdata.data;
+ sp < ep && len > 0; *p++ = *sp++, --len);
+ if (len != 0)
+ memset(p, t->bt_bval, len);
+ if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ }
+ t->bt_cmap = (caddr_t)sp;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_VMAP -- Get variable length records from a file.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_vmap(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ u_char *sp, *ep;
+ recno_t nrec;
+ int bval;
+
+ sp = (u_char *)t->bt_cmap;
+ ep = (u_char *)t->bt_emap;
+ bval = t->bt_bval;
+
+ for (nrec = t->bt_nrecs; nrec < top; ++nrec) {
+ if (sp >= ep) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ for (data.data = sp; sp < ep && *sp != bval; ++sp);
+ data.size = sp - (u_char *)data.data;
+ if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ ++sp;
+ }
+ t->bt_cmap = (caddr_t)sp;
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_open.c b/trunk/main/db1-ast/recno/rec_open.c
new file mode 100644
index 000000000..0ebc8c7c4
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_open.c
@@ -0,0 +1,241 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_open.c 8.10 (Berkeley) 9/1/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+DB *
+__rec_open(fname, flags, mode, openinfo, dflags)
+ const char *fname;
+ int flags, mode, dflags;
+ const RECNOINFO *openinfo;
+{
+ BTREE *t;
+ BTREEINFO btopeninfo;
+ DB *dbp;
+ PAGE *h;
+ struct stat sb;
+ int rfd = 0, sverrno;
+
+ /* Open the user's file -- if this fails, we're done. */
+ if (fname != NULL && (rfd = open(fname, flags, mode)) < 0)
+ return (NULL);
+
+ /* Create a btree in memory (backed by disk). */
+ dbp = NULL;
+ if (openinfo) {
+ if (openinfo->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT))
+ goto einval;
+ btopeninfo.flags = 0;
+ btopeninfo.cachesize = openinfo->cachesize;
+ btopeninfo.maxkeypage = 0;
+ btopeninfo.minkeypage = 0;
+ btopeninfo.psize = openinfo->psize;
+ btopeninfo.compare = NULL;
+ btopeninfo.prefix = NULL;
+ btopeninfo.lorder = openinfo->lorder;
+ dbp = __bt_open(openinfo->bfname,
+ O_RDWR, S_IRUSR | S_IWUSR, &btopeninfo, dflags);
+ } else
+ dbp = __bt_open(NULL, O_RDWR, S_IRUSR | S_IWUSR, NULL, dflags);
+ if (dbp == NULL)
+ goto err;
+
+ /*
+ * Some fields in the tree structure are recno specific. Fill them
+ * in and make the btree structure look like a recno structure. We
+ * don't change the bt_ovflsize value, it's close enough and slightly
+ * bigger.
+ */
+ t = dbp->internal;
+ if (openinfo) {
+ if (openinfo->flags & R_FIXEDLEN) {
+ F_SET(t, R_FIXLEN);
+ t->bt_reclen = openinfo->reclen;
+ if (t->bt_reclen == 0)
+ goto einval;
+ }
+ t->bt_bval = openinfo->bval;
+ } else
+ t->bt_bval = '\n';
+
+ F_SET(t, R_RECNO);
+ if (fname == NULL)
+ F_SET(t, R_EOF | R_INMEM);
+ else
+ t->bt_rfd = rfd;
+
+ if (fname != NULL) {
+ /*
+ * In 4.4BSD, stat(2) returns true for ISSOCK on pipes.
+ * Unfortunately, that's not portable, so we use lseek
+ * and check the errno values.
+ */
+ errno = 0;
+ if (lseek(rfd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) {
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ F_SET(t, R_RDONLY);
+ break;
+ default:
+ goto einval;
+ }
+slow: if ((t->bt_rfp = fdopen(rfd, "r")) == NULL)
+ goto err;
+ F_SET(t, R_CLOSEFP);
+ t->bt_irec =
+ F_ISSET(t, R_FIXLEN) ? __rec_fpipe : __rec_vpipe;
+ } else {
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ F_SET(t, R_RDONLY);
+ break;
+ case O_RDWR:
+ break;
+ default:
+ goto einval;
+ }
+
+ if (fstat(rfd, &sb))
+ goto err;
+ /*
+ * Kluge -- we'd like to test to see if the file is too
+ * big to mmap. Since, we don't know what size or type
+ * off_t's or size_t's are, what the largest unsigned
+ * integral type is, or what random insanity the local
+ * C compiler will perpetrate, doing the comparison in
+ * a portable way is flatly impossible. Hope that mmap
+ * fails if the file is too large.
+ */
+ if (sb.st_size == 0)
+ F_SET(t, R_EOF);
+ else {
+#ifdef MMAP_NOT_AVAILABLE
+ /*
+ * XXX
+ * Mmap doesn't work correctly on many current
+ * systems. In particular, it can fail subtly,
+ * with cache coherency problems. Don't use it
+ * for now.
+ */
+ t->bt_msize = sb.st_size;
+ if ((t->bt_smap = mmap(NULL, t->bt_msize,
+ PROT_READ, MAP_PRIVATE, rfd,
+ (off_t)0)) == (caddr_t)-1)
+ goto slow;
+ t->bt_cmap = t->bt_smap;
+ t->bt_emap = t->bt_smap + sb.st_size;
+ t->bt_irec = F_ISSET(t, R_FIXLEN) ?
+ __rec_fmap : __rec_vmap;
+ F_SET(t, R_MEMMAPPED);
+#else
+ goto slow;
+#endif
+ }
+ }
+ }
+
+ /* Use the recno routines. */
+ dbp->close = __rec_close;
+ dbp->del = __rec_delete;
+ dbp->fd = __rec_fd;
+ dbp->get = __rec_get;
+ dbp->put = __rec_put;
+ dbp->seq = __rec_seq;
+ dbp->sync = __rec_sync;
+
+ /* If the root page was created, reset the flags. */
+ if ((h = mpool_get(t->bt_mp, P_ROOT, 0)) == NULL)
+ goto err;
+ if ((h->flags & P_TYPE) == P_BLEAF) {
+ F_CLR(h, P_TYPE);
+ F_SET(h, P_RLEAF);
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ } else
+ mpool_put(t->bt_mp, h, 0);
+
+ if (openinfo && openinfo->flags & R_SNAPSHOT &&
+ !F_ISSET(t, R_EOF | R_INMEM) &&
+ t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
+ goto err;
+ return (dbp);
+
+einval: errno = EINVAL;
+err: sverrno = errno;
+ if (dbp != NULL)
+ (void)__bt_close(dbp);
+ if (fname != NULL)
+ (void)close(rfd);
+ errno = sverrno;
+ return (NULL);
+}
+
+int
+__rec_fd(dbp)
+ const DB *dbp;
+{
+ BTREE *t;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* In-memory database can't have a file descriptor. */
+ if (F_ISSET(t, R_INMEM)) {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (t->bt_rfd);
+}
diff --git a/trunk/main/db1-ast/recno/rec_put.c b/trunk/main/db1-ast/recno/rec_put.c
new file mode 100644
index 000000000..331699867
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_put.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_put.c 8.7 (Berkeley) 8/18/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_PUT -- Add a recno item to the tree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key
+ * data: data
+ * flag: R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is
+ * already in the tree and R_NOOVERWRITE specified.
+ */
+int
+__rec_put(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key;
+ const DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ DBT fdata, tdata;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /*
+ * If using fixed-length records, and the record is long, return
+ * EINVAL. If it's short, pad it out. Use the record data return
+ * memory, it's only short-term.
+ */
+ if (F_ISSET(t, R_FIXLEN) && data->size != t->bt_reclen) {
+ if (data->size > t->bt_reclen)
+ goto einval;
+
+ if (t->bt_rdata.size < t->bt_reclen) {
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_reclen) :
+ realloc(t->bt_rdata.data, t->bt_reclen);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.size = t->bt_reclen;
+ }
+ memmove(t->bt_rdata.data, data->data, data->size);
+ memset((char *)t->bt_rdata.data + data->size,
+ t->bt_bval, t->bt_reclen - data->size);
+ fdata.data = t->bt_rdata.data;
+ fdata.size = t->bt_reclen;
+ } else {
+ fdata.data = data->data;
+ fdata.size = data->size;
+ }
+
+ switch (flags) {
+ case R_CURSOR:
+ if (!F_ISSET(&t->bt_cursor, CURS_INIT))
+ goto einval;
+ nrec = t->bt_cursor.rcursor;
+ break;
+ case R_SETCURSOR:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ break;
+ case R_IAFTER:
+ if ((nrec = *(recno_t *)key->data) == 0) {
+ nrec = 1;
+ flags = R_IBEFORE;
+ }
+ break;
+ case 0:
+ case R_IBEFORE:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ break;
+ case R_NOOVERWRITE:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ if (nrec <= t->bt_nrecs)
+ return (RET_SPECIAL);
+ break;
+ default:
+einval: errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ /*
+ * Make sure that records up to and including the put record are
+ * already in the database. If skipping records, create empty ones.
+ */
+ if (nrec > t->bt_nrecs) {
+ if (!F_ISSET(t, R_EOF | R_INMEM) &&
+ t->bt_irec(t, nrec) == RET_ERROR)
+ return (RET_ERROR);
+ if (nrec > t->bt_nrecs + 1) {
+ if (F_ISSET(t, R_FIXLEN)) {
+ if ((tdata.data =
+ (void *)malloc(t->bt_reclen)) == NULL)
+ return (RET_ERROR);
+ tdata.size = t->bt_reclen;
+ memset(tdata.data, t->bt_bval, tdata.size);
+ } else {
+ tdata.data = NULL;
+ tdata.size = 0;
+ }
+ while (nrec > t->bt_nrecs + 1)
+ if (__rec_iput(t,
+ t->bt_nrecs, &tdata, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ if (F_ISSET(t, R_FIXLEN))
+ free(tdata.data);
+ }
+ }
+
+ if ((status = __rec_iput(t, nrec - 1, &fdata, flags)) != RET_SUCCESS)
+ return (status);
+
+ if (flags == R_SETCURSOR)
+ t->bt_cursor.rcursor = nrec;
+
+ F_SET(t, R_MODIFIED);
+ return (__rec_ret(t, NULL, nrec, key, NULL));
+}
+
+/*
+ * __REC_IPUT -- Add a recno item to the tree.
+ *
+ * Parameters:
+ * t: tree
+ * nrec: record number
+ * data: data
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_iput(t, nrec, data, flags)
+ BTREE *t;
+ recno_t nrec;
+ const DBT *data;
+ u_int flags;
+{
+ DBT tdata;
+ EPG *e;
+ PAGE *h;
+ indx_t index, nxtindex;
+ pgno_t pg;
+ u_int32_t nbytes;
+ int dflags, status;
+ char *dest, db[NOVFLSIZE];
+
+ /*
+ * If the data won't fit on a page, store it on indirect pages.
+ *
+ * XXX
+ * If the insert fails later on, these pages aren't recovered.
+ */
+ if (data->size > t->bt_ovflsize) {
+ if (__ovfl_put(t, data, &pg) == RET_ERROR)
+ return (RET_ERROR);
+ tdata.data = db;
+ tdata.size = NOVFLSIZE;
+ *(pgno_t *)db = pg;
+ *(u_int32_t *)(db + sizeof(pgno_t)) = data->size;
+ dflags = P_BIGDATA;
+ data = &tdata;
+ } else
+ dflags = 0;
+
+ /* __rec_search pins the returned page. */
+ if ((e = __rec_search(t, nrec,
+ nrec > t->bt_nrecs || flags == R_IAFTER || flags == R_IBEFORE ?
+ SINSERT : SEARCH)) == NULL)
+ return (RET_ERROR);
+
+ h = e->page;
+ index = e->index;
+
+ /*
+ * Add the specified key/data pair to the tree. The R_IAFTER and
+ * R_IBEFORE flags insert the key after/before the specified key.
+ *
+ * Pages are split as required.
+ */
+ switch (flags) {
+ case R_IAFTER:
+ ++index;
+ break;
+ case R_IBEFORE:
+ break;
+ default:
+ if (nrec < t->bt_nrecs &&
+ __rec_dleaf(t, h, index) == RET_ERROR) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ break;
+ }
+
+ /*
+ * If not enough room, split the page. The split code will insert
+ * the key and data and unpin the current page. If inserting into
+ * the offset array, shift the pointers up.
+ */
+ nbytes = NRLEAFDBT(data->size);
+ if ((u_int32_t) (h->upper - h->lower) < nbytes + sizeof(indx_t)) {
+ status = __bt_split(t, h, NULL, data, dflags, nbytes, index);
+ if (status == RET_SUCCESS)
+ ++t->bt_nrecs;
+ return (status);
+ }
+
+ if (index < (nxtindex = NEXTINDEX(h)))
+ memmove(h->linp + index + 1, h->linp + index,
+ (nxtindex - index) * sizeof(indx_t));
+ h->lower += sizeof(indx_t);
+
+ h->linp[index] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ WR_RLEAF(dest, data, dflags);
+
+ ++t->bt_nrecs;
+ F_SET(t, B_MODIFIED);
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_search.c b/trunk/main/db1-ast/recno/rec_search.c
new file mode 100644
index 000000000..e70fe4c13
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_search.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_search.c 8.4 (Berkeley) 7/14/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_SEARCH -- Search a btree for a key.
+ *
+ * Parameters:
+ * t: tree to search
+ * recno: key to find
+ * op: search operation
+ *
+ * Returns:
+ * EPG for matching record, if any, or the EPG for the location of the
+ * key, if it were inserted into the tree.
+ *
+ * Returns:
+ * The EPG for matching record, if any, or the EPG for the location
+ * of the key, if it were inserted into the tree, is entered into
+ * the bt_cur field of the tree. A pointer to the field is returned.
+ */
+EPG *
+__rec_search(t, recno, op)
+ BTREE *t;
+ recno_t recno;
+ enum SRCHOP op;
+{
+ register indx_t index;
+ register PAGE *h;
+ EPGNO *parent;
+ RINTERNAL *r;
+ pgno_t pg;
+ indx_t top;
+ recno_t total;
+ int sverrno;
+
+ BT_CLR(t);
+ for (pg = P_ROOT, total = 0;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ goto err;
+ if (h->flags & P_RLEAF) {
+ t->bt_cur.page = h;
+ t->bt_cur.index = recno - total;
+ return (&t->bt_cur);
+ }
+ for (index = 0, top = NEXTINDEX(h);;) {
+ r = GETRINTERNAL(h, index);
+ if (++index == top || total + r->nrecs > recno)
+ break;
+ total += r->nrecs;
+ }
+
+ BT_PUSH(t, pg, index - 1);
+
+ pg = r->pgno;
+ switch (op) {
+ case SDELETE:
+ --GETRINTERNAL(h, (index - 1))->nrecs;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ case SINSERT:
+ ++GETRINTERNAL(h, (index - 1))->nrecs;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ case SEARCH:
+ mpool_put(t->bt_mp, h, 0);
+ break;
+ }
+
+ }
+ /* Try and recover the tree. */
+err: sverrno = errno;
+ if (op != SEARCH)
+ while ((parent = BT_POP(t)) != NULL) {
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ break;
+ if (op == SINSERT)
+ --GETRINTERNAL(h, parent->index)->nrecs;
+ else
+ ++GETRINTERNAL(h, parent->index)->nrecs;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ }
+ errno = sverrno;
+ return (NULL);
+}
diff --git a/trunk/main/db1-ast/recno/rec_seq.c b/trunk/main/db1-ast/recno/rec_seq.c
new file mode 100644
index 000000000..ca3451ca6
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_seq.c
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_seq.c 8.3 (Berkeley) 7/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_SEQ -- Recno sequential scan interface.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key for positioning and return value
+ * data: data return value
+ * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+int
+__rec_seq(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key, *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG *e;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ switch(flags) {
+ case R_CURSOR:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ break;
+ case R_NEXT:
+ if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
+ nrec = t->bt_cursor.rcursor + 1;
+ break;
+ }
+ /* FALLTHROUGH */
+ case R_FIRST:
+ nrec = 1;
+ break;
+ case R_PREV:
+ if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
+ if ((nrec = t->bt_cursor.rcursor - 1) == 0)
+ return (RET_SPECIAL);
+ break;
+ }
+ /* FALLTHROUGH */
+ case R_LAST:
+ if (!F_ISSET(t, R_EOF | R_INMEM) &&
+ t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
+ return (RET_ERROR);
+ nrec = t->bt_nrecs;
+ break;
+ default:
+einval: errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) {
+ if (!F_ISSET(t, R_EOF | R_INMEM) &&
+ (status = t->bt_irec(t, nrec)) != RET_SUCCESS)
+ return (status);
+ if (t->bt_nrecs == 0 || nrec > t->bt_nrecs)
+ return (RET_SPECIAL);
+ }
+
+ if ((e = __rec_search(t, nrec - 1, SEARCH)) == NULL)
+ return (RET_ERROR);
+
+ F_SET(&t->bt_cursor, CURS_INIT);
+ t->bt_cursor.rcursor = nrec;
+
+ status = __rec_ret(t, e, nrec, key, data);
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e->page, 0);
+ else
+ t->bt_pinned = e->page;
+ return (status);
+}
diff --git a/trunk/main/db1-ast/recno/rec_utils.c b/trunk/main/db1-ast/recno/rec_utils.c
new file mode 100644
index 000000000..ddc309712
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_utils.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_utils.c 8.6 (Berkeley) 7/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __rec_ret --
+ * Build return data.
+ *
+ * Parameters:
+ * t: tree
+ * e: key/data pair to be returned
+ * nrec: record number
+ * key: user's key structure
+ * data: user's data structure
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__rec_ret(t, e, nrec, key, data)
+ BTREE *t;
+ EPG *e;
+ recno_t nrec;
+ DBT *key, *data;
+{
+ RLEAF *rl;
+ void *p;
+
+ if (key == NULL)
+ goto dataonly;
+
+ /* We have to copy the key, it's not on the page. */
+ if (sizeof(recno_t) > t->bt_rkey.size) {
+ p = (void *)(t->bt_rkey.data == NULL ?
+ malloc(sizeof(recno_t)) :
+ realloc(t->bt_rkey.data, sizeof(recno_t)));
+ if (p == NULL)
+ return (RET_ERROR);
+ t->bt_rkey.data = p;
+ t->bt_rkey.size = sizeof(recno_t);
+ }
+ memmove(t->bt_rkey.data, &nrec, sizeof(recno_t));
+ key->size = sizeof(recno_t);
+ key->data = t->bt_rkey.data;
+
+dataonly:
+ if (data == NULL)
+ return (RET_SUCCESS);
+
+ /*
+ * We must copy big keys/data to make them contiguous. Otherwise,
+ * leave the page pinned and don't copy unless the user specified
+ * concurrent access.
+ */
+ rl = GETRLEAF(e->page, e->index);
+ if (rl->flags & P_BIGDATA) {
+ if (__ovfl_get(t, rl->bytes,
+ &data->size, &t->bt_rdata.data, &t->bt_rdata.size))
+ return (RET_ERROR);
+ data->data = t->bt_rdata.data;
+ } else if (F_ISSET(t, B_DB_LOCK)) {
+ /* Use +1 in case the first record retrieved is 0 length. */
+ if (rl->dsize + 1 > t->bt_rdata.size) {
+ p = (void *)(t->bt_rdata.data == NULL ?
+ malloc(rl->dsize + 1) :
+ realloc(t->bt_rdata.data, rl->dsize + 1));
+ if (p == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.data = p;
+ t->bt_rdata.size = rl->dsize + 1;
+ }
+ memmove(t->bt_rdata.data, rl->bytes, rl->dsize);
+ data->size = rl->dsize;
+ data->data = t->bt_rdata.data;
+ } else {
+ data->size = rl->dsize;
+ data->data = rl->bytes;
+ }
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/recno.h b/trunk/main/db1-ast/recno/recno.h
new file mode 100644
index 000000000..bec772c2f
--- /dev/null
+++ b/trunk/main/db1-ast/recno/recno.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)recno.h 8.1 (Berkeley) 6/4/93
+ */
+
+enum SRCHOP { SDELETE, SINSERT, SEARCH}; /* Rec_search operation. */
+
+#include "../btree/btree.h"
+#include "extern.h"
diff --git a/trunk/main/devicestate.c b/trunk/main/devicestate.c
new file mode 100644
index 000000000..bd9261306
--- /dev/null
+++ b/trunk/main/devicestate.c
@@ -0,0 +1,553 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, 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 Device state management
+ *
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg \ref AstExtState
+ */
+
+/*! \page AstExtState Extension and device states in Asterisk
+ *
+ * Asterisk has an internal system that reports states
+ * for an extension. By using the dialplan priority -1,
+ * also called a \b hint, a connection can be made from an
+ * extension to one or many devices. The state of the extension
+ * now depends on the combined state of the devices.
+ *
+ * The device state is basically based on the current calls.
+ * If the devicestate engine can find a call from or to the
+ * device, it's in use.
+ *
+ * Some channel drivers implement a callback function for
+ * a better level of reporting device states. The SIP channel
+ * has a complicated system for this, which is improved
+ * by adding call limits to the configuration.
+ *
+ * Functions that want to check the status of an extension
+ * register themself as a \b watcher.
+ * Watchers in this system can subscribe either to all extensions
+ * or just a specific extensions.
+ *
+ * For non-device related states, there's an API called
+ * devicestate providers. This is an extendible system for
+ * delivering state information from outside sources or
+ * functions within Asterisk. Currently we have providers
+ * for app_meetme.c - the conference bridge - and call
+ * parking (metermaids).
+ *
+ * There are manly three subscribers to extension states
+ * within Asterisk:
+ * - AMI, the manager interface
+ * - app_queue.c - the Queue dialplan application
+ * - SIP subscriptions, a.k.a. "blinking lamps" or
+ * "buddy lists"
+ *
+ * The CLI command "show hints" show last known state
+ *
+ * \note None of these handle user states, like an IM presence
+ * system. res_jabber.c can subscribe and watch such states
+ * in jabber/xmpp based systems.
+ *
+ * \section AstDevStateArch Architecture for devicestates
+ *
+ * When a channel driver or asterisk app changes state for
+ * a watched object, it alerts the core. The core queues
+ * a change. When the change is processed, there's a query
+ * sent to the channel driver/provider if there's a function
+ * to handle that, otherwise a channel walk is issued to find
+ * a channel that involves the object.
+ *
+ * The changes are queued and processed by a separate thread.
+ * This thread calls the watchers subscribing to status
+ * changes for the object. For manager, this results
+ * in events. For SIP, NOTIFY requests.
+ *
+ * - Device states
+ * \arg \ref devicestate.c
+ * \arg \ref devicestate.h
+ *
+ * \section AstExtStateArch Architecture for extension states
+ *
+ * Hints are connected to extension. If an extension changes state
+ * it checks the hint devices. If there is a hint, the callbacks into
+ * device states are checked. The aggregated state is set for the hint
+ * and reported back.
+ *
+ * - Extension states
+ * \arg \ref AstENUM ast_extension_states
+ * \arg \ref pbx.c
+ * \arg \ref pbx.h
+ * - Structures
+ * - \ref ast_state_cb struct. Callbacks for watchers
+ * - Callback ast_state_cb_type
+ * - \ref ast_hint struct.
+ * - Functions
+ * - ast_extension_state_add()
+ * - ast_extension_state_del()
+ * - ast_get_hint()
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/event.h"
+
+/*! \brief Device state strings for printing */
+static const char *devstatestring[] = {
+ /* 0 AST_DEVICE_UNKNOWN */ "Unknown", /*!< Valid, but unknown state */
+ /* 1 AST_DEVICE_NOT_INUSE */ "Not in use", /*!< Not used */
+ /* 2 AST_DEVICE IN USE */ "In use", /*!< In use */
+ /* 3 AST_DEVICE_BUSY */ "Busy", /*!< Busy */
+ /* 4 AST_DEVICE_INVALID */ "Invalid", /*!< Invalid - not known to Asterisk */
+ /* 5 AST_DEVICE_UNAVAILABLE */ "Unavailable", /*!< Unavailable (not registred) */
+ /* 6 AST_DEVICE_RINGING */ "Ringing", /*!< Ring, ring, ring */
+ /* 7 AST_DEVICE_RINGINUSE */ "Ring+Inuse", /*!< Ring and in use */
+ /* 8 AST_DEVICE_ONHOLD */ "On Hold" /*!< On Hold */
+};
+
+/*! \brief A device state provider (not a channel) */
+struct devstate_prov {
+ char label[40];
+ ast_devstate_prov_cb_type callback;
+ AST_RWLIST_ENTRY(devstate_prov) list;
+};
+
+/*! \brief A list of providers */
+static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
+
+struct state_change {
+ AST_LIST_ENTRY(state_change) list;
+ char device[1];
+};
+
+/*! \brief The state change queue. State changes are queued
+ for processing by a separate thread */
+static AST_LIST_HEAD_STATIC(state_changes, state_change);
+
+/*! \brief The device state change notification thread */
+static pthread_t change_thread = AST_PTHREADT_NULL;
+
+/*! \brief Flag for the queue */
+static ast_cond_t change_pending;
+
+/*! \brief Whether or not to cache this device state value */
+enum devstate_cache {
+ /*! Cache this value as it is coming from a device state provider which is
+ * pushing up state change events to us as they happen */
+ CACHE_ON,
+ /*! Don't cache this result, since it was pulled from the device state provider.
+ * We only want to cache results from device state providers that are being nice
+ * and pushing state change events up to us as they happen. */
+ CACHE_OFF,
+};
+
+/* Forward declarations */
+static int getproviderstate(const char *provider, const char *address);
+
+/*! \brief Find devicestate as text message for output */
+const char *devstate2str(enum ast_device_state devstate)
+{
+ return devstatestring[devstate];
+}
+
+const char *ast_devstate_str(enum ast_device_state state)
+{
+ const char *res = "UNKNOWN";
+
+ switch (state) {
+ case AST_DEVICE_UNKNOWN:
+ break;
+ case AST_DEVICE_NOT_INUSE:
+ res = "NOT_INUSE";
+ break;
+ case AST_DEVICE_INUSE:
+ res = "INUSE";
+ break;
+ case AST_DEVICE_BUSY:
+ res = "BUSY";
+ break;
+ case AST_DEVICE_INVALID:
+ res = "INVALID";
+ break;
+ case AST_DEVICE_UNAVAILABLE:
+ res = "UNAVAILABLE";
+ break;
+ case AST_DEVICE_RINGING:
+ res = "RINGING";
+ break;
+ case AST_DEVICE_RINGINUSE:
+ res = "RINGINUSE";
+ break;
+ case AST_DEVICE_ONHOLD:
+ res = "ONHOLD";
+ break;
+ }
+
+ return res;
+}
+
+enum ast_device_state ast_devstate_val(const char *val)
+{
+ if (!strcasecmp(val, "NOT_INUSE"))
+ return AST_DEVICE_NOT_INUSE;
+ else if (!strcasecmp(val, "INUSE"))
+ return AST_DEVICE_INUSE;
+ else if (!strcasecmp(val, "BUSY"))
+ return AST_DEVICE_BUSY;
+ else if (!strcasecmp(val, "INVALID"))
+ return AST_DEVICE_INVALID;
+ else if (!strcasecmp(val, "UNAVAILABLE"))
+ return AST_DEVICE_UNAVAILABLE;
+ else if (!strcasecmp(val, "RINGING"))
+ return AST_DEVICE_RINGING;
+ else if (!strcasecmp(val, "RINGINUSE"))
+ return AST_DEVICE_RINGINUSE;
+ else if (!strcasecmp(val, "ONHOLD"))
+ return AST_DEVICE_ONHOLD;
+
+ return AST_DEVICE_UNKNOWN;
+}
+
+/*! \brief Find out if device is active in a call or not
+ \note find channels with the device's name in it
+ This function is only used for channels that does not implement
+ devicestate natively
+*/
+enum ast_device_state ast_parse_device_state(const char *device)
+{
+ struct ast_channel *chan;
+ char match[AST_CHANNEL_NAME];
+ enum ast_device_state res;
+
+ ast_copy_string(match, device, sizeof(match)-1);
+ strcat(match, "-");
+ chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
+
+ if (!chan)
+ return AST_DEVICE_UNKNOWN;
+
+ if (chan->_state == AST_STATE_RINGING)
+ res = AST_DEVICE_RINGING;
+ else
+ res = AST_DEVICE_INUSE;
+
+ ast_channel_unlock(chan);
+
+ return res;
+}
+
+static enum ast_device_state devstate_cached(const char *device)
+{
+ enum ast_device_state res = AST_DEVICE_UNKNOWN;
+ struct ast_event *event;
+
+ event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+ AST_EVENT_IE_END);
+
+ if (!event)
+ return res;
+
+ res = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+
+ ast_event_destroy(event);
+
+ return res;
+}
+
+/*! \brief Check device state through channel specific function or generic function */
+enum ast_device_state ast_device_state(const char *device)
+{
+ char *buf;
+ char *number;
+ const struct ast_channel_tech *chan_tech;
+ enum ast_device_state res;
+ /*! \brief Channel driver that provides device state */
+ char *tech;
+ /*! \brief Another provider of device state */
+ char *provider = NULL;
+
+ /* If the last known state is cached, just return that */
+ res = devstate_cached(device);
+ if (res != AST_DEVICE_UNKNOWN)
+ return res;
+
+ buf = ast_strdupa(device);
+ tech = strsep(&buf, "/");
+ if (!(number = buf)) {
+ if (!(provider = strsep(&tech, ":")))
+ return AST_DEVICE_INVALID;
+ /* We have a provider */
+ number = tech;
+ tech = NULL;
+ }
+
+ if (provider) {
+ ast_debug(3, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
+ return getproviderstate(provider, number);
+ }
+
+ ast_debug(4, "No provider found, checking channel drivers for %s - %s\n", tech, number);
+
+ if (!(chan_tech = ast_get_channel_tech(tech)))
+ return AST_DEVICE_INVALID;
+
+ if (!(chan_tech->devicestate)) /* Does the channel driver support device state notification? */
+ return ast_parse_device_state(device); /* No, try the generic function */
+
+ res = chan_tech->devicestate(number);
+
+ if (res != AST_DEVICE_UNKNOWN)
+ return res;
+
+ res = ast_parse_device_state(device);
+
+ if (res == AST_DEVICE_UNKNOWN)
+ return AST_DEVICE_NOT_INUSE;
+
+ return res;
+}
+
+/*! \brief Add device state provider */
+int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
+{
+ struct devstate_prov *devprov;
+
+ if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
+ return -1;
+
+ devprov->callback = callback;
+ ast_copy_string(devprov->label, label, sizeof(devprov->label));
+
+ AST_RWLIST_WRLOCK(&devstate_provs);
+ AST_RWLIST_INSERT_HEAD(&devstate_provs, devprov, list);
+ AST_RWLIST_UNLOCK(&devstate_provs);
+
+ return 0;
+}
+
+/*! \brief Remove device state provider */
+int ast_devstate_prov_del(const char *label)
+{
+ struct devstate_prov *devcb;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&devstate_provs);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
+ if (!strcasecmp(devcb->label, label)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(devcb);
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&devstate_provs);
+
+ return res;
+}
+
+/*! \brief Get provider device state */
+static int getproviderstate(const char *provider, const char *address)
+{
+ struct devstate_prov *devprov;
+ int res = AST_DEVICE_INVALID;
+
+
+ AST_RWLIST_RDLOCK(&devstate_provs);
+ AST_RWLIST_TRAVERSE(&devstate_provs, devprov, list) {
+ ast_debug(5, "Checking provider %s with %s\n", devprov->label, provider);
+
+ if (!strcasecmp(devprov->label, provider)) {
+ res = devprov->callback(address);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&devstate_provs);
+ return res;
+}
+
+static void devstate_event(const char *device, enum ast_device_state state, enum devstate_cache cache)
+{
+ struct ast_event *event;
+
+ if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+ AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
+ AST_EVENT_IE_END))) {
+ return;
+ }
+
+ if (cache == CACHE_ON) {
+ /* Cache this event, replacing an event in the cache with the same
+ * device name if it exists. */
+ ast_event_queue_and_cache(event,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
+ AST_EVENT_IE_END);
+ } else {
+ ast_event_queue(event);
+ }
+}
+
+/*! Called by the state change thread to find out what the state is, and then
+ * to queue up the state change event */
+static void do_state_change(const char *device)
+{
+ enum ast_device_state state;
+
+ state = ast_device_state(device);
+
+ ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
+
+ devstate_event(device, state, CACHE_OFF);
+}
+
+static int __ast_devstate_changed_literal(enum ast_device_state state, char *buf, int norecurse)
+{
+ char *device;
+ struct state_change *change;
+ char *tmp = NULL;
+
+ ast_debug(3, "Notification of state change to be queued on device/channel %s\n", buf);
+
+ device = buf;
+
+ if (state != AST_DEVICE_UNKNOWN) {
+ devstate_event(device, state, CACHE_ON);
+ } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
+ /* we could not allocate a change struct, or */
+ /* there is no background thread, so process the change now */
+ do_state_change(device);
+ } else {
+ /* queue the change */
+ strcpy(change->device, device);
+ AST_LIST_LOCK(&state_changes);
+ AST_LIST_INSERT_TAIL(&state_changes, change, list);
+ ast_cond_signal(&change_pending);
+ AST_LIST_UNLOCK(&state_changes);
+ }
+
+ /* The problem with this API is that a device may be called with the unique
+ * identifier appended or not, but it's separated from the channel name
+ * with a '-', which is also a legitimate character in a channel name. So,
+ * we have to force both names to get their names checked for state changes
+ * to ensure that the right one gets notified. Not a huge performance hit,
+ * but it might could be fixed by an enterprising programmer in trunk.
+ */
+ if (!norecurse && (tmp = strrchr(device, '-'))) {
+ *tmp = '\0';
+ __ast_devstate_changed_literal(state, device, 1);
+ }
+
+ return 1;
+}
+
+int ast_devstate_changed_literal(enum ast_device_state state, const char *dev)
+{
+ char *buf;
+
+ buf = ast_strdupa(dev);
+
+ return __ast_devstate_changed_literal(state, buf, 0);
+}
+
+int ast_device_state_changed_literal(const char *dev)
+{
+ char *buf;
+
+ buf = ast_strdupa(dev);
+
+ return __ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf, 0);
+}
+
+int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
+{
+ char buf[AST_MAX_EXTENSION];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ return __ast_devstate_changed_literal(state, buf, 0);
+}
+
+/*! \brief Accept change notification, add it to change queue */
+int ast_device_state_changed(const char *fmt, ...)
+{
+ char buf[AST_MAX_EXTENSION];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ return __ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf, 0);
+}
+
+/*! \brief Go through the dev state change queue and update changes in the dev state thread */
+static void *do_devstate_changes(void *data)
+{
+ struct state_change *next, *current;
+
+ for (;;) {
+ /* This basically pops off any state change entries, resets the list back to NULL, unlocks, and processes each state change */
+ AST_LIST_LOCK(&state_changes);
+ if (AST_LIST_EMPTY(&state_changes))
+ ast_cond_wait(&change_pending, &state_changes.lock);
+ next = AST_LIST_FIRST(&state_changes);
+ AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
+ AST_LIST_UNLOCK(&state_changes);
+
+ /* Process each state change */
+ while ((current = next)) {
+ next = AST_LIST_NEXT(current, list);
+ do_state_change(current->device);
+ ast_free(current);
+ }
+ }
+
+ return NULL;
+}
+
+/*! \brief Initialize the device state engine in separate thread */
+int ast_device_state_engine_init(void)
+{
+ ast_cond_init(&change_pending, NULL);
+ if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/trunk/main/dial.c b/trunk/main/dial.c
new file mode 100644
index 000000000..7b6ce871e
--- /dev/null
+++ b/trunk/main/dial.c
@@ -0,0 +1,1014 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Dialing API
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/dial.h"
+#include "asterisk/pbx.h"
+#include "asterisk/musiconhold.h"
+
+/*! \brief Main dialing structure. Contains global options, channels being dialed, and more! */
+struct ast_dial {
+ int num; /*!< Current number to give to next dialed channel */
+ int timeout; /*!< Maximum time allowed for dial attempts */
+ int actual_timeout; /*!< Actual timeout based on all factors (ie: channels) */
+ enum ast_dial_result state; /*!< Status of dial */
+ void *options[AST_DIAL_OPTION_MAX]; /*!< Global options */
+ ast_dial_state_callback state_callback; /*!< Status callback */
+ AST_LIST_HEAD_NOLOCK(, ast_dial_channel) channels; /*!< Channels being dialed */
+ pthread_t thread; /*!< Thread (if running in async) */
+ ast_mutex_t lock; /*! Lock to protect the thread information above */
+};
+
+/*! \brief Dialing channel structure. Contains per-channel dialing options, asterisk channel, and more! */
+struct ast_dial_channel {
+ int num; /*!< Unique number for dialed channel */
+ int timeout; /*!< Maximum time allowed for attempt */
+ char *tech; /*!< Technology being dialed */
+ char *device; /*!< Device being dialed */
+ void *options[AST_DIAL_OPTION_MAX]; /*!< Channel specific options */
+ int cause; /*!< Cause code in case of failure */
+ int is_running_app:1; /*!< Is this running an application? */
+ struct ast_channel *owner; /*!< Asterisk channel */
+ AST_LIST_ENTRY(ast_dial_channel) list; /*!< Linked list information */
+};
+
+/*! \brief Typedef for dial option enable */
+typedef void *(*ast_dial_option_cb_enable)(void *data);
+
+/*! \brief Typedef for dial option disable */
+typedef int (*ast_dial_option_cb_disable)(void *data);
+
+/*! \brief Structure for 'ANSWER_EXEC' option */
+struct answer_exec_struct {
+ char app[AST_MAX_APP]; /*!< Application name */
+ char *args; /*!< Application arguments */
+};
+
+/*! \brief Enable function for 'ANSWER_EXEC' option */
+static void *answer_exec_enable(void *data)
+{
+ struct answer_exec_struct *answer_exec = NULL;
+ char *app = ast_strdupa((char*)data), *args = NULL;
+
+ /* Not giving any data to this option is bad, mmmk? */
+ if (ast_strlen_zero(app))
+ return NULL;
+
+ /* Create new data structure */
+ if (!(answer_exec = ast_calloc(1, sizeof(*answer_exec))))
+ return NULL;
+
+ /* Parse out application and arguments */
+ if ((args = strchr(app, '|'))) {
+ *args++ = '\0';
+ answer_exec->args = ast_strdup(args);
+ }
+
+ /* Copy application name */
+ ast_copy_string(answer_exec->app, app, sizeof(answer_exec->app));
+
+ return answer_exec;
+}
+
+/*! \brief Disable function for 'ANSWER_EXEC' option */
+static int answer_exec_disable(void *data)
+{
+ struct answer_exec_struct *answer_exec = data;
+
+ /* Make sure we have a value */
+ if (!answer_exec)
+ return -1;
+
+ /* If arguments are present, free them too */
+ if (answer_exec->args)
+ ast_free(answer_exec->args);
+
+ /* This is simple - just free the structure */
+ ast_free(answer_exec);
+
+ return 0;
+}
+
+static void *music_enable(void *data)
+{
+ return ast_strdup(data);
+}
+
+static int music_disable(void *data)
+{
+ if (!data)
+ return -1;
+
+ ast_free(data);
+
+ return 0;
+}
+
+/*! \brief Application execution function for 'ANSWER_EXEC' option */
+static void answer_exec_run(struct ast_dial *dial, struct ast_dial_channel *dial_channel, char *app, char *args)
+{
+ struct ast_channel *chan = dial_channel->owner;
+ struct ast_app *ast_app = pbx_findapp(app);
+
+ /* If the application was not found, return immediately */
+ if (!ast_app)
+ return;
+
+ /* All is well... execute the application */
+ pbx_exec(chan, ast_app, args);
+
+ /* If another thread is not taking over hang up the channel */
+ ast_mutex_lock(&dial->lock);
+ if (dial->thread != AST_PTHREADT_STOP) {
+ ast_hangup(chan);
+ dial_channel->owner = NULL;
+ }
+ ast_mutex_unlock(&dial->lock);
+
+ return;
+}
+
+/*! \brief Options structure - maps options to respective handlers (enable/disable). This list MUST be perfectly kept in order, or else madness will happen. */
+static const struct ast_option_types {
+ enum ast_dial_option option;
+ ast_dial_option_cb_enable enable;
+ ast_dial_option_cb_disable disable;
+} option_types[] = {
+ { AST_DIAL_OPTION_RINGING, NULL, NULL }, /*!< Always indicate ringing to caller */
+ { AST_DIAL_OPTION_ANSWER_EXEC, answer_exec_enable, answer_exec_disable }, /*!< Execute application upon answer in async mode */
+ { AST_DIAL_OPTION_MUSIC, music_enable, music_disable }, /*!< Play music to the caller instead of ringing */
+ { AST_DIAL_OPTION_DISABLE_CALL_FORWARDING, NULL, NULL }, /*!< Disable call forwarding on channels */
+ { AST_DIAL_OPTION_MAX, NULL, NULL }, /*!< Terminator of list */
+};
+
+/*! \brief free the buffer if allocated, and set the pointer to the second arg */
+#define S_REPLACE(s, new_val) \
+ do { \
+ if (s) \
+ free(s); \
+ s = (new_val); \
+ } while (0)
+
+/*! \brief Maximum number of channels we can watch at a time */
+#define AST_MAX_WATCHERS 256
+
+/*! \brief Macro for finding the option structure to use on a dialed channel */
+#define FIND_RELATIVE_OPTION(dial, dial_channel, ast_dial_option) (dial_channel->options[ast_dial_option] ? dial_channel->options[ast_dial_option] : dial->options[ast_dial_option])
+
+/*! \brief Macro that determines whether a channel is the caller or not */
+#define IS_CALLER(chan, owner) (chan == owner ? 1 : 0)
+
+/*! \brief New dialing structure
+ * \note Create a dialing structure
+ * \return Returns a calloc'd ast_dial structure, NULL on failure
+ */
+struct ast_dial *ast_dial_create(void)
+{
+ struct ast_dial *dial = NULL;
+
+ /* Allocate new memory for structure */
+ if (!(dial = ast_calloc(1, sizeof(*dial))))
+ return NULL;
+
+ /* Initialize list of channels */
+ AST_LIST_HEAD_INIT_NOLOCK(&dial->channels);
+
+ /* Initialize thread to NULL */
+ dial->thread = AST_PTHREADT_NULL;
+
+ /* No timeout exists... yet */
+ dial->timeout = -1;
+ dial->actual_timeout = -1;
+
+ /* Can't forget about the lock */
+ ast_mutex_init(&dial->lock);
+
+ return dial;
+}
+
+/*! \brief Append a channel
+ * \note Appends a channel to a dialing structure
+ * \return Returns channel reference number on success, -1 on failure
+ */
+int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ /* Make sure we have required arguments */
+ if (!dial || !tech || !device)
+ return -1;
+
+ /* Allocate new memory for dialed channel structure */
+ if (!(channel = ast_calloc(1, sizeof(*channel))))
+ return -1;
+
+ /* Record technology and device for when we actually dial */
+ channel->tech = ast_strdup(tech);
+ channel->device = ast_strdup(device);
+
+ /* Grab reference number from dial structure */
+ channel->num = ast_atomic_fetchadd_int(&dial->num, +1);
+
+ /* No timeout exists... yet */
+ channel->timeout = -1;
+
+ /* Insert into channels list */
+ AST_LIST_INSERT_TAIL(&dial->channels, channel, list);
+
+ return channel->num;
+}
+
+/*! \brief Helper function that does the beginning dialing per-appended channel */
+static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_channel *chan)
+{
+ char numsubst[AST_MAX_EXTENSION];
+ int res = 1;
+
+ /* Copy device string over */
+ ast_copy_string(numsubst, channel->device, sizeof(numsubst));
+
+ /* If we fail to create our owner channel bail out */
+ if (!(channel->owner = ast_request(channel->tech, chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, numsubst, &channel->cause)))
+ return -1;
+
+ channel->owner->appl = "AppDial2";
+ channel->owner->data = "(Outgoing Line)";
+ channel->owner->whentohangup = 0;
+
+ /* Inherit everything from he who spawned this dial */
+ if (chan) {
+ ast_channel_inherit_variables(chan, channel->owner);
+
+ /* Copy over callerid information */
+ S_REPLACE(channel->owner->cid.cid_num, ast_strdup(chan->cid.cid_num));
+ S_REPLACE(channel->owner->cid.cid_name, ast_strdup(chan->cid.cid_name));
+ S_REPLACE(channel->owner->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
+ S_REPLACE(channel->owner->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
+
+ ast_string_field_set(channel->owner, language, chan->language);
+ ast_string_field_set(channel->owner, accountcode, chan->accountcode);
+ channel->owner->cdrflags = chan->cdrflags;
+ if (ast_strlen_zero(channel->owner->musicclass))
+ ast_string_field_set(channel->owner, musicclass, chan->musicclass);
+
+ channel->owner->cid.cid_pres = chan->cid.cid_pres;
+ channel->owner->cid.cid_ton = chan->cid.cid_ton;
+ channel->owner->cid.cid_tns = chan->cid.cid_tns;
+ channel->owner->adsicpe = chan->adsicpe;
+ channel->owner->transfercapability = chan->transfercapability;
+ }
+
+ /* Attempt to actually call this device */
+ if ((res = ast_call(channel->owner, numsubst, 0))) {
+ res = 0;
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ } else {
+ if (chan)
+ ast_poll_channel_add(chan, channel->owner);
+ res = 1;
+ ast_verb(3, "Called %s\n", numsubst);
+ }
+
+ return res;
+}
+
+/*! \brief Helper function that does the beginning dialing per dial structure */
+static int begin_dial(struct ast_dial *dial, struct ast_channel *chan)
+{
+ struct ast_dial_channel *channel = NULL;
+ int success = 0;
+
+ /* Iterate through channel list, requesting and calling each one */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ success += begin_dial_channel(channel, chan);
+ }
+
+ /* If number of failures matches the number of channels, then this truly failed */
+ return success;
+}
+
+/*! \brief Helper function to handle channels that have been call forwarded */
+static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_channel *chan)
+{
+ struct ast_channel *original = channel->owner;
+ char *tmp = ast_strdupa(channel->owner->call_forward);
+ char *tech = "Local", *device = tmp, *stuff;
+
+ /* If call forwarding is disabled just drop the original channel and don't attempt to dial the new one */
+ if (FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_DISABLE_CALL_FORWARDING)) {
+ ast_hangup(original);
+ channel->owner = NULL;
+ return 0;
+ }
+
+ /* Figure out the new destination */
+ if ((stuff = strchr(tmp, '/'))) {
+ *stuff++ = '\0';
+ tech = tmp;
+ device = stuff;
+ }
+
+ /* Drop old destination information */
+ ast_free(channel->tech);
+ ast_free(channel->device);
+
+ /* Update the dial channel with the new destination information */
+ channel->tech = ast_strdup(tech);
+ channel->device = ast_strdup(device);
+
+ /* Finally give it a go... send it out into the world */
+ begin_dial_channel(channel, chan);
+
+ /* Drop the original channel */
+ ast_hangup(original);
+
+ return 0;
+}
+
+/*! \brief Helper function that finds the dialed channel based on owner */
+static struct ast_dial_channel *find_relative_dial_channel(struct ast_dial *dial, struct ast_channel *owner)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner == owner)
+ break;
+ }
+
+ return channel;
+}
+
+static void set_state(struct ast_dial *dial, enum ast_dial_result state)
+{
+ dial->state = state;
+
+ if (dial->state_callback)
+ dial->state_callback(dial);
+}
+
+/*! \brief Helper function that handles control frames WITH owner */
+static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr, struct ast_channel *chan)
+{
+ if (fr->frametype == AST_FRAME_CONTROL) {
+ switch (fr->subclass) {
+ case AST_CONTROL_ANSWER:
+ ast_verb(3, "%s answered %s\n", channel->owner->name, chan->name);
+ AST_LIST_REMOVE(&dial->channels, channel, list);
+ AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
+ set_state(dial, AST_DIAL_RESULT_ANSWERED);
+ break;
+ case AST_CONTROL_BUSY:
+ ast_verb(3, "%s is busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(3, "%s is circuit-busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_RINGING:
+ ast_verb(3, "%s is ringing\n", channel->owner->name);
+ if (!dial->options[AST_DIAL_OPTION_MUSIC])
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ break;
+ case AST_CONTROL_PROGRESS:
+ ast_verb(3, "%s is making progress, passing it to %s\n", channel->owner->name, chan->name);
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+ set_state(dial, AST_DIAL_RESULT_PROGRESS);
+ break;
+ case AST_CONTROL_VIDUPDATE:
+ ast_verb(3, "%s requested a video update, passing it to %s\n", channel->owner->name, chan->name);
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ ast_verb(3, "%s is proceeding, passing it to %s\n", channel->owner->name, chan->name);
+ ast_indicate(chan, AST_CONTROL_PROCEEDING);
+ set_state(dial, AST_DIAL_RESULT_PROCEEDING);
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verb(3, "Call on %s placed on hold\n", chan->name);
+ ast_indicate(chan, AST_CONTROL_HOLD);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verb(3, "Call on %s left from hold\n", chan->name);
+ ast_indicate(chan, AST_CONTROL_UNHOLD);
+ break;
+ case AST_CONTROL_OFFHOOK:
+ case AST_CONTROL_FLASH:
+ break;
+ case -1:
+ /* Prod the channel */
+ ast_indicate(chan, -1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return;
+}
+
+/*! \brief Helper function that handles control frames WITHOUT owner */
+static void handle_frame_ownerless(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr)
+{
+ /* If we have no owner we can only update the state of the dial structure, so only look at control frames */
+ if (fr->frametype != AST_FRAME_CONTROL)
+ return;
+
+ switch (fr->subclass) {
+ case AST_CONTROL_ANSWER:
+ ast_verb(3, "%s answered\n", channel->owner->name);
+ AST_LIST_REMOVE(&dial->channels, channel, list);
+ AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
+ set_state(dial, AST_DIAL_RESULT_ANSWERED);
+ break;
+ case AST_CONTROL_BUSY:
+ ast_verb(3, "%s is busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(3, "%s is circuit-busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_RINGING:
+ ast_verb(3, "%s is ringing\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ break;
+ case AST_CONTROL_PROGRESS:
+ ast_verb(3, "%s is making progress\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_PROGRESS);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ ast_verb(3, "%s is proceeding\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_PROCEEDING);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+/*! \brief Helper function to handle when a timeout occurs on dialing attempt */
+static int handle_timeout_trip(struct ast_dial *dial, struct timeval start)
+{
+ struct ast_dial_channel *channel = NULL;
+ int diff = ast_tvdiff_ms(ast_tvnow(), start), lowest_timeout = -1, new_timeout = -1;
+
+ /* If the global dial timeout tripped switch the state to timeout so our channel loop will drop every channel */
+ if (diff >= dial->timeout) {
+ set_state(dial, AST_DIAL_RESULT_TIMEOUT);
+ new_timeout = 0;
+ }
+
+ /* Go through dropping out channels that have met their timeout */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (dial->state == AST_DIAL_RESULT_TIMEOUT || diff >= channel->timeout) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ } else if ((lowest_timeout == -1) || (lowest_timeout > channel->timeout)) {
+ lowest_timeout = channel->timeout;
+ }
+ }
+
+ /* Calculate the new timeout using the lowest timeout found */
+ if (lowest_timeout >= 0)
+ new_timeout = lowest_timeout - diff;
+
+ return new_timeout;
+}
+
+/*! \brief Helper function that basically keeps tabs on dialing attempts */
+static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_channel *chan)
+{
+ int timeout = -1;
+ struct ast_channel *cs[AST_MAX_WATCHERS], *who = NULL;
+ struct ast_dial_channel *channel = NULL;
+ struct answer_exec_struct *answer_exec = NULL;
+ struct timeval start;
+
+ set_state(dial, AST_DIAL_RESULT_TRYING);
+
+ /* If the "always indicate ringing" option is set, change state to ringing and indicate to the owner if present */
+ if (dial->options[AST_DIAL_OPTION_RINGING]) {
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ if (chan)
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ } else if (chan && dial->options[AST_DIAL_OPTION_MUSIC] &&
+ !ast_strlen_zero(dial->options[AST_DIAL_OPTION_MUSIC])) {
+ char *original_moh = ast_strdupa(chan->musicclass);
+ ast_indicate(chan, -1);
+ ast_string_field_set(chan, musicclass, dial->options[AST_DIAL_OPTION_MUSIC]);
+ ast_moh_start(chan, dial->options[AST_DIAL_OPTION_MUSIC], NULL);
+ ast_string_field_set(chan, musicclass, original_moh);
+ }
+
+ /* Record start time for timeout purposes */
+ start = ast_tvnow();
+
+ /* We actually figured out the maximum timeout we can do as they were added, so we can directly access the info */
+ timeout = dial->actual_timeout;
+
+ /* Go into an infinite loop while we are trying */
+ while ((dial->state != AST_DIAL_RESULT_UNANSWERED) && (dial->state != AST_DIAL_RESULT_ANSWERED) && (dial->state != AST_DIAL_RESULT_HANGUP) && (dial->state != AST_DIAL_RESULT_TIMEOUT)) {
+ int pos = 0, count = 0;
+ struct ast_frame *fr = NULL;
+
+ /* Set up channel structure array */
+ pos = count = 0;
+ if (chan)
+ cs[pos++] = chan;
+
+ /* Add channels we are attempting to dial */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner) {
+ cs[pos++] = channel->owner;
+ count++;
+ }
+ }
+
+ /* If we have no outbound channels in progress, switch state to unanswered and stop */
+ if (!count) {
+ set_state(dial, AST_DIAL_RESULT_UNANSWERED);
+ break;
+ }
+
+ /* Just to be safe... */
+ if (dial->thread == AST_PTHREADT_STOP)
+ break;
+
+ /* Wait for frames from channels */
+ who = ast_waitfor_n(cs, pos, &timeout);
+
+ /* Check to see if our thread is being cancelled */
+ if (dial->thread == AST_PTHREADT_STOP)
+ break;
+
+ /* If the timeout no longer exists OR if we got no channel it basically means the timeout was tripped, so handle it */
+ if (!timeout || !who) {
+ timeout = handle_timeout_trip(dial, start);
+ continue;
+ }
+
+ /* Find relative dial channel */
+ if (!chan || !IS_CALLER(chan, who))
+ channel = find_relative_dial_channel(dial, who);
+
+ /* See if this channel has been forwarded elsewhere */
+ if (!ast_strlen_zero(who->call_forward)) {
+ handle_call_forward(dial, channel, chan);
+ continue;
+ }
+
+ /* Attempt to read in a frame */
+ if (!(fr = ast_read(who))) {
+ /* If this is the caller then we switch state to hangup and stop */
+ if (chan && IS_CALLER(chan, who)) {
+ set_state(dial, AST_DIAL_RESULT_HANGUP);
+ break;
+ }
+ if (chan)
+ ast_poll_channel_del(chan, channel->owner);
+ ast_hangup(who);
+ channel->owner = NULL;
+ continue;
+ }
+
+ /* Process the frame */
+ if (chan)
+ handle_frame(dial, channel, fr, chan);
+ else
+ handle_frame_ownerless(dial, channel, fr);
+
+ /* Free the received frame and start all over */
+ ast_frfree(fr);
+ }
+
+ /* Do post-processing from loop */
+ if (dial->state == AST_DIAL_RESULT_ANSWERED) {
+ /* Hangup everything except that which answered */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (!channel->owner || channel->owner == who)
+ continue;
+ if (chan)
+ ast_poll_channel_del(chan, channel->owner);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ /* If ANSWER_EXEC is enabled as an option, execute application on answered channel */
+ if ((channel = find_relative_dial_channel(dial, who)) && (answer_exec = FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_ANSWER_EXEC))) {
+ channel->is_running_app = 1;
+ answer_exec_run(dial, channel, answer_exec->app, answer_exec->args);
+ channel->is_running_app = 0;
+ }
+
+ if (chan && dial->options[AST_DIAL_OPTION_MUSIC] &&
+ !ast_strlen_zero(dial->options[AST_DIAL_OPTION_MUSIC])) {
+ ast_moh_stop(chan);
+ }
+ } else if (dial->state == AST_DIAL_RESULT_HANGUP) {
+ /* Hangup everything */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (!channel->owner)
+ continue;
+ if (chan)
+ ast_poll_channel_del(chan, channel->owner);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ }
+
+ return dial->state;
+}
+
+/*! \brief Dial async thread function */
+static void *async_dial(void *data)
+{
+ struct ast_dial *dial = data;
+
+ /* This is really really simple... we basically pass monitor_dial a NULL owner and it changes it's behavior */
+ monitor_dial(dial, NULL);
+
+ return NULL;
+}
+
+/*! \brief Execute dialing synchronously or asynchronously
+ * \note Dials channels in a dial structure.
+ * \return Returns dial result code. (TRYING/INVALID/FAILED/ANSWERED/TIMEOUT/UNANSWERED).
+ */
+enum ast_dial_result ast_dial_run(struct ast_dial *dial, struct ast_channel *chan, int async)
+{
+ enum ast_dial_result res = AST_DIAL_RESULT_TRYING;
+
+ /* Ensure required arguments are passed */
+ if (!dial || (!chan && !async)) {
+ ast_debug(1, "invalid #1\n");
+ return AST_DIAL_RESULT_INVALID;
+ }
+
+ /* If there are no channels to dial we can't very well try to dial them */
+ if (AST_LIST_EMPTY(&dial->channels)) {
+ ast_debug(1, "invalid #2\n");
+ return AST_DIAL_RESULT_INVALID;
+ }
+
+ /* Dial each requested channel */
+ if (!begin_dial(dial, chan))
+ return AST_DIAL_RESULT_FAILED;
+
+ /* If we are running async spawn a thread and send it away... otherwise block here */
+ if (async) {
+ dial->state = AST_DIAL_RESULT_TRYING;
+ /* Try to create a thread */
+ if (ast_pthread_create(&dial->thread, NULL, async_dial, dial)) {
+ /* Failed to create the thread - hangup all dialed channels and return failed */
+ ast_dial_hangup(dial);
+ res = AST_DIAL_RESULT_FAILED;
+ }
+ } else {
+ res = monitor_dial(dial, chan);
+ }
+
+ return res;
+}
+
+/*! \brief Return channel that answered
+ * \note Returns the Asterisk channel that answered
+ * \param dial Dialing structure
+ */
+struct ast_channel *ast_dial_answered(struct ast_dial *dial)
+{
+ if (!dial)
+ return NULL;
+
+ return ((dial->state == AST_DIAL_RESULT_ANSWERED) ? AST_LIST_FIRST(&dial->channels)->owner : NULL);
+}
+
+/*! \brief Return state of dial
+ * \note Returns the state of the dial attempt
+ * \param dial Dialing structure
+ */
+enum ast_dial_result ast_dial_state(struct ast_dial *dial)
+{
+ return dial->state;
+}
+
+/*! \brief Cancel async thread
+ * \note Cancel a running async thread
+ * \param dial Dialing structure
+ */
+enum ast_dial_result ast_dial_join(struct ast_dial *dial)
+{
+ pthread_t thread;
+
+ /* If the dial structure is not running in async, return failed */
+ if (dial->thread == AST_PTHREADT_NULL)
+ return AST_DIAL_RESULT_FAILED;
+
+ /* Record thread */
+ thread = dial->thread;
+
+ /* Boom, commence locking */
+ ast_mutex_lock(&dial->lock);
+
+ /* Stop the thread */
+ dial->thread = AST_PTHREADT_STOP;
+
+ /* If the answered channel is running an application we have to soft hangup it, can't just poke the thread */
+ if (AST_LIST_FIRST(&dial->channels)->is_running_app) {
+ struct ast_channel *chan = AST_LIST_FIRST(&dial->channels)->owner;
+ ast_channel_lock(chan);
+ ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(chan);
+ } else {
+ /* Now we signal it with SIGURG so it will break out of it's waitfor */
+ pthread_kill(thread, SIGURG);
+ }
+
+ /* Yay done with it */
+ ast_mutex_unlock(&dial->lock);
+
+ /* Finally wait for the thread to exit */
+ pthread_join(thread, NULL);
+
+ /* Yay thread is all gone */
+ dial->thread = AST_PTHREADT_NULL;
+
+ return dial->state;
+}
+
+/*! \brief Hangup channels
+ * \note Hangup all active channels
+ * \param dial Dialing structure
+ */
+void ast_dial_hangup(struct ast_dial *dial)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ if (!dial)
+ return;
+
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ }
+
+ return;
+}
+
+/*! \brief Destroys a dialing structure
+ * \note Destroys (free's) the given ast_dial structure
+ * \param dial Dialing structure to free
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_destroy(struct ast_dial *dial)
+{
+ int i = 0;
+ struct ast_dial_channel *channel = NULL;
+
+ if (!dial)
+ return -1;
+
+ /* Hangup and deallocate all the dialed channels */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&dial->channels, channel, list) {
+ /* Disable any enabled options */
+ for (i = 0; i < AST_DIAL_OPTION_MAX; i++) {
+ if (!channel->options[i])
+ continue;
+ if (option_types[i].disable)
+ option_types[i].disable(channel->options[i]);
+ channel->options[i] = NULL;
+ }
+ /* Hang up channel if need be */
+ if (channel->owner) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ /* Free structure */
+ ast_free(channel->tech);
+ ast_free(channel->device);
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(channel);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* Disable any enabled options globally */
+ for (i = 0; i < AST_DIAL_OPTION_MAX; i++) {
+ if (!dial->options[i])
+ continue;
+ if (option_types[i].disable)
+ option_types[i].disable(dial->options[i]);
+ dial->options[i] = NULL;
+ }
+
+ /* Lock be gone! */
+ ast_mutex_destroy(&dial->lock);
+
+ /* Free structure */
+ ast_free(dial);
+
+ return 0;
+}
+
+/*! \brief Enables an option globally
+ * \param dial Dial structure to enable option on
+ * \param option Option to enable
+ * \param data Data to pass to this option (not always needed)
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_global_enable(struct ast_dial *dial, enum ast_dial_option option, void *data)
+{
+ /* If the option is already enabled, return failure */
+ if (dial->options[option])
+ return -1;
+
+ /* Execute enable callback if it exists, if not simply make sure the value is set */
+ if (option_types[option].enable)
+ dial->options[option] = option_types[option].enable(data);
+ else
+ dial->options[option] = (void*)1;
+
+ return 0;
+}
+
+/*! \brief Helper function for finding a channel in a dial structure based on number
+ */
+static struct ast_dial_channel *find_dial_channel(struct ast_dial *dial, int num)
+{
+ struct ast_dial_channel *channel = AST_LIST_LAST(&dial->channels);
+
+ /* We can try to predict programmer behavior, the last channel they added is probably the one they wanted to modify */
+ if (channel->num == num)
+ return channel;
+
+ /* Hrm not at the end... looking through the list it is! */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->num == num)
+ break;
+ }
+
+ return channel;
+}
+
+/*! \brief Enables an option per channel
+ * \param dial Dial structure
+ * \param num Channel number to enable option on
+ * \param option Option to enable
+ * \param data Data to pass to this option (not always needed)
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_enable(struct ast_dial *dial, int num, enum ast_dial_option option, void *data)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ /* Ensure we have required arguments */
+ if (!dial || AST_LIST_EMPTY(&dial->channels))
+ return -1;
+
+ if (!(channel = find_dial_channel(dial, num)))
+ return -1;
+
+ /* If the option is already enabled, return failure */
+ if (channel->options[option])
+ return -1;
+
+ /* Execute enable callback if it exists, if not simply make sure the value is set */
+ if (option_types[option].enable)
+ channel->options[option] = option_types[option].enable(data);
+ else
+ channel->options[option] = (void*)1;
+
+ return 0;
+}
+
+/*! \brief Disables an option globally
+ * \param dial Dial structure to disable option on
+ * \param option Option to disable
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_global_disable(struct ast_dial *dial, enum ast_dial_option option)
+{
+ /* If the option is not enabled, return failure */
+ if (!dial->options[option])
+ return -1;
+
+ /* Execute callback of option to disable if it exists */
+ if (option_types[option].disable)
+ option_types[option].disable(dial->options[option]);
+
+ /* Finally disable option on the structure */
+ dial->options[option] = NULL;
+
+ return 0;
+}
+
+/*! \brief Disables an option per channel
+ * \param dial Dial structure
+ * \param num Channel number to disable option on
+ * \param option Option to disable
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_disable(struct ast_dial *dial, int num, enum ast_dial_option option)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ /* Ensure we have required arguments */
+ if (!dial || AST_LIST_EMPTY(&dial->channels))
+ return -1;
+
+ if (!(channel = find_dial_channel(dial, num)))
+ return -1;
+
+ /* If the option is not enabled, return failure */
+ if (!channel->options[option])
+ return -1;
+
+ /* Execute callback of option to disable it if it exists */
+ if (option_types[option].disable)
+ option_types[option].disable(channel->options[option]);
+
+ /* Finally disable the option on the structure */
+ channel->options[option] = NULL;
+
+ return 0;
+}
+
+void ast_dial_set_state_callback(struct ast_dial *dial, ast_dial_state_callback callback)
+{
+ dial->state_callback = callback;
+}
+
+/*! \brief Set the maximum time (globally) allowed for trying to ring phones
+ * \param dial The dial structure to apply the time limit to
+ * \param timeout Maximum time allowed
+ * \return nothing
+ */
+void ast_dial_set_global_timeout(struct ast_dial *dial, int timeout)
+{
+ dial->timeout = timeout;
+
+ if (dial->timeout > 0 && dial->actual_timeout > dial->timeout)
+ dial->actual_timeout = dial->timeout;
+
+ return;
+}
+
+/*! \brief Set the maximum time (per channel) allowed for trying to ring the phone
+ * \param dial The dial structure the channel belongs to
+ * \param num Channel number to set timeout on
+ * \param timeout Maximum time allowed
+ * \return nothing
+ */
+void ast_dial_set_timeout(struct ast_dial *dial, int num, int timeout)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ if (!(channel = find_dial_channel(dial, num)))
+ return;
+
+ channel->timeout = timeout;
+
+ if (channel->timeout > 0 && dial->actual_timeout > channel->timeout)
+ dial->actual_timeout = channel->timeout;
+
+ return;
+}
diff --git a/trunk/main/dlfcn.c b/trunk/main/dlfcn.c
new file mode 100644
index 000000000..802e7942f
--- /dev/null
+++ b/trunk/main/dlfcn.c
@@ -0,0 +1,1309 @@
+/*
+Copyright (c) 2002 Jorge Acereda <jacereda@users.sourceforge.net> &
+ Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Portions may be copyright others, see the AUTHORS file included with this
+distribution.
+
+Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <pthread.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <mach-o/dyld.h>
+#include <mach-o/nlist.h>
+#include <mach-o/getsect.h>
+/* Just playing to see if it would compile with the freebsd headers, it does,
+ * but because of the different values for RTLD_LOCAL etc, it would break binary
+ * compat... oh well
+ */
+#ifndef __BSD_VISIBLE
+#define __BSD_VISIBLE 1
+#endif
+
+#include "asterisk/dlfcn-compat.h"
+
+#ifndef dl_restrict
+#define dl_restrict __restrict
+#endif
+/* This is not available on 10.1 */
+#ifndef LC_LOAD_WEAK_DYLIB
+#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
+#endif
+
+/* With this stuff here, this thing may actually compile/run on 10.0 systems
+ * Not that I have a 10.0 system to test it on anylonger
+ */
+#ifndef LC_REQ_DYLD
+#define LC_REQ_DYLD 0x80000000
+#endif
+#ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
+#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4
+#endif
+#ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR
+#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1
+#endif
+#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
+#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0
+#endif
+#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
+#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4
+#endif
+/* These symbols will be looked for in dyld */
+static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0;
+static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0;
+static NSSymbol(*dyld_NSLookupSymbolInImage)
+ (const struct mach_header *, const char *, unsigned long) = 0;
+
+/* Define this to make dlcompat reuse data block. This way in theory we save
+ * a little bit of overhead. However we then couldn't correctly catch excess
+ * calls to dlclose(). Hence we don't use this feature
+ */
+#undef REUSE_STATUS
+
+/* Size of the internal error message buffer (used by dlerror()) */
+#define ERR_STR_LEN 251
+
+/* Maximum number of search paths supported by getSearchPath */
+#define MAX_SEARCH_PATHS 32
+
+
+#define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF')
+#define MAGIC_DYLIB_MOD ((NSModule) 'DYMO')
+
+/* internal flags */
+#define DL_IN_LIST 0x01
+
+/* our mutex */
+static pthread_mutex_t dlcompat_mutex;
+/* Our thread specific storage
+ */
+static pthread_key_t dlerror_key;
+
+struct dlthread
+{
+ int lockcnt;
+ unsigned char errset;
+ char errstr[ERR_STR_LEN];
+};
+
+/* This is our central data structure. Whenever a module is loaded via
+ * dlopen(), we create such a struct.
+ */
+struct dlstatus
+{
+ struct dlstatus *next; /* pointer to next element in the linked list */
+ NSModule module;
+ const struct mach_header *lib;
+ int refs; /* reference count */
+ int mode; /* mode in which this module was loaded */
+ dev_t device;
+ ino_t inode;
+ int flags; /* Any internal flags we may need */
+};
+
+/* Head node of the dlstatus list */
+static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 };
+static struct dlstatus *stqueue = &mainStatus;
+
+
+/* Storage for the last error message (used by dlerror()) */
+/* static char err_str[ERR_STR_LEN]; */
+/* static int err_filled = 0; */
+
+/* Prototypes to internal functions */
+static void debug(const char *fmt, ...);
+static void error(const char *str, ...);
+static const char *safegetenv(const char *s);
+static const char *searchList(void);
+static const char *getSearchPath(int i);
+static const char *getFullPath(int i, const char *file);
+static const struct stat *findFile(const char *file, const char **fullPath);
+static int isValidStatus(struct dlstatus *status);
+static inline int isFlagSet(int mode, int flag);
+static struct dlstatus *lookupStatus(const struct stat *sbuf);
+static void insertStatus(struct dlstatus *dls, const struct stat *sbuf);
+static int promoteLocalToGlobal(struct dlstatus *dls);
+static void *reference(struct dlstatus *dls, int mode);
+static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError);
+static struct dlstatus *allocStatus(void);
+static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode);
+static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol);
+static const char *get_lib_name(const struct mach_header *mh);
+static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod);
+static void dlcompat_init_func(void);
+static inline void dolock(void);
+static inline void dounlock(void);
+static void dlerrorfree(void *data);
+static void resetdlerror(void);
+static const struct mach_header *my_find_image(const char *name);
+static const struct mach_header *image_for_address(const void *address);
+static void dlcompat_cleanup(void);
+static inline const char *dyld_error_str(void);
+
+#if FINK_BUILD
+/* Two Global Functions */
+void *dlsym_prepend_underscore(void *handle, const char *symbol);
+void *dlsym_auto_underscore(void *handle, const char *symbol);
+
+/* And their _intern counterparts */
+static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol);
+static void *dlsym_auto_underscore_intern(void *handle, const char *symbol);
+#endif
+
+/* Functions */
+
+static void debug(const char *fmt, ...)
+{
+#if DEBUG > 1
+ va_list arg;
+ va_start(arg, fmt);
+ fprintf(stderr, "DLDEBUG: ");
+ vfprintf(stderr, fmt, arg);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ va_end(arg);
+#endif
+}
+
+static void error(const char *str, ...)
+{
+ va_list arg;
+ struct dlthread *tss;
+ char * err_str;
+ va_start(arg, str);
+ tss = pthread_getspecific(dlerror_key);
+ err_str = tss->errstr;
+ strncpy(err_str, "dlcompat: ", ERR_STR_LEN);
+ vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg);
+ va_end(arg);
+ debug("ERROR: %s\n", err_str);
+ tss->errset = 1;
+}
+
+static void warning(const char *str)
+{
+#if DEBUG > 0
+ fprintf(stderr, "WARNING: dlcompat: %s\n", str);
+#endif
+}
+
+static const char *safegetenv(const char *s)
+{
+ const char *ss = getenv(s);
+ return ss ? ss : "";
+}
+
+/* because this is only used for debugging and error reporting functions, we
+ * don't really care about how elegant it is... it could use the load
+ * commands to find the install name of the library, but...
+ */
+static const char *get_lib_name(const struct mach_header *mh)
+{
+ unsigned long count = _dyld_image_count();
+ unsigned long i;
+ const char *val = NULL;
+ if (mh)
+ {
+ for (i = 0; i < count; i++)
+ {
+ if (mh == _dyld_get_image_header(i))
+ {
+ val = _dyld_get_image_name(i);
+ break;
+ }
+ }
+ }
+ return val;
+}
+
+/* Returns the mach_header for the module bu going through all the loaded images
+ * and finding the one with the same name as the module. There really ought to be
+ * an api for doing this, would be faster, but there isn't one right now
+ */
+static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod)
+{
+ const char *mod_name = NSNameOfModule(mod);
+ struct mach_header *mh = NULL;
+ unsigned long count = _dyld_image_count();
+ unsigned long i;
+ debug("Module name: %s", mod_name);
+ for (i = 0; i < count; i++)
+ {
+ if (!strcmp(mod_name, _dyld_get_image_name(i)))
+ {
+ mh = _dyld_get_image_header(i);
+ break;
+ }
+ }
+ return mh;
+}
+
+
+/* Compute and return a list of all directories that we should search when
+ * trying to locate a module. We first look at the values of LD_LIBRARY_PATH
+ * and DYLD_LIBRARY_PATH, and then finally fall back to looking into
+ * /usr/lib and /lib. Since both of the environments variables can contain a
+ * list of colon separated paths, we simply concat them and the two other paths
+ * into one big string, which we then can easily parse.
+ * Splitting this string into the actual path list is done by getSearchPath()
+ */
+static const char *searchList()
+{
+ size_t buf_size;
+ static char *buf=NULL;
+ const char *ldlp = safegetenv("LD_LIBRARY_PATH");
+ const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH");
+ const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH");
+ if (!stdpath)
+ stdpath = "/usr/local/lib:/lib:/usr/lib";
+ if (!buf)
+ {
+ buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4;
+ buf = ast_malloc(buf_size);
+ snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""),
+ stdpath, '\0');
+ }
+ return buf;
+}
+
+/* Returns the ith search path from the list as computed by searchList() */
+static const char *getSearchPath(int i)
+{
+ static const char *list = 0;
+ static char **path = (char **)0;
+ static int end = 0;
+ static int numsize = MAX_SEARCH_PATHS;
+ static char **tmp;
+ /* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */
+ if (i == -1)
+ {
+ return (const char*)path;
+ }
+ if (!path)
+ {
+ path = ast_calloc(MAX_SEARCH_PATHS, sizeof(char **));
+ }
+ if (!list && !end)
+ list = searchList();
+ if (i >= (numsize))
+ {
+ debug("Increasing size for long PATH");
+ tmp = ast_calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **));
+ if (tmp)
+ {
+ memcpy(tmp, path, sizeof(char **) * numsize);
+ ast_free(path);
+ path = tmp;
+ numsize += MAX_SEARCH_PATHS;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ while (!path[i] && !end)
+ {
+ path[i] = strsep((char **)&list, ":");
+
+ if (path[i][0] == 0)
+ path[i] = 0;
+ end = (list == 0);
+ }
+ return path[i];
+}
+
+static const char *getFullPath(int i, const char *file)
+{
+ static char buf[PATH_MAX];
+ const char *path = getSearchPath(i);
+ if (path)
+ {
+ snprintf(buf, PATH_MAX, "%s/%s", path, file);
+ }
+ return path ? buf : 0;
+}
+
+/* Given a file name, try to determine the full path for that file. Starts
+ * its search in the current directory, and then tries all paths in the
+ * search list in the order they are specified there.
+ */
+static const struct stat *findFile(const char *file, const char **fullPath)
+{
+ int i = 0;
+ static struct stat sbuf;
+ char *fileName;
+ debug("finding file %s", file);
+ *fullPath = file;
+ if (0 == stat(file, &sbuf))
+ return &sbuf;
+ if (strchr(file, '/'))
+ return 0; /* If the path had a / we don't look in env var places */
+ fileName = NULL;
+ if (!fileName)
+ fileName = (char *)file;
+ while ((*fullPath = getFullPath(i++, fileName)))
+ {
+ if (0 == stat(*fullPath, &sbuf))
+ return &sbuf;
+ }
+ ;
+ return 0;
+}
+
+/* Determine whether a given dlstatus is valid or not */
+static int isValidStatus(struct dlstatus *status)
+{
+ /* Walk the list to verify status is contained in it */
+ struct dlstatus *dls = stqueue;
+ while (dls && status != dls)
+ dls = dls->next;
+ if (dls == 0)
+ error("invalid handle");
+ else if ((dls->module == 0) || (dls->refs == 0))
+ error("handle to closed library");
+ else
+ return TRUE;
+ return FALSE;
+}
+
+static inline int isFlagSet(int mode, int flag)
+{
+ return (mode & flag) == flag;
+}
+
+static struct dlstatus *lookupStatus(const struct stat *sbuf)
+{
+ struct dlstatus *dls = stqueue;
+ debug("looking for status");
+ while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0
+ || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode))
+ dls = dls->next;
+ return dls;
+}
+
+static void insertStatus(struct dlstatus *dls, const struct stat *sbuf)
+{
+ debug("inserting status");
+ dls->inode = sbuf->st_ino;
+ dls->device = sbuf->st_dev;
+ dls->refs = 0;
+ dls->mode = 0;
+ if ((dls->flags & DL_IN_LIST) == 0)
+ {
+ dls->next = stqueue;
+ stqueue = dls;
+ dls->flags |= DL_IN_LIST;
+ }
+}
+
+static struct dlstatus *allocStatus()
+{
+ struct dlstatus *dls;
+#ifdef REUSE_STATUS
+ dls = stqueue;
+ while (dls && dls->module)
+ dls = dls->next;
+ if (!dls)
+#endif
+ dls = ast_malloc(sizeof(*dls));
+ dls->flags = 0;
+ return dls;
+}
+
+static int promoteLocalToGlobal(struct dlstatus *dls)
+{
+ static int (*p) (NSModule module) = 0;
+ debug("promoting");
+ if (!p)
+ _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p);
+ return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module));
+}
+
+static void *reference(struct dlstatus *dls, int mode)
+{
+ if (dls)
+ {
+ if (dls->module == MAGIC_DYLIB_MOD && !isFlagSet(mode, RTLD_GLOBAL))
+ {
+ warning("trying to open a .dylib with RTLD_LOCAL");
+ error("unable to open a .dylib with RTLD_LOCAL");
+ return NULL;
+ }
+ if (isFlagSet(mode, RTLD_GLOBAL) &&
+ !isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls))
+ {
+ error("unable to promote local module to global");
+ return NULL;
+ }
+ dls->mode |= mode;
+ dls->refs++;
+ }
+ else
+ debug("reference called with NULL argument");
+
+ return dls;
+}
+
+static const struct mach_header *my_find_image(const char *name)
+{
+ const struct mach_header *mh = 0;
+ const char *id = NULL;
+ int i = _dyld_image_count();
+ int j;
+ mh = (struct mach_header *)
+ dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED |
+ NSADDIMAGE_OPTION_RETURN_ON_ERROR);
+ if (!mh)
+ {
+ for (j = 0; j < i; j++)
+ {
+ id = _dyld_get_image_name(j);
+ if (!strcmp(id, name))
+ {
+ mh = _dyld_get_image_header(j);
+ break;
+ }
+ }
+ }
+ return mh;
+}
+
+/*
+ * dyld adds libraries by first adding the directly dependant libraries in link order, and
+ * then adding the dependencies for those libraries, so we should do the same... but we don't
+ * bother adding the extra dependencies, if the symbols are neither in the loaded image nor
+ * any of it's direct dependencies, then it probably isn't there.
+ */
+NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol)
+{
+ int n;
+ struct load_command *lc = 0;
+ struct mach_header *wh;
+ NSSymbol *nssym = 0;
+ if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd))
+ {
+ if ((wh = (struct mach_header *)
+ my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset +
+ (char *)lc))))
+ {
+ if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol))
+ {
+ nssym = dyld_NSLookupSymbolInImage(wh,
+ symbol,
+ NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
+ NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ break;
+ }
+ }
+ }
+ }
+ if ((!nssym) && NSIsSymbolNameDefined(symbol))
+ {
+ /* I've never seen this debug message...*/
+ debug("Symbol \"%s\" is defined but was not found", symbol);
+ }
+ }
+ return nssym;
+}
+
+/* Up to the caller to free() returned string */
+static inline const char *dyld_error_str()
+{
+ NSLinkEditErrors dylder;
+ int dylderno;
+ const char *dylderrstr;
+ const char *dyldfile;
+ const char* retStr = NULL;
+ NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr);
+ if (dylderrstr && strlen(dylderrstr))
+ {
+ retStr = ast_malloc(strlen(dylderrstr) +1);
+ strcpy((char*)retStr,dylderrstr);
+ }
+ return retStr;
+}
+
+static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError)
+{
+ NSSymbol *nssym = 0;
+ void *caller = __builtin_return_address(1); /* Be *very* careful about inlining */
+ const struct mach_header *caller_mh = 0;
+ const char* savedErrorStr = NULL;
+ resetdlerror();
+#ifndef RTLD_SELF
+#define RTLD_SELF ((void *) -3)
+#endif
+ if (NULL == dls)
+ dls = RTLD_SELF;
+ if ((RTLD_NEXT == dls) || (RTLD_SELF == dls))
+ {
+ if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ caller_mh = image_for_address(caller);
+ if (RTLD_SELF == dls)
+ {
+ /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE
+ * But it appears to work anyway, and looking at the code in dyld_libfuncs.c
+ * this is acceptable.
+ */
+ if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol))
+ {
+ nssym = dyld_NSLookupSymbolInImage(caller_mh,
+ symbol,
+ NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
+ NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ }
+ }
+ if (!nssym)
+ {
+ if (RTLD_SELF == dls)
+ savedErrorStr = dyld_error_str();
+ nssym = search_linked_libs(caller_mh, symbol);
+ }
+ }
+ else
+ {
+ if (canSetError)
+ error("RTLD_SELF and RTLD_NEXT are not supported");
+ return NULL;
+ }
+ }
+ if (!nssym)
+ {
+
+ if (RTLD_DEFAULT == dls)
+ {
+ dls = &mainStatus;
+ }
+ if (!isValidStatus(dls))
+ return NULL;
+
+ if (dls->module != MAGIC_DYLIB_MOD)
+ {
+ nssym = NSLookupSymbolInModule(dls->module, symbol);
+ if (!nssym && NSIsSymbolNameDefined(symbol))
+ {
+ debug("Searching dependencies");
+ savedErrorStr = dyld_error_str();
+ nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol);
+ }
+ }
+ else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol))
+ {
+ nssym = dyld_NSLookupSymbolInImage(dls->lib,
+ symbol,
+ NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
+ NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ }
+ else if (NSIsSymbolNameDefined(symbol))
+ {
+ debug("Searching dependencies");
+ savedErrorStr = dyld_error_str();
+ nssym = search_linked_libs(dls->lib, symbol);
+ }
+ }
+ else if (dls->module == MAGIC_DYLIB_MOD)
+ {
+ /* Global context, use NSLookupAndBindSymbol */
+ if (NSIsSymbolNameDefined(symbol))
+ {
+ /* There doesn't seem to be a return on error option for this call???
+ this is potentially broken, if binding fails, it will improperly
+ exit the application. */
+ nssym = NSLookupAndBindSymbol(symbol);
+ }
+ else
+ {
+ if (savedErrorStr)
+ ast_free(savedErrorStr);
+ savedErrorStr = ast_malloc(256);
+ snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol);
+ }
+ }
+ }
+ /* Error reporting */
+ if (!nssym)
+ {
+ if (!savedErrorStr || !strlen(savedErrorStr))
+ {
+ if (savedErrorStr)
+ ast_free(savedErrorStr);
+ savedErrorStr = ast_malloc(256);
+ snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol);
+ }
+ if (canSetError)
+ {
+ error(savedErrorStr);
+ }
+ else
+ {
+ debug(savedErrorStr);
+ }
+ if (savedErrorStr)
+ ast_free(savedErrorStr);
+ return NULL;
+ }
+ return NSAddressOfSymbol(nssym);
+}
+
+static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode)
+{
+ NSObjectFileImage ofi = 0;
+ NSObjectFileImageReturnCode ofirc;
+ struct dlstatus *dls;
+ NSLinkEditErrors ler;
+ int lerno;
+ const char *errstr;
+ const char *file;
+ void (*init) (void);
+ ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
+ switch (ofirc)
+ {
+ case NSObjectFileImageSuccess:
+ break;
+ case NSObjectFileImageInappropriateFile:
+ if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ if (!isFlagSet(mode, RTLD_GLOBAL))
+ {
+ warning("trying to open a .dylib with RTLD_LOCAL");
+ error("unable to open this file with RTLD_LOCAL");
+ return NULL;
+ }
+ }
+ else
+ {
+ error("opening this file is unsupported on this system");
+ return NULL;
+ }
+ break;
+ case NSObjectFileImageFailure:
+ error("object file setup failure");
+ return NULL;
+ case NSObjectFileImageArch:
+ error("no object for this architecture");
+ return NULL;
+ case NSObjectFileImageFormat:
+ error("bad object file format");
+ return NULL;
+ case NSObjectFileImageAccess:
+ error("can't read object file");
+ return NULL;
+ default:
+ error("unknown error from NSCreateObjectFileImageFromFile()");
+ return NULL;
+ }
+ dls = lookupStatus(sbuf);
+ if (!dls)
+ {
+ dls = allocStatus();
+ }
+ if (!dls)
+ {
+ error("unable to allocate memory");
+ return NULL;
+ }
+ dls->lib = 0;
+ if (ofirc == NSObjectFileImageInappropriateFile)
+ {
+ if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR)))
+ {
+ debug("Dynamic lib loaded at %ld", dls->lib);
+ ofi = MAGIC_DYLIB_OFI;
+ dls->module = MAGIC_DYLIB_MOD;
+ ofirc = NSObjectFileImageSuccess;
+ /* Although it is possible with a bit of work to modify this so it works and
+ functions with RTLD_NOW, I don't deem it necessary at the moment */
+ }
+ if (!(dls->module))
+ {
+ NSLinkEditError(&ler, &lerno, &file, &errstr);
+ if (!errstr || (!strlen(errstr)))
+ error("Can't open this file type");
+ else
+ error(errstr);
+ if ((dls->flags & DL_IN_LIST) == 0)
+ {
+ ast_free(dls);
+ }
+ return NULL;
+ }
+ }
+ else
+ {
+ dls->module = NSLinkModule(ofi, path,
+ NSLINKMODULE_OPTION_RETURN_ON_ERROR |
+ NSLINKMODULE_OPTION_PRIVATE |
+ (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0));
+ NSDestroyObjectFileImage(ofi);
+ if (dls->module)
+ {
+ dls->lib = get_mach_header_from_NSModule(dls->module);
+ }
+ }
+ if (!dls->module)
+ {
+ NSLinkEditError(&ler, &lerno, &file, &errstr);
+ if ((dls->flags & DL_IN_LIST) == 0)
+ {
+ ast_free(dls);
+ }
+ error(errstr);
+ return NULL;
+ }
+
+ insertStatus(dls, sbuf);
+ dls = reference(dls, mode);
+ if ((init = dlsymIntern(dls, "__init", 0)))
+ {
+ debug("calling _init()");
+ init();
+ }
+ return dls;
+}
+
+static void dlcompat_init_func(void)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage);
+ _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage",
+ (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage);
+ _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage);
+ if (pthread_mutex_init(&dlcompat_mutex, NULL))
+ exit(1);
+ if (pthread_key_create(&dlerror_key, &dlerrorfree))
+ exit(1);
+ /* And be neat and tidy and clean up after ourselves */
+ atexit(dlcompat_cleanup);
+ }
+}
+
+#if 0
+#pragma CALL_ON_LOAD dlcompat_init_func
+#endif
+
+static void dlcompat_cleanup(void)
+{
+ struct dlstatus *dls;
+ struct dlstatus *next;
+ char *data;
+ data = (char *)searchList();
+ if ( data )
+ ast_free(data);
+ data = (char *)getSearchPath(-1);
+ if ( data )
+ ast_free(data);
+ pthread_mutex_destroy(&dlcompat_mutex);
+ pthread_key_delete(dlerror_key);
+ next = stqueue;
+ while (next && (next != &mainStatus))
+ {
+ dls = next;
+ next = dls->next;
+ ast_free(dls);
+ }
+}
+
+static void resetdlerror()
+{
+ struct dlthread *tss;
+ tss = pthread_getspecific(dlerror_key);
+ tss->errset = 0;
+}
+
+static void dlerrorfree(void *data)
+{
+ ast_free(data);
+}
+
+/* We kind of want a recursive lock here, but meet a little trouble
+ * because they are not available pre OS X 10.2, so we fake it
+ * using thread specific storage to keep a lock count
+ */
+static inline void dolock(void)
+{
+ int err = 0;
+ struct dlthread *tss;
+ tss = pthread_getspecific(dlerror_key);
+ if (!tss)
+ {
+ tss = ast_malloc(sizeof(*tss));
+ tss->lockcnt = 0;
+ tss->errset = 0;
+ if (pthread_setspecific(dlerror_key, tss))
+ {
+ fprintf(stderr,"dlcompat: pthread_setspecific failed\n");
+ exit(1);
+ }
+ }
+ if (!tss->lockcnt)
+ err = pthread_mutex_lock(&dlcompat_mutex);
+ tss->lockcnt = tss->lockcnt +1;
+ if (err)
+ exit(err);
+}
+
+static inline void dounlock(void)
+{
+ int err = 0;
+ struct dlthread *tss;
+ tss = pthread_getspecific(dlerror_key);
+ tss->lockcnt = tss->lockcnt -1;
+ if (!tss->lockcnt)
+ err = pthread_mutex_unlock(&dlcompat_mutex);
+ if (err)
+ exit(err);
+}
+
+void *dlopen(const char *path, int mode)
+{
+ const struct stat *sbuf;
+ struct dlstatus *dls;
+ const char *fullPath;
+ dlcompat_init_func(); /* Just in case */
+ dolock();
+ resetdlerror();
+ if (!path)
+ {
+ dls = &mainStatus;
+ goto dlopenok;
+ }
+ if (!(sbuf = findFile(path, &fullPath)))
+ {
+ error("file \"%s\" not found", path);
+ goto dlopenerror;
+ }
+ /* Now checks that it hasn't been closed already */
+ if ((dls = lookupStatus(sbuf)) && (dls->refs > 0))
+ {
+ /* debug("status found"); */
+ dls = reference(dls, mode);
+ goto dlopenok;
+ }
+#ifdef RTLD_NOLOAD
+ if (isFlagSet(mode, RTLD_NOLOAD))
+ {
+ error("no existing handle and RTLD_NOLOAD specified");
+ goto dlopenerror;
+ }
+#endif
+ if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW))
+ {
+ error("how can I load something both RTLD_LAZY and RTLD_NOW?");
+ goto dlopenerror;
+ }
+ dls = loadModule(fullPath, sbuf, mode);
+
+ dlopenok:
+ dounlock();
+ return (void *)dls;
+ dlopenerror:
+ dounlock();
+ return NULL;
+}
+
+#if !FINK_BUILD
+void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
+{
+ int sym_len = strlen(symbol);
+ void *value = NULL;
+ char *malloc_sym = NULL;
+ dolock();
+ malloc_sym = ast_malloc(sym_len + 2);
+ if (malloc_sym)
+ {
+ sprintf(malloc_sym, "_%s", symbol);
+ value = dlsymIntern(handle, malloc_sym, 1);
+ ast_free(malloc_sym);
+ }
+ else
+ {
+ error("Unable to allocate memory");
+ goto dlsymerror;
+ }
+ dounlock();
+ return value;
+ dlsymerror:
+ dounlock();
+ return NULL;
+}
+#endif
+
+#if FINK_BUILD
+
+void *dlsym_prepend_underscore(void *handle, const char *symbol)
+{
+ void *answer;
+ dolock();
+ answer = dlsym_prepend_underscore_intern(handle, symbol);
+ dounlock();
+ return answer;
+}
+
+static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol)
+{
+/*
+ * A quick and easy way for porting packages which call dlsym(handle,"sym")
+ * If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then
+ * this function will be called, and will add the required underscore.
+ *
+ * Note that I haven't figured out yet which should be "standard", prepend
+ * the underscore always, or not at all. These global functions need to go away
+ * for opendarwin.
+ */
+ int sym_len = strlen(symbol);
+ void *value = NULL;
+ char *malloc_sym = NULL;
+ malloc_sym = ast_malloc(sym_len + 2);
+ if (malloc_sym)
+ {
+ sprintf(malloc_sym, "_%s", symbol);
+ value = dlsymIntern(handle, malloc_sym, 1);
+ ast_free(malloc_sym);
+ }
+ else
+ {
+ error("Unable to allocate memory");
+ }
+ return value;
+}
+
+void *dlsym_auto_underscore(void *handle, const char *symbol)
+{
+ void *answer;
+ dolock();
+ answer = dlsym_auto_underscore_intern(handle, symbol);
+ dounlock();
+ return answer;
+
+}
+static void *dlsym_auto_underscore_intern(void *handle, const char *symbol)
+{
+ struct dlstatus *dls = handle;
+ void *addr = 0;
+ addr = dlsymIntern(dls, symbol, 0);
+ if (!addr)
+ addr = dlsym_prepend_underscore_intern(handle, symbol);
+ return addr;
+}
+
+
+void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
+{
+ struct dlstatus *dls = handle;
+ void *addr = 0;
+ dolock();
+ addr = dlsymIntern(dls, symbol, 1);
+ dounlock();
+ return addr;
+}
+#endif
+
+int dlclose(void *handle)
+{
+ struct dlstatus *dls = handle;
+ dolock();
+ resetdlerror();
+ if (!isValidStatus(dls))
+ {
+ goto dlcloseerror;
+ }
+ if (dls->module == MAGIC_DYLIB_MOD)
+ {
+ const char *name;
+ if (!dls->lib)
+ {
+ name = "global context";
+ }
+ else
+ {
+ name = get_lib_name(dls->lib);
+ }
+ warning("trying to close a .dylib!");
+ error("Not closing \"%s\" - dynamic libraries cannot be closed", name);
+ goto dlcloseerror;
+ }
+ if (!dls->module)
+ {
+ error("module already closed");
+ goto dlcloseerror;
+ }
+
+ if (dls->refs == 1)
+ {
+ unsigned long options = 0;
+ void (*fini) (void);
+ if ((fini = dlsymIntern(dls, "__fini", 0)))
+ {
+ debug("calling _fini()");
+ fini();
+ }
+#ifdef __ppc__
+ options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
+#endif
+#if 1
+/* Currently, if a module contains c++ static destructors and it is unloaded, we
+ * get a segfault in atexit(), due to compiler and dynamic loader differences of
+ * opinion, this works around that.
+ * I really need a way to figure out from code if this is still necessary.
+ */
+ if ((const struct section *)NULL !=
+ getsectbynamefromheader(get_mach_header_from_NSModule(dls->module),
+ "__DATA", "__mod_term_func"))
+ {
+ options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
+ }
+#endif
+#ifdef RTLD_NODELETE
+ if (isFlagSet(dls->mode, RTLD_NODELETE))
+ options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
+#endif
+ if (!NSUnLinkModule(dls->module, options))
+ {
+ error("unable to unlink module");
+ goto dlcloseerror;
+ }
+ dls->refs--;
+ dls->module = 0;
+ /* Note: the dlstatus struct dls is neither removed from the list
+ * nor is the memory it occupies freed. This shouldn't pose a
+ * problem in mostly all cases, though.
+ */
+ }
+ dounlock();
+ return 0;
+ dlcloseerror:
+ dounlock();
+ return 1;
+}
+
+const char *dlerror(void)
+{
+ struct dlthread *tss;
+ char * err_str;
+ tss = pthread_getspecific(dlerror_key);
+ err_str = tss->errstr;
+ tss = pthread_getspecific(dlerror_key);
+ if (tss->errset == 0)
+ return 0;
+ tss->errset = 0;
+ return (err_str );
+}
+
+/* Given an address, return the mach_header for the image containing it
+ * or zero if the given address is not contained in any loaded images.
+ */
+const struct mach_header *image_for_address(const void *address)
+{
+ unsigned long i;
+ unsigned long j;
+ unsigned long count = _dyld_image_count();
+ struct mach_header *mh = 0;
+ struct load_command *lc = 0;
+ unsigned long addr = NULL;
+ for (i = 0; i < count; i++)
+ {
+ addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i);
+ mh = _dyld_get_image_header(i);
+ if (mh)
+ {
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SEGMENT == lc->cmd &&
+ addr >= ((struct segment_command *)lc)->vmaddr &&
+ addr <
+ ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
+ {
+ goto image_found;
+ }
+ }
+ }
+ mh = 0;
+ }
+ image_found:
+ return mh;
+}
+
+int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info)
+{
+/*
+ FIXME: USe the routine image_for_address.
+*/
+ unsigned long i;
+ unsigned long j;
+ unsigned long count = _dyld_image_count();
+ struct mach_header *mh = 0;
+ struct load_command *lc = 0;
+ unsigned long addr = NULL;
+ unsigned long table_off = (unsigned long)0;
+ int found = 0;
+ if (!info)
+ return 0;
+ dolock();
+ resetdlerror();
+ info->dli_fname = 0;
+ info->dli_fbase = 0;
+ info->dli_sname = 0;
+ info->dli_saddr = 0;
+/* Some of this was swiped from code posted by Douglas Davidson <ddavidso AT apple DOT com>
+ * to darwin-development AT lists DOT apple DOT com and slightly modified
+ */
+ for (i = 0; i < count; i++)
+ {
+ addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i);
+ mh = _dyld_get_image_header(i);
+ if (mh)
+ {
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SEGMENT == lc->cmd &&
+ addr >= ((struct segment_command *)lc)->vmaddr &&
+ addr <
+ ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
+ {
+ info->dli_fname = _dyld_get_image_name(i);
+ info->dli_fbase = (void *)mh;
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ }
+ if (!found)
+ {
+ dounlock();
+ return 0;
+ }
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SEGMENT == lc->cmd)
+ {
+ if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT"))
+ break;
+ }
+ }
+ table_off =
+ ((unsigned long)((struct segment_command *)lc)->vmaddr) -
+ ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i);
+ debug("table off %x", table_off);
+
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SYMTAB == lc->cmd)
+ {
+
+ struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off);
+ unsigned long numsyms = ((struct symtab_command *)lc)->nsyms;
+ struct nlist *nearest = NULL;
+ unsigned long diff = 0xffffffff;
+ unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off);
+ debug("symtable %x", symtable);
+ for (i = 0; i < numsyms; i++)
+ {
+ /* Ignore the following kinds of Symbols */
+ if ((!symtable->n_value) /* Undefined */
+ || (symtable->n_type >= N_PEXT) /* Debug symbol */
+ || (!(symtable->n_type & N_EXT)) /* Local Symbol */
+ )
+ {
+ symtable++;
+ continue;
+ }
+ if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr)))
+ {
+ diff = (unsigned long)symtable->n_value - addr;
+ nearest = symtable;
+ }
+ symtable++;
+ }
+ if (nearest)
+ {
+ info->dli_saddr = nearest->n_value + ((void *)p - addr);
+ info->dli_sname = (char *)(strtable + nearest->n_un.n_strx);
+ }
+ }
+ }
+ dounlock();
+ return 1;
+}
+
+
+/*
+ * Implement the dlfunc() interface, which behaves exactly the same as
+ * dlsym() except that it returns a function pointer instead of a data
+ * pointer. This can be used by applications to avoid compiler warnings
+ * about undefined behavior, and is intended as prior art for future
+ * POSIX standardization. This function requires that all pointer types
+ * have the same representation, which is true on all platforms FreeBSD
+ * runs on, but is not guaranteed by the C standard.
+ */
+#if 0
+dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol)
+{
+ union
+ {
+ void *d;
+ dlfunc_t f;
+ } rv;
+ int sym_len = strlen(symbol);
+ char *malloc_sym = NULL;
+ dolock();
+ malloc_sym = ast_malloc(sym_len + 2);
+ if (malloc_sym)
+ {
+ sprintf(malloc_sym, "_%s", symbol);
+ rv.d = dlsymIntern(handle, malloc_sym, 1);
+ ast_free(malloc_sym);
+ }
+ else
+ {
+ error("Unable to allocate memory");
+ goto dlfuncerror;
+ }
+ dounlock();
+ return rv.f;
+ dlfuncerror:
+ dounlock();
+ return NULL;
+}
+#endif
diff --git a/trunk/main/dns.c b/trunk/main/dns.c
new file mode 100644
index 000000000..34212fdf1
--- /dev/null
+++ b/trunk/main/dns.c
@@ -0,0 +1,299 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006 Thorsten Lockert
+ *
+ * Written by Thorsten Lockert <tholo@trollphone.org>
+ *
+ * Funding provided by Troll Phone Networks AS
+ *
+ * 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 DNS Support for Asterisk
+ *
+ * \author Thorsten Lockert <tholo@trollphone.org>
+ *
+ * \par Reference
+ * - DNR SRV records http://www.ietf.org/rfc/rfc2782.txt
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/network.h"
+#include <arpa/nameser.h> /* res_* functions */
+#include <resolv.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/dns.h"
+#include "asterisk/endian.h"
+
+#define MAX_SIZE 4096
+
+#ifdef __PDP_ENDIAN
+#if __BYTE_ORDER == __PDP_ENDIAN
+#define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
+#endif
+#endif
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define DETERMINED_BYTE_ORDER __BIG_ENDIAN
+#endif
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
+#endif
+
+/* The dns_HEADER structure definition below originated
+ in the arpa/nameser.h header file distributed with ISC
+ BIND, which contains the following copyright and license
+ notices:
+
+ * ++Copyright++ 1983, 1989, 1993
+ * -
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, 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.
+ * -
+ * --Copyright--
+ */
+
+typedef struct {
+ unsigned id:16; /*!< query identification number */
+#if DETERMINED_BYTE_ORDER == __BIG_ENDIAN
+ /* fields in third byte */
+ unsigned qr:1; /*!< response flag */
+ unsigned opcode:4; /*!< purpose of message */
+ unsigned aa:1; /*!< authoritive answer */
+ unsigned tc:1; /*!< truncated message */
+ unsigned rd:1; /*!< recursion desired */
+ /* fields in fourth byte */
+ unsigned ra:1; /*!< recursion available */
+ unsigned unused:1; /*!< unused bits (MBZ as of 4.9.3a3) */
+ unsigned ad:1; /*!< authentic data from named */
+ unsigned cd:1; /*!< checking disabled by resolver */
+ unsigned rcode:4; /*!< response code */
+#endif
+#if DETERMINED_BYTE_ORDER == __LITTLE_ENDIAN
+ /* fields in third byte */
+ unsigned rd:1; /*!< recursion desired */
+ unsigned tc:1; /*!< truncated message */
+ unsigned aa:1; /*!< authoritive answer */
+ unsigned opcode:4; /*!< purpose of message */
+ unsigned qr:1; /*!< response flag */
+ /* fields in fourth byte */
+ unsigned rcode:4; /*!< response code */
+ unsigned cd:1; /*!< checking disabled by resolver */
+ unsigned ad:1; /*!< authentic data from named */
+ unsigned unused:1; /*!< unused bits (MBZ as of 4.9.3a3) */
+ unsigned ra:1; /*!< recursion available */
+#endif
+ /* remaining bytes */
+ unsigned qdcount:16; /*!< number of question entries */
+ unsigned ancount:16; /*!< number of answer entries */
+ unsigned nscount:16; /*!< number of authority entries */
+ unsigned arcount:16; /*!< number of resource entries */
+} dns_HEADER;
+
+struct dn_answer {
+ unsigned short rtype;
+ unsigned short class;
+ unsigned int ttl;
+ unsigned short size;
+} __attribute__ ((__packed__));
+
+static int skip_name(unsigned char *s, int len)
+{
+ int x = 0;
+
+ while (x < len) {
+ if (*s == '\0') {
+ s++;
+ x++;
+ break;
+ }
+ if ((*s & 0xc0) == 0xc0) {
+ s += 2;
+ x += 2;
+ break;
+ }
+ x += *s + 1;
+ s += *s + 1;
+ }
+ if (x >= len)
+ return -1;
+ return x;
+}
+
+/*! \brief Parse DNS lookup result, call callback */
+static int dns_parse_answer(void *context,
+ int class, int type, unsigned char *answer, int len,
+ int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
+{
+ unsigned char *fullanswer = answer;
+ struct dn_answer *ans;
+ dns_HEADER *h;
+ int res;
+ int x;
+
+ h = (dns_HEADER *)answer;
+ answer += sizeof(dns_HEADER);
+ len -= sizeof(dns_HEADER);
+
+ for (x = 0; x < ntohs(h->qdcount); x++) {
+ if ((res = skip_name(answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Couldn't skip over name\n");
+ return -1;
+ }
+ answer += res + 4; /* Skip name and QCODE / QCLASS */
+ len -= res + 4;
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Strange query size\n");
+ return -1;
+ }
+ }
+
+ for (x = 0; x < ntohs(h->ancount); x++) {
+ if ((res = skip_name(answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed skipping name\n");
+ return -1;
+ }
+ answer += res;
+ len -= res;
+ ans = (struct dn_answer *)answer;
+ answer += sizeof(struct dn_answer);
+ len -= sizeof(struct dn_answer);
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Strange result size\n");
+ return -1;
+ }
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Length exceeds frame\n");
+ return -1;
+ }
+
+ if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
+ if (callback) {
+ if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
+ ast_log(LOG_WARNING, "Failed to parse result\n");
+ return -1;
+ }
+ if (res > 0)
+ return 1;
+ }
+ }
+ answer += ntohs(ans->size);
+ len -= ntohs(ans->size);
+ }
+ return 0;
+}
+
+#ifndef HAVE_RES_NINIT
+AST_MUTEX_DEFINE_STATIC(res_lock);
+#endif
+
+/*! \brief Lookup record in DNS
+\note Asterisk DNS is synchronus at this time. This means that if your DNS does
+not work properly, Asterisk might not start properly or a channel may lock.
+*/
+int ast_search_dns(void *context,
+ const char *dname, int class, int type,
+ int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
+{
+#ifdef HAVE_RES_NINIT
+ struct __res_state dnsstate;
+#endif
+ unsigned char answer[MAX_SIZE];
+ int res, ret = -1;
+
+#ifdef HAVE_RES_NINIT
+ res_ninit(&dnsstate);
+ res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
+#else
+ ast_mutex_lock(&res_lock);
+ res_init();
+ res = res_search(dname, class, type, answer, sizeof(answer));
+#endif
+ if (res > 0) {
+ if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
+ ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
+ ret = -1;
+ }
+ else if (res == 0) {
+ ast_debug(1, "No matches found in DNS for %s\n", dname);
+ ret = 0;
+ }
+ else
+ ret = 1;
+ }
+#ifdef HAVE_RES_NINIT
+#ifdef HAVE_RES_NDESTROY
+ res_ndestroy(&dnsstate);
+#else
+ res_nclose(&dnsstate);
+#endif
+#else
+#ifndef __APPLE__
+ res_close();
+#endif
+ ast_mutex_unlock(&res_lock);
+#endif
+
+ return ret;
+}
diff --git a/trunk/main/dnsmgr.c b/trunk/main/dnsmgr.c
new file mode 100644
index 000000000..ed2426c2e
--- /dev/null
+++ b/trunk/main/dnsmgr.c
@@ -0,0 +1,425 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Kevin P. Fleming
+ *
+ * Kevin P. Fleming <kpfleming@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 Background DNS update manager
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include <regex.h>
+#include <signal.h>
+
+#include "asterisk/dnsmgr.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/utils.h"
+#include "asterisk/config.h"
+#include "asterisk/sched.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+
+static struct sched_context *sched;
+static int refresh_sched = -1;
+static pthread_t refresh_thread = AST_PTHREADT_NULL;
+
+struct ast_dnsmgr_entry {
+ /*! where we will store the resulting address */
+ struct in_addr *result;
+ /*! the last result, used to check if address has changed */
+ struct in_addr last;
+ /*! Set to 1 if the entry changes */
+ int changed:1;
+ ast_mutex_t lock;
+ AST_RWLIST_ENTRY(ast_dnsmgr_entry) list;
+ /*! just 1 here, but we use calloc to allocate the correct size */
+ char name[1];
+};
+
+static AST_RWLIST_HEAD_STATIC(entry_list, ast_dnsmgr_entry);
+
+AST_MUTEX_DEFINE_STATIC(refresh_lock);
+
+#define REFRESH_DEFAULT 300
+
+static int enabled;
+static int refresh_interval;
+
+struct refresh_info {
+ struct entry_list *entries;
+ int verbose;
+ unsigned int regex_present:1;
+ regex_t filter;
+};
+
+static struct refresh_info master_refresh_info = {
+ .entries = &entry_list,
+ .verbose = 0,
+};
+
+struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result)
+{
+ struct ast_dnsmgr_entry *entry;
+
+ if (!result || ast_strlen_zero(name) || !(entry = ast_calloc(1, sizeof(*entry) + strlen(name))))
+ return NULL;
+
+ entry->result = result;
+ ast_mutex_init(&entry->lock);
+ strcpy(entry->name, name);
+ memcpy(&entry->last, result, sizeof(entry->last));
+
+ AST_RWLIST_WRLOCK(&entry_list);
+ AST_RWLIST_INSERT_HEAD(&entry_list, entry, list);
+ AST_RWLIST_UNLOCK(&entry_list);
+
+ return entry;
+}
+
+void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
+{
+ if (!entry)
+ return;
+
+ AST_RWLIST_WRLOCK(&entry_list);
+ AST_RWLIST_REMOVE(&entry_list, entry, list);
+ AST_RWLIST_UNLOCK(&entry_list);
+ ast_verb(4, "removing dns manager for '%s'\n", entry->name);
+
+ ast_mutex_destroy(&entry->lock);
+ ast_free(entry);
+}
+
+int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
+{
+ struct ast_hostent ahp;
+ struct hostent *hp;
+
+ if (ast_strlen_zero(name) || !result || !dnsmgr)
+ return -1;
+
+ if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
+ return 0;
+
+ ast_verb(4, "doing dnsmgr_lookup for '%s'\n", name);
+
+ /* if it's actually an IP address and not a name,
+ there's no need for a managed lookup */
+ if (inet_aton(name, result))
+ return 0;
+
+ /* do a lookup now but add a manager so it will automagically get updated in the background */
+ if ((hp = ast_gethostbyname(name, &ahp)))
+ memcpy(result, hp->h_addr, sizeof(result));
+
+ /* if dnsmgr is not enable don't bother adding an entry */
+ if (!enabled)
+ return 0;
+
+ ast_verb(3, "adding dns manager for '%s'\n", name);
+ *dnsmgr = ast_dnsmgr_get(name, result);
+ return !*dnsmgr;
+}
+
+/*
+ * Refresh a dnsmgr entry
+ */
+static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose)
+{
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ char iabuf[INET_ADDRSTRLEN];
+ char iabuf2[INET_ADDRSTRLEN];
+ struct in_addr tmp;
+ int changed = 0;
+
+ ast_mutex_lock(&entry->lock);
+ if (verbose)
+ ast_verb(3, "refreshing '%s'\n", entry->name);
+
+ if ((hp = ast_gethostbyname(entry->name, &ahp))) {
+ /* check to see if it has changed, do callback if requested (where de callback is defined ????) */
+ memcpy(&tmp, hp->h_addr, sizeof(tmp));
+ if (tmp.s_addr != entry->last.s_addr) {
+ ast_copy_string(iabuf, ast_inet_ntoa(entry->last), sizeof(iabuf));
+ ast_copy_string(iabuf2, ast_inet_ntoa(tmp), sizeof(iabuf2));
+ ast_log(LOG_NOTICE, "host '%s' changed from %s to %s\n",
+ entry->name, iabuf, iabuf2);
+ memcpy(entry->result, hp->h_addr, sizeof(entry->result));
+ memcpy(&entry->last, hp->h_addr, sizeof(entry->last));
+ changed = entry->changed = 1;
+ }
+
+ }
+ ast_mutex_unlock(&entry->lock);
+ return changed;
+}
+
+int ast_dnsmgr_refresh(struct ast_dnsmgr_entry *entry)
+{
+ return dnsmgr_refresh(entry, 0);
+}
+
+/*
+ * Check if dnsmgr entry has changed from since last call to this function
+ */
+int ast_dnsmgr_changed(struct ast_dnsmgr_entry *entry)
+{
+ int changed;
+
+ ast_mutex_lock(&entry->lock);
+
+ changed = entry->changed;
+ entry->changed = 0;
+
+ ast_mutex_unlock(&entry->lock);
+
+ return changed;
+}
+
+static void *do_refresh(void *data)
+{
+ for (;;) {
+ pthread_testcancel();
+ usleep((ast_sched_wait(sched)*1000));
+ pthread_testcancel();
+ ast_sched_runq(sched);
+ }
+ return NULL;
+}
+
+static int refresh_list(const void *data)
+{
+ struct refresh_info *info = (struct refresh_info *)data;
+ struct ast_dnsmgr_entry *entry;
+
+ /* if a refresh or reload is already in progress, exit now */
+ if (ast_mutex_trylock(&refresh_lock)) {
+ if (info->verbose)
+ ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
+ return -1;
+ }
+
+ ast_verb(3, "Refreshing DNS lookups.\n");
+ AST_RWLIST_RDLOCK(info->entries);
+ AST_RWLIST_TRAVERSE(info->entries, entry, list) {
+ if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
+ continue;
+
+ dnsmgr_refresh(entry, info->verbose);
+ }
+ AST_RWLIST_UNLOCK(info->entries);
+
+ ast_mutex_unlock(&refresh_lock);
+
+ /* automatically reschedule based on the interval */
+ return refresh_interval * 1000;
+}
+
+void dnsmgr_start_refresh(void)
+{
+ if (refresh_sched > -1) {
+ ast_sched_del(sched, refresh_sched);
+ refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
+ }
+}
+
+static int do_reload(int loading);
+
+static char *handle_cli_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dnsmgr reload";
+ e->usage =
+ "Usage: dnsmgr reload\n"
+ " Reloads the DNS manager configuration.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ do_reload(0);
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_refresh(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct refresh_info info = {
+ .entries = &entry_list,
+ .verbose = 1,
+ };
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dnsmgr refresh";
+ e->usage =
+ "Usage: dnsmgr refresh [pattern]\n"
+ " Peforms an immediate refresh of the managed DNS entries.\n"
+ " Optional regular expression pattern is used to filter the entries to refresh.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 3) {
+ if ( regcomp(&info.filter, a->argv[2], REG_EXTENDED | REG_NOSUB) )
+ return CLI_SHOWUSAGE;
+ else
+ info.regex_present = 1;
+ }
+
+ refresh_list(&info);
+
+ if (info.regex_present)
+ regfree(&info.filter);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int count = 0;
+ struct ast_dnsmgr_entry *entry;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dnsmgr status";
+ e->usage =
+ "Usage: dnsmgr status\n"
+ " Displays the DNS manager status.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
+ ast_cli(a->fd, "Refresh Interval: %d seconds\n", refresh_interval);
+ AST_RWLIST_RDLOCK(&entry_list);
+ AST_RWLIST_TRAVERSE(&entry_list, entry, list)
+ count++;
+ AST_RWLIST_UNLOCK(&entry_list);
+ ast_cli(a->fd, "Number of entries: %d\n", count);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_reload = AST_CLI_DEFINE(handle_cli_reload, "Reloads the DNS manager configuration");
+static struct ast_cli_entry cli_refresh = AST_CLI_DEFINE(handle_cli_refresh, "Performs an immediate refresh");
+static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the DNS manager status");
+
+int dnsmgr_init(void)
+{
+ if (!(sched = sched_context_create())) {
+ ast_log(LOG_ERROR, "Unable to create schedule context.\n");
+ return -1;
+ }
+ ast_cli_register(&cli_reload);
+ ast_cli_register(&cli_status);
+ ast_cli_register(&cli_refresh);
+ return do_reload(1);
+}
+
+int dnsmgr_reload(void)
+{
+ return do_reload(0);
+}
+
+static int do_reload(int loading)
+{
+ struct ast_config *config;
+ struct ast_flags config_flags = { loading ? 0 : CONFIG_FLAG_FILEUNCHANGED };
+ const char *interval_value;
+ const char *enabled_value;
+ int interval;
+ int was_enabled;
+ int res = -1;
+
+ if ((config = ast_config_load("dnsmgr.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ /* ensure that no refresh cycles run while the reload is in progress */
+ ast_mutex_lock(&refresh_lock);
+
+ /* reset defaults in preparation for reading config file */
+ refresh_interval = REFRESH_DEFAULT;
+ was_enabled = enabled;
+ enabled = 0;
+
+ if (refresh_sched > -1)
+ ast_sched_del(sched, refresh_sched);
+
+ if (config) {
+ if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
+ enabled = ast_true(enabled_value);
+ }
+ if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
+ if (sscanf(interval_value, "%d", &interval) < 1)
+ ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
+ else if (interval < 0)
+ ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
+ else
+ refresh_interval = interval;
+ }
+ ast_config_destroy(config);
+ }
+
+ if (enabled && refresh_interval)
+ ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
+
+ /* if this reload enabled the manager, create the background thread
+ if it does not exist */
+ if (enabled) {
+ if (!was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
+ if (ast_pthread_create_background(&refresh_thread, NULL, do_refresh, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
+ }
+ }
+ /* make a background refresh happen right away */
+ refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
+ res = 0;
+ }
+ /* if this reload disabled the manager and there is a background thread,
+ kill it */
+ else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
+ /* wake up the thread so it will exit */
+ pthread_cancel(refresh_thread);
+ pthread_kill(refresh_thread, SIGURG);
+ pthread_join(refresh_thread, NULL);
+ refresh_thread = AST_PTHREADT_NULL;
+ ast_cli_unregister(&cli_refresh);
+ res = 0;
+ }
+ else
+ res = 0;
+
+ ast_mutex_unlock(&refresh_lock);
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: DNSmgr\r\nStatus: %s\r/nMessage: DNSmgr reload Requested\r\n", enabled ? "Enabled" : "Disabled");
+
+ return res;
+}
diff --git a/trunk/main/dsp.c b/trunk/main/dsp.c
new file mode 100644
index 000000000..8355aca4e
--- /dev/null
+++ b/trunk/main/dsp.c
@@ -0,0 +1,1349 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Goertzel routines are borrowed from Steve Underwood's tremendous work on the
+ * DTMF detector.
+ *
+ * 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 Convenience Signal Processing routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Steve Underwood <steveu@coppice.org>
+ */
+
+/* Some routines from tone_detect.c by Steven Underwood as published under the zapata library */
+/*
+ tone_detect.c - General telephony tone detection, and specific
+ detection of DTMF.
+
+ Copyright (C) 2001 Steve Underwood <steveu@coppice.org>
+
+ Despite my general liking of the GPL, I place this code in the
+ public domain for the benefit of all mankind - even the slimy
+ ones who might try to proprietize my work and use it to my
+ detriment.
+*/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/dsp.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+
+/*! Number of goertzels for progress detect */
+enum gsamp_size {
+ GSAMP_SIZE_NA = 183, /*!< North America - 350, 440, 480, 620, 950, 1400, 1800 Hz */
+ GSAMP_SIZE_CR = 188, /*!< Costa Rica, Brazil - Only care about 425 Hz */
+ GSAMP_SIZE_UK = 160 /*!< UK disconnect goertzel feed - should trigger 400hz */
+};
+
+enum prog_mode {
+ PROG_MODE_NA = 0,
+ PROG_MODE_CR,
+ PROG_MODE_UK
+};
+
+enum freq_index {
+ /*! For US modes { */
+ HZ_350 = 0,
+ HZ_440,
+ HZ_480,
+ HZ_620,
+ HZ_950,
+ HZ_1400,
+ HZ_1800, /*!< } */
+
+ /*! For CR/BR modes */
+ HZ_425 = 0,
+
+ /*! For UK mode */
+ HZ_400 = 0
+};
+
+static struct progalias {
+ char *name;
+ enum prog_mode mode;
+} aliases[] = {
+ { "us", PROG_MODE_NA },
+ { "ca", PROG_MODE_NA },
+ { "cr", PROG_MODE_CR },
+ { "br", PROG_MODE_CR },
+ { "uk", PROG_MODE_UK },
+};
+
+static struct progress {
+ enum gsamp_size size;
+ int freqs[7];
+} modes[] = {
+ { GSAMP_SIZE_NA, { 350, 440, 480, 620, 950, 1400, 1800 } }, /*!< North America */
+ { GSAMP_SIZE_CR, { 425 } }, /*!< Costa Rica, Brazil */
+ { GSAMP_SIZE_UK, { 400 } }, /*!< UK */
+};
+
+#define DEFAULT_THRESHOLD 512
+
+enum busy_detect {
+ BUSY_PERCENT = 10, /*!< The percentage difference between the two last silence periods */
+ BUSY_PAT_PERCENT = 7, /*!< The percentage difference between measured and actual pattern */
+ BUSY_THRESHOLD = 100, /*!< Max number of ms difference between max and min times in busy */
+ BUSY_MIN = 75, /*!< Busy must be at least 80 ms in half-cadence */
+ BUSY_MAX =3100 /*!< Busy can't be longer than 3100 ms in half-cadence */
+};
+
+/*! Remember last 15 units */
+#define DSP_HISTORY 15
+
+#define TONE_THRESH 10.0 /*!< How much louder the tone should be than channel energy */
+#define TONE_MIN_THRESH 1e8 /*!< How much tone there should be at least to attempt */
+
+/*! All THRESH_XXX values are in GSAMP_SIZE chunks (us = 22ms) */
+enum gsamp_thresh {
+ THRESH_RING = 8, /*!< Need at least 150ms ring to accept */
+ THRESH_TALK = 2, /*!< Talk detection does not work continuously */
+ THRESH_BUSY = 4, /*!< Need at least 80ms to accept */
+ THRESH_CONGESTION = 4, /*!< Need at least 80ms to accept */
+ THRESH_HANGUP = 60, /*!< Need at least 1300ms to accept hangup */
+ THRESH_RING2ANSWER = 300 /*!< Timeout from start of ring to answer (about 6600 ms) */
+};
+
+#define MAX_DTMF_DIGITS 128
+
+/* Basic DTMF specs:
+ *
+ * Minimum tone on = 40ms
+ * Minimum tone off = 50ms
+ * Maximum digit rate = 10 per second
+ * Normal twist <= 8dB accepted
+ * Reverse twist <= 4dB accepted
+ * S/N >= 15dB will detect OK
+ * Attenuation <= 26dB will detect OK
+ * Frequency tolerance +- 1.5% will detect, +-3.5% will reject
+ */
+
+#define DTMF_THRESHOLD 8.0e7
+#define FAX_THRESHOLD 8.0e7
+#define FAX_2ND_HARMONIC 2.0 /* 4dB */
+#define DTMF_NORMAL_TWIST 6.3 /* 8dB */
+#ifdef RADIO_RELAX
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 6.5 : 2.5) /* 4dB normal */
+#else
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5) /* 4dB normal */
+#endif
+#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */
+#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */
+#define DTMF_2ND_HARMONIC_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 1.7 : 2.5) /* 4dB normal */
+#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */
+#define DTMF_TO_TOTAL_ENERGY 42.0
+
+#define BELL_MF_THRESHOLD 1.6e9
+#define BELL_MF_TWIST 4.0 /* 6dB */
+#define BELL_MF_RELATIVE_PEAK 12.6 /* 11dB */
+
+#if defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_COMPARE_TONE_AND_SILENCE)
+#error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE
+#endif
+
+typedef struct {
+ int v2;
+ int v3;
+ int chunky;
+ int fac;
+ int samples;
+} goertzel_state_t;
+
+typedef struct {
+ int value;
+ int power;
+} goertzel_result_t;
+
+typedef struct
+{
+ goertzel_state_t row_out[4];
+ goertzel_state_t col_out[4];
+ goertzel_state_t fax_tone;
+ int lasthit;
+ int mhit;
+ float energy;
+ int current_sample;
+
+ char digits[MAX_DTMF_DIGITS + 1];
+
+ int current_digits;
+ int detected_digits;
+ int lost_digits;
+ int digit_hits[16];
+ int fax_hits;
+} dtmf_detect_state_t;
+
+typedef struct
+{
+ goertzel_state_t tone_out[6];
+ int mhit;
+ int hits[5];
+ int current_sample;
+
+ char digits[MAX_DTMF_DIGITS + 1];
+
+ int current_digits;
+ int detected_digits;
+ int lost_digits;
+} mf_detect_state_t;
+
+static float dtmf_row[] =
+{
+ 697.0, 770.0, 852.0, 941.0
+};
+static float dtmf_col[] =
+{
+ 1209.0, 1336.0, 1477.0, 1633.0
+};
+
+static float mf_tones[] =
+{
+ 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0
+};
+
+static float fax_freq = 1100.0;
+
+static char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
+
+static char bell_mf_positions[] = "1247C-358A--69*---0B----#";
+
+static inline void goertzel_sample(goertzel_state_t *s, short sample)
+{
+ int v1;
+
+ v1 = s->v2;
+ s->v2 = s->v3;
+
+ s->v3 = (s->fac * s->v2) >> 15;
+ s->v3 = s->v3 - v1 + (sample >> s->chunky);
+ if (abs(s->v3) > 32768) {
+ s->chunky++;
+ s->v3 = s->v3 >> 1;
+ s->v2 = s->v2 >> 1;
+ v1 = v1 >> 1;
+ }
+}
+
+static inline void goertzel_update(goertzel_state_t *s, short *samps, int count)
+{
+ int i;
+
+ for (i=0;i<count;i++)
+ goertzel_sample(s, samps[i]);
+}
+
+
+static inline float goertzel_result(goertzel_state_t *s)
+{
+ goertzel_result_t r;
+ r.value = (s->v3 * s->v3) + (s->v2 * s->v2);
+ r.value -= ((s->v2 * s->v3) >> 15) * s->fac;
+ r.power = s->chunky * 2;
+ return (float)r.value * (float)(1 << r.power);
+}
+
+static inline void goertzel_init(goertzel_state_t *s, float freq, int samples)
+{
+ s->v2 = s->v3 = s->chunky = 0.0;
+ s->fac = (int)(32768.0 * 2.0 * cos(2.0 * M_PI * (freq / 8000.0)));
+ s->samples = samples;
+}
+
+static inline void goertzel_reset(goertzel_state_t *s)
+{
+ s->v2 = s->v3 = s->chunky = 0.0;
+}
+
+struct ast_dsp {
+ struct ast_frame f;
+ int threshold;
+ int totalsilence;
+ int totalnoise;
+ int features;
+ int ringtimeout;
+ int busymaybe;
+ int busycount;
+ int busy_tonelength;
+ int busy_quietlength;
+ int historicnoise[DSP_HISTORY];
+ int historicsilence[DSP_HISTORY];
+ goertzel_state_t freqs[7];
+ int freqcount;
+ int gsamps;
+ enum gsamp_size gsamp_size;
+ enum prog_mode progmode;
+ int tstate;
+ int tcount;
+ int digitmode;
+ int thinkdigit;
+ float genergy;
+ union {
+ dtmf_detect_state_t dtmf;
+ mf_detect_state_t mf;
+ } td;
+};
+
+static void ast_dtmf_detect_init (dtmf_detect_state_t *s)
+{
+ int i;
+
+ s->lasthit = 0;
+ for (i = 0; i < 4; i++) {
+ goertzel_init (&s->row_out[i], dtmf_row[i], 102);
+ goertzel_init (&s->col_out[i], dtmf_col[i], 102);
+ s->energy = 0.0;
+ }
+ /* Same for the fax dector */
+ goertzel_init (&s->fax_tone, fax_freq, 102);
+
+ s->current_sample = 0;
+ s->detected_digits = 0;
+ s->current_digits = 0;
+ memset(&s->digits, 0, sizeof(s->digits));
+ s->lost_digits = 0;
+ s->digits[0] = '\0';
+}
+
+static void ast_mf_detect_init (mf_detect_state_t *s)
+{
+ int i;
+ s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = 0;
+ for (i = 0; i < 6; i++) {
+ goertzel_init (&s->tone_out[i], mf_tones[i], 160);
+ }
+ s->current_digits = 0;
+ memset(&s->digits, 0, sizeof(s->digits));
+ s->current_sample = 0;
+ s->detected_digits = 0;
+ s->lost_digits = 0;
+ s->digits[0] = '\0';
+ s->mhit = 0;
+}
+
+static int dtmf_detect (dtmf_detect_state_t *s, int16_t amp[], int samples,
+ int digitmode, int *writeback, int faxdetect)
+{
+ float row_energy[4];
+ float col_energy[4];
+ float fax_energy = 0.0;
+ float famp;
+ int i;
+ int j;
+ int sample;
+ int best_row;
+ int best_col;
+ int hit;
+ int limit;
+
+ hit = 0;
+ for (sample = 0; sample < samples; sample = limit) {
+ /* 102 is optimised to meet the DTMF specs. */
+ if ((samples - sample) >= (102 - s->current_sample))
+ limit = sample + (102 - s->current_sample);
+ else
+ limit = samples;
+ /* The following unrolled loop takes only 35% (rough estimate) of the
+ time of a rolled loop on the machine on which it was developed */
+ for (j = sample; j < limit; j++) {
+ famp = amp[j];
+ s->energy += famp*famp;
+ /* With GCC 2.95, the following unrolled code seems to take about 35%
+ (rough estimate) as long as a neat little 0-3 loop */
+ goertzel_sample(s->row_out, amp[j]);
+ goertzel_sample(s->col_out, amp[j]);
+ goertzel_sample(s->row_out + 1, amp[j]);
+ goertzel_sample(s->col_out + 1, amp[j]);
+ goertzel_sample(s->row_out + 2, amp[j]);
+ goertzel_sample(s->col_out + 2, amp[j]);
+ goertzel_sample(s->row_out + 3, amp[j]);
+ goertzel_sample(s->col_out + 3, amp[j]);
+
+ /* Update fax tone */
+ if (faxdetect)
+ goertzel_sample(&s->fax_tone, amp[j]);
+ }
+ s->current_sample += (limit - sample);
+ if (s->current_sample < 102) {
+ if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
+ /* If we had a hit last time, go ahead and clear this out since likely it
+ will be another hit */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ continue;
+ }
+ /* Detect the fax energy, too */
+ if (faxdetect)
+ fax_energy = goertzel_result(&s->fax_tone);
+ /* We are at the end of a DTMF detection block */
+ /* Find the peak row and the peak column */
+ row_energy[0] = goertzel_result (&s->row_out[0]);
+ col_energy[0] = goertzel_result (&s->col_out[0]);
+
+ for (best_row = best_col = 0, i = 1; i < 4; i++) {
+ row_energy[i] = goertzel_result (&s->row_out[i]);
+ if (row_energy[i] > row_energy[best_row])
+ best_row = i;
+ col_energy[i] = goertzel_result (&s->col_out[i]);
+ if (col_energy[i] > col_energy[best_col])
+ best_col = i;
+ }
+ hit = 0;
+ /* Basic signal level test and the twist test */
+ if (row_energy[best_row] >= DTMF_THRESHOLD &&
+ col_energy[best_col] >= DTMF_THRESHOLD &&
+ col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST &&
+ col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) {
+ /* Relative peak test */
+ for (i = 0; i < 4; i++) {
+ if ((i != best_col &&
+ col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) ||
+ (i != best_row
+ && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) {
+ break;
+ }
+ }
+ /* ... and fraction of total energy test */
+ if (i >= 4 &&
+ (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy) {
+ /* Got a hit */
+ hit = dtmf_positions[(best_row << 2) + best_col];
+ if (!(digitmode & DSP_DIGITMODE_NOQUELCH)) {
+ /* Zero out frame data if this is part DTMF */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ }
+ }
+
+ /* Look for two successive similar results */
+ /* The logic in the next test is:
+ We need two successive identical clean detects, with
+ something different preceeding it. This can work with
+ back to back differing digits. More importantly, it
+ can work with nasty phones that give a very wobbly start
+ to a digit */
+ if (hit == s->lasthit && hit != s->mhit) {
+ if (hit) {
+ s->digit_hits[(best_row << 2) + best_col]++;
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS) {
+ s->digits[s->current_digits++] = hit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ s->lost_digits++;
+ }
+ }
+ s->mhit = hit;
+ }
+
+ if (!hit && faxdetect && (fax_energy >= FAX_THRESHOLD) &&
+ (fax_energy >= DTMF_TO_TOTAL_ENERGY*s->energy)) {
+ /* XXX Probably need better checking than just this the energy XXX */
+ hit = 'f';
+ s->fax_hits++;
+ } else {
+ if (s->fax_hits > 5) {
+ hit = 'f';
+ s->mhit = 'f';
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS) {
+ s->digits[s->current_digits++] = hit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ s->lost_digits++;
+ }
+ }
+ s->fax_hits = 0;
+ }
+ s->lasthit = hit;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 4; i++) {
+ goertzel_reset(&s->row_out[i]);
+ goertzel_reset(&s->col_out[i]);
+ }
+ if (faxdetect)
+ goertzel_reset (&s->fax_tone);
+ s->energy = 0.0;
+ s->current_sample = 0;
+ }
+ return (s->mhit); /* return the debounced hit */
+}
+
+/* MF goertzel size */
+#define MF_GSIZE 120
+
+static int mf_detect (mf_detect_state_t *s, int16_t amp[],
+ int samples, int digitmode, int *writeback)
+{
+ float energy[6];
+ int best;
+ int second_best;
+ float famp;
+ int i;
+ int j;
+ int sample;
+ int hit;
+ int limit;
+
+ hit = 0;
+ for (sample = 0; sample < samples; sample = limit) {
+ /* 80 is optimised to meet the MF specs. */
+ if ((samples - sample) >= (MF_GSIZE - s->current_sample))
+ limit = sample + (MF_GSIZE - s->current_sample);
+ else
+ limit = samples;
+ /* The following unrolled loop takes only 35% (rough estimate) of the
+ time of a rolled loop on the machine on which it was developed */
+ for (j = sample; j < limit; j++) {
+ famp = amp[j];
+ /* With GCC 2.95, the following unrolled code seems to take about 35%
+ (rough estimate) as long as a neat little 0-3 loop */
+ goertzel_sample(s->tone_out, amp[j]);
+ goertzel_sample(s->tone_out + 1, amp[j]);
+ goertzel_sample(s->tone_out + 2, amp[j]);
+ goertzel_sample(s->tone_out + 3, amp[j]);
+ goertzel_sample(s->tone_out + 4, amp[j]);
+ goertzel_sample(s->tone_out + 5, amp[j]);
+ }
+ s->current_sample += (limit - sample);
+ if (s->current_sample < MF_GSIZE) {
+ if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
+ /* If we had a hit last time, go ahead and clear this out since likely it
+ will be another hit */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ continue;
+ }
+ /* We're at the end of an MF detection block. */
+ /* Find the two highest energies. The spec says to look for
+ two tones and two tones only. Taking this literally -ie
+ only two tones pass the minimum threshold - doesn't work
+ well. The sinc function mess, due to rectangular windowing
+ ensure that! Find the two highest energies and ensure they
+ are considerably stronger than any of the others. */
+ energy[0] = goertzel_result(&s->tone_out[0]);
+ energy[1] = goertzel_result(&s->tone_out[1]);
+ if (energy[0] > energy[1]) {
+ best = 0;
+ second_best = 1;
+ } else {
+ best = 1;
+ second_best = 0;
+ }
+ /*endif*/
+ for (i=2;i<6;i++) {
+ energy[i] = goertzel_result(&s->tone_out[i]);
+ if (energy[i] >= energy[best]) {
+ second_best = best;
+ best = i;
+ } else if (energy[i] >= energy[second_best]) {
+ second_best = i;
+ }
+ }
+ /* Basic signal level and twist tests */
+ hit = 0;
+ if (energy[best] >= BELL_MF_THRESHOLD && energy[second_best] >= BELL_MF_THRESHOLD
+ && energy[best] < energy[second_best]*BELL_MF_TWIST
+ && energy[best]*BELL_MF_TWIST > energy[second_best]) {
+ /* Relative peak test */
+ hit = -1;
+ for (i=0;i<6;i++) {
+ if (i != best && i != second_best) {
+ if (energy[i]*BELL_MF_RELATIVE_PEAK >= energy[second_best]) {
+ /* The best two are not clearly the best */
+ hit = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (hit) {
+ /* Get the values into ascending order */
+ if (second_best < best) {
+ i = best;
+ best = second_best;
+ second_best = i;
+ }
+ best = best*5 + second_best - 1;
+ hit = bell_mf_positions[best];
+ /* Look for two successive similar results */
+ /* The logic in the next test is:
+ For KP we need 4 successive identical clean detects, with
+ two blocks of something different preceeding it. For anything
+ else we need two successive identical clean detects, with
+ two blocks of something different preceeding it. */
+ if (hit == s->hits[4] && hit == s->hits[3] &&
+ ((hit != '*' && hit != s->hits[2] && hit != s->hits[1])||
+ (hit == '*' && hit == s->hits[2] && hit != s->hits[1] &&
+ hit != s->hits[0]))) {
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS) {
+ s->digits[s->current_digits++] = hit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ s->lost_digits++;
+ }
+ }
+ } else {
+ hit = 0;
+ }
+ s->hits[0] = s->hits[1];
+ s->hits[1] = s->hits[2];
+ s->hits[2] = s->hits[3];
+ s->hits[3] = s->hits[4];
+ s->hits[4] = hit;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 6; i++)
+ goertzel_reset(&s->tone_out[i]);
+ s->current_sample = 0;
+ }
+ if ((!s->mhit) || (s->mhit != hit)) {
+ s->mhit = 0;
+ return(0);
+ }
+ return (hit);
+}
+
+static int __ast_dsp_digitdetect(struct ast_dsp *dsp, short *s, int len, int *writeback)
+{
+ int res;
+
+ if (dsp->digitmode & DSP_DIGITMODE_MF)
+ res = mf_detect(&dsp->td.mf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback);
+ else
+ res = dtmf_detect(&dsp->td.dtmf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback, dsp->features & DSP_FEATURE_FAX_DETECT);
+ return res;
+}
+
+int ast_dsp_digitdetect(struct ast_dsp *dsp, struct ast_frame *inf)
+{
+ short *s;
+ int len;
+ int ign=0;
+
+ if (inf->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n");
+ return 0;
+ }
+ if (inf->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
+ return 0;
+ }
+ s = inf->data;
+ len = inf->datalen / 2;
+ return __ast_dsp_digitdetect(dsp, s, len, &ign);
+}
+
+static inline int pair_there(float p1, float p2, float i1, float i2, float e)
+{
+ /* See if p1 and p2 are there, relative to i1 and i2 and total energy */
+ /* Make sure absolute levels are high enough */
+ if ((p1 < TONE_MIN_THRESH) || (p2 < TONE_MIN_THRESH))
+ return 0;
+ /* Amplify ignored stuff */
+ i2 *= TONE_THRESH;
+ i1 *= TONE_THRESH;
+ e *= TONE_THRESH;
+ /* Check first tone */
+ if ((p1 < i1) || (p1 < i2) || (p1 < e))
+ return 0;
+ /* And second */
+ if ((p2 < i1) || (p2 < i2) || (p2 < e))
+ return 0;
+ /* Guess it's there... */
+ return 1;
+}
+
+int ast_dsp_getdigits (struct ast_dsp *dsp, char *buf, int max)
+{
+ if (dsp->digitmode & DSP_DIGITMODE_MF) {
+ if (max > dsp->td.mf.current_digits)
+ max = dsp->td.mf.current_digits;
+ if (max > 0) {
+ memcpy(buf, dsp->td.mf.digits, max);
+ memmove(dsp->td.mf.digits, dsp->td.mf.digits + max, dsp->td.mf.current_digits - max);
+ dsp->td.mf.current_digits -= max;
+ }
+ buf[max] = '\0';
+ return max;
+ } else {
+ if (max > dsp->td.dtmf.current_digits)
+ max = dsp->td.dtmf.current_digits;
+ if (max > 0) {
+ memcpy (buf, dsp->td.dtmf.digits, max);
+ memmove (dsp->td.dtmf.digits, dsp->td.dtmf.digits + max, dsp->td.dtmf.current_digits - max);
+ dsp->td.dtmf.current_digits -= max;
+ }
+ buf[max] = '\0';
+ return max;
+ }
+}
+
+static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
+{
+ int x;
+ int y;
+ int pass;
+ int newstate = DSP_TONE_STATE_SILENCE;
+ int res = 0;
+ while (len) {
+ /* Take the lesser of the number of samples we need and what we have */
+ pass = len;
+ if (pass > dsp->gsamp_size - dsp->gsamps)
+ pass = dsp->gsamp_size - dsp->gsamps;
+ for (x=0;x<pass;x++) {
+ for (y=0;y<dsp->freqcount;y++)
+ goertzel_sample(&dsp->freqs[y], s[x]);
+ dsp->genergy += s[x] * s[x];
+ }
+ s += pass;
+ dsp->gsamps += pass;
+ len -= pass;
+ if (dsp->gsamps == dsp->gsamp_size) {
+ float hz[7];
+ for (y=0;y<7;y++)
+ hz[y] = goertzel_result(&dsp->freqs[y]);
+ switch (dsp->progmode) {
+ case PROG_MODE_NA:
+ if (pair_there(hz[HZ_480], hz[HZ_620], hz[HZ_350], hz[HZ_440], dsp->genergy)) {
+ newstate = DSP_TONE_STATE_BUSY;
+ } else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) {
+ newstate = DSP_TONE_STATE_RINGING;
+ } else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) {
+ newstate = DSP_TONE_STATE_DIALTONE;
+ } else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_SPECIAL1;
+ } else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) {
+ if (dsp->tstate == DSP_TONE_STATE_SPECIAL1)
+ newstate = DSP_TONE_STATE_SPECIAL2;
+ } else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) {
+ if (dsp->tstate == DSP_TONE_STATE_SPECIAL2)
+ newstate = DSP_TONE_STATE_SPECIAL3;
+ } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_TALKING;
+ } else
+ newstate = DSP_TONE_STATE_SILENCE;
+ break;
+ case PROG_MODE_CR:
+ if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_RINGING;
+ } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_TALKING;
+ } else
+ newstate = DSP_TONE_STATE_SILENCE;
+ break;
+ case PROG_MODE_UK:
+ if (hz[HZ_400] > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_HUNGUP;
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't process in unknown prog mode '%d'\n", dsp->progmode);
+ }
+ if (newstate == dsp->tstate) {
+ dsp->tcount++;
+ if (dsp->ringtimeout)
+ dsp->ringtimeout++;
+ switch (dsp->tstate) {
+ case DSP_TONE_STATE_RINGING:
+ if ((dsp->features & DSP_PROGRESS_RINGING) &&
+ (dsp->tcount==THRESH_RING)) {
+ res = AST_CONTROL_RINGING;
+ dsp->ringtimeout= 1;
+ }
+ break;
+ case DSP_TONE_STATE_BUSY:
+ if ((dsp->features & DSP_PROGRESS_BUSY) &&
+ (dsp->tcount==THRESH_BUSY)) {
+ res = AST_CONTROL_BUSY;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ case DSP_TONE_STATE_TALKING:
+ if ((dsp->features & DSP_PROGRESS_TALK) &&
+ (dsp->tcount==THRESH_TALK)) {
+ res = AST_CONTROL_ANSWER;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ case DSP_TONE_STATE_SPECIAL3:
+ if ((dsp->features & DSP_PROGRESS_CONGESTION) &&
+ (dsp->tcount==THRESH_CONGESTION)) {
+ res = AST_CONTROL_CONGESTION;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ case DSP_TONE_STATE_HUNGUP:
+ if ((dsp->features & DSP_FEATURE_CALL_PROGRESS) &&
+ (dsp->tcount==THRESH_HANGUP)) {
+ res = AST_CONTROL_HANGUP;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ }
+ if (dsp->ringtimeout==THRESH_RING2ANSWER) {
+ ast_debug(1, "Consider call as answered because of timeout after last ring\n");
+ res = AST_CONTROL_ANSWER;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ } else {
+ ast_debug(5, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
+ ast_debug(5, "Start state %d\n", newstate);
+ dsp->tstate = newstate;
+ dsp->tcount = 1;
+ }
+
+ /* Reset goertzel */
+ for (x=0;x<7;x++)
+ dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
+ dsp->gsamps = 0;
+ dsp->genergy = 0.0;
+ }
+ }
+
+ return res;
+}
+
+int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf)
+{
+ if (inf->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n");
+ return 0;
+ }
+ if (inf->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
+ return 0;
+ }
+ return __ast_dsp_call_progress(dsp, inf->data, inf->datalen / 2);
+}
+
+static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totalsilence)
+{
+ int accum;
+ int x;
+ int res = 0;
+
+ if (!len)
+ return 0;
+ accum = 0;
+ for (x=0;x<len; x++)
+ accum += abs(s[x]);
+ accum /= len;
+ if (accum < dsp->threshold) {
+ /* Silent */
+ dsp->totalsilence += len/8;
+ if (dsp->totalnoise) {
+ /* Move and save history */
+ memmove(dsp->historicnoise + DSP_HISTORY - dsp->busycount, dsp->historicnoise + DSP_HISTORY - dsp->busycount +1, dsp->busycount*sizeof(dsp->historicnoise[0]));
+ dsp->historicnoise[DSP_HISTORY - 1] = dsp->totalnoise;
+/* we don't want to check for busydetect that frequently */
+#if 0
+ dsp->busymaybe = 1;
+#endif
+ }
+ dsp->totalnoise = 0;
+ res = 1;
+ } else {
+ /* Not silent */
+ dsp->totalnoise += len/8;
+ if (dsp->totalsilence) {
+ int silence1 = dsp->historicsilence[DSP_HISTORY - 1];
+ int silence2 = dsp->historicsilence[DSP_HISTORY - 2];
+ /* Move and save history */
+ memmove(dsp->historicsilence + DSP_HISTORY - dsp->busycount, dsp->historicsilence + DSP_HISTORY - dsp->busycount + 1, dsp->busycount*sizeof(dsp->historicsilence[0]));
+ dsp->historicsilence[DSP_HISTORY - 1] = dsp->totalsilence;
+ /* check if the previous sample differs only by BUSY_PERCENT from the one before it */
+ if (silence1 < silence2) {
+ if (silence1 + silence1*BUSY_PERCENT/100 >= silence2)
+ dsp->busymaybe = 1;
+ else
+ dsp->busymaybe = 0;
+ } else {
+ if (silence1 - silence1*BUSY_PERCENT/100 <= silence2)
+ dsp->busymaybe = 1;
+ else
+ dsp->busymaybe = 0;
+ }
+ }
+ dsp->totalsilence = 0;
+ }
+ if (totalsilence)
+ *totalsilence = dsp->totalsilence;
+ return res;
+}
+
+int ast_dsp_busydetect(struct ast_dsp *dsp)
+{
+ int res = 0, x;
+#ifndef BUSYDETECT_TONEONLY
+ int avgsilence = 0, hitsilence = 0;
+#endif
+ int avgtone = 0, hittone = 0;
+ if (!dsp->busymaybe)
+ return res;
+ for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
+#ifndef BUSYDETECT_TONEONLY
+ avgsilence += dsp->historicsilence[x];
+#endif
+ avgtone += dsp->historicnoise[x];
+ }
+#ifndef BUSYDETECT_TONEONLY
+ avgsilence /= dsp->busycount;
+#endif
+ avgtone /= dsp->busycount;
+ for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
+#ifndef BUSYDETECT_TONEONLY
+ if (avgsilence > dsp->historicsilence[x]) {
+ if (avgsilence - (avgsilence*BUSY_PERCENT/100) <= dsp->historicsilence[x])
+ hitsilence++;
+ } else {
+ if (avgsilence + (avgsilence*BUSY_PERCENT/100) >= dsp->historicsilence[x])
+ hitsilence++;
+ }
+#endif
+ if (avgtone > dsp->historicnoise[x]) {
+ if (avgtone - (avgtone*BUSY_PERCENT/100) <= dsp->historicnoise[x])
+ hittone++;
+ } else {
+ if (avgtone + (avgtone*BUSY_PERCENT/100) >= dsp->historicnoise[x])
+ hittone++;
+ }
+ }
+#ifndef BUSYDETECT_TONEONLY
+ if ((hittone >= dsp->busycount - 1) && (hitsilence >= dsp->busycount - 1) &&
+ (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX) &&
+ (avgsilence >= BUSY_MIN && avgsilence <= BUSY_MAX)) {
+#else
+ if ((hittone >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX)) {
+#endif
+#ifdef BUSYDETECT_COMPARE_TONE_AND_SILENCE
+ if (avgtone > avgsilence) {
+ if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence)
+ res = 1;
+ } else {
+ if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence)
+ res = 1;
+ }
+#else
+ res = 1;
+#endif
+ }
+ /* If we know the expected busy tone length, check we are in the range */
+ if (res && (dsp->busy_tonelength > 0)) {
+ if (abs(avgtone - dsp->busy_tonelength) > (dsp->busy_tonelength*BUSY_PAT_PERCENT/100)) {
+#ifdef BUSYDETECT_DEBUG
+ ast_debug(5, "busy detector: avgtone of %d not close enough to desired %d\n",
+ avgtone, dsp->busy_tonelength);
+#endif
+ res = 0;
+ }
+ }
+#ifndef BUSYDETECT_TONEONLY
+ /* If we know the expected busy tone silent-period length, check we are in the range */
+ if (res && (dsp->busy_quietlength > 0)) {
+ if (abs(avgsilence - dsp->busy_quietlength) > (dsp->busy_quietlength*BUSY_PAT_PERCENT/100)) {
+#ifdef BUSYDETECT_DEBUG
+ ast_debug(5, "busy detector: avgsilence of %d not close enough to desired %d\n",
+ avgsilence, dsp->busy_quietlength);
+#endif
+ res = 0;
+ }
+ }
+#endif
+#if !defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_DEBUG)
+ if (res) {
+ ast_debug(5, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
+ } else {
+ ast_debug(5, "busy detector: FAILED with avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
+ }
+#endif
+ return res;
+}
+
+int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
+{
+ short *s;
+ int len;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n");
+ return 0;
+ }
+ if (f->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n");
+ return 0;
+ }
+ s = f->data;
+ len = f->datalen/2;
+ return __ast_dsp_silence(dsp, s, len, totalsilence);
+}
+
+struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *af)
+{
+ int silence;
+ int res;
+ int digit;
+ int x;
+ short *shortdata;
+ unsigned char *odata;
+ int len;
+ int writeback = 0;
+
+#define FIX_INF(inf) do { \
+ if (writeback) { \
+ switch (inf->subclass) { \
+ case AST_FORMAT_SLINEAR: \
+ break; \
+ case AST_FORMAT_ULAW: \
+ for (x=0;x<len;x++) \
+ odata[x] = AST_LIN2MU((unsigned short)shortdata[x]); \
+ break; \
+ case AST_FORMAT_ALAW: \
+ for (x=0;x<len;x++) \
+ odata[x] = AST_LIN2A((unsigned short)shortdata[x]); \
+ break; \
+ } \
+ } \
+ } while(0)
+
+ if (!af)
+ return NULL;
+ if (af->frametype != AST_FRAME_VOICE)
+ return af;
+ odata = af->data;
+ len = af->datalen;
+ /* Make sure we have short data */
+ switch (af->subclass) {
+ case AST_FORMAT_SLINEAR:
+ shortdata = af->data;
+ len = af->datalen / 2;
+ break;
+ case AST_FORMAT_ULAW:
+ shortdata = alloca(af->datalen * 2);
+ for (x = 0;x < len; x++)
+ shortdata[x] = AST_MULAW(odata[x]);
+ break;
+ case AST_FORMAT_ALAW:
+ shortdata = alloca(af->datalen * 2);
+ for (x = 0; x < len; x++)
+ shortdata[x] = AST_ALAW(odata[x]);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(af->subclass));
+ return af;
+ }
+ silence = __ast_dsp_silence(dsp, shortdata, len, NULL);
+ if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_NULL;
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && ast_dsp_busydetect(dsp)) {
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_CONTROL;
+ dsp->f.subclass = AST_CONTROL_BUSY;
+ ast_frfree(af);
+ ast_debug(1, "Requesting Hangup because the busy tone was detected on channel %s\n", chan->name);
+ return &dsp->f;
+ }
+ if ((dsp->features & DSP_FEATURE_DTMF_DETECT)) {
+ digit = __ast_dsp_digitdetect(dsp, shortdata, len, &writeback);
+#if 0
+ if (digit)
+ printf("Performing digit detection returned %d, digitmode is %d\n", digit, dsp->digitmode);
+#endif
+ if (dsp->digitmode & (DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX)) {
+ if (!dsp->thinkdigit) {
+ if (digit) {
+ /* Looks like we might have something.
+ * Request a conference mute for the moment */
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF;
+ dsp->f.subclass = 'm';
+ dsp->thinkdigit = 'x';
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ } else {
+ if (digit) {
+ /* Thought we saw one last time. Pretty sure we really have now */
+ if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) {
+ /* If we found a digit, and we're changing digits, go
+ ahead and send this one, but DON'T stop confmute because
+ we're detecting something else, too... */
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF_END;
+ dsp->f.subclass = dsp->thinkdigit;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ } else {
+ dsp->thinkdigit = digit;
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF_BEGIN;
+ dsp->f.subclass = dsp->thinkdigit;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ }
+ return &dsp->f;
+ } else {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ if (dsp->thinkdigit != 'x') {
+ /* If we found a digit, send it now */
+ dsp->f.frametype = AST_FRAME_DTMF_END;
+ dsp->f.subclass = dsp->thinkdigit;
+ dsp->thinkdigit = 0;
+ } else {
+ dsp->f.frametype = AST_FRAME_DTMF;
+ dsp->f.subclass = 'u';
+ dsp->thinkdigit = 0;
+ }
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ }
+ } else if (!digit) {
+ /* Only check when there is *not* a hit... */
+ if (dsp->digitmode & DSP_DIGITMODE_MF) {
+ if (dsp->td.mf.current_digits) {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF;
+ dsp->f.subclass = dsp->td.mf.digits[0];
+ memmove(dsp->td.mf.digits, dsp->td.mf.digits + 1, dsp->td.mf.current_digits);
+ dsp->td.mf.current_digits--;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ } else {
+ if (dsp->td.dtmf.current_digits) {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF_END;
+ dsp->f.subclass = dsp->td.dtmf.digits[0];
+ memmove(dsp->td.dtmf.digits, dsp->td.dtmf.digits + 1, dsp->td.dtmf.current_digits);
+ dsp->td.dtmf.current_digits--;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ }
+ }
+ }
+ if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {
+ res = __ast_dsp_call_progress(dsp, shortdata, len);
+ if (res) {
+ switch (res) {
+ case AST_CONTROL_ANSWER:
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_HANGUP:
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_CONTROL;
+ dsp->f.subclass = res;
+ dsp->f.src = "dsp_progress";
+ if (chan)
+ ast_queue_frame(chan, &dsp->f);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to represent call progress message %d\n", res);
+ }
+ }
+ }
+ FIX_INF(af);
+ return af;
+}
+
+static void ast_dsp_prog_reset(struct ast_dsp *dsp)
+{
+ int max = 0;
+ int x;
+
+ dsp->gsamp_size = modes[dsp->progmode].size;
+ dsp->gsamps = 0;
+ for (x = 0; x < sizeof(modes[dsp->progmode].freqs) / sizeof(modes[dsp->progmode].freqs[0]); x++) {
+ if (modes[dsp->progmode].freqs[x]) {
+ goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->gsamp_size);
+ max = x + 1;
+ }
+ }
+ dsp->freqcount = max;
+ dsp->ringtimeout= 0;
+}
+
+struct ast_dsp *ast_dsp_new(void)
+{
+ struct ast_dsp *dsp;
+
+ if ((dsp = ast_calloc(1, sizeof(*dsp)))) {
+ dsp->threshold = DEFAULT_THRESHOLD;
+ dsp->features = DSP_FEATURE_SILENCE_SUPPRESS;
+ dsp->busycount = DSP_HISTORY;
+ /* Initialize DTMF detector */
+ ast_dtmf_detect_init(&dsp->td.dtmf);
+ /* Initialize initial DSP progress detect parameters */
+ ast_dsp_prog_reset(dsp);
+ }
+ return dsp;
+}
+
+void ast_dsp_set_features(struct ast_dsp *dsp, int features)
+{
+ dsp->features = features;
+}
+
+void ast_dsp_free(struct ast_dsp *dsp)
+{
+ ast_free(dsp);
+}
+
+void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
+{
+ dsp->threshold = threshold;
+}
+
+void ast_dsp_set_busy_count(struct ast_dsp *dsp, int cadences)
+{
+ if (cadences < 4)
+ cadences = 4;
+ if (cadences > DSP_HISTORY)
+ cadences = DSP_HISTORY;
+ dsp->busycount = cadences;
+}
+
+void ast_dsp_set_busy_pattern(struct ast_dsp *dsp, int tonelength, int quietlength)
+{
+ dsp->busy_tonelength = tonelength;
+ dsp->busy_quietlength = quietlength;
+ ast_debug(1, "dsp busy pattern set to %d,%d\n", tonelength, quietlength);
+}
+
+void ast_dsp_digitreset(struct ast_dsp *dsp)
+{
+ int i;
+
+ dsp->thinkdigit = 0;
+ if (dsp->digitmode & DSP_DIGITMODE_MF) {
+ memset(dsp->td.mf.digits, 0, sizeof(dsp->td.mf.digits));
+ dsp->td.mf.current_digits = 0;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 6; i++) {
+ goertzel_reset(&dsp->td.mf.tone_out[i]);
+ }
+ dsp->td.mf.hits[4] = dsp->td.mf.hits[3] = dsp->td.mf.hits[2] = dsp->td.mf.hits[1] = dsp->td.mf.hits[0] = dsp->td.mf.mhit = 0;
+ dsp->td.mf.current_sample = 0;
+ } else {
+ memset(dsp->td.dtmf.digits, 0, sizeof(dsp->td.dtmf.digits));
+ dsp->td.dtmf.current_digits = 0;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 4; i++) {
+ goertzel_reset(&dsp->td.dtmf.row_out[i]);
+ goertzel_reset(&dsp->td.dtmf.col_out[i]);
+ }
+ goertzel_reset (&dsp->td.dtmf.fax_tone);
+ dsp->td.dtmf.lasthit = dsp->td.dtmf.mhit = 0;
+ dsp->td.dtmf.energy = 0.0;
+ dsp->td.dtmf.current_sample = 0;
+ }
+}
+
+void ast_dsp_reset(struct ast_dsp *dsp)
+{
+ int x;
+
+ dsp->totalsilence = 0;
+ dsp->gsamps = 0;
+ for (x=0;x<4;x++)
+ dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
+ memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
+ memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
+ dsp->ringtimeout= 0;
+}
+
+int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode)
+{
+ int new;
+ int old;
+
+ old = dsp->digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
+ new = digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
+ if (old != new) {
+ /* Must initialize structures if switching from MF to DTMF or vice-versa */
+ if (new & DSP_DIGITMODE_MF)
+ ast_mf_detect_init(&dsp->td.mf);
+ else
+ ast_dtmf_detect_init(&dsp->td.dtmf);
+ }
+ dsp->digitmode = digitmode;
+ return 0;
+}
+
+int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone)
+{
+ int x;
+
+ for (x = 0; x < ARRAY_LEN(aliases); x++) {
+ if (!strcasecmp(aliases[x].name, zone)) {
+ dsp->progmode = aliases[x].mode;
+ ast_dsp_prog_reset(dsp);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int ast_dsp_get_tstate(struct ast_dsp *dsp)
+{
+ return dsp->tstate;
+}
+
+int ast_dsp_get_tcount(struct ast_dsp *dsp)
+{
+ return dsp->tcount;
+}
diff --git a/trunk/main/ecdisa.h b/trunk/main/ecdisa.h
new file mode 100644
index 000000000..df6f773a0
--- /dev/null
+++ b/trunk/main/ecdisa.h
@@ -0,0 +1,15 @@
+/* ecdisa.h: Generated from frequency 2100
+ by gentone. 80 samples */
+static unsigned char ecdisa[80] = {
+ 255, 143, 58, 16, 171, 146, 34, 20,
+ 156, 151, 25, 26, 149, 159, 19, 38,
+ 145, 177, 16, 73, 143, 73, 16, 177,
+ 145, 38, 19, 159, 149, 26, 25, 151,
+ 156, 20, 34, 146, 171, 16, 58, 143,
+ 255, 15, 186, 144, 43, 18, 162, 148,
+ 28, 23, 153, 154, 21, 31, 147, 166,
+ 17, 49, 144, 201, 15, 201, 144, 49,
+ 17, 166, 147, 31, 21, 154, 153, 23,
+ 28, 148, 162, 18, 43, 144, 186, 15,
+
+};
diff --git a/trunk/main/editline/CHANGES b/trunk/main/editline/CHANGES
new file mode 100644
index 000000000..c18b56cdf
--- /dev/null
+++ b/trunk/main/editline/CHANGES
@@ -0,0 +1,42 @@
+2002-02-25 : Christos Zoulas <christos@netbsd.org>
+ * Bring in constification fixes from NetBSD tree.
+ * Use NetBSD's vis, fgetln
+
+2002-02-10 : Jason Evans <jasone@freebsd.org>
+
+ * Makefile.in : Append "" arguments to for loops, to avoid syntax errors
+ with some shells that occur if there are no arguments to the for
+ loops.
+
+2002-02-09 : Jason Evans <jasone@freebsd.org>
+
+ * Install man pages in @prefix@/man/, rather than @prefix@/share/man.
+
+ * Fix the Darwin -install_name S_LDFLAGS argument to default to a prefix
+ of /usr/local if --prefix is not specified.
+
+2002-02-05 : Jason Evans <jasone@freebsd.org>
+
+ * Convert to using an autoconf-generated config.h, rather than passing
+ -D_HAVE_<foo>=1 definitions on the command line. Include sys.h in
+ config.h, and include config.h in .c files rather than sys.h.
+
+ * Mangle function names for implementations in the np directory in order
+ to avoid namespace collisions with other code that may provide copies
+ of the same unimplemented functions. For example:
+
+ #define fgetln libedit_fgetln
+
+2002-02-03 : Jason Evans <jasone@freebsd.org>
+
+ * Add autoconf infrastructure, plus a generic Makefile that works with
+ at least BSD make, GNU make and Sun make.
+
+ * Port and/or test on FreeBSD 4.5, FreeBSD-current, NetBSD 1.5 (sparc64
+ and arm32), Apple OS X 10.1.2, Solaris 2.6, and Red Hat Linux 2.6.
+ Add the np directory, which contains implementations of non-portable
+ functions.
+
+ * Add the LIBEDIT_MAJOR and LIBEDIT_MINOR macros to histedit.h, since
+ there is otherwise no straightforward method of programmatically
+ detecting the library version.
diff --git a/trunk/main/editline/INSTALL b/trunk/main/editline/INSTALL
new file mode 100644
index 000000000..16fb6ffd1
--- /dev/null
+++ b/trunk/main/editline/INSTALL
@@ -0,0 +1,64 @@
+Building this distribution in many cases is as simple as typing the following
+while in the root directory of the source tree:
+
+ ./configure
+ make
+
+To install, do the above, then type:
+
+ make install
+
+Additional build targets of finer granularity include:
+
+ lib_a
+ lib_s
+ install_hdr
+ install_lib
+ install_lib_a
+ install_lib_s
+ install_man
+ test
+
+Cleanup targets include:
+
+ clean
+ distclean
+
+Optionally, pass any of the following (not a definitive list) arguments to
+'configure':
+
+--prefix=<install-root-dir>
+ Set the base directory in which to install. For example:
+
+ ./configure --prefix=/usr/local
+
+ will cause files to be installed into /usr/local/bin, /usr/local/man,
+ /usr/local/include, /usr/local/lib, and /usr/local/share.
+
+--disable-readline
+ By default, libedit is built and installed such that it works as a
+ drop-in replacement for the readline library. This option turns that
+ behavior off.
+
+--enable-debug
+ Build debugging code (for libedit development).
+
+Optionally, define environment variables, including (not exclusively):
+
+CFLAGS="?"
+ Pass these flags to the compiler. You probably shouldn't define this
+ unless you know what you are doing.
+
+CPPFLAGS="?"
+ Pass these flags to the C preprocessor. Note that CFLAGS is not passed
+ to 'cpp' when 'configure' is looking for include files, so you must use
+ CPPFLAGS instead if you need to help 'configure' find header files.
+
+LD_LIBRARY_PATH="?"
+ 'ld' uses this colon-separated list to find libraries.
+
+LDFLAGS="?"
+ Flags passed to 'gcc', which should normally be passed on to 'ld'.
+
+PATH="?"
+ 'configure' uses this to find programs.
diff --git a/trunk/main/editline/Makefile.in b/trunk/main/editline/Makefile.in
new file mode 100644
index 000000000..f17cfd43a
--- /dev/null
+++ b/trunk/main/editline/Makefile.in
@@ -0,0 +1,234 @@
+#
+# Generic Makefile for libedit.
+#
+
+OSTYPE=$(shell uname -s)
+define cyg_subst_sys
+ if uname -s | grep -qi cygwin; then \
+ cat $@ | sed -e s/"sys\.h"/"config.h"/g > $@.copy; \
+ mv --force $@.copy $@; \
+ fi
+endef
+
+SHELL = /bin/sh
+
+CC = @CC@
+AR = @AR@
+RANLIB = @RANLIB@
+CPPFLAGS = @CPPFLAGS@ -I.
+CFLAGS = @CFLAGS@
+A_CFLAGS = @A_CFLAGS@
+S_CFLAGS = @S_CFLAGS@
+LDFLAGS = @LDFLAGS@
+S_LDFLAGS = @S_LDFLAGS@
+LIBS = @LIBS@
+
+INSTALL = @INSTALL@
+PREFIX = @prefix@
+
+ifeq ($(OSTYPE),SunOS)
+CFLAGS+=-DSOLARIS -I../../include/solaris-compat
+endif
+
+# .c files.
+ACSRCS = @ACSRCS@
+BCSRCS = @BCSRCS@
+CCSRCS = @CCSRCS@
+
+# Generated .c files.
+AGCSRCS = @AGCSRCS@
+BGCSRCS = @BGCSRCS@
+
+# .h files.
+HDRS = @HDRS@
+
+# Generated .h files.
+AGHDRS = @AGHDRS@
+BGHDRS = @BGHDRS@
+
+# Installed .h files.
+IHDRS = @IHDRS@
+IHDR_LINKS = @IHDR_LINKS@
+HDR_DIRS = @HDR_DIRS@
+
+# Man pages.
+MAN3 = @MAN3@
+MAN5 = @MAN5@
+MAN3_LINKS = @MAN3_LINKS@
+MAN_DIRS = @MAN_DIRS@
+
+# Library.
+LIB_DIRS = @LIB_DIRS@
+LIB_VER = @LIB_VER@
+LIB_A = @LIB_A@
+LIB_A_LINKS = @LIB_A_LINKS@
+LIB_S = @LIB_S@
+LIB_S_LINKS = @LIB_S_LINKS@
+
+# Test program.
+TEST = @TEST@
+TCSRCS = @TCSRCS@
+
+# Clear out all paths, then set just one (default path) for the main build
+# directory.
+.PATH :
+.PATH : .
+
+.SUFFIXES :
+.SUFFIXES : .c .o .o_a .o_s
+
+all : lib_a lib_s
+
+lib_a : $(LIB_A)
+lib_s : $(LIB_S)
+
+test : $(TEST)
+
+install : install_hdr install_lib install_man
+
+install_hdr :
+ @for i in $(HDR_DIRS) ; do \
+ echo "$(INSTALL) -d $(PREFIX)/$$i/"; \
+ $(INSTALL) -d $(PREFIX)/$$i/; \
+ done
+ @for i in $(IHDRS); do \
+ echo "$(INSTALL) -m 0444 $$i $(PREFIX)/include/`dirname $$i`/"; \
+ $(INSTALL) -m 0444 $$i $(PREFIX)/include/`dirname $$i`/; \
+ done
+ @f=; \
+ for i in $(IHDR_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/include/$$i"; \
+ rm -f $(PREFIX)/include/$$i; \
+ echo "ln -s $$f $(PREFIX)/include/$$i"; \
+ ln -s $$f $(PREFIX)/include/$$i; \
+ f=; \
+ fi; \
+ done
+
+install_lib : install_lib_a install_lib_s
+
+install_lib_common :
+ @for i in $(LIB_DIRS) ; do \
+ echo "$(INSTALL) -d $(PREFIX)/$$i/"; \
+ $(INSTALL) -d $(PREFIX)/$$i/; \
+ done
+
+install_lib_a : $(LIB_A) install_lib_common
+ $(INSTALL) -m 0644 $(LIB_A) $(PREFIX)/lib/
+ @f=; \
+ for i in $(LIB_A_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/lib/$$i"; \
+ rm -f $(PREFIX)/lib/$$i; \
+ echo "ln -s $$f $(PREFIX)/lib/$$i"; \
+ ln -s $$f $(PREFIX)/lib/$$i; \
+ f=; \
+ fi; \
+ done
+
+install_lib_s : $(LIB_S) install_lib_common
+ $(INSTALL) -m 0755 $(LIB_S) $(PREFIX)/lib/
+ @f=; \
+ for i in $(LIB_S_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/lib/$$i"; \
+ rm -f $(PREFIX)/lib/$$i; \
+ echo "ln -s $$f $(PREFIX)/lib/$$i"; \
+ ln -s $$f $(PREFIX)/lib/$$i; \
+ f=; \
+ fi; \
+ done
+
+install_man :
+ @for i in $(MAN_DIRS) ; do \
+ echo "$(INSTALL) -d $(PREFIX)/$$i/"; \
+ $(INSTALL) -d $(PREFIX)/$$i/; \
+ done
+ @for i in $(MAN3); do \
+ echo $(INSTALL) -m 0444 $$i $(PREFIX)/man/man3/; \
+ $(INSTALL) -m 0444 $$i $(PREFIX)/man/man3/; \
+ done
+ @f=; \
+ for i in $(MAN3_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/man/man3/$$i"; \
+ rm -f $(PREFIX)/man/man3/$$i; \
+ echo "ln -s $$f $(PREFIX)/man/man3/$$i"; \
+ ln -s $$f $(PREFIX)/man/man3/$$i; \
+ f=; \
+ fi; \
+ done
+ @for i in $(MAN5); do\
+ echo $(INSTALL) -m 0444 $$i $(PREFIX)/man/man5/; \
+ $(INSTALL) -m 0444 $$i $(PREFIX)/man/man5/; \
+ done
+
+clean :
+ rm -f $(AGCSRCS) $(BGCSRCS) $(AGHDRS) $(BGHDRS) $(LIB_A) $(LIB_S)
+ rm -f $(BGCSRCS:.c=.o_a) $(CCSRCS:.c=.o_a)
+ rm -f $(BGCSRCS:.c=.o_s) $(CCSRCS:.c=.o_s)
+ rm -f $(TCSRCS:.c=.o) $(TEST)
+ rm -f *.s *.i
+
+distclean : clean
+ rm -f config.cache config.log config.status config.h Makefile
+
+#
+# Internal targets and rules.
+#
+
+$(LIB_A) : $(BGCSRCS:.c=.o_a) $(CCSRCS:.c=.o_a)
+ $(AR) cru $@ $?
+ $(RANLIB) $@
+
+$(LIB_S) : $(BGCSRCS:.c=.o_s) $(CCSRCS:.c=.o_s)
+ $(CC) $(S_LDFLAGS) -o $@ $(BGCSRCS:.c=.o_s) $(CCSRCS:.c=.o_s) $(LIBS)
+
+$(TEST) : $(TCSRCS:.c=.o) $(LIB_A)
+ $(CC) -o $@ $(TCSRCS:.c=.o) $(LIB_A) $(LIBS)
+
+common.h : common.c
+ $(SHELL) makelist -h common.c > $@
+
+emacs.h : emacs.c
+ $(SHELL) makelist -h emacs.c> $@
+
+vi.h : vi.c
+ $(SHELL) makelist -h vi.c > $@
+
+fcns.h : $(AGHDRS)
+ $(SHELL) makelist -fh $(AGHDRS) > $@
+
+fcns.c : $(AGHDRS) fcns.h
+ $(SHELL) makelist -fc $(AGHDRS) > $@
+ $(cyg_subst_sys)
+
+help.h : $(ACSRCS)
+ $(SHELL) makelist -bh $(ACSRCS) > $@
+
+help.c : $(ACSRCS) help.h
+ $(SHELL) makelist -bc $(ACSRCS) > $@
+ $(cyg_subst_sys)
+
+editline.c : $(ACSRCS) $(BCSRCS) $(AGCSRCS)
+ $(SHELL) makelist -e $(ACSRCS) $(BCSRCS) $(AGCSRCS) > $@
+
+.c.o :
+ $(CC) -c $(A_CFLAGS) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+.c.o_a : $(AGHDRS) $(BGHDRS)
+ $(CC) -c $(A_CFLAGS) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+.c.o_s : $(AGHDRS) $(BGHDRS)
+ $(CC) -c $(S_CFLAGS) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+$(CCSRCS) : $(BGHDRS)
diff --git a/trunk/main/editline/PLATFORMS b/trunk/main/editline/PLATFORMS
new file mode 100644
index 000000000..ea7c5bb68
--- /dev/null
+++ b/trunk/main/editline/PLATFORMS
@@ -0,0 +1,13 @@
+This distribution of libedit is expected to work on at least the following
+platforms. It may also work on additional platforms, but no explicit support
+for them is built into the configuration system.
+
+* Apple OS X 10.1.
+
+* FreeBSD 4.x.
+
+* NetBSD 1.5.
+
+* Red Hat Linux 7.2.
+
+* Sun Solaris 2.6.
diff --git a/trunk/main/editline/README b/trunk/main/editline/README
new file mode 100644
index 000000000..49a2a6947
--- /dev/null
+++ b/trunk/main/editline/README
@@ -0,0 +1,11 @@
+libedit is a command line editing and history library. It is designed to be
+used by interactive programs that allow the user to type commands at a terminal
+prompt.
+
+The following files may be of direct interest to the user:
+
+* CHANGES - Software change log.
+
+* INSTALL - Installation information.
+
+* PLATFORMS - Supported platforms and platform-specific information.
diff --git a/trunk/main/editline/TEST/test.c b/trunk/main/editline/TEST/test.c
new file mode 100644
index 000000000..3169a2071
--- /dev/null
+++ b/trunk/main/editline/TEST/test.c
@@ -0,0 +1,268 @@
+/* $NetBSD: test.c,v 1.9 2000/09/04 23:36:41 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n");
+#endif /* not lint */
+
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)test.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: test.c,v 1.9 2000/09/04 23:36:41 lukem Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * test.c: A little test program
+ */
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "histedit.h"
+#include "tokenizer.h"
+
+static int continuation = 0;
+static EditLine *el = NULL;
+
+static u_char complete(EditLine *, int);
+ int main(int, char **);
+static char *prompt(EditLine *);
+static void sig(int);
+
+static char *
+prompt(EditLine *el)
+{
+ static char a[] = "Edit$";
+ static char b[] = "Edit>";
+
+ return (continuation ? b : a);
+}
+
+static void
+sig(int i)
+{
+
+ (void) fprintf(stderr, "Got signal %d.\n", i);
+ el_reset(el);
+}
+
+static unsigned char
+complete(EditLine *el, int ch)
+{
+ DIR *dd = opendir(".");
+ struct dirent *dp;
+ const char* ptr;
+ const LineInfo *lf = el_line(el);
+ int len;
+
+ /*
+ * Find the last word
+ */
+ for (ptr = lf->cursor - 1; !isspace(*ptr) && ptr > lf->buffer; ptr--)
+ continue;
+ len = lf->cursor - ++ptr;
+
+ for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
+ if (len > strlen(dp->d_name))
+ continue;
+ if (strncmp(dp->d_name, ptr, len) == 0) {
+ closedir(dd);
+ if (el_insertstr(el, &dp->d_name[len]) == -1)
+ return (CC_ERROR);
+ else
+ return (CC_REFRESH);
+ }
+ }
+
+ closedir(dd);
+ return (CC_ERROR);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int num;
+ const char *buf;
+ Tokenizer *tok;
+#if 0
+ int lastevent = 0;
+#endif
+ int ncontinuation;
+ History *hist;
+ HistEvent ev;
+
+ (void) signal(SIGINT, sig);
+ (void) signal(SIGQUIT, sig);
+ (void) signal(SIGHUP, sig);
+ (void) signal(SIGTERM, sig);
+
+ hist = history_init(); /* Init the builtin history */
+ /* Remember 100 events */
+ history(hist, &ev, H_SETSIZE, 100);
+
+ tok = tok_init(NULL); /* Initialize the tokenizer */
+
+ /* Initialize editline */
+ el = el_init(*argv, stdin, stdout, stderr);
+
+ el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */
+ el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
+ el_set(el, EL_PROMPT, prompt); /* Set the prompt function */
+
+ /* Tell editline to use this history interface */
+ el_set(el, EL_HIST, history, hist);
+
+ /* Add a user-defined function */
+ el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete);
+
+ /* Bind tab to it */
+ el_set(el, EL_BIND, "^I", "ed-complete", NULL);
+
+ /*
+ * Bind j, k in vi command mode to previous and next line, instead
+ * of previous and next history.
+ */
+ el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL);
+ el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL);
+
+ /*
+ * Source the user's defaults file.
+ */
+ el_source(el, NULL);
+
+ while ((buf = el_gets(el, &num)) != NULL && num != 0) {
+ int ac;
+ const char **av;
+#ifdef DEBUG
+ (void) fprintf(stderr, "got %d %s", num, buf);
+#endif
+ if (!continuation && num == 1)
+ continue;
+
+ ncontinuation = tok_line(tok, buf, &ac, &av) > 0;
+#if 0
+ if (continuation) {
+ /*
+ * Append to the right event in case the user
+ * moved around in history.
+ */
+ if (history(hist, &ev, H_SET, lastevent) == -1)
+ err(1, "%d: %s\n", lastevent, ev.str);
+ history(hist, &ev, H_ADD , buf);
+ } else {
+ history(hist, &ev, H_ENTER, buf);
+ lastevent = ev.num;
+ }
+#else
+ /* Simpler */
+ history(hist, &ev, continuation ? H_APPEND : H_ENTER, buf);
+#endif
+
+ continuation = ncontinuation;
+ ncontinuation = 0;
+
+ if (strcmp(av[0], "history") == 0) {
+ int rv;
+
+ switch (ac) {
+ case 1:
+ for (rv = history(hist, &ev, H_LAST); rv != -1;
+ rv = history(hist, &ev, H_PREV))
+ (void) fprintf(stdout, "%4d %s",
+ ev.num, ev.str);
+ break;
+
+ case 2:
+ if (strcmp(av[1], "clear") == 0)
+ history(hist, &ev, H_CLEAR);
+ else
+ goto badhist;
+ break;
+
+ case 3:
+ if (strcmp(av[1], "load") == 0)
+ history(hist, &ev, H_LOAD, av[2]);
+ else if (strcmp(av[1], "save") == 0)
+ history(hist, &ev, H_SAVE, av[2]);
+ break;
+
+ badhist:
+ default:
+ (void) fprintf(stderr,
+ "Bad history arguments\n");
+ break;
+ }
+ } else if (el_parse(el, ac, av) == -1) {
+ switch (fork()) {
+ case 0:
+ execvp(av[0], (char *const *)av);
+ perror(av[0]);
+ _exit(1);
+ /*NOTREACHED*/
+ break;
+
+ case -1:
+ perror("fork");
+ break;
+
+ default:
+ if (wait(&num) == -1)
+ perror("wait");
+ (void) fprintf(stderr, "Exit %x\n", num);
+ break;
+ }
+ }
+
+ tok_reset(tok);
+ }
+
+ el_end(el);
+ tok_end(tok);
+ history_end(hist);
+
+ return (0);
+}
diff --git a/trunk/main/editline/chared.c b/trunk/main/editline/chared.c
new file mode 100644
index 000000000..8eaeb3b54
--- /dev/null
+++ b/trunk/main/editline/chared.c
@@ -0,0 +1,695 @@
+/* $NetBSD: chared.c,v 1.15 2002/03/18 16:00:50 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: chared.c,v 1.15 2002/03/18 16:00:50 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * chared.c: Character editor utilities
+ */
+#include <stdlib.h>
+#include "el.h"
+
+/* value to leave unused in line buffer */
+#define EL_LEAVE 2
+
+/* cv_undo():
+ * Handle state for the vi undo command
+ */
+protected void
+cv_undo(EditLine *el,int action, size_t size, char *ptr)
+{
+ c_undo_t *vu = &el->el_chared.c_undo;
+ vu->action = action;
+ vu->ptr = ptr;
+ vu->isize = size;
+ (void) memcpy(vu->buf, vu->ptr, size);
+#ifdef DEBUG_UNDO
+ (void) fprintf(el->el_errfile, "Undo buffer \"%s\" size = +%d -%d\n",
+ vu->ptr, vu->isize, vu->dsize);
+#endif
+}
+
+
+/* c_insert():
+ * Insert num characters
+ */
+protected void
+c_insert(EditLine *el, int num)
+{
+ char *cp;
+
+ if (el->el_line.lastchar + num >= el->el_line.limit)
+ return; /* can't go past end of buffer */
+
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ /* if I must move chars */
+ for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
+ cp[num] = *cp;
+ }
+ el->el_line.lastchar += num;
+}
+
+
+/* c_delafter():
+ * Delete num characters after the cursor
+ */
+protected void
+c_delafter(EditLine *el, int num)
+{
+
+ if (el->el_line.cursor + num > el->el_line.lastchar)
+ num = el->el_line.lastchar - el->el_line.cursor;
+
+ if (num > 0) {
+ char *cp;
+
+ if (el->el_map.current != el->el_map.emacs)
+ cv_undo(el, INSERT, (size_t)num, el->el_line.cursor);
+
+ for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
+ *cp = cp[num];
+
+ el->el_line.lastchar -= num;
+ }
+}
+
+
+/* c_delbefore():
+ * Delete num characters before the cursor
+ */
+protected void
+c_delbefore(EditLine *el, int num)
+{
+
+ if (el->el_line.cursor - num < el->el_line.buffer)
+ num = el->el_line.cursor - el->el_line.buffer;
+
+ if (num > 0) {
+ char *cp;
+
+ if (el->el_map.current != el->el_map.emacs)
+ cv_undo(el, INSERT, (size_t)num,
+ el->el_line.cursor - num);
+
+ for (cp = el->el_line.cursor - num;
+ cp <= el->el_line.lastchar;
+ cp++)
+ *cp = cp[num];
+
+ el->el_line.lastchar -= num;
+ }
+}
+
+
+/* ce__isword():
+ * Return if p is part of a word according to emacs
+ */
+protected int
+ce__isword(int p)
+{
+ return (isalpha(p) || isdigit(p) || strchr("*?_-.[]~=", p) != NULL);
+}
+
+
+/* cv__isword():
+ * Return if p is part of a word according to vi
+ */
+protected int
+cv__isword(int p)
+{
+ return (!isspace(p));
+}
+
+
+/* c__prev_word():
+ * Find the previous word
+ */
+protected char *
+c__prev_word(char *p, char *low, int n, int (*wtest)(int))
+{
+ p--;
+
+ while (n--) {
+ while ((p >= low) && !(*wtest)((unsigned char) *p))
+ p--;
+ while ((p >= low) && (*wtest)((unsigned char) *p))
+ p--;
+ }
+
+ /* cp now points to one character before the word */
+ p++;
+ if (p < low)
+ p = low;
+ /* cp now points where we want it */
+ return (p);
+}
+
+
+/* c__next_word():
+ * Find the next word
+ */
+protected char *
+c__next_word(char *p, char *high, int n, int (*wtest)(int))
+{
+ while (n--) {
+ while ((p < high) && !(*wtest)((unsigned char) *p))
+ p++;
+ while ((p < high) && (*wtest)((unsigned char) *p))
+ p++;
+ }
+ if (p > high)
+ p = high;
+ /* p now points where we want it */
+ return (p);
+}
+
+/* cv_next_word():
+ * Find the next word vi style
+ */
+protected char *
+cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int))
+{
+ int test;
+
+ while (n--) {
+ test = (*wtest)((unsigned char) *p);
+ while ((p < high) && (*wtest)((unsigned char) *p) == test)
+ p++;
+ /*
+ * vi historically deletes with cw only the word preserving the
+ * trailing whitespace! This is not what 'w' does..
+ */
+ if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
+ while ((p < high) && isspace((unsigned char) *p))
+ p++;
+ }
+
+ /* p now points where we want it */
+ if (p > high)
+ return (high);
+ else
+ return (p);
+}
+
+
+/* cv_prev_word():
+ * Find the previous word vi style
+ */
+protected char *
+cv_prev_word(EditLine *el, char *p, char *low, int n, int (*wtest)(int))
+{
+ int test;
+
+ while (n--) {
+ p--;
+ /*
+ * vi historically deletes with cb only the word preserving the
+ * leading whitespace! This is not what 'b' does..
+ */
+ if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
+ while ((p > low) && isspace((unsigned char) *p))
+ p--;
+ test = (*wtest)((unsigned char) *p);
+ while ((p >= low) && (*wtest)((unsigned char) *p) == test)
+ p--;
+ p++;
+ while (isspace((unsigned char) *p))
+ p++;
+ }
+
+ /* p now points where we want it */
+ if (p < low)
+ return (low);
+ else
+ return (p);
+}
+
+
+#ifdef notdef
+/* c__number():
+ * Ignore character p points to, return number appearing after that.
+ * A '$' by itself means a big number; "$-" is for negative; '^' means 1.
+ * Return p pointing to last char used.
+ */
+protected char *
+c__number(
+ char *p, /* character position */
+ int *num, /* Return value */
+ int dval) /* dval is the number to subtract from like $-3 */
+{
+ int i;
+ int sign = 1;
+
+ if (*++p == '^') {
+ *num = 1;
+ return (p);
+ }
+ if (*p == '$') {
+ if (*++p != '-') {
+ *num = 0x7fffffff; /* Handle $ */
+ return (--p);
+ }
+ sign = -1; /* Handle $- */
+ ++p;
+ }
+ for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0')
+ continue;
+ *num = (sign < 0 ? dval - i : i);
+ return (--p);
+}
+#endif
+
+/* cv_delfini():
+ * Finish vi delete action
+ */
+protected void
+cv_delfini(EditLine *el)
+{
+ int size;
+ int oaction;
+
+ if (el->el_chared.c_vcmd.action & INSERT)
+ el->el_map.current = el->el_map.key;
+
+ oaction = el->el_chared.c_vcmd.action;
+ el->el_chared.c_vcmd.action = NOP;
+
+ if (el->el_chared.c_vcmd.pos == 0)
+ return;
+
+
+ if (el->el_line.cursor > el->el_chared.c_vcmd.pos) {
+ size = (int) (el->el_line.cursor - el->el_chared.c_vcmd.pos);
+ c_delbefore(el, size);
+ el->el_line.cursor = el->el_chared.c_vcmd.pos;
+ re_refresh_cursor(el);
+ } else if (el->el_line.cursor < el->el_chared.c_vcmd.pos) {
+ size = (int)(el->el_chared.c_vcmd.pos - el->el_line.cursor);
+ c_delafter(el, size);
+ } else {
+ size = 1;
+ c_delafter(el, size);
+ }
+ switch (oaction) {
+ case DELETE|INSERT:
+ el->el_chared.c_undo.action = DELETE|INSERT;
+ break;
+ case DELETE:
+ el->el_chared.c_undo.action = INSERT;
+ break;
+ case NOP:
+ case INSERT:
+ default:
+ EL_ABORT((el->el_errfile, "Bad oaction %d\n", oaction));
+ break;
+ }
+
+
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.dsize = size;
+}
+
+
+#ifdef notdef
+/* ce__endword():
+ * Go to the end of this word according to emacs
+ */
+protected char *
+ce__endword(char *p, char *high, int n)
+{
+ p++;
+
+ while (n--) {
+ while ((p < high) && isspace((unsigned char) *p))
+ p++;
+ while ((p < high) && !isspace((unsigned char) *p))
+ p++;
+ }
+
+ p--;
+ return (p);
+}
+#endif
+
+
+/* cv__endword():
+ * Go to the end of this word according to vi
+ */
+protected char *
+cv__endword(char *p, char *high, int n)
+{
+ p++;
+
+ while (n--) {
+ while ((p < high) && isspace((unsigned char) *p))
+ p++;
+
+ if (isalnum((unsigned char) *p))
+ while ((p < high) && isalnum((unsigned char) *p))
+ p++;
+ else
+ while ((p < high) && !(isspace((unsigned char) *p) ||
+ isalnum((unsigned char) *p)))
+ p++;
+ }
+ p--;
+ return (p);
+}
+
+/* ch_init():
+ * Initialize the character editor
+ */
+protected int
+ch_init(EditLine *el)
+{
+ el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_line.buffer == NULL)
+ return (-1);
+
+ (void) memset(el->el_line.buffer, 0, EL_BUFSIZ);
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - 2];
+
+ el->el_chared.c_undo.buf = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_chared.c_undo.buf == NULL)
+ return (-1);
+ (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ);
+ el->el_chared.c_undo.action = NOP;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ el->el_chared.c_undo.ptr = el->el_line.buffer;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = el->el_line.buffer;
+ el->el_chared.c_vcmd.ins = el->el_line.buffer;
+
+ el->el_chared.c_kill.buf = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_chared.c_kill.buf == NULL)
+ return (-1);
+ (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ);
+ el->el_chared.c_kill.mark = el->el_line.buffer;
+ el->el_chared.c_kill.last = el->el_chared.c_kill.buf;
+
+ el->el_map.current = el->el_map.key;
+
+ el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
+ el->el_state.doingarg = 0;
+ el->el_state.metanext = 0;
+ el->el_state.argument = 1;
+ el->el_state.lastcmd = ED_UNASSIGNED;
+
+ el->el_chared.c_macro.nline = NULL;
+ el->el_chared.c_macro.level = -1;
+ el->el_chared.c_macro.macro = (char **) el_malloc(EL_MAXMACRO *
+ sizeof(char *));
+ if (el->el_chared.c_macro.macro == NULL)
+ return (-1);
+ return (0);
+}
+
+/* ch_reset():
+ * Reset the character editor
+ */
+protected void
+ch_reset(EditLine *el)
+{
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_line.lastchar = el->el_line.buffer;
+
+ el->el_chared.c_undo.action = NOP;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ el->el_chared.c_undo.ptr = el->el_line.buffer;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = el->el_line.buffer;
+ el->el_chared.c_vcmd.ins = el->el_line.buffer;
+
+ el->el_chared.c_kill.mark = el->el_line.buffer;
+
+ el->el_map.current = el->el_map.key;
+
+ el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
+ el->el_state.doingarg = 0;
+ el->el_state.metanext = 0;
+ el->el_state.argument = 1;
+ el->el_state.lastcmd = ED_UNASSIGNED;
+
+ el->el_chared.c_macro.level = -1;
+
+ el->el_history.eventno = 0;
+}
+
+/* ch_enlargebufs():
+ * Enlarge line buffer to be able to hold twice as much characters.
+ * Returns 1 if successful, 0 if not.
+ */
+protected int
+ch_enlargebufs(el, addlen)
+ EditLine *el;
+ size_t addlen;
+{
+ size_t sz, newsz;
+ char *newbuffer, *oldbuf, *oldkbuf;
+
+ sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE;
+ newsz = sz * 2;
+ /*
+ * If newly required length is longer than current buffer, we need
+ * to make the buffer big enough to hold both old and new stuff.
+ */
+ if (addlen > sz) {
+ while(newsz - sz < addlen)
+ newsz *= 2;
+ }
+
+ /*
+ * Reallocate line buffer.
+ */
+ newbuffer = el_realloc(el->el_line.buffer, newsz);
+ if (!newbuffer)
+ return 0;
+
+ /* zero the newly added memory, leave old data in */
+ (void) memset(&newbuffer[sz], 0, newsz - sz);
+
+ oldbuf = el->el_line.buffer;
+
+ el->el_line.buffer = newbuffer;
+ el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf);
+ el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf);
+ el->el_line.limit = &newbuffer[newsz - EL_LEAVE];
+
+ /*
+ * Reallocate kill buffer.
+ */
+ newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz);
+ if (!newbuffer)
+ return 0;
+
+ /* zero the newly added memory, leave old data in */
+ (void) memset(&newbuffer[sz], 0, newsz - sz);
+
+ oldkbuf = el->el_chared.c_kill.buf;
+
+ el->el_chared.c_kill.buf = newbuffer;
+ el->el_chared.c_kill.last = newbuffer +
+ (el->el_chared.c_kill.last - oldkbuf);
+ el->el_chared.c_kill.mark = el->el_line.buffer +
+ (el->el_chared.c_kill.mark - oldbuf);
+
+ /*
+ * Reallocate undo buffer.
+ */
+ newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz);
+ if (!newbuffer)
+ return 0;
+
+ /* zero the newly added memory, leave old data in */
+ (void) memset(&newbuffer[sz], 0, newsz - sz);
+
+ el->el_chared.c_undo.ptr = el->el_line.buffer +
+ (el->el_chared.c_undo.ptr - oldbuf);
+ el->el_chared.c_undo.buf = newbuffer;
+
+ if (!hist_enlargebuf(el, sz, newsz))
+ return 0;
+
+ return 1;
+}
+
+/* ch_end():
+ * Free the data structures used by the editor
+ */
+protected void
+ch_end(EditLine *el)
+{
+ el_free((ptr_t) el->el_line.buffer);
+ el->el_line.buffer = NULL;
+ el->el_line.limit = NULL;
+ el_free((ptr_t) el->el_chared.c_undo.buf);
+ el->el_chared.c_undo.buf = NULL;
+ el_free((ptr_t) el->el_chared.c_kill.buf);
+ el->el_chared.c_kill.buf = NULL;
+ el_free((ptr_t) el->el_chared.c_macro.macro);
+ el->el_chared.c_macro.macro = NULL;
+ ch_reset(el);
+}
+
+
+/* el_insertstr():
+ * Insert string at cursorI
+ */
+public int
+el_insertstr(EditLine *el, const char *s)
+{
+ size_t len;
+
+ if ((len = strlen(s)) == 0)
+ return (-1);
+ if (el->el_line.lastchar + len >= el->el_line.limit) {
+ if (!ch_enlargebufs(el, len))
+ return (-1);
+ }
+
+ c_insert(el, (int)len);
+ while (*s)
+ *el->el_line.cursor++ = *s++;
+ return (0);
+}
+
+
+/* el_deletestr():
+ * Delete num characters before the cursor
+ */
+public void
+el_deletestr(EditLine *el, int n)
+{
+ if (n <= 0)
+ return;
+
+ if (el->el_line.cursor < &el->el_line.buffer[n])
+ return;
+
+ c_delbefore(el, n); /* delete before dot */
+ el->el_line.cursor -= n;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer;
+}
+
+/* c_gets():
+ * Get a string
+ */
+protected int
+c_gets(EditLine *el, char *buf)
+{
+ char ch;
+ int len = 0;
+
+ for (ch = 0; ch == 0;) {
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+ switch (ch) {
+ case 0010: /* Delete and backspace */
+ case 0177:
+ if (len > 1) {
+ *el->el_line.cursor-- = '\0';
+ el->el_line.lastchar = el->el_line.cursor;
+ buf[len--] = '\0';
+ } else {
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ return (CC_REFRESH);
+ }
+ re_refresh(el);
+ ch = 0;
+ break;
+
+ case 0033: /* ESC */
+ case '\r': /* Newline */
+ case '\n':
+ break;
+
+ default:
+ if (len >= EL_BUFSIZ)
+ term_beep(el);
+ else {
+ buf[len++] = ch;
+ *el->el_line.cursor++ = ch;
+ el->el_line.lastchar = el->el_line.cursor;
+ }
+ re_refresh(el);
+ ch = 0;
+ break;
+ }
+ }
+ buf[len] = ch;
+ return (len);
+}
+
+
+/* c_hpos():
+ * Return the current horizontal position of the cursor
+ */
+protected int
+c_hpos(EditLine *el)
+{
+ char *ptr;
+
+ /*
+ * Find how many characters till the beginning of this line.
+ */
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (0);
+ else {
+ for (ptr = el->el_line.cursor - 1;
+ ptr >= el->el_line.buffer && *ptr != '\n';
+ ptr--)
+ continue;
+ return (el->el_line.cursor - ptr - 1);
+ }
+}
diff --git a/trunk/main/editline/chared.h b/trunk/main/editline/chared.h
new file mode 100644
index 000000000..403eca011
--- /dev/null
+++ b/trunk/main/editline/chared.h
@@ -0,0 +1,159 @@
+/* $NetBSD: chared.h,v 1.8 2002/03/18 16:00:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)chared.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.chared.h: Character editor interface
+ */
+#ifndef _h_el_chared
+#define _h_el_chared
+
+#include <ctype.h>
+#include <string.h>
+
+#include "histedit.h"
+
+#define EL_MAXMACRO 10
+
+/*
+ * This is a issue of basic "vi" look-and-feel. Defining VI_MOVE works
+ * like real vi: i.e. the transition from command<->insert modes moves
+ * the cursor.
+ *
+ * On the other hand we really don't want to move the cursor, because
+ * all the editing commands don't include the character under the cursor.
+ * Probably the best fix is to make all the editing commands aware of
+ * this fact.
+ */
+#define VI_MOVE
+
+
+typedef struct c_macro_t {
+ int level;
+ char **macro;
+ char *nline;
+} c_macro_t;
+
+/*
+ * Undo information for both vi and emacs
+ */
+typedef struct c_undo_t {
+ int action;
+ size_t isize;
+ size_t dsize;
+ char *ptr;
+ char *buf;
+} c_undo_t;
+
+/*
+ * Current action information for vi
+ */
+typedef struct c_vcmd_t {
+ int action;
+ char *pos;
+ char *ins;
+} c_vcmd_t;
+
+/*
+ * Kill buffer for emacs
+ */
+typedef struct c_kill_t {
+ char *buf;
+ char *last;
+ char *mark;
+} c_kill_t;
+
+/*
+ * Note that we use both data structures because the user can bind
+ * commands from both editors!
+ */
+typedef struct el_chared_t {
+ c_undo_t c_undo;
+ c_kill_t c_kill;
+ c_vcmd_t c_vcmd;
+ c_macro_t c_macro;
+} el_chared_t;
+
+
+#define STReof "^D\b\b"
+#define STRQQ "\"\""
+
+#define isglob(a) (strchr("*[]?", (a)) != NULL)
+#define isword(a) (isprint(a))
+
+#define NOP 0x00
+#define DELETE 0x01
+#define INSERT 0x02
+#define CHANGE 0x04
+
+#define CHAR_FWD 0
+#define CHAR_BACK 1
+
+#define MODE_INSERT 0
+#define MODE_REPLACE 1
+#define MODE_REPLACE_1 2
+
+#include "common.h"
+#include "vi.h"
+#include "emacs.h"
+#include "search.h"
+#include "fcns.h"
+
+
+protected int cv__isword(int);
+protected void cv_delfini(EditLine *);
+protected char *cv__endword(char *, char *, int);
+protected int ce__isword(int);
+protected void cv_undo(EditLine *, int, size_t, char *);
+protected char *cv_next_word(EditLine*, char *, char *, int, int (*)(int));
+protected char *cv_prev_word(EditLine*, char *, char *, int, int (*)(int));
+protected char *c__next_word(char *, char *, int, int (*)(int));
+protected char *c__prev_word(char *, char *, int, int (*)(int));
+protected void c_insert(EditLine *, int);
+protected void c_delbefore(EditLine *, int);
+protected void c_delafter(EditLine *, int);
+protected int c_gets(EditLine *, char *);
+protected int c_hpos(EditLine *);
+
+protected int ch_init(EditLine *);
+protected void ch_reset(EditLine *);
+protected int ch_enlargebufs(EditLine *, size_t);
+protected void ch_end(EditLine *);
+
+#endif /* _h_el_chared */
diff --git a/trunk/main/editline/common.c b/trunk/main/editline/common.c
new file mode 100644
index 000000000..c831e79a3
--- /dev/null
+++ b/trunk/main/editline/common.c
@@ -0,0 +1,951 @@
+/* $NetBSD: common.c,v 1.11 2002/03/18 16:00:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)common.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: common.c,v 1.11 2002/03/18 16:00:51 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * common.c: Common Editor functions
+ */
+#include "el.h"
+
+/* ed_end_of_file():
+ * Indicate end of file
+ * [^D]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_end_of_file(EditLine *el, int c)
+{
+
+ re_goto_bottom(el);
+ *el->el_line.lastchar = '\0';
+ return (CC_EOF);
+}
+
+
+/* ed_insert():
+ * Add character to the line
+ * Insert a character [bound to all insert keys]
+ */
+protected el_action_t
+ed_insert(EditLine *el, int c)
+{
+ int i;
+
+ if (c == '\0')
+ return (CC_ERROR);
+
+ if (el->el_line.lastchar + el->el_state.argument >=
+ el->el_line.limit) {
+ /* end of buffer space, try to allocate more */
+ if (!ch_enlargebufs(el, (size_t) el->el_state.argument))
+ return CC_ERROR; /* error allocating more */
+ }
+
+ if (el->el_state.argument == 1) {
+ if (el->el_state.inputmode != MODE_INSERT) {
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] =
+ *el->el_line.cursor;
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] =
+ '\0';
+ c_delafter(el, 1);
+ }
+ c_insert(el, 1);
+
+ *el->el_line.cursor++ = c;
+ el->el_state.doingarg = 0; /* just in case */
+ re_fastaddc(el); /* fast refresh for one char. */
+ } else {
+ if (el->el_state.inputmode != MODE_INSERT) {
+ for (i = 0; i < el->el_state.argument; i++)
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] =
+ el->el_line.cursor[i];
+
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] =
+ '\0';
+ c_delafter(el, el->el_state.argument);
+ }
+ c_insert(el, el->el_state.argument);
+
+ while (el->el_state.argument--)
+ *el->el_line.cursor++ = c;
+ re_refresh(el);
+ }
+
+ if (el->el_state.inputmode == MODE_REPLACE_1)
+ (void) vi_command_mode(el, 0);
+
+ return (CC_NORM);
+}
+
+
+/* ed_delete_prev_word():
+ * Delete from beginning of current word to cursor
+ * [M-^?] [^W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_delete_prev_word(EditLine *el, int c)
+{
+ char *cp, *p, *kp;
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
+ el->el_state.argument, ce__isword);
+
+ for (p = cp, kp = el->el_chared.c_kill.buf; p < el->el_line.cursor; p++)
+ *kp++ = *p;
+ el->el_chared.c_kill.last = kp;
+
+ c_delbefore(el, el->el_line.cursor - cp); /* delete before dot */
+ el->el_line.cursor = cp;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer; /* bounds check */
+ return (CC_REFRESH);
+}
+
+
+/* ed_delete_next_char():
+ * Delete character under cursor
+ * [^D] [x]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_delete_next_char(EditLine *el, int c)
+{
+#ifdef notdef /* XXX */
+#define EL el->el_line
+ (void) fprintf(el->el_errlfile,
+ "\nD(b: %x(%s) c: %x(%s) last: %x(%s) limit: %x(%s)\n",
+ EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar,
+ EL.lastchar, EL.limit, EL.limit);
+#endif
+ if (el->el_line.cursor == el->el_line.lastchar) {
+ /* if I'm at the end */
+ if (el->el_map.type == MAP_VI) {
+ if (el->el_line.cursor == el->el_line.buffer) {
+ /* if I'm also at the beginning */
+#ifdef KSHVI
+ return (CC_ERROR);
+#else
+ term_overwrite(el, STReof, 4);
+ /* then do a EOF */
+ term__flush();
+ return (CC_EOF);
+#endif
+ } else {
+#ifdef KSHVI
+ el->el_line.cursor--;
+#else
+ return (CC_ERROR);
+#endif
+ }
+ } else {
+ if (el->el_line.cursor != el->el_line.buffer)
+ el->el_line.cursor--;
+ else
+ return (CC_ERROR);
+ }
+ }
+ c_delafter(el, el->el_state.argument); /* delete after dot */
+ if (el->el_line.cursor >= el->el_line.lastchar &&
+ el->el_line.cursor > el->el_line.buffer)
+ /* bounds check */
+ el->el_line.cursor = el->el_line.lastchar - 1;
+ return (CC_REFRESH);
+}
+
+
+/* ed_kill_line():
+ * Cut to the end of line
+ * [^K] [^K]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_kill_line(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ cp = el->el_line.cursor;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.lastchar)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ /* zap! -- delete to end */
+ el->el_line.lastchar = el->el_line.cursor;
+ return (CC_REFRESH);
+}
+
+
+/* ed_move_to_end():
+ * Move cursor to the end of line
+ * [^E] [^E]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_move_to_end(EditLine *el, int c)
+{
+
+ el->el_line.cursor = el->el_line.lastchar;
+ if (el->el_map.type == MAP_VI) {
+#ifdef VI_MOVE
+ el->el_line.cursor--;
+#endif
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_move_to_beg():
+ * Move cursor to the beginning of line
+ * [^A] [^A]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_move_to_beg(EditLine *el, int c)
+{
+
+ el->el_line.cursor = el->el_line.buffer;
+
+ if (el->el_map.type == MAP_VI) {
+ /* We want FIRST non space character */
+ while (isspace((unsigned char) *el->el_line.cursor))
+ el->el_line.cursor++;
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_transpose_chars():
+ * Exchange the character to the left of the cursor with the one under it
+ * [^T] [^T]
+ */
+protected el_action_t
+ed_transpose_chars(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ if (el->el_line.lastchar <= &el->el_line.buffer[1])
+ return (CC_ERROR);
+ else
+ el->el_line.cursor++;
+ }
+ if (el->el_line.cursor > &el->el_line.buffer[1]) {
+ /* must have at least two chars entered */
+ c = el->el_line.cursor[-2];
+ el->el_line.cursor[-2] = el->el_line.cursor[-1];
+ el->el_line.cursor[-1] = c;
+ return (CC_REFRESH);
+ } else
+ return (CC_ERROR);
+}
+
+
+/* ed_next_char():
+ * Move to the right one character
+ * [^F] [^F]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_next_char(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor >= el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor += el->el_state.argument;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_prev_word():
+ * Move to the beginning of the current word
+ * [M-b] [b]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ el->el_line.cursor = c__prev_word(el->el_line.cursor,
+ el->el_line.buffer,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_prev_char():
+ * Move to the left one character
+ * [^B] [^B]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor > el->el_line.buffer) {
+ el->el_line.cursor -= el->el_state.argument;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer;
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+ } else
+ return (CC_ERROR);
+}
+
+
+/* ed_quoted_insert():
+ * Add the next character typed verbatim
+ * [^V] [^V]
+ */
+protected el_action_t
+ed_quoted_insert(EditLine *el, int c)
+{
+ int num;
+ char tc;
+
+ tty_quotemode(el);
+ num = el_getc(el, &tc);
+ c = (unsigned char) tc;
+ tty_noquotemode(el);
+ if (num == 1)
+ return (ed_insert(el, c));
+ else
+ return (ed_end_of_file(el, 0));
+}
+
+
+/* ed_digit():
+ * Adds to argument or enters a digit
+ */
+protected el_action_t
+ed_digit(EditLine *el, int c)
+{
+
+ if (!isdigit(c))
+ return (CC_ERROR);
+
+ if (el->el_state.doingarg) {
+ /* if doing an arg, add this in... */
+ if (el->el_state.lastcmd == EM_UNIVERSAL_ARGUMENT)
+ el->el_state.argument = c - '0';
+ else {
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.argument =
+ (el->el_state.argument * 10) + (c - '0');
+ }
+ return (CC_ARGHACK);
+ } else {
+ if (el->el_line.lastchar + 1 >= el->el_line.limit) {
+ if (!ch_enlargebufs(el, 1))
+ return (CC_ERROR);
+ }
+
+ if (el->el_state.inputmode != MODE_INSERT) {
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] =
+ *el->el_line.cursor;
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] =
+ '\0';
+ c_delafter(el, 1);
+ }
+ c_insert(el, 1);
+ *el->el_line.cursor++ = c;
+ el->el_state.doingarg = 0;
+ re_fastaddc(el);
+ }
+ return (CC_NORM);
+}
+
+
+/* ed_argument_digit():
+ * Digit that starts argument
+ * For ESC-n
+ */
+protected el_action_t
+ed_argument_digit(EditLine *el, int c)
+{
+
+ if (!isdigit(c))
+ return (CC_ERROR);
+
+ if (el->el_state.doingarg) {
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.argument = (el->el_state.argument * 10) +
+ (c - '0');
+ } else { /* else starting an argument */
+ el->el_state.argument = c - '0';
+ el->el_state.doingarg = 1;
+ }
+ return (CC_ARGHACK);
+}
+
+
+/* ed_unassigned():
+ * Indicates unbound character
+ * Bound to keys that are not assigned
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_unassigned(EditLine *el, int c)
+{
+
+ term_beep(el);
+ term__flush();
+ return (CC_NORM);
+}
+
+
+/**
+ ** TTY key handling.
+ **/
+
+/* ed_tty_sigint():
+ * Tty interrupt character
+ * [^C]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_sigint(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_dsusp():
+ * Tty delayed suspend character
+ * [^Y]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_dsusp(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_flush_output():
+ * Tty flush output characters
+ * [^O]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_flush_output(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_sigquit():
+ * Tty quit character
+ * [^\]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_sigquit(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_sigtstp():
+ * Tty suspend character
+ * [^Z]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_sigtstp(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_stop_output():
+ * Tty disallow output characters
+ * [^S]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_stop_output(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_start_output():
+ * Tty allow output characters
+ * [^Q]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_start_output(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_newline():
+ * Execute command
+ * [^J]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_newline(EditLine *el, int c)
+{
+
+ re_goto_bottom(el);
+ *el->el_line.lastchar++ = '\n';
+ *el->el_line.lastchar = '\0';
+ if (el->el_map.type == MAP_VI)
+ el->el_chared.c_vcmd.ins = el->el_line.buffer;
+ return (CC_NEWLINE);
+}
+
+
+/* ed_delete_prev_char():
+ * Delete the character to the left of the cursor
+ * [^?]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_delete_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor <= el->el_line.buffer)
+ return (CC_ERROR);
+
+ c_delbefore(el, el->el_state.argument);
+ el->el_line.cursor -= el->el_state.argument;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer;
+ return (CC_REFRESH);
+}
+
+
+/* ed_clear_screen():
+ * Clear screen leaving current line at the top
+ * [^L]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_clear_screen(EditLine *el, int c)
+{
+
+ term_clear_screen(el); /* clear the whole real screen */
+ re_clear_display(el); /* reset everything */
+ return (CC_REFRESH);
+}
+
+
+/* ed_redisplay():
+ * Redisplay everything
+ * ^R
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_redisplay(EditLine *el, int c)
+{
+
+ return (CC_REDISPLAY);
+}
+
+
+/* ed_start_over():
+ * Erase current line and start from scratch
+ * [^G]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_start_over(EditLine *el, int c)
+{
+
+ ch_reset(el);
+ return (CC_REFRESH);
+}
+
+
+/* ed_sequence_lead_in():
+ * First character in a bound sequence
+ * Placeholder for external keys
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_sequence_lead_in(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_prev_history():
+ * Move to the previous history line
+ * [^P] [k]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_history(EditLine *el, int c)
+{
+ char beep = 0;
+
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+
+ if (el->el_history.eventno == 0) { /* save the current buffer
+ * away */
+ (void) strncpy(el->el_history.buf, el->el_line.buffer,
+ EL_BUFSIZ - 1);
+ el->el_history.last = el->el_history.buf +
+ (el->el_line.lastchar - el->el_line.buffer);
+ }
+ el->el_history.eventno += el->el_state.argument;
+
+ if (hist_get(el) == CC_ERROR) {
+ beep = 1;
+ /* el->el_history.eventno was fixed by first call */
+ (void) hist_get(el);
+ }
+ re_refresh(el);
+ if (beep)
+ return (CC_ERROR);
+ else
+ return (CC_NORM); /* was CC_UP_HIST */
+}
+
+
+/* ed_next_history():
+ * Move to the next history line
+ * [^N] [j]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_next_history(EditLine *el, int c)
+{
+
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+
+ el->el_history.eventno -= el->el_state.argument;
+
+ if (el->el_history.eventno < 0) {
+ el->el_history.eventno = 0;
+ return (CC_ERROR);/* make it beep */
+ }
+ return (hist_get(el));
+}
+
+
+/* ed_search_prev_history():
+ * Search previous in history for a line matching the current
+ * next search history [M-P] [K]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_search_prev_history(EditLine *el, int c)
+{
+ const char *hp;
+ int h;
+ bool_t found = 0;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+ if (el->el_history.eventno < 0) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile,
+ "e_prev_search_hist(): eventno < 0;\n");
+#endif
+ el->el_history.eventno = 0;
+ return (CC_ERROR);
+ }
+ if (el->el_history.eventno == 0) {
+ (void) strncpy(el->el_history.buf, el->el_line.buffer,
+ EL_BUFSIZ - 1);
+ el->el_history.last = el->el_history.buf +
+ (el->el_line.lastchar - el->el_line.buffer);
+ }
+ if (el->el_history.ref == NULL)
+ return (CC_ERROR);
+
+ hp = HIST_FIRST(el);
+ if (hp == NULL)
+ return (CC_ERROR);
+
+ c_setpat(el); /* Set search pattern !! */
+
+ for (h = 1; h <= el->el_history.eventno; h++)
+ hp = HIST_NEXT(el);
+
+ while (hp != NULL) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
+#endif
+ if ((strncmp(hp, el->el_line.buffer, (size_t)
+ (el->el_line.lastchar - el->el_line.buffer)) ||
+ hp[el->el_line.lastchar - el->el_line.buffer]) &&
+ c_hmatch(el, hp)) {
+ found++;
+ break;
+ }
+ h++;
+ hp = HIST_NEXT(el);
+ }
+
+ if (!found) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "not found\n");
+#endif
+ return (CC_ERROR);
+ }
+ el->el_history.eventno = h;
+
+ return (hist_get(el));
+}
+
+
+/* ed_search_next_history():
+ * Search next in history for a line matching the current
+ * [M-N] [J]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_search_next_history(EditLine *el, int c)
+{
+ const char *hp;
+ int h;
+ bool_t found = 0;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+
+ if (el->el_history.eventno == 0)
+ return (CC_ERROR);
+
+ if (el->el_history.ref == NULL)
+ return (CC_ERROR);
+
+ hp = HIST_FIRST(el);
+ if (hp == NULL)
+ return (CC_ERROR);
+
+ c_setpat(el); /* Set search pattern !! */
+
+ for (h = 1; h < el->el_history.eventno && hp; h++) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
+#endif
+ if ((strncmp(hp, el->el_line.buffer, (size_t)
+ (el->el_line.lastchar - el->el_line.buffer)) ||
+ hp[el->el_line.lastchar - el->el_line.buffer]) &&
+ c_hmatch(el, hp))
+ found = h;
+ hp = HIST_NEXT(el);
+ }
+
+ if (!found) { /* is it the current history number? */
+ if (!c_hmatch(el, el->el_history.buf)) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "not found\n");
+#endif
+ return (CC_ERROR);
+ }
+ }
+ el->el_history.eventno = found;
+
+ return (hist_get(el));
+}
+
+
+/* ed_prev_line():
+ * Move up one line
+ * Could be [k] [^p]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_line(EditLine *el, int c)
+{
+ char *ptr;
+ int nchars = c_hpos(el);
+
+ /*
+ * Move to the line requested
+ */
+ if (*(ptr = el->el_line.cursor) == '\n')
+ ptr--;
+
+ for (; ptr >= el->el_line.buffer; ptr--)
+ if (*ptr == '\n' && --el->el_state.argument <= 0)
+ break;
+
+ if (el->el_state.argument > 0)
+ return (CC_ERROR);
+
+ /*
+ * Move to the beginning of the line
+ */
+ for (ptr--; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--)
+ continue;
+
+ /*
+ * Move to the character requested
+ */
+ for (ptr++;
+ nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n';
+ ptr++)
+ continue;
+
+ el->el_line.cursor = ptr;
+ return (CC_CURSOR);
+}
+
+
+/* ed_next_line():
+ * Move down one line
+ * Could be [j] [^n]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_next_line(EditLine *el, int c)
+{
+ char *ptr;
+ int nchars = c_hpos(el);
+
+ /*
+ * Move to the line requested
+ */
+ for (ptr = el->el_line.cursor; ptr < el->el_line.lastchar; ptr++)
+ if (*ptr == '\n' && --el->el_state.argument <= 0)
+ break;
+
+ if (el->el_state.argument > 0)
+ return (CC_ERROR);
+
+ /*
+ * Move to the character requested
+ */
+ for (ptr++;
+ nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n';
+ ptr++)
+ continue;
+
+ el->el_line.cursor = ptr;
+ return (CC_CURSOR);
+}
+
+
+/* ed_command():
+ * Editline extended command
+ * [M-X] [:]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_command(EditLine *el, int c)
+{
+ char tmpbuf[EL_BUFSIZ];
+ int tmplen;
+
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+
+ c_insert(el, 3); /* prompt + ": " */
+ *el->el_line.cursor++ = '\n';
+ *el->el_line.cursor++ = ':';
+ *el->el_line.cursor++ = ' ';
+ re_refresh(el);
+
+ tmplen = c_gets(el, tmpbuf);
+ tmpbuf[tmplen] = '\0';
+
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+
+ if (parse_line(el, tmpbuf) == -1)
+ return (CC_ERROR);
+ else
+ return (CC_REFRESH);
+}
diff --git a/trunk/main/editline/config.guess b/trunk/main/editline/config.guess
new file mode 100755
index 000000000..a6d8a945f
--- /dev/null
+++ b/trunk/main/editline/config.guess
@@ -0,0 +1,1449 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+timestamp='2004-06-24'
+
+# This file 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.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amd64:OpenBSD:*:*)
+ echo x86_64-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ cats:OpenBSD:*:*)
+ echo arm-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ luna88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:MirBSD:*:*)
+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms && exit 0 ;;
+ I*) echo ia64-dec-vms && exit 0 ;;
+ V*) echo vax-dec-vms && exit 0 ;;
+ esac
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/editline/config.h.in b/trunk/main/editline/config.h.in
new file mode 100644
index 000000000..151fb226d
--- /dev/null
+++ b/trunk/main/editline/config.h.in
@@ -0,0 +1,21 @@
+#undef SUNOS
+#undef CYGWIN
+
+#undef HAVE_SYS_CDEFS_H
+#undef HAVE_TERMCAP_H
+#undef HAVE_CURSES_H
+#undef HAVE_NCURSES_H
+#undef HAVE_TERM_H
+#undef HAVE_VIS_H
+#undef HAVE_ISSETUGID
+
+#undef HAVE_STRLCAT
+#undef HAVE_STRLCPY
+#undef HAVE_FGETLN
+#undef HAVE_STRVIS
+#undef HAVE_STRUNVIS
+
+#include "sys.h"
+#ifdef CYGWIN
+# include "cygdef.h"
+#endif
diff --git a/trunk/main/editline/config.sub b/trunk/main/editline/config.sub
new file mode 100755
index 000000000..838237e98
--- /dev/null
+++ b/trunk/main/editline/config.sub
@@ -0,0 +1,1412 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2001-09-14'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file 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.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dsp16xx \
+ | fr30 \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el | mips64vr4300 \
+ | mips64vr4300el | mips64vr5000 | mips64vr5000el \
+ | mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \
+ | mipsisa32 \
+ | mn10200 | mn10300 \
+ | ns16k | ns32k \
+ | openrisc \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | s390 | s390x \
+ | sh | sh[34] | sh[34]eb | shbe | shle \
+ | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+ | stormy16 | strongarm \
+ | tahoe | thumb | tic80 | tron \
+ | v850 \
+ | we32k \
+ | x86 | xscale \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alphapca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c54x-* \
+ | clipper-* | cray2-* | cydra-* \
+ | d10v-* | d30v-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | m32r-* \
+ | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+ | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \
+ | mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | s390-* | s390x-* \
+ | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | stormy16-* | strongarm-* | sv1-* \
+ | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+ | v850-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [cjt]90)
+ basic_machine=${basic_machine}-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i686-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ windows32)
+ basic_machine=i386-pc
+ os=-windows32-msvcrt
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh3eb | sh4eb)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto*)
+ os=-nto-qnx
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/editline/configure b/trunk/main/editline/configure
new file mode 100755
index 000000000..bb250aea1
--- /dev/null
+++ b/trunk/main/editline/configure
@@ -0,0 +1,2309 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --disable-readline Disable readline compatibility"
+ac_help="$ac_help
+ --enable-debug Enable debugging code"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=Makefile.in
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+CFLAGS=$CFLAGS
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:534: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:564: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:615: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:647: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 658 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:663: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:689: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:694: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:703: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:722: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+if test "x$CFLAGS" = "x" ; then
+ no_CFLAGS="yes"
+fi
+if test "x$no_CFLAGS" = "xyes" -a "x$GCC" = "xyes" ; then
+ CFLAGS="-Wall -pipe -g3"
+fi
+A_CFLAGS=""
+
+S_CFLAGS="-fPIC -DPIC"
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:764: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 779 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:785: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 796 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:802: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 813 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:819: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+
+ac_aux_dir=
+for ac_dir in ${GNUSYSTEM_AUX_DIR} $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:870: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+case "${host}" in
+ *-*-darwin*)
+ CFLAGS="$CFLAGS -fno-common -no-cpp-precomp"
+ ABI="macho"
+ ;;
+ *-*-freebsd*)
+ ABI="elf"
+ ;;
+ *-*-linux* | *cygwin*)
+ if echo ${host} | grep -q cygwin ; then \
+ echo "cygwin detected"; \
+ S_CFLAGS=""; \
+ echo "/* cygdef.h. Generated automatically by configure. */
+#ifndef _CYGDEF_H_
+#define _CYGDEF_H_ 1
+#include <sys/ioctl.h>
+#define __linux__ 1
+
+
+typedef void (*sig_t)(int);
+
+
+#endif /* _CYGDEF_H_ */" > cygdef.h; \
+ echo "
+ #define CYGWIN 1
+" > confdefs.h; \
+ fi
+ ABI="elf"
+ ;;
+ *-*-netbsd*)
+ echo $ac_n "checking ABI""... $ac_c" 1>&6
+echo "configure:903: checking ABI" >&5
+ cat > conftest.$ac_ext <<EOF
+#line 905 "configure"
+#include "confdefs.h"
+#ifdef __ELF__
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ABI="elf"
+else
+ rm -rf conftest*
+ ABI="aout"
+fi
+rm -f conftest*
+
+ echo "$ac_t""$ABI" 1>&6
+ ;;
+ *-*-solaris2*)
+ ABI="elf"
+ cat >> confdefs.h <<\EOF
+#define SUNOS 1
+EOF
+
+ ;;
+ *)
+ echo "$ac_t""Unsupported operating system: ${host}" 1>&6
+ ABI="elf"
+ ;;
+esac
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:949: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1004: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1034: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_AR'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$AR" in
+ /*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ ?:/*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a dos path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_AR="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac
+fi
+AR="$ac_cv_path_AR"
+if test -n "$AR"; then
+ echo "$ac_t""$AR" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking for tgetent in -ltermcap""... $ac_c" 1>&6
+echo "configure:1068: checking for tgetent in -ltermcap" >&5
+ac_lib_var=`echo termcap'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltermcap $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1076 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1087: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo termcap | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltermcap $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo $ac_n "checking for tgetent in -ltinfo""... $ac_c" 1>&6
+echo "configure:1114: checking for tgetent in -ltinfo" >&5
+ac_lib_var=`echo tinfo'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltinfo $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1122 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1133: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tinfo | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltinfo $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo $ac_n "checking for tgetent in -lcurses""... $ac_c" 1>&6
+echo "configure:1160: checking for tgetent in -lcurses" >&5
+ac_lib_var=`echo curses'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lcurses $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1168 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1179: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo curses | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lcurses $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo $ac_n "checking for tgetent in -lncurses""... $ac_c" 1>&6
+echo "configure:1206: checking for tgetent in -lncurses" >&5
+ac_lib_var=`echo ncurses'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lncurses $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1214 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1225: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo ncurses | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lncurses $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ { echo "configure: error: termcap support not found" 1>&2; exit 1; }
+fi
+
+fi
+
+fi
+
+fi
+
+
+for ac_hdr in termcap.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1265: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1270 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1275: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ for ac_hdr in term.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1303: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1308 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1313: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo "$ac_t""Need term.h since termcap.h is missing" 1>&6
+fi
+done
+
+ for ac_hdr in curses.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1345: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1350 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1355: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ for ac_hdr in ncurses.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1383: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1388 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1393: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo "$ac_t""Need curses.h or ncurses.h" 1>&6
+fi
+done
+
+fi
+done
+
+fi
+done
+
+
+for ac_hdr in sys/cdefs.h vis.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1432: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1437 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1442: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+for ac_func in issetugid
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1472: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1477 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1500: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in fgetln
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1639: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1644 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1667: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+CCSRCS="$CCSRCS np/fgetln.c"
+fi
+done
+
+for ac_func in strvis
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1695: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1700 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1723: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+CCSRCS="$CCSRCS np/vis.c"
+fi
+done
+
+for ac_func in strunvis
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1751: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1756 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1779: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+CCSRCS="$CCSRCS np/unvis.c"
+fi
+done
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1806 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef __RCSID
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D__RCSID(x)='"
+fi
+rm -f conftest*
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1825 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef __COPYRIGHT
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D__COPYRIGHT(x)='"
+fi
+rm -f conftest*
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1844 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef __RENAME
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D__RENAME(x)='"
+fi
+rm -f conftest*
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1863 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef _DIAGASSERT
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D_DIAGASSERT(x)='"
+fi
+rm -f conftest*
+
+
+# Check whether --enable-readline or --disable-readline was given.
+if test "${enable_readline+set}" = set; then
+ enableval="$enable_readline"
+ if test "x$enable_readline" != "xyes" ; then
+ enable_readline="no"
+fi
+
+else
+ enable_readline="yes"
+
+fi
+
+
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval="$enable_debug"
+ if test "x$enable_debug" != "xyes" ; then
+ enable_debug="no"
+fi
+
+else
+ enable_debug="no"
+
+fi
+
+if test "x$enable_debug" = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -DDEBUG_TTY -DDEBUG_KEY -DDEBUG_READ -DDEBUG"
+ CPPFLAGS="$CPPFLAGS -DDEBUG_REFRESH -DDEBUG_PASTE"
+fi
+
+
+
+ACSRCS="common.c emacs.c vi.c"
+BCSRCS="chared.c el.c hist.c key.c map.c parse.c prompt.c read.c refresh.c search.c sig.c term.c tty.c"
+CCSRCS="$CCSRCS history.c tokenizer.c"
+
+AGCSRCS="fcns.c help.c"
+BGCSRCS="editline.c"
+
+HDRS="chared.h el.h hist.h key.h map.h parse.h prompt.h refresh.h search.h sig.h sys.h term.h tokenizer.h tty.h"
+
+IHDRS="histedit.h"
+
+IHDR_LINKS=
+
+AGHDRS="common.h emacs.h vi.h"
+BGHDRS="fcns.h help.h"
+
+HDR_DIRS="include"
+
+MAN3="editline.3"
+MAN5="editrc.5"
+
+MAN3_LINKS=
+for i in el_init.3 el_end.3 el_reset.3 el_gets.3 el_getc.3 el_push.3 \
+ el_parse.3 el_set.3 el_get.3 el_source.3 el_resize.3 el_line.3 \
+ el_insertstr.3 el_deletestr.3 history_init.3 history_end.3 \
+ history.3 ; do
+ MAN3_LINKS="$MAN3_LINKS editline.3 $i"
+done
+
+MAN_DIRS="man/man3 man/man5"
+
+LIB_DIRS="lib"
+LIB_MAJOR="2"
+LIB_MINOR="6"
+LIB_A="libedit.a"
+LIB_A_LINKS=
+
+if test "x$ABI" = "xelf" ; then
+ LIB_S="libedit.so.$LIB_MAJOR"
+ LIB_S_LINK="libedit.so"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xaout" ; then
+ LIB_S="libedit.so.$LIB_MAJOR.$LIB_MINOR"
+ LIB_S_LINKS=
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xmacho" ; then
+ S_LDFLAGS="-shared"
+ LIB_S="libedit.$LIB_MAJOR.dylib"
+ LIB_S_LINK="libedit.dylib"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ if test "x$prefix" = "xNONE" ; then
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name /usr/local/lib/$LIB_S"
+ else
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name $prefix/lib/$LIB_S"
+ fi
+fi
+
+TEST="TEST/test"
+TCSRCS="TEST/test.c"
+
+if test "x$enable_readline" = "xyes" ; then
+ CCSRCS="$CCSRCS readline.c"
+ IHDRS="$IHDRS readline/readline.h"
+ IHDR_LINKS="readline.h readline/history.h"
+ HDR_DIRS="$HDR_DIRS include/readline"
+ LIB_A_LINKS="$LIB_A_LINKS libedit.a libreadline.a"
+ if test "x$ABI" = "xelf" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.so"
+ elif test "x$ABI" = "xaout" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S libreadline.so.$LIB_MAJOR.$LIB_MINOR"
+ elif test "x$ABI" = "xmacho" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.dylib"
+ fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@A_CFLAGS@%$A_CFLAGS%g
+s%@S_CFLAGS@%$S_CFLAGS%g
+s%@CPP@%$CPP%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@RANLIB@%$RANLIB%g
+s%@AR@%$AR%g
+s%@ACSRCS@%$ACSRCS%g
+s%@BCSRCS@%$BCSRCS%g
+s%@CCSRCS@%$CCSRCS%g
+s%@AGCSRCS@%$AGCSRCS%g
+s%@BGCSRCS@%$BGCSRCS%g
+s%@HDRS@%$HDRS%g
+s%@IHDRS@%$IHDRS%g
+s%@IHDR_LINKS@%$IHDR_LINKS%g
+s%@AGHDRS@%$AGHDRS%g
+s%@BGHDRS@%$BGHDRS%g
+s%@HDR_DIRS@%$HDR_DIRS%g
+s%@MAN3@%$MAN3%g
+s%@MAN5@%$MAN5%g
+s%@MAN3_LINKS@%$MAN3_LINKS%g
+s%@MAN_DIRS@%$MAN_DIRS%g
+s%@LIB_DIRS@%$LIB_DIRS%g
+s%@LIB_VER@%$LIB_VER%g
+s%@LIB_A@%$LIB_A%g
+s%@LIB_A_LINKS@%$LIB_A_LINKS%g
+s%@LIB_S@%$LIB_S%g
+s%@LIB_S_LINKS@%$LIB_S_LINKS%g
+s%@S_LDFLAGS@%$S_LDFLAGS%g
+s%@TEST@%$TEST%g
+s%@TCSRCS@%$TCSRCS%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/trunk/main/editline/configure.in b/trunk/main/editline/configure.in
new file mode 100644
index 000000000..7feebd696
--- /dev/null
+++ b/trunk/main/editline/configure.in
@@ -0,0 +1,274 @@
+dnl
+dnl Process this file with autoconf to produce a configure script.
+dnl
+AC_INIT(Makefile.in)
+
+dnl If CFLAGS isn't defined and using gcc, set CFLAGS to something reasonable.
+dnl Otherwise, just prevent autoconf from molesting CFLAGS.
+CFLAGS=$CFLAGS
+AC_PROG_CC
+if test "x$CFLAGS" = "x" ; then
+ no_CFLAGS="yes"
+fi
+if test "x$no_CFLAGS" = "xyes" -a "x$GCC" = "xyes" ; then
+ CFLAGS="-Wall -pipe -g3"
+fi
+A_CFLAGS=""
+AC_SUBST(A_CFLAGS)
+S_CFLAGS="-fPIC -DPIC"
+AC_SUBST(S_CFLAGS)
+AC_PROG_CPP
+
+dnl Platform-specific settings. The ABI can probably be determined
+dnl programmatically, but doing so is error-prone, which makes it generally
+dnl not worth the trouble.
+AC_CANONICAL_HOST
+case "${host}" in
+ *-*-darwin*)
+ CFLAGS="$CFLAGS -fno-common -no-cpp-precomp"
+ ABI="macho"
+ ;;
+ *-*-freebsd*)
+ ABI="elf"
+ ;;
+ *-*-linux* | *cygwin*)
+ if echo ${host} | grep -q cygwin ; then \
+ echo "cygwin detected"; \
+ S_CFLAGS=""; \
+ echo "/* cygdef.h. Generated automatically by configure. */
+#ifndef _CYGDEF_H_
+#define _CYGDEF_H_ 1
+#include <sys/ioctl.h>
+#define __linux__ 1
+
+
+typedef void (*sig_t)(int);
+
+
+#endif /* _CYGDEF_H_ */" > cygdef.h; \
+ echo "
+ #define CYGWIN 1
+" > confdefs.h; \
+ fi
+ ABI="elf"
+ ;;
+ *-*-netbsd*)
+ AC_MSG_CHECKING(ABI)
+ AC_EGREP_CPP(yes,
+[#ifdef __ELF__
+ yes
+#endif
+],
+ ABI="elf",
+ ABI="aout")
+ AC_MSG_RESULT($ABI)
+ ;;
+ *-*-solaris2*)
+ ABI="elf"
+ AC_DEFINE(SUNOS)
+ ;;
+ *)
+ AC_MSG_RESULT(Unsupported operating system: ${host})
+ ABI="elf"
+ ;;
+esac
+
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_PATH_PROG(AR, ar, , $PATH)
+
+dnl Search for termcap access routines in termcap, tinfo, curses, and ncurses.
+AC_CHECK_LIB(termcap, tgetent, , \
+ AC_CHECK_LIB(tinfo, tgetent, , \
+ AC_CHECK_LIB(curses, tgetent, , \
+ AC_CHECK_LIB(ncurses, tgetent, , \
+ AC_MSG_ERROR(termcap support not found)))))
+
+dnl Use termcap.h if it exists; otherwise we need both term.h and [n]curses.h.
+AC_CHECK_HEADERS(termcap.h, , \
+ AC_CHECK_HEADERS(term.h, , \
+ AC_MSG_RESULT(Need term.h since termcap.h is missing))
+ AC_CHECK_HEADERS(curses.h, , \
+ AC_CHECK_HEADERS(ncurses.h, , \
+ AC_MSG_RESULT(Need curses.h or ncurses.h))))
+
+AC_CHECK_HEADERS(sys/cdefs.h vis.h)
+
+AC_CHECK_FUNCS(issetugid)
+AC_CHECK_FUNCS(fgetln, , CCSRCS="$CCSRCS np/fgetln.c")
+AC_CHECK_FUNCS(strvis, , CCSRCS="$CCSRCS np/vis.c")
+AC_CHECK_FUNCS(strunvis, , CCSRCS="$CCSRCS np/unvis.c")
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef __RCSID
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D__RCSID(x)='"])
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef __COPYRIGHT
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D__COPYRIGHT(x)='"])
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef __RENAME
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D__RENAME(x)='"])
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef _DIAGASSERT
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D_DIAGASSERT(x)='"])
+
+dnl Enable readline compatibility by default.
+AC_ARG_ENABLE(readline, [ --disable-readline Disable readline compatibility],
+if test "x$enable_readline" != "xyes" ; then
+ enable_readline="no"
+fi
+,
+enable_readline="yes"
+)
+
+dnl Optionally enable debugging.
+AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging code],
+if test "x$enable_debug" != "xyes" ; then
+ enable_debug="no"
+fi
+,
+enable_debug="no"
+)
+if test "x$enable_debug" = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -DDEBUG_TTY -DDEBUG_KEY -DDEBUG_READ -DDEBUG"
+ CPPFLAGS="$CPPFLAGS -DDEBUG_REFRESH -DDEBUG_PASTE"
+else
+ CFLAGS="$CFLAGS -O"
+fi
+
+
+dnl
+dnl File lists. This is done here instead of in the Makefile in order to avoid
+dnl the need for conditionals.
+dnl
+
+dnl .c files.
+ACSRCS="common.c emacs.c vi.c"
+BCSRCS="chared.c el.c hist.c key.c map.c parse.c prompt.c read.c refresh.c search.c sig.c term.c tty.c"
+CCSRCS="$CCSRCS history.c tokenizer.c"
+
+dnl Generated .c files.
+AGCSRCS="fcns.c help.c"
+BGCSRCS="editline.c"
+
+dnl .h files.
+HDRS="chared.h el.h hist.h key.h map.h parse.h prompt.h refresh.h search.h sig.h sys.h term.h tokenizer.h tty.h"
+
+dnl Installed .h files.
+IHDRS="histedit.h"
+
+dnl Installed headers for readline compatibility.
+IHDR_LINKS=
+
+dnl Generated .h files.
+AGHDRS="common.h emacs.h vi.h"
+BGHDRS="fcns.h help.h"
+
+dnl Header installation directories.
+HDR_DIRS="include"
+
+dnl Man pages.
+MAN3="editline.3"
+MAN5="editrc.5"
+
+MAN3_LINKS=
+for i in el_init.3 el_end.3 el_reset.3 el_gets.3 el_getc.3 el_push.3 \
+ el_parse.3 el_set.3 el_get.3 el_source.3 el_resize.3 el_line.3 \
+ el_insertstr.3 el_deletestr.3 history_init.3 history_end.3 \
+ history.3 ; do
+ MAN3_LINKS="$MAN3_LINKS editline.3 $i"
+done
+
+dnl Man page installation directories.
+MAN_DIRS="man/man3 man/man5"
+
+dnl Library settings.
+LIB_DIRS="lib"
+LIB_MAJOR="2"
+LIB_MINOR="6"
+LIB_A="libedit.a"
+LIB_A_LINKS=
+
+if test "x$ABI" = "xelf" ; then
+ LIB_S="libedit.so.$LIB_MAJOR"
+ LIB_S_LINK="libedit.so"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xaout" ; then
+ LIB_S="libedit.so.$LIB_MAJOR.$LIB_MINOR"
+ LIB_S_LINKS=
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xmacho" ; then
+ S_LDFLAGS="-shared"
+ LIB_S="libedit.$LIB_MAJOR.dylib"
+ LIB_S_LINK="libedit.dylib"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ if test "x$prefix" = "xNONE" ; then
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name /usr/local/lib/$LIB_S"
+ else
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name $prefix/lib/$LIB_S"
+ fi
+fi
+
+dnl Test program.
+TEST="TEST/test"
+TCSRCS="TEST/test.c"
+
+dnl Add files to the lists if readline compatibility is enabled.
+if test "x$enable_readline" = "xyes" ; then
+ CCSRCS="$CCSRCS readline.c"
+ IHDRS="$IHDRS readline/readline.h"
+ IHDR_LINKS="readline.h readline/history.h"
+ HDR_DIRS="$HDR_DIRS include/readline"
+ LIB_A_LINKS="$LIB_A_LINKS libedit.a libreadline.a"
+ if test "x$ABI" = "xelf" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.so"
+ elif test "x$ABI" = "xaout" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S libreadline.so.$LIB_MAJOR.$LIB_MINOR"
+ elif test "x$ABI" = "xmacho" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.dylib"
+ fi
+fi
+
+AC_SUBST(ACSRCS)
+AC_SUBST(BCSRCS)
+AC_SUBST(CCSRCS)
+AC_SUBST(AGCSRCS)
+AC_SUBST(BGCSRCS)
+AC_SUBST(HDRS)
+AC_SUBST(IHDRS)
+AC_SUBST(IHDR_LINKS)
+AC_SUBST(AGHDRS)
+AC_SUBST(BGHDRS)
+AC_SUBST(HDR_DIRS)
+AC_SUBST(MAN3)
+AC_SUBST(MAN5)
+AC_SUBST(MAN3_LINKS)
+AC_SUBST(MAN_DIRS)
+AC_SUBST(LIB_DIRS)
+AC_SUBST(LIB_VER)
+AC_SUBST(LIB_A)
+AC_SUBST(LIB_A_LINKS)
+AC_SUBST(LIB_S)
+AC_SUBST(LIB_S_LINKS)
+AC_SUBST(S_LDFLAGS)
+AC_SUBST(TEST)
+AC_SUBST(TCSRCS)
+
+AC_CONFIG_HEADER(config.h)
+AC_OUTPUT(Makefile)
diff --git a/trunk/main/editline/editline.3 b/trunk/main/editline/editline.3
new file mode 100644
index 000000000..28f6ddb84
--- /dev/null
+++ b/trunk/main/editline/editline.3
@@ -0,0 +1,646 @@
+.\" $NetBSD: editline.3,v 1.25 2002/01/15 02:46:22 wiz Exp $
+.\"
+.\" Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This file was contributed to The NetBSD Foundation by Luke Mewburn.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd November 12, 1999
+.Os
+.Dt EDITLINE 3
+.Sh NAME
+.Nm editline ,
+.Nm el_init ,
+.Nm el_end ,
+.Nm el_reset ,
+.Nm el_gets ,
+.Nm el_getc ,
+.Nm el_push ,
+.Nm el_parse ,
+.Nm el_set ,
+.Nm el_source ,
+.Nm el_resize ,
+.Nm el_line ,
+.Nm el_insertstr ,
+.Nm el_deletestr ,
+.Nm history_init ,
+.Nm history_end ,
+.Nm history
+.Nd line editor and history functions
+.Sh LIBRARY
+.Lb libedit
+.Sh SYNOPSIS
+.Fd #include <histedit.h>
+.Ft EditLine *
+.Fn el_init "const char *prog" "FILE *fin" "FILE *fout" "FILE *ferr"
+.Ft void
+.Fn el_end "EditLine *e"
+.Ft void
+.Fn el_reset "EditLine *e"
+.Ft const char *
+.Fn el_gets "EditLine *e" "int *count"
+.Ft int
+.Fn el_getc "EditLine *e" "char *ch"
+.Ft void
+.Fn el_push "EditLine *e" "const char *str"
+.Ft int
+.Fn el_parse "EditLine *e" "int argc" "char *argv[]"
+.Ft int
+.Fn el_set "EditLine *e" "int op" "..."
+.Ft int
+.Fn el_get "EditLine *e" "int op" "void *result"
+.Ft int
+.Fn el_source "EditLine *e" "const char *file"
+.Ft void
+.Fn el_resize "EditLine *e"
+.Ft const LineInfo *
+.Fn el_line "EditLine *e"
+.Ft int
+.Fn el_insertstr "EditLine *e" "const char *str"
+.Ft void
+.Fn el_deletestr "EditLine *e" "int count"
+.Ft History *
+.Fn history_init
+.Ft void
+.Fn history_end "History *h"
+.Ft int
+.Fn history "History *h" "HistEvent *ev" "int op" "..."
+.Sh DESCRIPTION
+The
+.Nm
+library provides generic line editing and history functions,
+similar to those found in
+.Xr sh 1 .
+.Pp
+These functions are available in the
+.Nm libedit
+library (which needs the
+.Nm libtermcap
+library).
+Programs should be linked with
+.Fl ledit ltermcap .
+.Sh LINE EDITING FUNCTIONS
+The line editing functions use a common data structure,
+.Fa EditLine ,
+which is created by
+.Fn el_init
+and freed by
+.Fn el_end .
+.Pp
+The following functions are available:
+.Bl -tag -width 4n
+.It Fn el_init
+Initialise the line editor, and return a data structure
+to be used by all other line editing functions.
+.Fa prog
+is the name of the invoking program, used when reading the
+.Xr editrc 5
+file to determine which settings to use.
+.Fa fin ,
+.Fa fout
+and
+.Fa ferr
+are the input, output, and error streams (respectively) to use.
+In this documentation, references to
+.Dq the tty
+are actually to this input/output stream combination.
+.It Fn el_end
+Clean up and finish with
+.Fa e ,
+assumed to have been created with
+.Fn el_init .
+.It Fn el_reset
+Reset the tty and the parser.
+This should be called after an error which may have upset the tty's
+state.
+.It Fn el_gets
+Read a line from the tty.
+.Fa count
+is modified to contain the number of characters read.
+Returns the line read if successful, or
+.Dv NULL
+if no characters were read or if an error occurred.
+.It Fn el_getc
+Read a character from the tty.
+.Fa ch
+is modified to contain the character read.
+Returns the number of characters read if successful, -1 otherwise.
+.It Fn el_push
+Pushes
+.Fa str
+back onto the input stream.
+This is used by the macro expansion mechanism.
+Refer to the description of
+.Ic bind
+.Fl s
+in
+.Xr editrc 5
+for more information.
+.It Fn el_parse
+Parses the
+.Fa argv
+array (which is
+.Fa argc
+elements in size)
+to execute builtin
+.Nm
+commands.
+If the command is prefixed with
+.Dq prog :
+then
+.Fn el_parse
+will only execute the command if
+.Dq prog
+matches the
+.Fa prog
+argument supplied to
+.Fn el_init .
+The return value is
+-1 if the command is unknown,
+0 if there was no error or
+.Dq prog
+didn't match, or
+1 if the command returned an error.
+Refer to
+.Xr editrc 5
+for more information.
+.It Fn el_set
+Set
+.Nm
+parameters.
+.Fa op
+determines which parameter to set, and each operation has its
+own parameter list.
+.Pp
+The following values for
+.Fa op
+are supported, along with the required argument list:
+.Bl -tag -width 4n
+.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)"
+Define prompt printing function as
+.Fa f ,
+which is to return a string that contains the prompt.
+.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)"
+Define right side prompt printing function as
+.Fa f ,
+which is to return a string that contains the prompt.
+.It Dv EL_TERMINAL , Fa "const char *type"
+Define terminal type of the tty to be
+.Fa type ,
+or to
+.Ev TERM
+if
+.Fa type
+is
+.Dv NULL .
+.It Dv EL_EDITOR , Fa "const char *mode"
+Set editing mode to
+.Fa mode ,
+which must be one of
+.Dq emacs
+or
+.Dq vi .
+.It Dv EL_SIGNAL , Fa "int flag"
+If
+.Fa flag
+is non-zero,
+.Nm
+will install its own signal handler for the following signals when
+reading command input:
+.Dv SIGCONT ,
+.Dv SIGHUP ,
+.Dv SIGINT ,
+.Dv SIGQUIT ,
+.Dv SIGSTOP ,
+.Dv SIGTERM ,
+.Dv SIGTSTP ,
+and
+.Dv SIGWINCH .
+Otherwise, the current signal handlers will be used.
+.It Dv EL_BIND , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic bind
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_ECHOTC , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic echotc
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_SETTC , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic settc
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_SETTY , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic setty
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_TELLTC , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic telltc
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_ADDFN , Xo
+.Fa "const char *name" ,
+.Fa "const char *help" ,
+.Fa "unsigned char (*func)(EditLine *e, int ch)
+.Xc
+Add a user defined function,
+.Fn func ,
+referred to as
+.Fa name
+which is invoked when a key which is bound to
+.Fa name
+is entered.
+.Fa help
+is a description of
+.Fa name .
+At invocation time,
+.Fa ch
+is the key which caused the invocation.
+The return value of
+.Fn func
+should be one of:
+.Bl -tag -width "CC_REDISPLAY"
+.It Dv CC_NORM
+Add a normal character.
+.It Dv CC_NEWLINE
+End of line was entered.
+.It Dv CC_EOF
+EOF was entered.
+.It Dv CC_ARGHACK
+Expecting further command input as arguments, do nothing visually.
+.It Dv CC_REFRESH
+Refresh display.
+.It Dv CC_REFRESH_BEEP
+Refresh display, and beep.
+.It Dv CC_CURSOR
+Cursor moved, so update and perform
+.Dv CC_REFRESH .
+.It Dv CC_REDISPLAY
+Redisplay entire input line.
+This is useful if a key binding outputs extra information.
+.It Dv CC_ERROR
+An error occurred.
+Beep, and flush tty.
+.It Dv CC_FATAL
+Fatal error, reset tty to known state.
+.El
+.It Dv EL_HIST , Xo
+.Fa "History *(*func)(History *, int op, ...)" ,
+.Fa "const char *ptr"
+.Xc
+Defines which history function to use, which is usually
+.Fn history .
+.Fa ptr
+should be the value returned by
+.Fn history_init .
+.It Dv EL_EDITMODE , Fa "int flag"
+If
+.Fa flag
+is non-zero,
+editing is enabled (the default).
+Note that this is only an indication, and does not
+affect the operation of
+.Nm "" .
+At this time, it is the caller's responsibility to
+check this
+(using
+.Fn el_get )
+to determine if editing should be enabled or not.
+.It Dv EL_GETCFN , Fa "int (*f)(EditLine *, char *c)"
+Define the character reading function as
+.Fa f ,
+which is to return the number of characters read and store them in
+.Fa c .
+This function is called internally by
+.Fn el_gets
+and
+.Fn el_getc .
+The builtin function can be set or restored with the special function
+name ``EL_BUILTIN_GETCFN''.
+.It Dv EL_CLIENTDATA , Fa "void *data"
+Register
+.Fa data
+to be associated with this EditLine structure. It can be retrieved with
+the corresponding
+.Fn el_get
+call.
+.El
+.It Fn el_get
+Get
+.Nm
+parameters.
+.Fa op
+determines which parameter to retrieve into
+.Fa result .
+.Pp
+The following values for
+.Fa op
+are supported, along with actual type of
+.Fa result :
+.Bl -tag -width 4n
+.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)"
+Return a pointer to the function that displays the prompt.
+.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)"
+Return a pointer to the function that displays the rightside prompt.
+.It Dv EL_EDITOR , Fa "const char *"
+Return the name of the editor, which will be one of
+.Dq emacs
+or
+.Dq vi .
+.It Dv EL_SIGNAL , Fa "int *"
+Return non-zero if
+.Nm
+has installed private signal handlers (see
+.Fn el_get
+above).
+.It Dv EL_EDITMODE, Fa "int *"
+Return non-zero if editing is enabled.
+.It Dv EL_GETCFN, Fa "int (**f)(EditLine *, char *)"
+Return a pointer to the function that read characters, which is equal to
+``EL_BUILTIN_GETCFN'' in the case of the default builtin function.
+.It Dv EL_CLIENTDATA , Fa "void **data"
+Retrieve
+.Fa data
+previously registered with the corresponding
+.Fn el_set
+call.
+.El
+.It Fn el_source
+Initialise
+.Nm
+by reading the contents of
+.Fa file .
+.Fn el_parse
+is called for each line in
+.Fa file .
+If
+.Fa file
+is
+.Dv NULL ,
+try
+.Pa $PWD/.editrc
+then
+.Pa $HOME/.editrc .
+Refer to
+.Xr editrc 5
+for details on the format of
+.Fa file .
+.It Fn el_resize
+Must be called if the terminal size changes.
+If
+.Dv EL_SIGNAL
+has been set with
+.Fn el_set ,
+then this is done automatically.
+Otherwise, it's the responsibility of the application to call
+.Fn el_resize
+on the appropriate occasions.
+.It Fn el_line
+Return the editing information for the current line in a
+.Fa LineInfo
+structure, which is defined as follows:
+.Bd -literal
+typedef struct lineinfo {
+ const char *buffer; /* address of buffer */
+ const char *cursor; /* address of cursor */
+ const char *lastchar; /* address of last character */
+} LineInfo;
+.Ed
+.It Fn el_insertstr
+Insert
+.Fa str
+into the line at the cursor.
+Returns -1 if
+.Fa str
+is empty or won't fit, and 0 otherwise.
+.It Fn el_deletestr
+Delete
+.Fa num
+characters before the cursor.
+.El
+.Sh HISTORY LIST FUNCTIONS
+The history functions use a common data structure,
+.Fa History ,
+which is created by
+.Fn history_init
+and freed by
+.Fn history_end .
+.Pp
+The following functions are available:
+.Bl -tag -width 4n
+.It Fn history_init
+Initialise the history list, and return a data structure
+to be used by all other history list functions.
+.It Fn history_end
+Clean up and finish with
+.Fa h ,
+assumed to have been created with
+.Fn history_init .
+.It Fn history
+Perform operation
+.Fa op
+on the history list, with optional arguments as needed by the
+operation.
+.Fa ev
+is changed accordingly to operation.
+The following values for
+.Fa op
+are supported, along with the required argument list:
+.Bl -tag -width 4n
+.It Dv H_SETSIZE , Fa "int size"
+Set size of history to
+.Fa size
+elements.
+.It Dv H_GETSIZE
+Get number of events currently in history.
+.It Dv H_END
+Cleans up and finishes with
+.Fa h ,
+assumed to be created with
+.Fn history_init .
+.It Dv H_CLEAR
+Clear the history.
+.It Dv H_FUNC , Xo
+.Fa "void *ptr" ,
+.Fa "history_gfun_t first" ,
+.Fa "history_gfun_t next" ,
+.Fa "history_gfun_t last" ,
+.Fa "history_gfun_t prev" ,
+.Fa "history_gfun_t curr" ,
+.Fa "history_sfun_t set" ,
+.Fa "history_vfun_t clear" ,
+.Fa "history_efun_t enter" ,
+.Fa "history_efun_t add"
+.Xc
+Define functions to perform various history operations.
+.Fa ptr
+is the argument given to a function when it's invoked.
+.It Dv H_FIRST
+Return the first element in the history.
+.It Dv H_LAST
+Return the last element in the history.
+.It Dv H_PREV
+Return the previous element in the history.
+.It Dv H_NEXT
+Return the next element in the history.
+.It Dv H_CURR
+Return the current element in the history.
+.It Dv H_SET
+Set the cursor to point to the requested element.
+.It Dv H_ADD , Fa "const char *str"
+Append
+.Fa str
+to the current element of the history, or create an element with
+.It Dv H_APPEND , Fa "const char *str"
+Append
+.Fa str
+to the last new element of the history.
+.It Dv H_ENTER , Fa "const char *str"
+Add
+.Fa str
+as a new element to the history, and, if necessary,
+removing the oldest entry to keep the list to the created size.
+.It Dv H_PREV_STR , Fa "const char *str"
+Return the closest previous event that starts with
+.Fa str .
+.It Dv H_NEXT_STR , Fa "const char *str"
+Return the closest next event that starts with
+.Fa str .
+.It Dv H_PREV_EVENT , Fa "int e"
+Return the previous event numbered
+.Fa e .
+.It Dv H_NEXT_EVENT , Fa "int e"
+Return the next event numbered
+.Fa e .
+.It Dv H_LOAD , Fa "const char *file"
+Load the history list stored in
+.Fa file .
+.It Dv H_SAVE , Fa "const char *file"
+Save the history list to
+.Fa file .
+.El
+.Pp
+.Fn history
+returns 0 if the operation
+.Fa op
+succeeds. Otherwise, -1 is returned and
+.Fa ev
+is updated to contain more details about the error.
+.El
+.\"XXX.Sh EXAMPLES
+.\"XXX: provide some examples
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr signal 3 ,
+.Xr termcap 3 ,
+.Xr editrc 5
+.Sh HISTORY
+The
+.Nm
+library first appeared in
+.Bx 4.4 .
+.Dv CC_REDISPLAY
+appeared in
+.Nx 1.3 .
+.Dv CC_REFRESH_BEEP ,
+.Dv EL_EDITMODE
+and the readline emulation appeared in
+.Nx 1.4 .
+.Dv EL_RPROMPT
+appeared in
+.Nx 1.5 .
+.Sh AUTHORS
+The
+.Nm
+library was written by Christos Zoulas.
+Luke Mewburn wrote this manual and implemented
+.Dv CC_REDISPLAY ,
+.Dv CC_REFRESH_BEEP ,
+.Dv EL_EDITMODE ,
+and
+.Dv EL_RPROMPT .
+Jaromir Dolecek implemented the readline emulation.
+.Sh BUGS
+The tokenization functions are not publicly defined in
+.Fd <histedit.h> .
+.Pp
+At this time, it is the responsibility of the caller to
+check the result of the
+.Dv EL_EDITMODE
+operation of
+.Fn el_get
+(after an
+.Fn el_source
+or
+.Fn el_parse )
+to determine if
+.Nm
+should be used for further input.
+I.e.,
+.Dv EL_EDITMODE
+is purely an indication of the result of the most recent
+.Xr editrc 5
+.Ic edit
+command.
diff --git a/trunk/main/editline/editrc.5 b/trunk/main/editline/editrc.5
new file mode 100644
index 000000000..ddd12897b
--- /dev/null
+++ b/trunk/main/editline/editrc.5
@@ -0,0 +1,491 @@
+.\" $NetBSD: editrc.5,v 1.12 2002/01/15 02:46:44 wiz Exp $
+.\"
+.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This file was contributed to The NetBSD Foundation by Luke Mewburn.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd November 8, 2000
+.Os
+.Dt EDITRC 5
+.Sh NAME
+.Nm editrc
+.Nd configuration file for editline library
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file defines various settings to be used by the
+.Xr editline 3
+library.
+.Pp
+The format of each line is:
+.Dl [prog:]command [arg [...]]
+.Pp
+.Ar command
+is one of the
+.Xr editline 3
+builtin commands.
+Refer to
+.Sx BUILTIN COMMANDS
+for more information.
+.Pp
+.Ar prog
+is the program name string that a program defines when it calls
+.Xr el_init 3
+to setup
+.Xr editline 3 ,
+which is usually
+.Va argv[0] .
+.Ar command
+will be executed for any program which matches
+.Ar prog .
+.Pp
+.Ar prog
+may also be a
+.Xr regex 3
+style
+regular expression, in which case
+.Ar command
+will be executed for any program that matches the regular expression.
+.Pp
+If
+.Ar prog
+is absent,
+.Ar command
+is executed for all programs.
+.Sh BUILTIN COMMANDS
+The
+.Nm editline
+library has some builtin commands, which affect the way
+that the line editing and history functions operate.
+These are based on similar named builtins present in the
+.Xr tcsh 1
+shell.
+.Pp
+The following builtin commands are available:
+.Bl -tag -width 4n
+.It Ic bind Xo
+.Op Fl a
+.Op Fl e
+.Op Fl k
+.Op Fl l
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Ar key Op Ar command
+.Xc
+Without options, list all bound keys, and the editor command to which
+each is bound.
+If
+.Ar key
+is supplied, show the bindings for
+.Ar key .
+If
+.Ar key command
+is supplied, bind
+.Ar command
+to
+.Ar key .
+Options include:
+.Bl -tag -width 4n
+.It Fl e
+Bind all keys to the standard GNU Emacs-like bindings.
+.It Fl v
+Bind all keys to the standard
+.Xr vi 1 -like
+bindings.
+.It Fl a
+List or change key bindings in the
+.Xr vi 1
+mode alternate (command mode) key map.
+.It Fl k
+.Ar key
+is interpreted as a symbolic arrow key name, which may be one of
+.Sq up ,
+.Sq down ,
+.Sq left
+or
+.Sq right .
+.It Fl l
+List all editor commands and a short description of each.
+.It Fl r
+Remove a key's binding.
+.It Fl s
+.Ar command
+is taken as a literal string and treated as terminal input when
+.Ar key
+is typed.
+Bound keys in
+.Ar command
+are themselves reinterpreted, and this continues for ten levels of
+interpretation.
+.El
+.Pp
+.Ar command
+may be one of the commands documented in
+.Sx "EDITOR COMMANDS"
+below, or another key.
+.Pp
+.Ar key
+and
+.Ar command
+can contain control characters of the form
+.Sm off
+.Sq No ^ Ar character
+.Sm on
+.Po
+e.g.
+.Sq ^A
+.Pc ,
+and the following backslashed escape sequences:
+.Pp
+.Bl -tag -compact -offset indent -width 4n
+.It Ic \ea
+Bell
+.It Ic \eb
+Backspace
+.It Ic \ee
+Escape
+.It Ic \ef
+Formfeed
+.It Ic \en
+Newline
+.It Ic \er
+Carriage return
+.It Ic \et
+Horizontal tab
+.It Ic \ev
+Vertical tab
+.Sm off
+.It Sy \e Ar nnn
+.Sm on
+The ASCII character corresponding to the octal number
+.Ar nnn .
+.El
+.Pp
+.Sq \e
+nullifies the special meaning of the following character,
+if it has any, notably
+.Sq \e
+and
+.Sq ^ .
+.It Ic echotc Xo
+.Op Fl sv
+.Ar arg
+.Ar ...
+.Xc
+Exercise terminal capabilities given in
+.Ar arg Ar ... .
+If
+.Ar arg
+is
+.Sq baud ,
+.Sq cols ,
+.Sq lines ,
+.Sq rows ,
+.Sq meta or
+.Sq tabs ,
+the value of that capability is printed, with
+.Dq yes
+or
+.Dq no
+indicating that the terminal does or does not have that capability.
+.Pp
+.Fl s
+returns an emptry string for non-existent capabilities, rather than
+causing an error.
+.Fl v
+causes messages to be verbose.
+.It Ic edit Op Li on | Li off
+Enable or disable the
+.Nm editline
+functionality in a program.
+.It Ic history
+List the history.
+.It Ic telltc
+List the values of all the terminal capabilities (see
+.Xr termcap 5 ) .
+.It Ic settc Ar cap Ar val
+Set the terminal capability
+.Ar cap
+to
+.Ar val ,
+as defined in
+.Xr termcap 5 .
+No sanity checking is done.
+.It Ic setty Xo
+.Op Fl a
+.Op Fl d
+.Op Fl q
+.Op Fl x
+.Op Ar +mode
+.Op Ar -mode
+.Op Ar mode
+.Xc
+Control which tty modes that
+.Nm
+won't allow the user to change.
+.Fl d ,
+.Fl q
+or
+.Fl x
+tells
+.Ic setty
+to act on the
+.Sq edit ,
+.Sq quote
+or
+.Sq execute
+set of tty modes respectively; defaulting to
+.Fl x .
+.Pp
+Without other arguments,
+.Ic setty
+lists the modes in the chosen set which are fixed on
+.Po
+.Sq +mode
+.Pc
+or off
+.Po
+.Sq -mode
+.Pc .
+.Fl a
+lists all tty modes in the chosen set regardless of the setting.
+With
+.Ar +mode ,
+.Ar -mode
+or
+.Ar mode ,
+fixes
+.Ar mode
+on or off or removes control of
+.Ar mode
+in the chosen set.
+.El
+.Sh EDITOR COMMANDS
+The following editor commands are available for use in key bindings:
+.\" Section automatically generated with makelist
+.Bl -tag -width 4n
+.It Ic vi-paste-next
+Vi paste previous deletion to the right of the cursor.
+.It Ic vi-paste-prev
+Vi paste previous deletion to the left of the cursor.
+.It Ic vi-prev-space-word
+Vi move to the previous space delimited word.
+.It Ic vi-prev-word
+Vi move to the previous word.
+.It Ic vi-next-space-word
+Vi move to the next space delimited word.
+.It Ic vi-next-word
+Vi move to the next word.
+.It Ic vi-change-case
+Vi change case of character under the cursor and advance one character.
+.It Ic vi-change-meta
+Vi change prefix command.
+.It Ic vi-insert-at-bol
+Vi enter insert mode at the beginning of line.
+.It Ic vi-replace-char
+Vi replace character under the cursor with the next character typed.
+.It Ic vi-replace-mode
+Vi enter replace mode.
+.It Ic vi-substitute-char
+Vi replace character under the cursor and enter insert mode.
+.It Ic vi-substitute-line
+Vi substitute entire line.
+.It Ic vi-change-to-eol
+Vi change to end of line.
+.It Ic vi-insert
+Vi enter insert mode.
+.It Ic vi-add
+Vi enter insert mode after the cursor.
+.It Ic vi-add-at-eol
+Vi enter insert mode at end of line.
+.It Ic vi-delete-meta
+Vi delete prefix command.
+.It Ic vi-end-word
+Vi move to the end of the current space delimited word.
+.It Ic vi-to-end-word
+Vi move to the end of the current word.
+.It Ic vi-undo
+Vi undo last change.
+.It Ic vi-command-mode
+Vi enter command mode (use alternative key bindings).
+.It Ic vi-zero
+Vi move to the beginning of line.
+.It Ic vi-delete-prev-char
+Vi move to previous character (backspace).
+.It Ic vi-list-or-eof
+Vi list choices for completion or indicate end of file if empty line.
+.It Ic vi-kill-line-prev
+Vi cut from beginning of line to cursor.
+.It Ic vi-search-prev
+Vi search history previous.
+.It Ic vi-search-next
+Vi search history next.
+.It Ic vi-repeat-search-next
+Vi repeat current search in the same search direction.
+.It Ic vi-repeat-search-prev
+Vi repeat current search in the opposite search direction.
+.It Ic vi-next-char
+Vi move to the character specified next.
+.It Ic vi-prev-char
+Vi move to the character specified previous.
+.It Ic vi-to-next-char
+Vi move up to the character specified next.
+.It Ic vi-to-prev-char
+Vi move up to the character specified previous.
+.It Ic vi-repeat-next-char
+Vi repeat current character search in the same search direction.
+.It Ic vi-repeat-prev-char
+Vi repeat current character search in the opposite search direction.
+.It Ic em-delete-or-list
+Delete character under cursor or list completions if at end of line.
+.It Ic em-delete-next-word
+Cut from cursor to end of current word.
+.It Ic em-yank
+Paste cut buffer at cursor position.
+.It Ic em-kill-line
+Cut the entire line and save in cut buffer.
+.It Ic em-kill-region
+Cut area between mark and cursor and save in cut buffer.
+.It Ic em-copy-region
+Copy area between mark and cursor to cut buffer.
+.It Ic em-gosmacs-traspose
+Exchange the two characters before the cursor.
+.It Ic em-next-word
+Move next to end of current word.
+.It Ic em-upper-case
+Uppercase the characters from cursor to end of current word.
+.It Ic em-capitol-case
+Capitalize the characters from cursor to end of current word.
+.It Ic em-lower-case
+Lowercase the characters from cursor to end of current word.
+.It Ic em-set-mark
+Set the mark at cursor.
+.It Ic em-exchange-mark
+Exchange the cursor and mark.
+.It Ic em-universal-argument
+Universal argument (argument times 4).
+.It Ic em-meta-next
+Add 8th bit to next character typed.
+.It Ic em-toggle-overwrite
+Switch from insert to overwrite mode or vice versa.
+.It Ic em-copy-prev-word
+Copy current word to cursor.
+.It Ic em-inc-search-next
+Emacs incremental next search.
+.It Ic em-inc-search-prev
+Emacs incremental reverse search.
+.It Ic ed-end-of-file
+Indicate end of file.
+.It Ic ed-insert
+Add character to the line.
+.It Ic ed-delete-prev-word
+Delete from beginning of current word to cursor.
+.It Ic ed-delete-next-char
+Delete character under cursor.
+.It Ic ed-kill-line
+Cut to the end of line.
+.It Ic ed-move-to-end
+Move cursor to the end of line.
+.It Ic ed-move-to-beg
+Move cursor to the beginning of line.
+.It Ic ed-transpose-chars
+Exchange the character to the left of the cursor with the one under it.
+.It Ic ed-next-char
+Move to the right one character.
+.It Ic ed-prev-word
+Move to the beginning of the current word.
+.It Ic ed-prev-char
+Move to the left one character.
+.It Ic ed-quoted-insert
+Add the next character typed verbatim.
+.It Ic ed-digit
+Adds to argument or enters a digit.
+.It Ic ed-argument-digit
+Digit that starts argument.
+.It Ic ed-unassigned
+Indicates unbound character.
+.It Ic ed-tty-sigint
+Tty interrupt character.
+.It Ic ed-tty-dsusp
+Tty delayed suspend character.
+.It Ic ed-tty-flush-output
+Tty flush output characters.
+.It Ic ed-tty-sigquit
+Tty quit character.
+.It Ic ed-tty-sigtstp
+Tty suspend character.
+.It Ic ed-tty-stop-output
+Tty disallow output characters.
+.It Ic ed-tty-start-output
+Tty allow output characters.
+.It Ic ed-newline
+Execute command.
+.It Ic ed-delete-prev-char
+Delete the character to the left of the cursor.
+.It Ic ed-clear-screen
+Clear screen leaving current line at the top.
+.It Ic ed-redisplay
+Redisplay everything.
+.It Ic ed-start-over
+Erase current line and start from scratch.
+.It Ic ed-sequence-lead-in
+First character in a bound sequence.
+.It Ic ed-prev-history
+Move to the previous history line.
+.It Ic ed-next-history
+Move to the next history line.
+.It Ic ed-search-prev-history
+Search previous in history for a line matching the current.
+.It Ic ed-search-next-history
+Search next in history for a line matching the current.
+.It Ic ed-prev-line
+Move up one line.
+.It Ic ed-next-line
+Move down one line.
+.It Ic ed-command
+Editline extended command.
+.El
+.\" End of section automatically generated with makelist
+.Sh SEE ALSO
+.Xr editline 3 ,
+.Xr regex 3 ,
+.Xr termcap 5
+.Sh AUTHORS
+The
+.Nm editline
+library was written by Christos Zoulas,
+and this manual was written by Luke Mewburn,
+with some sections inspired by
+.Xr tcsh 1 .
diff --git a/trunk/main/editline/el.c b/trunk/main/editline/el.c
new file mode 100644
index 000000000..514316fbe
--- /dev/null
+++ b/trunk/main/editline/el.c
@@ -0,0 +1,509 @@
+/* $NetBSD: el.c,v 1.29 2002/03/18 16:00:52 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94";
+#else
+__RCSID("$NetBSD: el.c,v 1.29 2002/03/18 16:00:52 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * el.c: EditLine interface functions
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "el.h"
+
+/* el_init():
+ * Initialize editline and set default parameters.
+ */
+public EditLine *
+el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
+{
+
+ EditLine *el = (EditLine *) el_malloc(sizeof(EditLine));
+
+ if (el == NULL)
+ return (NULL);
+
+ memset(el, 0, sizeof(EditLine));
+
+ el->el_infd = fileno(fin);
+ el->el_outfile = fout;
+ el->el_errfile = ferr;
+ el->el_prog = strdup(prog);
+
+ /*
+ * Initialize all the modules. Order is important!!!
+ */
+ el->el_flags = 0;
+
+ if (term_init(el) == -1) {
+ free(el->el_prog);
+ el_free(el);
+ return NULL;
+ }
+ (void) key_init(el);
+ (void) map_init(el);
+ if (tty_init(el) == -1)
+ el->el_flags |= NO_TTY;
+ (void) ch_init(el);
+ (void) search_init(el);
+ (void) hist_init(el);
+ (void) prompt_init(el);
+ (void) sig_init(el);
+ (void) read_init(el);
+
+ return (el);
+}
+
+
+/* el_end():
+ * Clean up.
+ */
+public void
+el_end(EditLine *el)
+{
+
+ if (el == NULL)
+ return;
+
+ el_reset(el);
+
+ term_end(el);
+ key_end(el);
+ map_end(el);
+ tty_end(el);
+ ch_end(el);
+ search_end(el);
+ hist_end(el);
+ prompt_end(el);
+ sig_end(el);
+
+ if (el->el_prog)
+ el_free((ptr_t) el->el_prog);
+ el_free((ptr_t) el);
+}
+
+
+/* el_reset():
+ * Reset the tty and the parser
+ */
+public void
+el_reset(EditLine *el)
+{
+
+ tty_cookedmode(el);
+ ch_reset(el); /* XXX: Do we want that? */
+}
+
+
+/* el_set():
+ * set the editline parameters
+ */
+public int
+el_set(EditLine *el, int op, ...)
+{
+ va_list va;
+ int rv = 0;
+
+ if (el == NULL)
+ return (-1);
+ va_start(va, op);
+
+ switch (op) {
+ case EL_PROMPT:
+ case EL_RPROMPT:
+ rv = prompt_set(el, va_arg(va, el_pfunc_t), op);
+ break;
+
+ case EL_TERMINAL:
+ rv = term_set(el, va_arg(va, char *));
+ break;
+
+ case EL_EDITOR:
+ rv = map_set_editor(el, va_arg(va, char *));
+ break;
+
+ case EL_SIGNAL:
+ if (va_arg(va, int))
+ el->el_flags |= HANDLE_SIGNALS;
+ else
+ el->el_flags &= ~HANDLE_SIGNALS;
+ break;
+
+ case EL_BIND:
+ case EL_TELLTC:
+ case EL_SETTC:
+ case EL_ECHOTC:
+ case EL_SETTY:
+ {
+ const char *argv[20];
+ int i;
+
+ for (i = 1; i < 20; i++)
+ if ((argv[i] = va_arg(va, char *)) == NULL)
+ break;
+
+ switch (op) {
+ case EL_BIND:
+ argv[0] = "bind";
+ rv = map_bind(el, i, argv);
+ break;
+
+ case EL_TELLTC:
+ argv[0] = "telltc";
+ rv = term_telltc(el, i, argv);
+ break;
+
+ case EL_SETTC:
+ argv[0] = "settc";
+ rv = term_settc(el, i, argv);
+ break;
+
+ case EL_ECHOTC:
+ argv[0] = "echotc";
+ rv = term_echotc(el, i, argv);
+ break;
+
+ case EL_SETTY:
+ argv[0] = "setty";
+ rv = tty_stty(el, i, argv);
+ break;
+
+ default:
+ rv = -1;
+ EL_ABORT((el->el_errfile, "Bad op %d\n", op));
+ break;
+ }
+ break;
+ }
+
+ case EL_ADDFN:
+ {
+ char *name = va_arg(va, char *);
+ char *help = va_arg(va, char *);
+ el_func_t func = va_arg(va, el_func_t);
+
+ rv = map_addfunc(el, name, help, func);
+ break;
+ }
+
+ case EL_HIST:
+ {
+ hist_fun_t func = va_arg(va, hist_fun_t);
+ ptr_t ptr = va_arg(va, char *);
+
+ rv = hist_set(el, func, ptr);
+ break;
+ }
+
+ case EL_EDITMODE:
+ if (va_arg(va, int))
+ el->el_flags &= ~EDIT_DISABLED;
+ else
+ el->el_flags |= EDIT_DISABLED;
+ rv = 0;
+ break;
+
+ case EL_GETCFN:
+ {
+ el_rfunc_t rc = va_arg(va, el_rfunc_t);
+ rv = el_read_setfn(el, rc);
+ break;
+ }
+
+ case EL_CLIENTDATA:
+ el->el_data = va_arg(va, void *);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ va_end(va);
+ return (rv);
+}
+
+
+/* el_get():
+ * retrieve the editline parameters
+ */
+public int
+el_get(EditLine *el, int op, void *ret)
+{
+ int rv;
+
+ if (el == NULL || ret == NULL)
+ return (-1);
+ switch (op) {
+ case EL_PROMPT:
+ case EL_RPROMPT:
+ rv = prompt_get(el, (el_pfunc_t *) & ret, op);
+ break;
+
+ case EL_EDITOR:
+ rv = map_get_editor(el, (const char **) &ret);
+ break;
+
+ case EL_SIGNAL:
+ *((int *) ret) = (el->el_flags & HANDLE_SIGNALS);
+ rv = 0;
+ break;
+
+ case EL_EDITMODE:
+ *((int *) ret) = (!(el->el_flags & EDIT_DISABLED));
+ rv = 0;
+ break;
+
+#if 0 /* XXX */
+ case EL_TERMINAL:
+ rv = term_get(el, (const char *) &ret);
+ break;
+
+ case EL_BIND:
+ case EL_TELLTC:
+ case EL_SETTC:
+ case EL_ECHOTC:
+ case EL_SETTY:
+ {
+ char *argv[20];
+ int i;
+
+ for (i = 1; i < 20; i++)
+ if ((argv[i] = va_arg(va, char *)) == NULL)
+ break;
+
+ switch (op) {
+ case EL_BIND:
+ argv[0] = "bind";
+ rv = map_bind(el, i, argv);
+ break;
+
+ case EL_TELLTC:
+ argv[0] = "telltc";
+ rv = term_telltc(el, i, argv);
+ break;
+
+ case EL_SETTC:
+ argv[0] = "settc";
+ rv = term_settc(el, i, argv);
+ break;
+
+ case EL_ECHOTC:
+ argv[0] = "echotc";
+ rv = term_echotc(el, i, argv);
+ break;
+
+ case EL_SETTY:
+ argv[0] = "setty";
+ rv = tty_stty(el, i, argv);
+ break;
+
+ default:
+ rv = -1;
+ EL_ABORT((el->errfile, "Bad op %d\n", op));
+ break;
+ }
+ break;
+ }
+
+ case EL_ADDFN:
+ {
+ char *name = va_arg(va, char *);
+ char *help = va_arg(va, char *);
+ el_func_t func = va_arg(va, el_func_t);
+
+ rv = map_addfunc(el, name, help, func);
+ break;
+ }
+
+ case EL_HIST:
+ {
+ hist_fun_t func = va_arg(va, hist_fun_t);
+ ptr_t ptr = va_arg(va, char *);
+ rv = hist_set(el, func, ptr);
+ }
+ break;
+#endif /* XXX */
+
+ case EL_GETCFN:
+ *((el_rfunc_t *)ret) = el_read_getfn(el);
+ rv = 0;
+ break;
+
+ case EL_CLIENTDATA:
+ *((void **)ret) = el->el_data;
+ rv = 0;
+ break;
+
+ default:
+ rv = -1;
+ }
+
+ return (rv);
+}
+
+
+/* el_line():
+ * Return editing info
+ */
+public const LineInfo *
+el_line(EditLine *el)
+{
+
+ return (const LineInfo *) (void *) &el->el_line;
+}
+
+
+/* el_source():
+ * Source a file
+ */
+public int
+el_source(EditLine *el, const char *fname)
+{
+ FILE *fp;
+ size_t len;
+ char *ptr;
+
+ fp = NULL;
+ if (fname == NULL) {
+#ifdef HAVE_ISSETUGID
+ static const char elpath[] = "/.editrc";
+ char path[MAXPATHLEN];
+
+ if (issetugid())
+ return (-1);
+ if ((ptr = getenv("HOME")) == NULL)
+ return (-1);
+ if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
+ return (-1);
+ if (strlcat(path, elpath, sizeof(path)) >= sizeof(path))
+ return (-1);
+ fname = path;
+#else
+ /*
+ * If issetugid() is missing, always return an error, in order
+ * to keep from inadvertently opening up the user to a security
+ * hole.
+ */
+ return (-1);
+#endif
+ }
+ if (fp == NULL)
+ fp = fopen(fname, "r");
+ if (fp == NULL)
+ return (-1);
+
+ while ((ptr = fgetln(fp, &len)) != NULL) {
+ if (len > 0 && ptr[len - 1] == '\n')
+ --len;
+ ptr[len] = '\0';
+ if (parse_line(el, ptr) == -1) {
+ (void) fclose(fp);
+ return (-1);
+ }
+ }
+
+ (void) fclose(fp);
+ return (0);
+}
+
+
+/* el_resize():
+ * Called from program when terminal is resized
+ */
+public void
+el_resize(EditLine *el)
+{
+ int lins, cols;
+ sigset_t oset, nset;
+
+ (void) sigemptyset(&nset);
+ (void) sigaddset(&nset, SIGWINCH);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ /* get the correct window size */
+ if (term_get_size(el, &lins, &cols))
+ term_change_size(el, lins, cols);
+
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+
+
+/* el_beep():
+ * Called from the program to beep
+ */
+public void
+el_beep(EditLine *el)
+{
+
+ term_beep(el);
+}
+
+
+/* el_editmode()
+ * Set the state of EDIT_DISABLED from the `edit' command.
+ */
+protected int
+/*ARGSUSED*/
+el_editmode(EditLine *el, int argc, const char **argv)
+{
+ const char *how;
+
+ if (argv == NULL || argc != 2 || argv[1] == NULL)
+ return (-1);
+
+ how = argv[1];
+ if (strcmp(how, "on") == 0)
+ el->el_flags &= ~EDIT_DISABLED;
+ else if (strcmp(how, "off") == 0)
+ el->el_flags |= EDIT_DISABLED;
+ else {
+ (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how);
+ return (-1);
+ }
+ return (0);
+}
diff --git a/trunk/main/editline/el.h b/trunk/main/editline/el.h
new file mode 100644
index 000000000..641081e87
--- /dev/null
+++ b/trunk/main/editline/el.h
@@ -0,0 +1,145 @@
+/* $NetBSD: el.h,v 1.11 2002/03/18 16:00:52 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)el.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.h: Internal structures.
+ */
+#ifndef _h_el
+#define _h_el
+/*
+ * Local defaults
+ */
+#define KSHVI
+#define VIDEFAULT
+#define ANCHOR
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#define EL_BUFSIZ 1024 /* Maximum line size */
+
+#define HANDLE_SIGNALS 1<<0
+#define NO_TTY 1<<1
+#define EDIT_DISABLED 1<<2
+
+typedef int bool_t; /* True or not */
+
+typedef unsigned char el_action_t; /* Index to command array */
+
+typedef struct coord_t { /* Position on the screen */
+ int h;
+ int v;
+} coord_t;
+
+typedef struct el_line_t {
+ char *buffer; /* Input line */
+ char *cursor; /* Cursor position */
+ char *lastchar; /* Last character */
+ const char *limit; /* Max position */
+} el_line_t;
+
+/*
+ * Editor state
+ */
+typedef struct el_state_t {
+ int inputmode; /* What mode are we in? */
+ int doingarg; /* Are we getting an argument? */
+ int argument; /* Numeric argument */
+ int metanext; /* Is the next char a meta char */
+ el_action_t lastcmd; /* Previous command */
+} el_state_t;
+
+/*
+ * Until we come up with something better...
+ */
+#define el_malloc(a) malloc(a)
+#define el_realloc(a,b) realloc(a, b)
+#define el_free(a) free(a)
+
+#include "tty.h"
+#include "prompt.h"
+#include "key.h"
+#include "term.h"
+#include "refresh.h"
+#include "chared.h"
+#include "common.h"
+#include "search.h"
+#include "hist.h"
+#include "map.h"
+#include "parse.h"
+#include "sig.h"
+#include "help.h"
+#include "read.h"
+
+struct editline {
+ char *el_prog; /* the program name */
+ FILE *el_outfile; /* Stdio stuff */
+ FILE *el_errfile; /* Stdio stuff */
+ int el_infd; /* Input file descriptor */
+ int el_flags; /* Various flags. */
+ coord_t el_cursor; /* Cursor location */
+ char **el_display; /* Real screen image = what is there */
+ char **el_vdisplay; /* Virtual screen image = what we see */
+ void *el_data; /* Client data */
+ el_line_t el_line; /* The current line information */
+ el_state_t el_state; /* Current editor state */
+ el_term_t el_term; /* Terminal dependent stuff */
+ el_tty_t el_tty; /* Tty dependent stuff */
+ el_refresh_t el_refresh; /* Refresh stuff */
+ el_prompt_t el_prompt; /* Prompt stuff */
+ el_prompt_t el_rprompt; /* Prompt stuff */
+ el_chared_t el_chared; /* Characted editor stuff */
+ el_map_t el_map; /* Key mapping stuff */
+ el_key_t el_key; /* Key binding stuff */
+ el_history_t el_history; /* History stuff */
+ el_search_t el_search; /* Search stuff */
+ el_signal_t el_signal; /* Signal handling stuff */
+ el_read_t el_read; /* Character reading stuff */
+};
+
+protected int el_editmode(EditLine *, int, const char **);
+
+#ifdef DEBUG
+#define EL_ABORT(a) (void) (fprintf(el->el_errfile, "%s, %d: ", \
+ __FILE__, __LINE__), fprintf a, abort())
+#else
+#define EL_ABORT(a) abort()
+#endif
+#endif /* _h_el */
diff --git a/trunk/main/editline/emacs.c b/trunk/main/editline/emacs.c
new file mode 100644
index 000000000..f520d024b
--- /dev/null
+++ b/trunk/main/editline/emacs.c
@@ -0,0 +1,488 @@
+/* $NetBSD: emacs.c,v 1.10 2002/03/18 16:00:52 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: emacs.c,v 1.10 2002/03/18 16:00:52 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * emacs.c: Emacs functions
+ */
+#include "el.h"
+
+/* em_delete_or_list():
+ * Delete character under cursor or list completions if at end of line
+ * [^D]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_delete_or_list(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar) {
+ /* if I'm at the end */
+ if (el->el_line.cursor == el->el_line.buffer) {
+ /* and the beginning */
+ term_overwrite(el, STReof, 4); /* then do a EOF */
+ term__flush();
+ return (CC_EOF);
+ } else {
+ /*
+ * Here we could list completions, but it is an
+ * error right now
+ */
+ term_beep(el);
+ return (CC_ERROR);
+ }
+ } else {
+ c_delafter(el, el->el_state.argument); /* delete after dot */
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ /* bounds check */
+ return (CC_REFRESH);
+ }
+}
+
+
+/* em_delete_next_word():
+ * Cut from cursor to end of current word
+ * [M-d]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_delete_next_word(EditLine *el, int c)
+{
+ char *cp, *p, *kp;
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
+ /* save the text */
+ *kp++ = *p;
+ el->el_chared.c_kill.last = kp;
+
+ c_delafter(el, cp - el->el_line.cursor); /* delete after dot */
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ /* bounds check */
+ return (CC_REFRESH);
+}
+
+
+/* em_yank():
+ * Paste cut buffer at cursor position
+ * [^Y]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_yank(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) {
+ if (!ch_enlargebufs(el, 1))
+ return (CC_ERROR);
+ }
+
+ if (el->el_line.lastchar +
+ (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
+ el->el_line.limit)
+ return (CC_ERROR);
+
+ el->el_chared.c_kill.mark = el->el_line.cursor;
+ cp = el->el_line.cursor;
+
+ /* open the space, */
+ c_insert(el, el->el_chared.c_kill.last - el->el_chared.c_kill.buf);
+ /* copy the chars */
+ for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++)
+ *cp++ = *kp;
+
+ /* if an arg, cursor at beginning else cursor at end */
+ if (el->el_state.argument == 1)
+ el->el_line.cursor = cp;
+
+ return (CC_REFRESH);
+}
+
+
+/* em_kill_line():
+ * Cut the entire line and save in cut buffer
+ * [^U]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_kill_line(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ cp = el->el_line.buffer;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.lastchar)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ /* zap! -- delete all of it */
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ return (CC_REFRESH);
+}
+
+
+/* em_kill_region():
+ * Cut area between mark and cursor and save in cut buffer
+ * [^W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_kill_region(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ if (!el->el_chared.c_kill.mark)
+ return (CC_ERROR);
+
+ if (el->el_chared.c_kill.mark > el->el_line.cursor) {
+ cp = el->el_line.cursor;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_chared.c_kill.mark)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ c_delafter(el, cp - el->el_line.cursor);
+ } else { /* mark is before cursor */
+ cp = el->el_chared.c_kill.mark;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.cursor)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ c_delbefore(el, cp - el->el_chared.c_kill.mark);
+ el->el_line.cursor = el->el_chared.c_kill.mark;
+ }
+ return (CC_REFRESH);
+}
+
+
+/* em_copy_region():
+ * Copy area between mark and cursor to cut buffer
+ * [M-W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_copy_region(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ if (el->el_chared.c_kill.mark)
+ return (CC_ERROR);
+
+ if (el->el_chared.c_kill.mark > el->el_line.cursor) {
+ cp = el->el_line.cursor;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_chared.c_kill.mark)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ } else {
+ cp = el->el_chared.c_kill.mark;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.cursor)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ }
+ return (CC_NORM);
+}
+
+
+/* em_gosmacs_traspose():
+ * Exchange the two characters before the cursor
+ * Gosling emacs transpose chars [^T]
+ */
+protected el_action_t
+em_gosmacs_traspose(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor > &el->el_line.buffer[1]) {
+ /* must have at least two chars entered */
+ c = el->el_line.cursor[-2];
+ el->el_line.cursor[-2] = el->el_line.cursor[-1];
+ el->el_line.cursor[-1] = c;
+ return (CC_REFRESH);
+ } else
+ return (CC_ERROR);
+}
+
+
+/* em_next_word():
+ * Move next to end of current word
+ * [M-f]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_next_word(EditLine *el, int c)
+{
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = c__next_word(el->el_line.cursor,
+ el->el_line.lastchar,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* em_upper_case():
+ * Uppercase the characters from cursor to end of current word
+ * [M-u]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_upper_case(EditLine *el, int c)
+{
+ char *cp, *ep;
+
+ ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (cp = el->el_line.cursor; cp < ep; cp++)
+ if (islower((unsigned char) *cp))
+ *cp = toupper(*cp);
+
+ el->el_line.cursor = ep;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ return (CC_REFRESH);
+}
+
+
+/* em_capitol_case():
+ * Capitalize the characters from cursor to end of current word
+ * [M-c]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_capitol_case(EditLine *el, int c)
+{
+ char *cp, *ep;
+
+ ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (cp = el->el_line.cursor; cp < ep; cp++) {
+ if (isalpha((unsigned char) *cp)) {
+ if (islower((unsigned char) *cp))
+ *cp = toupper(*cp);
+ cp++;
+ break;
+ }
+ }
+ for (; cp < ep; cp++)
+ if (isupper((unsigned char) *cp))
+ *cp = tolower(*cp);
+
+ el->el_line.cursor = ep;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ return (CC_REFRESH);
+}
+
+
+/* em_lower_case():
+ * Lowercase the characters from cursor to end of current word
+ * [M-l]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_lower_case(EditLine *el, int c)
+{
+ char *cp, *ep;
+
+ ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (cp = el->el_line.cursor; cp < ep; cp++)
+ if (isupper((unsigned char) *cp))
+ *cp = tolower(*cp);
+
+ el->el_line.cursor = ep;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ return (CC_REFRESH);
+}
+
+
+/* em_set_mark():
+ * Set the mark at cursor
+ * [^@]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_set_mark(EditLine *el, int c)
+{
+
+ el->el_chared.c_kill.mark = el->el_line.cursor;
+ return (CC_NORM);
+}
+
+
+/* em_exchange_mark():
+ * Exchange the cursor and mark
+ * [^X^X]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_exchange_mark(EditLine *el, int c)
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ el->el_line.cursor = el->el_chared.c_kill.mark;
+ el->el_chared.c_kill.mark = cp;
+ return (CC_CURSOR);
+}
+
+
+/* em_universal_argument():
+ * Universal argument (argument times 4)
+ * [^U]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_universal_argument(EditLine *el, int c)
+{ /* multiply current argument by 4 */
+
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.doingarg = 1;
+ el->el_state.argument *= 4;
+ return (CC_ARGHACK);
+}
+
+
+/* em_meta_next():
+ * Add 8th bit to next character typed
+ * [<ESC>]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_meta_next(EditLine *el, int c)
+{
+
+ el->el_state.metanext = 1;
+ return (CC_ARGHACK);
+}
+
+
+/* em_toggle_overwrite():
+ * Switch from insert to overwrite mode or vice versa
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_toggle_overwrite(EditLine *el, int c)
+{
+
+ el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
+ MODE_REPLACE : MODE_INSERT;
+ return (CC_NORM);
+}
+
+
+/* em_copy_prev_word():
+ * Copy current word to cursor
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_copy_prev_word(EditLine *el, int c)
+{
+ char *cp, *oldc, *dp;
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ oldc = el->el_line.cursor;
+ /* does a bounds check */
+ cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
+ el->el_state.argument, ce__isword);
+
+ c_insert(el, oldc - cp);
+ for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++)
+ *dp++ = *cp;
+
+ el->el_line.cursor = dp;/* put cursor at end */
+
+ return (CC_REFRESH);
+}
+
+
+/* em_inc_search_next():
+ * Emacs incremental next search
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_inc_search_next(EditLine *el, int c)
+{
+
+ el->el_search.patlen = 0;
+ return (ce_inc_search(el, ED_SEARCH_NEXT_HISTORY));
+}
+
+
+/* em_inc_search_prev():
+ * Emacs incremental reverse search
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_inc_search_prev(EditLine *el, int c)
+{
+
+ el->el_search.patlen = 0;
+ return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY));
+}
diff --git a/trunk/main/editline/hist.c b/trunk/main/editline/hist.c
new file mode 100644
index 000000000..11f39ae10
--- /dev/null
+++ b/trunk/main/editline/hist.c
@@ -0,0 +1,197 @@
+/* $NetBSD: hist.c,v 1.10 2002/03/18 16:00:53 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)hist.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: hist.c,v 1.10 2002/03/18 16:00:53 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * hist.c: History access functions
+ */
+#include <stdlib.h>
+#include "el.h"
+
+/* hist_init():
+ * Initialization function.
+ */
+protected int
+hist_init(EditLine *el)
+{
+
+ el->el_history.fun = NULL;
+ el->el_history.ref = NULL;
+ el->el_history.buf = (char *) el_malloc(EL_BUFSIZ);
+ el->el_history.sz = EL_BUFSIZ;
+ if (el->el_history.buf == NULL)
+ return (-1);
+ el->el_history.last = el->el_history.buf;
+ return (0);
+}
+
+
+/* hist_end():
+ * clean up history;
+ */
+protected void
+hist_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_history.buf);
+ el->el_history.buf = NULL;
+}
+
+
+/* hist_set():
+ * Set new history interface
+ */
+protected int
+hist_set(EditLine *el, hist_fun_t fun, ptr_t ptr)
+{
+
+ el->el_history.ref = ptr;
+ el->el_history.fun = fun;
+ return (0);
+}
+
+
+/* hist_get():
+ * Get a history line and update it in the buffer.
+ * eventno tells us the event to get.
+ */
+protected el_action_t
+hist_get(EditLine *el)
+{
+ const char *hp;
+ int h;
+
+ if (el->el_history.eventno == 0) { /* if really the current line */
+ (void) strncpy(el->el_line.buffer, el->el_history.buf,
+ el->el_history.sz - 1);
+ el->el_line.lastchar = el->el_line.buffer +
+ (el->el_history.last - el->el_history.buf);
+
+#ifdef KSHVI
+ if (el->el_map.type == MAP_VI)
+ el->el_line.cursor = el->el_line.buffer;
+ else
+#endif /* KSHVI */
+ el->el_line.cursor = el->el_line.lastchar;
+
+ return (CC_REFRESH);
+ }
+ if (el->el_history.ref == NULL)
+ return (CC_ERROR);
+
+ hp = HIST_FIRST(el);
+
+ if (hp == NULL)
+ return (CC_ERROR);
+
+ for (h = 1; h < el->el_history.eventno; h++)
+ if ((hp = HIST_NEXT(el)) == NULL) {
+ el->el_history.eventno = h;
+ return (CC_ERROR);
+ }
+ (void) strncpy(el->el_line.buffer, hp,
+ (size_t)(el->el_line.limit - el->el_line.buffer));
+ el->el_line.lastchar = el->el_line.buffer + strlen(el->el_line.buffer);
+
+ if (el->el_line.lastchar > el->el_line.buffer) {
+ if (el->el_line.lastchar[-1] == '\n')
+ el->el_line.lastchar--;
+ if ((el->el_line.lastchar > el->el_line.buffer)&&(el->el_line.lastchar[-1] == ' ')) /* bill heckel */
+ el->el_line.lastchar--;
+ if (el->el_line.lastchar < el->el_line.buffer)
+ el->el_line.lastchar = el->el_line.buffer;
+ }
+#ifdef KSHVI
+ if (el->el_map.type == MAP_VI)
+ el->el_line.cursor = el->el_line.buffer;
+ else
+#endif /* KSHVI */
+ el->el_line.cursor = el->el_line.lastchar;
+
+ return (CC_REFRESH);
+}
+
+
+/* hist_list()
+ * List history entries
+ */
+protected int
+/*ARGSUSED*/
+hist_list(EditLine *el, int argc, const char **argv)
+{
+ const char *str;
+
+ if (el->el_history.ref == NULL)
+ return (-1);
+ for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el))
+ (void) fprintf(el->el_outfile, "%d %s",
+ el->el_history.ev.num, str);
+ return (0);
+}
+
+/* hist_enlargebuf()
+ * Enlarge history buffer to specified value. Called from el_enlargebufs().
+ * Return 0 for failure, 1 for success.
+ */
+protected int
+/*ARGSUSED*/
+hist_enlargebuf(EditLine *el, size_t oldsz, size_t newsz)
+{
+ char *newbuf;
+
+ newbuf = realloc(el->el_history.buf, newsz);
+ if (!newbuf)
+ return 0;
+
+ (void) memset(&newbuf[oldsz], '\0', newsz - oldsz);
+
+ el->el_history.last = newbuf +
+ (el->el_history.last - el->el_history.buf);
+ el->el_history.buf = newbuf;
+ el->el_history.sz = newsz;
+
+ return 1;
+}
diff --git a/trunk/main/editline/hist.h b/trunk/main/editline/hist.h
new file mode 100644
index 000000000..5fdccd08e
--- /dev/null
+++ b/trunk/main/editline/hist.h
@@ -0,0 +1,80 @@
+/* $NetBSD: hist.h,v 1.7 2002/03/18 16:00:53 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hist.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.hist.c: History functions
+ */
+#ifndef _h_el_hist
+#define _h_el_hist
+
+#include "histedit.h"
+
+typedef int (*hist_fun_t)(ptr_t, HistEvent *, int, ...);
+
+typedef struct el_history_t {
+ char *buf; /* The history buffer */
+ size_t sz; /* Size of history buffer */
+ char *last; /* The last character */
+ int eventno; /* Event we are looking for */
+ ptr_t ref; /* Argument for history fcns */
+ hist_fun_t fun; /* Event access */
+ HistEvent ev; /* Event cookie */
+} el_history_t;
+
+#define HIST_FUN(el, fn, arg) \
+ ((((*(el)->el_history.fun) ((el)->el_history.ref, &(el)->el_history.ev, \
+ fn, arg)) == -1) ? NULL : (el)->el_history.ev.str)
+
+#define HIST_NEXT(el) HIST_FUN(el, H_NEXT, NULL)
+#define HIST_FIRST(el) HIST_FUN(el, H_FIRST, NULL)
+#define HIST_LAST(el) HIST_FUN(el, H_LAST, NULL)
+#define HIST_PREV(el) HIST_FUN(el, H_PREV, NULL)
+#define HIST_EVENT(el, num) HIST_FUN(el, H_EVENT, num)
+#define HIST_LOAD(el, fname) HIST_FUN(el, H_LOAD fname)
+#define HIST_SAVE(el, fname) HIST_FUN(el, H_SAVE fname)
+
+protected int hist_init(EditLine *);
+protected void hist_end(EditLine *);
+protected el_action_t hist_get(EditLine *);
+protected int hist_set(EditLine *, hist_fun_t, ptr_t);
+protected int hist_list(EditLine *, int, const char **);
+protected int hist_enlargebuf(EditLine *, size_t, size_t);
+
+#endif /* _h_el_hist */
diff --git a/trunk/main/editline/histedit.h b/trunk/main/editline/histedit.h
new file mode 100644
index 000000000..e387e3b81
--- /dev/null
+++ b/trunk/main/editline/histedit.h
@@ -0,0 +1,197 @@
+/* $NetBSD: histedit.h,v 1.19 2002/03/18 16:00:54 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)histedit.h 8.2 (Berkeley) 1/3/94
+ */
+
+/*
+ * histedit.h: Line editor and history interface.
+ */
+#ifndef _HISTEDIT_H_
+#define _HISTEDIT_H_
+
+#define LIBEDIT_MAJOR 2
+#define LIBEDIT_MINOR 6
+
+#include <sys/types.h>
+#include <stdio.h>
+
+/*
+ * ==== Editing ====
+ */
+typedef struct editline EditLine;
+
+/*
+ * For user-defined function interface
+ */
+typedef struct lineinfo {
+ char *buffer;
+ char *cursor;
+ char *lastchar;
+} LineInfo;
+
+
+/*
+ * EditLine editor function return codes.
+ * For user-defined function interface
+ */
+#define CC_NORM 0
+#define CC_NEWLINE 1
+#define CC_EOF 2
+#define CC_ARGHACK 3
+#define CC_REFRESH 4
+#define CC_CURSOR 5
+#define CC_ERROR 6
+#define CC_FATAL 7
+#define CC_REDISPLAY 8
+#define CC_REFRESH_BEEP 9
+
+/*
+ * Initialization, cleanup, and resetting
+ */
+EditLine *el_init(const char *, FILE *, FILE *, FILE *);
+void el_reset(EditLine *);
+void el_end(EditLine *);
+
+
+/*
+ * Get a line, a character or push a string back in the input queue
+ */
+const char *el_gets(EditLine *, int *);
+int el_getc(EditLine *, char *);
+void el_push(EditLine *, char *);
+
+/*
+ * Beep!
+ */
+void el_beep(EditLine *);
+
+/*
+ * High level function internals control
+ * Parses argc, argv array and executes builtin editline commands
+ */
+int el_parse(EditLine *, int, const char **);
+
+/*
+ * Low level editline access functions
+ */
+int el_set(EditLine *, int, ...);
+int el_get(EditLine *, int, void *);
+
+/*
+ * el_set/el_get parameters
+ */
+#define EL_PROMPT 0 /* , el_pfunc_t); */
+#define EL_TERMINAL 1 /* , const char *); */
+#define EL_EDITOR 2 /* , const char *); */
+#define EL_SIGNAL 3 /* , int); */
+#define EL_BIND 4 /* , const char *, ..., NULL); */
+#define EL_TELLTC 5 /* , const char *, ..., NULL); */
+#define EL_SETTC 6 /* , const char *, ..., NULL); */
+#define EL_ECHOTC 7 /* , const char *, ..., NULL); */
+#define EL_SETTY 8 /* , const char *, ..., NULL); */
+#define EL_ADDFN 9 /* , const char *, const char * */
+ /* , el_func_t); */
+#define EL_HIST 10 /* , hist_fun_t, const char *); */
+#define EL_EDITMODE 11 /* , int); */
+#define EL_RPROMPT 12 /* , el_pfunc_t); */
+#define EL_GETCFN 13 /* , el_rfunc_t); */
+#define EL_CLIENTDATA 14 /* , void *); */
+
+#define EL_BUILTIN_GETCFN (NULL)
+
+/*
+ * Source named file or $PWD/.editrc or $HOME/.editrc
+ */
+int el_source(EditLine *, const char *);
+
+/*
+ * Must be called when the terminal changes size; If EL_SIGNAL
+ * is set this is done automatically otherwise it is the responsibility
+ * of the application
+ */
+void el_resize(EditLine *);
+
+
+/*
+ * User-defined function interface.
+ */
+const LineInfo *el_line(EditLine *);
+int el_insertstr(EditLine *, const char *);
+void el_deletestr(EditLine *, int);
+
+/*
+ * ==== History ====
+ */
+
+typedef struct history History;
+
+typedef struct HistEvent {
+ int num;
+ const char *str;
+} HistEvent;
+
+/*
+ * History access functions.
+ */
+History * history_init(void);
+void history_end(History *);
+
+int history(History *, HistEvent *, int, ...);
+
+#define H_FUNC 0 /* , UTSL */
+#define H_SETSIZE 1 /* , const int); */
+#define H_GETSIZE 2 /* , void); */
+#define H_FIRST 3 /* , void); */
+#define H_LAST 4 /* , void); */
+#define H_PREV 5 /* , void); */
+#define H_NEXT 6 /* , void); */
+#define H_CURR 8 /* , const int); */
+#define H_SET 7 /* , void); */
+#define H_ADD 9 /* , const char *); */
+#define H_ENTER 10 /* , const char *); */
+#define H_APPEND 11 /* , const char *); */
+#define H_END 12 /* , void); */
+#define H_NEXT_STR 13 /* , const char *); */
+#define H_PREV_STR 14 /* , const char *); */
+#define H_NEXT_EVENT 15 /* , const int); */
+#define H_PREV_EVENT 16 /* , const int); */
+#define H_LOAD 17 /* , const char *); */
+#define H_SAVE 18 /* , const char *); */
+#define H_CLEAR 19 /* , void); */
+
+#endif /* _HISTEDIT_H_ */
diff --git a/trunk/main/editline/history.c b/trunk/main/editline/history.c
new file mode 100644
index 000000000..f133d2eb0
--- /dev/null
+++ b/trunk/main/editline/history.c
@@ -0,0 +1,875 @@
+/* $NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * hist.c: History access functions
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#ifdef HAVE_VIS_H
+#include <vis.h>
+#else
+#include "np/vis.h"
+#endif
+#include <sys/stat.h>
+
+static const char hist_cookie[] = "_HiStOrY_V2_\n";
+
+#include "histedit.h"
+
+typedef int (*history_gfun_t)(ptr_t, HistEvent *);
+typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *);
+typedef void (*history_vfun_t)(ptr_t, HistEvent *);
+typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int);
+
+struct history {
+ ptr_t h_ref; /* Argument for history fcns */
+ int h_ent; /* Last entry point for history */
+ history_gfun_t h_first; /* Get the first element */
+ history_gfun_t h_next; /* Get the next element */
+ history_gfun_t h_last; /* Get the last element */
+ history_gfun_t h_prev; /* Get the previous element */
+ history_gfun_t h_curr; /* Get the current element */
+ history_sfun_t h_set; /* Set the current element */
+ history_vfun_t h_clear; /* Clear the history list */
+ history_efun_t h_enter; /* Add an element */
+ history_efun_t h_add; /* Append to an element */
+};
+#define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev)
+#define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev)
+#define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev)
+#define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev)
+#define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev)
+#define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n)
+#define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev)
+#define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str)
+#define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str)
+
+#define h_malloc(a) malloc(a)
+#define h_realloc(a, b) realloc((a), (b))
+#define h_free(a) free(a)
+
+typedef struct {
+ int num;
+ char *str;
+} HistEventPrivate;
+
+
+
+private int history_setsize(History *, HistEvent *, int);
+private int history_getsize(History *, HistEvent *);
+private int history_set_fun(History *, History *);
+private int history_load(History *, const char *);
+private int history_save(History *, const char *);
+private int history_prev_event(History *, HistEvent *, int);
+private int history_next_event(History *, HistEvent *, int);
+private int history_next_string(History *, HistEvent *, const char *);
+private int history_prev_string(History *, HistEvent *, const char *);
+
+
+/***********************************************************************/
+
+/*
+ * Builtin- history implementation
+ */
+typedef struct hentry_t {
+ HistEvent ev; /* What we return */
+ struct hentry_t *next; /* Next entry */
+ struct hentry_t *prev; /* Previous entry */
+} hentry_t;
+
+typedef struct history_t {
+ hentry_t list; /* Fake list header element */
+ hentry_t *cursor; /* Current element in the list */
+ int max; /* Maximum number of events */
+ int cur; /* Current number of events */
+ int eventid; /* For generation of unique event id */
+} history_t;
+
+private int history_def_first(ptr_t, HistEvent *);
+private int history_def_last(ptr_t, HistEvent *);
+private int history_def_next(ptr_t, HistEvent *);
+private int history_def_prev(ptr_t, HistEvent *);
+private int history_def_curr(ptr_t, HistEvent *);
+private int history_def_set(ptr_t, HistEvent *, const int n);
+private int history_def_enter(ptr_t, HistEvent *, const char *);
+private int history_def_add(ptr_t, HistEvent *, const char *);
+private void history_def_init(ptr_t *, HistEvent *, int);
+private void history_def_clear(ptr_t, HistEvent *);
+private int history_def_insert(history_t *, HistEvent *, const char *);
+private void history_def_delete(history_t *, HistEvent *, hentry_t *);
+
+#define history_def_setsize(p, num)(void) (((history_t *) p)->max = (num))
+#define history_def_getsize(p) (((history_t *) p)->cur)
+
+#define he_strerror(code) he_errlist[code]
+#define he_seterrev(evp, code) {\
+ evp->num = code;\
+ evp->str = he_strerror(code);\
+ }
+
+/* error messages */
+static const char *const he_errlist[] = {
+ "OK",
+ "unknown error",
+ "malloc() failed",
+ "first event not found",
+ "last event not found",
+ "empty list",
+ "no next event",
+ "no previous event",
+ "current event is invalid",
+ "event not found",
+ "can't read history from file",
+ "can't write history",
+ "required parameter(s) not supplied",
+ "history size negative",
+ "function not allowed with other history-functions-set the default",
+ "bad parameters"
+};
+/* error codes */
+#define _HE_OK 0
+#define _HE_UNKNOWN 1
+#define _HE_MALLOC_FAILED 2
+#define _HE_FIRST_NOTFOUND 3
+#define _HE_LAST_NOTFOUND 4
+#define _HE_EMPTY_LIST 5
+#define _HE_END_REACHED 6
+#define _HE_START_REACHED 7
+#define _HE_CURR_INVALID 8
+#define _HE_NOT_FOUND 9
+#define _HE_HIST_READ 10
+#define _HE_HIST_WRITE 11
+#define _HE_PARAM_MISSING 12
+#define _HE_SIZE_NEGATIVE 13
+#define _HE_NOT_ALLOWED 14
+#define _HE_BAD_PARAM 15
+
+/* history_def_first():
+ * Default function to return the first event in the history.
+ */
+private int
+history_def_first(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ h->cursor = h->list.next;
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_FIRST_NOTFOUND);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_last():
+ * Default function to return the last event in the history.
+ */
+private int
+history_def_last(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ h->cursor = h->list.prev;
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_LAST_NOTFOUND);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_next():
+ * Default function to return the next event in the history.
+ */
+private int
+history_def_next(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cursor != &h->list)
+ h->cursor = h->cursor->next;
+ else {
+ he_seterrev(ev, _HE_EMPTY_LIST);
+ return (-1);
+ }
+
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_END_REACHED);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_prev():
+ * Default function to return the previous event in the history.
+ */
+private int
+history_def_prev(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cursor != &h->list)
+ h->cursor = h->cursor->prev;
+ else {
+ he_seterrev(ev,
+ (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
+ return (-1);
+ }
+
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_START_REACHED);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_curr():
+ * Default function to return the current event in the history.
+ */
+private int
+history_def_curr(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev,
+ (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_set():
+ * Default function to set the current event in the history to the
+ * given one.
+ */
+private int
+history_def_set(ptr_t p, HistEvent *ev, const int n)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cur == 0) {
+ he_seterrev(ev, _HE_EMPTY_LIST);
+ return (-1);
+ }
+ if (h->cursor == &h->list || h->cursor->ev.num != n) {
+ for (h->cursor = h->list.next; h->cursor != &h->list;
+ h->cursor = h->cursor->next)
+ if (h->cursor->ev.num == n)
+ break;
+ }
+ if (h->cursor == &h->list) {
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+ }
+ return (0);
+}
+
+
+/* history_def_add():
+ * Append string to element
+ */
+private int
+history_def_add(ptr_t p, HistEvent *ev, const char *str)
+{
+ history_t *h = (history_t *) p;
+ size_t len;
+ char *s;
+ HistEventPrivate *evp = (void *)&h->cursor->ev;
+
+ if (h->cursor == &h->list)
+ return (history_def_enter(p, ev, str));
+ len = strlen(evp->str) + strlen(str) + 1;
+ s = (char *) h_malloc(len);
+ if (!s) {
+ he_seterrev(ev, _HE_MALLOC_FAILED);
+ return (-1);
+ }
+ (void) strlcpy(s, h->cursor->ev.str, len);
+ (void) strlcat(s, str, len);
+ h_free(evp->str);
+ evp->str = s;
+ *ev = h->cursor->ev;
+ return (0);
+}
+
+
+/* history_def_delete():
+ * Delete element hp of the h list
+ */
+/* ARGSUSED */
+private void
+history_def_delete(history_t *h, HistEvent *ev, hentry_t *hp)
+{
+ HistEventPrivate *evp = (void *)&hp->ev;
+ if (hp == &h->list)
+ abort();
+ hp->prev->next = hp->next;
+ hp->next->prev = hp->prev;
+ h_free((ptr_t) evp->str);
+ h_free(hp);
+ h->cur--;
+}
+
+
+/* history_def_insert():
+ * Insert element with string str in the h list
+ */
+private int
+history_def_insert(history_t *h, HistEvent *ev, const char *str)
+{
+
+ h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
+ if (h->cursor)
+ h->cursor->ev.str = strdup(str);
+ if (!h->cursor || !h->cursor->ev.str) {
+ he_seterrev(ev, _HE_MALLOC_FAILED);
+ return (-1);
+ }
+ h->cursor->ev.num = ++h->eventid;
+ h->cursor->next = h->list.next;
+ h->cursor->prev = &h->list;
+ h->list.next->prev = h->cursor;
+ h->list.next = h->cursor;
+ h->cur++;
+
+ *ev = h->cursor->ev;
+ return (0);
+}
+
+
+/* history_def_enter():
+ * Default function to enter an item in the history
+ */
+private int
+history_def_enter(ptr_t p, HistEvent *ev, const char *str)
+{
+ history_t *h = (history_t *) p;
+
+ if (history_def_insert(h, ev, str) == -1)
+ return (-1); /* error, keep error message */
+
+ /*
+ * Always keep at least one entry.
+ * This way we don't have to check for the empty list.
+ */
+ while (h->cur > h->max && h->cur > 0)
+ history_def_delete(h, ev, h->list.prev);
+
+ return (0);
+}
+
+
+/* history_def_init():
+ * Default history initialization function
+ */
+/* ARGSUSED */
+private void
+history_def_init(ptr_t *p, HistEvent *ev, int n)
+{
+ history_t *h = (history_t *) h_malloc(sizeof(history_t));
+
+ if (n <= 0)
+ n = 0;
+ h->eventid = 0;
+ h->cur = 0;
+ h->max = n;
+ h->list.next = h->list.prev = &h->list;
+ h->list.ev.str = NULL;
+ h->list.ev.num = 0;
+ h->cursor = &h->list;
+ *p = (ptr_t) h;
+}
+
+
+/* history_def_clear():
+ * Default history cleanup function
+ */
+private void
+history_def_clear(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ while (h->list.prev != &h->list)
+ history_def_delete(h, ev, h->list.prev);
+ h->eventid = 0;
+ h->cur = 0;
+}
+
+
+
+
+/************************************************************************/
+
+/* history_init():
+ * Initialization function.
+ */
+public History *
+history_init(void)
+{
+ History *h = (History *) h_malloc(sizeof(History));
+ HistEvent ev;
+
+ history_def_init(&h->h_ref, &ev, 0);
+ h->h_ent = -1;
+ h->h_next = history_def_next;
+ h->h_first = history_def_first;
+ h->h_last = history_def_last;
+ h->h_prev = history_def_prev;
+ h->h_curr = history_def_curr;
+ h->h_set = history_def_set;
+ h->h_clear = history_def_clear;
+ h->h_enter = history_def_enter;
+ h->h_add = history_def_add;
+
+ return (h);
+}
+
+
+/* history_end():
+ * clean up history;
+ */
+public void
+history_end(History *h)
+{
+ HistEvent ev;
+
+ if (h->h_next == history_def_next)
+ history_def_clear(h->h_ref, &ev);
+}
+
+
+
+/* history_setsize():
+ * Set history number of events
+ */
+private int
+history_setsize(History *h, HistEvent *ev, int num)
+{
+
+ if (h->h_next != history_def_next) {
+ he_seterrev(ev, _HE_NOT_ALLOWED);
+ return (-1);
+ }
+ if (num < 0) {
+ he_seterrev(ev, _HE_BAD_PARAM);
+ return (-1);
+ }
+ history_def_setsize(h->h_ref, num);
+ return (0);
+}
+
+
+/* history_getsize():
+ * Get number of events currently in history
+ */
+private int
+history_getsize(History *h, HistEvent *ev)
+{
+ int retval = 0;
+
+ if (h->h_next != history_def_next) {
+ he_seterrev(ev, _HE_NOT_ALLOWED);
+ return (-1);
+ }
+ retval = history_def_getsize(h->h_ref);
+ if (retval < -1) {
+ he_seterrev(ev, _HE_SIZE_NEGATIVE);
+ return (-1);
+ }
+ ev->num = retval;
+ return (0);
+}
+
+
+/* history_set_fun():
+ * Set history functions
+ */
+private int
+history_set_fun(History *h, History *nh)
+{
+ HistEvent ev;
+
+ if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
+ nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
+ nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
+ nh->h_ref == NULL) {
+ if (h->h_next != history_def_next) {
+ history_def_init(&h->h_ref, &ev, 0);
+ h->h_first = history_def_first;
+ h->h_next = history_def_next;
+ h->h_last = history_def_last;
+ h->h_prev = history_def_prev;
+ h->h_curr = history_def_curr;
+ h->h_set = history_def_set;
+ h->h_clear = history_def_clear;
+ h->h_enter = history_def_enter;
+ h->h_add = history_def_add;
+ }
+ return (-1);
+ }
+ if (h->h_next == history_def_next)
+ history_def_clear(h->h_ref, &ev);
+
+ h->h_ent = -1;
+ h->h_first = nh->h_first;
+ h->h_next = nh->h_next;
+ h->h_last = nh->h_last;
+ h->h_prev = nh->h_prev;
+ h->h_curr = nh->h_curr;
+ h->h_set = nh->h_set;
+ h->h_clear = nh->h_clear;
+ h->h_enter = nh->h_enter;
+ h->h_add = nh->h_add;
+
+ return (0);
+}
+
+
+/* history_load():
+ * History load function
+ */
+private int
+history_load(History *h, const char *fname)
+{
+ FILE *fp;
+ char *line;
+ size_t sz, max_size;
+ char *ptr;
+ int i = -1;
+ HistEvent ev;
+
+ if ((fp = fopen(fname, "r")) == NULL)
+ return (i);
+
+ if ((line = fgetln(fp, &sz)) == NULL)
+ goto done;
+
+ if (strncmp(line, hist_cookie, sz) != 0)
+ goto done;
+
+ ptr = h_malloc(max_size = 1024);
+ for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
+ char c = line[sz];
+
+ if (sz != 0 && line[sz - 1] == '\n')
+ line[--sz] = '\0';
+ else
+ line[sz] = '\0';
+
+ if (max_size < sz) {
+ max_size = (sz + 1023) & ~1023;
+ ptr = h_realloc(ptr, max_size);
+ }
+ (void) strunvis(ptr, line);
+ line[sz] = c;
+ HENTER(h, &ev, ptr);
+ }
+ h_free(ptr);
+
+done:
+ (void) fclose(fp);
+ return (i);
+}
+
+
+/* history_save():
+ * History save function
+ */
+private int
+history_save(History *h, const char *fname)
+{
+ FILE *fp;
+ HistEvent ev;
+ int i = 0, retval;
+ size_t len, max_size;
+ char *ptr;
+
+ if ((fp = fopen(fname, "w")) == NULL)
+ return (-1);
+
+ (void) fchmod(fileno(fp), S_IRUSR|S_IWUSR);
+ (void) fputs(hist_cookie, fp);
+ ptr = h_malloc(max_size = 1024);
+ for (retval = HLAST(h, &ev);
+ retval != -1;
+ retval = HPREV(h, &ev), i++) {
+ len = strlen(ev.str) * 4;
+ if (len >= max_size) {
+ max_size = (len + 1023) & 1023;
+ ptr = h_realloc(ptr, max_size);
+ }
+ (void) strvis(ptr, ev.str, VIS_WHITE);
+ (void) fprintf(fp, "%s\n", ev.str);
+ }
+ h_free(ptr);
+ (void) fclose(fp);
+ return (i);
+}
+
+
+/* history_prev_event():
+ * Find the previous event, with number given
+ */
+private int
+history_prev_event(History *h, HistEvent *ev, int num)
+{
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
+ if (ev->num == num)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history_next_event():
+ * Find the next event, with number given
+ */
+private int
+history_next_event(History *h, HistEvent *ev, int num)
+{
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
+ if (ev->num == num)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history_prev_string():
+ * Find the previous event beginning with string
+ */
+private int
+history_prev_string(History *h, HistEvent *ev, const char *str)
+{
+ size_t len = strlen(str);
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
+ if (strncmp(str, ev->str, len) == 0)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history_next_string():
+ * Find the next event beginning with string
+ */
+private int
+history_next_string(History *h, HistEvent *ev, const char *str)
+{
+ size_t len = strlen(str);
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
+ if (strncmp(str, ev->str, len) == 0)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history():
+ * User interface to history functions.
+ */
+int
+history(History *h, HistEvent *ev, int fun, ...)
+{
+ va_list va;
+ const char *str;
+ int retval;
+
+ va_start(va, fun);
+
+ he_seterrev(ev, _HE_OK);
+
+ switch (fun) {
+ case H_GETSIZE:
+ retval = history_getsize(h, ev);
+ break;
+
+ case H_SETSIZE:
+ retval = history_setsize(h, ev, va_arg(va, int));
+ break;
+
+ case H_ADD:
+ str = va_arg(va, const char *);
+ retval = HADD(h, ev, str);
+ break;
+
+ case H_ENTER:
+ str = va_arg(va, const char *);
+ if ((retval = HENTER(h, ev, str)) != -1)
+ h->h_ent = ev->num;
+ break;
+
+ case H_APPEND:
+ str = va_arg(va, const char *);
+ if ((retval = HSET(h, ev, h->h_ent)) != -1)
+ retval = HADD(h, ev, str);
+ break;
+
+ case H_FIRST:
+ retval = HFIRST(h, ev);
+ break;
+
+ case H_NEXT:
+ retval = HNEXT(h, ev);
+ break;
+
+ case H_LAST:
+ retval = HLAST(h, ev);
+ break;
+
+ case H_PREV:
+ retval = HPREV(h, ev);
+ break;
+
+ case H_CURR:
+ retval = HCURR(h, ev);
+ break;
+
+ case H_SET:
+ retval = HSET(h, ev, va_arg(va, const int));
+ break;
+
+ case H_CLEAR:
+ HCLEAR(h, ev);
+ retval = 0;
+ break;
+
+ case H_LOAD:
+ retval = history_load(h, va_arg(va, const char *));
+ if (retval == -1)
+ he_seterrev(ev, _HE_HIST_READ);
+ break;
+
+ case H_SAVE:
+ retval = history_save(h, va_arg(va, const char *));
+ if (retval == -1)
+ he_seterrev(ev, _HE_HIST_WRITE);
+ break;
+
+ case H_PREV_EVENT:
+ retval = history_prev_event(h, ev, va_arg(va, int));
+ break;
+
+ case H_NEXT_EVENT:
+ retval = history_next_event(h, ev, va_arg(va, int));
+ break;
+
+ case H_PREV_STR:
+ retval = history_prev_string(h, ev, va_arg(va, const char *));
+ break;
+
+ case H_NEXT_STR:
+ retval = history_next_string(h, ev, va_arg(va, const char *));
+ break;
+
+ case H_FUNC:
+ {
+ History hf;
+
+ hf.h_ref = va_arg(va, ptr_t);
+ h->h_ent = -1;
+ hf.h_first = va_arg(va, history_gfun_t);
+ hf.h_next = va_arg(va, history_gfun_t);
+ hf.h_last = va_arg(va, history_gfun_t);
+ hf.h_prev = va_arg(va, history_gfun_t);
+ hf.h_curr = va_arg(va, history_gfun_t);
+ hf.h_set = va_arg(va, history_sfun_t);
+ hf.h_clear = va_arg(va, history_vfun_t);
+ hf.h_enter = va_arg(va, history_efun_t);
+ hf.h_add = va_arg(va, history_efun_t);
+
+ if ((retval = history_set_fun(h, &hf)) == -1)
+ he_seterrev(ev, _HE_PARAM_MISSING);
+ break;
+ }
+
+ case H_END:
+ history_end(h);
+ retval = 0;
+ break;
+
+ default:
+ retval = -1;
+ he_seterrev(ev, _HE_UNKNOWN);
+ break;
+ }
+ va_end(va);
+ return (retval);
+}
diff --git a/trunk/main/editline/install-sh b/trunk/main/editline/install-sh
new file mode 100755
index 000000000..ebc66913e
--- /dev/null
+++ b/trunk/main/editline/install-sh
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, 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 name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/trunk/main/editline/key.c b/trunk/main/editline/key.c
new file mode 100644
index 000000000..0dcdf4191
--- /dev/null
+++ b/trunk/main/editline/key.c
@@ -0,0 +1,687 @@
+/* $NetBSD: key.c,v 1.13 2002/03/18 16:00:55 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: key.c,v 1.13 2002/03/18 16:00:55 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * key.c: This module contains the procedures for maintaining
+ * the extended-key map.
+ *
+ * An extended-key (key) is a sequence of keystrokes introduced
+ * with an sequence introducer and consisting of an arbitrary
+ * number of characters. This module maintains a map (the el->el_key.map)
+ * to convert these extended-key sequences into input strs
+ * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
+ *
+ * Warning:
+ * If key is a substr of some other keys, then the longer
+ * keys are lost!! That is, if the keys "abcd" and "abcef"
+ * are in el->el_key.map, adding the key "abc" will cause the first two
+ * definitions to be lost.
+ *
+ * Restrictions:
+ * -------------
+ * 1) It is not possible to have one key that is a
+ * substr of another.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+#include "el.h"
+
+/*
+ * The Nodes of the el->el_key.map. The el->el_key.map is a linked list
+ * of these node elements
+ */
+struct key_node_t {
+ char ch; /* single character of key */
+ int type; /* node type */
+ key_value_t val; /* command code or pointer to str, */
+ /* if this is a leaf */
+ struct key_node_t *next; /* ptr to next char of this key */
+ struct key_node_t *sibling; /* ptr to another key with same prefix*/
+};
+
+private int node_trav(EditLine *, key_node_t *, char *,
+ key_value_t *);
+private int node__try(EditLine *, key_node_t *, const char *,
+ key_value_t *, int);
+private key_node_t *node__get(int);
+private void node__put(EditLine *, key_node_t *);
+private int node__delete(EditLine *, key_node_t **, const char *);
+private int node_lookup(EditLine *, const char *, key_node_t *,
+ int);
+private int node_enum(EditLine *, key_node_t *, int);
+private int key__decode_char(char *, int, int);
+
+#define KEY_BUFSIZ EL_BUFSIZ
+
+
+/* key_init():
+ * Initialize the key maps
+ */
+protected int
+key_init(EditLine *el)
+{
+
+ el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ);
+ if (el->el_key.buf == NULL)
+ return (-1);
+ el->el_key.map = NULL;
+ key_reset(el);
+ return (0);
+}
+
+
+/* key_end():
+ * Free the key maps
+ */
+protected void
+key_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_key.buf);
+ el->el_key.buf = NULL;
+ node__put(el, el->el_key.map);
+ el->el_key.map = NULL;
+}
+
+
+/* key_map_cmd():
+ * Associate cmd with a key value
+ */
+protected key_value_t *
+key_map_cmd(EditLine *el, int cmd)
+{
+
+ el->el_key.val.cmd = (el_action_t) cmd;
+ return (&el->el_key.val);
+}
+
+
+/* key_map_str():
+ * Associate str with a key value
+ */
+protected key_value_t *
+key_map_str(EditLine *el, char *str)
+{
+
+ el->el_key.val.str = str;
+ return (&el->el_key.val);
+}
+
+
+/* key_reset():
+ * Takes all nodes on el->el_key.map and puts them on free list. Then
+ * initializes el->el_key.map with arrow keys
+ * [Always bind the ansi arrow keys?]
+ */
+protected void
+key_reset(EditLine *el)
+{
+
+ node__put(el, el->el_key.map);
+ el->el_key.map = NULL;
+ return;
+}
+
+
+/* key_get():
+ * Calls the recursive function with entry point el->el_key.map
+ * Looks up *ch in map and then reads characters until a
+ * complete match is found or a mismatch occurs. Returns the
+ * type of the match found (XK_STR, XK_CMD, or XK_EXE).
+ * Returns NULL in val.str and XK_STR for no match.
+ * The last character read is returned in *ch.
+ */
+protected int
+key_get(EditLine *el, char *ch, key_value_t *val)
+{
+
+ return (node_trav(el, el->el_key.map, ch, val));
+}
+
+
+/* key_add():
+ * Adds key to the el->el_key.map and associates the value in val with it.
+ * If key is already is in el->el_key.map, the new code is applied to the
+ * existing key. Ntype specifies if code is a command, an
+ * out str or a unix command.
+ */
+protected void
+key_add(EditLine *el, const char *key, key_value_t *val, int ntype)
+{
+
+ if (key[0] == '\0') {
+ (void) fprintf(el->el_errfile,
+ "key_add: Null extended-key not allowed.\n");
+ return;
+ }
+ if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
+ (void) fprintf(el->el_errfile,
+ "key_add: sequence-lead-in command not allowed\n");
+ return;
+ }
+ if (el->el_key.map == NULL)
+ /* tree is initially empty. Set up new node to match key[0] */
+ el->el_key.map = node__get(key[0]);
+ /* it is properly initialized */
+
+ /* Now recurse through el->el_key.map */
+ (void) node__try(el, el->el_key.map, key, val, ntype);
+ return;
+}
+
+
+/* key_clear():
+ *
+ */
+protected void
+key_clear(EditLine *el, el_action_t *map, const char *in)
+{
+
+ if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
+ ((map == el->el_map.key &&
+ el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
+ (map == el->el_map.alt &&
+ el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
+ (void) key_delete(el, in);
+}
+
+
+/* key_delete():
+ * Delete the key and all longer keys staring with key, if
+ * they exists.
+ */
+protected int
+key_delete(EditLine *el, const char *key)
+{
+
+ if (key[0] == '\0') {
+ (void) fprintf(el->el_errfile,
+ "key_delete: Null extended-key not allowed.\n");
+ return (-1);
+ }
+ if (el->el_key.map == NULL)
+ return (0);
+
+ (void) node__delete(el, &el->el_key.map, key);
+ return (0);
+}
+
+
+/* key_print():
+ * Print the binding associated with key key.
+ * Print entire el->el_key.map if null
+ */
+protected void
+key_print(EditLine *el, const char *key)
+{
+
+ /* do nothing if el->el_key.map is empty and null key specified */
+ if (el->el_key.map == NULL && *key == 0)
+ return;
+
+ el->el_key.buf[0] = '"';
+ if (node_lookup(el, key, el->el_key.map, 1) <= -1)
+ /* key is not bound */
+ (void) fprintf(el->el_errfile, "Unbound extended key \"%s\"\n",
+ key);
+ return;
+}
+
+
+/* node_trav():
+ * recursively traverses node in tree until match or mismatch is
+ * found. May read in more characters.
+ */
+private int
+node_trav(EditLine *el, key_node_t *ptr, char *ch, key_value_t *val)
+{
+
+ if (ptr->ch == *ch) {
+ /* match found */
+ if (ptr->next) {
+ /* key not complete so get next char */
+ if (el_getc(el, ch) != 1) { /* if EOF or error */
+ val->cmd = ED_END_OF_FILE;
+ return (XK_CMD);
+ /* PWP: Pretend we just read an end-of-file */
+ }
+ return (node_trav(el, ptr->next, ch, val));
+ } else {
+ *val = ptr->val;
+ if (ptr->type != XK_CMD)
+ *ch = '\0';
+ return (ptr->type);
+ }
+ } else {
+ /* no match found here */
+ if (ptr->sibling) {
+ /* try next sibling */
+ return (node_trav(el, ptr->sibling, ch, val));
+ } else {
+ /* no next sibling -- mismatch */
+ val->str = NULL;
+ return (XK_STR);
+ }
+ }
+}
+
+
+/* node__try():
+ * Find a node that matches *str or allocate a new one
+ */
+private int
+node__try(EditLine *el, key_node_t *ptr, const char *str, key_value_t *val, int ntype)
+{
+
+ if (ptr->ch != *str) {
+ key_node_t *xm;
+
+ for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
+ if (xm->sibling->ch == *str)
+ break;
+ if (xm->sibling == NULL)
+ xm->sibling = node__get(*str); /* setup new node */
+ ptr = xm->sibling;
+ }
+ if (*++str == '\0') {
+ /* we're there */
+ if (ptr->next != NULL) {
+ node__put(el, ptr->next);
+ /* lose longer keys with this prefix */
+ ptr->next = NULL;
+ }
+ switch (ptr->type) {
+ case XK_CMD:
+ case XK_NOD:
+ break;
+ case XK_STR:
+ case XK_EXE:
+ if (ptr->val.str)
+ el_free((ptr_t) ptr->val.str);
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
+ ptr->type));
+ break;
+ }
+
+ switch (ptr->type = ntype) {
+ case XK_CMD:
+ ptr->val = *val;
+ break;
+ case XK_STR:
+ case XK_EXE:
+ ptr->val.str = strdup(val->str);
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
+ break;
+ }
+ } else {
+ /* still more chars to go */
+ if (ptr->next == NULL)
+ ptr->next = node__get(*str); /* setup new node */
+ (void) node__try(el, ptr->next, str, val, ntype);
+ }
+ return (0);
+}
+
+
+/* node__delete():
+ * Delete node that matches str
+ */
+private int
+node__delete(EditLine *el, key_node_t **inptr, const char *str)
+{
+ key_node_t *ptr;
+ key_node_t *prev_ptr = NULL;
+
+ ptr = *inptr;
+
+ if (ptr->ch != *str) {
+ key_node_t *xm;
+
+ for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
+ if (xm->sibling->ch == *str)
+ break;
+ if (xm->sibling == NULL)
+ return (0);
+ prev_ptr = xm;
+ ptr = xm->sibling;
+ }
+ if (*++str == '\0') {
+ /* we're there */
+ if (prev_ptr == NULL)
+ *inptr = ptr->sibling;
+ else
+ prev_ptr->sibling = ptr->sibling;
+ ptr->sibling = NULL;
+ node__put(el, ptr);
+ return (1);
+ } else if (ptr->next != NULL &&
+ node__delete(el, &ptr->next, str) == 1) {
+ if (ptr->next != NULL)
+ return (0);
+ if (prev_ptr == NULL)
+ *inptr = ptr->sibling;
+ else
+ prev_ptr->sibling = ptr->sibling;
+ ptr->sibling = NULL;
+ node__put(el, ptr);
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+
+/* node__put():
+ * Puts a tree of nodes onto free list using free(3).
+ */
+private void
+node__put(EditLine *el, key_node_t *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ if (ptr->next != NULL) {
+ node__put(el, ptr->next);
+ ptr->next = NULL;
+ }
+ node__put(el, ptr->sibling);
+
+ switch (ptr->type) {
+ case XK_CMD:
+ case XK_NOD:
+ break;
+ case XK_EXE:
+ case XK_STR:
+ if (ptr->val.str != NULL)
+ el_free((ptr_t) ptr->val.str);
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
+ break;
+ }
+ el_free((ptr_t) ptr);
+}
+
+
+/* node__get():
+ * Returns pointer to an key_node_t for ch.
+ */
+private key_node_t *
+node__get(int ch)
+{
+ key_node_t *ptr;
+
+ ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
+ if (ptr == NULL)
+ return NULL;
+ ptr->ch = ch;
+ ptr->type = XK_NOD;
+ ptr->val.str = NULL;
+ ptr->next = NULL;
+ ptr->sibling = NULL;
+ return (ptr);
+}
+
+
+
+/* node_lookup():
+ * look for the str starting at node ptr.
+ * Print if last node
+ */
+private int
+node_lookup(EditLine *el, const char *str, key_node_t *ptr, int cnt)
+{
+ int ncnt;
+
+ if (ptr == NULL)
+ return (-1); /* cannot have null ptr */
+
+ if (*str == 0) {
+ /* no more chars in str. node_enum from here. */
+ (void) node_enum(el, ptr, cnt);
+ return (0);
+ } else {
+ /* If match put this char into el->el_key.buf. Recurse */
+ if (ptr->ch == *str) {
+ /* match found */
+ ncnt = key__decode_char(el->el_key.buf, cnt,
+ (unsigned char) ptr->ch);
+ if (ptr->next != NULL)
+ /* not yet at leaf */
+ return (node_lookup(el, str + 1, ptr->next,
+ ncnt + 1));
+ else {
+ /* next node is null so key should be complete */
+ if (str[1] == 0) {
+ el->el_key.buf[ncnt + 1] = '"';
+ el->el_key.buf[ncnt + 2] = '\0';
+ key_kprint(el, el->el_key.buf,
+ &ptr->val, ptr->type);
+ return (0);
+ } else
+ return (-1);
+ /* mismatch -- str still has chars */
+ }
+ } else {
+ /* no match found try sibling */
+ if (ptr->sibling)
+ return (node_lookup(el, str, ptr->sibling,
+ cnt));
+ else
+ return (-1);
+ }
+ }
+}
+
+
+/* node_enum():
+ * Traverse the node printing the characters it is bound in buffer
+ */
+private int
+node_enum(EditLine *el, key_node_t *ptr, int cnt)
+{
+ int ncnt;
+
+ if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */
+ el->el_key.buf[++cnt] = '"';
+ el->el_key.buf[++cnt] = '\0';
+ (void) fprintf(el->el_errfile,
+ "Some extended keys too long for internal print buffer");
+ (void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf);
+ return (0);
+ }
+ if (ptr == NULL) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile,
+ "node_enum: BUG!! Null ptr passed\n!");
+#endif
+ return (-1);
+ }
+ /* put this char at end of str */
+ ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch);
+ if (ptr->next == NULL) {
+ /* print this key and function */
+ el->el_key.buf[ncnt + 1] = '"';
+ el->el_key.buf[ncnt + 2] = '\0';
+ key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
+ } else
+ (void) node_enum(el, ptr->next, ncnt + 1);
+
+ /* go to sibling if there is one */
+ if (ptr->sibling)
+ (void) node_enum(el, ptr->sibling, cnt);
+ return (0);
+}
+
+
+/* key_kprint():
+ * Print the specified key and its associated
+ * function specified by val
+ */
+protected void
+key_kprint(EditLine *el, const char *key, key_value_t *val, int ntype)
+{
+ el_bindings_t *fp;
+ char unparsbuf[EL_BUFSIZ];
+ static const char fmt[] = "%-15s-> %s\n";
+
+ if (val != NULL)
+ switch (ntype) {
+ case XK_STR:
+ case XK_EXE:
+ (void) fprintf(el->el_outfile, fmt, key,
+ key__decode_str(val->str, unparsbuf,
+ ntype == XK_STR ? "\"\"" : "[]"));
+ break;
+ case XK_CMD:
+ for (fp = el->el_map.help; fp->name; fp++)
+ if (val->cmd == fp->func) {
+ (void) fprintf(el->el_outfile, fmt,
+ key, fp->name);
+ break;
+ }
+#ifdef DEBUG_KEY
+ if (fp->name == NULL)
+ (void) fprintf(el->el_outfile,
+ "BUG! Command not found.\n");
+#endif
+
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
+ break;
+ }
+ else
+ (void) fprintf(el->el_outfile, fmt, key, "no input");
+}
+
+
+/* key__decode_char():
+ * Put a printable form of char in buf.
+ */
+private int
+key__decode_char(char *buf, int cnt, int ch)
+{
+ if (ch == 0) {
+ buf[cnt++] = '^';
+ buf[cnt] = '@';
+ return (cnt);
+ }
+ if (iscntrl(ch)) {
+ buf[cnt++] = '^';
+ if (ch == '\177')
+ buf[cnt] = '?';
+ else
+ buf[cnt] = ch | 0100;
+ } else if (ch == '^') {
+ buf[cnt++] = '\\';
+ buf[cnt] = '^';
+ } else if (ch == '\\') {
+ buf[cnt++] = '\\';
+ buf[cnt] = '\\';
+ } else if (ch == ' ' || (isprint(ch) && !isspace(ch))) {
+ buf[cnt] = ch;
+ } else {
+ buf[cnt++] = '\\';
+ buf[cnt++] = (((unsigned int) ch >> 6) & 7) + '0';
+ buf[cnt++] = (((unsigned int) ch >> 3) & 7) + '0';
+ buf[cnt] = (ch & 7) + '0';
+ }
+ return (cnt);
+}
+
+
+/* key__decode_str():
+ * Make a printable version of the ey
+ */
+protected char *
+key__decode_str(const char *str, char *buf, const char *sep)
+{
+ char *b;
+ const char *p;
+
+ b = buf;
+ if (sep[0] != '\0')
+ *b++ = sep[0];
+ if (*str == 0) {
+ *b++ = '^';
+ *b++ = '@';
+ if (sep[0] != '\0' && sep[1] != '\0')
+ *b++ = sep[1];
+ *b++ = 0;
+ return (buf);
+ }
+ for (p = str; *p != 0; p++) {
+ if (iscntrl((unsigned char) *p)) {
+ *b++ = '^';
+ if (*p == '\177')
+ *b++ = '?';
+ else
+ *b++ = *p | 0100;
+ } else if (*p == '^' || *p == '\\') {
+ *b++ = '\\';
+ *b++ = *p;
+ } else if (*p == ' ' || (isprint((unsigned char) *p) &&
+ !isspace((unsigned char) *p))) {
+ *b++ = *p;
+ } else {
+ *b++ = '\\';
+ *b++ = (((unsigned int) *p >> 6) & 7) + '0';
+ *b++ = (((unsigned int) *p >> 3) & 7) + '0';
+ *b++ = (*p & 7) + '0';
+ }
+ }
+ if (sep[0] != '\0' && sep[1] != '\0')
+ *b++ = sep[1];
+ *b++ = 0;
+ return (buf); /* should check for overflow */
+}
diff --git a/trunk/main/editline/key.h b/trunk/main/editline/key.h
new file mode 100644
index 000000000..80d8626b8
--- /dev/null
+++ b/trunk/main/editline/key.h
@@ -0,0 +1,79 @@
+/* $NetBSD: key.h,v 1.6 2002/03/18 16:00:55 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)key.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.key.h: Key macro header
+ */
+#ifndef _h_el_key
+#define _h_el_key
+
+typedef union key_value_t {
+ el_action_t cmd; /* If it is a command the # */
+ char *str; /* If it is a string... */
+} key_value_t;
+
+typedef struct key_node_t key_node_t;
+
+typedef struct el_key_t {
+ char *buf; /* Key print buffer */
+ key_node_t *map; /* Key map */
+ key_value_t val; /* Local conversion buffer */
+} el_key_t;
+
+#define XK_CMD 0
+#define XK_STR 1
+#define XK_NOD 2
+#define XK_EXE 3
+
+protected int key_init(EditLine *);
+protected void key_end(EditLine *);
+protected key_value_t *key_map_cmd(EditLine *, int);
+protected key_value_t *key_map_str(EditLine *, char *);
+protected void key_reset(EditLine *);
+protected int key_get(EditLine *, char *, key_value_t *);
+protected void key_add(EditLine *, const char *, key_value_t *, int);
+protected void key_clear(EditLine *, el_action_t *, const char *);
+protected int key_delete(EditLine *, const char *);
+protected void key_print(EditLine *, const char *);
+protected void key_kprint(EditLine *, const char *, key_value_t *,
+ int);
+protected char *key__decode_str(const char *, char *, const char *);
+
+#endif /* _h_el_key */
diff --git a/trunk/main/editline/makelist b/trunk/main/editline/makelist
new file mode 100644
index 000000000..36f434cd0
--- /dev/null
+++ b/trunk/main/editline/makelist
@@ -0,0 +1,254 @@
+#!/bin/sh -
+# $NetBSD: makelist,v 1.7 2001/01/09 19:22:31 jdolecek Exp $
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Christos Zoulas of Cornell University.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)makelist 5.3 (Berkeley) 6/4/93
+
+# makelist.sh: Automatically generate header files...
+
+AWK=/usr/bin/awk
+USAGE="Usage: $0 -h|-e|-fc|-fh|-bc|-bh|-m <filenames>"
+
+if [ "x$1" = "x" ]
+then
+ echo $USAGE 1>&2
+ exit 1
+fi
+
+FLAG="$1"
+shift
+
+FILES="$@"
+
+case $FLAG in
+
+# generate foo.h file from foo.c
+#
+-h)
+ set - `echo $FILES | sed -e 's/\\./_/g'`
+ hdr="_h_`basename $1`"
+ cat $FILES | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#ifndef %s\n#define %s\n", "'$hdr'", "'$hdr'");
+ }
+ /\(\):/ {
+ pr = substr($2, 1, 2);
+ if (pr == "vi" || pr == "em" || pr == "ed") {
+ name = substr($2, 1, length($2) - 3);
+#
+# XXX: need a space between name and prototype so that -fc and -fh
+# parsing is much easier
+#
+ printf("protected el_action_t\t%s (EditLine *, int);\n", name);
+ }
+ }
+ END {
+ printf("#endif /* %s */\n", "'$hdr'");
+ }'
+ ;;
+
+# generate help.c from various .c files
+#
+-bc)
+ cat $FILES | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#include \"sys.h\"\n#include \"el.h\"\n");
+ printf("private const struct el_bindings_t el_func_help[] = {\n");
+ low = "abcdefghijklmnopqrstuvwxyz_";
+ high = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
+ for (i = 1; i <= length(low); i++)
+ tr[substr(low, i, 1)] = substr(high, i, 1);
+ }
+ /\(\):/ {
+ pr = substr($2, 1, 2);
+ if (pr == "vi" || pr == "em" || pr == "ed") {
+ name = substr($2, 1, length($2) - 3);
+ uname = "";
+ fname = "";
+ for (i = 1; i <= length(name); i++) {
+ s = substr(name, i, 1);
+ uname = uname tr[s];
+ if (s == "_")
+ s = "-";
+ fname = fname s;
+ }
+
+ printf(" { %-30.30s %-30.30s\n","\"" fname "\",", uname ",");
+ ok = 1;
+ }
+ }
+ /^ \*/ {
+ if (ok) {
+ printf(" \"");
+ for (i = 2; i < NF; i++)
+ printf("%s ", $i);
+ printf("%s\" },\n", $i);
+ ok = 0;
+ }
+ }
+ END {
+ printf(" { NULL, 0, NULL }\n");
+ printf("};\n");
+ printf("\nprotected const el_bindings_t* help__get()");
+ printf("{ return el_func_help; }\n");
+ }'
+ ;;
+
+# generate help.h from various .c files
+#
+-bh)
+ $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#ifndef _h_help_c\n#define _h_help_c\n");
+ printf("protected const el_bindings_t *help__get(void);\n");
+ printf("#endif /* _h_help_c */\n");
+ }' /dev/null
+ ;;
+
+# generate fcns.h from various .h files
+#
+-fh)
+ cat $FILES | $AWK '/el_action_t/ { print $3 }' | \
+ sort | tr '[:lower:]' '[:upper:]' | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#ifndef _h_fcns_c\n#define _h_fcns_c\n");
+ count = 0;
+ }
+ {
+ printf("#define\t%-30.30s\t%3d\n", $1, count++);
+ }
+ END {
+ printf("#define\t%-30.30s\t%3d\n", "EL_NUM_FCNS", count);
+
+ printf("typedef el_action_t (*el_func_t)(EditLine *, int);");
+ printf("\nprotected const el_func_t* func__get(void);\n");
+ printf("#endif /* _h_fcns_c */\n");
+ }'
+ ;;
+
+# generate fcns.c from various .h files
+#
+-fc)
+ cat $FILES | $AWK '/el_action_t/ { print $3 }' | sort | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#include \"sys.h\"\n#include \"el.h\"\n");
+ printf("private const el_func_t el_func[] = {");
+ maxlen = 80;
+ needn = 1;
+ len = 0;
+ }
+ {
+ clen = 25 + 2;
+ len += clen;
+ if (len >= maxlen)
+ needn = 1;
+ if (needn) {
+ printf("\n ");
+ needn = 0;
+ len = 4 + clen;
+ }
+ s = $1 ",";
+ printf("%-26.26s ", s);
+ }
+ END {
+ printf("\n};\n");
+ printf("\nprotected const el_func_t* func__get() { return el_func; }\n");
+ }'
+ ;;
+
+# generate editline.c from various .c files
+#
+-e)
+ echo "$FILES" | tr ' ' '\012' | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#define protected static\n");
+ printf("#define SCCSID\n");
+ }
+ {
+ printf("#include \"%s\"\n", $1);
+ }'
+ ;;
+
+# generate man page fragment from various .c files
+#
+-m)
+ cat $FILES | $AWK '
+ BEGIN {
+ printf(".\\\" Section automatically generated with makelist\n");
+ printf(".Bl -tag -width 4n\n");
+ }
+ /\(\):/ {
+ pr = substr($2, 1, 2);
+ if (pr == "vi" || pr == "em" || pr == "ed") {
+ name = substr($2, 1, length($2) - 3);
+ fname = "";
+ for (i = 1; i <= length(name); i++) {
+ s = substr(name, i, 1);
+ if (s == "_")
+ s = "-";
+ fname = fname s;
+ }
+
+ printf(".It Ic %s\n", fname);
+ ok = 1;
+ }
+ }
+ /^ \*/ {
+ if (ok) {
+ for (i = 2; i < NF; i++)
+ printf("%s ", $i);
+ printf("%s.\n", $i);
+ ok = 0;
+ }
+ }
+ END {
+ printf(".El\n");
+ printf(".\\\" End of section automatically generated with makelist\n");
+ }'
+ ;;
+
+*)
+ echo $USAGE 1>&2
+ exit 1
+ ;;
+
+esac
diff --git a/trunk/main/editline/map.c b/trunk/main/editline/map.c
new file mode 100644
index 000000000..4187cb597
--- /dev/null
+++ b/trunk/main/editline/map.c
@@ -0,0 +1,1418 @@
+/* $NetBSD: map.c,v 1.15 2002/03/18 16:00:55 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: map.c,v 1.15 2002/03/18 16:00:55 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * map.c: Editor function definitions
+ */
+#include <stdlib.h>
+#include "el.h"
+
+#define N_KEYS 256
+
+private void map_print_key(EditLine *, el_action_t *, const char *);
+private void map_print_some_keys(EditLine *, el_action_t *, int, int);
+private void map_print_all_keys(EditLine *);
+private void map_init_nls(EditLine *);
+private void map_init_meta(EditLine *);
+
+/* keymap tables ; should be N_KEYS*sizeof(KEYCMD) bytes long */
+
+
+private const el_action_t el_map_emacs[] = {
+ /* 0 */ EM_SET_MARK, /* ^@ */
+ /* 1 */ ED_MOVE_TO_BEG, /* ^A */
+ /* 2 */ ED_PREV_CHAR, /* ^B */
+ /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 4 */ EM_DELETE_OR_LIST, /* ^D */
+ /* 5 */ ED_MOVE_TO_END, /* ^E */
+ /* 6 */ ED_NEXT_CHAR, /* ^F */
+ /* 7 */ ED_UNASSIGNED, /* ^G */
+ /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */
+ /* 9 */ ED_UNASSIGNED, /* ^I */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_KILL_LINE, /* ^K */
+ /* 12 */ ED_CLEAR_SCREEN, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_NEXT_HISTORY, /* ^N */
+ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 16 */ ED_PREV_HISTORY, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_REDISPLAY, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */
+ /* 21 */ EM_KILL_LINE, /* ^U */
+ /* 22 */ ED_QUOTED_INSERT, /* ^V */
+ /* 23 */ EM_KILL_REGION, /* ^W */
+ /* 24 */ ED_SEQUENCE_LEAD_IN, /* ^X */
+ /* 25 */ EM_YANK, /* ^Y */
+ /* 26 */ ED_TTY_SIGTSTP, /* ^Z */
+ /* 27 */ EM_META_NEXT, /* ^[ */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_TTY_DSUSP, /* ^] */
+ /* 30 */ ED_UNASSIGNED, /* ^^ */
+ /* 31 */ ED_UNASSIGNED, /* ^_ */
+ /* 32 */ ED_INSERT, /* SPACE */
+ /* 33 */ ED_INSERT, /* ! */
+ /* 34 */ ED_INSERT, /* " */
+ /* 35 */ ED_INSERT, /* # */
+ /* 36 */ ED_INSERT, /* $ */
+ /* 37 */ ED_INSERT, /* % */
+ /* 38 */ ED_INSERT, /* & */
+ /* 39 */ ED_INSERT, /* ' */
+ /* 40 */ ED_INSERT, /* ( */
+ /* 41 */ ED_INSERT, /* ) */
+ /* 42 */ ED_INSERT, /* * */
+ /* 43 */ ED_INSERT, /* + */
+ /* 44 */ ED_INSERT, /* , */
+ /* 45 */ ED_INSERT, /* - */
+ /* 46 */ ED_INSERT, /* . */
+ /* 47 */ ED_INSERT, /* / */
+ /* 48 */ ED_DIGIT, /* 0 */
+ /* 49 */ ED_DIGIT, /* 1 */
+ /* 50 */ ED_DIGIT, /* 2 */
+ /* 51 */ ED_DIGIT, /* 3 */
+ /* 52 */ ED_DIGIT, /* 4 */
+ /* 53 */ ED_DIGIT, /* 5 */
+ /* 54 */ ED_DIGIT, /* 6 */
+ /* 55 */ ED_DIGIT, /* 7 */
+ /* 56 */ ED_DIGIT, /* 8 */
+ /* 57 */ ED_DIGIT, /* 9 */
+ /* 58 */ ED_INSERT, /* : */
+ /* 59 */ ED_INSERT, /* ; */
+ /* 60 */ ED_INSERT, /* < */
+ /* 61 */ ED_INSERT, /* = */
+ /* 62 */ ED_INSERT, /* > */
+ /* 63 */ ED_INSERT, /* ? */
+ /* 64 */ ED_INSERT, /* @ */
+ /* 65 */ ED_INSERT, /* A */
+ /* 66 */ ED_INSERT, /* B */
+ /* 67 */ ED_INSERT, /* C */
+ /* 68 */ ED_INSERT, /* D */
+ /* 69 */ ED_INSERT, /* E */
+ /* 70 */ ED_INSERT, /* F */
+ /* 71 */ ED_INSERT, /* G */
+ /* 72 */ ED_INSERT, /* H */
+ /* 73 */ ED_INSERT, /* I */
+ /* 74 */ ED_INSERT, /* J */
+ /* 75 */ ED_INSERT, /* K */
+ /* 76 */ ED_INSERT, /* L */
+ /* 77 */ ED_INSERT, /* M */
+ /* 78 */ ED_INSERT, /* N */
+ /* 79 */ ED_INSERT, /* O */
+ /* 80 */ ED_INSERT, /* P */
+ /* 81 */ ED_INSERT, /* Q */
+ /* 82 */ ED_INSERT, /* R */
+ /* 83 */ ED_INSERT, /* S */
+ /* 84 */ ED_INSERT, /* T */
+ /* 85 */ ED_INSERT, /* U */
+ /* 86 */ ED_INSERT, /* V */
+ /* 87 */ ED_INSERT, /* W */
+ /* 88 */ ED_INSERT, /* X */
+ /* 89 */ ED_INSERT, /* Y */
+ /* 90 */ ED_INSERT, /* Z */
+ /* 91 */ ED_INSERT, /* [ */
+ /* 92 */ ED_INSERT, /* \ */
+ /* 93 */ ED_INSERT, /* ] */
+ /* 94 */ ED_INSERT, /* ^ */
+ /* 95 */ ED_INSERT, /* _ */
+ /* 96 */ ED_INSERT, /* ` */
+ /* 97 */ ED_INSERT, /* a */
+ /* 98 */ ED_INSERT, /* b */
+ /* 99 */ ED_INSERT, /* c */
+ /* 100 */ ED_INSERT, /* d */
+ /* 101 */ ED_INSERT, /* e */
+ /* 102 */ ED_INSERT, /* f */
+ /* 103 */ ED_INSERT, /* g */
+ /* 104 */ ED_INSERT, /* h */
+ /* 105 */ ED_INSERT, /* i */
+ /* 106 */ ED_INSERT, /* j */
+ /* 107 */ ED_INSERT, /* k */
+ /* 108 */ ED_INSERT, /* l */
+ /* 109 */ ED_INSERT, /* m */
+ /* 110 */ ED_INSERT, /* n */
+ /* 111 */ ED_INSERT, /* o */
+ /* 112 */ ED_INSERT, /* p */
+ /* 113 */ ED_INSERT, /* q */
+ /* 114 */ ED_INSERT, /* r */
+ /* 115 */ ED_INSERT, /* s */
+ /* 116 */ ED_INSERT, /* t */
+ /* 117 */ ED_INSERT, /* u */
+ /* 118 */ ED_INSERT, /* v */
+ /* 119 */ ED_INSERT, /* w */
+ /* 120 */ ED_INSERT, /* x */
+ /* 121 */ ED_INSERT, /* y */
+ /* 122 */ ED_INSERT, /* z */
+ /* 123 */ ED_INSERT, /* { */
+ /* 124 */ ED_INSERT, /* | */
+ /* 125 */ ED_INSERT, /* } */
+ /* 126 */ ED_INSERT, /* ~ */
+ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */
+ /* 128 */ ED_UNASSIGNED, /* M-^@ */
+ /* 129 */ ED_UNASSIGNED, /* M-^A */
+ /* 130 */ ED_UNASSIGNED, /* M-^B */
+ /* 131 */ ED_UNASSIGNED, /* M-^C */
+ /* 132 */ ED_UNASSIGNED, /* M-^D */
+ /* 133 */ ED_UNASSIGNED, /* M-^E */
+ /* 134 */ ED_UNASSIGNED, /* M-^F */
+ /* 135 */ ED_UNASSIGNED, /* M-^G */
+ /* 136 */ ED_DELETE_PREV_WORD, /* M-^H */
+ /* 137 */ ED_UNASSIGNED, /* M-^I */
+ /* 138 */ ED_UNASSIGNED, /* M-^J */
+ /* 139 */ ED_UNASSIGNED, /* M-^K */
+ /* 140 */ ED_CLEAR_SCREEN, /* M-^L */
+ /* 141 */ ED_UNASSIGNED, /* M-^M */
+ /* 142 */ ED_UNASSIGNED, /* M-^N */
+ /* 143 */ ED_UNASSIGNED, /* M-^O */
+ /* 144 */ ED_UNASSIGNED, /* M-^P */
+ /* 145 */ ED_UNASSIGNED, /* M-^Q */
+ /* 146 */ ED_UNASSIGNED, /* M-^R */
+ /* 147 */ ED_UNASSIGNED, /* M-^S */
+ /* 148 */ ED_UNASSIGNED, /* M-^T */
+ /* 149 */ ED_UNASSIGNED, /* M-^U */
+ /* 150 */ ED_UNASSIGNED, /* M-^V */
+ /* 151 */ ED_UNASSIGNED, /* M-^W */
+ /* 152 */ ED_UNASSIGNED, /* M-^X */
+ /* 153 */ ED_UNASSIGNED, /* M-^Y */
+ /* 154 */ ED_UNASSIGNED, /* M-^Z */
+ /* 155 */ ED_UNASSIGNED, /* M-^[ */
+ /* 156 */ ED_UNASSIGNED, /* M-^\ */
+ /* 157 */ ED_UNASSIGNED, /* M-^] */
+ /* 158 */ ED_UNASSIGNED, /* M-^^ */
+ /* 159 */ EM_COPY_PREV_WORD, /* M-^_ */
+ /* 160 */ ED_UNASSIGNED, /* M-SPACE */
+ /* 161 */ ED_UNASSIGNED, /* M-! */
+ /* 162 */ ED_UNASSIGNED, /* M-" */
+ /* 163 */ ED_UNASSIGNED, /* M-# */
+ /* 164 */ ED_UNASSIGNED, /* M-$ */
+ /* 165 */ ED_UNASSIGNED, /* M-% */
+ /* 166 */ ED_UNASSIGNED, /* M-& */
+ /* 167 */ ED_UNASSIGNED, /* M-' */
+ /* 168 */ ED_UNASSIGNED, /* M-( */
+ /* 169 */ ED_UNASSIGNED, /* M-) */
+ /* 170 */ ED_UNASSIGNED, /* M-* */
+ /* 171 */ ED_UNASSIGNED, /* M-+ */
+ /* 172 */ ED_UNASSIGNED, /* M-, */
+ /* 173 */ ED_UNASSIGNED, /* M-- */
+ /* 174 */ ED_UNASSIGNED, /* M-. */
+ /* 175 */ ED_UNASSIGNED, /* M-/ */
+ /* 176 */ ED_ARGUMENT_DIGIT, /* M-0 */
+ /* 177 */ ED_ARGUMENT_DIGIT, /* M-1 */
+ /* 178 */ ED_ARGUMENT_DIGIT, /* M-2 */
+ /* 179 */ ED_ARGUMENT_DIGIT, /* M-3 */
+ /* 180 */ ED_ARGUMENT_DIGIT, /* M-4 */
+ /* 181 */ ED_ARGUMENT_DIGIT, /* M-5 */
+ /* 182 */ ED_ARGUMENT_DIGIT, /* M-6 */
+ /* 183 */ ED_ARGUMENT_DIGIT, /* M-7 */
+ /* 184 */ ED_ARGUMENT_DIGIT, /* M-8 */
+ /* 185 */ ED_ARGUMENT_DIGIT, /* M-9 */
+ /* 186 */ ED_UNASSIGNED, /* M-: */
+ /* 187 */ ED_UNASSIGNED, /* M-; */
+ /* 188 */ ED_UNASSIGNED, /* M-< */
+ /* 189 */ ED_UNASSIGNED, /* M-= */
+ /* 190 */ ED_UNASSIGNED, /* M-> */
+ /* 191 */ ED_UNASSIGNED, /* M-? */
+ /* 192 */ ED_UNASSIGNED, /* M-@ */
+ /* 193 */ ED_UNASSIGNED, /* M-A */
+ /* 194 */ ED_PREV_WORD, /* M-B */
+ /* 195 */ EM_CAPITOL_CASE, /* M-C */
+ /* 196 */ EM_DELETE_NEXT_WORD, /* M-D */
+ /* 197 */ ED_UNASSIGNED, /* M-E */
+ /* 198 */ EM_NEXT_WORD, /* M-F */
+ /* 199 */ ED_UNASSIGNED, /* M-G */
+ /* 200 */ ED_UNASSIGNED, /* M-H */
+ /* 201 */ ED_UNASSIGNED, /* M-I */
+ /* 202 */ ED_UNASSIGNED, /* M-J */
+ /* 203 */ ED_UNASSIGNED, /* M-K */
+ /* 204 */ EM_LOWER_CASE, /* M-L */
+ /* 205 */ ED_UNASSIGNED, /* M-M */
+ /* 206 */ ED_SEARCH_NEXT_HISTORY, /* M-N */
+ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */
+ /* 208 */ ED_SEARCH_PREV_HISTORY, /* M-P */
+ /* 209 */ ED_UNASSIGNED, /* M-Q */
+ /* 210 */ ED_UNASSIGNED, /* M-R */
+ /* 211 */ ED_UNASSIGNED, /* M-S */
+ /* 212 */ ED_UNASSIGNED, /* M-T */
+ /* 213 */ EM_UPPER_CASE, /* M-U */
+ /* 214 */ ED_UNASSIGNED, /* M-V */
+ /* 215 */ EM_COPY_REGION, /* M-W */
+ /* 216 */ ED_COMMAND, /* M-X */
+ /* 217 */ ED_UNASSIGNED, /* M-Y */
+ /* 218 */ ED_UNASSIGNED, /* M-Z */
+ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */
+ /* 220 */ ED_UNASSIGNED, /* M-\ */
+ /* 221 */ ED_UNASSIGNED, /* M-] */
+ /* 222 */ ED_UNASSIGNED, /* M-^ */
+ /* 223 */ ED_UNASSIGNED, /* M-_ */
+ /* 223 */ ED_UNASSIGNED, /* M-` */
+ /* 224 */ ED_UNASSIGNED, /* M-a */
+ /* 225 */ ED_PREV_WORD, /* M-b */
+ /* 226 */ EM_CAPITOL_CASE, /* M-c */
+ /* 227 */ EM_DELETE_NEXT_WORD, /* M-d */
+ /* 228 */ ED_UNASSIGNED, /* M-e */
+ /* 229 */ EM_NEXT_WORD, /* M-f */
+ /* 230 */ ED_UNASSIGNED, /* M-g */
+ /* 231 */ ED_UNASSIGNED, /* M-h */
+ /* 232 */ ED_UNASSIGNED, /* M-i */
+ /* 233 */ ED_UNASSIGNED, /* M-j */
+ /* 234 */ ED_UNASSIGNED, /* M-k */
+ /* 235 */ EM_LOWER_CASE, /* M-l */
+ /* 236 */ ED_UNASSIGNED, /* M-m */
+ /* 237 */ ED_SEARCH_NEXT_HISTORY, /* M-n */
+ /* 238 */ ED_UNASSIGNED, /* M-o */
+ /* 239 */ ED_SEARCH_PREV_HISTORY, /* M-p */
+ /* 240 */ ED_UNASSIGNED, /* M-q */
+ /* 241 */ ED_UNASSIGNED, /* M-r */
+ /* 242 */ ED_UNASSIGNED, /* M-s */
+ /* 243 */ ED_UNASSIGNED, /* M-t */
+ /* 244 */ EM_UPPER_CASE, /* M-u */
+ /* 245 */ ED_UNASSIGNED, /* M-v */
+ /* 246 */ EM_COPY_REGION, /* M-w */
+ /* 247 */ ED_COMMAND, /* M-x */
+ /* 248 */ ED_UNASSIGNED, /* M-y */
+ /* 249 */ ED_UNASSIGNED, /* M-z */
+ /* 250 */ ED_UNASSIGNED, /* M-{ */
+ /* 251 */ ED_UNASSIGNED, /* M-| */
+ /* 252 */ ED_UNASSIGNED, /* M-} */
+ /* 253 */ ED_UNASSIGNED, /* M-~ */
+ /* 254 */ ED_DELETE_PREV_WORD /* M-^? */
+ /* 255 */
+};
+
+
+/*
+ * keymap table for vi. Each index into above tbl; should be
+ * N_KEYS entries long. Vi mode uses a sticky-extend to do command mode:
+ * insert mode characters are in the normal keymap, and command mode
+ * in the extended keymap.
+ */
+private const el_action_t el_map_vi_insert[] = {
+#ifdef KSHVI
+ /* 0 */ ED_UNASSIGNED, /* ^@ */
+ /* 1 */ ED_INSERT, /* ^A */
+ /* 2 */ ED_INSERT, /* ^B */
+ /* 3 */ ED_INSERT, /* ^C */
+ /* 4 */ VI_LIST_OR_EOF, /* ^D */
+ /* 5 */ ED_INSERT, /* ^E */
+ /* 6 */ ED_INSERT, /* ^F */
+ /* 7 */ ED_INSERT, /* ^G */
+ /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */
+ /* 9 */ ED_INSERT, /* ^I */ /* Tab Key */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_INSERT, /* ^K */
+ /* 12 */ ED_INSERT, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_INSERT, /* ^N */
+ /* 15 */ ED_INSERT, /* ^O */
+ /* 16 */ ED_INSERT, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_INSERT, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_INSERT, /* ^T */
+ /* 21 */ VI_KILL_LINE_PREV, /* ^U */
+ /* 22 */ ED_QUOTED_INSERT, /* ^V */
+ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */
+ /* ED_DELETE_PREV_WORD: Only until strt edit pos */
+ /* 24 */ ED_INSERT, /* ^X */
+ /* 25 */ ED_INSERT, /* ^Y */
+ /* 26 */ ED_INSERT, /* ^Z */
+ /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* [ Esc ] key */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_INSERT, /* ^] */
+ /* 30 */ ED_INSERT, /* ^^ */
+ /* 31 */ ED_INSERT, /* ^_ */
+#else /* !KSHVI */
+ /*
+ * NOTE: These mappings do NOT Correspond well
+ * to the KSH VI editing assignments.
+ * On the other and they are convenient and
+ * many people have have gotten used to them.
+ */
+ /* 0 */ ED_UNASSIGNED, /* ^@ */
+ /* 1 */ ED_MOVE_TO_BEG, /* ^A */
+ /* 2 */ ED_PREV_CHAR, /* ^B */
+ /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 4 */ VI_LIST_OR_EOF, /* ^D */
+ /* 5 */ ED_MOVE_TO_END, /* ^E */
+ /* 6 */ ED_NEXT_CHAR, /* ^F */
+ /* 7 */ ED_UNASSIGNED, /* ^G */
+ /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */
+ /* 9 */ ED_UNASSIGNED, /* ^I */ /* Tab Key */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_KILL_LINE, /* ^K */
+ /* 12 */ ED_CLEAR_SCREEN, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_NEXT_HISTORY, /* ^N */
+ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 16 */ ED_PREV_HISTORY, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_REDISPLAY, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */
+ /* 21 */ VI_KILL_LINE_PREV, /* ^U */
+ /* 22 */ ED_QUOTED_INSERT, /* ^V */
+ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */
+ /* 24 */ ED_UNASSIGNED, /* ^X */
+ /* 25 */ ED_TTY_DSUSP, /* ^Y */
+ /* 26 */ ED_TTY_SIGTSTP, /* ^Z */
+ /* 27 */ VI_COMMAND_MODE, /* ^[ */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_UNASSIGNED, /* ^] */
+ /* 30 */ ED_UNASSIGNED, /* ^^ */
+ /* 31 */ ED_UNASSIGNED, /* ^_ */
+#endif /* KSHVI */
+ /* 32 */ ED_INSERT, /* SPACE */
+ /* 33 */ ED_INSERT, /* ! */
+ /* 34 */ ED_INSERT, /* " */
+ /* 35 */ ED_INSERT, /* # */
+ /* 36 */ ED_INSERT, /* $ */
+ /* 37 */ ED_INSERT, /* % */
+ /* 38 */ ED_INSERT, /* & */
+ /* 39 */ ED_INSERT, /* ' */
+ /* 40 */ ED_INSERT, /* ( */
+ /* 41 */ ED_INSERT, /* ) */
+ /* 42 */ ED_INSERT, /* * */
+ /* 43 */ ED_INSERT, /* + */
+ /* 44 */ ED_INSERT, /* , */
+ /* 45 */ ED_INSERT, /* - */
+ /* 46 */ ED_INSERT, /* . */
+ /* 47 */ ED_INSERT, /* / */
+ /* 48 */ ED_INSERT, /* 0 */
+ /* 49 */ ED_INSERT, /* 1 */
+ /* 50 */ ED_INSERT, /* 2 */
+ /* 51 */ ED_INSERT, /* 3 */
+ /* 52 */ ED_INSERT, /* 4 */
+ /* 53 */ ED_INSERT, /* 5 */
+ /* 54 */ ED_INSERT, /* 6 */
+ /* 55 */ ED_INSERT, /* 7 */
+ /* 56 */ ED_INSERT, /* 8 */
+ /* 57 */ ED_INSERT, /* 9 */
+ /* 58 */ ED_INSERT, /* : */
+ /* 59 */ ED_INSERT, /* ; */
+ /* 60 */ ED_INSERT, /* < */
+ /* 61 */ ED_INSERT, /* = */
+ /* 62 */ ED_INSERT, /* > */
+ /* 63 */ ED_INSERT, /* ? */
+ /* 64 */ ED_INSERT, /* @ */
+ /* 65 */ ED_INSERT, /* A */
+ /* 66 */ ED_INSERT, /* B */
+ /* 67 */ ED_INSERT, /* C */
+ /* 68 */ ED_INSERT, /* D */
+ /* 69 */ ED_INSERT, /* E */
+ /* 70 */ ED_INSERT, /* F */
+ /* 71 */ ED_INSERT, /* G */
+ /* 72 */ ED_INSERT, /* H */
+ /* 73 */ ED_INSERT, /* I */
+ /* 74 */ ED_INSERT, /* J */
+ /* 75 */ ED_INSERT, /* K */
+ /* 76 */ ED_INSERT, /* L */
+ /* 77 */ ED_INSERT, /* M */
+ /* 78 */ ED_INSERT, /* N */
+ /* 79 */ ED_INSERT, /* O */
+ /* 80 */ ED_INSERT, /* P */
+ /* 81 */ ED_INSERT, /* Q */
+ /* 82 */ ED_INSERT, /* R */
+ /* 83 */ ED_INSERT, /* S */
+ /* 84 */ ED_INSERT, /* T */
+ /* 85 */ ED_INSERT, /* U */
+ /* 86 */ ED_INSERT, /* V */
+ /* 87 */ ED_INSERT, /* W */
+ /* 88 */ ED_INSERT, /* X */
+ /* 89 */ ED_INSERT, /* Y */
+ /* 90 */ ED_INSERT, /* Z */
+ /* 91 */ ED_INSERT, /* [ */
+ /* 92 */ ED_INSERT, /* \ */
+ /* 93 */ ED_INSERT, /* ] */
+ /* 94 */ ED_INSERT, /* ^ */
+ /* 95 */ ED_INSERT, /* _ */
+ /* 96 */ ED_INSERT, /* ` */
+ /* 97 */ ED_INSERT, /* a */
+ /* 98 */ ED_INSERT, /* b */
+ /* 99 */ ED_INSERT, /* c */
+ /* 100 */ ED_INSERT, /* d */
+ /* 101 */ ED_INSERT, /* e */
+ /* 102 */ ED_INSERT, /* f */
+ /* 103 */ ED_INSERT, /* g */
+ /* 104 */ ED_INSERT, /* h */
+ /* 105 */ ED_INSERT, /* i */
+ /* 106 */ ED_INSERT, /* j */
+ /* 107 */ ED_INSERT, /* k */
+ /* 108 */ ED_INSERT, /* l */
+ /* 109 */ ED_INSERT, /* m */
+ /* 110 */ ED_INSERT, /* n */
+ /* 111 */ ED_INSERT, /* o */
+ /* 112 */ ED_INSERT, /* p */
+ /* 113 */ ED_INSERT, /* q */
+ /* 114 */ ED_INSERT, /* r */
+ /* 115 */ ED_INSERT, /* s */
+ /* 116 */ ED_INSERT, /* t */
+ /* 117 */ ED_INSERT, /* u */
+ /* 118 */ ED_INSERT, /* v */
+ /* 119 */ ED_INSERT, /* w */
+ /* 120 */ ED_INSERT, /* x */
+ /* 121 */ ED_INSERT, /* y */
+ /* 122 */ ED_INSERT, /* z */
+ /* 123 */ ED_INSERT, /* { */
+ /* 124 */ ED_INSERT, /* | */
+ /* 125 */ ED_INSERT, /* } */
+ /* 126 */ ED_INSERT, /* ~ */
+ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */
+ /* 128 */ ED_UNASSIGNED, /* M-^@ */
+ /* 129 */ ED_UNASSIGNED, /* M-^A */
+ /* 130 */ ED_UNASSIGNED, /* M-^B */
+ /* 131 */ ED_UNASSIGNED, /* M-^C */
+ /* 132 */ ED_UNASSIGNED, /* M-^D */
+ /* 133 */ ED_UNASSIGNED, /* M-^E */
+ /* 134 */ ED_UNASSIGNED, /* M-^F */
+ /* 135 */ ED_UNASSIGNED, /* M-^G */
+ /* 136 */ ED_UNASSIGNED, /* M-^H */
+ /* 137 */ ED_UNASSIGNED, /* M-^I */
+ /* 138 */ ED_UNASSIGNED, /* M-^J */
+ /* 139 */ ED_UNASSIGNED, /* M-^K */
+ /* 140 */ ED_UNASSIGNED, /* M-^L */
+ /* 141 */ ED_UNASSIGNED, /* M-^M */
+ /* 142 */ ED_UNASSIGNED, /* M-^N */
+ /* 143 */ ED_UNASSIGNED, /* M-^O */
+ /* 144 */ ED_UNASSIGNED, /* M-^P */
+ /* 145 */ ED_UNASSIGNED, /* M-^Q */
+ /* 146 */ ED_UNASSIGNED, /* M-^R */
+ /* 147 */ ED_UNASSIGNED, /* M-^S */
+ /* 148 */ ED_UNASSIGNED, /* M-^T */
+ /* 149 */ ED_UNASSIGNED, /* M-^U */
+ /* 150 */ ED_UNASSIGNED, /* M-^V */
+ /* 151 */ ED_UNASSIGNED, /* M-^W */
+ /* 152 */ ED_UNASSIGNED, /* M-^X */
+ /* 153 */ ED_UNASSIGNED, /* M-^Y */
+ /* 154 */ ED_UNASSIGNED, /* M-^Z */
+ /* 155 */ ED_UNASSIGNED, /* M-^[ */
+ /* 156 */ ED_UNASSIGNED, /* M-^\ */
+ /* 157 */ ED_UNASSIGNED, /* M-^] */
+ /* 158 */ ED_UNASSIGNED, /* M-^^ */
+ /* 159 */ ED_UNASSIGNED, /* M-^_ */
+ /* 160 */ ED_UNASSIGNED, /* M-SPACE */
+ /* 161 */ ED_UNASSIGNED, /* M-! */
+ /* 162 */ ED_UNASSIGNED, /* M-" */
+ /* 163 */ ED_UNASSIGNED, /* M-# */
+ /* 164 */ ED_UNASSIGNED, /* M-$ */
+ /* 165 */ ED_UNASSIGNED, /* M-% */
+ /* 166 */ ED_UNASSIGNED, /* M-& */
+ /* 167 */ ED_UNASSIGNED, /* M-' */
+ /* 168 */ ED_UNASSIGNED, /* M-( */
+ /* 169 */ ED_UNASSIGNED, /* M-) */
+ /* 170 */ ED_UNASSIGNED, /* M-* */
+ /* 171 */ ED_UNASSIGNED, /* M-+ */
+ /* 172 */ ED_UNASSIGNED, /* M-, */
+ /* 173 */ ED_UNASSIGNED, /* M-- */
+ /* 174 */ ED_UNASSIGNED, /* M-. */
+ /* 175 */ ED_UNASSIGNED, /* M-/ */
+ /* 176 */ ED_UNASSIGNED, /* M-0 */
+ /* 177 */ ED_UNASSIGNED, /* M-1 */
+ /* 178 */ ED_UNASSIGNED, /* M-2 */
+ /* 179 */ ED_UNASSIGNED, /* M-3 */
+ /* 180 */ ED_UNASSIGNED, /* M-4 */
+ /* 181 */ ED_UNASSIGNED, /* M-5 */
+ /* 182 */ ED_UNASSIGNED, /* M-6 */
+ /* 183 */ ED_UNASSIGNED, /* M-7 */
+ /* 184 */ ED_UNASSIGNED, /* M-8 */
+ /* 185 */ ED_UNASSIGNED, /* M-9 */
+ /* 186 */ ED_UNASSIGNED, /* M-: */
+ /* 187 */ ED_UNASSIGNED, /* M-; */
+ /* 188 */ ED_UNASSIGNED, /* M-< */
+ /* 189 */ ED_UNASSIGNED, /* M-= */
+ /* 190 */ ED_UNASSIGNED, /* M-> */
+ /* 191 */ ED_UNASSIGNED, /* M-? */
+ /* 192 */ ED_UNASSIGNED, /* M-@ */
+ /* 193 */ ED_UNASSIGNED, /* M-A */
+ /* 194 */ ED_UNASSIGNED, /* M-B */
+ /* 195 */ ED_UNASSIGNED, /* M-C */
+ /* 196 */ ED_UNASSIGNED, /* M-D */
+ /* 197 */ ED_UNASSIGNED, /* M-E */
+ /* 198 */ ED_UNASSIGNED, /* M-F */
+ /* 199 */ ED_UNASSIGNED, /* M-G */
+ /* 200 */ ED_UNASSIGNED, /* M-H */
+ /* 201 */ ED_UNASSIGNED, /* M-I */
+ /* 202 */ ED_UNASSIGNED, /* M-J */
+ /* 203 */ ED_UNASSIGNED, /* M-K */
+ /* 204 */ ED_UNASSIGNED, /* M-L */
+ /* 205 */ ED_UNASSIGNED, /* M-M */
+ /* 206 */ ED_UNASSIGNED, /* M-N */
+ /* 207 */ ED_UNASSIGNED, /* M-O */
+ /* 208 */ ED_UNASSIGNED, /* M-P */
+ /* 209 */ ED_UNASSIGNED, /* M-Q */
+ /* 210 */ ED_UNASSIGNED, /* M-R */
+ /* 211 */ ED_UNASSIGNED, /* M-S */
+ /* 212 */ ED_UNASSIGNED, /* M-T */
+ /* 213 */ ED_UNASSIGNED, /* M-U */
+ /* 214 */ ED_UNASSIGNED, /* M-V */
+ /* 215 */ ED_UNASSIGNED, /* M-W */
+ /* 216 */ ED_UNASSIGNED, /* M-X */
+ /* 217 */ ED_UNASSIGNED, /* M-Y */
+ /* 218 */ ED_UNASSIGNED, /* M-Z */
+ /* 219 */ ED_UNASSIGNED, /* M-[ */
+ /* 220 */ ED_UNASSIGNED, /* M-\ */
+ /* 221 */ ED_UNASSIGNED, /* M-] */
+ /* 222 */ ED_UNASSIGNED, /* M-^ */
+ /* 223 */ ED_UNASSIGNED, /* M-_ */
+ /* 224 */ ED_UNASSIGNED, /* M-` */
+ /* 225 */ ED_UNASSIGNED, /* M-a */
+ /* 226 */ ED_UNASSIGNED, /* M-b */
+ /* 227 */ ED_UNASSIGNED, /* M-c */
+ /* 228 */ ED_UNASSIGNED, /* M-d */
+ /* 229 */ ED_UNASSIGNED, /* M-e */
+ /* 230 */ ED_UNASSIGNED, /* M-f */
+ /* 231 */ ED_UNASSIGNED, /* M-g */
+ /* 232 */ ED_UNASSIGNED, /* M-h */
+ /* 233 */ ED_UNASSIGNED, /* M-i */
+ /* 234 */ ED_UNASSIGNED, /* M-j */
+ /* 235 */ ED_UNASSIGNED, /* M-k */
+ /* 236 */ ED_UNASSIGNED, /* M-l */
+ /* 237 */ ED_UNASSIGNED, /* M-m */
+ /* 238 */ ED_UNASSIGNED, /* M-n */
+ /* 239 */ ED_UNASSIGNED, /* M-o */
+ /* 240 */ ED_UNASSIGNED, /* M-p */
+ /* 241 */ ED_UNASSIGNED, /* M-q */
+ /* 242 */ ED_UNASSIGNED, /* M-r */
+ /* 243 */ ED_UNASSIGNED, /* M-s */
+ /* 244 */ ED_UNASSIGNED, /* M-t */
+ /* 245 */ ED_UNASSIGNED, /* M-u */
+ /* 246 */ ED_UNASSIGNED, /* M-v */
+ /* 247 */ ED_UNASSIGNED, /* M-w */
+ /* 248 */ ED_UNASSIGNED, /* M-x */
+ /* 249 */ ED_UNASSIGNED, /* M-y */
+ /* 250 */ ED_UNASSIGNED, /* M-z */
+ /* 251 */ ED_UNASSIGNED, /* M-{ */
+ /* 252 */ ED_UNASSIGNED, /* M-| */
+ /* 253 */ ED_UNASSIGNED, /* M-} */
+ /* 254 */ ED_UNASSIGNED, /* M-~ */
+ /* 255 */ ED_UNASSIGNED /* M-^? */
+};
+
+private const el_action_t el_map_vi_command[] = {
+ /* 0 */ ED_UNASSIGNED, /* ^@ */
+ /* 1 */ ED_MOVE_TO_BEG, /* ^A */
+ /* 2 */ ED_UNASSIGNED, /* ^B */
+ /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 4 */ ED_UNASSIGNED, /* ^D */
+ /* 5 */ ED_MOVE_TO_END, /* ^E */
+ /* 6 */ ED_UNASSIGNED, /* ^F */
+ /* 7 */ ED_UNASSIGNED, /* ^G */
+ /* 8 */ ED_PREV_CHAR, /* ^H */
+ /* 9 */ ED_UNASSIGNED, /* ^I */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_KILL_LINE, /* ^K */
+ /* 12 */ ED_CLEAR_SCREEN, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_NEXT_HISTORY, /* ^N */
+ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 16 */ ED_PREV_HISTORY, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_REDISPLAY, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_UNASSIGNED, /* ^T */
+ /* 21 */ VI_KILL_LINE_PREV, /* ^U */
+ /* 22 */ ED_UNASSIGNED, /* ^V */
+ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */
+ /* 24 */ ED_UNASSIGNED, /* ^X */
+ /* 25 */ ED_UNASSIGNED, /* ^Y */
+ /* 26 */ ED_UNASSIGNED, /* ^Z */
+ /* 27 */ EM_META_NEXT, /* ^[ */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_UNASSIGNED, /* ^] */
+ /* 30 */ ED_UNASSIGNED, /* ^^ */
+ /* 31 */ ED_UNASSIGNED, /* ^_ */
+ /* 32 */ ED_NEXT_CHAR, /* SPACE */
+ /* 33 */ ED_UNASSIGNED, /* ! */
+ /* 34 */ ED_UNASSIGNED, /* " */
+ /* 35 */ ED_UNASSIGNED, /* # */
+ /* 36 */ ED_MOVE_TO_END, /* $ */
+ /* 37 */ ED_UNASSIGNED, /* % */
+ /* 38 */ ED_UNASSIGNED, /* & */
+ /* 39 */ ED_UNASSIGNED, /* ' */
+ /* 40 */ ED_UNASSIGNED, /* ( */
+ /* 41 */ ED_UNASSIGNED, /* ) */
+ /* 42 */ ED_UNASSIGNED, /* * */
+ /* 43 */ ED_NEXT_HISTORY, /* + */
+ /* 44 */ VI_REPEAT_PREV_CHAR, /* , */
+ /* 45 */ ED_PREV_HISTORY, /* - */
+ /* 46 */ ED_UNASSIGNED, /* . */
+ /* 47 */ VI_SEARCH_PREV, /* / */
+ /* 48 */ VI_ZERO, /* 0 */
+ /* 49 */ ED_ARGUMENT_DIGIT, /* 1 */
+ /* 50 */ ED_ARGUMENT_DIGIT, /* 2 */
+ /* 51 */ ED_ARGUMENT_DIGIT, /* 3 */
+ /* 52 */ ED_ARGUMENT_DIGIT, /* 4 */
+ /* 53 */ ED_ARGUMENT_DIGIT, /* 5 */
+ /* 54 */ ED_ARGUMENT_DIGIT, /* 6 */
+ /* 55 */ ED_ARGUMENT_DIGIT, /* 7 */
+ /* 56 */ ED_ARGUMENT_DIGIT, /* 8 */
+ /* 57 */ ED_ARGUMENT_DIGIT, /* 9 */
+ /* 58 */ ED_COMMAND, /* : */
+ /* 59 */ VI_REPEAT_NEXT_CHAR, /* ; */
+ /* 60 */ ED_UNASSIGNED, /* < */
+ /* 61 */ ED_UNASSIGNED, /* = */
+ /* 62 */ ED_UNASSIGNED, /* > */
+ /* 63 */ VI_SEARCH_NEXT, /* ? */
+ /* 64 */ ED_UNASSIGNED, /* @ */
+ /* 65 */ VI_ADD_AT_EOL, /* A */
+ /* 66 */ VI_PREV_SPACE_WORD, /* B */
+ /* 67 */ VI_CHANGE_TO_EOL, /* C */
+ /* 68 */ ED_KILL_LINE, /* D */
+ /* 69 */ VI_TO_END_WORD, /* E */
+ /* 70 */ VI_PREV_CHAR, /* F */
+ /* 71 */ ED_UNASSIGNED, /* G */
+ /* 72 */ ED_UNASSIGNED, /* H */
+ /* 73 */ VI_INSERT_AT_BOL, /* I */
+ /* 74 */ ED_SEARCH_NEXT_HISTORY, /* J */
+ /* 75 */ ED_SEARCH_PREV_HISTORY, /* K */
+ /* 76 */ ED_UNASSIGNED, /* L */
+ /* 77 */ ED_UNASSIGNED, /* M */
+ /* 78 */ VI_REPEAT_SEARCH_PREV, /* N */
+ /* 79 */ ED_SEQUENCE_LEAD_IN, /* O */
+ /* 80 */ VI_PASTE_PREV, /* P */
+ /* 81 */ ED_UNASSIGNED, /* Q */
+ /* 82 */ VI_REPLACE_MODE, /* R */
+ /* 83 */ VI_SUBSTITUTE_LINE, /* S */
+ /* 84 */ VI_TO_PREV_CHAR, /* T */
+ /* 85 */ ED_UNASSIGNED, /* U */
+ /* 86 */ ED_UNASSIGNED, /* V */
+ /* 87 */ VI_NEXT_SPACE_WORD, /* W */
+ /* 88 */ ED_DELETE_PREV_CHAR, /* X */
+ /* 89 */ ED_UNASSIGNED, /* Y */
+ /* 90 */ ED_UNASSIGNED, /* Z */
+ /* 91 */ ED_SEQUENCE_LEAD_IN, /* [ */
+ /* 92 */ ED_UNASSIGNED, /* \ */
+ /* 93 */ ED_UNASSIGNED, /* ] */
+ /* 94 */ ED_MOVE_TO_BEG, /* ^ */
+ /* 95 */ ED_UNASSIGNED, /* _ */
+ /* 96 */ ED_UNASSIGNED, /* ` */
+ /* 97 */ VI_ADD, /* a */
+ /* 98 */ VI_PREV_WORD, /* b */
+ /* 99 */ VI_CHANGE_META, /* c */
+ /* 100 */ VI_DELETE_META, /* d */
+ /* 101 */ VI_END_WORD, /* e */
+ /* 102 */ VI_NEXT_CHAR, /* f */
+ /* 103 */ ED_UNASSIGNED, /* g */
+ /* 104 */ ED_PREV_CHAR, /* h */
+ /* 105 */ VI_INSERT, /* i */
+ /* 106 */ ED_NEXT_HISTORY, /* j */
+ /* 107 */ ED_PREV_HISTORY, /* k */
+ /* 108 */ ED_NEXT_CHAR, /* l */
+ /* 109 */ ED_UNASSIGNED, /* m */
+ /* 110 */ VI_REPEAT_SEARCH_NEXT, /* n */
+ /* 111 */ ED_UNASSIGNED, /* o */
+ /* 112 */ VI_PASTE_NEXT, /* p */
+ /* 113 */ ED_UNASSIGNED, /* q */
+ /* 114 */ VI_REPLACE_CHAR, /* r */
+ /* 115 */ VI_SUBSTITUTE_CHAR, /* s */
+ /* 116 */ VI_TO_NEXT_CHAR, /* t */
+ /* 117 */ VI_UNDO, /* u */
+ /* 118 */ ED_UNASSIGNED, /* v */
+ /* 119 */ VI_NEXT_WORD, /* w */
+ /* 120 */ ED_DELETE_NEXT_CHAR, /* x */
+ /* 121 */ ED_UNASSIGNED, /* y */
+ /* 122 */ ED_UNASSIGNED, /* z */
+ /* 123 */ ED_UNASSIGNED, /* { */
+ /* 124 */ ED_UNASSIGNED, /* | */
+ /* 125 */ ED_UNASSIGNED, /* } */
+ /* 126 */ VI_CHANGE_CASE, /* ~ */
+ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */
+ /* 128 */ ED_UNASSIGNED, /* M-^@ */
+ /* 129 */ ED_UNASSIGNED, /* M-^A */
+ /* 130 */ ED_UNASSIGNED, /* M-^B */
+ /* 131 */ ED_UNASSIGNED, /* M-^C */
+ /* 132 */ ED_UNASSIGNED, /* M-^D */
+ /* 133 */ ED_UNASSIGNED, /* M-^E */
+ /* 134 */ ED_UNASSIGNED, /* M-^F */
+ /* 135 */ ED_UNASSIGNED, /* M-^G */
+ /* 136 */ ED_UNASSIGNED, /* M-^H */
+ /* 137 */ ED_UNASSIGNED, /* M-^I */
+ /* 138 */ ED_UNASSIGNED, /* M-^J */
+ /* 139 */ ED_UNASSIGNED, /* M-^K */
+ /* 140 */ ED_UNASSIGNED, /* M-^L */
+ /* 141 */ ED_UNASSIGNED, /* M-^M */
+ /* 142 */ ED_UNASSIGNED, /* M-^N */
+ /* 143 */ ED_UNASSIGNED, /* M-^O */
+ /* 144 */ ED_UNASSIGNED, /* M-^P */
+ /* 145 */ ED_UNASSIGNED, /* M-^Q */
+ /* 146 */ ED_UNASSIGNED, /* M-^R */
+ /* 147 */ ED_UNASSIGNED, /* M-^S */
+ /* 148 */ ED_UNASSIGNED, /* M-^T */
+ /* 149 */ ED_UNASSIGNED, /* M-^U */
+ /* 150 */ ED_UNASSIGNED, /* M-^V */
+ /* 151 */ ED_UNASSIGNED, /* M-^W */
+ /* 152 */ ED_UNASSIGNED, /* M-^X */
+ /* 153 */ ED_UNASSIGNED, /* M-^Y */
+ /* 154 */ ED_UNASSIGNED, /* M-^Z */
+ /* 155 */ ED_UNASSIGNED, /* M-^[ */
+ /* 156 */ ED_UNASSIGNED, /* M-^\ */
+ /* 157 */ ED_UNASSIGNED, /* M-^] */
+ /* 158 */ ED_UNASSIGNED, /* M-^^ */
+ /* 159 */ ED_UNASSIGNED, /* M-^_ */
+ /* 160 */ ED_UNASSIGNED, /* M-SPACE */
+ /* 161 */ ED_UNASSIGNED, /* M-! */
+ /* 162 */ ED_UNASSIGNED, /* M-" */
+ /* 163 */ ED_UNASSIGNED, /* M-# */
+ /* 164 */ ED_UNASSIGNED, /* M-$ */
+ /* 165 */ ED_UNASSIGNED, /* M-% */
+ /* 166 */ ED_UNASSIGNED, /* M-& */
+ /* 167 */ ED_UNASSIGNED, /* M-' */
+ /* 168 */ ED_UNASSIGNED, /* M-( */
+ /* 169 */ ED_UNASSIGNED, /* M-) */
+ /* 170 */ ED_UNASSIGNED, /* M-* */
+ /* 171 */ ED_UNASSIGNED, /* M-+ */
+ /* 172 */ ED_UNASSIGNED, /* M-, */
+ /* 173 */ ED_UNASSIGNED, /* M-- */
+ /* 174 */ ED_UNASSIGNED, /* M-. */
+ /* 175 */ ED_UNASSIGNED, /* M-/ */
+ /* 176 */ ED_UNASSIGNED, /* M-0 */
+ /* 177 */ ED_UNASSIGNED, /* M-1 */
+ /* 178 */ ED_UNASSIGNED, /* M-2 */
+ /* 179 */ ED_UNASSIGNED, /* M-3 */
+ /* 180 */ ED_UNASSIGNED, /* M-4 */
+ /* 181 */ ED_UNASSIGNED, /* M-5 */
+ /* 182 */ ED_UNASSIGNED, /* M-6 */
+ /* 183 */ ED_UNASSIGNED, /* M-7 */
+ /* 184 */ ED_UNASSIGNED, /* M-8 */
+ /* 185 */ ED_UNASSIGNED, /* M-9 */
+ /* 186 */ ED_UNASSIGNED, /* M-: */
+ /* 187 */ ED_UNASSIGNED, /* M-; */
+ /* 188 */ ED_UNASSIGNED, /* M-< */
+ /* 189 */ ED_UNASSIGNED, /* M-= */
+ /* 190 */ ED_UNASSIGNED, /* M-> */
+ /* 191 */ ED_UNASSIGNED, /* M-? */
+ /* 192 */ ED_UNASSIGNED, /* M-@ */
+ /* 193 */ ED_UNASSIGNED, /* M-A */
+ /* 194 */ ED_UNASSIGNED, /* M-B */
+ /* 195 */ ED_UNASSIGNED, /* M-C */
+ /* 196 */ ED_UNASSIGNED, /* M-D */
+ /* 197 */ ED_UNASSIGNED, /* M-E */
+ /* 198 */ ED_UNASSIGNED, /* M-F */
+ /* 199 */ ED_UNASSIGNED, /* M-G */
+ /* 200 */ ED_UNASSIGNED, /* M-H */
+ /* 201 */ ED_UNASSIGNED, /* M-I */
+ /* 202 */ ED_UNASSIGNED, /* M-J */
+ /* 203 */ ED_UNASSIGNED, /* M-K */
+ /* 204 */ ED_UNASSIGNED, /* M-L */
+ /* 205 */ ED_UNASSIGNED, /* M-M */
+ /* 206 */ ED_UNASSIGNED, /* M-N */
+ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */
+ /* 208 */ ED_UNASSIGNED, /* M-P */
+ /* 209 */ ED_UNASSIGNED, /* M-Q */
+ /* 210 */ ED_UNASSIGNED, /* M-R */
+ /* 211 */ ED_UNASSIGNED, /* M-S */
+ /* 212 */ ED_UNASSIGNED, /* M-T */
+ /* 213 */ ED_UNASSIGNED, /* M-U */
+ /* 214 */ ED_UNASSIGNED, /* M-V */
+ /* 215 */ ED_UNASSIGNED, /* M-W */
+ /* 216 */ ED_UNASSIGNED, /* M-X */
+ /* 217 */ ED_UNASSIGNED, /* M-Y */
+ /* 218 */ ED_UNASSIGNED, /* M-Z */
+ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */
+ /* 220 */ ED_UNASSIGNED, /* M-\ */
+ /* 221 */ ED_UNASSIGNED, /* M-] */
+ /* 222 */ ED_UNASSIGNED, /* M-^ */
+ /* 223 */ ED_UNASSIGNED, /* M-_ */
+ /* 224 */ ED_UNASSIGNED, /* M-` */
+ /* 225 */ ED_UNASSIGNED, /* M-a */
+ /* 226 */ ED_UNASSIGNED, /* M-b */
+ /* 227 */ ED_UNASSIGNED, /* M-c */
+ /* 228 */ ED_UNASSIGNED, /* M-d */
+ /* 229 */ ED_UNASSIGNED, /* M-e */
+ /* 230 */ ED_UNASSIGNED, /* M-f */
+ /* 231 */ ED_UNASSIGNED, /* M-g */
+ /* 232 */ ED_UNASSIGNED, /* M-h */
+ /* 233 */ ED_UNASSIGNED, /* M-i */
+ /* 234 */ ED_UNASSIGNED, /* M-j */
+ /* 235 */ ED_UNASSIGNED, /* M-k */
+ /* 236 */ ED_UNASSIGNED, /* M-l */
+ /* 237 */ ED_UNASSIGNED, /* M-m */
+ /* 238 */ ED_UNASSIGNED, /* M-n */
+ /* 239 */ ED_UNASSIGNED, /* M-o */
+ /* 240 */ ED_UNASSIGNED, /* M-p */
+ /* 241 */ ED_UNASSIGNED, /* M-q */
+ /* 242 */ ED_UNASSIGNED, /* M-r */
+ /* 243 */ ED_UNASSIGNED, /* M-s */
+ /* 244 */ ED_UNASSIGNED, /* M-t */
+ /* 245 */ ED_UNASSIGNED, /* M-u */
+ /* 246 */ ED_UNASSIGNED, /* M-v */
+ /* 247 */ ED_UNASSIGNED, /* M-w */
+ /* 248 */ ED_UNASSIGNED, /* M-x */
+ /* 249 */ ED_UNASSIGNED, /* M-y */
+ /* 250 */ ED_UNASSIGNED, /* M-z */
+ /* 251 */ ED_UNASSIGNED, /* M-{ */
+ /* 252 */ ED_UNASSIGNED, /* M-| */
+ /* 253 */ ED_UNASSIGNED, /* M-} */
+ /* 254 */ ED_UNASSIGNED, /* M-~ */
+ /* 255 */ ED_UNASSIGNED /* M-^? */
+};
+
+
+/* map_init():
+ * Initialize and allocate the maps
+ */
+protected int
+map_init(EditLine *el)
+{
+
+ /*
+ * Make sure those are correct before starting.
+ */
+#ifdef MAP_DEBUG
+ if (sizeof(el_map_emacs) != N_KEYS * sizeof(el_action_t))
+ EL_ABORT((el->errfile, "Emacs map incorrect\n"));
+ if (sizeof(el_map_vi_command) != N_KEYS * sizeof(el_action_t))
+ EL_ABORT((el->errfile, "Vi command map incorrect\n"));
+ if (sizeof(el_map_vi_insert) != N_KEYS * sizeof(el_action_t))
+ EL_ABORT((el->errfile, "Vi insert map incorrect\n"));
+#endif
+
+ el->el_map.alt = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS);
+ if (el->el_map.alt == NULL)
+ return (-1);
+ el->el_map.key = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS);
+ if (el->el_map.key == NULL)
+ return (-1);
+ el->el_map.emacs = el_map_emacs;
+ el->el_map.vic = el_map_vi_command;
+ el->el_map.vii = el_map_vi_insert;
+ el->el_map.help = (el_bindings_t *) el_malloc(sizeof(el_bindings_t) *
+ EL_NUM_FCNS);
+ if (el->el_map.help == NULL)
+ return (-1);
+ (void) memcpy(el->el_map.help, help__get(),
+ sizeof(el_bindings_t) * EL_NUM_FCNS);
+ el->el_map.func = (el_func_t *)el_malloc(sizeof(el_func_t) *
+ EL_NUM_FCNS);
+ if (el->el_map.func == NULL)
+ return (-1);
+ memcpy(el->el_map.func, func__get(), sizeof(el_func_t) * EL_NUM_FCNS);
+ el->el_map.nfunc = EL_NUM_FCNS;
+
+#ifdef VIDEFAULT
+ map_init_vi(el);
+#else
+ map_init_emacs(el);
+#endif /* VIDEFAULT */
+ return (0);
+}
+
+
+/* map_end():
+ * Free the space taken by the editor maps
+ */
+protected void
+map_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_map.alt);
+ el->el_map.alt = NULL;
+ el_free((ptr_t) el->el_map.key);
+ el->el_map.key = NULL;
+ el->el_map.emacs = NULL;
+ el->el_map.vic = NULL;
+ el->el_map.vii = NULL;
+ el_free((ptr_t) el->el_map.help);
+ el->el_map.help = NULL;
+ el_free((ptr_t) el->el_map.func);
+ el->el_map.func = NULL;
+}
+
+
+/* map_init_nls():
+ * Find all the printable keys and bind them to self insert
+ */
+private void
+map_init_nls(EditLine *el)
+{
+ int i;
+
+ el_action_t *map = el->el_map.key;
+
+ for (i = 0200; i <= 0377; i++)
+ if (isprint(i))
+ map[i] = ED_INSERT;
+}
+
+
+/* map_init_meta():
+ * Bind all the meta keys to the appropriate ESC-<key> sequence
+ */
+private void
+map_init_meta(EditLine *el)
+{
+ char buf[3];
+ int i;
+ el_action_t *map = el->el_map.key;
+ el_action_t *alt = el->el_map.alt;
+
+ for (i = 0; i <= 0377 && map[i] != EM_META_NEXT; i++)
+ continue;
+
+ if (i > 0377) {
+ for (i = 0; i <= 0377 && alt[i] != EM_META_NEXT; i++)
+ continue;
+ if (i > 0377) {
+ i = 033;
+ if (el->el_map.type == MAP_VI)
+ map = alt;
+ } else
+ map = alt;
+ }
+ buf[0] = (char) i;
+ buf[2] = 0;
+ for (i = 0200; i <= 0377; i++)
+ switch (map[i]) {
+ case ED_INSERT:
+ case ED_UNASSIGNED:
+ case ED_SEQUENCE_LEAD_IN:
+ break;
+ default:
+ buf[1] = i & 0177;
+ key_add(el, buf, key_map_cmd(el, (int) map[i]), XK_CMD);
+ break;
+ }
+ map[(int) buf[0]] = ED_SEQUENCE_LEAD_IN;
+}
+
+
+/* map_init_vi():
+ * Initialize the vi bindings
+ */
+protected void
+map_init_vi(EditLine *el)
+{
+ int i;
+ el_action_t *key = el->el_map.key;
+ el_action_t *alt = el->el_map.alt;
+ const el_action_t *vii = el->el_map.vii;
+ const el_action_t *vic = el->el_map.vic;
+
+ el->el_map.type = MAP_VI;
+ el->el_map.current = el->el_map.key;
+
+ key_reset(el);
+
+ for (i = 0; i < N_KEYS; i++) {
+ key[i] = vii[i];
+ alt[i] = vic[i];
+ }
+
+ map_init_meta(el);
+ map_init_nls(el);
+
+ tty_bind_char(el, 1);
+ term_bind_arrow(el);
+}
+
+
+/* map_init_emacs():
+ * Initialize the emacs bindings
+ */
+protected void
+map_init_emacs(EditLine *el)
+{
+ int i;
+ char buf[3];
+ el_action_t *key = el->el_map.key;
+ el_action_t *alt = el->el_map.alt;
+ const el_action_t *emacs = el->el_map.emacs;
+
+ el->el_map.type = MAP_EMACS;
+ el->el_map.current = el->el_map.key;
+ key_reset(el);
+
+ for (i = 0; i < N_KEYS; i++) {
+ key[i] = emacs[i];
+ alt[i] = ED_UNASSIGNED;
+ }
+
+ map_init_meta(el);
+ map_init_nls(el);
+
+ buf[0] = CONTROL('X');
+ buf[1] = CONTROL('X');
+ buf[2] = 0;
+ key_add(el, buf, key_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD);
+
+ tty_bind_char(el, 1);
+ term_bind_arrow(el);
+}
+
+
+/* map_set_editor():
+ * Set the editor
+ */
+protected int
+map_set_editor(EditLine *el, char *editor)
+{
+
+ if (strcmp(editor, "emacs") == 0) {
+ map_init_emacs(el);
+ return (0);
+ }
+ if (strcmp(editor, "vi") == 0) {
+ map_init_vi(el);
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* map_get_editor():
+ * Retrieve the editor
+ */
+protected int
+map_get_editor(EditLine *el, const char **editor)
+{
+
+ if (editor == NULL)
+ return (-1);
+ switch (el->el_map.type) {
+ case MAP_EMACS:
+ *editor = "emacs";
+ return (0);
+ case MAP_VI:
+ *editor = "vi";
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* map_print_key():
+ * Print the function description for 1 key
+ */
+private void
+map_print_key(EditLine *el, el_action_t *map, const char *in)
+{
+ char outbuf[EL_BUFSIZ];
+ el_bindings_t *bp;
+
+ if (in[0] == '\0' || in[1] == '\0') {
+ (void) key__decode_str(in, outbuf, "");
+ for (bp = el->el_map.help; bp->name != NULL; bp++)
+ if (bp->func == map[(unsigned char) *in]) {
+ (void) fprintf(el->el_outfile,
+ "%s\t->\t%s\n", outbuf, bp->name);
+ return;
+ }
+ } else
+ key_print(el, in);
+}
+
+
+/* map_print_some_keys():
+ * Print keys from first to last
+ */
+private void
+map_print_some_keys(EditLine *el, el_action_t *map, int first, int last)
+{
+ el_bindings_t *bp;
+ char firstbuf[2], lastbuf[2];
+ char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ];
+
+ firstbuf[0] = first;
+ firstbuf[1] = 0;
+ lastbuf[0] = last;
+ lastbuf[1] = 0;
+ if (map[first] == ED_UNASSIGNED) {
+ if (first == last)
+ (void) fprintf(el->el_outfile,
+ "%-15s-> is undefined\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ));
+ return;
+ }
+ for (bp = el->el_map.help; bp->name != NULL; bp++) {
+ if (bp->func == map[first]) {
+ if (first == last) {
+ (void) fprintf(el->el_outfile, "%-15s-> %s\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ),
+ bp->name);
+ } else {
+ (void) fprintf(el->el_outfile,
+ "%-4s to %-7s-> %s\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ),
+ key__decode_str(lastbuf, extrabuf, STRQQ),
+ bp->name);
+ }
+ return;
+ }
+ }
+#ifdef MAP_DEBUG
+ if (map == el->el_map.key) {
+ (void) fprintf(el->el_outfile,
+ "BUG!!! %s isn't bound to anything.\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ));
+ (void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n",
+ first, el->el_map.key[first]);
+ } else {
+ (void) fprintf(el->el_outfile,
+ "BUG!!! %s isn't bound to anything.\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ));
+ (void) fprintf(el->el_outfile, "el->el_map.alt[%d] == %d\n",
+ first, el->el_map.alt[first]);
+ }
+#endif
+ EL_ABORT((el->el_errfile, "Error printing keys\n"));
+}
+
+
+/* map_print_all_keys():
+ * Print the function description for all keys.
+ */
+private void
+map_print_all_keys(EditLine *el)
+{
+ int prev, i;
+
+ (void) fprintf(el->el_outfile, "Standard key bindings\n");
+ prev = 0;
+ for (i = 0; i < N_KEYS; i++) {
+ if (el->el_map.key[prev] == el->el_map.key[i])
+ continue;
+ map_print_some_keys(el, el->el_map.key, prev, i - 1);
+ prev = i;
+ }
+ map_print_some_keys(el, el->el_map.key, prev, i - 1);
+
+ (void) fprintf(el->el_outfile, "Alternative key bindings\n");
+ prev = 0;
+ for (i = 0; i < N_KEYS; i++) {
+ if (el->el_map.alt[prev] == el->el_map.alt[i])
+ continue;
+ map_print_some_keys(el, el->el_map.alt, prev, i - 1);
+ prev = i;
+ }
+ map_print_some_keys(el, el->el_map.alt, prev, i - 1);
+
+ (void) fprintf(el->el_outfile, "Multi-character bindings\n");
+ key_print(el, "");
+ (void) fprintf(el->el_outfile, "Arrow key bindings\n");
+ term_print_arrow(el, "");
+}
+
+
+/* map_bind():
+ * Add/remove/change bindings
+ */
+protected int
+map_bind(EditLine *el, int argc, const char **argv)
+{
+ el_action_t *map;
+ int ntype, rem;
+ const char *p;
+ char inbuf[EL_BUFSIZ];
+ char outbuf[EL_BUFSIZ];
+ const char *in = NULL;
+ char *out = NULL;
+ el_bindings_t *bp;
+ int cmd;
+ int key;
+
+ if (argv == NULL)
+ return (-1);
+
+ map = el->el_map.key;
+ ntype = XK_CMD;
+ key = rem = 0;
+ for (argc = 1; (p = argv[argc]) != NULL; argc++)
+ if (p[0] == '-')
+ switch (p[1]) {
+ case 'a':
+ map = el->el_map.alt;
+ break;
+
+ case 's':
+ ntype = XK_STR;
+ break;
+#ifdef notyet
+ case 'c':
+ ntype = XK_EXE;
+ break;
+#endif
+ case 'k':
+ key = 1;
+ break;
+
+ case 'r':
+ rem = 1;
+ break;
+
+ case 'v':
+ map_init_vi(el);
+ return (0);
+
+ case 'e':
+ map_init_emacs(el);
+ return (0);
+
+ case 'l':
+ for (bp = el->el_map.help; bp->name != NULL;
+ bp++)
+ (void) fprintf(el->el_outfile,
+ "%s\n\t%s\n",
+ bp->name, bp->description);
+ return (0);
+ default:
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid switch `%c'.\n",
+ argv[0], p[1]);
+ }
+ else
+ break;
+
+ if (argv[argc] == NULL) {
+ map_print_all_keys(el);
+ return (0);
+ }
+ if (key)
+ in = argv[argc++];
+ else if ((in = parse__string(inbuf, argv[argc++])) == NULL) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid \\ or ^ in instring.\n",
+ argv[0]);
+ return (-1);
+ }
+ if (rem) {
+ if (key) {
+ (void) term_clear_arrow(el, in);
+ return (-1);
+ }
+ if (in[1])
+ (void) key_delete(el, in);
+ else if (map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN)
+ (void) key_delete(el, in);
+ else
+ map[(unsigned char) *in] = ED_UNASSIGNED;
+ return (0);
+ }
+ if (argv[argc] == NULL) {
+ if (key)
+ term_print_arrow(el, in);
+ else
+ map_print_key(el, map, in);
+ return (0);
+ }
+#ifdef notyet
+ if (argv[argc + 1] != NULL) {
+ bindkey_usage();
+ return (-1);
+ }
+#endif
+
+ switch (ntype) {
+ case XK_STR:
+ case XK_EXE:
+ if ((out = parse__string(outbuf, argv[argc])) == NULL) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid \\ or ^ in outstring.\n", argv[0]);
+ return (-1);
+ }
+ if (key)
+ term_set_arrow(el, in, key_map_str(el, out), ntype);
+ else
+ key_add(el, in, key_map_str(el, out), ntype);
+ map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN;
+ break;
+
+ case XK_CMD:
+ if ((cmd = parse_cmd(el, argv[argc])) == -1) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid command `%s'.\n", argv[0], argv[argc]);
+ return (-1);
+ }
+ if (key)
+ term_set_arrow(el, in, key_map_str(el, out), ntype);
+ else {
+ if (in[1]) {
+ key_add(el, in, key_map_cmd(el, cmd), ntype);
+ map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN;
+ } else {
+ key_clear(el, map, in);
+ map[(unsigned char) *in] = cmd;
+ }
+ }
+ break;
+
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type\n", ntype));
+ break;
+ }
+ return (0);
+}
+
+
+/* map_addfunc():
+ * add a user defined function
+ */
+protected int
+map_addfunc(EditLine *el, const char *name, const char *help, el_func_t func)
+{
+ void *p;
+ int nf = el->el_map.nfunc + 2;
+
+ if (name == NULL || help == NULL || func == NULL)
+ return (-1);
+
+ if ((p = el_realloc(el->el_map.func, nf * sizeof(el_func_t))) == NULL)
+ return (-1);
+ el->el_map.func = (el_func_t *) p;
+ if ((p = el_realloc(el->el_map.help, nf * sizeof(el_bindings_t)))
+ == NULL)
+ return (-1);
+ el->el_map.help = (el_bindings_t *) p;
+
+ nf = el->el_map.nfunc;
+ el->el_map.func[nf] = func;
+
+ el->el_map.help[nf].name = name;
+ el->el_map.help[nf].func = nf;
+ el->el_map.help[nf].description = help;
+ el->el_map.help[++nf].name = NULL;
+ el->el_map.nfunc++;
+
+ return (0);
+}
diff --git a/trunk/main/editline/map.h b/trunk/main/editline/map.h
new file mode 100644
index 000000000..3c9948ccf
--- /dev/null
+++ b/trunk/main/editline/map.h
@@ -0,0 +1,79 @@
+/* $NetBSD: map.h,v 1.7 2002/03/18 16:00:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)map.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.map.h: Editor maps
+ */
+#ifndef _h_el_map
+#define _h_el_map
+
+typedef struct el_bindings_t { /* for the "bind" shell command */
+ const char *name; /* function name for bind command */
+ int func; /* function numeric value */
+ const char *description; /* description of function */
+} el_bindings_t;
+
+
+typedef struct el_map_t {
+ el_action_t *alt; /* The current alternate key map */
+ el_action_t *key; /* The current normal key map */
+ el_action_t *current; /* The keymap we are using */
+ const el_action_t *emacs; /* The default emacs key map */
+ const el_action_t *vic; /* The vi command mode key map */
+ const el_action_t *vii; /* The vi insert mode key map */
+ int type; /* Emacs or vi */
+ el_bindings_t *help; /* The help for the editor functions */
+ el_func_t *func; /* List of available functions */
+ int nfunc; /* The number of functions/help items */
+} el_map_t;
+
+#define MAP_EMACS 0
+#define MAP_VI 1
+
+protected int map_bind(EditLine *, int, const char **);
+protected int map_init(EditLine *);
+protected void map_end(EditLine *);
+protected void map_init_vi(EditLine *);
+protected void map_init_emacs(EditLine *);
+protected int map_set_editor(EditLine *, char *);
+protected int map_get_editor(EditLine *, const char **);
+protected int map_addfunc(EditLine *, const char *, const char *, el_func_t);
+
+#endif /* _h_el_map */
diff --git a/trunk/main/editline/np/fgetln.c b/trunk/main/editline/np/fgetln.c
new file mode 100644
index 000000000..93da9914d
--- /dev/null
+++ b/trunk/main/editline/np/fgetln.c
@@ -0,0 +1,88 @@
+/* $NetBSD: fgetln.c,v 1.1.1.1 1999/04/12 07:43:21 crooksa Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+char *
+fgetln(fp, len)
+ FILE *fp;
+ size_t *len;
+{
+ static char *buf = NULL;
+ static size_t bufsiz = 0;
+ char *ptr;
+
+
+ if (buf == NULL) {
+ bufsiz = BUFSIZ;
+ if ((buf = malloc(bufsiz)) == NULL)
+ return NULL;
+ }
+
+ if (fgets(buf, bufsiz, fp) == NULL)
+ return NULL;
+ *len = 0;
+
+ while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
+ size_t nbufsiz = bufsiz + BUFSIZ;
+ char *nbuf = realloc(buf, nbufsiz);
+
+ if (nbuf == NULL) {
+ int oerrno = errno;
+ free(buf);
+ errno = oerrno;
+ buf = NULL;
+ return NULL;
+ } else
+ buf = nbuf;
+
+ *len = bufsiz;
+ if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL)
+ return buf;
+
+ bufsiz = nbufsiz;
+ }
+
+ *len = (ptr - buf) + 1;
+ return buf;
+}
diff --git a/trunk/main/editline/np/strlcat.c b/trunk/main/editline/np/strlcat.c
new file mode 100644
index 000000000..6c9f1e92d
--- /dev/null
+++ b/trunk/main/editline/np/strlcat.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD: src/lib/libc/string/strlcat.c,v 1.2.4.2 2001/07/09 23:30:06 obrien Exp $";
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(initial dst) + strlen(src); if retval >= siz,
+ * truncation occurred.
+ */
+size_t strlcat(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/trunk/main/editline/np/strlcpy.c b/trunk/main/editline/np/strlcpy.c
new file mode 100644
index 000000000..1f154bcf2
--- /dev/null
+++ b/trunk/main/editline/np/strlcpy.c
@@ -0,0 +1,75 @@
+/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD: src/lib/libc/string/strlcpy.c,v 1.2.4.1 2001/07/09 23:30:06 obrien Exp $";
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/trunk/main/editline/np/unvis.c b/trunk/main/editline/np/unvis.c
new file mode 100644
index 000000000..f43c4c749
--- /dev/null
+++ b/trunk/main/editline/np/unvis.c
@@ -0,0 +1,322 @@
+/* $NetBSD: unvis.c,v 1.22 2002/03/23 17:38:27 christos Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: unvis.c,v 1.22 2002/03/23 17:38:27 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#define __LIBC12_SOURCE__
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "np/vis.h"
+
+#ifdef __weak_alias
+__weak_alias(strunvis,_strunvis)
+__weak_alias(unvis,_unvis)
+#endif
+
+#ifdef __warn_references
+__warn_references(unvis,
+ "warning: reference to compatibility unvis(); include <vis.h> for correct reference")
+#endif
+
+#if !HAVE_VIS_H
+/*
+ * decode driven by state machine
+ */
+#define S_GROUND 0 /* haven't seen escape char */
+#define S_START 1 /* start decoding special sequence */
+#define S_META 2 /* metachar started (M) */
+#define S_META1 3 /* metachar more, regular char (-) */
+#define S_CTRL 4 /* control char started (^) */
+#define S_OCTAL2 5 /* octal digit 2 */
+#define S_OCTAL3 6 /* octal digit 3 */
+#define S_HEX1 7 /* hex digit */
+#define S_HEX2 8 /* hex digit 2 */
+
+#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
+
+int
+unvis(cp, c, astate, flag)
+ char *cp;
+ int c;
+ int *astate, flag;
+{
+ return __unvis13(cp, (int)c, astate, flag);
+}
+
+/*
+ * unvis - decode characters previously encoded by vis
+ */
+int
+__unvis13(cp, c, astate, flag)
+ char *cp;
+ int c;
+ int *astate, flag;
+{
+
+ _DIAGASSERT(cp != NULL);
+ _DIAGASSERT(astate != NULL);
+
+ if (flag & UNVIS_END) {
+ if (*astate == S_OCTAL2 || *astate == S_OCTAL3
+ || *astate == S_HEX2) {
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ }
+ return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
+ }
+
+ switch (*astate) {
+
+ case S_GROUND:
+ *cp = 0;
+ if (c == '\\') {
+ *astate = S_START;
+ return (0);
+ }
+ if ((flag & VIS_HTTPSTYLE) && c == '%') {
+ *astate = S_HEX1;
+ return (0);
+ }
+ *cp = c;
+ return (UNVIS_VALID);
+
+ case S_START:
+ switch(c) {
+ case '\\':
+ *cp = c;
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ *cp = (c - '0');
+ *astate = S_OCTAL2;
+ return (0);
+ case 'M':
+ *cp = (char)0200;
+ *astate = S_META;
+ return (0);
+ case '^':
+ *astate = S_CTRL;
+ return (0);
+ case 'n':
+ *cp = '\n';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'r':
+ *cp = '\r';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'b':
+ *cp = '\b';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'a':
+ *cp = '\007';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'v':
+ *cp = '\v';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 't':
+ *cp = '\t';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'f':
+ *cp = '\f';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 's':
+ *cp = ' ';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'E':
+ *cp = '\033';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case '\n':
+ /*
+ * hidden newline
+ */
+ *astate = S_GROUND;
+ return (UNVIS_NOCHAR);
+ case '$':
+ /*
+ * hidden marker
+ */
+ *astate = S_GROUND;
+ return (UNVIS_NOCHAR);
+ }
+ *astate = S_GROUND;
+ return (UNVIS_SYNBAD);
+
+ case S_META:
+ if (c == '-')
+ *astate = S_META1;
+ else if (c == '^')
+ *astate = S_CTRL;
+ else {
+ *astate = S_GROUND;
+ return (UNVIS_SYNBAD);
+ }
+ return (0);
+
+ case S_META1:
+ *astate = S_GROUND;
+ *cp |= c;
+ return (UNVIS_VALID);
+
+ case S_CTRL:
+ if (c == '?')
+ *cp |= 0177;
+ else
+ *cp |= c & 037;
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+
+ case S_OCTAL2: /* second possible octal digit */
+ if (isoctal(c)) {
+ /*
+ * yes - and maybe a third
+ */
+ *cp = (*cp << 3) + (c - '0');
+ *astate = S_OCTAL3;
+ return (0);
+ }
+ /*
+ * no - done with current sequence, push back passed char
+ */
+ *astate = S_GROUND;
+ return (UNVIS_VALIDPUSH);
+
+ case S_OCTAL3: /* third possible octal digit */
+ *astate = S_GROUND;
+ if (isoctal(c)) {
+ *cp = (*cp << 3) + (c - '0');
+ return (UNVIS_VALID);
+ }
+ /*
+ * we were done, push back passed char
+ */
+ return (UNVIS_VALIDPUSH);
+ case S_HEX1:
+ if (isxdigit(c)) {
+ *cp = xtod(c);
+ *astate = S_HEX2;
+ return (0);
+ }
+ /*
+ * no - done with current sequence, push back passed char
+ */
+ *astate = S_GROUND;
+ return (UNVIS_VALIDPUSH);
+ case S_HEX2:
+ *astate = S_GROUND;
+ if (isxdigit(c)) {
+ *cp = xtod(c) | (*cp << 4);
+ return (UNVIS_VALID);
+ }
+ return (UNVIS_VALIDPUSH);
+ default:
+ /*
+ * decoder in unknown state - (probably uninitialized)
+ */
+ *astate = S_GROUND;
+ return (UNVIS_SYNBAD);
+ }
+}
+
+/*
+ * strunvis - decode src into dst
+ *
+ * Number of chars decoded into dst is returned, -1 on error.
+ * Dst is null terminated.
+ */
+
+int
+strunvisx(dst, src, flag)
+ char *dst;
+ const char *src;
+ int flag;
+{
+ char c;
+ char *start = dst;
+ int state = 0;
+
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(dst != NULL);
+
+ while ((c = *src++) != '\0') {
+ again:
+ switch (__unvis13(dst, c, &state, flag)) {
+ case UNVIS_VALID:
+ dst++;
+ break;
+ case UNVIS_VALIDPUSH:
+ dst++;
+ goto again;
+ case 0:
+ case UNVIS_NOCHAR:
+ break;
+ default:
+ return (-1);
+ }
+ }
+ if (__unvis13(dst, c, &state, UNVIS_END) == UNVIS_VALID)
+ dst++;
+ *dst = '\0';
+ return (dst - start);
+}
+
+int
+strunvis(dst, src)
+ char *dst;
+ const char *src;
+{
+ return strunvisx(dst, src, 0);
+}
+#endif
diff --git a/trunk/main/editline/np/vis.c b/trunk/main/editline/np/vis.c
new file mode 100644
index 000000000..7d9117faa
--- /dev/null
+++ b/trunk/main/editline/np/vis.c
@@ -0,0 +1,347 @@
+/* $NetBSD: vis.c,v 1.22 2002/03/23 17:38:27 christos Exp $ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: vis.c,v 1.22 2002/03/23 17:38:27 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include "np/vis.h"
+#include <stdlib.h>
+
+#ifdef __weak_alias
+__weak_alias(strsvis,_strsvis)
+__weak_alias(strsvisx,_strsvisx)
+__weak_alias(strvis,_strvis)
+__weak_alias(strvisx,_strvisx)
+__weak_alias(svis,_svis)
+__weak_alias(vis,_vis)
+#endif
+
+#ifndef HAVE_VIS_H
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef BELL
+#if defined(__STDC__)
+#define BELL '\a'
+#else
+#define BELL '\007'
+#endif
+
+#ifdef SOLARIS
+#include <alloca.h>
+#endif
+
+#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
+#define issafe(c) (c == '\b' || c == BELL || c == '\r')
+#define xtoa(c) "0123456789abcdef"[c]
+
+#define MAXEXTRAS 5
+
+
+#define MAKEEXTRALIST(flag, extra, orig) \
+do { \
+ const char *o = orig; \
+ char *e; \
+ while (*o++) \
+ continue; \
+ extra = alloca((size_t)((o - orig) + MAXEXTRAS)); \
+ for (o = orig, e = extra; (*e++ = *o++) != '\0';) \
+ continue; \
+ e--; \
+ if (flag & VIS_SP) *e++ = ' '; \
+ if (flag & VIS_TAB) *e++ = '\t'; \
+ if (flag & VIS_NL) *e++ = '\n'; \
+ if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \
+ *e = '\0'; \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * This is HVIS, the macro of vis used to HTTP style (RFC 1808)
+ */
+#define HVIS(dst, c, flag, nextc, extra) \
+do \
+ if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { \
+ *dst++ = '%'; \
+ *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); \
+ *dst++ = xtoa((unsigned int)c & 0xf); \
+ } else { \
+ SVIS(dst, c, flag, nextc, extra); \
+ } \
+while (/*CONSTCOND*/0)
+
+/*
+ * This is SVIS, the central macro of vis.
+ * dst: Pointer to the destination buffer
+ * c: Character to encode
+ * flag: Flag word
+ * nextc: The character following 'c'
+ * extra: Pointer to the list of extra characters to be
+ * backslash-protected.
+ */
+#define SVIS(dst, c, flag, nextc, extra) \
+do { \
+ int isextra, isc; \
+ isextra = strchr(extra, c) != NULL; \
+ if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || \
+ ((flag & VIS_SAFE) && issafe(c)))) { \
+ *dst++ = c; \
+ break; \
+ } \
+ isc = 0; \
+ if (flag & VIS_CSTYLE) { \
+ switch (c) { \
+ case '\n': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'n'; \
+ break; \
+ case '\r': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'r'; \
+ break; \
+ case '\b': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'b'; \
+ break; \
+ case BELL: \
+ isc = 1; *dst++ = '\\'; *dst++ = 'a'; \
+ break; \
+ case '\v': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'v'; \
+ break; \
+ case '\t': \
+ isc = 1; *dst++ = '\\'; *dst++ = 't'; \
+ break; \
+ case '\f': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'f'; \
+ break; \
+ case ' ': \
+ isc = 1; *dst++ = '\\'; *dst++ = 's'; \
+ break; \
+ case '\0': \
+ isc = 1; *dst++ = '\\'; *dst++ = '0'; \
+ if (isoctal(nextc)) { \
+ *dst++ = '0'; \
+ *dst++ = '0'; \
+ } \
+ } \
+ } \
+ if (isc) break; \
+ if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { \
+ *dst++ = '\\'; \
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0'; \
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0'; \
+ *dst++ = (c & 07) + '0'; \
+ } else { \
+ if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; \
+ if (c & 0200) { \
+ c &= 0177; *dst++ = 'M'; \
+ } \
+ if (iscntrl(c)) { \
+ *dst++ = '^'; \
+ if (c == 0177) \
+ *dst++ = '?'; \
+ else \
+ *dst++ = c + '@'; \
+ } else { \
+ *dst++ = '-'; *dst++ = c; \
+ } \
+ } \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * svis - visually encode characters, also encoding the characters
+ * pointed to by `extra'
+ */
+char *
+svis(dst, c, flag, nextc, extra)
+ char *dst;
+ int c, flag, nextc;
+ const char *extra;
+{
+ char *nextra;
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+ if (flag & VIS_HTTPSTYLE)
+ HVIS(dst, c, flag, nextc, nextra);
+ else
+ SVIS(dst, c, flag, nextc, nextra);
+ *dst = '\0';
+ return(dst);
+}
+
+
+/*
+ * strsvis, strsvisx - visually encode characters from src into dst
+ *
+ * Extra is a pointer to a \0-terminated list of characters to
+ * be encoded, too. These functions are useful e. g. to
+ * encode strings in such a way so that they are not interpreted
+ * by a shell.
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strsvisx encodes exactly len bytes from src into dst.
+ * This is useful for encoding a block of data.
+ */
+int
+strsvis(dst, src, flag, extra)
+ char *dst;
+ const char *src;
+ int flag;
+ const char *extra;
+{
+ char c;
+ char *start;
+ char *nextra;
+
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+ if (flag & VIS_HTTPSTYLE) {
+ for (start = dst; (c = *src++) != '\0'; /* empty */)
+ HVIS(dst, c, flag, *src, nextra);
+ } else {
+ for (start = dst; (c = *src++) != '\0'; /* empty */)
+ SVIS(dst, c, flag, *src, nextra);
+ }
+ *dst = '\0';
+ return (dst - start);
+}
+
+
+int
+strsvisx(dst, src, len, flag, extra)
+ char *dst;
+ const char *src;
+ size_t len;
+ int flag;
+ const char *extra;
+{
+ char c;
+ char *start;
+ char *nextra;
+
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+
+ if (flag & VIS_HTTPSTYLE) {
+ for (start = dst; len > 0; len--) {
+ c = *src++;
+ HVIS(dst, c, flag, len ? *src : '\0', nextra);
+ }
+ } else {
+ for (start = dst; len > 0; len--) {
+ c = *src++;
+ SVIS(dst, c, flag, len ? *src : '\0', nextra);
+ }
+ }
+ *dst = '\0';
+ return (dst - start);
+}
+
+
+/*
+ * vis - visually encode characters
+ */
+char *
+vis(dst, c, flag, nextc)
+ char *dst;
+ int c, flag, nextc;
+
+{
+ char *extra;
+
+ _DIAGASSERT(dst != NULL);
+
+ MAKEEXTRALIST(flag, extra, "");
+ if (flag & VIS_HTTPSTYLE)
+ HVIS(dst, c, flag, nextc, extra);
+ else
+ SVIS(dst, c, flag, nextc, extra);
+ *dst = '\0';
+ return (dst);
+}
+
+
+/*
+ * strvis, strvisx - visually encode characters from src into dst
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strvisx encodes exactly len bytes from src into dst.
+ * This is useful for encoding a block of data.
+ */
+int
+strvis(dst, src, flag)
+ char *dst;
+ const char *src;
+ int flag;
+{
+ char *extra;
+
+ MAKEEXTRALIST(flag, extra, "");
+ return (strsvis(dst, src, flag, extra));
+}
+
+
+int
+strvisx(dst, src, len, flag)
+ char *dst;
+ const char *src;
+ size_t len;
+ int flag;
+{
+ char *extra;
+
+ MAKEEXTRALIST(flag, extra, "");
+ return (strsvisx(dst, src, len, flag, extra));
+}
+#endif
diff --git a/trunk/main/editline/np/vis.h b/trunk/main/editline/np/vis.h
new file mode 100644
index 000000000..0739c1dfa
--- /dev/null
+++ b/trunk/main/editline/np/vis.h
@@ -0,0 +1,96 @@
+/* $NetBSD: vis.h,v 1.12 2002/03/23 17:39:05 christos Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vis.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _VIS_H_
+#define _VIS_H_
+
+/*
+ * to select alternate encoding format
+ */
+#define VIS_OCTAL 0x01 /* use octal \ddd format */
+#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropiate */
+
+/*
+ * to alter set of characters encoded (default is to encode all
+ * non-graphic except space, tab, and newline).
+ */
+#define VIS_SP 0x04 /* also encode space */
+#define VIS_TAB 0x08 /* also encode tab */
+#define VIS_NL 0x10 /* also encode newline */
+#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
+#define VIS_SAFE 0x20 /* only encode "unsafe" characters */
+
+/*
+ * other
+ */
+#define VIS_NOSLASH 0x40 /* inhibit printing '\' */
+#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */
+
+/*
+ * unvis return codes
+ */
+#define UNVIS_VALID 1 /* character valid */
+#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */
+#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */
+#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */
+#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */
+
+/*
+ * unvis flags
+ */
+#define UNVIS_END 1 /* no more characters */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+char *vis __P((char *, int, int, int));
+char *svis __P((char *, int, int, int, const char *));
+int strvis __P((char *, const char *, int));
+int strsvis __P((char *, const char *, int, const char *));
+int strvisx __P((char *, const char *, size_t, int));
+int strsvisx __P((char *, const char *, size_t, int, const char *));
+int strunvis __P((char *, const char *));
+int strunvisx __P((char *, const char *, int));
+#ifdef __LIBC12_SOURCE__
+int unvis __P((char *, int, int *, int));
+int __unvis13 __P((char *, int, int *, int));
+#else
+int unvis __P((char *, int, int *, int)) __RENAME(__unvis13);
+#endif
+__END_DECLS
+
+#endif /* !_VIS_H_ */
diff --git a/trunk/main/editline/parse.c b/trunk/main/editline/parse.c
new file mode 100644
index 000000000..0a34f0b15
--- /dev/null
+++ b/trunk/main/editline/parse.c
@@ -0,0 +1,259 @@
+/* $NetBSD: parse.c,v 1.15 2002/03/18 16:00:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: parse.c,v 1.15 2002/03/18 16:00:56 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * parse.c: parse an editline extended command
+ *
+ * commands are:
+ *
+ * bind
+ * echotc
+ * edit
+ * gettc
+ * history
+ * settc
+ * setty
+ */
+#include "el.h"
+#include "tokenizer.h"
+#include <stdlib.h>
+
+private const struct {
+ const char *name;
+ int (*func)(EditLine *, int, const char **);
+} cmds[] = {
+ { "bind", map_bind },
+ { "echotc", term_echotc },
+ { "edit", el_editmode },
+ { "history", hist_list },
+ { "telltc", term_telltc },
+ { "settc", term_settc },
+ { "setty", tty_stty },
+ { NULL, NULL }
+};
+
+
+/* parse_line():
+ * Parse a line and dispatch it
+ */
+protected int
+parse_line(EditLine *el, const char *line)
+{
+ const char **argv;
+ int argc;
+ Tokenizer *tok;
+
+ tok = tok_init(NULL);
+ tok_line(tok, line, &argc, &argv);
+ argc = el_parse(el, argc, argv);
+ tok_end(tok);
+ return (argc);
+}
+
+
+/* el_parse():
+ * Command dispatcher
+ */
+public int
+el_parse(EditLine *el, int argc, const char *argv[])
+{
+ const char *ptr;
+ int i;
+
+ if (argc < 1)
+ return (-1);
+ ptr = strchr(argv[0], ':');
+ if (ptr != NULL) {
+ char *tprog;
+ size_t l;
+
+ if (ptr == argv[0])
+ return (0);
+ l = ptr - argv[0] - 1;
+ tprog = (char *) el_malloc(l + 1);
+ if (tprog == NULL)
+ return (0);
+ (void) strncpy(tprog, argv[0], l);
+ tprog[l] = '\0';
+ ptr++;
+ l = el_match(el->el_prog, tprog);
+ el_free(tprog);
+ if (!l)
+ return (0);
+ } else
+ ptr = argv[0];
+
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (strcmp(cmds[i].name, ptr) == 0) {
+ i = (*cmds[i].func) (el, argc, argv);
+ return (-i);
+ }
+ return (-1);
+}
+
+
+/* parse__escape():
+ * Parse a string of the form ^<char> \<odigit> \<char> and return
+ * the appropriate character or -1 if the escape is not valid
+ */
+protected int
+parse__escape(const char **const ptr)
+{
+ const char *p;
+ int c;
+
+ p = *ptr;
+
+ if (p[1] == 0)
+ return (-1);
+
+ if (*p == '\\') {
+ p++;
+ switch (*p) {
+ case 'a':
+ c = '\007'; /* Bell */
+ break;
+ case 'b':
+ c = '\010'; /* Backspace */
+ break;
+ case 't':
+ c = '\011'; /* Horizontal Tab */
+ break;
+ case 'n':
+ c = '\012'; /* New Line */
+ break;
+ case 'v':
+ c = '\013'; /* Vertical Tab */
+ break;
+ case 'f':
+ c = '\014'; /* Form Feed */
+ break;
+ case 'r':
+ c = '\015'; /* Carriage Return */
+ break;
+ case 'e':
+ c = '\033'; /* Escape */
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int cnt, ch;
+
+ for (cnt = 0, c = 0; cnt < 3; cnt++) {
+ ch = *p++;
+ if (ch < '0' || ch > '7') {
+ p--;
+ break;
+ }
+ c = (c << 3) | (ch - '0');
+ }
+ if ((c & 0xffffff00) != 0)
+ return (-1);
+ --p;
+ break;
+ }
+ default:
+ c = *p;
+ break;
+ }
+ } else if (*p == '^' && isalpha((unsigned char) p[1])) {
+ p++;
+ c = (*p == '?') ? '\177' : (*p & 0237);
+ } else
+ c = *p;
+ *ptr = ++p;
+ return (c);
+}
+/* parse__string():
+ * Parse the escapes from in and put the raw string out
+ */
+protected char *
+parse__string(char *out, const char *in)
+{
+ char *rv = out;
+ int n;
+
+ for (;;)
+ switch (*in) {
+ case '\0':
+ *out = '\0';
+ return (rv);
+
+ case '\\':
+ case '^':
+ if ((n = parse__escape(&in)) == -1)
+ return (NULL);
+ *out++ = n;
+ break;
+
+ default:
+ *out++ = *in++;
+ break;
+ }
+}
+
+
+/* parse_cmd():
+ * Return the command number for the command string given
+ * or -1 if one is not found
+ */
+protected int
+parse_cmd(EditLine *el, const char *cmd)
+{
+ el_bindings_t *b;
+
+ for (b = el->el_map.help; b->name != NULL; b++)
+ if (strcmp(b->name, cmd) == 0)
+ return (b->func);
+ return (-1);
+}
diff --git a/trunk/main/editline/parse.h b/trunk/main/editline/parse.h
new file mode 100644
index 000000000..4aaef2f83
--- /dev/null
+++ b/trunk/main/editline/parse.h
@@ -0,0 +1,52 @@
+/* $NetBSD: parse.h,v 1.4 2000/09/04 22:06:31 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)parse.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.parse.h: Parser functions
+ */
+#ifndef _h_el_parse
+#define _h_el_parse
+
+protected int parse_line(EditLine *, const char *);
+protected int parse__escape(const char ** const);
+protected char *parse__string(char *, const char *);
+protected int parse_cmd(EditLine *, const char *);
+
+#endif /* _h_el_parse */
diff --git a/trunk/main/editline/prompt.c b/trunk/main/editline/prompt.c
new file mode 100644
index 000000000..5c069e17a
--- /dev/null
+++ b/trunk/main/editline/prompt.c
@@ -0,0 +1,174 @@
+/* $NetBSD: prompt.c,v 1.9 2002/03/18 16:00:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)prompt.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: prompt.c,v 1.9 2002/03/18 16:00:56 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * prompt.c: Prompt printing functions
+ */
+#include <stdio.h>
+#include "el.h"
+
+private char *prompt_default(EditLine *);
+private char *prompt_default_r(EditLine *);
+
+/* prompt_default():
+ * Just a default prompt, in case the user did not provide one
+ */
+private char *
+/*ARGSUSED*/
+prompt_default(EditLine *el)
+{
+ static char a[3] = {'?', ' ', '\0'};
+
+ return (a);
+}
+
+
+/* prompt_default_r():
+ * Just a default rprompt, in case the user did not provide one
+ */
+private char *
+/*ARGSUSED*/
+prompt_default_r(EditLine *el)
+{
+ static char a[1] = {'\0'};
+
+ return (a);
+}
+
+
+/* prompt_print():
+ * Print the prompt and update the prompt position.
+ * We use an array of integers in case we want to pass
+ * literal escape sequences in the prompt and we want a
+ * bit to flag them
+ */
+protected void
+prompt_print(EditLine *el, int op)
+{
+ el_prompt_t *elp;
+ char *p;
+
+ if (op == EL_PROMPT)
+ elp = &el->el_prompt;
+ else
+ elp = &el->el_rprompt;
+ p = (elp->p_func) (el);
+ while (*p)
+ re_putc(el, *p++, 1);
+
+ elp->p_pos.v = el->el_refresh.r_cursor.v;
+ elp->p_pos.h = el->el_refresh.r_cursor.h;
+}
+
+
+/* prompt_init():
+ * Initialize the prompt stuff
+ */
+protected int
+prompt_init(EditLine *el)
+{
+
+ el->el_prompt.p_func = prompt_default;
+ el->el_prompt.p_pos.v = 0;
+ el->el_prompt.p_pos.h = 0;
+ el->el_rprompt.p_func = prompt_default_r;
+ el->el_rprompt.p_pos.v = 0;
+ el->el_rprompt.p_pos.h = 0;
+ return (0);
+}
+
+
+/* prompt_end():
+ * Clean up the prompt stuff
+ */
+protected void
+/*ARGSUSED*/
+prompt_end(EditLine *el)
+{
+}
+
+
+/* prompt_set():
+ * Install a prompt printing function
+ */
+protected int
+prompt_set(EditLine *el, el_pfunc_t prf, int op)
+{
+ el_prompt_t *p;
+
+ if (op == EL_PROMPT)
+ p = &el->el_prompt;
+ else
+ p = &el->el_rprompt;
+ if (prf == NULL) {
+ if (op == EL_PROMPT)
+ p->p_func = prompt_default;
+ else
+ p->p_func = prompt_default_r;
+ } else
+ p->p_func = prf;
+ p->p_pos.v = 0;
+ p->p_pos.h = 0;
+ return (0);
+}
+
+
+/* prompt_get():
+ * Retrieve the prompt printing function
+ */
+protected int
+prompt_get(EditLine *el, el_pfunc_t *prf, int op)
+{
+
+ if (prf == NULL)
+ return (-1);
+ if (op == EL_PROMPT)
+ *prf = el->el_prompt.p_func;
+ else
+ *prf = el->el_rprompt.p_func;
+ return (0);
+}
diff --git a/trunk/main/editline/prompt.h b/trunk/main/editline/prompt.h
new file mode 100644
index 000000000..08810e22a
--- /dev/null
+++ b/trunk/main/editline/prompt.h
@@ -0,0 +1,62 @@
+/* $NetBSD: prompt.h,v 1.5 2000/09/04 22:06:31 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)prompt.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.prompt.h: Prompt printing stuff
+ */
+#ifndef _h_el_prompt
+#define _h_el_prompt
+
+#include "histedit.h"
+
+typedef char * (*el_pfunc_t)(EditLine*);
+
+typedef struct el_prompt_t {
+ el_pfunc_t p_func; /* Function to return the prompt */
+ coord_t p_pos; /* position in the line after prompt */
+} el_prompt_t;
+
+protected void prompt_print(EditLine *, int);
+protected int prompt_set(EditLine *, el_pfunc_t, int);
+protected int prompt_get(EditLine *, el_pfunc_t *, int);
+protected int prompt_init(EditLine *);
+protected void prompt_end(EditLine *);
+
+#endif /* _h_el_prompt */
diff --git a/trunk/main/editline/read.c b/trunk/main/editline/read.c
new file mode 100644
index 000000000..ccd0a06e5
--- /dev/null
+++ b/trunk/main/editline/read.c
@@ -0,0 +1,555 @@
+/* $NetBSD: read.c,v 1.21 2002/03/18 16:00:57 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: read.c,v 1.21 2002/03/18 16:00:57 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * read.c: Clean this junk up! This is horrible code.
+ * Terminal read functions
+ */
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "el.h"
+
+#define OKCMD -1
+
+private int read__fixio(int, int);
+private int read_preread(EditLine *);
+private int read_char(EditLine *, char *);
+private int read_getcmd(EditLine *, el_action_t *, char *);
+
+/* read_init():
+ * Initialize the read stuff
+ */
+protected int
+read_init(EditLine *el)
+{
+ /* builtin read_char */
+ el->el_read.read_char = read_char;
+ return 0;
+}
+
+
+/* el_read_setfn():
+ * Set the read char function to the one provided.
+ * If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one.
+ */
+protected int
+el_read_setfn(EditLine *el, el_rfunc_t rc)
+{
+ el->el_read.read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc;
+ return 0;
+}
+
+
+/* el_read_getfn():
+ * return the current read char function, or EL_BUILTIN_GETCFN
+ * if it is the default one
+ */
+protected el_rfunc_t
+el_read_getfn(EditLine *el)
+{
+ return (el->el_read.read_char == read_char) ?
+ EL_BUILTIN_GETCFN : el->el_read.read_char;
+}
+
+
+#ifdef DEBUG_EDIT
+private void
+read_debug(EditLine *el)
+{
+
+ if (el->el_line.cursor > el->el_line.lastchar)
+ (void) fprintf(el->el_errfile, "cursor > lastchar\r\n");
+ if (el->el_line.cursor < el->el_line.buffer)
+ (void) fprintf(el->el_errfile, "cursor < buffer\r\n");
+ if (el->el_line.cursor > el->el_line.limit)
+ (void) fprintf(el->el_errfile, "cursor > limit\r\n");
+ if (el->el_line.lastchar > el->el_line.limit)
+ (void) fprintf(el->el_errfile, "lastchar > limit\r\n");
+ if (el->el_line.limit != &el->el_line.buffer[EL_BUFSIZ - 2])
+ (void) fprintf(el->el_errfile, "limit != &buffer[EL_BUFSIZ-2]\r\n");
+}
+#endif /* DEBUG_EDIT */
+
+
+/* read__fixio():
+ * Try to recover from a read error
+ */
+/* ARGSUSED */
+private int
+read__fixio(int fd, int e)
+{
+
+ switch (e) {
+ case -1: /* Make sure that the code is reachable */
+
+#ifdef EWOULDBLOCK
+ case EWOULDBLOCK:
+#ifndef TRY_AGAIN
+#define TRY_AGAIN
+#endif
+#endif /* EWOULDBLOCK */
+
+#if defined(POSIX) && defined(EAGAIN)
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EAGAIN:
+#ifndef TRY_AGAIN
+#define TRY_AGAIN
+#endif
+#endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */
+#endif /* POSIX && EAGAIN */
+
+ e = 0;
+#ifdef TRY_AGAIN
+#if defined(F_SETFL) && defined(O_NDELAY)
+ if ((e = fcntl(fd, F_GETFL, 0)) == -1)
+ return (-1);
+
+ if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1)
+ return (-1);
+ else
+ e = 1;
+#endif /* F_SETFL && O_NDELAY */
+
+#ifdef FIONBIO
+ {
+ int zero = 0;
+
+ if (ioctl(fd, FIONBIO, (ioctl_t) & zero) == -1)
+ return (-1);
+ else
+ e = 1;
+ }
+#endif /* FIONBIO */
+
+#endif /* TRY_AGAIN */
+ return (e ? 0 : -1);
+
+ case EINTR:
+ return (0);
+
+ default:
+ return (-1);
+ }
+}
+
+
+/* read_preread():
+ * Try to read the stuff in the input queue;
+ */
+private int
+read_preread(EditLine *el)
+{
+ int chrs = 0;
+
+ if (el->el_chared.c_macro.nline) {
+ el_free((ptr_t) el->el_chared.c_macro.nline);
+ el->el_chared.c_macro.nline = NULL;
+ }
+ if (el->el_tty.t_mode == ED_IO)
+ return (0);
+
+#ifdef FIONREAD
+ (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
+ if (chrs > 0) {
+ char buf[EL_BUFSIZ];
+
+ chrs = read(el->el_infd, buf,
+ (size_t) MIN(chrs, EL_BUFSIZ - 1));
+ if (chrs > 0) {
+ buf[chrs] = '\0';
+ el->el_chared.c_macro.nline = strdup(buf);
+ el_push(el, el->el_chared.c_macro.nline);
+ }
+ }
+#endif /* FIONREAD */
+
+ return (chrs > 0);
+}
+
+
+/* el_push():
+ * Push a macro
+ */
+public void
+el_push(EditLine *el, char *str)
+{
+ c_macro_t *ma = &el->el_chared.c_macro;
+
+ if (str != NULL && ma->level + 1 < EL_MAXMACRO) {
+ ma->level++;
+ ma->macro[ma->level] = str;
+ } else {
+ term_beep(el);
+ term__flush();
+ }
+}
+
+
+/* read_getcmd():
+ * Return next command from the input stream.
+ */
+private int
+read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
+{
+ el_action_t cmd = ED_UNASSIGNED;
+ int num;
+
+ while (cmd == ED_UNASSIGNED || cmd == ED_SEQUENCE_LEAD_IN) {
+ if ((num = el_getc(el, ch)) != 1) /* if EOF or error */
+ return (num);
+
+#ifdef KANJI
+ if ((*ch & 0200)) {
+ el->el_state.metanext = 0;
+ cmd = CcViMap[' '];
+ break;
+ } else
+#endif /* KANJI */
+
+ if (el->el_state.metanext) {
+ el->el_state.metanext = 0;
+ *ch |= 0200;
+ }
+ cmd = el->el_map.current[(unsigned char) *ch];
+ if (cmd == ED_SEQUENCE_LEAD_IN) {
+ key_value_t val;
+ switch (key_get(el, ch, &val)) {
+ case XK_CMD:
+ cmd = val.cmd;
+ break;
+ case XK_STR:
+ el_push(el, val.str);
+ break;
+#ifdef notyet
+ case XK_EXE:
+ /* XXX: In the future to run a user function */
+ RunCommand(val.str);
+ break;
+#endif
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type \n"));
+ break;
+ }
+ }
+ if (el->el_map.alt == NULL)
+ el->el_map.current = el->el_map.key;
+ }
+ *cmdnum = cmd;
+ return (OKCMD);
+}
+
+
+/* read_char():
+ * Read a character from the tty.
+ */
+private int
+read_char(EditLine *el, char *cp)
+{
+ int num_read;
+ int tried = 0;
+
+ while ((num_read = read(el->el_infd, cp, 1)) == -1)
+ if (!tried && read__fixio(el->el_infd, errno) == 0)
+ tried = 1;
+ else {
+ *cp = '\0';
+ return (-1);
+ }
+
+ return (num_read);
+}
+
+
+/* el_getc():
+ * Read a character
+ */
+public int
+el_getc(EditLine *el, char *cp)
+{
+ int num_read;
+ c_macro_t *ma = &el->el_chared.c_macro;
+
+ term__flush();
+ for (;;) {
+ if (ma->level < 0) {
+ if (!read_preread(el))
+ break;
+ }
+ if (ma->level < 0)
+ break;
+
+ if (*ma->macro[ma->level] == 0) {
+ ma->level--;
+ continue;
+ }
+ *cp = *ma->macro[ma->level]++ & 0377;
+ if (*ma->macro[ma->level] == 0) { /* Needed for QuoteMode
+ * On */
+ ma->level--;
+ }
+ return (1);
+ }
+
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile, "Turning raw mode on\n");
+#endif /* DEBUG_READ */
+ if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */
+ return (0);
+
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile, "Reading a character\n");
+#endif /* DEBUG_READ */
+ num_read = (*el->el_read.read_char)(el, cp);
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile, "Got it %c\n", *cp);
+#endif /* DEBUG_READ */
+ return (num_read);
+}
+
+
+public const char *
+el_gets(EditLine *el, int *nread)
+{
+ int retval;
+ el_action_t cmdnum = 0;
+ int num; /* how many chars we have read at NL */
+ char ch;
+#ifdef FIONREAD
+ c_macro_t *ma = &el->el_chared.c_macro;
+#endif /* FIONREAD */
+
+ if (el->el_flags & HANDLE_SIGNALS)
+ sig_set(el);
+
+ if (el->el_flags & NO_TTY) {
+ char *cp = el->el_line.buffer;
+ size_t idx;
+
+ while ((*el->el_read.read_char)(el, cp) == 1) {
+ /* make sure there is space for next character */
+ if (cp + 1 >= el->el_line.limit) {
+ idx = (cp - el->el_line.buffer);
+ if (!ch_enlargebufs(el, 2))
+ break;
+ cp = &el->el_line.buffer[idx];
+ }
+ cp++;
+ if (cp[-1] == '\r' || cp[-1] == '\n')
+ break;
+ }
+
+ el->el_line.cursor = el->el_line.lastchar = cp;
+ *cp = '\0';
+ if (nread)
+ *nread = el->el_line.cursor - el->el_line.buffer;
+ return (el->el_line.buffer);
+ }
+ re_clear_display(el); /* reset the display stuff */
+ ch_reset(el);
+
+#ifdef FIONREAD
+ if (el->el_tty.t_mode == EX_IO && ma->level < 0) {
+ long chrs = 0;
+
+ (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
+ if (chrs == 0) {
+ if (tty_rawmode(el) < 0) {
+ if (nread)
+ *nread = 0;
+ return (NULL);
+ }
+ }
+ }
+#endif /* FIONREAD */
+
+ re_refresh(el); /* print the prompt */
+
+ if (el->el_flags & EDIT_DISABLED) {
+ char *cp = el->el_line.buffer;
+ size_t idx;
+
+ term__flush();
+
+ while ((*el->el_read.read_char)(el, cp) == 1) {
+ /* make sure there is space next character */
+ if (cp + 1 >= el->el_line.limit) {
+ idx = (cp - el->el_line.buffer);
+ if (!ch_enlargebufs(el, 2))
+ break;
+ cp = &el->el_line.buffer[idx];
+ }
+ cp++;
+ if (cp[-1] == '\r' || cp[-1] == '\n')
+ break;
+ }
+
+ el->el_line.cursor = el->el_line.lastchar = cp;
+ *cp = '\0';
+ if (nread)
+ *nread = el->el_line.cursor - el->el_line.buffer;
+ return (el->el_line.buffer);
+ }
+ for (num = OKCMD; num == OKCMD;) { /* while still editing this
+ * line */
+#ifdef DEBUG_EDIT
+ read_debug(el);
+#endif /* DEBUG_EDIT */
+ /* if EOF or error */
+ if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile,
+ "Returning from el_gets %d\n", num);
+#endif /* DEBUG_READ */
+ break;
+ }
+ if ((int) cmdnum >= el->el_map.nfunc) { /* BUG CHECK command */
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile,
+ "ERROR: illegal command from key 0%o\r\n", ch);
+#endif /* DEBUG_EDIT */
+ continue; /* try again */
+ }
+ /* now do the real command */
+#ifdef DEBUG_READ
+ {
+ el_bindings_t *b;
+ for (b = el->el_map.help; b->name; b++)
+ if (b->func == cmdnum)
+ break;
+ if (b->name)
+ (void) fprintf(el->el_errfile,
+ "Executing %s\n", b->name);
+ else
+ (void) fprintf(el->el_errfile,
+ "Error command = %d\n", cmdnum);
+ }
+#endif /* DEBUG_READ */
+ retval = (*el->el_map.func[cmdnum]) (el, ch);
+
+ /* save the last command here */
+ el->el_state.lastcmd = cmdnum;
+
+ /* use any return value */
+ switch (retval) {
+ case CC_CURSOR:
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ re_refresh_cursor(el);
+ break;
+
+ case CC_REDISPLAY:
+ re_clear_lines(el);
+ re_clear_display(el);
+ /* FALLTHROUGH */
+
+ case CC_REFRESH:
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ re_refresh(el);
+ break;
+
+ case CC_REFRESH_BEEP:
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ re_refresh(el);
+ term_beep(el);
+ break;
+
+ case CC_NORM: /* normal char */
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ break;
+
+ case CC_ARGHACK: /* Suggested by Rich Salz */
+ /* <rsalz@pineapple.bbn.com> */
+ break; /* keep going... */
+
+ case CC_EOF: /* end of file typed */
+ num = 0;
+ break;
+
+ case CC_NEWLINE: /* normal end of line */
+ num = el->el_line.lastchar - el->el_line.buffer;
+ break;
+
+ case CC_FATAL: /* fatal error, reset to known state */
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile,
+ "*** editor fatal ERROR ***\r\n\n");
+#endif /* DEBUG_READ */
+ /* put (real) cursor in a known place */
+ re_clear_display(el); /* reset the display stuff */
+ ch_reset(el); /* reset the input pointers */
+ re_refresh(el); /* print the prompt again */
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ break;
+
+ case CC_ERROR:
+ default: /* functions we don't know about */
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile,
+ "*** editor ERROR ***\r\n\n");
+#endif /* DEBUG_READ */
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ term_beep(el);
+ term__flush();
+ break;
+ }
+ }
+
+ /* make sure the tty is set up correctly */
+ (void) tty_cookedmode(el);
+ term__flush(); /* flush any buffered output */
+ if (el->el_flags & HANDLE_SIGNALS)
+ sig_clr(el);
+ if (nread)
+ *nread = num;
+ return (num ? el->el_line.buffer : NULL);
+}
diff --git a/trunk/main/editline/read.h b/trunk/main/editline/read.h
new file mode 100644
index 000000000..b01e77db2
--- /dev/null
+++ b/trunk/main/editline/read.h
@@ -0,0 +1,55 @@
+/* $NetBSD: read.h,v 1.1 2001/09/27 19:29:50 christos Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Anthony Mallet.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * el.read.h: Character reading functions
+ */
+#ifndef _h_el_read
+#define _h_el_read
+
+typedef int (*el_rfunc_t)(EditLine *, char *);
+
+typedef struct el_read_t {
+ el_rfunc_t read_char; /* Function to read a character */
+} el_read_t;
+
+protected int read_init(EditLine *);
+protected int el_read_setfn(EditLine *, el_rfunc_t);
+protected el_rfunc_t el_read_getfn(EditLine *);
+
+#endif /* _h_el_read */
diff --git a/trunk/main/editline/readline.c b/trunk/main/editline/readline.c
new file mode 100644
index 000000000..3fbbb79a5
--- /dev/null
+++ b/trunk/main/editline/readline.c
@@ -0,0 +1,1665 @@
+/* $NetBSD: readline.c,v 1.21 2002/03/18 16:20:36 christos Exp $ */
+
+/*-
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jaromir Dolecek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+__RCSID("$NetBSD: readline.c,v 1.21 2002/03/18 16:20:36 christos Exp $");
+#endif /* not lint && not SCCSID */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <string.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+
+#ifdef SOLARIS
+#include <alloca.h>
+#endif
+
+#include "histedit.h"
+#include "readline/readline.h"
+#include "el.h"
+#include "fcns.h" /* for EL_NUM_FCNS */
+
+/* for rl_complete() */
+#define TAB '\r'
+
+/* see comment at the #ifdef for sense of this */
+#define GDB_411_HACK
+
+/* readline compatibility stuff - look at readline sources/documentation */
+/* to see what these variables mean */
+const char *rl_library_version = "EditLine wrapper";
+static char empty[] = { '\0' };
+static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' };
+static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$',
+ '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
+char *rl_readline_name = empty;
+FILE *rl_instream = NULL;
+FILE *rl_outstream = NULL;
+int rl_point = 0;
+int rl_end = 0;
+char *rl_line_buffer = NULL;
+
+int history_base = 1; /* probably never subject to change */
+int history_length = 0;
+int max_input_history = 0;
+char history_expansion_char = '!';
+char history_subst_char = '^';
+char *history_no_expand_chars = expand_chars;
+Function *history_inhibit_expansion_function = NULL;
+
+int rl_inhibit_completion = 0;
+int rl_attempted_completion_over = 0;
+char *rl_basic_word_break_characters = break_chars;
+char *rl_completer_word_break_characters = NULL;
+char *rl_completer_quote_characters = NULL;
+CPFunction *rl_completion_entry_function = NULL;
+CPPFunction *rl_attempted_completion_function = NULL;
+
+/*
+ * This is set to character indicating type of completion being done by
+ * rl_complete_internal(); this is available for application completion
+ * functions.
+ */
+int rl_completion_type = 0;
+
+/*
+ * If more than this number of items results from query for possible
+ * completions, we ask user if they are sure to really display the list.
+ */
+int rl_completion_query_items = 100;
+
+/*
+ * List of characters which are word break characters, but should be left
+ * in the parsed text when it is passed to the completion function.
+ * Shell uses this to help determine what kind of completing to do.
+ */
+char *rl_special_prefixes = (char *)NULL;
+
+/*
+ * This is the character appended to the completed words if at the end of
+ * the line. Default is ' ' (a space).
+ */
+int rl_completion_append_character = ' ';
+
+/* stuff below is used internally by libedit for readline emulation */
+
+/* if not zero, non-unique completions always show list of possible matches */
+static int _rl_complete_show_all = 0;
+
+static History *h = NULL;
+static EditLine *e = NULL;
+static int el_rl_complete_cmdnum = 0;
+
+/* internal functions */
+static unsigned char _el_rl_complete(EditLine *, int);
+static char *_get_prompt(EditLine *);
+static HIST_ENTRY *_move_history(int);
+static int _history_search_gen(const char *, int, int);
+static int _history_expand_command(const char *, size_t, char **);
+static char *_rl_compat_sub(const char *, const char *,
+ const char *, int);
+static int rl_complete_internal(int);
+static int _rl_qsort_string_compare(const void *, const void *);
+
+/*
+ * needed for prompt switching in readline()
+ */
+static char *el_rl_prompt = NULL;
+
+
+/* ARGSUSED */
+static char *
+_get_prompt(EditLine *el)
+{
+ return (el_rl_prompt);
+}
+
+
+/*
+ * generic function for moving around history
+ */
+static HIST_ENTRY *
+_move_history(int op)
+{
+ HistEvent ev;
+ static HIST_ENTRY rl_he;
+
+ if (history(h, &ev, op) != 0)
+ return (HIST_ENTRY *) NULL;
+
+ rl_he.line = ev.str;
+ rl_he.data = "";
+
+ return (&rl_he);
+}
+
+
+/*
+ * READLINE compatibility stuff
+ */
+
+/*
+ * initialize rl compat stuff
+ */
+int
+rl_initialize(void)
+{
+ HistEvent ev;
+ const LineInfo *li;
+ int i;
+ int editmode = 1;
+ struct termios t;
+
+ if (e != NULL)
+ el_end(e);
+ if (h != NULL)
+ history_end(h);
+
+ if (!rl_instream)
+ rl_instream = stdin;
+ if (!rl_outstream)
+ rl_outstream = stdout;
+
+ /*
+ * See if we don't really want to run the editor
+ */
+ if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0)
+ editmode = 0;
+
+ e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr);
+
+ if (!editmode)
+ el_set(e, EL_EDITMODE, 0);
+
+ h = history_init();
+ if (!e || !h)
+ return (-1);
+
+ history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */
+ history_length = 0;
+ max_input_history = INT_MAX;
+ el_set(e, EL_HIST, history, h);
+
+ /* for proper prompt printing in readline() */
+ el_rl_prompt = strdup("");
+ el_set(e, EL_PROMPT, _get_prompt);
+ el_set(e, EL_SIGNAL, 1);
+
+ /* set default mode to "emacs"-style and read setting afterwards */
+ /* so this can be overriden */
+ el_set(e, EL_EDITOR, "emacs");
+
+ /*
+ * Word completition - this has to go AFTER rebinding keys
+ * to emacs-style.
+ */
+ el_set(e, EL_ADDFN, "rl_complete",
+ "ReadLine compatible completition function",
+ _el_rl_complete);
+ el_set(e, EL_BIND, "^I", "rl_complete", NULL);
+
+ /*
+ * Find out where the rl_complete function was added; this is
+ * used later to detect that lastcmd was also rl_complete.
+ */
+ for(i=EL_NUM_FCNS; i < e->el_map.nfunc; i++) {
+ if (e->el_map.func[i] == _el_rl_complete) {
+ el_rl_complete_cmdnum = i;
+ break;
+ }
+ }
+
+ /* read settings from configuration file */
+ el_source(e, NULL);
+
+ /*
+ * Unfortunately, some applications really do use rl_point
+ * and rl_line_buffer directly.
+ */
+ li = el_line(e);
+ /* a cheesy way to get rid of const cast. */
+ rl_line_buffer = memchr(li->buffer, *li->buffer, 1);
+ rl_point = rl_end = 0;
+
+ return (0);
+}
+
+
+/*
+ * read one line from input stream and return it, chomping
+ * trailing newline (if there is any)
+ */
+char *
+readline(const char *prompt)
+{
+ HistEvent ev;
+ int count;
+ const char *ret;
+ char *buf;
+
+ if (e == NULL || h == NULL)
+ rl_initialize();
+
+ /* update prompt accordingly to what has been passed */
+ if (!prompt)
+ prompt = "";
+ if (strcmp(el_rl_prompt, prompt) != 0) {
+ free(el_rl_prompt);
+ el_rl_prompt = strdup(prompt);
+ }
+ /* get one line from input stream */
+ ret = el_gets(e, &count);
+
+ if (ret && count > 0) {
+ int lastidx;
+
+ buf = strdup(ret);
+ lastidx = count - 1;
+ if (buf[lastidx] == '\n')
+ buf[lastidx] = '\0';
+ } else
+ buf = NULL;
+
+ history(h, &ev, H_GETSIZE);
+ history_length = ev.num;
+
+ return buf;
+}
+
+/*
+ * history functions
+ */
+
+/*
+ * is normally called before application starts to use
+ * history expansion functions
+ */
+void
+using_history(void)
+{
+ if (h == NULL || e == NULL)
+ rl_initialize();
+}
+
+
+/*
+ * substitute ``what'' with ``with'', returning resulting string; if
+ * globally == 1, substitutes all occurences of what, otherwise only the
+ * first one
+ */
+static char *
+_rl_compat_sub(const char *str, const char *what, const char *with,
+ int globally)
+{
+ char *result;
+ const char *temp, *new;
+ int len, with_len, what_len, add;
+ size_t size, i;
+
+ result = malloc((size = 16));
+ temp = str;
+ with_len = strlen(with);
+ what_len = strlen(what);
+ len = 0;
+ do {
+ new = strstr(temp, what);
+ if (new) {
+ i = new - temp;
+ add = i + with_len;
+ if (i + add + 1 >= size) {
+ size += add + 1;
+ result = realloc(result, size);
+ }
+ (void) strncpy(&result[len], temp, i);
+ len += i;
+ (void) strcpy(&result[len], with); /* safe */
+ len += with_len;
+ temp = new + what_len;
+ } else {
+ add = strlen(temp);
+ if (len + add + 1 >= size) {
+ size += add + 1;
+ result = realloc(result, size);
+ }
+ (void) strcpy(&result[len], temp); /* safe */
+ len += add;
+ temp = NULL;
+ }
+ } while (temp && globally);
+ result[len] = '\0';
+
+ return (result);
+}
+
+
+/*
+ * the real function doing history expansion - takes as argument command
+ * to do and data upon which the command should be executed
+ * does expansion the way I've understood readline documentation
+ * word designator ``%'' isn't supported (yet ?)
+ *
+ * returns 0 if data was not modified, 1 if it was and 2 if the string
+ * should be only printed and not executed; in case of error,
+ * returns -1 and *result points to NULL
+ * it's callers responsibility to free() string returned in *result
+ */
+static int
+_history_expand_command(const char *command, size_t cmdlen, char **result)
+{
+ char **arr, *tempcmd, *line, *search = NULL, *cmd;
+ const char *event_data = NULL;
+ static char *from = NULL, *to = NULL;
+ int start = -1, end = -1, max, i, idx;
+ int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0, g_on = 0;
+ int event_num = 0, retval;
+ size_t cmdsize;
+
+ *result = NULL;
+
+ cmd = alloca(cmdlen + 1);
+ (void) strncpy(cmd, command, cmdlen);
+ cmd[cmdlen] = 0;
+
+ idx = 1;
+ /* find out which event to take */
+ if (cmd[idx] == history_expansion_char) {
+ event_num = history_length;
+ idx++;
+ } else {
+ int off, num;
+ size_t len;
+ off = idx;
+ while (cmd[off] && !strchr(":^$*-%", cmd[off]))
+ off++;
+ num = atoi(&cmd[idx]);
+ if (num != 0) {
+ event_num = num;
+ if (num < 0)
+ event_num += history_length + 1;
+ } else {
+ int prefix = 1, curr_num;
+ HistEvent ev;
+
+ len = off - idx;
+ if (cmd[idx] == '?') {
+ idx++, len--;
+ if (cmd[off - 1] == '?')
+ len--;
+ else if (cmd[off] != '\n' && cmd[off] != '\0')
+ return (-1);
+ prefix = 0;
+ }
+ search = alloca(len + 1);
+ (void) strncpy(search, &cmd[idx], len);
+ search[len] = '\0';
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ if (prefix)
+ retval = history_search_prefix(search, -1);
+ else
+ retval = history_search(search, -1);
+
+ if (retval == -1) {
+ fprintf(rl_outstream, "%s: Event not found\n",
+ search);
+ return (-1);
+ }
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ event_data = ev.str;
+
+ /* roll back to original position */
+ history(h, &ev, H_NEXT_EVENT, curr_num);
+ }
+ idx = off;
+ }
+
+ if (!event_data && event_num >= 0) {
+ HIST_ENTRY *rl_he;
+ rl_he = history_get(event_num);
+ if (!rl_he)
+ return (0);
+ event_data = rl_he->line;
+ } else
+ return (-1);
+
+ if (cmd[idx] != ':')
+ return (-1);
+ cmd += idx + 1;
+
+ /* recognize cmd */
+ if (*cmd == '^')
+ start = end = 1, cmd++;
+ else if (*cmd == '$')
+ start = end = -1, cmd++;
+ else if (*cmd == '*')
+ start = 1, end = -1, cmd++;
+ else if (isdigit((unsigned char) *cmd)) {
+ const char *temp;
+ int shifted = 0;
+
+ start = atoi(cmd);
+ temp = cmd;
+ for (; isdigit((unsigned char) *cmd); cmd++);
+ if (temp != cmd)
+ shifted = 1;
+ if (shifted && *cmd == '-') {
+ if (!isdigit((unsigned char) *(cmd + 1)))
+ end = -2;
+ else {
+ end = atoi(cmd + 1);
+ for (; isdigit((unsigned char) *cmd); cmd++);
+ }
+ } else if (shifted && *cmd == '*')
+ end = -1, cmd++;
+ else if (shifted)
+ end = start;
+ }
+ if (*cmd == ':')
+ cmd++;
+
+ line = strdup(event_data);
+ for (; *cmd; cmd++) {
+ if (*cmd == ':')
+ continue;
+ else if (*cmd == 'h')
+ h_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 't')
+ t_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'r')
+ r_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'e')
+ e_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'p')
+ p_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'g')
+ g_on = 2;
+ else if (*cmd == 's' || *cmd == '&') {
+ char *what, *with, delim;
+ int len, from_len;
+ size_t size;
+
+ if (*cmd == '&' && (from == NULL || to == NULL))
+ continue;
+ else if (*cmd == 's') {
+ delim = *(++cmd), cmd++;
+ size = 16;
+ what = realloc(from, size);
+ len = 0;
+ for (; *cmd && *cmd != delim; cmd++) {
+ if (*cmd == '\\'
+ && *(cmd + 1) == delim)
+ cmd++;
+ if (len >= size)
+ what = realloc(what,
+ (size <<= 1));
+ what[len++] = *cmd;
+ }
+ what[len] = '\0';
+ from = what;
+ if (*what == '\0') {
+ free(what);
+ if (search)
+ from = strdup(search);
+ else {
+ from = NULL;
+ return (-1);
+ }
+ }
+ cmd++; /* shift after delim */
+ if (!*cmd)
+ continue;
+
+ size = 16;
+ with = realloc(to, size);
+ len = 0;
+ from_len = strlen(from);
+ for (; *cmd && *cmd != delim; cmd++) {
+ if (len + from_len + 1 >= size) {
+ size += from_len + 1;
+ with = realloc(with, size);
+ }
+ if (*cmd == '&') {
+ /* safe */
+ (void) strcpy(&with[len], from);
+ len += from_len;
+ continue;
+ }
+ if (*cmd == '\\'
+ && (*(cmd + 1) == delim
+ || *(cmd + 1) == '&'))
+ cmd++;
+ with[len++] = *cmd;
+ }
+ with[len] = '\0';
+ to = with;
+
+ tempcmd = _rl_compat_sub(line, from, to,
+ (g_on) ? 1 : 0);
+ free(line);
+ line = tempcmd;
+ g_on = 0;
+ }
+ }
+ }
+
+ arr = history_tokenize(line);
+ free(line); /* no more needed */
+ if (arr && *arr == NULL)
+ free(arr), arr = NULL;
+ if (!arr)
+ return (-1);
+
+ /* find out max valid idx to array of array */
+ max = 0;
+ for (i = 0; arr[i]; i++)
+ max++;
+ max--;
+
+ /* set boundaries to something relevant */
+ if (start < 0)
+ start = 1;
+ if (end < 0)
+ end = max - ((end < -1) ? 1 : 0);
+
+ /* check boundaries ... */
+ if (start > max || end > max || start > end)
+ return (-1);
+
+ for (i = 0; i <= max; i++) {
+ char *temp;
+ if (h_on && (i == 1 || h_on > 1) &&
+ (temp = strrchr(arr[i], '/')))
+ *(temp + 1) = '\0';
+ if (t_on && (i == 1 || t_on > 1) &&
+ (temp = strrchr(arr[i], '/')))
+ (void) strcpy(arr[i], temp + 1);
+ if (r_on && (i == 1 || r_on > 1) &&
+ (temp = strrchr(arr[i], '.')))
+ *temp = '\0';
+ if (e_on && (i == 1 || e_on > 1) &&
+ (temp = strrchr(arr[i], '.')))
+ (void) strcpy(arr[i], temp);
+ }
+
+ cmdsize = 1, cmdlen = 0;
+ tempcmd = malloc(cmdsize);
+ for (i = start; start <= i && i <= end; i++) {
+ int arr_len;
+
+ arr_len = strlen(arr[i]);
+ if (cmdlen + arr_len + 1 >= cmdsize) {
+ cmdsize += arr_len + 1;
+ tempcmd = realloc(tempcmd, cmdsize);
+ }
+ (void) strcpy(&tempcmd[cmdlen], arr[i]); /* safe */
+ cmdlen += arr_len;
+ tempcmd[cmdlen++] = ' '; /* add a space */
+ }
+ while (cmdlen > 0 && isspace((unsigned char) tempcmd[cmdlen - 1]))
+ cmdlen--;
+ tempcmd[cmdlen] = '\0';
+
+ *result = tempcmd;
+
+ for (i = 0; i <= max; i++)
+ free(arr[i]);
+ free(arr), arr = (char **) NULL;
+ return (p_on) ? 2 : 1;
+}
+
+
+/*
+ * csh-style history expansion
+ */
+int
+history_expand(char *str, char **output)
+{
+ int i, retval = 0, idx;
+ size_t size;
+ char *temp, *result;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ *output = strdup(str); /* do it early */
+
+ if (str[0] == history_subst_char) {
+ /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
+ temp = alloca(4 + strlen(str) + 1);
+ temp[0] = temp[1] = history_expansion_char;
+ temp[2] = ':';
+ temp[3] = 's';
+ (void) strcpy(temp + 4, str);
+ str = temp;
+ }
+#define ADD_STRING(what, len) \
+ { \
+ if (idx + len + 1 > size) \
+ result = realloc(result, (size += len + 1)); \
+ (void)strncpy(&result[idx], what, len); \
+ idx += len; \
+ result[idx] = '\0'; \
+ }
+
+ result = NULL;
+ size = idx = 0;
+ for (i = 0; str[i];) {
+ int start, j, loop_again;
+ size_t len;
+
+ loop_again = 1;
+ start = j = i;
+loop:
+ for (; str[j]; j++) {
+ if (str[j] == '\\' &&
+ str[j + 1] == history_expansion_char) {
+ (void) strcpy(&str[j], &str[j + 1]);
+ continue;
+ }
+ if (!loop_again) {
+ if (str[j] == '?') {
+ while (str[j] && str[++j] != '?');
+ if (str[j] == '?')
+ j++;
+ } else if (isspace((unsigned char) str[j]))
+ break;
+ }
+ if (str[j] == history_expansion_char
+ && !strchr(history_no_expand_chars, str[j + 1])
+ && (!history_inhibit_expansion_function ||
+ (*history_inhibit_expansion_function)(str, j) == 0))
+ break;
+ }
+
+ if (str[j] && str[j + 1] != '#' && loop_again) {
+ i = j;
+ j++;
+ if (str[j] == history_expansion_char)
+ j++;
+ loop_again = 0;
+ goto loop;
+ }
+ len = i - start;
+ temp = &str[start];
+ ADD_STRING(temp, len);
+
+ if (str[i] == '\0' || str[i] != history_expansion_char
+ || str[i + 1] == '#') {
+ len = j - i;
+ temp = &str[i];
+ ADD_STRING(temp, len);
+ if (start == 0)
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ }
+ retval = _history_expand_command(&str[i], (size_t) (j - i),
+ &temp);
+ if (retval != -1) {
+ len = strlen(temp);
+ ADD_STRING(temp, len);
+ }
+ i = j;
+ } /* for(i ...) */
+
+ if (retval == 2) {
+ add_history(temp);
+#ifdef GDB_411_HACK
+ /* gdb 4.11 has been shipped with readline, where */
+ /* history_expand() returned -1 when the line */
+ /* should not be executed; in readline 2.1+ */
+ /* it should return 2 in such a case */
+ retval = -1;
+#endif
+ }
+ free(*output);
+ *output = result;
+
+ return (retval);
+}
+
+
+/*
+ * Parse the string into individual tokens, similarily to how shell would do it.
+ */
+char **
+history_tokenize(const char *str)
+{
+ int size = 1, result_idx = 0, i, start;
+ size_t len;
+ char **result = NULL, *temp, delim = '\0';
+
+ for (i = 0; str[i]; i++) {
+ while (isspace((unsigned char) str[i]))
+ i++;
+ start = i;
+ for (; str[i]; i++) {
+ if (str[i] == '\\') {
+ if (str[i+1] != '\0')
+ i++;
+ } else if (str[i] == delim)
+ delim = '\0';
+ else if (!delim &&
+ (isspace((unsigned char) str[i]) ||
+ strchr("()<>;&|$", str[i])))
+ break;
+ else if (!delim && strchr("'`\"", str[i]))
+ delim = str[i];
+ }
+
+ if (result_idx + 2 >= size) {
+ size <<= 1;
+ result = realloc(result, size * sizeof(char *));
+ }
+ len = i - start;
+ temp = malloc(len + 1);
+ (void) strncpy(temp, &str[start], len);
+ temp[len] = '\0';
+ result[result_idx++] = temp;
+ result[result_idx] = NULL;
+ }
+
+ return (result);
+}
+
+
+/*
+ * limit size of history record to ``max'' events
+ */
+void
+stifle_history(int max)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ if (history(h, &ev, H_SETSIZE, max) == 0)
+ max_input_history = max;
+}
+
+
+/*
+ * "unlimit" size of history - set the limit to maximum allowed int value
+ */
+int
+unstifle_history(void)
+{
+ HistEvent ev;
+ int omax;
+
+ history(h, &ev, H_SETSIZE, INT_MAX);
+ omax = max_input_history;
+ max_input_history = INT_MAX;
+ return (omax); /* some value _must_ be returned */
+}
+
+
+int
+history_is_stifled(void)
+{
+
+ /* cannot return true answer */
+ return (max_input_history != INT_MAX);
+}
+
+
+/*
+ * read history from a file given
+ */
+int
+read_history(const char *filename)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+ return (history(h, &ev, H_LOAD, filename));
+}
+
+
+/*
+ * write history to a file given
+ */
+int
+write_history(const char *filename)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+ return (history(h, &ev, H_SAVE, filename));
+}
+
+
+/*
+ * returns history ``num''th event
+ *
+ * returned pointer points to static variable
+ */
+HIST_ENTRY *
+history_get(int num)
+{
+ static HIST_ENTRY she;
+ HistEvent ev;
+ int i = 1, curr_num;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ /* rewind to beginning */
+ if (history(h, &ev, H_CURR) != 0)
+ return (NULL);
+ curr_num = ev.num;
+ if (history(h, &ev, H_LAST) != 0)
+ return (NULL); /* error */
+ while (i < num && history(h, &ev, H_PREV) == 0)
+ i++;
+ if (i != num)
+ return (NULL); /* not so many entries */
+
+ she.line = ev.str;
+ she.data = NULL;
+
+ /* rewind history to the same event it was before */
+ (void) history(h, &ev, H_FIRST);
+ (void) history(h, &ev, H_NEXT_EVENT, curr_num);
+
+ return (&she);
+}
+
+
+/*
+ * add the line to history table
+ */
+int
+add_history(const char *line)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ (void) history(h, &ev, H_ENTER, line);
+ if (history(h, &ev, H_GETSIZE) == 0)
+ history_length = ev.num;
+
+ return (!(history_length > 0)); /* return 0 if all is okay */
+}
+
+
+/*
+ * clear the history list - delete all entries
+ */
+void
+clear_history(void)
+{
+ HistEvent ev;
+
+ history(h, &ev, H_CLEAR);
+}
+
+
+/*
+ * returns offset of the current history event
+ */
+int
+where_history(void)
+{
+ HistEvent ev;
+ int curr_num, off;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (0);
+ curr_num = ev.num;
+
+ history(h, &ev, H_FIRST);
+ off = 1;
+ while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
+ off++;
+
+ return (off);
+}
+
+
+/*
+ * returns current history event or NULL if there is no such event
+ */
+HIST_ENTRY *
+current_history(void)
+{
+
+ return (_move_history(H_CURR));
+}
+
+
+/*
+ * returns total number of bytes history events' data are using
+ */
+int
+history_total_bytes(void)
+{
+ HistEvent ev;
+ int curr_num, size;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ history(h, &ev, H_FIRST);
+ size = 0;
+ do
+ size += strlen(ev.str);
+ while (history(h, &ev, H_NEXT) == 0);
+
+ /* get to the same position as before */
+ history(h, &ev, H_PREV_EVENT, curr_num);
+
+ return (size);
+}
+
+
+/*
+ * sets the position in the history list to ``pos''
+ */
+int
+history_set_pos(int pos)
+{
+ HistEvent ev;
+ int off, curr_num;
+
+ if (pos > history_length || pos < 0)
+ return (-1);
+
+ history(h, &ev, H_CURR);
+ curr_num = ev.num;
+ history(h, &ev, H_FIRST);
+ off = 0;
+ while (off < pos && history(h, &ev, H_NEXT) == 0)
+ off++;
+
+ if (off != pos) { /* do a rollback in case of error */
+ history(h, &ev, H_FIRST);
+ history(h, &ev, H_NEXT_EVENT, curr_num);
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * returns previous event in history and shifts pointer accordingly
+ */
+HIST_ENTRY *
+previous_history(void)
+{
+
+ return (_move_history(H_PREV));
+}
+
+
+/*
+ * returns next event in history and shifts pointer accordingly
+ */
+HIST_ENTRY *
+next_history(void)
+{
+
+ return (_move_history(H_NEXT));
+}
+
+
+/*
+ * generic history search function
+ */
+static int
+_history_search_gen(const char *str, int direction, int pos)
+{
+ HistEvent ev;
+ const char *strp;
+ int curr_num;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ for (;;) {
+ strp = strstr(ev.str, str);
+ if (strp && (pos < 0 || &ev.str[pos] == strp))
+ return (int) (strp - ev.str);
+ if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0)
+ break;
+ }
+
+ history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
+
+ return (-1);
+}
+
+
+/*
+ * searches for first history event containing the str
+ */
+int
+history_search(const char *str, int direction)
+{
+
+ return (_history_search_gen(str, direction, -1));
+}
+
+
+/*
+ * searches for first history event beginning with str
+ */
+int
+history_search_prefix(const char *str, int direction)
+{
+
+ return (_history_search_gen(str, direction, 0));
+}
+
+
+/*
+ * search for event in history containing str, starting at offset
+ * abs(pos); continue backward, if pos<0, forward otherwise
+ */
+/* ARGSUSED */
+int
+history_search_pos(const char *str, int direction, int pos)
+{
+ HistEvent ev;
+ int curr_num, off;
+
+ off = (pos > 0) ? pos : -pos;
+ pos = (pos > 0) ? 1 : -1;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
+ return (-1);
+
+
+ for (;;) {
+ if (strstr(ev.str, str))
+ return (off);
+ if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
+ break;
+ }
+
+ /* set "current" pointer back to previous state */
+ history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
+
+ return (-1);
+}
+
+
+/********************************/
+/* completition functions */
+
+/*
+ * does tilde expansion of strings of type ``~user/foo''
+ * if ``user'' isn't valid user name or ``txt'' doesn't start
+ * w/ '~', returns pointer to strdup()ed copy of ``txt''
+ *
+ * it's callers's responsibility to free() returned string
+ */
+char *
+tilde_expand(char *txt)
+{
+ struct passwd *pass;
+ char *temp;
+ size_t len = 0;
+
+ if (txt[0] != '~')
+ return (strdup(txt));
+
+ temp = strchr(txt + 1, '/');
+ if (temp == NULL)
+ temp = strdup(txt + 1);
+ else {
+ len = temp - txt + 1; /* text until string after slash */
+ temp = malloc(len);
+ (void) strncpy(temp, txt + 1, len - 2);
+ temp[len - 2] = '\0';
+ }
+ pass = getpwnam(temp);
+ free(temp); /* value no more needed */
+ if (pass == NULL)
+ return (strdup(txt));
+
+ /* update pointer txt to point at string immedially following */
+ /* first slash */
+ txt += len;
+
+ temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
+ (void) sprintf(temp, "%s/%s", pass->pw_dir, txt);
+
+ return (temp);
+}
+
+
+/*
+ * return first found file name starting by the ``text'' or NULL if no
+ * such file can be found.
+ * The first ``state'' matches are ignored.
+ *
+ * it's caller's responsibility to free returned string
+ */
+char *
+filename_completion_function(const char *text, int state)
+{
+ DIR *dir = NULL;
+ char *filename = NULL, *dirname = NULL;
+ size_t filename_len = 0;
+ struct dirent *entry;
+ char *temp;
+ size_t len;
+ int count = 0;
+
+ temp = strrchr(text, '/');
+ if (temp) {
+ temp++;
+ filename = realloc(filename, strlen(temp) + 1);
+ (void) strcpy(filename, temp);
+ len = temp - text; /* including last slash */
+ dirname = realloc(dirname, len + 1);
+ (void) strncpy(dirname, text, len);
+ dirname[len] = '\0';
+ } else {
+ filename = strdup(text);
+ dirname = NULL;
+ }
+
+ /* support for ``~user'' syntax */
+ if (dirname && *dirname == '~') {
+ temp = tilde_expand(dirname);
+ dirname = realloc(dirname, strlen(temp) + 1);
+ (void) strcpy(dirname, temp); /* safe */
+ free(temp); /* no longer needed */
+ }
+ /* will be used in cycle */
+ filename_len = strlen(filename);
+
+ dir = opendir(dirname ? dirname : ".");
+ if (!dir)
+ return (NULL); /* cannot open the directory */
+
+ /* find the match */
+ while ((entry = readdir(dir)) != NULL) {
+ /* otherwise, get first entry where first */
+ /* filename_len characters are equal */
+ if (
+#if defined(__SVR4) || defined(__linux__)
+ strlen(entry->d_name) >= filename_len
+#else
+ entry->d_namlen >= filename_len
+#endif
+ && strncmp(entry->d_name, filename,
+ filename_len) == 0
+ && (state-- == 0))
+ break;
+ }
+
+ if (entry) { /* match found */
+
+ struct stat stbuf;
+#if defined(__SVR4) || defined(__linux__)
+ len = strlen(entry->d_name) +
+#else
+ len = entry->d_namlen +
+#endif
+ ((dirname) ? strlen(dirname) : 0) + 1 + 1;
+ temp = malloc(len);
+ (void) sprintf(temp, "%s%s",
+ dirname ? dirname : "", entry->d_name); /* safe */
+
+ /* test, if it's directory */
+ if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
+ strcat(temp, "/"); /* safe */
+ } else
+ temp = NULL;
+ closedir(dir);
+
+ return (temp);
+}
+
+
+/*
+ * a completion generator for usernames; returns _first_ username
+ * which starts with supplied text
+ * text contains a partial username preceded by random character
+ * (usually '~'); state is ignored
+ * it's callers responsibility to free returned value
+ */
+char *
+username_completion_function(const char *text, int state)
+{
+ struct passwd *pwd;
+
+ if (text[0] == '\0')
+ return (NULL);
+
+ if (*text == '~')
+ text++;
+
+ if (state == 0)
+ setpwent();
+
+ while ((pwd = getpwent()) && text[0] == pwd->pw_name[0]
+ && strcmp(text, pwd->pw_name) == 0);
+
+ if (pwd == NULL) {
+ endpwent();
+ return (NULL);
+ }
+ return (strdup(pwd->pw_name));
+}
+
+
+/*
+ * el-compatible wrapper around rl_complete; needed for key binding
+ */
+/* ARGSUSED */
+static unsigned char
+_el_rl_complete(EditLine *el, int ch)
+{
+ return (unsigned char) rl_complete(0, ch);
+}
+
+
+/*
+ * returns list of completitions for text given
+ */
+char **
+completion_matches(const char *text, CPFunction *genfunc)
+{
+ char **match_list = NULL, *retstr, *prevstr;
+ size_t match_list_len, max_equal, which, i;
+ int matches;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ matches = 0;
+ match_list_len = 1;
+ while ((retstr = (*genfunc) (text, matches)) != NULL) {
+ if (matches + 1 >= match_list_len) {
+ match_list_len <<= 1;
+ match_list = realloc(match_list,
+ match_list_len * sizeof(char *));
+ }
+ match_list[++matches] = retstr;
+ }
+
+ if (!match_list)
+ return (char **) NULL; /* nothing found */
+
+ /* find least denominator and insert it to match_list[0] */
+ which = 2;
+ prevstr = match_list[1];
+ max_equal = strlen(prevstr);
+ for (; which <= matches; which++) {
+ for (i = 0; i < max_equal &&
+ prevstr[i] == match_list[which][i]; i++)
+ continue;
+ max_equal = i;
+ }
+
+ retstr = malloc(max_equal + 1);
+ (void) strncpy(retstr, match_list[1], max_equal);
+ retstr[max_equal] = '\0';
+ match_list[0] = retstr;
+
+ /* add NULL as last pointer to the array */
+ if (matches + 1 >= match_list_len)
+ match_list = realloc(match_list,
+ (match_list_len + 1) * sizeof(char *));
+ match_list[matches + 1] = (char *) NULL;
+
+ return (match_list);
+}
+
+/*
+ * Sort function for qsort(). Just wrapper around strcasecmp().
+ */
+static int
+_rl_qsort_string_compare(i1, i2)
+ const void *i1, *i2;
+{
+ /* LINTED const castaway */
+ const char *s1 = ((const char **)i1)[0];
+ /* LINTED const castaway */
+ const char *s2 = ((const char **)i2)[0];
+
+ return strcasecmp(s1, s2);
+}
+
+/*
+ * Display list of strings in columnar format on readline's output stream.
+ * 'matches' is list of strings, 'len' is number of strings in 'matches',
+ * 'max' is maximum length of string in 'matches'.
+ */
+void
+rl_display_match_list (matches, len, max)
+ char **matches;
+ int len, max;
+{
+ int i, idx, limit, count;
+ int screenwidth = e->el_term.t_size.h;
+
+ /*
+ * Find out how many entries can be put on one line, count
+ * with two spaces between strings.
+ */
+ limit = screenwidth / (max + 2);
+ if (limit == 0)
+ limit = 1;
+
+ /* how many lines of output */
+ count = len / limit;
+ if (count * limit < len)
+ count++;
+
+ /* Sort the items if they are not already sorted. */
+ qsort(&matches[1], (size_t)(len - 1), sizeof(char *),
+ _rl_qsort_string_compare);
+
+ idx = 1;
+ for(; count > 0; count--) {
+ for(i=0; i < limit && matches[idx]; i++, idx++)
+ fprintf(e->el_outfile, "%-*s ", max, matches[idx]);
+ fprintf(e->el_outfile, "\n");
+ }
+}
+
+/*
+ * Complete the word at or before point, called by rl_complete()
+ * 'what_to_do' says what to do with the completion.
+ * `?' means list the possible completions.
+ * TAB means do standard completion.
+ * `*' means insert all of the possible completions.
+ * `!' means to do standard completion, and list all possible completions if
+ * there is more than one.
+ *
+ * Note: '*' support is not implemented
+ */
+static int
+rl_complete_internal(int what_to_do)
+{
+ CPFunction *complet_func;
+ const LineInfo *li;
+ char *temp, **matches;
+ const char *ctemp;
+ size_t len;
+
+ rl_completion_type = what_to_do;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ complet_func = rl_completion_entry_function;
+ if (!complet_func)
+ complet_func = filename_completion_function;
+
+ /* We now look backwards for the start of a filename/variable word */
+ li = el_line(e);
+ ctemp = (const char *) li->cursor;
+ while (ctemp > li->buffer
+ && !strchr(rl_basic_word_break_characters, ctemp[-1])
+ && (!rl_special_prefixes
+ || !strchr(rl_special_prefixes, ctemp[-1]) ) )
+ ctemp--;
+
+ len = li->cursor - ctemp;
+ temp = alloca(len + 1);
+ (void) strncpy(temp, ctemp, len);
+ temp[len] = '\0';
+
+ /* these can be used by function called in completion_matches() */
+ /* or (*rl_attempted_completion_function)() */
+ rl_point = li->cursor - li->buffer;
+ rl_end = li->lastchar - li->buffer;
+
+ if (!rl_attempted_completion_function)
+ matches = completion_matches(temp, complet_func);
+ else {
+ int end = li->cursor - li->buffer;
+ matches = (*rl_attempted_completion_function) (temp, (int)
+ (end - len), end);
+ }
+
+ if (matches) {
+ int i, retval = CC_REFRESH;
+ int matches_num, maxlen, match_len, match_display=1;
+
+ /*
+ * Only replace the completed string with common part of
+ * possible matches if there is possible completion.
+ */
+ if (matches[0][0] != '\0') {
+ el_deletestr(e, (int) len);
+ el_insertstr(e, matches[0]);
+ }
+
+ if (what_to_do == '?')
+ goto display_matches;
+
+ if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {
+ /*
+ * We found exact match. Add a space after
+ * it, unless we do filename completition and the
+ * object is a directory.
+ */
+ size_t alen = strlen(matches[0]);
+ if ((complet_func != filename_completion_function
+ || (alen > 0 && (matches[0])[alen - 1] != '/'))
+ && rl_completion_append_character) {
+ char buf[2];
+ buf[0] = rl_completion_append_character;
+ buf[1] = '\0';
+ el_insertstr(e, buf);
+ }
+ } else if (what_to_do == '!') {
+ display_matches:
+ /*
+ * More than one match and requested to list possible
+ * matches.
+ */
+
+ for(i=1, maxlen=0; matches[i]; i++) {
+ match_len = strlen(matches[i]);
+ if (match_len > maxlen)
+ maxlen = match_len;
+ }
+ matches_num = i - 1;
+
+ /* newline to get on next line from command line */
+ fprintf(e->el_outfile, "\n");
+
+ /*
+ * If there are too many items, ask user for display
+ * confirmation.
+ */
+ if (matches_num > rl_completion_query_items) {
+ fprintf(e->el_outfile,
+ "Display all %d possibilities? (y or n) ",
+ matches_num);
+ fflush(e->el_outfile);
+ if (getc(stdin) != 'y')
+ match_display = 0;
+ fprintf(e->el_outfile, "\n");
+ }
+
+ if (match_display)
+ rl_display_match_list(matches, matches_num,
+ maxlen);
+ retval = CC_REDISPLAY;
+ } else if (matches[0][0]) {
+ /*
+ * There was some common match, but the name was
+ * not complete enough. Next tab will print possible
+ * completions.
+ */
+ el_beep(e);
+ } else {
+ /* lcd is not a valid object - further specification */
+ /* is needed */
+ el_beep(e);
+ retval = CC_NORM;
+ }
+
+ /* free elements of array and the array itself */
+ for (i = 0; matches[i]; i++)
+ free(matches[i]);
+ free(matches), matches = NULL;
+
+ return (retval);
+ }
+ return (CC_NORM);
+}
+
+
+/*
+ * complete word at current point
+ */
+int
+rl_complete(int ignore, int invoking_key)
+{
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ if (rl_inhibit_completion) {
+ rl_insert(ignore, invoking_key);
+ return (CC_REFRESH);
+ } else if (e->el_state.lastcmd == el_rl_complete_cmdnum)
+ return rl_complete_internal('?');
+ else if (_rl_complete_show_all)
+ return rl_complete_internal('!');
+ else
+ return (rl_complete_internal(TAB));
+}
+
+
+/*
+ * misc other functions
+ */
+
+/*
+ * bind key c to readline-type function func
+ */
+int
+rl_bind_key(int c, int func(int, int))
+{
+ int retval = -1;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ if (func == rl_insert) {
+ /* XXX notice there is no range checking of ``c'' */
+ e->el_map.key[c] = ED_INSERT;
+ retval = 0;
+ }
+ return (retval);
+}
+
+
+/*
+ * read one key from input - handles chars pushed back
+ * to input stream also
+ */
+int
+rl_read_key(void)
+{
+ char fooarr[2 * sizeof(int)];
+
+ if (e == NULL || h == NULL)
+ rl_initialize();
+
+ return (el_getc(e, fooarr));
+}
+
+
+/*
+ * reset the terminal
+ */
+/* ARGSUSED */
+void
+rl_reset_terminal(const char *p)
+{
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+ el_reset(e);
+}
+
+
+/*
+ * insert character ``c'' back into input stream, ``count'' times
+ */
+int
+rl_insert(int count, int c)
+{
+ char arr[2];
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ /* XXX - int -> char conversion can lose on multichars */
+ arr[0] = c;
+ arr[1] = '\0';
+
+ for (; count > 0; count--)
+ el_push(e, arr);
+
+ return (0);
+}
diff --git a/trunk/main/editline/readline/readline.h b/trunk/main/editline/readline/readline.h
new file mode 100644
index 000000000..7485dde40
--- /dev/null
+++ b/trunk/main/editline/readline/readline.h
@@ -0,0 +1,118 @@
+/* $NetBSD: readline.h,v 1.1 2001/01/05 21:15:50 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jaromir Dolecek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _READLINE_H_
+#define _READLINE_H_
+
+#include <sys/types.h>
+
+/* list of readline stuff supported by editline library's readline wrapper */
+
+/* typedefs */
+typedef int Function(const char *, int);
+typedef void VFunction(void);
+typedef char *CPFunction(const char *, int);
+typedef char **CPPFunction(const char *, int, int);
+
+typedef struct _hist_entry {
+ const char *line;
+ const char *data;
+} HIST_ENTRY;
+
+/* global variables used by readline enabled applications */
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern const char *rl_library_version;
+extern char *rl_readline_name;
+extern FILE *rl_instream;
+extern FILE *rl_outstream;
+extern char *rl_line_buffer;
+extern int rl_point, rl_end;
+extern int history_base, history_length;
+extern int max_input_history;
+extern char *rl_basic_word_break_characters;
+extern char *rl_completer_word_break_characters;
+extern char *rl_completer_quote_characters;
+extern CPFunction *rl_completion_entry_function;
+extern CPPFunction *rl_attempted_completion_function;
+extern int rl_completion_type;
+extern int rl_completion_query_items;
+extern char *rl_special_prefixes;
+extern int rl_completion_append_character;
+
+/* supported functions */
+char *readline(const char *);
+int rl_initialize(void);
+
+void using_history(void);
+int add_history(const char *);
+void clear_history(void);
+void stifle_history(int);
+int unstifle_history(void);
+int history_is_stifled(void);
+int where_history(void);
+HIST_ENTRY *current_history(void);
+HIST_ENTRY *history_get(int);
+int history_total_bytes(void);
+int history_set_pos(int);
+HIST_ENTRY *previous_history(void);
+HIST_ENTRY *next_history(void);
+int history_search(const char *, int);
+int history_search_prefix(const char *, int);
+int history_search_pos(const char *, int, int);
+int read_history(const char *);
+int write_history(const char *);
+int history_expand(char *, char **);
+char **history_tokenize(const char *);
+
+char *tilde_expand(char *);
+char *filename_completion_function(const char *, int);
+char *username_completion_function(const char *, int);
+int rl_complete(int, int);
+int rl_read_key(void);
+char **completion_matches(const char *, CPFunction *);
+void rl_display_match_list(char **, int, int);
+
+int rl_insert(int, int);
+void rl_reset_terminal(const char *);
+int rl_bind_key(int, int (*)(int, int));
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _READLINE_H_ */
diff --git a/trunk/main/editline/refresh.c b/trunk/main/editline/refresh.c
new file mode 100644
index 000000000..fcebe1253
--- /dev/null
+++ b/trunk/main/editline/refresh.c
@@ -0,0 +1,1104 @@
+/* $NetBSD: refresh.c,v 1.18 2002/03/18 16:00:58 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: refresh.c,v 1.18 2002/03/18 16:00:58 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * refresh.c: Lower level screen refreshing functions
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "el.h"
+
+private void re_addc(EditLine *, int);
+private void re_update_line(EditLine *, char *, char *, int);
+private void re_insert (EditLine *, char *, int, int, char *, int);
+private void re_delete(EditLine *, char *, int, int, int);
+private void re_fastputc(EditLine *, int);
+private void re__strncopy(char *, char *, size_t);
+private void re__copy_and_pad(char *, const char *, size_t);
+
+#ifdef DEBUG_REFRESH
+private void re_printstr(EditLine *, char *, char *, char *);
+#define __F el->el_errfile
+#define ELRE_ASSERT(a, b, c) do \
+ if (a) { \
+ (void) fprintf b; \
+ c; \
+ } \
+ while (0)
+#define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;)
+
+/* re_printstr():
+ * Print a string on the debugging pty
+ */
+private void
+re_printstr(EditLine *el, char *str, char *f, char *t)
+{
+
+ ELRE_DEBUG(1, (__F, "%s:\"", str));
+ while (f < t)
+ ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
+ ELRE_DEBUG(1, (__F, "\"\r\n"));
+}
+#else
+#define ELRE_ASSERT(a, b, c)
+#define ELRE_DEBUG(a, b)
+#endif
+
+
+/* re_addc():
+ * Draw c, expanding tabs, control chars etc.
+ */
+private void
+re_addc(EditLine *el, int c)
+{
+
+ if (isprint(c)) {
+ re_putc(el, c, 1);
+ return;
+ }
+ if (c == '\n') { /* expand the newline */
+ int oldv = el->el_refresh.r_cursor.v;
+ re_putc(el, '\0', 0); /* assure end of line */
+ if (oldv == el->el_refresh.r_cursor.v) { /* XXX */
+ el->el_refresh.r_cursor.h = 0; /* reset cursor pos */
+ el->el_refresh.r_cursor.v++;
+ }
+ return;
+ }
+ if (c == '\t') { /* expand the tab */
+ for (;;) {
+ re_putc(el, ' ', 1);
+ if ((el->el_refresh.r_cursor.h & 07) == 0)
+ break; /* go until tab stop */
+ }
+ } else if (iscntrl(c)) {
+ re_putc(el, '^', 1);
+ if (c == '\177')
+ re_putc(el, '?', 1);
+ else
+ /* uncontrolify it; works only for iso8859-1 like sets */
+ re_putc(el, (c | 0100), 1);
+ } else {
+ re_putc(el, '\\', 1);
+ re_putc(el, (int) ((((unsigned int) c >> 6) & 07) + '0'), 1);
+ re_putc(el, (int) ((((unsigned int) c >> 3) & 07) + '0'), 1);
+ re_putc(el, (c & 07) + '0', 1);
+ }
+}
+
+
+/* re_putc():
+ * Draw the character given
+ */
+protected void
+re_putc(EditLine *el, int c, int shift)
+{
+
+ ELRE_DEBUG(1, (__F, "printing %3.3o '%c'\r\n", c, c));
+
+ el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_refresh.r_cursor.h] = c;
+ if (!shift)
+ return;
+
+ el->el_refresh.r_cursor.h++; /* advance to next place */
+ if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) {
+ el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h] = '\0';
+ /* assure end of line */
+ el->el_refresh.r_cursor.h = 0; /* reset it. */
+
+ /*
+ * If we would overflow (input is longer than terminal size),
+ * emulate scroll by dropping first line and shuffling the rest.
+ * We do this via pointer shuffling - it's safe in this case
+ * and we avoid memcpy().
+ */
+ if (el->el_refresh.r_cursor.v + 1 >= el->el_term.t_size.v) {
+ int i, lins = el->el_term.t_size.v;
+ char *firstline = el->el_vdisplay[0];
+
+ for(i=1; i < lins; i++)
+ el->el_vdisplay[i-1] = el->el_vdisplay[i];
+
+ firstline[0] = '\0'; /* empty the string */
+ el->el_vdisplay[i-1] = firstline;
+ } else
+ el->el_refresh.r_cursor.v++;
+
+ ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_term.t_size.v,
+ (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
+ el->el_refresh.r_cursor.v, el->el_term.t_size.v),
+ abort());
+ }
+}
+
+
+/* re_refresh():
+ * draws the new virtual screen image from the current input
+ * line, then goes line-by-line changing the real image to the new
+ * virtual image. The routine to re-draw a line can be replaced
+ * easily in hopes of a smarter one being placed there.
+ */
+protected void
+re_refresh(EditLine *el)
+{
+ int i, rhdiff;
+ char *cp, *st;
+ coord_t cur;
+#ifdef notyet
+ size_t termsz;
+#endif
+
+ ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n",
+ el->el_line.buffer));
+
+ /* reset the Drawing cursor */
+ el->el_refresh.r_cursor.h = 0;
+ el->el_refresh.r_cursor.v = 0;
+
+ /* temporarily draw rprompt to calculate its size */
+ prompt_print(el, EL_RPROMPT);
+
+ /* reset the Drawing cursor */
+ el->el_refresh.r_cursor.h = 0;
+ el->el_refresh.r_cursor.v = 0;
+
+ cur.h = -1; /* set flag in case I'm not set */
+ cur.v = 0;
+
+ prompt_print(el, EL_PROMPT);
+
+ /* draw the current input buffer */
+#ifdef notyet
+ termsz = el->el_term.t_size.h * el->el_term.t_size.v;
+ if (el->el_line.lastchar - el->el_line.buffer > termsz) {
+ /*
+ * If line is longer than terminal, process only part
+ * of line which would influence display.
+ */
+ size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
+
+ st = el->el_line.lastchar - rem
+ - (termsz - (((rem / el->el_term.t_size.v) - 1)
+ * el->el_term.t_size.v));
+ } else
+#endif
+ st = el->el_line.buffer;
+
+ for (cp = st; cp < el->el_line.lastchar; cp++) {
+ if (cp == el->el_line.cursor) {
+ /* save for later */
+ cur.h = el->el_refresh.r_cursor.h;
+ cur.v = el->el_refresh.r_cursor.v;
+ }
+ re_addc(el, (unsigned char) *cp);
+ }
+
+ if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */
+ cur.h = el->el_refresh.r_cursor.h;
+ cur.v = el->el_refresh.r_cursor.v;
+ }
+ rhdiff = el->el_term.t_size.h - el->el_refresh.r_cursor.h -
+ el->el_rprompt.p_pos.h;
+ if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
+ !el->el_refresh.r_cursor.v && rhdiff > 1) {
+ /*
+ * have a right-hand side prompt that will fit
+ * on the end of the first line with at least
+ * one character gap to the input buffer.
+ */
+ while (--rhdiff > 0) /* pad out with spaces */
+ re_putc(el, ' ', 1);
+ prompt_print(el, EL_RPROMPT);
+ } else {
+ el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */
+ el->el_rprompt.p_pos.v = 0;
+ }
+
+ re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */
+
+ el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
+
+ ELRE_DEBUG(1, (__F,
+ "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
+ el->el_term.t_size.h, el->el_refresh.r_cursor.h,
+ el->el_refresh.r_cursor.v, el->el_vdisplay[0]));
+
+ ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
+ for (i = 0; i <= el->el_refresh.r_newcv; i++) {
+ /* NOTE THAT re_update_line MAY CHANGE el_display[i] */
+ re_update_line(el, el->el_display[i], el->el_vdisplay[i], i);
+
+ /*
+ * Copy the new line to be the current one, and pad out with
+ * spaces to the full width of the terminal so that if we try
+ * moving the cursor by writing the character that is at the
+ * end of the screen line, it won't be a NUL or some old
+ * leftover stuff.
+ */
+ re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
+ (size_t) el->el_term.t_size.h);
+ }
+ ELRE_DEBUG(1, (__F,
+ "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
+ el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
+
+ if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
+ for (; i <= el->el_refresh.r_oldcv; i++) {
+ term_move_to_line(el, i);
+ term_move_to_char(el, 0);
+ term_clear_EOL(el, (int) strlen(el->el_display[i]));
+#ifdef DEBUG_REFRESH
+ term_overwrite(el, "C\b", 2);
+#endif /* DEBUG_REFRESH */
+ el->el_display[i][0] = '\0';
+ }
+
+ el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
+ ELRE_DEBUG(1, (__F,
+ "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
+ el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
+ cur.h, cur.v));
+ term_move_to_line(el, cur.v); /* go to where the cursor is */
+ term_move_to_char(el, cur.h);
+}
+
+
+/* re_goto_bottom():
+ * used to go to last used screen line
+ */
+protected void
+re_goto_bottom(EditLine *el)
+{
+
+ term_move_to_line(el, el->el_refresh.r_oldcv);
+ term__putc('\r');
+ term__putc('\n');
+ re_clear_display(el);
+ term__flush();
+}
+
+
+/* re_insert():
+ * insert num characters of s into d (in front of the character)
+ * at dat, maximum length of d is dlen
+ */
+private void
+/*ARGSUSED*/
+re_insert(EditLine *el, char *d, int dat, int dlen, char *s, int num)
+{
+ char *a, *b;
+
+ if (num <= 0)
+ return;
+ if (num > dlen - dat)
+ num = dlen - dat;
+
+ ELRE_DEBUG(1,
+ (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
+
+ /* open up the space for num chars */
+ if (num > 0) {
+ b = d + dlen - 1;
+ a = b - num;
+ while (a >= &d[dat])
+ *b-- = *a--;
+ d[dlen] = '\0'; /* just in case */
+ }
+ ELRE_DEBUG(1, (__F,
+ "re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
+
+ /* copy the characters */
+ for (a = d + dat; (a < d + dlen) && (num > 0); num--)
+ *a++ = *s++;
+
+ ELRE_DEBUG(1,
+ (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
+ num, dat, dlen, d, s));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
+}
+
+
+/* re_delete():
+ * delete num characters d at dat, maximum length of d is dlen
+ */
+private void
+/*ARGSUSED*/
+re_delete(EditLine *el, char *d, int dat, int dlen, int num)
+{
+ char *a, *b;
+
+ if (num <= 0)
+ return;
+ if (dat + num >= dlen) {
+ d[dat] = '\0';
+ return;
+ }
+ ELRE_DEBUG(1,
+ (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+
+ /* open up the space for num chars */
+ if (num > 0) {
+ b = d + dat;
+ a = b + num;
+ while (a < &d[dlen])
+ *b++ = *a++;
+ d[dlen] = '\0'; /* just in case */
+ }
+ ELRE_DEBUG(1,
+ (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+}
+
+
+/* re__strncopy():
+ * Like strncpy without padding.
+ */
+private void
+re__strncopy(char *a, char *b, size_t n)
+{
+
+ while (n-- && *b)
+ *a++ = *b++;
+}
+
+
+/*****************************************************************
+ re_update_line() is based on finding the middle difference of each line
+ on the screen; vis:
+
+ /old first difference
+ /beginning of line | /old last same /old EOL
+ v v v v
+old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
+new: eddie> Oh, my little buggy says to me, as lurgid as
+ ^ ^ ^ ^
+ \beginning of line | \new last same \new end of line
+ \new first difference
+
+ all are character pointers for the sake of speed. Special cases for
+ no differences, as well as for end of line additions must be handled.
+**************************************************************** */
+
+/* Minimum at which doing an insert it "worth it". This should be about
+ * half the "cost" of going into insert mode, inserting a character, and
+ * going back out. This should really be calculated from the termcap
+ * data... For the moment, a good number for ANSI terminals.
+ */
+#define MIN_END_KEEP 4
+
+private void
+re_update_line(EditLine *el, char *old, char *new, int i)
+{
+ char *o, *n, *p, c;
+ char *ofd, *ols, *oe, *nfd, *nls, *ne;
+ char *osb, *ose, *nsb, *nse;
+ int fx, sx;
+
+ /*
+ * find first diff
+ */
+ for (o = old, n = new; *o && (*o == *n); o++, n++)
+ continue;
+ ofd = o;
+ nfd = n;
+
+ /*
+ * Find the end of both old and new
+ */
+ while (*o)
+ o++;
+ /*
+ * Remove any trailing blanks off of the end, being careful not to
+ * back up past the beginning.
+ */
+ while (ofd < o) {
+ if (o[-1] != ' ')
+ break;
+ o--;
+ }
+ oe = o;
+ *oe = '\0';
+
+ while (*n)
+ n++;
+
+ /* remove blanks from end of new */
+ while (nfd < n) {
+ if (n[-1] != ' ')
+ break;
+ n--;
+ }
+ ne = n;
+ *ne = '\0';
+
+ /*
+ * if no diff, continue to next line of redraw
+ */
+ if (*ofd == '\0' && *nfd == '\0') {
+ ELRE_DEBUG(1, (__F, "no difference.\r\n"));
+ return;
+ }
+ /*
+ * find last same pointer
+ */
+ while ((o > ofd) && (n > nfd) && (*--o == *--n))
+ continue;
+ ols = ++o;
+ nls = ++n;
+
+ /*
+ * find same begining and same end
+ */
+ osb = ols;
+ nsb = nls;
+ ose = ols;
+ nse = nls;
+
+ /*
+ * case 1: insert: scan from nfd to nls looking for *ofd
+ */
+ if (*ofd) {
+ for (c = *ofd, n = nfd; n < nls; n++) {
+ if (c == *n) {
+ for (o = ofd, p = n;
+ p < nls && o < ols && *o == *p;
+ o++, p++)
+ continue;
+ /*
+ * if the new match is longer and it's worth
+ * keeping, then we take it
+ */
+ if (((nse - nsb) < (p - n)) &&
+ (2 * (p - n) > n - nfd)) {
+ nsb = n;
+ nse = p;
+ osb = ofd;
+ ose = o;
+ }
+ }
+ }
+ }
+ /*
+ * case 2: delete: scan from ofd to ols looking for *nfd
+ */
+ if (*nfd) {
+ for (c = *nfd, o = ofd; o < ols; o++) {
+ if (c == *o) {
+ for (n = nfd, p = o;
+ p < ols && n < nls && *p == *n;
+ p++, n++)
+ continue;
+ /*
+ * if the new match is longer and it's worth
+ * keeping, then we take it
+ */
+ if (((ose - osb) < (p - o)) &&
+ (2 * (p - o) > o - ofd)) {
+ nsb = nfd;
+ nse = n;
+ osb = o;
+ ose = p;
+ }
+ }
+ }
+ }
+ /*
+ * Pragmatics I: If old trailing whitespace or not enough characters to
+ * save to be worth it, then don't save the last same info.
+ */
+ if ((oe - ols) < MIN_END_KEEP) {
+ ols = oe;
+ nls = ne;
+ }
+ /*
+ * Pragmatics II: if the terminal isn't smart enough, make the data
+ * dumber so the smart update doesn't try anything fancy
+ */
+
+ /*
+ * fx is the number of characters we need to insert/delete: in the
+ * beginning to bring the two same begins together
+ */
+ fx = (nsb - nfd) - (osb - ofd);
+ /*
+ * sx is the number of characters we need to insert/delete: in the
+ * end to bring the two same last parts together
+ */
+ sx = (nls - nse) - (ols - ose);
+
+ if (!EL_CAN_INSERT) {
+ if (fx > 0) {
+ osb = ols;
+ ose = ols;
+ nsb = nls;
+ nse = nls;
+ }
+ if (sx > 0) {
+ ols = oe;
+ nls = ne;
+ }
+ if ((ols - ofd) < (nls - nfd)) {
+ ols = oe;
+ nls = ne;
+ }
+ }
+ if (!EL_CAN_DELETE) {
+ if (fx < 0) {
+ osb = ols;
+ ose = ols;
+ nsb = nls;
+ nse = nls;
+ }
+ if (sx < 0) {
+ ols = oe;
+ nls = ne;
+ }
+ if ((ols - ofd) > (nls - nfd)) {
+ ols = oe;
+ nls = ne;
+ }
+ }
+ /*
+ * Pragmatics III: make sure the middle shifted pointers are correct if
+ * they don't point to anything (we may have moved ols or nls).
+ */
+ /* if the change isn't worth it, don't bother */
+ /* was: if (osb == ose) */
+ if ((ose - osb) < MIN_END_KEEP) {
+ osb = ols;
+ ose = ols;
+ nsb = nls;
+ nse = nls;
+ }
+ /*
+ * Now that we are done with pragmatics we recompute fx, sx
+ */
+ fx = (nsb - nfd) - (osb - ofd);
+ sx = (nls - nse) - (ols - ose);
+
+ ELRE_DEBUG(1, (__F, "\n"));
+ ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
+ ofd - old, osb - old, ose - old, ols - old, oe - old));
+ ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
+ nfd - new, nsb - new, nse - new, nls - new, ne - new));
+ ELRE_DEBUG(1, (__F,
+ "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
+ ELRE_DEBUG(1, (__F,
+ "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
+#ifdef DEBUG_REFRESH
+ re_printstr(el, "old- oe", old, oe);
+ re_printstr(el, "new- ne", new, ne);
+ re_printstr(el, "old-ofd", old, ofd);
+ re_printstr(el, "new-nfd", new, nfd);
+ re_printstr(el, "ofd-osb", ofd, osb);
+ re_printstr(el, "nfd-nsb", nfd, nsb);
+ re_printstr(el, "osb-ose", osb, ose);
+ re_printstr(el, "nsb-nse", nsb, nse);
+ re_printstr(el, "ose-ols", ose, ols);
+ re_printstr(el, "nse-nls", nse, nls);
+ re_printstr(el, "ols- oe", ols, oe);
+ re_printstr(el, "nls- ne", nls, ne);
+#endif /* DEBUG_REFRESH */
+
+ /*
+ * el_cursor.v to this line i MUST be in this routine so that if we
+ * don't have to change the line, we don't move to it. el_cursor.h to
+ * first diff char
+ */
+ term_move_to_line(el, i);
+
+ /*
+ * at this point we have something like this:
+ *
+ * /old /ofd /osb /ose /ols /oe
+ * v.....................v v..................v v........v
+ * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
+ * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
+ * ^.....................^ ^..................^ ^........^
+ * \new \nfd \nsb \nse \nls \ne
+ *
+ * fx is the difference in length between the chars between nfd and
+ * nsb, and the chars between ofd and osb, and is thus the number of
+ * characters to delete if < 0 (new is shorter than old, as above),
+ * or insert (new is longer than short).
+ *
+ * sx is the same for the second differences.
+ */
+
+ /*
+ * if we have a net insert on the first difference, AND inserting the
+ * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
+ * character (which is ne if nls != ne, otherwise is nse) off the edge
+ * of the screen (el->el_term.t_size.h) else we do the deletes first
+ * so that we keep everything we need to.
+ */
+
+ /*
+ * if the last same is the same like the end, there is no last same
+ * part, otherwise we want to keep the last same part set p to the
+ * last useful old character
+ */
+ p = (ols != oe) ? oe : ose;
+
+ /*
+ * if (There is a diffence in the beginning) && (we need to insert
+ * characters) && (the number of characters to insert is less than
+ * the term width)
+ * We need to do an insert!
+ * else if (we need to delete characters)
+ * We need to delete characters!
+ * else
+ * No insert or delete
+ */
+ if ((nsb != nfd) && fx > 0 &&
+ ((p - old) + fx <= el->el_term.t_size.h)) {
+ ELRE_DEBUG(1,
+ (__F, "first diff insert at %d...\r\n", nfd - new));
+ /*
+ * Move to the first char to insert, where the first diff is.
+ */
+ term_move_to_char(el, nfd - new);
+ /*
+ * Check if we have stuff to keep at end
+ */
+ if (nsb != ne) {
+ ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
+ /*
+ * insert fx chars of new starting at nfd
+ */
+ if (fx > 0) {
+ ELRE_DEBUG(!EL_CAN_INSERT, (__F,
+ "ERROR: cannot insert in early first diff\n"));
+ term_insertwrite(el, nfd, fx);
+ re_insert(el, old, ofd - old,
+ el->el_term.t_size.h, nfd, fx);
+ }
+ /*
+ * write (nsb-nfd) - fx chars of new starting at
+ * (nfd + fx)
+ */
+ term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
+ re__strncopy(ofd + fx, nfd + fx,
+ (size_t) ((nsb - nfd) - fx));
+ } else {
+ ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
+ term_overwrite(el, nfd, (nsb - nfd));
+ re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
+ /*
+ * Done
+ */
+ return;
+ }
+ } else if (fx < 0) {
+ ELRE_DEBUG(1,
+ (__F, "first diff delete at %d...\r\n", ofd - old));
+ /*
+ * move to the first char to delete where the first diff is
+ */
+ term_move_to_char(el, ofd - old);
+ /*
+ * Check if we have stuff to save
+ */
+ if (osb != oe) {
+ ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
+ /*
+ * fx is less than zero *always* here but we check
+ * for code symmetry
+ */
+ if (fx < 0) {
+ ELRE_DEBUG(!EL_CAN_DELETE, (__F,
+ "ERROR: cannot delete in first diff\n"));
+ term_deletechars(el, -fx);
+ re_delete(el, old, ofd - old,
+ el->el_term.t_size.h, -fx);
+ }
+ /*
+ * write (nsb-nfd) chars of new starting at nfd
+ */
+ term_overwrite(el, nfd, (nsb - nfd));
+ re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
+
+ } else {
+ ELRE_DEBUG(1, (__F,
+ "but with nothing left to save\r\n"));
+ /*
+ * write (nsb-nfd) chars of new starting at nfd
+ */
+ term_overwrite(el, nfd, (nsb - nfd));
+ ELRE_DEBUG(1, (__F,
+ "cleareol %d\n", (oe - old) - (ne - new)));
+ term_clear_EOL(el, (oe - old) - (ne - new));
+ /*
+ * Done
+ */
+ return;
+ }
+ } else
+ fx = 0;
+
+ if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
+ ELRE_DEBUG(1, (__F,
+ "second diff delete at %d...\r\n", (ose - old) + fx));
+ /*
+ * Check if we have stuff to delete
+ */
+ /*
+ * fx is the number of characters inserted (+) or deleted (-)
+ */
+
+ term_move_to_char(el, (ose - old) + fx);
+ /*
+ * Check if we have stuff to save
+ */
+ if (ols != oe) {
+ ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
+ /*
+ * Again a duplicate test.
+ */
+ if (sx < 0) {
+ ELRE_DEBUG(!EL_CAN_DELETE, (__F,
+ "ERROR: cannot delete in second diff\n"));
+ term_deletechars(el, -sx);
+ }
+ /*
+ * write (nls-nse) chars of new starting at nse
+ */
+ term_overwrite(el, nse, (nls - nse));
+ } else {
+ ELRE_DEBUG(1, (__F,
+ "but with nothing left to save\r\n"));
+ term_overwrite(el, nse, (nls - nse));
+ ELRE_DEBUG(1, (__F,
+ "cleareol %d\n", (oe - old) - (ne - new)));
+ if ((oe - old) - (ne - new) != 0)
+ term_clear_EOL(el, (oe - old) - (ne - new));
+ }
+ }
+ /*
+ * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
+ */
+ if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
+ ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
+ nfd - new));
+
+ term_move_to_char(el, nfd - new);
+ /*
+ * Check if we have stuff to keep at the end
+ */
+ if (nsb != ne) {
+ ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
+ /*
+ * We have to recalculate fx here because we set it
+ * to zero above as a flag saying that we hadn't done
+ * an early first insert.
+ */
+ fx = (nsb - nfd) - (osb - ofd);
+ if (fx > 0) {
+ /*
+ * insert fx chars of new starting at nfd
+ */
+ ELRE_DEBUG(!EL_CAN_INSERT, (__F,
+ "ERROR: cannot insert in late first diff\n"));
+ term_insertwrite(el, nfd, fx);
+ re_insert(el, old, ofd - old,
+ el->el_term.t_size.h, nfd, fx);
+ }
+ /*
+ * write (nsb-nfd) - fx chars of new starting at
+ * (nfd + fx)
+ */
+ term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
+ re__strncopy(ofd + fx, nfd + fx,
+ (size_t) ((nsb - nfd) - fx));
+ } else {
+ ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
+ term_overwrite(el, nfd, (nsb - nfd));
+ re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
+ }
+ }
+ /*
+ * line is now NEW up to nse
+ */
+ if (sx >= 0) {
+ ELRE_DEBUG(1, (__F,
+ "second diff insert at %d...\r\n", nse - new));
+ term_move_to_char(el, nse - new);
+ if (ols != oe) {
+ ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
+ if (sx > 0) {
+ /* insert sx chars of new starting at nse */
+ ELRE_DEBUG(!EL_CAN_INSERT, (__F,
+ "ERROR: cannot insert in second diff\n"));
+ term_insertwrite(el, nse, sx);
+ }
+ /*
+ * write (nls-nse) - sx chars of new starting at
+ * (nse + sx)
+ */
+ term_overwrite(el, nse + sx, (nls - nse) - sx);
+ } else {
+ ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
+ term_overwrite(el, nse, (nls - nse));
+
+ /*
+ * No need to do a clear-to-end here because we were
+ * doing a second insert, so we will have over
+ * written all of the old string.
+ */
+ }
+ }
+ ELRE_DEBUG(1, (__F, "done.\r\n"));
+}
+
+
+/* re__copy_and_pad():
+ * Copy string and pad with spaces
+ */
+private void
+re__copy_and_pad(char *dst, const char *src, size_t width)
+{
+ int i;
+
+ for (i = 0; i < width; i++) {
+ if (*src == '\0')
+ break;
+ *dst++ = *src++;
+ }
+
+ for (; i < width; i++)
+ *dst++ = ' ';
+
+ *dst = '\0';
+}
+
+
+/* re_refresh_cursor():
+ * Move to the new cursor position
+ */
+protected void
+re_refresh_cursor(EditLine *el)
+{
+ char *cp, c;
+ int h, v, th;
+
+ /* first we must find where the cursor is... */
+ h = el->el_prompt.p_pos.h;
+ v = el->el_prompt.p_pos.v;
+ th = el->el_term.t_size.h; /* optimize for speed */
+
+ /* do input buffer to el->el_line.cursor */
+ for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
+ c = *cp;
+ h++; /* all chars at least this long */
+
+ if (c == '\n') {/* handle newline in data part too */
+ h = 0;
+ v++;
+ } else {
+ if (c == '\t') { /* if a tab, to next tab stop */
+ while (h & 07) {
+ h++;
+ }
+ } else if (iscntrl((unsigned char) c)) {
+ /* if control char */
+ h++;
+ if (h > th) { /* if overflow, compensate */
+ h = 1;
+ v++;
+ }
+ } else if (!isprint((unsigned char) c)) {
+ h += 3;
+ if (h > th) { /* if overflow, compensate */
+ h = h - th;
+ v++;
+ }
+ }
+ }
+
+ if (h >= th) { /* check, extra long tabs picked up here also */
+ h = 0;
+ v++;
+ }
+ }
+
+ /* now go there */
+ term_move_to_line(el, v);
+ term_move_to_char(el, h);
+ term__flush();
+}
+
+
+/* re_fastputc():
+ * Add a character fast.
+ */
+private void
+re_fastputc(EditLine *el, int c)
+{
+
+ term__putc(c);
+ el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
+ if (el->el_cursor.h >= el->el_term.t_size.h) {
+ /* if we must overflow */
+ el->el_cursor.h = 0;
+
+ /*
+ * If we would overflow (input is longer than terminal size),
+ * emulate scroll by dropping first line and shuffling the rest.
+ * We do this via pointer shuffling - it's safe in this case
+ * and we avoid memcpy().
+ */
+ if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
+ int i, lins = el->el_term.t_size.v;
+ char *firstline = el->el_display[0];
+
+ for(i=1; i < lins; i++)
+ el->el_display[i-1] = el->el_display[i];
+
+ re__copy_and_pad(firstline, "", 0);
+ el->el_display[i-1] = firstline;
+ } else {
+ el->el_cursor.v++;
+ el->el_refresh.r_oldcv++;
+ }
+ if (EL_HAS_AUTO_MARGINS) {
+ if (EL_HAS_MAGIC_MARGINS) {
+ term__putc(' ');
+ term__putc('\b');
+ }
+ } else {
+ term__putc('\r');
+ term__putc('\n');
+ }
+ }
+}
+
+
+/* re_fastaddc():
+ * we added just one char, handle it fast.
+ * Assumes that screen cursor == real cursor
+ */
+protected void
+re_fastaddc(EditLine *el)
+{
+ char c;
+ int rhdiff;
+
+ c = el->el_line.cursor[-1];
+
+ if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
+ re_refresh(el); /* too hard to handle */
+ return;
+ }
+ rhdiff = el->el_term.t_size.h - el->el_cursor.h -
+ el->el_rprompt.p_pos.h;
+ if (el->el_rprompt.p_pos.h && rhdiff < 3) {
+ re_refresh(el); /* clear out rprompt if less than 1 char gap */
+ return;
+ } /* else (only do at end of line, no TAB) */
+ if (iscntrl((unsigned char) c)) { /* if control char, do caret */
+ char mc = (c == '\177') ? '?' : (c | 0100);
+ re_fastputc(el, '^');
+ re_fastputc(el, mc);
+ } else if (isprint((unsigned char) c)) { /* normal char */
+ re_fastputc(el, c);
+ } else {
+ re_fastputc(el, '\\');
+ re_fastputc(el, (int) ((((unsigned int) c >> 6) & 7) + '0'));
+ re_fastputc(el, (int) ((((unsigned int) c >> 3) & 7) + '0'));
+ re_fastputc(el, (c & 7) + '0');
+ }
+ term__flush();
+}
+
+
+/* re_clear_display():
+ * clear the screen buffers so that new new prompt starts fresh.
+ */
+protected void
+re_clear_display(EditLine *el)
+{
+ int i;
+
+ el->el_cursor.v = 0;
+ el->el_cursor.h = 0;
+ for (i = 0; i < el->el_term.t_size.v; i++)
+ el->el_display[i][0] = '\0';
+ el->el_refresh.r_oldcv = 0;
+}
+
+
+/* re_clear_lines():
+ * Make sure all lines are *really* blank
+ */
+protected void
+re_clear_lines(EditLine *el)
+{
+
+ if (EL_CAN_CEOL) {
+ int i;
+ term_move_to_char(el, 0);
+ for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
+ /* for each line on the screen */
+ term_move_to_line(el, i);
+ term_clear_EOL(el, el->el_term.t_size.h);
+ }
+ term_move_to_line(el, 0);
+ } else {
+ term_move_to_line(el, el->el_refresh.r_oldcv);
+ /* go to last line */
+ term__putc('\r'); /* go to BOL */
+ term__putc('\n'); /* go to new line */
+ }
+}
diff --git a/trunk/main/editline/refresh.h b/trunk/main/editline/refresh.h
new file mode 100644
index 000000000..33c0887c1
--- /dev/null
+++ b/trunk/main/editline/refresh.h
@@ -0,0 +1,63 @@
+/* $NetBSD: refresh.h,v 1.4 2001/01/10 07:45:42 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)refresh.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.refresh.h: Screen refresh functions
+ */
+#ifndef _h_el_refresh
+#define _h_el_refresh
+
+#include "histedit.h"
+
+typedef struct {
+ coord_t r_cursor; /* Refresh cursor position */
+ int r_oldcv; /* Vertical locations */
+ int r_newcv;
+} el_refresh_t;
+
+protected void re_putc(EditLine *, int, int);
+protected void re_clear_lines(EditLine *);
+protected void re_clear_display(EditLine *);
+protected void re_refresh(EditLine *);
+protected void re_refresh_cursor(EditLine *);
+protected void re_fastaddc(EditLine *);
+protected void re_goto_bottom(EditLine *);
+
+#endif /* _h_el_refresh */
diff --git a/trunk/main/editline/search.c b/trunk/main/editline/search.c
new file mode 100644
index 000000000..7c1cb84ef
--- /dev/null
+++ b/trunk/main/editline/search.c
@@ -0,0 +1,649 @@
+/* $NetBSD: search.c,v 1.12 2002/03/18 16:00:58 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: search.c,v 1.12 2002/03/18 16:00:58 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * search.c: History and character search functions
+ */
+#include <stdlib.h>
+#if defined(REGEX)
+#include <regex.h>
+#elif defined(REGEXP)
+#include <regexp.h>
+#endif
+#include "el.h"
+
+/*
+ * Adjust cursor in vi mode to include the character under it
+ */
+#define EL_CURSOR(el) \
+ ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
+ ((el)->el_map.current == (el)->el_map.alt)))
+
+/* search_init():
+ * Initialize the search stuff
+ */
+protected int
+search_init(EditLine *el)
+{
+
+ el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_search.patbuf == NULL)
+ return (-1);
+ el->el_search.patlen = 0;
+ el->el_search.patdir = -1;
+ el->el_search.chacha = '\0';
+ el->el_search.chadir = -1;
+ return (0);
+}
+
+
+/* search_end():
+ * Initialize the search stuff
+ */
+protected void
+search_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_search.patbuf);
+ el->el_search.patbuf = NULL;
+}
+
+
+#ifdef REGEXP
+/* regerror():
+ * Handle regular expression errors
+ */
+public void
+/*ARGSUSED*/
+regerror(const char *msg)
+{
+}
+#endif
+
+
+/* el_match():
+ * Return if string matches pattern
+ */
+protected int
+el_match(const char *str, const char *pat)
+{
+#if defined (REGEX)
+ regex_t re;
+ int rv;
+#elif defined (REGEXP)
+ regexp *rp;
+ int rv;
+#else
+ extern char *re_comp(const char *);
+ extern int re_exec(const char *);
+#endif
+
+ if (strstr(str, pat) != NULL)
+ return (1);
+
+#if defined(REGEX)
+ if (regcomp(&re, pat, 0) == 0) {
+ rv = regexec(&re, str, 0, NULL, 0) == 0;
+ regfree(&re);
+ } else {
+ rv = 0;
+ }
+ return (rv);
+#elif defined(REGEXP)
+ if ((re = regcomp(pat)) != NULL) {
+ rv = regexec(re, str);
+ free((ptr_t) re);
+ } else {
+ rv = 0;
+ }
+ return (rv);
+#else
+ if (re_comp(pat) != NULL)
+ return (0);
+ else
+ return (re_exec(str) == 1);
+#endif
+}
+
+
+/* c_hmatch():
+ * return True if the pattern matches the prefix
+ */
+protected int
+c_hmatch(EditLine *el, const char *str)
+{
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
+ el->el_search.patbuf, str);
+#endif /* SDEBUG */
+
+ return (el_match(str, el->el_search.patbuf));
+}
+
+
+/* c_setpat():
+ * Set the history seatch pattern
+ */
+protected void
+c_setpat(EditLine *el)
+{
+ if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY &&
+ el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) {
+ el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer;
+ if (el->el_search.patlen >= EL_BUFSIZ)
+ el->el_search.patlen = EL_BUFSIZ - 1;
+ if (el->el_search.patlen != 0) {
+ (void) strncpy(el->el_search.patbuf, el->el_line.buffer,
+ el->el_search.patlen);
+ el->el_search.patbuf[el->el_search.patlen] = '\0';
+ } else
+ el->el_search.patlen = strlen(el->el_search.patbuf);
+ }
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "\neventno = %d\n",
+ el->el_history.eventno);
+ (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen);
+ (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n",
+ el->el_search.patbuf);
+ (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n",
+ EL_CURSOR(el) - el->el_line.buffer,
+ el->el_line.lastchar - el->el_line.buffer);
+#endif
+}
+
+
+/* ce_inc_search():
+ * Emacs incremental search
+ */
+protected el_action_t
+ce_inc_search(EditLine *el, int dir)
+{
+ static const char STRfwd[] = {'f', 'w', 'd', '\0'},
+ STRbck[] = {'b', 'c', 'k', '\0'};
+ static char pchar = ':';/* ':' = normal, '?' = failed */
+ static char endcmd[2] = {'\0', '\0'};
+ char ch, *ocursor = el->el_line.cursor, oldpchar = pchar;
+ const char *cp;
+
+ el_action_t ret = CC_NORM;
+
+ int ohisteventno = el->el_history.eventno;
+ int oldpatlen = el->el_search.patlen;
+ int newdir = dir;
+ int done, redo;
+
+ if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 +
+ el->el_search.patlen >= el->el_line.limit)
+ return (CC_ERROR);
+
+ for (;;) {
+
+ if (el->el_search.patlen == 0) { /* first round */
+ pchar = ':';
+#ifdef ANCHOR
+ el->el_search.patbuf[el->el_search.patlen++] = '.';
+ el->el_search.patbuf[el->el_search.patlen++] = '*';
+#endif
+ }
+ done = redo = 0;
+ *el->el_line.lastchar++ = '\n';
+ for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd;
+ *cp; *el->el_line.lastchar++ = *cp++)
+ continue;
+ *el->el_line.lastchar++ = pchar;
+ for (cp = &el->el_search.patbuf[1];
+ cp < &el->el_search.patbuf[el->el_search.patlen];
+ *el->el_line.lastchar++ = *cp++)
+ continue;
+ *el->el_line.lastchar = '\0';
+ re_refresh(el);
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ switch (el->el_map.current[(unsigned char) ch]) {
+ case ED_INSERT:
+ case ED_DIGIT:
+ if (el->el_search.patlen > EL_BUFSIZ - 3)
+ term_beep(el);
+ else {
+ el->el_search.patbuf[el->el_search.patlen++] =
+ ch;
+ *el->el_line.lastchar++ = ch;
+ *el->el_line.lastchar = '\0';
+ re_refresh(el);
+ }
+ break;
+
+ case EM_INC_SEARCH_NEXT:
+ newdir = ED_SEARCH_NEXT_HISTORY;
+ redo++;
+ break;
+
+ case EM_INC_SEARCH_PREV:
+ newdir = ED_SEARCH_PREV_HISTORY;
+ redo++;
+ break;
+
+ case ED_DELETE_PREV_CHAR:
+ if (el->el_search.patlen > 1)
+ done++;
+ else
+ term_beep(el);
+ break;
+
+ default:
+ switch (ch) {
+ case 0007: /* ^G: Abort */
+ ret = CC_ERROR;
+ done++;
+ break;
+
+ case 0027: /* ^W: Append word */
+ /* No can do if globbing characters in pattern */
+ for (cp = &el->el_search.patbuf[1];; cp++)
+ if (cp >= &el->el_search.patbuf[el->el_search.patlen]) {
+ el->el_line.cursor +=
+ el->el_search.patlen - 1;
+ cp = c__next_word(el->el_line.cursor,
+ el->el_line.lastchar, 1,
+ ce__isword);
+ while (el->el_line.cursor < cp &&
+ *el->el_line.cursor != '\n') {
+ if (el->el_search.patlen >
+ EL_BUFSIZ - 3) {
+ term_beep(el);
+ break;
+ }
+ el->el_search.patbuf[el->el_search.patlen++] =
+ *el->el_line.cursor;
+ *el->el_line.lastchar++ =
+ *el->el_line.cursor++;
+ }
+ el->el_line.cursor = ocursor;
+ *el->el_line.lastchar = '\0';
+ re_refresh(el);
+ break;
+ } else if (isglob(*cp)) {
+ term_beep(el);
+ break;
+ }
+ break;
+
+ default: /* Terminate and execute cmd */
+ endcmd[0] = ch;
+ el_push(el, endcmd);
+ /* FALLTHROUGH */
+
+ case 0033: /* ESC: Terminate */
+ ret = CC_REFRESH;
+ done++;
+ break;
+ }
+ break;
+ }
+
+ while (el->el_line.lastchar > el->el_line.buffer &&
+ *el->el_line.lastchar != '\n')
+ *el->el_line.lastchar-- = '\0';
+ *el->el_line.lastchar = '\0';
+
+ if (!done) {
+
+ /* Can't search if unmatched '[' */
+ for (cp = &el->el_search.patbuf[el->el_search.patlen-1],
+ ch = ']';
+ cp > el->el_search.patbuf;
+ cp--)
+ if (*cp == '[' || *cp == ']') {
+ ch = *cp;
+ break;
+ }
+ if (el->el_search.patlen > 1 && ch != '[') {
+ if (redo && newdir == dir) {
+ if (pchar == '?') { /* wrap around */
+ el->el_history.eventno =
+ newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff;
+ if (hist_get(el) == CC_ERROR)
+ /* el->el_history.event
+ * no was fixed by
+ * first call */
+ (void) hist_get(el);
+ el->el_line.cursor = newdir ==
+ ED_SEARCH_PREV_HISTORY ?
+ el->el_line.lastchar :
+ el->el_line.buffer;
+ } else
+ el->el_line.cursor +=
+ newdir ==
+ ED_SEARCH_PREV_HISTORY ?
+ -1 : 1;
+ }
+#ifdef ANCHOR
+ el->el_search.patbuf[el->el_search.patlen++] =
+ '.';
+ el->el_search.patbuf[el->el_search.patlen++] =
+ '*';
+#endif
+ el->el_search.patbuf[el->el_search.patlen] =
+ '\0';
+ if (el->el_line.cursor < el->el_line.buffer ||
+ el->el_line.cursor > el->el_line.lastchar ||
+ (ret = ce_search_line(el,
+ &el->el_search.patbuf[1],
+ newdir)) == CC_ERROR) {
+ /* avoid c_setpat */
+ el->el_state.lastcmd =
+ (el_action_t) newdir;
+ ret = newdir == ED_SEARCH_PREV_HISTORY ?
+ ed_search_prev_history(el, 0) :
+ ed_search_next_history(el, 0);
+ if (ret != CC_ERROR) {
+ el->el_line.cursor = newdir ==
+ ED_SEARCH_PREV_HISTORY ?
+ el->el_line.lastchar :
+ el->el_line.buffer;
+ (void) ce_search_line(el,
+ &el->el_search.patbuf[1],
+ newdir);
+ }
+ }
+ el->el_search.patbuf[--el->el_search.patlen] =
+ '\0';
+ if (ret == CC_ERROR) {
+ term_beep(el);
+ if (el->el_history.eventno !=
+ ohisteventno) {
+ el->el_history.eventno =
+ ohisteventno;
+ if (hist_get(el) == CC_ERROR)
+ return (CC_ERROR);
+ }
+ el->el_line.cursor = ocursor;
+ pchar = '?';
+ } else {
+ pchar = ':';
+ }
+ }
+ ret = ce_inc_search(el, newdir);
+
+ if (ret == CC_ERROR && pchar == '?' && oldpchar == ':')
+ /*
+ * break abort of failed search at last
+ * non-failed
+ */
+ ret = CC_NORM;
+
+ }
+ if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
+ /* restore on normal return or error exit */
+ pchar = oldpchar;
+ el->el_search.patlen = oldpatlen;
+ if (el->el_history.eventno != ohisteventno) {
+ el->el_history.eventno = ohisteventno;
+ if (hist_get(el) == CC_ERROR)
+ return (CC_ERROR);
+ }
+ el->el_line.cursor = ocursor;
+ if (ret == CC_ERROR)
+ re_refresh(el);
+ }
+ if (done || ret != CC_NORM)
+ return (ret);
+ }
+}
+
+
+/* cv_search():
+ * Vi search.
+ */
+protected el_action_t
+cv_search(EditLine *el, int dir)
+{
+ char ch;
+ char tmpbuf[EL_BUFSIZ];
+ int tmplen;
+
+ tmplen = 0;
+#ifdef ANCHOR
+ tmpbuf[tmplen++] = '.';
+ tmpbuf[tmplen++] = '*';
+#endif
+
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_search.patdir = dir;
+
+ c_insert(el, 2); /* prompt + '\n' */
+ *el->el_line.cursor++ = '\n';
+ *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?';
+ re_refresh(el);
+
+#ifdef ANCHOR
+#define LEN 2
+#else
+#define LEN 0
+#endif
+
+ tmplen = c_gets(el, &tmpbuf[LEN]) + LEN;
+ ch = tmpbuf[tmplen];
+ tmpbuf[tmplen] = '\0';
+
+ if (tmplen == LEN) {
+ /*
+ * Use the old pattern, but wild-card it.
+ */
+ if (el->el_search.patlen == 0) {
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ re_refresh(el);
+ return (CC_ERROR);
+ }
+#ifdef ANCHOR
+ if (el->el_search.patbuf[0] != '.' &&
+ el->el_search.patbuf[0] != '*') {
+ (void) strncpy(tmpbuf, el->el_search.patbuf,
+ sizeof(tmpbuf) - 1);
+ el->el_search.patbuf[0] = '.';
+ el->el_search.patbuf[1] = '*';
+ (void) strncpy(&el->el_search.patbuf[2], tmpbuf,
+ EL_BUFSIZ - 3);
+ el->el_search.patlen++;
+ el->el_search.patbuf[el->el_search.patlen++] = '.';
+ el->el_search.patbuf[el->el_search.patlen++] = '*';
+ el->el_search.patbuf[el->el_search.patlen] = '\0';
+ }
+#endif
+ } else {
+#ifdef ANCHOR
+ tmpbuf[tmplen++] = '.';
+ tmpbuf[tmplen++] = '*';
+#endif
+ tmpbuf[tmplen] = '\0';
+ (void) strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1);
+ el->el_search.patlen = tmplen;
+ }
+ el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */
+ el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer;
+ if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) :
+ ed_search_next_history(el, 0)) == CC_ERROR) {
+ re_refresh(el);
+ return (CC_ERROR);
+ } else {
+ if (ch == 0033) {
+ re_refresh(el);
+ *el->el_line.lastchar++ = '\n';
+ *el->el_line.lastchar = '\0';
+ re_goto_bottom(el);
+ return (CC_NEWLINE);
+ } else
+ return (CC_REFRESH);
+ }
+}
+
+
+/* ce_search_line():
+ * Look for a pattern inside a line
+ */
+protected el_action_t
+ce_search_line(EditLine *el, char *pattern, int dir)
+{
+ char *cp;
+
+ if (dir == ED_SEARCH_PREV_HISTORY) {
+ for (cp = el->el_line.cursor; cp >= el->el_line.buffer; cp--)
+ if (el_match(cp, pattern)) {
+ el->el_line.cursor = cp;
+ return (CC_NORM);
+ }
+ return (CC_ERROR);
+ } else {
+ for (cp = el->el_line.cursor; *cp != '\0' &&
+ cp < el->el_line.limit; cp++)
+ if (el_match(cp, pattern)) {
+ el->el_line.cursor = cp;
+ return (CC_NORM);
+ }
+ return (CC_ERROR);
+ }
+}
+
+
+/* cv_repeat_srch():
+ * Vi repeat search
+ */
+protected el_action_t
+cv_repeat_srch(EditLine *el, int c)
+{
+
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n",
+ c, el->el_search.patlen, el->el_search.patbuf);
+#endif
+
+ el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */
+ el->el_line.lastchar = el->el_line.buffer;
+
+ switch (c) {
+ case ED_SEARCH_NEXT_HISTORY:
+ return (ed_search_next_history(el, 0));
+ case ED_SEARCH_PREV_HISTORY:
+ return (ed_search_prev_history(el, 0));
+ default:
+ return (CC_ERROR);
+ }
+}
+
+
+/* cv_csearch_back():
+ * Vi character search reverse
+ */
+protected el_action_t
+cv_csearch_back(EditLine *el, int ch, int count, int tflag)
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ while (count--) {
+ if (*cp == ch)
+ cp--;
+ while (cp > el->el_line.buffer && *cp != ch)
+ cp--;
+ }
+
+ if (cp < el->el_line.buffer || (cp == el->el_line.buffer && *cp != ch))
+ return (CC_ERROR);
+
+ if (*cp == ch && tflag)
+ cp++;
+
+ el->el_line.cursor = cp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ re_refresh_cursor(el);
+ return (CC_NORM);
+}
+
+
+/* cv_csearch_fwd():
+ * Vi character search forward
+ */
+protected el_action_t
+cv_csearch_fwd(EditLine *el, int ch, int count, int tflag)
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ while (count--) {
+ if (*cp == ch)
+ cp++;
+ while (cp < el->el_line.lastchar && *cp != ch)
+ cp++;
+ }
+
+ if (cp >= el->el_line.lastchar)
+ return (CC_ERROR);
+
+ if (*cp == ch && tflag)
+ cp--;
+
+ el->el_line.cursor = cp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ re_refresh_cursor(el);
+ return (CC_NORM);
+}
diff --git a/trunk/main/editline/search.h b/trunk/main/editline/search.h
new file mode 100644
index 000000000..676bbe2e3
--- /dev/null
+++ b/trunk/main/editline/search.h
@@ -0,0 +1,70 @@
+/* $NetBSD: search.h,v 1.5 2000/09/04 22:06:32 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)search.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.search.h: Line and history searching utilities
+ */
+#ifndef _h_el_search
+#define _h_el_search
+
+#include "histedit.h"
+
+typedef struct el_search_t {
+ char *patbuf; /* The pattern buffer */
+ size_t patlen; /* Length of the pattern buffer */
+ int patdir; /* Direction of the last search */
+ int chadir; /* Character search direction */
+ char chacha; /* Character we are looking for */
+} el_search_t;
+
+
+protected int el_match(const char *, const char *);
+protected int search_init(EditLine *);
+protected void search_end(EditLine *);
+protected int c_hmatch(EditLine *, const char *);
+protected void c_setpat(EditLine *);
+protected el_action_t ce_inc_search(EditLine *, int);
+protected el_action_t cv_search(EditLine *, int);
+protected el_action_t ce_search_line(EditLine *, char *, int);
+protected el_action_t cv_repeat_srch(EditLine *, int);
+protected el_action_t cv_csearch_back(EditLine *, int, int, int);
+protected el_action_t cv_csearch_fwd(EditLine *, int, int, int);
+
+#endif /* _h_el_search */
diff --git a/trunk/main/editline/sig.c b/trunk/main/editline/sig.c
new file mode 100644
index 000000000..0acba1247
--- /dev/null
+++ b/trunk/main/editline/sig.c
@@ -0,0 +1,198 @@
+/* $NetBSD: sig.c,v 1.9 2002/03/18 16:00:58 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)sig.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: sig.c,v 1.9 2002/03/18 16:00:58 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * sig.c: Signal handling stuff.
+ * our policy is to trap all signals, set a good state
+ * and pass the ball to our caller.
+ */
+#include "el.h"
+#include <stdlib.h>
+
+private EditLine *sel = NULL;
+
+private const int sighdl[] = {
+#define _DO(a) (a),
+ ALLSIGS
+#undef _DO
+ - 1
+};
+
+private void sig_handler(int);
+
+/* sig_handler():
+ * This is the handler called for all signals
+ * XXX: we cannot pass any data so we just store the old editline
+ * state in a private variable
+ */
+private void
+sig_handler(int signo)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+ (void) sigaddset(&nset, signo);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ switch (signo) {
+ case SIGCONT:
+ tty_rawmode(sel);
+ if (ed_redisplay(sel, 0) == CC_REFRESH)
+ re_refresh(sel);
+ term__flush();
+ break;
+
+ case SIGWINCH:
+ el_resize(sel);
+ break;
+
+ default:
+ tty_cookedmode(sel);
+ break;
+ }
+
+ for (i = 0; sighdl[i] != -1; i++)
+ if (signo == sighdl[i])
+ break;
+
+ (void) signal(signo, sel->el_signal[i]);
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void) kill(0, signo);
+}
+
+
+/* sig_init():
+ * Initialize all signal stuff
+ */
+protected int
+sig_init(EditLine *el)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+#define _DO(a) (void) sigaddset(&nset, a);
+ ALLSIGS
+#undef _DO
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+#define SIGSIZE (sizeof(sighdl) / sizeof(sighdl[0]) * sizeof(sig_t))
+
+ el->el_signal = (sig_t *) el_malloc(SIGSIZE);
+ if (el->el_signal == NULL)
+ return (-1);
+ for (i = 0; sighdl[i] != -1; i++)
+ el->el_signal[i] = SIG_ERR;
+
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+
+ return (0);
+}
+
+
+/* sig_end():
+ * Clear all signal stuff
+ */
+protected void
+sig_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_signal);
+ el->el_signal = NULL;
+}
+
+
+/* sig_set():
+ * set all the signal handlers
+ */
+protected void
+sig_set(EditLine *el)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+#define _DO(a) (void) sigaddset(&nset, a);
+ ALLSIGS
+#undef _DO
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ for (i = 0; sighdl[i] != -1; i++) {
+ sig_t s;
+ /* This could happen if we get interrupted */
+ if ((s = signal(sighdl[i], sig_handler)) != sig_handler)
+ el->el_signal[i] = s;
+ }
+ sel = el;
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+
+
+/* sig_clr():
+ * clear all the signal handlers
+ */
+protected void
+sig_clr(EditLine *el)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+#define _DO(a) (void) sigaddset(&nset, a);
+ ALLSIGS
+#undef _DO
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ for (i = 0; sighdl[i] != -1; i++)
+ if (el->el_signal[i] != SIG_ERR)
+ (void) signal(sighdl[i], el->el_signal[i]);
+
+ sel = NULL; /* we are going to die if the handler is
+ * called */
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+}
diff --git a/trunk/main/editline/sig.h b/trunk/main/editline/sig.h
new file mode 100644
index 000000000..e7231b65c
--- /dev/null
+++ b/trunk/main/editline/sig.h
@@ -0,0 +1,72 @@
+/* $NetBSD: sig.h,v 1.3 2000/09/04 22:06:32 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sig.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.sig.h: Signal handling functions
+ */
+#ifndef _h_el_sig
+#define _h_el_sig
+
+#include <signal.h>
+
+#include "histedit.h"
+
+/*
+ * Define here all the signals we are going to handle
+ * The _DO macro is used to iterate in the source code
+ */
+#define ALLSIGS \
+ _DO(SIGINT) \
+ _DO(SIGTSTP) \
+ _DO(SIGSTOP) \
+ _DO(SIGQUIT) \
+ _DO(SIGHUP) \
+ _DO(SIGTERM) \
+ _DO(SIGCONT) \
+ _DO(SIGWINCH)
+
+typedef sig_t *el_signal_t;
+
+protected void sig_end(EditLine*);
+protected int sig_init(EditLine*);
+protected void sig_set(EditLine*);
+protected void sig_clr(EditLine*);
+
+#endif /* _h_el_sig */
diff --git a/trunk/main/editline/sys.h b/trunk/main/editline/sys.h
new file mode 100644
index 000000000..87ecc899c
--- /dev/null
+++ b/trunk/main/editline/sys.h
@@ -0,0 +1,123 @@
+/* $NetBSD: sys.h,v 1.5 2002/03/18 16:00:59 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sys.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * sys.h: Put all the stupid compiler and system dependencies here...
+ */
+#ifndef _h_sys
+#define _h_sys
+
+#ifndef public
+# define public /* Externally visible functions/variables */
+#endif
+
+#ifndef private
+# define private static /* Always hidden internals */
+#endif
+
+#ifndef protected
+# define protected /* Redefined from elsewhere to "static" */
+ /* When we want to hide everything */
+#endif
+
+#ifndef _PTR_T
+# define _PTR_T
+typedef void *ptr_t;
+#endif
+
+#ifndef _IOCTL_T
+# define _IOCTL_T
+typedef void *ioctl_t;
+#endif
+
+#include <stdio.h>
+
+#ifndef HAVE_FGETLN
+#define fgetln libedit_fgetln
+char *fgetln(FILE *fp, size_t *len);
+#endif
+
+#define REGEX /* Use POSIX.2 regular expression functions */
+#undef REGEXP /* Use UNIX V8 regular expression functions */
+
+#ifdef SUNOS
+# undef REGEX
+# undef REGEXP
+/* # include <malloc.h> XXX Removed for Solaris build XXX */
+typedef void (*sig_t)(int);
+# ifdef __GNUC__
+/*
+ * Broken hdrs.
+ */
+#ifndef SOLARIS
+extern int tgetent(const char *bp, char *name);
+extern int tgetflag(const char *id);
+extern int tgetnum(const char *id);
+extern char *tgetstr(const char *id, char **area);
+#endif
+extern char *tgoto(const char *cap, int col, int row);
+extern int tputs(const char *str, int affcnt, int (*putc)(int));
+extern char *getenv(const char *);
+extern int fprintf(FILE *, const char *, ...);
+extern int sigsetmask(int);
+extern int sigblock(int);
+extern int fputc(int, FILE *);
+extern int fgetc(FILE *);
+extern int fflush(FILE *);
+extern int tolower(int);
+extern int toupper(int);
+extern int errno, sys_nerr;
+extern char *sys_errlist[];
+extern void perror(const char *);
+# include <string.h>
+# define strerror(e) sys_errlist[e]
+# endif
+# ifdef SABER
+extern ptr_t memcpy(ptr_t, const ptr_t, size_t);
+extern ptr_t memset(ptr_t, int, size_t);
+# endif
+extern char *fgetline(FILE *, int *);
+#endif
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#endif /* _h_sys */
diff --git a/trunk/main/editline/term.c b/trunk/main/editline/term.c
new file mode 100644
index 000000000..fb627cabb
--- /dev/null
+++ b/trunk/main/editline/term.c
@@ -0,0 +1,1587 @@
+/* $NetBSD: term.c,v 1.35 2002/03/18 16:00:59 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)term.c 8.2 (Berkeley) 4/30/95";
+#else
+__RCSID("$NetBSD: term.c,v 1.35 2002/03/18 16:00:59 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * term.c: Editor/termcap-curses interface
+ * We have to declare a static variable here, since the
+ * termcap putchar routine does not take an argument!
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+#ifdef HAVE_CURSES_H
+#include <curses.h>
+#endif
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
+#if defined(HAVE_TERM_H)
+#include "term.h"
+/* Can not use /usr/include/term.h because of a lot of incompatibilities, so just define some prototypes */
+extern int tgetent(char *, const char *);
+extern int tgetflag(const char *);
+extern int tgetnum(const char *);
+extern char *tgetstr(const char *, char **);
+extern int tputs (const char *, int, int (*)(int));
+extern char *tgoto (const char *, int, int);
+#endif /* defined(HAVE_TERM_H) */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include "el.h"
+
+/*
+ * IMPORTANT NOTE: these routines are allowed to look at the current screen
+ * and the current possition assuming that it is correct. If this is not
+ * true, then the update will be WRONG! This is (should be) a valid
+ * assumption...
+ */
+
+#define TC_BUFSIZE 2048
+
+#define GoodStr(a) (el->el_term.t_str[a] != NULL && \
+ el->el_term.t_str[a][0] != '\0')
+#define Str(a) el->el_term.t_str[a]
+#define Val(a) el->el_term.t_val[a]
+
+#ifdef notdef
+private const struct {
+ const char *b_name;
+ int b_rate;
+} baud_rate[] = {
+#ifdef B0
+ { "0", B0 },
+#endif
+#ifdef B50
+ { "50", B50 },
+#endif
+#ifdef B75
+ { "75", B75 },
+#endif
+#ifdef B110
+ { "110", B110 },
+#endif
+#ifdef B134
+ { "134", B134 },
+#endif
+#ifdef B150
+ { "150", B150 },
+#endif
+#ifdef B200
+ { "200", B200 },
+#endif
+#ifdef B300
+ { "300", B300 },
+#endif
+#ifdef B600
+ { "600", B600 },
+#endif
+#ifdef B900
+ { "900", B900 },
+#endif
+#ifdef B1200
+ { "1200", B1200 },
+#endif
+#ifdef B1800
+ { "1800", B1800 },
+#endif
+#ifdef B2400
+ { "2400", B2400 },
+#endif
+#ifdef B3600
+ { "3600", B3600 },
+#endif
+#ifdef B4800
+ { "4800", B4800 },
+#endif
+#ifdef B7200
+ { "7200", B7200 },
+#endif
+#ifdef B9600
+ { "9600", B9600 },
+#endif
+#ifdef EXTA
+ { "19200", EXTA },
+#endif
+#ifdef B19200
+ { "19200", B19200 },
+#endif
+#ifdef EXTB
+ { "38400", EXTB },
+#endif
+#ifdef B38400
+ { "38400", B38400 },
+#endif
+ { NULL, 0 }
+};
+#endif
+
+private const struct termcapstr {
+ const char *name;
+ const char *long_name;
+} tstr[] = {
+#define T_al 0
+ { "al", "add new blank line" },
+#define T_bl 1
+ { "bl", "audible bell" },
+#define T_cd 2
+ { "cd", "clear to bottom" },
+#define T_ce 3
+ { "ce", "clear to end of line" },
+#define T_ch 4
+ { "ch", "cursor to horiz pos" },
+#define T_cl 5
+ { "cl", "clear screen" },
+#define T_dc 6
+ { "dc", "delete a character" },
+#define T_dl 7
+ { "dl", "delete a line" },
+#define T_dm 8
+ { "dm", "start delete mode" },
+#define T_ed 9
+ { "ed", "end delete mode" },
+#define T_ei 10
+ { "ei", "end insert mode" },
+#define T_fs 11
+ { "fs", "cursor from status line" },
+#define T_ho 12
+ { "ho", "home cursor" },
+#define T_ic 13
+ { "ic", "insert character" },
+#define T_im 14
+ { "im", "start insert mode" },
+#define T_ip 15
+ { "ip", "insert padding" },
+#define T_kd 16
+ { "kd", "sends cursor down" },
+#define T_kl 17
+ { "kl", "sends cursor left" },
+#define T_kr 18
+ { "kr", "sends cursor right" },
+#define T_ku 19
+ { "ku", "sends cursor up" },
+#define T_md 20
+ { "md", "begin bold" },
+#define T_me 21
+ { "me", "end attributes" },
+#define T_nd 22
+ { "nd", "non destructive space" },
+#define T_se 23
+ { "se", "end standout" },
+#define T_so 24
+ { "so", "begin standout" },
+#define T_ts 25
+ { "ts", "cursor to status line" },
+#define T_up 26
+ { "up", "cursor up one" },
+#define T_us 27
+ { "us", "begin underline" },
+#define T_ue 28
+ { "ue", "end underline" },
+#define T_vb 29
+ { "vb", "visible bell" },
+#define T_DC 30
+ { "DC", "delete multiple chars" },
+#define T_DO 31
+ { "DO", "cursor down multiple" },
+#define T_IC 32
+ { "IC", "insert multiple chars" },
+#define T_LE 33
+ { "LE", "cursor left multiple" },
+#define T_RI 34
+ { "RI", "cursor right multiple" },
+#define T_UP 35
+ { "UP", "cursor up multiple" },
+#define T_kh 36
+ { "kh", "send cursor home" },
+#define T_at7 37
+ { "@7", "send cursor end" },
+#define T_str 38
+ { NULL, NULL }
+};
+
+private const struct termcapval {
+ const char *name;
+ const char *long_name;
+} tval[] = {
+#define T_am 0
+ { "am", "has automatic margins" },
+#define T_pt 1
+ { "pt", "has physical tabs" },
+#define T_li 2
+ { "li", "Number of lines" },
+#define T_co 3
+ { "co", "Number of columns" },
+#define T_km 4
+ { "km", "Has meta key" },
+#define T_xt 5
+ { "xt", "Tab chars destructive" },
+#define T_xn 6
+ { "xn", "newline ignored at right margin" },
+#define T_MT 7
+ { "MT", "Has meta key" }, /* XXX? */
+#define T_val 8
+ { NULL, NULL, }
+};
+/* do two or more of the attributes use me */
+
+private void term_setflags(EditLine *);
+private int term_rebuffer_display(EditLine *);
+private void term_free_display(EditLine *);
+private int term_alloc_display(EditLine *);
+private void term_alloc(EditLine *, const struct termcapstr *, const char *);
+private void term_init_arrow(EditLine *);
+private void term_reset_arrow(EditLine *);
+
+
+private FILE *term_outfile = NULL; /* XXX: How do we fix that? */
+
+
+/* term_setflags():
+ * Set the terminal capability flags
+ */
+private void
+term_setflags(EditLine *el)
+{
+ EL_FLAGS = 0;
+ if (el->el_tty.t_tabs)
+ EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
+
+ EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
+ EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
+ EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
+ EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
+ TERM_CAN_INSERT : 0;
+ EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
+ EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
+ EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
+
+ if (GoodStr(T_me) && GoodStr(T_ue))
+ EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
+ TERM_CAN_ME : 0;
+ else
+ EL_FLAGS &= ~TERM_CAN_ME;
+ if (GoodStr(T_me) && GoodStr(T_se))
+ EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
+ TERM_CAN_ME : 0;
+
+
+#ifdef DEBUG_SCREEN
+ if (!EL_CAN_UP) {
+ (void) fprintf(el->el_errfile,
+ "WARNING: Your terminal cannot move up.\n");
+ (void) fprintf(el->el_errfile,
+ "Editing may be odd for long lines.\n");
+ }
+ if (!EL_CAN_CEOL)
+ (void) fprintf(el->el_errfile, "no clear EOL capability.\n");
+ if (!EL_CAN_DELETE)
+ (void) fprintf(el->el_errfile, "no delete char capability.\n");
+ if (!EL_CAN_INSERT)
+ (void) fprintf(el->el_errfile, "no insert char capability.\n");
+#endif /* DEBUG_SCREEN */
+}
+
+
+/* term_init():
+ * Initialize the terminal stuff
+ */
+protected int
+term_init(EditLine *el)
+{
+
+ el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE);
+ if (el->el_term.t_buf == NULL)
+ return (-1);
+ el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE);
+ if (el->el_term.t_cap == NULL)
+ return (-1);
+ el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t));
+ if (el->el_term.t_fkey == NULL)
+ return (-1);
+ el->el_term.t_loc = 0;
+ el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *));
+ if (el->el_term.t_str == NULL)
+ return (-1);
+ (void) memset(el->el_term.t_str, 0, T_str * sizeof(char *));
+ el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int));
+ if (el->el_term.t_val == NULL)
+ return (-1);
+ (void) memset(el->el_term.t_val, 0, T_val * sizeof(int));
+ term_outfile = el->el_outfile;
+ (void) term_set(el, NULL);
+ term_init_arrow(el);
+ return (0);
+}
+/* term_end():
+ * Clean up the terminal stuff
+ */
+protected void
+term_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_term.t_buf);
+ el->el_term.t_buf = NULL;
+ el_free((ptr_t) el->el_term.t_cap);
+ el->el_term.t_cap = NULL;
+ el_free((ptr_t) el->el_term.t_fkey);
+ el->el_term.t_fkey = NULL;
+ el->el_term.t_loc = 0;
+ el_free((ptr_t) el->el_term.t_str);
+ el->el_term.t_str = NULL;
+ el_free((ptr_t) el->el_term.t_val);
+ el->el_term.t_val = NULL;
+ term_free_display(el);
+}
+
+
+/* term_alloc():
+ * Maintain a string pool for termcap strings
+ */
+private void
+term_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
+{
+ char termbuf[TC_BUFSIZE];
+ int tlen, clen;
+ char **tlist = el->el_term.t_str;
+ char **tmp, **str = &tlist[t - tstr];
+
+ if (cap == NULL || *cap == '\0') {
+ *str = NULL;
+ return;
+ } else
+ clen = strlen(cap);
+
+ tlen = *str == NULL ? 0 : strlen(*str);
+
+ /*
+ * New string is shorter; no need to allocate space
+ */
+ if (clen <= tlen) {
+ (void) strcpy(*str, cap); /* XXX strcpy is safe */
+ return;
+ }
+ /*
+ * New string is longer; see if we have enough space to append
+ */
+ if (el->el_term.t_loc + 3 < TC_BUFSIZE) {
+ /* XXX strcpy is safe */
+ (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc],
+ cap);
+ el->el_term.t_loc += clen + 1; /* one for \0 */
+ return;
+ }
+ /*
+ * Compact our buffer; no need to check compaction, cause we know it
+ * fits...
+ */
+ tlen = 0;
+ for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
+ if (*tmp != NULL && *tmp != '\0' && *tmp != *str) {
+ char *ptr;
+
+ for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
+ continue;
+ termbuf[tlen++] = '\0';
+ }
+ memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE);
+ el->el_term.t_loc = tlen;
+ if (el->el_term.t_loc + 3 >= TC_BUFSIZE) {
+ (void) fprintf(el->el_errfile,
+ "Out of termcap string space.\n");
+ return;
+ }
+ /* XXX strcpy is safe */
+ (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
+ el->el_term.t_loc += clen + 1; /* one for \0 */
+ return;
+}
+
+
+/* term_rebuffer_display():
+ * Rebuffer the display after the screen changed size
+ */
+private int
+term_rebuffer_display(EditLine *el)
+{
+ coord_t *c = &el->el_term.t_size;
+
+ term_free_display(el);
+
+ c->h = Val(T_co);
+ c->v = Val(T_li);
+
+ if (term_alloc_display(el) == -1)
+ return (-1);
+ return (0);
+}
+
+
+/* term_alloc_display():
+ * Allocate a new display.
+ */
+private int
+term_alloc_display(EditLine *el)
+{
+ int i;
+ char **b;
+ coord_t *c = &el->el_term.t_size;
+
+ b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
+ if (b == NULL)
+ return (-1);
+ for (i = 0; i < c->v; i++) {
+ b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
+ if (b[i] == NULL)
+ return (-1);
+ }
+ b[c->v] = NULL;
+ el->el_display = b;
+
+ b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
+ if (b == NULL)
+ return (-1);
+ for (i = 0; i < c->v; i++) {
+ b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
+ if (b[i] == NULL)
+ return (-1);
+ }
+ b[c->v] = NULL;
+ el->el_vdisplay = b;
+ return (0);
+}
+
+
+/* term_free_display():
+ * Free the display buffers
+ */
+private void
+term_free_display(EditLine *el)
+{
+ char **b;
+ char **bufp;
+
+ b = el->el_display;
+ el->el_display = NULL;
+ if (b != NULL) {
+ for (bufp = b; *bufp != NULL; bufp++)
+ el_free((ptr_t) * bufp);
+ el_free((ptr_t) b);
+ }
+ b = el->el_vdisplay;
+ el->el_vdisplay = NULL;
+ if (b != NULL) {
+ for (bufp = b; *bufp != NULL; bufp++)
+ el_free((ptr_t) * bufp);
+ el_free((ptr_t) b);
+ }
+}
+
+
+/* term_move_to_line():
+ * move to line <where> (first line == 0)
+ * as efficiently as possible
+ */
+protected void
+term_move_to_line(EditLine *el, int where)
+{
+ int del;
+
+ if (where == el->el_cursor.v)
+ return;
+
+ if (where > el->el_term.t_size.v) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_move_to_line: where is ridiculous: %d\r\n", where);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if ((del = where - el->el_cursor.v) > 0) {
+ while (del > 0) {
+ if (EL_HAS_AUTO_MARGINS &&
+ el->el_display[el->el_cursor.v][0] != '\0') {
+ /* move without newline */
+ term_move_to_char(el, el->el_term.t_size.h - 1);
+ term_overwrite(el,
+ &el->el_display[el->el_cursor.v][el->el_cursor.h],
+ 1);
+ /* updates Cursor */
+ del--;
+ } else {
+ if ((del > 1) && GoodStr(T_DO)) {
+ (void) tputs(tgoto(Str(T_DO), del, del),
+ del, term__putc);
+ del = 0;
+ } else {
+ for (; del > 0; del--)
+ term__putc('\n');
+ /* because the \n will become \r\n */
+ el->el_cursor.h = 0;
+ }
+ }
+ }
+ } else { /* del < 0 */
+ if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
+ (void) tputs(tgoto(Str(T_UP), -del, -del), -del,
+ term__putc);
+ else {
+ if (GoodStr(T_up))
+ for (; del < 0; del++)
+ (void) tputs(Str(T_up), 1, term__putc);
+ }
+ }
+ el->el_cursor.v = where;/* now where is here */
+}
+
+
+/* term_move_to_char():
+ * Move to the character position specified
+ */
+protected void
+term_move_to_char(EditLine *el, int where)
+{
+ int del, i;
+
+mc_again:
+ if (where == el->el_cursor.h)
+ return;
+
+ if (where > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_move_to_char: where is riduculous: %d\r\n", where);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if (!where) { /* if where is first column */
+ term__putc('\r'); /* do a CR */
+ el->el_cursor.h = 0;
+ return;
+ }
+ del = where - el->el_cursor.h;
+
+ if ((del < -4 || del > 4) && GoodStr(T_ch))
+ /* go there directly */
+ (void) tputs(tgoto(Str(T_ch), where, where), where, term__putc);
+ else {
+ if (del > 0) { /* moving forward */
+ if ((del > 4) && GoodStr(T_RI))
+ (void) tputs(tgoto(Str(T_RI), del, del),
+ del, term__putc);
+ else {
+ /* if I can do tabs, use them */
+ if (EL_CAN_TAB) {
+ if ((el->el_cursor.h & 0370) !=
+ (where & 0370)) {
+ /* if not within tab stop */
+ for (i =
+ (el->el_cursor.h & 0370);
+ i < (where & 0370);
+ i += 8)
+ term__putc('\t');
+ /* then tab over */
+ el->el_cursor.h = where & 0370;
+ }
+ }
+ /*
+ * it's usually cheaper to just write the
+ * chars, so we do.
+ */
+ /*
+ * NOTE THAT term_overwrite() WILL CHANGE
+ * el->el_cursor.h!!!
+ */
+ term_overwrite(el,
+ &el->el_display[el->el_cursor.v][el->el_cursor.h],
+ where - el->el_cursor.h);
+
+ }
+ } else { /* del < 0 := moving backward */
+ if ((-del > 4) && GoodStr(T_LE))
+ (void) tputs(tgoto(Str(T_LE), -del, -del),
+ -del, term__putc);
+ else { /* can't go directly there */
+ /*
+ * if the "cost" is greater than the "cost"
+ * from col 0
+ */
+ if (EL_CAN_TAB ?
+ (-del > (((unsigned int) where >> 3) +
+ (where & 07)))
+ : (-del > where)) {
+ term__putc('\r'); /* do a CR */
+ el->el_cursor.h = 0;
+ goto mc_again; /* and try again */
+ }
+ for (i = 0; i < -del; i++)
+ term__putc('\b');
+ }
+ }
+ }
+ el->el_cursor.h = where; /* now where is here */
+}
+
+
+/* term_overwrite():
+ * Overstrike num characters
+ */
+protected void
+term_overwrite(EditLine *el, const char *cp, int n)
+{
+ if (n <= 0)
+ return; /* catch bugs */
+
+ if (n > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_overwrite: n is riduculous: %d\r\n", n);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ do {
+ term__putc(*cp++);
+ el->el_cursor.h++;
+ } while (--n);
+
+ if (el->el_cursor.h >= el->el_term.t_size.h) { /* wrap? */
+ if (EL_HAS_AUTO_MARGINS) { /* yes */
+ el->el_cursor.h = 0;
+ el->el_cursor.v++;
+ if (EL_HAS_MAGIC_MARGINS) {
+ /* force the wrap to avoid the "magic"
+ * situation */
+ char c;
+ if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h])
+ != '\0')
+ term_overwrite(el, &c, 1);
+ else
+ term__putc(' ');
+ el->el_cursor.h = 1;
+ }
+ } else /* no wrap, but cursor stays on screen */
+ el->el_cursor.h = el->el_term.t_size.h;
+ }
+}
+
+
+/* term_deletechars():
+ * Delete num characters
+ */
+protected void
+term_deletechars(EditLine *el, int num)
+{
+ if (num <= 0)
+ return;
+
+ if (!EL_CAN_DELETE) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile, " ERROR: cannot delete \n");
+#endif /* DEBUG_EDIT */
+ return;
+ }
+ if (num > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_deletechars: num is riduculous: %d\r\n", num);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if (GoodStr(T_DC)) /* if I have multiple delete */
+ if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more
+ * expen. */
+ (void) tputs(tgoto(Str(T_DC), num, num),
+ num, term__putc);
+ return;
+ }
+ if (GoodStr(T_dm)) /* if I have delete mode */
+ (void) tputs(Str(T_dm), 1, term__putc);
+
+ if (GoodStr(T_dc)) /* else do one at a time */
+ while (num--)
+ (void) tputs(Str(T_dc), 1, term__putc);
+
+ if (GoodStr(T_ed)) /* if I have delete mode */
+ (void) tputs(Str(T_ed), 1, term__putc);
+}
+
+
+/* term_insertwrite():
+ * Puts terminal in insert character mode or inserts num
+ * characters in the line
+ */
+protected void
+term_insertwrite(EditLine *el, char *cp, int num)
+{
+ if (num <= 0)
+ return;
+ if (!EL_CAN_INSERT) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile, " ERROR: cannot insert \n");
+#endif /* DEBUG_EDIT */
+ return;
+ }
+ if (num > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "StartInsert: num is riduculous: %d\r\n", num);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if (GoodStr(T_IC)) /* if I have multiple insert */
+ if ((num > 1) || !GoodStr(T_ic)) {
+ /* if ic would be more expensive */
+ (void) tputs(tgoto(Str(T_IC), num, num),
+ num, term__putc);
+ term_overwrite(el, cp, num);
+ /* this updates el_cursor.h */
+ return;
+ }
+ if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */
+ (void) tputs(Str(T_im), 1, term__putc);
+
+ el->el_cursor.h += num;
+ do
+ term__putc(*cp++);
+ while (--num);
+
+ if (GoodStr(T_ip)) /* have to make num chars insert */
+ (void) tputs(Str(T_ip), 1, term__putc);
+
+ (void) tputs(Str(T_ei), 1, term__putc);
+ return;
+ }
+ do {
+ if (GoodStr(T_ic)) /* have to make num chars insert */
+ (void) tputs(Str(T_ic), 1, term__putc);
+ /* insert a char */
+
+ term__putc(*cp++);
+
+ el->el_cursor.h++;
+
+ if (GoodStr(T_ip)) /* have to make num chars insert */
+ (void) tputs(Str(T_ip), 1, term__putc);
+ /* pad the inserted char */
+
+ } while (--num);
+}
+
+
+/* term_clear_EOL():
+ * clear to end of line. There are num characters to clear
+ */
+protected void
+term_clear_EOL(EditLine *el, int num)
+{
+ int i;
+
+ if (EL_CAN_CEOL && GoodStr(T_ce))
+ (void) tputs(Str(T_ce), 1, term__putc);
+ else {
+ for (i = 0; i < num; i++)
+ term__putc(' ');
+ el->el_cursor.h += num; /* have written num spaces */
+ }
+}
+
+
+/* term_clear_screen():
+ * Clear the screen
+ */
+protected void
+term_clear_screen(EditLine *el)
+{ /* clear the whole screen and home */
+
+ if (GoodStr(T_cl))
+ /* send the clear screen code */
+ (void) tputs(Str(T_cl), Val(T_li), term__putc);
+ else if (GoodStr(T_ho) && GoodStr(T_cd)) {
+ (void) tputs(Str(T_ho), Val(T_li), term__putc); /* home */
+ /* clear to bottom of screen */
+ (void) tputs(Str(T_cd), Val(T_li), term__putc);
+ } else {
+ term__putc('\r');
+ term__putc('\n');
+ }
+}
+
+
+/* term_beep():
+ * Beep the way the terminal wants us
+ */
+protected void
+term_beep(EditLine *el)
+{
+ if (GoodStr(T_bl))
+ /* what termcap says we should use */
+ (void) tputs(Str(T_bl), 1, term__putc);
+ else
+ term__putc('\007'); /* an ASCII bell; ^G */
+}
+
+
+#ifdef notdef
+/* term_clear_to_bottom():
+ * Clear to the bottom of the screen
+ */
+protected void
+term_clear_to_bottom(EditLine *el)
+{
+ if (GoodStr(T_cd))
+ (void) tputs(Str(T_cd), Val(T_li), term__putc);
+ else if (GoodStr(T_ce))
+ (void) tputs(Str(T_ce), Val(T_li), term__putc);
+}
+#endif
+
+
+/* term_set():
+ * Read in the terminal capabilities from the requested terminal
+ */
+protected int
+term_set(EditLine *el, const char *term)
+{
+ int i;
+ char buf[TC_BUFSIZE];
+ char *area;
+ const struct termcapstr *t;
+ sigset_t oset, nset;
+ int lins, cols;
+
+ (void) sigemptyset(&nset);
+ (void) sigaddset(&nset, SIGWINCH);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ area = buf;
+
+
+ if (term == NULL)
+ term = getenv("TERM");
+
+ if (!term || !term[0])
+ term = "dumb";
+
+ if (strcmp(term, "emacs") == 0)
+ el->el_flags |= EDIT_DISABLED;
+
+ memset(el->el_term.t_cap, 0, TC_BUFSIZE);
+
+ i = tgetent(el->el_term.t_cap, term);
+
+ if (i <= 0) {
+ if (i == -1)
+ (void) fprintf(el->el_errfile,
+ "Cannot read termcap database;\n");
+ else if (i == 0)
+ (void) fprintf(el->el_errfile,
+ "No entry for terminal type \"%s\";\n", term);
+ (void) fprintf(el->el_errfile,
+ "using dumb terminal settings.\n");
+ Val(T_co) = 80; /* do a dumb terminal */
+ Val(T_pt) = Val(T_km) = Val(T_li) = 0;
+ Val(T_xt) = Val(T_MT);
+ for (t = tstr; t->name != NULL; t++)
+ term_alloc(el, t, NULL);
+ } else {
+ /* auto/magic margins */
+ Val(T_am) = tgetflag("am");
+ Val(T_xn) = tgetflag("xn");
+ /* Can we tab */
+ Val(T_pt) = tgetflag("pt");
+ Val(T_xt) = tgetflag("xt");
+ /* do we have a meta? */
+ Val(T_km) = tgetflag("km");
+ Val(T_MT) = tgetflag("MT");
+ /* Get the size */
+ Val(T_co) = tgetnum("co");
+ Val(T_li) = tgetnum("li");
+ for (t = tstr; t->name != NULL; t++)
+ term_alloc(el, t, tgetstr((char *)t->name, &area));
+ }
+
+ if (Val(T_co) < 2)
+ Val(T_co) = 80; /* just in case */
+ if (Val(T_li) < 1)
+ Val(T_li) = 24;
+
+ el->el_term.t_size.v = Val(T_co);
+ el->el_term.t_size.h = Val(T_li);
+
+ term_setflags(el);
+
+ /* get the correct window size */
+ (void) term_get_size(el, &lins, &cols);
+ if (term_change_size(el, lins, cols) == -1)
+ return (-1);
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ term_bind_arrow(el);
+ return (i <= 0 ? -1 : 0);
+}
+
+
+/* term_get_size():
+ * Return the new window size in lines and cols, and
+ * true if the size was changed.
+ */
+protected int
+term_get_size(EditLine *el, int *lins, int *cols)
+{
+
+ *cols = Val(T_co);
+ *lins = Val(T_li);
+
+#ifdef TIOCGWINSZ
+ {
+ struct winsize ws;
+ if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) {
+ if (ws.ws_col)
+ *cols = ws.ws_col;
+ if (ws.ws_row)
+ *lins = ws.ws_row;
+ }
+ }
+#endif
+#ifdef TIOCGSIZE
+ {
+ struct ttysize ts;
+ if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) {
+ if (ts.ts_cols)
+ *cols = ts.ts_cols;
+ if (ts.ts_lines)
+ *lins = ts.ts_lines;
+ }
+ }
+#endif
+ return (Val(T_co) != *cols || Val(T_li) != *lins);
+}
+
+
+/* term_change_size():
+ * Change the size of the terminal
+ */
+protected int
+term_change_size(EditLine *el, int lins, int cols)
+{
+ /*
+ * Just in case
+ */
+ Val(T_co) = (cols < 2) ? 80 : cols;
+ Val(T_li) = (lins < 1) ? 24 : lins;
+
+ /* re-make display buffers */
+ if (term_rebuffer_display(el) == -1)
+ return (-1);
+ re_clear_display(el);
+ return (0);
+}
+
+
+/* term_init_arrow():
+ * Initialize the arrow key bindings from termcap
+ */
+private void
+term_init_arrow(EditLine *el)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+
+ arrow[A_K_DN].name = "down";
+ arrow[A_K_DN].key = T_kd;
+ arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
+ arrow[A_K_DN].type = XK_CMD;
+
+ arrow[A_K_UP].name = "up";
+ arrow[A_K_UP].key = T_ku;
+ arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
+ arrow[A_K_UP].type = XK_CMD;
+
+ arrow[A_K_LT].name = "left";
+ arrow[A_K_LT].key = T_kl;
+ arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
+ arrow[A_K_LT].type = XK_CMD;
+
+ arrow[A_K_RT].name = "right";
+ arrow[A_K_RT].key = T_kr;
+ arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
+ arrow[A_K_RT].type = XK_CMD;
+
+ arrow[A_K_HO].name = "home";
+ arrow[A_K_HO].key = T_kh;
+ arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
+ arrow[A_K_HO].type = XK_CMD;
+
+ arrow[A_K_EN].name = "end";
+ arrow[A_K_EN].key = T_at7;
+ arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
+ arrow[A_K_EN].type = XK_CMD;
+}
+
+
+/* term_reset_arrow():
+ * Reset arrow key bindings
+ */
+private void
+term_reset_arrow(EditLine *el)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+ static const char strA[] = {033, '[', 'A', '\0'};
+ static const char strB[] = {033, '[', 'B', '\0'};
+ static const char strC[] = {033, '[', 'C', '\0'};
+ static const char strD[] = {033, '[', 'D', '\0'};
+ static const char strH[] = {033, '[', 'H', '\0'};
+ static const char strF[] = {033, '[', 'F', '\0'};
+ static const char stOA[] = {033, 'O', 'A', '\0'};
+ static const char stOB[] = {033, 'O', 'B', '\0'};
+ static const char stOC[] = {033, 'O', 'C', '\0'};
+ static const char stOD[] = {033, 'O', 'D', '\0'};
+ static const char stOH[] = {033, 'O', 'H', '\0'};
+ static const char stOF[] = {033, 'O', 'F', '\0'};
+
+ key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+
+ if (el->el_map.type == MAP_VI) {
+ key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ }
+}
+
+
+/* term_set_arrow():
+ * Set an arrow key binding
+ */
+protected int
+term_set_arrow(EditLine *el, const char *name, key_value_t *fun, int type)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+ int i;
+
+ for (i = 0; i < A_K_NKEYS; i++)
+ if (strcmp(name, arrow[i].name) == 0) {
+ arrow[i].fun = *fun;
+ arrow[i].type = type;
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* term_clear_arrow():
+ * Clear an arrow key binding
+ */
+protected int
+term_clear_arrow(EditLine *el, const char *name)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+ int i;
+
+ for (i = 0; i < A_K_NKEYS; i++)
+ if (strcmp(name, arrow[i].name) == 0) {
+ arrow[i].type = XK_NOD;
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* term_print_arrow():
+ * Print the arrow key bindings
+ */
+protected void
+term_print_arrow(EditLine *el, const char *name)
+{
+ int i;
+ fkey_t *arrow = el->el_term.t_fkey;
+
+ for (i = 0; i < A_K_NKEYS; i++)
+ if (*name == '\0' || strcmp(name, arrow[i].name) == 0)
+ if (arrow[i].type != XK_NOD)
+ key_kprint(el, arrow[i].name, &arrow[i].fun,
+ arrow[i].type);
+}
+
+
+/* term_bind_arrow():
+ * Bind the arrow keys
+ */
+protected void
+term_bind_arrow(EditLine *el)
+{
+ el_action_t *map;
+ const el_action_t *dmap;
+ int i, j;
+ char *p;
+ fkey_t *arrow = el->el_term.t_fkey;
+
+ /* Check if the components needed are initialized */
+ if (el->el_term.t_buf == NULL || el->el_map.key == NULL)
+ return;
+
+ map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
+ dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
+
+ term_reset_arrow(el);
+
+ for (i = 0; i < A_K_NKEYS; i++) {
+ p = el->el_term.t_str[arrow[i].key];
+ if (p && *p) {
+ j = (unsigned char) *p;
+ /*
+ * Assign the arrow keys only if:
+ *
+ * 1. They are multi-character arrow keys and the user
+ * has not re-assigned the leading character, or
+ * has re-assigned the leading character to be
+ * ED_SEQUENCE_LEAD_IN
+ * 2. They are single arrow keys pointing to an
+ * unassigned key.
+ */
+ if (arrow[i].type == XK_NOD)
+ key_clear(el, map, p);
+ else {
+ if (p[1] && (dmap[j] == map[j] ||
+ map[j] == ED_SEQUENCE_LEAD_IN)) {
+ key_add(el, p, &arrow[i].fun,
+ arrow[i].type);
+ map[j] = ED_SEQUENCE_LEAD_IN;
+ } else if (map[j] == ED_UNASSIGNED) {
+ key_clear(el, map, p);
+ if (arrow[i].type == XK_CMD)
+ map[j] = arrow[i].fun.cmd;
+ else
+ key_add(el, p, &arrow[i].fun,
+ arrow[i].type);
+ }
+ }
+ }
+ }
+}
+
+
+/* term__putc():
+ * Add a character
+ */
+protected int
+term__putc(int c)
+{
+
+ return (fputc(c, term_outfile));
+}
+
+
+/* term__flush():
+ * Flush output
+ */
+protected void
+term__flush(void)
+{
+
+ (void) fflush(term_outfile);
+}
+
+
+/* term_telltc():
+ * Print the current termcap characteristics
+ */
+protected int
+/*ARGSUSED*/
+term_telltc(EditLine *el, int argc, const char **argv)
+{
+ const struct termcapstr *t;
+ char **ts;
+ char upbuf[EL_BUFSIZ];
+
+ (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
+ (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
+ (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
+ Val(T_co), Val(T_li));
+ (void) fprintf(el->el_outfile,
+ "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
+ (void) fprintf(el->el_outfile,
+ "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
+ (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
+ EL_HAS_AUTO_MARGINS ? "has" : "does not have");
+ if (EL_HAS_AUTO_MARGINS)
+ (void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
+ EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
+
+ for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++)
+ (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
+ t->long_name,
+ t->name, *ts && **ts ?
+ key__decode_str(*ts, upbuf, "") : "(empty)");
+ (void) fputc('\n', el->el_outfile);
+ return (0);
+}
+
+
+/* term_settc():
+ * Change the current terminal characteristics
+ */
+protected int
+/*ARGSUSED*/
+term_settc(EditLine *el, int argc, const char **argv)
+{
+ const struct termcapstr *ts;
+ const struct termcapval *tv;
+ const char *what, *how;
+
+ if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
+ return (-1);
+
+ what = argv[1];
+ how = argv[2];
+
+ /*
+ * Do the strings first
+ */
+ for (ts = tstr; ts->name != NULL; ts++)
+ if (strcmp(ts->name, what) == 0)
+ break;
+
+ if (ts->name != NULL) {
+ term_alloc(el, ts, how);
+ term_setflags(el);
+ return (0);
+ }
+ /*
+ * Do the numeric ones second
+ */
+ for (tv = tval; tv->name != NULL; tv++)
+ if (strcmp(tv->name, what) == 0)
+ break;
+
+ if (tv->name != NULL) {
+ if (tv == &tval[T_pt] || tv == &tval[T_km] ||
+ tv == &tval[T_am] || tv == &tval[T_xn]) {
+ if (strcmp(how, "yes") == 0)
+ el->el_term.t_val[tv - tval] = 1;
+ else if (strcmp(how, "no") == 0)
+ el->el_term.t_val[tv - tval] = 0;
+ else {
+ (void) fprintf(el->el_errfile,
+ "settc: Bad value `%s'.\n", how);
+ return (-1);
+ }
+ term_setflags(el);
+ if (term_change_size(el, Val(T_li), Val(T_co)) == -1)
+ return (-1);
+ return (0);
+ } else {
+ long i;
+ char *ep;
+
+ i = strtol(how, &ep, 10);
+ if (*ep != '\0') {
+ (void) fprintf(el->el_errfile,
+ "settc: Bad value `%s'.\n", how);
+ return (-1);
+ }
+ el->el_term.t_val[tv - tval] = (int) i;
+ el->el_term.t_size.v = Val(T_co);
+ el->el_term.t_size.h = Val(T_li);
+ if (tv == &tval[T_co] || tv == &tval[T_li])
+ if (term_change_size(el, Val(T_li), Val(T_co))
+ == -1)
+ return (-1);
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+
+/* term_echotc():
+ * Print the termcap string out with variable substitution
+ */
+protected int
+/*ARGSUSED*/
+term_echotc(EditLine *el, int argc, const char **argv)
+{
+ char *cap, *scap, *ep;
+ int arg_need, arg_cols, arg_rows;
+ int verbose = 0, silent = 0;
+ char *area;
+ static const char fmts[] = "%s\n", fmtd[] = "%d\n";
+ const struct termcapstr *t;
+ char buf[TC_BUFSIZE];
+ long i;
+
+ area = buf;
+
+ if (argv == NULL || argv[1] == NULL)
+ return (-1);
+ argv++;
+
+ if (argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 's':
+ silent = 1;
+ break;
+ default:
+ /* stderror(ERR_NAME | ERR_TCUSAGE); */
+ break;
+ }
+ argv++;
+ }
+ if (!*argv || *argv[0] == '\0')
+ return (0);
+ if (strcmp(*argv, "tabs") == 0) {
+ (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "meta") == 0) {
+ (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "xn") == 0) {
+ (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
+ "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "am") == 0) {
+ (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
+ "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "baud") == 0) {
+#ifdef notdef
+ int i;
+
+ for (i = 0; baud_rate[i].b_name != NULL; i++)
+ if (el->el_tty.t_speed == baud_rate[i].b_rate) {
+ (void) fprintf(el->el_outfile, fmts,
+ baud_rate[i].b_name);
+ return (0);
+ }
+ (void) fprintf(el->el_outfile, fmtd, 0);
+#else
+ (void) fprintf(el->el_outfile, fmtd, (int) el->el_tty.t_speed);
+#endif
+ return (0);
+ } else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) {
+ (void) fprintf(el->el_outfile, fmtd, Val(T_li));
+ return (0);
+ } else if (strcmp(*argv, "cols") == 0) {
+ (void) fprintf(el->el_outfile, fmtd, Val(T_co));
+ return (0);
+ }
+ /*
+ * Try to use our local definition first
+ */
+ scap = NULL;
+ for (t = tstr; t->name != NULL; t++)
+ if (strcmp(t->name, *argv) == 0) {
+ scap = el->el_term.t_str[t - tstr];
+ break;
+ }
+ if (t->name == NULL)
+ scap = tgetstr((char *)argv, &area);
+ if (!scap || scap[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Termcap parameter `%s' not found.\n",
+ *argv);
+ return (-1);
+ }
+ /*
+ * Count home many values we need for this capability.
+ */
+ for (cap = scap, arg_need = 0; *cap; cap++)
+ if (*cap == '%')
+ switch (*++cap) {
+ case 'd':
+ case '2':
+ case '3':
+ case '.':
+ case '+':
+ arg_need++;
+ break;
+ case '%':
+ case '>':
+ case 'i':
+ case 'r':
+ case 'n':
+ case 'B':
+ case 'D':
+ break;
+ default:
+ /*
+ * hpux has lot's of them...
+ */
+ if (verbose)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: unknown termcap %% `%c'.\n",
+ *cap);
+ /* This is bad, but I won't complain */
+ break;
+ }
+
+ switch (arg_need) {
+ case 0:
+ argv++;
+ if (*argv && *argv[0]) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Extra argument `%s'.\n",
+ *argv);
+ return (-1);
+ }
+ (void) tputs(scap, 1, term__putc);
+ break;
+ case 1:
+ argv++;
+ if (!*argv || *argv[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Missing argument.\n");
+ return (-1);
+ }
+ arg_cols = 0;
+ i = strtol(*argv, &ep, 10);
+ if (*ep != '\0' || i < 0) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s' for rows.\n",
+ *argv);
+ return (-1);
+ }
+ arg_rows = (int) i;
+ argv++;
+ if (*argv && *argv[0]) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Extra argument `%s'.\n",
+ *argv);
+ return (-1);
+ }
+ (void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc);
+ break;
+ default:
+ /* This is wrong, but I will ignore it... */
+ if (verbose)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Too many required arguments (%d).\n",
+ arg_need);
+ /* FALLTHROUGH */
+ case 2:
+ argv++;
+ if (!*argv || *argv[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Missing argument.\n");
+ return (-1);
+ }
+ i = strtol(*argv, &ep, 10);
+ if (*ep != '\0' || i < 0) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s' for cols.\n",
+ *argv);
+ return (-1);
+ }
+ arg_cols = (int) i;
+ argv++;
+ if (!*argv || *argv[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Missing argument.\n");
+ return (-1);
+ }
+ i = strtol(*argv, &ep, 10);
+ if (*ep != '\0' || i < 0) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s' for rows.\n",
+ *argv);
+ return (-1);
+ }
+ arg_rows = (int) i;
+ if (*ep != '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s'.\n", *argv);
+ return (-1);
+ }
+ argv++;
+ if (*argv && *argv[0]) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Extra argument `%s'.\n",
+ *argv);
+ return (-1);
+ }
+ (void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows,
+ term__putc);
+ break;
+ }
+ return (0);
+}
diff --git a/trunk/main/editline/term.h b/trunk/main/editline/term.h
new file mode 100644
index 000000000..47e08e84b
--- /dev/null
+++ b/trunk/main/editline/term.h
@@ -0,0 +1,124 @@
+/* $NetBSD: term.h,v 1.13 2002/03/18 16:01:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)term.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.term.h: Termcap header
+ */
+#ifndef _h_el_term
+#define _h_el_term
+
+#include "histedit.h"
+
+typedef struct { /* Symbolic function key bindings */
+ const char *name; /* name of the key */
+ int key; /* Index in termcap table */
+ key_value_t fun; /* Function bound to it */
+ int type; /* Type of function */
+} fkey_t;
+
+typedef struct {
+ coord_t t_size; /* # lines and cols */
+ int t_flags;
+#define TERM_CAN_INSERT 0x001 /* Has insert cap */
+#define TERM_CAN_DELETE 0x002 /* Has delete cap */
+#define TERM_CAN_CEOL 0x004 /* Has CEOL cap */
+#define TERM_CAN_TAB 0x008 /* Can use tabs */
+#define TERM_CAN_ME 0x010 /* Can turn all attrs. */
+#define TERM_CAN_UP 0x020 /* Can move up */
+#define TERM_HAS_META 0x040 /* Has a meta key */
+#define TERM_HAS_AUTO_MARGINS 0x080 /* Has auto margins */
+#define TERM_HAS_MAGIC_MARGINS 0x100 /* Has magic margins */
+ char *t_buf; /* Termcap buffer */
+ int t_loc; /* location used */
+ char **t_str; /* termcap strings */
+ int *t_val; /* termcap values */
+ char *t_cap; /* Termcap buffer */
+ fkey_t *t_fkey; /* Array of keys */
+} el_term_t;
+
+/*
+ * fKey indexes
+ */
+#define A_K_DN 0
+#define A_K_UP 1
+#define A_K_LT 2
+#define A_K_RT 3
+#define A_K_HO 4
+#define A_K_EN 5
+#define A_K_NKEYS 6
+
+protected void term_move_to_line(EditLine *, int);
+protected void term_move_to_char(EditLine *, int);
+protected void term_clear_EOL(EditLine *, int);
+protected void term_overwrite(EditLine *, const char *, int);
+protected void term_insertwrite(EditLine *, char *, int);
+protected void term_deletechars(EditLine *, int);
+protected void term_clear_screen(EditLine *);
+protected void term_beep(EditLine *);
+protected int term_change_size(EditLine *, int, int);
+protected int term_get_size(EditLine *, int *, int *);
+protected int term_init(EditLine *);
+protected void term_bind_arrow(EditLine *);
+protected void term_print_arrow(EditLine *, const char *);
+protected int term_clear_arrow(EditLine *, const char *);
+protected int term_set_arrow(EditLine *, const char *, key_value_t *, int);
+protected void term_end(EditLine *);
+protected int term_set(EditLine *, const char *);
+protected int term_settc(EditLine *, int, const char **);
+protected int term_telltc(EditLine *, int, const char **);
+protected int term_echotc(EditLine *, int, const char **);
+protected int term__putc(int);
+protected void term__flush(void);
+
+/*
+ * Easy access macros
+ */
+#define EL_FLAGS (el)->el_term.t_flags
+
+#define EL_CAN_INSERT (EL_FLAGS & TERM_CAN_INSERT)
+#define EL_CAN_DELETE (EL_FLAGS & TERM_CAN_DELETE)
+#define EL_CAN_CEOL (EL_FLAGS & TERM_CAN_CEOL)
+#define EL_CAN_TAB (EL_FLAGS & TERM_CAN_TAB)
+#define EL_CAN_ME (EL_FLAGS & TERM_CAN_ME)
+#define EL_HAS_META (EL_FLAGS & TERM_HAS_META)
+#define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS)
+#define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS)
+
+#endif /* _h_el_term */
diff --git a/trunk/main/editline/tokenizer.c b/trunk/main/editline/tokenizer.c
new file mode 100644
index 000000000..f0de39bc9
--- /dev/null
+++ b/trunk/main/editline/tokenizer.c
@@ -0,0 +1,397 @@
+/* $NetBSD: tokenizer.c,v 1.10 2002/03/18 16:01:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)tokenizer.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: tokenizer.c,v 1.10 2002/03/18 16:01:00 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * tokenize.c: Bourne shell like tokenizer
+ */
+#include <string.h>
+#include <stdlib.h>
+#include "tokenizer.h"
+
+typedef enum {
+ Q_none, Q_single, Q_double, Q_one, Q_doubleone
+} quote_t;
+
+#define IFS "\t \n"
+
+#define TOK_KEEP 1
+#define TOK_EAT 2
+
+#define WINCR 20
+#define AINCR 10
+
+#define tok_malloc(a) malloc(a)
+#define tok_free(a) free(a)
+#define tok_realloc(a, b) realloc(a, b)
+
+
+struct tokenizer {
+ char *ifs; /* In field separator */
+ int argc, amax; /* Current and maximum number of args */
+ char **argv; /* Argument list */
+ char *wptr, *wmax; /* Space and limit on the word buffer */
+ char *wstart; /* Beginning of next word */
+ char *wspace; /* Space of word buffer */
+ quote_t quote; /* Quoting state */
+ int flags; /* flags; */
+};
+
+
+private void tok_finish(Tokenizer *);
+
+
+/* tok_finish():
+ * Finish a word in the tokenizer.
+ */
+private void
+tok_finish(Tokenizer *tok)
+{
+
+ *tok->wptr = '\0';
+ if ((tok->flags & TOK_KEEP) || tok->wptr != tok->wstart) {
+ tok->argv[tok->argc++] = tok->wstart;
+ tok->argv[tok->argc] = NULL;
+ tok->wstart = ++tok->wptr;
+ }
+ tok->flags &= ~TOK_KEEP;
+}
+
+
+/* tok_init():
+ * Initialize the tokenizer
+ */
+public Tokenizer *
+tok_init(const char *ifs)
+{
+ Tokenizer *tok = (Tokenizer *) tok_malloc(sizeof(Tokenizer));
+
+ tok->ifs = strdup(ifs ? ifs : IFS);
+ tok->argc = 0;
+ tok->amax = AINCR;
+ tok->argv = (char **) tok_malloc(sizeof(char *) * tok->amax);
+ if (tok->argv == NULL)
+ return (NULL);
+ tok->argv[0] = NULL;
+ tok->wspace = (char *) tok_malloc(WINCR);
+ if (tok->wspace == NULL)
+ return (NULL);
+ tok->wmax = tok->wspace + WINCR;
+ tok->wstart = tok->wspace;
+ tok->wptr = tok->wspace;
+ tok->flags = 0;
+ tok->quote = Q_none;
+
+ return (tok);
+}
+
+
+/* tok_reset():
+ * Reset the tokenizer
+ */
+public void
+tok_reset(Tokenizer *tok)
+{
+
+ tok->argc = 0;
+ tok->wstart = tok->wspace;
+ tok->wptr = tok->wspace;
+ tok->flags = 0;
+ tok->quote = Q_none;
+}
+
+
+/* tok_end():
+ * Clean up
+ */
+public void
+tok_end(Tokenizer *tok)
+{
+
+ tok_free((ptr_t) tok->ifs);
+ tok_free((ptr_t) tok->wspace);
+ tok_free((ptr_t) tok->argv);
+ tok_free((ptr_t) tok);
+}
+
+
+
+/* tok_line():
+ * Bourne shell like tokenizing
+ * Return:
+ * -1: Internal error
+ * 3: Quoted return
+ * 2: Unmatched double quote
+ * 1: Unmatched single quote
+ * 0: Ok
+ */
+public int
+tok_line(Tokenizer *tok, const char *line, int *argc, const char ***argv)
+{
+ const char *ptr;
+
+ for (;;) {
+ switch (*(ptr = line++)) {
+ case '\'':
+ tok->flags |= TOK_KEEP;
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none:
+ tok->quote = Q_single; /* Enter single quote
+ * mode */
+ break;
+
+ case Q_single: /* Exit single quote mode */
+ tok->quote = Q_none;
+ break;
+
+ case Q_one: /* Quote this ' */
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_double: /* Stay in double quote mode */
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_doubleone: /* Quote this ' */
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ case '"':
+ tok->flags &= ~TOK_EAT;
+ tok->flags |= TOK_KEEP;
+ switch (tok->quote) {
+ case Q_none: /* Enter double quote mode */
+ tok->quote = Q_double;
+ break;
+
+ case Q_double: /* Exit double quote mode */
+ tok->quote = Q_none;
+ break;
+
+ case Q_one: /* Quote this " */
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_single: /* Stay in single quote mode */
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_doubleone: /* Quote this " */
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ case '\\':
+ tok->flags |= TOK_KEEP;
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none: /* Quote next character */
+ tok->quote = Q_one;
+ break;
+
+ case Q_double: /* Quote next character */
+ tok->quote = Q_doubleone;
+ break;
+
+ case Q_one: /* Quote this, restore state */
+ *tok->wptr++ = *ptr;
+ tok->quote = Q_none;
+ break;
+
+ case Q_single: /* Stay in single quote mode */
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_doubleone: /* Quote this \ */
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ case '\n':
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none:
+ tok_finish(tok);
+ *argv = (const char **)tok->argv;
+ *argc = tok->argc;
+ return (0);
+
+ case Q_single:
+ case Q_double:
+ *tok->wptr++ = *ptr; /* Add the return */
+ break;
+
+ case Q_doubleone: /* Back to double, eat the '\n' */
+ tok->flags |= TOK_EAT;
+ tok->quote = Q_double;
+ break;
+
+ case Q_one: /* No quote, more eat the '\n' */
+ tok->flags |= TOK_EAT;
+ tok->quote = Q_none;
+ break;
+
+ default:
+ return (0);
+ }
+ break;
+
+ case '\0':
+ switch (tok->quote) {
+ case Q_none:
+ /* Finish word and return */
+ if (tok->flags & TOK_EAT) {
+ tok->flags &= ~TOK_EAT;
+ return (3);
+ }
+ tok_finish(tok);
+ *argv = (const char **)tok->argv;
+ *argc = tok->argc;
+ return (0);
+
+ case Q_single:
+ return (1);
+
+ case Q_double:
+ return (2);
+
+ case Q_doubleone:
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_one:
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ default:
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none:
+ if (strchr(tok->ifs, *ptr) != NULL)
+ tok_finish(tok);
+ else
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_single:
+ case Q_double:
+ *tok->wptr++ = *ptr;
+ break;
+
+
+ case Q_doubleone:
+ *tok->wptr++ = '\\';
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_one:
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+
+ }
+ break;
+ }
+
+ if (tok->wptr >= tok->wmax - 4) {
+ size_t size = tok->wmax - tok->wspace + WINCR;
+ char *s = (char *) tok_realloc(tok->wspace, size);
+ if (s == NULL)
+ return (-1);
+
+ if (s != tok->wspace) {
+ int i;
+ for (i = 0; i < tok->argc; i++) {
+ tok->argv[i] =
+ (tok->argv[i] - tok->wspace) + s;
+ }
+ tok->wptr = (tok->wptr - tok->wspace) + s;
+ tok->wstart = (tok->wstart - tok->wspace) + s;
+ tok->wspace = s;
+ }
+ tok->wmax = s + size;
+ }
+ if (tok->argc >= tok->amax - 4) {
+ char **p;
+ tok->amax += AINCR;
+ p = (char **) tok_realloc(tok->argv,
+ tok->amax * sizeof(char *));
+ if (p == NULL)
+ return (-1);
+ tok->argv = p;
+ }
+ }
+}
diff --git a/trunk/main/editline/tokenizer.h b/trunk/main/editline/tokenizer.h
new file mode 100644
index 000000000..7cc7a3346
--- /dev/null
+++ b/trunk/main/editline/tokenizer.h
@@ -0,0 +1,54 @@
+/* $NetBSD: tokenizer.h,v 1.5 2002/03/18 16:01:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tokenizer.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * tokenizer.h: Header file for tokenizer routines
+ */
+#ifndef _h_tokenizer
+#define _h_tokenizer
+
+typedef struct tokenizer Tokenizer;
+
+Tokenizer *tok_init(const char *);
+void tok_reset(Tokenizer *);
+void tok_end(Tokenizer *);
+int tok_line(Tokenizer *, const char *, int *, const char ***);
+
+#endif /* _h_tokenizer */
diff --git a/trunk/main/editline/tty.c b/trunk/main/editline/tty.c
new file mode 100644
index 000000000..256cf780b
--- /dev/null
+++ b/trunk/main/editline/tty.c
@@ -0,0 +1,1182 @@
+/* $NetBSD: tty.c,v 1.16 2002/03/18 16:01:01 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: tty.c,v 1.16 2002/03/18 16:01:01 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * tty.c: tty interface stuff
+ */
+#include "tty.h"
+#include "el.h"
+
+typedef struct ttymodes_t {
+ const char *m_name;
+ u_int m_value;
+ int m_type;
+} ttymodes_t;
+
+typedef struct ttymap_t {
+ int nch, och; /* Internal and termio rep of chars */
+ el_action_t bind[3]; /* emacs, vi, and vi-cmd */
+} ttymap_t;
+
+
+private const ttyperm_t ttyperm = {
+ {
+ {"iflag:", ICRNL, (INLCR | IGNCR)},
+ {"oflag:", (OPOST | ONLCR), ONLRET},
+ {"cflag:", 0, 0},
+ {"lflag:", (ISIG | ICANON | ECHO | ECHOE | ECHOCTL | IEXTEN),
+ (NOFLSH | ECHONL | EXTPROC | FLUSHO)},
+ {"chars:", 0, 0},
+ },
+ {
+ {"iflag:", (INLCR | ICRNL), IGNCR},
+ {"oflag:", (OPOST | ONLCR), ONLRET},
+ {"cflag:", 0, 0},
+ {"lflag:", ISIG,
+ (NOFLSH | ICANON | ECHO | ECHOK | ECHONL | EXTPROC | IEXTEN | FLUSHO)},
+ {"chars:", (C_SH(C_MIN) | C_SH(C_TIME) | C_SH(C_SWTCH) | C_SH(C_DSWTCH) |
+ C_SH(C_SUSP) | C_SH(C_DSUSP) | C_SH(C_EOL) | C_SH(C_DISCARD) |
+ C_SH(C_PGOFF) | C_SH(C_PAGE) | C_SH(C_STATUS)), 0}
+ },
+ {
+ {"iflag:", 0, IXON | IXOFF | INLCR | ICRNL},
+ {"oflag:", 0, 0},
+ {"cflag:", 0, 0},
+ {"lflag:", 0, ISIG | IEXTEN},
+ {"chars:", 0, 0},
+ }
+};
+
+private const ttychar_t ttychar = {
+ {
+ CINTR, CQUIT, CERASE, CKILL,
+ CEOF, CEOL, CEOL2, CSWTCH,
+ CDSWTCH, CERASE2, CSTART, CSTOP,
+ CWERASE, CSUSP, CDSUSP, CREPRINT,
+ CDISCARD, CLNEXT, CSTATUS, CPAGE,
+ CPGOFF, CKILL2, CBRK, CMIN,
+ CTIME
+ },
+ {
+ CINTR, CQUIT, CERASE, CKILL,
+ _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
+ _POSIX_VDISABLE, CERASE2, CSTART, CSTOP,
+ _POSIX_VDISABLE, CSUSP, _POSIX_VDISABLE, _POSIX_VDISABLE,
+ CDISCARD, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
+ _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1,
+ 0
+ },
+ {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0
+ }
+};
+
+private const ttymap_t tty_map[] = {
+#ifdef VERASE
+ {C_ERASE, VERASE,
+ {ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}},
+#endif /* VERASE */
+#ifdef VERASE2
+ {C_ERASE2, VERASE2,
+ {ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}},
+#endif /* VERASE2 */
+#ifdef VKILL
+ {C_KILL, VKILL,
+ {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}},
+#endif /* VKILL */
+#ifdef VKILL2
+ {C_KILL2, VKILL2,
+ {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}},
+#endif /* VKILL2 */
+#ifdef VEOF
+ {C_EOF, VEOF,
+ {EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED}},
+#endif /* VEOF */
+#ifdef VWERASE
+ {C_WERASE, VWERASE,
+ {ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD}},
+#endif /* VWERASE */
+#ifdef VREPRINT
+ {C_REPRINT, VREPRINT,
+ {ED_REDISPLAY, ED_INSERT, ED_REDISPLAY}},
+#endif /* VREPRINT */
+#ifdef VLNEXT
+ {C_LNEXT, VLNEXT,
+ {ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}},
+#endif /* VLNEXT */
+ {-1, -1,
+ {ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}}
+};
+
+private const ttymodes_t ttymodes[] = {
+#ifdef IGNBRK
+ {"ignbrk", IGNBRK, MD_INP},
+#endif /* IGNBRK */
+#ifdef BRKINT
+ {"brkint", BRKINT, MD_INP},
+#endif /* BRKINT */
+#ifdef IGNPAR
+ {"ignpar", IGNPAR, MD_INP},
+#endif /* IGNPAR */
+#ifdef PARMRK
+ {"parmrk", PARMRK, MD_INP},
+#endif /* PARMRK */
+#ifdef INPCK
+ {"inpck", INPCK, MD_INP},
+#endif /* INPCK */
+#ifdef ISTRIP
+ {"istrip", ISTRIP, MD_INP},
+#endif /* ISTRIP */
+#ifdef INLCR
+ {"inlcr", INLCR, MD_INP},
+#endif /* INLCR */
+#ifdef IGNCR
+ {"igncr", IGNCR, MD_INP},
+#endif /* IGNCR */
+#ifdef ICRNL
+ {"icrnl", ICRNL, MD_INP},
+#endif /* ICRNL */
+#ifdef IUCLC
+ {"iuclc", IUCLC, MD_INP},
+#endif /* IUCLC */
+#ifdef IXON
+ {"ixon", IXON, MD_INP},
+#endif /* IXON */
+#ifdef IXANY
+ {"ixany", IXANY, MD_INP},
+#endif /* IXANY */
+#ifdef IXOFF
+ {"ixoff", IXOFF, MD_INP},
+#endif /* IXOFF */
+#ifdef IMAXBEL
+ {"imaxbel", IMAXBEL, MD_INP},
+#endif /* IMAXBEL */
+
+#ifdef OPOST
+ {"opost", OPOST, MD_OUT},
+#endif /* OPOST */
+#ifdef OLCUC
+ {"olcuc", OLCUC, MD_OUT},
+#endif /* OLCUC */
+#ifdef ONLCR
+ {"onlcr", ONLCR, MD_OUT},
+#endif /* ONLCR */
+#ifdef OCRNL
+ {"ocrnl", OCRNL, MD_OUT},
+#endif /* OCRNL */
+#ifdef ONOCR
+ {"onocr", ONOCR, MD_OUT},
+#endif /* ONOCR */
+#ifdef ONOEOT
+ {"onoeot", ONOEOT, MD_OUT},
+#endif /* ONOEOT */
+#ifdef ONLRET
+ {"onlret", ONLRET, MD_OUT},
+#endif /* ONLRET */
+#ifdef OFILL
+ {"ofill", OFILL, MD_OUT},
+#endif /* OFILL */
+#ifdef OFDEL
+ {"ofdel", OFDEL, MD_OUT},
+#endif /* OFDEL */
+#ifdef NLDLY
+ {"nldly", NLDLY, MD_OUT},
+#endif /* NLDLY */
+#ifdef CRDLY
+ {"crdly", CRDLY, MD_OUT},
+#endif /* CRDLY */
+#ifdef TABDLY
+ {"tabdly", TABDLY, MD_OUT},
+#endif /* TABDLY */
+#ifdef XTABS
+ {"xtabs", XTABS, MD_OUT},
+#endif /* XTABS */
+#ifdef BSDLY
+ {"bsdly", BSDLY, MD_OUT},
+#endif /* BSDLY */
+#ifdef VTDLY
+ {"vtdly", VTDLY, MD_OUT},
+#endif /* VTDLY */
+#ifdef FFDLY
+ {"ffdly", FFDLY, MD_OUT},
+#endif /* FFDLY */
+#ifdef PAGEOUT
+ {"pageout", PAGEOUT, MD_OUT},
+#endif /* PAGEOUT */
+#ifdef WRAP
+ {"wrap", WRAP, MD_OUT},
+#endif /* WRAP */
+
+#ifdef CIGNORE
+ {"cignore", CIGNORE, MD_CTL},
+#endif /* CBAUD */
+#ifdef CBAUD
+ {"cbaud", CBAUD, MD_CTL},
+#endif /* CBAUD */
+#ifdef CSTOPB
+ {"cstopb", CSTOPB, MD_CTL},
+#endif /* CSTOPB */
+#ifdef CREAD
+ {"cread", CREAD, MD_CTL},
+#endif /* CREAD */
+#ifdef PARENB
+ {"parenb", PARENB, MD_CTL},
+#endif /* PARENB */
+#ifdef PARODD
+ {"parodd", PARODD, MD_CTL},
+#endif /* PARODD */
+#ifdef HUPCL
+ {"hupcl", HUPCL, MD_CTL},
+#endif /* HUPCL */
+#ifdef CLOCAL
+ {"clocal", CLOCAL, MD_CTL},
+#endif /* CLOCAL */
+#ifdef LOBLK
+ {"loblk", LOBLK, MD_CTL},
+#endif /* LOBLK */
+#ifdef CIBAUD
+ {"cibaud", CIBAUD, MD_CTL},
+#endif /* CIBAUD */
+#ifdef CRTSCTS
+#ifdef CCTS_OFLOW
+ {"ccts_oflow", CCTS_OFLOW, MD_CTL},
+#else
+ {"crtscts", CRTSCTS, MD_CTL},
+#endif /* CCTS_OFLOW */
+#endif /* CRTSCTS */
+#ifdef CRTS_IFLOW
+ {"crts_iflow", CRTS_IFLOW, MD_CTL},
+#endif /* CRTS_IFLOW */
+#ifdef CDTRCTS
+ {"cdtrcts", CDTRCTS, MD_CTL},
+#endif /* CDTRCTS */
+#ifdef MDMBUF
+ {"mdmbuf", MDMBUF, MD_CTL},
+#endif /* MDMBUF */
+#ifdef RCV1EN
+ {"rcv1en", RCV1EN, MD_CTL},
+#endif /* RCV1EN */
+#ifdef XMT1EN
+ {"xmt1en", XMT1EN, MD_CTL},
+#endif /* XMT1EN */
+
+#ifdef ISIG
+ {"isig", ISIG, MD_LIN},
+#endif /* ISIG */
+#ifdef ICANON
+ {"icanon", ICANON, MD_LIN},
+#endif /* ICANON */
+#ifdef XCASE
+ {"xcase", XCASE, MD_LIN},
+#endif /* XCASE */
+#ifdef ECHO
+ {"echo", ECHO, MD_LIN},
+#endif /* ECHO */
+#ifdef ECHOE
+ {"echoe", ECHOE, MD_LIN},
+#endif /* ECHOE */
+#ifdef ECHOK
+ {"echok", ECHOK, MD_LIN},
+#endif /* ECHOK */
+#ifdef ECHONL
+ {"echonl", ECHONL, MD_LIN},
+#endif /* ECHONL */
+#ifdef NOFLSH
+ {"noflsh", NOFLSH, MD_LIN},
+#endif /* NOFLSH */
+#ifdef TOSTOP
+ {"tostop", TOSTOP, MD_LIN},
+#endif /* TOSTOP */
+#ifdef ECHOCTL
+ {"echoctl", ECHOCTL, MD_LIN},
+#endif /* ECHOCTL */
+#ifdef ECHOPRT
+ {"echoprt", ECHOPRT, MD_LIN},
+#endif /* ECHOPRT */
+#ifdef ECHOKE
+ {"echoke", ECHOKE, MD_LIN},
+#endif /* ECHOKE */
+#ifdef DEFECHO
+ {"defecho", DEFECHO, MD_LIN},
+#endif /* DEFECHO */
+#ifdef FLUSHO
+ {"flusho", FLUSHO, MD_LIN},
+#endif /* FLUSHO */
+#ifdef PENDIN
+ {"pendin", PENDIN, MD_LIN},
+#endif /* PENDIN */
+#ifdef IEXTEN
+ {"iexten", IEXTEN, MD_LIN},
+#endif /* IEXTEN */
+#ifdef NOKERNINFO
+ {"nokerninfo", NOKERNINFO, MD_LIN},
+#endif /* NOKERNINFO */
+#ifdef ALTWERASE
+ {"altwerase", ALTWERASE, MD_LIN},
+#endif /* ALTWERASE */
+#ifdef EXTPROC
+ {"extproc", EXTPROC, MD_LIN},
+#endif /* EXTPROC */
+
+#if defined(VINTR)
+ {"intr", C_SH(C_INTR), MD_CHAR},
+#endif /* VINTR */
+#if defined(VQUIT)
+ {"quit", C_SH(C_QUIT), MD_CHAR},
+#endif /* VQUIT */
+#if defined(VERASE)
+ {"erase", C_SH(C_ERASE), MD_CHAR},
+#endif /* VERASE */
+#if defined(VKILL)
+ {"kill", C_SH(C_KILL), MD_CHAR},
+#endif /* VKILL */
+#if defined(VEOF)
+ {"eof", C_SH(C_EOF), MD_CHAR},
+#endif /* VEOF */
+#if defined(VEOL)
+ {"eol", C_SH(C_EOL), MD_CHAR},
+#endif /* VEOL */
+#if defined(VEOL2)
+ {"eol2", C_SH(C_EOL2), MD_CHAR},
+#endif /* VEOL2 */
+#if defined(VSWTCH)
+ {"swtch", C_SH(C_SWTCH), MD_CHAR},
+#endif /* VSWTCH */
+#if defined(VDSWTCH)
+ {"dswtch", C_SH(C_DSWTCH), MD_CHAR},
+#endif /* VDSWTCH */
+#if defined(VERASE2)
+ {"erase2", C_SH(C_ERASE2), MD_CHAR},
+#endif /* VERASE2 */
+#if defined(VSTART)
+ {"start", C_SH(C_START), MD_CHAR},
+#endif /* VSTART */
+#if defined(VSTOP)
+ {"stop", C_SH(C_STOP), MD_CHAR},
+#endif /* VSTOP */
+#if defined(VWERASE)
+ {"werase", C_SH(C_WERASE), MD_CHAR},
+#endif /* VWERASE */
+#if defined(VSUSP)
+ {"susp", C_SH(C_SUSP), MD_CHAR},
+#endif /* VSUSP */
+#if defined(VDSUSP)
+ {"dsusp", C_SH(C_DSUSP), MD_CHAR},
+#endif /* VDSUSP */
+#if defined(VREPRINT)
+ {"reprint", C_SH(C_REPRINT), MD_CHAR},
+#endif /* VREPRINT */
+#if defined(VDISCARD)
+ {"discard", C_SH(C_DISCARD), MD_CHAR},
+#endif /* VDISCARD */
+#if defined(VLNEXT)
+ {"lnext", C_SH(C_LNEXT), MD_CHAR},
+#endif /* VLNEXT */
+#if defined(VSTATUS)
+ {"status", C_SH(C_STATUS), MD_CHAR},
+#endif /* VSTATUS */
+#if defined(VPAGE)
+ {"page", C_SH(C_PAGE), MD_CHAR},
+#endif /* VPAGE */
+#if defined(VPGOFF)
+ {"pgoff", C_SH(C_PGOFF), MD_CHAR},
+#endif /* VPGOFF */
+#if defined(VKILL2)
+ {"kill2", C_SH(C_KILL2), MD_CHAR},
+#endif /* VKILL2 */
+#if defined(VBRK)
+ {"brk", C_SH(C_BRK), MD_CHAR},
+#endif /* VBRK */
+#if defined(VMIN)
+ {"min", C_SH(C_MIN), MD_CHAR},
+#endif /* VMIN */
+#if defined(VTIME)
+ {"time", C_SH(C_TIME), MD_CHAR},
+#endif /* VTIME */
+ {NULL, 0, -1},
+};
+
+
+
+#define tty_getty(el, td) tcgetattr((el)->el_infd, (td))
+#define tty_setty(el, td) tcsetattr((el)->el_infd, TCSADRAIN, (td))
+
+#define tty__gettabs(td) ((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1)
+#define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8)
+#define tty__cooked_mode(td) ((td)->c_lflag & ICANON)
+
+private void tty__getchar(struct termios *, unsigned char *);
+private void tty__setchar(struct termios *, unsigned char *);
+private speed_t tty__getspeed(struct termios *);
+private int tty_setup(EditLine *);
+
+#define t_qu t_ts
+
+
+/* tty_setup():
+ * Get the tty parameters and initialize the editing state
+ */
+private int
+tty_setup(EditLine *el)
+{
+ int rst = 1;
+
+ if (el->el_flags & EDIT_DISABLED)
+ return (0);
+
+ if (tty_getty(el, &el->el_tty.t_ed) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile,
+ "tty_setup: tty_getty: %s\n", strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed;
+
+ el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex);
+ el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex);
+ el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex);
+
+ el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ex.c_iflag |= el->el_tty.t_t[EX_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ex.c_oflag |= el->el_tty.t_t[EX_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ex.c_cflag |= el->el_tty.t_t[EX_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ex.c_lflag |= el->el_tty.t_t[EX_IO][MD_LIN].t_setmask;
+
+ /*
+ * Reset the tty chars to reasonable defaults
+ * If they are disabled, then enable them.
+ */
+ if (rst) {
+ if (tty__cooked_mode(&el->el_tty.t_ts)) {
+ tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
+ /*
+ * Don't affect CMIN and CTIME for the editor mode
+ */
+ for (rst = 0; rst < C_NCC - 2; rst++)
+ if (el->el_tty.t_c[TS_IO][rst] !=
+ el->el_tty.t_vdisable
+ && el->el_tty.t_c[ED_IO][rst] !=
+ el->el_tty.t_vdisable)
+ el->el_tty.t_c[ED_IO][rst] =
+ el->el_tty.t_c[TS_IO][rst];
+ for (rst = 0; rst < C_NCC; rst++)
+ if (el->el_tty.t_c[TS_IO][rst] !=
+ el->el_tty.t_vdisable)
+ el->el_tty.t_c[EX_IO][rst] =
+ el->el_tty.t_c[TS_IO][rst];
+ }
+ tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
+ if (tty_setty(el, &el->el_tty.t_ex) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile,
+ "tty_setup: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ } else
+ tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
+
+ el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ed.c_iflag |= el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ed.c_oflag |= el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ed.c_cflag |= el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ed.c_lflag |= el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
+
+ tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
+ tty_bind_char(el, 1);
+ return (0);
+}
+
+protected int
+tty_init(EditLine *el)
+{
+
+ el->el_tty.t_mode = EX_IO;
+ el->el_tty.t_vdisable = _POSIX_VDISABLE;
+ (void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t));
+ (void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t));
+ return (tty_setup(el));
+}
+
+
+/* tty_end():
+ * Restore the tty to its original settings
+ */
+protected void
+/*ARGSUSED*/
+tty_end(EditLine *el)
+{
+
+ /* XXX: Maybe reset to an initial state? */
+}
+
+
+/* tty__getspeed():
+ * Get the tty speed
+ */
+private speed_t
+tty__getspeed(struct termios *td)
+{
+ speed_t spd;
+
+ if ((spd = cfgetispeed(td)) == 0)
+ spd = cfgetospeed(td);
+ return (spd);
+}
+
+
+/* tty__getchar():
+ * Get the tty characters
+ */
+private void
+tty__getchar(struct termios *td, unsigned char *s)
+{
+
+#ifdef VINTR
+ s[C_INTR] = td->c_cc[VINTR];
+#endif /* VINTR */
+#ifdef VQUIT
+ s[C_QUIT] = td->c_cc[VQUIT];
+#endif /* VQUIT */
+#ifdef VERASE
+ s[C_ERASE] = td->c_cc[VERASE];
+#endif /* VERASE */
+#ifdef VKILL
+ s[C_KILL] = td->c_cc[VKILL];
+#endif /* VKILL */
+#ifdef VEOF
+ s[C_EOF] = td->c_cc[VEOF];
+#endif /* VEOF */
+#ifdef VEOL
+ s[C_EOL] = td->c_cc[VEOL];
+#endif /* VEOL */
+#ifdef VEOL2
+ s[C_EOL2] = td->c_cc[VEOL2];
+#endif /* VEOL2 */
+#ifdef VSWTCH
+ s[C_SWTCH] = td->c_cc[VSWTCH];
+#endif /* VSWTCH */
+#ifdef VDSWTCH
+ s[C_DSWTCH] = td->c_cc[VDSWTCH];
+#endif /* VDSWTCH */
+#ifdef VERASE2
+ s[C_ERASE2] = td->c_cc[VERASE2];
+#endif /* VERASE2 */
+#ifdef VSTART
+ s[C_START] = td->c_cc[VSTART];
+#endif /* VSTART */
+#ifdef VSTOP
+ s[C_STOP] = td->c_cc[VSTOP];
+#endif /* VSTOP */
+#ifdef VWERASE
+ s[C_WERASE] = td->c_cc[VWERASE];
+#endif /* VWERASE */
+#ifdef VSUSP
+ s[C_SUSP] = td->c_cc[VSUSP];
+#endif /* VSUSP */
+#ifdef VDSUSP
+ s[C_DSUSP] = td->c_cc[VDSUSP];
+#endif /* VDSUSP */
+#ifdef VREPRINT
+ s[C_REPRINT] = td->c_cc[VREPRINT];
+#endif /* VREPRINT */
+#ifdef VDISCARD
+ s[C_DISCARD] = td->c_cc[VDISCARD];
+#endif /* VDISCARD */
+#ifdef VLNEXT
+ s[C_LNEXT] = td->c_cc[VLNEXT];
+#endif /* VLNEXT */
+#ifdef VSTATUS
+ s[C_STATUS] = td->c_cc[VSTATUS];
+#endif /* VSTATUS */
+#ifdef VPAGE
+ s[C_PAGE] = td->c_cc[VPAGE];
+#endif /* VPAGE */
+#ifdef VPGOFF
+ s[C_PGOFF] = td->c_cc[VPGOFF];
+#endif /* VPGOFF */
+#ifdef VKILL2
+ s[C_KILL2] = td->c_cc[VKILL2];
+#endif /* KILL2 */
+#ifdef VMIN
+ s[C_MIN] = td->c_cc[VMIN];
+#endif /* VMIN */
+#ifdef VTIME
+ s[C_TIME] = td->c_cc[VTIME];
+#endif /* VTIME */
+} /* tty__getchar */
+
+
+/* tty__setchar():
+ * Set the tty characters
+ */
+private void
+tty__setchar(struct termios *td, unsigned char *s)
+{
+
+#ifdef VINTR
+ td->c_cc[VINTR] = s[C_INTR];
+#endif /* VINTR */
+#ifdef VQUIT
+ td->c_cc[VQUIT] = s[C_QUIT];
+#endif /* VQUIT */
+#ifdef VERASE
+ td->c_cc[VERASE] = s[C_ERASE];
+#endif /* VERASE */
+#ifdef VKILL
+ td->c_cc[VKILL] = s[C_KILL];
+#endif /* VKILL */
+#ifdef VEOF
+ td->c_cc[VEOF] = s[C_EOF];
+#endif /* VEOF */
+#ifdef VEOL
+ td->c_cc[VEOL] = s[C_EOL];
+#endif /* VEOL */
+#ifdef VEOL2
+ td->c_cc[VEOL2] = s[C_EOL2];
+#endif /* VEOL2 */
+#ifdef VSWTCH
+ td->c_cc[VSWTCH] = s[C_SWTCH];
+#endif /* VSWTCH */
+#ifdef VDSWTCH
+ td->c_cc[VDSWTCH] = s[C_DSWTCH];
+#endif /* VDSWTCH */
+#ifdef VERASE2
+ td->c_cc[VERASE2] = s[C_ERASE2];
+#endif /* VERASE2 */
+#ifdef VSTART
+ td->c_cc[VSTART] = s[C_START];
+#endif /* VSTART */
+#ifdef VSTOP
+ td->c_cc[VSTOP] = s[C_STOP];
+#endif /* VSTOP */
+#ifdef VWERASE
+ td->c_cc[VWERASE] = s[C_WERASE];
+#endif /* VWERASE */
+#ifdef VSUSP
+ td->c_cc[VSUSP] = s[C_SUSP];
+#endif /* VSUSP */
+#ifdef VDSUSP
+ td->c_cc[VDSUSP] = s[C_DSUSP];
+#endif /* VDSUSP */
+#ifdef VREPRINT
+ td->c_cc[VREPRINT] = s[C_REPRINT];
+#endif /* VREPRINT */
+#ifdef VDISCARD
+ td->c_cc[VDISCARD] = s[C_DISCARD];
+#endif /* VDISCARD */
+#ifdef VLNEXT
+ td->c_cc[VLNEXT] = s[C_LNEXT];
+#endif /* VLNEXT */
+#ifdef VSTATUS
+ td->c_cc[VSTATUS] = s[C_STATUS];
+#endif /* VSTATUS */
+#ifdef VPAGE
+ td->c_cc[VPAGE] = s[C_PAGE];
+#endif /* VPAGE */
+#ifdef VPGOFF
+ td->c_cc[VPGOFF] = s[C_PGOFF];
+#endif /* VPGOFF */
+#ifdef VKILL2
+ td->c_cc[VKILL2] = s[C_KILL2];
+#endif /* VKILL2 */
+#ifdef VMIN
+ td->c_cc[VMIN] = s[C_MIN];
+#endif /* VMIN */
+#ifdef VTIME
+ td->c_cc[VTIME] = s[C_TIME];
+#endif /* VTIME */
+} /* tty__setchar */
+
+
+/* tty_bind_char():
+ * Rebind the editline functions
+ */
+protected void
+tty_bind_char(EditLine *el, int force)
+{
+
+ unsigned char *t_n = el->el_tty.t_c[ED_IO];
+ unsigned char *t_o = el->el_tty.t_ed.c_cc;
+ unsigned char new[2], old[2];
+ const ttymap_t *tp;
+ el_action_t *map, *alt;
+ const el_action_t *dmap, *dalt;
+ new[1] = old[1] = '\0';
+
+ map = el->el_map.key;
+ alt = el->el_map.alt;
+ if (el->el_map.type == MAP_VI) {
+ dmap = el->el_map.vii;
+ dalt = el->el_map.vic;
+ } else {
+ dmap = el->el_map.emacs;
+ dalt = NULL;
+ }
+
+ for (tp = tty_map; tp->nch != -1; tp++) {
+ new[0] = t_n[tp->nch];
+ old[0] = t_o[tp->och];
+ if (new[0] == old[0] && !force)
+ continue;
+ /* Put the old default binding back, and set the new binding */
+ key_clear(el, map, (char *)old);
+ map[old[0]] = dmap[old[0]];
+ key_clear(el, map, (char *)new);
+ /* MAP_VI == 1, MAP_EMACS == 0... */
+ map[new[0]] = tp->bind[el->el_map.type];
+ if (dalt) {
+ key_clear(el, alt, (char *)old);
+ alt[old[0]] = dalt[old[0]];
+ key_clear(el, alt, (char *)new);
+ alt[new[0]] = tp->bind[el->el_map.type + 1];
+ }
+ }
+}
+
+
+/* tty_rawmode():
+ * Set terminal into 1 character at a time mode.
+ */
+protected int
+tty_rawmode(EditLine *el)
+{
+
+ if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO)
+ return (0);
+
+ if (el->el_flags & EDIT_DISABLED)
+ return (0);
+
+ if (tty_getty(el, &el->el_tty.t_ts) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "tty_rawmode: tty_getty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ /*
+ * We always keep up with the eight bit setting and the speed of the
+ * tty. But only we only believe changes that are made to cooked mode!
+ */
+ el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts);
+ el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts);
+
+ if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed ||
+ tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) {
+ (void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed);
+ (void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed);
+ (void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed);
+ (void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed);
+ }
+ if (tty__cooked_mode(&el->el_tty.t_ts)) {
+ if (el->el_tty.t_ts.c_cflag != el->el_tty.t_ex.c_cflag) {
+ el->el_tty.t_ex.c_cflag =
+ el->el_tty.t_ts.c_cflag;
+ el->el_tty.t_ex.c_cflag &=
+ ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ex.c_cflag |=
+ el->el_tty.t_t[EX_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_ed.c_cflag =
+ el->el_tty.t_ts.c_cflag;
+ el->el_tty.t_ed.c_cflag &=
+ ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ed.c_cflag |=
+ el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
+ }
+ if ((el->el_tty.t_ts.c_lflag != el->el_tty.t_ex.c_lflag) &&
+ (el->el_tty.t_ts.c_lflag != el->el_tty.t_ed.c_lflag)) {
+ el->el_tty.t_ex.c_lflag =
+ el->el_tty.t_ts.c_lflag;
+ el->el_tty.t_ex.c_lflag &=
+ ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ex.c_lflag |=
+ el->el_tty.t_t[EX_IO][MD_LIN].t_setmask;
+
+ el->el_tty.t_ed.c_lflag =
+ el->el_tty.t_ts.c_lflag;
+ el->el_tty.t_ed.c_lflag &=
+ ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ed.c_lflag |=
+ el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
+ }
+ if ((el->el_tty.t_ts.c_iflag != el->el_tty.t_ex.c_iflag) &&
+ (el->el_tty.t_ts.c_iflag != el->el_tty.t_ed.c_iflag)) {
+ el->el_tty.t_ex.c_iflag =
+ el->el_tty.t_ts.c_iflag;
+ el->el_tty.t_ex.c_iflag &=
+ ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ex.c_iflag |=
+ el->el_tty.t_t[EX_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_ed.c_iflag =
+ el->el_tty.t_ts.c_iflag;
+ el->el_tty.t_ed.c_iflag &=
+ ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ed.c_iflag |=
+ el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
+ }
+ if ((el->el_tty.t_ts.c_oflag != el->el_tty.t_ex.c_oflag) &&
+ (el->el_tty.t_ts.c_oflag != el->el_tty.t_ed.c_oflag)) {
+ el->el_tty.t_ex.c_oflag =
+ el->el_tty.t_ts.c_oflag;
+ el->el_tty.t_ex.c_oflag &=
+ ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ex.c_oflag |=
+ el->el_tty.t_t[EX_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_ed.c_oflag =
+ el->el_tty.t_ts.c_oflag;
+ el->el_tty.t_ed.c_oflag &=
+ ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ed.c_oflag |=
+ el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
+ }
+ if (tty__gettabs(&el->el_tty.t_ex) == 0)
+ el->el_tty.t_tabs = 0;
+ else
+ el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0;
+
+ {
+ int i;
+
+ tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
+ /*
+ * Check if the user made any changes.
+ * If he did, then propagate the changes to the
+ * edit and execute data structures.
+ */
+ for (i = 0; i < C_NCC; i++)
+ if (el->el_tty.t_c[TS_IO][i] !=
+ el->el_tty.t_c[EX_IO][i])
+ break;
+
+ if (i != C_NCC) {
+ /*
+ * Propagate changes only to the unprotected
+ * chars that have been modified just now.
+ */
+ for (i = 0; i < C_NCC; i++) {
+ if (!((el->el_tty.t_t[ED_IO][MD_CHAR].t_setmask & C_SH(i)))
+ && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
+ el->el_tty.t_c[ED_IO][i] = el->el_tty.t_c[TS_IO][i];
+ if (el->el_tty.t_t[ED_IO][MD_CHAR].t_clrmask & C_SH(i))
+ el->el_tty.t_c[ED_IO][i] = el->el_tty.t_vdisable;
+ }
+ tty_bind_char(el, 0);
+ tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
+
+ for (i = 0; i < C_NCC; i++) {
+ if (!((el->el_tty.t_t[EX_IO][MD_CHAR].t_setmask & C_SH(i)))
+ && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
+ el->el_tty.t_c[EX_IO][i] = el->el_tty.t_c[TS_IO][i];
+ if (el->el_tty.t_t[EX_IO][MD_CHAR].t_clrmask & C_SH(i))
+ el->el_tty.t_c[EX_IO][i] = el->el_tty.t_vdisable;
+ }
+ tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
+ }
+ }
+ }
+ if (tty_setty(el, &el->el_tty.t_ed) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = ED_IO;
+ return (0);
+}
+
+
+/* tty_cookedmode():
+ * Set the tty back to normal mode
+ */
+protected int
+tty_cookedmode(EditLine *el)
+{ /* set tty in normal setup */
+
+ if (el->el_tty.t_mode == EX_IO)
+ return (0);
+
+ if (el->el_flags & EDIT_DISABLED)
+ return (0);
+
+ if (tty_setty(el, &el->el_tty.t_ex) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile,
+ "tty_cookedmode: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = EX_IO;
+ return (0);
+}
+
+
+/* tty_quotemode():
+ * Turn on quote mode
+ */
+protected int
+tty_quotemode(EditLine *el)
+{
+ if (el->el_tty.t_mode == QU_IO)
+ return (0);
+
+ el->el_tty.t_qu = el->el_tty.t_ed;
+
+ el->el_tty.t_qu.c_iflag &= ~el->el_tty.t_t[QU_IO][MD_INP].t_clrmask;
+ el->el_tty.t_qu.c_iflag |= el->el_tty.t_t[QU_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_qu.c_oflag &= ~el->el_tty.t_t[QU_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_qu.c_oflag |= el->el_tty.t_t[QU_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_qu.c_cflag &= ~el->el_tty.t_t[QU_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_qu.c_cflag |= el->el_tty.t_t[QU_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_qu.c_lflag &= ~el->el_tty.t_t[QU_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_qu.c_lflag |= el->el_tty.t_t[QU_IO][MD_LIN].t_setmask;
+
+ if (tty_setty(el, &el->el_tty.t_qu) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = QU_IO;
+ return (0);
+}
+
+
+/* tty_noquotemode():
+ * Turn off quote mode
+ */
+protected int
+tty_noquotemode(EditLine *el)
+{
+
+ if (el->el_tty.t_mode != QU_IO)
+ return (0);
+ if (tty_setty(el, &el->el_tty.t_ed) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = ED_IO;
+ return (0);
+}
+
+
+/* tty_stty():
+ * Stty builtin
+ */
+protected int
+/*ARGSUSED*/
+tty_stty(EditLine *el, int argc, const char **argv)
+{
+ const ttymodes_t *m;
+ char x;
+ int aflag = 0;
+ const char *s, *d;
+ const char *name;
+ int z = EX_IO;
+
+ if (argv == NULL)
+ return (-1);
+ name = *argv++;
+
+ while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0')
+ switch (argv[0][1]) {
+ case 'a':
+ aflag++;
+ argv++;
+ break;
+ case 'd':
+ argv++;
+ z = ED_IO;
+ break;
+ case 'x':
+ argv++;
+ z = EX_IO;
+ break;
+ case 'q':
+ argv++;
+ z = QU_IO;
+ break;
+ default:
+ (void) fprintf(el->el_errfile,
+ "%s: Unknown switch `%c'.\n",
+ name, argv[0][1]);
+ return (-1);
+ }
+
+ if (!argv || !*argv) {
+ int i = -1;
+ int len = 0, st = 0, cu;
+ for (m = ttymodes; m->m_name; m++) {
+ if (m->m_type != i) {
+ (void) fprintf(el->el_outfile, "%s%s",
+ i != -1 ? "\n" : "",
+ el->el_tty.t_t[z][m->m_type].t_name);
+ i = m->m_type;
+ st = len =
+ strlen(el->el_tty.t_t[z][m->m_type].t_name);
+ }
+ x = (el->el_tty.t_t[z][i].t_setmask & m->m_value)
+ ? '+' : '\0';
+ x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value)
+ ? '-' : x;
+
+ if (x != '\0' || aflag) {
+
+ cu = strlen(m->m_name) + (x != '\0') + 1;
+
+ if (len + cu >= el->el_term.t_size.h) {
+ (void) fprintf(el->el_outfile, "\n%*s",
+ st, "");
+ len = st + cu;
+ } else
+ len += cu;
+
+ if (x != '\0')
+ (void) fprintf(el->el_outfile, "%c%s ",
+ x, m->m_name);
+ else
+ (void) fprintf(el->el_outfile, "%s ",
+ m->m_name);
+ }
+ }
+ (void) fprintf(el->el_outfile, "\n");
+ return (0);
+ }
+ while (argv && (s = *argv++)) {
+ switch (*s) {
+ case '+':
+ case '-':
+ x = *s++;
+ break;
+ default:
+ x = '\0';
+ break;
+ }
+ d = s;
+ for (m = ttymodes; m->m_name; m++)
+ if (strcmp(m->m_name, d) == 0)
+ break;
+
+ if (!m->m_name) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid argument `%s'.\n", name, d);
+ return (-1);
+ }
+ switch (x) {
+ case '+':
+ el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value;
+ el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
+ break;
+ case '-':
+ el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
+ el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value;
+ break;
+ default:
+ el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
+ el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
+ break;
+ }
+ }
+ return (0);
+}
+
+
+#ifdef notyet
+/* tty_printchar():
+ * DEbugging routine to print the tty characters
+ */
+private void
+tty_printchar(EditLine *el, unsigned char *s)
+{
+ ttyperm_t *m;
+ int i;
+
+ for (i = 0; i < C_NCC; i++) {
+ for (m = el->el_tty.t_t; m->m_name; m++)
+ if (m->m_type == MD_CHAR && C_SH(i) == m->m_value)
+ break;
+ if (m->m_name)
+ (void) fprintf(el->el_errfile, "%s ^%c ",
+ m->m_name, s[i] + 'A' - 1);
+ if (i % 5 == 0)
+ (void) fprintf(el->el_errfile, "\n");
+ }
+ (void) fprintf(el->el_errfile, "\n");
+}
+#endif /* notyet */
diff --git a/trunk/main/editline/tty.h b/trunk/main/editline/tty.h
new file mode 100644
index 000000000..e9597fceb
--- /dev/null
+++ b/trunk/main/editline/tty.h
@@ -0,0 +1,484 @@
+/* $NetBSD: tty.h,v 1.9 2002/03/18 16:01:01 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.tty.h: Local terminal header
+ */
+#ifndef _h_el_tty
+#define _h_el_tty
+
+#include "histedit.h"
+#include <termios.h>
+#include <unistd.h>
+
+/* Define our own since everyone gets it wrong! */
+#define CONTROL(A) ((A) & 037)
+
+/*
+ * Aix compatible names
+ */
+# if defined(VWERSE) && !defined(VWERASE)
+# define VWERASE VWERSE
+# endif /* VWERSE && !VWERASE */
+
+# if defined(VDISCRD) && !defined(VDISCARD)
+# define VDISCARD VDISCRD
+# endif /* VDISCRD && !VDISCARD */
+
+# if defined(VFLUSHO) && !defined(VDISCARD)
+# define VDISCARD VFLUSHO
+# endif /* VFLUSHO && VDISCARD */
+
+# if defined(VSTRT) && !defined(VSTART)
+# define VSTART VSTRT
+# endif /* VSTRT && ! VSTART */
+
+# if defined(VSTAT) && !defined(VSTATUS)
+# define VSTATUS VSTAT
+# endif /* VSTAT && ! VSTATUS */
+
+# ifndef ONLRET
+# define ONLRET 0
+# endif /* ONLRET */
+
+# ifndef TAB3
+# ifdef OXTABS
+# define TAB3 OXTABS
+# else
+# define TAB3 0
+# endif /* OXTABS */
+# endif /* !TAB3 */
+
+# if defined(OXTABS) && !defined(XTABS)
+# define XTABS OXTABS
+# endif /* OXTABS && !XTABS */
+
+# ifndef ONLCR
+# define ONLCR 0
+# endif /* ONLCR */
+
+# ifndef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN */
+
+# ifndef ECHOCTL
+# define ECHOCTL 0
+# endif /* ECHOCTL */
+
+# ifndef PARENB
+# define PARENB 0
+# endif /* PARENB */
+
+# ifndef EXTPROC
+# define EXTPROC 0
+# endif /* EXTPROC */
+
+# ifndef FLUSHO
+# define FLUSHO 0
+# endif /* FLUSHO */
+
+
+# if defined(VDISABLE) && !defined(_POSIX_VDISABLE)
+# define _POSIX_VDISABLE VDISABLE
+# endif /* VDISABLE && ! _POSIX_VDISABLE */
+
+/*
+ * Work around ISC's definition of IEXTEN which is
+ * XCASE!
+ */
+# ifdef ISC
+# if defined(IEXTEN) && defined(XCASE)
+# if IEXTEN == XCASE
+# undef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN == XCASE */
+# endif /* IEXTEN && XCASE */
+# if defined(IEXTEN) && !defined(XCASE)
+# define XCASE IEXTEN
+# undef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN && !XCASE */
+# endif /* ISC */
+
+/*
+ * Work around convex weirdness where turning off IEXTEN makes us
+ * lose all postprocessing!
+ */
+#if defined(convex) || defined(__convex__)
+# if defined(IEXTEN) && IEXTEN != 0
+# undef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN != 0 */
+#endif /* convex || __convex__ */
+
+/*
+ * So that we don't lose job control.
+ */
+#ifdef __SVR4
+# undef CSWTCH
+#endif
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE ((unsigned char) -1)
+#endif /* _POSIX_VDISABLE */
+
+#if !defined(CREPRINT) && defined(CRPRNT)
+# define CREPRINT CRPRNT
+#endif /* !CREPRINT && CRPRNT */
+#if !defined(CDISCARD) && defined(CFLUSH)
+# define CDISCARD CFLUSH
+#endif /* !CDISCARD && CFLUSH */
+
+#ifndef CINTR
+# define CINTR CONTROL('c')
+#endif /* CINTR */
+#ifndef CQUIT
+# define CQUIT 034 /* ^\ */
+#endif /* CQUIT */
+#ifndef CERASE
+# define CERASE 0177 /* ^? */
+#endif /* CERASE */
+#ifndef CKILL
+# define CKILL CONTROL('u')
+#endif /* CKILL */
+#ifndef CEOF
+# define CEOF CONTROL('d')
+#endif /* CEOF */
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif /* CEOL */
+#ifndef CEOL2
+# define CEOL2 _POSIX_VDISABLE
+#endif /* CEOL2 */
+#ifndef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif /* CSWTCH */
+#ifndef CDSWTCH
+# define CDSWTCH _POSIX_VDISABLE
+#endif /* CDSWTCH */
+#ifndef CERASE2
+# define CERASE2 _POSIX_VDISABLE
+#endif /* CERASE2 */
+#ifndef CSTART
+# define CSTART CONTROL('q')
+#endif /* CSTART */
+#ifndef CSTOP
+# define CSTOP CONTROL('s')
+#endif /* CSTOP */
+#ifndef CSUSP
+# define CSUSP CONTROL('z')
+#endif /* CSUSP */
+#ifndef CDSUSP
+# define CDSUSP CONTROL('y')
+#endif /* CDSUSP */
+
+#ifdef hpux
+
+# ifndef CREPRINT
+# define CREPRINT _POSIX_VDISABLE
+# endif /* CREPRINT */
+# ifndef CDISCARD
+# define CDISCARD _POSIX_VDISABLE
+# endif /* CDISCARD */
+# ifndef CLNEXT
+# define CLNEXT _POSIX_VDISABLE
+# endif /* CLNEXT */
+# ifndef CWERASE
+# define CWERASE _POSIX_VDISABLE
+# endif /* CWERASE */
+
+#else /* !hpux */
+
+# ifndef CREPRINT
+# define CREPRINT CONTROL('r')
+# endif /* CREPRINT */
+# ifndef CDISCARD
+# define CDISCARD CONTROL('o')
+# endif /* CDISCARD */
+# ifndef CLNEXT
+# define CLNEXT CONTROL('v')
+# endif /* CLNEXT */
+# ifndef CWERASE
+# define CWERASE CONTROL('w')
+# endif /* CWERASE */
+
+#endif /* hpux */
+
+#ifndef CSTATUS
+# define CSTATUS CONTROL('t')
+#endif /* CSTATUS */
+#ifndef CPAGE
+# define CPAGE ' '
+#endif /* CPAGE */
+#ifndef CPGOFF
+# define CPGOFF CONTROL('m')
+#endif /* CPGOFF */
+#ifndef CKILL2
+# define CKILL2 _POSIX_VDISABLE
+#endif /* CKILL2 */
+#ifndef CBRK
+# ifndef masscomp
+# define CBRK 0377
+# else
+# define CBRK '\0'
+# endif /* masscomp */
+#endif /* CBRK */
+#ifndef CMIN
+# define CMIN CEOF
+#endif /* CMIN */
+#ifndef CTIME
+# define CTIME CEOL
+#endif /* CTIME */
+
+/*
+ * Fix for sun inconsistency. On termio VSUSP and the rest of the
+ * ttychars > NCC are defined. So we undefine them.
+ */
+#if defined(TERMIO) || defined(POSIX)
+# if defined(POSIX) && defined(NCCS)
+# define NUMCC NCCS
+# else
+# ifdef NCC
+# define NUMCC NCC
+# endif /* NCC */
+# endif /* POSIX && NCCS */
+# ifdef NUMCC
+# ifdef VINTR
+# if NUMCC <= VINTR
+# undef VINTR
+# endif /* NUMCC <= VINTR */
+# endif /* VINTR */
+# ifdef VQUIT
+# if NUMCC <= VQUIT
+# undef VQUIT
+# endif /* NUMCC <= VQUIT */
+# endif /* VQUIT */
+# ifdef VERASE
+# if NUMCC <= VERASE
+# undef VERASE
+# endif /* NUMCC <= VERASE */
+# endif /* VERASE */
+# ifdef VKILL
+# if NUMCC <= VKILL
+# undef VKILL
+# endif /* NUMCC <= VKILL */
+# endif /* VKILL */
+# ifdef VEOF
+# if NUMCC <= VEOF
+# undef VEOF
+# endif /* NUMCC <= VEOF */
+# endif /* VEOF */
+# ifdef VEOL
+# if NUMCC <= VEOL
+# undef VEOL
+# endif /* NUMCC <= VEOL */
+# endif /* VEOL */
+# ifdef VEOL2
+# if NUMCC <= VEOL2
+# undef VEOL2
+# endif /* NUMCC <= VEOL2 */
+# endif /* VEOL2 */
+# ifdef VSWTCH
+# if NUMCC <= VSWTCH
+# undef VSWTCH
+# endif /* NUMCC <= VSWTCH */
+# endif /* VSWTCH */
+# ifdef VDSWTCH
+# if NUMCC <= VDSWTCH
+# undef VDSWTCH
+# endif /* NUMCC <= VDSWTCH */
+# endif /* VDSWTCH */
+# ifdef VERASE2
+# if NUMCC <= VERASE2
+# undef VERASE2
+# endif /* NUMCC <= VERASE2 */
+# endif /* VERASE2 */
+# ifdef VSTART
+# if NUMCC <= VSTART
+# undef VSTART
+# endif /* NUMCC <= VSTART */
+# endif /* VSTART */
+# ifdef VSTOP
+# if NUMCC <= VSTOP
+# undef VSTOP
+# endif /* NUMCC <= VSTOP */
+# endif /* VSTOP */
+# ifdef VWERASE
+# if NUMCC <= VWERASE
+# undef VWERASE
+# endif /* NUMCC <= VWERASE */
+# endif /* VWERASE */
+# ifdef VSUSP
+# if NUMCC <= VSUSP
+# undef VSUSP
+# endif /* NUMCC <= VSUSP */
+# endif /* VSUSP */
+# ifdef VDSUSP
+# if NUMCC <= VDSUSP
+# undef VDSUSP
+# endif /* NUMCC <= VDSUSP */
+# endif /* VDSUSP */
+# ifdef VREPRINT
+# if NUMCC <= VREPRINT
+# undef VREPRINT
+# endif /* NUMCC <= VREPRINT */
+# endif /* VREPRINT */
+# ifdef VDISCARD
+# if NUMCC <= VDISCARD
+# undef VDISCARD
+# endif /* NUMCC <= VDISCARD */
+# endif /* VDISCARD */
+# ifdef VLNEXT
+# if NUMCC <= VLNEXT
+# undef VLNEXT
+# endif /* NUMCC <= VLNEXT */
+# endif /* VLNEXT */
+# ifdef VSTATUS
+# if NUMCC <= VSTATUS
+# undef VSTATUS
+# endif /* NUMCC <= VSTATUS */
+# endif /* VSTATUS */
+# ifdef VPAGE
+# if NUMCC <= VPAGE
+# undef VPAGE
+# endif /* NUMCC <= VPAGE */
+# endif /* VPAGE */
+# ifdef VPGOFF
+# if NUMCC <= VPGOFF
+# undef VPGOFF
+# endif /* NUMCC <= VPGOFF */
+# endif /* VPGOFF */
+# ifdef VKILL2
+# if NUMCC <= VKILL2
+# undef VKILL2
+# endif /* NUMCC <= VKILL2 */
+# endif /* VKILL2 */
+# ifdef VBRK
+# if NUMCC <= VBRK
+# undef VBRK
+# endif /* NUMCC <= VBRK */
+# endif /* VBRK */
+# ifdef VMIN
+# if NUMCC <= VMIN
+# undef VMIN
+# endif /* NUMCC <= VMIN */
+# endif /* VMIN */
+# ifdef VTIME
+# if NUMCC <= VTIME
+# undef VTIME
+# endif /* NUMCC <= VTIME */
+# endif /* VTIME */
+# endif /* NUMCC */
+#endif /* !POSIX */
+
+#define C_INTR 0
+#define C_QUIT 1
+#define C_ERASE 2
+#define C_KILL 3
+#define C_EOF 4
+#define C_EOL 5
+#define C_EOL2 6
+#define C_SWTCH 7
+#define C_DSWTCH 8
+#define C_ERASE2 9
+#define C_START 10
+#define C_STOP 11
+#define C_WERASE 12
+#define C_SUSP 13
+#define C_DSUSP 14
+#define C_REPRINT 15
+#define C_DISCARD 16
+#define C_LNEXT 17
+#define C_STATUS 18
+#define C_PAGE 19
+#define C_PGOFF 20
+#define C_KILL2 21
+#define C_BRK 22
+#define C_MIN 23
+#define C_TIME 24
+#define C_NCC 25
+#define C_SH(A) (1 << (A))
+
+/*
+ * Terminal dependend data structures
+ */
+#define EX_IO 0 /* while we are executing */
+#define ED_IO 1 /* while we are editing */
+#define TS_IO 2 /* new mode from terminal */
+#define QU_IO 2 /* used only for quoted chars */
+#define NN_IO 3 /* The number of entries */
+
+#define MD_INP 0
+#define MD_OUT 1
+#define MD_CTL 2
+#define MD_LIN 3
+#define MD_CHAR 4
+#define MD_NN 5
+
+typedef struct {
+ const char *t_name;
+ u_int t_setmask;
+ u_int t_clrmask;
+} ttyperm_t[NN_IO][MD_NN];
+
+typedef unsigned char ttychar_t[NN_IO][C_NCC];
+
+protected int tty_init(EditLine *);
+protected void tty_end(EditLine *);
+protected int tty_stty(EditLine *, int, const char **);
+protected int tty_rawmode(EditLine *);
+protected int tty_cookedmode(EditLine *);
+protected int tty_quotemode(EditLine *);
+protected int tty_noquotemode(EditLine *);
+protected void tty_bind_char(EditLine *, int);
+
+typedef struct {
+ ttyperm_t t_t;
+ ttychar_t t_c;
+ struct termios t_ex, t_ed, t_ts;
+ int t_tabs;
+ int t_eight;
+ speed_t t_speed;
+ int t_mode;
+ unsigned char t_vdisable;
+} el_tty_t;
+
+
+#endif /* _h_el_tty */
diff --git a/trunk/main/editline/vi.c b/trunk/main/editline/vi.c
new file mode 100644
index 000000000..5683c7de0
--- /dev/null
+++ b/trunk/main/editline/vi.c
@@ -0,0 +1,941 @@
+/* $NetBSD: vi.c,v 1.9 2002/03/18 16:01:01 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: vi.c,v 1.9 2002/03/18 16:01:01 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * vi.c: Vi mode commands.
+ */
+#include "el.h"
+
+private el_action_t cv_action(EditLine *, int);
+private el_action_t cv_paste(EditLine *, int);
+
+/* cv_action():
+ * Handle vi actions.
+ */
+private el_action_t
+cv_action(EditLine *el, int c)
+{
+ char *cp, *kp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = 0;
+
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ kp = el->el_chared.c_undo.buf;
+ for (cp = el->el_line.buffer; cp < el->el_line.lastchar; cp++) {
+ *kp++ = *cp;
+ el->el_chared.c_undo.dsize++;
+ }
+
+ el->el_chared.c_undo.action = INSERT;
+ el->el_chared.c_undo.ptr = el->el_line.buffer;
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ if (c & INSERT)
+ el->el_map.current = el->el_map.key;
+
+ return (CC_REFRESH);
+ }
+ el->el_chared.c_vcmd.pos = el->el_line.cursor;
+ el->el_chared.c_vcmd.action = c;
+ return (CC_ARGHACK);
+
+#ifdef notdef
+ /*
+ * I don't think that this is needed. But we keep it for now
+ */
+ else
+ if (el_chared.c_vcmd.action == NOP) {
+ el->el_chared.c_vcmd.pos = el->el_line.cursor;
+ el->el_chared.c_vcmd.action = c;
+ return (CC_ARGHACK);
+ } else {
+ el->el_chared.c_vcmd.action = 0;
+ el->el_chared.c_vcmd.pos = 0;
+ return (CC_ERROR);
+ }
+#endif
+}
+
+
+/* cv_paste():
+ * Paste previous deletion before or after the cursor
+ */
+private el_action_t
+cv_paste(EditLine *el, int c)
+{
+ char *ptr;
+ c_undo_t *un = &el->el_chared.c_undo;
+
+#ifdef DEBUG_PASTE
+ (void) fprintf(el->el_errfile, "Paste: %x \"%s\" +%d -%d\n",
+ un->action, un->buf, un->isize, un->dsize);
+#endif
+ if (un->isize == 0)
+ return (CC_ERROR);
+
+ if (!c && el->el_line.cursor < el->el_line.lastchar)
+ el->el_line.cursor++;
+ ptr = el->el_line.cursor;
+
+ c_insert(el, (int) un->isize);
+ if (el->el_line.cursor + un->isize > el->el_line.lastchar)
+ return (CC_ERROR);
+ (void) memcpy(ptr, un->buf, un->isize);
+ return (CC_REFRESH);
+}
+
+
+/* vi_paste_next():
+ * Vi paste previous deletion to the right of the cursor
+ * [p]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_paste_next(EditLine *el, int c)
+{
+
+ return (cv_paste(el, 0));
+}
+
+
+/* vi_paste_prev():
+ * Vi paste previous deletion to the left of the cursor
+ * [P]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_paste_prev(EditLine *el, int c)
+{
+
+ return (cv_paste(el, 1));
+}
+
+
+/* vi_prev_space_word():
+ * Vi move to the previous space delimited word
+ * [B]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_prev_space_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_prev_word(el, el->el_line.cursor,
+ el->el_line.buffer,
+ el->el_state.argument,
+ cv__isword);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_prev_word():
+ * Vi move to the previous word
+ * [B]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_prev_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_prev_word(el, el->el_line.cursor,
+ el->el_line.buffer,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_next_space_word():
+ * Vi move to the next space delimited word
+ * [W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_next_space_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
+ el->el_line.lastchar,
+ el->el_state.argument,
+ cv__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_next_word():
+ * Vi move to the next word
+ * [w]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_next_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
+ el->el_line.lastchar,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_change_case():
+ * Vi change case of character under the cursor and advance one character
+ * [~]
+ */
+protected el_action_t
+vi_change_case(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ c = *el->el_line.cursor;
+ if (isupper(c))
+ *el->el_line.cursor++ = tolower(c);
+ else if (islower(c))
+ *el->el_line.cursor++ = toupper(c);
+ else
+ el->el_line.cursor++;
+ re_fastaddc(el);
+ return (CC_NORM);
+ }
+ return (CC_ERROR);
+}
+
+
+/* vi_change_meta():
+ * Vi change prefix command
+ * [c]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_change_meta(EditLine *el, int c)
+{
+
+ /*
+ * Delete with insert == change: first we delete and then we leave in
+ * insert mode.
+ */
+ return (cv_action(el, DELETE | INSERT));
+}
+
+
+/* vi_insert_at_bol():
+ * Vi enter insert mode at the beginning of line
+ * [I]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_insert_at_bol(EditLine *el, int c)
+{
+
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_chared.c_vcmd.ins = el->el_line.cursor;
+
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.action = DELETE;
+
+ el->el_map.current = el->el_map.key;
+ return (CC_CURSOR);
+}
+
+
+/* vi_replace_char():
+ * Vi replace character under the cursor with the next character typed
+ * [r]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_replace_char(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+ el->el_state.inputmode = MODE_REPLACE_1;
+ el->el_chared.c_undo.action = CHANGE;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ return (CC_NORM);
+}
+
+
+/* vi_replace_mode():
+ * Vi enter replace mode
+ * [R]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_replace_mode(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+ el->el_state.inputmode = MODE_REPLACE;
+ el->el_chared.c_undo.action = CHANGE;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ return (CC_NORM);
+}
+
+
+/* vi_substitute_char():
+ * Vi replace character under the cursor and enter insert mode
+ * [r]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_substitute_char(EditLine *el, int c)
+{
+
+ c_delafter(el, el->el_state.argument);
+ el->el_map.current = el->el_map.key;
+ return (CC_REFRESH);
+}
+
+
+/* vi_substitute_line():
+ * Vi substitute entire line
+ * [S]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_substitute_line(EditLine *el, int c)
+{
+
+ (void) em_kill_line(el, 0);
+ el->el_map.current = el->el_map.key;
+ return (CC_REFRESH);
+}
+
+
+/* vi_change_to_eol():
+ * Vi change to end of line
+ * [C]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_change_to_eol(EditLine *el, int c)
+{
+
+ (void) ed_kill_line(el, 0);
+ el->el_map.current = el->el_map.key;
+ return (CC_REFRESH);
+}
+
+
+/* vi_insert():
+ * Vi enter insert mode
+ * [i]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_insert(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+
+ el->el_chared.c_vcmd.ins = el->el_line.cursor;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.action = DELETE;
+
+ return (CC_NORM);
+}
+
+
+/* vi_add():
+ * Vi enter insert mode after the cursor
+ * [a]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_add(EditLine *el, int c)
+{
+ int ret;
+
+ el->el_map.current = el->el_map.key;
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ el->el_line.cursor++;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ ret = CC_CURSOR;
+ } else
+ ret = CC_NORM;
+
+ el->el_chared.c_vcmd.ins = el->el_line.cursor;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.action = DELETE;
+
+ return (ret);
+}
+
+
+/* vi_add_at_eol():
+ * Vi enter insert mode at end of line
+ * [A]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_add_at_eol(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+ el->el_line.cursor = el->el_line.lastchar;
+
+ /* Mark where insertion begins */
+ el->el_chared.c_vcmd.ins = el->el_line.lastchar;
+ el->el_chared.c_undo.ptr = el->el_line.lastchar;
+ el->el_chared.c_undo.action = DELETE;
+ return (CC_CURSOR);
+}
+
+
+/* vi_delete_meta():
+ * Vi delete prefix command
+ * [d]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_delete_meta(EditLine *el, int c)
+{
+
+ return (cv_action(el, DELETE));
+}
+
+
+/* vi_end_word():
+ * Vi move to the end of the current space delimited word
+ * [E]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_end_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv__endword(el->el_line.cursor,
+ el->el_line.lastchar, el->el_state.argument);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_to_end_word():
+ * Vi move to the end of the current word
+ * [e]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_to_end_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv__endword(el->el_line.cursor,
+ el->el_line.lastchar, el->el_state.argument);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_undo():
+ * Vi undo last change
+ * [u]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_undo(EditLine *el, int c)
+{
+ char *cp, *kp;
+ char temp;
+ int i, size;
+ c_undo_t *un = &el->el_chared.c_undo;
+
+#ifdef DEBUG_UNDO
+ (void) fprintf(el->el_errfile, "Undo: %x \"%s\" +%d -%d\n",
+ un->action, un->buf, un->isize, un->dsize);
+#endif
+ switch (un->action) {
+ case DELETE:
+ if (un->dsize == 0)
+ return (CC_NORM);
+
+ (void) memcpy(un->buf, un->ptr, un->dsize);
+ for (cp = un->ptr; cp <= el->el_line.lastchar; cp++)
+ *cp = cp[un->dsize];
+
+ el->el_line.lastchar -= un->dsize;
+ el->el_line.cursor = un->ptr;
+
+ un->action = INSERT;
+ un->isize = un->dsize;
+ un->dsize = 0;
+ break;
+
+ case DELETE | INSERT:
+ size = un->isize - un->dsize;
+ if (size > 0)
+ i = un->dsize;
+ else
+ i = un->isize;
+ cp = un->ptr;
+ kp = un->buf;
+ while (i-- > 0) {
+ temp = *kp;
+ *kp++ = *cp;
+ *cp++ = temp;
+ }
+ if (size > 0) {
+ el->el_line.cursor = cp;
+ c_insert(el, size);
+ while (size-- > 0 && cp < el->el_line.lastchar) {
+ temp = *kp;
+ *kp++ = *cp;
+ *cp++ = temp;
+ }
+ } else if (size < 0) {
+ size = -size;
+ for (; cp <= el->el_line.lastchar; cp++) {
+ *kp++ = *cp;
+ *cp = cp[size];
+ }
+ el->el_line.lastchar -= size;
+ }
+ el->el_line.cursor = un->ptr;
+ i = un->dsize;
+ un->dsize = un->isize;
+ un->isize = i;
+ break;
+
+ case INSERT:
+ if (un->isize == 0)
+ return (CC_NORM);
+
+ el->el_line.cursor = un->ptr;
+ c_insert(el, (int) un->isize);
+ (void) memcpy(un->ptr, un->buf, un->isize);
+ un->action = DELETE;
+ un->dsize = un->isize;
+ un->isize = 0;
+ break;
+
+ case CHANGE:
+ if (un->isize == 0)
+ return (CC_NORM);
+
+ el->el_line.cursor = un->ptr;
+ size = (int) (el->el_line.cursor - el->el_line.lastchar);
+ if (size < un->isize)
+ size = un->isize;
+ cp = un->ptr;
+ kp = un->buf;
+ for (i = 0; i < size; i++) {
+ temp = *kp;
+ *kp++ = *cp;
+ *cp++ = temp;
+ }
+ un->dsize = 0;
+ break;
+
+ default:
+ return (CC_ERROR);
+ }
+
+ return (CC_REFRESH);
+}
+
+
+/* vi_command_mode():
+ * Vi enter command mode (use alternative key bindings)
+ * [<ESC>]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_command_mode(EditLine *el, int c)
+{
+ int size;
+
+ /* [Esc] cancels pending action */
+ el->el_chared.c_vcmd.ins = 0;
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = 0;
+
+ el->el_state.doingarg = 0;
+ size = el->el_chared.c_undo.ptr - el->el_line.cursor;
+ if (size < 0)
+ size = -size;
+ if (el->el_chared.c_undo.action == (INSERT | DELETE) ||
+ el->el_chared.c_undo.action == DELETE)
+ el->el_chared.c_undo.dsize = size;
+ else
+ el->el_chared.c_undo.isize = size;
+
+ el->el_state.inputmode = MODE_INSERT;
+ el->el_map.current = el->el_map.alt;
+#ifdef VI_MOVE
+ if (el->el_line.cursor > el->el_line.buffer)
+ el->el_line.cursor--;
+#endif
+ return (CC_CURSOR);
+}
+
+
+/* vi_zero():
+ * Vi move to the beginning of line
+ * [0]
+ */
+protected el_action_t
+vi_zero(EditLine *el, int c)
+{
+
+ if (el->el_state.doingarg) {
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.argument =
+ (el->el_state.argument * 10) + (c - '0');
+ return (CC_ARGHACK);
+ } else {
+ el->el_line.cursor = el->el_line.buffer;
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+ }
+}
+
+
+/* vi_delete_prev_char():
+ * Vi move to previous character (backspace)
+ * [^H]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_delete_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_chared.c_vcmd.ins == 0)
+ return (CC_ERROR);
+
+ if (el->el_chared.c_vcmd.ins >
+ el->el_line.cursor - el->el_state.argument)
+ return (CC_ERROR);
+
+ c_delbefore(el, el->el_state.argument);
+ el->el_line.cursor -= el->el_state.argument;
+
+ return (CC_REFRESH);
+}
+
+
+/* vi_list_or_eof():
+ * Vi list choices for completion or indicate end of file if empty line
+ * [^D]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_list_or_eof(EditLine *el, int c)
+{
+
+#ifdef notyet
+ if (el->el_line.cursor == el->el_line.lastchar &&
+ el->el_line.cursor == el->el_line.buffer) {
+#endif
+ term_overwrite(el, STReof, 4); /* then do a EOF */
+ term__flush();
+ return (CC_EOF);
+#ifdef notyet
+ } else {
+ re_goto_bottom(el);
+ *el->el_line.lastchar = '\0'; /* just in case */
+ return (CC_LIST_CHOICES);
+ }
+#endif
+}
+
+
+/* vi_kill_line_prev():
+ * Vi cut from beginning of line to cursor
+ * [^U]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_kill_line_prev(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ cp = el->el_line.buffer;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.cursor)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ c_delbefore(el, el->el_line.cursor - el->el_line.buffer);
+ el->el_line.cursor = el->el_line.buffer; /* zap! */
+ return (CC_REFRESH);
+}
+
+
+/* vi_search_prev():
+ * Vi search history previous
+ * [?]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_search_prev(EditLine *el, int c)
+{
+
+ return (cv_search(el, ED_SEARCH_PREV_HISTORY));
+}
+
+
+/* vi_search_next():
+ * Vi search history next
+ * [/]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_search_next(EditLine *el, int c)
+{
+
+ return (cv_search(el, ED_SEARCH_NEXT_HISTORY));
+}
+
+
+/* vi_repeat_search_next():
+ * Vi repeat current search in the same search direction
+ * [n]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_repeat_search_next(EditLine *el, int c)
+{
+
+ if (el->el_search.patlen == 0)
+ return (CC_ERROR);
+ else
+ return (cv_repeat_srch(el, el->el_search.patdir));
+}
+
+
+/* vi_repeat_search_prev():
+ * Vi repeat current search in the opposite search direction
+ * [N]
+ */
+/*ARGSUSED*/
+protected el_action_t
+vi_repeat_search_prev(EditLine *el, int c)
+{
+
+ if (el->el_search.patlen == 0)
+ return (CC_ERROR);
+ else
+ return (cv_repeat_srch(el,
+ el->el_search.patdir == ED_SEARCH_PREV_HISTORY ?
+ ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY));
+}
+
+
+/* vi_next_char():
+ * Vi move to the character specified next
+ * [f]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_next_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ el->el_search.chadir = CHAR_FWD;
+ el->el_search.chacha = ch;
+
+ return (cv_csearch_fwd(el, ch, el->el_state.argument, 0));
+
+}
+
+
+/* vi_prev_char():
+ * Vi move to the character specified previous
+ * [F]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_prev_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ el->el_search.chadir = CHAR_BACK;
+ el->el_search.chacha = ch;
+
+ return (cv_csearch_back(el, ch, el->el_state.argument, 0));
+}
+
+
+/* vi_to_next_char():
+ * Vi move up to the character specified next
+ * [t]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_to_next_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ return (cv_csearch_fwd(el, ch, el->el_state.argument, 1));
+
+}
+
+
+/* vi_to_prev_char():
+ * Vi move up to the character specified previous
+ * [T]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_to_prev_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ return (cv_csearch_back(el, ch, el->el_state.argument, 1));
+}
+
+
+/* vi_repeat_next_char():
+ * Vi repeat current character search in the same search direction
+ * [;]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_repeat_next_char(EditLine *el, int c)
+{
+
+ if (el->el_search.chacha == 0)
+ return (CC_ERROR);
+
+ return (el->el_search.chadir == CHAR_FWD
+ ? cv_csearch_fwd(el, el->el_search.chacha,
+ el->el_state.argument, 0)
+ : cv_csearch_back(el, el->el_search.chacha,
+ el->el_state.argument, 0));
+}
+
+
+/* vi_repeat_prev_char():
+ * Vi repeat current character search in the opposite search direction
+ * [,]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_repeat_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_search.chacha == 0)
+ return (CC_ERROR);
+
+ return el->el_search.chadir == CHAR_BACK ?
+ cv_csearch_fwd(el, el->el_search.chacha, el->el_state.argument, 0) :
+ cv_csearch_back(el, el->el_search.chacha, el->el_state.argument, 0);
+}
diff --git a/trunk/main/enum.c b/trunk/main/enum.c
new file mode 100644
index 000000000..b54580263
--- /dev/null
+++ b/trunk/main/enum.c
@@ -0,0 +1,655 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Funding provided by nic.at
+ *
+ * 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 ENUM Support for Asterisk
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg Funding provided by nic.at
+ *
+ * \par Enum standards
+ *
+ * - NAPTR records: http://ietf.nri.reston.va.us/rfc/rfc2915.txt
+ * - DNS SRV records: http://www.ietf.org/rfc/rfc2782.txt
+ * - ENUM http://www.ietf.org/rfc/rfc3761.txt
+ * - ENUM for H.323: http://www.ietf.org/rfc/rfc3762.txt
+ * - ENUM SIP: http://www.ietf.org/rfc/rfc3764.txt
+ * - IANA ENUM Services: http://www.iana.org/assignments/enum-services
+ *
+ * \par Possible improvement
+ * \todo Implement a caching mechanism for multile enum lookups
+ * - See http://bugs.digium.com/view.php?id=6739
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef __APPLE__
+#if __APPLE_CC__ >= 1495
+#include <arpa/nameser_compat.h>
+#endif
+#endif
+#include <resolv.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "asterisk/enum.h"
+#include "asterisk/dns.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/manager.h"
+
+#ifdef __APPLE__
+#undef T_NAPTR
+#define T_NAPTR 35
+#endif
+
+#ifdef __APPLE__
+#undef T_TXT
+#define T_TXT 16
+#endif
+
+#define TOPLEV "e164.arpa." /*!< The IETF Enum standard root, managed by the ITU */
+
+/* Linked list from config file */
+static struct enum_search {
+ char toplev[512];
+ struct enum_search *next;
+} *toplevs;
+
+static int enumver;
+
+AST_MUTEX_DEFINE_STATIC(enumlock);
+
+/*! \brief Parse NAPTR record information elements */
+static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
+{
+ unsigned int len, olen;
+
+ len = olen = (unsigned int) src[0];
+ src++;
+ srclen--;
+
+ if (len > srclen) {
+ ast_log(LOG_WARNING, "ENUM parsing failed: Wanted %d characters, got %d\n", len, srclen);
+ return -1;
+ }
+
+ if (len > maxdatalen)
+ len = maxdatalen;
+ memcpy(data, src, len);
+
+ return olen + 1;
+}
+
+/*! \brief Parse DNS NAPTR record used in ENUM ---*/
+static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, char *naptrinput)
+{
+ char tech_return[80];
+ unsigned char *oanswer = answer;
+ char flags[512] = "";
+ char services[512] = "";
+ char *p;
+ char regexp[512] = "";
+ char repl[512] = "";
+ char temp[512] = "";
+ char delim;
+ char *delim2;
+ char *pattern, *subst, *d;
+ int res;
+ int regexp_len, size, backref;
+ int d_len = sizeof(temp) - 1;
+ regex_t preg;
+ regmatch_t pmatch[9];
+
+ tech_return[0] = '\0';
+
+ dst[0] = '\0';
+
+ if (len < sizeof(struct naptr)) {
+ ast_log(LOG_WARNING, "NAPTR record length too short\n");
+ return -1;
+ }
+ answer += sizeof(struct naptr);
+ len -= sizeof(struct naptr);
+ if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n");
+ return -1;
+ } else {
+ answer += res;
+ len -= res;
+ }
+ if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n");
+ return -1;
+ } else {
+ answer += res;
+ len -= res;
+ }
+ if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n");
+ return -1;
+ } else {
+ answer += res;
+ len -= res;
+ }
+
+ if ((res = dn_expand(oanswer, answer + len, answer, repl, sizeof(repl) - 1)) < 0) {
+ ast_log(LOG_WARNING, "Failed to expand hostname\n");
+ return -1;
+ }
+
+ ast_debug(3, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
+ naptrinput, flags, services, regexp, repl);
+
+ if (tolower(flags[0]) != 'u') {
+ ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n");
+ return -1;
+ }
+
+ p = strstr(services, "e2u+");
+ if (p == NULL)
+ p = strstr(services, "E2U+");
+ if (p){
+ p = p + 4;
+ if (strchr(p, ':')){
+ p = strchr(p, ':') + 1;
+ }
+ ast_copy_string(tech_return, p, sizeof(tech_return));
+ } else {
+
+ p = strstr(services, "+e2u");
+ if (p == NULL)
+ p = strstr(services, "+E2U");
+ if (p) {
+ *p = 0;
+ p = strchr(services, ':');
+ if (p)
+ *p = 0;
+ ast_copy_string(tech_return, services, sizeof(tech_return));
+ }
+ }
+
+ /* DEDBUGGING STUB
+ ast_copy_string(regexp, "!^\\+43(.*)$!\\1@bla.fasel!", sizeof(regexp) - 1);
+ */
+
+ regexp_len = strlen(regexp);
+ if (regexp_len < 7) {
+ ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
+ return -1;
+ }
+
+
+ delim = regexp[0];
+ delim2 = strchr(regexp + 1, delim);
+ if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
+ ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
+ return -1;
+ }
+
+ pattern = regexp + 1;
+ *delim2 = 0;
+ subst = delim2 + 1;
+ regexp[regexp_len-1] = 0;
+
+/*
+ * now do the regex wizardry.
+ */
+
+ if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
+ ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n",regexp);
+ return -1;
+ }
+
+ if (preg.re_nsub > 9) {
+ ast_log(LOG_WARNING, "NAPTR Regex compilation error: too many subs.\n");
+ regfree(&preg);
+ return -1;
+ }
+
+ if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
+ ast_log(LOG_WARNING, "NAPTR Regex match failed.\n");
+ regfree(&preg);
+ return -1;
+ }
+ regfree(&preg);
+
+ d = temp;
+ d_len--;
+ while (*subst && (d_len > 0)) {
+ if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
+ backref = subst[1]-'0';
+ size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
+ if (size > d_len) {
+ ast_log(LOG_WARNING, "Not enough space during NAPTR regex substitution.\n");
+ return -1;
+ }
+ memcpy(d, naptrinput + pmatch[backref].rm_so, size);
+ d += size;
+ d_len -= size;
+ subst += 2;
+ } else if (isprint(*subst)) {
+ *d++ = *subst++;
+ d_len--;
+ } else {
+ ast_log(LOG_WARNING, "Error during regex substitution.\n");
+ return -1;
+ }
+ }
+ *d = 0;
+ ast_copy_string(dst, temp, dstsize);
+ dst[dstsize - 1] = '\0';
+
+ if (*tech != '\0'){ /* check if it is requested NAPTR */
+ if (!strncasecmp(tech, "ALL", techsize)){
+ return 1; /* return or count any RR */
+ }
+ if (!strncasecmp(tech_return, tech, sizeof(tech_return)<techsize?sizeof(tech_return):techsize)){
+ ast_copy_string(tech, tech_return, techsize);
+ return 1; /* we got out RR */
+ } else { /* go to the next RR in the DNS answer */
+ return 0;
+ }
+ }
+
+ /* tech was not specified, return first parsed RR */
+ ast_copy_string(tech, tech_return, techsize);
+
+ return 1;
+}
+
+/* do not return requested value, just count RRs and return thei number in dst */
+#define ENUMLOOKUP_OPTIONS_COUNT 1
+
+
+/*! \brief Callback for TXT record lookup */
+static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct enum_context *c = (struct enum_context *)context;
+
+ if (answer == NULL) {
+ c->txt = NULL;
+ c->txtlen = 0;
+ return 0;
+ }
+
+ /* skip over first byte, as for some reason it's a vertical tab character */
+ answer += 1;
+ len -= 1;
+
+ /* answer is not null-terminated, but should be */
+ /* this is safe to do, as answer has extra bytes on the end we can
+ * safely overwrite with a null */
+ answer[len] = '\0';
+ /* now increment len so that len includes the null, so that we can
+ * compare apples to apples */
+ len +=1;
+
+ /* finally, copy the answer into c->txt */
+ ast_copy_string(c->txt, (const char *) answer, len < c->txtlen ? len : (c->txtlen));
+
+ /* just to be safe, let's make sure c->txt is null terminated */
+ c->txt[(c->txtlen)-1] = '\0';
+
+ return 1;
+}
+
+/*! \brief Callback from ENUM lookup function */
+static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct enum_context *c = context;
+ void *p = NULL;
+ int res;
+
+ res = parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
+ return -1;
+ } else if (res > 0 && !ast_strlen_zero(c->dst)){ /* ok, we got needed NAPTR */
+ if (c->options & ENUMLOOKUP_OPTIONS_COUNT){ /* counting RRs */
+ c->position++;
+ snprintf(c->dst, c->dstlen, "%d", c->position);
+ } else {
+ if ((p = ast_realloc(c->naptr_rrs, sizeof(*c->naptr_rrs) * (c->naptr_rrs_count + 1)))) {
+ c->naptr_rrs = p;
+ memcpy(&c->naptr_rrs[c->naptr_rrs_count].naptr, answer, sizeof(c->naptr_rrs->naptr));
+ c->naptr_rrs[c->naptr_rrs_count].result = ast_strdup(c->dst);
+ c->naptr_rrs[c->naptr_rrs_count].tech = ast_strdup(c->tech);
+ c->naptr_rrs[c->naptr_rrs_count].sort_pos = c->naptr_rrs_count;
+ c->naptr_rrs_count++;
+ }
+ c->dst[0] = 0;
+ }
+ return 0;
+ }
+
+ if (c->options & ENUMLOOKUP_OPTIONS_COUNT) { /* counting RRs */
+ snprintf(c->dst, c->dstlen, "%d", c->position);
+ }
+
+ return 0;
+}
+
+/* ENUM lookup */
+int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options, unsigned int record, struct enum_context **argcontext)
+{
+ struct enum_context *context;
+ char tmp[259 + 512];
+ char naptrinput[512];
+ int pos = strlen(number) - 1;
+ int newpos = 0;
+ int ret = -1;
+ struct enum_search *s = NULL;
+ int version = -1;
+ /* for ISN rewrite */
+ char *p1 = NULL;
+ char *p2 = NULL;
+ int k = 0;
+ int i = 0;
+ int z = 0;
+
+ if (!(context = ast_calloc(1, sizeof(*context))))
+ return -1;
+
+ ast_copy_string(naptrinput, number[0] == 'n' ? number+1 : number, sizeof(naptrinput));
+
+ context->naptrinput = naptrinput; /* The number */
+ context->dst = dst; /* Return string */
+ context->dstlen = dstlen;
+ context->tech = tech;
+ context->techlen = techlen;
+ context->options = 0;
+ context->position = record;
+ context->naptr_rrs = NULL;
+ context->naptr_rrs_count = 0;
+
+ if (options != NULL) {
+ if (*options == 'c') {
+ context->options = ENUMLOOKUP_OPTIONS_COUNT;
+ context->position = 0;
+ }
+ }
+
+ ast_debug(1, "ast_get_enum(): n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
+ number, tech, suffix, context->options, context->position);
+
+ if (pos > 128)
+ pos = 128;
+
+ /* ISN rewrite */
+ p1 = strchr(number, '*');
+
+ if (number[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */
+ p1 = NULL;
+ k = 1; /* strip 'n' from number */
+ }
+
+ if (p1 != NULL) {
+ p2 = p1+1;
+ while (p1 > number){
+ p1--;
+ tmp[newpos++] = *p1;
+ tmp[newpos++] = '.';
+ }
+ if (*p2) {
+ while (*p2 && newpos < 128){
+ tmp[newpos++] = *p2;
+ p2++;
+ }
+ tmp[newpos++] = '.';
+ }
+
+ } else {
+ while (pos >= k) {
+ if (isdigit(number[pos])) {
+ tmp[newpos++] = number[pos];
+ tmp[newpos++] = '.';
+ }
+ pos--;
+ }
+ }
+
+ if (chan && ast_autoservice_start(chan) < 0) {
+ ast_free(context);
+ return -1;
+ }
+
+ if (suffix) {
+ ast_copy_string(tmp + newpos, suffix, sizeof(tmp) - newpos);
+ ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback);
+ ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret);
+ } else {
+ ret = -1; /* this is actually dead code since the demise of app_enum.c */
+ for (;;) {
+ ast_mutex_lock(&enumlock);
+ if (version != enumver) {
+ /* Ooh, a reload... */
+ s = toplevs;
+ version = enumver;
+ } else {
+ s = s->next;
+ }
+ ast_mutex_unlock(&enumlock);
+
+ if (!s)
+ break;
+
+ ast_copy_string(tmp + newpos, s->toplev, sizeof(tmp) - newpos);
+ ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback);
+ ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret);
+ if (ret > 0)
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno));
+ strcpy(dst, "0");
+ ret = 0;
+ }
+
+ if (context->naptr_rrs_count >= context->position && ! (context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
+ /* sort array by NAPTR order/preference */
+ for (k = 0; k < context->naptr_rrs_count; k++) {
+ for (i = 0; i < context->naptr_rrs_count; i++) {
+ /* use order first and then preference to compare */
+ if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order)
+ && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
+ || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
+ && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)){
+ z = context->naptr_rrs[k].sort_pos;
+ context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
+ context->naptr_rrs[i].sort_pos = z;
+ continue;
+ }
+ if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) {
+ if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref)
+ && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
+ || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
+ && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)){
+ z = context->naptr_rrs[k].sort_pos;
+ context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
+ context->naptr_rrs[i].sort_pos = z;
+ }
+ }
+ }
+ }
+ for (k = 0; k < context->naptr_rrs_count; k++) {
+ if (context->naptr_rrs[k].sort_pos == context->position-1) {
+ ast_copy_string(context->dst, context->naptr_rrs[k].result, dstlen);
+ ast_copy_string(context->tech, context->naptr_rrs[k].tech, techlen);
+ break;
+ }
+ }
+ } else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
+ context->dst[0] = 0;
+ }
+ if (chan)
+ ret |= ast_autoservice_stop(chan);
+
+ if (!argcontext) {
+ for (k = 0; k < context->naptr_rrs_count; k++) {
+ ast_free(context->naptr_rrs[k].result);
+ ast_free(context->naptr_rrs[k].tech);
+ }
+ ast_free(context->naptr_rrs);
+ ast_free(context);
+ } else
+ *argcontext = context;
+
+ return ret;
+}
+
+/* Get TXT record from DNS. Really has nothing to do with enum, but anyway... */
+int ast_get_txt(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char *txt, int txtlen)
+{
+ struct enum_context context;
+ char tmp[259 + 512];
+ char naptrinput[512] = "+";
+ int pos = strlen(number) - 1;
+ int newpos = 0;
+ int ret = -1;
+ struct enum_search *s = NULL;
+ int version = -1;
+
+ strncat(naptrinput, number, sizeof(naptrinput) - 2);
+
+ context.naptrinput = naptrinput;
+ context.dst = dst;
+ context.dstlen = dstlen;
+ context.tech = tech;
+ context.techlen = techlen;
+ context.txt = txt;
+ context.txtlen = txtlen;
+
+ if (pos > 128)
+ pos = 128;
+ while (pos >= 0) {
+ tmp[newpos++] = number[pos--];
+ tmp[newpos++] = '.';
+ }
+
+ if (chan && ast_autoservice_start(chan) < 0)
+ return -1;
+
+ for (;;) {
+ ast_mutex_lock(&enumlock);
+ if (version != enumver) {
+ /* Ooh, a reload... */
+ s = toplevs;
+ version = enumver;
+ } else {
+ s = s->next;
+ }
+ if (s) {
+ ast_copy_string(tmp + newpos, s->toplev, sizeof(tmp) - newpos);
+ }
+ ast_mutex_unlock(&enumlock);
+ if (!s)
+ break;
+
+ ret = ast_search_dns(&context, tmp, C_IN, T_TXT, txt_callback);
+ if (ret > 0)
+ break;
+ }
+ if (ret < 0) {
+ ast_debug(2, "No such number found in ENUM: %s (%s)\n", tmp, strerror(errno));
+ ret = 0;
+ }
+ if (chan)
+ ret |= ast_autoservice_stop(chan);
+ return ret;
+}
+
+/*! \brief Add enum tree to linked list */
+static struct enum_search *enum_newtoplev(const char *s)
+{
+ struct enum_search *tmp;
+
+ if ((tmp = ast_calloc(1, sizeof(*tmp)))) {
+ ast_copy_string(tmp->toplev, s, sizeof(tmp->toplev));
+ }
+ return tmp;
+}
+
+/*! \brief Initialize the ENUM support subsystem */
+static int private_enum_init(int reload)
+{
+ struct ast_config *cfg;
+ struct enum_search *s, *sl;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("enum.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ /* Destroy existing list */
+ ast_mutex_lock(&enumlock);
+ s = toplevs;
+ while (s) {
+ sl = s;
+ s = s->next;
+ ast_free(sl);
+ }
+ toplevs = NULL;
+ if (cfg) {
+ sl = NULL;
+ v = ast_variable_browse(cfg, "general");
+ while (v) {
+ if (!strcasecmp(v->name, "search")) {
+ s = enum_newtoplev(v->value);
+ if (s) {
+ if (sl)
+ sl->next = s;
+ else
+ toplevs = s;
+ sl = s;
+ }
+ }
+ v = v->next;
+ }
+ ast_config_destroy(cfg);
+ } else {
+ toplevs = enum_newtoplev(TOPLEV);
+ }
+ enumver++;
+ ast_mutex_unlock(&enumlock);
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Enum\r\nStatus: Enabled\r\nMessage: ENUM reload Requested\r\n");
+ return 0;
+}
+
+int ast_enum_init(void)
+{
+ return private_enum_init(0);
+}
+
+int ast_enum_reload(void)
+{
+ return private_enum_init(1);
+}
diff --git a/trunk/main/event.c b/trunk/main/event.c
new file mode 100644
index 000000000..333b628a1
--- /dev/null
+++ b/trunk/main/event.c
@@ -0,0 +1,822 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Russell Bryant <russell@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 Internal generic event system
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/event.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/unaligned.h"
+
+/* Only use one thread for now to ensure ordered delivery */
+#define NUM_EVENT_THREADS 1
+
+/*!
+ * \brief An event information element
+ *
+ * \note The format of this structure is important. Since these events may
+ * be sent directly over a network, changing this structure will break
+ * compatibility with older versions. However, at this point, this code
+ * has not made it into a release, so it is still fair game for change.
+ */
+struct ast_event_ie {
+ enum ast_event_ie_type ie_type:16;
+ /*! Total length of the IE payload */
+ uint16_t ie_payload_len;
+ unsigned char ie_payload[0];
+} __attribute__ ((packed));
+
+/*!
+ * \brief An event
+ *
+ * An ast_event consists of an event header (this structure), and zero or
+ * more information elements defined by ast_event_ie.
+ *
+ * \note The format of this structure is important. Since these events may
+ * be sent directly over a network, changing this structure will break
+ * compatibility with older versions. However, at this point, this code
+ * has not made it into a release, so it is still fair game for change.
+ */
+struct ast_event {
+ /*! Event type */
+ enum ast_event_type type:16;
+ /*! Total length of the event */
+ uint16_t event_len:16;
+ /*! The data payload of the event, made up of information elements */
+ unsigned char payload[0];
+} __attribute__ ((packed));
+
+struct ast_event_ref {
+ struct ast_event *event;
+ AST_LIST_ENTRY(ast_event_ref) entry;
+};
+
+struct ast_event_iterator {
+ uint16_t event_len;
+ const struct ast_event *event;
+ struct ast_event_ie *ie;
+};
+
+/*! \brief data shared between event dispatching threads */
+static struct {
+ ast_cond_t cond;
+ ast_mutex_t lock;
+ AST_LIST_HEAD_NOLOCK(, ast_event_ref) event_q;
+} event_thread = {
+ .lock = AST_MUTEX_INIT_VALUE,
+};
+
+struct ast_event_ie_val {
+ AST_LIST_ENTRY(ast_event_ie_val) entry;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_ie_pltype ie_pltype;
+ union {
+ uint32_t uint;
+ const char *str;
+ } payload;
+};
+
+/*! \brief Event subscription */
+struct ast_event_sub {
+ enum ast_event_type type;
+ ast_event_cb_t cb;
+ void *userdata;
+ uint32_t uniqueid;
+ AST_LIST_HEAD_NOLOCK(, ast_event_ie_val) ie_vals;
+ AST_RWLIST_ENTRY(ast_event_sub) entry;
+};
+
+static uint32_t sub_uniqueid;
+
+/*! \brief Event subscriptions
+ * The event subscribers are indexed by which event they are subscribed to */
+static AST_RWLIST_HEAD(ast_event_sub_list, ast_event_sub) ast_event_subs[AST_EVENT_TOTAL];
+
+/*! \brief Cached events
+ * The event cache is indexed on the event type. The purpose of this is
+ * for events that express some sort of state. So, when someone first
+ * needs to know this state, it can get the last known state from the cache. */
+static AST_RWLIST_HEAD(ast_event_ref_list, ast_event_ref) ast_event_cache[AST_EVENT_TOTAL];
+
+static void ast_event_ie_val_destroy(struct ast_event_ie_val *ie_val)
+{
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ast_free((void *) ie_val->payload.str);
+
+ ast_free(ie_val);
+}
+
+enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type type, ...)
+{
+ va_list ap;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_subscriber_res res = AST_EVENT_SUB_NONE;
+ struct ast_event_ie_val *ie_val, *sub_ie_val;
+ struct ast_event_sub *sub;
+ AST_LIST_HEAD_NOLOCK_STATIC(ie_vals, ast_event_ie_val);
+
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
+ return res;
+ }
+
+ va_start(ap, type);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ struct ast_event_ie_val *ie_val = alloca(sizeof(*ie_val));
+ memset(ie_val, 0, sizeof(*ie_val));
+ ie_val->ie_type = ie_type;
+ ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ie_val->payload.uint = va_arg(ap, uint32_t);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ie_val->payload.str = ast_strdupa(va_arg(ap, const char *));
+ AST_LIST_INSERT_TAIL(&ie_vals, ie_val, entry);
+ }
+ va_end(ap);
+
+ AST_RWLIST_RDLOCK(&ast_event_subs[type]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[type], sub, entry) {
+ AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) {
+ AST_LIST_TRAVERSE(&sub->ie_vals, sub_ie_val, entry) {
+ if (sub_ie_val->ie_type == ie_val->ie_type)
+ break;
+ }
+ if (!sub_ie_val) {
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS)
+ break;
+ continue;
+ }
+ /* The subscriber doesn't actually care what the value is */
+ if (sub_ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS)
+ continue;
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ ie_val->payload.uint != sub_ie_val->payload.uint)
+ break;
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ strcmp(ie_val->payload.str, sub_ie_val->payload.str))
+ break;
+ }
+ if (!ie_val)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&ast_event_subs[type]);
+
+ if (sub) /* All parameters were matched */
+ return AST_EVENT_SUB_EXISTS;
+
+ AST_RWLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]);
+ if (!AST_LIST_EMPTY(&ast_event_subs[AST_EVENT_ALL]))
+ res = AST_EVENT_SUB_EXISTS;
+ AST_RWLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]);
+
+ return res;
+}
+
+/*! \brief Send AST_EVENT_SUB events to this subscriber of ... subscriber events */
+void ast_event_report_subs(const struct ast_event_sub *event_sub)
+{
+ struct ast_event *event;
+ struct ast_event_sub *sub;
+ enum ast_event_type event_type = -1;
+ struct ast_event_ie_val *ie_val;
+
+ if (event_sub->type != AST_EVENT_SUB)
+ return;
+
+ AST_LIST_TRAVERSE(&event_sub->ie_vals, ie_val, entry) {
+ if (ie_val->ie_type == AST_EVENT_IE_EVENTTYPE) {
+ event_type = ie_val->payload.uint;
+ break;
+ }
+ }
+
+ if (event_type == -1)
+ return;
+
+ AST_RWLIST_RDLOCK(&ast_event_subs[event_type]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[event_type], sub, entry) {
+ if (event_sub == sub)
+ continue;
+
+ event = ast_event_new(AST_EVENT_SUB,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END);
+
+ AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
+ switch (ie_val->ie_pltype) {
+ case AST_EVENT_IE_PLTYPE_EXISTS:
+ ast_event_append_ie_uint(&event, AST_EVENT_IE_EXISTS, ie_val->ie_type);
+ break;
+ case AST_EVENT_IE_PLTYPE_UINT:
+ ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
+ break;
+ case AST_EVENT_IE_PLTYPE_STR:
+ ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
+ break;
+ }
+ if (!event)
+ break;
+ }
+
+ if (!event)
+ continue;
+
+ event_sub->cb(event, event_sub->userdata);
+
+ ast_event_destroy(event);
+ }
+ AST_RWLIST_UNLOCK(&ast_event_subs[event_type]);
+}
+
+struct ast_event_sub *ast_event_subscribe(enum ast_event_type type, ast_event_cb_t cb,
+ void *userdata, ...)
+{
+ va_list ap;
+ enum ast_event_ie_type ie_type;
+ struct ast_event_sub *sub;
+ struct ast_event *event;
+
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
+ return NULL;
+ }
+
+ if (!(sub = ast_calloc(1, sizeof(*sub))))
+ return NULL;
+
+ va_start(ap, userdata);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ struct ast_event_ie_val *ie_val;
+ if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
+ continue;
+ ie_val->ie_type = ie_type;
+ ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ie_val->payload.uint = va_arg(ap, uint32_t);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR) {
+ if (!(ie_val->payload.str = ast_strdup(va_arg(ap, const char *)))) {
+ ast_free(ie_val);
+ continue;
+ }
+ }
+ AST_LIST_INSERT_TAIL(&sub->ie_vals, ie_val, entry);
+ }
+ va_end(ap);
+
+ sub->type = type;
+ sub->cb = cb;
+ sub->userdata = userdata;
+ sub->uniqueid = ast_atomic_fetchadd_int((int *) &sub_uniqueid, 1);
+
+ if (ast_event_check_subscriber(AST_EVENT_SUB,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, type,
+ AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) {
+ struct ast_event_ie_val *ie_val;
+
+ event = ast_event_new(AST_EVENT_SUB,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END);
+
+ AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
+ switch (ie_val->ie_pltype) {
+ case AST_EVENT_IE_PLTYPE_EXISTS:
+ ast_event_append_ie_uint(&event, AST_EVENT_IE_EXISTS, ie_val->ie_type);
+ break;
+ case AST_EVENT_IE_PLTYPE_UINT:
+ ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
+ break;
+ case AST_EVENT_IE_PLTYPE_STR:
+ ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
+ break;
+ }
+ if (!event)
+ break;
+ }
+
+ if (event)
+ ast_event_queue(event);
+ }
+
+ AST_RWLIST_WRLOCK(&ast_event_subs[type]);
+ AST_RWLIST_INSERT_TAIL(&ast_event_subs[type], sub, entry);
+ AST_RWLIST_UNLOCK(&ast_event_subs[type]);
+
+ return sub;
+}
+
+static void ast_event_sub_destroy(struct ast_event_sub *sub)
+{
+ struct ast_event_ie_val *ie_val;
+
+ while ((ie_val = AST_LIST_REMOVE_HEAD(&sub->ie_vals, entry)))
+ ast_event_ie_val_destroy(ie_val);
+
+ ast_free(sub);
+}
+
+void ast_event_unsubscribe(struct ast_event_sub *sub)
+{
+ struct ast_event *event;
+
+ AST_RWLIST_WRLOCK(&ast_event_subs[sub->type]);
+ AST_LIST_REMOVE(&ast_event_subs[sub->type], sub, entry);
+ AST_RWLIST_UNLOCK(&ast_event_subs[sub->type]);
+
+ if (ast_event_check_subscriber(AST_EVENT_UNSUB,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) {
+
+ event = ast_event_new(AST_EVENT_UNSUB,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END);
+
+ if (event)
+ ast_event_queue(event);
+ }
+
+ ast_event_sub_destroy(sub);
+}
+
+void ast_event_iterator_init(struct ast_event_iterator *iterator, const struct ast_event *event)
+{
+ iterator->event_len = ntohs(event->event_len);
+ iterator->event = event;
+ iterator->ie = (struct ast_event_ie *) ( ((char *) event) + sizeof(*event) );
+ return;
+}
+
+int ast_event_iterator_next(struct ast_event_iterator *iterator)
+{
+ iterator->ie = (struct ast_event_ie *) ( ((char *) iterator->ie) + sizeof(*iterator->ie) + ntohs(iterator->ie->ie_payload_len));
+ return ((iterator->event_len < (((char *) iterator->ie) - ((char *) iterator->event))) ? -1 : 0);
+}
+
+enum ast_event_ie_type ast_event_iterator_get_ie_type(struct ast_event_iterator *iterator)
+{
+ return iterator->ie->ie_type;
+}
+
+uint32_t ast_event_iterator_get_ie_uint(struct ast_event_iterator *iterator)
+{
+ return ntohl(get_unaligned_uint32(iterator->ie->ie_payload));
+}
+
+const char *ast_event_iterator_get_ie_str(struct ast_event_iterator *iterator)
+{
+ return (const char*)iterator->ie->ie_payload;
+}
+
+void *ast_event_iterator_get_ie_raw(struct ast_event_iterator *iterator)
+{
+ return iterator->ie->ie_payload;
+}
+
+enum ast_event_type ast_event_get_type(const struct ast_event *event)
+{
+ return ntohs(event->type);
+}
+
+uint32_t ast_event_get_ie_uint(const struct ast_event *event, enum ast_event_ie_type ie_type)
+{
+ const uint32_t *ie_val;
+
+ ie_val = ast_event_get_ie_raw(event, ie_type);
+
+ return ie_val ? ntohl(get_unaligned_uint32(ie_val)) : 0;
+}
+
+const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type)
+{
+ return ast_event_get_ie_raw(event, ie_type);
+}
+
+const void *ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type)
+{
+ struct ast_event_iterator iterator;
+ int res = 0;
+
+ ie_type = ntohs(ie_type);
+
+ for (ast_event_iterator_init(&iterator, event); !res; res = ast_event_iterator_next(&iterator)) {
+ if (ast_event_iterator_get_ie_type(&iterator) == ie_type)
+ return ast_event_iterator_get_ie_raw(&iterator);
+ }
+
+ return NULL;
+}
+
+int ast_event_append_ie_str(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const char *str)
+{
+ return ast_event_append_ie_raw(event, ie_type, str, strlen(str) + 1);
+}
+
+int ast_event_append_ie_uint(struct ast_event **event, enum ast_event_ie_type ie_type,
+ uint32_t data)
+{
+ data = htonl(data);
+ return ast_event_append_ie_raw(event, ie_type, &data, sizeof(data));
+}
+
+int ast_event_append_ie_raw(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const void *data, size_t data_len)
+{
+ struct ast_event_ie *ie;
+ unsigned int extra_len;
+ uint16_t event_len;
+
+ event_len = ntohs((*event)->event_len);
+ extra_len = sizeof(*ie) + data_len;
+
+ if (!(*event = ast_realloc(*event, event_len + extra_len)))
+ return -1;
+
+ ie = (struct ast_event_ie *) ( ((char *) *event) + event_len );
+ ie->ie_type = htons(ie_type);
+ ie->ie_payload_len = htons(data_len);
+ memcpy(ie->ie_payload, data, data_len);
+
+ (*event)->event_len = htons(event_len + extra_len);
+
+ return 0;
+}
+
+struct ast_event *ast_event_new(enum ast_event_type type, ...)
+{
+ va_list ap;
+ struct ast_event *event;
+ enum ast_event_type ie_type;
+ struct ast_event_ie_val *ie_val;
+ AST_LIST_HEAD_NOLOCK_STATIC(ie_vals, ast_event_ie_val);
+
+ /* Invalid type */
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_WARNING, "Someone tried to create an event of invalid "
+ "type '%d'!\n", type);
+ return NULL;
+ }
+
+ va_start(ap, type);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ struct ast_event_ie_val *ie_val = alloca(sizeof(*ie_val));
+ memset(ie_val, 0, sizeof(*ie_val));
+ ie_val->ie_type = ie_type;
+ ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ie_val->payload.uint = va_arg(ap, uint32_t);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ie_val->payload.str = ast_strdupa(va_arg(ap, const char *));
+ AST_LIST_INSERT_TAIL(&ie_vals, ie_val, entry);
+ }
+ va_end(ap);
+
+ if (!(event = ast_calloc(1, sizeof(*event))))
+ return NULL;
+
+ event->type = htons(type);
+ event->event_len = htons(sizeof(*event));
+
+ AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) {
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
+
+ if (!event)
+ break;
+ }
+
+ return event;
+}
+
+void ast_event_destroy(struct ast_event *event)
+{
+ ast_free(event);
+}
+
+static void ast_event_ref_destroy(struct ast_event_ref *event_ref)
+{
+ ast_event_destroy(event_ref->event);
+ ast_free(event_ref);
+}
+
+static struct ast_event *ast_event_dup(const struct ast_event *event)
+{
+ struct ast_event *dup_event;
+ uint16_t event_len;
+
+ event_len = ntohs(event->event_len);
+
+ if (!(dup_event = ast_calloc(1, event_len)))
+ return NULL;
+
+ memcpy(dup_event, event, event_len);
+
+ return dup_event;
+}
+
+struct ast_event *ast_event_get_cached(enum ast_event_type type, ...)
+{
+ va_list ap;
+ enum ast_event_ie_type ie_type;
+ struct ast_event *dup_event = NULL;
+ struct ast_event_ref *event_ref;
+ struct cache_arg {
+ AST_LIST_ENTRY(cache_arg) entry;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_ie_pltype ie_pltype;
+ union {
+ uint32_t uint;
+ const char *str;
+ } payload;
+ } *cache_arg;
+ AST_LIST_HEAD_NOLOCK_STATIC(cache_args, cache_arg);
+
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
+ return NULL;
+ }
+
+ va_start(ap, type);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ cache_arg = alloca(sizeof(*cache_arg));
+ memset(cache_arg, 0, sizeof(*cache_arg));
+ cache_arg->ie_type = ie_type;
+ cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ cache_arg->payload.uint = va_arg(ap, uint32_t);
+ else if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ cache_arg->payload.str = ast_strdupa(va_arg(ap, const char *));
+ AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
+ }
+ va_end(ap);
+
+ if (AST_LIST_EMPTY(&cache_args)) {
+ ast_log(LOG_ERROR, "Events can not be retrieved from the cache without "
+ "specifying at least one IE type!\n");
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&ast_event_cache[type]);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[type], event_ref, entry) {
+ AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
+ if ( ! ( (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ (cache_arg->payload.uint ==
+ ast_event_get_ie_uint(event_ref->event, cache_arg->ie_type))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ (!strcmp(cache_arg->payload.str,
+ ast_event_get_ie_str(event_ref->event, cache_arg->ie_type)))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
+ ast_event_get_ie_raw(event_ref->event, cache_arg->ie_type)) ) )
+ {
+ break;
+ }
+ }
+ if (!cache_arg) {
+ /* All parameters were matched on this cache entry, so return it */
+ dup_event = ast_event_dup(event_ref->event);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+ AST_RWLIST_UNLOCK(&ast_event_cache[type]);
+
+ return dup_event;
+}
+
+/*! \brief Duplicate an event and add it to the cache
+ * \note This assumes this index in to the cache is locked */
+static int ast_event_dup_and_cache(const struct ast_event *event)
+{
+ struct ast_event *dup_event;
+ struct ast_event_ref *event_ref;
+
+ if (!(dup_event = ast_event_dup(event)))
+ return -1;
+ if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
+ return -1;
+
+ event_ref->event = dup_event;
+
+ AST_LIST_INSERT_TAIL(&ast_event_cache[ntohs(event->type)], event_ref, entry);
+
+ return 0;
+}
+
+int ast_event_queue_and_cache(struct ast_event *event, ...)
+{
+ va_list ap;
+ enum ast_event_type ie_type;
+ uint16_t host_event_type;
+ struct ast_event_ref *event_ref;
+ int res;
+ struct cache_arg {
+ AST_LIST_ENTRY(cache_arg) entry;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_ie_pltype ie_pltype;
+ } *cache_arg;
+ AST_LIST_HEAD_NOLOCK_STATIC(cache_args, cache_arg);
+
+ host_event_type = ntohs(event->type);
+
+ /* Invalid type */
+ if (host_event_type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_WARNING, "Someone tried to queue an event of invalid "
+ "type '%d'!\n", host_event_type);
+ return -1;
+ }
+
+ va_start(ap, event);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ cache_arg = alloca(sizeof(*cache_arg));
+ memset(cache_arg, 0, sizeof(*cache_arg));
+ cache_arg->ie_type = ie_type;
+ cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
+ }
+ va_end(ap);
+
+ if (AST_LIST_EMPTY(&cache_args)) {
+ ast_log(LOG_ERROR, "Events can not be cached without specifying at "
+ "least one IE type!\n");
+ return ast_event_queue(event);
+ }
+
+ AST_RWLIST_WRLOCK(&ast_event_cache[host_event_type]);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[host_event_type], event_ref, entry) {
+ AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
+ if ( ! ( (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ (ast_event_get_ie_uint(event, cache_arg->ie_type) ==
+ ast_event_get_ie_uint(event_ref->event, cache_arg->ie_type))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ (!strcmp(ast_event_get_ie_str(event, cache_arg->ie_type),
+ ast_event_get_ie_str(event_ref->event, cache_arg->ie_type)))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
+ ast_event_get_ie_raw(event_ref->event, cache_arg->ie_type)) ) )
+ {
+ break;
+ }
+ }
+ if (!cache_arg) {
+ /* All parameters were matched on this cache entry, so remove it */
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_event_ref_destroy(event_ref);
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ res = ast_event_dup_and_cache(event);
+ AST_RWLIST_UNLOCK(&ast_event_cache[host_event_type]);
+
+ return (ast_event_queue(event) || res) ? -1 : 0;
+}
+
+int ast_event_queue(struct ast_event *event)
+{
+ struct ast_event_ref *event_ref;
+ uint16_t host_event_type;
+
+ host_event_type = ntohs(event->type);
+
+ /* Invalid type */
+ if (host_event_type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_WARNING, "Someone tried to queue an event of invalid "
+ "type '%d'!\n", host_event_type);
+ return -1;
+ }
+
+ /* If nobody has subscribed to this event type, throw it away now */
+ if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END)
+ == AST_EVENT_SUB_NONE) {
+ ast_event_destroy(event);
+ return 0;
+ }
+
+ if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
+ return -1;
+
+ event_ref->event = event;
+
+ ast_mutex_lock(&event_thread.lock);
+ AST_LIST_INSERT_TAIL(&event_thread.event_q, event_ref, entry);
+ ast_cond_signal(&event_thread.cond);
+ ast_mutex_unlock(&event_thread.lock);
+
+ return 0;
+}
+
+static void *ast_event_dispatcher(void *unused)
+{
+ for (;;) {
+ struct ast_event_ref *event_ref;
+ struct ast_event_sub *sub;
+ uint16_t host_event_type;
+
+ ast_mutex_lock(&event_thread.lock);
+ while (!(event_ref = AST_LIST_REMOVE_HEAD(&event_thread.event_q, entry)))
+ ast_cond_wait(&event_thread.cond, &event_thread.lock);
+ ast_mutex_unlock(&event_thread.lock);
+
+ host_event_type = ntohs(event_ref->event->type);
+
+ /* Subscribers to this specific event first */
+ AST_RWLIST_RDLOCK(&ast_event_subs[host_event_type]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[host_event_type], sub, entry) {
+ struct ast_event_ie_val *ie_val;
+ AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
+ ast_event_get_ie_raw(event_ref->event, ie_val->ie_type)) {
+ continue;
+ } else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ ast_event_get_ie_uint(event_ref->event, ie_val->ie_type)
+ == ie_val->payload.uint) {
+ continue;
+ } else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ !strcmp(ast_event_get_ie_str(event_ref->event, ie_val->ie_type),
+ ie_val->payload.str)) {
+ continue;
+ }
+ break;
+ }
+ if (ie_val)
+ continue;
+ sub->cb(event_ref->event, sub->userdata);
+ }
+ AST_RWLIST_UNLOCK(&ast_event_subs[host_event_type]);
+
+ /* Now to subscribers to all event types */
+ AST_RWLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry)
+ sub->cb(event_ref->event, sub->userdata);
+ AST_RWLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]);
+
+ ast_event_ref_destroy(event_ref);
+ }
+
+ return NULL;
+}
+
+void ast_event_init(void)
+{
+ int i;
+
+ for (i = 0; i < AST_EVENT_TOTAL; i++)
+ AST_RWLIST_HEAD_INIT(&ast_event_subs[i]);
+
+ for (i = 0; i < AST_EVENT_TOTAL; i++)
+ AST_RWLIST_HEAD_INIT(&ast_event_cache[i]);
+
+ ast_cond_init(&event_thread.cond, NULL);
+
+ for (i = 0; i < NUM_EVENT_THREADS; i++) {
+ pthread_t dont_care;
+ ast_pthread_create_background(&dont_care, NULL, ast_event_dispatcher, NULL);
+ }
+}
diff --git a/trunk/main/file.c b/trunk/main/file.c
new file mode 100644
index 000000000..fe51c2df7
--- /dev/null
+++ b/trunk/main/file.c
@@ -0,0 +1,1245 @@
+/*
+ * 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 Generic File Format Support.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "asterisk/_private.h" /* declare ast_file_init() */
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/mod_format.h"
+#include "asterisk/cli.h"
+#include "asterisk/channel.h"
+#include "asterisk/sched.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+
+/*
+ * The following variable controls the layout of localized sound files.
+ * If 0, use the historical layout with prefix just before the filename
+ * (i.e. digits/en/1.gsm , digits/it/1.gsm or default to digits/1.gsm),
+ * if 1 put the prefix at the beginning of the filename
+ * (i.e. en/digits/1.gsm, it/digits/1.gsm or default to digits/1.gsm).
+ * The latter permits a language to be entirely in one directory.
+ */
+int ast_language_is_prefix = 1;
+
+static AST_RWLIST_HEAD_STATIC(formats, ast_format);
+
+int __ast_format_register(const struct ast_format *f, struct ast_module *mod)
+{
+ struct ast_format *tmp;
+
+ AST_RWLIST_WRLOCK(&formats);
+ AST_RWLIST_TRAVERSE(&formats, tmp, list) {
+ if (!strcasecmp(f->name, tmp->name)) {
+ AST_RWLIST_UNLOCK(&formats);
+ ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", f->name);
+ return -1;
+ }
+ }
+ if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
+ AST_RWLIST_UNLOCK(&formats);
+ return -1;
+ }
+ *tmp = *f;
+ tmp->module = mod;
+ if (tmp->buf_size) {
+ /*
+ * Align buf_size properly, rounding up to the machine-specific
+ * alignment for pointers.
+ */
+ struct _test_align { void *a, *b; } p;
+ int align = (char *)&p.b - (char *)&p.a;
+ tmp->buf_size = ((f->buf_size + align - 1)/align)*align;
+ }
+
+ memset(&tmp->list, 0, sizeof(tmp->list));
+
+ AST_RWLIST_INSERT_HEAD(&formats, tmp, list);
+ AST_RWLIST_UNLOCK(&formats);
+ ast_verb(2, "Registered file format %s, extension(s) %s\n", f->name, f->exts);
+
+ return 0;
+}
+
+int ast_format_unregister(const char *name)
+{
+ struct ast_format *tmp;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&formats);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&formats, tmp, list) {
+ if (!strcasecmp(name, tmp->name)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(tmp);
+ res = 0;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&formats);
+
+ if (!res)
+ ast_verb(2, "Unregistered format %s\n", name);
+ else
+ ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
+
+ return res;
+}
+
+int ast_stopstream(struct ast_channel *tmp)
+{
+ ast_channel_lock(tmp);
+
+ /* Stop a running stream if there is one */
+ if (tmp->stream) {
+ ast_closestream(tmp->stream);
+ tmp->stream = NULL;
+ if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
+ ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
+ }
+ /* Stop the video stream too */
+ if (tmp->vstream != NULL) {
+ ast_closestream(tmp->vstream);
+ tmp->vstream = NULL;
+ }
+
+ ast_channel_unlock(tmp);
+
+ return 0;
+}
+
+int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res = -1;
+ int alt = 0;
+ if (f->frametype == AST_FRAME_VIDEO) {
+ if (fs->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ /* This is the audio portion. Call the video one... */
+ if (!fs->vfs && fs->filename) {
+ const char *type = ast_getformatname(f->subclass & ~0x1);
+ fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
+ ast_debug(1, "Opened video output file\n");
+ }
+ if (fs->vfs)
+ return ast_writestream(fs->vfs, f);
+ /* else ignore */
+ return 0;
+ } else {
+ /* Might / might not have mark set */
+ alt = 1;
+ }
+ } else if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
+ return -1;
+ }
+ if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
+ res = fs->fmt->write(fs, f);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Natural write failed\n");
+ else if (res > 0)
+ ast_log(LOG_WARNING, "Huh??\n");
+ } else {
+ /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
+ the one we've setup a translator for, we do the "wrong thing" XXX */
+ if (fs->trans && f->subclass != fs->lastwriteformat) {
+ ast_translator_free_path(fs->trans);
+ fs->trans = NULL;
+ }
+ if (!fs->trans)
+ fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
+ if (!fs->trans)
+ ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n",
+ fs->fmt->name, ast_getformatname(f->subclass));
+ else {
+ struct ast_frame *trf;
+ fs->lastwriteformat = f->subclass;
+ /* Get the translated frame but don't consume the original in case they're using it on another stream */
+ trf = ast_translate(fs->trans, f, 0);
+ if (trf) {
+ res = fs->fmt->write(fs, trf);
+ if (res)
+ ast_log(LOG_WARNING, "Translated frame write failed\n");
+ } else
+ res = 0;
+ }
+ }
+ return res;
+}
+
+static int copy(const char *infile, const char *outfile)
+{
+ int ifd, ofd, len;
+ char buf[4096]; /* XXX make it lerger. */
+
+ if ((ifd = open(infile, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+ return -1;
+ }
+ if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, AST_FILE_MODE)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+ close(ifd);
+ return -1;
+ }
+ while ( (len = read(ifd, buf, sizeof(buf)) ) ) {
+ int res;
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+ break;
+ }
+ /* XXX handle partial writes */
+ res = write(ofd, buf, len);
+ if (res != len) {
+ ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+ len = -1; /* error marker */
+ break;
+ }
+ }
+ close(ifd);
+ close(ofd);
+ if (len < 0) {
+ unlink(outfile);
+ return -1; /* error */
+ }
+ return 0; /* success */
+}
+
+/*!
+ * \brief construct a filename. Absolute pathnames are preserved,
+ * relative names are prefixed by the sounds/ directory.
+ * The wav49 suffix is replaced by 'WAV'.
+ * Returns a malloc'ed string to be freed by the caller.
+ */
+static char *build_filename(const char *filename, const char *ext)
+{
+ char *fn = NULL;
+
+ if (!strcmp(ext, "wav49"))
+ ext = "WAV";
+
+ if (filename[0] == '/')
+ asprintf(&fn, "%s.%s", filename, ext);
+ else
+ asprintf(&fn, "%s/sounds/%s.%s",
+ ast_config_AST_DATA_DIR, filename, ext);
+ return fn;
+}
+
+/* compare type against the list 'exts' */
+/* XXX need a better algorithm */
+static int exts_compare(const char *exts, const char *type)
+{
+ char tmp[256];
+ char *stringp = tmp, *ext;
+
+ ast_copy_string(tmp, exts, sizeof(tmp));
+ while ((ext = strsep(&stringp, "|"))) {
+ if (!strcmp(ext, type))
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct ast_filestream *get_filestream(struct ast_format *fmt, FILE *bfile)
+{
+ struct ast_filestream *s;
+
+ int l = sizeof(*s) + fmt->buf_size + fmt->desc_size; /* total allocation size */
+ if ( (s = ast_calloc(1, l)) == NULL)
+ return NULL;
+ s->fmt = fmt;
+ s->f = bfile;
+
+ if (fmt->desc_size)
+ s->_private = ((char *)(s+1)) + fmt->buf_size;
+ if (fmt->buf_size)
+ s->buf = (char *)(s+1);
+ s->fr.src = fmt->name;
+ return s;
+}
+
+/*
+ * Default implementations of open and rewrite.
+ * Only use them if you don't have expensive stuff to do.
+ */
+enum wrap_fn { WRAP_OPEN, WRAP_REWRITE };
+
+static int fn_wrapper(struct ast_filestream *s, const char *comment, enum wrap_fn mode)
+{
+ struct ast_format *f = s->fmt;
+ int ret = -1;
+
+ if (mode == WRAP_OPEN && f->open && f->open(s))
+ ast_log(LOG_WARNING, "Unable to open format %s\n", f->name);
+ else if (mode == WRAP_REWRITE && f->rewrite && f->rewrite(s, comment))
+ ast_log(LOG_WARNING, "Unable to rewrite format %s\n", f->name);
+ else {
+ /* preliminary checks succeed. update usecount */
+ ast_module_ref(f->module);
+ ret = 0;
+ }
+ return ret;
+}
+
+static int rewrite_wrapper(struct ast_filestream *s, const char *comment)
+{
+ return fn_wrapper(s, comment, WRAP_REWRITE);
+}
+
+static int open_wrapper(struct ast_filestream *s)
+{
+ return fn_wrapper(s, NULL, WRAP_OPEN);
+}
+
+enum file_action {
+ ACTION_EXISTS = 1, /* return matching format if file exists, 0 otherwise */
+ ACTION_DELETE, /* delete file, return 0 on success, -1 on error */
+ ACTION_RENAME, /* rename file. return 0 on success, -1 on error */
+ ACTION_OPEN,
+ ACTION_COPY /* copy file. return 0 on success, -1 on error */
+};
+
+/*!
+ * \brief perform various actions on a file. Second argument
+ * arg2 depends on the command:
+ * unused for EXISTS and DELETE
+ * destination file name (const char *) for COPY and RENAME
+ * struct ast_channel * for OPEN
+ * if fmt is NULL, OPEN will return the first matching entry,
+ * whereas other functions will run on all matching entries.
+ */
+static int ast_filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action)
+{
+ struct ast_format *f;
+ int res = (action == ACTION_EXISTS) ? 0 : -1;
+
+ AST_RWLIST_RDLOCK(&formats);
+ /* Check for a specific format */
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ char *stringp, *ext = NULL;
+
+ if (fmt && !exts_compare(f->exts, fmt))
+ continue;
+
+ /* Look for a file matching the supported extensions.
+ * The file must exist, and for OPEN, must match
+ * one of the formats supported by the channel.
+ */
+ stringp = ast_strdupa(f->exts); /* this is in the stack so does not need to be freed */
+ while ( (ext = strsep(&stringp, "|")) ) {
+ struct stat st;
+ char *fn = build_filename(filename, ext);
+
+ if (fn == NULL)
+ continue;
+
+ if ( stat(fn, &st) ) { /* file not existent */
+ ast_free(fn);
+ continue;
+ }
+ /* for 'OPEN' we need to be sure that the format matches
+ * what the channel can process
+ */
+ if (action == ACTION_OPEN) {
+ struct ast_channel *chan = (struct ast_channel *)arg2;
+ FILE *bfile;
+ struct ast_filestream *s;
+
+ if ( !(chan->writeformat & f->format) &&
+ !(f->format & AST_FORMAT_AUDIO_MASK && fmt)) {
+ ast_free(fn);
+ continue; /* not a supported format */
+ }
+ if ( (bfile = fopen(fn, "r")) == NULL) {
+ ast_free(fn);
+ continue; /* cannot open file */
+ }
+ s = get_filestream(f, bfile);
+ if (!s) {
+ fclose(bfile);
+ ast_free(fn); /* cannot allocate descriptor */
+ continue;
+ }
+ if (open_wrapper(s)) {
+ fclose(bfile);
+ ast_free(fn);
+ ast_free(s);
+ continue; /* cannot run open on file */
+ }
+ /* ok this is good for OPEN */
+ res = 1; /* found */
+ s->lasttimeout = -1;
+ s->fmt = f;
+ s->trans = NULL;
+ s->filename = NULL;
+ if (s->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ if (chan->stream)
+ ast_closestream(chan->stream);
+ chan->stream = s;
+ } else {
+ if (chan->vstream)
+ ast_closestream(chan->vstream);
+ chan->vstream = s;
+ }
+ ast_free(fn);
+ break;
+ }
+ switch (action) {
+ case ACTION_OPEN:
+ break; /* will never get here */
+
+ case ACTION_EXISTS: /* return the matching format */
+ res |= f->format;
+ break;
+
+ case ACTION_DELETE:
+ if ( (res = unlink(fn)) )
+ ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
+ break;
+
+ case ACTION_RENAME:
+ case ACTION_COPY: {
+ char *nfn = build_filename((const char *)arg2, ext);
+ if (!nfn)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ else {
+ res = action == ACTION_COPY ? copy(fn, nfn) : rename(fn, nfn);
+ if (res)
+ ast_log(LOG_WARNING, "%s(%s,%s) failed: %s\n",
+ action == ACTION_COPY ? "copy" : "rename",
+ fn, nfn, strerror(errno));
+ ast_free(nfn);
+ }
+ }
+ break;
+
+ default:
+ ast_log(LOG_WARNING, "Unknown helper %d\n", action);
+ }
+ ast_free(fn);
+ }
+ }
+ AST_RWLIST_UNLOCK(&formats);
+ return res;
+}
+
+/*!
+ * \brief helper routine to locate a file with a given format
+ * and language preference.
+ * Try preflang, preflang with stripped '_' suffix, or NULL.
+ * In the standard asterisk, language goes just before the last component.
+ * In an alternative configuration, the language should be a prefix
+ * to the actual filename.
+ *
+ * The last parameter(s) point to a buffer of sufficient size,
+ * which on success is filled with the matching filename.
+ */
+static int fileexists_core(const char *filename, const char *fmt, const char *preflang,
+ char *buf, int buflen)
+{
+ int res = -1;
+ int langlen; /* length of language string */
+ const char *c = strrchr(filename, '/');
+ int offset = c ? c - filename + 1 : 0; /* points right after the last '/' */
+
+ if (preflang == NULL)
+ preflang = "";
+ langlen = strlen(preflang);
+
+ if (buflen < langlen + strlen(filename) + 2) {
+ ast_log(LOG_WARNING, "buffer too small\n");
+ buf[0] = '\0'; /* set to empty */
+ buf = alloca(langlen + strlen(filename) + 2); /* room for everything */
+ }
+ if (buf == NULL)
+ return 0;
+ buf[0] = '\0';
+ for (;;) {
+ if (ast_language_is_prefix) { /* new layout */
+ if (langlen) {
+ strcpy(buf, preflang);
+ buf[langlen] = '/';
+ strcpy(buf + langlen + 1, filename);
+ } else
+ strcpy(buf, filename); /* first copy the full string */
+ } else { /* old layout */
+ strcpy(buf, filename); /* first copy the full string */
+ if (langlen) {
+ /* insert the language and suffix if needed */
+ strcpy(buf + offset, preflang);
+ sprintf(buf + offset + langlen, "/%s", filename + offset);
+ }
+ }
+ res = ast_filehelper(buf, NULL, fmt, ACTION_EXISTS);
+ if (res > 0) /* found format */
+ break;
+ if (langlen == 0) /* no more formats */
+ break;
+ if (preflang[langlen] == '_') /* we are on the local suffix */
+ langlen = 0; /* try again with no language */
+ else
+ langlen = (c = strchr(preflang, '_')) ? c - preflang : 0;
+ }
+ return res;
+}
+
+struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang)
+{
+ return ast_openstream_full(chan, filename, preflang, 0);
+}
+
+struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
+{
+ /*
+ * Use fileexists_core() to find a file in a compatible
+ * language and format, set up a suitable translator,
+ * and open the stream.
+ */
+ int fmts, res, buflen;
+ char *buf;
+
+ if (!asis) {
+ /* do this first, otherwise we detect the wrong writeformat */
+ ast_stopstream(chan);
+ if (chan->generator)
+ ast_deactivate_generator(chan);
+ }
+ if (preflang == NULL)
+ preflang = "";
+ buflen = strlen(preflang) + strlen(filename) + 2;
+ buf = alloca(buflen);
+ if (buf == NULL)
+ return NULL;
+ fmts = fileexists_core(filename, NULL, preflang, buf, buflen);
+ if (fmts > 0)
+ fmts &= AST_FORMAT_AUDIO_MASK;
+ if (fmts < 1) {
+ ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
+ return NULL;
+ }
+ chan->oldwriteformat = chan->writeformat;
+ /* Set the channel to a format we can work with */
+ res = ast_set_write_format(chan, fmts);
+ res = ast_filehelper(buf, chan, NULL, ACTION_OPEN);
+ if (res >= 0)
+ return chan->stream;
+ return NULL;
+}
+
+struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang)
+{
+ /* As above, but for video. But here we don't have translators
+ * so we must enforce a format.
+ */
+ unsigned int format;
+ char *buf;
+ int buflen;
+
+ if (preflang == NULL)
+ preflang = "";
+ buflen = strlen(preflang) + strlen(filename) + 2;
+ buf = alloca(buflen);
+ if (buf == NULL)
+ return NULL;
+
+ for (format = AST_FORMAT_AUDIO_MASK + 1; format <= AST_FORMAT_VIDEO_MASK; format = format << 1) {
+ int fd;
+ const char *fmt;
+
+ if (!(chan->nativeformats & format))
+ continue;
+ fmt = ast_getformatname(format);
+ if ( fileexists_core(filename, fmt, preflang, buf, buflen) < 1) /* no valid format */
+ continue;
+ fd = ast_filehelper(buf, chan, fmt, ACTION_OPEN);
+ if (fd >= 0)
+ return chan->vstream;
+ ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
+ }
+ return NULL;
+}
+
+struct ast_frame *ast_readframe(struct ast_filestream *s)
+{
+ struct ast_frame *f = NULL;
+ int whennext = 0;
+ if (s && s->fmt)
+ f = s->fmt->read(s, &whennext);
+ return f;
+}
+
+enum fsread_res {
+ FSREAD_FAILURE,
+ FSREAD_SUCCESS_SCHED,
+ FSREAD_SUCCESS_NOSCHED,
+};
+
+static int ast_fsread_audio(const void *data);
+
+static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
+{
+ int whennext = 0;
+
+ while (!whennext) {
+ struct ast_frame *fr;
+
+ if (s->orig_chan_name && strcasecmp(s->owner->name, s->orig_chan_name))
+ goto return_failure;
+
+ fr = s->fmt->read(s, &whennext);
+ if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
+ if (fr)
+ ast_log(LOG_WARNING, "Failed to write frame\n");
+ goto return_failure;
+ }
+ }
+ if (whennext != s->lasttimeout) {
+#ifdef HAVE_ZAPTEL
+ if (s->owner->timingfd > -1)
+ ast_settimeout(s->owner, whennext, ast_fsread_audio, s);
+ else
+#endif
+ s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_fsread_audio, s);
+ s->lasttimeout = whennext;
+ return FSREAD_SUCCESS_NOSCHED;
+ }
+ return FSREAD_SUCCESS_SCHED;
+
+return_failure:
+ s->owner->streamid = -1;
+#ifdef HAVE_ZAPTEL
+ ast_settimeout(s->owner, 0, NULL, NULL);
+#endif
+ return FSREAD_FAILURE;
+}
+
+static int ast_fsread_audio(const void *data)
+{
+ struct ast_filestream *fs = (struct ast_filestream *)data;
+ enum fsread_res res;
+
+ res = ast_readaudio_callback(fs);
+
+ if (res == FSREAD_SUCCESS_SCHED)
+ return 1;
+
+ return 0;
+}
+
+static int ast_fsread_video(const void *data);
+
+static enum fsread_res ast_readvideo_callback(struct ast_filestream *s)
+{
+ int whennext = 0;
+
+ while (!whennext) {
+ struct ast_frame *fr = s->fmt->read(s, &whennext);
+ if (!fr || ast_write(s->owner, fr)) { /* no stream or error, as above */
+ if (fr)
+ ast_log(LOG_WARNING, "Failed to write frame\n");
+ s->owner->vstreamid = -1;
+ return FSREAD_FAILURE;
+ }
+ }
+
+ if (whennext != s->lasttimeout) {
+ s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext / 8,
+ ast_fsread_video, s);
+ s->lasttimeout = whennext;
+ return FSREAD_SUCCESS_NOSCHED;
+ }
+
+ return FSREAD_SUCCESS_SCHED;
+}
+
+static int ast_fsread_video(const void *data)
+{
+ struct ast_filestream *fs = (struct ast_filestream *)data;
+ enum fsread_res res;
+
+ res = ast_readvideo_callback(fs);
+
+ if (res == FSREAD_SUCCESS_SCHED)
+ return 1;
+
+ return 0;
+}
+
+int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
+{
+ s->owner = chan;
+ return 0;
+}
+
+int ast_playstream(struct ast_filestream *s)
+{
+ enum fsread_res res;
+
+ if (s->fmt->format & AST_FORMAT_AUDIO_MASK)
+ res = ast_readaudio_callback(s);
+ else
+ res = ast_readvideo_callback(s);
+
+ return (res == FSREAD_FAILURE) ? -1 : 0;
+}
+
+int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ return fs->fmt->seek(fs, sample_offset, whence);
+}
+
+int ast_truncstream(struct ast_filestream *fs)
+{
+ return fs->fmt->trunc(fs);
+}
+
+off_t ast_tellstream(struct ast_filestream *fs)
+{
+ return fs->fmt->tell(fs);
+}
+
+int ast_stream_fastforward(struct ast_filestream *fs, off_t ms)
+{
+ return ast_seekstream(fs, ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
+}
+
+int ast_stream_rewind(struct ast_filestream *fs, off_t ms)
+{
+ return ast_seekstream(fs, -ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
+}
+
+int ast_closestream(struct ast_filestream *f)
+{
+ char *cmd = NULL;
+ size_t size = 0;
+ /* Stop a running stream if there is one */
+ if (f->owner) {
+ if (f->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ f->owner->stream = NULL;
+ if (f->owner->streamid > -1)
+ ast_sched_del(f->owner->sched, f->owner->streamid);
+ f->owner->streamid = -1;
+#ifdef HAVE_ZAPTEL
+ ast_settimeout(f->owner, 0, NULL, NULL);
+#endif
+ } else {
+ f->owner->vstream = NULL;
+ if (f->owner->vstreamid > -1)
+ ast_sched_del(f->owner->sched, f->owner->vstreamid);
+ f->owner->vstreamid = -1;
+ }
+ }
+ /* destroy the translator on exit */
+ if (f->trans)
+ ast_translator_free_path(f->trans);
+
+ if (f->realfilename && f->filename) {
+ size = strlen(f->filename) + strlen(f->realfilename) + 15;
+ cmd = alloca(size);
+ memset(cmd,0,size);
+ snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename);
+ ast_safe_system(cmd);
+ }
+
+ if (f->filename)
+ ast_free(f->filename);
+ if (f->realfilename)
+ ast_free(f->realfilename);
+ if (f->fmt->close)
+ f->fmt->close(f);
+ fclose(f->f);
+ if (f->vfs)
+ ast_closestream(f->vfs);
+ if (f->orig_chan_name)
+ free((void *) f->orig_chan_name);
+ ast_module_unref(f->fmt->module);
+ ast_free(f);
+ return 0;
+}
+
+
+/*
+ * Look the various language-specific places where a file could exist.
+ */
+int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
+{
+ char *buf;
+ int buflen;
+
+ if (preflang == NULL)
+ preflang = "";
+ buflen = strlen(preflang) + strlen(filename) + 2; /* room for everything */
+ buf = alloca(buflen);
+ if (buf == NULL)
+ return 0;
+ return fileexists_core(filename, fmt, preflang, buf, buflen);
+}
+
+int ast_filedelete(const char *filename, const char *fmt)
+{
+ return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
+}
+
+int ast_filerename(const char *filename, const char *filename2, const char *fmt)
+{
+ return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
+}
+
+int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
+{
+ return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
+}
+
+int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
+{
+ struct ast_filestream *fs;
+ struct ast_filestream *vfs=NULL;
+ char fmt[256];
+
+ fs = ast_openstream(chan, filename, preflang);
+ if (fs)
+ vfs = ast_openvstream(chan, filename, preflang);
+ if (vfs) {
+ ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_getformatname(vfs->fmt->format));
+ }
+ if (fs){
+ int res;
+ if (ast_test_flag(chan, AST_FLAG_MASQ_NOSTREAM))
+ fs->orig_chan_name = ast_strdup(chan->name);
+ if (ast_applystream(chan, fs))
+ return -1;
+ if (vfs && ast_applystream(chan, vfs))
+ return -1;
+ res = ast_playstream(fs);
+ if (!res && vfs)
+ res = ast_playstream(vfs);
+ ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", chan->name, filename, ast_getformatname(chan->writeformat), preflang ? preflang : "default");
+
+ return res;
+ }
+ ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname_multiple(fmt, sizeof(fmt), chan->nativeformats), strerror(errno));
+ return -1;
+}
+
+struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
+{
+ FILE *bfile;
+ struct ast_format *f;
+ struct ast_filestream *fs = NULL;
+ char *fn;
+
+ AST_RWLIST_RDLOCK(&formats);
+
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ fs = NULL;
+ if (!exts_compare(f->exts, type))
+ continue;
+
+ fn = build_filename(filename, type);
+ errno = 0;
+ bfile = fopen(fn, "r");
+ if (!bfile || (fs = get_filestream(f, bfile)) == NULL ||
+ open_wrapper(fs) ) {
+ ast_log(LOG_WARNING, "Unable to open %s\n", fn);
+ if (fs)
+ ast_free(fs);
+ if (bfile)
+ fclose(bfile);
+ ast_free(fn);
+ continue;
+ }
+ /* found it */
+ fs->trans = NULL;
+ fs->fmt = f;
+ fs->flags = flags;
+ fs->mode = mode;
+ fs->filename = ast_strdup(filename);
+ fs->vfs = NULL;
+ break;
+ }
+
+ AST_RWLIST_UNLOCK(&formats);
+ if (!fs)
+ ast_log(LOG_WARNING, "No such format '%s'\n", type);
+
+ return fs;
+}
+
+struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
+{
+ int fd, myflags = 0;
+ /* compiler claims this variable can be used before initialization... */
+ FILE *bfile = NULL;
+ struct ast_format *f;
+ struct ast_filestream *fs = NULL;
+ char *buf = NULL;
+ size_t size = 0;
+ int format_found = 0;
+
+ AST_RWLIST_RDLOCK(&formats);
+
+ /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
+ /* We really can't use O_APPEND as it will break WAV header updates */
+ if (flags & O_APPEND) {
+ flags &= ~O_APPEND;
+ } else {
+ myflags = O_TRUNC;
+ }
+
+ myflags |= O_WRONLY | O_CREAT;
+
+ /* XXX need to fix this - we should just do the fopen,
+ * not open followed by fdopen()
+ */
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ char *fn, *orig_fn = NULL;
+ if (fs)
+ break;
+
+ if (!exts_compare(f->exts, type))
+ continue;
+ else
+ format_found = 1;
+
+ fn = build_filename(filename, type);
+ fd = open(fn, flags | myflags, mode);
+ if (fd > -1) {
+ /* fdopen() the resulting file stream */
+ bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
+ if (!bfile) {
+ ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
+ close(fd);
+ fd = -1;
+ }
+ }
+
+ if (ast_opt_cache_record_files && (fd > -1)) {
+ char *c;
+
+ fclose(bfile); /* this also closes fd */
+ /*
+ We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
+ What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
+ */
+ orig_fn = ast_strdupa(fn);
+ for (c = fn; *c; c++)
+ if (*c == '/')
+ *c = '_';
+
+ size = strlen(fn) + strlen(record_cache_dir) + 2;
+ buf = alloca(size);
+ strcpy(buf, record_cache_dir);
+ strcat(buf, "/");
+ strcat(buf, fn);
+ ast_free(fn);
+ fn = buf;
+ fd = open(fn, flags | myflags, mode);
+ if (fd > -1) {
+ /* fdopen() the resulting file stream */
+ bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
+ if (!bfile) {
+ ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
+ close(fd);
+ fd = -1;
+ }
+ }
+ }
+ if (fd > -1) {
+ errno = 0;
+ fs = get_filestream(f, bfile);
+ if (!fs || rewrite_wrapper(fs, comment)) {
+ ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
+ close(fd);
+ if (orig_fn) {
+ unlink(fn);
+ unlink(orig_fn);
+ }
+ if (fs)
+ ast_free(fs);
+ fs = NULL;
+ continue;
+ }
+ fs->trans = NULL;
+ fs->fmt = f;
+ fs->flags = flags;
+ fs->mode = mode;
+ if (orig_fn) {
+ fs->realfilename = ast_strdup(orig_fn);
+ fs->filename = ast_strdup(fn);
+ } else {
+ fs->realfilename = NULL;
+ fs->filename = ast_strdup(filename);
+ }
+ fs->vfs = NULL;
+ /* If truncated, we'll be at the beginning; if not truncated, then append */
+ f->seek(fs, 0, SEEK_END);
+ } else if (errno != EEXIST) {
+ ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
+ if (orig_fn)
+ unlink(orig_fn);
+ }
+ /* if buf != NULL then fn is already free and pointing to it */
+ if (!buf)
+ ast_free(fn);
+ }
+
+ AST_RWLIST_UNLOCK(&formats);
+
+ if (!format_found)
+ ast_log(LOG_WARNING, "No such format '%s'\n", type);
+
+ return fs;
+}
+
+/*!
+ * \brief the core of all waitstream() functions
+ */
+static int waitstream_core(struct ast_channel *c, const char *breakon,
+ const char *forward, const char *rewind, int skip_ms,
+ int audiofd, int cmdfd, const char *context)
+{
+ const char *orig_chan_name = NULL;
+ int err = 0;
+
+ if (!breakon)
+ breakon = "";
+ if (!forward)
+ forward = "";
+ if (!rewind)
+ rewind = "";
+
+ /* Switch the channel to end DTMF frame only. waitstream_core doesn't care about the start of DTMF. */
+ ast_set_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ if (ast_test_flag(c, AST_FLAG_MASQ_NOSTREAM))
+ orig_chan_name = ast_strdupa(c->name);
+
+ while (c->stream) {
+ int res;
+ int ms;
+
+ if (orig_chan_name && strcasecmp(orig_chan_name, c->name)) {
+ ast_stopstream(c);
+ err = 1;
+ break;
+ }
+
+ ms = ast_sched_wait(c->sched);
+
+ if (ms < 0 && !c->timingfunc) {
+ ast_stopstream(c);
+ break;
+ }
+ if (ms < 0)
+ ms = 1000;
+ if (cmdfd < 0) {
+ res = ast_waitfor(c, ms);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ }
+ } else {
+ int outfd;
+ struct ast_channel *rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
+ if (!rchan && (outfd < 0) && (ms)) {
+ /* Continue */
+ if (errno == EINTR)
+ continue;
+ ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ } else if (outfd > -1) { /* this requires cmdfd set */
+ /* The FD we were watching has something waiting */
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return 1;
+ }
+ /* if rchan is set, it is 'c' */
+ res = rchan ? 1 : 0; /* map into 'res' values */
+ }
+ if (res > 0) {
+ struct ast_frame *fr = ast_read(c);
+ if (!fr) {
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ }
+ switch (fr->frametype) {
+ case AST_FRAME_DTMF_END:
+ if (context) {
+ const char exten[2] = { fr->subclass, '\0' };
+ if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) {
+ res = fr->subclass;
+ ast_frfree(fr);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ }
+ } else {
+ res = fr->subclass;
+ if (strchr(forward,res)) {
+ ast_stream_fastforward(c->stream, skip_ms);
+ } else if (strchr(rewind,res)) {
+ ast_stream_rewind(c->stream, skip_ms);
+ } else if (strchr(breakon, res)) {
+ ast_frfree(fr);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ }
+ }
+ break;
+ case AST_FRAME_CONTROL:
+ switch (fr->subclass) {
+ case AST_CONTROL_HANGUP:
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ ast_frfree(fr);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_ANSWER:
+ case AST_CONTROL_VIDUPDATE:
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ /* Unimportant */
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
+ }
+ break;
+ case AST_FRAME_VOICE:
+ /* Write audio if appropriate */
+ if (audiofd > -1)
+ write(audiofd, fr->data, fr->datalen);
+ default:
+ /* Ignore all others */
+ break;
+ }
+ ast_frfree(fr);
+ }
+ ast_sched_runq(c->sched);
+ }
+
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ return (err || c->_softhangup) ? -1 : 0;
+}
+
+int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms)
+{
+ return waitstream_core(c, breakon, forward, rewind, ms,
+ -1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */);
+}
+
+int ast_waitstream(struct ast_channel *c, const char *breakon)
+{
+ return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL);
+}
+
+int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
+{
+ return waitstream_core(c, breakon, NULL, NULL, 0,
+ audiofd, cmdfd, NULL /* no context */);
+}
+
+int ast_waitstream_exten(struct ast_channel *c, const char *context)
+{
+ /* Waitstream, with return in the case of a valid 1 digit extension */
+ /* in the current or specified context being pressed */
+
+ if (!context)
+ context = c->context;
+ return waitstream_core(c, NULL, NULL, NULL, 0,
+ -1, -1, context);
+}
+
+/*
+ * if the file name is non-empty, try to play it.
+ * Return 0 if success, -1 if error, digit if interrupted by a digit.
+ * If digits == "" then we can simply check for non-zero.
+ */
+int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
+{
+ int res = 0;
+ if (!ast_strlen_zero(file)) {
+ res = ast_streamfile(chan, file, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, digits);
+ }
+ return res;
+}
+
+static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-10s %-10s %-20s\n"
+#define FORMAT2 "%-10s %-10s %-20s\n"
+ struct ast_format *f;
+ int count_fmt = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show file formats";
+ e->usage =
+ "Usage: core show file formats\n"
+ " Displays currently registered file formats (if any).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT, "Format", "Name", "Extensions");
+ ast_cli(a->fd, FORMAT, "------", "----", "----------");
+
+ AST_RWLIST_RDLOCK(&formats);
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ ast_cli(a->fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
+ count_fmt++;
+ }
+ AST_RWLIST_UNLOCK(&formats);
+ ast_cli(a->fd, "%d file formats registered.\n", count_fmt);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+struct ast_cli_entry cli_file[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats")
+};
+
+int ast_file_init(void)
+{
+ ast_cli_register_multiple(cli_file, sizeof(cli_file) / sizeof(struct ast_cli_entry));
+ return 0;
+}
diff --git a/trunk/main/fixedjitterbuf.c b/trunk/main/fixedjitterbuf.c
new file mode 100644
index 000000000..8a885b8e6
--- /dev/null
+++ b/trunk/main/fixedjitterbuf.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2005, Attractel OOD
+ *
+ * Contributors:
+ * Slav Klenov <slav@securax.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.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ *
+ * \brief Jitterbuffering algorithm.
+ *
+ * \author Slav Klenov <slav@securax.org>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <assert.h>
+
+#include "asterisk/utils.h"
+#include "fixedjitterbuf.h"
+
+#undef FIXED_JB_DEBUG
+
+#ifdef FIXED_JB_DEBUG
+#define ASSERT(a)
+#else
+#define ASSERT(a) assert(a)
+#endif
+
+/*! \brief private fixed_jb structure */
+struct fixed_jb
+{
+ struct fixed_jb_frame *frames;
+ struct fixed_jb_frame *tail;
+ struct fixed_jb_conf conf;
+ long rxcore;
+ long delay;
+ long next_delivery;
+ int force_resynch;
+};
+
+
+static struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb);
+static void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame);
+static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame);
+static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now);
+
+static inline struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb)
+{
+ return ast_calloc(1, sizeof(*jb));
+}
+
+static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
+{
+ ast_free(frame);
+}
+
+static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame)
+{
+ struct fixed_jb_frame *fr;
+
+ /* unlink the frame */
+ fr = jb->frames;
+ jb->frames = fr->next;
+ if (jb->frames) {
+ jb->frames->prev = NULL;
+ } else {
+ /* the jb is empty - update tail */
+ jb->tail = NULL;
+ }
+
+ /* update next */
+ jb->next_delivery = fr->delivery + fr->ms;
+
+ /* copy the destination */
+ memcpy(frame, fr, sizeof(struct fixed_jb_frame));
+
+ /* and release the frame */
+ release_jb_frame(jb, fr);
+}
+
+
+struct fixed_jb *fixed_jb_new(struct fixed_jb_conf *conf)
+{
+ struct fixed_jb *jb;
+
+ if (!(jb = ast_calloc(1, sizeof(*jb))))
+ return NULL;
+
+ /* First copy our config */
+ memcpy(&jb->conf, conf, sizeof(struct fixed_jb_conf));
+
+ /* we dont need the passed config anymore - continue working with the saved one */
+ conf = &jb->conf;
+
+ /* validate the configuration */
+ if (conf->jbsize < 1)
+ conf->jbsize = FIXED_JB_SIZE_DEFAULT;
+
+ if (conf->resync_threshold < 1)
+ conf->resync_threshold = FIXED_JB_RESYNCH_THRESHOLD_DEFAULT;
+
+ /* Set the constant delay to the jitterbuf */
+ jb->delay = conf->jbsize;
+
+ return jb;
+}
+
+
+void fixed_jb_destroy(struct fixed_jb *jb)
+{
+ /* jitterbuf MUST be empty before it can be destroyed */
+ ASSERT(jb->frames == NULL);
+
+ ast_free(jb);
+}
+
+
+static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now)
+{
+ long diff, offset;
+ struct fixed_jb_frame *frame;
+
+ /* If jb is empty, just reinitialize the jb */
+ if (!jb->frames) {
+ /* debug check: tail should also be NULL */
+ ASSERT(jb->tail == NULL);
+
+ return fixed_jb_put_first(jb, data, ms, ts, now);
+ }
+
+ /* Adjust all jb state just as the new frame is with delivery = the delivery of the last
+ frame (e.g. this one with max delivery) + the length of the last frame. */
+
+ /* Get the diff in timestamps */
+ diff = ts - jb->tail->ts;
+
+ /* Ideally this should be just the length of the last frame. The deviation is the desired
+ offset */
+ offset = diff - jb->tail->ms;
+
+ /* Do we really need to resynch, or this is just a frame for dropping? */
+ if (!jb->force_resynch && (offset < jb->conf.resync_threshold && offset > -jb->conf.resync_threshold))
+ return FIXED_JB_DROP;
+
+ /* Reset the force resynch flag */
+ jb->force_resynch = 0;
+
+ /* apply the offset to the jb state */
+ jb->rxcore -= offset;
+ frame = jb->frames;
+ while (frame) {
+ frame->ts += offset;
+ frame = frame->next;
+ }
+
+ /* now jb_put() should add the frame at a last position */
+ return fixed_jb_put(jb, data, ms, ts, now);
+}
+
+
+void fixed_jb_set_force_resynch(struct fixed_jb *jb)
+{
+ jb->force_resynch = 1;
+}
+
+
+int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now)
+{
+ /* this is our first frame - set the base of the receivers time */
+ jb->rxcore = now - ts;
+
+ /* init next for a first time - it should be the time the first frame should be played */
+ jb->next_delivery = now + jb->delay;
+
+ /* put the frame */
+ return fixed_jb_put(jb, data, ms, ts, now);
+}
+
+
+int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now)
+{
+ struct fixed_jb_frame *frame, *next, *newframe;
+ long delivery;
+
+ /* debug check the validity of the input params */
+ ASSERT(data != NULL);
+ /* do not allow frames shorter than 2 ms */
+ ASSERT(ms >= 2);
+ ASSERT(ts >= 0);
+ ASSERT(now >= 0);
+
+ delivery = jb->rxcore + jb->delay + ts;
+
+ /* check if the new frame is not too late */
+ if (delivery < jb->next_delivery) {
+ /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
+ the force resynch flag was not set. */
+ return resynch_jb(jb, data, ms, ts, now);
+ }
+
+ /* what if the delivery time is bigger than next + delay? Seems like a frame for the future.
+ However, allow more resync_threshold ms in advance */
+ if (delivery > jb->next_delivery + jb->delay + jb->conf.resync_threshold) {
+ /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
+ the force resynch flag was not set. */
+ return resynch_jb(jb, data, ms, ts, now);
+ }
+
+ /* find the right place in the frames list, sorted by delivery time */
+ frame = jb->tail;
+ while (frame && frame->delivery > delivery) {
+ frame = frame->prev;
+ }
+
+ /* Check if the new delivery time is not covered already by the chosen frame */
+ if (frame && (frame->delivery == delivery ||
+ delivery < frame->delivery + frame->ms ||
+ (frame->next && delivery + ms > frame->next->delivery)))
+ {
+ /* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than
+ the size of the jb */
+
+ /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
+ the force resynch flag was not set. */
+ return resynch_jb(jb, data, ms, ts, now);
+ }
+
+ /* Reset the force resynch flag */
+ jb->force_resynch = 0;
+
+ /* Get a new frame */
+ newframe = alloc_jb_frame(jb);
+ newframe->data = data;
+ newframe->ts = ts;
+ newframe->ms = ms;
+ newframe->delivery = delivery;
+
+ /* and insert it right on place */
+ if (frame) {
+ next = frame->next;
+ frame->next = newframe;
+ if (next) {
+ newframe->next = next;
+ next->prev = newframe;
+ } else {
+ /* insert after the last frame - should update tail */
+ jb->tail = newframe;
+ newframe->next = NULL;
+ }
+ newframe->prev = frame;
+
+ return FIXED_JB_OK;
+ } else if (!jb->frames) {
+ /* the frame list is empty or thats just the first frame ever */
+ /* tail should also be NULL is that case */
+ ASSERT(jb->tail == NULL);
+ jb->frames = jb->tail = newframe;
+ newframe->next = NULL;
+ newframe->prev = NULL;
+
+ return FIXED_JB_OK;
+ } else {
+ /* insert on a first position - should update frames head */
+ newframe->next = jb->frames;
+ newframe->prev = NULL;
+ jb->frames->prev = newframe;
+ jb->frames = newframe;
+
+ return FIXED_JB_OK;
+ }
+}
+
+
+int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
+{
+ ASSERT(now >= 0);
+ ASSERT(interpl >= 2);
+
+ if (now < jb->next_delivery) {
+ /* too early for the next frame */
+ return FIXED_JB_NOFRAME;
+ }
+
+ /* Is the jb empty? */
+ if (!jb->frames) {
+ /* should interpolate a frame */
+ /* update next */
+ jb->next_delivery += interpl;
+
+ return FIXED_JB_INTERP;
+ }
+
+ /* Isn't it too late for the first frame available in the jb? */
+ if (now > jb->frames->delivery + jb->frames->ms) {
+ /* yes - should drop this frame and update next to point the next frame (get_jb_head() does it) */
+ get_jb_head(jb, frame);
+
+ return FIXED_JB_DROP;
+ }
+
+ /* isn't it too early to play the first frame available? */
+ if (now < jb->frames->delivery) {
+ /* yes - should interpolate one frame */
+ /* update next */
+ jb->next_delivery += interpl;
+
+ return FIXED_JB_INTERP;
+ }
+
+ /* we have a frame for playing now (get_jb_head() updates next) */
+ get_jb_head(jb, frame);
+
+ return FIXED_JB_OK;
+}
+
+
+long fixed_jb_next(struct fixed_jb *jb)
+{
+ return jb->next_delivery;
+}
+
+
+int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout)
+{
+ if (!jb->frames)
+ return FIXED_JB_NOFRAME;
+
+ get_jb_head(jb, frameout);
+
+ return FIXED_JB_OK;
+}
diff --git a/trunk/main/fixedjitterbuf.h b/trunk/main/fixedjitterbuf.h
new file mode 100644
index 000000000..541e99d2d
--- /dev/null
+++ b/trunk/main/fixedjitterbuf.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2005, Attractel OOD
+ *
+ * Contributors:
+ * Slav Klenov <slav@securax.org>
+ *
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
+ *
+ * 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 Jitterbuffering algorithm.
+ *
+ */
+
+#ifndef _FIXEDJITTERBUF_H_
+#define _FIXEDJITTERBUF_H_
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+
+/* return codes */
+enum {
+ FIXED_JB_OK,
+ FIXED_JB_DROP,
+ FIXED_JB_INTERP,
+ FIXED_JB_NOFRAME
+};
+
+
+/* defaults */
+#define FIXED_JB_SIZE_DEFAULT 200
+#define FIXED_JB_RESYNCH_THRESHOLD_DEFAULT 1000
+
+
+/* jb configuration properties */
+struct fixed_jb_conf
+{
+ long jbsize;
+ long resync_threshold;
+};
+
+
+struct fixed_jb_frame
+{
+ void *data;
+ long ts;
+ long ms;
+ long delivery;
+ struct fixed_jb_frame *next;
+ struct fixed_jb_frame *prev;
+};
+
+
+struct fixed_jb;
+
+
+/* jb interface */
+
+struct fixed_jb * fixed_jb_new(struct fixed_jb_conf *conf);
+
+void fixed_jb_destroy(struct fixed_jb *jb);
+
+int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now);
+
+int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now);
+
+int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl);
+
+long fixed_jb_next(struct fixed_jb *jb);
+
+int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout);
+
+void fixed_jb_set_force_resynch(struct fixed_jb *jb);
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _FIXEDJITTERBUF_H_ */
diff --git a/trunk/main/frame.c b/trunk/main/frame.c
new file mode 100644
index 000000000..3100636c5
--- /dev/null
+++ b/trunk/main/frame.c
@@ -0,0 +1,1513 @@
+/*
+ * 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 Frame and codec manipulation routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/translate.h"
+
+#ifdef TRACE_FRAMES
+static int headers;
+static AST_LIST_HEAD_STATIC(headerlist, ast_frame);
+#endif
+
+#if !defined(LOW_MEMORY)
+static void frame_cache_cleanup(void *data);
+
+/*! \brief A per-thread cache of frame headers */
+AST_THREADSTORAGE_CUSTOM(frame_cache, NULL, frame_cache_cleanup);
+
+/*!
+ * \brief Maximum ast_frame cache size
+ *
+ * In most cases where the frame header cache will be useful, the size
+ * of the cache will stay very small. However, it is not always the case that
+ * the same thread that allocates the frame will be the one freeing them, so
+ * sometimes a thread will never have any frames in its cache, or the cache
+ * will never be pulled from. For the latter case, we limit the maximum size.
+ */
+#define FRAME_CACHE_MAX_SIZE 10
+
+/*! \brief This is just so ast_frames, a list head struct for holding a list of
+ * ast_frame structures, is defined. */
+AST_LIST_HEAD_NOLOCK(ast_frames, ast_frame);
+
+struct ast_frame_cache {
+ struct ast_frames list;
+ size_t size;
+};
+#endif
+
+#define SMOOTHER_SIZE 8000
+
+enum frame_type {
+ TYPE_HIGH, /* 0x0 */
+ TYPE_LOW, /* 0x1 */
+ TYPE_SILENCE, /* 0x2 */
+ TYPE_DONTSEND /* 0x3 */
+};
+
+#define TYPE_MASK 0x3
+
+struct ast_smoother {
+ int size;
+ int format;
+ int readdata;
+ int optimizablestream;
+ int flags;
+ float samplesperbyte;
+ struct ast_frame f;
+ struct timeval delivery;
+ char data[SMOOTHER_SIZE];
+ char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
+ struct ast_frame *opt;
+ int len;
+};
+
+/*! \brief Definition of supported media formats (codecs) */
+static struct ast_format_list AST_FORMAT_LIST[] = {
+ { AST_FORMAT_G723_1 , "g723", 8000, "G.723.1", 20, 30, 300, 30, 30 }, /*!< G723.1 */
+ { AST_FORMAT_GSM, "gsm", 8000, "GSM", 33, 20, 300, 20, 20 }, /*!< codec_gsm.c */
+ { AST_FORMAT_ULAW, "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20 }, /*!< codec_ulaw.c */
+ { AST_FORMAT_ALAW, "alaw", 8000, "G.711 A-law", 80, 10, 150, 10, 20 }, /*!< codec_alaw.c */
+ { AST_FORMAT_G726, "g726", 8000, "G.726 RFC3551", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */
+ { AST_FORMAT_ADPCM, "adpcm" , 8000, "ADPCM", 40, 10, 300, 10, 20 }, /*!< codec_adpcm.c */
+ { AST_FORMAT_SLINEAR, "slin", 8000, "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE }, /*!< Signed linear */
+ { AST_FORMAT_LPC10, "lpc10", 8000, "LPC10", 7, 20, 20, 20, 20 }, /*!< codec_lpc10.c */
+ { AST_FORMAT_G729A, "g729", 8000, "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729 }, /*!< Binary commercial distribution */
+ { AST_FORMAT_SPEEX, "speex", 8000, "SpeeX", 10, 10, 60, 10, 20 }, /*!< codec_speex.c */
+ { AST_FORMAT_ILBC, "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30 }, /*!< codec_ilbc.c */ /* inc=30ms - workaround */
+ { AST_FORMAT_G726_AAL2, "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */
+ { AST_FORMAT_G722, "g722", 16000, "G722", 80, 10, 150, 10, 20 }, /*!< codec_g722.c */
+ { AST_FORMAT_SLINEAR16, "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20 }, /*!< Signed linear (16kHz) */
+ { AST_FORMAT_JPEG, "jpeg", 0, "JPEG image"}, /*!< See format_jpeg.c */
+ { AST_FORMAT_PNG, "png", 0, "PNG image"}, /*!< PNG Image format */
+ { AST_FORMAT_H261, "h261", 0, "H.261 Video" }, /*!< H.261 Video Passthrough */
+ { AST_FORMAT_H263, "h263", 0, "H.263 Video" }, /*!< H.263 Passthrough support, see format_h263.c */
+ { AST_FORMAT_H263_PLUS, "h263p", 0, "H.263+ Video" }, /*!< H.263plus passthrough support See format_h263.c */
+ { AST_FORMAT_H264, "h264", 0, "H.264 Video" }, /*!< Passthrough support, see format_h263.c */
+ { AST_FORMAT_MP4_VIDEO, "mpeg4", 0, "MPEG4 Video" }, /*!< Passthrough support for MPEG4 */
+ { AST_FORMAT_T140, "t140", 0, "Passthrough T.140 Realtime Text" }, /*!< Passthrough support for T.140 Realtime Text */
+};
+
+struct ast_frame ast_null_frame = { AST_FRAME_NULL, };
+
+void ast_smoother_reset(struct ast_smoother *s, int size)
+{
+ memset(s, 0, sizeof(*s));
+ s->size = size;
+}
+
+struct ast_smoother *ast_smoother_new(int size)
+{
+ struct ast_smoother *s;
+ if (size < 1)
+ return NULL;
+ if ((s = ast_malloc(sizeof(*s))))
+ ast_smoother_reset(s, size);
+ return s;
+}
+
+int ast_smoother_get_flags(struct ast_smoother *s)
+{
+ return s->flags;
+}
+
+void ast_smoother_set_flags(struct ast_smoother *s, int flags)
+{
+ s->flags = flags;
+}
+
+int ast_smoother_test_flag(struct ast_smoother *s, int flag)
+{
+ return (s->flags & flag);
+}
+
+int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap)
+{
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Huh? Can't smooth a non-voice frame!\n");
+ return -1;
+ }
+ if (!s->format) {
+ s->format = f->subclass;
+ s->samplesperbyte = (float)f->samples / (float)f->datalen;
+ } else if (s->format != f->subclass) {
+ ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
+ return -1;
+ }
+ if (s->len + f->datalen > SMOOTHER_SIZE) {
+ ast_log(LOG_WARNING, "Out of smoother space\n");
+ return -1;
+ }
+ if (((f->datalen == s->size) || ((f->datalen < 10) && (s->flags & AST_SMOOTHER_FLAG_G729)))
+ && !s->opt && (f->offset >= AST_MIN_OFFSET)) {
+ if (!s->len) {
+ /* Optimize by sending the frame we just got
+ on the next read, thus eliminating the douple
+ copy */
+ if (swap)
+ ast_swapcopy_samples(f->data, f->data, f->samples);
+ s->opt = f;
+ return 0;
+ } else {
+ s->optimizablestream++;
+ if (s->optimizablestream > 10) {
+ /* For the past 10 rounds, we have input and output
+ frames of the correct size for this smoother, yet
+ we were unable to optimize because there was still
+ some cruft left over. Lets just drop the cruft so
+ we can move to a fully optimized path */
+ if (swap)
+ ast_swapcopy_samples(f->data, f->data, f->samples);
+ s->len = 0;
+ s->opt = f;
+ return 0;
+ }
+ }
+ } else
+ s->optimizablestream = 0;
+ if (s->flags & AST_SMOOTHER_FLAG_G729) {
+ if (s->len % 10) {
+ ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n");
+ return 0;
+ }
+ }
+ if (swap)
+ ast_swapcopy_samples(s->data+s->len, f->data, f->samples);
+ else
+ memcpy(s->data + s->len, f->data, f->datalen);
+ /* If either side is empty, reset the delivery time */
+ if (!s->len || ast_tvzero(f->delivery) || ast_tvzero(s->delivery)) /* XXX really ? */
+ s->delivery = f->delivery;
+ s->len += f->datalen;
+ return 0;
+}
+
+struct ast_frame *ast_smoother_read(struct ast_smoother *s)
+{
+ struct ast_frame *opt;
+ int len;
+
+ /* IF we have an optimization frame, send it */
+ if (s->opt) {
+ if (s->opt->offset < AST_FRIENDLY_OFFSET)
+ ast_log(LOG_WARNING, "Returning a frame of inappropriate offset (%d).\n",
+ s->opt->offset);
+ opt = s->opt;
+ s->opt = NULL;
+ return opt;
+ }
+
+ /* Make sure we have enough data */
+ if (s->len < s->size) {
+ /* Or, if this is a G.729 frame with VAD on it, send it immediately anyway */
+ if (!((s->flags & AST_SMOOTHER_FLAG_G729) && (s->size % 10)))
+ return NULL;
+ }
+ len = s->size;
+ if (len > s->len)
+ len = s->len;
+ /* Make frame */
+ s->f.frametype = AST_FRAME_VOICE;
+ s->f.subclass = s->format;
+ s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
+ s->f.offset = AST_FRIENDLY_OFFSET;
+ s->f.datalen = len;
+ /* Samples will be improper given VAD, but with VAD the concept really doesn't even exist */
+ s->f.samples = len * s->samplesperbyte; /* XXX rounding */
+ s->f.delivery = s->delivery;
+ /* Fill Data */
+ memcpy(s->f.data, s->data, len);
+ s->len -= len;
+ /* Move remaining data to the front if applicable */
+ if (s->len) {
+ /* In principle this should all be fine because if we are sending
+ G.729 VAD, the next timestamp will take over anyawy */
+ memmove(s->data, s->data + len, s->len);
+ if (!ast_tvzero(s->delivery)) {
+ /* If we have delivery time, increment it, otherwise, leave it at 0 */
+ s->delivery = ast_tvadd(s->delivery, ast_samp2tv(s->f.samples, 8000));
+ }
+ }
+ /* Return frame */
+ return &s->f;
+}
+
+void ast_smoother_free(struct ast_smoother *s)
+{
+ ast_free(s);
+}
+
+static struct ast_frame *ast_frame_header_new(void)
+{
+ struct ast_frame *f;
+
+#if !defined(LOW_MEMORY)
+ struct ast_frame_cache *frames;
+
+ if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
+ if ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list))) {
+ size_t mallocd_len = f->mallocd_hdr_len;
+ memset(f, 0, sizeof(*f));
+ f->mallocd_hdr_len = mallocd_len;
+ f->mallocd = AST_MALLOCD_HDR;
+ frames->size--;
+ return f;
+ }
+ }
+ if (!(f = ast_calloc_cache(1, sizeof(*f))))
+ return NULL;
+#else
+ if (!(f = ast_calloc(1, sizeof(*f))))
+ return NULL;
+#endif
+
+ f->mallocd_hdr_len = sizeof(*f);
+#ifdef TRACE_FRAMES
+ AST_LIST_LOCK(&headerlist);
+ headers++;
+ AST_LIST_INSERT_HEAD(&headerlist, f, frame_list);
+ AST_LIST_UNLOCK(&headerlist);
+#endif
+
+ return f;
+}
+
+#if !defined(LOW_MEMORY)
+static void frame_cache_cleanup(void *data)
+{
+ struct ast_frame_cache *frames = data;
+ struct ast_frame *f;
+
+ while ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list)))
+ ast_free(f);
+
+ ast_free(frames);
+}
+#endif
+
+void ast_frame_free(struct ast_frame *fr, int cache)
+{
+ if (ast_test_flag(fr, AST_FRFLAG_FROM_TRANSLATOR))
+ ast_translate_frame_freed(fr);
+
+ if (!fr->mallocd)
+ return;
+
+#if !defined(LOW_MEMORY)
+ if (cache && fr->mallocd == AST_MALLOCD_HDR) {
+ /* Cool, only the header is malloc'd, let's just cache those for now
+ * to keep things simple... */
+ struct ast_frame_cache *frames;
+
+ if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))
+ && frames->size < FRAME_CACHE_MAX_SIZE) {
+ AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list);
+ frames->size++;
+ return;
+ }
+ }
+#endif
+
+ if (fr->mallocd & AST_MALLOCD_DATA) {
+ if (fr->data)
+ ast_free(fr->data - fr->offset);
+ }
+ if (fr->mallocd & AST_MALLOCD_SRC) {
+ if (fr->src)
+ ast_free((char *)fr->src);
+ }
+ if (fr->mallocd & AST_MALLOCD_HDR) {
+#ifdef TRACE_FRAMES
+ AST_LIST_LOCK(&headerlist);
+ headers--;
+ AST_LIST_REMOVE(&headerlist, fr, frame_list);
+ AST_LIST_UNLOCK(&headerlist);
+#endif
+ ast_free(fr);
+ }
+}
+
+/*!
+ * \brief 'isolates' a frame by duplicating non-malloc'ed components
+ * (header, src, data).
+ * On return all components are malloc'ed
+ */
+struct ast_frame *ast_frisolate(struct ast_frame *fr)
+{
+ struct ast_frame *out;
+ void *newdata;
+
+ ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR);
+
+ if (!(fr->mallocd & AST_MALLOCD_HDR)) {
+ /* Allocate a new header if needed */
+ if (!(out = ast_frame_header_new()))
+ return NULL;
+ out->frametype = fr->frametype;
+ out->subclass = fr->subclass;
+ out->datalen = fr->datalen;
+ out->samples = fr->samples;
+ out->offset = fr->offset;
+ out->data = fr->data;
+ /* Copy the timing data */
+ ast_copy_flags(out, fr, AST_FRFLAG_HAS_TIMING_INFO);
+ if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) {
+ out->ts = fr->ts;
+ out->len = fr->len;
+ out->seqno = fr->seqno;
+ }
+ } else
+ out = fr;
+
+ if (!(fr->mallocd & AST_MALLOCD_SRC)) {
+ if (fr->src) {
+ if (!(out->src = ast_strdup(fr->src))) {
+ if (out != fr)
+ ast_free(out);
+ return NULL;
+ }
+ }
+ } else
+ out->src = fr->src;
+
+ if (!(fr->mallocd & AST_MALLOCD_DATA)) {
+ if (!(newdata = ast_malloc(fr->datalen + AST_FRIENDLY_OFFSET))) {
+ if (out->src != fr->src)
+ ast_free((void *) out->src);
+ if (out != fr)
+ ast_free(out);
+ return NULL;
+ }
+ newdata += AST_FRIENDLY_OFFSET;
+ out->offset = AST_FRIENDLY_OFFSET;
+ out->datalen = fr->datalen;
+ memcpy(newdata, fr->data, fr->datalen);
+ out->data = newdata;
+ }
+
+ out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
+
+ return out;
+}
+
+struct ast_frame *ast_frdup(const struct ast_frame *f)
+{
+ struct ast_frame *out = NULL;
+ int len, srclen = 0;
+ void *buf = NULL;
+
+#if !defined(LOW_MEMORY)
+ struct ast_frame_cache *frames;
+#endif
+
+ /* Start with standard stuff */
+ len = sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
+ /* If we have a source, add space for it */
+ /*
+ * XXX Watch out here - if we receive a src which is not terminated
+ * properly, we can be easily attacked. Should limit the size we deal with.
+ */
+ if (f->src)
+ srclen = strlen(f->src);
+ if (srclen > 0)
+ len += srclen + 1;
+
+#if !defined(LOW_MEMORY)
+ if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&frames->list, out, frame_list) {
+ if (out->mallocd_hdr_len >= len) {
+ size_t mallocd_len = out->mallocd_hdr_len;
+
+ AST_LIST_REMOVE_CURRENT(frame_list);
+ memset(out, 0, sizeof(*out));
+ out->mallocd_hdr_len = mallocd_len;
+ buf = out;
+ frames->size--;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+#endif
+
+ if (!buf) {
+ if (!(buf = ast_calloc_cache(1, len)))
+ return NULL;
+ out = buf;
+ out->mallocd_hdr_len = len;
+ }
+
+ out->frametype = f->frametype;
+ out->subclass = f->subclass;
+ out->datalen = f->datalen;
+ out->samples = f->samples;
+ out->delivery = f->delivery;
+ /* Set us as having malloc'd header only, so it will eventually
+ get freed. */
+ out->mallocd = AST_MALLOCD_HDR;
+ out->offset = AST_FRIENDLY_OFFSET;
+ if (out->datalen) {
+ out->data = buf + sizeof(*out) + AST_FRIENDLY_OFFSET;
+ memcpy(out->data, f->data, out->datalen);
+ }
+ if (srclen > 0) {
+ out->src = buf + sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
+ /* Must have space since we allocated for it */
+ strcpy((char *)out->src, f->src);
+ }
+ ast_copy_flags(out, f, AST_FRFLAG_HAS_TIMING_INFO);
+ out->ts = f->ts;
+ out->len = f->len;
+ out->seqno = f->seqno;
+ return out;
+}
+
+void ast_swapcopy_samples(void *dst, const void *src, int samples)
+{
+ int i;
+ unsigned short *dst_s = dst;
+ const unsigned short *src_s = src;
+
+ for (i = 0; i < samples; i++)
+ dst_s[i] = (src_s[i]<<8) | (src_s[i]>>8);
+}
+
+
+struct ast_format_list *ast_get_format_list_index(int index)
+{
+ return &AST_FORMAT_LIST[index];
+}
+
+struct ast_format_list *ast_get_format_list(size_t *size)
+{
+ *size = (sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]));
+ return AST_FORMAT_LIST;
+}
+
+char* ast_getformatname(int format)
+{
+ int x;
+ char *ret = "unknown";
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ ret = AST_FORMAT_LIST[x].name;
+ break;
+ }
+ }
+ return ret;
+}
+
+char *ast_getformatname_multiple(char *buf, size_t size, int format)
+{
+ int x;
+ unsigned len;
+ char *start, *end = buf;
+
+ if (!size)
+ return buf;
+ snprintf(end, size, "0x%x (", format);
+ len = strlen(end);
+ end += len;
+ size -= len;
+ start = end;
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits & format) {
+ snprintf(end, size,"%s|",AST_FORMAT_LIST[x].name);
+ len = strlen(end);
+ end += len;
+ size -= len;
+ }
+ }
+ if (start == end)
+ ast_copy_string(start, "nothing)", size);
+ else if (size > 1)
+ *(end -1) = ')';
+ return buf;
+}
+
+static struct ast_codec_alias_table {
+ char *alias;
+ char *realname;
+} ast_codec_alias_table[] = {
+ { "slinear", "slin"},
+ { "slinear16", "slin16"},
+ { "g723.1", "g723"},
+};
+
+static const char *ast_expand_codec_alias(const char *in)
+{
+ int x;
+
+ for (x = 0; x < sizeof(ast_codec_alias_table) / sizeof(ast_codec_alias_table[0]); x++) {
+ if (!strcmp(in,ast_codec_alias_table[x].alias))
+ return ast_codec_alias_table[x].realname;
+ }
+ return in;
+}
+
+int ast_getformatbyname(const char *name)
+{
+ int x, all, format = 0;
+
+ all = strcasecmp(name, "all") ? 0 : 1;
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (all ||
+ !strcasecmp(AST_FORMAT_LIST[x].name,name) ||
+ !strcasecmp(AST_FORMAT_LIST[x].name,ast_expand_codec_alias(name))) {
+ format |= AST_FORMAT_LIST[x].bits;
+ if (!all)
+ break;
+ }
+ }
+
+ return format;
+}
+
+char *ast_codec2str(int codec)
+{
+ int x;
+ char *ret = "unknown";
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == codec) {
+ ret = AST_FORMAT_LIST[x].desc;
+ break;
+ }
+ }
+ return ret;
+}
+
+static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, found=0;
+ char hex[25];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show codecs [audio|video|image]";
+ e->usage =
+ "Usage: core show codecs [audio|video|image]\n"
+ " Displays codec mapping\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc < 3) || (a->argc > 4))
+ return CLI_SHOWUSAGE;
+
+ if (!ast_opt_dont_warn)
+ ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n"
+ "\tIt does not indicate anything about your configuration.\n");
+
+ ast_cli(a->fd, "%11s %9s %10s TYPE %8s %s\n","INT","BINARY","HEX","NAME","DESC");
+ ast_cli(a->fd, "--------------------------------------------------------------------------------\n");
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3],"audio"))) {
+ found = 1;
+ for (i=0;i<13;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(a->fd, "%11u (1 << %2d) %10s audio %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3],"image"))) {
+ found = 1;
+ for (i=16;i<18;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(a->fd, "%11u (1 << %2d) %10s image %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3],"video"))) {
+ found = 1;
+ for (i=18;i<22;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(a->fd, "%11u (1 << %2d) %10s video %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if (!found)
+ return CLI_SHOWUSAGE;
+ else
+ return CLI_SUCCESS;
+}
+
+static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int codec, i, found=0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show codec";
+ e->usage =
+ "Usage: core show codec <number>\n"
+ " Displays codec mapping\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ if (sscanf(a->argv[3],"%d",&codec) != 1)
+ return CLI_SHOWUSAGE;
+
+ for (i = 0; i < 32; i++)
+ if (codec & (1 << i)) {
+ found = 1;
+ ast_cli(a->fd, "%11u (1 << %2d) %s\n",1 << i,i,ast_codec2str(1<<i));
+ }
+
+ if (!found)
+ ast_cli(a->fd, "Codec %d not found\n", codec);
+
+ return CLI_SUCCESS;
+}
+
+/*! Dump a frame for debugging purposes */
+void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix)
+{
+ const char noname[] = "unknown";
+ char ftype[40] = "Unknown Frametype";
+ char cft[80];
+ char subclass[40] = "Unknown Subclass";
+ char csub[80];
+ char moreinfo[40] = "";
+ char cn[60];
+ char cp[40];
+ char cmn[40];
+
+ if (!name)
+ name = noname;
+
+
+ if (!f) {
+ ast_verbose("%s [ %s (NULL) ] [%s]\n",
+ term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
+ term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
+ term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
+ return;
+ }
+ /* XXX We should probably print one each of voice and video when the format changes XXX */
+ if (f->frametype == AST_FRAME_VOICE)
+ return;
+ if (f->frametype == AST_FRAME_VIDEO)
+ return;
+ switch(f->frametype) {
+ case AST_FRAME_DTMF_BEGIN:
+ strcpy(ftype, "DTMF Begin");
+ subclass[0] = f->subclass;
+ subclass[1] = '\0';
+ break;
+ case AST_FRAME_DTMF_END:
+ strcpy(ftype, "DTMF End");
+ subclass[0] = f->subclass;
+ subclass[1] = '\0';
+ break;
+ case AST_FRAME_CONTROL:
+ strcpy(ftype, "Control");
+ switch(f->subclass) {
+ case AST_CONTROL_HANGUP:
+ strcpy(subclass, "Hangup");
+ break;
+ case AST_CONTROL_RING:
+ strcpy(subclass, "Ring");
+ break;
+ case AST_CONTROL_RINGING:
+ strcpy(subclass, "Ringing");
+ break;
+ case AST_CONTROL_ANSWER:
+ strcpy(subclass, "Answer");
+ break;
+ case AST_CONTROL_BUSY:
+ strcpy(subclass, "Busy");
+ break;
+ case AST_CONTROL_TAKEOFFHOOK:
+ strcpy(subclass, "Take Off Hook");
+ break;
+ case AST_CONTROL_OFFHOOK:
+ strcpy(subclass, "Line Off Hook");
+ break;
+ case AST_CONTROL_CONGESTION:
+ strcpy(subclass, "Congestion");
+ break;
+ case AST_CONTROL_FLASH:
+ strcpy(subclass, "Flash");
+ break;
+ case AST_CONTROL_WINK:
+ strcpy(subclass, "Wink");
+ break;
+ case AST_CONTROL_OPTION:
+ strcpy(subclass, "Option");
+ break;
+ case AST_CONTROL_RADIO_KEY:
+ strcpy(subclass, "Key Radio");
+ break;
+ case AST_CONTROL_RADIO_UNKEY:
+ strcpy(subclass, "Unkey Radio");
+ break;
+ case -1:
+ strcpy(subclass, "Stop generators");
+ break;
+ default:
+ snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
+ }
+ break;
+ case AST_FRAME_NULL:
+ strcpy(ftype, "Null Frame");
+ strcpy(subclass, "N/A");
+ break;
+ case AST_FRAME_IAX:
+ /* Should never happen */
+ strcpy(ftype, "IAX Specific");
+ snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
+ break;
+ case AST_FRAME_TEXT:
+ strcpy(ftype, "Text");
+ strcpy(subclass, "N/A");
+ ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
+ break;
+ case AST_FRAME_IMAGE:
+ strcpy(ftype, "Image");
+ snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(f->subclass));
+ break;
+ case AST_FRAME_HTML:
+ strcpy(ftype, "HTML");
+ switch(f->subclass) {
+ case AST_HTML_URL:
+ strcpy(subclass, "URL");
+ ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
+ break;
+ case AST_HTML_DATA:
+ strcpy(subclass, "Data");
+ break;
+ case AST_HTML_BEGIN:
+ strcpy(subclass, "Begin");
+ break;
+ case AST_HTML_END:
+ strcpy(subclass, "End");
+ break;
+ case AST_HTML_LDCOMPLETE:
+ strcpy(subclass, "Load Complete");
+ break;
+ case AST_HTML_NOSUPPORT:
+ strcpy(subclass, "No Support");
+ break;
+ case AST_HTML_LINKURL:
+ strcpy(subclass, "Link URL");
+ ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
+ break;
+ case AST_HTML_UNLINK:
+ strcpy(subclass, "Unlink");
+ break;
+ case AST_HTML_LINKREJECT:
+ strcpy(subclass, "Link Reject");
+ break;
+ default:
+ snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
+ break;
+ }
+ break;
+ case AST_FRAME_MODEM:
+ strcpy(ftype, "Modem");
+ switch (f->subclass) {
+ case AST_MODEM_T38:
+ strcpy(subclass, "T.38");
+ break;
+ case AST_MODEM_V150:
+ strcpy(subclass, "V.150");
+ break;
+ default:
+ snprintf(subclass, sizeof(subclass), "Unknown MODEM frame '%d'\n", f->subclass);
+ break;
+ }
+ break;
+ default:
+ snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
+ }
+ if (!ast_strlen_zero(moreinfo))
+ ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",
+ term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
+ term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
+ f->frametype,
+ term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
+ f->subclass,
+ term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
+ term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
+ else
+ ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",
+ term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
+ term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
+ f->frametype,
+ term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
+ f->subclass,
+ term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
+}
+
+
+#ifdef TRACE_FRAMES
+static char *show_frame_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_frame *f;
+ int x=1;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show frame stats";
+ e->usage =
+ "Usage: core show frame stats\n"
+ " Displays debugging statistics from framer\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ AST_LIST_LOCK(&headerlist);
+ ast_cli(a->fd, " Framer Statistics \n");
+ ast_cli(a->fd, "---------------------------\n");
+ ast_cli(a->fd, "Total allocated headers: %d\n", headers);
+ ast_cli(a->fd, "Queue Dump:\n");
+ AST_LIST_TRAVERSE(&headerlist, f, frame_list)
+ ast_cli(a->fd, "%d. Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
+ AST_LIST_UNLOCK(&headerlist);
+ return CLI_SUCCESS;
+}
+#endif
+
+/* Builtin Asterisk CLI-commands for debugging */
+static struct ast_cli_entry my_clis[] = {
+ AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"),
+ AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"),
+#ifdef TRACE_FRAMES
+ AST_CLI_DEFINE(show_frame_stats, "Shows frame statistics"),
+#endif
+};
+
+int init_framer(void)
+{
+ ast_cli_register_multiple(my_clis, sizeof(my_clis) / sizeof(struct ast_cli_entry));
+ return 0;
+}
+
+void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right)
+{
+ int x, differential = (int) 'A', mem;
+ char *from, *to;
+
+ if (right) {
+ from = pref->order;
+ to = buf;
+ mem = size;
+ } else {
+ to = pref->order;
+ from = buf;
+ mem = 32;
+ }
+
+ memset(to, 0, mem);
+ for (x = 0; x < 32 ; x++) {
+ if (!from[x])
+ break;
+ to[x] = right ? (from[x] + differential) : (from[x] - differential);
+ }
+}
+
+int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size)
+{
+ int x, codec;
+ size_t total_len, slen;
+ char *formatname;
+
+ memset(buf,0,size);
+ total_len = size;
+ buf[0] = '(';
+ total_len--;
+ for(x = 0; x < 32 ; x++) {
+ if (total_len <= 0)
+ break;
+ if (!(codec = ast_codec_pref_index(pref,x)))
+ break;
+ if ((formatname = ast_getformatname(codec))) {
+ slen = strlen(formatname);
+ if (slen > total_len)
+ break;
+ strncat(buf,formatname,total_len);
+ total_len -= slen;
+ }
+ if (total_len && x < 31 && ast_codec_pref_index(pref , x + 1)) {
+ strncat(buf,"|",total_len);
+ total_len--;
+ }
+ }
+ if (total_len) {
+ strncat(buf,")",total_len);
+ total_len--;
+ }
+
+ return size - total_len;
+}
+
+int ast_codec_pref_index(struct ast_codec_pref *pref, int index)
+{
+ int slot = 0;
+
+
+ if ((index >= 0) && (index < sizeof(pref->order))) {
+ slot = pref->order[index];
+ }
+
+ return slot ? AST_FORMAT_LIST[slot-1].bits : 0;
+}
+
+/*! \brief Remove codec from pref list */
+void ast_codec_pref_remove(struct ast_codec_pref *pref, int format)
+{
+ struct ast_codec_pref oldorder;
+ int x, y = 0;
+ int slot;
+ int size;
+
+ if (!pref->order[0])
+ return;
+
+ memcpy(&oldorder, pref, sizeof(oldorder));
+ memset(pref, 0, sizeof(*pref));
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ slot = oldorder.order[x];
+ size = oldorder.framing[x];
+ if (! slot)
+ break;
+ if (AST_FORMAT_LIST[slot-1].bits != format) {
+ pref->order[y] = slot;
+ pref->framing[y++] = size;
+ }
+ }
+
+}
+
+/*! \brief Append codec to list */
+int ast_codec_pref_append(struct ast_codec_pref *pref, int format)
+{
+ int x, newindex = 0;
+
+ ast_codec_pref_remove(pref, format);
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ newindex = x + 1;
+ break;
+ }
+ }
+
+ if (newindex) {
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (!pref->order[x]) {
+ pref->order[x] = newindex;
+ break;
+ }
+ }
+ }
+
+ return x;
+}
+
+/*! \brief Prepend codec to list */
+void ast_codec_pref_prepend(struct ast_codec_pref *pref, int format, int only_if_existing)
+{
+ int x, newindex = 0;
+
+ /* First step is to get the codecs "index number" */
+ for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ newindex = x + 1;
+ break;
+ }
+ }
+ /* Done if its unknown */
+ if (!newindex)
+ return;
+
+ /* Now find any existing occurrence, or the end */
+ for (x = 0; x < 32; x++) {
+ if (!pref->order[x] || pref->order[x] == newindex)
+ break;
+ }
+
+ if (only_if_existing && !pref->order[x])
+ return;
+
+ /* Move down to make space to insert - either all the way to the end,
+ or as far as the existing location (which will be overwritten) */
+ for (; x > 0; x--) {
+ pref->order[x] = pref->order[x - 1];
+ pref->framing[x] = pref->framing[x - 1];
+ }
+
+ /* And insert the new entry */
+ pref->order[0] = newindex;
+ pref->framing[0] = 0; /* ? */
+}
+
+/*! \brief Set packet size for codec */
+int ast_codec_pref_setsize(struct ast_codec_pref *pref, int format, int framems)
+{
+ int x, index = -1;
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ index = x;
+ break;
+ }
+ }
+
+ if (index < 0)
+ return -1;
+
+ /* size validation */
+ if (!framems)
+ framems = AST_FORMAT_LIST[index].def_ms;
+
+ if (AST_FORMAT_LIST[index].inc_ms && framems % AST_FORMAT_LIST[index].inc_ms) /* avoid division by zero */
+ framems -= framems % AST_FORMAT_LIST[index].inc_ms;
+
+ if (framems < AST_FORMAT_LIST[index].min_ms)
+ framems = AST_FORMAT_LIST[index].min_ms;
+
+ if (framems > AST_FORMAT_LIST[index].max_ms)
+ framems = AST_FORMAT_LIST[index].max_ms;
+
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (pref->order[x] == (index + 1)) {
+ pref->framing[x] = framems;
+ break;
+ }
+ }
+
+ return x;
+}
+
+/*! \brief Get packet size for codec */
+struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, int format)
+{
+ int x, index = -1, framems = 0;
+ struct ast_format_list fmt = { 0, };
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ fmt = AST_FORMAT_LIST[x];
+ index = x;
+ break;
+ }
+ }
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (pref->order[x] == (index + 1)) {
+ framems = pref->framing[x];
+ break;
+ }
+ }
+
+ /* size validation */
+ if (!framems)
+ framems = AST_FORMAT_LIST[index].def_ms;
+
+ if (AST_FORMAT_LIST[index].inc_ms && framems % AST_FORMAT_LIST[index].inc_ms) /* avoid division by zero */
+ framems -= framems % AST_FORMAT_LIST[index].inc_ms;
+
+ if (framems < AST_FORMAT_LIST[index].min_ms)
+ framems = AST_FORMAT_LIST[index].min_ms;
+
+ if (framems > AST_FORMAT_LIST[index].max_ms)
+ framems = AST_FORMAT_LIST[index].max_ms;
+
+ fmt.cur_ms = framems;
+
+ return fmt;
+}
+
+/*! \brief Pick a codec */
+int ast_codec_choose(struct ast_codec_pref *pref, int formats, int find_best)
+{
+ int x, ret = 0, slot;
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ slot = pref->order[x];
+
+ if (!slot)
+ break;
+ if (formats & AST_FORMAT_LIST[slot-1].bits) {
+ ret = AST_FORMAT_LIST[slot-1].bits;
+ break;
+ }
+ }
+ if (ret & AST_FORMAT_AUDIO_MASK)
+ return ret;
+
+ ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec");
+
+ return find_best ? ast_best_codec(formats) : 0;
+}
+
+int ast_parse_allow_disallow(struct ast_codec_pref *pref, int *mask, const char *list, int allowing)
+{
+ int errors = 0;
+ char *parse = NULL, *this = NULL, *psize = NULL;
+ int format = 0, framems = 0;
+
+ parse = ast_strdupa(list);
+ while ((this = strsep(&parse, ","))) {
+ framems = 0;
+ if ((psize = strrchr(this, ':'))) {
+ *psize++ = '\0';
+ ast_debug(1, "Packetization for codec: %s is %s\n", this, psize);
+ framems = atoi(psize);
+ if (framems < 0) {
+ framems = 0;
+ errors++;
+ ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this);
+ }
+ }
+ if (!(format = ast_getformatbyname(this))) {
+ ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", allowing ? "allow" : "disallow", this);
+ errors++;
+ continue;
+ }
+
+ if (mask) {
+ if (allowing)
+ *mask |= format;
+ else
+ *mask &= ~format;
+ }
+
+ /* Set up a preference list for audio. Do not include video in preferences
+ since we can not transcode video and have to use whatever is offered
+ */
+ if (pref && (format & AST_FORMAT_AUDIO_MASK)) {
+ if (strcasecmp(this, "all")) {
+ if (allowing) {
+ ast_codec_pref_append(pref, format);
+ ast_codec_pref_setsize(pref, format, framems);
+ }
+ else
+ ast_codec_pref_remove(pref, format);
+ } else if (!allowing) {
+ memset(pref, 0, sizeof(*pref));
+ }
+ }
+ }
+ return errors;
+}
+
+static int g723_len(unsigned char buf)
+{
+ enum frame_type type = buf & TYPE_MASK;
+
+ switch(type) {
+ case TYPE_DONTSEND:
+ return 0;
+ break;
+ case TYPE_SILENCE:
+ return 4;
+ break;
+ case TYPE_HIGH:
+ return 24;
+ break;
+ case TYPE_LOW:
+ return 20;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Badly encoded frame (%d)\n", type);
+ }
+ return -1;
+}
+
+static int g723_samples(unsigned char *buf, int maxlen)
+{
+ int pos = 0;
+ int samples = 0;
+ int res;
+ while(pos < maxlen) {
+ res = g723_len(buf[pos]);
+ if (res <= 0)
+ break;
+ samples += 240;
+ pos += res;
+ }
+ return samples;
+}
+
+static unsigned char get_n_bits_at(unsigned char *data, int n, int bit)
+{
+ int byte = bit / 8; /* byte containing first bit */
+ int rem = 8 - (bit % 8); /* remaining bits in first byte */
+ unsigned char ret = 0;
+
+ if (n <= 0 || n > 8)
+ return 0;
+
+ if (rem < n) {
+ ret = (data[byte] << (n - rem));
+ ret |= (data[byte + 1] >> (8 - n + rem));
+ } else {
+ ret = (data[byte] >> (rem - n));
+ }
+
+ return (ret & (0xff >> (8 - n)));
+}
+
+static int speex_get_wb_sz_at(unsigned char *data, int len, int bit)
+{
+ static int SpeexWBSubModeSz[] = {
+ 0, 36, 112, 192,
+ 352, 0, 0, 0 };
+ int off = bit;
+ unsigned char c;
+
+ /* skip up to two wideband frames */
+ if (((len * 8 - off) >= 5) &&
+ get_n_bits_at(data, 1, off)) {
+ c = get_n_bits_at(data, 3, off + 1);
+ off += SpeexWBSubModeSz[c];
+
+ if (((len * 8 - off) >= 5) &&
+ get_n_bits_at(data, 1, off)) {
+ c = get_n_bits_at(data, 3, off + 1);
+ off += SpeexWBSubModeSz[c];
+
+ if (((len * 8 - off) >= 5) &&
+ get_n_bits_at(data, 1, off)) {
+ ast_log(LOG_WARNING, "Encountered corrupt speex frame; too many wideband frames in a row.\n");
+ return -1;
+ }
+ }
+
+ }
+ return off - bit;
+}
+
+static int speex_samples(unsigned char *data, int len)
+{
+ static int SpeexSubModeSz[] = {
+ 5, 43, 119, 160,
+ 220, 300, 364, 492,
+ 79, 0, 0, 0,
+ 0, 0, 0, 0 };
+ static int SpeexInBandSz[] = {
+ 1, 1, 4, 4,
+ 4, 4, 4, 4,
+ 8, 8, 16, 16,
+ 32, 32, 64, 64 };
+ int bit = 0;
+ int cnt = 0;
+ int off;
+ unsigned char c;
+
+ while ((len * 8 - bit) >= 5) {
+ /* skip wideband frames */
+ off = speex_get_wb_sz_at(data, len, bit);
+ if (off < 0) {
+ ast_log(LOG_WARNING, "Had error while reading wideband frames for speex samples\n");
+ break;
+ }
+ bit += off;
+
+ if ((len * 8 - bit) < 5) {
+ ast_log(LOG_WARNING, "Not enough bits remaining after wide band for speex samples.\n");
+ break;
+ }
+
+ /* get control bits */
+ c = get_n_bits_at(data, 5, bit);
+ bit += 5;
+
+ if (c == 15) {
+ /* terminator */
+ break;
+ } else if (c == 14) {
+ /* in-band signal; next 4 bits contain signal id */
+ c = get_n_bits_at(data, 4, bit);
+ bit += 4;
+ bit += SpeexInBandSz[c];
+ } else if (c == 13) {
+ /* user in-band; next 5 bits contain msg len */
+ c = get_n_bits_at(data, 5, bit);
+ bit += 5;
+ bit += c * 8;
+ } else if (c > 8) {
+ /* unknown */
+ break;
+ } else {
+ /* skip number bits for submode (less the 5 control bits) */
+ bit += SpeexSubModeSz[c] - 5;
+ cnt += 160; /* new frame */
+ }
+ }
+ return cnt;
+}
+
+int ast_codec_get_samples(struct ast_frame *f)
+{
+ int samples=0;
+ switch(f->subclass) {
+ case AST_FORMAT_SPEEX:
+ samples = speex_samples(f->data, f->datalen);
+ break;
+ case AST_FORMAT_G723_1:
+ samples = g723_samples(f->data, f->datalen);
+ break;
+ case AST_FORMAT_ILBC:
+ samples = 240 * (f->datalen / 50);
+ break;
+ case AST_FORMAT_GSM:
+ samples = 160 * (f->datalen / 33);
+ break;
+ case AST_FORMAT_G729A:
+ samples = f->datalen * 8;
+ break;
+ case AST_FORMAT_SLINEAR:
+ case AST_FORMAT_SLINEAR16:
+ samples = f->datalen / 2;
+ break;
+ case AST_FORMAT_LPC10:
+ /* assumes that the RTP packet contains one LPC10 frame */
+ samples = 22 * 8;
+ samples += (((char *)(f->data))[7] & 0x1) * 8;
+ break;
+ case AST_FORMAT_ULAW:
+ case AST_FORMAT_ALAW:
+ samples = f->datalen;
+ break;
+ case AST_FORMAT_G722:
+ case AST_FORMAT_ADPCM:
+ case AST_FORMAT_G726:
+ case AST_FORMAT_G726_AAL2:
+ samples = f->datalen * 2;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(f->subclass));
+ }
+ return samples;
+}
+
+int ast_codec_get_len(int format, int samples)
+{
+ int len = 0;
+
+ /* XXX Still need speex, g723, and lpc10 XXX */
+ switch(format) {
+ case AST_FORMAT_G723_1:
+ len = (samples / 240) * 20;
+ break;
+ case AST_FORMAT_ILBC:
+ len = (samples / 240) * 50;
+ break;
+ case AST_FORMAT_GSM:
+ len = (samples / 160) * 33;
+ break;
+ case AST_FORMAT_G729A:
+ len = samples / 8;
+ break;
+ case AST_FORMAT_SLINEAR:
+ case AST_FORMAT_SLINEAR16:
+ len = samples * 2;
+ break;
+ case AST_FORMAT_ULAW:
+ case AST_FORMAT_ALAW:
+ len = samples;
+ break;
+ case AST_FORMAT_G722:
+ case AST_FORMAT_ADPCM:
+ case AST_FORMAT_G726:
+ case AST_FORMAT_G726_AAL2:
+ len = samples / 2;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unable to calculate sample length for format %s\n", ast_getformatname(format));
+ }
+
+ return len;
+}
+
+int ast_frame_adjust_volume(struct ast_frame *f, int adjustment)
+{
+ int count;
+ short *fdata = f->data;
+ short adjust_value = abs(adjustment);
+
+ if ((f->frametype != AST_FRAME_VOICE) || (f->subclass != AST_FORMAT_SLINEAR))
+ return -1;
+
+ if (!adjustment)
+ return 0;
+
+ for (count = 0; count < f->samples; count++) {
+ if (adjustment > 0) {
+ ast_slinear_saturated_multiply(&fdata[count], &adjust_value);
+ } else if (adjustment < 0) {
+ ast_slinear_saturated_divide(&fdata[count], &adjust_value);
+ }
+ }
+
+ return 0;
+}
+
+int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
+{
+ int count;
+ short *data1, *data2;
+
+ if ((f1->frametype != AST_FRAME_VOICE) || (f1->subclass != AST_FORMAT_SLINEAR))
+ return -1;
+
+ if ((f2->frametype != AST_FRAME_VOICE) || (f2->subclass != AST_FORMAT_SLINEAR))
+ return -1;
+
+ if (f1->samples != f2->samples)
+ return -1;
+
+ for (count = 0, data1 = f1->data, data2 = f2->data;
+ count < f1->samples;
+ count++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+
+ return 0;
+}
diff --git a/trunk/main/fskmodem.c b/trunk/main/fskmodem.c
new file mode 100644
index 000000000..e59024834
--- /dev/null
+++ b/trunk/main/fskmodem.c
@@ -0,0 +1,360 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ * 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 FSK Modulator/Demodulator
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg Includes code and algorithms from the Zapata library.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/fskmodem.h"
+
+#define NBW 2
+#define BWLIST {75,800}
+#define NF 6
+#define FLIST {1400,1800,1200,2200,1300,2100}
+
+#define STATE_SEARCH_STARTBIT 0
+#define STATE_SEARCH_STARTBIT2 1
+#define STATE_SEARCH_STARTBIT3 2
+#define STATE_GET_BYTE 3
+
+static inline int iget_sample(short **buffer, int *len)
+{
+ int retval;
+ retval = (int) **buffer;
+ (*buffer)++;
+ (*len)--;
+ return retval;
+}
+
+#define IGET_SAMPLE iget_sample(&buffer, len)
+/*! \brief Coefficients for input filters
+ * Coefficients table, generated by program "mkfilter"
+ * mkfilter is part of the zapatatelephony.org distribution
+ * Format: coef[IDX_FREC][IDX_BW][IDX_COEF]
+ * IDX_COEF = 0 => 1/GAIN
+ * IDX_COEF = 1-6 => Coefficientes y[n]
+*/
+static double coef_in[NF][NBW][8]={
+ { { 1.8229206611e-04,-7.8997325866e-01,2.2401819940e+00,-4.6751353581e+00,5.5080745712e+00,-5.0571565772e+00,2.6215820004e+00,0.0000000000e+00,
+ }, { 9.8532175289e-02,-5.6297236492e-02,3.3146713415e-01,-9.2239200436e-01,1.4844365184e+00,-2.0183258642e+00,2.0074154497e+00,0.0000000000e+00,
+ }, }, { { 1.8229206610e-04,-7.8997325866e-01,7.7191410839e-01,-2.8075643964e+00,1.6948618347e+00,-3.0367273700e+00,9.0333559408e-01,0.0000000000e+00,
+ }, { 9.8531161839e-02,-5.6297236492e-02,1.1421579050e-01,-4.8122536483e-01,4.0121072432e-01,-7.4834487567e-01,6.9170822332e-01,0.0000000000e+00,
+ }, }, { { 1.8229206611e-04,-7.8997325866e-01,2.9003821430e+00,-6.1082779024e+00,7.7169345751e+00,-6.6075999680e+00,3.3941838836e+00,0.0000000000e+00,
+ }, { 9.8539686961e-02,-5.6297236492e-02,4.2915323820e-01,-1.2609358633e+00,2.2399213250e+00,-2.9928879142e+00,2.5990173742e+00,0.0000000000e+00,
+ }, }, { { 1.8229206610e-04,-7.8997325866e-01,-7.7191410839e-01,-2.8075643964e+00,-1.6948618347e+00,-3.0367273700e+00,-9.0333559408e-01,0.0000000000e+00,
+ }, { 9.8531161839e-02,-5.6297236492e-02,-1.1421579050e-01,-4.8122536483e-01,-4.0121072432e-01,-7.4834487567e-01,-6.9170822332e-01,0.0000000000e+00,
+ }, }, { { 1.8229206611e-04,-7.8997325866e-01,2.5782298908e+00,-5.3629717478e+00,6.5890882172e+00,-5.8012914776e+00,3.0171839130e+00,0.0000000000e+00,
+ }, { 9.8534230718e-02,-5.6297236492e-02,3.8148618075e-01,-1.0848760410e+00,1.8441165168e+00,-2.4860666655e+00,2.3103384142e+00,0.0000000000e+00,
+ }, }, { { 1.8229206610e-04,-7.8997325866e-01,-3.8715051001e-01,-2.6192408538e+00,-8.3977994034e-01,-2.8329897913e+00,-4.5306444352e-01,0.0000000000e+00,
+ }, { 9.8531160936e-02,-5.6297236492e-02,-5.7284484199e-02,-4.3673866734e-01,-1.9564766257e-01,-6.2028156584e-01,-3.4692356122e-01,0.0000000000e+00,
+ }, },
+};
+
+/*! \brief Coefficients for output filter
+ * Coefficients table, generated by program "mkfilter"
+ * Format: coef[IDX_BW][IDX_COEF]
+ * IDX_COEF = 0 => 1/GAIN
+ * IDX_COEF = 1-6 => Coefficientes y[n]
+*/
+static double coef_out[NBW][8]={
+ { 1.3868644653e-08,-6.3283665042e-01,4.0895057217e+00,-1.1020074592e+01,1.5850766191e+01,-1.2835109292e+01,5.5477477340e+00,0.0000000000e+00,
+ }, { 3.1262119724e-03,-7.8390522307e-03,8.5209627801e-02,-4.0804129163e-01,1.1157139955e+00,-1.8767603680e+00,1.8916395224e+00,0.0000000000e+00
+ },
+};
+
+/*! Integer Pass Band demodulator filter */
+static inline int ibpdfilter(struct filter_struct * fs, int in)
+{
+ int i,j;
+ int s;
+ int64_t s_interim;
+
+ /* integer filter */
+ s = in * fs->icoefs[0];
+ fs->ixv[(fs->ip+6)&7] = s;
+
+ s= (fs->ixv[fs->ip] + fs->ixv[(fs->ip+6)&7]) +
+ 6 * (fs->ixv[(fs->ip+1)&7] + fs->ixv[(fs->ip+5)&7]) +
+ 15 * (fs->ixv[(fs->ip+2)&7] + fs->ixv[(fs->ip+4)&7]) +
+ 20 * fs->ixv[(fs->ip+3)&7];
+
+ for (i=1, j=fs->ip; i < 7; i++,j++) {
+ /* Promote operation to 64 bit to prevent overflow that occurred in 32 bit) */
+ s_interim = (int64_t)(fs->iyv[j & 7]) *
+ (int64_t)(fs->icoefs[i]) /
+ (int64_t)(1024);
+ s += (int) s_interim;
+ }
+ fs->iyv[ j & 7] = s;
+ fs->ip++;
+ fs->ip &= 7;
+ return s;
+}
+
+/*! Integer Band Pass filter */
+static inline int ibpfilter(struct filter_struct * fs, int in)
+{
+ int i, j;
+ int s;
+ int64_t s_interim;
+
+ /* integer filter */
+ s = in * fs->icoefs[0] / 256;
+ fs->ixv[(fs->ip+6) & 7] = s;
+
+ s = (fs->ixv[(fs->ip+6) & 7] - fs->ixv[fs->ip])
+ + 3 * (fs->ixv[(fs->ip+2) & 7] - fs->ixv[(fs->ip+4) & 7]);
+
+ for (i=1,j=fs->ip; i < 7; i++,j++) {
+ s_interim = (int64_t)(fs->iyv[j&7]) *
+ (int64_t)(fs->icoefs[i]) /
+ (int64_t)(256);
+ s+= (int) s_interim;
+ }
+ fs->iyv[j&7]=s;
+ fs->ip++; fs->ip &= 7;
+ return s;
+}
+
+static inline int idemodulator(fsk_data *fskd, int *retval, int x)
+{
+ int is, im, id;
+ int ilin2;
+
+ is = ibpfilter(&fskd->space_filter, x);
+ im = ibpfilter(&fskd->mark_filter, x);
+
+ ilin2 = ((im * im) - (is * is))/(256 * 256);
+
+ id = ibpdfilter(&fskd->demod_filter, ilin2);
+
+ *retval = id;
+ return 0;
+}
+
+static int get_bit_raw(fsk_data *fskd, short *buffer, int *len)
+{
+ /* This function implements a DPLL to synchronize with the bits */
+ int f;
+
+ int ix;
+ /* PLL coeffs are set up in callerid_new */
+ for (f = 0;;){
+ if (idemodulator(fskd, &ix, IGET_SAMPLE)) return(-1);
+ if ((ix * fskd->xi0)<0) { /* Transicion */
+ if (!f) {
+ if (fskd->icont<(fskd->pllispb2))
+ fskd->icont+=fskd->pllids;
+ else
+ fskd->icont-=fskd->pllids;
+ f = 1;
+ }
+ }
+ fskd->xi0=ix;
+ fskd->icont+=32;
+ if (fskd->icont>fskd->pllispb) {
+ fskd->icont-=fskd->pllispb;
+ break;
+ }
+ }
+ f=(ix>0)?0x80:0;
+ return f;
+}
+
+int fskmodem_init(fsk_data *fskd)
+{
+ int i;
+
+ fskd->space_filter.ip = 0;
+ fskd->mark_filter.ip = 0;
+ fskd->demod_filter.ip = 0;
+
+ for ( i = 0 ; i < 7 ; i++ ) {
+ fskd->space_filter.icoefs[i] =
+ coef_in[fskd->f_space_idx][fskd->bw][i] * 256;
+ fskd->space_filter.ixv[i] = 0;;
+ fskd->space_filter.iyv[i] = 0;;
+
+ fskd->mark_filter.icoefs[i] =
+ coef_in[fskd->f_mark_idx][fskd->bw][i] * 256;
+ fskd->mark_filter.ixv[i] = 0;;
+ fskd->mark_filter.iyv[i] = 0;;
+
+ fskd->demod_filter.icoefs[i] =
+ coef_out[fskd->bw][i] * 1024;
+ fskd->demod_filter.ixv[i] = 0;;
+ fskd->demod_filter.iyv[i] = 0;;
+ }
+ return 0;
+}
+
+int fsk_serial(fsk_data *fskd, short *buffer, int *len, int *outbyte)
+{
+ int a;
+ int i, j, n1, r;
+ int samples = 0;
+ int olen;
+ int beginlen = *len;
+ int beginlenx;
+
+ switch (fskd->state) {
+ /* Pick up where we left off */
+ case STATE_SEARCH_STARTBIT2:
+ goto search_startbit2;
+ case STATE_SEARCH_STARTBIT3:
+ goto search_startbit3;
+ case STATE_GET_BYTE:
+ goto getbyte;
+ }
+ /* We await for start bit */
+ do {
+ /* this was jesus's nice, reasonable, working (at least with RTTY) code
+ to look for the beginning of the start bit. Unfortunately, since TTY/TDD's
+ just start sending a start bit with nothing preceding it at the beginning
+ of a transmission (what a LOSING design), we cant do it this elegantly */
+ /* NOT USED
+ if (demodulator(zap,&x1))
+ return -1;
+ for(;;) {
+ if (demodulator(zap,&x2))
+ return -1;
+ if (x1>0 && x2<0) break;
+ x1=x2;
+ }
+ */
+ /* this is now the imprecise, losing, but functional code to detect the
+ beginning of a start bit in the TDD sceanario. It just looks for sufficient
+ level to maybe, perhaps, guess, maybe that its maybe the beginning of
+ a start bit, perhaps. This whole thing stinks! */
+ beginlenx=beginlen; /* just to avoid unused war warnings */
+ if (idemodulator(fskd, &fskd->xi1, IGET_SAMPLE))
+ return -1;
+ samples++;
+ for(;;) {
+search_startbit2:
+ if (*len <= 0) {
+ fskd->state = STATE_SEARCH_STARTBIT2;
+ return 0;
+ }
+ samples++;
+ if (idemodulator(fskd,&fskd->xi2,IGET_SAMPLE))
+ return -1;
+#if 0
+ printf("xi2 = %d ", fskd->xi2);
+#endif
+ if (fskd->xi2 < 512)
+ break;
+ }
+search_startbit3:
+ /* We await for 0.5 bits before using DPLL */
+ i=fskd->ispb/2;
+ if (*len < i) {
+ fskd->state = STATE_SEARCH_STARTBIT3;
+ return 0;
+ }
+ for (; i>0; i--) {
+ if (idemodulator(fskd, &fskd->xi1, IGET_SAMPLE))
+ return(-1);
+#if 0
+ printf("xi1 = %d ", fskd->xi1);
+#endif
+ samples++;
+ }
+
+ /* x1 must be negative (start bit confirmation) */
+
+ } while (fskd->xi1>0);
+ fskd->state = STATE_GET_BYTE;
+
+getbyte:
+
+ /* Need at least 80 samples (for 1200) or
+ 1320 (for 45.5) to be sure we'll have a byte */
+ if (fskd->nbit < 8) {
+ if (*len < 1320)
+ return 0;
+ } else {
+ if (*len < 80)
+ return 0;
+ }
+
+ /* Now we read the data bits */
+ j = fskd->nbit;
+ for (a = n1 = 0; j; j--) {
+ olen = *len;
+ i = get_bit_raw(fskd, buffer, len);
+ buffer += (olen - *len);
+ if (i == -1)
+ return -1;
+ if (i)
+ n1++;
+ a >>= 1;
+ a |= i;
+ }
+ j = 8 - fskd->nbit;
+ a >>= j;
+
+ /* We read parity bit (if exists) and check parity */
+ if (fskd->parity) {
+ olen = *len;
+ i = get_bit_raw(fskd, buffer, len);
+ buffer += (olen - *len);
+ if (i == -1)
+ return -1;
+ if (i)
+ n1++;
+ if (fskd->parity == 1) { /* parity=1 (even) */
+ if (n1 & 1)
+ a |= 0x100; /* error */
+ } else { /* parity=2 (odd) */
+ if (!(n1 & 1))
+ a |= 0x100; /* error */
+ }
+ }
+
+ /* We read STOP bits. All of them must be 1 */
+
+ for (j = fskd->instop; j; j--) {
+ r = get_bit_raw(fskd, buffer, len);
+ if (r == -1)
+ return -1;
+ if (!r)
+ a |= 0x200;
+ }
+
+ /* And finally we return
+ * Bit 8 : Parity error
+ * Bit 9 : Framming error
+ */
+
+ *outbyte = a;
+ fskd->state = STATE_SEARCH_STARTBIT;
+ return 1;
+}
diff --git a/trunk/main/global_datastores.c b/trunk/main/global_datastores.c
new file mode 100644
index 000000000..9b87b2cb4
--- /dev/null
+++ b/trunk/main/global_datastores.c
@@ -0,0 +1,83 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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 globally-accessible datastore information and callbacks
+ *
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/global_datastores.h"
+#include "asterisk/linkedlists.h"
+
+static void dialed_interface_destroy(void *data)
+{
+ struct ast_dialed_interface *di = NULL;
+ AST_LIST_HEAD(, ast_dialed_interface) *dialed_interface_list = data;
+
+ if (!dialed_interface_list)
+ return;
+
+ AST_LIST_LOCK(dialed_interface_list);
+ while ((di = AST_LIST_REMOVE_HEAD(dialed_interface_list, list)))
+ ast_free(di);
+ AST_LIST_UNLOCK(dialed_interface_list);
+
+ AST_LIST_HEAD_DESTROY(dialed_interface_list);
+ ast_free(dialed_interface_list);
+}
+
+static void *dialed_interface_duplicate(void *data)
+{
+ struct ast_dialed_interface *di = NULL;
+ AST_LIST_HEAD(, ast_dialed_interface) *old_list;
+ AST_LIST_HEAD(, ast_dialed_interface) *new_list = NULL;
+
+ if(!(old_list = data))
+ return NULL;
+
+ if(!(new_list = ast_calloc(1, sizeof(*new_list))))
+ return NULL;
+
+ AST_LIST_HEAD_INIT(new_list);
+ AST_LIST_LOCK(old_list);
+ AST_LIST_TRAVERSE(old_list, di, list) {
+ struct ast_dialed_interface *di2 = ast_calloc(1, sizeof(*di2) + strlen(di->interface));
+ if(!di2) {
+ AST_LIST_UNLOCK(old_list);
+ dialed_interface_destroy(new_list);
+ return NULL;
+ }
+ strcpy(di2->interface, di->interface);
+ AST_LIST_INSERT_TAIL(new_list, di2, list);
+ }
+ AST_LIST_UNLOCK(old_list);
+
+ return new_list;
+}
+
+const struct ast_datastore_info dialed_interface_info = {
+ .type ="dialed-interface",
+ .destroy = dialed_interface_destroy,
+ .duplicate = dialed_interface_duplicate,
+};
diff --git a/trunk/main/hashtab.c b/trunk/main/hashtab.c
new file mode 100644
index 000000000..82a28ee10
--- /dev/null
+++ b/trunk/main/hashtab.c
@@ -0,0 +1,805 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Steve Murphy <murf@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 code to implement generic hash tables
+ *
+ * \author Steve Murphy <murf@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision")
+
+#include <ctype.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/hashtab.h"
+
+static void ast_hashtab_resize( struct ast_hashtab *tab);
+static void *ast_hashtab_lookup_internal(struct ast_hashtab *tab, const void *obj, unsigned int h);
+
+/* some standard, default routines for general use */
+
+int ast_hashtab_compare_strings(const void *a, const void *b)
+{
+ return strcmp((char*)a,(char*)b);
+}
+
+int ast_hashtab_compare_strings_nocase(const void *a, const void *b)
+{
+ return strcasecmp((const char*)a,(const char*)b);
+}
+
+int ast_hashtab_compare_ints(const void *a, const void *b)
+{
+ int ai = *((int *) a);
+ int bi = *((int *) b);
+
+ if (ai < bi)
+ return -1;
+
+ return !(ai == bi);
+}
+
+int ast_hashtab_compare_shorts(const void *a, const void *b)
+{
+ short as = *((short *) a);
+ short bs = *((short *) b);
+
+ if (as < bs)
+ return -1;
+
+ return !(as == bs);
+}
+
+int ast_hashtab_resize_java(struct ast_hashtab *tab)
+{
+ double loadfactor = (double) tab->hash_tab_elements / (double) tab->hash_tab_size;
+
+ return (loadfactor > 0.75);
+}
+
+int ast_hashtab_resize_tight(struct ast_hashtab *tab)
+{
+ return (tab->hash_tab_elements > tab->hash_tab_size); /* this is quicker than division */
+}
+
+int ast_hashtab_resize_none(struct ast_hashtab *tab) /* always return 0 -- no resizing */
+{
+ return 0;
+}
+
+int ast_is_prime(int num)
+{
+ int tnum, limit;
+
+ if (!(num & 0x1)) /* even number -- not prime */
+ return 0;
+
+ /* Loop through ODD numbers starting with 3 */
+
+ tnum = 3;
+ limit = num;
+ while (tnum < limit) {
+ if (!(num % tnum))
+ return 0;
+
+ /* really, we only need to check sqrt(num) numbers */
+ limit = num / tnum;
+
+ /* we only check odd numbers */
+ tnum = tnum + 2;
+ }
+
+ /* if we made it through the loop, the number is a prime */
+
+ return 1;
+}
+
+int ast_hashtab_newsize_java(struct ast_hashtab *tab)
+{
+ int i = (tab->hash_tab_size << 1); /* multiply by two */
+
+ while (!ast_is_prime(i))
+ i++;
+
+ return i;
+}
+
+int ast_hashtab_newsize_tight(struct ast_hashtab *tab)
+{
+ int x = (tab->hash_tab_size << 1);
+ int i = (tab->hash_tab_size + x);
+
+ while (!ast_is_prime(i))
+ i++;
+
+ return i;
+}
+
+int ast_hashtab_newsize_none(struct ast_hashtab *tab) /* always return current size -- no resizing */
+{
+ return tab->hash_tab_size;
+}
+
+unsigned int ast_hashtab_hash_string(const void *obj)
+{
+ unsigned char *str = (unsigned char *) obj;
+ unsigned int total;
+
+ for (total = 0; *str; str++)
+ {
+ unsigned int tmp = total;
+ total <<= 1; /* multiply by 2 */
+ total += tmp; /* multiply by 3 */
+ total <<= 2; /* multiply by 12 */
+ total += tmp; /* multiply by 13 */
+
+ total += ((unsigned int)(*str));
+ }
+ return total;
+}
+
+unsigned int ast_hashtab_hash_string_sax(const void *obj) /* from Josh */
+{
+ unsigned char *str = (unsigned char *) obj;
+ unsigned int total = 0, c = 0;
+
+ while ((c = *str++))
+ total ^= (total << 5) + (total >> 2) + (total << 10) + c;
+
+ return total;
+}
+
+unsigned int ast_hashtab_hash_string_nocase(const void *obj)
+{
+ unsigned char *str = (unsigned char*)obj;
+ unsigned int total;
+
+ for (total = 0; *str; str++) {
+ unsigned int tmp = total;
+ unsigned int charval = toupper(*str);
+
+ /* hopefully, the following is faster than multiplication by 7 */
+ /* why do I go to this bother? A good compiler will do this
+ anyway, if I say total *= 13 */
+ /* BTW, tried *= 7, and it doesn't do as well in spreading things around! */
+ total <<= 1; /* multiply by 2 */
+ total += tmp; /* multiply by 3 */
+ total <<= 2; /* multiply by 12 */
+ total += tmp; /* multiply by 13 */
+
+ total += (charval);
+ }
+
+ return total;
+}
+
+unsigned int ast_hashtab_hash_int(const int x)
+{
+ return x;
+}
+
+unsigned int ast_hashtab_hash_short(const short x)
+{
+ /* hmmmm.... modulus is best < 65535 !! */
+ return x;
+}
+
+struct ast_hashtab *ast_hashtab_create(int initial_buckets,
+ int (*compare)(const void *a, const void *b),
+ int (*resize)(struct ast_hashtab *),
+ int (*newsize)(struct ast_hashtab *tab),
+ unsigned int (*hash)(const void *obj),
+ int do_locking)
+{
+ struct ast_hashtab *ht;
+
+ if (!(ht = ast_calloc(1, sizeof(*ht))))
+ return NULL;
+
+ while (!ast_is_prime(initial_buckets)) /* make sure this is prime */
+ initial_buckets++;
+
+ if (!(ht->array = ast_calloc(initial_buckets, sizeof(*(ht->array))))) {
+ free(ht);
+ return NULL;
+ }
+
+ ht->hash_tab_size = initial_buckets;
+ ht->compare = compare;
+ ht->resize = resize;
+ ht->newsize = newsize;
+ ht->hash = hash;
+ ht->do_locking = do_locking;
+
+ if (do_locking)
+ ast_rwlock_init(&ht->lock);
+
+ if (!ht->resize)
+ ht->resize = ast_hashtab_resize_java;
+
+ if (!ht->newsize)
+ ht->newsize = ast_hashtab_newsize_java;
+
+ return ht;
+}
+
+struct ast_hashtab *ast_hashtab_dup(struct ast_hashtab *tab, void *(*obj_dup_func)(const void *obj))
+{
+ struct ast_hashtab *ht;
+ unsigned int i;
+
+ if (!(ht = ast_calloc(1, sizeof(*ht))))
+ return NULL;
+
+ if (!(ht->array = ast_calloc(tab->hash_tab_size, sizeof(*(ht->array))))) {
+ free(ht);
+ return NULL;
+ }
+
+ ht->hash_tab_size = tab->hash_tab_size;
+ ht->compare = tab->compare;
+ ht->resize = tab->resize;
+ ht->newsize = tab->newsize;
+ ht->hash = tab->hash;
+ ht->do_locking = tab->do_locking;
+
+ if (ht->do_locking)
+ ast_rwlock_init(&ht->lock);
+
+ /* now, dup the objects in the buckets and get them into the table */
+ /* the fast way is to use the existing array index, and not have to hash
+ the objects again */
+ for (i = 0; i < ht->hash_tab_size; i++) {
+ struct ast_hashtab_bucket *b = tab->array[i];
+ while (b) {
+ void *newobj = (*obj_dup_func)(b->object);
+ if (newobj)
+ ast_hashtab_insert_immediate_bucket(ht, newobj, i);
+ b = b->next;
+ }
+ }
+
+ return ht;
+}
+
+static void tlist_del_item(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
+{
+ /* item had better be in the list! or suffer the weirdness that occurs, later! */
+ if (*head == item) { /* first item in the list */
+ *head = item->tnext;
+ if (item->tnext)
+ item->tnext->tprev = NULL;
+ } else {
+ /* short circuit stuff */
+ item->tprev->tnext = item->tnext;
+ if (item->tnext)
+ item->tnext->tprev = item->tprev;
+ }
+}
+
+static void tlist_add_head(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
+{
+ if (*head) {
+ item->tnext = *head;
+ item->tprev = NULL;
+ (*head)->tprev = item;
+ *head = item;
+ } else {
+ /* the list is empty */
+ *head = item;
+ item->tprev = NULL;
+ item->tnext = NULL;
+ }
+}
+
+/* user-controlled hashtab locking. Create a hashtab without locking, then call the
+ following locking routines yourself to lock the table between threads. */
+
+void ast_hashtab_wrlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_wrlock(&tab->lock);
+}
+
+void ast_hashtab_rdlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_rdlock(&tab->lock);
+}
+
+void ast_hashtab_initlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_init(&tab->lock);
+}
+
+void ast_hashtab_destroylock(struct ast_hashtab *tab)
+{
+ ast_rwlock_destroy(&tab->lock);
+}
+
+void ast_hashtab_unlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_unlock(&tab->lock);
+}
+
+void ast_hashtab_destroy( struct ast_hashtab *tab, void (*objdestroyfunc)(void *obj))
+{
+ /* this func will free the hash table and all its memory. It
+ doesn't touch the objects stored in it */
+ if (tab) {
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ if (tab->array) {
+ /* go thru and destroy the buckets */
+ struct ast_hashtab_bucket *t;
+ int i;
+
+ while (tab->tlist) {
+ t = tab->tlist;
+ if (t->object && objdestroyfunc)
+ (*objdestroyfunc)((void *) t->object); /* I cast this because I'm not going to MOD it, I'm going to DESTROY it */
+
+ tlist_del_item(&(tab->tlist), tab->tlist);
+ free(t);
+ }
+
+ for (i = 0; i < tab->hash_tab_size; i++)
+ tab->array[i] = NULL; /* not totally necc., but best to destroy old ptrs */
+
+ free(tab->array);
+ }
+ if (tab->do_locking) {
+ ast_rwlock_unlock(&tab->lock);
+ ast_rwlock_destroy(&tab->lock);
+ }
+ free(tab);
+ }
+}
+
+int ast_hashtab_insert_immediate(struct ast_hashtab *tab, const void *obj)
+{
+ unsigned int h;
+ int res=0;
+
+ if (!tab || !obj)
+ return res;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+
+ res = ast_hashtab_insert_immediate_bucket(tab,obj,h);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return res;
+}
+
+int ast_hashtab_insert_immediate_bucket(struct ast_hashtab *tab, const void *obj, unsigned int h)
+{
+ int c;
+ struct ast_hashtab_bucket *b;
+
+ if (!tab || !obj)
+ return 0;
+
+ for (c = 0, b = tab->array[h]; b; b= b->next)
+ c++;
+
+ if (c + 1 > tab->largest_bucket_size)
+ tab->largest_bucket_size = c + 1;
+
+ if (!(b = ast_calloc(1, sizeof(*b))))
+ return 0;
+
+ b->object = obj;
+ b->next = tab->array[h];
+ tab->array[h] = b;
+
+ if (b->next)
+ b->next->prev = b;
+
+ tlist_add_head(&(tab->tlist), b);
+ tab->hash_tab_elements++;
+
+ if ((*tab->resize)(tab))
+ ast_hashtab_resize(tab);
+
+ return 1;
+}
+
+int ast_hashtab_insert_safe(struct ast_hashtab *tab, const void *obj)
+{
+ /* check to see if the element is already there; insert only if
+ it is not there. */
+ /* will force a resize if the resize func returns 1 */
+ /* returns 1 on success, 0 if there's a problem, or it's already there. */
+ unsigned int bucket = 0;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ if (!ast_hashtab_lookup_bucket(tab, obj, &bucket)) {
+ int ret2 = ast_hashtab_insert_immediate_bucket(tab, obj, bucket);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return ret2;
+ }
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return 0;
+}
+
+void *ast_hashtab_lookup(struct ast_hashtab *tab, const void *obj)
+{
+ /* lookup this object in the hash table. return a ptr if found, or NULL if not */
+ unsigned int h;
+ void *ret;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+
+ ret = ast_hashtab_lookup_internal(tab,obj,h);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return ret;
+}
+
+
+void *ast_hashtab_lookup_with_hash(struct ast_hashtab *tab, const void *obj, unsigned int hashval)
+{
+ /* lookup this object in the hash table. return a ptr if found, or NULL if not */
+ unsigned int h;
+ void *ret;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+
+ h = hashval % tab->hash_tab_size;
+
+ ret = ast_hashtab_lookup_internal(tab,obj,h);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return ret;
+}
+
+void *ast_hashtab_lookup_bucket(struct ast_hashtab *tab, const void *obj, unsigned int *bucket)
+{
+ /* lookup this object in the hash table. return a ptr if found, or NULL if not */
+ unsigned int h;
+ void *ret;
+
+ if (!tab || !obj)
+ return 0;
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+
+ ret = ast_hashtab_lookup_internal(tab,obj,h);
+
+ *bucket = h;
+
+ return ret;
+}
+
+static void *ast_hashtab_lookup_internal(struct ast_hashtab *tab, const void *obj, unsigned int h)
+{
+ struct ast_hashtab_bucket *b;
+
+ for (b = tab->array[h]; b; b = b->next) {
+ if (!(*tab->compare)(obj,b->object)) {
+ return (void*) b->object; /* I can't touch obj in this func, but the outside world is welcome to */
+ }
+ }
+
+ return NULL;
+}
+
+void ast_hashtab_get_stats( struct ast_hashtab *tab, int *biggest_bucket_size, int *resize_count, int *num_objects, int *num_buckets)
+{
+ /* returns key stats for the table */
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+ *biggest_bucket_size = tab->largest_bucket_size;
+ *resize_count = tab->resize_count;
+ *num_objects = tab->hash_tab_elements;
+ *num_buckets = tab->hash_tab_size;
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+}
+
+ /* this function returns the number of elements stored in the hashtab */
+int ast_hashtab_size( struct ast_hashtab *tab)
+{
+ return tab->hash_tab_elements;
+}
+
+ /* this function returns the size of the bucket array in the hashtab */
+int ast_hashtab_capacity( struct ast_hashtab *tab)
+{
+ return tab->hash_tab_size;
+}
+
+
+
+/* the insert operation calls this, and is wrlock'd when it does. */
+/* if you want to call it, you should set the wrlock yourself */
+
+
+static void ast_hashtab_resize( struct ast_hashtab *tab)
+{
+ /* this function is called either internally, when the resize func returns 1, or
+ externally by the user to force a resize of the hash table */
+ int newsize = (*tab->newsize)(tab), i, c;
+ unsigned int h;
+ struct ast_hashtab_bucket *b,*bn;
+
+ /* Since we keep a DLL of all the buckets in tlist,
+ all we have to do is free the array, malloc a new one,
+ and then go thru the tlist array and reassign them into
+ the bucket arrayj.
+ */
+ for (i = 0; i < tab->hash_tab_size; i++) { /* don't absolutely have to do this, but
+ why leave ptrs laying around */
+ tab->array[i] = 0; /* erase old ptrs */
+ }
+ free(tab->array);
+ if (!(tab->array = ast_calloc(newsize, sizeof(*(tab->array)))))
+ return;
+
+ /* now sort the buckets into their rightful new slots */
+ tab->resize_count++;
+ tab->hash_tab_size = newsize;
+ tab->largest_bucket_size = 0;
+
+ for (b = tab->tlist; b; b = bn) {
+ b->prev = 0;
+ bn = b->tnext;
+ h = (*tab->hash)(b->object) % tab->hash_tab_size;
+ b->next = tab->array[h];
+ if (b->next)
+ b->next->prev = b;
+ tab->array[h] = b;
+ }
+ /* recalc the largest bucket size */
+ for (i = 0; i < tab->hash_tab_size; i++) {
+ for (c = 0, b = tab->array[i]; b; b = b->next)
+ c++;
+ if (c > tab->largest_bucket_size)
+ tab->largest_bucket_size = c;
+ }
+}
+
+struct ast_hashtab_iter *ast_hashtab_start_traversal(struct ast_hashtab *tab)
+{
+ /* returns an iterator */
+ struct ast_hashtab_iter *it;
+
+ if (!(it = ast_calloc(1, sizeof(*it))))
+ return NULL;
+
+ it->next = tab->tlist;
+ it->tab = tab;
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+
+ return it;
+}
+
+/* use this function to get a write lock */
+struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *tab)
+{
+ /* returns an iterator */
+ struct ast_hashtab_iter *it;
+
+ if (!(it = ast_calloc(1, sizeof(*it))))
+ return NULL;
+
+ it->next = tab->tlist;
+ it->tab = tab;
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ return it;
+}
+
+void ast_hashtab_end_traversal(struct ast_hashtab_iter *it)
+{
+ if (it->tab->do_locking)
+ ast_rwlock_unlock(&it->tab->lock);
+ free(it);
+}
+
+void *ast_hashtab_next(struct ast_hashtab_iter *it)
+{
+ /* returns the next object in the list, advances iter one step */
+ struct ast_hashtab_bucket *retval;
+
+ if (it && it->next) { /* there's a next in the bucket list */
+ retval = it->next;
+ it->next = retval->tnext;
+ return (void *) retval->object;
+ }
+
+ return NULL;
+}
+
+static void *ast_hashtab_remove_object_internal(struct ast_hashtab *tab, struct ast_hashtab_bucket *b, int h)
+{
+ const void *obj2;
+
+ if (b->prev)
+ b->prev->next = b->next;
+ else
+ tab->array[h] = b->next;
+
+ if (b->next)
+ b->next->prev = b->prev;
+
+ tlist_del_item(&(tab->tlist), b);
+
+ obj2 = b->object;
+ b->object = b->next = (void*)2;
+ free(b); /* free up the hashbucket */
+ tab->hash_tab_elements--;
+#ifdef DEBUG
+ {
+ int c2;
+ struct ast_hashtab_bucket *b2;
+ /* do a little checking */
+ for (c2 = 0, b2 = tab->tlist; b2; b2 = b2->tnext) {
+ c2++;
+ }
+ if (c2 != tab->hash_tab_elements) {
+ printf("Hey! we didn't delete right! there are %d elements in the list, and we expected %d\n",
+ c2, tab->hash_tab_elements);
+ }
+ for (c2 = 0, b2 = tab->tlist; b2; b2 = b2->tnext) {
+ unsigned int obj3 = (unsigned long) obj2;
+ unsigned int b3 = (unsigned long) b;
+ if (b2->object == obj2)
+ printf("Hey-- you've still got a bucket pointing at ht_element %x\n", obj3);
+ if (b2->next == b)
+ printf("Hey-- you've still got a bucket with next ptr pointing to deleted bucket %x\n", b3);
+ if (b2->prev == b)
+ printf("Hey-- you've still got a bucket with prev ptr pointing to deleted bucket %x\n", b3);
+ if (b2->tprev == b)
+ printf("Hey-- you've still got a bucket with tprev ptr pointing to deleted bucket %x\n", b3);
+ if (b2->tnext == b)
+ printf("Hey-- you've still got a bucket with tnext ptr pointing to deleted bucket %x\n", b3);
+ }
+ }
+#endif
+ return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
+}
+
+void *ast_hashtab_remove_object_via_lookup(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object; removes the corresponding bucket */
+ const void *obj2;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ obj2 = ast_hashtab_remove_object_via_lookup_nolock(tab,obj);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return (void *)obj2;
+}
+
+void *ast_hashtab_remove_object_via_lookup_nolock(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object; removes the corresponding bucket */
+ unsigned int h;
+ struct ast_hashtab_bucket *b;
+
+ if (!tab || !obj)
+ return 0;
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+ for (b = tab->array[h]; b; b = b->next) {
+
+ if (!(*tab->compare)(obj, b->object)) {
+ const void *obj2;
+
+ obj2 = ast_hashtab_remove_object_internal(tab, b, h);
+
+ return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
+ }
+ }
+
+ return 0;
+}
+
+void *ast_hashtab_remove_this_object(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object by hash and then comparing pts in bucket list instead of
+ calling the compare routine; removes the bucket -- a slightly cheaper operation */
+ /* looks up the object; removes the corresponding bucket */
+ const void *obj2;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ obj2 = ast_hashtab_remove_this_object_nolock(tab,obj);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return (void *)obj2;
+}
+
+void *ast_hashtab_remove_this_object_nolock(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object by hash and then comparing pts in bucket list instead of
+ calling the compare routine; removes the bucket -- a slightly cheaper operation */
+ /* looks up the object; removes the corresponding bucket */
+ unsigned int h;
+ struct ast_hashtab_bucket *b;
+
+ if (!tab || !obj)
+ return 0;
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+ for (b = tab->array[h]; b; b = b->next) {
+
+ if (obj == b->object) {
+ const void *obj2;
+ obj2 = ast_hashtab_remove_object_internal(tab, b, h);
+
+ return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
+ }
+ }
+
+ return 0;
+}
diff --git a/trunk/main/http.c b/trunk/main/http.c
new file mode 100644
index 000000000..6e8021fa2
--- /dev/null
+++ b/trunk/main/http.c
@@ -0,0 +1,1118 @@
+/*
+ * 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 http server for AMI access
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * This program implements a tiny http server
+ * and was inspired by micro-httpd by Jef Poskanzer
+ *
+ * \ref AstHTTP - AMI over the http protocol
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/network.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <fcntl.h>
+
+#include "minimime/mm.h"
+
+#include "asterisk/cli.h"
+#include "asterisk/tcptls.h"
+#include "asterisk/http.h"
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/config.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/version.h"
+#include "asterisk/manager.h"
+
+#define MAX_PREFIX 80
+#define DEFAULT_PREFIX "/asterisk"
+
+/* See http.h for more information about the SSL implementation */
+#if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
+#define DO_SSL /* comment in/out if you want to support ssl */
+#endif
+
+static struct ast_tls_config http_tls_cfg;
+
+static void *httpd_helper_thread(void *arg);
+
+/*!
+ * we have up to two accepting threads, one for http, one for https
+ */
+static struct server_args http_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = NULL,
+ .poll_timeout = -1,
+ .name = "http server",
+ .accept_fn = server_root,
+ .worker_fn = httpd_helper_thread,
+};
+
+static struct server_args https_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = &http_tls_cfg,
+ .poll_timeout = -1,
+ .name = "https server",
+ .accept_fn = server_root,
+ .worker_fn = httpd_helper_thread,
+};
+
+static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
+
+struct ast_http_post_mapping {
+ AST_RWLIST_ENTRY(ast_http_post_mapping) entry;
+ char *from;
+ char *to;
+};
+
+static AST_RWLIST_HEAD_STATIC(post_mappings, ast_http_post_mapping);
+
+/* all valid URIs must be prepended by the string in prefix. */
+static char prefix[MAX_PREFIX];
+static int enablestatic;
+
+/*! \brief Limit the kinds of files we're willing to serve up */
+static struct {
+ const char *ext;
+ const char *mtype;
+} mimetypes[] = {
+ { "png", "image/png" },
+ { "jpg", "image/jpeg" },
+ { "js", "application/x-javascript" },
+ { "wav", "audio/x-wav" },
+ { "mp3", "audio/mpeg" },
+ { "svg", "image/svg+xml" },
+ { "svgz", "image/svg+xml" },
+ { "gif", "image/gif" },
+};
+
+struct http_uri_redirect {
+ AST_LIST_ENTRY(http_uri_redirect) entry;
+ char *dest;
+ char target[0];
+};
+
+static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
+
+static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
+{
+ int x;
+ if (ftype) {
+ for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
+ if (!strcasecmp(ftype, mimetypes[x].ext))
+ return mimetypes[x].mtype;
+ }
+ }
+ snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
+ return wkspace;
+}
+
+static struct ast_str *static_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+{
+ char *path;
+ char *ftype;
+ const char *mtype;
+ char wkspace[80];
+ struct stat st;
+ int len;
+ int fd;
+ struct timeval tv = ast_tvnow();
+ char buf[256];
+ struct ast_tm tm;
+
+ /* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
+ substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
+ if (!enablestatic || ast_strlen_zero(uri))
+ goto out403;
+ /* Disallow any funny filenames at all */
+ if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
+ goto out403;
+ if (strstr(uri, "/.."))
+ goto out403;
+
+ if ((ftype = strrchr(uri, '.')))
+ ftype++;
+ mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
+
+ /* Cap maximum length */
+ len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
+ if (len > 1024)
+ goto out403;
+
+ path = alloca(len);
+ sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
+ if (stat(path, &st))
+ goto out404;
+ if (S_ISDIR(st.st_mode))
+ goto out404;
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto out403;
+
+ ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
+ fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
+ "Server: Asterisk/%s\r\n"
+ "Date: %s\r\n"
+ "Connection: close\r\n"
+ "Cache-Control: no-cache, no-store\r\n"
+ "Content-Length: %d\r\n"
+ "Content-type: %s\r\n\r\n",
+ ast_get_version(), buf, (int) st.st_size, mtype);
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0)
+ fwrite(buf, 1, len, ser->f);
+
+ close(fd);
+ return NULL;
+
+out404:
+ *status = 404;
+ *title = ast_strdup("Not Found");
+ return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
+
+out403:
+ *status = 403;
+ *title = ast_strdup("Access Denied");
+ return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
+}
+
+
+static struct ast_str *httpstatus_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+{
+ struct ast_str *out = ast_str_create(512);
+ struct ast_variable *v;
+
+ if (out == NULL)
+ return out;
+
+ ast_str_append(&out, 0,
+ "\r\n"
+ "<title>Asterisk HTTP Status</title>\r\n"
+ "<body bgcolor=\"#ffffff\">\r\n"
+ "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
+ "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
+
+ ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
+ ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
+ ast_inet_ntoa(http_desc.oldsin.sin_addr));
+ ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
+ ntohs(http_desc.oldsin.sin_port));
+ if (http_tls_cfg.enabled)
+ ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
+ ntohs(https_desc.oldsin.sin_port));
+ ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ for (v = vars; v; v = v->next) {
+ if (strncasecmp(v->name, "cookie_", 7))
+ ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ }
+ ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ for (v = vars; v; v = v->next) {
+ if (!strncasecmp(v->name, "cookie_", 7))
+ ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ }
+ ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
+ return out;
+}
+
+static struct ast_http_uri statusuri = {
+ .callback = httpstatus_callback,
+ .description = "Asterisk HTTP General Status",
+ .uri = "httpstatus",
+ .has_subtree = 0,
+};
+
+static struct ast_http_uri staticuri = {
+ .callback = static_callback,
+ .description = "Asterisk HTTP Static Delivery",
+ .uri = "static",
+ .has_subtree = 1,
+ .static_content = 1,
+};
+
+struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
+{
+ struct ast_str *out = ast_str_create(512);
+ if (out == NULL)
+ return out;
+ ast_str_set(&out, 0,
+ "Content-type: text/html\r\n"
+ "%s"
+ "\r\n"
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+ "<html><head>\r\n"
+ "<title>%d %s</title>\r\n"
+ "</head><body>\r\n"
+ "<h1>%s</h1>\r\n"
+ "<p>%s</p>\r\n"
+ "<hr />\r\n"
+ "<address>Asterisk Server</address>\r\n"
+ "</body></html>\r\n",
+ (extra_header ? extra_header : ""), status, title, title, text);
+ return out;
+}
+
+/*! \brief
+ * Link the new uri into the list.
+ *
+ * They are sorted by length of
+ * the string, not alphabetically. Duplicate entries are not replaced,
+ * but the insertion order (using <= and not just <) makes sure that
+ * more recent insertions hide older ones.
+ * On a lookup, we just scan the list and stop at the first matching entry.
+ */
+int ast_http_uri_link(struct ast_http_uri *urih)
+{
+ struct ast_http_uri *uri;
+ int len = strlen(urih->uri);
+
+ AST_RWLIST_WRLOCK(&uris);
+
+ if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
+ AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
+ AST_RWLIST_UNLOCK(&uris);
+ return 0;
+ }
+
+ AST_RWLIST_TRAVERSE(&uris, uri, entry) {
+ if ( AST_RWLIST_NEXT(uri, entry)
+ && strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) {
+ AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
+ AST_RWLIST_UNLOCK(&uris);
+ return 0;
+ }
+ }
+
+ AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
+
+ AST_RWLIST_UNLOCK(&uris);
+
+ return 0;
+}
+
+void ast_http_uri_unlink(struct ast_http_uri *urih)
+{
+ AST_RWLIST_WRLOCK(&uris);
+ AST_RWLIST_REMOVE(&uris, urih, entry);
+ AST_RWLIST_UNLOCK(&uris);
+}
+
+/*! \note This assumes that the post_mappings list is locked */
+static struct ast_http_post_mapping *find_post_mapping(const char *uri)
+{
+ struct ast_http_post_mapping *post_map;
+
+ if (!ast_strlen_zero(prefix) && strncmp(prefix, uri, strlen(prefix))) {
+ ast_debug(1, "URI %s does not have prefix %s\n", uri, prefix);
+ return NULL;
+ }
+
+ uri += strlen(prefix);
+ if (*uri == '/')
+ uri++;
+
+ AST_RWLIST_TRAVERSE(&post_mappings, post_map, entry) {
+ if (!strcmp(uri, post_map->from))
+ return post_map;
+ }
+
+ return NULL;
+}
+
+static int get_filename(struct mm_mimepart *part, char *fn, size_t fn_len)
+{
+ const char *filename;
+
+ filename = mm_content_getdispositionparambyname(part->type, "filename");
+
+ if (ast_strlen_zero(filename))
+ return -1;
+
+ ast_copy_string(fn, filename, fn_len);
+
+ return 0;
+}
+
+static void post_raw(struct mm_mimepart *part, const char *post_dir, const char *fn)
+{
+ char filename[PATH_MAX];
+ FILE *f;
+ const char *body;
+ size_t body_len;
+
+ snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
+
+ ast_debug(1, "Posting raw data to %s\n", filename);
+
+ if (!(f = fopen(filename, "w"))) {
+ ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
+ return;
+ }
+
+ if (!(body = mm_mimepart_getbody(part, 0))) {
+ ast_debug(1, "Couldn't get the mimepart body\n");
+ fclose(f);
+ return;
+ }
+ body_len = mm_mimepart_getlength(part);
+
+ ast_debug(1, "Body length is %ld\n", (long int)body_len);
+
+ fwrite(body, 1, body_len, f);
+
+ fclose(f);
+}
+
+static struct ast_str *handle_post(struct server_instance *ser, char *uri,
+ int *status, char **title, int *contentlength, struct ast_variable *headers,
+ struct ast_variable *cookies)
+{
+ char buf;
+ FILE *f;
+ size_t res;
+ struct ast_variable *var;
+ int content_len = 0;
+ MM_CTX *ctx;
+ int mm_res, i;
+ struct ast_http_post_mapping *post_map;
+ const char *post_dir;
+ unsigned long ident = 0;
+
+ for (var = cookies; var; var = var->next) {
+ if (strcasecmp(var->name, "mansession_id"))
+ continue;
+
+ if (sscanf(var->value, "%lx", &ident) != 1) {
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
+ *status = 401;
+ *title = ast_strdup("Unauthorized");
+ return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+ }
+
+ break;
+ }
+ if (!var) {
+ *status = 401;
+ *title = ast_strdup("Unauthorized");
+ return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+ }
+
+ if (!(f = tmpfile()))
+ return NULL;
+
+ for (var = headers; var; var = var->next) {
+ if (!strcasecmp(var->name, "Content-Length")) {
+ if ((sscanf(var->value, "%u", &content_len)) != 1) {
+ ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
+ fclose(f);
+ return NULL;
+ }
+ ast_debug(1, "Got a Content-Length of %d\n", content_len);
+ } else if (!strcasecmp(var->name, "Content-Type"))
+ fprintf(f, "Content-Type: %s\r\n\r\n", var->value);
+ }
+
+ while ((res = fread(&buf, 1, 1, ser->f))) {
+ fwrite(&buf, 1, 1, f);
+ content_len--;
+ if (!content_len)
+ break;
+ }
+
+ if (fseek(f, SEEK_SET, 0)) {
+ ast_debug(1, "Failed to seek temp file back to beginning.\n");
+ fclose(f);
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&post_mappings);
+ if (!(post_map = find_post_mapping(uri))) {
+ ast_debug(1, "%s is not a valid URI for POST\n", uri);
+ AST_RWLIST_UNLOCK(&post_mappings);
+ fclose(f);
+ *status = 404;
+ *title = ast_strdup("Not Found");
+ return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
+ }
+ post_dir = ast_strdupa(post_map->to);
+ post_map = NULL;
+ AST_RWLIST_UNLOCK(&post_mappings);
+
+ ast_debug(1, "Going to post files to dir %s\n", post_dir);
+
+ if (!(ctx = mm_context_new())) {
+ fclose(f);
+ return NULL;
+ }
+
+ mm_res = mm_parse_fileptr(ctx, f, MM_PARSE_LOOSE, 0);
+ fclose(f);
+ if (mm_res == -1) {
+ ast_log(LOG_ERROR, "Error parsing MIME data\n");
+ mm_context_free(ctx);
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ mm_res = mm_context_countparts(ctx);
+ if (!mm_res) {
+ ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
+ mm_context_free(ctx);
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ if (option_debug) {
+ if (mm_context_iscomposite(ctx))
+ ast_debug(1, "Found %d MIME parts\n", mm_res - 1);
+ else
+ ast_debug(1, "We have a flat (not multi-part) message\n");
+ }
+
+ for (i = 1; i < mm_res; i++) {
+ struct mm_mimepart *part;
+ char fn[PATH_MAX];
+
+ if (!(part = mm_context_getpart(ctx, i))) {
+ ast_debug(1, "Failed to get mime part num %d\n", i);
+ continue;
+ }
+
+ if (get_filename(part, fn, sizeof(fn))) {
+ ast_debug(1, "Failed to retrieve a filename for part num %d\n", i);
+ continue;
+ }
+
+ if (!part->type) {
+ ast_debug(1, "This part has no content struct?\n");
+ continue;
+ }
+
+ /* XXX This assumes the MIME part body is not encoded! */
+ post_raw(part, post_dir, fn);
+ }
+
+ mm_context_free(ctx);
+
+ *status = 200;
+ *title = ast_strdup("OK");
+ return ast_http_error(200, "OK", NULL, "File successfully uploaded.");
+}
+
+static struct ast_str *handle_uri(struct server_instance *ser, char *uri, int *status,
+ char **title, int *contentlength, struct ast_variable **cookies,
+ unsigned int *static_content)
+{
+ char *c;
+ struct ast_str *out = NULL;
+ char *params = uri;
+ struct ast_http_uri *urih=NULL;
+ int l;
+ struct ast_variable *vars=NULL, *v, *prev = NULL;
+ struct http_uri_redirect *redirect;
+
+ strsep(&params, "?");
+ /* Extract arguments from the request and store them in variables. */
+ if (params) {
+ char *var, *val;
+
+ while ((val = strsep(&params, "&"))) {
+ var = strsep(&val, "=");
+ if (val)
+ ast_uri_decode(val);
+ else
+ val = "";
+ ast_uri_decode(var);
+ if ((v = ast_variable_new(var, val, ""))) {
+ if (vars)
+ prev->next = v;
+ else
+ vars = v;
+ prev = v;
+ }
+ }
+ }
+ /*
+ * Append the cookies to the variables (the only reason to have them
+ * at the end is to avoid another pass of the cookies list to find
+ * the tail).
+ */
+ if (prev)
+ prev->next = *cookies;
+ else
+ vars = *cookies;
+ *cookies = NULL;
+ ast_uri_decode(uri);
+
+ AST_RWLIST_RDLOCK(&uri_redirects);
+ AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
+ if (!strcasecmp(uri, redirect->target)) {
+ char buf[512];
+ snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
+ out = ast_http_error(302, "Moved Temporarily", buf,
+ "There is no spoon...");
+ *status = 302;
+ *title = ast_strdup("Moved Temporarily");
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&uri_redirects);
+ if (redirect)
+ goto cleanup;
+
+ /* We want requests to start with the prefix and '/' */
+ l = strlen(prefix);
+ if (l && !strncasecmp(uri, prefix, l) && uri[l] == '/') {
+ uri += l + 1;
+ /* scan registered uris to see if we match one. */
+ AST_RWLIST_RDLOCK(&uris);
+ AST_RWLIST_TRAVERSE(&uris, urih, entry) {
+ l = strlen(urih->uri);
+ c = uri + l; /* candidate */
+ if (strncasecmp(urih->uri, uri, l) /* no match */
+ || (*c && *c != '/')) /* substring */
+ continue;
+ if (*c == '/')
+ c++;
+ if (!*c || urih->has_subtree) {
+ uri = c;
+ break;
+ }
+ }
+ if (!urih)
+ AST_RWLIST_UNLOCK(&uris);
+ }
+ if (urih) {
+ if (urih->static_content)
+ *static_content = 1;
+ out = urih->callback(ser, uri, vars, status, title, contentlength);
+ AST_RWLIST_UNLOCK(&uris);
+ } else {
+ out = ast_http_error(404, "Not Found", NULL,
+ "The requested URL was not found on this server.");
+ *status = 404;
+ *title = ast_strdup("Not Found");
+ }
+
+cleanup:
+ ast_variables_destroy(vars);
+ return out;
+}
+
+#ifdef DO_SSL
+#if defined(HAVE_FUNOPEN)
+#define HOOK_T int
+#define LEN_T int
+#else
+#define HOOK_T ssize_t
+#define LEN_T size_t
+#endif
+/*!
+ * replacement read/write functions for SSL support.
+ * We use wrappers rather than SSL_read/SSL_write directly so
+ * we can put in some debugging.
+ */
+/*static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
+{
+ int i = SSL_read(cookie, buf, len-1);
+#if 0
+ if (i >= 0)
+ buf[i] = '\0';
+ ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
+#endif
+ return i;
+}
+
+static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
+{
+#if 0
+ char *s = alloca(len+1);
+ strncpy(s, buf, len);
+ s[len] = '\0';
+ ast_verbose("ssl write size %d <%s>\n", (int)len, s);
+#endif
+ return SSL_write(cookie, buf, len);
+}
+
+static int ssl_close(void *cookie)
+{
+ close(SSL_get_fd(cookie));
+ SSL_shutdown(cookie);
+ SSL_free(cookie);
+ return 0;
+}*/
+#endif /* DO_SSL */
+
+static void *httpd_helper_thread(void *data)
+{
+ char buf[4096];
+ char cookie[4096];
+ struct server_instance *ser = data;
+ struct ast_variable *var, *prev=NULL, *vars=NULL, *headers = NULL;
+ char *uri, *title=NULL;
+ int status = 200, contentlength = 0;
+ struct ast_str *out = NULL;
+ unsigned int static_content = 0;
+
+ if (!fgets(buf, sizeof(buf), ser->f))
+ goto done;
+
+ uri = ast_skip_nonblanks(buf); /* Skip method */
+ if (*uri)
+ *uri++ = '\0';
+
+ uri = ast_skip_blanks(uri); /* Skip white space */
+
+ if (*uri) { /* terminate at the first blank */
+ char *c = ast_skip_nonblanks(uri);
+ if (*c)
+ *c = '\0';
+ }
+
+ /* process "Cookie: " lines */
+ while (fgets(cookie, sizeof(cookie), ser->f)) {
+ char *vname, *vval;
+ int l;
+
+ /* Trim trailing characters */
+ ast_trim_blanks(cookie);
+ if (ast_strlen_zero(cookie))
+ break;
+ if (strncasecmp(cookie, "Cookie: ", 8)) {
+ char *name, *value;
+
+ value = ast_strdupa(cookie);
+ name = strsep(&value, ":");
+ if (!value)
+ continue;
+ value = ast_skip_blanks(value);
+ if (ast_strlen_zero(value))
+ continue;
+ var = ast_variable_new(name, value, "");
+ if (!var)
+ continue;
+ var->next = headers;
+ headers = var;
+ continue;
+ }
+
+ /* TODO - The cookie parsing code below seems to work
+ in IE6 and FireFox 1.5. However, it is not entirely
+ correct, and therefore may not work in all
+ circumstances.
+ For more details see RFC 2109 and RFC 2965 */
+
+ /* FireFox cookie strings look like:
+ Cookie: mansession_id="********"
+ InternetExplorer's look like:
+ Cookie: $Version="1"; mansession_id="********" */
+
+ /* If we got a FireFox cookie string, the name's right
+ after "Cookie: " */
+ vname = ast_skip_blanks(cookie + 8);
+
+ /* If we got an IE cookie string, we need to skip to
+ past the version to get to the name */
+ if (*vname == '$') {
+ strsep(&vname, ";");
+ if (!vname) /* no name ? */
+ continue;
+ vname = ast_skip_blanks(vname);
+ }
+ vval = strchr(vname, '=');
+ if (!vval)
+ continue;
+ /* Ditch the = and the quotes */
+ *vval++ = '\0';
+ if (*vval)
+ vval++;
+ if ( (l = strlen(vval)) )
+ vval[l - 1] = '\0'; /* trim trailing quote */
+ var = ast_variable_new(vname, vval, "");
+ if (var) {
+ if (prev)
+ prev->next = var;
+ else
+ vars = var;
+ prev = var;
+ }
+ }
+
+ if (!*uri)
+ out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
+ else if (!strcasecmp(buf, "post"))
+ out = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
+ else if (strcasecmp(buf, "get"))
+ out = ast_http_error(501, "Not Implemented", NULL,
+ "Attempt to use unimplemented / unsupported method");
+ else /* try to serve it */
+ out = handle_uri(ser, uri, &status, &title, &contentlength, &vars, &static_content);
+
+ /* If they aren't mopped up already, clean up the cookies */
+ if (vars)
+ ast_variables_destroy(vars);
+ /* Clean up all the header information pulled as well */
+ if (headers)
+ ast_variables_destroy(headers);
+
+ if (out) {
+ struct timeval tv = ast_tvnow();
+ char timebuf[256];
+ struct ast_tm tm;
+
+ ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
+ fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
+ "Server: Asterisk/%s\r\n"
+ "Date: %s\r\n"
+ "Connection: close\r\n"
+ "%s",
+ status, title ? title : "OK", ast_get_version(), timebuf,
+ static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
+ /* We set the no-cache headers only for dynamic content.
+ * If you want to make sure the static file you requested is not from cache,
+ * append a random variable to your GET request. Ex: 'something.html?r=109987734'
+ */
+ if (!contentlength) { /* opaque body ? just dump it hoping it is properly formatted */
+ fprintf(ser->f, "%s", out->str);
+ } else {
+ char *tmp = strstr(out->str, "\r\n\r\n");
+
+ if (tmp) {
+ fprintf(ser->f, "Content-length: %d\r\n", contentlength);
+ /* first write the header, then the body */
+ fwrite(out->str, 1, (tmp + 4 - out->str), ser->f);
+ fwrite(tmp + 4, 1, contentlength, ser->f);
+ }
+ }
+ ast_free(out);
+ }
+ if (title)
+ ast_free(title);
+
+done:
+ fclose(ser->f);
+ ast_free(ser);
+ return NULL;
+}
+
+/*!
+ * \brief Add a new URI redirect
+ * The entries in the redirect list are sorted by length, just like the list
+ * of URI handlers.
+ */
+static void add_redirect(const char *value)
+{
+ char *target, *dest;
+ struct http_uri_redirect *redirect, *cur;
+ unsigned int target_len;
+ unsigned int total_len;
+
+ dest = ast_strdupa(value);
+ dest = ast_skip_blanks(dest);
+ target = strsep(&dest, " ");
+ target = ast_skip_blanks(target);
+ target = strsep(&target, " "); /* trim trailing whitespace */
+
+ if (!dest) {
+ ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
+ return;
+ }
+
+ target_len = strlen(target) + 1;
+ total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
+
+ if (!(redirect = ast_calloc(1, total_len)))
+ return;
+
+ redirect->dest = redirect->target + target_len;
+ strcpy(redirect->target, target);
+ strcpy(redirect->dest, dest);
+
+ AST_RWLIST_WRLOCK(&uri_redirects);
+
+ target_len--; /* So we can compare directly with strlen() */
+ if ( AST_RWLIST_EMPTY(&uri_redirects)
+ || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
+ AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
+ AST_RWLIST_UNLOCK(&uri_redirects);
+ return;
+ }
+
+ AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
+ if ( AST_RWLIST_NEXT(cur, entry)
+ && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
+ AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
+ AST_RWLIST_UNLOCK(&uri_redirects);
+ return;
+ }
+ }
+
+ AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
+
+ AST_RWLIST_UNLOCK(&uri_redirects);
+}
+
+static void destroy_post_mapping(struct ast_http_post_mapping *post_map)
+{
+ if (post_map->from)
+ ast_free(post_map->from);
+ if (post_map->to)
+ ast_free(post_map->to);
+ ast_free(post_map);
+}
+
+static void destroy_post_mappings(void)
+{
+ struct ast_http_post_mapping *post_map;
+
+ AST_RWLIST_WRLOCK(&post_mappings);
+ while ((post_map = AST_RWLIST_REMOVE_HEAD(&post_mappings, entry)))
+ destroy_post_mapping(post_map);
+ AST_RWLIST_UNLOCK(&post_mappings);
+}
+
+static void add_post_mapping(const char *from, const char *to)
+{
+ struct ast_http_post_mapping *post_map;
+
+ if (!(post_map = ast_calloc(1, sizeof(*post_map))))
+ return;
+
+ if (!(post_map->from = ast_strdup(from))) {
+ destroy_post_mapping(post_map);
+ return;
+ }
+
+ if (!(post_map->to = ast_strdup(to))) {
+ destroy_post_mapping(post_map);
+ return;
+ }
+
+ AST_RWLIST_WRLOCK(&post_mappings);
+ AST_RWLIST_INSERT_TAIL(&post_mappings, post_map, entry);
+ AST_RWLIST_UNLOCK(&post_mappings);
+}
+
+static int __ast_http_load(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ int enabled=0;
+ int newenablestatic=0;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ char newprefix[MAX_PREFIX];
+ int have_sslbindaddr = 0;
+ struct http_uri_redirect *redirect;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("http.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ /* default values */
+ memset(&http_desc.sin, 0, sizeof(http_desc.sin));
+ http_desc.sin.sin_port = htons(8088);
+
+ memset(&https_desc.sin, 0, sizeof(https_desc.sin));
+ https_desc.sin.sin_port = htons(8089);
+
+ strcpy(newprefix, DEFAULT_PREFIX);
+
+ http_tls_cfg.enabled = 0;
+ if (http_tls_cfg.certfile)
+ ast_free(http_tls_cfg.certfile);
+ http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
+ if (http_tls_cfg.cipher)
+ ast_free(http_tls_cfg.cipher);
+ http_tls_cfg.cipher = ast_strdup("");
+
+ AST_RWLIST_WRLOCK(&uri_redirects);
+ while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry)))
+ ast_free(redirect);
+ AST_RWLIST_UNLOCK(&uri_redirects);
+
+ destroy_post_mappings();
+
+ if (cfg) {
+ v = ast_variable_browse(cfg, "general");
+ for (; v; v = v->next) {
+ if (!strcasecmp(v->name, "enabled"))
+ enabled = ast_true(v->value);
+ else if (!strcasecmp(v->name, "sslenable"))
+ http_tls_cfg.enabled = ast_true(v->value);
+ else if (!strcasecmp(v->name, "sslbindport"))
+ https_desc.sin.sin_port = htons(atoi(v->value));
+ else if (!strcasecmp(v->name, "sslcert")) {
+ ast_free(http_tls_cfg.certfile);
+ http_tls_cfg.certfile = ast_strdup(v->value);
+ } else if (!strcasecmp(v->name, "sslcipher")) {
+ ast_free(http_tls_cfg.cipher);
+ http_tls_cfg.cipher = ast_strdup(v->value);
+ }
+ else if (!strcasecmp(v->name, "enablestatic"))
+ newenablestatic = ast_true(v->value);
+ else if (!strcasecmp(v->name, "bindport"))
+ http_desc.sin.sin_port = htons(atoi(v->value));
+ else if (!strcasecmp(v->name, "sslbindaddr")) {
+ if ((hp = ast_gethostbyname(v->value, &ahp))) {
+ memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
+ have_sslbindaddr = 1;
+ } else {
+ ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "bindaddr")) {
+ if ((hp = ast_gethostbyname(v->value, &ahp))) {
+ memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
+ } else {
+ ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "prefix")) {
+ if (!ast_strlen_zero(v->value)) {
+ newprefix[0] = '/';
+ ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
+ } else {
+ newprefix[0] = '\0';
+ }
+ } else if (!strcasecmp(v->name, "redirect")) {
+ add_redirect(v->value);
+ } else {
+ ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
+ }
+ }
+
+ for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next)
+ add_post_mapping(v->name, v->value);
+
+ ast_config_destroy(cfg);
+ }
+ if (!have_sslbindaddr)
+ https_desc.sin.sin_addr = http_desc.sin.sin_addr;
+ if (enabled)
+ http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
+ if (strcmp(prefix, newprefix))
+ ast_copy_string(prefix, newprefix, sizeof(prefix));
+ enablestatic = newenablestatic;
+ server_start(&http_desc);
+ if (ssl_setup(https_desc.tls_cfg))
+ server_start(&https_desc);
+
+ return 0;
+}
+
+static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_http_uri *urih;
+ struct http_uri_redirect *redirect;
+ struct ast_http_post_mapping *post_map;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "http show status";
+ e->usage =
+ "Usage: http show status\n"
+ " Lists status of internal HTTP engine\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "HTTP Server Status:\n");
+ ast_cli(a->fd, "Prefix: %s\n", prefix);
+ if (!http_desc.oldsin.sin_family)
+ ast_cli(a->fd, "Server Disabled\n\n");
+ else {
+ ast_cli(a->fd, "Server Enabled and Bound to %s:%d\n\n",
+ ast_inet_ntoa(http_desc.oldsin.sin_addr),
+ ntohs(http_desc.oldsin.sin_port));
+ if (http_tls_cfg.enabled)
+ ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
+ ast_inet_ntoa(https_desc.oldsin.sin_addr),
+ ntohs(https_desc.oldsin.sin_port));
+ }
+
+ ast_cli(a->fd, "Enabled URI's:\n");
+ AST_RWLIST_RDLOCK(&uris);
+ if (AST_RWLIST_EMPTY(&uris)) {
+ ast_cli(a->fd, "None.\n");
+ } else {
+ AST_RWLIST_TRAVERSE(&uris, urih, entry)
+ ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
+ }
+ AST_RWLIST_UNLOCK(&uris);
+
+ ast_cli(a->fd, "\nEnabled Redirects:\n");
+ AST_RWLIST_RDLOCK(&uri_redirects);
+ AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
+ ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
+ if (AST_RWLIST_EMPTY(&uri_redirects))
+ ast_cli(a->fd, " None.\n");
+ AST_RWLIST_UNLOCK(&uri_redirects);
+
+
+ ast_cli(a->fd, "\nPOST mappings:\n");
+ AST_RWLIST_RDLOCK(&post_mappings);
+ AST_LIST_TRAVERSE(&post_mappings, post_map, entry)
+ ast_cli(a->fd, "%s/%s => %s\n", prefix, post_map->from, post_map->to);
+ ast_cli(a->fd, "%s\n", AST_LIST_EMPTY(&post_mappings) ? "None.\n" : "");
+ AST_RWLIST_UNLOCK(&post_mappings);
+
+ return CLI_SUCCESS;
+}
+
+int ast_http_reload(void)
+{
+ return __ast_http_load(1);
+}
+
+static struct ast_cli_entry cli_http[] = {
+ AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
+};
+
+int ast_http_init(void)
+{
+ mm_library_init();
+ mm_codec_registerdefaultcodecs();
+
+ ast_http_uri_link(&statusuri);
+ ast_http_uri_link(&staticuri);
+ ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
+
+ return __ast_http_load(0);
+}
diff --git a/trunk/main/image.c b/trunk/main/image.c
new file mode 100644
index 000000000..7096311a2
--- /dev/null
+++ b/trunk/main/image.c
@@ -0,0 +1,208 @@
+/*
+ * 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 Image Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/image.h"
+#include "asterisk/translate.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+
+/* XXX Why don't we just use the formats struct for this? */
+static AST_RWLIST_HEAD_STATIC(imagers, ast_imager);
+
+int ast_image_register(struct ast_imager *img)
+{
+ AST_RWLIST_WRLOCK(&imagers);
+ AST_RWLIST_INSERT_HEAD(&imagers, img, list);
+ AST_RWLIST_UNLOCK(&imagers);
+ ast_verb(2, "Registered format '%s' (%s)\n", img->name, img->desc);
+ return 0;
+}
+
+void ast_image_unregister(struct ast_imager *img)
+{
+ AST_RWLIST_WRLOCK(&imagers);
+ img = AST_RWLIST_REMOVE(&imagers, img, list);
+ AST_RWLIST_UNLOCK(&imagers);
+
+ if (img)
+ ast_verb(2, "Unregistered format '%s' (%s)\n", img->name, img->desc);
+}
+
+int ast_supports_images(struct ast_channel *chan)
+{
+ if (!chan || !chan->tech)
+ return 0;
+ if (!chan->tech->send_image)
+ return 0;
+ return 1;
+}
+
+static int file_exists(char *filename)
+{
+ int res;
+ struct stat st;
+ res = stat(filename, &st);
+ if (!res)
+ return st.st_size;
+ return 0;
+}
+
+static void make_filename(char *buf, int len, char *filename, const char *preflang, char *ext)
+{
+ if (filename[0] == '/') {
+ if (!ast_strlen_zero(preflang))
+ snprintf(buf, len, "%s-%s.%s", filename, preflang, ext);
+ else
+ snprintf(buf, len, "%s.%s", filename, ext);
+ } else {
+ if (!ast_strlen_zero(preflang))
+ snprintf(buf, len, "%s/%s/%s-%s.%s", ast_config_AST_DATA_DIR, "images", filename, preflang, ext);
+ else
+ snprintf(buf, len, "%s/%s/%s.%s", ast_config_AST_DATA_DIR, "images", filename, ext);
+ }
+}
+
+struct ast_frame *ast_read_image(char *filename, const char *preflang, int format)
+{
+ struct ast_imager *i;
+ char buf[256];
+ char tmp[80];
+ char *e;
+ struct ast_imager *found = NULL;
+ int fd;
+ int len=0;
+ struct ast_frame *f = NULL;
+
+ AST_RWLIST_RDLOCK(&imagers);
+ AST_RWLIST_TRAVERSE(&imagers, i, list) {
+ if (i->format & format) {
+ char *stringp=NULL;
+ ast_copy_string(tmp, i->exts, sizeof(tmp));
+ stringp=tmp;
+ e = strsep(&stringp, "|");
+ while (e) {
+ make_filename(buf, sizeof(buf), filename, preflang, e);
+ if ((len = file_exists(buf))) {
+ found = i;
+ break;
+ }
+ make_filename(buf, sizeof(buf), filename, NULL, e);
+ if ((len = file_exists(buf))) {
+ found = i;
+ break;
+ }
+ e = strsep(&stringp, "|");
+ }
+ }
+ if (found)
+ break;
+ }
+
+ if (found) {
+ fd = open(buf, O_RDONLY);
+ if (fd > -1) {
+ if (!found->identify || found->identify(fd)) {
+ /* Reset file pointer */
+ lseek(fd, 0, SEEK_SET);
+ f = found->read_image(fd,len);
+ } else
+ ast_log(LOG_WARNING, "%s does not appear to be a %s file\n", buf, found->name);
+ close(fd);
+ } else
+ ast_log(LOG_WARNING, "Unable to open '%s': %s\n", buf, strerror(errno));
+ } else
+ ast_log(LOG_WARNING, "Image file '%s' not found\n", filename);
+
+ AST_RWLIST_UNLOCK(&imagers);
+
+ return f;
+}
+
+int ast_send_image(struct ast_channel *chan, char *filename)
+{
+ struct ast_frame *f;
+ int res = -1;
+ if (chan->tech->send_image) {
+ f = ast_read_image(filename, chan->language, -1);
+ if (f) {
+ res = chan->tech->send_image(chan, f);
+ ast_frfree(f);
+ }
+ }
+ return res;
+}
+
+static char *handle_core_show_image_formats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%10s %10s %50s %10s\n"
+#define FORMAT2 "%10s %10s %50s %10s\n"
+ struct ast_imager *i;
+ int count_fmt = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show image formats";
+ e->usage =
+ "Usage: core show image formats\n"
+ " Displays currently registered image formats (if any).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, FORMAT, "Name", "Extensions", "Description", "Format");
+ ast_cli(a->fd, FORMAT, "----", "----------", "-----------", "------");
+ AST_RWLIST_RDLOCK(&imagers);
+ AST_RWLIST_TRAVERSE(&imagers, i, list) {
+ ast_cli(a->fd, FORMAT2, i->name, i->exts, i->desc, ast_getformatname(i->format));
+ count_fmt++;
+ }
+ AST_RWLIST_UNLOCK(&imagers);
+ ast_cli(a->fd, "\n%d image format%s registered.\n", count_fmt, count_fmt == 1 ? "" : "s");
+ return CLI_SUCCESS;
+}
+
+struct ast_cli_entry cli_image[] = {
+ AST_CLI_DEFINE(handle_core_show_image_formats, "Displays image formats")
+};
+
+int ast_image_init(void)
+{
+ ast_cli_register_multiple(cli_image, ARRAY_LEN(cli_image));
+ return 0;
+}
diff --git a/trunk/main/indications.c b/trunk/main/indications.c
new file mode 100644
index 000000000..e555d0357
--- /dev/null
+++ b/trunk/main/indications.c
@@ -0,0 +1,598 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002, Pauline Middelink
+ *
+ *
+ * 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 Tone Management
+ *
+ * \author Pauline Middelink <middelink@polyware.nl>
+ *
+ * This set of function allow us to play a list of tones on a channel.
+ * Each element has two frequencies, which are mixed together and a
+ * duration. For silence both frequencies can be set to 0.
+ * The playtones can be given as a comma separated string.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/indications.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+
+static int midi_tohz[128] = {
+ 8,8,9,9,10,10,11,12,12,13,14,
+ 15,16,17,18,19,20,21,23,24,25,
+ 27,29,30,32,34,36,38,41,43,46,
+ 48,51,55,58,61,65,69,73,77,82,
+ 87,92,97,103,110,116,123,130,138,146,
+ 155,164,174,184,195,207,220,233,246,261,
+ 277,293,311,329,349,369,391,415,440,466,
+ 493,523,554,587,622,659,698,739,783,830,
+ 880,932,987,1046,1108,1174,1244,1318,1396,1479,
+ 1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,
+ 2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,
+ 4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,
+ 8869,9397,9956,10548,11175,11839,12543
+ };
+
+struct playtones_item {
+ int fac1;
+ int init_v2_1;
+ int init_v3_1;
+ int fac2;
+ int init_v2_2;
+ int init_v3_2;
+ int modulate;
+ int duration;
+};
+
+struct playtones_def {
+ int vol;
+ int reppos;
+ int nitems;
+ int interruptible;
+ struct playtones_item *items;
+};
+
+struct playtones_state {
+ int vol;
+ int v1_1;
+ int v2_1;
+ int v3_1;
+ int v1_2;
+ int v2_2;
+ int v3_2;
+ int reppos;
+ int nitems;
+ struct playtones_item *items;
+ int npos;
+ int oldnpos;
+ int pos;
+ int origwfmt;
+ struct ast_frame f;
+ unsigned char offset[AST_FRIENDLY_OFFSET];
+ short data[4000];
+};
+
+static void playtones_release(struct ast_channel *chan, void *params)
+{
+ struct playtones_state *ps = params;
+
+ if (chan)
+ ast_set_write_format(chan, ps->origwfmt);
+ if (ps->items)
+ ast_free(ps->items);
+
+ ast_free(ps);
+}
+
+static void * playtones_alloc(struct ast_channel *chan, void *params)
+{
+ struct playtones_def *pd = params;
+ struct playtones_state *ps = NULL;
+
+ if (!(ps = ast_calloc(1, sizeof(*ps))))
+ return NULL;
+
+ ps->origwfmt = chan->writeformat;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
+ playtones_release(NULL, ps);
+ ps = NULL;
+ } else {
+ ps->vol = pd->vol;
+ ps->reppos = pd->reppos;
+ ps->nitems = pd->nitems;
+ ps->items = pd->items;
+ ps->oldnpos = -1;
+ }
+
+ /* Let interrupts interrupt :) */
+ if (pd->interruptible)
+ ast_set_flag(chan, AST_FLAG_WRITE_INT);
+ else
+ ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+
+ return ps;
+}
+
+static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct playtones_state *ps = data;
+ struct playtones_item *pi;
+ int x;
+ /* we need to prepare a frame with 16 * timelen samples as we're
+ * generating SLIN audio
+ */
+ len = samples * 2;
+ if (len > sizeof(ps->data) / 2 - 1) {
+ ast_log(LOG_WARNING, "Can't generate that much data!\n");
+ return -1;
+ }
+ memset(&ps->f, 0, sizeof(ps->f));
+
+ pi = &ps->items[ps->npos];
+ if (ps->oldnpos != ps->npos) {
+ /* Load new parameters */
+ ps->v1_1 = 0;
+ ps->v2_1 = pi->init_v2_1;
+ ps->v3_1 = pi->init_v3_1;
+ ps->v1_2 = 0;
+ ps->v2_2 = pi->init_v2_2;
+ ps->v3_2 = pi->init_v3_2;
+ ps->oldnpos = ps->npos;
+ }
+ for (x=0;x<len/2;x++) {
+ ps->v1_1 = ps->v2_1;
+ ps->v2_1 = ps->v3_1;
+ ps->v3_1 = (pi->fac1 * ps->v2_1 >> 15) - ps->v1_1;
+
+ ps->v1_2 = ps->v2_2;
+ ps->v2_2 = ps->v3_2;
+ ps->v3_2 = (pi->fac2 * ps->v2_2 >> 15) - ps->v1_2;
+ if (pi->modulate) {
+ int p;
+ p = ps->v3_2 - 32768;
+ if (p < 0) p = -p;
+ p = ((p * 9) / 10) + 1;
+ ps->data[x] = (ps->v3_1 * p) >> 15;
+ } else
+ ps->data[x] = ps->v3_1 + ps->v3_2;
+ }
+
+ ps->f.frametype = AST_FRAME_VOICE;
+ ps->f.subclass = AST_FORMAT_SLINEAR;
+ ps->f.datalen = len;
+ ps->f.samples = samples;
+ ps->f.offset = AST_FRIENDLY_OFFSET;
+ ps->f.data = ps->data;
+ ps->f.delivery.tv_sec = 0;
+ ps->f.delivery.tv_usec = 0;
+ ast_write(chan, &ps->f);
+
+ ps->pos += x;
+ if (pi->duration && ps->pos >= pi->duration * 8) { /* item finished? */
+ ps->pos = 0; /* start new item */
+ ps->npos++;
+ if (ps->npos >= ps->nitems) { /* last item? */
+ if (ps->reppos == -1) /* repeat set? */
+ return -1;
+ ps->npos = ps->reppos; /* redo from top */
+ }
+ }
+ return 0;
+}
+
+static struct ast_generator playtones = {
+ alloc: playtones_alloc,
+ release: playtones_release,
+ generate: playtones_generator,
+};
+
+int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
+{
+ char *s, *data = ast_strdupa(playlst); /* cute */
+ struct playtones_def d = { vol, -1, 0, 1, NULL};
+ char *stringp;
+ char *separator;
+
+ if (vol < 1)
+ d.vol = 7219; /* Default to -8db */
+
+ d.interruptible = interruptible;
+
+ stringp=data;
+ /* the stringp/data is not null here */
+ /* check if the data is separated with '|' or with ',' by default */
+ if (strchr(stringp,'|'))
+ separator = "|";
+ else
+ separator = ",";
+ s = strsep(&stringp,separator);
+ while (s && *s) {
+ int freq1, freq2, time, modulate=0, midinote=0;
+
+ if (s[0]=='!')
+ s++;
+ else if (d.reppos == -1)
+ d.reppos = d.nitems;
+ if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
+ /* f1+f2/time format */
+ } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
+ /* f1+f2 format */
+ time = 0;
+ } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3) {
+ /* f1*f2/time format */
+ modulate = 1;
+ } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) {
+ /* f1*f2 format */
+ time = 0;
+ modulate = 1;
+ } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
+ /* f1/time format */
+ freq2 = 0;
+ } else if (sscanf(s, "%d", &freq1) == 1) {
+ /* f1 format */
+ freq2 = 0;
+ time = 0;
+ } else if (sscanf(s, "M%d+M%d/%d", &freq1, &freq2, &time) == 3) {
+ /* Mf1+Mf2/time format */
+ midinote = 1;
+ } else if (sscanf(s, "M%d+M%d", &freq1, &freq2) == 2) {
+ /* Mf1+Mf2 format */
+ time = 0;
+ midinote = 1;
+ } else if (sscanf(s, "M%d*M%d/%d", &freq1, &freq2, &time) == 3) {
+ /* Mf1*Mf2/time format */
+ modulate = 1;
+ midinote = 1;
+ } else if (sscanf(s, "M%d*M%d", &freq1, &freq2) == 2) {
+ /* Mf1*Mf2 format */
+ time = 0;
+ modulate = 1;
+ midinote = 1;
+ } else if (sscanf(s, "M%d/%d", &freq1, &time) == 2) {
+ /* Mf1/time format */
+ freq2 = -1;
+ midinote = 1;
+ } else if (sscanf(s, "M%d", &freq1) == 1) {
+ /* Mf1 format */
+ freq2 = -1;
+ time = 0;
+ midinote = 1;
+ } else {
+ ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
+ return -1;
+ }
+
+ if (midinote) {
+ /* midi notes must be between 0 and 127 */
+ if ((freq1 >= 0) && (freq1 <= 127))
+ freq1 = midi_tohz[freq1];
+ else
+ freq1 = 0;
+
+ if ((freq2 >= 0) && (freq2 <= 127))
+ freq2 = midi_tohz[freq2];
+ else
+ freq2 = 0;
+ }
+
+ if (!(d.items = ast_realloc(d.items, (d.nitems + 1) * sizeof(*d.items)))) {
+ return -1;
+ }
+ d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (freq1 / 8000.0)) * 32768.0;
+ d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (freq1 / 8000.0)) * d.vol;
+ d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (freq1 / 8000.0)) * d.vol;
+
+ d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (freq2 / 8000.0)) * 32768.0;
+ d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (freq2 / 8000.0)) * d.vol;
+ d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (freq2 / 8000.0)) * d.vol;
+ d.items[d.nitems].duration = time;
+ d.items[d.nitems].modulate = modulate;
+ d.nitems++;
+
+ s = strsep(&stringp,separator);
+ }
+
+ if (ast_activate_generator(chan, &playtones, &d)) {
+ ast_free(d.items);
+ return -1;
+ }
+ return 0;
+}
+
+void ast_playtones_stop(struct ast_channel *chan)
+{
+ ast_deactivate_generator(chan);
+}
+
+/*--------------------------------------------*/
+
+static AST_RWLIST_HEAD_STATIC(tone_zones, ind_tone_zone);
+static struct ind_tone_zone *current_tonezone;
+
+struct ind_tone_zone *ast_walk_indications(const struct ind_tone_zone *cur)
+{
+ struct ind_tone_zone *tz = NULL;
+
+ AST_RWLIST_RDLOCK(&tone_zones);
+ /* If cur is not NULL, then we have to iterate through - otherwise just return the first entry */
+ if (cur) {
+ AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
+ if (tz == cur)
+ break;
+ }
+ tz = AST_RWLIST_NEXT(tz, list);
+ } else {
+ tz = AST_RWLIST_FIRST(&tone_zones);
+ }
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ return tz;
+}
+
+/* Set global indication country */
+int ast_set_indication_country(const char *country)
+{
+ struct ind_tone_zone *zone = NULL;
+
+ /* If no country is specified or we are unable to find the zone, then return not found */
+ if (!country || !(zone = ast_get_indication_zone(country)))
+ return 1;
+
+ ast_verb(3, "Setting default indication country to '%s'\n", country);
+
+ /* Protect the current tonezone using the tone_zones lock as well */
+ AST_RWLIST_WRLOCK(&tone_zones);
+ current_tonezone = zone;
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ /* Zone was found */
+ return 0;
+}
+
+/* locate tone_zone, given the country. if country == NULL, use the default country */
+struct ind_tone_zone *ast_get_indication_zone(const char *country)
+{
+ struct ind_tone_zone *tz = NULL;
+ int alias_loop = 0;
+
+ AST_RWLIST_RDLOCK(&tone_zones);
+
+ if (!country) {
+ if (current_tonezone)
+ tz = current_tonezone;
+ else
+ tz = AST_LIST_FIRST(&tone_zones);
+ } else {
+ do {
+ AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
+ if (!strcasecmp(tz->country, country))
+ break;
+ }
+ if (!tz)
+ break;
+ /* If this is an alias then we have to search yet again otherwise we have found the zonezone */
+ if (tz->alias && tz->alias[0])
+ country = tz->alias;
+ else
+ break;
+ } while ((++alias_loop < 20) && tz);
+ }
+
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ /* If we reached the maximum loops to find the proper country via alias, print out a notice */
+ if (alias_loop == 20)
+ ast_log(LOG_NOTICE, "Alias loop for '%s' is bonkers\n", country);
+
+ return tz;
+}
+
+/* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
+struct ind_tone_zone_sound *ast_get_indication_tone(const struct ind_tone_zone *zone, const char *indication)
+{
+ struct ind_tone_zone_sound *ts = NULL;
+
+ AST_RWLIST_RDLOCK(&tone_zones);
+
+ /* If no zone is already specified we need to try to pick one */
+ if (!zone) {
+ if (current_tonezone) {
+ zone = current_tonezone;
+ } else if (!(zone = AST_LIST_FIRST(&tone_zones))) {
+ /* No zone has been found ;( */
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return NULL;
+ }
+ }
+
+ /* Look through list of tones in the zone searching for the right one */
+ for (ts = zone->tones; ts; ts = ts->next) {
+ if (!strcasecmp(ts->name, indication))
+ break;
+ }
+
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ return ts;
+}
+
+/* helper function to delete a tone_zone in its entirety */
+static inline void free_zone(struct ind_tone_zone* zone)
+{
+ while (zone->tones) {
+ struct ind_tone_zone_sound *tmp = zone->tones->next;
+ ast_free((void *)zone->tones->name);
+ ast_free((void *)zone->tones->data);
+ ast_free(zone->tones);
+ zone->tones = tmp;
+ }
+
+ if (zone->ringcadence)
+ ast_free(zone->ringcadence);
+
+ ast_free(zone);
+}
+
+/*--------------------------------------------*/
+
+/* add a new country, if country exists, it will be replaced. */
+int ast_register_indication_country(struct ind_tone_zone *zone)
+{
+ struct ind_tone_zone *tz = NULL;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
+ /* If this is not the same zone, then just continue to the next entry */
+ if (strcasecmp(zone->country, tz->country))
+ continue;
+ /* If this zone we are going to remove is the current default then make the new zone the default */
+ if (tz == current_tonezone)
+ current_tonezone = zone;
+ /* Remove from the linked list */
+ AST_RWLIST_REMOVE_CURRENT(list);
+ /* Finally free the zone itself */
+ free_zone(tz);
+ break;
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ /* Add zone to the list */
+ AST_RWLIST_INSERT_TAIL(&tone_zones, zone, list);
+
+ /* It's all over. */
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ ast_verb(3, "Registered indication country '%s'\n", zone->country);
+
+ return 0;
+}
+
+/* remove an existing country and all its indications, country must exist.
+ * Also, all countries which are an alias for the specified country are removed. */
+int ast_unregister_indication_country(const char *country)
+{
+ struct ind_tone_zone *tz = NULL;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
+ if (country && (strcasecmp(country, tz->country) && strcasecmp(country, tz->alias)))
+ continue;
+ /* If this tonezone is the current default then unset it */
+ if (tz == current_tonezone) {
+ ast_log(LOG_NOTICE,"Removed default indication country '%s'\n", tz->country);
+ current_tonezone = NULL;
+ }
+ /* Remove from the list */
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(3, "Unregistered indication country '%s'\n", tz->country);
+ free_zone(tz);
+ res = 0;
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ return res;
+}
+
+/* add a new indication to a tone_zone. tone_zone must exist. if the indication already
+ * exists, it will be replaced. */
+int ast_register_indication(struct ind_tone_zone *zone, const char *indication, const char *tonelist)
+{
+ struct ind_tone_zone_sound *ts, *ps;
+
+ /* is it an alias? stop */
+ if (zone->alias[0])
+ return -1;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
+ if (strcasecmp(indication,ts->name)==0) {
+ /* indication already there, replace */
+ ast_free((void*)ts->name);
+ ast_free((void*)ts->data);
+ break;
+ }
+ }
+ if (!ts) {
+ /* not there, we have to add */
+ if (!(ts = ast_malloc(sizeof(*ts)))) {
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return -2;
+ }
+ ts->next = NULL;
+ }
+ if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return -2;
+ }
+ if (ps)
+ ps->next = ts;
+ else
+ zone->tones = ts;
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return 0;
+}
+
+/* remove an existing country's indication. Both country and indication must exist */
+int ast_unregister_indication(struct ind_tone_zone *zone, const char *indication)
+{
+ struct ind_tone_zone_sound *ts,*ps = NULL, *tmp;
+ int res = -1;
+
+ /* is it an alias? stop */
+ if (zone->alias[0])
+ return -1;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ ts = zone->tones;
+ while (ts) {
+ if (strcasecmp(indication,ts->name)==0) {
+ /* indication found */
+ tmp = ts->next;
+ if (ps)
+ ps->next = tmp;
+ else
+ zone->tones = tmp;
+ ast_free((void*)ts->name);
+ ast_free((void*)ts->data);
+ ast_free(ts);
+ ts = tmp;
+ res = 0;
+ }
+ else {
+ /* next zone please */
+ ps = ts;
+ ts = ts->next;
+ }
+ }
+ /* indication not found, goodbye */
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return res;
+}
diff --git a/trunk/main/io.c b/trunk/main/io.c
new file mode 100644
index 000000000..68b73d26c
--- /dev/null
+++ b/trunk/main/io.c
@@ -0,0 +1,381 @@
+/*
+ * 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 I/O Managment (Derived from Cheops-NG)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "asterisk/io.h"
+#include "asterisk/utils.h"
+
+#ifdef DEBUG_IO
+#define DEBUG DEBUG_M
+#else
+#define DEBUG(a)
+#endif
+
+/*! \brief
+ * Kept for each file descriptor
+ */
+struct io_rec {
+ ast_io_cb callback; /*!< What is to be called */
+ void *data; /*!< Data to be passed */
+ int *id; /*!< ID number */
+};
+
+/* These two arrays are keyed with
+ the same index. it's too bad that
+ pollfd doesn't have a callback field
+ or something like that. They grow as
+ needed, by GROW_SHRINK_SIZE structures
+ at once */
+
+#define GROW_SHRINK_SIZE 512
+
+/*! \brief Global IO variables are now in a struct in order to be
+ made threadsafe */
+struct io_context {
+ struct pollfd *fds; /*!< Poll structure */
+ struct io_rec *ior; /*!< Associated I/O records */
+ unsigned int fdcnt; /*!< First available fd */
+ unsigned int maxfdcnt; /*!< Maximum available fd */
+ int current_ioc; /*!< Currently used io callback */
+ int needshrink; /*!< Whether something has been deleted */
+};
+
+/*! \brief Create an I/O context */
+struct io_context *io_context_create(void)
+{
+ struct io_context *tmp = NULL;
+
+ if (!(tmp = ast_malloc(sizeof(*tmp))))
+ return NULL;
+
+ tmp->needshrink = 0;
+ tmp->fdcnt = 0;
+ tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
+ tmp->current_ioc = -1;
+
+ if (!(tmp->fds = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->fds)))) {
+ ast_free(tmp);
+ tmp = NULL;
+ } else {
+ if (!(tmp->ior = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->ior)))) {
+ ast_free(tmp->fds);
+ ast_free(tmp);
+ tmp = NULL;
+ }
+ }
+
+ return tmp;
+}
+
+void io_context_destroy(struct io_context *ioc)
+{
+ /* Free associated memory with an I/O context */
+ if (ioc->fds)
+ ast_free(ioc->fds);
+ if (ioc->ior)
+ ast_free(ioc->ior);
+
+ ast_free(ioc);
+}
+
+/*! \brief
+ * Grow the size of our arrays.
+ * \return 0 on success or -1 on failure
+ */
+static int io_grow(struct io_context *ioc)
+{
+ void *tmp;
+
+ DEBUG(ast_debug(1, "io_grow()\n"));
+
+ ioc->maxfdcnt += GROW_SHRINK_SIZE;
+
+ if ((tmp = ast_realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(*ioc->ior)))) {
+ ioc->ior = tmp;
+ if ((tmp = ast_realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(*ioc->fds)))) {
+ ioc->fds = tmp;
+ } else {
+ /*
+ * Failed to allocate enough memory for the pollfd. Not
+ * really any need to shrink back the iorec's as we'll
+ * probably want to grow them again soon when more memory
+ * is available, and then they'll already be the right size
+ */
+ ioc->maxfdcnt -= GROW_SHRINK_SIZE;
+ return -1;
+ }
+ } else {
+ /*
+ * Memory allocation failure. We return to the old size, and
+ * return a failure
+ */
+ ioc->maxfdcnt -= GROW_SHRINK_SIZE;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! \brief
+ * Add a new I/O entry for this file descriptor
+ * with the given event mask, to call callback with
+ * data as an argument.
+ * \return Returns NULL on failure.
+ */
+int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
+{
+ int *ret;
+
+ DEBUG(ast_debug(1, "ast_io_add()\n"));
+
+ if (ioc->fdcnt >= ioc->maxfdcnt) {
+ /*
+ * We don't have enough space for this entry. We need to
+ * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
+ */
+ if (io_grow(ioc))
+ return NULL;
+ }
+
+ /*
+ * At this point, we've got sufficiently large arrays going
+ * and we can make an entry for it in the pollfd and io_r
+ * structures.
+ */
+ ioc->fds[ioc->fdcnt].fd = fd;
+ ioc->fds[ioc->fdcnt].events = events;
+ ioc->fds[ioc->fdcnt].revents = 0;
+ ioc->ior[ioc->fdcnt].callback = callback;
+ ioc->ior[ioc->fdcnt].data = data;
+
+ if (!(ioc->ior[ioc->fdcnt].id = ast_malloc(sizeof(*ioc->ior[ioc->fdcnt].id)))) {
+ /* Bonk if we couldn't allocate an int */
+ return NULL;
+ }
+
+ *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
+ ret = ioc->ior[ioc->fdcnt].id;
+ ioc->fdcnt++;
+
+ return ret;
+}
+
+int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
+{
+ /* If this id exceeds our file descriptor count it doesn't exist here */
+ if (*id > ioc->fdcnt)
+ return NULL;
+
+ if (fd > -1)
+ ioc->fds[*id].fd = fd;
+ if (callback)
+ ioc->ior[*id].callback = callback;
+ if (events)
+ ioc->fds[*id].events = events;
+ if (data)
+ ioc->ior[*id].data = data;
+
+ return id;
+}
+
+static int io_shrink(struct io_context *ioc)
+{
+ int getfrom, putto = 0;
+
+ /*
+ * Bring the fields from the very last entry to cover over
+ * the entry we are removing, then decrease the size of the
+ * arrays by one.
+ */
+ for (getfrom = 0; getfrom < ioc->fdcnt; getfrom++) {
+ if (ioc->ior[getfrom].id) {
+ /* In use, save it */
+ if (getfrom != putto) {
+ ioc->fds[putto] = ioc->fds[getfrom];
+ ioc->ior[putto] = ioc->ior[getfrom];
+ *(ioc->ior[putto].id) = putto;
+ }
+ putto++;
+ }
+ }
+ ioc->fdcnt = putto;
+ ioc->needshrink = 0;
+ /* FIXME: We should free some memory if we have lots of unused
+ io structs */
+ return 0;
+}
+
+int ast_io_remove(struct io_context *ioc, int *_id)
+{
+ int x;
+
+ if (!_id) {
+ ast_log(LOG_WARNING, "Asked to remove NULL?\n");
+ return -1;
+ }
+
+ for (x = 0; x < ioc->fdcnt; x++) {
+ if (ioc->ior[x].id == _id) {
+ /* Free the int immediately and set to NULL so we know it's unused now */
+ ast_free(ioc->ior[x].id);
+ ioc->ior[x].id = NULL;
+ ioc->fds[x].events = 0;
+ ioc->fds[x].revents = 0;
+ ioc->needshrink = 1;
+ if (ioc->current_ioc == -1)
+ io_shrink(ioc);
+ return 0;
+ }
+ }
+
+ ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
+
+ return -1;
+}
+
+/*! \brief
+ * Make the poll call, and call
+ * the callbacks for anything that needs
+ * to be handled
+ */
+int ast_io_wait(struct io_context *ioc, int howlong)
+{
+ int res, x, origcnt;
+
+ DEBUG(ast_debug(1, "ast_io_wait()\n"));
+
+ if ((res = poll(ioc->fds, ioc->fdcnt, howlong)) <= 0)
+ return res;
+
+ /* At least one event tripped */
+ origcnt = ioc->fdcnt;
+ for (x = 0; x < origcnt; x++) {
+ /* Yes, it is possible for an entry to be deleted and still have an
+ event waiting if it occurs after the original calling id */
+ if (ioc->fds[x].revents && ioc->ior[x].id) {
+ /* There's an event waiting */
+ ioc->current_ioc = *ioc->ior[x].id;
+ if (ioc->ior[x].callback) {
+ if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
+ /* Time to delete them since they returned a 0 */
+ ast_io_remove(ioc, ioc->ior[x].id);
+ }
+ }
+ ioc->current_ioc = -1;
+ }
+ }
+
+ if (ioc->needshrink)
+ io_shrink(ioc);
+
+ return res;
+}
+
+void ast_io_dump(struct io_context *ioc)
+{
+ /*
+ * Print some debugging information via
+ * the logger interface
+ */
+ int x;
+
+ ast_debug(1, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
+ ast_debug(1, "================================================\n");
+ ast_debug(1, "| ID FD Callback Data Events |\n");
+ ast_debug(1, "+------+------+-----------+-----------+--------+\n");
+ for (x = 0; x < ioc->fdcnt; x++) {
+ ast_debug(1, "| %.4d | %.4d | %p | %p | %.6x |\n",
+ *ioc->ior[x].id,
+ ioc->fds[x].fd,
+ ioc->ior[x].callback,
+ ioc->ior[x].data,
+ ioc->fds[x].events);
+ }
+ ast_debug(1, "================================================\n");
+}
+
+/* Unrelated I/O functions */
+
+int ast_hide_password(int fd)
+{
+ struct termios tios;
+ int res;
+ int old;
+ if (!isatty(fd))
+ return -1;
+ res = tcgetattr(fd, &tios);
+ if (res < 0)
+ return -1;
+ old = tios.c_lflag & (ECHO | ECHONL);
+ tios.c_lflag &= ~ECHO;
+ tios.c_lflag |= ECHONL;
+ res = tcsetattr(fd, TCSAFLUSH, &tios);
+ if (res < 0)
+ return -1;
+ return old;
+}
+
+int ast_restore_tty(int fd, int oldstate)
+{
+ int res;
+ struct termios tios;
+ if (oldstate < 0)
+ return 0;
+ res = tcgetattr(fd, &tios);
+ if (res < 0)
+ return -1;
+ tios.c_lflag &= ~(ECHO | ECHONL);
+ tios.c_lflag |= oldstate;
+ res = tcsetattr(fd, TCSAFLUSH, &tios);
+ if (res < 0)
+ return -1;
+ return 0;
+}
+
+int ast_get_termcols(int fd)
+{
+ struct winsize win;
+ int cols = 0;
+
+ if (!isatty(fd))
+ return -1;
+
+ if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
+ if ( !cols && win.ws_col > 0 )
+ cols = (int) win.ws_col;
+ } else {
+ /* assume 80 characters if the ioctl fails for some reason */
+ cols = 80;
+ }
+
+ return cols;
+}
+
diff --git a/trunk/main/jitterbuf.c b/trunk/main/jitterbuf.c
new file mode 100644
index 000000000..27b93a659
--- /dev/null
+++ b/trunk/main/jitterbuf.c
@@ -0,0 +1,844 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.
+ *
+ * Contributors:
+ * Steve Kann <stevek@stevek.com>
+ *
+ * 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 jitterbuf: an application-independent jitterbuffer
+ * \author Steve Kann <stevek@stevek.com>
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "jitterbuf.h"
+#include "asterisk/utils.h"
+
+/*! define these here, just for ancient compiler systems */
+#define JB_LONGMAX 2147483647L
+#define JB_LONGMIN (-JB_LONGMAX - 1L)
+
+#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
+#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
+#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+
+#ifdef DEEP_DEBUG
+#define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+#else
+#define jb_dbg2(...) ((void)0)
+#endif
+
+static jb_output_function_t warnf, errf, dbgf;
+
+void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg)
+{
+ errf = err;
+ warnf = warn;
+ dbgf = dbg;
+}
+
+static void increment_losspct(jitterbuf *jb)
+{
+ jb->info.losspct = (100000 + 499 * jb->info.losspct)/500;
+}
+
+static void decrement_losspct(jitterbuf *jb)
+{
+ jb->info.losspct = (499 * jb->info.losspct)/500;
+}
+
+void jb_reset(jitterbuf *jb)
+{
+ /* only save settings */
+ jb_conf s = jb->info.conf;
+ memset(jb, 0, sizeof(*jb));
+ jb->info.conf = s;
+
+ /* initialize length, using the default value */
+ jb->info.current = jb->info.target = jb->info.conf.target_extra = JB_TARGET_EXTRA;
+ jb->info.silence_begin_ts = -1;
+}
+
+jitterbuf * jb_new()
+{
+ jitterbuf *jb;
+
+ if (!(jb = ast_malloc(sizeof(*jb))))
+ return NULL;
+
+ jb_reset(jb);
+
+ jb_dbg2("jb_new() = %x\n", jb);
+ return jb;
+}
+
+void jb_destroy(jitterbuf *jb)
+{
+ jb_frame *frame;
+ jb_dbg2("jb_destroy(%x)\n", jb);
+
+ /* free all the frames on the "free list" */
+ frame = jb->free;
+ while (frame != NULL) {
+ jb_frame *next = frame->next;
+ ast_free(frame);
+ frame = next;
+ }
+
+ /* free ourselves! */
+ ast_free(jb);
+}
+
+
+
+#if 0
+static int longcmp(const void *a, const void *b)
+{
+ return *(long *)a - *(long *)b;
+}
+#endif
+
+/*! \brief simple history manipulation
+ \note maybe later we can make the history buckets variable size, or something? */
+/* drop parameter determines whether we will drop outliers to minimize
+ * delay */
+static int history_put(jitterbuf *jb, long ts, long now, long ms)
+{
+ long delay = now - (ts - jb->info.resync_offset);
+ long threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold;
+ long kicked;
+
+ /* don't add special/negative times to history */
+ if (ts <= 0)
+ return 0;
+
+ /* check for drastic change in delay */
+ if (jb->info.conf.resync_threshold != -1) {
+ if (abs(delay - jb->info.last_delay) > threshold) {
+ jb->info.cnt_delay_discont++;
+ if (jb->info.cnt_delay_discont > 3) {
+ /* resync the jitterbuffer */
+ jb->info.cnt_delay_discont = 0;
+ jb->hist_ptr = 0;
+ jb->hist_maxbuf_valid = 0;
+
+ jb_warn("Resyncing the jb. last_delay %ld, this delay %ld, threshold %ld, new offset %ld\n", jb->info.last_delay, delay, threshold, ts - now);
+ jb->info.resync_offset = ts - now;
+ jb->info.last_delay = delay = 0; /* after resync, frame is right on time */
+ } else {
+ return -1;
+ }
+ } else {
+ jb->info.last_delay = delay;
+ jb->info.cnt_delay_discont = 0;
+ }
+ }
+
+ kicked = jb->history[jb->hist_ptr % JB_HISTORY_SZ];
+
+ jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay;
+
+ /* optimization; the max/min buffers don't need to be recalculated, if this packet's
+ * entry doesn't change them. This happens if this packet is not involved, _and_ any packet
+ * that got kicked out of the history is also not involved
+ * We do a number of comparisons, but it's probably still worthwhile, because it will usually
+ * succeed, and should be a lot faster than going through all 500 packets in history */
+ if (!jb->hist_maxbuf_valid)
+ return 0;
+
+ /* don't do this until we've filled history
+ * (reduces some edge cases below) */
+ if (jb->hist_ptr < JB_HISTORY_SZ)
+ goto invalidate;
+
+ /* if the new delay would go into min */
+ if (delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* or max.. */
+ if (delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* or the kicked delay would be in min */
+ if (kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ if (kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* if we got here, we don't need to invalidate, 'cause this delay didn't
+ * affect things */
+ return 0;
+ /* end optimization */
+
+
+invalidate:
+ jb->hist_maxbuf_valid = 0;
+ return 0;
+}
+
+static void history_calc_maxbuf(jitterbuf *jb)
+{
+ int i,j;
+
+ if (jb->hist_ptr == 0)
+ return;
+
+
+ /* initialize maxbuf/minbuf to the latest value */
+ for (i=0;i<JB_HISTORY_MAXBUF_SZ;i++) {
+/*
+ * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ */
+ jb->hist_maxbuf[i] = JB_LONGMIN;
+ jb->hist_minbuf[i] = JB_LONGMAX;
+ }
+
+ /* use insertion sort to populate maxbuf */
+ /* we want it to be the top "n" values, in order */
+
+ /* start at the beginning, or JB_HISTORY_SZ frames ago */
+ i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0;
+
+ for (;i<jb->hist_ptr;i++) {
+ long toins = jb->history[i % JB_HISTORY_SZ];
+
+ /* if the maxbuf should get this */
+ if (toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) {
+
+ /* insertion-sort it into the maxbuf */
+ for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+ /* found where it fits */
+ if (toins > jb->hist_maxbuf[j]) {
+ /* move over */
+ memmove(jb->hist_maxbuf + j + 1, jb->hist_maxbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_maxbuf[0]));
+ /* insert */
+ jb->hist_maxbuf[j] = toins;
+
+ break;
+ }
+ }
+ }
+
+ /* if the minbuf should get this */
+ if (toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) {
+
+ /* insertion-sort it into the maxbuf */
+ for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+ /* found where it fits */
+ if (toins < jb->hist_minbuf[j]) {
+ /* move over */
+ memmove(jb->hist_minbuf + j + 1, jb->hist_minbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_minbuf[0]));
+ /* insert */
+ jb->hist_minbuf[j] = toins;
+
+ break;
+ }
+ }
+ }
+
+ if (0) {
+ int k;
+ fprintf(stderr, "toins = %ld\n", toins);
+ fprintf(stderr, "maxbuf =");
+ for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
+ fprintf(stderr, "%ld ", jb->hist_maxbuf[k]);
+ fprintf(stderr, "\nminbuf =");
+ for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
+ fprintf(stderr, "%ld ", jb->hist_minbuf[k]);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ jb->hist_maxbuf_valid = 1;
+}
+
+static void history_get(jitterbuf *jb)
+{
+ long max, min, jitter;
+ int index;
+ int count;
+
+ if (!jb->hist_maxbuf_valid)
+ history_calc_maxbuf(jb);
+
+ /* count is how many items in history we're examining */
+ count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ;
+
+ /* index is the "n"ths highest/lowest that we'll look for */
+ index = count * JB_HISTORY_DROPPCT / 100;
+
+ /* sanity checks for index */
+ if (index > (JB_HISTORY_MAXBUF_SZ - 1))
+ index = JB_HISTORY_MAXBUF_SZ - 1;
+
+
+ if (index < 0) {
+ jb->info.min = 0;
+ jb->info.jitter = 0;
+ return;
+ }
+
+ max = jb->hist_maxbuf[index];
+ min = jb->hist_minbuf[index];
+
+ jitter = max - min;
+
+ /* these debug stmts compare the difference between looking at the absolute jitter, and the
+ * values we get by throwing away the outliers */
+ /*
+ fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter);
+ fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", 0, jb->hist_minbuf[0], jb->hist_maxbuf[0], jb->hist_maxbuf[0]-jb->hist_minbuf[0]);
+ */
+
+ jb->info.min = min;
+ jb->info.jitter = jitter;
+}
+
+/* returns 1 if frame was inserted into head of queue, 0 otherwise */
+static int queue_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts)
+{
+ jb_frame *frame;
+ jb_frame *p;
+ int head = 0;
+ long resync_ts = ts - jb->info.resync_offset;
+
+ if ((frame = jb->free)) {
+ jb->free = frame->next;
+ } else if (!(frame = ast_malloc(sizeof(*frame)))) {
+ jb_err("cannot allocate frame\n");
+ return 0;
+ }
+
+ jb->info.frames_cur++;
+
+ frame->data = data;
+ frame->ts = resync_ts;
+ frame->ms = ms;
+ frame->type = type;
+
+ /*
+ * frames are a circular list, jb-frames points to to the lowest ts,
+ * jb->frames->prev points to the highest ts
+ */
+
+ if (!jb->frames) { /* queue is empty */
+ jb->frames = frame;
+ frame->next = frame;
+ frame->prev = frame;
+ head = 1;
+ } else if (resync_ts < jb->frames->ts) {
+ frame->next = jb->frames;
+ frame->prev = jb->frames->prev;
+
+ frame->next->prev = frame;
+ frame->prev->next = frame;
+
+ /* frame is out of order */
+ jb->info.frames_ooo++;
+
+ jb->frames = frame;
+ head = 1;
+ } else {
+ p = jb->frames;
+
+ /* frame is out of order */
+ if (resync_ts < p->prev->ts) jb->info.frames_ooo++;
+
+ while (resync_ts < p->prev->ts && p->prev != jb->frames)
+ p = p->prev;
+
+ frame->next = p;
+ frame->prev = p->prev;
+
+ frame->next->prev = frame;
+ frame->prev->next = frame;
+ }
+ return head;
+}
+
+static long queue_next(jitterbuf *jb)
+{
+ if (jb->frames)
+ return jb->frames->ts;
+ else
+ return -1;
+}
+
+static long queue_last(jitterbuf *jb)
+{
+ if (jb->frames)
+ return jb->frames->prev->ts;
+ else
+ return -1;
+}
+
+static jb_frame *_queue_get(jitterbuf *jb, long ts, int all)
+{
+ jb_frame *frame;
+ frame = jb->frames;
+
+ if (!frame)
+ return NULL;
+
+ /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */
+
+ if (all || ts >= frame->ts) {
+ /* remove this frame */
+ frame->prev->next = frame->next;
+ frame->next->prev = frame->prev;
+
+ if (frame->next == frame)
+ jb->frames = NULL;
+ else
+ jb->frames = frame->next;
+
+
+ /* insert onto "free" single-linked list */
+ frame->next = jb->free;
+ jb->free = frame;
+
+ jb->info.frames_cur--;
+
+ /* we return the frame pointer, even though it's on free list,
+ * but caller must copy data */
+ return frame;
+ }
+
+ return NULL;
+}
+
+static jb_frame *queue_get(jitterbuf *jb, long ts)
+{
+ return _queue_get(jb,ts,0);
+}
+
+static jb_frame *queue_getall(jitterbuf *jb)
+{
+ return _queue_get(jb,0,1);
+}
+
+#if 0
+/* some diagnostics */
+static void jb_dbginfo(jitterbuf *jb)
+{
+ if (dbgf == NULL)
+ return;
+
+ jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
+ jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur);
+
+ jb_dbg("jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",
+ jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence_begin_ts, jb->info.current - jb->info.min,
+ jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8);
+ if (jb->info.frames_in > 0)
+ jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
+ jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost),
+ jb->info.frames_late * 100/jb->info.frames_in);
+ jb_dbg("jb info: queue %d -> %d. last_ts %d (queue len: %d) last_ms %d\n",
+ queue_next(jb),
+ queue_last(jb),
+ jb->info.next_voice_ts,
+ queue_last(jb) - queue_next(jb),
+ jb->info.last_voice_ms);
+}
+#endif
+
+#ifdef DEEP_DEBUG
+static void jb_chkqueue(jitterbuf *jb)
+{
+ int i=0;
+ jb_frame *p = jb->frames;
+
+ if (!p) {
+ return;
+ }
+
+ do {
+ if (p->next == NULL) {
+ jb_err("Queue is BROKEN at item [%d]", i);
+ }
+ i++;
+ p=p->next;
+ } while (p->next != jb->frames);
+}
+
+static void jb_dbgqueue(jitterbuf *jb)
+{
+ int i=0;
+ jb_frame *p = jb->frames;
+
+ jb_dbg("queue: ");
+
+ if (!p) {
+ jb_dbg("EMPTY\n");
+ return;
+ }
+
+ do {
+ jb_dbg("[%d]=%ld ", i++, p->ts);
+ p=p->next;
+ } while (p->next != jb->frames);
+
+ jb_dbg("\n");
+}
+#endif
+
+enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now)
+{
+ long numts;
+
+ jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now);
+
+ jb->info.frames_in++;
+
+ if (jb->frames && jb->dropem)
+ return JB_DROP;
+ jb->dropem = 0;
+
+ if (type == JB_TYPE_VOICE) {
+ /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
+ * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
+ if (history_put(jb,ts,now,ms))
+ return JB_DROP;
+ }
+ numts = 0;
+ if (jb->frames)
+ numts = jb->frames->prev->ts - jb->frames->ts;
+ if (numts >= jb->info.conf.max_jitterbuf) {
+ ast_debug(1, "Attempting to exceed Jitterbuf max %ld timeslots\n",
+ jb->info.conf.max_jitterbuf);
+ jb->dropem = 1;
+ return JB_DROP;
+ }
+ /* if put into head of queue, caller needs to reschedule */
+ if (queue_put(jb,data,type,ms,ts)) {
+ return JB_SCHED;
+ }
+ return JB_OK;
+}
+
+
+static enum jb_return_code _jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl)
+{
+ jb_frame *frame;
+ long diff;
+ static int dbg_cnt = 0;
+
+ /*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
+ /* get jitter info */
+ history_get(jb);
+
+ if (dbg_cnt && dbg_cnt % 50 == 0) {
+ jb_dbg("\n");
+ }
+ dbg_cnt++;
+
+ /* target */
+ jb->info.target = jb->info.jitter + jb->info.min + jb->info.conf.target_extra;
+
+ /* if a hard clamp was requested, use it */
+ if ((jb->info.conf.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.conf.max_jitterbuf)) {
+ jb_dbg("clamping target from %d to %d\n", (jb->info.target - jb->info.min), jb->info.conf.max_jitterbuf);
+ jb->info.target = jb->info.min + jb->info.conf.max_jitterbuf;
+ }
+
+ diff = jb->info.target - jb->info.current;
+
+ /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff, */
+ /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */
+
+ /* let's work on non-silent case first */
+ if (!jb->info.silence_begin_ts) {
+ /* we want to grow */
+ if ((diff > 0) &&
+ /* we haven't grown in the delay length */
+ (((jb->info.last_adjustment + JB_ADJUST_DELAY) < now) ||
+ /* we need to grow more than the "length" we have left */
+ (diff > queue_last(jb) - queue_next(jb)) ) ) {
+ /* grow by interp frame length */
+ jb->info.current += interpl;
+ jb->info.next_voice_ts += interpl;
+ jb->info.last_voice_ms = interpl;
+ jb->info.last_adjustment = now;
+ jb->info.cnt_contig_interp++;
+ if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
+ jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
+ }
+ jb_dbg("G");
+ return JB_INTERP;
+ }
+
+ frame = queue_get(jb, jb->info.next_voice_ts - jb->info.current);
+
+ /* not a voice frame; just return it. */
+ if (frame && frame->type != JB_TYPE_VOICE) {
+ if (frame->type == JB_TYPE_SILENCE) {
+ jb->info.silence_begin_ts = frame->ts;
+ jb->info.cnt_contig_interp = 0;
+ }
+
+ *frameout = *frame;
+ jb->info.frames_out++;
+ jb_dbg("o");
+ return JB_OK;
+ }
+
+
+ /* voice frame is later than expected */
+ if (frame && frame->ts + jb->info.current < jb->info.next_voice_ts) {
+ if (frame->ts + jb->info.current > jb->info.next_voice_ts - jb->info.last_voice_ms) {
+ /* either we interpolated past this frame in the last jb_get */
+ /* or the frame is still in order, but came a little too quick */
+ *frameout = *frame;
+ /* reset expectation for next frame */
+ jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.cnt_contig_interp = 0;
+ jb_dbg("v");
+ return JB_OK;
+ } else {
+ /* voice frame is late */
+ *frameout = *frame;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_late++;
+ jb->info.frames_lost--;
+ jb_dbg("l");
+ /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
+ jb_warninfo(jb); */
+ return JB_DROP;
+ }
+ }
+
+ /* keep track of frame sizes, to allow for variable sized-frames */
+ if (frame && frame->ms > 0) {
+ jb->info.last_voice_ms = frame->ms;
+ }
+
+ /* we want to shrink; shrink at 1 frame / 500ms */
+ /* unless we don't have a frame, then shrink 1 frame */
+ /* every 80ms (though perhaps we can shrink even faster */
+ /* in this case) */
+ if (diff < -jb->info.conf.target_extra &&
+ ((!frame && jb->info.last_adjustment + 80 < now) ||
+ (jb->info.last_adjustment + 500 < now))) {
+
+ jb->info.last_adjustment = now;
+ jb->info.cnt_contig_interp = 0;
+
+ if (frame) {
+ *frameout = *frame;
+ /* shrink by frame size we're throwing out */
+ jb->info.current -= frame->ms;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_dropped++;
+ jb_dbg("s");
+ return JB_DROP;
+ } else {
+ /* shrink by last_voice_ms */
+ jb->info.current -= jb->info.last_voice_ms;
+ jb->info.frames_lost++;
+ increment_losspct(jb);
+ jb_dbg("S");
+ return JB_NOFRAME;
+ }
+ }
+
+ /* lost frame */
+ if (!frame) {
+ /* this is a bit of a hack for now, but if we're close to
+ * target, and we find a missing frame, it makes sense to
+ * grow, because the frame might just be a bit late;
+ * otherwise, we presently get into a pattern where we return
+ * INTERP for the lost frame, then it shows up next, and we
+ * throw it away because it's late */
+ /* I've recently only been able to replicate this using
+ * iaxclient talking to app_echo on asterisk. In this case,
+ * my outgoing packets go through asterisk's (old)
+ * jitterbuffer, and then might get an unusual increasing delay
+ * there if it decides to grow?? */
+ /* Update: that might have been a different bug, that has been fixed..
+ * But, this still seemed like a good idea, except that it ended up making a single actual
+ * lost frame get interpolated two or more times, when there was "room" to grow, so it might
+ * be a bit of a bad idea overall */
+ /*if (diff > -1 * jb->info.last_voice_ms) {
+ jb->info.current += jb->info.last_voice_ms;
+ jb->info.last_adjustment = now;
+ jb_warn("g");
+ return JB_INTERP;
+ } */
+ jb->info.frames_lost++;
+ increment_losspct(jb);
+ jb->info.next_voice_ts += interpl;
+ jb->info.last_voice_ms = interpl;
+ jb->info.cnt_contig_interp++;
+ if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
+ jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
+ }
+ jb_dbg("L");
+ return JB_INTERP;
+ }
+
+ /* normal case; return the frame, increment stuff */
+ *frameout = *frame;
+ jb->info.next_voice_ts += frame->ms;
+ jb->info.frames_out++;
+ jb->info.cnt_contig_interp = 0;
+ decrement_losspct(jb);
+ jb_dbg("v");
+ return JB_OK;
+ } else {
+ /* TODO: after we get the non-silent case down, we'll make the
+ * silent case -- basically, we'll just grow and shrink faster
+ * here, plus handle next_voice_ts a bit differently */
+
+ /* to disable silent special case altogether, just uncomment this: */
+ /* jb->info.silence_begin_ts = 0; */
+
+ /* shrink interpl len every 10ms during silence */
+ if (diff < -jb->info.conf.target_extra &&
+ jb->info.last_adjustment + 10 <= now) {
+ jb->info.current -= interpl;
+ jb->info.last_adjustment = now;
+ }
+
+ frame = queue_get(jb, now - jb->info.current);
+ if (!frame) {
+ return JB_NOFRAME;
+ } else if (frame->type != JB_TYPE_VOICE) {
+ /* normal case; in silent mode, got a non-voice frame */
+ *frameout = *frame;
+ jb->info.frames_out++;
+ return JB_OK;
+ }
+ if (frame->ts < jb->info.silence_begin_ts) {
+ /* voice frame is late */
+ *frameout = *frame;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_late++;
+ jb->info.frames_lost--;
+ jb_dbg("l");
+ /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
+ jb_warninfo(jb); */
+ return JB_DROP;
+ } else {
+ /* voice frame */
+ /* try setting current to target right away here */
+ jb->info.current = jb->info.target;
+ jb->info.silence_begin_ts = 0;
+ jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
+ jb->info.last_voice_ms = frame->ms;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ *frameout = *frame;
+ jb_dbg("V");
+ return JB_OK;
+ }
+ }
+}
+
+long jb_next(jitterbuf *jb)
+{
+ if (jb->info.silence_begin_ts) {
+ if (jb->frames) {
+ long next = queue_next(jb);
+ history_get(jb);
+ /* shrink during silence */
+ if (jb->info.target - jb->info.current < -jb->info.conf.target_extra)
+ return jb->info.last_adjustment + 10;
+ return next + jb->info.target;
+ }
+ else
+ return JB_LONGMAX;
+ } else {
+ return jb->info.next_voice_ts;
+ }
+}
+
+enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl)
+{
+ enum jb_return_code ret = _jb_get(jb, frameout, now, interpl);
+#if 0
+ static int lastts=0;
+ int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0;
+ jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists);
+ if (thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n");
+ lastts = thists;
+#endif
+ if (ret == JB_INTERP)
+ frameout->ms = jb->info.last_voice_ms;
+
+ return ret;
+}
+
+enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout)
+{
+ jb_frame *frame;
+ frame = queue_getall(jb);
+
+ if (!frame) {
+ return JB_NOFRAME;
+ }
+
+ *frameout = *frame;
+ return JB_OK;
+}
+
+
+enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats)
+{
+
+ history_get(jb);
+
+ *stats = jb->info;
+
+ return JB_OK;
+}
+
+enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf)
+{
+ /* take selected settings from the struct */
+
+ jb->info.conf.max_jitterbuf = conf->max_jitterbuf;
+ jb->info.conf.resync_threshold = conf->resync_threshold;
+ jb->info.conf.max_contig_interp = conf->max_contig_interp;
+
+ /* -1 indicates use of the default JB_TARGET_EXTRA value */
+ jb->info.conf.target_extra = ( conf->target_extra == -1 )
+ ? JB_TARGET_EXTRA
+ : conf->target_extra
+ ;
+
+ /* update these to match new target_extra setting */
+ jb->info.current = jb->info.conf.target_extra;
+ jb->info.target = jb->info.conf.target_extra;
+
+ return JB_OK;
+}
+
+
diff --git a/trunk/main/libresample/LICENSE.txt b/trunk/main/libresample/LICENSE.txt
new file mode 100644
index 000000000..4ccd6ccfd
--- /dev/null
+++ b/trunk/main/libresample/LICENSE.txt
@@ -0,0 +1,463 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 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.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/trunk/main/libresample/Makefile.asterisk b/trunk/main/libresample/Makefile.asterisk
new file mode 100644
index 000000000..815671a2f
--- /dev/null
+++ b/trunk/main/libresample/Makefile.asterisk
@@ -0,0 +1,11 @@
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+ASTCFLAGS+= -Isrc -Iinclude
+
+libresample.a: src/resample.o src/resamplesubs.o src/filterkit.o
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+clean::
+ rm -f src/*.o libresample.a
diff --git a/trunk/main/libresample/Makefile.in b/trunk/main/libresample/Makefile.in
new file mode 100644
index 000000000..8d17d19b4
--- /dev/null
+++ b/trunk/main/libresample/Makefile.in
@@ -0,0 +1,52 @@
+# Run configure to generate Makefile from Makefile.in on
+# any system supported by GNU autoconf. For all other
+# systems, use this file as a template to create a
+# working Makefile.
+
+CC = @CC@
+CFLAGS = @CFLAGS@ -Wall
+
+LIBS = @LIBS@ -lm
+
+AR = @AR@
+RANLIB = @RANLIB@
+
+OBJS = \
+ src/resample.c.o \
+ src/resamplesubs.c.o \
+ src/filterkit.c.o
+
+TARGETS = @TARGETS@
+
+all: $(TARGETS)
+
+libresample.a: $(OBJS) Makefile
+ $(AR) ruv libresample.a $(OBJS)
+ ranlib libresample.a
+
+tests/testresample: libresample.a tests/testresample.c
+ $(CC) -o tests/testresample \
+ $(CFLAGS) tests/testresample.c \
+ libresample.a $(LIBS)
+
+tests/compareresample: libresample.a tests/compareresample.c
+ $(CC) -o tests/compareresample \
+ $(CFLAGS) tests/compareresample.c \
+ libresample.a -lsamplerate $(LIBS)
+
+tests/resample-sndfile: libresample.a tests/resample-sndfile.c
+ $(CC) -o tests/resample-sndfile \
+ $(CFLAGS) tests/resample-sndfile.c \
+ libresample.a -lsndfile $(LIBS)
+
+clean:
+ rm -f $(TARGETS) $(OBJS)
+
+distclean: clean
+ rm -f Makefile
+ rm -f config.status config.cache config.log src/config.h
+ rm -f *~ src/*~ tests/*~ include/*~
+
+%.c.o: %.c Makefile include/libresample.h \
+ src/resample_defs.h src/filterkit.h src/config.h
+ $(CC) -c $(CFLAGS) $< -o $@
diff --git a/trunk/main/libresample/README.txt b/trunk/main/libresample/README.txt
new file mode 100644
index 000000000..14be45b83
--- /dev/null
+++ b/trunk/main/libresample/README.txt
@@ -0,0 +1,84 @@
+libresample
+
+Real-time library interface by Dominic Mazzoni
+
+Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+License: LGPL - see the file LICENSE.txt for more information
+
+History:
+
+This library is not the highest-quality resampling library
+available, nor is it the most flexible, nor is it the
+fastest. But it is pretty good in all of these regards, and
+it is quite portable. The best resampling library I am aware
+of is libsamplerate by Erik de Castro Lopo. It's small, fast,
+and very high quality. However, it uses the GPL for its
+license (with commercial options available) and I needed
+a more free library. So I wrote this library, using
+the LGPL resample-1.7 library by Julius Smith as a basis.
+
+Resample-1.7 is a fixed-point resampler, and as a result
+has only limited precision. I rewrote it to use single-precision
+floating-point arithmetic instead and increased the number
+of filter coefficients between time steps significantly.
+On modern processors it can resample in real time even
+with this extra overhead.
+
+Resample-1.7 was designed to read and write from files, so
+I removed all of that code and replaced it with an API that
+lets you pass samples in small chunks. It should be easy
+to link to resample-1.7 as a library.
+
+Changes in version 0.1.3:
+
+* Fixed two bugs that were causing subtle problems
+ on Intel x86 processors due to differences in roundoff errors.
+
+* Prefixed most function names with lrs and changed header file
+ from resample.h to libresample.h, to avoid namespace
+ collisions with existing programs and libraries.
+
+* Added resample_dup (thanks to Glenn Maynard)
+
+* Argument to resample_get_filter_width takes a const void *
+ (thanks to Glenn Maynard)
+
+* resample-sndfile clips output to -1...1 (thanks to Glenn Maynard)
+
+Usage notes:
+
+- If the output buffer you pass is too small, resample_process
+ may not use any input samples because its internal output
+ buffer is too full to process any more. So do not assume
+ that it is an error just because no input samples were
+ consumed. Just keep passing valid output buffers.
+
+- Given a resampling factor f > 1, and a number of input
+ samples n, the number of output samples should be between
+ floor(n - f) and ceil(n + f). In other words, if you
+ resample 1000 samples at a factor of 8, the number of
+ output samples might be between 7992 and 8008. Do not
+ assume that it will be exactly 8000. If you need exactly
+ 8000 outputs, pad the input with extra zeros as necessary.
+
+License and warranty:
+
+All of the files in this package are Copyright 2003 by Dominic
+Mazzoni <dominic@minorninth.com>. This library was based heavily
+on Resample-1.7, Copyright 1994-2002 by Julius O. Smith III
+<jos@ccrma.stanford.edu>, all rights reserved.
+
+Permission to use and copy is granted subject to the terms of the
+"GNU Lesser General Public License" (LGPL) as published by the
+Free Software Foundation; either version 2.1 of the License,
+or any later version. In addition, Julius O. Smith III requests
+that a copy of any modified files be sent by email to
+jos@ccrma.stanford.edu so that he may incorporate them into the
+CCRMA version.
+
+ This library 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
+ Lesser General Public License for more details.
diff --git a/trunk/main/libresample/config.guess b/trunk/main/libresample/config.guess
new file mode 100755
index 000000000..e8c6fc0c3
--- /dev/null
+++ b/trunk/main/libresample/config.guess
@@ -0,0 +1,1432 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2004-01-05'
+
+# This file 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.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pegasos:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha*:OpenVMS:*:*)
+ echo alpha-hp-vms
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ # GNU/KFreeBSD systems have a "k" prefix to indicate we are using
+ # FreeBSD's kernel, but not the complete OS.
+ case ${LIBC} in gnu) kernel_only='k' ;; esac
+ echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+ *:DRAGONFLY:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly${UNAME_RELEASE}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/libresample/config.sub b/trunk/main/libresample/config.sub
new file mode 100755
index 000000000..463186dbf
--- /dev/null
+++ b/trunk/main/libresample/config.sub
@@ -0,0 +1,1537 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2004-01-05'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file 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.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | msp430 \
+ | ns16k | ns32k \
+ | openrisc | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32r-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | msp430-* \
+ | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+ | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nv1)
+ basic_machine=nv1-cray
+ os=-unicosmp
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ or32 | or32-*)
+ basic_machine=or32-unknown
+ os=-coff
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -netbsd* | -openbsd* | -kfreebsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/libresample/configure b/trunk/main/libresample/configure
new file mode 100755
index 000000000..638280399
--- /dev/null
+++ b/trunk/main/libresample/configure
@@ -0,0 +1,4552 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="src/resample.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT RANLIB ac_ct_RANLIB AR TARGETS CPP EGREP LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
+ headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd "$ac_popdir"
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CC" && break
+done
+
+ CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+ (eval $ac_link_default) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Find the output, starting from the most likely. This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+ ;;
+ conftest.$ac_ext )
+ # This is the source file.
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ # FIXME: I believe we export ac_cv_exeext for Libtool,
+ # but it would be cool to find out if it's true. Does anybody
+ # maintain Libtool? --akim.
+ export ac_cv_exeext
+ break;;
+ * )
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ export ac_cv_exeext
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std1 is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std1. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX 10.20 and later -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno)
+ echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+ *)
+ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+ CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C. Since we use `exit',
+# in C++ we need to declare it. In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+ choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+fi
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ RANLIB=$ac_ct_RANLIB
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_path_AR" && ac_cv_path_AR="no"
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+
+if test -n "$AR"; then
+ echo "$as_me:$LINENO: result: $AR" >&5
+echo "${ECHO_T}$AR" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+if [ $AR = "no" ] ; then
+ { { echo "$as_me:$LINENO: error: \"Could not find ar - needed to create a library\"" >&5
+echo "$as_me: error: \"Could not find ar - needed to create a library\"" >&2;}
+ { (exit 1); exit 1; }; };
+fi
+
+
+TARGETS="libresample.a tests/testresample"
+
+
+echo "$as_me:$LINENO: checking for sf_open in -lsndfile" >&5
+echo $ECHO_N "checking for sf_open in -lsndfile... $ECHO_C" >&6
+if test "${ac_cv_lib_sndfile_sf_open+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsndfile $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char sf_open ();
+int
+main ()
+{
+sf_open ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_sndfile_sf_open=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_sndfile_sf_open=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_sndfile_sf_open" >&5
+echo "${ECHO_T}$ac_cv_lib_sndfile_sf_open" >&6
+if test $ac_cv_lib_sndfile_sf_open = yes; then
+ have_libsndfile=yes
+else
+ have_libsndfile=no
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+ then ac_cv_prog_egrep='grep -E'
+ else ac_cv_prog_egrep='egrep'
+ fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_header_stdc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ exit(2);
+ exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "${ac_cv_header_sndfile_h+set}" = set; then
+ echo "$as_me:$LINENO: checking for sndfile.h" >&5
+echo $ECHO_N "checking for sndfile.h... $ECHO_C" >&6
+if test "${ac_cv_header_sndfile_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sndfile_h" >&5
+echo "${ECHO_T}$ac_cv_header_sndfile_h" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking sndfile.h usability" >&5
+echo $ECHO_N "checking sndfile.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sndfile.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking sndfile.h presence" >&5
+echo $ECHO_N "checking sndfile.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sndfile.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sndfile.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sndfile.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sndfile.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sndfile.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sndfile.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sndfile.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sndfile.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sndfile.h: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for sndfile.h" >&5
+echo $ECHO_N "checking for sndfile.h... $ECHO_C" >&6
+if test "${ac_cv_header_sndfile_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sndfile_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sndfile_h" >&5
+echo "${ECHO_T}$ac_cv_header_sndfile_h" >&6
+
+fi
+if test $ac_cv_header_sndfile_h = yes; then
+ :
+else
+ have_libsndfile=no
+fi
+
+
+
+if [ $have_libsndfile = "yes" ] ; then
+ TARGETS="$TARGETS tests/resample-sndfile"
+fi
+
+echo "$as_me:$LINENO: checking for src_simple in -lsamplerate" >&5
+echo $ECHO_N "checking for src_simple in -lsamplerate... $ECHO_C" >&6
+if test "${ac_cv_lib_samplerate_src_simple+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsamplerate $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char src_simple ();
+int
+main ()
+{
+src_simple ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_samplerate_src_simple=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_samplerate_src_simple=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_samplerate_src_simple" >&5
+echo "${ECHO_T}$ac_cv_lib_samplerate_src_simple" >&6
+if test $ac_cv_lib_samplerate_src_simple = yes; then
+ have_libsamplerate=yes
+else
+ have_libsamplerate=no
+fi
+
+if test "${ac_cv_header_samplerate_h+set}" = set; then
+ echo "$as_me:$LINENO: checking for samplerate.h" >&5
+echo $ECHO_N "checking for samplerate.h... $ECHO_C" >&6
+if test "${ac_cv_header_samplerate_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_samplerate_h" >&5
+echo "${ECHO_T}$ac_cv_header_samplerate_h" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking samplerate.h usability" >&5
+echo $ECHO_N "checking samplerate.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <samplerate.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking samplerate.h presence" >&5
+echo $ECHO_N "checking samplerate.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <samplerate.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: samplerate.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: samplerate.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: samplerate.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: samplerate.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: samplerate.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: samplerate.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: samplerate.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: samplerate.h: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for samplerate.h" >&5
+echo $ECHO_N "checking for samplerate.h... $ECHO_C" >&6
+if test "${ac_cv_header_samplerate_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_samplerate_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_samplerate_h" >&5
+echo "${ECHO_T}$ac_cv_header_samplerate_h" >&6
+
+fi
+if test $ac_cv_header_samplerate_h = yes; then
+ :
+else
+ have_libsamplerate=no
+fi
+
+
+
+if [ $have_libsamplerate = "yes" ] ; then
+ TARGETS="$TARGETS tests/compareresample"
+fi
+
+
+for ac_header in inttypes.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ ac_config_headers="$ac_config_headers src/config.h:src/configtemplate.h"
+
+ ac_config_files="$ac_config_files Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "src/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/config.h:src/configtemplate.h" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@AR@,$AR,;t t
+s,@TARGETS@,$TARGETS,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='[ ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ # Do quote $f, to prevent DOS paths from being IFS'd.
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+ # Remove the trailing spaces.
+ sed 's/[ ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h. The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status. Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless. Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo ' :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+ # Write a limited-size here document to $tmp/defines.sed.
+ echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#define' lines.
+ echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/defines.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+ rm -f conftest.defines
+ mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo ' fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+ # Write a limited-size here document to $tmp/undefs.sed.
+ echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#undef'
+ echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+ rm -f conftest.undefs
+ mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ echo "/* Generated by configure. */" >$tmp/config.h
+ else
+ echo "/* $ac_file. Generated by configure. */" >$tmp/config.h
+ fi
+ cat $tmp/in >>$tmp/config.h
+ rm -f $tmp/in
+ if test x"$ac_file" != x-; then
+ if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ rm -f $ac_file
+ mv $tmp/config.h $ac_file
+ fi
+ else
+ cat $tmp/config.h
+ rm -f $tmp/config.h
+ fi
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+echo ""
+
+if [ $have_libsamplerate = "yes" ] ; then
+ echo "Configured to build tests/resample-sndfile using libsndfile"
+ echo ""
+else
+ echo "Could not find libsndfile - needed if you want to"
+ echo "compile tests/resample-sndfile"
+ echo ""
+fi
+
+if [ $have_libsamplerate = "yes" ] ; then
+ echo "Configured to build tests/compareresample to compare against"
+ echo "Erik de Castro Lopo's libsamplerate library."
+ echo ""
+else
+ echo "Could not find libsamplerate - only needed if you want to"
+ echo "compile tests/compareresample to compare their performance."
+ echo ""
+fi
+
+echo "Type 'configure --help' to see options."
+echo ""
+echo "Type 'make' to build libresample and tests."
diff --git a/trunk/main/libresample/configure.in b/trunk/main/libresample/configure.in
new file mode 100644
index 000000000..e676b69f2
--- /dev/null
+++ b/trunk/main/libresample/configure.in
@@ -0,0 +1,68 @@
+dnl
+dnl libresample configure.in script
+dnl
+dnl Dominic Mazzoni
+dnl
+
+dnl Require autoconf >= 2.13
+AC_PREREQ(2.13)
+
+dnl Init autoconf and make sure configure is being called
+dnl from the right directory
+AC_INIT([src/resample.c])
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_RANLIB
+
+AC_PATH_PROG(AR, ar, no)
+if [[ $AR = "no" ]] ; then
+ AC_MSG_ERROR("Could not find ar - needed to create a library");
+fi
+
+AC_SUBST(TARGETS)
+TARGETS="libresample.a tests/testresample"
+
+AC_CHECK_LIB(sndfile, sf_open, have_libsndfile=yes, have_libsndfile=no)
+AC_CHECK_HEADER(sndfile.h, , have_libsndfile=no)
+
+if [[ $have_libsndfile = "yes" ]] ; then
+ TARGETS="$TARGETS tests/resample-sndfile"
+fi
+
+AC_CHECK_LIB(samplerate, src_simple, have_libsamplerate=yes, have_libsamplerate=no)
+AC_CHECK_HEADER(samplerate.h, , have_libsamplerate=no)
+
+if [[ $have_libsamplerate = "yes" ]] ; then
+ TARGETS="$TARGETS tests/compareresample"
+fi
+
+AC_CHECK_HEADERS(inttypes.h)
+
+AC_CONFIG_HEADER(src/config.h:src/configtemplate.h)
+AC_OUTPUT([Makefile])
+
+echo ""
+
+if [[ $have_libsamplerate = "yes" ]] ; then
+ echo "Configured to build tests/resample-sndfile using libsndfile"
+ echo ""
+else
+ echo "Could not find libsndfile - needed if you want to"
+ echo "compile tests/resample-sndfile"
+ echo ""
+fi
+
+if [[ $have_libsamplerate = "yes" ]] ; then
+ echo "Configured to build tests/compareresample to compare against"
+ echo "Erik de Castro Lopo's libsamplerate library."
+ echo ""
+else
+ echo "Could not find libsamplerate - only needed if you want to"
+ echo "compile tests/compareresample to compare their performance."
+ echo ""
+fi
+
+echo "Type 'configure --help' to see options."
+echo ""
+echo "Type 'make' to build libresample and tests."
diff --git a/trunk/main/libresample/include/libresample.h b/trunk/main/libresample/include/libresample.h
new file mode 100644
index 000000000..e5ce4851a
--- /dev/null
+++ b/trunk/main/libresample/include/libresample.h
@@ -0,0 +1,120 @@
+/**********************************************************************
+
+ resample.h
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+/*!
+ * \file
+ * \brief libresample API
+ * \author Dominic Mazzoni
+ */
+
+#ifndef LIBRESAMPLE_INCLUDED
+#define LIBRESAMPLE_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*!
+ * \brief Create a resampler
+ *
+ * \arg highQuality Set this argument to non-zero to enable higher quality
+ * resampling.
+ * \arg minFactor This is the minimum resampling factor that will be used for
+ * this resampler. The resampling factor is calculated in the following
+ * way: ( from sample rate / to sample rate ).
+ * \arg maxFactor This is the maximum resampling factor that will be used for
+ * this resampler.
+ *
+ * Use this function to create a new resampler that will maintain state
+ * information about the stream of audio being resampled.
+ *
+ * \return A handle to a new resampler
+ */
+void *resample_open(int highQuality,
+ double minFactor,
+ double maxFactor);
+
+/*!
+ * \brief Duplicate a resampler
+ *
+ * \arg handle the resampler to duplicate
+ *
+ * \return A new handle to a resampler, initialized with the same parameters
+ * used to create the original resampler.
+ */
+void *resample_dup(const void *handle);
+
+/*!
+ * \brief Get filter width for resampler
+ *
+ * \arg handle the resampler
+ *
+ * \return the filter width.
+ */
+int resample_get_filter_width(const void *handle);
+
+/*!
+ * \brief Resample a chunk of audio
+ *
+ * \arg handle the resampler
+ * \arg factor the resampling factor. This factor should be calculated as
+ * ( from sample rate / to sample rate ). So, for converting from 8 kHz
+ * to 16 kHz, this value would be 2.0.
+ * \arg inBuffer the input buffer for audio to resample.
+ * \arg inBufferLen the number of samples in the input buffer
+ * \arg lastFlag Set this argument to non-zero if the data in the input buffer
+ * is known to be the end of a stream. This would be used if you're
+ * resampling a file, for example.
+ * \arg inBufferUsed This is an output parameter that indicates how many
+ * samples were consumed from the input buffer. Generally, this function
+ * is called in a loop until you know that the entire input buffer has
+ * been consumed, as it may take multiple calls to complete.
+ * \arg outBuffer This is the output buffer. This function will write the
+ * resampled audio into this buffer.
+ * \arg outBufferLen This parameter specifies how many samples there is room
+ * for in the output buffer.
+ *
+ * This is the main function used for resampling audio. It should be called
+ * in a loop until all of the data from the input buffer is consumed, or the
+ * output buffer has been filled.
+ *
+ * \return the number of samples written to the output buffer. If the return
+ * value is equal to the value provided in the outBufferLen parameter,
+ * then the output buffer has been filled.
+ */
+int resample_process(void *handle,
+ double factor,
+ float *inBuffer,
+ int inBufferLen,
+ int lastFlag,
+ int *inBufferUsed,
+ float *outBuffer,
+ int outBufferLen);
+
+/*!
+ * \brief Close a resampler
+ *
+ * \arg handle the resampler to close
+ *
+ * Use this function to release a handle to a resampler that was created using
+ * either resample_open() or resample_dup().
+ *
+ * \return nothing.
+ */
+void resample_close(void *handle);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* LIBRESAMPLE_INCLUDED */
diff --git a/trunk/main/libresample/install-sh b/trunk/main/libresample/install-sh
new file mode 100755
index 000000000..e9de23842
--- /dev/null
+++ b/trunk/main/libresample/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, 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 name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/trunk/main/libresample/src/configtemplate.h b/trunk/main/libresample/src/configtemplate.h
new file mode 100644
index 000000000..94ae1cea9
--- /dev/null
+++ b/trunk/main/libresample/src/configtemplate.h
@@ -0,0 +1,7 @@
+/* Run configure to generate config.h automatically on any
+ system supported by GNU autoconf. For all other systems,
+ use this file as a template to create config.h
+*/
+
+#undef HAVE_INTTYPES_H
+
diff --git a/trunk/main/libresample/src/filterkit.c b/trunk/main/libresample/src/filterkit.c
new file mode 100644
index 000000000..bc92285f2
--- /dev/null
+++ b/trunk/main/libresample/src/filterkit.c
@@ -0,0 +1,215 @@
+/**********************************************************************
+
+ resamplesubs.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+ This file provides Kaiser-windowed low-pass filter support,
+ including a function to create the filter coefficients, and
+ two functions to apply the filter at a particular point.
+
+**********************************************************************/
+
+/* Definitions */
+#include "resample_defs.h"
+
+#include "filterkit.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+/* LpFilter()
+ *
+ * reference: "Digital Filters, 2nd edition"
+ * R.W. Hamming, pp. 178-179
+ *
+ * Izero() computes the 0th order modified bessel function of the first kind.
+ * (Needed to compute Kaiser window).
+ *
+ * LpFilter() computes the coeffs of a Kaiser-windowed low pass filter with
+ * the following characteristics:
+ *
+ * c[] = array in which to store computed coeffs
+ * frq = roll-off frequency of filter
+ * N = Half the window length in number of coeffs
+ * Beta = parameter of Kaiser window
+ * Num = number of coeffs before 1/frq
+ *
+ * Beta trades the rejection of the lowpass filter against the transition
+ * width from passband to stopband. Larger Beta means a slower
+ * transition and greater stopband rejection. See Rabiner and Gold
+ * (Theory and Application of DSP) under Kaiser windows for more about
+ * Beta. The following table from Rabiner and Gold gives some feel
+ * for the effect of Beta:
+ *
+ * All ripples in dB, width of transition band = D*N where N = window length
+ *
+ * BETA D PB RIP SB RIP
+ * 2.120 1.50 +-0.27 -30
+ * 3.384 2.23 0.0864 -40
+ * 4.538 2.93 0.0274 -50
+ * 5.658 3.62 0.00868 -60
+ * 6.764 4.32 0.00275 -70
+ * 7.865 5.0 0.000868 -80
+ * 8.960 5.7 0.000275 -90
+ * 10.056 6.4 0.000087 -100
+ */
+
+#define IzeroEPSILON 1E-21 /* Max error acceptable in Izero */
+
+static double Izero(double x)
+{
+ double sum, u, halfx, temp;
+ int n;
+
+ sum = u = n = 1;
+ halfx = x/2.0;
+ do {
+ temp = halfx/(double)n;
+ n += 1;
+ temp *= temp;
+ u *= temp;
+ sum += u;
+ } while (u >= IzeroEPSILON*sum);
+ return(sum);
+}
+
+void lrsLpFilter(double c[], int N, double frq, double Beta, int Num)
+{
+ double IBeta, temp, temp1, inm1;
+ int i;
+
+ /* Calculate ideal lowpass filter impulse response coefficients: */
+ c[0] = 2.0*frq;
+ for (i=1; i<N; i++) {
+ temp = PI*(double)i/(double)Num;
+ c[i] = sin(2.0*temp*frq)/temp; /* Analog sinc function, cutoff = frq */
+ }
+
+ /*
+ * Calculate and Apply Kaiser window to ideal lowpass filter.
+ * Note: last window value is IBeta which is NOT zero.
+ * You're supposed to really truncate the window here, not ramp
+ * it to zero. This helps reduce the first sidelobe.
+ */
+ IBeta = 1.0/Izero(Beta);
+ inm1 = 1.0/((double)(N-1));
+ for (i=1; i<N; i++) {
+ temp = (double)i * inm1;
+ temp1 = 1.0 - temp*temp;
+ temp1 = (temp1<0? 0: temp1); /* make sure it's not negative since
+ we're taking the square root - this
+ happens on Pentium 4's due to tiny
+ roundoff errors */
+ c[i] *= Izero(Beta*sqrt(temp1)) * IBeta;
+ }
+}
+
+float lrsFilterUp(float Imp[], /* impulse response */
+ float ImpD[], /* impulse response deltas */
+ UWORD Nwing, /* len of one wing of filter */
+ BOOL Interp, /* Interpolate coefs using deltas? */
+ float *Xp, /* Current sample */
+ double Ph, /* Phase */
+ int Inc) /* increment (1 for right wing or -1 for left) */
+{
+ float *Hp, *Hdp = NULL, *End;
+ double a = 0;
+ float v, t;
+
+ Ph *= Npc; /* Npc is number of values per 1/delta in impulse response */
+
+ v = 0.0; /* The output value */
+ Hp = &Imp[(int)Ph];
+ End = &Imp[Nwing];
+ if (Interp) {
+ Hdp = &ImpD[(int)Ph];
+ a = Ph - floor(Ph); /* fractional part of Phase */
+ }
+
+ if (Inc == 1) /* If doing right wing... */
+ { /* ...drop extra coeff, so when Ph is */
+ End--; /* 0.5, we don't do too many mult's */
+ if (Ph == 0) /* If the phase is zero... */
+ { /* ...then we've already skipped the */
+ Hp += Npc; /* first sample, so we must also */
+ Hdp += Npc; /* skip ahead in Imp[] and ImpD[] */
+ }
+ }
+
+ if (Interp)
+ while (Hp < End) {
+ t = *Hp; /* Get filter coeff */
+ t += (*Hdp)*a; /* t is now interp'd filter coeff */
+ Hdp += Npc; /* Filter coeff differences step */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Hp += Npc; /* Filter coeff step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+ else
+ while (Hp < End) {
+ t = *Hp; /* Get filter coeff */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Hp += Npc; /* Filter coeff step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+
+ return v;
+}
+
+float lrsFilterUD(float Imp[], /* impulse response */
+ float ImpD[], /* impulse response deltas */
+ UWORD Nwing, /* len of one wing of filter */
+ BOOL Interp, /* Interpolate coefs using deltas? */
+ float *Xp, /* Current sample */
+ double Ph, /* Phase */
+ int Inc, /* increment (1 for right wing or -1 for left) */
+ double dhb) /* filter sampling period */
+{
+ float a;
+ float *Hp, *Hdp, *End;
+ float v, t;
+ double Ho;
+
+ v = 0.0; /* The output value */
+ Ho = Ph*dhb;
+ End = &Imp[Nwing];
+ if (Inc == 1) /* If doing right wing... */
+ { /* ...drop extra coeff, so when Ph is */
+ End--; /* 0.5, we don't do too many mult's */
+ if (Ph == 0) /* If the phase is zero... */
+ Ho += dhb; /* ...then we've already skipped the */
+ } /* first sample, so we must also */
+ /* skip ahead in Imp[] and ImpD[] */
+
+ if (Interp)
+ while ((Hp = &Imp[(int)Ho]) < End) {
+ t = *Hp; /* Get IR sample */
+ Hdp = &ImpD[(int)Ho]; /* get interp bits from diff table*/
+ a = Ho - floor(Ho); /* a is logically between 0 and 1 */
+ t += (*Hdp)*a; /* t is now interp'd filter coeff */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Ho += dhb; /* IR step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+ else
+ while ((Hp = &Imp[(int)Ho]) < End) {
+ t = *Hp; /* Get IR sample */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Ho += dhb; /* IR step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+
+ return v;
+}
diff --git a/trunk/main/libresample/src/filterkit.h b/trunk/main/libresample/src/filterkit.h
new file mode 100644
index 000000000..9df0ae869
--- /dev/null
+++ b/trunk/main/libresample/src/filterkit.h
@@ -0,0 +1,28 @@
+/**********************************************************************
+
+ resamplesubs.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+/* Definitions */
+#include "resample_defs.h"
+
+/*
+ * FilterUp() - Applies a filter to a given sample when up-converting.
+ * FilterUD() - Applies a filter to a given sample when up- or down-
+ */
+
+float lrsFilterUp(float Imp[], float ImpD[], UWORD Nwing, BOOL Interp,
+ float *Xp, double Ph, int Inc);
+
+float lrsFilterUD(float Imp[], float ImpD[], UWORD Nwing, BOOL Interp,
+ float *Xp, double Ph, int Inc, double dhb);
+
+void lrsLpFilter(double c[], int N, double frq, double Beta, int Num);
diff --git a/trunk/main/libresample/src/resample.c b/trunk/main/libresample/src/resample.c
new file mode 100644
index 000000000..85ff75f76
--- /dev/null
+++ b/trunk/main/libresample/src/resample.c
@@ -0,0 +1,347 @@
+/**********************************************************************
+
+ resample.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+ This is the main source file, implementing all of the API
+ functions and handling all of the buffering logic.
+
+**********************************************************************/
+
+/* External interface */
+#include "../include/libresample.h"
+
+/* Definitions */
+#include "resample_defs.h"
+
+#include "filterkit.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+typedef struct {
+ float *Imp;
+ float *ImpD;
+ float LpScl;
+ UWORD Nmult;
+ UWORD Nwing;
+ double minFactor;
+ double maxFactor;
+ UWORD XSize;
+ float *X;
+ UWORD Xp; /* Current "now"-sample pointer for input */
+ UWORD Xread; /* Position to put new samples */
+ UWORD Xoff;
+ UWORD YSize;
+ float *Y;
+ UWORD Yp;
+ double Time;
+} rsdata;
+
+void *resample_dup(const void * handle)
+{
+ const rsdata *cpy = (const rsdata *)handle;
+ rsdata *hp = (rsdata *)malloc(sizeof(rsdata));
+
+ hp->minFactor = cpy->minFactor;
+ hp->maxFactor = cpy->maxFactor;
+ hp->Nmult = cpy->Nmult;
+ hp->LpScl = cpy->LpScl;
+ hp->Nwing = cpy->Nwing;
+
+ hp->Imp = (float *)malloc(hp->Nwing * sizeof(float));
+ memcpy(hp->Imp, cpy->Imp, hp->Nwing * sizeof(float));
+ hp->ImpD = (float *)malloc(hp->Nwing * sizeof(float));
+ memcpy(hp->ImpD, cpy->ImpD, hp->Nwing * sizeof(float));
+
+ hp->Xoff = cpy->Xoff;
+ hp->XSize = cpy->XSize;
+ hp->X = (float *)malloc((hp->XSize + hp->Xoff) * sizeof(float));
+ memcpy(hp->X, cpy->X, (hp->XSize + hp->Xoff) * sizeof(float));
+ hp->Xp = cpy->Xp;
+ hp->Xread = cpy->Xread;
+ hp->YSize = cpy->YSize;
+ hp->Y = (float *)malloc(hp->YSize * sizeof(float));
+ memcpy(hp->Y, cpy->Y, hp->YSize * sizeof(float));
+ hp->Yp = cpy->Yp;
+ hp->Time = cpy->Time;
+
+ return (void *)hp;
+}
+
+void *resample_open(int highQuality, double minFactor, double maxFactor)
+{
+ double *Imp64;
+ double Rolloff, Beta;
+ rsdata *hp;
+ UWORD Xoff_min, Xoff_max;
+ int i;
+
+ /* Just exit if we get invalid factors */
+ if (minFactor <= 0.0 || maxFactor <= 0.0 || maxFactor < minFactor) {
+ #if defined(DEBUG)
+ fprintf(stderr,
+ "libresample: "
+ "minFactor and maxFactor must be positive real numbers,\n"
+ "and maxFactor should be larger than minFactor.\n");
+ #endif
+ return 0;
+ }
+
+ hp = (rsdata *)malloc(sizeof(rsdata));
+
+ hp->minFactor = minFactor;
+ hp->maxFactor = maxFactor;
+
+ if (highQuality)
+ hp->Nmult = 35;
+ else
+ hp->Nmult = 11;
+
+ hp->LpScl = 1.0;
+ hp->Nwing = Npc*(hp->Nmult-1)/2; /* # of filter coeffs in right wing */
+
+ Rolloff = 0.90;
+ Beta = 6;
+
+ Imp64 = (double *)malloc(hp->Nwing * sizeof(double));
+
+ lrsLpFilter(Imp64, hp->Nwing, 0.5*Rolloff, Beta, Npc);
+
+ hp->Imp = (float *)malloc(hp->Nwing * sizeof(float));
+ hp->ImpD = (float *)malloc(hp->Nwing * sizeof(float));
+ for(i=0; i<hp->Nwing; i++)
+ hp->Imp[i] = Imp64[i];
+
+ /* Storing deltas in ImpD makes linear interpolation
+ of the filter coefficients faster */
+ for (i=0; i<hp->Nwing-1; i++)
+ hp->ImpD[i] = hp->Imp[i+1] - hp->Imp[i];
+
+ /* Last coeff. not interpolated */
+ hp->ImpD[hp->Nwing-1] = - hp->Imp[hp->Nwing-1];
+
+ free(Imp64);
+
+ /* Calc reach of LP filter wing (plus some creeping room) */
+ Xoff_min = ((hp->Nmult+1)/2.0) * MAX(1.0, 1.0/minFactor) + 10;
+ Xoff_max = ((hp->Nmult+1)/2.0) * MAX(1.0, 1.0/maxFactor) + 10;
+ hp->Xoff = MAX(Xoff_min, Xoff_max);
+
+ /* Make the inBuffer size at least 4096, but larger if necessary
+ in order to store the minimum reach of the LP filter and then some.
+ Then allocate the buffer an extra Xoff larger so that
+ we can zero-pad up to Xoff zeros at the end when we reach the
+ end of the input samples. */
+ hp->XSize = MAX(2*hp->Xoff+10, 4096);
+ hp->X = (float *)malloc((hp->XSize + hp->Xoff) * sizeof(float));
+ hp->Xp = hp->Xoff;
+ hp->Xread = hp->Xoff;
+
+ /* Need Xoff zeros at begining of X buffer */
+ for(i=0; i<hp->Xoff; i++)
+ hp->X[i]=0;
+
+ /* Make the outBuffer long enough to hold the entire processed
+ output of one inBuffer */
+ hp->YSize = (int)(((double)hp->XSize)*maxFactor+2.0);
+ hp->Y = (float *)malloc(hp->YSize * sizeof(float));
+ hp->Yp = 0;
+
+ hp->Time = (double)hp->Xoff; /* Current-time pointer for converter */
+
+ return (void *)hp;
+}
+
+int resample_get_filter_width(const void *handle)
+{
+ const rsdata *hp = (const rsdata *)handle;
+ return hp->Xoff;
+}
+
+int resample_process(void *handle,
+ double factor,
+ float *inBuffer,
+ int inBufferLen,
+ int lastFlag,
+ int *inBufferUsed, /* output param */
+ float *outBuffer,
+ int outBufferLen)
+{
+ rsdata *hp = (rsdata *)handle;
+ float *Imp = hp->Imp;
+ float *ImpD = hp->ImpD;
+ float LpScl = hp->LpScl;
+ UWORD Nwing = hp->Nwing;
+ BOOL interpFilt = FALSE; /* TRUE means interpolate filter coeffs */
+ int outSampleCount;
+ UWORD Nout, Ncreep, Nreuse;
+ int Nx;
+ int i, len;
+
+ #if defined(DEBUG)
+ fprintf(stderr, "resample_process: in=%d, out=%d lastFlag=%d\n",
+ inBufferLen, outBufferLen, lastFlag);
+ #endif
+
+ /* Initialize inBufferUsed and outSampleCount to 0 */
+ *inBufferUsed = 0;
+ outSampleCount = 0;
+
+ if (factor < hp->minFactor || factor > hp->maxFactor) {
+ #if defined(DEBUG)
+ fprintf(stderr,
+ "libresample: factor %f is not between "
+ "minFactor=%f and maxFactor=%f",
+ factor, hp->minFactor, hp->maxFactor);
+ #endif
+ return -1;
+ }
+
+ /* Start by copying any samples still in the Y buffer to the output
+ buffer */
+ if (hp->Yp && (outBufferLen-outSampleCount)>0) {
+ len = MIN(outBufferLen-outSampleCount, hp->Yp);
+ for(i=0; i<len; i++)
+ outBuffer[outSampleCount+i] = hp->Y[i];
+ outSampleCount += len;
+ for(i=0; i<hp->Yp-len; i++)
+ hp->Y[i] = hp->Y[i+len];
+ hp->Yp -= len;
+ }
+
+ /* If there are still output samples left, return now - we need
+ the full output buffer available to us... */
+ if (hp->Yp)
+ return outSampleCount;
+
+ /* Account for increased filter gain when using factors less than 1 */
+ if (factor < 1)
+ LpScl = LpScl*factor;
+
+ for(;;) {
+
+ /* This is the maximum number of samples we can process
+ per loop iteration */
+
+ #if defined(DEBUG)
+ printf("XSize: %d Xoff: %d Xread: %d Xp: %d lastFlag: %d\n",
+ hp->XSize, hp->Xoff, hp->Xread, hp->Xp, lastFlag);
+ #endif
+
+ /* Copy as many samples as we can from the input buffer into X */
+ len = hp->XSize - hp->Xread;
+
+ if (len >= (inBufferLen - (*inBufferUsed)))
+ len = (inBufferLen - (*inBufferUsed));
+
+ for(i=0; i<len; i++)
+ hp->X[hp->Xread + i] = inBuffer[(*inBufferUsed) + i];
+
+ *inBufferUsed += len;
+ hp->Xread += len;
+
+ if (lastFlag && (*inBufferUsed == inBufferLen)) {
+ /* If these are the last samples, zero-pad the
+ end of the input buffer and make sure we process
+ all the way to the end */
+ Nx = hp->Xread - hp->Xoff;
+ for(i=0; i<hp->Xoff; i++)
+ hp->X[hp->Xread + i] = 0;
+ }
+ else
+ Nx = hp->Xread - 2 * hp->Xoff;
+
+ #if defined(DEBUG)
+ fprintf(stderr, "new len=%d Nx=%d\n", len, Nx);
+ #endif
+
+ if (Nx <= 0)
+ break;
+
+ /* Resample stuff in input buffer */
+ if (factor >= 1) { /* SrcUp() is faster if we can use it */
+ Nout = lrsSrcUp(hp->X, hp->Y, factor, &hp->Time, Nx,
+ Nwing, LpScl, Imp, ImpD, interpFilt);
+ }
+ else {
+ Nout = lrsSrcUD(hp->X, hp->Y, factor, &hp->Time, Nx,
+ Nwing, LpScl, Imp, ImpD, interpFilt);
+ }
+
+ #if defined(DEBUG)
+ printf("Nout: %d\n", Nout);
+ #endif
+
+ hp->Time -= Nx; /* Move converter Nx samples back in time */
+ hp->Xp += Nx; /* Advance by number of samples processed */
+
+ /* Calc time accumulation in Time */
+ Ncreep = (int)(hp->Time) - hp->Xoff;
+ if (Ncreep) {
+ hp->Time -= Ncreep; /* Remove time accumulation */
+ hp->Xp += Ncreep; /* and add it to read pointer */
+ }
+
+ /* Copy part of input signal that must be re-used */
+ Nreuse = hp->Xread - (hp->Xp - hp->Xoff);
+
+ for (i=0; i<Nreuse; i++)
+ hp->X[i] = hp->X[i + (hp->Xp - hp->Xoff)];
+
+ #if defined(DEBUG)
+ printf("New Xread=%d\n", Nreuse);
+ #endif
+
+ hp->Xread = Nreuse; /* Pos in input buff to read new data into */
+ hp->Xp = hp->Xoff;
+
+ /* Check to see if output buff overflowed (shouldn't happen!) */
+ if (Nout > hp->YSize) {
+ #if defined(DEBUG)
+ printf("Nout: %d YSize: %d\n", Nout, hp->YSize);
+ #endif
+ fprintf(stderr, "libresample: Output array overflow!\n");
+ return -1;
+ }
+
+ hp->Yp = Nout;
+
+ /* Copy as many samples as possible to the output buffer */
+ if (hp->Yp && (outBufferLen-outSampleCount)>0) {
+ len = MIN(outBufferLen-outSampleCount, hp->Yp);
+ for(i=0; i<len; i++)
+ outBuffer[outSampleCount+i] = hp->Y[i];
+ outSampleCount += len;
+ for(i=0; i<hp->Yp-len; i++)
+ hp->Y[i] = hp->Y[i+len];
+ hp->Yp -= len;
+ }
+
+ /* If there are still output samples left, return now,
+ since we need the full output buffer available */
+ if (hp->Yp)
+ break;
+ }
+
+ return outSampleCount;
+}
+
+void resample_close(void *handle)
+{
+ rsdata *hp = (rsdata *)handle;
+ free(hp->X);
+ free(hp->Y);
+ free(hp->Imp);
+ free(hp->ImpD);
+ free(hp);
+}
+
diff --git a/trunk/main/libresample/src/resample_defs.h b/trunk/main/libresample/src/resample_defs.h
new file mode 100644
index 000000000..a0e7afc37
--- /dev/null
+++ b/trunk/main/libresample/src/resample_defs.h
@@ -0,0 +1,88 @@
+/**********************************************************************
+
+ resample_defs.h
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#ifndef __RESAMPLE_DEFS__
+#define __RESAMPLE_DEFS__
+
+#if 0
+#if !defined(WIN32) && !defined(__CYGWIN__)
+#include "config.h"
+#endif
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef PI
+#define PI (3.14159265358979232846)
+#endif
+
+#ifndef PI2
+#define PI2 (6.28318530717958465692)
+#endif
+
+#define D2R (0.01745329348) /* (2*pi)/360 */
+#define R2D (57.29577951) /* 360/(2*pi) */
+
+#ifndef MAX
+#define MAX(x,y) ((x)>(y) ?(x):(y))
+#endif
+#ifndef MIN
+#define MIN(x,y) ((x)<(y) ?(x):(y))
+#endif
+
+#ifndef ABS
+#define ABS(x) ((x)<0 ?(-(x)):(x))
+#endif
+
+#ifndef SGN
+#define SGN(x) ((x)<0 ?(-1):((x)==0?(0):(1)))
+#endif
+
+#if HAVE_INTTYPES_H
+ #include <inttypes.h>
+ typedef char BOOL;
+ typedef int32_t WORD;
+ typedef uint32_t UWORD;
+#else
+ typedef char BOOL;
+ typedef int WORD;
+ typedef unsigned int UWORD;
+#endif
+
+#ifdef DEBUG
+#define INLINE
+#else
+#define INLINE inline
+#endif
+
+/* Accuracy */
+
+#define Npc 4096
+
+/* Function prototypes */
+
+int lrsSrcUp(float X[], float Y[], double factor, double *Time,
+ UWORD Nx, UWORD Nwing, float LpScl,
+ float Imp[], float ImpD[], BOOL Interp);
+
+int lrsSrcUD(float X[], float Y[], double factor, double *Time,
+ UWORD Nx, UWORD Nwing, float LpScl,
+ float Imp[], float ImpD[], BOOL Interp);
+
+#endif
diff --git a/trunk/main/libresample/src/resamplesubs.c b/trunk/main/libresample/src/resamplesubs.c
new file mode 100644
index 000000000..c3c095dc0
--- /dev/null
+++ b/trunk/main/libresample/src/resamplesubs.c
@@ -0,0 +1,123 @@
+/**********************************************************************
+
+ resamplesubs.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+ This file provides the routines that do sample-rate conversion
+ on small arrays, calling routines from filterkit.
+
+**********************************************************************/
+
+/* Definitions */
+#include "resample_defs.h"
+
+#include "filterkit.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+/* Sampling rate up-conversion only subroutine;
+ * Slightly faster than down-conversion;
+ */
+int lrsSrcUp(float X[],
+ float Y[],
+ double factor,
+ double *TimePtr,
+ UWORD Nx,
+ UWORD Nwing,
+ float LpScl,
+ float Imp[],
+ float ImpD[],
+ BOOL Interp)
+{
+ float *Xp, *Ystart;
+ float v;
+
+ double CurrentTime = *TimePtr;
+ double dt; /* Step through input signal */
+ double endTime; /* When Time reaches EndTime, return to user */
+
+ dt = 1.0/factor; /* Output sampling period */
+
+ Ystart = Y;
+ endTime = CurrentTime + Nx;
+ while (CurrentTime < endTime)
+ {
+ double LeftPhase = CurrentTime-floor(CurrentTime);
+ double RightPhase = 1.0 - LeftPhase;
+
+ Xp = &X[(int)CurrentTime]; /* Ptr to current input sample */
+ /* Perform left-wing inner product */
+ v = lrsFilterUp(Imp, ImpD, Nwing, Interp, Xp,
+ LeftPhase, -1);
+ /* Perform right-wing inner product */
+ v += lrsFilterUp(Imp, ImpD, Nwing, Interp, Xp+1,
+ RightPhase, 1);
+
+ v *= LpScl; /* Normalize for unity filter gain */
+
+ *Y++ = v; /* Deposit output */
+ CurrentTime += dt; /* Move to next sample by time increment */
+ }
+
+ *TimePtr = CurrentTime;
+ return (Y - Ystart); /* Return the number of output samples */
+}
+
+/* Sampling rate conversion subroutine */
+
+int lrsSrcUD(float X[],
+ float Y[],
+ double factor,
+ double *TimePtr,
+ UWORD Nx,
+ UWORD Nwing,
+ float LpScl,
+ float Imp[],
+ float ImpD[],
+ BOOL Interp)
+{
+ float *Xp, *Ystart;
+ float v;
+
+ double CurrentTime = (*TimePtr);
+ double dh; /* Step through filter impulse response */
+ double dt; /* Step through input signal */
+ double endTime; /* When Time reaches EndTime, return to user */
+
+ dt = 1.0/factor; /* Output sampling period */
+
+ dh = MIN(Npc, factor*Npc); /* Filter sampling period */
+
+ Ystart = Y;
+ endTime = CurrentTime + Nx;
+ while (CurrentTime < endTime)
+ {
+ double LeftPhase = CurrentTime-floor(CurrentTime);
+ double RightPhase = 1.0 - LeftPhase;
+
+ Xp = &X[(int)CurrentTime]; /* Ptr to current input sample */
+ /* Perform left-wing inner product */
+ v = lrsFilterUD(Imp, ImpD, Nwing, Interp, Xp,
+ LeftPhase, -1, dh);
+ /* Perform right-wing inner product */
+ v += lrsFilterUD(Imp, ImpD, Nwing, Interp, Xp+1,
+ RightPhase, 1, dh);
+
+ v *= LpScl; /* Normalize for unity filter gain */
+ *Y++ = v; /* Deposit output */
+
+ CurrentTime += dt; /* Move to next sample by time increment */
+ }
+
+ *TimePtr = CurrentTime;
+ return (Y - Ystart); /* Return the number of output samples */
+}
diff --git a/trunk/main/libresample/tests/compareresample.c b/trunk/main/libresample/tests/compareresample.c
new file mode 100644
index 000000000..8773c9d4e
--- /dev/null
+++ b/trunk/main/libresample/tests/compareresample.c
@@ -0,0 +1,183 @@
+/**********************************************************************
+
+ compareresample.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#include "../include/libresample.h"
+
+#include <samplerate.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <sys/time.h>
+
+#define MIN(A, B) (A) < (B)? (A) : (B)
+
+void dostat(char *name, float *d1, float *d2, int len)
+{
+ int i;
+ double sum, sumsq, err, rmserr;
+
+ sum = 0.0;
+ sumsq = 0.0;
+ for(i=0; i<len; i++) {
+ double diff = d1[i] - d2[i];
+ sum += fabs(diff);
+ sumsq += diff * diff;
+ }
+ err = sum / len;
+ rmserr = sqrt(sumsq / len);
+ printf(" %s: Avg err: %f RMS err: %f\n", name, err, rmserr);
+}
+
+void runtest(float *src, int srclen,
+ float *ans, int anslen,
+ double factor)
+{
+ struct timeval tv0, tv1;
+ int dstlen = (int)(srclen * factor);
+ float *dst_rs = (float *)malloc((dstlen+100) * sizeof(float));
+ float *dst_rabbit = (float *)malloc((dstlen+100) * sizeof(float));
+ void *handle;
+ SRC_DATA rabbit;
+ double deltat;
+ int srcblocksize = srclen;
+ int dstblocksize = dstlen;
+ int i, out, out_rabbit, o, srcused;
+ int statlen, srcpos;
+
+ /* do resample */
+
+ for(i=0; i<dstlen+100; i++)
+ dst_rs[i] = -99.0;
+
+ gettimeofday(&tv0, NULL);
+
+ handle = resample_open(1, factor, factor);
+ out = 0;
+ srcpos = 0;
+ for(;;) {
+ int srcBlock = MIN(srclen-srcpos, srcblocksize);
+ int lastFlag = (srcBlock == srclen-srcpos);
+
+ o = resample_process(handle, factor,
+ &src[srcpos], srcBlock,
+ lastFlag, &srcused,
+ &dst_rs[out], MIN(dstlen-out, dstblocksize));
+ srcpos += srcused;
+ if (o >= 0)
+ out += o;
+ if (o < 0 || (o == 0 && srcpos == srclen))
+ break;
+ }
+ resample_close(handle);
+
+ gettimeofday(&tv1, NULL);
+ deltat =
+ (tv1.tv_sec + tv1.tv_usec * 0.000001) -
+ (tv0.tv_sec + tv0.tv_usec * 0.000001);
+
+ if (o < 0) {
+ printf("Error: resample_process returned an error: %d\n", o);
+ }
+
+ if (out <= 0) {
+ printf("Error: resample_process returned %d samples\n", out);
+ free(dst_rs);
+ return;
+ }
+
+ printf(" resample: %.3f seconds, %d outputs\n", deltat, out);
+
+ /* do rabbit (Erik's libsamplerate) */
+
+ for(i=0; i<dstlen+100; i++)
+ dst_rabbit[i] = -99.0;
+
+ rabbit.data_in = src;
+ rabbit.data_out = dst_rabbit;
+ rabbit.input_frames = srclen;
+ rabbit.output_frames = dstlen;
+ rabbit.input_frames_used = 0;
+ rabbit.output_frames_gen = 0;
+ rabbit.end_of_input = 1;
+ rabbit.src_ratio = factor;
+
+ gettimeofday(&tv0, NULL);
+
+ /* src_simple(&rabbit, SRC_SINC_BEST_QUALITY, 1); */
+ src_simple(&rabbit, SRC_SINC_FASTEST, 1);
+ /* src_simple(&rabbit, SRC_LINEAR, 1); */
+
+ gettimeofday(&tv1, NULL);
+ deltat =
+ (tv1.tv_sec + tv1.tv_usec * 0.000001) -
+ (tv0.tv_sec + tv0.tv_usec * 0.000001);
+
+ out_rabbit = rabbit.output_frames_gen;
+
+ printf(" rabbit : %.3f seconds, %d outputs\n",
+ deltat, out_rabbit);
+
+ statlen = MIN(out, out_rabbit);
+ if (anslen > 0)
+ statlen = MIN(statlen, anslen);
+
+ if (ans) {
+ dostat("resample ", dst_rs, ans, statlen);
+ dostat("rabbit ", dst_rabbit, ans, statlen);
+ }
+ dostat( "RS vs rabbit", dst_rs, dst_rabbit, statlen);
+
+ free(dst_rs);
+ free(dst_rabbit);
+}
+
+int main(int argc, char **argv)
+{
+ int i, srclen;
+ float *src, *ans;
+
+ printf("\n*** sin wave, factor = 1.0 *** \n\n");
+ srclen = 100000;
+ src = malloc(srclen * sizeof(float));
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/100.0);
+
+ runtest(src, srclen, src, srclen, 1.0);
+
+ printf("\n*** sin wave, factor = 0.25 *** \n\n");
+ srclen = 100000;
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/100.0);
+ ans = malloc((srclen/4) * sizeof(float));
+ for(i=0; i<srclen/4; i++)
+ ans[i] = sin(i/25.0);
+
+ runtest(src, srclen, ans, srclen/4, 0.25);
+ free(ans);
+
+ printf("\n*** sin wave, factor = 4.0 *** \n\n");
+ srclen = 20000;
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/100.0);
+ ans = malloc((srclen*4) * sizeof(float));
+ for(i=0; i<srclen*4; i++)
+ ans[i] = sin(i/400.0);
+
+ runtest(src, srclen, ans, srclen*4, 4.0);
+ free(ans);
+ free(src);
+
+ return 0;
+}
diff --git a/trunk/main/libresample/tests/resample-sndfile.c b/trunk/main/libresample/tests/resample-sndfile.c
new file mode 100644
index 000000000..e780228c1
--- /dev/null
+++ b/trunk/main/libresample/tests/resample-sndfile.c
@@ -0,0 +1,213 @@
+/**********************************************************************
+
+ resample-sndfile.c
+
+ Written by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#include "../include/libresample.h"
+
+#include <sndfile.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <sys/time.h>
+
+#define MIN(A, B) (A) < (B)? (A) : (B)
+
+void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s -by <ratio> <input> <output>\n", progname);
+ fprintf(stderr, " %s -to <rate> <input> <output>\n", progname);
+ fprintf(stderr, "\n");
+ exit(-1);
+}
+
+int main(int argc, char **argv)
+{
+ SNDFILE *srcfile, *dstfile;
+ SF_INFO srcinfo, dstinfo;
+ SF_FORMAT_INFO formatinfo;
+ char *extension;
+ void **handle;
+ int channels;
+ int srclen, dstlen;
+ float *src, *srci;
+ float *dst, *dsti;
+ double ratio = 0.0;
+ double srcrate;
+ double dstrate = 0.0;
+ struct timeval tv0, tv1;
+ double deltat;
+ int numformats;
+ int pos, bufferpos, outcount;
+ int i, c;
+
+ if (argc != 5)
+ usage(argv[0]);
+
+ if (!strcmp(argv[1], "-by")) {
+ ratio = atof(argv[2]);
+ if (ratio <= 0.0) {
+ fprintf(stderr, "Ratio of %f is illegal\n", ratio);
+ usage(argv[0]);
+ }
+ }
+ else if (!strcmp(argv[1], "-to")) {
+ dstrate = atof(argv[2]);
+ if (dstrate < 10.0 || dstrate > 100000.0) {
+ fprintf(stderr, "Sample rate of %f is illegal\n", dstrate);
+ usage(argv[0]);
+ }
+ }
+ else
+ usage(argv[0]);
+
+ memset(&srcinfo, 0, sizeof(srcinfo));
+ memset(&dstinfo, 0, sizeof(dstinfo));
+ srcfile = sf_open(argv[3], SFM_READ, &srcinfo);
+ if (!srcfile) {
+ fprintf(stderr, "%s", sf_strerror(NULL));
+ exit(-1);
+ }
+
+ srcrate = srcinfo.samplerate;
+ if (dstrate == 0.0)
+ dstrate = srcrate * ratio;
+ else
+ ratio = dstrate / srcrate;
+
+ channels = srcinfo.channels;
+
+ /* figure out format of destination file */
+
+ extension = strstr(argv[4], ".");
+ if (extension) {
+ extension++;
+ sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT,
+ &numformats, sizeof(numformats));
+ for(i=0; i<numformats; i++) {
+ memset(&formatinfo, 0, sizeof(formatinfo));
+ formatinfo.format = i;
+ sf_command(NULL, SFC_GET_FORMAT_MAJOR,
+ &formatinfo, sizeof(formatinfo));
+ if (!strcmp(formatinfo.extension, extension)) {
+ printf("Using %s for output format.\n", formatinfo.name);
+ dstinfo.format = formatinfo.format |
+ (srcinfo.format & SF_FORMAT_SUBMASK);
+ break;
+ }
+ }
+ }
+
+ if (!dstinfo.format) {
+ if (extension)
+ printf("Warning: output format (%s) not recognized, "
+ "using same as input format.\n",
+ extension);
+ dstinfo.format = srcinfo.format;
+ }
+
+ dstinfo.samplerate = (int)(dstrate + 0.5);
+ dstinfo.channels = channels;
+
+ dstfile = sf_open(argv[4], SFM_WRITE, &dstinfo);
+ if (!dstfile) {
+ fprintf(stderr, "%s", sf_strerror(NULL));
+ exit(-1);
+ }
+
+ printf("Source: %s (%d frames, %.2f Hz)\n",
+ argv[3], (int)srcinfo.frames, srcrate);
+ printf("Destination: %s (%.2f Hz, ratio=%.5f)\n", argv[4],
+ dstrate, ratio);
+
+ srclen = 4096;
+ dstlen = (srclen * ratio + 1000);
+ srci = (float *)malloc(srclen * channels * sizeof(float));
+ dsti = (float *)malloc(dstlen * channels * sizeof(float));
+ src = (float *)malloc(srclen * sizeof(float));
+ dst = (float *)malloc(dstlen * sizeof(float));
+
+ handle = (void **)malloc(channels * sizeof(void *));
+ for(c=0; c<channels; c++)
+ handle[c] = resample_open(1, ratio, ratio);
+
+ gettimeofday(&tv0, NULL);
+
+ pos = 0;
+ bufferpos = 0;
+ outcount = 0;
+ while(pos < srcinfo.frames) {
+ int block = MIN(srclen-bufferpos, srcinfo.frames-pos);
+ int lastFlag = (pos+block == srcinfo.frames);
+ int inUsed, inUsed2=0, out=0, out2=0;
+
+ sf_readf_float(srcfile, &srci[bufferpos*channels], block);
+ block += bufferpos;
+
+ for(c=0; c<channels; c++) {
+ for(i=0; i<block; i++)
+ src[i] = srci[i*channels+c];
+
+ inUsed = 0;
+ out = resample_process(handle[c], ratio, src, block, lastFlag,
+ &inUsed, dst, dstlen);
+ if (c==0) {
+ inUsed2 = inUsed;
+ out2 = out;
+ }
+ else {
+ if (inUsed2 != inUsed || out2 != out) {
+ fprintf(stderr, "Fatal error: channels out of sync!\n");
+ exit(-1);
+ }
+ }
+
+ for(i=0; i<out; i++)
+ {
+ if(dst[i] <= -1)
+ dsti[i*channels+c] = -1;
+ else if(dst[i] >= 1)
+ dsti[i*channels+c] = 1;
+ else
+ dsti[i*channels+c] = dst[i];
+ }
+ }
+
+ sf_writef_float(dstfile, dsti, out);
+
+ bufferpos = block - inUsed;
+ for(i=0; i<bufferpos*channels; i++)
+ srci[i] = srci[i+(inUsed*channels)];
+ pos += inUsed;
+ outcount += out;
+ }
+
+ sf_close(srcfile);
+ sf_close(dstfile);
+
+ gettimeofday(&tv1, NULL);
+ deltat =
+ (tv1.tv_sec + tv1.tv_usec * 0.000001) -
+ (tv0.tv_sec + tv0.tv_usec * 0.000001);
+
+ printf("Elapsed time: %.3f seconds\n", deltat);
+ printf("%d frames written to output file\n", outcount);
+
+ free(src);
+ free(srci);
+ free(dst);
+ free(dsti);
+
+ exit(0);
+}
diff --git a/trunk/main/libresample/tests/testresample.c b/trunk/main/libresample/tests/testresample.c
new file mode 100644
index 000000000..a59aa8bfd
--- /dev/null
+++ b/trunk/main/libresample/tests/testresample.c
@@ -0,0 +1,182 @@
+/**********************************************************************
+
+ testresample.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#include "../include/libresample.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define MIN(A, B) (A) < (B)? (A) : (B)
+
+void runtest(int srclen, double freq, double factor,
+ int srcblocksize, int dstblocksize)
+{
+ int expectedlen = (int)(srclen * factor);
+ int dstlen = expectedlen + 1000;
+ float *src = (float *)malloc((srclen+100) * sizeof(float));
+ float *dst = (float *)malloc((dstlen+100) * sizeof(float));
+ void *handle;
+ double sum, sumsq, err, rmserr;
+ int i, out, o, srcused, errcount, rangecount;
+ int statlen, srcpos, lendiff;
+ int fwidth;
+
+ printf("-- srclen: %d sin freq: %.1f factor: %.3f srcblk: %d dstblk: %d\n",
+ srclen, freq, factor, srcblocksize, dstblocksize);
+
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/freq);
+ for(i=srclen; i<srclen+100; i++)
+ src[i] = -99.0;
+
+ for(i=0; i<dstlen+100; i++)
+ dst[i] = -99.0;
+
+ handle = resample_open(1, factor, factor);
+ fwidth = resample_get_filter_width(handle);
+ out = 0;
+ srcpos = 0;
+ for(;;) {
+ int srcBlock = MIN(srclen-srcpos, srcblocksize);
+ int lastFlag = (srcBlock == srclen-srcpos);
+
+ o = resample_process(handle, factor,
+ &src[srcpos], srcBlock,
+ lastFlag, &srcused,
+ &dst[out], MIN(dstlen-out, dstblocksize));
+ srcpos += srcused;
+ if (o >= 0)
+ out += o;
+ if (o < 0 || (o == 0 && srcpos == srclen))
+ break;
+ }
+ resample_close(handle);
+
+ if (o < 0) {
+ printf("Error: resample_process returned an error: %d\n", o);
+ }
+
+ if (out <= 0) {
+ printf("Error: resample_process returned %d samples\n", out);
+ free(src);
+ free(dst);
+ return;
+ }
+
+ lendiff = abs(out - expectedlen);
+ if (lendiff > (int)(2*factor + 1.0)) {
+ printf(" Expected ~%d, got %d samples out\n",
+ expectedlen, out);
+ }
+
+ sum = 0.0;
+ sumsq = 0.0;
+ errcount = 0.0;
+
+ /* Don't compute statistics on all output values; the last few
+ are guaranteed to be off because it's based on far less
+ interpolation. */
+ statlen = out - fwidth;
+
+ for(i=0; i<statlen; i++) {
+ double diff = sin((i/freq)/factor) - dst[i];
+ if (fabs(diff) > 0.05) {
+ if (errcount == 0)
+ printf(" First error at i=%d: expected %.3f, got %.3f\n",
+ i, sin((i/freq)/factor), dst[i]);
+ errcount++;
+ }
+ sum += fabs(diff);
+ sumsq += diff * diff;
+ }
+
+ rangecount = 0;
+ for(i=0; i<statlen; i++) {
+ if (dst[i] < -1.01 || dst[i] > 1.01) {
+ if (rangecount == 0)
+ printf(" Error at i=%d: value is %.3f\n", i, dst[i]);
+ rangecount++;
+ }
+ }
+ if (rangecount > 1)
+ printf(" At least %d samples were out of range\n", rangecount);
+
+ if (errcount > 0) {
+ i = out - 1;
+ printf(" i=%d: expected %.3f, got %.3f\n",
+ i, sin((i/freq)/factor), dst[i]);
+ printf(" At least %d samples had significant error.\n", errcount);
+ }
+ err = sum / statlen;
+ rmserr = sqrt(sumsq / statlen);
+ printf(" Out: %d samples Avg err: %f RMS err: %f\n", out, err, rmserr);
+ free(src);
+ free(dst);
+}
+
+int main(int argc, char **argv)
+{
+ int i, srclen, dstlen, ifreq;
+ double factor;
+
+ printf("\n*** Vary source block size*** \n\n");
+ srclen = 10000;
+ ifreq = 100;
+ for(i=0; i<20; i++) {
+ factor = ((rand() % 16) + 1) / 4.0;
+ dstlen = (int)(srclen * factor + 10);
+ runtest(srclen, (double)ifreq, factor, 64, dstlen);
+ runtest(srclen, (double)ifreq, factor, 32, dstlen);
+ runtest(srclen, (double)ifreq, factor, 8, dstlen);
+ runtest(srclen, (double)ifreq, factor, 2, dstlen);
+ runtest(srclen, (double)ifreq, factor, srclen, dstlen);
+ }
+
+ printf("\n*** Vary dest block size ***\n\n");
+ srclen = 10000;
+ ifreq = 100;
+ for(i=0; i<20; i++) {
+ factor = ((rand() % 16) + 1) / 4.0;
+ runtest(srclen, (double)ifreq, factor, srclen, 32);
+ dstlen = (int)(srclen * factor + 10);
+ runtest(srclen, (double)ifreq, factor, srclen, dstlen);
+ }
+
+ printf("\n*** Resample factor 1.0, testing different srclen ***\n\n");
+ ifreq = 40;
+ for(i=0; i<100; i++) {
+ srclen = (rand() % 30000) + 10;
+ dstlen = (int)(srclen + 10);
+ runtest(srclen, (double)ifreq, 1.0, srclen, dstlen);
+ }
+
+ printf("\n*** Resample factor 1.0, testing different sin freq ***\n\n");
+ srclen = 10000;
+ for(i=0; i<100; i++) {
+ ifreq = ((int)rand() % 10000) + 1;
+ dstlen = (int)(srclen * 10);
+ runtest(srclen, (double)ifreq, 1.0, srclen, dstlen);
+ }
+
+ printf("\n*** Resample with different factors ***\n\n");
+ srclen = 10000;
+ ifreq = 100;
+ for(i=0; i<100; i++) {
+ factor = ((rand() % 64) + 1) / 4.0;
+ dstlen = (int)(srclen * factor + 10);
+ runtest(srclen, (double)ifreq, factor, srclen, dstlen);
+ }
+
+ return 0;
+}
diff --git a/trunk/main/libresample/win/libresample.dsp b/trunk/main/libresample/win/libresample.dsp
new file mode 100644
index 000000000..4ebb51e46
--- /dev/null
+++ b/trunk/main/libresample/win/libresample.dsp
@@ -0,0 +1,116 @@
+# Microsoft Developer Studio Project File - Name="libresample" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=libresample - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "libresample.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libresample.mak" CFG="libresample - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libresample - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "libresample - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "libresample - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"libresample.lib"
+
+!ELSEIF "$(CFG)" == "libresample - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"libresampled.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "libresample - Win32 Release"
+# Name "libresample - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\filterkit.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\resample.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\resamplesubs.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\filterkit.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\libresample.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\resample_defs.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/trunk/main/libresample/win/libresample.vcproj b/trunk/main/libresample/win/libresample.vcproj
new file mode 100644
index 000000000..c23845f47
--- /dev/null
+++ b/trunk/main/libresample/win/libresample.vcproj
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="libresample"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="4"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/libresample.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="libresampled.lib"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="4"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/O2"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ OptimizeForProcessor="3"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ StringPooling="TRUE"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Release/libresample.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="libresample.lib"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath="..\src\filterkit.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\src\resample.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\src\resamplesubs.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath="..\src\filterkit.h">
+ </File>
+ <File
+ RelativePath="..\include\libresample.h">
+ </File>
+ <File
+ RelativePath="..\src\resample_defs.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/trunk/main/loader.c b/trunk/main/loader.c
new file mode 100644
index 000000000..1d0797720
--- /dev/null
+++ b/trunk/main/loader.c
@@ -0,0 +1,1006 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@digium.com>
+ * Luigi Rizzo <rizzo@icir.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 Module Loader
+ * \author Mark Spencer <markster@digium.com>
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ * \author Luigi Rizzo <rizzo@icir.org>
+ * - See ModMngMnt
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
+#include <dirent.h>
+
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/cdr.h"
+#include "asterisk/enum.h"
+#include "asterisk/rtp.h"
+#include "asterisk/http.h"
+#include "asterisk/lock.h"
+
+#ifdef DLFCNCOMPAT
+#include "asterisk/dlfcn-compat.h"
+#else
+#include <dlfcn.h>
+#endif
+
+#include "asterisk/md5.h"
+#include "asterisk/utils.h"
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+
+#ifndef RTLD_LOCAL
+#define RTLD_LOCAL 0
+#endif
+
+struct ast_module_user {
+ struct ast_channel *chan;
+ AST_LIST_ENTRY(ast_module_user) entry;
+};
+
+AST_LIST_HEAD(module_user_list, ast_module_user);
+
+static unsigned char expected_key[] =
+{ 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
+ 0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
+
+static char buildopt_sum[33] = AST_BUILDOPT_SUM;
+
+static unsigned int embedding = 1; /* we always start out by registering embedded modules,
+ since they are here before we dlopen() any
+ */
+
+struct ast_module {
+ const struct ast_module_info *info;
+ void *lib; /* the shared lib, or NULL if embedded */
+ int usecount; /* the number of 'users' currently in this module */
+ struct module_user_list users; /* the list of users in the module */
+ struct {
+ unsigned int running:1;
+ unsigned int declined:1;
+ } flags;
+ AST_LIST_ENTRY(ast_module) entry;
+ char resource[0];
+};
+
+static AST_LIST_HEAD_STATIC(module_list, ast_module);
+
+/*
+ * module_list is cleared by its constructor possibly after
+ * we start accumulating embedded modules, so we need to
+ * use another list (without the lock) to accumulate them.
+ * Then we update the main list when embedding is done.
+ */
+static struct module_list embedded_module_list;
+
+struct loadupdate {
+ int (*updater)(void);
+ AST_LIST_ENTRY(loadupdate) entry;
+};
+
+static AST_LIST_HEAD_STATIC(updaters, loadupdate);
+
+AST_MUTEX_DEFINE_STATIC(reloadlock);
+
+/* when dynamic modules are being loaded, ast_module_register() will
+ need to know what filename the module was loaded from while it
+ is being registered
+*/
+struct ast_module *resource_being_loaded;
+
+/* XXX: should we check for duplicate resource names here? */
+
+void ast_module_register(const struct ast_module_info *info)
+{
+ struct ast_module *mod;
+
+ if (embedding) {
+ if (!(mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1)))
+ return;
+ strcpy(mod->resource, info->name);
+ } else {
+ mod = resource_being_loaded;
+ }
+
+ mod->info = info;
+ AST_LIST_HEAD_INIT(&mod->users);
+
+ /* during startup, before the loader has been initialized,
+ there are no threads, so there is no need to take the lock
+ on this list to manipulate it. it is also possible that it
+ might be unsafe to use the list lock at that point... so
+ let's avoid it altogether
+ */
+ if (embedding) {
+ AST_LIST_INSERT_TAIL(&embedded_module_list, mod, entry);
+ } else {
+ AST_LIST_LOCK(&module_list);
+ /* it is paramount that the new entry be placed at the tail of
+ the list, otherwise the code that uses dlopen() to load
+ dynamic modules won't be able to find out if the module it
+ just opened was registered or failed to load
+ */
+ AST_LIST_INSERT_TAIL(&module_list, mod, entry);
+ AST_LIST_UNLOCK(&module_list);
+ }
+
+ /* give the module a copy of its own handle, for later use in registrations and the like */
+ *((struct ast_module **) &(info->self)) = mod;
+}
+
+void ast_module_unregister(const struct ast_module_info *info)
+{
+ struct ast_module *mod = NULL;
+
+ /* it is assumed that the users list in the module structure
+ will already be empty, or we cannot have gotten to this
+ point
+ */
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
+ if (mod->info == info) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&module_list);
+
+ if (mod) {
+ AST_LIST_HEAD_DESTROY(&mod->users);
+ ast_free(mod);
+ }
+}
+
+struct ast_module_user *__ast_module_user_add(struct ast_module *mod,
+ struct ast_channel *chan)
+{
+ struct ast_module_user *u = ast_calloc(1, sizeof(*u));
+
+ if (!u)
+ return NULL;
+
+ u->chan = chan;
+
+ AST_LIST_LOCK(&mod->users);
+ AST_LIST_INSERT_HEAD(&mod->users, u, entry);
+ AST_LIST_UNLOCK(&mod->users);
+
+ ast_atomic_fetchadd_int(&mod->usecount, +1);
+
+ ast_update_use_count();
+
+ return u;
+}
+
+void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
+{
+ AST_LIST_LOCK(&mod->users);
+ AST_LIST_REMOVE(&mod->users, u, entry);
+ AST_LIST_UNLOCK(&mod->users);
+ ast_atomic_fetchadd_int(&mod->usecount, -1);
+ ast_free(u);
+
+ ast_update_use_count();
+}
+
+void __ast_module_user_hangup_all(struct ast_module *mod)
+{
+ struct ast_module_user *u;
+
+ AST_LIST_LOCK(&mod->users);
+ while ((u = AST_LIST_REMOVE_HEAD(&mod->users, entry))) {
+ ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
+ ast_atomic_fetchadd_int(&mod->usecount, -1);
+ ast_free(u);
+ }
+ AST_LIST_UNLOCK(&mod->users);
+
+ ast_update_use_count();
+}
+
+/*! \note
+ * In addition to modules, the reload command handles some extra keywords
+ * which are listed here together with the corresponding handlers.
+ * This table is also used by the command completion code.
+ */
+static struct reload_classes {
+ const char *name;
+ int (*reload_fn)(void);
+} reload_classes[] = { /* list in alpha order, longest match first for cli completion */
+ { "cdr", ast_cdr_engine_reload },
+ { "dnsmgr", dnsmgr_reload },
+ { "extconfig", read_config_maps },
+ { "enum", ast_enum_reload },
+ { "manager", reload_manager },
+ { "rtp", ast_rtp_reload },
+ { "http", ast_http_reload },
+ { "logger", logger_reload },
+ { NULL, NULL }
+};
+
+static int printdigest(const unsigned char *d)
+{
+ int x, pos;
+ char buf[256]; /* large enough so we don't have to worry */
+
+ for (pos = 0, x = 0; x < 16; x++)
+ pos += sprintf(buf + pos, " %02x", *d++);
+
+ ast_debug(1, "Unexpected signature:%s\n", buf);
+
+ return 0;
+}
+
+static int key_matches(const unsigned char *key1, const unsigned char *key2)
+{
+ int x;
+
+ for (x = 0; x < 16; x++) {
+ if (key1[x] != key2[x])
+ return 0;
+ }
+
+ return 1;
+}
+
+static int verify_key(const unsigned char *key)
+{
+ struct MD5Context c;
+ unsigned char digest[16];
+
+ MD5Init(&c);
+ MD5Update(&c, key, strlen((char *)key));
+ MD5Final(digest, &c);
+
+ if (key_matches(expected_key, digest))
+ return 0;
+
+ printdigest(digest);
+
+ return -1;
+}
+
+static int resource_name_match(const char *name1_in, const char *name2_in)
+{
+ char *name1 = (char *) name1_in;
+ char *name2 = (char *) name2_in;
+
+ /* trim off any .so extensions */
+ if (!strcasecmp(name1 + strlen(name1) - 3, ".so")) {
+ name1 = ast_strdupa(name1);
+ name1[strlen(name1) - 3] = '\0';
+ }
+ if (!strcasecmp(name2 + strlen(name2) - 3, ".so")) {
+ name2 = ast_strdupa(name2);
+ name2[strlen(name2) - 3] = '\0';
+ }
+
+ return strcasecmp(name1, name2);
+}
+
+static struct ast_module *find_resource(const char *resource, int do_lock)
+{
+ struct ast_module *cur;
+
+ if (do_lock)
+ AST_LIST_LOCK(&module_list);
+
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ if (!resource_name_match(resource, cur->resource))
+ break;
+ }
+
+ if (do_lock)
+ AST_LIST_UNLOCK(&module_list);
+
+ return cur;
+}
+
+#ifdef LOADABLE_MODULES
+static void unload_dynamic_module(struct ast_module *mod)
+{
+ void *lib = mod->lib;
+
+ /* WARNING: the structure pointed to by mod is going to
+ disappear when this operation succeeds, so we can't
+ dereference it */
+
+ if (lib)
+ while (!dlclose(lib));
+}
+
+static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only)
+{
+ char fn[PATH_MAX] = "";
+ void *lib = NULL;
+ struct ast_module *mod;
+ unsigned int wants_global;
+ int space; /* room needed for the descriptor */
+ int missing_so = 0;
+
+ space = sizeof(*resource_being_loaded) + strlen(resource_in) + 1;
+ if (strcasecmp(resource_in + strlen(resource_in) - 3, ".so")) {
+ missing_so = 1;
+ space += 3; /* room for the extra ".so" */
+ }
+
+ snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, missing_so ? ".so" : "");
+
+ /* make a first load of the module in 'quiet' mode... don't try to resolve
+ any symbols, and don't export any symbols. this will allow us to peek into
+ the module's info block (if available) to see what flags it has set */
+
+ resource_being_loaded = ast_calloc(1, space);
+ if (!resource_being_loaded)
+ return NULL;
+ strcpy(resource_being_loaded->resource, resource_in);
+ if (missing_so)
+ strcat(resource_being_loaded->resource, ".so");
+
+ if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
+ ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
+ ast_free(resource_being_loaded);
+ return NULL;
+ }
+
+ /* the dlopen() succeeded, let's find out if the module
+ registered itself */
+ /* note that this will only work properly as long as
+ ast_module_register() (which is called by the module's
+ constructor) places the new module at the tail of the
+ module_list
+ */
+ if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
+ ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
+ /* no, it did not, so close it and return */
+ while (!dlclose(lib));
+ /* note that the module's destructor will call ast_module_unregister(),
+ which will free the structure we allocated in resource_being_loaded */
+ return NULL;
+ }
+
+ wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
+
+ /* if we are being asked only to load modules that provide global symbols,
+ and this one does not, then close it and return */
+ if (global_symbols_only && !wants_global) {
+ while (!dlclose(lib));
+ return NULL;
+ }
+
+ /* if the system supports RTLD_NOLOAD, we can just 'promote' the flags
+ on the already-opened library to what we want... if not, we have to
+ close it and start over
+ */
+#if defined(HAVE_RTLD_NOLOAD) && !defined(__Darwin__)
+ if (!dlopen(fn, RTLD_NOLOAD | (wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
+ ast_log(LOG_WARNING, "Unable to promote flags on module '%s': %s\n", resource_in, dlerror());
+ while (!dlclose(lib));
+ ast_free(resource_being_loaded);
+ return NULL;
+ }
+#else
+ while (!dlclose(lib));
+ resource_being_loaded = NULL;
+
+ /* start the load process again */
+ resource_being_loaded = ast_calloc(1, space);
+ if (!resource_being_loaded)
+ return NULL;
+ strcpy(resource_being_loaded->resource, resource_in);
+ if (missing_so)
+ strcat(resource_being_loaded->resource, ".so");
+
+ if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
+ ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
+ ast_free(resource_being_loaded);
+ return NULL;
+ }
+
+ /* since the module was successfully opened, and it registered itself
+ the previous time we did that, we're going to assume it worked this
+ time too :) */
+#endif
+
+ AST_LIST_LAST(&module_list)->lib = lib;
+ resource_being_loaded = NULL;
+
+ return AST_LIST_LAST(&module_list);
+}
+#endif
+
+void ast_module_shutdown(void)
+{
+ struct ast_module *mod;
+ AST_LIST_HEAD_NOLOCK_STATIC(local_module_list, ast_module);
+
+ /* We have to call the unload() callbacks in reverse order that the modules
+ * exist in the module list so it is the reverse order of how they were
+ * loaded. */
+
+ AST_LIST_LOCK(&module_list);
+ while ((mod = AST_LIST_REMOVE_HEAD(&module_list, entry)))
+ AST_LIST_INSERT_HEAD(&local_module_list, mod, entry);
+ AST_LIST_UNLOCK(&module_list);
+
+ while ((mod = AST_LIST_REMOVE_HEAD(&local_module_list, entry))) {
+ if (mod->info->unload)
+ mod->info->unload();
+ /* Since this should only be called when shutting down "gracefully",
+ * all channels should be down before we get to this point, meaning
+ * there will be no module users left. */
+ AST_LIST_HEAD_DESTROY(&mod->users);
+ free(mod);
+ }
+}
+
+int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
+{
+ struct ast_module *mod;
+ int res = -1;
+ int error = 0;
+
+ AST_LIST_LOCK(&module_list);
+
+ if (!(mod = find_resource(resource_name, 0))) {
+ AST_LIST_UNLOCK(&module_list);
+ return 0;
+ }
+
+ if (!(mod->flags.running || mod->flags.declined))
+ error = 1;
+
+ if (!error && (mod->usecount > 0)) {
+ if (force)
+ ast_log(LOG_WARNING, "Warning: Forcing removal of module '%s' with use count %d\n",
+ resource_name, mod->usecount);
+ else {
+ ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
+ mod->usecount);
+ error = 1;
+ }
+ }
+
+ if (!error) {
+ __ast_module_user_hangup_all(mod);
+ res = mod->info->unload();
+
+ if (res) {
+ ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
+ if (force <= AST_FORCE_FIRM)
+ error = 1;
+ else
+ ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
+ }
+ }
+
+ if (!error)
+ mod->flags.running = mod->flags.declined = 0;
+
+ AST_LIST_UNLOCK(&module_list);
+
+ if (!error && !mod->lib && mod->info && mod->info->restore_globals)
+ mod->info->restore_globals();
+
+#ifdef LOADABLE_MODULES
+ if (!error)
+ unload_dynamic_module(mod);
+#endif
+
+ if (!error)
+ ast_update_use_count();
+
+ return res;
+}
+
+char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload)
+{
+ struct ast_module *cur;
+ int i, which=0, l = strlen(word);
+ char *ret = NULL;
+
+ if (pos != rpos)
+ return NULL;
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ if (!strncasecmp(word, cur->resource, l) &&
+ (cur->info->reload || !needsreload) &&
+ ++which > state) {
+ ret = ast_strdup(cur->resource);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&module_list);
+
+ if (!ret) {
+ for (i=0; !ret && reload_classes[i].name; i++) {
+ if (!strncasecmp(word, reload_classes[i].name, l) && ++which > state)
+ ret = ast_strdup(reload_classes[i].name);
+ }
+ }
+
+ return ret;
+}
+
+int ast_module_reload(const char *name)
+{
+ struct ast_module *cur;
+ int res = 0; /* return value. 0 = not found, others, see below */
+ int i;
+
+ if (ast_mutex_trylock(&reloadlock)) {
+ ast_verbose("The previous reload command didn't finish yet\n");
+ return -1; /* reload already in progress */
+ }
+ ast_lastreloadtime = ast_tvnow();
+
+ /* Call "predefined" reload here first */
+ for (i = 0; reload_classes[i].name; i++) {
+ if (!name || !strcasecmp(name, reload_classes[i].name)) {
+ reload_classes[i].reload_fn(); /* XXX should check error ? */
+ res = 2; /* found and reloaded */
+ }
+ }
+
+ if (name && res) {
+ ast_mutex_unlock(&reloadlock);
+ return res;
+ }
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ const struct ast_module_info *info = cur->info;
+
+ if (name && resource_name_match(name, cur->resource))
+ continue;
+
+ if (!(cur->flags.running || cur->flags.declined))
+ continue;
+
+ if (!info->reload) { /* cannot be reloaded */
+ if (res < 1) /* store result if possible */
+ res = 1; /* 1 = no reload() method */
+ continue;
+ }
+
+ res = 2;
+ ast_verb(3, "Reloading module '%s' (%s)\n", cur->resource, info->description);
+ info->reload();
+ }
+ AST_LIST_UNLOCK(&module_list);
+
+ ast_mutex_unlock(&reloadlock);
+
+ return res;
+}
+
+static unsigned int inspect_module(const struct ast_module *mod)
+{
+ if (!mod->info->description) {
+ ast_log(LOG_WARNING, "Module '%s' does not provide a description.\n", mod->resource);
+ return 1;
+ }
+
+ if (!mod->info->key) {
+ ast_log(LOG_WARNING, "Module '%s' does not provide a license key.\n", mod->resource);
+ return 1;
+ }
+
+ if (verify_key((unsigned char *) mod->info->key)) {
+ ast_log(LOG_WARNING, "Module '%s' did not provide a valid license key.\n", mod->resource);
+ return 1;
+ }
+
+ if (!ast_strlen_zero(mod->info->buildopt_sum) &&
+ strcmp(buildopt_sum, mod->info->buildopt_sum)) {
+ ast_log(LOG_WARNING, "Module '%s' was not compiled with the same compile-time options as this version of Asterisk.\n", mod->resource);
+ ast_log(LOG_WARNING, "Module '%s' will not be initialized as it may cause instability.\n", mod->resource);
+ return 1;
+ }
+
+ return 0;
+}
+
+static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only)
+{
+ struct ast_module *mod;
+ enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
+ char tmp[256];
+
+ if ((mod = find_resource(resource_name, 0))) {
+ if (mod->flags.running) {
+ ast_log(LOG_WARNING, "Module '%s' already exists.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS))
+ return AST_MODULE_LOAD_SKIP;
+ } else {
+#ifdef LOADABLE_MODULES
+ if (!(mod = load_dynamic_module(resource_name, global_symbols_only))) {
+ /* don't generate a warning message during load_modules() */
+ if (!global_symbols_only) {
+ ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+ } else {
+ return AST_MODULE_LOAD_SKIP;
+ }
+ }
+#else
+ ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+#endif
+ }
+
+ if (inspect_module(mod)) {
+ ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
+#ifdef LOADABLE_MODULES
+ unload_dynamic_module(mod);
+#endif
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (!mod->lib && mod->info->backup_globals()) {
+ ast_log(LOG_WARNING, "Module '%s' was unable to backup its global data.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ mod->flags.declined = 0;
+
+ if (mod->info->load)
+ res = mod->info->load();
+
+ switch (res) {
+ case AST_MODULE_LOAD_SUCCESS:
+ if (!ast_fully_booted) {
+ ast_verb(1, "%s => (%s)\n", resource_name, term_color(tmp, mod->info->description, COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
+ if (ast_opt_console && !option_verbose)
+ ast_verbose( ".");
+ } else {
+ ast_verb(1, "Loaded %s => (%s)\n", resource_name, mod->info->description);
+ }
+
+ mod->flags.running = 1;
+
+ ast_update_use_count();
+ break;
+ case AST_MODULE_LOAD_DECLINE:
+ mod->flags.declined = 1;
+ break;
+ case AST_MODULE_LOAD_FAILURE:
+ break;
+ case AST_MODULE_LOAD_SKIP:
+ /* modules should never return this value */
+ break;
+ }
+
+ return res;
+}
+
+int ast_load_resource(const char *resource_name)
+{
+ AST_LIST_LOCK(&module_list);
+ load_resource(resource_name, 0);
+ AST_LIST_UNLOCK(&module_list);
+
+ return 0;
+}
+
+struct load_order_entry {
+ char *resource;
+ AST_LIST_ENTRY(load_order_entry) entry;
+};
+
+AST_LIST_HEAD_NOLOCK(load_order, load_order_entry);
+
+static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order)
+{
+ struct load_order_entry *order;
+
+ AST_LIST_TRAVERSE(load_order, order, entry) {
+ if (!resource_name_match(order->resource, resource))
+ return NULL;
+ }
+
+ if (!(order = ast_calloc(1, sizeof(*order))))
+ return NULL;
+
+ order->resource = ast_strdup(resource);
+ AST_LIST_INSERT_TAIL(load_order, order, entry);
+
+ return order;
+}
+int load_modules(unsigned int preload_only)
+{
+ struct ast_config *cfg;
+ struct ast_module *mod;
+ struct load_order_entry *order;
+ struct ast_variable *v;
+ unsigned int load_count;
+ struct load_order load_order;
+ int res = 0;
+ struct ast_flags config_flags = { 0 };
+ int modulecount = 0;
+#ifdef LOADABLE_MODULES
+ struct dirent *dirent;
+ DIR *dir;
+#endif
+
+ /* all embedded modules have registered themselves by now */
+ embedding = 0;
+
+ ast_verb(1, "Asterisk Dynamic Loader Starting:\n");
+
+ AST_LIST_HEAD_INIT_NOLOCK(&load_order);
+
+ AST_LIST_LOCK(&module_list);
+
+ if (embedded_module_list.first) {
+ module_list.first = embedded_module_list.first;
+ module_list.last = embedded_module_list.last;
+ embedded_module_list.first = NULL;
+ }
+
+ if (!(cfg = ast_config_load(AST_MODULE_CONFIG, config_flags))) {
+ ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG);
+ goto done;
+ }
+
+ /* first, find all the modules we have been explicitly requested to load */
+ for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
+ if (!strcasecmp(v->name, preload_only ? "preload" : "load"))
+ add_to_load_order(v->value, &load_order);
+ }
+
+ /* check if 'autoload' is on */
+ if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
+ /* if so, first add all the embedded modules that are not already running to the load order */
+ AST_LIST_TRAVERSE(&module_list, mod, entry) {
+ /* if it's not embedded, skip it */
+ if (mod->lib)
+ continue;
+
+ if (mod->flags.running)
+ continue;
+
+ order = add_to_load_order(mod->resource, &load_order);
+ }
+
+#ifdef LOADABLE_MODULES
+ /* if we are allowed to load dynamic modules, scan the directory for
+ for all available modules and add them as well */
+ if ((dir = opendir(ast_config_AST_MODULE_DIR))) {
+ while ((dirent = readdir(dir))) {
+ int ld = strlen(dirent->d_name);
+
+ /* Must end in .so to load it. */
+
+ if (ld < 4)
+ continue;
+
+ if (strcasecmp(dirent->d_name + ld - 3, ".so"))
+ continue;
+
+ /* if there is already a module by this name in the module_list,
+ skip this file */
+ if (find_resource(dirent->d_name, 0))
+ continue;
+
+ add_to_load_order(dirent->d_name, &load_order);
+ }
+
+ closedir(dir);
+ } else {
+ if (!ast_opt_quiet)
+ ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n",
+ ast_config_AST_MODULE_DIR);
+ }
+#endif
+ }
+
+ /* now scan the config for any modules we are prohibited from loading and
+ remove them from the load order */
+ for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
+ if (strcasecmp(v->name, "noload"))
+ continue;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
+ if (!resource_name_match(order->resource, v->value)) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_free(order->resource);
+ ast_free(order);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+
+ /* we are done with the config now, all the information we need is in the
+ load_order list */
+ ast_config_destroy(cfg);
+
+ load_count = 0;
+ AST_LIST_TRAVERSE(&load_order, order, entry)
+ load_count++;
+
+ if (load_count)
+ ast_log(LOG_NOTICE, "%d modules will be loaded.\n", load_count);
+
+ /* first, load only modules that provide global symbols */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
+ switch (load_resource(order->resource, 1)) {
+ case AST_MODULE_LOAD_SUCCESS:
+ modulecount++;
+ case AST_MODULE_LOAD_DECLINE:
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_free(order->resource);
+ ast_free(order);
+ break;
+ case AST_MODULE_LOAD_FAILURE:
+ res = -1;
+ goto done;
+ case AST_MODULE_LOAD_SKIP:
+ /* try again later */
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* now load everything else */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
+ switch (load_resource(order->resource, 0)) {
+ case AST_MODULE_LOAD_SUCCESS:
+ modulecount++;
+ case AST_MODULE_LOAD_DECLINE:
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_free(order->resource);
+ ast_free(order);
+ break;
+ case AST_MODULE_LOAD_FAILURE:
+ res = -1;
+ goto done;
+ case AST_MODULE_LOAD_SKIP:
+ /* should not happen */
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+done:
+ while ((order = AST_LIST_REMOVE_HEAD(&load_order, entry))) {
+ ast_free(order->resource);
+ ast_free(order);
+ }
+
+ AST_LIST_UNLOCK(&module_list);
+
+ /* Tell manager clients that are aggressive at logging in that we're done
+ loading modules. If there's a DNS problem in chan_sip, we might not
+ even reach this */
+ manager_event(EVENT_FLAG_SYSTEM, "ModuleLoadReport", "ModuleLoadStatus: Done\r\nModuleSelection: %s\r\nModuleCount: %d\r\n", preload_only ? "Preload" : "All", modulecount);
+
+ return res;
+}
+
+void ast_update_use_count(void)
+{
+ /* Notify any module monitors that the use count for a
+ resource has changed */
+ struct loadupdate *m;
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE(&updaters, m, entry)
+ m->updater();
+ AST_LIST_UNLOCK(&module_list);
+}
+
+int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
+ const char *like)
+{
+ struct ast_module *cur;
+ int unlock = -1;
+ int total_mod_loaded = 0;
+
+ if (AST_LIST_TRYLOCK(&module_list))
+ unlock = 0;
+
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, like);
+ }
+
+ if (unlock)
+ AST_LIST_UNLOCK(&module_list);
+
+ return total_mod_loaded;
+}
+
+/*! \brief Check if module exists */
+int ast_module_check(const char *name)
+{
+ struct ast_module *cur;
+
+ if (ast_strlen_zero(name))
+ return 0; /* FALSE */
+
+ cur = find_resource(name, 1);
+
+ return (cur != NULL);
+}
+
+
+int ast_loader_register(int (*v)(void))
+{
+ struct loadupdate *tmp;
+
+ if (!(tmp = ast_malloc(sizeof(*tmp))))
+ return -1;
+
+ tmp->updater = v;
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
+ AST_LIST_UNLOCK(&module_list);
+
+ return 0;
+}
+
+int ast_loader_unregister(int (*v)(void))
+{
+ struct loadupdate *cur;
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
+ if (cur->updater == v) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&module_list);
+
+ return cur ? 0 : -1;
+}
+
+struct ast_module *ast_module_ref(struct ast_module *mod)
+{
+ ast_atomic_fetchadd_int(&mod->usecount, +1);
+ ast_update_use_count();
+
+ return mod;
+}
+
+void ast_module_unref(struct ast_module *mod)
+{
+ ast_atomic_fetchadd_int(&mod->usecount, -1);
+ ast_update_use_count();
+}
diff --git a/trunk/main/logger.c b/trunk/main/logger.c
new file mode 100644
index 000000000..252b620b6
--- /dev/null
+++ b/trunk/main/logger.c
@@ -0,0 +1,1203 @@
+/*
+ * 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 Asterisk Logger
+ *
+ * Logging routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+/*
+ * define _ASTERISK_LOGGER_H to prevent the inclusion of logger.h;
+ * it redefines LOG_* which we need to define syslog_level_map.
+ * Later, we force the inclusion of logger.h again.
+ */
+#define _ASTERISK_LOGGER_H
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include <signal.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#if ((defined(AST_DEVMODE)) && (defined(linux)))
+#include <execinfo.h>
+#define MAX_BACKTRACE_FRAMES 20
+#endif
+
+#define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
+ from <syslog.h> which is included by logger.h */
+#include <syslog.h>
+
+static int syslog_level_map[] = {
+ LOG_DEBUG,
+ LOG_INFO, /* arbitrary equivalent of LOG_EVENT */
+ LOG_NOTICE,
+ LOG_WARNING,
+ LOG_ERR,
+ LOG_DEBUG,
+ LOG_DEBUG
+};
+
+#define SYSLOG_NLEVELS sizeof(syslog_level_map) / sizeof(int)
+
+#undef _ASTERISK_LOGGER_H /* now include logger.h */
+#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/term.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/manager.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/strings.h"
+#include "asterisk/pbx.h"
+
+#if defined(__linux__) && !defined(__NR_gettid)
+#include <asm/unistd.h>
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid)
+#define GETTID() syscall(__NR_gettid)
+#else
+#define GETTID() getpid()
+#endif
+
+static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
+
+static char queue_log_name[256] = QUEUELOG;
+static char exec_after_rotate[256] = "";
+
+static int filesize_reload_needed;
+static int global_logmask = -1;
+
+enum rotatestrategy {
+ SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */
+ ROTATE = 1 << 1, /* Rotate all files, such that the oldest file has the highest suffix */
+ TIMESTAMP = 1 << 2, /* Append the epoch timestamp onto the end of the archived file */
+} rotatestrategy = SEQUENTIAL;
+
+static struct {
+ unsigned int queue_log:1;
+ unsigned int event_log:1;
+} logfiles = { 1, 1 };
+
+static char hostname[MAXHOSTNAMELEN];
+
+enum logtypes {
+ LOGTYPE_SYSLOG,
+ LOGTYPE_FILE,
+ LOGTYPE_CONSOLE,
+};
+
+struct logchannel {
+ int logmask; /* What to log to this channel */
+ int disabled; /* If this channel is disabled or not */
+ int facility; /* syslog facility */
+ enum logtypes type; /* Type of log channel */
+ FILE *fileptr; /* logfile logging file pointer */
+ char filename[256]; /* Filename */
+ AST_LIST_ENTRY(logchannel) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(logchannels, logchannel);
+
+enum logmsgtypes {
+ LOGMSG_NORMAL = 0,
+ LOGMSG_VERBOSE,
+};
+
+struct logmsg {
+ enum logmsgtypes type;
+ char date[256];
+ int level;
+ const char *file;
+ int line;
+ const char *function;
+ AST_LIST_ENTRY(logmsg) list;
+ char str[0];
+};
+
+static AST_LIST_HEAD_STATIC(logmsgs, logmsg);
+static pthread_t logthread = AST_PTHREADT_NULL;
+static ast_cond_t logcond;
+static int close_logger_thread;
+
+static FILE *eventlog;
+static FILE *qlog;
+
+/*! \brief Logging channels used in the Asterisk logging system */
+static char *levels[] = {
+ "DEBUG",
+ "EVENT",
+ "NOTICE",
+ "WARNING",
+ "ERROR",
+ "VERBOSE",
+ "DTMF"
+};
+
+/*! \brief Colors used in the console for logging */
+static int colors[] = {
+ COLOR_BRGREEN,
+ COLOR_BRBLUE,
+ COLOR_YELLOW,
+ COLOR_BRRED,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_BRGREEN
+};
+
+AST_THREADSTORAGE(verbose_buf);
+#define VERBOSE_BUF_INIT_SIZE 256
+
+AST_THREADSTORAGE(log_buf);
+#define LOG_BUF_INIT_SIZE 256
+
+static int make_components(const char *s, int lineno)
+{
+ char *w;
+ int res = 0;
+ char *stringp = ast_strdupa(s);
+
+ while ((w = strsep(&stringp, ","))) {
+ w = ast_skip_blanks(w);
+ if (!strcasecmp(w, "error"))
+ res |= (1 << __LOG_ERROR);
+ else if (!strcasecmp(w, "warning"))
+ res |= (1 << __LOG_WARNING);
+ else if (!strcasecmp(w, "notice"))
+ res |= (1 << __LOG_NOTICE);
+ else if (!strcasecmp(w, "event"))
+ res |= (1 << __LOG_EVENT);
+ else if (!strcasecmp(w, "debug"))
+ res |= (1 << __LOG_DEBUG);
+ else if (!strcasecmp(w, "verbose"))
+ res |= (1 << __LOG_VERBOSE);
+ else if (!strcasecmp(w, "dtmf"))
+ res |= (1 << __LOG_DTMF);
+ else {
+ fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
+ }
+ }
+
+ return res;
+}
+
+static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno)
+{
+ struct logchannel *chan;
+ char *facility;
+#ifndef SOLARIS
+ CODE *cptr;
+#endif
+
+ if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan))))
+ return NULL;
+
+ if (!strcasecmp(channel, "console")) {
+ chan->type = LOGTYPE_CONSOLE;
+ } else if (!strncasecmp(channel, "syslog", 6)) {
+ /*
+ * syntax is:
+ * syslog.facility => level,level,level
+ */
+ facility = strchr(channel, '.');
+ if (!facility++ || !facility) {
+ facility = "local0";
+ }
+
+#ifndef SOLARIS
+ /*
+ * Walk through the list of facilitynames (defined in sys/syslog.h)
+ * to see if we can find the one we have been given
+ */
+ chan->facility = -1;
+ cptr = facilitynames;
+ while (cptr->c_name) {
+ if (!strcasecmp(facility, cptr->c_name)) {
+ chan->facility = cptr->c_val;
+ break;
+ }
+ cptr++;
+ }
+#else
+ chan->facility = -1;
+ if (!strcasecmp(facility, "kern"))
+ chan->facility = LOG_KERN;
+ else if (!strcasecmp(facility, "USER"))
+ chan->facility = LOG_USER;
+ else if (!strcasecmp(facility, "MAIL"))
+ chan->facility = LOG_MAIL;
+ else if (!strcasecmp(facility, "DAEMON"))
+ chan->facility = LOG_DAEMON;
+ else if (!strcasecmp(facility, "AUTH"))
+ chan->facility = LOG_AUTH;
+ else if (!strcasecmp(facility, "SYSLOG"))
+ chan->facility = LOG_SYSLOG;
+ else if (!strcasecmp(facility, "LPR"))
+ chan->facility = LOG_LPR;
+ else if (!strcasecmp(facility, "NEWS"))
+ chan->facility = LOG_NEWS;
+ else if (!strcasecmp(facility, "UUCP"))
+ chan->facility = LOG_UUCP;
+ else if (!strcasecmp(facility, "CRON"))
+ chan->facility = LOG_CRON;
+ else if (!strcasecmp(facility, "LOCAL0"))
+ chan->facility = LOG_LOCAL0;
+ else if (!strcasecmp(facility, "LOCAL1"))
+ chan->facility = LOG_LOCAL1;
+ else if (!strcasecmp(facility, "LOCAL2"))
+ chan->facility = LOG_LOCAL2;
+ else if (!strcasecmp(facility, "LOCAL3"))
+ chan->facility = LOG_LOCAL3;
+ else if (!strcasecmp(facility, "LOCAL4"))
+ chan->facility = LOG_LOCAL4;
+ else if (!strcasecmp(facility, "LOCAL5"))
+ chan->facility = LOG_LOCAL5;
+ else if (!strcasecmp(facility, "LOCAL6"))
+ chan->facility = LOG_LOCAL6;
+ else if (!strcasecmp(facility, "LOCAL7"))
+ chan->facility = LOG_LOCAL7;
+#endif /* Solaris */
+
+ if (0 > chan->facility) {
+ fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
+ ast_free(chan);
+ return NULL;
+ }
+
+ chan->type = LOGTYPE_SYSLOG;
+ snprintf(chan->filename, sizeof(chan->filename), "%s", channel);
+ openlog("asterisk", LOG_PID, chan->facility);
+ } else {
+ if (channel[0] == '/') {
+ if (!ast_strlen_zero(hostname)) {
+ snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname);
+ } else {
+ ast_copy_string(chan->filename, channel, sizeof(chan->filename));
+ }
+ }
+
+ if (!ast_strlen_zero(hostname)) {
+ snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s", ast_config_AST_LOG_DIR, channel, hostname);
+ } else {
+ snprintf(chan->filename, sizeof(chan->filename), "%s/%s", ast_config_AST_LOG_DIR, channel);
+ }
+ chan->fileptr = fopen(chan->filename, "a");
+ if (!chan->fileptr) {
+ /* Can't log here, since we're called with a lock */
+ fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
+ }
+ chan->type = LOGTYPE_FILE;
+ }
+ chan->logmask = make_components(components, lineno);
+ return chan;
+}
+
+static void init_logger_chain(int reload, int locked)
+{
+ struct logchannel *chan;
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ const char *s;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("logger.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return;
+
+ /* delete our list of log channels */
+ if (!locked)
+ AST_RWLIST_WRLOCK(&logchannels);
+ while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list)))
+ ast_free(chan);
+ if (!locked)
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ global_logmask = 0;
+ errno = 0;
+ /* close syslog */
+ closelog();
+
+ /* If no config file, we're fine, set default options. */
+ if (!cfg) {
+ if (errno)
+ fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
+ else
+ fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
+ if (!(chan = ast_calloc(1, sizeof(*chan))))
+ return;
+ chan->type = LOGTYPE_CONSOLE;
+ chan->logmask = 28; /*warning,notice,error */
+ if (!locked)
+ AST_RWLIST_WRLOCK(&logchannels);
+ AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
+ if (!locked)
+ AST_RWLIST_UNLOCK(&logchannels);
+ global_logmask |= chan->logmask;
+ return;
+ }
+
+ if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
+ if (ast_true(s)) {
+ if (gethostname(hostname, sizeof(hostname) - 1)) {
+ ast_copy_string(hostname, "unknown", sizeof(hostname));
+ fprintf(stderr, "What box has no hostname???\n");
+ }
+ } else
+ hostname[0] = '\0';
+ } else
+ hostname[0] = '\0';
+ if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
+ ast_copy_string(dateformat, s, sizeof(dateformat));
+ else
+ ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+ if ((s = ast_variable_retrieve(cfg, "general", "queue_log")))
+ logfiles.queue_log = ast_true(s);
+ if ((s = ast_variable_retrieve(cfg, "general", "event_log")))
+ logfiles.event_log = ast_true(s);
+ if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name")))
+ ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
+ if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate")))
+ ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
+ if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
+ if (strcasecmp(s, "timestamp") == 0)
+ rotatestrategy = TIMESTAMP;
+ else if (strcasecmp(s, "rotate") == 0)
+ rotatestrategy = ROTATE;
+ else if (strcasecmp(s, "sequential") == 0)
+ rotatestrategy = SEQUENTIAL;
+ else
+ fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
+ } else {
+ if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
+ rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
+ fprintf(stderr, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
+ }
+ }
+
+ if (!locked)
+ AST_RWLIST_WRLOCK(&logchannels);
+ var = ast_variable_browse(cfg, "logfiles");
+ for (; var; var = var->next) {
+ if (!(chan = make_logchannel(var->name, var->value, var->lineno)))
+ continue;
+ AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
+ global_logmask |= chan->logmask;
+ }
+ if (!locked)
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ ast_config_destroy(cfg);
+}
+
+void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
+{
+ va_list ap;
+ char qlog_msg[8192];
+ int qlog_len;
+ char time_str[16];
+
+ if (ast_check_realtime("queue_log")) {
+ va_start(ap, fmt);
+ vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
+ va_end(ap);
+ snprintf(time_str, sizeof(time_str), "%ld", (long)time(NULL));
+ ast_store_realtime("queue_log", "time", time_str,
+ "callid", callid,
+ "queuename", queuename,
+ "agent", agent,
+ "event", event,
+ "data", qlog_msg,
+ NULL);
+ } else {
+ if (qlog) {
+ va_start(ap, fmt);
+ qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
+ vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
+ va_end(ap);
+ }
+ AST_RWLIST_RDLOCK(&logchannels);
+ if (qlog) {
+ fprintf(qlog, "%s\n", qlog_msg);
+ fflush(qlog);
+ }
+ AST_RWLIST_UNLOCK(&logchannels);
+ }
+}
+
+static int rotate_file(const char *filename)
+{
+ char old[PATH_MAX];
+ char new[PATH_MAX];
+ int x, y, which, found, res = 0, fd;
+ char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
+
+ switch (rotatestrategy) {
+ case SEQUENTIAL:
+ for (x = 0; ; x++) {
+ snprintf(new, sizeof(new), "%s.%d", filename, x);
+ fd = open(new, O_RDONLY);
+ if (fd > -1)
+ close(fd);
+ else
+ break;
+ }
+ if (rename(filename, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
+ res = -1;
+ }
+ break;
+ case TIMESTAMP:
+ snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
+ if (rename(filename, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
+ res = -1;
+ }
+ break;
+ case ROTATE:
+ /* Find the next empty slot, including a possible suffix */
+ for (x = 0; ; x++) {
+ found = 0;
+ for (which = 0; which < sizeof(suffixes) / sizeof(suffixes[0]); which++) {
+ snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
+ fd = open(new, O_RDONLY);
+ if (fd > -1)
+ close(fd);
+ else {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+
+ /* Found an empty slot */
+ for (y = x; y > -1; y--) {
+ for (which = 0; which < sizeof(suffixes) / sizeof(suffixes[0]); which++) {
+ snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
+ fd = open(old, O_RDONLY);
+ if (fd > -1) {
+ /* Found the right suffix */
+ close(fd);
+ snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
+ if (rename(old, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
+ res = -1;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Finally, rename the current file */
+ snprintf(new, sizeof(new), "%s.0", filename);
+ if (rename(filename, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
+ res = -1;
+ }
+ }
+
+ if (!ast_strlen_zero(exec_after_rotate)) {
+ struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Logger/rotate");
+ char buf[512];
+ pbx_builtin_setvar_helper(c, "filename", filename);
+ pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
+ system(buf);
+ ast_channel_free(c);
+ }
+ return res;
+}
+
+static int reload_logger(int rotate)
+{
+ char old[PATH_MAX] = "";
+ int event_rotate = rotate, queue_rotate = rotate;
+ struct logchannel *f;
+ int res = 0;
+ struct stat st;
+
+ AST_RWLIST_WRLOCK(&logchannels);
+
+ if (eventlog) {
+ if (rotate < 0) {
+ /* Check filesize - this one typically doesn't need an auto-rotate */
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
+ if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */
+ fclose(eventlog);
+ eventlog = NULL;
+ } else
+ event_rotate = 0;
+ } else {
+ fclose(eventlog);
+ eventlog = NULL;
+ }
+ } else
+ event_rotate = 0;
+
+ if (qlog) {
+ if (rotate < 0) {
+ /* Check filesize - this one typically doesn't need an auto-rotate */
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
+ if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */
+ fclose(qlog);
+ qlog = NULL;
+ } else
+ event_rotate = 0;
+ } else {
+ fclose(qlog);
+ qlog = NULL;
+ }
+ } else
+ queue_rotate = 0;
+ qlog = NULL;
+
+ ast_mkdir(ast_config_AST_LOG_DIR, 0777);
+
+ AST_RWLIST_TRAVERSE(&logchannels, f, list) {
+ if (f->disabled) {
+ f->disabled = 0; /* Re-enable logging at reload */
+ manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
+ }
+ if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
+ fclose(f->fileptr); /* Close file */
+ f->fileptr = NULL;
+ if (rotate)
+ rotate_file(f->filename);
+ }
+ }
+
+ filesize_reload_needed = 0;
+
+ init_logger_chain(rotate ? 0 : 1 /* reload */, 1 /* locked */);
+
+ if (logfiles.event_log) {
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
+ if (event_rotate)
+ rotate_file(old);
+
+ eventlog = fopen(old, "a");
+ if (eventlog) {
+ ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
+ ast_verb(1, "Asterisk Event Logger restarted\n");
+ } else {
+ ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
+ res = -1;
+ }
+ }
+
+ if (logfiles.queue_log) {
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
+ if (queue_rotate)
+ rotate_file(old);
+
+ qlog = fopen(old, "a");
+ if (qlog) {
+ AST_RWLIST_UNLOCK(&logchannels);
+ ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
+ AST_RWLIST_WRLOCK(&logchannels);
+ ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
+ ast_verb(1, "Asterisk Queue Logger restarted\n");
+ } else {
+ ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
+ res = -1;
+ }
+ }
+
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ return res;
+}
+
+/*! \brief Reload the logger module without rotating log files (also used from loader.c during
+ a full Asterisk reload) */
+int logger_reload(void)
+{
+ if(reload_logger(0))
+ return RESULT_FAILURE;
+ return RESULT_SUCCESS;
+}
+
+static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger reload";
+ e->usage =
+ "Usage: logger reload\n"
+ " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (reload_logger(0)) {
+ ast_cli(a->fd, "Failed to reload the logger\n");
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger rotate";
+ e->usage =
+ "Usage: logger rotate\n"
+ " Rotates and Reopens the log files.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (reload_logger(1)) {
+ ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command to show logging system configuration */
+static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMATL "%-35.35s %-8.8s %-9.9s "
+ struct logchannel *chan;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger show channels";
+ e->usage =
+ "Usage: logger show channels\n"
+ " List configured logger channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ ast_cli(a->fd,FORMATL, "Channel", "Type", "Status");
+ ast_cli(a->fd, "Configuration\n");
+ ast_cli(a->fd,FORMATL, "-------", "----", "------");
+ ast_cli(a->fd, "-------------\n");
+ AST_RWLIST_RDLOCK(&logchannels);
+ AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
+ ast_cli(a->fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
+ chan->disabled ? "Disabled" : "Enabled");
+ ast_cli(a->fd, " - ");
+ if (chan->logmask & (1 << __LOG_DEBUG))
+ ast_cli(a->fd, "Debug ");
+ if (chan->logmask & (1 << __LOG_DTMF))
+ ast_cli(a->fd, "DTMF ");
+ if (chan->logmask & (1 << __LOG_VERBOSE))
+ ast_cli(a->fd, "Verbose ");
+ if (chan->logmask & (1 << __LOG_WARNING))
+ ast_cli(a->fd, "Warning ");
+ if (chan->logmask & (1 << __LOG_NOTICE))
+ ast_cli(a->fd, "Notice ");
+ if (chan->logmask & (1 << __LOG_ERROR))
+ ast_cli(a->fd, "Error ");
+ if (chan->logmask & (1 << __LOG_EVENT))
+ ast_cli(a->fd, "Event ");
+ ast_cli(a->fd, "\n");
+ }
+ AST_RWLIST_UNLOCK(&logchannels);
+ ast_cli(a->fd, "\n");
+
+ return CLI_SUCCESS;
+}
+
+struct verb {
+ void (*verboser)(const char *string);
+ AST_LIST_ENTRY(verb) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(verbosers, verb);
+
+static struct ast_cli_entry cli_logger[] = {
+ AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
+ AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
+ AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files")
+};
+
+static int handle_SIGXFSZ(int sig)
+{
+ /* Indicate need to reload */
+ filesize_reload_needed = 1;
+ return 0;
+}
+
+static void ast_log_vsyslog(int level, const char *file, int line, const char *function, char *str)
+{
+ char buf[BUFSIZ];
+
+ if (level >= SYSLOG_NLEVELS) {
+ /* we are locked here, so cannot ast_log() */
+ fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
+ return;
+ }
+
+ if (level == __LOG_VERBOSE) {
+ snprintf(buf, sizeof(buf), "VERBOSE[%ld]: %s", (long)GETTID(), str);
+ level = __LOG_DEBUG;
+ } else if (level == __LOG_DTMF) {
+ snprintf(buf, sizeof(buf), "DTMF[%ld]: %s", (long)GETTID(), str);
+ level = __LOG_DEBUG;
+ } else {
+ snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: %s",
+ levels[level], (long)GETTID(), file, line, function, str);
+ }
+
+ term_strip(buf, buf, strlen(buf) + 1);
+ syslog(syslog_level_map[level], "%s", buf);
+}
+
+/*! \brief Print a normal log message to the channels */
+static void logger_print_normal(struct logmsg *logmsg)
+{
+ struct logchannel *chan = NULL;
+ char buf[BUFSIZ];
+
+ AST_RWLIST_RDLOCK(&logchannels);
+
+ if (logfiles.event_log && logmsg->level == __LOG_EVENT) {
+ fprintf(eventlog, "%s asterisk[%ld]: %s", logmsg->date, (long)getpid(), logmsg->str);
+ fflush(eventlog);
+ AST_RWLIST_UNLOCK(&logchannels);
+ return;
+ }
+
+ if (!AST_RWLIST_EMPTY(&logchannels)) {
+ AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
+ /* If the channel is disabled, then move on to the next one */
+ if (chan->disabled)
+ continue;
+ /* Check syslog channels */
+ if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
+ ast_log_vsyslog(logmsg->level, logmsg->file, logmsg->line, logmsg->function, logmsg->str);
+ /* Console channels */
+ } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
+ char linestr[128];
+ char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
+
+ /* If the level is verbose, then skip it */
+ if (logmsg->level == __LOG_VERBOSE)
+ continue;
+
+ /* Turn the numerical line number into a string */
+ snprintf(linestr, sizeof(linestr), "%d", logmsg->line);
+ /* Build string to print out */
+ snprintf(buf, sizeof(buf), "[%s] %s[%ld]: %s:%s %s: %s",
+ logmsg->date,
+ term_color(tmp1, levels[logmsg->level], colors[logmsg->level], 0, sizeof(tmp1)),
+ (long)GETTID(),
+ term_color(tmp2, logmsg->file, COLOR_BRWHITE, 0, sizeof(tmp2)),
+ term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
+ term_color(tmp4, logmsg->function, COLOR_BRWHITE, 0, sizeof(tmp4)),
+ logmsg->str);
+ /* Print out */
+ ast_console_puts_mutable(buf);
+ /* File channels */
+ } else if (chan->type == LOGTYPE_FILE && (chan->logmask & (1 << logmsg->level))) {
+ int res = 0;
+
+ /* If no file pointer exists, skip it */
+ if (!chan->fileptr)
+ continue;
+
+ /* Print out to the file */
+ res = fprintf(chan->fileptr, "[%s] %s[%ld] %s: %s",
+ logmsg->date, levels[logmsg->level], (long)GETTID(), logmsg->file, logmsg->str);
+ if (res <= 0 && !ast_strlen_zero(logmsg->str)) {
+ fprintf(stderr, "**** Asterisk Logging Error: ***********\n");
+ if (errno == ENOMEM || errno == ENOSPC)
+ fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
+ else
+ fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
+ manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
+ chan->disabled = 1;
+ } else if (res > 0) {
+ fflush(chan->fileptr);
+ }
+ }
+ }
+ } else if (logmsg->level != __LOG_VERBOSE) {
+ fputs(logmsg->str, stdout);
+ }
+
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ /* If we need to reload because of the file size, then do so */
+ if (filesize_reload_needed) {
+ reload_logger(-1);
+ ast_log(LOG_EVENT, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
+ if (option_verbose)
+ ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
+ }
+
+ return;
+}
+
+/*! \brief Print a verbose message to the verbosers */
+static void logger_print_verbose(struct logmsg *logmsg)
+{
+ struct verb *v = NULL;
+
+ /* Iterate through the list of verbosers and pass them the log message string */
+ AST_RWLIST_RDLOCK(&verbosers);
+ AST_RWLIST_TRAVERSE(&verbosers, v, list)
+ v->verboser(logmsg->str);
+ AST_RWLIST_UNLOCK(&verbosers);
+
+ return;
+}
+
+/*! \brief Actual logging thread */
+static void *logger_thread(void *data)
+{
+ struct logmsg *next = NULL, *msg = NULL;
+
+ for (;;) {
+ /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */
+ AST_LIST_LOCK(&logmsgs);
+ if (AST_LIST_EMPTY(&logmsgs))
+ ast_cond_wait(&logcond, &logmsgs.lock);
+ next = AST_LIST_FIRST(&logmsgs);
+ AST_LIST_HEAD_INIT_NOLOCK(&logmsgs);
+ AST_LIST_UNLOCK(&logmsgs);
+
+ /* Otherwise go through and process each message in the order added */
+ while ((msg = next)) {
+ /* Get the next entry now so that we can free our current structure later */
+ next = AST_LIST_NEXT(msg, list);
+
+ /* Depending on the type, send it to the proper function */
+ if (msg->type == LOGMSG_NORMAL)
+ logger_print_normal(msg);
+ else if (msg->type == LOGMSG_VERBOSE)
+ logger_print_verbose(msg);
+
+ /* Free the data since we are done */
+ ast_free(msg);
+ }
+
+ /* If we should stop, then stop */
+ if (close_logger_thread)
+ break;
+ }
+
+ return NULL;
+}
+
+int init_logger(void)
+{
+ char tmp[256];
+ int res = 0;
+
+ /* auto rotate if sig SIGXFSZ comes a-knockin */
+ (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
+
+ /* start logger thread */
+ ast_cond_init(&logcond, NULL);
+ if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
+ ast_cond_destroy(&logcond);
+ return -1;
+ }
+
+ /* register the logger cli commands */
+ ast_cli_register_multiple(cli_logger, sizeof(cli_logger) / sizeof(struct ast_cli_entry));
+
+ ast_mkdir(ast_config_AST_LOG_DIR, 0777);
+
+ /* create log channels */
+ init_logger_chain(0 /* reload */, 0 /* locked */);
+
+ /* create the eventlog */
+ if (logfiles.event_log) {
+ snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
+ eventlog = fopen(tmp, "a");
+ if (eventlog) {
+ ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
+ if (option_verbose)
+ ast_verbose("Asterisk Event Logger Started %s\n", tmp);
+ } else {
+ ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
+ res = -1;
+ }
+ }
+
+ if (logfiles.queue_log) {
+ snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
+ qlog = fopen(tmp, "a");
+ ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
+ }
+ return res;
+}
+
+void close_logger(void)
+{
+ struct logchannel *f = NULL;
+
+ /* Stop logger thread */
+ AST_LIST_LOCK(&logmsgs);
+ close_logger_thread = 1;
+ ast_cond_signal(&logcond);
+ AST_LIST_UNLOCK(&logmsgs);
+
+ if (logthread != AST_PTHREADT_NULL)
+ pthread_join(logthread, NULL);
+
+ AST_RWLIST_WRLOCK(&logchannels);
+
+ if (eventlog) {
+ fclose(eventlog);
+ eventlog = NULL;
+ }
+
+ if (qlog) {
+ fclose(qlog);
+ qlog = NULL;
+ }
+
+ AST_RWLIST_TRAVERSE(&logchannels, f, list) {
+ if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
+ fclose(f->fileptr);
+ f->fileptr = NULL;
+ }
+ }
+
+ closelog(); /* syslog */
+
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ return;
+}
+
+/*!
+ * \brief send log messages to syslog and/or the console
+ */
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ struct logmsg *logmsg = NULL;
+ struct ast_str *buf = NULL;
+ struct ast_tm tm;
+ struct timeval tv = ast_tvnow();
+ int res = 0;
+ va_list ap;
+
+ if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
+ return;
+
+ if (AST_RWLIST_EMPTY(&logchannels)) {
+ /*
+ * we don't have the logger chain configured yet,
+ * so just log to stdout
+ */
+ if (level != __LOG_VERBOSE) {
+ int res;
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, BUFSIZ, fmt, ap); /* XXX BUFSIZ ? */
+ va_end(ap);
+ if (res != AST_DYNSTR_BUILD_FAILED) {
+ term_filter_escapes(buf->str);
+ fputs(buf->str, stdout);
+ }
+ }
+ return;
+ }
+
+ /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
+ are non-zero; LOG_DEBUG messages can still be displayed if option_debug
+ is zero, if option_verbose is non-zero (this allows for 'level zero'
+ LOG_DEBUG messages to be displayed, if the logmask on any channel
+ allows it)
+ */
+ if (!option_verbose && !option_debug && (level == __LOG_DEBUG))
+ return;
+
+ /* Ignore anything that never gets logged anywhere */
+ if (!(global_logmask & (1 << level)))
+ return;
+
+ /* Build string */
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
+ va_end(ap);
+
+ /* If the build failed, then abort and free this structure */
+ if (res == AST_DYNSTR_BUILD_FAILED)
+ return;
+
+ /* Create a new logging message */
+ if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1)))
+ return;
+
+ /* Copy string over */
+ strcpy(logmsg->str, buf->str);
+
+ /* Set type to be normal */
+ logmsg->type = LOGMSG_NORMAL;
+
+ /* Create our date/time */
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(logmsg->date, sizeof(logmsg->date), dateformat, &tm);
+
+ /* Copy over data */
+ logmsg->level = level;
+ logmsg->file = file;
+ logmsg->line = line;
+ logmsg->function = function;
+
+ /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
+ if (logthread != AST_PTHREADT_NULL) {
+ AST_LIST_LOCK(&logmsgs);
+ AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
+ ast_cond_signal(&logcond);
+ AST_LIST_UNLOCK(&logmsgs);
+ } else {
+ logger_print_normal(logmsg);
+ ast_free(logmsg);
+ }
+
+ return;
+}
+
+void ast_backtrace(void)
+{
+#ifdef linux
+#ifdef AST_DEVMODE
+ int count=0, i=0;
+ void **addresses;
+ char **strings;
+
+ if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) {
+ count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
+ if ((strings = backtrace_symbols(addresses, count))) {
+ ast_debug(1, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
+ for (i=0; i < count ; i++) {
+#if __WORDSIZE == 32
+ ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
+#elif __WORDSIZE == 64
+ ast_log(LOG_DEBUG, "#%d: [%016lX] %s\n", i, (unsigned long)addresses[i], strings[i]);
+#endif
+ }
+ free(strings);
+ } else {
+ ast_debug(1, "Could not allocate memory for backtrace\n");
+ }
+ ast_free(addresses);
+ }
+#else
+ ast_log(LOG_WARNING, "Must run configure with '--enable-dev-mode' for stack backtraces.\n");
+#endif
+#else /* ndef linux */
+ ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
+#endif
+}
+
+void ast_verbose(const char *fmt, ...)
+{
+ struct logmsg *logmsg = NULL;
+ struct ast_str *buf = NULL;
+ int res = 0;
+ va_list ap;
+
+ if (!(buf = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)))
+ return;
+
+ if (ast_opt_timestamp) {
+ struct timeval tv;
+ struct ast_tm tm;
+ char date[40];
+ char *datefmt;
+
+ tv = ast_tvnow();
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(date, sizeof(date), dateformat, &tm);
+ datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
+ sprintf(datefmt, "[%s] %s", date, fmt);
+ fmt = datefmt;
+ }
+
+ /* Build string */
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ /* If the build failed then we can drop this allocated message */
+ if (res == AST_DYNSTR_BUILD_FAILED)
+ return;
+
+ if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1)))
+ return;
+
+ strcpy(logmsg->str, buf->str);
+
+ ast_log(LOG_VERBOSE, logmsg->str);
+
+ /* Set type */
+ logmsg->type = LOGMSG_VERBOSE;
+
+ /* Add to the list and poke the thread if possible */
+ if (logthread != AST_PTHREADT_NULL) {
+ AST_LIST_LOCK(&logmsgs);
+ AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
+ ast_cond_signal(&logcond);
+ AST_LIST_UNLOCK(&logmsgs);
+ } else {
+ logger_print_verbose(logmsg);
+ ast_free(logmsg);
+ }
+}
+
+int ast_register_verbose(void (*v)(const char *string))
+{
+ struct verb *verb;
+
+ if (!(verb = ast_malloc(sizeof(*verb))))
+ return -1;
+
+ verb->verboser = v;
+
+ AST_RWLIST_WRLOCK(&verbosers);
+ AST_RWLIST_INSERT_HEAD(&verbosers, verb, list);
+ AST_RWLIST_UNLOCK(&verbosers);
+
+ return 0;
+}
+
+int ast_unregister_verbose(void (*v)(const char *string))
+{
+ struct verb *cur;
+
+ AST_RWLIST_WRLOCK(&verbosers);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
+ if (cur->verboser == v) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(cur);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&verbosers);
+
+ return cur ? 0 : -1;
+}
diff --git a/trunk/main/manager.c b/trunk/main/manager.c
new file mode 100644
index 000000000..c9e69eb5b
--- /dev/null
+++ b/trunk/main/manager.c
@@ -0,0 +1,3768 @@
+/*
+ * 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 The Asterisk Management Interface - AMI
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \extref OpenSSL http://www.openssl.org - for AMI/SSL
+ *
+ * At the moment this file contains a number of functions, namely:
+ *
+ * - data structures storing AMI state
+ * - AMI-related API functions, used by internal asterisk components
+ * - handlers for AMI-related CLI functions
+ * - handlers for AMI functions (available through the AMI socket)
+ * - the code for the main AMI listener thread and individual session threads
+ * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
+ *
+ * \ref amiconf
+ */
+
+/*! \addtogroup Group_AMI AMI functions
+*/
+/*! @{
+ Doxygen group */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use various ast_config_AST_* */
+#include <ctype.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/mman.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/manager.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/callerid.h"
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/md5.h"
+#include "asterisk/acl.h"
+#include "asterisk/utils.h"
+#include "asterisk/tcptls.h"
+#include "asterisk/http.h"
+#include "asterisk/version.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/version.h"
+#include "asterisk/term.h"
+#include "asterisk/astobj2.h"
+
+/*!
+ * Linked list of events.
+ * Global events are appended to the list by append_event().
+ * The usecount is the number of stored pointers to the element,
+ * excluding the list pointers. So an element that is only in
+ * the list has a usecount of 0, not 1.
+ *
+ * Clients have a pointer to the last event processed, and for each
+ * of these clients we track the usecount of the elements.
+ * If we have a pointer to an entry in the list, it is safe to navigate
+ * it forward because elements will not be deleted, but only appended.
+ * The worst that can happen is seeing the pointer still NULL.
+ *
+ * When the usecount of an element drops to 0, and the element is the
+ * first in the list, we can remove it. Removal is done within the
+ * main thread, which is woken up for the purpose.
+ *
+ * For simplicity of implementation, we make sure the list is never empty.
+ */
+struct eventqent {
+ int usecount; /*!< # of clients who still need the event */
+ int category;
+ unsigned int seq; /*!< sequence number */
+ AST_LIST_ENTRY(eventqent) eq_next;
+ char eventdata[1]; /*!< really variable size, allocated by append_event() */
+};
+
+static AST_LIST_HEAD_STATIC(all_events, eventqent);
+
+static int displayconnects = 1;
+static int allowmultiplelogin = 1;
+static int timestampevents;
+static int httptimeout = 60;
+static int manager_enabled = 0;
+static int webmanager_enabled = 0;
+
+static int block_sockets;
+static int num_sessions;
+
+static int manager_debug; /*!< enable some debugging code in the manager */
+
+/*! \brief
+ * Descriptor for a manager session, either on the AMI socket or over HTTP.
+ *
+ * \note
+ * AMI session have managerid == 0; the entry is created upon a connect,
+ * and destroyed with the socket.
+ * HTTP sessions have managerid != 0, the value is used as a search key
+ * to lookup sessions (using the mansession_id cookie).
+ */
+static const char *command_blacklist[] = {
+ "module load",
+ "module unload",
+};
+
+struct mansession {
+ pthread_t ms_t; /*!< Execution thread, basically useless */
+ ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
+ /* XXX need to document which fields it is protecting */
+ struct sockaddr_in sin; /*!< address we are connecting from */
+ FILE *f; /*!< fdopen() on the underlying fd */
+ int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
+ int inuse; /*!< number of HTTP sessions using this entry */
+ int needdestroy; /*!< Whether an HTTP session should be destroyed */
+ pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
+ unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
+ time_t sessionstart; /*!< Session start time */
+ time_t sessiontimeout; /*!< Session timeout if HTTP */
+ char username[80]; /*!< Logged in username */
+ char challenge[10]; /*!< Authentication challenge */
+ int authenticated; /*!< Authentication status */
+ int readperm; /*!< Authorization for reading */
+ int writeperm; /*!< Authorization for writing */
+ char inbuf[1025]; /*!< Buffer */
+ /* we use the extra byte to add a '\0' and simplify parsing */
+ int inlen; /*!< number of buffered bytes */
+ int send_events; /*!< XXX what ? */
+ struct eventqent *last_ev; /*!< last event processed. */
+ int writetimeout; /*!< Timeout for ast_carefulwrite() */
+ AST_LIST_ENTRY(mansession) list;
+};
+
+#define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
+
+static AST_LIST_HEAD_STATIC(sessions, mansession);
+
+/*! \brief user descriptor, as read from the config file.
+ *
+ * \note It is still missing some fields -- e.g. we can have multiple permit and deny
+ * lines which are not supported here, and readperm/writeperm/writetimeout
+ * are not stored.
+ */
+struct ast_manager_user {
+ char username[80];
+ char *secret;
+ struct ast_ha *ha; /*!< ACL setting */
+ int readperm; /*! Authorization for reading */
+ int writeperm; /*! Authorization for writing */
+ int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
+ int displayconnects; /*!< XXX unused */
+ int keep; /*!< mark entries created on a reload */
+ AST_RWLIST_ENTRY(ast_manager_user) list;
+};
+
+/*! \brief list of users found in the config file */
+static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
+
+/*! \brief list of actions registered */
+static AST_RWLIST_HEAD_STATIC(actions, manager_action);
+
+/*! \brief list of hooks registered */
+static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
+
+/*! \brief Add a custom hook to be called when an event is fired */
+void ast_manager_register_hook(struct manager_custom_hook *hook)
+{
+ AST_RWLIST_WRLOCK(&manager_hooks);
+ AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
+ AST_RWLIST_UNLOCK(&manager_hooks);
+ return;
+}
+
+/*! \brief Delete a custom hook to be called when an event is fired */
+void ast_manager_unregister_hook(struct manager_custom_hook *hook)
+{
+ AST_RWLIST_WRLOCK(&manager_hooks);
+ AST_RWLIST_REMOVE(&manager_hooks, hook, list);
+ AST_RWLIST_UNLOCK(&manager_hooks);
+ return;
+}
+
+/*! \brief
+ * Event list management functions.
+ * We assume that the event list always has at least one element,
+ * and the delete code will not remove the last entry even if the
+ *
+ */
+#if 0
+static time_t __deb(time_t start, const char *msg)
+{
+ time_t now = time(NULL);
+ ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
+ if (start != 0 && now - start > 5)
+ ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
+ return now;
+}
+
+static void LOCK_EVENTS(void)
+{
+ time_t start = __deb(0, "about to lock events");
+ AST_LIST_LOCK(&all_events);
+ __deb(start, "done lock events");
+}
+
+static void UNLOCK_EVENTS(void)
+{
+ __deb(0, "about to unlock events");
+ AST_LIST_UNLOCK(&all_events);
+}
+
+static void LOCK_SESS(void)
+{
+ time_t start = __deb(0, "about to lock sessions");
+ AST_LIST_LOCK(&sessions);
+ __deb(start, "done lock sessions");
+}
+
+static void UNLOCK_SESS(void)
+{
+ __deb(0, "about to unlock sessions");
+ AST_LIST_UNLOCK(&sessions);
+}
+#endif
+
+int check_manager_enabled()
+{
+ return manager_enabled;
+}
+
+int check_webmanager_enabled()
+{
+ return (webmanager_enabled && manager_enabled);
+}
+
+/*!
+ * Grab a reference to the last event, update usecount as needed.
+ * Can handle a NULL pointer.
+ */
+static struct eventqent *grab_last(void)
+{
+ struct eventqent *ret;
+
+ AST_LIST_LOCK(&all_events);
+ ret = AST_LIST_LAST(&all_events);
+ /* the list is never empty now, but may become so when
+ * we optimize it in the future, so be prepared.
+ */
+ if (ret)
+ ast_atomic_fetchadd_int(&ret->usecount, 1);
+ AST_LIST_UNLOCK(&all_events);
+ return ret;
+}
+
+/*!
+ * Purge unused events. Remove elements from the head
+ * as long as their usecount is 0 and there is a next element.
+ */
+static void purge_events(void)
+{
+ struct eventqent *ev;
+
+ AST_LIST_LOCK(&all_events);
+ while ( (ev = AST_LIST_FIRST(&all_events)) &&
+ ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
+ AST_LIST_REMOVE_HEAD(&all_events, eq_next);
+ ast_free(ev);
+ }
+ AST_LIST_UNLOCK(&all_events);
+}
+
+/*!
+ * helper functions to convert back and forth between
+ * string and numeric representation of set of flags
+ */
+static struct permalias {
+ int num;
+ char *label;
+} perms[] = {
+ { EVENT_FLAG_SYSTEM, "system" },
+ { EVENT_FLAG_CALL, "call" },
+ { EVENT_FLAG_LOG, "log" },
+ { EVENT_FLAG_VERBOSE, "verbose" },
+ { EVENT_FLAG_COMMAND, "command" },
+ { EVENT_FLAG_AGENT, "agent" },
+ { EVENT_FLAG_USER, "user" },
+ { EVENT_FLAG_CONFIG, "config" },
+ { EVENT_FLAG_DTMF, "dtmf" },
+ { EVENT_FLAG_REPORTING, "reporting" },
+ { EVENT_FLAG_CDR, "cdr" },
+ { EVENT_FLAG_DIALPLAN, "dialplan" },
+ { -1, "all" },
+ { 0, "none" },
+};
+
+/*! \brief Convert authority code to a list of options */
+static char *authority_to_str(int authority, struct ast_str **res)
+{
+ int i;
+ char *sep = "";
+
+ (*res)->used = 0;
+ for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
+ if (authority & perms[i].num) {
+ ast_str_append(res, 0, "%s%s", sep, perms[i].label);
+ sep = ",";
+ }
+ }
+
+ if ((*res)->used == 0) /* replace empty string with something sensible */
+ ast_str_append(res, 0, "<none>");
+
+ return (*res)->str;
+}
+
+/*! Tells you if smallstr exists inside bigstr
+ which is delim by delim and uses no buf or stringsep
+ ast_instring("this|that|more","this",'|') == 1;
+
+ feel free to move this to app.c -anthm */
+static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
+{
+ const char *val = bigstr, *next;
+
+ do {
+ if ((next = strchr(val, delim))) {
+ if (!strncmp(val, smallstr, (next - val)))
+ return 1;
+ else
+ continue;
+ } else
+ return !strcmp(smallstr, val);
+ } while (*(val = (next + 1)));
+
+ return 0;
+}
+
+static int get_perm(const char *instr)
+{
+ int x = 0, ret = 0;
+
+ if (!instr)
+ return 0;
+
+ for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
+ if (ast_instring(instr, perms[x].label, ','))
+ ret |= perms[x].num;
+ }
+
+ return ret;
+}
+
+/*!
+ * A number returns itself, false returns 0, true returns all flags,
+ * other strings return the flags that are set.
+ */
+static int strings_to_mask(const char *string)
+{
+ const char *p;
+
+ if (ast_strlen_zero(string))
+ return -1;
+
+ for (p = string; *p; p++)
+ if (*p < '0' || *p > '9')
+ break;
+ if (!p) /* all digits */
+ return atoi(string);
+ if (ast_false(string))
+ return 0;
+ if (ast_true(string)) { /* all permissions */
+ int x, ret = 0;
+ for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
+ ret |= perms[x].num;
+ return ret;
+ }
+ return get_perm(string);
+}
+
+static int check_manager_session_inuse(const char *name)
+{
+ struct mansession *session = NULL;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, session, list) {
+ if (!strcasecmp(session->username, name))
+ break;
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ return session ? 1 : 0;
+}
+
+
+/*!
+ * lookup an entry in the list of registered users.
+ * must be called with the list lock held.
+ */
+static struct ast_manager_user *get_manager_by_name_locked(const char *name)
+{
+ struct ast_manager_user *user = NULL;
+
+ AST_RWLIST_TRAVERSE(&users, user, list)
+ if (!strcasecmp(user->username, name))
+ break;
+ return user;
+}
+
+/*! \brief Get displayconnects config option.
+ * \param s manager session to get parameter from.
+ * \return displayconnects config option value.
+ */
+static int manager_displayconnects (struct mansession *s)
+{
+ struct ast_manager_user *user = NULL;
+ int ret = 0;
+
+ AST_RWLIST_RDLOCK(&users);
+ if ((user = get_manager_by_name_locked (s->username)))
+ ret = user->displayconnects;
+ AST_RWLIST_UNLOCK(&users);
+
+ return ret;
+}
+
+static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct manager_action *cur;
+ struct ast_str *authority;
+ int num, l, which;
+ char *ret = NULL;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show command";
+ e->usage =
+ "Usage: manager show command <actionname>\n"
+ " Shows the detailed description for a specific Asterisk manager interface command.\n";
+ return NULL;
+ case CLI_GENERATE:
+ l = strlen(a->word);
+ which = 0;
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
+ ret = ast_strdup(cur->action);
+ break; /* make sure we exit even if ast_strdup() returns NULL */
+ }
+ }
+ AST_RWLIST_UNLOCK(&actions);
+ return ret;
+ }
+ authority = ast_str_alloca(80);
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ for (num = 3; num < a->argc; num++) {
+ if (!strcasecmp(cur->action, a->argv[num])) {
+ ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
+ cur->action, cur->synopsis,
+ authority_to_str(cur->authority, &authority),
+ S_OR(cur->description, ""));
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&actions);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager debug [on|off]";
+ e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc == 2)
+ ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
+ else if (a->argc == 3) {
+ if (!strcasecmp(a->argv[2], "on"))
+ manager_debug = 1;
+ else if (!strcasecmp(a->argv[2], "off"))
+ manager_debug = 0;
+ else
+ return CLI_SHOWUSAGE;
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_manager_user *user = NULL;
+ int l, which;
+ char *ret = NULL;
+ struct ast_str *rauthority = ast_str_alloca(80);
+ struct ast_str *wauthority = ast_str_alloca(80);
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show user";
+ e->usage =
+ " Usage: manager show user <user>\n"
+ " Display all information related to the manager user specified.\n";
+ return NULL;
+ case CLI_GENERATE:
+ l = strlen(a->word);
+ which = 0;
+ if (a->pos != 3)
+ return NULL;
+ AST_RWLIST_RDLOCK(&users);
+ AST_RWLIST_TRAVERSE(&users, user, list) {
+ if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
+ ret = ast_strdup(user->username);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&users);
+ return ret;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&users);
+
+ if (!(user = get_manager_by_name_locked(a->argv[3]))) {
+ ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
+ AST_RWLIST_UNLOCK(&users);
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd,"\n");
+ ast_cli(a->fd,
+ " username: %s\n"
+ " secret: %s\n"
+ " acl: %s\n"
+ " read perm: %s\n"
+ " write perm: %s\n"
+ "displayconnects: %s\n",
+ (user->username ? user->username : "(N/A)"),
+ (user->secret ? "<Set>" : "(N/A)"),
+ (user->ha ? "yes" : "no"),
+ authority_to_str(user->readperm, &rauthority),
+ authority_to_str(user->writeperm, &wauthority),
+ (user->displayconnects ? "yes" : "no"));
+
+ AST_RWLIST_UNLOCK(&users);
+
+ return CLI_SUCCESS;
+}
+
+
+static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_manager_user *user = NULL;
+ int count_amu = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show users";
+ e->usage =
+ "Usage: manager show users\n"
+ " Prints a listing of all managers that are currently configured on that\n"
+ " system.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&users);
+
+ /* If there are no users, print out something along those lines */
+ if (AST_RWLIST_EMPTY(&users)) {
+ ast_cli(a->fd, "There are no manager users.\n");
+ AST_RWLIST_UNLOCK(&users);
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd, "\nusername\n--------\n");
+
+ AST_RWLIST_TRAVERSE(&users, user, list) {
+ ast_cli(a->fd, "%s\n", user->username);
+ count_amu++;
+ }
+
+ AST_RWLIST_UNLOCK(&users);
+
+ ast_cli(a->fd,"-------------------\n");
+ ast_cli(a->fd,"%d manager users configured.\n", count_amu);
+
+ return CLI_SUCCESS;
+}
+
+
+/*! \brief CLI command manager list commands */
+static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct manager_action *cur;
+ struct ast_str *authority;
+ static const char *format = " %-15.15s %-15.15s %-55.55s\n";
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show commands";
+ e->usage =
+ "Usage: manager show commands\n"
+ " Prints a listing of all the available Asterisk manager interface commands.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ authority = ast_str_alloca(80);
+ ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
+ ast_cli(a->fd, format, "------", "---------", "--------");
+
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list)
+ ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
+ AST_RWLIST_UNLOCK(&actions);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command manager list connected */
+static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct mansession *s;
+ time_t now = time(NULL);
+ static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
+ static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
+ int count = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show connected";
+ e->usage =
+ "Usage: manager show connected\n"
+ " Prints a listing of the users that are currently connected to the\n"
+ "Asterisk manager interface.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_cli(a->fd, format2, s->username, ast_inet_ntoa(s->sin.sin_addr), (int)(s->sessionstart), (int)(now - s->sessionstart), s->fd, s->inuse, s->readperm, s->writeperm);
+ count++;
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ ast_cli(a->fd, "%d users connected.\n", count);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command manager list eventq */
+/* Should change to "manager show connected" */
+static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct eventqent *s;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show eventq";
+ e->usage =
+ "Usage: manager show eventq\n"
+ " Prints a listing of all events pending in the Asterisk manger\n"
+ "event queue.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ AST_LIST_LOCK(&all_events);
+ AST_LIST_TRAVERSE(&all_events, s, eq_next) {
+ ast_cli(a->fd, "Usecount: %d\n",s->usecount);
+ ast_cli(a->fd, "Category: %d\n", s->category);
+ ast_cli(a->fd, "Event:\n%s", s->eventdata);
+ }
+ AST_LIST_UNLOCK(&all_events);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command manager reload */
+static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager reload";
+ e->usage =
+ "Usage: manager reload\n"
+ " Reloads the manager configuration.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+ reload_manager();
+ return CLI_SUCCESS;
+}
+
+
+static struct ast_cli_entry cli_manager[] = {
+ AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
+ AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
+ AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
+ AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
+ AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
+ AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
+ AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
+ AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
+};
+
+/*
+ * Decrement the usecount for the event; if it goes to zero,
+ * (why check for e->next ?) wakeup the
+ * main thread, which is in charge of freeing the record.
+ * Returns the next record.
+ */
+static struct eventqent *unref_event(struct eventqent *e)
+{
+ ast_atomic_fetchadd_int(&e->usecount, -1);
+ return AST_LIST_NEXT(e, eq_next);
+}
+
+static void ref_event(struct eventqent *e)
+{
+ ast_atomic_fetchadd_int(&e->usecount, 1);
+}
+
+/*
+ * destroy a session, leaving the usecount
+ */
+static void free_session(struct mansession *s)
+{
+ struct eventqent *eqe = s->last_ev;
+ if (s->f != NULL)
+ fclose(s->f);
+ ast_mutex_destroy(&s->__lock);
+ ast_free(s);
+ unref_event(eqe);
+}
+
+static void destroy_session(struct mansession *s)
+{
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_REMOVE(&sessions, s, list);
+ ast_atomic_fetchadd_int(&num_sessions, -1);
+ free_session(s);
+ AST_LIST_UNLOCK(&sessions);
+}
+
+const char *astman_get_header(const struct message *m, char *var)
+{
+ int x, l = strlen(var);
+
+ for (x = 0; x < m->hdrcount; x++) {
+ const char *h = m->headers[x];
+ if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
+ return h + l + 2;
+ }
+
+ return "";
+}
+
+struct ast_variable *astman_get_variables(const struct message *m)
+{
+ int varlen, x, y;
+ struct ast_variable *head = NULL, *cur;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(vars)[32];
+ );
+
+ varlen = strlen("Variable: ");
+
+ for (x = 0; x < m->hdrcount; x++) {
+ char *parse, *var, *val;
+
+ if (strncasecmp("Variable: ", m->headers[x], varlen))
+ continue;
+ parse = ast_strdupa(m->headers[x] + varlen);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (!args.argc)
+ continue;
+ for (y = 0; y < args.argc; y++) {
+ if (!args.vars[y])
+ continue;
+ var = val = ast_strdupa(args.vars[y]);
+ strsep(&val, "=");
+ if (!val || ast_strlen_zero(var))
+ continue;
+ cur = ast_variable_new(var, val, "");
+ cur->next = head;
+ head = cur;
+ }
+ }
+
+ return head;
+}
+
+/*!
+ * helper function to send a string to the socket.
+ * Return -1 on error (e.g. buffer full).
+ */
+static int send_string(struct mansession *s, char *string)
+{
+ int len = strlen(string); /* residual length */
+ char *src = string;
+ struct timeval start = ast_tvnow();
+ int n = 0;
+
+ for (;;) {
+ int elapsed;
+ struct pollfd fd;
+ n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
+ if (n == len /* ok */ || n < 0 /* error */)
+ break;
+ len -= n; /* skip already written data */
+ src += n;
+ fd.fd = s->fd;
+ fd.events = POLLOUT;
+ n = -1; /* error marker */
+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
+ if (elapsed > s->writetimeout)
+ break;
+ if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
+ break;
+ }
+ fflush(s->f);
+ return n < 0 ? -1 : 0;
+}
+
+/*!
+ * \brief thread local buffer for astman_append
+ *
+ * \note This can not be defined within the astman_append() function
+ * because it declares a couple of functions that get used to
+ * initialize the thread local storage key.
+ */
+AST_THREADSTORAGE(astman_append_buf);
+/*! \brief initial allocated size for the astman_append_buf */
+#define ASTMAN_APPEND_BUF_INITSIZE 256
+
+/*!
+ * utility functions for creating AMI replies
+ */
+void astman_append(struct mansession *s, const char *fmt, ...)
+{
+ va_list ap;
+ struct ast_str *buf;
+
+ if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
+ return;
+
+ va_start(ap, fmt);
+ ast_str_set_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ if (s->f != NULL)
+ send_string(s, buf->str);
+ else
+ ast_verbose("fd == -1 in astman_append, should not happen\n");
+}
+
+/*! \note NOTE: XXX this comment is unclear and possibly wrong.
+ Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
+ hold the session lock _or_ be running in an action callback (in which case s->busy will
+ be non-zero). In either of these cases, there is no need to lock-protect the session's
+ fd, since no other output will be sent (events will be queued), and no input will
+ be read until either the current action finishes or get_input() obtains the session
+ lock.
+ */
+
+/*! \brief send a response with an optional message,
+ * and terminate it with an empty line.
+ * m is used only to grab the 'ActionID' field.
+ *
+ * Use the explicit constant MSG_MOREDATA to remove the empty line.
+ * XXX MSG_MOREDATA should go to a header file.
+ */
+#define MSG_MOREDATA ((char *)astman_send_response)
+static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
+{
+ const char *id = astman_get_header(m,"ActionID");
+
+ astman_append(s, "Response: %s\r\n", resp);
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n", id);
+ if (listflag)
+ astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
+ if (msg == MSG_MOREDATA)
+ return;
+ else if (msg)
+ astman_append(s, "Message: %s\r\n\r\n", msg);
+ else
+ astman_append(s, "\r\n");
+}
+
+void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
+{
+ astman_send_response_full(s, m, resp, msg, NULL);
+}
+
+void astman_send_error(struct mansession *s, const struct message *m, char *error)
+{
+ astman_send_response_full(s, m, "Error", error, NULL);
+}
+
+void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
+{
+ astman_send_response_full(s, m, "Success", msg, NULL);
+}
+
+static void astman_start_ack(struct mansession *s, const struct message *m)
+{
+ astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
+}
+
+void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
+{
+ astman_send_response_full(s, m, "Success", msg, listflag);
+}
+
+
+/*! \brief
+ Rather than braindead on,off this now can also accept a specific int mask value
+ or a ',' delim list of mask strings (the same as manager.conf) -anthm
+*/
+static int set_eventmask(struct mansession *s, const char *eventmask)
+{
+ int maskint = strings_to_mask(eventmask);
+
+ ast_mutex_lock(&s->__lock);
+ if (maskint >= 0)
+ s->send_events = maskint;
+ ast_mutex_unlock(&s->__lock);
+
+ return maskint;
+}
+
+/*
+ * Here we start with action_ handlers for AMI actions,
+ * and the internal functions used by them.
+ * Generally, the handlers are called action_foo()
+ */
+
+/* helper function for action_login() */
+static int authenticate(struct mansession *s, const struct message *m)
+{
+ const char *username = astman_get_header(m, "Username");
+ const char *password = astman_get_header(m, "Secret");
+ int error = -1;
+ struct ast_manager_user *user = NULL;
+
+ if (ast_strlen_zero(username)) /* missing username */
+ return -1;
+
+ /* locate user in locked state */
+ AST_RWLIST_WRLOCK(&users);
+
+ if (!(user = get_manager_by_name_locked(username))) {
+ ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
+ } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
+ ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
+ } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
+ const char *key = astman_get_header(m, "Key");
+ if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
+ int x;
+ int len = 0;
+ char md5key[256] = "";
+ struct MD5Context md5;
+ unsigned char digest[16];
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
+ MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
+ MD5Final(digest, &md5);
+ for (x=0; x<16; x++)
+ len += sprintf(md5key + len, "%2.2x", digest[x]);
+ if (!strcmp(md5key, key))
+ error = 0;
+ } else {
+ ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
+ S_OR(s->challenge, ""));
+ }
+ } else if (password && user->secret && !strcmp(password, user->secret))
+ error = 0;
+
+ if (error) {
+ ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
+ AST_RWLIST_UNLOCK(&users);
+ return -1;
+ }
+
+ /* auth complete */
+
+ ast_copy_string(s->username, username, sizeof(s->username));
+ s->readperm = user->readperm;
+ s->writeperm = user->writeperm;
+ s->writetimeout = user->writetimeout;
+ s->sessionstart = time(NULL);
+ set_eventmask(s, astman_get_header(m, "Events"));
+
+ AST_RWLIST_UNLOCK(&users);
+ return 0;
+}
+
+/*! \brief Manager PING */
+static char mandescr_ping[] =
+"Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
+" manager connection open.\n"
+"Variables: NONE\n";
+
+static int action_ping(struct mansession *s, const struct message *m)
+{
+ astman_send_response(s, m, "Success", "Ping: Pong\r\n");
+ return 0;
+}
+
+static char mandescr_getconfig[] =
+"Description: A 'GetConfig' action will dump the contents of a configuration\n"
+"file by category and contents.\n"
+"Variables:\n"
+" Filename: Configuration filename (e.g. foo.conf)\n";
+
+static int action_getconfig(struct mansession *s, const struct message *m)
+{
+ struct ast_config *cfg;
+ const char *fn = astman_get_header(m, "Filename");
+ int catcount = 0;
+ int lineno = 0;
+ char *category=NULL;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
+
+ if (ast_strlen_zero(fn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+ if (!(cfg = ast_config_load(fn, config_flags))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+ astman_start_ack(s, m);
+ while ((category = ast_category_browse(cfg, category))) {
+ lineno = 0;
+ astman_append(s, "Category-%06d: %s\r\n", catcount, category);
+ for (v = ast_variable_browse(cfg, category); v; v = v->next)
+ astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
+ catcount++;
+ }
+ ast_config_destroy(cfg);
+ astman_append(s, "\r\n");
+
+ return 0;
+}
+
+/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
+static void json_escape(char *out, const char *in)
+{
+ for (; *in; in++) {
+ if (*in == '\\' || *in == '\"')
+ *out++ = '\\';
+ *out++ = *in;
+ }
+ *out = '\0';
+}
+
+static char mandescr_getconfigjson[] =
+"Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
+"file by category and contents in JSON format. This only makes sense to be used\n"
+"using rawman over the HTTP interface.\n"
+"Variables:\n"
+" Filename: Configuration filename (e.g. foo.conf)\n";
+
+static int action_getconfigjson(struct mansession *s, const struct message *m)
+{
+ struct ast_config *cfg;
+ const char *fn = astman_get_header(m, "Filename");
+ char *category = NULL;
+ struct ast_variable *v;
+ int comma1 = 0;
+ char *buf = NULL;
+ unsigned int buf_len = 0;
+ struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
+
+ if (ast_strlen_zero(fn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+
+ if (!(cfg = ast_config_load(fn, config_flags))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+
+ buf_len = 512;
+ buf = alloca(buf_len);
+
+ astman_start_ack(s, m);
+ astman_append(s, "JSON: {");
+ while ((category = ast_category_browse(cfg, category))) {
+ int comma2 = 0;
+ if (buf_len < 2 * strlen(category) + 1) {
+ buf_len *= 2;
+ buf = alloca(buf_len);
+ }
+ json_escape(buf, category);
+ astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
+ if (!comma1)
+ comma1 = 1;
+ for (v = ast_variable_browse(cfg, category); v; v = v->next) {
+ if (comma2)
+ astman_append(s, ",");
+ if (buf_len < 2 * strlen(v->name) + 1) {
+ buf_len *= 2;
+ buf = alloca(buf_len);
+ }
+ json_escape(buf, v->name);
+ astman_append(s, "\"%s", buf);
+ if (buf_len < 2 * strlen(v->value) + 1) {
+ buf_len *= 2;
+ buf = alloca(buf_len);
+ }
+ json_escape(buf, v->value);
+ astman_append(s, "%s\"", buf);
+ if (!comma2)
+ comma2 = 1;
+ }
+ astman_append(s, "]");
+ }
+ astman_append(s, "}\r\n\r\n");
+
+ ast_config_destroy(cfg);
+
+ return 0;
+}
+
+/* helper function for action_updateconfig */
+static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
+{
+ int x;
+ char hdr[40];
+ const char *action, *cat, *var, *value, *match;
+ struct ast_category *category;
+ struct ast_variable *v;
+
+ for (x = 0; x < 100000; x++) {
+ unsigned int object = 0;
+
+ snprintf(hdr, sizeof(hdr), "Action-%06d", x);
+ action = astman_get_header(m, hdr);
+ if (ast_strlen_zero(action))
+ break;
+ snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
+ cat = astman_get_header(m, hdr);
+ snprintf(hdr, sizeof(hdr), "Var-%06d", x);
+ var = astman_get_header(m, hdr);
+ snprintf(hdr, sizeof(hdr), "Value-%06d", x);
+ value = astman_get_header(m, hdr);
+ if (!ast_strlen_zero(value) && *value == '>') {
+ object = 1;
+ value++;
+ }
+ snprintf(hdr, sizeof(hdr), "Match-%06d", x);
+ match = astman_get_header(m, hdr);
+ if (!strcasecmp(action, "newcat")) {
+ if (!ast_strlen_zero(cat)) {
+ category = ast_category_new(cat, dfn, 99999);
+ if (category) {
+ ast_category_append(cfg, category);
+ }
+ }
+ } else if (!strcasecmp(action, "renamecat")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
+ category = ast_category_get(cfg, cat);
+ if (category)
+ ast_category_rename(category, value);
+ }
+ } else if (!strcasecmp(action, "delcat")) {
+ if (!ast_strlen_zero(cat))
+ ast_category_delete(cfg, cat);
+ } else if (!strcasecmp(action, "update")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
+ ast_variable_update(category, var, value, match, object);
+ } else if (!strcasecmp(action, "delete")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
+ ast_variable_delete(category, var, match);
+ } else if (!strcasecmp(action, "append")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
+ (category = ast_category_get(cfg, cat)) &&
+ (v = ast_variable_new(var, value, dfn))){
+ if (object || (match && !strcasecmp(match, "object")))
+ v->object = 1;
+ ast_variable_append(category, v);
+ }
+ }
+ }
+}
+
+static char mandescr_updateconfig[] =
+"Description: A 'UpdateConfig' action will modify, create, or delete\n"
+"configuration elements in Asterisk configuration files.\n"
+"Variables (X's represent 6 digit number beginning with 000000):\n"
+" SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
+" DstFilename: Configuration filename to write(e.g. foo.conf)\n"
+" Reload: Whether or not a reload should take place (or name of specific module)\n"
+" Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
+" Cat-XXXXXX: Category to operate on\n"
+" Var-XXXXXX: Variable to work on\n"
+" Value-XXXXXX: Value to work on\n"
+" Match-XXXXXX: Extra match required to match line\n";
+
+static int action_updateconfig(struct mansession *s, const struct message *m)
+{
+ struct ast_config *cfg;
+ const char *sfn = astman_get_header(m, "SrcFilename");
+ const char *dfn = astman_get_header(m, "DstFilename");
+ int res;
+ const char *rld = astman_get_header(m, "Reload");
+ struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
+
+ if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+ if (!(cfg = ast_config_load(sfn, config_flags))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+ handle_updates(s, m, cfg, dfn);
+ ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
+ res = config_text_file_save(dfn, cfg, "Manager");
+ ast_config_destroy(cfg);
+ if (res) {
+ astman_send_error(s, m, "Save of config failed");
+ return 0;
+ }
+ astman_send_ack(s, m, NULL);
+ if (!ast_strlen_zero(rld)) {
+ if (ast_true(rld))
+ rld = NULL;
+ ast_module_reload(rld);
+ }
+ return 0;
+}
+
+/*! \brief Manager WAITEVENT */
+static char mandescr_waitevent[] =
+"Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
+"a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
+"session, events will be generated and queued.\n"
+"Variables: \n"
+" Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
+
+static int action_waitevent(struct mansession *s, const struct message *m)
+{
+ const char *timeouts = astman_get_header(m, "Timeout");
+ int timeout = -1;
+ int x;
+ int needexit = 0;
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ if (!ast_strlen_zero(timeouts)) {
+ sscanf(timeouts, "%i", &timeout);
+ if (timeout < -1)
+ timeout = -1;
+ /* XXX maybe put an upper bound, or prevent the use of 0 ? */
+ }
+
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread != AST_PTHREADT_NULL)
+ pthread_kill(s->waiting_thread, SIGURG);
+
+ if (s->managerid) { /* AMI-over-HTTP session */
+ /*
+ * Make sure the timeout is within the expire time of the session,
+ * as the client will likely abort the request if it does not see
+ * data coming after some amount of time.
+ */
+ time_t now = time(NULL);
+ int max = s->sessiontimeout - now - 10;
+
+ if (max < 0) /* We are already late. Strange but possible. */
+ max = 0;
+ if (timeout < 0 || timeout > max)
+ timeout = max;
+ if (!s->send_events) /* make sure we record events */
+ s->send_events = -1;
+ }
+ ast_mutex_unlock(&s->__lock);
+
+ /* XXX should this go inside the lock ? */
+ s->waiting_thread = pthread_self(); /* let new events wake up this thread */
+ ast_debug(1, "Starting waiting for an event!\n");
+
+ for (x=0; x < timeout || timeout < 0; x++) {
+ ast_mutex_lock(&s->__lock);
+ if (NEW_EVENT(s))
+ needexit = 1;
+ /* We can have multiple HTTP session point to the same mansession entry.
+ * The way we deal with it is not very nice: newcomers kick out the previous
+ * HTTP session. XXX this needs to be improved.
+ */
+ if (s->waiting_thread != pthread_self())
+ needexit = 1;
+ if (s->needdestroy)
+ needexit = 1;
+ ast_mutex_unlock(&s->__lock);
+ if (needexit)
+ break;
+ if (s->managerid == 0) { /* AMI session */
+ if (ast_wait_for_input(s->fd, 1000))
+ break;
+ } else { /* HTTP session */
+ sleep(1);
+ }
+ }
+ ast_debug(1, "Finished waiting for an event!\n");
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread == pthread_self()) {
+ struct eventqent *eqe;
+ astman_send_response(s, m, "Success", "Waiting for Event completed.");
+ while ( (eqe = NEW_EVENT(s)) ) {
+ ref_event(eqe);
+ if (((s->readperm & eqe->category) == eqe->category) &&
+ ((s->send_events & eqe->category) == eqe->category)) {
+ astman_append(s, "%s", eqe->eventdata);
+ }
+ s->last_ev = unref_event(s->last_ev);
+ }
+ astman_append(s,
+ "Event: WaitEventComplete\r\n"
+ "%s"
+ "\r\n", idText);
+ s->waiting_thread = AST_PTHREADT_NULL;
+ } else {
+ ast_debug(1, "Abandoning event request!\n");
+ }
+ ast_mutex_unlock(&s->__lock);
+ return 0;
+}
+
+static char mandescr_listcommands[] =
+"Description: Returns the action name and synopsis for every\n"
+" action that is available to the user\n"
+"Variables: NONE\n";
+
+/*! \note The actionlock is read-locked by the caller of this function */
+static int action_listcommands(struct mansession *s, const struct message *m)
+{
+ struct manager_action *cur;
+ struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
+
+ astman_start_ack(s, m);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ if (s->writeperm & cur->authority || cur->authority == 0)
+ astman_append(s, "%s: %s (Priv: %s)\r\n",
+ cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
+ }
+ astman_append(s, "\r\n");
+
+ return 0;
+}
+
+static char mandescr_events[] =
+"Description: Enable/Disable sending of events to this manager\n"
+" client.\n"
+"Variables:\n"
+" EventMask: 'on' if all events should be sent,\n"
+" 'off' if no events should be sent,\n"
+" 'system,call,log' to select which flags events should have to be sent.\n";
+
+static int action_events(struct mansession *s, const struct message *m)
+{
+ const char *mask = astman_get_header(m, "EventMask");
+ int res;
+
+ res = set_eventmask(s, mask);
+ if (res > 0)
+ astman_send_response(s, m, "Success", "Events: On\r\n");
+ else if (res == 0)
+ astman_send_response(s, m, "Success", "Events: Off\r\n");
+
+ return 0;
+}
+
+static char mandescr_logoff[] =
+"Description: Logoff this manager session\n"
+"Variables: NONE\n";
+
+static int action_logoff(struct mansession *s, const struct message *m)
+{
+ astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
+ return -1;
+}
+
+static int action_login(struct mansession *s, const struct message *m)
+{
+ if (authenticate(s, m)) {
+ sleep(1);
+ astman_send_error(s, m, "Authentication failed");
+ return -1;
+ }
+ s->authenticated = 1;
+ if (manager_displayconnects(s))
+ ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
+ astman_send_ack(s, m, "Authentication accepted");
+ return 0;
+}
+
+static int action_challenge(struct mansession *s, const struct message *m)
+{
+ const char *authtype = astman_get_header(m, "AuthType");
+
+ if (!strcasecmp(authtype, "MD5")) {
+ if (ast_strlen_zero(s->challenge))
+ snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
+ ast_mutex_lock(&s->__lock);
+ astman_start_ack(s, m);
+ astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
+ ast_mutex_unlock(&s->__lock);
+ } else {
+ astman_send_error(s, m, "Must specify AuthType");
+ }
+ return 0;
+}
+
+static char mandescr_hangup[] =
+"Description: Hangup a channel\n"
+"Variables: \n"
+" Channel: The channel name to be hungup\n";
+
+static int action_hangup(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(c);
+ astman_send_ack(s, m, "Channel Hungup");
+ return 0;
+}
+
+static char mandescr_setvar[] =
+"Description: Set a global or local channel variable.\n"
+"Variables: (Names marked with * are required)\n"
+" Channel: Channel to set variable for\n"
+" *Variable: Variable name\n"
+" *Value: Value\n";
+
+static int action_setvar(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *varname = astman_get_header(m, "Variable");
+ const char *varval = astman_get_header(m, "Value");
+
+ if (ast_strlen_zero(varname)) {
+ astman_send_error(s, m, "No variable specified");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(name)) {
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ }
+
+ pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
+
+ if (c)
+ ast_channel_unlock(c);
+
+ astman_send_ack(s, m, "Variable Set");
+
+ return 0;
+}
+
+static char mandescr_getvar[] =
+"Description: Get the value of a global or local channel variable.\n"
+"Variables: (Names marked with * are required)\n"
+" Channel: Channel to read variable from\n"
+" *Variable: Variable name\n"
+" ActionID: Optional Action id for message matching.\n";
+
+static int action_getvar(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *varname = astman_get_header(m, "Variable");
+ char *varval;
+ char workspace[1024] = "";
+
+ if (ast_strlen_zero(varname)) {
+ astman_send_error(s, m, "No variable specified");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(name)) {
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ }
+
+ if (varname[strlen(varname) - 1] == ')') {
+ ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
+ varval = workspace;
+ } else {
+ pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
+ }
+
+ if (c)
+ ast_channel_unlock(c);
+ astman_start_ack(s, m);
+ astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
+
+ return 0;
+}
+
+
+/*! \brief Manager "status" command to show channels */
+/* Needs documentation... */
+static int action_status(struct mansession *s, const struct message *m)
+{
+ const char *name = astman_get_header(m,"Channel");
+ struct ast_channel *c;
+ char bridge[256];
+ struct timeval now = ast_tvnow();
+ long elapsed_seconds = 0;
+ int channels = 0;
+ int all = ast_strlen_zero(name); /* set if we want all channels */
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ if (all)
+ c = ast_channel_walk_locked(NULL);
+ else {
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ }
+ astman_send_ack(s, m, "Channel status will follow");
+
+ /* if we look by name, we break after the first iteration */
+ while (c) {
+ channels++;
+ if (c->_bridge)
+ snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
+ else
+ bridge[0] = '\0';
+ if (c->pbx) {
+ if (c->cdr) {
+ elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
+ }
+ astman_append(s,
+ "Event: Status\r\n"
+ "Privilege: Call\r\n"
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Accountcode: %s\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "Seconds: %ld\r\n"
+ "%s"
+ "Uniqueid: %s\r\n"
+ "%s"
+ "\r\n",
+ c->name,
+ S_OR(c->cid.cid_num, ""),
+ S_OR(c->cid.cid_name, ""),
+ c->accountcode,
+ c->_state,
+ ast_state2str(c->_state), c->context,
+ c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
+ } else {
+ astman_append(s,
+ "Event: Status\r\n"
+ "Privilege: Call\r\n"
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Account: %s\r\n"
+ "State: %s\r\n"
+ "%s"
+ "Uniqueid: %s\r\n"
+ "%s"
+ "\r\n",
+ c->name,
+ S_OR(c->cid.cid_num, "<unknown>"),
+ S_OR(c->cid.cid_name, "<unknown>"),
+ c->accountcode,
+ ast_state2str(c->_state), bridge, c->uniqueid, idText);
+ }
+ ast_channel_unlock(c);
+ if (!all)
+ break;
+ c = ast_channel_walk_locked(c);
+ }
+ astman_append(s,
+ "Event: StatusComplete\r\n"
+ "%s"
+ "Items: %d\r\n"
+ "\r\n",idText, channels);
+ return 0;
+}
+
+static char mandescr_sendtext[] =
+"Description: Sends A Text Message while in a call.\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel to send message to\n"
+" *Message: Message to send\n"
+" ActionID: Optional Action id for message matching.\n";
+
+static int action_sendtext(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *textmsg = astman_get_header(m, "Message");
+ int res = 0;
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+
+ if (ast_strlen_zero(textmsg)) {
+ astman_send_error(s, m, "No Message specified");
+ return 0;
+ }
+
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+
+ res = ast_sendtext(c, textmsg);
+ ast_channel_unlock(c);
+
+ if (res > 0)
+ astman_send_ack(s, m, "Success");
+ else
+ astman_send_error(s, m, "Failure");
+
+ return res;
+}
+
+static char mandescr_redirect[] =
+"Description: Redirect (transfer) a call.\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel to redirect\n"
+" ExtraChannel: Second call leg to transfer (optional)\n"
+" *Exten: Extension to transfer to\n"
+" *Context: Context to transfer to\n"
+" *Priority: Priority to transfer to\n"
+" ActionID: Optional Action id for message matching.\n";
+
+/*! \brief action_redirect: The redirect manager command */
+static int action_redirect(struct mansession *s, const struct message *m)
+{
+ const char *name = astman_get_header(m, "Channel");
+ const char *name2 = astman_get_header(m, "ExtraChannel");
+ const char *exten = astman_get_header(m, "Exten");
+ const char *context = astman_get_header(m, "Context");
+ const char *priority = astman_get_header(m, "Priority");
+ struct ast_channel *chan, *chan2 = NULL;
+ int pi = 0;
+ int res;
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "Channel not specified");
+ return 0;
+ }
+ if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
+ if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
+ astman_send_error(s, m, "Invalid priority\n");
+ return 0;
+ }
+ }
+ /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
+ chan = ast_get_channel_by_name_locked(name);
+ if (!chan) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
+ astman_send_error(s, m, buf);
+ return 0;
+ }
+ if (ast_check_hangup(chan)) {
+ astman_send_error(s, m, "Redirect failed, channel not up.\n");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ if (!ast_strlen_zero(name2))
+ chan2 = ast_get_channel_by_name_locked(name2);
+ if (chan2 && ast_check_hangup(chan2)) {
+ astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
+ ast_channel_unlock(chan);
+ ast_channel_unlock(chan2);
+ return 0;
+ }
+ res = ast_async_goto(chan, context, exten, pi);
+ if (!res) {
+ if (!ast_strlen_zero(name2)) {
+ if (chan2)
+ res = ast_async_goto(chan2, context, exten, pi);
+ else
+ res = -1;
+ if (!res)
+ astman_send_ack(s, m, "Dual Redirect successful");
+ else
+ astman_send_error(s, m, "Secondary redirect failed");
+ } else
+ astman_send_ack(s, m, "Redirect successful");
+ } else
+ astman_send_error(s, m, "Redirect failed");
+ if (chan)
+ ast_channel_unlock(chan);
+ if (chan2)
+ ast_channel_unlock(chan2);
+ return 0;
+}
+
+static char mandescr_command[] =
+"Description: Run a CLI command.\n"
+"Variables: (Names marked with * are required)\n"
+" *Command: Asterisk CLI command to run\n"
+" ActionID: Optional Action id for message matching.\n";
+
+/*! \brief Manager command "command" - execute CLI command */
+static int action_command(struct mansession *s, const struct message *m)
+{
+ const char *cmd = astman_get_header(m, "Command");
+ const char *id = astman_get_header(m, "ActionID");
+ char *buf, *final_buf;
+ char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
+ int fd = mkstemp(template), i = 0;
+ off_t l;
+
+ for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
+ if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
+ astman_send_error(s, m, "Command blacklisted");
+ return 0;
+ }
+ }
+
+ astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n", id);
+ /* FIXME: Wedge a ActionID response in here, waiting for later changes */
+ ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
+ l = lseek(fd, 0, SEEK_END); /* how many chars available */
+
+ /* This has a potential to overflow the stack. Hence, use the heap. */
+ buf = ast_calloc(1, l + 1);
+ final_buf = ast_calloc(1, l + 1);
+ if (buf) {
+ lseek(fd, 0, SEEK_SET);
+ read(fd, buf, l);
+ buf[l] = '\0';
+ if (final_buf) {
+ term_strip(final_buf, buf, l);
+ final_buf[l] = '\0';
+ }
+ astman_append(s, S_OR(final_buf, buf));
+ ast_free(buf);
+ }
+ close(fd);
+ unlink(template);
+ astman_append(s, "--END COMMAND--\r\n\r\n");
+ if (final_buf)
+ ast_free(final_buf);
+ return 0;
+}
+
+/* helper function for originate */
+struct fast_originate_helper {
+ char tech[AST_MAX_EXTENSION];
+ char data[AST_MAX_EXTENSION];
+ int timeout;
+ char app[AST_MAX_APP];
+ char appdata[AST_MAX_EXTENSION];
+ char cid_name[AST_MAX_EXTENSION];
+ char cid_num[AST_MAX_EXTENSION];
+ char context[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ char idtext[AST_MAX_EXTENSION];
+ char account[AST_MAX_ACCOUNT_CODE];
+ int priority;
+ struct ast_variable *vars;
+};
+
+static void *fast_originate(void *data)
+{
+ struct fast_originate_helper *in = data;
+ int res;
+ int reason = 0;
+ struct ast_channel *chan = NULL;
+ char requested_channel[AST_CHANNEL_NAME];
+
+ if (!ast_strlen_zero(in->app)) {
+ res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
+ S_OR(in->cid_num, NULL),
+ S_OR(in->cid_name, NULL),
+ in->vars, in->account, &chan);
+ } else {
+ res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
+ S_OR(in->cid_num, NULL),
+ S_OR(in->cid_name, NULL),
+ in->vars, in->account, &chan);
+ }
+
+ if (!chan)
+ snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
+ /* Tell the manager what happened with the channel */
+ manager_event(EVENT_FLAG_CALL, "OriginateResponse",
+ "%s"
+ "Response: %s\r\n"
+ "Channel: %s\r\n"
+ "Context: %s\r\n"
+ "Exten: %s\r\n"
+ "Reason: %d\r\n"
+ "Uniqueid: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n",
+ in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
+ chan ? chan->uniqueid : "<null>",
+ S_OR(in->cid_num, "<unknown>"),
+ S_OR(in->cid_name, "<unknown>")
+ );
+
+ /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
+ if (chan)
+ ast_channel_unlock(chan);
+ ast_free(in);
+ return NULL;
+}
+
+static char mandescr_originate[] =
+"Description: Generates an outgoing call to a Extension/Context/Priority or\n"
+" Application/Data\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel name to call\n"
+" Exten: Extension to use (requires 'Context' and 'Priority')\n"
+" Context: Context to use (requires 'Exten' and 'Priority')\n"
+" Priority: Priority to use (requires 'Exten' and 'Context')\n"
+" Application: Application to use\n"
+" Data: Data to use (requires 'Application')\n"
+" Timeout: How long to wait for call to be answered (in ms)\n"
+" CallerID: Caller ID to be set on the outgoing channel\n"
+" Variable: Channel variable to set, multiple Variable: headers are allowed\n"
+" Account: Account code\n"
+" Async: Set to 'true' for fast origination\n";
+
+static int action_originate(struct mansession *s, const struct message *m)
+{
+ const char *name = astman_get_header(m, "Channel");
+ const char *exten = astman_get_header(m, "Exten");
+ const char *context = astman_get_header(m, "Context");
+ const char *priority = astman_get_header(m, "Priority");
+ const char *timeout = astman_get_header(m, "Timeout");
+ const char *callerid = astman_get_header(m, "CallerID");
+ const char *account = astman_get_header(m, "Account");
+ const char *app = astman_get_header(m, "Application");
+ const char *appdata = astman_get_header(m, "Data");
+ const char *async = astman_get_header(m, "Async");
+ const char *id = astman_get_header(m, "ActionID");
+ struct ast_variable *vars = astman_get_variables(m);
+ char *tech, *data;
+ char *l = NULL, *n = NULL;
+ int pi = 0;
+ int res;
+ int to = 30000;
+ int reason = 0;
+ char tmp[256];
+ char tmp2[256];
+
+ pthread_t th;
+ if (!name) {
+ astman_send_error(s, m, "Channel not specified");
+ return 0;
+ }
+ if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
+ if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
+ astman_send_error(s, m, "Invalid priority\n");
+ return 0;
+ }
+ }
+ if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
+ astman_send_error(s, m, "Invalid timeout\n");
+ return 0;
+ }
+ ast_copy_string(tmp, name, sizeof(tmp));
+ tech = tmp;
+ data = strchr(tmp, '/');
+ if (!data) {
+ astman_send_error(s, m, "Invalid channel\n");
+ return 0;
+ }
+ *data++ = '\0';
+ ast_copy_string(tmp2, callerid, sizeof(tmp2));
+ ast_callerid_parse(tmp2, &n, &l);
+ if (n) {
+ if (ast_strlen_zero(n))
+ n = NULL;
+ }
+ if (l) {
+ ast_shrink_phone_number(l);
+ if (ast_strlen_zero(l))
+ l = NULL;
+ }
+ if (ast_true(async)) {
+ struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
+ if (!fast) {
+ res = -1;
+ } else {
+ if (!ast_strlen_zero(id))
+ snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
+ ast_copy_string(fast->tech, tech, sizeof(fast->tech));
+ ast_copy_string(fast->data, data, sizeof(fast->data));
+ ast_copy_string(fast->app, app, sizeof(fast->app));
+ ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
+ if (l)
+ ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
+ if (n)
+ ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
+ fast->vars = vars;
+ ast_copy_string(fast->context, context, sizeof(fast->context));
+ ast_copy_string(fast->exten, exten, sizeof(fast->exten));
+ ast_copy_string(fast->account, account, sizeof(fast->account));
+ fast->timeout = to;
+ fast->priority = pi;
+ if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
+ res = -1;
+ } else {
+ res = 0;
+ }
+ }
+ } else if (!ast_strlen_zero(app)) {
+ res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
+ } else {
+ if (exten && context && pi)
+ res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
+ else {
+ astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
+ return 0;
+ }
+ }
+ if (!res)
+ astman_send_ack(s, m, "Originate successfully queued");
+ else
+ astman_send_error(s, m, "Originate failed");
+ return 0;
+}
+
+/*! \brief Help text for manager command mailboxstatus
+ */
+static char mandescr_mailboxstatus[] =
+"Description: Checks a voicemail account for status.\n"
+"Variables: (Names marked with * are required)\n"
+" *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
+" ActionID: Optional ActionID for message matching.\n"
+"Returns number of messages.\n"
+" Message: Mailbox Status\n"
+" Mailbox: <mailboxid>\n"
+" Waiting: <count>\n"
+"\n";
+
+static int action_mailboxstatus(struct mansession *s, const struct message *m)
+{
+ const char *mailbox = astman_get_header(m, "Mailbox");
+ int ret;
+
+ if (ast_strlen_zero(mailbox)) {
+ astman_send_error(s, m, "Mailbox not specified");
+ return 0;
+ }
+ ret = ast_app_has_voicemail(mailbox, NULL);
+ astman_start_ack(s, m);
+ astman_append(s, "Message: Mailbox Status\r\n"
+ "Mailbox: %s\r\n"
+ "Waiting: %d\r\n\r\n", mailbox, ret);
+ return 0;
+}
+
+static char mandescr_mailboxcount[] =
+"Description: Checks a voicemail account for new messages.\n"
+"Variables: (Names marked with * are required)\n"
+" *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
+" ActionID: Optional ActionID for message matching.\n"
+"Returns number of new and old messages.\n"
+" Message: Mailbox Message Count\n"
+" Mailbox: <mailboxid>\n"
+" NewMessages: <count>\n"
+" OldMessages: <count>\n"
+"\n";
+static int action_mailboxcount(struct mansession *s, const struct message *m)
+{
+ const char *mailbox = astman_get_header(m, "Mailbox");
+ int newmsgs = 0, oldmsgs = 0;
+
+ if (ast_strlen_zero(mailbox)) {
+ astman_send_error(s, m, "Mailbox not specified");
+ return 0;
+ }
+ ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
+ astman_start_ack(s, m);
+ astman_append(s, "Message: Mailbox Message Count\r\n"
+ "Mailbox: %s\r\n"
+ "NewMessages: %d\r\n"
+ "OldMessages: %d\r\n"
+ "\r\n",
+ mailbox, newmsgs, oldmsgs);
+ return 0;
+}
+
+static char mandescr_extensionstate[] =
+"Description: Report the extension state for given extension.\n"
+" If the extension has a hint, will use devicestate to check\n"
+" the status of the device connected to the extension.\n"
+"Variables: (Names marked with * are required)\n"
+" *Exten: Extension to check state on\n"
+" *Context: Context for extension\n"
+" ActionId: Optional ID for this transaction\n"
+"Will return an \"Extension Status\" message.\n"
+"The response will include the hint for the extension and the status.\n";
+
+static int action_extensionstate(struct mansession *s, const struct message *m)
+{
+ const char *exten = astman_get_header(m, "Exten");
+ const char *context = astman_get_header(m, "Context");
+ char hint[256] = "";
+ int status;
+ if (ast_strlen_zero(exten)) {
+ astman_send_error(s, m, "Extension not specified");
+ return 0;
+ }
+ if (ast_strlen_zero(context))
+ context = "default";
+ status = ast_extension_state(NULL, context, exten);
+ ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
+ astman_start_ack(s, m);
+ astman_append(s, "Message: Extension Status\r\n"
+ "Exten: %s\r\n"
+ "Context: %s\r\n"
+ "Hint: %s\r\n"
+ "Status: %d\r\n\r\n",
+ exten, context, hint, status);
+ return 0;
+}
+
+static char mandescr_timeout[] =
+"Description: Hangup a channel after a certain time.\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel name to hangup\n"
+" *Timeout: Maximum duration of the call (sec)\n"
+"Acknowledges set time with 'Timeout Set' message\n";
+
+static int action_timeout(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c;
+ const char *name = astman_get_header(m, "Channel");
+ int timeout = atoi(astman_get_header(m, "Timeout"));
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ if (!timeout) {
+ astman_send_error(s, m, "No timeout specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ ast_channel_setwhentohangup(c, timeout);
+ ast_channel_unlock(c);
+ astman_send_ack(s, m, "Timeout Set");
+ return 0;
+}
+
+/*!
+ * Send any applicable events to the client listening on this socket.
+ * Wait only for a finite time on each event, and drop all events whether
+ * they are successfully sent or not.
+ */
+static int process_events(struct mansession *s)
+{
+ int ret = 0;
+
+ ast_mutex_lock(&s->__lock);
+ if (s->f != NULL) {
+ struct eventqent *eqe;
+
+ while ( (eqe = NEW_EVENT(s)) ) {
+ ref_event(eqe);
+ if (!ret && s->authenticated &&
+ (s->readperm & eqe->category) == eqe->category &&
+ (s->send_events & eqe->category) == eqe->category) {
+ if (send_string(s, eqe->eventdata) < 0)
+ ret = -1; /* don't send more */
+ }
+ s->last_ev = unref_event(s->last_ev);
+ }
+ }
+ ast_mutex_unlock(&s->__lock);
+ return ret;
+}
+
+static char mandescr_userevent[] =
+"Description: Send an event to manager sessions.\n"
+"Variables: (Names marked with * are required)\n"
+" *UserEvent: EventStringToSend\n"
+" Header1: Content1\n"
+" HeaderN: ContentN\n";
+
+static int action_userevent(struct mansession *s, const struct message *m)
+{
+ const char *event = astman_get_header(m, "UserEvent");
+ char body[2048] = "";
+ int x, bodylen = 0;
+ for (x = 0; x < m->hdrcount; x++) {
+ if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
+ ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
+ bodylen += strlen(m->headers[x]);
+ ast_copy_string(body + bodylen, "\r\n", 3);
+ bodylen += 2;
+ }
+ }
+
+ manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
+ return 0;
+}
+
+static char mandescr_coresettings[] =
+"Description: Query for Core PBX settings.\n"
+"Variables: (Names marked with * are optional)\n"
+" *ActionID: ActionID of this transaction\n";
+
+/*! \brief Show PBX core settings information */
+static int action_coresettings(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ char idText[150] = "";
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
+
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "AMIversion: %s\r\n"
+ "AsteriskVersion: %s\r\n"
+ "SystemName: %s\r\n"
+ "CoreMaxCalls: %d\r\n"
+ "CoreMaxLoadAvg: %f\r\n"
+ "CoreRunUser: %s\r\n"
+ "CoreRunGroup: %s\r\n"
+ "CoreMaxFilehandles: %d\r\n"
+ "CoreRealTimeEnabled: %s\r\n"
+ "CoreCDRenabled: %s\r\n"
+ "CoreHTTPenabled: %s\r\n"
+ ,
+ idText,
+ AMI_VERSION,
+ ast_get_version(),
+ ast_config_AST_SYSTEM_NAME,
+ option_maxcalls,
+ option_maxload,
+ ast_config_AST_RUN_USER,
+ ast_config_AST_RUN_GROUP,
+ option_maxfiles,
+ ast_realtime_enabled() ? "Yes" : "No",
+ check_cdr_enabled() ? "Yes" : "No",
+ check_webmanager_enabled() ? "Yes" : "No"
+ );
+ return 0;
+}
+
+static char mandescr_corestatus[] =
+"Description: Query for Core PBX status.\n"
+"Variables: (Names marked with * are optional)\n"
+" *ActionID: ActionID of this transaction\n";
+
+/*! \brief Show PBX core status information */
+static int action_corestatus(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ char idText[150];
+ char startuptime[150];
+ char reloadtime[150];
+ struct ast_tm tm;
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
+
+ ast_localtime(&ast_startuptime, &tm, NULL);
+ ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
+ ast_localtime(&ast_lastreloadtime, &tm, NULL);
+ ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
+
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "CoreStartupTime: %s\r\n"
+ "CoreReloadTime: %s\r\n"
+ "CoreCurrentCalls: %d\r\n"
+ "",
+ idText,
+ startuptime,
+ reloadtime,
+ ast_active_channels()
+ );
+ return 0;
+}
+
+static char mandescr_reload[] =
+"Description: Send a reload event.\n"
+"Variables: (Names marked with * are optional)\n"
+" *ActionID: ActionID of this transaction\n"
+" *Module: Name of the module to reload\n";
+
+/*! \brief Send a reload event */
+static int action_reload(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ const char *module = astman_get_header(m, "Module");
+ int res = ast_module_reload(S_OR(module, NULL));
+ char idText[80] = "";
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
+ if (res == 2)
+ astman_append(s, "Response: Success\r\n%s", idText);
+ else
+ astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
+ return 0;
+}
+
+static char mandescr_coreshowchannels[] =
+"Description: List currently defined channels and some information\n"
+" about them.\n"
+"Variables:\n"
+" ActionID: Optional Action id for message matching.\n";
+
+/*! \brief Manager command "CoreShowChannels" - List currently defined channels
+ * and some information about them. */
+static int action_coreshowchannels(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ char actionidtext[256] = "";
+ struct ast_channel *c = NULL;
+ int numchans = 0;
+ int duration, durh, durm, durs;
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
+
+ astman_send_listack(s, m, "Channels will follow", "start");
+
+ while ((c = ast_channel_walk_locked(c)) != NULL) {
+ struct ast_channel *bc = ast_bridged_channel(c);
+ char durbuf[10] = "";
+
+ if (c->cdr && !ast_tvzero(c->cdr->start)) {
+ duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
+ durh = duration / 3600;
+ durm = (duration % 3600) / 60;
+ durs = duration % 60;
+ snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
+ }
+
+ astman_append(s,
+ "Channel: %s\r\n"
+ "UniqueID: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "Application: %s\r\n"
+ "ApplicationData: %s\r\n"
+ "CallerIDnum: %s\r\n"
+ "Duration: %s\r\n"
+ "AccountCode: %s\r\n"
+ "BridgedChannel: %s\r\n"
+ "BridgedUniqueID: %s\r\n"
+ "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
+ c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
+ S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
+ ast_channel_unlock(c);
+ numchans++;
+ }
+
+ astman_append(s,
+ "Event: CoreShowChannelsComplete\r\n"
+ "EventList: Complete\r\n"
+ "ListItems: %d\r\n"
+ "%s"
+ "\r\n", numchans, actionidtext);
+
+ return 0;
+}
+
+static char mandescr_modulecheck[] =
+"Description: Checks if Asterisk module is loaded\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this transaction. Will be returned.\n"
+" Module: <name> Asterisk module name (not including extension)\n"
+"\n"
+"Will return Success/Failure\n"
+"For success returns, the module revision number is included.\n";
+
+/* Manager function to check if module is loaded */
+static int manager_modulecheck(struct mansession *s, const struct message *m)
+{
+ int res;
+ const char *module = astman_get_header(m, "Module");
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[BUFSIZ];
+ const char *version;
+ char filename[BUFSIZ/2];
+ char *cut;
+
+ snprintf(filename, sizeof(filename), module);
+ if ((cut = strchr(filename, '.'))) {
+ *cut = '\0';
+ } else {
+ cut = filename + strlen(filename);
+ }
+ sprintf(cut, ".so");
+ ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
+ res = ast_module_check(filename);
+ if (!res) {
+ astman_send_error(s, m, "Module not loaded");
+ return 0;
+ }
+ sprintf(cut, ".c");
+ ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
+ version = ast_file_version_find(filename);
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+ astman_append(s, "Response: Success\r\n%s", idText);
+ astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
+ return 0;
+}
+
+static char mandescr_moduleload[] =
+"Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this transaction. Will be returned.\n"
+" Module: <name> Asterisk module name (including .so extension)\n"
+" or subsystem identifier:\n"
+" cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
+" LoadType: load | unload | reload\n"
+" The operation to be done on module\n"
+" If no module is specified for a reload loadtype, all modules are reloaded";
+
+static int manager_moduleload(struct mansession *s, const struct message *m)
+{
+ int res;
+ const char *module = astman_get_header(m, "Module");
+ const char *loadtype = astman_get_header(m, "LoadType");
+
+ if (!loadtype || strlen(loadtype) == 0)
+ astman_send_error(s, m, "Incomplete ModuleLoad action.");
+ if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
+ astman_send_error(s, m, "Need module name");
+
+ if (!strcasecmp(loadtype, "load")) {
+ res = ast_load_resource(module);
+ if (res)
+ astman_send_error(s, m, "Could not load module.");
+ else
+ astman_send_ack(s, m, "Module loaded.");
+ } else if (!strcasecmp(loadtype, "unload")) {
+ res = ast_unload_resource(module, AST_FORCE_SOFT);
+ if (res)
+ astman_send_error(s, m, "Could not unload module.");
+ else
+ astman_send_ack(s, m, "Module unloaded.");
+ } else if (!strcasecmp(loadtype, "reload")) {
+ if (module != NULL) {
+ res = ast_module_reload(module);
+ if (res == 0)
+ astman_send_error(s, m, "No such module.");
+ else if (res == 1)
+ astman_send_error(s, m, "Module does not support reload action.");
+ else
+ astman_send_ack(s, m, "Module reloaded.");
+ } else {
+ ast_module_reload(NULL); /* Reload all modules */
+ astman_send_ack(s, m, "All modules reloaded");
+ }
+ } else
+ astman_send_error(s, m, "Incomplete ModuleLoad action.");
+ return 0;
+}
+
+/*
+ * Done with the action handlers here, we start with the code in charge
+ * of accepting connections and serving them.
+ * accept_thread() forks a new thread for each connection, session_do(),
+ * which in turn calls get_input() repeatedly until a full message has
+ * been accumulated, and then invokes process_message() to pass it to
+ * the appropriate handler.
+ */
+
+/*
+ * Process an AMI message, performing desired action.
+ * Return 0 on success, -1 on error that require the session to be destroyed.
+ */
+static int process_message(struct mansession *s, const struct message *m)
+{
+ char action[80] = "";
+ int ret = 0;
+ struct manager_action *tmp;
+ const char *user = astman_get_header(m, "Username");
+
+ ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
+ ast_debug(1, "Manager received command '%s'\n", action);
+
+ if (ast_strlen_zero(action)) {
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, "Missing action in request");
+ ast_mutex_unlock(&s->__lock);
+ return 0;
+ }
+
+ if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, "Permission denied");
+ ast_mutex_unlock(&s->__lock);
+ return 0;
+ }
+
+ if (!allowmultiplelogin && !s->authenticated && user &&
+ (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
+ if (check_manager_session_inuse(user)) {
+ sleep(1);
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, "Login Already In Use");
+ ast_mutex_unlock(&s->__lock);
+ return -1;
+ }
+ }
+
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, tmp, list) {
+ if (strcasecmp(action, tmp->action))
+ continue;
+ if (s->writeperm & tmp->authority || tmp->authority == 0)
+ ret = tmp->func(s, m);
+ else
+ astman_send_error(s, m, "Permission denied");
+ break;
+ }
+ AST_RWLIST_UNLOCK(&actions);
+
+ if (!tmp) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, buf);
+ ast_mutex_unlock(&s->__lock);
+ }
+ if (ret)
+ return ret;
+ /* Once done with our message, deliver any pending events */
+ return process_events(s);
+}
+
+/*!
+ * Read one full line (including crlf) from the manager socket.
+ * \note \verbatim
+ * \r\n is the only valid terminator for the line.
+ * (Note that, later, '\0' will be considered as the end-of-line marker,
+ * so everything between the '\0' and the '\r\n' will not be used).
+ * Also note that we assume output to have at least "maxlen" space.
+ * \endverbatim
+ */
+static int get_input(struct mansession *s, char *output)
+{
+ int res, x;
+ int maxlen = sizeof(s->inbuf) - 1;
+ char *src = s->inbuf;
+
+ /*
+ * Look for \r\n within the buffer. If found, copy to the output
+ * buffer and return, trimming the \r\n (not used afterwards).
+ */
+ for (x = 0; x < s->inlen; x++) {
+ int cr; /* set if we have \r */
+ if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
+ cr = 2; /* Found. Update length to include \r\n */
+ else if (src[x] == '\n')
+ cr = 1; /* also accept \n only */
+ else
+ continue;
+ memmove(output, src, x); /*... but trim \r\n */
+ output[x] = '\0'; /* terminate the string */
+ x += cr; /* number of bytes used */
+ s->inlen -= x; /* remaining size */
+ memmove(src, src + x, s->inlen); /* remove used bytes */
+ return 1;
+ }
+ if (s->inlen >= maxlen) {
+ /* no crlf found, and buffer full - sorry, too long for us */
+ ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
+ s->inlen = 0;
+ }
+ res = 0;
+ while (res == 0) {
+ /* XXX do we really need this locking ? */
+ ast_mutex_lock(&s->__lock);
+ s->waiting_thread = pthread_self();
+ ast_mutex_unlock(&s->__lock);
+
+ res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
+
+ ast_mutex_lock(&s->__lock);
+ s->waiting_thread = AST_PTHREADT_NULL;
+ ast_mutex_unlock(&s->__lock);
+ }
+ if (res < 0) {
+ /* If we get a signal from some other thread (typically because
+ * there are new events queued), return 0 to notify the caller.
+ */
+ if (errno == EINTR)
+ return 0;
+ ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
+ return -1;
+ }
+ ast_mutex_lock(&s->__lock);
+ res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
+ if (res < 1)
+ res = -1; /* error return */
+ else {
+ s->inlen += res;
+ src[s->inlen] = '\0';
+ res = 0;
+ }
+ ast_mutex_unlock(&s->__lock);
+ return res;
+}
+
+static int do_message(struct mansession *s)
+{
+ struct message m = { 0 };
+ char header_buf[sizeof(s->inbuf)] = { '\0' };
+ int res;
+
+ for (;;) {
+ /* Check if any events are pending and do them if needed */
+ if (process_events(s))
+ return -1;
+ res = get_input(s, header_buf);
+ if (res == 0) {
+ continue;
+ } else if (res > 0) {
+ if (ast_strlen_zero(header_buf))
+ return process_message(s, &m) ? -1 : 0;
+ else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
+ m.headers[m.hdrcount++] = ast_strdupa(header_buf);
+ } else {
+ return res;
+ }
+ }
+}
+
+/*! \brief The body of the individual manager session.
+ * Call get_input() to read one line at a time
+ * (or be woken up on new events), collect the lines in a
+ * message until found an empty line, and execute the request.
+ * In any case, deliver events asynchronously through process_events()
+ * (called from here if no line is available, or at the end of
+ * process_message(). )
+ */
+static void *session_do(void *data)
+{
+ struct server_instance *ser = data;
+ struct mansession *s = ast_calloc(1, sizeof(*s));
+ int flags;
+ int res;
+
+ if (s == NULL)
+ goto done;
+
+ s->writetimeout = 100;
+ s->waiting_thread = AST_PTHREADT_NULL;
+
+ flags = fcntl(ser->fd, F_GETFL);
+ if (!block_sockets) /* make sure socket is non-blocking */
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+ fcntl(ser->fd, F_SETFL, flags);
+
+ ast_mutex_init(&s->__lock);
+ s->send_events = -1;
+ /* these fields duplicate those in the 'ser' structure */
+ s->fd = ser->fd;
+ s->f = ser->f;
+ s->sin = ser->requestor;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_INSERT_HEAD(&sessions, s, list);
+ ast_atomic_fetchadd_int(&num_sessions, 1);
+ AST_LIST_UNLOCK(&sessions);
+ /* Hook to the tail of the event queue */
+ s->last_ev = grab_last();
+ s->f = ser->f;
+ astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
+ for (;;) {
+ if ((res = do_message(s)) < 0)
+ break;
+ }
+ /* session is over, explain why and terminate */
+ if (s->authenticated) {
+ if (manager_displayconnects(s))
+ ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ } else {
+ if (displayconnects)
+ ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
+ }
+
+ /* It is possible under certain circumstances for this session thread
+ to complete its work and exit *before* the thread that created it
+ has finished executing the ast_pthread_create_background() function.
+ If this occurs, some versions of glibc appear to act in a buggy
+ fashion and attempt to write data into memory that it thinks belongs
+ to the thread but is in fact not owned by the thread (or may have
+ been freed completely).
+
+ Causing this thread to yield to other threads at least one time
+ appears to work around this bug.
+ */
+ usleep(1);
+
+ destroy_session(s);
+
+done:
+ ast_free(ser);
+ return NULL;
+}
+
+/*! \brief remove at most n_max stale session from the list. */
+static void purge_sessions(int n_max)
+{
+ struct mansession *s;
+ time_t now = time(NULL);
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
+ if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_atomic_fetchadd_int(&num_sessions, -1);
+ if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
+ ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
+ s->username, ast_inet_ntoa(s->sin.sin_addr));
+ }
+ free_session(s); /* XXX outside ? */
+ if (--n_max <= 0)
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&sessions);
+}
+
+/*
+ * events are appended to a queue from where they
+ * can be dispatched to clients.
+ */
+static int append_event(const char *str, int category)
+{
+ struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
+ static int seq; /* sequence number */
+
+ if (!tmp)
+ return -1;
+
+ /* need to init all fields, because ast_malloc() does not */
+ tmp->usecount = 0;
+ tmp->category = category;
+ tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
+ AST_LIST_NEXT(tmp, eq_next) = NULL;
+ strcpy(tmp->eventdata, str);
+
+ AST_LIST_LOCK(&all_events);
+ AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
+ AST_LIST_UNLOCK(&all_events);
+
+ return 0;
+}
+
+/* XXX see if can be moved inside the function */
+AST_THREADSTORAGE(manager_event_buf);
+#define MANAGER_EVENT_BUF_INITSIZE 256
+
+/*! \brief manager_event: Send AMI event to client */
+int __manager_event(int category, const char *event,
+ const char *file, int line, const char *func, const char *fmt, ...)
+{
+ struct mansession *s;
+ struct manager_custom_hook *hook;
+ struct ast_str *auth = ast_str_alloca(80);
+ const char *cat_str;
+ va_list ap;
+ struct timeval now;
+ struct ast_str *buf;
+
+ /* Abort if there aren't any manager sessions */
+ if (!num_sessions)
+ return 0;
+
+ if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
+ return -1;
+
+ cat_str = authority_to_str(category, &auth);
+ ast_str_set(&buf, 0,
+ "Event: %s\r\nPrivilege: %s\r\n",
+ event, cat_str);
+
+ if (timestampevents) {
+ now = ast_tvnow();
+ ast_str_append(&buf, 0,
+ "Timestamp: %ld.%06lu\r\n",
+ now.tv_sec, (unsigned long) now.tv_usec);
+ }
+ if (manager_debug) {
+ static int seq;
+ ast_str_append(&buf, 0,
+ "SequenceNumber: %d\r\n",
+ ast_atomic_fetchadd_int(&seq, 1));
+ ast_str_append(&buf, 0,
+ "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
+ }
+
+ va_start(ap, fmt);
+ ast_str_append_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ ast_str_append(&buf, 0, "\r\n");
+
+ append_event(buf->str, category);
+
+ /* Wake up any sleeping sessions */
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread != AST_PTHREADT_NULL)
+ pthread_kill(s->waiting_thread, SIGURG);
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ AST_RWLIST_RDLOCK(&manager_hooks);
+ AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
+ hook->helper(category, event, buf->str);
+ }
+ AST_RWLIST_UNLOCK(&manager_hooks);
+
+ return 0;
+}
+
+/*
+ * support functions to register/unregister AMI action handlers,
+ */
+int ast_manager_unregister(char *action)
+{
+ struct manager_action *cur;
+
+ AST_RWLIST_WRLOCK(&actions);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
+ if (!strcasecmp(action, cur->action)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(cur);
+ ast_verb(2, "Manager unregistered action %s\n", action);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&actions);
+
+ return 0;
+}
+
+static int manager_state_cb(char *context, char *exten, int state, void *data)
+{
+ /* Notify managers of change */
+ char hint[BUFSIZ];
+ ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
+
+ manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
+ return 0;
+}
+
+static int ast_manager_register_struct(struct manager_action *act)
+{
+ struct manager_action *cur, *prev = NULL;
+
+ AST_RWLIST_WRLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ int ret = strcasecmp(cur->action, act->action);
+ if (ret == 0) {
+ ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
+ AST_RWLIST_UNLOCK(&actions);
+ return -1;
+ }
+ if (ret > 0) { /* Insert these alphabetically */
+ prev = cur;
+ break;
+ }
+ }
+
+ if (prev)
+ AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
+ else
+ AST_RWLIST_INSERT_HEAD(&actions, act, list);
+
+ ast_verb(2, "Manager registered action %s\n", act->action);
+
+ AST_RWLIST_UNLOCK(&actions);
+
+ return 0;
+}
+
+/*! \brief register a new command with manager, including online help. This is
+ the preferred way to register a manager command */
+int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
+{
+ struct manager_action *cur = NULL;
+
+ if (!(cur = ast_calloc(1, sizeof(*cur))))
+ return -1;
+
+ cur->action = action;
+ cur->authority = auth;
+ cur->func = func;
+ cur->synopsis = synopsis;
+ cur->description = description;
+
+ ast_manager_register_struct(cur);
+
+ return 0;
+}
+/*! @}
+ END Doxygen group */
+
+/*
+ * The following are support functions for AMI-over-http.
+ * The common entry point is generic_http_callback(),
+ * which extracts HTTP header and URI fields and reformats
+ * them into AMI messages, locates a proper session
+ * (using the mansession_id Cookie or GET variable),
+ * and calls process_message() as for regular AMI clients.
+ * When done, the output (which goes to a temporary file)
+ * is read back into a buffer and reformatted as desired,
+ * then fed back to the client over the original socket.
+ */
+
+enum output_format {
+ FORMAT_RAW,
+ FORMAT_HTML,
+ FORMAT_XML,
+};
+
+static char *contenttype[] = {
+ [FORMAT_RAW] = "plain",
+ [FORMAT_HTML] = "html",
+ [FORMAT_XML] = "xml",
+};
+
+/*!
+ * locate an http session in the list. The search key (ident) is
+ * the value of the mansession_id cookie (0 is not valid and means
+ * a session on the AMI socket).
+ */
+static struct mansession *find_session(unsigned long ident)
+{
+ struct mansession *s;
+
+ if (ident == 0)
+ return NULL;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if (s->managerid == ident && !s->needdestroy) {
+ ast_atomic_fetchadd_int(&s->inuse, 1);
+ break;
+ }
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ return s;
+}
+
+int astman_verify_session_readpermissions(unsigned long ident, int perm)
+{
+ int result = 0;
+ struct mansession *s;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if ((s->managerid == ident) && (s->readperm & perm)) {
+ result = 1;
+ ast_mutex_unlock(&s->__lock);
+ break;
+ }
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+ return result;
+}
+
+int astman_verify_session_writepermissions(unsigned long ident, int perm)
+{
+ int result = 0;
+ struct mansession *s;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if ((s->managerid == ident) && (s->writeperm & perm)) {
+ result = 1;
+ ast_mutex_unlock(&s->__lock);
+ break;
+ }
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+ return result;
+}
+
+/*
+ * convert to xml with various conversion:
+ * mode & 1 -> lowercase;
+ * mode & 2 -> replace non-alphanumeric chars with underscore
+ */
+static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
+{
+ /* store in a local buffer to avoid calling ast_str_append too often */
+ char buf[256];
+ char *dst = buf;
+ int space = sizeof(buf);
+ /* repeat until done and nothing to flush */
+ for ( ; *src || dst != buf ; src++) {
+ if (*src == '\0' || space < 10) { /* flush */
+ *dst++ = '\0';
+ ast_str_append(out, 0, "%s", buf);
+ dst = buf;
+ space = sizeof(buf);
+ if (*src == '\0')
+ break;
+ }
+
+ if ( (mode & 2) && !isalnum(*src)) {
+ *dst++ = '_';
+ space--;
+ continue;
+ }
+ switch (*src) {
+ case '<':
+ strcpy(dst, "&lt;");
+ dst += 4;
+ space -= 4;
+ break;
+ case '>':
+ strcpy(dst, "&gt;");
+ dst += 4;
+ space -= 4;
+ break;
+ case '\"':
+ strcpy(dst, "&quot;");
+ dst += 6;
+ space -= 6;
+ break;
+ case '\'':
+ strcpy(dst, "&apos;");
+ dst += 6;
+ space -= 6;
+ break;
+ case '&':
+ strcpy(dst, "&amp;");
+ dst += 5;
+ space -= 5;
+ break;
+
+ default:
+ *dst++ = mode ? tolower(*src) : *src;
+ space--;
+ }
+ }
+}
+
+struct variable_count {
+ char *varname;
+ int count;
+};
+
+static int compress_char(char c)
+{
+ c &= 0x7f;
+ if (c < 32)
+ return 0;
+ else if (c >= 'a' && c <= 'z')
+ return c - 64;
+ else if (c > 'z')
+ return '_';
+ else
+ return c - 32;
+}
+
+static int variable_count_hash_fn(const void *vvc, const int flags)
+{
+ const struct variable_count *vc = vvc;
+ int res = 0, i;
+ for (i = 0; i < 5; i++) {
+ if (vc->varname[i] == '\0')
+ break;
+ res += compress_char(vc->varname[i]) << (i * 6);
+ }
+ return res;
+}
+
+static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
+{
+ /* Due to the simplicity of struct variable_count, it makes no difference
+ * if you pass in objects or strings, the same operation applies. This is
+ * due to the fact that the hash occurs on the first element, which means
+ * the address of both the struct and the string are exactly the same. */
+ struct variable_count *vc = obj;
+ char *str = vstr;
+ return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
+}
+
+/*! \brief Convert the input into XML or HTML.
+ * The input is supposed to be a sequence of lines of the form
+ * Name: value
+ * optionally followed by a blob of unformatted text.
+ * A blank line is a section separator. Basically, this is a
+ * mixture of the format of Manager Interface and CLI commands.
+ * The unformatted text is considered as a single value of a field
+ * named 'Opaque-data'.
+ *
+ * At the moment the output format is the following (but it may
+ * change depending on future requirements so don't count too
+ * much on it when writing applications):
+ *
+ * General: the unformatted text is used as a value of
+ * XML output: to be completed
+ *
+ * \verbatim
+ * Each section is within <response type="object" id="xxx">
+ * where xxx is taken from ajaxdest variable or defaults to unknown
+ * Each row is reported as an attribute Name="value" of an XML
+ * entity named from the variable ajaxobjtype, default to "generic"
+ * \endverbatim
+ *
+ * HTML output:
+ * each Name-value pair is output as a single row of a two-column table.
+ * Sections (blank lines in the input) are separated by a <HR>
+ *
+ */
+static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
+{
+ struct ast_variable *v;
+ const char *dest = NULL;
+ char *var, *val;
+ const char *objtype = NULL;
+ int in_data = 0; /* parsing data */
+ int inobj = 0;
+ int xml = (format == FORMAT_XML);
+ struct variable_count *vc = NULL;
+ struct ao2_container *vco = NULL;
+
+ for (v = vars; v; v = v->next) {
+ if (!dest && !strcasecmp(v->name, "ajaxdest"))
+ dest = v->value;
+ else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
+ objtype = v->value;
+ }
+ if (!dest)
+ dest = "unknown";
+ if (!objtype)
+ objtype = "generic";
+
+ /* we want to stop when we find an empty line */
+ while (in && *in) {
+ val = strsep(&in, "\r\n"); /* mark start and end of line */
+ if (in && *in == '\n') /* remove trailing \n if any */
+ in++;
+ ast_trim_blanks(val);
+ ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
+ if (ast_strlen_zero(val)) {
+ if (in_data) { /* close data */
+ ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
+ in_data = 0;
+ }
+ ast_str_append(out, 0, xml ? " /></response>\n" :
+ "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ inobj = 0;
+ ao2_ref(vco, -1);
+ vco = NULL;
+ continue;
+ }
+
+ /* we expect Name: value lines */
+ if (in_data) {
+ var = NULL;
+ } else {
+ var = strsep(&val, ":");
+ if (val) { /* found the field name */
+ val = ast_skip_blanks(val);
+ ast_trim_blanks(var);
+ } else { /* field name not found, move to opaque mode */
+ val = var;
+ var = "Opaque-data";
+ }
+ }
+
+ if (!inobj) {
+ if (xml)
+ ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
+ else
+ ast_str_append(out, 0, "<body>\n");
+ vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
+ inobj = 1;
+ }
+
+ if (!in_data) { /* build appropriate line start */
+ ast_str_append(out, 0, xml ? " " : "<tr><td>");
+ if ((vc = ao2_find(vco, var, 0)))
+ vc->count++;
+ else {
+ /* Create a new entry for this one */
+ vc = ao2_alloc(sizeof(*vc), NULL);
+ vc->varname = var;
+ vc->count = 1;
+ ao2_link(vco, vc);
+ }
+ xml_copy_escape(out, var, xml ? 1 | 2 : 0);
+ if (vc->count > 1)
+ ast_str_append(out, 0, "-%d", vc->count);
+ ao2_ref(vc, -1);
+ ast_str_append(out, 0, xml ? "='" : "</td><td>");
+ if (!strcmp(var, "Opaque-data"))
+ in_data = 1;
+ }
+ xml_copy_escape(out, val, 0); /* data field */
+ if (!in_data)
+ ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
+ else
+ ast_str_append(out, 0, xml ? "\n" : "<br>\n");
+ }
+ if (inobj) {
+ ast_str_append(out, 0, xml ? " /></response>\n" :
+ "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ ao2_ref(vco, -1);
+ }
+}
+
+static struct ast_str *generic_http_callback(enum output_format format,
+ struct sockaddr_in *requestor, const char *uri,
+ struct ast_variable *params, int *status,
+ char **title, int *contentlength)
+{
+ struct mansession *s = NULL;
+ unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
+ int blastaway = 0;
+ struct ast_variable *v;
+ char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
+ struct ast_str *out = NULL;
+ struct message m = { 0 };
+ unsigned int x;
+ size_t hdrlen;
+
+ for (v = params; v; v = v->next) {
+ if (!strcasecmp(v->name, "mansession_id")) {
+ sscanf(v->value, "%lx", &ident);
+ break;
+ }
+ }
+
+ if (!(s = find_session(ident))) {
+ /* Create new session.
+ * While it is not in the list we don't need any locking
+ */
+ if (!(s = ast_calloc(1, sizeof(*s)))) {
+ *status = 500;
+ goto generic_callback_out;
+ }
+ s->sin = *requestor;
+ s->fd = -1;
+ s->waiting_thread = AST_PTHREADT_NULL;
+ s->send_events = 0;
+ ast_mutex_init(&s->__lock);
+ ast_mutex_lock(&s->__lock);
+ s->inuse = 1;
+ s->managerid = rand() | 1; /* make sure it is non-zero */
+ s->last_ev = grab_last();
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_INSERT_HEAD(&sessions, s, list);
+ ast_atomic_fetchadd_int(&num_sessions, 1);
+ AST_LIST_UNLOCK(&sessions);
+ }
+
+ ast_mutex_unlock(&s->__lock);
+
+ if (!(out = ast_str_create(1024))) {
+ *status = 500;
+ goto generic_callback_out;
+ }
+
+ s->fd = mkstemp(template); /* create a temporary file for command output */
+ unlink(template);
+ s->f = fdopen(s->fd, "w+");
+
+ for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
+ hdrlen = strlen(v->name) + strlen(v->value) + 3;
+ m.headers[m.hdrcount] = alloca(hdrlen);
+ snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
+ m.hdrcount = x + 1;
+ }
+
+ if (process_message(s, &m)) {
+ if (s->authenticated) {
+ if (manager_displayconnects(s))
+ ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ } else {
+ if (displayconnects)
+ ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
+ }
+ s->needdestroy = 1;
+ }
+
+ ast_str_append(&out, 0,
+ "Content-type: text/%s\r\n"
+ "Cache-Control: no-cache;\r\n"
+ "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
+ "\r\n",
+ contenttype[format],
+ s->managerid, httptimeout);
+
+ if (format == FORMAT_XML) {
+ ast_str_append(&out, 0, "<ajax-response>\n");
+ } else if (format == FORMAT_HTML) {
+
+#define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
+#define TEST_STRING \
+ "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
+ user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
+ <input type=\"submit\"></form>"
+
+ ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
+ ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
+ ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
+ ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
+ }
+
+ if (s->f != NULL) { /* have temporary output */
+ char *buf;
+ size_t l = ftell(s->f);
+
+ if (l) {
+ if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
+ if (format == FORMAT_XML || format == FORMAT_HTML)
+ xml_translate(&out, buf, params, format);
+ else
+ ast_str_append(&out, 0, buf);
+ munmap(buf, l);
+ }
+ } else if (format == FORMAT_XML || format == FORMAT_HTML) {
+ xml_translate(&out, "", params, format);
+ }
+ fclose(s->f);
+ s->f = NULL;
+ s->fd = -1;
+ }
+
+ if (format == FORMAT_XML) {
+ ast_str_append(&out, 0, "</ajax-response>\n");
+ } else if (format == FORMAT_HTML)
+ ast_str_append(&out, 0, "</table></body>\r\n");
+
+ ast_mutex_lock(&s->__lock);
+ /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
+ s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
+
+ if (s->needdestroy) {
+ if (s->inuse == 1) {
+ ast_debug(1, "Need destroy, doing it now!\n");
+ blastaway = 1;
+ } else {
+ ast_debug(1, "Need destroy, but can't do it yet!\n");
+ if (s->waiting_thread != AST_PTHREADT_NULL)
+ pthread_kill(s->waiting_thread, SIGURG);
+ s->inuse--;
+ }
+ } else
+ s->inuse--;
+ ast_mutex_unlock(&s->__lock);
+
+ if (blastaway)
+ destroy_session(s);
+generic_callback_out:
+ if (*status != 200)
+ return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
+ return out;
+}
+
+static struct ast_str *manager_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, params, status, title, contentlength);
+}
+
+static struct ast_str *mxml_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_XML, &ser->requestor, uri, params, status, title, contentlength);
+}
+
+static struct ast_str *rawman_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, params, status, title, contentlength);
+}
+
+struct ast_http_uri rawmanuri = {
+ .description = "Raw HTTP Manager Event Interface",
+ .uri = "rawman",
+ .has_subtree = 0,
+ .callback = rawman_http_callback,
+};
+
+struct ast_http_uri manageruri = {
+ .description = "HTML Manager Event Interface",
+ .uri = "manager",
+ .has_subtree = 0,
+ .callback = manager_http_callback,
+};
+
+struct ast_http_uri managerxmluri = {
+ .description = "XML Manager Event Interface",
+ .uri = "mxml",
+ .has_subtree = 0,
+ .callback = mxml_http_callback,
+};
+
+static int registered = 0;
+static int webregged = 0;
+
+/*! \brief cleanup code called at each iteration of server_root,
+ * guaranteed to happen every 5 seconds at most
+ */
+static void purge_old_stuff(void *data)
+{
+ purge_sessions(1);
+ purge_events();
+}
+
+struct ast_tls_config ami_tls_cfg;
+static struct server_args ami_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = NULL,
+ .poll_timeout = 5000, /* wake up every 5 seconds */
+ .periodic_fn = purge_old_stuff,
+ .name = "AMI server",
+ .accept_fn = server_root, /* thread doing the accept() */
+ .worker_fn = session_do, /* thread handling the session */
+};
+
+static struct server_args amis_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = &ami_tls_cfg,
+ .poll_timeout = -1, /* the other does the periodic cleanup */
+ .name = "AMI TLS server",
+ .accept_fn = server_root, /* thread doing the accept() */
+ .worker_fn = session_do, /* thread handling the session */
+};
+
+static int __init_manager(int reload)
+{
+ struct ast_config *ucfg = NULL, *cfg = NULL;
+ const char *val;
+ char *cat = NULL;
+ int newhttptimeout = 60;
+ int have_sslbindaddr = 0;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct ast_manager_user *user = NULL;
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if (!registered) {
+ /* Register default actions */
+ ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
+ ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
+ ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
+ ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
+ ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
+ ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
+ ast_manager_register("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status" );
+ ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
+ ast_manager_register2("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar, "Gets a Channel Variable", mandescr_getvar );
+ ast_manager_register2("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
+ ast_manager_register2("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
+ ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
+ ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
+ ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
+ ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
+ ast_manager_register2("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
+ ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
+ ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
+ ast_manager_register2("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
+ ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
+ ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
+ ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
+ ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
+ ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
+ ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
+ ast_manager_register2("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload, "Send a reload event", mandescr_reload);
+ ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
+ ast_manager_register2("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload, "Module management", mandescr_moduleload);
+ ast_manager_register2("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck, "Check if module is loaded", mandescr_modulecheck);
+
+ ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
+ ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
+ registered = 1;
+ /* Append placeholder event so master_eventq never runs dry */
+ append_event("Event: Placeholder\r\n\r\n", 0);
+ }
+ if ((cfg = ast_config_load("manager.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ displayconnects = 1;
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf. Asterisk management interface (AMI) disabled.\n");
+ return 0;
+ }
+
+ /* default values */
+ memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
+ memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
+ amis_desc.sin.sin_port = htons(5039);
+ ami_desc.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
+
+ ami_tls_cfg.enabled = 0;
+ if (ami_tls_cfg.certfile)
+ ast_free(ami_tls_cfg.certfile);
+ ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
+ if (ami_tls_cfg.cipher)
+ ast_free(ami_tls_cfg.cipher);
+ ami_tls_cfg.cipher = ast_strdup("");
+
+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+ val = var->value;
+ if (!strcasecmp(var->name, "sslenable"))
+ ami_tls_cfg.enabled = ast_true(val);
+ else if (!strcasecmp(var->name, "sslbindport"))
+ amis_desc.sin.sin_port = htons(atoi(val));
+ else if (!strcasecmp(var->name, "sslbindaddr")) {
+ if ((hp = ast_gethostbyname(val, &ahp))) {
+ memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
+ have_sslbindaddr = 1;
+ } else {
+ ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
+ }
+ } else if (!strcasecmp(var->name, "sslcert")) {
+ ast_free(ami_tls_cfg.certfile);
+ ami_tls_cfg.certfile = ast_strdup(val);
+ } else if (!strcasecmp(var->name, "sslcipher")) {
+ ast_free(ami_tls_cfg.cipher);
+ ami_tls_cfg.cipher = ast_strdup(val);
+ } else if (!strcasecmp(var->name, "enabled")) {
+ manager_enabled = ast_true(val);
+ } else if (!strcasecmp(var->name, "block-sockets")) {
+ block_sockets = ast_true(val);
+ } else if (!strcasecmp(var->name, "webenabled")) {
+ webmanager_enabled = ast_true(val);
+ } else if (!strcasecmp(var->name, "port")) {
+ ami_desc.sin.sin_port = htons(atoi(val));
+ } else if (!strcasecmp(var->name, "bindaddr")) {
+ if (!inet_aton(val, &ami_desc.sin.sin_addr)) {
+ ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
+ memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr));
+ }
+ } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
+ allowmultiplelogin = ast_true(val);
+ } else if (!strcasecmp(var->name, "displayconnects")) {
+ displayconnects = ast_true(val);
+ } else if (!strcasecmp(var->name, "timestampevents")) {
+ timestampevents = ast_true(val);
+ } else if (!strcasecmp(var->name, "debug")) {
+ manager_debug = ast_true(val);
+ } else if (!strcasecmp(var->name, "httptimeout")) {
+ newhttptimeout = atoi(val);
+ } else {
+ ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
+ var->name, val);
+ }
+ }
+
+ if (manager_enabled)
+ ami_desc.sin.sin_family = AF_INET;
+ if (!have_sslbindaddr)
+ amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
+ if (ami_tls_cfg.enabled)
+ amis_desc.sin.sin_family = AF_INET;
+
+
+ AST_RWLIST_WRLOCK(&users);
+
+ /* First, get users from users.conf */
+ ucfg = ast_config_load("users.conf", config_flags);
+ if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED)) {
+ const char *hasmanager;
+ int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
+
+ while ((cat = ast_category_browse(ucfg, cat))) {
+ if (!strcasecmp(cat, "general"))
+ continue;
+
+ hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
+ if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
+ const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
+ const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
+ const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
+ const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
+ const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
+
+ /* Look for an existing entry,
+ * if none found - create one and add it to the list
+ */
+ if (!(user = get_manager_by_name_locked(cat))) {
+ if (!(user = ast_calloc(1, sizeof(*user))))
+ break;
+
+ /* Copy name over */
+ ast_copy_string(user->username, cat, sizeof(user->username));
+ /* Insert into list */
+ AST_LIST_INSERT_TAIL(&users, user, list);
+ user->ha = NULL;
+ user->readperm = -1;
+ user->writeperm = -1;
+ /* Default displayconnect from [general] */
+ user->displayconnects = displayconnects;
+ user->writetimeout = 100;
+ }
+
+ if (!user_secret)
+ user_secret = ast_variable_retrieve(ucfg, "general", "secret");
+ if (!user_read)
+ user_read = ast_variable_retrieve(ucfg, "general", "read");
+ if (!user_write)
+ user_write = ast_variable_retrieve(ucfg, "general", "write");
+ if (!user_displayconnects)
+ user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
+ if (!user_writetimeout)
+ user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
+
+ if (!ast_strlen_zero(user_secret)) {
+ if (user->secret)
+ ast_free(user->secret);
+ user->secret = ast_strdup(user_secret);
+ }
+
+ if (user_read)
+ user->readperm = get_perm(user_read);
+ if (user_write)
+ user->writeperm = get_perm(user_write);
+ if (user_displayconnects)
+ user->displayconnects = ast_true(user_displayconnects);
+
+ if (user_writetimeout) {
+ int val = atoi(user_writetimeout);
+ if (val < 100)
+ ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
+ else
+ user->writetimeout = val;
+ }
+ }
+ }
+ ast_config_destroy(ucfg);
+ }
+
+ /* cat is NULL here in any case */
+
+ while ((cat = ast_category_browse(cfg, cat))) {
+ struct ast_ha *oldha;
+
+ if (!strcasecmp(cat, "general"))
+ continue;
+
+ /* Look for an existing entry, if none found - create one and add it to the list */
+ if (!(user = get_manager_by_name_locked(cat))) {
+ if (!(user = ast_calloc(1, sizeof(*user))))
+ break;
+ /* Copy name over */
+ ast_copy_string(user->username, cat, sizeof(user->username));
+
+ user->ha = NULL;
+ user->readperm = 0;
+ user->writeperm = 0;
+ /* Default displayconnect from [general] */
+ user->displayconnects = displayconnects;
+ user->writetimeout = 100;
+
+ /* Insert into list */
+ AST_RWLIST_INSERT_TAIL(&users, user, list);
+ }
+
+ /* Make sure we keep this user and don't destroy it during cleanup */
+ user->keep = 1;
+ oldha = user->ha;
+ user->ha = NULL;
+
+ var = ast_variable_browse(cfg, cat);
+ for (; var; var = var->next) {
+ if (!strcasecmp(var->name, "secret")) {
+ if (user->secret)
+ ast_free(user->secret);
+ user->secret = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "deny") ||
+ !strcasecmp(var->name, "permit")) {
+ user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
+ } else if (!strcasecmp(var->name, "read") ) {
+ user->readperm = get_perm(var->value);
+ } else if (!strcasecmp(var->name, "write") ) {
+ user->writeperm = get_perm(var->value);
+ } else if (!strcasecmp(var->name, "displayconnects") ) {
+ user->displayconnects = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "writetimeout")) {
+ int val = atoi(var->value);
+ if (val < 100)
+ ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
+ else
+ user->writetimeout = val;
+ } else
+ ast_debug(1, "%s is an unknown option.\n", var->name);
+ }
+ ast_free_ha(oldha);
+ }
+ ast_config_destroy(cfg);
+
+ /* Perform cleanup - essentially prune out old users that no longer exist */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
+ if (user->keep) { /* valid record. clear flag for the next round */
+ user->keep = 0;
+ continue;
+ }
+ /* We do not need to keep this user so take them out of the list */
+ AST_RWLIST_REMOVE_CURRENT(list);
+ /* Free their memory now */
+ if (user->secret)
+ ast_free(user->secret);
+ ast_free_ha(user->ha);
+ ast_free(user);
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ AST_RWLIST_UNLOCK(&users);
+
+ if (webmanager_enabled && manager_enabled) {
+ if (!webregged) {
+ ast_http_uri_link(&rawmanuri);
+ ast_http_uri_link(&manageruri);
+ ast_http_uri_link(&managerxmluri);
+ webregged = 1;
+ }
+ } else {
+ if (webregged) {
+ ast_http_uri_unlink(&rawmanuri);
+ ast_http_uri_unlink(&manageruri);
+ ast_http_uri_unlink(&managerxmluri);
+ webregged = 0;
+ }
+ }
+
+ if (newhttptimeout > 0)
+ httptimeout = newhttptimeout;
+
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
+
+ server_start(&ami_desc);
+ if (ssl_setup(amis_desc.tls_cfg))
+ server_start(&amis_desc);
+ return 0;
+}
+
+int init_manager(void)
+{
+ return __init_manager(0);
+}
+
+int reload_manager(void)
+{
+ return __init_manager(1);
+}
diff --git a/trunk/main/md5.c b/trunk/main/md5.c
new file mode 100644
index 000000000..594c5eff5
--- /dev/null
+++ b/trunk/main/md5.c
@@ -0,0 +1,265 @@
+
+/*!\file
+\brief MD5 checksum routines used for authentication. Not covered by GPL, but
+ in the public domain as per the copyright below */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/endian.h"
+#include "asterisk/md5.h"
+
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define HIGHFIRST 1
+# endif
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+ ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/trunk/main/minimime/Make.conf b/trunk/main/minimime/Make.conf
new file mode 100644
index 000000000..de149932a
--- /dev/null
+++ b/trunk/main/minimime/Make.conf
@@ -0,0 +1,7 @@
+CC=gcc
+PREFIX=/usr
+LIBNAME=libmmime.so.0.0
+HAVE_STRLCAT=
+HAVE_STRLCPY=
+INSTALL=/usr/bin/install
+HAVE_DEBUG=1
diff --git a/trunk/main/minimime/Makefile b/trunk/main/minimime/Makefile
new file mode 100644
index 000000000..555d17a04
--- /dev/null
+++ b/trunk/main/minimime/Makefile
@@ -0,0 +1,67 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for resource modules
+#
+# Copyright (C) 2007, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+LIBMMIME:=libmmime.a
+MM_SRCS= \
+ mimeparser.tab.c \
+ mimeparser.yy.c \
+ mm_init.c \
+ mm_base64.c \
+ mm_codecs.c \
+ mm_contenttype.c \
+ mm_context.c \
+ mm_envelope.c \
+ mm_error.c \
+ mm_header.c \
+ mm_mem.c \
+ mm_mimepart.c \
+ mm_mimeutil.c \
+ mm_param.c \
+ mm_parse.c \
+ mm_util.c
+
+MM_OBJS:=$(MM_SRCS:%.c=%.o)
+MM_HDRS:=mm.h mm_util.h
+
+# Use weaker error checking because we have some automatically generated
+# files. However just mask out -Werror, because other warnings below:
+# -Wundef -Wstrict-prototypes -Wmissing-declarations
+# -Wmissing-prototypes
+# may actually be important and spot out real bugs.
+ASTCFLAGS:=$(filter-out -Werror,$(ASTCFLAGS))
+
+ASTCFLAGS+=-std=c99
+
+all: $(LIBMMIME)
+
+$(LIBMMIME): $(MM_OBJS)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+mimeparser.yy.c:
+ flex -Pmimeparser_yy -omimeparser.yy.c mimeparser.l
+
+mimeparser.tab.c:
+ bison -d -pmimeparser_yy -omimeparser.tab.c mimeparser.y
+
+clean::
+ rm -f $(LIBMMIME) *.o
+
+.PHONY: clean all
+
+ifneq ($(wildcard .*.d),)
+ include .*.d
+endif
diff --git a/trunk/main/minimime/mimeparser.h b/trunk/main/minimime/mimeparser.h
new file mode 100644
index 000000000..f6bfbaad7
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.h
@@ -0,0 +1,76 @@
+#ifndef _MIMEPARSER_H_INCLUDED
+#define _MIMEPARSER_H_INCLUDED
+
+#include "mm.h"
+
+struct s_position
+{
+ size_t opaque_start;
+ size_t start;
+ size_t end;
+};
+
+struct lexer_state
+{
+ int header_state;
+ int lineno;
+ size_t current_pos;
+ int condition;
+
+ int is_envelope;
+
+ size_t message_len;
+ size_t buffer_length;
+
+ /* temporary marker variables */
+ size_t body_opaque_start;
+ size_t body_start;
+ size_t body_end;
+ size_t preamble_start;
+ size_t preamble_end;
+ size_t postamble_start;
+ size_t postamble_end;
+
+ char *boundary_string;
+ char *endboundary_string;
+ const char *message_buffer;
+};
+
+
+struct parser_state
+{
+ MM_CTX *ctx;
+ struct mm_mimepart *envelope;
+ struct mm_mimepart *temppart;
+ struct mm_mimepart *current_mimepart;
+ struct mm_content *ctype;
+ int parsemode;
+ int have_contenttype;
+ int debug;
+ int mime_parts;
+ struct lexer_state lstate;
+};
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+#include "mimeparser.tab.h"
+
+/**
+ * Prototypes for functions used by the parser routines
+ */
+int count_lines(char *);
+int dprintf2(struct parser_state *, const char *, ...);
+int mimeparser_yyparse(struct parser_state *, void *);
+int mimeparser_yylex(YYSTYPE *, void *);
+int mimeparser_yyerror(struct parser_state *, void *, const char *);
+int mimeparser_yylex_init(yyscan_t* scanner);
+int mimeparser_yylex_destroy(yyscan_t yyscanner);
+void reset_lexer_state(void *yyscanner, struct parser_state *pstate);
+int PARSER_initialize(struct parser_state *pstate, yyscan_t scanner);
+void PARSER_setbuffer(const char *string, yyscan_t scanner);
+void PARSER_setfp(FILE *fp, yyscan_t scanner);
+
+#endif /* ! _MIMEPARSER_H_INCLUDED */
diff --git a/trunk/main/minimime/mimeparser.l b/trunk/main/minimime/mimeparser.l
new file mode 100644
index 000000000..19d42cf3a
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.l
@@ -0,0 +1,484 @@
+%{
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * This is a lexer file for parsing MIME compatible messages. It is intended
+ * to satisfy at least RFC 2045 (Format of Internet Message Bodies). It still
+ * has quite a few problems:
+ *
+ * - The parsing could probably be done in a more elegant way
+ * - I don't know what performance impact REJECT has on the parser
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mimeparser.tab.h"
+
+#define NAMEOF(v) #v
+/* BC() is a debug wrapper for lex' BEGIN() macro */
+#define BC(x) do { \
+ struct lexer_state *lstate = yyget_extra(yyscanner); \
+ BEGIN(x); \
+ lstate->condition = x; \
+} while(0);
+
+#define ZERO(x) memset(x, '\0', sizeof(x))
+
+#define PREALLOC_BUFFER 100000
+#undef YY_BUF_SIZE
+#define YY_BUF_SIZE 65536
+
+enum header_states
+{
+ STATE_MAIL = 0,
+ STATE_CTYPE,
+ STATE_CDISP,
+ STATE_CENC,
+ STATE_MIME
+};
+
+
+
+%}
+
+%option reentrant
+%option yylineno
+%option bison-bridge
+
+%s headers
+%s header
+%s headervalue
+%s tspecialvalue
+%s comment
+%s body
+%s postamble
+%s preamble
+%s boundary
+%s endboundary
+%s endoffile
+
+STRING [a-zA-Z0-9\-\.\_]
+TSPECIAL [a-zA-Z0-9)(<>@,;:/\-.=_\+'? ]
+TSPECIAL_LITE [a-zA-Z0-9)(<>@,-._+'?\[\]]
+
+%%
+
+<INITIAL,headers>^[a-zA-Z]+[a-zA-Z0-9\-\_]* {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ BC(header);
+
+ /* Depending on what header we are processing, we enter a different
+ * state and return a different value.
+ */
+ if (!strcasecmp(yytext, "Content-Type")) {
+ lstate->header_state = STATE_CTYPE;
+ return CONTENTTYPE_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Transfer-Encoding")) {
+ lstate->header_state = STATE_CENC;
+ return CONTENTENCODING_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Disposition")) {
+ lstate->header_state = STATE_CDISP;
+ return CONTENTDISPOSITION_HEADER;
+ } else if (!strcasecmp(yytext, "MIME-Version")) {
+ lstate->header_state = STATE_MAIL;
+ return MIMEVERSION_HEADER;
+ } else {
+ lstate->header_state = STATE_MAIL;
+ return MAIL_HEADER;
+ }
+}
+
+<INITIAL,headers>. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /* dprintf2("Unknown header char: %c\n", *yytext); */
+ lstate->current_pos += yyleng;
+ return ANY;
+}
+
+<headers>^(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno++;
+
+ lstate->current_pos += yyleng;
+
+ /* This marks the end of headers. Depending on whether we are in the
+ * envelope currently we need to parse either a body or the preamble
+ * now.
+ */
+ if (lstate->is_envelope == 0 || lstate->boundary_string == NULL) {
+ BC(body);
+ lstate->body_start = lstate->current_pos;
+ } else {
+ lstate->is_envelope = 0;
+ lstate->preamble_start = lstate->current_pos;
+ BC(preamble);
+ }
+
+ return ENDOFHEADERS;
+}
+
+<header>\: {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return COLON;
+}
+
+<header>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headers);
+ /* dprintf2("Invalid header, returning EOL\n"); */
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+
+<headervalue>(\n|\r\n)[\ \t]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+<headervalue>.+|(.+(\n|\r\n)[\ \t]+.+)+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ if (lstate->header_state != STATE_MAIL && lstate->header_state != STATE_CENC) {
+ REJECT;
+ }
+ lstate->current_pos += yyleng;
+ while (*yytext && isspace(*yytext)) yytext++;
+ /* Do we actually have a header value? */
+ if (*yytext == '\0') {
+ yylval_param->string = strdup("");
+ } else {
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ }
+ return WORD;
+}
+
+<headervalue,tspecialvalue>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /* marks the end of one header line */
+ lstate->lineno++;
+ BC(headers);
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+
+<headervalue>;|;(\r\n|\n)[\ \t]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return SEMICOLON;
+}
+
+<headervalue>\= {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return EQUAL;
+}
+
+<headervalue>\" {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(tspecialvalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+
+<headervalue>{STRING}+|{TSPECIAL_LITE}+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return WORD;
+}
+
+<headervalue>[\ |\t]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+<tspecialvalue>{TSPECIAL}+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ return TSPECIAL;
+}
+
+<tspecialvalue>\" {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+
+<body>^\-\-{TSPECIAL}+\-\- {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin and end with two dashes. If we have catched a valid
+ * end boundary, which actually ends a body, we save the current
+ * position, put the token back on the input stream and let the
+ * endboundary condition parse the actual token.
+ */
+ if (lstate->endboundary_string != NULL) {
+ if (strcmp(lstate->endboundary_string, yytext)) {
+ /* dprintf2("YYTEXT != end_boundary: '%s'\n", yytext); */
+ REJECT;
+ } else {
+ lstate->current_pos += yyleng;
+ /* dprintf2("YYTEXT == lstate->end_boundary: '%s'\n", yytext); */
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start =
+ lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos - yyleng;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(endboundary);
+ return BODY;
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+
+<body,preamble>^\-\-{TSPECIAL}+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin with two dashes.
+ */
+ if (lstate->boundary_string != NULL) {
+ if (strcmp(lstate->boundary_string, yytext)) {
+ /* dprintf2("YYTEXT != boundary: '%s'\n", yytext);*/
+ REJECT;
+ } else {
+ /* dprintf2("YYTEXT == boundary: '%s'\n", yytext);*/
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start = lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(boundary);
+ return BODY;
+ } else if (lstate->preamble_start) {
+ yylval_param->position.start = lstate->preamble_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->preamble_start = lstate->preamble_end = 0;
+ yyless(0);
+ BC(boundary);
+ return PREAMBLE;
+ } else {
+ BC(boundary);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return(BOUNDARY);
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+
+<body>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ lstate->lineno++;
+}
+
+<body>\r {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ /* dprintf2("stray CR in body...\n"); */
+}
+
+<body>[^\r\n]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+<body><<EOF>> {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ if (lstate->boundary_string == NULL && lstate->body_start) {
+ yylval_param->position.opaque_start = 0;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_start = 0;
+ return BODY;
+ } else if (lstate->body_start) {
+ return POSTAMBLE;
+ }
+ yyterminate();
+}
+
+<preamble,postamble>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /* dprintf2("Preamble CR/LF at line %d\n", lineno); */
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+
+<boundary>[^\r\n]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return BOUNDARY;
+}
+
+<endboundary>[^\r\n]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return ENDBOUNDARY;
+}
+
+<boundary>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headers);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ lstate->body_opaque_start = lstate->current_pos;
+ return EOL;
+}
+
+<endboundary>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(postamble);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+
+<preamble>. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+
+<postamble>. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+
+. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return((int)*yytext);
+}
+
+
+%%
+
+void reset_lexer_state(void *yyscanner, struct parser_state *pstate)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ struct lexer_state *lstate = &(pstate->lstate);
+
+ yyset_extra((void*)lstate, yyscanner);
+ BEGIN(0);
+ lstate->header_state = STATE_MAIL;
+ lstate->lineno = 0;
+ lstate->current_pos = 1;
+ lstate->condition = 0;
+
+ lstate->is_envelope = 1;
+
+ lstate->message_len = 0;
+ lstate->buffer_length = 0;
+
+ /* temporary marker variables */
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ lstate->preamble_start = 0;
+ lstate->preamble_end = 0;
+ lstate->postamble_start = 0;
+ lstate->postamble_end = 0;
+}
+
+void
+PARSER_setbuffer(const char *string, yyscan_t scanner)
+{
+ struct lexer_state *lstate = yyget_extra(scanner);
+ lstate->message_buffer = string;
+ yy_scan_string(string, scanner);
+}
+
+void
+PARSER_setfp(FILE *fp, yyscan_t scanner)
+{
+ /* looks like a bug in bison 2.2a -- the wrong code is generated for yyset_in !! */
+ struct yyguts_t * yyg = (struct yyguts_t*) scanner;
+ yyg->yyin_r = fp;
+
+ if (0) {
+ /* This is just to make a compiler warning go away */
+ yyunput(0, NULL, scanner);
+ }
+}
+
+/**
+ * Counts how many lines a given string represents in the message (in case of
+ * folded header values, for example, or a message body).
+ */
+int
+count_lines(char *txt)
+{
+ char *o;
+ int line;
+
+ line = 0;
+
+ for (o = txt; *o != '\0'; o++)
+ if (*o == '\n')
+ line++;
+
+ return line;
+}
diff --git a/trunk/main/minimime/mimeparser.tab.c b/trunk/main/minimime/mimeparser.tab.c
new file mode 100644
index 000000000..ef9a4a3fd
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.tab.c
@@ -0,0 +1,2339 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ 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, 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse mimeparser_yyparse
+#define yylex mimeparser_yylex
+#define yyerror mimeparser_yyerror
+#define yylval mimeparser_yylval
+#define yychar mimeparser_yychar
+#define yydebug mimeparser_yydebug
+#define yynerrs mimeparser_yynerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ ANY = 258,
+ COLON = 259,
+ DASH = 260,
+ DQUOTE = 261,
+ ENDOFHEADERS = 262,
+ EOL = 263,
+ EOM = 264,
+ EQUAL = 265,
+ MIMEVERSION_HEADER = 266,
+ SEMICOLON = 267,
+ CONTENTDISPOSITION_HEADER = 268,
+ CONTENTENCODING_HEADER = 269,
+ CONTENTTYPE_HEADER = 270,
+ MAIL_HEADER = 271,
+ HEADERVALUE = 272,
+ BOUNDARY = 273,
+ ENDBOUNDARY = 274,
+ CONTENTTYPE_VALUE = 275,
+ TSPECIAL = 276,
+ WORD = 277,
+ BODY = 278,
+ PREAMBLE = 279,
+ POSTAMBLE = 280
+ };
+#endif
+/* Tokens. */
+#define ANY 258
+#define COLON 259
+#define DASH 260
+#define DQUOTE 261
+#define ENDOFHEADERS 262
+#define EOL 263
+#define EOM 264
+#define EQUAL 265
+#define MIMEVERSION_HEADER 266
+#define SEMICOLON 267
+#define CONTENTDISPOSITION_HEADER 268
+#define CONTENTENCODING_HEADER 269
+#define CONTENTTYPE_HEADER 270
+#define MAIL_HEADER 271
+#define HEADERVALUE 272
+#define BOUNDARY 273
+#define ENDBOUNDARY 274
+#define CONTENTTYPE_VALUE 275
+#define TSPECIAL 276
+#define WORD 277
+#define BODY 278
+#define PREAMBLE 279
+#define POSTAMBLE 280
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "mimeparser.y"
+
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * These are the grammatic definitions in yacc syntax to parse MIME conform
+ * messages.
+ *
+ * TODO:
+ * - honour parse flags passed to us (partly done)
+ * - parse Content-Disposition header (partly done)
+ * - parse Content-Encoding header
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mm.h"
+#include "mm_internal.h"
+
+int set_boundary(char *,struct parser_state *);
+int mimeparser_yywrap(void);
+void reset_environ(struct parser_state *pstate);
+int PARSER_initialize(struct parser_state *pstate, void *yyscanner);
+
+static char *PARSE_readmessagepart(size_t, size_t, size_t, size_t *,yyscan_t, struct parser_state *);
+FILE *mimeparser_yyget_in (yyscan_t yyscanner );
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 67 "mimeparser.y"
+{
+ int number;
+ char *string;
+ struct s_position position;
+}
+/* Line 187 of yacc.c. */
+#line 220 "mimeparser.tab.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 233 "mimeparser.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined(YYENABLE_NLS)
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 26
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 61
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 28
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 29
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 50
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 83
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 280
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 27, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 26, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 7, 8, 15, 18, 21, 23,
+ 25, 27, 28, 30, 31, 34, 36, 40, 42, 44,
+ 46, 48, 50, 52, 57, 61, 66, 72, 77, 83,
+ 85, 90, 95, 98, 101, 103, 107, 111, 114, 116,
+ 120, 123, 125, 129, 133, 135, 137, 141, 143, 146,
+ 148
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 29, 0, -1, 30, -1, 32, -1, -1, 33, 34,
+ 31, 36, 55, 35, -1, 33, 56, -1, 38, 33,
+ -1, 53, -1, 38, -1, 24, -1, -1, 25, -1,
+ -1, 36, 37, -1, 37, -1, 54, 33, 56, -1,
+ 39, -1, 40, -1, 41, -1, 43, -1, 44, -1,
+ 45, -1, 16, 4, 22, 8, -1, 16, 4, 8,
+ -1, 15, 4, 47, 8, -1, 15, 4, 47, 48,
+ 8, -1, 13, 4, 42, 8, -1, 13, 4, 42,
+ 49, 8, -1, 22, -1, 14, 4, 22, 8, -1,
+ 11, 4, 22, 8, -1, 46, 8, -1, 46, 3,
+ -1, 3, -1, 22, 26, 22, -1, 12, 50, 48,
+ -1, 12, 50, -1, 12, -1, 12, 51, 49, -1,
+ 12, 51, -1, 12, -1, 22, 10, 52, -1, 22,
+ 10, 52, -1, 22, -1, 21, -1, 27, 21, 27,
+ -1, 7, -1, 18, 8, -1, 19, -1, 23, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 112, 112, 114, 119, 118, 131, 139, 141, 165,
+ 169, 184, 188, 191, 195, 197, 201, 216, 218, 228,
+ 230, 232, 234, 248, 255, 274, 282, 292, 298, 306,
+ 329, 336, 343, 347, 349, 353, 362, 364, 366, 380,
+ 382, 384, 398, 429, 443, 449, 464, 472, 479, 498,
+ 517
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "ANY", "COLON", "DASH", "DQUOTE",
+ "ENDOFHEADERS", "EOL", "EOM", "EQUAL", "MIMEVERSION_HEADER", "SEMICOLON",
+ "CONTENTDISPOSITION_HEADER", "CONTENTENCODING_HEADER",
+ "CONTENTTYPE_HEADER", "MAIL_HEADER", "HEADERVALUE", "BOUNDARY",
+ "ENDBOUNDARY", "CONTENTTYPE_VALUE", "TSPECIAL", "WORD", "BODY",
+ "PREAMBLE", "POSTAMBLE", "'/'", "'\"'", "$accept", "message",
+ "multipart_message", "@1", "singlepart_message", "headers", "preamble",
+ "postamble", "mimeparts", "mimepart", "header", "mail_header",
+ "contenttype_header", "contentdisposition_header", "content_disposition",
+ "contentencoding_header", "mimeversion_header", "invalid_header", "any",
+ "mimetype", "contenttype_parameters", "content_disposition_parameters",
+ "contenttype_parameter", "content_disposition_parameter",
+ "contenttype_parameter_value", "end_headers", "boundary", "endboundary",
+ "body", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 47, 34
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 28, 29, 29, 31, 30, 32, 33, 33, 33,
+ 34, 34, 35, 35, 36, 36, 37, 38, 38, 38,
+ 38, 38, 38, 39, 39, 40, 40, 41, 41, 42,
+ 43, 44, 45, 46, 46, 47, 48, 48, 48, 49,
+ 49, 49, 50, 51, 52, 52, 52, 53, 54, 55,
+ 56
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 0, 6, 2, 2, 1, 1,
+ 1, 0, 1, 0, 2, 1, 3, 1, 1, 1,
+ 1, 1, 1, 4, 3, 4, 5, 4, 5, 1,
+ 4, 4, 2, 2, 1, 3, 3, 2, 1, 3,
+ 2, 1, 3, 3, 1, 1, 3, 1, 2, 1,
+ 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 34, 47, 0, 0, 0, 0, 0, 0, 2,
+ 3, 11, 9, 17, 18, 19, 20, 21, 22, 0,
+ 8, 0, 0, 0, 0, 0, 1, 50, 10, 4,
+ 6, 7, 33, 32, 0, 29, 0, 0, 0, 0,
+ 24, 0, 0, 31, 27, 41, 0, 30, 0, 25,
+ 38, 0, 23, 0, 0, 15, 0, 0, 40, 28,
+ 35, 0, 37, 26, 48, 49, 14, 13, 0, 0,
+ 39, 0, 36, 12, 5, 16, 45, 44, 0, 43,
+ 42, 0, 46
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 8, 9, 42, 10, 11, 29, 74, 54, 55,
+ 12, 13, 14, 15, 36, 16, 17, 18, 19, 39,
+ 51, 46, 62, 58, 79, 20, 56, 67, 30
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -20
+static const yytype_int8 yypact[] =
+{
+ 3, -20, -20, 17, 21, 22, 23, 24, 5, -20,
+ -20, -11, 3, -20, -20, -20, -20, -20, -20, 1,
+ -20, 7, 8, 9, 10, -7, -20, -20, -20, -20,
+ -20, -20, -20, -20, 25, -20, -1, 26, 11, 12,
+ -20, 27, 18, -20, -20, 16, 31, -20, 19, -20,
+ 20, 32, -20, 35, 4, -20, 3, 36, 33, -20,
+ -20, 37, 38, -20, -20, -20, -20, 28, 29, -19,
+ -20, -19, -20, -20, -20, -20, -20, -20, 30, -20,
+ -20, 34, -20
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -20, -20, -20, -20, -20, -12, -20, -20, -20, -6,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -13, -4, -20, -20, -16, -20, -20, -20, -10
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 31, 40, 76, 77, 32, 26, 1, 44, 78, 33,
+ 2, 45, 27, 28, 3, 41, 4, 5, 6, 7,
+ 49, 21, 53, 65, 50, 22, 23, 24, 25, 34,
+ 35, 37, 38, 43, 47, 52, 53, 48, 57, 59,
+ 63, 60, 61, 64, 68, 45, 69, 71, 66, 72,
+ 50, 81, 27, 73, 70, 80, 0, 0, 75, 0,
+ 0, 82
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 12, 8, 21, 22, 3, 0, 3, 8, 27, 8,
+ 7, 12, 23, 24, 11, 22, 13, 14, 15, 16,
+ 8, 4, 18, 19, 12, 4, 4, 4, 4, 22,
+ 22, 22, 22, 8, 8, 8, 18, 26, 22, 8,
+ 8, 22, 22, 8, 56, 12, 10, 10, 54, 62,
+ 12, 21, 23, 25, 58, 71, -1, -1, 68, -1,
+ -1, 27
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 7, 11, 13, 14, 15, 16, 29, 30,
+ 32, 33, 38, 39, 40, 41, 43, 44, 45, 46,
+ 53, 4, 4, 4, 4, 4, 0, 23, 24, 34,
+ 56, 33, 3, 8, 22, 22, 42, 22, 22, 47,
+ 8, 22, 31, 8, 8, 12, 49, 8, 26, 8,
+ 12, 48, 8, 18, 36, 37, 54, 22, 51, 8,
+ 22, 22, 50, 8, 8, 19, 37, 55, 33, 10,
+ 49, 10, 48, 25, 35, 56, 21, 22, 27, 52,
+ 52, 21, 27
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (pstate, yyscanner, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if defined(YYLTYPE_IS_TRIVIAL)
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, yyscanner)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, pstate, yyscanner); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, pstate, yyscanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (pstate);
+ YYUSE (yyscanner);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, pstate, yyscanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, pstate, yyscanner);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule, pstate, yyscanner)
+ YYSTYPE *yyvsp;
+ int yyrule;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , pstate, yyscanner);
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule, pstate, yyscanner); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, pstate, yyscanner)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (pstate);
+ YYUSE (yyscanner);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (struct parser_state *pstate, void *yyscanner);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (struct parser_state *pstate, void *yyscanner)
+#else
+int
+yyparse (pstate, yyscanner)
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+#endif
+{
+ /* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+#line 119 "mimeparser.y"
+ {
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ pstate->current_mimepart = mm_mimepart_new();
+ pstate->have_contenttype = 0;
+ ;}
+ break;
+
+ case 5:
+#line 125 "mimeparser.y"
+ {
+ dprintf2(pstate,"This was a multipart message\n");
+ ;}
+ break;
+
+ case 6:
+#line 132 "mimeparser.y"
+ {
+ dprintf2(pstate,"This was a single part message\n");
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ ;}
+ break;
+
+ case 8:
+#line 142 "mimeparser.y"
+ {
+ /* If we did not find a Content-Type header for the current
+ * MIME part (or envelope), we create one and attach it.
+ * According to the RFC, a type of "text/plain" and a
+ * charset of "us-ascii" can be assumed.
+ */
+ struct mm_content *ct;
+ struct mm_param *param;
+
+ if (!pstate->have_contenttype) {
+ ct = mm_content_new();
+ mm_content_settype(ct, "text/plain");
+
+ param = mm_param_new();
+ param->name = xstrdup("charset");
+ param->value = xstrdup("us-ascii");
+
+ mm_content_attachtypeparam(ct, param);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, ct);
+ }
+ pstate->have_contenttype = 0;
+ ;}
+ break;
+
+ case 10:
+#line 170 "mimeparser.y"
+ {
+ char *preamble;
+ size_t offset;
+
+ if ((yyvsp[(1) - (1)].position).start != (yyvsp[(1) - (1)].position).end) {
+ preamble = PARSE_readmessagepart(0, (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end,
+ &offset,yyscanner,pstate);
+ if (preamble == NULL) {
+ return(-1);
+ }
+ pstate->ctx->preamble = preamble;
+ dprintf2(pstate,"PREAMBLE:\n%s\n", preamble);
+ }
+ ;}
+ break;
+
+ case 12:
+#line 189 "mimeparser.y"
+ {
+ ;}
+ break;
+
+ case 16:
+#line 202 "mimeparser.y"
+ {
+
+ if (mm_context_attachpart(pstate->ctx, pstate->current_mimepart) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(-1);
+ }
+
+ pstate->temppart = mm_mimepart_new();
+ pstate->current_mimepart = pstate->temppart;
+ pstate->mime_parts++;
+ ;}
+ break;
+
+ case 18:
+#line 219 "mimeparser.y"
+ {
+ pstate->have_contenttype = 1;
+ if (mm_content_iscomposite(pstate->envelope->type)) {
+ pstate->ctx->messagetype = MM_MSGTYPE_MULTIPART;
+ } else {
+ pstate->ctx->messagetype = MM_MSGTYPE_FLAT;
+ }
+ ;}
+ break;
+
+ case 22:
+#line 235 "mimeparser.y"
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ ;}
+ break;
+
+ case 23:
+#line 249 "mimeparser.y"
+ {
+ struct mm_mimeheader *hdr;
+ hdr = mm_mimeheader_generate((yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].string));
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ ;}
+ break;
+
+ case 24:
+#line 256 "mimeparser.y"
+ {
+ struct mm_mimeheader *hdr;
+
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+
+ hdr = mm_mimeheader_generate((yyvsp[(1) - (3)].string), xstrdup(""));
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ ;}
+ break;
+
+ case 25:
+#line 275 "mimeparser.y"
+ {
+ mm_content_settype(pstate->ctype, "%s", (yyvsp[(3) - (4)].string));
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type -> %s\n", (yyvsp[(3) - (4)].string));
+ pstate->ctype = mm_content_new();
+ ;}
+ break;
+
+ case 26:
+#line 283 "mimeparser.y"
+ {
+ mm_content_settype(pstate->ctype, "%s", (yyvsp[(3) - (5)].string));
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type (P) -> %s\n", (yyvsp[(3) - (5)].string));
+ pstate->ctype = mm_content_new();
+ ;}
+ break;
+
+ case 27:
+#line 293 "mimeparser.y"
+ {
+ dprintf2(pstate,"Content-Disposition -> %s\n", (yyvsp[(3) - (4)].string));
+ pstate->ctype->disposition_type = xstrdup((yyvsp[(3) - (4)].string));
+ ;}
+ break;
+
+ case 28:
+#line 299 "mimeparser.y"
+ {
+ dprintf2(pstate,"Content-Disposition (P) -> %s; params\n", (yyvsp[(3) - (5)].string));
+ pstate->ctype->disposition_type = xstrdup((yyvsp[(3) - (5)].string));
+ ;}
+ break;
+
+ case 29:
+#line 307 "mimeparser.y"
+ {
+ /*
+ * According to RFC 2183, the content disposition value may
+ * only be "inline", "attachment" or an extension token. We
+ * catch invalid values here if we are not in loose parsing
+ * mode.
+ */
+ if (strcasecmp((yyvsp[(1) - (1)].string), "inline") && strcasecmp((yyvsp[(1) - (1)].string), "attachment")
+ && strncasecmp((yyvsp[(1) - (1)].string), "X-", 2)) {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid content-disposition");
+ return(-1);
+ }
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ (yyval.string) = (yyvsp[(1) - (1)].string);
+ ;}
+ break;
+
+ case 30:
+#line 330 "mimeparser.y"
+ {
+ dprintf2(pstate,"Content-Transfer-Encoding -> %s\n", (yyvsp[(3) - (4)].string));
+ ;}
+ break;
+
+ case 31:
+#line 337 "mimeparser.y"
+ {
+ dprintf2(pstate,"MIME-Version -> '%s'\n", (yyvsp[(3) - (4)].string));
+ ;}
+ break;
+
+ case 35:
+#line 354 "mimeparser.y"
+ {
+ char type[255];
+ snprintf(type, sizeof(type), "%s/%s", (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].string));
+ (yyval.string) = type;
+ ;}
+ break;
+
+ case 38:
+#line 367 "mimeparser.y"
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Type header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ ;}
+ break;
+
+ case 41:
+#line 385 "mimeparser.y"
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Disposition header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ ;}
+ break;
+
+ case 42:
+#line 399 "mimeparser.y"
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ dprintf2(pstate,"Param: '%s', Value: '%s'\n", (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].string));
+
+ /* Catch an eventual boundary identifier */
+ if (!strcasecmp((yyvsp[(1) - (3)].string), "boundary")) {
+ if (pstate->lstate.boundary_string == NULL) {
+ set_boundary((yyvsp[(3) - (3)].string),pstate);
+ } else {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("duplicate boundary "
+ "found");
+ return -1;
+ } else {
+ /* TODO: attach MM_WARNING_DUPPARAM */
+ }
+ }
+ }
+
+ param->name = xstrdup((yyvsp[(1) - (3)].string));
+ param->value = xstrdup((yyvsp[(3) - (3)].string));
+
+ mm_content_attachtypeparam(pstate->ctype, param);
+ ;}
+ break;
+
+ case 43:
+#line 430 "mimeparser.y"
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ param->name = xstrdup((yyvsp[(1) - (3)].string));
+ param->value = xstrdup((yyvsp[(3) - (3)].string));
+
+ mm_content_attachdispositionparam(pstate->ctype, param);
+
+ ;}
+ break;
+
+ case 44:
+#line 444 "mimeparser.y"
+ {
+ dprintf2(pstate,"contenttype_param_val: WORD=%s\n", (yyvsp[(1) - (1)].string));
+ (yyval.string) = (yyvsp[(1) - (1)].string);
+ ;}
+ break;
+
+ case 45:
+#line 450 "mimeparser.y"
+ {
+ dprintf2(pstate,"contenttype_param_val: TSPECIAL\n");
+ /* For broken MIME implementation */
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("tspecial without quotes");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVAL */
+ }
+ (yyval.string) = (yyvsp[(1) - (1)].string);
+ ;}
+ break;
+
+ case 46:
+#line 465 "mimeparser.y"
+ {
+ dprintf2(pstate,"contenttype_param_val: \"TSPECIAL\"\n" );
+ (yyval.string) = (yyvsp[(2) - (3)].string);
+ ;}
+ break;
+
+ case 47:
+#line 473 "mimeparser.y"
+ {
+ dprintf2(pstate,"End of headers at line %d\n", pstate->lstate.lineno);
+ ;}
+ break;
+
+ case 48:
+#line 480 "mimeparser.y"
+ {
+ if (pstate->lstate.boundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.boundary_string, (yyvsp[(1) - (2)].string))) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid boundary: '%s' (%d)", (yyvsp[(1) - (2)].string), strlen((yyvsp[(1) - (2)].string)));
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"New MIME part... (%s)\n", (yyvsp[(1) - (2)].string));
+ ;}
+ break;
+
+ case 49:
+#line 499 "mimeparser.y"
+ {
+ if (pstate->lstate.endboundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.endboundary_string, (yyvsp[(1) - (1)].string))) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid end boundary: %s", (yyvsp[(1) - (1)].string));
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"End of MIME message\n");
+ ;}
+ break;
+
+ case 50:
+#line 518 "mimeparser.y"
+ {
+ char *body;
+ size_t offset;
+
+ dprintf2(pstate,"BODY (%d/%d), SIZE %d\n", (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end, (yyvsp[(1) - (1)].position).end - (yyvsp[(1) - (1)].position).start);
+
+ body = PARSE_readmessagepart((yyvsp[(1) - (1)].position).opaque_start, (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end,
+ &offset,yyscanner,pstate);
+
+ if (body == NULL) {
+ return(-1);
+ }
+ pstate->current_mimepart->opaque_body = body;
+ pstate->current_mimepart->body = body + offset;
+ pstate->current_mimepart->opaque_length = (yyvsp[(1) - (1)].position).end - (yyvsp[(1) - (1)].position).start - 2 + offset;
+ pstate->current_mimepart->length = pstate->current_mimepart->opaque_length - offset;
+ ;}
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 1913 "mimeparser.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (pstate, yyscanner, YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (pstate, yyscanner, yymsg);
+ }
+ else
+ {
+ yyerror (pstate, yyscanner, YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, pstate, yyscanner);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, pstate, yyscanner);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (pstate, yyscanner, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, pstate, yyscanner);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, pstate, yyscanner);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 537 "mimeparser.y"
+
+
+/*
+ * This function gets the specified part from the currently parsed message.
+ */
+static char *
+PARSE_readmessagepart(size_t opaque_start, size_t real_start, size_t end,
+ size_t *offset, yyscan_t yyscanner, struct parser_state *pstate)
+{
+ size_t body_size;
+ size_t current;
+ size_t start;
+ char *body;
+
+ /* calculate start and offset markers for the opaque and
+ * header stripped body message.
+ */
+ if (opaque_start > 0) {
+ /* Multipart message */
+ if (real_start) {
+ if (real_start < opaque_start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency (S:%d/O:%d)",
+ real_start,
+ opaque_start);
+ return(NULL);
+ }
+ start = opaque_start;
+ *offset = real_start - start;
+ /* Flat message */
+ } else {
+ start = opaque_start;
+ *offset = 0;
+ }
+ } else {
+ start = real_start;
+ *offset = 0;
+ }
+
+ /* The next three cases should NOT happen anytime */
+ if (end <= start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,2");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < *offset) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency, S:%d,O:%d,L:%d", start, offset, pstate->lstate.lineno);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < 0 || end < 0) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,4");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ /* XXX: do we want to enforce a maximum body size? make it a
+ * parser option? */
+
+ /* Read in the body message */
+ body_size = end - start;
+
+ if (body_size < 1) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("size of body cannot be < 1");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ body = (char *)malloc(body_size + 1);
+ if (body == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(NULL);
+ }
+
+ /* Get the message body either from a stream or a memory
+ * buffer.
+ */
+ if (mimeparser_yyget_in(yyscanner) != NULL) {
+ FILE *x = mimeparser_yyget_in(yyscanner);
+ current = ftell(x);
+ fseek(x, start - 1, SEEK_SET);
+ fread(body, body_size - 1, 1, x);
+ fseek(x, current, SEEK_SET);
+ } else if (pstate->lstate.message_buffer != NULL) {
+ strlcpy(body, pstate->lstate.message_buffer + start - 1, body_size);
+ }
+
+ return(body);
+
+}
+
+int
+yyerror(struct parser_state *pstate, void *yyscanner, const char *str)
+{
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("%s", str);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return -1;
+}
+
+int
+mimeparser_yywrap(void)
+{
+ return 1;
+}
+
+/**
+ * Sets the boundary value for the current message
+ */
+int
+set_boundary(char *str, struct parser_state *pstate)
+{
+ size_t blen;
+
+ blen = strlen(str);
+
+ pstate->lstate.boundary_string = (char *)malloc(blen + 3);
+ pstate->lstate.endboundary_string = (char *)malloc(blen + 5);
+
+ if (pstate->lstate.boundary_string == NULL || pstate->lstate.endboundary_string == NULL) {
+ if (pstate->lstate.boundary_string != NULL) {
+ free(pstate->lstate.boundary_string);
+ }
+ if (pstate->lstate.endboundary_string != NULL) {
+ free(pstate->lstate.endboundary_string);
+ }
+ return -1;
+ }
+
+ pstate->ctx->boundary = xstrdup(str);
+
+ snprintf(pstate->lstate.boundary_string, blen + 3, "--%s", str);
+ snprintf(pstate->lstate.endboundary_string, blen + 5, "--%s--", str);
+
+ return 0;
+}
+
+/**
+ * Debug printf()
+ */
+int
+dprintf2(struct parser_state *pstate, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+ if (pstate->debug == 0) return 1;
+
+ va_start(ap, fmt);
+ vasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s", msg);
+ free(msg);
+
+ return 0;
+
+}
+
+void reset_environ(struct parser_state *pstate)
+{
+ pstate->lstate.lineno = 0;
+ pstate->lstate.boundary_string = NULL;
+ pstate->lstate.endboundary_string = NULL;
+ pstate->lstate.message_buffer = NULL;
+ pstate->mime_parts = 0;
+ pstate->debug = 0;
+ pstate->envelope = NULL;
+ pstate->temppart = NULL;
+ pstate->ctype = NULL;
+ pstate->current_mimepart = NULL;
+
+ pstate->have_contenttype = 0;
+}
+/**
+ * Initializes the parser engine.
+ */
+int
+PARSER_initialize(struct parser_state *pstate, void *yyscanner)
+{
+ void reset_lexer_state(void *yyscanner, struct parser_state *);
+#if 0
+ if (pstate->ctx != NULL) {
+ xfree(pstate->ctx);
+ pstate->ctx = NULL;
+ }
+ if (pstate->envelope != NULL) {
+ xfree(pstate->envelope);
+ pstate->envelope = NULL;
+ }
+ if (pstate->ctype != NULL) {
+ xfree(pstate->ctype);
+ pstate->ctype = NULL;
+ }
+#endif
+ /* yydebug = 1; */
+ reset_environ(pstate);
+ reset_lexer_state(yyscanner,pstate);
+
+ pstate->envelope = mm_mimepart_new();
+ pstate->current_mimepart = pstate->envelope;
+ pstate->ctype = mm_content_new();
+
+ pstate->have_contenttype = 0;
+
+ return 1;
+}
+
+
+
diff --git a/trunk/main/minimime/mimeparser.tab.h b/trunk/main/minimime/mimeparser.tab.h
new file mode 100644
index 000000000..c19731fd2
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.tab.h
@@ -0,0 +1,112 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ 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, 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ ANY = 258,
+ COLON = 259,
+ DASH = 260,
+ DQUOTE = 261,
+ ENDOFHEADERS = 262,
+ EOL = 263,
+ EOM = 264,
+ EQUAL = 265,
+ MIMEVERSION_HEADER = 266,
+ SEMICOLON = 267,
+ CONTENTDISPOSITION_HEADER = 268,
+ CONTENTENCODING_HEADER = 269,
+ CONTENTTYPE_HEADER = 270,
+ MAIL_HEADER = 271,
+ HEADERVALUE = 272,
+ BOUNDARY = 273,
+ ENDBOUNDARY = 274,
+ CONTENTTYPE_VALUE = 275,
+ TSPECIAL = 276,
+ WORD = 277,
+ BODY = 278,
+ PREAMBLE = 279,
+ POSTAMBLE = 280
+ };
+#endif
+/* Tokens. */
+#define ANY 258
+#define COLON 259
+#define DASH 260
+#define DQUOTE 261
+#define ENDOFHEADERS 262
+#define EOL 263
+#define EOM 264
+#define EQUAL 265
+#define MIMEVERSION_HEADER 266
+#define SEMICOLON 267
+#define CONTENTDISPOSITION_HEADER 268
+#define CONTENTENCODING_HEADER 269
+#define CONTENTTYPE_HEADER 270
+#define MAIL_HEADER 271
+#define HEADERVALUE 272
+#define BOUNDARY 273
+#define ENDBOUNDARY 274
+#define CONTENTTYPE_VALUE 275
+#define TSPECIAL 276
+#define WORD 277
+#define BODY 278
+#define PREAMBLE 279
+#define POSTAMBLE 280
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 67 "mimeparser.y"
+{
+ int number;
+ char *string;
+ struct s_position position;
+}
+/* Line 1489 of yacc.c. */
+#line 105 "mimeparser.tab.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
diff --git a/trunk/main/minimime/mimeparser.y b/trunk/main/minimime/mimeparser.y
new file mode 100644
index 000000000..7c32a290d
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.y
@@ -0,0 +1,748 @@
+%{
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * These are the grammatic definitions in yacc syntax to parse MIME conform
+ * messages.
+ *
+ * TODO:
+ * - honour parse flags passed to us (partly done)
+ * - parse Content-Disposition header (partly done)
+ * - parse Content-Encoding header
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mm.h"
+#include "mm_internal.h"
+
+int set_boundary(char *,struct parser_state *);
+int mimeparser_yywrap(void);
+void reset_environ(struct parser_state *pstate);
+int PARSER_initialize(struct parser_state *pstate, void *yyscanner);
+
+static char *PARSE_readmessagepart(size_t, size_t, size_t, size_t *,yyscan_t, struct parser_state *);
+FILE *mimeparser_yyget_in (yyscan_t yyscanner );
+
+%}
+
+%pure-parser
+%parse-param {struct parser_state *pstate}
+%parse-param {void *yyscanner}
+%lex-param {void *yyscanner}
+
+%union
+{
+ int number;
+ char *string;
+ struct s_position position;
+}
+
+%token ANY
+%token COLON
+%token DASH
+%token DQUOTE
+%token ENDOFHEADERS
+%token EOL
+%token EOM
+%token EQUAL
+%token MIMEVERSION_HEADER
+%token SEMICOLON
+
+%token <string> CONTENTDISPOSITION_HEADER
+%token <string> CONTENTENCODING_HEADER
+%token <string> CONTENTTYPE_HEADER
+%token <string> MAIL_HEADER
+%token <string> HEADERVALUE
+%token <string> BOUNDARY
+%token <string> ENDBOUNDARY
+%token <string> CONTENTTYPE_VALUE
+%token <string> TSPECIAL
+%token <string> WORD
+
+%token <position> BODY
+%token <position> PREAMBLE
+%token <position> POSTAMBLE
+
+%type <string> content_disposition
+%type <string> contenttype_parameter_value
+%type <string> mimetype
+%type <string> body
+
+%start message
+
+%%
+
+/* This is a parser for a MIME-conform message, which is in either single
+ * part or multi part format.
+ */
+message :
+ multipart_message
+ |
+ singlepart_message
+ ;
+
+multipart_message:
+ headers preamble
+ {
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ pstate->current_mimepart = mm_mimepart_new();
+ pstate->have_contenttype = 0;
+ }
+ mimeparts endboundary postamble
+ {
+ dprintf2(pstate,"This was a multipart message\n");
+ }
+ ;
+
+singlepart_message:
+ headers body
+ {
+ dprintf2(pstate,"This was a single part message\n");
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ }
+ ;
+
+headers :
+ header headers
+ |
+ end_headers
+ {
+ /* If we did not find a Content-Type header for the current
+ * MIME part (or envelope), we create one and attach it.
+ * According to the RFC, a type of "text/plain" and a
+ * charset of "us-ascii" can be assumed.
+ */
+ struct mm_content *ct;
+ struct mm_param *param;
+
+ if (!pstate->have_contenttype) {
+ ct = mm_content_new();
+ mm_content_settype(ct, "text/plain");
+
+ param = mm_param_new();
+ param->name = xstrdup("charset");
+ param->value = xstrdup("us-ascii");
+
+ mm_content_attachtypeparam(ct, param);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, ct);
+ }
+ pstate->have_contenttype = 0;
+ }
+ |
+ header
+ ;
+
+preamble:
+ PREAMBLE
+ {
+ char *preamble;
+ size_t offset;
+
+ if ($1.start != $1.end) {
+ preamble = PARSE_readmessagepart(0, $1.start, $1.end,
+ &offset,yyscanner,pstate);
+ if (preamble == NULL) {
+ return(-1);
+ }
+ pstate->ctx->preamble = preamble;
+ dprintf2(pstate,"PREAMBLE:\n%s\n", preamble);
+ }
+ }
+ |
+ ;
+
+postamble:
+ POSTAMBLE
+ {
+ }
+ |
+ ;
+
+mimeparts:
+ mimeparts mimepart
+ |
+ mimepart
+ ;
+
+mimepart:
+ boundary headers body
+ {
+
+ if (mm_context_attachpart(pstate->ctx, pstate->current_mimepart) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(-1);
+ }
+
+ pstate->temppart = mm_mimepart_new();
+ pstate->current_mimepart = pstate->temppart;
+ pstate->mime_parts++;
+ }
+ ;
+
+header :
+ mail_header
+ |
+ contenttype_header
+ {
+ pstate->have_contenttype = 1;
+ if (mm_content_iscomposite(pstate->envelope->type)) {
+ pstate->ctx->messagetype = MM_MSGTYPE_MULTIPART;
+ } else {
+ pstate->ctx->messagetype = MM_MSGTYPE_FLAT;
+ }
+ }
+ |
+ contentdisposition_header
+ |
+ contentencoding_header
+ |
+ mimeversion_header
+ |
+ invalid_header
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ }
+ ;
+
+mail_header:
+ MAIL_HEADER COLON WORD EOL
+ {
+ struct mm_mimeheader *hdr;
+ hdr = mm_mimeheader_generate($1, $3);
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ }
+ |
+ MAIL_HEADER COLON EOL
+ {
+ struct mm_mimeheader *hdr;
+
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+
+ hdr = mm_mimeheader_generate($1, xstrdup(""));
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ }
+ ;
+
+contenttype_header:
+ CONTENTTYPE_HEADER COLON mimetype EOL
+ {
+ mm_content_settype(pstate->ctype, "%s", $3);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type -> %s\n", $3);
+ pstate->ctype = mm_content_new();
+ }
+ |
+ CONTENTTYPE_HEADER COLON mimetype contenttype_parameters EOL
+ {
+ mm_content_settype(pstate->ctype, "%s", $3);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type (P) -> %s\n", $3);
+ pstate->ctype = mm_content_new();
+ }
+ ;
+
+contentdisposition_header:
+ CONTENTDISPOSITION_HEADER COLON content_disposition EOL
+ {
+ dprintf2(pstate,"Content-Disposition -> %s\n", $3);
+ pstate->ctype->disposition_type = xstrdup($3);
+ }
+ |
+ CONTENTDISPOSITION_HEADER COLON content_disposition content_disposition_parameters EOL
+ {
+ dprintf2(pstate,"Content-Disposition (P) -> %s; params\n", $3);
+ pstate->ctype->disposition_type = xstrdup($3);
+ }
+ ;
+
+content_disposition:
+ WORD
+ {
+ /*
+ * According to RFC 2183, the content disposition value may
+ * only be "inline", "attachment" or an extension token. We
+ * catch invalid values here if we are not in loose parsing
+ * mode.
+ */
+ if (strcasecmp($1, "inline") && strcasecmp($1, "attachment")
+ && strncasecmp($1, "X-", 2)) {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid content-disposition");
+ return(-1);
+ }
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ $$ = $1;
+ }
+ ;
+
+contentencoding_header:
+ CONTENTENCODING_HEADER COLON WORD EOL
+ {
+ dprintf2(pstate,"Content-Transfer-Encoding -> %s\n", $3);
+ }
+ ;
+
+mimeversion_header:
+ MIMEVERSION_HEADER COLON WORD EOL
+ {
+ dprintf2(pstate,"MIME-Version -> '%s'\n", $3);
+ }
+ ;
+
+invalid_header:
+ any EOL
+ ;
+
+any:
+ any ANY
+ |
+ ANY
+ ;
+
+mimetype:
+ WORD '/' WORD
+ {
+ char type[255];
+ snprintf(type, sizeof(type), "%s/%s", $1, $3);
+ $$ = type;
+ }
+ ;
+
+contenttype_parameters:
+ SEMICOLON contenttype_parameter contenttype_parameters
+ |
+ SEMICOLON contenttype_parameter
+ |
+ SEMICOLON
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Type header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ }
+ ;
+
+content_disposition_parameters:
+ SEMICOLON content_disposition_parameter content_disposition_parameters
+ |
+ SEMICOLON content_disposition_parameter
+ |
+ SEMICOLON
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Disposition header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ }
+ ;
+
+contenttype_parameter:
+ WORD EQUAL contenttype_parameter_value
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ dprintf2(pstate,"Param: '%s', Value: '%s'\n", $1, $3);
+
+ /* Catch an eventual boundary identifier */
+ if (!strcasecmp($1, "boundary")) {
+ if (pstate->lstate.boundary_string == NULL) {
+ set_boundary($3,pstate);
+ } else {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("duplicate boundary "
+ "found");
+ return -1;
+ } else {
+ /* TODO: attach MM_WARNING_DUPPARAM */
+ }
+ }
+ }
+
+ param->name = xstrdup($1);
+ param->value = xstrdup($3);
+
+ mm_content_attachtypeparam(pstate->ctype, param);
+ }
+ ;
+
+content_disposition_parameter:
+ WORD EQUAL contenttype_parameter_value
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ param->name = xstrdup($1);
+ param->value = xstrdup($3);
+
+ mm_content_attachdispositionparam(pstate->ctype, param);
+
+ }
+ ;
+
+contenttype_parameter_value:
+ WORD
+ {
+ dprintf2(pstate,"contenttype_param_val: WORD=%s\n", $1);
+ $$ = $1;
+ }
+ |
+ TSPECIAL
+ {
+ dprintf2(pstate,"contenttype_param_val: TSPECIAL\n");
+ /* For broken MIME implementation */
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("tspecial without quotes");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVAL */
+ }
+ $$ = $1;
+ }
+ |
+ '"' TSPECIAL '"'
+ {
+ dprintf2(pstate,"contenttype_param_val: \"TSPECIAL\"\n" );
+ $$ = $2;
+ }
+ ;
+
+end_headers :
+ ENDOFHEADERS
+ {
+ dprintf2(pstate,"End of headers at line %d\n", pstate->lstate.lineno);
+ }
+ ;
+
+boundary :
+ BOUNDARY EOL
+ {
+ if (pstate->lstate.boundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.boundary_string, $1)) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid boundary: '%s' (%d)", $1, strlen($1));
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"New MIME part... (%s)\n", $1);
+ }
+ ;
+
+endboundary :
+ ENDBOUNDARY
+ {
+ if (pstate->lstate.endboundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.endboundary_string, $1)) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid end boundary: %s", $1);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"End of MIME message\n");
+ }
+ ;
+
+body:
+ BODY
+ {
+ char *body;
+ size_t offset;
+
+ dprintf2(pstate,"BODY (%d/%d), SIZE %d\n", $1.start, $1.end, $1.end - $1.start);
+
+ body = PARSE_readmessagepart($1.opaque_start, $1.start, $1.end,
+ &offset,yyscanner,pstate);
+
+ if (body == NULL) {
+ return(-1);
+ }
+ pstate->current_mimepart->opaque_body = body;
+ pstate->current_mimepart->body = body + offset;
+ pstate->current_mimepart->opaque_length = $1.end - $1.start - 2 + offset;
+ pstate->current_mimepart->length = pstate->current_mimepart->opaque_length - offset;
+ }
+ ;
+
+%%
+
+/*
+ * This function gets the specified part from the currently parsed message.
+ */
+static char *
+PARSE_readmessagepart(size_t opaque_start, size_t real_start, size_t end,
+ size_t *offset, yyscan_t yyscanner, struct parser_state *pstate)
+{
+ size_t body_size;
+ size_t current;
+ size_t start;
+ char *body;
+
+ /* calculate start and offset markers for the opaque and
+ * header stripped body message.
+ */
+ if (opaque_start > 0) {
+ /* Multipart message */
+ if (real_start) {
+ if (real_start < opaque_start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency (S:%d/O:%d)",
+ real_start,
+ opaque_start);
+ return(NULL);
+ }
+ start = opaque_start;
+ *offset = real_start - start;
+ /* Flat message */
+ } else {
+ start = opaque_start;
+ *offset = 0;
+ }
+ } else {
+ start = real_start;
+ *offset = 0;
+ }
+
+ /* The next three cases should NOT happen anytime */
+ if (end <= start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,2");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < *offset) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency, S:%d,O:%d,L:%d", start, offset, pstate->lstate.lineno);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < 0 || end < 0) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,4");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ /* XXX: do we want to enforce a maximum body size? make it a
+ * parser option? */
+
+ /* Read in the body message */
+ body_size = end - start;
+
+ if (body_size < 1) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("size of body cannot be < 1");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ body = (char *)malloc(body_size + 1);
+ if (body == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(NULL);
+ }
+
+ /* Get the message body either from a stream or a memory
+ * buffer.
+ */
+ if (mimeparser_yyget_in(yyscanner) != NULL) {
+ FILE *x = mimeparser_yyget_in(yyscanner);
+ current = ftell(x);
+ fseek(x, start - 1, SEEK_SET);
+ fread(body, body_size - 1, 1, x);
+ fseek(x, current, SEEK_SET);
+ } else if (pstate->lstate.message_buffer != NULL) {
+ strlcpy(body, pstate->lstate.message_buffer + start - 1, body_size);
+ }
+
+ return(body);
+
+}
+
+int
+yyerror(struct parser_state *pstate, void *yyscanner, const char *str)
+{
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("%s", str);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return -1;
+}
+
+int
+mimeparser_yywrap(void)
+{
+ return 1;
+}
+
+/**
+ * Sets the boundary value for the current message
+ */
+int
+set_boundary(char *str, struct parser_state *pstate)
+{
+ size_t blen;
+
+ blen = strlen(str);
+
+ pstate->lstate.boundary_string = (char *)malloc(blen + 3);
+ pstate->lstate.endboundary_string = (char *)malloc(blen + 5);
+
+ if (pstate->lstate.boundary_string == NULL || pstate->lstate.endboundary_string == NULL) {
+ if (pstate->lstate.boundary_string != NULL) {
+ free(pstate->lstate.boundary_string);
+ }
+ if (pstate->lstate.endboundary_string != NULL) {
+ free(pstate->lstate.endboundary_string);
+ }
+ return -1;
+ }
+
+ pstate->ctx->boundary = xstrdup(str);
+
+ snprintf(pstate->lstate.boundary_string, blen + 3, "--%s", str);
+ snprintf(pstate->lstate.endboundary_string, blen + 5, "--%s--", str);
+
+ return 0;
+}
+
+/**
+ * Debug printf()
+ */
+int
+dprintf2(struct parser_state *pstate, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+ if (pstate->debug == 0) return 1;
+
+ va_start(ap, fmt);
+ vasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s", msg);
+ free(msg);
+
+ return 0;
+
+}
+
+void reset_environ(struct parser_state *pstate)
+{
+ pstate->lstate.lineno = 0;
+ pstate->lstate.boundary_string = NULL;
+ pstate->lstate.endboundary_string = NULL;
+ pstate->lstate.message_buffer = NULL;
+ pstate->mime_parts = 0;
+ pstate->debug = 0;
+ pstate->envelope = NULL;
+ pstate->temppart = NULL;
+ pstate->ctype = NULL;
+ pstate->current_mimepart = NULL;
+
+ pstate->have_contenttype = 0;
+}
+/**
+ * Initializes the parser engine.
+ */
+int
+PARSER_initialize(struct parser_state *pstate, void *yyscanner)
+{
+ void reset_lexer_state(void *yyscanner, struct parser_state *);
+#if 0
+ if (pstate->ctx != NULL) {
+ xfree(pstate->ctx);
+ pstate->ctx = NULL;
+ }
+ if (pstate->envelope != NULL) {
+ xfree(pstate->envelope);
+ pstate->envelope = NULL;
+ }
+ if (pstate->ctype != NULL) {
+ xfree(pstate->ctype);
+ pstate->ctype = NULL;
+ }
+#endif
+ /* yydebug = 1; */
+ reset_environ(pstate);
+ reset_lexer_state(yyscanner,pstate);
+
+ pstate->envelope = mm_mimepart_new();
+ pstate->current_mimepart = pstate->envelope;
+ pstate->ctype = mm_content_new();
+
+ pstate->have_contenttype = 0;
+
+ return 1;
+}
+
+
diff --git a/trunk/main/minimime/mimeparser.yy.c b/trunk/main/minimime/mimeparser.yy.c
new file mode 100644
index 000000000..4cf454c7c
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.yy.c
@@ -0,0 +1,2627 @@
+#line 2 "mimeparser.yy.c"
+
+#line 4 "mimeparser.yy.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 33
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+int mimeparser_yylex_init (yyscan_t* scanner);
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE mimeparser_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+ * access to the local variable yy_act. Since yyless() is a macro, it would break
+ * existing scanners that call yyless() from OUTSIDE mimeparser_yylex.
+ * One obvious solution it to make yy_act a global. I tried that, and saw
+ * a 5% performance hit in a non-yylineno scanner, because yy_act is
+ * normally declared as a register variable-- so it is not worth it.
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+ int yyl;\
+ for ( yyl = n; yyl < yyleng; ++yyl )\
+ if ( yytext[yyl] == '\n' )\
+ --yylineno;\
+ }while(0)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via mimeparser_yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void mimeparser_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void mimeparser_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE mimeparser_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void mimeparser_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void mimeparser_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void mimeparser_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void mimeparser_yypop_buffer_state (yyscan_t yyscanner );
+
+static void mimeparser_yyensure_buffer_stack (yyscan_t yyscanner );
+static void mimeparser_yy_load_buffer_state (yyscan_t yyscanner );
+static void mimeparser_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER mimeparser_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE mimeparser_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE mimeparser_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE mimeparser_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *mimeparser_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *mimeparser_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void mimeparser_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer mimeparser_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ mimeparser_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ mimeparser_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 30
+#define YY_END_OF_BUFFER 31
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_acclist[179] =
+ { 0,
+ 31, 2, 29, 30, 28, 30, 2, 29, 30, 1,
+ 2, 29, 30, 3, 28, 30, 2, 29, 30, 29,
+ 30, 5, 28, 30, 29, 30, 4, 29, 30, 7,
+ 29, 30, 7, 13, 29, 30, 8, 28, 30, 7,
+ 29, 30, 7, 11, 29, 30, 7, 12, 29, 30,
+ 7, 12, 29, 30, 7, 9, 29, 30, 7, 10,
+ 29, 30, 8, 28, 30, 29, 30, 14, 29, 30,
+ 15, 29, 30, 29, 30, 20, 29, 30, 18, 28,
+ 30, 19, 29, 30, 20, 29, 30, 27, 29, 30,
+ 21, 28, 30, 27, 29, 30, 26, 29, 30, 26,
+
+ 29, 30, 26, 29, 30, 22, 29, 30, 24, 28,
+ 30, 29, 30, 23, 29, 30, 25, 28, 30, 29,
+ 30, 28, 1, 1, 3, 28, 5, 28, 7, 7,
+ 7, 13, 6, 8, 28, 7, 12, 7, 12, 7,
+ 8, 28, 14, 20, 18, 28, 20, 21, 28, 22,
+ 24, 28, 23, 25, 28, 6, 9, 17, 20, 17,
+ 7, 7, 6, 7, 7, 9, 17, 20, 7, 7,
+ 6, 7, 7, 9, 16, 17, 20, 7
+ } ;
+
+static yyconst flex_int16_t yy_accept[112] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 5, 7, 10, 14,
+ 17, 20, 22, 25, 27, 30, 33, 37, 40, 43,
+ 47, 51, 55, 59, 63, 66, 68, 71, 74, 76,
+ 79, 82, 85, 88, 91, 94, 97, 100, 103, 106,
+ 109, 112, 114, 117, 120, 122, 123, 124, 125, 127,
+ 129, 130, 130, 131, 133, 134, 136, 138, 140, 140,
+ 141, 143, 144, 145, 147, 148, 150, 150, 151, 153,
+ 154, 156, 156, 156, 157, 158, 158, 160, 161, 162,
+
+ 163, 165, 167, 169, 170, 171, 173, 175, 178, 179,
+ 179
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 5, 1, 6, 1, 1, 1, 1, 7, 7,
+ 7, 1, 7, 7, 8, 9, 10, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 12, 13, 7,
+ 14, 7, 7, 7, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 16, 1, 16, 1, 11, 1, 15, 15, 15, 15,
+
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 1, 17, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[18] =
+ { 0,
+ 1, 1, 2, 3, 4, 1, 4, 5, 4, 4,
+ 5, 4, 4, 4, 5, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[128] =
+ { 0,
+ 0, 2, 4, 15, 28, 38, 50, 0, 67, 0,
+ 6, 8, 10, 82, 12, 17, 19, 88, 21, 23,
+ 25, 30, 32, 34, 228, 253, 253, 224, 208, 253,
+ 219, 253, 253, 165, 253, 40, 95, 43, 43, 84,
+ 110, 119, 90, 98, 253, 155, 0, 253, 153, 0,
+ 253, 148, 131, 253, 253, 132, 253, 128, 116, 0,
+ 253, 119, 0, 253, 108, 253, 0, 80, 253, 253,
+ 100, 103, 103, 0, 118, 127, 0, 0, 131, 106,
+ 253, 0, 0, 253, 136, 253, 0, 0, 253, 0,
+ 253, 138, 139, 143, 144, 145, 153, 0, 0, 155,
+
+ 160, 161, 81, 112, 169, 162, 173, 41, 173, 253,
+ 178, 183, 188, 193, 198, 203, 208, 34, 213, 215,
+ 220, 225, 230, 235, 237, 242, 247
+ } ;
+
+static yyconst flex_int16_t yy_def[128] =
+ { 0,
+ 111, 111, 111, 111, 112, 112, 110, 7, 110, 9,
+ 112, 112, 113, 113, 114, 114, 115, 115, 116, 116,
+ 117, 117, 112, 112, 110, 110, 110, 110, 118, 110,
+ 110, 110, 110, 110, 110, 119, 119, 110, 119, 119,
+ 119, 41, 119, 119, 110, 110, 120, 110, 110, 121,
+ 110, 110, 121, 110, 110, 110, 110, 110, 110, 122,
+ 110, 110, 123, 110, 110, 110, 118, 118, 110, 110,
+ 119, 110, 119, 37, 110, 110, 41, 42, 110, 119,
+ 110, 120, 121, 110, 124, 110, 125, 122, 110, 123,
+ 110, 126, 110, 126, 126, 110, 124, 125, 127, 127,
+
+ 127, 127, 97, 127, 127, 105, 105, 97, 127, 0,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110
+ } ;
+
+static yyconst flex_int16_t yy_nxt[271] =
+ { 0,
+ 110, 110, 27, 28, 27, 28, 27, 28, 27, 49,
+ 27, 49, 51, 52, 55, 56, 29, 30, 31, 55,
+ 56, 55, 58, 61, 62, 61, 62, 64, 65, 29,
+ 33, 34, 64, 65, 27, 49, 27, 49, 67, 35,
+ 33, 34, 72, 73, 75, 76, 73, 75, 108, 35,
+ 36, 37, 38, 39, 37, 40, 41, 42, 42, 36,
+ 42, 36, 43, 44, 42, 41, 37, 32, 32, 45,
+ 46, 47, 48, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 32, 32, 51, 52, 72, 73, 108, 53,
+ 55, 58, 79, 80, 68, 59, 74, 72, 73, 74,
+
+ 72, 73, 72, 73, 92, 93, 73, 92, 96, 73,
+ 91, 74, 72, 73, 72, 109, 77, 77, 77, 75,
+ 77, 89, 75, 87, 77, 77, 78, 78, 94, 78,
+ 86, 94, 95, 78, 86, 95, 83, 83, 85, 100,
+ 92, 83, 100, 92, 101, 102, 95, 101, 102, 95,
+ 84, 83, 83, 83, 83, 66, 105, 81, 83, 105,
+ 103, 106, 107, 106, 106, 107, 106, 70, 83, 83,
+ 105, 72, 109, 105, 107, 93, 109, 107, 26, 26,
+ 26, 26, 26, 32, 32, 32, 32, 32, 50, 50,
+ 50, 50, 50, 54, 54, 54, 54, 54, 57, 57,
+
+ 57, 57, 57, 60, 60, 60, 60, 60, 63, 63,
+ 63, 63, 63, 71, 71, 71, 71, 71, 82, 82,
+ 83, 69, 68, 83, 83, 88, 66, 110, 88, 88,
+ 90, 110, 110, 90, 90, 97, 110, 110, 97, 97,
+ 98, 98, 99, 110, 99, 99, 99, 104, 110, 104,
+ 104, 104, 25, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110
+ } ;
+
+static yyconst flex_int16_t yy_chk[271] =
+ { 0,
+ 0, 0, 1, 1, 2, 2, 3, 3, 11, 11,
+ 12, 12, 13, 13, 15, 15, 2, 4, 4, 16,
+ 16, 17, 17, 19, 19, 20, 20, 21, 21, 4,
+ 5, 5, 22, 22, 23, 23, 24, 24, 118, 5,
+ 6, 6, 36, 36, 38, 39, 39, 38, 108, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 14, 14, 40, 40, 103, 14,
+ 18, 18, 43, 43, 68, 18, 37, 37, 37, 37,
+
+ 44, 44, 71, 71, 72, 73, 73, 72, 80, 80,
+ 65, 37, 41, 41, 104, 104, 41, 41, 41, 75,
+ 41, 62, 75, 59, 41, 41, 42, 42, 76, 42,
+ 58, 76, 79, 42, 56, 79, 85, 85, 53, 92,
+ 93, 85, 92, 93, 94, 95, 96, 94, 95, 96,
+ 52, 85, 85, 97, 97, 49, 100, 46, 97, 100,
+ 97, 101, 102, 106, 101, 102, 106, 34, 97, 97,
+ 105, 105, 105, 105, 107, 109, 109, 107, 111, 111,
+ 111, 111, 111, 112, 112, 112, 112, 112, 113, 113,
+ 113, 113, 113, 114, 114, 114, 114, 114, 115, 115,
+
+ 115, 115, 115, 116, 116, 116, 116, 116, 117, 117,
+ 117, 117, 117, 119, 119, 119, 119, 119, 120, 120,
+ 121, 31, 29, 121, 121, 122, 28, 25, 122, 122,
+ 123, 0, 0, 123, 123, 124, 0, 0, 124, 124,
+ 125, 125, 126, 0, 126, 126, 126, 127, 0, 127,
+ 127, 127, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110
+ } ;
+
+/* Table of booleans, true if rule could match eol. */
+static yyconst flex_int32_t yy_rule_can_match_eol[31] =
+ { 0,
+0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, };
+
+#define REJECT \
+{ \
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ \
+yy_cp = yyg->yy_full_match; /* restore poss. backed-over text */ \
+++yyg->yy_lp; \
+goto find_rule; \
+}
+
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "mimeparser.l"
+#line 2 "mimeparser.l"
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * This is a lexer file for parsing MIME compatible messages. It is intended
+ * to satisfy at least RFC 2045 (Format of Internet Message Bodies). It still
+ * has quite a few problems:
+ *
+ * - The parsing could probably be done in a more elegant way
+ * - I don't know what performance impact REJECT has on the parser
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mimeparser.tab.h"
+
+#define NAMEOF(v) #v
+/* BC() is a debug wrapper for lex' BEGIN() macro */
+#define BC(x) do { \
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); \
+ BEGIN(x); \
+ lstate->condition = x; \
+} while(0);
+
+#define ZERO(x) memset(x, '\0', sizeof(x))
+
+#define PREALLOC_BUFFER 100000
+#undef YY_BUF_SIZE
+#define YY_BUF_SIZE 65536
+
+enum header_states
+{
+ STATE_MAIL = 0,
+ STATE_CTYPE,
+ STATE_CDISP,
+ STATE_CENC,
+ STATE_MIME
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#line 658 "mimeparser.yy.c"
+
+#define INITIAL 0
+#define headers 1
+#define header 2
+#define headervalue 3
+#define tspecialvalue 4
+#define comment 5
+#define body 6
+#define postamble 7
+#define preamble 8
+#define boundary 9
+#define endboundary 10
+#define endoffile 11
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ yy_state_type *yy_state_buf;
+ yy_state_type *yy_state_ptr;
+ char *yy_full_match;
+ int yy_lp;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int mimeparser_yylex_destroy (yyscan_t yyscanner );
+
+int mimeparser_yyget_debug (yyscan_t yyscanner );
+
+void mimeparser_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE mimeparser_yyget_extra (yyscan_t yyscanner );
+
+void mimeparser_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *mimeparser_yyget_in (yyscan_t yyscanner );
+
+void mimeparser_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *mimeparser_yyget_out (yyscan_t yyscanner );
+
+void mimeparser_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int mimeparser_yyget_leng (yyscan_t yyscanner );
+
+char *mimeparser_yyget_text (yyscan_t yyscanner );
+
+int mimeparser_yyget_lineno (yyscan_t yyscanner );
+
+int mimeparser_yyget_column (yyscan_t yyscanner);
+
+void mimeparser_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+void mimeparser_yyset_column (int column_no , yyscan_t yyscanner);
+
+YYSTYPE * mimeparser_yyget_lval (yyscan_t yyscanner );
+
+void mimeparser_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int mimeparser_yywrap (yyscan_t yyscanner );
+#else
+extern int mimeparser_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int mimeparser_yylex \
+ (YYSTYPE * yylval_param ,yyscan_t yyscanner);
+
+#define YY_DECL int mimeparser_yylex \
+ (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ if ( yyleng > 0 ) \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \
+ (yytext[yyleng - 1] == '\n'); \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 94 "mimeparser.l"
+
+
+#line 909 "mimeparser.yy.c"
+
+ yylval = yylval_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ /* Create the reject buffer large enough to save one state per allowed character. */
+ if ( ! yyg->yy_state_buf )
+ yyg->yy_state_buf = (yy_state_type *)mimeparser_yyalloc(YY_STATE_BUF_SIZE ,yyscanner);
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ mimeparser_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ mimeparser_yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+ yy_current_state += YY_AT_BOL();
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 111 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 253 );
+
+yy_find_action:
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+find_rule: /* we branch to this label when backing up */
+ for ( ; ; ) /* until we find what rule we matched */
+ {
+ if ( yyg->yy_lp && yyg->yy_lp < yy_accept[yy_current_state + 1] )
+ {
+ yy_act = yy_acclist[yyg->yy_lp];
+ {
+ yyg->yy_full_match = yy_cp;
+ break;
+ }
+ }
+ --yy_cp;
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+ if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+ {
+ int yyl;
+ for ( yyl = 0; yyl < yyleng; ++yyl )
+ if ( yytext[yyl] == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+ }
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+case 1:
+YY_RULE_SETUP
+#line 96 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ BC(header);
+
+ /* Depending on what header we are processing, we enter a different
+ * state and return a different value.
+ */
+ if (!strcasecmp(yytext, "Content-Type")) {
+ lstate->header_state = STATE_CTYPE;
+ return CONTENTTYPE_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Transfer-Encoding")) {
+ lstate->header_state = STATE_CENC;
+ return CONTENTENCODING_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Disposition")) {
+ lstate->header_state = STATE_CDISP;
+ return CONTENTDISPOSITION_HEADER;
+ } else if (!strcasecmp(yytext, "MIME-Version")) {
+ lstate->header_state = STATE_MAIL;
+ return MIMEVERSION_HEADER;
+ } else {
+ lstate->header_state = STATE_MAIL;
+ return MAIL_HEADER;
+ }
+}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 124 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /* dprintf2("Unknown header char: %c\n", *yytext); */
+ lstate->current_pos += yyleng;
+ return ANY;
+}
+ YY_BREAK
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+#line 131 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno++;
+
+ lstate->current_pos += yyleng;
+
+ /* This marks the end of headers. Depending on whether we are in the
+ * envelope currently we need to parse either a body or the preamble
+ * now.
+ */
+ if (lstate->is_envelope == 0 || lstate->boundary_string == NULL) {
+ BC(body);
+ lstate->body_start = lstate->current_pos;
+ } else {
+ lstate->is_envelope = 0;
+ lstate->preamble_start = lstate->current_pos;
+ BC(preamble);
+ }
+
+ return ENDOFHEADERS;
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 153 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return COLON;
+}
+ YY_BREAK
+case 5:
+/* rule 5 can match eol */
+YY_RULE_SETUP
+#line 160 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headers);
+ /* dprintf2("Invalid header, returning EOL\n"); */
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+ YY_BREAK
+case 6:
+/* rule 6 can match eol */
+YY_RULE_SETUP
+#line 168 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+#line 173 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ if (lstate->header_state != STATE_MAIL && lstate->header_state != STATE_CENC) {
+ REJECT;
+ }
+ lstate->current_pos += yyleng;
+ while (*yytext && isspace(*yytext)) yytext++;
+ /* Do we actually have a header value? */
+ if (*yytext == '\0') {
+ yylval_param->string = strdup("");
+ } else {
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ }
+ return WORD;
+}
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+#line 190 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /* marks the end of one header line */
+ lstate->lineno++;
+ BC(headers);
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+ YY_BREAK
+case 9:
+/* rule 9 can match eol */
+YY_RULE_SETUP
+#line 199 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return SEMICOLON;
+}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 206 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return EQUAL;
+}
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 212 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(tspecialvalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 219 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return WORD;
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 227 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 232 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ return TSPECIAL;
+}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 240 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 247 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin and end with two dashes. If we have catched a valid
+ * end boundary, which actually ends a body, we save the current
+ * position, put the token back on the input stream and let the
+ * endboundary condition parse the actual token.
+ */
+ if (lstate->endboundary_string != NULL) {
+ if (strcmp(lstate->endboundary_string, yytext)) {
+ /* dprintf2("YYTEXT != end_boundary: '%s'\n", yytext); */
+ REJECT;
+ } else {
+ lstate->current_pos += yyleng;
+ /* dprintf2("YYTEXT == lstate->end_boundary: '%s'\n", yytext); */
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start =
+ lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos - yyleng;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(endboundary);
+ return BODY;
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 282 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin with two dashes.
+ */
+ if (lstate->boundary_string != NULL) {
+ if (strcmp(lstate->boundary_string, yytext)) {
+ /* dprintf2("YYTEXT != boundary: '%s'\n", yytext);*/
+ REJECT;
+ } else {
+ /* dprintf2("YYTEXT == boundary: '%s'\n", yytext);*/
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start = lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(boundary);
+ return BODY;
+ } else if (lstate->preamble_start) {
+ yylval_param->position.start = lstate->preamble_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->preamble_start = lstate->preamble_end = 0;
+ yyless(0);
+ BC(boundary);
+ return PREAMBLE;
+ } else {
+ BC(boundary);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return(BOUNDARY);
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+ YY_BREAK
+case 18:
+/* rule 18 can match eol */
+YY_RULE_SETUP
+#line 324 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ lstate->lineno++;
+}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 330 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ /* dprintf2("stray CR in body...\n"); */
+}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 336 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case YY_STATE_EOF(body):
+#line 341 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ if (lstate->boundary_string == NULL && lstate->body_start) {
+ yylval_param->position.opaque_start = 0;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_start = 0;
+ return BODY;
+ } else if (lstate->body_start) {
+ return POSTAMBLE;
+ }
+ yyterminate();
+}
+ YY_BREAK
+case 21:
+/* rule 21 can match eol */
+YY_RULE_SETUP
+#line 355 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /* dprintf2("Preamble CR/LF at line %d\n", lineno); */
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 362 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return BOUNDARY;
+}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 369 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return ENDBOUNDARY;
+}
+ YY_BREAK
+case 24:
+/* rule 24 can match eol */
+YY_RULE_SETUP
+#line 376 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headers);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ lstate->body_opaque_start = lstate->current_pos;
+ return EOL;
+}
+ YY_BREAK
+case 25:
+/* rule 25 can match eol */
+YY_RULE_SETUP
+#line 385 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(postamble);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 392 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 398 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+#line 403 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 410 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return((int)*yytext);
+}
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 417 "mimeparser.l"
+ECHO;
+ YY_BREAK
+#line 1438 "mimeparser.yy.c"
+ case YY_STATE_EOF(INITIAL):
+ case YY_STATE_EOF(headers):
+ case YY_STATE_EOF(header):
+ case YY_STATE_EOF(headervalue):
+ case YY_STATE_EOF(tspecialvalue):
+ case YY_STATE_EOF(comment):
+ case YY_STATE_EOF(postamble):
+ case YY_STATE_EOF(preamble):
+ case YY_STATE_EOF(boundary):
+ case YY_STATE_EOF(endboundary):
+ case YY_STATE_EOF(endoffile):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * mimeparser_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( mimeparser_yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of mimeparser_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ mimeparser_yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+ yy_current_state += YY_AT_BOL();
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 111 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+
+ register YY_CHAR yy_c = 1;
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 111 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 110);
+ if ( ! yy_is_jam )
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ if ( c == '\n' ){
+ --yylineno;
+ }
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ mimeparser_yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( mimeparser_yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n');
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void mimeparser_yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ mimeparser_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ mimeparser_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ mimeparser_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void mimeparser_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * mimeparser_yypop_buffer_state();
+ * mimeparser_yypush_buffer_state(new_buffer);
+ */
+ mimeparser_yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ mimeparser_yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (mimeparser_yywrap()) processing, but the only time this flag
+ * is looked at is after mimeparser_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void mimeparser_yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE mimeparser_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) mimeparser_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) mimeparser_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ mimeparser_yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with mimeparser_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void mimeparser_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ mimeparser_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ mimeparser_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a mimeparser_yyrestart() or at EOF.
+ */
+ static void mimeparser_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ mimeparser_yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then mimeparser_yy_init_buffer was _probably_
+ * called from mimeparser_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void mimeparser_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ mimeparser_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ mimeparser_yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from mimeparser_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from mimeparser_yy_switch_to_buffer. */
+ mimeparser_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ mimeparser_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ mimeparser_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void mimeparser_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)mimeparser_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)mimeparser_yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE mimeparser_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) mimeparser_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ mimeparser_yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to mimeparser_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * mimeparser_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE mimeparser_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return mimeparser_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to mimeparser_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE mimeparser_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) mimeparser_yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = mimeparser_yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in mimeparser_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE mimeparser_yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int mimeparser_yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int mimeparser_yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *mimeparser_yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *mimeparser_yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int mimeparser_yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *mimeparser_yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "mimeparser_yyset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "mimeparser_yyset_column called with no buffer" , yyscanner);
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see mimeparser_yy_switch_to_buffer
+ */
+void mimeparser_yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void mimeparser_yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int mimeparser_yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void mimeparser_yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * mimeparser_yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void mimeparser_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+/* User-visible API */
+
+/* mimeparser_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int mimeparser_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) mimeparser_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from mimeparser_yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+ yyg->yy_state_buf = 0;
+ yyg->yy_state_ptr = 0;
+ yyg->yy_full_match = 0;
+ yyg->yy_lp = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * mimeparser_yylex_init()
+ */
+ return 0;
+}
+
+/* mimeparser_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int mimeparser_yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ mimeparser_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ mimeparser_yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ mimeparser_yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ mimeparser_yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ mimeparser_yyfree ( yyg->yy_state_buf , yyscanner);
+ yyg->yy_state_buf = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * mimeparser_yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ mimeparser_yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *mimeparser_yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *mimeparser_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void mimeparser_yyfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see mimeparser_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 417 "mimeparser.l"
+
+
+
+void reset_lexer_state(void *yyscanner, struct parser_state *pstate)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ struct lexer_state *lstate = &(pstate->lstate);
+
+ mimeparser_yyset_extra((void*)lstate,yyscanner);
+ BEGIN(0);
+ lstate->header_state = STATE_MAIL;
+ lstate->lineno = 0;
+ lstate->current_pos = 1;
+ lstate->condition = 0;
+
+ lstate->is_envelope = 1;
+
+ lstate->message_len = 0;
+ lstate->buffer_length = 0;
+
+ /* temporary marker variables */
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ lstate->preamble_start = 0;
+ lstate->preamble_end = 0;
+ lstate->postamble_start = 0;
+ lstate->postamble_end = 0;
+}
+
+void
+PARSER_setbuffer(const char *string, yyscan_t scanner)
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(scanner);
+ lstate->message_buffer = string;
+ mimeparser_yy_scan_string(string,scanner);
+}
+
+void
+PARSER_setfp(FILE *fp, yyscan_t scanner)
+{
+ /* looks like a bug in bison 2.2a -- the wrong code is generated for mimeparser_yyset_in !! */
+ struct yyguts_t * yyg = (struct yyguts_t*) scanner;
+ yyg->yyin_r = fp;
+
+ if (0) {
+ /* This is just to make a compiler warning go away */
+ yyunput(0, NULL, scanner);
+ }
+}
+
+/**
+ * Counts how many lines a given string represents in the message (in case of
+ * folded header values, for example, or a message body).
+ */
+int
+count_lines(char *txt)
+{
+ char *o;
+ int line;
+
+ line = 0;
+
+ for (o = txt; *o != '\0'; o++)
+ if (*o == '\n')
+ line++;
+
+ return line;
+}
+
diff --git a/trunk/main/minimime/minimime.c b/trunk/main/minimime/minimime.c
new file mode 100644
index 000000000..1b324585c
--- /dev/null
+++ b/trunk/main/minimime/minimime.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * MiniMIME test program
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include "mm.h"
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "MiniMIME test suite\n"
+ "Usage: ./minimime [-m] <filename>\n\n"
+ " -m : use memory based scanning\n\n"
+ );
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ MM_CTX *ctx;
+ struct mm_mimeheader *header, *lastheader;
+ struct mm_warning *lastwarning;
+ struct mm_mimepart *part;
+ struct mm_content *ct;
+ int parts, i;
+ struct stat st;
+ int fd;
+ char *buf;
+ int scan_mode = 0;
+
+ lastheader = NULL;
+
+ while ((i = getopt(argc, argv, "m")) != -1) {
+ switch(i) {
+ case 'm':
+ scan_mode = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ }
+
+#ifdef __HAVE_LEAK_DETECTION
+ /* Initialize memory leak detection if compiled in */
+ MM_leakd_init();
+#endif
+
+ /* Initialize MiniMIME library */
+ mm_library_init();
+
+ /* Register all default codecs (base64/qp) */
+ mm_codec_registerdefaultcodecs();
+
+ do {
+ /* Create a new context */
+ ctx = mm_context_new();
+
+ /* Parse a file into our context */
+ if (scan_mode == 0) {
+ i = mm_parse_file(ctx, argv[0], MM_PARSE_LOOSE, 0);
+ } else {
+ if (stat(argv[0], &st) == -1) {
+ fprintf(stderr, "INFO: stat");
+ }
+
+ if ((fd = open(argv[0], O_RDONLY)) == -1) {
+ fdprintf(stderr, "INFO: open");
+ }
+
+ buf = (char *)malloc(st.st_size);
+ if (buf == NULL) {
+ fdprintf(stderr, "INFO: malloc");
+ }
+
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ fdprintf(stderr, "INFO: read");
+ }
+
+ close(fd);
+ buf[st.st_size] = '\0';
+
+ i = mm_parse_mem(ctx, buf, MM_PARSE_LOOSE, 0);
+ }
+
+ if (i == -1 || mm_errno != MM_ERROR_NONE) {
+ printf("ERROR: %s at line %d\n", mm_error_string(), mm_error_lineno());
+ exit(1);
+ }
+
+ /* Get the number of MIME parts */
+ parts = mm_context_countparts(ctx);
+ if (parts == 0) {
+ printf("ERROR: got zero MIME parts, huh\n");
+ exit(1);
+ } else {
+ if (mm_context_iscomposite(ctx)) {
+ printf("Got %d MIME parts\n", parts - 1);
+ } else {
+ printf("Flat message (not multipart)\n");
+ }
+ }
+
+ /* Get the main MIME part */
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ fprintf(stderr, "Could not get envelope part\n");
+ exit(1);
+ }
+
+ printf("Printing envelope headers:\n");
+ /* Print all headers */
+ if (mm_mimepart_headers_start(part, &lastheader) == -1) {
+ fprintf(stderr, "No headers in envelope\n");
+ exit(1);
+ }
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) {
+ printf("%s: %s\n", header->name, header->value);
+ }
+
+ printf("%s\n", mm_content_tostring(part->type));
+ printf("\n");
+
+ ct = part->type;
+ assert(ct != NULL);
+
+ if (mm_context_iscomposite(ctx) == 0) {
+ printf("Printing body part for FLAT message:\n");
+ part = mm_context_getpart(ctx, 0);
+ printf("%s", part->body);
+ }
+
+ /* Loop through all MIME parts beginning with 1 */
+ for (i = 1; i < mm_context_countparts(ctx); i++) {
+ char *decoded;
+
+ printf("Printing headers for MIME part %d\n", i);
+
+ /* Get the current MIME entity */
+ part = mm_context_getpart(ctx, i);
+ if (part == NULL) {
+ fprintf(stderr, "Should have %d parts but "
+ "couldn't retrieve part %d",
+ mm_context_countparts(ctx), i);
+ exit(1);
+ }
+
+ /* Print all headers */
+ if (mm_mimepart_headers_start(part, &lastheader) == -1) {
+ printf("Ups no headers\n");
+ }
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) {
+ printf("%s: %s\n", header->name, header->value);
+ }
+
+ printf("Part Type: %s\n", mm_content_tostring(part->type));
+
+ /* Print MIME part body */
+ printf("\nPrinting message BODY:\n%s\n", (char *)part->opaque_body);
+ decoded = mm_mimepart_decode(part);
+ if (decoded != NULL) {
+ printf("DECODED:\n%s\n", decoded);
+ free(decoded);
+ }
+ }
+
+ /* Print out all warnings that we might have received */
+ if (mm_context_haswarnings(ctx) > 0) {
+ lastwarning = NULL;
+ fprintf(stderr, "WARNINGS:\n");
+#if 0
+ while ((warning = mm_warning_next(ctx, &lastwarning))
+ != NULL) {
+ fprintf(stderr, " -> %s\n", warning->message);
+ }
+#endif
+ }
+
+ printf("ENVELOPE:\n");
+
+ do {
+ char *env;
+ size_t env_len;
+
+ mm_context_flatten(ctx, &env, &env_len, 0);
+ printf("%s", env);
+
+ } while (0);
+
+ mm_context_free(ctx);
+ ctx = NULL;
+
+#ifdef __HAVE_LEAK_DETECTION
+ MM_leakd_printallocated();
+#endif
+
+ } while (0);
+
+ return 0;
+}
diff --git a/trunk/main/minimime/mm.h b/trunk/main/minimime/mm.h
new file mode 100644
index 000000000..449cd8f72
--- /dev/null
+++ b/trunk/main/minimime/mm.h
@@ -0,0 +1,367 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MM_H_INCLUDED
+#define _MM_H_INCLUDED
+
+#include "asterisk.h"
+#include "mm_queue.h"
+#include "mm_mem.h"
+
+#define MM_MIME_LINELEN 998
+#define MM_BASE64_LINELEN 76
+
+TAILQ_HEAD(mm_mimeheaders, mm_mimeheader);
+TAILQ_HEAD(mm_mimeparts, mm_mimepart);
+TAILQ_HEAD(mm_params, mm_param);
+SLIST_HEAD(mm_codecs, mm_codec);
+SLIST_HEAD(mm_warnings, mm_warning);
+
+/*
+ * Parser modes
+ */
+enum mm_parsemodes
+{
+ /** Parse loosely, accept some MIME quirks */
+ MM_PARSE_LOOSE = 0,
+ /** Parse as strict as possible */
+ MM_PARSE_STRICT
+};
+
+/*
+ * Available parser flags
+ */
+enum mm_parseflags
+{
+ MM_PARSE_NONE = (1L << 0),
+ MM_PARSE_STRIPCOMMENTS = (1L << 1)
+};
+
+/*
+ * Enumeration of MIME encodings
+ */
+enum mm_encoding
+{
+ MM_ENCODING_NONE = 0,
+ MM_ENCODING_BASE64,
+ MM_ENCODING_QUOTEDPRINTABLE,
+ MM_ENCODING_UNKNOWN
+};
+
+/*
+ * Message type
+ */
+enum mm_messagetype
+{
+ /** Flat message */
+ MM_MSGTYPE_FLAT = 0,
+ /** Composite message */
+ MM_MSGTYPE_MULTIPART
+};
+
+/*
+ * Enumeration of error categories
+ */
+enum mm_errors
+{
+ MM_ERROR_NONE = 0,
+ MM_ERROR_UNDEF,
+ MM_ERROR_ERRNO,
+ MM_ERROR_PARSE,
+ MM_ERROR_MIME,
+ MM_ERROR_CODEC,
+ MM_ERROR_PROGRAM
+};
+
+enum mm_warning_ids
+{
+ MM_WARN_NONE = 0,
+ MM_WARN_PARSE,
+ MM_WARN_MIME,
+ MM_WARN_CODEC
+};
+
+enum mm_addressfields {
+ MM_ADDR_TO = 0,
+ MM_ADDR_CC,
+ MM_ADDR_BCC,
+ MM_ADDR_FROM,
+ MM_ADDR_SENDER,
+ MM_ADDR_REPLY_TO
+};
+
+enum mm_flatten_flags {
+ MM_FLATTEN_NONE = 0,
+ MM_FLATTEN_SKIPENVELOPE = (1L << 1),
+ MM_FLATTEN_OPAQUE = (1L << 2),
+ MM_FLATTEN_NOPREAMBLE = (1L << 3)
+};
+
+/*
+ * More information about an error
+ */
+struct mm_error_data
+{
+ int error_id;
+ int error_where;
+ int lineno;
+ char error_msg[128];
+};
+
+extern int mm_errno;
+extern struct mm_error_data mm_error;
+
+enum mm_warning_code
+{
+ MM_WARNING_NONE = 0,
+ MM_WARNING_INVHDR,
+};
+
+/*
+ * A parser warning
+ */
+struct mm_warning
+{
+ enum mm_warning_code warning;
+ uint32_t lineno;
+ SLIST_ENTRY(mm_warning) next;
+};
+
+/*
+ * Representation of a MiniMIME codec object
+ */
+struct mm_codec
+{
+ enum mm_encoding id;
+ char *encoding;
+
+ char *(*encoder)(char *, uint32_t);
+ char *(*decoder)(char *);
+
+ SLIST_ENTRY(mm_codec) next;
+};
+
+/*
+ * Representation of a MIME Content-Type parameter
+ */
+struct mm_param
+{
+ char *name;
+ char *value;
+
+ TAILQ_ENTRY(mm_param) next;
+};
+
+/*
+ * Representation of a mail or MIME header field
+ */
+struct mm_mimeheader
+{
+ char *name;
+ char *value;
+
+ struct mm_params params;
+
+ TAILQ_ENTRY(mm_mimeheader) next;
+};
+
+/*
+ * Representation of a MIME Content-Type object
+ */
+struct mm_content
+{
+ char *maintype;
+ char *subtype;
+ char *disposition_type;
+
+ struct mm_params type_params;
+ struct mm_params disposition_params;
+
+ char *encstring;
+ enum mm_encoding encoding;
+};
+
+/*
+ * Representation of a MIME part
+ */
+struct mm_mimepart
+{
+ struct mm_mimeheaders headers;
+
+ size_t opaque_length;
+ char *opaque_body;
+
+ size_t length;
+ char *body;
+
+ struct mm_content *type;
+
+ TAILQ_ENTRY(mm_mimepart) next;
+};
+
+/*
+ * Represantation of a MiniMIME context
+ */
+struct mm_context
+{
+ struct mm_mimeparts parts;
+ enum mm_messagetype messagetype;
+ struct mm_warnings warnings;
+ struct mm_codecs codecs;
+ char *boundary;
+ char *preamble;
+ size_t max_message_size;
+};
+
+typedef struct mm_context MM_CTX;
+typedef struct mm_context mm_ctx_t;
+
+char *mm_unquote(const char *);
+char *mm_uncomment(const char *);
+char *mm_stripchars(char *, char *);
+char *mm_addchars(char *, char *, uint16_t);
+int mm_gendate(char **);
+void mm_striptrailing(char **, const char *);
+int mm_mimeutil_genboundary(char *, size_t, char **);
+
+int mm_library_init(void);
+int mm_library_isinitialized(void);
+
+int mm_parse_mem(MM_CTX *, const char *, int, int);
+int mm_parse_file(MM_CTX *, const char *, int, int);
+int mm_parse_fileptr(MM_CTX *, FILE *, int, int);
+
+MM_CTX *mm_context_new(void);
+void mm_context_free(MM_CTX *);
+int mm_context_attachpart(MM_CTX *, struct mm_mimepart *);
+int mm_context_deletepart(MM_CTX *, int, int);
+int mm_context_countparts(MM_CTX *);
+struct mm_mimepart *mm_context_getpart(MM_CTX *, int);
+int mm_context_iscomposite(MM_CTX *);
+int mm_context_haswarnings(MM_CTX *);
+int mm_context_flatten(MM_CTX *, char **, size_t *, int);
+
+int mm_envelope_getheaders(MM_CTX *, char **, size_t *);
+int mm_envelope_setheader(MM_CTX *, const char *, const char *, ...);
+
+struct mm_mimeheader *mm_mimeheader_new(void);
+void mm_mimeheader_free(struct mm_mimeheader *);
+struct mm_mimeheader *mm_mimeheader_generate(const char *, const char *);
+int mm_mimeheader_uncomment(struct mm_mimeheader *);
+int mm_mimeheader_uncommentbyname(struct mm_mimepart *, const char *);
+int mm_mimeheader_uncommentall(struct mm_mimepart *);
+int mm_mimeheader_tostring(struct mm_mimeheader *);
+char *mm_mimeheader_getparambyname(struct mm_mimeheader *hdr, const char *name);
+int mm_mimeheader_attachparam(struct mm_mimeheader *hdr, struct mm_param *param);
+
+struct mm_mimepart *mm_mimepart_new(void);
+void mm_mimepart_free(struct mm_mimepart *);
+int mm_mimepart_attachheader(struct mm_mimepart *, struct mm_mimeheader *);
+int mm_mimepart_countheaders(struct mm_mimepart *part);
+int mm_mimepart_countheaderbyname(struct mm_mimepart *, const char *);
+struct mm_mimeheader *mm_mimepart_getheaderbyname(struct mm_mimepart *, const char *, int);
+const char *mm_mimepart_getheadervalue(struct mm_mimepart *, const char *, int);
+int mm_mimepart_headers_start(struct mm_mimepart *, struct mm_mimeheader **);
+struct mm_mimeheader *mm_mimepart_headers_next(struct mm_mimepart *, struct mm_mimeheader **);
+char *mm_mimepart_decode(struct mm_mimepart *);
+struct mm_content *mm_mimepart_getcontent(struct mm_mimepart *);
+size_t mm_mimepart_getlength(struct mm_mimepart *);
+char *mm_mimepart_getbody(struct mm_mimepart *, int);
+void mm_mimepart_attachcontenttype(struct mm_mimepart *, struct mm_content *);
+int mm_mimepart_setdefaultcontenttype(struct mm_mimepart *, int);
+int mm_mimepart_flatten(struct mm_mimepart *, char **, size_t *, int);
+struct mm_mimepart *mm_mimepart_fromfile(const char *);
+
+struct mm_content *mm_content_new(void);
+void mm_content_free(struct mm_content *);
+int mm_content_attachtypeparam(struct mm_content *, struct mm_param *);
+int mm_content_attachdispositionparam(struct mm_content *, struct mm_param *);
+struct mm_content *mm_content_parse(const char *, int);
+char *mm_content_gettypeparambyname(struct mm_content *, const char *);
+char *mm_content_getdispositionparambyname(struct mm_content *, const char *);
+struct mm_param *mm_content_gettypeparamobjbyname(struct mm_content *, const char *);
+struct mm_param *mm_content_getdispositionparamobjbyname(struct mm_content *, const char *);
+int mm_content_setmaintype(struct mm_content *, char *, int);
+int mm_content_setsubtype(struct mm_content *, char *, int);
+int mm_content_settype(struct mm_content *, const char *, ...);
+int mm_content_setdispositiontype(struct mm_content *ct, char *value, int copy);
+char *mm_content_getmaintype(struct mm_content *);
+char *mm_content_getsubtype(struct mm_content *);
+char *mm_content_gettype(struct mm_content *);
+char *mm_content_getdispositiontype(struct mm_content *ct);
+int mm_content_iscomposite(struct mm_content *);
+int mm_content_isvalidencoding(const char *);
+int mm_content_setencoding(struct mm_content *, const char *);
+char *mm_content_typeparamstostring(struct mm_content *);
+char *mm_content_dispositionparamstostring(struct mm_content *);
+char *mm_content_tostring(struct mm_content *);
+
+struct mm_param *mm_param_new(void);
+void mm_param_free(struct mm_param *);
+
+char *mm_flatten_mimepart(struct mm_mimepart *);
+char *mm_flatten_context(MM_CTX *);
+
+int mm_codec_isregistered(const char *);
+int mm_codec_hasdecoder(const char *);
+int mm_codec_hasencoder(const char *);
+int mm_codec_register(const char *, char *(*encoder)(char *, uint32_t), char *(*decoder)(char *));
+int mm_codec_unregister(const char *);
+int mm_codec_unregisterall(void);
+void mm_codec_registerdefaultcodecs(void);
+
+char *mm_base64_decode(char *);
+char *mm_base64_encode(char *, uint32_t);
+
+void mm_error_init(void);
+void mm_error_setmsg(const char *, ...);
+void mm_error_setlineno(int lineno);
+char *mm_error_string(void);
+int mm_error_lineno(void);
+
+void mm_warning_add(MM_CTX *, int, const char *, ...);
+struct mm_warning *mm_warning_next(MM_CTX *, struct mm_warning **);
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif /* ! HAVE_STRLCPY */
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *, const char *, size_t);
+#endif /* ! HAVE_STRLCAT */
+
+#define MM_ISINIT() do { \
+ assert(mm_library_isinitialized() == 1); \
+} while (0);
+
+#endif /* ! _MM_H_INCLUDED */
diff --git a/trunk/main/minimime/mm_base64.c b/trunk/main/minimime/mm_base64.c
new file mode 100644
index 000000000..ee6dc183b
--- /dev/null
+++ b/trunk/main/minimime/mm_base64.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2003 Jann Fischer <jfi@openbsd.de>
+ * All rights reserved.
+ *
+ * XXX: This piece of software is not nearly MIME compatible as it should be.
+ *
+ * This is based on third-party code, see the copyright notice below.
+ *
+ */
+
+/* $Id$ */
+
+/***********************************************************
+ Copyright 1998 by Carnegie Mellon University
+
+ 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 name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY 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.
+******************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+#define XX 127
+
+static int _mm_base64_decode(char *);
+static char *_mm_base64_encode(char *, uint32_t);
+
+/*
+ * Tables for encoding/decoding base64
+ */
+static const char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char index_64[256] = {
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
+ 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
+ XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
+ XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+#define CHAR64(c) (index_64[(unsigned char)(c)])
+
+/*
+ * mm_base64_decode()
+ *
+ * Decodes the data pointed to by 'data' from the BASE64 encoding to the data
+ * format it was encoded from. Returns a pointer to a string on success or
+ * NULL on error. The string returned needs to be freed by the caller at some
+ * later point.
+ *
+ */
+char *
+mm_base64_decode(char *data)
+{
+ char *buf;
+
+ assert(data != NULL);
+
+ buf = mm_stripchars(data, "\r\n");
+ assert(buf != NULL);
+
+ _mm_base64_decode(buf);
+ assert(buf != NULL);
+ return(buf);
+}
+
+/*
+ * mm_base64_encode()
+ *
+ * Encodes the data pointed to by 'data', which is of the length specified in
+ * 'len' to the BASE64 format. Returns a pointer to a string containing the
+ * BASE64 encoding, whose lines are broken at the MIME recommended linelength
+ * of 76 characters. If an error occured, returns NULL. The string returned
+ * needs to be freed by the caller at some later point.
+ *
+ */
+char *
+mm_base64_encode(char *data, uint32_t len) {
+ char *buf;
+ char *ret;
+
+ assert(data != NULL);
+
+ buf = _mm_base64_encode(data, len);
+ assert(buf != NULL);
+
+ ret = mm_addchars(buf, "\r\n", MM_BASE64_LINELEN);
+ xfree(buf);
+ assert(ret != NULL);
+ return ret;
+}
+
+/*
+ * Decode in-place the base64 data in 'input'. Returns the length
+ * of the decoded data, or -1 if there was an error.
+ */
+static int
+_mm_base64_decode(char *input)
+{
+ uint32_t len = 0;
+ unsigned char *output = (unsigned char *)input;
+ int c1, c2, c3, c4;
+
+ while (*input) {
+ c1 = *input++;
+ if (CHAR64(c1) == XX) return -1;
+ c2 = *input++;
+ if (CHAR64(c2) == XX) return -1;
+ c3 = *input++;
+ if (c3 != '=' && CHAR64(c3) == XX) return -1;
+ c4 = *input++;
+ if (c4 != '=' && CHAR64(c4) == XX) return -1;
+ *output++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+ ++len;
+ if (c3 == '=') break;
+ *output++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+ ++len;
+ if (c4 == '=') break;
+ *output++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+ ++len;
+ }
+ *output = 0;
+
+ return len;
+}
+
+/*
+ * Encode the given binary string of length 'len' and return Base64
+ * in a char buffer. It allocates the space for buffer.
+ * caller must free the space.
+ */
+static char *
+_mm_base64_encode(char *data, uint32_t len)
+{
+ char *buf;
+ uint32_t buflen;
+ int c1;
+ int c2;
+ int c3;
+ uint32_t maxbuf;
+
+ buflen = 0;
+
+#ifdef RUBBISH
+ maxbuf = len*4/3 + 1; /* size after expantion */
+#endif
+ maxbuf = len*2 + 20; /* size after expantion */
+
+ buf = (char *)xmalloc(maxbuf);
+
+ while (len && buflen < (maxbuf - 6)) {
+
+ c1 = (unsigned char)*data++;
+ buf[buflen++] = basis_64[c1>>2];
+
+ if (--len == 0) c2 = 0;
+ else c2 = (unsigned char)*data++;
+ buf[buflen++] = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
+
+ if (len == 0) {
+ buf[buflen++] = '=';
+ buf[buflen++] = '=';
+ break;
+ }
+
+ if (--len == 0) c3 = 0;
+ else c3 = (unsigned char)*data++;
+
+ buf[buflen++] = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
+ if (len == 0) {
+ buf[buflen++] = '=';
+
+ break;
+ }
+
+ --len;
+ buf[buflen++] = basis_64[c3 & 0x3F];
+ }
+
+ buf[buflen]=0;
+ return buf;
+}
diff --git a/trunk/main/minimime/mm_codecs.c b/trunk/main/minimime/mm_codecs.c
new file mode 100644
index 000000000..23f94aeba
--- /dev/null
+++ b/trunk/main/minimime/mm_codecs.c
@@ -0,0 +1,250 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+extern struct mm_codecs codecs;
+
+/** @file mm_codecs.c
+ *
+ * This module contains functions to manipulate MiniMIME codecs
+ *
+ */
+
+/** @defgroup codecs Manipulating MiniMIME codecs */
+
+/** @{
+ * @name Codec manipulation
+ */
+
+/**
+ * Looks up whether a context has an decoder installed for a given encoding
+ *
+ * @param encoding The encoding specifier to look up
+ * @return 1 if a decoder is installed or 0 if not
+ * @ingroup codecs
+ */
+int
+mm_codec_hasdecoder(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ assert(codec->encoding != NULL);
+ if (!strcasecmp(codec->encoding, encoding)) {
+ if (codec->decoder != NULL)
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Looks up whether a context has an encoder installed for a given encoding
+ *
+ * @param ctx A valid MIME context
+ * @param encoding The encoding specifier to look up
+ * @return 1 if an encoder is installed or 0 if not
+ * @ingroup codecs
+ */
+int
+mm_codec_hasencoder(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ assert(codec->encoding != NULL);
+ if (!strcasecmp(codec->encoding, encoding)) {
+ if (codec->encoder != NULL)
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Looks up whether a codec for a given encoding is installed to a context
+ *
+ * @param encoding The encoding specifier to look up
+ * @return 1 if a codec was found or 0 if not
+ * @ingroup codecs
+ */
+int
+mm_codec_isregistered(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (!strcasecmp(codec->encoding, encoding)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Registers a codec with the MiniMIME library
+ *
+ * @param encoding The encoding specifier for which to register the codec
+ * @param encoder The encoder function for this encoding
+ * @param decoder The decoder function for this encoding
+ * @return 1 if successfull or 0 if not
+ * @ingroup codecs
+ *
+ * This function registers a codec for a given MiniMIME context. The codec
+ * may provide an decoder, an encoder or both (but not none). If there is
+ * a codec already installed for this encoding, the function will puke.
+ */
+int
+mm_codec_register(const char *encoding,
+ char *(*encoder)(char *data, uint32_t i),
+ char *(*decoder)(char *data))
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ assert(mm_codec_isregistered(encoding) != 1);
+
+ codec = (struct mm_codec *)xmalloc(sizeof(struct mm_codec));
+
+ codec->encoding = xstrdup(encoding);
+ codec->encoder = encoder;
+ codec->decoder = decoder;
+
+ if (SLIST_EMPTY(&codecs)) {
+ SLIST_INSERT_HEAD(&codecs, codec, next);
+ return 1;
+ } else {
+ struct mm_codec *lcodec, *tcodec;
+ tcodec = NULL;
+ SLIST_FOREACH(lcodec, &codecs, next) {
+ if (lcodec != NULL)
+ tcodec = lcodec;
+ }
+ assert(tcodec != NULL);
+ SLIST_INSERT_AFTER(tcodec, codec, next);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Unregisters a MiniMIME codec
+ *
+ * @param encoding The encoding specifier which to unregister
+ * @return 0 if unregistered successfully, or -1 if there was no such codec
+ * @ingroup codecs
+ */
+int
+mm_codec_unregister(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (!strcasecmp(codec->encoding, encoding)) {
+ xfree(codec->encoding);
+ xfree(codec);
+ codec = NULL;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Unregisters all codecs within a context
+ *
+ * @param ctx A valid MiniMIME context
+ * @return 0 if all codecs were unregistered successfully or -1 if an error
+ * occured.
+ * @note Foobar
+ */
+int
+mm_codec_unregisterall(void)
+{
+ struct mm_codec *codec;
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (mm_codec_unregister(codec->encoding) == -1) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Registers the default codecs to a MiniMIME context
+ *
+ * This functions registers the codecs for the following encodings to a
+ * MiniMIME context:
+ *
+ * - Base64
+ * - (TODO:) Quoted-Printable
+ */
+void
+mm_codec_registerdefaultcodecs(void)
+{
+ mm_codec_register("base64", mm_base64_encode, mm_base64_decode);
+}
+
+
+/** @} */
diff --git a/trunk/main/minimime/mm_contenttype.c b/trunk/main/minimime/mm_contenttype.c
new file mode 100644
index 000000000..27fbc0c12
--- /dev/null
+++ b/trunk/main/minimime/mm_contenttype.c
@@ -0,0 +1,759 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/* This file is documented using Doxygen */
+
+/**
+ * @file mm_contenttype.c
+ *
+ * This module contains functions for manipulating Content-Type objects.
+ */
+
+/** @defgroup contenttype Accessing and manipulating Content-Type objects */
+
+struct mm_encoding_mappings {
+ const char *idstring;
+ int type;
+};
+
+static struct mm_encoding_mappings mm_content_enctypes[] = {
+ { "Base64", MM_ENCODING_BASE64 },
+ { "Quoted-Printable", MM_ENCODING_QUOTEDPRINTABLE },
+ { NULL, - 1},
+};
+
+static const char *mm_composite_maintypes[] = {
+ "multipart",
+ "message",
+ NULL,
+};
+
+static const char *mm_composite_encodings[] = {
+ "7bit",
+ "8bit",
+ "binary",
+ NULL,
+};
+
+/** @{
+ * @name Functions for manipulating Content objects
+ */
+
+/**
+ * Creates a new object to hold a Content representation.
+ * The allocated memory must later be freed using mm_content_free()
+ *
+ * @return An object representing a MIME Content-Type
+ * @see mm_content_free
+ * @ingroup contenttype
+ */
+struct mm_content *
+mm_content_new(void)
+{
+ struct mm_content *ct;
+
+ ct = (struct mm_content *)xmalloc(sizeof(struct mm_content));
+
+ ct->maintype = NULL;
+ ct->subtype = NULL;
+
+ TAILQ_INIT(&ct->type_params);
+ TAILQ_INIT(&ct->disposition_params);
+
+ ct->encoding = MM_ENCODING_NONE;
+ ct->encstring = NULL;
+
+ return ct;
+}
+
+/**
+ * Releases all memory associated with an Content object
+ *
+ * @param ct A Content-Type object
+ * @return Nothing
+ * @ingroup contenttype
+ */
+void
+mm_content_free(struct mm_content *ct)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ if (ct->maintype != NULL) {
+ xfree(ct->maintype);
+ ct->maintype = NULL;
+ }
+ if (ct->subtype != NULL) {
+ xfree(ct->subtype);
+ ct->subtype = NULL;
+ }
+ if (ct->encstring != NULL) {
+ xfree(ct->encstring);
+ ct->encstring = NULL;
+ }
+
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ TAILQ_REMOVE(&ct->type_params, param, next);
+ mm_param_free(param);
+ }
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ TAILQ_REMOVE(&ct->disposition_params, param, next);
+ mm_param_free(param);
+ }
+
+ xfree(ct);
+}
+
+/**
+ * Attaches a content-type parameter to a Content object
+ *
+ * @param ct The target Content object
+ * @param param The Content-Type parameter which to attach
+ * @return 0 on success and -1 on failure
+ * @ingroup contenttype
+ */
+int
+mm_content_attachtypeparam(struct mm_content *ct, struct mm_param *param)
+{
+ assert(ct != NULL);
+ assert(param != NULL);
+
+ if (TAILQ_EMPTY(&ct->type_params)) {
+ TAILQ_INSERT_HEAD(&ct->type_params, param, next);
+ } else {
+ TAILQ_INSERT_TAIL(&ct->type_params, param, next);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Attaches a content-disposition parameter to a Content-Disposition object
+ *
+ * @param ct The target Content object
+ * @param param The Content-Type parameter which to attach
+ * @return 0 on success and -1 on failure
+ * @ingroup contenttype
+ */
+int
+mm_content_attachdispositionparam(struct mm_content *ct, struct mm_param *param)
+{
+ assert(ct != NULL);
+ assert(param != NULL);
+
+ if (TAILQ_EMPTY(&ct->disposition_params)) {
+ TAILQ_INSERT_HEAD(&ct->disposition_params, param, next);
+ } else {
+ TAILQ_INSERT_TAIL(&ct->disposition_params, param, next);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Gets a Content-Type parameter value from a Content object.
+ *
+ * @param ct the Content object
+ * @param name the name of the parameter to retrieve
+ * @return The value of the parameter on success or a NULL pointer on failure
+ * @ingroup contenttype
+ */
+char *
+mm_content_gettypeparambyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param->value;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Gets a Content-Disposition parameter value from a Content object.
+ *
+ * @param ct the Content object
+ * @param name the name of the parameter to retrieve
+ * @return The value of the parameter on success or a NULL pointer on failure
+ * @ingroup contenttype
+ */
+char *
+mm_content_getdispositionparambyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param->value;
+ }
+ }
+
+ return NULL;
+}
+
+struct mm_param *
+mm_content_gettypeparamobjbyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param;
+ }
+ }
+
+ return NULL;
+}
+
+struct mm_param *
+mm_content_getdispositionparamobjbyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Sets the MIME main Content-Type for a MIME Content object
+ *
+ * @param ct The MIME Content object
+ * @param value The value which to set the main type to
+ * @param copy Whether to make a copy of the value (original value must be
+ * freed afterwards to prevent memory leaks).
+ */
+int
+mm_content_setmaintype(struct mm_content *ct, char *value, int copy)
+{
+ assert(ct != NULL);
+ assert(value != NULL);
+
+ if (copy) {
+ /**
+ * @bug The xfree() call could lead to undesirable results.
+ * Do we really need it?
+ */
+ if (ct->maintype != NULL) {
+ xfree(ct->maintype);
+ }
+ ct->maintype = xstrdup(value);
+ } else {
+ ct->maintype = value;
+ }
+
+ return 0;
+}
+
+/**
+ * Retrieves the main MIME Content-Type stored in a Content object
+ *
+ * @param ct A valid Content object
+ * @returns A pointer to the string representing the main type
+ * @ingroup contenttype
+ */
+char *
+mm_content_getmaintype(struct mm_content *ct)
+{
+ assert(ct != NULL);
+ assert(ct->maintype != NULL);
+
+ return ct->maintype;
+}
+
+/**
+ * Sets the MIME Content-Disposition type for a MIME Content object
+ *
+ * @param ct The MIME Content object
+ * @param value The value which to set the main type to
+ * @param copy Whether to make a copy of the value (original value must be
+ * freed afterwards to prevent memory leaks).
+ */
+int
+mm_content_setdispositiontype(struct mm_content *ct, char *value, int copy)
+{
+ assert(ct != NULL);
+ assert(value != NULL);
+
+ if (copy) {
+ /**
+ * @bug The xfree() call could lead to undesirable results.
+ * Do we really need it?
+ */
+ if (ct->disposition_type != NULL) {
+ xfree(ct->disposition_type);
+ }
+ ct->disposition_type = xstrdup(value);
+ } else {
+ ct->disposition_type = value;
+ }
+
+ return 0;
+}
+
+/**
+ * Retrieves the Content-Disposition MIME type stored in a Content object
+ *
+ * @param ct A valid Content-Type object
+ * @returns A pointer to the string representing the main type
+ * @ingroup contenttype
+ */
+char *
+mm_content_getdispositiontype(struct mm_content *ct)
+{
+ assert(ct != NULL);
+ assert(ct->disposition_type != NULL);
+
+ return ct->disposition_type;
+}
+
+/**
+ * Retrieves the sub MIME Content-Type stored in a Content object
+ *
+ * @param ct A valid Content-Type object
+ * @return A pointer to the string holding the current sub MIME type
+ * @ingroup contenttype
+ */
+char *
+mm_content_getsubtype(struct mm_content *ct)
+{
+ assert(ct != NULL);
+ assert(ct->subtype != NULL);
+
+ return ct->subtype;
+}
+
+/**
+ * Sets the MIME sub Content-Type for a MIME Content object
+ *
+ * @param ct The MIME Content-Type object
+ * @param value The value which to set the sub type to
+ * @param copy Whether to make a copy of the value (original value must be
+ * freed afterwards to prevent memory leaks).
+ */
+int
+mm_content_setsubtype(struct mm_content *ct, char *value, int copy)
+{
+ assert(ct != NULL);
+ assert(value != NULL);
+
+ if (copy) {
+ /**
+ * @bug The xfree() call could lead to undesirable results.
+ * Do we really need it?
+ */
+ if (ct->subtype != NULL) {
+ xfree(ct->subtype);
+ }
+ ct->subtype = xstrdup(value);
+ } else {
+ ct->subtype = value;
+ }
+
+ return 0;
+}
+
+int
+mm_content_settype(struct mm_content *ct, const char *fmt, ...)
+{
+ char *maint, *subt;
+ char buf[512], *parse;
+ va_list ap;
+
+ mm_errno = MM_ERROR_NONE;
+
+ va_start(ap, fmt);
+ /* Make sure no truncation occurs */
+ if (vsnprintf(buf, sizeof buf, fmt, ap) > sizeof buf) {
+ mm_errno = MM_ERROR_ERRNO;
+ mm_error_setmsg("Input string too long");
+ return -1;
+ }
+ va_end(ap);
+
+ parse = buf;
+ maint = strsep(&parse, "/");
+ if (maint == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("Invalid type specifier: %s", buf);
+ return -1;
+ }
+ ct->maintype = xstrdup(maint);
+
+ subt = strsep(&parse, "");
+ if (subt == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("Invalid type specifier: %s", buf);
+ return -1;
+ }
+ ct->subtype = xstrdup(subt);
+
+ return 0;
+}
+
+/**
+ * Checks whether the Content-Type represents a composite message or not
+ *
+ * @param ct A valid Content-Type object
+ * @returns 1 if the Content-Type object represents a composite message or
+ * 0 if not.
+ */
+int
+mm_content_iscomposite(struct mm_content *ct)
+{
+ int i;
+
+ for (i = 0; mm_composite_maintypes[i] != NULL; i++) {
+ if (!strcasecmp(ct->maintype, mm_composite_maintypes[i])) {
+ return 1;
+ }
+ }
+
+ /* Not found */
+ return 0;
+}
+
+/**
+ * Verifies whether a string represents a valid encoding or not.
+ *
+ * @param encoding The string to verify
+ * @return 1 if the encoding string is valid or 0 if not
+ *
+ */
+int
+mm_content_isvalidencoding(const char *encoding)
+{
+ int i;
+
+ for (i = 0; mm_composite_encodings[i] != NULL; i++) {
+ if (!strcasecmp(encoding, mm_composite_encodings[i])) {
+ return 1;
+ }
+ }
+
+ /* Not found */
+ return 0;
+}
+
+/**
+ * Set the encoding of a MIME entitity according to a mapping table
+ *
+ * @param ct A valid content type object
+ * @param encoding A string representing the content encoding
+ * @return 0 if successfull or -1 if not (i.e. unknown content encoding)
+ */
+int
+mm_content_setencoding(struct mm_content *ct, const char *encoding)
+{
+ int i;
+
+ assert(ct != NULL);
+ assert(encoding != NULL);
+
+ for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) {
+ if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) {
+ ct->encoding = mm_content_enctypes[i].type;
+ ct->encstring = xstrdup(encoding);
+ return 0;
+ }
+ }
+
+ /* If we didn't find a mapping, set the encoding to unknown */
+ ct->encoding = MM_ENCODING_UNKNOWN;
+ ct->encstring = NULL;
+ return 1;
+}
+
+/**
+ * Gets the numerical ID of a content encoding identifier
+ *
+ * @param ct A valid Content Type object
+ * @param encoding A string representing the content encoding identifier
+ * @return The numerical ID of the content encoding
+ */
+#if 0
+int
+mm_content_getencoding(struct mm_content *ct, const char *encoding)
+{
+ int i;
+
+ assert(ct != NULL);
+
+ for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) {
+ if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) {
+ return mm_content_enctypes[i].type;
+ }
+ }
+
+ /* Not found */
+ return MM_ENCODING_UNKNOWN;
+}
+#endif
+
+/**
+ * Constructs a MIME conform string of Content-Type parameters.
+ *
+ * @param ct A valid Content Type object
+ * @return A pointer to a string representing the Content-Type parameters
+ * in MIME terminology, or NULL if either the Content-Type object
+ * is invalid, has no parameters or no memory could be allocated.
+ *
+ * This function constructs a MIME conform string including all the parameters
+ * associated with the given Content-Type object. It should NOT be used if
+ * you need an opaque copy of the current MIME part (e.g. for PGP purposes).
+ */
+char *
+mm_content_typeparamstostring(struct mm_content *ct)
+{
+ size_t size, new_size;
+ struct mm_param *param;
+ char *param_string, *cur_param;
+ char *buf;
+
+ size = 1;
+ param_string = NULL;
+ cur_param = NULL;
+
+ param_string = (char *) xmalloc(size);
+ *param_string = '\0';
+
+ /* Concatenate all Content-Type parameters attached to the current
+ * Content-Type object to a single string.
+ */
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ if (asprintf(&cur_param, "; %s=\"%s\"", param->name,
+ param->value) == -1) {
+ goto cleanup;
+ }
+
+ new_size = size + strlen(cur_param) + 1;
+
+ if (new_size < 0 || new_size > 1000) {
+ size = 0;
+ goto cleanup;
+ }
+
+ buf = (char *) xrealloc(param_string, new_size);
+ if (buf == NULL) {
+ size = 0;
+ goto cleanup;
+ }
+
+ param_string = buf;
+ size = new_size;
+ strlcat(param_string, cur_param, size);
+
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+
+ return param_string;
+
+cleanup:
+ if (param_string != NULL) {
+ xfree(param_string);
+ param_string = NULL;
+ }
+ if (cur_param != NULL) {
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+ return NULL;
+}
+
+/**
+ * Constructs a MIME conformant string of Content-Disposition parameters.
+ *
+ * @param ct A valid Content object
+ * @return A pointer to a string representing the Content-Disposition parameters
+ * in MIME terminology, or NULL if either the Content object
+ * is invalid, has no Disposition parameters or no memory could be allocated.
+ *
+ * This function constructs a MIME conforming string including all the parameters
+ * associated with the given Content-Disposition object. It should NOT be used if
+ * you need an opaque copy of the current MIME part (e.g. for PGP purposes).
+ */
+char *
+mm_content_dispositionparamstostring(struct mm_content *ct)
+{
+ size_t size, new_size;
+ struct mm_param *param;
+ char *param_string, *cur_param;
+ char *buf;
+
+ size = 1;
+ param_string = NULL;
+ cur_param = NULL;
+
+ param_string = (char *) xmalloc(size);
+ *param_string = '\0';
+
+ /* Concatenate all Content-Disposition parameters attached to the current
+ * Content object to a single string.
+ */
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ if (asprintf(&cur_param, "; %s=\"%s\"", param->name,
+ param->value) == -1) {
+ goto cleanup;
+ }
+
+ new_size = size + strlen(cur_param) + 1;
+
+ if (new_size < 0 || new_size > 1000) {
+ size = 0;
+ goto cleanup;
+ }
+
+ buf = (char *) xrealloc(param_string, new_size);
+ if (buf == NULL) {
+ size = 0;
+ goto cleanup;
+ }
+
+ param_string = buf;
+ size = new_size;
+ strlcat(param_string, cur_param, size);
+
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+
+ return param_string;
+
+cleanup:
+ if (param_string != NULL) {
+ xfree(param_string);
+ param_string = NULL;
+ }
+ if (cur_param != NULL) {
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+ return NULL;
+}
+
+/**
+ * Creates a Content-Type header according to the object given
+ *
+ * @param ct A valid Content-Type object
+ *
+ */
+char *
+mm_content_tostring(struct mm_content *ct)
+{
+ char *paramstring;
+ char *buf;
+ char *headerstring;
+ size_t size;
+
+ paramstring = NULL;
+ headerstring = NULL;
+ buf = NULL;
+
+ if (ct == NULL) {
+ return NULL;
+ }
+ if (ct->maintype == NULL || ct->subtype == NULL) {
+ return NULL;
+ }
+
+ size = strlen(ct->maintype) + strlen(ct->subtype) + 2;
+ headerstring = (char *)xmalloc(size);
+ snprintf(headerstring, size, "%s/%s", ct->maintype, ct->subtype);
+
+ paramstring = mm_content_typeparamstostring(ct);
+ if (paramstring == NULL) {
+ goto cleanup;
+ }
+
+ size += strlen(paramstring) + strlen("Content-Type: ") + 1;
+ buf = (char *)malloc(size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ snprintf(buf, size, "Content-Type: %s%s", headerstring, paramstring);
+
+ xfree(headerstring);
+ xfree(paramstring);
+
+ headerstring = NULL;
+ paramstring = NULL;
+
+ return buf;
+
+cleanup:
+ if (paramstring != NULL) {
+ xfree(paramstring);
+ paramstring = NULL;
+ }
+ if (headerstring != NULL) {
+ xfree(headerstring);
+ headerstring = NULL;
+ }
+ if (buf != NULL) {
+ xfree(buf);
+ buf = NULL;
+ }
+ return NULL;
+}
+
+/** @} */
diff --git a/trunk/main/minimime/mm_context.c b/trunk/main/minimime/mm_context.c
new file mode 100644
index 000000000..d67c1e6b3
--- /dev/null
+++ b/trunk/main/minimime/mm_context.c
@@ -0,0 +1,614 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/** @file mm_context.c
+ *
+ * Modules for manipulating MiniMIME contexts
+ */
+
+/** @defgroup context Accessing and manipulating MIME contexts
+ *
+ * Each message in MiniMIME is represented by a so called ``context''. A
+ * context holds all necessary information given about a MIME message, such
+ * as the envelope, all MIME parts etc.
+ */
+
+/** @{
+ * @name Manipulating MiniMIME contexts
+ */
+
+/**
+ * Creates a new MiniMIME context object.
+ *
+ * @return a new MiniMIME context object
+ * @see mm_context_free
+ *
+ * This function creates a new MiniMIME context, which will hold a message.
+ * The memory needed is allocated dynamically and should later be free'd
+ * using mm_context_free().
+ *
+ * Before a context can be created, the MiniMIME library needs to be
+ * initialized properly using mm_library_init().
+ *
+ */
+MM_CTX *
+mm_context_new(void)
+{
+ MM_CTX *ctx;
+
+ MM_ISINIT();
+
+ ctx = (MM_CTX *)xmalloc(sizeof(MM_CTX));
+ ctx->messagetype = MM_MSGTYPE_FLAT; /* This is the default */
+ ctx->boundary = NULL;
+ ctx->preamble = xstrdup("This is a message in MIME format, generated "
+ "by MiniMIME 0.1");
+
+ TAILQ_INIT(&ctx->parts);
+ SLIST_INIT(&ctx->warnings);
+
+ return ctx;
+}
+
+/**
+ * Releases a MiniMIME context object
+ *
+ * @param ctx A valid MiniMIME context
+ * @see mm_context_new
+ *
+ * This function releases all memory associated with MiniMIME context object
+ * that was created using mm_context_new(). It will also release all memory
+ * used for the MIME parts attached, and their specific properties (such as
+ * Content-Type information, headers, and the body data).
+ */
+void
+mm_context_free(MM_CTX *ctx)
+{
+ struct mm_mimepart *part;
+ struct mm_warning *warning, *nxt;
+
+ assert(ctx != NULL);
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ TAILQ_REMOVE(&ctx->parts, part, next);
+ mm_mimepart_free(part);
+ }
+
+ if (ctx->boundary != NULL) {
+ xfree(ctx->boundary);
+ ctx->boundary = NULL;
+ }
+
+ if (ctx->preamble != NULL) {
+ xfree(ctx->preamble);
+ ctx->preamble = NULL;
+ }
+
+ for (warning = SLIST_FIRST(&ctx->warnings);
+ warning != SLIST_END(&ctx->warnings);
+ warning = nxt) {
+ nxt = SLIST_NEXT(warning, next);
+ SLIST_REMOVE(&ctx->warnings, warning, mm_warning, next);
+ xfree(warning);
+ warning = NULL;
+ }
+
+ xfree(ctx);
+ ctx = NULL;
+}
+
+/**
+ * Attaches a MIME part object to a MiniMIME context.
+ *
+ * @param ctx the MiniMIME context
+ * @param part the MIME part object to attach
+ * @return 0 on success or -1 on failure. Sets mm_errno on failure.
+ *
+ * This function attaches a MIME part to a context, appending it to the end
+ * of the message.
+ *
+ * The MIME part should be initialized before attaching it using
+ * mm_mimepart_new().
+ */
+int
+mm_context_attachpart(MM_CTX *ctx, struct mm_mimepart *part)
+{
+ assert(ctx != NULL);
+ assert(part != NULL);
+
+ if (TAILQ_EMPTY(&ctx->parts)) {
+ TAILQ_INSERT_HEAD(&ctx->parts, part, next);
+ } else {
+ TAILQ_INSERT_TAIL(&ctx->parts, part, next);
+ }
+
+ return 0;
+}
+
+/**
+ * Attaches a MIME part object to a MiniMIME context at a given position
+ *
+ * @param ctx A valid MiniMIME context
+ * @param part The MIME part object to attach
+ * @param pos After which part to attach the object
+ * @return 0 on success or -1 if the given position is invalid
+ * @see mm_context_attachpart
+ *
+ * This function attaches a MIME part object after a given position in the
+ * specified context. If the position is invalid (out of range), the part
+ * will not get attached to the message and the function returns -1. If
+ * the index was in range, the MIME part will get attached after the MIME
+ * part at the given position, moving any possible following MIME parts one
+ * down the hierarchy.
+ */
+#if 0
+int
+mm_context_attachpart_after(MM_CTX *ctx, struct mm_mimepart *part, int pos)
+{
+ struct mm_mimepart *p;
+ int where;
+
+ where = 0;
+ p = NULL;
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (where == pos) {
+ p = part;
+ }
+ }
+
+ if (p == NULL) {
+ return(-1);
+ }
+
+ TAILQ_INSERT_AFTER(&ctx->parts, p, part, next);
+
+ return(0);
+}
+#endif
+
+/**
+ * Deletes a MIME part object from a MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param which The number of the MIME part object to delete
+ * @param freemem Whether to free the memory associated with the MIME part
+ * object
+ * @return 0 on success or -1 on failure. Sets mm_errno on failure.
+ *
+ * This function deletes a MIME part from a given context. The MIME part to
+ * delete is specified as numerical index by the parameter ``which''. If the
+ * parameter ``freemem'' is set to anything greater than 0, the memory that
+ * is associated will be free'd by using mm_mimepart_free(), otherwise the
+ * memory is left untouched (if you still have a pointer to the MIME part
+ * around).
+ */
+int
+mm_context_deletepart(MM_CTX *ctx, int which, int freemem)
+{
+ struct mm_mimepart *part;
+ int cur;
+
+ assert(ctx != NULL);
+ assert(which >= 0);
+
+ cur = 0;
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (cur == which) {
+ TAILQ_REMOVE(&ctx->parts, part, next);
+ if (freemem)
+ mm_mimepart_free(part);
+ return 0;
+ }
+ cur++;
+ }
+
+ return -1;
+}
+
+/**
+ * Counts the number of attached MIME part objects in a given MiniMIME context
+ *
+ * @param ctx The MiniMIME context
+ * @returns The number of attached MIME part objects
+ */
+int
+mm_context_countparts(MM_CTX *ctx)
+{
+ int count;
+ struct mm_mimepart *part;
+
+ assert(ctx != NULL);
+
+ count = 0;
+
+ if (TAILQ_EMPTY(&ctx->parts)) {
+ return 0;
+ } else {
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ count++;
+ }
+ }
+
+ assert(count > -1);
+
+ return count;
+}
+
+/**
+ * Gets a specified MIME part object from a MimeMIME context
+ *
+ * @param ctx The MiniMIME context
+ * @param which The number of the MIME part object to retrieve
+ * @returns The requested MIME part object on success or a NULL pointer if
+ * there is no such part.
+ */
+struct mm_mimepart *
+mm_context_getpart(MM_CTX *ctx, int which)
+{
+ struct mm_mimepart *part;
+ int cur;
+
+ assert(ctx != NULL);
+
+ cur = 0;
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (cur == which) {
+ return part;
+ }
+ cur++;
+ }
+
+ return NULL;
+}
+
+/**
+ * Checks whether a given context represents a composite (multipart) message
+ *
+ * @param ctx A valid MiniMIME context object
+ * @return 1 if the context is a composite message or 0 if it's flat
+ *
+ */
+int
+mm_context_iscomposite(MM_CTX *ctx)
+{
+ if (ctx->messagetype == MM_MSGTYPE_MULTIPART) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Checks whether there are any warnings associated with a given context
+ *
+ * @param ctx A valid MiniMIME context
+ * @return 1 if there are warnings associated with the context, otherwise 0
+ */
+int
+mm_context_haswarnings(MM_CTX *ctx)
+{
+ if (SLIST_EMPTY(&ctx->warnings)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/**
+ * Generates a generic boundary string for a given context
+ *
+ * @param ctx A valid MiniMIME context
+ * @return 0 on success or -1 on failure
+ *
+ * This function generates a default boundary string for the given context.
+ * If there is already a boundary for the context, the memory will be free()'d.
+ */
+#if 0
+int
+mm_context_generateboundary(MM_CTX *ctx)
+{
+ char *boundary;
+ struct mm_mimepart *part;
+ struct mm_param *param;
+
+ if (mm_mimeutil_genboundary("++MiniMIME++", 20, &boundary) == -1) {
+ return(-1);
+ }
+
+ if (ctx->boundary != NULL) {
+ xfree(ctx->boundary);
+ ctx->boundary = NULL;
+ }
+
+ /* If we already have an envelope, make sure that we also justify the
+ * "boundary" parameter of the envelope.
+ */
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return(0);
+ }
+ if (part->type != NULL) {
+ param = mm_content_gettypeparamobjbyname(part->type, "boundary");
+ if (param == NULL) {
+ param = mm_param_new();
+ param->name = xstrdup("boundary");
+ param->value = xstrdup(boundary);
+ mm_content_attachtypeparam(part->type, param);
+ } else {
+ if (param->value != NULL) {
+ xfree(param->value);
+ param->value = NULL;
+ }
+ param->value = xstrdup(boundary);
+ }
+ }
+
+ ctx->boundary = boundary;
+ return(0);
+}
+#endif
+
+/**
+ * Sets a preamble for the given MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context
+ * @param preamble The preamble to set
+ * @return 0 on success or -1 on failure
+ *
+ * This function sets the MIME preamble (the text between the end of envelope
+ * headers and the beginning of the first MIME part) for a given context
+ * object. If preamble is a NULL-pointer then the preamble will be deleted,
+ * and the currently associated memory will be free automagically.
+ */
+#if 0
+int
+mm_context_setpreamble(MM_CTX *ctx, char *preamble)
+{
+ if (ctx == NULL)
+ return(-1);
+
+ if (preamble == NULL) {
+ if (ctx->preamble != NULL) {
+ xfree(ctx->preamble);
+ }
+ ctx->preamble = NULL;
+ } else {
+ ctx->preamble = xstrdup(preamble);
+ }
+ return(0);
+}
+#endif
+
+#if 0
+char *
+mm_context_getpreamble(MM_CTX *ctx)
+{
+ if (ctx == NULL)
+ return(NULL);
+
+ return(ctx->preamble);
+}
+#endif
+
+/**
+ * Creates an ASCII message of the specified context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param flat Where to store the message
+ * @param flags Flags that affect the flattening process
+ *
+ * This function ``flattens'' a MiniMIME context, that is, it creates an ASCII
+ * represantation of the message the context contains. The flags can be a
+ * bitwise combination of the following constants:
+ *
+ * - MM_FLATTEN_OPAQUE : use opaque MIME parts when flattening
+ * - MM_FLATTEN_SKIPENVELOPE : do not flatten the envelope part
+ *
+ * Great care is taken to not produce invalid MIME output.
+ */
+#if 0
+int
+mm_context_flatten(MM_CTX *ctx, char **flat, size_t *length, int flags)
+{
+ struct mm_mimepart *part;
+ char *message;
+ char *flatpart;
+ char *buf;
+ char *envelope_headers;
+ size_t message_size;
+ size_t tmp_size;
+ char envelope;
+
+ mm_errno = MM_ERROR_NONE;
+ envelope = 1;
+
+ message = NULL;
+ message_size = 0;
+
+ if (ctx->boundary == NULL) {
+ if (mm_context_iscomposite(ctx)) {
+ mm_context_generateboundary(ctx);
+ }
+ }
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (envelope) {
+ if (flags & MM_FLATTEN_SKIPENVELOPE) {
+ envelope = 0;
+ if ((message = (char *) malloc(1)) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+ *message = '\0';
+ continue;
+ }
+
+ if (part->type == NULL && mm_context_countparts(ctx) > 1) {
+ if (mm_mimepart_setdefaultcontenttype(part, 1)
+ == -1) {
+ goto cleanup;
+ }
+ if (mm_context_generateboundary(ctx) == -1) {
+ goto cleanup;
+ }
+ ctx->messagetype = MM_MSGTYPE_MULTIPART;
+ }
+
+ if (mm_envelope_getheaders(ctx, &envelope_headers,
+ &tmp_size) == -1) {
+ return -1;
+ }
+
+ message = envelope_headers;
+ message_size = tmp_size;
+ envelope = 0;
+
+ if (ctx->preamble != NULL
+ && mm_context_iscomposite(ctx)
+ && !(flags & MM_FLATTEN_NOPREAMBLE)) {
+ tmp_size += strlen(ctx->preamble)
+ + (strlen("\r\n") * 2);
+ buf = (char *)xrealloc(message, tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+ message_size += tmp_size;
+ message = buf;
+ strlcat(message, "\r\n", message_size);
+ strlcat(message, ctx->preamble, message_size);
+ strlcat(message, "\r\n", message_size);
+ }
+ } else {
+ /* Enforce Content-Type if none exist */
+ if (part->type == NULL) {
+ if (mm_mimepart_setdefaultcontenttype(part, 0)
+ == -1) {
+ goto cleanup;
+ }
+ }
+
+ /* Append a boundary if necessary */
+ if (ctx->boundary != NULL) {
+ tmp_size = strlen(ctx->boundary) +
+ (strlen("\r\n") * 2) + strlen("--");
+
+ if (tmp_size < 1) {
+ return(-1);
+ }
+ if (message_size + tmp_size < 1) {
+ return(-1);
+ }
+
+ buf = (char *)xrealloc(message, message_size
+ + tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+ message_size += tmp_size;
+ message = buf;
+ strlcat(message, "\r\n", message_size);
+ strlcat(message, "--", message_size);
+ strlcat(message, ctx->boundary, message_size);
+ strlcat(message, "\r\n", message_size);
+ }
+
+ if (mm_mimepart_flatten(part, &flatpart, &tmp_size,
+ (flags & MM_FLATTEN_OPAQUE)) == -1) {
+ goto cleanup;
+ }
+
+ if (tmp_size < 1) {
+ goto cleanup;
+ }
+
+ buf = (char *) xrealloc(message, message_size
+ + tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ message_size += tmp_size;
+ message = buf;
+
+ strlcat(message, flatpart, message_size);
+ xfree(flatpart);
+ flatpart = NULL;
+ }
+ }
+
+ /* Append end boundary */
+ if (ctx->boundary != NULL && mm_context_iscomposite(ctx)) {
+ tmp_size = strlen(ctx->boundary) + (strlen("\r\n") * 2)
+ + (strlen("--") * 2);
+ buf = (char *)xrealloc(message, message_size + tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ message_size += tmp_size;
+ message = buf;
+ if (message[strlen(message)-1] != 13)
+ strlcat(message, "\r", message_size);
+ strlcat(message, "\n", message_size);
+ strlcat(message, "--", message_size);
+ strlcat(message, ctx->boundary, message_size);
+ strlcat(message, "--", message_size);
+ strlcat(message, "\r\n", message_size);
+ }
+
+ *flat = message;
+ *length = message_size;
+
+ return 0;
+
+cleanup:
+ if (message != NULL) {
+ xfree(message);
+ message = NULL;
+ }
+ return -1;
+}
+#endif
+
+/** @} */
diff --git a/trunk/main/minimime/mm_envelope.c b/trunk/main/minimime/mm_envelope.c
new file mode 100644
index 000000000..7400d3de2
--- /dev/null
+++ b/trunk/main/minimime/mm_envelope.c
@@ -0,0 +1,271 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/** @file mm_envelope.c
+ *
+ * This module contains functions for accessing a message's envelope. This
+ * are mainly wrapper functions for easy access.
+ */
+
+/** @defgroup envelope Accessing and manipulating a message's envelope
+ */
+
+/** @{
+ * @name Accessing and manipulating a message's envelope
+ */
+
+/**
+ * Gets an ASCII representation of all envelope headers
+ *
+ * @param ctx A valid MiniMIME context
+ * @param result Where to store the resulting ASCII headers
+ * @param length Where to store the length of the result
+ * @returns 0 on success or -1 on failure.
+ * @note Sets mm_errno on failure
+ *
+ * This is mainly a convinience function. It constructs an ASCII representation
+ * from all of the message's envelope headers and stores the result in headers.
+ * Memory is allocated dynamically, and the total length of the result is
+ * stored in length. This function takes care that the output is MIME conform,
+ * and folds long lines according to the MIME standard at position 78 of the
+ * string. It also nicely formats all MIME related header fields, such as
+ * the Content-Type header.
+ *
+ * Since the memory needed to store the result is allocated dynamically, one
+ * should take care of freeing it again when it's not needed anymore. If an
+ * error occurs, *result will be set to NULL, *length will be set to zero
+ * and mm_errno will be set to a reasonable value.
+ *
+ */
+int
+mm_envelope_getheaders(MM_CTX *ctx, char **result, size_t *length)
+{
+ struct mm_mimepart *part;
+ struct mm_mimeheader *hdr;
+ char *buf, *hdrbuf;
+ size_t headers_length, tmp_length;
+
+ headers_length = 1;
+ buf = NULL;
+
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return -1;
+ }
+
+ /* Initialize our buffer */
+ if ((buf = (char *)xmalloc(headers_length)) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+ *buf = '\0';
+
+ /* Store each envelope header */
+ TAILQ_FOREACH(hdr, &part->headers, next) {
+ tmp_length = strlen(hdr->name) + strlen(hdr->value)
+ + strlen(": \r\n");
+ hdrbuf = (char *) xrealloc(buf, headers_length + tmp_length);
+ if (hdrbuf == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+
+ headers_length += tmp_length;
+ buf = hdrbuf;
+
+ strlcat(buf, hdr->name, headers_length);
+ strlcat(buf, ": ", headers_length);
+ strlcat(buf, hdr->value, headers_length);
+ strlcat(buf, "\r\n", headers_length);
+ }
+
+ /* Construct and store MIME headers */
+ if (part->type != NULL) {
+ char *typebuf;
+ typebuf = mm_content_tostring(part->type);
+ if (typebuf == NULL) {
+ goto cleanup;
+ }
+ tmp_length = strlen(typebuf) + strlen("\r\n");
+
+ hdrbuf = (char *) xrealloc(buf, headers_length + tmp_length);
+ if (hdrbuf == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+
+ headers_length += tmp_length;
+ buf = hdrbuf;
+
+ strlcat(buf, typebuf, headers_length);
+ strlcat(buf, "\r\n", headers_length);
+ }
+
+ *result = buf;
+ *length = headers_length;
+
+ return 0;
+
+cleanup:
+ if (buf != NULL) {
+ xfree(buf);
+ buf = NULL;
+ }
+ *result = NULL;
+ *length = 0;
+ return -1;
+}
+
+/**
+ * Sets a header field in the envelope
+ *
+ * @param ctx A valid MiniMIME context
+ * @param name The name of the header field to set
+ * @param fmt A format string specifying the value of the header field
+ * @return 0 on success or -1 on failure
+ *
+ * This function generates a new MIME header and attaches it to the first
+ * MIME part (the envelope) found in the given context. If no part is
+ * attached already, the function will return an error. The function will
+ * store a copy of ``name'' as the header's name field, and dynamically
+ * allocate the memory needed to build the format string.
+ */
+int
+mm_envelope_setheader(MM_CTX *ctx, const char *name, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ struct mm_mimeheader *hdr;
+ struct mm_mimepart *part;
+
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return(-1);
+ }
+
+ hdr = mm_mimeheader_new();
+ if (hdr == NULL) {
+ return(-1);
+ }
+
+ hdr->name = xstrdup(name);
+
+ va_start(ap, fmt);
+ if (vasprintf(&buf, fmt, ap) == -1) {
+ goto cleanup;
+ }
+ va_end(ap);
+
+ hdr->value = buf;
+
+ if (mm_mimepart_attachheader(part, hdr) == -1) {
+ goto cleanup;
+ }
+
+ return(0);
+
+cleanup:
+ if (hdr != NULL) {
+ if (hdr->name != NULL) {
+ xfree(hdr->name);
+ hdr->name = NULL;
+ }
+ if (hdr->value != NULL) {
+ xfree(hdr->value);
+ hdr->value = NULL;
+ }
+ }
+ return(-1);
+}
+
+/**
+ * Gets the list of recipients for a MIME message
+ *
+ * @param ctx A valid MiniMIME context
+ * @param result Where to store the result
+ * @param length Where to store the length of the result
+ * @returns 0 on success or -1 on error
+ * @note Sets mm_errno on error
+ *
+ * This functions gets the list of recipients for a given MIME message. It
+ * does so by concatenating the "From" and "Cc" header fields, and storing
+ * the results in recipients. The memory needed to store the result is
+ * allocated dynamically, and the total length of the result is stored in
+ * length.
+ *
+ * One should take care to free() the result once it's not needed anymore.
+ */
+#if 0
+int
+mm_envelope_getrecipients(MM_CTX *ctx, char **result, size_t *length)
+{
+ struct mm_mimepart *part;
+ struct mm_mimeheader *to, *cc;
+ size_t recipients_length = 0;
+
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return -1;
+ }
+
+ to = mm_mimepart_getheaderbyname(part, "From", 0);
+ cc = mm_mimepart_getheaderbyname(part, "Cc", 0);
+
+ if (to == NULL || cc == NULL) {
+ *result = NULL;
+ *length = 0;
+ return -1;
+ }
+
+ if (to != NULL) {
+ recipients_length += strlen(to->value);
+ }
+ if (cc != NULL) {
+ recipients_length += strlen(cc->value);
+ }
+
+ return 0;
+}
+#endif
+
+/** @} */
diff --git a/trunk/main/minimime/mm_error.c b/trunk/main/minimime/mm_error.c
new file mode 100644
index 000000000..5b27bf724
--- /dev/null
+++ b/trunk/main/minimime/mm_error.c
@@ -0,0 +1,123 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/** @file mm_error.c
+ *
+ * This module contains functions for MiniMIME error information/manipulation
+ */
+
+/** @defgroup error MiniMIME error functions */
+
+/**
+ * Initializes the global error object
+ *
+ * @ingroup error
+ *
+ * This function initializes the global error object mm_error. This must be
+ * done when the library is initialized, and is automatically called from
+ * mm_init_library().
+ */
+void
+mm_error_init(void)
+{
+ mm_error.error_id = 0;
+ mm_error.error_where = 0;
+ mm_error.lineno = 0;
+ memset(&mm_error.error_msg, '\0', sizeof(mm_error.error_msg));
+}
+
+/**
+ * Sets a descriptive error message
+ *
+ * @param fmt The error message as format string
+ * @ingroup error
+ *
+ * This function is called from the various MiniMIME modules in case an
+ * error occured. Should never be called by the user.
+ */
+void
+mm_error_setmsg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(mm_error.error_msg, sizeof(mm_error.error_msg), fmt, ap);
+ va_end(ap);
+
+}
+
+void
+mm_error_setlineno(int lineno)
+{
+ mm_error.lineno = lineno;
+}
+
+/**
+ * Retrieves the current error message
+ *
+ * @return The currently set error message
+ * @ingroup error
+ *
+ * This function can be used to retrieve a descriptive error message for the
+ * current error, much like strerror() function of libc. When this function
+ * is called without an error being set, it returns the string "No error".
+ * The string returned does not need to be freed, since it is not dynamically
+ * allocated by the library.
+ */
+char *
+mm_error_string(void)
+{
+ if (mm_errno != MM_ERROR_ERRNO && mm_error.error_msg[0] == '\0') {
+ return "No error";
+ } else if (mm_errno == MM_ERROR_ERRNO) {
+ return strerror(errno);
+ } else {
+ return mm_error.error_msg;
+ }
+}
+
+int
+mm_error_lineno(void)
+{
+ return mm_error.lineno;
+}
diff --git a/trunk/main/minimime/mm_header.c b/trunk/main/minimime/mm_header.c
new file mode 100644
index 000000000..827c19cac
--- /dev/null
+++ b/trunk/main/minimime/mm_header.c
@@ -0,0 +1,213 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/** @file mm_header.c
+ *
+ * This module contains functions for manipulating MIME headers
+ */
+
+/**
+ * Creates a new MIME header object
+ *
+ * @return A new and initialized MIME header object
+ * @see mm_mimeheader_free
+ *
+ * This function creates and initializes a new MIME header object, which must
+ * later be freed using mm_mimeheader_free()
+ */
+struct mm_mimeheader *
+mm_mimeheader_new(void)
+{
+ struct mm_mimeheader *header;
+
+ header = (struct mm_mimeheader *)xmalloc(sizeof(struct mm_mimeheader));
+
+ header->name = NULL;
+ header->value = NULL;
+ TAILQ_INIT(&header->params);
+
+ return header;
+}
+
+/**
+ * Frees a MIME header object
+ *
+ * @param header The MIME header object which to free
+ */
+void
+mm_mimeheader_free(struct mm_mimeheader *header)
+{
+ struct mm_param *param;
+ assert(header != NULL);
+
+ if (header->name != NULL) {
+ xfree(header->name);
+ header->name = NULL;
+ }
+ if (header->value != NULL) {
+ xfree(header->value);
+ header->value = NULL;
+ }
+
+ TAILQ_FOREACH(param, &header->params, next) {
+ TAILQ_REMOVE(&header->params, param, next);
+ mm_param_free(param);
+ }
+xfree(header);
+ header = NULL;
+}
+
+/**
+ * Creates a new MIME header, but does no checks whatsoever (create as-is)
+ */
+struct mm_mimeheader *
+mm_mimeheader_generate(const char *name, const char *value)
+{
+ struct mm_mimeheader *header;
+
+ header = mm_mimeheader_new();
+
+ header->name = xstrdup(name);
+ header->value = xstrdup(value);
+
+ return header;
+}
+
+/**
+ * Attaches a parameter to a MimeHeader object
+ *
+ * @param hdr The target MimeHeader object
+ * @param param The parameter to attach
+ * @return 0 on success and -1 on failure
+ * @ingroup mimeheader
+ */
+int
+mm_mimeheader_attachparam(struct mm_mimeheader *hdr, struct mm_param *param)
+{
+ assert(hdr != NULL);
+ assert(param != NULL);
+
+ if (TAILQ_EMPTY(&hdr->params)) {
+ TAILQ_INSERT_HEAD(&hdr->params, param, next);
+ } else {
+ TAILQ_INSERT_TAIL(&hdr->params, param, next);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Gets a parameter value from a MimeHeader object.
+ *
+ * @param hdr the MimeHeader object
+ * @param name the name of the parameter to retrieve
+ * @return The value of the parameter on success or a NULL pointer on failure
+ * @ingroup mimeheader
+ */
+char *
+mm_mimeheader_getparambyname(struct mm_mimeheader *hdr, const char *name)
+{
+ struct mm_param *param;
+
+ assert(hdr != NULL);
+
+ TAILQ_FOREACH(param, &hdr->params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param->value;
+ }
+ }
+
+ return NULL;
+}
+
+int
+mm_mimeheader_uncomment(struct mm_mimeheader *header)
+{
+ char *new;
+
+ assert(header != NULL);
+ assert(header->name != NULL);
+ assert(header->value != NULL);
+
+ new = mm_uncomment(header->value);
+ if (new == NULL)
+ return -1;
+
+ xfree(header->value);
+ header->value = new;
+
+ return 0;
+}
+
+int
+mm_mimeheader_uncommentbyname(struct mm_mimepart *part, const char *name)
+{
+ struct mm_mimeheader *header;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if (!strcasecmp(header->name, name)) {
+ return mm_mimeheader_uncomment(header);
+ }
+ }
+
+ /* Not found */
+ return -1;
+}
+
+int
+mm_mimeheader_uncommentall(struct mm_mimepart *part)
+{
+ struct mm_mimeheader *header;
+ int ret, r;
+
+ ret = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if ((r = mm_mimeheader_uncomment(header)) == -1) {
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
diff --git a/trunk/main/minimime/mm_init.c b/trunk/main/minimime/mm_init.c
new file mode 100644
index 000000000..2ec37db6b
--- /dev/null
+++ b/trunk/main/minimime/mm_init.c
@@ -0,0 +1,65 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+int mm_errno;
+struct mm_error_data mm_error;
+static int mm_initialized;
+struct mm_codecs codecs;
+
+int
+mm_library_init(void)
+{
+ assert(mm_initialized != 1);
+
+ mm_errno = MM_ERROR_NONE;
+ mm_initialized = 1;
+
+ SLIST_INIT(&codecs);
+
+ mm_error_init();
+
+ return 0;
+}
+
+int
+mm_library_isinitialized(void)
+{
+ return mm_initialized;
+}
diff --git a/trunk/main/minimime/mm_internal.h b/trunk/main/minimime/mm_internal.h
new file mode 100644
index 000000000..155591487
--- /dev/null
+++ b/trunk/main/minimime/mm_internal.h
@@ -0,0 +1,65 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file mm_internal.h
+ * Data definitions for MiniMIME
+ */
+#ifndef _MM_INTERNAL_H_INCLUDED
+#define _MM_INTERNAL_H_INCLUDED
+
+#include "mm.h"
+
+#define debugp(m, ...) do { \
+ fprintf(stderr, "%s:%d:: ", __FILE__, __LINE__); \
+ fprintf(stderr, m, ## __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ fflush(stderr); \
+} while (0);
+
+/**
+ * @{
+ * @name Utility functions
+ */
+#ifndef __HAVE_LEAK_DETECTION
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t);
+void xfree(void *);
+char *xstrdup(const char *);
+#endif
+
+char *xstrsep(char **, const char *);
+
+/* THIS FILE IS INTENTIONALLY LEFT BLANK */
+
+#endif /* ! _MM_INTERNAL_H_INCLUDED */
diff --git a/trunk/main/minimime/mm_mem.c b/trunk/main/minimime/mm_mem.c
new file mode 100644
index 000000000..6c915e564
--- /dev/null
+++ b/trunk/main/minimime/mm_mem.c
@@ -0,0 +1,170 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+#ifdef __HAVE_LEAK_DETECTION
+# include "mm_mem.h"
+
+static struct MM_chunks chunks;
+
+void *
+MM_malloc(size_t size, char *filename, int line)
+{
+ struct MM_mem_chunk *chunk;
+ void *pointer;
+
+ pointer = malloc(size);
+ if (pointer == NULL)
+ fdprintf(stderr, "INFO: malloc");
+
+ chunk = (struct MM_mem_chunk *)malloc(sizeof(struct MM_mem_chunk));
+ if (chunk == NULL)
+ fdprintf(stderr, "INFO: malloc");
+
+ chunk->address = pointer;
+ chunk->size = size;
+ chunk->filename = filename;
+ chunk->line = line;
+
+ TAILQ_INSERT_TAIL(&chunks, chunk, next);
+
+ return pointer;
+}
+
+char *
+MM_strdup(const char *s, char *filename, int line)
+{
+ char *r;
+
+ r = (char *)MM_malloc(strlen(s)+1, filename, line);
+ strlcpy(r, s, strlen(s) + 1);
+ if (strlen(r) != strlen(s)) {
+ debugp("%d:%d", strlen(s), strlen(r));
+ }
+ return r;
+
+}
+
+void *
+MM_realloc(void *p, size_t new_size, char *filename, int line)
+{
+ void *r;
+ void *a;
+ struct MM_mem_chunk *chunk;
+ struct MM_mem_chunk *last;
+
+ a = p;
+ chunk = NULL;
+ last = NULL;
+
+ assert(new_size > 0);
+
+ TAILQ_FOREACH(chunk, &chunks, next) {
+ if (chunk->address == p) {
+ last = chunk;
+ }
+ }
+
+ if (last == NULL) {
+ debugp("MM_realloc: did not find chunk at %p (%s:%d) "
+ ", creating new", p, filename, line);
+ return MM_malloc(new_size, filename, line);
+ }
+
+ r = realloc(p, new_size);
+ if (r == NULL)
+ return NULL;
+
+ last->address = r;
+ last->size = new_size;
+ last->filename = filename;
+ last->line = line;
+
+ return r;
+}
+
+void
+MM_free(void *pointer, char *filename, int line, char *name)
+{
+ struct MM_mem_chunk *chunk, *nxt;
+
+ for (chunk = TAILQ_FIRST(&chunks); chunk != TAILQ_END(&chunks);
+ chunk = nxt) {
+ nxt = TAILQ_NEXT(&chunks, next);
+ if (chunk->address == pointer) {
+ TAILQ_REMOVE(&chunks, chunk, next);
+ free(chunk->address);
+ free(chunk);
+ return;
+ }
+ }
+
+ debugp("FREE: did not find storage %s (at %p), %s:%d", name, pointer,
+ filename, line);
+}
+
+void
+MM_leakd_flush(void)
+{
+ debugp("flushing memory informations");
+ while (!TAILQ_EMPTY(&chunks))
+ SLIST_REMOVE_HEAD(&chunks, next);
+}
+
+void
+MM_leakd_printallocated(void)
+{
+ struct MM_mem_chunk *chunk;
+ debugp("printing dynamic memory allocations");
+ TAILQ_FOREACH(chunk, &chunks, next) {
+ debugp(" chunk: %p (alloc'ed at %s:%d, size %d)\n",
+ chunk->address, chunk->filename, chunk->line, chunk->size);
+ }
+}
+
+void
+MM_leakd_init(void)
+{
+ TAILQ_INIT(&chunks);
+}
+
+#endif /* !__HAVE_LEAK_DETECTOR */
diff --git a/trunk/main/minimime/mm_mem.h b/trunk/main/minimime/mm_mem.h
new file mode 100644
index 000000000..34840d033
--- /dev/null
+++ b/trunk/main/minimime/mm_mem.h
@@ -0,0 +1,32 @@
+#ifndef __MEM_H
+#define __MEM_H
+
+#ifdef __HAVE_LEAK_DETECTION
+
+#define NAMEOF(v) #v
+#define xmalloc(x) MM_malloc(x, __FILE__, __LINE__)
+#define xfree(x) MM_free(x, __FILE__, __LINE__, NAMEOF(x))
+#define xstrdup(x) MM_strdup(x, __FILE__, __LINE__)
+#define xrealloc(x, y) MM_realloc(x, y, __FILE__, __LINE__)
+
+TAILQ_HEAD(MM_chunks, MM_mem_chunk);
+
+struct MM_mem_chunk {
+ void *address;
+ const char *filename;
+ uint32_t line;
+ size_t size;
+
+ TAILQ_ENTRY(MM_mem_chunk) next;
+};
+
+void *MM_malloc(size_t, char *, int);
+void *MM_realloc(void *, size_t, char *, int);
+void MM_free(void *, char *, int, char *);
+char *MM_strdup(const char *, char *, int);
+void MM_leakd_init(void);
+void MM_leakd_printallocated(void);
+void MM_leakd_flush(void);
+
+#endif /* __HAVE_LEAK_DETECTION */
+#endif /* ! HAVE_MEM_H */
diff --git a/trunk/main/minimime/mm_mimepart.c b/trunk/main/minimime/mm_mimepart.c
new file mode 100644
index 000000000..631debae0
--- /dev/null
+++ b/trunk/main/minimime/mm_mimepart.c
@@ -0,0 +1,659 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/** @file mm_mimepart.c
+ *
+ * This module contains functions for manipulating MIME header objects.
+ */
+
+/** @defgroup mimepart Accessing and manipulating MIME parts
+ *
+ * MIME parts, also called entities, represent the structure of a MIME
+ * message. ``Normal'' internet messages have only a single part, and
+ * are called ``flat'' messages. Multipart messages have more then one
+ * part, and each MIME part can have it's own subset of headers.
+ *
+ * Provided here are functions to easily access all informations from
+ * a MIME part, including their specific headers and bodies.
+ */
+
+/** @{
+ * @name Creating and destroying MIME parts
+ */
+
+/**
+ * Allocates memory for a new mm_mimepart structure and initializes it.
+ *
+ * @return A pointer to a struct of type mm_mimeheader or NULL on failure
+ * @see mm_mimepart_free
+ * @note The memory must be freed by using mm_mimepart_free() later on.
+ */
+struct mm_mimepart *
+mm_mimepart_new(void)
+{
+ struct mm_mimepart *part;
+
+ part = (struct mm_mimepart *)xmalloc(sizeof(struct mm_mimepart));
+
+ TAILQ_INIT(&part->headers);
+
+ part->opaque_length = 0;
+ part->opaque_body = NULL;
+
+ part->length = 0;
+ part->body = NULL;
+
+ part->type = NULL;
+
+ return part;
+}
+
+/**
+ * Creates a MIME part from a file
+ *
+ * @param filename The name of the file to create the MIME part from
+ * @return A pointer to a new MIME part object
+ *
+ * This function creates a new MIME part object from a file. The object should
+ * be freed using mm_mimepart_free() later on. This function does NOT set the
+ * Content-Type and neither does any encoding work.
+ */
+struct mm_mimepart *
+mm_mimepart_fromfile(const char *filename)
+{
+ int fd;
+ char *data;
+ size_t r;
+ struct stat st;
+ struct mm_mimepart *part;
+
+ mm_errno = MM_ERROR_NONE;
+
+ if ((fd = open(filename, O_RDONLY)) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ return NULL;
+ }
+
+ if ((stat(filename, &st)) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ close(fd);
+ return NULL;
+ }
+
+ data = xmalloc(st.st_size);
+ r = read(fd, data, st.st_size);
+ if (r != st.st_size) {
+ mm_errno = MM_ERROR_ERRNO;
+ close(fd);
+ return(NULL);
+ }
+
+ data[r] = '\0';
+ close(fd);
+
+ part = mm_mimepart_new();
+ part->length = r;
+ part->body = data;
+
+ return part;
+}
+
+
+/**
+ * Frees all memory allocated by a mm_mimepart object.
+ *
+ * @param part A pointer to an allocated mm_mimepart object
+ * @see mm_mimepart_new
+ */
+void
+mm_mimepart_free(struct mm_mimepart *part)
+{
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ mm_mimeheader_free(header);
+ TAILQ_REMOVE(&part->headers, header, next);
+ }
+
+ if (part->opaque_body != NULL) {
+ xfree(part->opaque_body);
+ part->opaque_body = NULL;
+ part->body = NULL;
+ } else if (part->body != NULL) {
+ xfree(part->body);
+ part->body = NULL;
+ }
+
+ if (part->type != NULL) {
+ mm_content_free(part->type);
+ part->type = NULL;
+ }
+
+ xfree(part);
+ part = NULL;
+}
+
+/** @} */
+
+/** @{
+ * @name Accessing the MIME part's mail header
+ */
+
+/**
+ * Attaches a mm_mimeheader object to a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param header A valid MIME header object
+ * @return 0 if successfull or -1 if the header could not be attached
+ */
+int
+mm_mimepart_attachheader(struct mm_mimepart *part, struct mm_mimeheader *header)
+{
+ assert(part != NULL);
+ assert(header != NULL);
+
+ if (TAILQ_EMPTY(&part->headers)) {
+ TAILQ_INSERT_HEAD(&part->headers, header, next);
+ } else {
+ TAILQ_INSERT_TAIL(&part->headers, header, next);
+ }
+
+ return(0);
+}
+
+/**
+ * Retrieves the number of MIME headers available in a MIME part
+ *
+ * @param part A valid MIME part object
+ * @return The number of MIME headers within the MIME part
+ */
+int
+mm_mimepart_countheaders(struct mm_mimepart *part)
+{
+ int found;
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ found = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ found++;
+ }
+
+ return found;
+}
+
+/**
+ * Retrieves the number of MIME headers with a given name in a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param name The name of the MIME header which to count for
+ * @return The number of MIME headers within the MIME part
+ */
+int
+mm_mimepart_countheaderbyname(struct mm_mimepart *part, const char *name)
+{
+ int found;
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ found = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if (strcasecmp(header->name, name) == 0) {
+ found++;
+ }
+ }
+
+ return found;
+}
+
+/**
+ * Get a MIME header object from a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param name The name of the MIME header which to retrieve
+ * @param idx Which header field to get (in case of multiple headers of the
+ * same name).
+ * @return A pointer to the requested MIME header on success, or NULL if there
+ * either isn't a header with the requested name or idx is out of
+ * range.
+ */
+struct mm_mimeheader *
+mm_mimepart_getheaderbyname(struct mm_mimepart *part, const char *name, int idx)
+{
+ struct mm_mimeheader *header;
+ int curidx;
+
+ curidx = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if (!strcasecmp(header->name, name)) {
+ if (curidx == idx)
+ return header;
+ else
+ curidx++;
+ }
+ }
+
+ /* Not found */
+ return NULL;
+}
+
+/**
+ * Gets the value of a MIME header object
+ *
+ * @param part A valid MIME part object
+ * @param name The name of the header field to get the value from
+ * @param idx The index of the header field to get, in case there are multiple
+ * headers with the same name.
+ * @return A pointer to the requested value on success, or NULL if there either
+ * isn't a header with the requested name or idx is out of range.
+ *
+ */
+const char *
+mm_mimepart_getheadervalue(struct mm_mimepart *part, const char *name, int idx)
+{
+ struct mm_mimeheader *header;
+
+ header = mm_mimepart_getheaderbyname(part, name, idx);
+ if (header == NULL)
+ return NULL;
+ else
+ return header->value;
+}
+
+/**
+ * Initializes a header loop for a given MIME part
+ *
+ * @param part A valid MIME part object
+ * @param id The address of a MIME header object (to allow reentrance)
+ * @return 0 on success or -1 on failure
+ * @see mm_mimepart_headers_next
+ *
+ * Looping through headers can be done in the following way:
+ *
+ * @code
+ * struct mm_mimeheader *header, *lheader;
+ *
+ * mm_mimepart_headers_start(part, &lheader);
+ *
+ * while ((header = mm_mimepart_headers_next(part, &lheader)) != NULL) {
+ * printf("%s: %s\n", header->name, header->value);
+ * }
+ *
+ * @endcode
+ *
+ * For convienience, the macro mm_mimepart_headers_foreach() can be used to
+ * loop through headers in a one-shot manner.
+ */
+int
+mm_mimepart_headers_start(struct mm_mimepart *part, struct mm_mimeheader **id)
+{
+ assert(part != NULL);
+
+ if (TAILQ_EMPTY(&part->headers)) {
+ return -1;
+ }
+ *id = NULL;
+ return 0;
+}
+
+/**
+ * Returns the next MIME header of a given MIME part object
+ *
+ * @param part A valid MIME part object
+ * @param id A previously initialized MIME header object
+ * @return A pointer to the MIME header object or NULL if end of headers was
+ * reached.
+ * @see mm_mimepart_headers_start
+ */
+struct mm_mimeheader *
+mm_mimepart_headers_next(struct mm_mimepart *part, struct mm_mimeheader **id)
+{
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ if (*id == NULL) {
+ header = TAILQ_FIRST(&part->headers);
+ } else {
+ header = TAILQ_NEXT(*id, next);
+ }
+ *id = header;
+
+ return header;
+}
+
+/** @} */
+
+/** @{
+ * @name Accessing and manipulating the MIME part's body
+ */
+
+/**
+ * Gets the pointer to the MIME part's body data
+ *
+ * @param part A valid MIME part object
+ * @param opaque Whether to get the opaque part or not
+ * @return A pointer to the MIME part's body
+ * @see mm_mimepart_setbody
+ *
+ */
+char *
+mm_mimepart_getbody(struct mm_mimepart *part, int opaque)
+{
+ assert(part != NULL);
+
+ if (opaque)
+ return part->opaque_body;
+ else
+ return part->body;
+}
+
+/**
+ * Sets the MIME part's body data
+ *
+ * @param part A valid MIME part object
+ * @param data A pointer to the data which to set
+ * @see mm_mimepart_getbody
+ *
+ * This functions sets the body data for a given MIME part. The string pointed
+ * to by data must be NUL-terminated. The data is copied into the MIME part's
+ * body, and thus, the memory pointed to by data can be freed after the
+ * operation.
+ */
+#if 0
+void
+mm_mimepart_setbody(struct mm_mimepart *part, const char *data, int opaque)
+{
+ assert(part != NULL);
+ assert(data != NULL);
+
+ if (opaque) {
+ part->opaque_body = xstrdup(data);
+ part->body = part->opaque_body;
+ } else {
+ part->body = xstrdup(data);
+ }
+ part->length = strlen(data);
+}
+#endif
+
+/**
+ * Gets the length of a given MIME part object
+ *
+ * @param part A valid MIME part object
+ * @returns The size of the part's body in byte.
+ *
+ * This function returns the total length of the given MIME part's body. The
+ * length does not include the headers of the MIME parts. If the function
+ * returns 0, no body part is set currently.
+ */
+size_t
+mm_mimepart_getlength(struct mm_mimepart *part)
+{
+ assert(part != NULL);
+
+ return part->length;
+}
+
+
+/**
+ * Decodes a MIME part according to it's encoding using MiniMIME codecs
+ *
+ * @param A valid MIME part object
+ * @return 0 if the MIME part could be successfully decoded or -1 if not
+ * @note Sets mm_errno on error
+ *
+ * This function decodes the body of a MIME part with a registered decoder
+ * according to it's Content-Transfer-Encoding header field.
+ */
+char *
+mm_mimepart_decode(struct mm_mimepart *part)
+{
+ extern struct mm_codecs codecs;
+ struct mm_codec *codec;
+ void *decoded;
+
+ assert(part != NULL);
+ assert(part->type != NULL);
+
+ decoded = NULL;
+
+ /* No encoding associated */
+ if (part->type->encstring == NULL)
+ return NULL;
+
+ /* Loop through codecs and find a suitable one */
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (!strcasecmp(part->type->encstring, codec->encoding)) {
+ decoded = codec->decoder((char *)part->body);
+ break;
+ }
+ }
+
+ return decoded;
+}
+
+/**
+ * Creates an ASCII representation of the given MIME part
+ *
+ * @param part A valid MIME part object
+ * @param result Where to store the result
+ * @param length Where to store the length of the result
+ * @param opaque Whether to use the opaque MIME part
+ * @returtn 0 on success or -1 on error.
+ * @see mm_context_flatten
+ *
+ * This function creates an ASCII representation of a given MIME part. It will
+ * dynamically allocate the memory needed and stores the result in the memory
+ * region pointed to by result. The length of the result will be stored in
+ * length. If opaque is set to 1, mm_mimepart_flatten will store an opaque
+ * version of the MIME part in result, which means no headers will be created
+ * or sanitized. This is particulary useful if the part is digitally signed by
+ * e.g. PGP, and the signature spans the header fields of the part in question.
+ *
+ */
+int
+mm_mimepart_flatten(struct mm_mimepart *part, char **result, size_t *length,
+ int opaque)
+{
+ size_t part_length;
+ char *buf;
+ char *ct_hdr;
+
+ *result = NULL;
+ *length = 0;
+ buf = NULL;
+ ct_hdr = NULL;
+ part_length = 0;
+
+ if (opaque && part->opaque_body != NULL) {
+ part_length = strlen(part->opaque_body);
+ *result = xstrdup(part->opaque_body);
+ *length = part_length;
+ return(0);
+ } else {
+ if (part->type == NULL) {
+ return(-1);
+ }
+
+ ct_hdr = mm_content_tostring(part->type);
+ if (ct_hdr == NULL) {
+ return(-1);
+ }
+
+ part_length += strlen(ct_hdr) + 2;
+ part_length += strlen("\r\n") * 2;
+ part_length += strlen(part->body);
+
+ if (part_length < 0) {
+ goto cleanup;
+ }
+
+ buf = (char *) xmalloc(part_length);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ snprintf(buf, part_length,
+ "%s\r\n\r\n%s\r\n",
+ ct_hdr,
+ part->body);
+
+ xfree(ct_hdr);
+ ct_hdr = NULL;
+
+ *result = buf;
+ *length = part_length;
+ }
+
+ return(0);
+
+cleanup:
+ if (ct_hdr != NULL) {
+ xfree(ct_hdr);
+ ct_hdr = NULL;
+ }
+ if (buf != NULL) {
+ xfree(buf);
+ buf = NULL;
+ }
+
+ *result = NULL;
+ *length = 0;
+
+ return -1;
+}
+
+/**
+ * Sets the default Content-Type for a given MIME part
+ *
+ * @param part A valid MIME part object
+ * @param part Whether the Content-Type should be for composite or not
+ * @return 0 on success or -1 on failure
+ *
+ * This function sets a default Content-Type according to RFC 2045 with a value
+ * of "text/plain; charset="us-ascii"". This function should only be used if
+ * the MIME part in question does not have a valid Content-Type specification.
+ */
+int
+mm_mimepart_setdefaultcontenttype(struct mm_mimepart *part, int composite)
+{
+ struct mm_content *type;
+ struct mm_param *param;
+
+ if (part == NULL) {
+ return(-1);
+ }
+
+ if (part->type != NULL) {
+ mm_content_free(part->type);
+ part->type = NULL;
+ }
+
+ type = mm_content_new();
+ if (composite) {
+ type->maintype = xstrdup("multipart");
+ type->subtype = xstrdup("mixed");
+ } else {
+ type->maintype = xstrdup("text");
+ type->subtype = xstrdup("plain");
+ param = mm_param_new();
+ param->name = xstrdup("charset");
+ param->value = xstrdup("us-ascii");
+ mm_content_attachtypeparam(type, param);
+ }
+
+ mm_mimepart_attachcontenttype(part, type);
+
+ return (0);
+}
+
+/** @{
+ * @name Accessing the MIME part's Content-Type information
+ */
+
+/**
+ * Attaches a context type object to a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param ct The content type object to attach
+ * @return Nothing
+ *
+ * This function attaches a Content-Type object to a MIME part. It does not
+ * care whether the Content-Type suites the actual content in the MIME part,
+ * so the programmer should take care of that.
+ */
+void
+mm_mimepart_attachcontenttype(struct mm_mimepart *part, struct mm_content *ct)
+{
+ part->type = ct;
+}
+
+/**
+ * Gets the Content-Type of a given MIME part object
+ *
+ * @param part A valid MIME part object
+ * @return The Content-Type object of the specified MIME part
+ *
+ * This function returns a pointer to the Content-Type object of the given
+ * MIME part. This pointer might be set to NULL, indicating that there is
+ * no Content-Type object for the given MIME part currently.
+ */
+struct mm_content *
+mm_mimepart_getcontent(struct mm_mimepart *part)
+{
+ assert(part != NULL);
+
+ return part->type;
+}
+
+/** @} */
diff --git a/trunk/main/minimime/mm_mimeutil.c b/trunk/main/minimime/mm_mimeutil.c
new file mode 100644
index 000000000..4a5cc35e9
--- /dev/null
+++ b/trunk/main/minimime/mm_mimeutil.c
@@ -0,0 +1,137 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2004 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+#define MM_DATE_LENGTH 50
+
+static const char boundary_charset[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.=";
+
+/** @file mm_mimeutil.c
+ *
+ * This module contains various MIME related utility functions.
+ */
+
+/** @defgroup mimeutil MIME related utility functions */
+
+/**
+ * Generates an RFC 2822 conform date string
+ *
+ * @param timezone Whether to include timezone information
+ * @returns A pointer to the actual date string
+ * @note The pointer returned must be freed some time
+ *
+ * This function generates an RFC 2822 conform date string to use in message
+ * headers. It allocates memory to hold the string and returns a pointer to
+ * it. The generated date is in the format (example):
+ *
+ * Thu, 25 December 2003 16:35:22 +0100 (CET)
+ *
+ * This function dynamically allocates memory and returns a pointer to it.
+ * This memory should be released with free() once not needed anymore.
+ */
+#if 0
+int
+mm_mimeutil_gendate(char **result)
+{
+ time_t curtime;
+ struct tm *curtm;
+
+ if (result != NULL) {
+ curtime = time(NULL);
+ curtm = localtime(&curtime);
+ if ((*result = (char *) malloc(MM_DATE_LENGTH)) == NULL) {
+ return(-1);
+ }
+ return(strftime(*result, MM_DATE_LENGTH,
+ "%a, %d %b %G %T %z (%Z)", curtm));
+ } else {
+ return(-1);
+ }
+}
+#endif
+
+int
+mm_mimeutil_genboundary(char *prefix, size_t length, char **result)
+{
+ size_t total;
+ size_t preflen;
+ struct timeval curtm;
+ int i;
+ int pos;
+
+ total = 0;
+ preflen = 0;
+
+ if (result == NULL) {
+ return(-1);
+ }
+ *result = NULL;
+
+ gettimeofday(&curtm, NULL);
+ srandom(curtm.tv_usec);
+
+ if (prefix != NULL) {
+ total = strlen(prefix);
+ preflen = total;
+ }
+
+ total += length;
+
+ if ((*result = (char *) xmalloc(total + 1)) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(-1);
+ }
+
+ *result = '\0';
+
+ if (prefix != NULL) {
+ strlcat(*result, prefix, total);
+ }
+
+ for (i = 0; i < length - 1; i++) {
+ pos = random() % strlen(boundary_charset);
+ *result[i + preflen] = boundary_charset[pos];
+ }
+ *result[total] = '\0';
+
+ return (0);
+}
diff --git a/trunk/main/minimime/mm_param.c b/trunk/main/minimime/mm_param.c
new file mode 100644
index 000000000..ee4147bf5
--- /dev/null
+++ b/trunk/main/minimime/mm_param.c
@@ -0,0 +1,225 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/**
+ * @file mm_param.c
+ *
+ * Functions to manipulate MIME parameters
+ */
+
+/** @defgroup param Accessing and manipulating MIME parameters */
+
+/** @{
+ *
+ * @name Functions for manipulating MIME parameters
+ *
+ * MIME parameters are properties attached to certain MIME headers, such as
+ * Content-Type and Content-Disposition. MIME parameters have a textual
+ * representations as in <i>name=value</i>. They contain important information
+ * about the MIME structure of a message, such as the boundary string used,
+ * which charset was used to encode the message and so on. This module
+ * provides simple to use functions to query or set MIME parameters.
+ *
+ * Each MIME header may hold an arbitrary amount of such parameters, which
+ * are delimeted by each other with a semicolon.
+ */
+
+/**
+ * Creates a new object to hold a MIME parameter.
+ *
+ * @return An object representing a MIME parameter
+ * @see mm_param_free
+ * @note The allocated memory must later be freed using mm_param_free()
+ */
+struct mm_param *
+mm_param_new(void)
+{
+ struct mm_param *param;
+
+ param = (struct mm_param *)xmalloc(sizeof(struct mm_param));
+
+ param->name = NULL;
+ param->value = NULL;
+
+ return param;
+}
+
+/**
+ * Releases all memory associated with a MIME parameter object.
+ *
+ * @param param A valid MIME parameter object to be freed
+ * @return Nothing
+ * @see mm_param_new
+ */
+void
+mm_param_free(struct mm_param *param)
+{
+ assert(param != NULL);
+
+ if (param->name != NULL) {
+ xfree(param->name);
+ param->name = NULL;
+ }
+ if (param->value != NULL) {
+ xfree(param->value);
+ param->value = NULL;
+ }
+ xfree(param);
+}
+
+/**
+ * Generates a new Content-Type parameter with the given name and value
+ *
+ * @param name The name of the MIME parameter
+ * @param value The value of the MIME parameter
+ * @returns A new MIME parameter object
+ * @see mm_param_free
+ * @see mm_param_new
+ *
+ * This function generates a new MIME parameter, with the name
+ * and value given as the arguments. The needed memory for the operation
+ * is allocated dynamically. It stores a copy of name and value in the
+ * actual object, so the memory holding the arguments can safely be
+ * freed after successfull return of this function.
+ */
+#if 0
+struct mm_param *
+mm_param_generate(const char *name, const char *value)
+{
+ struct mm_param *param;
+
+ param = mm_param_new();
+
+ param->name = xstrdup(name);
+ param->value = xstrdup(value);
+
+ return param;
+}
+#endif
+
+/**
+ * Sets the name of the given MIME parameter
+ *
+ * @param param A valid MIME parameter object
+ * @param name The new name of the parameter
+ * @param copy If set to > 0, copy the value stored in name
+ * @returns The address of the previous name for passing to free()
+ */
+#if 0
+char *
+mm_param_setname(struct mm_param *param, const char *name, int copy)
+{
+ char *retadr;
+ assert(param != NULL);
+
+ retadr = param->name;
+
+ if (copy)
+ param->name = xstrdup(name);
+ else
+ param->name = (char *)name;
+
+ return retadr;
+}
+#endif
+
+/**
+ * Sets the value of the given MIME parameter
+ *
+ * @param param A valid MIME parameter object
+ * @param name The new value for the parameter
+ * @param copy If set to > 0, copy the value stored in value
+ * @returns The address of the previous value for passing to free()
+ */
+#if 0
+char *
+mm_param_setvalue(struct mm_param *param, const char *value, int copy)
+{
+ char *retadr;
+ assert(param != NULL);
+
+ retadr = param->value;
+
+ if (copy)
+ param->value = xstrdup(value);
+ else
+ param->value = (char *)value;
+
+ return retadr;
+}
+#endif
+
+/**
+ * Gets the name of a MIME parameter object
+ *
+ * @param param A valid MIME parameter object
+ * @returns The name of the MIME parameter
+ */
+#if 0
+const char *
+mm_param_getname(struct mm_param *param)
+{
+ assert(param != NULL);
+ return param->name;
+}
+#endif
+
+/**
+ * Gets the value of a MIME parameter object
+ *
+ * @param param A valid MIME parameter object
+ * @returns The value of the MIME parameter
+ */
+#if 0
+const char *
+mm_param_getvalue(struct mm_param *param)
+{
+ assert(param != NULL);
+ return param->value;
+}
+#endif
+
+/** @} */
diff --git a/trunk/main/minimime/mm_parse.c b/trunk/main/minimime/mm_parse.c
new file mode 100644
index 000000000..4223d99e0
--- /dev/null
+++ b/trunk/main/minimime/mm_parse.c
@@ -0,0 +1,168 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+#include "mimeparser.h"
+#include "mimeparser.tab.h"
+
+/** @file mm_parse.c
+ *
+ * Functions to parse MIME messages
+ */
+
+/**
+ * Parses a NUL-terminated string into a MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param text The NUL-terminated string to parse
+ * @param parsemode The parsemode
+ * @param flags The flags to pass to the parser
+ * @returns 0 on success or -1 on failure
+ * @note Sets mm_errno if an error occurs
+ *
+ * This function parses a MIME message, stored in the memory region pointed to
+ * by text (must be NUL-terminated) according to the parseflags and stores the
+ * results in the MiniMIME context specified by ctx.
+ *
+ * The following modes can be used to specify how the message should be
+ * parsed:
+ *
+ * - MM_PARSE_STRICT: Do not tolerate MIME violations
+ * - MM_PARSE_LOOSE: Tolerate as much MIME violations as possible
+ *
+ * The context needs to be initialized before using mm_context_new() and may
+ * be freed using mm_context_free().
+ */
+int
+mm_parse_mem(MM_CTX *ctx, const char *text, int parsemode, int flags)
+{
+ void *yyscanner;
+ int res;
+ struct parser_state pstate;
+
+ pstate.ctx = ctx;
+ pstate.parsemode = parsemode;
+
+ mimeparser_yylex_init(&yyscanner);
+ PARSER_initialize(&pstate, yyscanner);
+
+ PARSER_setbuffer(text, yyscanner);
+ PARSER_setfp(NULL, yyscanner);
+
+ res = mimeparser_yyparse(&pstate,yyscanner);
+ mimeparser_yylex_destroy(yyscanner);
+ return res;
+}
+
+/**
+ * Parses a file into a MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param filename The name of the file to parse
+ * @param parsemode The parsemode
+ * @param flags The flags to pass to the parser
+ * @returns 0 on success or -1 on failure
+ * @note Sets mm_errno if an error occurs
+ *
+ * This function parses a MIME message, stored in the filesystem according to
+ * the parseflags and stores the results in the MiniMIME context specified by
+ * ctx.
+ *
+ * The following modes can be used to specify how the message should be
+ * parsed:
+ *
+ * - MM_PARSE_STRICT: Do not tolerate MIME violations
+ * - MM_PARSE_LOOSE: Tolerate as much MIME violations as possible
+ *
+ * The context needs to be initialized before using mm_context_new() and may
+ * be freed using mm_context_free().
+ */
+int
+mm_parse_file(MM_CTX *ctx, const char *filename, int parsemode, int flags)
+{
+ FILE *fp;
+ int res;
+ void *yyscanner;
+ struct parser_state pstate;
+
+ mimeparser_yylex_init(&yyscanner);
+
+ if ((fp = fopen(filename, "r")) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return -1;
+ }
+
+ PARSER_setfp(fp,yyscanner);
+ PARSER_initialize(&pstate, yyscanner);
+
+ pstate.ctx = ctx;
+ pstate.parsemode = parsemode;
+
+ res = mimeparser_yyparse(&pstate,yyscanner);
+ mimeparser_yylex_destroy(yyscanner);
+ fclose(fp);
+ return res;
+}
+
+int
+mm_parse_fileptr(MM_CTX *ctx, FILE *f, int parsemode, int flags)
+{
+ int res;
+ void *yyscanner;
+ struct parser_state pstate;
+
+ mimeparser_yylex_init(&yyscanner);
+
+ PARSER_setfp(f, yyscanner);
+ PARSER_initialize(&pstate, yyscanner);
+
+ pstate.ctx = ctx;
+ pstate.parsemode = parsemode;
+
+ res = mimeparser_yyparse(&pstate,yyscanner);
+ mimeparser_yylex_destroy(yyscanner);
+
+ return res;
+}
diff --git a/trunk/main/minimime/mm_queue.h b/trunk/main/minimime/mm_queue.h
new file mode 100644
index 000000000..893e2fae2
--- /dev/null
+++ b/trunk/main/minimime/mm_queue.h
@@ -0,0 +1,508 @@
+/* $OpenBSD: queue.h,v 1.25 2004/04/08 16:08:21 henning Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
+ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/trunk/main/minimime/mm_util.c b/trunk/main/minimime/mm_util.c
new file mode 100644
index 000000000..90debcb6e
--- /dev/null
+++ b/trunk/main/minimime/mm_util.c
@@ -0,0 +1,412 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/** @file mm_util.c
+ *
+ * This module contains utility functions for the MiniMIME library
+ */
+
+/** @defgroup util General purpose utility functions */
+
+#ifndef __HAVE_LEAK_DETECTION
+/**
+ * Allocates a block of memory
+ *
+ * @param size The size of the memory region to allocate
+ * @return A pointer to the allocated memory region
+ * @ingroup util
+ *
+ * xmalloc() calls abort() if either the size argument is negative or the
+ * requested memory amount could not be allocated via an assert() call.
+ */
+void *
+xmalloc(size_t size)
+{
+ void *p;
+
+ assert(size > 0);
+ p = malloc(size);
+ assert(p != NULL);
+
+ return p;
+}
+
+/**
+ * realloc() wrapper
+ *
+ * @param p Pointer to a memory region which should be reallocated
+ * @param size The new size of the memory region
+ * @return A pointer to the reallocated memory region
+ * @ingroup util
+ *
+ * xrealloc() is a wrapper around realloc() which calls abort() if either the
+ * size argument is negative or the requested memory amount could not be
+ * allocated.
+ */
+void *
+xrealloc(void *p, size_t size)
+{
+ void *n;
+
+ assert(size > 0);
+ n = realloc(p, size);
+ assert(n != NULL);
+
+ return n;
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *p;
+
+ assert(str != NULL);
+ p = strdup(str);
+ assert(p != NULL);
+
+ return p;
+}
+
+void
+xfree(void *p)
+{
+ assert(p != NULL);
+ free(p);
+ p = NULL;
+ assert(p == NULL);
+}
+#endif /* ! __HAVE_LEAK_DETECTION */
+
+/**
+ * Unquotes a string
+ *
+ * @param string The quoted string to unquote
+ * @return A pointer to the unquoted string
+ * @ingroup util
+ *
+ * This function unquotes a string. That is, it returns a pointer to a newly
+ * allocated memory region in which the unquoted string is stored. Only
+ * leading and trailing double-qoutes are removed. The string needs to be
+ * freed when it is not needed anymore.
+ */
+char *
+mm_unquote(const char *string)
+{
+ char *ret;
+
+ if (string[0] != '\"' || string[strlen(string)-1] != '\"')
+ return xstrdup(string);
+
+ ret = xstrdup(string + 1);
+ ret[strlen(ret)-1] = '\0';
+
+ return ret;
+}
+
+
+/**
+ * Removes MIME comments from a string
+ *
+ * @param string The string to uncomment
+ * @return A pointer to the uncommented string or NULL on error. Sets mm_errno.
+ * @ingroup util
+ *
+ * This function removes MIME comments from a string (included in parantheses).
+ * It returns a pointer to a newly allocated memory region in which the
+ * uncommented string is stored. The returned string needs to be freed when
+ * it's not used anymore.
+ */
+char *
+mm_uncomment(const char *string)
+{
+ char *buf, *new, *orig, *token;
+ size_t new_size;
+ int found;
+ int open;
+
+ assert(string != NULL);
+
+ new_size = strlen(string) + 1;
+ new = NULL;
+ buf = NULL;
+ orig = NULL;
+ found = 0;
+ open = 0;
+ mm_errno = MM_ERROR_NONE;
+
+ buf = xstrdup(string);
+ orig = buf;
+
+ while (*buf != '\0') {
+ if (*buf == '(') {
+ open++;
+ new_size--;
+ found++;
+ } else if (*buf == ')') {
+ open--;
+ new_size--;
+ } else {
+ if (open)
+ new_size--;
+ }
+ buf++;
+ }
+
+ if (open != 0) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("Uncommenting: parantheses are unbalanced");
+ goto cleanup;
+ }
+
+ if (!found) {
+ new = orig;
+ return orig;
+ }
+
+ new = xmalloc(new_size + 1);
+ *new = '\0';
+ buf = orig;
+ token = buf;
+
+ /* Tokenize our string by parentheses, and copy the portions which are
+ * not commented to our destination.
+ */
+ open = 0;
+ while (*buf != '\0') {
+ if (*buf == '(') {
+ if (!open) {
+ *buf = '\0';
+ strlcat(new, token, new_size);
+ token = buf+1;
+ }
+ open++;
+ }
+ if (*buf == ')') {
+ open--;
+ token = buf + 1;
+ }
+ buf++;
+ }
+
+ strlcat(new, token, new_size);
+
+cleanup:
+ if (orig != NULL) {
+ xfree(orig);
+ orig = NULL;
+ }
+
+ if (mm_errno != MM_ERROR_NONE) {
+ if (new != NULL) {
+ xfree(new);
+ new = NULL;
+ }
+ return NULL;
+ } else {
+ return new;
+ }
+}
+
+/**
+ * separate strings
+ *
+ * @param stringp A pointer to the string being splitted
+ * @param delim The delimeter string
+ * @ingroup util
+ *
+ * This function works similar to strsep(), with the difference that delim is
+ * treated as a whole.
+ */
+char *
+xstrsep(char **stringp, const char *delim)
+{
+ char *p;
+ char *s;
+ char *r;
+
+ if (*stringp == NULL || *stringp == '\0')
+ return NULL;
+
+ p = *stringp;
+
+ if ((s = strstr(p, delim)) == NULL) {
+ r = p;
+ while (*p != '\0')
+ p++;
+ *stringp = NULL;
+ return r;
+ } else {
+ r = p;
+ p += strlen(p) - strlen(s);
+ *p = '\0';
+ *stringp = p + strlen(delim);
+ return r;
+ }
+}
+
+/**
+ * Strips a given character set from a string
+ *
+ * @param input The string which to strip
+ * @param strip The character set to strip off
+ * @return A copy of the original string with all chars stripped
+ * @ingroup util
+ */
+char *
+mm_stripchars(char *input, char *strip)
+{
+ char *output, *orig;
+ int i, j, chars;
+
+ assert(input != NULL);
+ assert(strip != '\0');
+
+ chars = 0;
+ orig = input;
+
+ while (*orig != '\0') {
+ for (i = 0; i < strlen(strip); i++) {
+ if (*orig == strip[i]) {
+ chars++;
+ break;
+ }
+ }
+ orig++;
+ }
+
+ /* If we have not found any char in the input, return a dup of the orig
+ string */
+ if (chars == 0)
+ return(xstrdup(input));
+
+ output = (char *)xmalloc(strlen(input) - chars);
+ orig = output;
+
+ for (i = 0; i < strlen(input); i++) {
+ int stripc;
+ stripc = 0;
+ for (j = 0; j < strlen(strip); j++) {
+ if (input[i] == strip[j]) {
+ stripc = 1;
+ break;
+ }
+ }
+ if (stripc == 0) {
+ *output = input[i];
+ output++;
+ }
+ }
+
+ *output = '\0';
+
+ return(orig);
+}
+
+/**
+ * Adds characters to a string at given positions
+ *
+ * @param input The string to which to add characters
+ * @param add The character string to add
+ * @param linelength The position where to add the character
+ * @return A copy of the string with characters added
+ * @ingroup util
+ *
+ * This function adds the characters add at each linelength positions and
+ * returns this new string.
+ */
+char *
+mm_addchars(char *input, char *add, uint16_t linelength)
+{
+ uint32_t len;
+ uint32_t i;
+ uint32_t l;
+ uint32_t j;
+ uint16_t addcrlf;
+ char *output;
+ char *orig;
+
+ len = strlen(input);
+ if (len <= linelength)
+ return(xstrdup(input));
+
+ addcrlf = len / linelength;
+
+ output = (char *)xmalloc(len + (addcrlf * strlen(add)));
+ orig = output;
+
+ for (i = 0, l = 0; i < len; i++, l++) {
+ if (l == linelength) {
+ for (j = 0; j < strlen(add); j++) {
+ *output = add[j];
+ output++;
+ }
+ l = 0;
+ }
+ *output = input[i];
+ output++;
+ }
+
+ *output = '\0';
+ output = orig;
+
+ return(orig);
+}
+
+void
+mm_striptrailing(char **what, const char *charset)
+{
+ size_t eos, i, hit;
+ char *str;
+
+ str = *what;
+ for (eos = strlen(str)-1; eos >= 0; eos--) {
+ hit = 0;
+ for (i = 0; i < strlen(charset); i++) {
+ if (str[eos] == charset[i]) {
+ str[eos] = '\0';
+ hit = 1;
+ break;
+ }
+ }
+ if (!hit)
+ break;
+ }
+}
diff --git a/trunk/main/minimime/mm_util.h b/trunk/main/minimime/mm_util.h
new file mode 100644
index 000000000..b7058657d
--- /dev/null
+++ b/trunk/main/minimime/mm_util.h
@@ -0,0 +1,50 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MM_UTIL_H
+#define __MM_UTIL_H
+
+#define STRIP_TRAILING(str, charset) do { \
+ size_t eos, i, hit; \
+ for (eos = strlen(str); eos > 0; eos--) { \
+ hit = 0; \
+ for (i = 0; i <= strlen(charset); i++) { \
+ if (str[eos] == charset[i]) {\
+ str[eos] = '\0'; \
+ hit = 1; \
+ break; \
+ } \
+ } \
+ if (!hit) \
+ break; \
+ } \
+} while (0);
+
+#endif /* ! __MM_UTIL_H */
diff --git a/trunk/main/minimime/mm_warnings.c b/trunk/main/minimime/mm_warnings.c
new file mode 100644
index 000000000..c9b8a7daf
--- /dev/null
+++ b/trunk/main/minimime/mm_warnings.c
@@ -0,0 +1,99 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/**
+ * Attaches a warning message to a context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param type The type of the warning
+ * @param fmt The warning message as format string
+ */
+void
+mm_warning_add(MM_CTX *ctx, int type, const char *fmt, ...)
+{
+ struct mm_warning *warning;
+ char buf[1024];
+ va_list ap;
+
+ assert(ctx != NULL);
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ va_end(ap);
+
+ warning = (struct mm_warning *)xmalloc(sizeof(struct mm_warning));
+ warning->message = xstrdup(buf);
+ warning->type = type;
+
+ if (SLIST_EMPTY(&ctx->warnings)) {
+ SLIST_INSERT_HEAD(&ctx->warnings, warning, next);
+ } else {
+ struct mm_warning *last, *after;
+
+ after = NULL;
+
+ SLIST_FOREACH(last, &ctx->warnings, next) {
+ if (last != NULL) {
+ after = last;
+ }
+ }
+
+ assert(after != NULL);
+
+ SLIST_INSERT_AFTER(after, warning, next);
+ }
+}
+
+struct mm_warning *
+mm_warning_next(MM_CTX *ctx, struct mm_warning **last)
+{
+ struct mm_warning *warning;
+
+ if (*last == NULL) {
+ warning = SLIST_FIRST(&ctx->warnings);
+ } else {
+ warning = SLIST_NEXT(*last, next);
+ }
+
+ *last = warning;
+ return warning;
+}
diff --git a/trunk/main/minimime/strlcat.c b/trunk/main/minimime/strlcat.c
new file mode 100644
index 000000000..2d2da0413
--- /dev/null
+++ b/trunk/main/minimime/strlcat.c
@@ -0,0 +1,70 @@
+/* $OpenBSD: strlcat.c,v 1.9 2003/03/14 14:35:29 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.9 2003/03/14 14:35:29 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/trunk/main/minimime/strlcpy.c b/trunk/main/minimime/strlcpy.c
new file mode 100644
index 000000000..94785a309
--- /dev/null
+++ b/trunk/main/minimime/strlcpy.c
@@ -0,0 +1,66 @@
+/* $OpenBSD: strlcpy.c,v 1.6 2003/03/14 14:35:29 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.6 2003/03/14 14:35:29 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/trunk/main/minimime/sys/mm_queue.h b/trunk/main/minimime/sys/mm_queue.h
new file mode 100644
index 000000000..c85bb240c
--- /dev/null
+++ b/trunk/main/minimime/sys/mm_queue.h
@@ -0,0 +1,503 @@
+/* $OpenBSD: queue.h,v 1.22 2001/06/23 04:39:35 angelos Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
+ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/trunk/main/minimime/test.sh b/trunk/main/minimime/test.sh
new file mode 100755
index 000000000..1beca0b74
--- /dev/null
+++ b/trunk/main/minimime/test.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+# MiniMIME test cases
+
+[ ! -x ./tests/parse -o ! -x ./tests/create ] && {
+ echo "You need to compile the test suite first to accomplish tests"
+ exit 1
+}
+
+LD_LIBRARY_PATH=${PWD}
+export LD_LIBRARY_PATH
+
+DIRECTORY=${1:-tests/messages}
+FILES=${2:-"*"}
+
+TESTS=0
+F_ERRORS=0
+F_INVALID=""
+M_ERRORS=0
+M_INVALID=""
+for f in ${DIRECTORY}/${FILES}; do
+ if [ -f "${f}" ]; then
+ TESTS=$((TESTS + 2))
+ echo -n "Running PARSER test for $f (file)... "
+ output=`./tests/parse $f 2>&1`
+ [ $? != 0 ] && {
+ echo "FAILED ($output)"
+ F_ERRORS=$((F_ERRORS + 1))
+ F_INVALID="${F_INVALID} ${f} "
+ } || {
+ echo "PASSED"
+ }
+ echo -n "Running PARSER test for $f (memory)... "
+ output=`./tests/parse -m $f 2>&1`
+ [ $? != 0 ] && {
+ echo "FAILED ($output)"
+ M_ERRORS=$((M_ERRORS + 1))
+ M_INVALID="${M_INVALID} ${f} "
+ } || {
+ echo "PASSED"
+ }
+ fi
+done
+
+echo "Ran a total of ${TESTS} tests"
+
+if [ ${F_ERRORS} -gt 0 ]; then
+ echo "!! ${F_ERRORS} messages had errors in file based parsing"
+ echo "-> ${F_INVALID}"
+fi
+if [ ${M_ERRORS} -gt 0 ]; then
+ echo "!! ${F_ERRORS} messages had errors in memory based parsing"
+fi
+
+unset LD_LIBRARY_PATH
diff --git a/trunk/main/minimime/tests/Makefile b/trunk/main/minimime/tests/Makefile
new file mode 100644
index 000000000..ae460c6b2
--- /dev/null
+++ b/trunk/main/minimime/tests/Makefile
@@ -0,0 +1,18 @@
+BINARIES=parse create
+CFLAGS=-Wall -ggdb -g3 -I..
+LDFLAGS=-L..
+LIBS=-lmmime
+CC=gcc
+
+all: parse create
+
+parse: parse.o
+ $(CC) -o parse parse.o $(LDFLAGS) $(LIBS)
+
+create: create.o
+ $(CC) -o create create.o $(LDFLAGS) $(LIBS)
+
+clean:
+ rm -f $(BINARIES)
+ rm -f *.o
+ rm -f *.core
diff --git a/trunk/main/minimime/tests/create.c b/trunk/main/minimime/tests/create.c
new file mode 100644
index 000000000..c881f17f7
--- /dev/null
+++ b/trunk/main/minimime/tests/create.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * MiniMIME test program - create.c
+ *
+ * Creates a MIME message of the given MIME parts
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include "mm.h"
+
+const char *progname;
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "MiniMIME test suite\n"
+ "USAGE: %s <part> [<part_2>[<part_N>[...]]]\n",
+ progname
+ );
+}
+
+void
+print_error(void)
+{
+ fprintf(stderr, "ERROR: %s\n", mm_error_string());
+}
+
+int
+main(int argc, char **argv)
+{
+ MM_CTX *ctx;
+ struct mm_mimepart *part;
+ char *data;
+ size_t length;
+ int i;
+
+ progname = argv[0];
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ mm_library_init();
+
+ ctx = mm_context_new();
+
+ part = mm_mimepart_new();
+ mm_context_attachpart(ctx, part);
+ mm_envelope_setheader(ctx, "From", "foo@bar.com");
+
+ for (i=1; i < argc; i++) {
+ part = mm_mimepart_fromfile(argv[i]);
+ if (part == NULL) {
+ print_error();
+ exit(1);
+ }
+ mm_context_attachpart(ctx, part);
+ }
+
+ if (mm_context_flatten(ctx, &data, &length, 0) == -1) {
+ print_error();
+ exit(1);
+ }
+
+ printf("%s", data);
+
+ exit(0);
+}
diff --git a/trunk/main/minimime/tests/messages/test1.txt b/trunk/main/minimime/tests/messages/test1.txt
new file mode 100644
index 000000000..2c24b1972
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test1.txt
@@ -0,0 +1,50 @@
+Return-Path: <rezine@hannover.ccc.de>
+X-Original-To: test@mistrust.net
+Delivered-To: rezine@hannover.ccc.de
+Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218])
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
+ (Client did not present a certificate)
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 90EDEBBF2
+ for <test@mistrust.net>; Sun, 24 Aug 2003 16:05:29 +0200 (CEST)
+Date: Sun, 24 Aug 2003 15:49:15 +0200
+From: Jann Fischer <rezine@hannover.ccc.de>
+To: test@mistrust.net
+Subject: Test
+Message-Id: <20030824154915.12cb3f85.rezine@hannover.ccc.de>
+Organization: Chaos Computer Club Hannover
+X-Mailer: Who-Cares 5.23
+Mime-Version: 1.0
+Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
+ boundary="=.2S1ZDSX8ir3lbt"
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/pgp-encrypted
+
+Version: 1
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/octet-stream
+
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.2.2 (OpenBSD)
+
+hQEOA3TvLJ6KBZLBEAP/cl4DiRH5+8S7/kP2BIVdDavHJ9cwHh8awGoyddhMKPJ3
+2558r8MKT0Etjpo649O5WUvT5Z2Jcp12+dTPlAC1kvoIjNNk8+Oe3JCREz/pXYnm
+5ANSCThVYSS34jppgT3NsqiV8sQK3e+Nq/NY7SoKVAV37L0fU4HHozcDZfXqOLsE
+AJgfxjRjjEazPHmgTTu8Pnt5gmlxyP35Yy0pl+gJmboG3Cn5WBcD/rrQf8oiwrB6
+Vak2Hk9TNU7hDO2IRolz4wUfkId47SK31PdhDLBnNPWn6LNWHd+G4hI97e+xeqLW
+dpG7Li5CdP0gfuHx2ux9Y5buWVVtqPhdDUlRaIBfM7Fu0sENAeREANAtdPHn0yTf
+V4T5NvImY3gXgLST5wNm3Ft+4nIDZrcnSy04x4faTLFBOcY95W0O1omILHyN5Ste
+Le5NhXhQRKyl6ebXtIvEOsJOK4NT6JaUF20l4yvgf0AnetG9Pbzc37mRqmE6Fb8O
+h/De3iqw7dexaQc+LaD3XTmvPyyDK2aI4cXOdc9WOzrWR7+9iEiY32SFsQWMRMZJ
+GdKkGk22K2p7MPFaU3MHQ3Af+WCN4mRW8SurFxH1379Y5e1IPfTeL6OBkj8hHilX
+Y+Y7523ADiStJsONIZPBXJVhZ/VAJ+jL+T1/Xht10VsJcWAY8A9tP+jNgyg8dh+J
+JgWVchQOZipdftYwR7w5GkhL2Nc5NYBJBg4DFd9g2nnwuzaAKYO5kMTzEmm9KOYq
+0DC5ukok4SGDwWPUIogNHmaSnFr723hYuJC7DwSxHXVG3VxxF78u1gzEnImOWRsf
+1RzGb7b8Lf7Rj98H5cNiZ55BXAmidjm7WghCLsT2GvxviqQoRIJ2h/WHM0Bl2v3F
+Dpa3N01p2NIIgQLRoXXyBCZTwGOH4y9nBj5PU7vzzSrMweHHt1BwHXcqItCyWFXX
+2tj4//Dyw3Lw/L5xGxYRP1Q=
+=fSLd
+-----END PGP MESSAGE-----
+
+--=.2S1ZDSX8ir3lbt--
diff --git a/trunk/main/minimime/tests/messages/test2.txt b/trunk/main/minimime/tests/messages/test2.txt
new file mode 100644
index 000000000..2c24b1972
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test2.txt
@@ -0,0 +1,50 @@
+Return-Path: <rezine@hannover.ccc.de>
+X-Original-To: test@mistrust.net
+Delivered-To: rezine@hannover.ccc.de
+Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218])
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
+ (Client did not present a certificate)
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 90EDEBBF2
+ for <test@mistrust.net>; Sun, 24 Aug 2003 16:05:29 +0200 (CEST)
+Date: Sun, 24 Aug 2003 15:49:15 +0200
+From: Jann Fischer <rezine@hannover.ccc.de>
+To: test@mistrust.net
+Subject: Test
+Message-Id: <20030824154915.12cb3f85.rezine@hannover.ccc.de>
+Organization: Chaos Computer Club Hannover
+X-Mailer: Who-Cares 5.23
+Mime-Version: 1.0
+Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
+ boundary="=.2S1ZDSX8ir3lbt"
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/pgp-encrypted
+
+Version: 1
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/octet-stream
+
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.2.2 (OpenBSD)
+
+hQEOA3TvLJ6KBZLBEAP/cl4DiRH5+8S7/kP2BIVdDavHJ9cwHh8awGoyddhMKPJ3
+2558r8MKT0Etjpo649O5WUvT5Z2Jcp12+dTPlAC1kvoIjNNk8+Oe3JCREz/pXYnm
+5ANSCThVYSS34jppgT3NsqiV8sQK3e+Nq/NY7SoKVAV37L0fU4HHozcDZfXqOLsE
+AJgfxjRjjEazPHmgTTu8Pnt5gmlxyP35Yy0pl+gJmboG3Cn5WBcD/rrQf8oiwrB6
+Vak2Hk9TNU7hDO2IRolz4wUfkId47SK31PdhDLBnNPWn6LNWHd+G4hI97e+xeqLW
+dpG7Li5CdP0gfuHx2ux9Y5buWVVtqPhdDUlRaIBfM7Fu0sENAeREANAtdPHn0yTf
+V4T5NvImY3gXgLST5wNm3Ft+4nIDZrcnSy04x4faTLFBOcY95W0O1omILHyN5Ste
+Le5NhXhQRKyl6ebXtIvEOsJOK4NT6JaUF20l4yvgf0AnetG9Pbzc37mRqmE6Fb8O
+h/De3iqw7dexaQc+LaD3XTmvPyyDK2aI4cXOdc9WOzrWR7+9iEiY32SFsQWMRMZJ
+GdKkGk22K2p7MPFaU3MHQ3Af+WCN4mRW8SurFxH1379Y5e1IPfTeL6OBkj8hHilX
+Y+Y7523ADiStJsONIZPBXJVhZ/VAJ+jL+T1/Xht10VsJcWAY8A9tP+jNgyg8dh+J
+JgWVchQOZipdftYwR7w5GkhL2Nc5NYBJBg4DFd9g2nnwuzaAKYO5kMTzEmm9KOYq
+0DC5ukok4SGDwWPUIogNHmaSnFr723hYuJC7DwSxHXVG3VxxF78u1gzEnImOWRsf
+1RzGb7b8Lf7Rj98H5cNiZ55BXAmidjm7WghCLsT2GvxviqQoRIJ2h/WHM0Bl2v3F
+Dpa3N01p2NIIgQLRoXXyBCZTwGOH4y9nBj5PU7vzzSrMweHHt1BwHXcqItCyWFXX
+2tj4//Dyw3Lw/L5xGxYRP1Q=
+=fSLd
+-----END PGP MESSAGE-----
+
+--=.2S1ZDSX8ir3lbt--
diff --git a/trunk/main/minimime/tests/messages/test3.txt b/trunk/main/minimime/tests/messages/test3.txt
new file mode 100644
index 000000000..082b6fb61
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test3.txt
@@ -0,0 +1,12 @@
+From: Jann Fischer <rezine@criminology.de>
+To: cipherlist <cipherlist@mistrust.net>
+Subject: Foobar
+Date: blahblah
+MIME-Version: 1.0 (MiniMIME)
+Content-Type: multipart/mixed; boundary="abcd"
+
+--abcd
+Content-Type: plain/text;
+
+This is a test :->
+--abcd--
diff --git a/trunk/main/minimime/tests/messages/test4.txt b/trunk/main/minimime/tests/messages/test4.txt
new file mode 100644
index 000000000..a08246939
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test4.txt
@@ -0,0 +1,168 @@
+X-Envelope-From: <511-bounces@hannover.ccc.de>
+X-Envelope-To: <rezine@criminology.de>
+X-Delivery-Time: 1070263752
+Received: from gost.hannover.ccc.de (hannover.ccc.de [62.48.71.164])
+ by mailin.webmailer.de (8.12.10/8.12.10) with ESMTP id hB17TAUR020052
+ for <rezine@criminology.de>; Mon, 1 Dec 2003 08:29:10 +0100 (MET)
+Received: from localhost.hannover.ccc.de (unknown [127.0.0.1])
+ by gost.hannover.ccc.de (Postfix) with ESMTP
+ id 092C8BC81; Mon, 1 Dec 2003 08:29:23 +0100 (CET)
+X-Original-To: 511@hannover.ccc.de
+Delivered-To: 511@hannover.ccc.de
+Received: from sbapp3 (unknown [211.157.36.9])
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 3F93ABC7C
+ for <511@hannover.ccc.de>; Mon, 1 Dec 2003 08:29:12 +0100 (CET)
+From: "Vanessa Lintner" <reply@seekercenter.net>
+To: 511@hannover.ccc.de
+Date: Mon, 1 Dec 2003 15:30:57 +0800
+X-Priority: 3
+X-Library: Indy 8.0.25
+Message-Id: <20031201072912.3F93ABC7C@gost.hannover.ccc.de>
+Subject: [CCC511] http://lists.hannover.ccc.de
+X-BeenThere: 511@hannover.ccc.de
+X-Mailman-Version: 2.1.2
+Precedence: list
+Reply-To: Vanessa Lintner <vanessa@seekercenter.net>,
+ Oeffentliche Mailingliste des C3H <511@hannover.ccc.de>
+List-Id: Oeffentliche Mailingliste des C3H <511.hannover.ccc.de>
+List-Unsubscribe: <http://hannover.ccc.de/mailman/listinfo/511>,
+ <mailto:511-request@hannover.ccc.de?subject=unsubscribe>
+List-Post: <mailto:511@hannover.ccc.de>
+List-Help: <mailto:511-request@hannover.ccc.de?subject=help>
+List-Subscribe: <http://hannover.ccc.de/mailman/listinfo/511>,
+ <mailto:511-request@hannover.ccc.de?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============14807035762661644=="
+Sender: 511-bounces@hannover.ccc.de
+Errors-To: 511-bounces@hannover.ccc.de
+
+--===============14807035762661644==
+Content-Type: text/html;
+
+<html>
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<style type="text/css">
+.stbtm {
+ BACKGROUND-COLOR:#cecbde; BORDER-BOTTOM: #665b8e 1px solid; BORDER-LEFT: #ffffff 1px solid; BORDER-RIGHT: #665b8e 1px solid; BORDER-TOP: #ffffff 1px solid; COLOR: #000000; FONT-SIZE: 12pt; HEIGHT: 26px; WIDTH: 120px; clip: rect( )}
+.stedit {
+ background-color:#484C68; white-space: nowrap; border: #000000; BORDER-BOTTOM: #ffffff 1px solid; BORDER-LEFT: #ffffff 1px solid; BORDER-RIGHT: #ffffff 1px solid; BORDER-TOP: #ffffff 1px solid; FONT-SIZE: 10pt; color: #CCCCCC; font-weight: bold}
+
+</style>
+</head>
+<BODY leftMargin=0 onload="" topMargin=0 marginheight="0" marginwidth="0" bgcolor="#FFFFFF">
+ <table border="0" cellspacing="0" cellpadding="0" width="580">
+ <tr>
+ <td width="20" rowspan="2">&nbsp;</td>
+ <td colspan="3">
+ <table border="0" cellspacing="0" cellpadding="0" align="left" width="560">
+ <tr>
+ <td width="330" height="307">
+ <table width="330" border="0" cellspacing="0" cellpadding="0" background="http://www.imagespool.com/skbmp/letter_01.gif" height="307">
+ <tr>
+ <td>
+ <p> <font face=Arial size=2> </font> <font face=Arial size=2><font face="Verdana, Arial, Helvetica, sans-serif" color="#000000">Hello,<br>
+ <br>
+ I have visited <a href='http://lists.hannover.ccc.de'>lists.hannover.ccc.de</a>
+ and noticed that your website is not listed on some search
+ engines. I am sure that through our service the number of
+ people who visit your website will definitely increase.
+ <a target=_blank href="http://www.seekercenter.net/index.php">SeekerCenter</a>
+ is a unique technology that instantly submits your website
+ to over 500,000 search engines and directories -- a really
+ low-cost and effective way to advertise your site. For more
+ details please go to <a target=_blank href="http://www.seekercenter.net/index.php">SeekerCenter.net</a>.<br>
+ <br>
+ Give your website maximum exposure today!<br>
+ Looking forward to hearing from you.<br>
+ <br>
+ </font></font>
+ <table border=0 width=100%>
+ <tr>
+ <td width=50%> <font face="Arial" color="#000000" size="2">Best
+ Regards,<br>
+ Vanessa Lintner<br>
+ Sales &amp; Marketing <br>
+ <a target=_blank href="http://www.seekercenter.net/index.php">www.SeekerCenter.net</a></font>
+ <TD><td width=50%>
+ <div align="center" valign=middle>
+ <form target=_blank action=http://www.seekercenter.net method=POST>
+ <input type="submit" name="Submit" value="Signup Now!!!" class="stbtm">
+ </form>
+ </div>
+ </TD>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ <td width="250" height="64" valign="middle">
+ <table width="230" border="0" cellspacing="0" cellpadding="0">
+ <tr>
+ <td colspan="3" height="2"></td>
+ </tr>
+ <tr>
+ <td colspan="3"><img src="http://report.imagespool.com/report_email.php?s=1&e=511@hannover.ccc.de" border=0 width=0 height=0>
+ <p><img src="http://www.imagespool.com/skbmp/letter_04.gif" height="12"></p>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3"><img src="http://www.imagespool.com/skbmp/letter_05.gif" height="127"><img src="http://ww2.imagespool.com/1/9/b/0r066.jpg" width="177" height="127"><img src="http://www.imagespool.com/skbmp/letter_07.gif" width="33" height="127"></td>
+ </tr>
+ <tr>
+ <td colspan="3" height="92" background="http://www.imagespool.com/skbmp/letter_08.gif" valign="bottom">
+ <table width="230" border="0" cellspacing="0" cellpadding="0" height="92">
+ <tr>
+ <td width="36" height="43">&nbsp;</td>
+ <td width="157" height="43">&nbsp;</td>
+ <td width="134" height="43">&nbsp;</td>
+ </tr>
+ <tr>
+ <td width="36" height="2">&nbsp;</td>
+ <td width="157" height="2">&nbsp;</td>
+ <td width="134" height="2">&nbsp;</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr> </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <table width="560" border="0" cellspacing="0" cellpadding="1" bordercolor="0">
+ <tr>
+ <td>&nbsp;</td>
+ </tr>
+ <tr>
+ <td bgcolor="#EFEFEF"><font face="Verdana, Arial, Helvetica, sans-serif" size="1">You
+ are receiving this email because you opted-in to receive special
+ offers through a partner website. If you feel that you received
+ this email in error or do not wish to receive additional special
+ offers, please enter your email address here and click the button
+ of &quot;Remove Me&quot;: <a href="http://www.seekercenter.net/remove.php?email=511@hannover.ccc.de">
+ <img src="http://www.imagespool.com/skbmp/removeme.gif" width="73" height="17" border="0"></a>
+ </font></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
+
+--===============14807035762661644==
+Content-Type: text/plain; charset="iso-8859-1"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+_______________________________________________
+511 mailing list
+511@hannover.ccc.de
+--===============14807035762661644==--
diff --git a/trunk/main/minimime/tests/messages/test5.txt b/trunk/main/minimime/tests/messages/test5.txt
new file mode 100644
index 000000000..5e4cdb17d
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test5.txt
@@ -0,0 +1,44 @@
+Return-Path: <rezine@criminology.de>
+X-Original-To: rezine@mistrust.net
+Delivered-To: rezine@hannover.ccc.de
+Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218])
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
+ (Client did not present a certificate)
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 79E9BBC7C
+ for <rezine@mistrust.net>; Wed, 24 Dec 2003 13:35:36 +0100 (CET)
+Received: from thinktank.niedersachsen.de (localhost [127.0.0.1])
+ by thinktank.niedersachsen.de (8.12.9/8.12.2) with SMTP id hBOCZBFU029588
+ for <rezine@mistrust.net>; Wed, 24 Dec 2003 13:35:11 +0100 (CET)
+Date: Wed, 24 Dec 2003 13:35:11 +0100
+From: Jann Fischer <rezine@criminology.de>
+To: rezine@mistrust.net
+Subject: Test
+Message-Id: <20031224133511.5f4b6d9b.rezine@criminology.de>
+X-Mailer: Who Cares 5.23
+Mime-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800"
+
+This is a multi-part message in MIME format.
+
+--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: 7bit
+
+Test
+
+--
+Be careful who you follow.
+0x6D839821 | FA8C 3663 9906 D8C3 AC16 F7C4 66E0 F351 6D83 9821
+
+--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800
+Content-Type: application/octet-stream;
+ name="bar.c"
+Content-Disposition: attachment;
+ filename="bar.c"
+Content-Transfer-Encoding: base64
+
+I2luY2x1ZGUgPHN0ZGlvLmg+Cgp2b2lkCm1haW4oaW50IGFyZ2MsIGNoYXIgKiphcmd2KQp7CgkJ
+cHJpbnRmKCIlc1xuIiwgYXJndlswXSk7Cn0K
+
+--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800--
diff --git a/trunk/main/minimime/tests/messages/test6.txt b/trunk/main/minimime/tests/messages/test6.txt
new file mode 100644
index 000000000..fb4e7a14c
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test6.txt
@@ -0,0 +1,12 @@
+From: Me
+Date: Foobar
+To: There
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="abcde"
+
+--abcde
+Content-Type: text/plain
+
+Blah blah
+Blah
+--abcde--
diff --git a/trunk/main/minimime/tests/messages/test7.txt b/trunk/main/minimime/tests/messages/test7.txt
new file mode 100644
index 000000000..1cda11e3f
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test7.txt
@@ -0,0 +1,64 @@
+Return-Path: MAILER-DAEMON
+Received: from chaos.verfassungsschutz.de (localhost [IPv6:::1])
+ by chaos.verfassungsschutz.de (8.12.7/8.12.2) with ESMTP id h2EKV1oM031761
+ for <jfi@chaos.verfassungsschutz.de>; Fri, 14 Mar 2003 21:31:18 +0100 (CET)
+Received: from localhost (localhost)
+ by chaos.verfassungsschutz.de (8.12.7/8.12.2/Submit) id h2BNU1vr029177;
+ Wed, 12 Mar 2003 00:35:01 +0100 (CET)
+Date: Wed, 12 Mar 2003 00:35:01 +0100 (CET)
+From: Mail Delivery Subsystem <MAILER-DAEMON@chaos.verfassungsschutz.de>
+Message-Id: <200303112335.h2BNU1vr029177@chaos.verfassungsschutz.de>
+To: jfi@chaos.verfassungsschutz.de
+MIME-Version: 1.0
+Content-Type: multipart/report; report-type=delivery-status;
+ boundary="h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de"
+Subject: Warning: could not send message for past 4 hours
+Auto-Submitted: auto-generated (warning-timeout)
+
+This is a MIME-encapsulated message
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de
+
+ **********************************************
+ ** THIS IS A WARNING MESSAGE ONLY **
+ ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **
+ **********************************************
+
+The original message was received at Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+from jfi@localhost
+
+ ----- Transcript of session follows -----
+451 4.4.1 reply: read error from localhost
+rezine@kommunism.us... Deferred: Connection timed out with localhost
+Warning: message still undelivered after 4 hours
+Will keep trying until message is 5 days old
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de
+Content-Type: message/delivery-status
+
+Reporting-MTA: dns; chaos.verfassungsschutz.de
+Arrival-Date: Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+
+Final-Recipient: RFC822; rezine@kommunism.us
+Action: delayed
+Status: 4.4.2
+Last-Attempt-Date: Wed, 12 Mar 2003 00:35:01 +0100 (CET)
+Will-Retry-Until: Sun, 16 Mar 2003 20:18:36 +0100 (CET)
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de
+Content-Type: message/rfc822
+
+Return-Path: <jfi>
+Received: (from jfi@localhost)
+ by chaos.verfassungsschutz.de (8.12.7/8.12.2/Submit) id h2BJIawm025679
+ for rezine@kommunism.us; Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+Date: Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+From: Jann Fischer <jfi>
+Message-Id: <200303111918.h2BJIawm025679@chaos.verfassungsschutz.de>
+To: rezine@kommunism.us
+Subject: Test
+
+Test
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de--
+
diff --git a/trunk/main/minimime/tests/parse.c b/trunk/main/minimime/tests/parse.c
new file mode 100644
index 000000000..3d3bdf028
--- /dev/null
+++ b/trunk/main/minimime/tests/parse.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * MiniMIME test program - parse.c
+ *
+ * Parses any given messages
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include "mm.h"
+
+const char *progname;
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "MiniMIME test suite\n"
+ "Usage: %s [-m] <filename>\n\n"
+ " -m : use memory based scanning\n\n",
+ progname
+ );
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ MM_CTX *ctx;
+ struct mm_mimeheader *header, *lastheader = NULL;
+ struct mm_mimepart *part;
+ struct mm_content *ct;
+ int parts, i;
+ struct stat st;
+ int fd;
+ char *buf;
+ int scan_mode = 0;
+
+ progname = strdup(argv[0]);
+
+ while ((i = getopt(argc, argv, "m")) != -1) {
+ switch(i) {
+ case 'm':
+ scan_mode = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ }
+
+#ifdef __HAVE_LEAK_DETECTION
+ /* Initialize memory leak detection if compiled in */
+ MM_leakd_init();
+#endif
+
+ /* Initialize MiniMIME library */
+ mm_library_init();
+
+ /* Register all default codecs (base64/qp) */
+ mm_codec_registerdefaultcodecs();
+
+ do {
+ /* Create a new context */
+ ctx = mm_context_new();
+
+ /* Parse a file into our context */
+ if (scan_mode == 0) {
+ i = mm_parse_file(ctx, argv[0], MM_PARSE_LOOSE, 0);
+ } else {
+ if (stat(argv[0], &st) == -1) {
+ err(1, "stat");
+ }
+
+ if ((fd = open(argv[0], O_RDONLY)) == -1) {
+ err(1, "open");
+ }
+
+ buf = (char *)malloc(st.st_size);
+ if (buf == NULL) {
+ err(1, "malloc");
+ }
+
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ err(1, "read");
+ }
+
+ close(fd);
+ buf[st.st_size] = '\0';
+
+ i = mm_parse_mem(ctx, buf, MM_PARSE_LOOSE, 0);
+ }
+
+ if (i == -1 || mm_errno != MM_ERROR_NONE) {
+ printf("ERROR: %s at line %d\n", mm_error_string(), mm_error_lineno());
+ exit(1);
+ }
+
+ /* Get the number of MIME parts */
+ parts = mm_context_countparts(ctx);
+ if (parts == 0) {
+ printf("ERROR: got zero MIME parts, huh\n");
+ exit(1);
+ } else {
+ if (mm_context_iscomposite(ctx)) {
+ printf("Got %d MIME parts\n", parts - 1);
+ } else {
+ printf("Flat message (not multipart)\n");
+ }
+ }
+
+ /* Get the main MIME part */
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ fprintf(stderr, "Could not get envelope part\n");
+ exit(1);
+ }
+
+ printf("Printing envelope headers:\n");
+ /* Print all headers */
+ lastheader = NULL;
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL)
+ printf("%s: %s\n", header->name, header->value);
+
+ printf("%s\n", mm_content_tostring(part->type));
+ printf("\n");
+
+ ct = part->type;
+ assert(ct != NULL);
+
+ if (mm_context_iscomposite(ctx) == 0) {
+ printf("Printing body part for FLAT message:\n");
+ part = mm_context_getpart(ctx, 0);
+ printf("%s", part->body);
+ }
+
+ /* Loop through all MIME parts beginning with 1 */
+ for (i = 1; i < mm_context_countparts(ctx); i++) {
+ char *decoded;
+
+ printf("Printing headers for MIME part %d\n", i);
+
+ /* Get the current MIME entity */
+ part = mm_context_getpart(ctx, i);
+ if (part == NULL) {
+ fprintf(stderr, "Should have %d parts but "
+ "couldn't retrieve part %d",
+ mm_context_countparts(ctx), i);
+ exit(1);
+ }
+
+ /* Print all headers */
+ lastheader = NULL;
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL)
+ printf("%s: %s\n", header->name, header->value);
+
+ printf("%s\n", mm_content_tostring(part->type));
+
+ /* Print MIME part body */
+ printf("\nPRINTING MESSAGE BODY (%d):\n%s\n", i, part->opaque_body);
+ decoded = mm_mimepart_decode(part);
+ if (decoded != NULL) {
+ printf("DECODED:\n%s\n", decoded);
+ free(decoded);
+ }
+ }
+
+ printf("RECONSTRUCTED MESSAGE:\n");
+
+ do {
+ char *env;
+ size_t env_len;
+
+ mm_context_flatten(ctx, &env, &env_len, 0);
+ printf("%s", env);
+ free(env);
+
+ } while (0);
+
+ mm_context_free(ctx);
+ ctx = NULL;
+
+#ifdef __HAVE_LEAK_DETECTION
+ MM_leakd_printallocated();
+#endif
+
+ } while (0);
+
+ return 0;
+}
diff --git a/trunk/main/netsock.c b/trunk/main/netsock.c
new file mode 100644
index 000000000..27def180c
--- /dev/null
+++ b/trunk/main/netsock.c
@@ -0,0 +1,208 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@digium.com>
+ * 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 Network socket handling
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#if defined (SOLARIS)
+#include <sys/sockio.h>
+#endif
+
+#include "asterisk/netsock.h"
+#include "asterisk/utils.h"
+
+struct ast_netsock {
+ ASTOBJ_COMPONENTS(struct ast_netsock);
+ struct sockaddr_in bindaddr;
+ int sockfd;
+ int *ioref;
+ struct io_context *ioc;
+ void *data;
+};
+
+struct ast_netsock_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct ast_netsock);
+ struct io_context *ioc;
+};
+
+static void ast_netsock_destroy(struct ast_netsock *netsock)
+{
+ ast_io_remove(netsock->ioc, netsock->ioref);
+ close(netsock->sockfd);
+ ast_free(netsock);
+}
+
+struct ast_netsock_list *ast_netsock_list_alloc(void)
+{
+ return ast_calloc(1, sizeof(struct ast_netsock_list));
+}
+
+int ast_netsock_init(struct ast_netsock_list *list)
+{
+ memset(list, 0, sizeof(*list));
+ ASTOBJ_CONTAINER_INIT(list);
+
+ return 0;
+}
+
+int ast_netsock_release(struct ast_netsock_list *list)
+{
+ ASTOBJ_CONTAINER_DESTROYALL(list, ast_netsock_destroy);
+ ASTOBJ_CONTAINER_DESTROY(list);
+
+ return 0;
+}
+
+struct ast_netsock *ast_netsock_find(struct ast_netsock_list *list,
+ struct sockaddr_in *sa)
+{
+ struct ast_netsock *sock = NULL;
+
+ ASTOBJ_CONTAINER_TRAVERSE(list, !sock, {
+ ASTOBJ_RDLOCK(iterator);
+ if (!inaddrcmp(&iterator->bindaddr, sa))
+ sock = iterator;
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ return sock;
+}
+
+struct ast_netsock *ast_netsock_bindaddr(struct ast_netsock_list *list, struct io_context *ioc, struct sockaddr_in *bindaddr, int tos, int cos, ast_io_cb callback, void *data)
+{
+ int netsocket = -1;
+ int *ioref;
+
+ struct ast_netsock *ns;
+ const int reuseFlag = 1;
+
+ /* Make a UDP socket */
+ netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (netsocket < 0) {
+ ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (setsockopt(netsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseFlag, sizeof reuseFlag) < 0) {
+ ast_log(LOG_WARNING, "Error setting SO_REUSEADDR on sockfd '%d'\n", netsocket);
+ }
+ if (bind(netsocket,(struct sockaddr *)bindaddr, sizeof(struct sockaddr_in))) {
+ ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(bindaddr->sin_addr), ntohs(bindaddr->sin_port), strerror(errno));
+ close(netsocket);
+ return NULL;
+ }
+
+ ast_netsock_set_qos(netsocket, tos, cos, "IAX2");
+
+ ast_enable_packet_fragmentation(netsocket);
+
+ if (!(ns = ast_calloc(1, sizeof(*ns)))) {
+ close(netsocket);
+ return NULL;
+ }
+
+ /* Establish I/O callback for socket read */
+ if (!(ioref = ast_io_add(ioc, netsocket, callback, AST_IO_IN, ns))) {
+ close(netsocket);
+ ast_free(ns);
+ return NULL;
+ }
+ ASTOBJ_INIT(ns);
+ ns->ioref = ioref;
+ ns->ioc = ioc;
+ ns->sockfd = netsocket;
+ ns->data = data;
+ memcpy(&ns->bindaddr, bindaddr, sizeof(ns->bindaddr));
+ ASTOBJ_CONTAINER_LINK(list, ns);
+
+ return ns;
+}
+
+int ast_netsock_set_qos(int netsocket, int tos, int cos, const char *desc)
+{
+ int res;
+
+ if ((res = setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))))
+ ast_log(LOG_WARNING, "Unable to set %s TOS to %d, may be you have no root privileges\n", desc, tos);
+ else if (tos)
+ ast_verb(2, "Using %s TOS bits %d\n", desc, tos);
+
+#if defined(linux)
+ if (setsockopt(netsocket, SOL_SOCKET, SO_PRIORITY, &cos, sizeof(cos)))
+ ast_log(LOG_WARNING, "Unable to set %s CoS to %d\n", desc, cos);
+ else if (cos)
+ ast_verb(2, "Using %s CoS mark %d\n", desc, cos);
+#endif
+
+ return res;
+}
+
+
+struct ast_netsock *ast_netsock_bind(struct ast_netsock_list *list, struct io_context *ioc, const char *bindinfo, int defaultport, int tos, int cos, ast_io_cb callback, void *data)
+{
+ struct sockaddr_in sin;
+ char *tmp;
+ char *host;
+ char *port;
+ int portno;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(defaultport);
+ tmp = ast_strdupa(bindinfo);
+
+ host = strsep(&tmp, ":");
+ port = tmp;
+
+ if (port && ((portno = atoi(port)) > 0))
+ sin.sin_port = htons(portno);
+
+ inet_aton(host, &sin.sin_addr);
+
+ return ast_netsock_bindaddr(list, ioc, &sin, tos, cos, callback, data);
+}
+
+int ast_netsock_sockfd(const struct ast_netsock *ns)
+{
+ return ns ? ns-> sockfd : -1;
+}
+
+const struct sockaddr_in *ast_netsock_boundaddr(const struct ast_netsock *ns)
+{
+ return &(ns->bindaddr);
+}
+
+void *ast_netsock_data(const struct ast_netsock *ns)
+{
+ return ns->data;
+}
+
+void ast_netsock_unref(struct ast_netsock *ns)
+{
+ ASTOBJ_UNREF(ns, ast_netsock_destroy);
+}
diff --git a/trunk/main/pbx.c b/trunk/main/pbx.c
new file mode 100644
index 000000000..3820c4615
--- /dev/null
+++ b/trunk/main/pbx.c
@@ -0,0 +1,7800 @@
+/*
+ * 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 Core PBX routines.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#if defined(HAVE_SYSINFO)
+#include <sys/sysinfo.h>
+#endif
+#if defined(SOLARIS)
+#include <sys/loadavg.h>
+#endif
+
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cdr.h"
+#include "asterisk/config.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/linkedlists.h"
+#define SAY_STUBS /* generate declarations and stubs for say methods */
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/app.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/event.h"
+#include "asterisk/hashtab.h"
+#include "asterisk/module.h"
+#include "asterisk/indications.h"
+
+/*!
+ * \note I M P O R T A N T :
+ *
+ * The speed of extension handling will likely be among the most important
+ * aspects of this PBX. The switching scheme as it exists right now isn't
+ * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
+ * of priorities, but a constant search time here would be great ;-)
+ *
+ * A new algorithm to do searching based on a 'compiled' pattern tree is introduced
+ * here, and shows a fairly flat (constant) search time, even for over
+ * 1000 patterns. Might Still needs some work-- there are some fine points of the matching
+ * spec about tie-breaking based on the characters in character sets, but this
+ * should be do-able via the weight system currently being used.
+ *
+ * Also, using a hash table for context/priority name lookup can help prevent
+ * the find_extension routines from absorbing exponential cpu cycles. I've tested
+ * find_extension with red-black trees, which have O(log2(n)) speed. Right now,
+ * I'm using hash tables, which do searches (ideally) in O(1) time.
+ *
+ */
+
+#ifdef LOW_MEMORY
+#define EXT_DATA_SIZE 256
+#else
+#define EXT_DATA_SIZE 8192
+#endif
+
+#define SWITCH_DATA_LENGTH 256
+
+#define VAR_BUF_SIZE 4096
+
+#define VAR_NORMAL 1
+#define VAR_SOFTTRAN 2
+#define VAR_HARDTRAN 3
+
+#define BACKGROUND_SKIP (1 << 0)
+#define BACKGROUND_NOANSWER (1 << 1)
+#define BACKGROUND_MATCHEXTEN (1 << 2)
+#define BACKGROUND_PLAYBACK (1 << 3)
+
+AST_APP_OPTIONS(background_opts, {
+ AST_APP_OPTION('s', BACKGROUND_SKIP),
+ AST_APP_OPTION('n', BACKGROUND_NOANSWER),
+ AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
+ AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
+});
+
+#define WAITEXTEN_MOH (1 << 0)
+#define WAITEXTEN_DIALTONE (1 << 1)
+
+AST_APP_OPTIONS(waitexten_opts, {
+ AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
+ AST_APP_OPTION_ARG('d', WAITEXTEN_DIALTONE, 0),
+});
+
+struct ast_context;
+struct ast_app;
+
+/*!
+ \brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ struct ast_hashtab *peer_tree; /*!< Priorities list in tree form -- only on the head of the peer list */
+ struct ast_hashtab *peer_label_tree; /*!< labeled priorities in the peer list -- only on the head of the peer list */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+/*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief match_char: forms a syntax tree for quick matching of extension patterns */
+struct match_char
+{
+ int is_pattern; /* the pattern started with '_' */
+ int deleted; /* if this is set, then... don't return it */
+ char *x; /* the pattern itself-- matches a single char */
+ int specificity; /* simply the strlen of x, or 10 for X, 9 for Z, and 8 for N; and '.' and '!' will add 11 ? */
+ struct match_char *alt_char;
+ struct match_char *next_char;
+ struct ast_exten *exten; /* attached to last char of a pattern for exten */
+};
+
+struct scoreboard /* make sure all fields are 0 before calling new_find_extension */
+{
+ int total_specificity;
+ int total_length;
+ char last_char; /* set to ! or . if they are the end of the pattern */
+ int canmatch; /* if the string to match was just too short */
+ struct match_char *node;
+ struct ast_exten *canmatch_exten;
+ struct ast_exten *exten;
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_context {
+ ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
+ struct ast_exten *root; /*!< The root of the list of extensions */
+ struct ast_hashtab *root_tree; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */
+ struct match_char *pattern_tree; /*!< A tree to speed up extension pattern matching */
+ struct ast_context *next; /*!< Link them together */
+ struct ast_include *includes; /*!< Include other contexts */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ struct ast_module *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+
+/*! \brief ast_state_cb: An extension state notify register item */
+struct ast_state_cb {
+ int id;
+ void *data;
+ ast_state_cb_type callback;
+ struct ast_state_cb *next;
+};
+
+/*! \brief Structure for dial plan hints
+
+ \note Hints are pointers from an extension in the dialplan to one or
+ more devices (tech/name)
+ - See \ref AstExtState
+*/
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
+};
+
+static const struct cfextension_states {
+ int extension_state;
+ const char * const text;
+} extension_states[] = {
+ { AST_EXTENSION_NOT_INUSE, "Idle" },
+ { AST_EXTENSION_INUSE, "InUse" },
+ { AST_EXTENSION_BUSY, "Busy" },
+ { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
+ { AST_EXTENSION_RINGING, "Ringing" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
+ { AST_EXTENSION_ONHOLD, "Hold" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
+};
+
+struct statechange {
+ AST_LIST_ENTRY(statechange) entry;
+ char dev[0];
+};
+
+/*!
+ * \brief Data used by the device state thread
+ */
+static struct {
+ /*! Set to 1 to stop the thread */
+ unsigned int stop:1;
+ /*! The device state monitoring thread */
+ pthread_t thread;
+ /*! Lock for the state change queue */
+ ast_mutex_t lock;
+ /*! Condition for the state change queue */
+ ast_cond_t cond;
+ /*! Queue of state changes */
+ AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
+} device_state = {
+ .thread = AST_PTHREADT_NULL,
+};
+
+struct pbx_exception {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(context); /*!< Context associated with this exception */
+ AST_STRING_FIELD(exten); /*!< Exten associated with this exception */
+ AST_STRING_FIELD(reason); /*!< The exception reason */
+ );
+
+ int priority; /*!< Priority associated with this exception */
+};
+
+static int pbx_builtin_answer(struct ast_channel *, void *);
+static int pbx_builtin_goto(struct ast_channel *, void *);
+static int pbx_builtin_hangup(struct ast_channel *, void *);
+static int pbx_builtin_background(struct ast_channel *, void *);
+static int pbx_builtin_wait(struct ast_channel *, void *);
+static int pbx_builtin_waitexten(struct ast_channel *, void *);
+static int pbx_builtin_keepalive(struct ast_channel *, void *);
+static int pbx_builtin_resetcdr(struct ast_channel *, void *);
+static int pbx_builtin_setamaflags(struct ast_channel *, void *);
+static int pbx_builtin_ringing(struct ast_channel *, void *);
+static int pbx_builtin_progress(struct ast_channel *, void *);
+static int pbx_builtin_congestion(struct ast_channel *, void *);
+static int pbx_builtin_busy(struct ast_channel *, void *);
+static int pbx_builtin_noop(struct ast_channel *, void *);
+static int pbx_builtin_gotoif(struct ast_channel *, void *);
+static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
+static int pbx_builtin_execiftime(struct ast_channel *, void *);
+static int pbx_builtin_saynumber(struct ast_channel *, void *);
+static int pbx_builtin_saydigits(struct ast_channel *, void *);
+static int pbx_builtin_saycharacters(struct ast_channel *, void *);
+static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
+static int matchcid(const char *cidpattern, const char *callerid);
+int pbx_builtin_setvar(struct ast_channel *, void *);
+void log_match_char_tree(struct match_char *node, char *prefix); /* for use anywhere */
+static int pbx_builtin_setvar_multiple(struct ast_channel *, void *);
+static int pbx_builtin_importvar(struct ast_channel *, void *);
+static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
+static void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid);
+static struct match_char *already_in_tree(struct match_char *current, char *pat);
+static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, struct ast_exten *e1, int findonly);
+static struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity);
+static void create_match_char_tree(struct ast_context *con);
+static struct ast_exten *get_canmatch_exten(struct match_char *node);
+static void destroy_pattern_tree(struct match_char *pattern_tree);
+static int hashtab_compare_contexts(const void *ah_a, const void *ah_b);
+static int hashtab_compare_extens(const void *ha_a, const void *ah_b);
+static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b);
+static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b);
+static unsigned int hashtab_hash_contexts(const void *obj);
+static unsigned int hashtab_hash_extens(const void *obj);
+static unsigned int hashtab_hash_priority(const void *obj);
+static unsigned int hashtab_hash_labels(const void *obj);
+
+/* labels, contexts are case sensitive priority numbers are ints */
+static int hashtab_compare_contexts(const void *ah_a, const void *ah_b)
+{
+ const struct ast_context *ac = ah_a;
+ const struct ast_context *bc = ah_b;
+ /* assume context names are registered in a string table! */
+ return strcmp(ac->name, bc->name);
+}
+
+static int hashtab_compare_extens(const void *ah_a, const void *ah_b)
+{
+ const struct ast_exten *ac = ah_a;
+ const struct ast_exten *bc = ah_b;
+ int x = strcmp(ac->exten, bc->exten);
+ if (x) /* if exten names are diff, then return */
+ return x;
+ /* but if they are the same, do the cidmatch values match? */
+ if (ac->matchcid && bc->matchcid) {
+ return strcmp(ac->cidmatch,bc->cidmatch);
+ } else if (!ac->matchcid && !bc->matchcid) {
+ return 0; /* if there's no matchcid on either side, then this is a match */
+ } else {
+ return 1; /* if there's matchcid on one but not the other, they are different */
+ }
+}
+
+static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b)
+{
+ const struct ast_exten *ac = ah_a;
+ const struct ast_exten *bc = ah_b;
+ return ac->priority != bc->priority;
+}
+
+static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b)
+{
+ const struct ast_exten *ac = ah_a;
+ const struct ast_exten *bc = ah_b;
+ return strcmp(ac->label, bc->label);
+}
+
+static unsigned int hashtab_hash_contexts(const void *obj)
+{
+ const struct ast_context *ac = obj;
+ return ast_hashtab_hash_string(ac->name);
+}
+
+static unsigned int hashtab_hash_extens(const void *obj)
+{
+ const struct ast_exten *ac = obj;
+ unsigned int x = ast_hashtab_hash_string(ac->exten);
+ unsigned int y = 0;
+ if (ac->matchcid)
+ y = ast_hashtab_hash_string(ac->cidmatch);
+ return x+y;
+}
+
+static unsigned int hashtab_hash_priority(const void *obj)
+{
+ const struct ast_exten *ac = obj;
+ return ast_hashtab_hash_int(ac->priority);
+}
+
+static unsigned int hashtab_hash_labels(const void *obj)
+{
+ const struct ast_exten *ac = obj;
+ return ast_hashtab_hash_string(ac->label);
+}
+
+
+AST_RWLOCK_DEFINE_STATIC(globalslock);
+static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+static int autofallthrough = 1;
+static int extenpatternmatchnew = 0;
+
+/*! \brief Subscription for device state change events */
+static struct ast_event_sub *device_state_sub;
+
+AST_MUTEX_DEFINE_STATIC(maxcalllock);
+static int countcalls;
+static int totalcalls;
+
+static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
+
+/*! \brief Declaration of builtin applications */
+static struct pbx_builtin {
+ char name[AST_MAX_APP];
+ int (*execute)(struct ast_channel *chan, void *data);
+ char *synopsis;
+ char *description;
+} builtins[] =
+{
+ /* These applications are built into the PBX core and do not
+ need separate modules */
+
+ { "Answer", pbx_builtin_answer,
+ "Answer a channel if ringing",
+ " Answer([delay]): If the call has not been answered, this application will\n"
+ "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
+ "Asterisk will wait this number of milliseconds before returning to\n"
+ "the dialplan after answering the call.\n"
+ },
+
+ { "BackGround", pbx_builtin_background,
+ "Play an audio file while waiting for digits of an extension to go to.",
+ " Background(filename1[&filename2...][,options[,langoverride][,context]]):\n"
+ "This application will play the given list of files (do not put extension)\n"
+ "while waiting for an extension to be dialed by the calling channel. To\n"
+ "continue waiting for digits after this application has finished playing\n"
+ "files, the WaitExten application should be used. The 'langoverride' option\n"
+ "explicitly specifies which language to attempt to use for the requested sound\n"
+ "files. If a 'context' is specified, this is the dialplan context that this\n"
+ "application will use when exiting to a dialed extension."
+ " If one of the requested sound files does not exist, call processing will be\n"
+ "terminated.\n"
+ " Options:\n"
+ " s - Causes the playback of the message to be skipped\n"
+ " if the channel is not in the 'up' state (i.e. it\n"
+ " hasn't been answered yet). If this happens, the\n"
+ " application will return immediately.\n"
+ " n - Don't answer the channel before playing the files.\n"
+ " m - Only break if a digit hit matches a one digit\n"
+ " extension in the destination context.\n"
+ "This application sets the following channel variable upon completion:\n"
+ " BACKGROUNDSTATUS The status of the background attempt as a text string, one of\n"
+ " SUCCESS | FAILED\n"
+ },
+
+ { "Busy", pbx_builtin_busy,
+ "Indicate the Busy condition",
+ " Busy([timeout]): This application will indicate the busy condition to\n"
+ "the calling channel. If the optional timeout is specified, the calling channel\n"
+ "will be hung up after the specified number of seconds. Otherwise, this\n"
+ "application will wait until the calling channel hangs up.\n"
+ },
+
+ { "Congestion", pbx_builtin_congestion,
+ "Indicate the Congestion condition",
+ " Congestion([timeout]): This application will indicate the congestion\n"
+ "condition to the calling channel. If the optional timeout is specified, the\n"
+ "calling channel will be hung up after the specified number of seconds.\n"
+ "Otherwise, this application will wait until the calling channel hangs up.\n"
+ },
+
+ { "ExecIfTime", pbx_builtin_execiftime,
+ "Conditional application execution based on the current time",
+ " ExecIfTime(<times>,<weekdays>,<mdays>,<months>?appname[(appargs)]):\n"
+ "This application will execute the specified dialplan application, with optional\n"
+ "arguments, if the current time matches the given time specification.\n"
+ },
+
+ { "Goto", pbx_builtin_goto,
+ "Jump to a particular priority, extension, or context",
+ " Goto([[context,]extension,]priority): This application will set the current\n"
+ "context, extension, and priority in the channel structure. After it completes, the\n"
+ "pbx engine will continue dialplan execution at the specified location.\n"
+ "If no specific extension, or extension and context, are specified, then this\n"
+ "application will just set the specified priority of the current extension.\n"
+ " At least a priority is required as an argument, or the goto will return a -1,\n"
+ "and the channel and call will be terminated.\n"
+ " If the location that is put into the channel information is bogus, and asterisk cannot\n"
+ "find that location in the dialplan,\n"
+ "then the execution engine will try to find and execute the code in the 'i' (invalid)\n"
+ "extension in the current context. If that does not exist, it will try to execute the\n"
+ "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
+ "channel is hung up, and the execution of instructions on the channel is terminated.\n"
+ "What this means is that, for example, you specify a context that does not exist, then\n"
+ "it will not be possible to find the 'h' or 'i' extensions, and the call will terminate!\n"
+ },
+
+ { "GotoIf", pbx_builtin_gotoif,
+ "Conditional goto",
+ " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will set the current\n"
+ "context, extension, and priority in the channel structure based on the evaluation of\n"
+ "the given condition. After this application completes, the\n"
+ "pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
+ "The channel will continue at\n"
+ "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n"
+ "false. The labels are specified with the same syntax as used within the Goto\n"
+ "application. If the label chosen by the condition is omitted, no jump is\n"
+ "performed, and the execution passes to the next instruction.\n"
+ "If the target location is bogus, and does not exist, the execution engine will try \n"
+ "to find and execute the code in the 'i' (invalid)\n"
+ "extension in the current context. If that does not exist, it will try to execute the\n"
+ "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
+ "channel is hung up, and the execution of instructions on the channel is terminated.\n"
+ "Remember that this command can set the current context, and if the context specified\n"
+ "does not exist, then it will not be able to find any 'h' or 'i' extensions there, and\n"
+ "the channel and call will both be terminated!\n"
+ },
+
+ { "GotoIfTime", pbx_builtin_gotoiftime,
+ "Conditional Goto based on the current time",
+ " GotoIfTime(<times>,<weekdays>,<mdays>,<months>?[[context,]exten,]priority):\n"
+ "This application will set the context, extension, and priority in the channel structure\n"
+ "if the current time matches the given time specification. Otherwise, nothing is done.\n"
+ "Further information on the time specification can be found in examples\n"
+ "illustrating how to do time-based context includes in the dialplan.\n"
+ "If the target jump location is bogus, the same actions would be taken as for Goto.\n"
+ },
+
+ { "ImportVar", pbx_builtin_importvar,
+ "Import a variable from a channel into a new variable",
+ " ImportVar(newvar=channelname,variable): This application imports a variable\n"
+ "from the specified channel (as opposed to the current one) and stores it as\n"
+ "a variable in the current channel (the channel that is calling this\n"
+ "application). Variables created by this application have the same inheritance\n"
+ "properties as those created with the Set application. See the documentation for\n"
+ "Set for more information.\n"
+ },
+
+ { "Hangup", pbx_builtin_hangup,
+ "Hang up the calling channel",
+ " Hangup([causecode]): This application will hang up the calling channel.\n"
+ "If a causecode is given the channel's hangup cause will be set to the given\n"
+ "value.\n"
+ },
+
+ { "NoOp", pbx_builtin_noop,
+ "Do Nothing (No Operation)",
+ " NoOp(): This application does nothing. However, it is useful for debugging\n"
+ "purposes. Any text that is provided as arguments to this application can be\n"
+ "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
+ "variables or functions without having any effect. Alternatively, see the\n"
+ "Verbose() application for finer grain control of output at custom verbose levels.\n"
+ },
+
+ { "Progress", pbx_builtin_progress,
+ "Indicate progress",
+ " Progress(): This application will request that in-band progress information\n"
+ "be provided to the calling channel.\n"
+ },
+
+ { "RaiseException", pbx_builtin_raise_exception,
+ "Handle an exceptional condition",
+ " RaiseException(<reason>): This application will jump to the \"e\" extension\n"
+ "in the current context, setting the dialplan function EXCEPTION(). If the \"e\"\n"
+ "extension does not exist, the call will hangup.\n"
+ },
+
+ { "ResetCDR", pbx_builtin_resetcdr,
+ "Resets the Call Data Record",
+ " ResetCDR([options]): This application causes the Call Data Record to be\n"
+ "reset.\n"
+ " Options:\n"
+ " w -- Store the current CDR record before resetting it.\n"
+ " a -- Store any stacked records.\n"
+ " v -- Save CDR variables.\n"
+ },
+
+ { "Ringing", pbx_builtin_ringing,
+ "Indicate ringing tone",
+ " Ringing(): This application will request that the channel indicate a ringing\n"
+ "tone to the user.\n"
+ },
+
+ { "SayAlpha", pbx_builtin_saycharacters,
+ "Say Alpha",
+ " SayAlpha(string): This application will play the sounds that correspond to\n"
+ "the letters of the given string.\n"
+ },
+
+ { "SayDigits", pbx_builtin_saydigits,
+ "Say Digits",
+ " SayDigits(digits): This application will play the sounds that correspond\n"
+ "to the digits of the given number. This will use the language that is currently\n"
+ "set for the channel. See the LANGUAGE function for more information on setting\n"
+ "the language for the channel.\n"
+ },
+
+ { "SayNumber", pbx_builtin_saynumber,
+ "Say Number",
+ " SayNumber(digits[,gender]): This application will play the sounds that\n"
+ "correspond to the given number. Optionally, a gender may be specified.\n"
+ "This will use the language that is currently set for the channel. See the\n"
+ "LANGUAGE function for more information on setting the language for the channel.\n"
+ },
+
+ { "SayPhonetic", pbx_builtin_sayphonetic,
+ "Say Phonetic",
+ " SayPhonetic(string): This application will play the sounds from the phonetic\n"
+ "alphabet that correspond to the letters in the given string.\n"
+ },
+
+ { "Set", pbx_builtin_setvar,
+ "Set channel variable or function value",
+ " Set(name=value)\n"
+ "This function can be used to set the value of channel variables or dialplan\n"
+ "functions. When setting variables, if the variable name is prefixed with _,\n"
+ "the variable will be inherited into channels created from the current\n"
+ "channel. If the variable name is prefixed with __, the variable will be\n"
+ "inherited into channels created from the current channel and all children\n"
+ "channels.\n"
+ },
+
+ { "MSet", pbx_builtin_setvar_multiple,
+ "Set channel variable(s) or function value(s)",
+ " MSet(name1=value1,name2=value2,...)\n"
+ "This function can be used to set the value of channel variables or dialplan\n"
+ "functions. When setting variables, if the variable name is prefixed with _,\n"
+ "the variable will be inherited into channels created from the current\n"
+ "channel. If the variable name is prefixed with __, the variable will be\n"
+ "inherited into channels created from the current channel and all children\n"
+ "channels.\n\n"
+ "MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus\n"
+ "prone to doing things that you may not expect. Avoid its use if possible.\n"
+ },
+
+ { "SetAMAFlags", pbx_builtin_setamaflags,
+ "Set the AMA Flags",
+ " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
+ " billing purposes.\n"
+ },
+
+ { "Wait", pbx_builtin_wait,
+ "Waits for some time",
+ " Wait(seconds): This application waits for a specified number of seconds.\n"
+ "Then, dialplan execution will continue at the next priority.\n"
+ " Note that the seconds can be passed with fractions of a second. For example,\n"
+ "'1.5' will ask the application to wait for 1.5 seconds.\n"
+ },
+
+ { "WaitExten", pbx_builtin_waitexten,
+ "Waits for an extension to be entered",
+ " WaitExten([seconds][,options]): This application waits for the user to enter\n"
+ "a new extension for a specified number of seconds.\n"
+ " Note that the seconds can be passed with fractions of a second. For example,\n"
+ "'1.5' will ask the application to wait for 1.5 seconds.\n"
+ " Options:\n"
+ " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
+ " Optionally, specify the class for music on hold within parenthesis.\n"
+ },
+
+ { "KeepAlive", pbx_builtin_keepalive,
+ "returns AST_PBX_KEEPALIVE value",
+ " KeepAlive(): This application is chiefly meant for internal use with Gosubs.\n"
+ "Please do not run it alone from the dialplan!\n"
+ },
+
+};
+
+static struct ast_context *contexts;
+static struct ast_hashtab *contexts_tree = NULL;
+
+AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
+
+static AST_RWLIST_HEAD_STATIC(apps, ast_app);
+
+static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
+
+static int stateid = 1;
+/* WARNING:
+ When holding this list's lock, do _not_ do anything that will cause conlock
+ to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
+ function will take the locks in conlock/hints order, so any other
+ paths that require both locks must also take them in that order.
+*/
+static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
+struct ast_state_cb *statecbs;
+
+/*
+ \note This function is special. It saves the stack so that no matter
+ how many times it is called, it returns to the same place */
+int pbx_exec(struct ast_channel *c, /*!< Channel */
+ struct ast_app *app, /*!< Application */
+ void *data) /*!< Data for execution */
+{
+ int res;
+ struct ast_module_user *u = NULL;
+ const char *saved_c_appl;
+ const char *saved_c_data;
+
+ if (c->cdr && !ast_check_hangup(c))
+ ast_cdr_setapp(c->cdr, app->name, data);
+
+ /* save channel values */
+ saved_c_appl= c->appl;
+ saved_c_data= c->data;
+
+ c->appl = app->name;
+ c->data = data;
+ if (app->module)
+ u = __ast_module_user_add(app->module, c);
+ res = app->execute(c, data);
+ if (app->module && u)
+ __ast_module_user_remove(app->module, u);
+ /* restore channel values */
+ c->appl = saved_c_appl;
+ c->data = saved_c_data;
+ return res;
+}
+
+
+/*! Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK 128
+
+/*! \brief Find application handle in linked list
+ */
+struct ast_app *pbx_findapp(const char *app)
+{
+ struct ast_app *tmp;
+
+ AST_RWLIST_RDLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, tmp, list) {
+ if (!strcasecmp(tmp->name, app))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&apps);
+
+ return tmp;
+}
+
+static struct ast_switch *pbx_findswitch(const char *sw)
+{
+ struct ast_switch *asw;
+
+ AST_RWLIST_RDLOCK(&switches);
+ AST_RWLIST_TRAVERSE(&switches, asw, list) {
+ if (!strcasecmp(asw->name, sw))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&switches);
+
+ return asw;
+}
+
+static inline int include_valid(struct ast_include *i)
+{
+ if (!i->hastime)
+ return 1;
+
+ return ast_check_timing(&(i->timing));
+}
+
+static void pbx_destroy(struct ast_pbx *p)
+{
+ ast_free(p);
+}
+
+/* form a tree that fully describes all the patterns in a context's extensions
+ * in this tree, a "node" consists of a series of match_char structs linked in a chain
+ * via the alt_char pointers. More than one pattern can share the same parts of the
+ * tree as other extensions with the same pattern to that point. The algorithm to
+ * find which pattern best matches a string, would follow **all** matching paths. As
+ * complete matches are found, a "max" match record would be updated if the match first involves
+ * a longer string, then, on a tie, a smaller total of specificity. This can be accomplished
+ * by recursive calls affecting a shared scoreboard.
+ * As and example, consider these 4 extensions:
+ * (a) NXXNXXXXXX
+ * (b) 307754XXXX
+ * (c) fax
+ * (d) NXXXXXXXXX
+ *
+ * In the above, between (a) and (d), (a) is a more specific pattern than (d), and would win over
+ * most numbers. For all numbers beginning with 307754, (b) should always win.
+ *
+ * These pattern should form a tree that looks like this:
+ * { "N" } --next--> { "X" } --next--> { "X" } --next--> { "N" } --next--> { "X" } ... blah ... --> { "X" exten_match: (a) }
+ * | |
+ * | |alt
+ * |alt |
+ * | { "X" } --next--> { "X" } ... blah ... --> { "X" exten_match: (d) }
+ * |
+ * { "3" } --next--> { "0" } --next--> { "7" } --next--> { "7" } --next--> { "5" } ... blah ... --> { "X" exten_match: (b) }
+ * |
+ * |alt
+ * |
+ * { "f" } --next--> { "a" } --next--> { "x" exten_match: (c) }
+ *
+ * In the above, I could easily turn "N" into "23456789", but I think that a quick "if( *z >= '2' && *z <= '9' )" might take
+ * fewer CPU cycles than a call to index("23456789",*z), where *z is the char to match...
+ *
+ * traversal is pretty simple: one routine merely traverses the alt list, and for each match in the pattern, it calls itself
+ * on the corresponding next pointer, incrementing also the pointer of the string to be matched, and passing the total specificity and length.
+ * We pass a pointer to a scoreboard down through, also.
+ * When an exten_match pointer is set, or a '.' or a '!' is encountered, we update the scoreboard only if the length is greater, or in case
+ * of equal length, if the specificity is lower, and return. Hope the limit on stack depth won't be a problem... this routine should
+ * be pretty lean as far a stack usage goes. Any non-match terminates the recursion down a branch.
+ *
+ * In the above example, with the number "3077549999" as the pattern, the traversor should match extensions a, b and d. All are
+ * of length 10; but they have total specificities of 96, 46, and 98, respectively. (b) wins with its lower specificity number!
+ *
+ * Just how much time this algorithm might save over a plain linear traversal over all possible patterns is unknown. But, it should
+ * be pretty close to optimal for this sort of overall algorithm.
+ *
+ * */
+
+/* you only really update the scoreboard, if the new score is BETTER than the
+ * one stored there. ignore it otherwise.
+ */
+
+
+static void update_scoreboard(struct scoreboard *board, int length, int spec, struct ast_exten *exten, char last, const char *callerid, int deleted, struct match_char *node)
+{
+ /* doing a matchcid() check here would be easy and cheap, but...
+ unfortunately, it only works if an extension can have only one
+ cid to match. That's not real life. CID patterns need to exist
+ in the tree for this sort of thing to work properly. */
+
+ /* if this extension is marked as deleted, then skip this -- if it never shows
+ on the scoreboard, it will never be found */
+ if (deleted)
+ return;
+ if (length > board->total_length) {
+ board->total_specificity = spec;
+ board->total_length = length;
+ board->exten = exten;
+ board->last_char = last;
+ board->node = node;
+ } else if (length == board->total_length && spec < board->total_specificity) {
+ board->total_specificity = spec;
+ board->total_length = length;
+ board->exten = exten;
+ board->last_char = last;
+ board->node = node;
+ }
+}
+
+void log_match_char_tree(struct match_char *node, char *prefix)
+{
+ char my_prefix[1024];
+ char extenstr[40];
+
+ extenstr[0] = 0;
+ if (node && node->exten && node->exten)
+ sprintf(extenstr,"(%p)",node->exten);
+
+ if (strlen(node->x) > 1 )
+ ast_log(LOG_DEBUG,"%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted? 'D':'-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ else
+ ast_log(LOG_DEBUG,"%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted? 'D':'-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ strcpy(my_prefix,prefix);
+ strcat(my_prefix,"+ ");
+ if (node->next_char)
+ log_match_char_tree(node->next_char, my_prefix);
+ if (node->alt_char)
+ log_match_char_tree(node->alt_char, prefix);
+}
+
+static void cli_match_char_tree(struct match_char *node, char *prefix, int fd)
+{
+ char my_prefix[1024];
+ char extenstr[40];
+
+ extenstr[0] = 0;
+ if (node && node->exten && node->exten)
+ sprintf(extenstr,"(%p)",node->exten);
+
+ if (strlen(node->x) > 1)
+ ast_cli(fd, "%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ else
+ ast_cli(fd, "%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ strcpy(my_prefix,prefix);
+ strcat(my_prefix,"+ ");
+ if (node->next_char)
+ cli_match_char_tree(node->next_char, my_prefix, fd);
+ if (node->alt_char)
+ cli_match_char_tree(node->alt_char, prefix, fd);
+}
+
+static struct ast_exten *get_canmatch_exten(struct match_char *node)
+{
+ /* find the exten at the end of the rope */
+ struct match_char *node2 = node;
+ for (node2 = node; node2; node2 = node2->next_char)
+ if (node2->exten)
+ return node2->exten;
+ return 0;
+}
+
+static struct ast_exten *trie_find_next_match(struct match_char *node)
+{
+ struct match_char *m3;
+ struct match_char *m4;
+ struct ast_exten *e3;
+
+ if (node && node->x[0] == '.' && !node->x[1]) /* dot and ! will ALWAYS be next match in a matchmore */
+ return node->exten;
+
+ if (node && node->x[0] == '!' && !node->x[1])
+ return node->exten;
+
+ if (!node || !node->next_char)
+ return NULL;
+
+ m3 = node->next_char;
+
+ if (m3->exten)
+ return m3->exten;
+ for(m4=m3->alt_char; m4; m4 = m4->alt_char) {
+ if (m4->exten)
+ return m4->exten;
+ }
+ for(m4=m3; m4; m4 = m4->alt_char) {
+ e3 = trie_find_next_match(m3);
+ if (e3)
+ return e3;
+ }
+ return NULL;
+}
+
+static void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid)
+{
+ struct match_char *p; /* note minimal stack storage requirements */
+#ifdef DEBUG_THIS
+ if (tree)
+ ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree %s\n", str, tree->x);
+ else
+ ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree NULL\n", str);
+#endif
+ for (p=tree; p; p=p->alt_char) {
+ if (p->x[0] == 'N' && p->x[1] == 0 && *str >= '2' && *str <= '9' ) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p);
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1))
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ else
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ } else if (p->x[0] == 'Z' && p->x[1] == 0 && *str >= '1' && *str <= '9' ) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted,p);
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1))
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ else
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ } else if (p->x[0] == 'X' && p->x[1] == 0 && *str >= '0' && *str <= '9' ) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted,p);
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1))
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ else
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ } else if (p->x[0] == '.' && p->x[1] == 0) {
+ /* how many chars will the . match against? */
+ int i = 0;
+ const char *str2 = str;
+ while (*str2++) {
+ i++;
+ }
+ if (p->exten && !(*(str+1)))
+ update_scoreboard(score, length+i, spec+(i*p->specificity), p->exten, '.', callerid, p->deleted, p);
+ if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
+ new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid);
+ }
+ return;
+ } else if (p->x[0] == '!' && p->x[1] == 0) {
+ /* how many chars will the . match against? */
+ int i = 0;
+ const char *str2 = str;
+ while (*str2++) {
+ i++;
+ }
+ if (p->exten && !(*(str+1)))
+ update_scoreboard(score, length+1, spec+(p->specificity*i), p->exten, '!', callerid, p->deleted, p);
+ if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
+ new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid);
+ }
+ return;
+ } else if (p->x[0] == '/' && p->x[1] == 0) {
+ /* the pattern in the tree includes the cid match! */
+ if (p->next_char && callerid && *callerid) {
+ new_find_extension(callerid, score, p->next_char, length+1, spec, callerid);
+ }
+ } else if (index(p->x, *str)) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p);
+
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1)) {
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else {
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ }
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+/* the algorithm for forming the extension pattern tree is also a bit simple; you
+ * traverse all the extensions in a context, and for each char of the extension,
+ * you see if it exists in the tree; if it doesn't, you add it at the appropriate
+ * spot. What more can I say? At the end of the list, you cap it off by adding the
+ * address of the extension involved. Duplicate patterns will be complained about.
+ *
+ * Ideally, this would be done for each context after it is created and fully
+ * filled. It could be done as a finishing step after extensions.conf or .ael is
+ * loaded, or it could be done when the first search is encountered. It should only
+ * have to be done once, until the next unload or reload.
+ *
+ * I guess forming this pattern tree would be analogous to compiling a regex.
+ */
+
+static struct match_char *already_in_tree(struct match_char *current, char *pat)
+{
+ struct match_char *t;
+ if (!current)
+ return 0;
+ for (t=current; t; t=t->alt_char) {
+ if (strcmp(pat,t->x) == 0) /* uh, we may want to sort exploded [] contents to make matching easy */
+ return t;
+ }
+ return 0;
+}
+
+static struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity)
+{
+ struct match_char *m = ast_calloc(1,sizeof(struct match_char));
+ m->x = ast_strdup(pattern);
+ m->is_pattern = is_pattern;
+ if (specificity == 1 && is_pattern && pattern[0] == 'N')
+ m->specificity = 98;
+ else if (specificity == 1 && is_pattern && pattern[0] == 'Z')
+ m->specificity = 99;
+ else if (specificity == 1 && is_pattern && pattern[0] == 'X')
+ m->specificity = 100;
+ else if (specificity == 1 && is_pattern && pattern[0] == '.')
+ m->specificity = 200;
+ else if (specificity == 1 && is_pattern && pattern[0] == '!')
+ m->specificity = 200;
+ else
+ m->specificity = specificity;
+
+ if (!con->pattern_tree) {
+ con->pattern_tree = m;
+ } else {
+ if (already) { /* switch to the new regime (traversing vs appending)*/
+ m->alt_char = current->alt_char;
+ current->alt_char = m;
+ } else {
+ if (current->next_char) {
+ m->alt_char = current->next_char->alt_char;
+ current->next_char = m;
+ } else {
+ current->next_char = m;
+ }
+ }
+ }
+ return m;
+}
+
+static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, struct ast_exten *e1, int findonly)
+{
+ struct match_char *m1=0,*m2=0;
+ int specif;
+ int already;
+ int pattern = 0;
+ char buf[256];
+ char extenbuf[512];
+ char *s1 = extenbuf;
+ int l1 = strlen(e1->exten) + strlen(e1->cidmatch) + 2;
+
+
+ strncpy(extenbuf,e1->exten,sizeof(extenbuf));
+ if (e1->matchcid && l1 <= sizeof(extenbuf)) {
+ strcat(extenbuf,"/");
+ strcat(extenbuf,e1->cidmatch);
+ } else if (l1 > sizeof(extenbuf)) {
+ ast_log(LOG_ERROR,"The pattern %s/%s is too big to deal with: it will be ignored! Disaster!\n", e1->exten, e1->cidmatch);
+ return 0;
+ }
+#ifdef NEED_DEBUG
+ ast_log(LOG_DEBUG,"Adding exten %s%c%s to tree\n", s1, e1->matchcid? '/':' ', e1->matchcid? e1->cidmatch : "");
+#endif
+ m1 = con->pattern_tree; /* each pattern starts over at the root of the pattern tree */
+ already = 1;
+
+ if ( *s1 == '_') {
+ pattern = 1;
+ s1++;
+ }
+ while( *s1 ) {
+ if (pattern && *s1 == '[' && *(s1-1) != '\\') {
+ char *s2 = buf;
+ buf[0] = 0;
+ s1++; /* get past the '[' */
+ while (*s1 != ']' && *(s1-1) != '\\' ) {
+ if (*s1 == '\\') {
+ if (*(s1+1) == ']') {
+ *s2++ = ']';
+ s1++;s1++;
+ } else if (*(s1+1) == '\\') {
+ *s2++ = '\\';
+ s1++;s1++;
+ } else if (*(s1+1) == '-') {
+ *s2++ = '-';
+ s1++; s1++;
+ } else if (*(s1+1) == '[') {
+ *s2++ = '[';
+ s1++; s1++;
+ }
+ } else if (*s1 == '-') { /* remember to add some error checking to all this! */
+ char s3 = *(s1-1);
+ char s4 = *(s1+1);
+ for (s3++; s3 <= s4; s3++) {
+ *s2++ = s3;
+ }
+ s1++; s1++;
+ } else {
+ *s2++ = *s1++;
+ }
+ }
+ *s2 = 0; /* null terminate the exploded range */
+ specif = strlen(buf);
+ } else {
+
+ if (*s1 == '\\') {
+ s1++;
+ buf[0] = *s1;
+ } else {
+ if (pattern) {
+ if (*s1 == 'n') /* make sure n,x,z patterns are canonicalized to N,X,Z */
+ *s1 = 'N';
+ else if (*s1 == 'x')
+ *s1 = 'X';
+ else if (*s1 == 'z')
+ *s1 = 'Z';
+ }
+ buf[0] = *s1;
+ }
+ buf[1] = 0;
+ specif = 1;
+ }
+ m2 = 0;
+ if (already && (m2=already_in_tree(m1,buf)) && m2->next_char) {
+ if (!(*(s1+1))) { /* if this is the end of the pattern, but not the end of the tree, then mark this node with the exten...
+ a shorter pattern might win if the longer one doesn't match */
+ m2->exten = e1;
+ m2->deleted = 0;
+ }
+ m1 = m2->next_char; /* m1 points to the node to compare against */
+ } else {
+ if (m2) {
+ if (findonly)
+ return m2;
+ m1 = m2;
+ } else {
+ if (findonly)
+ return m1;
+ m1 = add_pattern_node(con, m1, buf, pattern, already,specif); /* m1 is the node just added */
+ }
+
+ if (!(*(s1+1))) {
+ m1->deleted = 0;
+ m1->exten = e1;
+ }
+
+ already = 0;
+ }
+ s1++; /* advance to next char */
+ }
+ return m1;
+}
+
+static void create_match_char_tree(struct ast_context *con)
+{
+ struct ast_hashtab_iter *t1;
+ struct ast_exten *e1;
+#ifdef NEED_DEBUG
+ int biggest_bucket, resizes, numobjs, numbucks;
+
+ ast_log(LOG_DEBUG,"Creating Extension Trie for context %s\n", con->name);
+ ast_hashtab_get_stats(con->root_tree, &biggest_bucket, &resizes, &numobjs, &numbucks);
+ ast_log(LOG_DEBUG,"This tree has %d objects in %d bucket lists, longest list=%d objects, and has resized %d times\n",
+ numobjs, numbucks, biggest_bucket, resizes);
+#endif
+ t1 = ast_hashtab_start_traversal(con->root_tree);
+ while( (e1 = ast_hashtab_next(t1)) ) {
+ if (e1->exten)
+ add_exten_to_pattern_tree(con, e1, 0);
+ else
+ ast_log(LOG_ERROR,"Attempt to create extension with no extension name.\n");
+ }
+ ast_hashtab_end_traversal(t1);
+}
+
+static void destroy_pattern_tree(struct match_char *pattern_tree) /* pattern tree is a simple binary tree, sort of, so the proper way to destroy it is... recursively! */
+{
+ /* destroy all the alternates */
+ if (pattern_tree->alt_char) {
+ destroy_pattern_tree(pattern_tree->alt_char);
+ pattern_tree->alt_char = 0;
+ }
+ /* destroy all the nexts */
+ if (pattern_tree->next_char) {
+ destroy_pattern_tree(pattern_tree->next_char);
+ pattern_tree->next_char = 0;
+ }
+ pattern_tree->exten = 0; /* never hurts to make sure there's no pointers laying around */
+ if (pattern_tree->x)
+ free(pattern_tree->x);
+ free(pattern_tree);
+}
+
+/*
+ * Special characters used in patterns:
+ * '_' underscore is the leading character of a pattern.
+ * In other position it is treated as a regular char.
+ * ' ' '-' space and '-' are separator and ignored.
+ * . one or more of any character. Only allowed at the end of
+ * a pattern.
+ * ! zero or more of anything. Also impacts the result of CANMATCH
+ * and MATCHMORE. Only allowed at the end of a pattern.
+ * In the core routine, ! causes a match with a return code of 2.
+ * In turn, depending on the search mode: (XXX check if it is implemented)
+ * - E_MATCH retuns 1 (does match)
+ * - E_MATCHMORE returns 0 (no match)
+ * - E_CANMATCH returns 1 (does match)
+ *
+ * / should not appear as it is considered the separator of the CID info.
+ * XXX at the moment we may stop on this char.
+ *
+ * X Z N match ranges 0-9, 1-9, 2-9 respectively.
+ * [ denotes the start of a set of character. Everything inside
+ * is considered literally. We can have ranges a-d and individual
+ * characters. A '[' and '-' can be considered literally if they
+ * are just before ']'.
+ * XXX currently there is no way to specify ']' in a range, nor \ is
+ * considered specially.
+ *
+ * When we compare a pattern with a specific extension, all characters in the extension
+ * itself are considered literally with the only exception of '-' which is considered
+ * as a separator and thus ignored.
+ * XXX do we want to consider space as a separator as well ?
+ * XXX do we want to consider the separators in non-patterns as well ?
+ */
+
+/*!
+ * \brief helper functions to sort extensions and patterns in the desired way,
+ * so that more specific patterns appear first.
+ *
+ * ext_cmp1 compares individual characters (or sets of), returning
+ * an int where bits 0-7 are the ASCII code of the first char in the set,
+ * while bit 8-15 are the cardinality of the set minus 1.
+ * This way more specific patterns (smaller cardinality) appear first.
+ * Wildcards have a special value, so that we can directly compare them to
+ * sets by subtracting the two values. In particular:
+ * 0x000xx one character, xx
+ * 0x0yyxx yy character set starting with xx
+ * 0x10000 '.' (one or more of anything)
+ * 0x20000 '!' (zero or more of anything)
+ * 0x30000 NUL (end of string)
+ * 0x40000 error in set.
+ * The pointer to the string is advanced according to needs.
+ * NOTES:
+ * 1. the empty set is equivalent to NUL.
+ * 2. given that a full set has always 0 as the first element,
+ * we could encode the special cases as 0xffXX where XX
+ * is 1, 2, 3, 4 as used above.
+ */
+static int ext_cmp1(const char **p)
+{
+ uint32_t chars[8];
+ int c, cmin = 0xff, count = 0;
+ const char *end;
+
+ /* load, sign extend and advance pointer until we find
+ * a valid character.
+ */
+ while ( (c = *(*p)++) && (c == ' ' || c == '-') )
+ ; /* ignore some characters */
+
+ /* always return unless we have a set of chars */
+ switch (c) {
+ default: /* ordinary character */
+ return 0x0000 | (c & 0xff);
+
+ case 'N': /* 2..9 */
+ return 0x0700 | '2' ;
+
+ case 'X': /* 0..9 */
+ return 0x0900 | '0';
+
+ case 'Z': /* 1..9 */
+ return 0x0800 | '1';
+
+ case '.': /* wildcard */
+ return 0x10000;
+
+ case '!': /* earlymatch */
+ return 0x20000; /* less specific than NULL */
+
+ case '\0': /* empty string */
+ *p = NULL;
+ return 0x30000;
+
+ case '[': /* pattern */
+ break;
+ }
+ /* locate end of set */
+ end = strchr(*p, ']');
+
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0x40000; /* XXX make this entry go last... */
+ }
+
+ bzero(chars, sizeof(chars)); /* clear all chars in the set */
+ for (; *p < end ; (*p)++) {
+ unsigned char c1, c2; /* first-last char in range */
+ c1 = (unsigned char)((*p)[0]);
+ if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
+ c2 = (unsigned char)((*p)[2]);
+ *p += 2; /* skip a total of 3 chars */
+ } else /* individual character */
+ c2 = c1;
+ if (c1 < cmin)
+ cmin = c1;
+ for (; c1 <= c2; c1++) {
+ uint32_t mask = 1 << (c1 % 32);
+ if ( (chars[ c1 / 32 ] & mask) == 0)
+ count += 0x100;
+ chars[ c1 / 32 ] |= mask;
+ }
+ }
+ (*p)++;
+ return count == 0 ? 0x30000 : (count | cmin);
+}
+
+/*!
+ * \brief the full routine to compare extensions in rules.
+ */
+static int ext_cmp(const char *a, const char *b)
+{
+ /* make sure non-patterns come first.
+ * If a is not a pattern, it either comes first or
+ * we use strcmp to compare the strings.
+ */
+ int ret = 0;
+
+ if (a[0] != '_')
+ return (b[0] == '_') ? -1 : strcmp(a, b);
+
+ /* Now we know a is a pattern; if b is not, a comes first */
+ if (b[0] != '_')
+ return 1;
+#if 0 /* old mode for ext matching */
+ return strcmp(a, b);
+#endif
+ /* ok we need full pattern sorting routine */
+ while (!ret && a && b)
+ ret = ext_cmp1(&a) - ext_cmp1(&b);
+ if (ret == 0)
+ return 0;
+ else
+ return (ret > 0) ? 1 : -1;
+}
+
+int ast_extension_cmp(const char *a, const char *b)
+{
+ return ext_cmp(a, b);
+}
+
+/*!
+ * \internal
+ * \brief used ast_extension_{match|close}
+ * mode is as follows:
+ * E_MATCH success only on exact match
+ * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
+ * E_CANMATCH either of the above.
+ * \retval 0 on no-match
+ * \retval 1 on match
+ * \retval 2 on early match.
+ */
+
+static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ mode &= E_MATCH_MASK; /* only consider the relevant bits */
+
+ if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
+ return 1;
+
+ if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
+ int ld = strlen(data), lp = strlen(pattern);
+
+ if (lp < ld) /* pattern too short, cannot match */
+ return 0;
+ /* depending on the mode, accept full or partial match or both */
+ if (mode == E_MATCH)
+ return !strcmp(pattern, data); /* 1 on match, 0 on fail */
+ if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
+ return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
+ else
+ return 0;
+ }
+ pattern++; /* skip leading _ */
+ /*
+ * XXX below we stop at '/' which is a separator for the CID info. However we should
+ * not store '/' in the pattern at all. When we insure it, we can remove the checks.
+ */
+ while (*data && *pattern && *pattern != '/') {
+ const char *end;
+
+ if (*data == '-') { /* skip '-' in data (just a separator) */
+ data++;
+ continue;
+ }
+ switch (toupper(*pattern)) {
+ case '[': /* a range */
+ end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0; /* unconditional failure */
+ }
+ for (pattern++; pattern != end; pattern++) {
+ if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
+ if (*data >= pattern[0] && *data <= pattern[2])
+ break; /* match found */
+ else {
+ pattern += 2; /* skip a total of 3 chars */
+ continue;
+ }
+ } else if (*data == pattern[0])
+ break; /* match found */
+ }
+ if (pattern == end)
+ return 0;
+ pattern = end; /* skip and continue */
+ break;
+ case 'N':
+ if (*data < '2' || *data > '9')
+ return 0;
+ break;
+ case 'X':
+ if (*data < '0' || *data > '9')
+ return 0;
+ break;
+ case 'Z':
+ if (*data < '1' || *data > '9')
+ return 0;
+ break;
+ case '.': /* Must match, even with more digits */
+ return 1;
+ case '!': /* Early match */
+ return 2;
+ case ' ':
+ case '-': /* Ignore these in patterns */
+ data--; /* compensate the final data++ */
+ break;
+ default:
+ if (*data != *pattern)
+ return 0;
+ }
+ data++;
+ pattern++;
+ }
+ if (*data) /* data longer than pattern, no match */
+ return 0;
+ /*
+ * match so far, but ran off the end of the data.
+ * Depending on what is next, determine match or not.
+ */
+ if (*pattern == '\0' || *pattern == '/') /* exact match */
+ return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
+ else if (*pattern == '!') /* early match */
+ return 2;
+ else /* partial match */
+ return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
+}
+
+/*
+ * Wrapper around _extension_match_core() to do performance measurement
+ * using the profiling code.
+ */
+static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ int i;
+ static int prof_id = -2; /* marker for 'unallocated' id */
+ if (prof_id == -2)
+ prof_id = ast_add_profile("ext_match", 0);
+ ast_mark(prof_id, 1);
+ i = _extension_match_core(pattern, data, mode);
+ ast_mark(prof_id, 0);
+ return i;
+}
+
+int ast_extension_match(const char *pattern, const char *data)
+{
+ return extension_match_core(pattern, data, E_MATCH);
+}
+
+int ast_extension_close(const char *pattern, const char *data, int needmore)
+{
+ if (needmore != E_MATCHMORE && needmore != E_CANMATCH)
+ ast_log(LOG_WARNING, "invalid argument %d\n", needmore);
+ return extension_match_core(pattern, data, needmore);
+}
+
+struct fake_context /* this struct is purely for matching in the hashtab */
+{
+ ast_rwlock_t lock;
+ struct ast_exten *root;
+ struct ast_hashtab *root_tree;
+ struct match_char *pattern_tree;
+ struct ast_context *next;
+ struct ast_include *includes;
+ struct ast_ignorepat *ignorepats;
+ const char *registrar;
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts;
+ ast_mutex_t macrolock;
+ char name[256];
+};
+
+struct ast_context *ast_context_find(const char *name)
+{
+ struct ast_context *tmp = NULL;
+ struct fake_context item;
+ strncpy(item.name,name,256);
+ ast_rdlock_contexts();
+ if( contexts_tree ) {
+ tmp = ast_hashtab_lookup(contexts_tree,&item);
+ } else {
+ while ( (tmp = ast_walk_contexts(tmp)) ) {
+ if (!name || !strcasecmp(name, tmp->name))
+ break;
+ }
+ }
+ ast_unlock_contexts();
+ return tmp;
+}
+
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+static int matchcid(const char *cidpattern, const char *callerid)
+{
+ /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
+ failing to get a number should count as a match, otherwise not */
+
+ if (ast_strlen_zero(callerid))
+ return ast_strlen_zero(cidpattern) ? 1 : 0;
+
+ return ast_extension_match(cidpattern, callerid);
+}
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass, struct pbx_find_info *q,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action)
+{
+ int x, res;
+ struct ast_context *tmp=0;
+ struct ast_exten *e=0, *eroot=0;
+ struct ast_include *i = 0;
+ struct ast_sw *sw = 0;
+ struct ast_exten pattern = {0};
+ struct scoreboard score = {0};
+
+ pattern.label = label;
+ pattern.priority = priority;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Looking for cont/ext/prio/label/action = %s/%s/%d/%s/%d\n", context, exten, priority, label, (int)action);
+#endif
+ /* Initialize status if appropriate */
+ if (q->stacklen == 0) {
+ q->status = STATUS_NO_CONTEXT;
+ q->swo = NULL;
+ q->data = NULL;
+ q->foundcontext = NULL;
+ } else if (q->stacklen >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
+ return NULL;
+ }
+
+ /* Check first to see if we've already been checked */
+ for (x = 0; x < q->stacklen; x++) {
+ if (!strcasecmp(q->incstack[x], context))
+ return NULL;
+ }
+
+ if (bypass) /* bypass means we only look there */
+ tmp = bypass;
+ else { /* look in contexts */
+ struct fake_context item;
+ strncpy(item.name,context,256);
+ tmp = ast_hashtab_lookup(contexts_tree,&item);
+#ifdef NOTNOW
+ tmp = NULL;
+ while ((tmp = ast_walk_contexts(tmp)) ) {
+ if (!strcmp(tmp->name, context))
+ break;
+ }
+#endif
+ if (!tmp)
+ return NULL;
+
+ }
+
+ if (q->status < STATUS_NO_EXTENSION)
+ q->status = STATUS_NO_EXTENSION;
+
+ /* Do a search for matching extension */
+
+ eroot = NULL;
+ score.total_specificity = 0;
+ score.exten = 0;
+ score.total_length = 0;
+ if (!tmp->pattern_tree && tmp->root_tree)
+ {
+ create_match_char_tree(tmp);
+#ifdef NEED_DEBUG
+ ast_log(LOG_DEBUG,"Tree Created in context %s:\n", context);
+ log_match_char_tree(tmp->pattern_tree," ");
+#endif
+ }
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"The Trie we are searching in:\n");
+ log_match_char_tree(tmp->pattern_tree, ":: ");
+#endif
+
+ if (extenpatternmatchnew) {
+ new_find_extension(exten, &score, tmp->pattern_tree, 0, 0, callerid);
+ eroot = score.exten;
+
+ if (score.last_char == '!' && action == E_MATCHMORE) {
+ /* We match an extension ending in '!'.
+ * The decision in this case is final and is NULL (no match).
+ */
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning MATCHMORE NULL with exclamation point.\n");
+#endif
+ return NULL;
+ }
+
+ if (!eroot && (action == E_CANMATCH || action == E_MATCHMORE) && score.canmatch_exten) {
+ q->status = STATUS_SUCCESS;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning CANMATCH exten %s\n", score.canmatch_exten->exten);
+#endif
+ return score.canmatch_exten;
+ }
+
+ if ((action == E_MATCHMORE || action == E_CANMATCH) && eroot) {
+ if (score.node) {
+ struct ast_exten *z = trie_find_next_match(score.node);
+#ifdef NEED_DEBUG
+ if (z)
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten %s\n", z->exten);
+ else
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten NULL\n");
+#endif
+ return z;
+ }
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE NULL (no next_match)\n");
+#endif
+ return NULL; /* according to the code, complete matches are null matches in MATCHMORE mode */
+ }
+
+ if (eroot) {
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ if (action == E_FINDLABEL && label ) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+ } else {
+ e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+ }
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning complete match of exten %s\n", e->exten);
+#endif
+ return e;
+ }
+ }
+ } else {
+
+ /* scan the list trying to match extension and CID */
+ eroot = NULL;
+ while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
+ int match = extension_match_core(eroot->exten, exten, action);
+ /* 0 on fail, 1 on match, 2 on earlymatch */
+
+ if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
+ continue; /* keep trying */
+ if (match == 2 && action == E_MATCHMORE) {
+ /* We match an extension ending in '!'.
+ * The decision in this case is final and is NULL (no match).
+ */
+ return NULL;
+ }
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ if (action == E_FINDLABEL && label ) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+ } else {
+ e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+ }
+#ifdef NOTNOW
+ while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
+ /* Match label or priority */
+ if (action == E_FINDLABEL) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ if (label && e->label && !strcmp(label, e->label))
+ break; /* found it */
+ } else if (e->priority == priority) {
+ break; /* found it */
+ } /* else keep searching */
+ }
+#endif
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+ return e;
+ }
+ }
+ }
+
+
+ /* Check alternative switches */
+ AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
+ struct ast_switch *asw = pbx_findswitch(sw->name);
+ ast_switch_f *aswf = NULL;
+ char *datap;
+
+ if (!asw) {
+ ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
+ continue;
+ }
+ /* Substitute variables now */
+
+ if (sw->eval)
+ pbx_substitute_variables_helper(chan, sw->data, sw->tmpdata, SWITCH_DATA_LENGTH - 1);
+
+ /* equivalent of extension_match_core() at the switch level */
+ if (action == E_CANMATCH)
+ aswf = asw->canmatch;
+ else if (action == E_MATCHMORE)
+ aswf = asw->matchmore;
+ else /* action == E_MATCH */
+ aswf = asw->exists;
+ datap = sw->eval ? sw->tmpdata : sw->data;
+ if (!aswf)
+ res = 0;
+ else {
+ if (chan)
+ ast_autoservice_start(chan);
+ res = aswf(chan, context, exten, priority, callerid, datap);
+ if (chan)
+ ast_autoservice_stop(chan);
+ }
+ if (res) { /* Got a match */
+ q->swo = asw;
+ q->data = datap;
+ q->foundcontext = context;
+ /* XXX keep status = STATUS_NO_CONTEXT ? */
+ return NULL;
+ }
+ }
+ q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
+ /* Now try any includes we have in this context */
+ for (i = tmp->includes; i; i = i->next) {
+ if (include_valid(i)) {
+ if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action))) {
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning recursive match of %s\n", e->exten);
+#endif
+ return e;
+ }
+ if (q->swo)
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/*!
+ * \brief extract offset:length from variable name.
+ * \return 1 if there is a offset:length part, which is
+ * trimmed off (values go into variables)
+ */
+static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
+{
+ int parens=0;
+
+ *offset = 0;
+ *length = INT_MAX;
+ *isfunc = 0;
+ for (; *var; var++) {
+ if (*var == '(') {
+ (*isfunc)++;
+ parens++;
+ } else if (*var == ')') {
+ parens--;
+ } else if (*var == ':' && parens == 0) {
+ *var++ = '\0';
+ sscanf(var, "%d:%d", offset, length);
+ return 1; /* offset:length valid */
+ }
+ }
+ return 0;
+}
+
+/*!
+ *\brief takes a substring. It is ok to call with value == workspace.
+ * \param value
+ * \param offset < 0 means start from the end of the string and set the beginning
+ * to be that many characters back.
+ * \param length is the length of the substring, a value less than 0 means to leave
+ * that many off the end.
+ * \param workspace
+ * \param workspace_len
+ * Always return a copy in workspace.
+ */
+static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
+{
+ char *ret = workspace;
+ int lr; /* length of the input string after the copy */
+
+ ast_copy_string(workspace, value, workspace_len); /* always make a copy */
+
+ lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
+
+ /* Quick check if no need to do anything */
+ if (offset == 0 && length >= lr) /* take the whole string */
+ return ret;
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr)
+ return ret + lr; /* the final '\0' */
+
+ ret += offset; /* move to the start position */
+ if (length >= 0 && length < lr - offset) /* truncate if necessary */
+ ret[length] = '\0';
+ else if (length < 0) {
+ if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
+ ret[lr + length - offset] = '\0';
+ else
+ ret[0] = '\0';
+ }
+
+ return ret;
+}
+
+/*! \brief Support for Asterisk built-in variables in the dialplan
+
+\note See also
+ - \ref AstVar Channel variables
+ - \ref AstCauses The HANGUPCAUSE variable
+ */
+void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
+{
+ const char not_found = '\0';
+ char *tmpvar;
+ const char *s; /* the result */
+ int offset, length;
+ int i, need_substring;
+ struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
+
+ if (c) {
+ ast_channel_lock(c);
+ places[0] = &c->varshead;
+ }
+ /*
+ * Make a copy of var because parse_variable_name() modifies the string.
+ * Then if called directly, we might need to run substring() on the result;
+ * remember this for later in 'need_substring', 'offset' and 'length'
+ */
+ tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
+ need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
+
+ /*
+ * Look first into predefined variables, then into variable lists.
+ * Variable 's' points to the result, according to the following rules:
+ * s == &not_found (set at the beginning) means that we did not find a
+ * matching variable and need to look into more places.
+ * If s != &not_found, s is a valid result string as follows:
+ * s = NULL if the variable does not have a value;
+ * you typically do this when looking for an unset predefined variable.
+ * s = workspace if the result has been assembled there;
+ * typically done when the result is built e.g. with an snprintf(),
+ * so we don't need to do an additional copy.
+ * s != workspace in case we have a string, that needs to be copied
+ * (the ast_copy_string is done once for all at the end).
+ * Typically done when the result is already available in some string.
+ */
+ s = &not_found; /* default value */
+ if (c) { /* This group requires a valid channel */
+ /* Names with common parts are looked up a piece at a time using strncmp. */
+ if (!strncmp(var, "CALL", 4)) {
+ if (!strncmp(var + 4, "ING", 3)) {
+ if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
+ s = workspace;
+ } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
+ s = workspace;
+ } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
+ s = workspace;
+ } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
+ s = workspace;
+ }
+ }
+ } else if (!strcmp(var, "HINT")) {
+ s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
+ } else if (!strcmp(var, "HINTNAME")) {
+ s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
+ } else if (!strcmp(var, "EXTEN")) {
+ s = c->exten;
+ } else if (!strcmp(var, "CONTEXT")) {
+ s = c->context;
+ } else if (!strcmp(var, "PRIORITY")) {
+ snprintf(workspace, workspacelen, "%d", c->priority);
+ s = workspace;
+ } else if (!strcmp(var, "CHANNEL")) {
+ s = c->name;
+ } else if (!strcmp(var, "UNIQUEID")) {
+ s = c->uniqueid;
+ } else if (!strcmp(var, "HANGUPCAUSE")) {
+ snprintf(workspace, workspacelen, "%d", c->hangupcause);
+ s = workspace;
+ }
+ }
+ if (s == &not_found) { /* look for more */
+ if (!strcmp(var, "EPOCH")) {
+ snprintf(workspace, workspacelen, "%u",(int)time(NULL));
+ s = workspace;
+ } else if (!strcmp(var, "SYSTEMNAME")) {
+ s = ast_config_AST_SYSTEM_NAME;
+ }
+ }
+ /* if not found, look into chanvars or global vars */
+ for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
+ struct ast_var_t *variables;
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (strcasecmp(ast_var_name(variables), var)==0) {
+ s = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ }
+ if (s == &not_found || s == NULL)
+ *ret = NULL;
+ else {
+ if (s != workspace)
+ ast_copy_string(workspace, s, workspacelen);
+ *ret = workspace;
+ if (need_substring)
+ *ret = substring(*ret, offset, length, workspace, workspacelen);
+ }
+
+ if (c)
+ ast_channel_unlock(c);
+}
+
+static void exception_store_free(void *data)
+{
+ struct pbx_exception *exception = data;
+ ast_string_field_free_memory(exception);
+ ast_free(exception);
+}
+
+static struct ast_datastore_info exception_store_info = {
+ .type = "EXCEPTION",
+ .destroy = exception_store_free,
+};
+
+int pbx_builtin_raise_exception(struct ast_channel *chan, void *vreason)
+{
+ const char *reason = vreason;
+ struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+ struct pbx_exception *exception = NULL;
+
+ if (!ds) {
+ ds = ast_channel_datastore_alloc(&exception_store_info, NULL);
+ if (!ds)
+ return -1;
+ exception = ast_calloc(1, sizeof(struct pbx_exception));
+ if (!exception) {
+ ast_channel_datastore_free(ds);
+ return -1;
+ }
+ if (ast_string_field_init(exception, 128)) {
+ ast_free(exception);
+ ast_channel_datastore_free(ds);
+ return -1;
+ }
+ ds->data = exception;
+ ast_channel_datastore_add(chan, ds);
+ } else
+ exception = ds->data;
+
+ ast_string_field_set(exception, reason, reason);
+ ast_string_field_set(exception, context, chan->context);
+ ast_string_field_set(exception, exten, chan->exten);
+ exception->priority = chan->priority;
+ set_ext_pri(chan, "e", 0);
+ return 0;
+}
+
+static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
+{
+ struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+ struct pbx_exception *exception = NULL;
+ if (!ds || !ds->data)
+ return -1;
+ exception = ds->data;
+ if (!strcasecmp(data, "REASON"))
+ ast_copy_string(buf, exception->reason, buflen);
+ else if (!strcasecmp(data, "CONTEXT"))
+ ast_copy_string(buf, exception->context, buflen);
+ else if (!strncasecmp(data, "EXTEN", 5))
+ ast_copy_string(buf, exception->exten, buflen);
+ else if (!strcasecmp(data, "PRIORITY"))
+ snprintf(buf, buflen, "%d", exception->priority);
+ else
+ return -1;
+ return 0;
+}
+
+static struct ast_custom_function exception_function = {
+ .name = "EXCEPTION",
+ .synopsis = "Retrieve the details of the current dialplan exception",
+ .desc =
+"The following fields are available for retrieval:\n"
+" reason INVALID, ERROR, RESPONSETIMEOUT, ABSOLUTETIMEOUT, or custom\n"
+" value set by the RaiseException() application\n"
+" context The context executing when the exception occurred\n"
+" exten The extension executing when the exception occurred\n"
+" priority The numeric priority executing when the exception occurred\n",
+ .syntax = "EXCEPTION(<field>)",
+ .read = acf_exception_read,
+};
+
+static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_custom_function *acf;
+ int count_acf = 0;
+ int like = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show functions [like]";
+ e->usage =
+ "Usage: core show functions [like <text>]\n"
+ " List builtin functions, optionally only those matching a given string\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
+ like = 1;
+ } else if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
+
+ AST_RWLIST_RDLOCK(&acf_root);
+ AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!like || strstr(acf->name, a->argv[4])) {
+ count_acf++;
+ ast_cli(a->fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
+ }
+ }
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_custom_function *acf;
+ /* Maximum number of characters added by terminal coloring is 22 */
+ char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
+ char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
+ char stxtitle[40], *syntax = NULL;
+ int synopsis_size, description_size, syntax_size;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show function";
+ e->usage =
+ "Usage: core show function <function>\n"
+ " Describe a particular dialplan function.\n";
+ return NULL;
+ case CLI_GENERATE:
+ wordlen = strlen(a->word);
+ /* case-insensitive for convenience in this 'complete' function */
+ AST_RWLIST_RDLOCK(&acf_root);
+ AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!strncasecmp(a->word, acf->name, wordlen) && ++which > a->n) {
+ ret = ast_strdup(acf->name);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ return ret;
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ if (!(acf = ast_custom_function_find(a->argv[3]))) {
+ ast_cli(a->fd, "No function by that name registered.\n");
+ return CLI_FAILURE;
+
+ }
+
+ if (acf->synopsis)
+ synopsis_size = strlen(acf->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (acf->desc)
+ description_size = strlen(acf->desc) + 23;
+ else
+ description_size = strlen("Not available") + 23;
+ description = alloca(description_size);
+
+ if (acf->syntax)
+ syntax_size = strlen(acf->syntax) + 23;
+ else
+ syntax_size = strlen("Not available") + 23;
+ syntax = alloca(syntax_size);
+
+ snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
+ term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
+ term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
+ term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
+ term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
+ term_color(syntax,
+ acf->syntax ? acf->syntax : "Not available",
+ COLOR_CYAN, 0, syntax_size);
+ term_color(synopsis,
+ acf->synopsis ? acf->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ acf->desc ? acf->desc : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(a->fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
+
+ return CLI_SUCCESS;
+}
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ struct ast_custom_function *acf = NULL;
+
+ AST_RWLIST_RDLOCK(&acf_root);
+ AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!strcmp(name, acf->name))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ return acf;
+}
+
+int ast_custom_function_unregister(struct ast_custom_function *acf)
+{
+ struct ast_custom_function *cur;
+
+ if (!acf)
+ return -1;
+
+ AST_RWLIST_WRLOCK(&acf_root);
+ if ((cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist)))
+ ast_verb(2, "Unregistered custom function %s\n", cur->name);
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ return cur ? 0 : -1;
+}
+
+int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
+{
+ struct ast_custom_function *cur;
+ char tmps[80];
+
+ if (!acf)
+ return -1;
+
+ acf->mod = mod;
+
+ AST_RWLIST_WRLOCK(&acf_root);
+
+ AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
+ if (!strcmp(acf->name, cur->name)) {
+ ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
+ AST_RWLIST_UNLOCK(&acf_root);
+ return -1;
+ }
+ }
+
+ /* Store in alphabetical order */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
+ if (strcasecmp(acf->name, cur->name) < 0) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ if (!cur)
+ AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
+
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ ast_verb(2, "Registered custom function '%s'\n", term_color(tmps, acf->name, COLOR_BRCYAN, 0, sizeof(tmps)));
+
+ return 0;
+}
+
+/*! \brief return a pointer to the arguments of the function,
+ * and terminates the function name with '\\0'
+ */
+static char *func_args(char *function)
+{
+ char *args = strchr(function, '(');
+
+ if (!args)
+ ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
+ else {
+ char *p;
+ *args++ = '\0';
+ if ((p = strrchr(args, ')')) )
+ *p = '\0';
+ else
+ ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
+ }
+ return args;
+}
+
+int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
+{
+ char *copy = ast_strdupa(function);
+ char *args = func_args(copy);
+ struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+
+ if (acfptr == NULL)
+ ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+ else if (!acfptr->read)
+ ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
+ else {
+ int res;
+ struct ast_module_user *u = NULL;
+ if (acfptr->mod)
+ u = __ast_module_user_add(acfptr->mod, chan);
+ res = acfptr->read(chan, copy, args, workspace, len);
+ if (acfptr->mod && u)
+ __ast_module_user_remove(acfptr->mod, u);
+ return res;
+ }
+ return -1;
+}
+
+int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+{
+ char *copy = ast_strdupa(function);
+ char *args = func_args(copy);
+ struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+
+ if (acfptr == NULL)
+ ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+ else if (!acfptr->write)
+ ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
+ else {
+ int res;
+ struct ast_module_user *u = NULL;
+ if (acfptr->mod)
+ u = __ast_module_user_add(acfptr->mod, chan);
+ res = acfptr->write(chan, copy, args, value);
+ if (acfptr->mod && u)
+ __ast_module_user_remove(acfptr->mod, u);
+ return res;
+ }
+
+ return -1;
+}
+
+static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
+ char *cp4;
+ const char *tmp, *whereweare;
+ int length, offset, offset2, isfunction;
+ char *workspace = NULL;
+ char *ltmp = NULL, *var = NULL;
+ char *nextvar, *nextexp, *nextthing;
+ char *vars, *vare;
+ int pos, brackets, needsub, len;
+
+ *cp2 = 0; /* just in case nothing ends up there */
+ whereweare=tmp=cp1;
+ while (!ast_strlen_zero(whereweare) && count) {
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch (nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ default:
+ pos = 1;
+ }
+ }
+
+ if (pos) {
+ /* Can't copy more than 'count' bytes */
+ if (pos > count)
+ pos = count;
+
+ /* Copy that many bytes */
+ memcpy(cp2, whereweare, pos);
+
+ count -= pos;
+ cp2 += pos;
+ whereweare += pos;
+ *cp2 = 0;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ } else if (vare[0] == '{') {
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ if (!workspace)
+ workspace = alloca(VAR_BUF_SIZE);
+
+ workspace[0] = '\0';
+
+ parse_variable_name(vars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ if (c || !headp)
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ else {
+ struct varshead old;
+ struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
+ if (c) {
+ memcpy(&old, &c->varshead, sizeof(old));
+ memcpy(&c->varshead, headp, sizeof(c->varshead));
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ /* Don't deallocate the varshead that was passed in */
+ memcpy(&c->varshead, &old, sizeof(c->varshead));
+ ast_channel_free(c);
+ } else
+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
+ }
+ ast_debug(1, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+ }
+ if (cp4) {
+ cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
+
+ length = strlen(cp4);
+ if (length > count)
+ length = count;
+ memcpy(cp2, cp4, length);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ length = ast_expr(vars, cp2, count, c);
+
+ if (length) {
+ ast_debug(1, "Expression result is '%s'\n", cp2);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ }
+ }
+}
+
+void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
+}
+
+void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
+}
+
+static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
+{
+ const char *tmp;
+
+ /* Nothing more to do */
+ if (!e->data)
+ return;
+
+ /* No variables or expressions in e->data, so why scan it? */
+ if ((!(tmp = strchr(e->data, '$'))) || (!strstr(tmp, "${") && !strstr(tmp, "$["))) {
+ ast_copy_string(passdata, e->data, datalen);
+ return;
+ }
+
+ pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
+}
+
+/*!
+ * \brief The return value depends on the action:
+ *
+ * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
+ * and return 0 on failure, -1 on match;
+ * E_FINDLABEL maps the label to a priority, and returns
+ * the priority on success, ... XXX
+ * E_SPAWN, spawn an application,
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ *
+ * \note The channel is auto-serviced in this function, because doing an extension
+ * match may block for a long time. For example, if the lookup has to use a network
+ * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
+ * auto-service code will queue up any important signalling frames to be processed
+ * after this is done.
+ */
+static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action, int *found, int combined_find_spawn)
+{
+ struct ast_exten *e;
+ struct ast_app *app;
+ int res;
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+ char passdata[EXT_DATA_SIZE];
+
+ int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
+
+ ast_rdlock_contexts();
+ if (found)
+ *found = 0;
+
+ e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
+ if (e) {
+ if (found)
+ *found = 1;
+ if (matching_action) {
+ ast_unlock_contexts();
+ return -1; /* success, we found it */
+ } else if (action == E_FINDLABEL) { /* map the label to a priority */
+ res = e->priority;
+ ast_unlock_contexts();
+ return res; /* the priority we were looking for */
+ } else { /* spawn */
+ if (!e->cached_app)
+ e->cached_app = pbx_findapp(e->app);
+ app = e->cached_app;
+ ast_unlock_contexts();
+ if (!app) {
+ ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
+ return -1;
+ }
+ if (c->context != context)
+ ast_copy_string(c->context, context, sizeof(c->context));
+ if (c->exten != exten)
+ ast_copy_string(c->exten, exten, sizeof(c->exten));
+ c->priority = priority;
+ pbx_substitute_variables(passdata, sizeof(passdata), c, e);
+ ast_debug(1, "Launching '%s'\n", app->name);
+ if (VERBOSITY_ATLEAST(3)) {
+ char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
+ ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n",
+ exten, context, priority,
+ term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
+ term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
+ term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
+ "in new stack");
+ }
+ manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
+ "Channel: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "Application: %s\r\n"
+ "AppData: %s\r\n"
+ "Uniqueid: %s\r\n",
+ c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
+ return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
+ }
+ } else if (q.swo) { /* not found here, but in another switch */
+ ast_unlock_contexts();
+ if (matching_action) {
+ return -1;
+ } else {
+ if (!q.swo->exec) {
+ ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
+ res = -1;
+ }
+ return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
+ }
+ } else { /* not found anywhere, see what happened */
+ ast_unlock_contexts();
+ switch (q.status) {
+ case STATUS_NO_CONTEXT:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
+ break;
+ case STATUS_NO_EXTENSION:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
+ break;
+ case STATUS_NO_PRIORITY:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
+ break;
+ case STATUS_NO_LABEL:
+ if (context && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
+ break;
+ default:
+ ast_debug(1, "Shouldn't happen!\n");
+ }
+
+ return (matching_action) ? 0 : -1;
+ }
+}
+
+/*! \brief Find hint for given extension in context */
+static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e;
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
+
+ ast_rdlock_contexts();
+ e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
+ ast_unlock_contexts();
+
+ return e;
+}
+
+/*! \brief Check state of extension by using hints */
+static int ast_extension_state2(struct ast_exten *e)
+{
+ char hint[AST_MAX_EXTENSION];
+ char *cur, *rest;
+ int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1;
+ int busy = 0, inuse = 0, ring = 0;
+
+ if (!e)
+ return -1;
+
+ ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
+
+ rest = hint; /* One or more devices separated with a & character */
+ while ( (cur = strsep(&rest, "&")) ) {
+ int res = ast_device_state(cur);
+ switch (res) {
+ case AST_DEVICE_NOT_INUSE:
+ allunavailable = 0;
+ allbusy = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_INUSE:
+ inuse = 1;
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_RINGING:
+ ring = 1;
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_RINGINUSE:
+ inuse = 1;
+ ring = 1;
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_ONHOLD:
+ allunavailable = 0;
+ allfree = 0;
+ break;
+ case AST_DEVICE_BUSY:
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ busy = 1;
+ break;
+ case AST_DEVICE_UNAVAILABLE:
+ case AST_DEVICE_INVALID:
+ allbusy = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ default:
+ allunavailable = 0;
+ allbusy = 0;
+ allfree = 0;
+ allonhold = 0;
+ }
+ }
+
+ if (!inuse && ring)
+ return AST_EXTENSION_RINGING;
+ if (inuse && ring)
+ return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
+ if (inuse)
+ return AST_EXTENSION_INUSE;
+ if (allfree)
+ return AST_EXTENSION_NOT_INUSE;
+ if (allonhold)
+ return AST_EXTENSION_ONHOLD;
+ if (allbusy)
+ return AST_EXTENSION_BUSY;
+ if (allunavailable)
+ return AST_EXTENSION_UNAVAILABLE;
+ if (busy)
+ return AST_EXTENSION_INUSE;
+
+ return AST_EXTENSION_NOT_INUSE;
+}
+
+/*! \brief Return extension_state as string */
+const char *ast_extension_state2str(int extension_state)
+{
+ int i;
+
+ for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
+ if (extension_states[i].extension_state == extension_state)
+ return extension_states[i].text;
+ }
+ return "Unknown";
+}
+
+/*! \brief Check extension state for an extension by using hint */
+int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e;
+
+ e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
+ if (!e)
+ return -1; /* No hint, return -1 */
+
+ return ast_extension_state2(e); /* Check all devices in the hint */
+}
+
+static void handle_statechange(const char *device)
+{
+ struct ast_hint *hint;
+
+ AST_RWLIST_RDLOCK(&hints);
+
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ struct ast_state_cb *cblist;
+ char buf[AST_MAX_EXTENSION];
+ char *parse = buf;
+ char *cur;
+ int state;
+
+ ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
+ while ( (cur = strsep(&parse, "&")) ) {
+ if (!strcasecmp(cur, device))
+ break;
+ }
+ if (!cur)
+ continue;
+
+ /* Get device state for this hint */
+ state = ast_extension_state2(hint->exten);
+
+ if ((state == -1) || (state == hint->laststate))
+ continue;
+
+ /* Device state changed since last check - notify the watchers */
+
+ /* For general callbacks */
+ for (cblist = statecbs; cblist; cblist = cblist->next)
+ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
+
+ /* For extension callbacks */
+ for (cblist = hint->callbacks; cblist; cblist = cblist->next)
+ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
+
+ hint->laststate = state; /* record we saw the change */
+ }
+
+ AST_RWLIST_UNLOCK(&hints);
+}
+
+static int statechange_queue(const char *dev)
+{
+ struct statechange *sc;
+
+ if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
+ return 0;
+
+ strcpy(sc->dev, dev);
+
+ ast_mutex_lock(&device_state.lock);
+ AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
+ ast_cond_signal(&device_state.cond);
+ ast_mutex_unlock(&device_state.lock);
+
+ return 0;
+}
+
+static void *device_state_thread(void *data)
+{
+ struct statechange *sc;
+
+ while (!device_state.stop) {
+ ast_mutex_lock(&device_state.lock);
+ while (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
+ ast_cond_wait(&device_state.cond, &device_state.lock);
+ /* Check to see if we were woken up to see the request to stop */
+ if (device_state.stop) {
+ ast_mutex_unlock(&device_state.lock);
+ return NULL;
+ }
+ }
+ ast_mutex_unlock(&device_state.lock);
+
+ handle_statechange(sc->dev);
+
+ ast_free(sc);
+ }
+
+ return NULL;
+}
+
+/*! \brief Add watcher for extension states */
+int ast_extension_state_add(const char *context, const char *exten,
+ ast_state_cb_type callback, void *data)
+{
+ struct ast_hint *hint;
+ struct ast_state_cb *cblist;
+ struct ast_exten *e;
+
+ /* If there's no context and extension: add callback to statecbs list */
+ if (!context && !exten) {
+ AST_RWLIST_WRLOCK(&hints);
+
+ for (cblist = statecbs; cblist; cblist = cblist->next) {
+ if (cblist->callback == callback) {
+ cblist->data = data;
+ AST_RWLIST_UNLOCK(&hints);
+ return 0;
+ }
+ }
+
+ /* Now insert the callback */
+ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+ cblist->id = 0;
+ cblist->callback = callback;
+ cblist->data = data;
+
+ cblist->next = statecbs;
+ statecbs = cblist;
+
+ AST_RWLIST_UNLOCK(&hints);
+ return 0;
+ }
+
+ if (!context || !exten)
+ return -1;
+
+ /* This callback type is for only one hint, so get the hint */
+ e = ast_hint_extension(NULL, context, exten);
+ if (!e) {
+ return -1;
+ }
+
+ /* Find the hint in the list of hints */
+ AST_RWLIST_WRLOCK(&hints);
+
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e)
+ break;
+ }
+
+ if (!hint) {
+ /* We have no hint, sorry */
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+
+ /* Now insert the callback in the callback list */
+ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+ cblist->id = stateid++; /* Unique ID for this callback */
+ cblist->callback = callback; /* Pointer to callback routine */
+ cblist->data = data; /* Data for the callback */
+
+ cblist->next = hint->callbacks;
+ hint->callbacks = cblist;
+
+ AST_RWLIST_UNLOCK(&hints);
+ return cblist->id;
+}
+
+/*! \brief Remove a watcher from the callback list */
+int ast_extension_state_del(int id, ast_state_cb_type callback)
+{
+ struct ast_state_cb **p_cur = NULL; /* address of pointer to us */
+ int ret = -1;
+
+ if (!id && !callback)
+ return -1;
+
+ AST_RWLIST_WRLOCK(&hints);
+
+ if (!id) { /* id == 0 is a callback without extension */
+ for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) {
+ if ((*p_cur)->callback == callback)
+ break;
+ }
+ } else { /* callback with extension, find the callback based on ID */
+ struct ast_hint *hint;
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) {
+ if ((*p_cur)->id == id)
+ break;
+ }
+ if (*p_cur) /* found in the inner loop */
+ break;
+ }
+ }
+ if (p_cur && *p_cur) {
+ struct ast_state_cb *cur = *p_cur;
+ *p_cur = cur->next;
+ ast_free(cur);
+ ret = 0;
+ }
+ AST_RWLIST_UNLOCK(&hints);
+ return ret;
+}
+
+/*! \brief Add hint to hint list, check initial extension state */
+static int ast_add_hint(struct ast_exten *e)
+{
+ struct ast_hint *hint;
+
+ if (!e)
+ return -1;
+
+ AST_RWLIST_WRLOCK(&hints);
+
+ /* Search if hint exists, do nothing */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e) {
+ AST_RWLIST_UNLOCK(&hints);
+ ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+ return -1;
+ }
+ }
+
+ ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+
+ if (!(hint = ast_calloc(1, sizeof(*hint)))) {
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+ /* Initialize and insert new item at the top */
+ hint->exten = e;
+ hint->laststate = ast_extension_state2(e);
+ AST_RWLIST_INSERT_HEAD(&hints, hint, list);
+
+ AST_RWLIST_UNLOCK(&hints);
+ return 0;
+}
+
+/*! \brief Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{
+ struct ast_hint *hint;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&hints);
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == oe) {
+ hint->exten = ne;
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&hints);
+
+ return res;
+}
+
+/*! \brief Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
+{
+ /* Cleanup the Notifys if hint is removed */
+ struct ast_hint *hint;
+ struct ast_state_cb *cblist, *cbprev;
+ int res = -1;
+
+ if (!e)
+ return -1;
+
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
+ if (hint->exten == e) {
+ cbprev = NULL;
+ cblist = hint->callbacks;
+ while (cblist) {
+ /* Notify with -1 and remove all callbacks */
+ cbprev = cblist;
+ cblist = cblist->next;
+ cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
+ ast_free(cbprev);
+ }
+ hint->callbacks = NULL;
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(hint);
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ return res;
+}
+
+
+/*! \brief Get hint for channel */
+int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e = ast_hint_extension(c, context, exten);
+
+ if (e) {
+ if (hint)
+ ast_copy_string(hint, ast_get_extension_app(e), hintsize);
+ if (name) {
+ const char *tmp = ast_get_extension_app_data(e);
+ if (tmp)
+ ast_copy_string(name, tmp, namesize);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
+}
+
+int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
+}
+
+int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
+}
+
+int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH, 0, 0);
+}
+
+int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE, 0, 0);
+}
+
+int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
+}
+
+/*! helper function to set extension and priority */
+static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
+{
+ ast_channel_lock(c);
+ ast_copy_string(c->exten, exten, sizeof(c->exten));
+ c->priority = pri;
+ ast_channel_unlock(c);
+}
+
+/*!
+ * \brief collect digits from the channel into the buffer.
+ * \retval 0 on timeout or done.
+ * \retval -1 on error.
+*/
+static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
+{
+ int digit;
+
+ buf[pos] = '\0'; /* make sure it is properly terminated */
+ while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
+ /* As long as we're willing to wait, and as long as it's not defined,
+ keep reading digits until we can't possibly get a right answer anymore. */
+ digit = ast_waitfordigit(c, waittime * 1000);
+ if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
+ c->_softhangup = 0;
+ } else {
+ if (!digit) /* No entry */
+ break;
+ if (digit < 0) /* Error, maybe a hangup */
+ return -1;
+ if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
+ buf[pos++] = digit;
+ buf[pos] = '\0';
+ }
+ waittime = c->pbx->dtimeout;
+ }
+ }
+ return 0;
+}
+
+static int __ast_pbx_run(struct ast_channel *c)
+{
+ int found = 0; /* set if we find at least one match */
+ int res = 0;
+ int autoloopflag;
+ int error = 0; /* set an error conditions */
+
+ /* A little initial setup here */
+ if (c->pbx) {
+ ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
+ /* XXX and now what ? */
+ ast_free(c->pbx);
+ }
+ if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
+ return -1;
+ if (c->amaflags) {
+ if (!c->cdr) {
+ c->cdr = ast_cdr_alloc();
+ if (!c->cdr) {
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ ast_free(c->pbx);
+ return -1;
+ }
+ ast_cdr_init(c->cdr, c);
+ }
+ }
+ /* Set reasonable defaults */
+ c->pbx->rtimeout = 10;
+ c->pbx->dtimeout = 5;
+
+ autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
+ ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
+
+ /* Start by trying whatever the channel is set to */
+ if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ /* If not successful fall back to 's' */
+ ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
+ /* XXX the original code used the existing priority in the call to
+ * ast_exists_extension(), and reset it to 1 afterwards.
+ * I believe the correct thing is to set it to 1 immediately.
+ */
+ set_ext_pri(c, "s", 1);
+ if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ /* JK02: And finally back to default if everything else failed */
+ ast_verb(2, "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
+ ast_copy_string(c->context, "default", sizeof(c->context));
+ }
+ }
+ if (c->cdr && ast_tvzero(c->cdr->start))
+ ast_cdr_start(c->cdr);
+ for (;;) {
+ char dst_exten[256]; /* buffer to accumulate digits */
+ int pos = 0; /* XXX should check bounds */
+ int digit = 0;
+
+ /* loop on priorities in this context/exten */
+ while ( !(res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num, &found,1))) {
+ if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "T", 1, c->cid.cid_num)) {
+ set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
+ /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
+ c->whentohangup = 0;
+ c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
+ } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "ABSOLUTETIMEOUT");
+ /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
+ c->whentohangup = 0;
+ c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
+ } else if (ast_check_hangup(c)) {
+ ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n",
+ c->exten, c->priority);
+ error = 1;
+ break;
+ }
+ c->priority++;
+ } /* end while - from here on we can use 'break' to go out */
+ if (found && res) {
+ /* Something bad happened, or a hangup has been requested. */
+ if (strchr("0123456789ABCDEF*#", res)) {
+ ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
+ pos = 0;
+ dst_exten[pos++] = digit = res;
+ dst_exten[pos] = '\0';
+ } else if (res == AST_PBX_KEEPALIVE) {
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+ error = 1;
+ } else {
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+
+ if ((res == AST_PBX_ERROR) && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ /* if we are already on the 'e' exten, don't jump to it again */
+ if (!strcmp(c->exten, "e")) {
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited ERROR while already on 'e' exten on '%s'\n", c->context, c->exten, c->priority, c->name);
+ error = 1;
+ } else {
+ pbx_builtin_raise_exception(c, "ERROR");
+ continue;
+ }
+ }
+
+ if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
+ c->_softhangup = 0;
+ } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
+ /* atimeout, nothing bad */
+ } else {
+ if (c->cdr)
+ ast_cdr_update(c);
+ error = 1;
+ break;
+ }
+ }
+ }
+ if (error)
+ break;
+
+ /*!\note
+ * We get here on a failure of some kind: non-existing extension or
+ * hangup. We have options, here. We can either catch the failure
+ * and continue, or we can drop out entirely. */
+
+ if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
+ /*!\note
+ * If there is no match at priority 1, it is not a valid extension anymore.
+ * Try to continue at "i" (for invalid) or "e" (for exception) or exit if
+ * neither exist.
+ */
+ if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
+ ast_verb(3, "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
+ pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
+ set_ext_pri(c, "i", 1);
+ } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "INVALID");
+ } else {
+ ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
+ c->name, c->exten, c->context);
+ error = 1; /* we know what to do with it */
+ break;
+ }
+ } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
+ /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
+ c->_softhangup = 0;
+ } else { /* keypress received, get more digits for a full extension */
+ int waittime = 0;
+ if (digit)
+ waittime = c->pbx->dtimeout;
+ else if (!autofallthrough)
+ waittime = c->pbx->rtimeout;
+ if (!waittime) {
+ const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
+ if (!status)
+ status = "UNKNOWN";
+ ast_verb(3, "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
+ if (!strcasecmp(status, "CONGESTION"))
+ res = pbx_builtin_congestion(c, "10");
+ else if (!strcasecmp(status, "CHANUNAVAIL"))
+ res = pbx_builtin_congestion(c, "10");
+ else if (!strcasecmp(status, "BUSY"))
+ res = pbx_builtin_busy(c, "10");
+ error = 1; /* XXX disable message */
+ break; /* exit from the 'for' loop */
+ }
+
+ if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
+ break;
+ if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
+ set_ext_pri(c, dst_exten, 1);
+ else {
+ /* No such extension */
+ if (!ast_strlen_zero(dst_exten)) {
+ /* An invalid extension */
+ if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
+ ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
+ pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
+ set_ext_pri(c, "i", 1);
+ } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "INVALID");
+ } else {
+ ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
+ found = 1; /* XXX disable message */
+ break;
+ }
+ } else {
+ /* A simple timeout */
+ if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
+ ast_verb(3, "Timeout on %s\n", c->name);
+ set_ext_pri(c, "t", 1);
+ } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "RESPONSETIMEOUT");
+ } else {
+ ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
+ found = 1; /* XXX disable message */
+ break;
+ }
+ }
+ }
+ if (c->cdr) {
+ ast_verb(2, "CDR updated on %s\n",c->name);
+ ast_cdr_update(c);
+ }
+ }
+ }
+ if (!found && !error)
+ ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
+ if (res != AST_PBX_KEEPALIVE)
+ ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
+ if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
+ if (c->cdr && ast_opt_end_cdr_before_h_exten)
+ ast_cdr_end(c->cdr);
+ set_ext_pri(c, "h", 1);
+ while ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num, &found, 1)) == 0) {
+ c->priority++;
+ }
+ if (found && res) {
+ /* Something bad happened, or a hangup has been requested. */
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ }
+ }
+ ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
+
+ pbx_destroy(c->pbx);
+ c->pbx = NULL;
+ if (res != AST_PBX_KEEPALIVE)
+ ast_hangup(c);
+ return 0;
+}
+
+/*!
+ * \brief Increase call count for channel
+ * \retval 0 on success
+ * \retval non-zero if a configured limit (maxcalls, maxload, minmemfree) was reached
+*/
+static int increase_call_count(const struct ast_channel *c)
+{
+ int failed = 0;
+ double curloadavg;
+#if defined(HAVE_SYSINFO)
+ long curfreemem;
+ struct sysinfo sys_info;
+#endif
+
+ ast_mutex_lock(&maxcalllock);
+ if (option_maxcalls) {
+ if (countcalls >= option_maxcalls) {
+ ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
+ failed = -1;
+ }
+ }
+ if (option_maxload) {
+ getloadavg(&curloadavg, 1);
+ if (curloadavg >= option_maxload) {
+ ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
+ failed = -1;
+ }
+ }
+#if defined(HAVE_SYSINFO)
+ if (option_minmemfree) {
+ if (!sysinfo(&sys_info)) {
+ /* make sure that the free system memory is above the configured low watermark
+ * convert the amount of freeram from mem_units to MB */
+ curfreemem = sys_info.freeram / sys_info.mem_unit;
+ curfreemem /= 1024*1024;
+ if (curfreemem < option_minmemfree) {
+ ast_log(LOG_WARNING, "Available system memory (~%ldMB) is below the configured low watermark (%ldMB)\n", curfreemem, option_minmemfree);
+ failed = -1;
+ }
+ }
+ }
+#endif
+
+ if (!failed) {
+ countcalls++;
+ totalcalls++;
+ }
+ ast_mutex_unlock(&maxcalllock);
+
+ return failed;
+}
+
+static void decrease_call_count(void)
+{
+ ast_mutex_lock(&maxcalllock);
+ if (countcalls > 0)
+ countcalls--;
+ ast_mutex_unlock(&maxcalllock);
+}
+
+static void destroy_exten(struct ast_exten *e)
+{
+ if (e->priority == PRIORITY_HINT)
+ ast_remove_hint(e);
+
+ if (e->peer_tree)
+ ast_hashtab_destroy(e->peer_tree,0);
+ if (e->peer_label_tree)
+ ast_hashtab_destroy(e->peer_label_tree, 0);
+ if (e->datad)
+ e->datad(e->data);
+ ast_free(e);
+}
+
+static void *pbx_thread(void *data)
+{
+ /* Oh joyeous kernel, we're a new thread, with nothing to do but
+ answer this channel and get it going.
+ */
+ /* NOTE:
+ The launcher of this function _MUST_ increment 'countcalls'
+ before invoking the function; it will be decremented when the
+ PBX has finished running on the channel
+ */
+ struct ast_channel *c = data;
+
+ __ast_pbx_run(c);
+ decrease_call_count();
+
+ pthread_exit(NULL);
+
+ return NULL;
+}
+
+enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
+{
+ pthread_t t;
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
+ return AST_PBX_FAILED;
+ }
+
+ if (increase_call_count(c))
+ return AST_PBX_CALL_LIMIT;
+
+ /* Start a new thread, and get something handling this channel. */
+ if (ast_pthread_create_detached(&t, NULL, pbx_thread, c)) {
+ ast_log(LOG_WARNING, "Failed to create new channel thread\n");
+ return AST_PBX_FAILED;
+ }
+
+ return AST_PBX_SUCCESS;
+}
+
+enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
+{
+ enum ast_pbx_result res = AST_PBX_SUCCESS;
+
+ if (increase_call_count(c))
+ return AST_PBX_CALL_LIMIT;
+
+ res = __ast_pbx_run(c);
+ decrease_call_count();
+
+ return res;
+}
+
+int ast_active_calls(void)
+{
+ return countcalls;
+}
+
+int ast_processed_calls(void)
+{
+ return totalcalls;
+}
+
+int pbx_set_autofallthrough(int newval)
+{
+ int oldval = autofallthrough;
+ autofallthrough = newval;
+ return oldval;
+}
+
+int pbx_set_extenpatternmatchnew(int newval)
+{
+ int oldval = extenpatternmatchnew;
+ extenpatternmatchnew = newval;
+ return oldval;
+}
+
+/*!
+ * \brief lookup for a context with a given name,
+ * \retval with conlock held if found.
+ * \retval NULL if not found.
+*/
+static struct ast_context *find_context_locked(const char *context)
+{
+ struct ast_context *c = NULL;
+ struct fake_context item;
+ strncpy(item.name, context, 256);
+ ast_rdlock_contexts();
+ c = ast_hashtab_lookup(contexts_tree,&item);
+
+#ifdef NOTNOW
+
+ while ( (c = ast_walk_contexts(c)) ) {
+ if (!strcmp(ast_get_context_name(c), context))
+ return c;
+ }
+#endif
+ if (!c)
+ ast_unlock_contexts();
+
+ return c;
+}
+
+/*!
+ * \brief Remove included contexts.
+ * This function locks contexts list by &conlist, search for the right context
+ * structure, leave context list locked and call ast_context_remove_include2
+ * which removes include, unlock contexts list and return ...
+*/
+int ast_context_remove_include(const char *context, const char *include, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ /* found, remove include from this context ... */
+ ret = ast_context_remove_include2(c, include, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*!
+ * \brief Locks context, remove included contexts, unlocks context.
+ * When we call this function, &conlock lock must be locked, because when
+ * we giving *con argument, some process can remove/change this context
+ * and after that there can be segfault.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
+{
+ struct ast_include *i, *pi = NULL;
+ int ret = -1;
+
+ ast_wrlock_context(con);
+
+ /* find our include */
+ for (i = con->includes; i; pi = i, i = i->next) {
+ if (!strcmp(i->name, include) &&
+ (!registrar || !strcmp(i->registrar, registrar))) {
+ /* remove from list */
+ if (pi)
+ pi->next = i->next;
+ else
+ con->includes = i->next;
+ /* free include and return */
+ ast_free(i);
+ ret = 0;
+ break;
+ }
+ }
+
+ ast_unlock_context(con);
+
+ return ret;
+}
+
+/*!
+ * \note This function locks contexts list by &conlist, search for the rigt context
+ * structure, leave context list locked and call ast_context_remove_switch2
+ * which removes switch, unlock contexts list and return ...
+ */
+int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
+{
+ int ret = -1; /* default error return */
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ /* remove switch from this context ... */
+ ret = ast_context_remove_switch2(c, sw, data, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*!
+ * \brief This function locks given context, removes switch, unlock context and
+ * return.
+ * \note When we call this function, &conlock lock must be locked, because when
+ * we giving *con argument, some process can remove/change this context
+ * and after that there can be segfault.
+ *
+ */
+int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
+{
+ struct ast_sw *i;
+ int ret = -1;
+
+ ast_wrlock_context(con);
+
+ /* walk switches */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
+ if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
+ (!registrar || !strcmp(i->registrar, registrar))) {
+ /* found, remove from list */
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(i); /* free switch and return */
+ ret = 0;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ ast_unlock_context(con);
+
+ return ret;
+}
+
+/*
+ * \note This functions lock contexts list, search for the right context,
+ * call ast_context_remove_extension2, unlock contexts list and return.
+ * In this function we are using
+ */
+int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
+{
+ int ret = -1; /* default error return */
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) { /* ... remove extension ... */
+ ret = ast_context_remove_extension2(c, extension, priority, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*!
+ * \brief This functionc locks given context, search for the right extension and
+ * fires out all peer in this extensions with given priority. If priority
+ * is set to 0, all peers are removed. After that, unlock context and
+ * return.
+ * \note When do you want to call this function, make sure that &conlock is locked,
+ * because some process can handle with your *con context before you lock
+ * it.
+ *
+ */
+int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
+{
+ struct ast_exten *exten, *prev_exten = NULL;
+ struct ast_exten *peer;
+ struct ast_exten ex, *exten2, *exten3;
+ char dummy_name[1024];
+
+ ast_wrlock_context(con);
+
+ /* Handle this is in the new world */
+
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Removing %s/%s/%d from trees, registrar=%s\n", con->name, extension, priority, registrar);
+#endif
+ /* find this particular extension */
+ ex.exten = dummy_name;
+ ex.matchcid = 0;
+ ast_copy_string(dummy_name,extension, sizeof(dummy_name));
+ exten = ast_hashtab_lookup(con->root_tree, &ex);
+ if (exten) {
+ if (priority == 0)
+ {
+ exten2 = ast_hashtab_remove_this_object(con->root_tree, exten);
+ if (!exten2)
+ ast_log(LOG_ERROR,"Trying to delete the exten %s from context %s, but could not remove from the root_tree\n", extension, con->name);
+ if (con->pattern_tree) {
+
+ struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
+
+ if (x->exten) { /* this test for safety purposes */
+ x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
+ x->exten = 0; /* get rid of what will become a bad pointer */
+ } else {
+ ast_log(LOG_WARNING,"Trying to delete an exten from a context, but the pattern tree node returned isn't a full extension\n");
+ }
+ }
+ } else {
+ ex.priority = priority;
+ exten2 = ast_hashtab_lookup(exten->peer_tree, &ex);
+ if (exten2) {
+
+ if (exten2->label) { /* if this exten has a label, remove that, too */
+ exten3 = ast_hashtab_remove_this_object(exten->peer_label_tree,exten2);
+ if (!exten3)
+ ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_tree of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten);
+ }
+
+ exten3 = ast_hashtab_remove_this_object(exten->peer_tree, exten2);
+ if (!exten3)
+ ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_tree of context %s, extension %s!\n", priority, con->name, exten2->exten);
+ if (ast_hashtab_size(exten->peer_tree) == 0) {
+ /* well, if the last priority of an exten is to be removed,
+ then, the extension is removed, too! */
+ exten3 = ast_hashtab_remove_this_object(con->root_tree, exten);
+ if (!exten3)
+ ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_tree (%s) (priority %d)\n", exten->exten, con->name, priority);
+ if (con->pattern_tree) {
+ struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
+ if (x->exten) { /* this test for safety purposes */
+ x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
+ x->exten = 0; /* get rid of what will become a bad pointer */
+ }
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR,"Could not find priority %d of exten %s in context %s!\n",
+ priority, exten->exten, con->name);
+ }
+ }
+ } else {
+ /* hmmm? this exten is not in this pattern tree? */
+ ast_log(LOG_WARNING,"Cannot find extension %s in root_tree in context %s\n",
+ extension, con->name);
+ }
+#ifdef NEED_DEBUG
+ if (con->pattern_tree) {
+ ast_log(LOG_NOTICE,"match char tree after exten removal:\n");
+ log_match_char_tree(con->pattern_tree, " ");
+ }
+#endif
+
+
+ /* scan the extension list to find matching extension-registrar */
+ for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
+ if (!strcmp(exten->exten, extension) &&
+ (!registrar || !strcmp(exten->registrar, registrar)))
+ break;
+ }
+ if (!exten) {
+ /* we can't find right extension */
+ ast_unlock_context(con);
+ return -1;
+ }
+
+ /* should we free all peers in this extension? (priority == 0)? */
+ if (priority == 0) {
+ /* remove this extension from context list */
+ if (prev_exten)
+ prev_exten->next = exten->next;
+ else
+ con->root = exten->next;
+
+ /* fire out all peers */
+ while ( (peer = exten) ) {
+ exten = peer->peer; /* prepare for next entry */
+ destroy_exten(peer);
+ }
+ } else {
+ /* scan the priority list to remove extension with exten->priority == priority */
+ struct ast_exten *previous_peer = NULL;
+
+ for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
+ if (peer->priority == priority &&
+ (!registrar || !strcmp(peer->registrar, registrar) ))
+ break; /* found our priority */
+ }
+ if (!peer) { /* not found */
+ ast_unlock_context(con);
+ return -1;
+ }
+ /* we are first priority extension? */
+ if (!previous_peer) {
+ /*
+ * We are first in the priority chain, so must update the extension chain.
+ * The next node is either the next priority or the next extension
+ */
+ struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
+ if (next_node && next_node == peer->peer) {
+ next_node->peer_tree = exten->peer_tree; /* move the priority hash tabs over */
+ exten->peer_tree = 0;
+ next_node->peer_label_tree = exten->peer_label_tree;
+ exten->peer_label_tree = 0;
+ }
+ if (!prev_exten) { /* change the root... */
+ con->root = next_node;
+ } else {
+ prev_exten->next = next_node; /* unlink */
+ }
+ if (peer->peer) { /* XXX update the new head of the pri list */
+ peer->peer->next = peer->next;
+ }
+
+ } else { /* easy, we are not first priority in extension */
+ previous_peer->peer = peer->peer;
+ }
+
+ /* now, free whole priority extension */
+ destroy_exten(peer);
+ /* XXX should we return -1 ? */
+ }
+ ast_unlock_context(con);
+ return 0;
+}
+
+
+/*!
+ * \note This function locks contexts list by &conlist, searches for the right context
+ * structure, and locks the macrolock mutex in that context.
+ * macrolock is used to limit a macro to be executed by one call at a time.
+ */
+int ast_context_lockmacro(const char *context)
+{
+ struct ast_context *c = NULL;
+ int ret = -1;
+ struct fake_context item;
+
+ ast_rdlock_contexts();
+
+ strncpy(item.name,context,256);
+ c = ast_hashtab_lookup(contexts_tree,&item);
+ if (c)
+ ret = 0;
+
+
+#ifdef NOTNOW
+
+ while ((c = ast_walk_contexts(c))) {
+ if (!strcmp(ast_get_context_name(c), context)) {
+ ret = 0;
+ break;
+ }
+ }
+
+#endif
+ ast_unlock_contexts();
+
+ /* if we found context, lock macrolock */
+ if (ret == 0)
+ ret = ast_mutex_lock(&c->macrolock);
+
+ return ret;
+}
+
+/*!
+ * \note This function locks contexts list by &conlist, searches for the right context
+ * structure, and unlocks the macrolock mutex in that context.
+ * macrolock is used to limit a macro to be executed by one call at a time.
+ */
+int ast_context_unlockmacro(const char *context)
+{
+ struct ast_context *c = NULL;
+ int ret = -1;
+ struct fake_context item;
+
+ ast_rdlock_contexts();
+
+ strncpy(item.name, context, 256);
+ c = ast_hashtab_lookup(contexts_tree,&item);
+ if (c)
+ ret = 0;
+#ifdef NOTNOW
+
+ while ((c = ast_walk_contexts(c))) {
+ if (!strcmp(ast_get_context_name(c), context)) {
+ ret = 0;
+ break;
+ }
+ }
+
+#endif
+ ast_unlock_contexts();
+
+ /* if we found context, unlock macrolock */
+ if (ret == 0)
+ ret = ast_mutex_unlock(&c->macrolock);
+
+ return ret;
+}
+
+/*! \brief Dynamically register a new dial plan application */
+int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description, void *mod)
+{
+ struct ast_app *tmp, *cur = NULL;
+ char tmps[80];
+ int length, res;
+
+ AST_RWLIST_WRLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, tmp, list) {
+ if (!(res = strcasecmp(app, tmp->name))) {
+ ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
+ AST_RWLIST_UNLOCK(&apps);
+ return -1;
+ } else if (res < 0)
+ break;
+ }
+
+ length = sizeof(*tmp) + strlen(app) + 1;
+
+ if (!(tmp = ast_calloc(1, length))) {
+ AST_RWLIST_UNLOCK(&apps);
+ return -1;
+ }
+
+ strcpy(tmp->name, app);
+ tmp->execute = execute;
+ tmp->synopsis = synopsis;
+ tmp->description = description;
+ tmp->module = mod;
+
+ /* Store in alphabetical order */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
+ if (strcasecmp(tmp->name, cur->name) < 0) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ if (!cur)
+ AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
+
+ ast_verb(2, "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
+
+ AST_RWLIST_UNLOCK(&apps);
+
+ return 0;
+}
+
+/*
+ * Append to the list. We don't have a tail pointer because we need
+ * to scan the list anyways to check for duplicates during insertion.
+ */
+int ast_register_switch(struct ast_switch *sw)
+{
+ struct ast_switch *tmp;
+
+ AST_RWLIST_WRLOCK(&switches);
+ AST_RWLIST_TRAVERSE(&switches, tmp, list) {
+ if (!strcasecmp(tmp->name, sw->name)) {
+ AST_RWLIST_UNLOCK(&switches);
+ ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
+ return -1;
+ }
+ }
+ AST_RWLIST_INSERT_TAIL(&switches, sw, list);
+ AST_RWLIST_UNLOCK(&switches);
+
+ return 0;
+}
+
+void ast_unregister_switch(struct ast_switch *sw)
+{
+ AST_RWLIST_WRLOCK(&switches);
+ AST_RWLIST_REMOVE(&switches, sw, list);
+ AST_RWLIST_UNLOCK(&switches);
+}
+
+/*
+ * Help for CLI commands ...
+ */
+
+/*
+ * \brief 'show application' CLI command implementation function...
+ */
+static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_app *aa;
+ int app, no_registered_app = 1;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show application";
+ e->usage =
+ "Usage: core show application <application> [<application> [<application> [...]]]\n"
+ " Describes a particular application.\n";
+ return NULL;
+ case CLI_GENERATE:
+ /*
+ * There is a possibility to show informations about more than one
+ * application at one time. You can type 'show application Dial Echo' and
+ * you will see informations about these two applications ...
+ */
+ wordlen = strlen(a->word);
+ /* return the n-th [partial] matching entry */
+ AST_RWLIST_RDLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, aa, list) {
+ if (!strncasecmp(a->word, aa->name, wordlen) && ++which > a->n) {
+ ret = ast_strdup(aa->name);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&apps);
+
+ return ret;
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ /* ... go through all applications ... */
+ AST_RWLIST_RDLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, aa, list) {
+ /* ... compare this application name with all arguments given
+ * to 'show application' command ... */
+ for (app = 3; app < a->argc; app++) {
+ if (!strcasecmp(aa->name, a->argv[app])) {
+ /* Maximum number of characters added by terminal coloring is 22 */
+ char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
+ char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
+ int synopsis_size, description_size;
+
+ no_registered_app = 0;
+
+ if (aa->synopsis)
+ synopsis_size = strlen(aa->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (aa->description)
+ description_size = strlen(aa->description) + 23;
+ else
+ description_size = strlen("Not available") + 23;
+ description = alloca(description_size);
+
+ if (synopsis && description) {
+ snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", aa->name);
+ term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
+ term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
+ term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
+ term_color(synopsis,
+ aa->synopsis ? aa->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ aa->description ? aa->description : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(a->fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
+ } else {
+ /* ... one of our applications, show info ...*/
+ ast_cli(a->fd,"\n -= Info about application '%s' =- \n\n"
+ "[Synopsis]\n %s\n\n"
+ "[Description]\n%s\n",
+ aa->name,
+ aa->synopsis ? aa->synopsis : "Not available",
+ aa->description ? aa->description : "Not available");
+ }
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&apps);
+
+ /* we found at least one app? no? */
+ if (no_registered_app) {
+ ast_cli(a->fd, "Your application(s) is (are) not registered\n");
+ return CLI_FAILURE;
+ }
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief handle_show_hints: CLI support for listing registered dial plan hints */
+static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_hint *hint;
+ int num = 0;
+ int watchers;
+ struct ast_state_cb *watcher;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hints";
+ e->usage =
+ "Usage: core show hints\n"
+ " List registered hints\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&hints);
+ if (AST_RWLIST_EMPTY(&hints)) {
+ ast_cli(a->fd, "There are no registered dialplan hints\n");
+ AST_RWLIST_UNLOCK(&hints);
+ return CLI_SUCCESS;
+ }
+ /* ... we have hints ... */
+ ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ watchers = 0;
+ for (watcher = hint->callbacks; watcher; watcher = watcher->next)
+ watchers++;
+ ast_cli(a->fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
+ ast_get_extension_name(hint->exten),
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ ast_get_extension_app(hint->exten),
+ ast_extension_state2str(hint->laststate), watchers);
+ num++;
+ }
+ ast_cli(a->fd, "----------------\n");
+ ast_cli(a->fd, "- %d hints registered\n", num);
+ AST_RWLIST_UNLOCK(&hints);
+ return CLI_SUCCESS;
+}
+
+/*! \brief autocomplete for CLI command 'core show hint' */
+static char *complete_core_show_hint(const char *line, const char *word, int pos, int state)
+{
+ struct ast_hint *hint;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ if (pos != 3)
+ return NULL;
+
+ wordlen = strlen(word);
+
+ AST_RWLIST_RDLOCK(&hints);
+ /* walk through all hints */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (!strncasecmp(word, ast_get_extension_name(hint->exten), wordlen) && ++which > state) {
+ ret = ast_strdup(ast_get_extension_name(hint->exten));
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&hints);
+
+ return ret;
+}
+
+/*! \brief handle_show_hint: CLI support for listing registered dial plan hint */
+static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_hint *hint;
+ int watchers;
+ int num = 0, extenlen;
+ struct ast_state_cb *watcher;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hint";
+ e->usage =
+ "Usage: core show hint <exten>\n"
+ " List registered hint\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_core_show_hint(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&hints);
+ if (AST_RWLIST_EMPTY(&hints)) {
+ ast_cli(a->fd, "There are no registered dialplan hints\n");
+ AST_RWLIST_UNLOCK(&hints);
+ return CLI_SUCCESS;
+ }
+ extenlen = strlen(a->argv[3]);
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) {
+ watchers = 0;
+ for (watcher = hint->callbacks; watcher; watcher = watcher->next)
+ watchers++;
+ ast_cli(a->fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
+ ast_get_extension_name(hint->exten),
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ ast_get_extension_app(hint->exten),
+ ast_extension_state2str(hint->laststate), watchers);
+ num++;
+ }
+ }
+ AST_RWLIST_UNLOCK(&hints);
+ if (!num)
+ ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]);
+ else
+ ast_cli(a->fd, "%d hint%s matching extension %s\n", num, (num!=1 ? "s":""), a->argv[3]);
+ return CLI_SUCCESS;
+}
+
+
+/*! \brief handle_show_switches: CLI support for listing registered dial plan switches */
+static char *handle_show_switches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_switch *sw;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show switches";
+ e->usage =
+ "Usage: core show switches\n"
+ " List registered switches\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&switches);
+
+ if (AST_RWLIST_EMPTY(&switches)) {
+ AST_RWLIST_UNLOCK(&switches);
+ ast_cli(a->fd, "There are no registered alternative switches\n");
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd, "\n -= Registered Asterisk Alternative Switches =-\n");
+ AST_RWLIST_TRAVERSE(&switches, sw, list)
+ ast_cli(a->fd, "%s: %s\n", sw->name, sw->description);
+
+ AST_RWLIST_UNLOCK(&switches);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_show_applications(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_app *aa;
+ int like = 0, describing = 0;
+ int total_match = 0; /* Number of matches in like clause */
+ int total_apps = 0; /* Number of apps registered */
+ static char* choices[] = { "like", "describing", NULL };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show applications [like|describing]";
+ e->usage =
+ "Usage: core show applications [{like|describing} <text>]\n"
+ " List applications which are currently available.\n"
+ " If 'like', <text> will be a substring of the app name\n"
+ " If 'describing', <text> will be a substring of the description\n";
+ return NULL;
+ case CLI_GENERATE:
+ return (a->pos != 3) ? NULL : ast_cli_complete(a->word, choices, a->n);
+ }
+
+ AST_RWLIST_RDLOCK(&apps);
+
+ if (AST_RWLIST_EMPTY(&apps)) {
+ ast_cli(a->fd, "There are no registered applications\n");
+ AST_RWLIST_UNLOCK(&apps);
+ return CLI_SUCCESS;
+ }
+
+ /* core list applications like <keyword> */
+ if ((a->argc == 5) && (!strcmp(a->argv[3], "like"))) {
+ like = 1;
+ } else if ((a->argc > 4) && (!strcmp(a->argv[3], "describing"))) {
+ describing = 1;
+ }
+
+ /* core list applications describing <keyword1> [<keyword2>] [...] */
+ if ((!like) && (!describing)) {
+ ast_cli(a->fd, " -= Registered Asterisk Applications =-\n");
+ } else {
+ ast_cli(a->fd, " -= Matching Asterisk Applications =-\n");
+ }
+
+ AST_RWLIST_TRAVERSE(&apps, aa, list) {
+ int printapp = 0;
+ total_apps++;
+ if (like) {
+ if (strcasestr(aa->name, a->argv[4])) {
+ printapp = 1;
+ total_match++;
+ }
+ } else if (describing) {
+ if (aa->description) {
+ /* Match all words on command line */
+ int i;
+ printapp = 1;
+ for (i = 4; i < a->argc; i++) {
+ if (!strcasestr(aa->description, a->argv[i])) {
+ printapp = 0;
+ } else {
+ total_match++;
+ }
+ }
+ }
+ } else {
+ printapp = 1;
+ }
+
+ if (printapp) {
+ ast_cli(a->fd," %20s: %s\n", aa->name, aa->synopsis ? aa->synopsis : "<Synopsis not available>");
+ }
+ }
+ if ((!like) && (!describing)) {
+ ast_cli(a->fd, " -= %d Applications Registered =-\n",total_apps);
+ } else {
+ ast_cli(a->fd, " -= %d Applications Matching =-\n",total_match);
+ }
+
+ AST_RWLIST_UNLOCK(&apps);
+
+ return CLI_SUCCESS;
+}
+
+/*
+ * 'show dialplan' CLI command implementation functions ...
+ */
+static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
+ int state)
+{
+ struct ast_context *c = NULL;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ /* we are do completion of [exten@]context on second position only */
+ if (pos != 2)
+ return NULL;
+
+ ast_rdlock_contexts();
+
+ wordlen = strlen(word);
+
+ /* walk through all contexts and return the n-th match */
+ while ( (c = ast_walk_contexts(c)) ) {
+ if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
+ ret = ast_strdup(ast_get_context_name(c));
+ break;
+ }
+ }
+
+ ast_unlock_contexts();
+
+ return ret;
+}
+
+/*! \brief Counters for the show dialplan manager command */
+struct dialplan_counters {
+ int total_items;
+ int total_context;
+ int total_exten;
+ int total_prio;
+ int context_existence;
+ int extension_existence;
+};
+
+/*! \brief helper function to print an extension */
+static void print_ext(struct ast_exten *e, char * buf, int buflen)
+{
+ int prio = ast_get_extension_priority(e);
+ if (prio == PRIORITY_HINT) {
+ snprintf(buf, buflen, "hint: %s",
+ ast_get_extension_app(e));
+ } else {
+ snprintf(buf, buflen, "%d. %s(%s)",
+ prio, ast_get_extension_app(e),
+ (!ast_strlen_zero(ast_get_extension_app_data(e)) ? (char *)ast_get_extension_app_data(e) : ""));
+ }
+}
+
+/* XXX not verified */
+static int show_dialplan_helper(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, const char *includes[])
+{
+ struct ast_context *c = NULL;
+ int res = 0, old_total_exten = dpc->total_exten;
+
+ ast_rdlock_contexts();
+
+ /* walk all contexts ... */
+ while ( (c = ast_walk_contexts(c)) ) {
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_ignorepat *ip;
+ char buf[256], buf2[256];
+ int context_info_printed = 0;
+
+ if (context && strcmp(ast_get_context_name(c), context))
+ continue; /* skip this one, name doesn't match */
+
+ dpc->context_existence = 1;
+
+ ast_rdlock_context(c);
+
+ /* are we looking for exten too? if yes, we print context
+ * only if we find our extension.
+ * Otherwise print context even if empty ?
+ * XXX i am not sure how the rinclude is handled.
+ * I think it ought to go inside.
+ */
+ if (!exten) {
+ dpc->total_context++;
+ ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
+ ast_get_context_name(c), ast_get_context_registrar(c));
+ context_info_printed = 1;
+ }
+
+ /* walk extensions ... */
+ e = NULL;
+ while ( (e = ast_walk_context_extensions(c, e)) ) {
+ struct ast_exten *p;
+
+ if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
+ continue; /* skip, extension match failed */
+
+ dpc->extension_existence = 1;
+
+ /* may we print context info? */
+ if (!context_info_printed) {
+ dpc->total_context++;
+ if (rinclude) { /* TODO Print more info about rinclude */
+ ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
+ ast_get_context_name(c), ast_get_context_registrar(c));
+ } else {
+ ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
+ ast_get_context_name(c), ast_get_context_registrar(c));
+ }
+ context_info_printed = 1;
+ }
+ dpc->total_prio++;
+
+ /* write extension name and first peer */
+ if (e->matchcid)
+ snprintf(buf, sizeof(buf), "'%s' (CID match '%s') => ", ast_get_extension_name(e), e->cidmatch);
+ else
+ snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
+
+ print_ext(e, buf2, sizeof(buf2));
+
+ ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
+ ast_get_extension_registrar(e));
+
+ dpc->total_exten++;
+ /* walk next extension peers */
+ p = e; /* skip the first one, we already got it */
+ while ( (p = ast_walk_extension_priorities(e, p)) ) {
+ const char *el = ast_get_extension_label(p);
+ dpc->total_prio++;
+ if (el)
+ snprintf(buf, sizeof(buf), " [%s]", el);
+ else
+ buf[0] = '\0';
+ print_ext(p, buf2, sizeof(buf2));
+
+ ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2,
+ ast_get_extension_registrar(p));
+ }
+ }
+
+ /* walk included and write info ... */
+ i = NULL;
+ while ( (i = ast_walk_context_includes(c, i)) ) {
+ snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
+ if (exten) {
+ /* Check all includes for the requested extension */
+ if (includecount >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
+ } else {
+ int dupe=0;
+ int x;
+ for (x=0;x<includecount;x++) {
+ if (!strcasecmp(includes[x], ast_get_include_name(i))) {
+ dupe++;
+ break;
+ }
+ }
+ if (!dupe) {
+ includes[includecount] = ast_get_include_name(i);
+ show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
+ } else {
+ ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
+ }
+ }
+ } else {
+ ast_cli(fd, " Include => %-45s [%s]\n",
+ buf, ast_get_include_registrar(i));
+ }
+ }
+
+ /* walk ignore patterns and write info ... */
+ ip = NULL;
+ while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
+ const char *ipname = ast_get_ignorepat_name(ip);
+ char ignorepat[AST_MAX_EXTENSION];
+ snprintf(buf, sizeof(buf), "'%s'", ipname);
+ snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
+ if (!exten || ast_extension_match(ignorepat, exten)) {
+ ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
+ buf, ast_get_ignorepat_registrar(ip));
+ }
+ }
+ if (!rinclude) {
+ struct ast_sw *sw = NULL;
+ while ( (sw = ast_walk_context_switches(c, sw)) ) {
+ snprintf(buf, sizeof(buf), "'%s/%s'",
+ ast_get_switch_name(sw),
+ ast_get_switch_data(sw));
+ ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
+ buf, ast_get_switch_registrar(sw));
+ }
+ }
+
+ if (option_debug && c->pattern_tree)
+ {
+ ast_cli(fd,"\r\n In-mem exten Trie for Fast Extension Pattern Matching:\r\n\r\n");
+
+ ast_cli(fd,"\r\n Explanation: Node Contents Format = <char(s) to match>:<pattern?>:<specif>:[matched extension]\r\n");
+ ast_cli(fd, " Where <char(s) to match> is a set of chars, any one of which should match the current character\r\n");
+ ast_cli(fd, " <pattern?>: Y if this a pattern match (eg. _XZN[5-7]), N otherwise\r\n");
+ ast_cli(fd, " <specif>: an assigned 'exactness' number for this matching char. The lower the number, the more exact the match\r\n");
+ ast_cli(fd, " [matched exten]: If all chars matched to this point, which extension this matches. In form: EXTEN:<exten string> \r\n");
+ ast_cli(fd, " In general, you match a trie node to a string character, from left to right. All possible matching chars\r\n");
+ ast_cli(fd, " are in a string vertically, separated by an unbroken string of '+' characters.\r\n\r\n");
+ cli_match_char_tree(c->pattern_tree, " ", fd);
+ }
+
+ ast_unlock_context(c);
+
+ /* if we print something in context, make an empty line */
+ if (context_info_printed)
+ ast_cli(fd, "\r\n");
+ }
+ ast_unlock_contexts();
+
+ return (dpc->total_exten == old_total_exten) ? -1 : res;
+}
+
+static char *handle_show_dialplan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *exten = NULL, *context = NULL;
+ /* Variables used for different counters */
+ struct dialplan_counters counters;
+ const char *incstack[AST_PBX_MAX_STACK];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan show";
+ e->usage =
+ "Usage: dialplan show [exten@][context]\n"
+ " Show dialplan\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_show_dialplan_context(a->line, a->word, a->pos, a->n);
+ }
+
+ memset(&counters, 0, sizeof(counters));
+
+ if (a->argc != 2 && a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ /* we obtain [exten@]context? if yes, split them ... */
+ if (a->argc == 3) {
+ if (strchr(a->argv[2], '@')) { /* split into exten & context */
+ context = ast_strdupa(a->argv[2]);
+ exten = strsep(&context, "@");
+ /* change empty strings to NULL */
+ if (ast_strlen_zero(exten))
+ exten = NULL;
+ } else { /* no '@' char, only context given */
+ context = a->argv[2];
+ }
+ if (ast_strlen_zero(context))
+ context = NULL;
+ }
+ /* else Show complete dial plan, context and exten are NULL */
+ show_dialplan_helper(a->fd, context, exten, &counters, NULL, 0, incstack);
+
+ /* check for input failure and throw some error messages */
+ if (context && !counters.context_existence) {
+ ast_cli(a->fd, "There is no existence of '%s' context\n", context);
+ return CLI_FAILURE;
+ }
+
+ if (exten && !counters.extension_existence) {
+ if (context)
+ ast_cli(a->fd, "There is no existence of %s@%s extension\n",
+ exten, context);
+ else
+ ast_cli(a->fd,
+ "There is no existence of '%s' extension in all contexts\n",
+ exten);
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd,"-= %d %s (%d %s) in %d %s. =-\n",
+ counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
+ counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
+ counters.total_context, counters.total_context == 1 ? "context" : "contexts");
+
+ /* everything ok */
+ return CLI_SUCCESS;
+}
+
+/*! \brief Send ack once */
+static void manager_dpsendack(struct mansession *s, const struct message *m)
+{
+ astman_send_listack(s, m, "DialPlan list will follow", "start");
+}
+
+/*! \brief Show dialplan extensions
+ * XXX this function is similar but not exactly the same as the CLI's
+ * show dialplan. Must check whether the difference is intentional or not.
+ */
+static int manager_show_dialplan_helper(struct mansession *s, const struct message *m,
+ const char *actionidtext, const char *context,
+ const char *exten, struct dialplan_counters *dpc,
+ struct ast_include *rinclude)
+{
+ struct ast_context *c;
+ int res=0, old_total_exten = dpc->total_exten;
+
+ if (ast_strlen_zero(exten))
+ exten = NULL;
+ if (ast_strlen_zero(context))
+ context = NULL;
+
+ ast_debug(3, "manager_show_dialplan: Context: -%s- Extension: -%s-\n", context, exten);
+
+ /* try to lock contexts */
+ if (ast_rdlock_contexts()) {
+ astman_send_error(s, m, "Failed to lock contexts\r\n");
+ ast_log(LOG_WARNING, "Failed to lock contexts list for manager: listdialplan\n");
+ return -1;
+ }
+
+ c = NULL; /* walk all contexts ... */
+ while ( (c = ast_walk_contexts(c)) ) {
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_ignorepat *ip;
+
+ if (context && strcmp(ast_get_context_name(c), context) != 0)
+ continue; /* not the name we want */
+
+ dpc->context_existence = 1;
+
+ ast_debug(3, "manager_show_dialplan: Found Context: %s \n", ast_get_context_name(c));
+
+ if (ast_rdlock_context(c)) { /* failed to lock */
+ ast_debug(3, "manager_show_dialplan: Failed to lock context\n");
+ continue;
+ }
+
+ /* XXX note- an empty context is not printed */
+ e = NULL; /* walk extensions in context */
+ while ( (e = ast_walk_context_extensions(c, e)) ) {
+ struct ast_exten *p;
+
+ /* looking for extension? is this our extension? */
+ if (exten && !ast_extension_match(ast_get_extension_name(e), exten)) {
+ /* not the one we are looking for, continue */
+ ast_debug(3, "manager_show_dialplan: Skipping extension %s\n", ast_get_extension_name(e));
+ continue;
+ }
+ ast_debug(3, "manager_show_dialplan: Found Extension: %s \n", ast_get_extension_name(e));
+
+ dpc->extension_existence = 1;
+
+ /* may we print context info? */
+ dpc->total_context++;
+ dpc->total_exten++;
+
+ p = NULL; /* walk next extension peers */
+ while ( (p = ast_walk_extension_priorities(e, p)) ) {
+ int prio = ast_get_extension_priority(p);
+
+ dpc->total_prio++;
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nExtension: %s\r\n", ast_get_context_name(c), ast_get_extension_name(e) );
+
+ /* XXX maybe make this conditional, if p != e ? */
+ if (ast_get_extension_label(p))
+ astman_append(s, "ExtensionLabel: %s\r\n", ast_get_extension_label(p));
+
+ if (prio == PRIORITY_HINT) {
+ astman_append(s, "Priority: hint\r\nApplication: %s\r\n", ast_get_extension_app(p));
+ } else {
+ astman_append(s, "Priority: %d\r\nApplication: %s\r\nAppData: %s\r\n", prio, ast_get_extension_app(p), (char *) ast_get_extension_app_data(p));
+ }
+ astman_append(s, "Registrar: %s\r\n\r\n", ast_get_extension_registrar(e));
+ }
+ }
+
+ i = NULL; /* walk included and write info ... */
+ while ( (i = ast_walk_context_includes(c, i)) ) {
+ if (exten) {
+ /* Check all includes for the requested extension */
+ manager_show_dialplan_helper(s, m, actionidtext, ast_get_include_name(i), exten, dpc, i);
+ } else {
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nIncludeContext: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_include_name(i), ast_get_include_registrar(i));
+ astman_append(s, "\r\n");
+ ast_debug(3, "manager_show_dialplan: Found Included context: %s \n", ast_get_include_name(i));
+ }
+ }
+
+ ip = NULL; /* walk ignore patterns and write info ... */
+ while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
+ const char *ipname = ast_get_ignorepat_name(ip);
+ char ignorepat[AST_MAX_EXTENSION];
+
+ snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
+ if (!exten || ast_extension_match(ignorepat, exten)) {
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nIgnorePattern: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ipname, ast_get_ignorepat_registrar(ip));
+ astman_append(s, "\r\n");
+ }
+ }
+ if (!rinclude) {
+ struct ast_sw *sw = NULL;
+ while ( (sw = ast_walk_context_switches(c, sw)) ) {
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nSwitch: %s/%s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_switch_name(sw), ast_get_switch_data(sw), ast_get_switch_registrar(sw));
+ astman_append(s, "\r\n");
+ ast_debug(3, "manager_show_dialplan: Found Switch : %s \n", ast_get_switch_name(sw));
+ }
+ }
+
+ ast_unlock_context(c);
+ }
+ ast_unlock_contexts();
+
+ if (dpc->total_exten == old_total_exten) {
+ ast_debug(3, "manager_show_dialplan: Found nothing new\n");
+ /* Nothing new under the sun */
+ return -1;
+ } else {
+ return res;
+ }
+}
+
+/*! \brief Manager listing of dial plan */
+static int manager_show_dialplan(struct mansession *s, const struct message *m)
+{
+ const char *exten, *context;
+ const char *id = astman_get_header(m, "ActionID");
+ char idtext[256];
+ int res;
+
+ /* Variables used for different counters */
+ struct dialplan_counters counters;
+
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+ else
+ idtext[0] = '\0';
+
+ memset(&counters, 0, sizeof(counters));
+
+ exten = astman_get_header(m, "Extension");
+ context = astman_get_header(m, "Context");
+
+ res = manager_show_dialplan_helper(s, m, idtext, context, exten, &counters, NULL);
+
+ if (context && !counters.context_existence) {
+ char errorbuf[BUFSIZ];
+
+ snprintf(errorbuf, sizeof(errorbuf), "Did not find context %s\r\n", context);
+ astman_send_error(s, m, errorbuf);
+ return 0;
+ }
+ if (exten && !counters.extension_existence) {
+ char errorbuf[BUFSIZ];
+
+ if (context)
+ snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s@%s\r\n", exten, context);
+ else
+ snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s in any context\r\n", exten);
+ astman_send_error(s, m, errorbuf);
+ return 0;
+ }
+
+ manager_event(EVENT_FLAG_CONFIG, "ShowDialPlanComplete",
+ "EventList: Complete\r\n"
+ "ListItems: %d\r\n"
+ "ListExtensions: %d\r\n"
+ "ListPriorities: %d\r\n"
+ "ListContexts: %d\r\n"
+ "%s"
+ "\r\n", counters.total_items, counters.total_exten, counters.total_prio, counters.total_context, idtext);
+
+ /* everything ok */
+ return 0;
+}
+
+static char mandescr_show_dialplan[] =
+"Description: Show dialplan contexts and extensions.\n"
+"Be aware that showing the full dialplan may take a lot of capacity\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this AMI transaction (optional)\n"
+" Extension: <extension> Extension (Optional)\n"
+" Context: <context> Context (Optional)\n"
+"\n";
+
+
+/*! \brief CLI support for listing global variables in a parseable way */
+static char *handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i = 0;
+ struct ast_var_t *newvariable;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show globals";
+ e->usage =
+ "Usage: core show globals\n"
+ " List current global dialplan variables and their values\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE (&globals, newvariable, entries) {
+ i++;
+ ast_cli(a->fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
+ }
+ ast_rwlock_unlock(&globalslock);
+ ast_cli(a->fd, "\n -- %d variables\n", i);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set global";
+ e->usage =
+ "Usage: core set global <name> <value>\n"
+ " Set global dialplan variable <name> to <value>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args + 2)
+ return CLI_SHOWUSAGE;
+
+ pbx_builtin_setvar_helper(NULL, a->argv[3], a->argv[4]);
+ ast_cli(a->fd, "\n -- Global variable %s set to %s\n", a->argv[3], a->argv[4]);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+ const char *chan_name, *var_name, *var_value;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set chanvar";
+ e->usage =
+ "Usage: core set chanvar <channel> <varname> <value>\n"
+ " Set channel variable <varname> to <value>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+ }
+
+ if (a->argc != e->args + 3)
+ return CLI_SHOWUSAGE;
+
+ chan_name = a->argv[e->args];
+ var_name = a->argv[e->args + 1];
+ var_value = a->argv[e->args + 2];
+
+ if (!(chan = ast_get_channel_by_name_locked(chan_name))) {
+ ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
+ return CLI_FAILURE;
+ }
+
+ pbx_builtin_setvar_helper(chan, var_name, var_value);
+
+ ast_channel_unlock(chan);
+
+ ast_cli(a->fd, "\n -- Channel variable '%s' set to '%s' for '%s'\n",
+ var_name, var_value, chan_name);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set extenpatternmatchnew true";
+ e->usage =
+ "Usage: dialplan set extenpatternmatchnew true|false\n"
+ " Use the NEW extension pattern matching algorithm, true or false.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ oldval = pbx_set_extenpatternmatchnew(1);
+
+ if (oldval)
+ ast_cli(a->fd, "\n -- Still using the NEW pattern match algorithm for extension names in the dialplan.\n");
+ else
+ ast_cli(a->fd, "\n -- Switched to using the NEW pattern match algorithm for extension names in the dialplan.\n");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_unset_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set extenpatternmatchnew false";
+ e->usage =
+ "Usage: dialplan set extenpatternmatchnew true|false\n"
+ " Use the NEW extension pattern matching algorithm, true or false.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ oldval = pbx_set_extenpatternmatchnew(0);
+
+ if (!oldval)
+ ast_cli(a->fd, "\n -- Still using the OLD pattern match algorithm for extension names in the dialplan.\n");
+ else
+ ast_cli(a->fd, "\n -- Switched to using the OLD pattern match algorithm for extension names in the dialplan.\n");
+
+ return CLI_SUCCESS;
+}
+
+/*
+ * CLI entries for upper commands ...
+ */
+static struct ast_cli_entry pbx_cli[] = {
+ AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),
+ AST_CLI_DEFINE(handle_show_functions, "Shows registered dialplan functions"),
+ AST_CLI_DEFINE(handle_show_switches, "Show alternative switches"),
+ AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
+ AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
+ AST_CLI_DEFINE(handle_show_globals, "Show global dialplan variables"),
+ AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
+ AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
+ AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable"),
+ AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"),
+ AST_CLI_DEFINE(handle_show_dialplan, "Show dialplan"),
+ AST_CLI_DEFINE(handle_unset_extenpatternmatchnew, "Use the Old extension pattern matching algorithm."),
+ AST_CLI_DEFINE(handle_set_extenpatternmatchnew, "Use the New extension pattern matching algorithm."),
+};
+
+static void unreference_cached_app(struct ast_app *app)
+{
+ struct ast_context *context = NULL;
+ struct ast_exten *eroot = NULL, *e = NULL;
+
+ ast_rdlock_contexts();
+ while ((context = ast_walk_contexts(context))) {
+ while ((eroot = ast_walk_context_extensions(context, eroot))) {
+ while ((e = ast_walk_extension_priorities(eroot, e))) {
+ if (e->cached_app == app)
+ e->cached_app = NULL;
+ }
+ }
+ }
+ ast_unlock_contexts();
+
+ return;
+}
+
+int ast_unregister_application(const char *app)
+{
+ struct ast_app *tmp;
+
+ AST_RWLIST_WRLOCK(&apps);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
+ if (!strcasecmp(app, tmp->name)) {
+ unreference_cached_app(tmp);
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(2, "Unregistered application '%s'\n", tmp->name);
+ ast_free(tmp);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&apps);
+
+ return tmp ? 0 : -1;
+}
+
+static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
+{
+ struct ast_context *tmp, **local_contexts;
+ struct fake_context search;
+ int length = sizeof(struct ast_context) + strlen(name) + 1;
+
+ if (!contexts_tree) {
+ contexts_tree = ast_hashtab_create(17,
+ hashtab_compare_contexts,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_contexts,
+ 0);
+ }
+
+ if (!extcontexts) {
+ ast_rdlock_contexts();
+ local_contexts = &contexts;
+ strncpy(search.name,name,sizeof(search.name));
+ tmp = ast_hashtab_lookup(contexts_tree, &search);
+ if (!existsokay && tmp) {
+ ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+ }
+ ast_unlock_contexts();
+ if (tmp)
+ return tmp;
+ } else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */
+ local_contexts = extcontexts;
+ for (tmp = *local_contexts; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, name)) {
+ if (!existsokay) {
+ ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+ tmp = NULL;
+ }
+ return tmp;
+ }
+ }
+ }
+
+ if ((tmp = ast_calloc(1, length))) {
+ ast_rwlock_init(&tmp->lock);
+ ast_mutex_init(&tmp->macrolock);
+ strcpy(tmp->name, name);
+ tmp->root = NULL;
+ tmp->root_tree = NULL;
+ tmp->registrar = registrar;
+ tmp->includes = NULL;
+ tmp->ignorepats = NULL;
+ }
+ if (!extcontexts) {
+ ast_wrlock_contexts();
+ tmp->next = *local_contexts;
+ *local_contexts = tmp;
+ ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */
+ ast_unlock_contexts();
+ } else {
+ tmp->next = *local_contexts;
+ *local_contexts = tmp;
+ }
+ ast_debug(1, "Registered context '%s'\n", tmp->name);
+ ast_verb(3, "Registered extension context '%s'\n", tmp->name);
+ return tmp;
+}
+
+struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 0);
+}
+
+struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 1);
+}
+void __ast_context_destroy(struct ast_context *con, const char *registrar);
+
+struct store_hint {
+ char *context;
+ char *exten;
+ struct ast_state_cb *callbacks;
+ int laststate;
+ AST_LIST_ENTRY(store_hint) list;
+ char data[1];
+};
+
+AST_LIST_HEAD(store_hints, store_hint);
+
+/* XXX this does not check that multiple contexts are merged */
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ struct ast_context *tmp, *lasttmp = NULL;
+ struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
+ struct store_hint *this;
+ struct ast_hint *hint;
+ struct ast_exten *exten;
+ int length;
+ struct ast_state_cb *thiscb, *prevcb;
+
+ /* it is very important that this function hold the hint list lock _and_ the conlock
+ during its operation; not only do we need to ensure that the list of contexts
+ and extensions does not change, but also that no hint callbacks (watchers) are
+ added or removed during the merge/delete process
+
+ in addition, the locks _must_ be taken in this order, because there are already
+ other code paths that use this order
+ */
+ ast_wrlock_contexts();
+ AST_RWLIST_WRLOCK(&hints);
+
+ /* preserve all watchers for hints associated with this registrar */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
+ length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
+ if (!(this = ast_calloc(1, length)))
+ continue;
+ this->callbacks = hint->callbacks;
+ hint->callbacks = NULL;
+ this->laststate = hint->laststate;
+ this->context = this->data;
+ strcpy(this->data, hint->exten->parent->name);
+ this->exten = this->data + strlen(this->context) + 1;
+ strcpy(this->exten, hint->exten->exten);
+ AST_LIST_INSERT_HEAD(&store, this, list);
+ }
+ }
+
+ tmp = *extcontexts;
+ if (registrar) {
+ /* XXX remove previous contexts from same registrar */
+ ast_debug(1, "must remove any reg %s\n", registrar);
+ __ast_context_destroy(NULL,registrar);
+ while (tmp) {
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ } else {
+ /* XXX remove contexts with the same name */
+ while (tmp) {
+ ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
+ __ast_context_destroy(tmp,tmp->registrar);
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ }
+ tmp = *extcontexts;
+ while (tmp) {
+ ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */
+ tmp = tmp->next;
+ }
+ if (lasttmp) {
+ lasttmp->next = contexts;
+ contexts = *extcontexts;
+ *extcontexts = NULL;
+ } else
+ ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
+
+ /* restore the watchers for hints that can be found; notify those that
+ cannot be restored
+ */
+ while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
+ struct pbx_find_info q = { .stacklen = 0 };
+ exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
+ /* Find the hint in the list of hints */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == exten)
+ break;
+ }
+ if (!exten || !hint) {
+ /* this hint has been removed, notify the watchers */
+ prevcb = NULL;
+ thiscb = this->callbacks;
+ while (thiscb) {
+ prevcb = thiscb;
+ thiscb = thiscb->next;
+ prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
+ ast_free(prevcb);
+ }
+ } else {
+ thiscb = this->callbacks;
+ while (thiscb->next)
+ thiscb = thiscb->next;
+ thiscb->next = hint->callbacks;
+ hint->callbacks = this->callbacks;
+ hint->laststate = this->laststate;
+ }
+ ast_free(this);
+ }
+
+ AST_RWLIST_UNLOCK(&hints);
+ ast_unlock_contexts();
+
+ return;
+}
+
+/*
+ * errno values
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ */
+int ast_context_add_include(const char *context, const char *include, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_context_add_include2(c, include, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*! \brief Helper for get_range.
+ * return the index of the matching entry, starting from 1.
+ * If names is not supplied, try numeric values.
+ */
+static int lookup_name(const char *s, char *const names[], int max)
+{
+ int i;
+
+ if (names) {
+ for (i = 0; names[i]; i++) {
+ if (!strcasecmp(s, names[i]))
+ return i+1;
+ }
+ } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
+ return i;
+ }
+ return 0; /* error return */
+}
+
+/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
+ * names, if supplied, is an array of names that should be mapped to numbers.
+ */
+static unsigned get_range(char *src, int max, char *const names[], const char *msg)
+{
+ int s, e; /* start and ending position */
+ unsigned int mask = 0;
+
+ /* Check for whole range */
+ if (ast_strlen_zero(src) || !strcmp(src, "*")) {
+ s = 0;
+ e = max - 1;
+ } else {
+ /* Get start and ending position */
+ char *c = strchr(src, '-');
+ if (c)
+ *c++ = '\0';
+ /* Find the start */
+ s = lookup_name(src, names, max);
+ if (!s) {
+ ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
+ return 0;
+ }
+ s--;
+ if (c) { /* find end of range */
+ e = lookup_name(c, names, max);
+ if (!e) {
+ ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
+ return 0;
+ }
+ e--;
+ } else
+ e = s;
+ }
+ /* Fill the mask. Remember that ranges are cyclic */
+ mask = 1 << e; /* initialize with last element */
+ while (s != e) {
+ if (s >= max) {
+ s = 0;
+ mask |= (1 << s);
+ } else {
+ mask |= (1 << s);
+ s++;
+ }
+ }
+ return mask;
+}
+
+/*! \brief store a bitmask of valid times, one bit each 2 minute */
+static void get_timerange(struct ast_timing *i, char *times)
+{
+ char *e;
+ int x;
+ int s1, s2;
+ int e1, e2;
+ /* int cth, ctm; */
+
+ /* start disabling all times, fill the fields with 0's, as they may contain garbage */
+ memset(i->minmask, 0, sizeof(i->minmask));
+
+ /* 2-minutes per bit, since the mask has only 32 bits :( */
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ for (x=0; x<24; x++)
+ i->minmask[x] = 0x3fffffff; /* 30 bits */
+ return;
+ }
+ /* Otherwise expect a range */
+ e = strchr(times, '-');
+ if (!e) {
+ ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
+ return;
+ }
+ *e++ = '\0';
+ /* XXX why skip non digits ? */
+ while (*e && !isdigit(*e))
+ e++;
+ if (!*e) {
+ ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
+ return;
+ }
+ if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
+ return;
+ }
+ if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
+ return;
+ }
+ /* XXX this needs to be optimized */
+#if 1
+ s1 = s1 * 30 + s2/2;
+ if ((s1 < 0) || (s1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
+ return;
+ }
+ e1 = e1 * 30 + e2/2;
+ if ((e1 < 0) || (e1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
+ return;
+ }
+ /* Go through the time and enable each appropriate bit */
+ for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
+ i->minmask[x/30] |= (1 << (x % 30));
+ }
+ /* Do the last one */
+ i->minmask[x/30] |= (1 << (x % 30));
+#else
+ for (cth=0; cth<24; cth++) {
+ /* Initialize masks to blank */
+ i->minmask[cth] = 0;
+ for (ctm=0; ctm<30; ctm++) {
+ if (
+ /* First hour with more than one hour */
+ (((cth == s1) && (ctm >= s2)) &&
+ ((cth < e1)))
+ /* Only one hour */
+ || (((cth == s1) && (ctm >= s2)) &&
+ ((cth == e1) && (ctm <= e2)))
+ /* In between first and last hours (more than 2 hours) */
+ || ((cth > s1) &&
+ (cth < e1))
+ /* Last hour with more than one hour */
+ || ((cth > s1) &&
+ ((cth == e1) && (ctm <= e2)))
+ )
+ i->minmask[cth] |= (1 << (ctm / 2));
+ }
+ }
+#endif
+ /* All done */
+ return;
+}
+
+static char *days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+ NULL,
+};
+
+static char *months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+ NULL,
+};
+
+int ast_build_timing(struct ast_timing *i, const char *info_in)
+{
+ char info_save[256];
+ char *info;
+
+ /* Check for empty just in case */
+ if (ast_strlen_zero(info_in))
+ return 0;
+ /* make a copy just in case we were passed a static string */
+ ast_copy_string(info_save, info_in, sizeof(info_save));
+ info = info_save;
+ /* Assume everything except time */
+ i->monthmask = 0xfff; /* 12 bits */
+ i->daymask = 0x7fffffffU; /* 31 bits */
+ i->dowmask = 0x7f; /* 7 bits */
+ /* on each call, use strsep() to move info to the next argument */
+ get_timerange(i, strsep(&info, "|,"));
+ if (info)
+ i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
+ if (info)
+ i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
+ if (info)
+ i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
+ return 1;
+}
+
+int ast_check_timing(const struct ast_timing *i)
+{
+ struct ast_tm tm;
+ struct timeval tv = ast_tvnow();
+
+ ast_localtime(&tv, &tm, NULL);
+
+ /* If it's not the right month, return */
+ if (!(i->monthmask & (1 << tm.tm_mon)))
+ return 0;
+
+ /* If it's not that time of the month.... */
+ /* Warning, tm_mday has range 1..31! */
+ if (!(i->daymask & (1 << (tm.tm_mday-1))))
+ return 0;
+
+ /* If it's not the right day of the week */
+ if (!(i->dowmask & (1 << tm.tm_wday)))
+ return 0;
+
+ /* Sanity check the hour just to be safe */
+ if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
+ ast_log(LOG_WARNING, "Insane time...\n");
+ return 0;
+ }
+
+ /* Now the tough part, we calculate if it fits
+ in the right time based on min/hour */
+ if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
+ return 0;
+
+ /* If we got this far, then we're good */
+ return 1;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+ struct ast_include *new_include;
+ char *c;
+ struct ast_include *i, *il = NULL; /* include, include_last */
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_include);
+ length += 2 * (strlen(value) + 1);
+
+ /* allocate new include structure ... */
+ if (!(new_include = ast_calloc(1, length)))
+ return -1;
+ /* Fill in this structure. Use 'p' for assignments, as the fields
+ * in the structure are 'const char *'
+ */
+ p = new_include->stuff;
+ new_include->name = p;
+ strcpy(p, value);
+ p += strlen(value) + 1;
+ new_include->rname = p;
+ strcpy(p, value);
+ /* Strip off timing info, and process if it is there */
+ if ( (c = strchr(p, ',')) ) {
+ *c++ = '\0';
+ new_include->hastime = ast_build_timing(&(new_include->timing), c);
+ }
+ new_include->next = NULL;
+ new_include->registrar = registrar;
+
+ ast_wrlock_context(con);
+
+ /* ... go to last include and check if context is already included too... */
+ for (i = con->includes; i; i = i->next) {
+ if (!strcasecmp(i->name, new_include->name)) {
+ ast_free(new_include);
+ ast_unlock_context(con);
+ errno = EEXIST;
+ return -1;
+ }
+ il = i;
+ }
+
+ /* ... include new context into context list, unlock, return */
+ if (il)
+ il->next = new_include;
+ else
+ con->includes = new_include;
+ ast_verb(3, "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
+
+ ast_unlock_context(con);
+
+ return 0;
+}
+
+/*
+ * errno values
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ */
+int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) { /* found, add switch to this context */
+ ret = ast_context_add_switch2(c, sw, data, eval, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+ struct ast_sw *new_sw;
+ struct ast_sw *i;
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_sw);
+ length += strlen(value) + 1;
+ if (data)
+ length += strlen(data);
+ length++;
+ if (eval) {
+ /* Create buffer for evaluation of variables */
+ length += SWITCH_DATA_LENGTH;
+ length++;
+ }
+
+ /* allocate new sw structure ... */
+ if (!(new_sw = ast_calloc(1, length)))
+ return -1;
+ /* ... fill in this structure ... */
+ p = new_sw->stuff;
+ new_sw->name = p;
+ strcpy(new_sw->name, value);
+ p += strlen(value) + 1;
+ new_sw->data = p;
+ if (data) {
+ strcpy(new_sw->data, data);
+ p += strlen(data) + 1;
+ } else {
+ strcpy(new_sw->data, "");
+ p++;
+ }
+ if (eval)
+ new_sw->tmpdata = p;
+ new_sw->eval = eval;
+ new_sw->registrar = registrar;
+
+ /* ... try to lock this context ... */
+ ast_wrlock_context(con);
+
+ /* ... go to last sw and check if context is already swd too... */
+ AST_LIST_TRAVERSE(&con->alts, i, list) {
+ if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
+ ast_free(new_sw);
+ ast_unlock_context(con);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ /* ... sw new context into context list, unlock, return */
+ AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
+
+ ast_verb(3, "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
+
+ ast_unlock_context(con);
+
+ return 0;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - there is not context existence
+ */
+int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
+{
+ struct ast_ignorepat *ip, *ipl = NULL;
+
+ ast_wrlock_context(con);
+
+ for (ip = con->ignorepats; ip; ip = ip->next) {
+ if (!strcmp(ip->pattern, ignorepat) &&
+ (!registrar || (registrar == ip->registrar))) {
+ if (ipl) {
+ ipl->next = ip->next;
+ ast_free(ip);
+ } else {
+ con->ignorepats = ip->next;
+ ast_free(ip);
+ }
+ ast_unlock_context(con);
+ return 0;
+ }
+ ipl = ip;
+ }
+
+ ast_unlock_context(con);
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - there is no existence of context
+ */
+int ast_context_add_ignorepat(const char *context, const char *value, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_context_add_ignorepat2(c, value, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
+ int length;
+ length = sizeof(struct ast_ignorepat);
+ length += strlen(value) + 1;
+ if (!(ignorepat = ast_calloc(1, length)))
+ return -1;
+ /* The cast to char * is because we need to write the initial value.
+ * The field is not supposed to be modified otherwise
+ */
+ strcpy((char *)ignorepat->pattern, value);
+ ignorepat->next = NULL;
+ ignorepat->registrar = registrar;
+ ast_wrlock_context(con);
+ for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
+ ignorepatl = ignorepatc;
+ if (!strcasecmp(ignorepatc->pattern, value)) {
+ /* Already there */
+ ast_unlock_context(con);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+ if (ignorepatl)
+ ignorepatl->next = ignorepat;
+ else
+ con->ignorepats = ignorepat;
+ ast_unlock_context(con);
+ return 0;
+
+}
+
+int ast_ignore_pattern(const char *context, const char *pattern)
+{
+ struct ast_context *con = ast_context_find(context);
+ if (con) {
+ struct ast_ignorepat *pat;
+ for (pat = con->ignorepats; pat; pat = pat->next) {
+ if (ast_extension_match(pat->pattern, pattern))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ *
+ */
+int ast_add_extension(const char *context, int replace, const char *extension,
+ int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *), const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
+ application, data, datad, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+ if (!chan)
+ return -1;
+
+ ast_channel_lock(chan);
+
+ if (!ast_strlen_zero(context))
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ if (!ast_strlen_zero(exten))
+ ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+ if (priority > -1) {
+ chan->priority = priority;
+ /* see flag description in channel.h for explanation */
+ if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
+ chan->priority--;
+ }
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+ int res = 0;
+
+ ast_channel_lock(chan);
+
+ if (chan->pbx) { /* This channel is currently in the PBX */
+ ast_explicit_goto(chan, context, exten, priority);
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
+ } else {
+ /* In order to do it when the channel doesn't really exist within
+ the PBX, we have to make a new channel, masquerade, and start the PBX
+ at the new location */
+ struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
+ if (chan->cdr) {
+ tmpchan->cdr = ast_cdr_dup(chan->cdr);
+ }
+ if (!tmpchan)
+ res = -1;
+ else {
+ /* Make formats okay */
+ tmpchan->readformat = chan->readformat;
+ tmpchan->writeformat = chan->writeformat;
+ /* Setup proper location */
+ ast_explicit_goto(tmpchan,
+ S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
+
+ /* Masquerade into temp channel */
+ ast_channel_masquerade(tmpchan, chan);
+
+ /* Grab the locks and get going */
+ ast_channel_lock(tmpchan);
+ ast_do_masquerade(tmpchan);
+ ast_channel_unlock(tmpchan);
+ /* Start the PBX going on our stolen channel */
+ if (ast_pbx_start(tmpchan)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
+ ast_hangup(tmpchan);
+ res = -1;
+ }
+ }
+ }
+ ast_channel_unlock(chan);
+ return res;
+}
+
+int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
+{
+ struct ast_channel *chan;
+ int res = -1;
+
+ chan = ast_get_channel_by_name_locked(channame);
+ if (chan) {
+ res = ast_async_goto(chan, context, exten, priority);
+ ast_channel_unlock(chan);
+ }
+ return res;
+}
+
+/*! \brief copy a string skipping whitespace */
+static int ext_strncpy(char *dst, const char *src, int len)
+{
+ int count=0;
+
+ while (*src && (count < len - 1)) {
+ switch (*src) {
+ case ' ':
+ /* otherwise exten => [a-b],1,... doesn't work */
+ /* case '-': */
+ /* Ignore */
+ break;
+ default:
+ *dst = *src;
+ dst++;
+ }
+ src++;
+ count++;
+ }
+ *dst = '\0';
+
+ return count;
+}
+
+/*!
+ * \brief add the extension in the priority chain.
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+static int add_pri(struct ast_context *con, struct ast_exten *tmp,
+ struct ast_exten *el, struct ast_exten *e, int replace)
+{
+ struct ast_exten *ep;
+ struct ast_exten *eh=e;
+
+ for (ep = NULL; e ; ep = e, e = e->peer) {
+ if (e->priority >= tmp->priority)
+ break;
+ }
+ if (!e) { /* go at the end, and ep is surely set because the list is not empty */
+ ast_hashtab_insert_safe(eh->peer_tree, tmp);
+ if (tmp->label)
+ ast_hashtab_insert_safe(eh->peer_label_tree, tmp);
+ ep->peer = tmp;
+ return 0; /* success */
+ }
+ if (e->priority == tmp->priority) {
+ /* Can't have something exactly the same. Is this a
+ replacement? If so, replace, otherwise, bonk. */
+ if (!replace) {
+ ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
+ if (tmp->datad)
+ tmp->datad(tmp->data);
+ ast_free(tmp);
+ return -1;
+ }
+ /* we are replacing e, so copy the link fields and then update
+ * whoever pointed to e to point to us
+ */
+ tmp->next = e->next; /* not meaningful if we are not first in the peer list */
+ tmp->peer = e->peer; /* always meaningful */
+ if (ep) { /* We're in the peer list, just insert ourselves */
+ ast_hashtab_remove_object_via_lookup(eh->peer_tree,e);
+ if (e->label)
+ ast_hashtab_remove_object_via_lookup(eh->peer_label_tree,e);
+ ast_hashtab_insert_safe(eh->peer_tree,tmp);
+ if (tmp->label)
+ ast_hashtab_insert_safe(eh->peer_label_tree,tmp);
+ ep->peer = tmp;
+ } else if (el) { /* We're the first extension. Take over e's functions */
+ struct match_char *x = add_exten_to_pattern_tree(con, e, 1);
+ tmp->peer_tree = e->peer_tree;
+ tmp->peer_label_tree = e->peer_label_tree;
+ ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e);
+ ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+ if (e->label)
+ ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e);
+ if (tmp->label)
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ ast_hashtab_remove_object_via_lookup(con->root_tree, e);
+ ast_hashtab_insert_safe(con->root_tree, tmp);
+ el->next = tmp;
+ /* The pattern trie points to this exten; replace the pointer,
+ and all will be well */
+
+ if (x->exten) { /* this test for safety purposes */
+ x->exten = tmp; /* replace what would become a bad pointer */
+ } else {
+ ast_log(LOG_ERROR,"Trying to delete an exten from a context, but the pattern tree node returned isn't an extension\n");
+ }
+ } else { /* We're the very first extension. */
+ struct match_char *x = add_exten_to_pattern_tree(con, e, 1);
+ ast_hashtab_remove_object_via_lookup(con->root_tree,e);
+ ast_hashtab_insert_safe(con->root_tree,tmp);
+ tmp->peer_tree = e->peer_tree;
+ tmp->peer_label_tree = e->peer_label_tree;
+ ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e);
+ ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+ if (e->label)
+ ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e);
+ if (tmp->label)
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ ast_hashtab_remove_object_via_lookup(con->root_tree, e);
+ ast_hashtab_insert_safe(con->root_tree, tmp);
+ con->root = tmp;
+ /* The pattern trie points to this exten; replace the pointer,
+ and all will be well */
+ if (x->exten) { /* this test for safety purposes */
+ x->exten = tmp; /* replace what would become a bad pointer */
+ } else {
+ ast_log(LOG_ERROR,"Trying to delete an exten from a context, but the pattern tree node returned isn't an extension\n");
+ }
+ }
+ if (tmp->priority == PRIORITY_HINT)
+ ast_change_hint(e,tmp);
+ /* Destroy the old one */
+ if (e->datad)
+ e->datad(e->data);
+ ast_free(e);
+ } else { /* Slip ourselves in just before e */
+ tmp->peer = e;
+ tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
+ if (ep) { /* Easy enough, we're just in the peer list */
+ if (tmp->label)
+ ast_hashtab_insert_safe(eh->peer_label_tree, tmp);
+ ast_hashtab_insert_safe(eh->peer_tree, tmp);
+ ep->peer = tmp;
+ } else { /* we are the first in some peer list, so link in the ext list */
+ tmp->peer_tree = e->peer_tree;
+ tmp->peer_label_tree = e ->peer_label_tree;
+ e->peer_tree = 0;
+ e->peer_label_tree = 0;
+ ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+ if (tmp->label) {
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ }
+ ast_hashtab_remove_object_via_lookup(con->root_tree,e);
+ ast_hashtab_insert_safe(con->root_tree,tmp);
+ if (el)
+ el->next = tmp; /* in the middle... */
+ else
+ con->root = tmp; /* ... or at the head */
+ e->next = NULL; /* e is no more at the head, so e->next must be reset */
+ }
+ /* And immediately return success. */
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ return 0;
+}
+
+/*! \brief
+ * Main interface to add extensions to the list for out context.
+ *
+ * We sort extensions in order of matching preference, so that we can
+ * stop the search as soon as we find a suitable match.
+ * This ordering also takes care of wildcards such as '.' (meaning
+ * "one or more of any character") and '!' (which is 'earlymatch',
+ * meaning "zero or more of any character" but also impacts the
+ * return value from CANMATCH and EARLYMATCH.
+ *
+ * The extension match rules defined in the devmeeting 2006.05.05 are
+ * quite simple: WE SELECT THE LONGEST MATCH.
+ * In detail, "longest" means the number of matched characters in
+ * the extension. In case of ties (e.g. _XXX and 333) in the length
+ * of a pattern, we give priority to entries with the smallest cardinality
+ * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
+ * while the latter has 7, etc.
+ * In case of same cardinality, the first element in the range counts.
+ * If we still have a tie, any final '!' will make this as a possibly
+ * less specific pattern.
+ *
+ * EBUSY - can't lock
+ * EEXIST - extension with the same priority exist and no replace is set
+ *
+ */
+int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ /*
+ * Sort extensions (or patterns) according to the rules indicated above.
+ * These are implemented by the function ext_cmp()).
+ * All priorities for the same ext/pattern/cid are kept in a list,
+ * using the 'peer' field as a link field..
+ */
+ struct ast_exten *tmp, *tmp2, *e, *el = NULL;
+ int res;
+ int length;
+ char *p;
+ char expand_buf[VAR_BUF_SIZE];
+ struct ast_exten dummy_exten = {0};
+ char dummy_name[1024];
+
+ /* if we are adding a hint, and there are global variables, and the hint
+ contains variable references, then expand them
+ */
+ ast_rwlock_rdlock(&globalslock);
+ if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
+ pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
+ application = expand_buf;
+ }
+ ast_rwlock_unlock(&globalslock);
+
+ length = sizeof(struct ast_exten);
+ length += strlen(extension) + 1;
+ length += strlen(application) + 1;
+ if (label)
+ length += strlen(label) + 1;
+ if (callerid)
+ length += strlen(callerid) + 1;
+ else
+ length ++; /* just the '\0' */
+
+ /* Be optimistic: Build the extension structure first */
+ if (!(tmp = ast_calloc(1, length)))
+ return -1;
+
+ /* use p as dst in assignments, as the fields are const char * */
+ p = tmp->stuff;
+ if (label) {
+ tmp->label = p;
+ strcpy(p, label);
+ p += strlen(label) + 1;
+ }
+ tmp->exten = p;
+ p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
+ tmp->priority = priority;
+ tmp->cidmatch = p; /* but use p for assignments below */
+ if (callerid) {
+ p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
+ tmp->matchcid = 1;
+ } else {
+ *p++ = '\0';
+ tmp->matchcid = 0;
+ }
+ tmp->app = p;
+ strcpy(p, application);
+ tmp->parent = con;
+ tmp->data = data;
+ tmp->datad = datad;
+ tmp->registrar = registrar;
+
+ ast_wrlock_context(con);
+
+ if (con->pattern_tree) { /* usually, on initial load, the pattern_tree isn't formed until the first find_exten; so if we are adding
+ an extension, and the trie exists, then we need to incrementally add this pattern to it. */
+ strncpy(dummy_name,extension,sizeof(dummy_name));
+ dummy_exten.exten = dummy_name;
+ dummy_exten.matchcid = 0;
+ dummy_exten.cidmatch = 0;
+ tmp2 = ast_hashtab_lookup(con->root_tree,&dummy_exten);
+ if (!tmp2) {
+ /* hmmm, not in the trie; */
+ add_exten_to_pattern_tree(con, tmp, 0);
+ ast_hashtab_insert_safe(con->root_tree, tmp); /* for the sake of completeness */
+ }
+ }
+ res = 0; /* some compilers will think it is uninitialized otherwise */
+ for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
+ res = ext_cmp(e->exten, extension);
+ if (res == 0) { /* extension match, now look at cidmatch */
+ if (!e->matchcid && !tmp->matchcid)
+ res = 0;
+ else if (tmp->matchcid && !e->matchcid)
+ res = 1;
+ else if (e->matchcid && !tmp->matchcid)
+ res = -1;
+ else
+ res = strcasecmp(e->cidmatch, tmp->cidmatch);
+ }
+ if (res >= 0)
+ break;
+ }
+ if (e && res == 0) { /* exact match, insert in the pri chain */
+ res = add_pri(con, tmp, el, e, replace);
+ ast_unlock_context(con);
+ if (res < 0) {
+ errno = EEXIST; /* XXX do we care ? */
+ return 0; /* XXX should we return -1 maybe ? */
+ }
+ } else {
+ /*
+ * not an exact match, this is the first entry with this pattern,
+ * so insert in the main list right before 'e' (if any)
+ */
+ tmp->next = e;
+ if (el) { /* there is another exten already in this context */
+ el->next = tmp;
+ tmp->peer_tree = ast_hashtab_create(13,
+ hashtab_compare_exten_numbers,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_priority,
+ 0);
+ tmp->peer_label_tree = ast_hashtab_create(7,
+ hashtab_compare_exten_labels,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_labels,
+ 0);
+ if (label)
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ ast_hashtab_insert_safe(tmp->peer_tree, tmp);
+
+ } else { /* this is the first exten in this context */
+ if (!con->root_tree)
+ con->root_tree = ast_hashtab_create(27,
+ hashtab_compare_extens,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_extens,
+ 0);
+ con->root = tmp;
+ con->root->peer_tree = ast_hashtab_create(13,
+ hashtab_compare_exten_numbers,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_priority,
+ 0);
+ con->root->peer_label_tree = ast_hashtab_create(7,
+ hashtab_compare_exten_labels,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_labels,
+ 0);
+ if (label)
+ ast_hashtab_insert_safe(con->root->peer_label_tree,tmp);
+ ast_hashtab_insert_safe(con->root->peer_tree, tmp);
+ }
+ ast_hashtab_insert_safe(con->root_tree, tmp);
+ ast_unlock_context(con);
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ if (option_debug) {
+ if (tmp->matchcid) {
+ ast_debug(1, "Added extension '%s' priority %d (CID match '%s') to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_debug(1, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+
+ if (tmp->matchcid) {
+ ast_verb(3, "Added extension '%s' priority %d (CID match '%s')to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_verb(3, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+
+ return 0;
+}
+
+struct async_stat {
+ pthread_t p;
+ struct ast_channel *chan;
+ char context[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ int priority;
+ int timeout;
+ char app[AST_MAX_EXTENSION];
+ char appdata[1024];
+};
+
+static void *async_wait(void *data)
+{
+ struct async_stat *as = data;
+ struct ast_channel *chan = as->chan;
+ int timeout = as->timeout;
+ int res;
+ struct ast_frame *f;
+ struct ast_app *app;
+
+ while (timeout && (chan->_state != AST_STATE_UP)) {
+ res = ast_waitfor(chan, timeout);
+ if (res < 1)
+ break;
+ if (timeout > -1)
+ timeout = res;
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if ((f->subclass == AST_CONTROL_BUSY) ||
+ (f->subclass == AST_CONTROL_CONGESTION) ) {
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ }
+ if (chan->_state == AST_STATE_UP) {
+ if (!ast_strlen_zero(as->app)) {
+ app = pbx_findapp(as->app);
+ if (app) {
+ ast_verb(3, "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
+ pbx_exec(chan, app, as->appdata);
+ } else
+ ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
+ } else {
+ if (!ast_strlen_zero(as->context))
+ ast_copy_string(chan->context, as->context, sizeof(chan->context));
+ if (!ast_strlen_zero(as->exten))
+ ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
+ if (as->priority > 0)
+ chan->priority = as->priority;
+ /* Run the PBX */
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
+ } else {
+ /* PBX will have taken care of this */
+ chan = NULL;
+ }
+ }
+ }
+ ast_free(as);
+ if (chan)
+ ast_hangup(chan);
+ return NULL;
+}
+
+/*!
+ * \brief Function to post an empty cdr after a spool call fails.
+ * \note This function posts an empty cdr for a failed spool call
+*/
+static int ast_pbx_outgoing_cdr_failed(void)
+{
+ /* allocate a channel */
+ struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
+
+ if (!chan)
+ return -1; /* failure */
+
+ if (!chan->cdr) {
+ /* allocation of the cdr failed */
+ ast_channel_free(chan); /* free the channel */
+ return -1; /* return failure */
+ }
+
+ /* allocation of the cdr was successful */
+ ast_cdr_init(chan->cdr, chan); /* initialize our channel's cdr */
+ ast_cdr_start(chan->cdr); /* record the start and stop time */
+ ast_cdr_end(chan->cdr);
+ ast_cdr_failed(chan->cdr); /* set the status to failed */
+ ast_cdr_detach(chan->cdr); /* post and free the record */
+ ast_channel_free(chan); /* free the channel */
+
+ return 0; /* success */
+}
+
+int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
+{
+ struct ast_channel *chan;
+ struct async_stat *as;
+ int res = -1, cdr_res = -1;
+ struct outgoing_helper oh;
+
+ if (sync) {
+ oh.context = context;
+ oh.exten = exten;
+ oh.priority = priority;
+ oh.cid_num = cid_num;
+ oh.cid_name = cid_name;
+ oh.account = account;
+ oh.vars = vars;
+ oh.parent_channel = NULL;
+
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+ ast_channel_lock(chan);
+ }
+ if (chan) {
+ if (chan->_state == AST_STATE_UP) {
+ res = 0;
+ ast_verb(4, "Channel %s was answered.\n", chan->name);
+
+ if (sync > 1) {
+ if (channel)
+ ast_channel_unlock(chan);
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
+ if (channel)
+ *channel = NULL;
+ ast_hangup(chan);
+ res = -1;
+ }
+ } else {
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
+ if (channel) {
+ *channel = NULL;
+ ast_channel_unlock(chan);
+ }
+ ast_hangup(chan);
+ res = -1;
+ }
+ }
+ } else {
+ ast_verb(4, "Channel %s was never answered.\n", chan->name);
+
+ if (chan->cdr) { /* update the cdr */
+ /* here we update the status of the call, which sould be busy.
+ * if that fails then we set the status to failed */
+ if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+
+ if (channel) {
+ *channel = NULL;
+ ast_channel_unlock(chan);
+ }
+ ast_hangup(chan);
+ }
+ }
+
+ if (res < 0) { /* the call failed for some reason */
+ if (*reason == 0) { /* if the call failed (not busy or no answer)
+ * update the cdr with the failed message */
+ cdr_res = ast_pbx_outgoing_cdr_failed();
+ if (cdr_res != 0) {
+ res = cdr_res;
+ goto outgoing_exten_cleanup;
+ }
+ }
+
+ /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
+ /* check if "failed" exists */
+ if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
+ chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
+ if (chan) {
+ char failed_reason[4] = "";
+ if (!ast_strlen_zero(context))
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ set_ext_pri(chan, "failed", 1);
+ ast_set_variables(chan, vars);
+ snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
+ pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ ast_pbx_run(chan);
+ }
+ }
+ }
+ } else {
+ if (!(as = ast_calloc(1, sizeof(*as)))) {
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+ ast_channel_lock(chan);
+ }
+ if (!chan) {
+ ast_free(as);
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ as->chan = chan;
+ ast_copy_string(as->context, context, sizeof(as->context));
+ set_ext_pri(as->chan, exten, priority);
+ as->timeout = timeout;
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ if (ast_pthread_create_detached(&as->p, NULL, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ ast_free(as);
+ if (channel) {
+ *channel = NULL;
+ ast_channel_unlock(chan);
+ }
+ ast_hangup(chan);
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ res = 0;
+ }
+outgoing_exten_cleanup:
+ ast_variables_destroy(vars);
+ return res;
+}
+
+struct app_tmp {
+ char app[256];
+ char data[256];
+ struct ast_channel *chan;
+ pthread_t t;
+};
+
+/*! \brief run the application and free the descriptor once done */
+static void *ast_pbx_run_app(void *data)
+{
+ struct app_tmp *tmp = data;
+ struct ast_app *app;
+ app = pbx_findapp(tmp->app);
+ if (app) {
+ ast_verb(4, "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
+ pbx_exec(tmp->chan, app, tmp->data);
+ } else
+ ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
+ ast_hangup(tmp->chan);
+ ast_free(tmp);
+ return NULL;
+}
+
+int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
+{
+ struct ast_channel *chan;
+ struct app_tmp *tmp;
+ int res = -1, cdr_res = -1;
+ struct outgoing_helper oh;
+
+ memset(&oh, 0, sizeof(oh));
+ oh.vars = vars;
+ oh.account = account;
+
+ if (locked_channel)
+ *locked_channel = NULL;
+ if (ast_strlen_zero(app)) {
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ if (sync) {
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (chan) {
+ if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
+ ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
+ } else {
+ chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
+ if (!chan->cdr) {
+ /* allocation of the cdr failed */
+ ast_free(chan->pbx);
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ /* allocation of the cdr was successful */
+ ast_cdr_init(chan->cdr, chan); /* initialize our channel's cdr */
+ ast_cdr_start(chan->cdr);
+ }
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ if (chan->_state == AST_STATE_UP) {
+ res = 0;
+ ast_verb(4, "Channel %s was answered.\n", chan->name);
+ tmp = ast_calloc(1, sizeof(*tmp));
+ if (!tmp)
+ res = -1;
+ else {
+ ast_copy_string(tmp->app, app, sizeof(tmp->app));
+ if (appdata)
+ ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
+ tmp->chan = chan;
+ if (sync > 1) {
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_pbx_run_app(tmp);
+ } else {
+ if (locked_channel)
+ ast_channel_lock(chan);
+ if (ast_pthread_create_detached(&tmp->t, NULL, ast_pbx_run_app, tmp)) {
+ ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
+ ast_free(tmp);
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_hangup(chan);
+ res = -1;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ }
+ }
+ } else {
+ ast_verb(4, "Channel %s was never answered.\n", chan->name);
+ if (chan->cdr) { /* update the cdr */
+ /* here we update the status of the call, which sould be busy.
+ * if that fails then we set the status to failed */
+ if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+ ast_hangup(chan);
+ }
+ }
+
+ if (res < 0) { /* the call failed for some reason */
+ if (*reason == 0) { /* if the call failed (not busy or no answer)
+ * update the cdr with the failed message */
+ cdr_res = ast_pbx_outgoing_cdr_failed();
+ if (cdr_res != 0) {
+ res = cdr_res;
+ goto outgoing_app_cleanup;
+ }
+ }
+ }
+
+ } else {
+ struct async_stat *as;
+ if (!(as = ast_calloc(1, sizeof(*as)))) {
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (!chan) {
+ ast_free(as);
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ as->chan = chan;
+ ast_copy_string(as->app, app, sizeof(as->app));
+ if (appdata)
+ ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
+ as->timeout = timeout;
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ /* Start a new thread, and get something handling this channel. */
+ if (locked_channel)
+ ast_channel_lock(chan);
+ if (ast_pthread_create_detached(&as->p, NULL, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ ast_free(as);
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_hangup(chan);
+ res = -1;
+ goto outgoing_app_cleanup;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ res = 0;
+ }
+outgoing_app_cleanup:
+ ast_variables_destroy(vars);
+ return res;
+}
+
+void __ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ struct ast_context *tmp, *tmpl=NULL;
+ struct ast_include *tmpi;
+ struct ast_sw *sw;
+ struct ast_exten *e, *el, *en;
+ struct ast_ignorepat *ipi;
+
+ for (tmp = contexts; tmp; ) {
+ struct ast_context *next; /* next starting point */
+ for (; tmp; tmpl = tmp, tmp = tmp->next) {
+ ast_debug(1, "check ctx %s %s\n", tmp->name, tmp->registrar);
+ if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
+ (!con || !strcasecmp(tmp->name, con->name)) )
+ break; /* found it */
+ }
+ if (!tmp) /* not found, we are done */
+ break;
+ ast_wrlock_context(tmp);
+ ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar);
+ ast_hashtab_remove_this_object(contexts_tree,tmp);
+
+ next = tmp->next;
+ if (tmpl)
+ tmpl->next = next;
+ else
+ contexts = next;
+ /* Okay, now we're safe to let it go -- in a sense, we were
+ ready to let it go as soon as we locked it. */
+ ast_unlock_context(tmp);
+ for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
+ struct ast_include *tmpil = tmpi;
+ tmpi = tmpi->next;
+ ast_free(tmpil);
+ }
+ for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
+ struct ast_ignorepat *ipl = ipi;
+ ipi = ipi->next;
+ ast_free(ipl);
+ }
+ /* destroy the hash tabs */
+ if (tmp->root_tree) {
+ ast_hashtab_destroy(tmp->root_tree, 0);
+ }
+ /* and destroy the pattern tree */
+ if (tmp->pattern_tree)
+ destroy_pattern_tree(tmp->pattern_tree);
+
+ while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
+ ast_free(sw);
+ for (e = tmp->root; e;) {
+ for (en = e->peer; en;) {
+ el = en;
+ en = en->peer;
+ destroy_exten(el);
+ }
+ el = e;
+ e = e->next;
+ destroy_exten(el);
+ }
+ ast_rwlock_destroy(&tmp->lock);
+ ast_free(tmp);
+ /* if we have a specific match, we are done, otherwise continue */
+ tmp = con ? NULL : next;
+ }
+}
+
+void ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ ast_wrlock_contexts();
+ __ast_context_destroy(con,registrar);
+ ast_unlock_contexts();
+}
+
+static void wait_for_hangup(struct ast_channel *chan, void *data)
+{
+ int res;
+ struct ast_frame *f;
+ double waitsec;
+ int waittime;
+
+ if (ast_strlen_zero(data) || (sscanf(data, "%lg", &waitsec) != 1) || (waitsec < 0))
+ waitsec = -1;
+ if (waitsec > -1) {
+ waittime = waitsec * 1000.0;
+ ast_safe_sleep(chan, waittime);
+ } else do {
+ res = ast_waitfor(chan, -1);
+ if (res < 0)
+ return;
+ f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+ } while(f);
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_progress(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_busy(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_BUSY);
+ /* Don't change state of an UP channel, just indicate
+ busy in audio */
+ if (chan->_state != AST_STATE_UP)
+ ast_setstate(chan, AST_STATE_BUSY);
+ wait_for_hangup(chan, data);
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_CONGESTION);
+ /* Don't change state of an UP channel, just indicate
+ congestion in audio */
+ if (chan->_state != AST_STATE_UP)
+ ast_setstate(chan, AST_STATE_BUSY);
+ wait_for_hangup(chan, data);
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_answer(struct ast_channel *chan, void *data)
+{
+ int delay = 0;
+
+ if ((chan->_state != AST_STATE_UP) && !ast_strlen_zero(data))
+ delay = atoi(data);
+
+ return __ast_answer(chan, delay);
+}
+
+static int pbx_builtin_keepalive(struct ast_channel *chan, void *data)
+{
+ return AST_PBX_KEEPALIVE;
+}
+
+AST_APP_OPTIONS(resetcdr_opts, {
+ AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
+ AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
+ AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
+});
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
+{
+ char *args;
+ struct ast_flags flags = { 0 };
+
+ if (!ast_strlen_zero(data)) {
+ args = ast_strdupa(data);
+ ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
+ }
+
+ ast_cdr_reset(chan->cdr, &flags);
+
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
+{
+ /* Copy the AMA Flags as specified */
+ ast_cdr_setamaflags(chan, data ? data : "");
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
+{
+ if (!ast_strlen_zero(data)) {
+ int cause;
+ char *endptr;
+
+ if ((cause = ast_str2cause(data)) > -1) {
+ chan->hangupcause = cause;
+ return -1;
+ }
+
+ cause = strtol((const char *) data, &endptr, 10);
+ if (cause != 0 || (data != endptr)) {
+ chan->hangupcause = cause;
+ return -1;
+ }
+
+ ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
+ }
+
+ if (!chan->hangupcause) {
+ chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ }
+
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ char *s, *ts;
+ struct ast_timing timing;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?[[context,]extension,]priority\n");
+ return -1;
+ }
+
+ ts = s = ast_strdupa(data);
+
+ /* Separate the Goto path */
+ strsep(&ts, "?");
+
+ /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
+ if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
+ res = pbx_builtin_goto(chan, ts);
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
+{
+ char *s, *appname;
+ struct ast_timing timing;
+ struct ast_app *app;
+ static const char *usage = "ExecIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?<appname>[(<appargs>)]";
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s\n", usage);
+ return -1;
+ }
+
+ appname = ast_strdupa(data);
+
+ s = strsep(&appname, "?"); /* Separate the timerange and application name/data */
+ if (!appname) { /* missing application */
+ ast_log(LOG_WARNING, "%s\n", usage);
+ return -1;
+ }
+
+ if (!ast_build_timing(&timing, s)) {
+ ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
+ return -1;
+ }
+
+ if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
+ return 0;
+
+ /* now split appname(appargs) */
+ if ((s = strchr(appname, '('))) {
+ char *e;
+ *s++ = '\0';
+ if ((e = strrchr(s, ')')))
+ *e = '\0';
+ else
+ ast_log(LOG_WARNING, "Failed to find closing parenthesis\n");
+ }
+
+
+ if ((app = pbx_findapp(appname))) {
+ return pbx_exec(chan, app, S_OR(s, ""));
+ } else {
+ ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
+ return -1;
+ }
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_wait(struct ast_channel *chan, void *data)
+{
+ double s;
+ int ms;
+
+ /* Wait for "n" seconds */
+ if (data && (s = atof(data)) > 0.0) {
+ ms = s * 1000.0;
+ return ast_safe_sleep(chan, ms);
+ }
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
+{
+ int ms, res;
+ double s;
+ struct ast_flags flags = {0};
+ char *opts[1] = { NULL };
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(timeout);
+ AST_APP_ARG(options);
+ );
+
+ if (!ast_strlen_zero(data)) {
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+ } else
+ memset(&args, 0, sizeof(args));
+
+ if (args.options)
+ ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
+
+ if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
+ ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
+ } else if (ast_test_flag(&flags, WAITEXTEN_MOH)) {
+ ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
+ } else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE)) {
+ const struct ind_tone_zone_sound *ts = ast_get_indication_tone(chan->zone, "dial");
+ if (ts)
+ ast_playtones_start(chan, 0, ts->data, 0);
+ else
+ ast_tonepair_start(chan, 350, 440, 0, 0);
+ }
+ /* Wait for "n" seconds */
+ if (args.timeout && (s = atof(args.timeout)) > 0)
+ ms = s * 1000.0;
+ else if (chan->pbx)
+ ms = chan->pbx->rtimeout * 1000;
+ else
+ ms = 10000;
+
+ res = ast_waitfordigit(chan, ms);
+ if (!res) {
+ if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
+ ast_verb(3, "Timeout on %s, continuing...\n", chan->name);
+ } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
+ ast_verb(3, "Timeout on %s, going to 't'\n", chan->name);
+ set_ext_pri(chan, "t", 0); /* XXX is the 0 correct ? */
+ } else {
+ ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
+ res = -1;
+ }
+ }
+
+ if (ast_test_flag(&flags, WAITEXTEN_MOH))
+ ast_indicate(chan, AST_CONTROL_UNHOLD);
+ else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE))
+ ast_playtones_stop(chan);
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_background(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int mres = 0;
+ struct ast_flags flags = {0};
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(options);
+ AST_APP_ARG(lang);
+ AST_APP_ARG(context);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.lang))
+ args.lang = (char *)chan->language; /* XXX this is const */
+
+ if (ast_strlen_zero(args.context))
+ args.context = chan->context;
+
+ if (args.options) {
+ if (!strcasecmp(args.options, "skip"))
+ flags.flags = BACKGROUND_SKIP;
+ else if (!strcasecmp(args.options, "noanswer"))
+ flags.flags = BACKGROUND_NOANSWER;
+ else
+ ast_app_parse_options(background_opts, &flags, NULL, args.options);
+ }
+
+ /* Answer if need be */
+ if (chan->_state != AST_STATE_UP) {
+ if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
+ goto done;
+ } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
+ res = ast_answer(chan);
+ }
+ }
+
+ if (!res) {
+ char *back = args.filename;
+ char *front;
+
+ ast_stopstream(chan); /* Stop anything playing */
+ /* Stream the list of files */
+ while (!res && (front = strsep(&back, "&")) ) {
+ if ( (res = ast_streamfile(chan, front, args.lang)) ) {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
+ res = 0;
+ mres = 1;
+ break;
+ }
+ if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
+ res = ast_waitstream(chan, "");
+ } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
+ res = ast_waitstream_exten(chan, args.context);
+ } else {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ if (args.context != chan->context && res) {
+ snprintf(chan->exten, sizeof(chan->exten), "%c", res);
+ ast_copy_string(chan->context, args.context, sizeof(chan->context));
+ chan->priority = 0;
+ res = 0;
+ }
+done:
+ pbx_builtin_setvar_helper(chan, "BACKGROUNDSTATUS", mres ? "FAILED" : "SUCCESS");
+ return res;
+}
+
+/*! Goto
+ * \ingroup applications
+ */
+static int pbx_builtin_goto(struct ast_channel *chan, void *data)
+{
+ int res = ast_parseable_goto(chan, data);
+ if (!res)
+ ast_verb(3, "Goto (%s,%s,%d)\n", chan->context, chan->exten, chan->priority + 1);
+ return res;
+}
+
+
+int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
+{
+ struct ast_var_t *variables;
+ const char *var, *val;
+ int total = 0;
+
+ if (!chan)
+ return 0;
+
+ (*buf)->used = 0;
+ (*buf)->str[0] = '\0';
+
+ ast_channel_lock(chan);
+
+ AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
+ if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
+ /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
+ ) {
+ if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ } else
+ break;
+ }
+
+ ast_channel_unlock(chan);
+
+ return total;
+}
+
+const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
+{
+ struct ast_var_t *variables;
+ const char *ret = NULL;
+ int i;
+ struct varshead *places[2] = { NULL, &globals };
+
+ if (!name)
+ return NULL;
+
+ if (chan) {
+ ast_channel_lock(chan);
+ places[0] = &chan->varshead;
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (!strcmp(name, ast_var_name(variables))) {
+ ret = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ if (ret)
+ break;
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+
+ return ret;
+}
+
+void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+
+ if (name[strlen(name)-1] == ')') {
+ char *function = ast_strdupa(name);
+
+ ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
+ ast_func_write(chan, function, value);
+ return;
+ }
+
+ if (chan) {
+ ast_channel_lock(chan);
+ headp = &chan->varshead;
+ } else {
+ ast_rwlock_wrlock(&globalslock);
+ headp = &globals;
+ }
+
+ if (value) {
+ if (headp == &globals)
+ ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+ else
+ ast_rwlock_unlock(&globalslock);
+}
+
+void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ const char *nametail = name;
+
+ if (name[strlen(name) - 1] == ')') {
+ char *function = ast_strdupa(name);
+
+ ast_func_write(chan, function, value);
+ return;
+ }
+
+ if (chan) {
+ ast_channel_lock(chan);
+ headp = &chan->varshead;
+ } else {
+ ast_rwlock_wrlock(&globalslock);
+ headp = &globals;
+ }
+
+ /* For comparison purposes, we have to strip leading underscores */
+ if (*nametail == '_') {
+ nametail++;
+ if (*nametail == '_')
+ nametail++;
+ }
+
+ AST_LIST_TRAVERSE (headp, newvariable, entries) {
+ if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE(headp, newvariable, entries);
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+
+ if (value) {
+ if (headp == &globals)
+ ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
+ "Channel: %s\r\n"
+ "Variable: %s\r\n"
+ "Value: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan ? chan->name : "none", name, value,
+ chan ? chan->uniqueid : "none");
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+ else
+ ast_rwlock_unlock(&globalslock);
+}
+
+int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ char *name, *value, *mydata;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
+ return 0;
+ }
+
+ mydata = ast_strdupa(data);
+ name = strsep(&mydata, "=");
+ value = mydata;
+ if (strchr(name, ' '))
+ ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
+
+ pbx_builtin_setvar_helper(chan, name, value);
+ return(0);
+}
+
+static int pbx_builtin_setvar_multiple(struct ast_channel *chan, void *vdata)
+{
+ char *data;
+ int x;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(pair)[24];
+ );
+ AST_DECLARE_APP_ARGS(pair,
+ AST_APP_ARG(name);
+ AST_APP_ARG(value);
+ );
+
+ if (ast_strlen_zero(vdata) || !chan) {
+ ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
+ return 0;
+ }
+
+ data = ast_strdupa(vdata);
+ AST_STANDARD_APP_ARGS(args, data);
+
+ for (x = 0; x < args.argc; x++) {
+ AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
+ if (pair.argc == 2) {
+ pbx_builtin_setvar_helper(chan, pair.name, pair.value);
+ if (strchr(pair.name, ' '))
+ ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
+ } else {
+ ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, chan->exten, chan->context, chan->priority);
+ }
+ }
+
+ return 0;
+}
+
+int pbx_builtin_importvar(struct ast_channel *chan, void *data)
+{
+ char *name;
+ char *value;
+ char *channel;
+ char tmp[VAR_BUF_SIZE];
+ static int deprecation_warning = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
+ return 0;
+ }
+ tmp[0] = 0;
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "ImportVar is deprecated. Please use Set(varname=${IMPORT(channel,variable)}) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ value = ast_strdupa(data);
+ name = strsep(&value,"=");
+ channel = strsep(&value,",");
+ if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
+ struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
+ if (chan2) {
+ char *s = alloca(strlen(value) + 4);
+ if (s) {
+ sprintf(s, "${%s}", value);
+ pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
+ }
+ ast_channel_unlock(chan2);
+ }
+ pbx_builtin_setvar_helper(chan, name, tmp);
+ }
+
+ return(0);
+}
+
+static int pbx_builtin_noop(struct ast_channel *chan, void *data)
+{
+ return 0;
+}
+
+void pbx_builtin_clear_globals(void)
+{
+ struct ast_var_t *vardata;
+
+ ast_rwlock_wrlock(&globalslock);
+ while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
+ ast_var_delete(vardata);
+ ast_rwlock_unlock(&globalslock);
+}
+
+int pbx_checkcondition(const char *condition)
+{
+ if (ast_strlen_zero(condition)) /* NULL or empty strings are false */
+ return 0;
+ else if (*condition >= '0' && *condition <= '9') /* Numbers are evaluated for truth */
+ return atoi(condition);
+ else /* Strings are true */
+ return 1;
+}
+
+static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
+{
+ char *condition, *branch1, *branch2, *branch;
+ char *stringp;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
+ return 0;
+ }
+
+ stringp = ast_strdupa(data);
+ condition = strsep(&stringp,"?");
+ branch1 = strsep(&stringp,":");
+ branch2 = strsep(&stringp,"");
+ branch = pbx_checkcondition(condition) ? branch1 : branch2;
+
+ if (ast_strlen_zero(branch)) {
+ ast_debug(1, "Not taking any branch\n");
+ return 0;
+ }
+
+ return pbx_builtin_goto(chan, branch);
+}
+
+static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
+{
+ char tmp[256];
+ char *number = tmp;
+ char *options;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
+ return -1;
+ }
+ ast_copy_string(tmp, data, sizeof(tmp));
+ strsep(&number, ",");
+ options = strsep(&number, ",");
+ if (options) {
+ if ( strcasecmp(options, "f") && strcasecmp(options, "m") &&
+ strcasecmp(options, "c") && strcasecmp(options, "n") ) {
+ ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
+ return -1;
+ }
+ }
+ return ast_say_number(chan, atoi(tmp), "", chan->language, options);
+}
+
+static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_digit_str(chan, data, "", chan->language);
+ return res;
+}
+
+static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_character_str(chan, data, "", chan->language);
+ return res;
+}
+
+static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_phonetic_str(chan, data, "", chan->language);
+ return res;
+}
+
+static void device_state_cb(const struct ast_event *event, void *unused)
+{
+ const char *device;
+
+ device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
+ if (ast_strlen_zero(device)) {
+ ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
+ return;
+ }
+
+ statechange_queue(device);
+}
+
+int load_pbx(void)
+{
+ int x;
+
+ /* Initialize the PBX */
+ ast_verb(1, "Asterisk PBX Core Initializing\n");
+ ast_verb(1, "Registering builtin applications:\n");
+
+ ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
+ __ast_custom_function_register(&exception_function, NULL);
+
+ /* Register builtin applications */
+ for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
+ ast_verb(1, "[%s]\n", builtins[x].name);
+ if (ast_register_application2(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description, NULL)) {
+ ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
+ return -1;
+ }
+ }
+
+ /* Register manager application */
+ ast_manager_register2("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan, "List dialplan", mandescr_show_dialplan);
+
+ ast_mutex_init(&device_state.lock);
+ ast_cond_init(&device_state.cond, NULL);
+ ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
+
+ if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL,
+ AST_EVENT_IE_END))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Lock context list functions ...
+ */
+int ast_wrlock_contexts()
+{
+ return ast_rwlock_wrlock(&conlock);
+}
+
+int ast_rdlock_contexts()
+{
+ return ast_rwlock_rdlock(&conlock);
+}
+
+int ast_unlock_contexts()
+{
+ return ast_rwlock_unlock(&conlock);
+}
+
+/*
+ * Lock context ...
+ */
+int ast_wrlock_context(struct ast_context *con)
+{
+ return ast_rwlock_wrlock(&con->lock);
+}
+
+int ast_rdlock_context(struct ast_context *con)
+{
+ return ast_rwlock_rdlock(&con->lock);
+}
+
+int ast_unlock_context(struct ast_context *con)
+{
+ return ast_rwlock_unlock(&con->lock);
+}
+
+/*
+ * Name functions ...
+ */
+const char *ast_get_context_name(struct ast_context *con)
+{
+ return con ? con->name : NULL;
+}
+
+struct ast_context *ast_get_extension_context(struct ast_exten *exten)
+{
+ return exten ? exten->parent : NULL;
+}
+
+const char *ast_get_extension_name(struct ast_exten *exten)
+{
+ return exten ? exten->exten : NULL;
+}
+
+const char *ast_get_extension_label(struct ast_exten *exten)
+{
+ return exten ? exten->label : NULL;
+}
+
+const char *ast_get_include_name(struct ast_include *inc)
+{
+ return inc ? inc->name : NULL;
+}
+
+const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
+{
+ return ip ? ip->pattern : NULL;
+}
+
+int ast_get_extension_priority(struct ast_exten *exten)
+{
+ return exten ? exten->priority : -1;
+}
+
+/*
+ * Registrar info functions ...
+ */
+const char *ast_get_context_registrar(struct ast_context *c)
+{
+ return c ? c->registrar : NULL;
+}
+
+const char *ast_get_extension_registrar(struct ast_exten *e)
+{
+ return e ? e->registrar : NULL;
+}
+
+const char *ast_get_include_registrar(struct ast_include *i)
+{
+ return i ? i->registrar : NULL;
+}
+
+const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
+{
+ return ip ? ip->registrar : NULL;
+}
+
+int ast_get_extension_matchcid(struct ast_exten *e)
+{
+ return e ? e->matchcid : 0;
+}
+
+const char *ast_get_extension_cidmatch(struct ast_exten *e)
+{
+ return e ? e->cidmatch : NULL;
+}
+
+const char *ast_get_extension_app(struct ast_exten *e)
+{
+ return e ? e->app : NULL;
+}
+
+void *ast_get_extension_app_data(struct ast_exten *e)
+{
+ return e ? e->data : NULL;
+}
+
+const char *ast_get_switch_name(struct ast_sw *sw)
+{
+ return sw ? sw->name : NULL;
+}
+
+const char *ast_get_switch_data(struct ast_sw *sw)
+{
+ return sw ? sw->data : NULL;
+}
+
+const char *ast_get_switch_registrar(struct ast_sw *sw)
+{
+ return sw ? sw->registrar : NULL;
+}
+
+/*
+ * Walking functions ...
+ */
+struct ast_context *ast_walk_contexts(struct ast_context *con)
+{
+ return con ? con->next : contexts;
+}
+
+struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten)
+{
+ if (!exten)
+ return con ? con->root : NULL;
+ else
+ return exten->next;
+}
+
+struct ast_sw *ast_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw)
+{
+ if (!sw)
+ return con ? AST_LIST_FIRST(&con->alts) : NULL;
+ else
+ return AST_LIST_NEXT(sw, list);
+}
+
+struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority)
+{
+ return priority ? priority->peer : exten;
+}
+
+struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc)
+{
+ if (!inc)
+ return con ? con->includes : NULL;
+ else
+ return inc->next;
+}
+
+struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
+ struct ast_ignorepat *ip)
+{
+ if (!ip)
+ return con ? con->ignorepats : NULL;
+ else
+ return ip->next;
+}
+
+int ast_context_verify_includes(struct ast_context *con)
+{
+ struct ast_include *inc = NULL;
+ int res = 0;
+
+ while ( (inc = ast_walk_context_includes(con, inc)) )
+ if (!ast_context_find(inc->rname)) {
+ res = -1;
+ ast_log(LOG_WARNING, "Context '%s' tries to include nonexistent context '%s'\n",
+ ast_get_context_name(con), inc->rname);
+ }
+ return res;
+}
+
+
+static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
+{
+ int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+ if (!chan)
+ return -2;
+
+ if (context == NULL)
+ context = chan->context;
+ if (exten == NULL)
+ exten = chan->exten;
+
+ goto_func = (async) ? ast_async_goto : ast_explicit_goto;
+ if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
+ return goto_func(chan, context, exten, priority);
+ else
+ return -3;
+}
+
+int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
+{
+ return __ast_goto_if_exists(chan, context, exten, priority, 0);
+}
+
+int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
+{
+ return __ast_goto_if_exists(chan, context, exten, priority, 1);
+}
+
+int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
+{
+ char *exten, *pri, *context;
+ char *stringp;
+ int ipri;
+ int mode = 0;
+
+ if (ast_strlen_zero(goto_string)) {
+ ast_log(LOG_WARNING, "Goto requires an argument ([[context,]extension,]priority)\n");
+ return -1;
+ }
+ stringp = ast_strdupa(goto_string);
+ context = strsep(&stringp, ","); /* guaranteed non-null */
+ exten = strsep(&stringp, ",");
+ pri = strsep(&stringp, ",");
+ if (!exten) { /* Only a priority in this one */
+ pri = context;
+ exten = NULL;
+ context = NULL;
+ } else if (!pri) { /* Only an extension and priority in this one */
+ pri = exten;
+ exten = context;
+ context = NULL;
+ }
+ if (*pri == '+') {
+ mode = 1;
+ pri++;
+ } else if (*pri == '-') {
+ mode = -1;
+ pri++;
+ }
+ if (sscanf(pri, "%d", &ipri) != 1) {
+ if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
+ pri, chan->cid.cid_num)) < 1) {
+ ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
+ return -1;
+ } else
+ mode = 0;
+ }
+ /* At this point we have a priority and maybe an extension and a context */
+
+ if (mode)
+ ipri = chan->priority + (ipri * mode);
+
+ ast_explicit_goto(chan, context, exten, ipri);
+ ast_cdr_update(chan);
+ return 0;
+
+}
diff --git a/trunk/main/plc.c b/trunk/main/plc.c
new file mode 100644
index 000000000..ef549ca2c
--- /dev/null
+++ b/trunk/main/plc.c
@@ -0,0 +1,248 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2004 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * This version may be optionally licenced under the GNU LGPL licence.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ *
+ * \brief SpanDSP - a series of DSP components for telephony
+ *
+ * \author Steve Underwood <steveu@coppice.org>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/plc.h"
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+#if !defined(INT16_MAX)
+#define INT16_MAX (32767)
+#define INT16_MIN (-32767-1)
+#endif
+
+/* We do a straight line fade to zero volume in 50ms when we are filling in for missing data. */
+#define ATTENUATION_INCREMENT 0.0025 /* Attenuation per sample */
+
+#define ms_to_samples(t) (((t)*DEFAULT_SAMPLE_RATE)/1000)
+
+static inline int16_t fsaturate(double damp)
+{
+ if (damp > 32767.0)
+ return INT16_MAX;
+ if (damp < -32768.0)
+ return INT16_MIN;
+ return (int16_t) rint(damp);
+}
+
+static void save_history(plc_state_t *s, int16_t *buf, int len)
+{
+ if (len >= PLC_HISTORY_LEN) {
+ /* Just keep the last part of the new data, starting at the beginning of the buffer */
+ memcpy(s->history, buf + len - PLC_HISTORY_LEN, sizeof(int16_t) * PLC_HISTORY_LEN);
+ s->buf_ptr = 0;
+ return;
+ }
+ if (s->buf_ptr + len > PLC_HISTORY_LEN) {
+ /* Wraps around - must break into two sections */
+ memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t) * (PLC_HISTORY_LEN - s->buf_ptr));
+ len -= (PLC_HISTORY_LEN - s->buf_ptr);
+ memcpy(s->history, buf + (PLC_HISTORY_LEN - s->buf_ptr), sizeof(int16_t)*len);
+ s->buf_ptr = len;
+ return;
+ }
+ /* Can use just one section */
+ memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t)*len);
+ s->buf_ptr += len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static void normalise_history(plc_state_t *s)
+{
+ int16_t tmp[PLC_HISTORY_LEN];
+
+ if (s->buf_ptr == 0)
+ return;
+ memcpy(tmp, s->history, sizeof(int16_t)*s->buf_ptr);
+ memcpy(s->history, s->history + s->buf_ptr, sizeof(int16_t) * (PLC_HISTORY_LEN - s->buf_ptr));
+ memcpy(s->history + PLC_HISTORY_LEN - s->buf_ptr, tmp, sizeof(int16_t) * s->buf_ptr);
+ s->buf_ptr = 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static int __inline__ amdf_pitch(int min_pitch, int max_pitch, int16_t amp[], int len)
+{
+ int i;
+ int j;
+ int acc;
+ int min_acc;
+ int pitch;
+
+ pitch = min_pitch;
+ min_acc = INT_MAX;
+ for (i = max_pitch; i <= min_pitch; i++) {
+ acc = 0;
+ for (j = 0; j < len; j++)
+ acc += abs(amp[i + j] - amp[j]);
+ if (acc < min_acc) {
+ min_acc = acc;
+ pitch = i;
+ }
+ }
+ return pitch;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int plc_rx(plc_state_t *s, int16_t amp[], int len)
+{
+ int i;
+ int pitch_overlap;
+ float old_step;
+ float new_step;
+ float old_weight;
+ float new_weight;
+ float gain;
+
+ if (s->missing_samples) {
+ /* Although we have a real signal, we need to smooth it to fit well
+ with the synthetic signal we used for the previous block */
+
+ /* The start of the real data is overlapped with the next 1/4 cycle
+ of the synthetic data. */
+ pitch_overlap = s->pitch >> 2;
+ if (pitch_overlap > len)
+ pitch_overlap = len;
+ gain = 1.0 - s->missing_samples*ATTENUATION_INCREMENT;
+ if (gain < 0.0)
+ gain = 0.0;
+ new_step = 1.0/pitch_overlap;
+ old_step = new_step*gain;
+ new_weight = new_step;
+ old_weight = (1.0 - new_step)*gain;
+ for (i = 0; i < pitch_overlap; i++) {
+ amp[i] = fsaturate(old_weight * s->pitchbuf[s->pitch_offset] + new_weight * amp[i]);
+ if (++s->pitch_offset >= s->pitch)
+ s->pitch_offset = 0;
+ new_weight += new_step;
+ old_weight -= old_step;
+ if (old_weight < 0.0)
+ old_weight = 0.0;
+ }
+ s->missing_samples = 0;
+ }
+ save_history(s, amp, len);
+ return len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int plc_fillin(plc_state_t *s, int16_t amp[], int len)
+{
+ int i;
+ int pitch_overlap;
+ float old_step;
+ float new_step;
+ float old_weight;
+ float new_weight;
+ float gain;
+ int16_t *orig_amp;
+ int orig_len;
+
+ orig_amp = amp;
+ orig_len = len;
+ if (s->missing_samples == 0) {
+ /* As the gap in real speech starts we need to assess the last known pitch,
+ and prepare the synthetic data we will use for fill-in */
+ normalise_history(s);
+ s->pitch = amdf_pitch(PLC_PITCH_MIN, PLC_PITCH_MAX, s->history + PLC_HISTORY_LEN - CORRELATION_SPAN - PLC_PITCH_MIN, CORRELATION_SPAN);
+ /* We overlap a 1/4 wavelength */
+ pitch_overlap = s->pitch >> 2;
+ /* Cook up a single cycle of pitch, using a single of the real signal with 1/4
+ cycle OLA'ed to make the ends join up nicely */
+ /* The first 3/4 of the cycle is a simple copy */
+ for (i = 0; i < s->pitch - pitch_overlap; i++)
+ s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i];
+ /* The last 1/4 of the cycle is overlapped with the end of the previous cycle */
+ new_step = 1.0/pitch_overlap;
+ new_weight = new_step;
+ for ( ; i < s->pitch; i++) {
+ s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i] * (1.0 - new_weight) + s->history[PLC_HISTORY_LEN - 2 * s->pitch + i]*new_weight;
+ new_weight += new_step;
+ }
+ /* We should now be ready to fill in the gap with repeated, decaying cycles
+ of what is in pitchbuf */
+
+ /* We need to OLA the first 1/4 wavelength of the synthetic data, to smooth
+ it into the previous real data. To avoid the need to introduce a delay
+ in the stream, reverse the last 1/4 wavelength, and OLA with that. */
+ gain = 1.0;
+ new_step = 1.0 / pitch_overlap;
+ old_step = new_step;
+ new_weight = new_step;
+ old_weight = 1.0 - new_step;
+ for (i = 0; i < pitch_overlap; i++) {
+ amp[i] = fsaturate(old_weight * s->history[PLC_HISTORY_LEN - 1 - i] + new_weight * s->pitchbuf[i]);
+ new_weight += new_step;
+ old_weight -= old_step;
+ if (old_weight < 0.0)
+ old_weight = 0.0;
+ }
+ s->pitch_offset = i;
+ } else {
+ gain = 1.0 - s->missing_samples*ATTENUATION_INCREMENT;
+ i = 0;
+ }
+ for ( ; gain > 0.0 && i < len; i++) {
+ amp[i] = s->pitchbuf[s->pitch_offset] * gain;
+ gain -= ATTENUATION_INCREMENT;
+ if (++s->pitch_offset >= s->pitch)
+ s->pitch_offset = 0;
+ }
+ for ( ; i < len; i++)
+ amp[i] = 0;
+ s->missing_samples += orig_len;
+ save_history(s, amp, len);
+ return len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+plc_state_t *plc_init(plc_state_t *s)
+{
+ memset(s, 0, sizeof(*s));
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/trunk/main/poll.c b/trunk/main/poll.c
new file mode 100644
index 000000000..bd283866d
--- /dev/null
+++ b/trunk/main/poll.c
@@ -0,0 +1,306 @@
+/*---------------------------------------------------------------------------*\
+ $Id$
+
+ NAME
+
+ poll - select(2)-based poll() emulation function for BSD systems.
+
+ SYNOPSIS
+ #include "poll.h"
+
+ struct pollfd
+ {
+ int fd;
+ short events;
+ short revents;
+ }
+
+ int poll (struct pollfd *pArray, unsigned long n_fds, int timeout)
+
+ DESCRIPTION
+
+ This file, and the accompanying "poll.h", implement the System V
+ poll(2) system call for BSD systems (which typically do not provide
+ poll()). Poll() provides a method for multiplexing input and output
+ on multiple open file descriptors; in traditional BSD systems, that
+ capability is provided by select(). While the semantics of select()
+ differ from those of poll(), poll() can be readily emulated in terms
+ of select() -- which is how this function is implemented.
+
+ REFERENCES
+ Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990.
+
+ NOTES
+ 1. This software requires an ANSI C compiler.
+
+ LICENSE
+
+ This software is released under the following license:
+
+ Copyright (c) 1995-2002 Brian M. Clapper
+ All rights reserved.
+
+ Redistribution and use in source and binary forms are
+ permitted provided that: (1) source distributions retain
+ this entire copyright notice and comment; (2) modifications
+ made to the software are prominently mentioned, and a copy
+ of the original software (or a pointer to its location) are
+ included; and (3) distributions including binaries display
+ the following acknowledgement: "This product includes
+ software developed by Brian M. Clapper <bmc@clapper.org>"
+ in the documentation or other materials provided with the
+ distribution. The name of the author may not be used to
+ endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE.
+
+ Effectively, this means you can do what you want with the software
+ except remove this notice or take advantage of the author's name.
+ If you modify the software and redistribute your modified version,
+ you must indicate that your version is a modification of the
+ original, and you must provide either a pointer to or a copy of the
+ original.
+\*---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------*\
+ Includes
+\*---------------------------------------------------------------------------*/
+
+#include <unistd.h> /* standard Unix definitions */
+#include <sys/types.h> /* system types */
+#include <sys/time.h> /* time definitions */
+#include <assert.h> /* assertion macros */
+#include <string.h> /* string functions */
+
+#include "asterisk/poll-compat.h" /* this package */
+
+/*---------------------------------------------------------------------------*\
+ Macros
+\*---------------------------------------------------------------------------*/
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+/*---------------------------------------------------------------------------*\
+ Private Functions
+\*---------------------------------------------------------------------------*/
+
+static int map_poll_spec
+#if __STDC__ > 0
+ (struct pollfd *pArray,
+ unsigned long n_fds,
+ fd_set *pReadSet,
+ fd_set *pWriteSet,
+ fd_set *pExceptSet)
+#else
+ (pArray, n_fds, pReadSet, pWriteSet, pExceptSet)
+ struct pollfd *pArray;
+ unsigned long n_fds;
+ fd_set *pReadSet;
+ fd_set *pWriteSet;
+ fd_set *pExceptSet;
+#endif
+{
+ register unsigned long i; /* loop control */
+ register struct pollfd *pCur; /* current array element */
+ register int max_fd = -1; /* return value */
+
+ /*
+ Map the poll() structures into the file descriptor sets required
+ by select().
+ */
+ for (i = 0, pCur = pArray; i < n_fds; i++, pCur++)
+ {
+ /* Skip any bad FDs in the array. */
+
+ if (pCur->fd < 0)
+ continue;
+
+ if (pCur->events & POLLIN)
+ {
+ /* "Input Ready" notification desired. */
+ FD_SET (pCur->fd, pReadSet);
+ }
+
+ if (pCur->events & POLLOUT)
+ {
+ /* "Output Possible" notification desired. */
+ FD_SET (pCur->fd, pWriteSet);
+ }
+
+ if (pCur->events & POLLPRI)
+ {
+ /*
+ "Exception Occurred" notification desired. (Exceptions
+ include out of band data.
+ */
+ FD_SET (pCur->fd, pExceptSet);
+ }
+
+ max_fd = MAX (max_fd, pCur->fd);
+ }
+
+ return max_fd;
+}
+
+static struct timeval *map_timeout
+#if __STDC__ > 0
+ (int poll_timeout, struct timeval *pSelTimeout)
+#else
+ (poll_timeout, pSelTimeout)
+ int poll_timeout;
+ struct timeval *pSelTimeout;
+#endif
+{
+ struct timeval *pResult;
+
+ /*
+ Map the poll() timeout value into a select() timeout. The possible
+ values of the poll() timeout value, and their meanings, are:
+
+ VALUE MEANING
+
+ -1 wait indefinitely (until signal occurs)
+ 0 return immediately, don't block
+ >0 wait specified number of milliseconds
+
+ select() uses a "struct timeval", which specifies the timeout in
+ seconds and microseconds, so the milliseconds value has to be mapped
+ accordingly.
+ */
+
+ assert (pSelTimeout != (struct timeval *) NULL);
+
+ switch (poll_timeout)
+ {
+ case -1:
+ /*
+ A NULL timeout structure tells select() to wait indefinitely.
+ */
+ pResult = (struct timeval *) NULL;
+ break;
+
+ case 0:
+ /*
+ "Return immediately" (test) is specified by all zeros in
+ a timeval structure.
+ */
+ pSelTimeout->tv_sec = 0;
+ pSelTimeout->tv_usec = 0;
+ pResult = pSelTimeout;
+ break;
+
+ default:
+ /* Wait the specified number of milliseconds. */
+ pSelTimeout->tv_sec = poll_timeout / 1000; /* get seconds */
+ poll_timeout %= 1000; /* remove seconds */
+ pSelTimeout->tv_usec = poll_timeout * 1000; /* get microseconds */
+ pResult = pSelTimeout;
+ break;
+ }
+
+
+ return pResult;
+}
+
+static void map_select_results
+#if __STDC__ > 0
+ (struct pollfd *pArray,
+ unsigned long n_fds,
+ fd_set *pReadSet,
+ fd_set *pWriteSet,
+ fd_set *pExceptSet)
+#else
+ (pArray, n_fds, pReadSet, pWriteSet, pExceptSet)
+ struct pollfd *pArray;
+ unsigned long n_fds;
+ fd_set *pReadSet;
+ fd_set *pWriteSet;
+ fd_set *pExceptSet;
+#endif
+{
+ register unsigned long i; /* loop control */
+ register struct pollfd *pCur; /* current array element */
+
+ for (i = 0, pCur = pArray; i < n_fds; i++, pCur++)
+ {
+ /* Skip any bad FDs in the array. */
+
+ if (pCur->fd < 0)
+ continue;
+
+ /* Exception events take priority over input events. */
+
+ pCur->revents = 0;
+ if (FD_ISSET (pCur->fd, pExceptSet))
+ pCur->revents |= POLLPRI;
+
+ else if (FD_ISSET (pCur->fd, pReadSet))
+ pCur->revents |= POLLIN;
+
+ if (FD_ISSET (pCur->fd, pWriteSet))
+ pCur->revents |= POLLOUT;
+ }
+
+ return;
+}
+
+/*---------------------------------------------------------------------------*\
+ Public Functions
+\*---------------------------------------------------------------------------*/
+
+int poll
+
+#if __STDC__ > 0
+ (struct pollfd *pArray, unsigned long n_fds, int timeout)
+#else
+ (pArray, n_fds, timeout)
+ struct pollfd *pArray;
+ unsigned long n_fds;
+ int timeout;
+#endif
+
+{
+ fd_set read_descs; /* input file descs */
+ fd_set write_descs; /* output file descs */
+ fd_set except_descs; /* exception descs */
+ struct timeval stime; /* select() timeout value */
+ int ready_descriptors; /* function result */
+ int max_fd; /* maximum fd value */
+ struct timeval *pTimeout; /* actually passed */
+
+ FD_ZERO (&read_descs);
+ FD_ZERO (&write_descs);
+ FD_ZERO (&except_descs);
+
+ assert (pArray != (struct pollfd *) NULL);
+
+ /* Map the poll() file descriptor list in the select() data structures. */
+
+ max_fd = map_poll_spec (pArray, n_fds,
+ &read_descs, &write_descs, &except_descs);
+
+ /* Map the poll() timeout value in the select() timeout structure. */
+
+ pTimeout = map_timeout (timeout, &stime);
+
+ /* Make the select() call. */
+
+ ready_descriptors = select (max_fd + 1, &read_descs, &write_descs,
+ &except_descs, pTimeout);
+
+ if (ready_descriptors >= 0)
+ {
+ map_select_results (pArray, n_fds,
+ &read_descs, &write_descs, &except_descs);
+ }
+
+ return ready_descriptors;
+}
diff --git a/trunk/main/privacy.c b/trunk/main/privacy.c
new file mode 100644
index 000000000..dfb197f38
--- /dev/null
+++ b/trunk/main/privacy.c
@@ -0,0 +1,112 @@
+/*
+ * 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 Privacy Routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+#include <dirent.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/astdb.h"
+#include "asterisk/callerid.h"
+#include "asterisk/privacy.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+int ast_privacy_check(char *dest, char *cid)
+{
+ char tmp[256] = "";
+ char *trimcid = "";
+ char *n, *l;
+ int res;
+ char key[256], result[256];
+ if (cid)
+ ast_copy_string(tmp, cid, sizeof(tmp));
+ ast_callerid_parse(tmp, &n, &l);
+ if (l) {
+ ast_shrink_phone_number(l);
+ trimcid = l;
+ }
+ snprintf(key, sizeof(key), "%s/%s", dest, trimcid);
+ res = ast_db_get("privacy", key, result, sizeof(result));
+ if (!res) {
+ if (!strcasecmp(result, "allow"))
+ return AST_PRIVACY_ALLOW;
+ if (!strcasecmp(result, "deny"))
+ return AST_PRIVACY_DENY;
+ if (!strcasecmp(result, "kill"))
+ return AST_PRIVACY_KILL;
+ if (!strcasecmp(result, "torture"))
+ return AST_PRIVACY_TORTURE;
+ }
+ return AST_PRIVACY_UNKNOWN;
+}
+
+int ast_privacy_reset(char *dest)
+{
+ if (!dest)
+ return -1;
+ return ast_db_deltree("privacy", dest);
+}
+
+int ast_privacy_set(char *dest, char *cid, int status)
+{
+ char tmp[256] = "";
+ char *trimcid = "";
+ char *n, *l;
+ int res;
+ char key[256];
+ if (cid)
+ ast_copy_string(tmp, cid, sizeof(tmp));
+ ast_callerid_parse(tmp, &n, &l);
+ if (l) {
+ ast_shrink_phone_number(l);
+ trimcid = l;
+ }
+ if (ast_strlen_zero(trimcid)) {
+ /* Don't store anything for empty Caller*ID */
+ return 0;
+ }
+ snprintf(key, sizeof(key), "%s/%s", dest, trimcid);
+ if (status == AST_PRIVACY_UNKNOWN)
+ res = ast_db_del("privacy", key);
+ else if (status == AST_PRIVACY_ALLOW)
+ res = ast_db_put("privacy", key, "allow");
+ else if (status == AST_PRIVACY_DENY)
+ res = ast_db_put("privacy", key, "deny");
+ else if (status == AST_PRIVACY_KILL)
+ res = ast_db_put("privacy", key, "kill");
+ else if (status == AST_PRIVACY_TORTURE)
+ res = ast_db_put("privacy", key, "torture");
+ else
+ res = -1;
+ return res;
+}
diff --git a/trunk/main/rtp.c b/trunk/main/rtp.c
new file mode 100644
index 000000000..874f8ddb2
--- /dev/null
+++ b/trunk/main/rtp.c
@@ -0,0 +1,4093 @@
+/*
+ * 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 Supports RTP and RTCP with Symmetric RTP support for NAT traversal.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note RTP is defined in RFC 3550.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "asterisk/rtp.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/acl.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/netsock.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/unaligned.h"
+
+#define MAX_TIMESTAMP_SKEW 640
+
+#define RTP_SEQ_MOD (1<<16) /*!< A sequence number can't be more than 16 bits */
+#define RTCP_DEFAULT_INTERVALMS 5000 /*!< Default milli-seconds between RTCP reports we send */
+#define RTCP_MIN_INTERVALMS 500 /*!< Min milli-seconds between RTCP reports we send */
+#define RTCP_MAX_INTERVALMS 60000 /*!< Max milli-seconds between RTCP reports we send */
+
+#define RTCP_PT_FUR 192
+#define RTCP_PT_SR 200
+#define RTCP_PT_RR 201
+#define RTCP_PT_SDES 202
+#define RTCP_PT_BYE 203
+#define RTCP_PT_APP 204
+
+#define RTP_MTU 1200
+
+#define DEFAULT_DTMF_TIMEOUT 3000 /*!< samples */
+
+static int dtmftimeout = DEFAULT_DTMF_TIMEOUT;
+
+static int rtpstart; /*!< First port for RTP sessions (set in rtp.conf) */
+static int rtpend; /*!< Last port for RTP sessions (set in rtp.conf) */
+static int rtpdebug; /*!< Are we debugging? */
+static int rtcpdebug; /*!< Are we debugging RTCP? */
+static int rtcpstats; /*!< Are we debugging RTCP? */
+static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */
+static int stundebug; /*!< Are we debugging stun? */
+static struct sockaddr_in rtpdebugaddr; /*!< Debug packets to/from this host */
+static struct sockaddr_in rtcpdebugaddr; /*!< Debug RTCP packets to/from this host */
+#ifdef SO_NO_CHECK
+static int nochecksums;
+#endif
+
+/* Uncomment this to enable more intense native bridging, but note: this is currently buggy */
+/* #define P2P_INTENSE */
+
+/*!
+ * \brief Structure representing a RTP session.
+ *
+ * RTP session is defined on page 9 of RFC 3550: "An association among a set of participants communicating with RTP. A participant may be involved in multiple RTP sessions at the same time [...]"
+ *
+ */
+/*! \brief The value of each payload format mapping: */
+struct rtpPayloadType {
+ int isAstFormat; /*!< whether the following code is an AST_FORMAT */
+ int code;
+};
+
+
+/*! \brief RTP session description */
+struct ast_rtp {
+ int s;
+ struct ast_frame f;
+ unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */
+ unsigned int themssrc; /*!< Their SSRC */
+ unsigned int rxssrc;
+ unsigned int lastts;
+ unsigned int lastrxts;
+ unsigned int lastividtimestamp;
+ unsigned int lastovidtimestamp;
+ unsigned int lastitexttimestamp;
+ unsigned int lastotexttimestamp;
+ unsigned int lasteventseqn;
+ int lastrxseqno; /*!< Last received sequence number */
+ unsigned short seedrxseqno; /*!< What sequence number did they start with?*/
+ unsigned int seedrxts; /*!< What RTP timestamp did they start with? */
+ unsigned int rxcount; /*!< How many packets have we received? */
+ unsigned int rxoctetcount; /*!< How many octets have we received? should be rxcount *160*/
+ unsigned int txcount; /*!< How many packets have we sent? */
+ unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/
+ unsigned int cycles; /*!< Shifted count of sequence number cycles */
+ double rxjitter; /*!< Interarrival jitter at the moment */
+ double rxtransit; /*!< Relative transit time for previous packet */
+ int lasttxformat;
+ int lastrxformat;
+
+ int rtptimeout; /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */
+ int rtpholdtimeout; /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */
+ int rtpkeepalive; /*!< Send RTP comfort noice packets for keepalive */
+
+ /* DTMF Reception Variables */
+ char resp;
+ unsigned int lastevent;
+ int dtmfcount;
+ unsigned int dtmfsamples;
+ /* DTMF Transmission Variables */
+ unsigned int lastdigitts;
+ char sending_digit; /*!< boolean - are we sending digits */
+ char send_digit; /*!< digit we are sending */
+ int send_payload;
+ int send_duration;
+ int nat;
+ unsigned int flags;
+ struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
+ struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
+ struct timeval rxcore;
+ struct timeval txcore;
+ double drxcore; /*!< The double representation of the first received packet */
+ struct timeval lastrx; /*!< timeval when we last received a packet */
+ struct timeval dtmfmute;
+ struct ast_smoother *smoother;
+ int *ioid;
+ unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */
+ unsigned short rxseqno;
+ struct sched_context *sched;
+ struct io_context *io;
+ void *data;
+ ast_rtp_callback callback;
+#ifdef P2P_INTENSE
+ ast_mutex_t bridge_lock;
+#endif
+ struct rtpPayloadType current_RTP_PT[MAX_RTP_PT];
+ int rtp_lookup_code_cache_isAstFormat; /*!< a cache for the result of rtp_lookup_code(): */
+ int rtp_lookup_code_cache_code;
+ int rtp_lookup_code_cache_result;
+ struct ast_rtcp *rtcp;
+ struct ast_codec_pref pref;
+ struct ast_rtp *bridged; /*!< Who we are Packet bridged to */
+};
+
+/* Forward declarations */
+static int ast_rtcp_write(const void *data);
+static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw);
+static int ast_rtcp_write_sr(const void *data);
+static int ast_rtcp_write_rr(const void *data);
+static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp);
+static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp);
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit);
+
+#define FLAG_3389_WARNING (1 << 0)
+#define FLAG_NAT_ACTIVE (3 << 1)
+#define FLAG_NAT_INACTIVE (0 << 1)
+#define FLAG_NAT_INACTIVE_NOWARN (1 << 1)
+#define FLAG_HAS_DTMF (1 << 3)
+#define FLAG_P2P_SENT_MARK (1 << 4)
+#define FLAG_P2P_NEED_DTMF (1 << 5)
+#define FLAG_CALLBACK_MODE (1 << 6)
+#define FLAG_DTMF_COMPENSATE (1 << 7)
+#define FLAG_HAS_STUN (1 << 8)
+
+/*!
+ * \brief Structure defining an RTCP session.
+ *
+ * The concept "RTCP session" is not defined in RFC 3550, but since
+ * this structure is analogous to ast_rtp, which tracks a RTP session,
+ * it is logical to think of this as a RTCP session.
+ *
+ * RTCP packet is defined on page 9 of RFC 3550.
+ *
+ */
+struct ast_rtcp {
+ int s; /*!< Socket */
+ struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
+ struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
+ unsigned int soc; /*!< What they told us */
+ unsigned int spc; /*!< What they told us */
+ unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/
+ struct timeval rxlsr; /*!< Time when we got their last SR */
+ struct timeval txlsr; /*!< Time when we sent or last SR*/
+ unsigned int expected_prior; /*!< no. packets in previous interval */
+ unsigned int received_prior; /*!< no. packets received in previous interval */
+ int schedid; /*!< Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/
+ unsigned int rr_count; /*!< number of RRs we've sent, not including report blocks in SR's */
+ unsigned int sr_count; /*!< number of SRs we've sent */
+ unsigned int lastsrtxcount; /*!< Transmit packet count when last SR sent */
+ double accumulated_transit; /*!< accumulated a-dlsr-lsr */
+ double rtt; /*!< Last reported rtt */
+ unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */
+ unsigned int reported_lost; /*!< Reported lost packets in their RR */
+ char quality[AST_MAX_USER_FIELD];
+ double maxrxjitter;
+ double minrxjitter;
+ double maxrtt;
+ double minrtt;
+ int sendfur;
+};
+
+/*!
+ * \brief STUN support code
+ *
+ * This code provides some support for doing STUN transactions.
+ * Eventually it should be moved elsewhere as other protocols
+ * than RTP can benefit from it - e.g. SIP.
+ * STUN is described in RFC3489 and it is based on the exchange
+ * of UDP packets between a client and one or more servers to
+ * determine the externally visible address (and port) of the client
+ * once it has gone through the NAT boxes that connect it to the
+ * outside.
+ * The simplest request packet is just the header defined in
+ * struct stun_header, and from the response we may just look at
+ * one attribute, STUN_MAPPED_ADDRESS, that we find in the response.
+ * By doing more transactions with different server addresses we
+ * may determine more about the behaviour of the NAT boxes, of
+ * course - the details are in the RFC.
+ *
+ * All STUN packets start with a simple header made of a type,
+ * length (excluding the header) and a 16-byte random transaction id.
+ * Following the header we may have zero or more attributes, each
+ * structured as a type, length and a value (whose format depends
+ * on the type, but often contains addresses).
+ * Of course all fields are in network format.
+ */
+
+typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id;
+
+struct stun_header {
+ unsigned short msgtype;
+ unsigned short msglen;
+ stun_trans_id id;
+ unsigned char ies[0];
+} __attribute__((packed));
+
+struct stun_attr {
+ unsigned short attr;
+ unsigned short len;
+ unsigned char value[0];
+} __attribute__((packed));
+
+/*
+ * The format normally used for addresses carried by STUN messages.
+ */
+struct stun_addr {
+ unsigned char unused;
+ unsigned char family;
+ unsigned short port;
+ unsigned int addr;
+} __attribute__((packed));
+
+#define STUN_IGNORE (0)
+#define STUN_ACCEPT (1)
+
+/*! \brief STUN message types
+ * 'BIND' refers to transactions used to determine the externally
+ * visible addresses. 'SEC' refers to transactions used to establish
+ * a session key for subsequent requests.
+ * 'SEC' functionality is not supported here.
+ */
+
+#define STUN_BINDREQ 0x0001
+#define STUN_BINDRESP 0x0101
+#define STUN_BINDERR 0x0111
+#define STUN_SECREQ 0x0002
+#define STUN_SECRESP 0x0102
+#define STUN_SECERR 0x0112
+
+/*! \brief Basic attribute types in stun messages.
+ * Messages can also contain custom attributes (codes above 0x7fff)
+ */
+#define STUN_MAPPED_ADDRESS 0x0001
+#define STUN_RESPONSE_ADDRESS 0x0002
+#define STUN_CHANGE_REQUEST 0x0003
+#define STUN_SOURCE_ADDRESS 0x0004
+#define STUN_CHANGED_ADDRESS 0x0005
+#define STUN_USERNAME 0x0006
+#define STUN_PASSWORD 0x0007
+#define STUN_MESSAGE_INTEGRITY 0x0008
+#define STUN_ERROR_CODE 0x0009
+#define STUN_UNKNOWN_ATTRIBUTES 0x000a
+#define STUN_REFLECTED_FROM 0x000b
+
+/*! \brief helper function to print message names */
+static const char *stun_msg2str(int msg)
+{
+ switch (msg) {
+ case STUN_BINDREQ:
+ return "Binding Request";
+ case STUN_BINDRESP:
+ return "Binding Response";
+ case STUN_BINDERR:
+ return "Binding Error Response";
+ case STUN_SECREQ:
+ return "Shared Secret Request";
+ case STUN_SECRESP:
+ return "Shared Secret Response";
+ case STUN_SECERR:
+ return "Shared Secret Error Response";
+ }
+ return "Non-RFC3489 Message";
+}
+
+/*! \brief helper function to print attribute names */
+static const char *stun_attr2str(int msg)
+{
+ switch (msg) {
+ case STUN_MAPPED_ADDRESS:
+ return "Mapped Address";
+ case STUN_RESPONSE_ADDRESS:
+ return "Response Address";
+ case STUN_CHANGE_REQUEST:
+ return "Change Request";
+ case STUN_SOURCE_ADDRESS:
+ return "Source Address";
+ case STUN_CHANGED_ADDRESS:
+ return "Changed Address";
+ case STUN_USERNAME:
+ return "Username";
+ case STUN_PASSWORD:
+ return "Password";
+ case STUN_MESSAGE_INTEGRITY:
+ return "Message Integrity";
+ case STUN_ERROR_CODE:
+ return "Error Code";
+ case STUN_UNKNOWN_ATTRIBUTES:
+ return "Unknown Attributes";
+ case STUN_REFLECTED_FROM:
+ return "Reflected From";
+ }
+ return "Non-RFC3489 Attribute";
+}
+
+/*! \brief here we store credentials extracted from a message */
+struct stun_state {
+ const char *username;
+ const char *password;
+};
+
+static int stun_process_attr(struct stun_state *state, struct stun_attr *attr)
+{
+ if (stundebug)
+ ast_verbose("Found STUN Attribute %s (%04x), length %d\n",
+ stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
+ switch (ntohs(attr->attr)) {
+ case STUN_USERNAME:
+ state->username = (const char *) (attr->value);
+ break;
+ case STUN_PASSWORD:
+ state->password = (const char *) (attr->value);
+ break;
+ default:
+ if (stundebug)
+ ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n",
+ stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
+ }
+ return 0;
+}
+
+/*! \brief append a string to an STUN message */
+static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
+{
+ int size = sizeof(**attr) + strlen(s);
+ if (*left > size) {
+ (*attr)->attr = htons(attrval);
+ (*attr)->len = htons(strlen(s));
+ memcpy((*attr)->value, s, strlen(s));
+ (*attr) = (struct stun_attr *)((*attr)->value + strlen(s));
+ *len += size;
+ *left -= size;
+ }
+}
+
+/*! \brief append an address to an STUN message */
+static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left)
+{
+ int size = sizeof(**attr) + 8;
+ struct stun_addr *addr;
+ if (*left > size) {
+ (*attr)->attr = htons(attrval);
+ (*attr)->len = htons(8);
+ addr = (struct stun_addr *)((*attr)->value);
+ addr->unused = 0;
+ addr->family = 0x01;
+ addr->port = sin->sin_port;
+ addr->addr = sin->sin_addr.s_addr;
+ (*attr) = (struct stun_attr *)((*attr)->value + 8);
+ *len += size;
+ *left -= size;
+ }
+}
+
+/*! \brief wrapper to send an STUN message */
+static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
+{
+ return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0,
+ (struct sockaddr *)dst, sizeof(*dst));
+}
+
+/*! \brief helper function to generate a random request id */
+static void stun_req_id(struct stun_header *req)
+{
+ int x;
+ for (x = 0; x < 4; x++)
+ req->id.id[x] = ast_random();
+}
+
+size_t ast_rtp_alloc_size(void)
+{
+ return sizeof(struct ast_rtp);
+}
+
+/*! \brief callback type to be invoked on stun responses. */
+typedef int (stun_cb_f)(struct stun_attr *attr, void *arg);
+
+/*! \brief handle an incoming STUN message.
+ *
+ * Do some basic sanity checks on packet size and content,
+ * try to extract a bit of information, and possibly reply.
+ * At the moment this only processes BIND requests, and returns
+ * the externally visible address of the request.
+ * If a callback is specified, invoke it with the attribute.
+ */
+static int stun_handle_packet(int s, struct sockaddr_in *src,
+ unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
+{
+ struct stun_header *hdr = (struct stun_header *)data;
+ struct stun_attr *attr;
+ struct stun_state st;
+ int ret = STUN_IGNORE;
+ int x;
+
+ /* On entry, 'len' is the length of the udp payload. After the
+ * initial checks it becomes the size of unprocessed options,
+ * while 'data' is advanced accordingly.
+ */
+ if (len < sizeof(struct stun_header)) {
+ ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
+ return -1;
+ }
+ len -= sizeof(struct stun_header);
+ data += sizeof(struct stun_header);
+ x = ntohs(hdr->msglen); /* len as advertised in the message */
+ if (stundebug)
+ ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x);
+ if (x > len) {
+ ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
+ } else
+ len = x;
+ memset(&st, 0, sizeof(st));
+ while (len) {
+ if (len < sizeof(struct stun_attr)) {
+ ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
+ break;
+ }
+ attr = (struct stun_attr *)data;
+ /* compute total attribute length */
+ x = ntohs(attr->len) + sizeof(struct stun_attr);
+ if (x > len) {
+ ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
+ break;
+ }
+ if (stun_cb)
+ stun_cb(attr, arg);
+ if (stun_process_attr(&st, attr)) {
+ ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
+ break;
+ }
+ /* Clear attribute id: in case previous entry was a string,
+ * this will act as the terminator for the string.
+ */
+ attr->attr = 0;
+ data += x;
+ len -= x;
+ }
+ /* Null terminate any string.
+ * XXX NOTE, we write past the size of the buffer passed by the
+ * caller, so this is potentially dangerous. The only thing that
+ * saves us is that usually we read the incoming message in a
+ * much larger buffer in the struct ast_rtp
+ */
+ *data = '\0';
+
+ /* Now prepare to generate a reply, which at the moment is done
+ * only for properly formed (len == 0) STUN_BINDREQ messages.
+ */
+ if (len == 0) {
+ unsigned char respdata[1024];
+ struct stun_header *resp = (struct stun_header *)respdata;
+ int resplen = 0; /* len excluding header */
+ int respleft = sizeof(respdata) - sizeof(struct stun_header);
+
+ resp->id = hdr->id;
+ resp->msgtype = 0;
+ resp->msglen = 0;
+ attr = (struct stun_attr *)resp->ies;
+ switch (ntohs(hdr->msgtype)) {
+ case STUN_BINDREQ:
+ if (stundebug)
+ ast_verbose("STUN Bind Request, username: %s\n",
+ st.username ? st.username : "<none>");
+ if (st.username)
+ append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
+ append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
+ resp->msglen = htons(resplen);
+ resp->msgtype = htons(STUN_BINDRESP);
+ stun_send(s, src, resp);
+ ret = STUN_ACCEPT;
+ break;
+ default:
+ if (stundebug)
+ ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
+ }
+ }
+ return ret;
+}
+
+/*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response.
+ * This is used as a callback for stun_handle_response
+ * when called from ast_stun_request.
+ */
+static int stun_get_mapped(struct stun_attr *attr, void *arg)
+{
+ struct stun_addr *addr = (struct stun_addr *)(attr + 1);
+ struct sockaddr_in *sa = (struct sockaddr_in *)arg;
+
+ if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
+ return 1; /* not us. */
+ sa->sin_port = addr->port;
+ sa->sin_addr.s_addr = addr->addr;
+ return 0;
+}
+
+/*! \brief Generic STUN request
+ * Send a generic stun request to the server specified,
+ * possibly waiting for a reply and filling the 'reply' field with
+ * the externally visible address. Note that in this case the request
+ * will be blocking.
+ * (Note, the interface may change slightly in the future).
+ *
+ * \param s the socket used to send the request
+ * \param dst the address of the STUN server
+ * \param username if non null, add the username in the request
+ * \param answer if non null, the function waits for a response and
+ * puts here the externally visible address.
+ * \return 0 on success, other values on error.
+ */
+int ast_stun_request(int s, struct sockaddr_in *dst,
+ const char *username, struct sockaddr_in *answer)
+{
+ struct stun_header *req;
+ unsigned char reqdata[1024];
+ int reqlen, reqleft;
+ struct stun_attr *attr;
+ int res = 0;
+ int retry;
+
+ req = (struct stun_header *)reqdata;
+ stun_req_id(req);
+ reqlen = 0;
+ reqleft = sizeof(reqdata) - sizeof(struct stun_header);
+ req->msgtype = 0;
+ req->msglen = 0;
+ attr = (struct stun_attr *)req->ies;
+ if (username)
+ append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
+ req->msglen = htons(reqlen);
+ req->msgtype = htons(STUN_BINDREQ);
+ for (retry = 0; retry < 3; retry++) { /* XXX make retries configurable */
+ /* send request, possibly wait for reply */
+ unsigned char reply_buf[1024];
+ fd_set rfds;
+ struct timeval to = { 3, 0 }; /* timeout, make it configurable */
+ struct sockaddr_in src;
+ socklen_t srclen;
+
+ res = stun_send(s, dst, req);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n",
+ retry, res);
+ continue;
+ }
+ if (answer == NULL)
+ break;
+ FD_ZERO(&rfds);
+ FD_SET(s, &rfds);
+ res = ast_select(s + 1, &rfds, NULL, NULL, &to);
+ if (res <= 0) /* timeout or error */
+ continue;
+ bzero(&src, sizeof(src));
+ srclen = sizeof(src);
+ /* XXX pass -1 in the size, because stun_handle_packet might
+ * write past the end of the buffer.
+ */
+ res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1,
+ 0, (struct sockaddr *)&src, &srclen);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n",
+ retry, res);
+ continue;
+ }
+ bzero(answer, sizeof(struct sockaddr_in));
+ stun_handle_packet(s, &src, reply_buf, res,
+ stun_get_mapped, answer);
+ res = 0; /* signal regular exit */
+ break;
+ }
+ return res;
+}
+
+/*! \brief send a STUN BIND request to the given destination.
+ * Optionally, add a username if specified.
+ */
+void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username)
+{
+ ast_stun_request(rtp->s, suggestion, username, NULL);
+}
+
+/*! \brief List of current sessions */
+static AST_RWLIST_HEAD_STATIC(protos, ast_rtp_protocol);
+
+static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw)
+{
+ unsigned int sec, usec, frac;
+ sec = tv.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */
+ usec = tv.tv_usec;
+ frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6);
+ *msw = sec;
+ *lsw = frac;
+}
+
+int ast_rtp_fd(struct ast_rtp *rtp)
+{
+ return rtp->s;
+}
+
+int ast_rtcp_fd(struct ast_rtp *rtp)
+{
+ if (rtp->rtcp)
+ return rtp->rtcp->s;
+ return -1;
+}
+
+unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp)
+{
+ unsigned int interval;
+ /*! \todo XXX Do a more reasonable calculation on this one
+ * Look in RFC 3550 Section A.7 for an example*/
+ interval = rtcpinterval;
+ return interval;
+}
+
+/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */
+void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp)
+{
+ rtp->rtptimeout = (-1) * rtp->rtptimeout;
+ rtp->rtpholdtimeout = (-1) * rtp->rtpholdtimeout;
+}
+
+/*! \brief Set rtp timeout */
+void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout)
+{
+ rtp->rtptimeout = timeout;
+}
+
+/*! \brief Set rtp hold timeout */
+void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout)
+{
+ rtp->rtpholdtimeout = timeout;
+}
+
+/*! \brief set RTP keepalive interval */
+void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period)
+{
+ rtp->rtpkeepalive = period;
+}
+
+/*! \brief Get rtp timeout */
+int ast_rtp_get_rtptimeout(struct ast_rtp *rtp)
+{
+ if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */
+ return 0;
+ return rtp->rtptimeout;
+}
+
+/*! \brief Get rtp hold timeout */
+int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp)
+{
+ if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */
+ return 0;
+ return rtp->rtpholdtimeout;
+}
+
+/*! \brief Get RTP keepalive interval */
+int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp)
+{
+ return rtp->rtpkeepalive;
+}
+
+void ast_rtp_set_data(struct ast_rtp *rtp, void *data)
+{
+ rtp->data = data;
+}
+
+void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback)
+{
+ rtp->callback = callback;
+}
+
+void ast_rtp_setnat(struct ast_rtp *rtp, int nat)
+{
+ rtp->nat = nat;
+}
+
+int ast_rtp_getnat(struct ast_rtp *rtp)
+{
+ return ast_test_flag(rtp, FLAG_NAT_ACTIVE);
+}
+
+void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf)
+{
+ ast_set2_flag(rtp, dtmf ? 1 : 0, FLAG_HAS_DTMF);
+}
+
+void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate)
+{
+ ast_set2_flag(rtp, compensate ? 1 : 0, FLAG_DTMF_COMPENSATE);
+}
+
+void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable)
+{
+ ast_set2_flag(rtp, stun_enable ? 1 : 0, FLAG_HAS_STUN);
+}
+
+static void rtp_bridge_lock(struct ast_rtp *rtp)
+{
+#ifdef P2P_INTENSE
+ ast_mutex_lock(&rtp->bridge_lock);
+#endif
+ return;
+}
+
+static void rtp_bridge_unlock(struct ast_rtp *rtp)
+{
+#ifdef P2P_INTENSE
+ ast_mutex_unlock(&rtp->bridge_lock);
+#endif
+ return;
+}
+
+static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type)
+{
+ if (((ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && type == AST_FRAME_DTMF_END) ||
+ (type == AST_FRAME_DTMF_BEGIN)) && ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) {
+ ast_debug(1, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr));
+ rtp->resp = 0;
+ rtp->dtmfsamples = 0;
+ return &ast_null_frame;
+ }
+ ast_debug(1, "Sending dtmf: %d (%c), at %s\n", rtp->resp, rtp->resp, ast_inet_ntoa(rtp->them.sin_addr));
+ if (rtp->resp == 'X') {
+ rtp->f.frametype = AST_FRAME_CONTROL;
+ rtp->f.subclass = AST_CONTROL_FLASH;
+ } else {
+ rtp->f.frametype = type;
+ rtp->f.subclass = rtp->resp;
+ }
+ rtp->f.datalen = 0;
+ rtp->f.samples = 0;
+ rtp->f.mallocd = 0;
+ rtp->f.src = "RTP";
+ return &rtp->f;
+
+}
+
+static inline int rtp_debug_test_addr(struct sockaddr_in *addr)
+{
+ if (rtpdebug == 0)
+ return 0;
+ if (rtpdebugaddr.sin_addr.s_addr) {
+ if (((ntohs(rtpdebugaddr.sin_port) != 0)
+ && (rtpdebugaddr.sin_port != addr->sin_port))
+ || (rtpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+ return 0;
+ }
+ return 1;
+}
+
+static inline int rtcp_debug_test_addr(struct sockaddr_in *addr)
+{
+ if (rtcpdebug == 0)
+ return 0;
+ if (rtcpdebugaddr.sin_addr.s_addr) {
+ if (((ntohs(rtcpdebugaddr.sin_port) != 0)
+ && (rtcpdebugaddr.sin_port != addr->sin_port))
+ || (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+ return 0;
+ }
+ return 1;
+}
+
+
+static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *data, int len)
+{
+ unsigned int event;
+ char resp = 0;
+ struct ast_frame *f = NULL;
+ unsigned char seq;
+ unsigned int flags;
+ unsigned int power;
+
+ /* We should have at least 4 bytes in RTP data */
+ if (len < 4)
+ return f;
+
+ /* The format of Cisco RTP DTMF packet looks like next:
+ +0 - sequence number of DTMF RTP packet (begins from 1,
+ wrapped to 0)
+ +1 - set of flags
+ +1 (bit 0) - flaps by different DTMF digits delimited by audio
+ or repeated digit without audio???
+ +2 (+4,+6,...) - power level? (rises from 0 to 32 at begin of tone
+ then falls to 0 at its end)
+ +3 (+5,+7,...) - detected DTMF digit (0..9,*,#,A-D,...)
+ Repeated DTMF information (bytes 4/5, 6/7) is history shifted right
+ by each new packet and thus provides some redudancy.
+
+ Sample of Cisco RTP DTMF packet is (all data in hex):
+ 19 07 00 02 12 02 20 02
+ showing end of DTMF digit '2'.
+
+ The packets
+ 27 07 00 02 0A 02 20 02
+ 28 06 20 02 00 02 0A 02
+ shows begin of new digit '2' with very short pause (20 ms) after
+ previous digit '2'. Bit +1.0 flips at begin of new digit.
+
+ Cisco RTP DTMF packets comes as replacement of audio RTP packets
+ so its uses the same sequencing and timestamping rules as replaced
+ audio packets. Repeat interval of DTMF packets is 20 ms and not rely
+ on audio framing parameters. Marker bit isn't used within stream of
+ DTMFs nor audio stream coming immediately after DTMF stream. Timestamps
+ are not sequential at borders between DTMF and audio streams,
+ */
+
+ seq = data[0];
+ flags = data[1];
+ power = data[2];
+ event = data[3] & 0x1f;
+
+ if (option_debug > 2 || rtpdebug)
+ ast_debug(0, "Cisco DTMF Digit: %02x (len=%d, seq=%d, flags=%02x, power=%d, history count=%d)\n", event, len, seq, flags, power, (len - 4) / 2);
+ if (event < 10) {
+ resp = '0' + event;
+ } else if (event < 11) {
+ resp = '*';
+ } else if (event < 12) {
+ resp = '#';
+ } else if (event < 16) {
+ resp = 'A' + (event - 12);
+ } else if (event < 17) {
+ resp = 'X';
+ }
+ if ((!rtp->resp && power) || (rtp->resp && (rtp->resp != resp))) {
+ rtp->resp = resp;
+ /* Why we should care on DTMF compensation at reception? */
+ if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) {
+ f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
+ rtp->dtmfsamples = 0;
+ }
+ } else if ((rtp->resp == resp) && !power) {
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+ f->samples = rtp->dtmfsamples * 8;
+ rtp->resp = 0;
+ } else if (rtp->resp == resp)
+ rtp->dtmfsamples += 20 * 8;
+ rtp->dtmfcount = dtmftimeout;
+ return f;
+}
+
+/*!
+ * \brief Process RTP DTMF and events according to RFC 2833.
+ *
+ * RFC 2833 is "RTP Payload for DTMF Digits, Telephony Tones and Telephony Signals".
+ *
+ * \param rtp
+ * \param data
+ * \param len
+ * \param seqno
+ * \param timestamp
+ * \returns
+ */
+static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp)
+{
+ unsigned int event;
+ unsigned int event_end;
+ unsigned int samples;
+ char resp = 0;
+ struct ast_frame *f = NULL;
+
+ /* Figure out event, event end, and samples */
+ event = ntohl(*((unsigned int *)(data)));
+ event >>= 24;
+ event_end = ntohl(*((unsigned int *)(data)));
+ event_end <<= 8;
+ event_end >>= 24;
+ samples = ntohl(*((unsigned int *)(data)));
+ samples &= 0xFFFF;
+
+ /* Print out debug if turned on */
+ if (rtpdebug || option_debug > 2)
+ ast_debug(0, "- RTP 2833 Event: %08x (len = %d)\n", event, len);
+
+ /* Figure out what digit was pressed */
+ if (event < 10) {
+ resp = '0' + event;
+ } else if (event < 11) {
+ resp = '*';
+ } else if (event < 12) {
+ resp = '#';
+ } else if (event < 16) {
+ resp = 'A' + (event - 12);
+ } else if (event < 17) { /* Event 16: Hook flash */
+ resp = 'X';
+ } else {
+ /* Not a supported event */
+ ast_log(LOG_DEBUG, "Ignoring RTP 2833 Event: %08x. Not a DTMF Digit.\n", event);
+ return &ast_null_frame;
+ }
+
+ if (ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) {
+ if ((rtp->lastevent != timestamp) || (rtp->resp && rtp->resp != resp)) {
+ rtp->resp = resp;
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+ f->len = 0;
+ rtp->lastevent = timestamp;
+ }
+ } else {
+ if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) {
+ rtp->resp = resp;
+ f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
+ } else if ((event_end & 0x80) && (rtp->lastevent != seqno) && rtp->resp) {
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+ f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */
+ rtp->resp = 0;
+ rtp->lastevent = seqno;
+ }
+ }
+
+ rtp->dtmfcount = dtmftimeout;
+ rtp->dtmfsamples = samples;
+
+ return f;
+}
+
+/*!
+ * \brief Process Comfort Noise RTP.
+ *
+ * This is incomplete at the moment.
+ *
+*/
+static struct ast_frame *process_rfc3389(struct ast_rtp *rtp, unsigned char *data, int len)
+{
+ struct ast_frame *f = NULL;
+ /* Convert comfort noise into audio with various codecs. Unfortunately this doesn't
+ totally help us out becuase we don't have an engine to keep it going and we are not
+ guaranteed to have it every 20ms or anything */
+ if (rtpdebug)
+ ast_debug(0, "- RTP 3389 Comfort noise event: Level %d (len = %d)\n", rtp->lastrxformat, len);
+
+ if (!(ast_test_flag(rtp, FLAG_3389_WARNING))) {
+ ast_log(LOG_NOTICE, "Comfort noise support incomplete in Asterisk (RFC 3389). Please turn off on client if possible. Client IP: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr));
+ ast_set_flag(rtp, FLAG_3389_WARNING);
+ }
+
+ /* Must have at least one byte */
+ if (!len)
+ return NULL;
+ if (len < 24) {
+ rtp->f.data = rtp->rawdata + AST_FRIENDLY_OFFSET;
+ rtp->f.datalen = len - 1;
+ rtp->f.offset = AST_FRIENDLY_OFFSET;
+ memcpy(rtp->f.data, data + 1, len - 1);
+ } else {
+ rtp->f.data = NULL;
+ rtp->f.offset = 0;
+ rtp->f.datalen = 0;
+ }
+ rtp->f.frametype = AST_FRAME_CNG;
+ rtp->f.subclass = data[0] & 0x7f;
+ rtp->f.datalen = len - 1;
+ rtp->f.samples = 0;
+ rtp->f.delivery.tv_usec = rtp->f.delivery.tv_sec = 0;
+ f = &rtp->f;
+ return f;
+}
+
+static int rtpread(int *id, int fd, short events, void *cbdata)
+{
+ struct ast_rtp *rtp = cbdata;
+ struct ast_frame *f;
+ f = ast_rtp_read(rtp);
+ if (f) {
+ if (rtp->callback)
+ rtp->callback(rtp, f, rtp->data);
+ }
+ return 1;
+}
+
+struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
+{
+ socklen_t len;
+ int position, i, packetwords;
+ int res;
+ struct sockaddr_in sin;
+ unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned int *rtcpheader;
+ int pt;
+ struct timeval now;
+ unsigned int length;
+ int rc;
+ double rttsec;
+ uint64_t rtt = 0;
+ unsigned int dlsr;
+ unsigned int lsr;
+ unsigned int msw;
+ unsigned int lsw;
+ unsigned int comp;
+ struct ast_frame *f = &ast_null_frame;
+
+ if (!rtp || !rtp->rtcp)
+ return &ast_null_frame;
+
+ len = sizeof(sin);
+
+ res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET,
+ 0, (struct sockaddr *)&sin, &len);
+ rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
+
+ if (res < 0) {
+ if (errno == EBADF)
+ CRASH;
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", strerror(errno));
+ return NULL;
+ }
+ return &ast_null_frame;
+ }
+
+ packetwords = res / 4;
+
+ if (rtp->nat) {
+ /* Send to whoever sent to us */
+ if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (rtp->rtcp->them.sin_port != sin.sin_port)) {
+ memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ }
+ }
+
+ ast_debug(1, "Got RTCP report of %d bytes\n", res);
+
+ /* Process a compound packet */
+ position = 0;
+ while (position < packetwords) {
+ i = position;
+ length = ntohl(rtcpheader[i]);
+ pt = (length & 0xff0000) >> 16;
+ rc = (length & 0x1f000000) >> 24;
+ length &= 0xffff;
+
+ if ((i + length) > packetwords) {
+ if (option_debug || rtpdebug)
+ ast_log(LOG_DEBUG, "RTCP Read too short\n");
+ return &ast_null_frame;
+ }
+
+ if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose("\n\nGot RTCP from %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown");
+ ast_verbose("Reception reports: %d\n", rc);
+ ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]);
+ }
+
+ i += 2; /* Advance past header and ssrc */
+
+ switch (pt) {
+ case RTCP_PT_SR:
+ gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */
+ rtp->rtcp->spc = ntohl(rtcpheader[i+3]);
+ rtp->rtcp->soc = ntohl(rtcpheader[i + 4]);
+ rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/
+
+ if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose("NTP timestamp: %lu.%010lu\n", (unsigned long) ntohl(rtcpheader[i]), (unsigned long) ntohl(rtcpheader[i + 1]) * 4096);
+ ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2]));
+ ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4]));
+ }
+ i += 5;
+ if (rc < 1)
+ break;
+ /* Intentional fall through */
+ case RTCP_PT_RR:
+ /* Don't handle multiple reception reports (rc > 1) yet */
+ /* Calculate RTT per RFC */
+ gettimeofday(&now, NULL);
+ timeval2ntp(now, &msw, &lsw);
+ if (ntohl(rtcpheader[i + 4]) && ntohl(rtcpheader[i + 5])) { /* We must have the LSR && DLSR */
+ comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16);
+ lsr = ntohl(rtcpheader[i + 4]);
+ dlsr = ntohl(rtcpheader[i + 5]);
+ rtt = comp - lsr - dlsr;
+
+ /* Convert end to end delay to usec (keeping the calculation in 64bit space)
+ sess->ee_delay = (eedelay * 1000) / 65536; */
+ if (rtt < 4294) {
+ rtt = (rtt * 1000000) >> 16;
+ } else {
+ rtt = (rtt * 1000) >> 16;
+ rtt *= 1000;
+ }
+ rtt = rtt / 1000.;
+ rttsec = rtt / 1000.;
+
+ if (comp - dlsr >= lsr) {
+ rtp->rtcp->accumulated_transit += rttsec;
+ rtp->rtcp->rtt = rttsec;
+ if (rtp->rtcp->maxrtt<rttsec)
+ rtp->rtcp->maxrtt = rttsec;
+ if (rtp->rtcp->minrtt>rttsec)
+ rtp->rtcp->minrtt = rttsec;
+ } else if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose("Internal RTCP NTP clock skew detected: "
+ "lsr=%u, now=%u, dlsr=%u (%d:%03dms), "
+ "diff=%d\n",
+ lsr, comp, dlsr, dlsr / 65536,
+ (dlsr % 65536) * 1000 / 65536,
+ dlsr - (comp - lsr));
+ }
+ }
+
+ rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]);
+ rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff;
+ if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose(" Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24));
+ ast_verbose(" Packets lost so far: %d\n", rtp->rtcp->reported_lost);
+ ast_verbose(" Highest sequence number: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff));
+ ast_verbose(" Sequence number cycles: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16);
+ ast_verbose(" Interarrival jitter: %u\n", rtp->rtcp->reported_jitter);
+ ast_verbose(" Last SR(our NTP): %lu.%010lu\n",(unsigned long) ntohl(rtcpheader[i + 4]) >> 16,((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096);
+ ast_verbose(" DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i + 5])/65536.0);
+ if (rtt)
+ ast_verbose(" RTT: %lu(sec)\n", (unsigned long) rtt);
+ }
+ if (rtt) {
+ manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n"
+ "PT: %d(%s)\r\n"
+ "ReceptionReports: %d\r\n"
+ "SenderSSRC: %u\r\n"
+ "FractionLost: %ld\r\n"
+ "PacketsLost: %d\r\n"
+ "HighestSequence: %ld\r\n"
+ "SequenceNumberCycles: %ld\r\n"
+ "IAJitter: %u\r\n"
+ "LastSR: %lu.%010lu\r\n"
+ "DLSR: %4.4f(sec)\r\n"
+ "RTT: %llu(sec)\r\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port),
+ pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown",
+ rc,
+ rtcpheader[i + 1],
+ (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24),
+ rtp->rtcp->reported_lost,
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff),
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16,
+ rtp->rtcp->reported_jitter,
+ (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096,
+ ntohl(rtcpheader[i + 5])/65536.0,
+ (unsigned long long)rtt);
+ } else {
+ manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n"
+ "PT: %d(%s)\r\n"
+ "ReceptionReports: %d\r\n"
+ "SenderSSRC: %u\r\n"
+ "FractionLost: %ld\r\n"
+ "PacketsLost: %d\r\n"
+ "HighestSequence: %ld\r\n"
+ "SequenceNumberCycles: %ld\r\n"
+ "IAJitter: %u\r\n"
+ "LastSR: %lu.%010lu\r\n"
+ "DLSR: %4.4f(sec)\r\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port),
+ pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown",
+ rc,
+ rtcpheader[i + 1],
+ (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24),
+ rtp->rtcp->reported_lost,
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff),
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16,
+ rtp->rtcp->reported_jitter,
+ (unsigned long) ntohl(rtcpheader[i + 4]) >> 16,
+ ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096,
+ ntohl(rtcpheader[i + 5])/65536.0);
+ }
+ break;
+ case RTCP_PT_FUR:
+ if (rtcp_debug_test_addr(&sin))
+ ast_verbose("Received an RTCP Fast Update Request\n");
+ rtp->f.frametype = AST_FRAME_CONTROL;
+ rtp->f.subclass = AST_CONTROL_VIDUPDATE;
+ rtp->f.datalen = 0;
+ rtp->f.samples = 0;
+ rtp->f.mallocd = 0;
+ rtp->f.src = "RTP";
+ f = &rtp->f;
+ break;
+ case RTCP_PT_SDES:
+ if (rtcp_debug_test_addr(&sin))
+ ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ break;
+ case RTCP_PT_BYE:
+ if (rtcp_debug_test_addr(&sin))
+ ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ break;
+ default:
+ ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ break;
+ }
+ position += (length + 1);
+ }
+
+ return f;
+}
+
+static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark)
+{
+ struct timeval now;
+ double transit;
+ double current_time;
+ double d;
+ double dtv;
+ double prog;
+
+ if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) {
+ gettimeofday(&rtp->rxcore, NULL);
+ rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000;
+ /* map timestamp to a real time */
+ rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */
+ rtp->rxcore.tv_sec -= timestamp / 8000;
+ rtp->rxcore.tv_usec -= (timestamp % 8000) * 125;
+ /* Round to 0.1ms for nice, pretty timestamps */
+ rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100;
+ if (rtp->rxcore.tv_usec < 0) {
+ /* Adjust appropriately if necessary */
+ rtp->rxcore.tv_usec += 1000000;
+ rtp->rxcore.tv_sec -= 1;
+ }
+ }
+
+ gettimeofday(&now,NULL);
+ /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */
+ tv->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000;
+ tv->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125;
+ if (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec += 1;
+ }
+ prog = (double)((timestamp-rtp->seedrxts)/8000.);
+ dtv = (double)rtp->drxcore + (double)(prog);
+ current_time = (double)now.tv_sec + (double)now.tv_usec/1000000;
+ transit = current_time - dtv;
+ d = transit - rtp->rxtransit;
+ rtp->rxtransit = transit;
+ if (d<0)
+ d=-d;
+ rtp->rxjitter += (1./16.) * (d - rtp->rxjitter);
+ if (rtp->rtcp && rtp->rxjitter > rtp->rtcp->maxrxjitter)
+ rtp->rtcp->maxrxjitter = rtp->rxjitter;
+ if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter)
+ rtp->rtcp->minrxjitter = rtp->rxjitter;
+}
+
+/*! \brief Perform a Packet2Packet RTP write */
+static int bridge_p2p_rtp_write(struct ast_rtp *rtp, struct ast_rtp *bridged, unsigned int *rtpheader, int len, int hdrlen)
+{
+ int res = 0, payload = 0, bridged_payload = 0, mark;
+ struct rtpPayloadType rtpPT;
+ int reconstruct = ntohl(rtpheader[0]);
+
+ /* Get fields from packet */
+ payload = (reconstruct & 0x7f0000) >> 16;
+ mark = (((reconstruct & 0x800000) >> 23) != 0);
+
+ /* Check what the payload value should be */
+ rtpPT = ast_rtp_lookup_pt(rtp, payload);
+
+ /* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */
+ if (!bridged->current_RTP_PT[payload].code)
+ return -1;
+
+ /* If the payload is DTMF, and we are listening for DTMF - then feed it into the core */
+ if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) && !rtpPT.isAstFormat && rtpPT.code == AST_RTP_DTMF)
+ return -1;
+
+ /* Otherwise adjust bridged payload to match */
+ bridged_payload = ast_rtp_lookup_code(bridged, rtpPT.isAstFormat, rtpPT.code);
+
+ /* If the mark bit has not been sent yet... do it now */
+ if (!ast_test_flag(rtp, FLAG_P2P_SENT_MARK)) {
+ mark = 1;
+ ast_set_flag(rtp, FLAG_P2P_SENT_MARK);
+ }
+
+ /* Reconstruct part of the packet */
+ reconstruct &= 0xFF80FFFF;
+ reconstruct |= (bridged_payload << 16);
+ reconstruct |= (mark << 23);
+ rtpheader[0] = htonl(reconstruct);
+
+ /* Send the packet back out */
+ res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them));
+ if (res < 0) {
+ if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
+ ast_debug(1, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno));
+ } else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) {
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port));
+ ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN);
+ }
+ return 0;
+ } else if (rtp_debug_test_addr(&bridged->them))
+ ast_verbose("Sent RTP P2P packet to %s:%u (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen);
+
+ return 0;
+}
+
+struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
+{
+ int res;
+ struct sockaddr_in sin;
+ socklen_t len;
+ unsigned int seqno;
+ int version;
+ int payloadtype;
+ int hdrlen = 12;
+ int padding;
+ int mark;
+ int ext;
+ int cc;
+ unsigned int ssrc;
+ unsigned int timestamp;
+ unsigned int *rtpheader;
+ struct rtpPayloadType rtpPT;
+ struct ast_rtp *bridged = NULL;
+
+ /* If time is up, kill it */
+ if (rtp->sending_digit)
+ ast_rtp_senddigit_continuation(rtp);
+
+ len = sizeof(sin);
+
+ /* Cache where the header will go */
+ res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET,
+ 0, (struct sockaddr *)&sin, &len);
+
+ rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
+ if (res < 0) {
+ if (errno == EBADF)
+ CRASH;
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "RTP Read error: %s. Hanging up.\n", strerror(errno));
+ return NULL;
+ }
+ return &ast_null_frame;
+ }
+
+ if (res < hdrlen) {
+ ast_log(LOG_WARNING, "RTP Read too short\n");
+ return &ast_null_frame;
+ }
+
+ /* Get fields */
+ seqno = ntohl(rtpheader[0]);
+
+ /* Check RTP version */
+ version = (seqno & 0xC0000000) >> 30;
+ if (!version) {
+ /* If the two high bits are 0, this might be a
+ * STUN message, so process it. stun_handle_packet()
+ * answers to requests, and it returns STUN_ACCEPT
+ * if the request is valid.
+ */
+ if ((stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == STUN_ACCEPT) &&
+ (!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) {
+ memcpy(&rtp->them, &sin, sizeof(rtp->them));
+ }
+ return &ast_null_frame;
+ }
+
+#if 0 /* Allow to receive RTP stream with closed transmission path */
+ /* If we don't have the other side's address, then ignore this */
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return &ast_null_frame;
+#endif
+
+ /* Send to whoever send to us if NAT is turned on */
+ if (rtp->nat) {
+ if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (rtp->them.sin_port != sin.sin_port)) {
+ rtp->them = sin;
+ if (rtp->rtcp) {
+ memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
+ rtp->rtcp->them.sin_port = htons(ntohs(rtp->them.sin_port)+1);
+ }
+ rtp->rxseqno = 0;
+ ast_set_flag(rtp, FLAG_NAT_ACTIVE);
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+ }
+ }
+
+ /* If we are bridged to another RTP stream, send direct */
+ if ((bridged = ast_rtp_get_bridged(rtp)) && !bridge_p2p_rtp_write(rtp, bridged, rtpheader, res, hdrlen))
+ return &ast_null_frame;
+
+ if (version != 2)
+ return &ast_null_frame;
+
+ payloadtype = (seqno & 0x7f0000) >> 16;
+ padding = seqno & (1 << 29);
+ mark = seqno & (1 << 23);
+ ext = seqno & (1 << 28);
+ cc = (seqno & 0xF000000) >> 24;
+ seqno &= 0xffff;
+ timestamp = ntohl(rtpheader[1]);
+ ssrc = ntohl(rtpheader[2]);
+
+ if (!mark && rtp->rxssrc && rtp->rxssrc != ssrc) {
+ if (option_debug || rtpdebug)
+ ast_debug(0, "Forcing Marker bit, because SSRC has changed\n");
+ mark = 1;
+ }
+
+ rtp->rxssrc = ssrc;
+
+ if (padding) {
+ /* Remove padding bytes */
+ res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1];
+ }
+
+ if (cc) {
+ /* CSRC fields present */
+ hdrlen += cc*4;
+ }
+
+ if (ext) {
+ /* RTP Extension present */
+ hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2;
+ hdrlen += 4;
+ if (option_debug) {
+ int profile;
+ profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16;
+ if (profile == 0x505a)
+ ast_debug(1, "Found Zfone extension in RTP stream - zrtp - not supported.\n");
+ else
+ ast_debug(1, "Found unknown RTP Extensions %x\n", profile);
+ }
+ }
+
+ if (res < hdrlen) {
+ ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d)\n", res, hdrlen);
+ return &ast_null_frame;
+ }
+
+ rtp->rxcount++; /* Only count reasonably valid packets, this'll make the rtcp stats more accurate */
+
+ if (rtp->rxcount==1) {
+ /* This is the first RTP packet successfully received from source */
+ rtp->seedrxseqno = seqno;
+ }
+
+ /* Do not schedule RR if RTCP isn't run */
+ if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) {
+ /* Schedule transmission of Receiver Report */
+ rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
+ }
+ if ( (int)rtp->lastrxseqno - (int)seqno > 100) /* if so it would indicate that the sender cycled; allow for misordering */
+ rtp->cycles += RTP_SEQ_MOD;
+
+ rtp->lastrxseqno = seqno;
+
+ if (rtp->themssrc==0)
+ rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
+
+ if (rtp_debug_test_addr(&sin))
+ ast_verbose("Got RTP packet from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen);
+
+ rtpPT = ast_rtp_lookup_pt(rtp, payloadtype);
+ if (!rtpPT.isAstFormat) {
+ struct ast_frame *f = NULL;
+
+ /* This is special in-band data that's not one of our codecs */
+ if (rtpPT.code == AST_RTP_DTMF) {
+ /* It's special -- rfc2833 process it */
+ if (rtp_debug_test_addr(&sin)) {
+ unsigned char *data;
+ unsigned int event;
+ unsigned int event_end;
+ unsigned int duration;
+ data = rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen;
+ event = ntohl(*((unsigned int *)(data)));
+ event >>= 24;
+ event_end = ntohl(*((unsigned int *)(data)));
+ event_end <<= 8;
+ event_end >>= 24;
+ duration = ntohl(*((unsigned int *)(data)));
+ duration &= 0xFFFF;
+ ast_verbose("Got RTP RFC2833 from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp, res - hdrlen, (mark?1:0), event, ((event_end & 0x80)?1:0), duration);
+ }
+ f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp);
+ } else if (rtpPT.code == AST_RTP_CISCO_DTMF) {
+ /* It's really special -- process it the Cisco way */
+ if (rtp->lastevent <= seqno || (rtp->lastevent >= 65530 && seqno <= 6)) {
+ f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
+ rtp->lastevent = seqno;
+ }
+ } else if (rtpPT.code == AST_RTP_CN) {
+ /* Comfort Noise */
+ f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
+ } else {
+ ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, ast_inet_ntoa(rtp->them.sin_addr));
+ }
+ return f ? f : &ast_null_frame;
+ }
+ rtp->lastrxformat = rtp->f.subclass = rtpPT.code;
+ rtp->f.frametype = (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) ? AST_FRAME_VOICE : (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) ? AST_FRAME_VIDEO : AST_FRAME_TEXT;
+
+ if (!rtp->lastrxts)
+ rtp->lastrxts = timestamp;
+
+ rtp->rxseqno = seqno;
+
+ /* Record received timestamp as last received now */
+ rtp->lastrxts = timestamp;
+
+ rtp->f.mallocd = 0;
+ rtp->f.datalen = res - hdrlen;
+ rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
+ rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
+ rtp->f.seqno = seqno;
+ if (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) {
+ rtp->f.samples = ast_codec_get_samples(&rtp->f);
+ if (rtp->f.subclass == AST_FORMAT_SLINEAR)
+ ast_frame_byteswap_be(&rtp->f);
+ calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
+ /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */
+ ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
+ rtp->f.ts = timestamp / 8;
+ rtp->f.len = rtp->f.samples / 8;
+ } else if(rtp->f.subclass & AST_FORMAT_VIDEO_MASK) {
+ /* Video -- samples is # of samples vs. 90000 */
+ if (!rtp->lastividtimestamp)
+ rtp->lastividtimestamp = timestamp;
+ rtp->f.samples = timestamp - rtp->lastividtimestamp;
+ rtp->lastividtimestamp = timestamp;
+ rtp->f.delivery.tv_sec = 0;
+ rtp->f.delivery.tv_usec = 0;
+ /* Pass the RTP marker bit as bit 0 in the subclass field.
+ * This is ok because subclass is actually a bitmask, and
+ * the low bits represent audio formats, that are not
+ * involved here since we deal with video.
+ */
+ if (mark)
+ rtp->f.subclass |= 0x1;
+ } else {
+ /* TEXT -- samples is # of samples vs. 1000 */
+ if (!rtp->lastitexttimestamp)
+ rtp->lastitexttimestamp = timestamp;
+ rtp->f.samples = timestamp - rtp->lastitexttimestamp;
+ rtp->lastitexttimestamp = timestamp;
+ rtp->f.delivery.tv_sec = 0;
+ rtp->f.delivery.tv_usec = 0;
+ }
+ rtp->f.src = "RTP";
+ return &rtp->f;
+}
+
+/* The following array defines the MIME Media type (and subtype) for each
+ of our codecs, or RTP-specific data type. */
+static struct {
+ struct rtpPayloadType payloadType;
+ char* type;
+ char* subtype;
+} mimeTypes[] = {
+ {{1, AST_FORMAT_G723_1}, "audio", "G723"},
+ {{1, AST_FORMAT_GSM}, "audio", "GSM"},
+ {{1, AST_FORMAT_ULAW}, "audio", "PCMU"},
+ {{1, AST_FORMAT_ULAW}, "audio", "G711U"},
+ {{1, AST_FORMAT_ALAW}, "audio", "PCMA"},
+ {{1, AST_FORMAT_ALAW}, "audio", "G711A"},
+ {{1, AST_FORMAT_G726}, "audio", "G726-32"},
+ {{1, AST_FORMAT_ADPCM}, "audio", "DVI4"},
+ {{1, AST_FORMAT_SLINEAR}, "audio", "L16"},
+ {{1, AST_FORMAT_LPC10}, "audio", "LPC"},
+ {{1, AST_FORMAT_G729A}, "audio", "G729"},
+ {{1, AST_FORMAT_G729A}, "audio", "G729A"},
+ {{1, AST_FORMAT_SPEEX}, "audio", "speex"},
+ {{1, AST_FORMAT_ILBC}, "audio", "iLBC"},
+ {{1, AST_FORMAT_G722}, "audio", "G722"},
+ {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32"},
+ {{0, AST_RTP_DTMF}, "audio", "telephone-event"},
+ {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event"},
+ {{0, AST_RTP_CN}, "audio", "CN"},
+ {{1, AST_FORMAT_JPEG}, "video", "JPEG"},
+ {{1, AST_FORMAT_PNG}, "video", "PNG"},
+ {{1, AST_FORMAT_H261}, "video", "H261"},
+ {{1, AST_FORMAT_H263}, "video", "H263"},
+ {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998"},
+ {{1, AST_FORMAT_H264}, "video", "H264"},
+ {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES"},
+ {{1, AST_FORMAT_T140}, "text", "T140"},
+};
+
+/*!
+ * \brief Mapping between Asterisk codecs and rtp payload types
+ *
+ * Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s:
+ * also, our own choices for dynamic payload types. This is our master
+ * table for transmission
+ *
+ * See http://www.iana.org/assignments/rtp-parameters for a list of
+ * assigned values
+ */
+static struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = {
+ [0] = {1, AST_FORMAT_ULAW},
+#ifdef USE_DEPRECATED_G726
+ [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */
+#endif
+ [3] = {1, AST_FORMAT_GSM},
+ [4] = {1, AST_FORMAT_G723_1},
+ [5] = {1, AST_FORMAT_ADPCM}, /* 8 kHz */
+ [6] = {1, AST_FORMAT_ADPCM}, /* 16 kHz */
+ [7] = {1, AST_FORMAT_LPC10},
+ [8] = {1, AST_FORMAT_ALAW},
+ [9] = {1, AST_FORMAT_G722},
+ [10] = {1, AST_FORMAT_SLINEAR}, /* 2 channels */
+ [11] = {1, AST_FORMAT_SLINEAR}, /* 1 channel */
+ [13] = {0, AST_RTP_CN},
+ [16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */
+ [17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */
+ [18] = {1, AST_FORMAT_G729A},
+ [19] = {0, AST_RTP_CN}, /* Also used for CN */
+ [26] = {1, AST_FORMAT_JPEG},
+ [31] = {1, AST_FORMAT_H261},
+ [34] = {1, AST_FORMAT_H263},
+ [97] = {1, AST_FORMAT_ILBC},
+ [98] = {1, AST_FORMAT_H263_PLUS},
+ [99] = {1, AST_FORMAT_H264},
+ [101] = {0, AST_RTP_DTMF},
+ [102] = {1, AST_FORMAT_T140}, /* Real time text chat */
+ [103] = {1, AST_FORMAT_H263_PLUS},
+ [104] = {1, AST_FORMAT_MP4_VIDEO},
+ [110] = {1, AST_FORMAT_SPEEX},
+ [111] = {1, AST_FORMAT_G726},
+ [112] = {1, AST_FORMAT_G726_AAL2},
+ [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */
+};
+
+void ast_rtp_pt_clear(struct ast_rtp* rtp)
+{
+ int i;
+
+ if (!rtp)
+ return;
+
+ rtp_bridge_lock(rtp);
+
+ for (i = 0; i < MAX_RTP_PT; ++i) {
+ rtp->current_RTP_PT[i].isAstFormat = 0;
+ rtp->current_RTP_PT[i].code = 0;
+ }
+
+ rtp->rtp_lookup_code_cache_isAstFormat = 0;
+ rtp->rtp_lookup_code_cache_code = 0;
+ rtp->rtp_lookup_code_cache_result = 0;
+
+ rtp_bridge_unlock(rtp);
+}
+
+void ast_rtp_pt_default(struct ast_rtp* rtp)
+{
+ int i;
+
+ rtp_bridge_lock(rtp);
+
+ /* Initialize to default payload types */
+ for (i = 0; i < MAX_RTP_PT; ++i) {
+ rtp->current_RTP_PT[i].isAstFormat = static_RTP_PT[i].isAstFormat;
+ rtp->current_RTP_PT[i].code = static_RTP_PT[i].code;
+ }
+
+ rtp->rtp_lookup_code_cache_isAstFormat = 0;
+ rtp->rtp_lookup_code_cache_code = 0;
+ rtp->rtp_lookup_code_cache_result = 0;
+
+ rtp_bridge_unlock(rtp);
+}
+
+void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src)
+{
+ unsigned int i;
+
+ rtp_bridge_lock(dest);
+ rtp_bridge_lock(src);
+
+ for (i=0; i < MAX_RTP_PT; ++i) {
+ dest->current_RTP_PT[i].isAstFormat =
+ src->current_RTP_PT[i].isAstFormat;
+ dest->current_RTP_PT[i].code =
+ src->current_RTP_PT[i].code;
+ }
+ dest->rtp_lookup_code_cache_isAstFormat = 0;
+ dest->rtp_lookup_code_cache_code = 0;
+ dest->rtp_lookup_code_cache_result = 0;
+
+ rtp_bridge_unlock(src);
+ rtp_bridge_unlock(dest);
+}
+
+/*! \brief Get channel driver interface structure */
+static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
+{
+ struct ast_rtp_protocol *cur = NULL;
+
+ AST_RWLIST_RDLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (cur->type == chan->tech->type)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&protos);
+
+ return cur;
+}
+
+int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1)
+{
+ // dest = c0, src = c1
+ struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */
+ struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */
+ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL;
+ enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED;
+ int srccodec, destcodec, nat_active = 0;
+
+ /* Lock channels */
+ ast_channel_lock(c0);
+ if (c1) {
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+ }
+
+ /* Find channel driver interfaces */
+ destpr = get_proto(c0);
+ if (c1)
+ srcpr = get_proto(c1);
+ if (!destpr) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c0->name);
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ if (!srcpr) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c1 ? c1->name : "<unspecified>");
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return -1;
+ }
+
+ /* Get audio, video and text interface (if native bridge is possible) */
+ audio_dest_res = destpr->get_rtp_info(c0, &destp);
+ video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(c0, &vdestp) : AST_RTP_GET_FAILED;
+ text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(c0, &tdestp) : AST_RTP_GET_FAILED;
+ if (srcpr) {
+ audio_src_res = srcpr->get_rtp_info(c1, &srcp);
+ video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(c1, &vsrcp) : AST_RTP_GET_FAILED;
+ text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(c1, &tsrcp) : AST_RTP_GET_FAILED;
+ }
+
+ /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
+ if (audio_dest_res != AST_RTP_TRY_NATIVE) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ if (audio_src_res == AST_RTP_TRY_NATIVE && srcpr->get_codec)
+ srccodec = srcpr->get_codec(c1);
+ else
+ srccodec = 0;
+ if (audio_dest_res == AST_RTP_TRY_NATIVE && destpr->get_codec)
+ destcodec = destpr->get_codec(c0);
+ else
+ destcodec = 0;
+ /* Ensure we have at least one matching codec */
+ if (!(srccodec & destcodec)) {
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return 0;
+ }
+ /* Consider empty media as non-existant */
+ if (audio_src_res == AST_RTP_TRY_NATIVE && !srcp->them.sin_addr.s_addr)
+ srcp = NULL;
+ if (srcp && (srcp->nat || ast_test_flag(srcp, FLAG_NAT_ACTIVE)))
+ nat_active = 1;
+ /* Bridge media early */
+ if (destpr->set_rtp_peer(c0, srcp, vsrcp, tsrcp, srccodec, nat_active))
+ ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>");
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ ast_debug(1, "Setting early bridge SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>");
+ return 0;
+}
+
+int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media)
+{
+ struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */
+ struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */
+ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL;
+ enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED;
+ int srccodec, destcodec;
+
+ /* Lock channels */
+ ast_channel_lock(dest);
+ while (ast_channel_trylock(src)) {
+ ast_channel_unlock(dest);
+ usleep(1);
+ ast_channel_lock(dest);
+ }
+
+ /* Find channel driver interfaces */
+ if (!(destpr = get_proto(dest))) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", dest->name);
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ return 0;
+ }
+ if (!(srcpr = get_proto(src))) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", src->name);
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ return 0;
+ }
+
+ /* Get audio and video interface (if native bridge is possible) */
+ audio_dest_res = destpr->get_rtp_info(dest, &destp);
+ video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(dest, &vdestp) : AST_RTP_GET_FAILED;
+ text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(dest, &tdestp) : AST_RTP_GET_FAILED;
+ audio_src_res = srcpr->get_rtp_info(src, &srcp);
+ video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(src, &vsrcp) : AST_RTP_GET_FAILED;
+ text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(src, &tsrcp) : AST_RTP_GET_FAILED;
+
+ /* Ensure we have at least one matching codec */
+ if (srcpr->get_codec)
+ srccodec = srcpr->get_codec(src);
+ else
+ srccodec = 0;
+ if (destpr->get_codec)
+ destcodec = destpr->get_codec(dest);
+ else
+ destcodec = 0;
+
+ /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
+ if (audio_dest_res != AST_RTP_TRY_NATIVE || audio_src_res != AST_RTP_TRY_NATIVE || !(srccodec & destcodec)) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ return 0;
+ }
+ ast_rtp_pt_copy(destp, srcp);
+ if (vdestp && vsrcp)
+ ast_rtp_pt_copy(vdestp, vsrcp);
+ if (tdestp && tsrcp)
+ ast_rtp_pt_copy(tdestp, tsrcp);
+ if (media) {
+ /* Bridge early */
+ if (destpr->set_rtp_peer(dest, srcp, vsrcp, tsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE)))
+ ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", dest->name, src->name);
+ }
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ ast_debug(1, "Seeded SDP of '%s' with that of '%s'\n", dest->name, src->name);
+ return 1;
+}
+
+/*! \brief Make a note of a RTP payload type that was seen in a SDP "m=" line.
+ * By default, use the well-known value for this type (although it may
+ * still be set to a different value by a subsequent "a=rtpmap:" line)
+ */
+void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt)
+{
+ if (pt < 0 || pt > MAX_RTP_PT || static_RTP_PT[pt].code == 0)
+ return; /* bogus payload type */
+
+ rtp_bridge_lock(rtp);
+ rtp->current_RTP_PT[pt] = static_RTP_PT[pt];
+ rtp_bridge_unlock(rtp);
+}
+
+/*! \brief remove setting from payload type list if the rtpmap header indicates
+ an unknown media type */
+void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt)
+{
+ rtp_bridge_lock(rtp);
+ rtp->current_RTP_PT[pt].isAstFormat = 0;
+ rtp->current_RTP_PT[pt].code = 0;
+ rtp_bridge_unlock(rtp);
+}
+
+/*! \brief Make a note of a RTP payload type (with MIME type) that was seen in
+ * an SDP "a=rtpmap:" line.
+ * \return 0 if the MIME type was found and set, -1 if it wasn't found
+ */
+int ast_rtp_set_rtpmap_type(struct ast_rtp *rtp, int pt,
+ char *mimeType, char *mimeSubtype,
+ enum ast_rtp_options options)
+{
+ unsigned int i;
+ int found = 0;
+
+ if (pt < 0 || pt > MAX_RTP_PT)
+ return -1; /* bogus payload type */
+
+ rtp_bridge_lock(rtp);
+
+ for (i = 0; i < sizeof(mimeTypes)/sizeof(mimeTypes[0]); ++i) {
+ if (strcasecmp(mimeSubtype, mimeTypes[i].subtype) == 0 &&
+ strcasecmp(mimeType, mimeTypes[i].type) == 0) {
+ found = 1;
+ rtp->current_RTP_PT[pt] = mimeTypes[i].payloadType;
+ if ((mimeTypes[i].payloadType.code == AST_FORMAT_G726) &&
+ mimeTypes[i].payloadType.isAstFormat &&
+ (options & AST_RTP_OPT_G726_NONSTANDARD))
+ rtp->current_RTP_PT[pt].code = AST_FORMAT_G726_AAL2;
+ break;
+ }
+ }
+
+ rtp_bridge_unlock(rtp);
+
+ return (found ? 0 : -1);
+}
+
+/*! \brief Return the union of all of the codecs that were set by rtp_set...() calls
+ * They're returned as two distinct sets: AST_FORMATs, and AST_RTPs */
+void ast_rtp_get_current_formats(struct ast_rtp* rtp,
+ int* astFormats, int* nonAstFormats)
+{
+ int pt;
+
+ rtp_bridge_lock(rtp);
+
+ *astFormats = *nonAstFormats = 0;
+ for (pt = 0; pt < MAX_RTP_PT; ++pt) {
+ if (rtp->current_RTP_PT[pt].isAstFormat) {
+ *astFormats |= rtp->current_RTP_PT[pt].code;
+ } else {
+ *nonAstFormats |= rtp->current_RTP_PT[pt].code;
+ }
+ }
+
+ rtp_bridge_unlock(rtp);
+
+ return;
+}
+
+struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt)
+{
+ struct rtpPayloadType result;
+
+ result.isAstFormat = result.code = 0;
+
+ if (pt < 0 || pt > MAX_RTP_PT)
+ return result; /* bogus payload type */
+
+ /* Start with negotiated codecs */
+ rtp_bridge_lock(rtp);
+ result = rtp->current_RTP_PT[pt];
+ rtp_bridge_unlock(rtp);
+
+ /* If it doesn't exist, check our static RTP type list, just in case */
+ if (!result.code)
+ result = static_RTP_PT[pt];
+
+ return result;
+}
+
+/*! \brief Looks up an RTP code out of our *static* outbound list */
+int ast_rtp_lookup_code(struct ast_rtp* rtp, const int isAstFormat, const int code)
+{
+ int pt = 0;
+
+ rtp_bridge_lock(rtp);
+
+ if (isAstFormat == rtp->rtp_lookup_code_cache_isAstFormat &&
+ code == rtp->rtp_lookup_code_cache_code) {
+ /* Use our cached mapping, to avoid the overhead of the loop below */
+ pt = rtp->rtp_lookup_code_cache_result;
+ rtp_bridge_unlock(rtp);
+ return pt;
+ }
+
+ /* Check the dynamic list first */
+ for (pt = 0; pt < MAX_RTP_PT; ++pt) {
+ if (rtp->current_RTP_PT[pt].code == code && rtp->current_RTP_PT[pt].isAstFormat == isAstFormat) {
+ rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat;
+ rtp->rtp_lookup_code_cache_code = code;
+ rtp->rtp_lookup_code_cache_result = pt;
+ rtp_bridge_unlock(rtp);
+ return pt;
+ }
+ }
+
+ /* Then the static list */
+ for (pt = 0; pt < MAX_RTP_PT; ++pt) {
+ if (static_RTP_PT[pt].code == code && static_RTP_PT[pt].isAstFormat == isAstFormat) {
+ rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat;
+ rtp->rtp_lookup_code_cache_code = code;
+ rtp->rtp_lookup_code_cache_result = pt;
+ rtp_bridge_unlock(rtp);
+ return pt;
+ }
+ }
+
+ rtp_bridge_unlock(rtp);
+
+ return -1;
+}
+
+const char *ast_rtp_lookup_mime_subtype(const int isAstFormat, const int code,
+ enum ast_rtp_options options)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(mimeTypes)/sizeof(mimeTypes[0]); ++i) {
+ if ((mimeTypes[i].payloadType.code == code) && (mimeTypes[i].payloadType.isAstFormat == isAstFormat)) {
+ if (isAstFormat &&
+ (code == AST_FORMAT_G726_AAL2) &&
+ (options & AST_RTP_OPT_G726_NONSTANDARD))
+ return "G726-32";
+ else
+ return mimeTypes[i].subtype;
+ }
+ }
+
+ return "";
+}
+
+char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability,
+ const int isAstFormat, enum ast_rtp_options options)
+{
+ int format;
+ unsigned len;
+ char *end = buf;
+ char *start = buf;
+
+ if (!buf || !size)
+ return NULL;
+
+ snprintf(end, size, "0x%x (", capability);
+
+ len = strlen(end);
+ end += len;
+ size -= len;
+ start = end;
+
+ for (format = 1; format < AST_RTP_MAX; format <<= 1) {
+ if (capability & format) {
+ const char *name = ast_rtp_lookup_mime_subtype(isAstFormat, format, options);
+
+ snprintf(end, size, "%s|", name);
+ len = strlen(end);
+ end += len;
+ size -= len;
+ }
+ }
+
+ if (start == end)
+ ast_copy_string(start, "nothing)", size);
+ else if (size > 1)
+ *(end -1) = ')';
+
+ return buf;
+}
+
+/*! \brief Open RTP or RTCP socket for a session.
+ * Print a message on failure.
+ */
+static int rtp_socket(const char *type)
+{
+ int s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (type == NULL)
+ type = "RTP/RTCP";
+ ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno));
+ } else {
+ long flags = fcntl(s, F_GETFL);
+ fcntl(s, F_SETFL, flags | O_NONBLOCK);
+#ifdef SO_NO_CHECK
+ if (nochecksums)
+ setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
+#endif
+ }
+ return s;
+}
+
+/*!
+ * \brief Initialize a new RTCP session.
+ *
+ * \returns The newly initialized RTCP session.
+ */
+static struct ast_rtcp *ast_rtcp_new(void)
+{
+ struct ast_rtcp *rtcp;
+
+ if (!(rtcp = ast_calloc(1, sizeof(*rtcp))))
+ return NULL;
+ rtcp->s = rtp_socket("RTCP");
+ rtcp->us.sin_family = AF_INET;
+ rtcp->them.sin_family = AF_INET;
+
+ if (rtcp->s < 0) {
+ ast_free(rtcp);
+ return NULL;
+ }
+
+ return rtcp;
+}
+
+/*!
+ * \brief Initialize a new RTP structure.
+ *
+ */
+void ast_rtp_new_init(struct ast_rtp *rtp)
+{
+#ifdef P2P_INTENSE
+ ast_mutex_init(&rtp->bridge_lock);
+#endif
+
+ rtp->them.sin_family = AF_INET;
+ rtp->us.sin_family = AF_INET;
+ rtp->ssrc = ast_random();
+ rtp->seqno = ast_random() & 0xffff;
+ ast_set_flag(rtp, FLAG_HAS_DTMF);
+
+ return;
+}
+
+struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr addr)
+{
+ struct ast_rtp *rtp;
+ int x;
+ int startplace;
+
+ if (!(rtp = ast_calloc(1, sizeof(*rtp))))
+ return NULL;
+
+ ast_rtp_new_init(rtp);
+
+ rtp->s = rtp_socket("RTP");
+ if (rtp->s < 0)
+ goto fail;
+ if (sched && rtcpenable) {
+ rtp->sched = sched;
+ rtp->rtcp = ast_rtcp_new();
+ }
+
+ /*
+ * Try to bind the RTP port, x, and possibly the RTCP port, x+1 as well.
+ * Start from a random (even, by RTP spec) port number, and
+ * iterate until success or no ports are available.
+ * Note that the requirement of RTP port being even, or RTCP being the
+ * next one, cannot be enforced in presence of a NAT box because the
+ * mapping is not under our control.
+ */
+ x = (ast_random() % (rtpend-rtpstart)) + rtpstart;
+ x = x & ~1; /* make it an even number */
+ startplace = x; /* remember the starting point */
+ /* this is constant across the loop */
+ rtp->us.sin_addr = addr;
+ if (rtp->rtcp)
+ rtp->rtcp->us.sin_addr = addr;
+ for (;;) {
+ rtp->us.sin_port = htons(x);
+ if (!bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) {
+ /* bind succeeded, if no rtcp then we are done */
+ if (!rtp->rtcp)
+ break;
+ /* have rtcp, try to bind it */
+ rtp->rtcp->us.sin_port = htons(x + 1);
+ if (!bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us)))
+ break; /* success again, we are really done */
+ /*
+ * RTCP bind failed, so close and recreate the
+ * already bound RTP socket for the next round.
+ */
+ close(rtp->s);
+ rtp->s = rtp_socket("RTP");
+ if (rtp->s < 0)
+ goto fail;
+ }
+ /*
+ * If we get here, there was an error in one of the bind()
+ * calls, so make sure it is nothing unexpected.
+ */
+ if (errno != EADDRINUSE) {
+ /* We got an error that wasn't expected, abort! */
+ ast_log(LOG_ERROR, "Unexpected bind error: %s\n", strerror(errno));
+ goto fail;
+ }
+ /*
+ * One of the ports is in use. For the next iteration,
+ * increment by two and handle wraparound.
+ * If we reach the starting point, then declare failure.
+ */
+ x += 2;
+ if (x > rtpend)
+ x = (rtpstart + 1) & ~1;
+ if (x == startplace) {
+ ast_log(LOG_ERROR, "No RTP ports remaining. Can't setup media stream for this call.\n");
+ goto fail;
+ }
+ }
+ rtp->sched = sched;
+ rtp->io = io;
+ if (callbackmode) {
+ rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
+ ast_set_flag(rtp, FLAG_CALLBACK_MODE);
+ }
+ ast_rtp_pt_default(rtp);
+ return rtp;
+
+fail:
+ if (rtp->s >= 0)
+ close(rtp->s);
+ if (rtp->rtcp) {
+ close(rtp->rtcp->s);
+ ast_free(rtp->rtcp);
+ }
+ ast_free(rtp);
+ return NULL;
+}
+
+struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode)
+{
+ struct in_addr ia;
+
+ memset(&ia, 0, sizeof(ia));
+ return ast_rtp_new_with_bindaddr(sched, io, rtcpenable, callbackmode, ia);
+}
+
+int ast_rtp_setqos(struct ast_rtp *rtp, int tos, int cos, char *desc)
+{
+ return ast_netsock_set_qos(rtp->s, tos, cos, desc);
+}
+
+void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
+{
+ rtp->them.sin_port = them->sin_port;
+ rtp->them.sin_addr = them->sin_addr;
+ if (rtp->rtcp) {
+ rtp->rtcp->them.sin_port = htons(ntohs(them->sin_port) + 1);
+ rtp->rtcp->them.sin_addr = them->sin_addr;
+ }
+ rtp->rxseqno = 0;
+}
+
+int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
+{
+ if ((them->sin_family != AF_INET) ||
+ (them->sin_port != rtp->them.sin_port) ||
+ (them->sin_addr.s_addr != rtp->them.sin_addr.s_addr)) {
+ them->sin_family = AF_INET;
+ them->sin_port = rtp->them.sin_port;
+ them->sin_addr = rtp->them.sin_addr;
+ return 1;
+ }
+ return 0;
+}
+
+void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us)
+{
+ *us = rtp->us;
+}
+
+struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp)
+{
+ struct ast_rtp *bridged = NULL;
+
+ rtp_bridge_lock(rtp);
+ bridged = rtp->bridged;
+ rtp_bridge_unlock(rtp);
+
+ return bridged;
+}
+
+void ast_rtp_stop(struct ast_rtp *rtp)
+{
+ if (rtp->rtcp && rtp->rtcp->schedid > 0) {
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ }
+
+ memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
+ memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port));
+ if (rtp->rtcp) {
+ memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr));
+ memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port));
+ }
+
+ ast_clear_flag(rtp, FLAG_P2P_SENT_MARK);
+}
+
+void ast_rtp_reset(struct ast_rtp *rtp)
+{
+ memset(&rtp->rxcore, 0, sizeof(rtp->rxcore));
+ memset(&rtp->txcore, 0, sizeof(rtp->txcore));
+ memset(&rtp->dtmfmute, 0, sizeof(rtp->dtmfmute));
+ rtp->lastts = 0;
+ rtp->lastdigitts = 0;
+ rtp->lastrxts = 0;
+ rtp->lastividtimestamp = 0;
+ rtp->lastovidtimestamp = 0;
+ rtp->lastitexttimestamp = 0;
+ rtp->lastotexttimestamp = 0;
+ rtp->lasteventseqn = 0;
+ rtp->lastevent = 0;
+ rtp->lasttxformat = 0;
+ rtp->lastrxformat = 0;
+ rtp->dtmfcount = 0;
+ rtp->dtmfsamples = 0;
+ rtp->seqno = 0;
+ rtp->rxseqno = 0;
+}
+
+char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual)
+{
+ /*
+ *ssrc our ssrc
+ *themssrc their ssrc
+ *lp lost packets
+ *rxjitter our calculated jitter(rx)
+ *rxcount no. received packets
+ *txjitter reported jitter of the other end
+ *txcount transmitted packets
+ *rlp remote lost packets
+ *rtt round trip time
+ */
+
+ if (qual && rtp) {
+ qual->local_ssrc = rtp->ssrc;
+ qual->local_jitter = rtp->rxjitter;
+ qual->local_count = rtp->rxcount;
+ qual->remote_ssrc = rtp->themssrc;
+ qual->remote_count = rtp->txcount;
+ if (rtp->rtcp) {
+ qual->local_lostpackets = rtp->rtcp->expected_prior - rtp->rtcp->received_prior;
+ qual->remote_lostpackets = rtp->rtcp->reported_lost;
+ qual->remote_jitter = rtp->rtcp->reported_jitter / 65536.0;
+ qual->rtt = rtp->rtcp->rtt;
+ }
+ }
+ if (rtp->rtcp) {
+ snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality),
+ "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f",
+ rtp->ssrc,
+ rtp->themssrc,
+ rtp->rtcp->expected_prior - rtp->rtcp->received_prior,
+ rtp->rxjitter,
+ rtp->rxcount,
+ (double)rtp->rtcp->reported_jitter / 65536.0,
+ rtp->txcount,
+ rtp->rtcp->reported_lost,
+ rtp->rtcp->rtt);
+ return rtp->rtcp->quality;
+ } else
+ return "<Unknown> - RTP/RTCP has already been destroyed";
+}
+
+void ast_rtp_destroy(struct ast_rtp *rtp)
+{
+ if (rtcp_debug_test_addr(&rtp->them) || rtcpstats) {
+ /*Print some info on the call here */
+ ast_verbose(" RTP-stats\n");
+ ast_verbose("* Our Receiver:\n");
+ ast_verbose(" SSRC: %u\n", rtp->themssrc);
+ ast_verbose(" Received packets: %u\n", rtp->rxcount);
+ ast_verbose(" Lost packets: %u\n", rtp->rtcp->expected_prior - rtp->rtcp->received_prior);
+ ast_verbose(" Jitter: %.4f\n", rtp->rxjitter);
+ ast_verbose(" Transit: %.4f\n", rtp->rxtransit);
+ ast_verbose(" RR-count: %u\n", rtp->rtcp->rr_count);
+ ast_verbose("* Our Sender:\n");
+ ast_verbose(" SSRC: %u\n", rtp->ssrc);
+ ast_verbose(" Sent packets: %u\n", rtp->txcount);
+ ast_verbose(" Lost packets: %u\n", rtp->rtcp->reported_lost);
+ ast_verbose(" Jitter: %u\n", rtp->rtcp->reported_jitter / (unsigned int)65536.0);
+ ast_verbose(" SR-count: %u\n", rtp->rtcp->sr_count);
+ ast_verbose(" RTT: %f\n", rtp->rtcp->rtt);
+ }
+
+ manager_event(EVENT_FLAG_REPORTING, "RTPReceiverStat", "SSRC: %u\r\n"
+ "ReceivedPackets: %u\r\n"
+ "LostPackets: %u\r\n"
+ "Jitter: %.4f\r\n"
+ "Transit: %.4f\r\n"
+ "RRCount: %u\r\n",
+ rtp->themssrc,
+ rtp->rxcount,
+ rtp->rtcp->expected_prior - rtp->rtcp->received_prior,
+ rtp->rxjitter,
+ rtp->rxtransit,
+ rtp->rtcp->rr_count);
+ manager_event(EVENT_FLAG_REPORTING, "RTPSenderStat", "SSRC: %u\r\n"
+ "SentPackets: %u\r\n"
+ "LostPackets: %u\r\n"
+ "Jitter: %u\r\n"
+ "SRCount: %u\r\n"
+ "RTT: %f\r\n",
+ rtp->ssrc,
+ rtp->txcount,
+ rtp->rtcp->reported_lost,
+ rtp->rtcp->reported_jitter,
+ rtp->rtcp->sr_count,
+ rtp->rtcp->rtt);
+ if (rtp->smoother)
+ ast_smoother_free(rtp->smoother);
+ if (rtp->ioid)
+ ast_io_remove(rtp->io, rtp->ioid);
+ if (rtp->s > -1)
+ close(rtp->s);
+ if (rtp->rtcp) {
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ close(rtp->rtcp->s);
+ ast_free(rtp->rtcp);
+ rtp->rtcp=NULL;
+ }
+#ifdef P2P_INTENSE
+ ast_mutex_destroy(&rtp->bridge_lock);
+#endif
+ ast_free(rtp);
+}
+
+static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery)
+{
+ struct timeval t;
+ long ms;
+ if (ast_tvzero(rtp->txcore)) {
+ rtp->txcore = ast_tvnow();
+ /* Round to 20ms for nice, pretty timestamps */
+ rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000;
+ }
+ /* Use previous txcore if available */
+ t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow();
+ ms = ast_tvdiff_ms(t, rtp->txcore);
+ if (ms < 0)
+ ms = 0;
+ /* Use what we just got for next time */
+ rtp->txcore = t;
+ return (unsigned int) ms;
+}
+
+/*! \brief Send begin frames for DTMF */
+int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12, res = 0, i = 0, payload = 0;
+ char data[256];
+
+ if ((digit <= '9') && (digit >= '0'))
+ digit -= '0';
+ else if (digit == '*')
+ digit = 10;
+ else if (digit == '#')
+ digit = 11;
+ else if ((digit >= 'A') && (digit <= 'D'))
+ digit = digit - 'A' + 12;
+ else if ((digit >= 'a') && (digit <= 'd'))
+ digit = digit - 'a' + 12;
+ else {
+ ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
+ return 0;
+ }
+
+ /* If we have no peer, return immediately */
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return 0;
+
+ payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF);
+
+ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
+ rtp->send_duration = 160;
+
+ /* Get a pointer to the header */
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno));
+ rtpheader[1] = htonl(rtp->lastdigitts);
+ rtpheader[2] = htonl(rtp->ssrc);
+
+ for (i = 0; i < 2; i++) {
+ rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration));
+ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+ if (res < 0)
+ ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+ /* Increment sequence number */
+ rtp->seqno++;
+ /* Increment duration */
+ rtp->send_duration += 160;
+ /* Clear marker bit and set seqno */
+ rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno));
+ }
+
+ /* Since we received a begin, we can safely store the digit and disable any compensation */
+ rtp->sending_digit = 1;
+ rtp->send_digit = digit;
+ rtp->send_payload = payload;
+
+ return 0;
+}
+
+/*! \brief Send continuation frame for DTMF */
+static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12, res = 0;
+ char data[256];
+
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return 0;
+
+ /* Setup packet to send */
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno));
+ rtpheader[1] = htonl(rtp->lastdigitts);
+ rtpheader[2] = htonl(rtp->ssrc);
+ rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (rtp->send_duration));
+ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
+
+ /* Transmit */
+ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+ if (res < 0)
+ ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+
+ /* Increment sequence number */
+ rtp->seqno++;
+ /* Increment duration */
+ rtp->send_duration += 160;
+
+ return 0;
+}
+
+/*! \brief Send end packets for DTMF */
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12, res = 0, i = 0;
+ char data[256];
+
+ /* If no address, then bail out */
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return 0;
+
+ if ((digit <= '9') && (digit >= '0'))
+ digit -= '0';
+ else if (digit == '*')
+ digit = 10;
+ else if (digit == '#')
+ digit = 11;
+ else if ((digit >= 'A') && (digit <= 'D'))
+ digit = digit - 'A' + 12;
+ else if ((digit >= 'a') && (digit <= 'd'))
+ digit = digit - 'a' + 12;
+ else {
+ ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
+ return 0;
+ }
+
+ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
+
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno));
+ rtpheader[1] = htonl(rtp->lastdigitts);
+ rtpheader[2] = htonl(rtp->ssrc);
+ rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration));
+ /* Set end bit */
+ rtpheader[3] |= htonl((1 << 23));
+ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
+ /* Send 3 termination packets */
+ for (i = 0; i < 3; i++) {
+ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+ if (res < 0)
+ ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+ }
+ rtp->sending_digit = 0;
+ rtp->send_digit = 0;
+ /* Increment lastdigitts */
+ rtp->lastdigitts += 960;
+ rtp->seqno++;
+
+ return res;
+}
+
+/*! \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */
+int ast_rtcp_send_h261fur(void *data)
+{
+ struct ast_rtp *rtp = data;
+ int res;
+
+ rtp->rtcp->sendfur = 1;
+ res = ast_rtcp_write(data);
+
+ return res;
+}
+
+/*! \brief Send RTCP sender's report */
+static int ast_rtcp_write_sr(const void *data)
+{
+ struct ast_rtp *rtp = (struct ast_rtp *)data;
+ int res;
+ int len = 0;
+ struct timeval now;
+ unsigned int now_lsw;
+ unsigned int now_msw;
+ unsigned int *rtcpheader;
+ unsigned int lost;
+ unsigned int extended;
+ unsigned int expected;
+ unsigned int expected_interval;
+ unsigned int received_interval;
+ int lost_interval;
+ int fraction;
+ struct timeval dlsr;
+ char bdata[512];
+
+ /* Commented condition is always not NULL if rtp->rtcp is not NULL */
+ if (!rtp || !rtp->rtcp/* || (&rtp->rtcp->them.sin_addr == 0)*/)
+ return 0;
+
+ if (!rtp->rtcp->them.sin_addr.s_addr) { /* This'll stop rtcp for this rtp session */
+ ast_verbose("RTCP SR transmission error, rtcp halted\n");
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ gettimeofday(&now, NULL);
+ timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[1] = htonl(rtp->ssrc); /* Our SSRC */
+ rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/
+ rtcpheader[3] = htonl(now_lsw); /* now, LSW */
+ rtcpheader[4] = htonl(rtp->lastts); /* FIXME shouldn't be that, it should be now */
+ rtcpheader[5] = htonl(rtp->txcount); /* No. packets sent */
+ rtcpheader[6] = htonl(rtp->txoctetcount); /* No. bytes sent */
+ len += 28;
+
+ extended = rtp->cycles + rtp->lastrxseqno;
+ expected = extended - rtp->seedrxseqno + 1;
+ if (rtp->rxcount > expected)
+ expected += rtp->rxcount - expected;
+ lost = expected - rtp->rxcount;
+ expected_interval = expected - rtp->rtcp->expected_prior;
+ rtp->rtcp->expected_prior = expected;
+ received_interval = rtp->rxcount - rtp->rtcp->received_prior;
+ rtp->rtcp->received_prior = rtp->rxcount;
+ lost_interval = expected_interval - received_interval;
+ if (expected_interval == 0 || lost_interval <= 0)
+ fraction = 0;
+ else
+ fraction = (lost_interval << 8) / expected_interval;
+ timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
+ rtcpheader[7] = htonl(rtp->themssrc);
+ rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
+ rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
+ rtcpheader[10] = htonl((unsigned int)(rtp->rxjitter * 65536.));
+ rtcpheader[11] = htonl(rtp->rtcp->themrxlsr);
+ rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
+ len += 24;
+
+ rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1));
+
+ if (rtp->rtcp->sendfur) {
+ rtcpheader[13] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1);
+ rtcpheader[14] = htonl(rtp->ssrc); /* Our SSRC */
+ len += 8;
+ rtp->rtcp->sendfur = 0;
+ }
+
+ /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */
+ /* it can change mid call, and SDES can't) */
+ rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
+ rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */
+ rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */
+ len += 12;
+
+ res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno));
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ /* FIXME Don't need to get a new one */
+ gettimeofday(&rtp->rtcp->txlsr, NULL);
+ rtp->rtcp->sr_count++;
+
+ rtp->rtcp->lastsrtxcount = rtp->txcount;
+
+ if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
+ ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ ast_verbose(" Our SSRC: %u\n", rtp->ssrc);
+ ast_verbose(" Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096);
+ ast_verbose(" Sent(RTP): %u\n", rtp->lastts);
+ ast_verbose(" Sent packets: %u\n", rtp->txcount);
+ ast_verbose(" Sent octets: %u\n", rtp->txoctetcount);
+ ast_verbose(" Report block:\n");
+ ast_verbose(" Fraction lost: %u\n", fraction);
+ ast_verbose(" Cumulative loss: %u\n", lost);
+ ast_verbose(" IA jitter: %.4f\n", rtp->rxjitter);
+ ast_verbose(" Their last SR: %u\n", rtp->rtcp->themrxlsr);
+ ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0));
+ }
+ manager_event(EVENT_FLAG_REPORTING, "RTCPSent", "To %s:%d\r\n"
+ "OurSSRC: %u\r\n"
+ "SentNTP: %u.%010u\r\n"
+ "SentRTP: %u\r\n"
+ "SentPackets: %u\r\n"
+ "SentOctets: %u\r\n"
+ "ReportBlock:\r\n"
+ "FractionLost: %u\r\n"
+ "CumulativeLoss: %u\r\n"
+ "IAJitter: %.4f\r\n"
+ "TheirLastSR: %u\r\n"
+ "DLSR: %4.4f (sec)\r\n",
+ ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port),
+ rtp->ssrc,
+ (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096,
+ rtp->lastts,
+ rtp->txcount,
+ rtp->txoctetcount,
+ fraction,
+ lost,
+ rtp->rxjitter,
+ rtp->rtcp->themrxlsr,
+ (double)(ntohl(rtcpheader[12])/65536.0));
+ return res;
+}
+
+/*! \brief Send RTCP recipient's report */
+static int ast_rtcp_write_rr(const void *data)
+{
+ struct ast_rtp *rtp = (struct ast_rtp *)data;
+ int res;
+ int len = 32;
+ unsigned int lost;
+ unsigned int extended;
+ unsigned int expected;
+ unsigned int expected_interval;
+ unsigned int received_interval;
+ int lost_interval;
+ struct timeval now;
+ unsigned int *rtcpheader;
+ char bdata[1024];
+ struct timeval dlsr;
+ int fraction;
+
+ if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0))
+ return 0;
+
+ if (!rtp->rtcp->them.sin_addr.s_addr) {
+ ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted\n");
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ extended = rtp->cycles + rtp->lastrxseqno;
+ expected = extended - rtp->seedrxseqno + 1;
+ lost = expected - rtp->rxcount;
+ expected_interval = expected - rtp->rtcp->expected_prior;
+ rtp->rtcp->expected_prior = expected;
+ received_interval = rtp->rxcount - rtp->rtcp->received_prior;
+ rtp->rtcp->received_prior = rtp->rxcount;
+ lost_interval = expected_interval - received_interval;
+ if (expected_interval == 0 || lost_interval <= 0)
+ fraction = 0;
+ else
+ fraction = (lost_interval << 8) / expected_interval;
+ gettimeofday(&now, NULL);
+ timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1));
+ rtcpheader[1] = htonl(rtp->ssrc);
+ rtcpheader[2] = htonl(rtp->themssrc);
+ rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
+ rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
+ rtcpheader[5] = htonl((unsigned int)(rtp->rxjitter * 65536.));
+ rtcpheader[6] = htonl(rtp->rtcp->themrxlsr);
+ rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
+
+ if (rtp->rtcp->sendfur) {
+ rtcpheader[8] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); /* Header from page 36 in RFC 3550 */
+ rtcpheader[9] = htonl(rtp->ssrc); /* Our SSRC */
+ len += 8;
+ rtp->rtcp->sendfur = 0;
+ }
+
+ /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos
+ it can change mid call, and SDES can't) */
+ rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
+ rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */
+ rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */
+ len += 12;
+
+ res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
+
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno));
+ /* Remove the scheduler */
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ rtp->rtcp->rr_count++;
+
+ if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
+ ast_verbose("\n* Sending RTCP RR to %s:%d\n"
+ " Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n"
+ " IA jitter: %.4f\n"
+ " Their last SR: %u\n"
+ " DLSR: %4.4f (sec)\n\n",
+ ast_inet_ntoa(rtp->rtcp->them.sin_addr),
+ ntohs(rtp->rtcp->them.sin_port),
+ rtp->ssrc, rtp->themssrc, fraction, lost,
+ rtp->rxjitter,
+ rtp->rtcp->themrxlsr,
+ (double)(ntohl(rtcpheader[7])/65536.0));
+ }
+
+ return res;
+}
+
+/*! \brief Write and RTCP packet to the far end
+ * \note Decide if we are going to send an SR (with Reception Block) or RR
+ * RR is sent if we have not sent any rtp packets in the previous interval */
+static int ast_rtcp_write(const void *data)
+{
+ struct ast_rtp *rtp = (struct ast_rtp *)data;
+ int res;
+
+ if (!rtp || !rtp->rtcp)
+ return 0;
+
+ if (rtp->txcount > rtp->rtcp->lastsrtxcount)
+ res = ast_rtcp_write_sr(data);
+ else
+ res = ast_rtcp_write_rr(data);
+
+ return res;
+}
+
+/*! \brief generate comfort noice (CNG) */
+int ast_rtp_sendcng(struct ast_rtp *rtp, int level)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12;
+ int res;
+ int payload;
+ char data[256];
+ level = 127 - (level & 0x7f);
+ payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_CN);
+
+ /* If we have no peer, return immediately */
+ if (!rtp->them.sin_addr.s_addr)
+ return 0;
+
+ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
+
+ /* Get a pointer to the header */
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno++));
+ rtpheader[1] = htonl(rtp->lastts);
+ rtpheader[2] = htonl(rtp->ssrc);
+ data[12] = level;
+ if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
+ res = sendto(rtp->s, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them));
+ if (res <0)
+ ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent Comfort Noise RTP packet to %s:%u (type %d, seq %u, ts %u, len %d)\n"
+ , ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen);
+
+ }
+ return 0;
+}
+
+/*! \brief Write RTP packet with audio or video media frames into UDP packet */
+static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec)
+{
+ unsigned char *rtpheader;
+ int hdrlen = 12;
+ int res;
+ unsigned int ms;
+ int pred;
+ int mark = 0;
+
+ ms = calc_txstamp(rtp, &f->delivery);
+ /* Default prediction */
+ if (f->subclass & AST_FORMAT_AUDIO_MASK) {
+ pred = rtp->lastts + f->samples;
+
+ /* Re-calculate last TS */
+ rtp->lastts = rtp->lastts + ms * 8;
+ if (ast_tvzero(f->delivery)) {
+ /* If this isn't an absolute delivery time, Check if it is close to our prediction,
+ and if so, go with our prediction */
+ if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW)
+ rtp->lastts = pred;
+ else {
+ ast_debug(3, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
+ mark = 1;
+ }
+ }
+ } else if(f->subclass & AST_FORMAT_VIDEO_MASK) {
+ mark = f->subclass & 0x1;
+ pred = rtp->lastovidtimestamp + f->samples;
+ /* Re-calculate last TS */
+ rtp->lastts = rtp->lastts + ms * 90;
+ /* If it's close to our prediction, go for it */
+ if (ast_tvzero(f->delivery)) {
+ if (abs(rtp->lastts - pred) < 7200) {
+ rtp->lastts = pred;
+ rtp->lastovidtimestamp += f->samples;
+ } else {
+ ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples);
+ rtp->lastovidtimestamp = rtp->lastts;
+ }
+ }
+ } else {
+ pred = rtp->lastotexttimestamp + f->samples;
+ /* Re-calculate last TS */
+ rtp->lastts = rtp->lastts + ms * 90;
+ /* If it's close to our prediction, go for it */
+ if (ast_tvzero(f->delivery)) {
+ if (abs(rtp->lastts - pred) < 7200) {
+ rtp->lastts = pred;
+ rtp->lastotexttimestamp += f->samples;
+ } else {
+ ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples);
+ rtp->lastotexttimestamp = rtp->lastts;
+ }
+ }
+ }
+ /* If the timestamp for non-digit packets has moved beyond the timestamp
+ for digits, update the digit timestamp.
+ */
+ if (rtp->lastts > rtp->lastdigitts)
+ rtp->lastdigitts = rtp->lastts;
+
+ if (ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO))
+ rtp->lastts = f->ts * 8;
+
+ /* Get a pointer to the header */
+ rtpheader = (unsigned char *)(f->data - hdrlen);
+
+ put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23)));
+ put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
+ put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
+
+ if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
+ res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them));
+ if (res <0) {
+ if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
+ ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
+ } else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) {
+ /* Only give this error message once if we are not RTP debugging */
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+ ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN);
+ }
+ } else {
+ rtp->txcount++;
+ rtp->txoctetcount +=(res - hdrlen);
+
+ if (rtp->rtcp && rtp->rtcp->schedid < 1)
+ rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
+ }
+
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), codec, rtp->seqno, rtp->lastts,res - hdrlen);
+ }
+
+ rtp->seqno++;
+
+ return 0;
+}
+
+int ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs)
+{
+ int x;
+ for (x = 0; x < 32; x++) { /* Ugly way */
+ rtp->pref.order[x] = prefs->order[x];
+ rtp->pref.framing[x] = prefs->framing[x];
+ }
+ if (rtp->smoother)
+ ast_smoother_free(rtp->smoother);
+ rtp->smoother = NULL;
+ return 0;
+}
+
+struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp)
+{
+ return &rtp->pref;
+}
+
+int ast_rtp_codec_getformat(int pt)
+{
+ if (pt < 0 || pt > MAX_RTP_PT)
+ return 0; /* bogus payload type */
+
+ if (static_RTP_PT[pt].isAstFormat)
+ return static_RTP_PT[pt].code;
+ else
+ return 0;
+}
+
+int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
+{
+ struct ast_frame *f;
+ int codec;
+ int hdrlen = 12;
+ int subclass;
+
+
+ /* If we have no peer, return immediately */
+ if (!rtp->them.sin_addr.s_addr)
+ return 0;
+
+ /* If there is no data length, return immediately */
+ if (!_f->datalen)
+ return 0;
+
+ /* Make sure we have enough space for RTP header */
+ if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO) && (_f->frametype != AST_FRAME_TEXT)) {
+ ast_log(LOG_WARNING, "RTP can only send voice, video and text\n");
+ return -1;
+ }
+
+ /* The bottom bit of a video subclass contains the marker bit */
+ subclass = _f->subclass;
+ if (_f->frametype == AST_FRAME_VIDEO)
+ subclass &= ~0x1;
+
+ codec = ast_rtp_lookup_code(rtp, 1, subclass);
+ if (codec < 0) {
+ ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(_f->subclass));
+ return -1;
+ }
+
+ if (rtp->lasttxformat != subclass) {
+ /* New format, reset the smoother */
+ ast_debug(1, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass));
+ rtp->lasttxformat = subclass;
+ if (rtp->smoother)
+ ast_smoother_free(rtp->smoother);
+ rtp->smoother = NULL;
+ }
+
+ if (!rtp->smoother && subclass != AST_FORMAT_SPEEX && subclass != AST_FORMAT_G723_1) {
+ struct ast_format_list fmt = ast_codec_pref_getsize(&rtp->pref, subclass);
+ if (fmt.inc_ms) { /* if codec parameters is set / avoid division by zero */
+ if (!(rtp->smoother = ast_smoother_new((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms))) {
+ ast_log(LOG_WARNING, "Unable to create smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms));
+ return -1;
+ }
+ if (fmt.flags)
+ ast_smoother_set_flags(rtp->smoother, fmt.flags);
+ ast_debug(1, "Created smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms));
+ }
+ }
+ if (rtp->smoother) {
+ if (ast_smoother_test_flag(rtp->smoother, AST_SMOOTHER_FLAG_BE)) {
+ ast_smoother_feed_be(rtp->smoother, _f);
+ } else {
+ ast_smoother_feed(rtp->smoother, _f);
+ }
+
+ while ((f = ast_smoother_read(rtp->smoother)) && (f->data))
+ ast_rtp_raw_write(rtp, f, codec);
+ } else {
+ /* Don't buffer outgoing frames; send them one-per-packet: */
+ if (_f->offset < hdrlen)
+ f = ast_frdup(_f); /*! \bug XXX this might never be free'd. Why do we do this? */
+ else
+ f = _f;
+ if (f->data)
+ ast_rtp_raw_write(rtp, f, codec);
+ if (f != _f)
+ ast_frfree(f);
+ }
+
+ return 0;
+}
+
+/*! \brief Unregister interface to channel driver */
+void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto)
+{
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_REMOVE(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+}
+
+/*! \brief Register interface to channel driver */
+int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
+{
+ struct ast_rtp_protocol *cur;
+
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (!strcmp(cur->type, proto->type)) {
+ ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
+ AST_RWLIST_UNLOCK(&protos);
+ return -1;
+ }
+ }
+ AST_RWLIST_INSERT_HEAD(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+
+ return 0;
+}
+
+/*! \brief Bridge loop for true native bridge (reinvite) */
+static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp *tp0, struct ast_rtp *tp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
+{
+ struct ast_frame *fr = NULL;
+ struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
+ int oldcodec0 = codec0, oldcodec1 = codec1;
+ struct sockaddr_in ac1 = {0,}, vac1 = {0,}, tac1 = {0,}, ac0 = {0,}, vac0 = {0,}, tac0 = {0,};
+ struct sockaddr_in t1 = {0,}, vt1 = {0,}, tt1 = {0,}, t0 = {0,}, vt0 = {0,}, tt0 = {0,};
+
+ /* Set it up so audio goes directly between the two endpoints */
+
+ /* Test the first channel */
+ if (!(pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) {
+ ast_rtp_get_peer(p1, &ac1);
+ if (vp1)
+ ast_rtp_get_peer(vp1, &vac1);
+ if (tp1)
+ ast_rtp_get_peer(tp1, &tac1);
+ } else
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
+
+ /* Test the second channel */
+ if (!(pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) {
+ ast_rtp_get_peer(p0, &ac0);
+ if (vp0)
+ ast_rtp_get_peer(vp0, &vac0);
+ if (tp0)
+ ast_rtp_get_peer(tp0, &tac0);
+ } else
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name);
+
+ /* Now we can unlock and move into our loop */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ ast_poll_channel_add(c0, c1);
+
+ /* Throw our channels into the structure and enter the loop */
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ /* Check if anything changed */
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr) ||
+ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
+ ast_debug(1, "Oooh, something is weird, backing out\n");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (c1->tech_pvt == pvt1)
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ ast_poll_channel_del(c0, c1);
+ return AST_BRIDGE_RETRY;
+ }
+
+ /* Check if they have changed their address */
+ ast_rtp_get_peer(p1, &t1);
+ if (vp1)
+ ast_rtp_get_peer(vp1, &vt1);
+ if (tp1)
+ ast_rtp_get_peer(tp1, &tt1);
+ if (pr1->get_codec)
+ codec1 = pr1->get_codec(c1);
+ ast_rtp_get_peer(p0, &t0);
+ if (vp0)
+ ast_rtp_get_peer(vp0, &vt0);
+ if (tp0)
+ ast_rtp_get_peer(tp0, &tt0);
+ if (pr0->get_codec)
+ codec0 = pr0->get_codec(c0);
+ if ((inaddrcmp(&t1, &ac1)) ||
+ (vp1 && inaddrcmp(&vt1, &vac1)) ||
+ (tp1 && inaddrcmp(&tt1, &tac1)) ||
+ (codec1 != oldcodec1)) {
+ ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
+ c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1);
+ ast_debug(2, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n",
+ c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1);
+ ast_debug(2, "Oooh, '%s' changed end taddress to %s:%d (format %d)\n",
+ c1->name, ast_inet_ntoa(tt1.sin_addr), ntohs(tt1.sin_port), codec1);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(tac1.sin_addr), ntohs(tac1.sin_port), oldcodec1);
+ if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, tt1.sin_addr.s_addr ? tp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))
+ ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name);
+ memcpy(&ac1, &t1, sizeof(ac1));
+ memcpy(&vac1, &vt1, sizeof(vac1));
+ memcpy(&tac1, &tt1, sizeof(tac1));
+ oldcodec1 = codec1;
+ }
+ if ((inaddrcmp(&t0, &ac0)) ||
+ (vp0 && inaddrcmp(&vt0, &vac0)) ||
+ (tp0 && inaddrcmp(&tt0, &tac0))) {
+ ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
+ c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0);
+ if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, tt0.sin_addr.s_addr ? tp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))
+ ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name);
+ memcpy(&ac0, &t0, sizeof(ac0));
+ memcpy(&vac0, &vt0, sizeof(vac0));
+ memcpy(&tac0, &tt0, sizeof(tac0));
+ oldcodec0 = codec0;
+ }
+
+ /* Wait for frame to come in on the channels */
+ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
+ if (!timeoutms) {
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ return AST_BRIDGE_RETRY;
+ }
+ ast_debug(1, "Ooh, empty read...\n");
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ fr = ast_read(who);
+ other = (who == c0) ? c1 : c0;
+ if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) &&
+ (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
+ /* Break out of bridge */
+ *fo = fr;
+ *rc = who;
+ ast_debug(1, "Oooh, got a %s\n", fr ? "digit" : "hangup");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (c1->tech_pvt == pvt1)
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ ast_poll_channel_del(c0, c1);
+ return AST_BRIDGE_COMPLETE;
+ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+ if ((fr->subclass == AST_CONTROL_HOLD) ||
+ (fr->subclass == AST_CONTROL_UNHOLD) ||
+ (fr->subclass == AST_CONTROL_VIDUPDATE)) {
+ if (fr->subclass == AST_CONTROL_HOLD) {
+ /* If we someone went on hold we want the other side to reinvite back to us */
+ if (who == c0)
+ pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0);
+ else
+ pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0);
+ } else if (fr->subclass == AST_CONTROL_UNHOLD) {
+ /* If they went off hold they should go back to being direct */
+ if (who == c0)
+ pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE));
+ else
+ pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE));
+ }
+ /* Update local address information */
+ ast_rtp_get_peer(p0, &t0);
+ memcpy(&ac0, &t0, sizeof(ac0));
+ ast_rtp_get_peer(p1, &t1);
+ memcpy(&ac1, &t1, sizeof(ac1));
+ /* Update codec information */
+ if (pr0->get_codec && c0->tech_pvt)
+ oldcodec0 = codec0 = pr0->get_codec(c0);
+ if (pr1->get_codec && c1->tech_pvt)
+ oldcodec1 = codec1 = pr1->get_codec(c1);
+ ast_indicate_data(other, fr->subclass, fr->data, fr->datalen);
+ ast_frfree(fr);
+ } else {
+ *fo = fr;
+ *rc = who;
+ ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
+ return AST_BRIDGE_COMPLETE;
+ }
+ } else {
+ if ((fr->frametype == AST_FRAME_DTMF_BEGIN) ||
+ (fr->frametype == AST_FRAME_DTMF_END) ||
+ (fr->frametype == AST_FRAME_VOICE) ||
+ (fr->frametype == AST_FRAME_VIDEO) ||
+ (fr->frametype == AST_FRAME_IMAGE) ||
+ (fr->frametype == AST_FRAME_HTML) ||
+ (fr->frametype == AST_FRAME_MODEM) ||
+ (fr->frametype == AST_FRAME_TEXT)) {
+ ast_write(other, fr);
+ }
+ ast_frfree(fr);
+ }
+ /* Swap priority */
+#ifndef HAVE_EPOLL
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+#endif
+ }
+
+ ast_poll_channel_del(c0, c1);
+
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+
+ return AST_BRIDGE_FAILED;
+}
+
+/*! \brief P2P RTP Callback */
+#ifdef P2P_INTENSE
+static int p2p_rtp_callback(int *id, int fd, short events, void *cbdata)
+{
+ int res = 0, hdrlen = 12;
+ struct sockaddr_in sin;
+ socklen_t len;
+ unsigned int *header;
+ struct ast_rtp *rtp = cbdata, *bridged = NULL;
+
+ if (!rtp)
+ return 1;
+
+ len = sizeof(sin);
+ if ((res = recvfrom(fd, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0)
+ return 1;
+
+ header = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
+
+ /* If NAT support is turned on, then see if we need to change their address */
+ if ((rtp->nat) &&
+ ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (rtp->them.sin_port != sin.sin_port))) {
+ rtp->them = sin;
+ rtp->rxseqno = 0;
+ ast_set_flag(rtp, FLAG_NAT_ACTIVE);
+ if (option_debug || rtpdebug)
+ ast_debug(0, "P2P RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+ }
+
+ /* Write directly out to other RTP stream if bridged */
+ if ((bridged = ast_rtp_get_bridged(rtp)))
+ bridge_p2p_rtp_write(rtp, bridged, header, res, hdrlen);
+
+ return 1;
+}
+
+/*! \brief Helper function to switch a channel and RTP stream into callback mode */
+static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod)
+{
+ /* If we need DTMF, are looking for STUN, or we have no IO structure then we can't do direct callback */
+ if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) || ast_test_flag(rtp, FLAG_HAS_STUN) || !rtp->io)
+ return 0;
+
+ /* If the RTP structure is already in callback mode, remove it temporarily */
+ if (rtp->ioid) {
+ ast_io_remove(rtp->io, rtp->ioid);
+ rtp->ioid = NULL;
+ }
+
+ /* Steal the file descriptors from the channel */
+ chan->fds[0] = -1;
+
+ /* Now, fire up callback mode */
+ iod[0] = ast_io_add(rtp->io, ast_rtp_fd(rtp), p2p_rtp_callback, AST_IO_IN, rtp);
+
+ return 1;
+}
+#else
+static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod)
+{
+ return 0;
+}
+#endif
+
+/*! \brief Helper function to switch a channel and RTP stream out of callback mode */
+static int p2p_callback_disable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod)
+{
+ ast_channel_lock(chan);
+
+ /* Remove the callback from the IO context */
+ ast_io_remove(rtp->io, iod[0]);
+
+ /* Restore file descriptors */
+ chan->fds[0] = ast_rtp_fd(rtp);
+ ast_channel_unlock(chan);
+
+ /* Restore callback mode if previously used */
+ if (ast_test_flag(rtp, FLAG_CALLBACK_MODE))
+ rtp->ioid = ast_io_add(rtp->io, ast_rtp_fd(rtp), rtpread, AST_IO_IN, rtp);
+
+ return 0;
+}
+
+/*! \brief Helper function that sets what an RTP structure is bridged to */
+static void p2p_set_bridge(struct ast_rtp *rtp0, struct ast_rtp *rtp1)
+{
+ rtp_bridge_lock(rtp0);
+ rtp0->bridged = rtp1;
+ rtp_bridge_unlock(rtp0);
+
+ return;
+}
+
+/*! \brief Bridge loop for partial native bridge (packet2packet)
+
+ In p2p mode, Asterisk is a very basic RTP proxy, just forwarding whatever
+ rtp/rtcp we get in to the channel.
+ \note this currently only works for Audio
+*/
+static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
+{
+ struct ast_frame *fr = NULL;
+ struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
+ int *p0_iod[2] = {NULL, NULL}, *p1_iod[2] = {NULL, NULL};
+ int p0_callback = 0, p1_callback = 0;
+ enum ast_bridge_result res = AST_BRIDGE_FAILED;
+
+ /* Okay, setup each RTP structure to do P2P forwarding */
+ ast_clear_flag(p0, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p0, p1);
+ ast_clear_flag(p1, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p1, p0);
+
+ /* Activate callback modes if possible */
+ p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]);
+ p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]);
+
+ /* Now let go of the channel locks and be on our way */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ ast_poll_channel_add(c0, c1);
+
+ /* Go into a loop forwarding frames until we don't need to anymore */
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ /* If the underlying formats have changed force this bridge to break */
+ if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) {
+ ast_debug(3, "p2p-rtp-bridge: Oooh, formats changed, backing out\n");
+ res = AST_BRIDGE_FAILED_NOWARN;
+ break;
+ }
+ /* Check if anything changed */
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr) ||
+ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
+ ast_debug(3, "p2p-rtp-bridge: Oooh, something is weird, backing out\n");
+ /* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */
+ if ((c0->masq || c0->masqr) && (fr = ast_read(c0)))
+ ast_frfree(fr);
+ if ((c1->masq || c1->masqr) && (fr = ast_read(c1)))
+ ast_frfree(fr);
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ /* Wait on a channel to feed us a frame */
+ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
+ if (!timeoutms) {
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ if (option_debug > 2)
+ ast_log(LOG_NOTICE, "p2p-rtp-bridge: Ooh, empty read...\n");
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ /* Read in frame from channel */
+ fr = ast_read(who);
+ other = (who == c0) ? c1 : c0;
+ /* Depending on the frame we may need to break out of our bridge */
+ if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) &&
+ ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) |
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) {
+ /* Record received frame and who */
+ *fo = fr;
+ *rc = who;
+ ast_debug(3, "p2p-rtp-bridge: Ooh, got a %s\n", fr ? "digit" : "hangup");
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+ if ((fr->subclass == AST_CONTROL_HOLD) ||
+ (fr->subclass == AST_CONTROL_UNHOLD) ||
+ (fr->subclass == AST_CONTROL_VIDUPDATE)) {
+ /* If we are going on hold, then break callback mode and P2P bridging */
+ if (fr->subclass == AST_CONTROL_HOLD) {
+ if (p0_callback)
+ p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]);
+ if (p1_callback)
+ p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]);
+ p2p_set_bridge(p0, NULL);
+ p2p_set_bridge(p1, NULL);
+ } else if (fr->subclass == AST_CONTROL_UNHOLD) {
+ /* If we are off hold, then go back to callback mode and P2P bridging */
+ ast_clear_flag(p0, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p0, p1);
+ ast_clear_flag(p1, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p1, p0);
+ p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]);
+ p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]);
+ }
+ ast_indicate_data(other, fr->subclass, fr->data, fr->datalen);
+ ast_frfree(fr);
+ } else {
+ *fo = fr;
+ *rc = who;
+ ast_debug(3, "p2p-rtp-bridge: Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ } else {
+ if ((fr->frametype == AST_FRAME_DTMF_BEGIN) ||
+ (fr->frametype == AST_FRAME_DTMF_END) ||
+ (fr->frametype == AST_FRAME_VOICE) ||
+ (fr->frametype == AST_FRAME_VIDEO) ||
+ (fr->frametype == AST_FRAME_IMAGE) ||
+ (fr->frametype == AST_FRAME_HTML) ||
+ (fr->frametype == AST_FRAME_MODEM) ||
+ (fr->frametype == AST_FRAME_TEXT)) {
+ ast_write(other, fr);
+ }
+
+ ast_frfree(fr);
+ }
+ /* Swap priority */
+#ifndef HAVE_EPOLL
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+#endif
+ }
+
+ /* If we are totally avoiding the core, then restore our link to it */
+ if (p0_callback)
+ p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]);
+ if (p1_callback)
+ p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]);
+
+ /* Break out of the direct bridge */
+ p2p_set_bridge(p0, NULL);
+ p2p_set_bridge(p1, NULL);
+
+ ast_poll_channel_del(c0, c1);
+
+ return res;
+}
+
+/*! \page AstRTPbridge The Asterisk RTP bridge
+ The RTP bridge is called from the channel drivers that are using the RTP
+ subsystem in Asterisk - like SIP, H.323 and Jingle/Google Talk.
+
+ This bridge aims to offload the Asterisk server by setting up
+ the media stream directly between the endpoints, keeping the
+ signalling in Asterisk.
+
+ It checks with the channel driver, using a callback function, if
+ there are possibilities for a remote bridge.
+
+ If this fails, the bridge hands off to the core bridge. Reasons
+ can be NAT support needed, DTMF features in audio needed by
+ the PBX for transfers or spying/monitoring on channels.
+
+ If transcoding is needed - we can't do a remote bridge.
+ If only NAT support is needed, we're using Asterisk in
+ RTP proxy mode with the p2p RTP bridge, basically
+ forwarding incoming audio packets to the outbound
+ stream on a network level.
+
+ References:
+ - ast_rtp_bridge()
+ - ast_channel_early_bridge()
+ - ast_channel_bridge()
+ - rtp.c
+ - rtp.h
+*/
+/*! \brief Bridge calls. If possible and allowed, initiate
+ re-invite so the peers exchange media directly outside
+ of Asterisk.
+*/
+enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+{
+ struct ast_rtp *p0 = NULL, *p1 = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vp0 = NULL, *vp1 = NULL; /* Video RTP channels */
+ struct ast_rtp *tp0 = NULL, *tp1 = NULL; /* Text RTP channels */
+ struct ast_rtp_protocol *pr0 = NULL, *pr1 = NULL;
+ enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED, text_p0_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED, text_p1_res = AST_RTP_GET_FAILED;
+ enum ast_bridge_result res = AST_BRIDGE_FAILED;
+ int codec0 = 0, codec1 = 0;
+ void *pvt0 = NULL, *pvt1 = NULL;
+
+ /* Lock channels */
+ ast_channel_lock(c0);
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+
+ /* Find channel driver interfaces */
+ if (!(pr0 = get_proto(c0))) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+ if (!(pr1 = get_proto(c1))) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+
+ /* Get channel specific interface structures */
+ pvt0 = c0->tech_pvt;
+ pvt1 = c1->tech_pvt;
+
+ /* Get audio and video interface (if native bridge is possible) */
+ audio_p0_res = pr0->get_rtp_info(c0, &p0);
+ video_p0_res = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0, &vp0) : AST_RTP_GET_FAILED;
+ text_p0_res = pr0->get_trtp_info ? pr0->get_trtp_info(c0, &vp0) : AST_RTP_GET_FAILED;
+ audio_p1_res = pr1->get_rtp_info(c1, &p1);
+ video_p1_res = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1, &vp1) : AST_RTP_GET_FAILED;
+ text_p1_res = pr1->get_trtp_info ? pr1->get_trtp_info(c1, &vp1) : AST_RTP_GET_FAILED;
+
+ /* If we are carrying video, and both sides are not reinviting... then fail the native bridge */
+ if (video_p0_res != AST_RTP_GET_FAILED && (audio_p0_res != AST_RTP_TRY_NATIVE || video_p0_res != AST_RTP_TRY_NATIVE))
+ audio_p0_res = AST_RTP_GET_FAILED;
+ if (video_p1_res != AST_RTP_GET_FAILED && (audio_p1_res != AST_RTP_TRY_NATIVE || video_p1_res != AST_RTP_TRY_NATIVE))
+ audio_p1_res = AST_RTP_GET_FAILED;
+
+ /* Check if a bridge is possible (partial/native) */
+ if (audio_p0_res == AST_RTP_GET_FAILED || audio_p1_res == AST_RTP_GET_FAILED) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ /* If we need to feed DTMF frames into the core then only do a partial native bridge */
+ if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
+ ast_set_flag(p0, FLAG_P2P_NEED_DTMF);
+ audio_p0_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ if (ast_test_flag(p1, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) {
+ ast_set_flag(p1, FLAG_P2P_NEED_DTMF);
+ audio_p1_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ /* If both sides are not using the same method of DTMF transmission
+ * (ie: one is RFC2833, other is INFO... then we can not do direct media.
+ * --------------------------------------------------
+ * | DTMF Mode | HAS_DTMF | Accepts Begin Frames |
+ * |-----------|------------|-----------------------|
+ * | Inband | False | True |
+ * | RFC2833 | True | True |
+ * | SIP INFO | False | False |
+ * --------------------------------------------------
+ * However, if DTMF from both channels is being monitored by the core, then
+ * we can still do packet-to-packet bridging, because passing through the
+ * core will handle DTMF mode translation.
+ */
+ if ( (ast_test_flag(p0, FLAG_HAS_DTMF) != ast_test_flag(p1, FLAG_HAS_DTMF)) ||
+ (!c0->tech->send_digit_begin != !c1->tech->send_digit_begin)) {
+ if (!ast_test_flag(p0, FLAG_P2P_NEED_DTMF) || !ast_test_flag(p1, FLAG_P2P_NEED_DTMF)) {
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+ audio_p0_res = AST_RTP_TRY_PARTIAL;
+ audio_p1_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ /* If the core will need to compensate and the P2P bridge will need to feed up DTMF frames then we can not reliably do so yet, so do not P2P bridge */
+ if ((audio_p0_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p0, FLAG_P2P_NEED_DTMF) && ast_test_flag(p0, FLAG_DTMF_COMPENSATE)) ||
+ (audio_p1_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p1, FLAG_P2P_NEED_DTMF) && ast_test_flag(p1, FLAG_DTMF_COMPENSATE))) {
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ /* Get codecs from both sides */
+ codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0;
+ codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0;
+ if (codec0 && codec1 && !(codec0 & codec1)) {
+ /* Hey, we can't do native bridging if both parties speak different codecs */
+ ast_debug(3, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ /* If either side can only do a partial bridge, then don't try for a true native bridge */
+ if (audio_p0_res == AST_RTP_TRY_PARTIAL || audio_p1_res == AST_RTP_TRY_PARTIAL) {
+ struct ast_format_list fmt0, fmt1;
+
+ /* In order to do Packet2Packet bridging both sides must be in the same rawread/rawwrite */
+ if (c0->rawreadformat != c1->rawwriteformat || c1->rawreadformat != c0->rawwriteformat) {
+ ast_debug(1, "Cannot packet2packet bridge - raw formats are incompatible\n");
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+ /* They must also be using the same packetization */
+ fmt0 = ast_codec_pref_getsize(&p0->pref, c0->rawreadformat);
+ fmt1 = ast_codec_pref_getsize(&p1->pref, c1->rawreadformat);
+ if (fmt0.cur_ms != fmt1.cur_ms) {
+ ast_debug(1, "Cannot packet2packet bridge - packetization settings prevent it\n");
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ ast_verb(3, "Packet2Packet bridging %s and %s\n", c0->name, c1->name);
+ res = bridge_p2p_loop(c0, c1, p0, p1, timeoutms, flags, fo, rc, pvt0, pvt1);
+ } else {
+ ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name);
+ res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, tp0, tp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1);
+ }
+
+ return res;
+}
+
+static char *rtp_do_debug_ip(struct ast_cli_args *a)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p, *arg;
+
+ arg = a->argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL) {
+ ast_cli(a->fd, "Lookup failed for '%s'\n", arg);
+ return CLI_FAILURE;
+ }
+ rtpdebugaddr.sin_family = AF_INET;
+ memcpy(&rtpdebugaddr.sin_addr, hp->h_addr, sizeof(rtpdebugaddr.sin_addr));
+ rtpdebugaddr.sin_port = htons(port);
+ if (port == 0)
+ ast_cli(a->fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtpdebugaddr.sin_addr));
+ else
+ ast_cli(a->fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtpdebugaddr.sin_addr), port);
+ rtpdebug = 1;
+ return CLI_SUCCESS;
+}
+
+static char *rtcp_do_debug_ip(struct ast_cli_args *a)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p, *arg;
+
+ arg = a->argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL) {
+ ast_cli(a->fd, "Lookup failed for '%s'\n", arg);
+ return CLI_FAILURE;
+ }
+ rtcpdebugaddr.sin_family = AF_INET;
+ memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr));
+ rtcpdebugaddr.sin_port = htons(port);
+ if (port == 0)
+ ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr));
+ else
+ ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port);
+ rtcpdebug = 1;
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_rtp_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rtp debug [off|ip]";
+ e->usage =
+ "Usage: rtp debug [off]|[ip host[:port]]\n"
+ " Enable/Disable dumping of all RTP packets. If 'ip' is\n"
+ " specified, limit the dumped packets to those to and from\n"
+ " the specified 'host' with optional port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 2) {
+ rtpdebug = 1;
+ memset(&rtpdebugaddr, 0, sizeof(rtpdebugaddr));
+ ast_cli(a->fd, "RTP Debugging Enabled\n");
+ } else if (a->argc == 3) {
+ if (strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+ rtpdebug = 0;
+ ast_cli(a->fd, "RTP Debugging Disabled\n");
+ } else {
+ if (strncasecmp(a->argv[2], "ip", 2))
+ return CLI_SHOWUSAGE;
+ return rtp_do_debug_ip(a);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_rtcp_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rtcp debug [off|ip]";
+ e->usage =
+ "Usage: rtcp debug [off]|[ip host[:port]]\n"
+ " Enable/Disable dumping of all RTCP packets. If 'ip' is\n"
+ " specified, limit the dumped packets to those to and from\n"
+ " the specified 'host' with optional port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 2) {
+ rtcpdebug = 1;
+ memset(&rtcpdebugaddr, 0, sizeof(rtcpdebugaddr));
+ ast_cli(a->fd, "RTCP Debugging Enabled\n");
+ } else if (a->argc == 3) {
+ if (strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+ rtcpdebug = 0;
+ ast_cli(a->fd, "RTCP Debugging Disabled\n");
+ } else {
+ if (strncasecmp(a->argv[2], "ip", 2))
+ return CLI_SHOWUSAGE;
+ return rtcp_do_debug_ip(a);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_rtcp_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rtcp stats [off]";
+ e->usage =
+ "Usage: rtcp stats [off]\n"
+ " Enable/Disable dumping of RTCP stats.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 3)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 3 && strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+
+ rtcpstats = (a->argc == 3) ? 0 : 1;
+ ast_cli(a->fd, "RTCP Stats %s\n", rtcpstats ? "Enabled" : "Disabled");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_stun_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stun debug [off]";
+ e->usage =
+ "Usage: stun debug [off]\n"
+ " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n"
+ " debugging\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 3)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 3 && strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+
+ stundebug = (a->argc == 3) ? 0 : 1;
+ ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled");
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_rtp[] = {
+ AST_CLI_DEFINE(handle_cli_rtp_debug, "Enable/Disable RTP debugging"),
+ AST_CLI_DEFINE(handle_cli_rtcp_debug, "Enable/Disable RTCP debugging"),
+ AST_CLI_DEFINE(handle_cli_rtcp_stats, "Enable/Disable RTCP stats"),
+ AST_CLI_DEFINE(handle_cli_stun_debug, "Enable/Disable STUN debugging"),
+};
+
+static int __ast_rtp_reload(int reload)
+{
+ struct ast_config *cfg;
+ const char *s;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("rtp.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ rtpstart = 5000;
+ rtpend = 31000;
+ dtmftimeout = DEFAULT_DTMF_TIMEOUT;
+ if (cfg) {
+ if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) {
+ rtpstart = atoi(s);
+ if (rtpstart < 1024)
+ rtpstart = 1024;
+ if (rtpstart > 65535)
+ rtpstart = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) {
+ rtpend = atoi(s);
+ if (rtpend < 1024)
+ rtpend = 1024;
+ if (rtpend > 65535)
+ rtpend = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) {
+ rtcpinterval = atoi(s);
+ if (rtcpinterval == 0)
+ rtcpinterval = 0; /* Just so we're clear... it's zero */
+ if (rtcpinterval < RTCP_MIN_INTERVALMS)
+ rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */
+ if (rtcpinterval > RTCP_MAX_INTERVALMS)
+ rtcpinterval = RTCP_MAX_INTERVALMS;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) {
+#ifdef SO_NO_CHECK
+ if (ast_false(s))
+ nochecksums = 1;
+ else
+ nochecksums = 0;
+#else
+ if (ast_false(s))
+ ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n");
+#endif
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) {
+ dtmftimeout = atoi(s);
+ if ((dtmftimeout < 0) || (dtmftimeout > 20000)) {
+ ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n",
+ dtmftimeout, DEFAULT_DTMF_TIMEOUT);
+ dtmftimeout = DEFAULT_DTMF_TIMEOUT;
+ };
+ }
+ ast_config_destroy(cfg);
+ }
+ if (rtpstart >= rtpend) {
+ ast_log(LOG_WARNING, "Unreasonable values for RTP start/end port in rtp.conf\n");
+ rtpstart = 5000;
+ rtpend = 31000;
+ }
+ ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend);
+ return 0;
+}
+
+int ast_rtp_reload(void)
+{
+ return __ast_rtp_reload(1);
+}
+
+/*! \brief Initialize the RTP system in Asterisk */
+void ast_rtp_init(void)
+{
+ ast_cli_register_multiple(cli_rtp, sizeof(cli_rtp) / sizeof(struct ast_cli_entry));
+ __ast_rtp_reload(0);
+}
+
diff --git a/trunk/main/say.c b/trunk/main/say.c
new file mode 100644
index 000000000..da660d6fc
--- /dev/null
+++ b/trunk/main/say.c
@@ -0,0 +1,7461 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * George Konstantoulakis <gkon@inaccessnetworks.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 Say numbers and dates (maybe words one day too)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
+ *
+ * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
+ * Next Generation Networks (NGN).
+ * \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,
+ * IP Crossing Co.,Ltd.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <netinet/in.h>
+#include <time.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef SOLARIS
+#include <iso/limits_iso.h>
+#endif
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/say.h"
+#include "asterisk/lock.h"
+#include "asterisk/localtime.h"
+#include "asterisk/utils.h"
+
+/* Forward declaration */
+static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
+
+
+static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ const char *fn;
+ char fnbuf[10], asciibuf[20] = "letters/ascii";
+ char ltr;
+ int num = 0;
+ int res = 0;
+
+ while (str[num] && !res) {
+ fn = NULL;
+ switch (str[num]) {
+ case ('*'):
+ fn = "digits/star";
+ break;
+ case ('#'):
+ fn = "digits/pound";
+ break;
+ case ('!'):
+ fn = "letters/exclaimation-point";
+ break;
+ case ('@'):
+ fn = "letters/at";
+ break;
+ case ('$'):
+ fn = "letters/dollar";
+ break;
+ case ('-'):
+ fn = "letters/dash";
+ break;
+ case ('.'):
+ fn = "letters/dot";
+ break;
+ case ('='):
+ fn = "letters/equals";
+ break;
+ case ('+'):
+ fn = "letters/plus";
+ break;
+ case ('/'):
+ fn = "letters/slash";
+ break;
+ case (' '):
+ fn = "letters/space";
+ break;
+ case ('0'):
+ case ('1'):
+ case ('2'):
+ case ('3'):
+ case ('4'):
+ case ('5'):
+ case ('6'):
+ case ('7'):
+ case ('8'):
+ case ('9'):
+ strcpy(fnbuf, "digits/X");
+ fnbuf[7] = str[num];
+ fn = fnbuf;
+ break;
+ default:
+ ltr = str[num];
+ if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
+ strcpy(fnbuf, "letters/X");
+ fnbuf[8] = ltr;
+ fn = fnbuf;
+ }
+ if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
+ (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
+ res = ast_streamfile(chan, fn, lang);
+ if (!res) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ num++;
+ }
+
+ return res;
+}
+
+static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ const char *fn;
+ char fnbuf[256];
+ char ltr;
+ int num = 0;
+ int res = 0;
+
+ while (str[num] && !res) {
+ fn = NULL;
+ switch (str[num]) {
+ case ('*'):
+ fn = "digits/star";
+ break;
+ case ('#'):
+ fn = "digits/pound";
+ break;
+ case ('!'):
+ fn = "letters/exclaimation-point";
+ break;
+ case ('@'):
+ fn = "letters/at";
+ break;
+ case ('$'):
+ fn = "letters/dollar";
+ break;
+ case ('-'):
+ fn = "letters/dash";
+ break;
+ case ('.'):
+ fn = "letters/dot";
+ break;
+ case ('='):
+ fn = "letters/equals";
+ break;
+ case ('+'):
+ fn = "letters/plus";
+ break;
+ case ('/'):
+ fn = "letters/slash";
+ break;
+ case (' '):
+ fn = "letters/space";
+ break;
+ case ('0'):
+ case ('1'):
+ case ('2'):
+ case ('3'):
+ case ('4'):
+ case ('5'):
+ case ('6'):
+ case ('7'):
+ case ('8'):
+ strcpy(fnbuf, "digits/X");
+ fnbuf[7] = str[num];
+ fn = fnbuf;
+ break;
+ default: /* '9' falls here... */
+ ltr = str[num];
+ if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
+ strcpy(fnbuf, "phonetic/X_p");
+ fnbuf[9] = ltr;
+ fn = fnbuf;
+ }
+ if (fn && ast_fileexists(fn, NULL, lang) > 0) {
+ res = ast_streamfile(chan, fn, lang);
+ if (!res) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ num++;
+ }
+
+ return res;
+}
+
+static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ const char *fn;
+ char fnbuf[256];
+ int num = 0;
+ int res = 0;
+
+ while (str[num] && !res) {
+ fn = NULL;
+ switch (str[num]) {
+ case ('*'):
+ fn = "digits/star";
+ break;
+ case ('#'):
+ fn = "digits/pound";
+ break;
+ case ('-'):
+ fn = "digits/minus";
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ strcpy(fnbuf, "digits/X");
+ fnbuf[7] = str[num];
+ fn = fnbuf;
+ break;
+ }
+ if (fn && ast_fileexists(fn, NULL, lang) > 0) {
+ res = ast_streamfile(chan, fn, lang);
+ if (!res) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ num++;
+ }
+
+ return res;
+}
+
+/* Forward declarations */
+/*! \page Def_syntaxlang Asterisk Language Syntaxes supported
+ \note Not really language codes.
+ For these language codes, Asterisk will change the syntax when
+ saying numbers (and in some cases dates and voicemail messages
+ as well)
+ \arg \b da - Danish
+ \arg \b de - German
+ \arg \b en - English (US)
+ \arg \b en_GB - English (British)
+ \arg \b es - Spanish, Mexican
+ \arg \b fr - French
+ \arg \b he - Hebrew
+ \arg \b it - Italian
+ \arg \b nl - Dutch
+ \arg \b no - Norwegian
+ \arg \b pl - Polish
+ \arg \b pt - Portuguese
+ \arg \b pt_BR - Portuguese (Brazil)
+ \arg \b se - Swedish
+ \arg \b tw - Taiwanese / Chinese
+ \arg \b ru - Russian
+ \arg \b ge - Georgian
+ \arg \b hu - Hungarian
+
+ \par Gender:
+ For Some languages the numbers differ for gender and plural.
+ \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
+ \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
+ use the option argument 'p' for plural enumerations like in German
+
+ Date/Time functions currently have less languages supported than saynumber().
+
+ \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
+
+ See contrib/i18n.testsuite.conf for some examples of the different syntaxes
+
+ \par Portuguese
+ Portuguese sound files needed for Time/Date functions:
+ pt-ah
+ pt-ao
+ pt-de
+ pt-e
+ pt-ora
+ pt-meianoite
+ pt-meiodia
+ pt-sss
+
+ \par Spanish
+ Spanish sound files needed for Time/Date functions:
+ es-de
+ es-el
+
+ \par Italian
+ Italian sound files needed for Time/Date functions:
+ ore-una
+ ore-mezzanotte
+
+*/
+
+/* Forward declarations of language specific variants of ast_say_number_full */
+static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+
+/* Forward declarations of language specific variants of ast_say_enumeration_full */
+static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+
+/* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
+static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_th(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+
+static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
+{
+ int res;
+ if ((res = ast_streamfile(chan, file, lang)))
+ ast_log(LOG_WARNING, "Unable to play message %s\n", file);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+/*! \brief ast_say_number_full: call language-specific functions */
+/* Called from AGI */
+static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ if (!strcasecmp(language,"en") ) { /* English syntax */
+ return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
+ return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
+ return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "de") ) { /* German syntax */
+ return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
+ return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
+ return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
+ return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "fr") ) { /* French syntax */
+ return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
+ return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "hu") ) { /* Hungarian syntax */
+ return(ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
+ return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
+ return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
+ return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "pt") || !strcasecmp(language, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
+ return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
+ return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
+ return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "th") ) { /* Thai syntax */
+ return(ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "ge") ) { /* Georgian syntax */
+ return(ast_say_number_full_ge(chan, num, ints, language, options, audiofd, ctrlfd));
+ }
+
+ /* Default to english */
+ return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
+}
+
+/*! \brief ast_say_number_full_en: English syntax */
+/* This is the default syntax, if no other syntax defined in this file is used */
+static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+static int exp10_int(int power)
+{
+ int x, res= 1;
+ for (x=0;x<power;x++)
+ res *= 10;
+ return res;
+}
+
+/*! \brief ast_say_number_full_cz: Czech syntax */
+/* files needed:
+ * 1m,2m - gender male
+ * 1w,2w - gender female
+ * 3,4,...,20
+ * 30,40,...,90
+ *
+ * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
+ *
+ * for each number 10^(3n + 3) exist 3 files represented as:
+ * 1 tousand = jeden tisic = 1_E3
+ * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
+ * 5,6,... tousands = pet,sest,... tisic = 5_E3
+ *
+ * million = _E6
+ * miliard = _E9
+ * etc...
+ *
+ * tousand, milion are gender male, so 1 and 2 is 1m 2m
+ * miliard is gender female, so 1 and 2 is 1w 2w
+ */
+static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+
+ int hundered = 0;
+ int left = 0;
+ int length = 0;
+
+ /* options - w = woman, m = man, n = neutral. Defaultl is woman */
+ if (!options)
+ options = "w";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 3 ) {
+ snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
+ playh = 0;
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d",num);
+ playh = 0;
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 1000) {
+ hundered = num / 100;
+ if ( hundered == 1 ) {
+ ast_copy_string(fn, "digits/1sto", sizeof(fn));
+ } else if ( hundered == 2 ) {
+ ast_copy_string(fn, "digits/2ste", sizeof(fn));
+ } else {
+ res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
+ if (res)
+ return res;
+ if (hundered == 3 || hundered == 4) {
+ ast_copy_string(fn, "digits/sta", sizeof(fn));
+ } else if ( hundered > 4 ) {
+ ast_copy_string(fn, "digits/set", sizeof(fn));
+ }
+ }
+ num -= (hundered * 100);
+ } else { /* num > 1000 */
+ length = (int)log10(num)+1;
+ while ( (length % 3 ) != 1 ) {
+ length--;
+ }
+ left = num / (exp10_int(length-1));
+ if ( left == 2 ) {
+ switch (length-1) {
+ case 9: options = "w"; /* 1,000,000,000 gender female */
+ break;
+ default : options = "m"; /* others are male */
+ }
+ }
+ if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
+ res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
+ if (res)
+ return res;
+ }
+ if ( left >= 5 ) { /* >= 5 have the same declesion */
+ snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
+ } else if ( left >= 2 && left <= 4 ) {
+ snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
+ } else { /* left == 1 */
+ snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
+ }
+ num -= left * (exp10_int(length-1));
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_da: Danish syntax */
+/* New files:
+ In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
+ */
+static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ int cn = 1; /* +1 = commune; -1 = neuter */
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "n",1)) cn = -1;
+
+ while (!res && (num || playh || playa )) {
+ /* The grammar for Danish numbers is the same as for English except
+ * for the following:
+ * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
+ * - numbers 20 through 99 are said in reverse order, i.e. 21 is
+ * "one-and twenty" and 68 is "eight-and sixty".
+ * - "million" is different in singular and plural form
+ * - numbers > 1000 with zero as the third digit from last have an
+ * "and" before the last two digits, i.e. 2034 is "two thousand and
+ * four-and thirty" and 1000012 is "one million and twelve".
+ */
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num == 1 && cn == -1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ }
+ } else {
+ if (num < 1000) {
+ int hundreds = num / 100;
+ if (hundreds == 1)
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
+
+ playh++;
+ num -= 100 * hundreds;
+ if (num)
+ playa++;
+
+ } else {
+ if (num < 1000000) {
+ res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
+ if (res)
+ return res;
+ num = num % 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) {
+ int millions = num / 1000000;
+ res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (millions == 1)
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ else
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ num = num % 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ if (num && num < 100)
+ playa++;
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_de: German syntax */
+/* New files:
+ In addition to English, the following sounds are required:
+ "millions"
+ "1-and" through "9-and"
+ "1F" (eine)
+ "1N" (ein)
+ NB "1" is recorded as 'eins'
+ */
+static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0, t = 0;
+ int mf = 1; /* +1 = male and neuter; -1 = female */
+ char fn[256] = "";
+ char fna[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && (!strncasecmp(options, "f",1)))
+ mf = -1;
+
+ while (!res && num) {
+ /* The grammar for German numbers is the same as for English except
+ * for the following:
+ * - numbers 20 through 99 are said in reverse order, i.e. 21 is
+ * "one-and twenty" and 68 is "eight-and sixty".
+ * - "one" varies according to gender
+ * - 100 is 'hundert', however all other instances are 'ein hundert'
+ * - 1000 is 'tausend', however all other instances are 'ein tausend'
+ * - 1000000 is always 'eine million'
+ * - "million" is different in singular and plural form
+ */
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 100 && t) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ t = 0;
+ } else if (num == 1 && mf == -1) {
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ }
+ } else if (num == 100 && t == 0) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ num = 0;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ ast_copy_string(fna, "digits/hundred", sizeof(fna));
+ t = 1;
+ } else if (num == 1000 && t == 0) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = 0;
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ t = 1;
+ if (thousands == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ ast_copy_string(fna, "digits/thousand", sizeof(fna));
+ } else {
+ res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ }
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ t = 1;
+ if (millions == 1) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/million", sizeof(fna));
+ } else {
+ res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ }
+ } else if (num <= INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ t = 1;
+ if (billions == 1) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/milliard", sizeof(fna));
+ } else {
+ res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ ast_copy_string(fn, "digits/milliards", sizeof(fn));
+ }
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ if (!res) {
+ if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ strcpy(fna, "");
+ }
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
+/* New files:
+ In addition to American English, the following sounds are required: "and"
+ */
+static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh || playa )) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
+
+ playh++;
+ num -= 100 * hundreds;
+ if (num)
+ playa++;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num %= 1000;
+ if (num && num < 100)
+ playa++;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num %= 1000000;
+ if (num && num < 100)
+ playa++;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_es: Spanish syntax */
+/* New files:
+ Requires a few new audios:
+ 1F.gsm: feminine 'una'
+ 21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm
+ */
+static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playa = 0;
+ int mf = 0; /* +1 = male; -1 = female */
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options) {
+ if (!strncasecmp(options, "f",1))
+ mf = -1;
+ else if (!strncasecmp(options, "m", 1))
+ mf = 1;
+ }
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num == 1) {
+ if (mf < 0)
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else if (mf > 0)
+ snprintf(fn, sizeof(fn), "digits/%dM", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 31) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
+ num %= 10;
+ if (num)
+ playa++;
+ } else if (num == 100) {
+ ast_copy_string(fn, "digits/100", sizeof(fn));
+ num = 0;
+ } else if (num < 200) {
+ ast_copy_string(fn, "digits/100-and", sizeof(fn));
+ num -= 100;
+ } else {
+ if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
+ num %= 100;
+ } else if (num < 2000) {
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000) {
+ res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 2147483640) {
+ if ((num/1000000) == 1) {
+ res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ }
+ num %= 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+
+ }
+
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_fr: French syntax */
+/* Extra sounds needed:
+ 1F: feminin 'une'
+ et: 'and' */
+static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ int mf = 1; /* +1 = male; -1 = female */
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "f",1))
+ mf = -1;
+
+ while (!res && (num || playh || playa)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/et", sizeof(fn));
+ playa = 0;
+ } else if (num == 1) {
+ if (mf < 0)
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 21) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 70) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
+ if ((num % 10) == 1) playa++;
+ num = num % 10;
+ } else if (num < 80) {
+ ast_copy_string(fn, "digits/60", sizeof(fn));
+ if ((num % 10) == 1) playa++;
+ num -= 60;
+ } else if (num < 100) {
+ ast_copy_string(fn, "digits/80", sizeof(fn));
+ num = num - 80;
+ } else if (num < 200) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ num = num - 100;
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num = num % 100;
+ } else if (num < 2000) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num - 1000;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num % 1000;
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num = num % 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+
+
+/*! \brief ast_say_number_full_he: Hebrew syntax */
+/* Extra sounds needed:
+ 1F: feminin 'one'
+ ve: 'and'
+ 1hundred: 1 hundred
+ 2hundred: 2 hundreds
+ 2thousands: 2 thousand
+ thousands: plural of 'thousand'
+ 3sF 'Smichut forms (female)
+ 4sF
+ 5sF
+ 6sF
+ 7sF
+ 8sF
+ 9sF
+ 3s 'Smichut' forms (male)
+ 4s
+ 5s
+ 6s
+ 7s
+ 9s
+ 10s
+ 11s
+ 12s
+ 13s
+ 14s
+ 15s
+ 16s
+ 17s
+ 18s
+ 19s
+
+TODO: 've' should sometimed be 'hu':
+* before 'shtaym' (2, F)
+* before 'shnaym' (2, M)
+* before 'shlosha' (3, M)
+* before 'shmone' (8, M)
+* before 'shlosim' (30)
+* before 'shmonim' (80)
+
+What about:
+'sheva' (7, F)?
+'tesha' (9, F)?
+*/
+#define SAY_NUM_BUF_SIZE 256
+static int ast_say_number_full_he(struct ast_channel *chan, int num,
+ const char *ints, const char *language, const char *options,
+ int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int state = 0; /* no need to save anything */
+ int mf = 1; /* +1 = Masculin; -1 = Feminin */
+ char fn[SAY_NUM_BUF_SIZE] = "";
+ ast_verb(3, "ast_say_digits_full: started. "
+ "num: %d, options=\"%s\"\n",
+ num, options
+ );
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "f",1))
+ mf = -1;
+
+ /* Do we have work to do? */
+ while (!res && (num || (state>0) )) {
+ /* first type of work: play a second sound. In this loop
+ * we can only play one sound file at a time. Thus playing
+ * a second one requires repeating the loop just for the
+ * second file. The variable 'state' remembers where we were.
+ * state==0 is the normal mode and it means that we continue
+ * to check if the number num has yet anything left.
+ */
+ ast_verb(3, "ast_say_digits_full: num: %d, "
+ "state=%d, options=\"%s\", mf=%d\n",
+ num, state, options, mf
+ );
+ if (state==1) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ state = 0;
+ } else if (state==2) {
+ ast_copy_string(fn, "digits/ve", sizeof(fn));
+ state = 0;
+ } else if (state==3) {
+ ast_copy_string(fn, "digits/thousands", sizeof(fn));
+ state=0;
+ } else if (num <21) {
+ if (mf < 0)
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
+ num = num % 10;
+ if (num>0) state=2;
+ } else if (num < 200) {
+ ast_copy_string(fn, "digits/1hundred", sizeof(fn));
+ num = num - 100;
+ state=2;
+ } else if (num < 300) {
+ ast_copy_string(fn, "digits/2hundred", sizeof(fn));
+ num = num - 200;
+ state=2;
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ state=1;
+ num = num % 100;
+ } else if (num < 2000) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num - 1000;
+ } else if (num < 3000) {
+ ast_copy_string(fn, "digits/2thousand", sizeof(fn));
+ num = num - 2000;
+ if (num>0) state=2;
+ } else if (num < 20000) {
+ snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
+ num = num % 1000;
+ state=3;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num % 1000;
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num = num % 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_hu: Hungarian syntax */
+/* Extra sounds need:
+ 10en: "tizen"
+ 20on: "huszon"
+*/
+static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ /*
+ Hungarian support
+ like english, except numbers up to 29 are from 2 words.
+ 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
+ */
+
+ while(!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 11 || num == 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 20) {
+ ast_copy_string(fn, "digits/10en", sizeof(fn));
+ num -= 10;
+ } else if (num < 30) {
+ ast_copy_string(fn, "digits/20on", sizeof(fn));
+ num -= 20;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if(!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_it: Italian */
+static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int tempnum = 0;
+ char fn[256] = "";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ /*
+ Italian support
+
+ Like english, numbers up to 20 are a single 'word', and others
+ compound, but with exceptions.
+ For example 21 is not twenty-one, but there is a single word in 'it'.
+ Idem for 28 (ie when a the 2nd part of a compund number
+ starts with a vowel)
+
+ There are exceptions also for hundred, thousand and million.
+ In english 100 = one hundred, 200 is two hundred.
+ In italian 100 = cento , like to say hundred (without one),
+ 200 and more are like english.
+
+ Same applies for thousand:
+ 1000 is one thousand in en, 2000 is two thousand.
+ In it we have 1000 = mille , 2000 = 2 mila
+
+ For million(s) we use the plural, if more than one
+ Also, one million is abbreviated in it, like on-million,
+ or 'un milione', not 'uno milione'.
+ So the right file is provided.
+ */
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 21) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 28) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 31) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 38) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 41) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 48) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 51) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 58) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 61) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 68) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 71) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 78) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 81) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 88) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 91) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 98) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000) {
+ if ((num / 100) > 1) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ } else {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ }
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ if ((num/1000) > 1)
+ res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ tempnum = num;
+ num %= 1000;
+ if ((tempnum / 1000) < 2)
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
+ ast_copy_string(fn, "digits/thousands", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ if ((num / 1000000) > 1)
+ res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ tempnum = num;
+ num %= 1000000;
+ if ((tempnum / 1000000) < 2)
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ else
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_nl: dutch syntax */
+/* New files: digits/nl-en
+ */
+static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int units = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+ while (!res && (num || playh )) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ units = num % 10;
+ if (units > 0) {
+ res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num = num - units;
+ ast_copy_string(fn, "digits/nl-en", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num - units);
+ num = 0;
+ }
+ } else if (num < 200) {
+ /* hundred, not one-hundred */
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ num %= 100;
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", num / 100);
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1100) {
+ /* thousand, not one-thousand */
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else if (num < 10000) { /* 1,100 to 9,9999 */
+ res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 100;
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_no: Norwegian syntax */
+/* New files:
+ In addition to American English, the following sounds are required: "and", "1N"
+ */
+static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ int cn = 1; /* +1 = commune; -1 = neuter */
+ char fn[256] = "";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "n",1)) cn = -1;
+
+ while (!res && (num || playh || playa )) {
+ /* The grammar for Norwegian numbers is the same as for English except
+ * for the following:
+ * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
+ * "and" before the last two digits, i.e. 2034 is "two thousand and
+ * thirty-four" and 1000012 is "one million and twelve".
+ */
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num == 1 && cn == -1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ if (hundreds == 1)
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
+
+ playh++;
+ num -= 100 * hundreds;
+ if (num)
+ playa++;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num %= 1000;
+ if (num && num < 100)
+ playa++;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num %= 1000000;
+ if (num && num < 100)
+ playa++;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+typedef struct {
+ char *separator_dziesiatek;
+ char *cyfry[10];
+ char *cyfry2[10];
+ char *setki[10];
+ char *dziesiatki[10];
+ char *nastki[10];
+ char *rzedy[3][3];
+} odmiana;
+
+static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
+{
+ if (rzad==0)
+ return "";
+
+ if (i==1)
+ return odm->rzedy[rzad - 1][0];
+ if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
+ return odm->rzedy[rzad - 1][1];
+ else
+ return odm->rzedy[rzad - 1][2];
+}
+
+static char* pl_append(char* buffer, char* str)
+{
+ strcpy(buffer, str);
+ buffer += strlen(str);
+ return buffer;
+}
+
+static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
+{
+ char file_name[255] = "digits/";
+ strcat(file_name, fn);
+ ast_debug(1, "Trying to play: %s\n", file_name);
+ if (!ast_streamfile(chan, file_name, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+}
+
+static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
+{
+ /* Initialise variables to allow compilation on Debian-stable, etc */
+ int m1000E6 = 0;
+ int i1000E6 = 0;
+ int m1000E3 = 0;
+ int i1000E3 = 0;
+ int m1000 = 0;
+ int i1000 = 0;
+ int m100 = 0;
+ int i100 = 0;
+
+ if (i == 0 && rzad > 0) {
+ return;
+ }
+ if (i == 0) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
+ return;
+ }
+
+ m1000E6 = i % 1000000000;
+ i1000E6 = i / 1000000000;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
+
+ m1000E3 = m1000E6 % 1000000;
+ i1000E3 = m1000E6 / 1000000;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
+
+ m1000 = m1000E3 % 1000;
+ i1000 = m1000E3 / 1000;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
+
+ m100 = m1000 % 100;
+ i100 = m1000 / 100;
+
+ if (i100>0)
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
+
+ if ( m100 > 0 && m100 <=9 ) {
+ if (m1000>0)
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
+ else
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
+ } else if (m100 % 10 == 0) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
+ } else if (m100 <= 19 ) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
+ } else if (m100 != 0) {
+ if (odm->separator_dziesiatek[0]==' ') {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
+ } else {
+ char buf[10];
+ char *b = buf;
+ b = pl_append(b, odm->dziesiatki[m100 / 10]);
+ b = pl_append(b, odm->separator_dziesiatek);
+ b = pl_append(b, odm->cyfry2[m100 % 10]);
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
+ }
+ }
+
+ if (rzad > 0) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
+ }
+}
+
+/* ast_say_number_full_pl: Polish syntax */
+static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+/*
+Sounds needed:
+0 zero
+1 jeden
+10 dziesiec
+100 sto
+1000 tysiac
+1000000 milion
+1000000000 miliard
+1000000000.2 miliardy
+1000000000.5 miliardow
+1000000.2 miliony
+1000000.5 milionow
+1000.2 tysiace
+1000.5 tysiecy
+100m stu
+10m dziesieciu
+11 jedenascie
+11m jedenastu
+12 dwanascie
+12m dwunastu
+13 trzynascie
+13m trzynastu
+14 czternascie
+14m czternastu
+15 pietnascie
+15m pietnastu
+16 szesnascie
+16m szesnastu
+17 siedemnascie
+17m siedemnastu
+18 osiemnascie
+18m osiemnastu
+19 dziewietnascie
+19m dziewietnastu
+1z jedna
+2 dwa
+20 dwadziescia
+200 dwiescie
+200m dwustu
+20m dwudziestu
+2-1m dwaj
+2-2m dwoch
+2z dwie
+3 trzy
+30 trzydziesci
+300 trzysta
+300m trzystu
+30m trzydziestu
+3-1m trzej
+3-2m trzech
+4 cztery
+40 czterdziesci
+400 czterysta
+400m czterystu
+40m czterdziestu
+4-1m czterej
+4-2m czterech
+5 piec
+50 piecdziesiat
+500 piecset
+500m pieciuset
+50m piedziesieciu
+5m pieciu
+6 szesc
+60 szescdziesiat
+600 szescset
+600m szesciuset
+60m szescdziesieciu
+6m szesciu
+7 siedem
+70 siedemdziesiat
+700 siedemset
+700m siedmiuset
+70m siedemdziesieciu
+7m siedmiu
+8 osiem
+80 osiemdziesiat
+800 osiemset
+800m osmiuset
+80m osiemdziesieciu
+8m osmiu
+9 dziewiec
+90 dziewiecdziesiat
+900 dziewiecset
+900m dziewieciuset
+90m dziewiedziesieciu
+9m dziewieciu
+and combinations of eg.: 20_1, 30m_3m, etc...
+
+*/
+{
+ char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
+
+ char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
+
+ char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
+
+ char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
+
+ char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
+
+ char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
+
+ char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
+
+ char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
+
+ char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
+
+ /* Initialise variables to allow compilation on Debian-stable, etc */
+ odmiana *o;
+
+ static odmiana *odmiana_nieosobowa = NULL;
+ static odmiana *odmiana_meska = NULL;
+ static odmiana *odmiana_zenska = NULL;
+
+ if (odmiana_nieosobowa == NULL) {
+ odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
+
+ odmiana_nieosobowa->separator_dziesiatek = " ";
+
+ memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
+ memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
+ memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
+ memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
+ memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
+ memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
+ }
+
+ if (odmiana_zenska == NULL) {
+ odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
+
+ odmiana_zenska->separator_dziesiatek = " ";
+
+ memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
+ memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
+ memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
+ memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
+ memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
+ memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
+ }
+
+ if (odmiana_meska == NULL) {
+ odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
+
+ odmiana_meska->separator_dziesiatek = " ";
+
+ memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
+ memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
+ memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
+ memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
+ memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
+ memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
+ }
+
+ if (options) {
+ if (strncasecmp(options, "f", 1) == 0)
+ o = odmiana_zenska;
+ else if (strncasecmp(options, "m", 1) == 0)
+ o = odmiana_meska;
+ else
+ o = odmiana_nieosobowa;
+ } else
+ o = odmiana_nieosobowa;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
+ return 0;
+}
+
+/* ast_say_number_full_pt: Portuguese syntax */
+/* Extra sounds needed: */
+/* For feminin all sound files end with F */
+/* 100E for 100+ something */
+/* 1000000S for plural */
+/* pt-e for 'and' */
+static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int mf = 1; /* +1 = male; -1 = female */
+ char fn[256] = "";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "f",1))
+ mf = -1;
+
+ while (!res && num ) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 20) {
+ if ((num == 1 || num == 2) && (mf < 0))
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
+ if (num % 10)
+ playh = 1;
+ num = num % 10;
+ } else if (num < 1000) {
+ if (num == 100)
+ ast_copy_string(fn, "digits/100", sizeof(fn));
+ else if (num < 200)
+ ast_copy_string(fn, "digits/100E", sizeof(fn));
+ else {
+ if (mf < 0 && num > 199)
+ snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
+ if (num % 100)
+ playh = 1;
+ }
+ num = num % 100;
+ } else if (num < 1000000) {
+ if (num > 1999) {
+ res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ }
+ ast_copy_string(fn, "digits/1000", sizeof(fn));
+ if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
+ playh = 1;
+ num = num % 1000;
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
+ if (res)
+ return res;
+ if (num < 2000000)
+ ast_copy_string(fn, "digits/1000000", sizeof(fn));
+ else
+ ast_copy_string(fn, "digits/1000000S", sizeof(fn));
+
+ if ((num % 1000000) &&
+ /* no thousands */
+ ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
+ /* no hundreds and below */
+ (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
+ playh = 1;
+ num = num % 1000000;
+ } else {
+ /* number is too big */
+ ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ if (!res && playh) {
+ res = wait_file(chan, ints, "digits/pt-e", language);
+ ast_stopstream(chan);
+ playh = 0;
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_se: Swedish syntax */
+static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ int cn = 1; /* +1 = commune; -1 = neuter */
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+ if (options && !strncasecmp(options, "n",1)) cn = -1;
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num == 1 && cn == -1) { /* En eller ett? */
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ num = 0;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ ast_stopstream(chan);
+ }
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
+static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 10) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+
+/*! \brief determine last digits for thousands/millions (ru) */
+static int get_lastdigits_ru(int num) {
+ if (num < 20) {
+ return num;
+ } else if (num < 100) {
+ return get_lastdigits_ru(num % 10);
+ } else if (num < 1000) {
+ return get_lastdigits_ru(num % 100);
+ }
+ return 0; /* number too big */
+}
+
+
+/*! \brief ast_say_number_full_ru: Russian syntax */
+/*! \brief additional files:
+ n00.gsm (one hundred, two hundred, ...)
+ thousand.gsm
+ million.gsm
+ thousands-i.gsm (tisyachi)
+ million-a.gsm (milliona)
+ thousands.gsm
+ millions.gsm
+ 1f.gsm (odna)
+ 2f.gsm (dve)
+
+ where 'n' from 1 to 9
+*/
+static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int lastdigits = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 20) {
+ if (options && strlen(options) == 1 && num < 3) {
+ snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ }
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
+ num %= 10;
+ } else if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
+ num %= 100;
+ } else if (num < 1000000) { /* 1,000,000 */
+ lastdigits = get_lastdigits_ru(num / 1000);
+ /* say thousands */
+ if (lastdigits < 3) {
+ res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
+ } else {
+ res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
+ }
+ if (res)
+ return res;
+ if (lastdigits == 1) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else if (lastdigits > 1 && lastdigits < 5) {
+ ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/thousands", sizeof(fn));
+ }
+ num %= 1000;
+ } else if (num < 1000000000) { /* 1,000,000,000 */
+ lastdigits = get_lastdigits_ru(num / 1000000);
+ /* say millions */
+ res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (lastdigits == 1) {
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else if (lastdigits > 1 && lastdigits < 5) {
+ ast_copy_string(fn, "digits/million-a", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ }
+ num %= 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while(!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/lop", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/roi", sizeof(fn));
+ playh = 0;
+ } else if (num < 100) {
+ if ((num <= 20) || ((num % 10) == 1)) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
+ num %= 10;
+ }
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else if (num < 10000) { /* 10,000 */
+ res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/pan", sizeof(fn));
+ } else if (num < 100000) { /* 100,000 */
+ res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 10000;
+ ast_copy_string(fn, "digits/muan", sizeof(fn));
+ } else if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 100000;
+ ast_copy_string(fn, "digits/san", sizeof(fn));
+ } else {
+ res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/larn", sizeof(fn));
+ }
+ if (!res) {
+ if(!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_enumeration_full: call language-specific functions */
+/* Called from AGI */
+static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ if (!strcasecmp(language,"en") ) { /* English syntax */
+ return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
+ return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "de") ) { /* German syntax */
+ return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
+ }
+
+ /* Default to english */
+ return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
+}
+
+/*! \brief ast_say_enumeration_full_en: English syntax */
+/* This is the default syntax, if no other syntax defined in this file is used */
+static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0, t = 0;
+ char fn[256] = "";
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/h-%d", num);
+ num = 0;
+ } else if (num < 100) {
+ int tens = num / 10;
+ num = num % 10;
+ if (num == 0) {
+ snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
+ }
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds > 1 || t == 1) {
+ res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
+ }
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
+ }
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ if (thousands > 1 || t == 1) {
+ res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
+ }
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
+ }
+ t = 1;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ t = 1;
+ res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-million", sizeof(fn));
+ }
+ } else if (num < INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ t = 1;
+ res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/billion", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-billion", sizeof(fn));
+ }
+ } else if (num == INT_MAX) {
+ ast_copy_string(fn, "digits/h-last", sizeof(fn));
+ num = 0;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_enumeration_full_da: Danish syntax */
+static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
+ int res = 0, t = 0;
+ char fn[256] = "", fna[256] = "";
+ char *gender;
+
+ if (options && !strncasecmp(options, "f",1)) {
+ gender = "F";
+ } else if (options && !strncasecmp(options, "n",1)) {
+ gender = "N";
+ } else {
+ gender = "";
+ }
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 100 && t) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ t = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ }
+ } else if (num == 100 && t == 0) {
+ snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
+ num = 0;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ if (num) {
+ ast_copy_string(fna, "digits/hundred", sizeof(fna));
+ } else {
+ snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
+ }
+ t = 1;
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ if (thousands == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ ast_copy_string(fna, "digits/thousand", sizeof(fna));
+ } else {
+ if (t) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ } else {
+ res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ if (millions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/million", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ if (billions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/milliard", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/milliards", sizeof(fna));
+ } else {
+ snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num == INT_MAX) {
+ snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
+ num = 0;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ if (!res) {
+ if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ strcpy(fna, "");
+ }
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_enumeration_full_de: German syntax */
+static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
+ int res = 0, t = 0;
+ char fn[256] = "", fna[256] = "";
+ char *gender;
+
+ if (options && !strncasecmp(options, "f",1)) {
+ gender = "F";
+ } else if (options && !strncasecmp(options, "n",1)) {
+ gender = "N";
+ } else {
+ gender = "";
+ }
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 100 && t) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ t = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ }
+ } else if (num == 100 && t == 0) {
+ snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
+ num = 0;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ if (num) {
+ ast_copy_string(fna, "digits/hundred", sizeof(fna));
+ } else {
+ snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
+ }
+ t = 1;
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ if (thousands == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ ast_copy_string(fna, "digits/thousand", sizeof(fna));
+ } else {
+ if (t) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ } else {
+ res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ if (millions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/million", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ if (billions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/milliard", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/milliards", sizeof(fna));
+ } else {
+ snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num == INT_MAX) {
+ snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
+ num = 0;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ if (!res) {
+ if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ strcpy(fna, "");
+ }
+ }
+ }
+ return res;
+}
+
+static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_date_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
+ return(ast_say_date_da(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_date_de(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_date_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "hu") ) { /* Hungarian syntax */
+ return(ast_say_date_hu(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_date_nl(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_date_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_date_gr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "th") ) { /* Thai syntax */
+ return(ast_say_date_th(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_date_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_date_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct ast_tm tm;
+ struct timeval tv = { t, 0 };
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Danish syntax */
+int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ /* Year */
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
+ res = wait_file(chan, ints, fn, lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred", lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+/* German syntax */
+int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ /* Year */
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ /* say 1967 as 'neunzehn hundert sieben und sechzig' */
+ snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
+ res = wait_file(chan, ints, fn, lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred", lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+/* Hungarian syntax */
+int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ return res;
+}
+
+/* French syntax */
+int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ ast_copy_string(fn, "digits/tee", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ ast_copy_string(fn, "digits/duan", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res){
+ ast_copy_string(fn, "digits/posor", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ }
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-de", lang);
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-de", lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+
+ return res;
+}
+
+static int say_date_with_format(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
+ return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
+ return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
+ return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
+ return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "pl") ) { /* Polish syntax */
+ return(ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "th") ) { /* Thai syntax */
+ return(ast_say_date_with_format_th(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
+ }
+
+ /* Default to English */
+ return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
+}
+
+/* English syntax */
+int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "ABdY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ } else if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan, ints, "digits/19", lang);
+ if (!res) {
+ if (tm.tm_year <= 9) {
+ /* 1901 - 1909 */
+ res = wait_file(chan,ints, "digits/oh", lang);
+ }
+
+ res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (format[offset] == 'H') {
+ /* e.g. oh-eight */
+ if (tm.tm_hour < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ }
+ } else {
+ /* e.g. eight */
+ if (tm.tm_hour == 0) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ }
+ }
+ if (!res) {
+ if (tm.tm_hour != 0) {
+ int remainder = tm.tm_hour;
+ if (tm.tm_hour > 20) {
+ res = wait_file(chan,ints, "digits/20",lang);
+ remainder -= 20;
+ }
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ break;
+ case 'M':
+ case 'N':
+ /* Minute */
+ if (tm.tm_min == 0) {
+ if (format[offset] == 'M') {
+ res = wait_file(chan, ints, "digits/oclock", lang);
+ } else {
+ res = wait_file(chan, ints, "digits/hundred", lang);
+ }
+ } else if (tm.tm_min < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ gettimeofday(&now,NULL);
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now;
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ now = ast_tvnow();
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Danish syntax */
+int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "A dBY HMS";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
+ break;
+ case 'Y':
+ /* Year */
+ {
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ /* say 1967 as 'nineteen hundred seven and sixty' */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred",lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ res = wait_file(chan,ints,"digits/oclock",lang);
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ if (!res) {
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ break;
+ case 'H':
+ /* 24-Hour, single digit hours preceeded by "oh" (0) */
+ if (tm.tm_hour < 10 && tm.tm_hour > 0) {
+ res = wait_file(chan,ints, "digits/0",lang);
+ }
+ /* FALLTRHU */
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ }
+ if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
+ if (tm.tm_min == 1) {
+ res = wait_file(chan,ints,"digits/minute",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/minutes",lang);
+ }
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = wait_file(chan,ints, "digits/and",lang);
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
+ if (!res) {
+ res = wait_file(chan,ints, "digits/seconds",lang);
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* German syntax */
+int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "A dBY HMS";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
+ break;
+ case 'Y':
+ /* Year */
+ {
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ /* say 1967 as 'neunzehn hundert sieben und sechzig' */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred",lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints,"digits/oclock",lang);
+ }
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ if (!res) {
+ res = wait_file(chan,ints,"digits/oclock",lang);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ }
+ if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
+ if (tm.tm_min == 1) {
+ res = wait_file(chan,ints,"digits/minute",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/minutes",lang);
+ }
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = wait_file(chan,ints, "digits/and",lang);
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
+ if (!res) {
+ res = wait_file(chan,ints, "digits/seconds",lang);
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_date_with_format_th(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "a 'digits/tee' e 'digits/duan' hY I 'digits/naliga' M 'digits/natee'";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'M':
+ case 'N':
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
+ break;
+ case 'T':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* TODO: this probably is not the correct format for doxygen remarks */
+
+/** ast_say_date_with_format_he Say formatted date in Hebrew
+ *
+ * \ref ast_say_date_with_format_en for the details of the options
+ *
+ * Changes from the English version:
+ *
+ * * don't replicate in here the logic of ast_say_number_full_he
+ *
+ * * year is always 4-digit (because it's simpler)
+ *
+ * * added c, x, and X. Mainly for my tests
+ *
+ * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
+ *
+ * TODO:
+ * * A "ha" is missing in the standard date format, before the 'd'.
+ * * The numbers of 3000--19000 are not handled well
+ **/
+#define IL_DATE_STR "AdBY"
+#define IL_TIME_STR "IMp"
+#define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
+int ast_say_date_with_format_he(struct ast_channel *chan, time_t time,
+ const char *ints, const char *lang, const char *format,
+ const char *timezone)
+{
+ /* TODO: This whole function is cut&paste from
+ * ast_say_date_with_format_en . Is that considered acceptable?
+ **/
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = IL_DATE_STR_FULL;
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e': /* Day of the month */
+ /* I'm not sure exactly what the parameters
+ * audiofd and ctrlfd to
+ * ast_say_number_full_he mean, but it seems
+ * safe to pass -1 there.
+ *
+ * At least in one of the pathes :-(
+ */
+ res = ast_say_number_full_he(chan, tm.tm_mday,
+ ints, lang, "m", -1, -1
+ );
+ break;
+ case 'Y': /* Year */
+ res = ast_say_number_full_he(chan, tm.tm_year+1900,
+ ints, lang, "f", -1, -1
+ );
+ break;
+ case 'I':
+ case 'l': /* 12-Hour */
+ {
+ int hour = tm.tm_hour;
+ hour = hour%12;
+ if (hour == 0) hour=12;
+
+ res = ast_say_number_full_he(chan, hour,
+ ints, lang, "f", -1, -1
+ );
+ }
+ break;
+ case 'H':
+ case 'k': /* 24-Hour */
+ /* With 'H' there is an 'oh' after a single-
+ * digit hour */
+ if ((format[offset] == 'H') &&
+ (tm.tm_hour <10)&&(tm.tm_hour>0)
+ ) { /* e.g. oh-eight */
+ res = wait_file(chan,ints, "digits/oh",lang);
+ }
+
+ res = ast_say_number_full_he(chan, tm.tm_hour,
+ ints, lang, "f", -1, -1
+ );
+ break;
+ case 'M': /* Minute */
+ res = ast_say_number_full_he(chan, tm.tm_min,
+ ints, lang,"f", -1, -1
+ );
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or "date" */
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A
+ * (weekday), or "date" */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+ char todo = format[offset]; /* The letter to format*/
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ if (todo == 'Q') {
+ res = wait_file(chan,
+ ints,
+ "digits/today",
+ lang);
+ }
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if ((todo != 'Q') &&
+ (beg_today - 86400 * 6 < time))
+ {
+ /* Within the last week */
+ res = ast_say_date_with_format_he(chan,
+ time, ints, lang,
+ "A", timezone);
+ } else {
+ res = ast_say_date_with_format_he(chan,
+ time, ints, lang,
+ IL_DATE_STR, timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S': /* Seconds */
+ res = ast_say_number_full_he(chan, tm.tm_sec,
+ ints, lang, "f", -1, -1
+ );
+ break;
+ case 'T':
+ res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
+ break;
+ /* c, x, and X seem useful for testing. Not sure
+ * if thiey're good for the general public */
+ case 'c':
+ res = ast_say_date_with_format_he(chan, time,
+ ints, lang, IL_DATE_STR_FULL, timezone);
+ break;
+ case 'x':
+ res = ast_say_date_with_format_he(chan, time,
+ ints, lang, IL_DATE_STR, timezone);
+ break;
+ case 'X': /* Currently not locale-dependent...*/
+ res = ast_say_date_with_format_he(chan, time,
+ ints, lang, IL_TIME_STR, timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+
+/* Spanish syntax */
+int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
+ break;
+ case 'M':
+ /* Minute */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 18)
+ res = wait_file(chan, ints, "digits/p-m", lang);
+ else if (tm.tm_hour > 12)
+ res = wait_file(chan, ints, "digits/afternoon", lang);
+ else if (tm.tm_hour)
+ res = wait_file(chan, ints, "digits/a-m", lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ int ten, one;
+ ten = (tm.tm_sec / 10) * 10;
+ one = (tm.tm_sec % 10);
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ /* Fifty, not fifty-zero */
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* French syntax
+oclock = heure
+*/
+int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "AdBY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First */
+ if (tm.tm_mday == 1) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ }
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/2",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ }
+ if (tm.tm_year > 100) {
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
+ }
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ if (!res) {
+ wait_file(chan,ints, "digits/9",lang);
+ wait_file(chan,ints, "digits/hundred",lang);
+ res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res)
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
+ if (!res)
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min == 0) {
+ break;
+ }
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/second",lang);
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "AdB 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First day of the month is spelled as ordinal */
+ if (tm.tm_mday == 1) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ }
+ }
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/ore-2000",lang);
+ if (tm.tm_year > 100) {
+ if (!res) {
+ /* This works until the end of 2021 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/ore-1900",lang);
+ if ((!res) && (tm.tm_year != 0)) {
+ if (tm.tm_year <= 21) {
+ /* 1910 - 1921 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
+ int ten, one;
+ ten = tm.tm_year / 10;
+ one = tm.tm_year % 10;
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (tm.tm_hour == 0) {
+ res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
+ } else if (tm.tm_hour == 1) {
+ res = wait_file(chan,ints, "digits/ore-una",lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ int ten, one;
+ ten = (tm.tm_sec / 10) * 10;
+ one = (tm.tm_sec % 10);
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ /* Fifty, not fifty-zero */
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "ABdY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/2",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ }
+ if (tm.tm_year > 100) {
+ if (!res) {
+ /* This works until the end of 2020 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/19",lang);
+ if (!res) {
+ if (tm.tm_year <= 9) {
+ /* 1901 - 1909 */
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if (tm.tm_year <= 20) {
+ /* 1910 - 1920 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ /* 1921 - 1999 */
+ int ten, one;
+ ten = tm.tm_year / 10;
+ one = tm.tm_year % 10;
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/nl-uur",lang);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
+ break;
+ case 'T':
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Polish syntax */
+int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { thetime, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset = 0 ; format[offset] != '\0' ; offset++) {
+ int remainder;
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset = 0;
+ for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan, ints, sndfile, lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ remainder = tm.tm_mday;
+ if (tm.tm_mday > 30) {
+ res = wait_file(chan, ints, "digits/h-30", lang);
+ remainder -= 30;
+ }
+ if (tm.tm_mday > 20 && tm.tm_mday < 30) {
+ res = wait_file(chan, ints, "digits/h-20", lang);
+ remainder -= 20;
+ }
+ if (!res) {
+ snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
+ res = wait_file(chan, ints, nextmsg, lang);
+ }
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 100) {
+ res = wait_file(chan, ints, "digits/2", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/1000.2",lang);
+ if (tm.tm_year > 100) {
+ if (!res)
+ res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
+ }
+ } else if (tm.tm_year == 100) {
+ res = wait_file(chan, ints, "digits/h-2000", lang);
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ break;
+ } else {
+ res = wait_file(chan, ints, "digits/1000", lang);
+ if (!res) {
+ wait_file(chan, ints, "digits/900", lang);
+ res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
+ }
+ }
+ }
+ if (!res)
+ wait_file(chan, ints, "digits/year", lang);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/t-12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
+
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (tm.tm_hour != 0) {
+ snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
+ res = wait_file(chan, ints, nextmsg, lang);
+ } else
+ res = wait_file(chan, ints, "digits/t-24", lang);
+ break;
+ case 'M':
+ case 'N':
+ /* Minute */
+ if (tm.tm_min == 0) {
+ if (format[offset] == 'M') {
+ res = wait_file(chan, ints, "digits/oclock", lang);
+ } else {
+ res = wait_file(chan, ints, "digits/100", lang);
+ }
+ } else
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < thetime) {
+ /* Today */
+ res = wait_file(chan, ints, "digits/today", lang);
+ } else if (beg_today - 86400 < thetime) {
+ /* Yesterday */
+ res = wait_file(chan, ints, "digits/yesterday", lang);
+ } else {
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < thetime) {
+ /* Today */
+ } else if ((beg_today - 86400) < thetime) {
+ /* Yesterday */
+ res = wait_file(chan, ints, "digits/yesterday", lang);
+ } else if (beg_today - 86400 * 6 < thetime) {
+ /* Within the last week */
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = wait_file(chan, ints, "digits/and", lang);
+ if (!res) {
+ if (tm.tm_sec == 1) {
+ res = wait_file(chan, ints, "digits/1z", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/second-a", lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ if (!res) {
+ int ten, one;
+ ten = tm.tm_sec / 10;
+ one = tm.tm_sec % 10;
+
+ if (one > 1 && one < 5 && ten != 1)
+ res = wait_file(chan,ints, "digits/seconds",lang);
+ else
+ res = wait_file(chan,ints, "digits/second",lang);
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res)
+ break;
+ }
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (!strcasecmp(lang, "pt_BR")) {
+ if (tm.tm_hour == 0) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-a", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meianoite", lang);
+ } else if (tm.tm_hour == 12) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-ao", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meiodia", lang);
+ } else {
+ if (format[offset] == 'I') {
+ if ((tm.tm_hour % 12) != 1)
+ res = wait_file(chan, ints, "digits/pt-as", lang);
+ else
+ res = wait_file(chan, ints, "digits/pt-a", lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
+ }
+ } else {
+ if (tm.tm_hour == 0) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-ah", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meianoite", lang);
+ }
+ else if (tm.tm_hour == 12) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-ao", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meiodia", lang);
+ }
+ else {
+ if (format[offset] == 'I') {
+ res = wait_file(chan, ints, "digits/pt-ah", lang);
+ if ((tm.tm_hour % 12) != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
+ }
+ }
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if ((!res) && (format[offset] == 'H')) {
+ if (tm.tm_hour > 1) {
+ res = wait_file(chan,ints,"digits/hours",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/hour",lang);
+ }
+ }
+ } else {
+ res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
+ if (!res) {
+ if (tm.tm_hour != 0) {
+ int remainder = tm.tm_hour;
+ if (tm.tm_hour > 20) {
+ res = wait_file(chan,ints, "digits/20",lang);
+ remainder -= 20;
+ }
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ break;
+ case 'M':
+ /* Minute */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
+ if (!res) {
+ if (tm.tm_min > 1) {
+ res = wait_file(chan,ints,"digits/minutes",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/minute",lang);
+ }
+ }
+ } else {
+ if (tm.tm_min == 0) {
+ res = wait_file(chan, ints, "digits/pt-hora", lang);
+ if (tm.tm_hour != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (!strcasecmp(lang, "pt_BR")) {
+ if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
+ res = wait_file(chan, ints, "digits/pt-da", lang);
+ if (!res) {
+ if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
+ res = wait_file(chan, ints, "digits/morning", lang);
+ else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
+ res = wait_file(chan, ints, "digits/afternoon", lang);
+ else res = wait_file(chan, ints, "digits/night", lang);
+ }
+ }
+ } else {
+ if (tm.tm_hour > 12)
+ res = wait_file(chan, ints, "digits/p-m", lang);
+ else if (tm.tm_hour && tm.tm_hour < 12)
+ res = wait_file(chan, ints, "digits/a-m", lang);
+ }
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
+ if (!res) {
+ if (tm.tm_sec > 1) {
+ res = wait_file(chan,ints,"digits/seconds",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/second",lang);
+ }
+ }
+ } else {
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ int ten, one;
+ ten = (tm.tm_sec / 10) * 10;
+ one = (tm.tm_sec % 10);
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ /* Fifty, not fifty-zero */
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "YBdAkM";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ case 'm':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) res = wait_file(chan,ints,"digits/day",lang);
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/2",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ }
+ if (tm.tm_year > 100) {
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/year",lang);
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/1",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/9",lang);
+ }
+ if (!res) {
+ if (tm.tm_year <= 9) {
+ /* 1901 - 1909 */
+ res = wait_file(chan,ints, "digits/0",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else {
+ /* 1910 - 1999 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/year",lang);
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ }
+ break;
+ case 'H':
+ if (tm.tm_hour < 10) {
+ res = wait_file(chan, ints, "digits/0", lang);
+ }
+ case 'k':
+ /* 24-Hour */
+ if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ if (!(tm.tm_min % 10) || tm.tm_min < 10) {
+ if (tm.tm_min < 10) {
+ res = wait_file(chan, ints, "digits/0", lang);
+ }
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/minute",lang);
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "kM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
+ if (tm.tm_sec < 10) {
+ res = wait_file(chan, ints, "digits/0", lang);
+ }
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/second",lang);
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_time_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_time_de(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_time_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "hu") ) { /* Hungarian syntax */
+ return(ast_say_time_hu(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_time_nl(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
+ return(ast_say_time_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
+ return(ast_say_time_pt_BR(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "tw") ) { /* Taiwanese syntax */
+ } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_time_tw(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_time_gr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "th") ) {
+ return(ast_say_time_th(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_time_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_time_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+
+ if (tm.tm_min > 9) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oh", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+/* German syntax */
+int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ if (tm.tm_min > 0)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ return res;
+}
+
+/* Hungarian syntax */
+int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ if (tm.tm_min > 0) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ if (!res)
+ res = ast_streamfile(chan, "digits/minute", lang);
+ }
+ return res;
+}
+
+/* French syntax */
+int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (tm.tm_min) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/nl-uur", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ if (tm.tm_min > 0)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, "f");
+ if (tm.tm_min) {
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-e", lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-hora", lang);
+ if (tm.tm_hour != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Brazilian Portuguese syntax */
+int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if (!res) {
+ if (tm.tm_hour > 1)
+ res = wait_file(chan, ints, "digits/hours", lang);
+ else
+ res = wait_file(chan, ints, "digits/hour", lang);
+ }
+ if ((!res) && (tm.tm_min)) {
+ res = wait_file(chan, ints, "digits/pt-e", lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ if (!res) {
+ if (tm.tm_min > 1)
+ res = wait_file(chan, ints, "digits/minutes", lang);
+ else
+ res = wait_file(chan, ints, "digits/minute", lang);
+ }
+ }
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour;
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 24;
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/minute", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_datetime_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_datetime_de(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_datetime_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_datetime_nl(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "hu") ) { /* Hungarian syntax */
+ return(ast_say_datetime_hu(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
+ return(ast_say_datetime_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
+ return(ast_say_datetime_pt_BR(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_datetime_tw(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_datetime_gr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "th") ) { /* Thai syntax */
+ return(ast_say_datetime_th(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_datetime_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_datetime_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+
+ if (tm.tm_min > 9) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oh", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* German syntax */
+int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+
+}
+
+/* Hungarian syntax */
+int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* French syntax */
+int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (tm.tm_min > 0) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res) {
+ res = ast_streamfile(chan, "digits/nl-om", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+
+ if (tm.tm_min > 9) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oh", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Brazilian Portuguese syntax */
+int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res){
+ ast_copy_string(fn, "digits/posor", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 24;
+ if (!res){
+ ast_copy_string(fn, "digits/wela", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/minute", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_datetime_from_now_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_datetime_from_now_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_datetime_from_now_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ struct timeval nowtv = ast_tvnow(), tv = { t, 0 };
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowtv, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ } /* Otherwise, it was today */
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* French syntax */
+int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ struct timeval nowtv = ast_tvnow(), tv = { t, 0 };
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowtv, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ } /* Otherwise, it was today */
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ struct timeval nowtv = ast_tvnow(), tv = { t, 0 };
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowtv, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-de", lang);
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ } /* Otherwise, it was today */
+ if (!strcasecmp(lang, "pt_BR")) {
+ if (tm.tm_hour > 1) {
+ ast_copy_string(fn, "digits/pt-as", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/pt-a", sizeof(fn));
+ }
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ } else {
+ ast_copy_string(fn, "digits/pt-ah", sizeof(fn));
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ if (tm.tm_hour != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ }
+ return res;
+}
+
+
+/*********************************** GREEK SUPPORT ***************************************/
+
+
+/*
+ * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
+ */
+static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
+ int tmp;
+ int left;
+ int res;
+ char fn[256] = "";
+
+ /* ast_debug(1, "\n\n Saying number female %s %d \n\n",lang, num); */
+ if (num < 5) {
+ snprintf(fn, sizeof(fn), "digits/female-%d", num);
+ res = wait_file(chan, ints, fn, lang);
+ } else if (num < 13) {
+ res = ast_say_number(chan, num, ints, lang, (char *) NULL);
+ } else if (num <100 ) {
+ tmp = (num/10) * 10;
+ left = num - tmp;
+ snprintf(fn, sizeof(fn), "digits/%d", tmp);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (left)
+ gr_say_number_female(left, chan, ints, lang);
+
+ } else {
+ return -1;
+ }
+ return res;
+}
+
+
+
+/*
+ * A list of the files that you need to create
+ -> digits/xilia = "xilia"
+ -> digits/myrio = "ekatomyrio"
+ -> digits/thousands = "xiliades"
+ -> digits/millions = "ektatomyria"
+ -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
+ -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
+ e,g 80 = "ogdonta"
+ Here we must note that we use digits/tens/100 to utter "ekato"
+ and digits/hundred-100 to utter "ekaton"
+ -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
+ "terakosia". Here again we use hundreds/1000 for "xilia"
+ and digits/thousnds for "xiliades"
+*/
+
+static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
+{
+ int res = 0;
+ char fn[256] = "";
+ int i=0;
+
+
+ if (!num) {
+ ast_copy_string(fn, "digits/0", sizeof(fn));
+ res = ast_streamfile(chan, fn, chan->language);
+ if (!res)
+ return ast_waitstream(chan, ints);
+ }
+
+ while (!res && num ) {
+ i++;
+ if (num < 13) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num <= 100) {
+ /* 13 < num <= 100 */
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 200) {
+ /* 100 < num < 200 */
+ snprintf(fn, sizeof(fn), "digits/hundred-100");
+ num %= 100;
+ } else if (num < 1000) {
+ /* 200 < num < 1000 */
+ snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
+ num %= 100;
+ } else if (num < 2000){
+ snprintf(fn, sizeof(fn), "digits/xilia");
+ num %= 1000;
+ } else {
+ /* num > 1000 */
+ if (num < 1000000) {
+ res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ snprintf(fn, sizeof(fn), "digits/thousands");
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ snprintf(fn, sizeof(fn), "digits/millions");
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+
+/*
+ * The format is weekday - day - month -year
+ *
+ * A list of the files that you need to create
+ * digits/day-[1..7] : "Deytera .. Paraskeyh"
+ * digits/months/1..12 : "Ianouariou .. Dekembriou"
+ Attention the months are in
+ "gekinh klhsh"
+ */
+
+
+static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct ast_tm tm;
+ struct timeval tv = { t, 0 };
+
+ char fn[256];
+ int res = 0;
+
+
+ ast_localtime(&tv, &tm, NULL);
+ /* W E E K - D A Y */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ /* D A Y */
+ if (!res) {
+ gr_say_number_female(tm.tm_mday, chan, ints, lang);
+ }
+ /* M O N T H */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ /* Y E A R */
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+
+
+/* A list of the files that you need to create
+ * digits/female/1..4 : "Mia, dyo , treis, tesseris "
+ * digits/kai : "KAI"
+ * didgits : "h wra"
+ * digits/p-m : "meta meshmbrias"
+ * digits/a-m : "pro meshmbrias"
+ */
+
+static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+
+ res = gr_say_number_female(hour, chan, ints, lang);
+ if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/kai", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/hwra", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+
+
+static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ /* W E E K - D A Y */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ /* D A Y */
+ if (!res) {
+ gr_say_number_female(tm.tm_mday, chan, ints, lang);
+ }
+ /* M O N T H */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ res = ast_say_time_gr(chan, t, ints, lang);
+ return res;
+}
+
+static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "AdBY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* first - thirtyfirst */
+ gr_say_number_female(tm.tm_mday, chan, ints, lang);
+ break;
+ case 'Y':
+ /* Year */
+
+ ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ gr_say_number_female(12, chan, ints, lang);
+ else if (tm.tm_hour > 12)
+ gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
+ else
+ gr_say_number_female(tm.tm_hour, chan, ints, lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ gr_say_number_female(tm.tm_hour, chan, ints, lang);
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/kai", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ ast_copy_string(nextmsg, "digits/kai", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res)
+ res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
+ if (!res)
+ ast_copy_string(nextmsg, "digits/seconds", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'T':
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+
+
+
+/*********************************** Georgian Support ***************************************/
+
+
+/*
+ Convert a number into a semi-localized string. Only for Georgian.
+ res must be of at least 256 bytes, preallocated.
+ The output corresponds to Georgian spoken numbers, so
+ it may be either converted to real words by applying a direct conversion
+ table, or played just by substituting the entities with played files.
+
+ Output may consist of the following tokens (separated by spaces):
+ 0, minus.
+ 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
+ 10-19.
+ 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
+ 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
+ 1000, 1000_. (atasi, atas).
+ 1000000, 1000000_. (milioni, milion).
+ 1000000000, 1000000000_. (miliardi, miliard).
+
+ To be able to play the sounds, each of the above tokens needs
+ a corresponding sound file. (e.g. 200_.gsm).
+*/
+static char* ast_translate_number_ge(int num, char* res, int res_len)
+{
+ char buf[256];
+ int digit = 0;
+ int remainder = 0;
+
+
+ if (num < 0) {
+ strncat(res, "minus ", res_len - strlen(res) - 1);
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ }
+
+
+ /* directly read the numbers */
+ if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
+ snprintf(buf, sizeof(buf), "%d", num);
+ strncat(res, buf, res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num < 40) { /* ocda... */
+ strncat(res, "20_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 20, res, res_len);
+ }
+
+ if (num < 60) { /* ormocda... */
+ strncat(res, "40_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 40, res, res_len);
+ }
+
+ if (num < 80) { /* samocda... */
+ strncat(res, "60_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 60, res, res_len);
+ }
+
+ if (num < 100) { /* otxmocda... */
+ strncat(res, "80_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 80, res, res_len);
+ }
+
+
+ if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
+ remainder = num % 100;
+ digit = (num - remainder) / 100;
+
+ if (remainder == 0) {
+ snprintf(buf, sizeof(buf), "%d", num);
+ strncat(res, buf, res_len - strlen(res) - 1);
+ return res;
+ } else {
+ snprintf(buf, sizeof(buf), "%d_ ", digit*100);
+ strncat(res, buf, res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+ }
+ }
+
+
+ if (num == 1000) {
+ strncat(res, "1000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num < 1000000) {
+ remainder = num % 1000;
+ digit = (num - remainder) / 1000;
+
+ if (remainder == 0) {
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+ if (digit == 1) {
+ strncat(res, "1000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+ }
+
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+
+ }
+
+
+ if (num == 1000000) {
+ strncat(res, "1 1000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num < 1000000000) {
+ remainder = num % 1000000;
+ digit = (num - remainder) / 1000000;
+
+ if (remainder == 0) {
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+
+ }
+
+
+ if (num == 1000000000) {
+ strncat(res, "1 1000000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num > 1000000000) {
+ remainder = num % 1000000000;
+ digit = (num - remainder) / 1000000000;
+
+ if (remainder == 0) {
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+
+ }
+
+ return res;
+
+}
+
+
+
+/*! \brief ast_say_number_full_ge: Georgian syntax */
+static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ char fn[512] = "";
+ char* s = 0;
+ const char* remainder = fn;
+
+ if (!num)
+ return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
+
+
+ ast_translate_number_ge(num, fn, 512);
+
+
+
+ while (res == 0 && (s = strstr(remainder, " "))) {
+ size_t len = s - remainder;
+ char* new_string = ast_malloc(len + 1 + strlen("digits/"));
+
+ sprintf(new_string, "digits/");
+ strncat(new_string, remainder, len); /* we can't sprintf() it, it's not null-terminated. */
+/* new_string[len + strlen("digits/")] = '\0'; */
+
+ if (!ast_streamfile(chan, new_string, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+
+ ast_free(new_string);
+
+ remainder = s + 1; /* position just after the found space char. */
+ while (*remainder == ' ') /* skip multiple spaces */
+ remainder++;
+ }
+
+
+ /* the last chunk. */
+ if (res == 0 && *remainder) {
+
+ char* new_string = ast_malloc(strlen(remainder) + 1 + strlen("digits/"));
+ sprintf(new_string, "digits/%s", remainder);
+
+ if (!ast_streamfile(chan, new_string, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+
+ ast_free(new_string);
+
+ }
+
+
+ return res;
+
+}
+
+
+
+/*
+Georgian support for date/time requires the following files (*.gsm):
+
+mon-1, mon-2, ... (ianvari, tebervali, ...)
+day-1, day-2, ... (orshabati, samshabati, ...)
+saati_da
+tsuti
+tslis
+*/
+
+
+
+/* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
+static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+/* if (!res)
+ res = ast_waitstream(chan, ints);
+*/
+ }
+
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ return res;
+
+}
+
+
+
+
+
+/* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
+static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
+ if (!res) {
+ res = ast_streamfile(chan, "digits/saati_da", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ if (tm.tm_min) {
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
+
+ if (!res) {
+ res = ast_streamfile(chan, "digits/tsuti", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ }
+ return res;
+}
+
+
+
+/* Georgian syntax. Say date, then say time. */
+static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+
+}
+
+
+
+
+/* Georgian syntax */
+static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ struct timeval tv = { t, 0 }, nowt = ast_tvnow();
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowt, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ } /* Otherwise, it was today */
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+
+ return res;
+}
+
+
+
+/*
+ * remap the 'say' functions to use those in this file
+ */
+static void __attribute__((constructor)) __say_init(void)
+{
+ ast_say_number_full = say_number_full;
+ ast_say_enumeration_full = say_enumeration_full;
+ ast_say_digit_str_full = say_digit_str_full;
+ ast_say_character_str_full = say_character_str_full;
+ ast_say_phonetic_str_full = say_phonetic_str_full;
+ ast_say_datetime = say_datetime;
+ ast_say_time = say_time;
+ ast_say_date = say_date;
+ ast_say_datetime_from_now = say_datetime_from_now;
+ ast_say_date_with_format = say_date_with_format;
+}
diff --git a/trunk/main/sched.c b/trunk/main/sched.c
new file mode 100644
index 000000000..3aeeb9d82
--- /dev/null
+++ b/trunk/main/sched.c
@@ -0,0 +1,406 @@
+/*
+ * 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 Scheduler Routines (from cheops-NG)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef DEBUG_SCHEDULER
+#define DEBUG(a) do { \
+ if (option_debug) \
+ DEBUG_M(a) \
+ } while (0)
+#else
+#define DEBUG(a)
+#endif
+
+#include <sys/time.h>
+
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+
+struct sched {
+ AST_LIST_ENTRY(sched) list;
+ int id; /*!< ID number of event */
+ struct timeval when; /*!< Absolute time event should take place */
+ int resched; /*!< When to reschedule */
+ int variable; /*!< Use return value from callback to reschedule */
+ const void *data; /*!< Data */
+ ast_sched_cb callback; /*!< Callback */
+};
+
+struct sched_context {
+ ast_mutex_t lock;
+ unsigned int eventcnt; /*!< Number of events processed */
+ unsigned int schedcnt; /*!< Number of outstanding schedule events */
+ AST_LIST_HEAD_NOLOCK(, sched) schedq; /*!< Schedule entry and main queue */
+
+#ifdef SCHED_MAX_CACHE
+ AST_LIST_HEAD_NOLOCK(, sched) schedc; /*!< Cache of unused schedule structures and how many */
+ unsigned int schedccnt;
+#endif
+};
+
+struct sched_context *sched_context_create(void)
+{
+ struct sched_context *tmp;
+
+ if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ return NULL;
+
+ ast_mutex_init(&tmp->lock);
+ tmp->eventcnt = 1;
+
+ return tmp;
+}
+
+void sched_context_destroy(struct sched_context *con)
+{
+ struct sched *s;
+
+ ast_mutex_lock(&con->lock);
+
+#ifdef SCHED_MAX_CACHE
+ /* Eliminate the cache */
+ while ((s = AST_LIST_REMOVE_HEAD(&con->schedc, list)))
+ ast_free(s);
+#endif
+
+ /* And the queue */
+ while ((s = AST_LIST_REMOVE_HEAD(&con->schedq, list)))
+ ast_free(s);
+
+ /* And the context */
+ ast_mutex_unlock(&con->lock);
+ ast_mutex_destroy(&con->lock);
+ ast_free(con);
+}
+
+static struct sched *sched_alloc(struct sched_context *con)
+{
+ struct sched *tmp;
+
+ /*
+ * We keep a small cache of schedule entries
+ * to minimize the number of necessary malloc()'s
+ */
+#ifdef SCHED_MAX_CACHE
+ if ((tmp = AST_LIST_REMOVE_HEAD(&con->schedc, list)))
+ con->schedccnt--;
+ else
+#endif
+ tmp = ast_calloc(1, sizeof(*tmp));
+
+ return tmp;
+}
+
+static void sched_release(struct sched_context *con, struct sched *tmp)
+{
+ /*
+ * Add to the cache, or just free() if we
+ * already have too many cache entries
+ */
+
+#ifdef SCHED_MAX_CACHE
+ if (con->schedccnt < SCHED_MAX_CACHE) {
+ AST_LIST_INSERT_HEAD(&con->schedc, tmp, list);
+ con->schedccnt++;
+ } else
+#endif
+ ast_free(tmp);
+}
+
+/*! \brief
+ * Return the number of milliseconds
+ * until the next scheduled event
+ */
+int ast_sched_wait(struct sched_context *con)
+{
+ int ms;
+
+ DEBUG(ast_debug(1, "ast_sched_wait()\n"));
+
+ ast_mutex_lock(&con->lock);
+ if (AST_LIST_EMPTY(&con->schedq)) {
+ ms = -1;
+ } else {
+ ms = ast_tvdiff_ms(AST_LIST_FIRST(&con->schedq)->when, ast_tvnow());
+ if (ms < 0)
+ ms = 0;
+ }
+ ast_mutex_unlock(&con->lock);
+
+ return ms;
+}
+
+
+/*! \brief
+ * Take a sched structure and put it in the
+ * queue, such that the soonest event is
+ * first in the list.
+ */
+static void schedule(struct sched_context *con, struct sched *s)
+{
+
+ struct sched *cur = NULL;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&con->schedq, cur, list) {
+ if (ast_tvcmp(s->when, cur->when) == -1) {
+ AST_LIST_INSERT_BEFORE_CURRENT(s, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ if (!cur)
+ AST_LIST_INSERT_TAIL(&con->schedq, s, list);
+
+ con->schedcnt++;
+}
+
+/*! \brief
+ * given the last event *tv and the offset in milliseconds 'when',
+ * computes the next value,
+ */
+static int sched_settime(struct timeval *tv, int when)
+{
+ struct timeval now = ast_tvnow();
+
+ /*ast_debug(1, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
+ if (ast_tvzero(*tv)) /* not supplied, default to now */
+ *tv = now;
+ *tv = ast_tvadd(*tv, ast_samp2tv(when, 1000));
+ if (ast_tvcmp(*tv, now) < 0) {
+ ast_debug(1, "Request to schedule in the past?!?!\n");
+ *tv = now;
+ }
+ return 0;
+}
+
+int ast_sched_replace_variable(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
+{
+ /* 0 means the schedule item is new; do not delete */
+ if (old_id > 0)
+ ast_sched_del(con, old_id);
+ return ast_sched_add_variable(con, when, callback, data, variable);
+}
+
+/*! \brief
+ * Schedule callback(data) to happen when ms into the future
+ */
+int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
+{
+ struct sched *tmp;
+ int res = -1;
+ DEBUG(ast_debug(1, "ast_sched_add()\n"));
+ if (!when) {
+ ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
+ return -1;
+ }
+ ast_mutex_lock(&con->lock);
+ if ((tmp = sched_alloc(con))) {
+ tmp->id = con->eventcnt++;
+ tmp->callback = callback;
+ tmp->data = data;
+ tmp->resched = when;
+ tmp->variable = variable;
+ tmp->when = ast_tv(0, 0);
+ if (sched_settime(&tmp->when, when)) {
+ sched_release(con, tmp);
+ } else {
+ schedule(con, tmp);
+ res = tmp->id;
+ }
+ }
+#ifdef DUMP_SCHEDULER
+ /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
+ if (option_debug)
+ ast_sched_dump(con);
+#endif
+ ast_mutex_unlock(&con->lock);
+ return res;
+}
+
+int ast_sched_replace(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data)
+{
+ if (old_id > -1)
+ ast_sched_del(con, old_id);
+ return ast_sched_add(con, when, callback, data);
+}
+
+int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data)
+{
+ return ast_sched_add_variable(con, when, callback, data, 0);
+}
+
+/*! \brief
+ * Delete the schedule entry with number
+ * "id". It's nearly impossible that there
+ * would be two or more in the list with that
+ * id.
+ */
+int ast_sched_del(struct sched_context *con, int id)
+{
+ struct sched *s;
+
+ DEBUG(ast_debug(1, "ast_sched_del()\n"));
+
+ ast_mutex_lock(&con->lock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&con->schedq, s, list) {
+ if (s->id == id) {
+ AST_LIST_REMOVE_CURRENT(list);
+ con->schedcnt--;
+ sched_release(con, s);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+#ifdef DUMP_SCHEDULER
+ /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
+ if (option_debug)
+ ast_sched_dump(con);
+#endif
+ ast_mutex_unlock(&con->lock);
+
+ if (!s) {
+ ast_debug(1, "Attempted to delete nonexistent schedule entry %d!\n", id);
+#ifdef DO_CRASH
+ CRASH;
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! \brief Dump the contents of the scheduler to LOG_DEBUG */
+void ast_sched_dump(const struct sched_context *con)
+{
+ struct sched *q;
+ struct timeval tv = ast_tvnow();
+#ifdef SCHED_MAX_CACHE
+ ast_debug(1, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
+#else
+ ast_debug(1, "Asterisk Schedule Dump (%d in Q, %d Total)\n", con->schedcnt, con->eventcnt - 1);
+#endif
+
+ ast_debug(1, "=============================================================\n");
+ ast_debug(1, "|ID Callback Data Time (sec:ms) |\n");
+ ast_debug(1, "+-----+-----------------+-----------------+-----------------+\n");
+ AST_LIST_TRAVERSE(&con->schedq, q, list) {
+ struct timeval delta = ast_tvsub(q->when, tv);
+
+ ast_debug(1, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
+ q->id,
+ q->callback,
+ q->data,
+ delta.tv_sec,
+ (long int)delta.tv_usec);
+ }
+ ast_debug(1, "=============================================================\n");
+}
+
+/*! \brief
+ * Launch all events which need to be run at this time.
+ */
+int ast_sched_runq(struct sched_context *con)
+{
+ struct sched *current;
+ struct timeval tv;
+ int numevents;
+ int res;
+
+ DEBUG(ast_debug(1, "ast_sched_runq()\n"));
+
+ ast_mutex_lock(&con->lock);
+
+ for (numevents = 0; !AST_LIST_EMPTY(&con->schedq); numevents++) {
+ /* schedule all events which are going to expire within 1ms.
+ * We only care about millisecond accuracy anyway, so this will
+ * help us get more than one event at one time if they are very
+ * close together.
+ */
+ tv = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
+ if (ast_tvcmp(AST_LIST_FIRST(&con->schedq)->when, tv) != -1)
+ break;
+
+ current = AST_LIST_REMOVE_HEAD(&con->schedq, list);
+ con->schedcnt--;
+
+ /*
+ * At this point, the schedule queue is still intact. We
+ * have removed the first event and the rest is still there,
+ * so it's permissible for the callback to add new events, but
+ * trying to delete itself won't work because it isn't in
+ * the schedule queue. If that's what it wants to do, it
+ * should return 0.
+ */
+
+ ast_mutex_unlock(&con->lock);
+ res = current->callback(current->data);
+ ast_mutex_lock(&con->lock);
+
+ if (res) {
+ /*
+ * If they return non-zero, we should schedule them to be
+ * run again.
+ */
+ if (sched_settime(&current->when, current->variable? res : current->resched)) {
+ sched_release(con, current);
+ } else
+ schedule(con, current);
+ } else {
+ /* No longer needed, so release it */
+ sched_release(con, current);
+ }
+ }
+
+ ast_mutex_unlock(&con->lock);
+
+ return numevents;
+}
+
+long ast_sched_when(struct sched_context *con,int id)
+{
+ struct sched *s;
+ long secs = -1;
+ DEBUG(ast_debug(1, "ast_sched_when()\n"));
+
+ ast_mutex_lock(&con->lock);
+ AST_LIST_TRAVERSE(&con->schedq, s, list) {
+ if (s->id == id)
+ break;
+ }
+ if (s) {
+ struct timeval now = ast_tvnow();
+ secs = s->when.tv_sec - now.tv_sec;
+ }
+ ast_mutex_unlock(&con->lock);
+
+ return secs;
+}
diff --git a/trunk/main/sha1.c b/trunk/main/sha1.c
new file mode 100644
index 000000000..4226ecf63
--- /dev/null
+++ b/trunk/main/sha1.c
@@ -0,0 +1,337 @@
+/*! \file
+ *
+ * \brief Based on the RFC 3174
+ *
+ * Full Copyright Statement
+ *
+ * Copyright (C) The Internet Society (2001). All Rights Reserved.
+ *
+ * This document and translations of it may be copied and furnished to
+ * others, and derivative works that comment on or otherwise explain it
+ * or assist in its implementation may be prepared, copied, published
+ * and distributed, in whole or in part, without restriction of any
+ * kind, provided that the above copyright notice and this paragraph are
+ * included on all such copies and derivative works. However, this
+ * document itself may not be modified in any way, such as by removing
+ * the copyright notice or references to the Internet Society or other
+ * Internet organizations, except as needed for the purpose of
+ * developing Internet standards in which case the procedures for
+ * copyrights defined in the Internet Standards process must be
+ * followed, or as required to translate it into languages other than
+ * English.
+ *
+ * The limited permissions granted above are perpetual and will not be
+ * revoked by the Internet Society or its successors or assigns.
+
+ * This document and the information contained herein is provided on an
+ * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ *
+ * Description:
+ * This file implements the Secure Hashing Algorithm 1 as
+ * defined in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * The SHA-1, produces a 160-bit message digest for a given
+ * data stream. It should take about 2**n steps to find a
+ * message with the same digest as a given message and
+ * 2**(n/2) to find any two messages with the same digest,
+ * when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code
+ * uses <stdint.h> (included via "sha1.h" to define 32 and 8
+ * bit unsigned integer types. If your C compiler does not
+ * support 32 bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits
+ * long. Although SHA-1 allows a message digest to be generated
+ * for messages of any number of bits less than 2^64, this
+ * implementation only works with messages with a length that is
+ * a multiple of the size of an 8-bit character.
+ *
+ */
+
+#include "asterisk.h"
+#include "asterisk/sha1.h"
+
+/*! Define the SHA1 circular left shift macro */
+#define SHA1CircularShift(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+void SHA1PadMessage(SHA1Context *);
+void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*!
+ * \brief SHA1Reset
+ * \param context the context to be reset.
+ * This function will initialize the SHA1Context in preparation
+ * for computing a new SHA1 message digest.
+ * \return sha Error Code.
+ */
+int SHA1Reset(SHA1Context *context)
+{
+ if (!context) {
+ return shaNull;
+ }
+
+ context->Length_Low = 0;
+ context->Length_High = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = 0x67452301;
+ context->Intermediate_Hash[1] = 0xEFCDAB89;
+ context->Intermediate_Hash[2] = 0x98BADCFE;
+ context->Intermediate_Hash[3] = 0x10325476;
+ context->Intermediate_Hash[4] = 0xC3D2E1F0;
+
+ context->Computed = 0;
+ context->Corrupted = 0;
+
+ return shaSuccess;
+}
+
+/*!
+ * \brief SHA1Result
+ * \param context [in/out] The context to use to calculate the SHA-1 hash.
+ * \param Message_Digest [out] Where the digest is returned.
+ * This function will return the 160-bit message digest into the
+ * Message_Digest array provided by the caller.
+ * \note The first octet of hash is stored in the 0th element,
+ * the last octet of hash in the 19th element.
+ * \return sha Error Code.
+ */
+int SHA1Result( SHA1Context *context,
+ uint8_t Message_Digest[SHA1HashSize])
+{
+ int i;
+
+ if (!context || !Message_Digest) {
+ return shaNull;
+ }
+
+ if (context->Corrupted) {
+ return context->Corrupted;
+ }
+
+ if (!context->Computed) {
+ SHA1PadMessage(context);
+ for (i = 0; i < 64; ++i) {
+ /* message may be sensitive, clear it out */
+ context->Message_Block[i] = 0;
+ }
+ context->Length_Low = 0; /* and clear length */
+ context->Length_High = 0;
+ context->Computed = 1;
+ }
+
+ for (i = 0; i < SHA1HashSize; ++i) {
+ Message_Digest[i] = context->Intermediate_Hash[i >> 2] >> 8 * ( 3 - ( i & 0x03 ) );
+ }
+
+ return shaSuccess;
+}
+
+/*!
+ * \brief SHA1Input
+ * \param context [in/out] The SHA context to update
+ * \param message_array [in] An array of characters representing the next portion of
+ * the message.
+ * \param length [in] The length of the message in message_array.
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ * \return sha Error Code.
+ */
+int SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length)
+{
+ if (!length) {
+ return shaSuccess;
+ }
+
+ if (!context || !message_array) {
+ return shaNull;
+ }
+
+ if (context->Computed) {
+ context->Corrupted = shaStateError;
+ return shaStateError;
+ }
+
+ if (context->Corrupted) {
+ return context->Corrupted;
+ }
+
+ while (length-- && !context->Corrupted) {
+ context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);
+
+ context->Length_Low += 8;
+ if (context->Length_Low == 0) {
+ context->Length_High++;
+ if (context->Length_High == 0) {
+ /* Message is too long */
+ context->Corrupted = 1;
+ }
+ }
+
+ if (context->Message_Block_Index == 64) {
+ SHA1ProcessMessageBlock(context);
+ }
+
+ message_array++;
+ }
+
+ return shaSuccess;
+}
+
+/*!
+ * \brief Process the next 512 bits of the message stored in the Message_Block array.
+ * \param context [in/out] The SHA context to update
+ * \note Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the publication.
+ * \returns nothing.
+ */
+void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+ const uint32_t K[] = { /* Constants defined in SHA-1 */
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ uint32_t temp; /* Temporary word value */
+ uint32_t W[80]; /* Word sequence */
+ uint32_t A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = 0; t < 16; t++) {
+ W[t] = context->Message_Block[t * 4] << 24;
+ W[t] |= context->Message_Block[t * 4 + 1] << 16;
+ W[t] |= context->Message_Block[t * 4 + 2] << 8;
+ W[t] |= context->Message_Block[t * 4 + 3];
+ }
+
+ for (t = 16; t < 80; t++) {
+ W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+
+ for (t = 0; t < 20; t++) {
+ temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 20; t < 40; t++) {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 40; t < 60; t++) {
+ temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 60; t < 80; t++) {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+
+ context->Message_Block_Index = 0;
+}
+
+
+/*!
+ * \brief Pad message to be 512 bits.
+ * \param context [in/out] The context to pad.
+ *
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64
+ * bits represent the length of the original message. All bits in
+ * between should be 0. This function will pad the message
+ * according to those rules by filling the Message_Block array
+ * accordingly. It will also call the ProcessMessageBlock function
+ * provided appropriately. When it returns, it can be assumed that
+ * the message digest has been computed.
+ *
+ * \returns nothing.
+ */
+
+void SHA1PadMessage(SHA1Context *context)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index > 55) {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while (context->Message_Block_Index < 64) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+
+ SHA1ProcessMessageBlock(context);
+
+ while (context->Message_Block_Index < 56) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ } else {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while (context->Message_Block_Index < 56) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = context->Length_High >> 24;
+ context->Message_Block[57] = context->Length_High >> 16;
+ context->Message_Block[58] = context->Length_High >> 8;
+ context->Message_Block[59] = context->Length_High;
+ context->Message_Block[60] = context->Length_Low >> 24;
+ context->Message_Block[61] = context->Length_Low >> 16;
+ context->Message_Block[62] = context->Length_Low >> 8;
+ context->Message_Block[63] = context->Length_Low;
+
+ SHA1ProcessMessageBlock(context);
+}
diff --git a/trunk/main/slinfactory.c b/trunk/main/slinfactory.c
new file mode 100644
index 000000000..25d232a37
--- /dev/null
+++ b/trunk/main/slinfactory.c
@@ -0,0 +1,165 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Anthony Minessale II.
+ *
+ * Anthony Minessale <anthmct@yahoo.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 A machine to gather up arbitrary frames and convert them
+ * to raw slinear on demand.
+ *
+ * \author Anthony Minessale <anthmct@yahoo.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/frame.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/translate.h"
+
+void ast_slinfactory_init(struct ast_slinfactory *sf)
+{
+ memset(sf, 0, sizeof(*sf));
+ sf->offset = sf->hold;
+}
+
+void ast_slinfactory_destroy(struct ast_slinfactory *sf)
+{
+ struct ast_frame *f;
+
+ if (sf->trans) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+
+ while ((f = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list)))
+ ast_frfree(f);
+}
+
+int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f)
+{
+ struct ast_frame *begin_frame = f, *duped_frame = NULL, *frame_ptr;
+ unsigned int x;
+
+ if (f->subclass != AST_FORMAT_SLINEAR) {
+ if (sf->trans && f->subclass != sf->format) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+ if (!sf->trans) {
+ if (!(sf->trans = ast_translator_build_path(AST_FORMAT_SLINEAR, f->subclass))) {
+ ast_log(LOG_WARNING, "Cannot build a path from %s to slin\n", ast_getformatname(f->subclass));
+ return 0;
+ }
+ sf->format = f->subclass;
+ }
+ if (!(begin_frame = ast_translate(sf->trans, f, 0)) || !(duped_frame = ast_frdup(begin_frame)))
+ return 0;
+ } else {
+ if (sf->trans) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+ if (!(duped_frame = ast_frdup(f)))
+ return 0;
+ }
+
+ x = 0;
+ AST_LIST_TRAVERSE(&sf->queue, frame_ptr, frame_list)
+ x++;
+
+ AST_LIST_INSERT_TAIL(&sf->queue, duped_frame, frame_list);
+
+ sf->size += duped_frame->samples;
+
+ return x;
+}
+
+int ast_slinfactory_read(struct ast_slinfactory *sf, short *buf, size_t samples)
+{
+ struct ast_frame *frame_ptr;
+ unsigned int sofar = 0, ineed, remain;
+ short *frame_data, *offset = buf;
+
+ while (sofar < samples) {
+ ineed = samples - sofar;
+
+ if (sf->holdlen) {
+ if ((sofar + sf->holdlen) <= ineed) {
+ memcpy(offset, sf->hold, sf->holdlen * sizeof(*offset));
+ sofar += sf->holdlen;
+ offset += sf->holdlen;
+ sf->holdlen = 0;
+ sf->offset = sf->hold;
+ } else {
+ remain = sf->holdlen - ineed;
+ memcpy(offset, sf->offset, ineed * sizeof(*offset));
+ sofar += ineed;
+ sf->offset += ineed;
+ sf->holdlen = remain;
+ }
+ continue;
+ }
+
+ if ((frame_ptr = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list))) {
+ frame_data = frame_ptr->data;
+
+ if ((sofar + frame_ptr->samples) <= ineed) {
+ memcpy(offset, frame_data, frame_ptr->samples * sizeof(*offset));
+ sofar += frame_ptr->samples;
+ offset += frame_ptr->samples;
+ } else {
+ remain = frame_ptr->samples - ineed;
+ memcpy(offset, frame_data, ineed * sizeof(*offset));
+ sofar += ineed;
+ frame_data += ineed;
+ memcpy(sf->hold, frame_data, remain * sizeof(*offset));
+ sf->holdlen = remain;
+ }
+ ast_frfree(frame_ptr);
+ } else {
+ break;
+ }
+ }
+
+ sf->size -= sofar;
+ return sofar;
+}
+
+unsigned int ast_slinfactory_available(const struct ast_slinfactory *sf)
+{
+ return sf->size;
+}
+
+void ast_slinfactory_flush(struct ast_slinfactory *sf)
+{
+ struct ast_frame *fr = NULL;
+
+ if (sf->trans) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+
+ while ((fr = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list)))
+ ast_frfree(fr);
+
+ sf->size = sf->holdlen = 0;
+ sf->offset = sf->hold;
+
+ return;
+}
diff --git a/trunk/main/srv.c b/trunk/main/srv.c
new file mode 100644
index 000000000..3950e255f
--- /dev/null
+++ b/trunk/main/srv.c
@@ -0,0 +1,238 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Funding provided by nic.at
+ *
+ * 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 DNS SRV Record Lookup Support for Asterisk
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg See also \ref AstENUM
+ *
+ * \note Funding provided by nic.at
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef __APPLE__
+#if __APPLE_CC__ >= 1495
+#include <arpa/nameser_compat.h>
+#endif
+#endif
+#include <resolv.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/srv.h"
+#include "asterisk/dns.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+
+#ifdef __APPLE__
+#undef T_SRV
+#define T_SRV 33
+#endif
+
+struct srv_entry {
+ unsigned short priority;
+ unsigned short weight;
+ unsigned short port;
+ unsigned int weight_sum;
+ AST_LIST_ENTRY(srv_entry) list;
+ char host[1];
+};
+
+struct srv_context {
+ unsigned int have_weights:1;
+ AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries;
+};
+
+static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
+{
+ struct srv {
+ unsigned short priority;
+ unsigned short weight;
+ unsigned short port;
+ } __attribute__ ((__packed__)) *srv = (struct srv *) answer;
+
+ int res = 0;
+ char repl[256] = "";
+ struct srv_entry *entry;
+
+ if (len < sizeof(*srv))
+ return -1;
+
+ answer += sizeof(*srv);
+ len -= sizeof(*srv);
+
+ if ((res = dn_expand(msg, answer + len, answer, repl, sizeof(repl) - 1)) <= 0) {
+ ast_log(LOG_WARNING, "Failed to expand hostname\n");
+ return -1;
+ }
+
+ /* the magic value "." for the target domain means that this service
+ is *NOT* available at the domain we searched */
+ if (!strcmp(repl, "."))
+ return -1;
+
+ if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(repl))))
+ return -1;
+
+ entry->priority = ntohs(srv->priority);
+ entry->weight = ntohs(srv->weight);
+ entry->port = ntohs(srv->port);
+ strcpy(entry->host, repl);
+
+ *result = entry;
+
+ return 0;
+}
+
+static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct srv_context *c = (struct srv_context *) context;
+ struct srv_entry *entry = NULL;
+ struct srv_entry *current;
+
+ if (parse_srv(answer, len, fullanswer, &entry))
+ return -1;
+
+ if (entry->weight)
+ c->have_weights = 1;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&c->entries, current, list) {
+ /* insert this entry just before the first existing
+ entry with a higher priority */
+ if (current->priority <= entry->priority)
+ continue;
+
+ AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
+ entry = NULL;
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* if we didn't find a place to insert the entry before an existing
+ entry, then just add it to the end */
+ if (entry)
+ AST_LIST_INSERT_TAIL(&c->entries, entry, list);
+
+ return 1;
+}
+
+/* Do the bizarre SRV record weight-handling algorithm
+ involving sorting and random number generation...
+
+ See RFC 2782 if you want know why this code does this
+*/
+static void process_weights(struct srv_context *context)
+{
+ struct srv_entry *current;
+ struct srv_entries newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+ while (AST_LIST_FIRST(&context->entries)) {
+ unsigned int random_weight;
+ unsigned int weight_sum;
+ unsigned short cur_priority = AST_LIST_FIRST(&context->entries)->priority;
+ struct srv_entries temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+ weight_sum = 0;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&context->entries, current, list) {
+ if (current->priority != cur_priority)
+ break;
+
+ AST_LIST_MOVE_CURRENT(&temp_list, list);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ while (AST_LIST_FIRST(&temp_list)) {
+ weight_sum = 0;
+ AST_LIST_TRAVERSE(&temp_list, current, list)
+ current->weight_sum = weight_sum += current->weight;
+
+ /* if all the remaining entries have weight == 0,
+ then just append them to the result list and quit */
+ if (weight_sum == 0) {
+ AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
+ break;
+ }
+
+ random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
+ if (current->weight < random_weight)
+ continue;
+
+ AST_LIST_MOVE_CURRENT(&newlist, list);
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+
+ }
+
+ /* now that the new list has been ordered,
+ put it in place */
+
+ AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
+}
+
+int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
+{
+ struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE };
+ struct srv_entry *current;
+ int ret;
+
+ if (chan && ast_autoservice_start(chan) < 0)
+ return -1;
+
+ ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback);
+
+ if (context.have_weights)
+ process_weights(&context);
+
+ if (chan)
+ ret |= ast_autoservice_stop(chan);
+
+ /* TODO: there could be a "." entry in the returned list of
+ answers... if so, this requires special handling */
+
+ /* the list of entries will be sorted in the proper selection order
+ already, so we just need the first one (if any) */
+
+ if ((ret > 0) && (current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
+ ast_copy_string(host, current->host, hostlen);
+ *port = current->port;
+ ast_free(current);
+ ast_verb(3, "ast_get_srv: SRV lookup for '%s' mapped to host %s, port %d\n",
+ service, host, *port);
+ } else {
+ host[0] = '\0';
+ *port = -1;
+ }
+
+ while ((current = AST_LIST_REMOVE_HEAD(&context.entries, list)))
+ ast_free(current);
+
+ return ret;
+}
diff --git a/trunk/main/stdtime/Makefile b/trunk/main/stdtime/Makefile
new file mode 100644
index 000000000..cbe3c48f7
--- /dev/null
+++ b/trunk/main/stdtime/Makefile
@@ -0,0 +1,29 @@
+OBJS=localtime.o
+
+all: libtime.a
+
+libtime.a: $(OBJS)
+ ar rv $@ $(OBJS)
+ ranlib $@
+
+install:
+
+uninstall:
+
+clean-depend:
+ rm -f .depend
+
+clean: clean-depend
+ rm -f libtime.a *.o test
+
+depend: .depend
+
+.depend:
+ ../build_tools/mkdep $(CFLAGS) *.c
+
+test: test.c
+ ${CC} ${CFLAGS} -o test test.c
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/trunk/main/stdtime/localtime.c b/trunk/main/stdtime/localtime.c
new file mode 100644
index 000000000..2d60beefd
--- /dev/null
+++ b/trunk/main/stdtime/localtime.c
@@ -0,0 +1,1820 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Most of this code is in the public domain, so clarified as of
+ * June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
+ *
+ * All modifications to this code to abstract timezones away from
+ * the environment are by Tilghman Lesher, <tlesher@vcch.com>, with
+ * the copyright assigned to Digium.
+ *
+ * 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
+ *
+ * Multi-timezone Localtime code
+ *
+ * The original source from this file may be obtained from ftp://elsie.nci.nih.gov/pub/
+ */
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
+*/
+
+/* #define DEBUG */
+
+/*LINTLIBRARY*/
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <float.h>
+
+#include "private.h"
+#include "tzfile.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/localtime.h"
+#include "asterisk/strings.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/utils.h"
+
+#ifndef lint
+#ifndef NOID
+static char __attribute__((unused)) elsieid[] = "@(#)localtime.c 8.5";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef TZ_ABBR_MAX_LEN
+#define TZ_ABBR_MAX_LEN 16
+#endif /* !defined TZ_ABBR_MAX_LEN */
+
+#ifndef TZ_ABBR_CHAR_SET
+#define TZ_ABBR_CHAR_SET \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
+#endif /* !defined TZ_ABBR_CHAR_SET */
+
+#ifndef TZ_ABBR_ERR_CHAR
+#define TZ_ABBR_ERR_CHAR '_'
+#endif /* !defined TZ_ABBR_ERR_CHAR */
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE O_RDONLY
+#endif /* !defined O_BINARY */
+
+static const char gmt[] = "GMT";
+static const struct timeval WRONG = { 0, 0 };
+
+/*! \note
+ * The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+ * We default to US rules as of 1999-08-17.
+ * POSIX 1003.1 section 8.1.1 says that the default DST rules are
+ * implementation dependent; for historical reasons, US rules are a
+ * common default.
+ */
+#ifndef TZDEFRULESTRING
+#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
+#endif /* !defined TZDEFDST */
+
+/*!< \brief time type information */
+struct ttinfo { /* time type information */
+ long tt_gmtoff; /* UTC offset in seconds */
+ int tt_isdst; /* used to set tm_isdst */
+ int tt_abbrind; /* abbreviation list index */
+ int tt_ttisstd; /* TRUE if transition is std time */
+ int tt_ttisgmt; /* TRUE if transition is UTC */
+};
+
+/*! \brief leap second information */
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+};
+
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX 255
+#endif /* !defined TZNAME_MAX */
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+struct state {
+ /*! Name of the file that this references */
+ char name[TZ_STRLEN_MAX + 1];
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ int goback;
+ int goahead;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+ (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+ AST_LIST_ENTRY(state) list;
+};
+
+struct rule {
+ int r_type; /* type of rule--see below */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ long r_time; /* transition time of rule */
+};
+
+#define JULIAN_DAY 0 /* Jn - Julian day */
+#define DAY_OF_YEAR 1 /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+static long detzcode P((const char * codep));
+static time_t detzcode64 P((const char * codep));
+static int differ_by_repeat P((time_t t1, time_t t0));
+static const char * getzname P((const char * strp));
+static const char * getqzname P((const char * strp, const int delim));
+static const char * getnum P((const char * strp, int * nump, int min,
+ int max));
+static const char * getsecs P((const char * strp, long * secsp));
+static const char * getoffset P((const char * strp, long * offsetp));
+static const char * getrule P((const char * strp, struct rule * rulep));
+static int gmtload P((struct state * sp));
+static struct ast_tm * gmtsub P((const struct timeval * timep, long offset,
+ struct ast_tm * tmp));
+static struct ast_tm * localsub P((const struct timeval * timep, long offset,
+ struct ast_tm * tmp, const struct state *sp));
+static int increment_overflow P((int * number, int delta));
+static int leaps_thru_end_of P((int y));
+static int long_increment_overflow P((long * number, int delta));
+static int long_normalize_overflow P((long * tensptr,
+ int * unitsptr, const int base));
+static int normalize_overflow P((int * tensptr, int * unitsptr,
+ const int base));
+static struct timeval time1 P((struct ast_tm * tmp,
+ struct ast_tm * (*funcp) P((const struct timeval *,
+ long, struct ast_tm *, const struct state *sp)),
+ long offset, const struct state *sp));
+static struct timeval time2 P((struct ast_tm *tmp,
+ struct ast_tm * (*funcp) P((const struct timeval *,
+ long, struct ast_tm*, const struct state *sp)),
+ long offset, int * okayp, const struct state *sp));
+static struct timeval time2sub P((struct ast_tm *tmp,
+ struct ast_tm * (*funcp) (const struct timeval *,
+ long, struct ast_tm*, const struct state *sp),
+ long offset, int * okayp, int do_norm_secs, const struct state *sp));
+static struct ast_tm * timesub P((const struct timeval * timep, long offset,
+ const struct state * sp, struct ast_tm * tmp));
+static int tmcomp P((const struct ast_tm * atmp,
+ const struct ast_tm * btmp));
+static time_t transtime P((time_t janfirst, int year,
+ const struct rule * rulep, long offset));
+static int tzload P((const char * name, struct state * sp,
+ int doextend));
+static int tzparse P((const char * name, struct state * sp,
+ int lastditch));
+
+static AST_LIST_HEAD_STATIC(zonelist, state);
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+/*! \note
+** Section 4.12.3 of X3.159-1989 requires that
+** Except for the strftime function, these functions [asctime,
+** ctime, gmtime, localtime] return values in one of two static
+** objects: a broken-down time structure and an array of char.
+** Thanks to Paul Eggert for noting this.
+*/
+
+static long detzcode(const char * const codep)
+{
+ long result;
+ int i;
+
+ result = (codep[0] & 0x80) ? ~0L : 0;
+ for (i = 0; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+ return result;
+}
+
+static time_t detzcode64(const char * const codep)
+{
+ time_t result;
+ int i;
+
+ result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0;
+ for (i = 0; i < 8; ++i)
+ result = result * 256 + (codep[i] & 0xff);
+ return result;
+}
+
+static int differ_by_repeat(const time_t t1, const time_t t0)
+{
+ const long long at1 = t1, at0 = t0;
+ if (TYPE_INTEGRAL(time_t) &&
+ TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+ return 0;
+ return at1 - at0 == SECSPERREPEAT;
+}
+
+static int tzload(const char *name, struct state * const sp, const int doextend)
+{
+ const char * p;
+ int i;
+ int fid;
+ int stored;
+ int nread;
+ union {
+ struct tzhead tzhead;
+ char buf[2 * sizeof(struct tzhead) +
+ 2 * sizeof *sp +
+ 4 * TZ_MAX_TIMES];
+ } u;
+
+ if (name == NULL && (name = TZDEFAULT) == NULL)
+ return -1;
+ {
+ int doaccess;
+ /*
+ ** Section 4.9.1 of the C standard says that
+ ** "FILENAME_MAX expands to an integral constant expression
+ ** that is the size needed for an array of char large enough
+ ** to hold the longest file name string that the implementation
+ ** guarantees can be opened."
+ */
+ char fullname[FILENAME_MAX + 1];
+
+ if (name[0] == ':')
+ ++name;
+ doaccess = name[0] == '/';
+ if (!doaccess) {
+ if ((p = TZDIR) == NULL)
+ return -1;
+ if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+ return -1;
+ (void) strcpy(fullname, p);
+ (void) strcat(fullname, "/");
+ (void) strcat(fullname, name);
+ /*
+ ** Set doaccess if '.' (as in "../") shows up in name.
+ */
+ if (strchr(name, '.') != NULL)
+ doaccess = TRUE;
+ name = fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+ return -1;
+ if ((fid = open(name, OPEN_MODE)) == -1)
+ return -1;
+ }
+ nread = read(fid, u.buf, sizeof u.buf);
+ if (close(fid) < 0 || nread <= 0)
+ return -1;
+ for (stored = 4; stored <= 8; stored *= 2) {
+ int ttisstdcnt;
+ int ttisgmtcnt;
+
+ ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
+ ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
+ sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
+ sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
+ sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
+ sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
+ p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+ if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+ sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+ sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+ sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+ (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
+ (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
+ return -1;
+ if (nread - (p - u.buf) <
+ sp->timecnt * stored + /* ats */
+ sp->timecnt + /* types */
+ sp->typecnt * 6 + /* ttinfos */
+ sp->charcnt + /* chars */
+ sp->leapcnt * (stored + 4) + /* lsinfos */
+ ttisstdcnt + /* ttisstds */
+ ttisgmtcnt) /* ttisgmts */
+ return -1;
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->ats[i] = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
+ }
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->types[i] = (unsigned char) *p++;
+ if (sp->types[i] >= sp->typecnt)
+ return -1;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_gmtoff = detzcode(p);
+ p += 4;
+ ttisp->tt_isdst = (unsigned char) *p++;
+ if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+ return -1;
+ ttisp->tt_abbrind = (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return -1;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+ for (i = 0; i < sp->leapcnt; ++i) {
+ struct lsinfo * lsisp;
+
+ lsisp = &sp->lsis[i];
+ lsisp->ls_trans = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
+ lsisp->ls_corr = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = FALSE;
+ else {
+ ttisp->tt_ttisstd = *p++;
+ if (ttisp->tt_ttisstd != TRUE &&
+ ttisp->tt_ttisstd != FALSE)
+ return -1;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisgmtcnt == 0)
+ ttisp->tt_ttisgmt = FALSE;
+ else {
+ ttisp->tt_ttisgmt = *p++;
+ if (ttisp->tt_ttisgmt != TRUE &&
+ ttisp->tt_ttisgmt != FALSE)
+ return -1;
+ }
+ }
+ /*
+ ** Out-of-sort ats should mean we're running on a
+ ** signed time_t system but using a data file with
+ ** unsigned values (or vice versa).
+ */
+ for (i = 0; i < sp->timecnt - 2; ++i)
+ if (sp->ats[i] > sp->ats[i + 1]) {
+ ++i;
+ if (TYPE_SIGNED(time_t)) {
+ /*
+ ** Ignore the end (easy).
+ */
+ sp->timecnt = i;
+ } else {
+ /*
+ ** Ignore the beginning (harder).
+ */
+ int j;
+
+ for (j = 0; j + i < sp->timecnt; ++j) {
+ sp->ats[j] = sp->ats[j + i];
+ sp->types[j] = sp->types[j + i];
+ }
+ sp->timecnt = j;
+ }
+ break;
+ }
+ /*
+ ** If this is an old file, we're done.
+ */
+ if (u.tzhead.tzh_version[0] == '\0')
+ break;
+ nread -= p - u.buf;
+ for (i = 0; i < nread; ++i)
+ u.buf[i] = p[i];
+ /*
+ ** If this is a narrow integer time_t system, we're done.
+ */
+ if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+ break;
+ }
+ if (doextend && nread > 2 &&
+ u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ struct state ts;
+ int result;
+
+ u.buf[nread - 1] = '\0';
+ result = tzparse(&u.buf[1], &ts, FALSE);
+ if (result == 0 && ts.typecnt == 2 &&
+ sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+ for (i = 0; i < 2; ++i)
+ ts.ttis[i].tt_abbrind +=
+ sp->charcnt;
+ for (i = 0; i < ts.charcnt; ++i)
+ sp->chars[sp->charcnt++] =
+ ts.chars[i];
+ i = 0;
+ while (i < ts.timecnt &&
+ ts.ats[i] <=
+ sp->ats[sp->timecnt - 1])
+ ++i;
+ while (i < ts.timecnt &&
+ sp->timecnt < TZ_MAX_TIMES) {
+ sp->ats[sp->timecnt] =
+ ts.ats[i];
+ sp->types[sp->timecnt] =
+ sp->typecnt +
+ ts.types[i];
+ ++sp->timecnt;
+ ++i;
+ }
+ sp->ttis[sp->typecnt++] = ts.ttis[0];
+ sp->ttis[sp->typecnt++] = ts.ttis[1];
+ }
+ }
+ i = 2 * YEARSPERREPEAT;
+ sp->goback = sp->goahead = sp->timecnt > i;
+ sp->goback = sp->goback && sp->types[i] == sp->types[0] &&
+ differ_by_repeat(sp->ats[i], sp->ats[0]);
+ sp->goahead = sp->goahead &&
+ sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] &&
+ differ_by_repeat(sp->ats[sp->timecnt - 1],
+ sp->ats[sp->timecnt - 1 - i]);
+ return 0;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*! \brief
+** Given a pointer into a time zone string, scan until a character that is not
+** a valid character in a zone name is found. Return a pointer to that
+** character.
+*/
+
+static const char * getzname(const char *strp)
+{
+ char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into an extended time zone string, scan until the ending
+** delimiter of the zone name is located. Return a pointer to the delimiter.
+**
+** As with getzname above, the legal character set is actually quite
+** restricted, with other characters producing undefined results.
+** We don't do any checking here; checking is done later in common-case code.
+*/
+
+static const char * getqzname(const char *strp, const int delim)
+{
+ int c;
+
+ while ((c = *strp) != '\0' && c != delim)
+ ++strp;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *getnum(const char *strp, int *nump, const int min, const int max)
+{
+ char c;
+ int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *getsecs(const char *strp, long * const secsp)
+{
+ int num;
+
+ /*
+ ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ ** "M10.4.6/26", which does not conform to Posix,
+ ** but which specifies the equivalent of
+ ** ``02:00 on the first Sunday on or after 23 Oct''.
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (long) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* `SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *getoffset(const char *strp, long *offsetp)
+{
+ int neg = 0;
+
+ if (*strp == '-') {
+ neg = 1;
+ ++strp;
+ } else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract a rule in the form
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *getrule(const char *strp, struct rule *rulep)
+{
+ if (*strp == 'J') {
+ /*
+ ** Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ } else if (*strp == 'M') {
+ /*
+ ** Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ } else if (is_digit(*strp)) {
+ /*
+ ** Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ } else return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/') {
+ /*
+ ** Time specified.
+ */
+ ++strp;
+ strp = getsecs(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*! \brief
+** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+** year, a rule, and the offset from UTC at the time that rule takes effect,
+** calculate the Epoch-relative time that rule takes effect.
+*/
+
+static time_t transtime(const time_t janfirst, const int year, const struct rule *rulep, const long offset)
+{
+ int leapyear;
+ time_t value;
+ int i;
+ int d, m1, yy0, yy1, yy2, dow;
+
+ INITIALIZE(value);
+ leapyear = isleap(year);
+ switch (rulep->r_type) {
+
+ case JULIAN_DAY:
+ /*
+ ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ ** years.
+ ** In non-leap years, or if the day number is 59 or less, just
+ ** add SECSPERDAY times the day number-1 to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECSPERDAY;
+ break;
+
+ case DAY_OF_YEAR:
+ /*
+ ** n - day of year.
+ ** Just add SECSPERDAY times the day number to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+ value = janfirst;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+ /*
+ ** Use Zeller's Congruence to get day-of-week of first day of
+ ** month.
+ */
+ m1 = (rulep->r_mon + 9) % 12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0 / 100;
+ yy2 = yy0 % 100;
+ dow = ((26 * m1 - 2) / 10 +
+ 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ if (dow < 0)
+ dow += DAYSPERWEEK;
+
+ /*
+ ** "dow" is the day-of-week of the first day of the month. Get
+ ** the day-of-month (zero-origin) of the first "dow" day of the
+ ** month.
+ */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYSPERWEEK;
+ for (i = 1; i < rulep->r_week; ++i) {
+ if (d + DAYSPERWEEK >=
+ mon_lengths[leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYSPERWEEK;
+ }
+
+ /*
+ ** "d" is the day-of-month (zero-origin) of the day we want.
+ */
+ value += d * SECSPERDAY;
+ break;
+ }
+
+ /*
+ ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+ ** question. To get the Epoch-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from UTC.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*! \note
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int tzparse(const char *name, struct state *sp, const int lastditch)
+{
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ long stdoffset;
+ long dstoffset;
+ time_t * atp;
+ unsigned char * typep;
+ char * cp;
+ int load_result;
+
+ INITIALIZE(dstname);
+ stdname = name;
+ if (lastditch) {
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof sp->chars)
+ stdlen = (sizeof sp->chars) - 1;
+ stdoffset = 0;
+ } else {
+ if (*name == '<') {
+ name++;
+ stdname = name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return -1;
+ stdlen = name - stdname;
+ name++;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ }
+ if (*name == '\0')
+ return -1;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ load_result = tzload(TZDEFRULES, sp, FALSE);
+ if (load_result != 0)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ if (*name == '<') {
+ dstname = ++name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return -1;
+ dstlen = name - dstname;
+ name++;
+ } else {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST zone name */
+ }
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return -1;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == '\0' && load_result != 0)
+ name = TZDEFRULESTRING;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ int year;
+ time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = getrule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR forward.
+ */
+ sp->ttis[0].tt_gmtoff = -dstoffset;
+ sp->ttis[0].tt_isdst = 1;
+ sp->ttis[0].tt_abbrind = stdlen + 1;
+ sp->ttis[1].tt_gmtoff = -stdoffset;
+ sp->ttis[1].tt_isdst = 0;
+ sp->ttis[1].tt_abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ sp->timecnt = 0;
+ for (year = EPOCH_YEAR;
+ sp->timecnt + 2 <= TZ_MAX_TIMES;
+ ++year) {
+ time_t newfirst;
+
+ starttime = transtime(janfirst, year, &start,
+ stdoffset);
+ endtime = transtime(janfirst, year, &end,
+ dstoffset);
+ if (starttime > endtime) {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ } else {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ sp->timecnt += 2;
+ newfirst = janfirst;
+ newfirst += year_lengths[isleap(year)] *
+ SECSPERDAY;
+ if (newfirst <= janfirst)
+ break;
+ janfirst = newfirst;
+ }
+ } else {
+ long theirstdoffset;
+ long theirdstoffset;
+ long theiroffset;
+ int isdst;
+ int i;
+ int j;
+
+ if (*name != '\0')
+ return -1;
+ /*
+ ** Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst) {
+ theirstdoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst) {
+ theirdstoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = FALSE;
+ theiroffset = theirstdoffset;
+ /*
+ ** Now juggle transition times and types
+ ** tracking offsets as you do.
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisgmt) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time
+ ** offset to the transition time.
+ */
+ /*
+ ** Transitions from DST to DDST
+ ** will effectively disappear since
+ ** POSIX provides for only one DST
+ ** offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd) {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ } else {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_gmtoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ ** ttisstd and ttisgmt need not be handled.
+ */
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = FALSE;
+ sp->ttis[0].tt_abbrind = 0;
+ sp->ttis[1].tt_gmtoff = -dstoffset;
+ sp->ttis[1].tt_isdst = TRUE;
+ sp->ttis[1].tt_abbrind = stdlen + 1;
+ sp->typecnt = 2;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = 0;
+ sp->ttis[0].tt_abbrind = 0;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if ((size_t) sp->charcnt > sizeof sp->chars)
+ return -1;
+ cp = sp->chars;
+ (void) strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ (void) strncpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return 0;
+}
+
+static int gmtload(struct state *sp)
+{
+ if (tzload(gmt, sp, TRUE) != 0)
+ return tzparse(gmt, sp, TRUE);
+ else
+ return -1;
+}
+
+static const struct state *ast_tzset(const char *zone)
+{
+ struct state *sp;
+
+ if (ast_strlen_zero(zone))
+ zone = "/etc/localtime";
+
+ AST_LIST_LOCK(&zonelist);
+ AST_LIST_TRAVERSE(&zonelist, sp, list) {
+ if (!strcmp(sp->name, zone)) {
+ AST_LIST_UNLOCK(&zonelist);
+ return sp;
+ }
+ }
+ AST_LIST_UNLOCK(&zonelist);
+
+ if (!(sp = ast_calloc(1, sizeof *sp)))
+ return NULL;
+
+ if (tzload(zone, sp, TRUE) != 0) {
+ if (zone[0] == ':' || tzparse(zone, sp, FALSE) != 0)
+ (void) gmtload(sp);
+ }
+ ast_copy_string(sp->name, zone, sizeof(sp->name));
+ AST_LIST_LOCK(&zonelist);
+ AST_LIST_INSERT_TAIL(&zonelist, sp, list);
+ AST_LIST_UNLOCK(&zonelist);
+ return sp;
+}
+
+/*! \note
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+static struct ast_tm *localsub(const struct timeval *timep, const long offset, struct ast_tm *tmp, const struct state *sp)
+{
+ const struct ttinfo * ttisp;
+ int i;
+ struct ast_tm * result;
+ struct timeval t;
+ memcpy(&t, timep, sizeof(t));
+
+ if (sp == NULL)
+ return gmtsub(timep, offset, tmp);
+ if ((sp->goback && t.tv_sec < sp->ats[0]) ||
+ (sp->goahead && t.tv_sec > sp->ats[sp->timecnt - 1])) {
+ struct timeval newt = t;
+ time_t seconds;
+ time_t tcycles;
+ int_fast64_t icycles;
+
+ if (t.tv_sec < sp->ats[0])
+ seconds = sp->ats[0] - t.tv_sec;
+ else seconds = t.tv_sec - sp->ats[sp->timecnt - 1];
+ --seconds;
+ tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ ++tcycles;
+ icycles = tcycles;
+ if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ return NULL;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t.tv_sec < sp->ats[0])
+ newt.tv_sec += seconds;
+ else newt.tv_sec -= seconds;
+ if (newt.tv_sec < sp->ats[0] ||
+ newt.tv_sec > sp->ats[sp->timecnt - 1])
+ return NULL; /* "cannot happen" */
+ result = localsub(&newt, offset, tmp, sp);
+ if (result == tmp) {
+ time_t newy;
+
+ newy = tmp->tm_year;
+ if (t.tv_sec < sp->ats[0])
+ newy -= icycles * YEARSPERREPEAT;
+ else
+ newy += icycles * YEARSPERREPEAT;
+ tmp->tm_year = newy;
+ if (tmp->tm_year != newy)
+ return NULL;
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t.tv_sec < sp->ats[0]) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst) {
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ }
+ } else {
+ int lo = 1;
+ int hi = sp->timecnt;
+
+ while (lo < hi) {
+ int mid = (lo + hi) >> 1;
+
+ if (t.tv_sec < sp->ats[mid])
+ hi = mid;
+ else
+ lo = mid + 1;
+ }
+ i = (int) sp->types[lo - 1];
+ }
+ ttisp = &sp->ttis[i];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_gmtoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+ tmp->tm_isdst = ttisp->tt_isdst;
+ tmp->tm_gmtoff = ttisp->tt_gmtoff;
+#ifdef TM_ZONE
+ tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+ tmp->tm_usec = timep->tv_usec;
+ return result;
+}
+
+struct ast_tm *ast_localtime(const struct timeval *timep, struct ast_tm *tmp, const char *zone)
+{
+ const struct state *sp = ast_tzset(zone);
+ memset(tmp, 0, sizeof(*tmp));
+ return sp ? localsub(timep, 0L, tmp, sp) : NULL;
+}
+
+/*
+** This function provides informaton about daylight savings time
+** for the given timezone. This includes whether it can determine
+** if daylight savings is used for this timezone, the UTC times for
+** when daylight savings transitions, and the offset in seconds from
+** UTC.
+*/
+
+void ast_get_dst_info(const time_t * const timep, int *dst_enabled, time_t *dst_start, time_t *dst_end, int *gmt_off, const char * const zone)
+{
+ int i;
+ int transition1 = -1;
+ int transition2 = -1;
+ time_t seconds;
+ int bounds_exceeded = 0;
+ time_t t = *timep;
+ const struct state *sp;
+
+ if (NULL == dst_enabled)
+ return;
+ *dst_enabled = 0;
+
+ if (NULL == dst_start || NULL == dst_end || NULL == gmt_off)
+ return;
+
+ *gmt_off = 0;
+
+ sp = ast_tzset(zone);
+ if (NULL == sp)
+ return;
+
+ /* If the desired time exceeds the bounds of the defined time transitions
+ * then give give up on determining DST info and simply look for gmt offset
+ * This requires that I adjust the given time using increments of Gregorian
+ * repeats to place the time within the defined time transitions in the
+ * timezone structure.
+ */
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t tcycles;
+ int_fast64_t icycles;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ ++tcycles;
+ icycles = tcycles;
+ if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ return;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ t += seconds;
+ else
+ t -= seconds;
+
+ if (t < sp->ats[0] || t > sp->ats[sp->timecnt - 1])
+ return; /* "cannot happen" */
+
+ bounds_exceeded = 1;
+ }
+
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ /* I have no transition times or I'm before time */
+ *dst_enabled = 0;
+ /* Find where I can get gmtoff */
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ *gmt_off = sp->ttis[i].tt_gmtoff;
+ return;
+ }
+
+ for (i = 1; i < sp->timecnt; ++i) {
+ if (t < sp->ats[i]) {
+ transition1 = sp->types[i - 1];
+ transition2 = sp->types[i];
+ break;
+ }
+ }
+ /* if I found transition times that do not bounded the given time and these correspond to
+ or the bounding zones do not reflect a changes in day light savings, then I do not have dst active */
+ if (i >= sp->timecnt || 0 > transition1 || 0 > transition2 ||
+ (sp->ttis[transition1].tt_isdst == sp->ttis[transition2].tt_isdst)) {
+ *dst_enabled = 0;
+ *gmt_off = sp->ttis[sp->types[sp->timecnt -1]].tt_gmtoff;
+ } else {
+ /* I have valid daylight savings information. */
+ if(sp->ttis[transition2].tt_isdst)
+ *gmt_off = sp->ttis[transition1].tt_gmtoff;
+ else
+ *gmt_off = sp->ttis[transition2].tt_gmtoff;
+
+ /* If I adjusted the time earlier, indicate that the dst is invalid */
+ if (!bounds_exceeded) {
+ *dst_enabled = 1;
+ /* Determine which of the bounds is the start of daylight savings and which is the end */
+ if(sp->ttis[transition2].tt_isdst) {
+ *dst_start = sp->ats[i];
+ *dst_end = sp->ats[i -1];
+ } else {
+ *dst_start = sp->ats[i -1];
+ *dst_end = sp->ats[i];
+ }
+ }
+ }
+ return;
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct ast_tm *gmtsub(const struct timeval *timep, const long offset, struct ast_tm *tmp)
+{
+ struct ast_tm * result;
+ struct state *sp;
+
+ AST_LIST_LOCK(&zonelist);
+ AST_LIST_TRAVERSE(&zonelist, sp, list) {
+ if (!strcmp(sp->name, "UTC"))
+ break;
+ }
+
+ if (!sp) {
+ if (!(sp = (struct state *) ast_calloc(1, sizeof *sp)))
+ return NULL;
+ gmtload(sp);
+ AST_LIST_INSERT_TAIL(&zonelist, sp, list);
+ }
+ AST_LIST_UNLOCK(&zonelist);
+
+ result = timesub(timep, offset, sp, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ if (offset != 0)
+ tmp->TM_ZONE = " ";
+ else
+ tmp->TM_ZONE = sp->chars;
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+/*! \brief
+** Return the number of leap years through the end of the given year
+** where, to make the math easy, the answer for year zero is defined as zero.
+*/
+
+static int leaps_thru_end_of(const int y)
+{
+ return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+ -(leaps_thru_end_of(-(y + 1)) + 1);
+}
+
+static struct ast_tm *timesub(const struct timeval *timep, const long offset, const struct state *sp, struct ast_tm *tmp)
+{
+ const struct lsinfo * lp;
+ time_t tdays;
+ int idays; /* unsigned would be so 2003 */
+ long rem;
+ int y;
+ const int * ip;
+ long corr;
+ int hit;
+ int i;
+ long seconds;
+
+
+ corr = 0;
+ hit = 0;
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (timep->tv_sec >= lp->ls_trans) {
+ if (timep->tv_sec == lp->ls_trans) {
+ hit = ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ while (i > 0 &&
+ sp->lsis[i].ls_trans ==
+ sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr ==
+ sp->lsis[i - 1].ls_corr + 1) {
+ ++hit;
+ --i;
+ }
+ }
+ corr = lp->ls_corr;
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = timep->tv_sec / SECSPERDAY;
+ rem = timep->tv_sec - tdays * SECSPERDAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+ int newy;
+ time_t tdelta;
+ int idelta;
+ int leapdays;
+
+ tdelta = tdays / DAYSPERLYEAR;
+ idelta = tdelta;
+ if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
+ return NULL;
+ if (idelta == 0)
+ idelta = (tdays < 0) ? -1 : 1;
+ newy = y;
+ if (increment_overflow(&newy, idelta))
+ return NULL;
+ leapdays = leaps_thru_end_of(newy - 1) -
+ leaps_thru_end_of(y - 1);
+ tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
+ tdays -= leapdays;
+ y = newy;
+ }
+
+ seconds = tdays * SECSPERDAY + 0.5;
+ tdays = seconds / SECSPERDAY;
+ rem += seconds - tdays * SECSPERDAY;
+
+ /*
+ ** Given the range, we can now fearlessly cast...
+ */
+ idays = tdays;
+ rem += offset - corr;
+ while (rem < 0) {
+ rem += SECSPERDAY;
+ --idays;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++idays;
+ }
+ while (idays < 0) {
+ if (increment_overflow(&y, -1))
+ return NULL;
+ idays += year_lengths[isleap(y)];
+ }
+ while (idays >= year_lengths[isleap(y)]) {
+ idays -= year_lengths[isleap(y)];
+ if (increment_overflow(&y, 1))
+ return NULL;
+ }
+ tmp->tm_year = y;
+ if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+ return NULL;
+ tmp->tm_yday = idays;
+ /*
+ ** The "extra" mods below avoid overflow problems.
+ */
+ tmp->tm_wday = EPOCH_WDAY +
+ ((y - EPOCH_YEAR) % DAYSPERWEEK) *
+ (DAYSPERNYEAR % DAYSPERWEEK) +
+ leaps_thru_end_of(y - 1) -
+ leaps_thru_end_of(EPOCH_YEAR - 1) +
+ idays;
+ tmp->tm_wday %= DAYSPERWEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ ip = mon_lengths[isleap(y)];
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ idays -= ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (idays + 1);
+ tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+ tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+ tmp->tm_usec = timep->tv_usec;
+ return tmp;
+}
+
+/*! \note
+** Adapted from code provided by Robert Elz, who writes:
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago.
+** It does a binary search of the time_t space. Since time_t's are
+** just 32 bits, its a max of 32 iterations (even at 64 bits it
+** would still be very reasonable).
+*/
+
+/*! \brief
+** Simplified normalize logic courtesy Paul Eggert.
+*/
+
+static int increment_overflow(int *number, int delta)
+{
+ int number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int long_increment_overflow(long *number, int delta)
+{
+ long number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int normalize_overflow(int *tensptr, int *unitsptr, const int base)
+{
+ int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static int long_normalize_overflow(long *tensptr, int *unitsptr, const int base)
+{
+ int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return long_increment_overflow(tensptr, tensdelta);
+}
+
+static int tmcomp(const struct ast_tm *atmp, const struct ast_tm *btmp)
+{
+ int result;
+
+ if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+ (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0 &&
+ (result = (atmp->tm_sec - btmp->tm_sec)) == 0)
+ result = atmp->tm_usec - btmp->tm_usec;
+ return result;
+}
+
+static struct timeval time2sub(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm *, const struct state *), const long offset, int *okayp, const int do_norm_secs, const struct state *sp)
+{
+ int dir;
+ int i, j;
+ int saved_seconds;
+ long li;
+ time_t lo;
+ time_t hi;
+ long y;
+ struct timeval newt = { 0, 0 };
+ struct timeval t = { 0, 0 };
+ struct ast_tm yourtm, mytm;
+
+ *okayp = FALSE;
+ yourtm = *tmp;
+ if (do_norm_secs) {
+ if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+ SECSPERMIN))
+ return WRONG;
+ }
+ if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+ return WRONG;
+ y = yourtm.tm_year;
+ if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
+ return WRONG;
+ /*
+ ** Turn y into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (long_increment_overflow(&y, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (long_increment_overflow(&y, -1))
+ return WRONG;
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(li)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(li)];
+ if (long_increment_overflow(&y, 1))
+ return WRONG;
+ }
+ for ( ; ; ) {
+ i = mon_lengths[isleap(y)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (long_increment_overflow(&y, 1))
+ return WRONG;
+ }
+ }
+ if (long_increment_overflow(&y, -TM_YEAR_BASE))
+ return WRONG;
+ yourtm.tm_year = y;
+ if (yourtm.tm_year != y)
+ return WRONG;
+ if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+ saved_seconds = 0;
+ else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ /*
+ ** We can't set tm_sec to 0, because that might push the
+ ** time below the minimum representable time.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ return WRONG;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Do a binary search (this works whatever time_t's type is).
+ */
+ if (!TYPE_SIGNED(time_t)) {
+ lo = 0;
+ hi = lo - 1;
+ } else if (!TYPE_INTEGRAL(time_t)) {
+ if (sizeof(time_t) > sizeof(float))
+ hi = (time_t) DBL_MAX;
+ else hi = (time_t) FLT_MAX;
+ lo = -hi;
+ } else {
+ lo = 1;
+ for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
+ lo *= 2;
+ hi = -(lo + 1);
+ }
+ for ( ; ; ) {
+ t.tv_sec = lo / 2 + hi / 2;
+ if (t.tv_sec < lo)
+ t.tv_sec = lo;
+ else if (t.tv_sec > hi)
+ t.tv_sec = hi;
+ if ((*funcp)(&t, offset, &mytm, sp) == NULL) {
+ /*
+ ** Assume that t is too extreme to be represented in
+ ** a struct ast_tm; arrange things so that it is less
+ ** extreme on the next pass.
+ */
+ dir = (t.tv_sec > 0) ? 1 : -1;
+ } else dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (t.tv_sec == lo) {
+ ++t.tv_sec;
+ if (t.tv_sec <= lo)
+ return WRONG;
+ ++lo;
+ } else if (t.tv_sec == hi) {
+ --t.tv_sec;
+ if (t.tv_sec >= hi)
+ return WRONG;
+ --hi;
+ }
+ if (lo > hi)
+ return WRONG;
+ if (dir > 0)
+ hi = t.tv_sec;
+ else lo = t.tv_sec;
+ continue;
+ }
+ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+ break;
+ /*
+ ** Right time, wrong type.
+ ** Hunt for right time, right type.
+ ** It's okay to guess wrong since the guess
+ ** gets checked.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt.tv_sec = t.tv_sec + sp->ttis[j].tt_gmtoff -
+ sp->ttis[i].tt_gmtoff;
+ if ((*funcp)(&newt, offset, &mytm, sp) == NULL)
+ continue;
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ return WRONG;
+ }
+label:
+ newt.tv_sec = t.tv_sec + saved_seconds;
+ if ((newt.tv_sec < t.tv_sec) != (saved_seconds < 0))
+ return WRONG;
+ t.tv_sec = newt.tv_sec;
+ if ((*funcp)(&t, offset, tmp, sp))
+ *okayp = TRUE;
+ return t;
+}
+
+static struct timeval time2(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm*, const struct state *sp), const long offset, int *okayp, const struct state *sp)
+{
+ struct timeval t;
+
+ /*! \note
+ ** First try without normalization of seconds
+ ** (in case tm_sec contains a value associated with a leap second).
+ ** If that fails, try with normalization of seconds.
+ */
+ t = time2sub(tmp, funcp, offset, okayp, FALSE, sp);
+ return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp);
+}
+
+static struct timeval time1(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm *, const struct state *), const long offset, const struct state *sp)
+{
+ struct timeval t;
+ int samei, otheri;
+ int sameind, otherind;
+ int i;
+ int nseen;
+ int seen[TZ_MAX_TYPES];
+ int types[TZ_MAX_TYPES];
+ int okay;
+
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ t = time2(tmp, funcp, offset, &okay, sp);
+#ifdef PCTS
+ /*
+ ** PCTS code courtesy Grant Sullivan.
+ */
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+ if (okay || tmp->tm_isdst < 0)
+ return t;
+#endif /* !defined PCTS */
+ /*
+ ** We're supposed to assume that somebody took a time of one type
+ ** and did some math on it that yielded a "struct ast_tm" that's bad.
+ ** We try to divine the type they started from and adjust to the
+ ** type they need.
+ */
+ if (sp == NULL)
+ return WRONG;
+ for (i = 0; i < sp->typecnt; ++i)
+ seen[i] = FALSE;
+ nseen = 0;
+ for (i = sp->timecnt - 1; i >= 0; --i)
+ if (!seen[sp->types[i]]) {
+ seen[sp->types[i]] = TRUE;
+ types[nseen++] = sp->types[i];
+ }
+ for (sameind = 0; sameind < nseen; ++sameind) {
+ samei = types[sameind];
+ if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+ continue;
+ for (otherind = 0; otherind < nseen; ++otherind) {
+ otheri = types[otherind];
+ if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+ continue;
+ tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, offset, &okay, sp);
+ if (okay)
+ return t;
+ tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
+}
+
+struct timeval ast_mktime(struct ast_tm *tmp, const char *zone)
+{
+ const struct state *sp;
+ if (!(sp = ast_tzset(zone)))
+ return WRONG;
+ return time1(tmp, localsub, 0L, sp);
+}
+
+int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm)
+{
+ size_t fmtlen = strlen(tmp) + 1;
+ char *format = ast_calloc(1, fmtlen), *fptr = format, *newfmt;
+ int decimals = -1, i, res;
+ long fraction;
+
+ if (!format)
+ return -1;
+ for (; *tmp; tmp++) {
+ if (*tmp == '%') {
+ switch (tmp[1]) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ if (tmp[2] != 'q')
+ goto defcase;
+ decimals = tmp[1] - '0';
+ tmp++;
+ /* Fall through */
+ case 'q': /* Milliseconds */
+ if (decimals == -1)
+ decimals = 3;
+
+ /* Juggle some memory to fit the item */
+ newfmt = ast_realloc(format, fmtlen + decimals);
+ if (!newfmt) {
+ ast_free(format);
+ return -1;
+ }
+ fptr = fptr - format + newfmt;
+ format = newfmt;
+ fmtlen += decimals;
+
+ /* Reduce the fraction of time to the accuracy needed */
+ for (i = 6, fraction = tm->tm_usec; i > decimals; i--)
+ fraction /= 10;
+ fptr += sprintf(fptr, "%0*ld", decimals, fraction);
+
+ /* Reset, in case more than one 'q' specifier exists */
+ decimals = -1;
+ tmp++;
+ break;
+ default:
+ goto defcase;
+ }
+ } else
+defcase: *fptr++ = *tmp;
+ }
+ *fptr = '\0';
+#undef strftime
+ res = (int)strftime(buf, len, format, (struct tm *)tm);
+ ast_free(format);
+ return res;
+}
+
diff --git a/trunk/main/stdtime/private.h b/trunk/main/stdtime/private.h
new file mode 100644
index 000000000..11793088d
--- /dev/null
+++ b/trunk/main/stdtime/private.h
@@ -0,0 +1,358 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char __attribute__((unused)) privatehid[] = "@(#)private.h 8.3";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#define GRANDPARENTED "Local time zone must be set--see zic manual page"
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_ADJTIME
+#define HAVE_ADJTIME 1
+#endif /* !defined HAVE_ADJTIME */
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+#define HAVE_INCOMPATIBLE_CTIME_R 0
+#endif /* !defined INCOMPATIBLE_CTIME_R */
+
+#ifndef HAVE_SETTIMEOFDAY
+#define HAVE_SETTIMEOFDAY 3
+#endif /* !defined HAVE_SETTIMEOFDAY */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR 1
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_SYS_STAT_H
+#define HAVE_SYS_STAT_H 1
+#endif /* !defined HAVE_SYS_STAT_H */
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H 1
+#endif /* !defined HAVE_SYS_WAIT_H */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif /* !defined HAVE_UNISTD_H */
+
+#ifndef HAVE_UTMPX_H
+#define HAVE_UTMPX_H 0
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME "/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#define asctime_r _incompatible_asctime_r
+#define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h" /* for CHAR_BIT et al. */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT
+#include "libintl.h"
+#endif /* HAVE_GETTEXT */
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
+#endif /* HAVE_SYS_WAIT_H */
+
+#ifndef WIFEXITED
+#define WIFEXITED(status) (((status) & 0xff) == 0)
+#endif /* !defined WIFEXITED */
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
+#endif /* !defined WEXITSTATUS */
+
+#if HAVE_UNISTD_H
+#include "unistd.h" /* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H */
+
+#if !HAVE_UNISTD_H
+#ifndef F_OK
+#define F_OK 0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+#endif /* !HAVE_UNISTD_H */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__'s value depends on previously-included
+** files.
+** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
+*/
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+ (199901 <= __STDC_VERSION__ || \
+ 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include "stdint.h"
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
+#if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long int_fast64_t;
+#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#if (LONG_MAX >> 31) < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#endif /* (LONG_MAX >> 31) < 0xffffffff */
+typedef long int_fast64_t;
+#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+/*
+** If your compiler lacks prototypes, "#define P(x) ()".
+*/
+
+#ifndef P
+#define P(x) x
+#endif /* !defined P */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_SUCCESS.
+*/
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_FAILURE.
+*/
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+/*
+** SunOS 4.1.1 headers lack FILENAME_MAX.
+*/
+
+#ifndef FILENAME_MAX
+
+#ifndef MAXPATHLEN
+#ifdef unix
+#include "sys/param.h"
+#endif /* defined unix */
+#endif /* !defined MAXPATHLEN */
+
+#ifdef MAXPATHLEN
+#define FILENAME_MAX MAXPATHLEN
+#endif /* defined MAXPATHLEN */
+#ifndef MAXPATHLEN
+#define FILENAME_MAX 1024 /* Pure guesswork */
+#endif /* !defined MAXPATHLEN */
+
+#endif /* !defined FILENAME_MAX */
+
+/*
+** SunOS 4.1.1 libraries lack remove.
+*/
+
+#ifndef remove
+extern int unlink P((const char * filename));
+#define remove unlink
+#endif /* !defined remove */
+
+/*
+** Some ancient errno.h implementations don't declare errno.
+** But some newer errno.h implementations define it as a macro.
+** Fix the former without affecting the latter.
+*/
+
+#ifndef errno
+extern int errno;
+#endif /* !defined errno */
+
+/*
+** Private function declarations.
+*/
+
+char * icalloc P((int nelem, int elsize));
+char * icatalloc P((char * old, const char * new));
+char * icpyalloc P((const char * string));
+char * imalloc P((int n));
+void * irealloc P((void * pointer, int size));
+void icfree P((char * pointer));
+void ifree P((char * pointer));
+const char * scheck P((const char * string, const char * format));
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+/*
+** Since the definition of TYPE_INTEGRAL contains floating point numbers,
+** it cannot be used in preprocessor directives.
+*/
+
+#ifndef TYPE_INTEGRAL
+#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
+#endif /* !defined TYPE_INTEGRAL */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+ 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#endif /* !HAVE_GETTEXT */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r P((struct tm const *, char *));
+char *ctime_r P((time_t const *, char *));
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+#ifndef YEARSPERREPEAT
+#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+#endif /* !defined YEARSPERREPEAT */
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR 31556952L
+#endif /* !defined AVGSECSPERYEAR */
+
+#ifndef SECSPERREPEAT
+#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#endif /* !defined SECSPERREPEAT */
+
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined SECSPERREPEAT_BITS */
+
+/*
+** UNIX was a registered trademark of The Open Group in 2003.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/trunk/main/stdtime/test.c b/trunk/main/stdtime/test.c
new file mode 100644
index 000000000..9e8ce45da
--- /dev/null
+++ b/trunk/main/stdtime/test.c
@@ -0,0 +1,21 @@
+/*! \file
+ \brief Testing localtime functionality */
+
+#include "localtime.c"
+#include <sys/time.h>
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ struct timeval tv;
+ struct tm tm;
+ char *zone[4] = { "America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles" };
+ int i;
+
+ gettimeofday(&tv,NULL);
+
+ for (i=0;i<4;i++) {
+ ast_localtime(&tv.tv_sec,&tm,zone[i]);
+ printf("Localtime at %s is %04d/%02d/%02d %02d:%02d:%02d\n",zone[i],tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+ return 0;
+}
diff --git a/trunk/main/stdtime/tzfile.h b/trunk/main/stdtime/tzfile.h
new file mode 100644
index 000000000..9201b967f
--- /dev/null
+++ b/trunk/main/stdtime/tzfile.h
@@ -0,0 +1,184 @@
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char __attribute__((unused)) tzfilehid[] = "@(#)tzfile.h 8.1";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#ifdef SOLARIS
+#define TZDIR "/usr/share/lib/zoneinfo"
+#else
+#define TZDIR "/usr/share/zoneinfo"
+#endif /* defined SOLARIS */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' as of 2005 */
+ char tzh_reserved[15]; /* reserved--must be zero */
+ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded UTC offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
+** time is standard time, if FALSE,
+** transition time is wall clock time
+** if absent, transition times are
+** assumed to be wall clock time
+** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
+** time is UTC, if FALSE,
+** transition time is local time
+** if absent, transition times are
+** assumed to be local time
+*/
+
+/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+#define TZ_MAX_TIMES 1200
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+/*
+** Must be at least 14 for Europe/Riga as of Jan 12 1995,
+** as noted by Earl Chew.
+*/
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined TZFILE_H */
diff --git a/trunk/main/strcompat.c b/trunk/main/strcompat.c
new file mode 100644
index 000000000..37ee407cd
--- /dev/null
+++ b/trunk/main/strcompat.c
@@ -0,0 +1,463 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Compatibility functions for strsep and strtoq missing on Solaris
+ */
+
+#include "asterisk.h"
+
+#include <ctype.h>
+
+#ifndef HAVE_STRSEP
+char *strsep(char **str, const char *delims)
+{
+ char *token;
+
+ if (!*str) {
+ /* No more tokens */
+ return NULL;
+ }
+
+ token = *str;
+ while (**str != '\0') {
+ if (strchr(delims, **str)) {
+ **str = '\0';
+ (*str)++;
+ return token;
+ }
+ (*str)++;
+ }
+
+ /* There is no other token */
+ *str = NULL;
+
+ return token;
+}
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite)
+{
+ unsigned char *buf;
+ int buflen;
+
+ buflen = strlen(name) + strlen(value) + 2;
+ buf = alloca(buflen);
+
+ if (!overwrite && getenv(name))
+ return 0;
+
+ snprintf(buf, buflen, "%s=%s", name, value);
+
+ return putenv(buf);
+}
+#endif
+
+#ifndef HAVE_UNSETENV
+int unsetenv(const char *name)
+{
+ return setenv(name, "", 0);
+}
+#endif
+
+#ifndef HAVE_STRCASESTR
+static char *upper(const char *orig, char *buf, int bufsize)
+{
+ int i = 0;
+
+ while (i < (bufsize - 1) && orig[i]) {
+ buf[i] = toupper(orig[i]);
+ i++;
+ }
+
+ buf[i] = '\0';
+
+ return buf;
+}
+
+char *strcasestr(const char *haystack, const char *needle)
+{
+ char *u1, *u2;
+ int u1len = strlen(haystack) + 1, u2len = strlen(needle) + 1;
+
+ u1 = alloca(u1len);
+ u2 = alloca(u2len);
+ if (u1 && u2) {
+ char *offset;
+ if (u2len > u1len) {
+ /* Needle bigger than haystack */
+ return NULL;
+ }
+ offset = strstr(upper(haystack, u1, u1len), upper(needle, u2, u2len));
+ if (offset) {
+ /* Return the offset into the original string */
+ return ((char *)((unsigned long)haystack + (unsigned long)(offset - u1)));
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+#endif /* !HAVE_STRCASESTR */
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t n)
+{
+ size_t len;
+
+ for (len = 0; len < n; len++)
+ if (s[len] == '\0')
+ break;
+
+ return len;
+}
+#endif /* !HAVE_STRNLEN */
+
+#if !defined(HAVE_STRNDUP) && !defined(__AST_DEBUG_MALLOC)
+char *strndup(const char *s, size_t n)
+{
+ size_t len = strnlen(s, n);
+ char *new = malloc(len + 1);
+
+ if (!new)
+ return NULL;
+
+ new[len] = '\0';
+ return memcpy(new, s, len);
+}
+#endif /* !defined(HAVE_STRNDUP) && !defined(__AST_DEBUG_MALLOC) */
+
+#if !defined(HAVE_VASPRINTF) && !defined(__AST_DEBUG_MALLOC)
+int vasprintf(char **strp, const char *fmt, va_list ap)
+{
+ int size;
+ va_list ap2;
+ char s;
+
+ *strp = NULL;
+ va_copy(ap2, ap);
+ size = vsnprintf(&s, 1, fmt, ap2);
+ va_end(ap2);
+ *strp = malloc(size + 1);
+ if (!*strp)
+ return -1;
+ vsnprintf(*strp, size + 1, fmt, ap);
+
+ return size;
+}
+#endif /* !defined(HAVE_VASPRINTF) && !defined(__AST_DEBUG_MALLOC) */
+
+/*
+ * Based on Code from bsd-asprintf from OpenSSH
+ * Copyright (c) 2004 Darren Tucker.
+ *
+ * Based originally on asprintf.c from OpenBSD:
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, 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.
+ */
+#if !defined(HAVE_ASPRINTF) && !defined(__AST_DEBUG_MALLOC)
+int asprintf(char **str, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ *str = NULL;
+ va_start(ap, fmt);
+ ret = vasprintf(str, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif /* !defined(HAVE_ASPRINTF) && !defined(__AST_DEBUG_MALLOC) */
+
+#ifndef HAVE_STRTOQ
+#ifndef LONG_MIN
+#define LONG_MIN (-9223372036854775807L-1L)
+ /* min value of a "long int" */
+#endif
+#ifndef LONG_MAX
+#define LONG_MAX 9223372036854775807L
+ /* max value of a "long int" */
+#endif
+
+/*! \brief
+ * Convert a string to a quad integer.
+ *
+ * \note Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+uint64_t strtoq(const char *nptr, char **endptr, int base)
+{
+ const char *s;
+ uint64_t acc;
+ unsigned char c;
+ uint64_t qbase, cutoff;
+ int neg, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ s = nptr;
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '\0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '\0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for quads is
+ * [-9223372036854775808..9223372036854775807] and the input base
+ * is 10, cutoff will be set to 922337203685477580 and cutlim to
+ * either 7 (neg==0) or 8 (neg==1), meaning that if we have
+ * accumulated a value > 922337203685477580, or equal but the
+ * next digit is > 7 (or 8), the number is too big, and we will
+ * return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ qbase = (unsigned)base;
+ cutoff = neg ? (uint64_t)-(LONG_MIN + LONG_MAX) + LONG_MAX : LONG_MAX;
+ cutlim = cutoff % qbase;
+ cutoff /= qbase;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (!isascii(c))
+ break;
+ if (isdigit(c))
+ c -= '\0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= qbase;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *((const char **)endptr) = any ? s - 1 : nptr;
+ return acc;
+}
+#endif /* !HAVE_STRTOQ */
+
+#ifndef HAVE_GETLOADAVG
+#ifdef linux
+/*! \brief Alternative method of getting load avg on Linux only */
+int getloadavg(double *list, int nelem)
+{
+ FILE *LOADAVG;
+ double avg[3] = { 0.0, 0.0, 0.0 };
+ int i, res = -1;
+
+ if ((LOADAVG = fopen("/proc/loadavg", "r"))) {
+ fscanf(LOADAVG, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]);
+ res = 0;
+ fclose(LOADAVG);
+ }
+
+ for (i = 0; (i < nelem) && (i < 3); i++) {
+ list[i] = avg[i];
+ }
+
+ return res;
+}
+#else /* !linux */
+/*! \brief Return something that won't cancel the call, but still return -1, in case
+ * we correct the implementation to check return value */
+int getloadavg(double *list, int nelem)
+{
+ int i;
+
+ for (i = 0; i < nelem; i++) {
+ list[i] = 0.1;
+ }
+ return -1;
+}
+#endif /* linux */
+#endif /* !HAVE_GETLOADAVG */
+
+
+/*
+ * For strlcat()
+ *
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return dlen + strlen(s);
+
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return dlen + (s - src); /* count does not include NUL */
+}
+#endif /* HAVE_STRLCAT */
+
+/*
+ * For strlcpy()
+ *
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return s - src - 1; /* count does not include NUL */
+}
+#endif /* HAVE_STRLCPY */
diff --git a/trunk/main/tcptls.c b/trunk/main/tcptls.c
new file mode 100644
index 000000000..27faa0c18
--- /dev/null
+++ b/trunk/main/tcptls.c
@@ -0,0 +1,452 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 - 2008, Digium, Inc.
+ *
+ * Luigi Rizzo (TCP and TLS server code)
+ * Brett Bryant <brettbryant@gmail.com> (updated for client support)
+ *
+ * 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 Code to support TCP and TLS server/client
+ *
+ * \author Luigi Rizzo
+ * \author Brett Bryant <brettbryant@gmail.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <sys/signal.h>
+
+#include "asterisk/compat.h"
+#include "asterisk/tcptls.h"
+#include "asterisk/http.h"
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/options.h"
+#include "asterisk/manager.h"
+
+/*!
+ * replacement read/write functions for SSL support.
+ * We use wrappers rather than SSL_read/SSL_write directly so
+ * we can put in some debugging.
+ */
+
+#ifdef DO_SSL
+static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
+{
+ int i = SSL_read(cookie, buf, len-1);
+#if 0
+ if (i >= 0)
+ buf[i] = '\0';
+ ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
+#endif
+ return i;
+}
+
+static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
+{
+#if 0
+ char *s = alloca(len+1);
+ strncpy(s, buf, len);
+ s[len] = '\0';
+ ast_verbose("ssl write size %d <%s>\n", (int)len, s);
+#endif
+ return SSL_write(cookie, buf, len);
+}
+
+static int ssl_close(void *cookie)
+{
+ close(SSL_get_fd(cookie));
+ SSL_shutdown(cookie);
+ SSL_free(cookie);
+ return 0;
+}
+#endif /* DO_SSL */
+
+HOOK_T server_read(struct server_instance *ser, void *buf, size_t count)
+{
+ if (!ser->ssl)
+ return read(ser->fd, buf, count);
+#ifdef DO_SSL
+ else
+ return ssl_read(ser->ssl, buf, count);
+#endif
+}
+
+HOOK_T server_write(struct server_instance *ser, void *buf, size_t count)
+{
+ if (!ser->ssl)
+ return write(ser->fd, buf, count);
+#ifdef DO_SSL
+ else
+ return ssl_write(ser->ssl, buf, count);
+#endif
+}
+
+void *server_root(void *data)
+{
+ struct server_args *desc = data;
+ int fd;
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+ struct server_instance *ser;
+ pthread_t launched;
+
+ for (;;) {
+ int i, flags;
+
+ if (desc->periodic_fn)
+ desc->periodic_fn(desc);
+ i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
+ if (i <= 0)
+ continue;
+ sinlen = sizeof(sin);
+ fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
+ if (fd < 0) {
+ if ((errno != EAGAIN) && (errno != EINTR))
+ ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
+ continue;
+ }
+ ser = ast_calloc(1, sizeof(*ser));
+ if (!ser) {
+ ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
+ close(fd);
+ continue;
+ }
+ flags = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ ser->fd = fd;
+ ser->parent = desc;
+ memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
+
+ ser->client = 0;
+
+ if (ast_pthread_create_detached_background(&launched, NULL, ast_make_file_from_fd, ser)) {
+ ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
+ close(ser->fd);
+ ast_free(ser);
+ }
+ }
+ return NULL;
+}
+
+static int __ssl_setup(struct ast_tls_config *cfg, int client)
+{
+#ifndef DO_SSL
+ cfg->enabled = 0;
+ return 0;
+#else
+ if (!cfg->enabled)
+ return 0;
+
+ SSL_load_error_strings();
+ SSLeay_add_ssl_algorithms();
+
+ if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) {
+ ast_log(LOG_DEBUG, "Sorry, SSL_CTX_new call returned null...\n");
+ cfg->enabled = 0;
+ return 0;
+ }
+ if (!ast_strlen_zero(cfg->certfile)) {
+ if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
+ SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
+ SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) {
+ if (!client) {
+ /* Clients don't need a certificate, but if its setup we can use it */
+ ast_verbose("ssl cert error <%s>", cfg->certfile);
+ sleep(2);
+ cfg->enabled = 0;
+ return 0;
+ }
+ }
+ }
+ if (!ast_strlen_zero(cfg->cipher)) {
+ if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
+ if (!client) {
+ ast_verbose("ssl cipher error <%s>", cfg->cipher);
+ sleep(2);
+ cfg->enabled = 0;
+ return 0;
+ }
+ }
+ }
+ if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) {
+ if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0)
+ ast_verbose("ssl CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath);
+ }
+
+ ast_verbose("ssl cert ok\n");
+ return 1;
+#endif
+}
+
+int ssl_setup(struct ast_tls_config *cfg)
+{
+ return __ssl_setup(cfg, 0);
+}
+
+/*! A generic client routine for a TCP client
+ * and starts a thread for handling accept()
+ */
+struct server_instance *client_start(struct server_args *desc)
+{
+ int flags;
+ struct server_instance *ser = NULL;
+
+ /* Do nothing if nothing has changed */
+ if(!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
+ return NULL;
+ }
+
+ desc->oldsin = desc->sin;
+
+ if (desc->accept_fd != -1)
+ close(desc->accept_fd);
+
+ desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (desc->accept_fd < 0) {
+ ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
+ desc->name, strerror(errno));
+ return NULL;
+ }
+
+ if (connect(desc->accept_fd, (const struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
+ ast_log(LOG_NOTICE, "Unable to connect %s to %s:%d: %s\n",
+ desc->name,
+ ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
+ strerror(errno));
+ goto error;
+ }
+
+ if (!(ser = ast_calloc(1, sizeof(*ser))))
+ goto error;
+
+ flags = fcntl(desc->accept_fd, F_GETFL);
+ fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
+
+ ser->fd = desc->accept_fd;
+ ser->parent = desc;
+ ser->parent->worker_fn = NULL;
+ memcpy(&ser->requestor, &desc->sin, sizeof(ser->requestor));
+
+ ser->client = 1;
+
+ if (desc->tls_cfg) {
+ desc->tls_cfg->enabled = 1;
+ __ssl_setup(desc->tls_cfg, 1);
+ }
+
+ if (!ast_make_file_from_fd(ser))
+ goto error;
+
+ return ser;
+
+error:
+ close(desc->accept_fd);
+ desc->accept_fd = -1;
+ if (ser)
+ ast_free(ser);
+ return NULL;
+}
+
+/*!
+ * This is a generic (re)start routine for a TCP server,
+ * which does the socket/bind/listen and starts a thread for handling
+ * accept().
+ */
+
+void server_start(struct server_args *desc)
+{
+ int flags;
+ int x = 1;
+
+ /* Do nothing if nothing has changed */
+ if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
+ return;
+ }
+
+ desc->oldsin = desc->sin;
+
+ /* Shutdown a running server if there is one */
+ if (desc->master != AST_PTHREADT_NULL) {
+ pthread_cancel(desc->master);
+ pthread_kill(desc->master, SIGURG);
+ pthread_join(desc->master, NULL);
+ }
+
+ if (desc->accept_fd != -1)
+ close(desc->accept_fd);
+
+ /* If there's no new server, stop here */
+ if (desc->sin.sin_family == 0)
+ return;
+
+ desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (desc->accept_fd < 0) {
+ ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
+ desc->name, strerror(errno));
+ return;
+ }
+
+ setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
+ if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
+ ast_log(LOG_NOTICE, "Unable to bind %s to %s:%d: %s\n",
+ desc->name,
+ ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
+ strerror(errno));
+ goto error;
+ }
+ if (listen(desc->accept_fd, 10)) {
+ ast_log(LOG_NOTICE, "Unable to listen for %s!\n", desc->name);
+ goto error;
+ }
+ flags = fcntl(desc->accept_fd, F_GETFL);
+ fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
+ if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
+ ast_log(LOG_NOTICE, "Unable to launch %s on %s:%d: %s\n",
+ desc->name,
+ ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
+ strerror(errno));
+ goto error;
+ }
+ return;
+
+error:
+ close(desc->accept_fd);
+ desc->accept_fd = -1;
+}
+
+void server_stop(struct server_args *desc)
+{
+ /* Shutdown a running server if there is one */
+ if (desc->master != AST_PTHREADT_NULL) {
+ pthread_cancel(desc->master);
+ pthread_kill(desc->master, SIGURG);
+ pthread_join(desc->master, NULL);
+ }
+ if (desc->accept_fd != -1)
+ close(desc->accept_fd);
+ desc->accept_fd = -1;
+}
+
+/*!
+* creates a FILE * from the fd passed by the accept thread.
+* This operation is potentially expensive (certificate verification),
+* so we do it in the child thread context.
+*/
+void *ast_make_file_from_fd(void *data)
+{
+ struct server_instance *ser = data;
+ int (*ssl_setup)(SSL *) = (ser->client) ? SSL_connect : SSL_accept;
+ int ret;
+ char err[256];
+
+ /*
+ * open a FILE * as appropriate.
+ */
+ if (!ser->parent->tls_cfg)
+ ser->f = fdopen(ser->fd, "w+");
+#ifdef DO_SSL
+ else if ( (ser->ssl = SSL_new(ser->parent->tls_cfg->ssl_ctx)) ) {
+ SSL_set_fd(ser->ssl, ser->fd);
+ if ((ret = ssl_setup(ser->ssl)) <= 0) {
+ if(option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err));
+ } else {
+#if defined(HAVE_FUNOPEN) /* the BSD interface */
+ ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
+
+#elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
+ static const cookie_io_functions_t cookie_funcs = {
+ ssl_read, ssl_write, NULL, ssl_close
+ };
+ ser->f = fopencookie(ser->ssl, "w+", cookie_funcs);
+#else
+ /* could add other methods here */
+ ast_log(LOG_WARNING, "no ser->f methods attempted!");
+#endif
+ if ((ser->client && !ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER))
+ || (!ser->client && ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) {
+ X509 *peer;
+ long res;
+ peer = SSL_get_peer_certificate(ser->ssl);
+ if (!peer)
+ ast_log(LOG_WARNING, "No peer certificate\n");
+ res = SSL_get_verify_result(ser->ssl);
+ if (res != X509_V_OK)
+ ast_log(LOG_WARNING, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res));
+ if (!ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
+ ASN1_STRING *str;
+ unsigned char *str2;
+ X509_NAME *name = X509_get_subject_name(peer);
+ int pos = -1;
+ int found = 0;
+
+ for (;;) {
+ /* Walk the certificate to check all available "Common Name" */
+ /* XXX Probably should do a gethostbyname on the hostname and compare that as well */
+ pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos);
+ if (pos < 0)
+ break;
+ str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
+ ASN1_STRING_to_UTF8(&str2, str);
+ if (str2) {
+ if (!strcasecmp(ser->parent->hostname, (char *) str2))
+ found = 1;
+ ast_log(LOG_DEBUG, "SSL Common Name compare s1='%s' s2='%s'\n", ser->parent->hostname, str2);
+ OPENSSL_free(str2);
+ }
+ if (found)
+ break;
+ }
+ if (!found) {
+ ast_log(LOG_WARNING, "Certificate common name did not match (%s)\n", ser->parent->hostname);
+ if (peer)
+ X509_free(peer);
+ fclose(ser->f);
+ return NULL;
+ }
+ }
+ if (peer)
+ X509_free(peer);
+ }
+ }
+ if (!ser->f) /* no success opening descriptor stacking */
+ SSL_free(ser->ssl);
+ }
+#endif /* DO_SSL */
+
+ if (!ser->f) {
+ close(ser->fd);
+ ast_log(LOG_WARNING, "FILE * open failed!\n");
+ ast_free(ser);
+ return NULL;
+ }
+
+ if (ser && ser->parent->worker_fn)
+ return ser->parent->worker_fn(ser);
+ else
+ return ser;
+}
diff --git a/trunk/main/tdd.c b/trunk/main/tdd.c
new file mode 100644
index 000000000..13dda7e11
--- /dev/null
+++ b/trunk/main/tdd.c
@@ -0,0 +1,338 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ * 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 TTY/TDD Generation support
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note Includes code and algorithms from the Zapata library.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "asterisk/ulaw.h"
+#include "asterisk/tdd.h"
+#include "asterisk/fskmodem.h"
+#include "ecdisa.h"
+
+struct tdd_state {
+ fsk_data fskd;
+ char rawdata[256];
+ short oldstuff[4096];
+ int oldlen;
+ int pos;
+ int modo;
+ int mode;
+ int charnum;
+};
+
+static float dr[4], di[4];
+static float tddsb = 176.0; /* 45.5 baud */
+
+#define TDD_SPACE 1800.0 /* 1800 hz for "0" */
+#define TDD_MARK 1400.0 /* 1400 hz for "1" */
+
+static int tdd_decode_baudot(struct tdd_state *tdd,unsigned char data) /* covert baudot into ASCII */
+{
+ static char ltrs[32] = { '<','E','\n','A',' ','S','I','U',
+ '\n','D','R','J','N','F','C','K',
+ 'T','Z','L','W','H','Y','P','Q',
+ 'O','B','G','^','M','X','V','^' };
+ static char figs[32] = { '<','3','\n','-',' ','\'','8','7',
+ '\n','$','4','\'',',','!',':','(',
+ '5','\"',')','2','=','6','0','1',
+ '9','?','+','^','.','/',';','^' };
+ int d = 0; /* return 0 if not decodeable */
+ switch (data) {
+ case 0x1f:
+ tdd->modo = 0;
+ break;
+ case 0x1b:
+ tdd->modo = 1;
+ break;
+ default:
+ if (tdd->modo == 0)
+ d = ltrs[data];
+ else
+ d = figs[data];
+ break;
+ }
+ return d;
+}
+
+void tdd_init(void)
+{
+ /* Initialize stuff for inverse FFT */
+ dr[0] = cos(TDD_SPACE * 2.0 * M_PI / 8000.0);
+ di[0] = sin(TDD_SPACE * 2.0 * M_PI / 8000.0);
+ dr[1] = cos(TDD_MARK * 2.0 * M_PI / 8000.0);
+ di[1] = sin(TDD_MARK * 2.0 * M_PI / 8000.0);
+}
+
+struct tdd_state *tdd_new(void)
+{
+ struct tdd_state *tdd;
+ tdd = calloc(1, sizeof(*tdd));
+ if (tdd) {
+ tdd->fskd.ispb = 176; /* 45.5 baud */
+ /* Set up for 45.5 / 8000 freq *32 to allow ints */
+ tdd->fskd.pllispb = (int)((8000 * 32 * 2) / 90);
+ tdd->fskd.pllids = tdd->fskd.pllispb/32;
+ tdd->fskd.pllispb2 = tdd->fskd.pllispb/2;
+ tdd->fskd.hdlc = 0; /* Async */
+ tdd->fskd.nbit = 5; /* 5 bits */
+ tdd->fskd.instop = 1; /* integer rep of 1.5 stop bits */
+ tdd->fskd.parity = 0; /* No parity */
+ tdd->fskd.bw=0; /* Filter 75 Hz */
+ tdd->fskd.f_mark_idx = 0; /* 1400 Hz */
+ tdd->fskd.f_space_idx = 1; /* 1800 Hz */
+ tdd->fskd.xi0 = 0;
+ tdd->fskd.state = 0;
+ tdd->pos = 0;
+ tdd->mode = 0;
+ tdd->charnum = 0;
+ fskmodem_init(&tdd->fskd);
+ } else
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return tdd;
+}
+
+int ast_tdd_gen_ecdisa(unsigned char *outbuf, int len)
+{
+ int pos = 0;
+ int cnt;
+ while (len) {
+ cnt = len > sizeof(ecdisa) ? sizeof(ecdisa) : len;
+ memcpy(outbuf + pos, ecdisa, cnt);
+ pos += cnt;
+ len -= cnt;
+ }
+ return 0;
+}
+
+int tdd_feed(struct tdd_state *tdd, unsigned char *ubuf, int len)
+{
+ int mylen = len;
+ int olen;
+ int b = 'X';
+ int res;
+ int c,x;
+ short *buf = calloc(1, 2 * len + tdd->oldlen);
+ short *obuf = buf;
+ if (!buf) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return -1;
+ }
+ memcpy(buf, tdd->oldstuff, tdd->oldlen);
+ mylen += tdd->oldlen/2;
+ for (x = 0; x < len; x++)
+ buf[x + tdd->oldlen / 2] = AST_MULAW(ubuf[x]);
+ c = res = 0;
+ while (mylen >= 1320) { /* has to have enough to work on */
+ olen = mylen;
+ res = fsk_serial(&tdd->fskd, buf, &mylen, &b);
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "fsk_serial made mylen < 0 (%d) (olen was %d)\n", mylen, olen);
+ free(obuf);
+ return -1;
+ }
+ buf += (olen - mylen);
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serial failed\n");
+ free(obuf);
+ return -1;
+ }
+ if (res == 1) {
+ /* Ignore invalid bytes */
+ if (b > 0x7f)
+ continue;
+ c = tdd_decode_baudot(tdd,b);
+ if ((c < 1) || (c > 126))
+ continue; /* if not valid */
+ break;
+ }
+ }
+ if (mylen) {
+ memcpy(tdd->oldstuff, buf, mylen * 2);
+ tdd->oldlen = mylen * 2;
+ } else
+ tdd->oldlen = 0;
+ free(obuf);
+ if (res) {
+ tdd->mode = 2;
+/* put it in mode where it
+ reliably puts teleprinter in correct shift mode */
+ return(c);
+ }
+ return 0;
+}
+
+void tdd_free(struct tdd_state *tdd)
+{
+ free(tdd);
+}
+
+static inline float tdd_getcarrier(float *cr, float *ci, int bit)
+{
+ /* Move along. There's nothing to see here... */
+ float t;
+ t = *cr * dr[bit] - *ci * di[bit];
+ *ci = *cr * di[bit] + *ci * dr[bit];
+ *cr = t;
+
+ t = 2.0 - (*cr * *cr + *ci * *ci);
+ *cr *= t;
+ *ci *= t;
+ return *cr;
+}
+
+#define PUT_BYTE(a) do { \
+ *(buf++) = (a); \
+ bytes++; \
+} while(0)
+
+#define PUT_AUDIO_SAMPLE(y) do { \
+ int index = (short)(rint(8192.0 * (y))); \
+ *(buf++) = AST_LIN2MU(index); \
+ bytes++; \
+} while(0)
+
+#define PUT_TDD_MARKMS do { \
+ int x; \
+ for (x=0;x<8;x++) \
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, 1)); \
+} while(0)
+
+#define PUT_TDD_BAUD(bit) do { \
+ while (scont < tddsb) { \
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, bit)); \
+ scont += 1.0; \
+ } \
+ scont -= tddsb; \
+} while(0)
+
+#define PUT_TDD_STOP do { \
+ while (scont < (tddsb * 1.5)) { \
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, 1)); \
+ scont += 1.0; \
+ } \
+ scont -= (tddsb * 1.5); \
+} while(0)
+
+
+#define PUT_TDD(byte) do { \
+ int z; \
+ unsigned char b = (byte); \
+ PUT_TDD_BAUD(0); /* Start bit */ \
+ for (z = 0; z < 5; z++) { \
+ PUT_TDD_BAUD(b & 1); \
+ b >>= 1; \
+ } \
+ PUT_TDD_STOP; /* Stop bit */ \
+} while(0);
+
+/*! Generate TDD hold tone */
+int tdd_gen_holdtone(unsigned char *buf)
+{
+ int bytes=0;
+ float scont=0.0,cr=1.0,ci=0.0;
+ while(scont < tddsb*10.0) {
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, 1));
+ scont += 1.0;
+ }
+ return bytes;
+}
+
+int tdd_generate(struct tdd_state *tdd, unsigned char *buf, const char *str)
+{
+ int bytes=0;
+ int i,x;
+ char c;
+ /*! Baudot letters */
+ static unsigned char lstr[31] = "\000E\nA SIU\rDRJNFCKTZLWHYPQOBG\000MXV";
+ /*! Baudot figures */
+ static unsigned char fstr[31] = "\0003\n- \00787\r$4',!:(5\")2\0006019?+\000./;";
+ /* Initial carriers (real/imaginary) */
+ float cr = 1.0;
+ float ci = 0.0;
+ float scont = 0.0;
+
+ for(x = 0; str[x]; x++) {
+ /* Do synch for each 72th character */
+ if ( (tdd->charnum++) % 72 == 0)
+ PUT_TDD(tdd->mode ? 27 /* FIGS */ : 31 /* LTRS */);
+
+ c = toupper(str[x]);
+#if 0
+ printf("%c",c); fflush(stdout);
+#endif
+ if (c == 0) { /* send null */
+ PUT_TDD(0);
+ continue;
+ }
+ if (c == '\r') { /* send c/r */
+ PUT_TDD(8);
+ continue;
+ }
+ if (c == '\n') { /* send c/r and l/f */
+ PUT_TDD(8);
+ PUT_TDD(2);
+ continue;
+ }
+ if (c == ' ') { /* send space */
+ PUT_TDD(4);
+ continue;
+ }
+ for (i = 0; i < 31; i++) {
+ if (lstr[i] == c)
+ break;
+ }
+ if (i < 31) { /* if we found it */
+ if (tdd->mode) { /* if in figs mode, change it */
+ PUT_TDD(31); /* Send LTRS */
+ tdd->mode = 0;
+ }
+ PUT_TDD(i);
+ continue;
+ }
+ for (i = 0; i < 31; i++) {
+ if (fstr[i] == c)
+ break;
+ }
+ if (i < 31) { /* if we found it */
+ if (tdd->mode != 1) { /* if in ltrs mode, change it */
+ PUT_TDD(27); /* send FIGS */
+ tdd->mode = 1;
+ }
+ PUT_TDD(i); /* send byte */
+ continue;
+ }
+ }
+ return bytes;
+}
+
diff --git a/trunk/main/term.c b/trunk/main/term.c
new file mode 100644
index 000000000..ea005aa31
--- /dev/null
+++ b/trunk/main/term.c
@@ -0,0 +1,297 @@
+/*
+ * 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 Terminal Routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "asterisk/term.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+
+static int vt100compat;
+
+static char prepdata[80] = "";
+static char enddata[80] = "";
+static char quitdata[80] = "";
+
+static const char *termpath[] = {
+ "/usr/share/terminfo",
+ "/usr/local/share/misc/terminfo",
+ "/usr/lib/terminfo",
+ NULL
+ };
+
+/* Ripped off from Ross Ridge, but it's public domain code (libmytinfo) */
+static short convshort(char *s)
+{
+ register int a,b;
+
+ a = (int) s[0] & 0377;
+ b = (int) s[1] & 0377;
+
+ if (a == 0377 && b == 0377)
+ return -1;
+ if (a == 0376 && b == 0377)
+ return -2;
+
+ return a + b * 256;
+}
+
+int ast_term_init(void)
+{
+ char *term = getenv("TERM");
+ char termfile[256] = "";
+ char buffer[512] = "";
+ int termfd = -1, parseokay = 0, i;
+
+ if (!term)
+ return 0;
+ if (!ast_opt_console || ast_opt_no_color || !ast_opt_no_fork)
+ return 0;
+
+ for (i=0 ;; i++) {
+ if (termpath[i] == NULL) {
+ break;
+ }
+ snprintf(termfile, sizeof(termfile), "%s/%c/%s", termpath[i], *term, term);
+ termfd = open(termfile, O_RDONLY);
+ if (termfd > -1) {
+ break;
+ }
+ }
+ if (termfd > -1) {
+ int actsize = read(termfd, buffer, sizeof(buffer) - 1);
+ short sz_names = convshort(buffer + 2);
+ short sz_bools = convshort(buffer + 4);
+ short n_nums = convshort(buffer + 6);
+
+ /* if ((sz_names + sz_bools) & 1)
+ sz_bools++; */
+
+ if (sz_names + sz_bools + n_nums < actsize) {
+ /* Offset 13 is defined in /usr/include/term.h, though we do not
+ * include it here, as it conflicts with include/asterisk/term.h */
+ short max_colors = convshort(buffer + 12 + sz_names + sz_bools + 13 * 2);
+ if (max_colors > 0) {
+ vt100compat = 1;
+ }
+ parseokay = 1;
+ }
+ close(termfd);
+ }
+
+ if (!parseokay) {
+ /* These comparisons should not be substrings nor case-insensitive, as
+ * terminal types are very particular about how they treat suffixes and
+ * capitalization. For example, terminal type 'linux-m' does NOT
+ * support color, while 'linux' does. Not even all vt100* terminals
+ * support color, either (e.g. 'vt100+fnkeys'). */
+ if (!strcmp(term, "linux")) {
+ vt100compat = 1;
+ } else if (!strcmp(term, "xterm")) {
+ vt100compat = 1;
+ } else if (!strcmp(term, "xterm-color")) {
+ vt100compat = 1;
+ } else if (!strncmp(term, "Eterm", 5)) {
+ /* Both entries which start with Eterm support color */
+ vt100compat = 1;
+ } else if (!strcmp(term, "vt100")) {
+ vt100compat = 1;
+ } else if (!strncmp(term, "crt", 3)) {
+ /* Both crt terminals support color */
+ vt100compat = 1;
+ }
+ }
+
+ if (vt100compat) {
+ /* Make commands show up in nice colors */
+ snprintf(prepdata, sizeof(prepdata), "%c[%d;%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN, COLOR_BLACK + 10);
+ snprintf(enddata, sizeof(enddata), "%c[%d;%d;%dm", ESC, ATTR_RESET, COLOR_WHITE, COLOR_BLACK + 10);
+ snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
+ }
+ return 0;
+}
+
+char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
+{
+ int attr=0;
+ char tmp[40];
+ if (!vt100compat) {
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ if (!fgcolor && !bgcolor) {
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ if ((fgcolor & 128) && (bgcolor & 128)) {
+ /* Can't both be highlighted */
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ if (!bgcolor)
+ bgcolor = COLOR_BLACK;
+
+ if (bgcolor) {
+ bgcolor &= ~128;
+ bgcolor += 10;
+ }
+ if (fgcolor & 128) {
+ attr = ATTR_BRIGHT;
+ fgcolor &= ~128;
+ }
+ if (fgcolor && bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d;%d", fgcolor, bgcolor);
+ } else if (bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", bgcolor);
+ } else if (fgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", fgcolor);
+ }
+ if (attr) {
+ snprintf(outbuf, maxout, "%c[%d;%sm%s%c[0;%d;%dm", ESC, attr, tmp, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10);
+ } else {
+ snprintf(outbuf, maxout, "%c[%sm%s%c[0;%d;%dm", ESC, tmp, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10);
+ }
+ return outbuf;
+}
+
+char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout)
+{
+ int attr=0;
+ char tmp[40];
+ if ((!vt100compat) || (!fgcolor && !bgcolor)) {
+ *outbuf = '\0';
+ return outbuf;
+ }
+ if ((fgcolor & 128) && (bgcolor & 128)) {
+ /* Can't both be highlighted */
+ *outbuf = '\0';
+ return outbuf;
+ }
+ if (!bgcolor)
+ bgcolor = COLOR_BLACK;
+
+ if (bgcolor) {
+ bgcolor &= ~128;
+ bgcolor += 10;
+ }
+ if (fgcolor & 128) {
+ attr = ATTR_BRIGHT;
+ fgcolor &= ~128;
+ }
+ if (fgcolor && bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d;%d", fgcolor, bgcolor);
+ } else if (bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", bgcolor);
+ } else if (fgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", fgcolor);
+ }
+ if (attr) {
+ snprintf(outbuf, maxout, "%c[%d;%sm", ESC, attr, tmp);
+ } else {
+ snprintf(outbuf, maxout, "%c[%sm", ESC, tmp);
+ }
+ return outbuf;
+}
+
+char *term_strip(char *outbuf, char *inbuf, int maxout)
+{
+ char *outbuf_ptr = outbuf, *inbuf_ptr = inbuf;
+
+ while (outbuf_ptr < outbuf + maxout) {
+ switch (*inbuf_ptr) {
+ case ESC:
+ while (*inbuf_ptr && (*inbuf_ptr != 'm'))
+ inbuf_ptr++;
+ break;
+ default:
+ *outbuf_ptr = *inbuf_ptr;
+ outbuf_ptr++;
+ }
+ if (! *inbuf_ptr)
+ break;
+ inbuf_ptr++;
+ }
+ return outbuf;
+}
+
+char *term_prompt(char *outbuf, const char *inbuf, int maxout)
+{
+ if (!vt100compat) {
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ snprintf(outbuf, maxout, "%c[%d;%d;%dm%c%c[%d;%d;%dm%s",
+ ESC, ATTR_BRIGHT, COLOR_BLUE, COLOR_BLACK + 10,
+ inbuf[0],
+ ESC, 0, COLOR_WHITE, COLOR_BLACK + 10,
+ inbuf + 1);
+ return outbuf;
+}
+
+/* filter escape sequences */
+void term_filter_escapes(char *line)
+{
+ int i;
+ int len = strlen(line);
+
+ for (i = 0; i < len; i++) {
+ if (line[i] != ESC)
+ continue;
+ if ((i < (len - 2)) &&
+ (line[i + 1] == 0x5B)) {
+ switch (line[i + 2]) {
+ case 0x30:
+ case 0x31:
+ case 0x33:
+ continue;
+ }
+ }
+ /* replace ESC with a space */
+ line[i] = ' ';
+ }
+}
+
+char *term_prep(void)
+{
+ return prepdata;
+}
+
+char *term_end(void)
+{
+ return enddata;
+}
+
+char *term_quit(void)
+{
+ return quitdata;
+}
diff --git a/trunk/main/threadstorage.c b/trunk/main/threadstorage.c
new file mode 100644
index 000000000..ae1719ff8
--- /dev/null
+++ b/trunk/main/threadstorage.c
@@ -0,0 +1,239 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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 Debugging support for thread-local-storage objects
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk.h"
+#include "asterisk/_private.h"
+
+#if !defined(DEBUG_THREADLOCALS)
+
+void threadstorage_init(void)
+{
+}
+
+#else /* !defined(DEBUG_THREADLOCALS) */
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/cli.h"
+
+struct tls_object {
+ void *key;
+ size_t size;
+ const char *file;
+ const char *function;
+ unsigned int line;
+ pthread_t thread;
+ AST_LIST_ENTRY(tls_object) entry;
+};
+
+static AST_LIST_HEAD_NOLOCK_STATIC(tls_objects, tls_object);
+AST_MUTEX_DEFINE_STATIC_NOTRACKING(threadstoragelock);
+
+
+void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line)
+{
+ struct tls_object *to;
+
+ if (!(to = ast_calloc(1, sizeof(*to))))
+ return;
+
+ to->key = key;
+ to->size = len;
+ to->file = file;
+ to->function = function;
+ to->line = line;
+ to->thread = pthread_self();
+
+ ast_mutex_lock(&threadstoragelock);
+ AST_LIST_INSERT_TAIL(&tls_objects, to, entry);
+ ast_mutex_unlock(&threadstoragelock);
+}
+
+void __ast_threadstorage_object_remove(void *key)
+{
+ struct tls_object *to;
+
+ ast_mutex_lock(&threadstoragelock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
+ if (to->key == key) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ ast_mutex_unlock(&threadstoragelock);
+ if (to)
+ ast_free(to);
+}
+
+void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len)
+{
+ struct tls_object *to;
+
+ ast_mutex_lock(&threadstoragelock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
+ if (to->key == key_old) {
+ to->key = key_new;
+ to->size = len;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ ast_mutex_unlock(&threadstoragelock);
+}
+
+static char *handle_cli_threadstorage_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ size_t len = 0;
+ unsigned int count = 0;
+ struct tls_object *to;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "threadstorage show allocations";
+ e->usage =
+ "Usage: threadstorage show allocations [<file>]\n"
+ " Dumps a list of all thread-specific memory allocations,\n"
+ " optionally limited to those from a specific file\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 4)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&tls_objects, to, entry) {
+ if (fn && strcasecmp(to->file, fn))
+ continue;
+
+ ast_cli(a->fd, "%10d bytes allocated in %20s at line %5d of %25s (thread %p)\n",
+ (int) to->size, to->function, to->line, to->file, (void *) to->thread);
+ len += to->size;
+ count++;
+ }
+
+ ast_mutex_unlock(&threadstoragelock);
+
+ ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_threadstorage_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ size_t len = 0;
+ unsigned int count = 0;
+ struct tls_object *to;
+ struct file {
+ const char *name;
+ size_t len;
+ unsigned int count;
+ AST_LIST_ENTRY(file) entry;
+ } *file;
+ AST_LIST_HEAD_NOLOCK_STATIC(file_summary, file);
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "threadstorage show summary";
+ e->usage =
+ "Usage: threadstorage show summary [<file>]\n"
+ " Summarizes thread-specific memory allocations by file, or optionally\n"
+ " by function, if a file is specified\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 4)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&tls_objects, to, entry) {
+ if (fn && strcasecmp(to->file, fn))
+ continue;
+
+ AST_LIST_TRAVERSE(&file_summary, file, entry) {
+ if ((!fn && (file->name == to->file)) || (fn && (file->name == to->function)))
+ break;
+ }
+
+ if (!file) {
+ file = alloca(sizeof(*file));
+ memset(file, 0, sizeof(*file));
+ file->name = fn ? to->function : to->file;
+ AST_LIST_INSERT_TAIL(&file_summary, file, entry);
+ }
+
+ file->len += to->size;
+ file->count++;
+ }
+
+ ast_mutex_unlock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&file_summary, file, entry) {
+ len += file->len;
+ count += file->count;
+ if (fn) {
+ ast_cli(a->fd, "%10d bytes in %d allocation%ss in function %s\n",
+ (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
+ } else {
+ ast_cli(a->fd, "%10d bytes in %d allocation%s in file %s\n",
+ (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
+ }
+ }
+
+ ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli[] = {
+ AST_CLI_DEFINE(handle_cli_threadstorage_show_allocations, "Display outstanding thread local storage allocations"),
+ AST_CLI_DEFINE(handle_cli_threadstorage_show_summary, "Summarize outstanding memory allocations")
+};
+
+void threadstorage_init(void)
+{
+ ast_cli_register_multiple(cli, sizeof(cli) / sizeof(cli[0]));
+}
+
+#endif /* !defined(DEBUG_THREADLOCALS) */
+
diff --git a/trunk/main/translate.c b/trunk/main/translate.c
new file mode 100644
index 000000000..1af88ecfb
--- /dev/null
+++ b/trunk/main/translate.c
@@ -0,0 +1,904 @@
+/*
+ * 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 Translate via the use of pseudo channels
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+
+#define MAX_RECALC 1000 /* max sample recalc */
+
+/*! \brief the list of translators */
+static AST_RWLIST_HEAD_STATIC(translators, ast_translator);
+
+struct translator_path {
+ struct ast_translator *step; /*!< Next step translator */
+ unsigned int cost; /*!< Complete cost to destination */
+ unsigned int multistep; /*!< Multiple conversions required for this translation */
+};
+
+/*! \brief a matrix that, for any pair of supported formats,
+ * indicates the total cost of translation and the first step.
+ * The full path can be reconstricted iterating on the matrix
+ * until step->dstfmt == desired_format.
+ *
+ * Array indexes are 'src' and 'dest', in that order.
+ *
+ * Note: the lock in the 'translators' list is also used to protect
+ * this structure.
+ */
+static struct translator_path tr_matrix[MAX_FORMAT][MAX_FORMAT];
+
+/*! \todo
+ * TODO: sample frames for each supported input format.
+ * We build this on the fly, by taking an SLIN frame and using
+ * the existing converter to play with it.
+ */
+
+/*! \brief returns the index of the lowest bit set */
+static force_inline int powerof(unsigned int d)
+{
+ int x = ffs(d);
+
+ if (x)
+ return x - 1;
+
+ ast_log(LOG_WARNING, "No bits set? %d\n", d);
+
+ return -1;
+}
+
+/*
+ * wrappers around the translator routines.
+ */
+
+/*!
+ * \brief Allocate the descriptor, required outbuf space,
+ * and possibly also plc and desc.
+ */
+static void *newpvt(struct ast_translator *t)
+{
+ struct ast_trans_pvt *pvt;
+ int len;
+ int useplc = t->plc_samples > 0 && t->useplc; /* cache, because it can change on the fly */
+ char *ofs;
+
+ /*
+ * compute the required size adding private descriptor,
+ * plc, buffer, AST_FRIENDLY_OFFSET.
+ */
+ len = sizeof(*pvt) + t->desc_size;
+ if (useplc)
+ len += sizeof(plc_state_t);
+ if (t->buf_size)
+ len += AST_FRIENDLY_OFFSET + t->buf_size;
+ pvt = ast_calloc(1, len);
+ if (!pvt)
+ return NULL;
+ pvt->t = t;
+ ofs = (char *)(pvt + 1); /* pointer to data space */
+ if (t->desc_size) { /* first comes the descriptor */
+ pvt->pvt = ofs;
+ ofs += t->desc_size;
+ }
+ if (useplc) { /* then plc state */
+ pvt->plc = (plc_state_t *)ofs;
+ ofs += sizeof(plc_state_t);
+ }
+ if (t->buf_size) /* finally buffer and header */
+ pvt->outbuf = ofs + AST_FRIENDLY_OFFSET;
+ /* call local init routine, if present */
+ if (t->newpvt && t->newpvt(pvt)) {
+ ast_free(pvt);
+ return NULL;
+ }
+ ast_module_ref(t->module);
+ return pvt;
+}
+
+static void destroy(struct ast_trans_pvt *pvt)
+{
+ struct ast_translator *t = pvt->t;
+
+ if (ast_test_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR)) {
+ /* If this flag is still set, that means that the translation path has
+ * been torn down, while we still have a frame out there being used.
+ * When ast_frfree() gets called on that frame, this ast_trans_pvt
+ * will get destroyed, too. */
+
+ pvt->destroy = 1;
+
+ return;
+ }
+
+ if (t->destroy)
+ t->destroy(pvt);
+ ast_free(pvt);
+ ast_module_unref(t->module);
+}
+
+/*! \brief framein wrapper, deals with plc and bound checks. */
+static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int16_t *dst = (int16_t *)pvt->outbuf;
+ int ret;
+ int samples = pvt->samples; /* initial value */
+
+ /* Copy the last in jb timing info to the pvt */
+ ast_copy_flags(&pvt->f, f, AST_FRFLAG_HAS_TIMING_INFO);
+ pvt->f.ts = f->ts;
+ pvt->f.len = f->len;
+ pvt->f.seqno = f->seqno;
+
+ if (f->samples == 0) {
+ ast_log(LOG_WARNING, "no samples for %s\n", pvt->t->name);
+ }
+ if (pvt->t->buffer_samples) { /* do not pass empty frames to callback */
+ if (f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
+ if (pvt->plc) {
+ int l = pvt->t->plc_samples;
+ if (pvt->samples + l > pvt->t->buffer_samples) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ l = plc_fillin(pvt->plc, dst + pvt->samples, l);
+ pvt->samples += l;
+ pvt->datalen = pvt->samples * 2; /* SLIN has 2bytes for 1sample */
+ }
+ /* We don't want generic PLC. If the codec has native PLC, then do that */
+ if (!pvt->t->native_plc)
+ return 0;
+ }
+ if (pvt->samples + f->samples > pvt->t->buffer_samples) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ }
+ /* we require a framein routine, wouldn't know how to do
+ * it otherwise.
+ */
+ ret = pvt->t->framein(pvt, f);
+ /* possibly store data for plc */
+ if (!ret && pvt->plc) {
+ int l = pvt->t->plc_samples;
+ if (pvt->samples < l)
+ l = pvt->samples;
+ plc_rx(pvt->plc, dst + pvt->samples - l, l);
+ }
+ /* diagnostic ... */
+ if (pvt->samples == samples)
+ ast_log(LOG_WARNING, "%s did not update samples %d\n",
+ pvt->t->name, pvt->samples);
+ return ret;
+}
+
+/*! \brief generic frameout routine.
+ * If samples and datalen are 0, take whatever is in pvt
+ * and reset them, otherwise take the values in the caller and
+ * leave alone the pvt values.
+ */
+struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt,
+ int datalen, int samples)
+{
+ struct ast_frame *f = &pvt->f;
+
+ if (samples)
+ f->samples = samples;
+ else {
+ if (pvt->samples == 0)
+ return NULL;
+ f->samples = pvt->samples;
+ pvt->samples = 0;
+ }
+ if (datalen)
+ f->datalen = datalen;
+ else {
+ f->datalen = pvt->datalen;
+ pvt->datalen = 0;
+ }
+
+ f->frametype = AST_FRAME_VOICE;
+ f->subclass = 1 << (pvt->t->dstfmt);
+ f->mallocd = 0;
+ f->offset = AST_FRIENDLY_OFFSET;
+ f->src = pvt->t->name;
+ f->data = pvt->outbuf;
+
+ ast_set_flag(f, AST_FRFLAG_FROM_TRANSLATOR);
+
+ return f;
+}
+
+static struct ast_frame *default_frameout(struct ast_trans_pvt *pvt)
+{
+ return ast_trans_frameout(pvt, 0, 0);
+}
+
+/* end of callback wrappers and helpers */
+
+void ast_translator_free_path(struct ast_trans_pvt *p)
+{
+ struct ast_trans_pvt *pn = p;
+ while ( (p = pn) ) {
+ pn = p->next;
+ destroy(p);
+ }
+}
+
+/*! \brief Build a chain of translators based upon the given source and dest formats */
+struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
+{
+ struct ast_trans_pvt *head = NULL, *tail = NULL;
+
+ source = powerof(source);
+ dest = powerof(dest);
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ while (source != dest) {
+ struct ast_trans_pvt *cur;
+ struct ast_translator *t = tr_matrix[source][dest].step;
+ if (!t) {
+ ast_log(LOG_WARNING, "No translator path from %s to %s\n",
+ ast_getformatname(source), ast_getformatname(dest));
+ AST_RWLIST_UNLOCK(&translators);
+ return NULL;
+ }
+ if (!(cur = newpvt(t))) {
+ ast_log(LOG_WARNING, "Failed to build translator step from %d to %d\n", source, dest);
+ if (head)
+ ast_translator_free_path(head);
+ AST_RWLIST_UNLOCK(&translators);
+ return NULL;
+ }
+ if (!head)
+ head = cur;
+ else
+ tail->next = cur;
+ tail = cur;
+ cur->nextin = cur->nextout = ast_tv(0, 0);
+ /* Keep going if this isn't the final destination */
+ source = cur->t->dstfmt;
+ }
+
+ AST_RWLIST_UNLOCK(&translators);
+ return head;
+}
+
+static inline int format_rate(int format)
+{
+ if (format == AST_FORMAT_G722 || format == AST_FORMAT_SLINEAR16)
+ return 16000;
+
+ return 8000;
+}
+
+/*! \brief do the actual translation */
+struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
+{
+ struct ast_trans_pvt *p = path;
+ struct ast_frame *out = f;
+ struct timeval delivery;
+ int has_timing_info;
+ long ts;
+ long len;
+ int seqno;
+
+ has_timing_info = ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO);
+ ts = f->ts;
+ len = f->len;
+ seqno = f->seqno;
+
+ /* XXX hmmm... check this below */
+ if (!ast_tvzero(f->delivery)) {
+ if (!ast_tvzero(path->nextin)) {
+ /* Make sure this is in line with what we were expecting */
+ if (!ast_tveq(path->nextin, f->delivery)) {
+ /* The time has changed between what we expected and this
+ most recent time on the new packet. If we have a
+ valid prediction adjust our output time appropriately */
+ if (!ast_tvzero(path->nextout)) {
+ path->nextout = ast_tvadd(path->nextout,
+ ast_tvsub(f->delivery, path->nextin));
+ }
+ path->nextin = f->delivery;
+ }
+ } else {
+ /* This is our first pass. Make sure the timing looks good */
+ path->nextin = f->delivery;
+ path->nextout = f->delivery;
+ }
+ /* Predict next incoming sample */
+ path->nextin = ast_tvadd(path->nextin, ast_samp2tv(f->samples, format_rate(f->subclass)));
+ }
+ delivery = f->delivery;
+ for ( ; out && p ; p = p->next) {
+ framein(p, out);
+ out = p->t->frameout(p);
+ }
+ if (consume)
+ ast_frfree(f);
+ if (out == NULL)
+ return NULL;
+ /* we have a frame, play with times */
+ if (!ast_tvzero(delivery)) {
+ /* Regenerate prediction after a discontinuity */
+ if (ast_tvzero(path->nextout))
+ path->nextout = ast_tvnow();
+
+ /* Use next predicted outgoing timestamp */
+ out->delivery = path->nextout;
+
+ /* Predict next outgoing timestamp from samples in this
+ frame. */
+ path->nextout = ast_tvadd(path->nextout, ast_samp2tv(out->samples, format_rate(out->subclass)));
+ } else {
+ out->delivery = ast_tv(0, 0);
+ ast_set2_flag(out, has_timing_info, AST_FRFLAG_HAS_TIMING_INFO);
+ if (has_timing_info) {
+ out->ts = ts;
+ out->len = len;
+ out->seqno = seqno;
+ }
+ }
+ /* Invalidate prediction if we're entering a silence period */
+ if (out->frametype == AST_FRAME_CNG)
+ path->nextout = ast_tv(0, 0);
+ return out;
+}
+
+/*! \brief compute the cost of a single translation step */
+static void calc_cost(struct ast_translator *t, int seconds)
+{
+ int num_samples = 0;
+ struct ast_trans_pvt *pvt;
+ struct rusage start;
+ struct rusage end;
+ int cost;
+ int out_rate = format_rate(t->dstfmt);
+
+ if (!seconds)
+ seconds = 1;
+
+ /* If they don't make samples, give them a terrible score */
+ if (!t->sample) {
+ ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name);
+ t->cost = 999999;
+ return;
+ }
+
+ pvt = newpvt(t);
+ if (!pvt) {
+ ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
+ t->cost = 999999;
+ return;
+ }
+
+ getrusage(RUSAGE_SELF, &start);
+
+ /* Call the encoder until we've processed the required number of samples */
+ while (num_samples < seconds * out_rate) {
+ struct ast_frame *f = t->sample();
+ if (!f) {
+ ast_log(LOG_WARNING, "Translator '%s' failed to produce a sample frame.\n", t->name);
+ destroy(pvt);
+ t->cost = 999999;
+ return;
+ }
+ framein(pvt, f);
+ ast_frfree(f);
+ while ((f = t->frameout(pvt))) {
+ num_samples += f->samples;
+ ast_frfree(f);
+ }
+ }
+
+ getrusage(RUSAGE_SELF, &end);
+
+ cost = ((end.ru_utime.tv_sec - start.ru_utime.tv_sec) * 1000000) + end.ru_utime.tv_usec - start.ru_utime.tv_usec;
+ cost += ((end.ru_stime.tv_sec - start.ru_stime.tv_sec) * 1000000) + end.ru_stime.tv_usec - start.ru_stime.tv_usec;
+
+ destroy(pvt);
+
+ t->cost = cost / seconds;
+
+ if (!t->cost)
+ t->cost = 1;
+}
+
+/*!
+ * \brief rebuild a translation matrix.
+ * \note This function expects the list of translators to be locked
+*/
+static void rebuild_matrix(int samples)
+{
+ struct ast_translator *t;
+ int x; /* source format index */
+ int y; /* intermediate format index */
+ int z; /* destination format index */
+
+ ast_debug(1, "Resetting translation matrix\n");
+
+ bzero(tr_matrix, sizeof(tr_matrix));
+
+ /* first, compute all direct costs */
+ AST_RWLIST_TRAVERSE(&translators, t, list) {
+ if (!t->active)
+ continue;
+
+ x = t->srcfmt;
+ z = t->dstfmt;
+
+ if (samples)
+ calc_cost(t, samples);
+
+ if (!tr_matrix[x][z].step || t->cost < tr_matrix[x][z].cost) {
+ tr_matrix[x][z].step = t;
+ tr_matrix[x][z].cost = t->cost;
+ }
+ }
+
+ /*
+ * For each triple x, y, z of distinct formats, check if there is
+ * a path from x to z through y which is cheaper than what is
+ * currently known, and in case, update the matrix.
+ * Repeat until the matrix is stable.
+ */
+ for (;;) {
+ int changed = 0;
+ for (x = 0; x < MAX_FORMAT; x++) { /* source format */
+ for (y=0; y < MAX_FORMAT; y++) { /* intermediate format */
+ if (x == y) /* skip ourselves */
+ continue;
+
+ for (z=0; z<MAX_FORMAT; z++) { /* dst format */
+ int newcost;
+
+ if (z == x || z == y) /* skip null conversions */
+ continue;
+ if (!tr_matrix[x][y].step) /* no path from x to y */
+ continue;
+ if (!tr_matrix[y][z].step) /* no path from y to z */
+ continue;
+ newcost = tr_matrix[x][y].cost + tr_matrix[y][z].cost;
+ if (tr_matrix[x][z].step && newcost >= tr_matrix[x][z].cost)
+ continue; /* x->y->z is more expensive than
+ * the existing path */
+ /* ok, we can get from x to z via y with a cost that
+ is the sum of the transition from x to y and
+ from y to z */
+
+ tr_matrix[x][z].step = tr_matrix[x][y].step;
+ tr_matrix[x][z].cost = newcost;
+ tr_matrix[x][z].multistep = 1;
+ ast_debug(3, "Discovered %d cost path from %s to %s, via %d\n", tr_matrix[x][z].cost, ast_getformatname(x), ast_getformatname(z), y);
+ changed++;
+ }
+ }
+ }
+ if (!changed)
+ break;
+ }
+}
+
+static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define SHOW_TRANS 16
+ int x, y, z;
+ int curlen = 0, longest = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show translation [recalc]";
+ e->usage =
+ "Usage: core show translation [recalc [<recalc seconds>]]\n"
+ " Displays known codec translators and the cost associated\n"
+ " with each conversion. If the argument 'recalc' is supplied along\n"
+ " with optional number of seconds to test a new test will be performed\n"
+ " as the chart is being displayed.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 5)
+ return CLI_SHOWUSAGE;
+
+ if (a->argv[3] && !strcasecmp(a->argv[3], "recalc")) {
+ z = a->argv[4] ? atoi(a->argv[4]) : 1;
+
+ if (z <= 0) {
+ ast_cli(a->fd, " Recalc must be greater than 0. Defaulting to 1.\n");
+ z = 1;
+ }
+
+ if (z > MAX_RECALC) {
+ ast_cli(a->fd, " Maximum limit of recalc exceeded by %d, truncating value to %d\n", z - MAX_RECALC, MAX_RECALC);
+ z = MAX_RECALC;
+ }
+ ast_cli(a->fd, " Recalculating Codec Translation (number of sample seconds: %d)\n\n", z);
+ AST_RWLIST_WRLOCK(&translators);
+ rebuild_matrix(z);
+ AST_RWLIST_UNLOCK(&translators);
+ } else if (a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n");
+ ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n");
+ /* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */
+ for (x = 0; x < SHOW_TRANS; x++) {
+ curlen = strlen(ast_getformatname(1 << (x)));
+ if (curlen > longest)
+ longest = curlen;
+ }
+ for (x = -1; x < SHOW_TRANS; x++) {
+ struct ast_str *out = ast_str_alloca(120);
+ /*Go ahead and move to next iteration if dealing with an unknown codec*/
+ if(x >= 0 && !strcmp(ast_getformatname(1 << (x)), "unknown"))
+ continue;
+ ast_str_set(&out, -1, " ");
+ for (y = -1; y < SHOW_TRANS; y++) {
+ /*Go ahead and move to next iteration if dealing with an unknown codec*/
+ if (y >= 0 && !strcmp(ast_getformatname(1 << (y)), "unknown"))
+ continue;
+ if (y >= 0)
+ curlen = strlen(ast_getformatname(1 << (y)));
+ if (curlen < 5)
+ curlen = 5;
+ if (x >= 0 && y >= 0 && tr_matrix[x][y].step) {
+ /* XXX 99999 is a little hackish
+ We don't want this number being larger than the shortest (or current) codec
+ For now, that is "gsm" */
+ ast_str_append(&out, -1, "%*d", curlen + 1, tr_matrix[x][y].cost > 99999 ? 0 : tr_matrix[x][y].cost);
+ } else if (x == -1 && y >= 0) {
+ /* Top row - use a dynamic size */
+ ast_str_append(&out, -1, "%*s", curlen + 1, ast_getformatname(1 << (y)) );
+ } else if (y == -1 && x >= 0) {
+ /* Left column - use a static size. */
+ ast_str_append(&out, -1, "%*s", longest, ast_getformatname(1 << (x)) );
+ } else if (x >= 0 && y >= 0) {
+ ast_str_append(&out, -1, "%*s", curlen + 1, "-");
+ } else {
+ ast_str_append(&out, -1, "%*s", longest, "");
+ }
+ }
+ ast_str_append(&out, -1, "\n");
+ ast_cli(a->fd, out->str);
+ }
+ AST_RWLIST_UNLOCK(&translators);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_translate[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_translation, "Display translation matrix")
+};
+
+/*! \brief register codec translator */
+int __ast_register_translator(struct ast_translator *t, struct ast_module *mod)
+{
+ static int added_cli = 0;
+ struct ast_translator *u;
+ char tmp[80];
+
+ if (!mod) {
+ ast_log(LOG_WARNING, "Missing module pointer, you need to supply one\n");
+ return -1;
+ }
+
+ if (!t->buf_size) {
+ ast_log(LOG_WARNING, "empty buf size, you need to supply one\n");
+ return -1;
+ }
+
+ t->module = mod;
+
+ t->srcfmt = powerof(t->srcfmt);
+ t->dstfmt = powerof(t->dstfmt);
+ t->active = 1;
+
+ if (t->plc_samples) {
+ if (t->buffer_samples < t->plc_samples) {
+ ast_log(LOG_WARNING, "plc_samples %d buffer_samples %d\n",
+ t->plc_samples, t->buffer_samples);
+ return -1;
+ }
+ if (t->dstfmt != powerof(AST_FORMAT_SLINEAR))
+ ast_log(LOG_WARNING, "plc_samples %d format %x\n",
+ t->plc_samples, t->dstfmt);
+ }
+ if (t->srcfmt >= MAX_FORMAT) {
+ ast_log(LOG_WARNING, "Source format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt));
+ return -1;
+ }
+
+ if (t->dstfmt >= MAX_FORMAT) {
+ ast_log(LOG_WARNING, "Destination format %s is larger than MAX_FORMAT\n", ast_getformatname(t->dstfmt));
+ return -1;
+ }
+
+ if (t->buf_size) {
+ /*
+ * Align buf_size properly, rounding up to the machine-specific
+ * alignment for pointers.
+ */
+ struct _test_align { void *a, *b; } p;
+ int align = (char *)&p.b - (char *)&p.a;
+
+ t->buf_size = ((t->buf_size + align - 1) / align) * align;
+ }
+
+ if (t->frameout == NULL)
+ t->frameout = default_frameout;
+
+ calc_cost(t, 1);
+
+ ast_verb(2, "Registered translator '%s' from format %s to %s, cost %d\n",
+ term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)),
+ ast_getformatname(1 << t->srcfmt), ast_getformatname(1 << t->dstfmt), t->cost);
+
+ if (!added_cli) {
+ ast_cli_register_multiple(cli_translate, sizeof(cli_translate) / sizeof(struct ast_cli_entry));
+ added_cli++;
+ }
+
+ AST_RWLIST_WRLOCK(&translators);
+
+ /* find any existing translators that provide this same srcfmt/dstfmt,
+ and put this one in order based on cost */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+ if ((u->srcfmt == t->srcfmt) &&
+ (u->dstfmt == t->dstfmt) &&
+ (u->cost > t->cost)) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(t, list);
+ t = NULL;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ /* if no existing translator was found for this format combination,
+ add it to the beginning of the list */
+ if (t)
+ AST_RWLIST_INSERT_HEAD(&translators, t, list);
+
+ rebuild_matrix(0);
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return 0;
+}
+
+/*! \brief unregister codec translator */
+int ast_unregister_translator(struct ast_translator *t)
+{
+ char tmp[80];
+ struct ast_translator *u;
+ int found = 0;
+
+ AST_RWLIST_WRLOCK(&translators);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+ if (u == t) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(2, "Unregistered translator '%s' from format %s to %s\n", term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), ast_getformatname(1 << t->srcfmt), ast_getformatname(1 << t->dstfmt));
+ found = 1;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (found)
+ rebuild_matrix(0);
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return (u ? 0 : -1);
+}
+
+void ast_translator_activate(struct ast_translator *t)
+{
+ AST_RWLIST_WRLOCK(&translators);
+ t->active = 1;
+ rebuild_matrix(0);
+ AST_RWLIST_UNLOCK(&translators);
+}
+
+void ast_translator_deactivate(struct ast_translator *t)
+{
+ AST_RWLIST_WRLOCK(&translators);
+ t->active = 0;
+ rebuild_matrix(0);
+ AST_RWLIST_UNLOCK(&translators);
+}
+
+/*! \brief Calculate our best translator source format, given costs, and a desired destination */
+int ast_translator_best_choice(int *dst, int *srcs)
+{
+ int x,y;
+ int best = -1;
+ int bestdst = 0;
+ int cur, cursrc;
+ int besttime = INT_MAX;
+ int beststeps = INT_MAX;
+ int common = ((*dst) & (*srcs)) & AST_FORMAT_AUDIO_MASK; /* are there common formats ? */
+
+ if (common) { /* yes, pick one and return */
+ for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) {
+ if (cur & common) /* guaranteed to find one */
+ break;
+ }
+ /* We are done, this is a common format to both. */
+ *srcs = *dst = cur;
+ return 0;
+ } else { /* No, we will need to translate */
+ AST_RWLIST_RDLOCK(&translators);
+ for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) {
+ if (! (cur & *dst))
+ continue;
+ for (cursrc = 1, x = 0; x <= MAX_AUDIO_FORMAT; cursrc <<= 1, x++) {
+ if (!(*srcs & cursrc) || !tr_matrix[x][y].step ||
+ tr_matrix[x][y].cost > besttime)
+ continue; /* not existing or no better */
+ if (tr_matrix[x][y].cost < besttime ||
+ tr_matrix[x][y].multistep < beststeps) {
+ /* better than what we have so far */
+ best = cursrc;
+ bestdst = cur;
+ besttime = tr_matrix[x][y].cost;
+ beststeps = tr_matrix[x][y].multistep;
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&translators);
+ if (best > -1) {
+ *srcs = best;
+ *dst = bestdst;
+ best = 0;
+ }
+ return best;
+ }
+}
+
+unsigned int ast_translate_path_steps(unsigned int dest, unsigned int src)
+{
+ unsigned int res = -1;
+
+ /* convert bitwise format numbers into array indices */
+ src = powerof(src);
+ dest = powerof(dest);
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ if (tr_matrix[src][dest].step)
+ res = tr_matrix[src][dest].multistep + 1;
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return res;
+}
+
+unsigned int ast_translate_available_formats(unsigned int dest, unsigned int src)
+{
+ unsigned int res = dest;
+ unsigned int x;
+ unsigned int src_audio = src & AST_FORMAT_AUDIO_MASK;
+ unsigned int src_video = src & AST_FORMAT_VIDEO_MASK;
+
+ /* if we don't have a source format, we just have to try all
+ possible destination formats */
+ if (!src)
+ return dest;
+
+ /* If we have a source audio format, get its format index */
+ if (src_audio)
+ src_audio = powerof(src_audio);
+
+ /* If we have a source video format, get its format index */
+ if (src_video)
+ src_video = powerof(src_video);
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ /* For a given source audio format, traverse the list of
+ known audio formats to determine whether there exists
+ a translation path from the source format to the
+ destination format. */
+ for (x = 1; src_audio && (x & AST_FORMAT_AUDIO_MASK); x <<= 1) {
+ /* if this is not a desired format, nothing to do */
+ if (!dest & x)
+ continue;
+
+ /* if the source is supplying this format, then
+ we can leave it in the result */
+ if (src & x)
+ continue;
+
+ /* if we don't have a translation path from the src
+ to this format, remove it from the result */
+ if (!tr_matrix[src_audio][powerof(x)].step) {
+ res &= ~x;
+ continue;
+ }
+
+ /* now check the opposite direction */
+ if (!tr_matrix[powerof(x)][src_audio].step)
+ res &= ~x;
+ }
+
+ /* For a given source video format, traverse the list of
+ known video formats to determine whether there exists
+ a translation path from the source format to the
+ destination format. */
+ for (; src_video && (x & AST_FORMAT_VIDEO_MASK); x <<= 1) {
+ /* if this is not a desired format, nothing to do */
+ if (!dest & x)
+ continue;
+
+ /* if the source is supplying this format, then
+ we can leave it in the result */
+ if (src & x)
+ continue;
+
+ /* if we don't have a translation path from the src
+ to this format, remove it from the result */
+ if (!tr_matrix[src_video][powerof(x)].step) {
+ res &= ~x;
+ continue;
+ }
+
+ /* now check the opposite direction */
+ if (!tr_matrix[powerof(x)][src_video].step)
+ res &= ~x;
+ }
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return res;
+}
+
+void ast_translate_frame_freed(struct ast_frame *fr)
+{
+ struct ast_trans_pvt *pvt;
+
+ ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR);
+
+ pvt = (struct ast_trans_pvt *) (((char *) fr) - offsetof(struct ast_trans_pvt, f));
+
+ if (!pvt->destroy)
+ return;
+
+ destroy(pvt);
+}
diff --git a/trunk/main/udptl.c b/trunk/main/udptl.c
new file mode 100644
index 000000000..12de3fd53
--- /dev/null
+++ b/trunk/main/udptl.c
@@ -0,0 +1,1273 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * UDPTL support for T.38
+ *
+ * Copyright (C) 2005, Steve Underwood, partly based on RTP code which is
+ * Copyright (C) 1999-2006, Digium, Inc.
+ *
+ * Steve Underwood <steveu@coppice.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.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*!
+ * \file
+ *
+ * \brief UDPTL support for T.38 faxing
+ *
+ *
+ * \author Mark Spencer <markster@digium.com>, Steve Underwood <steveu@coppice.org>
+ *
+ * \page T38fax_udptl T38 fax passhtrough :: UDPTL
+ *
+ * Asterisk supports T.38 fax passthrough. Asterisk will not be a client, server
+ * or any form of gateway. Currently fax passthrough is only implemented in the
+ * SIP channel for strict SIP to SIP calls. If you are using chan_local or chan_agent
+ * as a proxy channel, T.38 passthrough will not work.
+ *
+ * UDPTL is handled very much like RTP. It can be reinvited to go directly between
+ * the endpoints, without involving Asterisk in the media stream.
+ *
+ * \b References:
+ * - chan_sip.c
+ * - udptl.c
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "asterisk/udptl.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/acl.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/netsock.h"
+#include "asterisk/cli.h"
+#include "asterisk/unaligned.h"
+
+#define UDPTL_MTU 1200
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+static int udptlstart;
+static int udptlend;
+static int udptldebug; /*!< Are we debugging? */
+static struct sockaddr_in udptldebugaddr; /*!< Debug packets to/from this host */
+#ifdef SO_NO_CHECK
+static int nochecksums;
+#endif
+static int udptlfectype;
+static int udptlfecentries;
+static int udptlfecspan;
+static int udptlmaxdatagram;
+
+#define LOCAL_FAX_MAX_DATAGRAM 400
+#define MAX_FEC_ENTRIES 5
+#define MAX_FEC_SPAN 5
+
+#define UDPTL_BUF_MASK 15
+
+typedef struct {
+ int buf_len;
+ uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+} udptl_fec_tx_buffer_t;
+
+typedef struct {
+ int buf_len;
+ uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+ int fec_len[MAX_FEC_ENTRIES];
+ uint8_t fec[MAX_FEC_ENTRIES][LOCAL_FAX_MAX_DATAGRAM];
+ int fec_span;
+ int fec_entries;
+} udptl_fec_rx_buffer_t;
+
+/*! \brief Structure for an UDPTL session */
+struct ast_udptl {
+ int fd;
+ char resp;
+ struct ast_frame f[16];
+ unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned int lasteventseqn;
+ int nat;
+ int flags;
+ struct sockaddr_in us;
+ struct sockaddr_in them;
+ int *ioid;
+ struct sched_context *sched;
+ struct io_context *io;
+ void *data;
+ ast_udptl_callback callback;
+ int udptl_offered_from_local;
+
+ /*! This option indicates the error correction scheme used in transmitted UDPTL
+ packets. */
+ int error_correction_scheme;
+
+ /*! This option indicates the number of error correction entries transmitted in
+ UDPTL packets. */
+ int error_correction_entries;
+
+ /*! This option indicates the span of the error correction entries in transmitted
+ UDPTL packets (FEC only). */
+ int error_correction_span;
+
+ /*! This option indicates the maximum size of a UDPTL packet that can be accepted by
+ the remote device. */
+ int far_max_datagram_size;
+
+ /*! This option indicates the maximum size of a UDPTL packet that we are prepared to
+ accept. */
+ int local_max_datagram_size;
+
+ int verbose;
+
+ struct sockaddr_in far;
+
+ int tx_seq_no;
+ int rx_seq_no;
+ int rx_expected_seq_no;
+
+ udptl_fec_tx_buffer_t tx[UDPTL_BUF_MASK + 1];
+ udptl_fec_rx_buffer_t rx[UDPTL_BUF_MASK + 1];
+};
+
+static AST_RWLIST_HEAD_STATIC(protos, ast_udptl_protocol);
+
+static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len);
+static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, uint8_t *ifp, int ifp_len);
+
+static inline int udptl_debug_test_addr(struct sockaddr_in *addr)
+{
+ if (udptldebug == 0)
+ return 0;
+ if (udptldebugaddr.sin_addr.s_addr) {
+ if (((ntohs(udptldebugaddr.sin_port) != 0)
+ && (udptldebugaddr.sin_port != addr->sin_port))
+ || (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+ return 0;
+ }
+ return 1;
+}
+
+static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue)
+{
+ if ((buf[*len] & 0x80) == 0) {
+ if (*len >= limit)
+ return -1;
+ *pvalue = buf[*len];
+ (*len)++;
+ return 0;
+ }
+ if ((buf[*len] & 0x40) == 0) {
+ if (*len >= limit - 1)
+ return -1;
+ *pvalue = (buf[*len] & 0x3F) << 8;
+ (*len)++;
+ *pvalue |= buf[*len];
+ (*len)++;
+ return 0;
+ }
+ if (*len >= limit)
+ return -1;
+ *pvalue = (buf[*len] & 0x3F) << 14;
+ (*len)++;
+ /* Indicate we have a fragment */
+ return 1;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p_object, int *p_num_octets)
+{
+ int octet_cnt;
+ int octet_idx;
+ int stat;
+ int i;
+ const uint8_t **pbuf;
+
+ for (octet_idx = 0, *p_num_octets = 0; ; octet_idx += octet_cnt) {
+ if ((stat = decode_length(buf, limit, len, &octet_cnt)) < 0)
+ return -1;
+ if (octet_cnt > 0) {
+ *p_num_octets += octet_cnt;
+
+ pbuf = &p_object[octet_idx];
+ i = 0;
+ /* Make sure the buffer contains at least the number of bits requested */
+ if ((*len + octet_cnt) > limit)
+ return -1;
+
+ *pbuf = &buf[*len];
+ *len += octet_cnt;
+ }
+ if (stat == 0)
+ break;
+ }
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int encode_length(uint8_t *buf, int *len, int value)
+{
+ int multiplier;
+
+ if (value < 0x80) {
+ /* 1 octet */
+ buf[*len] = value;
+ (*len)++;
+ return value;
+ }
+ if (value < 0x4000) {
+ /* 2 octets */
+ /* Set the first bit of the first octet */
+ buf[*len] = ((0x8000 | value) >> 8) & 0xFF;
+ (*len)++;
+ buf[*len] = value & 0xFF;
+ (*len)++;
+ return value;
+ }
+ /* Fragmentation */
+ multiplier = (value < 0x10000) ? (value >> 14) : 4;
+ /* Set the first 2 bits of the octet */
+ buf[*len] = 0xC0 | multiplier;
+ (*len)++;
+ return multiplier << 14;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int encode_open_type(uint8_t *buf, int *len, const uint8_t *data, int num_octets)
+{
+ int enclen;
+ int octet_idx;
+ uint8_t zero_byte;
+
+ /* If open type is of zero length, add a single zero byte (10.1) */
+ if (num_octets == 0) {
+ zero_byte = 0;
+ data = &zero_byte;
+ num_octets = 1;
+ }
+ /* Encode the open type */
+ for (octet_idx = 0; ; num_octets -= enclen, octet_idx += enclen) {
+ if ((enclen = encode_length(buf, len, num_octets)) < 0)
+ return -1;
+ if (enclen > 0) {
+ memcpy(&buf[*len], &data[octet_idx], enclen);
+ *len += enclen;
+ }
+ if (enclen >= num_octets)
+ break;
+ }
+
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
+{
+ int stat;
+ int stat2;
+ int i;
+ int j;
+ int k;
+ int l;
+ int m;
+ int x;
+ int limit;
+ int which;
+ int ptr;
+ int count;
+ int total_count;
+ int seq_no;
+ const uint8_t *ifp;
+ const uint8_t *data;
+ int ifp_len;
+ int repaired[16];
+ const uint8_t *bufs[16];
+ int lengths[16];
+ int span;
+ int entries;
+ int ifp_no;
+
+ ptr = 0;
+ ifp_no = 0;
+ memset(&s->f[0], 0, sizeof(s->f[0]));
+
+ /* Decode seq_number */
+ if (ptr + 2 > len)
+ return -1;
+ seq_no = (buf[0] << 8) | buf[1];
+ ptr += 2;
+
+ /* Break out the primary packet */
+ if ((stat = decode_open_type(buf, len, &ptr, &ifp, &ifp_len)) != 0)
+ return -1;
+ /* Decode error_recovery */
+ if (ptr + 1 > len)
+ return -1;
+ if ((buf[ptr++] & 0x80) == 0) {
+ /* Secondary packet mode for error recovery */
+ if (seq_no > s->rx_seq_no) {
+ /* We received a later packet than we expected, so we need to check if we can fill in the gap from the
+ secondary packets. */
+ total_count = 0;
+ do {
+ if ((stat2 = decode_length(buf, len, &ptr, &count)) < 0)
+ return -1;
+ for (i = 0; i < count; i++) {
+ if ((stat = decode_open_type(buf, len, &ptr, &bufs[total_count + i], &lengths[total_count + i])) != 0)
+ return -1;
+ }
+ total_count += count;
+ }
+ while (stat2 > 0);
+ /* Step through in reverse order, so we go oldest to newest */
+ for (i = total_count; i > 0; i--) {
+ if (seq_no - i >= s->rx_seq_no) {
+ /* This one wasn't seen before */
+ /* Decode the secondary IFP packet */
+ //fprintf(stderr, "Secondary %d, len %d\n", seq_no - i, lengths[i - 1]);
+ s->f[ifp_no].frametype = AST_FRAME_MODEM;
+ s->f[ifp_no].subclass = AST_MODEM_T38;
+
+ s->f[ifp_no].mallocd = 0;
+ s->f[ifp_no].seqno = seq_no - i;
+ s->f[ifp_no].datalen = lengths[i - 1];
+ s->f[ifp_no].data = (uint8_t *) bufs[i - 1];
+ s->f[ifp_no].offset = 0;
+ s->f[ifp_no].src = "UDPTL";
+ if (ifp_no > 0)
+ AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
+ AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
+ ifp_no++;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* FEC mode for error recovery */
+ /* Our buffers cannot tolerate overlength IFP packets in FEC mode */
+ if (ifp_len > LOCAL_FAX_MAX_DATAGRAM)
+ return -1;
+ /* Update any missed slots in the buffer */
+ for ( ; seq_no > s->rx_seq_no; s->rx_seq_no++) {
+ x = s->rx_seq_no & UDPTL_BUF_MASK;
+ s->rx[x].buf_len = -1;
+ s->rx[x].fec_len[0] = 0;
+ s->rx[x].fec_span = 0;
+ s->rx[x].fec_entries = 0;
+ }
+
+ x = seq_no & UDPTL_BUF_MASK;
+
+ memset(repaired, 0, sizeof(repaired));
+
+ /* Save the new IFP packet */
+ memcpy(s->rx[x].buf, ifp, ifp_len);
+ s->rx[x].buf_len = ifp_len;
+ repaired[x] = TRUE;
+
+ /* Decode the FEC packets */
+ /* The span is defined as an unconstrained integer, but will never be more
+ than a small value. */
+ if (ptr + 2 > len)
+ return -1;
+ if (buf[ptr++] != 1)
+ return -1;
+ span = buf[ptr++];
+ s->rx[x].fec_span = span;
+
+ /* The number of entries is defined as a length, but will only ever be a small
+ value. Treat it as such. */
+ if (ptr + 1 > len)
+ return -1;
+ entries = buf[ptr++];
+ s->rx[x].fec_entries = entries;
+
+ /* Decode the elements */
+ for (i = 0; i < entries; i++) {
+ if ((stat = decode_open_type(buf, len, &ptr, &data, &s->rx[x].fec_len[i])) != 0)
+ return -1;
+ if (s->rx[x].fec_len[i] > LOCAL_FAX_MAX_DATAGRAM)
+ return -1;
+
+ /* Save the new FEC data */
+ memcpy(s->rx[x].fec[i], data, s->rx[x].fec_len[i]);
+#if 0
+ fprintf(stderr, "FEC: ");
+ for (j = 0; j < s->rx[x].fec_len[i]; j++)
+ fprintf(stderr, "%02X ", data[j]);
+ fprintf(stderr, "\n");
+#endif
+ }
+
+ /* See if we can reconstruct anything which is missing */
+ /* TODO: this does not comprehensively hunt back and repair everything that is possible */
+ for (l = x; l != ((x - (16 - span*entries)) & UDPTL_BUF_MASK); l = (l - 1) & UDPTL_BUF_MASK) {
+ if (s->rx[l].fec_len[0] <= 0)
+ continue;
+ for (m = 0; m < s->rx[l].fec_entries; m++) {
+ limit = (l + m) & UDPTL_BUF_MASK;
+ for (which = -1, k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK) {
+ if (s->rx[k].buf_len <= 0)
+ which = (which == -1) ? k : -2;
+ }
+ if (which >= 0) {
+ /* Repairable */
+ for (j = 0; j < s->rx[l].fec_len[m]; j++) {
+ s->rx[which].buf[j] = s->rx[l].fec[m][j];
+ for (k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK)
+ s->rx[which].buf[j] ^= (s->rx[k].buf_len > j) ? s->rx[k].buf[j] : 0;
+ }
+ s->rx[which].buf_len = s->rx[l].fec_len[m];
+ repaired[which] = TRUE;
+ }
+ }
+ }
+ /* Now play any new packets forwards in time */
+ for (l = (x + 1) & UDPTL_BUF_MASK, j = seq_no - UDPTL_BUF_MASK; l != x; l = (l + 1) & UDPTL_BUF_MASK, j++) {
+ if (repaired[l]) {
+ //fprintf(stderr, "Fixed packet %d, len %d\n", j, l);
+ s->f[ifp_no].frametype = AST_FRAME_MODEM;
+ s->f[ifp_no].subclass = AST_MODEM_T38;
+
+ s->f[ifp_no].mallocd = 0;
+ s->f[ifp_no].seqno = j;
+ s->f[ifp_no].datalen = s->rx[l].buf_len;
+ s->f[ifp_no].data = s->rx[l].buf;
+ s->f[ifp_no].offset = 0;
+ s->f[ifp_no].src = "UDPTL";
+ if (ifp_no > 0)
+ AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
+ AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
+ ifp_no++;
+ }
+ }
+ }
+
+ /* If packets are received out of sequence, we may have already processed this packet from the error
+ recovery information in a packet already received. */
+ if (seq_no >= s->rx_seq_no) {
+ /* Decode the primary IFP packet */
+ s->f[ifp_no].frametype = AST_FRAME_MODEM;
+ s->f[ifp_no].subclass = AST_MODEM_T38;
+
+ s->f[ifp_no].mallocd = 0;
+ s->f[ifp_no].seqno = seq_no;
+ s->f[ifp_no].datalen = ifp_len;
+ s->f[ifp_no].data = (uint8_t *) ifp;
+ s->f[ifp_no].offset = 0;
+ s->f[ifp_no].src = "UDPTL";
+ if (ifp_no > 0)
+ AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
+ AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
+
+ ifp_no++;
+ }
+
+ s->rx_seq_no = seq_no + 1;
+ return ifp_no;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, uint8_t *ifp, int ifp_len)
+{
+ uint8_t fec[LOCAL_FAX_MAX_DATAGRAM];
+ int i;
+ int j;
+ int seq;
+ int entry;
+ int entries;
+ int span;
+ int m;
+ int len;
+ int limit;
+ int high_tide;
+
+ seq = s->tx_seq_no & 0xFFFF;
+
+ /* Map the sequence number to an entry in the circular buffer */
+ entry = seq & UDPTL_BUF_MASK;
+
+ /* We save the message in a circular buffer, for generating FEC or
+ redundancy sets later on. */
+ s->tx[entry].buf_len = ifp_len;
+ memcpy(s->tx[entry].buf, ifp, ifp_len);
+
+ /* Build the UDPTLPacket */
+
+ len = 0;
+ /* Encode the sequence number */
+ buf[len++] = (seq >> 8) & 0xFF;
+ buf[len++] = seq & 0xFF;
+
+ /* Encode the primary IFP packet */
+ if (encode_open_type(buf, &len, ifp, ifp_len) < 0)
+ return -1;
+
+ /* Encode the appropriate type of error recovery information */
+ switch (s->error_correction_scheme)
+ {
+ case UDPTL_ERROR_CORRECTION_NONE:
+ /* Encode the error recovery type */
+ buf[len++] = 0x00;
+ /* The number of entries will always be zero, so it is pointless allowing
+ for the fragmented case here. */
+ if (encode_length(buf, &len, 0) < 0)
+ return -1;
+ break;
+ case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+ /* Encode the error recovery type */
+ buf[len++] = 0x00;
+ if (s->tx_seq_no > s->error_correction_entries)
+ entries = s->error_correction_entries;
+ else
+ entries = s->tx_seq_no;
+ /* The number of entries will always be small, so it is pointless allowing
+ for the fragmented case here. */
+ if (encode_length(buf, &len, entries) < 0)
+ return -1;
+ /* Encode the elements */
+ for (i = 0; i < entries; i++) {
+ j = (entry - i - 1) & UDPTL_BUF_MASK;
+ if (encode_open_type(buf, &len, s->tx[j].buf, s->tx[j].buf_len) < 0)
+ return -1;
+ }
+ break;
+ case UDPTL_ERROR_CORRECTION_FEC:
+ span = s->error_correction_span;
+ entries = s->error_correction_entries;
+ if (seq < s->error_correction_span*s->error_correction_entries) {
+ /* In the initial stages, wind up the FEC smoothly */
+ entries = seq/s->error_correction_span;
+ if (seq < s->error_correction_span)
+ span = 0;
+ }
+ /* Encode the error recovery type */
+ buf[len++] = 0x80;
+ /* Span is defined as an inconstrained integer, which it dumb. It will only
+ ever be a small value. Treat it as such. */
+ buf[len++] = 1;
+ buf[len++] = span;
+ /* The number of entries is defined as a length, but will only ever be a small
+ value. Treat it as such. */
+ buf[len++] = entries;
+ for (m = 0; m < entries; m++) {
+ /* Make an XOR'ed entry the maximum length */
+ limit = (entry + m) & UDPTL_BUF_MASK;
+ high_tide = 0;
+ for (i = (limit - span*entries) & UDPTL_BUF_MASK; i != limit; i = (i + entries) & UDPTL_BUF_MASK) {
+ if (high_tide < s->tx[i].buf_len) {
+ for (j = 0; j < high_tide; j++)
+ fec[j] ^= s->tx[i].buf[j];
+ for ( ; j < s->tx[i].buf_len; j++)
+ fec[j] = s->tx[i].buf[j];
+ high_tide = s->tx[i].buf_len;
+ } else {
+ for (j = 0; j < s->tx[i].buf_len; j++)
+ fec[j] ^= s->tx[i].buf[j];
+ }
+ }
+ if (encode_open_type(buf, &len, fec, high_tide) < 0)
+ return -1;
+ }
+ break;
+ }
+
+ if (s->verbose)
+ fprintf(stderr, "\n");
+
+ s->tx_seq_no++;
+ return len;
+}
+
+int ast_udptl_fd(struct ast_udptl *udptl)
+{
+ return udptl->fd;
+}
+
+void ast_udptl_set_data(struct ast_udptl *udptl, void *data)
+{
+ udptl->data = data;
+}
+
+void ast_udptl_set_callback(struct ast_udptl *udptl, ast_udptl_callback callback)
+{
+ udptl->callback = callback;
+}
+
+void ast_udptl_setnat(struct ast_udptl *udptl, int nat)
+{
+ udptl->nat = nat;
+}
+
+static int udptlread(int *id, int fd, short events, void *cbdata)
+{
+ struct ast_udptl *udptl = cbdata;
+ struct ast_frame *f;
+
+ if ((f = ast_udptl_read(udptl))) {
+ if (udptl->callback)
+ udptl->callback(udptl, f, udptl->data);
+ }
+ return 1;
+}
+
+struct ast_frame *ast_udptl_read(struct ast_udptl *udptl)
+{
+ int res;
+ struct sockaddr_in sin;
+ socklen_t len;
+ uint16_t seqno = 0;
+ uint16_t *udptlheader;
+
+ len = sizeof(sin);
+
+ /* Cache where the header will go */
+ res = recvfrom(udptl->fd,
+ udptl->rawdata + AST_FRIENDLY_OFFSET,
+ sizeof(udptl->rawdata) - AST_FRIENDLY_OFFSET,
+ 0,
+ (struct sockaddr *) &sin,
+ &len);
+ udptlheader = (uint16_t *)(udptl->rawdata + AST_FRIENDLY_OFFSET);
+ if (res < 0) {
+ if (errno != EAGAIN)
+ ast_log(LOG_WARNING, "UDPTL read error: %s\n", strerror(errno));
+ if (errno == EBADF)
+ CRASH;
+ return &ast_null_frame;
+ }
+
+ /* Ignore if the other side hasn't been given an address yet. */
+ if (!udptl->them.sin_addr.s_addr || !udptl->them.sin_port)
+ return &ast_null_frame;
+
+ if (udptl->nat) {
+ /* Send to whoever sent to us */
+ if ((udptl->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (udptl->them.sin_port != sin.sin_port)) {
+ memcpy(&udptl->them, &sin, sizeof(udptl->them));
+ ast_debug(1, "UDPTL NAT: Using address %s:%d\n", ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
+ }
+ }
+
+ if (udptl_debug_test_addr(&sin)) {
+ ast_verb(1, "Got UDPTL packet from %s:%d (type %d, seq %d, len %d)\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), 0, seqno, res);
+ }
+#if 0
+ printf("Got UDPTL packet from %s:%d (seq %d, len = %d)\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), seqno, res);
+#endif
+ if (udptl_rx_packet(udptl, udptl->rawdata + AST_FRIENDLY_OFFSET, res) < 1)
+ return &ast_null_frame;
+
+ return &udptl->f[0];
+}
+
+void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local)
+{
+ if (udptl)
+ udptl->udptl_offered_from_local = local;
+ else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl)
+{
+ if (udptl)
+ return udptl->error_correction_scheme;
+ else {
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+ return -1;
+ }
+}
+
+void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec)
+{
+ if (udptl) {
+ switch (ec) {
+ case UDPTL_ERROR_CORRECTION_FEC:
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
+ break;
+ case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
+ break;
+ case UDPTL_ERROR_CORRECTION_NONE:
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
+ break;
+ default:
+ ast_log(LOG_WARNING, "error correction parameter invalid\n");
+ };
+ } else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl)
+{
+ if (udptl)
+ return udptl->local_max_datagram_size;
+ else {
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+ return -1;
+ }
+}
+
+int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl)
+{
+ if (udptl)
+ return udptl->far_max_datagram_size;
+ else {
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+ return -1;
+ }
+}
+
+void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram)
+{
+ if (udptl)
+ udptl->local_max_datagram_size = max_datagram;
+ else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram)
+{
+ if (udptl)
+ udptl->far_max_datagram_size = max_datagram;
+ else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr addr)
+{
+ struct ast_udptl *udptl;
+ int x;
+ int startplace;
+ int i;
+ long int flags;
+
+ if (!(udptl = ast_calloc(1, sizeof(*udptl))))
+ return NULL;
+
+ if (udptlfectype == 2)
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
+ else if (udptlfectype == 1)
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
+ else
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
+ udptl->error_correction_span = udptlfecspan;
+ udptl->error_correction_entries = udptlfecentries;
+
+ udptl->far_max_datagram_size = udptlmaxdatagram;
+ udptl->local_max_datagram_size = udptlmaxdatagram;
+
+ memset(&udptl->rx, 0, sizeof(udptl->rx));
+ memset(&udptl->tx, 0, sizeof(udptl->tx));
+ for (i = 0; i <= UDPTL_BUF_MASK; i++) {
+ udptl->rx[i].buf_len = -1;
+ udptl->tx[i].buf_len = -1;
+ }
+
+ udptl->them.sin_family = AF_INET;
+ udptl->us.sin_family = AF_INET;
+
+ if ((udptl->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ ast_free(udptl);
+ ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
+ return NULL;
+ }
+ flags = fcntl(udptl->fd, F_GETFL);
+ fcntl(udptl->fd, F_SETFL, flags | O_NONBLOCK);
+#ifdef SO_NO_CHECK
+ if (nochecksums)
+ setsockopt(udptl->fd, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
+#endif
+ /* Find us a place */
+ x = (ast_random() % (udptlend - udptlstart)) + udptlstart;
+ startplace = x;
+ for (;;) {
+ udptl->us.sin_port = htons(x);
+ udptl->us.sin_addr = addr;
+ if (bind(udptl->fd, (struct sockaddr *) &udptl->us, sizeof(udptl->us)) == 0)
+ break;
+ if (errno != EADDRINUSE) {
+ ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno));
+ close(udptl->fd);
+ ast_free(udptl);
+ return NULL;
+ }
+ if (++x > udptlend)
+ x = udptlstart;
+ if (x == startplace) {
+ ast_log(LOG_WARNING, "No UDPTL ports remaining\n");
+ close(udptl->fd);
+ ast_free(udptl);
+ return NULL;
+ }
+ }
+ if (io && sched && callbackmode) {
+ /* Operate this one in a callback mode */
+ udptl->sched = sched;
+ udptl->io = io;
+ udptl->ioid = ast_io_add(udptl->io, udptl->fd, udptlread, AST_IO_IN, udptl);
+ }
+ return udptl;
+}
+
+struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context *io, int callbackmode)
+{
+ struct in_addr ia;
+ memset(&ia, 0, sizeof(ia));
+ return ast_udptl_new_with_bindaddr(sched, io, callbackmode, ia);
+}
+
+int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos)
+{
+ return ast_netsock_set_qos(udptl->fd, tos, cos, "UDPTL");
+}
+
+void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
+{
+ udptl->them.sin_port = them->sin_port;
+ udptl->them.sin_addr = them->sin_addr;
+}
+
+void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
+{
+ them->sin_family = AF_INET;
+ them->sin_port = udptl->them.sin_port;
+ them->sin_addr = udptl->them.sin_addr;
+}
+
+void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us)
+{
+ memcpy(us, &udptl->us, sizeof(udptl->us));
+}
+
+void ast_udptl_stop(struct ast_udptl *udptl)
+{
+ memset(&udptl->them.sin_addr, 0, sizeof(udptl->them.sin_addr));
+ memset(&udptl->them.sin_port, 0, sizeof(udptl->them.sin_port));
+}
+
+void ast_udptl_destroy(struct ast_udptl *udptl)
+{
+ if (udptl->ioid)
+ ast_io_remove(udptl->io, udptl->ioid);
+ if (udptl->fd > -1)
+ close(udptl->fd);
+ ast_free(udptl);
+}
+
+int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f)
+{
+ int seq;
+ int len;
+ int res;
+ uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+
+ /* If we have no peer, return immediately */
+ if (s->them.sin_addr.s_addr == INADDR_ANY)
+ return 0;
+
+ /* If there is no data length, return immediately */
+ if (f->datalen == 0)
+ return 0;
+
+ if (f->frametype != AST_FRAME_MODEM) {
+ ast_log(LOG_WARNING, "UDPTL can only send T.38 data\n");
+ return -1;
+ }
+
+ /* Save seq_no for debug output because udptl_build_packet increments it */
+ seq = s->tx_seq_no & 0xFFFF;
+
+ /* Cook up the UDPTL packet, with the relevant EC info. */
+ len = udptl_build_packet(s, buf, f->data, f->datalen);
+
+ if (len > 0 && s->them.sin_port && s->them.sin_addr.s_addr) {
+ if ((res = sendto(s->fd, buf, len, 0, (struct sockaddr *) &s->them, sizeof(s->them))) < 0)
+ ast_log(LOG_NOTICE, "UDPTL Transmission error to %s:%d: %s\n", ast_inet_ntoa(s->them.sin_addr), ntohs(s->them.sin_port), strerror(errno));
+#if 0
+ printf("Sent %d bytes of UDPTL data to %s:%d\n", res, ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
+#endif
+ if (udptl_debug_test_addr(&s->them))
+ ast_verb(1, "Sent UDPTL packet to %s:%d (type %d, seq %d, len %d)\n",
+ ast_inet_ntoa(s->them.sin_addr),
+ ntohs(s->them.sin_port), 0, seq, len);
+ }
+
+ return 0;
+}
+
+void ast_udptl_proto_unregister(struct ast_udptl_protocol *proto)
+{
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_REMOVE(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+}
+
+int ast_udptl_proto_register(struct ast_udptl_protocol *proto)
+{
+ struct ast_udptl_protocol *cur;
+
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (cur->type == proto->type) {
+ ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
+ AST_RWLIST_UNLOCK(&protos);
+ return -1;
+ }
+ }
+ AST_RWLIST_INSERT_TAIL(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+ return 0;
+}
+
+static struct ast_udptl_protocol *get_proto(struct ast_channel *chan)
+{
+ struct ast_udptl_protocol *cur = NULL;
+
+ AST_RWLIST_RDLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (cur->type == chan->tech->type)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&protos);
+
+ return cur;
+}
+
+int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
+{
+ struct ast_frame *f;
+ struct ast_channel *who;
+ struct ast_channel *cs[3];
+ struct ast_udptl *p0;
+ struct ast_udptl *p1;
+ struct ast_udptl_protocol *pr0;
+ struct ast_udptl_protocol *pr1;
+ struct sockaddr_in ac0;
+ struct sockaddr_in ac1;
+ struct sockaddr_in t0;
+ struct sockaddr_in t1;
+ void *pvt0;
+ void *pvt1;
+ int to;
+
+ ast_channel_lock(c0);
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+ pr0 = get_proto(c0);
+ pr1 = get_proto(c1);
+ if (!pr0) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ if (!pr1) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ pvt0 = c0->tech_pvt;
+ pvt1 = c1->tech_pvt;
+ p0 = pr0->get_udptl_info(c0);
+ p1 = pr1->get_udptl_info(c1);
+ if (!p0 || !p1) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return -2;
+ }
+ if (pr0->set_udptl_peer(c0, p1)) {
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
+ } else {
+ /* Store UDPTL peer */
+ ast_udptl_get_peer(p1, &ac1);
+ }
+ if (pr1->set_udptl_peer(c1, p0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
+ else {
+ /* Store UDPTL peer */
+ ast_udptl_get_peer(p0, &ac0);
+ }
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
+ ast_debug(1, "Oooh, something is weird, backing out\n");
+ /* Tell it to try again later */
+ return -3;
+ }
+ to = -1;
+ ast_udptl_get_peer(p1, &t1);
+ ast_udptl_get_peer(p0, &t0);
+ if (inaddrcmp(&t1, &ac1)) {
+ ast_debug(1, "Oooh, '%s' changed end address to %s:%d\n",
+ c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port));
+ ast_debug(1, "Oooh, '%s' was %s:%d\n",
+ c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port));
+ memcpy(&ac1, &t1, sizeof(ac1));
+ }
+ if (inaddrcmp(&t0, &ac0)) {
+ ast_debug(1, "Oooh, '%s' changed end address to %s:%d\n",
+ c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port));
+ ast_debug(1, "Oooh, '%s' was %s:%d\n",
+ c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port));
+ memcpy(&ac0, &t0, sizeof(ac0));
+ }
+ who = ast_waitfor_n(cs, 2, &to);
+ if (!who) {
+ ast_debug(1, "Ooh, empty read...\n");
+ /* check for hangup / whentohangup */
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ f = ast_read(who);
+ if (!f) {
+ *fo = f;
+ *rc = who;
+ ast_debug(1, "Oooh, got a %s\n", f ? "digit" : "hangup");
+ /* That's all we needed */
+ return 0;
+ } else {
+ if (f->frametype == AST_FRAME_MODEM) {
+ /* Forward T.38 frames if they happen upon us */
+ if (who == c0) {
+ ast_write(c1, f);
+ } else if (who == c1) {
+ ast_write(c0, f);
+ }
+ }
+ ast_frfree(f);
+ }
+ /* Swap priority. Not that it's a big deal at this point */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ }
+ return -1;
+}
+
+static char *handle_cli_udptl_debug_ip(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port;
+ char *p;
+ char *arg;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "udptl debug ip";
+ e->usage =
+ "Usage: udptl debug [ip host[:port]]\n"
+ " Enable dumping of all UDPTL packets to and from host.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ port = 0;
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ arg = a->argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL)
+ return CLI_SHOWUSAGE;
+ udptldebugaddr.sin_family = AF_INET;
+ memcpy(&udptldebugaddr.sin_addr, hp->h_addr, sizeof(udptldebugaddr.sin_addr));
+ udptldebugaddr.sin_port = htons(port);
+ if (port == 0)
+ ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s\n", ast_inet_ntoa(udptldebugaddr.sin_addr));
+ else
+ ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(udptldebugaddr.sin_addr), port);
+ udptldebug = 1;
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_udptl_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "udptl debug";
+ e->usage =
+ "Usage: udptl debug\n"
+ " Enable dumping of all UDPTL packets.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ udptldebug = 1;
+ memset(&udptldebugaddr, 0, sizeof(udptldebugaddr));
+
+ ast_cli(a->fd, "UDPTL Debugging Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_udptl_debug_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "udptl debug off";
+ e->usage =
+ "Usage: udptl debug off\n"
+ " Disable dumping of all UDPTL packets.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ udptldebug = 0;
+
+ ast_cli(a->fd, "UDPTL Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_udptl[] = {
+ AST_CLI_DEFINE(handle_cli_udptl_debug, "Enable UDPTL debugging"),
+ AST_CLI_DEFINE(handle_cli_udptl_debug_ip, "Enable UDPTL debugging on IP"),
+ AST_CLI_DEFINE(handle_cli_udptl_debug_off, "Disable UDPTL debugging")
+};
+
+static void __ast_udptl_reload(int reload)
+{
+ struct ast_config *cfg;
+ const char *s;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("udptl.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return;
+
+ udptlstart = 4500;
+ udptlend = 4999;
+ udptlfectype = 0;
+ udptlfecentries = 0;
+ udptlfecspan = 0;
+ udptlmaxdatagram = 0;
+
+ if (cfg) {
+ if ((s = ast_variable_retrieve(cfg, "general", "udptlstart"))) {
+ udptlstart = atoi(s);
+ if (udptlstart < 1024)
+ udptlstart = 1024;
+ if (udptlstart > 65535)
+ udptlstart = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "udptlend"))) {
+ udptlend = atoi(s);
+ if (udptlend < 1024)
+ udptlend = 1024;
+ if (udptlend > 65535)
+ udptlend = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "udptlchecksums"))) {
+#ifdef SO_NO_CHECK
+ if (ast_false(s))
+ nochecksums = 1;
+ else
+ nochecksums = 0;
+#else
+ if (ast_false(s))
+ ast_log(LOG_WARNING, "Disabling UDPTL checksums is not supported on this operating system!\n");
+#endif
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "T38FaxUdpEC"))) {
+ if (strcmp(s, "t38UDPFEC") == 0)
+ udptlfectype = 2;
+ else if (strcmp(s, "t38UDPRedundancy") == 0)
+ udptlfectype = 1;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "T38FaxMaxDatagram"))) {
+ udptlmaxdatagram = atoi(s);
+ if (udptlmaxdatagram < 0)
+ udptlmaxdatagram = 0;
+ if (udptlmaxdatagram > LOCAL_FAX_MAX_DATAGRAM)
+ udptlmaxdatagram = LOCAL_FAX_MAX_DATAGRAM;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "UDPTLFECentries"))) {
+ udptlfecentries = atoi(s);
+ if (udptlfecentries < 0)
+ udptlfecentries = 0;
+ if (udptlfecentries > MAX_FEC_ENTRIES)
+ udptlfecentries = MAX_FEC_ENTRIES;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "UDPTLFECspan"))) {
+ udptlfecspan = atoi(s);
+ if (udptlfecspan < 0)
+ udptlfecspan = 0;
+ if (udptlfecspan > MAX_FEC_SPAN)
+ udptlfecspan = MAX_FEC_SPAN;
+ }
+ ast_config_destroy(cfg);
+ }
+ if (udptlstart >= udptlend) {
+ ast_log(LOG_WARNING, "Unreasonable values for UDPTL start/end\n");
+ udptlstart = 4500;
+ udptlend = 4999;
+ }
+ ast_verb(2, "UDPTL allocating from port range %d -> %d\n", udptlstart, udptlend);
+}
+
+void ast_udptl_reload(void)
+{
+ __ast_udptl_reload(1);
+}
+
+void ast_udptl_init(void)
+{
+ ast_cli_register_multiple(cli_udptl, sizeof(cli_udptl) / sizeof(struct ast_cli_entry));
+ __ast_udptl_reload(0);
+}
diff --git a/trunk/main/ulaw.c b/trunk/main/ulaw.c
new file mode 100644
index 000000000..18a118f60
--- /dev/null
+++ b/trunk/main/ulaw.c
@@ -0,0 +1,246 @@
+/*
+ * 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 u-Law to Signed linear conversion
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/ulaw.h"
+
+#if 0
+/* ZEROTRAP is the military recommendation to improve the encryption
+ * of u-Law traffic. It is irrelevant with modern encryption systems
+ * like AES, and will simply degrade the signal quality.
+ * ZEROTRAP is not implemented in AST_LIN2MU and so the coding table
+ * tests will fail if you use it */
+#define ZEROTRAP /*!< turn on the trap as per the MIL-STD */
+#endif
+
+#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+#ifndef G711_NEW_ALGORITHM
+
+unsigned char __ast_lin2mu[16384];
+short __ast_mulaw[256];
+
+static unsigned char linear2ulaw(short sample)
+{
+ static int exp_lut[256] = {
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 };
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0)
+ sample = -sample; /* get magnitude */
+ if (sample > CLIP)
+ sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[(sample >> 7) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+
+#ifdef ZEROTRAP
+ if (ulawbyte == 0)
+ ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+#else
+
+unsigned char __ast_lin2mu[AST_ULAW_TAB_SIZE];
+short __ast_mulaw[256];
+
+static unsigned char linear2ulaw(short sample, int full_coding)
+{
+ static const unsigned exp_lut[256] = {
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 };
+ unsigned sign, exponent, mantissa, mag;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ ast_ulaw_get_sign_mag(sample, &sign, &mag);
+ if (mag > CLIP)
+ mag = CLIP; /* clip the magnitude */
+
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0)
+ sample = -sample; /* get magnitude */
+ if (sample > CLIP)
+ sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ mag += BIAS;
+ exponent = exp_lut[(mag >> 7) & 0xFF];
+ mantissa = (mag >> (exponent + 3)) & 0x0F;
+
+ if (full_coding) {
+ /* full encoding, with sign and xform */
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+ if (ulawbyte == 0)
+ ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+ } else {
+ /* half-cooked coding -- mantissa+exponent only (for lookup tab) */
+ ulawbyte = (exponent << 4) | mantissa;
+ }
+
+ return ulawbyte;
+}
+
+static inline short ulaw2linear(unsigned char ulawbyte)
+{
+ unsigned exponent, mantissa;
+ short sample;
+ static const short etab[]={0,132,396,924,1980,4092,8316,16764};
+
+ ulawbyte = ~ulawbyte;
+ exponent = (ulawbyte & 0x70) >> 4;
+ mantissa = ulawbyte & 0x0f;
+ sample = mantissa << (exponent + 3);
+ sample += etab[exponent];
+ if (ulawbyte & 0x80)
+ sample = -sample;
+ return sample;
+}
+#endif
+
+/*!
+ * \brief Set up mu-law conversion table
+ */
+void ast_ulaw_init(void)
+{
+ int i;
+
+ /*
+ * Set up mu-law conversion table
+ */
+#ifndef G711_NEW_ALGORITHM
+ for (i = 0;i < 256;i++) {
+ short mu,e,f,y;
+ static short etab[]={0,132,396,924,1980,4092,8316,16764};
+
+ mu = 255-i;
+ e = (mu & 0x70)/16;
+ f = mu & 0x0f;
+ y = f * (1 << (e + 3));
+ y += etab[e];
+ if (mu & 0x80) y = -y;
+ __ast_mulaw[i] = y;
+ }
+ /* set up the reverse (mu-law) conversion table */
+ for (i = -32768; i < 32768; i++) {
+ __ast_lin2mu[((unsigned short)i) >> 2] = linear2ulaw(i);
+ }
+#else
+
+ for (i = 0; i < 256; i++) {
+ __ast_mulaw[i] = ulaw2linear(i);
+ }
+ /* set up the reverse (mu-law) conversion table */
+ for (i = 0; i <= 32768; i += AST_ULAW_STEP) {
+ AST_LIN2MU_LOOKUP(i) = linear2ulaw(i, 0 /* half-cooked */);
+ }
+#endif
+
+#ifdef TEST_CODING_TABLES
+ for (i = -32768; i < 32768; ++i) {
+#ifndef G711_NEW_ALGORITHM
+ unsigned char e1 = linear2ulaw(i);
+#else
+ unsigned char e1 = linear2ulaw(i, 1);
+#endif
+ short d1 = ulaw2linear(e1);
+ unsigned char e2 = AST_LIN2MU(i);
+ short d2 = ulaw2linear(e2);
+ short d3 = AST_MULAW(e1);
+
+ if (e1 != e2 || d1 != d3 || d2 != d3) {
+ ast_log(LOG_WARNING, "u-Law coding tables test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2);
+ }
+ }
+ ast_log(LOG_NOTICE, "u-Law coding table test complete.\n");
+#endif /* TEST_CODING_TABLES */
+
+#ifdef TEST_TANDEM_TRANSCODING
+ /* tandem transcoding test */
+ for (i = -32768; i < 32768; ++i) {
+ unsigned char e1 = AST_LIN2MU(i);
+ short d1 = AST_MULAW(e1);
+ unsigned char e2 = AST_LIN2MU(d1);
+ short d2 = AST_MULAW(e2);
+ unsigned char e3 = AST_LIN2MU(d2);
+ short d3 = AST_MULAW(e3);
+
+ if (i < 0 && e1 == 0x7f && e2 == 0xff && e3 == 0xff)
+ continue; /* known and normal negative 0 case */
+
+ if (e1 != e2 || e2 != e3 || d1 != d2 || d2 != d3) {
+ ast_log(LOG_WARNING, "u-Law tandem transcoding test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d, d3=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2, (int)d3);
+ }
+ }
+ ast_log(LOG_NOTICE, "u-Law tandem transcoding test complete.\n");
+#endif /* TEST_TANDEM_TRANSCODING */
+}
+
diff --git a/trunk/main/utils.c b/trunk/main/utils.c
new file mode 100644
index 000000000..1b91781fa
--- /dev/null
+++ b/trunk/main/utils.c
@@ -0,0 +1,1557 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Utility functions
+ *
+ * \note These are important for portability and security,
+ * so please use them in favour of other routines.
+ * Please consult the CODING GUIDELINES for more information.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_DEV_URANDOM
+#include <fcntl.h>
+#endif
+
+#include "asterisk/network.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in lock.h if required */
+#include "asterisk/lock.h"
+#include "asterisk/io.h"
+#include "asterisk/md5.h"
+#include "asterisk/sha1.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/strings.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/time.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/stringfields.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/utils.h"
+
+#define AST_API_MODULE
+#include "asterisk/threadstorage.h"
+
+static char base64[64];
+static char b2a[256];
+
+AST_THREADSTORAGE(inet_ntoa_buf);
+
+#if !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_6)
+
+#define ERANGE 34 /*!< duh? ERANGE value copied from web... */
+#undef gethostbyname
+
+AST_MUTEX_DEFINE_STATIC(__mutex);
+
+/*! \brief Reentrant replacement for gethostbyname for BSD-based systems.
+\note This
+routine is derived from code originally written and placed in the public
+domain by Enzo Michelangeli <em@em.no-ip.com> */
+
+static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result,
+ int *h_errnop)
+{
+ int hsave;
+ struct hostent *ph;
+ ast_mutex_lock(&__mutex); /* begin critical area */
+ hsave = h_errno;
+
+ ph = gethostbyname(name);
+ *h_errnop = h_errno; /* copy h_errno to *h_herrnop */
+ if (ph == NULL) {
+ *result = NULL;
+ } else {
+ char **p, **q;
+ char *pbuf;
+ int nbytes=0;
+ int naddr=0, naliases=0;
+ /* determine if we have enough space in buf */
+
+ /* count how many addresses */
+ for (p = ph->h_addr_list; *p != 0; p++) {
+ nbytes += ph->h_length; /* addresses */
+ nbytes += sizeof(*p); /* pointers */
+ naddr++;
+ }
+ nbytes += sizeof(*p); /* one more for the terminating NULL */
+
+ /* count how many aliases, and total length of strings */
+ for (p = ph->h_aliases; *p != 0; p++) {
+ nbytes += (strlen(*p)+1); /* aliases */
+ nbytes += sizeof(*p); /* pointers */
+ naliases++;
+ }
+ nbytes += sizeof(*p); /* one more for the terminating NULL */
+
+ /* here nbytes is the number of bytes required in buffer */
+ /* as a terminator must be there, the minimum value is ph->h_length */
+ if (nbytes > buflen) {
+ *result = NULL;
+ ast_mutex_unlock(&__mutex); /* end critical area */
+ return ERANGE; /* not enough space in buf!! */
+ }
+
+ /* There is enough space. Now we need to do a deep copy! */
+ /* Allocation in buffer:
+ from [0] to [(naddr-1) * sizeof(*p)]:
+ pointers to addresses
+ at [naddr * sizeof(*p)]:
+ NULL
+ from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
+ pointers to aliases
+ at [(naddr+naliases+1) * sizeof(*p)]:
+ NULL
+ then naddr addresses (fixed length), and naliases aliases (asciiz).
+ */
+
+ *ret = *ph; /* copy whole structure (not its address!) */
+
+ /* copy addresses */
+ q = (char **)buf; /* pointer to pointers area (type: char **) */
+ ret->h_addr_list = q; /* update pointer to address list */
+ pbuf = buf + ((naddr + naliases + 2) * sizeof(*p)); /* skip that area */
+ for (p = ph->h_addr_list; *p != 0; p++) {
+ memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
+ *q++ = pbuf; /* the pointer is the one inside buf... */
+ pbuf += ph->h_length; /* advance pbuf */
+ }
+ *q++ = NULL; /* address list terminator */
+
+ /* copy aliases */
+ ret->h_aliases = q; /* update pointer to aliases list */
+ for (p = ph->h_aliases; *p != 0; p++) {
+ strcpy(pbuf, *p); /* copy alias strings */
+ *q++ = pbuf; /* the pointer is the one inside buf... */
+ pbuf += strlen(*p); /* advance pbuf */
+ *pbuf++ = 0; /* string terminator */
+ }
+ *q++ = NULL; /* terminator */
+
+ strcpy(pbuf, ph->h_name); /* copy alias strings */
+ ret->h_name = pbuf;
+ pbuf += strlen(ph->h_name); /* advance pbuf */
+ *pbuf++ = 0; /* string terminator */
+
+ *result = ret; /* and let *result point to structure */
+
+ }
+ h_errno = hsave; /* restore h_errno */
+ ast_mutex_unlock(&__mutex); /* end critical area */
+
+ return (*result == NULL); /* return 0 on success, non-zero on error */
+}
+
+
+#endif
+
+/*! \brief Re-entrant (thread safe) version of gethostbyname that replaces the
+ standard gethostbyname (which is not thread safe)
+*/
+struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
+{
+ int res;
+ int herrno;
+ int dots=0;
+ const char *s;
+ struct hostent *result = NULL;
+ /* Although it is perfectly legitimate to lookup a pure integer, for
+ the sake of the sanity of people who like to name their peers as
+ integers, we break with tradition and refuse to look up a
+ pure integer */
+ s = host;
+ res = 0;
+ while (s && *s) {
+ if (*s == '.')
+ dots++;
+ else if (!isdigit(*s))
+ break;
+ s++;
+ }
+ if (!s || !*s) {
+ /* Forge a reply for IP's to avoid octal IP's being interpreted as octal */
+ if (dots != 3)
+ return NULL;
+ memset(hp, 0, sizeof(struct ast_hostent));
+ hp->hp.h_addrtype = AF_INET;
+ hp->hp.h_addr_list = (void *) hp->buf;
+ hp->hp.h_addr = hp->buf + sizeof(void *);
+ if (inet_pton(AF_INET, host, hp->hp.h_addr) > 0)
+ return &hp->hp;
+ return NULL;
+
+ }
+#ifdef HAVE_GETHOSTBYNAME_R_5
+ result = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &herrno);
+
+ if (!result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
+ return NULL;
+#else
+ res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
+
+ if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
+ return NULL;
+#endif
+ return &hp->hp;
+}
+
+
+
+AST_MUTEX_DEFINE_STATIC(test_lock);
+AST_MUTEX_DEFINE_STATIC(test_lock2);
+static pthread_t test_thread;
+static int lock_count = 0;
+static int test_errors = 0;
+
+/*! \brief This is a regression test for recursive mutexes.
+ test_for_thread_safety() will return 0 if recursive mutex locks are
+ working properly, and non-zero if they are not working properly. */
+static void *test_thread_body(void *data)
+{
+ ast_mutex_lock(&test_lock);
+ lock_count += 10;
+ if (lock_count != 10)
+ test_errors++;
+ ast_mutex_lock(&test_lock);
+ lock_count += 10;
+ if (lock_count != 20)
+ test_errors++;
+ ast_mutex_lock(&test_lock2);
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 10;
+ if (lock_count != 10)
+ test_errors++;
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 10;
+ ast_mutex_unlock(&test_lock2);
+ if (lock_count != 0)
+ test_errors++;
+ return NULL;
+}
+
+int test_for_thread_safety(void)
+{
+ ast_mutex_lock(&test_lock2);
+ ast_mutex_lock(&test_lock);
+ lock_count += 1;
+ ast_mutex_lock(&test_lock);
+ lock_count += 1;
+ ast_pthread_create(&test_thread, NULL, test_thread_body, NULL);
+ usleep(100);
+ if (lock_count != 2)
+ test_errors++;
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 1;
+ usleep(100);
+ if (lock_count != 1)
+ test_errors++;
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 1;
+ if (lock_count != 0)
+ test_errors++;
+ ast_mutex_unlock(&test_lock2);
+ usleep(100);
+ if (lock_count != 0)
+ test_errors++;
+ pthread_join(test_thread, NULL);
+ return(test_errors); /* return 0 on success. */
+}
+
+/*! \brief Produce 32 char MD5 hash of value. */
+void ast_md5_hash(char *output, char *input)
+{
+ struct MD5Context md5;
+ unsigned char digest[16];
+ char *ptr;
+ int x;
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)input, strlen(input));
+ MD5Final(digest, &md5);
+ ptr = output;
+ for (x = 0; x < 16; x++)
+ ptr += sprintf(ptr, "%2.2x", digest[x]);
+}
+
+/*! \brief Produce 40 char SHA1 hash of value. */
+void ast_sha1_hash(char *output, char *input)
+{
+ struct SHA1Context sha;
+ char *ptr;
+ int x;
+ uint8_t Message_Digest[20];
+
+ SHA1Reset(&sha);
+
+ SHA1Input(&sha, (const unsigned char *) input, strlen(input));
+
+ SHA1Result(&sha, Message_Digest);
+ ptr = output;
+ for (x = 0; x < 20; x++)
+ ptr += sprintf(ptr, "%2.2x", Message_Digest[x]);
+}
+
+/*! \brief decode BASE64 encoded text */
+int ast_base64decode(unsigned char *dst, const char *src, int max)
+{
+ int cnt = 0;
+ unsigned int byte = 0;
+ unsigned int bits = 0;
+ int incnt = 0;
+ while (*src && (cnt < max)) {
+ /* Shift in 6 bits of input */
+ byte <<= 6;
+ byte |= (b2a[(int)(*src)]) & 0x3f;
+ bits += 6;
+ src++;
+ incnt++;
+ /* If we have at least 8 bits left over, take that character
+ off the top */
+ if (bits >= 8) {
+ bits -= 8;
+ *dst = (byte >> bits) & 0xff;
+ dst++;
+ cnt++;
+ }
+ }
+ /* Dont worry about left over bits, they're extra anyway */
+ return cnt;
+}
+
+/*! \brief encode text to BASE64 coding */
+int ast_base64encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks)
+{
+ int cnt = 0;
+ int col = 0;
+ unsigned int byte = 0;
+ int bits = 0;
+ int cntin = 0;
+ /* Reserve space for null byte at end of string */
+ max--;
+ while ((cntin < srclen) && (cnt < max)) {
+ byte <<= 8;
+ byte |= *(src++);
+ bits += 8;
+ cntin++;
+ if ((bits == 24) && (cnt + 4 <= max)) {
+ *dst++ = base64[(byte >> 18) & 0x3f];
+ *dst++ = base64[(byte >> 12) & 0x3f];
+ *dst++ = base64[(byte >> 6) & 0x3f];
+ *dst++ = base64[byte & 0x3f];
+ cnt += 4;
+ col += 4;
+ bits = 0;
+ byte = 0;
+ }
+ if (linebreaks && (cnt < max) && (col == 64)) {
+ *dst++ = '\n';
+ cnt++;
+ col = 0;
+ }
+ }
+ if (bits && (cnt + 4 <= max)) {
+ /* Add one last character for the remaining bits,
+ padding the rest with 0 */
+ byte <<= 24 - bits;
+ *dst++ = base64[(byte >> 18) & 0x3f];
+ *dst++ = base64[(byte >> 12) & 0x3f];
+ if (bits == 16)
+ *dst++ = base64[(byte >> 6) & 0x3f];
+ else
+ *dst++ = '=';
+ *dst++ = '=';
+ cnt += 4;
+ }
+ if (linebreaks && (cnt < max)) {
+ *dst++ = '\n';
+ cnt++;
+ }
+ *dst = '\0';
+ return cnt;
+}
+
+int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
+{
+ return ast_base64encode_full(dst, src, srclen, max, 0);
+}
+
+static void base64_init(void)
+{
+ int x;
+ memset(b2a, -1, sizeof(b2a));
+ /* Initialize base-64 Conversion table */
+ for (x = 0; x < 26; x++) {
+ /* A-Z */
+ base64[x] = 'A' + x;
+ b2a['A' + x] = x;
+ /* a-z */
+ base64[x + 26] = 'a' + x;
+ b2a['a' + x] = x + 26;
+ /* 0-9 */
+ if (x < 10) {
+ base64[x + 52] = '0' + x;
+ b2a['0' + x] = x + 52;
+ }
+ }
+ base64[62] = '+';
+ base64[63] = '/';
+ b2a[(int)'+'] = 62;
+ b2a[(int)'/'] = 63;
+}
+
+/*! \brief ast_uri_encode: Turn text string to URI-encoded %XX version
+\note At this point, we're converting from ISO-8859-x (8-bit), not UTF8
+ as in the SIP protocol spec
+ If doreserved == 1 we will convert reserved characters also.
+ RFC 2396, section 2.4
+ outbuf needs to have more memory allocated than the instring
+ to have room for the expansion. Every char that is converted
+ is replaced by three ASCII characters.
+
+ Note: The doreserved option is needed for replaces header in
+ SIP transfers.
+*/
+char *ast_uri_encode(const char *string, char *outbuf, int buflen, int doreserved)
+{
+ char *reserved = ";/?:@&=+$, "; /* Reserved chars */
+
+ const char *ptr = string; /* Start with the string */
+ char *out = NULL;
+ char *buf = NULL;
+
+ ast_copy_string(outbuf, string, buflen);
+
+ /* If there's no characters to convert, just go through and don't do anything */
+ while (*ptr) {
+ if ((*ptr < 32 || (unsigned char) *ptr) > 127 || (doreserved && strchr(reserved, *ptr)) ) {
+ /* Oops, we need to start working here */
+ if (!buf) {
+ buf = outbuf;
+ out = buf + (ptr - string) ; /* Set output ptr */
+ }
+ out += sprintf(out, "%%%02x", (unsigned char) *ptr);
+ } else if (buf) {
+ *out = *ptr; /* Continue copying the string */
+ out++;
+ }
+ ptr++;
+ }
+ if (buf)
+ *out = '\0';
+ return outbuf;
+}
+
+/*! \brief ast_uri_decode: Decode SIP URI, URN, URL (overwrite the string) */
+void ast_uri_decode(char *s)
+{
+ char *o;
+ unsigned int tmp;
+
+ for (o = s; *s; s++, o++) {
+ if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) {
+ /* have '%', two chars and correct parsing */
+ *o = tmp;
+ s += 2; /* Will be incremented once more when we break out */
+ } else /* all other cases, just copy */
+ *o = *s;
+ }
+ *o = '\0';
+}
+
+/*! \brief ast_inet_ntoa: Recursive thread safe replacement of inet_ntoa */
+const char *ast_inet_ntoa(struct in_addr ia)
+{
+ char *buf;
+
+ if (!(buf = ast_threadstorage_get(&inet_ntoa_buf, INET_ADDRSTRLEN)))
+ return "";
+
+ return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN);
+}
+
+#ifdef HAVE_DEV_URANDOM
+static int dev_urandom_fd;
+#endif
+
+#ifndef __linux__
+#undef pthread_create /* For ast_pthread_create function only */
+#endif /* !__linux__ */
+
+#if !defined(LOW_MEMORY)
+
+#ifdef DEBUG_THREADS
+
+/*! \brief A reasonable maximum number of locks a thread would be holding ... */
+#define AST_MAX_LOCKS 64
+
+/* Allow direct use of pthread_mutex_t and friends */
+#undef pthread_mutex_t
+#undef pthread_mutex_lock
+#undef pthread_mutex_unlock
+#undef pthread_mutex_init
+#undef pthread_mutex_destroy
+
+/*!
+ * \brief Keep track of which locks a thread holds
+ *
+ * There is an instance of this struct for every active thread
+ */
+struct thr_lock_info {
+ /*! The thread's ID */
+ pthread_t thread_id;
+ /*! The thread name which includes where the thread was started */
+ const char *thread_name;
+ /*! This is the actual container of info for what locks this thread holds */
+ struct {
+ const char *file;
+ int line_num;
+ const char *func;
+ const char *lock_name;
+ void *lock_addr;
+ int times_locked;
+ enum ast_lock_type type;
+ /*! This thread is waiting on this lock */
+ int pending:2;
+ } locks[AST_MAX_LOCKS];
+ /*! This is the number of locks currently held by this thread.
+ * The index (num_locks - 1) has the info on the last one in the
+ * locks member */
+ unsigned int num_locks;
+ /*! Protects the contents of the locks member
+ * Intentionally not ast_mutex_t */
+ pthread_mutex_t lock;
+ AST_LIST_ENTRY(thr_lock_info) entry;
+};
+
+/*!
+ * \brief Locked when accessing the lock_infos list
+ */
+AST_MUTEX_DEFINE_STATIC(lock_infos_lock);
+/*!
+ * \brief A list of each thread's lock info
+ */
+static AST_LIST_HEAD_NOLOCK_STATIC(lock_infos, thr_lock_info);
+
+/*!
+ * \brief Destroy a thread's lock info
+ *
+ * This gets called automatically when the thread stops
+ */
+static void lock_info_destroy(void *data)
+{
+ struct thr_lock_info *lock_info = data;
+
+ pthread_mutex_lock(&lock_infos_lock.mutex);
+ AST_LIST_REMOVE(&lock_infos, lock_info, entry);
+ pthread_mutex_unlock(&lock_infos_lock.mutex);
+
+ pthread_mutex_destroy(&lock_info->lock);
+ if (lock_info->thread_name)
+ free((void *) lock_info->thread_name);
+ free(lock_info);
+}
+
+/*!
+ * \brief The thread storage key for per-thread lock info
+ */
+AST_THREADSTORAGE_CUSTOM(thread_lock_info, NULL, lock_info_destroy);
+
+void ast_store_lock_info(enum ast_lock_type type, const char *filename,
+ int line_num, const char *func, const char *lock_name, void *lock_addr)
+{
+ struct thr_lock_info *lock_info;
+ int i;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+
+ for (i = 0; i < lock_info->num_locks; i++) {
+ if (lock_info->locks[i].lock_addr == lock_addr) {
+ lock_info->locks[i].times_locked++;
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+ }
+
+ if (lock_info->num_locks == AST_MAX_LOCKS) {
+ /* Can't use ast_log here, because it will cause infinite recursion */
+ fprintf(stderr, "XXX ERROR XXX A thread holds more locks than '%d'."
+ " Increase AST_MAX_LOCKS!\n", AST_MAX_LOCKS);
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+
+ lock_info->locks[i].file = filename;
+ lock_info->locks[i].line_num = line_num;
+ lock_info->locks[i].func = func;
+ lock_info->locks[i].lock_name = lock_name;
+ lock_info->locks[i].lock_addr = lock_addr;
+ lock_info->locks[i].times_locked = 1;
+ lock_info->locks[i].type = type;
+ lock_info->locks[i].pending = 1;
+ lock_info->num_locks++;
+
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+void ast_mark_lock_acquired(void)
+{
+ struct thr_lock_info *lock_info;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+ lock_info->locks[lock_info->num_locks - 1].pending = 0;
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+void ast_mark_lock_failed(void)
+{
+ struct thr_lock_info *lock_info;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+ lock_info->locks[lock_info->num_locks - 1].pending = -1;
+ lock_info->locks[lock_info->num_locks - 1].times_locked--;
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+void ast_remove_lock_info(void *lock_addr)
+{
+ struct thr_lock_info *lock_info;
+ int i = 0;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+
+ for (i = lock_info->num_locks - 1; i >= 0; i--) {
+ if (lock_info->locks[i].lock_addr == lock_addr)
+ break;
+ }
+
+ if (i == -1) {
+ /* Lock not found :( */
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+
+ if (lock_info->locks[i].times_locked > 1) {
+ lock_info->locks[i].times_locked--;
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+
+ if (i < lock_info->num_locks - 1) {
+ /* Not the last one ... *should* be rare! */
+ memmove(&lock_info->locks[i], &lock_info->locks[i + 1],
+ (lock_info->num_locks - (i + 1)) * sizeof(lock_info->locks[0]));
+ }
+
+ lock_info->num_locks--;
+
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+static const char *locktype2str(enum ast_lock_type type)
+{
+ switch (type) {
+ case AST_MUTEX:
+ return "MUTEX";
+ case AST_RDLOCK:
+ return "RDLOCK";
+ case AST_WRLOCK:
+ return "WRLOCK";
+ }
+
+ return "UNKNOWN";
+}
+
+static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct thr_lock_info *lock_info;
+ struct ast_str *str;
+
+ if (!(str = ast_str_create(4096)))
+ return CLI_FAILURE;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show locks";
+ e->usage =
+ "Usage: core show locks\n"
+ " This command is for lock debugging. It prints out which locks\n"
+ "are owned by each active thread.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_str_append(&str, 0, "\n"
+ "=======================================================================\n"
+ "=== Currently Held Locks ==============================================\n"
+ "=======================================================================\n"
+ "===\n"
+ "=== <file> <line num> <function> <lock name> <lock addr> (times locked)\n"
+ "===\n");
+
+ if (!str)
+ return CLI_FAILURE;
+
+ pthread_mutex_lock(&lock_infos_lock.mutex);
+ AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
+ int i;
+ ast_str_append(&str, 0, "=== Thread ID: %d (%s)\n", (int) lock_info->thread_id,
+ lock_info->thread_name);
+ pthread_mutex_lock(&lock_info->lock);
+ for (i = 0; str && i < lock_info->num_locks; i++) {
+ int j;
+ ast_mutex_t *lock;
+
+ ast_str_append(&str, 0, "=== ---> %sLock #%d (%s): %s %d %s %s %p (%d)\n",
+ lock_info->locks[i].pending > 0 ? "Waiting for " :
+ lock_info->locks[i].pending < 0 ? "Tried and failed to get " : "", i,
+ lock_info->locks[i].file,
+ locktype2str(lock_info->locks[i].type),
+ lock_info->locks[i].line_num,
+ lock_info->locks[i].func, lock_info->locks[i].lock_name,
+ lock_info->locks[i].lock_addr,
+ lock_info->locks[i].times_locked);
+
+ if (!lock_info->locks[i].pending || lock_info->locks[i].pending == -1)
+ continue;
+
+ /* We only have further details for mutexes right now */
+ if (lock_info->locks[i].type != AST_MUTEX)
+ continue;
+
+ lock = lock_info->locks[i].lock_addr;
+
+ ast_reentrancy_lock(lock);
+ for (j = 0; str && j < lock->reentrancy; j++) {
+ ast_str_append(&str, 0, "=== --- ---> Locked Here: %s line %d (%s)\n",
+ lock->file[j], lock->lineno[j], lock->func[j]);
+ }
+ ast_reentrancy_unlock(lock);
+ }
+ pthread_mutex_unlock(&lock_info->lock);
+ if (!str)
+ break;
+ ast_str_append(&str, 0, "=== -------------------------------------------------------------------\n"
+ "===\n");
+ if (!str)
+ break;
+ }
+ pthread_mutex_unlock(&lock_infos_lock.mutex);
+
+ if (!str)
+ return CLI_FAILURE;
+
+ ast_str_append(&str, 0, "=======================================================================\n"
+ "\n");
+
+ if (!str)
+ return CLI_FAILURE;
+
+ ast_cli(a->fd, "%s", str->str);
+
+ ast_free(str);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry utils_cli[] = {
+ AST_CLI_DEFINE(handle_show_locks, "Show which locks are held by which thread"),
+};
+
+#endif /* DEBUG_THREADS */
+
+/*
+ * support for 'show threads'. The start routine is wrapped by
+ * dummy_start(), so that ast_register_thread() and
+ * ast_unregister_thread() know the thread identifier.
+ */
+struct thr_arg {
+ void *(*start_routine)(void *);
+ void *data;
+ char *name;
+};
+
+/*
+ * on OS/X, pthread_cleanup_push() and pthread_cleanup_pop()
+ * are odd macros which start and end a block, so they _must_ be
+ * used in pairs (the latter with a '1' argument to call the
+ * handler on exit.
+ * On BSD we don't need this, but we keep it for compatibility.
+ */
+static void *dummy_start(void *data)
+{
+ void *ret;
+ struct thr_arg a = *((struct thr_arg *) data); /* make a local copy */
+#ifdef DEBUG_THREADS
+ struct thr_lock_info *lock_info;
+ pthread_mutexattr_t mutex_attr;
+#endif
+
+ /* note that even though data->name is a pointer to allocated memory,
+ we are not freeing it here because ast_register_thread is going to
+ keep a copy of the pointer and then ast_unregister_thread will
+ free the memory
+ */
+ ast_free(data);
+ ast_register_thread(a.name);
+ pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
+
+#ifdef DEBUG_THREADS
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return NULL;
+
+ lock_info->thread_id = pthread_self();
+ lock_info->thread_name = strdup(a.name);
+
+ pthread_mutexattr_init(&mutex_attr);
+ pthread_mutexattr_settype(&mutex_attr, AST_MUTEX_KIND);
+ pthread_mutex_init(&lock_info->lock, &mutex_attr);
+ pthread_mutexattr_destroy(&mutex_attr);
+
+ pthread_mutex_lock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
+ AST_LIST_INSERT_TAIL(&lock_infos, lock_info, entry);
+ pthread_mutex_unlock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
+#endif /* DEBUG_THREADS */
+
+ ret = a.start_routine(a.data);
+
+ pthread_cleanup_pop(1);
+
+ return ret;
+}
+
+#endif /* !LOW_MEMORY */
+
+int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *data, size_t stacksize, const char *file, const char *caller,
+ int line, const char *start_fn)
+{
+#if !defined(LOW_MEMORY)
+ struct thr_arg *a;
+#endif
+
+ if (!attr) {
+ attr = alloca(sizeof(*attr));
+ pthread_attr_init(attr);
+ }
+
+#ifdef __linux__
+ /* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
+ which is kind of useless. Change this here to
+ PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
+ priority will propagate down to new threads by default.
+ This does mean that callers cannot set a different priority using
+ PTHREAD_EXPLICIT_SCHED in the attr argument; instead they must set
+ the priority afterwards with pthread_setschedparam(). */
+ if ((errno = pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED)))
+ ast_log(LOG_WARNING, "pthread_attr_setinheritsched: %s\n", strerror(errno));
+#endif
+
+ if (!stacksize)
+ stacksize = AST_STACKSIZE;
+
+ if ((errno = pthread_attr_setstacksize(attr, stacksize ? stacksize : AST_STACKSIZE)))
+ ast_log(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(errno));
+
+#if !defined(LOW_MEMORY)
+ if ((a = ast_malloc(sizeof(*a)))) {
+ a->start_routine = start_routine;
+ a->data = data;
+ start_routine = dummy_start;
+ asprintf(&a->name, "%-20s started at [%5d] %s %s()",
+ start_fn, line, file, caller);
+ data = a;
+ }
+#endif /* !LOW_MEMORY */
+
+ return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
+}
+
+
+int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *data, size_t stacksize, const char *file, const char *caller,
+ int line, const char *start_fn)
+{
+ unsigned char attr_destroy = 0;
+ int res;
+
+ if (!attr) {
+ attr = alloca(sizeof(*attr));
+ pthread_attr_init(attr);
+ attr_destroy = 1;
+ }
+
+ if ((errno = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)))
+ ast_log(LOG_WARNING, "pthread_attr_setdetachstate: %s\n", strerror(errno));
+
+ res = ast_pthread_create_stack(thread, attr, start_routine, data,
+ stacksize, file, caller, line, start_fn);
+
+ if (attr_destroy)
+ pthread_attr_destroy(attr);
+
+ return res;
+}
+
+int ast_wait_for_input(int fd, int ms)
+{
+ struct pollfd pfd[1];
+ memset(pfd, 0, sizeof(pfd));
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN|POLLPRI;
+ return poll(pfd, 1, ms);
+}
+
+/*!
+ * Try to write string, but wait no more than ms milliseconds before timing out.
+ *
+ * \note The code assumes that the file descriptor has NONBLOCK set,
+ * so there is only one system call made to do a write, unless we actually
+ * have a need to wait. This way, we get better performance.
+ * If the descriptor is blocking, all assumptions on the guaranteed
+ * detail do not apply anymore.
+ * Also note that in the current implementation, the delay is per-write,
+ * so you still have no guarantees, anyways.
+ * Fortunately the routine is only used in a few places (cli.c, manager.c,
+ * res_agi.c) so it is reasonably easy to check how it behaves there.
+ *
+ * XXX We either need to fix the code, or fix the documentation.
+ */
+int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
+{
+ /* Try to write string, but wait no more than ms milliseconds
+ before timing out */
+ int res = 0;
+ struct pollfd fds[1];
+ while (len) {
+ res = write(fd, s, len);
+ if ((res < 0) && (errno != EAGAIN)) {
+ return -1;
+ }
+ if (res < 0)
+ res = 0;
+ len -= res;
+ s += res;
+ res = 0;
+ if (len) {
+ fds[0].fd = fd;
+ fds[0].events = POLLOUT;
+ /* Wait until writable again */
+ res = poll(fds, 1, timeoutms);
+ if (res < 1)
+ return -1;
+ }
+ }
+ return res;
+}
+
+char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
+{
+ char *e;
+ char *q;
+
+ s = ast_strip(s);
+ if ((q = strchr(beg_quotes, *s)) && *q != '\0') {
+ e = s + strlen(s) - 1;
+ if (*e == *(end_quotes + (q - beg_quotes))) {
+ s++;
+ *e = '\0';
+ }
+ }
+
+ return s;
+}
+
+char *ast_unescape_semicolon(char *s)
+{
+ char *e;
+ char *work = s;
+
+ while ((e = strchr(work, ';'))) {
+ if ((e > work) && (*(e-1) == '\\')) {
+ memmove(e - 1, e, strlen(e) + 1);
+ work = e;
+ } else {
+ work = e + 1;
+ }
+ }
+
+ return s;
+}
+
+/* !\brief unescape some C sequences in place, return pointer to the original string.
+ */
+char *ast_unescape_c(char *src)
+{
+ char c, *ret, *dst;
+
+ if (src == NULL)
+ return NULL;
+ for (ret = dst = src; (c = *src++); *dst++ = c ) {
+ if (c != '\\')
+ continue; /* copy char at the end of the loop */
+ switch ((c = *src++)) {
+ case '\0': /* special, trailing '\' */
+ c = '\\';
+ break;
+ case 'b': /* backspace */
+ c = '\b';
+ break;
+ case 'f': /* form feed */
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ }
+ /* default, use the char literally */
+ }
+ *dst = '\0';
+ return ret;
+}
+
+int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
+{
+ int result;
+
+ if (!buffer || !*buffer || !space || !*space)
+ return -1;
+
+ result = vsnprintf(*buffer, *space, fmt, ap);
+
+ if (result < 0)
+ return -1;
+ else if (result > *space)
+ result = *space;
+
+ *buffer += result;
+ *space -= result;
+ return 0;
+}
+
+int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = ast_build_string_va(buffer, space, fmt, ap);
+ va_end(ap);
+
+ return result;
+}
+
+int ast_true(const char *s)
+{
+ if (ast_strlen_zero(s))
+ return 0;
+
+ /* Determine if this is a true value */
+ if (!strcasecmp(s, "yes") ||
+ !strcasecmp(s, "true") ||
+ !strcasecmp(s, "y") ||
+ !strcasecmp(s, "t") ||
+ !strcasecmp(s, "1") ||
+ !strcasecmp(s, "on"))
+ return -1;
+
+ return 0;
+}
+
+int ast_false(const char *s)
+{
+ if (ast_strlen_zero(s))
+ return 0;
+
+ /* Determine if this is a false value */
+ if (!strcasecmp(s, "no") ||
+ !strcasecmp(s, "false") ||
+ !strcasecmp(s, "n") ||
+ !strcasecmp(s, "f") ||
+ !strcasecmp(s, "0") ||
+ !strcasecmp(s, "off"))
+ return -1;
+
+ return 0;
+}
+
+#define ONE_MILLION 1000000
+/*
+ * put timeval in a valid range. usec is 0..999999
+ * negative values are not allowed and truncated.
+ */
+static struct timeval tvfix(struct timeval a)
+{
+ if (a.tv_usec >= ONE_MILLION) {
+ ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
+ a.tv_sec, (long int) a.tv_usec);
+ a.tv_sec += a.tv_usec / ONE_MILLION;
+ a.tv_usec %= ONE_MILLION;
+ } else if (a.tv_usec < 0) {
+ ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
+ a.tv_sec, (long int) a.tv_usec);
+ a.tv_usec = 0;
+ }
+ return a;
+}
+
+struct timeval ast_tvadd(struct timeval a, struct timeval b)
+{
+ /* consistency checks to guarantee usec in 0..999999 */
+ a = tvfix(a);
+ b = tvfix(b);
+ a.tv_sec += b.tv_sec;
+ a.tv_usec += b.tv_usec;
+ if (a.tv_usec >= ONE_MILLION) {
+ a.tv_sec++;
+ a.tv_usec -= ONE_MILLION;
+ }
+ return a;
+}
+
+struct timeval ast_tvsub(struct timeval a, struct timeval b)
+{
+ /* consistency checks to guarantee usec in 0..999999 */
+ a = tvfix(a);
+ b = tvfix(b);
+ a.tv_sec -= b.tv_sec;
+ a.tv_usec -= b.tv_usec;
+ if (a.tv_usec < 0) {
+ a.tv_sec-- ;
+ a.tv_usec += ONE_MILLION;
+ }
+ return a;
+}
+#undef ONE_MILLION
+
+/*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
+ * BSD libc (and others) do not. */
+
+#ifndef linux
+AST_MUTEX_DEFINE_STATIC(randomlock);
+#endif
+
+long int ast_random(void)
+{
+ long int res;
+#ifdef HAVE_DEV_URANDOM
+ if (dev_urandom_fd >= 0) {
+ int read_res = read(dev_urandom_fd, &res, sizeof(res));
+ if (read_res > 0) {
+ long int rm = RAND_MAX;
+ res = res < 0 ? ~res : res;
+ rm++;
+ return res % rm;
+ }
+ }
+#endif
+#ifdef linux
+ res = random();
+#else
+ ast_mutex_lock(&randomlock);
+ res = random();
+ ast_mutex_unlock(&randomlock);
+#endif
+ return res;
+}
+
+char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
+{
+ char *dataPut = start;
+ int inEscape = 0;
+ int inQuotes = 0;
+
+ for (; *start; start++) {
+ if (inEscape) {
+ *dataPut++ = *start; /* Always goes verbatim */
+ inEscape = 0;
+ } else {
+ if (*start == '\\') {
+ inEscape = 1; /* Do not copy \ into the data */
+ } else if (*start == '\'') {
+ inQuotes = 1 - inQuotes; /* Do not copy ' into the data */
+ } else {
+ /* Replace , with |, unless in quotes */
+ *dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
+ }
+ }
+ }
+ if (start != dataPut)
+ *dataPut = 0;
+ return dataPut;
+}
+
+void ast_join(char *s, size_t len, char * const w[])
+{
+ int x, ofs = 0;
+ const char *src;
+
+ /* Join words into a string */
+ if (!s)
+ return;
+ for (x = 0; ofs < len && w[x]; x++) {
+ if (x > 0)
+ s[ofs++] = ' ';
+ for (src = w[x]; *src && ofs < len; src++)
+ s[ofs++] = *src;
+ }
+ if (ofs == len)
+ ofs--;
+ s[ofs] = '\0';
+}
+
+/*
+ * stringfields support routines.
+ */
+
+const char __ast_string_field_empty[] = ""; /*!< the empty string */
+
+/*! \brief add a new block to the pool.
+ * We can only allocate from the topmost pool, so the
+ * fields in *mgr reflect the size of that only.
+ */
+static int add_string_pool(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t size)
+{
+ struct ast_string_field_pool *pool;
+
+ if (!(pool = ast_calloc(1, sizeof(*pool) + size)))
+ return -1;
+
+ pool->prev = *pool_head;
+ *pool_head = pool;
+ mgr->size = size;
+ mgr->used = 0;
+
+ return 0;
+}
+
+/*
+ * This is an internal API, code should not use it directly.
+ * It initializes all fields as empty, then uses 'size' for 3 functions:
+ * size > 0 means initialize the pool list with a pool of given size.
+ * This must be called right after allocating the object.
+ * size = 0 means release all pools except the most recent one.
+ * This is useful to e.g. reset an object to the initial value.
+ * size < 0 means release all pools.
+ * This must be done before destroying the object.
+ */
+int __ast_string_field_init(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, int size)
+{
+ const char **p = (const char **)pool_head + 1;
+ struct ast_string_field_pool *cur = *pool_head;
+
+ /* clear fields - this is always necessary */
+ while ((struct ast_string_field_mgr *)p != mgr)
+ *p++ = __ast_string_field_empty;
+ if (size > 0) { /* allocate the initial pool */
+ *pool_head = NULL;
+ return add_string_pool(mgr, pool_head, size);
+ }
+ if (size < 0) { /* reset all pools */
+ *pool_head = NULL;
+ } else { /* preserve the first pool */
+ if (cur == NULL) {
+ ast_log(LOG_WARNING, "trying to reset empty pool\n");
+ return -1;
+ }
+ cur = cur->prev;
+ (*pool_head)->prev = NULL;
+ mgr->used = 0;
+ }
+ while (cur) {
+ struct ast_string_field_pool *prev = cur->prev;
+ ast_free(cur);
+ cur = prev;
+ }
+ return 0;
+}
+
+ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t needed)
+{
+ char *result = NULL;
+ size_t space = mgr->size - mgr->used;
+
+ if (__builtin_expect(needed > space, 0)) {
+ size_t new_size = mgr->size * 2;
+
+ while (new_size < needed)
+ new_size *= 2;
+
+ if (add_string_pool(mgr, pool_head, new_size))
+ return NULL;
+ }
+
+ result = (*pool_head)->base + mgr->used;
+ mgr->used += needed;
+ return result;
+}
+
+void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head,
+ const ast_string_field *ptr, const char *format, va_list ap1, va_list ap2)
+{
+ size_t needed;
+ char *dst = (*pool_head)->base + mgr->used;
+ const char **p = (const char **)ptr;
+ size_t space = mgr->size - mgr->used;
+
+ /* try to write using available space */
+ needed = vsnprintf(dst, space, format, ap1) + 1;
+
+ va_end(ap1);
+
+ if (needed > space) { /* if it fails, reallocate */
+ size_t new_size = mgr->size * 2;
+
+ while (new_size < needed)
+ new_size *= 2;
+
+ if (add_string_pool(mgr, pool_head, new_size))
+ return;
+
+ dst = (*pool_head)->base + mgr->used;
+ vsprintf(dst, format, ap2);
+ }
+
+ *p = dst;
+ mgr->used += needed;
+}
+
+void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head,
+ const ast_string_field *ptr, const char *format, ...)
+{
+ va_list ap1, ap2;
+
+ va_start(ap1, format);
+ va_start(ap2, format); /* va_copy does not exist on FreeBSD */
+
+ __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap1, ap2);
+
+ va_end(ap1);
+ va_end(ap2);
+}
+/* end of stringfields support */
+
+AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
+
+int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
+{
+ int ret;
+ ast_mutex_lock(&fetchadd_m);
+ ret = *p;
+ *p += v;
+ ast_mutex_unlock(&fetchadd_m);
+ return ret;
+}
+
+/*! \brief
+ * get values from config variables.
+ */
+int ast_get_timeval(const char *src, struct timeval *dst, struct timeval _default, int *consumed)
+{
+ long double dtv = 0.0;
+ int scanned;
+
+ if (dst == NULL)
+ return -1;
+
+ *dst = _default;
+
+ if (ast_strlen_zero(src))
+ return -1;
+
+ /* only integer at the moment, but one day we could accept more formats */
+ if (sscanf(src, "%Lf%n", &dtv, &scanned) > 0) {
+ dst->tv_sec = dtv;
+ dst->tv_usec = (dtv - dst->tv_sec) * 1000000.0;
+ if (consumed)
+ *consumed = scanned;
+ return 0;
+ } else
+ return -1;
+}
+
+/*! \brief
+ * get values from config variables.
+ */
+int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
+{
+ long t;
+ int scanned;
+
+ if (dst == NULL)
+ return -1;
+
+ *dst = _default;
+
+ if (ast_strlen_zero(src))
+ return -1;
+
+ /* only integer at the moment, but one day we could accept more formats */
+ if (sscanf(src, "%ld%n", &t, &scanned) == 1) {
+ *dst = t;
+ if (consumed)
+ *consumed = scanned;
+ return 0;
+ } else
+ return -1;
+}
+
+/*!
+ * core handler for dynamic strings.
+ * This is not meant to be called directly, but rather through the
+ * various wrapper macros
+ * ast_str_set(...)
+ * ast_str_append(...)
+ * ast_str_set_va(...)
+ * ast_str_append_va(...)
+ */
+int __ast_str_helper(struct ast_str **buf, size_t max_len,
+ int append, const char *fmt, va_list ap)
+{
+ int res, need;
+ int offset = (append && (*buf)->len) ? (*buf)->used : 0;
+
+ if (max_len < 0)
+ max_len = (*buf)->len; /* don't exceed the allocated space */
+ /*
+ * Ask vsnprintf how much space we need. Remember that vsnprintf
+ * does not count the final '\0' so we must add 1.
+ */
+ res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap);
+
+ need = res + offset + 1;
+ /*
+ * If there is not enough space and we are below the max length,
+ * reallocate the buffer and return a message telling to retry.
+ */
+ if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) {
+ if (max_len && max_len < need) /* truncate as needed */
+ need = max_len;
+ else if (max_len == 0) /* if unbounded, give more room for next time */
+ need += 16 + need/4;
+ if (0) /* debugging */
+ ast_verbose("extend from %d to %d\n", (int)(*buf)->len, need);
+ if (ast_str_make_space(buf, need)) {
+ ast_verbose("failed to extend from %d to %d\n", (int)(*buf)->len, need);
+ return AST_DYNSTR_BUILD_FAILED;
+ }
+ (*buf)->str[offset] = '\0'; /* Truncate the partial write. */
+
+ /* va_end() and va_start() must be done before calling
+ * vsnprintf() again. */
+ return AST_DYNSTR_BUILD_RETRY;
+ }
+ /* update space used, keep in mind the truncation */
+ (*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset;
+
+ return res;
+}
+
+void ast_enable_packet_fragmentation(int sock)
+{
+#if defined(HAVE_IP_MTU_DISCOVER)
+ int val = IP_PMTUDISC_DONT;
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)))
+ ast_log(LOG_WARNING, "Unable to disable PMTU discovery. Large UDP packets may fail to be delivered when sent from this socket.\n");
+#endif /* HAVE_IP_MTU_DISCOVER */
+}
+
+int ast_mkdir(const char *path, int mode)
+{
+ char *ptr;
+ int len = strlen(path), count = 0, x, piececount = 0;
+ char *tmp = ast_strdupa(path);
+ char **pieces;
+ char *fullpath = alloca(len + 1);
+ int res = 0;
+
+ for (ptr = tmp; *ptr; ptr++) {
+ if (*ptr == '/')
+ count++;
+ }
+
+ /* Count the components to the directory path */
+ pieces = alloca(count * sizeof(*pieces));
+ for (ptr = tmp; *ptr; ptr++) {
+ if (*ptr == '/') {
+ *ptr = '\0';
+ pieces[piececount++] = ptr + 1;
+ }
+ }
+
+ *fullpath = '\0';
+ for (x = 0; x < piececount; x++) {
+ /* This looks funky, but the buffer is always ideally-sized, so it's fine. */
+ strcat(fullpath, "/");
+ strcat(fullpath, pieces[x]);
+ res = mkdir(fullpath, mode);
+ if (res && errno != EEXIST)
+ return errno;
+ }
+ return 0;
+}
+
+int ast_utils_init(void)
+{
+#ifdef HAVE_DEV_URANDOM
+ dev_urandom_fd = open("/dev/urandom", O_RDONLY);
+#endif
+ base64_init();
+#ifdef DEBUG_THREADS
+ ast_cli_register_multiple(utils_cli, sizeof(utils_cli) / sizeof(utils_cli[0]));
+#endif
+ return 0;
+}
diff --git a/trunk/makeopts.in b/trunk/makeopts.in
new file mode 100644
index 000000000..aac5cf8b1
--- /dev/null
+++ b/trunk/makeopts.in
@@ -0,0 +1,227 @@
+# NOTE: Names of _INCLUDE and _LIB entries in this file must be
+# the exact uppercase equivalents of the names used for
+# dependencies in menuselect for the same package.
+
+CC=@PTHREAD_CC@
+HOST_CC=cc
+CXX=@CXX@
+
+INSTALL=@INSTALL@
+AWK=@AWK@
+GREP=@GREP@
+AR=@AR@
+RANLIB=@RANLIB@
+FIND=@FIND@
+COMPRESS=@COMPRESS@
+BASENAME=@BASENAME@
+ID=@ID@
+SHELL=@SHELL@
+LN=@LN@
+DOT=@DOT@
+STRIP=@STRIP@
+WGET=@WGET@
+FETCH=@FETCH@
+DOWNLOAD=@DOWNLOAD@
+RUBBER=@RUBBER@
+KPATHSEA=@KPATHSEA@
+
+BUILD_PLATFORM=@BUILD_PLATFORM@
+BUILD_CPU=@BUILD_CPU@
+BUILD_VENDOR=@BUILD_VENDOR@
+BUILD_OS=@BUILD_OS@
+
+HOST_PLATFORM=@HOST_PLATFORM@
+HOST_CPU=@HOST_CPU@
+HOST_VENDOR=@HOST_VENDOR@
+HOST_OS=@HOST_OS@
+
+PROC=@HOST_CPU@
+OSARCH=@OSARCH@
+OSREV=@PBX_OSREV@
+
+GC_CFLAGS=@GC_CFLAGS@
+GC_LDFLAGS=@GC_LDFLAGS@
+
+PTHREAD_CFLAGS=@PTHREAD_CFLAGS@
+PTHREAD_LIBS=@PTHREAD_LIBS@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+datarootdir = @datarootdir@
+datadir = @datadir@
+includedir = @includedir@
+infodir = @infodir@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+
+AST_DEVMODE=@AST_DEVMODE@
+
+AST_DECLARATION_AFTER_STATEMENT=@AST_DECLARATION_AFTER_STATEMENT@
+
+ASOUND_INCLUDE=@ALSA_INCLUDE@
+ASOUND_LIB=@ALSA_LIB@
+
+CURL_INCLUDE=@CURL_INCLUDE@
+CURL_LIB=@CURL_LIB@
+
+CURSES_INCLUDE=@CURSES_INCLUDE@
+CURSES_LIB=@CURSES_LIB@
+CURSES_DIR=@CURSES_DIR@
+
+EDITLINE_LIB=@EDITLINE_LIB@
+
+FREETDS_INCLUDE=@FREETDS_INCLUDE@
+FREETDS_LIB=@FREETDS_LIB@
+
+GSM_INTERNAL=@GSM_INTERNAL@
+GSM_INCLUDE=@GSM_INCLUDE@
+GSM_LIB=@GSM_LIB@
+
+GTK_INCLUDE=@GTK_INCLUDE@
+GTK_LIB=@GTK_LIB@
+
+GTK2_INCLUDE=@GTK2_INCLUDE@
+GTK2_LIB=@GTK2_LIB@
+
+ICONV_INCLUDE=@ICONV_INCLUDE@
+ICONV_LIB=@ICONV_LIB@
+
+IKSEMEL_INCLUDE=@IKSEMEL_INCLUDE@
+IKSEMEL_LIB=@IKSEMEL_LIB@
+
+IMAP_TK_INCLUDE=@IMAP_TK_INCLUDE@
+IMAP_TK_LIB=@IMAP_TK_LIB@
+
+JACK_INCLUDE=@JACK_INCLUDE@
+JACK_LIB=@JACK_LIB@
+
+LUA_INCLUDE=@LUA_INCLUDE@
+LUA_LIB=@LUA_LIB@
+
+NBS_INCLUDE=@NBS_INCLUDE@
+NBS_LIB=@NBS_LIB@
+
+NCURSES_INCLUDE=@NCURSES_INCLUDE@
+NCURSES_LIB=@NCURSES_LIB@
+NCURSES_DIR=@NCURSES_DIR@
+
+NETSNMP_INCLUDE=@NETSNMP_INCLUDE@
+NETSNMP_LIB=@NETSNMP_LIB@
+
+NEWT_INCLUDE=@NEWT_INCLUDE@
+NEWT_LIB=@NEWT_LIB@
+
+OGG_INCLUDE=@OGG_INCLUDE@
+OGG_LIB=@OGG_LIB@
+
+OSPTK_INCLUDE=@OSPTK_INCLUDE@
+OSPTK_LIB=@OSPTK_LIB@
+
+# ossaudio can optionally use ffmpeg, x11, sdl and sdl_image.
+# Because sdl_image in turn depends on sdl, we don't duplicate the include
+OSSAUDIO_INCLUDE=@OSS_INCLUDE@ @FFMPEG_INCLUDE@ @SDL_INCLUDE@ @X11_INCLUDE@
+OSSAUDIO_LIB=@OSS_LIB@ @FFMPEG_LIB@ @SDL_LIB@ @SDL_IMAGE_LIB@ @X11_LIB@
+
+PGSQL_INCLUDE=@PGSQL_INCLUDE@
+PGSQL_LIB=@PGSQL_LIB@
+
+POPT_INCLUDE=@POPT_INCLUDE@
+POPT_LIB=@POPT_LIB@
+
+PORTAUDIO_INCLUDE=@PORTAUDIO_INCLUDE@
+PORTAUDIO_LIB=@PORTAUDIO_LIB@
+
+PRI_INCLUDE=@PRI_INCLUDE@
+PRI_LIB=@PRI_LIB@
+
+SS7_INCLUDE=@SS7_INCLUDE@
+SS7_LIB=@SS7_LIB@
+
+PWLIB_INCLUDE=@PWLIB_INCLUDE@
+PWLIB_LIB=@PWLIB_LIB@
+
+RADIUS_INCLUDE=@RADIUS_INCLUDE@
+RADIUS_LIB=@RADIUS_LIB@
+
+FFMPEG_INCLUDE=@FFMPEG_INCLUDE@
+FFMPEG_LIB=@FFMPEG_LIB@
+
+X11_INCLUDE=@X11_INCLUDE@
+X11_LIB=@X11_LIB@
+
+SDL_INCLUDE=@SDL_INCLUDE@
+SDL_LIB=@SDL_LIB@
+
+SDL_IMAGE_INCLUDE=@SDL_IMAGE_INCLUDE@
+SDL_IMAGE_LIB=@SDL_IMAGE_LIB@
+
+SPEEX_INCLUDE=@SPEEX_INCLUDE@
+SPEEX_LIB=@SPEEX_LIB@
+
+SPEEXDSP_INCLUDE=@SPEEXDSP_INCLUDE@
+SPEEXDSP_LIB=@SPEEXDSP_LIB@
+
+SQLITE_INCLUDE=@SQLITE_INCLUDE@
+SQLITE_LIB=@SQLITE_LIB@
+
+SQLITE3_INCLUDE=@SQLITE3_INCLUDE@
+SQLITE3_LIB=@SQLITE3_LIB@
+
+SSL_INCLUDE=@OPENSSL_INCLUDE@
+SSL_LIB=@OPENSSL_LIB@
+
+CRYPTO_INCLUDE=@CRYPTO_INCLUDE@
+CRYPTO_LIB=@CRYPTO_LIB@
+
+TONEZONE_INCLUDE=@TONEZONE_INCLUDE@
+TONEZONE_LIB=@TONEZONE_LIB@
+
+USB_INCLUDE=@USB_INCLUDE@
+USB_LIB=@USB_LIB@
+
+UNIXODBC_INCLUDE=@UNIXODBC_INCLUDE@
+UNIXODBC_LIB=@UNIXODBC_LIB@
+
+VORBIS_INCLUDE=@VORBIS_INCLUDE@
+VORBIS_LIB=@VORBIS_LIB@
+
+VPBAPI_INCLUDE=@VPB_INCLUDE@
+VPBAPI_LIB=@VPB_LIB@
+
+ZAPTEL_INCLUDE=@ZAPTEL_INCLUDE@
+
+ZLIB_INCLUDE=@ZLIB_INCLUDE@
+ZLIB_LIB=@ZLIB_LIB@
+
+ISDNNET_INCLUDE=@ISDNNET_INCLUDE@
+ISDNNET_LIB=@ISDNNET_LIB@
+
+MISDN_INCLUDE=@MISDN_INCLUDE@
+MISDN_LIB=@MISDN_LIB@
+
+SUPPSERV_INCLUDE=@SUPPSERV_INCLUDE@
+SUPPSERV_LIB=@SUPPSERV_LIB@
+
+CAP_LIB=@CAP_LIB@
+CAP_INCLUDE=@CAP_INCLUDE@
+
+BKTR_INCLUDE=@BKTR_INCLUDE@
+BKTR_LIB=@BKTR_LIB@
+
+TERMCAP_INCLUDE=@TERMCAP_INCLUDE@
+TERMCAP_LIB=@TERMCAP_LIB@
+TERMCAP_DIR=@TERMCAP_DIR@
+
+TINFO_INCLUDE=@TINFO_INCLUDE@
+TINFO_LIB=@TINFO_LIB@
+TINFO_DIR=@TINFO_DIR@
+
+# if poll is not present, let the makefile know.
+POLL_AVAILABLE=@HAS_POLL@
diff --git a/trunk/missing b/trunk/missing
new file mode 100755
index 000000000..22e101ab1
--- /dev/null
+++ b/trunk/missing
@@ -0,0 +1,198 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+# Copyright (C) 1996, 1997, 2001, 2002 Free Software Foundation, Inc.
+# Franc,ois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# 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, 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.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.in; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+case "$1" in
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]"
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing - GNU libit 0.0"
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+ aclocal*)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`acinclude.m4' or \`$configure_ac'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`$configure_ac'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`acconfig.h' or \`$configure_ac'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' $configure_ac`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case "$f" in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake*)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`$configure_ac'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ bison|yacc)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f y.tab.h ]; then
+ echo >y.tab.h
+ fi
+ if [ ! -f y.tab.c ]; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex|flex)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f lex.yy.c ]; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ makeinfo)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+ if test -z "$file"; then
+ file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
+ fi
+ touch $file
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and you do not seem to have it handy on your
+ system. You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequirements for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/trunk/mkinstalldirs b/trunk/mkinstalldirs
new file mode 100755
index 000000000..6b3b5fc5d
--- /dev/null
+++ b/trunk/mkinstalldirs
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id$
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp"
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/trunk/pbx/Makefile b/trunk/pbx/Makefile
new file mode 100644
index 000000000..731057b1d
--- /dev/null
+++ b/trunk/pbx/Makefile
@@ -0,0 +1,32 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for PBX modules
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=pbx
+MENUSELECT_CATEGORY=PBX
+MENUSELECT_DESCRIPTION=PBX Modules
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ LIBS+= -lres_ael_share.so -lres_monitor.so
+endif
+
+clean::
+ rm -f ael/*.o
+
+dundi-parser.o: dundi-parser.h
+dundi-parser.o: ASTCFLAGS+=-I.
+
+$(if $(filter pbx_dundi,$(EMBEDDED_MODS)),modules.link,pbx_dundi.so): dundi-parser.o
diff --git a/trunk/pbx/ael/ael-test/ael-ntest10/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest10/extensions.ael
new file mode 100644
index 000000000..4a8386ccf
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest10/extensions.ael
@@ -0,0 +1,131 @@
+macro endsess()
+{
+ NoOp(hithere);
+}
+
+macro nullchk(type)
+{
+ NoOp(${type} is this);
+}
+
+macro endcall(type) {
+ switch(${type}) {
+ case out:
+ &nullchk(callid);
+ if(${testnotnull}) {
+ &endsess();
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ else {
+ptr1: // <-- valid label
+ Softhangup(${CHANNEL});
+ break ;
+ }
+ Noop(esac) ;
+ }
+}
+
+macro endcall2(type) {
+ switch(${type}) {
+ case out:
+ &nullchk(callid);
+ if(${testnotnull}) {
+ &endsess();
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ case out2:
+ {
+ptr1: // <-- valid label
+ Softhangup(${CHANNEL});
+ break ;
+ }
+ Noop(esac) ;
+ }
+}
+
+macro endcall3(type) {
+ switch(${type}) {
+ case out:
+ &nullchk(callid);
+ if(${testnotnull}) {
+ &endsess();
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ Noop(esac) ;
+ }
+ if(${testnotnull}) {
+ goto ptr1;
+ }
+ switch(${type}) {
+ case out:
+ if(${testnotnull}) {
+ptr1: // <-- valid label
+ Softhangup(${CHANNEL});
+ break ;
+ }
+ Noop(esac) ;
+ }
+}
+
+macro endcall4(type) {
+ switch(${type}) {
+ case out:
+ &nullchk(callid);
+ if(${testnotnull}) {
+ &endsess();
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ Noop(esac) ;
+ }
+ if(${testnotnull}) {
+ goto ptr1;
+ }
+ switch(${type}) {
+ case out:
+ switch(${type})
+ {
+ case in:
+ if(${testnotnull}) {
+ptr1: // <-- valid label
+ Softhangup(${CHANNEL});
+ break ;
+ }
+ Noop(esac) ;
+ }
+ }
+}
+
+macro endcall5(type) {
+ switch(${type}) {
+ case out:
+ &nullchk(callid);
+ if(${testnotnull}) {
+ &endsess();
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ case in:
+ &nullchk(callid);
+ ptr2:
+ if(${testnotnull}) {
+ &endsess();
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ Noop(esac) ;
+ }
+ if(${testnotnull}) {
+ goto ptr1;
+ }
+ switch(${type}) {
+ case out:
+ switch(${type})
+ {
+ case in:
+ if(${testnotnull}) {
+ptr1: // <-- valid label
+ Softhangup(${CHANNEL});
+ break ;
+ }
+ Noop(esac) ;
+ }
+ }
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest12/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest12/extensions.ael
new file mode 100644
index 000000000..1e3183358
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest12/extensions.ael
@@ -0,0 +1,13 @@
+context test1
+{
+ 771 => {
+ for( i=0;
+ ${i} <= 3;
+ i = ${i} + 1 )
+ NoOp(i is '${i}');
+ }
+ 772 => {
+ for(i=0; ${i} <= 3;i= ${i} + 1 )
+ NoOp(i is '${i}');
+ }
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest22/extensions.ael
new file mode 100644
index 000000000..b787f4b03
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/extensions.ael
@@ -0,0 +1,7 @@
+#include "t1/*.ael"
+
+context z
+{
+ 123 => NoOp(hi there, z);
+ 124 => NoOp(hi there, z);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/qq.ael b/trunk/pbx/ael/ael-test/ael-ntest22/qq.ael
new file mode 100644
index 000000000..c446f53fc
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/qq.ael
@@ -0,0 +1,6 @@
+
+
+context qq
+{
+ 567 => NoOp(hi there, qq);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t1/a.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t1/a.ael
new file mode 100644
index 000000000..62e3fc588
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t1/a.ael
@@ -0,0 +1,4 @@
+context a
+{
+ 134 => NoOp(hi there, a);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t1/b.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t1/b.ael
new file mode 100644
index 000000000..29d8d1ff4
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t1/b.ael
@@ -0,0 +1,6 @@
+
+
+context b
+{
+ 456 => NoOp(hithere, b);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t1/c.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t1/c.ael
new file mode 100644
index 000000000..3c6df4bde
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t1/c.ael
@@ -0,0 +1,13 @@
+
+
+context c
+{
+ 567 => NoOp(hi there, c);
+}
+
+#include "t2/*.ael"
+
+context w
+{
+ 890 => NoOp(hi there, w);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t2/d.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t2/d.ael
new file mode 100644
index 000000000..6362278f7
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t2/d.ael
@@ -0,0 +1,4 @@
+context d
+{
+ 134 => NoOp(hi there, d);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t2/e.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t2/e.ael
new file mode 100644
index 000000000..9465c8b4e
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t2/e.ael
@@ -0,0 +1,6 @@
+
+
+context e
+{
+ 456 => NoOp(hithere, e);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t2/f.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t2/f.ael
new file mode 100644
index 000000000..ba15a6389
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t2/f.ael
@@ -0,0 +1,9 @@
+#include "qq.ael"
+
+context f
+{
+ 567 => NoOp(hi there, f);
+}
+
+#include "t3/*.ael"
+
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/g.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/g.ael
new file mode 100644
index 000000000..0f1ecc805
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/g.ael
@@ -0,0 +1,4 @@
+context g
+{
+ 134 => NoOp(hi there, g);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/h.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/h.ael
new file mode 100644
index 000000000..f9e3ca89f
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/h.ael
@@ -0,0 +1,6 @@
+
+
+context h
+{
+ 456 => NoOp(hithere, h);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/i.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/i.ael
new file mode 100644
index 000000000..5639a1e98
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/i.ael
@@ -0,0 +1,4 @@
+context i
+{
+ 134 => NoOp(hi there, i);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest22/t3/j.ael b/trunk/pbx/ael/ael-test/ael-ntest22/t3/j.ael
new file mode 100644
index 000000000..8dfc6c05f
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest22/t3/j.ael
@@ -0,0 +1,6 @@
+
+
+context j
+{
+ 567 => NoOp(hi there, j);
+}
diff --git a/trunk/pbx/ael/ael-test/ael-ntest9/extensions.ael b/trunk/pbx/ael/ael-test/ael-ntest9/extensions.ael
new file mode 100755
index 000000000..b9762ed54
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-ntest9/extensions.ael
@@ -0,0 +1,12 @@
+
+context workext {
+ ignorepat => 8;
+ ignorepat => 9;
+ 793 => {
+ Set(QUERYSTRING=SELECT\ foo\,\ bar\ FROM\ foobar);
+ Verbose(2|${QUERYSTRING});
+ query="SELECT foo\, bar FROM foobar" ;
+ Verbose(2|${query}) ;
+ }
+}
+
diff --git a/trunk/pbx/ael/ael-test/ael-test1/extensions.ael b/trunk/pbx/ael/ael-test/ael-test1/extensions.ael
new file mode 100644
index 000000000..e1943f67c
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test1/extensions.ael
@@ -0,0 +1,163 @@
+
+macro testdial(number, timeout) {
+ Dial(IAX2/vpconnect-t02/${number},${timeout},${OG_DIAL_FLAGS});
+ switch (${DIALSTATUS}) {
+ case CHANUNAVAIL:
+ goto dial-trunk2;
+ break;
+ default:
+ NoOp(t02 Unavailable - ${DIALSTATUS});
+ return;
+ }
+
+dial-trunk2:
+ Dial(IAX2/vpconnect-t01/${number},${timeout},${OG_DIAL_FLAGS});
+
+}
+
+macro exten-gen(name,pword)
+{
+ if( ${DB_EXISTS(org/${GroupID}/${name}/secret)} = 0 )
+ goto other|nomatch|begin;
+ if( ${DB(org/${GroupID}/${name}/secret)}foo != ${pword}foo )
+ goto other|nomatch|begin;
+
+};
+
+context what {
+ who =>
+ {
+ random(51) NoOp(This should appear 51% of the time);
+
+ random( 60 )
+ {
+ NoOp( This should appear 60% of the time );
+ }
+ else
+ {
+ random(75)
+ {
+ NoOp( This should appear 30% of the time! );
+ }
+ else
+ {
+ NoOp( This should appear 10% of the time! );
+ }
+ }
+ }
+}
+
+context other {
+ nomatch => {
+ begin:
+ NoOp(Hello!);
+ switch(${DIALSTATUS})
+ {
+ case BUSY:
+ NoOp(wow);
+ case TORTURE:
+ NoOp(woow);
+ };
+ NoOp(woohoo);
+ };
+};
+
+context testloop {
+ includes {
+ other|16:00-23:59|m0n-fri|*|*;
+ };
+
+ 1 => {
+ for (x=0; ${x} < 3; x=${x} + 1) {
+ Verbose(x is ${x} !);
+ if( ${x} = 1 )
+ continue;
+ if( ${x} = 2 )
+ break;
+ };
+ ifTime(14:00-25:00|sat-sun|*|*) {
+ BackGround(Hello);
+ } else
+ BackGround(Sorry);
+ NoOp(This is a totally useless NOOP);
+ };
+ 2 => {
+ y=10;
+ while (${y} >= 0) {
+ Verbose(y is ${y} !);
+ if( ${y} = 1 )
+ continue;
+ if( ${y} = 2 )
+ break;
+ if( ${y} = 3 )
+ return;
+ y=${y}-1;
+ };
+ };
+ regexten hint(nasty/Thingy&nasty/Thingamabob) 3 => {
+ for (x=0; ${x} < 3; x=${x} + 1)
+ {
+ Verbose(x is ${x} !);
+ if( ${x} = 4 )
+ break;
+ if( ${x} = 5 )
+ continue;
+ if( ${x} = 6 )
+ return;
+
+ y=10;
+ while (${y} >= 0)
+ {
+ Verbose(y is ${y} !);
+ if( ${y} = 4 )
+ break;
+ if( ${y} = 5 )
+ continue;
+ if( ${y} = 6 )
+ return;
+ y=${y}-1;
+ };
+ };
+ };
+ 4 => {
+ y=10;
+ while (${y} >= 0)
+ {
+ Verbose(y is ${y} !);
+ if( ${y} = 4 )
+ break;
+ if( ${y} = 5 )
+ continue;
+ if( ${y} = 6 )
+ return;
+ for (x=0; ${x} < 3; x=${x} + 1)
+ {
+ Verbose(x is ${x} !);
+ if( ${x} = 4 )
+ break;
+ if( ${x} = 5 )
+ continue;
+ if( ${x} = 6 )
+ return;
+ for (z=0; ${z} < 17; z=${z} + 1)
+ {
+ Verbose(z is ${z} !);
+ Verbose(z is ${z} !);
+ if( ${z} = 4 )
+ break;
+ if( ${z} = 5 )
+ continue;
+ if( ${z} = 6 )
+ return;
+ Verbose(z is ${z} !);
+ Verbose(z is ${z} !);
+ };
+
+ };
+ y=${y}-1;
+ };
+ };
+ 5 => {
+ &exten-gen(axel,brain);
+ };
+};
diff --git a/trunk/pbx/ael/ael-test/ael-test11/extensions.ael b/trunk/pbx/ael/ael-test/ael-test11/extensions.ael
new file mode 100644
index 000000000..a6b2226f8
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test11/extensions.ael
@@ -0,0 +1,57 @@
+context test1
+{
+ s =>
+ {
+ goto lab1;
+ if( ${testnotnull} )
+ {
+ lab1:
+ NoOp(hello);
+ }
+ else
+ {
+ lab1:
+ MoOp(goodbye);
+ }
+ }
+
+ 1 =>
+ {
+ lab1:
+ NoOp(This one is OK.);
+ }
+}
+
+macro endcall5(type) {
+ switch(${type}) {
+ case out:
+ if(${testnotnull}) {
+ NoOp(whoosh);
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ case in:
+ ptr1: // The First label is the valid one...
+ if(${testnotnull}) {
+ NoOp(wow);
+ goto ptr1 ; // <-- goto call to valid label
+ }
+ Noop(esac) ;
+ }
+ if(${testnotnull}) {
+ goto ptr1;
+ }
+ switch(${type}) {
+ case out:
+ switch(${type})
+ {
+ case in:
+ if(${testnotnull}) {
+ptr1: // <-- duplicate label (macros are about the equiv of an extension)
+ Softhangup(${CHANNEL});
+ break ;
+ }
+ Noop(esac) ;
+ }
+ }
+ return;
+}
diff --git a/trunk/pbx/ael/ael-test/ael-test14/extensions.ael b/trunk/pbx/ael/ael-test/ael-test14/extensions.ael
new file mode 100644
index 000000000..20d69134f
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test14/extensions.ael
@@ -0,0 +1,20 @@
+context test1
+{
+ 10 => {
+ // nothing but a comment!
+ }
+
+ 11 => {
+ switch(${somevar})
+ {
+ case somecase:
+ // nothing but a comment!
+ break;
+ case somecase:
+ // nothing but a comment!
+ continue;
+ }
+ break;
+ }
+
+}
diff --git a/trunk/pbx/ael/ael-test/ael-test15/extensions.ael b/trunk/pbx/ael/ael-test/ael-test15/extensions.ael
new file mode 100644
index 000000000..c9cfdab96
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test15/extensions.ael
@@ -0,0 +1,17 @@
+/* and some comments
+ would make a nice touch */
+
+context t1
+{
+ /* this a test of block comments */
+
+ _15x => {
+ /* more comments
+ across several lines
+ * what do you think*
+ */
+ }
+
+}
+
+/* amd some more */
diff --git a/trunk/pbx/ael/ael-test/ael-test16/extensions.ael b/trunk/pbx/ael/ael-test/ael-test16/extensions.ael
new file mode 100644
index 000000000..5f3b2e4e9
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test16/extensions.ael
@@ -0,0 +1,4 @@
+context real-small {
+
+}
+
diff --git a/trunk/pbx/ael/ael-test/ael-test18/extensions.ael b/trunk/pbx/ael/ael-test/ael-test18/extensions.ael
new file mode 100644
index 000000000..ee03d5909
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test18/extensions.ael
@@ -0,0 +1,40 @@
+context default
+{
+
+706/3077610011 => {
+ JabberStatus(asterisk|jmls@mike,StatusCode);
+
+ switch(${StatusCode}) {
+ case 1:
+ Dial(SIP/706,12);
+ switch(${DIALSTATUS}) {
+ case BUSY:
+ Voicemail(b706);
+ break;
+ default:
+ Voicemail(u706);
+ };
+ BackGround(hello);
+ break;
+ default:
+ Voicemail(u706);
+ };
+ ifTime(3:00-13:00|*|*|*)
+ {
+ NoOp(hello);
+ label1:
+ NoOp(goodbye);
+ }
+ else
+ {
+ NoOp(hithere);
+ label2:
+ NoOp(whatonearth?);
+ }
+ goto label1;
+ goto label2;
+ Hangup();
+ };
+
+}
+
diff --git a/trunk/pbx/ael/ael-test/ael-test19/extensions.ael b/trunk/pbx/ael/ael-test/ael-test19/extensions.ael
new file mode 100644
index 000000000..07af91482
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test19/extensions.ael
@@ -0,0 +1,377 @@
+context dialextens
+{
+ /*
+ 101 thru 123, 149 thru 152
+ */
+ _10X => Dial(Zap/${EXTEN:2},30,Ttw);
+ _1ZX => Dial(Zap/${EXTEN:1},30,Ttw);
+}
+/*
+ Due to extenal wiring:
+
+ dialing 125 will ring 101
+ dialing 126 will ring 102
+ and so on until
+ dialing 147 will ring 123
+
+We can dial out on zap 69 thru 72; and 25-47
+
+*/
+
+context dialthrus
+{
+ /* 369-372; 325-347 */
+ _3XX => Dial(Zap/${EXTEN:1},30,Ttw);
+}
+
+context t1incoming
+{
+ includes
+ {
+ dialextens;
+ parkedcalls;
+ }
+ s => {
+ Answer();
+ Background(welcome-to-test-machine);
+ }
+
+}
+
+context t1extension
+{
+ includes
+ {
+ dialextens;
+ dialthrus;
+ }
+
+}
+
+context incoming
+{
+ includes
+ {
+ dialextens;
+ parkedcalls;
+ }
+ s => {
+ Answer();
+ Background(welcome-to-test-machine);
+ }
+}
+
+context incoming
+{
+ s => {
+ Answer();
+ }
+}
+
+macro std-priv-exten( dev, ext , timeout, opts, torcont, dontcont )
+{
+ // &increment_chosecount();
+ dial_again:
+ Dial(${dev},${timeout},${opts});
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+
+ case BUSY:
+ label_busy:
+ Read(reply|work-exten-busy|1||2|15);
+ if ("${reply}"=="")
+ goto label_busy; // infinite loop if Read has probs!!
+ switch(${reply})
+ {
+ case 1:
+ Set(time1=${EPOCH});
+
+ label_redial:
+
+ WaitMusicOnHold(5);
+ Dial(${dev},${timeout},${opts});
+
+ switch(${DIALSTATUS})
+ {
+ case BUSY:
+ if(${EPOCH}-${time1} >= 20)
+ goto label_busy;
+ goto label_redial;
+
+ default:
+ return;// goto work_line|s|loopback;
+ }
+ break;
+ case 2:
+ Voicemail(${ext}|b);
+ break;
+ case 3:
+ return; // goto work_line|s|loopback;
+ default:
+ Background(invalid);
+ goto label_busy;
+ }
+ break;
+
+ case ANSWER:
+ break;
+
+ case NOANSWER:
+ noanswer_label:
+ Read(reply|work-exten-noanswer|1|skip|2|15);
+ switch(${reply})
+ {
+ case 1:
+ switch(${ext})
+ {
+ case 10:
+ Background(no-cell);
+ break;
+ case 11:
+ // &ciddial(2729495,3072729495,30,tw,${GRAMS_TELCO},${WORK_TELCO});
+ break;
+ case 12:
+ // &ciddial(2725560,3072725560,30,tw,${GRAMS_TELCO},${WORK_TELCO});
+ break;
+ case 13:
+ // &ciddial(2720197,3072720197,30,tw,${GRAMS_TELCO},${WORK_TELCO});
+ break;
+ case 14:
+ // &ciddial(2501174,3072501174,30,tw,${GRAMS_TELCO},${WORK_TELCO});
+ break;
+ case 15:
+ Background(no-cell);
+ break;
+ case 16:
+ Background(no-cell);
+ break;
+ default:
+ Background(invalid);
+ break;
+ }
+ goto noanswer_label;
+ break;
+ case 2:
+ Voicemail(${ext}|u);
+ break;
+ case 3:
+ return; // goto work_line|s|loopback;
+ default:
+ Background(invalid);
+ goto noanswer_label;
+ }
+ Voicemail(${ext}|u);
+ break;
+ default:
+ Voicemail(${ext}|u);
+ }
+}
+/* Putting these 3 funcs in extensions.conf!
+macro funcC(a,b)
+{
+ Set(Key=);
+ menu:
+ Read(Key,main-menu,1,n,1,5);
+ if("${Key}" = "2")
+ goto y,lab1;
+ catch y
+ { lab1:
+ &funcB(${a},${b});
+ }
+}
+
+macro funcB(a,b)
+{
+ Set(Key=);
+ menu:
+ Read(Key,tt-monkeys,1,n,1,5);
+ if("${Key}" = "2")
+ goto z,lab2;
+ catch z
+ { lab2:
+ &funcC(${a},${b});
+ }
+}
+
+macro funcA()
+{
+ &funcB(1,2);
+}
+*/
+
+context extension
+{
+ includes
+ {
+ dialextens;
+ dialthrus;
+ parkedcalls;
+ }
+ 5 => {
+ Record(recording:wav);
+ Background(recording);
+ }
+
+ 81 => {
+ iterations=1000000;
+ Set(time1=${EPOCH});
+ for(i=1; ${i}<${iterations}; i=${i}+1)
+ {
+ NoOp(Hello);
+ }
+ Set(time2=${EPOCH});
+ Verbose(The time diff is $[${time2} - ${time1} ] seconds);
+ Verbose(Which means that the priorities/sec = $[4* ${iterations} / (${time2} - ${time1}) ]);
+ SayNumber($[4 * ${iterations} / (${time2} - ${time1}) ]);
+ }
+ 82 => {
+ &ndeep(100000);
+ Verbose(Finished 100000 levels deep call!);
+ }
+ 83 => {
+ switch (${EXTEN})
+ {
+ pattern 8X:
+ Verbose(do something to prepare it);
+ pattern 9X:
+ Verbose(handle both 1xx and 2xx calls);
+ pattern [4-7]X:
+ Verbose(and this too!);
+ }
+ Set(junky=${RAND(0|99999)});
+ Verbose(Here is a random number: ${junky}.);
+ }
+ 84 => {
+ agi(agi://192.168.134.252/|hello|goodbye|whatever|whoknows,hell2,hello3);
+ }
+ 85 => {
+ &std-priv-exten( Zap/50, 150 , 25, mtw, torcont, dontcont );
+ }
+ 86 => {
+ Verbose(The version is: ${VERSION()} );
+ Verbose(The versionnum is: ${VERSION(ASTERISK_VERSION_NUM)} );
+ Verbose(The user is: ${VERSION(BUILD_USER)} );
+ Verbose(The hostname is: ${VERSION(BUILD_HOSTNAME)} );
+ Verbose(The machine is: ${VERSION(BUILD_MACHINE)} );
+ Verbose(The OS is: ${VERSION(BUILD_OS)} );
+ Verbose(The date is: ${VERSION(BUILD_DATE)} );
+ Verbose(The kernel is: ${VERSION(BUILD_KERNEL)} );
+ Set(vinf=${VERSION()});
+ Set(vrand=${RAND()});
+ if( ${ISNULL(${vinf})} )
+ {
+ if( ${ISNULL(${vrand})} )
+ Verbose(Version 1.2 or earlier);
+ else
+ Verbose(Version 1.4!!!);
+ }
+ else
+ Verbose(${vinf} indicates version pre-1.6 or higher);
+ }
+ 871 => {
+ NoOp( 1 1 1 1 1 1 1);
+ NoOp( 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6);
+ NoOp(012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678890123456789012345678901234567890);
+ NoOp(${EXTEN:1:2} ${EXTEN} ${EXTEN:1} 1 1 1 1 1 1 1);
+ &dialoutpstn(${TDIRECTCALL-PST}/0${EXTEN},${E${CALLERID(num)}-OPT},${TDIRECTCALL-CID},${TDIRECTCALL-MAX},RotaPadrao) ;
+
+ }
+ 872 => {
+ Set(ChannelOnly=${CUT(CHANNEL||1)});
+ Verbose(ChannelOnly=${ChannelOnly}; neat huh?);
+ Set(ChannelOnly=${CUT(CHANNEL,,1)});
+ Verbose(ChannelOnly=${ChannelOnly}; neat huh?);
+ }
+ 873 => {
+ NOOP(this is a forkcdr test);
+ Set(CALLERID(num)=1234567890);
+ Set(CALLERID(name)=before fork);
+ Forkcdr(v);
+ Set(CALLERID(num)=0987654321);
+ Set(CALLERID(name)=after fork);
+ Answer();
+ Echo();
+ Hangup();
+ }
+ 874 => {
+ SayDigits(307-754-5675);
+ SayPhoneNumber(307-754-5675);
+ SayDigits(--);
+ SayPhoneNumber(123-456-7890);
+ SayDigits(++);
+ SayPhoneNumber(307-754-4454);
+ }
+ 875 => {
+ &funcA();
+ &funcD();
+ }
+ 876 => {
+ NoOp(Query resultid ${connid} SELECT var1\, var2 FROM did);
+ NoOp($["Query resultid ${connid} SELECT var1\, var2 FROM did"]);
+ NoOp($["Query resultid ${connid} SELECT var1, var2 FROM did"]);
+ goto test5,s,1;
+ }
+ 88 => {
+ SET(LIMIT_PLAYAUDIO_CALLER=yes);
+ SET(LIMIT_PLAYAUDIO_CALLEE=no);
+ SET(LIMIT_TIMEOUT_FILE=timeup);
+ SET(LIMIT_CONNECT_FILE=limit60);
+ SET(LIMIT_WARNING_FILE=almostup);
+ Dial(Zap/51,20,L(60000:30000:8000));
+ }
+ 89 => {
+ goto callbackmenu|100|1;
+ }
+}
+
+context income1
+{
+ s => {
+ Answer();
+ Dial(Zap/50,20,m);
+ }
+ 150 => Dial(Zap/50,20,m);
+}
+
+context callbackmenu
+{
+ _X. => {
+ Answer();
+ Wait(1);
+ Set(TIMEOUT(digit)=5);
+ Set(TIMEOUT(response)=30);
+ DISA(no-password,callbackdialout);
+ }
+}
+
+context callbackdialout
+{
+ _X. => {
+ Dial(Zap/51,20,w);
+ }
+
+}
+
+
+macro dialoutpstn(something1, something2, something3, something4, something5)
+{
+ Verbose(${something1}--- ${something2}--- ${something3}--- ${something4}--- ${something5});
+}
+
+macro ndeep(level)
+{
+ if( ${level} == 0)
+ {
+ Verbose(2|Got to Level 0);
+ return;
+ }
+ &ndeep($[${level}-1]);
+ return;
+}
diff --git a/trunk/pbx/ael/ael-test/ael-test2/apptest.ael2 b/trunk/pbx/ael/ael-test/ael-test2/apptest.ael2
new file mode 100644
index 000000000..c477d8531
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test2/apptest.ael2
@@ -0,0 +1,146 @@
+// this is a quick test to see how many of the apps we can spot j options in
+// include this in a macro or extension
+// at this moment, there are 18 apps that accept the j option.
+
+ AddQueueMember(zork,iface,20,j);
+ ADSIProg(sfile);
+ AgentCallbackLogin(agent,s,30@cont);
+ AgentLogin(agent,s);
+ AgentMonitorOutgoing(dcn);
+ AGI(whatever);
+ AlarmReceiver();
+ Answer(2);
+ AppendCDRUserField(value);
+ Authenticate(pword,adjmr);
+ BackGround(filename,snm,eng);
+ BackgroundDetect(filename,20,2,10);
+ Busy(10);
+ ChangeMonitor(fnamebase);
+ ChanIsAvail(Zap/5,sj);
+ ChanSpy(prefix,bg()qrv);
+ Congestion(5);
+ ControlPlayback(filename,10,6,4,0,5,7,j);
+ DateTime(unixtime,tz,fmt);
+ DBdel(fam/key);
+ DBdeltree(fam);
+ DeadAGI(command);
+ Dial(zap/1,45,A()CdD()fgG()hHjL()m()M()nNoprS()tTwW);
+ Dictate(basedir);
+ Directory(cont,dcont,f);
+ DISA(68986869876,context);
+ DumpChan(verblev);
+ DUNDiLookup(90709780978,context,bj);
+ EAGI(command);
+ Echo();
+ EndWhile();
+ Exec(appname,args);
+ ExecIf(expr,app,data);
+ ExecIfTime(*,*,*,*,appname);
+ ExternalIVR(command,arg1);
+ Festival(text);
+ Flash();
+ ForkCDR(v);
+ GetCPEID();
+ Gosub(cont,exten,priority);
+ GosubIf(cond?label);
+ Goto(cont,exten,prior);
+ GotoIf(cond?t:f);
+ GotoIfTime(*,*,*,*?cont,ext,prior);
+ Hangup();
+ HasNewVoicemail(vmbox,var,j);
+ HasVoicemail(vmbox,var,j);
+ IAX2Provision(template);
+ ICES(xmlconfig);
+ ImportVar(nevar@chann,var);
+ Log(NOTICE,message);
+ LookupBlacklist(j);
+ LookupCIDName();
+ Macro(macro,arg1);
+ MacroExit();
+ MacroIf(expr?etc);
+ MailboxExists(mbox@cont,j);
+ Math(v,2+2);
+ MeetMe(5555,aAbcdDeimMpPqrstTovwxX);
+ MeetMeAdmin(5555,e,user);
+ MeetMeCount(5555,var);
+ Milliwatt();
+ MixMonitor(filename,abv()V()W(),command);
+ Monitor(file.fmt,base,mb);
+ MP3Player(location);
+ MusicOnHold(class);
+ NBScat();
+ NoCDR();
+ NoOp(ignored);
+ Page(Zap/1,dq);
+ Park(exten);
+ ParkAndAnnounce(template,5,238,retcont);
+ ParkedCall(exten);
+ PauseQueueMember(queue,zap,j);
+ Pickup(ext@cont);
+ Playback(file,j);
+ PlayTones(arg);
+ PrivacyManager(3,4,j);
+ Progress();
+ Queue(queuename,dhHnrtTwW,http://www.where.what,over,5);
+ Random(30,cont,ext,pri);
+ Read(var,fname,10,skip,2,5);
+ ReadFile(var=file,10);
+ RealTime(fam,2,val,prefix);
+ RealTimeUpdate(fam,2,val,2,newval);
+ Record(file,2,10,anqst);
+ RemoveQueueMember(queuename,iface,j);
+ ResetCDR(wav);
+ RetryDial(annound,4,2);
+ Return();
+ Ringing();
+ RxFAX(fname,caller);
+ SayAlpha(string);
+ SayDigits(string);
+ SayNumber(digits);
+ SayPhonetic(string);
+ SayUnixTime(unixtime,tz,fmt);
+ SendDTMF(digits,10);
+ SendImage(filename);
+ SendText(text,j);
+ SendURL(URL);
+ Set(a=b);
+ SetAMAFlags();
+ SetCallerID(clid,a);
+ SetCallerPres(allowed_passed_screen);
+ SetCDRUserField(value);
+ SetGlobalVar(var=val);
+ SetMusicOnHold(class);
+ SetTransferCapability(SPEECH);
+ SIPAddHeader(header);
+ SIPDtmfMode(inband,info,rfc);
+ SIPGetHeader(var@headername);
+ SMS(name);
+ SoftHangup(zap/1,a);
+ StackPop();
+ StartMusicOnHold(class);
+ StopMonitor();
+ StopMusicOnHold();
+ StopPlayTones();
+ System(command);
+ TestClient(testid);
+ TestServer();
+ Transfer(zap/1,j);
+ TrySystem(command);
+ TxFAX(filename,caller,debug);
+ UnpauseQueueMember(queuename,iface,j);
+ UserEvent(eventanme,body);
+ Verbose(5,message);
+ VMAuthenticate(mailbox@cont,s);
+ VoiceMail(mailbox@cont,bg()suj);
+ VoiceMailMain(mailbox@cont,pg()s);
+ Wait(2);
+ WaitExten(3,m());
+ WaitForRing(2);
+ WaitForSilence(2,y);
+ WaitMusicOnHold(2);
+ While(expr);
+ Zapateller(answer,5);
+ ZapBarge(channel);
+ ZapRAS(arg);
+ ZapScan(group);
+ ZapSendKeypadFacility();
diff --git a/trunk/pbx/ael/ael-test/ael-test2/extensions.ael b/trunk/pbx/ael/ael-test/ael-test2/extensions.ael
new file mode 100644
index 000000000..176338872
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test2/extensions.ael
@@ -0,0 +1,8 @@
+context test1
+{
+ s =>
+ {
+ #include "apptest.ael2";
+ }
+}
+
diff --git a/trunk/pbx/ael/ael-test/ael-test20/extensions.ael b/trunk/pbx/ael/ael-test/ael-test20/extensions.ael
new file mode 100644
index 000000000..8ec219864
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test20/extensions.ael
@@ -0,0 +1,8 @@
+context interesting {
+ eswitches {
+ Realtime/default@extensions;
+ IAX2/context@${CURSERVER};
+ }
+ 13 => NoOp(LuckyNumber!);
+}
+
diff --git a/trunk/pbx/ael/ael-test/ael-test3/extensions.ael b/trunk/pbx/ael/ael-test/ael-test3/extensions.ael
new file mode 100755
index 000000000..ff1f6aea5
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test3/extensions.ael
@@ -0,0 +1,3184 @@
+globals
+{
+ static=yes;
+ writeprotect=yes;
+ CONSOLE=Console/dsp; // Console interface for demo
+ IAXINFO=murf:tlhfckoct; // IAXtel username/password
+ FWDNUMBER=544788 ; // your calling number
+ FWDCIDNAME="Joe-Worker"; // your caller id
+ FWDPASSWORD=zingledoodle ; // your password
+ FWDRINGS=Zap/6 ; // the phone to ring
+ FWDVMBOX=1 ; // the VM box for this user
+}
+
+macro std-exten( ext , dev )
+{
+ Dial(${dev}/${ext},20);
+ goto privacyManagerFailed|s|begin;
+ switch(${DIALSTATUS})
+ {
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ case ANSWER:
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+ catch a {
+ VoiceMailMain(${ext});
+ }
+}
+
+macro std-priv-exten_1( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_2( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_3( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_4( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_5( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_6( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_7( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_8( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_9( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_10( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_11( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_12( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_13( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_14( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_15( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_16( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_17( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_18( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_19( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_20( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_21( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_22( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_23( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_24( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_25( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_26( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_27( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_28( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_29( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_30( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_31( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_32( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_33( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_34( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_35( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_36( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_37( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_38( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_39( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_40( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_41( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_42( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_43( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_44( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_45( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_46( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_47( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_48( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_49( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_50( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_51( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_52( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_53( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_54( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_55( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_56( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_57( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_58( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_59( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_60( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_61( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_62( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_63( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_64( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_65( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_66( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_67( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_68( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_69( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_70( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_71( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_72( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_73( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+macro fillcidname()
+{
+ if( "${CALLERID(num)}" = "" ) // nothing to work with, quit!!!
+ return;
+ Set(cidn=${DB(cidname/${CALLERID(num)})});
+ if( "${CALLERID(name)}" != "" )
+ {
+ if( ("${cidn}" = "Privacy Manager" & "${CALLERID(name)}" != "Privacy Manager") | "${cidn}" = "" ) // if the entry isn't in the database,
+ // or if an entry exists, and it's "Privacy Manager", empty, (or add other useless possibilities).
+ {
+ Set(DB(cidname/${CALLERID(num)})=${CALLERID(name)}); // then set or override what's in the DB
+ }
+ }
+ // Now, we fill in the callerid info from the incoming entry, if it's stuff worth using
+ // Ignore fundamentally semi-anonymous information from local cell phones
+ // if the db has an entry for this number, and it's not a canned string from a cell phone company
+ if( ( "${cidn}" != "" ) & ( "${CALLERID(name)}" = ""
+ | "${CALLERID(name)}" = "CODY,WY "
+ | "${CALLERID(name)}" = "POWELL,WY "
+ | "${CALLERID(name)}" = "WIRELESS CALLER"
+ | "${CALLERID(name)}" = "SUBSCRIBER,WIRE"
+ | "${CALLERID(name)}" = "CELLULAR ONE"
+ | "${CALLERID(name)}" = "Cellular One Customer"
+ | "${CALLERID(name)}" = "CELLULAR ONE "
+ | "${CALLERID(name)}" = "Privacy Manager"
+ | "${CALLERID(name)}" = "RIVERTON,WY "
+ | "${CALLERID(name)}" = "BASIN,WY "
+ | "${CALLERID(name)}" = "BILLINGS,MT "
+ | "${CALLERID(name)}" = "PROVO,UT "
+ | "${CALLERID(name)}" = "TOLL FREE " ) ) // put stuff in the above, that the phone company tends to put in your callerid,
+ // that you would rather override with DB info
+ // there's no way to guess them all, but you can get the most popular ones...
+ // why cell phones can't do CID like everybody else, ....?
+ {
+ Set(CALLERID(name)=${cidn}); // Override what the phone company provides with what's in the DB for this number.
+ }
+}
+
+macro ciddial(dialnum, lookup, waittime, dialopts, ddev)
+{
+ Set(cidnu=${CALLERID(num)});
+ Set(cidn=${DB(cidname/${lookup})});
+ Set(CALLERID(name)=${cidn});
+ Dial(${ddev}/${dialnum}|${waittime}|${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_voip);
+ CALLERID(num)=7075679201;
+ Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_cell);
+ CALLERID(num)=${cidnu}; // put the original number back
+ Dial(Zap/2/${lookup},${waittime},${dialopts});
+ }
+ }
+}
+
+macro ciddial3(dialnum, lookup, waittime, dialopts, ddev)
+{
+ Set(cidnu=${CALLERID(num)});
+ Set(cidn=${DB(cidname/${lookup})});
+ Set(CALLERID(name)=${cidn});
+ Dial(${ddev}/${dialnum}|${waittime}|${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_cell);
+ Dial(Zap/2/${lookup},${waittime},${dialopts});
+ }
+}
+
+macro ciddial2(dialnum, lookup, waittime, dialopts, ddev) // give priority to tctwest, then the ZAP in emergencies
+{
+ Set(cidn=${DB(cidname/${lookup})});
+ Set(cidnu=${CALLERID(num)});
+ Set(CALLERID(name)=${cidn});
+ Set(CALLERID(num)=7075679201);
+ Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ Set(CALLERID(num)=${cidnu}); // put the original number back
+ BackGround(try_zap);
+ Dial(${ddev}/${dialnum},${waittime}|${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_cell);
+ Dial(Zap/2/${lookup},${waittime},${dialopts});
+ }
+ }
+}
+
+macro callerid-liar()
+{
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/priv-callerintros/LIAR.gsm&);
+ Background(priv-liar); // Script: OOOps! Sorry! I don't allow men with ski masks pulled over their
+ // faces to get in the front door, and unidentified callers won't fair
+ // any better. You entered *MY* phone number. That won't work.
+ // If you are telemarketing, cross me off the list, and don't call again.
+ // If you did this by mistake, forgive my defenses, and call again.
+ // Alternate: (priv-liar2)
+ // Script: You have chosen to try to deceive my system and withold your CallerID,
+ // by entering my own phone number as YOUR CallerID. I find this
+ // offensive because you are being dishonest. I will not do business nor
+ // waste my time talking to anyone who is less than honest and forthcoming.
+ // Take me off your call list and do not call me again.
+ Hangup();
+}
+
+macro callerid-bad()
+{
+ mycid=${CALLERID(num)}:"1([0-9]+)";
+ Set(CALLERID(num)=${mycid});
+ Wait(0);
+}
+
+context privacyManagerFailed {
+ s => {
+ begin:
+ Background(PrivManInstructions); // Script: OOps, that didn't go well. You need to enter *your* area code, and *your* 7 digit
+ // phone number, for a total of 10 digits, or you'll be handed over to the monkeys. Let's
+ // try this again, and hopefully you can get past our front-line defenses!
+ PrivacyManager();
+ if( "${PRIVACYMGRSTATUS}" = "FAILED" )
+ {
+ Background(tt-allbusy);
+ Background(tt-somethingwrong);
+ Background(tt-monkeysintro);
+ Background(tt-monkeys);
+ Background(tt-weasels);
+ Hangup();
+ }
+ else
+ {
+ goto homeline|s|postPriv;
+ }
+ }
+}
+
+// Some comments
+// Some more comments
+
+context homeline {
+ s => {
+ begin:
+ Answer();
+ Set(repeatcount=0);
+ Zapateller(nocallerid);
+ PrivacyManager();
+ if( "${PRIVACYMGRSTATUS}" = "FAILED" )
+ {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/privmanfailed.gsm);
+ &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket);
+ Hangup();
+ return;
+// goto privacyManagerFailed|s|begin;
+ }
+ postPriv:
+ &fillcidname();
+ Set(CONFCIDNA=${CALLERID(name)});
+ Set(CONFCIDNU=${CALLERID(num)});
+ AGI(callall);
+ AGI(submit-announce.agi);
+ if( "${CALLERID(num)}" : "1" )
+ {
+ &callerid-bad();
+ }
+ if( "${CALLERID(num)}" = "7077577685" & "${CALLERID(name)}" : "Privacy Manager" )
+ {
+ &callerid-liar();
+ }
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ Set(lds=${DB(playlds/${CALLERID(num)})});
+ if( "${lds}" = "1" )
+ {
+ SetMusicOnHold(mohlds);
+ }
+ direct=${DB(DirectCall/${CALLERID(num)})};
+ if( "${direct}" != "" & ${direct} != 0 )
+ {
+ verbose(direct is XXX#${direct}XXXX);
+ Playback(greetings/direct); // Welcome to the Murphy residence. This system will automatically try to connect you to...
+ Playback(/var/spool/asterisk/voicemail/default/${direct}/greet);
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/${direct}/greet.wav&);
+ switch(${direct})
+ {
+ case 1: //Steve
+ &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket);
+ goto s|loopback;
+ case 2: //Sonya
+ &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket);
+ goto s|loopback;
+ default: // all the kids
+ Set(z=${direct}-2);
+ goto homeline-kids|${z}|1;
+ }
+ }
+ loopback:
+ ifTime(*|*|20-25|dec)
+ {
+ Playback(greetings/christmas);
+ }
+ else ifTime(*|*|31|dec)
+ {
+ Playback(greetings/newyear);
+ }
+ else ifTime(*|*|1|jan)
+ {
+ Playback(greetings/newyear);
+ }
+ else ifTime(*|*|14|feb)
+ {
+ Playback(greetings/valentines);
+ }
+ else ifTime(*|*|17|mar)
+ {
+ Playback(greetings/stPat);
+ }
+ else ifTime(*|*|31|oct)
+ {
+ Playback(greetings/halloween);
+ }
+ else ifTime(*|mon|15-21|jan)
+ {
+ Playback(greetings/mlkDay);
+ }
+ else ifTime(*|thu|22-28|nov)
+ {
+ Playback(greetings/thanksgiving);
+ }
+ else ifTime(*|mon|25-31|may)
+ {
+ Playback(greetings/memorial);
+ }
+ else ifTime(*|mon|1-7|sep)
+ {
+ Playback(greetings/labor);
+ }
+ else ifTime(*|mon|15-21|feb)
+ {
+ Playback(greetings/president);
+ }
+ else ifTime(*|sun|8-14|may)
+ {
+ Playback(greetings/mothers);
+ }
+ else ifTime(*|sun|15-21|jun)
+ {
+ Playback(greetings/fathers);
+ }
+ else
+ {
+ Playback(greetings/hello); // None of the above? Just a plain hello will do
+ }
+ Background(murphy-homeline-intro1); // Script: Hello-- Welcome to the Murphy's! If you already know what
+ // option you want, you don't have to wait for this entire spiel-- just
+ // have at it.
+ // If you are calling because this number is on a list of some sort, dial 6.
+ // If you want Sonya, dial 1.
+ // If you want one of the kids, dial 2.
+ // If you want Steve, dial 3.
+ // to play with your introduction, dial 5.
+ // If we don't seem to be giving you the time of day, try 7.
+ // Have a good day!
+
+ }
+ 1 => { // Sonya
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/2/greet.wav&);
+ &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 2 => { // Kids
+ goto homeline-kids|s|begin;
+ }
+ 21 => {
+ Dial(IAX2/seaniax,20,T);
+ }
+ 3 => { // Steve
+ &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 4 => { // Voicemail
+ VoicemailMain();
+ goto s|loopback;
+ }
+ 5 => { // play with intro
+ goto home-introduction|s|begin;
+ }
+ 6 => { // Telemarketers
+ goto telemarket|s|begin;
+ }
+ 7 => { // time of day, riddle
+ agi(tts-riddle.agi);
+ Background(gsm/what-time-it-is2);
+ SayUnixTime();
+ goto s|loopback;
+ }
+ 792 => { // Page All
+ goto pageall|s|begin;
+ }
+ 793 => { // check the tone recognition
+ Read(zz,,0,,1,0);
+ SayDigits(${zz});
+ }
+ t => {
+ Set(repeatcount=${repeatcount} + 1);
+ if( ${repeatcount} < 3 )
+ {
+ goto s|loopback; // just loopback isn't enough
+ }
+ Hangup();
+ }
+ i => {
+ Background(invalid);
+ goto s|loopback;
+ }
+ o => {
+ Congestion();
+ }
+ fax => {
+ Dial(Zap/4);
+ }
+}
+
+// Some comments
+// Some more comments
+
+context pageall {
+ s => {
+ begin:
+ AGI(callall);
+ MeetMe(5555,dtqp);
+ MeetMeAdmin(5555,K);
+ Hangup();
+ }
+
+ h => {
+ begin:
+ MeetMeAdmin(5555,K);
+ Background(conf-muted);
+ Hangup();
+ }
+}
+
+// Some comments
+// Some more comments
+
+context add-to-conference {
+ start => {
+ NoCDR();
+ MeetMe(5555,dmqp);
+ }
+ h => {
+ Hangup();
+ }
+}
+
+context home-introduction {
+ s => {
+ begin:
+ Background(intro-options); // Script: To hear your Introduction, dial 1.
+ // to record a new introduction, dial 2.
+ // to return to the main menu, dial 3.
+ // to hear what this is all about, dial 4.
+ }
+ 1 => {
+ Playback(priv-callerintros/${CALLERID(num)});
+ goto s|begin;
+ }
+ 2 => {
+ goto home-introduction-record|s|begin;
+ }
+ 3 => {
+ goto homeline|s|loopback;
+ }
+ 4 => {
+ Playback(intro-intro); // Script:
+ // This may seem a little strange, but it really is a neat
+ // thing, both for you and for us. I've taped a short introduction
+ // for many of the folks who normally call us. Using the Caller ID
+ // from each incoming call, the system plays the introduction
+ // for that phone number over a speaker, just as the call comes in.
+ // This helps the folks
+ // here in the house more quickly determine who is calling.
+ // and gets the right ones to gravitate to the phone.
+ // You can listen to, and record a new intro for your phone number
+ // using this menu.
+ goto s|begin;
+ }
+ t => {
+ goto s|begin;
+ }
+ i => {
+ Background(invalid);
+ goto s|begin;
+ }
+ o => {
+ goto s|begin;
+ }
+}
+
+context home-introduction-record {
+ s => {
+ begin:
+ Background(intro-record-choices); // Script:
+ // If you want some advice about recording your
+ // introduction, dial 1.
+ // otherwise, dial 2, and introduce yourself after
+ // the beep.
+ }
+ 1 => {
+ Playback(intro-record);
+ // Your introduction should be short and sweet and crisp.
+ // Your introduction will be limited to 10 seconds.
+ // This is NOT meant to be a voice mail message, so
+ // please, don't say anything about why you are calling.
+ // After we are done making the recording, your introduction
+ // will be saved for playback.
+ // If you are the only person that would call from this number,
+ // please state your name. Otherwise, state your business
+ // or residence name instead. For instance, if you are
+ // friend of the family, say, Olie McPherson, and both
+ // you and your kids might call here a lot, you might
+ // say: "This is the distinguished Olie McPherson Residence!"
+ // If you are the only person calling, you might say this:
+ // "This is the illustrious Kermit McFrog! Pick up the Phone, someone!!"
+ // If you are calling from a business, you might pronounce a more sedate introduction,like,
+ // "Fritz from McDonalds calling.", or perhaps the more original introduction:
+ // "John, from the Park County Morgue. You stab 'em, we slab 'em!".
+ // Just one caution: the kids will hear what you record every time
+ // you call. So watch your language!
+ // I will begin recording after the tone.
+ // When you are done, hit the # key. Gather your thoughts and get
+ // ready. Remember, the # key will end the recording, and play back
+ // your intro. Good Luck, and Thank you!"
+ goto 2|begin;
+ }
+ 2 => {
+ begin:
+ Background(intro-start);
+ // OK, here we go! After the beep, please give your introduction.
+ Background(beep);
+ Record(priv-callerintros/${CALLERID(num)}:gsm,3);
+ Background(priv-callerintros/${CALLERID(num)});
+ goto home-introduction|s|begin;
+ }
+ t => {
+ goto s|begin;
+ }
+ i => {
+ Background(invalid);
+ goto s|begin;
+ }
+ o => {
+ goto s|begin;
+ }
+}
+
+context homeline-kids {
+ s => {
+ begin:
+ Background(murphy-homeline-kids); // Which Kid? 1=Sean, 2:Eric, 3:Ryan, 4:Kyle, 5:Amber, 6:Alex, 7:Neal
+ }
+ 1 => { // SEAN
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/3/greet.wav&);
+ // &std-priv-exten(Zap/3r2&Zap/5r2,3,35,mtw,telemarket,telemarket);
+ &std-priv-exten(IAX2/seaniax&Zap/5r2,3,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 2 => { // ERIC
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&);
+ Voicemail(u4);
+ goto homeline|s|loopback;
+
+ // SetMusicOnHold(erics);
+ // TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ // TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&);
+ // &std-priv-exten(Zap/3r2&Zap/5r2,4,35,mtw,telemarket,telemarket);
+ // goto homeline|s|loopback;
+ }
+ 3 => { // RYAN
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/5/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,5,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 4 => { // KYLE
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/6/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,6,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 5 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/7/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,7,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+
+ }
+ 6 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/8/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,8,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 7 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/9/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,9,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ t => {
+ goto s|begin;
+ }
+ i => {
+ Background(invalid);
+ goto s|begin;
+ }
+ o => {
+ goto s|begin;
+ }
+}
+
+context voipworkline {
+ s => {
+ begin:
+ Answer();
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ goto workline|s|loopback;
+ }
+ 7075679201 => {
+ Answer();
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ goto workline|s|loopback;
+ }
+}
+
+context workline {
+ s => {
+ begin:
+ Answer();
+ Wait(1);
+ Set(repeatcount=0);
+ Zapateller(nocallerid);
+// PrivacyManager();
+// if( "${PRIVACYMGRSTATUS}" = "FAILED" )
+// {
+// goto privacyManagerFailed|s|begin;
+// }
+ &fillcidname();
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ loopback:
+ Background(greetings/greeting); //script: Hello
+ Background(murphy-office-intro1); //script: welcome to Steve Murphy's office. If you are dialing
+ // this number because it was on a calling list of any sort, dial 6.
+ // Otherwise, dial 1, and hopefully, you will reach Steve.
+ }
+ 1 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/1/greet.wav&);
+
+ &std-priv-exten(Zap/6&Sip/murf,1,30,mtw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 4 => {
+ VoicemailMain();
+ goto s|loopback;
+ }
+ 6 => {
+ goto telemarket|s|begin;
+ }
+ 793 => { // check the tone recognition
+ Read(zz,,0,,1,0);
+ SayDigits(${zz});
+ }
+ t => {
+ repeatcount=${repeatcount} + 1;
+ if( ${repeatcount} < 3 )
+ {
+ goto s|loopback; // just loopback isn't enough
+ }
+ Hangup();
+ }
+ i => {
+ Background(invalid);
+ goto s|loopback;
+ }
+ o => {
+ Congestion();
+ }
+ fax => {
+ Answer();
+ Dial(Zap/4);
+ }
+}
+
+context dialFWD {
+ ignorepat => 8;
+ ignorepat => 9;
+ _83. => {
+ Set(CALLERID(name)=${FWDCIDNAME});
+ Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r);
+ Congestion();
+ }
+ _82NXX => {
+ Set(CALLERID(name)=${FWDCIDNAME});
+ Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r);
+ Congestion();
+ }
+ _92NXX => {
+ Set(CALLERID(name)=${FWDCIDNAME});
+ Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r);
+ Congestion();
+ }
+}
+
+context dialiaxtel {
+ ignorepat => 8;
+ ignorepat => 9;
+ _81700NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+ _81800NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+ _91700NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+ _91800NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+
+}
+
+context dialgoiax {
+ ignorepat => 9;
+ _93. => {
+ Set(CALLERID(name)="Joe Worker");
+ Dial(IAX2/878201007658:stickyfinger295@server1.goiax.com/${EXTEN:2},60,r);
+ Congestion();
+ }
+
+}
+
+context homefirst {
+ ignorepat => 9;
+ _91NXXNXXXXXX => {
+ &ciddial(${EXTEN:1},${EXTEN:2},30,TW,Zap/1);
+ }
+ _9754XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9574XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9202XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9219XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9254XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9716XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9NXXXXXX => {
+ &ciddial(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9011. => {
+ &ciddial(${EXTEN:1},${EXTEN:1},30,TW,Zap/1);
+ }
+ _9911 => {
+ Dial(Zap/1/911,30,T);
+ }
+ _9411 => {
+ Dial(Zap/1/411,30,T);
+ }
+}
+
+context workfirst {
+ ignorepat => 9;
+ _91NXXNXXXXXX => {
+ &ciddial2(${EXTEN:1},${EXTEN:2},30,TW,Zap/1);
+ }
+ _9754XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9574XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9202XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9219XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9254XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9716XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9NXXXXXX => {
+ &ciddial2(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9911 => {
+ Dial(Zap/1/911,30,T);
+ }
+ _9411 => {
+ Dial(Zap/1/411,30,T);
+ }
+}
+
+context force_cell {
+ ignorepat => 8;
+ _81NXXNXXXXXX => {
+ &ciddial(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/2);
+ }
+ _8754XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8574XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8202XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8219XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8254XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8716XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8NXXXXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8911 => {
+ Dial(Zap/1/911|30|T);
+ }
+ _8411 => {
+ Dial(Zap/1/411|30|T);
+ }
+}
+
+context force_home {
+ ignorepat => 8;
+ _81NXXNXXXXXX => {
+ &ciddial3(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/1);
+ }
+ _8754XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8574XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8202XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8219XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8254XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8716XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8NXXXXXX => {
+ &ciddial3(1707${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8911 => {
+ Dial(Zap/1/911|30|T);
+ }
+ _8411 => {
+ Dial(Zap/1/411|30|T);
+ }
+}
+
+context homeext {
+ ignorepat => 8;
+ ignorepat => 9;
+ includes {
+ parkedcalls;
+ homefirst;
+ force_cell;
+ }
+ s => {
+ loopback:
+ Wait(0);
+ }
+ 1 => {
+ &std-priv-exten(Zap/3&Zap/5,2,35,mtw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 2 => {
+ &std-priv-exten(Zap/6&Zap/5,1,35,mpA(beep3)Tt,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 4 => {
+ VoicemailMain();
+ }
+ 5 => {
+ Record(recording:gsm);
+ Background(recording);
+ }
+ 6 => {
+ Background(recording);
+ }
+ 760 => {
+ DateTime();
+ goto s|loopback;
+ }
+ 761 => {
+ Record(announcement:gsm);
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/announcement.gsm&);
+ goto s|loopback;
+ }
+ 762 => {
+ agi(tts-riddle.agi);
+ Background(gsm/what-time-it-is2);
+ SayUnixTime();
+ goto s|loopback;
+ }
+ 763 => {
+ Set(CALLERID(num)=);
+ Dial(Zap/6r3,35,mptA(beep3)); //results: it should ALWAYS ask for an intro; the intro should not be left behind
+ Hangup();
+ }
+ 764 => {
+ Set(CALLERID(num)=);
+ Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; shouldn't anyway if no callerid
+ Hangup();
+ }
+ 765 => {
+ Set(CALLERID(num)=);
+ Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call.
+ Hangup();
+ }
+ 766 => {
+ Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call.
+ Hangup();
+ }
+ 767 => {
+ Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; the interesting case, because callerID should be present.
+ Hangup();
+ }
+ 769 => {
+ Playtones(dial);
+ Wait(2);
+ Playtones(busy);
+ Wait(2);
+ Playtones(ring);
+ Wait(2);
+ Playtones(congestion);
+ Wait(2);
+ Playtones(callwaiting);
+ Wait(2);
+ Playtones(dialrecall);
+ Wait(2);
+ Playtones(record);
+ Wait(2);
+ Playtones(info);
+ Wait(5);
+ Hangup();
+ }
+ 790 => {
+ MeetMe(790,p);
+ }
+ 792 => {
+ goto pageall|s|begin;
+ }
+ 795 => {
+ AGI(wakeup.agi);Congestion();
+ }
+ 544716 => { // Incoming call from FWD
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ goto s|loopback;
+ }
+
+ i => {
+ Background(invalid);
+ goto s|loopback;
+ }
+ o => {
+ goto s|loopback;
+ }
+ t => {
+ Congestion();
+ }
+}
+
+context fromvmhome {
+ 1 => {
+ Dial(Zap/6&Sip/murf|20|Tt);
+ }
+ 2 => {
+ Dial(Zap/3&Zap/5|20|Tt);
+ }
+ _707202XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707219XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707254XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707716XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707754XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707574XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _NXXNXXXXXX => {
+ &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1);
+ }
+ _1NXXNXXXXXX => { // HAND DIALING
+ &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1);
+ }
+ _754XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _574XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _NXXXXXX => {
+ &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _911 => {
+ &ciddial(911,911,30,TW,Zap/1);
+ }
+ _411 => {
+ &ciddial(411,411,30,TW,Zap/1);
+ }
+}
+
+context fromvmwork {
+ 1 => {
+ Dial(Zap/6&Sip/murf|20|Tt);
+ }
+ 2 => {
+ Dial(Zap/3&Zap/5|20|Tt);
+ }
+ _707202XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707219XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707254XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707716XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707754XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707574XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _NXXNXXXXXX => {
+ &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1);
+ }
+ _1NXXNXXXXXX => { // HAND DIALING
+ &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1);
+ }
+ _754XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _574XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _NXXXXXX => {
+ &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ 911 => {
+ &ciddial(911,911,30,TW,Zap/1);
+ }
+ 411 => {
+ &ciddial(411,411,30,TW,Zap/1);
+ }
+}
+
+context fromSeanUniden {
+ includes
+ {
+ parkedcalls;
+ }
+ 21 => {
+ Dial(IAX2/seaniax,20,T);
+ }
+ _707202XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707219XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707254XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707716XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707754XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707574XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _NXXNXXXXXX => {
+ &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1);
+ }
+ _1NXXNXXXXXX => {
+ &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1);
+ }
+ _754XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _574XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _NXXXXXX => {
+ &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ 911 => {
+ &ciddial(911,911,30,TW,Zap/1);
+ }
+ 411 => {
+ &ciddial(411,411,30,TW,Zap/1);
+ }
+}
+
+context workext {
+ ignorepat => 8;
+ ignorepat => 9;
+ includes {
+ parkedcalls;
+ workfirst;
+ force_home;
+ dialFWD;
+ dialiaxtel;
+ dialgoiax;
+ }
+ s => {
+ loopback:
+ Wait(0);
+ }
+ 1 => {
+ Dial(Zap/3&Zap/5,20,tT);
+ }
+ 2 => {
+ Dial(Zap/5&Zap/6,20,tT);
+ }
+ 21 => {
+ Dial(IAX2/seaniax,20,T);
+ }
+ 22 => {
+ Set(CALLERID(num)=1234567890);
+ Set(CALLERID(name)=TestCaller);
+ Dial(Zap/5,20,mP()A(beep)tw);
+ NoOp(here is dialstatus: ${DIALSTATUS}...);
+ goto s|loopback;
+ }
+ 4 => {
+ VoicemailMain();
+ goto s|loopback;
+ }
+ 5 => {
+ Record(recording:gsm);
+ Background(recording);
+ }
+ 6 => {
+ ZapBarge();
+ }
+ 760 => {
+ DateTime();
+ goto s|loopback;
+ }
+ 761 => {
+ ZapBarge();
+ goto s|loopback;
+ }
+ 765 => {
+ Playback(demo-echotest);
+ Echo();
+ Playback(demo-echodone);
+ goto s|loopback;
+ }
+ 766 => {
+ Festival(The other thing to watch is neuro-electronics: the ability to interface technology with our neural system: My wife: Sigrid: has had a cochlear implant since 1996. This once profoundly deaf person now uses the phone: recognizes accents: and listens to movies and recorded books.);
+ goto s|loopback;
+ }
+ 767 => {
+ agi(tts-riddle.agi);
+ Background(gsm/what-time-it-is2);
+ SayUnixTime();
+ goto s|loopback;
+ }
+ 768 => {
+ agi(tts-computer.agi);
+ }
+ 771 => {
+ eagi(eagi-test);
+ agi(my-agi-test);
+ }
+ 772 => {
+ agi(wakeup.agi);
+ }
+ 775 => {
+ if( ${EXTEN}=${EXTEN} )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ if( ${EXTEN}=${LANGUAGE} )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ BackGround(digits/2);
+ }
+ 776 => {
+ Set(TEST=00359889811777);
+ if( ${TEST}= 00359889811777 )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ if( ${TEST}= 00359889811888 )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ Hangup();
+ }
+ 790 => {
+ MeetMe(790,p);
+ }
+ 792 => {
+ goto pageall|s|begin;
+ }
+ 793 => {
+ #include "include1.ael2"
+ }
+ 795 => {
+ AGI(wakeup.agi);
+ Congestion();
+ }
+ 797 => {
+ Set(CONFCIDNA=${CALLERID(name)});
+ Set(CONFCIDNU=${CALLERID(num)});
+ AGI(callall);
+ AGI(submit-announce.agi);
+ Hangup();
+ }
+}
+
+context wakeup {
+ 3 => {
+ Dial(Zap/3|30);
+ }
+ 4 => {
+ Dial(Zap/4|30);
+
+ }
+ 5 => {
+ Dial(Zap/5|30);
+
+ }
+ 6 => {
+ Dial(Zap/6|30);
+
+ }
+ 99 => {
+ Dial(IAX2/murfiaxphone|30);
+ }
+ 97 => {
+ Dial(IAX2/ryaniax|30);
+ }
+ 94 => {
+ Dial(IAX2/seaniax|30);
+ }
+}
+
+context announce-all {
+ s => {
+ begin:
+ MeetMe(5555,dtqp);
+ MeetMeAdmin(5555,K);
+ Hangup();
+ }
+ h => {
+ MeetMeAdmin(5555,K);
+ Hangup();
+ }
+}
+
+// now include the telemarketer torture scripts!
+
+#include "telemarket_torture.ael2"
+
+
diff --git a/trunk/pbx/ael/ael-test/ael-test3/include1.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include1.ael2
new file mode 100644
index 000000000..80c562cb2
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test3/include1.ael2
@@ -0,0 +1,3 @@
+ NoOp(Hello, this is included from include1.ael2);
+ #include "include2.ael2"
+
diff --git a/trunk/pbx/ael/ael-test/ael-test3/include2.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include2.ael2
new file mode 100644
index 000000000..8d892fb0c
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test3/include2.ael2
@@ -0,0 +1,4 @@
+ NoOp(This was included from include2.ael2);
+ #include "include3.ael2"
+ #include "include4.ael2"
+
diff --git a/trunk/pbx/ael/ael-test/ael-test3/include3.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include3.ael2
new file mode 100644
index 000000000..3c6c1e3dd
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test3/include3.ael2
@@ -0,0 +1,2 @@
+ NoOp(This is include3.ael2!);
+ #include "include5.ael2"
diff --git a/trunk/pbx/ael/ael-test/ael-test3/include4.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include4.ael2
new file mode 100644
index 000000000..7d3703a5e
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test3/include4.ael2
@@ -0,0 +1,2 @@
+ NoOp(This is include4.ael2! Isn't it cool!?!?!?!);
+ NoOp(4 doesn't include anything);
diff --git a/trunk/pbx/ael/ael-test/ael-test3/include5.ael2 b/trunk/pbx/ael/ael-test/ael-test3/include5.ael2
new file mode 100644
index 000000000..0e18983ef
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test3/include5.ael2
@@ -0,0 +1 @@
+ NoOp(Include5.ael2 doesn't include anything, either!);
diff --git a/trunk/pbx/ael/ael-test/ael-test3/telemarket_torture.ael2 b/trunk/pbx/ael/ael-test/ael-test3/telemarket_torture.ael2
new file mode 100755
index 000000000..ebd8e9f2f
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test3/telemarket_torture.ael2
@@ -0,0 +1,812 @@
+//
+// AN EXCERSIZE IN BAD DIALPLAN DESIGN
+// (What better testing ground than on telemarketers?)
+//
+
+
+// BAD DESIGN: long, boring introductions followed by long, drawn out menus of choices.
+// if they survive to the last option, how will they remember the choices?
+//
+
+// BAD DESIGN: Amateur Recording. Poor voice quality, too quiet.
+// Also, the announcer is definitely not vocally gifted.
+// Also, the long pauses and clicks between the intro
+// and menu choices might lead some to think that
+// the announcements are over, and hang up. Too bad!
+
+// WORSE DESIGN: Instead of using the Background application, the Playback
+// application is used. After taking so much time and trouble
+// to record this material, the caller must listen and enjoy
+// every syllable before they can make an option choice. None
+// of that interrupting with a choice. We want them to savour
+// every word!
+
+// GOOD/BAD, ER INSIDIOUS -- DANGLE A CARROT-- GIVE THE LISTENER A GOOD REASON TO
+// HANG ON AND VOLUNTARILY LISTEN TO THE TORTURE.
+// BUT, DON'T MAKE PROMISES YOU WON'T KEEP!
+
+
+context telemarket {
+ s => {
+ begin:
+ Playback(telemarketer-intro); // ; Script:
+ // Due to the extremely high volume of calls from everything from telemarketers
+ // to Septic System Bacteria vendors, we are asking all such organizations
+ // to remove this number from their call list, or as need be, to add this
+ // number to their No-Call list, whichever is relevent.
+
+ // [THE CARROT:]
+ // We HAVE made some exceptions, and if you wish to see if your organization
+ // has been exempted, please listen to and follow the following prompts.
+ //
+ // Otherwise, please Cease calling this number!
+ //
+ Playback(telemarketer-choices);
+ // if you represent a charitable organization, please dial 1,
+ // if you represent a political organization, please dial 2.
+ // if you represent a polling company, please dial 3,
+ // if you represent a market research organization, please dial 4.
+ // if you represent a magazine or newsletter, please dial 5.
+ // if you represent a commercial organization, please dial 6.
+ }
+ 1 => goto telemarket-charity|s|begin;
+ 2 => goto telemarket-political|s|begin;
+ 3 => goto telemarket-pollster|s|begin;
+ 4 => goto telemarket-research|s|begin;
+ 5 => goto telemarket-magazine|s|begin;
+ 6 => goto telemarket-commercial|s|begin;
+ 7 => goto telemarket-other|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-charity {
+ s => {
+ begin:
+ Playback(telemark-charity-intro);
+ // We have contributed generously to many worthy causes in the past, and will
+ // continue to do so in the future. But we suspect that such organizatons
+ // have sold our name and phone number to each other until we are now hounded
+ // day and night by literally hundreds of such organizations.
+ // Enough is Enough!
+ //
+ // If we have contributed to your cause in the past, we may, perhaps, be disposed to
+ // do so in the future, at our option,
+ // we give no pledges nor make any commitments here.
+ // Send us material via the post if you feel this necessary
+ // but do not even consider email. Any email or further phone calls from your organization
+ // in the future, will be considered an act of aggression, and we will
+ // blacklist your organization for the rest of our natural lives.
+ //
+ // To see if your organization is exempt from these prohibitions, please
+ // comply with the following options.
+ Playback(telemark-charity-choices);
+ // If your organization is disease or genetic defect related, dial 1,
+ // If your organization is handicap related, dial 2.
+ // If your organization is a police or fireman or other similar support entity, please dial 3.
+ // If your organization is a grade school to high school related
+ // fund raiser or other type of activity, please dial 4.
+ // If your organization is a college or univerity or alumnis organization, please dial 5.
+ // If your organization is animal rights or ecology related organization, please dial 6.
+ // If your organization is a political action or candidate support related, please dial 7.
+ // If your organization is a substance abuse related organization or cause, please dial 8.
+ // And any other charity or tax exempt organization should dial 9.
+ }
+ 1 => goto telemarket-char-disease|s|begin;
+ 2 => goto telemarket-char-handicap|s|begin;
+ 3 => goto telemarket-char-police|s|begin;
+ 4 => goto telemarket-char-school|s|begin;
+ 5 => goto telemarket-char-college|s|begin;
+ 6 => goto telemarket-char-animal|s|begin;
+ 7 => goto telemarket-char-candidate|s|begin;
+ 8 => goto telemarket-char-abuse|s|begin;
+ 9 => goto telemarket-char-other|s|begin;
+// BAD DESIGN: referring all timeouts,invalid choices, etc, back to the root of the menu tree will frustrate users no end!
+// WORSE DESIGN: How about having the user have to push a button to repeat the current menu? When a time out could just
+// automatically do it for the user?
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-char-disease {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-handicap {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-police {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-school {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-college {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-animal {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-candidate {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-abuse {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-other {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-sorry {
+ s => {
+ begin:
+ Playback(telemarket-sorry);
+ // Sorry -- your organization is not exempt. Please stop calling us.
+ // Thank you. goodbye.
+ Hangup();
+ }
+}
+
+
+// BAD DESIGN: Hanging up on your audience, no matter what the outcome, is not a nice thing to do!
+
+context telemarket-exception {
+ s => {
+ begin:
+ Playback(telemarket-success);
+ // Congratulations. Your organization IS exempt. Please call us back,
+ // but this time, just act like a normal caller. Thank you. Goodbye.
+ Hangup();
+ }
+}
+
+
+// BAD DESIGN: Making long cascading menu choices is a nasty thing to do to callers!
+// BAD DESIGN: Putting the most frequently encountered items at the end of a list is also a nasty thing to do!
+
+
+// GOOD DESIGN: All rejection notices use a single context. All Acceptance also. To change a rejection to an
+// acceptance, just change the reference from telemarket-sorry to telemarket-exception
+
+
+context telemarket-political {
+ s => {
+ begin:
+ Playback(telemark-polit-intro);
+ // To see if your organization is exempt from our prohibitions,
+ // please follow the following prompts.
+ // please note that they are not in alphabetical order, and you will have to
+ // give them your full attention.
+ Playback(telemark-polit-choices);
+ // if You represent the America First Party, dial 1.
+ // if You represent the American Party, dial 2.
+ // if You represent the American Heritage Party, dial 3.
+ // if You represent the American Independent Party, dial 4.
+ // if You represent the American Nazi Party, dial 5.
+ // if You represent the Pot Party, dial 6.
+ // if You represent the American Reform Party, dial 7.
+ // if You represent the Christian Falenqist Party of America, dial 8.
+ // all others, please dial 9.
+ }
+ 1 => goto telemarket-poli-Am1st|s|begin;
+ 2 => goto telemarket-poli-American|s|begin;
+ 3 => goto telemarket-poli-AmHer|s|begin;
+ 4 => goto telemarket-poli-AmInd|s|begin;
+ 5 => goto telemarket-poli-AmNaz|s|begin;
+ 6 => goto telemarket-poli-Pot|s|begin;
+ 7 => goto telemarket-poli-AmRef|s|begin;
+ 8 => goto telemarket-poli-CFP|s|begin;
+ 9 => goto telemarket-political2|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-political2 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ // Thank you for your patience, and I congratulate you for your persistence.
+ // Just a few more options!
+ //
+ Playback(telemark-polit2-choices);
+ // if You represent the Communist Party USA, dial 1.
+ // if You represent the Constitution Party, dial 2.
+ // if You represent the Family Values Party, dial 3.
+ // if You represent the Freedom Socialist Party, dial 4.
+ // if You represent the Grass Roots Party, dial 5.
+ // if You represent the Green Party, dial 6.
+ // if You represent the Greens Party, dial 7.
+ // if You represent the Independence Party, dial 8.
+ // all others, goto 9.
+ }
+ 1 => goto telemarket-poli-Communist|s|begin;
+ 2 => goto telemarket-poli-Constit|s|begin;
+ 3 => goto telemarket-poli-FamVal|s|begin;
+ 4 => goto telemarket-poli-FreedSoc|s|begin;
+ 5 => goto telemarket-poli-Grassroot|s|begin;
+ 6 => goto telemarket-poli-Green|s|begin;
+ 7 => goto telemarket-poli-Greens|s|begin;
+ 8 => goto telemarket-poli-Independence|s|begin;
+ 9 => goto telemarket-political3|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-political3 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ Playback(telemark-polit3-choices);
+ // if You represent the Independant American Party, dial 1.
+ // if You represent the Labor Party, dial 2.
+ // if You represent the Libertarian Party, dial 3.
+ // if You represent the Light Party, dial 4.
+ // if You represent the Natural Law Party, dial 5.
+ // if You represent the New Party, dial 6.
+ // if You represent the New Union Party, dial 7.
+ // if You represent the Peace and Freedom Party, dial 8.
+ // all others, hang on, dial 9.
+ }
+ 1 => goto telemarket-poli-IndAm|s|begin;
+ 2 => goto telemarket-poli-Labor|s|begin;
+ 3 => goto telemarket-poli-Liber|s|begin;
+ 4 => goto telemarket-poli-Light|s|begin;
+ 5 => goto telemarket-poli-NatLaw|s|begin;
+ 6 => goto telemarket-poli-New|s|begin;
+ 7 => goto telemarket-poli-NewUn|s|begin;
+ 8 => goto telemarket-poli-PeaceFree|s|begin;
+ 9 => goto telemarket-political4|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-political4 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ Playback(telemark-polit4-choices);
+ // if You represent the Prohibition Party, dial 1.
+ // if You represent the Reform Party, dial 2.
+ // if You represent the Revolution , dial 3.
+ // if You represent the Socialist Party USA, dial 4.
+ // if You represent the Socialist Action Party, dial 5.
+ // if You represent the Socialist Equality Party, dial 6.
+ // if You represent the Socialist Labor Party, dial 7.
+ // if You represent the Socialist Workers Party, dial 8.
+ // all others, hang on, and dial 9.
+ }
+ 1 => goto telemarket-poli-Prohib|s|begin;
+ 2 => goto telemarket-poli-Ref|s|begin;
+ 3 => goto telemarket-poli-Revol|s|begin;
+ 4 => goto telemarket-poli-SocPart|s|begin;
+ 5 => goto telemarket-poli-SocAct|s|begin;
+ 6 => goto telemarket-poli-SocEq|s|begin;
+ 7 => goto telemarket-poli-SocLab|s|begin;
+ 8 => goto telemarket-poli-SocWork|s|begin;
+ 9 => goto telemarket-political5|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-political5 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ Playback(telemark-polit5-choices);
+ // if You represent the Southern Party, dial 1.
+ // if You represent the Southern Independence Party, dial 2.
+ // if You represent the US Pacifist Party, dial 3.
+ // if You represent the We the People Party, dial 4.
+ // if You represent the Workers World Party, dial 5.
+ // if You represent the Democratic Party, dial 6.
+ // if You represent the Republican Party, dial 7.
+ // all others, may dial 8.
+ }
+ 1 => goto telemarket-poli-South|s|begin;
+ 2 => goto telemarket-poli-SoInd|s|begin;
+ 3 => goto telemarket-poli-USPac|s|begin;
+ 4 => goto telemarket-poli-WTP|s|begin;
+ 5 => goto telemarket-poli-WWP|s|begin;
+ 6 => goto telemarket-poli-Democrat|s|begin;
+ 7 => goto telemarket-poli-Repub|s|begin;
+ 8 => goto telemarket-poli-other|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-poli-other {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Repub {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Democrat {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-WWP {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-WTP {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-USPac {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SoInd {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-South {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocWork {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocLab {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocEq {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocAct {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocPart {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Revol {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Ref {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Prohib {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-PeaceFree {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-NewUn {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-New {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-NatLaw {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Light {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Liber {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Labor {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-IndAm {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Independence {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Greens {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Green {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Grassroot {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-FreedSoc {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-FamVal {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Constit {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Communist {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-CFP {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-AmRef {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+// BAD DESIGN: Putting in infinite loops in the menus, whether by design or mistake is not nice!
+context telemarket-poli-Pot {
+ s => {
+ begin:
+ goto telemarket-political|s|begin; // will the Pot Party Guys even notice an infinite loop?
+ }
+}
+
+context telemarket-poli-AmNaz {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-AmInd {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-AmHer {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-American {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Am1st {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+
+context telemarket-pollster {
+ s => {
+ begin:
+ Playback(telemark-poll-intro);
+ // I'm sorry-- We are just not available for doing any polling at the moment. So,
+ // please remove us from your list.
+ goto telemarket-sorry|s|begin;
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-research {
+ s => {
+ begin:
+ Playback(telemark-research-intro);
+ // I'd like to say I'd love to help you with your market survey, but that would be a complete
+ // and total lie. I am not interested in helping you with Market Surveys.
+ //
+ // Please remove me from your call list. It just doesn't pay enough. But Thank you.
+ goto telemarket-sorry|s|begin;
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-magazine {
+ s => {
+ begin:
+ Playback(telemark-mag-choices);
+ // If you are calling to see if I would like a NEW free subscription
+ // to your magazine or newsletter, please dial 1.
+ // If you are calling to see if I want to Renew an existing subscription, please dial 2.
+ // If you are representing some publisher, and want my opinion about something, or are doing
+ // some kind of survey, please dial 3.
+ // If you are calling to verify that some previous caller actually called me, and the
+ // verification information is correct, please dial 4.
+ // and if your call purpose doesn't match any of the above, please dial 5.
+ }
+ 1 => goto telemark-mag-new|s|begin;
+ 2 => goto telemark-mag-renew|s|begin;
+ 3 => goto telemark-mag-survey|s|begin;
+ 4 => goto telemark-mag-verify|s|begin;
+ 5 => goto telemark-mag-other|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-new {
+ s => {
+ begin:
+ Playback(telemark-mag-new);
+ // I'm sorry, I'm maxed out, and the answer is NO.
+ // If you really think I'd LOVE to add your publication to the pile I already get,
+ // Send something via the post. Don't call me.
+ // Thank you. bye.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-renew {
+ s => {
+ begin:
+ Playback(telemark-mag-renew);
+ // So, you want to see if I want to Renew, do you? The answer is most likely "YES".
+ //
+ // But, I will not answer a long list of questions over the phone. Send such
+ // categorization info via the post, and stop bothering me over the phone,
+ // if this is what you want.
+ // Do you need verification information? Normally I opt out of such nonsense, if possible.
+ // If not, use whatever of the following you can:
+ // My birth month is October.
+ // My birthplace is Kigali, in Rwanda, in Afica.
+ // My eye color is orange.
+ // All of these are wonderfully false, but I use them regularly for such purposes. Thank you.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-survey {
+ s => {
+ begin:
+ Playback(telemark-mag-survey);
+ // Sorry, I don't have time to answer survey or opinion questions. Find someone
+ // else to help build your marketing database, I guess. Good Luck.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-verify {
+ s => {
+ begin:
+ Playback(telemark-mag-verify);
+ // If you are calling to verify that your own agents aren't ripping you off,
+ // sorry, I can't help you. I opt out whenever I can, mainly because I'm not
+ // paid enough for this kind of thing. I always lie, and I can't remember
+ // what I might have said. Sorry. Goodbye.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-other {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+
+
+// BAD DESIGN: Is it entrapment, when you lure telemarketers to reveal their contact information,
+// Just so you can report them to the FTC/FCC? If it is, isn't it unethical for them
+// to hide their CID (via Anonymous, usually), to hide their identities from the public?
+
+// BTW -- What telemarketer would be stupid enough to fall for this? I'll bet not a single one!
+// For that matter, what telemarketer will be stupid enough to even enter any of this? I'll bet not a single one!
+// (but it was fun messing around).
+
+context telemarket-commercial {
+ s => {
+ begin:
+ Playback(telemark-comm-intro); // Script: Please leave your name, organization, and phone number, plus
+ // a short description of the purpose of your call, at the prompt.
+ // We will do our best to respond to your call! And, in the mean time,
+ // do not forget to add us to your no-call list!
+ Voicemail(u82);
+ goto telemarket-sorry|s|begin;
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-other {
+ s => {
+ begin:
+ Playback(telemark-other-intro);
+ // Please review the previous menu options, and see if you really don't
+ // fit in one of the previous categories.
+ // If you do not, go ahead, and call me again, and let me know what category
+ // I should have included in the above list. I appreciate this. Thank you much!
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
diff --git a/trunk/pbx/ael/ael-test/ael-test4/apptest.ael2 b/trunk/pbx/ael/ael-test/ael-test4/apptest.ael2
new file mode 100644
index 000000000..c477d8531
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test4/apptest.ael2
@@ -0,0 +1,146 @@
+// this is a quick test to see how many of the apps we can spot j options in
+// include this in a macro or extension
+// at this moment, there are 18 apps that accept the j option.
+
+ AddQueueMember(zork,iface,20,j);
+ ADSIProg(sfile);
+ AgentCallbackLogin(agent,s,30@cont);
+ AgentLogin(agent,s);
+ AgentMonitorOutgoing(dcn);
+ AGI(whatever);
+ AlarmReceiver();
+ Answer(2);
+ AppendCDRUserField(value);
+ Authenticate(pword,adjmr);
+ BackGround(filename,snm,eng);
+ BackgroundDetect(filename,20,2,10);
+ Busy(10);
+ ChangeMonitor(fnamebase);
+ ChanIsAvail(Zap/5,sj);
+ ChanSpy(prefix,bg()qrv);
+ Congestion(5);
+ ControlPlayback(filename,10,6,4,0,5,7,j);
+ DateTime(unixtime,tz,fmt);
+ DBdel(fam/key);
+ DBdeltree(fam);
+ DeadAGI(command);
+ Dial(zap/1,45,A()CdD()fgG()hHjL()m()M()nNoprS()tTwW);
+ Dictate(basedir);
+ Directory(cont,dcont,f);
+ DISA(68986869876,context);
+ DumpChan(verblev);
+ DUNDiLookup(90709780978,context,bj);
+ EAGI(command);
+ Echo();
+ EndWhile();
+ Exec(appname,args);
+ ExecIf(expr,app,data);
+ ExecIfTime(*,*,*,*,appname);
+ ExternalIVR(command,arg1);
+ Festival(text);
+ Flash();
+ ForkCDR(v);
+ GetCPEID();
+ Gosub(cont,exten,priority);
+ GosubIf(cond?label);
+ Goto(cont,exten,prior);
+ GotoIf(cond?t:f);
+ GotoIfTime(*,*,*,*?cont,ext,prior);
+ Hangup();
+ HasNewVoicemail(vmbox,var,j);
+ HasVoicemail(vmbox,var,j);
+ IAX2Provision(template);
+ ICES(xmlconfig);
+ ImportVar(nevar@chann,var);
+ Log(NOTICE,message);
+ LookupBlacklist(j);
+ LookupCIDName();
+ Macro(macro,arg1);
+ MacroExit();
+ MacroIf(expr?etc);
+ MailboxExists(mbox@cont,j);
+ Math(v,2+2);
+ MeetMe(5555,aAbcdDeimMpPqrstTovwxX);
+ MeetMeAdmin(5555,e,user);
+ MeetMeCount(5555,var);
+ Milliwatt();
+ MixMonitor(filename,abv()V()W(),command);
+ Monitor(file.fmt,base,mb);
+ MP3Player(location);
+ MusicOnHold(class);
+ NBScat();
+ NoCDR();
+ NoOp(ignored);
+ Page(Zap/1,dq);
+ Park(exten);
+ ParkAndAnnounce(template,5,238,retcont);
+ ParkedCall(exten);
+ PauseQueueMember(queue,zap,j);
+ Pickup(ext@cont);
+ Playback(file,j);
+ PlayTones(arg);
+ PrivacyManager(3,4,j);
+ Progress();
+ Queue(queuename,dhHnrtTwW,http://www.where.what,over,5);
+ Random(30,cont,ext,pri);
+ Read(var,fname,10,skip,2,5);
+ ReadFile(var=file,10);
+ RealTime(fam,2,val,prefix);
+ RealTimeUpdate(fam,2,val,2,newval);
+ Record(file,2,10,anqst);
+ RemoveQueueMember(queuename,iface,j);
+ ResetCDR(wav);
+ RetryDial(annound,4,2);
+ Return();
+ Ringing();
+ RxFAX(fname,caller);
+ SayAlpha(string);
+ SayDigits(string);
+ SayNumber(digits);
+ SayPhonetic(string);
+ SayUnixTime(unixtime,tz,fmt);
+ SendDTMF(digits,10);
+ SendImage(filename);
+ SendText(text,j);
+ SendURL(URL);
+ Set(a=b);
+ SetAMAFlags();
+ SetCallerID(clid,a);
+ SetCallerPres(allowed_passed_screen);
+ SetCDRUserField(value);
+ SetGlobalVar(var=val);
+ SetMusicOnHold(class);
+ SetTransferCapability(SPEECH);
+ SIPAddHeader(header);
+ SIPDtmfMode(inband,info,rfc);
+ SIPGetHeader(var@headername);
+ SMS(name);
+ SoftHangup(zap/1,a);
+ StackPop();
+ StartMusicOnHold(class);
+ StopMonitor();
+ StopMusicOnHold();
+ StopPlayTones();
+ System(command);
+ TestClient(testid);
+ TestServer();
+ Transfer(zap/1,j);
+ TrySystem(command);
+ TxFAX(filename,caller,debug);
+ UnpauseQueueMember(queuename,iface,j);
+ UserEvent(eventanme,body);
+ Verbose(5,message);
+ VMAuthenticate(mailbox@cont,s);
+ VoiceMail(mailbox@cont,bg()suj);
+ VoiceMailMain(mailbox@cont,pg()s);
+ Wait(2);
+ WaitExten(3,m());
+ WaitForRing(2);
+ WaitForSilence(2,y);
+ WaitMusicOnHold(2);
+ While(expr);
+ Zapateller(answer,5);
+ ZapBarge(channel);
+ ZapRAS(arg);
+ ZapScan(group);
+ ZapSendKeypadFacility();
diff --git a/trunk/pbx/ael/ael-test/ael-test4/extensions.ael b/trunk/pbx/ael/ael-test/ael-test4/extensions.ael
new file mode 100644
index 000000000..838aa2489
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test4/extensions.ael
@@ -0,0 +1,8 @@
+context test1
+{
+ test2 =>
+ {
+ #include "apptest.ael2";
+ }
+}
+
diff --git a/trunk/pbx/ael/ael-test/ael-test5/extensions.ael b/trunk/pbx/ael/ael-test/ael-test5/extensions.ael
new file mode 100644
index 000000000..e4f703b86
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test5/extensions.ael
@@ -0,0 +1,838 @@
+///////////////////////////////////////////////////////////////////////////////
+// Helpdesk Queue
+
+context hd-queue {
+ s => {
+ NoOp(Add a background sound to tell the user their options);
+ Queue(helpdesk|t);
+ NoOp(Put in options to apologize and send user to voicemail);
+ };
+
+ 0 => goto default|0|1;
+ 1 => {
+ Dial(u41950@ixtlchochitl.zvbwu.edu);
+ Congestion(10);
+ Hangup;
+ };
+};
+
+
+context l903-calling {
+ _9903NXXXXXX => {
+ Realtime(l903_ext,exchange,${EXTEN:4:3},l903_);
+ if ("${l903_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+};
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from houston.conf
+// Converted the extension list to the database
+
+context houston-calling {
+ _9713NXXXXXX => {
+ Realtime(hou_713_ext,exchange,${EXTEN:4:3},hou_713_);
+ if ("${hou_713_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+
+ _9281NXXXXXX => {
+ Realtime(hou_281_ext,exchange,${EXTEN:4:3},hou_281_);
+ if ("${hou_281_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+
+ _9832NXXXXXX => {
+ Realtime(hou_832_ext,exchange,${EXTEN:4:3},hou_832_);
+ if ("${hou_832_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from huntsville.conf
+// Converted the extension list to the database
+
+context huntsville-calling {
+ _9NXXXXXX => {
+ Realtime(hv_ext,exchange,${EXTEN:1:3},hv_);
+ if ("${hv_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+
+ _NXXXXXX => {
+ NoOp(Stripping last four to see what extension we're dialing);
+ Set(LAST4=${EXTEN:3});
+ StripLSD(4);
+ };
+
+ i => Playback(pbx-invalid);
+ h => Hangup;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from macros.conf
+
+macro dialout( number ) {
+ Realtime(call_info,exten,${CALLERIDNUM:5},mon_);
+ if ("${mon_monitor}" = "YES") {
+ Dial(SIP/${number}@zgw1.zvbwu.edu,,wW);
+ Dial(SIP/${number}@zgw2.zvbwu.edu,,wW);
+ } else {
+ Dial(SIP/${number}@zgw1.zvbwu.edu);
+ Dial(SIP/${number}@zgw2.zvbwu.edu);
+ };
+ return;
+};
+
+// Standard extension macro:
+// ${ext} - Extension
+macro stdexten( ext ) {
+ Realtime(sipusers,name,${ext},sip_user_);
+ Realtime(call_info,exten|${ext},info_);
+ if ("${sip_user_name}foo" = "foo") {
+ Wait(1);
+ &dialout(${ext});
+ Congestion(10);
+ Hangup;
+ };
+ NoOp(${CALLERIDNUM});
+ RealtimeUpdate(call_info,exten,${ext},calltrace,${CALLERIDNUM});
+ System(/usr/local/bin/db_update.sh call_info calltrace ${CALLERIDNUM} exten ${ext} &);
+ &checkdnd(${ext});
+ &checkcf(${ext});
+ Realtime(call_info,exten,${CALLERIDNUM:5},mon_);
+ if ("${mon_monitor}" = "YES") {
+ Dial(SIP/${info_forwardto},25,wW);
+ } else {
+ Dial(SIP/${info_forwardto},25);
+ };
+ switch ("${DIALSTATUS}") {
+ case "BUSY":
+ &checkcfb(${ext});
+ break;
+ case "CHANUNAVAIL":
+ Dial(IAX2/asterisk:password@ixtlchochitl.zvbwu.edu/${info_forwardto},25,wW);
+ MailboxExists(${ext});
+// if ("${VMBOXEXISTSSTATUS}" = "FAILED") {
+// Congestion(10);
+// Hangup;
+// };
+ &uvm(${ext});
+ Hangup;
+ break;
+ case "CONGESTION":
+ MailboxExists(${ext});
+ if ("${VMBOXEXISTSSTATUS}" = "FAILED") {
+ Congestion(10);
+ Hangup;
+ };
+ &bvm(${ext});
+ Hangup;
+ break;
+ default:
+ MailboxExists(${ext});
+ if ("${VMBOXEXISTSSTATUS}" = "FAILED") {
+ Congestion(10);
+ Hangup;
+ };
+ &uvm(${ext});
+ Hangup;
+ };
+ Hangup;
+};
+
+macro uvm( ext ) {
+ Dial(SIP/u${ext}@ixtlchochitl.zvbwu.edu);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Congestion(10);
+ Hangup;
+};
+
+macro bvm( ext ) {
+ Dial(SIP/b${ext}@ixtlchochitl.zvbwu.edu);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Congestion(10);
+ Hangup;
+};
+
+macro checkdnd( ext ) {
+ if ("${info_donotdisturb}foo" = "foo") {
+ NoOp(Do Not Disturb is not active);
+ } else
+ &uvm(${ext});
+ return;
+};
+
+macro checkcf( ext ) {
+ if ("${info_forwardto}foo" = "foo")
+ if ("${ext}" = "43974") {
+ Set(info_forwardto=${ext}&SCCP/${ext});
+ } else {
+ Set(info_forwardto=${ext}&SIP/${ext}w);
+ };
+ return;
+};
+
+macro checkcfb( ext ) {
+ if ("${info_forwardbusy}foo" = "foo") {
+ Wait(1);
+ MailboxExists(${ext});
+ if ("${VMBOXEXISTSSTATUS}" = "FAILED") {
+ &dialout(${ext});
+ Hangup;
+ };
+ &bvm(${ext});
+ Hangup;
+ };
+ &stdexten(${info_forwardbusy});
+ return;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from test.conf
+
+context test-include {
+ includes {
+ test-digium;
+ test-sounds;
+ test-phinfo;
+ };
+};
+
+context test-digium {
+ *500 => {
+ Dial(IAX2/guest@misery.digium.com/s@default);
+ Playback(demo-nogo);
+ Hangup;
+ };
+};
+
+context test-sounds {
+ *501 => {
+ Answer;
+ Musiconhold;
+ Wait(1);
+ Hangup;
+ };
+};
+
+context test-phinfo {
+ *505 => {
+ Answer;
+ NoOp(${CALLERIDNUM:5});
+ SayDigits(${CALLERIDNUM:5});
+ Hangup;
+ };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from external.conf
+
+context long-distance {
+ includes {
+ local1;
+ };
+
+ _91XXXXXXXXXX => &dialout(${EXTEN});
+ _9011. => &dialout(${EXTEN});
+};
+
+context local1 {
+ includes {
+ default;
+ };
+
+ 911 => &dialout(911);
+ 9911 => &dialout(9911);
+
+ _9NXXXXXX => goto huntsville-calling|${EXTEN}|1;
+ _936NXXXXXX => {
+ goto 9${EXTEN:3}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _832NXXXXXX => {
+ goto 9${EXTEN}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _713NXXXXXX => {
+ goto 9${EXTEN}|1 ;
+ Congestion(10);
+ Hangup;
+ };
+
+ _281NXXXXXX => {
+ goto 9${EXTEN}|1;
+ Congestion(10);
+ Hangup;
+
+ };
+
+ _NXXNXXXXXX => {
+ goto 9${EXTEN}|1;
+ goto 91${EXTEN}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _91800NXXXXXX => &dialout(${EXTEN});
+ _91866NXXXXXX => &dialout(${EXTEN});
+ _91877NXXXXXX => &dialout(${EXTEN});
+ _91888NXXXXXX => &dialout(${EXTEN});
+ _91900NXXXXXX => &dialout(${EXTEN});
+ _91976NXXXXXX => &dialout(${EXTEN});
+ _9713NXXXXXX => goto houston-calling|${EXTEN}|1;
+ _9281NXXXXXX => goto houston-calling|${EXTEN}|1;
+ _9832NXXXXXX => goto houston-calling|${EXTEN}|1;
+ _9903NXXXXXX => goto l903-calling|${EXTEN}|1;
+
+ _31NXXNXXXXXX => &dialout(${EXTEN});
+
+ h => Hangup;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from internal.conf
+
+context from-scm2 {
+ _4XXXX => {
+ NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM});
+ Dial(SIP/${EXTEN},20,wW);
+ Hangup;
+ };
+
+ _6XXXX => {
+ NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM});
+ Dial(SIP/${EXTEN},20,wW);
+ Hangup;
+ };
+};
+
+///////////////////////////////////////////////////////////
+// All internal extensions work through the default context
+// Phones that can only make internal calls should be in
+// this context.
+///////////////////////////////////////////////////////////
+
+context default {
+// Include the contexts in the files that allow us to make these phone calls
+ includes {
+ vm-include;
+ apps-include;
+ test-include;
+ };
+
+// ALWAYS have an 'h' extension
+ h => {
+ NoOp(Hangup cause was: ${HANGUPCAUSE});
+ Hangup;
+ };
+
+// We like to hear that we dialed an invalid extension
+ i => Playback(pbx-invalid);
+
+// Dial the operator
+ 0 => &dialout(0);
+
+// Send voicemail calls to the vm-* contexts to be handled
+ voicemail => goto vm-direct|s|1;
+ 5555 => goto vm-direct|s|1;
+ 62100 => goto vm-extension|s|1;
+
+// These are our campus extensions, send them to the macro
+ _6XXXX => &stdexten(${EXTEN});
+ _4XXXX => &stdexten(${EXTEN});
+// These are campus extensions as well, might need to take this out though.
+ _9294XXXX => goto _4XXXX|1;
+ _9496XXXX => goto _6XXXX|1;
+
+// These allows us to dial from the directory in our phone without worrying about dialing 9
+ _936294XXXX => {
+ goto ${EXTEN:5}|1;
+ goto 9${EXTEN:3}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _936496XXXX => {
+ goto ${EXTEN:5}|1;
+ goto 9${EXTEN:3}|1;
+ Congestion(10);
+ Hangup;
+ };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from apps.conf
+
+context apps-include {
+ includes {
+ app-agents;
+ app-dnd;
+ app-callforward;
+ app-calltrace;
+ app-conferences;
+ app-ssd;
+ app-psd;
+ app-idblock;
+ app-helpdesk;
+ app-dictate;
+ app-set-monitor;
+ };
+};
+
+context app-agents {
+ *54 => {
+ Answer;
+ Wait(1);
+ Read(agent_no|agent-user);
+ AgentCallbackLogin(${agent_no}|s${CALLERIDNUM:5});
+ Playback(agent-loginok);
+ Hangup;
+ };
+
+ *55 => {
+ Answer;
+ Wait(1);
+ AgentCallbackLogin(${agent_no});
+ Hangup;
+ };
+};
+
+context app-calltrace {
+// caller dials this to find out the last call missed and possibly call back
+ *69 => goto app-calltrace-perform|s|1;
+};
+
+context app-calltrace-perform {
+ s => {
+ Answer;
+ Wait(1);
+ Background(info-about-last-call);
+ Background(telephone-number);
+ RealTime(call_info|exten|${CALLERIDNUM:5}|ct_);
+ if ("${ct_calltrace}foo" = "foo") {
+ Playback(loligo/from-unknown-caller);
+ Hangup;
+ } else {
+ SayDigits("${ct_calltrace}");
+ Set(TIMEOUT(digit)=3);
+ Set(TIMEOUT(response)=7);
+ Background(loligo/to-call-this-number);
+ Background(press-1);
+ Background(loligo/silence/5);
+ };
+ };
+
+ 1 => goto local1|${ct_calltrace}|1;
+
+ i => {
+ Playback(vm-goodbye);
+ Hangup;
+ };
+
+ t => {
+ Playback(vm-goodbye);
+ Hangup;
+ };
+};
+
+context app-set-monitor {
+ *50 => {
+ Realtime(call_info,exten,${CALLERIDNUM:5},mon_set_);
+ if ("${mon_set_monitor}" = "YES") {
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},monitor|);
+ System(/usr/local/bin/db_update.sh call_info monitor '' exten ${CALLERIDNUM:5} &);
+ } else {
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},monitor,YES);
+ System(/usr/local/bin/db_update.sh call_info monitor YES exten ${CALLERIDNUM:5} &);
+ };
+ NoOp(${mon_set_monitor});
+ Hangup;
+ };
+};
+
+context app-dnd {
+ *78 => {
+ Answer;
+ Wait(1);
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},donotdisturb,YES);
+ System(/usr/local/bin/db_update.sh call_info donotdisturb YES exten ${CALLERIDNUM:5} &);
+ Playback(do-not-disturb);
+ Playback(loligo/activated);
+ Hangup;
+ };
+
+ *79 => {
+ Answer;
+ Wait(1);
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},donotdisturb|);
+ System(/usr/local/bin/db_update.sh call_info donotdisturb '' exten ${CALLERIDNUM:5} &);
+ Playback(do-not-disturb);
+ Playback(loligo/de-activated);
+ Hangup;
+ };
+};
+
+context app-callforward {
+ // forwards calling extension to input number *72{EXTEN}
+ _*72. => {
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardto,${EXTEN:3});
+ System(/usr/local/bin/db_update.sh call_info forwardto ${EXTEN:3} exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-unconditional);
+ Playback(loligo/for);
+ Playback(loligo/extension);
+ SayDigits(${CALLERIDNUM:5});
+ Playback(loligo/is-set-to);
+ SayDigits(${EXTEN:3});
+ Hangup;
+ };
+
+ // prompts for extension to forward to
+ *72 => {
+ Answer;
+ Wait(1);
+ Playback(please-enter-your);
+ Playback(extension);
+ Background(then-press-pound);
+ VMAuthenticate(|s);
+ Background(loligo/ent-target-attendant);
+ Read(toext,loligo/then-press-pound);
+ Wait(1);
+ RealtimeUpdate(call_info,exten,${AUTH_MAILBOX},forwardto,${toext});
+ System(/usr/local/bin/db_update.sh call_info forwardto ${toext} exten ${AUTH_MAILBOX} &);
+ Playback(loligo/call-fwd-unconditional);
+ Playback(loligo/for);
+ Playback(loligo/extension);
+ SayDigits(${AUTH_MAILBOX});
+ Playback(loligo/is-set-to);
+ SayDigits(${toext});
+ Hangup;
+ };
+
+ // cancels dialed extension call forward
+ _*73. => {
+ Realtime(voicemail,mailbox,${EXTEN:3},auth_);
+ Answer;
+ Wait(1);
+ Authenticate(${auth_password});
+ RealtimeUpdate(call_info,exten,${EXTEN:3},forwardto,);
+ System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${EXTEN:3} &);
+ Wait(1);
+ SayDigits(${EXTEN:3});
+ Playback(loligo/call-fwd-cancelled);
+ Hangup;
+ };
+
+ // cancels call forward for calling extension
+ *73 => {
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardto,);
+ System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-cancelled);
+ Hangup;
+ };
+
+ // dialed call forward on busy
+ _*90. => {
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardbusy,${EXTEN:3});
+ System(/usr/local/bin/db_update.sh call_info forwardbusy ${EXTEN:3} exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-on-busy);
+ Playback(loligo/for);
+ Playback(loligo/extension);
+ SayDigits(${CALLERIDNUM:5});
+ Playback(loligo/is-set-to);
+ SayDigits(${EXTEN:3});
+ Hangup;
+ };
+
+ // cancels call forward on busy for calling extension
+ *91 => {
+ RealtimeUpdate(call_info,exten,${CALLERIDNUM:5},forwardbusy|);
+ System(/usr/local/bin/db_update.sh call_info forwardbusy '' exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-on-busy);
+ Playback(loligo/de-activated);
+ Hangup;
+ };
+
+ h => Hangup;
+};
+
+context app-idblock {
+ _*67. => {
+ Set(CALLERID(name)=Anonymous);
+ &stdexten(${EXTEN:3});
+ };
+};
+
+context app-dictate {
+ *1 => {
+ Dictate();
+ Hangup;
+ };
+};
+
+context app-ssd {
+// *59 <xx> <y.> - Set system speed dial <xx> to digits <y.>
+// *59 <xx> 0 - Delete system speed dial <xx>
+// *59 <xx> - Review system speed dial <xx>
+// *1xx - Dial speed dial <xx>
+ _*59XXX. => {
+ Answer;
+ RealtimeUpdate(ssd,sd,${EXTEN:3:2},extension,${EXTEN:5});
+ System(/usr/local/bin/db_update.sh systemsd extension ${EXTEN:5} sd ${EXTEN:3:2} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-set-to);
+ SayDigits(${EXTEN:5});
+ Hangup;
+ };
+
+ _*59XX0 => {
+ Answer;
+ RealtimeUpdate(ssd,sd,${EXTEN:3:2},extension,);
+ System(/usr/local/bin/db_update.sh systemsd extension '' sd ${EXTEN:3:2} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-cleared);
+ Hangup;
+ };
+
+ _*59XX => {
+ Answer;
+ Realtime(ssd,sd,${EXTEN:3},ssd_);
+ if ("${ssd_extension}foo" = "foo") {
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-set-to);
+ SayDigits(${ssd_extension});
+ Hangup;
+ };
+
+ // NTC = number to call
+ _*1XX => {
+ Realtime(ssd,sd,${EXTEN:2},ssd_);
+ if ("${ssd_extension}foo" = "foo") {
+ Answer;
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ &stdexten(${ssd_extension});
+ Congestion(10);
+ Hangup;
+ };
+};
+
+macro check-psd-exists ( ext ) {
+ Realtime(psd,extension,${ext},psd_);
+ if ("${psd_extension}foo" = "foo") {
+ System(/usr/local/bin/create_psd.sh ${ext});
+ } else
+ NoOp(PSD set for ${ext});
+ return;
+};
+
+context app-psd {
+// *89 <xx> <y.> - Set personal speed dial <xx> to digits <y.>
+// *89 <xx> 0 - Delete personal speed dial <xx>
+// *89 <xx> - Review personal speed dial <xx>
+// *2xx - Dial personal speed dial <xx>
+ _*89XXX. => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Answer;
+ RealtimeUpdate(psd,extension,${CALLERIDNUM:5},s${EXTEN:3:2},${EXTEN:5});
+ System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} ${EXTEN:5} extension ${CALLERIDNUM:5} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-set-to);
+ SayDigits(${EXTEN:5});
+ Hangup;
+ };
+
+ _*89XX0 => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Answer;
+ RealtimeUpdate(psd|extension|${CALLERIDNUM:5}|s${EXTEN:3:2}|);
+ System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} '' extension ${CALLERIDNUM:5} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-cleared);
+ Hangup;
+ };
+
+ _*89XX => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Answer;
+ Realtime(psd|extension|${CALLERIDNUM:5}|psd_);
+ Wait(1);
+ if ("${psd_s${EXTEN:3:2}}foo" = "foo") {
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-set-to);
+ SayDigits(${psd_s${EXTEN:3:2}});
+ Hangup;
+ };
+
+ // NTC = number to call
+ _*2XX => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Realtime(psd|extension|${CALLERIDNUM:5}|psd_);
+ if ("${psd_s${EXTEN:2}}foo" = "foo") {
+ Answer;
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ &stdexten(${psd_s${EXTEN:2}});
+ Congestion(10);
+ Hangup;
+ };
+};
+
+context app-helpdesk {
+ *4357 => {
+ &stdexten(41950);
+ Congestion;
+ };
+};
+
+context app-conferences {
+// waiting for room number announcement
+ *86 => goto app-conf-hidden|s|1;
+};
+
+context app-conf-hidden {
+ s => {
+ Wait(1);
+ Playback(loligo/please-enter-the);
+ Playback(loligo/extension);
+ read(roomtoenter,loligo/then-press-pound);
+ Meetme(${roomtoenter});
+ Waitexten(8);
+ Hangup;
+ };
+
+ _1. => Meetme(${EXTEN});
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from vm.conf:
+
+context vm-include {
+ includes {
+ vm-direct;
+ vm-extension;
+ vm-directory;
+ };
+};
+
+context vm-direct {
+ s => {
+ Dial(SIP/5555@ixtlchochitl.zvbwu.edu,20);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Playback(extra/pls-try-call-later);
+ Congestion(10);
+ Hangup;
+ };
+};
+
+context vm-extension {
+ s => {
+ Dial(SIP/62100@ixtlchochitl.zvbwu.edu,20);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Playback(extra/pls-try-call-later);
+ Congestion(10);
+ Hangup;
+ };
+};
+
+context vm-directory {
+ 5556 => {
+ Dial(SIP/5556@ixtlchochitl.zvbwu.edu);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Playback(extra/pls-try-call-later);
+ Congestion(10);
+ Hangup;
+ };
+};
diff --git a/trunk/pbx/ael/ael-test/ael-test6/extensions.ael b/trunk/pbx/ael/ael-test/ael-test6/extensions.ael
new file mode 100644
index 000000000..13ebf67fd
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test6/extensions.ael
@@ -0,0 +1,833 @@
+///////////////////////////////////////////////////////////////////////////////
+// Helpdesk Queue
+
+context hd-queue {
+ s => {
+ NoOp(Add a background sound to tell the user their options);
+ Queue(helpdesk|t);
+ NoOp(Put in options to apologize and send user to voicemail);
+ };
+
+ 0 => goto default|0|1;
+ 1 => {
+ Dial(u41950@svm1.shsu.edu);
+ Congestion(10);
+ Hangup;
+ };
+};
+
+
+context l903-calling {
+ _9903NXXXXXX => {
+ Realtime(l903_ext|exchange|${EXTEN:4:3}|l903_);
+ if ("${l903_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+};
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from houston.conf
+// Converted the extension list to the database
+
+context houston-calling {
+ _9713NXXXXXX => {
+ Realtime(hou_713_ext|exchange|${EXTEN:4:3}|hou_713_);
+ if ("${hou_713_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+
+ _9281NXXXXXX => {
+ Realtime(hou_281_ext|exchange|${EXTEN:4:3}|hou_281_);
+ if ("${hou_281_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+
+ _9832NXXXXXX => {
+ Realtime(hou_832_ext|exchange|${EXTEN:4:3}|hou_832_);
+ if ("${hou_832_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from huntsville.conf
+// Converted the extension list to the database
+
+context huntsville-calling {
+ _9NXXXXXX => {
+ Realtime(hv_ext|exchange|${EXTEN:1:3}|hv_);
+ if ("${hv_exchange}foo" = "foo") {
+ Playback(num-outside-area);
+ SayDigits(1);
+ Playback(and-area-code);
+ Playback(before-the-number);
+ Hangup;
+ };
+ &dialout(${EXTEN});
+ Congestion(10);
+ Hangup;
+ };
+
+ _NXXXXXX => {
+ NoOp(Stripping last four to see what extension we're dialing);
+ Set(LAST4=${EXTEN:3});
+ StripLSD(4);
+ };
+
+ i => Playback(pbx-invalid);
+ h => Hangup;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from macros.conf
+
+macro dialout( number ) {
+ Realtime(call_info|exten|${CALLERIDNUM:5}|mon_);
+ if ("${mon_monitor}" = "YES") {
+ Dial(SIP/${number}@sgw1.shsu.edu,,wW);
+ Dial(SIP/${number}@sgw2.shsu.edu,,wW);
+ } else {
+ Dial(SIP/${number}@sgw1.shsu.edu);
+ Dial(SIP/${number}@sgw2.shsu.edu);
+ };
+};
+
+// Standard extension macro:
+// ${ext} - Extension
+macro stdexten( ext ) {
+ Realtime(sipusers|name|${ext}|sip_user_);
+ Realtime(call_info|exten|${ext}|info_);
+ if ("${sip_user_name}foo" = "foo") {
+ Wait(1);
+ &dialout(${ext});
+ Congestion(10);
+ Hangup;
+ };
+ NoOp(${CALLERIDNUM});
+ RealtimeUpdate(call_info|exten|${ext}|calltrace|${CALLERIDNUM});
+ System(/usr/local/bin/db_update.sh call_info calltrace ${CALLERIDNUM} exten ${ext} &);
+ &checkdnd(${ext});
+ &checkcf(${ext});
+ Realtime(call_info|exten|${CALLERIDNUM:5}|mon_);
+ if ("${mon_monitor}" = "YES") {
+ Dial(SIP/${info_forwardto},25,wW);
+ } else {
+ Dial(SIP/${info_forwardto},25);
+ };
+ switch ("${DIALSTATUS}") {
+ case "BUSY":
+ &checkcfb(${ext});
+ break;
+ case "CHANUNAVAIL":
+ Dial(IAX2/asterisk:password@scm2.shsu.edu/${info_forwardto},25,wW);
+ MailboxExists(${ext});
+// if ("${VMBOXEXISTSSTATUS}" = "FAILED") {
+// Congestion(10);
+// Hangup;
+// };
+ &uvm(${ext});
+ Hangup;
+ break;
+ case "CONGESTION":
+ MailboxExists(${ext});
+ if ("$(VMBOXEXISTSSTATUS}" = "FAILED") {
+ Congestion(10);
+ Hangup;
+ };
+ &bvm(${ext});
+ Hangup;
+ break;
+ default:
+ MailboxExists(${ext});
+ if ("$(VMBOXEXISTSSTATUS}" = "FAILED") {
+ Congestion(10);
+ Hangup;
+ };
+ &uvm(${ext});
+ Hangup;
+ };
+ Hangup;
+};
+
+macro uvm( ext ) {
+ Dial(SIP/u${ext}@svm1.shsu.edu);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Congestion(10);
+ Hangup;
+};
+
+macro bvm( ext ) {
+ Dial(SIP/b${ext}@svm1.shsu.edu);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Congestion(10);
+ Hangup;
+};
+
+macro checkdnd( ext ) {
+ if ("${info_donotdisturb}foo" = "foo") {
+ NoOp(Do Not Disturb is not active);
+ } else
+ &uvm(${ext});
+};
+
+macro checkcf( ext ) {
+ if ("${info_forwardto}foo" = "foo")
+ if ("${ext}" = "43974") {
+ Set(info_forwardto=${ext}&SCCP/${ext});
+ } else {
+ Set(info_forwardto=${ext}&SIP/${ext}w);
+ };
+};
+
+macro checkcfb( ext ) {
+ if ("${info_forwardbusy}foo" = "foo") {
+ Wait(1);
+ MailboxExists(${ext});
+ if ("$(VMBOXEXISTSSTATUS}" = "FAILED") {
+ &dialout(${ext});
+ Hangup;
+ };
+ &bvm(${ext});
+ Hangup;
+ };
+ &stdexten(${info_forwardbusy});
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from test.conf
+
+context test-include {
+ includes {
+ test-digium;
+ test-sounds;
+ test-phinfo;
+ };
+};
+
+context test-digium {
+ *500 => {
+ Dial(IAX2/guest@misery.digium.com/s@default);
+ Playback(demo-nogo);
+ Hangup;
+ };
+};
+
+context test-sounds {
+ *501 => {
+ Answer;
+ Musiconhold;
+ Wait(1);
+ Hangup;
+ };
+};
+
+context test-phinfo {
+ *505 => {
+ Answer;
+ NoOp(${CALLERIDNUM:5});
+ SayDigits(${CALLERIDNUM:5});
+ Hangup;
+ };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from external.conf
+
+context long-distance {
+ includes {
+ local1;
+ };
+
+ _91XXXXXXXXXX => &dialout(${EXTEN});
+ _9011. => &dialout(${EXTEN});
+};
+
+context local1 {
+ includes {
+ default;
+ };
+
+ 911 => &dialout(911);
+ 9911 => &dialout(9911);
+
+ _9NXXXXXX => goto huntsville-calling|${EXTEN}|1;
+ _936NXXXXXX => {
+ Goto 9${EXTEN:3}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _832NXXXXXX => {
+ goto 9${EXTEN}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _713NXXXXXX => {
+ goto 9${EXTEN}|1 ;
+ Congestion(10);
+ Hangup;
+ };
+
+ _281NXXXXXX => {
+ goto 9${EXTEN}|1;
+ Congestion(10);
+ Hangup;
+
+ };
+
+ _NXXNXXXXXX => {
+ goto 9${EXTEN}|1;
+ goto 91${EXTEN}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _91800NXXXXXX => &dialout(${EXTEN});
+ _91866NXXXXXX => &dialout(${EXTEN});
+ _91877NXXXXXX => &dialout(${EXTEN});
+ _91888NXXXXXX => &dialout(${EXTEN});
+ _91900NXXXXXX => &dialout(${EXTEN});
+ _91976NXXXXXX => &dialout(${EXTEN});
+ _9713NXXXXXX => goto houston-calling|${EXTEN}|1;
+ _9281NXXXXXX => goto houston-calling|${EXTEN}|1;
+ _9832NXXXXXX => goto houston-calling|${EXTEN}|1;
+ _9903NXXXXXX => goto l903-calling|${EXTEN}|1;
+
+ _31NXXNXXXXXX => &dialout(${EXTEN});
+
+ h => Hangup;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from internal.conf
+
+context from-scm2 {
+ _4XXXX => {
+ NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM});
+ Dial(SIP/${EXTEN},20,wW);
+ Hangup;
+ };
+
+ _6XXXX => {
+ NoOp(DIALING SIP EXTENSION ${EXTEN} - FROM ${CALLERIDNUM});
+ Dial(SIP/${EXTEN},20,wW);
+ Hangup;
+ };
+};
+
+///////////////////////////////////////////////////////////
+// All internal extensions work through the default context
+// Phones that can only make internal calls should be in
+// this context.
+///////////////////////////////////////////////////////////
+
+context default {
+// Include the contexts in the files that allow us to make these phone calls
+ includes {
+ vm-include;
+ apps-include;
+ test-include;
+ };
+
+// ALWAYS have an 'h' extension
+ h => {
+ NoOp(Hangup cause was: ${HANGUPCAUSE});
+ Hangup;
+ };
+
+// We like to hear that we dialed an invalid extension
+ i => Playback(pbx-invalid);
+
+// Dial the operator
+ 0 => &dialout(0);
+
+// Send voicemail calls to the vm-* contexts to be handled
+ voicemail => goto vm-direct|s|1;
+ 5555 => goto vm-direct|s|1;
+ 62100 => goto vm-extension|s|1;
+
+// These are our campus extensions, send them to the macro
+ _6XXXX => &stdexten(${EXTEN});
+ _4XXXX => &stdexten(${EXTEN});
+// These are campus extensions as well, might need to take this out though.
+ _9294XXXX => goto _4XXXX|1;
+ _9496XXXX => goto _6XXXX|1;
+
+// These allows us to dial from the directory in our phone without worrying about dialing 9
+ _936294XXXX => {
+ goto ${EXTEN:5}|1;
+ goto 9${EXTEN:3}|1;
+ Congestion(10);
+ Hangup;
+ };
+
+ _936496XXXX => {
+ goto ${EXTEN:5}|1;
+ goto 9${EXTEN:3}|1;
+ Congestion(10);
+ Hangup;
+ };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from apps.conf
+
+context apps-include {
+ includes {
+ app-agents;
+ app-dnd;
+ app-callforward;
+ app-calltrace;
+ app-conferences;
+ app-ssd;
+ app-psd;
+ app-idblock;
+ app-helpdesk;
+ app-dictate;
+ app-set-monitor;
+ };
+};
+
+context app-agents {
+ *54 => {
+ Answer;
+ Wait(1);
+ Read(agent_no|agent-user);
+ AgentCallbackLogin(${agent_no}|s${CALLERIDNUM:5});
+ Playback(agent-loginok);
+ Hangup;
+ };
+
+ *55 => {
+ Answer;
+ Wait(1);
+ AgentCallbackLogin(${agent_no});
+ Hangup;
+ };
+};
+
+context app-calltrace {
+// caller dials this to find out the last call missed and possibly call back
+ *69 => goto app-calltrace-perform|s|1;
+};
+
+context app-calltrace-perform {
+ s => {
+ Answer;
+ Wait(1);
+ Background(info-about-last-call);
+ Background(telephone-number);
+ RealTime(call_info|exten|${CALLERIDNUM:5}|ct_);
+ if ("${ct_calltrace}foo" = "foo") {
+ Playback(loligo/from-unknown-caller);
+ Hangup;
+ } else {
+ SayDigits("${ct_calltrace}");
+ Set(TIMEOUT(digit)=3);
+ Set(TIMEOUT(response)=7);
+ Background(loligo/to-call-this-number);
+ Background(press-1);
+ Background(loligo/silence/5);
+ };
+ };
+
+ 1 => goto local1|${ct_calltrace}|1;
+
+ i => {
+ Playback(vm-goodbye);
+ Hangup;
+ };
+
+ t => {
+ Playback(vm-goodbye);
+ Hangup;
+ };
+};
+
+context app-set-monitor {
+ *50 => {
+ Realtime(call_info|exten|${CALLERIDNUM:5}|mon_set_);
+ if ("${mon_set_monitor}" = "YES") {
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|monitor|);
+ System(/usr/local/bin/db_update.sh call_info monitor '' exten ${CALLERIDNUM:5} &);
+ } else {
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|monitor|YES);
+ System(/usr/local/bin/db_update.sh call_info monitor YES exten ${CALLERIDNUM:5} &);
+ };
+ NoOp(${mon_set_monitor});
+ Hangup;
+ };
+};
+
+context app-dnd {
+ *78 => {
+ Answer;
+ Wait(1);
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|donotdisturb|YES);
+ System(/usr/local/bin/db_update.sh call_info donotdisturb YES exten ${CALLERIDNUM:5} &);
+ Playback(do-not-disturb);
+ Playback(loligo/activated);
+ Hangup;
+ };
+
+ *79 => {
+ Answer;
+ Wait(1);
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|donotdisturb|);
+ System(/usr/local/bin/db_update.sh call_info donotdisturb '' exten ${CALLERIDNUM:5} &);
+ Playback(do-not-disturb);
+ Playback(loligo/de-activated);
+ Hangup;
+ };
+};
+
+context app-callforward {
+ // forwards calling extension to input number *72{EXTEN}
+ _*72. => {
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardto|${EXTEN:3});
+ System(/usr/local/bin/db_update.sh call_info forwardto ${EXTEN:3} exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-unconditional);
+ Playback(loligo/for);
+ Playback(loligo/extension);
+ SayDigits(${CALLERIDNUM:5});
+ Playback(loligo/is-set-to);
+ SayDigits(${EXTEN:3});
+ Hangup;
+ };
+
+ // prompts for extension to forward to
+ *72 => {
+ Answer;
+ Wait(1);
+ Playback(please-enter-your);
+ Playback(extension);
+ Background(then-press-pound);
+ VMAuthenticate(|s);
+ Background(loligo/ent-target-attendant);
+ Read(toext,loligo/then-press-pound);
+ Wait(1);
+ RealtimeUpdate(call_info|exten|${AUTH_MAILBOX}|forwardto|${toext});
+ System(/usr/local/bin/db_update.sh call_info forwardto ${toext} exten ${AUTH_MAILBOX} &);
+ Playback(loligo/call-fwd-unconditional);
+ Playback(loligo/for);
+ Playback(loligo/extension);
+ SayDigits(${AUTH_MAILBOX});
+ Playback(loligo/is-set-to);
+ SayDigits(${toext});
+ Hangup;
+ };
+
+ // cancels dialed extension call forward
+ _*73. => {
+ Realtime(voicemail|mailbox|${EXTEN:3}|auth_);
+ Answer;
+ Wait(1);
+ Authenticate(${auth_password});
+ RealtimeUpdate(call_info|exten|${EXTEN:3}|forwardto|);
+ System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${EXTEN:3} &);
+ Wait(1);
+ SayDigits(${EXTEN:3});
+ Playback(loligo/call-fwd-cancelled);
+ Hangup;
+ };
+
+ // cancels call forward for calling extension
+ *73 => {
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardto|);
+ System(/usr/local/bin/db_update.sh call_info forwardto '' exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-cancelled);
+ Hangup;
+ };
+
+ // dialed call forward on busy
+ _*90. => {
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardbusy|${EXTEN:3});
+ System(/usr/local/bin/db_update.sh call_info forwardbusy ${EXTEN:3} exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-on-busy);
+ Playback(loligo/for);
+ Playback(loligo/extension);
+ SayDigits(${CALLERIDNUM:5});
+ Playback(loligo/is-set-to);
+ SayDigits(${EXTEN:3});
+ Hangup;
+ };
+
+ // cancels call forward on busy for calling extension
+ *91 => {
+ RealtimeUpdate(call_info|exten|${CALLERIDNUM:5}|forwardbusy|);
+ System(/usr/local/bin/db_update.sh call_info forwardbusy '' exten ${CALLERIDNUM:5} &);
+ Answer;
+ Wait(1);
+ Playback(loligo/call-fwd-on-busy);
+ Playback(loligo/de-activated);
+ Hangup;
+ };
+
+ h => Hangup;
+};
+
+context app-idblock {
+ _*67. => {
+ Set(CALLERID(name)=Anonymous);
+ &stdexten(${EXTEN:3});
+ };
+};
+
+context app-dictate {
+ *1 => {
+ Dictate();
+ Hangup;
+ };
+};
+
+context app-ssd {
+// *59 <xx> <y.> - Set system speed dial <xx> to digits <y.>
+// *59 <xx> 0 - Delete system speed dial <xx>
+// *59 <xx> - Review system speed dial <xx>
+// *1xx - Dial speed dial <xx>
+ _*59XXX. => {
+ Answer;
+ RealtimeUpdate(ssd|sd|${EXTEN:3:2}|extension|${EXTEN:5});
+ System(/usr/local/bin/db_update.sh systemsd extension ${EXTEN:5} sd ${EXTEN:3:2} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-set-to);
+ SayDigits(${EXTEN:5});
+ Hangup;
+ };
+
+ _*59XX0 => {
+ Answer;
+ RealtimeUpdate(ssd|sd|${EXTEN:3:2}|extension|);
+ System(/usr/local/bin/db_update.sh systemsd extension '' sd ${EXTEN:3:2} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-cleared);
+ Hangup;
+ };
+
+ _*59XX => {
+ Answer;
+ Realtime(ssd|sd|${EXTEN:3}|ssd_);
+ if ("${ssd_extension}foo" = "foo") {
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-set-to);
+ SayDigits(${ssd_extension});
+ Hangup;
+ };
+
+ // NTC = number to call
+ _*1XX => {
+ Realtime(ssd|sd|${EXTEN:2}|ssd_);
+ if ("${ssd_extension}foo" = "foo") {
+ Answer;
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ &stdexten(${ssd_extension});
+ Congestion(10);
+ Hangup;
+ };
+};
+
+macro check-psd-exists ( ext ) {
+ Realtime(psd|extension|${ext}|psd_);
+ if ("${psd_extension}foo" = "foo") {
+ System(/usr/local/bin/create_psd.sh ${ext});
+ } else
+ NoOp(PSD set for ${ext});
+};
+
+context app-psd {
+// *89 <xx> <y.> - Set personal speed dial <xx> to digits <y.>
+// *89 <xx> 0 - Delete personal speed dial <xx>
+// *89 <xx> - Review personal speed dial <xx>
+// *2xx - Dial personal speed dial <xx>
+ _*89XXX. => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Answer;
+ RealtimeUpdate(psd|extension|${CALLERIDNUM:5}|s${EXTEN:3:2}|${EXTEN:5});
+ System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} ${EXTEN:5} extension ${CALLERIDNUM:5} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-set-to);
+ SayDigits(${EXTEN:5});
+ Hangup;
+ };
+
+ _*89XX0 => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Answer;
+ RealtimeUpdate(psd|extension|${CALLERIDNUM:5}|s${EXTEN:3:2}|);
+ System(/usr/local/bin/db_update.sh personalsd s${EXTEN:3:2} '' extension ${CALLERIDNUM:5} &);
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/has-been-cleared);
+ Hangup;
+ };
+
+ _*89XX => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Answer;
+ Realtime(psd|extension|${CALLERIDNUM:5}|psd_);
+ Wait(1);
+ if ("${psd_s${EXTEN:3:2}}foo" = "foo") {
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:3:2});
+ Playback(loligo/is-set-to);
+ SayDigits(${psd_s${EXTEN:3:2}});
+ Hangup;
+ };
+
+ // NTC = number to call
+ _*2XX => {
+ &check-psd-exists(${CALLERIDNUM:5});
+ Realtime(psd|extension|${CALLERIDNUM:5}|psd_);
+ if ("${psd_s${EXTEN:2}}foo" = "foo") {
+ Answer;
+ Wait(1);
+ Playback(loligo/speed-dial);
+ SayDigits(${EXTEN:2});
+ Playback(loligo/is-not-set);
+ Hangup;
+ };
+ &stdexten(${psd_s${EXTEN:2}});
+ Congestion(10);
+ Hangup;
+ };
+};
+
+context app-helpdesk {
+ *4357 => {
+ &stdexten(41950);
+ Congestion;
+ };
+};
+
+context app-conferences {
+// waiting for room number announcement
+ *86 => goto app-conf-hidden|s|1;
+};
+
+context app-conf-hidden {
+ s => {
+ Wait(1);
+ Playback(loligo/please-enter-the);
+ Playback(loligo/extension);
+ read(roomtoenter,loligo/then-press-pound);
+ Meetme(${roomtoenter});
+ Waitexten(8);
+ Hangup;
+ };
+
+ _1. => Meetme(${EXTEN});
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Extensions pulled from vm.conf:
+
+context vm-include {
+ includes {
+ vm-direct;
+ vm-extension;
+ vm-directory;
+ };
+};
+
+context vm-direct {
+ s => {
+ Dial(SIP/5555@svm1.shsu.edu,20);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Playback(extra/pls-try-call-later);
+ Congestion(10);
+ Hangup;
+ };
+};
+
+context vm-extension {
+ s => {
+ Dial(SIP/62100@svm1.shsu.edu,20);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Playback(extra/pls-try-call-later);
+ Congestion(10);
+ Hangup;
+ };
+};
+
+context vm-directory {
+ 5556 => {
+ Dial(SIP/5556@svm1.shsu.edu);
+ Playback(im-sorry);
+ Playback(voice-mail-system);
+ Playback(down);
+ Playback(extra/pls-try-call-later);
+ Congestion(10);
+ Hangup;
+ };
+};
diff --git a/trunk/pbx/ael/ael-test/ael-test7/extensions.ael b/trunk/pbx/ael/ael-test/ael-test7/extensions.ael
new file mode 100644
index 000000000..27ed374f5
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test7/extensions.ael
@@ -0,0 +1,460 @@
+//
+// Example AEL config file
+//
+
+globals {
+ CONSOLE=Console/dsp;
+ TRUNKMSD=0; //MSD digits to strip (usually 1 or 0)
+ TRUNCPROTO=SIP;
+ TRUNK=sunrocket;
+ PSTN=pstn-spa3k;
+ PSTNPROTO=SIP;
+ TARIOPROTO=SIP;
+ TARIO=tario;
+ CPPROTO=SIP;
+ CPACKET1=callpacket1;
+ CPACKET2=callpacket2;
+ SELLVOIP=1577040314;
+ SVPROTO=IAX2;
+};
+
+
+macro stdexten (ext , dev ) {
+ PrivacyManager(3,10);
+ if("${PRIVACYMGRSTATUS}" = "FAILED") {
+ Playback(vm-goodbye);
+ Hangup();
+ };
+
+ AGI(calleridnamelookup.agi);
+ Dial(${dev}/${ext},30,t);
+ switch(${DIALSTATUS}) {
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ };
+ catch a {
+ VoiceMailMain(${ext});
+ return;
+ };
+};
+
+macro announce_minutes(minutes) {
+ Playback(vm-youhave);
+ SayNumber(${minutes});
+ Playback(vm-minutes);
+ Wait(1);
+};
+
+// Check if given provider allows only some free minutes per month
+// and announce number of free minutes remaining.
+// The limit will be reset monthly by cron job.
+// The macro sets the following variables:
+// MINUTES_LIMIT - number of free minutes per month
+// MINUTES_USED - number of free minutes used in the current month
+// PROVIDER - provider name
+
+macro checkanddial(prov,proto,ext,arg1,arg2,arg3,arg4) {
+ Set(MINUTES_LIMIT=0);
+ Set(MINUTES_USED=0);
+ Set(PROVIDER=${prov});
+
+ if(${DB_EXISTS(Provider/${prov}/used)})
+ Set(MINUTES_USED=${DB_RESULT});
+
+ country_c = 0;
+ switch(${LEN(${ext})}) { //assuming all international numbers are 11 digits long.
+ case 10: //NXXNXXXXXX
+ country_c=1;
+ break;
+ case 11: //XNXXNXXXXXX
+ country_c = ${ext:0:1};
+ break;
+ default: //011XNXXNXXXXXX
+ country_c = ${ext:3:1};
+ break;
+ };
+
+ if("${prov}" = "${TRUNK}" & ${country_c} != 1) { // SunRocket international calls
+ Set(MINUTES_LIMIT=${DB(Provider/${prov}/limit)});
+ &announce_minutes($[${MINUTES_LIMIT} - ${MINUTES_USED}]);
+ };
+ if("${prov}" = "${CPACKET1}" | "${prov}" = "${CPACKET2}") { // Callpacket has a limit on domestic calls
+ Set(MINUTES_LIMIT=${DB(Provider/${prov}/limit)});
+ &announce_minutes($[${MINUTES_LIMIT} - ${MINUTES_USED}]);
+ };
+ DeadAGI(dial.agi,${proto}/${ext}@${prov},${arg1},${arg2},${arg3},${arg4});
+};
+
+macro trunkdial(ext) { // Dial sunrocket and set correct collerid
+ if("${CALLERID(num)}" = "1") {
+ Set(CALLERID(num)=7322271653);
+ } else {
+ Set(CALLERID(num)=7326260100);
+ };
+ Set(CALLERID(name)=Sergey Okhapkin);
+ &checkanddial(${TRUNK},${TRUNCPROTO},${ext},60,T);
+ Hangup;
+};
+
+macro checklocal(ext) { // lookup the number in DB and call the number via pstn or sunrocket
+ Set(AREACODE=${ext:0:3});
+ Set(EXCHANGE=${ext:3:3});
+ Set(IS_LOCAL=${DB_EXISTS(localnum/${AREACODE}/${EXCHANGE})});
+ if(${IS_LOCAL}) {
+ &checkanddial(${PSTN},${PSTNPROTO},${ext},60,T);
+ if ("${DIALSTATUS}" = "BUSY")
+ &trunkdial(${ext});
+ } else
+ &trunkdial(${ext});
+};
+
+macro autodial(ext) { // Find Least Cost Route
+ LCDial(${ext},60,T);
+ if("${DIALSTATUS}" = "NOPROVIDER")
+ Playback(invalid);
+ Hangup();
+};
+
+context default { // Calls to us
+ s => {
+ Wait(1);
+ Answer;
+start:
+ Set(TIMEOUT(digit)=3);
+ Set(TIMEOUT(response)=10);
+repeat:
+ for (x=0; ${x} < 5; x=${x} + 1) {
+ Background(home/greeting);
+ WaitExten();
+ };
+ };
+ t => jump *;
+ i => { // invalid extension
+ Playback(invalid);
+ goto s|repeat;
+ };
+ _* => {
+ Playback(vm-goodbye);
+ Wait(1);
+ Hangup;
+ };
+ 1 => &stdexten(1,SIP/1);
+ 2 => &stdexten(2,SIP/2);
+ 3 => &stdexten(3,SIP/3);
+
+ 2271653 => jump 1;
+ 7322271653 => jump 1;
+ 17322271653 => jump 1;
+
+ 6260100 => jump 2;
+ 7326260100 => jump 2;
+ 17326260100 => jump 2;
+ 8058701100 => jump 2;
+ 3103622835 => jump 2;
+ sos => jump 2;
+ 1400898 => jump 2;
+
+ 6260101 => jump s;
+ 7326260101 => jump s;
+ 17326260101 => jump s;
+
+ 2271677 => jump 3;
+ 7322271677 => jump 3;
+ 17322271677 => jump 3;
+ galka => jump 3;
+ 911 => Dial(${PSTNPROTO}/911@${PSTN},60,);
+ 380 => Dial(SIP/topspeen@212.40.38.70,60,T);
+
+ // Fun stuff
+ 100 => {
+ SayUnixTime();
+ goto s|start;
+ };
+ 101 => { // Voicemail
+ VoicemailMain(${CALLERID(num)});
+ Hangup;
+ };
+ 102 => MusicOnHold();
+// 103 => {
+// Wait(1);
+//start:
+// Read(NUMBER,vm-enter-num-to-call);
+// LCDial(${NUMBER},T);
+// goto start;
+// };
+ 105 => jump s@phrase-menu;
+ 7312 => {
+ ForkCDR;
+ Set(CALLERID(name)=Sergey Okhapkin);
+ Set(CALLERID(num)=7326260100);
+ DISA(1111|home);
+ };
+};
+
+context goiax {
+ s => {
+ Answer();
+ Ringing();
+ Wait(1);
+start:
+ Read(NUMBER,vm-enter-num-to-call);
+ Set(CALLERID(name)=Central NJ);
+ Dial(IAX2/14301@fwdOUT/q${NUMBER},60,T);
+ goto start;
+ };
+
+};
+
+context phrase-menu {
+
+ s => {
+ Answer; // Answer the line
+ TIMEOUT(digit)=2; // Set Digit Timeout to 5 seconds
+ TIMEOUT(response)=10; // Set Response Timeout to 10 seconds
+ BackGround(custom/phrase-menu); // Play main menu.
+ };
+ 1 => { // Phrase Recording
+ Wait(1);
+ Read(PHRASEID|custom/enter-phrase-num);
+ Wait(2); // give yourself 2 secs to take a breath and wait for beep
+ Record(custom/${PHRASEID}:gsm);
+ Wait(2);
+ Playback(custom/${PHRASEID});
+ Wait(1);
+ jump s;
+ };
+ 2 => { // Phrase review
+ Wait(1);
+ Read(PHRASEID|custom/enter-phrase-num);
+ Wait(1);
+ Playback(custom/${PHRASEID});
+ Wait(1);
+ jump s;
+ };
+ t => Hangup;
+ i => {
+ Playback(custom/invalid-option);
+ jump s;
+ };
+};
+
+context outbound {
+ // North America seven-, ten- and eleven digits
+ _NXXXXXX => &autodial(1732${EXTEN});
+ _NXXNXXXXXX => &autodial(1${EXTEN});
+ _ZNXXNXXXXX. => &autodial(${EXTEN});
+ // Toll free numbers via PSTN
+// _1800NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T);
+// _1888NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T);
+// _1877NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T);
+// _1866NXXXXXX => &checkanddial(${PSTN},${PSTNPROTO},${EXTEN},60,T);
+
+ _011. => { //International context accessed through trunk
+ &trunkdial(${EXTEN});
+ };
+ _012. => { //fwdOUT
+ Set(CALLERID(name)=Central NJ);
+ Dial(IAX2/14301@fwdOUT/q${EXTEN:3},60,T);
+ };
+ _013X. => { //NECC
+ Dial(${PSTNPROTO}/011${EXTEN:3}@${PSTN},60,T);
+ };
+ _0131. => { //NECC to US
+ Dial(${PSTNPROTO}/${EXTEN:3}@${PSTN},60,T);
+ };
+ _014. => { //TARIO by SIP ID
+ Set(CALLERID(name)=Sergey Okhapkin);
+ Set(CALLERID(num)=1400898);
+ Dial(${TARIOPROTO}/${EXTEN:3}@${TARIO},60,T);
+ };
+ _0157. => { //TARIO outbound Russia
+ Set(CALLERID(name)=Sergey Okhapkin);
+ Set(CALLERID(num)=1400898);
+ Dial(${TARIOPROTO}/8${EXTEN:4}@${TARIO},60,T);
+ };
+// _015. => { //TARIO outbound international
+// CALLERID(name)="Sergey Okhapkin";
+// CALLERID(num)=1400898;
+// Dial(${TARIOPROTO}/810${EXTEN:3}@${TARIO},60,T);
+// };
+ _0161NXXNXXXXXX => { //Callpacket outbound USA/Canada
+ &checkanddial(${CPACKET1},${CPPROTO},${EXTEN:3},60,T);
+ };
+ _0171NXXNXXXXXX => { //Callpacket outbound USA/Canada
+ &checkanddial(${CPACKET2},${CPPROTO},${EXTEN:3},60,T);
+ };
+ _0181NXXNXXXXXX => { //sellvoip outbound USA/Canada
+ Dial(${SVPROTO}/${SELLVOIP}@${SELLVOIP}/${EXTEN:3},60,T);
+ };
+ _019. => { //Voipbuster
+ Dial(IAX2/sokhapkin@voipbuster/00${EXTEN:3},60,T);
+ };
+};
+
+context home { //calls from us
+ includes {
+ default;
+ outbound;
+ };
+};
+
+context sunrocket-in {
+ 7322271653 => jump s;
+ 7326260100 => jump 2@default;
+ s => {
+ if("${CALLERID(num)}" = "sunrocketcom")
+ Set(CALLERID(num)=);
+ switch(${CALLERID(RDNIS)}) {
+ case 7326260100:
+ jump 2@default;
+ break;
+ case 7326260101:
+ jump s@default;
+ break;
+ default:
+ jump 1@default;
+ break;
+ };
+ };
+};
+
+context pstn-in {
+ 3 => {
+ if ("${CALLERID(num)}" = "7322271677")
+ Set(CALLERID(num)=);
+ jump 3@default;
+ };
+};
+
+context tario.net-in {
+ _X. => {
+ Set(CALLERID(name)=);
+ if("${CALLERID(num):-11:1}" = "8")
+ Set(CALLERID(num)=7${CALLERID(num):1});
+ if("${SIP_HEADER(To)}" = "<sip:2271677@sipnet.ru>") {
+ jump 3@default;
+ } else if("${SIP_HEADER(To)}" = "<sip:2271653@sipnet.ru>") {
+ jump 1@default;
+ } else
+ jump 2@default;
+ };
+};
+
+context from-callpacket {
+ 8058701100 => jump 2@default;
+ 3103622835 => {
+ Answer;
+ Ringing;
+ Wait(10);
+ Voicemail(b3103622835);
+ Hangup;
+ };
+ a => Hangup;
+};
+
+context fromfwdOUT { // make sure we only accept US and Canada calls, limit to 30 minutes
+ includes {
+ fromfwdOUT-catchbad;
+ fromfwdOUT-isgood;
+ fromfwdOUT-catchall;
+ };
+};
+
+context fromfwdOUT-isgood {
+ _17326260100 => jump 2@default;
+ _17326260101 => jump s@default;
+ _17322271653 => jump 1@default;
+ _17322271677 => jump 3@default;
+ _1NXXNXXXXXX => {
+ Set(CALLERID(name)=Sergey Okhapkin);
+// Set(CALLERID(num)=7326260100);
+// Dial(${TRUNCPROTO}/*67${EXTEN:${TRUNKMSD}}@${TRUNK},60,,L(1800000:60000));
+ Dial(${CPPROTO}/${EXTEN}@${CPACKET2},60,,L(1800000:60000));
+ };
+};
+
+context fromfwdOUT-catchbad { //block bahamas, etc
+ _1900. => congestion ; //N11
+ _1XXX976. => congestion ; //N11
+ _1XXX555. => congestion ; //N11
+ _1X11. => congestion ; //N11
+ _1867. => congestion ; //Yukon (sorry mike)
+
+ // exten => _1NPA Country
+ _1242. => congestion; //BAHAMAS
+ _1246. => congestion; //BARBADOS
+ _1264. => congestion; //ANGUILLA
+ _1268. => congestion; //ANTIGUA/BARBUDA
+ _1284. => congestion; //BRITISH VIRGIN ISLANDS
+ _1345. => congestion; //CAYMAN ISLANDS
+ _1441. => congestion; //BERMUDA
+ _1473. => congestion; //GRENADA
+ _1649. => congestion; //TURKS & CAICOS ISLANDS
+ _1664. => congestion; //MONTSERRAT
+ _1758. => congestion; //ST. LUCIA
+ _1767. => congestion; //DOMINICA
+ _1784. => congestion; //ST. VINCENT & GRENADINES
+ _1809. => congestion; //DOMINICAN REPUBLIC
+ _1829. => congestion; //DOMINICAN REPUBLIC
+ _1868. => congestion; //TRINIDAD AND TOBAGO
+ _1869. => congestion; //ST. KITTS AND NEVIS
+ _1876. => congestion; //JAMAICA
+ _1787. => congestion; //Puerto Rico 787, 939 $0.07
+ _1939. => congestion; //Puerto Rico 787, 939 $0.07
+ _1671. => congestion; //Guam 671 $0.08
+ _1340. => congestion; //U.S. Virgin Islands 340 $0.06
+};
+
+context fromfwdOUT-catchall {
+ _X. => Congestion;
+ h => Hangup ; //hangup event
+ i => Hangup ; //invalid event
+ t => Hangup ; //timeout event
+};
+
+context ael-demo {
+ s => {
+ Wait(1);
+ Answer();
+ TIMEOUT(digit)=5;
+ TIMEOUT(response)=10;
+restart:
+ Background(demo-congrats);
+instructions:
+ for (x=0; ${x} < 3; x=${x} + 1) {
+ Background(demo-instruct);
+ WaitExten();
+ };
+ };
+ 2 => {
+ Background(demo-moreinfo);
+ goto s|instructions;
+ };
+ 3 => {
+ LANGUAGE()=fr;
+ goto s|restart;
+ };
+ 500 => {
+ Playback(demo-abouttotry);
+ Dial(IAX2/guest@misery.digium.com);
+ Playback(demo-nogo);
+ goto s|instructions;
+ };
+ 600 => {
+ Playback(demo-echotest);
+ Echo();
+ Playback(demo-echodone);
+ goto s|instructions;
+ };
+ _1234 => &std-exten-ael(${EXTEN}, "IAX2");
+ # => {
+ Playback(demo-thanks);
+ Hangup();
+ };
+ t => jump #;
+ i => Playback(invalid);
+};
+
diff --git a/trunk/pbx/ael/ael-test/ael-test8/extensions.ael b/trunk/pbx/ael/ael-test/ael-test8/extensions.ael
new file mode 100644
index 000000000..17bc74e6f
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-test8/extensions.ael
@@ -0,0 +1,27 @@
+context default
+{
+
+706/3077610011 => {
+ JabberStatus(asterisk|jmls@mike,StatusCode);
+
+ switch(${StatusCode}) {
+ case 1:
+ Dial(SIP/706,12);
+ switch(${DIALSTATUS}) {
+ case BUSY:
+ Voicemail(b706);
+ break;
+ default:
+ Voicemail(u706);
+ };
+ BackGround(hello);
+ break;
+ default:
+ Voicemail(u706);
+ };
+
+ Hangup();
+ };
+
+}
+
diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/extensions.ael b/trunk/pbx/ael/ael-test/ael-vtest13/extensions.ael
new file mode 100755
index 000000000..b7e953e62
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest13/extensions.ael
@@ -0,0 +1,3183 @@
+globals
+{
+ static=yes;
+ writeprotect=yes;
+ CONSOLE=Console/dsp; // Console interface for demo
+ IAXINFO=murf:tlhfckoct; // IAXtel username/password
+ FWDNUMBER=544788 ; // your calling number
+ FWDCIDNAME="Joe-Worker"; // your caller id
+ FWDPASSWORD=zingledoodle ; // your password
+ FWDRINGS=Zap/6 ; // the phone to ring
+ FWDVMBOX=1 ; // the VM box for this user
+}
+
+macro std-exten( ext , dev )
+{
+ Dial(${dev}/${ext},20);
+ switch(${DIALSTATUS})
+ {
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ case ANSWER:
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+ catch a {
+ VoiceMailMain(${ext});
+ }
+}
+
+macro std-priv-exten_1( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_2( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_3( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_4( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_5( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_6( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_7( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_8( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_9( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_10( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_11( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_12( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_13( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_14( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_15( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_16( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_17( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_18( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_19( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_20( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_21( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_22( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_23( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_24( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_25( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_26( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_27( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_28( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_29( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_30( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_31( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_32( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_33( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_34( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_35( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_36( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_37( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_38( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_39( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_40( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_41( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_42( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_43( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_44( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_45( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_46( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_47( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_48( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_49( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_50( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_51( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_52( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_53( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_54( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_55( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_56( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_57( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_58( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_59( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_60( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_61( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_62( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_63( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_64( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_65( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_66( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_67( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_68( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_69( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_70( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_71( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_72( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten_73( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+
+macro std-priv-exten( dev, ext , timeout, opts, torcont, dontcont )
+{
+ Dial(${dev},${timeout},${opts});
+ NoOp(${DIALSTATUS} was chosen);
+ switch(${DIALSTATUS})
+ {
+ case TORTURE:
+ goto ${torcont}|s|begin;
+ break;
+ case DONTCALL:
+ goto ${dontcont}|s|begin;
+ break;
+ case BUSY:
+ Voicemail(b${ext});
+ break;
+ case ANSWER:
+ break;
+ case NOANSWER:
+ Voicemail(u${ext});
+ break;
+ default:
+ Voicemail(u${ext});
+ }
+}
+
+macro fillcidname()
+{
+ if( "${CALLERID(num)}" = "" ) // nothing to work with, quit!!!
+ return;
+ Set(cidn=${DB(cidname/${CALLERID(num)})});
+ if( "${CALLERID(name)}" != "" )
+ {
+ if( ("${cidn}" = "Privacy Manager" & "${CALLERID(name)}" != "Privacy Manager") | "${cidn}" = "" ) // if the entry isn't in the database,
+ // or if an entry exists, and it's "Privacy Manager", empty, (or add other useless possibilities).
+ {
+ Set(DB(cidname/${CALLERID(num)})=${CALLERID(name)}); // then set or override what's in the DB
+ }
+ }
+ // Now, we fill in the callerid info from the incoming entry, if it's stuff worth using
+ // Ignore fundamentally semi-anonymous information from local cell phones
+ // if the db has an entry for this number, and it's not a canned string from a cell phone company
+ if( ( "${cidn}" != "" ) & ( "${CALLERID(name)}" = ""
+ | "${CALLERID(name)}" = "CODY,WY "
+ | "${CALLERID(name)}" = "POWELL,WY "
+ | "${CALLERID(name)}" = "WIRELESS CALLER"
+ | "${CALLERID(name)}" = "SUBSCRIBER,WIRE"
+ | "${CALLERID(name)}" = "CELLULAR ONE"
+ | "${CALLERID(name)}" = "Cellular One Customer"
+ | "${CALLERID(name)}" = "CELLULAR ONE "
+ | "${CALLERID(name)}" = "Privacy Manager"
+ | "${CALLERID(name)}" = "RIVERTON,WY "
+ | "${CALLERID(name)}" = "BASIN,WY "
+ | "${CALLERID(name)}" = "BILLINGS,MT "
+ | "${CALLERID(name)}" = "PROVO,UT "
+ | "${CALLERID(name)}" = "TOLL FREE " ) ) // put stuff in the above, that the phone company tends to put in your callerid,
+ // that you would rather override with DB info
+ // there's no way to guess them all, but you can get the most popular ones...
+ // why cell phones can't do CID like everybody else, ....?
+ {
+ Set(CALLERID(name)=${cidn}); // Override what the phone company provides with what's in the DB for this number.
+ }
+}
+
+macro ciddial(dialnum, lookup, waittime, dialopts, ddev)
+{
+ Set(cidnu=${CALLERID(num)});
+ Set(cidn=${DB(cidname/${lookup})});
+ Set(CALLERID(name)=${cidn});
+ Dial(${ddev}/${dialnum}|${waittime}|${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_voip);
+ CALLERID(num)=7075679201;
+ Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_cell);
+ CALLERID(num)=${cidnu}; // put the original number back
+ Dial(Zap/2/${lookup},${waittime},${dialopts});
+ }
+ }
+}
+
+macro ciddial3(dialnum, lookup, waittime, dialopts, ddev)
+{
+ Set(cidnu=${CALLERID(num)});
+ Set(cidn=${DB(cidname/${lookup})});
+ Set(CALLERID(name)=${cidn});
+ Dial(${ddev}/${dialnum}|${waittime}|${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_cell);
+ Dial(Zap/2/${lookup},${waittime},${dialopts});
+ }
+}
+
+macro ciddial2(dialnum, lookup, waittime, dialopts, ddev) // give priority to tctwest, then the ZAP in emergencies
+{
+ Set(cidn=${DB(cidname/${lookup})});
+ Set(cidnu=${CALLERID(num)});
+ Set(CALLERID(name)=${cidn});
+ Set(CALLERID(num)=7075679201);
+ Dial(SIP/1${lookup}@tctwest,${waittime},${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ Set(CALLERID(num)=${cidnu}); // put the original number back
+ BackGround(try_zap);
+ Dial(${ddev}/${dialnum},${waittime}|${dialopts});
+ if( "${DIALSTATUS}" = "CHANUNAVAIL" )
+ {
+ BackGround(try_cell);
+ Dial(Zap/2/${lookup},${waittime},${dialopts});
+ }
+ }
+}
+
+macro callerid-liar()
+{
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/priv-callerintros/LIAR.gsm&);
+ Background(priv-liar); // Script: OOOps! Sorry! I don't allow men with ski masks pulled over their
+ // faces to get in the front door, and unidentified callers won't fair
+ // any better. You entered *MY* phone number. That won't work.
+ // If you are telemarketing, cross me off the list, and don't call again.
+ // If you did this by mistake, forgive my defenses, and call again.
+ // Alternate: (priv-liar2)
+ // Script: You have chosen to try to deceive my system and withold your CallerID,
+ // by entering my own phone number as YOUR CallerID. I find this
+ // offensive because you are being dishonest. I will not do business nor
+ // waste my time talking to anyone who is less than honest and forthcoming.
+ // Take me off your call list and do not call me again.
+ Hangup();
+}
+
+macro callerid-bad()
+{
+ mycid=${CALLERID(num)}:"1([0-9]+)";
+ Set(CALLERID(num)=${mycid});
+ Wait(0);
+}
+
+context privacyManagerFailed {
+ s => {
+ begin:
+ Background(PrivManInstructions); // Script: OOps, that didn't go well. You need to enter *your* area code, and *your* 7 digit
+ // phone number, for a total of 10 digits, or you'll be handed over to the monkeys. Let's
+ // try this again, and hopefully you can get past our front-line defenses!
+ PrivacyManager();
+ if( "${PRIVACYMGRSTATUS}" = "FAILED" )
+ {
+ Background(tt-allbusy);
+ Background(tt-somethingwrong);
+ Background(tt-monkeysintro);
+ Background(tt-monkeys);
+ Background(tt-weasels);
+ Hangup();
+ }
+ else
+ {
+ goto homeline|s|postPriv;
+ }
+ }
+}
+
+// Some comments
+// Some more comments
+
+context homeline {
+ s => {
+ begin:
+ Answer();
+ Set(repeatcount=0);
+ Zapateller(nocallerid);
+ PrivacyManager();
+ if( "${PRIVACYMGRSTATUS}" = "FAILED" )
+ {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/privmanfailed.gsm);
+ &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket);
+ Hangup();
+ return;
+// goto privacyManagerFailed|s|begin;
+ }
+ postPriv:
+ &fillcidname();
+ Set(CONFCIDNA=${CALLERID(name)});
+ Set(CONFCIDNU=${CALLERID(num)});
+ AGI(callall);
+ AGI(submit-announce.agi);
+ if( "${CALLERID(num)}" : "1" )
+ {
+ &callerid-bad();
+ }
+ if( "${CALLERID(num)}" = "7077577685" & "${CALLERID(name)}" : "Privacy Manager" )
+ {
+ &callerid-liar();
+ }
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ Set(lds=${DB(playlds/${CALLERID(num)})});
+ if( "${lds}" = "1" )
+ {
+ SetMusicOnHold(mohlds);
+ }
+ direct=${DB(DirectCall/${CALLERID(num)})};
+ if( "${direct}" != "" & ${direct} != 0 )
+ {
+ verbose(direct is XXX#${direct}XXXX);
+ Playback(greetings/direct); // Welcome to the Murphy residence. This system will automatically try to connect you to...
+ Playback(/var/spool/asterisk/voicemail/default/${direct}/greet);
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/${direct}/greet.wav&);
+ switch(${direct})
+ {
+ case 1: //Steve
+ &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket);
+ goto s|loopback;
+ case 2: //Sonya
+ &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket);
+ goto s|loopback;
+ default: // all the kids
+ Set(z=${direct}-2);
+ goto homeline-kids|${z}|1;
+ }
+ }
+ loopback:
+ ifTime(*|*|20-25|dec)
+ {
+ Playback(greetings/christmas);
+ }
+ else ifTime(*|*|31|dec)
+ {
+ Playback(greetings/newyear);
+ }
+ else ifTime(*|*|1|jan)
+ {
+ Playback(greetings/newyear);
+ }
+ else ifTime(*|*|14|feb)
+ {
+ Playback(greetings/valentines);
+ }
+ else ifTime(*|*|17|mar)
+ {
+ Playback(greetings/stPat);
+ }
+ else ifTime(*|*|31|oct)
+ {
+ Playback(greetings/halloween);
+ }
+ else ifTime(*|mon|15-21|jan)
+ {
+ Playback(greetings/mlkDay);
+ }
+ else ifTime(*|thu|22-28|nov)
+ {
+ Playback(greetings/thanksgiving);
+ }
+ else ifTime(*|mon|25-31|may)
+ {
+ Playback(greetings/memorial);
+ }
+ else ifTime(*|mon|1-7|sep)
+ {
+ Playback(greetings/labor);
+ }
+ else ifTime(*|mon|15-21|feb)
+ {
+ Playback(greetings/president);
+ }
+ else ifTime(*|sun|8-14|may)
+ {
+ Playback(greetings/mothers);
+ }
+ else ifTime(*|sun|15-21|jun)
+ {
+ Playback(greetings/fathers);
+ }
+ else
+ {
+ Playback(greetings/hello); // None of the above? Just a plain hello will do
+ }
+ Background(murphy-homeline-intro1); // Script: Hello-- Welcome to the Murphy's! If you already know what
+ // option you want, you don't have to wait for this entire spiel-- just
+ // have at it.
+ // If you are calling because this number is on a list of some sort, dial 6.
+ // If you want Sonya, dial 1.
+ // If you want one of the kids, dial 2.
+ // If you want Steve, dial 3.
+ // to play with your introduction, dial 5.
+ // If we don't seem to be giving you the time of day, try 7.
+ // Have a good day!
+
+ }
+ 1 => { // Sonya
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/2/greet.wav&);
+ &std-priv-exten(Zap/3r1&Zap/5r1,2,25,mtw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 2 => { // Kids
+ goto homeline-kids|s|begin;
+ }
+ 21 => {
+ Dial(IAX2/seaniax,20,T);
+ }
+ 3 => { // Steve
+ &std-priv-exten(Zap/6r3&Sip/murf,1,25,mpA(beep)tw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 4 => { // Voicemail
+ VoicemailMain();
+ goto s|loopback;
+ }
+ 5 => { // play with intro
+ goto home-introduction|s|begin;
+ }
+ 6 => { // Telemarketers
+ goto telemarket|s|begin;
+ }
+ 7 => { // time of day, riddle
+ agi(tts-riddle.agi);
+ Background(gsm/what-time-it-is2);
+ SayUnixTime();
+ goto s|loopback;
+ }
+ 792 => { // Page All
+ goto pageall|s|begin;
+ }
+ 793 => { // check the tone recognition
+ Read(zz,,0,,1,0);
+ SayDigits(${zz});
+ }
+ t => {
+ Set(repeatcount=${repeatcount} + 1);
+ if( ${repeatcount} < 3 )
+ {
+ goto s|loopback; // just loopback isn't enough
+ }
+ Hangup();
+ }
+ i => {
+ Background(invalid);
+ goto s|loopback;
+ }
+ o => {
+ Congestion();
+ }
+ fax => {
+ Dial(Zap/4);
+ }
+}
+
+// Some comments
+// Some more comments
+
+context pageall {
+ s => {
+ begin:
+ AGI(callall);
+ MeetMe(5555,dtqp);
+ MeetMeAdmin(5555,K);
+ Hangup();
+ }
+
+ h => {
+ begin:
+ MeetMeAdmin(5555,K);
+ Background(conf-muted);
+ Hangup();
+ }
+}
+
+// Some comments
+// Some more comments
+
+context add-to-conference {
+ start => {
+ NoCDR();
+ MeetMe(5555,dmqp);
+ }
+ h => {
+ Hangup();
+ }
+}
+
+context home-introduction {
+ s => {
+ begin:
+ Background(intro-options); // Script: To hear your Introduction, dial 1.
+ // to record a new introduction, dial 2.
+ // to return to the main menu, dial 3.
+ // to hear what this is all about, dial 4.
+ }
+ 1 => {
+ Playback(priv-callerintros/${CALLERID(num)});
+ goto s|begin;
+ }
+ 2 => {
+ goto home-introduction-record|s|begin;
+ }
+ 3 => {
+ goto homeline|s|loopback;
+ }
+ 4 => {
+ Playback(intro-intro); // Script:
+ // This may seem a little strange, but it really is a neat
+ // thing, both for you and for us. I've taped a short introduction
+ // for many of the folks who normally call us. Using the Caller ID
+ // from each incoming call, the system plays the introduction
+ // for that phone number over a speaker, just as the call comes in.
+ // This helps the folks
+ // here in the house more quickly determine who is calling.
+ // and gets the right ones to gravitate to the phone.
+ // You can listen to, and record a new intro for your phone number
+ // using this menu.
+ goto s|begin;
+ }
+ t => {
+ goto s|begin;
+ }
+ i => {
+ Background(invalid);
+ goto s|begin;
+ }
+ o => {
+ goto s|begin;
+ }
+}
+
+context home-introduction-record {
+ s => {
+ begin:
+ Background(intro-record-choices); // Script:
+ // If you want some advice about recording your
+ // introduction, dial 1.
+ // otherwise, dial 2, and introduce yourself after
+ // the beep.
+ }
+ 1 => {
+ Playback(intro-record);
+ // Your introduction should be short and sweet and crisp.
+ // Your introduction will be limited to 10 seconds.
+ // This is NOT meant to be a voice mail message, so
+ // please, don't say anything about why you are calling.
+ // After we are done making the recording, your introduction
+ // will be saved for playback.
+ // If you are the only person that would call from this number,
+ // please state your name. Otherwise, state your business
+ // or residence name instead. For instance, if you are
+ // friend of the family, say, Olie McPherson, and both
+ // you and your kids might call here a lot, you might
+ // say: "This is the distinguished Olie McPherson Residence!"
+ // If you are the only person calling, you might say this:
+ // "This is the illustrious Kermit McFrog! Pick up the Phone, someone!!"
+ // If you are calling from a business, you might pronounce a more sedate introduction,like,
+ // "Fritz from McDonalds calling.", or perhaps the more original introduction:
+ // "John, from the Park County Morgue. You stab 'em, we slab 'em!".
+ // Just one caution: the kids will hear what you record every time
+ // you call. So watch your language!
+ // I will begin recording after the tone.
+ // When you are done, hit the # key. Gather your thoughts and get
+ // ready. Remember, the # key will end the recording, and play back
+ // your intro. Good Luck, and Thank you!"
+ goto 2|begin;
+ }
+ 2 => {
+ begin:
+ Background(intro-start);
+ // OK, here we go! After the beep, please give your introduction.
+ Background(beep);
+ Record(priv-callerintros/${CALLERID(num)}:gsm,3);
+ Background(priv-callerintros/${CALLERID(num)});
+ goto home-introduction|s|begin;
+ }
+ t => {
+ goto s|begin;
+ }
+ i => {
+ Background(invalid);
+ goto s|begin;
+ }
+ o => {
+ goto s|begin;
+ }
+}
+
+context homeline-kids {
+ s => {
+ begin:
+ Background(murphy-homeline-kids); // Which Kid? 1=Sean, 2:Eric, 3:Ryan, 4:Kyle, 5:Amber, 6:Alex, 7:Neal
+ }
+ 1 => { // SEAN
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/3/greet.wav&);
+ // &std-priv-exten(Zap/3r2&Zap/5r2,3,35,mtw,telemarket,telemarket);
+ &std-priv-exten(IAX2/seaniax&Zap/5r2,3,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 2 => { // ERIC
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&);
+ Voicemail(u4);
+ goto homeline|s|loopback;
+
+ // SetMusicOnHold(erics);
+ // TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ // TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&);
+ // &std-priv-exten(Zap/3r2&Zap/5r2,4,35,mtw,telemarket,telemarket);
+ // goto homeline|s|loopback;
+ }
+ 3 => { // RYAN
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/5/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,5,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 4 => { // KYLE
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/6/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,6,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 5 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/7/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,7,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+
+ }
+ 6 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/8/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,8,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ 7 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/9/greet.wav&);
+ &std-priv-exten(Zap/3r2&Zap/5r2,9,35,mtw,telemarket,telemarket);
+ goto homeline|s|loopback;
+ }
+ t => {
+ goto s|begin;
+ }
+ i => {
+ Background(invalid);
+ goto s|begin;
+ }
+ o => {
+ goto s|begin;
+ }
+}
+
+context voipworkline {
+ s => {
+ begin:
+ Answer();
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ goto workline|s|loopback;
+ }
+ 7075679201 => {
+ Answer();
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ goto workline|s|loopback;
+ }
+}
+
+context workline {
+ s => {
+ begin:
+ Answer();
+ Wait(1);
+ Set(repeatcount=0);
+ Zapateller(nocallerid);
+// PrivacyManager();
+// if( "${PRIVACYMGRSTATUS}" = "FAILED" )
+// {
+// goto privacyManagerFailed|s|begin;
+// }
+ &fillcidname();
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ loopback:
+ Background(greetings/greeting); //script: Hello
+ Background(murphy-office-intro1); //script: welcome to Steve Murphy's office. If you are dialing
+ // this number because it was on a calling list of any sort, dial 6.
+ // Otherwise, dial 1, and hopefully, you will reach Steve.
+ }
+ 1 => {
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm);
+ TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/1/greet.wav&);
+
+ &std-priv-exten(Zap/6&Sip/murf,1,30,mtw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 4 => {
+ VoicemailMain();
+ goto s|loopback;
+ }
+ 6 => {
+ goto telemarket|s|begin;
+ }
+ 793 => { // check the tone recognition
+ Read(zz,,0,,1,0);
+ SayDigits(${zz});
+ }
+ t => {
+ repeatcount=${repeatcount} + 1;
+ if( ${repeatcount} < 3 )
+ {
+ goto s|loopback; // just loopback isn't enough
+ }
+ Hangup();
+ }
+ i => {
+ Background(invalid);
+ goto s|loopback;
+ }
+ o => {
+ Congestion();
+ }
+ fax => {
+ Answer();
+ Dial(Zap/4);
+ }
+}
+
+context dialFWD {
+ ignorepat => 8;
+ ignorepat => 9;
+ _83. => {
+ Set(CALLERID(name)=${FWDCIDNAME});
+ Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r);
+ Congestion();
+ }
+ _82NXX => {
+ Set(CALLERID(name)=${FWDCIDNAME});
+ Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r);
+ Congestion();
+ }
+ _92NXX => {
+ Set(CALLERID(name)=${FWDCIDNAME});
+ Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2},60,r);
+ Congestion();
+ }
+}
+
+context dialiaxtel {
+ ignorepat => 8;
+ ignorepat => 9;
+ _81700NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+ _81800NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+ _91700NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+ _91800NXXXXXX => {
+ Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel);
+ }
+
+}
+
+context dialgoiax {
+ ignorepat => 9;
+ _93. => {
+ Set(CALLERID(name)="Joe Worker");
+ Dial(IAX2/878201007658:stickyfinger295@server1.goiax.com/${EXTEN:2},60,r);
+ Congestion();
+ }
+
+}
+
+context homefirst {
+ ignorepat => 9;
+ _91NXXNXXXXXX => {
+ &ciddial(${EXTEN:1},${EXTEN:2},30,TW,Zap/1);
+ }
+ _9754XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9574XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9202XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9219XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9254XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9716XXXX => {
+ &ciddial(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9NXXXXXX => {
+ &ciddial(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9011. => {
+ &ciddial(${EXTEN:1},${EXTEN:1},30,TW,Zap/1);
+ }
+ _9911 => {
+ Dial(Zap/1/911,30,T);
+ }
+ _9411 => {
+ Dial(Zap/1/411,30,T);
+ }
+}
+
+context workfirst {
+ ignorepat => 9;
+ _91NXXNXXXXXX => {
+ &ciddial2(${EXTEN:1},${EXTEN:2},30,TW,Zap/1);
+ }
+ _9754XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9574XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9202XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9219XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9254XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9716XXXX => {
+ &ciddial2(${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9NXXXXXX => {
+ &ciddial2(1707${EXTEN:1},707${EXTEN:1},30,TW,Zap/1);
+ }
+ _9911 => {
+ Dial(Zap/1/911,30,T);
+ }
+ _9411 => {
+ Dial(Zap/1/411,30,T);
+ }
+}
+
+context force_cell {
+ ignorepat => 8;
+ _81NXXNXXXXXX => {
+ &ciddial(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/2);
+ }
+ _8754XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8574XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8202XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8219XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8254XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8716XXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8NXXXXXX => {
+ &ciddial(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/2);
+ }
+ _8911 => {
+ Dial(Zap/1/911|30|T);
+ }
+ _8411 => {
+ Dial(Zap/1/411|30|T);
+ }
+}
+
+context force_home {
+ ignorepat => 8;
+ _81NXXNXXXXXX => {
+ &ciddial3(${EXTEN:1}#,${EXTEN:2},30,TW,Zap/1);
+ }
+ _8754XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8574XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8202XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8219XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8254XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8716XXXX => {
+ &ciddial3(${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8NXXXXXX => {
+ &ciddial3(1707${EXTEN:1}#,707${EXTEN:1},30,TW,Zap/1);
+ }
+ _8911 => {
+ Dial(Zap/1/911|30|T);
+ }
+ _8411 => {
+ Dial(Zap/1/411|30|T);
+ }
+}
+
+context homeext {
+ ignorepat => 8;
+ ignorepat => 9;
+ includes {
+ parkedcalls;
+ homefirst;
+ force_cell;
+ }
+ s => {
+ loopback:
+ Wait(0);
+ }
+ 1 => {
+ &std-priv-exten(Zap/3&Zap/5,2,35,mtw,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 2 => {
+ &std-priv-exten(Zap/6&Zap/5,1,35,mpA(beep3)Tt,telemarket,telemarket);
+ goto s|loopback;
+ }
+ 4 => {
+ VoicemailMain();
+ }
+ 5 => {
+ Record(recording:gsm);
+ Background(recording);
+ }
+ 6 => {
+ Background(recording);
+ }
+ 760 => {
+ DateTime();
+ goto s|loopback;
+ }
+ 761 => {
+ Record(announcement:gsm);
+ TrySystem(/usr/bin/play /var/lib/asterisk/sounds/announcement.gsm&);
+ goto s|loopback;
+ }
+ 762 => {
+ agi(tts-riddle.agi);
+ Background(gsm/what-time-it-is2);
+ SayUnixTime();
+ goto s|loopback;
+ }
+ 763 => {
+ Set(CALLERID(num)=);
+ Dial(Zap/6r3,35,mptA(beep3)); //results: it should ALWAYS ask for an intro; the intro should not be left behind
+ Hangup();
+ }
+ 764 => {
+ Set(CALLERID(num)=);
+ Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; shouldn't anyway if no callerid
+ Hangup();
+ }
+ 765 => {
+ Set(CALLERID(num)=);
+ Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call.
+ Hangup();
+ }
+ 766 => {
+ Dial(Zap/6r3,35,mptNA(beep3)); //results: Don't screen if there's CALLERID; it should screen the call.
+ Hangup();
+ }
+ 767 => {
+ Dial(Zap/6r3,35,mptnA(beep3)); //results: Don't save the intro; the interesting case, because callerID should be present.
+ Hangup();
+ }
+ 769 => {
+ Playtones(dial);
+ Wait(2);
+ Playtones(busy);
+ Wait(2);
+ Playtones(ring);
+ Wait(2);
+ Playtones(congestion);
+ Wait(2);
+ Playtones(callwaiting);
+ Wait(2);
+ Playtones(dialrecall);
+ Wait(2);
+ Playtones(record);
+ Wait(2);
+ Playtones(info);
+ Wait(5);
+ Hangup();
+ }
+ 790 => {
+ MeetMe(790,p);
+ }
+ 792 => {
+ goto pageall|s|begin;
+ }
+ 795 => {
+ AGI(wakeup.agi);Congestion();
+ }
+ 544716 => { // Incoming call from FWD
+ TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&);
+ goto s|loopback;
+ }
+
+ i => {
+ Background(invalid);
+ goto s|loopback;
+ }
+ o => {
+ goto s|loopback;
+ }
+ t => {
+ Congestion();
+ }
+}
+
+context fromvmhome {
+ 1 => {
+ Dial(Zap/6&Sip/murf|20|Tt);
+ }
+ 2 => {
+ Dial(Zap/3&Zap/5|20|Tt);
+ }
+ _707202XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707219XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707254XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707716XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707754XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707574XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _NXXNXXXXXX => {
+ &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1);
+ }
+ _1NXXNXXXXXX => { // HAND DIALING
+ &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1);
+ }
+ _754XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _574XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _NXXXXXX => {
+ &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _911 => {
+ &ciddial(911,911,30,TW,Zap/1);
+ }
+ _411 => {
+ &ciddial(411,411,30,TW,Zap/1);
+ }
+}
+
+context fromvmwork {
+ 1 => {
+ Dial(Zap/6&Sip/murf|20|Tt);
+ }
+ 2 => {
+ Dial(Zap/3&Zap/5|20|Tt);
+ }
+ _707202XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707219XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707254XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707716XXXX => {
+ &ciddial(1${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707754XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707574XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _NXXNXXXXXX => {
+ &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1);
+ }
+ _1NXXNXXXXXX => { // HAND DIALING
+ &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1);
+ }
+ _754XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _574XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _NXXXXXX => {
+ &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ 911 => {
+ &ciddial(911,911,30,TW,Zap/1);
+ }
+ 411 => {
+ &ciddial(411,411,30,TW,Zap/1);
+ }
+}
+
+context fromSeanUniden {
+ includes
+ {
+ parkedcalls;
+ }
+ 21 => {
+ Dial(IAX2/seaniax,20,T);
+ }
+ _707202XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707219XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707254XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707716XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707754XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _707574XXXX => {
+ &ciddial(${EXTEN:3},${EXTEN},30,TW,Zap/1);
+ }
+ _NXXNXXXXXX => {
+ &ciddial(1${EXTEN},${EXTEN},30,TW,Zap/1);
+ }
+ _1NXXNXXXXXX => {
+ &ciddial(${EXTEN},${EXTEN:1},30,TW,Zap/1);
+ }
+ _754XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _574XXXX => {
+ &ciddial(${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ _NXXXXXX => {
+ &ciddial(1707${EXTEN},707${EXTEN},30,TW,Zap/1);
+ }
+ 911 => {
+ &ciddial(911,911,30,TW,Zap/1);
+ }
+ 411 => {
+ &ciddial(411,411,30,TW,Zap/1);
+ }
+}
+
+context workext {
+ ignorepat => 8;
+ ignorepat => 9;
+ includes {
+ parkedcalls;
+ workfirst;
+ force_home;
+ dialFWD;
+ dialiaxtel;
+ dialgoiax;
+ }
+ s => {
+ loopback:
+ Wait(0);
+ }
+ 1 => {
+ Dial(Zap/3&Zap/5,20,tT);
+ }
+ 2 => {
+ Dial(Zap/5&Zap/6,20,tT);
+ }
+ 21 => {
+ Dial(IAX2/seaniax,20,T);
+ }
+ 22 => {
+ Set(CALLERID(num)=1234567890);
+ Set(CALLERID(name)=TestCaller);
+ Dial(Zap/5,20,mP()A(beep)tw);
+ NoOp(here is dialstatus: ${DIALSTATUS}...);
+ goto s|loopback;
+ }
+ 4 => {
+ VoicemailMain();
+ goto s|loopback;
+ }
+ 5 => {
+ Record(recording:gsm);
+ Background(recording);
+ }
+ 6 => {
+ ZapBarge();
+ }
+ 760 => {
+ DateTime();
+ goto s|loopback;
+ }
+ 761 => {
+ ZapBarge();
+ goto s|loopback;
+ }
+ 765 => {
+ Playback(demo-echotest);
+ Echo();
+ Playback(demo-echodone);
+ goto s|loopback;
+ }
+ 766 => {
+ Festival(The other thing to watch is neuro-electronics: the ability to interface technology with our neural system: My wife: Sigrid: has had a cochlear implant since 1996. This once profoundly deaf person now uses the phone: recognizes accents: and listens to movies and recorded books.);
+ goto s|loopback;
+ }
+ 767 => {
+ agi(tts-riddle.agi);
+ Background(gsm/what-time-it-is2);
+ SayUnixTime();
+ goto s|loopback;
+ }
+ 768 => {
+ agi(tts-computer.agi);
+ }
+ 771 => {
+ eagi(eagi-test);
+ agi(my-agi-test);
+ }
+ 772 => {
+ agi(wakeup.agi);
+ }
+ 775 => {
+ if( ${EXTEN}=${EXTEN} )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ if( ${EXTEN}=${LANGUAGE} )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ BackGround(digits/2);
+ }
+ 776 => {
+ Set(TEST=00359889811777);
+ if( ${TEST}= 00359889811777 )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ if( ${TEST}= 00359889811888 )
+ {
+ BackGround(digits/1);
+ }
+ else
+ {
+ BackGround(digits/0);
+ }
+ Hangup();
+ }
+ 790 => {
+ MeetMe(790,p);
+ }
+ 792 => {
+ goto pageall|s|begin;
+ }
+ 793 => {
+ #include "include1.ael2"
+ }
+ 795 => {
+ AGI(wakeup.agi);
+ Congestion();
+ }
+ 797 => {
+ Set(CONFCIDNA=${CALLERID(name)});
+ Set(CONFCIDNU=${CALLERID(num)});
+ AGI(callall);
+ AGI(submit-announce.agi);
+ Hangup();
+ }
+}
+
+context wakeup {
+ 3 => {
+ Dial(Zap/3|30);
+ }
+ 4 => {
+ Dial(Zap/4|30);
+
+ }
+ 5 => {
+ Dial(Zap/5|30);
+
+ }
+ 6 => {
+ Dial(Zap/6|30);
+
+ }
+ 99 => {
+ Dial(IAX2/murfiaxphone|30);
+ }
+ 97 => {
+ Dial(IAX2/ryaniax|30);
+ }
+ 94 => {
+ Dial(IAX2/seaniax|30);
+ }
+}
+
+context announce-all {
+ s => {
+ begin:
+ MeetMe(5555,dtqp);
+ MeetMeAdmin(5555,K);
+ Hangup();
+ }
+ h => {
+ MeetMeAdmin(5555,K);
+ Hangup();
+ }
+}
+
+// now include the telemarketer torture scripts!
+
+#include "telemarket_torture.ael2"
+
+
diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include1.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include1.ael2
new file mode 100644
index 000000000..80c562cb2
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest13/include1.ael2
@@ -0,0 +1,3 @@
+ NoOp(Hello, this is included from include1.ael2);
+ #include "include2.ael2"
+
diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include2.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include2.ael2
new file mode 100644
index 000000000..8d892fb0c
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest13/include2.ael2
@@ -0,0 +1,4 @@
+ NoOp(This was included from include2.ael2);
+ #include "include3.ael2"
+ #include "include4.ael2"
+
diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include3.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include3.ael2
new file mode 100644
index 000000000..3c6c1e3dd
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest13/include3.ael2
@@ -0,0 +1,2 @@
+ NoOp(This is include3.ael2!);
+ #include "include5.ael2"
diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include4.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include4.ael2
new file mode 100644
index 000000000..7d3703a5e
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest13/include4.ael2
@@ -0,0 +1,2 @@
+ NoOp(This is include4.ael2! Isn't it cool!?!?!?!);
+ NoOp(4 doesn't include anything);
diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/include5.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/include5.ael2
new file mode 100644
index 000000000..0e18983ef
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest13/include5.ael2
@@ -0,0 +1 @@
+ NoOp(Include5.ael2 doesn't include anything, either!);
diff --git a/trunk/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2 b/trunk/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2
new file mode 100755
index 000000000..ebd8e9f2f
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2
@@ -0,0 +1,812 @@
+//
+// AN EXCERSIZE IN BAD DIALPLAN DESIGN
+// (What better testing ground than on telemarketers?)
+//
+
+
+// BAD DESIGN: long, boring introductions followed by long, drawn out menus of choices.
+// if they survive to the last option, how will they remember the choices?
+//
+
+// BAD DESIGN: Amateur Recording. Poor voice quality, too quiet.
+// Also, the announcer is definitely not vocally gifted.
+// Also, the long pauses and clicks between the intro
+// and menu choices might lead some to think that
+// the announcements are over, and hang up. Too bad!
+
+// WORSE DESIGN: Instead of using the Background application, the Playback
+// application is used. After taking so much time and trouble
+// to record this material, the caller must listen and enjoy
+// every syllable before they can make an option choice. None
+// of that interrupting with a choice. We want them to savour
+// every word!
+
+// GOOD/BAD, ER INSIDIOUS -- DANGLE A CARROT-- GIVE THE LISTENER A GOOD REASON TO
+// HANG ON AND VOLUNTARILY LISTEN TO THE TORTURE.
+// BUT, DON'T MAKE PROMISES YOU WON'T KEEP!
+
+
+context telemarket {
+ s => {
+ begin:
+ Playback(telemarketer-intro); // ; Script:
+ // Due to the extremely high volume of calls from everything from telemarketers
+ // to Septic System Bacteria vendors, we are asking all such organizations
+ // to remove this number from their call list, or as need be, to add this
+ // number to their No-Call list, whichever is relevent.
+
+ // [THE CARROT:]
+ // We HAVE made some exceptions, and if you wish to see if your organization
+ // has been exempted, please listen to and follow the following prompts.
+ //
+ // Otherwise, please Cease calling this number!
+ //
+ Playback(telemarketer-choices);
+ // if you represent a charitable organization, please dial 1,
+ // if you represent a political organization, please dial 2.
+ // if you represent a polling company, please dial 3,
+ // if you represent a market research organization, please dial 4.
+ // if you represent a magazine or newsletter, please dial 5.
+ // if you represent a commercial organization, please dial 6.
+ }
+ 1 => goto telemarket-charity|s|begin;
+ 2 => goto telemarket-political|s|begin;
+ 3 => goto telemarket-pollster|s|begin;
+ 4 => goto telemarket-research|s|begin;
+ 5 => goto telemarket-magazine|s|begin;
+ 6 => goto telemarket-commercial|s|begin;
+ 7 => goto telemarket-other|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-charity {
+ s => {
+ begin:
+ Playback(telemark-charity-intro);
+ // We have contributed generously to many worthy causes in the past, and will
+ // continue to do so in the future. But we suspect that such organizatons
+ // have sold our name and phone number to each other until we are now hounded
+ // day and night by literally hundreds of such organizations.
+ // Enough is Enough!
+ //
+ // If we have contributed to your cause in the past, we may, perhaps, be disposed to
+ // do so in the future, at our option,
+ // we give no pledges nor make any commitments here.
+ // Send us material via the post if you feel this necessary
+ // but do not even consider email. Any email or further phone calls from your organization
+ // in the future, will be considered an act of aggression, and we will
+ // blacklist your organization for the rest of our natural lives.
+ //
+ // To see if your organization is exempt from these prohibitions, please
+ // comply with the following options.
+ Playback(telemark-charity-choices);
+ // If your organization is disease or genetic defect related, dial 1,
+ // If your organization is handicap related, dial 2.
+ // If your organization is a police or fireman or other similar support entity, please dial 3.
+ // If your organization is a grade school to high school related
+ // fund raiser or other type of activity, please dial 4.
+ // If your organization is a college or univerity or alumnis organization, please dial 5.
+ // If your organization is animal rights or ecology related organization, please dial 6.
+ // If your organization is a political action or candidate support related, please dial 7.
+ // If your organization is a substance abuse related organization or cause, please dial 8.
+ // And any other charity or tax exempt organization should dial 9.
+ }
+ 1 => goto telemarket-char-disease|s|begin;
+ 2 => goto telemarket-char-handicap|s|begin;
+ 3 => goto telemarket-char-police|s|begin;
+ 4 => goto telemarket-char-school|s|begin;
+ 5 => goto telemarket-char-college|s|begin;
+ 6 => goto telemarket-char-animal|s|begin;
+ 7 => goto telemarket-char-candidate|s|begin;
+ 8 => goto telemarket-char-abuse|s|begin;
+ 9 => goto telemarket-char-other|s|begin;
+// BAD DESIGN: referring all timeouts,invalid choices, etc, back to the root of the menu tree will frustrate users no end!
+// WORSE DESIGN: How about having the user have to push a button to repeat the current menu? When a time out could just
+// automatically do it for the user?
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-char-disease {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-handicap {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-police {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-school {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-college {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-animal {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-candidate {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-abuse {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-char-other {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-sorry {
+ s => {
+ begin:
+ Playback(telemarket-sorry);
+ // Sorry -- your organization is not exempt. Please stop calling us.
+ // Thank you. goodbye.
+ Hangup();
+ }
+}
+
+
+// BAD DESIGN: Hanging up on your audience, no matter what the outcome, is not a nice thing to do!
+
+context telemarket-exception {
+ s => {
+ begin:
+ Playback(telemarket-success);
+ // Congratulations. Your organization IS exempt. Please call us back,
+ // but this time, just act like a normal caller. Thank you. Goodbye.
+ Hangup();
+ }
+}
+
+
+// BAD DESIGN: Making long cascading menu choices is a nasty thing to do to callers!
+// BAD DESIGN: Putting the most frequently encountered items at the end of a list is also a nasty thing to do!
+
+
+// GOOD DESIGN: All rejection notices use a single context. All Acceptance also. To change a rejection to an
+// acceptance, just change the reference from telemarket-sorry to telemarket-exception
+
+
+context telemarket-political {
+ s => {
+ begin:
+ Playback(telemark-polit-intro);
+ // To see if your organization is exempt from our prohibitions,
+ // please follow the following prompts.
+ // please note that they are not in alphabetical order, and you will have to
+ // give them your full attention.
+ Playback(telemark-polit-choices);
+ // if You represent the America First Party, dial 1.
+ // if You represent the American Party, dial 2.
+ // if You represent the American Heritage Party, dial 3.
+ // if You represent the American Independent Party, dial 4.
+ // if You represent the American Nazi Party, dial 5.
+ // if You represent the Pot Party, dial 6.
+ // if You represent the American Reform Party, dial 7.
+ // if You represent the Christian Falenqist Party of America, dial 8.
+ // all others, please dial 9.
+ }
+ 1 => goto telemarket-poli-Am1st|s|begin;
+ 2 => goto telemarket-poli-American|s|begin;
+ 3 => goto telemarket-poli-AmHer|s|begin;
+ 4 => goto telemarket-poli-AmInd|s|begin;
+ 5 => goto telemarket-poli-AmNaz|s|begin;
+ 6 => goto telemarket-poli-Pot|s|begin;
+ 7 => goto telemarket-poli-AmRef|s|begin;
+ 8 => goto telemarket-poli-CFP|s|begin;
+ 9 => goto telemarket-political2|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-political2 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ // Thank you for your patience, and I congratulate you for your persistence.
+ // Just a few more options!
+ //
+ Playback(telemark-polit2-choices);
+ // if You represent the Communist Party USA, dial 1.
+ // if You represent the Constitution Party, dial 2.
+ // if You represent the Family Values Party, dial 3.
+ // if You represent the Freedom Socialist Party, dial 4.
+ // if You represent the Grass Roots Party, dial 5.
+ // if You represent the Green Party, dial 6.
+ // if You represent the Greens Party, dial 7.
+ // if You represent the Independence Party, dial 8.
+ // all others, goto 9.
+ }
+ 1 => goto telemarket-poli-Communist|s|begin;
+ 2 => goto telemarket-poli-Constit|s|begin;
+ 3 => goto telemarket-poli-FamVal|s|begin;
+ 4 => goto telemarket-poli-FreedSoc|s|begin;
+ 5 => goto telemarket-poli-Grassroot|s|begin;
+ 6 => goto telemarket-poli-Green|s|begin;
+ 7 => goto telemarket-poli-Greens|s|begin;
+ 8 => goto telemarket-poli-Independence|s|begin;
+ 9 => goto telemarket-political3|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+context telemarket-political3 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ Playback(telemark-polit3-choices);
+ // if You represent the Independant American Party, dial 1.
+ // if You represent the Labor Party, dial 2.
+ // if You represent the Libertarian Party, dial 3.
+ // if You represent the Light Party, dial 4.
+ // if You represent the Natural Law Party, dial 5.
+ // if You represent the New Party, dial 6.
+ // if You represent the New Union Party, dial 7.
+ // if You represent the Peace and Freedom Party, dial 8.
+ // all others, hang on, dial 9.
+ }
+ 1 => goto telemarket-poli-IndAm|s|begin;
+ 2 => goto telemarket-poli-Labor|s|begin;
+ 3 => goto telemarket-poli-Liber|s|begin;
+ 4 => goto telemarket-poli-Light|s|begin;
+ 5 => goto telemarket-poli-NatLaw|s|begin;
+ 6 => goto telemarket-poli-New|s|begin;
+ 7 => goto telemarket-poli-NewUn|s|begin;
+ 8 => goto telemarket-poli-PeaceFree|s|begin;
+ 9 => goto telemarket-political4|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-political4 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ Playback(telemark-polit4-choices);
+ // if You represent the Prohibition Party, dial 1.
+ // if You represent the Reform Party, dial 2.
+ // if You represent the Revolution , dial 3.
+ // if You represent the Socialist Party USA, dial 4.
+ // if You represent the Socialist Action Party, dial 5.
+ // if You represent the Socialist Equality Party, dial 6.
+ // if You represent the Socialist Labor Party, dial 7.
+ // if You represent the Socialist Workers Party, dial 8.
+ // all others, hang on, and dial 9.
+ }
+ 1 => goto telemarket-poli-Prohib|s|begin;
+ 2 => goto telemarket-poli-Ref|s|begin;
+ 3 => goto telemarket-poli-Revol|s|begin;
+ 4 => goto telemarket-poli-SocPart|s|begin;
+ 5 => goto telemarket-poli-SocAct|s|begin;
+ 6 => goto telemarket-poli-SocEq|s|begin;
+ 7 => goto telemarket-poli-SocLab|s|begin;
+ 8 => goto telemarket-poli-SocWork|s|begin;
+ 9 => goto telemarket-political5|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-political5 {
+ s => {
+ begin:
+ Playback(telemark-politx-intro);
+ Playback(telemark-polit5-choices);
+ // if You represent the Southern Party, dial 1.
+ // if You represent the Southern Independence Party, dial 2.
+ // if You represent the US Pacifist Party, dial 3.
+ // if You represent the We the People Party, dial 4.
+ // if You represent the Workers World Party, dial 5.
+ // if You represent the Democratic Party, dial 6.
+ // if You represent the Republican Party, dial 7.
+ // all others, may dial 8.
+ }
+ 1 => goto telemarket-poli-South|s|begin;
+ 2 => goto telemarket-poli-SoInd|s|begin;
+ 3 => goto telemarket-poli-USPac|s|begin;
+ 4 => goto telemarket-poli-WTP|s|begin;
+ 5 => goto telemarket-poli-WWP|s|begin;
+ 6 => goto telemarket-poli-Democrat|s|begin;
+ 7 => goto telemarket-poli-Repub|s|begin;
+ 8 => goto telemarket-poli-other|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-poli-other {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Repub {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Democrat {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-WWP {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-WTP {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-USPac {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SoInd {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-South {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocWork {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocLab {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocEq {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocAct {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-SocPart {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Revol {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Ref {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Prohib {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-PeaceFree {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-NewUn {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-New {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-NatLaw {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Light {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Liber {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Labor {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-IndAm {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Independence {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Greens {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Green {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Grassroot {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-FreedSoc {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-FamVal {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Constit {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Communist {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-CFP {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-AmRef {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+// BAD DESIGN: Putting in infinite loops in the menus, whether by design or mistake is not nice!
+context telemarket-poli-Pot {
+ s => {
+ begin:
+ goto telemarket-political|s|begin; // will the Pot Party Guys even notice an infinite loop?
+ }
+}
+
+context telemarket-poli-AmNaz {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-AmInd {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-AmHer {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-American {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+context telemarket-poli-Am1st {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+
+context telemarket-pollster {
+ s => {
+ begin:
+ Playback(telemark-poll-intro);
+ // I'm sorry-- We are just not available for doing any polling at the moment. So,
+ // please remove us from your list.
+ goto telemarket-sorry|s|begin;
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-research {
+ s => {
+ begin:
+ Playback(telemark-research-intro);
+ // I'd like to say I'd love to help you with your market survey, but that would be a complete
+ // and total lie. I am not interested in helping you with Market Surveys.
+ //
+ // Please remove me from your call list. It just doesn't pay enough. But Thank you.
+ goto telemarket-sorry|s|begin;
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-magazine {
+ s => {
+ begin:
+ Playback(telemark-mag-choices);
+ // If you are calling to see if I would like a NEW free subscription
+ // to your magazine or newsletter, please dial 1.
+ // If you are calling to see if I want to Renew an existing subscription, please dial 2.
+ // If you are representing some publisher, and want my opinion about something, or are doing
+ // some kind of survey, please dial 3.
+ // If you are calling to verify that some previous caller actually called me, and the
+ // verification information is correct, please dial 4.
+ // and if your call purpose doesn't match any of the above, please dial 5.
+ }
+ 1 => goto telemark-mag-new|s|begin;
+ 2 => goto telemark-mag-renew|s|begin;
+ 3 => goto telemark-mag-survey|s|begin;
+ 4 => goto telemark-mag-verify|s|begin;
+ 5 => goto telemark-mag-other|s|begin;
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-new {
+ s => {
+ begin:
+ Playback(telemark-mag-new);
+ // I'm sorry, I'm maxed out, and the answer is NO.
+ // If you really think I'd LOVE to add your publication to the pile I already get,
+ // Send something via the post. Don't call me.
+ // Thank you. bye.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-renew {
+ s => {
+ begin:
+ Playback(telemark-mag-renew);
+ // So, you want to see if I want to Renew, do you? The answer is most likely "YES".
+ //
+ // But, I will not answer a long list of questions over the phone. Send such
+ // categorization info via the post, and stop bothering me over the phone,
+ // if this is what you want.
+ // Do you need verification information? Normally I opt out of such nonsense, if possible.
+ // If not, use whatever of the following you can:
+ // My birth month is October.
+ // My birthplace is Kigali, in Rwanda, in Afica.
+ // My eye color is orange.
+ // All of these are wonderfully false, but I use them regularly for such purposes. Thank you.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-survey {
+ s => {
+ begin:
+ Playback(telemark-mag-survey);
+ // Sorry, I don't have time to answer survey or opinion questions. Find someone
+ // else to help build your marketing database, I guess. Good Luck.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-verify {
+ s => {
+ begin:
+ Playback(telemark-mag-verify);
+ // If you are calling to verify that your own agents aren't ripping you off,
+ // sorry, I can't help you. I opt out whenever I can, mainly because I'm not
+ // paid enough for this kind of thing. I always lie, and I can't remember
+ // what I might have said. Sorry. Goodbye.
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemark-mag-other {
+ s => {
+ begin:
+ goto telemarket-sorry|s|begin;
+ }
+}
+
+
+
+// BAD DESIGN: Is it entrapment, when you lure telemarketers to reveal their contact information,
+// Just so you can report them to the FTC/FCC? If it is, isn't it unethical for them
+// to hide their CID (via Anonymous, usually), to hide their identities from the public?
+
+// BTW -- What telemarketer would be stupid enough to fall for this? I'll bet not a single one!
+// For that matter, what telemarketer will be stupid enough to even enter any of this? I'll bet not a single one!
+// (but it was fun messing around).
+
+context telemarket-commercial {
+ s => {
+ begin:
+ Playback(telemark-comm-intro); // Script: Please leave your name, organization, and phone number, plus
+ // a short description of the purpose of your call, at the prompt.
+ // We will do our best to respond to your call! And, in the mean time,
+ // do not forget to add us to your no-call list!
+ Voicemail(u82);
+ goto telemarket-sorry|s|begin;
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
+
+
+context telemarket-other {
+ s => {
+ begin:
+ Playback(telemark-other-intro);
+ // Please review the previous menu options, and see if you really don't
+ // fit in one of the previous categories.
+ // If you do not, go ahead, and call me again, and let me know what category
+ // I should have included in the above list. I appreciate this. Thank you much!
+ Hangup();
+ }
+ t => goto telemarket|s|begin;
+ i => goto telemarket|s|begin;
+ o => goto telemarket|s|begin;
+}
diff --git a/trunk/pbx/ael/ael-test/ael-vtest17/extensions.ael b/trunk/pbx/ael/ael-test/ael-vtest17/extensions.ael
new file mode 100644
index 000000000..d13fe99d7
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest17/extensions.ael
@@ -0,0 +1,116 @@
+context dialextens
+{
+ /*
+ 101 thru 123, 149 thru 152
+ */
+ _10X => Dial(Zap/${EXTEN:2},30,tw);
+ _1ZX => Dial(Zap/${EXTEN:1},30,tw);
+}
+/*
+ Due to extenal wiring:
+
+ dialing 125 will ring 101
+ dialing 126 will ring 102
+ and so on until
+ dialing 147 will ring 123
+
+We can dial out on zap 69 thru 72; and 25-47
+
+*/
+
+context dialthrus
+{
+ /* 369-372; 325-347 */
+ _3XX => Dial(Zap/${EXTEN:1},30,tw);
+}
+
+context t1incoming
+{
+ includes
+ {
+ dialextens;
+ parkedcalls;
+ }
+ s => {
+ Answer();
+ Background(welcome-to-test-machine);
+ }
+
+}
+
+context t1extension
+{
+ includes
+ {
+ dialextens;
+ dialthrus;
+ }
+
+}
+
+context incoming
+{
+ includes
+ {
+ dialextens;
+ parkedcalls;
+ }
+ s => {
+ Answer();
+ Background(welcome-to-test-machine);
+ }
+}
+
+context extension
+{
+ includes
+ {
+ dialextens;
+ dialthrus;
+ }
+ 5 => {
+ Record(recording:gsm);
+ Background(recording);
+ }
+
+ 81 => {
+ iterations=1000000;
+ Set(time1=${EPOCH});
+ for(i=1; ${i}<${iterations}; i=${i}+1)
+ {
+ NoOp(Hello);
+ }
+ Set(time2=${EPOCH});
+ Verbose(The time diff is $[${time2} - ${time1} ] seconds);
+ Verbose(Which means that the priorities/sec = $[4* ${iterations} / (${time2} - ${time1}) ]);
+ SayNumber($[4 * ${iterations} / (${time2} - ${time1}) ]);
+ }
+ 82 => {
+ &ndeep(100000);
+ Verbose(Finished 100000 levels deep call!);
+ }
+ 83 => {
+ switch (${EXTEN})
+ {
+ pattern 8X:
+ Verbose(do something to prepare it);
+ pattern 9X:
+ Verbose(handle both 8x and 9x calls);
+ pattern [4-7]X:
+ Verbose(and this too!);
+
+ }
+
+ }
+}
+
+macro ndeep(level)
+{
+ if( ${level} == 0)
+ {
+ Verbose(2|Got to Level 0);
+ return;
+ }
+ &ndeep($[${level}-1]);
+ return;
+}
diff --git a/trunk/pbx/ael/ael-test/ael-vtest21/extensions.ael b/trunk/pbx/ael/ael-test/ael-vtest21/extensions.ael
new file mode 100644
index 000000000..95f25302a
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ael-vtest21/extensions.ael
@@ -0,0 +1,14 @@
+globals {
+ AXLHAFT=wow-to-the-tenth-power;
+ JibberWorthy=zinger3;
+ OFFICE_CODE=503;
+}
+
+context from-enum {
+
+ _${OFFICE_CODE}XXXX => {
+ Answer();
+ goto ${EXTEN:3}|1;
+ }
+}
+
diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest10 b/trunk/pbx/ael/ael-test/ref.ael-ntest10
new file mode 100644
index 000000000..9eb8ac989
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-ntest10
@@ -0,0 +1,171 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)
+
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1-4: The macro endsess does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 6-9: The macro nullchk does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 11-26: The macro endcall does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 28-44: The macro endcall2 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 36-36: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 46-68: The macro endcall3 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 48-48: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 60-60: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 70-96: The macro endcall4 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 72-72: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 84-84: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 87-87: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 98-131: The macro endcall5 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 106-106: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 119-119: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 122-122: A default case was automatically added to the switch.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+Executed ast_context_create(conts, name=endsess, registrar=pbx_ael);
+Executed ast_context_create(conts, name=nullchk, registrar=pbx_ael);
+Executed ast_context_create(conts, name=endcall, registrar=pbx_ael);
+Executed ast_context_create(conts, name=endcall2, registrar=pbx_ael);
+Executed ast_context_create(conts, name=endcall3, registrar=pbx_ael);
+Executed ast_context_create(conts, name=endcall4, registrar=pbx_ael);
+Executed ast_context_create(conts, name=endcall5, registrar=pbx_ael);
+Executed ast_add_extension2(context=endsess, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endsess, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=nullchk, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=nullchk, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=NoOp, data=${type} is this, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=nullchk, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-1-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall-1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=_sw-1-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-1-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:15, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-1-out,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=14, label=(null), callerid=(null), appl=Goto, data=17, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=15, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=16, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=17, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall-out-1-2, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=18, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall, rep=0, exten=sw-1-out, priority=19, label=(null), callerid=(null), appl=Goto, data=sw-1-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-3-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall2-3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=_sw-3-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-3-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out2, priority=10, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out2, priority=11, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out2, priority=12, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out2, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-3-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-3-out2,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall2-out-3-4, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall2, rep=0, exten=sw-3-out, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-3-out2,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-5-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall3-5, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?5:6, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=Goto, data=sw-8-out,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall3-7, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=Goto, data=sw-8-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall3-8, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=_sw-8-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-8-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-8-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-8-out, priority=10, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?11:13, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-8-out, priority=11, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-8-out, priority=12, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-8-out, priority=13, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall3-out-8-9, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-8-out, priority=14, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-8-out, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-8-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=_sw-5-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-5-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-8-out,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall3-out-5-6, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-out, priority=15, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall3, rep=0, exten=sw-5-out, priority=16, label=(null), callerid=(null), appl=Goto, data=sw-5-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-10-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall4-10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?5:6, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=Goto, data=sw-14-in,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall4-12, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=Goto, data=sw-13-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall4-13, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=_sw-13-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-13-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-13-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-13-out, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-14-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-13-out, priority=11, label=(null), callerid=(null), appl=NoOp, data=Finish switch-sw-endcall4-out-13-14, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-13-out, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-13-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=_sw-14-., priority=10, label=(null), callerid=(null), appl=Goto, data=sw-13-out,11, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-14-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-14-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-14-in, priority=10, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?11:13, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-14-in, priority=11, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-14-in, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-13-out,11, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-14-in, priority=13, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-sw-endcall4-out-13-in-14-15, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-14-in, priority=14, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-14-in, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-14-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=_sw-10-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-10-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-14-in,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall4-out-10-11, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-out, priority=15, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall4, rep=0, exten=sw-10-out, priority=16, label=(null), callerid=(null), appl=Goto, data=sw-10-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=1, label=(null), callerid=(null), appl=Set, data=LOCAL(type)=${ARG1}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=2, label=(null), callerid=(null), appl=Goto, data=sw-16-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=3, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall5-16, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=4, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?5:6, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=5, label=(null), callerid=(null), appl=Goto, data=sw-21-in,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish if-endcall5-19, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=7, label=(null), callerid=(null), appl=Goto, data=sw-20-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=8, label=(null), callerid=(null), appl=NoOp, data=Finish switch-endcall5-20, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=s, priority=9, label=(null), callerid=(null), appl=Return, data=, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=_sw-20-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,8, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-20-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-20-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-20-out, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-21-${type},10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-20-out, priority=11, label=(null), callerid=(null), appl=NoOp, data=Finish switch-sw-endcall5-out-20-21, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-20-out, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-20-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=_sw-21-., priority=10, label=(null), callerid=(null), appl=Goto, data=sw-20-out,11, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-21-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-21-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-21-in, priority=10, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?11:13, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-21-in, priority=11, label=ptr1, callerid=(null), appl=Softhangup, data=${CHANNEL}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-21-in, priority=12, label=(null), callerid=(null), appl=Goto, data=sw-20-out,11, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-21-in, priority=13, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-sw-endcall5-out-20-in-21-22, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-21-in, priority=14, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-21-in, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-21-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=_sw-16-., priority=10, label=(null), callerid=(null), appl=Goto, data=s,3, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-, priority=10, label=(null), callerid=(null), appl=Goto, data=sw-16-.|10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-in, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-in, priority=11, label=ptr2, callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-in, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-in, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-21-in,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-in, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall5-in-16-18, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-in, priority=15, label=(null), callerid=(null), appl=Noop, data=esac, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-in, priority=16, label=(null), callerid=(null), appl=Goto, data=sw-16-.,10, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=10, label=(null), callerid=(null), appl=Gosub, data=nullchk,s,1(callid), FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=11, label=(null), callerid=(null), appl=GotoIf, data=$[${testnotnull}]?12:14, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=12, label=(null), callerid=(null), appl=Gosub, data=endsess,s,1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=13, label=(null), callerid=(null), appl=Goto, data=sw-21-in,ptr1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=14, label=(null), callerid=(null), appl=NoOp, data=Finish if-sw-endcall5-out-16-17, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=endcall5, rep=0, exten=sw-16-out, priority=15, label=(null), callerid=(null), appl=Goto, data=sw-16-in,10, FREE, registrar=pbx_ael);
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+Executed ast_merge_contexts_and_delete();
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+Executed ast_walk_contexts();
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 7 contexts, 37 extensions, 131 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest12 b/trunk/pbx/ael/ael-test/ref.ael-ntest12
new file mode 100644
index 000000000..184ce2d6c
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-ntest12
@@ -0,0 +1,30 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)
+
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+Executed ast_context_find_or_create(conts, name=test1, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=1, label=(null), callerid=(null), appl=Set, data=i=$[0], FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=2, label=(null), callerid=(null), appl=GotoIf, data=$[
+ ${i} <= 3]?3:6, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=3, label=(null), callerid=(null), appl=NoOp, data=i is '${i}', FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=4, label=(null), callerid=(null), appl=Set, data=i=$[ ${i} + 1 ], FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=5, label=(null), callerid=(null), appl=Goto, data=2, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=771, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish for-test1-1, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=1, label=(null), callerid=(null), appl=Set, data=i=$[0], FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=2, label=(null), callerid=(null), appl=GotoIf, data=$[ ${i} <= 3]?3:6, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=3, label=(null), callerid=(null), appl=NoOp, data=i is '${i}', FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=4, label=(null), callerid=(null), appl=Set, data=i=$[ ${i} + 1 ], FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=5, label=(null), callerid=(null), appl=Goto, data=2, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=test1, rep=0, exten=772, priority=6, label=(null), callerid=(null), appl=NoOp, data=Finish for-test1-2, FREE, registrar=pbx_ael);
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+Executed ast_merge_contexts_and_delete();
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+Executed ast_walk_contexts();
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 2 extensions, 12 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest22 b/trunk/pbx/ael/ael-test/ref.ael-ntest22
new file mode 100644
index 000000000..c9317f64e
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-ntest22
@@ -0,0 +1,54 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)
+
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t1/a.ael, 41 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t1/b.ael, 42 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t1/c.ael, 106 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t2/d.ael, 41 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t2/e.ael, 42 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t2/f.ael, 82 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././qq.ael, 45 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/g.ael, 41 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/h.ael, 42 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/i.ael, 41 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././t3/j.ael, 43 chars
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+Executed ast_context_find_or_create(conts, name=a, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=b, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=c, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=d, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=e, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=qq, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=f, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=g, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=h, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=i, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=j, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=w, registrar=pbx_ael);
+Executed ast_context_find_or_create(conts, name=z, registrar=pbx_ael);
+Executed ast_add_extension2(context=a, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, a, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=b, rep=0, exten=456, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere, b, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=c, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, c, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=d, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, d, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=e, rep=0, exten=456, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere, e, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=qq, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, qq, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=f, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, f, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=g, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, g, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=h, rep=0, exten=456, priority=1, label=(null), callerid=(null), appl=NoOp, data=hithere, h, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=i, rep=0, exten=134, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, i, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=j, rep=0, exten=567, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, j, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=w, rep=0, exten=890, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, w, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=z, rep=0, exten=123, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, z, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=z, rep=0, exten=124, priority=1, label=(null), callerid=(null), appl=NoOp, data=hi there, z, FREE, registrar=pbx_ael);
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+Executed ast_merge_contexts_and_delete();
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+Executed ast_walk_contexts();
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 13 contexts, 13 extensions, 14 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-ntest9 b/trunk/pbx/ael/ael-test/ref.ael-ntest9
new file mode 100644
index 000000000..e40790e59
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-ntest9
@@ -0,0 +1,23 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)
+
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+Executed ast_context_find_or_create(conts, name=workext, registrar=pbx_ael);
+Executed ast_context_add_ignorepat2(con, value=8, registrar=pbx_ael);
+Executed ast_context_add_ignorepat2(con, value=9, registrar=pbx_ael);
+Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=1, label=(null), callerid=(null), appl=Set, data=QUERYSTRING=SELECT\ foo\,\ bar\ FROM\ foobar, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=2, label=(null), callerid=(null), appl=Verbose, data=2|${QUERYSTRING}, FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=3, label=(null), callerid=(null), appl=Set, data=query=$["SELECT foo\, bar FROM foobar" ], FREE, registrar=pbx_ael);
+Executed ast_add_extension2(context=workext, rep=0, exten=793, priority=4, label=(null), callerid=(null), appl=Verbose, data=2|${query}, FREE, registrar=pbx_ael);
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+Executed ast_merge_contexts_and_delete();
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+Executed ast_walk_contexts();
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 4 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test1 b/trunk/pbx/ael/ael-test/ref.ael-test1
new file mode 100644
index 000000000..5538514fe
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test1
@@ -0,0 +1,18 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2-16: The macro testdial does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 18-25: The macro exten-gen does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:1362 func: check_goto Warning: file ./extensions.ael, line 21-21: It's bad form to have a goto in a macro to a target outside the macro!
+LOG: lev:3 file:pval.c line:1362 func: check_goto Warning: file ./extensions.ael, line 23-23: It's bad form to have a goto in a macro to a target outside the macro!
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 58-58: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:922 func: check_dow Warning: file ./extensions.ael, line 67-67: The day (m0n) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!
+LOG: lev:3 file:pval.c line:880 func: check_timerange Warning: file ./extensions.ael, line 78-78: The end time (25:00) is out of range!
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 5 contexts, 16 extensions, 157 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test11 b/trunk/pbx/ael/ael-test/ref.ael-test11
new file mode 100644
index 000000000..c47f2b8bf
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test11
@@ -0,0 +1,13 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:4 file:pval.c line:1147 func: check_label Error: file ./extensions.ael, line 13-13: Duplicate label lab1! Previously defined at file ./extensions.ael, line 8.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 32-32: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 44-44: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 47-47: A default case was automatically added to the switch.
+LOG: lev:4 file:pval.c line:1147 func: check_label Error: file ./extensions.ael, line 49-49: Duplicate label ptr1! Previously defined at file ./extensions.ael, line 33.
+LOG: lev:4 file:pbx_ael.c line:139 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile.
+LOG: lev:4 file:ael2_parse line:531 func: main 0 contexts, 0 extensions, 0 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test14 b/trunk/pbx/ael/ael-test/ref.ael-test14
new file mode 100644
index 000000000..2cd4ed1b1
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test14
@@ -0,0 +1,11 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 13-13: A default case was automatically added to the switch.
+LOG: lev:4 file:pval.c line:1071 func: check_continue Error: file ./extensions.ael, line 15-15: 'continue' not in 'for' or 'while' statement!
+LOG: lev:4 file:pval.c line:1052 func: check_break Error: file ./extensions.ael, line 17-17: 'break' not in switch, for, or while statement!
+LOG: lev:4 file:pbx_ael.c line:139 func: pbx_load_module Sorry, but 0 syntax errors and 2 semantic errors were detected. It doesn't make sense to compile.
+LOG: lev:4 file:ael2_parse line:531 func: main 0 contexts, 0 extensions, 0 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test15 b/trunk/pbx/ael/ael-test/ref.ael-test15
new file mode 100644
index 000000000..995068563
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test15
@@ -0,0 +1,12 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:4070 func: ast_compile_ael2 Warning: file ./extensions.ael, line 8-13: Empty Extension!
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 0 extensions, 0 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test16 b/trunk/pbx/ael/ael-test/ref.ael-test16
new file mode 100644
index 000000000..f43257d46
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test16
@@ -0,0 +1,12 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:3786 func: add_extensions This file is Empty!
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 0 extensions, 0 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test18 b/trunk/pbx/ael/ael-test/ref.ael-test18
new file mode 100644
index 000000000..072135ef2
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test18
@@ -0,0 +1,11 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 7 extensions, 27 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test19 b/trunk/pbx/ael/ael-test/ref.ael-test19
new file mode 100644
index 000000000..e093d75e5
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test19
@@ -0,0 +1,18 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:2300 func: check_context_names Warning: file ./extensions.ael, line 49-62: The context name (incoming) is also declared in file ./extensions.ael, line 62-69! (and neither is marked 'extend')
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 71-175: The macro std-priv-exten does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:2284 func: check_switch_expr Warning: file ./extensions.ael, line 245-246: A default case was automatically added to the switch.
+LOG: lev:3 file:pval.c line:2426 func: check_pval_item Warning: file ./extensions.ael, line 312-312: macro call to non-existent funcA! (Not even in the extensions.conf stuff!)
+LOG: lev:3 file:pval.c line:2426 func: check_pval_item Warning: file ./extensions.ael, line 313-313: macro call to non-existent funcD! (Not even in the extensions.conf stuff!)
+LOG: lev:3 file:pval.c line:1346 func: check_goto Warning: file ./extensions.ael, line 319-319: goto: Couldn't find goto target test5|s|1, not even in extensions.conf!
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 363-366: The macro dialoutpstn does not end with a return; I will insert one.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 13 contexts, 57 extensions, 188 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test2 b/trunk/pbx/ael/ael-test/ref.ael-test2
new file mode 100644
index 000000000..892b63464
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test2
@@ -0,0 +1,28 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././apptest.ael2, 3474 chars
+LOG: lev:3 file:ael.y line:546 func: ael_yyparse ==== File: ././apptest.ael2, Line 46, Cols: 8-11: Suggestion: Use the goto statement instead of the Goto() application call in AEL.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:1346 func: check_goto Warning: file ././apptest.ael2, line 46-46: goto: Couldn't find goto target cont|exten|prior, not even in extensions.conf!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2512 func: check_pval_item Warning: file ././apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement.
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 142 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test20 b/trunk/pbx/ael/ael-test/ref.ael-test20
new file mode 100644
index 000000000..065281f94
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test20
@@ -0,0 +1,11 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 1 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test3 b/trunk/pbx/ael/ael-test/ref.ael-test3
new file mode 100644
index 000000000..5ff89ef8b
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test3
@@ -0,0 +1,99 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include1.ael2, 78 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include2.ael2, 98 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include3.ael2, 57 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include5.ael2, 56 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././include4.ael2, 87 chars
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././telemarket_torture.ael2, 28036 chars
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 14-34: The macro std-exten does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:1362 func: check_goto Warning: file ./extensions.ael, line 17-17: It's bad form to have a goto in a macro to a target outside the macro!
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 36-59: The macro std-priv-exten_1 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 62-85: The macro std-priv-exten_2 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 88-111: The macro std-priv-exten_3 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 114-137: The macro std-priv-exten_4 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 140-163: The macro std-priv-exten_5 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 166-189: The macro std-priv-exten_6 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 192-215: The macro std-priv-exten_7 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 218-241: The macro std-priv-exten_8 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 244-267: The macro std-priv-exten_9 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 270-293: The macro std-priv-exten_10 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 296-319: The macro std-priv-exten_11 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 322-345: The macro std-priv-exten_12 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 348-371: The macro std-priv-exten_13 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 374-397: The macro std-priv-exten_14 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 400-423: The macro std-priv-exten_15 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 426-449: The macro std-priv-exten_16 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 452-475: The macro std-priv-exten_17 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 478-501: The macro std-priv-exten_18 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 504-527: The macro std-priv-exten_19 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 530-553: The macro std-priv-exten_20 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 556-579: The macro std-priv-exten_21 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 582-605: The macro std-priv-exten_22 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 608-631: The macro std-priv-exten_23 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 634-657: The macro std-priv-exten_24 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 660-683: The macro std-priv-exten_25 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 686-709: The macro std-priv-exten_26 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 712-735: The macro std-priv-exten_27 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 738-761: The macro std-priv-exten_28 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 764-787: The macro std-priv-exten_29 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 790-813: The macro std-priv-exten_30 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 816-839: The macro std-priv-exten_31 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 842-865: The macro std-priv-exten_32 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 868-891: The macro std-priv-exten_33 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 894-917: The macro std-priv-exten_34 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 920-943: The macro std-priv-exten_35 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 946-969: The macro std-priv-exten_36 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 972-995: The macro std-priv-exten_37 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 998-1021: The macro std-priv-exten_38 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1024-1047: The macro std-priv-exten_39 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1050-1073: The macro std-priv-exten_40 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1076-1099: The macro std-priv-exten_41 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1102-1125: The macro std-priv-exten_42 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1128-1151: The macro std-priv-exten_43 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1154-1177: The macro std-priv-exten_44 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1180-1203: The macro std-priv-exten_45 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1206-1229: The macro std-priv-exten_46 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1232-1255: The macro std-priv-exten_47 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1258-1281: The macro std-priv-exten_48 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1284-1307: The macro std-priv-exten_49 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1310-1333: The macro std-priv-exten_50 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1336-1359: The macro std-priv-exten_51 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1362-1385: The macro std-priv-exten_52 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1388-1411: The macro std-priv-exten_53 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1414-1437: The macro std-priv-exten_54 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1440-1463: The macro std-priv-exten_55 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1466-1489: The macro std-priv-exten_56 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1492-1515: The macro std-priv-exten_57 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1518-1541: The macro std-priv-exten_58 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1544-1567: The macro std-priv-exten_59 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1570-1593: The macro std-priv-exten_60 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1596-1619: The macro std-priv-exten_61 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1622-1645: The macro std-priv-exten_62 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1648-1671: The macro std-priv-exten_63 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1674-1697: The macro std-priv-exten_64 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1700-1723: The macro std-priv-exten_65 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1726-1749: The macro std-priv-exten_66 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1752-1775: The macro std-priv-exten_67 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1778-1801: The macro std-priv-exten_68 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1804-1827: The macro std-priv-exten_69 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1830-1853: The macro std-priv-exten_70 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1856-1879: The macro std-priv-exten_71 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1882-1905: The macro std-priv-exten_72 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1908-1931: The macro std-priv-exten_73 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1934-1957: The macro std-priv-exten does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1959-1995: The macro fillcidname does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 1997-2015: The macro ciddial does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2017-2028: The macro ciddial3 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2030-2048: The macro ciddial2 does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2050-2065: The macro callerid-liar does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 2067-2072: The macro callerid-bad does not end with a return; I will insert one.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 172 contexts, 934 extensions, 2482 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test4 b/trunk/pbx/ael/ael-test/ref.ael-test4
new file mode 100644
index 000000000..892b63464
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test4
@@ -0,0 +1,28 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:ael.flex line:662 func: setup_filestack --Read in included file ././apptest.ael2, 3474 chars
+LOG: lev:3 file:ael.y line:546 func: ael_yyparse ==== File: ././apptest.ael2, Line 46, Cols: 8-11: Suggestion: Use the goto statement instead of the Goto() application call in AEL.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 35-35: application call to EndWhile affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 37-37: application call to ExecIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 38-38: application call to ExecIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 44-44: application call to Gosub affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 45-45: application call to GosubIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:1346 func: check_goto Warning: file ././apptest.ael2, line 46-46: goto: Couldn't find goto target cont|exten|prior, not even in extensions.conf!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 47-47: application call to GotoIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 48-48: application call to GotoIfTime affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 58-58: application call to Macro affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2512 func: check_pval_item Warning: file ././apptest.ael2, line 59-59: I am converting the MacroExit call here to a return statement.
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 60-60: application call to MacroIf affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 85-85: application call to Random affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 94-94: application call to Return affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 119-119: application call to StackPop affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:3 file:pval.c line:2507 func: check_pval_item Warning: file ././apptest.ael2, line 141-141: application call to While affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 1 extensions, 142 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test5 b/trunk/pbx/ael/ael-test/ref.ael-test5
new file mode 100644
index 000000000..79c327413
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test5
@@ -0,0 +1,14 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 130-183: The macro stdexten does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 185-192: The macro uvm does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 194-201: The macro bvm does not end with a return; I will insert one.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 38 contexts, 91 extensions, 493 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test6 b/trunk/pbx/ael/ael-test/ref.ael-test6
new file mode 100644
index 000000000..968c7c71e
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test6
@@ -0,0 +1,24 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:4 file:ael.flex line:288 func: ael_yylex File=./extensions.ael, line=165, column=49: Mismatched '}' in expression!
+LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 165, Cols: 51-51: Error: syntax error, unexpected '=', expecting ')'
+LOG: lev:4 file:ael.flex line:288 func: ael_yylex File=./extensions.ael, line=174, column=49: Mismatched '}' in expression!
+LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 174, Cols: 51-51: Error: syntax error, unexpected '=', expecting ')'
+LOG: lev:4 file:ael.flex line:288 func: ael_yylex File=./extensions.ael, line=222, column=41: Mismatched '}' in expression!
+LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 222, Cols: 43-43: Error: syntax error, unexpected '=', expecting ')'
+LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 291, Cols: 21-28: Error: syntax error, unexpected word, expecting '(' or ';' or '=' or ':'
+LOG: lev:4 file:ael.y line:772 func: ael_yyerror ==== File: ./extensions.ael, Line 291, Cols: 32-32: Error: syntax error, unexpected '|', expecting '(' or ';' or '=' or ':'
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 116-125: The macro dialout does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 129-182: The macro stdexten does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 184-191: The macro uvm does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 193-200: The macro bvm does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 202-207: The macro checkdnd does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 209-216: The macro checkcf does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 218-230: The macro checkcfb does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 688-694: The macro check-psd-exists does not end with a return; I will insert one.
+LOG: lev:4 file:pbx_ael.c line:139 func: pbx_load_module Sorry, but 5 syntax errors and 0 semantic errors were detected. It doesn't make sense to compile.
+LOG: lev:4 file:ael2_parse line:531 func: main 0 contexts, 0 extensions, 0 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test7 b/trunk/pbx/ael/ael-test/ref.ael-test7
new file mode 100644
index 000000000..69f0c9f91
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test7
@@ -0,0 +1,19 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 22-42: The macro stdexten does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 44-49: The macro announce_minutes does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 59-89: The macro checkanddial does not end with a return; I will insert one.
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 91-100: The macro trunkdial does not end with a return; I will insert one.
+LOG: lev:4 file:pval.c line:2468 func: check_pval_item Error: file ./extensions.ael, line 98-98: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 102-112: The macro checklocal does not end with a return; I will insert one.
+LOG: lev:4 file:pval.c line:2468 func: check_pval_item Error: file ./extensions.ael, line 107-107: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments
+LOG: lev:3 file:pval.c line:671 func: check_macro_returns Warning: file ./extensions.ael, line 114-119: The macro autodial does not end with a return; I will insert one.
+LOG: lev:4 file:pval.c line:2468 func: check_pval_item Error: file ./extensions.ael, line 284-284: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments
+LOG: lev:4 file:pval.c line:2468 func: check_pval_item Error: file ./extensions.ael, line 287-287: The macro call to checkanddial has 5 arguments, but the macro definition has 7 arguments
+LOG: lev:3 file:pval.c line:2426 func: check_pval_item Warning: file ./extensions.ael, line 452-452: macro call to non-existent std-exten-ael! (Not even in the extensions.conf stuff!)
+LOG: lev:4 file:pbx_ael.c line:139 func: pbx_load_module Sorry, but 0 syntax errors and 4 semantic errors were detected. It doesn't make sense to compile.
+LOG: lev:4 file:ael2_parse line:531 func: main 0 contexts, 0 extensions, 0 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-test8 b/trunk/pbx/ael/ael-test/ref.ael-test8
new file mode 100644
index 000000000..4e5d0aa37
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-test8
@@ -0,0 +1,11 @@
+
+(If you find progress and other non-error messages irritating, you can use -q to suppress them)
+
+(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)
+LOG: lev:2 file:pbx_ael.c line:113 func: pbx_load_module Starting AEL load process.
+LOG: lev:2 file:pbx_ael.c line:126 func: pbx_load_module AEL load process: parsed config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:129 func: pbx_load_module AEL load process: checked config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:131 func: pbx_load_module AEL load process: compiled config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:134 func: pbx_load_module AEL load process: merged config file name './extensions.ael'.
+LOG: lev:2 file:pbx_ael.c line:137 func: pbx_load_module AEL load process: verified config file name './extensions.ael'.
+LOG: lev:4 file:ael2_parse line:531 func: main 1 contexts, 7 extensions, 17 priorities
diff --git a/trunk/pbx/ael/ael-test/ref.ael-vtest13 b/trunk/pbx/ael/ael-test/ref.ael-vtest13
new file mode 100644
index 000000000..989a18258
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-vtest13
@@ -0,0 +1,3030 @@
+[globals]
+static=yes
+writeprotect=yes
+CONSOLE=Console/dsp
+IAXINFO=murf:tlhfckoct
+FWDNUMBER=544788
+FWDCIDNAME="Joe-Worker"
+FWDPASSWORD=zingledoodle
+FWDRINGS=Zap/6
+FWDVMBOX=1
+
+
+[std-exten]
+exten => s,1,Set(LOCAL(ext)=${ARG1})
+exten => s,2,Set(LOCAL(dev)=${ARG2})
+exten => s,3,Dial(${dev}/${ext}\,20)
+exten => s,4,Goto(sw-1-${DIALSTATUS}\,10)
+exten => s,5,NoOp(Finish switch-std-exten-1)
+exten => s,6,Return()
+exten => a,1,VoiceMailMain(${ext})
+exten => _sw-1-.,10,Voicemail(u${ext})
+exten => _sw-1-.,11,Goto(s\,5)
+exten => sw-1-,10,Goto(sw-1-.|10)
+exten => sw-1-ANSWER,10,Goto(s\,5)
+exten => sw-1-NOANSWER,10,Voicemail(u${ext})
+exten => sw-1-NOANSWER,11,Goto(s\,5)
+exten => sw-1-BUSY,10,Voicemail(b${ext})
+exten => sw-1-BUSY,11,Goto(s\,5)
+
+
+[std-priv-exten_1]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-3-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_1-3)
+exten => s,11,Return()
+exten => _sw-3-.,10,Voicemail(u${ext})
+exten => _sw-3-.,11,Goto(s\,10)
+exten => sw-3-,10,Goto(sw-3-.|10)
+exten => sw-3-NOANSWER,10,Voicemail(u${ext})
+exten => sw-3-NOANSWER,11,Goto(s\,10)
+exten => sw-3-ANSWER,10,Goto(s\,10)
+exten => sw-3-BUSY,10,Voicemail(b${ext})
+exten => sw-3-BUSY,11,Goto(s\,10)
+exten => sw-3-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-3-DONTCALL,11,Goto(s\,10)
+exten => sw-3-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-3-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_2]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-4-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_2-4)
+exten => s,11,Return()
+exten => _sw-4-.,10,Voicemail(u${ext})
+exten => _sw-4-.,11,Goto(s\,10)
+exten => sw-4-,10,Goto(sw-4-.|10)
+exten => sw-4-NOANSWER,10,Voicemail(u${ext})
+exten => sw-4-NOANSWER,11,Goto(s\,10)
+exten => sw-4-ANSWER,10,Goto(s\,10)
+exten => sw-4-BUSY,10,Voicemail(b${ext})
+exten => sw-4-BUSY,11,Goto(s\,10)
+exten => sw-4-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-4-DONTCALL,11,Goto(s\,10)
+exten => sw-4-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-4-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_3]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-5-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_3-5)
+exten => s,11,Return()
+exten => _sw-5-.,10,Voicemail(u${ext})
+exten => _sw-5-.,11,Goto(s\,10)
+exten => sw-5-,10,Goto(sw-5-.|10)
+exten => sw-5-NOANSWER,10,Voicemail(u${ext})
+exten => sw-5-NOANSWER,11,Goto(s\,10)
+exten => sw-5-ANSWER,10,Goto(s\,10)
+exten => sw-5-BUSY,10,Voicemail(b${ext})
+exten => sw-5-BUSY,11,Goto(s\,10)
+exten => sw-5-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-5-DONTCALL,11,Goto(s\,10)
+exten => sw-5-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-5-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_4]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-6-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_4-6)
+exten => s,11,Return()
+exten => _sw-6-.,10,Voicemail(u${ext})
+exten => _sw-6-.,11,Goto(s\,10)
+exten => sw-6-,10,Goto(sw-6-.|10)
+exten => sw-6-NOANSWER,10,Voicemail(u${ext})
+exten => sw-6-NOANSWER,11,Goto(s\,10)
+exten => sw-6-ANSWER,10,Goto(s\,10)
+exten => sw-6-BUSY,10,Voicemail(b${ext})
+exten => sw-6-BUSY,11,Goto(s\,10)
+exten => sw-6-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-6-DONTCALL,11,Goto(s\,10)
+exten => sw-6-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-6-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_5]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-7-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_5-7)
+exten => s,11,Return()
+exten => _sw-7-.,10,Voicemail(u${ext})
+exten => _sw-7-.,11,Goto(s\,10)
+exten => sw-7-,10,Goto(sw-7-.|10)
+exten => sw-7-NOANSWER,10,Voicemail(u${ext})
+exten => sw-7-NOANSWER,11,Goto(s\,10)
+exten => sw-7-ANSWER,10,Goto(s\,10)
+exten => sw-7-BUSY,10,Voicemail(b${ext})
+exten => sw-7-BUSY,11,Goto(s\,10)
+exten => sw-7-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-7-DONTCALL,11,Goto(s\,10)
+exten => sw-7-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-7-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_6]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-8-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_6-8)
+exten => s,11,Return()
+exten => _sw-8-.,10,Voicemail(u${ext})
+exten => _sw-8-.,11,Goto(s\,10)
+exten => sw-8-,10,Goto(sw-8-.|10)
+exten => sw-8-NOANSWER,10,Voicemail(u${ext})
+exten => sw-8-NOANSWER,11,Goto(s\,10)
+exten => sw-8-ANSWER,10,Goto(s\,10)
+exten => sw-8-BUSY,10,Voicemail(b${ext})
+exten => sw-8-BUSY,11,Goto(s\,10)
+exten => sw-8-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-8-DONTCALL,11,Goto(s\,10)
+exten => sw-8-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-8-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_7]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-9-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_7-9)
+exten => s,11,Return()
+exten => _sw-9-.,10,Voicemail(u${ext})
+exten => _sw-9-.,11,Goto(s\,10)
+exten => sw-9-,10,Goto(sw-9-.|10)
+exten => sw-9-NOANSWER,10,Voicemail(u${ext})
+exten => sw-9-NOANSWER,11,Goto(s\,10)
+exten => sw-9-ANSWER,10,Goto(s\,10)
+exten => sw-9-BUSY,10,Voicemail(b${ext})
+exten => sw-9-BUSY,11,Goto(s\,10)
+exten => sw-9-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-9-DONTCALL,11,Goto(s\,10)
+exten => sw-9-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-9-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_8]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-10-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_8-10)
+exten => s,11,Return()
+exten => _sw-10-.,10,Voicemail(u${ext})
+exten => _sw-10-.,11,Goto(s\,10)
+exten => sw-10-,10,Goto(sw-10-.|10)
+exten => sw-10-NOANSWER,10,Voicemail(u${ext})
+exten => sw-10-NOANSWER,11,Goto(s\,10)
+exten => sw-10-ANSWER,10,Goto(s\,10)
+exten => sw-10-BUSY,10,Voicemail(b${ext})
+exten => sw-10-BUSY,11,Goto(s\,10)
+exten => sw-10-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-10-DONTCALL,11,Goto(s\,10)
+exten => sw-10-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-10-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_9]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-11-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_9-11)
+exten => s,11,Return()
+exten => _sw-11-.,10,Voicemail(u${ext})
+exten => _sw-11-.,11,Goto(s\,10)
+exten => sw-11-,10,Goto(sw-11-.|10)
+exten => sw-11-NOANSWER,10,Voicemail(u${ext})
+exten => sw-11-NOANSWER,11,Goto(s\,10)
+exten => sw-11-ANSWER,10,Goto(s\,10)
+exten => sw-11-BUSY,10,Voicemail(b${ext})
+exten => sw-11-BUSY,11,Goto(s\,10)
+exten => sw-11-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-11-DONTCALL,11,Goto(s\,10)
+exten => sw-11-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-11-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_10]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-12-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_10-12)
+exten => s,11,Return()
+exten => _sw-12-.,10,Voicemail(u${ext})
+exten => _sw-12-.,11,Goto(s\,10)
+exten => sw-12-,10,Goto(sw-12-.|10)
+exten => sw-12-NOANSWER,10,Voicemail(u${ext})
+exten => sw-12-NOANSWER,11,Goto(s\,10)
+exten => sw-12-ANSWER,10,Goto(s\,10)
+exten => sw-12-BUSY,10,Voicemail(b${ext})
+exten => sw-12-BUSY,11,Goto(s\,10)
+exten => sw-12-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-12-DONTCALL,11,Goto(s\,10)
+exten => sw-12-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-12-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_11]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-13-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_11-13)
+exten => s,11,Return()
+exten => _sw-13-.,10,Voicemail(u${ext})
+exten => _sw-13-.,11,Goto(s\,10)
+exten => sw-13-,10,Goto(sw-13-.|10)
+exten => sw-13-NOANSWER,10,Voicemail(u${ext})
+exten => sw-13-NOANSWER,11,Goto(s\,10)
+exten => sw-13-ANSWER,10,Goto(s\,10)
+exten => sw-13-BUSY,10,Voicemail(b${ext})
+exten => sw-13-BUSY,11,Goto(s\,10)
+exten => sw-13-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-13-DONTCALL,11,Goto(s\,10)
+exten => sw-13-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-13-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_12]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-14-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_12-14)
+exten => s,11,Return()
+exten => _sw-14-.,10,Voicemail(u${ext})
+exten => _sw-14-.,11,Goto(s\,10)
+exten => sw-14-,10,Goto(sw-14-.|10)
+exten => sw-14-NOANSWER,10,Voicemail(u${ext})
+exten => sw-14-NOANSWER,11,Goto(s\,10)
+exten => sw-14-ANSWER,10,Goto(s\,10)
+exten => sw-14-BUSY,10,Voicemail(b${ext})
+exten => sw-14-BUSY,11,Goto(s\,10)
+exten => sw-14-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-14-DONTCALL,11,Goto(s\,10)
+exten => sw-14-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-14-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_13]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-15-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_13-15)
+exten => s,11,Return()
+exten => _sw-15-.,10,Voicemail(u${ext})
+exten => _sw-15-.,11,Goto(s\,10)
+exten => sw-15-,10,Goto(sw-15-.|10)
+exten => sw-15-NOANSWER,10,Voicemail(u${ext})
+exten => sw-15-NOANSWER,11,Goto(s\,10)
+exten => sw-15-ANSWER,10,Goto(s\,10)
+exten => sw-15-BUSY,10,Voicemail(b${ext})
+exten => sw-15-BUSY,11,Goto(s\,10)
+exten => sw-15-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-15-DONTCALL,11,Goto(s\,10)
+exten => sw-15-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-15-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_14]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-16-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_14-16)
+exten => s,11,Return()
+exten => _sw-16-.,10,Voicemail(u${ext})
+exten => _sw-16-.,11,Goto(s\,10)
+exten => sw-16-,10,Goto(sw-16-.|10)
+exten => sw-16-NOANSWER,10,Voicemail(u${ext})
+exten => sw-16-NOANSWER,11,Goto(s\,10)
+exten => sw-16-ANSWER,10,Goto(s\,10)
+exten => sw-16-BUSY,10,Voicemail(b${ext})
+exten => sw-16-BUSY,11,Goto(s\,10)
+exten => sw-16-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-16-DONTCALL,11,Goto(s\,10)
+exten => sw-16-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-16-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_15]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-17-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_15-17)
+exten => s,11,Return()
+exten => _sw-17-.,10,Voicemail(u${ext})
+exten => _sw-17-.,11,Goto(s\,10)
+exten => sw-17-,10,Goto(sw-17-.|10)
+exten => sw-17-NOANSWER,10,Voicemail(u${ext})
+exten => sw-17-NOANSWER,11,Goto(s\,10)
+exten => sw-17-ANSWER,10,Goto(s\,10)
+exten => sw-17-BUSY,10,Voicemail(b${ext})
+exten => sw-17-BUSY,11,Goto(s\,10)
+exten => sw-17-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-17-DONTCALL,11,Goto(s\,10)
+exten => sw-17-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-17-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_16]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-18-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_16-18)
+exten => s,11,Return()
+exten => _sw-18-.,10,Voicemail(u${ext})
+exten => _sw-18-.,11,Goto(s\,10)
+exten => sw-18-,10,Goto(sw-18-.|10)
+exten => sw-18-NOANSWER,10,Voicemail(u${ext})
+exten => sw-18-NOANSWER,11,Goto(s\,10)
+exten => sw-18-ANSWER,10,Goto(s\,10)
+exten => sw-18-BUSY,10,Voicemail(b${ext})
+exten => sw-18-BUSY,11,Goto(s\,10)
+exten => sw-18-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-18-DONTCALL,11,Goto(s\,10)
+exten => sw-18-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-18-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_17]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-19-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_17-19)
+exten => s,11,Return()
+exten => _sw-19-.,10,Voicemail(u${ext})
+exten => _sw-19-.,11,Goto(s\,10)
+exten => sw-19-,10,Goto(sw-19-.|10)
+exten => sw-19-NOANSWER,10,Voicemail(u${ext})
+exten => sw-19-NOANSWER,11,Goto(s\,10)
+exten => sw-19-ANSWER,10,Goto(s\,10)
+exten => sw-19-BUSY,10,Voicemail(b${ext})
+exten => sw-19-BUSY,11,Goto(s\,10)
+exten => sw-19-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-19-DONTCALL,11,Goto(s\,10)
+exten => sw-19-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-19-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_18]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-20-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_18-20)
+exten => s,11,Return()
+exten => _sw-20-.,10,Voicemail(u${ext})
+exten => _sw-20-.,11,Goto(s\,10)
+exten => sw-20-,10,Goto(sw-20-.|10)
+exten => sw-20-NOANSWER,10,Voicemail(u${ext})
+exten => sw-20-NOANSWER,11,Goto(s\,10)
+exten => sw-20-ANSWER,10,Goto(s\,10)
+exten => sw-20-BUSY,10,Voicemail(b${ext})
+exten => sw-20-BUSY,11,Goto(s\,10)
+exten => sw-20-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-20-DONTCALL,11,Goto(s\,10)
+exten => sw-20-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-20-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_19]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-21-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_19-21)
+exten => s,11,Return()
+exten => _sw-21-.,10,Voicemail(u${ext})
+exten => _sw-21-.,11,Goto(s\,10)
+exten => sw-21-,10,Goto(sw-21-.|10)
+exten => sw-21-NOANSWER,10,Voicemail(u${ext})
+exten => sw-21-NOANSWER,11,Goto(s\,10)
+exten => sw-21-ANSWER,10,Goto(s\,10)
+exten => sw-21-BUSY,10,Voicemail(b${ext})
+exten => sw-21-BUSY,11,Goto(s\,10)
+exten => sw-21-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-21-DONTCALL,11,Goto(s\,10)
+exten => sw-21-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-21-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_20]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-22-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_20-22)
+exten => s,11,Return()
+exten => _sw-22-.,10,Voicemail(u${ext})
+exten => _sw-22-.,11,Goto(s\,10)
+exten => sw-22-,10,Goto(sw-22-.|10)
+exten => sw-22-NOANSWER,10,Voicemail(u${ext})
+exten => sw-22-NOANSWER,11,Goto(s\,10)
+exten => sw-22-ANSWER,10,Goto(s\,10)
+exten => sw-22-BUSY,10,Voicemail(b${ext})
+exten => sw-22-BUSY,11,Goto(s\,10)
+exten => sw-22-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-22-DONTCALL,11,Goto(s\,10)
+exten => sw-22-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-22-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_21]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-23-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_21-23)
+exten => s,11,Return()
+exten => _sw-23-.,10,Voicemail(u${ext})
+exten => _sw-23-.,11,Goto(s\,10)
+exten => sw-23-,10,Goto(sw-23-.|10)
+exten => sw-23-NOANSWER,10,Voicemail(u${ext})
+exten => sw-23-NOANSWER,11,Goto(s\,10)
+exten => sw-23-ANSWER,10,Goto(s\,10)
+exten => sw-23-BUSY,10,Voicemail(b${ext})
+exten => sw-23-BUSY,11,Goto(s\,10)
+exten => sw-23-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-23-DONTCALL,11,Goto(s\,10)
+exten => sw-23-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-23-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_22]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-24-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_22-24)
+exten => s,11,Return()
+exten => _sw-24-.,10,Voicemail(u${ext})
+exten => _sw-24-.,11,Goto(s\,10)
+exten => sw-24-,10,Goto(sw-24-.|10)
+exten => sw-24-NOANSWER,10,Voicemail(u${ext})
+exten => sw-24-NOANSWER,11,Goto(s\,10)
+exten => sw-24-ANSWER,10,Goto(s\,10)
+exten => sw-24-BUSY,10,Voicemail(b${ext})
+exten => sw-24-BUSY,11,Goto(s\,10)
+exten => sw-24-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-24-DONTCALL,11,Goto(s\,10)
+exten => sw-24-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-24-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_23]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-25-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_23-25)
+exten => s,11,Return()
+exten => _sw-25-.,10,Voicemail(u${ext})
+exten => _sw-25-.,11,Goto(s\,10)
+exten => sw-25-,10,Goto(sw-25-.|10)
+exten => sw-25-NOANSWER,10,Voicemail(u${ext})
+exten => sw-25-NOANSWER,11,Goto(s\,10)
+exten => sw-25-ANSWER,10,Goto(s\,10)
+exten => sw-25-BUSY,10,Voicemail(b${ext})
+exten => sw-25-BUSY,11,Goto(s\,10)
+exten => sw-25-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-25-DONTCALL,11,Goto(s\,10)
+exten => sw-25-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-25-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_24]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-26-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_24-26)
+exten => s,11,Return()
+exten => _sw-26-.,10,Voicemail(u${ext})
+exten => _sw-26-.,11,Goto(s\,10)
+exten => sw-26-,10,Goto(sw-26-.|10)
+exten => sw-26-NOANSWER,10,Voicemail(u${ext})
+exten => sw-26-NOANSWER,11,Goto(s\,10)
+exten => sw-26-ANSWER,10,Goto(s\,10)
+exten => sw-26-BUSY,10,Voicemail(b${ext})
+exten => sw-26-BUSY,11,Goto(s\,10)
+exten => sw-26-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-26-DONTCALL,11,Goto(s\,10)
+exten => sw-26-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-26-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_25]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-27-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_25-27)
+exten => s,11,Return()
+exten => _sw-27-.,10,Voicemail(u${ext})
+exten => _sw-27-.,11,Goto(s\,10)
+exten => sw-27-,10,Goto(sw-27-.|10)
+exten => sw-27-NOANSWER,10,Voicemail(u${ext})
+exten => sw-27-NOANSWER,11,Goto(s\,10)
+exten => sw-27-ANSWER,10,Goto(s\,10)
+exten => sw-27-BUSY,10,Voicemail(b${ext})
+exten => sw-27-BUSY,11,Goto(s\,10)
+exten => sw-27-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-27-DONTCALL,11,Goto(s\,10)
+exten => sw-27-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-27-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_26]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-28-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_26-28)
+exten => s,11,Return()
+exten => _sw-28-.,10,Voicemail(u${ext})
+exten => _sw-28-.,11,Goto(s\,10)
+exten => sw-28-,10,Goto(sw-28-.|10)
+exten => sw-28-NOANSWER,10,Voicemail(u${ext})
+exten => sw-28-NOANSWER,11,Goto(s\,10)
+exten => sw-28-ANSWER,10,Goto(s\,10)
+exten => sw-28-BUSY,10,Voicemail(b${ext})
+exten => sw-28-BUSY,11,Goto(s\,10)
+exten => sw-28-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-28-DONTCALL,11,Goto(s\,10)
+exten => sw-28-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-28-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_27]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-29-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_27-29)
+exten => s,11,Return()
+exten => _sw-29-.,10,Voicemail(u${ext})
+exten => _sw-29-.,11,Goto(s\,10)
+exten => sw-29-,10,Goto(sw-29-.|10)
+exten => sw-29-NOANSWER,10,Voicemail(u${ext})
+exten => sw-29-NOANSWER,11,Goto(s\,10)
+exten => sw-29-ANSWER,10,Goto(s\,10)
+exten => sw-29-BUSY,10,Voicemail(b${ext})
+exten => sw-29-BUSY,11,Goto(s\,10)
+exten => sw-29-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-29-DONTCALL,11,Goto(s\,10)
+exten => sw-29-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-29-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_28]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-30-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_28-30)
+exten => s,11,Return()
+exten => _sw-30-.,10,Voicemail(u${ext})
+exten => _sw-30-.,11,Goto(s\,10)
+exten => sw-30-,10,Goto(sw-30-.|10)
+exten => sw-30-NOANSWER,10,Voicemail(u${ext})
+exten => sw-30-NOANSWER,11,Goto(s\,10)
+exten => sw-30-ANSWER,10,Goto(s\,10)
+exten => sw-30-BUSY,10,Voicemail(b${ext})
+exten => sw-30-BUSY,11,Goto(s\,10)
+exten => sw-30-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-30-DONTCALL,11,Goto(s\,10)
+exten => sw-30-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-30-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_29]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-31-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_29-31)
+exten => s,11,Return()
+exten => _sw-31-.,10,Voicemail(u${ext})
+exten => _sw-31-.,11,Goto(s\,10)
+exten => sw-31-,10,Goto(sw-31-.|10)
+exten => sw-31-NOANSWER,10,Voicemail(u${ext})
+exten => sw-31-NOANSWER,11,Goto(s\,10)
+exten => sw-31-ANSWER,10,Goto(s\,10)
+exten => sw-31-BUSY,10,Voicemail(b${ext})
+exten => sw-31-BUSY,11,Goto(s\,10)
+exten => sw-31-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-31-DONTCALL,11,Goto(s\,10)
+exten => sw-31-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-31-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_30]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-32-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_30-32)
+exten => s,11,Return()
+exten => _sw-32-.,10,Voicemail(u${ext})
+exten => _sw-32-.,11,Goto(s\,10)
+exten => sw-32-,10,Goto(sw-32-.|10)
+exten => sw-32-NOANSWER,10,Voicemail(u${ext})
+exten => sw-32-NOANSWER,11,Goto(s\,10)
+exten => sw-32-ANSWER,10,Goto(s\,10)
+exten => sw-32-BUSY,10,Voicemail(b${ext})
+exten => sw-32-BUSY,11,Goto(s\,10)
+exten => sw-32-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-32-DONTCALL,11,Goto(s\,10)
+exten => sw-32-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-32-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_31]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-33-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_31-33)
+exten => s,11,Return()
+exten => _sw-33-.,10,Voicemail(u${ext})
+exten => _sw-33-.,11,Goto(s\,10)
+exten => sw-33-,10,Goto(sw-33-.|10)
+exten => sw-33-NOANSWER,10,Voicemail(u${ext})
+exten => sw-33-NOANSWER,11,Goto(s\,10)
+exten => sw-33-ANSWER,10,Goto(s\,10)
+exten => sw-33-BUSY,10,Voicemail(b${ext})
+exten => sw-33-BUSY,11,Goto(s\,10)
+exten => sw-33-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-33-DONTCALL,11,Goto(s\,10)
+exten => sw-33-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-33-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_32]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-34-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_32-34)
+exten => s,11,Return()
+exten => _sw-34-.,10,Voicemail(u${ext})
+exten => _sw-34-.,11,Goto(s\,10)
+exten => sw-34-,10,Goto(sw-34-.|10)
+exten => sw-34-NOANSWER,10,Voicemail(u${ext})
+exten => sw-34-NOANSWER,11,Goto(s\,10)
+exten => sw-34-ANSWER,10,Goto(s\,10)
+exten => sw-34-BUSY,10,Voicemail(b${ext})
+exten => sw-34-BUSY,11,Goto(s\,10)
+exten => sw-34-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-34-DONTCALL,11,Goto(s\,10)
+exten => sw-34-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-34-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_33]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-35-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_33-35)
+exten => s,11,Return()
+exten => _sw-35-.,10,Voicemail(u${ext})
+exten => _sw-35-.,11,Goto(s\,10)
+exten => sw-35-,10,Goto(sw-35-.|10)
+exten => sw-35-NOANSWER,10,Voicemail(u${ext})
+exten => sw-35-NOANSWER,11,Goto(s\,10)
+exten => sw-35-ANSWER,10,Goto(s\,10)
+exten => sw-35-BUSY,10,Voicemail(b${ext})
+exten => sw-35-BUSY,11,Goto(s\,10)
+exten => sw-35-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-35-DONTCALL,11,Goto(s\,10)
+exten => sw-35-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-35-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_34]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-36-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_34-36)
+exten => s,11,Return()
+exten => _sw-36-.,10,Voicemail(u${ext})
+exten => _sw-36-.,11,Goto(s\,10)
+exten => sw-36-,10,Goto(sw-36-.|10)
+exten => sw-36-NOANSWER,10,Voicemail(u${ext})
+exten => sw-36-NOANSWER,11,Goto(s\,10)
+exten => sw-36-ANSWER,10,Goto(s\,10)
+exten => sw-36-BUSY,10,Voicemail(b${ext})
+exten => sw-36-BUSY,11,Goto(s\,10)
+exten => sw-36-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-36-DONTCALL,11,Goto(s\,10)
+exten => sw-36-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-36-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_35]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-37-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_35-37)
+exten => s,11,Return()
+exten => _sw-37-.,10,Voicemail(u${ext})
+exten => _sw-37-.,11,Goto(s\,10)
+exten => sw-37-,10,Goto(sw-37-.|10)
+exten => sw-37-NOANSWER,10,Voicemail(u${ext})
+exten => sw-37-NOANSWER,11,Goto(s\,10)
+exten => sw-37-ANSWER,10,Goto(s\,10)
+exten => sw-37-BUSY,10,Voicemail(b${ext})
+exten => sw-37-BUSY,11,Goto(s\,10)
+exten => sw-37-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-37-DONTCALL,11,Goto(s\,10)
+exten => sw-37-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-37-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_36]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-38-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_36-38)
+exten => s,11,Return()
+exten => _sw-38-.,10,Voicemail(u${ext})
+exten => _sw-38-.,11,Goto(s\,10)
+exten => sw-38-,10,Goto(sw-38-.|10)
+exten => sw-38-NOANSWER,10,Voicemail(u${ext})
+exten => sw-38-NOANSWER,11,Goto(s\,10)
+exten => sw-38-ANSWER,10,Goto(s\,10)
+exten => sw-38-BUSY,10,Voicemail(b${ext})
+exten => sw-38-BUSY,11,Goto(s\,10)
+exten => sw-38-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-38-DONTCALL,11,Goto(s\,10)
+exten => sw-38-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-38-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_37]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-39-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_37-39)
+exten => s,11,Return()
+exten => _sw-39-.,10,Voicemail(u${ext})
+exten => _sw-39-.,11,Goto(s\,10)
+exten => sw-39-,10,Goto(sw-39-.|10)
+exten => sw-39-NOANSWER,10,Voicemail(u${ext})
+exten => sw-39-NOANSWER,11,Goto(s\,10)
+exten => sw-39-ANSWER,10,Goto(s\,10)
+exten => sw-39-BUSY,10,Voicemail(b${ext})
+exten => sw-39-BUSY,11,Goto(s\,10)
+exten => sw-39-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-39-DONTCALL,11,Goto(s\,10)
+exten => sw-39-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-39-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_38]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-40-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_38-40)
+exten => s,11,Return()
+exten => _sw-40-.,10,Voicemail(u${ext})
+exten => _sw-40-.,11,Goto(s\,10)
+exten => sw-40-,10,Goto(sw-40-.|10)
+exten => sw-40-NOANSWER,10,Voicemail(u${ext})
+exten => sw-40-NOANSWER,11,Goto(s\,10)
+exten => sw-40-ANSWER,10,Goto(s\,10)
+exten => sw-40-BUSY,10,Voicemail(b${ext})
+exten => sw-40-BUSY,11,Goto(s\,10)
+exten => sw-40-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-40-DONTCALL,11,Goto(s\,10)
+exten => sw-40-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-40-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_39]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-41-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_39-41)
+exten => s,11,Return()
+exten => _sw-41-.,10,Voicemail(u${ext})
+exten => _sw-41-.,11,Goto(s\,10)
+exten => sw-41-,10,Goto(sw-41-.|10)
+exten => sw-41-NOANSWER,10,Voicemail(u${ext})
+exten => sw-41-NOANSWER,11,Goto(s\,10)
+exten => sw-41-ANSWER,10,Goto(s\,10)
+exten => sw-41-BUSY,10,Voicemail(b${ext})
+exten => sw-41-BUSY,11,Goto(s\,10)
+exten => sw-41-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-41-DONTCALL,11,Goto(s\,10)
+exten => sw-41-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-41-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_40]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-42-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_40-42)
+exten => s,11,Return()
+exten => _sw-42-.,10,Voicemail(u${ext})
+exten => _sw-42-.,11,Goto(s\,10)
+exten => sw-42-,10,Goto(sw-42-.|10)
+exten => sw-42-NOANSWER,10,Voicemail(u${ext})
+exten => sw-42-NOANSWER,11,Goto(s\,10)
+exten => sw-42-ANSWER,10,Goto(s\,10)
+exten => sw-42-BUSY,10,Voicemail(b${ext})
+exten => sw-42-BUSY,11,Goto(s\,10)
+exten => sw-42-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-42-DONTCALL,11,Goto(s\,10)
+exten => sw-42-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-42-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_41]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-43-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_41-43)
+exten => s,11,Return()
+exten => _sw-43-.,10,Voicemail(u${ext})
+exten => _sw-43-.,11,Goto(s\,10)
+exten => sw-43-,10,Goto(sw-43-.|10)
+exten => sw-43-NOANSWER,10,Voicemail(u${ext})
+exten => sw-43-NOANSWER,11,Goto(s\,10)
+exten => sw-43-ANSWER,10,Goto(s\,10)
+exten => sw-43-BUSY,10,Voicemail(b${ext})
+exten => sw-43-BUSY,11,Goto(s\,10)
+exten => sw-43-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-43-DONTCALL,11,Goto(s\,10)
+exten => sw-43-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-43-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_42]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-44-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_42-44)
+exten => s,11,Return()
+exten => _sw-44-.,10,Voicemail(u${ext})
+exten => _sw-44-.,11,Goto(s\,10)
+exten => sw-44-,10,Goto(sw-44-.|10)
+exten => sw-44-NOANSWER,10,Voicemail(u${ext})
+exten => sw-44-NOANSWER,11,Goto(s\,10)
+exten => sw-44-ANSWER,10,Goto(s\,10)
+exten => sw-44-BUSY,10,Voicemail(b${ext})
+exten => sw-44-BUSY,11,Goto(s\,10)
+exten => sw-44-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-44-DONTCALL,11,Goto(s\,10)
+exten => sw-44-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-44-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_43]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-45-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_43-45)
+exten => s,11,Return()
+exten => _sw-45-.,10,Voicemail(u${ext})
+exten => _sw-45-.,11,Goto(s\,10)
+exten => sw-45-,10,Goto(sw-45-.|10)
+exten => sw-45-NOANSWER,10,Voicemail(u${ext})
+exten => sw-45-NOANSWER,11,Goto(s\,10)
+exten => sw-45-ANSWER,10,Goto(s\,10)
+exten => sw-45-BUSY,10,Voicemail(b${ext})
+exten => sw-45-BUSY,11,Goto(s\,10)
+exten => sw-45-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-45-DONTCALL,11,Goto(s\,10)
+exten => sw-45-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-45-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_44]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-46-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_44-46)
+exten => s,11,Return()
+exten => _sw-46-.,10,Voicemail(u${ext})
+exten => _sw-46-.,11,Goto(s\,10)
+exten => sw-46-,10,Goto(sw-46-.|10)
+exten => sw-46-NOANSWER,10,Voicemail(u${ext})
+exten => sw-46-NOANSWER,11,Goto(s\,10)
+exten => sw-46-ANSWER,10,Goto(s\,10)
+exten => sw-46-BUSY,10,Voicemail(b${ext})
+exten => sw-46-BUSY,11,Goto(s\,10)
+exten => sw-46-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-46-DONTCALL,11,Goto(s\,10)
+exten => sw-46-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-46-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_45]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-47-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_45-47)
+exten => s,11,Return()
+exten => _sw-47-.,10,Voicemail(u${ext})
+exten => _sw-47-.,11,Goto(s\,10)
+exten => sw-47-,10,Goto(sw-47-.|10)
+exten => sw-47-NOANSWER,10,Voicemail(u${ext})
+exten => sw-47-NOANSWER,11,Goto(s\,10)
+exten => sw-47-ANSWER,10,Goto(s\,10)
+exten => sw-47-BUSY,10,Voicemail(b${ext})
+exten => sw-47-BUSY,11,Goto(s\,10)
+exten => sw-47-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-47-DONTCALL,11,Goto(s\,10)
+exten => sw-47-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-47-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_46]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-48-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_46-48)
+exten => s,11,Return()
+exten => _sw-48-.,10,Voicemail(u${ext})
+exten => _sw-48-.,11,Goto(s\,10)
+exten => sw-48-,10,Goto(sw-48-.|10)
+exten => sw-48-NOANSWER,10,Voicemail(u${ext})
+exten => sw-48-NOANSWER,11,Goto(s\,10)
+exten => sw-48-ANSWER,10,Goto(s\,10)
+exten => sw-48-BUSY,10,Voicemail(b${ext})
+exten => sw-48-BUSY,11,Goto(s\,10)
+exten => sw-48-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-48-DONTCALL,11,Goto(s\,10)
+exten => sw-48-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-48-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_47]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-49-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_47-49)
+exten => s,11,Return()
+exten => _sw-49-.,10,Voicemail(u${ext})
+exten => _sw-49-.,11,Goto(s\,10)
+exten => sw-49-,10,Goto(sw-49-.|10)
+exten => sw-49-NOANSWER,10,Voicemail(u${ext})
+exten => sw-49-NOANSWER,11,Goto(s\,10)
+exten => sw-49-ANSWER,10,Goto(s\,10)
+exten => sw-49-BUSY,10,Voicemail(b${ext})
+exten => sw-49-BUSY,11,Goto(s\,10)
+exten => sw-49-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-49-DONTCALL,11,Goto(s\,10)
+exten => sw-49-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-49-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_48]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-50-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_48-50)
+exten => s,11,Return()
+exten => _sw-50-.,10,Voicemail(u${ext})
+exten => _sw-50-.,11,Goto(s\,10)
+exten => sw-50-,10,Goto(sw-50-.|10)
+exten => sw-50-NOANSWER,10,Voicemail(u${ext})
+exten => sw-50-NOANSWER,11,Goto(s\,10)
+exten => sw-50-ANSWER,10,Goto(s\,10)
+exten => sw-50-BUSY,10,Voicemail(b${ext})
+exten => sw-50-BUSY,11,Goto(s\,10)
+exten => sw-50-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-50-DONTCALL,11,Goto(s\,10)
+exten => sw-50-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-50-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_49]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-51-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_49-51)
+exten => s,11,Return()
+exten => _sw-51-.,10,Voicemail(u${ext})
+exten => _sw-51-.,11,Goto(s\,10)
+exten => sw-51-,10,Goto(sw-51-.|10)
+exten => sw-51-NOANSWER,10,Voicemail(u${ext})
+exten => sw-51-NOANSWER,11,Goto(s\,10)
+exten => sw-51-ANSWER,10,Goto(s\,10)
+exten => sw-51-BUSY,10,Voicemail(b${ext})
+exten => sw-51-BUSY,11,Goto(s\,10)
+exten => sw-51-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-51-DONTCALL,11,Goto(s\,10)
+exten => sw-51-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-51-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_50]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-52-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_50-52)
+exten => s,11,Return()
+exten => _sw-52-.,10,Voicemail(u${ext})
+exten => _sw-52-.,11,Goto(s\,10)
+exten => sw-52-,10,Goto(sw-52-.|10)
+exten => sw-52-NOANSWER,10,Voicemail(u${ext})
+exten => sw-52-NOANSWER,11,Goto(s\,10)
+exten => sw-52-ANSWER,10,Goto(s\,10)
+exten => sw-52-BUSY,10,Voicemail(b${ext})
+exten => sw-52-BUSY,11,Goto(s\,10)
+exten => sw-52-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-52-DONTCALL,11,Goto(s\,10)
+exten => sw-52-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-52-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_51]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-53-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_51-53)
+exten => s,11,Return()
+exten => _sw-53-.,10,Voicemail(u${ext})
+exten => _sw-53-.,11,Goto(s\,10)
+exten => sw-53-,10,Goto(sw-53-.|10)
+exten => sw-53-NOANSWER,10,Voicemail(u${ext})
+exten => sw-53-NOANSWER,11,Goto(s\,10)
+exten => sw-53-ANSWER,10,Goto(s\,10)
+exten => sw-53-BUSY,10,Voicemail(b${ext})
+exten => sw-53-BUSY,11,Goto(s\,10)
+exten => sw-53-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-53-DONTCALL,11,Goto(s\,10)
+exten => sw-53-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-53-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_52]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-54-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_52-54)
+exten => s,11,Return()
+exten => _sw-54-.,10,Voicemail(u${ext})
+exten => _sw-54-.,11,Goto(s\,10)
+exten => sw-54-,10,Goto(sw-54-.|10)
+exten => sw-54-NOANSWER,10,Voicemail(u${ext})
+exten => sw-54-NOANSWER,11,Goto(s\,10)
+exten => sw-54-ANSWER,10,Goto(s\,10)
+exten => sw-54-BUSY,10,Voicemail(b${ext})
+exten => sw-54-BUSY,11,Goto(s\,10)
+exten => sw-54-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-54-DONTCALL,11,Goto(s\,10)
+exten => sw-54-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-54-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_53]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-55-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_53-55)
+exten => s,11,Return()
+exten => _sw-55-.,10,Voicemail(u${ext})
+exten => _sw-55-.,11,Goto(s\,10)
+exten => sw-55-,10,Goto(sw-55-.|10)
+exten => sw-55-NOANSWER,10,Voicemail(u${ext})
+exten => sw-55-NOANSWER,11,Goto(s\,10)
+exten => sw-55-ANSWER,10,Goto(s\,10)
+exten => sw-55-BUSY,10,Voicemail(b${ext})
+exten => sw-55-BUSY,11,Goto(s\,10)
+exten => sw-55-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-55-DONTCALL,11,Goto(s\,10)
+exten => sw-55-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-55-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_54]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-56-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_54-56)
+exten => s,11,Return()
+exten => _sw-56-.,10,Voicemail(u${ext})
+exten => _sw-56-.,11,Goto(s\,10)
+exten => sw-56-,10,Goto(sw-56-.|10)
+exten => sw-56-NOANSWER,10,Voicemail(u${ext})
+exten => sw-56-NOANSWER,11,Goto(s\,10)
+exten => sw-56-ANSWER,10,Goto(s\,10)
+exten => sw-56-BUSY,10,Voicemail(b${ext})
+exten => sw-56-BUSY,11,Goto(s\,10)
+exten => sw-56-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-56-DONTCALL,11,Goto(s\,10)
+exten => sw-56-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-56-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_55]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-57-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_55-57)
+exten => s,11,Return()
+exten => _sw-57-.,10,Voicemail(u${ext})
+exten => _sw-57-.,11,Goto(s\,10)
+exten => sw-57-,10,Goto(sw-57-.|10)
+exten => sw-57-NOANSWER,10,Voicemail(u${ext})
+exten => sw-57-NOANSWER,11,Goto(s\,10)
+exten => sw-57-ANSWER,10,Goto(s\,10)
+exten => sw-57-BUSY,10,Voicemail(b${ext})
+exten => sw-57-BUSY,11,Goto(s\,10)
+exten => sw-57-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-57-DONTCALL,11,Goto(s\,10)
+exten => sw-57-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-57-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_56]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-58-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_56-58)
+exten => s,11,Return()
+exten => _sw-58-.,10,Voicemail(u${ext})
+exten => _sw-58-.,11,Goto(s\,10)
+exten => sw-58-,10,Goto(sw-58-.|10)
+exten => sw-58-NOANSWER,10,Voicemail(u${ext})
+exten => sw-58-NOANSWER,11,Goto(s\,10)
+exten => sw-58-ANSWER,10,Goto(s\,10)
+exten => sw-58-BUSY,10,Voicemail(b${ext})
+exten => sw-58-BUSY,11,Goto(s\,10)
+exten => sw-58-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-58-DONTCALL,11,Goto(s\,10)
+exten => sw-58-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-58-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_57]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-59-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_57-59)
+exten => s,11,Return()
+exten => _sw-59-.,10,Voicemail(u${ext})
+exten => _sw-59-.,11,Goto(s\,10)
+exten => sw-59-,10,Goto(sw-59-.|10)
+exten => sw-59-NOANSWER,10,Voicemail(u${ext})
+exten => sw-59-NOANSWER,11,Goto(s\,10)
+exten => sw-59-ANSWER,10,Goto(s\,10)
+exten => sw-59-BUSY,10,Voicemail(b${ext})
+exten => sw-59-BUSY,11,Goto(s\,10)
+exten => sw-59-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-59-DONTCALL,11,Goto(s\,10)
+exten => sw-59-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-59-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_58]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-60-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_58-60)
+exten => s,11,Return()
+exten => _sw-60-.,10,Voicemail(u${ext})
+exten => _sw-60-.,11,Goto(s\,10)
+exten => sw-60-,10,Goto(sw-60-.|10)
+exten => sw-60-NOANSWER,10,Voicemail(u${ext})
+exten => sw-60-NOANSWER,11,Goto(s\,10)
+exten => sw-60-ANSWER,10,Goto(s\,10)
+exten => sw-60-BUSY,10,Voicemail(b${ext})
+exten => sw-60-BUSY,11,Goto(s\,10)
+exten => sw-60-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-60-DONTCALL,11,Goto(s\,10)
+exten => sw-60-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-60-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_59]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-61-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_59-61)
+exten => s,11,Return()
+exten => _sw-61-.,10,Voicemail(u${ext})
+exten => _sw-61-.,11,Goto(s\,10)
+exten => sw-61-,10,Goto(sw-61-.|10)
+exten => sw-61-NOANSWER,10,Voicemail(u${ext})
+exten => sw-61-NOANSWER,11,Goto(s\,10)
+exten => sw-61-ANSWER,10,Goto(s\,10)
+exten => sw-61-BUSY,10,Voicemail(b${ext})
+exten => sw-61-BUSY,11,Goto(s\,10)
+exten => sw-61-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-61-DONTCALL,11,Goto(s\,10)
+exten => sw-61-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-61-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_60]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-62-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_60-62)
+exten => s,11,Return()
+exten => _sw-62-.,10,Voicemail(u${ext})
+exten => _sw-62-.,11,Goto(s\,10)
+exten => sw-62-,10,Goto(sw-62-.|10)
+exten => sw-62-NOANSWER,10,Voicemail(u${ext})
+exten => sw-62-NOANSWER,11,Goto(s\,10)
+exten => sw-62-ANSWER,10,Goto(s\,10)
+exten => sw-62-BUSY,10,Voicemail(b${ext})
+exten => sw-62-BUSY,11,Goto(s\,10)
+exten => sw-62-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-62-DONTCALL,11,Goto(s\,10)
+exten => sw-62-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-62-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_61]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-63-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_61-63)
+exten => s,11,Return()
+exten => _sw-63-.,10,Voicemail(u${ext})
+exten => _sw-63-.,11,Goto(s\,10)
+exten => sw-63-,10,Goto(sw-63-.|10)
+exten => sw-63-NOANSWER,10,Voicemail(u${ext})
+exten => sw-63-NOANSWER,11,Goto(s\,10)
+exten => sw-63-ANSWER,10,Goto(s\,10)
+exten => sw-63-BUSY,10,Voicemail(b${ext})
+exten => sw-63-BUSY,11,Goto(s\,10)
+exten => sw-63-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-63-DONTCALL,11,Goto(s\,10)
+exten => sw-63-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-63-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_62]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-64-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_62-64)
+exten => s,11,Return()
+exten => _sw-64-.,10,Voicemail(u${ext})
+exten => _sw-64-.,11,Goto(s\,10)
+exten => sw-64-,10,Goto(sw-64-.|10)
+exten => sw-64-NOANSWER,10,Voicemail(u${ext})
+exten => sw-64-NOANSWER,11,Goto(s\,10)
+exten => sw-64-ANSWER,10,Goto(s\,10)
+exten => sw-64-BUSY,10,Voicemail(b${ext})
+exten => sw-64-BUSY,11,Goto(s\,10)
+exten => sw-64-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-64-DONTCALL,11,Goto(s\,10)
+exten => sw-64-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-64-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_63]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-65-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_63-65)
+exten => s,11,Return()
+exten => _sw-65-.,10,Voicemail(u${ext})
+exten => _sw-65-.,11,Goto(s\,10)
+exten => sw-65-,10,Goto(sw-65-.|10)
+exten => sw-65-NOANSWER,10,Voicemail(u${ext})
+exten => sw-65-NOANSWER,11,Goto(s\,10)
+exten => sw-65-ANSWER,10,Goto(s\,10)
+exten => sw-65-BUSY,10,Voicemail(b${ext})
+exten => sw-65-BUSY,11,Goto(s\,10)
+exten => sw-65-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-65-DONTCALL,11,Goto(s\,10)
+exten => sw-65-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-65-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_64]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-66-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_64-66)
+exten => s,11,Return()
+exten => _sw-66-.,10,Voicemail(u${ext})
+exten => _sw-66-.,11,Goto(s\,10)
+exten => sw-66-,10,Goto(sw-66-.|10)
+exten => sw-66-NOANSWER,10,Voicemail(u${ext})
+exten => sw-66-NOANSWER,11,Goto(s\,10)
+exten => sw-66-ANSWER,10,Goto(s\,10)
+exten => sw-66-BUSY,10,Voicemail(b${ext})
+exten => sw-66-BUSY,11,Goto(s\,10)
+exten => sw-66-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-66-DONTCALL,11,Goto(s\,10)
+exten => sw-66-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-66-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_65]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-67-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_65-67)
+exten => s,11,Return()
+exten => _sw-67-.,10,Voicemail(u${ext})
+exten => _sw-67-.,11,Goto(s\,10)
+exten => sw-67-,10,Goto(sw-67-.|10)
+exten => sw-67-NOANSWER,10,Voicemail(u${ext})
+exten => sw-67-NOANSWER,11,Goto(s\,10)
+exten => sw-67-ANSWER,10,Goto(s\,10)
+exten => sw-67-BUSY,10,Voicemail(b${ext})
+exten => sw-67-BUSY,11,Goto(s\,10)
+exten => sw-67-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-67-DONTCALL,11,Goto(s\,10)
+exten => sw-67-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-67-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_66]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-68-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_66-68)
+exten => s,11,Return()
+exten => _sw-68-.,10,Voicemail(u${ext})
+exten => _sw-68-.,11,Goto(s\,10)
+exten => sw-68-,10,Goto(sw-68-.|10)
+exten => sw-68-NOANSWER,10,Voicemail(u${ext})
+exten => sw-68-NOANSWER,11,Goto(s\,10)
+exten => sw-68-ANSWER,10,Goto(s\,10)
+exten => sw-68-BUSY,10,Voicemail(b${ext})
+exten => sw-68-BUSY,11,Goto(s\,10)
+exten => sw-68-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-68-DONTCALL,11,Goto(s\,10)
+exten => sw-68-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-68-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_67]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-69-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_67-69)
+exten => s,11,Return()
+exten => _sw-69-.,10,Voicemail(u${ext})
+exten => _sw-69-.,11,Goto(s\,10)
+exten => sw-69-,10,Goto(sw-69-.|10)
+exten => sw-69-NOANSWER,10,Voicemail(u${ext})
+exten => sw-69-NOANSWER,11,Goto(s\,10)
+exten => sw-69-ANSWER,10,Goto(s\,10)
+exten => sw-69-BUSY,10,Voicemail(b${ext})
+exten => sw-69-BUSY,11,Goto(s\,10)
+exten => sw-69-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-69-DONTCALL,11,Goto(s\,10)
+exten => sw-69-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-69-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_68]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-70-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_68-70)
+exten => s,11,Return()
+exten => _sw-70-.,10,Voicemail(u${ext})
+exten => _sw-70-.,11,Goto(s\,10)
+exten => sw-70-,10,Goto(sw-70-.|10)
+exten => sw-70-NOANSWER,10,Voicemail(u${ext})
+exten => sw-70-NOANSWER,11,Goto(s\,10)
+exten => sw-70-ANSWER,10,Goto(s\,10)
+exten => sw-70-BUSY,10,Voicemail(b${ext})
+exten => sw-70-BUSY,11,Goto(s\,10)
+exten => sw-70-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-70-DONTCALL,11,Goto(s\,10)
+exten => sw-70-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-70-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_69]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-71-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_69-71)
+exten => s,11,Return()
+exten => _sw-71-.,10,Voicemail(u${ext})
+exten => _sw-71-.,11,Goto(s\,10)
+exten => sw-71-,10,Goto(sw-71-.|10)
+exten => sw-71-NOANSWER,10,Voicemail(u${ext})
+exten => sw-71-NOANSWER,11,Goto(s\,10)
+exten => sw-71-ANSWER,10,Goto(s\,10)
+exten => sw-71-BUSY,10,Voicemail(b${ext})
+exten => sw-71-BUSY,11,Goto(s\,10)
+exten => sw-71-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-71-DONTCALL,11,Goto(s\,10)
+exten => sw-71-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-71-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_70]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-72-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_70-72)
+exten => s,11,Return()
+exten => _sw-72-.,10,Voicemail(u${ext})
+exten => _sw-72-.,11,Goto(s\,10)
+exten => sw-72-,10,Goto(sw-72-.|10)
+exten => sw-72-NOANSWER,10,Voicemail(u${ext})
+exten => sw-72-NOANSWER,11,Goto(s\,10)
+exten => sw-72-ANSWER,10,Goto(s\,10)
+exten => sw-72-BUSY,10,Voicemail(b${ext})
+exten => sw-72-BUSY,11,Goto(s\,10)
+exten => sw-72-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-72-DONTCALL,11,Goto(s\,10)
+exten => sw-72-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-72-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_71]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-73-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_71-73)
+exten => s,11,Return()
+exten => _sw-73-.,10,Voicemail(u${ext})
+exten => _sw-73-.,11,Goto(s\,10)
+exten => sw-73-,10,Goto(sw-73-.|10)
+exten => sw-73-NOANSWER,10,Voicemail(u${ext})
+exten => sw-73-NOANSWER,11,Goto(s\,10)
+exten => sw-73-ANSWER,10,Goto(s\,10)
+exten => sw-73-BUSY,10,Voicemail(b${ext})
+exten => sw-73-BUSY,11,Goto(s\,10)
+exten => sw-73-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-73-DONTCALL,11,Goto(s\,10)
+exten => sw-73-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-73-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_72]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-74-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_72-74)
+exten => s,11,Return()
+exten => _sw-74-.,10,Voicemail(u${ext})
+exten => _sw-74-.,11,Goto(s\,10)
+exten => sw-74-,10,Goto(sw-74-.|10)
+exten => sw-74-NOANSWER,10,Voicemail(u${ext})
+exten => sw-74-NOANSWER,11,Goto(s\,10)
+exten => sw-74-ANSWER,10,Goto(s\,10)
+exten => sw-74-BUSY,10,Voicemail(b${ext})
+exten => sw-74-BUSY,11,Goto(s\,10)
+exten => sw-74-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-74-DONTCALL,11,Goto(s\,10)
+exten => sw-74-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-74-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten_73]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-75-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten_73-75)
+exten => s,11,Return()
+exten => _sw-75-.,10,Voicemail(u${ext})
+exten => _sw-75-.,11,Goto(s\,10)
+exten => sw-75-,10,Goto(sw-75-.|10)
+exten => sw-75-NOANSWER,10,Voicemail(u${ext})
+exten => sw-75-NOANSWER,11,Goto(s\,10)
+exten => sw-75-ANSWER,10,Goto(s\,10)
+exten => sw-75-BUSY,10,Voicemail(b${ext})
+exten => sw-75-BUSY,11,Goto(s\,10)
+exten => sw-75-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-75-DONTCALL,11,Goto(s\,10)
+exten => sw-75-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-75-TORTURE,11,Goto(s\,10)
+
+
+[std-priv-exten]
+exten => s,1,Set(LOCAL(dev)=${ARG1})
+exten => s,2,Set(LOCAL(ext)=${ARG2})
+exten => s,3,Set(LOCAL(timeout)=${ARG3})
+exten => s,4,Set(LOCAL(opts)=${ARG4})
+exten => s,5,Set(LOCAL(torcont)=${ARG5})
+exten => s,6,Set(LOCAL(dontcont)=${ARG6})
+exten => s,7,Dial(${dev}\,${timeout}\,${opts})
+exten => s,8,NoOp(${DIALSTATUS} was chosen)
+exten => s,9,Goto(sw-76-${DIALSTATUS}\,10)
+exten => s,10,NoOp(Finish switch-std-priv-exten-76)
+exten => s,11,Return()
+exten => _sw-76-.,10,Voicemail(u${ext})
+exten => _sw-76-.,11,Goto(s\,10)
+exten => sw-76-,10,Goto(sw-76-.|10)
+exten => sw-76-NOANSWER,10,Voicemail(u${ext})
+exten => sw-76-NOANSWER,11,Goto(s\,10)
+exten => sw-76-ANSWER,10,Goto(s\,10)
+exten => sw-76-BUSY,10,Voicemail(b${ext})
+exten => sw-76-BUSY,11,Goto(s\,10)
+exten => sw-76-DONTCALL,10,Goto(${dontcont}\,s\,begin)
+exten => sw-76-DONTCALL,11,Goto(s\,10)
+exten => sw-76-TORTURE,10,Goto(${torcont}\,s\,begin)
+exten => sw-76-TORTURE,11,Goto(s\,10)
+
+
+[fillcidname]
+exten => s,1,GotoIf($["${CALLERID(num)}" = "" ]?2:3)
+exten => s,2,Return()
+exten => s,3,NoOp(Finish if-fillcidname-77)
+exten => s,4,Set(cidn=${DB(cidname/${CALLERID(num)})})
+exten => s,5,GotoIf($["${CALLERID(name)}" != "" ]?6:9)
+exten => s,6,GotoIf($[("${cidn}" = "Privacy Manager" & "${CALLERID(name)}" != "Privacy Manager") | "${cidn}" = "" ]?7:8)
+exten => s,7,Set(DB(cidname/${CALLERID(num)})=${CALLERID(name)})
+exten => s,8,NoOp(Finish if-if-fillcidname-78-79)
+exten => s,9,NoOp(Finish if-fillcidname-78)
+exten => s,10,GotoIf($[( "${cidn}" != "" ) & ( "${CALLERID(name)}" = "" | "${CALLERID(name)}" = "CODY\,WY " | "${CALLERID(name)}" = "POWELL\,WY " | "${CALLERID(name)}" = "WIRELESS CALLER" | "${CALLERID(name)}" = "SUBSCRIBER\,WIRE" | "${CALLERID(name)}" = "CELLULAR ONE" | "${CALLERID(name)}" = "Cellular One Customer" | "${CALLERID(name)}" = "CELLULAR ONE " | "${CALLERID(name)}" = "Privacy Manager" | "${CALLERID(name)}" = "RIVERTON\,WY " | "${CALLERID(name)}" = "BASIN\,WY " | "${CALLERID(name)}" = "BILLINGS\,MT " | "${CALLERID(name)}" = "PROVO\,UT " | "${CALLERID(name)}" = "TOLL FREE " ) ]?11:12)
+exten => s,11,Set(CALLERID(name)=${cidn})
+exten => s,12,NoOp(Finish if-fillcidname-80)
+exten => s,13,Return()
+
+
+[ciddial]
+exten => s,1,Set(LOCAL(dialnum)=${ARG1})
+exten => s,2,Set(LOCAL(lookup)=${ARG2})
+exten => s,3,Set(LOCAL(waittime)=${ARG3})
+exten => s,4,Set(LOCAL(dialopts)=${ARG4})
+exten => s,5,Set(LOCAL(ddev)=${ARG5})
+exten => s,6,Set(cidnu=${CALLERID(num)})
+exten => s,7,Set(cidn=${DB(cidname/${lookup})})
+exten => s,8,Set(CALLERID(name)=${cidn})
+exten => s,9,Dial(${ddev}/${dialnum}|${waittime}|${dialopts})
+exten => s,10,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?11:19)
+exten => s,11,BackGround(try_voip)
+exten => s,12,Set(CALLERID(num)=$[7075679201])
+exten => s,13,Dial(SIP/1${lookup}@tctwest\,${waittime}\,${dialopts})
+exten => s,14,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?15:18)
+exten => s,15,BackGround(try_cell)
+exten => s,16,Set(CALLERID(num)=$[${cidnu}])
+exten => s,17,Dial(Zap/2/${lookup}\,${waittime}\,${dialopts})
+exten => s,18,NoOp(Finish if-if-ciddial-81-82)
+exten => s,19,NoOp(Finish if-ciddial-81)
+exten => s,20,Return()
+
+
+[ciddial3]
+exten => s,1,Set(LOCAL(dialnum)=${ARG1})
+exten => s,2,Set(LOCAL(lookup)=${ARG2})
+exten => s,3,Set(LOCAL(waittime)=${ARG3})
+exten => s,4,Set(LOCAL(dialopts)=${ARG4})
+exten => s,5,Set(LOCAL(ddev)=${ARG5})
+exten => s,6,Set(cidnu=${CALLERID(num)})
+exten => s,7,Set(cidn=${DB(cidname/${lookup})})
+exten => s,8,Set(CALLERID(name)=${cidn})
+exten => s,9,Dial(${ddev}/${dialnum}|${waittime}|${dialopts})
+exten => s,10,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?11:13)
+exten => s,11,BackGround(try_cell)
+exten => s,12,Dial(Zap/2/${lookup}\,${waittime}\,${dialopts})
+exten => s,13,NoOp(Finish if-ciddial3-83)
+exten => s,14,Return()
+
+
+[ciddial2]
+exten => s,1,Set(LOCAL(dialnum)=${ARG1})
+exten => s,2,Set(LOCAL(lookup)=${ARG2})
+exten => s,3,Set(LOCAL(waittime)=${ARG3})
+exten => s,4,Set(LOCAL(dialopts)=${ARG4})
+exten => s,5,Set(LOCAL(ddev)=${ARG5})
+exten => s,6,Set(cidn=${DB(cidname/${lookup})})
+exten => s,7,Set(cidnu=${CALLERID(num)})
+exten => s,8,Set(CALLERID(name)=${cidn})
+exten => s,9,Set(CALLERID(num)=7075679201)
+exten => s,10,Dial(SIP/1${lookup}@tctwest\,${waittime}\,${dialopts})
+exten => s,11,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?12:19)
+exten => s,12,Set(CALLERID(num)=${cidnu})
+exten => s,13,BackGround(try_zap)
+exten => s,14,Dial(${ddev}/${dialnum}\,${waittime}|${dialopts})
+exten => s,15,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL" ]?16:18)
+exten => s,16,BackGround(try_cell)
+exten => s,17,Dial(Zap/2/${lookup}\,${waittime}\,${dialopts})
+exten => s,18,NoOp(Finish if-if-ciddial2-84-85)
+exten => s,19,NoOp(Finish if-ciddial2-84)
+exten => s,20,Return()
+
+
+[callerid-liar]
+exten => s,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/priv-callerintros/LIAR.gsm&)
+exten => s,2,Background(priv-liar)
+exten => s,3,Hangup()
+exten => s,4,Return()
+
+
+[callerid-bad]
+exten => s,1,Set(mycid=$[${CALLERID(num)}:"1([0-9]+)"])
+exten => s,2,Set(CALLERID(num)=${mycid})
+exten => s,3,Wait(0)
+exten => s,4,Return()
+
+
+[privacyManagerFailed]
+exten => s,1(begin),Background(PrivManInstructions)
+exten => s,2,PrivacyManager()
+exten => s,3,GotoIf($["${PRIVACYMGRSTATUS}" = "FAILED" ]?4:11)
+exten => s,4,Background(tt-allbusy)
+exten => s,5,Background(tt-somethingwrong)
+exten => s,6,Background(tt-monkeysintro)
+exten => s,7,Background(tt-monkeys)
+exten => s,8,Background(tt-weasels)
+exten => s,9,Hangup()
+exten => s,10,Goto(12)
+exten => s,11,Goto(homeline\,s\,postPriv)
+exten => s,12,NoOp(Finish if-privacyManagerFailed-86)
+
+
+[homeline]
+exten => s,1(begin),Answer()
+exten => s,2,Set(repeatcount=0)
+exten => s,3,Zapateller(nocallerid)
+exten => s,4,PrivacyManager()
+exten => s,5,GotoIf($["${PRIVACYMGRSTATUS}" = "FAILED" ]?6:10)
+exten => s,6,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/privmanfailed.gsm)
+exten => s,7,Gosub(std-priv-exten\,s\,1(Zap/3r1&Zap/5r1\,2\,25\,mtw\,telemarket\,telemarket))
+exten => s,8,Hangup()
+exten => s,9,Return()
+exten => s,10,NoOp(Finish if-homeline-87)
+exten => s,11(postPriv),Gosub(fillcidname\,s\,1)
+exten => s,12,Set(CONFCIDNA=${CALLERID(name)})
+exten => s,13,Set(CONFCIDNU=${CALLERID(num)})
+exten => s,14,AGI(callall)
+exten => s,15,AGI(submit-announce.agi)
+exten => s,16,GotoIf($["${CALLERID(num)}" : "1" ]?17:18)
+exten => s,17,Gosub(callerid-bad\,s\,1)
+exten => s,18,NoOp(Finish if-homeline-88)
+exten => s,19,GotoIf($["${CALLERID(num)}" = "7077577685" & "${CALLERID(name)}" : "Privacy Manager" ]?20:21)
+exten => s,20,Gosub(callerid-liar\,s\,1)
+exten => s,21,NoOp(Finish if-homeline-89)
+exten => s,22,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&)
+exten => s,23,Set(lds=${DB(playlds/${CALLERID(num)})})
+exten => s,24,GotoIf($["${lds}" = "1" ]?25:26)
+exten => s,25,SetMusicOnHold(mohlds)
+exten => s,26,NoOp(Finish if-homeline-90)
+exten => s,27,Set(direct=$[${DB(DirectCall/${CALLERID(num)})}])
+exten => s,28,GotoIf($["${direct}" != "" & ${direct} != 0 ]?29:36)
+exten => s,29,verbose(direct is XXX#${direct}XXXX)
+exten => s,30,Playback(greetings/direct)
+exten => s,31,Playback(/var/spool/asterisk/voicemail/default/${direct}/greet)
+exten => s,32,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => s,33,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/${direct}/greet.wav&)
+exten => s,34,Goto(sw-92-${direct}\,10)
+exten => s,35,NoOp(Finish switch-if-homeline-91-92)
+exten => s,36,NoOp(Finish if-homeline-91)
+exten => s,37(loopback),GotoIfTime(*\,*\,20-25\,dec?39)
+exten => s,38,Goto(41)
+exten => s,39,Playback(greetings/christmas)
+exten => s,40,Goto(102)
+exten => s,41,GotoIfTime(*\,*\,31\,dec?43)
+exten => s,42,Goto(45)
+exten => s,43,Playback(greetings/newyear)
+exten => s,44,Goto(101)
+exten => s,45,GotoIfTime(*\,*\,1\,jan?47)
+exten => s,46,Goto(49)
+exten => s,47,Playback(greetings/newyear)
+exten => s,48,Goto(100)
+exten => s,49,GotoIfTime(*\,*\,14\,feb?51)
+exten => s,50,Goto(53)
+exten => s,51,Playback(greetings/valentines)
+exten => s,52,Goto(99)
+exten => s,53,GotoIfTime(*\,*\,17\,mar?55)
+exten => s,54,Goto(57)
+exten => s,55,Playback(greetings/stPat)
+exten => s,56,Goto(98)
+exten => s,57,GotoIfTime(*\,*\,31\,oct?59)
+exten => s,58,Goto(61)
+exten => s,59,Playback(greetings/halloween)
+exten => s,60,Goto(97)
+exten => s,61,GotoIfTime(*\,mon\,15-21\,jan?63)
+exten => s,62,Goto(65)
+exten => s,63,Playback(greetings/mlkDay)
+exten => s,64,Goto(96)
+exten => s,65,GotoIfTime(*\,thu\,22-28\,nov?67)
+exten => s,66,Goto(69)
+exten => s,67,Playback(greetings/thanksgiving)
+exten => s,68,Goto(95)
+exten => s,69,GotoIfTime(*\,mon\,25-31\,may?71)
+exten => s,70,Goto(73)
+exten => s,71,Playback(greetings/memorial)
+exten => s,72,Goto(94)
+exten => s,73,GotoIfTime(*\,mon\,1-7\,sep?75)
+exten => s,74,Goto(77)
+exten => s,75,Playback(greetings/labor)
+exten => s,76,Goto(93)
+exten => s,77,GotoIfTime(*\,mon\,15-21\,feb?79)
+exten => s,78,Goto(81)
+exten => s,79,Playback(greetings/president)
+exten => s,80,Goto(92)
+exten => s,81,GotoIfTime(*\,sun\,8-14\,may?83)
+exten => s,82,Goto(85)
+exten => s,83,Playback(greetings/mothers)
+exten => s,84,Goto(91)
+exten => s,85,GotoIfTime(*\,sun\,15-21\,jun?87)
+exten => s,86,Goto(89)
+exten => s,87,Playback(greetings/fathers)
+exten => s,88,Goto(90)
+exten => s,89,Playback(greetings/hello)
+exten => s,90,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102-103-104-105)
+exten => s,91,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102-103-104)
+exten => s,92,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102-103)
+exten => s,93,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101-102)
+exten => s,94,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100-101)
+exten => s,95,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99-100)
+exten => s,96,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98-99)
+exten => s,97,NoOp(Finish iftime-iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97-98)
+exten => s,98,NoOp(Finish iftime-iftime-iftime-iftime-iftime-homeline-93-94-95-96-97)
+exten => s,99,NoOp(Finish iftime-iftime-iftime-iftime-homeline-93-94-95-96)
+exten => s,100,NoOp(Finish iftime-iftime-iftime-homeline-93-94-95)
+exten => s,101,NoOp(Finish iftime-iftime-homeline-93-94)
+exten => s,102,NoOp(Finish iftime-homeline-93)
+exten => s,103,Background(murphy-homeline-intro1)
+exten => _sw-92-.,10,Set(z=${direct}-2)
+exten => _sw-92-.,11,Goto(homeline-kids\,${z}\,1)
+exten => sw-92-,10,Goto(sw-92-.|10)
+exten => sw-92-2,10,Gosub(std-priv-exten\,s\,1(Zap/3r1&Zap/5r1\,2\,25\,mtw\,telemarket\,telemarket))
+exten => sw-92-2,11,Goto(s\,loopback)
+exten => sw-92-1,10,Gosub(std-priv-exten\,s\,1(Zap/6r3&Sip/murf\,1\,25\,mpA(beep)tw\,telemarket\,telemarket))
+exten => sw-92-1,11,Goto(s\,loopback)
+exten => 1,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 1,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/2/greet.wav&)
+exten => 1,3,Gosub(std-priv-exten\,s\,1(Zap/3r1&Zap/5r1\,2\,25\,mtw\,telemarket\,telemarket))
+exten => 1,4,Goto(s\,loopback)
+exten => 2,1,Goto(homeline-kids\,s\,begin)
+exten => 21,1,Dial(IAX2/seaniax\,20\,T)
+exten => 3,1,Gosub(std-priv-exten\,s\,1(Zap/6r3&Sip/murf\,1\,25\,mpA(beep)tw\,telemarket\,telemarket))
+exten => 3,2,Goto(s\,loopback)
+exten => 4,1,VoicemailMain()
+exten => 4,2,Goto(s\,loopback)
+exten => 5,1,Goto(home-introduction\,s\,begin)
+exten => 6,1,Goto(telemarket\,s\,begin)
+exten => 7,1,agi(tts-riddle.agi)
+exten => 7,2,Background(gsm/what-time-it-is2)
+exten => 7,3,SayUnixTime()
+exten => 7,4,Goto(s\,loopback)
+exten => 792,1,Goto(pageall\,s\,begin)
+exten => 793,1,Read(zz\,\,0\,\,1\,0)
+exten => 793,2,SayDigits(${zz})
+exten => t,1,Set(repeatcount=${repeatcount} + 1)
+exten => t,2,GotoIf($[${repeatcount} < 3 ]?3:4)
+exten => t,3,Goto(s\,loopback)
+exten => t,4,NoOp(Finish if-homeline-106)
+exten => t,5,Hangup()
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s\,loopback)
+exten => o,1,Congestion()
+exten => fax,1,Dial(Zap/4)
+
+
+[pageall]
+exten => s,1(begin),AGI(callall)
+exten => s,2,MeetMe(5555\,dtqp)
+exten => s,3,MeetMeAdmin(5555\,K)
+exten => s,4,Hangup()
+exten => h,1(begin),MeetMeAdmin(5555\,K)
+exten => h,2,Background(conf-muted)
+exten => h,3,Hangup()
+
+
+[add-to-conference]
+exten => start,1,NoCDR()
+exten => start,2,MeetMe(5555\,dmqp)
+exten => h,1,Hangup()
+
+
+[home-introduction]
+exten => s,1(begin),Background(intro-options)
+exten => 1,1,Playback(priv-callerintros/${CALLERID(num)})
+exten => 1,2,Goto(s\,begin)
+exten => 2,1,Goto(home-introduction-record\,s\,begin)
+exten => 3,1,Goto(homeline\,s\,loopback)
+exten => 4,1,Playback(intro-intro)
+exten => 4,2,Goto(s\,begin)
+exten => t,1,Goto(s\,begin)
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s\,begin)
+exten => o,1,Goto(s\,begin)
+
+
+[home-introduction-record]
+exten => s,1(begin),Background(intro-record-choices)
+exten => 1,1,Playback(intro-record)
+exten => 1,2,Goto(2\,begin)
+exten => 2,1(begin),Background(intro-start)
+exten => 2,2,Background(beep)
+exten => 2,3,Record(priv-callerintros/${CALLERID(num)}:gsm\,3)
+exten => 2,4,Background(priv-callerintros/${CALLERID(num)})
+exten => 2,5,Goto(home-introduction\,s\,begin)
+exten => t,1,Goto(s\,begin)
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s\,begin)
+exten => o,1,Goto(s\,begin)
+
+
+[homeline-kids]
+exten => s,1(begin),Background(murphy-homeline-kids)
+exten => 1,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 1,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/3/greet.wav&)
+exten => 1,3,Gosub(std-priv-exten\,s\,1(IAX2/seaniax&Zap/5r2\,3\,35\,mtw\,telemarket\,telemarket))
+exten => 1,4,Goto(homeline\,s\,loopback)
+exten => 2,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 2,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/4/greet.wav&)
+exten => 2,3,Voicemail(u4)
+exten => 2,4,Goto(homeline\,s\,loopback)
+exten => 3,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 3,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/5/greet.wav&)
+exten => 3,3,Gosub(std-priv-exten\,s\,1(Zap/3r2&Zap/5r2\,5\,35\,mtw\,telemarket\,telemarket))
+exten => 3,4,Goto(homeline\,s\,loopback)
+exten => 4,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 4,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/6/greet.wav&)
+exten => 4,3,Gosub(std-priv-exten\,s\,1(Zap/3r2&Zap/5r2\,6\,35\,mtw\,telemarket\,telemarket))
+exten => 4,4,Goto(homeline\,s\,loopback)
+exten => 5,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 5,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/7/greet.wav&)
+exten => 5,3,Gosub(std-priv-exten\,s\,1(Zap/3r2&Zap/5r2\,7\,35\,mtw\,telemarket\,telemarket))
+exten => 5,4,Goto(homeline\,s\,loopback)
+exten => 6,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 6,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/8/greet.wav&)
+exten => 6,3,Gosub(std-priv-exten\,s\,1(Zap/3r2&Zap/5r2\,8\,35\,mtw\,telemarket\,telemarket))
+exten => 6,4,Goto(homeline\,s\,loopback)
+exten => 7,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 7,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/9/greet.wav&)
+exten => 7,3,Gosub(std-priv-exten\,s\,1(Zap/3r2&Zap/5r2\,9\,35\,mtw\,telemarket\,telemarket))
+exten => 7,4,Goto(homeline\,s\,loopback)
+exten => t,1,Goto(s\,begin)
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s\,begin)
+exten => o,1,Goto(s\,begin)
+
+
+[voipworkline]
+exten => s,1(begin),Answer()
+exten => s,2,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&)
+exten => s,3,Goto(workline\,s\,loopback)
+exten => 7075679201,1,Answer()
+exten => 7075679201,2,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&)
+exten => 7075679201,3,Goto(workline\,s\,loopback)
+
+
+[workline]
+exten => s,1(begin),Answer()
+exten => s,2,Wait(1)
+exten => s,3,Set(repeatcount=0)
+exten => s,4,Zapateller(nocallerid)
+exten => s,5,Gosub(fillcidname\,s\,1)
+exten => s,6,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&)
+exten => s,7(loopback),Background(greetings/greeting)
+exten => s,8,Background(murphy-office-intro1)
+exten => 1,1,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/call-for.gsm)
+exten => 1,2,TrySystem(/usr/bin/play /var/spool/asterisk/voicemail/default/1/greet.wav&)
+exten => 1,3,Gosub(std-priv-exten\,s\,1(Zap/6&Sip/murf\,1\,30\,mtw\,telemarket\,telemarket))
+exten => 1,4,Goto(s\,loopback)
+exten => 4,1,VoicemailMain()
+exten => 4,2,Goto(s\,loopback)
+exten => 6,1,Goto(telemarket\,s\,begin)
+exten => 793,1,Read(zz\,\,0\,\,1\,0)
+exten => 793,2,SayDigits(${zz})
+exten => t,1,Set(repeatcount=$[${repeatcount} + 1])
+exten => t,2,GotoIf($[${repeatcount} < 3 ]?3:4)
+exten => t,3,Goto(s\,loopback)
+exten => t,4,NoOp(Finish if-workline-107)
+exten => t,5,Hangup()
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s\,loopback)
+exten => o,1,Congestion()
+exten => fax,1,Answer()
+exten => fax,2,Dial(Zap/4)
+
+
+[dialFWD]
+ignorepat => 8
+ignorepat => 9
+exten => _83.,1,Set(CALLERID(name)=${FWDCIDNAME})
+exten => _83.,2,Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2}\,60\,r)
+exten => _83.,3,Congestion()
+exten => _82NXX,1,Set(CALLERID(name)=${FWDCIDNAME})
+exten => _82NXX,2,Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2}\,60\,r)
+exten => _82NXX,3,Congestion()
+exten => _92NXX,1,Set(CALLERID(name)=${FWDCIDNAME})
+exten => _92NXX,2,Dial(IAX2/${FWDNUMBER}:${FWDPASSWORD}@iax2.fwdnet.net/${EXTEN:2}\,60\,r)
+exten => _92NXX,3,Congestion()
+
+
+[dialiaxtel]
+ignorepat => 8
+ignorepat => 9
+exten => _81700NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel)
+exten => _81800NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel)
+exten => _91700NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel)
+exten => _91800NXXXXXX,1,Dial(IAX2/zorch:zilchnoodle@iaxtel.com/${EXTEN:1}@iaxtel)
+
+
+[dialgoiax]
+ignorepat => 9
+exten => _93.,1,Set(CALLERID(name)="Joe Worker")
+exten => _93.,2,Dial(IAX2/878201007658:stickyfinger295@server1.goiax.com/${EXTEN:2}\,60\,r)
+exten => _93.,3,Congestion()
+
+
+[homefirst]
+ignorepat => 9
+exten => _91NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,${EXTEN:2}\,30\,TW\,Zap/1))
+exten => _9754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9202XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9219XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9254XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9716XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9011.,1,Gosub(ciddial\,s\,1(${EXTEN:1}\,${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9911,1,Dial(Zap/1/911\,30\,T)
+exten => _9411,1,Dial(Zap/1/411\,30\,T)
+
+
+[workfirst]
+ignorepat => 9
+exten => _91NXXNXXXXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,${EXTEN:2}\,30\,TW\,Zap/1))
+exten => _9754XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9574XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9202XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9219XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9254XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9716XXXX,1,Gosub(ciddial2\,s\,1(${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9NXXXXXX,1,Gosub(ciddial2\,s\,1(1707${EXTEN:1}\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _9911,1,Dial(Zap/1/911\,30\,T)
+exten => _9411,1,Dial(Zap/1/411\,30\,T)
+
+
+[force_cell]
+ignorepat => 8
+exten => _81NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,${EXTEN:2}\,30\,TW\,Zap/2))
+exten => _8754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2))
+exten => _8574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2))
+exten => _8202XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2))
+exten => _8219XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2))
+exten => _8254XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2))
+exten => _8716XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2))
+exten => _8NXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/2))
+exten => _8911,1,Dial(Zap/1/911|30|T)
+exten => _8411,1,Dial(Zap/1/411|30|T)
+
+
+[force_home]
+ignorepat => 8
+exten => _81NXXNXXXXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,${EXTEN:2}\,30\,TW\,Zap/1))
+exten => _8754XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _8574XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _8202XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _8219XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _8254XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _8716XXXX,1,Gosub(ciddial3\,s\,1(${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _8NXXXXXX,1,Gosub(ciddial3\,s\,1(1707${EXTEN:1}#\,707${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _8911,1,Dial(Zap/1/911|30|T)
+exten => _8411,1,Dial(Zap/1/411|30|T)
+
+
+[homeext]
+ignorepat => 8
+ignorepat => 9
+include => parkedcalls
+include => homefirst
+include => force_cell
+exten => s,1(loopback),Wait(0)
+exten => 1,1,Gosub(std-priv-exten\,s\,1(Zap/3&Zap/5\,2\,35\,mtw\,telemarket\,telemarket))
+exten => 1,2,Goto(s\,loopback)
+exten => 2,1,Gosub(std-priv-exten\,s\,1(Zap/6&Zap/5\,1\,35\,mpA(beep3)Tt\,telemarket\,telemarket))
+exten => 2,2,Goto(s\,loopback)
+exten => 4,1,VoicemailMain()
+exten => 5,1,Record(recording:gsm)
+exten => 5,2,Background(recording)
+exten => 6,1,Background(recording)
+exten => 760,1,DateTime()
+exten => 760,2,Goto(s\,loopback)
+exten => 761,1,Record(announcement:gsm)
+exten => 761,2,TrySystem(/usr/bin/play /var/lib/asterisk/sounds/announcement.gsm&)
+exten => 761,3,Goto(s\,loopback)
+exten => 762,1,agi(tts-riddle.agi)
+exten => 762,2,Background(gsm/what-time-it-is2)
+exten => 762,3,SayUnixTime()
+exten => 762,4,Goto(s\,loopback)
+exten => 763,1,Set(CALLERID(num)=)
+exten => 763,2,Dial(Zap/6r3\,35\,mptA(beep3))
+exten => 763,3,Hangup()
+exten => 764,1,Set(CALLERID(num)=)
+exten => 764,2,Dial(Zap/6r3\,35\,mptnA(beep3))
+exten => 764,3,Hangup()
+exten => 765,1,Set(CALLERID(num)=)
+exten => 765,2,Dial(Zap/6r3\,35\,mptNA(beep3))
+exten => 765,3,Hangup()
+exten => 766,1,Dial(Zap/6r3\,35\,mptNA(beep3))
+exten => 766,2,Hangup()
+exten => 767,1,Dial(Zap/6r3\,35\,mptnA(beep3))
+exten => 767,2,Hangup()
+exten => 769,1,Playtones(dial)
+exten => 769,2,Wait(2)
+exten => 769,3,Playtones(busy)
+exten => 769,4,Wait(2)
+exten => 769,5,Playtones(ring)
+exten => 769,6,Wait(2)
+exten => 769,7,Playtones(congestion)
+exten => 769,8,Wait(2)
+exten => 769,9,Playtones(callwaiting)
+exten => 769,10,Wait(2)
+exten => 769,11,Playtones(dialrecall)
+exten => 769,12,Wait(2)
+exten => 769,13,Playtones(record)
+exten => 769,14,Wait(2)
+exten => 769,15,Playtones(info)
+exten => 769,16,Wait(5)
+exten => 769,17,Hangup()
+exten => 790,1,MeetMe(790\,p)
+exten => 792,1,Goto(pageall\,s\,begin)
+exten => 795,1,AGI(wakeup.agi)
+exten => 795,2,Congestion()
+exten => 544716,1,TrySystem(/usr/local/bin/who-is-it ${CALLERID(num)} "${CALLERID(name)}"&)
+exten => 544716,2,Goto(s\,loopback)
+exten => i,1,Background(invalid)
+exten => i,2,Goto(s\,loopback)
+exten => o,1,Goto(s\,loopback)
+exten => t,1,Congestion()
+
+
+[fromvmhome]
+exten => 1,1,Dial(Zap/6&Sip/murf|20|Tt)
+exten => 2,1,Dial(Zap/3&Zap/5|20|Tt)
+exten => _707202XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707219XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707254XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707716XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _NXXNXXXXXX,1,Gosub(ciddial\,s\,1(1${EXTEN}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _1NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => _574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => _NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => _911,1,Gosub(ciddial\,s\,1(911\,911\,30\,TW\,Zap/1))
+exten => _411,1,Gosub(ciddial\,s\,1(411\,411\,30\,TW\,Zap/1))
+
+
+[fromvmwork]
+exten => 1,1,Dial(Zap/6&Sip/murf|20|Tt)
+exten => 2,1,Dial(Zap/3&Zap/5|20|Tt)
+exten => _707202XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707219XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707254XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707716XXXX,1,Gosub(ciddial\,s\,1(1${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _NXXNXXXXXX,1,Gosub(ciddial\,s\,1(1${EXTEN}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _1NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => _574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => _NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => 911,1,Gosub(ciddial\,s\,1(911\,911\,30\,TW\,Zap/1))
+exten => 411,1,Gosub(ciddial\,s\,1(411\,411\,30\,TW\,Zap/1))
+
+
+[fromSeanUniden]
+include => parkedcalls
+exten => 21,1,Dial(IAX2/seaniax\,20\,T)
+exten => _707202XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707219XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707254XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707716XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _707574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN:3}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _NXXNXXXXXX,1,Gosub(ciddial\,s\,1(1${EXTEN}\,${EXTEN}\,30\,TW\,Zap/1))
+exten => _1NXXNXXXXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,${EXTEN:1}\,30\,TW\,Zap/1))
+exten => _754XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => _574XXXX,1,Gosub(ciddial\,s\,1(${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => _NXXXXXX,1,Gosub(ciddial\,s\,1(1707${EXTEN}\,707${EXTEN}\,30\,TW\,Zap/1))
+exten => 911,1,Gosub(ciddial\,s\,1(911\,911\,30\,TW\,Zap/1))
+exten => 411,1,Gosub(ciddial\,s\,1(411\,411\,30\,TW\,Zap/1))
+
+
+[workext]
+ignorepat => 8
+ignorepat => 9
+include => parkedcalls
+include => workfirst
+include => force_home
+include => dialFWD
+include => dialiaxtel
+include => dialgoiax
+exten => s,1(loopback),Wait(0)
+exten => 1,1,Dial(Zap/3&Zap/5\,20\,tT)
+exten => 2,1,Dial(Zap/5&Zap/6\,20\,tT)
+exten => 21,1,Dial(IAX2/seaniax\,20\,T)
+exten => 22,1,Set(CALLERID(num)=1234567890)
+exten => 22,2,Set(CALLERID(name)=TestCaller)
+exten => 22,3,Dial(Zap/5\,20\,mP()A(beep)tw)
+exten => 22,4,NoOp(here is dialstatus: ${DIALSTATUS}...)
+exten => 22,5,Goto(s\,loopback)
+exten => 4,1,VoicemailMain()
+exten => 4,2,Goto(s\,loopback)
+exten => 5,1,Record(recording:gsm)
+exten => 5,2,Background(recording)
+exten => 6,1,ZapBarge()
+exten => 760,1,DateTime()
+exten => 760,2,Goto(s\,loopback)
+exten => 761,1,ZapBarge()
+exten => 761,2,Goto(s\,loopback)
+exten => 765,1,Playback(demo-echotest)
+exten => 765,2,Echo()
+exten => 765,3,Playback(demo-echodone)
+exten => 765,4,Goto(s\,loopback)
+exten => 766,1,Festival(The other thing to watch is neuro-electronics: the ability to interface technology with our neural system: My wife: Sigrid: has had a cochlear implant since 1996. This once profoundly deaf person now uses the phone: recognizes accents: and listens to movies and recorded books.)
+exten => 766,2,Goto(s\,loopback)
+exten => 767,1,agi(tts-riddle.agi)
+exten => 767,2,Background(gsm/what-time-it-is2)
+exten => 767,3,SayUnixTime()
+exten => 767,4,Goto(s\,loopback)
+exten => 768,1,agi(tts-computer.agi)
+exten => 771,1,eagi(eagi-test)
+exten => 771,2,agi(my-agi-test)
+exten => 772,1,agi(wakeup.agi)
+exten => 775,1,GotoIf($[${EXTEN}=${EXTEN} ]?2:4)
+exten => 775,2,BackGround(digits/1)
+exten => 775,3,Goto(5)
+exten => 775,4,BackGround(digits/0)
+exten => 775,5,NoOp(Finish if-workext-108)
+exten => 775,6,GotoIf($[${EXTEN}=${LANGUAGE} ]?7:9)
+exten => 775,7,BackGround(digits/1)
+exten => 775,8,Goto(10)
+exten => 775,9,BackGround(digits/0)
+exten => 775,10,NoOp(Finish if-workext-109)
+exten => 775,11,BackGround(digits/2)
+exten => 776,1,Set(TEST=00359889811777)
+exten => 776,2,GotoIf($[${TEST}= 00359889811777 ]?3:5)
+exten => 776,3,BackGround(digits/1)
+exten => 776,4,Goto(6)
+exten => 776,5,BackGround(digits/0)
+exten => 776,6,NoOp(Finish if-workext-110)
+exten => 776,7,GotoIf($[${TEST}= 00359889811888 ]?8:10)
+exten => 776,8,BackGround(digits/1)
+exten => 776,9,Goto(11)
+exten => 776,10,BackGround(digits/0)
+exten => 776,11,NoOp(Finish if-workext-111)
+exten => 776,12,Hangup()
+exten => 790,1,MeetMe(790\,p)
+exten => 792,1,Goto(pageall\,s\,begin)
+exten => 793,1,NoOp(Hello\, this is included from include1.ael2)
+exten => 793,2,NoOp(This was included from include2.ael2)
+exten => 793,3,NoOp(This is include3.ael2!)
+exten => 793,4,NoOp(Include5.ael2 doesn't include anything\, either!)
+exten => 793,5,NoOp(This is include4.ael2! Isn't it cool!?!?!?!)
+exten => 793,6,NoOp(4 doesn't include anything)
+exten => 795,1,AGI(wakeup.agi)
+exten => 795,2,Congestion()
+exten => 797,1,Set(CONFCIDNA=${CALLERID(name)})
+exten => 797,2,Set(CONFCIDNU=${CALLERID(num)})
+exten => 797,3,AGI(callall)
+exten => 797,4,AGI(submit-announce.agi)
+exten => 797,5,Hangup()
+
+
+[wakeup]
+exten => 3,1,Dial(Zap/3|30)
+exten => 4,1,Dial(Zap/4|30)
+exten => 5,1,Dial(Zap/5|30)
+exten => 6,1,Dial(Zap/6|30)
+exten => 99,1,Dial(IAX2/murfiaxphone|30)
+exten => 97,1,Dial(IAX2/ryaniax|30)
+exten => 94,1,Dial(IAX2/seaniax|30)
+
+
+[announce-all]
+exten => s,1(begin),MeetMe(5555\,dtqp)
+exten => s,2,MeetMeAdmin(5555\,K)
+exten => s,3,Hangup()
+exten => h,1,MeetMeAdmin(5555\,K)
+exten => h,2,Hangup()
+
+
+[telemarket]
+exten => s,1(begin),Playback(telemarketer-intro)
+exten => s,2,Playback(telemarketer-choices)
+exten => 1,1,Goto(telemarket-charity\,s\,begin)
+exten => 2,1,Goto(telemarket-political\,s\,begin)
+exten => 3,1,Goto(telemarket-pollster\,s\,begin)
+exten => 4,1,Goto(telemarket-research\,s\,begin)
+exten => 5,1,Goto(telemarket-magazine\,s\,begin)
+exten => 6,1,Goto(telemarket-commercial\,s\,begin)
+exten => 7,1,Goto(telemarket-other\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-charity]
+exten => s,1(begin),Playback(telemark-charity-intro)
+exten => s,2,Playback(telemark-charity-choices)
+exten => 1,1,Goto(telemarket-char-disease\,s\,begin)
+exten => 2,1,Goto(telemarket-char-handicap\,s\,begin)
+exten => 3,1,Goto(telemarket-char-police\,s\,begin)
+exten => 4,1,Goto(telemarket-char-school\,s\,begin)
+exten => 5,1,Goto(telemarket-char-college\,s\,begin)
+exten => 6,1,Goto(telemarket-char-animal\,s\,begin)
+exten => 7,1,Goto(telemarket-char-candidate\,s\,begin)
+exten => 8,1,Goto(telemarket-char-abuse\,s\,begin)
+exten => 9,1,Goto(telemarket-char-other\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-char-disease]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-handicap]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-police]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-school]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-college]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-animal]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-candidate]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-abuse]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-char-other]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-sorry]
+exten => s,1(begin),Playback(telemarket-sorry)
+exten => s,2,Hangup()
+
+
+[telemarket-exception]
+exten => s,1(begin),Playback(telemarket-success)
+exten => s,2,Hangup()
+
+
+[telemarket-political]
+exten => s,1(begin),Playback(telemark-polit-intro)
+exten => s,2,Playback(telemark-polit-choices)
+exten => 1,1,Goto(telemarket-poli-Am1st\,s\,begin)
+exten => 2,1,Goto(telemarket-poli-American\,s\,begin)
+exten => 3,1,Goto(telemarket-poli-AmHer\,s\,begin)
+exten => 4,1,Goto(telemarket-poli-AmInd\,s\,begin)
+exten => 5,1,Goto(telemarket-poli-AmNaz\,s\,begin)
+exten => 6,1,Goto(telemarket-poli-Pot\,s\,begin)
+exten => 7,1,Goto(telemarket-poli-AmRef\,s\,begin)
+exten => 8,1,Goto(telemarket-poli-CFP\,s\,begin)
+exten => 9,1,Goto(telemarket-political2\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-political2]
+exten => s,1(begin),Playback(telemark-politx-intro)
+exten => s,2,Playback(telemark-polit2-choices)
+exten => 1,1,Goto(telemarket-poli-Communist\,s\,begin)
+exten => 2,1,Goto(telemarket-poli-Constit\,s\,begin)
+exten => 3,1,Goto(telemarket-poli-FamVal\,s\,begin)
+exten => 4,1,Goto(telemarket-poli-FreedSoc\,s\,begin)
+exten => 5,1,Goto(telemarket-poli-Grassroot\,s\,begin)
+exten => 6,1,Goto(telemarket-poli-Green\,s\,begin)
+exten => 7,1,Goto(telemarket-poli-Greens\,s\,begin)
+exten => 8,1,Goto(telemarket-poli-Independence\,s\,begin)
+exten => 9,1,Goto(telemarket-political3\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-political3]
+exten => s,1(begin),Playback(telemark-politx-intro)
+exten => s,2,Playback(telemark-polit3-choices)
+exten => 1,1,Goto(telemarket-poli-IndAm\,s\,begin)
+exten => 2,1,Goto(telemarket-poli-Labor\,s\,begin)
+exten => 3,1,Goto(telemarket-poli-Liber\,s\,begin)
+exten => 4,1,Goto(telemarket-poli-Light\,s\,begin)
+exten => 5,1,Goto(telemarket-poli-NatLaw\,s\,begin)
+exten => 6,1,Goto(telemarket-poli-New\,s\,begin)
+exten => 7,1,Goto(telemarket-poli-NewUn\,s\,begin)
+exten => 8,1,Goto(telemarket-poli-PeaceFree\,s\,begin)
+exten => 9,1,Goto(telemarket-political4\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-political4]
+exten => s,1(begin),Playback(telemark-politx-intro)
+exten => s,2,Playback(telemark-polit4-choices)
+exten => 1,1,Goto(telemarket-poli-Prohib\,s\,begin)
+exten => 2,1,Goto(telemarket-poli-Ref\,s\,begin)
+exten => 3,1,Goto(telemarket-poli-Revol\,s\,begin)
+exten => 4,1,Goto(telemarket-poli-SocPart\,s\,begin)
+exten => 5,1,Goto(telemarket-poli-SocAct\,s\,begin)
+exten => 6,1,Goto(telemarket-poli-SocEq\,s\,begin)
+exten => 7,1,Goto(telemarket-poli-SocLab\,s\,begin)
+exten => 8,1,Goto(telemarket-poli-SocWork\,s\,begin)
+exten => 9,1,Goto(telemarket-political5\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-political5]
+exten => s,1(begin),Playback(telemark-politx-intro)
+exten => s,2,Playback(telemark-polit5-choices)
+exten => 1,1,Goto(telemarket-poli-South\,s\,begin)
+exten => 2,1,Goto(telemarket-poli-SoInd\,s\,begin)
+exten => 3,1,Goto(telemarket-poli-USPac\,s\,begin)
+exten => 4,1,Goto(telemarket-poli-WTP\,s\,begin)
+exten => 5,1,Goto(telemarket-poli-WWP\,s\,begin)
+exten => 6,1,Goto(telemarket-poli-Democrat\,s\,begin)
+exten => 7,1,Goto(telemarket-poli-Repub\,s\,begin)
+exten => 8,1,Goto(telemarket-poli-other\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-poli-other]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Repub]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Democrat]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-WWP]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-WTP]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-USPac]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-SoInd]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-South]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-SocWork]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-SocLab]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-SocEq]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-SocAct]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-SocPart]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Revol]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Ref]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Prohib]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-PeaceFree]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-NewUn]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-New]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-NatLaw]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Light]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Liber]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Labor]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-IndAm]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Independence]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Greens]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Green]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Grassroot]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-FreedSoc]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-FamVal]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Constit]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Communist]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-CFP]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-AmRef]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Pot]
+exten => s,1(begin),Goto(telemarket-political\,s\,begin)
+
+
+[telemarket-poli-AmNaz]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-AmInd]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-AmHer]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-American]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-poli-Am1st]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-pollster]
+exten => s,1(begin),Playback(telemark-poll-intro)
+exten => s,2,Goto(telemarket-sorry\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-research]
+exten => s,1(begin),Playback(telemark-research-intro)
+exten => s,2,Goto(telemarket-sorry\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-magazine]
+exten => s,1(begin),Playback(telemark-mag-choices)
+exten => 1,1,Goto(telemark-mag-new\,s\,begin)
+exten => 2,1,Goto(telemark-mag-renew\,s\,begin)
+exten => 3,1,Goto(telemark-mag-survey\,s\,begin)
+exten => 4,1,Goto(telemark-mag-verify\,s\,begin)
+exten => 5,1,Goto(telemark-mag-other\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemark-mag-new]
+exten => s,1(begin),Playback(telemark-mag-new)
+exten => s,2,Hangup()
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemark-mag-renew]
+exten => s,1(begin),Playback(telemark-mag-renew)
+exten => s,2,Hangup()
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemark-mag-survey]
+exten => s,1(begin),Playback(telemark-mag-survey)
+exten => s,2,Hangup()
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemark-mag-verify]
+exten => s,1(begin),Playback(telemark-mag-verify)
+exten => s,2,Hangup()
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemark-mag-other]
+exten => s,1(begin),Goto(telemarket-sorry\,s\,begin)
+
+
+[telemarket-commercial]
+exten => s,1(begin),Playback(telemark-comm-intro)
+exten => s,2,Voicemail(u82)
+exten => s,3,Goto(telemarket-sorry\,s\,begin)
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
+
+
+[telemarket-other]
+exten => s,1(begin),Playback(telemark-other-intro)
+exten => s,2,Hangup()
+exten => t,1,Goto(telemarket\,s\,begin)
+exten => i,1,Goto(telemarket\,s\,begin)
+exten => o,1,Goto(telemarket\,s\,begin)
diff --git a/trunk/pbx/ael/ael-test/ref.ael-vtest17 b/trunk/pbx/ael/ael-test/ref.ael-vtest17
new file mode 100644
index 000000000..bb4204caa
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-vtest17
@@ -0,0 +1,69 @@
+
+
+[dialextens]
+exten => _10X,1,Dial(Zap/${EXTEN:2}\,30\,tw)
+exten => _1ZX,1,Dial(Zap/${EXTEN:1}\,30\,tw)
+
+
+[dialthrus]
+exten => _3XX,1,Dial(Zap/${EXTEN:1}\,30\,tw)
+
+
+[t1incoming]
+include => dialextens
+include => parkedcalls
+exten => s,1,Answer()
+exten => s,2,Background(welcome-to-test-machine)
+
+
+[incoming]
+include => dialextens
+include => parkedcalls
+exten => s,1,Answer()
+exten => s,2,Background(welcome-to-test-machine)
+
+
+[extension]
+include => dialextens
+include => dialthrus
+exten => 5,1,Record(recording:gsm)
+exten => 5,2,Background(recording)
+exten => 81,1,Set(iterations=$[1000000])
+exten => 81,2,Set(time1=${EPOCH})
+exten => 81,3,Set(i=$[1])
+exten => 81,4,GotoIf($[${i}<${iterations}]?5:8)
+exten => 81,5,NoOp(Hello)
+exten => 81,6,Set(i=$[${i}+1])
+exten => 81,7,Goto(4)
+exten => 81,8,NoOp(Finish for-extension-1)
+exten => 81,9,Set(time2=${EPOCH})
+exten => 81,10,Verbose(The time diff is $[${time2} - ${time1} ] seconds)
+exten => 81,11,Verbose(Which means that the priorities/sec = $[4* ${iterations} / (${time2} - ${time1}) ])
+exten => 81,12,SayNumber($[4 * ${iterations} / (${time2} - ${time1}) ])
+exten => 82,1,Gosub(ndeep\,s\,1(100000))
+exten => 82,2,Verbose(Finished 100000 levels deep call!)
+exten => 83,1,Goto(sw-2-${EXTEN}\,10)
+exten => 83,2,NoOp(Finish switch-extension-2)
+exten => _sw-2-.,10,Goto(83\,2)
+exten => sw-2-,10,Goto(sw-2-.|10)
+exten => _sw-2-[4-7]X,10,Verbose(and this too!)
+exten => _sw-2-[4-7]X,11,Goto(sw-2-.\,10)
+exten => _sw-2-9X,10,Verbose(handle both 8x and 9x calls)
+exten => _sw-2-9X,11,Goto(sw-2-49\,10)
+exten => _sw-2-8X,10,Verbose(do something to prepare it)
+exten => _sw-2-8X,11,Goto(sw-2-99\,10)
+
+
+[ndeep]
+exten => s,1,Set(LOCAL(level)=${ARG1})
+exten => s,2,GotoIf($[${level} == 0]?3:5)
+exten => s,3,Verbose(2|Got to Level 0)
+exten => s,4,Return()
+exten => s,5,NoOp(Finish if-ndeep-3)
+exten => s,6,Gosub(ndeep\,s\,1($[${level}-1]))
+exten => s,7,Return()
+
+
+[t1extension]
+include => dialextens
+include => dialthrus
diff --git a/trunk/pbx/ael/ael-test/ref.ael-vtest21 b/trunk/pbx/ael/ael-test/ref.ael-vtest21
new file mode 100644
index 000000000..711540aa3
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/ref.ael-vtest21
@@ -0,0 +1,9 @@
+[globals]
+AXLHAFT=wow-to-the-tenth-power
+JibberWorthy=zinger3
+OFFICE_CODE=503
+
+
+[from-enum]
+exten => _${OFFICE_CODE}XXXX,1,Answer()
+exten => _${OFFICE_CODE}XXXX,2,Goto(${EXTEN:3}\,1)
diff --git a/trunk/pbx/ael/ael-test/runtests b/trunk/pbx/ael/ael-test/runtests
new file mode 100755
index 000000000..9209f0a54
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/runtests
@@ -0,0 +1,56 @@
+#!/bin/bash
+ORIG=`mktemp /tmp/mytest.XXXXXX`
+NEW=`mktemp /tmp/mytest.XXXXXX`
+
+do_filter() {
+ sed 's/line:[0-9]*//; /^Executed.*/d; s/column=[0-9]*/ /; s/Cols: [0-9]*-[0-9]*/___/'
+}
+
+for i in ael-test*; do
+ echo -n Test: $i..................
+ (cd $i; ../../../../utils/aelparse -n -d | grep -v -i 'seconds' > ../res.$i)
+ do_filter < res.$i > $NEW
+ do_filter < ref.$i > $ORIG
+ if (diff -q $NEW $ORIG > /dev/null 2>&1 ) then
+ echo PASSED
+ rm res.$i
+ else
+ echo %%%%%%FAILED%%%%%%
+ # diff -u ref.$i res.$i
+ diff -u $ORIG $NEW
+ fi
+
+done
+
+for i in ael-ntest*; do
+ echo -n Test: $i.................
+ (cd $i; ../../../../utils/aelparse -d | grep -v -i 'seconds' > ../res.$i)
+ do_filter < res.$i > $NEW
+ do_filter < ref.$i > $ORIG
+ if (diff -q $NEW $ORIG > /dev/null 2>&1 ) then
+ echo PASSED
+ rm res.$i
+ else
+ echo %%%%%%FAILED%%%%%%
+ # diff -u ref.$i res.$i
+ diff -u $ORIG $NEW
+ fi
+
+done
+
+for i in ael-vtest*; do
+ echo -n Test: $i.................
+ (cd $i; ../../../../utils/aelparse -d -w -n | grep -v -i 'seconds' > ../res2.$i)
+
+ if (diff -q ref.$i $i/extensions.conf.aeldump > /dev/null 2>&1 ) then
+ echo PASSED
+ rm res2.$i
+ rm $i/extensions.conf.aeldump
+ else
+ echo %%%%%%FAILED%%%%%%
+ # diff -u ref.$i res.$i
+ diff -u ref.$i $i/extensions.conf.aeldump
+ fi
+
+done
+rm $NEW $ORIG
diff --git a/trunk/pbx/ael/ael-test/setref b/trunk/pbx/ael/ael-test/setref
new file mode 100755
index 000000000..b483f05ae
--- /dev/null
+++ b/trunk/pbx/ael/ael-test/setref
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+for i in res.*; do
+ refname=`echo $i | sed 's/^res/ref/'`
+ echo $refname
+ mv $i $refname
+done
diff --git a/trunk/pbx/dundi-parser.c b/trunk/pbx/dundi-parser.c
new file mode 100644
index 000000000..bab1bfc91
--- /dev/null
+++ b/trunk/pbx/dundi-parser.c
@@ -0,0 +1,846 @@
+/*
+ * 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 Distributed Universal Number Discovery (DUNDi)
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/utils.h"
+#include "asterisk/dundi.h"
+#include "dundi-parser.h"
+
+static void internaloutput(const char *str)
+{
+ fputs(str, stdout);
+}
+
+static void internalerror(const char *str)
+{
+ fprintf(stderr, "WARNING: %s", str);
+}
+
+static void (*outputf)(const char *str) = internaloutput;
+static void (*errorf)(const char *str) = internalerror;
+
+char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid)
+{
+ int x;
+ char *os = s;
+ if (maxlen < 18) {
+ if (s && (maxlen > 0))
+ *s = '\0';
+ } else {
+ for (x=0;x<5;x++) {
+ sprintf(s, "%02x:", eid->eid[x]);
+ s += 3;
+ }
+ sprintf(s, "%02x", eid->eid[5]);
+ }
+ return os;
+}
+
+char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
+{
+ int x;
+ char *os = s;
+ if (maxlen < 13) {
+ if (s && (maxlen > 0))
+ *s = '\0';
+ } else {
+ for (x=0;x<6;x++) {
+ sprintf(s, "%02X", eid->eid[x]);
+ s += 2;
+ }
+ }
+ return os;
+}
+
+int dundi_str_to_eid(dundi_eid *eid, const char *s)
+{
+ unsigned int eid_int[6];
+ int x;
+ if (sscanf(s, "%x:%x:%x:%x:%x:%x", &eid_int[0], &eid_int[1], &eid_int[2],
+ &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
+ return -1;
+ for (x=0;x<6;x++)
+ eid->eid[x] = eid_int[x];
+ return 0;
+}
+
+int dundi_str_short_to_eid(dundi_eid *eid, const char *s)
+{
+ unsigned int eid_int[6];
+ int x;
+ if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
+ &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
+ return -1;
+ for (x=0;x<6;x++)
+ eid->eid[x] = eid_int[x];
+ return 0;
+}
+
+int dundi_eid_zero(dundi_eid *eid)
+{
+ int x;
+ for (x=0;x<sizeof(eid->eid) / sizeof(eid->eid[0]);x++)
+ if (eid->eid[x]) return 0;
+ return 1;
+}
+
+int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2)
+{
+ return memcmp(eid1, eid2, sizeof(dundi_eid));
+}
+
+static void dump_string(char *output, int maxlen, void *value, int len)
+{
+ if (maxlen > len + 1)
+ maxlen = len + 1;
+
+ snprintf(output, maxlen, "%s", (char *) value);
+}
+
+static void dump_cbypass(char *output, int maxlen, void *value, int len)
+{
+ snprintf(output, maxlen, "Bypass Caches");
+}
+
+static void dump_eid(char *output, int maxlen, void *value, int len)
+{
+ if (len == 6)
+ dundi_eid_to_str(output, maxlen, (dundi_eid *)value);
+ else
+ snprintf(output, maxlen, "Invalid EID len %d", len);
+}
+
+char *dundi_hint2str(char *buf, int bufsiz, int flags)
+{
+ strcpy(buf, "");
+ buf[bufsiz-1] = '\0';
+ if (flags & DUNDI_HINT_TTL_EXPIRED) {
+ strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_HINT_DONT_ASK) {
+ strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_HINT_UNAFFECTED) {
+ strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
+ }
+ /* Get rid of trailing | */
+ if (ast_strlen_zero(buf))
+ strcpy(buf, "NONE|");
+ buf[strlen(buf)-1] = '\0';
+ return buf;
+}
+
+static void dump_hint(char *output, int maxlen, void *value, int len)
+{
+ char tmp2[256];
+ char tmp3[256];
+ int datalen;
+ struct dundi_hint *hint;
+ if (len < sizeof(*hint)) {
+ snprintf(output, maxlen, "<invalid contents>");
+ return;
+ }
+
+ hint = (struct dundi_hint *) value;;
+
+ datalen = len - offsetof(struct dundi_hint, data);
+ if (datalen > sizeof(tmp3) - 1)
+ datalen = sizeof(tmp3) - 1;
+
+ memcpy(tmp3, hint->data, datalen);
+ tmp3[datalen] = '\0';
+
+ dundi_hint2str(tmp2, sizeof(tmp2), ntohs(hint->flags));
+
+ if (ast_strlen_zero(tmp3))
+ snprintf(output, maxlen, "[%s]", tmp2);
+ else
+ snprintf(output, maxlen, "[%s] %s", tmp2, tmp3);
+}
+
+static void dump_cause(char *output, int maxlen, void *value, int len)
+{
+ static char *causes[] = {
+ "SUCCESS",
+ "GENERAL",
+ "DYNAMIC",
+ "NOAUTH" ,
+ };
+ char tmp2[256];
+ struct dundi_cause *cause;
+ int datalen;
+ int causecode;
+
+ if (len < sizeof(*cause)) {
+ snprintf(output, maxlen, "<invalid contents>");
+ return;
+ }
+
+ cause = (struct dundi_cause*) value;
+ causecode = cause->causecode;
+
+ datalen = len - offsetof(struct dundi_cause, desc);
+ if (datalen > sizeof(tmp2) - 1)
+ datalen = sizeof(tmp2) - 1;
+
+ memcpy(tmp2, cause->desc, datalen);
+ tmp2[datalen] = '\0';
+
+ if (causecode < sizeof(causes) / sizeof(causes[0])) {
+ if (ast_strlen_zero(tmp2))
+ snprintf(output, maxlen, "%s", causes[causecode]);
+ else
+ snprintf(output, maxlen, "%s: %s", causes[causecode], tmp2);
+ } else {
+ if (ast_strlen_zero(tmp2))
+ snprintf(output, maxlen, "%d", causecode);
+ else
+ snprintf(output, maxlen, "%d: %s", causecode, tmp2);
+ }
+}
+
+static void dump_int(char *output, int maxlen, void *value, int len)
+{
+ if (len == (int)sizeof(unsigned int))
+ snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
+ else
+ ast_copy_string(output, "Invalid INT", maxlen);
+}
+
+static void dump_short(char *output, int maxlen, void *value, int len)
+{
+ if (len == (int)sizeof(unsigned short))
+ snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
+ else
+ ast_copy_string(output, "Invalid SHORT", maxlen);
+}
+
+static void dump_byte(char *output, int maxlen, void *value, int len)
+{
+ if (len == (int)sizeof(unsigned char))
+ snprintf(output, maxlen, "%d", *((unsigned char *)value));
+ else
+ ast_copy_string(output, "Invalid BYTE", maxlen);
+}
+
+static char *proto2str(int proto, char *buf, int bufsiz)
+{
+ switch(proto) {
+ case DUNDI_PROTO_NONE:
+ strncpy(buf, "None", bufsiz - 1);
+ break;
+ case DUNDI_PROTO_IAX:
+ strncpy(buf, "IAX", bufsiz - 1);
+ break;
+ case DUNDI_PROTO_SIP:
+ strncpy(buf, "SIP", bufsiz - 1);
+ break;
+ case DUNDI_PROTO_H323:
+ strncpy(buf, "H.323", bufsiz - 1);
+ break;
+ default:
+ snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
+ }
+ buf[bufsiz-1] = '\0';
+ return buf;
+}
+
+char *dundi_flags2str(char *buf, int bufsiz, int flags)
+{
+ strcpy(buf, "");
+ buf[bufsiz-1] = '\0';
+ if (flags & DUNDI_FLAG_EXISTS) {
+ strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_MATCHMORE) {
+ strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_CANMATCH) {
+ strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_IGNOREPAT) {
+ strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_RESIDENTIAL) {
+ strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_COMMERCIAL) {
+ strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_MOBILE) {
+ strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_NOUNSOLICITED) {
+ strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
+ }
+ if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
+ strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
+ }
+ /* Get rid of trailing | */
+ if (ast_strlen_zero(buf))
+ strcpy(buf, "NONE|");
+ buf[strlen(buf)-1] = '\0';
+ return buf;
+}
+
+static void dump_answer(char *output, int maxlen, void *value, int len)
+{
+ struct dundi_answer *answer;
+ char proto[40];
+ char flags[40];
+ char eid_str[40];
+ char tmp[512]="";
+ int datalen;
+
+ if (len < sizeof(*answer)) {
+ snprintf(output, maxlen, "Invalid Answer");
+ return;
+ }
+
+ answer = (struct dundi_answer *)(value);
+
+ datalen = len - offsetof(struct dundi_answer, data);
+ if (datalen > sizeof(tmp) - 1)
+ datalen = sizeof(tmp) - 1;
+
+ memcpy(tmp, answer->data, datalen);
+ tmp[datalen] = '\0';
+
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
+ snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]",
+ dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)),
+ ntohs(answer->weight),
+ proto2str(answer->protocol, proto, sizeof(proto)),
+ tmp, eid_str);
+}
+
+static void dump_encrypted(char *output, int maxlen, void *value, int len)
+{
+ char iv[33];
+ int x;
+ if ((len > 16) && !(len % 16)) {
+ /* Build up IV */
+ for (x=0;x<16;x++) {
+ snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]);
+ }
+ snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
+ } else
+ snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len);
+}
+
+static void dump_raw(char *output, int maxlen, void *value, int len)
+{
+ int x;
+ unsigned char *u = value;
+ output[maxlen - 1] = '\0';
+ strcpy(output, "[ ");
+ for (x=0;x<len;x++) {
+ snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]);
+ }
+ strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
+}
+
+static struct dundi_ie {
+ int ie;
+ char *name;
+ void (*dump)(char *output, int maxlen, void *value, int len);
+} ies[] = {
+ { DUNDI_IE_EID, "ENTITY IDENT", dump_eid },
+ { DUNDI_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
+ { DUNDI_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
+ { DUNDI_IE_EID_DIRECT, "DIRECT EID", dump_eid },
+ { DUNDI_IE_ANSWER, "ANSWER", dump_answer },
+ { DUNDI_IE_TTL, "TTL", dump_short },
+ { DUNDI_IE_VERSION, "VERSION", dump_short },
+ { DUNDI_IE_EXPIRATION, "EXPIRATION", dump_short },
+ { DUNDI_IE_UNKNOWN, "UKWN DUNDI CMD", dump_byte },
+ { DUNDI_IE_CAUSE, "CAUSE", dump_cause },
+ { DUNDI_IE_REQEID, "REQUEST EID", dump_eid },
+ { DUNDI_IE_ENCDATA, "ENCDATA", dump_encrypted },
+ { DUNDI_IE_SHAREDKEY, "SHAREDKEY", dump_raw },
+ { DUNDI_IE_SIGNATURE, "SIGNATURE", dump_raw },
+ { DUNDI_IE_KEYCRC32, "KEYCRC32", dump_int },
+ { DUNDI_IE_HINT, "HINT", dump_hint },
+ { DUNDI_IE_DEPARTMENT, "DEPARTMENT", dump_string },
+ { DUNDI_IE_ORGANIZATION, "ORGANIZTN", dump_string },
+ { DUNDI_IE_LOCALITY, "LOCALITY", dump_string },
+ { DUNDI_IE_STATE_PROV, "STATEPROV", dump_string },
+ { DUNDI_IE_COUNTRY, "COUNTRY", dump_string },
+ { DUNDI_IE_EMAIL, "EMAIL", dump_string },
+ { DUNDI_IE_PHONE, "PHONE", dump_string },
+ { DUNDI_IE_IPADDR, "ADDRESS", dump_string },
+ { DUNDI_IE_CACHEBYPASS, "CBYPASS", dump_cbypass },
+};
+
+const char *dundi_ie2str(int ie)
+{
+ int x;
+ for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
+ if (ies[x].ie == ie)
+ return ies[x].name;
+ }
+ return "Unknown IE";
+}
+
+static void dump_ies(unsigned char *iedata, int spaces, int len)
+{
+ int ielen;
+ int ie;
+ int x;
+ int found;
+ char interp[1024];
+ char tmp[1024];
+ if (len < 2)
+ return;
+ while(len >= 2) {
+ ie = iedata[0];
+ ielen = iedata[1];
+ /* Encrypted data is the remainder */
+ if (ie == DUNDI_IE_ENCDATA)
+ ielen = len - 2;
+ if (ielen + 2> len) {
+ snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
+ outputf(tmp);
+ return;
+ }
+ found = 0;
+ for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
+ if (ies[x].ie == ie) {
+ if (ies[x].dump) {
+ ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
+ snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp);
+ outputf(tmp);
+ } else {
+ if (ielen)
+ snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
+ else
+ strcpy(interp, "Present");
+ snprintf(tmp, (int)sizeof(tmp), " %s%-15.15s : %s\n", (spaces ? " " : "" ), ies[x].name, interp);
+ outputf(tmp);
+ }
+ found++;
+ }
+ }
+ if (!found) {
+ snprintf(tmp, (int)sizeof(tmp), " %sUnknown IE %03d : Present\n", (spaces ? " " : "" ), ie);
+ outputf(tmp);
+ }
+ iedata += (2 + ielen);
+ len -= (2 + ielen);
+ }
+ outputf("\n");
+}
+
+void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
+{
+ char *pref[] = {
+ "Tx",
+ "Rx",
+ " ETx",
+ " Erx" };
+ char *commands[] = {
+ "ACK ",
+ "DPDISCOVER ",
+ "DPRESPONSE ",
+ "EIDQUERY ",
+ "EIDRESPONSE ",
+ "PRECACHERQ ",
+ "PRECACHERP ",
+ "INVALID ",
+ "UNKNOWN CMD ",
+ "NULL ",
+ "REQREQ ",
+ "REGRESPONSE ",
+ "CANCEL ",
+ "ENCRYPT ",
+ "ENCREJ " };
+ char class2[20];
+ char *class;
+ char subclass2[20];
+ char *subclass;
+ char tmp[256];
+ char retries[20];
+ if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS)
+ strcpy(retries, "Yes");
+ else
+ strcpy(retries, "No");
+ if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) {
+ /* Ignore frames with high bit set to 1 */
+ return;
+ }
+ if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
+ snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
+ class = class2;
+ } else {
+ class = commands[(int)(fhi->cmdresp & 0x3f)];
+ }
+ snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
+ subclass = subclass2;
+ snprintf(tmp, (int)sizeof(tmp),
+ "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
+ pref[rx],
+ retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
+ outputf(tmp);
+ snprintf(tmp, (int)sizeof(tmp),
+ "%s Flags: %s STrans: %5.5d DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? " " : "",
+ subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
+ ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
+ fhi->cmdresp & 0x80 ? " (Final)" : "");
+ outputf(tmp);
+ dump_ies(fhi->ies, rx > 1, datalen);
+}
+
+int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
+{
+ char tmp[256];
+ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+ snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
+ errorf(tmp);
+ return -1;
+ }
+ ied->buf[ied->pos++] = ie;
+ ied->buf[ied->pos++] = datalen;
+ memcpy(ied->buf + ied->pos, data, datalen);
+ ied->pos += datalen;
+ return 0;
+}
+
+int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *data)
+{
+ char tmp[256];
+ int datalen = data ? strlen(data) + 1 : 1;
+ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+ snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
+ errorf(tmp);
+ return -1;
+ }
+ ied->buf[ied->pos++] = ie;
+ ied->buf[ied->pos++] = datalen;
+ ied->buf[ied->pos++] = cause;
+ memcpy(ied->buf + ied->pos, data, datalen-1);
+ ied->pos += datalen-1;
+ return 0;
+}
+
+int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data)
+{
+ char tmp[256];
+ int datalen = data ? strlen(data) + 2 : 2;
+ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+ snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
+ errorf(tmp);
+ return -1;
+ }
+ ied->buf[ied->pos++] = ie;
+ ied->buf[ied->pos++] = datalen;
+ flags = htons(flags);
+ memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
+ ied->pos += 2;
+ memcpy(ied->buf + ied->pos, data, datalen-1);
+ ied->pos += datalen-2;
+ return 0;
+}
+
+int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
+{
+ char tmp[256];
+ datalen += 16;
+ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+ snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
+ errorf(tmp);
+ return -1;
+ }
+ ied->buf[ied->pos++] = ie;
+ ied->buf[ied->pos++] = datalen;
+ memcpy(ied->buf + ied->pos, iv, 16);
+ ied->pos += 16;
+ if (data) {
+ memcpy(ied->buf + ied->pos, data, datalen-16);
+ ied->pos += datalen-16;
+ }
+ return 0;
+}
+
+int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, char *data)
+{
+ char tmp[256];
+ int datalen = data ? strlen(data) + 11 : 11;
+ int x;
+ unsigned short myw;
+ if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+ snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
+ errorf(tmp);
+ return -1;
+ }
+ ied->buf[ied->pos++] = ie;
+ ied->buf[ied->pos++] = datalen;
+ for (x=0;x<6;x++)
+ ied->buf[ied->pos++] = eid->eid[x];
+ ied->buf[ied->pos++] = protocol;
+ myw = htons(flags);
+ memcpy(ied->buf + ied->pos, &myw, 2);
+ ied->pos += 2;
+ myw = htons(weight);
+ memcpy(ied->buf + ied->pos, &myw, 2);
+ ied->pos += 2;
+ memcpy(ied->buf + ied->pos, data, datalen-11);
+ ied->pos += datalen-11;
+ return 0;
+}
+
+int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
+{
+ return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
+}
+
+int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value)
+{
+ unsigned int newval;
+ newval = htonl(value);
+ return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
+}
+
+int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value)
+{
+ unsigned short newval;
+ newval = htons(value);
+ return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
+}
+
+int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, char *str)
+{
+ return dundi_ie_append_raw(ied, ie, str, strlen(str));
+}
+
+int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
+{
+ return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid));
+}
+
+int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
+{
+ return dundi_ie_append_raw(ied, ie, &dat, 1);
+}
+
+int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie)
+{
+ return dundi_ie_append_raw(ied, ie, NULL, 0);
+}
+
+void dundi_set_output(void (*func)(const char *))
+{
+ outputf = func;
+}
+
+void dundi_set_error(void (*func)(const char *))
+{
+ errorf = func;
+}
+
+int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
+{
+ /* Parse data into information elements */
+ int len;
+ int ie;
+ char tmp[256];
+ memset(ies, 0, (int)sizeof(struct dundi_ies));
+ ies->ttl = -1;
+ ies->expiration = -1;
+ ies->unknowncmd = -1;
+ ies->cause = -1;
+ while(datalen >= 2) {
+ ie = data[0];
+ len = data[1];
+ if (len > datalen - 2) {
+ errorf("Information element length exceeds message size\n");
+ return -1;
+ }
+ switch(ie) {
+ case DUNDI_IE_EID:
+ case DUNDI_IE_EID_DIRECT:
+ if (len != (int)sizeof(dundi_eid)) {
+ errorf("Improper entity identifer, expecting 6 bytes!\n");
+ } else if (ies->eidcount < DUNDI_MAX_STACK) {
+ ies->eids[ies->eidcount] = (dundi_eid *)(data + 2);
+ ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT);
+ ies->eidcount++;
+ } else
+ errorf("Too many entities in stack!\n");
+ break;
+ case DUNDI_IE_REQEID:
+ if (len != (int)sizeof(dundi_eid)) {
+ errorf("Improper requested entity identifer, expecting 6 bytes!\n");
+ } else
+ ies->reqeid = (dundi_eid *)(data + 2);
+ break;
+ case DUNDI_IE_CALLED_CONTEXT:
+ ies->called_context = (char *)data + 2;
+ break;
+ case DUNDI_IE_CALLED_NUMBER:
+ ies->called_number = (char *)data + 2;
+ break;
+ case DUNDI_IE_ANSWER:
+ if (len < sizeof(struct dundi_answer)) {
+ snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len);
+ errorf(tmp);
+ } else {
+ if (ies->anscount < DUNDI_MAX_ANSWERS)
+ ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2);
+ else
+ errorf("Ignoring extra answers!\n");
+ }
+ break;
+ case DUNDI_IE_TTL:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->ttl = ntohs(*((unsigned short *)(data + 2)));
+ break;
+ case DUNDI_IE_VERSION:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->version = ntohs(*((unsigned short *)(data + 2)));
+ break;
+ case DUNDI_IE_EXPIRATION:
+ if (len != (int)sizeof(unsigned short)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+ errorf(tmp);
+ } else
+ ies->expiration = ntohs(*((unsigned short *)(data + 2)));
+ break;
+ case DUNDI_IE_KEYCRC32:
+ if (len != (int)sizeof(unsigned int)) {
+ snprintf(tmp, (int)sizeof(tmp), "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+ errorf(tmp);
+ } else
+ ies->keycrc32 = ntohl(*((unsigned int *)(data + 2)));
+ break;
+ case DUNDI_IE_UNKNOWN:
+ if (len == 1)
+ ies->unknowncmd = data[2];
+ else {
+ snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
+ errorf(tmp);
+ }
+ break;
+ case DUNDI_IE_CAUSE:
+ if (len >= 1) {
+ ies->cause = data[2];
+ ies->causestr = (char *)data + 3;
+ } else {
+ snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len);
+ errorf(tmp);
+ }
+ break;
+ case DUNDI_IE_HINT:
+ if (len >= 2) {
+ ies->hint = (struct dundi_hint *)(data + 2);
+ } else {
+ snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len);
+ errorf(tmp);
+ }
+ break;
+ case DUNDI_IE_DEPARTMENT:
+ ies->q_dept = (char *)data + 2;
+ break;
+ case DUNDI_IE_ORGANIZATION:
+ ies->q_org = (char *)data + 2;
+ break;
+ case DUNDI_IE_LOCALITY:
+ ies->q_locality = (char *)data + 2;
+ break;
+ case DUNDI_IE_STATE_PROV:
+ ies->q_stateprov = (char *)data + 2;
+ break;
+ case DUNDI_IE_COUNTRY:
+ ies->q_country = (char *)data + 2;
+ break;
+ case DUNDI_IE_EMAIL:
+ ies->q_email = (char *)data + 2;
+ break;
+ case DUNDI_IE_PHONE:
+ ies->q_phone = (char *)data + 2;
+ break;
+ case DUNDI_IE_IPADDR:
+ ies->q_ipaddr = (char *)data + 2;
+ break;
+ case DUNDI_IE_ENCDATA:
+ /* Recalculate len as the remainder of the message, regardless of
+ theoretical length */
+ len = datalen - 2;
+ if ((len > 16) && !(len % 16)) {
+ ies->encblock = (struct dundi_encblock *)(data + 2);
+ ies->enclen = len - 16;
+ } else {
+ snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len);
+ errorf(tmp);
+ }
+ break;
+ case DUNDI_IE_SHAREDKEY:
+ if (len == 128) {
+ ies->encsharedkey = (unsigned char *)(data + 2);
+ } else {
+ snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len);
+ errorf(tmp);
+ }
+ break;
+ case DUNDI_IE_SIGNATURE:
+ if (len == 128) {
+ ies->encsig = (unsigned char *)(data + 2);
+ } else {
+ snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len);
+ errorf(tmp);
+ }
+ break;
+ case DUNDI_IE_CACHEBYPASS:
+ ies->cbypass = 1;
+ break;
+ default:
+ snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len);
+ outputf(tmp);
+ }
+ /* Overwrite information element with 0, to null terminate previous portion */
+ data[0] = 0;
+ datalen -= (len + 2);
+ data += (len + 2);
+ }
+ /* Null-terminate last field */
+ *data = '\0';
+ if (datalen) {
+ errorf("Invalid information element contents, strange boundary\n");
+ return -1;
+ }
+ return 0;
+}
diff --git a/trunk/pbx/dundi-parser.h b/trunk/pbx/dundi-parser.h
new file mode 100644
index 000000000..8ff772347
--- /dev/null
+++ b/trunk/pbx/dundi-parser.h
@@ -0,0 +1,88 @@
+/*
+ * Distributed Universal Number Discovery (DUNDi)
+ *
+ * Copyright (C) 2004 - 2005, Digium Inc.
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ *
+ * This program is Free Software distributed under the terms of
+ * of the GNU General Public License.
+ */
+
+#ifndef _DUNDI_PARSER_H
+#define _DUNDI_PARSER_H
+
+#include "asterisk/dundi.h"
+#include "asterisk/aes.h"
+
+#define DUNDI_MAX_STACK 512
+#define DUNDI_MAX_ANSWERS 100
+
+struct dundi_ies {
+ dundi_eid *eids[DUNDI_MAX_STACK + 1];
+ int eid_direct[DUNDI_MAX_STACK + 1];
+ dundi_eid *reqeid;
+ int eidcount;
+ char *called_context;
+ char *called_number;
+ struct dundi_answer *answers[DUNDI_MAX_ANSWERS + 1];
+ struct dundi_hint *hint;
+ int anscount;
+ int ttl;
+ int version;
+ int expiration;
+ int unknowncmd;
+ unsigned char *pubkey;
+ int cause;
+ char *q_dept;
+ char *q_org;
+ char *q_locality;
+ char *q_stateprov;
+ char *q_country;
+ char *q_email;
+ char *q_phone;
+ char *q_ipaddr;
+ char *causestr;
+ unsigned char *encsharedkey;
+ unsigned char *encsig;
+ unsigned long keycrc32;
+ struct dundi_encblock *encblock;
+ int enclen;
+ int cbypass;
+};
+
+struct dundi_ie_data {
+ int pos;
+ unsigned char buf[8192];
+};
+
+/* Choose a different function for output */
+extern void dundi_set_output(void (*output)(const char *data));
+/* Choose a different function for errors */
+extern void dundi_set_error(void (*output)(const char *data));
+extern void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen);
+
+extern const char *dundi_ie2str(int ie);
+
+extern int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen);
+extern int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin);
+extern int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value);
+extern int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value);
+extern int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, char *str);
+extern int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid);
+extern int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *desc);
+extern int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data);
+extern int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, char *desc);
+extern int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen);
+extern int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat);
+extern int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie);
+extern int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen);
+extern char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid);
+extern char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid);
+extern int dundi_str_to_eid(dundi_eid *eid, const char *s);
+extern int dundi_str_short_to_eid(dundi_eid *eid, const char *s);
+extern int dundi_eid_zero(dundi_eid *eid);
+extern int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2);
+extern char *dundi_flags2str(char *s, int maxlen, int flags);
+extern char *dundi_hint2str(char *s, int maxlen, int flags);
+#endif
diff --git a/trunk/pbx/pbx_ael.c b/trunk/pbx/pbx_ael.c
new file mode 100644
index 000000000..cffa65f40
--- /dev/null
+++ b/trunk/pbx/pbx_ael.c
@@ -0,0 +1,313 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.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 Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
+ *
+ */
+
+/*** MODULEINFO
+ <depend>res_ael_share</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <regex.h>
+#include <sys/stat.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/callerid.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/pval.h"
+#ifdef AAL_ARGCHECK
+#include "asterisk/argdesc.h"
+#endif
+
+/* these functions are in ../ast_expr2.fl */
+
+#define DEBUG_READ (1 << 0)
+#define DEBUG_TOKENS (1 << 1)
+#define DEBUG_MACROS (1 << 2)
+#define DEBUG_CONTEXTS (1 << 3)
+
+static char *config = "extensions.ael";
+static char *registrar = "pbx_ael";
+static int pbx_load_module(void);
+
+#ifndef AAL_ARGCHECK
+/* for the time being, short circuit all the AAL related structures
+ without permanently removing the code; after/during the AAL
+ development, this code can be properly re-instated
+*/
+
+#endif
+
+#ifdef AAL_ARGCHECK
+int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
+int option_matches( struct argdesc *should, pval *is, struct argapp *app);
+int ael_is_funcname(char *name);
+#endif
+
+int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
+void check_pval(pval *item, struct argapp *apps, int in_globals);
+void check_pval_item(pval *item, struct argapp *apps, int in_globals);
+void check_switch_expr(pval *item, struct argapp *apps);
+void ast_expr_register_extra_error_info(char *errmsg);
+void ast_expr_clear_extra_error_info(void);
+struct pval *find_macro(char *name);
+struct pval *find_context(char *name);
+struct pval *find_context(char *name);
+struct pval *find_macro(char *name);
+struct ael_priority *new_prio(void);
+struct ael_extension *new_exten(void);
+void linkprio(struct ael_extension *exten, struct ael_priority *prio);
+void destroy_extensions(struct ael_extension *exten);
+void set_priorities(struct ael_extension *exten);
+void add_extensions(struct ael_extension *exten);
+void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root);
+void destroy_pval(pval *item);
+void destroy_pval_item(pval *item);
+int is_float(char *arg );
+int is_int(char *arg );
+int is_empty(char *arg);
+
+/* static void substitute_commas(char *str); */
+
+static int aeldebug = 0;
+
+/* interface stuff */
+
+/* if all the below are static, who cares if they are present? */
+
+static int pbx_load_module(void)
+{
+ int errs=0, sem_err=0, sem_warn=0, sem_note=0;
+ char *rfilename;
+ struct ast_context *local_contexts=NULL, *con;
+ struct pval *parse_tree;
+
+ ast_log(LOG_NOTICE, "Starting AEL load process.\n");
+ if (config[0] == '/')
+ rfilename = (char *)config;
+ else {
+ rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
+ sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
+ }
+ if (access(rfilename,R_OK) != 0) {
+ ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ parse_tree = ael2_parse(rfilename, &errs);
+ ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
+ ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
+ if (errs == 0 && sem_err == 0) {
+ ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
+ ast_compile_ael2(&local_contexts, parse_tree);
+ ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
+
+ ast_merge_contexts_and_delete(&local_contexts, registrar);
+ ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
+ for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
+ ast_context_verify_includes(con);
+ ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
+ } else {
+ ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err);
+ destroy_pval(parse_tree); /* free up the memory */
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ destroy_pval(parse_tree); /* free up the memory */
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+/* CLI interface */
+static char *handle_cli_ael_debug_multiple(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ael debug [read|tokens|macros|contexts|off]";
+ e->usage =
+ "Usage: ael debug [read|tokens|macros|contexts|off]\n"
+ " Enable AEL read, token, macro, or context debugging,\n"
+ " or disable all AEL debugging messages. Note: this\n"
+ " currently does nothing.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ if (!strcasecmp(a->argv[2], "read"))
+ aeldebug |= DEBUG_READ;
+ else if (!strcasecmp(a->argv[2], "tokens"))
+ aeldebug |= DEBUG_TOKENS;
+ else if (!strcasecmp(a->argv[2], "macros"))
+ aeldebug |= DEBUG_MACROS;
+ else if (!strcasecmp(a->argv[2], "contexts"))
+ aeldebug |= DEBUG_CONTEXTS;
+ else if (!strcasecmp(a->argv[2], "off"))
+ aeldebug = 0;
+ else
+ return CLI_SHOWUSAGE;
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_ael_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ael reload";
+ e->usage =
+ "Usage: ael reload\n"
+ " Reloads AEL configuration.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ return (pbx_load_module() ? CLI_FAILURE : CLI_SUCCESS);
+}
+
+static struct ast_cli_entry cli_ael[] = {
+ AST_CLI_DEFINE(handle_cli_ael_reload, "Reload AEL configuration"),
+ AST_CLI_DEFINE(handle_cli_ael_debug_multiple, "Enable AEL debugging flags")
+};
+
+static int unload_module(void)
+{
+ ast_context_destroy(NULL, registrar);
+ ast_cli_unregister_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
+ return 0;
+}
+
+static int load_module(void)
+{
+ ast_cli_register_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
+ return (pbx_load_module());
+}
+
+static int reload(void)
+{
+ return pbx_load_module();
+}
+
+#ifdef STANDALONE_AEL
+#define AST_MODULE "ael"
+int ael_external_load_module(void);
+int ael_external_load_module(void)
+{
+ pbx_load_module();
+ return 1;
+}
+#endif
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
+#ifdef AAL_ARGCHECK
+static char *ael_funclist[] =
+{
+ "AGENT",
+ "ARRAY",
+ "BASE64_DECODE",
+ "BASE64_ENCODE",
+ "CALLERID",
+ "CDR",
+ "CHANNEL",
+ "CHECKSIPDOMAIN",
+ "CHECK_MD5",
+ "CURL",
+ "CUT",
+ "DB",
+ "DB_EXISTS",
+ "DUNDILOOKUP",
+ "ENUMLOOKUP",
+ "ENV",
+ "EVAL",
+ "EXISTS",
+ "FIELDQTY",
+ "FILTER",
+ "GROUP",
+ "GROUP_COUNT",
+ "GROUP_LIST",
+ "GROUP_MATCH_COUNT",
+ "IAXPEER",
+ "IF",
+ "IFTIME",
+ "ISNULL",
+ "KEYPADHASH",
+ "LANGUAGE",
+ "LEN",
+ "MATH",
+ "MD5",
+ "MUSICCLASS",
+ "QUEUEAGENTCOUNT",
+ "QUEUE_MEMBER_COUNT",
+ "QUEUE_MEMBER_LIST",
+ "QUOTE",
+ "RAND",
+ "REGEX",
+ "SET",
+ "SHA1",
+ "SIPCHANINFO",
+ "SIPPEER",
+ "SIP_HEADER",
+ "SORT",
+ "STAT",
+ "STRFTIME",
+ "STRPTIME",
+ "TIMEOUT",
+ "TXTCIDNAME",
+ "URIDECODE",
+ "URIENCODE",
+ "VMCOUNT"
+};
+
+
+int ael_is_funcname(char *name)
+{
+ int s,t;
+ t = sizeof(ael_funclist)/sizeof(char*);
+ s = 0;
+ while ((s < t) && strcasecmp(name, ael_funclist[s]))
+ s++;
+ if ( s < t )
+ return 1;
+ else
+ return 0;
+}
+#endif
diff --git a/trunk/pbx/pbx_config.c b/trunk/pbx/pbx_config.c
new file mode 100644
index 000000000..4a7a3d368
--- /dev/null
+++ b/trunk/pbx/pbx_config.c
@@ -0,0 +1,1668 @@
+/*
+ * 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 Populate and remember extensions from static config file
+ *
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+
+#include "asterisk/paths.h" /* ast_config_AST_CONFIG_DIR */
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/cli.h"
+#include "asterisk/channel.h" /* AST_MAX_EXTENSION */
+#include "asterisk/callerid.h"
+
+static char *config = "extensions.conf";
+static char *registrar = "pbx_config";
+static char userscontext[AST_MAX_EXTENSION] = "default";
+
+static int static_config = 0;
+static int write_protect_config = 1;
+static int autofallthrough_config = 1;
+static int clearglobalvars_config = 0;
+static int extenpatternmatchnew_config = 0;
+
+AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
+
+static struct ast_context *local_contexts = NULL;
+
+/*
+ * Prototypes for our completion functions
+ */
+static char *complete_dialplan_remove_include(struct ast_cli_args *);
+static char *complete_dialplan_add_include(struct ast_cli_args *);
+static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *);
+static char *complete_dialplan_add_ignorepat(struct ast_cli_args *);
+static char *complete_dialplan_remove_extension(struct ast_cli_args *);
+static char *complete_dialplan_add_extension(struct ast_cli_args *);
+
+/*
+ * Implementation of functions provided by this module
+ */
+
+/*!
+ * REMOVE INCLUDE command stuff
+ */
+static char *handle_cli_dialplan_remove_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan remove include";
+ e->usage =
+ "Usage: dialplan remove include <context> from <context>\n"
+ " Remove an included context from another context.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_dialplan_remove_include(a);
+ }
+
+ if (strcmp(a->argv[4], "from"))
+ return CLI_SHOWUSAGE;
+
+ if (!ast_context_remove_include(a->argv[5], a->argv[3], registrar)) {
+ ast_cli(a->fd, "We are not including '%s' into '%s' now\n",
+ a->argv[3], a->argv[5]);
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd, "Failed to remove '%s' include from '%s' context\n",
+ a->argv[3], a->argv[5]);
+ return CLI_FAILURE;
+}
+
+/*! \brief return true if 'name' is included by context c */
+static int lookup_ci(struct ast_context *c, const char *name)
+{
+ struct ast_include *i = NULL;
+
+ if (ast_rdlock_context(c)) /* error, skip */
+ return 0;
+ while ( (i = ast_walk_context_includes(c, i)) )
+ if (!strcmp(name, ast_get_include_name(i)))
+ break;
+ ast_unlock_context(c);
+ return i ? -1 /* success */ : 0;
+}
+
+/*! \brief return true if 'name' is in the ignorepats for context c */
+static int lookup_c_ip(struct ast_context *c, const char *name)
+{
+ struct ast_ignorepat *ip = NULL;
+
+ if (ast_rdlock_context(c)) /* error, skip */
+ return 0;
+ while ( (ip = ast_walk_context_ignorepats(c, ip)) )
+ if (!strcmp(name, ast_get_ignorepat_name(ip)))
+ break;
+ ast_unlock_context(c);
+ return ip ? -1 /* success */ : 0;
+}
+
+/*! \brief moves to the n-th word in the string, or empty string if none */
+static const char *skip_words(const char *p, int n)
+{
+ int in_blank = 0;
+ for (;n && *p; p++) {
+ if (isblank(*p) /* XXX order is important */ && !in_blank) {
+ n--; /* one word is gone */
+ in_blank = 1;
+ } else if (/* !is_blank(*p), we know already, && */ in_blank) {
+ in_blank = 0;
+ }
+ }
+ return p;
+}
+
+/*! \brief match the first 'len' chars of word. len==0 always succeeds */
+static int partial_match(const char *s, const char *word, int len)
+{
+ return (len == 0 || !strncmp(s, word, len));
+}
+
+/*! \brief split extension\@context in two parts, return -1 on error.
+ * The return string is malloc'ed and pointed by *ext
+ */
+static int split_ec(const char *src, char **ext, char ** const ctx)
+{
+ char *c, *e = ast_strdup(src); /* now src is not used anymore */
+
+ if (e == NULL)
+ return -1; /* malloc error */
+ /* now, parse values from 'exten@context' */
+ *ext = e;
+ c = strchr(e, '@');
+ if (c == NULL) /* no context part */
+ *ctx = ""; /* it is not overwritten, anyways */
+ else { /* found context, check for duplicity ... */
+ *c++ = '\0';
+ *ctx = c;
+ if (strchr(c, '@')) { /* two @, not allowed */
+ free(e);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* _X_ is the string we need to complete */
+static char *complete_dialplan_remove_include(struct ast_cli_args *a)
+{
+ int which = 0;
+ char *res = NULL;
+ int len = strlen(a->word); /* how many bytes to match */
+ struct ast_context *c = NULL;
+
+ if (a->pos == 3) { /* "dialplan remove include _X_" */
+ if (ast_wrlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock context list\n");
+ return NULL;
+ }
+ /* walk contexts and their includes, return the n-th match */
+ while (!res && (c = ast_walk_contexts(c))) {
+ struct ast_include *i = NULL;
+
+ if (ast_rdlock_context(c)) /* error ? skip this one */
+ continue;
+
+ while ( !res && (i = ast_walk_context_includes(c, i)) ) {
+ const char *i_name = ast_get_include_name(i);
+ struct ast_context *nc = NULL;
+ int already_served = 0;
+
+ if (!partial_match(i_name, a->word, len))
+ continue; /* not matched */
+
+ /* check if this include is already served or not */
+
+ /* go through all contexts again till we reach actual
+ * context or already_served = 1
+ */
+ while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
+ already_served = lookup_ci(nc, i_name);
+
+ if (!already_served && ++which > a->n)
+ res = strdup(i_name);
+ }
+ ast_unlock_context(c);
+ }
+
+ ast_unlock_contexts();
+ return res;
+ } else if (a->pos == 4) { /* "dialplan remove include CTX _X_" */
+ /*
+ * complete as 'from', but only if previous context is really
+ * included somewhere
+ */
+ char *context, *dupline;
+ const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */
+
+ if (a->n > 0)
+ return NULL;
+ context = dupline = strdup(s);
+ if (!dupline) {
+ ast_log(LOG_ERROR, "Out of free memory\n");
+ return NULL;
+ }
+ strsep(&dupline, " ");
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock contexts list\n");
+ free(context);
+ return NULL;
+ }
+
+ /* go through all contexts and check if is included ... */
+ while (!res && (c = ast_walk_contexts(c)))
+ if (lookup_ci(c, context)) /* context is really included, complete "from" command */
+ res = strdup("from");
+ ast_unlock_contexts();
+ if (!res)
+ ast_log(LOG_WARNING, "%s not included anywhere\n", context);
+ free(context);
+ return res;
+ } else if (a->pos == 5) { /* "dialplan remove include CTX from _X_" */
+ /*
+ * Context from which we removing include ...
+ */
+ char *context, *dupline, *from;
+ const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */
+ context = dupline = strdup(s);
+ if (!dupline) {
+ ast_log(LOG_ERROR, "Out of free memory\n");
+ return NULL;
+ }
+
+ strsep(&dupline, " "); /* skip context */
+
+ /* fourth word must be 'from' */
+ from = strsep(&dupline, " ");
+ if (!from || strcmp(from, "from")) {
+ free(context);
+ return NULL;
+ }
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock context list\n");
+ free(context);
+ return NULL;
+ }
+
+ /* walk through all contexts ... */
+ c = NULL;
+ while ( !res && (c = ast_walk_contexts(c))) {
+ const char *c_name = ast_get_context_name(c);
+ if (!partial_match(c_name, a->word, len)) /* not a good target */
+ continue;
+ /* walk through all includes and check if it is our context */
+ if (lookup_ci(c, context) && ++which > a->n)
+ res = strdup(c_name);
+ }
+ ast_unlock_contexts();
+ free(context);
+ return res;
+ }
+
+ return NULL;
+}
+
+/*!
+ * REMOVE EXTENSION command stuff
+ */
+static char *handle_cli_dialplan_remove_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int removing_priority = 0;
+ char *exten, *context;
+ char *ret = CLI_FAILURE;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan remove extension";
+ e->usage =
+ "Usage: dialplan remove extension exten@context [priority]\n"
+ " Remove an extension from a given context. If a priority\n"
+ " is given, only that specific priority from the given extension\n"
+ " will be removed.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_dialplan_remove_extension(a);
+ }
+
+ if (a->argc != 5 && a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ /*
+ * Priority input checking ...
+ */
+ if (a->argc == 5) {
+ char *c = a->argv[4];
+
+ /* check for digits in whole parameter for right priority ...
+ * why? because atoi (strtol) returns 0 if any characters in
+ * string and whole extension will be removed, it's not good
+ */
+ if (!strcmp("hint", c))
+ removing_priority = PRIORITY_HINT;
+ else {
+ while (*c && isdigit(*c))
+ c++;
+ if (*c) { /* non-digit in string */
+ ast_cli(a->fd, "Invalid priority '%s'\n", a->argv[4]);
+ return CLI_FAILURE;
+ }
+ removing_priority = atoi(a->argv[4]);
+ }
+
+ if (removing_priority == 0) {
+ ast_cli(a->fd, "If you want to remove whole extension, please " \
+ "omit priority argument\n");
+ return CLI_FAILURE;
+ }
+ }
+
+ /* XXX original overwrote argv[3] */
+ /*
+ * Format exten@context checking ...
+ */
+ if (split_ec(a->argv[3], &exten, &context))
+ return CLI_FAILURE; /* XXX malloc failure */
+ if ((!strlen(exten)) || (!(strlen(context)))) {
+ ast_cli(a->fd, "Missing extension or context name in third argument '%s'\n",
+ a->argv[3]);
+ free(exten);
+ return CLI_FAILURE;
+ }
+
+ if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
+ if (!removing_priority)
+ ast_cli(a->fd, "Whole extension %s@%s removed\n",
+ exten, context);
+ else
+ ast_cli(a->fd, "Extension %s@%s with priority %d removed\n",
+ exten, context, removing_priority);
+
+ ret = CLI_SUCCESS;
+ } else {
+ ast_cli(a->fd, "Failed to remove extension %s@%s\n", exten, context);
+ ret = CLI_FAILURE;
+ }
+ free(exten);
+ return ret;
+}
+
+#define BROKEN_READLINE 1
+
+#ifdef BROKEN_READLINE
+/*
+ * There is one funny thing, when you have word like 300@ and you hit
+ * <tab>, you arguments will like as your word is '300 ', so it '@'
+ * characters acts sometimes as word delimiter and sometimes as a part
+ * of word
+ *
+ * This fix function, allocates new word variable and store here every
+ * time xxx@yyy always as one word and correct pos is set too
+ *
+ * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
+ * bug or feature ...
+ */
+static int fix_complete_args(const char *line, char **word, int *pos)
+{
+ char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
+ int words = 0;
+
+ _line = strdup(line);
+
+ _strsep_line = _line;
+ while (_strsep_line) {
+ _previous_word = _word;
+ _word = strsep(&_strsep_line, " ");
+
+ if (_word && strlen(_word)) words++;
+ }
+
+
+ if (_word || _previous_word) {
+ if (_word) {
+ if (!strlen(_word)) words++;
+ *word = strdup(_word);
+ } else
+ *word = strdup(_previous_word);
+ *pos = words - 1;
+ free(_line);
+ return 0;
+ }
+
+ free(_line);
+ return -1;
+}
+#endif /* BROKEN_READLINE */
+
+static char *complete_dialplan_remove_extension(struct ast_cli_args *a)
+{
+ char *ret = NULL;
+ int which = 0;
+
+#ifdef BROKEN_READLINE
+ char *word2;
+ /*
+ * Fix arguments, *word is a new allocated structure, REMEMBER to
+ * free *word when you want to return from this function ...
+ */
+ if (fix_complete_args(a->line, &word2, &a->pos)) {
+ ast_log(LOG_ERROR, "Out of free memory\n");
+ return NULL;
+ }
+ a->word = word2;
+#endif
+
+ if (a->pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
+ struct ast_context *c = NULL;
+ char *context = NULL, *exten = NULL;
+ int le = 0; /* length of extension */
+ int lc = 0; /* length of context */
+
+ lc = split_ec(a->word, &exten, &context);
+#ifdef BROKEN_READLINE
+ free(word2);
+#endif
+ if (lc) /* error */
+ return NULL;
+ le = strlen(exten);
+ lc = strlen(context);
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock context list\n");
+ goto error2;
+ }
+
+ /* find our context ... */
+ while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */
+ struct ast_exten *e = NULL;
+ /* XXX locking ? */
+ if (!partial_match(ast_get_context_name(c), context, lc))
+ continue; /* context not matched */
+ while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
+ if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > a->n) { /* n-th match */
+ /* If there is an extension then return exten@context. XXX otherwise ? */
+ if (exten)
+ asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c));
+ break;
+ }
+ }
+ if (e) /* got a match */
+ break;
+ }
+
+ ast_unlock_contexts();
+ error2:
+ if (exten)
+ free(exten);
+ } else if (a->pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
+ char *exten = NULL, *context, *p;
+ struct ast_context *c;
+ int le, lc, len;
+ const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'extension' */
+ int i = split_ec(s, &exten, &context); /* parse ext@context */
+
+ if (i) /* error */
+ goto error3;
+ if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
+ *p = '\0';
+ if ( (p = strchr(context, ' ')) ) /* remove space after context */
+ *p = '\0';
+ le = strlen(exten);
+ lc = strlen(context);
+ len = strlen(a->word);
+ if (le == 0 || lc == 0)
+ goto error3;
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock context list\n");
+ goto error3;
+ }
+
+ /* walk contexts */
+ c = NULL;
+ while ( (c = ast_walk_contexts(c)) ) {
+ /* XXX locking on c ? */
+ struct ast_exten *e;
+ if (strcmp(ast_get_context_name(c), context) != 0)
+ continue;
+ /* got it, we must match here */
+ e = NULL;
+ while ( (e = ast_walk_context_extensions(c, e)) ) {
+ struct ast_exten *priority;
+ char buffer[10];
+
+ if (strcmp(ast_get_extension_name(e), exten) != 0)
+ continue;
+ /* XXX lock e ? */
+ priority = NULL;
+ while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
+ snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
+ if (partial_match(buffer, a->word, len) && ++which > a->n) /* n-th match */
+ ret = strdup(buffer);
+ }
+ break;
+ }
+ break;
+ }
+ ast_unlock_contexts();
+ error3:
+ if (exten)
+ free(exten);
+#ifdef BROKEN_READLINE
+ free(word2);
+#endif
+ }
+ return ret;
+}
+
+/*!
+ * Include context ...
+ */
+static char *handle_cli_dialplan_add_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan add include";
+ e->usage =
+ "Usage: dialplan add include <context> into <context>\n"
+ " Include a context in another context.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_dialplan_add_include(a);
+ }
+
+ if (a->argc != 6) /* dialplan add include CTX in CTX */
+ return CLI_SHOWUSAGE;
+
+ /* fifth arg must be 'into' ... */
+ if (strcmp(a->argv[4], "into"))
+ return CLI_SHOWUSAGE;
+
+ if (ast_context_add_include(a->argv[5], a->argv[3], registrar)) {
+ switch (errno) {
+ case ENOMEM:
+ ast_cli(a->fd, "Out of memory for context addition\n");
+ break;
+
+ case EBUSY:
+ ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
+ break;
+
+ case EEXIST:
+ ast_cli(a->fd, "Context '%s' already included in '%s' context\n",
+ a->argv[3], a->argv[5]);
+ break;
+
+ case ENOENT:
+ case EINVAL:
+ ast_cli(a->fd, "There is no existence of context '%s'\n",
+ errno == ENOENT ? a->argv[5] : a->argv[3]);
+ break;
+
+ default:
+ ast_cli(a->fd, "Failed to include '%s' in '%s' context\n",
+ a->argv[3], a->argv[5]);
+ break;
+ }
+ return CLI_FAILURE;
+ }
+
+ /* show some info ... */
+ ast_cli(a->fd, "Context '%s' included in '%s' context\n",
+ a->argv[3], a->argv[5]);
+
+ return CLI_SUCCESS;
+}
+
+static char *complete_dialplan_add_include(struct ast_cli_args *a)
+{
+ struct ast_context *c;
+ int which = 0;
+ char *ret = NULL;
+ int len = strlen(a->word);
+
+ if (a->pos == 3) { /* 'dialplan add include _X_' (context) ... */
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock context list\n");
+ return NULL;
+ }
+ for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
+ if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n)
+ ret = strdup(ast_get_context_name(c));
+ ast_unlock_contexts();
+ return ret;
+ } else if (a->pos == 4) { /* dialplan add include CTX _X_ */
+ /* complete as 'into' if context exists or we are unable to check */
+ char *context, *dupline;
+ struct ast_context *c;
+ const char *s = skip_words(a->line, 3); /* should not fail */
+
+ if (a->n != 0) /* only once */
+ return NULL;
+
+ /* parse context from line ... */
+ context = dupline = strdup(s);
+ if (!context) {
+ ast_log(LOG_ERROR, "Out of free memory\n");
+ return strdup("into");
+ }
+ strsep(&dupline, " ");
+
+ /* check for context existence ... */
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock context list\n");
+ /* our fault, we can't check, so complete 'into' ... */
+ ret = strdup("into");
+ } else {
+ for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
+ if (!strcmp(context, ast_get_context_name(c)))
+ ret = strdup("into"); /* found */
+ ast_unlock_contexts();
+ }
+ free(context);
+ return ret;
+ } else if (a->pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
+ char *context, *dupline, *into;
+ const char *s = skip_words(a->line, 3); /* should not fail */
+ context = dupline = strdup(s);
+ if (!dupline) {
+ ast_log(LOG_ERROR, "Out of free memory\n");
+ return NULL;
+ }
+ strsep(&dupline, " "); /* skip context */
+ into = strsep(&dupline, " ");
+ /* error if missing context or fifth word is not 'into' */
+ if (!strlen(context) || strcmp(into, "into")) {
+ ast_log(LOG_ERROR, "bad context %s or missing into %s\n",
+ context, into);
+ goto error3;
+ }
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock context list\n");
+ goto error3;
+ }
+
+ for (c = NULL; (c = ast_walk_contexts(c)); )
+ if (!strcmp(context, ast_get_context_name(c)))
+ break;
+ if (c) { /* first context exists, go on... */
+ /* go through all contexts ... */
+ for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
+ if (!strcmp(context, ast_get_context_name(c)))
+ continue; /* skip ourselves */
+ if (partial_match(ast_get_context_name(c), a->word, len) &&
+ !lookup_ci(c, context) /* not included yet */ &&
+ ++which > a->n)
+ ret = strdup(ast_get_context_name(c));
+ }
+ } else {
+ ast_log(LOG_ERROR, "context %s not found\n", context);
+ }
+ ast_unlock_contexts();
+ error3:
+ free(context);
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief 'save dialplan' CLI command implementation functions ...
+ */
+static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char filename[256];
+ struct ast_context *c;
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ int incomplete = 0; /* incomplete config write? */
+ FILE *output;
+ struct ast_flags config_flags = { 0 };
+ const char *base, *slash, *file;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan save";
+ e->usage =
+ "Usage: dialplan save [/path/to/extension/file]\n"
+ " Save dialplan created by pbx_config module.\n"
+ "\n"
+ "Example: dialplan save (/etc/asterisk/extensions.conf)\n"
+ " dialplan save /home/markster (/home/markster/extensions.conf)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (! (static_config && !write_protect_config)) {
+ ast_cli(a->fd,
+ "I can't save dialplan now, see '%s' example file.\n",
+ config);
+ return CLI_FAILURE;
+ }
+
+ if (a->argc != 2 && a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ if (ast_mutex_lock(&save_dialplan_lock)) {
+ ast_cli(a->fd,
+ "Failed to lock dialplan saving (another proccess saving?)\n");
+ return CLI_FAILURE;
+ }
+ /* XXX the code here is quite loose, a pathname with .conf in it
+ * is assumed to be a complete pathname
+ */
+ if (a->argc == 3) { /* have config path. Look for *.conf */
+ base = a->argv[2];
+ if (!strstr(a->argv[2], ".conf")) { /*no, this is assumed to be a pathname */
+ /* if filename ends with '/', do not add one */
+ slash = (*(a->argv[2] + strlen(a->argv[2]) -1) == '/') ? "/" : "";
+ file = config; /* default: 'extensions.conf' */
+ } else { /* yes, complete file name */
+ slash = "";
+ file = "";
+ }
+ } else {
+ /* no config file, default one */
+ base = ast_config_AST_CONFIG_DIR;
+ slash = "/";
+ file = config;
+ }
+ snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
+
+ cfg = ast_config_load("extensions.conf", config_flags);
+
+ /* try to lock contexts list */
+ if (ast_rdlock_contexts()) {
+ ast_cli(a->fd, "Failed to lock contexts list\n");
+ ast_mutex_unlock(&save_dialplan_lock);
+ ast_config_destroy(cfg);
+ return CLI_FAILURE;
+ }
+
+ /* create new file ... */
+ if (!(output = fopen(filename, "wt"))) {
+ ast_cli(a->fd, "Failed to create file '%s'\n",
+ filename);
+ ast_unlock_contexts();
+ ast_mutex_unlock(&save_dialplan_lock);
+ ast_config_destroy(cfg);
+ return CLI_FAILURE;
+ }
+
+ /* fireout general info */
+ fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\nextenpatternmatchnew=%s\n\n",
+ static_config ? "yes" : "no",
+ write_protect_config ? "yes" : "no",
+ autofallthrough_config ? "yes" : "no",
+ clearglobalvars_config ? "yes" : "no",
+ extenpatternmatchnew_config ? "yes" : "no");
+
+ if ((v = ast_variable_browse(cfg, "globals"))) {
+ fprintf(output, "[globals]\n");
+ while(v) {
+ fprintf(output, "%s => %s\n", v->name, v->value);
+ v = v->next;
+ }
+ fprintf(output, "\n");
+ }
+
+ ast_config_destroy(cfg);
+
+#define PUT_CTX_HDR do { \
+ if (!context_header_written) { \
+ fprintf(output, "[%s]\n", ast_get_context_name(c)); \
+ context_header_written = 1; \
+ } \
+ } while (0)
+
+ /* walk all contexts */
+ for (c = NULL; (c = ast_walk_contexts(c)); ) {
+ int context_header_written = 0;
+ struct ast_exten *e, *last_written_e = NULL;
+ struct ast_include *i;
+ struct ast_ignorepat *ip;
+ struct ast_sw *sw;
+
+ /* try to lock context and fireout all info */
+ if (ast_rdlock_context(c)) { /* lock failure */
+ incomplete = 1;
+ continue;
+ }
+ /* registered by this module? */
+ /* XXX do we need this ? */
+ if (!strcmp(ast_get_context_registrar(c), registrar)) {
+ fprintf(output, "[%s]\n", ast_get_context_name(c));
+ context_header_written = 1;
+ }
+
+ /* walk extensions ... */
+ for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) {
+ struct ast_exten *p = NULL;
+
+ /* fireout priorities */
+ while ( (p = ast_walk_extension_priorities(e, p)) ) {
+ if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
+ continue;
+
+ /* make empty line between different extensions */
+ if (last_written_e != NULL &&
+ strcmp(ast_get_extension_name(last_written_e),
+ ast_get_extension_name(p)))
+ fprintf(output, "\n");
+ last_written_e = p;
+
+ PUT_CTX_HDR;
+
+ if (ast_get_extension_priority(p) == PRIORITY_HINT) { /* easy */
+ fprintf(output, "exten => %s,hint,%s\n",
+ ast_get_extension_name(p),
+ ast_get_extension_app(p));
+ } else {
+ const char *sep, *cid;
+ const char *el = ast_get_extension_label(p);
+ char label[128] = "";
+
+ if (ast_get_extension_matchcid(p)) {
+ sep = "/";
+ cid = ast_get_extension_cidmatch(p);
+ } else
+ sep = cid = "";
+
+ if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2)))
+ incomplete = 1; /* error encountered or label > 125 chars */
+
+ fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
+ ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid),
+ ast_get_extension_priority(p), label,
+ ast_get_extension_app(p), (ast_strlen_zero(ast_get_extension_app_data(p)) ? "" : (const char *)ast_get_extension_app_data(p)));
+ }
+ }
+ }
+
+ /* written any extensions? ok, write space between exten & inc */
+ if (last_written_e)
+ fprintf(output, "\n");
+
+ /* walk through includes */
+ for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) {
+ if (strcmp(ast_get_include_registrar(i), registrar) != 0)
+ continue; /* not mine */
+ PUT_CTX_HDR;
+ fprintf(output, "include => %s\n", ast_get_include_name(i));
+ }
+ if (ast_walk_context_includes(c, NULL))
+ fprintf(output, "\n");
+
+ /* walk through switches */
+ for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
+ if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
+ continue; /* not mine */
+ PUT_CTX_HDR;
+ fprintf(output, "switch => %s/%s\n",
+ ast_get_switch_name(sw), ast_get_switch_data(sw));
+ }
+
+ if (ast_walk_context_switches(c, NULL))
+ fprintf(output, "\n");
+
+ /* fireout ignorepats ... */
+ for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) {
+ if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
+ continue; /* not mine */
+ PUT_CTX_HDR;
+ fprintf(output, "ignorepat => %s\n",
+ ast_get_ignorepat_name(ip));
+ }
+
+ ast_unlock_context(c);
+ }
+
+ ast_unlock_contexts();
+ ast_mutex_unlock(&save_dialplan_lock);
+ fclose(output);
+
+ if (incomplete) {
+ ast_cli(a->fd, "Saved dialplan is incomplete\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Dialplan successfully saved into '%s'\n",
+ filename);
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief ADD EXTENSION command stuff
+ */
+static char *handle_cli_dialplan_add_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *whole_exten;
+ char *exten, *prior;
+ int iprior = -2;
+ char *cidmatch, *app, *app_data;
+ char *start, *end;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan add extension";
+ e->usage =
+ "Usage: dialplan add extension <exten>,<priority>,<app>,<app-data>\n"
+ " into <context> [replace]\n\n"
+ " This command will add new extension into <context>. If there is an\n"
+ " existence of extension with the same priority and last 'replace'\n"
+ " arguments is given here we simply replace this extension.\n"
+ "\n"
+ "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
+ " Now, you can dial 6123 and talk to Markster :)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_dialplan_add_extension(a);
+ }
+
+ /* check for arguments at first */
+ if (a->argc != 6 && a->argc != 7)
+ return CLI_SHOWUSAGE;
+ if (strcmp(a->argv[4], "into"))
+ return CLI_SHOWUSAGE;
+ if (a->argc == 7)
+ if (strcmp(a->argv[6], "replace"))
+ return CLI_SHOWUSAGE;
+
+ /* XXX overwrite argv[3] */
+ whole_exten = a->argv[3];
+ exten = strsep(&whole_exten,",");
+ if (strchr(exten, '/')) {
+ cidmatch = exten;
+ strsep(&cidmatch,"/");
+ } else {
+ cidmatch = NULL;
+ }
+ prior = strsep(&whole_exten,",");
+ if (prior) {
+ if (!strcmp(prior, "hint")) {
+ iprior = PRIORITY_HINT;
+ } else {
+ if (sscanf(prior, "%d", &iprior) != 1) {
+ ast_cli(a->fd, "'%s' is not a valid priority\n", prior);
+ prior = NULL;
+ }
+ }
+ }
+ app = whole_exten;
+ if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
+ *start = *end = '\0';
+ app_data = start + 1;
+ } else {
+ if (app) {
+ app_data = strchr(app, ',');
+ if (app_data) {
+ *app_data = '\0';
+ app_data++;
+ }
+ } else
+ app_data = NULL;
+ }
+
+ if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
+ return CLI_SHOWUSAGE;
+
+ if (!app_data)
+ app_data="";
+ if (ast_add_extension(a->argv[5], a->argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
+ (void *)strdup(app_data), ast_free_ptr, registrar)) {
+ switch (errno) {
+ case ENOMEM:
+ ast_cli(a->fd, "Out of free memory\n");
+ break;
+
+ case EBUSY:
+ ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
+ break;
+
+ case ENOENT:
+ ast_cli(a->fd, "No existence of '%s' context\n", a->argv[5]);
+ break;
+
+ case EEXIST:
+ ast_cli(a->fd, "Extension %s@%s with priority %s already exists\n",
+ exten, a->argv[5], prior);
+ break;
+
+ default:
+ ast_cli(a->fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
+ exten, prior, app, app_data, a->argv[5]);
+ break;
+ }
+ return CLI_FAILURE;
+ }
+
+ if (a->argc == 7)
+ ast_cli(a->fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
+ exten, a->argv[5], prior, exten, prior, app, app_data);
+ else
+ ast_cli(a->fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
+ exten, prior, app, app_data, a->argv[5]);
+
+ return CLI_SUCCESS;
+}
+
+/*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
+static char *complete_dialplan_add_extension(struct ast_cli_args *a)
+{
+ int which = 0;
+
+ if (a->pos == 4) { /* complete 'into' word ... */
+ return (a->n == 0) ? strdup("into") : NULL;
+ } else if (a->pos == 5) { /* complete context */
+ struct ast_context *c = NULL;
+ int len = strlen(a->word);
+ char *res = NULL;
+
+ /* try to lock contexts list ... */
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_WARNING, "Failed to lock contexts list\n");
+ return NULL;
+ }
+
+ /* walk through all contexts */
+ while ( !res && (c = ast_walk_contexts(c)) )
+ if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n)
+ res = strdup(ast_get_context_name(c));
+ ast_unlock_contexts();
+ return res;
+ } else if (a->pos == 6) {
+ return a->n == 0 ? strdup("replace") : NULL;
+ }
+ return NULL;
+}
+
+/*!
+ * IGNOREPAT CLI stuff
+ */
+static char *handle_cli_dialplan_add_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan add ignorepat";
+ e->usage =
+ "Usage: dialplan add ignorepat <pattern> into <context>\n"
+ " This command adds a new ignore pattern into context <context>\n"
+ "\n"
+ "Example: dialplan add ignorepat _3XX into local\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_dialplan_add_ignorepat(a);
+ }
+
+ if (a->argc != 6)
+ return CLI_SHOWUSAGE;
+
+ if (strcmp(a->argv[4], "into"))
+ return CLI_SHOWUSAGE;
+
+ if (ast_context_add_ignorepat(a->argv[5], a->argv[3], registrar)) {
+ switch (errno) {
+ case ENOMEM:
+ ast_cli(a->fd, "Out of free memory\n");
+ break;
+
+ case ENOENT:
+ ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]);
+ break;
+
+ case EEXIST:
+ ast_cli(a->fd, "Ignore pattern '%s' already included in '%s' context\n",
+ a->argv[3], a->argv[5]);
+ break;
+
+ case EBUSY:
+ ast_cli(a->fd, "Failed to lock context(s) list, please, try again later\n");
+ break;
+
+ default:
+ ast_cli(a->fd, "Failed to add ingore pattern '%s' into '%s' context\n",
+ a->argv[3], a->argv[5]);
+ break;
+ }
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Ignore pattern '%s' added into '%s' context\n",
+ a->argv[3], a->argv[5]);
+
+ return CLI_SUCCESS;
+}
+
+static char *complete_dialplan_add_ignorepat(struct ast_cli_args *a)
+{
+ if (a->pos == 4)
+ return a->n == 0 ? strdup("into") : NULL;
+ else if (a->pos == 5) {
+ struct ast_context *c;
+ int which = 0;
+ char *dupline, *ignorepat = NULL;
+ const char *s;
+ char *ret = NULL;
+ int len = strlen(a->word);
+
+ /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
+ s = skip_words(a->line, 3);
+ if (s == NULL)
+ return NULL;
+ dupline = strdup(s);
+ if (!dupline) {
+ ast_log(LOG_ERROR, "Malloc failure\n");
+ return NULL;
+ }
+ ignorepat = strsep(&dupline, " ");
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_ERROR, "Failed to lock contexts list\n");
+ return NULL;
+ }
+
+ for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
+ int found = 0;
+
+ if (!partial_match(ast_get_context_name(c), a->word, len))
+ continue; /* not mine */
+ if (ignorepat) /* there must be one, right ? */
+ found = lookup_c_ip(c, ignorepat);
+ if (!found && ++which > a->n)
+ ret = strdup(ast_get_context_name(c));
+ }
+
+ if (ignorepat)
+ free(ignorepat);
+ ast_unlock_contexts();
+ return ret;
+ }
+
+ return NULL;
+}
+
+static char *handle_cli_dialplan_remove_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan remove ignorepat";
+ e->usage =
+ "Usage: dialplan remove ignorepat <pattern> from <context>\n"
+ " This command removes an ignore pattern from context <context>\n"
+ "\n"
+ "Example: dialplan remove ignorepat _3XX from local\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_dialplan_remove_ignorepat(a);
+ }
+
+ if (a->argc != 6)
+ return CLI_SHOWUSAGE;
+
+ if (strcmp(a->argv[4], "from"))
+ return CLI_SHOWUSAGE;
+
+ if (ast_context_remove_ignorepat(a->argv[5], a->argv[3], registrar)) {
+ switch (errno) {
+ case EBUSY:
+ ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
+ break;
+
+ case ENOENT:
+ ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]);
+ break;
+
+ case EINVAL:
+ ast_cli(a->fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
+ a->argv[3], a->argv[5]);
+ break;
+
+ default:
+ ast_cli(a->fd, "Failed to remove ignore pattern '%s' from '%s' context\n",
+ a->argv[3], a->argv[5]);
+ break;
+ }
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Ignore pattern '%s' removed from '%s' context\n",
+ a->argv[3], a->argv[5]);
+ return CLI_SUCCESS;
+}
+
+static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *a)
+{
+ struct ast_context *c;
+ int which = 0;
+ char *ret = NULL;
+
+ if (a->pos == 3) {
+ int len = strlen(a->word);
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_WARNING, "Failed to lock contexts list\n");
+ return NULL;
+ }
+
+ for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
+ struct ast_ignorepat *ip;
+
+ if (ast_rdlock_context(c)) /* error, skip it */
+ continue;
+
+ for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
+ if (partial_match(ast_get_ignorepat_name(ip), a->word, len) && ++which > a->n) {
+ /* n-th match */
+ struct ast_context *cw = NULL;
+ int found = 0;
+ while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
+ /* XXX do i stop on c, or skip it ? */
+ found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
+ }
+ if (!found)
+ ret = strdup(ast_get_ignorepat_name(ip));
+ }
+ }
+ ast_unlock_context(c);
+ }
+ ast_unlock_contexts();
+ return ret;
+ } else if (a->pos == 4) {
+ return a->n == 0 ? strdup("from") : NULL;
+ } else if (a->pos == 5) { /* XXX check this */
+ char *dupline, *duplinet, *ignorepat;
+ int len = strlen(a->word);
+
+ dupline = strdup(a->line);
+ if (!dupline) {
+ ast_log(LOG_WARNING, "Out of free memory\n");
+ return NULL;
+ }
+
+ duplinet = dupline;
+ strsep(&duplinet, " ");
+ strsep(&duplinet, " ");
+ ignorepat = strsep(&duplinet, " ");
+
+ if (!ignorepat) {
+ free(dupline);
+ return NULL;
+ }
+
+ if (ast_rdlock_contexts()) {
+ ast_log(LOG_WARNING, "Failed to lock contexts list\n");
+ free(dupline);
+ return NULL;
+ }
+
+ for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
+ if (ast_rdlock_context(c)) /* fail, skip it */
+ continue;
+ if (!partial_match(ast_get_context_name(c), a->word, len))
+ continue;
+ if (lookup_c_ip(c, ignorepat) && ++which > a->n)
+ ret = strdup(ast_get_context_name(c));
+ ast_unlock_context(c);
+ }
+ ast_unlock_contexts();
+ free(dupline);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static int pbx_load_module(void);
+
+static char *handle_cli_dialplan_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan reload";
+ e->usage =
+ "Usage: dialplan reload\n"
+ " Reload extensions.conf without reloading any other\n"
+ " modules. This command does not delete global variables\n"
+ " unless clearglobalvars is set to yes in extensions.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ if (clearglobalvars_config)
+ pbx_builtin_clear_globals();
+
+ pbx_load_module();
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * CLI entries for commands provided by this module
+ */
+static struct ast_cli_entry cli_pbx_config[] = {
+ AST_CLI_DEFINE(handle_cli_dialplan_add_extension, "Add new extension into context"),
+ AST_CLI_DEFINE(handle_cli_dialplan_remove_extension, "Remove a specified extension"),
+ AST_CLI_DEFINE(handle_cli_dialplan_add_ignorepat, "Add new ignore pattern"),
+ AST_CLI_DEFINE(handle_cli_dialplan_remove_ignorepat, "Remove ignore pattern from context"),
+ AST_CLI_DEFINE(handle_cli_dialplan_add_include, "Include context in other context"),
+ AST_CLI_DEFINE(handle_cli_dialplan_remove_include, "Remove a specified include from context"),
+ AST_CLI_DEFINE(handle_cli_dialplan_reload, "Reload extensions and *only* extensions")
+};
+
+static struct ast_cli_entry cli_dialplan_save =
+ AST_CLI_DEFINE(handle_cli_dialplan_save, "Save dialplan");
+
+/*!
+ * Standard module functions ...
+ */
+static int unload_module(void)
+{
+ if (static_config && !write_protect_config)
+ ast_cli_unregister(&cli_dialplan_save);
+ ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
+ ast_context_destroy(NULL, registrar);
+ return 0;
+}
+
+static int pbx_load_config(const char *config_file)
+{
+ struct ast_config *cfg;
+ char *end;
+ char *label;
+ char realvalue[256];
+ int lastpri = -2;
+ struct ast_context *con;
+ struct ast_variable *v;
+ const char *cxt;
+ const char *aft;
+ const char *newpm;
+ struct ast_flags config_flags = { 0 };
+
+ cfg = ast_config_load(config_file, config_flags);
+ if (!cfg)
+ return 0;
+
+ /* Use existing config to populate the PBX table */
+ static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
+ write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
+ if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
+ autofallthrough_config = ast_true(aft);
+ if ((newpm = ast_variable_retrieve(cfg, "general", "extenpatternmatchnew")))
+ extenpatternmatchnew_config = ast_true(newpm);
+ clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
+
+
+ if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext")))
+ ast_copy_string(userscontext, cxt, sizeof(userscontext));
+ else
+ ast_copy_string(userscontext, "default", sizeof(userscontext));
+
+ for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ pbx_builtin_setvar_helper(NULL, v->name, realvalue);
+ }
+ for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
+ /* All categories but "general" or "globals" are considered contexts */
+ if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
+ continue;
+ con=ast_context_find_or_create(&local_contexts,cxt, registrar);
+ if (con == NULL)
+ continue;
+
+ for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
+ if (!strcasecmp(v->name, "exten")) {
+ char *tc = ast_strdup(v->value);
+ if (tc) {
+ int ipri = -2;
+ char realext[256]="";
+ char *plus, *firstp;
+ char *pri, *appl, *data, *cidmatch;
+ char *stringp = tc;
+ char *ext = strsep(&stringp, ",");
+ if (!ext)
+ ext="";
+ pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
+ cidmatch = strchr(realext, '/');
+ if (cidmatch) {
+ *cidmatch++ = '\0';
+ ast_shrink_phone_number(cidmatch);
+ }
+ pri = strsep(&stringp, ",");
+ if (!pri)
+ pri="";
+ pri = ast_skip_blanks(pri);
+ pri = ast_trim_blanks(pri);
+ label = strchr(pri, '(');
+ if (label) {
+ *label++ = '\0';
+ end = strchr(label, ')');
+ if (end)
+ *end = '\0';
+ else
+ ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
+ }
+ plus = strchr(pri, '+');
+ if (plus)
+ *plus++ = '\0';
+ if (!strcmp(pri,"hint"))
+ ipri=PRIORITY_HINT;
+ else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
+ if (lastpri > -2)
+ ipri = lastpri + 1;
+ else
+ ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
+ } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
+ if (lastpri > -2)
+ ipri = lastpri;
+ else
+ ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
+ } else if (sscanf(pri, "%d", &ipri) != 1 &&
+ (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
+ ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
+ ipri = 0;
+ }
+ appl = S_OR(stringp, "");
+ /* Find the first occurrence of '(' */
+ firstp = strchr(appl, '(');
+ if (!firstp) {
+ /* No arguments */
+ data = "";
+ } else {
+ appl = strsep(&stringp, "(");
+ data = stringp;
+ end = strrchr(data, ')');
+ if ((end = strrchr(data, ')'))) {
+ *end = '\0';
+ } else {
+ ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
+ }
+ }
+
+ if (!data)
+ data = "";
+ appl = ast_skip_blanks(appl);
+ if (ipri) {
+ if (plus)
+ ipri += atoi(plus);
+ lastpri = ipri;
+ if (!ast_opt_dont_warn && !strcmp(realext, "_."))
+ ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v->lineno);
+ if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free_ptr, registrar)) {
+ ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
+ }
+ }
+ free(tc);
+ }
+ } else if (!strcasecmp(v->name, "include")) {
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ if (ast_context_add_include2(con, realvalue, registrar))
+ ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
+ } else if (!strcasecmp(v->name, "ignorepat")) {
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ if (ast_context_add_ignorepat2(con, realvalue, registrar))
+ ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
+ } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
+ char *stringp = realvalue;
+ char *appl, *data;
+
+ if (!strcasecmp(v->name, "switch"))
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ else
+ ast_copy_string(realvalue, v->value, sizeof(realvalue));
+ appl = strsep(&stringp, "/");
+ data = S_OR(stringp, "");
+ if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
+ ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
+ } else {
+ ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+ return 1;
+}
+
+static void append_interface(char *iface, int maxlen, char *add)
+{
+ int len = strlen(iface);
+ if (strlen(add) + len < maxlen - 2) {
+ if (strlen(iface)) {
+ iface[len] = '&';
+ strcpy(iface + len + 1, add);
+ } else
+ strcpy(iface, add);
+ }
+}
+
+static void pbx_load_users(void)
+{
+ struct ast_config *cfg;
+ char *cat, *chan;
+ const char *zapchan;
+ const char *hasexten;
+ char tmp[256];
+ char iface[256];
+ char zapcopy[256];
+ char *c;
+ int len;
+ int hasvoicemail;
+ int start, finish, x;
+ struct ast_context *con = NULL;
+ struct ast_flags config_flags = { 0 };
+
+ cfg = ast_config_load("users.conf", config_flags);
+ if (!cfg)
+ return;
+
+ for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) {
+ if (!strcasecmp(cat, "general"))
+ continue;
+ iface[0] = '\0';
+ len = sizeof(iface);
+ if (ast_true(ast_config_option(cfg, cat, "hassip"))) {
+ snprintf(tmp, sizeof(tmp), "SIP/%s", cat);
+ append_interface(iface, sizeof(iface), tmp);
+ }
+ if (ast_true(ast_config_option(cfg, cat, "hasiax"))) {
+ snprintf(tmp, sizeof(tmp), "IAX2/%s", cat);
+ append_interface(iface, sizeof(iface), tmp);
+ }
+ if (ast_true(ast_config_option(cfg, cat, "hash323"))) {
+ snprintf(tmp, sizeof(tmp), "H323/%s", cat);
+ append_interface(iface, sizeof(iface), tmp);
+ }
+ hasexten = ast_config_option(cfg, cat, "hasexten");
+ if (hasexten && !ast_true(hasexten))
+ continue;
+ hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail"));
+ zapchan = ast_variable_retrieve(cfg, cat, "zapchan");
+ if (!zapchan)
+ zapchan = ast_variable_retrieve(cfg, "general", "zapchan");
+ if (!ast_strlen_zero(zapchan)) {
+ ast_copy_string(zapcopy, zapchan, sizeof(zapcopy));
+ c = zapcopy;
+ chan = strsep(&c, ",");
+ while (chan) {
+ if (sscanf(chan, "%d-%d", &start, &finish) == 2) {
+ /* Range */
+ } else if (sscanf(chan, "%d", &start)) {
+ /* Just one */
+ finish = start;
+ } else {
+ start = 0; finish = 0;
+ }
+ if (finish < start) {
+ x = finish;
+ finish = start;
+ start = x;
+ }
+ for (x = start; x <= finish; x++) {
+ snprintf(tmp, sizeof(tmp), "Zap/%d", x);
+ append_interface(iface, sizeof(iface), tmp);
+ }
+ chan = strsep(&c, ",");
+ }
+ }
+ if (!ast_strlen_zero(iface)) {
+ /* Only create a context here when it is really needed. Otherwise default empty context
+ created by pbx_config may conflict with the one explicitly created by pbx_ael */
+ if (!con)
+ con = ast_context_find_or_create(&local_contexts, userscontext, registrar);
+
+ if (!con) {
+ ast_log(LOG_ERROR, "Can't find/create user context '%s'\n", userscontext);
+ return;
+ }
+
+ /* Add hint */
+ ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar);
+ /* If voicemail, use "stdexten" else use plain old dial */
+ if (hasvoicemail) {
+ snprintf(tmp, sizeof(tmp), "stdexten,%s,${HINT}", cat);
+ ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free_ptr, registrar);
+ } else {
+ ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free_ptr, registrar);
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+}
+
+static int pbx_load_module(void)
+{
+ struct ast_context *con;
+
+ if(!pbx_load_config(config))
+ return AST_MODULE_LOAD_DECLINE;
+
+ pbx_load_users();
+
+ ast_merge_contexts_and_delete(&local_contexts, registrar);
+
+ for (con = NULL; (con = ast_walk_contexts(con));)
+ ast_context_verify_includes(con);
+
+ pbx_set_autofallthrough(autofallthrough_config);
+ pbx_set_extenpatternmatchnew(extenpatternmatchnew_config);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int load_module(void)
+{
+ if (pbx_load_module())
+ return AST_MODULE_LOAD_DECLINE;
+
+ if (static_config && !write_protect_config)
+ ast_cli_register(&cli_dialplan_save);
+ ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload(void)
+{
+ if (clearglobalvars_config)
+ pbx_builtin_clear_globals();
+ return pbx_load_module();
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/pbx/pbx_dundi.c b/trunk/pbx/pbx_dundi.c
new file mode 100644
index 000000000..26a3e51a2
--- /dev/null
+++ b/trunk/pbx/pbx_dundi.c
@@ -0,0 +1,4847 @@
+/*
+ * 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 Distributed Universal Number Discovery (DUNDi)
+ */
+
+/*** MODULEINFO
+ <depend>zlib</depend>
+ <use>crypto</use>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/network.h"
+#include <sys/ioctl.h>
+#include <zlib.h>
+#include <sys/signal.h>
+#include <pthread.h>
+#include <net/if.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
+#include <net/if_dl.h>
+#include <ifaddrs.h>
+#endif
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/frame.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/md5.h"
+#include "asterisk/dundi.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/utils.h"
+#include "asterisk/netsock.h"
+#include "asterisk/crypto.h"
+#include "asterisk/astdb.h"
+#include "asterisk/acl.h"
+#include "asterisk/aes.h"
+#include "asterisk/app.h"
+
+#include "dundi-parser.h"
+
+#define MAX_RESULTS 64
+
+#define MAX_PACKET_SIZE 8192
+
+#define MAX_WEIGHT 59999
+
+#define DUNDI_MODEL_INBOUND (1 << 0)
+#define DUNDI_MODEL_OUTBOUND (1 << 1)
+#define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
+
+/*! Keep times of last 10 lookups */
+#define DUNDI_TIMING_HISTORY 10
+
+enum {
+ FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
+ FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
+ FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
+ FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
+ FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
+ FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
+ FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
+};
+
+#define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
+
+#if 0
+#define DUNDI_SECRET_TIME 15 /* Testing only */
+#else
+#define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
+#endif
+
+static struct io_context *io;
+static struct sched_context *sched;
+static int netsocket = -1;
+static pthread_t netthreadid = AST_PTHREADT_NULL;
+static pthread_t precachethreadid = AST_PTHREADT_NULL;
+static unsigned int tos = 0;
+static int dundidebug = 0;
+static int authdebug = 0;
+static int dundi_ttl = DUNDI_DEFAULT_TTL;
+static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
+static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
+static int global_autokilltimeout = 0;
+static dundi_eid global_eid;
+static int default_expiration = 60;
+static int global_storehistory = 0;
+static char dept[80];
+static char org[80];
+static char locality[80];
+static char stateprov[80];
+static char country[80];
+static char email[80];
+static char phone[80];
+static char secretpath[80];
+static char cursecret[80];
+static char ipaddr[80];
+static time_t rotatetime;
+static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
+static int dundi_shutdown = 0;
+
+struct permission {
+ AST_LIST_ENTRY(permission) list;
+ int allow;
+ char name[0];
+};
+
+struct dundi_packet {
+ AST_LIST_ENTRY(dundi_packet) list;
+ struct dundi_hdr *h;
+ int datalen;
+ struct dundi_transaction *parent;
+ int retransid;
+ int retrans;
+ unsigned char data[0];
+};
+
+struct dundi_hint_metadata {
+ unsigned short flags;
+ char exten[AST_MAX_EXTENSION];
+};
+
+struct dundi_precache_queue {
+ AST_LIST_ENTRY(dundi_precache_queue) list;
+ char *context;
+ time_t expiration;
+ char number[0];
+};
+
+struct dundi_request;
+
+struct dundi_transaction {
+ struct sockaddr_in addr; /*!< Other end of transaction */
+ struct timeval start; /*!< When this transaction was created */
+ dundi_eid eids[DUNDI_MAX_STACK + 1];
+ int eidcount; /*!< Number of eids in eids */
+ dundi_eid us_eid; /*!< Our EID, to them */
+ dundi_eid them_eid; /*!< Their EID, to us */
+ ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
+ ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
+ unsigned int flags; /*!< Has final packet been sent */
+ int ttl; /*!< Remaining TTL for queries on this one */
+ int thread; /*!< We have a calling thread */
+ int retranstimer; /*!< How long to wait before retransmissions */
+ int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
+ int autokilltimeout; /*!< Recommended timeout for autokill */
+ unsigned short strans; /*!< Our transaction identifier */
+ unsigned short dtrans; /*!< Their transaction identifer */
+ unsigned char iseqno; /*!< Next expected received seqno */
+ unsigned char oiseqno; /*!< Last received incoming seqno */
+ unsigned char oseqno; /*!< Next transmitted seqno */
+ unsigned char aseqno; /*!< Last acknowledge seqno */
+ AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
+ struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
+ struct dundi_request *parent; /*!< Parent request (if there is one) */
+ AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
+ AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
+};
+
+struct dundi_request {
+ char dcontext[AST_MAX_EXTENSION];
+ char number[AST_MAX_EXTENSION];
+ dundi_eid query_eid;
+ dundi_eid root_eid;
+ struct dundi_result *dr;
+ struct dundi_entity_info *dei;
+ struct dundi_hint_metadata *hmd;
+ int maxcount;
+ int respcount;
+ int expiration;
+ int cbypass;
+ int pfds[2];
+ unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */
+ AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
+ AST_LIST_ENTRY(dundi_request) list;
+};
+
+struct dundi_mapping {
+ char dcontext[AST_MAX_EXTENSION];
+ char lcontext[AST_MAX_EXTENSION];
+ int _weight;
+ char *weightstr;
+ int options;
+ int tech;
+ int dead;
+ char dest[AST_MAX_EXTENSION];
+ AST_LIST_ENTRY(dundi_mapping) list;
+};
+
+struct dundi_peer {
+ dundi_eid eid;
+ struct sockaddr_in addr; /*!< Address of DUNDi peer */
+ AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
+ struct permissionlist include;
+ dundi_eid us_eid;
+ char inkey[80];
+ char outkey[80];
+ int dead;
+ int registerid;
+ int qualifyid;
+ int sentfullkey;
+ int order;
+ unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
+ unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
+ unsigned long us_keycrc32; /*!< CRC-32 of our key */
+ ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
+ ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
+ unsigned long them_keycrc32; /*!< CRC-32 of our key */
+ ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
+ ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
+ time_t keyexpire; /*!< When to expire/recreate key */
+ int registerexpire;
+ int lookuptimes[DUNDI_TIMING_HISTORY];
+ char *lookups[DUNDI_TIMING_HISTORY];
+ int avgms;
+ struct dundi_transaction *regtrans; /*!< Registration transaction */
+ struct dundi_transaction *qualtrans; /*!< Qualify transaction */
+ int model; /*!< Pull model */
+ int pcmodel; /*!< Push/precache model */
+ /*! Dynamic peers register with us */
+ unsigned int dynamic:1;
+ int lastms; /*!< Last measured latency */
+ int maxms; /*!< Max permissible latency */
+ struct timeval qualtx; /*!< Time of transmit */
+ AST_LIST_ENTRY(dundi_peer) list;
+};
+
+static AST_LIST_HEAD_STATIC(peers, dundi_peer);
+static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
+static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
+static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
+static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
+
+/*!
+ * \brief Wildcard peer
+ *
+ * This peer is created if the [*] entry is specified in dundi.conf
+ */
+static struct dundi_peer *any_peer;
+
+static int dundi_xmit(struct dundi_packet *pack);
+
+static void dundi_debug_output(const char *data)
+{
+ if (dundidebug)
+ ast_verbose("%s", data);
+}
+
+static void dundi_error_output(const char *data)
+{
+ ast_log(LOG_WARNING, "%s", data);
+}
+
+static int has_permission(struct permissionlist *permlist, char *cont)
+{
+ struct permission *perm;
+ int res = 0;
+
+ AST_LIST_TRAVERSE(permlist, perm, list) {
+ if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
+ res = perm->allow;
+ }
+
+ return res;
+}
+
+static char *tech2str(int tech)
+{
+ switch(tech) {
+ case DUNDI_PROTO_NONE:
+ return "None";
+ case DUNDI_PROTO_IAX:
+ return "IAX2";
+ case DUNDI_PROTO_SIP:
+ return "SIP";
+ case DUNDI_PROTO_H323:
+ return "H323";
+ default:
+ return "Unknown";
+ }
+}
+
+static int str2tech(char *str)
+{
+ if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
+ return DUNDI_PROTO_IAX;
+ else if (!strcasecmp(str, "SIP"))
+ return DUNDI_PROTO_SIP;
+ else if (!strcasecmp(str, "H323"))
+ return DUNDI_PROTO_H323;
+ else
+ return -1;
+}
+
+static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
+static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
+static struct dundi_transaction *create_transaction(struct dundi_peer *p);
+static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
+{
+ struct dundi_transaction *trans;
+
+ /* Look for an exact match first */
+ AST_LIST_TRAVERSE(&alltrans, trans, all) {
+ if (!inaddrcmp(&trans->addr, sin) &&
+ ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
+ ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
+ if (hdr->strans)
+ trans->dtrans = ntohs(hdr->strans) & 32767;
+ return trans;
+ }
+ }
+
+ switch(hdr->cmdresp & 0x7f) {
+ case DUNDI_COMMAND_DPDISCOVER:
+ case DUNDI_COMMAND_EIDQUERY:
+ case DUNDI_COMMAND_PRECACHERQ:
+ case DUNDI_COMMAND_REGREQ:
+ case DUNDI_COMMAND_NULL:
+ case DUNDI_COMMAND_ENCRYPT:
+ if (!hdr->strans)
+ break;
+ /* Create new transaction */
+ if (!(trans = create_transaction(NULL)))
+ break;
+ memcpy(&trans->addr, sin, sizeof(trans->addr));
+ trans->dtrans = ntohs(hdr->strans) & 32767;
+ default:
+ break;
+ }
+
+ return trans;
+}
+
+static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
+
+static int dundi_ack(struct dundi_transaction *trans, int final)
+{
+ return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
+}
+static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
+{
+ struct {
+ struct dundi_packet pack;
+ struct dundi_hdr hdr;
+ } tmp;
+ struct dundi_transaction trans;
+ /* Never respond to an INVALID with another INVALID */
+ if (h->cmdresp == DUNDI_COMMAND_INVALID)
+ return;
+ memset(&tmp, 0, sizeof(tmp));
+ memset(&trans, 0, sizeof(trans));
+ memcpy(&trans.addr, sin, sizeof(trans.addr));
+ tmp.hdr.strans = h->dtrans;
+ tmp.hdr.dtrans = h->strans;
+ tmp.hdr.iseqno = h->oseqno;
+ tmp.hdr.oseqno = h->iseqno;
+ tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
+ tmp.hdr.cmdflags = 0;
+ tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
+ tmp.pack.datalen = sizeof(struct dundi_hdr);
+ tmp.pack.parent = &trans;
+ dundi_xmit(&tmp.pack);
+}
+
+static void reset_global_eid(void)
+{
+#if defined(SIOCGIFHWADDR)
+ int s, x = 0;
+ char eid_str[20];
+ struct ifreq ifr;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return;
+ for (x = 0; x < 10; x++) {
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
+ if (ioctl(s, SIOCGIFHWADDR, &ifr))
+ continue;
+ memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
+ ast_debug(1, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
+ close(s);
+ return;
+ }
+ close(s);
+#else
+#if defined(ifa_broadaddr) && !defined(SOLARIS)
+ char eid_str[20];
+ struct ifaddrs *ifap;
+
+ if (getifaddrs(&ifap) == 0) {
+ struct ifaddrs *p;
+ for (p = ifap; p; p = p->ifa_next) {
+ if ((p->ifa_addr->sa_family == AF_LINK) && !(p->ifa_flags & IFF_LOOPBACK) && (p->ifa_flags & IFF_RUNNING)) {
+ struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
+ memcpy(&(global_eid.eid), sdp->sdl_data + sdp->sdl_nlen, 6);
+ ast_debug(1, "Seeding global EID '%s' from '%s' using 'getifaddrs'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), p->ifa_name);
+ freeifaddrs(ifap);
+ return;
+ }
+ }
+ freeifaddrs(ifap);
+ }
+#endif
+#endif
+ ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
+}
+
+static int get_trans_id(void)
+{
+ struct dundi_transaction *t;
+ int stid = (ast_random() % 32766) + 1;
+ int tid = stid;
+
+ do {
+ AST_LIST_TRAVERSE(&alltrans, t, all) {
+ if (t->strans == tid)
+ break;
+ }
+ if (!t)
+ return tid;
+ tid = (tid % 32766) + 1;
+ } while (tid != stid);
+
+ return 0;
+}
+
+static int reset_transaction(struct dundi_transaction *trans)
+{
+ int tid;
+ tid = get_trans_id();
+ if (tid < 1)
+ return -1;
+ trans->strans = tid;
+ trans->dtrans = 0;
+ trans->iseqno = 0;
+ trans->oiseqno = 0;
+ trans->oseqno = 0;
+ trans->aseqno = 0;
+ ast_clear_flag(trans, FLAG_FINAL);
+ return 0;
+}
+
+static struct dundi_peer *find_peer(dundi_eid *eid)
+{
+ struct dundi_peer *cur = NULL;
+
+ if (!eid)
+ eid = &empty_eid;
+
+ AST_LIST_TRAVERSE(&peers, cur, list) {
+ if (!dundi_eid_cmp(&cur->eid,eid))
+ break;
+ }
+
+ if (!cur && any_peer)
+ cur = any_peer;
+
+ return cur;
+}
+
+static void build_iv(unsigned char *iv)
+{
+ /* XXX Would be nice to be more random XXX */
+ unsigned int *fluffy;
+ int x;
+ fluffy = (unsigned int *)(iv);
+ for (x=0;x<4;x++)
+ fluffy[x] = ast_random();
+}
+
+struct dundi_query_state {
+ dundi_eid *eids[DUNDI_MAX_STACK + 1];
+ int directs[DUNDI_MAX_STACK + 1];
+ dundi_eid reqeid;
+ char called_context[AST_MAX_EXTENSION];
+ char called_number[AST_MAX_EXTENSION];
+ struct dundi_mapping *maps;
+ int nummaps;
+ int nocache;
+ struct dundi_transaction *trans;
+ void *chal;
+ int challen;
+ int ttl;
+ char fluffy[0];
+};
+
+static int get_mapping_weight(struct dundi_mapping *map)
+{
+ char buf[32];
+
+ buf[0] = 0;
+ if (map->weightstr) {
+ pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
+ if (sscanf(buf, "%d", &map->_weight) != 1)
+ map->_weight = MAX_WEIGHT;
+ }
+
+ return map->_weight;
+}
+
+static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
+{
+ struct ast_flags flags = {0};
+ int x;
+ if (!ast_strlen_zero(map->lcontext)) {
+ if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
+ ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
+ if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
+ ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
+ if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
+ ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
+ if (ast_ignore_pattern(map->lcontext, called_number))
+ ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
+
+ /* Clearly we can't say 'don't ask' anymore if we found anything... */
+ if (ast_test_flag(&flags, AST_FLAGS_ALL))
+ ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
+
+ if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
+ /* Skip partial answers */
+ ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
+ }
+ if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
+ struct varshead headp;
+ struct ast_var_t *newvariable;
+ ast_set_flag(&flags, map->options & 0xffff);
+ ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
+ dr[anscnt].techint = map->tech;
+ dr[anscnt].weight = get_mapping_weight(map);
+ dr[anscnt].expiration = dundi_cache_time;
+ ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
+ dr[anscnt].eid = *us_eid;
+ dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
+ if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
+ AST_LIST_HEAD_INIT_NOLOCK(&headp);
+ newvariable = ast_var_assign("NUMBER", called_number);
+ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+ newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
+ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+ newvariable = ast_var_assign("SECRET", cursecret);
+ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+ newvariable = ast_var_assign("IPADDR", ipaddr);
+ AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
+ pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
+ while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
+ ast_var_delete(newvariable);
+ } else
+ dr[anscnt].dest[0] = '\0';
+ anscnt++;
+ } else {
+ /* No answers... Find the fewest number of digits from the
+ number for which we have no answer. */
+ char tmp[AST_MAX_EXTENSION + 1] = "";
+ for (x = 0; x < (sizeof(tmp) - 1); x++) {
+ tmp[x] = called_number[x];
+ if (!tmp[x])
+ break;
+ if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
+ /* Oops found something we can't match. If this is longer
+ than the running hint, we have to consider it */
+ if (strlen(tmp) > strlen(hmd->exten)) {
+ ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
+ }
+ break;
+ }
+ }
+ }
+ }
+ return anscnt;
+}
+
+static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
+
+static void *dundi_lookup_thread(void *data)
+{
+ struct dundi_query_state *st = data;
+ struct dundi_result dr[MAX_RESULTS];
+ struct dundi_ie_data ied;
+ struct dundi_hint_metadata hmd;
+ char eid_str[20];
+ int res, x;
+ int ouranswers=0;
+ int max = 999999;
+ int expiration = dundi_cache_time;
+
+ ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
+ st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
+ memset(&ied, 0, sizeof(ied));
+ memset(&dr, 0, sizeof(dr));
+ memset(&hmd, 0, sizeof(hmd));
+ /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
+ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
+ for (x=0;x<st->nummaps;x++)
+ ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
+ if (ouranswers < 0)
+ ouranswers = 0;
+ for (x=0;x<ouranswers;x++) {
+ if (dr[x].weight < max)
+ max = dr[x].weight;
+ }
+
+ if (max) {
+ /* If we do not have a canonical result, keep looking */
+ res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
+ if (res > 0) {
+ /* Append answer in result */
+ ouranswers += res;
+ } else {
+ if ((res < -1) && (!ouranswers))
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
+ }
+ }
+ AST_LIST_LOCK(&peers);
+ /* Truncate if "don't ask" isn't present */
+ if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
+ hmd.exten[0] = '\0';
+ if (ast_test_flag(st->trans, FLAG_DEAD)) {
+ ast_debug(1, "Our transaction went away!\n");
+ st->trans->thread = 0;
+ destroy_trans(st->trans, 0);
+ } else {
+ for (x=0;x<ouranswers;x++) {
+ /* Add answers */
+ if (dr[x].expiration && (expiration > dr[x].expiration))
+ expiration = dr[x].expiration;
+ dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
+ }
+ dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
+ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
+ dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+ st->trans->thread = 0;
+ }
+ AST_LIST_UNLOCK(&peers);
+ ast_free(st);
+ return NULL;
+}
+
+static void *dundi_precache_thread(void *data)
+{
+ struct dundi_query_state *st = data;
+ struct dundi_ie_data ied;
+ struct dundi_hint_metadata hmd;
+ char eid_str[20];
+
+ ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
+ st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
+ memset(&ied, 0, sizeof(ied));
+
+ /* Now produce precache */
+ dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
+
+ AST_LIST_LOCK(&peers);
+ /* Truncate if "don't ask" isn't present */
+ if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
+ hmd.exten[0] = '\0';
+ if (ast_test_flag(st->trans, FLAG_DEAD)) {
+ ast_debug(1, "Our transaction went away!\n");
+ st->trans->thread = 0;
+ destroy_trans(st->trans, 0);
+ } else {
+ dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
+ st->trans->thread = 0;
+ }
+ AST_LIST_UNLOCK(&peers);
+ ast_free(st);
+ return NULL;
+}
+
+static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]);
+
+static void *dundi_query_thread(void *data)
+{
+ struct dundi_query_state *st = data;
+ struct dundi_entity_info dei;
+ struct dundi_ie_data ied;
+ struct dundi_hint_metadata hmd;
+ char eid_str[20];
+ int res;
+
+ ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
+ st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
+ memset(&ied, 0, sizeof(ied));
+ memset(&dei, 0, sizeof(dei));
+ memset(&hmd, 0, sizeof(hmd));
+ if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
+ /* Ooh, it's us! */
+ ast_debug(1, "Neat, someone look for us!\n");
+ ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
+ ast_copy_string(dei.org, org, sizeof(dei.org));
+ ast_copy_string(dei.locality, locality, sizeof(dei.locality));
+ ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
+ ast_copy_string(dei.country, country, sizeof(dei.country));
+ ast_copy_string(dei.email, email, sizeof(dei.email));
+ ast_copy_string(dei.phone, phone, sizeof(dei.phone));
+ res = 1;
+ } else {
+ /* If we do not have a canonical result, keep looking */
+ res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
+ }
+ AST_LIST_LOCK(&peers);
+ if (ast_test_flag(st->trans, FLAG_DEAD)) {
+ ast_debug(1, "Our transaction went away!\n");
+ st->trans->thread = 0;
+ destroy_trans(st->trans, 0);
+ } else {
+ if (res) {
+ dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
+ dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
+ dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
+ dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
+ dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
+ dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
+ dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
+ if (!ast_strlen_zero(dei.ipaddr))
+ dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
+ }
+ dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
+ dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
+ st->trans->thread = 0;
+ }
+ AST_LIST_UNLOCK(&peers);
+ ast_free(st);
+ return NULL;
+}
+
+static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
+{
+ struct dundi_query_state *st;
+ int totallen;
+ int x;
+ int skipfirst=0;
+ char eid_str[20];
+ char *s;
+ pthread_t lookupthread;
+
+ if (ies->eidcount > 1) {
+ /* Since it is a requirement that the first EID is the authenticating host
+ and the last EID is the root, it is permissible that the first and last EID
+ could be the same. In that case, we should go ahead copy only the "root" section
+ since we will not need it for authentication. */
+ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
+ skipfirst = 1;
+ }
+ totallen = sizeof(struct dundi_query_state);
+ totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
+ st = ast_calloc(1, totallen);
+ if (st) {
+ ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
+ memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
+ st->trans = trans;
+ st->ttl = ies->ttl - 1;
+ if (st->ttl < 0)
+ st->ttl = 0;
+ s = st->fluffy;
+ for (x=skipfirst;ies->eids[x];x++) {
+ st->eids[x-skipfirst] = (dundi_eid *)s;
+ *st->eids[x-skipfirst] = *ies->eids[x];
+ s += sizeof(dundi_eid);
+ }
+ ast_debug(1, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
+
+ trans->thread = 1;
+ if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
+ struct dundi_ie_data ied = { 0, };
+ trans->thread = 0;
+ ast_log(LOG_WARNING, "Unable to create thread!\n");
+ ast_free(st);
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
+ dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
+ return -1;
+ }
+ } else {
+ struct dundi_ie_data ied = { 0, };
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
+ dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
+ return -1;
+ }
+ return 0;
+}
+
+static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
+{
+ int unaffected;
+ char key1[256];
+ char key2[256];
+ char eidpeer_str[20];
+ char eidroot_str[20];
+ char data[80];
+ time_t timeout;
+
+ if (expiration < 0)
+ expiration = dundi_cache_time;
+
+ /* Only cache hint if "don't ask" is there... */
+ if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
+ return 0;
+
+ unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
+
+ dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
+ dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+ snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
+ snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
+
+ time(&timeout);
+ timeout += expiration;
+ snprintf(data, sizeof(data), "%ld|", (long)(timeout));
+
+ ast_db_put("dundi/cache", key1, data);
+ ast_debug(1, "Caching hint at '%s'\n", key1);
+ ast_db_put("dundi/cache", key2, data);
+ ast_debug(1, "Caching hint at '%s'\n", key2);
+ return 0;
+}
+
+static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
+{
+ int x;
+ char key1[256];
+ char key2[256];
+ char data[1024];
+ char eidpeer_str[20];
+ char eidroot_str[20];
+ time_t timeout;
+
+ if (expiration < 1)
+ expiration = dundi_cache_time;
+
+ /* Keep pushes a little longer, cut pulls a little short */
+ if (push)
+ expiration += 10;
+ else
+ expiration -= 10;
+ if (expiration < 1)
+ expiration = 1;
+ dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
+ dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+ snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
+ snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
+ /* Build request string */
+ time(&timeout);
+ timeout += expiration;
+ snprintf(data, sizeof(data), "%ld|", (long)(timeout));
+ for (x=start;x<req->respcount;x++) {
+ /* Skip anything with an illegal pipe in it */
+ if (strchr(req->dr[x].dest, '|'))
+ continue;
+ snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
+ req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
+ dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
+ }
+ ast_db_put("dundi/cache", key1, data);
+ ast_db_put("dundi/cache", key2, data);
+ return 0;
+}
+
+static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
+{
+ struct dundi_query_state *st;
+ int totallen;
+ int x,z;
+ struct dundi_ie_data ied;
+ char *s;
+ struct dundi_result dr2[MAX_RESULTS];
+ struct dundi_request dr;
+ struct dundi_hint_metadata hmd;
+
+ struct dundi_mapping *cur;
+ int mapcount;
+ int skipfirst = 0;
+
+ pthread_t lookupthread;
+
+ memset(&dr2, 0, sizeof(dr2));
+ memset(&dr, 0, sizeof(dr));
+ memset(&hmd, 0, sizeof(hmd));
+
+ /* Forge request structure to hold answers for cache */
+ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
+ dr.dr = dr2;
+ dr.maxcount = MAX_RESULTS;
+ dr.expiration = dundi_cache_time;
+ dr.hmd = &hmd;
+ dr.pfds[0] = dr.pfds[1] = -1;
+ trans->parent = &dr;
+ ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
+ ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
+
+ for (x=0;x<ies->anscount;x++) {
+ if (trans->parent->respcount < trans->parent->maxcount) {
+ /* Make sure it's not already there */
+ for (z=0;z<trans->parent->respcount;z++) {
+ if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
+ !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
+ break;
+ }
+ if (z == trans->parent->respcount) {
+ /* Copy into parent responses */
+ trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
+ trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
+ trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
+ trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
+ if (ies->expiration > 0)
+ trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
+ else
+ trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
+ dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
+ sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
+ &ies->answers[x]->eid);
+ ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
+ sizeof(trans->parent->dr[trans->parent->respcount].dest));
+ ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
+ sizeof(trans->parent->dr[trans->parent->respcount].tech));
+ trans->parent->respcount++;
+ ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
+ } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
+ /* Update weight if appropriate */
+ trans->parent->dr[z].weight = ies->answers[x]->weight;
+ }
+ } else
+ ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
+ trans->parent->number, trans->parent->dcontext);
+
+ }
+ /* Save all the results (if any) we had. Even if no results, still cache lookup. */
+ cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
+ if (ies->hint)
+ cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
+
+ totallen = sizeof(struct dundi_query_state);
+ /* Count matching map entries */
+ mapcount = 0;
+ AST_LIST_TRAVERSE(&mappings, cur, list) {
+ if (!strcasecmp(cur->dcontext, ccontext))
+ mapcount++;
+ }
+
+ /* If no maps, return -1 immediately */
+ if (!mapcount)
+ return -1;
+
+ if (ies->eidcount > 1) {
+ /* Since it is a requirement that the first EID is the authenticating host
+ and the last EID is the root, it is permissible that the first and last EID
+ could be the same. In that case, we should go ahead copy only the "root" section
+ since we will not need it for authentication. */
+ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
+ skipfirst = 1;
+ }
+
+ /* Prepare to run a query and then propagate that as necessary */
+ totallen += mapcount * sizeof(struct dundi_mapping);
+ totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
+ st = ast_calloc(1, totallen);
+ if (st) {
+ ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
+ ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
+ st->trans = trans;
+ st->ttl = ies->ttl - 1;
+ st->nocache = ies->cbypass;
+ if (st->ttl < 0)
+ st->ttl = 0;
+ s = st->fluffy;
+ for (x=skipfirst;ies->eids[x];x++) {
+ st->eids[x-skipfirst] = (dundi_eid *)s;
+ *st->eids[x-skipfirst] = *ies->eids[x];
+ st->directs[x-skipfirst] = ies->eid_direct[x];
+ s += sizeof(dundi_eid);
+ }
+ /* Append mappings */
+ x = 0;
+ st->maps = (struct dundi_mapping *)s;
+ AST_LIST_TRAVERSE(&mappings, cur, list) {
+ if (!strcasecmp(cur->dcontext, ccontext)) {
+ if (x < mapcount) {
+ st->maps[x] = *cur;
+ st->maps[x].list.next = NULL;
+ x++;
+ }
+ }
+ }
+ st->nummaps = mapcount;
+ ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
+ trans->thread = 1;
+ if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
+ trans->thread = 0;
+ ast_log(LOG_WARNING, "Unable to create thread!\n");
+ ast_free(st);
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
+ dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
+ dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
+ return -1;
+ }
+ return 0;
+}
+
+static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
+{
+ struct dundi_query_state *st;
+ int totallen;
+ int x;
+ struct dundi_ie_data ied;
+ char *s;
+ struct dundi_mapping *cur;
+ int mapcount = 0;
+ int skipfirst = 0;
+
+ pthread_t lookupthread;
+ totallen = sizeof(struct dundi_query_state);
+ /* Count matching map entries */
+ AST_LIST_TRAVERSE(&mappings, cur, list) {
+ if (!strcasecmp(cur->dcontext, ccontext))
+ mapcount++;
+ }
+ /* If no maps, return -1 immediately */
+ if (!mapcount)
+ return -1;
+
+ if (ies->eidcount > 1) {
+ /* Since it is a requirement that the first EID is the authenticating host
+ and the last EID is the root, it is permissible that the first and last EID
+ could be the same. In that case, we should go ahead copy only the "root" section
+ since we will not need it for authentication. */
+ if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
+ skipfirst = 1;
+ }
+
+ totallen += mapcount * sizeof(struct dundi_mapping);
+ totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
+ st = ast_calloc(1, totallen);
+ if (st) {
+ ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
+ ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
+ st->trans = trans;
+ st->ttl = ies->ttl - 1;
+ st->nocache = ies->cbypass;
+ if (st->ttl < 0)
+ st->ttl = 0;
+ s = st->fluffy;
+ for (x=skipfirst;ies->eids[x];x++) {
+ st->eids[x-skipfirst] = (dundi_eid *)s;
+ *st->eids[x-skipfirst] = *ies->eids[x];
+ st->directs[x-skipfirst] = ies->eid_direct[x];
+ s += sizeof(dundi_eid);
+ }
+ /* Append mappings */
+ x = 0;
+ st->maps = (struct dundi_mapping *)s;
+ AST_LIST_TRAVERSE(&mappings, cur, list) {
+ if (!strcasecmp(cur->dcontext, ccontext)) {
+ if (x < mapcount) {
+ st->maps[x] = *cur;
+ st->maps[x].list.next = NULL;
+ x++;
+ }
+ }
+ }
+ st->nummaps = mapcount;
+ ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
+ trans->thread = 1;
+ if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
+ trans->thread = 0;
+ ast_log(LOG_WARNING, "Unable to create thread!\n");
+ ast_free(st);
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
+ dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
+ dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+ return -1;
+ }
+ return 0;
+}
+
+static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
+{
+ char data[1024];
+ char *ptr, *term, *src;
+ int tech;
+ struct ast_flags flags;
+ int weight;
+ int length;
+ int z;
+ char fs[256];
+
+ /* Build request string */
+ if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
+ time_t timeout;
+ ptr = data;
+ if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
+ int expiration = timeout - now;
+ if (expiration > 0) {
+ ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
+ ptr += length + 1;
+ while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
+ ptr += length;
+ term = strchr(ptr, '|');
+ if (term) {
+ *term = '\0';
+ src = strrchr(ptr, '/');
+ if (src) {
+ *src = '\0';
+ src++;
+ } else
+ src = "";
+ ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
+ tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
+ /* Make sure it's not already there */
+ for (z=0;z<req->respcount;z++) {
+ if ((req->dr[z].techint == tech) &&
+ !strcmp(req->dr[z].dest, ptr))
+ break;
+ }
+ if (z == req->respcount) {
+ /* Copy into parent responses */
+ ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
+ req->dr[req->respcount].weight = weight;
+ req->dr[req->respcount].techint = tech;
+ req->dr[req->respcount].expiration = expiration;
+ dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
+ dundi_eid_to_str(req->dr[req->respcount].eid_str,
+ sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
+ ast_copy_string(req->dr[req->respcount].dest, ptr,
+ sizeof(req->dr[req->respcount].dest));
+ ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
+ sizeof(req->dr[req->respcount].tech));
+ req->respcount++;
+ ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
+ } else if (req->dr[z].weight > weight)
+ req->dr[z].weight = weight;
+ ptr = term + 1;
+ }
+ }
+ /* We found *something* cached */
+ if (expiration < *lowexpiration)
+ *lowexpiration = expiration;
+ return 1;
+ } else
+ ast_db_del("dundi/cache", key);
+ } else
+ ast_db_del("dundi/cache", key);
+ }
+
+ return 0;
+}
+
+static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
+{
+ char key[256];
+ char eid_str[20];
+ char eidroot_str[20];
+ time_t now;
+ int res=0;
+ int res2=0;
+ char eid_str_full[20];
+ char tmp[256]="";
+ int x;
+
+ time(&now);
+ dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
+ dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+ dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
+ snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
+ res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+ snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
+ res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+ snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
+ res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+ x = 0;
+ if (!req->respcount) {
+ while(!res2) {
+ /* Look and see if we have a hint that would preclude us from looking at this
+ peer for this number. */
+ if (!(tmp[x] = req->number[x]))
+ break;
+ x++;
+ /* Check for hints */
+ snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
+ res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+ snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
+ res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+ snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
+ res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+ if (res2) {
+ if (strlen(tmp) > strlen(req->hmd->exten)) {
+ /* Update meta data if appropriate */
+ ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
+ }
+ }
+ }
+ res |= res2;
+ }
+
+ return res;
+}
+
+static void qualify_peer(struct dundi_peer *peer, int schedonly);
+
+static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
+{
+ if (!trans->addr.sin_addr.s_addr)
+ memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
+ trans->us_eid = p->us_eid;
+ trans->them_eid = p->eid;
+ /* Enable encryption if appropriate */
+ if (!ast_strlen_zero(p->inkey))
+ ast_set_flag(trans, FLAG_ENCRYPT);
+ if (p->maxms) {
+ trans->autokilltimeout = p->maxms;
+ trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
+ if (p->lastms > 1) {
+ trans->retranstimer = p->lastms * 2;
+ /* Keep it from being silly */
+ if (trans->retranstimer < 150)
+ trans->retranstimer = 150;
+ }
+ if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
+ trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
+ } else
+ trans->autokilltimeout = global_autokilltimeout;
+}
+
+/*! \note Called with the peers list already locked */
+static int do_register_expire(const void *data)
+{
+ struct dundi_peer *peer = (struct dundi_peer *)data;
+ char eid_str[20];
+ ast_debug(1, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ peer->registerexpire = -1;
+ peer->lastms = 0;
+ memset(&peer->addr, 0, sizeof(peer->addr));
+ return 0;
+}
+
+static int update_key(struct dundi_peer *peer)
+{
+ unsigned char key[16];
+ struct ast_key *ekey, *skey;
+ char eid_str[20];
+ int res;
+ if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
+ build_iv(key);
+ ast_aes_encrypt_key(key, &peer->us_ecx);
+ ast_aes_decrypt_key(key, &peer->us_dcx);
+ ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
+ if (!ekey) {
+ ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
+ peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ return -1;
+ }
+ skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
+ if (!skey) {
+ ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
+ peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ return -1;
+ }
+ if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
+ ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
+ return -1;
+ }
+ if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
+ ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
+ return -1;
+ }
+ peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
+ peer->sentfullkey = 0;
+ /* Looks good */
+ time(&peer->keyexpire);
+ peer->keyexpire += dundi_key_ttl;
+ }
+ return 0;
+}
+
+static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
+{
+ unsigned char curblock[16];
+ int x;
+ memcpy(curblock, iv, sizeof(curblock));
+ while(len > 0) {
+ for (x=0;x<16;x++)
+ curblock[x] ^= src[x];
+ ast_aes_encrypt(curblock, dst, ecx);
+ memcpy(curblock, dst, sizeof(curblock));
+ dst += 16;
+ src += 16;
+ len -= 16;
+ }
+ return 0;
+}
+static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
+{
+ unsigned char lastblock[16];
+ int x;
+ memcpy(lastblock, iv, sizeof(lastblock));
+ while(len > 0) {
+ ast_aes_decrypt(src, dst, dcx);
+ for (x=0;x<16;x++)
+ dst[x] ^= lastblock[x];
+ memcpy(lastblock, src, sizeof(lastblock));
+ dst += 16;
+ src += 16;
+ len -= 16;
+ }
+ return 0;
+}
+
+static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
+{
+ int space = *dstlen;
+ unsigned long bytes;
+ struct dundi_hdr *h;
+ unsigned char *decrypt_space;
+ decrypt_space = alloca(srclen);
+ if (!decrypt_space)
+ return NULL;
+ decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
+ /* Setup header */
+ h = (struct dundi_hdr *)dst;
+ *h = *ohdr;
+ bytes = space - 6;
+ if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
+ ast_debug(1, "Ouch, uncompress failed :(\n");
+ return NULL;
+ }
+ /* Update length */
+ *dstlen = bytes + 6;
+ /* Return new header */
+ return h;
+}
+
+static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
+{
+ unsigned char *compress_space;
+ int len;
+ int res;
+ unsigned long bytes;
+ struct dundi_ie_data ied;
+ struct dundi_peer *peer;
+ unsigned char iv[16];
+ len = pack->datalen + pack->datalen / 100 + 42;
+ compress_space = alloca(len);
+ if (compress_space) {
+ memset(compress_space, 0, len);
+ /* We care about everthing save the first 6 bytes of header */
+ bytes = len;
+ res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
+ if (res != Z_OK) {
+ ast_debug(1, "Ouch, compression failed!\n");
+ return -1;
+ }
+ memset(&ied, 0, sizeof(ied));
+ /* Say who we are */
+ if (!pack->h->iseqno && !pack->h->oseqno) {
+ /* Need the key in the first copy */
+ if (!(peer = find_peer(&trans->them_eid)))
+ return -1;
+ if (update_key(peer))
+ return -1;
+ if (!peer->sentfullkey)
+ ast_set_flag(trans, FLAG_SENDFULLKEY);
+ /* Append key data */
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+ if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
+ dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
+ dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
+ } else {
+ dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
+ }
+ /* Setup contexts */
+ trans->ecx = peer->us_ecx;
+ trans->dcx = peer->us_dcx;
+
+ /* We've sent the full key */
+ peer->sentfullkey = 1;
+ }
+ /* Build initialization vector */
+ build_iv(iv);
+ /* Add the field, rounded up to 16 bytes */
+ dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
+ /* Copy the data */
+ if ((ied.pos + bytes) >= sizeof(ied.buf)) {
+ ast_log(LOG_NOTICE, "Final packet too large!\n");
+ return -1;
+ }
+ encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
+ ied.pos += ((bytes + 15) / 16) * 16;
+ /* Reconstruct header */
+ pack->datalen = sizeof(struct dundi_hdr);
+ pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
+ pack->h->cmdflags = 0;
+ memcpy(pack->h->ies, ied.buf, ied.pos);
+ pack->datalen += ied.pos;
+ return 0;
+ }
+ return -1;
+}
+
+static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
+{
+ unsigned char dst[128];
+ int res;
+ struct ast_key *key, *skey;
+ char eid_str[20];
+ ast_debug(1, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
+ if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
+ /* A match */
+ return 1;
+ } else if (!newkey || !newsig)
+ return 0;
+ if (!memcmp(peer->rxenckey, newkey, 128) &&
+ !memcmp(peer->rxenckey + 128, newsig, 128)) {
+ /* By definition, a match */
+ return 1;
+ }
+ /* Decrypt key */
+ key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
+ if (!key) {
+ ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
+ peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ return -1;
+ }
+
+ skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
+ if (!skey) {
+ ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
+ peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ return -1;
+ }
+
+ /* First check signature */
+ res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
+ if (res)
+ return 0;
+
+ res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
+ if (res != 16) {
+ if (res >= 0)
+ ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
+ return 0;
+ }
+ /* Decrypted, passes signature */
+ ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
+ memcpy(peer->rxenckey, newkey, 128);
+ memcpy(peer->rxenckey + 128, newsig, 128);
+ peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
+ ast_aes_decrypt_key(dst, &peer->them_dcx);
+ ast_aes_encrypt_key(dst, &peer->them_ecx);
+ return 1;
+}
+
+static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
+{
+ struct permission *cur, *perm;
+
+ memcpy(peer_dst, peer_src, sizeof(*peer_dst));
+
+ memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
+ memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
+
+ AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
+ if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
+ continue;
+
+ perm->allow = cur->allow;
+ strcpy(perm->name, cur->name);
+
+ AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
+ }
+
+ AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
+ if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
+ continue;
+
+ perm->allow = cur->allow;
+ strcpy(perm->name, cur->name);
+
+ AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
+ }
+}
+
+static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
+{
+ /* Handle canonical command / response */
+ int final = hdr->cmdresp & 0x80;
+ int cmd = hdr->cmdresp & 0x7f;
+ int x,y,z;
+ int resp;
+ int res;
+ int authpass=0;
+ unsigned char *bufcpy;
+ struct dundi_ie_data ied;
+ struct dundi_ies ies;
+ struct dundi_peer *peer = NULL;
+ char eid_str[20];
+ char eid_str2[20];
+ memset(&ied, 0, sizeof(ied));
+ memset(&ies, 0, sizeof(ies));
+ if (datalen) {
+ bufcpy = alloca(datalen);
+ if (!bufcpy)
+ return -1;
+ /* Make a copy for parsing */
+ memcpy(bufcpy, hdr->ies, datalen);
+ ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
+ if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
+ ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
+ return -1;
+ }
+ }
+ switch(cmd) {
+ case DUNDI_COMMAND_DPDISCOVER:
+ case DUNDI_COMMAND_EIDQUERY:
+ case DUNDI_COMMAND_PRECACHERQ:
+ if (cmd == DUNDI_COMMAND_EIDQUERY)
+ resp = DUNDI_COMMAND_EIDRESPONSE;
+ else if (cmd == DUNDI_COMMAND_PRECACHERQ)
+ resp = DUNDI_COMMAND_PRECACHERP;
+ else
+ resp = DUNDI_COMMAND_DPRESPONSE;
+ /* A dialplan or entity discover -- qualify by highest level entity */
+ peer = find_peer(ies.eids[0]);
+ if (!peer) {
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
+ dundi_send(trans, resp, 0, 1, &ied);
+ } else {
+ int hasauth = 0;
+ trans->us_eid = peer->us_eid;
+ if (strlen(peer->inkey)) {
+ hasauth = encrypted;
+ } else
+ hasauth = 1;
+ if (hasauth) {
+ /* Okay we're authentiated and all, now we check if they're authorized */
+ if (!ies.called_context)
+ ies.called_context = "e164";
+ if (cmd == DUNDI_COMMAND_EIDQUERY) {
+ res = dundi_answer_entity(trans, &ies, ies.called_context);
+ } else {
+ if (ast_strlen_zero(ies.called_number)) {
+ /* They're not permitted to access that context */
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
+ dundi_send(trans, resp, 0, 1, &ied);
+ } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
+ (peer->model & DUNDI_MODEL_INBOUND) &&
+ has_permission(&peer->permit, ies.called_context)) {
+ res = dundi_answer_query(trans, &ies, ies.called_context);
+ if (res < 0) {
+ /* There is no such dundi context */
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
+ dundi_send(trans, resp, 0, 1, &ied);
+ }
+ } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
+ (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
+ has_permission(&peer->include, ies.called_context)) {
+ res = dundi_prop_precache(trans, &ies, ies.called_context);
+ if (res < 0) {
+ /* There is no such dundi context */
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
+ dundi_send(trans, resp, 0, 1, &ied);
+ }
+ } else {
+ /* They're not permitted to access that context */
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
+ dundi_send(trans, resp, 0, 1, &ied);
+ }
+ }
+ } else {
+ /* They're not permitted to access that context */
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
+ dundi_send(trans, resp, 0, 1, &ied);
+ }
+ }
+ break;
+ case DUNDI_COMMAND_REGREQ:
+ /* A register request -- should only have one entity */
+ peer = find_peer(ies.eids[0]);
+
+ /* if the peer is not found and we have a valid 'any_peer' setting */
+ if (any_peer && peer == any_peer) {
+ /* copy any_peer into a new peer object */
+ peer = ast_calloc(1, sizeof(*peer));
+ if (peer) {
+ deep_copy_peer(peer, any_peer);
+
+ /* set EID to remote EID */
+ peer->eid = *ies.eids[0];
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_INSERT_HEAD(&peers, peer, list);
+ AST_LIST_UNLOCK(&peers);
+ }
+ }
+
+ if (!peer || !peer->dynamic) {
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
+ dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
+ } else {
+ int hasauth = 0;
+ trans->us_eid = peer->us_eid;
+ if (!ast_strlen_zero(peer->inkey)) {
+ hasauth = encrypted;
+ } else
+ hasauth = 1;
+ if (hasauth) {
+ int expire = default_expiration;
+ char data[256];
+ int needqual = 0;
+ if (peer->registerexpire > -1)
+ ast_sched_del(sched, peer->registerexpire);
+ peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
+ snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
+ ntohs(trans->addr.sin_port), expire);
+ ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
+ if (inaddrcmp(&peer->addr, &trans->addr)) {
+ ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
+ ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
+ needqual = 1;
+ }
+
+ memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
+ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
+ dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
+ if (needqual)
+ qualify_peer(peer, 1);
+ }
+ }
+ break;
+ case DUNDI_COMMAND_DPRESPONSE:
+ /* A dialplan response, lets see what we got... */
+ if (ies.cause < 1) {
+ /* Success of some sort */
+ ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
+ if (ast_test_flag(trans, FLAG_ENCRYPT)) {
+ authpass = encrypted;
+ } else
+ authpass = 1;
+ if (authpass) {
+ /* Pass back up answers */
+ if (trans->parent && trans->parent->dr) {
+ y = trans->parent->respcount;
+ for (x=0;x<ies.anscount;x++) {
+ if (trans->parent->respcount < trans->parent->maxcount) {
+ /* Make sure it's not already there */
+ for (z=0;z<trans->parent->respcount;z++) {
+ if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
+ !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
+ break;
+ }
+ if (z == trans->parent->respcount) {
+ /* Copy into parent responses */
+ trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
+ trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
+ trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
+ trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
+ if (ies.expiration > 0)
+ trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
+ else
+ trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
+ dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
+ sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
+ &ies.answers[x]->eid);
+ ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
+ sizeof(trans->parent->dr[trans->parent->respcount].dest));
+ ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
+ sizeof(trans->parent->dr[trans->parent->respcount].tech));
+ trans->parent->respcount++;
+ ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
+ } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
+ /* Update weight if appropriate */
+ trans->parent->dr[z].weight = ies.answers[x]->weight;
+ }
+ } else
+ ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
+ trans->parent->number, trans->parent->dcontext);
+ }
+ /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
+ the cache know if this request was unaffected by our entity list. */
+ cache_save(&trans->them_eid, trans->parent, y,
+ ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
+ if (ies.hint) {
+ cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
+ if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
+ ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
+ if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
+ if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
+ ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
+ sizeof(trans->parent->hmd->exten));
+ }
+ } else {
+ ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
+ }
+ }
+ if (ies.expiration > 0) {
+ if (trans->parent->expiration > ies.expiration) {
+ trans->parent->expiration = ies.expiration;
+ }
+ }
+ }
+ /* Close connection if not final */
+ if (!final)
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ }
+
+ } else {
+ /* Auth failure, check for data */
+ if (!final) {
+ /* Cancel if they didn't already */
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ }
+ }
+ break;
+ case DUNDI_COMMAND_EIDRESPONSE:
+ /* A dialplan response, lets see what we got... */
+ if (ies.cause < 1) {
+ /* Success of some sort */
+ ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
+ if (ast_test_flag(trans, FLAG_ENCRYPT)) {
+ authpass = encrypted;
+ } else
+ authpass = 1;
+ if (authpass) {
+ /* Pass back up answers */
+ if (trans->parent && trans->parent->dei && ies.q_org) {
+ if (!trans->parent->respcount) {
+ trans->parent->respcount++;
+ if (ies.q_dept)
+ ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
+ if (ies.q_org)
+ ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
+ if (ies.q_locality)
+ ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
+ if (ies.q_stateprov)
+ ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
+ if (ies.q_country)
+ ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
+ if (ies.q_email)
+ ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
+ if (ies.q_phone)
+ ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
+ if (ies.q_ipaddr)
+ ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
+ if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
+ /* If it's them, update our address */
+ ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
+ }
+ }
+ if (ies.hint) {
+ if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
+ ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
+ }
+ }
+ /* Close connection if not final */
+ if (!final)
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ }
+
+ } else {
+ /* Auth failure, check for data */
+ if (!final) {
+ /* Cancel if they didn't already */
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ }
+ }
+ break;
+ case DUNDI_COMMAND_REGRESPONSE:
+ /* A dialplan response, lets see what we got... */
+ if (ies.cause < 1) {
+ int hasauth;
+ /* Success of some sort */
+ if (ast_test_flag(trans, FLAG_ENCRYPT)) {
+ hasauth = encrypted;
+ } else
+ hasauth = 1;
+
+ if (!hasauth) {
+ ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
+ if (!final) {
+ dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
+ }
+ } else {
+ ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
+ dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
+ /* Close connection if not final */
+ if (!final)
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ }
+ } else {
+ /* Auth failure, cancel if they didn't for some reason */
+ if (!final) {
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ }
+ }
+ break;
+ case DUNDI_COMMAND_INVALID:
+ case DUNDI_COMMAND_NULL:
+ case DUNDI_COMMAND_PRECACHERP:
+ /* Do nothing special */
+ if (!final)
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ break;
+ case DUNDI_COMMAND_ENCREJ:
+ if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
+ /* No really, it's over at this point */
+ if (!final)
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ } else {
+ /* Send with full key */
+ ast_set_flag(trans, FLAG_SENDFULLKEY);
+ if (final) {
+ /* Ooops, we got a final message, start by sending ACK... */
+ dundi_ack(trans, hdr->cmdresp & 0x80);
+ trans->aseqno = trans->iseqno;
+ /* Now, we gotta create a new transaction */
+ if (!reset_transaction(trans)) {
+ /* Make sure handle_frame doesn't destroy us */
+ hdr->cmdresp &= 0x7f;
+ /* Parse the message we transmitted */
+ memset(&ies, 0, sizeof(ies));
+ dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
+ /* Reconstruct outgoing encrypted packet */
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+ dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
+ dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
+ if (ies.encblock)
+ dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
+ dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
+ peer->sentfullkey = 1;
+ }
+ }
+ }
+ break;
+ case DUNDI_COMMAND_ENCRYPT:
+ if (!encrypted) {
+ /* No nested encryption! */
+ if ((trans->iseqno == 1) && !trans->oseqno) {
+ if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
+ ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
+ (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
+ if (!final) {
+ dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
+ }
+ break;
+ }
+ apply_peer(trans, peer);
+ /* Key passed, use new contexts for this session */
+ trans->ecx = peer->them_ecx;
+ trans->dcx = peer->them_dcx;
+ }
+ if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
+ struct dundi_hdr *dhdr;
+ unsigned char decoded[MAX_PACKET_SIZE];
+ int ddatalen;
+ ddatalen = sizeof(decoded);
+ dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
+ if (dhdr) {
+ /* Handle decrypted response */
+ if (dundidebug)
+ dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
+ handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
+ /* Carry back final flag */
+ hdr->cmdresp |= dhdr->cmdresp & 0x80;
+ break;
+ } else {
+ ast_debug(1, "Ouch, decrypt failed :(\n");
+ }
+ }
+ }
+ if (!final) {
+ /* Turn off encryption */
+ ast_clear_flag(trans, FLAG_ENCRYPT);
+ dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
+ }
+ break;
+ default:
+ /* Send unknown command if we don't know it, with final flag IFF it's the
+ first command in the dialog and only if we haven't recieved final notification */
+ if (!final) {
+ dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
+ dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
+ }
+ }
+ return 0;
+}
+
+static void destroy_packet(struct dundi_packet *pack, int needfree);
+static void destroy_packets(struct packetlist *p)
+{
+ struct dundi_packet *pack;
+
+ while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
+ if (pack->retransid > -1)
+ ast_sched_del(sched, pack->retransid);
+ ast_free(pack);
+ }
+}
+
+
+static int ack_trans(struct dundi_transaction *trans, int iseqno)
+{
+ struct dundi_packet *pack;
+
+ /* Ack transmitted packet corresponding to iseqno */
+ AST_LIST_TRAVERSE(&trans->packets, pack, list) {
+ if ((pack->h->oseqno + 1) % 255 == iseqno) {
+ destroy_packet(pack, 0);
+ if (!AST_LIST_EMPTY(&trans->lasttrans)) {
+ ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
+ destroy_packets(&trans->lasttrans);
+ }
+ AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
+ if (trans->autokillid > -1)
+ ast_sched_del(sched, trans->autokillid);
+ trans->autokillid = -1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
+{
+ struct dundi_transaction *trans;
+ trans = find_transaction(h, sin);
+ if (!trans) {
+ dundi_reject(h, sin);
+ return 0;
+ }
+ /* Got a transaction, see where this header fits in */
+ if (h->oseqno == trans->iseqno) {
+ /* Just what we were looking for... Anything but ack increments iseqno */
+ if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
+ /* If final, we're done */
+ destroy_trans(trans, 0);
+ return 0;
+ }
+ if (h->cmdresp != DUNDI_COMMAND_ACK) {
+ trans->oiseqno = trans->iseqno;
+ trans->iseqno++;
+ handle_command_response(trans, h, datalen, 0);
+ }
+ if (trans->aseqno != trans->iseqno) {
+ dundi_ack(trans, h->cmdresp & 0x80);
+ trans->aseqno = trans->iseqno;
+ }
+ /* Delete any saved last transmissions */
+ destroy_packets(&trans->lasttrans);
+ if (h->cmdresp & 0x80) {
+ /* Final -- destroy now */
+ destroy_trans(trans, 0);
+ }
+ } else if (h->oseqno == trans->oiseqno) {
+ /* Last incoming sequence number -- send ACK without processing */
+ dundi_ack(trans, 0);
+ } else {
+ /* Out of window -- simply drop */
+ ast_debug(1, "Dropping packet out of window!\n");
+ }
+ return 0;
+}
+
+static int socket_read(int *id, int fd, short events, void *cbdata)
+{
+ struct sockaddr_in sin;
+ int res;
+ struct dundi_hdr *h;
+ char buf[MAX_PACKET_SIZE];
+ socklen_t len = sizeof(sin);
+
+ res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
+ if (res < 0) {
+ if (errno != ECONNREFUSED)
+ ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
+ return 1;
+ }
+ if (res < sizeof(struct dundi_hdr)) {
+ ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
+ return 1;
+ }
+ buf[res] = '\0';
+ h = (struct dundi_hdr *) buf;
+ if (dundidebug)
+ dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
+ AST_LIST_LOCK(&peers);
+ handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
+ AST_LIST_UNLOCK(&peers);
+ return 1;
+}
+
+static void build_secret(char *secret, int seclen)
+{
+ unsigned char tmp[16];
+ char *s;
+ build_iv(tmp);
+ secret[0] = '\0';
+ ast_base64encode(secret, tmp, sizeof(tmp), seclen);
+ /* Eliminate potential bad characters */
+ while((s = strchr(secret, ';'))) *s = '+';
+ while((s = strchr(secret, '/'))) *s = '+';
+ while((s = strchr(secret, ':'))) *s = '+';
+ while((s = strchr(secret, '@'))) *s = '+';
+}
+
+
+static void save_secret(const char *newkey, const char *oldkey)
+{
+ char tmp[256];
+ if (oldkey)
+ snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
+ else
+ snprintf(tmp, sizeof(tmp), "%s", newkey);
+ rotatetime = time(NULL) + DUNDI_SECRET_TIME;
+ ast_db_put(secretpath, "secret", tmp);
+ snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
+ ast_db_put(secretpath, "secretexpiry", tmp);
+}
+
+static void load_password(void)
+{
+ char *current=NULL;
+ char *last=NULL;
+ char tmp[256];
+ time_t expired;
+
+ ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
+ if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
+ ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
+ current = strchr(tmp, ';');
+ if (!current)
+ current = tmp;
+ else {
+ *current = '\0';
+ current++;
+ };
+ if ((time(NULL) - expired) < 0) {
+ if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
+ expired = time(NULL) + DUNDI_SECRET_TIME;
+ } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
+ last = current;
+ current = NULL;
+ } else {
+ last = NULL;
+ current = NULL;
+ }
+ }
+ if (current) {
+ /* Current key is still valid, just setup rotatation properly */
+ ast_copy_string(cursecret, current, sizeof(cursecret));
+ rotatetime = expired;
+ } else {
+ /* Current key is out of date, rotate or eliminate all together */
+ build_secret(cursecret, sizeof(cursecret));
+ save_secret(cursecret, last);
+ }
+}
+
+static void check_password(void)
+{
+ char oldsecret[80];
+ time_t now;
+
+ time(&now);
+#if 0
+ printf("%ld/%ld\n", now, rotatetime);
+#endif
+ if ((now - rotatetime) >= 0) {
+ /* Time to rotate keys */
+ ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
+ build_secret(cursecret, sizeof(cursecret));
+ save_secret(cursecret, oldsecret);
+ }
+}
+
+static void *network_thread(void *ignore)
+{
+ /* Our job is simple: Send queued messages, retrying if necessary. Read frames
+ from the network, and queue them for delivery to the channels */
+ int res;
+ /* Establish I/O callback for socket read */
+ ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
+
+ while (!dundi_shutdown) {
+ res = ast_sched_wait(sched);
+ if ((res > 1000) || (res < 0))
+ res = 1000;
+ res = ast_io_wait(io, res);
+ if (res >= 0) {
+ AST_LIST_LOCK(&peers);
+ ast_sched_runq(sched);
+ AST_LIST_UNLOCK(&peers);
+ }
+ check_password();
+ }
+
+ netthreadid = AST_PTHREADT_NULL;
+
+ return NULL;
+}
+
+static void *process_precache(void *ign)
+{
+ struct dundi_precache_queue *qe;
+ time_t now;
+ char context[256];
+ char number[256];
+ int run;
+
+ while (!dundi_shutdown) {
+ time(&now);
+ run = 0;
+ AST_LIST_LOCK(&pcq);
+ if ((qe = AST_LIST_FIRST(&pcq))) {
+ if (!qe->expiration) {
+ /* Gone... Remove... */
+ AST_LIST_REMOVE_HEAD(&pcq, list);
+ ast_free(qe);
+ } else if (qe->expiration < now) {
+ /* Process this entry */
+ qe->expiration = 0;
+ ast_copy_string(context, qe->context, sizeof(context));
+ ast_copy_string(number, qe->number, sizeof(number));
+ run = 1;
+ }
+ }
+ AST_LIST_UNLOCK(&pcq);
+ if (run) {
+ dundi_precache(context, number);
+ } else
+ sleep(1);
+ }
+
+ precachethreadid = AST_PTHREADT_NULL;
+
+ return NULL;
+}
+
+static int start_network_thread(void)
+{
+ ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
+ ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
+ return 0;
+}
+
+static char *dundi_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi debug";
+ e->usage =
+ "Usage: dundi debug\n"
+ " Enables dumping of DUNDi packets for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ dundidebug = 1;
+ ast_cli(a->fd, "DUNDi Debugging Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *dundi_do_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi store history";
+ e->usage =
+ "Usage: dundi store history\n"
+ " Enables storing of DUNDi requests and times for debugging\n"
+ "purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ global_storehistory = 1;
+ ast_cli(a->fd, "DUNDi History Storage Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int stats = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi flush [stats]";
+ e->usage =
+ "Usage: dundi flush [stats]\n"
+ " Flushes DUNDi answer cache, used primarily for debug. If\n"
+ "'stats' is present, clears timer statistics instead of normal\n"
+ "operation.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if ((a->argc < 2) || (a->argc > 3))
+ return CLI_SHOWUSAGE;
+ if (a->argc > 2) {
+ if (!strcasecmp(a->argv[2], "stats"))
+ stats = 1;
+ else
+ return CLI_SHOWUSAGE;
+ }
+ if (stats) {
+ /* Flush statistics */
+ struct dundi_peer *p;
+ int x;
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&peers, p, list) {
+ for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
+ if (p->lookups[x])
+ ast_free(p->lookups[x]);
+ p->lookups[x] = NULL;
+ p->lookuptimes[x] = 0;
+ }
+ p->avgms = 0;
+ }
+ AST_LIST_UNLOCK(&peers);
+ } else {
+ ast_db_deltree("dundi/cache", NULL);
+ ast_cli(a->fd, "DUNDi Cache Flushed\n");
+ }
+ return CLI_SUCCESS;
+}
+
+static char *dundi_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi no debug";
+ e->usage =
+ "Usage: dundi no debug\n"
+ " Disables dumping of DUNDi packets for debugging purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ dundidebug = 0;
+ ast_cli(a->fd, "DUNDi Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *dundi_no_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi no store history";
+ e->usage =
+ "Usage: dundi no store history\n"
+ " Disables storing of DUNDi requests and times for debugging\n"
+ "purposes\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ global_storehistory = 0;
+ ast_cli(a->fd, "DUNDi History Storage Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *model2str(int model)
+{
+ switch(model) {
+ case DUNDI_MODEL_INBOUND:
+ return "Inbound";
+ case DUNDI_MODEL_OUTBOUND:
+ return "Outbound";
+ case DUNDI_MODEL_SYMMETRIC:
+ return "Symmetric";
+ default:
+ return "Unknown";
+ }
+}
+
+static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
+{
+ int which=0, len;
+ char *ret = NULL;
+ struct dundi_peer *p;
+ char eid_str[20];
+
+ if (pos != rpos)
+ return NULL;
+ AST_LIST_LOCK(&peers);
+ len = strlen(word);
+ AST_LIST_TRAVERSE(&peers, p, list) {
+ const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
+ if (!strncasecmp(word, s, len) && ++which > state) {
+ ret = ast_strdup(s);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&peers);
+ return ret;
+}
+
+static int rescomp(const void *a, const void *b)
+{
+ const struct dundi_result *resa, *resb;
+ resa = a;
+ resb = b;
+ if (resa->weight < resb->weight)
+ return -1;
+ if (resa->weight > resb->weight)
+ return 1;
+ return 0;
+}
+
+static void sort_results(struct dundi_result *results, int count)
+{
+ qsort(results, count, sizeof(results[0]), rescomp);
+}
+
+static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+ char tmp[256];
+ char fs[80] = "";
+ char *context;
+ int x;
+ int bypass = 0;
+ struct dundi_result dr[MAX_RESULTS];
+ struct timeval start;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi lookup";
+ e->usage =
+ "Usage: dundi lookup <number>[@context] [bypass]\n"
+ " Lookup the given number within the given DUNDi context\n"
+ "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
+ "keyword is specified.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc < 3) || (a->argc > 4))
+ return CLI_SHOWUSAGE;
+ if (a->argc > 3) {
+ if (!strcasecmp(a->argv[3], "bypass"))
+ bypass=1;
+ else
+ return CLI_SHOWUSAGE;
+ }
+ ast_copy_string(tmp, a->argv[2], sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ }
+ start = ast_tvnow();
+ res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
+
+ if (res < 0)
+ ast_cli(a->fd, "DUNDi lookup returned error.\n");
+ else if (!res)
+ ast_cli(a->fd, "DUNDi lookup returned no results.\n");
+ else
+ sort_results(dr, res);
+ for (x=0;x<res;x++) {
+ ast_cli(a->fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
+ ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
+ }
+ ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
+ return CLI_SUCCESS;
+}
+
+static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+ char tmp[256];
+ char *context;
+ struct timeval start;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi precache";
+ e->usage =
+ "Usage: dundi precache <number>[@context]\n"
+ " Lookup the given number within the given DUNDi context\n"
+ "(or e164 if none is specified) and precaches the results to any\n"
+ "upstream DUNDi push servers.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if ((a->argc < 3) || (a->argc > 3))
+ return CLI_SHOWUSAGE;
+ ast_copy_string(tmp, a->argv[2], sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ }
+ start = ast_tvnow();
+ res = dundi_precache(context, tmp);
+
+ if (res < 0)
+ ast_cli(a->fd, "DUNDi precache returned error.\n");
+ else if (!res)
+ ast_cli(a->fd, "DUNDi precache returned no error.\n");
+ ast_cli(a->fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
+ return CLI_SUCCESS;
+}
+
+static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+ char tmp[256];
+ char *context;
+ dundi_eid eid;
+ struct dundi_entity_info dei;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi query";
+ e->usage =
+ "Usage: dundi query <entity>[@context]\n"
+ " Attempts to retrieve contact information for a specific\n"
+ "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
+ "e164 if none is specified).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if ((a->argc < 3) || (a->argc > 3))
+ return CLI_SHOWUSAGE;
+ if (dundi_str_to_eid(&eid, a->argv[2])) {
+ ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
+ return CLI_SHOWUSAGE;
+ }
+ ast_copy_string(tmp, a->argv[2], sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ }
+ res = dundi_query_eid(&dei, context, eid);
+ if (res < 0)
+ ast_cli(a->fd, "DUNDi Query EID returned error.\n");
+ else if (!res)
+ ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
+ else {
+ ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
+ ast_cli(a->fd, "Department: %s\n", dei.orgunit);
+ ast_cli(a->fd, "Organization: %s\n", dei.org);
+ ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
+ ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
+ ast_cli(a->fd, "Country: %s\n", dei.country);
+ ast_cli(a->fd, "E-mail: %s\n", dei.email);
+ ast_cli(a->fd, "Phone: %s\n", dei.phone);
+ ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct dundi_peer *peer;
+ struct permission *p;
+ char *order;
+ char eid_str[20];
+ int x, cnt;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi show peer";
+ e->usage =
+ "Usage: dundi show peer [peer]\n"
+ " Provide a detailed description of a specifid DUNDi peer.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&peers, peer, list) {
+ if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
+ break;
+ }
+ if (peer) {
+ switch(peer->order) {
+ case 0:
+ order = "Primary";
+ break;
+ case 1:
+ order = "Secondary";
+ break;
+ case 2:
+ order = "Tertiary";
+ break;
+ case 3:
+ order = "Quartiary";
+ break;
+ default:
+ order = "Unknown";
+ }
+ ast_cli(a->fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
+ ast_cli(a->fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
+ ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
+ ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
+ ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
+ ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
+ if (!AST_LIST_EMPTY(&peer->include))
+ ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
+ AST_LIST_TRAVERSE(&peer->include, p, list)
+ ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
+ if (!AST_LIST_EMPTY(&peer->permit))
+ ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
+ AST_LIST_TRAVERSE(&peer->permit, p, list)
+ ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
+ cnt = 0;
+ for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
+ if (peer->lookups[x]) {
+ if (!cnt)
+ ast_cli(a->fd, "Last few query times:\n");
+ ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
+ cnt++;
+ }
+ }
+ if (cnt)
+ ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
+ } else
+ ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
+ AST_LIST_UNLOCK(&peers);
+ return CLI_SUCCESS;
+}
+
+static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
+#define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
+ struct dundi_peer *peer;
+ int registeredonly=0;
+ char avgms[20];
+ char eid_str[20];
+ int online_peers = 0;
+ int offline_peers = 0;
+ int unmonitored_peers = 0;
+ int total_peers = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi show peers [registered|include|exclude|begin]";
+ e->usage =
+ "Usage: dundi show peers [registered|include|exclude|begin]\n"
+ " Lists all known DUNDi peers.\n"
+ " If 'registered' is present, only registered peers are shown.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5))
+ return CLI_SHOWUSAGE;
+ if ((a->argc == 4)) {
+ if (!strcasecmp(a->argv[3], "registered")) {
+ registeredonly = 1;
+ } else
+ return CLI_SHOWUSAGE;
+ }
+ AST_LIST_LOCK(&peers);
+ ast_cli(a->fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
+ AST_LIST_TRAVERSE(&peers, peer, list) {
+ char status[20];
+ int print_line = -1;
+ char srch[2000];
+ total_peers++;
+ if (registeredonly && !peer->addr.sin_addr.s_addr)
+ continue;
+ if (peer->maxms) {
+ if (peer->lastms < 0) {
+ strcpy(status, "UNREACHABLE");
+ offline_peers++;
+ }
+ else if (peer->lastms > peer->maxms) {
+ snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
+ offline_peers++;
+ }
+ else if (peer->lastms) {
+ snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
+ online_peers++;
+ }
+ else {
+ strcpy(status, "UNKNOWN");
+ offline_peers++;
+ }
+ } else {
+ strcpy(status, "Unmonitored");
+ unmonitored_peers++;
+ }
+ if (peer->avgms)
+ snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
+ else
+ strcpy(avgms, "Unavail");
+ snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+ peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
+
+ if (a->argc == 5) {
+ if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
+ print_line = -1;
+ } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
+ print_line = 1;
+ } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
+ print_line = -1;
+ } else {
+ print_line = 0;
+ }
+ }
+
+ if (print_line) {
+ ast_cli(a->fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+ peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
+ }
+ }
+ ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
+ AST_LIST_UNLOCK(&peers);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
+#define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
+ struct dundi_transaction *trans;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi show trans";
+ e->usage =
+ "Usage: dundi show trans\n"
+ " Lists all known DUNDi transactions.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ AST_LIST_LOCK(&peers);
+ ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
+ AST_LIST_TRAVERSE(&alltrans, trans, all) {
+ ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
+ ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
+ }
+ AST_LIST_UNLOCK(&peers);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char eid_str[20];
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi show entityid";
+ e->usage =
+ "Usage: dundi show entityid\n"
+ " Displays the global entityid for this host.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ AST_LIST_LOCK(&peers);
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
+ AST_LIST_UNLOCK(&peers);
+ ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
+ return CLI_SUCCESS;
+}
+
+static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
+#define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
+ struct dundi_request *req;
+ char eidstr[20];
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi show requests";
+ e->usage =
+ "Usage: dundi show requests\n"
+ " Lists all known pending DUNDi requests.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ AST_LIST_LOCK(&peers);
+ ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
+ AST_LIST_TRAVERSE(&requests, req, list) {
+ ast_cli(a->fd, FORMAT, req->number, req->dcontext,
+ dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
+ }
+ AST_LIST_UNLOCK(&peers);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+/* Grok-a-dial DUNDi */
+
+static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
+#define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
+ struct dundi_mapping *map;
+ char fs[256];
+ char weight[8];
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi show mappings";
+ e->usage =
+ "Usage: dundi show mappings\n"
+ " Lists all known DUNDi mappings.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ AST_LIST_LOCK(&peers);
+ ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
+ AST_LIST_TRAVERSE(&mappings, map, list) {
+ snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
+ ast_cli(a->fd, FORMAT, map->dcontext, weight,
+ ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
+ dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
+ }
+ AST_LIST_UNLOCK(&peers);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
+#define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
+ struct dundi_precache_queue *qe;
+ int h,m,s;
+ time_t now;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dundi show precache";
+ e->usage =
+ "Usage: dundi show precache\n"
+ " Lists all known DUNDi scheduled precache updates.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ time(&now);
+ ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
+ AST_LIST_LOCK(&pcq);
+ AST_LIST_TRAVERSE(&pcq, qe, list) {
+ s = qe->expiration - now;
+ h = s / 3600;
+ s = s % 3600;
+ m = s / 60;
+ s = s % 60;
+ ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
+ }
+ AST_LIST_UNLOCK(&pcq);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static struct ast_cli_entry cli_dundi[] = {
+ AST_CLI_DEFINE(dundi_do_debug, "Enable DUNDi debugging"),
+ AST_CLI_DEFINE(dundi_no_debug, "Disable DUNDi debugging"),
+ AST_CLI_DEFINE(dundi_do_store_history, "Enable DUNDi historic records"),
+ AST_CLI_DEFINE(dundi_no_store_history, "Disable DUNDi historic records"),
+ AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
+ AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
+ AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
+ AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
+ AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
+ AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
+ AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
+ AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
+ AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
+ AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
+ AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
+};
+
+static struct dundi_transaction *create_transaction(struct dundi_peer *p)
+{
+ struct dundi_transaction *trans;
+ int tid;
+
+ /* Don't allow creation of transactions to non-registered peers */
+ if (p && !p->addr.sin_addr.s_addr)
+ return NULL;
+ tid = get_trans_id();
+ if (tid < 1)
+ return NULL;
+ if (!(trans = ast_calloc(1, sizeof(*trans))))
+ return NULL;
+
+ if (global_storehistory) {
+ trans->start = ast_tvnow();
+ ast_set_flag(trans, FLAG_STOREHIST);
+ }
+ trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
+ trans->autokillid = -1;
+ if (p) {
+ apply_peer(trans, p);
+ if (!p->sentfullkey)
+ ast_set_flag(trans, FLAG_SENDFULLKEY);
+ }
+ trans->strans = tid;
+ AST_LIST_INSERT_HEAD(&alltrans, trans, all);
+
+ return trans;
+}
+
+static int dundi_xmit(struct dundi_packet *pack)
+{
+ int res;
+ if (dundidebug)
+ dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
+ res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
+ ast_inet_ntoa(pack->parent->addr.sin_addr),
+ ntohs(pack->parent->addr.sin_port), strerror(errno));
+ }
+ if (res > 0)
+ res = 0;
+ return res;
+}
+
+static void destroy_packet(struct dundi_packet *pack, int needfree)
+{
+ if (pack->parent)
+ AST_LIST_REMOVE(&pack->parent->packets, pack, list);
+ if (pack->retransid > -1)
+ ast_sched_del(sched, pack->retransid);
+ if (needfree)
+ ast_free(pack);
+ else
+ pack->retransid = -1;
+}
+
+static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
+{
+ struct dundi_peer *peer;
+ int ms;
+ int x;
+ int cnt;
+ char eid_str[20];
+ if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
+ AST_LIST_TRAVERSE(&peers, peer, list) {
+ if (peer->regtrans == trans)
+ peer->regtrans = NULL;
+ if (peer->qualtrans == trans) {
+ if (fromtimeout) {
+ if (peer->lastms > -1)
+ ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ peer->lastms = -1;
+ } else {
+ ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
+ if (ms < 1)
+ ms = 1;
+ if (ms < peer->maxms) {
+ if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
+ ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ } else if (peer->lastms < peer->maxms) {
+ ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
+ }
+ peer->lastms = ms;
+ }
+ peer->qualtrans = NULL;
+ }
+ if (ast_test_flag(trans, FLAG_STOREHIST)) {
+ if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
+ if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
+ peer->avgms = 0;
+ cnt = 0;
+ if (peer->lookups[DUNDI_TIMING_HISTORY-1])
+ ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
+ for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
+ peer->lookuptimes[x] = peer->lookuptimes[x-1];
+ peer->lookups[x] = peer->lookups[x-1];
+ if (peer->lookups[x]) {
+ peer->avgms += peer->lookuptimes[x];
+ cnt++;
+ }
+ }
+ peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
+ peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
+ if (peer->lookups[0]) {
+ sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
+ peer->avgms += peer->lookuptimes[0];
+ cnt++;
+ }
+ if (cnt)
+ peer->avgms /= cnt;
+ }
+ }
+ }
+ }
+ }
+ if (trans->parent) {
+ /* Unlink from parent if appropriate */
+ AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
+ if (AST_LIST_EMPTY(&trans->parent->trans)) {
+ /* Wake up sleeper */
+ if (trans->parent->pfds[1] > -1) {
+ write(trans->parent->pfds[1], "killa!", 6);
+ }
+ }
+ }
+ /* Unlink from all trans */
+ AST_LIST_REMOVE(&alltrans, trans, all);
+ destroy_packets(&trans->packets);
+ destroy_packets(&trans->lasttrans);
+ if (trans->autokillid > -1)
+ ast_sched_del(sched, trans->autokillid);
+ trans->autokillid = -1;
+ if (trans->thread) {
+ /* If used by a thread, mark as dead and be done */
+ ast_set_flag(trans, FLAG_DEAD);
+ } else
+ ast_free(trans);
+}
+
+static int dundi_rexmit(const void *data)
+{
+ struct dundi_packet *pack = (struct dundi_packet *)data;
+ int res;
+ AST_LIST_LOCK(&peers);
+ if (pack->retrans < 1) {
+ pack->retransid = -1;
+ if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
+ ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
+ ast_inet_ntoa(pack->parent->addr.sin_addr),
+ ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
+ destroy_trans(pack->parent, 1);
+ res = 0;
+ } else {
+ /* Decrement retransmission, try again */
+ pack->retrans--;
+ dundi_xmit(pack);
+ res = 1;
+ }
+ AST_LIST_UNLOCK(&peers);
+ return res;
+}
+
+static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
+{
+ struct dundi_packet *pack;
+ int res;
+ int len;
+ char eid_str[20];
+ len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
+ /* Reserve enough space for encryption */
+ if (ast_test_flag(trans, FLAG_ENCRYPT))
+ len += 384;
+ pack = ast_calloc(1, len);
+ if (pack) {
+ pack->h = (struct dundi_hdr *)(pack->data);
+ if (cmdresp != DUNDI_COMMAND_ACK) {
+ pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
+ pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
+ AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
+ }
+ pack->parent = trans;
+ pack->h->strans = htons(trans->strans);
+ pack->h->dtrans = htons(trans->dtrans);
+ pack->h->iseqno = trans->iseqno;
+ pack->h->oseqno = trans->oseqno;
+ pack->h->cmdresp = cmdresp;
+ pack->datalen = sizeof(struct dundi_hdr);
+ if (ied) {
+ memcpy(pack->h->ies, ied->buf, ied->pos);
+ pack->datalen += ied->pos;
+ }
+ if (final) {
+ pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
+ ast_set_flag(trans, FLAG_FINAL);
+ }
+ pack->h->cmdflags = flags;
+ if (cmdresp != DUNDI_COMMAND_ACK) {
+ trans->oseqno++;
+ trans->oseqno = trans->oseqno % 256;
+ }
+ trans->aseqno = trans->iseqno;
+ /* If we have their public key, encrypt */
+ if (ast_test_flag(trans, FLAG_ENCRYPT)) {
+ switch(cmdresp) {
+ case DUNDI_COMMAND_REGREQ:
+ case DUNDI_COMMAND_REGRESPONSE:
+ case DUNDI_COMMAND_DPDISCOVER:
+ case DUNDI_COMMAND_DPRESPONSE:
+ case DUNDI_COMMAND_EIDQUERY:
+ case DUNDI_COMMAND_EIDRESPONSE:
+ case DUNDI_COMMAND_PRECACHERQ:
+ case DUNDI_COMMAND_PRECACHERP:
+ if (dundidebug)
+ dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
+ res = dundi_encrypt(trans, pack);
+ break;
+ default:
+ res = 0;
+ }
+ } else
+ res = 0;
+ if (!res)
+ res = dundi_xmit(pack);
+ if (res)
+ ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
+
+ if (cmdresp == DUNDI_COMMAND_ACK)
+ ast_free(pack);
+ return res;
+ }
+ return -1;
+}
+
+static int do_autokill(const void *data)
+{
+ struct dundi_transaction *trans = (struct dundi_transaction *)data;
+ char eid_str[20];
+ ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
+ trans->autokillid = -1;
+ destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
+ return 0;
+}
+
+static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
+{
+ struct dundi_peer *p;
+ if (!dundi_eid_cmp(eid, us)) {
+ dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
+ return;
+ }
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&peers, p, list) {
+ if (!dundi_eid_cmp(&p->eid, eid)) {
+ if (has_permission(&p->include, context))
+ dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
+ else
+ dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
+ break;
+ }
+ }
+ if (!p)
+ dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
+ AST_LIST_UNLOCK(&peers);
+}
+
+static int dundi_discover(struct dundi_transaction *trans)
+{
+ struct dundi_ie_data ied;
+ int x;
+ if (!trans->parent) {
+ ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
+ return -1;
+ }
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+ if (!dundi_eid_zero(&trans->us_eid))
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
+ for (x=0;x<trans->eidcount;x++)
+ dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
+ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
+ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
+ dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
+ if (trans->parent->cbypass)
+ dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
+ if (trans->autokilltimeout)
+ trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
+ return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
+}
+
+static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
+{
+ struct dundi_ie_data ied;
+ int x, res;
+ int max = 999999;
+ int expiration = dundi_cache_time;
+ int ouranswers=0;
+ dundi_eid *avoid[1] = { NULL, };
+ int direct[1] = { 0, };
+ struct dundi_result dr[MAX_RESULTS];
+ struct dundi_hint_metadata hmd;
+ if (!trans->parent) {
+ ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
+ return -1;
+ }
+ memset(&hmd, 0, sizeof(hmd));
+ memset(&dr, 0, sizeof(dr));
+ /* Look up the answers we're going to include */
+ for (x=0;x<mapcount;x++)
+ ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
+ if (ouranswers < 0)
+ ouranswers = 0;
+ for (x=0;x<ouranswers;x++) {
+ if (dr[x].weight < max)
+ max = dr[x].weight;
+ }
+ if (max) {
+ /* If we do not have a canonical result, keep looking */
+ res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
+ if (res > 0) {
+ /* Append answer in result */
+ ouranswers += res;
+ }
+ }
+
+ if (ouranswers > 0) {
+ *foundanswers += ouranswers;
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+ if (!dundi_eid_zero(&trans->us_eid))
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+ for (x=0;x<trans->eidcount;x++)
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
+ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
+ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
+ dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
+ for (x=0;x<ouranswers;x++) {
+ /* Add answers */
+ if (dr[x].expiration && (expiration > dr[x].expiration))
+ expiration = dr[x].expiration;
+ dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
+ }
+ dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
+ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
+ if (trans->autokilltimeout)
+ trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
+ if (expiration < *minexp)
+ *minexp = expiration;
+ return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
+ } else {
+ /* Oops, nothing to send... */
+ destroy_trans(trans, 0);
+ return 0;
+ }
+}
+
+static int dundi_query(struct dundi_transaction *trans)
+{
+ struct dundi_ie_data ied;
+ int x;
+ if (!trans->parent) {
+ ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
+ return -1;
+ }
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+ if (!dundi_eid_zero(&trans->us_eid))
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+ for (x=0;x<trans->eidcount;x++)
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
+ dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
+ dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
+ dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
+ if (trans->autokilltimeout)
+ trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
+ return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
+}
+
+static int discover_transactions(struct dundi_request *dr)
+{
+ struct dundi_transaction *trans;
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
+ dundi_discover(trans);
+ }
+ AST_LIST_UNLOCK(&peers);
+ return 0;
+}
+
+static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
+{
+ struct dundi_transaction *trans;
+
+ /* Mark all as "in thread" so they don't disappear */
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
+ if (trans->thread)
+ ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
+ trans->thread = 1;
+ }
+ AST_LIST_UNLOCK(&peers);
+
+ AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
+ if (!ast_test_flag(trans, FLAG_DEAD))
+ precache_trans(trans, maps, mapcount, expiration, foundanswers);
+ }
+
+ /* Cleanup any that got destroyed in the mean time */
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
+ trans->thread = 0;
+ if (ast_test_flag(trans, FLAG_DEAD)) {
+ ast_debug(1, "Our transaction went away!\n");
+ /* This is going to remove the transaction from the dundi_request's list, as well
+ * as the global transactions list */
+ destroy_trans(trans, 0);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&peers);
+
+ return 0;
+}
+
+static int query_transactions(struct dundi_request *dr)
+{
+ struct dundi_transaction *trans;
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
+ dundi_query(trans);
+ }
+ AST_LIST_UNLOCK(&peers);
+
+ return 0;
+}
+
+static int optimize_transactions(struct dundi_request *dr, int order)
+{
+ /* Minimize the message propagation through DUNDi by
+ alerting the network to hops which should be not be considered */
+ struct dundi_transaction *trans;
+ struct dundi_peer *peer;
+ dundi_eid tmp;
+ int x;
+ int needpush;
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
+ /* Pop off the true root */
+ if (trans->eidcount) {
+ tmp = trans->eids[--trans->eidcount];
+ needpush = 1;
+ } else {
+ tmp = trans->us_eid;
+ needpush = 0;
+ }
+
+ AST_LIST_TRAVERSE(&peers, peer, list) {
+ if (has_permission(&peer->include, dr->dcontext) &&
+ dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
+ (peer->order <= order)) {
+ /* For each other transaction, make sure we don't
+ ask this EID about the others if they're not
+ already in the list */
+ if (!dundi_eid_cmp(&tmp, &peer->eid))
+ x = -1;
+ else {
+ for (x=0;x<trans->eidcount;x++) {
+ if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
+ break;
+ }
+ }
+ if (x == trans->eidcount) {
+ /* Nope not in the list, if needed, add us at the end since we're the source */
+ if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
+ trans->eids[trans->eidcount++] = peer->eid;
+ /* Need to insert the real root (or us) at the bottom now as
+ a requirement now. */
+ needpush = 1;
+ }
+ }
+ }
+ }
+ /* If necessary, push the true root back on the end */
+ if (needpush)
+ trans->eids[trans->eidcount++] = tmp;
+ }
+ AST_LIST_UNLOCK(&peers);
+
+ return 0;
+}
+
+static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
+{
+ struct dundi_transaction *trans;
+ int x;
+ char eid_str[20];
+ char eid_str2[20];
+
+ /* Ignore if not registered */
+ if (!p->addr.sin_addr.s_addr)
+ return 0;
+ if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
+ return 0;
+
+ if (ast_strlen_zero(dr->number))
+ ast_debug(1, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
+ else
+ ast_debug(1, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
+
+ trans = create_transaction(p);
+ if (!trans)
+ return -1;
+ trans->parent = dr;
+ trans->ttl = ttl;
+ for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
+ trans->eids[x] = *avoid[x];
+ trans->eidcount = x;
+ AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
+
+ return 0;
+}
+
+static void cancel_request(struct dundi_request *dr)
+{
+ struct dundi_transaction *trans;
+
+ AST_LIST_LOCK(&peers);
+ while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
+ /* Orphan transaction from request */
+ trans->parent = NULL;
+ /* Send final cancel */
+ dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+ }
+ AST_LIST_UNLOCK(&peers);
+}
+
+static void abort_request(struct dundi_request *dr)
+{
+ struct dundi_transaction *trans;
+
+ AST_LIST_LOCK(&peers);
+ while ((trans = AST_LIST_FIRST(&dr->trans))) {
+ /* This will remove the transaction from the list */
+ destroy_trans(trans, 0);
+ }
+ AST_LIST_UNLOCK(&peers);
+}
+
+static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
+{
+ struct dundi_peer *p;
+ int x;
+ int res;
+ int pass;
+ int allowconnect;
+ char eid_str[20];
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&peers, p, list) {
+ if (modeselect == 1) {
+ /* Send the precache to push upstreams only! */
+ pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
+ allowconnect = 1;
+ } else {
+ /* Normal lookup / EID query */
+ pass = has_permission(&p->include, dr->dcontext);
+ allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
+ }
+ if (skip) {
+ if (!dundi_eid_cmp(skip, &p->eid))
+ pass = 0;
+ }
+ if (pass) {
+ if (p->order <= order) {
+ /* Check order first, then check cache, regardless of
+ omissions, this gets us more likely to not have an
+ affected answer. */
+ if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
+ res = 0;
+ /* Make sure we haven't already seen it and that it won't
+ affect our answer */
+ for (x=0;avoid[x];x++) {
+ if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
+ /* If not a direct connection, it affects our answer */
+ if (directs && !directs[x])
+ ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
+ break;
+ }
+ }
+ /* Make sure we can ask */
+ if (allowconnect) {
+ if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
+ /* Check for a matching or 0 cache entry */
+ append_transaction(dr, p, ttl, avoid);
+ } else {
+ ast_debug(1, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
+ }
+ }
+ }
+ *foundcache |= res;
+ } else if (!*skipped || (p->order < *skipped))
+ *skipped = p->order;
+ }
+ }
+ AST_LIST_UNLOCK(&peers);
+}
+
+static int register_request(struct dundi_request *dr, struct dundi_request **pending)
+{
+ struct dundi_request *cur;
+ int res=0;
+ char eid_str[20];
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&requests, cur, list) {
+ ast_debug(1, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
+ dr->dcontext, dr->number);
+ if (!strcasecmp(cur->dcontext, dr->dcontext) &&
+ !strcasecmp(cur->number, dr->number) &&
+ (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
+ ast_debug(1, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
+ cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
+ *pending = cur;
+ res = 1;
+ break;
+ }
+ }
+ if (!res) {
+ ast_debug(1, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
+ dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
+ /* Go ahead and link us in since nobody else is searching for this */
+ AST_LIST_INSERT_HEAD(&requests, dr, list);
+ *pending = NULL;
+ }
+ AST_LIST_UNLOCK(&peers);
+ return res;
+}
+
+static void unregister_request(struct dundi_request *dr)
+{
+ AST_LIST_LOCK(&peers);
+ AST_LIST_REMOVE(&requests, dr, list);
+ AST_LIST_UNLOCK(&peers);
+}
+
+static int check_request(struct dundi_request *dr)
+{
+ struct dundi_request *cur;
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&requests, cur, list) {
+ if (cur == dr)
+ break;
+ }
+ AST_LIST_UNLOCK(&peers);
+
+ return cur ? 1 : 0;
+}
+
+static unsigned long avoid_crc32(dundi_eid *avoid[])
+{
+ /* Idea is that we're calculating a checksum which is independent of
+ the order that the EID's are listed in */
+ unsigned long acrc32 = 0;
+ int x;
+ for (x=0;avoid[x];x++) {
+ /* Order doesn't matter */
+ if (avoid[x+1]) {
+ acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
+ }
+ }
+ return acrc32;
+}
+
+static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
+{
+ int res;
+ struct dundi_request dr, *pending;
+ dundi_eid *rooteid=NULL;
+ int x;
+ int ttlms;
+ int ms;
+ int foundcache;
+ int skipped=0;
+ int order=0;
+ char eid_str[20];
+ struct timeval start;
+
+ /* Don't do anthing for a hungup channel */
+ if (chan && ast_check_hangup(chan))
+ return 0;
+
+ ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
+
+ for (x=0;avoid[x];x++)
+ rooteid = avoid[x];
+ /* Now perform real check */
+ memset(&dr, 0, sizeof(dr));
+ if (pipe(dr.pfds)) {
+ ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
+ return -1;
+ }
+ dr.dr = result;
+ dr.hmd = hmd;
+ dr.maxcount = maxret;
+ dr.expiration = *expiration;
+ dr.cbypass = cbypass;
+ dr.crc32 = avoid_crc32(avoid);
+ ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
+ ast_copy_string(dr.number, number, sizeof(dr.number));
+ if (rooteid)
+ dr.root_eid = *rooteid;
+ res = register_request(&dr, &pending);
+ if (res) {
+ /* Already a request */
+ if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
+ /* This is on behalf of someone else. Go ahead and close this out since
+ they'll get their answer anyway. */
+ ast_debug(1, "Oooh, duplicate request for '%s@%s' for '%s'\n",
+ dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
+ close(dr.pfds[0]);
+ close(dr.pfds[1]);
+ return -2;
+ } else {
+ /* Wait for the cache to populate */
+ ast_debug(1, "Waiting for similar request for '%s@%s' for '%s'\n",
+ dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
+ start = ast_tvnow();
+ while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
+ /* XXX Would be nice to have a way to poll/select here XXX */
+ /* XXX this is a busy wait loop!!! */
+ usleep(1);
+ }
+ /* Continue on as normal, our cache should kick in */
+ }
+ }
+ /* Create transactions */
+ do {
+ order = skipped;
+ skipped = 0;
+ foundcache = 0;
+ build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
+ } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
+ /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
+ do this earlier because we didn't know if we were going to have transactions
+ or not. */
+ if (!ttl) {
+ ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
+ abort_request(&dr);
+ unregister_request(&dr);
+ close(dr.pfds[0]);
+ close(dr.pfds[1]);
+ return 0;
+ }
+
+ /* Optimize transactions */
+ optimize_transactions(&dr, order);
+ /* Actually perform transactions */
+ discover_transactions(&dr);
+ /* Wait for transaction to come back */
+ start = ast_tvnow();
+ while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
+ ms = 100;
+ ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
+ }
+ if (chan && ast_check_hangup(chan))
+ ast_debug(1, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
+ cancel_request(&dr);
+ unregister_request(&dr);
+ res = dr.respcount;
+ *expiration = dr.expiration;
+ close(dr.pfds[0]);
+ close(dr.pfds[1]);
+ return res;
+}
+
+int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
+{
+ struct dundi_hint_metadata hmd;
+ dundi_eid *avoid[1] = { NULL, };
+ int direct[1] = { 0, };
+ int expiration = dundi_cache_time;
+ memset(&hmd, 0, sizeof(hmd));
+ hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
+ return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
+}
+
+static void reschedule_precache(const char *number, const char *context, int expiration)
+{
+ int len;
+ struct dundi_precache_queue *qe, *prev;
+
+ AST_LIST_LOCK(&pcq);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
+ if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
+ AST_LIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ if (!qe) {
+ len = sizeof(*qe);
+ len += strlen(number) + 1;
+ len += strlen(context) + 1;
+ if (!(qe = ast_calloc(1, len))) {
+ AST_LIST_UNLOCK(&pcq);
+ return;
+ }
+ strcpy(qe->number, number);
+ qe->context = qe->number + strlen(number) + 1;
+ strcpy(qe->context, context);
+ }
+ time(&qe->expiration);
+ qe->expiration += expiration;
+ if ((prev = AST_LIST_FIRST(&pcq))) {
+ while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
+ prev = AST_LIST_NEXT(prev, list);
+ AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
+ } else
+ AST_LIST_INSERT_HEAD(&pcq, qe, list);
+ AST_LIST_UNLOCK(&pcq);
+}
+
+static void dundi_precache_full(void)
+{
+ struct dundi_mapping *cur;
+ struct ast_context *con;
+ struct ast_exten *e;
+
+ AST_LIST_TRAVERSE(&mappings, cur, list) {
+ ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
+ ast_rdlock_contexts();
+ con = NULL;
+ while ((con = ast_walk_contexts(con))) {
+ if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
+ continue;
+ /* Found the match, now queue them all up */
+ ast_rdlock_context(con);
+ e = NULL;
+ while ((e = ast_walk_context_extensions(con, e)))
+ reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
+ ast_unlock_context(con);
+ }
+ ast_unlock_contexts();
+ }
+}
+
+static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
+{
+ struct dundi_request dr;
+ struct dundi_hint_metadata hmd;
+ struct dundi_result dr2[MAX_RESULTS];
+ struct timeval start;
+ struct dundi_mapping *maps = NULL, *cur;
+ int nummaps = 0;
+ int foundanswers;
+ int foundcache, skipped, ttlms, ms;
+ if (!context)
+ context = "e164";
+ ast_debug(1, "Precache internal (%s@%s)!\n", number, context);
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&mappings, cur, list) {
+ if (!strcasecmp(cur->dcontext, context))
+ nummaps++;
+ }
+ if (nummaps) {
+ maps = alloca(nummaps * sizeof(*maps));
+ nummaps = 0;
+ if (maps) {
+ AST_LIST_TRAVERSE(&mappings, cur, list) {
+ if (!strcasecmp(cur->dcontext, context))
+ maps[nummaps++] = *cur;
+ }
+ }
+ }
+ AST_LIST_UNLOCK(&peers);
+ if (!nummaps || !maps)
+ return -1;
+ ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
+ memset(&dr2, 0, sizeof(dr2));
+ memset(&dr, 0, sizeof(dr));
+ memset(&hmd, 0, sizeof(hmd));
+ dr.dr = dr2;
+ ast_copy_string(dr.number, number, sizeof(dr.number));
+ ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
+ dr.maxcount = MAX_RESULTS;
+ dr.expiration = dundi_cache_time;
+ dr.hmd = &hmd;
+ dr.pfds[0] = dr.pfds[1] = -1;
+ pipe(dr.pfds);
+ build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
+ optimize_transactions(&dr, 0);
+ foundanswers = 0;
+ precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
+ if (foundanswers) {
+ if (dr.expiration > 0)
+ reschedule_precache(dr.number, dr.dcontext, dr.expiration);
+ else
+ ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
+ }
+ start = ast_tvnow();
+ while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
+ if (dr.pfds[0] > -1) {
+ ms = 100;
+ ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
+ } else
+ usleep(1);
+ }
+ cancel_request(&dr);
+ if (dr.pfds[0] > -1) {
+ close(dr.pfds[0]);
+ close(dr.pfds[1]);
+ }
+ return 0;
+}
+
+int dundi_precache(const char *context, const char *number)
+{
+ dundi_eid *avoid[1] = { NULL, };
+ return dundi_precache_internal(context, number, dundi_ttl, avoid);
+}
+
+static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[])
+{
+ int res;
+ struct dundi_request dr;
+ dundi_eid *rooteid=NULL;
+ int x;
+ int ttlms;
+ int skipped=0;
+ int foundcache=0;
+ struct timeval start;
+
+ ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
+
+ for (x=0;avoid[x];x++)
+ rooteid = avoid[x];
+ /* Now perform real check */
+ memset(&dr, 0, sizeof(dr));
+ dr.hmd = hmd;
+ dr.dei = dei;
+ dr.pfds[0] = dr.pfds[1] = -1;
+ ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
+ memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
+ if (rooteid)
+ dr.root_eid = *rooteid;
+ /* Create transactions */
+ build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
+
+ /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
+ do this earlier because we didn't know if we were going to have transactions
+ or not. */
+ if (!ttl) {
+ ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
+ return 0;
+ }
+
+ /* Optimize transactions */
+ optimize_transactions(&dr, 9999);
+ /* Actually perform transactions */
+ query_transactions(&dr);
+ /* Wait for transaction to come back */
+ start = ast_tvnow();
+ while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
+ usleep(1);
+ res = dr.respcount;
+ return res;
+}
+
+int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
+{
+ dundi_eid *avoid[1] = { NULL, };
+ struct dundi_hint_metadata hmd;
+ memset(&hmd, 0, sizeof(hmd));
+ return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
+}
+
+static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
+{
+ char *context;
+ char *opts;
+ int results;
+ int x;
+ int bypass = 0;
+ struct ast_module_user *u;
+ struct dundi_result dr[MAX_RESULTS];
+
+ buf[0] = '\0';
+
+ if (ast_strlen_zero(num)) {
+ ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
+ return -1;
+ }
+
+ u = ast_module_user_add(chan);
+
+ context = strchr(num, '|');
+ if (context) {
+ *context++ = '\0';
+ opts = strchr(context, '|');
+ if (opts) {
+ *opts++ = '\0';
+ if (strchr(opts, 'b'))
+ bypass = 1;
+ }
+ }
+
+ if (ast_strlen_zero(context))
+ context = "e164";
+
+ results = dundi_lookup(dr, MAX_RESULTS, NULL, context, num, bypass);
+ if (results > 0) {
+ sort_results(dr, results);
+ for (x = 0; x < results; x++) {
+ if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
+ snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
+ break;
+ }
+ }
+ }
+
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+/*! DUNDILOOKUP
+ * \ingroup functions
+*/
+
+static struct ast_custom_function dundi_function = {
+ .name = "DUNDILOOKUP",
+ .synopsis = "Do a DUNDi lookup of a phone number.",
+ .syntax = "DUNDILOOKUP(number[|context[|options]])",
+ .desc = "This will do a DUNDi lookup of the given phone number.\n"
+ "If no context is given, the default will be e164. The result of\n"
+ "this function will return the Technology/Resource found in the first result\n"
+ "in the DUNDi lookup. If no results were found, the result will be blank.\n"
+ "If the 'b' option is specified, the internal DUNDi cache will\n"
+ "be bypassed.\n",
+ .read = dundifunc_read,
+};
+
+enum {
+ OPT_BYPASS_CACHE = (1 << 0),
+};
+
+AST_APP_OPTIONS(dundi_query_opts, BEGIN_OPTIONS
+ AST_APP_OPTION('b', OPT_BYPASS_CACHE),
+END_OPTIONS );
+
+unsigned int dundi_result_id;
+
+struct dundi_result_datastore {
+ struct dundi_result results[MAX_RESULTS];
+ unsigned int num_results;
+ unsigned int id;
+};
+
+static void drds_destroy(struct dundi_result_datastore *drds)
+{
+ ast_free(drds);
+}
+
+static void drds_destroy_cb(void *data)
+{
+ struct dundi_result_datastore *drds = data;
+ drds_destroy(drds);
+}
+
+static const struct ast_datastore_info dundi_result_datastore_info = {
+ .type = "DUNDIQUERY",
+ .destroy = drds_destroy_cb,
+};
+
+static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_module_user *u;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(number);
+ AST_APP_ARG(context);
+ AST_APP_ARG(options);
+ );
+ struct ast_flags opts = { 0, };
+ char *parse;
+ struct dundi_result_datastore *drds;
+ struct ast_datastore *datastore;
+
+ u = ast_module_user_add(chan);
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
+
+ if (ast_strlen_zero(args.context))
+ args.context = "e164";
+
+ if (!(drds = ast_calloc(1, sizeof(*drds)))) {
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
+ snprintf(buf, len, "%u", drds->id);
+
+ if (!(datastore = ast_channel_datastore_alloc(&dundi_result_datastore_info, buf))) {
+ drds_destroy(drds);
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ datastore->data = drds;
+
+ drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
+ args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
+
+ if (drds->num_results > 0)
+ sort_results(drds->results, drds->num_results);
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+static struct ast_custom_function dundi_query_function = {
+ .name = "DUNDIQUERY",
+ .synopsis = "Initiate a DUNDi query.",
+ .syntax = "DUNDIQUERY(number[|context[|options]])",
+ .desc = "This will do a DUNDi lookup of the given phone number.\n"
+ "If no context is given, the default will be e164. The result of\n"
+ "this function will be a numeric ID that can be used to retrieve\n"
+ "the results with the DUNDIRESULT function. If the 'b' option is\n"
+ "is specified, the internal DUNDi cache will be bypassed.\n",
+ .read = dundi_query_read,
+};
+
+static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_module_user *u;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(id);
+ AST_APP_ARG(resultnum);
+ );
+ char *parse;
+ unsigned int num;
+ struct dundi_result_datastore *drds;
+ struct ast_datastore *datastore;
+ int res = -1;
+
+ u = ast_module_user_add(chan);
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
+ goto finish;
+ }
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
+ goto finish;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.id)) {
+ ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
+ goto finish;
+ }
+
+ if (ast_strlen_zero(args.resultnum)) {
+ ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
+ goto finish;
+ }
+
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &dundi_result_datastore_info, args.id);
+ ast_channel_unlock(chan);
+
+ if (!datastore) {
+ ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
+ goto finish;
+ }
+
+ drds = datastore->data;
+
+ if (!strcasecmp(args.resultnum, "getnum")) {
+ snprintf(buf, len, "%u", drds->num_results);
+ res = 0;
+ goto finish;
+ }
+
+ if (sscanf(args.resultnum, "%u", &num) != 1) {
+ ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
+ args.resultnum);
+ goto finish;
+ }
+
+ if (num && num <= drds->num_results) {
+ snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
+ res = 0;
+ } else
+ ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
+
+finish:
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static struct ast_custom_function dundi_result_function = {
+ .name = "DUNDIRESULT",
+ .synopsis = "Retrieve results from a DUNDIQUERY",
+ .syntax = "DUNDIRESULT(id|resultnum)",
+ .desc = "This function will retrieve results from a previous use\n"
+ "of the DUNDIQUERY function.\n"
+ " id - This argument is the identifier returned by the DUNDIQUERY function.\n"
+ " resultnum - This is the number of the result that you want to retrieve.\n"
+ " Results start at 1. If this argument is specified as \"getnum\",\n"
+ " then it will return the total number of results that are available.\n",
+ .read = dundi_result_read,
+};
+
+static void mark_peers(void)
+{
+ struct dundi_peer *peer;
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&peers, peer, list) {
+ peer->dead = 1;
+ }
+ AST_LIST_UNLOCK(&peers);
+}
+
+static void mark_mappings(void)
+{
+ struct dundi_mapping *map;
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&mappings, map, list) {
+ map->dead = 1;
+ }
+ AST_LIST_UNLOCK(&peers);
+}
+
+static void destroy_permissions(struct permissionlist *permlist)
+{
+ struct permission *perm;
+
+ while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
+ ast_free(perm);
+}
+
+static void destroy_peer(struct dundi_peer *peer)
+{
+ if (peer->registerid > -1)
+ ast_sched_del(sched, peer->registerid);
+ if (peer->regtrans)
+ destroy_trans(peer->regtrans, 0);
+ if (peer->qualifyid > -1)
+ ast_sched_del(sched, peer->qualifyid);
+ destroy_permissions(&peer->permit);
+ destroy_permissions(&peer->include);
+ ast_free(peer);
+}
+
+static void destroy_map(struct dundi_mapping *map)
+{
+ if (map->weightstr)
+ ast_free(map->weightstr);
+ ast_free(map);
+}
+
+static void prune_peers(void)
+{
+ struct dundi_peer *peer;
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
+ if (peer->dead) {
+ AST_LIST_REMOVE_CURRENT(list);
+ destroy_peer(peer);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&peers);
+}
+
+static void prune_mappings(void)
+{
+ struct dundi_mapping *map;
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
+ if (map->dead) {
+ AST_LIST_REMOVE_CURRENT(list);
+ destroy_map(map);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&peers);
+}
+
+static void append_permission(struct permissionlist *permlist, const char *s, int allow)
+{
+ struct permission *perm;
+
+ if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
+ return;
+
+ strcpy(perm->name, s);
+ perm->allow = allow;
+
+ AST_LIST_INSERT_TAIL(permlist, perm, list);
+}
+
+#define MAX_OPTS 128
+
+static void build_mapping(const char *name, const char *value)
+{
+ char *t, *fields[MAX_OPTS];
+ struct dundi_mapping *map;
+ int x;
+ int y;
+
+ t = ast_strdupa(value);
+
+ AST_LIST_TRAVERSE(&mappings, map, list) {
+ /* Find a double match */
+ if (!strcasecmp(map->dcontext, name) &&
+ (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
+ (!value[strlen(map->lcontext)] ||
+ (value[strlen(map->lcontext)] == ','))))
+ break;
+ }
+ if (!map) {
+ if (!(map = ast_calloc(1, sizeof(*map))))
+ return;
+ AST_LIST_INSERT_HEAD(&mappings, map, list);
+ map->dead = 1;
+ }
+ map->options = 0;
+ memset(fields, 0, sizeof(fields));
+ x = 0;
+ while (t && x < MAX_OPTS) {
+ fields[x++] = t;
+ t = strchr(t, ',');
+ if (t) {
+ *t = '\0';
+ t++;
+ }
+ } /* Russell was here, arrrr! */
+ if ((x == 1) && ast_strlen_zero(fields[0])) {
+ /* Placeholder mapping */
+ ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
+ map->dead = 0;
+ } else if (x >= 4) {
+ ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
+ ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
+ if ((sscanf(fields[1], "%d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
+ ast_copy_string(map->dest, fields[3], sizeof(map->dest));
+ if ((map->tech = str2tech(fields[2])))
+ map->dead = 0;
+ } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
+ map->weightstr = ast_strdup(fields[1]);
+ ast_copy_string(map->dest, fields[3], sizeof(map->dest));
+ if ((map->tech = str2tech(fields[2])))
+ map->dead = 0;
+ } else {
+ ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
+ }
+ for (y = 4;y < x; y++) {
+ if (!strcasecmp(fields[y], "nounsolicited"))
+ map->options |= DUNDI_FLAG_NOUNSOLICITED;
+ else if (!strcasecmp(fields[y], "nocomunsolicit"))
+ map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
+ else if (!strcasecmp(fields[y], "residential"))
+ map->options |= DUNDI_FLAG_RESIDENTIAL;
+ else if (!strcasecmp(fields[y], "commercial"))
+ map->options |= DUNDI_FLAG_COMMERCIAL;
+ else if (!strcasecmp(fields[y], "mobile"))
+ map->options |= DUNDI_FLAG_MOBILE;
+ else if (!strcasecmp(fields[y], "nopartial"))
+ map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
+ else
+ ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
+ }
+ } else
+ ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
+}
+
+/* \note Called with the peers list already locked */
+static int do_register(const void *data)
+{
+ struct dundi_ie_data ied;
+ struct dundi_peer *peer = (struct dundi_peer *)data;
+ char eid_str[20];
+ char eid_str2[20];
+ ast_debug(1, "Register us as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
+ peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
+ /* Destroy old transaction if there is one */
+ if (peer->regtrans)
+ destroy_trans(peer->regtrans, 0);
+ peer->regtrans = create_transaction(peer);
+ if (peer->regtrans) {
+ ast_set_flag(peer->regtrans, FLAG_ISREG);
+ memset(&ied, 0, sizeof(ied));
+ dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+ dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
+ dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
+ dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
+
+ } else
+ ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+
+ return 0;
+}
+
+static int do_qualify(const void *data)
+{
+ struct dundi_peer *peer = (struct dundi_peer *)data;
+ peer->qualifyid = -1;
+ qualify_peer(peer, 0);
+ return 0;
+}
+
+static void qualify_peer(struct dundi_peer *peer, int schedonly)
+{
+ int when;
+ if (peer->qualifyid > -1)
+ ast_sched_del(sched, peer->qualifyid);
+ peer->qualifyid = -1;
+ if (peer->qualtrans)
+ destroy_trans(peer->qualtrans, 0);
+ peer->qualtrans = NULL;
+ if (peer->maxms > 0) {
+ when = 60000;
+ if (peer->lastms < 0)
+ when = 10000;
+ if (schedonly)
+ when = 5000;
+ peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
+ if (!schedonly)
+ peer->qualtrans = create_transaction(peer);
+ if (peer->qualtrans) {
+ peer->qualtx = ast_tvnow();
+ ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
+ dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
+ }
+ }
+}
+static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
+{
+ char data[256];
+ char *c;
+ int port, expire;
+ char eid_str[20];
+ dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
+ if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
+ c = strchr(data, ':');
+ if (c) {
+ *c = '\0';
+ c++;
+ if (sscanf(c, "%d:%d", &port, &expire) == 2) {
+ /* Got it! */
+ inet_aton(data, &peer->addr.sin_addr);
+ peer->addr.sin_family = AF_INET;
+ peer->addr.sin_port = htons(port);
+ peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
+ }
+ }
+ }
+}
+
+
+static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
+{
+ struct dundi_peer *peer;
+ struct ast_hostent he;
+ struct hostent *hp;
+ dundi_eid testeid;
+ int needregister=0;
+ char eid_str[20];
+
+ AST_LIST_LOCK(&peers);
+ AST_LIST_TRAVERSE(&peers, peer, list) {
+ if (!dundi_eid_cmp(&peer->eid, eid)) {
+ break;
+ }
+ }
+ if (!peer) {
+ /* Add us into the list */
+ if (!(peer = ast_calloc(1, sizeof(*peer)))) {
+ AST_LIST_UNLOCK(&peers);
+ return;
+ }
+ peer->registerid = -1;
+ peer->registerexpire = -1;
+ peer->qualifyid = -1;
+ peer->addr.sin_family = AF_INET;
+ peer->addr.sin_port = htons(DUNDI_PORT);
+ populate_addr(peer, eid);
+ AST_LIST_INSERT_HEAD(&peers, peer, list);
+ }
+ peer->dead = 0;
+ peer->eid = *eid;
+ peer->us_eid = global_eid;
+ destroy_permissions(&peer->permit);
+ destroy_permissions(&peer->include);
+ if (peer->registerid > -1)
+ ast_sched_del(sched, peer->registerid);
+ peer->registerid = -1;
+ for (; v; v = v->next) {
+ if (!strcasecmp(v->name, "inkey")) {
+ ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
+ } else if (!strcasecmp(v->name, "outkey")) {
+ ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
+ } else if (!strcasecmp(v->name, "host")) {
+ if (!strcasecmp(v->value, "dynamic")) {
+ peer->dynamic = 1;
+ } else {
+ hp = ast_gethostbyname(v->value, &he);
+ if (hp) {
+ memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
+ peer->dynamic = 0;
+ } else {
+ ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
+ peer->dead = 1;
+ }
+ }
+ } else if (!strcasecmp(v->name, "ustothem")) {
+ if (!dundi_str_to_eid(&testeid, v->value))
+ peer->us_eid = testeid;
+ else
+ ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "include")) {
+ append_permission(&peer->include, v->value, 1);
+ } else if (!strcasecmp(v->name, "permit")) {
+ append_permission(&peer->permit, v->value, 1);
+ } else if (!strcasecmp(v->name, "noinclude")) {
+ append_permission(&peer->include, v->value, 0);
+ } else if (!strcasecmp(v->name, "deny")) {
+ append_permission(&peer->permit, v->value, 0);
+ } else if (!strcasecmp(v->name, "register")) {
+ needregister = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "order")) {
+ if (!strcasecmp(v->value, "primary"))
+ peer->order = 0;
+ else if (!strcasecmp(v->value, "secondary"))
+ peer->order = 1;
+ else if (!strcasecmp(v->value, "tertiary"))
+ peer->order = 2;
+ else if (!strcasecmp(v->value, "quartiary"))
+ peer->order = 3;
+ else {
+ ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "qualify")) {
+ if (!strcasecmp(v->value, "no")) {
+ peer->maxms = 0;
+ } else if (!strcasecmp(v->value, "yes")) {
+ peer->maxms = DEFAULT_MAXMS;
+ } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
+ ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
+ peer->maxms = 0;
+ }
+ } else if (!strcasecmp(v->name, "model")) {
+ if (!strcasecmp(v->value, "inbound"))
+ peer->model = DUNDI_MODEL_INBOUND;
+ else if (!strcasecmp(v->value, "outbound"))
+ peer->model = DUNDI_MODEL_OUTBOUND;
+ else if (!strcasecmp(v->value, "symmetric"))
+ peer->model = DUNDI_MODEL_SYMMETRIC;
+ else if (!strcasecmp(v->value, "none"))
+ peer->model = 0;
+ else {
+ ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
+ v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "precache")) {
+ if (!strcasecmp(v->value, "inbound"))
+ peer->pcmodel = DUNDI_MODEL_INBOUND;
+ else if (!strcasecmp(v->value, "outbound"))
+ peer->pcmodel = DUNDI_MODEL_OUTBOUND;
+ else if (!strcasecmp(v->value, "symmetric"))
+ peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
+ else if (!strcasecmp(v->value, "none"))
+ peer->pcmodel = 0;
+ else {
+ ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
+ v->value, v->lineno);
+ }
+ }
+ }
+ (*globalpcmode) |= peer->pcmodel;
+ if (!peer->model && !peer->pcmodel) {
+ ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ peer->dead = 1;
+ } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
+ ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ peer->dead = 1;
+ } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
+ ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ peer->dead = 1;
+ } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
+ ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
+ ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n",
+ dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+ } else {
+ if (needregister) {
+ peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
+ }
+ qualify_peer(peer, 1);
+ }
+ AST_LIST_UNLOCK(&peers);
+}
+
+static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
+{
+ struct dundi_result results[MAX_RESULTS];
+ int res;
+ int x;
+ int found = 0;
+ if (!strncasecmp(context, "macro-", 6)) {
+ if (!chan) {
+ ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
+ return -1;
+ }
+ /* If done as a macro, use macro extension */
+ if (!strcasecmp(exten, "s")) {
+ exten = pbx_builtin_getvar_helper(chan, "ARG1");
+ if (ast_strlen_zero(exten))
+ exten = chan->macroexten;
+ if (ast_strlen_zero(exten))
+ exten = chan->exten;
+ if (ast_strlen_zero(exten)) {
+ ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
+ return -1;
+ }
+ }
+ if (ast_strlen_zero(data))
+ data = "e164";
+ } else {
+ if (ast_strlen_zero(data))
+ data = context;
+ }
+ res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
+ for (x=0;x<res;x++) {
+ if (ast_test_flag(results + x, flag))
+ found++;
+ }
+ if (found >= priority)
+ return 1;
+ return 0;
+}
+
+static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
+}
+
+static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
+}
+
+static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ struct dundi_result results[MAX_RESULTS];
+ int res;
+ int x=0;
+ char req[1024];
+ const char *dundiargs;
+ struct ast_app *dial;
+
+ if (!strncasecmp(context, "macro-", 6)) {
+ if (!chan) {
+ ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
+ return -1;
+ }
+ /* If done as a macro, use macro extension */
+ if (!strcasecmp(exten, "s")) {
+ exten = pbx_builtin_getvar_helper(chan, "ARG1");
+ if (ast_strlen_zero(exten))
+ exten = chan->macroexten;
+ if (ast_strlen_zero(exten))
+ exten = chan->exten;
+ if (ast_strlen_zero(exten)) {
+ ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
+ return -1;
+ }
+ }
+ if (ast_strlen_zero(data))
+ data = "e164";
+ } else {
+ if (ast_strlen_zero(data))
+ data = context;
+ }
+ res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
+ if (res > 0) {
+ sort_results(results, res);
+ for (x=0;x<res;x++) {
+ if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
+ if (!--priority)
+ break;
+ }
+ }
+ }
+ if (x < res) {
+ /* Got a hit! */
+ dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
+ snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest,
+ S_OR(dundiargs, ""));
+ dial = pbx_findapp("Dial");
+ if (dial)
+ res = pbx_exec(chan, dial, req);
+ } else
+ res = -1;
+ return res;
+}
+
+static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
+}
+
+static struct ast_switch dundi_switch =
+{
+ name: "DUNDi",
+ description: "DUNDi Discovered Dialplan Switch",
+ exists: dundi_exists,
+ canmatch: dundi_canmatch,
+ exec: dundi_exec,
+ matchmore: dundi_matchmore,
+};
+
+static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ char *cat;
+ int x;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ char hn[MAXHOSTNAMELEN] = "";
+ struct ast_hostent he;
+ struct hostent *hp;
+ struct sockaddr_in sin2;
+ static int last_port = 0;
+ int globalpcmodel = 0;
+ dundi_eid testeid;
+
+ if (!(cfg = ast_config_load(config_file, config_flags))) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ dundi_ttl = DUNDI_DEFAULT_TTL;
+ dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
+ any_peer = NULL;
+
+ ipaddr[0] = '\0';
+ if (!gethostname(hn, sizeof(hn)-1)) {
+ hp = ast_gethostbyname(hn, &he);
+ if (hp) {
+ memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
+ ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
+ } else
+ ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
+ } else
+ ast_log(LOG_WARNING, "Unable to get host name!\n");
+ AST_LIST_LOCK(&peers);
+ reset_global_eid();
+ global_storehistory = 0;
+ ast_copy_string(secretpath, "dundi", sizeof(secretpath));
+ v = ast_variable_browse(cfg, "general");
+ while(v) {
+ if (!strcasecmp(v->name, "port")){
+ sin->sin_port = ntohs(atoi(v->value));
+ if(last_port==0){
+ last_port=sin->sin_port;
+ } else if(sin->sin_port != last_port)
+ ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
+ } else if (!strcasecmp(v->name, "bindaddr")) {
+ struct hostent *hp;
+ struct ast_hostent he;
+ hp = ast_gethostbyname(v->value, &he);
+ if (hp) {
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
+ } else
+ ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
+ } else if (!strcasecmp(v->name, "authdebug")) {
+ authdebug = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "ttl")) {
+ if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
+ dundi_ttl = x;
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
+ v->value, v->lineno, DUNDI_DEFAULT_TTL);
+ }
+ } else if (!strcasecmp(v->name, "autokill")) {
+ if (sscanf(v->value, "%d", &x) == 1) {
+ if (x >= 0)
+ global_autokilltimeout = x;
+ else
+ ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
+ } else if (ast_true(v->value)) {
+ global_autokilltimeout = DEFAULT_MAXMS;
+ } else {
+ global_autokilltimeout = 0;
+ }
+ } else if (!strcasecmp(v->name, "entityid")) {
+ if (!dundi_str_to_eid(&testeid, v->value))
+ global_eid = testeid;
+ else
+ ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "tos")) {
+ if (ast_str2tos(v->value, &tos))
+ ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
+ } else if (!strcasecmp(v->name, "department")) {
+ ast_copy_string(dept, v->value, sizeof(dept));
+ } else if (!strcasecmp(v->name, "organization")) {
+ ast_copy_string(org, v->value, sizeof(org));
+ } else if (!strcasecmp(v->name, "locality")) {
+ ast_copy_string(locality, v->value, sizeof(locality));
+ } else if (!strcasecmp(v->name, "stateprov")) {
+ ast_copy_string(stateprov, v->value, sizeof(stateprov));
+ } else if (!strcasecmp(v->name, "country")) {
+ ast_copy_string(country, v->value, sizeof(country));
+ } else if (!strcasecmp(v->name, "email")) {
+ ast_copy_string(email, v->value, sizeof(email));
+ } else if (!strcasecmp(v->name, "phone")) {
+ ast_copy_string(phone, v->value, sizeof(phone));
+ } else if (!strcasecmp(v->name, "storehistory")) {
+ global_storehistory = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "cachetime")) {
+ if ((sscanf(v->value, "%d", &x) == 1)) {
+ dundi_cache_time = x;
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
+ v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
+ }
+ }
+ v = v->next;
+ }
+ AST_LIST_UNLOCK(&peers);
+ mark_mappings();
+ v = ast_variable_browse(cfg, "mappings");
+ while(v) {
+ build_mapping(v->name, v->value);
+ v = v->next;
+ }
+ prune_mappings();
+ mark_peers();
+ cat = ast_category_browse(cfg, NULL);
+ while(cat) {
+ if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
+ /* Entries */
+ if (!dundi_str_to_eid(&testeid, cat))
+ build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
+ else if (!strcasecmp(cat, "*")) {
+ build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
+ any_peer = find_peer(NULL);
+ } else
+ ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ prune_peers();
+ ast_config_destroy(cfg);
+ load_password();
+ if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
+ dundi_precache_full();
+ return 0;
+}
+
+static int unload_module(void)
+{
+ pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid;
+ ast_module_user_hangup_all();
+
+ /* Stop all currently running threads */
+ dundi_shutdown = 1;
+ if (previous_netthreadid != AST_PTHREADT_NULL) {
+ pthread_kill(previous_netthreadid, SIGURG);
+ pthread_join(previous_netthreadid, NULL);
+ }
+ if (previous_precachethreadid != AST_PTHREADT_NULL) {
+ pthread_kill(previous_precachethreadid, SIGURG);
+ pthread_join(previous_precachethreadid, NULL);
+ }
+
+ ast_cli_unregister_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
+ ast_unregister_switch(&dundi_switch);
+ ast_custom_function_unregister(&dundi_function);
+ ast_custom_function_unregister(&dundi_query_function);
+ ast_custom_function_unregister(&dundi_result_function);
+ close(netsocket);
+ io_context_destroy(io);
+ sched_context_destroy(sched);
+
+ mark_mappings();
+ prune_mappings();
+ mark_peers();
+ prune_peers();
+
+ return 0;
+}
+
+static int reload(void)
+{
+ struct sockaddr_in sin;
+
+ if (set_config("dundi.conf", &sin, 1))
+ return AST_MODULE_LOAD_FAILURE;
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int load_module(void)
+{
+ struct sockaddr_in sin;
+
+ dundi_set_output(dundi_debug_output);
+ dundi_set_error(dundi_error_output);
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ntohs(DUNDI_PORT);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ /* Make a UDP socket */
+ io = io_context_create();
+ sched = sched_context_create();
+
+ if (!io || !sched)
+ return AST_MODULE_LOAD_FAILURE;
+
+ if (set_config("dundi.conf", &sin, 0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (netsocket < 0) {
+ ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ if (bind(netsocket, (struct sockaddr *) &sin, sizeof(sin))) {
+ ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ ast_netsock_set_qos(netsocket, tos, 0, "DUNDi");
+
+ if (start_network_thread()) {
+ ast_log(LOG_ERROR, "Unable to start network thread\n");
+ close(netsocket);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ ast_cli_register_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(*cli_dundi));
+ if (ast_register_switch(&dundi_switch))
+ ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
+ ast_custom_function_register(&dundi_function);
+ ast_custom_function_register(&dundi_query_function);
+ ast_custom_function_register(&dundi_result_function);
+
+ ast_verb(2, "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/trunk/pbx/pbx_gtkconsole.c b/trunk/pbx/pbx_gtkconsole.c
new file mode 100644
index 000000000..5e47ac668
--- /dev/null
+++ b/trunk/pbx/pbx_gtkconsole.c
@@ -0,0 +1,502 @@
+/*
+ * 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 GTK Console monitor -- very kludgy right now
+ *
+ */
+
+/*** MODULEINFO
+ <depend>gtk</depend>
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/paths.h"
+#include "asterisk/term.h"
+
+AST_MUTEX_DEFINE_STATIC(verb_lock);
+
+static pthread_t console_thread;
+
+static int inuse=0;
+static int clipipe[2];
+static int cleanupid = -1;
+
+static GtkWidget *window;
+static GtkWidget *quit;
+static GtkWidget *closew;
+static GtkWidget *verb;
+static GtkWidget *modules;
+static GtkWidget *statusbar;
+static GtkWidget *cli;
+
+static struct timeval last;
+
+static void update_statusbar(char *msg)
+{
+ gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 1);
+ gtk_statusbar_push(GTK_STATUSBAR(statusbar), 1, msg);
+}
+
+static int unload_module(void)
+{
+ if (inuse) {
+ /* Kill off the main thread */
+ pthread_cancel(console_thread);
+ gdk_threads_enter();
+ gtk_widget_destroy(window);
+ gdk_threads_leave();
+ close(clipipe[0]);
+ close(clipipe[1]);
+ }
+ return 0;
+}
+
+static int cleanup(void *useless)
+{
+ gdk_threads_enter();
+ gtk_clist_thaw(GTK_CLIST(verb));
+ gtk_widget_queue_resize(verb->parent);
+ gtk_clist_moveto(GTK_CLIST(verb), GTK_CLIST(verb)->rows - 1, 0, 0, 0);
+ cleanupid = -1;
+ gdk_threads_leave();
+ return 0;
+}
+
+
+static void __verboser(const char *_stuff)
+{
+ char *s2[2];
+ struct timeval tv;
+ int ms;
+ char *stuff;
+
+ stuff = ast_strdupa(_stuff);
+ term_strip(stuff, stuff, strlen(stuff) + 1);
+
+ s2[0] = (char *)stuff;
+ s2[1] = NULL;
+ gtk_clist_freeze(GTK_CLIST(verb));
+ gtk_clist_append(GTK_CLIST(verb), s2);
+ if (!ast_tvzero(last)) {
+ gdk_threads_leave();
+ gettimeofday(&tv, NULL);
+ if (cleanupid > -1)
+ gtk_timeout_remove(cleanupid);
+ ms = ast_tvdiff_ms(tv, last);
+ if (ms < 100) {
+ /* We just got a message within 100ms, so just schedule an update
+ in the near future */
+ cleanupid = gtk_timeout_add(200, cleanup, NULL);
+ } else {
+ cleanup(&cleanupid);
+ }
+ last = tv;
+ } else {
+ gettimeofday(&last, NULL);
+ }
+}
+
+static void verboser(const char *stuff)
+{
+ ast_mutex_lock(&verb_lock);
+ /* Lock appropriately if we're really being called in verbose mode */
+ __verboser(stuff);
+ ast_mutex_unlock(&verb_lock);
+}
+
+static void cliinput(void *data, int source, GdkInputCondition ic)
+{
+ static char buf[256];
+ static int offset = 0;
+ int res;
+ char *c;
+ char *l;
+ char n;
+ /* Read as much stuff is there */
+ res = read(source, buf + offset, sizeof(buf) - 1 - offset);
+ if (res > -1)
+ buf[res + offset] = '\0';
+ /* make sure we've null terminated whatever we have so far */
+ c = buf;
+ l = buf;
+ while(*c) {
+ if (*c == '\n') {
+ /* Keep the trailing \n */
+ c++;
+ n = *c;
+ *c = '\0';
+ __verboser(l);
+ *(c - 1) = '\0';
+ *c = n;
+ l = c;
+ } else
+ c++;
+ }
+ if (strlen(l)) {
+ /* We have some left over */
+ memmove(buf, l, strlen(l) + 1);
+ offset = strlen(buf);
+ } else {
+ offset = 0;
+ }
+
+}
+
+static void remove_module(void)
+{
+ int res;
+ char *module;
+ char buf[256];
+ if (GTK_CLIST(modules)->selection) {
+ module = (char *) gtk_clist_get_row_data(GTK_CLIST(modules), (long) GTK_CLIST(modules)->selection->data);
+ gdk_threads_leave();
+ res = ast_unload_resource(module, 0);
+ gdk_threads_enter();
+ if (res) {
+ snprintf(buf, sizeof(buf), "Module '%s' is in use", module);
+ update_statusbar(buf);
+ } else {
+ snprintf(buf, sizeof(buf), "Module '%s' removed", module);
+ update_statusbar(buf);
+ }
+ }
+}
+
+static int reload(void)
+{
+ int res, x;
+ char *module;
+ char buf[256];
+ if (GTK_CLIST(modules)->selection) {
+ module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (long) GTK_CLIST(modules)->selection->data);
+ module = strdup(module);
+ if (module) {
+ gdk_threads_leave();
+ res = ast_unload_resource(module, 0);
+ gdk_threads_enter();
+ if (res) {
+ snprintf(buf, sizeof(buf), "Module '%s' is in use", module);
+ update_statusbar(buf);
+ } else {
+ gdk_threads_leave();
+ res = ast_load_resource(module);
+ gdk_threads_enter();
+ if (res) {
+ snprintf(buf, sizeof(buf), "Error reloading module '%s'", module);
+ } else {
+ snprintf(buf, sizeof(buf), "Module '%s' reloaded", module);
+ }
+ for (x=0; x < GTK_CLIST(modules)->rows; x++) {
+ if (!strcmp((char *)gtk_clist_get_row_data(GTK_CLIST(modules), x), module)) {
+ gtk_clist_select_row(GTK_CLIST(modules), x, -1);
+ break;
+ }
+ }
+ update_statusbar(buf);
+
+ }
+ free(module);
+ }
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static void file_ok_sel(GtkWidget *w, GtkFileSelection *fs)
+{
+ char tmp[PATH_MAX];
+ char *module = gtk_file_selection_get_filename(fs);
+ char buf[256];
+ snprintf(tmp, sizeof(tmp), "%s/", ast_config_AST_MODULE_DIR);
+ if (!strncmp(module, (char *)tmp, strlen(tmp)))
+ module += strlen(tmp);
+ gdk_threads_leave();
+ if (ast_load_resource(module)) {
+ snprintf(buf, sizeof(buf), "Error loading module '%s'.", module);
+ update_statusbar(buf);
+ } else {
+ snprintf(buf, sizeof(buf), "Module '%s' loaded", module);
+ update_statusbar(buf);
+ }
+ gdk_threads_enter();
+ gtk_widget_destroy(GTK_WIDGET(fs));
+}
+
+static void add_module(void)
+{
+ char tmp[PATH_MAX];
+ GtkWidget *filew;
+ snprintf(tmp, sizeof(tmp), "%s/*.so", ast_config_AST_MODULE_DIR);
+ filew = gtk_file_selection_new("Load Module");
+ gtk_signal_connect(GTK_OBJECT (GTK_FILE_SELECTION(filew)->ok_button),
+ "clicked", GTK_SIGNAL_FUNC(file_ok_sel), filew);
+ gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION(filew)->cancel_button),
+ "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(filew));
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(filew), (char *)tmp);
+ gtk_widget_show(filew);
+}
+
+static int add_mod(const char *module, const char *description, int usecount, const char *like)
+{
+ char use[10];
+ const char *pass[4];
+ int row;
+ snprintf(use, sizeof(use), "%d", usecount);
+ pass[0] = module;
+ pass[1] = description;
+ pass[2] = use;
+ pass[3] = NULL;
+ row = gtk_clist_append(GTK_CLIST(modules), (char **) pass);
+ gtk_clist_set_row_data(GTK_CLIST(modules), row, (char *) module);
+ return 0;
+}
+
+static int mod_update(void)
+{
+ char *module= NULL;
+ /* Update the mod stuff */
+ if (GTK_CLIST(modules)->selection) {
+ module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (long) GTK_CLIST(modules)->selection->data);
+ }
+ gtk_clist_freeze(GTK_CLIST(modules));
+ gtk_clist_clear(GTK_CLIST(modules));
+ ast_update_module_list(add_mod, NULL);
+ if (module)
+ gtk_clist_select_row(GTK_CLIST(modules), gtk_clist_find_row_from_data(GTK_CLIST(modules), module), -1);
+ gtk_clist_thaw(GTK_CLIST(modules));
+ return 1;
+}
+
+static void exit_now(GtkWidget *widget, gpointer data)
+{
+ ast_loader_unregister(mod_update);
+ gtk_main_quit();
+ inuse--;
+ ast_update_use_count();
+ ast_unregister_verbose(verboser);
+ ast_unload_resource("pbx_gtkconsole", 0);
+ ast_verb(2, "GTK Console Monitor Exiting\n");
+ /* XXX Trying to quit after calling this makes asterisk segfault XXX */
+}
+
+static void exit_completely(GtkWidget *widget, gpointer data)
+{
+#if 0
+ /* Clever... */
+ ast_cli_command(clipipe[1], "quit");
+#else
+ kill(getpid(), SIGTERM);
+#endif
+}
+
+static void exit_nicely(GtkWidget *widget, gpointer data)
+{
+ fflush(stdout);
+ gtk_widget_destroy(window);
+}
+
+static void *consolethread(void *data)
+{
+ gtk_widget_show(window);
+ gdk_threads_enter();
+ gtk_main();
+ gdk_threads_leave();
+ return NULL;
+}
+
+static int cli_activate(void)
+{
+ char buf[256] = "";
+ strncpy(buf, gtk_entry_get_text(GTK_ENTRY(cli)), sizeof(buf) - 1);
+ gtk_entry_set_text(GTK_ENTRY(cli), "");
+ if (strlen(buf)) {
+ ast_cli_command(clipipe[1], buf);
+ }
+ return TRUE;
+}
+
+static int show_console(void)
+{
+ GtkWidget *hbox;
+ GtkWidget *wbox;
+ GtkWidget *notebook;
+ GtkWidget *sw;
+ GtkWidget *bbox, *hbbox, *add, *removew, *reloadw;
+ char *modtitles[3] = { "Module", "Description", "Use Count" };
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ statusbar = gtk_statusbar_new();
+ gtk_widget_show(statusbar);
+
+ gtk_signal_connect(GTK_OBJECT(window), "delete_event",
+ GTK_SIGNAL_FUNC (exit_nicely), window);
+ gtk_signal_connect(GTK_OBJECT(window), "destroy",
+ GTK_SIGNAL_FUNC (exit_now), window);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+
+ quit = gtk_button_new_with_label("Quit Asterisk");
+ gtk_signal_connect(GTK_OBJECT(quit), "clicked",
+ GTK_SIGNAL_FUNC (exit_completely), window);
+ gtk_widget_show(quit);
+
+ closew = gtk_button_new_with_label("Close Window");
+ gtk_signal_connect(GTK_OBJECT(closew), "clicked",
+ GTK_SIGNAL_FUNC (exit_nicely), window);
+ gtk_widget_show(closew);
+
+ notebook = gtk_notebook_new();
+ verb = gtk_clist_new(1);
+ gtk_clist_columns_autosize(GTK_CLIST(verb));
+ sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_container_add(GTK_CONTAINER(sw), verb);
+ gtk_widget_show(verb);
+ gtk_widget_show(sw);
+ gtk_widget_set_usize(verb, 640, 400);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, gtk_label_new("Verbose Status"));
+
+
+ modules = gtk_clist_new_with_titles(3, modtitles);
+ gtk_clist_columns_autosize(GTK_CLIST(modules));
+ gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 0, TRUE);
+ gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 1, TRUE);
+ gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 2, TRUE);
+ gtk_clist_set_sort_column(GTK_CLIST(modules), 0);
+ gtk_clist_set_auto_sort(GTK_CLIST(modules), TRUE);
+ gtk_clist_column_titles_passive(GTK_CLIST(modules));
+ sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_container_add(GTK_CONTAINER(sw), modules);
+ gtk_clist_set_selection_mode(GTK_CLIST(modules), GTK_SELECTION_BROWSE);
+ gtk_widget_show(modules);
+ gtk_widget_show(sw);
+
+ add = gtk_button_new_with_label("Load...");
+ gtk_widget_show(add);
+ removew = gtk_button_new_with_label("Unload");
+ gtk_widget_show(removew);
+ reloadw = gtk_button_new_with_label("Reload");
+ gtk_widget_show(reloadw);
+ gtk_signal_connect(GTK_OBJECT(removew), "clicked",
+ GTK_SIGNAL_FUNC (remove_module), window);
+ gtk_signal_connect(GTK_OBJECT(add), "clicked",
+ GTK_SIGNAL_FUNC (add_module), window);
+ gtk_signal_connect(GTK_OBJECT(reloadw), "clicked",
+ GTK_SIGNAL_FUNC (reload), window);
+
+ bbox = gtk_vbox_new(FALSE, 5);
+ gtk_widget_show(bbox);
+
+ gtk_widget_set_usize(bbox, 100, -1);
+ gtk_box_pack_start(GTK_BOX(bbox), add, FALSE, FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(bbox), removew, FALSE, FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(bbox), reloadw, FALSE, FALSE, 5);
+
+ hbbox = gtk_hbox_new(FALSE, 5);
+ gtk_widget_show(hbbox);
+
+ gtk_box_pack_start(GTK_BOX(hbbox), sw, TRUE, TRUE, 5);
+ gtk_box_pack_start(GTK_BOX(hbbox), bbox, FALSE, FALSE, 5);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbbox, gtk_label_new("Module Information"));
+
+ gtk_widget_show(notebook);
+
+ wbox = gtk_hbox_new(FALSE, 5);
+ gtk_widget_show(wbox);
+ gtk_box_pack_end(GTK_BOX(wbox), quit, FALSE, FALSE, 5);
+ gtk_box_pack_end(GTK_BOX(wbox), closew, FALSE, FALSE, 5);
+
+ hbox = gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(hbox);
+
+ /* Command line */
+ cli = gtk_entry_new();
+ gtk_widget_show(cli);
+
+ gtk_signal_connect(GTK_OBJECT(cli), "activate",
+ GTK_SIGNAL_FUNC (cli_activate), NULL);
+
+ gtk_box_pack_start(GTK_BOX(hbox), notebook, TRUE, TRUE, 5);
+ gtk_box_pack_start(GTK_BOX(hbox), wbox, FALSE, FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(hbox), cli, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), statusbar, FALSE, FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window), hbox);
+ gtk_window_set_title(GTK_WINDOW(window), "Asterisk Console");
+ gtk_widget_grab_focus(cli);
+ ast_pthread_create(&console_thread, NULL, consolethread, NULL);
+ /* XXX Okay, seriously fix me! XXX */
+ usleep(100000);
+ ast_register_verbose(verboser);
+ gtk_clist_freeze(GTK_CLIST(verb));
+ ast_loader_register(mod_update);
+ gtk_clist_thaw(GTK_CLIST(verb));
+ gdk_input_add(clipipe[0], GDK_INPUT_READ, cliinput, NULL);
+ mod_update();
+ update_statusbar("Asterisk Console Ready");
+ return 0;
+}
+
+
+static int load_module(void)
+{
+ if (pipe(clipipe)) {
+ ast_log(LOG_WARNING, "Unable to create CLI pipe\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ g_thread_init(NULL);
+ if (gtk_init_check(NULL, NULL)) {
+ if (!show_console()) {
+ inuse++;
+ ast_update_use_count();
+ ast_verb(2, "Launched GTK Console monitor\n");
+ } else
+ ast_log(LOG_WARNING, "Unable to start GTK console\n");
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Unable to start GTK console monitor -- ignoring\n");
+ else
+ ast_verb(2, "GTK is not available -- skipping monitor\n");
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "GTK Console",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/pbx/pbx_loopback.c b/trunk/pbx/pbx_loopback.c
new file mode 100644
index 000000000..e6f4ed904
--- /dev/null
+++ b/trunk/pbx/pbx_loopback.c
@@ -0,0 +1,176 @@
+/*
+ * 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 Loopback PBX Module
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/frame.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/md5.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/utils.h"
+#include "asterisk/crypto.h"
+#include "asterisk/astdb.h"
+
+
+/* Loopback switch substitutes ${EXTEN}, ${CONTEXT}, and ${PRIORITY} into
+ the data passed to it to try to get a string of the form:
+
+ [exten]@context[:priority][/extramatch]
+
+ Where exten, context, and priority are another extension, context, and priority
+ to lookup and "extramatch" is an extra match restriction the *original* number
+ must fit if specified. The "extramatch" begins with _ like an exten pattern
+ if it is specified. Note that the search context MUST be a different context
+ from the current context or the search will not succeed in an effort to reduce
+ the likelihood of loops (they're still possible if you try hard, so be careful!)
+
+*/
+
+
+#define LOOPBACK_COMMON \
+ char buf[1024]; \
+ int res; \
+ char *newexten=(char *)exten, *newcontext=(char *)context; \
+ int newpriority=priority; \
+ char *newpattern=NULL; \
+ loopback_helper(buf, sizeof(buf), exten, context, priority, data); \
+ loopback_subst(&newexten, &newcontext, &newpriority, &newpattern, buf); \
+ ast_log(LOG_DEBUG, "Parsed into %s @ %s priority %d\n", newexten, newcontext, newpriority); \
+ if (!strcasecmp(newcontext, context)) return -1
+
+
+static char *loopback_helper(char *buf, int buflen, const char *exten, const char *context, int priority, const char *data)
+{
+ struct ast_var_t *newvariable;
+ struct varshead headp;
+ char tmp[80];
+
+ snprintf(tmp, sizeof(tmp), "%d", priority);
+ AST_LIST_HEAD_INIT_NOLOCK(&headp);
+ AST_LIST_INSERT_HEAD(&headp, ast_var_assign("EXTEN", exten), entries);
+ AST_LIST_INSERT_HEAD(&headp, ast_var_assign("CONTEXT", context), entries);
+ AST_LIST_INSERT_HEAD(&headp, ast_var_assign("PRIORITY", tmp), entries);
+ /* Substitute variables */
+ pbx_substitute_variables_varshead(&headp, data, buf, buflen);
+ /* free the list */
+ while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
+ ast_var_delete(newvariable);
+ return buf;
+}
+
+static void loopback_subst(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf)
+{
+ char *con;
+ char *pri;
+ *newpattern = strchr(buf, '/');
+ if (*newpattern)
+ *(*newpattern)++ = '\0';
+ con = strchr(buf, '@');
+ if (con) {
+ *con++ = '\0';
+ pri = strchr(con, ':');
+ } else
+ pri = strchr(buf, ':');
+ if (!ast_strlen_zero(buf))
+ *newexten = buf;
+ if (!ast_strlen_zero(con))
+ *newcontext = con;
+ if (!ast_strlen_zero(pri))
+ sscanf(pri, "%d", priority);
+}
+
+static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ LOOPBACK_COMMON;
+ res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
+ if (newpattern && !ast_extension_match(newpattern, exten))
+ res = 0;
+ return res;
+}
+
+static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ LOOPBACK_COMMON;
+ res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
+ if (newpattern && !ast_extension_match(newpattern, exten))
+ res = 0;
+ return res;
+}
+
+static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int found;
+ LOOPBACK_COMMON;
+ res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid, &found, 0);
+ /* XXX hmmm... res is overridden ? */
+ if (newpattern && !ast_extension_match(newpattern, exten))
+ res = -1;
+ return res;
+}
+
+static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ LOOPBACK_COMMON;
+ res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
+ if (newpattern && !ast_extension_match(newpattern, exten))
+ res = 0;
+ return res;
+}
+
+static struct ast_switch loopback_switch =
+{
+ name: "Loopback",
+ description: "Loopback Dialplan Switch",
+ exists: loopback_exists,
+ canmatch: loopback_canmatch,
+ exec: loopback_exec,
+ matchmore: loopback_matchmore,
+};
+
+static int unload_module(void)
+{
+ ast_unregister_switch(&loopback_switch);
+ return 0;
+}
+
+static int load_module(void)
+{
+ if (ast_register_switch(&loopback_switch))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Loopback Switch");
diff --git a/trunk/pbx/pbx_lua.c b/trunk/pbx/pbx_lua.c
new file mode 100644
index 000000000..a23b31ee2
--- /dev/null
+++ b/trunk/pbx/pbx_lua.c
@@ -0,0 +1,1354 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Matthew Nicholson <mnicholson@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
+ *
+ * \author Matthew Nicholson <mnicholson@digium.com>
+ * \brief Lua PBX Switch
+ *
+ */
+
+/*** MODULEINFO
+ <depend>lua</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/term.h"
+#include "asterisk/paths.h"
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+static char *config = "extensions.lua";
+
+#define LUA_EXT_DATA_SIZE 256
+#define LUA_BUF_SIZE 4096
+
+static char *lua_read_extensions_file(lua_State *L, long *size);
+static int lua_load_extensions(lua_State *L, struct ast_channel *chan);
+static int lua_reload_extensions(lua_State *L);
+static void lua_free_extensions(void);
+static int lua_sort_extensions(lua_State *L);
+static int lua_extension_cmp(lua_State *L);
+static int lua_find_extension(lua_State *L, const char *context, const char *exten, int priority, ast_switch_f *func, int push_func);
+static int lua_pbx_findapp(lua_State *L);
+static int lua_pbx_exec(lua_State *L);
+
+static int lua_get_variable_value(lua_State *L);
+static int lua_set_variable_value(lua_State *L);
+static int lua_get_variable(lua_State *L);
+static int lua_set_variable(lua_State *L);
+static int lua_func_read(lua_State *L);
+
+static int lua_autoservice_start(lua_State *L);
+static int lua_autoservice_stop(lua_State *L);
+static int lua_autoservice_status(lua_State *L);
+static int lua_check_hangup(lua_State *L);
+
+static void lua_update_registry(lua_State *L, const char *context, const char *exten, int priority);
+static void lua_push_variable_table(lua_State *L, const char *name);
+static void lua_create_app_table(lua_State *L);
+static void lua_create_channel_table(lua_State *L);
+static void lua_create_variable_metatable(lua_State *L);
+static void lua_create_application_metatable(lua_State *L);
+static void lua_create_autoservice_functions(lua_State *L);
+static void lua_create_hangup_function(lua_State *L);
+
+void lua_state_destroy(void *data);
+static lua_State *lua_get_state(struct ast_channel *chan);
+
+static int exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
+static int canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
+static int matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
+static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data);
+
+AST_MUTEX_DEFINE_STATIC(config_file_lock);
+char *config_file_data = NULL;
+long config_file_size = 0;
+
+static const struct ast_datastore_info lua_datastore = {
+ .type = "lua",
+ .destroy = lua_state_destroy,
+};
+
+
+/*!
+ * \brief The destructor for lua_datastore
+ */
+void lua_state_destroy(void *data)
+{
+ if (data)
+ lua_close(data);
+}
+
+/*!
+ * \brief [lua_CFunction] Find an app and return it in a lua table (for access from lua, don't
+ * call directly)
+ *
+ * This function would be called in the following example as it would be found
+ * in extensions.lua.
+ *
+ * \code
+ * app.dial
+ * \endcode
+ */
+static int lua_pbx_findapp(lua_State *L)
+{
+ const char *app_name = luaL_checkstring(L, 2);
+
+ lua_newtable(L);
+
+ lua_pushstring(L, "name");
+ lua_pushstring(L, app_name);
+ lua_settable(L, -3);
+
+ luaL_getmetatable(L, "application");
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+/*!
+ * \brief [lua_CFunction] This function is part of the 'application' metatable
+ * and is used to execute applications similar to pbx_exec() (for access from
+ * lua, don't call directly)
+ *
+ * \param L the lua_State to use
+ * \return nothing
+ *
+ * This funciton is executed as the '()' operator for apps accessed through the
+ * 'app' table.
+ *
+ * \code
+ * app.playback('demo-congrats')
+ * \endcode
+ */
+static int lua_pbx_exec(lua_State *L)
+{
+ int res, nargs = lua_gettop(L);
+ char data[LUA_EXT_DATA_SIZE] = "";
+ char *data_next = data, *app_name;
+ char *context, *exten;
+ char tmp[80], tmp2[80], tmp3[LUA_EXT_DATA_SIZE];
+ int priority, autoservice;
+ size_t data_left = sizeof(data);
+ struct ast_app *app;
+ struct ast_channel *chan;
+
+ lua_getfield(L, 1, "name");
+ app_name = ast_strdupa(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ if (!(app = pbx_findapp(app_name))) {
+ lua_pushstring(L, "application '");
+ lua_pushstring(L, app_name);
+ lua_pushstring(L, "' not found");
+ lua_concat(L, 3);
+ return lua_error(L);
+ }
+
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "context");
+ context = ast_strdupa(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "exten");
+ exten = ast_strdupa(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "priority");
+ priority = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+
+ if (nargs > 1) {
+ int i;
+
+ if (!lua_isnil(L, 2))
+ ast_build_string(&data_next, &data_left, "%s", luaL_checkstring(L, 2));
+
+ for (i = 3; i <= nargs; i++) {
+ if (lua_isnil(L, i))
+ ast_build_string(&data_next, &data_left, ",");
+ else
+ ast_build_string(&data_next, &data_left, ",%s", luaL_checkstring(L, i));
+ }
+ }
+
+ ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n",
+ exten, context, priority,
+ term_color(tmp, app_name, COLOR_BRCYAN, 0, sizeof(tmp)),
+ term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
+ term_color(tmp3, data, COLOR_BRMAGENTA, 0, sizeof(tmp3)));
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+ autoservice = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ if (autoservice)
+ ast_autoservice_stop(chan);
+
+ res = pbx_exec(chan, app, data);
+
+ if (autoservice)
+ ast_autoservice_start(chan);
+
+ /* error executing an application, report it */
+ if (res) {
+ lua_pushinteger(L, res);
+ return lua_error(L);
+ }
+ return 0;
+}
+
+/*!
+ * \brief [lua_CFunction] Used to get the value of a variable or dialplan
+ * function (for access from lua, don't call directly)
+ *
+ * The value of the variable or function is returned. This function is the
+ * 'get()' function in the following example as would be seen in
+ * extensions.lua.
+ *
+ * \code
+ * channel.variable:get()
+ * \endcode
+ */
+static int lua_get_variable_value(lua_State *L)
+{
+ struct ast_channel *chan;
+ char *value = NULL, *name;
+ char *workspace = alloca(LUA_BUF_SIZE);
+ int autoservice;
+
+ workspace[0] = '\0';
+
+ if (!lua_istable(L, 1)) {
+ lua_pushstring(L, "User probably used '.' instead of ':' for retrieving a channel variable value");
+ return lua_error(L);
+ }
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "name");
+ name = ast_strdupa(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+ autoservice = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ if (autoservice)
+ ast_autoservice_stop(chan);
+
+ /* if this is a dialplan function then use ast_func_read(), otherwise
+ * use pbx_retrieve_variable() */
+ if (!ast_strlen_zero(name) && name[strlen(name) - 1] == ')') {
+ value = ast_func_read(chan, name, workspace, LUA_BUF_SIZE) ? NULL : workspace;
+ } else {
+ pbx_retrieve_variable(chan, name, &value, workspace, LUA_BUF_SIZE, &chan->varshead);
+ }
+
+ if (autoservice)
+ ast_autoservice_start(chan);
+
+ if (value) {
+ lua_pushstring(L, value);
+ } else {
+ lua_pushnil(L);
+ }
+
+ return 1;
+}
+
+/*!
+ * \brief [lua_CFunction] Used to set the value of a variable or dialplan
+ * function (for access from lua, don't call directly)
+ *
+ * This function is the 'set()' function in the following example as would be
+ * seen in extensions.lua.
+ *
+ * \code
+ * channel.variable:set("value")
+ * \endcode
+ */
+static int lua_set_variable_value(lua_State *L)
+{
+ const char *name, *value;
+ struct ast_channel *chan;
+ int autoservice;
+
+ if (!lua_istable(L, 1)) {
+ lua_pushstring(L, "User probably used '.' instead of ':' for setting a channel variable");
+ return lua_error(L);
+ }
+
+ lua_getfield(L, 1, "name");
+ name = ast_strdupa(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ value = luaL_checkstring(L, 2);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+ autoservice = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ if (autoservice)
+ ast_autoservice_stop(chan);
+
+ pbx_builtin_setvar_helper(chan, name, value);
+
+ if (autoservice)
+ ast_autoservice_start(chan);
+
+ return 0;
+}
+
+/*!
+ * \brief Update the lua registry with the given context, exten, and priority.
+ *
+ * \param L the lua_State to use
+ * \param context the new context
+ * \param exten the new exten
+ * \param priority the new priority
+ */
+static void lua_update_registry(lua_State *L, const char *context, const char *exten, int priority)
+{
+ lua_pushstring(L, context);
+ lua_setfield(L, LUA_REGISTRYINDEX, "context");
+
+ lua_pushstring(L, exten);
+ lua_setfield(L, LUA_REGISTRYINDEX, "exten");
+
+ lua_pushinteger(L, priority);
+ lua_setfield(L, LUA_REGISTRYINDEX, "priority");
+}
+
+/*!
+ * \brief Push a 'variable' table on the stack for access the channel variable
+ * with the given name.
+ *
+ * \param L the lua_State to use
+ * \param name the name of the variable
+ */
+static void lua_push_variable_table(lua_State *L, const char *name)
+{
+ lua_newtable(L);
+ luaL_getmetatable(L, "variable");
+ lua_setmetatable(L, -2);
+
+ lua_pushstring(L, name);
+ lua_setfield(L, -2, "name");
+
+ lua_pushcfunction(L, &lua_get_variable_value);
+ lua_setfield(L, -2, "get");
+
+ lua_pushcfunction(L, &lua_set_variable_value);
+ lua_setfield(L, -2, "set");
+}
+
+/*!
+ * \brief Create the global 'app' table for executing applications
+ *
+ * \param L the lua_State to use
+ */
+static void lua_create_app_table(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_newmetatable(L, "app");
+
+ lua_pushstring(L, "__index");
+ lua_pushcfunction(L, &lua_pbx_findapp);
+ lua_settable(L, -3);
+
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "app");
+}
+
+/*!
+ * \brief Create the global 'channel' table for accesing channel variables
+ *
+ * \param L the lua_State to use
+ */
+static void lua_create_channel_table(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_newmetatable(L, "channel_data");
+
+ lua_pushstring(L, "__index");
+ lua_pushcfunction(L, &lua_get_variable);
+ lua_settable(L, -3);
+
+ lua_pushstring(L, "__newindex");
+ lua_pushcfunction(L, &lua_set_variable);
+ lua_settable(L, -3);
+
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "channel");
+}
+
+/*!
+ * \brief Create the 'variable' metatable, used to retrieve channel variables
+ *
+ * \param L the lua_State to use
+ */
+static void lua_create_variable_metatable(lua_State *L)
+{
+ luaL_newmetatable(L, "variable");
+
+ lua_pushstring(L, "__call");
+ lua_pushcfunction(L, &lua_func_read);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+}
+
+/*!
+ * \brief Create the 'application' metatable, used to execute asterisk
+ * applications from lua
+ *
+ * \param L the lua_State to use
+ */
+static void lua_create_application_metatable(lua_State *L)
+{
+ luaL_newmetatable(L, "application");
+
+ lua_pushstring(L, "__call");
+ lua_pushcfunction(L, &lua_pbx_exec);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+}
+
+/*!
+ * \brief Create the autoservice functions
+ *
+ * \param L the lua_State to use
+ */
+static void lua_create_autoservice_functions(lua_State *L)
+{
+ lua_pushcfunction(L, &lua_autoservice_start);
+ lua_setglobal(L, "autoservice_start");
+
+ lua_pushcfunction(L, &lua_autoservice_stop);
+ lua_setglobal(L, "autoservice_stop");
+
+ lua_pushcfunction(L, &lua_autoservice_status);
+ lua_setglobal(L, "autoservice_status");
+
+ lua_pushboolean(L, 0);
+ lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
+}
+
+/*!
+ * \brief Create the hangup check function
+ *
+ * \param L the lua_State to use
+ */
+static void lua_create_hangup_function(lua_State *L)
+{
+ lua_pushcfunction(L, &lua_check_hangup);
+ lua_setglobal(L, "check_hangup");
+}
+
+/*!
+ * \brief [lua_CFunction] Return a lua 'variable' object (for access from lua, don't call
+ * directly)
+ *
+ * This function is called to lookup a variable construct a 'variable' object.
+ * It would be called in the following example as would be seen in
+ * extensions.lua.
+ *
+ * \code
+ * channel.variable
+ * \endcode
+ */
+static int lua_get_variable(lua_State *L)
+{
+ struct ast_channel *chan;
+ char *name = ast_strdupa(luaL_checkstring(L, 2));
+ char *value = NULL;
+ char *workspace = alloca(LUA_BUF_SIZE);
+ workspace[0] = '\0';
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ lua_push_variable_table(L, name);
+
+ /* if this is not a request for a dialplan funciton attempt to retrieve
+ * the value of the variable */
+ if (!ast_strlen_zero(name) && name[strlen(name) - 1] != ')') {
+ pbx_retrieve_variable(chan, name, &value, workspace, LUA_BUF_SIZE, &chan->varshead);
+ }
+
+ if (value) {
+ lua_pushstring(L, value);
+ lua_setfield(L, -2, "value");
+ }
+
+ return 1;
+}
+
+/*!
+ * \brief [lua_CFunction] Set the value of a channel variable or dialplan
+ * function (for access from lua, don't call directly)
+ *
+ * This function is called to set a variable or dialplan function. It would be
+ * called in the following example as would be seen in extensions.lua.
+ *
+ * \code
+ * channel.variable = "value"
+ * \endcode
+ */
+static int lua_set_variable(lua_State *L)
+{
+ struct ast_channel *chan;
+ int autoservice;
+ const char *name = luaL_checkstring(L, 2);
+ const char *value = luaL_checkstring(L, 3);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+ autoservice = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ if (autoservice)
+ ast_autoservice_stop(chan);
+
+ pbx_builtin_setvar_helper(chan, name, value);
+
+ if (autoservice)
+ ast_autoservice_start(chan);
+
+ return 0;
+}
+
+/*!
+ * \brief [lua_CFunction] Create a 'variable' object for accessing a dialplan
+ * function (for access from lua, don't call directly)
+ *
+ * This function is called to create a 'variable' object to access a dialplan
+ * function. It would be called in the following example as would be seen in
+ * extensions.lua.
+ *
+ * \code
+ * channel.func("arg1", "arg2", "arg3")
+ * \endcode
+ *
+ * To actually do anything with the resulting value you must use the 'get()'
+ * and 'set()' methods (the reason is the resulting value is not a value, but
+ * an object in the form of a lua table).
+ */
+static int lua_func_read(lua_State *L)
+{
+ int nargs = lua_gettop(L);
+ char fullname[LUA_EXT_DATA_SIZE] = "";
+ char *fullname_next = fullname, *name;
+ size_t fullname_left = sizeof(fullname);
+
+ lua_getfield(L, 1, "name");
+ name = ast_strdupa(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ ast_build_string(&fullname_next, &fullname_left, "%s(", name);
+
+ if (nargs > 1) {
+ int i;
+
+ if (!lua_isnil(L, 2))
+ ast_build_string(&fullname_next, &fullname_left, "%s", luaL_checkstring(L, 2));
+
+ for (i = 3; i <= nargs; i++) {
+ if (lua_isnil(L, i))
+ ast_build_string(&fullname_next, &fullname_left, ",");
+ else
+ ast_build_string(&fullname_next, &fullname_left, ",%s", luaL_checkstring(L, i));
+ }
+ }
+
+ ast_build_string(&fullname_next, &fullname_left, ")");
+
+ lua_push_variable_table(L, fullname);
+
+ return 1;
+}
+
+/*!
+ * \brief [lua_CFunction] Tell pbx_lua to maintain an autoservice on this
+ * channel (for access from lua, don't call directly)
+ *
+ * \param L the lua_State to use
+ *
+ * This function will set a flag that will cause pbx_lua to maintain an
+ * autoservice on this channel. The autoservice will automatically be stopped
+ * and restarted before calling applications and functions.
+ *
+ * \return This function returns the result of the ast_autoservice_start()
+ * function as a boolean to its lua caller.
+ */
+static int lua_autoservice_start(lua_State *L)
+{
+ struct ast_channel *chan;
+ int res;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ res = ast_autoservice_start(chan);
+
+ lua_pushboolean(L, !res);
+ lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
+
+ lua_pushboolean(L, !res);
+ return 1;
+}
+
+/*!
+ * \brief [lua_CFunction] Tell pbx_lua to stop maintaning an autoservice on
+ * this channel (for access from lua, don't call directly)
+ *
+ * \param L the lua_State to use
+ *
+ * This function will stop any autoservice running and turn off the autoservice
+ * flag. If this function returns false, it's probably because no autoservice
+ * was running to begin with.
+ *
+ * \return This function returns the result of the ast_autoservice_stop()
+ * function as a boolean to its lua caller.
+ */
+static int lua_autoservice_stop(lua_State *L)
+{
+ struct ast_channel *chan;
+ int res;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ res = ast_autoservice_stop(chan);
+
+ lua_pushboolean(L, 0);
+ lua_setfield(L, LUA_REGISTRYINDEX, "autoservice");
+
+ lua_pushboolean(L, !res);
+ return 1;
+}
+
+/*!
+ * \brief [lua_CFunction] Get the status of the autoservice flag (for access
+ * from lua, don't call directly)
+ *
+ * \param L the lua_State to use
+ *
+ * \return This function returns the status of the autoservice flag as a
+ * boolean to its lua caller.
+ */
+static int lua_autoservice_status(lua_State *L)
+{
+ lua_getfield(L, LUA_REGISTRYINDEX, "autoservice");
+ return 1;
+}
+
+/*!
+ * \brief [lua_CFunction] Check if this channel has been hungup or not (for
+ * access from lua, don't call directly)
+ *
+ * \param L the lua_State to use
+ *
+ * \return This function returns true if the channel was hungup
+ */
+static int lua_check_hangup(lua_State *L)
+{
+ struct ast_channel *chan;
+ lua_getfield(L, LUA_REGISTRYINDEX, "channel");
+ chan = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ lua_pushboolean(L, ast_check_hangup(chan));
+ return 1;
+}
+
+/*!
+ * \brief Store the sort order of each context
+
+ * In the event of an error, an error string will be pushed onto the lua stack.
+ *
+ * \retval 0 success
+ * \retval 1 failure
+ */
+static int lua_sort_extensions(lua_State *L)
+{
+ int extensions, extensions_order;
+
+ /* create the extensions_order table */
+ lua_newtable(L);
+ lua_setfield(L, LUA_REGISTRYINDEX, "extensions_order");
+ lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order");
+ extensions_order = lua_gettop(L);
+
+ /* sort each context in the extensions table */
+ /* load the 'extensions' table */
+ lua_getglobal(L, "extensions");
+ extensions = lua_gettop(L);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ lua_pushstring(L, "Unable to find 'extensions' table in extensions.lua\n");
+ return 1;
+ }
+
+ /* iterate through the extensions table and create a
+ * matching table (holding the sort order) in the
+ * extensions_order table for each context that is found
+ */
+ for (lua_pushnil(L); lua_next(L, extensions); lua_pop(L, 1)) {
+ int context = lua_gettop(L);
+ int context_name = context - 1;
+ int context_order;
+
+ lua_pushvalue(L, context_name);
+ lua_newtable(L);
+ context_order = lua_gettop(L);
+
+ /* iterate through this context an popluate the corrisponding
+ * table in the extensions_order table */
+ for (lua_pushnil(L); lua_next(L, context); lua_pop(L, 1)) {
+ int exten = lua_gettop(L) - 1;
+
+ lua_pushinteger(L, lua_objlen(L, context_order) + 1);
+ lua_pushvalue(L, exten);
+ lua_settable(L, context_order);
+ }
+ lua_settable(L, extensions_order); /* put the context_order table in the extensions_order table */
+
+ /* now sort the new table */
+
+ /* push the table.sort function */
+ lua_getglobal(L, "table");
+ lua_getfield(L, -1, "sort");
+ lua_remove(L, -2); /* remove the 'table' table */
+
+ /* push the context_order table */
+ lua_pushvalue(L, context_name);
+ lua_gettable(L, extensions_order);
+
+ /* push the comp function */
+ lua_pushcfunction(L, &lua_extension_cmp);
+
+ if (lua_pcall(L, 2, 0, 0)) {
+ lua_insert(L, -5);
+ lua_pop(L, 4);
+ return 1;
+ }
+ }
+
+ /* remove the extensions table and the extensions_order table */
+ lua_pop(L, 2);
+ return 0;
+}
+
+/*!
+ * \brief [lua_CFunction] Compare two extensions (for access from lua, don't
+ * call directly)
+ *
+ * This function returns true if the first extension passed should match after
+ * the second. It behaves like the '<' operator.
+ */
+static int lua_extension_cmp(lua_State *L)
+{
+ const char *a = luaL_checkstring(L, -2);
+ const char *b = luaL_checkstring(L, -1);
+
+ if (ast_extension_cmp(a, b) == -1)
+ lua_pushboolean(L, 1);
+ else
+ lua_pushboolean(L, 0);
+
+ return 1;
+}
+
+/*!
+ * \brief Load the extensions.lua file in to a buffer and execute the file
+ *
+ * \param L the lua_State to use
+ * \param size a pointer to store the size of the buffer
+ *
+ * \note The caller is expected to free the buffer at some point.
+ *
+ * \return a pointer to the buffer
+ */
+static char *lua_read_extensions_file(lua_State *L, long *size)
+{
+ FILE *f;
+ char *data;
+ char *path = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
+ sprintf(path, "%s/%s", ast_config_AST_CONFIG_DIR, config);
+
+ if (!(f = fopen(path, "r"))) {
+ lua_pushstring(L, "cannot open '");
+ lua_pushstring(L, path);
+ lua_pushstring(L, "' for reading: ");
+ lua_pushstring(L, strerror(errno));
+ lua_concat(L, 4);
+
+ return NULL;
+ }
+
+ fseek(f, 0l, SEEK_END);
+ *size = ftell(f);
+
+ fseek(f, 0l, SEEK_SET);
+
+ if (!(data = ast_malloc(*size))) {
+ *size = 0;
+ fclose(f);
+ lua_pushstring(L, "not enough memory");
+ return NULL;
+ }
+
+ fread(data, sizeof(char), *size, f);
+ fclose(f);
+
+ if (luaL_loadbuffer(L, data, *size, "extensions.lua")
+ || lua_pcall(L, 0, LUA_MULTRET, 0)
+ || lua_sort_extensions(L)) {
+ ast_free(data);
+ data = NULL;
+ *size = 0;
+ }
+ return data;
+}
+
+/*!
+ * \brief Load the extensions.lua file from the internal buffer
+ *
+ * \param L the lua_State to use
+ * \param chan channel to work on
+ *
+ * This function also sets up some constructs used by the extensions.lua file.
+ * In the event of an error, an error string will be pushed onto the lua stack.
+ *
+ * \retval 0 success
+ * \retval 1 failure
+ */
+static int lua_load_extensions(lua_State *L, struct ast_channel *chan)
+{
+
+ /* store a pointer to this channel */
+ lua_pushlightuserdata(L, chan);
+ lua_setfield(L, LUA_REGISTRYINDEX, "channel");
+
+ luaL_openlibs(L);
+
+ /* load and sort extensions */
+ ast_mutex_lock(&config_file_lock);
+ if (luaL_loadbuffer(L, config_file_data, config_file_size, "extensions.lua")
+ || lua_pcall(L, 0, LUA_MULTRET, 0)
+ || lua_sort_extensions(L)) {
+ ast_mutex_unlock(&config_file_lock);
+ return 1;
+ }
+ ast_mutex_unlock(&config_file_lock);
+
+ /* now we setup special tables and functions */
+
+ lua_create_app_table(L);
+ lua_create_channel_table(L);
+
+ lua_create_variable_metatable(L);
+ lua_create_application_metatable(L);
+
+ lua_create_autoservice_functions(L);
+ lua_create_hangup_function(L);
+
+ return 0;
+}
+
+/*!
+ * \brief Reload the extensions file and update the internal buffers if it
+ * loads correctly.
+ *
+ * \warning This function should not be called on a lua_State returned from
+ * lua_get_state().
+ *
+ * \param L the lua_State to use (must be freshly allocated with
+ * luaL_newstate(), don't use lua_get_state())
+ */
+static int lua_reload_extensions(lua_State *L)
+{
+ long size = 0;
+ char *data = NULL;
+
+ luaL_openlibs(L);
+
+ if (!(data = lua_read_extensions_file(L, &size))) {
+ return 1;
+ }
+
+ ast_mutex_lock(&config_file_lock);
+
+ if (config_file_data)
+ ast_free(config_file_data);
+
+ config_file_data = data;
+ config_file_size = size;
+
+ ast_mutex_unlock(&config_file_lock);
+ return 0;
+}
+
+/*!
+ * \brief Free the internal extensions buffer.
+ */
+static void lua_free_extensions()
+{
+ ast_mutex_lock(&config_file_lock);
+ config_file_size = 0;
+ ast_free(config_file_data);
+ ast_mutex_unlock(&config_file_lock);
+}
+
+/*!
+ * \brief Get the lua_State for this channel
+ *
+ * If no channel is passed then a new state is allocated. States with no
+ * channel assocatied with them should only be used for matching extensions.
+ * If the channel does not yet have a lua state associated with it, one will be
+ * created.
+ *
+ * \note If no channel was passed then the caller is expected to free the state
+ * using lua_close().
+ *
+ * \return a lua_State
+ */
+static lua_State *lua_get_state(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore = NULL;
+ lua_State *L;
+
+ if (!chan) {
+ lua_State *L = luaL_newstate();
+ if (!L) {
+ ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n");
+ return NULL;
+ }
+
+ if (lua_load_extensions(L, NULL)) {
+ const char *error = lua_tostring(L, -1);
+ ast_log(LOG_ERROR, "Error loading extensions.lua: %s\n", error);
+ lua_close(L);
+ return NULL;
+ }
+ return L;
+ } else {
+ datastore = ast_channel_datastore_find(chan, &lua_datastore, NULL);
+
+ if (!datastore) {
+ /* nothing found, allocate a new lua state */
+ datastore = ast_channel_datastore_alloc(&lua_datastore, NULL);
+ if (!datastore) {
+ ast_log(LOG_ERROR, "Error allocation channel datastore for lua_State\n");
+ return NULL;
+ }
+
+ datastore->data = luaL_newstate();
+ if (!datastore->data) {
+ ast_channel_datastore_free(datastore);
+ ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n");
+ return NULL;
+ }
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+
+ L = datastore->data;
+
+ if (lua_load_extensions(L, chan)) {
+ const char *error = lua_tostring(L, -1);
+ ast_log(LOG_ERROR, "Error loading extensions.lua for %s: %s\n", chan->name, error);
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_remove(chan, datastore);
+ ast_channel_unlock(chan);
+
+ ast_channel_datastore_free(datastore);
+ return NULL;
+ }
+ }
+
+ return datastore->data;
+ }
+}
+
+static int exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res;
+ lua_State *L;
+ struct ast_module_user *u = ast_module_user_add(chan);
+ if (!u) {
+ ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n");
+ return 0;
+ }
+
+ L = lua_get_state(chan);
+ if (!L) {
+ ast_module_user_remove(u);
+ return 0;
+ }
+
+ res = lua_find_extension(L, context, exten, priority, &exists, 0);
+
+ if (!chan) lua_close(L);
+ ast_module_user_remove(u);
+ return res;
+}
+
+static int canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res;
+ lua_State *L;
+ struct ast_module_user *u = ast_module_user_add(chan);
+ if (!u) {
+ ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n");
+ return 0;
+ }
+
+ L = lua_get_state(chan);
+ if (!L) {
+ ast_module_user_remove(u);
+ return 0;
+ }
+
+ res = lua_find_extension(L, context, exten, priority, &canmatch, 0);
+
+ if (!chan) lua_close(L);
+ ast_module_user_remove(u);
+ return res;
+}
+
+static int matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res;
+ lua_State *L;
+ struct ast_module_user *u = ast_module_user_add(chan);
+ if (!u) {
+ ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n");
+ return 0;
+ }
+
+ L = lua_get_state(chan);
+ if (!L) {
+ ast_module_user_remove(u);
+ return 0;
+ }
+
+ res = lua_find_extension(L, context, exten, priority, &matchmore, 0);
+
+ if (!chan) lua_close(L);
+ ast_module_user_remove(u);
+ return res;
+}
+
+
+static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res;
+ lua_State *L;
+ struct ast_module_user *u = ast_module_user_add(chan);
+ if (!u) {
+ ast_log(LOG_ERROR, "Error adjusting use count, probably could not allocate memory\n");
+ return -1;
+ }
+
+ L = lua_get_state(chan);
+ if (!L) {
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ /* push the extension function onto the stack */
+ if (!lua_find_extension(L, context, exten, priority, &exists, 1)) {
+ ast_log(LOG_ERROR, "Could not find extension %s in context %s\n", exten, context);
+ if (!chan) lua_close(L);
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ lua_update_registry(L, context, exten, priority);
+
+ lua_pushstring(L, context);
+ lua_pushstring(L, exten);
+
+ res = lua_pcall(L, 2, 0, 0);
+ if (res) {
+ if (res == LUA_ERRRUN) {
+ if (lua_isnumber(L, -1)) {
+ res = lua_tointeger(L, -1);
+ } else if (lua_isstring(L, -1)) {
+ const char *error = lua_tostring(L, -1);
+ ast_log(LOG_ERROR, "Error executing lua extension: %s\n", error);
+ res = -1;
+ }
+ } else {
+ res = -1;
+ }
+ }
+ if (!chan) lua_close(L);
+ ast_module_user_remove(u);
+ return res;
+}
+
+/*!
+ * \brief Locate an extensions and optionally push the matching function on the
+ * stack
+ *
+ * \param L the lua_State to use
+ * \param context the context to look in
+ * \param exten the extension to look up
+ * \param priority the priority to check, '1' is the only valid priority
+ * \param func the calling func, used to adjust matching behavior between,
+ * match, canmatch, and matchmore
+ * \param push_func whether or not to push the lua function for the given
+ * extension onto the stack
+ */
+static int lua_find_extension(lua_State *L, const char *context, const char *exten, int priority, ast_switch_f *func, int push_func)
+{
+ int context_table, context_order_table, i;
+
+ ast_debug(2, "Looking up %s@%s:%i\n", exten, context, priority);
+ if (priority != 1)
+ return 0;
+
+ /* load the 'extensions' table */
+ lua_getglobal(L, "extensions");
+ if (lua_isnil(L, -1)) {
+ ast_log(LOG_ERROR, "Unable to find 'extensions' table in extensions.lua\n");
+ lua_pop(L, 1);
+ return 0;
+ }
+
+ /* load the given context */
+ lua_getfield(L, -1, context);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 2);
+ return 0;
+ }
+
+ /* remove the extensions table */
+ lua_remove(L, -2);
+
+ context_table = lua_gettop(L);
+
+ /* load the extensions order table for this context */
+ lua_getfield(L, LUA_REGISTRYINDEX, "extensions_order");
+ lua_getfield(L, -1, context);
+
+ lua_remove(L, -2); /* remove the extensions order table */
+
+ context_order_table = lua_gettop(L);
+
+ /* step through the extensions looking for a match */
+ for (i = 1; i < lua_objlen(L, context_order_table) + 1; i++) {
+ int e_index, isnumber, match = 0;
+ const char *e;
+
+ lua_pushinteger(L, i);
+ lua_gettable(L, context_order_table);
+ e_index = lua_gettop(L);
+ isnumber = lua_isnumber(L, e_index);
+
+ if (!(e = lua_tostring(L, e_index))) {
+ lua_pop(L, 1);
+ continue;
+ }
+
+ /* make sure this is not the 'include' extension */
+ if (!strcasecmp(e, "include")) {
+ lua_pop(L, 1);
+ continue;
+ }
+
+ if (func == &matchmore)
+ match = ast_extension_close(e, exten, E_MATCHMORE);
+ else if (func == &canmatch)
+ match = ast_extension_close(e, exten, E_CANMATCH);
+ else
+ match = ast_extension_match(e, exten);
+
+ /* the extension matching functions return 0 on fail, 1 on
+ * match, 2 on earlymatch */
+
+ if (!match) {
+ lua_pop(L, 1);
+ continue; /* keep trying */
+ }
+
+ if (func == &matchmore && match == 2) {
+ /* We match an extension ending in '!'. The decision in
+ * this case is final and counts as no match. */
+ lua_pop(L, 3);
+ return 0;
+ }
+
+ /* remove the context table, the context order table, and the
+ * extension (or replace the extension with the corisponding
+ * function) */
+ if (push_func) {
+ /* here we must convert the exten back to an integer
+ * because lua_tostring will change the value on the
+ * stack to a string */
+ if (isnumber) {
+ int e_int = lua_tointeger(L, e_index);
+ lua_pop(L, 1); /* the exten should be the top of the stack */
+ lua_pushinteger(L, e_int);
+ }
+ lua_gettable(L, context_table);
+ lua_insert(L, -3);
+ lua_pop(L, 2);
+ } else {
+ lua_pop(L, 3);
+ }
+
+ return 1;
+ }
+
+ /* load the includes for this context */
+ lua_getfield(L, context_table, "include");
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 3);
+ return 0;
+ }
+
+ /* remove the context and the order table*/
+ lua_remove(L, context_order_table);
+ lua_remove(L, context_table);
+
+ /* Now try any includes we have in this context */
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ const char *c = lua_tostring(L, -1);
+ if (!c)
+ continue;
+
+ if (lua_find_extension(L, c, exten, priority, func, push_func)) {
+ /* remove the value, the key, and the includes table
+ * from the stack. Leave the function behind if
+ * necessary */
+
+ if (push_func)
+ lua_insert(L, -4);
+
+ lua_pop(L, 3);
+ return 1;
+ }
+ }
+
+ /* pop the includes table */
+ lua_pop(L, 1);
+ return 0;
+}
+
+static struct ast_switch lua_switch = {
+ .name = "Lua",
+ .description = "Lua PBX Switch",
+ .exists = exists,
+ .canmatch = canmatch,
+ .exec = exec,
+ .matchmore = matchmore,
+};
+
+
+static int load_or_reload_lua_stuff(void)
+{
+ int res = AST_MODULE_LOAD_SUCCESS;
+
+ lua_State *L = luaL_newstate();
+ if (!L) {
+ ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (lua_reload_extensions(L)) {
+ const char *error = lua_tostring(L, -1);
+ ast_log(LOG_ERROR, "Error loading extensions.lua: %s\n", error);
+ res = AST_MODULE_LOAD_DECLINE;
+ }
+
+ lua_close(L);
+ return res;
+}
+
+static int unload_module(void)
+{
+ ast_unregister_switch(&lua_switch);
+ lua_free_extensions();
+ return 0;
+}
+
+static int reload(void)
+{
+ return load_or_reload_lua_stuff();
+}
+
+static int load_module(void)
+{
+ int res;
+
+ if ((res = load_or_reload_lua_stuff()))
+ return res;
+
+ if (ast_register_switch(&lua_switch)) {
+ ast_log(LOG_ERROR, "Unable to register LUA PBX switch\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Lua PBX Switch",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/trunk/pbx/pbx_realtime.c b/trunk/pbx/pbx_realtime.c
new file mode 100644
index 000000000..d4876bd7f
--- /dev/null
+++ b/trunk/pbx/pbx_realtime.c
@@ -0,0 +1,251 @@
+/*
+ * 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 Realtime PBX Module
+ *
+ * \arg See also: \ref AstARA
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/frame.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/md5.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/utils.h"
+#include "asterisk/crypto.h"
+#include "asterisk/astdb.h"
+
+#define MODE_MATCH 0
+#define MODE_MATCHMORE 1
+#define MODE_CANMATCH 2
+
+#define EXT_DATA_SIZE 256
+
+
+/* Realtime switch looks up extensions in the supplied realtime table.
+
+ [context@][realtimetable][/options]
+
+ If the realtimetable is omitted it is assumed to be "extensions". If no context is
+ specified the context is assumed to be whatever is the container.
+
+ The realtime table should have entries for context,exten,priority,app,args
+
+ The realtime table currently does not support callerid fields.
+
+*/
+
+
+static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode)
+{
+ struct ast_variable *var;
+ struct ast_config *cfg;
+ char pri[20];
+ char *ematch;
+ char rexten[AST_MAX_EXTENSION + 20]="";
+ int match;
+ snprintf(pri, sizeof(pri), "%d", priority);
+ switch(mode) {
+ case MODE_MATCHMORE:
+ ematch = "exten LIKE";
+ snprintf(rexten, sizeof(rexten), "%s_%%", exten);
+ break;
+ case MODE_CANMATCH:
+ ematch = "exten LIKE";
+ snprintf(rexten, sizeof(rexten), "%s%%", exten);
+ break;
+ case MODE_MATCH:
+ default:
+ ematch = "exten";
+ ast_copy_string(rexten, exten, sizeof(rexten));
+ }
+ var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, NULL);
+ if (!var) {
+ cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, NULL);
+ if (cfg) {
+ char *cat = ast_category_browse(cfg, NULL);
+
+ while(cat) {
+ switch(mode) {
+ case MODE_MATCHMORE:
+ match = ast_extension_close(cat, exten, 1);
+ break;
+ case MODE_CANMATCH:
+ match = ast_extension_close(cat, exten, 0);
+ break;
+ case MODE_MATCH:
+ default:
+ match = ast_extension_match(cat, exten);
+ }
+ if (match) {
+ var = ast_category_detach_variables(ast_category_get(cfg, cat));
+ break;
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_config_destroy(cfg);
+ }
+ }
+ return var;
+}
+
+static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
+{
+ const char *ctx = NULL;
+ char *table;
+ struct ast_variable *var=NULL;
+ char *buf = ast_strdupa(data);
+ if (buf) {
+ char *opts = strchr(buf, '/');
+ if (opts)
+ *opts++ = '\0';
+ table = strchr(buf, '@');
+ if (table) {
+ *table++ = '\0';
+ ctx = buf;
+ }
+ ctx = S_OR(ctx, context);
+ table = S_OR(table, "extensions");
+ var = realtime_switch_common(table, ctx, exten, priority, mode);
+ }
+ return var;
+}
+
+static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
+ if (var) {
+ ast_variables_destroy(var);
+ return 1;
+ }
+ return 0;
+}
+
+static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
+ if (var) {
+ ast_variables_destroy(var);
+ return 1;
+ }
+ return 0;
+}
+
+static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ int res = -1;
+ struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
+
+ if (var) {
+ char *tmp="";
+ char *app = NULL;
+ struct ast_variable *v;
+
+ for (v = var; v ; v = v->next) {
+ if (!strcasecmp(v->name, "app"))
+ app = ast_strdupa(v->value);
+ else if (!strcasecmp(v->name, "appdata"))
+ tmp = ast_strdupa(v->value);
+ }
+ ast_variables_destroy(var);
+ if (!ast_strlen_zero(app)) {
+ struct ast_app *a = pbx_findapp(app);
+ if (a) {
+ char appdata[512];
+ char tmp1[80];
+ char tmp2[80];
+ char tmp3[EXT_DATA_SIZE];
+
+ appdata[0] = 0; /* just in case the substitute var func isn't called */
+ if(!ast_strlen_zero(tmp))
+ pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1);
+ ast_verb(3, "Executing %s(\"%s\", \"%s\")\n",
+ term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
+ term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
+ term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
+ manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
+ "Channel: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "Application: %s\r\n"
+ "AppData: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan->name, chan->context, chan->exten, chan->priority, app, !ast_strlen_zero(appdata) ? appdata : "(NULL)", chan->uniqueid);
+
+ res = pbx_exec(chan, a, appdata);
+ } else
+ ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
+ } else {
+ ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
+ }
+ }
+ return res;
+}
+
+static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+ struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
+ if (var) {
+ ast_variables_destroy(var);
+ return 1;
+ }
+ return 0;
+}
+
+static struct ast_switch realtime_switch =
+{
+ name: "Realtime",
+ description: "Realtime Dialplan Switch",
+ exists: realtime_exists,
+ canmatch: realtime_canmatch,
+ exec: realtime_exec,
+ matchmore: realtime_matchmore,
+};
+
+static int unload_module(void)
+{
+ ast_unregister_switch(&realtime_switch);
+ return 0;
+}
+
+static int load_module(void)
+{
+ if (ast_register_switch(&realtime_switch))
+ return AST_MODULE_LOAD_FAILURE;
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch");
diff --git a/trunk/pbx/pbx_spool.c b/trunk/pbx/pbx_spool.c
new file mode 100644
index 000000000..113fc2b73
--- /dev/null
+++ b/trunk/pbx/pbx_spool.c
@@ -0,0 +1,509 @@
+/*
+ * 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 Full-featured outgoing call spool support
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/callerid.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+
+/*
+ * pbx_spool is similar in spirit to qcall, but with substantially enhanced functionality...
+ * The spool file contains a header
+ */
+
+enum {
+ /*! Always delete the call file after a call succeeds or the
+ * maximum number of retries is exceeded, even if the
+ * modification time of the call file is in the future.
+ */
+ SPOOL_FLAG_ALWAYS_DELETE = (1 << 0),
+ /* Don't unlink the call file after processing, move in qdonedir */
+ SPOOL_FLAG_ARCHIVE = (1 << 1)
+};
+
+static char qdir[255];
+static char qdonedir[255];
+
+struct outgoing {
+ char fn[256];
+ /*! Current number of retries */
+ int retries;
+ /*! Maximum number of retries permitted */
+ int maxretries;
+ /*! How long to wait between retries (in seconds) */
+ int retrytime;
+ /*! How long to wait for an answer */
+ int waittime;
+ /*! PID which is currently calling */
+ long callingpid;
+
+ /*! What to connect to outgoing */
+ char tech[256];
+ char dest[256];
+
+ /* If application */
+ char app[256];
+ char data[256];
+
+ /* If extension/context/priority */
+ char exten[AST_MAX_EXTENSION];
+ char context[AST_MAX_CONTEXT];
+ int priority;
+
+ /* CallerID Information */
+ char cid_num[256];
+ char cid_name[256];
+
+ /*! account code */
+ char account[AST_MAX_ACCOUNT_CODE];
+
+ /*! Variables and Functions */
+ struct ast_variable *vars;
+
+ /*! Maximum length of call */
+ int maxlen;
+
+ /*! options */
+ struct ast_flags options;
+};
+
+static void init_outgoing(struct outgoing *o)
+{
+ o->priority = 1;
+ o->retrytime = 300;
+ o->waittime = 45;
+ ast_set_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE);
+}
+
+static void free_outgoing(struct outgoing *o)
+{
+ ast_free(o);
+}
+
+static int apply_outgoing(struct outgoing *o, char *fn, FILE *f)
+{
+ char buf[256];
+ char *c, *c2;
+ int lineno = 0;
+ struct ast_variable *var;
+
+ while(fgets(buf, sizeof(buf), f)) {
+ lineno++;
+ /* Trim comments */
+ c = buf;
+ while ((c = strchr(c, '#'))) {
+ if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t'))
+ *c = '\0';
+ else
+ c++;
+ }
+
+ c = buf;
+ while ((c = strchr(c, ';'))) {
+ if ((c > buf) && (c[-1] == '\\')) {
+ memmove(c - 1, c, strlen(c) + 1);
+ c++;
+ } else {
+ *c = '\0';
+ break;
+ }
+ }
+
+ /* Trim trailing white space */
+ while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33)
+ buf[strlen(buf) - 1] = '\0';
+ if (!ast_strlen_zero(buf)) {
+ c = strchr(buf, ':');
+ if (c) {
+ *c = '\0';
+ c++;
+ while ((*c) && (*c < 33))
+ c++;
+#if 0
+ printf("'%s' is '%s' at line %d\n", buf, c, lineno);
+#endif
+ if (!strcasecmp(buf, "channel")) {
+ ast_copy_string(o->tech, c, sizeof(o->tech));
+ if ((c2 = strchr(o->tech, '/'))) {
+ *c2 = '\0';
+ c2++;
+ ast_copy_string(o->dest, c2, sizeof(o->dest));
+ } else {
+ ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, fn);
+ o->tech[0] = '\0';
+ }
+ } else if (!strcasecmp(buf, "callerid")) {
+ ast_callerid_split(c, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
+ } else if (!strcasecmp(buf, "application")) {
+ ast_copy_string(o->app, c, sizeof(o->app));
+ } else if (!strcasecmp(buf, "data")) {
+ ast_copy_string(o->data, c, sizeof(o->data));
+ } else if (!strcasecmp(buf, "maxretries")) {
+ if (sscanf(c, "%d", &o->maxretries) != 1) {
+ ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn);
+ o->maxretries = 0;
+ }
+ } else if (!strcasecmp(buf, "context")) {
+ ast_copy_string(o->context, c, sizeof(o->context));
+ } else if (!strcasecmp(buf, "extension")) {
+ ast_copy_string(o->exten, c, sizeof(o->exten));
+ } else if (!strcasecmp(buf, "priority")) {
+ if ((sscanf(c, "%d", &o->priority) != 1) || (o->priority < 1)) {
+ ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, fn);
+ o->priority = 1;
+ }
+ } else if (!strcasecmp(buf, "retrytime")) {
+ if ((sscanf(c, "%d", &o->retrytime) != 1) || (o->retrytime < 1)) {
+ ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, fn);
+ o->retrytime = 300;
+ }
+ } else if (!strcasecmp(buf, "waittime")) {
+ if ((sscanf(c, "%d", &o->waittime) != 1) || (o->waittime < 1)) {
+ ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, fn);
+ o->waittime = 45;
+ }
+ } else if (!strcasecmp(buf, "retry")) {
+ o->retries++;
+ } else if (!strcasecmp(buf, "startretry")) {
+ if (sscanf(c, "%ld", &o->callingpid) != 1) {
+ ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
+ o->callingpid = 0;
+ }
+ } else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
+ o->callingpid = 0;
+ o->retries++;
+ } else if (!strcasecmp(buf, "delayedretry")) {
+ } else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
+ c2 = c;
+ strsep(&c2, "=");
+ if (c2) {
+ var = ast_variable_new(c, c2, fn);
+ if (var) {
+ var->next = o->vars;
+ o->vars = var;
+ }
+ } else
+ ast_log(LOG_WARNING, "Malformed \"%s\" argument. Should be \"%s: variable=value\"\n", buf, buf);
+ } else if (!strcasecmp(buf, "account")) {
+ ast_copy_string(o->account, c, sizeof(o->account));
+ } else if (!strcasecmp(buf, "alwaysdelete")) {
+ ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE);
+ } else if (!strcasecmp(buf, "archive")) {
+ ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
+ } else {
+ ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn);
+ }
+ } else
+ ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, fn);
+ }
+ }
+ ast_copy_string(o->fn, fn, sizeof(o->fn));
+ if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
+ ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn);
+ return -1;
+ }
+ return 0;
+}
+
+static void safe_append(struct outgoing *o, time_t now, char *s)
+{
+ int fd;
+ FILE *f;
+ struct utimbuf tbuf;
+
+ if ((fd = open(o->fn, O_WRONLY | O_APPEND)) < 0)
+ return;
+
+ if ((f = fdopen(fd, "a"))) {
+ fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now);
+ fclose(f);
+ } else
+ close(fd);
+
+ /* Update the file time */
+ tbuf.actime = now;
+ tbuf.modtime = now + o->retrytime;
+ if (utime(o->fn, &tbuf))
+ ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno));
+}
+
+/*!
+ * \brief Remove a call file from the outgoing queue optionally moving it in the archive dir
+ *
+ * \param o the pointer to outgoing struct
+ * \param status the exit status of the call. Can be "Completed", "Failed" or "Expired"
+ */
+static int remove_from_queue(struct outgoing *o, const char *status)
+{
+ int fd;
+ FILE *f;
+ char newfn[256];
+ const char *bname;
+
+ if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) {
+ struct stat current_file_status;
+
+ if (!stat(o->fn, &current_file_status)) {
+ if (time(NULL) < current_file_status.st_mtime)
+ return 0;
+ }
+ }
+
+ if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) {
+ unlink(o->fn);
+ return 0;
+ }
+
+ if (ast_mkdir(qdonedir, 0777)) {
+ ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir);
+ unlink(o->fn);
+ return -1;
+ }
+
+ if ((fd = open(o->fn, O_WRONLY | O_APPEND))) {
+ if ((f = fdopen(fd, "a"))) {
+ fprintf(f, "Status: %s\n", status);
+ fclose(f);
+ } else
+ close(fd);
+ }
+
+ if (!(bname = strrchr(o->fn, '/')))
+ bname = o->fn;
+ else
+ bname++;
+ snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname);
+ /* a existing call file the archive dir is overwritten */
+ unlink(newfn);
+ if (rename(o->fn, newfn) != 0) {
+ unlink(o->fn);
+ return -1;
+ } else
+ return 0;
+}
+
+static void *attempt_thread(void *data)
+{
+ struct outgoing *o = data;
+ int res, reason;
+ if (!ast_strlen_zero(o->app)) {
+ ast_verb(3, "Attempting call on %s/%s for application %s(%s) (Retry %d)\n", o->tech, o->dest, o->app, o->data, o->retries);
+ res = ast_pbx_outgoing_app(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
+ } else {
+ ast_verb(3, "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries);
+ res = ast_pbx_outgoing_exten(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->context, o->exten, o->priority, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
+ }
+ if (res) {
+ ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason));
+ if (o->retries >= o->maxretries + 1) {
+ /* Max retries exceeded */
+ ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : "");
+ remove_from_queue(o, "Expired");
+ } else {
+ /* Notate that the call is still active */
+ safe_append(o, time(NULL), "EndRetry");
+ }
+ } else {
+ ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
+ ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest);
+ remove_from_queue(o, "Completed");
+ }
+ free_outgoing(o);
+ return NULL;
+}
+
+static void launch_service(struct outgoing *o)
+{
+ pthread_t t;
+ int ret;
+
+ if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
+ ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
+ free_outgoing(o);
+ }
+}
+
+static int scan_service(char *fn, time_t now, time_t atime)
+{
+ struct outgoing *o = NULL;
+ FILE *f;
+ int res = 0;
+
+ if (!(o = ast_calloc(1, sizeof(*o)))) {
+ ast_log(LOG_WARNING, "Out of memory ;(\n");
+ return -1;
+ }
+
+ init_outgoing(o);
+
+ /* Attempt to open the file */
+ if (!(f = fopen(fn, "r+"))) {
+ remove_from_queue(o, "Failed");
+ free_outgoing(o);
+ ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno));
+ return -1;
+ }
+
+ /* Read in and verify the contents */
+ if (apply_outgoing(o, fn, f)) {
+ remove_from_queue(o, "Failed");
+ free_outgoing(o);
+ ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn);
+ fclose(f);
+ return -1;
+ }
+
+#if 0
+ printf("Filename: %s, Retries: %d, max: %d\n", fn, o->retries, o->maxretries);
+#endif
+ fclose(f);
+ if (o->retries <= o->maxretries) {
+ now += o->retrytime;
+ if (o->callingpid && (o->callingpid == ast_mainpid)) {
+ safe_append(o, time(NULL), "DelayedRetry");
+ ast_log(LOG_DEBUG, "Delaying retry since we're currently running '%s'\n", o->fn);
+ free_outgoing(o);
+ } else {
+ /* Increment retries */
+ o->retries++;
+ /* If someone else was calling, they're presumably gone now
+ so abort their retry and continue as we were... */
+ if (o->callingpid)
+ safe_append(o, time(NULL), "AbortRetry");
+
+ safe_append(o, now, "StartRetry");
+ launch_service(o);
+ }
+ res = now;
+ } else {
+ ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : "");
+ remove_from_queue(o, "Expired");
+ free_outgoing(o);
+ }
+
+ return res;
+}
+
+static void *scan_thread(void *unused)
+{
+ struct stat st;
+ DIR *dir;
+ struct dirent *de;
+ char fn[256];
+ int res;
+ time_t last = 0, next = 0, now;
+
+ for(;;) {
+ /* Wait a sec */
+ sleep(1);
+ time(&now);
+
+ if (stat(qdir, &st)) {
+ ast_log(LOG_WARNING, "Unable to stat %s\n", qdir);
+ continue;
+ }
+
+ /* Make sure it is time for us to execute our check */
+ if ((st.st_mtime == last) && (next && (next > now)))
+ continue;
+
+#if 0
+ printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime);
+ printf("Ooh, something changed / timeout\n");
+#endif
+ next = 0;
+ last = st.st_mtime;
+
+ if (!(dir = opendir(qdir))) {
+ ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno));
+ continue;
+ }
+
+ while ((de = readdir(dir))) {
+ snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name);
+ if (stat(fn, &st)) {
+ ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno));
+ continue;
+ }
+ if (!S_ISREG(st.st_mode))
+ continue;
+ if (st.st_mtime <= now) {
+ res = scan_service(fn, now, st.st_atime);
+ if (res > 0) {
+ /* Update next service time */
+ if (!next || (res < next)) {
+ next = res;
+ }
+ } else if (res)
+ ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn);
+ } else {
+ /* Update "next" update if necessary */
+ if (!next || (st.st_mtime < next))
+ next = st.st_mtime;
+ }
+ }
+ closedir(dir);
+ }
+ return NULL;
+}
+
+static int unload_module(void)
+{
+ return -1;
+}
+
+static int load_module(void)
+{
+ pthread_t thread;
+ int ret;
+ snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
+ if (ast_mkdir(qdir, 0777)) {
+ ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done");
+
+ if ((ret = ast_pthread_create_detached_background(&thread, NULL, scan_thread, NULL))) {
+ ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Outgoing Spool Support");
diff --git a/trunk/phoneprov/000000000000-directory.xml b/trunk/phoneprov/000000000000-directory.xml
new file mode 100644
index 000000000..a79e6eda0
--- /dev/null
+++ b/trunk/phoneprov/000000000000-directory.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" standalone="yes"?>
+<directory>
+ <item_list>
+ ${PP_EACH_USER(<item><fn>%{DISPLAY_NAME}</fn><ct>%{CALLERID}</ct><bw>1</bw></item>|${MAC})}
+ </item_list>
+</directory>
diff --git a/trunk/phoneprov/000000000000-phone.cfg b/trunk/phoneprov/000000000000-phone.cfg
new file mode 100644
index 000000000..c04a5cb00
--- /dev/null
+++ b/trunk/phoneprov/000000000000-phone.cfg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="yes"?>
diff --git a/trunk/phoneprov/000000000000.cfg b/trunk/phoneprov/000000000000.cfg
new file mode 100644
index 000000000..a1377382e
--- /dev/null
+++ b/trunk/phoneprov/000000000000.cfg
@@ -0,0 +1,2 @@
+<?xml version="1.0" standalone="yes"?>
+ <APPLICATION APP_FILE_PATH="sip.ld" CONFIG_FILES="${IF($[${STAT(e|${CUSTOM_CONFIG})}] ? "custom.cfg, ")}config/${TOLOWER(${MAC})}, sip.cfg" MISC_FILES="" LOG_FILE_DIRECTORY=""/>
diff --git a/trunk/phoneprov/polycom.xml b/trunk/phoneprov/polycom.xml
new file mode 100644
index 000000000..05195a862
--- /dev/null
+++ b/trunk/phoneprov/polycom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<phone1>
+ <reg reg.1.displayName="${DISPLAY_NAME}" reg.1.address="${USERNAME}" reg.1.label="${LABEL}" reg.1.type="private" reg.1.thirdPartyName="" reg.1.auth.userId="${USERNAME}" reg.1.auth.password="${SECRET}" reg.1.server.1.address="${SERVER}" reg.1.server.1.port="${SERVER_PORT}" reg.1.server.1.transport="DNSnaptr" reg.1.server.1.expires="300" reg.1.server.1.register="1" reg.1.server.1.retryTimeOut="" reg.1.server.1.retryMaxCount="" reg.1.server.1.expires.lineSeize="" reg.1.acd-login-logout="0" reg.1.acd-agent-available="0" reg.1.ringType="2" reg.1.lineKeys="2" reg.1.callsPerLineKey="1"/>
+ <call>
+ <donotdisturb call.donotdisturb.perReg="1" />
+ <autoOffHook call.autoOffHook.1.enabled="0" call.autoOffHook.1.contact="" call.autoOffHook.2.enabled="0" call.autoOffHook.2.contact="" call.autoOffHook.3.enabled="0" call.autoOffHook.3.contact="" call.autoOffHook.4.enabled="0" call.autoOffHook.4.contact="" call.autoOffHook.5.enabled="0" call.autoOffHook.5.contact="" call.autoOffHook.6.enabled="0" call.autoOffHook.6.contact=""/>
+ <serverMissedCall call.serverMissedCall.1.enabled="0" call.serverMissedCall.2.enabled="0" call.serverMissedCall.3.enabled="0" call.serverMissedCall.4.enabled="0" call.serverMissedCall.5.enabled="0" call.serverMissedCall.6.enabled="0"/>
+ </call>
+ <divert divert.1.contact="" divert.1.autoOnSpecificCaller="1" divert.1.sharedDisabled="1" divert.2.contact="" divert.2.autoOnSpecificCaller="1" divert.2.sharedDisabled="1" divert.3.contact="" divert.3.autoOnSpecificCaller="1" divert.3.sharedDisabled="1" divert.4.contact="" divert.4.autoOnSpecificCaller="1" divert.4.sharedDisabled="1" divert.5.contact="" divert.5.autoOnSpecificCaller="1" divert.5.sharedDisabled="1" divert.6.contact="" divert.6.autoOnSpecificCaller="1" divert.6.sharedDisabled="1">
+ <fwd divert.fwd.1.enabled="1" divert.fwd.2.enabled="1" divert.fwd.3.enabled="1" divert.fwd.4.enabled="1" divert.fwd.5.enabled="1" divert.fwd.6.enabled="1"/>
+ <busy divert.busy.1.enabled="1" divert.busy.1.contact="" divert.busy.2.enabled="1" divert.busy.2.contact="" divert.busy.3.enabled="1" divert.busy.3.contact="" divert.busy.4.enabled="1" divert.busy.4.contact="" divert.busy.5.enabled="1" divert.busy.5.contact="" divert.busy.6.enabled="1" divert.busy.6.contact=""/>
+ <noanswer divert.noanswer.1.enabled="1" divert.noanswer.1.timeout="60" divert.noanswer.1.contact="" divert.noanswer.2.enabled="1" divert.noanswer.2.timeout="60" divert.noanswer.2.contact="" divert.noanswer.3.enabled="1" divert.noanswer.3.timeout="60" divert.noanswer.3.contact="" divert.noanswer.4.enabled="1" divert.noanswer.4.timeout="60" divert.noanswer.4.contact="" divert.noanswer.5.enabled="1" divert.noanswer.5.timeout="60" divert.noanswer.5.contact="" divert.noanswer.6.enabled="1" divert.noanswer.6.timeout="60" divert.noanswer.6.contact=""/>
+ <dnd divert.dnd.1.enabled="0" divert.dnd.1.contact="" divert.dnd.2.enabled="0" divert.dnd.2.contact="" divert.dnd.3.enabled="0" divert.dnd.3.contact="" divert.dnd.4.enabled="0" divert.dnd.4.contact="" divert.dnd.5.enabled="0" divert.dnd.5.contact="" divert.dnd.6.enabled="0" divert.dnd.6.contact=""/>
+ </divert>
+ <dialplan
+ dialplan.1.impossibleMatchHandling="2" dialplan.1.removeEndOfDial="0"
+ />
+ <digitmap
+ dialplan.1.digitmap="" dialplan.1.digitmap.timeOut="3"
+ />
+ <routing>
+ <server dialplan.1.routing.server.1.address="" dialplan.1.routing.server.1.port="" dialplan.2.routing.server.1.address="" dialplan.2.routing.server.1.port="" dialplan.3.routing.server.1.address="" dialplan.3.routing.server.1.port="" dialplan.4.routing.server.1.address="" dialplan.4.routing.server.1.port="" dialplan.5.routing.server.1.address="" dialplan.5.routing.server.1.port="" dialplan.6.routing.server.1.address="" dialplan.6.routing.server.1.port=""/>
+ <emergency dialplan.1.routing.emergency.1.value="" dialplan.1.routing.emergency.1.server.1="" dialplan.2.routing.emergency.1.value="" dialplan.2.routing.emergency.1.server.1="" dialplan.3.routing.emergency.1.value="" dialplan.3.routing.emergency.1.server.1="" dialplan.4.routing.emergency.1.value="" dialplan.4.routing.emergency.1.server.1="" dialplan.5.routing.emergency.1.value="" dialplan.5.routing.emergency.1.server.1="" dialplan.6.routing.emergency.1.value="" dialplan.6.routing.emergency.1.server.1=""/>
+ </routing>
+ <msg msg.bypassInstantMessage="1">
+ <mwi msg.mwi.1.callBackMode="contact" msg.mwi.1.callBack="${VOICEMAIL_EXTEN}" />
+ </msg>
+ <nat nat.ip="" nat.signalPort="" nat.mediaPortStart=""/>
+ <user_preferences up.oneTouchVoiceMail="1" up.welcomeSoundEnabled="0" />
+ <volume voice.volume.persist.handset="1" voice.volume.persist.headset="1" />
+ <SNTP tcpIpApp.sntp.address="time" tcpIpApp.sntp.gmtOffset="-21600" />
+ <HTTPD httpd.enabled="1" />
+ <feature
+ feature.1.name="presence" feature.1.enabled="1"
+ feature.8.name="calllist-missed" feature.8.enabled="1"
+ />
+</phone1>
diff --git a/trunk/redhat/asterisk.spec b/trunk/redhat/asterisk.spec
new file mode 100644
index 000000000..4f4ab892c
--- /dev/null
+++ b/trunk/redhat/asterisk.spec
@@ -0,0 +1,137 @@
+Summary: Asterisk PBX
+Name: asterisk
+Distribution: RedHat
+Version: CVS
+Release: 1
+Copyright: Linux Support Services, inc.
+Group: Utilities/System
+Vendor: Linux Support Services, inc.
+Packager: Robert Vojta <vojta@ipex.cz>
+BuildRoot: /tmp/asterisk
+
+%description
+Asterisk is an Open Source PBX and telephony development platform that
+can both replace a conventional PBX and act as a platform for developing
+custom telephony applications for delivering dynamic content over a
+telephone similarly to how one can deliver dynamic content through a
+web browser using CGI and a web server.
+
+Asterisk talks to a variety of telephony hardware including BRI, PRI,
+POTS, and IP telephony clients using the Inter-Asterisk eXchange
+protocol (e.g. gnophone or miniphone). For more information and a
+current list of supported hardware, see www.asteriskpbx.com.
+
+%package devel
+Summary: Header files for building Asterisk modules
+Group: Development/Libraries
+
+%description devel
+This package contains the development header files that are needed
+to compile 3rd party modules.
+
+%post
+ln -sf /var/spool/asterisk/vm /var/lib/asterisk/sounds/vm
+
+%files
+#
+# Configuration files
+#
+%attr(0755,root,root) %dir /etc/asterisk
+%config(noreplace) %attr(0640,root,root) /etc/asterisk/*.conf
+%config(noreplace) %attr(0640,root,root) /etc/asterisk/*.adsi
+%config(noreplace) %attr(0640,root,root) /etc/asterisk/extensions.ael
+
+#
+# RedHat specific init script file
+#
+%attr(0755,root,root) /etc/rc.d/init.d/asterisk
+
+#
+# Modules
+#
+%attr(0755,root,root) %dir /usr/lib/asterisk
+%attr(0755,root,root) %dir /usr/lib/asterisk/modules
+%attr(0755,root,root) /usr/lib/asterisk/modules/*.so
+
+#
+# Asterisk
+#
+%attr(0755,root,root) /usr/sbin/asterisk
+%attr(0755,root,root) /usr/sbin/safe_asterisk
+%attr(0755,root,root) /usr/sbin/astgenkey
+%attr(0755,root,root) /usr/sbin/astman
+%attr(0755,root,root) /usr/sbin/autosupport
+%attr(0755,root,root) /usr/sbin/smsq
+%attr(0755,root,root) /usr/sbin/stereorize
+%attr(0755,root,root) /usr/sbin/streamplayer
+
+#
+# CDR Locations
+#
+%attr(0755,root,root) %dir /var/log/asterisk
+%attr(0755,root,root) %dir /var/log/asterisk/cdr-csv
+#
+# Running directories
+#
+%attr(0755,root,root) %dir /var/run
+#
+# Sound files
+#
+%attr(0755,root,root) %dir /var/lib/asterisk
+%attr(0755,root,root) %dir /var/lib/asterisk/sounds
+%attr(0644,root,root) /var/lib/asterisk/sounds/*.gsm
+%attr(0755,root,root) %dir /var/lib/asterisk/sounds/dictate
+%attr(0644,root,root) /var/lib/asterisk/sounds/dictate/*.gsm
+%attr(0755,root,root) %dir /var/lib/asterisk/sounds/digits
+%attr(0644,root,root) /var/lib/asterisk/sounds/digits/*.gsm
+%attr(0755,root,root) %dir /var/lib/asterisk/sounds/letters
+%attr(0644,root,root) /var/lib/asterisk/sounds/letters/*.gsm
+%attr(0755,root,root) %dir /var/lib/asterisk/sounds/phonetic
+%attr(0644,root,root) /var/lib/asterisk/sounds/phonetic/*.gsm
+%attr(0755,root,root) %dir /var/lib/asterisk/sounds/silence
+%attr(0644,root,root) /var/lib/asterisk/sounds/silence/*.gsm
+%attr(0755,root,root) %dir /var/lib/asterisk/mohmp3
+%attr(0644,root,root) /var/lib/asterisk/mohmp3/*
+%attr(0755,root,root) %dir /var/lib/asterisk/images
+%attr(0644,root,root) /var/lib/asterisk/images/*
+%attr(0755,root,root) %dir /var/lib/asterisk/keys
+%attr(0644,root,root) /var/lib/asterisk/keys/*
+%attr(0755,root,root) %dir /var/lib/asterisk/agi-bin
+%attr(0755,root,root) %dir /var/lib/asterisk/agi-bin/*
+#
+# Man page
+#
+%attr(0644,root,root) /usr/share/man/man8/asterisk.8
+%attr(0644,root,root) /usr/share/man/man8/astgenkey.8
+%attr(0644,root,root) /usr/share/man/man8/autosupport.8
+%attr(0644,root,root) /usr/share/man/man8/safe_asterisk.8
+
+#
+# Firmware
+#
+%attr(0755,root,root) %dir /var/lib/asterisk/firmware
+%attr(0755,root,root) %dir /var/lib/asterisk/firmware/iax
+%attr(0755,root,root) /var/lib/asterisk/firmware/iax/*.bin
+
+#
+# Example voicemail files
+#
+%attr(0755,root,root) %dir /var/spool/asterisk
+%attr(0755,root,root) %dir /var/spool/asterisk/voicemail
+%attr(0755,root,root) %dir /var/spool/asterisk/voicemail/default
+%attr(0755,root,root) %dir /var/spool/asterisk/voicemail/default/1234
+%attr(0755,root,root) %dir /var/spool/asterisk/voicemail/default/1234/INBOX
+%attr(0644,root,root) /var/spool/asterisk/voicemail/default/1234/*.gsm
+
+#
+# Misc other spool
+#
+%attr(0755,root,root) %dir /var/spool/asterisk/system
+%attr(0755,root,root) %dir /var/spool/asterisk/tmp
+
+%files devel
+#
+# Include files
+#
+%attr(0755,root,root) %dir %{_includedir}/asterisk
+%attr(0644,root,root) %{_includedir}/asterisk/*.h
diff --git a/trunk/redhat/rpmmacros b/trunk/redhat/rpmmacros
new file mode 100644
index 000000000..fbf2ee3ed
--- /dev/null
+++ b/trunk/redhat/rpmmacros
@@ -0,0 +1,3 @@
+%_tmppath /tmp/asterisk
+%_topdir %{_tmppath}/redhat
+%_rpmfilename %%{ARCH}/%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
diff --git a/trunk/redhat/rpmrc b/trunk/redhat/rpmrc
new file mode 100644
index 000000000..88c57d10b
--- /dev/null
+++ b/trunk/redhat/rpmrc
@@ -0,0 +1,3 @@
+#############################################################
+macrofiles: /usr/lib/rpm/macros:/usr/lib/rpm/%{_target}/macros:/etc/rpm/macros:/etc/rpm/%{_target}/macros:~/.rpmmacros:/tmp/asterisk/rpmmacros
+#############################################################
diff --git a/trunk/res/Makefile b/trunk/res/Makefile
new file mode 100644
index 000000000..72f4e8ae0
--- /dev/null
+++ b/trunk/res/Makefile
@@ -0,0 +1,53 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for resource modules
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=res
+MENUSELECT_CATEGORY=RES
+MENUSELECT_DESCRIPTION=Resource Modules
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ # cygwin has some dependencies among res_ things.
+ # We use order-only dependencies, and then add the libraries as required.
+ res_features.so: | res_monitor.so
+ res_features.so_LIBS:= -lres_monitor.so
+ #
+ res_agi.so: | res_speech.so
+ res_agi.so_LIBS:= -lres_speech.so
+endif
+
+ael/ael_lex.o: ael/ael_lex.c ../include/asterisk/ael_structs.h ael/ael.tab.h
+ael/ael_lex.o: ASTCFLAGS+=-I. -Iael
+
+ael/ael.tab.o: ael/ael.tab.c ael/ael.tab.h ../include/asterisk/ael_structs.h
+ael/ael.tab.o: ASTCFLAGS+=-I. -Iael -DYYENABLE_NLS=0
+
+$(if $(filter res_snmp,$(EMBEDDED_MODS)),modules.link,res_snmp.so): snmp/agent.o
+
+$(if $(filter res_ael_share,$(EMBEDDED_MODS)),modules.link,res_ael_share.so): ael/ael_lex.o ael/ael.tab.o ael/pval.o
+
+ael/ael_lex.c:
+ (cd ael; flex ael.flex; sed -i -e "/begin standard C headers/i#include \"asterisk.h\"" ael_lex.c)
+ (cd ael; sed 's@#if __STDC_VERSION__ >= 199901L@#if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L@' ael_lex.c > zz; mv zz ael_lex.c)
+
+ael/ael.tab.c ael/ael.tab.h:
+ (cd ael; bison -v -d ael.y)
+
+ael/pval.o: ael/pval.c
+
+clean::
+ rm -f snmp/*.o
+ rm -f ael/*.o
diff --git a/trunk/res/ael/ael.flex b/trunk/res/ael/ael.flex
new file mode 100644
index 000000000..cf2d36a2c
--- /dev/null
+++ b/trunk/res/ael/ael.flex
@@ -0,0 +1,694 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.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 Flex scanner description of tokens used in AEL2 .
+ *
+ */
+
+/*
+ * Start with flex options:
+ *
+ * %x describes the contexts we have: paren, semic and argg, plus INITIAL
+ */
+%x paren semic argg comment
+
+/* prefix used for various globally-visible functions and variables.
+ * This renames also yywrap, but since we do not use it, we just
+ * add option noyywrap to remove it.
+ */
+%option prefix="ael_yy"
+%option noyywrap 8bit
+
+/* yyfree normally just frees its arg. It can be null sometimes,
+ which some systems will complain about, so, we'll define our own version */
+%option noyyfree
+
+/* batch gives a bit more performance if we are using it in
+ * a non-interactive mode. We probably don't care much.
+ */
+%option batch
+
+/* outfile is the filename to be used instead of lex.yy.c */
+%option outfile="ael_lex.c"
+
+/*
+ * These are not supported in flex 2.5.4, but we need them
+ * at the moment:
+ * reentrant produces a thread-safe parser. Not 100% sure that
+ * we require it, though.
+ * bison-bridge passes an additional yylval argument to yylex().
+ * bison-locations is probably not needed.
+ */
+%option reentrant
+%option bison-bridge
+%option bison-locations
+
+%{
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#if defined(__Darwin__) || defined(__CYGWIN__)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+# include <glob.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/utils.h"
+#include "ael/ael.tab.h"
+#include "asterisk/ael_structs.h"
+
+/*
+ * A stack to keep track of matching brackets ( [ { } ] )
+ */
+static char pbcstack[400]; /* XXX missing size checks */
+static int pbcpos = 0;
+static void pbcpush(char x);
+static int pbcpop(char x);
+
+static int parencount = 0;
+
+/*
+ * current line, column and filename, updated as we read the input.
+ */
+static int my_lineno = 1; /* current line in the source */
+static int my_col = 1; /* current column in the source */
+char *my_file = 0; /* used also in the bison code */
+char *prev_word; /* XXX document it */
+
+#define MAX_INCLUDE_DEPTH 50
+
+/*
+ * flex is not too smart, and generates global functions
+ * without prototypes so the compiler may complain.
+ * To avoid that, we declare the prototypes here,
+ * even though these functions are not used.
+ */
+int ael_yyget_column (yyscan_t yyscanner);
+void ael_yyset_column (int column_no , yyscan_t yyscanner);
+
+int ael_yyparse (struct parse_io *);
+
+/*
+ * A stack to process include files.
+ * As we switch into the new file we need to store the previous
+ * state to restore it later.
+ */
+struct stackelement {
+ char *fname;
+ int lineno;
+ int colno;
+ glob_t globbuf; /* the current globbuf */
+ int globbuf_pos; /* where we are in the current globbuf */
+ YY_BUFFER_STATE bufstate;
+};
+
+static struct stackelement include_stack[MAX_INCLUDE_DEPTH];
+static int include_stack_index = 0;
+static void setup_filestack(char *fnamebuf, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t xscan, int create);
+
+/*
+ * if we use the @n feature of bison, we must supply the start/end
+ * location of tokens in the structure pointed by yylloc.
+ * Simple tokens are just assumed to be on the same line, so
+ * the line number is constant, and the column is incremented
+ * by the length of the token.
+ */
+#ifdef FLEX_BETA /* set for 2.5.33 */
+
+/* compute the total number of lines and columns in the text
+ * passed as argument.
+ */
+static void pbcwhere(const char *text, int *line, int *col )
+{
+ int loc_line = *line;
+ int loc_col = *col;
+ char c;
+ while ( (c = *text++) ) {
+ if ( c == '\t' ) {
+ loc_col += 8 - (loc_col % 8);
+ } else if ( c == '\n' ) {
+ loc_line++;
+ loc_col = 1;
+ } else
+ loc_col++;
+ }
+ *line = loc_line;
+ *col = loc_col;
+}
+
+#define STORE_POS do { \
+ yylloc->first_line = yylloc->last_line = my_lineno; \
+ yylloc->first_column=my_col; \
+ yylloc->last_column=my_col+yyleng-1; \
+ my_col+=yyleng; \
+ } while (0)
+
+#define STORE_LOC do { \
+ yylloc->first_line = my_lineno; \
+ yylloc->first_column=my_col; \
+ pbcwhere(yytext, &my_lineno, &my_col); \
+ yylloc->last_line = my_lineno; \
+ yylloc->last_column = my_col - 1; \
+ } while (0)
+#else
+#define STORE_POS
+#define STORE_LOC
+#endif
+%}
+
+
+NOPARENS ([^()\[\]\{\}]|\\[()\[\]\{\}])*
+
+NOARGG ([^(),\{\}\[\]]|\\[,()\[\]\{\}])*
+
+NOSEMIC ([^;()\{\}\[\]]|\\[;()\[\]\{\}])*
+
+HIBIT [\x80-\xff]
+
+%%
+
+\{ { STORE_POS; return LC;}
+\} { STORE_POS; return RC;}
+\( { STORE_POS; return LP;}
+\) { STORE_POS; return RP;}
+\; { STORE_POS; return SEMI;}
+\= { STORE_POS; return EQ;}
+\, { STORE_POS; return COMMA;}
+\: { STORE_POS; return COLON;}
+\& { STORE_POS; return AMPER;}
+\| { STORE_POS; return BAR;}
+\=\> { STORE_POS; return EXTENMARK;}
+\@ { STORE_POS; return AT;}
+\/\/[^\n]* {/*comment*/}
+context { STORE_POS; return KW_CONTEXT;}
+abstract { STORE_POS; return KW_ABSTRACT;}
+extend { STORE_POS; return KW_EXTEND;}
+macro { STORE_POS; return KW_MACRO;};
+globals { STORE_POS; return KW_GLOBALS;}
+local { STORE_POS; return KW_LOCAL;}
+ignorepat { STORE_POS; return KW_IGNOREPAT;}
+switch { STORE_POS; return KW_SWITCH;}
+if { STORE_POS; return KW_IF;}
+ifTime { STORE_POS; return KW_IFTIME;}
+random { STORE_POS; return KW_RANDOM;}
+regexten { STORE_POS; return KW_REGEXTEN;}
+hint { STORE_POS; return KW_HINT;}
+else { STORE_POS; return KW_ELSE;}
+goto { STORE_POS; return KW_GOTO;}
+jump { STORE_POS; return KW_JUMP;}
+return { STORE_POS; return KW_RETURN;}
+break { STORE_POS; return KW_BREAK;}
+continue { STORE_POS; return KW_CONTINUE;}
+for { STORE_POS; return KW_FOR;}
+while { STORE_POS; return KW_WHILE;}
+case { STORE_POS; return KW_CASE;}
+default { STORE_POS; return KW_DEFAULT;}
+pattern { STORE_POS; return KW_PATTERN;}
+catch { STORE_POS; return KW_CATCH;}
+switches { STORE_POS; return KW_SWITCHES;}
+eswitches { STORE_POS; return KW_ESWITCHES;}
+includes { STORE_POS; return KW_INCLUDES;}
+"/*" { BEGIN(comment); my_col += 2; }
+
+<comment>[^*\n]* { my_col += yyleng; }
+<comment>[^*\n]*\n { ++my_lineno; my_col=1;}
+<comment>"*"+[^*/\n]* { my_col += yyleng; }
+<comment>"*"+[^*/\n]*\n { ++my_lineno; my_col=1;}
+<comment>"*/" { my_col += 2; BEGIN(INITIAL); }
+
+\n { my_lineno++; my_col = 1; }
+[ ]+ { my_col += yyleng; }
+[\t]+ { my_col += (yyleng*8)-(my_col%8); }
+
+([-a-zA-Z0-9'"_/.\<\>\*\\\+!$#\[\]]|{HIBIT})([-a-zA-Z0-9'"_/.!\*\\\+\<\>\{\}$#\[\]]|{HIBIT})* {
+ STORE_POS;
+ yylval->str = strdup(yytext);
+ prev_word = yylval->str;
+ return word;
+ }
+
+
+
+ /*
+ * context used for arguments of if_head, random_head, switch_head,
+ * for (last statement), while (XXX why not iftime_head ?).
+ * End with the matching parentheses.
+ * A comma at the top level is valid here, unlike in argg where it
+ * is an argument separator so it must be returned as a token.
+ */
+<paren>{NOPARENS}\) {
+ if ( pbcpop(')') ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ prev_word = 0;
+ return word;
+ }
+ parencount--;
+ if ( parencount >= 0) {
+ yymore();
+ } else {
+ STORE_LOC;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */
+ unput(')');
+ BEGIN(0);
+ return word;
+ }
+ }
+
+<paren>{NOPARENS}[\(\[\{] {
+ char c = yytext[yyleng-1];
+ if (c == '(')
+ parencount++;
+ pbcpush(c);
+ yymore();
+ }
+
+<paren>{NOPARENS}[\]\}] {
+ char c = yytext[yyleng-1];
+ if ( pbcpop(c)) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n",
+ my_file, my_lineno, my_col, c);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+ yymore();
+ }
+
+
+ /*
+ * handlers for arguments to a macro or application calls.
+ * We enter this context when we find the initial '(' and
+ * stay here until we close all matching parentheses,
+ * and find the comma (argument separator) or the closing ')'
+ * of the (external) call, which happens when parencount == 0
+ * before the decrement.
+ */
+<argg>{NOARGG}[\(\[\{] {
+ char c = yytext[yyleng-1];
+ if (c == '(')
+ parencount++;
+ pbcpush(c);
+ yymore();
+ }
+
+<argg>{NOARGG}\) {
+ if ( pbcpop(')') ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression!\n", my_file, my_lineno, my_col);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+
+ parencount--;
+ if( parencount >= 0){
+ yymore();
+ } else {
+ STORE_LOC;
+ BEGIN(0);
+ if ( !strcmp(yytext, ")") )
+ return RP;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */
+ unput(')');
+ return word;
+ }
+ }
+
+<argg>{NOARGG}\, {
+ if( parencount != 0) { /* printf("Folding in a comma!\n"); */
+ yymore();
+ } else {
+ STORE_LOC;
+ if( !strcmp(yytext,"," ) )
+ return COMMA;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0';
+ unput(',');
+ return word;
+ }
+ }
+
+<argg>{NOARGG}[\]\}] {
+ char c = yytext[yyleng-1];
+ if ( pbcpop(c) ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+ yymore();
+ }
+
+ /*
+ * context used to find tokens in the right hand side of assignments,
+ * or in the first and second operand of a 'for'. As above, match
+ * commas and use ';' as a separator (hence return it as a separate token).
+ */
+<semic>{NOSEMIC}[\(\[\{] {
+ char c = yytext[yyleng-1];
+ yymore();
+ pbcpush(c);
+ }
+
+<semic>{NOSEMIC}[\)\]\}] {
+ char c = yytext[yyleng-1];
+ if ( pbcpop(c) ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+ yymore();
+ }
+
+<semic>{NOSEMIC}; {
+ STORE_LOC;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0';
+ unput(';');
+ BEGIN(0);
+ return word;
+ }
+
+\#include[ \t]+\"[^\"]+\" {
+ char fnamebuf[1024],*p1,*p2;
+ int glob_ret;
+ glob_t globbuf; /* the current globbuf */
+ int globbuf_pos = -1; /* where we are in the current globbuf */
+ globbuf.gl_offs = 0; /* initialize it to silence gcc */
+
+ p1 = strchr(yytext,'"');
+ p2 = strrchr(yytext,'"');
+ if ( include_stack_index >= MAX_INCLUDE_DEPTH ) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Includes nested too deeply! Wow!!! How did you do that?\n", my_file, my_lineno, my_col);
+ } else if ( (int)(p2-p1) > sizeof(fnamebuf) - 1 ) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Filename is incredibly way too long (%d chars!). Inclusion ignored!\n", my_file, my_lineno, my_col, yyleng - 10);
+ } else {
+ strncpy(fnamebuf, p1+1, p2-p1-1);
+ fnamebuf[p2-p1-1] = 0;
+ if (fnamebuf[0] != '/') {
+ char fnamebuf2[1024];
+ snprintf(fnamebuf2,sizeof(fnamebuf2), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, fnamebuf);
+ ast_copy_string(fnamebuf,fnamebuf2,sizeof(fnamebuf));
+ }
+#ifdef SOLARIS
+ glob_ret = glob(fnamebuf, GLOB_NOCHECK, NULL, &globbuf);
+#else
+ glob_ret = glob(fnamebuf, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
+#endif
+ if (glob_ret == GLOB_NOSPACE) {
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Not enough memory\n", fnamebuf);
+ } else if (glob_ret == GLOB_ABORTED) {
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Read error\n", fnamebuf);
+ } else if (glob_ret == GLOB_NOMATCH) {
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: No matches!\n", fnamebuf);
+ } else {
+ globbuf_pos = 0;
+ }
+ }
+ if (globbuf_pos > -1) {
+ setup_filestack(fnamebuf, sizeof(fnamebuf), &globbuf, 0, yyscanner, 1);
+ }
+ }
+
+
+<<EOF>> {
+ char fnamebuf[2048];
+ if (include_stack_index > 0 && include_stack[include_stack_index-1].globbuf_pos < include_stack[include_stack_index-1].globbuf.gl_pathc-1) {
+ free(my_file);
+ my_file = 0;
+ yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
+ include_stack[include_stack_index-1].globbuf_pos++;
+ setup_filestack(fnamebuf, sizeof(fnamebuf), &include_stack[include_stack_index-1].globbuf, include_stack[include_stack_index-1].globbuf_pos, yyscanner, 0);
+ /* finish this */
+
+ } else {
+ if (include_stack[include_stack_index].fname) {
+ free(include_stack[include_stack_index].fname);
+ include_stack[include_stack_index].fname = 0;
+ }
+ if ( --include_stack_index < 0 ) {
+ yyterminate();
+ } else {
+ if (my_file) {
+ free(my_file);
+ my_file = 0;
+ }
+ globfree(&include_stack[include_stack_index].globbuf);
+ include_stack[include_stack_index].globbuf_pos = -1;
+
+ yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
+ yy_switch_to_buffer(include_stack[include_stack_index].bufstate, yyscanner );
+ my_lineno = include_stack[include_stack_index].lineno;
+ my_col = include_stack[include_stack_index].colno;
+ my_file = strdup(include_stack[include_stack_index].fname);
+ }
+ }
+ }
+
+%%
+
+static void pbcpush(char x)
+{
+ pbcstack[pbcpos++] = x;
+}
+
+void ael_yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ free( (char*) ptr );
+}
+
+static int pbcpop(char x)
+{
+ if ( ( x == ')' && pbcstack[pbcpos-1] == '(' )
+ || ( x == ']' && pbcstack[pbcpos-1] == '[' )
+ || ( x == '}' && pbcstack[pbcpos-1] == '{' )) {
+ pbcpos--;
+ return 0;
+ }
+ return 1; /* error */
+}
+
+static int c_prevword(void)
+{
+ char *c = prev_word;
+ if (c == NULL)
+ return 0;
+ while ( *c ) {
+ switch (*c) {
+ case '{':
+ case '[':
+ case '(':
+ pbcpush(*c);
+ break;
+ case '}':
+ case ']':
+ case ')':
+ if (pbcpop(*c))
+ return 1;
+ break;
+ }
+ c++;
+ }
+ return 0;
+}
+
+
+/*
+ * The following three functions, reset_*, are used in the bison
+ * code to switch context. As a consequence, we need to
+ * declare them global and add a prototype so that the
+ * compiler does not complain.
+ *
+ * NOTE: yyg is declared because it is used in the BEGIN macros,
+ * though that should be hidden as the macro changes
+ * depending on the flex options that we use - in particular,
+ * %reentrant changes the way the macro is declared;
+ * without %reentrant, BEGIN uses yystart instead of yyg
+ */
+
+void reset_parencount(yyscan_t yyscanner );
+void reset_parencount(yyscan_t yyscanner )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ parencount = 0;
+ pbcpos = 0;
+ pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */
+ c_prevword();
+ BEGIN(paren);
+}
+
+void reset_semicount(yyscan_t yyscanner );
+void reset_semicount(yyscan_t yyscanner )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ pbcpos = 0;
+ BEGIN(semic);
+}
+
+void reset_argcount(yyscan_t yyscanner );
+void reset_argcount(yyscan_t yyscanner )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ parencount = 0;
+ pbcpos = 0;
+ pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */
+ c_prevword();
+ BEGIN(argg);
+}
+
+/* used elsewhere, but some local vars */
+struct pval *ael2_parse(char *filename, int *errors)
+{
+ struct pval *pval;
+ struct parse_io *io;
+ char *buffer;
+ struct stat stats;
+ FILE *fin;
+
+ /* extern int ael_yydebug; */
+
+ io = calloc(sizeof(struct parse_io),1);
+ /* reset the global counters */
+ prev_word = 0;
+ my_lineno = 1;
+ include_stack_index=0;
+ my_col = 0;
+ /* ael_yydebug = 1; */
+ ael_yylex_init(&io->scanner);
+ fin = fopen(filename,"r");
+ if ( !fin ) {
+ ast_log(LOG_ERROR,"File %s could not be opened\n", filename);
+ *errors = 1;
+ return 0;
+ }
+ if (my_file)
+ free(my_file);
+ my_file = strdup(filename);
+ stat(filename, &stats);
+ buffer = (char*)malloc(stats.st_size+2);
+ fread(buffer, 1, stats.st_size, fin);
+ buffer[stats.st_size]=0;
+ fclose(fin);
+
+ ael_yy_scan_string (buffer ,io->scanner);
+ ael_yyset_lineno(1 , io->scanner);
+
+ /* ael_yyset_in (fin , io->scanner); OLD WAY */
+
+ ael_yyparse(io);
+
+
+ pval = io->pval;
+ *errors = io->syntax_error_count;
+
+ ael_yylex_destroy(io->scanner);
+ free(buffer);
+ free(io);
+
+ return pval;
+}
+
+static void setup_filestack(char *fnamebuf2, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t yyscanner, int create)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ int error, i;
+ FILE *in1;
+ char fnamebuf[2048];
+
+ if (globbuf && globbuf->gl_pathv && globbuf->gl_pathc > 0)
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz);
+#else
+ ast_copy_string(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz);
+#endif
+ else {
+ ast_log(LOG_ERROR,"Include file name not present!\n");
+ return;
+ }
+ for (i=0; i<include_stack_index; i++) {
+ if ( !strcmp(fnamebuf,include_stack[i].fname )) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Nice Try!!! But %s has already been included (perhaps by another file), and would cause an infinite loop of file inclusions!!! Include directive ignored\n",
+ my_file, my_lineno, my_col, fnamebuf);
+ break;
+ }
+ }
+ error = 1;
+ if (i == include_stack_index)
+ error = 0; /* we can use this file */
+ if ( !error ) { /* valid file name */
+ /* relative vs. absolute */
+ if (fnamebuf[0] != '/')
+ snprintf(fnamebuf2, fnamebuf_siz, "%s/%s", ast_config_AST_CONFIG_DIR, fnamebuf);
+ else
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(fnamebuf2, fnamebuf, fnamebuf_siz);
+#else
+ ast_copy_string(fnamebuf2, fnamebuf, fnamebuf_siz);
+#endif
+ in1 = fopen( fnamebuf2, "r" );
+
+ if ( ! in1 ) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Couldn't find the include file: %s; ignoring the Include directive!\n", my_file, my_lineno, my_col, fnamebuf2);
+ } else {
+ char *buffer;
+ struct stat stats;
+ stat(fnamebuf2, &stats);
+ buffer = (char*)malloc(stats.st_size+1);
+ fread(buffer, 1, stats.st_size, in1);
+ buffer[stats.st_size] = 0;
+ ast_log(LOG_NOTICE," --Read in included file %s, %d chars\n",fnamebuf2, (int)stats.st_size);
+ fclose(in1);
+ if (my_file)
+ free(my_file);
+ my_file = strdup(fnamebuf2);
+ include_stack[include_stack_index].fname = strdup(my_file);
+ include_stack[include_stack_index].lineno = my_lineno;
+ include_stack[include_stack_index].colno = my_col+yyleng;
+ if (create)
+ include_stack[include_stack_index].globbuf = *globbuf;
+
+ include_stack[include_stack_index].globbuf_pos = 0;
+
+ include_stack[include_stack_index].bufstate = YY_CURRENT_BUFFER;
+ if (create)
+ include_stack_index++;
+ yy_switch_to_buffer(ael_yy_scan_string (buffer ,yyscanner),yyscanner);
+ free(buffer);
+ my_lineno = 1;
+ my_col = 1;
+ BEGIN(INITIAL);
+ }
+ }
+}
diff --git a/trunk/res/ael/ael.tab.c b/trunk/res/ael/ael.tab.c
new file mode 100644
index 000000000..aa1ec0fad
--- /dev/null
+++ b/trunk/res/ael/ael.tab.c
@@ -0,0 +1,3413 @@
+/* A Bison parser, made by GNU Bison 2.1a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ 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, 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.1a"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names. */
+#define yyparse ael_yyparse
+#define yylex ael_yylex
+#define yyerror ael_yyerror
+#define yylval ael_yylval
+#define yychar ael_yychar
+#define yydebug ael_yydebug
+#define yynerrs ael_yynerrs
+#define yylloc ael_yylloc
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ KW_CONTEXT = 258,
+ LC = 259,
+ RC = 260,
+ LP = 261,
+ RP = 262,
+ SEMI = 263,
+ EQ = 264,
+ COMMA = 265,
+ COLON = 266,
+ AMPER = 267,
+ BAR = 268,
+ AT = 269,
+ KW_MACRO = 270,
+ KW_GLOBALS = 271,
+ KW_IGNOREPAT = 272,
+ KW_SWITCH = 273,
+ KW_IF = 274,
+ KW_IFTIME = 275,
+ KW_ELSE = 276,
+ KW_RANDOM = 277,
+ KW_ABSTRACT = 278,
+ KW_EXTEND = 279,
+ EXTENMARK = 280,
+ KW_GOTO = 281,
+ KW_JUMP = 282,
+ KW_RETURN = 283,
+ KW_BREAK = 284,
+ KW_CONTINUE = 285,
+ KW_REGEXTEN = 286,
+ KW_HINT = 287,
+ KW_FOR = 288,
+ KW_WHILE = 289,
+ KW_CASE = 290,
+ KW_PATTERN = 291,
+ KW_DEFAULT = 292,
+ KW_CATCH = 293,
+ KW_SWITCHES = 294,
+ KW_ESWITCHES = 295,
+ KW_INCLUDES = 296,
+ KW_LOCAL = 297,
+ word = 298
+ };
+#endif
+/* Tokens. */
+#define KW_CONTEXT 258
+#define LC 259
+#define RC 260
+#define LP 261
+#define RP 262
+#define SEMI 263
+#define EQ 264
+#define COMMA 265
+#define COLON 266
+#define AMPER 267
+#define BAR 268
+#define AT 269
+#define KW_MACRO 270
+#define KW_GLOBALS 271
+#define KW_IGNOREPAT 272
+#define KW_SWITCH 273
+#define KW_IF 274
+#define KW_IFTIME 275
+#define KW_ELSE 276
+#define KW_RANDOM 277
+#define KW_ABSTRACT 278
+#define KW_EXTEND 279
+#define EXTENMARK 280
+#define KW_GOTO 281
+#define KW_JUMP 282
+#define KW_RETURN 283
+#define KW_BREAK 284
+#define KW_CONTINUE 285
+#define KW_REGEXTEN 286
+#define KW_HINT 287
+#define KW_FOR 288
+#define KW_WHILE 289
+#define KW_CASE 290
+#define KW_PATTERN 291
+#define KW_DEFAULT 292
+#define KW_CATCH 293
+#define KW_SWITCHES 294
+#define KW_ESWITCHES 295
+#define KW_INCLUDES 296
+#define KW_LOCAL 297
+#define word 298
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "ael.y"
+
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.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 Bison Grammar description of AEL2.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/ael_structs.h"
+
+pval * linku1(pval *head, pval *tail);
+static void set_dads(pval *dad, pval *child_list);
+void reset_parencount(yyscan_t yyscanner);
+void reset_semicount(yyscan_t yyscanner);
+void reset_argcount(yyscan_t yyscanner );
+
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+
+extern char *my_file;
+#ifdef AAL_ARGCHECK
+int ael_is_funcname(char *name);
+#endif
+static char *ael_token_subst(const char *mess);
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 54 "ael.y"
+{
+ int intval; /* integer value, typically flags */
+ char *str; /* strings */
+ struct pval *pval; /* full objects */
+}
+/* Line 198 of yacc.c. */
+#line 238 "ael.tab.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+#line 60 "ael.y"
+
+ /* declaring these AFTER the union makes things a lot simpler! */
+void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
+int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner);
+
+/* create a new object with start-end marker */
+pval *npval(pvaltype type, int first_line, int last_line,
+ int first_column, int last_column);
+
+/* create a new object with start-end marker, simplified interface.
+ * Must be declared here because YYLTYPE is not known before
+ */
+static pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last);
+
+/* another frontend for npval, this time for a string */
+static pval *nword(char *string, YYLTYPE *pos);
+
+/* update end position of an object, return the object */
+static pval *update_last(pval *, YYLTYPE *);
+
+
+/* Line 221 of yacc.c. */
+#line 283 "ael.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# ifdef __cplusplus
+extern "C" {
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifdef __cplusplus
+}
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+ + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 17
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 327
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 44
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 56
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 142
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 288
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 298
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 5, 7, 10, 13, 15, 17, 19,
+ 21, 23, 25, 32, 34, 35, 37, 40, 43, 52,
+ 57, 58, 61, 64, 65, 71, 72, 79, 80, 82,
+ 86, 89, 90, 93, 96, 98, 100, 102, 104, 106,
+ 108, 110, 113, 115, 120, 124, 130, 135, 143, 152,
+ 153, 156, 159, 165, 167, 175, 176, 181, 184, 187,
+ 192, 194, 197, 199, 202, 206, 210, 212, 215, 219,
+ 221, 224, 228, 234, 238, 240, 242, 246, 250, 253,
+ 254, 255, 256, 269, 273, 275, 279, 282, 285, 286,
+ 292, 295, 298, 301, 305, 307, 310, 311, 313, 317,
+ 321, 327, 333, 339, 345, 346, 349, 352, 357, 358,
+ 364, 368, 369, 373, 377, 380, 382, 383, 385, 386,
+ 390, 391, 394, 399, 403, 408, 409, 412, 414, 416,
+ 422, 427, 432, 433, 437, 443, 446, 448, 452, 455,
+ 459, 462, 467
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 45, 0, -1, 46, -1, 47, -1, 46, 47, -1,
+ 46, 1, -1, 49, -1, 51, -1, 52, -1, 8,
+ -1, 43, -1, 37, -1, 50, 3, 48, 4, 59,
+ 5, -1, 23, -1, -1, 24, -1, 24, 23, -1,
+ 23, 24, -1, 15, 43, 6, 58, 7, 4, 92,
+ 5, -1, 16, 4, 53, 5, -1, -1, 54, 53,
+ -1, 1, 53, -1, -1, 43, 9, 55, 43, 8,
+ -1, -1, 42, 43, 9, 57, 43, 8, -1, -1,
+ 43, -1, 58, 10, 43, -1, 58, 1, -1, -1,
+ 60, 59, -1, 1, 59, -1, 62, -1, 99, -1,
+ 94, -1, 95, -1, 61, -1, 54, -1, 56, -1,
+ 43, 1, -1, 8, -1, 17, 25, 43, 8, -1,
+ 43, 25, 74, -1, 43, 14, 43, 25, 74, -1,
+ 31, 43, 25, 74, -1, 32, 6, 70, 7, 43,
+ 25, 74, -1, 31, 32, 6, 70, 7, 43, 25,
+ 74, -1, -1, 74, 63, -1, 1, 63, -1, 71,
+ 11, 71, 11, 71, -1, 43, -1, 64, 13, 71,
+ 13, 71, 13, 71, -1, -1, 6, 67, 69, 7,
+ -1, 19, 66, -1, 22, 66, -1, 20, 6, 65,
+ 7, -1, 43, -1, 43, 43, -1, 43, -1, 70,
+ 43, -1, 70, 11, 43, -1, 70, 12, 43, -1,
+ 43, -1, 43, 43, -1, 43, 43, 43, -1, 43,
+ -1, 43, 43, -1, 72, 11, 43, -1, 18, 66,
+ 4, 90, 5, -1, 4, 63, 5, -1, 54, -1,
+ 56, -1, 26, 80, 8, -1, 27, 82, 8, -1,
+ 43, 11, -1, -1, -1, -1, 33, 6, 75, 43,
+ 8, 76, 43, 8, 77, 43, 7, 74, -1, 34,
+ 66, 74, -1, 73, -1, 12, 83, 8, -1, 87,
+ 8, -1, 43, 8, -1, -1, 87, 9, 78, 43,
+ 8, -1, 29, 8, -1, 28, 8, -1, 30, 8,
+ -1, 68, 74, 79, -1, 8, -1, 21, 74, -1,
+ -1, 72, -1, 72, 13, 72, -1, 72, 10, 72,
+ -1, 72, 13, 72, 13, 72, -1, 72, 10, 72,
+ 10, 72, -1, 37, 13, 72, 13, 72, -1, 37,
+ 10, 72, 10, 72, -1, -1, 10, 43, -1, 72,
+ 81, -1, 72, 81, 14, 48, -1, -1, 43, 6,
+ 84, 89, 7, -1, 43, 6, 7, -1, -1, 43,
+ 6, 86, -1, 85, 89, 7, -1, 85, 7, -1,
+ 43, -1, -1, 69, -1, -1, 89, 10, 88, -1,
+ -1, 91, 90, -1, 35, 43, 11, 63, -1, 37,
+ 11, 63, -1, 36, 43, 11, 63, -1, -1, 93,
+ 92, -1, 74, -1, 99, -1, 38, 43, 4, 63,
+ 5, -1, 39, 4, 96, 5, -1, 40, 4, 96,
+ 5, -1, -1, 43, 8, 96, -1, 43, 14, 43,
+ 8, 96, -1, 1, 96, -1, 48, -1, 48, 13,
+ 65, -1, 97, 8, -1, 98, 97, 8, -1, 98,
+ 1, -1, 41, 4, 98, 5, -1, 41, 4, 5,
+ -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 186, 186, 189, 190, 191, 194, 195, 196, 197,
+ 200, 201, 204, 213, 214, 215, 216, 217, 220, 226,
+ 232, 233, 234, 237, 237, 243, 243, 250, 251, 252,
+ 253, 256, 257, 258, 261, 262, 263, 264, 265, 266,
+ 267, 268, 269, 272, 277, 281, 289, 294, 299, 308,
+ 309, 310, 316, 321, 325, 333, 333, 337, 340, 343,
+ 354, 355, 362, 363, 367, 371, 377, 378, 383, 391,
+ 392, 396, 402, 411, 414, 415, 416, 419, 422, 425,
+ 426, 427, 425, 433, 437, 438, 439, 440, 443, 443,
+ 476, 477, 478, 479, 483, 486, 487, 490, 491, 494,
+ 497, 501, 505, 509, 515, 516, 520, 523, 529, 529,
+ 534, 542, 542, 553, 560, 563, 564, 567, 568, 571,
+ 574, 575, 578, 582, 586, 592, 593, 596, 597, 598,
+ 604, 609, 614, 615, 616, 618, 621, 622, 629, 630,
+ 631, 634, 637
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "KW_CONTEXT", "LC", "RC", "LP", "RP",
+ "SEMI", "EQ", "COMMA", "COLON", "AMPER", "BAR", "AT", "KW_MACRO",
+ "KW_GLOBALS", "KW_IGNOREPAT", "KW_SWITCH", "KW_IF", "KW_IFTIME",
+ "KW_ELSE", "KW_RANDOM", "KW_ABSTRACT", "KW_EXTEND", "EXTENMARK",
+ "KW_GOTO", "KW_JUMP", "KW_RETURN", "KW_BREAK", "KW_CONTINUE",
+ "KW_REGEXTEN", "KW_HINT", "KW_FOR", "KW_WHILE", "KW_CASE", "KW_PATTERN",
+ "KW_DEFAULT", "KW_CATCH", "KW_SWITCHES", "KW_ESWITCHES", "KW_INCLUDES",
+ "KW_LOCAL", "word", "$accept", "file", "objects", "object",
+ "context_name", "context", "opt_abstract", "macro", "globals",
+ "global_statements", "assignment", "@1", "local_assignment", "@2",
+ "arglist", "elements", "element", "ignorepat", "extension", "statements",
+ "timerange", "timespec", "test_expr", "@3", "if_like_head", "word_list",
+ "hint_word", "word3_list", "goto_word", "switch_statement", "statement",
+ "@4", "@5", "@6", "@7", "opt_else", "target", "opt_pri", "jumptarget",
+ "macro_call", "@8", "application_call_head", "@9", "application_call",
+ "opt_word", "eval_arglist", "case_statements", "case_statement",
+ "macro_statements", "macro_statement", "switches", "eswitches",
+ "switchlist", "included_entry", "includeslist", "includes", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 44, 45, 46, 46, 46, 47, 47, 47, 47,
+ 48, 48, 49, 50, 50, 50, 50, 50, 51, 52,
+ 53, 53, 53, 55, 54, 57, 56, 58, 58, 58,
+ 58, 59, 59, 59, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 61, 62, 62, 62, 62, 62, 63,
+ 63, 63, 64, 64, 65, 67, 66, 68, 68, 68,
+ 69, 69, 70, 70, 70, 70, 71, 71, 71, 72,
+ 72, 72, 73, 74, 74, 74, 74, 74, 74, 75,
+ 76, 77, 74, 74, 74, 74, 74, 74, 78, 74,
+ 74, 74, 74, 74, 74, 79, 79, 80, 80, 80,
+ 80, 80, 80, 80, 81, 81, 82, 82, 84, 83,
+ 83, 86, 85, 87, 87, 88, 88, 89, 89, 89,
+ 90, 90, 91, 91, 91, 92, 92, 93, 93, 93,
+ 94, 95, 96, 96, 96, 96, 97, 97, 98, 98,
+ 98, 99, 99
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 2, 2, 1, 1, 1, 1,
+ 1, 1, 6, 1, 0, 1, 2, 2, 8, 4,
+ 0, 2, 2, 0, 5, 0, 6, 0, 1, 3,
+ 2, 0, 2, 2, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 3, 5, 4, 7, 8, 0,
+ 2, 2, 5, 1, 7, 0, 4, 2, 2, 4,
+ 1, 2, 1, 2, 3, 3, 1, 2, 3, 1,
+ 2, 3, 5, 3, 1, 1, 3, 3, 2, 0,
+ 0, 0, 12, 3, 1, 3, 2, 2, 0, 5,
+ 2, 2, 2, 3, 1, 2, 0, 1, 3, 3,
+ 5, 5, 5, 5, 0, 2, 2, 4, 0, 5,
+ 3, 0, 3, 3, 2, 1, 0, 1, 0, 3,
+ 0, 2, 4, 3, 4, 0, 2, 1, 1, 5,
+ 4, 4, 0, 3, 5, 2, 1, 3, 2, 3,
+ 2, 4, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 14, 9, 0, 0, 13, 15, 0, 0, 3, 6,
+ 0, 7, 8, 0, 0, 17, 16, 1, 5, 4,
+ 0, 27, 0, 0, 0, 0, 11, 10, 0, 28,
+ 0, 22, 23, 19, 21, 0, 30, 0, 0, 0,
+ 0, 42, 0, 0, 0, 0, 0, 0, 0, 0,
+ 39, 40, 0, 0, 38, 34, 36, 37, 35, 125,
+ 29, 0, 33, 0, 0, 0, 0, 0, 0, 0,
+ 0, 41, 0, 0, 12, 32, 0, 94, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 74, 75, 0, 84, 127, 118, 0, 0,
+ 125, 128, 24, 0, 0, 0, 62, 0, 0, 0,
+ 0, 0, 142, 136, 0, 0, 25, 0, 44, 0,
+ 0, 0, 0, 0, 55, 0, 57, 0, 58, 0,
+ 69, 97, 0, 104, 0, 91, 90, 92, 79, 0,
+ 0, 111, 87, 78, 96, 114, 60, 117, 0, 86,
+ 88, 18, 126, 43, 0, 46, 0, 0, 0, 63,
+ 135, 0, 0, 130, 131, 0, 138, 140, 141, 0,
+ 0, 0, 51, 73, 50, 108, 85, 0, 120, 53,
+ 0, 0, 0, 0, 0, 70, 0, 0, 0, 76,
+ 0, 106, 77, 0, 83, 0, 112, 0, 93, 61,
+ 113, 116, 0, 0, 0, 64, 65, 133, 0, 137,
+ 139, 0, 45, 110, 118, 0, 0, 0, 0, 0,
+ 120, 67, 0, 59, 0, 0, 0, 99, 71, 98,
+ 105, 0, 0, 0, 95, 115, 119, 0, 0, 0,
+ 0, 26, 0, 56, 0, 0, 0, 72, 121, 68,
+ 66, 0, 0, 0, 0, 0, 0, 107, 80, 129,
+ 89, 0, 47, 134, 109, 0, 0, 123, 0, 0,
+ 103, 102, 101, 100, 0, 48, 122, 124, 0, 52,
+ 0, 0, 81, 54, 0, 0, 0, 82
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 6, 7, 8, 113, 9, 10, 11, 12, 24,
+ 92, 39, 93, 170, 30, 52, 53, 54, 55, 120,
+ 180, 181, 125, 177, 94, 147, 107, 182, 131, 95,
+ 121, 193, 274, 284, 202, 198, 132, 191, 134, 123,
+ 214, 97, 196, 98, 236, 148, 219, 220, 99, 100,
+ 56, 57, 110, 114, 115, 58
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -219
+static const yytype_int16 yypact[] =
+{
+ 102, -219, -25, 40, 24, 29, 70, 249, -219, -219,
+ 74, -219, -219, 107, 11, -219, -219, -219, -219, -219,
+ -6, 65, 11, 137, 148, 11, -219, -219, 152, -219,
+ 87, -219, -219, -219, -219, 123, -219, 173, 126, 136,
+ 123, -219, 159, -11, 186, 190, 194, 196, 158, 86,
+ -219, -219, 203, 123, -219, -219, -219, -219, -219, 177,
+ -219, 201, -219, 170, 208, 191, 179, 12, 12, 19,
+ 214, -219, 181, 213, -219, -219, 115, -219, 183, 222,
+ 222, 223, 222, 36, 187, 226, 228, 229, 232, 222,
+ 202, 182, -219, -219, 213, -219, -219, 16, 113, 239,
+ 177, -219, -219, 240, 179, 213, -219, 22, 12, 101,
+ 246, 248, -219, 241, 250, 10, -219, 234, -219, 56,
+ 255, 56, 256, 253, -219, 259, -219, 224, -219, 26,
+ 225, 157, 258, 119, 261, -219, -219, -219, -219, 213,
+ 266, -219, -219, -219, 254, -219, 231, -219, 59, -219,
+ -219, -219, -219, -219, 60, -219, 233, 235, 236, -219,
+ -219, 12, 237, -219, -219, 224, -219, -219, -219, 263,
+ 238, 213, -219, -219, -219, 270, -219, 242, 124, -1,
+ 269, 276, 273, 187, 187, -219, 187, 243, 187, -219,
+ 244, 274, -219, 247, -219, 115, -219, 213, -219, -219,
+ -219, 251, 252, 257, 264, -219, -219, -219, 283, -219,
+ -219, 284, -219, -219, 242, 286, 260, 262, 285, 292,
+ 124, 265, 267, -219, 267, 172, 15, 176, -219, 165,
+ -219, -6, 290, 294, -219, -219, -219, 293, 277, 213,
+ 12, -219, 129, -219, 295, 296, 56, -219, -219, -219,
+ 268, 291, 298, 187, 187, 187, 187, -219, -219, -219,
+ -219, 213, -219, -219, -219, 56, 56, -219, 267, 267,
+ 301, 301, 301, 301, 271, -219, -219, -219, 300, -219,
+ 307, 267, -219, -219, 275, 309, 213, -219
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -219, -219, -219, 310, -19, -219, -219, -219, -219, 125,
+ 5, -219, -15, -219, -219, -31, -219, -219, -219, -114,
+ -219, 154, 25, -219, -219, 143, 217, -218, -82, -219,
+ -59, -219, -219, -219, -219, -219, -219, -219, -219, -219,
+ -219, -219, -219, -219, -219, 108, 103, -219, 227, -219,
+ -219, -219, -65, 209, -219, -51
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -133
+static const yytype_int16 yytable[] =
+{
+ 96, 28, 133, 111, 251, 172, 252, 174, 101, 62,
+ -66, 167, 22, 108, 118, 168, -20, -132, 13, 25,
+ 51, 64, 75, 145, 112, 51, 187, 25, 254, 156,
+ 25, 26, 65, 157, 158, 144, 183, 27, 51, 184,
+ 50, 96, 221, 160, 14, 50, 155, 26, 15, 101,
+ 278, 279, 16, 27, 23, 109, 26, 119, 50, 146,
+ 76, -49, 27, 283, 77, 159, 200, 203, 78, 201,
+ 17, 157, 158, 129, 79, 80, 81, 20, 82, 130,
+ 194, 233, 83, 84, 85, 86, 87, 71, 36, 88,
+ 89, -49, -49, -49, 37, 32, 207, 38, 48, 91,
+ 72, 225, 226, 159, 227, 126, 229, 128, 29, 161,
+ 1, 73, 212, 21, 139, 162, 119, 2, 3, 76,
+ -49, 149, 150, 77, 40, 4, 5, 78, -31, 190,
+ 187, 41, 267, 79, 80, 81, 264, 82, 234, 201,
+ 42, 83, 84, 85, 86, 87, 32, 31, 88, 89,
+ 34, 276, 277, 33, 43, 44, 35, 48, 91, 216,
+ 217, 218, 45, 46, 47, 48, 49, 186, 187, 60,
+ 188, 270, 271, 272, 273, 263, 187, 59, 256, 61,
+ 262, 76, 253, 187, 63, 77, 255, 187, 141, 78,
+ 142, 32, 66, 143, 67, 79, 80, 81, 68, 82,
+ 69, 70, 275, 83, 84, 85, 86, 87, 74, 102,
+ 88, 89, 257, 103, 104, 90, 105, 76, 47, 48,
+ 91, 77, 106, 116, 117, 78, 122, 287, 124, 127,
+ 130, 79, 80, 81, 135, 82, 136, 137, 138, 83,
+ 84, 85, 86, 87, 151, 140, 88, 89, 153, -2,
+ 18, 163, -14, 164, 165, 48, 91, 1, 166, 171,
+ 173, 176, 175, 178, 2, 3, 189, 179, 185, 192,
+ 195, 210, 4, 5, 199, 197, 204, 213, 205, 206,
+ 208, 211, 222, 223, 224, 146, 228, 230, 231, 239,
+ 232, 240, 241, 243, 235, 237, 246, 247, 258, 259,
+ 238, 260, 261, 244, 268, 245, 265, 266, 249, 269,
+ 250, 221, 187, 281, 280, 282, 286, 19, 285, 209,
+ 215, 154, 242, 248, 169, 0, 0, 152
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 59, 20, 84, 68, 222, 119, 224, 121, 59, 40,
+ 11, 1, 1, 1, 73, 5, 5, 5, 43, 14,
+ 35, 32, 53, 7, 5, 40, 11, 22, 13, 7,
+ 25, 37, 43, 11, 12, 94, 10, 43, 53, 13,
+ 35, 100, 43, 108, 4, 40, 105, 37, 24, 100,
+ 268, 269, 23, 43, 43, 43, 37, 1, 53, 43,
+ 4, 5, 43, 281, 8, 43, 7, 7, 12, 10,
+ 0, 11, 12, 37, 18, 19, 20, 3, 22, 43,
+ 139, 195, 26, 27, 28, 29, 30, 1, 1, 33,
+ 34, 35, 36, 37, 7, 9, 161, 10, 42, 43,
+ 14, 183, 184, 43, 186, 80, 188, 82, 43, 8,
+ 8, 25, 171, 6, 89, 14, 1, 15, 16, 4,
+ 5, 8, 9, 8, 1, 23, 24, 12, 5, 10,
+ 11, 8, 246, 18, 19, 20, 7, 22, 197, 10,
+ 17, 26, 27, 28, 29, 30, 9, 22, 33, 34,
+ 25, 265, 266, 5, 31, 32, 4, 42, 43, 35,
+ 36, 37, 39, 40, 41, 42, 43, 10, 11, 43,
+ 13, 253, 254, 255, 256, 240, 11, 4, 13, 43,
+ 239, 4, 10, 11, 25, 8, 10, 11, 6, 12,
+ 8, 9, 6, 11, 4, 18, 19, 20, 4, 22,
+ 4, 43, 261, 26, 27, 28, 29, 30, 5, 8,
+ 33, 34, 231, 43, 6, 38, 25, 4, 41, 42,
+ 43, 8, 43, 9, 43, 12, 43, 286, 6, 6,
+ 43, 18, 19, 20, 8, 22, 8, 8, 6, 26,
+ 27, 28, 29, 30, 5, 43, 33, 34, 8, 0,
+ 1, 5, 3, 5, 13, 42, 43, 8, 8, 25,
+ 5, 8, 6, 4, 15, 16, 8, 43, 43, 8,
+ 4, 8, 23, 24, 43, 21, 43, 7, 43, 43,
+ 43, 43, 13, 7, 11, 43, 43, 43, 14, 25,
+ 43, 8, 8, 7, 43, 43, 11, 5, 8, 5,
+ 43, 8, 25, 43, 13, 43, 11, 11, 43, 11,
+ 43, 43, 11, 13, 43, 8, 7, 7, 43, 165,
+ 177, 104, 214, 220, 115, -1, -1, 100
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 8, 15, 16, 23, 24, 45, 46, 47, 49,
+ 50, 51, 52, 43, 4, 24, 23, 0, 1, 47,
+ 3, 6, 1, 43, 53, 54, 37, 43, 48, 43,
+ 58, 53, 9, 5, 53, 4, 1, 7, 10, 55,
+ 1, 8, 17, 31, 32, 39, 40, 41, 42, 43,
+ 54, 56, 59, 60, 61, 62, 94, 95, 99, 4,
+ 43, 43, 59, 25, 32, 43, 6, 4, 4, 4,
+ 43, 1, 14, 25, 5, 59, 4, 8, 12, 18,
+ 19, 20, 22, 26, 27, 28, 29, 30, 33, 34,
+ 38, 43, 54, 56, 68, 73, 74, 85, 87, 92,
+ 93, 99, 8, 43, 6, 25, 43, 70, 1, 43,
+ 96, 96, 5, 48, 97, 98, 9, 43, 74, 1,
+ 63, 74, 43, 83, 6, 66, 66, 6, 66, 37,
+ 43, 72, 80, 72, 82, 8, 8, 8, 6, 66,
+ 43, 6, 8, 11, 74, 7, 43, 69, 89, 8,
+ 9, 5, 92, 8, 70, 74, 7, 11, 12, 43,
+ 96, 8, 14, 5, 5, 13, 8, 1, 5, 97,
+ 57, 25, 63, 5, 63, 6, 8, 67, 4, 43,
+ 64, 65, 71, 10, 13, 43, 10, 11, 13, 8,
+ 10, 81, 8, 75, 74, 4, 86, 21, 79, 43,
+ 7, 10, 78, 7, 43, 43, 43, 96, 43, 65,
+ 8, 43, 74, 7, 84, 69, 35, 36, 37, 90,
+ 91, 43, 13, 7, 11, 72, 72, 72, 43, 72,
+ 43, 14, 43, 63, 74, 43, 88, 43, 43, 25,
+ 8, 8, 89, 7, 43, 43, 11, 5, 90, 43,
+ 43, 71, 71, 10, 13, 10, 13, 48, 8, 5,
+ 8, 25, 74, 96, 7, 11, 11, 63, 13, 11,
+ 72, 72, 72, 72, 76, 74, 63, 63, 71, 71,
+ 43, 13, 8, 71, 77, 43, 7, 74
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (&yylloc, parseio, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, Location, parseio); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp, struct parse_io *parseio)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, parseio)
+ FILE *yyoutput;
+ int yytype;
+ const YYSTYPE * const yyvaluep;
+ const YYLTYPE * const yylocationp;
+ struct parse_io *parseio;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (yylocationp);
+ YYUSE (parseio);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp, struct parse_io *parseio)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, parseio)
+ FILE *yyoutput;
+ int yytype;
+ const YYSTYPE * const yyvaluep;
+ const YYLTYPE * const yylocationp;
+ struct parse_io *parseio;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ YY_LOCATION_PRINT (yyoutput, *yylocationp);
+ YYFPRINTF (yyoutput, ": ");
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, parseio);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, struct parse_io *parseio)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule, parseio)
+ YYSTYPE *yyvsp;
+ YYLTYPE *yylsp;
+ int yyrule;
+ struct parse_io *parseio;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , &(yylsp[(yyi + 1) - (yynrhs)]) , parseio);
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, yylsp, Rule, parseio); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ size_t yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn < YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, struct parse_io *parseio)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp, parseio)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ YYLTYPE *yylocationp;
+ struct parse_io *parseio;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (yylocationp);
+ YYUSE (parseio);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+ case 43: /* "word" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1456 "ael.tab.c"
+ break;
+ case 46: /* "objects" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1464 "ael.tab.c"
+ break;
+ case 47: /* "object" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1472 "ael.tab.c"
+ break;
+ case 48: /* "context_name" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1477 "ael.tab.c"
+ break;
+ case 49: /* "context" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1485 "ael.tab.c"
+ break;
+ case 51: /* "macro" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1493 "ael.tab.c"
+ break;
+ case 52: /* "globals" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1501 "ael.tab.c"
+ break;
+ case 53: /* "global_statements" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1509 "ael.tab.c"
+ break;
+ case 54: /* "assignment" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1517 "ael.tab.c"
+ break;
+ case 56: /* "local_assignment" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1525 "ael.tab.c"
+ break;
+ case 58: /* "arglist" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1533 "ael.tab.c"
+ break;
+ case 59: /* "elements" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1541 "ael.tab.c"
+ break;
+ case 60: /* "element" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1549 "ael.tab.c"
+ break;
+ case 61: /* "ignorepat" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1557 "ael.tab.c"
+ break;
+ case 62: /* "extension" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1565 "ael.tab.c"
+ break;
+ case 63: /* "statements" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1573 "ael.tab.c"
+ break;
+ case 64: /* "timerange" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1578 "ael.tab.c"
+ break;
+ case 65: /* "timespec" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1586 "ael.tab.c"
+ break;
+ case 66: /* "test_expr" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1591 "ael.tab.c"
+ break;
+ case 68: /* "if_like_head" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1599 "ael.tab.c"
+ break;
+ case 69: /* "word_list" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1604 "ael.tab.c"
+ break;
+ case 71: /* "word3_list" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1609 "ael.tab.c"
+ break;
+ case 72: /* "goto_word" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1614 "ael.tab.c"
+ break;
+ case 73: /* "switch_statement" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1622 "ael.tab.c"
+ break;
+ case 74: /* "statement" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1630 "ael.tab.c"
+ break;
+ case 79: /* "opt_else" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1638 "ael.tab.c"
+ break;
+ case 80: /* "target" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1646 "ael.tab.c"
+ break;
+ case 81: /* "opt_pri" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1651 "ael.tab.c"
+ break;
+ case 82: /* "jumptarget" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1659 "ael.tab.c"
+ break;
+ case 83: /* "macro_call" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1667 "ael.tab.c"
+ break;
+ case 85: /* "application_call_head" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1675 "ael.tab.c"
+ break;
+ case 87: /* "application_call" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1683 "ael.tab.c"
+ break;
+ case 88: /* "opt_word" */
+#line 178 "ael.y"
+ { free((yyvaluep->str));};
+#line 1688 "ael.tab.c"
+ break;
+ case 89: /* "eval_arglist" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1696 "ael.tab.c"
+ break;
+ case 90: /* "case_statements" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1704 "ael.tab.c"
+ break;
+ case 91: /* "case_statement" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1712 "ael.tab.c"
+ break;
+ case 92: /* "macro_statements" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1720 "ael.tab.c"
+ break;
+ case 93: /* "macro_statement" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1728 "ael.tab.c"
+ break;
+ case 94: /* "switches" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1736 "ael.tab.c"
+ break;
+ case 95: /* "eswitches" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1744 "ael.tab.c"
+ break;
+ case 96: /* "switchlist" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1752 "ael.tab.c"
+ break;
+ case 97: /* "included_entry" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1760 "ael.tab.c"
+ break;
+ case 98: /* "includeslist" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1768 "ael.tab.c"
+ break;
+ case 99: /* "includes" */
+#line 165 "ael.y"
+ {
+ destroy_pval((yyvaluep->pval));
+ prev_word=0;
+ };
+#line 1776 "ael.tab.c"
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (struct parse_io *parseio);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (struct parse_io *parseio)
+#else
+int
+yyparse (parseio)
+ struct parse_io *parseio;
+#endif
+#endif
+{
+ /* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+/* Location data for the look-ahead symbol. */
+YYLTYPE yylloc;
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+ /* The location stack. */
+ YYLTYPE yylsa[YYINITDEPTH];
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ /* The locations where the error started and ended. */
+ YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+ YYLTYPE yyloc;
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ yylsp = yyls;
+#if YYLTYPE_IS_TRIVIAL
+ /* Initialize the default location before parsing starts. */
+ yylloc.first_line = yylloc.last_line = 1;
+ yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+ YYLTYPE *yyls1 = yyls;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yyls1, yysize * sizeof (*yylsp),
+ &yystacksize);
+ yyls = yyls1;
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+ YYSTACK_RELOCATE (yyls);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+ yylsp = yyls + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+ *++yylsp = yylloc;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+ /* Default location. */
+ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 186 "ael.y"
+ { (yyval.pval) = parseio->pval = (yyvsp[(1) - (1)].pval); ;}
+ break;
+
+ case 3:
+#line 189 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 4:
+#line 190 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
+ break;
+
+ case 5:
+#line 191 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (2)].pval);;}
+ break;
+
+ case 6:
+#line 194 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 7:
+#line 195 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 8:
+#line 196 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 9:
+#line 197 "ael.y"
+ {(yyval.pval)=0;/* allow older docs to be read */;}
+ break;
+
+ case 10:
+#line 200 "ael.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
+ break;
+
+ case 11:
+#line 201 "ael.y"
+ { (yyval.str) = strdup("default"); ;}
+ break;
+
+ case 12:
+#line 204 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_CONTEXT, &(yylsp[(1) - (6)]), &(yylsp[(6) - (6)]));
+ (yyval.pval)->u1.str = (yyvsp[(3) - (6)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(5) - (6)].pval);
+ set_dads((yyval.pval),(yyvsp[(5) - (6)].pval));
+ (yyval.pval)->u3.abstract = (yyvsp[(1) - (6)].intval);;}
+ break;
+
+ case 13:
+#line 213 "ael.y"
+ { (yyval.intval) = 1; ;}
+ break;
+
+ case 14:
+#line 214 "ael.y"
+ { (yyval.intval) = 0; ;}
+ break;
+
+ case 15:
+#line 215 "ael.y"
+ { (yyval.intval) = 2; ;}
+ break;
+
+ case 16:
+#line 216 "ael.y"
+ { (yyval.intval)=3; ;}
+ break;
+
+ case 17:
+#line 217 "ael.y"
+ { (yyval.intval)=3; ;}
+ break;
+
+ case 18:
+#line 220 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_MACRO, &(yylsp[(1) - (8)]), &(yylsp[(8) - (8)]));
+ (yyval.pval)->u1.str = (yyvsp[(2) - (8)].str); (yyval.pval)->u2.arglist = (yyvsp[(4) - (8)].pval); (yyval.pval)->u3.macro_statements = (yyvsp[(7) - (8)].pval);
+ set_dads((yyval.pval),(yyvsp[(7) - (8)].pval));;}
+ break;
+
+ case 19:
+#line 226 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_GLOBALS, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
+ (yyval.pval)->u1.statements = (yyvsp[(3) - (4)].pval);
+ set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;}
+ break;
+
+ case 20:
+#line 232 "ael.y"
+ { (yyval.pval) = NULL; ;}
+ break;
+
+ case 21:
+#line 233 "ael.y"
+ {(yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
+ break;
+
+ case 22:
+#line 234 "ael.y"
+ {(yyval.pval)=(yyvsp[(2) - (2)].pval);;}
+ break;
+
+ case 23:
+#line 237 "ael.y"
+ { reset_semicount(parseio->scanner); ;}
+ break;
+
+ case 24:
+#line 237 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_VARDEC, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
+ (yyval.pval)->u1.str = (yyvsp[(1) - (5)].str);
+ (yyval.pval)->u2.val = (yyvsp[(4) - (5)].str); ;}
+ break;
+
+ case 25:
+#line 243 "ael.y"
+ { reset_semicount(parseio->scanner); ;}
+ break;
+
+ case 26:
+#line 243 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_LOCALVARDEC, &(yylsp[(1) - (6)]), &(yylsp[(6) - (6)]));
+ (yyval.pval)->u1.str = (yyvsp[(2) - (6)].str);
+ (yyval.pval)->u2.val = (yyvsp[(5) - (6)].str); ;}
+ break;
+
+ case 27:
+#line 250 "ael.y"
+ { (yyval.pval) = NULL; ;}
+ break;
+
+ case 28:
+#line 251 "ael.y"
+ { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
+ break;
+
+ case 29:
+#line 252 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)]))); ;}
+ break;
+
+ case 30:
+#line 253 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (2)].pval);;}
+ break;
+
+ case 31:
+#line 256 "ael.y"
+ {(yyval.pval)=0;;}
+ break;
+
+ case 32:
+#line 257 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
+ break;
+
+ case 33:
+#line 258 "ael.y"
+ { (yyval.pval)=(yyvsp[(2) - (2)].pval);;}
+ break;
+
+ case 34:
+#line 261 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 35:
+#line 262 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 36:
+#line 263 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 37:
+#line 264 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 38:
+#line 265 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 39:
+#line 266 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 40:
+#line 267 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 41:
+#line 268 "ael.y"
+ {free((yyvsp[(1) - (2)].str)); (yyval.pval)=0;;}
+ break;
+
+ case 42:
+#line 269 "ael.y"
+ {(yyval.pval)=0;/* allow older docs to be read */;}
+ break;
+
+ case 43:
+#line 272 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_IGNOREPAT, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
+ (yyval.pval)->u1.str = (yyvsp[(3) - (4)].str);;}
+ break;
+
+ case 44:
+#line 277 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
+ (yyval.pval)->u1.str = (yyvsp[(1) - (3)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(3) - (3)].pval); set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;}
+ break;
+
+ case 45:
+#line 281 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (5)]), &(yylsp[(3) - (5)]));
+ (yyval.pval)->u1.str = malloc(strlen((yyvsp[(1) - (5)].str))+strlen((yyvsp[(3) - (5)].str))+2);
+ strcpy((yyval.pval)->u1.str,(yyvsp[(1) - (5)].str));
+ strcat((yyval.pval)->u1.str,"@");
+ strcat((yyval.pval)->u1.str,(yyvsp[(3) - (5)].str));
+ free((yyvsp[(1) - (5)].str));
+ (yyval.pval)->u2.statements = (yyvsp[(5) - (5)].pval); set_dads((yyval.pval),(yyvsp[(5) - (5)].pval));;}
+ break;
+
+ case 46:
+#line 289 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
+ (yyval.pval)->u1.str = (yyvsp[(2) - (4)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(4) - (4)].pval); set_dads((yyval.pval),(yyvsp[(4) - (4)].pval));
+ (yyval.pval)->u4.regexten=1;;}
+ break;
+
+ case 47:
+#line 294 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (7)]), &(yylsp[(7) - (7)]));
+ (yyval.pval)->u1.str = (yyvsp[(5) - (7)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(7) - (7)].pval); set_dads((yyval.pval),(yyvsp[(7) - (7)].pval));
+ (yyval.pval)->u3.hints = (yyvsp[(3) - (7)].str);;}
+ break;
+
+ case 48:
+#line 299 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_EXTENSION, &(yylsp[(1) - (8)]), &(yylsp[(8) - (8)]));
+ (yyval.pval)->u1.str = (yyvsp[(6) - (8)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(8) - (8)].pval); set_dads((yyval.pval),(yyvsp[(8) - (8)].pval));
+ (yyval.pval)->u4.regexten=1;
+ (yyval.pval)->u3.hints = (yyvsp[(4) - (8)].str);;}
+ break;
+
+ case 49:
+#line 308 "ael.y"
+ { (yyval.pval) = NULL; ;}
+ break;
+
+ case 50:
+#line 309 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
+ break;
+
+ case 51:
+#line 310 "ael.y"
+ {(yyval.pval)=(yyvsp[(2) - (2)].pval);;}
+ break;
+
+ case 52:
+#line 316 "ael.y"
+ {
+ asprintf(&(yyval.str), "%s:%s:%s", (yyvsp[(1) - (5)].str), (yyvsp[(3) - (5)].str), (yyvsp[(5) - (5)].str));
+ free((yyvsp[(1) - (5)].str));
+ free((yyvsp[(3) - (5)].str));
+ free((yyvsp[(5) - (5)].str)); ;}
+ break;
+
+ case 53:
+#line 321 "ael.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
+ break;
+
+ case 54:
+#line 325 "ael.y"
+ {
+ (yyval.pval) = nword((yyvsp[(1) - (7)].str), &(yylsp[(1) - (7)]));
+ (yyval.pval)->next = nword((yyvsp[(3) - (7)].str), &(yylsp[(3) - (7)]));
+ (yyval.pval)->next->next = nword((yyvsp[(5) - (7)].str), &(yylsp[(5) - (7)]));
+ (yyval.pval)->next->next->next = nword((yyvsp[(7) - (7)].str), &(yylsp[(7) - (7)])); ;}
+ break;
+
+ case 55:
+#line 333 "ael.y"
+ { reset_parencount(parseio->scanner); ;}
+ break;
+
+ case 56:
+#line 333 "ael.y"
+ { (yyval.str) = (yyvsp[(3) - (4)].str); ;}
+ break;
+
+ case 57:
+#line 337 "ael.y"
+ {
+ (yyval.pval)= npval2(PV_IF, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
+ (yyval.pval)->u1.str = (yyvsp[(2) - (2)].str); ;}
+ break;
+
+ case 58:
+#line 340 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_RANDOM, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
+ (yyval.pval)->u1.str=(yyvsp[(2) - (2)].str);;}
+ break;
+
+ case 59:
+#line 343 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_IFTIME, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
+ (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval);
+ prev_word = 0; ;}
+ break;
+
+ case 60:
+#line 354 "ael.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str);;}
+ break;
+
+ case 61:
+#line 355 "ael.y"
+ {
+ asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
+ free((yyvsp[(1) - (2)].str));
+ free((yyvsp[(2) - (2)].str));
+ prev_word = (yyval.str);;}
+ break;
+
+ case 62:
+#line 362 "ael.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
+ break;
+
+ case 63:
+#line 363 "ael.y"
+ {
+ asprintf(&((yyval.str)), "%s %s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
+ free((yyvsp[(1) - (2)].str));
+ free((yyvsp[(2) - (2)].str)); ;}
+ break;
+
+ case 64:
+#line 367 "ael.y"
+ {
+ asprintf(&((yyval.str)), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str));
+ free((yyvsp[(1) - (3)].str));
+ free((yyvsp[(3) - (3)].str)); ;}
+ break;
+
+ case 65:
+#line 371 "ael.y"
+ { /* there are often '&' in hints */
+ asprintf(&((yyval.str)), "%s&%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str));
+ free((yyvsp[(1) - (3)].str));
+ free((yyvsp[(3) - (3)].str));;}
+ break;
+
+ case 66:
+#line 377 "ael.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str);;}
+ break;
+
+ case 67:
+#line 378 "ael.y"
+ {
+ asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
+ free((yyvsp[(1) - (2)].str));
+ free((yyvsp[(2) - (2)].str));
+ prev_word = (yyval.str);;}
+ break;
+
+ case 68:
+#line 383 "ael.y"
+ {
+ asprintf(&((yyval.str)), "%s%s%s", (yyvsp[(1) - (3)].str), (yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str));
+ free((yyvsp[(1) - (3)].str));
+ free((yyvsp[(2) - (3)].str));
+ free((yyvsp[(3) - (3)].str));
+ prev_word=(yyval.str);;}
+ break;
+
+ case 69:
+#line 391 "ael.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str);;}
+ break;
+
+ case 70:
+#line 392 "ael.y"
+ {
+ asprintf(&((yyval.str)), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str));
+ free((yyvsp[(1) - (2)].str));
+ free((yyvsp[(2) - (2)].str));;}
+ break;
+
+ case 71:
+#line 396 "ael.y"
+ {
+ asprintf(&((yyval.str)), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str));
+ free((yyvsp[(1) - (3)].str));
+ free((yyvsp[(3) - (3)].str));;}
+ break;
+
+ case 72:
+#line 402 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_SWITCH, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
+ (yyval.pval)->u1.str = (yyvsp[(2) - (5)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(4) - (5)].pval); set_dads((yyval.pval),(yyvsp[(4) - (5)].pval));;}
+ break;
+
+ case 73:
+#line 411 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_STATEMENTBLOCK, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
+ (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval); set_dads((yyval.pval),(yyvsp[(2) - (3)].pval));;}
+ break;
+
+ case 74:
+#line 414 "ael.y"
+ { (yyval.pval) = (yyvsp[(1) - (1)].pval); ;}
+ break;
+
+ case 75:
+#line 415 "ael.y"
+ { (yyval.pval) = (yyvsp[(1) - (1)].pval); ;}
+ break;
+
+ case 76:
+#line 416 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
+ (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval);;}
+ break;
+
+ case 77:
+#line 419 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
+ (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval);;}
+ break;
+
+ case 78:
+#line 422 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_LABEL, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
+ (yyval.pval)->u1.str = (yyvsp[(1) - (2)].str); ;}
+ break;
+
+ case 79:
+#line 425 "ael.y"
+ {reset_semicount(parseio->scanner);;}
+ break;
+
+ case 80:
+#line 426 "ael.y"
+ {reset_semicount(parseio->scanner);;}
+ break;
+
+ case 81:
+#line 427 "ael.y"
+ {reset_parencount(parseio->scanner);;}
+ break;
+
+ case 82:
+#line 427 "ael.y"
+ { /* XXX word_list maybe ? */
+ (yyval.pval) = npval2(PV_FOR, &(yylsp[(1) - (12)]), &(yylsp[(12) - (12)]));
+ (yyval.pval)->u1.for_init = (yyvsp[(4) - (12)].str);
+ (yyval.pval)->u2.for_test=(yyvsp[(7) - (12)].str);
+ (yyval.pval)->u3.for_inc = (yyvsp[(10) - (12)].str);
+ (yyval.pval)->u4.for_statements = (yyvsp[(12) - (12)].pval); set_dads((yyval.pval),(yyvsp[(12) - (12)].pval));;}
+ break;
+
+ case 83:
+#line 433 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_WHILE, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
+ (yyval.pval)->u1.str = (yyvsp[(2) - (3)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(3) - (3)].pval); set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;}
+ break;
+
+ case 84:
+#line 437 "ael.y"
+ { (yyval.pval) = (yyvsp[(1) - (1)].pval); ;}
+ break;
+
+ case 85:
+#line 438 "ael.y"
+ { (yyval.pval) = update_last((yyvsp[(2) - (3)].pval), &(yylsp[(2) - (3)])); ;}
+ break;
+
+ case 86:
+#line 439 "ael.y"
+ { (yyval.pval) = update_last((yyvsp[(1) - (2)].pval), &(yylsp[(2) - (2)])); ;}
+ break;
+
+ case 87:
+#line 440 "ael.y"
+ {
+ (yyval.pval)= npval2(PV_APPLICATION_CALL, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)]));
+ (yyval.pval)->u1.str = (yyvsp[(1) - (2)].str);;}
+ break;
+
+ case 88:
+#line 443 "ael.y"
+ {reset_semicount(parseio->scanner);;}
+ break;
+
+ case 89:
+#line 443 "ael.y"
+ {
+ char *bufx;
+ int tot=0;
+ pval *pptr;
+ (yyval.pval) = npval2(PV_VARDEC, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
+ (yyval.pval)->u2.val=(yyvsp[(4) - (5)].str);
+ /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
+ /* string to big to fit in the buffer? */
+ tot+=strlen((yyvsp[(1) - (5)].pval)->u1.str);
+ for(pptr=(yyvsp[(1) - (5)].pval)->u2.arglist;pptr;pptr=pptr->next) {
+ tot+=strlen(pptr->u1.str);
+ tot++; /* for a sep like a comma */
+ }
+ tot+=4; /* for safety */
+ bufx = calloc(1, tot);
+ strcpy(bufx,(yyvsp[(1) - (5)].pval)->u1.str);
+ strcat(bufx,"(");
+ /* XXX need to advance the pointer or the loop is very inefficient */
+ for (pptr=(yyvsp[(1) - (5)].pval)->u2.arglist;pptr;pptr=pptr->next) {
+ if ( pptr != (yyvsp[(1) - (5)].pval)->u2.arglist )
+ strcat(bufx,",");
+ strcat(bufx,pptr->u1.str);
+ }
+ strcat(bufx,")");
+#ifdef AAL_ARGCHECK
+ if ( !ael_is_funcname((yyvsp[(1) - (5)].pval)->u1.str) )
+ ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Function call? The name %s is not in my internal list of function names\n",
+ my_file, (yylsp[(1) - (5)]).first_line, (yylsp[(1) - (5)]).first_column, (yylsp[(1) - (5)]).last_column, (yyvsp[(1) - (5)].pval)->u1.str);
+#endif
+ (yyval.pval)->u1.str = bufx;
+ destroy_pval((yyvsp[(1) - (5)].pval)); /* the app call it is not, get rid of that chain */
+ prev_word = 0;
+ ;}
+ break;
+
+ case 90:
+#line 476 "ael.y"
+ { (yyval.pval) = npval2(PV_BREAK, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;}
+ break;
+
+ case 91:
+#line 477 "ael.y"
+ { (yyval.pval) = npval2(PV_RETURN, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;}
+ break;
+
+ case 92:
+#line 478 "ael.y"
+ { (yyval.pval) = npval2(PV_CONTINUE, &(yylsp[(1) - (2)]), &(yylsp[(2) - (2)])); ;}
+ break;
+
+ case 93:
+#line 479 "ael.y"
+ {
+ (yyval.pval) = update_last((yyvsp[(1) - (3)].pval), &(yylsp[(2) - (3)]));
+ (yyval.pval)->u2.statements = (yyvsp[(2) - (3)].pval); set_dads((yyval.pval),(yyvsp[(2) - (3)].pval));
+ (yyval.pval)->u3.else_statements = (yyvsp[(3) - (3)].pval);set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;}
+ break;
+
+ case 94:
+#line 483 "ael.y"
+ { (yyval.pval)=0; ;}
+ break;
+
+ case 95:
+#line 486 "ael.y"
+ { (yyval.pval) = (yyvsp[(2) - (2)].pval); ;}
+ break;
+
+ case 96:
+#line 487 "ael.y"
+ { (yyval.pval) = NULL ; ;}
+ break;
+
+ case 97:
+#line 490 "ael.y"
+ { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
+ break;
+
+ case 98:
+#line 491 "ael.y"
+ {
+ (yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)]));
+ (yyval.pval)->next = nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)])); ;}
+ break;
+
+ case 99:
+#line 494 "ael.y"
+ {
+ (yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)]));
+ (yyval.pval)->next = nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)])); ;}
+ break;
+
+ case 100:
+#line 497 "ael.y"
+ {
+ (yyval.pval) = nword((yyvsp[(1) - (5)].str), &(yylsp[(1) - (5)]));
+ (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
+ (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;}
+ break;
+
+ case 101:
+#line 501 "ael.y"
+ {
+ (yyval.pval) = nword((yyvsp[(1) - (5)].str), &(yylsp[(1) - (5)]));
+ (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
+ (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;}
+ break;
+
+ case 102:
+#line 505 "ael.y"
+ {
+ (yyval.pval) = nword(strdup("default"), &(yylsp[(1) - (5)]));
+ (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
+ (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;}
+ break;
+
+ case 103:
+#line 509 "ael.y"
+ {
+ (yyval.pval) = nword(strdup("default"), &(yylsp[(1) - (5)]));
+ (yyval.pval)->next = nword((yyvsp[(3) - (5)].str), &(yylsp[(3) - (5)]));
+ (yyval.pval)->next->next = nword((yyvsp[(5) - (5)].str), &(yylsp[(5) - (5)])); ;}
+ break;
+
+ case 104:
+#line 515 "ael.y"
+ { (yyval.str) = strdup("1"); ;}
+ break;
+
+ case 105:
+#line 516 "ael.y"
+ { (yyval.str) = (yyvsp[(2) - (2)].str); ;}
+ break;
+
+ case 106:
+#line 520 "ael.y"
+ { /* ext[, pri] default 1 */
+ (yyval.pval) = nword((yyvsp[(1) - (2)].str), &(yylsp[(1) - (2)]));
+ (yyval.pval)->next = nword((yyvsp[(2) - (2)].str), &(yylsp[(2) - (2)])); ;}
+ break;
+
+ case 107:
+#line 523 "ael.y"
+ { /* context, ext, pri */
+ (yyval.pval) = nword((yyvsp[(4) - (4)].str), &(yylsp[(4) - (4)]));
+ (yyval.pval)->next = nword((yyvsp[(1) - (4)].str), &(yylsp[(1) - (4)]));
+ (yyval.pval)->next->next = nword((yyvsp[(2) - (4)].str), &(yylsp[(2) - (4)])); ;}
+ break;
+
+ case 108:
+#line 529 "ael.y"
+ {reset_argcount(parseio->scanner);;}
+ break;
+
+ case 109:
+#line 529 "ael.y"
+ {
+ /* XXX original code had @2 but i think we need @5 */
+ (yyval.pval) = npval2(PV_MACRO_CALL, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
+ (yyval.pval)->u1.str = (yyvsp[(1) - (5)].str);
+ (yyval.pval)->u2.arglist = (yyvsp[(4) - (5)].pval);;}
+ break;
+
+ case 110:
+#line 534 "ael.y"
+ {
+ (yyval.pval)= npval2(PV_MACRO_CALL, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
+ (yyval.pval)->u1.str = (yyvsp[(1) - (3)].str); ;}
+ break;
+
+ case 111:
+#line 542 "ael.y"
+ {reset_argcount(parseio->scanner);;}
+ break;
+
+ case 112:
+#line 542 "ael.y"
+ {
+ if (strcasecmp((yyvsp[(1) - (3)].str),"goto") == 0) {
+ (yyval.pval) = npval2(PV_GOTO, &(yylsp[(1) - (3)]), &(yylsp[(2) - (3)]));
+ free((yyvsp[(1) - (3)].str)); /* won't be using this */
+ ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Suggestion: Use the goto statement instead of the Goto() application call in AEL.\n", my_file, (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column, (yylsp[(1) - (3)]).last_column );
+ } else {
+ (yyval.pval)= npval2(PV_APPLICATION_CALL, &(yylsp[(1) - (3)]), &(yylsp[(2) - (3)]));
+ (yyval.pval)->u1.str = (yyvsp[(1) - (3)].str);
+ } ;}
+ break;
+
+ case 113:
+#line 553 "ael.y"
+ {
+ (yyval.pval) = update_last((yyvsp[(1) - (3)].pval), &(yylsp[(3) - (3)]));
+ if( (yyval.pval)->type == PV_GOTO )
+ (yyval.pval)->u1.list = (yyvsp[(2) - (3)].pval);
+ else
+ (yyval.pval)->u2.arglist = (yyvsp[(2) - (3)].pval);
+ ;}
+ break;
+
+ case 114:
+#line 560 "ael.y"
+ { (yyval.pval) = update_last((yyvsp[(1) - (2)].pval), &(yylsp[(2) - (2)])); ;}
+ break;
+
+ case 115:
+#line 563 "ael.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str) ;}
+ break;
+
+ case 116:
+#line 564 "ael.y"
+ { (yyval.str) = strdup(""); ;}
+ break;
+
+ case 117:
+#line 567 "ael.y"
+ { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
+ break;
+
+ case 118:
+#line 568 "ael.y"
+ {
+ (yyval.pval)= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/);
+ (yyval.pval)->u1.str = strdup(""); ;}
+ break;
+
+ case 119:
+#line 571 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), nword((yyvsp[(3) - (3)].str), &(yylsp[(3) - (3)]))); ;}
+ break;
+
+ case 120:
+#line 574 "ael.y"
+ { (yyval.pval) = NULL; ;}
+ break;
+
+ case 121:
+#line 575 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
+ break;
+
+ case 122:
+#line 578 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_CASE, &(yylsp[(1) - (4)]), &(yylsp[(3) - (4)])); /* XXX 3 or 4 ? */
+ (yyval.pval)->u1.str = (yyvsp[(2) - (4)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(4) - (4)].pval); set_dads((yyval.pval),(yyvsp[(4) - (4)].pval));;}
+ break;
+
+ case 123:
+#line 582 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_DEFAULT, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));
+ (yyval.pval)->u1.str = NULL;
+ (yyval.pval)->u2.statements = (yyvsp[(3) - (3)].pval);set_dads((yyval.pval),(yyvsp[(3) - (3)].pval));;}
+ break;
+
+ case 124:
+#line 586 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_PATTERN, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)])); /* XXX@3 or @4 ? */
+ (yyval.pval)->u1.str = (yyvsp[(2) - (4)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(4) - (4)].pval);set_dads((yyval.pval),(yyvsp[(4) - (4)].pval));;}
+ break;
+
+ case 125:
+#line 592 "ael.y"
+ { (yyval.pval) = NULL; ;}
+ break;
+
+ case 126:
+#line 593 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval)); ;}
+ break;
+
+ case 127:
+#line 596 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 128:
+#line 597 "ael.y"
+ { (yyval.pval)=(yyvsp[(1) - (1)].pval);;}
+ break;
+
+ case 129:
+#line 598 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_CATCH, &(yylsp[(1) - (5)]), &(yylsp[(5) - (5)]));
+ (yyval.pval)->u1.str = (yyvsp[(2) - (5)].str);
+ (yyval.pval)->u2.statements = (yyvsp[(4) - (5)].pval); set_dads((yyval.pval),(yyvsp[(4) - (5)].pval));;}
+ break;
+
+ case 130:
+#line 604 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_SWITCHES, &(yylsp[(1) - (4)]), &(yylsp[(2) - (4)]));
+ (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval); set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;}
+ break;
+
+ case 131:
+#line 609 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_ESWITCHES, &(yylsp[(1) - (4)]), &(yylsp[(2) - (4)]));
+ (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval); set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;}
+ break;
+
+ case 132:
+#line 614 "ael.y"
+ { (yyval.pval) = NULL; ;}
+ break;
+
+ case 133:
+#line 615 "ael.y"
+ { (yyval.pval) = linku1(nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)])), (yyvsp[(3) - (3)].pval)); ;}
+ break;
+
+ case 134:
+#line 616 "ael.y"
+ { char *x; asprintf(&x,"%s@%s", (yyvsp[(1) - (5)].str),(yyvsp[(3) - (5)].str)); free((yyvsp[(1) - (5)].str)); free((yyvsp[(3) - (5)].str));
+ (yyval.pval) = linku1(nword(x, &(yylsp[(1) - (5)])), (yyvsp[(5) - (5)].pval));;}
+ break;
+
+ case 135:
+#line 618 "ael.y"
+ {(yyval.pval)=(yyvsp[(2) - (2)].pval);;}
+ break;
+
+ case 136:
+#line 621 "ael.y"
+ { (yyval.pval) = nword((yyvsp[(1) - (1)].str), &(yylsp[(1) - (1)])); ;}
+ break;
+
+ case 137:
+#line 622 "ael.y"
+ {
+ (yyval.pval) = nword((yyvsp[(1) - (3)].str), &(yylsp[(1) - (3)]));
+ (yyval.pval)->u2.arglist = (yyvsp[(3) - (3)].pval);
+ prev_word=0; /* XXX sure ? */ ;}
+ break;
+
+ case 138:
+#line 629 "ael.y"
+ { (yyval.pval) = (yyvsp[(1) - (2)].pval); ;}
+ break;
+
+ case 139:
+#line 630 "ael.y"
+ { (yyval.pval) = linku1((yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval)); ;}
+ break;
+
+ case 140:
+#line 631 "ael.y"
+ {(yyval.pval)=(yyvsp[(1) - (2)].pval);;}
+ break;
+
+ case 141:
+#line 634 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_INCLUDES, &(yylsp[(1) - (4)]), &(yylsp[(4) - (4)]));
+ (yyval.pval)->u1.list = (yyvsp[(3) - (4)].pval);set_dads((yyval.pval),(yyvsp[(3) - (4)].pval));;}
+ break;
+
+ case 142:
+#line 637 "ael.y"
+ {
+ (yyval.pval) = npval2(PV_INCLUDES, &(yylsp[(1) - (3)]), &(yylsp[(3) - (3)]));;}
+ break;
+
+
+/* Line 1270 of yacc.c. */
+#line 3012 "ael.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+ *++yylsp = yyloc;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (&yylloc, parseio, YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (&yylloc, parseio, yymsg);
+ }
+ else
+ {
+ yyerror (&yylloc, parseio, YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+ yyerror_range[0] = yylloc;
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, &yylloc, parseio);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ yyerror_range[0] = yylsp[1-yylen];
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ yyerror_range[0] = *yylsp;
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, yylsp, parseio);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+ yyerror_range[1] = yylloc;
+ /* Using YYLLOC is tempting, but would change the location of
+ the look-ahead. YYLOC is available though. */
+ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+ *++yylsp = yyloc;
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (&yylloc, parseio, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, &yylloc, parseio);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, yylsp, parseio);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+
+
+#line 642 "ael.y"
+
+
+static char *token_equivs1[] =
+{
+ "AMPER",
+ "AT",
+ "BAR",
+ "COLON",
+ "COMMA",
+ "EQ",
+ "EXTENMARK",
+ "KW_BREAK",
+ "KW_CASE",
+ "KW_CATCH",
+ "KW_CONTEXT",
+ "KW_CONTINUE",
+ "KW_DEFAULT",
+ "KW_ELSE",
+ "KW_ESWITCHES",
+ "KW_FOR",
+ "KW_GLOBALS",
+ "KW_GOTO",
+ "KW_HINT",
+ "KW_IFTIME",
+ "KW_IF",
+ "KW_IGNOREPAT",
+ "KW_INCLUDES"
+ "KW_JUMP",
+ "KW_MACRO",
+ "KW_PATTERN",
+ "KW_REGEXTEN",
+ "KW_RETURN",
+ "KW_SWITCHES",
+ "KW_SWITCH",
+ "KW_WHILE",
+ "LC",
+ "LP",
+ "RC",
+ "RP",
+ "SEMI",
+};
+
+static char *token_equivs2[] =
+{
+ "&",
+ "@",
+ "|",
+ ":",
+ ",",
+ "=",
+ "=>",
+ "break",
+ "case",
+ "catch",
+ "context",
+ "continue",
+ "default",
+ "else",
+ "eswitches",
+ "for",
+ "globals",
+ "goto",
+ "hint",
+ "ifTime",
+ "if",
+ "ignorepat",
+ "includes"
+ "jump",
+ "macro",
+ "pattern",
+ "regexten",
+ "return",
+ "switches",
+ "switch",
+ "while",
+ "{",
+ "(",
+ "}",
+ ")",
+ ";",
+};
+
+
+static char *ael_token_subst(const char *mess)
+{
+ /* calc a length, malloc, fill, and return; yyerror had better free it! */
+ int len=0,i;
+ const char *p;
+ char *res, *s,*t;
+ int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
+
+ for (p=mess; *p; p++) {
+ for (i=0; i<token_equivs_entries; i++) {
+ if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
+ {
+ len+=strlen(token_equivs2[i])+2;
+ p += strlen(token_equivs1[i])-1;
+ break;
+ }
+ }
+ len++;
+ }
+ res = calloc(1, len+1);
+ res[0] = 0;
+ s = res;
+ for (p=mess; *p;) {
+ int found = 0;
+ for (i=0; i<token_equivs_entries; i++) {
+ if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
+ *s++ = '\'';
+ for (t=token_equivs2[i]; *t;) {
+ *s++ = *t++;
+ }
+ *s++ = '\'';
+ p += strlen(token_equivs1[i]);
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ *s++ = *p++;
+ }
+ *s++ = 0;
+ return res;
+}
+
+void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s)
+{
+ char *s2 = ael_token_subst((char *)s);
+ if (locp->first_line == locp->last_line) {
+ ast_log(LOG_ERROR, "==== File: %s, Line %d, Cols: %d-%d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_column, s2);
+ } else {
+ ast_log(LOG_ERROR, "==== File: %s, Line %d Col %d to Line %d Col %d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_line, locp->last_column, s2);
+ }
+ free(s2);
+ parseio->syntax_error_count++;
+}
+
+struct pval *npval(pvaltype type, int first_line, int last_line,
+ int first_column, int last_column)
+{
+ pval *z = calloc(1, sizeof(struct pval));
+ z->type = type;
+ z->startline = first_line;
+ z->endline = last_line;
+ z->startcol = first_column;
+ z->endcol = last_column;
+ z->filename = strdup(my_file);
+ return z;
+}
+
+static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last)
+{
+ return npval(type, first->first_line, last->last_line,
+ first->first_column, last->last_column);
+}
+
+static struct pval *update_last(pval *obj, YYLTYPE *last)
+{
+ obj->endline = last->last_line;
+ obj->endcol = last->last_column;
+ return obj;
+}
+
+/* frontend for npval to create a PV_WORD string from the given token */
+static pval *nword(char *string, YYLTYPE *pos)
+{
+ pval *p = npval2(PV_WORD, pos, pos);
+ if (p)
+ p->u1.str = string;
+ return p;
+}
+
+/* this routine adds a dad ptr to each element in the list */
+static void set_dads(struct pval *dad, struct pval *child_list)
+{
+ struct pval *t;
+
+ for(t=child_list;t;t=t->next) /* simple stuff */
+ t->dad = dad;
+}
+
+
diff --git a/trunk/res/ael/ael.tab.h b/trunk/res/ael/ael.tab.h
new file mode 100644
index 000000000..95b011852
--- /dev/null
+++ b/trunk/res/ael/ael.tab.h
@@ -0,0 +1,154 @@
+/* A Bison parser, made by GNU Bison 2.1a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ 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, 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ KW_CONTEXT = 258,
+ LC = 259,
+ RC = 260,
+ LP = 261,
+ RP = 262,
+ SEMI = 263,
+ EQ = 264,
+ COMMA = 265,
+ COLON = 266,
+ AMPER = 267,
+ BAR = 268,
+ AT = 269,
+ KW_MACRO = 270,
+ KW_GLOBALS = 271,
+ KW_IGNOREPAT = 272,
+ KW_SWITCH = 273,
+ KW_IF = 274,
+ KW_IFTIME = 275,
+ KW_ELSE = 276,
+ KW_RANDOM = 277,
+ KW_ABSTRACT = 278,
+ KW_EXTEND = 279,
+ EXTENMARK = 280,
+ KW_GOTO = 281,
+ KW_JUMP = 282,
+ KW_RETURN = 283,
+ KW_BREAK = 284,
+ KW_CONTINUE = 285,
+ KW_REGEXTEN = 286,
+ KW_HINT = 287,
+ KW_FOR = 288,
+ KW_WHILE = 289,
+ KW_CASE = 290,
+ KW_PATTERN = 291,
+ KW_DEFAULT = 292,
+ KW_CATCH = 293,
+ KW_SWITCHES = 294,
+ KW_ESWITCHES = 295,
+ KW_INCLUDES = 296,
+ KW_LOCAL = 297,
+ word = 298
+ };
+#endif
+/* Tokens. */
+#define KW_CONTEXT 258
+#define LC 259
+#define RC 260
+#define LP 261
+#define RP 262
+#define SEMI 263
+#define EQ 264
+#define COMMA 265
+#define COLON 266
+#define AMPER 267
+#define BAR 268
+#define AT 269
+#define KW_MACRO 270
+#define KW_GLOBALS 271
+#define KW_IGNOREPAT 272
+#define KW_SWITCH 273
+#define KW_IF 274
+#define KW_IFTIME 275
+#define KW_ELSE 276
+#define KW_RANDOM 277
+#define KW_ABSTRACT 278
+#define KW_EXTEND 279
+#define EXTENMARK 280
+#define KW_GOTO 281
+#define KW_JUMP 282
+#define KW_RETURN 283
+#define KW_BREAK 284
+#define KW_CONTINUE 285
+#define KW_REGEXTEN 286
+#define KW_HINT 287
+#define KW_FOR 288
+#define KW_WHILE 289
+#define KW_CASE 290
+#define KW_PATTERN 291
+#define KW_DEFAULT 292
+#define KW_CATCH 293
+#define KW_SWITCHES 294
+#define KW_ESWITCHES 295
+#define KW_INCLUDES 296
+#define KW_LOCAL 297
+#define word 298
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 54 "ael.y"
+{
+ int intval; /* integer value, typically flags */
+ char *str; /* strings */
+ struct pval *pval; /* full objects */
+}
+/* Line 1536 of yacc.c. */
+#line 131 "ael.tab.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+
diff --git a/trunk/res/ael/ael.y b/trunk/res/ael/ael.y
new file mode 100644
index 000000000..a8df1cb51
--- /dev/null
+++ b/trunk/res/ael/ael.y
@@ -0,0 +1,823 @@
+%{
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.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 Bison Grammar description of AEL2.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/ael_structs.h"
+
+pval * linku1(pval *head, pval *tail);
+static void set_dads(pval *dad, pval *child_list);
+void reset_parencount(yyscan_t yyscanner);
+void reset_semicount(yyscan_t yyscanner);
+void reset_argcount(yyscan_t yyscanner );
+
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+
+extern char *my_file;
+#ifdef AAL_ARGCHECK
+int ael_is_funcname(char *name);
+#endif
+static char *ael_token_subst(const char *mess);
+
+%}
+
+
+%union {
+ int intval; /* integer value, typically flags */
+ char *str; /* strings */
+ struct pval *pval; /* full objects */
+}
+
+%{
+ /* declaring these AFTER the union makes things a lot simpler! */
+void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
+int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner);
+
+/* create a new object with start-end marker */
+pval *npval(pvaltype type, int first_line, int last_line,
+ int first_column, int last_column);
+
+/* create a new object with start-end marker, simplified interface.
+ * Must be declared here because YYLTYPE is not known before
+ */
+static pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last);
+
+/* another frontend for npval, this time for a string */
+static pval *nword(char *string, YYLTYPE *pos);
+
+/* update end position of an object, return the object */
+static pval *update_last(pval *, YYLTYPE *);
+%}
+
+
+%token KW_CONTEXT LC RC LP RP SEMI EQ COMMA COLON AMPER BAR AT
+%token KW_MACRO KW_GLOBALS KW_IGNOREPAT KW_SWITCH KW_IF KW_IFTIME KW_ELSE KW_RANDOM KW_ABSTRACT KW_EXTEND
+%token EXTENMARK KW_GOTO KW_JUMP KW_RETURN KW_BREAK KW_CONTINUE KW_REGEXTEN KW_HINT
+%token KW_FOR KW_WHILE KW_CASE KW_PATTERN KW_DEFAULT KW_CATCH KW_SWITCHES KW_ESWITCHES
+%token KW_INCLUDES KW_LOCAL
+
+%right BAR COMMA
+
+%token <str> word
+
+%type <pval>includes
+%type <pval>includeslist
+%type <pval>switchlist
+%type <pval>eswitches
+%type <pval>switches
+%type <pval>macro_statement
+%type <pval>macro_statements
+%type <pval>case_statement
+%type <pval>case_statements
+%type <pval>eval_arglist
+%type <pval>application_call
+%type <pval>application_call_head
+%type <pval>macro_call
+%type <pval>target jumptarget
+%type <pval>statement
+%type <pval>switch_statement
+
+%type <pval>if_like_head
+%type <pval>statements
+%type <pval>extension
+%type <pval>ignorepat
+%type <pval>element
+%type <pval>elements
+%type <pval>arglist
+%type <pval>assignment
+%type <pval>local_assignment
+%type <pval>global_statements
+%type <pval>globals
+%type <pval>macro
+%type <pval>context
+%type <pval>object
+%type <pval>objects
+%type <pval>file
+/* XXX lr changes */
+%type <pval>opt_else
+%type <pval>timespec
+%type <pval>included_entry
+
+%type <str>opt_word
+%type <str>context_name
+%type <str>timerange
+
+%type <str>goto_word
+%type <str>word_list
+%type <str>word3_list hint_word
+%type <str>test_expr
+%type <str>opt_pri
+
+%type <intval>opt_abstract
+
+/*
+ * OPTIONS
+ */
+
+%locations /* track source location using @n variables (yylloc in flex) */
+%pure-parser /* pass yylval and yylloc as arguments to yylex(). */
+%name-prefix="ael_yy"
+/*
+ * add an additional argument, parseio, to yyparse(),
+ * which is then accessible in the grammar actions
+ */
+%parse-param {struct parse_io *parseio}
+
+/* there will be two shift/reduce conflicts, they involve the if statement, where a single statement occurs not wrapped in curlies in the "true" section
+ the default action to shift will attach the else to the preceeding if. */
+%expect 3
+%error-verbose
+
+/*
+ * declare destructors for objects.
+ * The former is for pval, the latter for strings.
+ * NOTE: we must not have a destructor for a 'file' object.
+ */
+%destructor {
+ destroy_pval($$);
+ prev_word=0;
+ } includes includeslist switchlist eswitches switches
+ macro_statement macro_statements case_statement case_statements
+ eval_arglist application_call application_call_head
+ macro_call target jumptarget statement switch_statement
+ if_like_head statements extension
+ ignorepat element elements arglist assignment local_assignment
+ global_statements globals macro context object objects
+ opt_else
+ timespec included_entry
+
+%destructor { free($$);} word word_list goto_word word3_list opt_word context_name
+ timerange
+ test_expr
+ opt_pri
+
+
+%%
+
+file : objects { $$ = parseio->pval = $1; }
+ ;
+
+objects : object {$$=$1;}
+ | objects object { $$ = linku1($1, $2); }
+ | objects error {$$=$1;}
+ ;
+
+object : context {$$=$1;}
+ | macro {$$=$1;}
+ | globals {$$=$1;}
+ | SEMI {$$=0;/* allow older docs to be read */}
+ ;
+
+context_name : word { $$ = $1; }
+ | KW_DEFAULT { $$ = strdup("default"); }
+ ;
+
+context : opt_abstract KW_CONTEXT context_name LC elements RC {
+ $$ = npval2(PV_CONTEXT, &@1, &@6);
+ $$->u1.str = $3;
+ $$->u2.statements = $5;
+ set_dads($$,$5);
+ $$->u3.abstract = $1;}
+ ;
+
+/* optional "abstract" keyword XXX there is no regression test for this */
+opt_abstract: KW_ABSTRACT { $$ = 1; }
+ | /* nothing */ { $$ = 0; }
+ | KW_EXTEND { $$ = 2; }
+ | KW_EXTEND KW_ABSTRACT { $$=3; }
+ | KW_ABSTRACT KW_EXTEND { $$=3; }
+ ;
+
+macro : KW_MACRO word LP arglist RP LC macro_statements RC {
+ $$ = npval2(PV_MACRO, &@1, &@8);
+ $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7;
+ set_dads($$,$7);}
+ ;
+
+globals : KW_GLOBALS LC global_statements RC {
+ $$ = npval2(PV_GLOBALS, &@1, &@4);
+ $$->u1.statements = $3;
+ set_dads($$,$3);}
+ ;
+
+global_statements : { $$ = NULL; }
+ | assignment global_statements {$$ = linku1($1, $2); }
+ | error global_statements {$$=$2;}
+ ;
+
+assignment : word EQ { reset_semicount(parseio->scanner); } word SEMI {
+ $$ = npval2(PV_VARDEC, &@1, &@5);
+ $$->u1.str = $1;
+ $$->u2.val = $4; }
+ ;
+
+local_assignment : KW_LOCAL word EQ { reset_semicount(parseio->scanner); } word SEMI {
+ $$ = npval2(PV_LOCALVARDEC, &@1, &@6);
+ $$->u1.str = $2;
+ $$->u2.val = $5; }
+ ;
+
+/* XXX this matches missing arguments, is this desired ? */
+arglist : /* empty */ { $$ = NULL; }
+ | word { $$ = nword($1, &@1); }
+ | arglist COMMA word { $$ = linku1($1, nword($3, &@3)); }
+ | arglist error {$$=$1;}
+ ;
+
+elements : {$$=0;}
+ | element elements { $$ = linku1($1, $2); }
+ | error elements { $$=$2;}
+ ;
+
+element : extension {$$=$1;}
+ | includes {$$=$1;}
+ | switches {$$=$1;}
+ | eswitches {$$=$1;}
+ | ignorepat {$$=$1;}
+ | assignment {$$=$1;}
+ | local_assignment {$$=$1;}
+ | word error {free($1); $$=0;}
+ | SEMI {$$=0;/* allow older docs to be read */}
+ ;
+
+ignorepat : KW_IGNOREPAT EXTENMARK word SEMI {
+ $$ = npval2(PV_IGNOREPAT, &@1, &@4);
+ $$->u1.str = $3;}
+ ;
+
+extension : word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@3);
+ $$->u1.str = $1;
+ $$->u2.statements = $3; set_dads($$,$3);}
+ | word AT word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@3);
+ $$->u1.str = malloc(strlen($1)+strlen($3)+2);
+ strcpy($$->u1.str,$1);
+ strcat($$->u1.str,"@");
+ strcat($$->u1.str,$3);
+ free($1);
+ $$->u2.statements = $5; set_dads($$,$5);}
+ | KW_REGEXTEN word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@4);
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);
+ $$->u4.regexten=1;}
+ | KW_HINT LP hint_word RP word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@7);
+ $$->u1.str = $5;
+ $$->u2.statements = $7; set_dads($$,$7);
+ $$->u3.hints = $3;}
+ | KW_REGEXTEN KW_HINT LP hint_word RP word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@8);
+ $$->u1.str = $6;
+ $$->u2.statements = $8; set_dads($$,$8);
+ $$->u4.regexten=1;
+ $$->u3.hints = $4;}
+ ;
+
+/* list of statements in a block or after a case label - can be empty */
+statements : /* empty */ { $$ = NULL; }
+ | statement statements { $$ = linku1($1, $2); }
+ | error statements {$$=$2;}
+ ;
+
+/* hh:mm-hh:mm, due to the way the parser works we do not
+ * detect the '-' but only the ':' as separator
+ */
+timerange: word3_list COLON word3_list COLON word3_list {
+ asprintf(&$$, "%s:%s:%s", $1, $3, $5);
+ free($1);
+ free($3);
+ free($5); }
+ | word { $$ = $1; }
+ ;
+
+/* full time specification range|dow|*|* */
+timespec : timerange BAR word3_list BAR word3_list BAR word3_list {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5);
+ $$->next->next->next = nword($7, &@7); }
+ ;
+
+/* expression used in if, random, while, switch */
+test_expr : LP { reset_parencount(parseio->scanner); } word_list RP { $$ = $3; }
+ ;
+
+/* 'if' like statements: if, iftime, random */
+if_like_head : KW_IF test_expr {
+ $$= npval2(PV_IF, &@1, &@2);
+ $$->u1.str = $2; }
+ | KW_RANDOM test_expr {
+ $$ = npval2(PV_RANDOM, &@1, &@2);
+ $$->u1.str=$2;}
+ | KW_IFTIME LP timespec RP {
+ $$ = npval2(PV_IFTIME, &@1, &@4);
+ $$->u1.list = $3;
+ prev_word = 0; }
+ ;
+
+/* word_list is a hack to fix a problem with context switching between bison and flex;
+ by the time you register a new context with flex, you've already got a look-ahead token
+ from the old context, with no way to put it back and start afresh. So, we kludge this
+ and merge the words back together. */
+
+word_list : word { $$ = $1;}
+ | word word {
+ asprintf(&($$), "%s%s", $1, $2);
+ free($1);
+ free($2);
+ prev_word = $$;}
+ ;
+
+hint_word : word { $$ = $1; }
+ | hint_word word {
+ asprintf(&($$), "%s %s", $1, $2);
+ free($1);
+ free($2); }
+ | hint_word COLON word {
+ asprintf(&($$), "%s:%s", $1, $3);
+ free($1);
+ free($3); }
+ | hint_word AMPER word { /* there are often '&' in hints */
+ asprintf(&($$), "%s&%s", $1, $3);
+ free($1);
+ free($3);}
+
+
+word3_list : word { $$ = $1;}
+ | word word {
+ asprintf(&($$), "%s%s", $1, $2);
+ free($1);
+ free($2);
+ prev_word = $$;}
+ | word word word {
+ asprintf(&($$), "%s%s%s", $1, $2, $3);
+ free($1);
+ free($2);
+ free($3);
+ prev_word=$$;}
+ ;
+
+goto_word : word { $$ = $1;}
+ | word word {
+ asprintf(&($$), "%s%s", $1, $2);
+ free($1);
+ free($2);}
+ | goto_word COLON word {
+ asprintf(&($$), "%s:%s", $1, $3);
+ free($1);
+ free($3);}
+ ;
+
+switch_statement : KW_SWITCH test_expr LC case_statements RC {
+ $$ = npval2(PV_SWITCH, &@1, &@5);
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);}
+ ;
+
+/*
+ * Definition of a statememt in our language
+ */
+statement : LC statements RC {
+ $$ = npval2(PV_STATEMENTBLOCK, &@1, &@3);
+ $$->u1.list = $2; set_dads($$,$2);}
+ | assignment { $$ = $1; }
+ | local_assignment { $$ = $1; }
+ | KW_GOTO target SEMI {
+ $$ = npval2(PV_GOTO, &@1, &@3);
+ $$->u1.list = $2;}
+ | KW_JUMP jumptarget SEMI {
+ $$ = npval2(PV_GOTO, &@1, &@3);
+ $$->u1.list = $2;}
+ | word COLON {
+ $$ = npval2(PV_LABEL, &@1, &@2);
+ $$->u1.str = $1; }
+ | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
+ {reset_semicount(parseio->scanner);} word SEMI
+ {reset_parencount(parseio->scanner);} word RP statement { /* XXX word_list maybe ? */
+ $$ = npval2(PV_FOR, &@1, &@12);
+ $$->u1.for_init = $4;
+ $$->u2.for_test=$7;
+ $$->u3.for_inc = $10;
+ $$->u4.for_statements = $12; set_dads($$,$12);}
+ | KW_WHILE test_expr statement {
+ $$ = npval2(PV_WHILE, &@1, &@3);
+ $$->u1.str = $2;
+ $$->u2.statements = $3; set_dads($$,$3);}
+ | switch_statement { $$ = $1; }
+ | AMPER macro_call SEMI { $$ = update_last($2, &@2); }
+ | application_call SEMI { $$ = update_last($1, &@2); }
+ | word SEMI {
+ $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
+ $$->u1.str = $1;}
+ | application_call EQ {reset_semicount(parseio->scanner);} word SEMI {
+ char *bufx;
+ int tot=0;
+ pval *pptr;
+ $$ = npval2(PV_VARDEC, &@1, &@5);
+ $$->u2.val=$4;
+ /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
+ /* string to big to fit in the buffer? */
+ tot+=strlen($1->u1.str);
+ for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
+ tot+=strlen(pptr->u1.str);
+ tot++; /* for a sep like a comma */
+ }
+ tot+=4; /* for safety */
+ bufx = calloc(1, tot);
+ strcpy(bufx,$1->u1.str);
+ strcat(bufx,"(");
+ /* XXX need to advance the pointer or the loop is very inefficient */
+ for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
+ if ( pptr != $1->u2.arglist )
+ strcat(bufx,",");
+ strcat(bufx,pptr->u1.str);
+ }
+ strcat(bufx,")");
+#ifdef AAL_ARGCHECK
+ if ( !ael_is_funcname($1->u1.str) )
+ ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Function call? The name %s is not in my internal list of function names\n",
+ my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str);
+#endif
+ $$->u1.str = bufx;
+ destroy_pval($1); /* the app call it is not, get rid of that chain */
+ prev_word = 0;
+ }
+ | KW_BREAK SEMI { $$ = npval2(PV_BREAK, &@1, &@2); }
+ | KW_RETURN SEMI { $$ = npval2(PV_RETURN, &@1, &@2); }
+ | KW_CONTINUE SEMI { $$ = npval2(PV_CONTINUE, &@1, &@2); }
+ | if_like_head statement opt_else {
+ $$ = update_last($1, &@2);
+ $$->u2.statements = $2; set_dads($$,$2);
+ $$->u3.else_statements = $3;set_dads($$,$3);}
+ | SEMI { $$=0; }
+ ;
+
+opt_else : KW_ELSE statement { $$ = $2; }
+ | { $$ = NULL ; }
+
+
+target : goto_word { $$ = nword($1, &@1); }
+ | goto_word BAR goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3); }
+ | goto_word COMMA goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3); }
+ | goto_word BAR goto_word BAR goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ | goto_word COMMA goto_word COMMA goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ | KW_DEFAULT BAR goto_word BAR goto_word {
+ $$ = nword(strdup("default"), &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ | KW_DEFAULT COMMA goto_word COMMA goto_word {
+ $$ = nword(strdup("default"), &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ ;
+
+opt_pri : /* empty */ { $$ = strdup("1"); }
+ | COMMA word { $$ = $2; }
+ ;
+
+/* XXX please document the form of jumptarget */
+jumptarget : goto_word opt_pri { /* ext[, pri] default 1 */
+ $$ = nword($1, &@1);
+ $$->next = nword($2, &@2); } /* jump extension[,priority][@context] */
+ | goto_word opt_pri AT context_name { /* context, ext, pri */
+ $$ = nword($4, &@4);
+ $$->next = nword($1, &@1);
+ $$->next->next = nword($2, &@2); }
+ ;
+
+macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP {
+ /* XXX original code had @2 but i think we need @5 */
+ $$ = npval2(PV_MACRO_CALL, &@1, &@5);
+ $$->u1.str = $1;
+ $$->u2.arglist = $4;}
+ | word LP RP {
+ $$= npval2(PV_MACRO_CALL, &@1, &@3);
+ $$->u1.str = $1; }
+ ;
+
+/* XXX application_call_head must be revised. Having 'word LP { ...'
+ * just as above should work fine, however it gives a different result.
+ */
+application_call_head: word LP {reset_argcount(parseio->scanner);} {
+ if (strcasecmp($1,"goto") == 0) {
+ $$ = npval2(PV_GOTO, &@1, &@2);
+ free($1); /* won't be using this */
+ ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Suggestion: Use the goto statement instead of the Goto() application call in AEL.\n", my_file, @1.first_line, @1.first_column, @1.last_column );
+ } else {
+ $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
+ $$->u1.str = $1;
+ } }
+ ;
+
+application_call : application_call_head eval_arglist RP {
+ $$ = update_last($1, &@3);
+ if( $$->type == PV_GOTO )
+ $$->u1.list = $2;
+ else
+ $$->u2.arglist = $2;
+ }
+ | application_call_head RP { $$ = update_last($1, &@2); }
+ ;
+
+opt_word : word { $$ = $1 }
+ | { $$ = strdup(""); }
+ ;
+
+eval_arglist : word_list { $$ = nword($1, &@1); }
+ | /*nothing! */ {
+ $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/);
+ $$->u1.str = strdup(""); }
+ | eval_arglist COMMA opt_word { $$ = linku1($1, nword($3, &@3)); }
+ ;
+
+case_statements: /* empty */ { $$ = NULL; }
+ | case_statement case_statements { $$ = linku1($1, $2); }
+ ;
+
+case_statement: KW_CASE word COLON statements {
+ $$ = npval2(PV_CASE, &@1, &@3); /* XXX 3 or 4 ? */
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);}
+ | KW_DEFAULT COLON statements {
+ $$ = npval2(PV_DEFAULT, &@1, &@3);
+ $$->u1.str = NULL;
+ $$->u2.statements = $3;set_dads($$,$3);}
+ | KW_PATTERN word COLON statements {
+ $$ = npval2(PV_PATTERN, &@1, &@4); /* XXX@3 or @4 ? */
+ $$->u1.str = $2;
+ $$->u2.statements = $4;set_dads($$,$4);}
+ ;
+
+macro_statements: /* empty */ { $$ = NULL; }
+ | macro_statement macro_statements { $$ = linku1($1, $2); }
+ ;
+
+macro_statement : statement {$$=$1;}
+ | includes { $$=$1;}
+ | KW_CATCH word LC statements RC {
+ $$ = npval2(PV_CATCH, &@1, &@5);
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);}
+ ;
+
+switches : KW_SWITCHES LC switchlist RC {
+ $$ = npval2(PV_SWITCHES, &@1, &@2);
+ $$->u1.list = $3; set_dads($$,$3);}
+ ;
+
+eswitches : KW_ESWITCHES LC switchlist RC {
+ $$ = npval2(PV_ESWITCHES, &@1, &@2);
+ $$->u1.list = $3; set_dads($$,$3);}
+ ;
+
+switchlist : /* empty */ { $$ = NULL; }
+ | word SEMI switchlist { $$ = linku1(nword($1, &@1), $3); }
+ | word AT word SEMI switchlist { char *x; asprintf(&x,"%s@%s", $1,$3); free($1); free($3);
+ $$ = linku1(nword(x, &@1), $5);}
+ | error switchlist {$$=$2;}
+ ;
+
+included_entry : context_name { $$ = nword($1, &@1); }
+ | context_name BAR timespec {
+ $$ = nword($1, &@1);
+ $$->u2.arglist = $3;
+ prev_word=0; /* XXX sure ? */ }
+ ;
+
+/* list of ';' separated context names followed by optional timespec */
+includeslist : included_entry SEMI { $$ = $1; }
+ | includeslist included_entry SEMI { $$ = linku1($1, $2); }
+ | includeslist error {$$=$1;}
+ ;
+
+includes : KW_INCLUDES LC includeslist RC {
+ $$ = npval2(PV_INCLUDES, &@1, &@4);
+ $$->u1.list = $3;set_dads($$,$3);}
+ | KW_INCLUDES LC RC {
+ $$ = npval2(PV_INCLUDES, &@1, &@3);}
+ ;
+
+
+%%
+
+static char *token_equivs1[] =
+{
+ "AMPER",
+ "AT",
+ "BAR",
+ "COLON",
+ "COMMA",
+ "EQ",
+ "EXTENMARK",
+ "KW_BREAK",
+ "KW_CASE",
+ "KW_CATCH",
+ "KW_CONTEXT",
+ "KW_CONTINUE",
+ "KW_DEFAULT",
+ "KW_ELSE",
+ "KW_ESWITCHES",
+ "KW_FOR",
+ "KW_GLOBALS",
+ "KW_GOTO",
+ "KW_HINT",
+ "KW_IFTIME",
+ "KW_IF",
+ "KW_IGNOREPAT",
+ "KW_INCLUDES"
+ "KW_JUMP",
+ "KW_MACRO",
+ "KW_PATTERN",
+ "KW_REGEXTEN",
+ "KW_RETURN",
+ "KW_SWITCHES",
+ "KW_SWITCH",
+ "KW_WHILE",
+ "LC",
+ "LP",
+ "RC",
+ "RP",
+ "SEMI",
+};
+
+static char *token_equivs2[] =
+{
+ "&",
+ "@",
+ "|",
+ ":",
+ ",",
+ "=",
+ "=>",
+ "break",
+ "case",
+ "catch",
+ "context",
+ "continue",
+ "default",
+ "else",
+ "eswitches",
+ "for",
+ "globals",
+ "goto",
+ "hint",
+ "ifTime",
+ "if",
+ "ignorepat",
+ "includes"
+ "jump",
+ "macro",
+ "pattern",
+ "regexten",
+ "return",
+ "switches",
+ "switch",
+ "while",
+ "{",
+ "(",
+ "}",
+ ")",
+ ";",
+};
+
+
+static char *ael_token_subst(const char *mess)
+{
+ /* calc a length, malloc, fill, and return; yyerror had better free it! */
+ int len=0,i;
+ const char *p;
+ char *res, *s,*t;
+ int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
+
+ for (p=mess; *p; p++) {
+ for (i=0; i<token_equivs_entries; i++) {
+ if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
+ {
+ len+=strlen(token_equivs2[i])+2;
+ p += strlen(token_equivs1[i])-1;
+ break;
+ }
+ }
+ len++;
+ }
+ res = calloc(1, len+1);
+ res[0] = 0;
+ s = res;
+ for (p=mess; *p;) {
+ int found = 0;
+ for (i=0; i<token_equivs_entries; i++) {
+ if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
+ *s++ = '\'';
+ for (t=token_equivs2[i]; *t;) {
+ *s++ = *t++;
+ }
+ *s++ = '\'';
+ p += strlen(token_equivs1[i]);
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ *s++ = *p++;
+ }
+ *s++ = 0;
+ return res;
+}
+
+void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s)
+{
+ char *s2 = ael_token_subst((char *)s);
+ if (locp->first_line == locp->last_line) {
+ ast_log(LOG_ERROR, "==== File: %s, Line %d, Cols: %d-%d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_column, s2);
+ } else {
+ ast_log(LOG_ERROR, "==== File: %s, Line %d Col %d to Line %d Col %d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_line, locp->last_column, s2);
+ }
+ free(s2);
+ parseio->syntax_error_count++;
+}
+
+struct pval *npval(pvaltype type, int first_line, int last_line,
+ int first_column, int last_column)
+{
+ pval *z = calloc(1, sizeof(struct pval));
+ z->type = type;
+ z->startline = first_line;
+ z->endline = last_line;
+ z->startcol = first_column;
+ z->endcol = last_column;
+ z->filename = strdup(my_file);
+ return z;
+}
+
+static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last)
+{
+ return npval(type, first->first_line, last->last_line,
+ first->first_column, last->last_column);
+}
+
+static struct pval *update_last(pval *obj, YYLTYPE *last)
+{
+ obj->endline = last->last_line;
+ obj->endcol = last->last_column;
+ return obj;
+}
+
+/* frontend for npval to create a PV_WORD string from the given token */
+static pval *nword(char *string, YYLTYPE *pos)
+{
+ pval *p = npval2(PV_WORD, pos, pos);
+ if (p)
+ p->u1.str = string;
+ return p;
+}
+
+/* this routine adds a dad ptr to each element in the list */
+static void set_dads(struct pval *dad, struct pval *child_list)
+{
+ struct pval *t;
+
+ for(t=child_list;t;t=t->next) /* simple stuff */
+ t->dad = dad;
+}
+
diff --git a/trunk/res/ael/ael_lex.c b/trunk/res/ael/ael_lex.c
new file mode 100644
index 000000000..26206af5d
--- /dev/null
+++ b/trunk/res/ael/ael_lex.c
@@ -0,0 +1,3127 @@
+#line 2 "ael_lex.c"
+
+#line 4 "ael_lex.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 33
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+#include "asterisk.h"
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+int ael_yylex_init (yyscan_t* scanner);
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE ael_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via ael_yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void ael_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void ael_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE ael_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void ael_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ael_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ael_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void ael_yypop_buffer_state (yyscan_t yyscanner );
+
+static void ael_yyensure_buffer_stack (yyscan_t yyscanner );
+static void ael_yy_load_buffer_state (yyscan_t yyscanner );
+static void ael_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER ael_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE ael_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE ael_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE ael_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *ael_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *ael_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void ael_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer ael_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ ael_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ ael_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define ael_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyg->yytext_ptr -= yyg->yy_more_len; \
+ yyleng = (size_t) (yy_cp - yyg->yytext_ptr); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 63
+#define YY_END_OF_BUFFER 64
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[244] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 43, 43,
+ 64, 63, 50, 48, 49, 51, 51, 9, 3, 4,
+ 7, 51, 8, 5, 6, 12, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 1, 10, 2, 63, 53, 52, 63, 54,
+ 63, 59, 60, 61, 63, 63, 55, 56, 57, 63,
+ 58, 43, 44, 45, 50, 49, 51, 51, 42, 13,
+ 11, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 22, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 0, 53, 52, 0, 54, 53,
+
+ 52, 54, 0, 59, 60, 61, 0, 59, 60, 61,
+ 0, 55, 56, 57, 0, 58, 55, 56, 57, 58,
+ 43, 44, 45, 46, 45, 47, 51, 13, 13, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 33, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 35, 51, 51,
+ 51, 27, 51, 51, 51, 28, 26, 51, 51, 51,
+ 29, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 31, 38, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 19, 17, 51, 51, 51, 51, 51, 34,
+
+ 51, 51, 51, 51, 51, 51, 16, 51, 23, 51,
+ 51, 51, 24, 51, 30, 21, 51, 51, 14, 51,
+ 36, 51, 18, 51, 51, 37, 51, 51, 51, 15,
+ 32, 51, 51, 41, 25, 39, 0, 40, 20, 0,
+ 0, 62, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 5, 6, 7, 5, 1, 8, 5, 9,
+ 10, 11, 5, 12, 5, 5, 13, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 14, 15, 5,
+ 16, 17, 1, 18, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 19, 5, 5, 5, 5, 5, 5,
+ 20, 21, 22, 1, 5, 1, 23, 24, 25, 26,
+
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 5, 39, 40, 41, 42, 5, 43, 44,
+ 5, 5, 45, 46, 47, 1, 1, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48
+ } ;
+
+static yyconst flex_int32_t yy_meta[49] =
+ { 0,
+ 1, 1, 2, 1, 3, 4, 3, 1, 1, 1,
+ 5, 1, 3, 1, 1, 1, 3, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 1, 3, 3
+ } ;
+
+static yyconst flex_int16_t yy_base[257] =
+ { 0,
+ 0, 0, 40, 43, 82, 121, 160, 199, 48, 55,
+ 320, 986, 317, 986, 314, 0, 286, 986, 986, 986,
+ 986, 43, 986, 986, 299, 986, 291, 275, 32, 286,
+ 33, 275, 34, 280, 46, 268, 272, 285, 284, 49,
+ 263, 275, 986, 986, 986, 74, 986, 986, 90, 986,
+ 238, 986, 986, 986, 277, 316, 986, 986, 986, 355,
+ 986, 301, 986, 67, 301, 298, 0, 265, 0, 402,
+ 986, 260, 269, 65, 259, 266, 253, 248, 249, 250,
+ 251, 243, 246, 262, 244, 254, 243, 252, 251, 234,
+ 238, 52, 242, 241, 104, 986, 986, 138, 986, 143,
+
+ 177, 182, 440, 986, 986, 986, 479, 518, 557, 596,
+ 635, 986, 986, 986, 674, 986, 713, 752, 791, 830,
+ 268, 986, 104, 986, 105, 986, 245, 0, 877, 228,
+ 245, 240, 241, 224, 241, 236, 231, 234, 0, 233,
+ 219, 214, 223, 215, 217, 212, 226, 206, 202, 216,
+ 214, 198, 198, 204, 203, 197, 202, 0, 204, 101,
+ 191, 0, 191, 195, 207, 0, 0, 193, 187, 183,
+ 0, 189, 181, 190, 179, 171, 175, 188, 185, 168,
+ 183, 0, 0, 157, 164, 162, 170, 168, 159, 162,
+ 157, 153, 0, 0, 139, 142, 135, 138, 137, 0,
+
+ 136, 136, 116, 114, 114, 124, 0, 110, 0, 108,
+ 118, 108, 0, 113, 0, 112, 111, 93, 0, 106,
+ 0, 96, 0, 86, 61, 0, 62, 49, 118, 0,
+ 0, 46, 38, 0, 0, 0, 169, 0, 0, 0,
+ 51, 986, 986, 923, 928, 933, 938, 941, 946, 951,
+ 956, 961, 965, 970, 975, 980
+ } ;
+
+static yyconst flex_int16_t yy_def[257] =
+ { 0,
+ 243, 1, 244, 244, 245, 245, 246, 246, 247, 247,
+ 243, 243, 243, 243, 243, 248, 248, 243, 243, 243,
+ 243, 248, 243, 243, 243, 243, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 243, 243, 243, 249, 243, 243, 249, 243,
+ 250, 243, 243, 243, 250, 251, 243, 243, 243, 251,
+ 243, 252, 243, 253, 243, 243, 248, 248, 248, 254,
+ 243, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 249, 243, 243, 249, 243, 249,
+
+ 249, 249, 250, 243, 243, 243, 250, 250, 250, 250,
+ 251, 243, 243, 243, 251, 243, 251, 251, 251, 251,
+ 252, 243, 253, 243, 253, 243, 248, 255, 254, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 243, 248, 248, 256,
+ 256, 243, 0, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243
+ } ;
+
+static yyconst flex_int16_t yy_nxt[1035] =
+ { 0,
+ 12, 13, 14, 15, 16, 16, 17, 18, 19, 20,
+ 16, 21, 22, 23, 24, 25, 16, 26, 16, 16,
+ 16, 16, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 16, 37, 38, 16, 16, 39, 40, 41,
+ 16, 16, 42, 16, 43, 44, 45, 16, 47, 48,
+ 63, 47, 48, 69, 74, 70, 242, 63, 64, 47,
+ 49, 50, 47, 49, 50, 64, 77, 81, 75, 124,
+ 82, 91, 78, 84, 85, 92, 79, 125, 239, 126,
+ 151, 86, 96, 97, 47, 238, 50, 47, 236, 50,
+ 52, 53, 152, 96, 98, 99, 54, 235, 100, 101,
+
+ 234, 52, 55, 53, 132, 133, 124, 124, 233, 100,
+ 98, 102, 96, 97, 243, 125, 243, 243, 96, 237,
+ 99, 237, 232, 96, 98, 99, 52, 184, 53, 52,
+ 53, 185, 231, 230, 100, 54, 102, 229, 228, 227,
+ 52, 55, 53, 226, 225, 224, 100, 101, 96, 223,
+ 99, 96, 97, 222, 221, 220, 219, 100, 98, 102,
+ 218, 217, 96, 98, 99, 52, 216, 53, 57, 58,
+ 237, 59, 237, 215, 240, 214, 213, 212, 211, 57,
+ 60, 61, 100, 210, 102, 96, 97, 96, 209, 99,
+ 96, 97, 208, 207, 206, 205, 96, 98, 99, 204,
+
+ 203, 96, 98, 99, 57, 202, 61, 57, 58, 201,
+ 59, 200, 199, 198, 197, 196, 195, 194, 57, 60,
+ 61, 96, 193, 99, 192, 191, 96, 190, 99, 189,
+ 188, 187, 186, 183, 182, 181, 180, 179, 178, 177,
+ 176, 175, 174, 57, 173, 61, 104, 105, 172, 171,
+ 170, 169, 106, 168, 167, 166, 165, 104, 107, 105,
+ 164, 163, 162, 161, 160, 159, 158, 157, 156, 155,
+ 122, 154, 153, 150, 149, 148, 147, 146, 145, 144,
+ 143, 142, 104, 141, 105, 108, 109, 140, 139, 138,
+ 137, 110, 136, 135, 134, 131, 108, 107, 109, 130,
+
+ 127, 66, 65, 122, 94, 93, 90, 89, 88, 87,
+ 83, 80, 76, 73, 72, 71, 68, 66, 65, 243,
+ 243, 108, 243, 109, 112, 113, 243, 114, 243, 243,
+ 243, 243, 243, 243, 243, 112, 115, 116, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 112, 243, 116, 117, 118, 243, 119, 243, 243, 243,
+ 243, 243, 243, 243, 117, 115, 120, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 117,
+
+ 243, 120, 128, 128, 243, 128, 243, 243, 243, 128,
+ 128, 128, 243, 128, 243, 128, 128, 128, 243, 128,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 128, 104, 105,
+ 243, 243, 243, 243, 106, 243, 243, 243, 243, 104,
+ 107, 105, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 104, 243, 105, 108, 109, 243,
+ 243, 243, 243, 110, 243, 243, 243, 243, 108, 107,
+
+ 109, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 108, 243, 109, 104, 105, 243, 243,
+ 243, 243, 106, 243, 243, 243, 243, 104, 107, 105,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 104, 243, 105, 104, 105, 243, 243, 243,
+ 243, 106, 243, 243, 243, 243, 104, 107, 105, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+
+ 243, 104, 243, 105, 104, 105, 243, 243, 243, 243,
+ 106, 243, 243, 243, 243, 104, 107, 105, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 104, 243, 105, 112, 113, 243, 114, 243, 243, 243,
+ 243, 243, 243, 243, 112, 115, 116, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 112,
+ 243, 116, 117, 118, 243, 119, 243, 243, 243, 243,
+ 243, 243, 243, 117, 115, 120, 243, 243, 243, 243,
+
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 117, 243,
+ 120, 112, 113, 243, 114, 243, 243, 243, 243, 243,
+ 243, 243, 112, 115, 116, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 112, 243, 116,
+ 112, 113, 243, 114, 243, 243, 243, 243, 243, 243,
+ 243, 112, 115, 116, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 112, 243, 116, 112,
+
+ 113, 243, 114, 243, 243, 243, 243, 243, 243, 243,
+ 112, 115, 116, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 112, 243, 116, 112, 113,
+ 243, 114, 243, 243, 243, 243, 243, 243, 243, 112,
+ 115, 116, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 112, 243, 116, 128, 128, 243,
+ 128, 243, 243, 243, 128, 128, 128, 243, 128, 243,
+ 128, 128, 128, 243, 128, 243, 243, 243, 243, 243,
+
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 128, 46, 46, 46, 46, 46, 51, 51,
+ 51, 51, 51, 56, 56, 56, 56, 56, 62, 62,
+ 62, 62, 62, 67, 67, 67, 95, 95, 95, 95,
+ 95, 103, 103, 103, 103, 103, 111, 111, 111, 111,
+ 111, 121, 121, 121, 121, 123, 123, 123, 123, 123,
+ 129, 243, 129, 129, 129, 128, 243, 128, 128, 128,
+ 241, 241, 241, 243, 241, 11, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243
+ } ;
+
+static yyconst flex_int16_t yy_chk[1035] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 3,
+ 9, 4, 4, 22, 29, 22, 241, 10, 9, 3,
+ 3, 3, 4, 4, 4, 10, 31, 33, 29, 64,
+ 33, 40, 31, 35, 35, 40, 31, 64, 233, 64,
+ 92, 35, 46, 46, 3, 232, 3, 4, 228, 4,
+ 5, 5, 92, 46, 46, 46, 5, 227, 49, 49,
+
+ 225, 5, 5, 5, 74, 74, 123, 125, 224, 49,
+ 49, 49, 95, 95, 123, 125, 123, 125, 46, 229,
+ 46, 229, 222, 95, 95, 95, 5, 160, 5, 6,
+ 6, 160, 220, 218, 49, 6, 49, 217, 216, 214,
+ 6, 6, 6, 212, 211, 210, 98, 98, 95, 208,
+ 95, 100, 100, 206, 205, 204, 203, 98, 98, 98,
+ 202, 201, 100, 100, 100, 6, 199, 6, 7, 7,
+ 237, 7, 237, 198, 237, 197, 196, 195, 192, 7,
+ 7, 7, 98, 191, 98, 101, 101, 100, 190, 100,
+ 102, 102, 189, 188, 187, 186, 101, 101, 101, 185,
+
+ 184, 102, 102, 102, 7, 181, 7, 8, 8, 180,
+ 8, 179, 178, 177, 176, 175, 174, 173, 8, 8,
+ 8, 101, 172, 101, 170, 169, 102, 168, 102, 165,
+ 164, 163, 161, 159, 157, 156, 155, 154, 153, 152,
+ 151, 150, 149, 8, 148, 8, 51, 51, 147, 146,
+ 145, 144, 51, 143, 142, 141, 140, 51, 51, 51,
+ 138, 137, 136, 135, 134, 133, 132, 131, 130, 127,
+ 121, 94, 93, 91, 90, 89, 88, 87, 86, 85,
+ 84, 83, 51, 82, 51, 55, 55, 81, 80, 79,
+ 78, 55, 77, 76, 75, 73, 55, 55, 55, 72,
+
+ 68, 66, 65, 62, 42, 41, 39, 38, 37, 36,
+ 34, 32, 30, 28, 27, 25, 17, 15, 13, 11,
+ 0, 55, 0, 55, 56, 56, 0, 56, 0, 0,
+ 0, 0, 0, 0, 0, 56, 56, 56, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 56, 0, 56, 60, 60, 0, 60, 0, 0, 0,
+ 0, 0, 0, 0, 60, 60, 60, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 60,
+
+ 0, 60, 70, 70, 0, 70, 0, 0, 0, 70,
+ 70, 70, 0, 70, 0, 70, 70, 70, 0, 70,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 70, 103, 103,
+ 0, 0, 0, 0, 103, 0, 0, 0, 0, 103,
+ 103, 103, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 103, 0, 103, 107, 107, 0,
+ 0, 0, 0, 107, 0, 0, 0, 0, 107, 107,
+
+ 107, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 107, 0, 107, 108, 108, 0, 0,
+ 0, 0, 108, 0, 0, 0, 0, 108, 108, 108,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 108, 0, 108, 109, 109, 0, 0, 0,
+ 0, 109, 0, 0, 0, 0, 109, 109, 109, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 109, 0, 109, 110, 110, 0, 0, 0, 0,
+ 110, 0, 0, 0, 0, 110, 110, 110, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 110, 0, 110, 111, 111, 0, 111, 0, 0, 0,
+ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 111,
+ 0, 111, 115, 115, 0, 115, 0, 0, 0, 0,
+ 0, 0, 0, 115, 115, 115, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 115, 0,
+ 115, 117, 117, 0, 117, 0, 0, 0, 0, 0,
+ 0, 0, 117, 117, 117, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 117, 0, 117,
+ 118, 118, 0, 118, 0, 0, 0, 0, 0, 0,
+ 0, 118, 118, 118, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 118, 0, 118, 119,
+
+ 119, 0, 119, 0, 0, 0, 0, 0, 0, 0,
+ 119, 119, 119, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 119, 0, 119, 120, 120,
+ 0, 120, 0, 0, 0, 0, 0, 0, 0, 120,
+ 120, 120, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 120, 0, 120, 129, 129, 0,
+ 129, 0, 0, 0, 129, 129, 129, 0, 129, 0,
+ 129, 129, 129, 0, 129, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 129, 244, 244, 244, 244, 244, 245, 245,
+ 245, 245, 245, 246, 246, 246, 246, 246, 247, 247,
+ 247, 247, 247, 248, 248, 248, 249, 249, 249, 249,
+ 249, 250, 250, 250, 250, 250, 251, 251, 251, 251,
+ 251, 252, 252, 252, 252, 253, 253, 253, 253, 253,
+ 254, 0, 254, 254, 254, 255, 0, 255, 255, 255,
+ 256, 256, 256, 0, 256, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
+ 243, 243, 243, 243
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() (yyg->yy_more_flag = 1)
+#define YY_MORE_ADJ yyg->yy_more_len
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "ael.flex"
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.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 Flex scanner description of tokens used in AEL2 .
+ *
+ */
+/*
+ * Start with flex options:
+ *
+ * %x describes the contexts we have: paren, semic and argg, plus INITIAL
+ */
+
+/* prefix used for various globally-visible functions and variables.
+ * This renames also ael_yywrap, but since we do not use it, we just
+ * add option noyywrap to remove it.
+ */
+/* ael_yyfree normally just frees its arg. It can be null sometimes,
+ which some systems will complain about, so, we'll define our own version */
+/* batch gives a bit more performance if we are using it in
+ * a non-interactive mode. We probably don't care much.
+ */
+/* outfile is the filename to be used instead of lex.yy.c */
+/*
+ * These are not supported in flex 2.5.4, but we need them
+ * at the moment:
+ * reentrant produces a thread-safe parser. Not 100% sure that
+ * we require it, though.
+ * bison-bridge passes an additional yylval argument to ael_yylex().
+ * bison-locations is probably not needed.
+ */
+#line 63 "ael.flex"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#if defined(__Darwin__) || defined(__CYGWIN__)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+# include <glob.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/utils.h"
+#include "ael/ael.tab.h"
+#include "asterisk/ael_structs.h"
+
+/*
+ * A stack to keep track of matching brackets ( [ { } ] )
+ */
+static char pbcstack[400]; /* XXX missing size checks */
+static int pbcpos = 0;
+static void pbcpush(char x);
+static int pbcpop(char x);
+
+static int parencount = 0;
+
+/*
+ * current line, column and filename, updated as we read the input.
+ */
+static int my_lineno = 1; /* current line in the source */
+static int my_col = 1; /* current column in the source */
+char *my_file = 0; /* used also in the bison code */
+char *prev_word; /* XXX document it */
+
+#define MAX_INCLUDE_DEPTH 50
+
+/*
+ * flex is not too smart, and generates global functions
+ * without prototypes so the compiler may complain.
+ * To avoid that, we declare the prototypes here,
+ * even though these functions are not used.
+ */
+int ael_yyget_column (yyscan_t yyscanner);
+void ael_yyset_column (int column_no , yyscan_t yyscanner);
+
+int ael_yyparse (struct parse_io *);
+
+/*
+ * A stack to process include files.
+ * As we switch into the new file we need to store the previous
+ * state to restore it later.
+ */
+struct stackelement {
+ char *fname;
+ int lineno;
+ int colno;
+ glob_t globbuf; /* the current globbuf */
+ int globbuf_pos; /* where we are in the current globbuf */
+ YY_BUFFER_STATE bufstate;
+};
+
+static struct stackelement include_stack[MAX_INCLUDE_DEPTH];
+static int include_stack_index = 0;
+static void setup_filestack(char *fnamebuf, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t xscan, int create);
+
+/*
+ * if we use the @n feature of bison, we must supply the start/end
+ * location of tokens in the structure pointed by yylloc.
+ * Simple tokens are just assumed to be on the same line, so
+ * the line number is constant, and the column is incremented
+ * by the length of the token.
+ */
+#ifdef FLEX_BETA /* set for 2.5.33 */
+
+/* compute the total number of lines and columns in the text
+ * passed as argument.
+ */
+static void pbcwhere(const char *text, int *line, int *col )
+{
+ int loc_line = *line;
+ int loc_col = *col;
+ char c;
+ while ( (c = *text++) ) {
+ if ( c == '\t' ) {
+ loc_col += 8 - (loc_col % 8);
+ } else if ( c == '\n' ) {
+ loc_line++;
+ loc_col = 1;
+ } else
+ loc_col++;
+ }
+ *line = loc_line;
+ *col = loc_col;
+}
+
+#define STORE_POS do { \
+ yylloc->first_line = yylloc->last_line = my_lineno; \
+ yylloc->first_column=my_col; \
+ yylloc->last_column=my_col+yyleng-1; \
+ my_col+=yyleng; \
+ } while (0)
+
+#define STORE_LOC do { \
+ yylloc->first_line = my_lineno; \
+ yylloc->first_column=my_col; \
+ pbcwhere(yytext, &my_lineno, &my_col); \
+ yylloc->last_line = my_lineno; \
+ yylloc->last_column = my_col - 1; \
+ } while (0)
+#else
+#define STORE_POS
+#define STORE_LOC
+#endif
+#line 909 "ael_lex.c"
+
+#define INITIAL 0
+#define paren 1
+#define semic 2
+#define argg 3
+#define comment 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ YYLTYPE * yylloc_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+ # define yylloc yyg->yylloc_r
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int ael_yylex_destroy (yyscan_t yyscanner );
+
+int ael_yyget_debug (yyscan_t yyscanner );
+
+void ael_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE ael_yyget_extra (yyscan_t yyscanner );
+
+void ael_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *ael_yyget_in (yyscan_t yyscanner );
+
+void ael_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *ael_yyget_out (yyscan_t yyscanner );
+
+void ael_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int ael_yyget_leng (yyscan_t yyscanner );
+
+char *ael_yyget_text (yyscan_t yyscanner );
+
+int ael_yyget_lineno (yyscan_t yyscanner );
+
+void ael_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * ael_yyget_lval (yyscan_t yyscanner );
+
+void ael_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+ YYLTYPE *ael_yyget_lloc (yyscan_t yyscanner );
+
+ void ael_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int ael_yywrap (yyscan_t yyscanner );
+#else
+extern int ael_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int ael_yylex (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 187 "ael.flex"
+
+
+#line 1151 "ael_lex.c"
+
+ yylval = yylval_param;
+
+ yylloc = yylloc_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ ael_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ael_yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yyg->yy_more_len = 0;
+ if ( yyg->yy_more_flag )
+ {
+ yyg->yy_more_len = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ yyg->yy_more_flag = 0;
+ }
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 244 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 243 );
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 189 "ael.flex"
+{ STORE_POS; return LC;}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 190 "ael.flex"
+{ STORE_POS; return RC;}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 191 "ael.flex"
+{ STORE_POS; return LP;}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 192 "ael.flex"
+{ STORE_POS; return RP;}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 193 "ael.flex"
+{ STORE_POS; return SEMI;}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 194 "ael.flex"
+{ STORE_POS; return EQ;}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 195 "ael.flex"
+{ STORE_POS; return COMMA;}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 196 "ael.flex"
+{ STORE_POS; return COLON;}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 197 "ael.flex"
+{ STORE_POS; return AMPER;}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 198 "ael.flex"
+{ STORE_POS; return BAR;}
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 199 "ael.flex"
+{ STORE_POS; return EXTENMARK;}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 200 "ael.flex"
+{ STORE_POS; return AT;}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 201 "ael.flex"
+{/*comment*/}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 202 "ael.flex"
+{ STORE_POS; return KW_CONTEXT;}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 203 "ael.flex"
+{ STORE_POS; return KW_ABSTRACT;}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 204 "ael.flex"
+{ STORE_POS; return KW_EXTEND;}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 205 "ael.flex"
+{ STORE_POS; return KW_MACRO;};
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 206 "ael.flex"
+{ STORE_POS; return KW_GLOBALS;}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 207 "ael.flex"
+{ STORE_POS; return KW_LOCAL;}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 208 "ael.flex"
+{ STORE_POS; return KW_IGNOREPAT;}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 209 "ael.flex"
+{ STORE_POS; return KW_SWITCH;}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 210 "ael.flex"
+{ STORE_POS; return KW_IF;}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 211 "ael.flex"
+{ STORE_POS; return KW_IFTIME;}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 212 "ael.flex"
+{ STORE_POS; return KW_RANDOM;}
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 213 "ael.flex"
+{ STORE_POS; return KW_REGEXTEN;}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 214 "ael.flex"
+{ STORE_POS; return KW_HINT;}
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 215 "ael.flex"
+{ STORE_POS; return KW_ELSE;}
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 216 "ael.flex"
+{ STORE_POS; return KW_GOTO;}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 217 "ael.flex"
+{ STORE_POS; return KW_JUMP;}
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 218 "ael.flex"
+{ STORE_POS; return KW_RETURN;}
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 219 "ael.flex"
+{ STORE_POS; return KW_BREAK;}
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 220 "ael.flex"
+{ STORE_POS; return KW_CONTINUE;}
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 221 "ael.flex"
+{ STORE_POS; return KW_FOR;}
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 222 "ael.flex"
+{ STORE_POS; return KW_WHILE;}
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 223 "ael.flex"
+{ STORE_POS; return KW_CASE;}
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 224 "ael.flex"
+{ STORE_POS; return KW_DEFAULT;}
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 225 "ael.flex"
+{ STORE_POS; return KW_PATTERN;}
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 226 "ael.flex"
+{ STORE_POS; return KW_CATCH;}
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 227 "ael.flex"
+{ STORE_POS; return KW_SWITCHES;}
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 228 "ael.flex"
+{ STORE_POS; return KW_ESWITCHES;}
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 229 "ael.flex"
+{ STORE_POS; return KW_INCLUDES;}
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 230 "ael.flex"
+{ BEGIN(comment); my_col += 2; }
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 232 "ael.flex"
+{ my_col += yyleng; }
+ YY_BREAK
+case 44:
+/* rule 44 can match eol */
+YY_RULE_SETUP
+#line 233 "ael.flex"
+{ ++my_lineno; my_col=1;}
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 234 "ael.flex"
+{ my_col += yyleng; }
+ YY_BREAK
+case 46:
+/* rule 46 can match eol */
+YY_RULE_SETUP
+#line 235 "ael.flex"
+{ ++my_lineno; my_col=1;}
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 236 "ael.flex"
+{ my_col += 2; BEGIN(INITIAL); }
+ YY_BREAK
+case 48:
+/* rule 48 can match eol */
+YY_RULE_SETUP
+#line 238 "ael.flex"
+{ my_lineno++; my_col = 1; }
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 239 "ael.flex"
+{ my_col += yyleng; }
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 240 "ael.flex"
+{ my_col += (yyleng*8)-(my_col%8); }
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 242 "ael.flex"
+{
+ STORE_POS;
+ yylval->str = strdup(yytext);
+ prev_word = yylval->str;
+ return word;
+ }
+ YY_BREAK
+/*
+ * context used for arguments of if_head, random_head, switch_head,
+ * for (last statement), while (XXX why not iftime_head ?).
+ * End with the matching parentheses.
+ * A comma at the top level is valid here, unlike in argg where it
+ * is an argument separator so it must be returned as a token.
+ */
+case 52:
+/* rule 52 can match eol */
+YY_RULE_SETUP
+#line 258 "ael.flex"
+{
+ if ( pbcpop(')') ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ prev_word = 0;
+ return word;
+ }
+ parencount--;
+ if ( parencount >= 0) {
+ yymore();
+ } else {
+ STORE_LOC;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */
+ unput(')');
+ BEGIN(0);
+ return word;
+ }
+ }
+ YY_BREAK
+case 53:
+/* rule 53 can match eol */
+YY_RULE_SETUP
+#line 280 "ael.flex"
+{
+ char c = yytext[yyleng-1];
+ if (c == '(')
+ parencount++;
+ pbcpush(c);
+ yymore();
+ }
+ YY_BREAK
+case 54:
+/* rule 54 can match eol */
+YY_RULE_SETUP
+#line 288 "ael.flex"
+{
+ char c = yytext[yyleng-1];
+ if ( pbcpop(c)) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n",
+ my_file, my_lineno, my_col, c);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+ yymore();
+ }
+ YY_BREAK
+/*
+ * handlers for arguments to a macro or application calls.
+ * We enter this context when we find the initial '(' and
+ * stay here until we close all matching parentheses,
+ * and find the comma (argument separator) or the closing ')'
+ * of the (external) call, which happens when parencount == 0
+ * before the decrement.
+ */
+case 55:
+/* rule 55 can match eol */
+YY_RULE_SETUP
+#line 310 "ael.flex"
+{
+ char c = yytext[yyleng-1];
+ if (c == '(')
+ parencount++;
+ pbcpush(c);
+ yymore();
+ }
+ YY_BREAK
+case 56:
+/* rule 56 can match eol */
+YY_RULE_SETUP
+#line 318 "ael.flex"
+{
+ if ( pbcpop(')') ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression!\n", my_file, my_lineno, my_col);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+
+ parencount--;
+ if( parencount >= 0){
+ yymore();
+ } else {
+ STORE_LOC;
+ BEGIN(0);
+ if ( !strcmp(yytext, ")") )
+ return RP;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */
+ unput(')');
+ return word;
+ }
+ }
+ YY_BREAK
+case 57:
+/* rule 57 can match eol */
+YY_RULE_SETUP
+#line 342 "ael.flex"
+{
+ if( parencount != 0) { /* printf("Folding in a comma!\n"); */
+ yymore();
+ } else {
+ STORE_LOC;
+ if( !strcmp(yytext,"," ) )
+ return COMMA;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0';
+ unput(',');
+ return word;
+ }
+ }
+ YY_BREAK
+case 58:
+/* rule 58 can match eol */
+YY_RULE_SETUP
+#line 356 "ael.flex"
+{
+ char c = yytext[yyleng-1];
+ if ( pbcpop(c) ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+ yymore();
+ }
+ YY_BREAK
+/*
+ * context used to find tokens in the right hand side of assignments,
+ * or in the first and second operand of a 'for'. As above, match
+ * commas and use ';' as a separator (hence return it as a separate token).
+ */
+case 59:
+/* rule 59 can match eol */
+YY_RULE_SETUP
+#line 373 "ael.flex"
+{
+ char c = yytext[yyleng-1];
+ yymore();
+ pbcpush(c);
+ }
+ YY_BREAK
+case 60:
+/* rule 60 can match eol */
+YY_RULE_SETUP
+#line 379 "ael.flex"
+{
+ char c = yytext[yyleng-1];
+ if ( pbcpop(c) ) { /* error */
+ STORE_LOC;
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
+ BEGIN(0);
+ yylval->str = strdup(yytext);
+ return word;
+ }
+ yymore();
+ }
+ YY_BREAK
+case 61:
+/* rule 61 can match eol */
+YY_RULE_SETUP
+#line 391 "ael.flex"
+{
+ STORE_LOC;
+ yylval->str = strdup(yytext);
+ yylval->str[yyleng-1] = '\0';
+ unput(';');
+ BEGIN(0);
+ return word;
+ }
+ YY_BREAK
+case 62:
+/* rule 62 can match eol */
+YY_RULE_SETUP
+#line 400 "ael.flex"
+{
+ char fnamebuf[1024],*p1,*p2;
+ int glob_ret;
+ glob_t globbuf; /* the current globbuf */
+ int globbuf_pos = -1; /* where we are in the current globbuf */
+ globbuf.gl_offs = 0; /* initialize it to silence gcc */
+
+ p1 = strchr(yytext,'"');
+ p2 = strrchr(yytext,'"');
+ if ( include_stack_index >= MAX_INCLUDE_DEPTH ) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Includes nested too deeply! Wow!!! How did you do that?\n", my_file, my_lineno, my_col);
+ } else if ( (int)(p2-p1) > sizeof(fnamebuf) - 1 ) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Filename is incredibly way too long (%d chars!). Inclusion ignored!\n", my_file, my_lineno, my_col, yyleng - 10);
+ } else {
+ strncpy(fnamebuf, p1+1, p2-p1-1);
+ fnamebuf[p2-p1-1] = 0;
+ if (fnamebuf[0] != '/') {
+ char fnamebuf2[1024];
+ snprintf(fnamebuf2,sizeof(fnamebuf2), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, fnamebuf);
+ ast_copy_string(fnamebuf,fnamebuf2,sizeof(fnamebuf));
+ }
+#ifdef SOLARIS
+ glob_ret = glob(fnamebuf, GLOB_NOCHECK, NULL, &globbuf);
+#else
+ glob_ret = glob(fnamebuf, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
+#endif
+ if (glob_ret == GLOB_NOSPACE) {
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Not enough memory\n", fnamebuf);
+ } else if (glob_ret == GLOB_ABORTED) {
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Read error\n", fnamebuf);
+ } else if (glob_ret == GLOB_NOMATCH) {
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: No matches!\n", fnamebuf);
+ } else {
+ globbuf_pos = 0;
+ }
+ }
+ if (globbuf_pos > -1) {
+ setup_filestack(fnamebuf, sizeof(fnamebuf), &globbuf, 0, yyscanner, 1);
+ }
+ }
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(paren):
+case YY_STATE_EOF(semic):
+case YY_STATE_EOF(argg):
+case YY_STATE_EOF(comment):
+#line 445 "ael.flex"
+{
+ char fnamebuf[2048];
+ if (include_stack_index > 0 && include_stack[include_stack_index-1].globbuf_pos < include_stack[include_stack_index-1].globbuf.gl_pathc-1) {
+ free(my_file);
+ my_file = 0;
+ ael_yy_delete_buffer(YY_CURRENT_BUFFER,yyscanner );
+ include_stack[include_stack_index-1].globbuf_pos++;
+ setup_filestack(fnamebuf, sizeof(fnamebuf), &include_stack[include_stack_index-1].globbuf, include_stack[include_stack_index-1].globbuf_pos, yyscanner, 0);
+ /* finish this */
+
+ } else {
+ if (include_stack[include_stack_index].fname) {
+ free(include_stack[include_stack_index].fname);
+ include_stack[include_stack_index].fname = 0;
+ }
+ if ( --include_stack_index < 0 ) {
+ yyterminate();
+ } else {
+ if (my_file) {
+ free(my_file);
+ my_file = 0;
+ }
+ globfree(&include_stack[include_stack_index].globbuf);
+ include_stack[include_stack_index].globbuf_pos = -1;
+
+ ael_yy_delete_buffer(YY_CURRENT_BUFFER,yyscanner );
+ ael_yy_switch_to_buffer(include_stack[include_stack_index].bufstate,yyscanner );
+ my_lineno = include_stack[include_stack_index].lineno;
+ my_col = include_stack[include_stack_index].colno;
+ my_file = strdup(include_stack[include_stack_index].fname);
+ }
+ }
+ }
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 479 "ael.flex"
+ECHO;
+ YY_BREAK
+#line 1784 "ael_lex.c"
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * ael_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( ael_yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of ael_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ ael_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ ael_yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 244 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ register char *yy_cp = yyg->yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 244 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 243);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ ael_yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( ael_yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void ael_yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ ael_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ael_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ael_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ ael_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void ael_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * ael_yypop_buffer_state();
+ * ael_yypush_buffer_state(new_buffer);
+ */
+ ael_yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ ael_yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (ael_yywrap()) processing, but the only time this flag
+ * is looked at is after ael_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void ael_yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE ael_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) ael_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ael_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) ael_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ael_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ ael_yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with ael_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void ael_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ ael_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ ael_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a ael_yyrestart() or at EOF.
+ */
+ static void ael_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ ael_yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then ael_yy_init_buffer was _probably_
+ * called from ael_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void ael_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ ael_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void ael_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ ael_yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from ael_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from ael_yy_switch_to_buffer. */
+ ael_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void ael_yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ ael_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ ael_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void ael_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ael_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ael_yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ael_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) ael_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ael_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ ael_yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to ael_yylex() will
+ * scan from a @e copy of @a str.
+ * @param str a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * ael_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE ael_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return ael_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to ael_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ael_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) ael_yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ael_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = ael_yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in ael_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE ael_yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int ael_yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int ael_yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ael_yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ael_yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int ael_yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *ael_yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void ael_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void ael_yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "ael_yyset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void ael_yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "ael_yyset_column called with no buffer" , yyscanner);
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see ael_yy_switch_to_buffer
+ */
+void ael_yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void ael_yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int ael_yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void ael_yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * ael_yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void ael_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+YYLTYPE *ael_yyget_lloc (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylloc;
+}
+
+void ael_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylloc = yylloc_param;
+}
+
+/* User-visible API */
+
+/* ael_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int ael_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ael_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from ael_yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * ael_yylex_init()
+ */
+ return 0;
+}
+
+/* ael_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int ael_yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ ael_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ ael_yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ ael_yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ ael_yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * ael_yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ ael_yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *ael_yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *ael_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 479 "ael.flex"
+
+
+
+static void pbcpush(char x)
+{
+ pbcstack[pbcpos++] = x;
+}
+
+void ael_yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ free( (char*) ptr );
+}
+
+static int pbcpop(char x)
+{
+ if ( ( x == ')' && pbcstack[pbcpos-1] == '(' )
+ || ( x == ']' && pbcstack[pbcpos-1] == '[' )
+ || ( x == '}' && pbcstack[pbcpos-1] == '{' )) {
+ pbcpos--;
+ return 0;
+ }
+ return 1; /* error */
+}
+
+static int c_prevword(void)
+{
+ char *c = prev_word;
+ if (c == NULL)
+ return 0;
+ while ( *c ) {
+ switch (*c) {
+ case '{':
+ case '[':
+ case '(':
+ pbcpush(*c);
+ break;
+ case '}':
+ case ']':
+ case ')':
+ if (pbcpop(*c))
+ return 1;
+ break;
+ }
+ c++;
+ }
+ return 0;
+}
+
+
+/*
+ * The following three functions, reset_*, are used in the bison
+ * code to switch context. As a consequence, we need to
+ * declare them global and add a prototype so that the
+ * compiler does not complain.
+ *
+ * NOTE: yyg is declared because it is used in the BEGIN macros,
+ * though that should be hidden as the macro changes
+ * depending on the flex options that we use - in particular,
+ * %reentrant changes the way the macro is declared;
+ * without %reentrant, BEGIN uses yystart instead of yyg
+ */
+
+void reset_parencount(yyscan_t yyscanner );
+void reset_parencount(yyscan_t yyscanner )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ parencount = 0;
+ pbcpos = 0;
+ pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */
+ c_prevword();
+ BEGIN(paren);
+}
+
+void reset_semicount(yyscan_t yyscanner );
+void reset_semicount(yyscan_t yyscanner )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ pbcpos = 0;
+ BEGIN(semic);
+}
+
+void reset_argcount(yyscan_t yyscanner );
+void reset_argcount(yyscan_t yyscanner )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ parencount = 0;
+ pbcpos = 0;
+ pbcpush('('); /* push '(' so the last pcbpop (parencount= -1) will succeed */
+ c_prevword();
+ BEGIN(argg);
+}
+
+/* used elsewhere, but some local vars */
+struct pval *ael2_parse(char *filename, int *errors)
+{
+ struct pval *pval;
+ struct parse_io *io;
+ char *buffer;
+ struct stat stats;
+ FILE *fin;
+
+ /* extern int ael_yydebug; */
+
+ io = calloc(sizeof(struct parse_io),1);
+ /* reset the global counters */
+ prev_word = 0;
+ my_lineno = 1;
+ include_stack_index=0;
+ my_col = 0;
+ /* ael_yydebug = 1; */
+ ael_yylex_init(&io->scanner);
+ fin = fopen(filename,"r");
+ if ( !fin ) {
+ ast_log(LOG_ERROR,"File %s could not be opened\n", filename);
+ *errors = 1;
+ return 0;
+ }
+ if (my_file)
+ free(my_file);
+ my_file = strdup(filename);
+ stat(filename, &stats);
+ buffer = (char*)malloc(stats.st_size+2);
+ fread(buffer, 1, stats.st_size, fin);
+ buffer[stats.st_size]=0;
+ fclose(fin);
+
+ ael_yy_scan_string (buffer ,io->scanner);
+ ael_yyset_lineno(1 , io->scanner);
+
+ /* ael_yyset_in (fin , io->scanner); OLD WAY */
+
+ ael_yyparse(io);
+
+
+ pval = io->pval;
+ *errors = io->syntax_error_count;
+
+ ael_yylex_destroy(io->scanner);
+ free(buffer);
+ free(io);
+
+ return pval;
+}
+
+static void setup_filestack(char *fnamebuf2, int fnamebuf_siz, glob_t *globbuf, int globpos, yyscan_t yyscanner, int create)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ int error, i;
+ FILE *in1;
+ char fnamebuf[2048];
+
+ if (globbuf && globbuf->gl_pathv && globbuf->gl_pathc > 0)
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz);
+#else
+ ast_copy_string(fnamebuf, globbuf->gl_pathv[globpos], fnamebuf_siz);
+#endif
+ else {
+ ast_log(LOG_ERROR,"Include file name not present!\n");
+ return;
+ }
+ for (i=0; i<include_stack_index; i++) {
+ if ( !strcmp(fnamebuf,include_stack[i].fname )) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Nice Try!!! But %s has already been included (perhaps by another file), and would cause an infinite loop of file inclusions!!! Include directive ignored\n",
+ my_file, my_lineno, my_col, fnamebuf);
+ break;
+ }
+ }
+ error = 1;
+ if (i == include_stack_index)
+ error = 0; /* we can use this file */
+ if ( !error ) { /* valid file name */
+ /* relative vs. absolute */
+ if (fnamebuf[0] != '/')
+ snprintf(fnamebuf2, fnamebuf_siz, "%s/%s", ast_config_AST_CONFIG_DIR, fnamebuf);
+ else
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(fnamebuf2, fnamebuf, fnamebuf_siz);
+#else
+ ast_copy_string(fnamebuf2, fnamebuf, fnamebuf_siz);
+#endif
+ in1 = fopen( fnamebuf2, "r" );
+
+ if ( ! in1 ) {
+ ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Couldn't find the include file: %s; ignoring the Include directive!\n", my_file, my_lineno, my_col, fnamebuf2);
+ } else {
+ char *buffer;
+ struct stat stats;
+ stat(fnamebuf2, &stats);
+ buffer = (char*)malloc(stats.st_size+1);
+ fread(buffer, 1, stats.st_size, in1);
+ buffer[stats.st_size] = 0;
+ ast_log(LOG_NOTICE," --Read in included file %s, %d chars\n",fnamebuf2, (int)stats.st_size);
+ fclose(in1);
+ if (my_file)
+ free(my_file);
+ my_file = strdup(fnamebuf2);
+ include_stack[include_stack_index].fname = strdup(my_file);
+ include_stack[include_stack_index].lineno = my_lineno;
+ include_stack[include_stack_index].colno = my_col+yyleng;
+ if (create)
+ include_stack[include_stack_index].globbuf = *globbuf;
+
+ include_stack[include_stack_index].globbuf_pos = 0;
+
+ include_stack[include_stack_index].bufstate = YY_CURRENT_BUFFER;
+ if (create)
+ include_stack_index++;
+ ael_yy_switch_to_buffer(ael_yy_scan_string (buffer ,yyscanner),yyscanner);
+ free(buffer);
+ my_lineno = 1;
+ my_col = 1;
+ BEGIN(INITIAL);
+ }
+ }
+}
+
diff --git a/trunk/res/ael/pval.c b/trunk/res/ael/pval.c
new file mode 100644
index 000000000..56794041d
--- /dev/null
+++ b/trunk/res/ael/pval.c
@@ -0,0 +1,5435 @@
+
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.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 Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <regex.h>
+#include <sys/stat.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h"
+#include "asterisk/callerid.h"
+#include "asterisk/pval.h"
+#include "asterisk/ael_structs.h"
+#ifdef AAL_ARGCHECK
+#include "asterisk/argdesc.h"
+#endif
+#include "asterisk/utils.h"
+
+extern int localized_pbx_load_module(void);
+
+static char expr_output[2096];
+#define AST_PBX_MAX_STACK 128
+
+/* these functions are in ../ast_expr2.fl */
+
+static int errs, warns;
+static int notes;
+#ifdef STANDALONE
+static int extensions_dot_conf_loaded = 0;
+#endif
+static char *registrar = "pbx_ael";
+
+static pval *current_db;
+static pval *current_context;
+static pval *current_extension;
+
+static const char *match_context;
+static const char *match_exten;
+static const char *match_label;
+static int in_abstract_context;
+static int count_labels; /* true, put matcher in label counting mode */
+static int label_count; /* labels are only meant to be counted in a context or exten */
+static int return_on_context_match;
+static pval *last_matched_label;
+struct pval *match_pval(pval *item);
+static void check_timerange(pval *p);
+static void check_dow(pval *DOW);
+static void check_day(pval *DAY);
+static void check_month(pval *MON);
+static void check_expr2_input(pval *expr, char *str);
+static int extension_matches(pval *here, const char *exten, const char *pattern);
+static void check_goto(pval *item);
+static void find_pval_goto_item(pval *item, int lev);
+static void find_pval_gotos(pval *item, int lev);
+static int check_break(pval *item);
+static int check_continue(pval *item);
+static void check_label(pval *item);
+static void check_macro_returns(pval *macro);
+
+static struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont);
+static struct pval *find_first_label_in_current_context(char *label, pval *curr_cont);
+static void print_pval_list(FILE *fin, pval *item, int depth);
+
+static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext);
+static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label);
+static pval *get_goto_target(pval *item);
+static int label_inside_case(pval *label);
+static void attach_exten(struct ael_extension **list, struct ael_extension *newmem);
+static void fix_gotos_in_extensions(struct ael_extension *exten);
+static pval *get_extension_or_contxt(pval *p);
+static pval *get_contxt(pval *p);
+static void remove_spaces_before_equals(char *str);
+
+/* PRETTY PRINTER FOR AEL: ============================================================================= */
+
+static void print_pval(FILE *fin, pval *item, int depth)
+{
+ int i;
+ pval *lp;
+
+ for (i=0; i<depth; i++) {
+ fprintf(fin, "\t"); /* depth == indentation */
+ }
+
+ switch ( item->type ) {
+ case PV_WORD:
+ fprintf(fin,"%s;\n", item->u1.str); /* usually, words are encapsulated in something else */
+ break;
+
+ case PV_MACRO:
+ fprintf(fin,"macro %s(", item->u1.str);
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ if (lp != item->u2.arglist )
+ fprintf(fin,", ");
+ fprintf(fin,"%s", lp->u1.str);
+ }
+ fprintf(fin,") {\n");
+ print_pval_list(fin,item->u3.macro_statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n\n");
+ break;
+
+ case PV_CONTEXT:
+ if ( item->u3.abstract )
+ fprintf(fin,"abstract context %s {\n", item->u1.str);
+ else
+ fprintf(fin,"context %s {\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n\n");
+ break;
+
+ case PV_MACRO_CALL:
+ fprintf(fin,"&%s(", item->u1.str);
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ if ( lp != item->u2.arglist )
+ fprintf(fin,", ");
+ fprintf(fin,"%s", lp->u1.str);
+ }
+ fprintf(fin,");\n");
+ break;
+
+ case PV_APPLICATION_CALL:
+ fprintf(fin,"%s(", item->u1.str);
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ if ( lp != item->u2.arglist )
+ fprintf(fin,",");
+ fprintf(fin,"%s", lp->u1.str);
+ }
+ fprintf(fin,");\n");
+ break;
+
+ case PV_CASE:
+ fprintf(fin,"case %s:\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements, depth+1);
+ break;
+
+ case PV_PATTERN:
+ fprintf(fin,"pattern %s:\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements, depth+1);
+ break;
+
+ case PV_DEFAULT:
+ fprintf(fin,"default:\n");
+ print_pval_list(fin,item->u2.statements, depth+1);
+ break;
+
+ case PV_CATCH:
+ fprintf(fin,"catch %s {\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements, depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_SWITCHES:
+ fprintf(fin,"switches {\n");
+ print_pval_list(fin,item->u1.list,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_ESWITCHES:
+ fprintf(fin,"eswitches {\n");
+ print_pval_list(fin,item->u1.list,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_INCLUDES:
+ fprintf(fin,"includes {\n");
+ for (lp=item->u1.list; lp; lp=lp->next) {
+ for (i=0; i<depth+1; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"%s", lp->u1.str); /* usually, words are encapsulated in something else */
+ if (lp->u2.arglist)
+ fprintf(fin,"|%s|%s|%s|%s",
+ lp->u2.arglist->u1.str,
+ lp->u2.arglist->next->u1.str,
+ lp->u2.arglist->next->next->u1.str,
+ lp->u2.arglist->next->next->next->u1.str
+ );
+ fprintf(fin,";\n"); /* usually, words are encapsulated in something else */
+ }
+
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"};\n");
+ break;
+
+ case PV_STATEMENTBLOCK:
+ fprintf(fin,"{\n");
+ print_pval_list(fin,item->u1.list, depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"}\n");
+ break;
+
+ case PV_VARDEC:
+ fprintf(fin,"%s=%s;\n", item->u1.str, item->u2.val);
+ break;
+
+ case PV_LOCALVARDEC:
+ fprintf(fin,"local %s=%s;\n", item->u1.str, item->u2.val);
+ break;
+
+ case PV_GOTO:
+ fprintf(fin,"goto %s", item->u1.list->u1.str);
+ if ( item->u1.list->next )
+ fprintf(fin,",%s", item->u1.list->next->u1.str);
+ if ( item->u1.list->next && item->u1.list->next->next )
+ fprintf(fin,",%s", item->u1.list->next->next->u1.str);
+ fprintf(fin,"\n");
+ break;
+
+ case PV_LABEL:
+ fprintf(fin,"%s:\n", item->u1.str);
+ break;
+
+ case PV_FOR:
+ fprintf(fin,"for (%s; %s; %s)\n", item->u1.for_init, item->u2.for_test, item->u3.for_inc);
+ print_pval_list(fin,item->u4.for_statements,depth+1);
+ break;
+
+ case PV_WHILE:
+ fprintf(fin,"while (%s)\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ break;
+
+ case PV_BREAK:
+ fprintf(fin,"break;\n");
+ break;
+
+ case PV_RETURN:
+ fprintf(fin,"return;\n");
+ break;
+
+ case PV_CONTINUE:
+ fprintf(fin,"continue;\n");
+ break;
+
+ case PV_RANDOM:
+ case PV_IFTIME:
+ case PV_IF:
+ if ( item->type == PV_IFTIME ) {
+
+ fprintf(fin,"ifTime ( %s|%s|%s|%s )\n",
+ item->u1.list->u1.str,
+ item->u1.list->next->u1.str,
+ item->u1.list->next->next->u1.str,
+ item->u1.list->next->next->next->u1.str
+ );
+ } else if ( item->type == PV_RANDOM ) {
+ fprintf(fin,"random ( %s )\n", item->u1.str );
+ } else
+ fprintf(fin,"if ( %s )\n", item->u1.str);
+ if ( item->u2.statements && item->u2.statements->next ) {
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"{\n");
+ print_pval_list(fin,item->u2.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ if ( item->u3.else_statements )
+ fprintf(fin,"}\n");
+ else
+ fprintf(fin,"};\n");
+ } else if (item->u2.statements ) {
+ print_pval_list(fin,item->u2.statements,depth+1);
+ } else {
+ if (item->u3.else_statements )
+ fprintf(fin, " {} ");
+ else
+ fprintf(fin, " {}; ");
+ }
+ if ( item->u3.else_statements ) {
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"else\n");
+ print_pval_list(fin,item->u3.else_statements, depth);
+ }
+ break;
+
+ case PV_SWITCH:
+ fprintf(fin,"switch( %s ) {\n", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"}\n");
+ break;
+
+ case PV_EXTENSION:
+ if ( item->u4.regexten )
+ fprintf(fin, "regexten ");
+ if ( item->u3.hints )
+ fprintf(fin,"hints(%s) ", item->u3.hints);
+
+ fprintf(fin,"%s => ", item->u1.str);
+ print_pval_list(fin,item->u2.statements,depth+1);
+ fprintf(fin,"\n");
+ break;
+
+ case PV_IGNOREPAT:
+ fprintf(fin,"ignorepat => %s;\n", item->u1.str);
+ break;
+
+ case PV_GLOBALS:
+ fprintf(fin,"globals {\n");
+ print_pval_list(fin,item->u1.statements,depth+1);
+ for (i=0; i<depth; i++) {
+ fprintf(fin,"\t"); /* depth == indentation */
+ }
+ fprintf(fin,"}\n");
+ break;
+ }
+}
+
+static void print_pval_list(FILE *fin, pval *item, int depth)
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+ print_pval(fin, i, depth);
+ }
+}
+
+void ael2_print(char *fname, pval *tree)
+{
+ FILE *fin = fopen(fname,"w");
+ if ( !fin ) {
+ ast_log(LOG_ERROR, "Couldn't open %s for writing.\n", fname);
+ return;
+ }
+ print_pval_list(fin, tree, 0);
+ fclose(fin);
+}
+
+
+/* EMPTY TEMPLATE FUNCS FOR AEL TRAVERSAL: ============================================================================= */
+
+void traverse_pval_template(pval *item, int depth);
+void traverse_pval_item_template(pval *item, int depth);
+
+
+void traverse_pval_item_template(pval *item, int depth)/* depth comes in handy for a pretty print (indentation),
+ but you may not need it */
+{
+ pval *lp;
+
+ switch ( item->type ) {
+ case PV_WORD:
+ /* fields: item->u1.str == string associated with this (word). */
+ break;
+
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+
+ }
+ traverse_pval_item_template(item->u3.macro_statements,depth+1);
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_MACRO_CALL:
+ /* fields: item->u1.str == name of macro to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ }
+ break;
+
+ case PV_APPLICATION_CALL:
+ /* fields: item->u1.str == name of application to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ }
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_SWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ break;
+
+ case PV_ESWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ item->u2.arglist == pval list of 4 PV_WORD elements for time values
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ traverse_pval_item_template(item->u2.arglist,depth+1);
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ traverse_pval_item_template(item->u1.list,depth+1);
+ break;
+
+ case PV_LOCALVARDEC:
+ case PV_VARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+
+ if ( item->u1.list->next )
+ ;
+ if ( item->u1.list->next && item->u1.list->next->next )
+ ;
+
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ traverse_pval_item_template(item->u4.for_statements,depth+1);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_BREAK:
+ /* fields: none
+ */
+ break;
+
+ case PV_RETURN:
+ /* fields: none
+ */
+ break;
+
+ case PV_CONTINUE:
+ /* fields: none
+ */
+ break;
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == there are 4 linked PV_WORDs here.
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ if ( item->u3.else_statements ) {
+ traverse_pval_item_template(item->u3.else_statements,depth+1);
+ }
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ if ( item->u3.else_statements ) {
+ traverse_pval_item_template(item->u3.else_statements,depth+1);
+ }
+ break;
+
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ if ( item->u3.else_statements ) {
+ traverse_pval_item_template(item->u3.else_statements,depth+1);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ traverse_pval_item_template(item->u2.statements,depth+1);
+ break;
+
+ case PV_IGNOREPAT:
+ /* fields: item->u1.str == the ignorepat data
+ */
+ break;
+
+ case PV_GLOBALS:
+ /* fields: item->u1.statements == pval list of statements, usually vardecs
+ */
+ traverse_pval_item_template(item->u1.statements,depth+1);
+ break;
+ }
+}
+
+void traverse_pval_template(pval *item, int depth) /* depth comes in handy for a pretty print (indentation),
+ but you may not need it */
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+ traverse_pval_item_template(i, depth);
+ }
+}
+
+
+/* SEMANTIC CHECKING FOR AEL: ============================================================================= */
+
+/* (not all that is syntactically legal is good! */
+
+
+static void check_macro_returns(pval *macro)
+{
+ pval *i;
+ if (!macro->u3.macro_statements)
+ {
+ pval *z = calloc(1, sizeof(struct pval));
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s is empty! I will insert a return.\n",
+ macro->filename, macro->startline, macro->endline, macro->u1.str);
+
+ z->type = PV_RETURN;
+ z->startline = macro->startline;
+ z->endline = macro->endline;
+ z->startcol = macro->startcol;
+ z->endcol = macro->endcol;
+ z->filename = strdup(macro->filename);
+
+ macro->u3.macro_statements = z;
+ return;
+ }
+ for (i=macro->u3.macro_statements; i; i=i->next) {
+ /* if the last statement in the list is not return, then insert a return there */
+ if (i->next == NULL) {
+ if (i->type != PV_RETURN) {
+ pval *z = calloc(1, sizeof(struct pval));
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n",
+ macro->filename, macro->startline, macro->endline, macro->u1.str);
+
+ z->type = PV_RETURN;
+ z->startline = macro->startline;
+ z->endline = macro->endline;
+ z->startcol = macro->startcol;
+ z->endcol = macro->endcol;
+ z->filename = strdup(macro->filename);
+
+ i->next = z;
+ return;
+ }
+ }
+ }
+ return;
+}
+
+
+
+static int extension_matches(pval *here, const char *exten, const char *pattern)
+{
+ int err1;
+ regex_t preg;
+
+ /* simple case, they match exactly, the pattern and exten name */
+ if (!strcmp(pattern,exten) == 0)
+ return 1;
+
+ if (pattern[0] == '_') {
+ char reg1[2000];
+ const char *p;
+ char *r = reg1;
+
+ if ( strlen(pattern)*5 >= 2000 ) /* safety valve */ {
+ ast_log(LOG_ERROR,"Error: The pattern %s is way too big. Pattern matching cancelled.\n",
+ pattern);
+ return 0;
+ }
+ /* form a regular expression from the pattern, and then match it against exten */
+ *r++ = '^'; /* what if the extension is a pattern ?? */
+ *r++ = '_'; /* what if the extension is a pattern ?? */
+ *r++ = '?';
+ for (p=pattern+1; *p; p++) {
+ switch ( *p ) {
+ case 'X':
+ *r++ = '[';
+ *r++ = '0';
+ *r++ = '-';
+ *r++ = '9';
+ *r++ = 'X';
+ *r++ = ']';
+ break;
+
+ case 'Z':
+ *r++ = '[';
+ *r++ = '1';
+ *r++ = '-';
+ *r++ = '9';
+ *r++ = 'Z';
+ *r++ = ']';
+ break;
+
+ case 'N':
+ *r++ = '[';
+ *r++ = '2';
+ *r++ = '-';
+ *r++ = '9';
+ *r++ = 'N';
+ *r++ = ']';
+ break;
+
+ case '[':
+ while ( *p && *p != ']' ) {
+ *r++ = *p++;
+ }
+ if ( *p != ']') {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The extension pattern '%s' is missing a closing bracket \n",
+ here->filename, here->startline, here->endline, pattern);
+ }
+ break;
+
+ case '.':
+ case '!':
+ *r++ = '.';
+ *r++ = '*';
+ break;
+ case '*':
+ *r++ = '\\';
+ *r++ = '*';
+ break;
+ default:
+ *r++ = *p;
+ break;
+
+ }
+ }
+ *r++ = '$'; /* what if the extension is a pattern ?? */
+ *r++ = *p++; /* put in the closing null */
+ err1 = regcomp(&preg, reg1, REG_NOSUB|REG_EXTENDED);
+ if ( err1 ) {
+ char errmess[500];
+ regerror(err1,&preg,errmess,sizeof(errmess));
+ regfree(&preg);
+ ast_log(LOG_WARNING, "Regcomp of %s failed, error code %d\n",
+ reg1, err1);
+ return 0;
+ }
+ err1 = regexec(&preg, exten, 0, 0, 0);
+ regfree(&preg);
+
+ if ( err1 ) {
+ /* ast_log(LOG_NOTICE,"*****************************[%d]Extension %s did not match %s(%s)\n",
+ err1,exten, pattern, reg1); */
+ return 0; /* no match */
+ } else {
+ /* ast_log(LOG_NOTICE,"*****************************Extension %s matched %s\n",
+ exten, pattern); */
+ return 1;
+ }
+
+
+ } else {
+ if ( strcmp(exten,pattern) == 0 ) {
+ return 1;
+ } else
+ return 0;
+ }
+}
+
+
+static void check_expr2_input(pval *expr, char *str)
+{
+ int spaces = strspn(str,"\t \n");
+ if ( !strncmp(str+spaces,"$[",2) ) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The expression '%s' is redundantly wrapped in '$[ ]'. \n",
+ expr->filename, expr->startline, expr->endline, str);
+ warns++;
+ }
+}
+
+static void check_includes(pval *includes)
+{
+ struct pval *p4;
+ for (p4=includes->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_other_context = find_context(incl_context);
+ if (!that_other_context && strcmp(incl_context, "parkedcalls") != 0) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The included context '%s' cannot be found.\n\
+ (You may ignore this warning if '%s' exists in extensions.conf, or is created by another module. I cannot check for those.)\n",
+ includes->filename, includes->startline, includes->endline, incl_context, incl_context);
+ warns++;
+ }
+ }
+}
+
+
+static void check_timerange(pval *p)
+{
+ char *times;
+ char *e;
+ int s1, s2;
+ int e1, e2;
+
+ times = ast_strdupa(p->u1.str);
+
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ return;
+ }
+ /* Otherwise expect a range */
+ e = strchr(times, '-');
+ if (!e) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) requires a '-' surrounded by two 24-hour times of day!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ return;
+ }
+ *e = '\0';
+ e++;
+ while (*e && !isdigit(*e))
+ e++;
+ if (!*e) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The time range format (%s) is missing the end time!\n",
+ p->filename, p->startline, p->endline, p->u1.str);
+ warns++;
+ }
+ if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) isn't quite right!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ }
+ if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) isn't quite right!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ }
+
+ s1 = s1 * 30 + s2/2;
+ if ((s1 < 0) || (s1 >= 24*30)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start time (%s) is out of range!\n",
+ p->filename, p->startline, p->endline, times);
+ warns++;
+ }
+ e1 = e1 * 30 + e2/2;
+ if ((e1 < 0) || (e1 >= 24*30)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end time (%s) is out of range!\n",
+ p->filename, p->startline, p->endline, e);
+ warns++;
+ }
+ return;
+}
+
+static char *days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+};
+
+/*! \brief get_dow: Get day of week */
+static void check_dow(pval *DOW)
+{
+ char *dow;
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e;
+
+ dow = ast_strdupa(DOW->u1.str);
+
+ /* Check for all days */
+ if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
+ return;
+ /* Get start and ending days */
+ c = strchr(dow, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ } else
+ c = NULL;
+ /* Find the start */
+ s = 0;
+ while ((s < 7) && strcasecmp(dow, days[s])) s++;
+ if (s >= 7) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
+ DOW->filename, DOW->startline, DOW->endline, dow);
+ warns++;
+ }
+ if (c) {
+ e = 0;
+ while ((e < 7) && strcasecmp(c, days[e])) e++;
+ if (e >= 7) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day (%s) must be one of 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'!\n",
+ DOW->filename, DOW->startline, DOW->endline, c);
+ warns++;
+ }
+ } else
+ e = s;
+}
+
+static void check_day(pval *DAY)
+{
+ char *day;
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e;
+
+ day = ast_strdupa(DAY->u1.str);
+
+ /* Check for all days */
+ if (ast_strlen_zero(day) || !strcmp(day, "*")) {
+ return;
+ }
+ /* Get start and ending days */
+ c = strchr(day, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ }
+ /* Find the start */
+ if (sscanf(day, "%d", &s) != 1) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number!\n",
+ DAY->filename, DAY->startline, DAY->endline, day);
+ warns++;
+ }
+ else if ((s < 1) || (s > 31)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start day of month (%s) must be a number in the range [1-31]!\n",
+ DAY->filename, DAY->startline, DAY->endline, day);
+ warns++;
+ }
+ s--;
+ if (c) {
+ if (sscanf(c, "%d", &e) != 1) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number!\n",
+ DAY->filename, DAY->startline, DAY->endline, c);
+ warns++;
+ }
+ else if ((e < 1) || (e > 31)) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end day of month (%s) must be a number in the range [1-31]!\n",
+ DAY->filename, DAY->startline, DAY->endline, day);
+ warns++;
+ }
+ e--;
+ } else
+ e = s;
+}
+
+static char *months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+};
+
+static void check_month(pval *MON)
+{
+ char *mon;
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e;
+
+ mon = ast_strdupa(MON->u1.str);
+
+ /* Check for all days */
+ if (ast_strlen_zero(mon) || !strcmp(mon, "*"))
+ return ;
+ /* Get start and ending days */
+ c = strchr(mon, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ }
+ /* Find the start */
+ s = 0;
+ while ((s < 12) && strcasecmp(mon, months[s])) s++;
+ if (s >= 12) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The start month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
+ MON->filename, MON->startline, MON->endline, mon);
+ warns++;
+ }
+ if (c) {
+ e = 0;
+ while ((e < 12) && strcasecmp(mon, months[e])) e++;
+ if (e >= 12) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The end month (%s) must be a one of: 'jan', 'feb', ..., 'dec'!\n",
+ MON->filename, MON->startline, MON->endline, c);
+ warns++;
+ }
+ } else
+ e = s;
+}
+
+static int check_break(pval *item)
+{
+ pval *p = item;
+
+ while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
+ /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
+ no sense */
+ if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN
+ || p->type == PV_WHILE || p->type == PV_FOR ) {
+ return 1;
+ }
+ p = p->dad;
+ }
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'break' not in switch, for, or while statement!\n",
+ item->filename, item->startline, item->endline);
+ errs++;
+
+ return 0;
+}
+
+static int check_continue(pval *item)
+{
+ pval *p = item;
+
+ while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
+ /* a break is allowed in WHILE, FOR, CASE, DEFAULT, PATTERN; otherwise, it don't make
+ no sense */
+ if( p->type == PV_WHILE || p->type == PV_FOR ) {
+ return 1;
+ }
+ p = p->dad;
+ }
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: 'continue' not in 'for' or 'while' statement!\n",
+ item->filename, item->startline, item->endline);
+ errs++;
+
+ return 0;
+}
+
+static struct pval *in_macro(pval *item)
+{
+ struct pval *curr;
+ curr = item;
+ while( curr ) {
+ if( curr->type == PV_MACRO ) {
+ return curr;
+ }
+ curr = curr->dad;
+ }
+ return 0;
+}
+
+static struct pval *in_context(pval *item)
+{
+ struct pval *curr;
+ curr = item;
+ while( curr ) {
+ if( curr->type == PV_MACRO || curr->type == PV_CONTEXT ) {
+ return curr;
+ }
+ curr = curr->dad;
+ }
+ return 0;
+}
+
+
+/* general purpose goto finder */
+
+static void check_label(pval *item)
+{
+ struct pval *curr;
+ struct pval *x;
+ int alright = 0;
+
+ /* A label outside an extension just plain does not make sense! */
+
+ curr = item;
+
+ while( curr ) {
+ if( curr->type == PV_MACRO || curr->type == PV_EXTENSION ) {
+ alright = 1;
+ break;
+ }
+ curr = curr->dad;
+ }
+ if( !alright )
+ {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Label %s is not within an extension or macro!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ errs++;
+ }
+
+
+ /* basically, ensure that a label is not repeated in a context. Period.
+ The method: well, for each label, find the first label in the context
+ with the same name. If it's not the current label, then throw an error. */
+
+
+ /* printf("==== check_label: ====\n"); */
+ if( !current_extension )
+ curr = current_context;
+ else
+ curr = current_extension;
+
+ x = find_first_label_in_current_context((char *)item->u1.str, curr);
+ /* printf("Hey, check_label found with item = %x, and x is %x, and currcont is %x, label name is %s\n", item,x, current_context, (char *)item->u1.str); */
+ if( x && x != item )
+ {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: Duplicate label %s! Previously defined at file %s, line %d.\n",
+ item->filename, item->startline, item->endline, item->u1.str, x->filename, x->startline);
+ errs++;
+ }
+ /* printf("<<<<< check_label: ====\n"); */
+}
+
+static pval *get_goto_target(pval *item)
+{
+ /* just one item-- the label should be in the current extension */
+ pval *curr_ext = get_extension_or_contxt(item); /* containing exten, or macro */
+ pval *curr_cont;
+
+ if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
+ struct pval *x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), curr_ext);
+ return x;
+ }
+
+ curr_cont = get_contxt(item);
+
+ /* TWO items */
+ if (item->u1.list->next && !item->u1.list->next->next) {
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
+ struct pval *x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, curr_cont);
+ return x;
+ }
+ }
+
+ /* All 3 items! */
+ if (item->u1.list->next && item->u1.list->next->next) {
+ /* all three */
+ pval *first = item->u1.list;
+ pval *second = item->u1.list->next;
+ pval *third = item->u1.list->next->next;
+
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${")
+ && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
+ struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
+ if (!x) {
+
+ struct pval *p3;
+ struct pval *that_context = find_context(item->u1.list->u1.str);
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ if (that_context) {
+ for (p3=that_context->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_other_context = find_context(incl_context);
+ if (that_other_context) {
+ struct pval *x3;
+ x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
+ if (x3) {
+ return x3;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return x;
+ }
+ }
+ return 0;
+}
+
+static void check_goto(pval *item)
+{
+ /* check for the target of the goto-- does it exist? */
+ if ( !(item->u1.list)->next && !(item->u1.list)->u1.str ) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: empty label reference found!\n",
+ item->filename, item->startline, item->endline);
+ errs++;
+ }
+
+ /* just one item-- the label should be in the current extension */
+
+ if (item->u1.list && !item->u1.list->next && !strstr((item->u1.list)->u1.str,"${")) {
+ struct pval *z = get_extension_or_contxt(item);
+ struct pval *x = 0;
+ if (z)
+ x = find_label_in_current_extension((char*)((item->u1.list)->u1.str), z); /* if in macro, use current context instead */
+ /* printf("Called find_label_in_current_extension with arg %s; current_extension is %x: %d\n",
+ (char*)((item->u1.list)->u1.str), current_extension?current_extension:current_context, current_extension?current_extension->type:current_context->type); */
+ if (!x) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s exists in the current extension!\n",
+ item->filename, item->startline, item->endline, item->u1.list->u1.str);
+ errs++;
+ }
+ else
+ return;
+ }
+
+ /* TWO items */
+ if (item->u1.list->next && !item->u1.list->next->next) {
+ /* two items */
+ /* printf("Calling find_label_in_current_context with args %s, %s\n",
+ (char*)((item->u1.list)->u1.str), (char *)item->u1.list->next->u1.str); */
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${") ) /* Don't try to match variables */ {
+ struct pval *z = get_contxt(item);
+ struct pval *x = 0;
+
+ if (z)
+ x = find_label_in_current_context((char *)item->u1.list->u1.str, (char *)item->u1.list->next->u1.str, z);
+
+ if (!x) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label '%s,%s' exists in the current context, or any of its inclusions!\n",
+ item->filename, item->startline, item->endline, item->u1.list->u1.str, item->u1.list->next->u1.str );
+ errs++;
+ }
+ else
+ return;
+ }
+ }
+
+ /* All 3 items! */
+ if (item->u1.list->next && item->u1.list->next->next) {
+ /* all three */
+ pval *first = item->u1.list;
+ pval *second = item->u1.list->next;
+ pval *third = item->u1.list->next->next;
+
+ /* printf("Calling find_label_in_current_db with args %s, %s, %s\n",
+ (char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str); */
+ if (!strstr((item->u1.list)->u1.str,"${")
+ && !strstr(item->u1.list->next->u1.str,"${")
+ && !strstr(item->u1.list->next->next->u1.str,"${")) /* Don't try to match variables */ {
+ struct pval *x = find_label_in_current_db((char*)first->u1.str, (char*)second->u1.str, (char*)third->u1.str);
+ if (!x) {
+ struct pval *p3;
+ struct pval *found = 0;
+ struct pval *that_context = find_context(item->u1.list->u1.str);
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ if (that_context) {
+ for (p3=that_context->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_other_context = find_context(incl_context);
+ if (that_other_context) {
+ struct pval *x3;
+ x3 = find_label_in_current_context((char *)item->u1.list->next->u1.str, (char *)item->u1.list->next->next->u1.str, that_other_context);
+ if (x3) {
+ found = x3;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!found) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: goto: no label %s|%s exists in the context %s or its inclusions!\n",
+ item->filename, item->startline, item->endline, item->u1.list->next->u1.str, item->u1.list->next->next->u1.str, item->u1.list->u1.str );
+ errs++;
+ } else {
+ struct pval *mac = in_macro(item); /* is this goto inside a macro? */
+ if( mac ) { /* yes! */
+ struct pval *targ = in_context(found);
+ if( mac != targ )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
+ item->filename, item->startline, item->endline);
+ warns++;
+ }
+ }
+ }
+ } else {
+ /* here is where code would go to check for target existence in extensions.conf files */
+#ifdef STANDALONE
+ struct pbx_find_info pfiq = {.stacklen = 0 };
+ extern int localized_pbx_load_module(void);
+ /* if this is a standalone, we will need to make sure the
+ localized load of extensions.conf is done */
+ if (!extensions_dot_conf_loaded) {
+ localized_pbx_load_module();
+ extensions_dot_conf_loaded++;
+ }
+
+ pbx_find_extension(NULL, NULL, &pfiq, first->u1.str, second->u1.str, atoi(third->u1.str),
+ atoi(third->u1.str) ? NULL : third->u1.str, NULL,
+ atoi(third->u1.str) ? E_MATCH : E_FINDLABEL);
+
+ if (pfiq.status != STATUS_SUCCESS) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s, not even in extensions.conf!\n",
+ item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str);
+ warns++;
+ }
+#else
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: goto: Couldn't find goto target %s|%s|%s in the AEL code!\n",
+ item->filename, item->startline, item->endline, first->u1.str, second->u1.str, third->u1.str);
+ warns++;
+#endif
+ }
+ } else {
+ struct pval *mac = in_macro(item); /* is this goto inside a macro? */
+ if( mac ) { /* yes! */
+ struct pval *targ = in_context(x);
+ if( mac != targ )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: It's bad form to have a goto in a macro to a target outside the macro!\n",
+ item->filename, item->startline, item->endline);
+ warns++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void find_pval_goto_item(pval *item, int lev)
+{
+ struct pval *p4;
+ if (lev>100) {
+ ast_log(LOG_ERROR,"find_pval_goto in infinite loop!\n\n");
+ return;
+ }
+
+ switch ( item->type ) {
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+
+ /* printf("Descending into matching macro %s\n", match_context); */
+ find_pval_gotos(item->u3.macro_statements,lev+1); /* if we're just searching for a context, don't bother descending into them */
+
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ find_pval_gotos(item->u1.list,lev+1);
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+ check_goto(item); /* THE WHOLE FUNCTION OF THIS ENTIRE ROUTINE!!!! */
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ for (p4=item->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_context = find_context(incl_context);
+ if (that_context) {
+ find_pval_gotos(that_context,lev+1); /* keep working up the includes */
+ }
+ }
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ find_pval_gotos(item->u4.for_statements,lev+1);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF */
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF*/
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ find_pval_gotos(item->u2.statements,lev+1);
+
+ if (item->u3.else_statements) {
+ find_pval_gotos(item->u3.else_statements,lev+1);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ find_pval_gotos(item->u3.else_statements,lev+1);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+
+ find_pval_gotos(item->u2.statements,lev+1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void find_pval_gotos(pval *item,int lev)
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+
+ find_pval_goto_item(i, lev);
+ }
+}
+
+
+
+/* general purpose label finder */
+static struct pval *match_pval_item(pval *item)
+{
+ pval *x;
+
+ switch ( item->type ) {
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ /* printf(" matching in MACRO %s, match_context=%s; retoncontmtch=%d; \n", item->u1.str, match_context, return_on_context_match); */
+ if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
+
+ /* printf("MACRO: match context is: %s\n", match_context); */
+
+ if (return_on_context_match && !strcmp(item->u1.str, match_context)) /* if we're just searching for a context, don't bother descending into them */ {
+ /* printf("Returning on matching macro %s\n", match_context); */
+ return item;
+ }
+
+
+ if (!return_on_context_match) {
+ /* printf("Descending into matching macro %s/%s\n", match_context, item->u1.str); */
+ if ((x=match_pval(item->u3.macro_statements))) {
+ /* printf("Responded with pval match %x\n", x); */
+ return x;
+ }
+ }
+ } else {
+ /* printf("Skipping context/macro %s\n", item->u1.str); */
+ }
+
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ /* printf(" matching in CONTEXT\n"); */
+ if (!strcmp(match_context,"*") || !strcmp(item->u1.str, match_context)) {
+ if (return_on_context_match && !strcmp(item->u1.str, match_context)) {
+ /* printf("Returning on matching context %s\n", match_context); */
+ /* printf("non-CONTEXT: Responded with pval match %x\n", x); */
+ return item;
+ }
+
+ if (!return_on_context_match ) {
+ /* printf("Descending into matching context %s\n", match_context); */
+ if ((x=match_pval(item->u2.statements))) /* if we're just searching for a context, don't bother descending into them */ {
+ /* printf("CONTEXT: Responded with pval match %x\n", x); */
+ return x;
+ }
+ }
+ } else {
+ /* printf("Skipping context/macro %s\n", item->u1.str); */
+ }
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* printf(" matching in CASE\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("CASE: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* printf(" matching in PATTERN\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("PATTERN: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ /* printf(" matching in DEFAULT\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("DEFAULT: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ /* printf(" matching in CATCH\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("CATCH: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ /* printf(" matching in STATEMENTBLOCK\n"); */
+ if ((x=match_pval(item->u1.list))) {
+ /* printf("STATEMENTBLOCK: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ /* printf("PV_LABEL %s (cont=%s, exten=%s\n",
+ item->u1.str, current_context->u1.str, (current_extension?current_extension->u1.str:"<macro>"));*/
+
+ if (count_labels) {
+ if (!strcmp(match_label, item->u1.str)) {
+ label_count++;
+ last_matched_label = item;
+ }
+
+ } else {
+ if (!strcmp(match_label, item->u1.str)) {
+ /* printf("LABEL: Responded with pval match %x\n", x); */
+ return item;
+ }
+ }
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ /* printf(" matching in FOR\n"); */
+ if ((x=match_pval(item->u4.for_statements))) {
+ /* printf("FOR: Responded with pval match %x\n", x);*/
+ return x;
+ }
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ /* printf(" matching in WHILE\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("WHILE: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF */
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the time values, 4 of them, as PV_WORD structs in a list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to PV_IF*/
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ /* printf(" matching in IF/IFTIME/RANDOM\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ return x;
+ }
+ if (item->u3.else_statements) {
+ if ((x=match_pval(item->u3.else_statements))) {
+ /* printf("IF/IFTIME/RANDOM: Responded with pval match %x\n", x); */
+ return x;
+ }
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ /* printf(" matching in SWITCH\n"); */
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("SWITCH: Responded with pval match %x\n", x); */
+ return x;
+ }
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ /* printf(" matching in EXTENSION\n"); */
+ if (!strcmp(match_exten,"*") || extension_matches(item, match_exten, item->u1.str) ) {
+ /* printf("Descending into matching exten %s => %s\n", match_exten, item->u1.str); */
+ if (strcmp(match_label,"1") == 0) {
+ if (item->u2.statements) {
+ struct pval *p5 = item->u2.statements;
+ while (p5 && p5->type == PV_LABEL) /* find the first non-label statement in this context. If it exists, there's a "1" */
+ p5 = p5->next;
+ if (p5)
+ return p5;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ if ((x=match_pval(item->u2.statements))) {
+ /* printf("EXTENSION: Responded with pval match %x\n", x); */
+ return x;
+ }
+ } else {
+ /* printf("Skipping exten %s\n", item->u1.str); */
+ }
+ break;
+ default:
+ /* printf(" matching in default = %d\n", item->type); */
+ break;
+ }
+ return 0;
+}
+
+struct pval *match_pval(pval *item)
+{
+ pval *i;
+
+ for (i=item; i; i=i->next) {
+ pval *x;
+ /* printf(" -- match pval: item %d\n", i->type); */
+
+ if ((x = match_pval_item(i))) {
+ /* printf("match_pval: returning x=%x\n", (int)x); */
+ return x; /* cut the search short */
+ }
+ }
+ return 0;
+}
+
+#if 0
+int count_labels_in_current_context(char *label)
+{
+ label_count = 0;
+ count_labels = 1;
+ return_on_context_match = 0;
+ match_pval(current_context->u2.statements);
+
+ return label_count;
+}
+#endif
+
+struct pval *find_first_label_in_current_context(char *label, pval *curr_cont)
+{
+ /* printf(" --- Got args %s, %s\n", exten, label); */
+ struct pval *ret;
+ struct pval *p3;
+
+ count_labels = 0;
+ return_on_context_match = 0;
+ match_context = "*";
+ match_exten = "*";
+ match_label = label;
+
+ ret = match_pval(curr_cont);
+ if (ret)
+ return ret;
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_context = find_context(incl_context);
+ if (that_context) {
+ struct pval *x3;
+ x3 = find_first_label_in_current_context(label, that_context);
+ if (x3) {
+ return x3;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+struct pval *find_label_in_current_context(char *exten, char *label, pval *curr_cont)
+{
+ /* printf(" --- Got args %s, %s\n", exten, label); */
+ struct pval *ret;
+ struct pval *p3;
+
+ count_labels = 0;
+ return_on_context_match = 0;
+ match_context = "*";
+ match_exten = exten;
+ match_label = label;
+ ret = match_pval(curr_cont->u2.statements);
+ if (ret)
+ return ret;
+
+ /* the target of the goto could be in an included context!! Fancy that!! */
+ /* look for includes in the current context */
+ for (p3=curr_cont->u2.statements; p3; p3=p3->next) {
+ if (p3->type == PV_INCLUDES) {
+ struct pval *p4;
+ for (p4=p3->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ char *incl_context = p4->u1.str;
+ /* find a matching context name */
+ struct pval *that_context = find_context(incl_context);
+ if (that_context) {
+ struct pval *x3;
+ x3 = find_label_in_current_context(exten, label, that_context);
+ if (x3) {
+ return x3;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static struct pval *find_label_in_current_extension(const char *label, pval *curr_ext)
+{
+ /* printf(" --- Got args %s\n", label); */
+ count_labels = 0;
+ return_on_context_match = 0;
+ match_context = "*";
+ match_exten = "*";
+ match_label = label;
+ return match_pval(curr_ext);
+}
+
+static struct pval *find_label_in_current_db(const char *context, const char *exten, const char *label)
+{
+ /* printf(" --- Got args %s, %s, %s\n", context, exten, label); */
+ count_labels = 0;
+ return_on_context_match = 0;
+
+ match_context = context;
+ match_exten = exten;
+ match_label = label;
+
+ return match_pval(current_db);
+}
+
+
+struct pval *find_macro(char *name)
+{
+ return_on_context_match = 1;
+ count_labels = 0;
+ match_context = name;
+ match_exten = "*"; /* don't really need to set these, shouldn't be reached */
+ match_label = "*";
+ return match_pval(current_db);
+}
+
+struct pval *find_context(char *name)
+{
+ return_on_context_match = 1;
+ count_labels = 0;
+ match_context = name;
+ match_exten = "*"; /* don't really need to set these, shouldn't be reached */
+ match_label = "*";
+ return match_pval(current_db);
+}
+
+int is_float(char *arg )
+{
+ char *s;
+ for (s=arg; *s; s++) {
+ if (*s != '.' && (*s < '0' || *s > '9'))
+ return 0;
+ }
+ return 1;
+}
+int is_int(char *arg )
+{
+ char *s;
+ for (s=arg; *s; s++) {
+ if (*s < '0' || *s > '9')
+ return 0;
+ }
+ return 1;
+}
+int is_empty(char *arg)
+{
+ if (!arg)
+ return 1;
+ if (*arg == 0)
+ return 1;
+ while (*arg) {
+ if (*arg != ' ' && *arg != '\t')
+ return 0;
+ arg++;
+ }
+ return 1;
+}
+
+#ifdef AAL_ARGCHECK
+int option_matches_j( struct argdesc *should, pval *is, struct argapp *app)
+{
+ struct argchoice *ac;
+ char *opcop,*q,*p;
+
+ switch (should->dtype) {
+ case ARGD_OPTIONSET:
+ if ( strstr(is->u1.str,"${") )
+ return 0; /* no checking anything if there's a var reference in there! */
+
+ opcop = ast_strdupa(is->u1.str);
+
+ for (q=opcop;*q;q++) { /* erase the innards of X(innard) type arguments, so we don't get confused later */
+ if ( *q == '(' ) {
+ p = q+1;
+ while (*p && *p != ')' )
+ *p++ = '+';
+ q = p+1;
+ }
+ }
+
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
+ return 0;
+ }
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)==1 || strchr(ac->name,'(')) {
+ char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */
+
+ if (p && *p == 'j') {
+ ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The j option in the %s application call is not appropriate for AEL!\n",
+ is->filename, is->startline, is->endline, app->name);
+ errs++;
+ }
+
+ if (p) {
+ *p = '+';
+ if (ac->name[1] == '(') {
+ if (*(p+1) != '(') {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call should have an (argument), but doesn't!\n",
+ is->filename, is->startline, is->endline, ac->name[0], app->name);
+ warns++;
+ }
+ }
+ }
+ }
+ }
+ for (q=opcop; *q; q++) {
+ if ( *q != '+' && *q != '(' && *q != ')') {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The %c option in the %s application call is not available as an option!\n",
+ is->filename, is->startline, is->endline, *q, app->name);
+ warns++;
+ }
+ }
+ return 1;
+ break;
+ default:
+ return 0;
+ }
+
+}
+
+int option_matches( struct argdesc *should, pval *is, struct argapp *app)
+{
+ struct argchoice *ac;
+ char *opcop;
+
+ switch (should->dtype) {
+ case ARGD_STRING:
+ if (is_empty(is->u1.str) && should->type == ARGD_REQUIRED)
+ return 0;
+ if (is->u1.str && strlen(is->u1.str) > 0) /* most will match */
+ return 1;
+ break;
+
+ case ARGD_INT:
+ if (is_int(is->u1.str))
+ return 1;
+ else
+ return 0;
+ break;
+
+ case ARGD_FLOAT:
+ if (is_float(is->u1.str))
+ return 1;
+ else
+ return 0;
+ break;
+
+ case ARGD_ENUM:
+ if( !is->u1.str || strlen(is->u1.str) == 0 )
+ return 1; /* a null arg in the call will match an enum, I guess! */
+ for (ac=should->choices; ac; ac=ac->next) {
+ if (strcmp(ac->name,is->u1.str) == 0)
+ return 1;
+ }
+ return 0;
+ break;
+
+ case ARGD_OPTIONSET:
+ opcop = ast_strdupa(is->u1.str);
+
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)>1 && strchr(ac->name,'(') == 0 && strcmp(ac->name,is->u1.str) == 0) /* multichar option, no parens, and a match? */
+ return 1;
+ }
+ for (ac=app->opts; ac; ac=ac->next) {
+ if (strlen(ac->name)==1 || strchr(ac->name,'(')) {
+ char *p = strchr(opcop,ac->name[0]); /* wipe out all matched options in the user-supplied string */
+
+ if (p) {
+ *p = '+';
+ if (ac->name[1] == '(') {
+ if (*(p+1) == '(') {
+ char *q = p+1;
+ while (*q && *q != ')') {
+ *q++ = '+';
+ }
+ *q = '+';
+ }
+ }
+ }
+ }
+ }
+ return 1;
+ break;
+ case ARGD_VARARG:
+ return 1; /* matches anything */
+ break;
+ }
+ return 1; /* unless some for-sure match or non-match returns, then it must be close enough ... */
+}
+#endif
+
+int check_app_args(pval* appcall, pval *arglist, struct argapp *app)
+{
+#ifdef AAL_ARGCHECK
+ struct argdesc *ad = app->args;
+ pval *pa;
+ int z;
+
+ for (pa = arglist; pa; pa=pa->next) {
+ if (!ad) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
+ arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name);
+ warns++;
+ return 1;
+ } else {
+ /* find the first entry in the ad list that will match */
+ do {
+ if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
+ break;
+
+ z= option_matches( ad, pa, app);
+ if (!z) {
+ if ( !arglist )
+ arglist=appcall;
+
+ if (ad->type == ARGD_REQUIRED) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
+ arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
+ warns++;
+ return 1;
+ }
+ } else if (z && ad->dtype == ARGD_OPTIONSET) {
+ option_matches_j( ad, pa, app);
+ }
+ ad = ad->next;
+ } while (ad && !z);
+ }
+ }
+ /* any app nodes left, that are not optional? */
+ for ( ; ad; ad=ad->next) {
+ if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) {
+ if ( !arglist )
+ arglist=appcall;
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
+ arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
+ warns++;
+ return 1;
+ }
+ }
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+void check_switch_expr(pval *item, struct argapp *apps)
+{
+#ifdef AAL_ARGCHECK
+ /* get and clean the variable name */
+ char *buff1, *p;
+ struct argapp *a,*a2;
+ struct appsetvar *v,*v2;
+ struct argchoice *c;
+ pval *t;
+
+ p = item->u1.str;
+ while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) )
+ p++;
+
+ buff1 = ast_strdupa(p);
+
+ while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t'))
+ buff1[strlen(buff1)-1] = 0;
+ /* buff1 now contains the variable name */
+ v = 0;
+ for (a=apps; a; a=a->next) {
+ for (v=a->setvars;v;v=v->next) {
+ if (strcmp(v->name,buff1) == 0) {
+ break;
+ }
+ }
+ if ( v )
+ break;
+ }
+ if (v && v->vals) {
+ /* we have a match, to a variable that has a set of determined values */
+ int def= 0;
+ int pat = 0;
+ int f1 = 0;
+
+ /* first of all, does this switch have a default case ? */
+ for (t=item->u2.statements; t; t=t->next) {
+ if (t->type == PV_DEFAULT) {
+ def =1;
+ break;
+ }
+ if (t->type == PV_PATTERN) {
+ pat++;
+ }
+ }
+ if (def || pat) /* nothing to check. All cases accounted for! */
+ return;
+ for (c=v->vals; c; c=c->next) {
+ f1 = 0;
+ for (t=item->u2.statements; t; t=t->next) {
+ if (t->type == PV_CASE || t->type == PV_PATTERN) {
+ if (!strcmp(t->u1.str,c->name)) {
+ f1 = 1;
+ break;
+ }
+ }
+ }
+ if (!f1) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
+ item->filename, item->startline, item->endline, item->u1.str, c->name);
+ warns++;
+ }
+ }
+ /* next, is there an app call in the current exten, that would set this var? */
+ f1 = 0;
+ t = current_extension->u2.statements;
+ if ( t && t->type == PV_STATEMENTBLOCK )
+ t = t->u1.statements;
+ for (; t && t != item; t=t->next) {
+ if (t->type == PV_APPLICATION_CALL) {
+ /* find the application that matches the u1.str */
+ for (a2=apps; a2; a2=a2->next) {
+ if (strcasecmp(a2->name, t->u1.str)==0) {
+ for (v2=a2->setvars; v2; v2=v2->next) {
+ if (strcmp(v2->name, buff1) == 0) {
+ /* found an app that sets the var */
+ f1 = 1;
+ break;
+ }
+ }
+ }
+ if (f1)
+ break;
+ }
+ }
+ if (f1)
+ break;
+ }
+
+ /* see if it sets the var */
+ if (!f1) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the expression (%s) value!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ }
+#else
+ pval *t,*tl=0,*p2;
+ int def= 0;
+
+ /* first of all, does this switch have a default case ? */
+ for (t=item->u2.statements; t; t=t->next) {
+ if (t->type == PV_DEFAULT) {
+ def =1;
+ break;
+ }
+ tl = t;
+ }
+ if (def) /* nothing to check. All cases accounted for! */
+ return;
+ /* if no default, warn and insert a default case at the end */
+ p2 = tl->next = calloc(1, sizeof(struct pval));
+
+ p2->type = PV_DEFAULT;
+ p2->startline = tl->startline;
+ p2->endline = tl->endline;
+ p2->startcol = tl->startcol;
+ p2->endcol = tl->endcol;
+ p2->filename = strdup(tl->filename);
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n",
+ p2->filename, p2->startline, p2->endline);
+ warns++;
+
+#endif
+}
+
+static void check_context_names(void)
+{
+ pval *i,*j;
+ for (i=current_db; i; i=i->next) {
+ if (i->type == PV_CONTEXT || i->type == PV_MACRO) {
+ for (j=i->next; j; j=j->next) {
+ if ( j->type == PV_CONTEXT || j->type == PV_MACRO ) {
+ if ( !strcmp(i->u1.str, j->u1.str) && !(i->u3.abstract&2) && !(j->u3.abstract&2) )
+ {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: The context name (%s) is also declared in file %s, line %d-%d! (and neither is marked 'extend')\n",
+ i->filename, i->startline, i->endline, i->u1.str, j->filename, j->startline, j->endline);
+ warns++;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void check_abstract_reference(pval *abstract_context)
+{
+ pval *i,*j;
+ /* find some context includes that reference this context */
+
+
+ /* otherwise, print out a warning */
+ for (i=current_db; i; i=i->next) {
+ if (i->type == PV_CONTEXT) {
+ for (j=i->u2. statements; j; j=j->next) {
+ if ( j->type == PV_INCLUDES ) {
+ struct pval *p4;
+ for (p4=j->u1.list; p4; p4=p4->next) {
+ /* for each context pointed to, find it, then find a context/label that matches the
+ target here! */
+ if ( !strcmp(p4->u1.str, abstract_context->u1.str) )
+ return; /* found a match! */
+ }
+ }
+ }
+ }
+ }
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find a reference to this abstract context (%s) in any other context!\n",
+ abstract_context->filename, abstract_context->startline, abstract_context->endline, abstract_context->u1.str);
+ warns++;
+}
+
+
+void check_pval_item(pval *item, struct argapp *apps, int in_globals)
+{
+ pval *lp;
+#ifdef AAL_ARGCHECK
+ struct argapp *app, *found;
+#endif
+ struct pval *macro_def;
+ struct pval *app_def;
+
+ char errmsg[4096];
+ char *strp;
+
+ switch (item->type) {
+ case PV_WORD:
+ /* fields: item->u1.str == string associated with this (word).
+ item->u2.arglist == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
+ break;
+
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ in_abstract_context = 0;
+ current_context = item;
+ current_extension = 0;
+
+ check_macro_returns(item);
+
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+
+ }
+ check_pval(item->u3.macro_statements, apps,in_globals);
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ current_context = item;
+ current_extension = 0;
+ if ( item->u3.abstract ) {
+ in_abstract_context = 1;
+ check_abstract_reference(item);
+ } else
+ in_abstract_context = 0;
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_MACRO_CALL:
+ /* fields: item->u1.str == name of macro to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+#ifdef STANDALONE
+ /* if this is a standalone, we will need to make sure the
+ localized load of extensions.conf is done */
+ if (!extensions_dot_conf_loaded) {
+ localized_pbx_load_module();
+ extensions_dot_conf_loaded++;
+ }
+#endif
+ macro_def = find_macro(item->u1.str);
+ if (!macro_def) {
+#ifdef STANDALONE
+ struct pbx_find_info pfiq = {.stacklen = 0 };
+ struct pbx_find_info pfiq2 = {.stacklen = 0 };
+
+ /* look for the macro in the extensions.conf world */
+ pbx_find_extension(NULL, NULL, &pfiq, item->u1.str, "s", 1, NULL, NULL, E_MATCH);
+
+ if (pfiq.status != STATUS_SUCCESS) {
+ char namebuf2[256];
+ snprintf(namebuf2, 256, "macro-%s", item->u1.str);
+
+ /* look for the macro in the extensions.conf world */
+ pbx_find_extension(NULL, NULL, &pfiq2, namebuf2, "s", 1, NULL, NULL, E_MATCH);
+
+ if (pfiq2.status == STATUS_SUCCESS) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (macro-%s was found in the extensions.conf stuff, but we are using gosubs!)\n",
+ item->filename, item->startline, item->endline, item->u1.str, item->u1.str);
+ warns++;
+ } else {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (Not even in the extensions.conf stuff!)\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ }
+#else
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s cannot be found in the AEL code!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+
+#endif
+#ifdef THIS_IS_1DOT4
+ char namebuf2[256];
+ snprintf(namebuf2, 256, "macro-%s", item->u1.str);
+
+ /* look for the macro in the extensions.conf world */
+ pbx_find_extension(NULL, NULL, &pfiq, namebuf2, "s", 1, NULL, NULL, E_MATCH);
+
+ if (pfiq.status != STATUS_SUCCESS) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s was not found in the AEL, nor the extensions.conf !\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+
+#endif
+
+ } else if (macro_def->type != PV_MACRO) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ errs++;
+ } else {
+ /* macro_def is a MACRO, so do the args match in number? */
+ int hereargs = 0;
+ int thereargs = 0;
+
+ for (lp=item->u2.arglist; lp; lp=lp->next) {
+ hereargs++;
+ }
+ for (lp=macro_def->u2.arglist; lp; lp=lp->next) {
+ thereargs++;
+ }
+ if (hereargs != thereargs ) {
+ ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n",
+ item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs);
+ errs++;
+ }
+ }
+ break;
+
+ case PV_APPLICATION_CALL:
+ /* fields: item->u1.str == name of application to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ /* Need to check to see if the application is available! */
+ app_def = find_context(item->u1.str);
+ if (app_def && app_def->type == PV_MACRO) {
+ ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ errs++;
+ }
+ if (strcasecmp(item->u1.str,"GotoIf") == 0
+ || strcasecmp(item->u1.str,"GotoIfTime") == 0
+ || strcasecmp(item->u1.str,"while") == 0
+ || strcasecmp(item->u1.str,"endwhile") == 0
+ || strcasecmp(item->u1.str,"random") == 0
+ || strcasecmp(item->u1.str,"gosub") == 0
+ || strcasecmp(item->u1.str,"return") == 0
+ || strcasecmp(item->u1.str,"gosubif") == 0
+ || strcasecmp(item->u1.str,"continuewhile") == 0
+ || strcasecmp(item->u1.str,"endwhile") == 0
+ || strcasecmp(item->u1.str,"execif") == 0
+ || strcasecmp(item->u1.str,"execiftime") == 0
+ || strcasecmp(item->u1.str,"exitwhile") == 0
+ || strcasecmp(item->u1.str,"goto") == 0
+ || strcasecmp(item->u1.str,"macro") == 0
+ || strcasecmp(item->u1.str,"macroexclusive") == 0
+ || strcasecmp(item->u1.str,"macroif") == 0
+ || strcasecmp(item->u1.str,"stackpop") == 0
+ || strcasecmp(item->u1.str,"execIf") == 0 ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ if (strcasecmp(item->u1.str,"macroexit") == 0) {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n",
+ item->filename, item->startline, item->endline);
+ item->type = PV_RETURN;
+ free(item->u1.str);
+ item->u1.str = 0;
+ }
+
+#ifdef AAL_ARGCHECK
+ found = 0;
+ for (app=apps; app; app=app->next) {
+ if (strcasecmp(app->name, item->u1.str) == 0) {
+ found =app;
+ break;
+ }
+ }
+ if (!found) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ } else
+ check_app_args(item, item->u2.arglist, app);
+#endif
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* Make sure sequence of statements under case is terminated with goto, return, or break */
+ /* find the last statement */
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ /* Make sure sequence of statements under case is terminated with goto, return, or break */
+ /* find the last statement */
+
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_SWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ break;
+
+ case PV_ESWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ check_includes(item);
+ for (lp=item->u1.list; lp; lp=lp->next){
+ char *incl_context = lp->u1.str;
+ struct pval *that_context = find_context(incl_context);
+
+ if ( lp->u2.arglist ) {
+ check_timerange(lp->u2.arglist);
+ check_dow(lp->u2.arglist->next);
+ check_day(lp->u2.arglist->next->next);
+ check_month(lp->u2.arglist->next->next->next);
+ }
+
+ if (that_context) {
+ find_pval_gotos(that_context->u2.statements,0);
+
+ }
+ }
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ check_pval(item->u1.list, apps,in_globals);
+ break;
+
+ case PV_VARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
+ if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
+ snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u2.val);
+ warns++;
+ }
+ check_expr2_input(item,item->u2.val);
+ }
+ break;
+
+ case PV_LOCALVARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
+ snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u2.val);
+ warns++;
+ }
+ check_expr2_input(item,item->u2.val);
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+ /* don't check goto's in abstract contexts */
+ if ( in_abstract_context )
+ break;
+
+ check_goto(item);
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+
+ check_label(item);
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.for_test);
+ ast_expr_register_extra_error_info(errmsg);
+
+ strp = strchr(item->u1.for_init, '=');
+ if (strp) {
+ ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
+ }
+ ast_expr(item->u2.for_test, expr_output, sizeof(expr_output),NULL);
+ strp = strchr(item->u3.for_inc, '=');
+ if (strp) {
+ ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
+ }
+ if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u2.for_test);
+ warns++;
+ }
+ if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u3.for_inc);
+ warns++;
+ }
+ check_expr2_input(item,item->u2.for_test);
+ check_expr2_input(item,item->u3.for_inc);
+
+ ast_expr_clear_extra_error_info();
+ check_pval(item->u4.for_statements, apps,in_globals);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ check_expr2_input(item,item->u1.str);
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_BREAK:
+ /* fields: none
+ */
+ check_break(item);
+ break;
+
+ case PV_RETURN:
+ /* fields: none
+ */
+ break;
+
+ case PV_CONTINUE:
+ /* fields: none
+ */
+ check_continue(item);
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random number expression, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ check_expr2_input(item,item->u1.str);
+ check_pval(item->u2.statements, apps,in_globals);
+ if (item->u3.else_statements) {
+ check_pval(item->u3.else_statements, apps,in_globals);
+ }
+ break;
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the if time values, 4 of them, each in PV_WORD, linked list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ if ( item->u2.arglist ) {
+ check_timerange(item->u1.list);
+ check_dow(item->u1.list->next);
+ check_day(item->u1.list->next->next);
+ check_month(item->u1.list->next->next->next);
+ }
+
+ check_pval(item->u2.statements, apps,in_globals);
+ if (item->u3.else_statements) {
+ check_pval(item->u3.else_statements, apps,in_globals);
+ }
+ break;
+
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
+ ast_expr_register_extra_error_info(errmsg);
+ ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
+ ast_expr_clear_extra_error_info();
+ if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
+ ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
+ item->filename, item->startline, item->endline, item->u1.str);
+ warns++;
+ }
+ check_expr2_input(item,item->u1.str);
+ check_pval(item->u2.statements, apps,in_globals);
+ if (item->u3.else_statements) {
+ check_pval(item->u3.else_statements, apps,in_globals);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ /* we can check the switch expression, see if it matches any of the app variables...
+ if it does, then, are all the possible cases accounted for? */
+ check_switch_expr(item, apps);
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ current_extension = item ;
+
+ check_pval(item->u2.statements, apps,in_globals);
+ break;
+
+ case PV_IGNOREPAT:
+ /* fields: item->u1.str == the ignorepat data
+ */
+ break;
+
+ case PV_GLOBALS:
+ /* fields: item->u1.statements == pval list of statements, usually vardecs
+ */
+ in_abstract_context = 0;
+ check_pval(item->u1.statements, apps, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+void check_pval(pval *item, struct argapp *apps, int in_globals)
+{
+ pval *i;
+
+ /* checks to do:
+ 1. Do goto's point to actual labels?
+ 2. Do macro calls reference a macro?
+ 3. Does the number of macro args match the definition?
+ 4. Is a macro call missing its & at the front?
+ 5. Application calls-- we could check syntax for existing applications,
+ but I need some some sort of universal description bnf for a general
+ sort of method for checking arguments, in number, maybe even type, at least.
+ Don't want to hand code checks for hundreds of applications.
+ */
+
+ for (i=item; i; i=i->next) {
+ check_pval_item(i,apps,in_globals);
+ }
+}
+
+void ael2_semantic_check(pval *item, int *arg_errs, int *arg_warns, int *arg_notes)
+{
+
+#ifdef AAL_ARGCHECK
+ int argapp_errs =0;
+ char *rfilename;
+#endif
+ struct argapp *apps=0;
+
+ if (!item)
+ return; /* don't check an empty tree */
+#ifdef AAL_ARGCHECK
+ rfilename = alloca(10 + strlen(ast_config_AST_VAR_DIR));
+ sprintf(rfilename, "%s/applist", ast_config_AST_VAR_DIR);
+
+ apps = argdesc_parse(rfilename, &argapp_errs); /* giveth */
+#endif
+ current_db = item;
+ errs = warns = notes = 0;
+
+ check_context_names();
+ check_pval(item, apps, 0);
+
+#ifdef AAL_ARGCHECK
+ argdesc_destroy(apps); /* taketh away */
+#endif
+ current_db = 0;
+
+ *arg_errs = errs;
+ *arg_warns = warns;
+ *arg_notes = notes;
+}
+
+/* =============================================================================================== */
+/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
+/* =============================================================================================== */
+
+static int control_statement_count = 0;
+
+struct ael_priority *new_prio(void)
+{
+ struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1);
+ return x;
+}
+
+struct ael_extension *new_exten(void)
+{
+ struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1);
+ return x;
+}
+
+void linkprio(struct ael_extension *exten, struct ael_priority *prio)
+{
+ if (!exten->plist) {
+ exten->plist = prio;
+ exten->plist_last = prio;
+ } else {
+ exten->plist_last->next = prio;
+ exten->plist_last = prio;
+ }
+ if( !prio->exten )
+ prio->exten = exten; /* don't override the switch value */
+}
+
+void destroy_extensions(struct ael_extension *exten)
+{
+ struct ael_extension *ne, *nen;
+ for (ne=exten; ne; ne=nen) {
+ struct ael_priority *pe, *pen;
+
+ if (ne->name)
+ free(ne->name);
+
+ /* cidmatch fields are allocated with name, and freed when
+ the name field is freed. Don't do a free for this field,
+ unless you LIKE to see a crash! */
+
+ if (ne->hints)
+ free(ne->hints);
+
+ for (pe=ne->plist; pe; pe=pen) {
+ pen = pe->next;
+ if (pe->app)
+ free(pe->app);
+ pe->app = 0;
+ if (pe->appargs)
+ free(pe->appargs);
+ pe->appargs = 0;
+ pe->origin = 0;
+ pe->goto_true = 0;
+ pe->goto_false = 0;
+ free(pe);
+ }
+ nen = ne->next_exten;
+ ne->next_exten = 0;
+ ne->plist =0;
+ ne->plist_last = 0;
+ ne->next_exten = 0;
+ ne->loop_break = 0;
+ ne->loop_continue = 0;
+ free(ne);
+ }
+}
+
+static int label_inside_case(pval *label)
+{
+ pval *p = label;
+
+ while( p && p->type != PV_MACRO && p->type != PV_CONTEXT ) /* early cutout, sort of */ {
+ if( p->type == PV_CASE || p->type == PV_DEFAULT || p->type == PV_PATTERN ) {
+ return 1;
+ }
+
+ p = p->dad;
+ }
+ return 0;
+}
+
+static void linkexten(struct ael_extension *exten, struct ael_extension *add)
+{
+ add->next_exten = exten->next_exten; /* this will reverse the order. Big deal. */
+ exten->next_exten = add;
+}
+
+static void remove_spaces_before_equals(char *str)
+{
+ char *p;
+ while( str && *str && *str != '=' )
+ {
+ if( *str == ' ' || *str == '\n' || *str == '\r' || *str == '\t' )
+ {
+ p = str;
+ while( *p )
+ {
+ *p = *(p+1);
+ p++;
+ }
+ }
+ else
+ str++;
+ }
+}
+
+/* =============================================================================================== */
+/* "CODE" GENERATOR -- Convert the AEL representation to asterisk extension language */
+/* =============================================================================================== */
+
+static void gen_match_to_pattern(char *pattern, char *result)
+{
+ /* the result will be a string that will be matched by pattern */
+ char *p=pattern, *t=result;
+ while (*p) {
+ if (*p == 'x' || *p == 'n' || *p == 'z' || *p == 'X' || *p == 'N' || *p == 'Z')
+ *t++ = '9';
+ else if (*p == '[') {
+ char *z = p+1;
+ while (*z != ']')
+ z++;
+ if (*(z+1)== ']')
+ z++;
+ *t++=*(p+1); /* use the first char in the set */
+ p = z;
+ } else {
+ *t++ = *p;
+ }
+ p++;
+ }
+ *t++ = 0; /* cap it off */
+}
+
+static void gen_prios(struct ael_extension *exten, char *label, pval *statement, struct ael_extension *mother_exten, struct ast_context *this_context )
+{
+ pval *p,*p2,*p3;
+ struct ael_priority *pr;
+ struct ael_priority *for_init, *for_test, *for_inc, *for_loop, *for_end;
+ struct ael_priority *while_test, *while_loop, *while_end;
+ struct ael_priority *switch_test, *switch_end, *fall_thru, *switch_empty;
+ struct ael_priority *if_test, *if_end, *if_skip, *if_false;
+#ifdef OLD_RAND_ACTION
+ struct ael_priority *rand_test, *rand_end, *rand_skip;
+#endif
+ char buf1[2000];
+ char buf2[2000];
+ char *strp, *strp2;
+ char new_label[2000];
+ int default_exists;
+ int local_control_statement_count;
+ int first;
+ struct ael_priority *loop_break_save;
+ struct ael_priority *loop_continue_save;
+ struct ael_extension *switch_case,*switch_null;
+
+ for (p=statement; p; p=p->next) {
+ switch (p->type) {
+ case PV_VARDEC:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"%s=$[%s]", p->u1.str, p->u2.val);
+ pr->app = strdup("Set");
+ remove_spaces_before_equals(buf1);
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_LOCALVARDEC:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"LOCAL(%s)=$[%s]", p->u1.str, p->u2.val);
+ pr->app = strdup("Set");
+ remove_spaces_before_equals(buf1);
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_GOTO:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ p->u2.goto_target = get_goto_target(p);
+ if( p->u2.goto_target ) {
+ p->u3.goto_target_in_case = p->u2.goto_target->u2.label_in_case = label_inside_case(p->u2.goto_target);
+ }
+
+ if (!p->u1.list->next) /* just one */ {
+ pr->app = strdup("Goto");
+ if (!mother_exten)
+ pr->appargs = strdup(p->u1.list->u1.str);
+ else { /* for the case of simple within-extension gotos in case/pattern/default statement blocks: */
+ snprintf(buf1,sizeof(buf1),"%s,%s", mother_exten->name, p->u1.list->u1.str);
+ pr->appargs = strdup(buf1);
+ }
+
+ } else if (p->u1.list->next && !p->u1.list->next->next) /* two */ {
+ snprintf(buf1,sizeof(buf1),"%s,%s", p->u1.list->u1.str, p->u1.list->next->u1.str);
+ pr->app = strdup("Goto");
+ pr->appargs = strdup(buf1);
+ } else if (p->u1.list->next && p->u1.list->next->next) {
+ snprintf(buf1,sizeof(buf1),"%s,%s,%s", p->u1.list->u1.str,
+ p->u1.list->next->u1.str,
+ p->u1.list->next->next->u1.str);
+ pr->app = strdup("Goto");
+ pr->appargs = strdup(buf1);
+ }
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_LABEL:
+ pr = new_prio();
+ pr->type = AEL_LABEL;
+ pr->origin = p;
+ p->u3.compiled_label = exten;
+ linkprio(exten, pr);
+ break;
+
+ case PV_FOR:
+ control_statement_count++;
+ loop_break_save = exten->loop_break; /* save them, then restore before leaving */
+ loop_continue_save = exten->loop_continue;
+ snprintf(new_label,sizeof(new_label),"for-%s-%d", label, control_statement_count);
+ for_init = new_prio();
+ for_inc = new_prio();
+ for_test = new_prio();
+ for_loop = new_prio();
+ for_end = new_prio();
+ for_init->type = AEL_APPCALL;
+ for_inc->type = AEL_APPCALL;
+ for_test->type = AEL_FOR_CONTROL;
+ for_test->goto_false = for_end;
+ for_loop->type = AEL_CONTROL1; /* simple goto */
+ for_end->type = AEL_APPCALL;
+ for_init->app = strdup("Set");
+
+ strcpy(buf2,p->u1.for_init);
+ remove_spaces_before_equals(buf2);
+ strp = strchr(buf2, '=');
+ if (strp) {
+ strp2 = strchr(p->u1.for_init, '=');
+ *(strp+1) = 0;
+ strcat(buf2,"$[");
+ strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
+ strcat(buf2,"]");
+ for_init->appargs = strdup(buf2);
+ } else {
+ strp2 = p->u1.for_init;
+ while (*strp2 && isspace(*strp2))
+ strp2++;
+ if (*strp2 == '&') { /* itsa macro call */
+ char *strp3 = strp2+1;
+ while (*strp3 && isspace(*strp3))
+ strp3++;
+ strcpy(buf2, strp3);
+ strp3 = strchr(buf2,'(');
+ if (strp3) {
+ *strp3 = '|';
+ }
+ while ((strp3=strchr(buf2,','))) {
+ *strp3 = '|';
+ }
+ strp3 = strrchr(buf2, ')');
+ if (strp3)
+ *strp3 = 0; /* remove the closing paren */
+
+ for_init->appargs = strdup(buf2);
+ free(for_init->app);
+ for_init->app = strdup("Macro");
+ } else { /* must be a regular app call */
+ char *strp3;
+ strcpy(buf2, strp2);
+ strp3 = strchr(buf2,'(');
+ if (strp3) {
+ *strp3 = 0;
+ free(for_init->app);
+ for_init->app = strdup(buf2);
+ for_init->appargs = strdup(strp3+1);
+ strp3 = strrchr(for_init->appargs, ')');
+ if (strp3)
+ *strp3 = 0; /* remove the closing paren */
+ }
+ }
+ }
+
+ strcpy(buf2,p->u3.for_inc);
+ remove_spaces_before_equals(buf2);
+ strp = strchr(buf2, '=');
+ if (strp) { /* there's an = in this part; that means an assignment. set it up */
+ strp2 = strchr(p->u3.for_inc, '=');
+ *(strp+1) = 0;
+ strcat(buf2,"$[");
+ strncat(buf2,strp2+1, sizeof(buf2)-strlen(strp2+1)-2);
+ strcat(buf2,"]");
+ for_inc->appargs = strdup(buf2);
+ for_inc->app = strdup("Set");
+ } else {
+ strp2 = p->u3.for_inc;
+ while (*strp2 && isspace(*strp2))
+ strp2++;
+ if (*strp2 == '&') { /* itsa macro call */
+ char *strp3 = strp2+1;
+ while (*strp3 && isspace(*strp3))
+ strp3++;
+ strcpy(buf2, strp3);
+ strp3 = strchr(buf2,'(');
+ if (strp3) {
+ *strp3 = ',';
+ }
+ strp3 = strrchr(buf2, ')');
+ if (strp3)
+ *strp3 = 0; /* remove the closing paren */
+
+ for_inc->appargs = strdup(buf2);
+
+ for_inc->app = strdup("Macro");
+ } else { /* must be a regular app call */
+ char *strp3;
+ strcpy(buf2, strp2);
+ strp3 = strchr(buf2,'(');
+ if (strp3) {
+ *strp3 = 0;
+ for_inc->app = strdup(buf2);
+ for_inc->appargs = strdup(strp3+1);
+ strp3 = strrchr(for_inc->appargs, ')');
+ if (strp3)
+ *strp3 = 0; /* remove the closing paren */
+ }
+ }
+ }
+ snprintf(buf1,sizeof(buf1),"$[%s]",p->u2.for_test);
+ for_test->app = 0;
+ for_test->appargs = strdup(buf1);
+ for_loop->goto_true = for_test;
+ snprintf(buf1,sizeof(buf1),"Finish for-%s-%d", label, control_statement_count);
+ for_end->app = strdup("NoOp");
+ for_end->appargs = strdup(buf1);
+ /* link & load! */
+ linkprio(exten, for_init);
+ linkprio(exten, for_test);
+
+ /* now, put the body of the for loop here */
+ exten->loop_break = for_end;
+ exten->loop_continue = for_inc;
+
+ gen_prios(exten, new_label, p->u4.for_statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ linkprio(exten, for_inc);
+ linkprio(exten, for_loop);
+ linkprio(exten, for_end);
+
+
+ exten->loop_break = loop_break_save;
+ exten->loop_continue = loop_continue_save;
+ for_loop->origin = p;
+ break;
+
+ case PV_WHILE:
+ control_statement_count++;
+ loop_break_save = exten->loop_break; /* save them, then restore before leaving */
+ loop_continue_save = exten->loop_continue;
+ snprintf(new_label,sizeof(new_label),"while-%s-%d", label, control_statement_count);
+ while_test = new_prio();
+ while_loop = new_prio();
+ while_end = new_prio();
+ while_test->type = AEL_FOR_CONTROL;
+ while_test->goto_false = while_end;
+ while_loop->type = AEL_CONTROL1; /* simple goto */
+ while_end->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
+ while_test->app = 0;
+ while_test->appargs = strdup(buf1);
+ while_loop->goto_true = while_test;
+ snprintf(buf1,sizeof(buf1),"Finish while-%s-%d", label, control_statement_count);
+ while_end->app = strdup("NoOp");
+ while_end->appargs = strdup(buf1);
+
+ linkprio(exten, while_test);
+
+ /* now, put the body of the for loop here */
+ exten->loop_break = while_end;
+ exten->loop_continue = while_test;
+
+ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the while body statements here */
+
+ linkprio(exten, while_loop);
+ linkprio(exten, while_end);
+
+
+ exten->loop_break = loop_break_save;
+ exten->loop_continue = loop_continue_save;
+ while_loop->origin = p;
+ break;
+
+ case PV_SWITCH:
+ control_statement_count++;
+ local_control_statement_count = control_statement_count;
+ loop_break_save = exten->loop_break; /* save them, then restore before leaving */
+ loop_continue_save = exten->loop_continue;
+ snprintf(new_label,sizeof(new_label),"sw-%s-%d", label, control_statement_count);
+
+ switch_test = new_prio();
+ switch_end = new_prio();
+ switch_test->type = AEL_APPCALL;
+ switch_end->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",control_statement_count, p->u1.str);
+ switch_test->app = strdup("Goto");
+ switch_test->appargs = strdup(buf1);
+ snprintf(buf1,sizeof(buf1),"Finish switch-%s-%d", label, control_statement_count);
+ switch_end->app = strdup("NoOp");
+ switch_end->appargs = strdup(buf1);
+ switch_end->origin = p;
+ switch_end->exten = exten;
+
+ linkprio(exten, switch_test);
+ linkprio(exten, switch_end);
+
+ exten->loop_break = switch_end;
+ exten->loop_continue = 0;
+ default_exists = 0;
+
+ for (p2=p->u2.statements; p2; p2=p2->next) {
+ /* now, for each case/default put the body of the for loop here */
+ if (p2->type == PV_CASE) {
+ /* ok, generate a extension and link it in */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ switch_case->is_switch = 1;
+ /* the break/continue locations are inherited from parent */
+ switch_case->loop_break = exten->loop_break;
+ switch_case->loop_continue = exten->loop_continue;
+
+ linkexten(exten,switch_case);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s", local_control_statement_count, p2->u1.str);
+ switch_case->name = strdup(buf1);
+ snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
+
+ gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the case body statements here */
+
+ /* here is where we write code to "fall thru" to the next case... if there is one... */
+ for (p3=p2->u2.statements; p3; p3=p3->next) {
+ if (!p3->next)
+ break;
+ }
+ /* p3 now points the last statement... */
+ if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) {
+ /* is there a following CASE/PATTERN/DEFAULT? */
+ if (p2->next && p2->next->type == PV_CASE) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_PATTERN) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ gen_match_to_pattern(p2->next->u1.str, buf2);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10", local_control_statement_count, buf2);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_DEFAULT) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (!p2->next) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_CONTROL1;
+ fall_thru->goto_true = switch_end;
+ fall_thru->app = strdup("Goto");
+ linkprio(switch_case, fall_thru);
+ }
+ }
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+ } else if (p2->type == PV_PATTERN) {
+ /* ok, generate a extension and link it in */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ switch_case->is_switch = 1;
+ /* the break/continue locations are inherited from parent */
+ switch_case->loop_break = exten->loop_break;
+ switch_case->loop_continue = exten->loop_continue;
+
+ linkexten(exten,switch_case);
+ snprintf(buf1,sizeof(buf1),"_sw-%d-%s", local_control_statement_count, p2->u1.str);
+ switch_case->name = strdup(buf1);
+ snprintf(new_label,sizeof(new_label),"sw-%s-%s-%d", label, p2->u1.str, local_control_statement_count);
+
+ gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the while body statements here */
+ /* here is where we write code to "fall thru" to the next case... if there is one... */
+ for (p3=p2->u2.statements; p3; p3=p3->next) {
+ if (!p3->next)
+ break;
+ }
+ /* p3 now points the last statement... */
+ if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
+ /* is there a following CASE/PATTERN/DEFAULT? */
+ if (p2->next && p2->next->type == PV_CASE) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_PATTERN) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ gen_match_to_pattern(p2->next->u1.str, buf2);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_DEFAULT) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (!p2->next) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_CONTROL1;
+ fall_thru->goto_true = switch_end;
+ fall_thru->app = strdup("Goto");
+ linkprio(switch_case, fall_thru);
+ }
+ }
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+ } else if (p2->type == PV_DEFAULT) {
+ /* ok, generate a extension and link it in */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ switch_case->is_switch = 1;
+
+ /* new: the default case intros a pattern with ., which covers ALMOST everything.
+ but it doesn't cover a NULL pattern. So, we'll define a null extension to match
+ that goto's the default extension. */
+
+ default_exists++;
+ switch_null = new_exten();
+ switch_null->context = this_context;
+ switch_null->is_switch = 1;
+ switch_empty = new_prio();
+ snprintf(buf1,sizeof(buf1),"sw-%d-.|10",local_control_statement_count);
+ switch_empty->app = strdup("Goto");
+ switch_empty->appargs = strdup(buf1);
+ linkprio(switch_null, switch_empty);
+ snprintf(buf1,sizeof(buf1),"sw-%d-", local_control_statement_count);
+ switch_null->name = strdup(buf1);
+ switch_null->loop_break = exten->loop_break;
+ switch_null->loop_continue = exten->loop_continue;
+ linkexten(exten,switch_null);
+
+ /* the break/continue locations are inherited from parent */
+ switch_case->loop_break = exten->loop_break;
+ switch_case->loop_continue = exten->loop_continue;
+ linkexten(exten,switch_case);
+ snprintf(buf1,sizeof(buf1),"_sw-%d-.", local_control_statement_count);
+ switch_case->name = strdup(buf1);
+
+ snprintf(new_label,sizeof(new_label),"sw-%s-default-%d", label, local_control_statement_count);
+
+ gen_prios(switch_case, new_label, p2->u2.statements, exten, this_context); /* this will link in all the default: body statements here */
+
+ /* here is where we write code to "fall thru" to the next case... if there is one... */
+ for (p3=p2->u2.statements; p3; p3=p3->next) {
+ if (!p3->next)
+ break;
+ }
+ /* p3 now points the last statement... */
+ if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) {
+ /* is there a following CASE/PATTERN/DEFAULT? */
+ if (p2->next && p2->next->type == PV_CASE) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, p2->next->u1.str);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_PATTERN) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ gen_match_to_pattern(p2->next->u1.str, buf2);
+ snprintf(buf1,sizeof(buf1),"sw-%d-%s,10",local_control_statement_count, buf2);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (p2->next && p2->next->type == PV_DEFAULT) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_APPCALL;
+ fall_thru->app = strdup("Goto");
+ snprintf(buf1,sizeof(buf1),"sw-%d-.,10",local_control_statement_count);
+ fall_thru->appargs = strdup(buf1);
+ linkprio(switch_case, fall_thru);
+ } else if (!p2->next) {
+ fall_thru = new_prio();
+ fall_thru->type = AEL_CONTROL1;
+ fall_thru->goto_true = switch_end;
+ fall_thru->app = strdup("Goto");
+ linkprio(switch_case, fall_thru);
+ }
+ }
+ if (switch_case->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+ } else {
+ /* what could it be??? */
+ }
+ }
+
+ exten->loop_break = loop_break_save;
+ exten->loop_continue = loop_continue_save;
+ switch_test->origin = p;
+ switch_end->origin = p;
+ break;
+
+ case PV_MACRO_CALL:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"%s,s,1", p->u1.str);
+ first = 1;
+ for (p2 = p->u2.arglist; p2; p2 = p2->next) {
+ if (first)
+ {
+ strcat(buf1,"(");
+ first = 0;
+ }
+ else
+ strcat(buf1,",");
+ strcat(buf1,p2->u1.str);
+ }
+ if (!first)
+ strcat(buf1,")");
+
+ pr->app = strdup("Gosub");
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_APPLICATION_CALL:
+ pr = new_prio();
+ pr->type = AEL_APPCALL;
+ buf1[0] = 0;
+ for (p2 = p->u2.arglist; p2; p2 = p2->next) {
+ if (p2 != p->u2.arglist )
+ strcat(buf1,",");
+ strcat(buf1,p2->u1.str);
+ }
+ pr->app = strdup(p->u1.str);
+ pr->appargs = strdup(buf1);
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_BREAK:
+ pr = new_prio();
+ pr->type = AEL_CONTROL1; /* simple goto */
+ pr->goto_true = exten->loop_break;
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_RETURN: /* hmmmm */
+ pr = new_prio();
+ pr->type = AEL_RETURN; /* simple Return */
+ /* exten->return_needed++; */
+ pr->app = strdup("Return");
+ pr->appargs = strdup("");
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_CONTINUE:
+ pr = new_prio();
+ pr->type = AEL_CONTROL1; /* simple goto */
+ pr->goto_true = exten->loop_continue;
+ pr->origin = p;
+ linkprio(exten, pr);
+ break;
+
+ case PV_IFTIME:
+ control_statement_count++;
+ snprintf(new_label,sizeof(new_label),"iftime-%s-%d", label, control_statement_count);
+
+ if_test = new_prio();
+ if_test->type = AEL_IFTIME_CONTROL;
+ snprintf(buf1,sizeof(buf1),"%s,%s,%s,%s",
+ p->u1.list->u1.str,
+ p->u1.list->next->u1.str,
+ p->u1.list->next->next->u1.str,
+ p->u1.list->next->next->next->u1.str);
+ if_test->app = 0;
+ if_test->appargs = strdup(buf1);
+ if_test->origin = p;
+
+ if_end = new_prio();
+ if_end->type = AEL_APPCALL;
+ snprintf(buf1,sizeof(buf1),"Finish iftime-%s-%d", label, control_statement_count);
+ if_end->app = strdup("NoOp");
+ if_end->appargs = strdup(buf1);
+
+ if (p->u3.else_statements) {
+ if_skip = new_prio();
+ if_skip->type = AEL_CONTROL1; /* simple goto */
+ if_skip->goto_true = if_end;
+ if_skip->origin = p;
+
+ } else {
+ if_skip = 0;
+
+ if_test->goto_false = if_end;
+ }
+
+ if_false = new_prio();
+ if_false->type = AEL_CONTROL1;
+ if (p->u3.else_statements) {
+ if_false->goto_true = if_skip; /* +1 */
+ } else {
+ if_false->goto_true = if_end;
+ }
+
+ /* link & load! */
+ linkprio(exten, if_test);
+ linkprio(exten, if_false);
+
+ /* now, put the body of the if here */
+
+ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ if (p->u3.else_statements) {
+ linkprio(exten, if_skip);
+ gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ }
+
+ linkprio(exten, if_end);
+
+ break;
+
+ case PV_RANDOM:
+ case PV_IF:
+ control_statement_count++;
+ snprintf(new_label,sizeof(new_label),"if-%s-%d", label, control_statement_count);
+
+ if_test = new_prio();
+ if_end = new_prio();
+ if_test->type = AEL_IF_CONTROL;
+ if_end->type = AEL_APPCALL;
+ if ( p->type == PV_RANDOM )
+ snprintf(buf1,sizeof(buf1),"$[${RAND(0,99)} < (%s)]",p->u1.str);
+ else
+ snprintf(buf1,sizeof(buf1),"$[%s]",p->u1.str);
+ if_test->app = 0;
+ if_test->appargs = strdup(buf1);
+ snprintf(buf1,sizeof(buf1),"Finish if-%s-%d", label, control_statement_count);
+ if_end->app = strdup("NoOp");
+ if_end->appargs = strdup(buf1);
+ if_test->origin = p;
+
+ if (p->u3.else_statements) {
+ if_skip = new_prio();
+ if_skip->type = AEL_CONTROL1; /* simple goto */
+ if_skip->goto_true = if_end;
+ if_test->goto_false = if_skip;;
+ } else {
+ if_skip = 0;
+ if_test->goto_false = if_end;;
+ }
+
+ /* link & load! */
+ linkprio(exten, if_test);
+
+ /* now, put the body of the if here */
+
+ gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ if (p->u3.else_statements) {
+ linkprio(exten, if_skip);
+ gen_prios(exten, new_label, p->u3.else_statements, mother_exten, this_context); /* this will link in all the statements here */
+
+ }
+
+ linkprio(exten, if_end);
+
+ break;
+
+ case PV_STATEMENTBLOCK:
+ gen_prios(exten, label, p->u1.list, mother_exten, this_context ); /* recurse into the block */
+ break;
+
+ case PV_CATCH:
+ control_statement_count++;
+ /* generate an extension with name of catch, put all catch stats
+ into this exten! */
+ switch_case = new_exten();
+ switch_case->context = this_context;
+ linkexten(exten,switch_case);
+ switch_case->name = strdup(p->u1.str);
+ snprintf(new_label,sizeof(new_label),"catch-%s-%d",p->u1.str, control_statement_count);
+
+ gen_prios(switch_case, new_label, p->u2.statements,mother_exten,this_context); /* this will link in all the catch body statements here */
+ if (switch_case->return_needed) { /* returns now generate a Return() app call, no longer a goto to the end of the exten */
+ char buf[2000];
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", switch_case->name);
+ np2->appargs = strdup(buf);
+ linkprio(switch_case, np2);
+ switch_case-> return_target = np2;
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void set_priorities(struct ael_extension *exten)
+{
+ int i;
+ struct ael_priority *pr;
+ do {
+ if (exten->is_switch)
+ i = 10;
+ else if (exten->regexten)
+ i=2;
+ else
+ i=1;
+
+ for (pr=exten->plist; pr; pr=pr->next) {
+ pr->priority_num = i;
+
+ if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan,
+ but we want them to point to the right
+ priority, which would be the next line
+ after the label; */
+ i++;
+ }
+
+ exten = exten->next_exten;
+ } while ( exten );
+}
+
+void add_extensions(struct ael_extension *exten)
+{
+ struct ael_priority *pr;
+ char *label=0;
+ char realext[AST_MAX_EXTENSION];
+ if (!exten) {
+ ast_log(LOG_WARNING, "This file is Empty!\n" );
+ return;
+ }
+ do {
+ struct ael_priority *last = 0;
+
+ pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1);
+ if (exten->hints) {
+ if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch,
+ exten->hints, NULL, ast_free_ptr, registrar)) {
+ ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n",
+ exten->name);
+ }
+ }
+
+ for (pr=exten->plist; pr; pr=pr->next) {
+ char app[2000];
+ char appargs[2000];
+
+ /* before we can add the extension, we need to prep the app/appargs;
+ the CONTROL types need to be done after the priority numbers are calculated.
+ */
+ if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ {
+ last = pr;
+ continue;
+ }
+
+ if (pr->app)
+ strcpy(app, pr->app);
+ else
+ app[0] = 0;
+ if (pr->appargs )
+ strcpy(appargs, pr->appargs);
+ else
+ appargs[0] = 0;
+ switch( pr->type ) {
+ case AEL_APPCALL:
+ /* easy case. Everything is all set up */
+ break;
+
+ case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
+ /* simple, unconditional goto. */
+ strcpy(app,"Goto");
+ if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) {
+ snprintf(appargs,sizeof(appargs),"%s,%d", pr->goto_true->exten->name, pr->goto_true->priority_num);
+ } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) {
+ snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1);
+ } else
+ snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num);
+ break;
+
+ case AEL_FOR_CONTROL: /* WHILE loop test, FOR loop test */
+ strcpy(app,"GotoIf");
+ snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
+ break;
+
+ case AEL_IF_CONTROL:
+ strcpy(app,"GotoIf");
+ if (pr->origin->u3.else_statements )
+ snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1);
+ else
+ snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
+ break;
+
+ case AEL_RAND_CONTROL:
+ strcpy(app,"Random");
+ snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1);
+ break;
+
+ case AEL_IFTIME_CONTROL:
+ strcpy(app,"GotoIfTime");
+ snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2);
+ break;
+
+ case AEL_RETURN:
+ strcpy(app,"Return");
+ appargs[0] = 0;
+ break;
+
+ default:
+ break;
+ }
+ if (last && last->type == AEL_LABEL ) {
+ label = last->origin->u1.str;
+ }
+ else
+ label = 0;
+
+ if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch,
+ app, strdup(appargs), ast_free_ptr, registrar)) {
+ ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num,
+ exten->name);
+ }
+ last = pr;
+ }
+ exten = exten->next_exten;
+ } while ( exten );
+}
+
+static void attach_exten(struct ael_extension **list, struct ael_extension *newmem)
+{
+ /* travel to the end of the list... */
+ struct ael_extension *lptr;
+ if( !*list ) {
+ *list = newmem;
+ return;
+ }
+ lptr = *list;
+
+ while( lptr->next_exten ) {
+ lptr = lptr->next_exten;
+ }
+ /* lptr should now pointing to the last element in the list; it has a null next_exten pointer */
+ lptr->next_exten = newmem;
+}
+
+static pval *get_extension_or_contxt(pval *p)
+{
+ while( p && p->type != PV_EXTENSION && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
+
+ p = p->dad;
+ }
+
+ return p;
+}
+
+static pval *get_contxt(pval *p)
+{
+ while( p && p->type != PV_CONTEXT && p->type != PV_MACRO ) {
+
+ p = p->dad;
+ }
+
+ return p;
+}
+
+static void fix_gotos_in_extensions(struct ael_extension *exten)
+{
+ struct ael_extension *e;
+ for(e=exten;e;e=e->next_exten) {
+
+ struct ael_priority *p;
+ for(p=e->plist;p;p=p->next) {
+
+ if( p->origin && p->origin->type == PV_GOTO && p->origin->u3.goto_target_in_case ) {
+
+ /* fix the extension of the goto target to the actual extension in the post-compiled dialplan */
+
+ pval *target = p->origin->u2.goto_target;
+ struct ael_extension *z = target->u3.compiled_label;
+ pval *pv2 = p->origin;
+ char buf1[500];
+ char *apparg_save = p->appargs;
+
+ p->appargs = 0;
+ if (!pv2->u1.list->next) /* just one -- it won't hurt to repeat the extension */ {
+ snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->u1.str);
+ p->appargs = strdup(buf1);
+
+ } else if (pv2->u1.list->next && !pv2->u1.list->next->next) /* two */ {
+ snprintf(buf1,sizeof(buf1),"%s,%s", z->name, pv2->u1.list->next->u1.str);
+ p->appargs = strdup(buf1);
+ } else if (pv2->u1.list->next && pv2->u1.list->next->next) {
+ snprintf(buf1,sizeof(buf1),"%s,%s,%s", pv2->u1.list->u1.str,
+ z->name,
+ pv2->u1.list->next->next->u1.str);
+ p->appargs = strdup(buf1);
+ }
+ else
+ printf("WHAT? The goto doesn't fall into one of three cases for GOTO????\n");
+
+ if( apparg_save ) {
+ free(apparg_save);
+ }
+ }
+ }
+ }
+}
+
+
+void ast_compile_ael2(struct ast_context **local_contexts, struct pval *root)
+{
+ pval *p,*p2;
+ struct ast_context *context;
+ char buf[2000];
+ struct ael_extension *exten;
+ struct ael_extension *exten_list = 0;
+
+ for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there
+ when we try to eval them */
+ switch (p->type) {
+ case PV_GLOBALS:
+ /* just VARDEC elements */
+ for (p2=p->u1.list; p2; p2=p2->next) {
+ char buf2[2000];
+ snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val);
+ pbx_builtin_setvar(NULL, buf2);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (p=root; p; p=p->next ) {
+ pval *lp;
+ int argc;
+
+ switch (p->type) {
+ case PV_MACRO:
+
+ context = ast_context_create(local_contexts, p->u1.str, registrar);
+
+ exten = new_exten();
+ exten->context = context;
+ exten->name = strdup("s");
+ argc = 1;
+ for (lp=p->u2.arglist; lp; lp=lp->next) {
+ /* for each arg, set up a "Set" command */
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("Set");
+ snprintf(buf,sizeof(buf),"LOCAL(%s)=${ARG%d}", lp->u1.str, argc++);
+ remove_spaces_before_equals(buf);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ }
+
+ /* CONTAINS APPCALLS, CATCH, just like extensions... */
+ gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context );
+ if (exten->return_needed) { /* most likely, this will go away */
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ exten-> return_target = np2;
+ }
+
+ set_priorities(exten);
+ attach_exten(&exten_list, exten);
+ break;
+
+ case PV_GLOBALS:
+ /* already done */
+ break;
+
+ case PV_CONTEXT:
+ context = ast_context_find_or_create(local_contexts, p->u1.str, registrar);
+
+ /* contexts contain: ignorepat, includes, switches, eswitches, extensions, */
+ for (p2=p->u2.statements; p2; p2=p2->next) {
+ pval *p3;
+ char *s3;
+
+ switch (p2->type) {
+ case PV_EXTENSION:
+ exten = new_exten();
+ exten->name = strdup(p2->u1.str);
+ exten->context = context;
+
+ if( (s3=strchr(exten->name, '/') ) != 0 )
+ {
+ *s3 = 0;
+ exten->cidmatch = s3+1;
+ }
+
+ if ( p2->u3.hints )
+ exten->hints = strdup(p2->u3.hints);
+ exten->regexten = p2->u4.regexten;
+ gen_prios(exten, p->u1.str, p2->u2.statements, 0, context );
+ if (exten->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"End of Extension %s", exten->name);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ exten-> return_target = np2;
+ }
+ /* is the last priority in the extension a label? Then add a trailing no-op */
+ if( !exten->plist_last )
+ {
+ ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Empty Extension!\n",
+ p2->filename, p2->startline, p2->endline);
+ }
+
+ if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) {
+ struct ael_priority *np2 = new_prio();
+ np2->type = AEL_APPCALL;
+ np2->app = strdup("NoOp");
+ snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str);
+ np2->appargs = strdup(buf);
+ linkprio(exten, np2);
+ }
+
+ set_priorities(exten);
+ attach_exten(&exten_list, exten);
+ break;
+
+ case PV_IGNOREPAT:
+ ast_context_add_ignorepat2(context, p2->u1.str, registrar);
+ break;
+
+ case PV_INCLUDES:
+ for (p3 = p2->u1.list; p3 ;p3=p3->next) {
+ if ( p3->u2.arglist ) {
+ snprintf(buf,sizeof(buf), "%s,%s,%s,%s,%s",
+ p3->u1.str,
+ p3->u2.arglist->u1.str,
+ p3->u2.arglist->next->u1.str,
+ p3->u2.arglist->next->next->u1.str,
+ p3->u2.arglist->next->next->next->u1.str);
+ ast_context_add_include2(context, buf, registrar);
+ } else
+ ast_context_add_include2(context, p3->u1.str, registrar);
+ }
+ break;
+
+ case PV_SWITCHES:
+ for (p3 = p2->u1.list; p3 ;p3=p3->next) {
+ char *c = strchr(p3->u1.str, '/');
+ if (c) {
+ *c = '\0';
+ c++;
+ } else
+ c = "";
+
+ ast_context_add_switch2(context, p3->u1.str, c, 0, registrar);
+ }
+ break;
+
+ case PV_ESWITCHES:
+ for (p3 = p2->u1.list; p3 ;p3=p3->next) {
+ char *c = strchr(p3->u1.str, '/');
+ if (c) {
+ *c = '\0';
+ c++;
+ } else
+ c = "";
+
+ ast_context_add_switch2(context, p3->u1.str, c, 1, registrar);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ /* huh? what? */
+ break;
+
+ }
+ }
+ /* moved these from being done after a macro or extension were processed,
+ to after all processing is done, for the sake of fixing gotos to labels inside cases... */
+ /* I guess this would be considered 2nd pass of compiler now... */
+ fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */
+ add_extensions(exten_list); /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */
+ destroy_extensions(exten_list); /* all that remains is an empty husk, discard of it as is proper */
+
+}
+
+
+/* DESTROY the PVAL tree ============================================================================ */
+
+
+
+void destroy_pval_item(pval *item)
+{
+ if (item == NULL) {
+ ast_log(LOG_WARNING, "null item\n");
+ return;
+ }
+
+ if (item->filename)
+ free(item->filename);
+
+ switch (item->type) {
+ case PV_WORD:
+ /* fields: item->u1.str == string associated with this (word). */
+ if (item->u1.str )
+ free(item->u1.str);
+ if ( item->u2.arglist )
+ destroy_pval(item->u2.arglist);
+ break;
+
+ case PV_MACRO:
+ /* fields: item->u1.str == name of macro
+ item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+
+ item->u3.macro_statements == pval list of statements in macro body.
+ */
+ destroy_pval(item->u2.arglist);
+ if (item->u1.str )
+ free(item->u1.str);
+ destroy_pval(item->u3.macro_statements);
+ break;
+
+ case PV_CONTEXT:
+ /* fields: item->u1.str == name of context
+ item->u2.statements == pval list of statements in context body
+ item->u3.abstract == int 1 if an abstract keyword were present
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_MACRO_CALL:
+ /* fields: item->u1.str == name of macro to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.arglist);
+ break;
+
+ case PV_APPLICATION_CALL:
+ /* fields: item->u1.str == name of application to call
+ item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
+ item->u2.arglist->u1.str == argument
+ item->u2.arglist->next == next arg
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.arglist);
+ break;
+
+ case PV_CASE:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_PATTERN:
+ /* fields: item->u1.str == value of case
+ item->u2.statements == pval list of statements under the case
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_DEFAULT:
+ /* fields:
+ item->u2.statements == pval list of statements under the case
+ */
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_CATCH:
+ /* fields: item->u1.str == name of extension to catch
+ item->u2.statements == pval list of statements in context body
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_SWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_ESWITCHES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_INCLUDES:
+ /* fields: item->u1.list == pval list of PV_WORD elements, one per entry in the list
+ item->u2.arglist == pval list of 4 PV_WORD elements for time values
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_STATEMENTBLOCK:
+ /* fields: item->u1.list == pval list of statements in block, one per entry in the list
+ */
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_LOCALVARDEC:
+ case PV_VARDEC:
+ /* fields: item->u1.str == variable name
+ item->u2.val == variable value to assign
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ if (item->u2.val)
+ free(item->u2.val);
+ break;
+
+ case PV_GOTO:
+ /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user.
+ item->u1.list->u1.str == where the data on a PV_WORD will always be.
+ */
+
+ destroy_pval(item->u1.list);
+ break;
+
+ case PV_LABEL:
+ /* fields: item->u1.str == label name
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ break;
+
+ case PV_FOR:
+ /* fields: item->u1.for_init == a string containing the initalizer
+ item->u2.for_test == a string containing the loop test
+ item->u3.for_inc == a string containing the loop increment
+
+ item->u4.for_statements == a pval list of statements in the for ()
+ */
+ if (item->u1.for_init)
+ free(item->u1.for_init);
+ if (item->u2.for_test)
+ free(item->u2.for_test);
+ if (item->u3.for_inc)
+ free(item->u3.for_inc);
+ destroy_pval(item->u4.for_statements);
+ break;
+
+ case PV_WHILE:
+ /* fields: item->u1.str == the while conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the while ()
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_BREAK:
+ /* fields: none
+ */
+ break;
+
+ case PV_RETURN:
+ /* fields: none
+ */
+ break;
+
+ case PV_CONTINUE:
+ /* fields: none
+ */
+ break;
+
+ case PV_IFTIME:
+ /* fields: item->u1.list == the 4 time values, in PV_WORD structs, linked list
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ destroy_pval(item->u1.list);
+ destroy_pval(item->u2.statements);
+ if (item->u3.else_statements) {
+ destroy_pval(item->u3.else_statements);
+ }
+ break;
+
+ case PV_RANDOM:
+ /* fields: item->u1.str == the random percentage, as supplied by user
+
+ item->u2.statements == a pval list of statements in the true part ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ fall thru to If */
+ case PV_IF:
+ /* fields: item->u1.str == the if conditional, as supplied by user
+
+ item->u2.statements == a pval list of statements in the if ()
+ item->u3.else_statements == a pval list of statements in the else
+ (could be zero)
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ if (item->u3.else_statements) {
+ destroy_pval(item->u3.else_statements);
+ }
+ break;
+
+ case PV_SWITCH:
+ /* fields: item->u1.str == the switch expression
+
+ item->u2.statements == a pval list of statements in the switch,
+ (will be case statements, most likely!)
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_EXTENSION:
+ /* fields: item->u1.str == the extension name, label, whatever it's called
+
+ item->u2.statements == a pval list of statements in the extension
+ item->u3.hints == a char * hint argument
+ item->u4.regexten == an int boolean. non-zero says that regexten was specified
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ if (item->u3.hints)
+ free(item->u3.hints);
+ destroy_pval(item->u2.statements);
+ break;
+
+ case PV_IGNOREPAT:
+ /* fields: item->u1.str == the ignorepat data
+ */
+ if (item->u1.str)
+ free(item->u1.str);
+ break;
+
+ case PV_GLOBALS:
+ /* fields: item->u1.statements == pval list of statements, usually vardecs
+ */
+ destroy_pval(item->u1.statements);
+ break;
+ }
+ free(item);
+}
+
+void destroy_pval(pval *item)
+{
+ pval *i,*nxt;
+
+ for (i=item; i; i=nxt) {
+ nxt = i->next;
+
+ destroy_pval_item(i);
+ }
+}
+
+#ifdef AAL_ARGCHECK
+static char *ael_funclist[] =
+{
+ "AGENT",
+ "ARRAY",
+ "BASE64_DECODE",
+ "BASE64_ENCODE",
+ "CALLERID",
+ "CDR",
+ "CHANNEL",
+ "CHECKSIPDOMAIN",
+ "CHECK_MD5",
+ "CURL",
+ "CUT",
+ "DB",
+ "DB_EXISTS",
+ "DUNDILOOKUP",
+ "ENUMLOOKUP",
+ "ENV",
+ "EVAL",
+ "EXISTS",
+ "FIELDQTY",
+ "FILTER",
+ "GROUP",
+ "GROUP_COUNT",
+ "GROUP_LIST",
+ "GROUP_MATCH_COUNT",
+ "IAXPEER",
+ "IF",
+ "IFTIME",
+ "ISNULL",
+ "KEYPADHASH",
+ "LANGUAGE",
+ "LEN",
+ "MATH",
+ "MD5",
+ "MUSICCLASS",
+ "QUEUEAGENTCOUNT",
+ "QUEUE_MEMBER_COUNT",
+ "QUEUE_MEMBER_LIST",
+ "QUOTE",
+ "RAND",
+ "REGEX",
+ "SET",
+ "SHA1",
+ "SIPCHANINFO",
+ "SIPPEER",
+ "SIP_HEADER",
+ "SORT",
+ "STAT",
+ "STRFTIME",
+ "STRPTIME",
+ "TIMEOUT",
+ "TXTCIDNAME",
+ "URIDECODE",
+ "URIENCODE",
+ "VMCOUNT"
+};
+
+
+int ael_is_funcname(char *name)
+{
+ int s,t;
+ t = sizeof(ael_funclist)/sizeof(char*);
+ s = 0;
+ while ((s < t) && strcasecmp(name, ael_funclist[s]))
+ s++;
+ if ( s < t )
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+
+/* PVAL PI */
+
+/* ----------------- implementation ------------------- */
+
+
+int pvalCheckType( pval *p, char *funcname, pvaltype type )
+{
+ if (p->type != type)
+ {
+ ast_log(LOG_ERROR, "Func: %s the pval passed is not appropriate for this function!\n", funcname);
+ return 0;
+ }
+ return 1;
+}
+
+
+pval *pvalCreateNode( pvaltype type )
+{
+ pval *p = calloc(1,sizeof(pval)); /* why, oh why, don't I use ast_calloc? Way, way, way too messy if I do! */
+ p->type = type; /* remember, this can be used externally or internally to asterisk */
+ return p;
+}
+
+pvaltype pvalObjectGetType( pval *p )
+{
+ return p->type;
+}
+
+
+void pvalWordSetString( pval *p, char *str)
+{
+ if (!pvalCheckType(p, "pvalWordSetString", PV_WORD))
+ return;
+ p->u1.str = str;
+}
+
+char *pvalWordGetString( pval *p )
+{
+ if (!pvalCheckType(p, "pvalWordGetString", PV_WORD))
+ return 0;
+ return p->u1.str;
+}
+
+
+void pvalMacroSetName( pval *p, char *name)
+{
+ if (!pvalCheckType(p, "pvalMacroSetName", PV_MACRO))
+ return;
+ p->u1.str = name;
+}
+
+char *pvalMacroGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalMacroGetName", PV_MACRO))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalMacroSetArglist( pval *p, pval *arglist )
+{
+ if (!pvalCheckType(p, "pvalMacroSetArglist", PV_MACRO))
+ return;
+ p->u2.arglist = arglist;
+}
+
+void pvalMacroAddArg( pval *p, pval *arg ) /* single arg only! */
+{
+ if (!pvalCheckType(p, "pvalMacroAddArg", PV_MACRO))
+ return;
+ if (!p->u2.arglist)
+ p->u2.arglist = arg;
+ else
+ linku1(p->u2.arglist, arg);
+
+}
+
+pval *pvalMacroWalkArgs( pval *p, pval **arg )
+{
+ if (!pvalCheckType(p, "pvalMacroWalkArgs", PV_MACRO))
+ return 0;
+ if (!(*arg))
+ *arg = p->u2.arglist;
+ else {
+ *arg = (*arg)->next;
+ }
+ return *arg;
+}
+
+void pvalMacroAddStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalMacroAddStatement", PV_MACRO))
+ return;
+ if (!p->u3.macro_statements)
+ p->u3.macro_statements = statement;
+ else
+ linku1(p->u3.macro_statements, statement);
+
+
+}
+
+pval *pvalMacroWalkStatements( pval *p, pval **next_statement )
+{
+ if (!pvalCheckType(p, "pvalMacroWalkStatements", PV_MACRO))
+ return 0;
+ if (!(*next_statement))
+ *next_statement = p->u3.macro_statements;
+ else {
+ *next_statement = (*next_statement)->next;
+ }
+ return *next_statement;
+}
+
+
+
+void pvalContextSetName( pval *p, char *name)
+{
+ if (!pvalCheckType(p, "pvalContextSetName", PV_CONTEXT))
+ return;
+ p->u1.str = name;
+}
+
+char *pvalContextGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextGetName", PV_CONTEXT))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalContextSetAbstract( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextSetAbstract", PV_CONTEXT))
+ return;
+ p->u3.abstract = 1;
+}
+
+void pvalContextUnsetAbstract( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextUnsetAbstract", PV_CONTEXT))
+ return;
+ p->u3.abstract = 0;
+}
+
+int pvalContextGetAbstract( pval *p )
+{
+ if (!pvalCheckType(p, "pvalContextGetAbstract", PV_CONTEXT))
+ return 0;
+ return p->u3.abstract;
+}
+
+
+
+void pvalContextAddStatement( pval *p, pval *statement) /* this includes SWITCHES, INCLUDES, IGNOREPAT, etc */
+{
+ if (!pvalCheckType(p, "pvalContextAddStatement", PV_CONTEXT))
+ return;
+ if (!p->u2.statements)
+ p->u2.statements = statement;
+ else
+ linku1(p->u2.statements, statement);
+}
+
+pval *pvalContextWalkStatements( pval *p, pval **statements )
+{
+ if (!pvalCheckType(p, "pvalContextWalkStatements", PV_CONTEXT))
+ return 0;
+ if (!(*statements))
+ *statements = p->u2.statements;
+ else {
+ *statements = (*statements)->next;
+ }
+ return *statements;
+}
+
+
+void pvalMacroCallSetMacroName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalMacroCallSetMacroName", PV_MACRO_CALL))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalMacroCallGetMacroName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalMacroCallGetMacroName", PV_MACRO_CALL))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalMacroCallSetArglist( pval *p, pval *arglist )
+{
+ if (!pvalCheckType(p, "pvalMacroCallSetArglist", PV_MACRO_CALL))
+ return;
+ p->u2.arglist = arglist;
+}
+
+void pvalMacroCallAddArg( pval *p, pval *arg )
+{
+ if (!pvalCheckType(p, "pvalMacroCallGetAddArg", PV_MACRO_CALL))
+ return;
+ if (!p->u2.arglist)
+ p->u2.arglist = arg;
+ else
+ linku1(p->u2.arglist, arg);
+}
+
+pval *pvalMacroCallWalkArgs( pval *p, pval **args )
+{
+ if (!pvalCheckType(p, "pvalMacroCallWalkArgs", PV_MACRO_CALL))
+ return 0;
+ if (!(*args))
+ *args = p->u2.arglist;
+ else {
+ *args = (*args)->next;
+ }
+ return *args;
+}
+
+
+void pvalAppCallSetAppName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalAppCallSetAppName", PV_APPLICATION_CALL))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalAppCallGetAppName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalAppCallGetAppName", PV_APPLICATION_CALL))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalAppCallSetArglist( pval *p, pval *arglist )
+{
+ if (!pvalCheckType(p, "pvalAppCallSetArglist", PV_APPLICATION_CALL))
+ return;
+ p->u2.arglist = arglist;
+}
+
+void pvalAppCallAddArg( pval *p, pval *arg )
+{
+ if (!pvalCheckType(p, "pvalAppCallAddArg", PV_APPLICATION_CALL))
+ return;
+ if (!p->u2.arglist)
+ p->u2.arglist = arg;
+ else
+ linku1(p->u2.arglist, arg);
+}
+
+pval *pvalAppCallWalkArgs( pval *p, pval **args )
+{
+ if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL))
+ return 0;
+ if (!(*args))
+ *args = p->u2.arglist;
+ else {
+ *args = (*args)->next;
+ }
+ return *args;
+}
+
+
+void pvalCasePatSetVal( pval *p, char *val )
+{
+ if (!pvalCheckType(p, "pvalAppCallWalkArgs", PV_APPLICATION_CALL))
+ return;
+ p->u1.str = val;
+}
+
+char* pvalCasePatGetVal( pval *p )
+{
+ return p->u1.str;
+}
+
+void pvalCasePatDefAddStatement( pval *p, pval *statement )
+{
+ if (!p->u2.arglist)
+ p->u2.statements = statement;
+ else
+ linku1(p->u2.statements, statement);
+}
+
+pval *pvalCasePatDefWalkStatements( pval *p, pval **statement )
+{
+ if (!(*statement))
+ *statement = p->u2.statements;
+ else {
+ *statement = (*statement)->next;
+ }
+ return *statement;
+}
+
+
+void pvalCatchSetExtName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalCatchSetExtName", PV_CATCH))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalCatchGetExtName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalCatchGetExtName", PV_CATCH))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalCatchSetStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalCatchSetStatement", PV_CATCH))
+ return;
+ p->u2.statements = statement;
+}
+
+pval *pvalCatchGetStatement( pval *p )
+{
+ if (!pvalCheckType(p, "pvalCatchGetStatement", PV_CATCH))
+ return 0;
+ return p->u2.statements;
+}
+
+
+void pvalSwitchesAddSwitch( pval *p, char *name )
+{
+ pval *s;
+ if (!pvalCheckType(p, "pvalSwitchesAddSwitch", PV_SWITCHES))
+ return;
+ s = pvalCreateNode(PV_WORD);
+ s->u1.str = name;
+ p->u1.list = linku1(p->u1.list, s);
+}
+
+char* pvalSwitchesWalkNames( pval *p, pval **next_item )
+{
+ if (!pvalCheckType(p, "pvalSwitchesWalkNames", PV_SWITCHES))
+ return 0;
+ if (!(*next_item))
+ *next_item = p->u1.list;
+ else {
+ *next_item = (*next_item)->next;
+ }
+ return (*next_item)->u1.str;
+}
+
+void pvalESwitchesAddSwitch( pval *p, char *name )
+{
+ pval *s;
+ if (!pvalCheckType(p, "pvalESwitchesAddSwitch", PV_ESWITCHES))
+ return;
+ s = pvalCreateNode(PV_WORD);
+ s->u1.str = name;
+ p->u1.list = linku1(p->u1.list, s);
+}
+
+char* pvalESwitchesWalkNames( pval *p, pval **next_item )
+{
+ if (!pvalCheckType(p, "pvalESwitchesWalkNames", PV_ESWITCHES))
+ return 0;
+ if (!(*next_item))
+ *next_item = p->u1.list;
+ else {
+ *next_item = (*next_item)->next;
+ }
+ return (*next_item)->u1.str;
+}
+
+
+void pvalIncludesAddInclude( pval *p, const char *include )
+{
+ pval *s;
+ if (!pvalCheckType(p, "pvalIncludesAddSwitch", PV_INCLUDES))
+ return;
+ s = pvalCreateNode(PV_WORD);
+ s->u1.str = (char *)include;
+ p->u1.list = linku1(p->u1.list, s);
+}
+ /* an include is a WORD with string set to path */
+
+void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range )
+{
+ pval *hr = pvalCreateNode(PV_WORD);
+ pval *dom = pvalCreateNode(PV_WORD);
+ pval *dow = pvalCreateNode(PV_WORD);
+ pval *mon = pvalCreateNode(PV_WORD);
+ pval *s = pvalCreateNode(PV_WORD);
+
+ if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES))
+ return;
+
+ s->u1.str = (char *)include;
+ p->u1.list = linku1(p->u1.list, s);
+
+ hr->u1.str = hour_range;
+ dom->u1.str = dom_range;
+ dow->u1.str = dow_range;
+ mon->u1.str = month_range;
+
+ s->u2.arglist = hr;
+
+ hr->next = dom;
+ dom->next = dow;
+ dow->next = mon;
+ mon->next = 0;
+}
+ /* is this right??? come back and correct it */ /*the ptr is to the WORD */
+void pvalIncludeGetTimeConstraints( pval *p, char **hour_range, char **dom_range, char **dow_range, char **month_range )
+{
+ if (!pvalCheckType(p, "pvalIncludeGetTimeConstraints", PV_WORD))
+ return;
+ if (p->u2.arglist) {
+ *hour_range = p->u2.arglist->u1.str;
+ *dom_range = p->u2.arglist->next->u1.str;
+ *dow_range = p->u2.arglist->next->next->u1.str;
+ *month_range = p->u2.arglist->next->next->next->u1.str;
+ } else {
+ *hour_range = 0;
+ *dom_range = 0;
+ *dow_range = 0;
+ *month_range = 0;
+ }
+}
+ /* is this right??? come back and correct it */ /*the ptr is to the WORD */
+char* pvalIncludesWalk( pval *p, pval **next_item )
+{
+ if (!pvalCheckType(p, "pvalIncludesWalk", PV_INCLUDES))
+ return 0;
+ if (!(*next_item))
+ *next_item = p->u1.list;
+ else {
+ *next_item = (*next_item)->next;
+ }
+ return (*next_item)->u1.str;
+}
+
+
+void pvalStatementBlockAddStatement( pval *p, pval *statement)
+{
+ if (!pvalCheckType(p, "pvalStatementBlockAddStatement", PV_STATEMENTBLOCK))
+ return;
+ p->u1.list = linku1(p->u1.list, statement);
+}
+
+pval *pvalStatementBlockWalkStatements( pval *p, pval **next_statement)
+{
+ if (!pvalCheckType(p, "pvalStatementBlockWalkStatements", PV_STATEMENTBLOCK))
+ return 0;
+ if (!(*next_statement))
+ *next_statement = p->u1.list;
+ else {
+ *next_statement = (*next_statement)->next;
+ }
+ return *next_statement;
+}
+
+void pvalVarDecSetVarname( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalVarDecSetVarname", PV_VARDEC))
+ return;
+ p->u1.str = name;
+}
+
+void pvalVarDecSetValue( pval *p, char *value )
+{
+ if (!pvalCheckType(p, "pvalVarDecSetValue", PV_VARDEC))
+ return;
+ p->u2.val = value;
+}
+
+char* pvalVarDecGetVarname( pval *p )
+{
+ if (!pvalCheckType(p, "pvalVarDecGetVarname", PV_VARDEC))
+ return 0;
+ return p->u1.str;
+}
+
+char* pvalVarDecGetValue( pval *p )
+{
+ if (!pvalCheckType(p, "pvalVarDecGetValue", PV_VARDEC))
+ return 0;
+ return p->u2.val;
+}
+
+void pvalGotoSetTarget( pval *p, char *context, char *exten, char *label )
+{
+ pval *con, *ext, *pri;
+
+ if (!pvalCheckType(p, "pvalGotoSetTarget", PV_GOTO))
+ return;
+ if (context && strlen(context)) {
+ con = pvalCreateNode(PV_WORD);
+ ext = pvalCreateNode(PV_WORD);
+ pri = pvalCreateNode(PV_WORD);
+
+ con->u1.str = context;
+ ext->u1.str = exten;
+ pri->u1.str = label;
+
+ con->next = ext;
+ ext->next = pri;
+ p->u1.list = con;
+ } else if (exten && strlen(exten)) {
+ ext = pvalCreateNode(PV_WORD);
+ pri = pvalCreateNode(PV_WORD);
+
+ ext->u1.str = exten;
+ pri->u1.str = label;
+
+ ext->next = pri;
+ p->u1.list = ext;
+ } else {
+ pri = pvalCreateNode(PV_WORD);
+
+ pri->u1.str = label;
+
+ p->u1.list = pri;
+ }
+}
+
+void pvalGotoGetTarget( pval *p, char **context, char **exten, char **label )
+{
+ if (!pvalCheckType(p, "pvalGotoGetTarget", PV_GOTO))
+ return;
+ if (p->u1.list && p->u1.list->next && p->u1.list->next->next) {
+ *context = p->u1.list->u1.str;
+ *exten = p->u1.list->next->u1.str;
+ *label = p->u1.list->next->next->u1.str;
+
+ } else if (p->u1.list && p->u1.list->next ) {
+ *exten = p->u1.list->u1.str;
+ *label = p->u1.list->next->u1.str;
+ *context = 0;
+
+ } else if (p->u1.list) {
+ *label = p->u1.list->u1.str;
+ *context = 0;
+ *exten = 0;
+
+ } else {
+ *context = 0;
+ *exten = 0;
+ *label = 0;
+ }
+}
+
+
+void pvalLabelSetName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalLabelSetName", PV_LABEL))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalLabelGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalLabelGetName", PV_LABEL))
+ return 0;
+ return p->u1.str;
+}
+
+
+void pvalForSetInit( pval *p, char *init )
+{
+ if (!pvalCheckType(p, "pvalForSetInit", PV_FOR))
+ return;
+ p->u1.for_init = init;
+}
+
+void pvalForSetTest( pval *p, char *test )
+{
+ if (!pvalCheckType(p, "pvalForSetTest", PV_FOR))
+ return;
+ p->u2.for_test = test;
+}
+
+void pvalForSetInc( pval *p, char *inc )
+{
+ if (!pvalCheckType(p, "pvalForSetInc", PV_FOR))
+ return;
+ p->u3.for_inc = inc;
+}
+
+void pvalForSetStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalForSetStatement", PV_FOR))
+ return;
+ p->u4.for_statements = statement;
+}
+
+char* pvalForGetInit( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetInit", PV_FOR))
+ return 0;
+ return p->u1.for_init;
+}
+
+char* pvalForGetTest( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetTest", PV_FOR))
+ return 0;
+ return p->u2.for_test;
+}
+
+char* pvalForGetInc( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetInc", PV_FOR))
+ return 0;
+ return p->u3.for_inc;
+}
+
+pval* pvalForGetStatement( pval *p )
+{
+ if (!pvalCheckType(p, "pvalForGetStatement", PV_FOR))
+ return 0;
+ return p->u4.for_statements;
+}
+
+
+
+void pvalIfSetCondition( pval *p, char *expr )
+{
+ if (!pvalCheckType(p, "pvalIfSetCondition", PV_IF))
+ return;
+ p->u1.str = expr;
+}
+
+char* pvalIfGetCondition( pval *p )
+{
+ if (!pvalCheckType(p, "pvalIfGetCondition", PV_IFTIME))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalIfTimeSetCondition( pval *p, char *hour_range, char *dow_range, char *dom_range, char *mon_range ) /* time range format: 24-hour format begin-end|dow range|dom range|month range */
+{
+ pval *hr = pvalCreateNode(PV_WORD);
+ pval *dow = pvalCreateNode(PV_WORD);
+ pval *dom = pvalCreateNode(PV_WORD);
+ pval *mon = pvalCreateNode(PV_WORD);
+ if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME))
+ return;
+ pvalWordSetString(hr, hour_range);
+ pvalWordSetString(dow, dow_range);
+ pvalWordSetString(dom, dom_range);
+ pvalWordSetString(mon, mon_range);
+ dom->next = mon;
+ dow->next = dom;
+ hr->next = dow;
+ p->u1.list = hr;
+}
+
+ /* is this right??? come back and correct it */
+void pvalIfTimeGetCondition( pval *p, char **hour_range, char **dow_range, char **dom_range, char **month_range )
+{
+ if (!pvalCheckType(p, "pvalIfTimeGetCondition", PV_IFTIME))
+ return;
+ *hour_range = p->u1.list->u1.str;
+ *dow_range = p->u1.list->next->u1.str;
+ *dom_range = p->u1.list->next->next->u1.str;
+ *month_range = p->u1.list->next->next->next->u1.str;
+}
+
+void pvalRandomSetCondition( pval *p, char *percent )
+{
+ if (!pvalCheckType(p, "pvalRandomSetCondition", PV_RANDOM))
+ return;
+ p->u1.str = percent;
+}
+
+char* pvalRandomGetCondition( pval *p )
+{
+ if (!pvalCheckType(p, "pvalRandomGetCondition", PV_RANDOM))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalConditionalSetThenStatement( pval *p, pval *statement )
+{
+ p->u2.statements = statement;
+}
+
+void pvalConditionalSetElseStatement( pval *p, pval *statement )
+{
+ p->u3.else_statements = statement;
+}
+
+pval* pvalConditionalGetThenStatement( pval *p )
+{
+ return p->u2.statements;
+}
+
+pval* pvalConditionalGetElseStatement( pval *p )
+{
+ return p->u3.else_statements;
+}
+
+void pvalSwitchSetTestexpr( pval *p, char *expr )
+{
+ if (!pvalCheckType(p, "pvalSwitchSetTestexpr", PV_SWITCH))
+ return;
+ p->u1.str = expr;
+}
+
+char* pvalSwitchGetTestexpr( pval *p )
+{
+ if (!pvalCheckType(p, "pvalSwitchGetTestexpr", PV_SWITCH))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalSwitchAddCase( pval *p, pval *Case )
+{
+ if (!pvalCheckType(p, "pvalSwitchAddCase", PV_SWITCH))
+ return;
+ if (!pvalCheckType(Case, "pvalSwitchAddCase", PV_CASE))
+ return;
+ if (!p->u2.statements)
+ p->u2.statements = Case;
+ else
+ linku1(p->u2.statements, Case);
+}
+
+pval* pvalSwitchWalkCases( pval *p, pval **next_case )
+{
+ if (!pvalCheckType(p, "pvalSwitchWalkCases", PV_SWITCH))
+ return 0;
+ if (!(*next_case))
+ *next_case = p->u2.statements;
+ else {
+ *next_case = (*next_case)->next;
+ }
+ return *next_case;
+}
+
+
+void pvalExtenSetName( pval *p, char *name )
+{
+ if (!pvalCheckType(p, "pvalExtenSetName", PV_EXTENSION))
+ return;
+ p->u1.str = name;
+}
+
+char* pvalExtenGetName( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetName", PV_EXTENSION))
+ return 0;
+ return p->u1.str;
+}
+
+void pvalExtenSetRegexten( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenSetRegexten", PV_EXTENSION))
+ return;
+ p->u4.regexten = 1;
+}
+
+void pvalExtenUnSetRegexten( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenUnSetRegexten", PV_EXTENSION))
+ return;
+ p->u4.regexten = 0;
+}
+
+int pvalExtenGetRegexten( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetRegexten", PV_EXTENSION))
+ return 0;
+ return p->u4.regexten;
+}
+
+void pvalExtenSetHints( pval *p, char *hints )
+{
+ if (!pvalCheckType(p, "pvalExtenSetHints", PV_EXTENSION))
+ return;
+ p->u3.hints = hints;
+}
+
+char* pvalExtenGetHints( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetHints", PV_EXTENSION))
+ return 0;
+ return p->u3.hints;
+}
+
+void pvalExtenSetStatement( pval *p, pval *statement )
+{
+ if (!pvalCheckType(p, "pvalExtenSetStatement", PV_EXTENSION))
+ return;
+ p->u2.statements = statement;
+}
+
+pval* pvalExtenGetStatement( pval *p )
+{
+ if (!pvalCheckType(p, "pvalExtenGetStatement", PV_EXTENSION))
+ return 0;
+ return p->u2.statements;
+}
+
+
+void pvalIgnorePatSetPattern( pval *p, char *pat )
+{
+ if (!pvalCheckType(p, "pvalIgnorePatSetPattern", PV_IGNOREPAT))
+ return;
+ p->u1.str = pat;
+}
+
+char* pvalIgnorePatGetPattern( pval *p )
+{
+ if (!pvalCheckType(p, "pvalIgnorePatGetPattern", PV_IGNOREPAT))
+ return 0;
+ return p->u1.str;
+}
+
+
+void pvalGlobalsAddStatement( pval *p, pval *statement )
+{
+ if (p->type != PV_GLOBALS) {
+ ast_log(LOG_ERROR, "pvalGlobalsAddStatement called where first arg is not a Globals!\n");
+ } else {
+ if (!p->u1.statements) {
+ p->u1.statements = statement;
+ } else {
+ p->u1.statements = linku1(p->u1.statements,statement);
+ }
+ }
+}
+
+pval* pvalGlobalsWalkStatements( pval *p, pval **next_statement )
+{
+ if (!pvalCheckType(p, "pvalGlobalsWalkStatements", PV_GLOBALS))
+ return 0;
+ if (!next_statement) {
+ *next_statement = p;
+ return p;
+ } else {
+ *next_statement = (*next_statement)->next;
+ return (*next_statement)->next;
+ }
+}
+
+
+void pvalTopLevAddObject( pval *p, pval *contextOrObj )
+{
+ if (p) {
+ linku1(p,contextOrObj);
+ } else {
+ ast_log(LOG_ERROR, "First arg to pvalTopLevel is NULL!\n");
+ }
+}
+
+pval *pvalTopLevWalkObjects(pval *p, pval **next_obj )
+{
+ if (!next_obj) {
+ *next_obj = p;
+ return p;
+ } else {
+ *next_obj = (*next_obj)->next;
+ return (*next_obj)->next;
+ }
+}
+
+/* append second element to the list in the first one via next pointers */
+pval * linku1(pval *head, pval *tail)
+{
+ if (!head)
+ return tail;
+ if (tail) {
+ if (!head->next) {
+ head->next = tail;
+ } else {
+ head->u1_last->next = tail;
+ }
+ head->u1_last = tail;
+ tail->prev = head; /* the dad link only points to containers */
+ }
+ return head;
+}
+
+#ifdef HERE_BY_MISTAKE_I_THINK
+static char *config = "extensions.ael";
+int do_pbx_load_module(void)
+{
+ int errs, sem_err, sem_warn, sem_note;
+ char *rfilename;
+ struct ast_context *local_contexts=NULL, *con;
+ struct pval *parse_tree;
+
+ ast_log(LOG_NOTICE, "Starting AEL load process.\n");
+ if (config[0] == '/')
+ rfilename = (char *)config;
+ else {
+ rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
+ sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
+ }
+ ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename);
+
+ if (access(rfilename,R_OK) != 0) {
+ ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ parse_tree = ael2_parse(rfilename, &errs);
+ ast_log(LOG_DEBUG, "AEL load process: parsed config file name '%s'.\n", rfilename);
+ ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
+ if (errs == 0 && sem_err == 0) {
+ ast_log(LOG_DEBUG, "AEL load process: checked config file name '%s'.\n", rfilename);
+ ast_compile_ael2(&local_contexts, parse_tree);
+ ast_log(LOG_DEBUG, "AEL load process: compiled config file name '%s'.\n", rfilename);
+
+ ast_merge_contexts_and_delete(&local_contexts, registrar);
+ ast_log(LOG_DEBUG, "AEL load process: merged config file name '%s'.\n", rfilename);
+ for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
+ ast_context_verify_includes(con);
+ ast_log(LOG_DEBUG, "AEL load process: verified config file name '%s'.\n", rfilename);
+ } else {
+ ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err);
+ destroy_pval(parse_tree); /* free up the memory */
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ destroy_pval(parse_tree); /* free up the memory */
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+#endif
+
diff --git a/trunk/res/res_adsi.c b/trunk/res/res_adsi.c
new file mode 100644
index 000000000..2df5324aa
--- /dev/null
+++ b/trunk/res/res_adsi.c
@@ -0,0 +1,1120 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ * 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 ADSI support
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note this module is required by app_voicemail and app_getcpeid
+ * \todo Move app_getcpeid into this module
+ * \todo Create a core layer so that app_voicemail does not require
+ * res_adsi to load
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+#include <math.h>
+
+#include "asterisk/adsi.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/callerid.h"
+#include "asterisk/fskmodem.h"
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/file.h"
+
+#define DEFAULT_ADSI_MAX_RETRIES 3
+
+#define ADSI_MAX_INTRO 20
+#define ADSI_MAX_SPEED_DIAL 6
+
+#define ADSI_FLAG_DATAMODE (1 << 8)
+
+static int maxretries = DEFAULT_ADSI_MAX_RETRIES;
+
+/* Asterisk ADSI button definitions */
+#define ADSI_SPEED_DIAL 10 /* 10-15 are reserved for speed dial */
+
+static char intro[ADSI_MAX_INTRO][20];
+static int aligns[ADSI_MAX_INTRO];
+
+#define SPEEDDIAL_MAX_LEN 20
+static char speeddial[ADSI_MAX_SPEED_DIAL][3][SPEEDDIAL_MAX_LEN];
+
+static int alignment = 0;
+
+static int adsi_generate(unsigned char *buf, int msgtype, unsigned char *msg, int msglen, int msgnum, int last, int codec)
+{
+ int sum, x, bytes = 0;
+ /* Initial carrier (imaginary) */
+ float cr = 1.0, ci = 0.0, scont = 0.0;
+
+ if (msglen > 255)
+ msglen = 255;
+
+ /* If first message, Send 150ms of MARK's */
+ if (msgnum == 1) {
+ for (x = 0; x < 150; x++) /* was 150 */
+ PUT_CLID_MARKMS;
+ }
+
+ /* Put message type */
+ PUT_CLID(msgtype);
+ sum = msgtype;
+
+ /* Put message length (plus one for the message number) */
+ PUT_CLID(msglen + 1);
+ sum += msglen + 1;
+
+ /* Put message number */
+ PUT_CLID(msgnum);
+ sum += msgnum;
+
+ /* Put actual message */
+ for (x = 0; x < msglen; x++) {
+ PUT_CLID(msg[x]);
+ sum += msg[x];
+ }
+
+ /* Put 2's compliment of sum */
+ PUT_CLID(256-(sum & 0xff));
+
+#if 0
+ if (last) {
+ /* Put trailing marks */
+ for (x = 0; x < 50; x++)
+ PUT_CLID_MARKMS;
+ }
+#endif
+ return bytes;
+
+}
+
+static int adsi_careful_send(struct ast_channel *chan, unsigned char *buf, int len, int *remainder)
+{
+ /* Sends carefully on a full duplex channel by using reading for
+ timing */
+ struct ast_frame *inf, outf;
+ int amt;
+
+ /* Zero out our outgoing frame */
+ memset(&outf, 0, sizeof(outf));
+
+ if (remainder && *remainder) {
+ amt = len;
+
+ /* Send remainder if provided */
+ if (amt > *remainder)
+ amt = *remainder;
+ else
+ *remainder = *remainder - amt;
+ outf.frametype = AST_FRAME_VOICE;
+ outf.subclass = AST_FORMAT_ULAW;
+ outf.data = buf;
+ outf.datalen = amt;
+ outf.samples = amt;
+ if (ast_write(chan, &outf)) {
+ ast_log(LOG_WARNING, "Failed to carefully write frame\n");
+ return -1;
+ }
+ /* Update pointers and lengths */
+ buf += amt;
+ len -= amt;
+ }
+
+ while(len) {
+ amt = len;
+ /* If we don't get anything at all back in a second, forget
+ about it */
+ if (ast_waitfor(chan, 1000) < 1)
+ return -1;
+ /* Detect hangup */
+ if (!(inf = ast_read(chan)))
+ return -1;
+
+ /* Drop any frames that are not voice */
+ if (inf->frametype != AST_FRAME_VOICE) {
+ ast_frfree(inf);
+ continue;
+ }
+
+ if (inf->subclass != AST_FORMAT_ULAW) {
+ ast_log(LOG_WARNING, "Channel not in ulaw?\n");
+ ast_frfree(inf);
+ return -1;
+ }
+ /* Send no more than they sent us */
+ if (amt > inf->datalen)
+ amt = inf->datalen;
+ else if (remainder)
+ *remainder = inf->datalen - amt;
+ outf.frametype = AST_FRAME_VOICE;
+ outf.subclass = AST_FORMAT_ULAW;
+ outf.data = buf;
+ outf.datalen = amt;
+ outf.samples = amt;
+ if (ast_write(chan, &outf)) {
+ ast_log(LOG_WARNING, "Failed to carefully write frame\n");
+ ast_frfree(inf);
+ return -1;
+ }
+ /* Update pointers and lengths */
+ buf += amt;
+ len -= amt;
+ ast_frfree(inf);
+ }
+ return 0;
+}
+
+static int __adsi_transmit_messages(struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype)
+{
+ /* msglen must be no more than 256 bits, each */
+ unsigned char buf[24000 * 5];
+ int pos = 0, res, x, start = 0, retries = 0, waittime, rem = 0, def;
+ char ack[3];
+ struct ast_frame *f;
+
+ if (chan->adsicpe == AST_ADSI_UNAVAILABLE) {
+ /* Don't bother if we know they don't support ADSI */
+ errno = ENOSYS;
+ return -1;
+ }
+
+ while(retries < maxretries) {
+ if (!(chan->adsicpe & ADSI_FLAG_DATAMODE)) {
+ /* Generate CAS (no SAS) */
+ ast_gen_cas(buf, 0, 680, AST_FORMAT_ULAW);
+
+ /* Send CAS */
+ if (adsi_careful_send(chan, buf, 680, NULL))
+ ast_log(LOG_WARNING, "Unable to send CAS\n");
+
+ /* Wait For DTMF result */
+ waittime = 500;
+ for(;;) {
+ if (((res = ast_waitfor(chan, waittime)) < 1)) {
+ /* Didn't get back DTMF A in time */
+ ast_debug(1, "No ADSI CPE detected (%d)\n", res);
+ if (!chan->adsicpe)
+ chan->adsicpe = AST_ADSI_UNAVAILABLE;
+ errno = ENOSYS;
+ return -1;
+ }
+ waittime = res;
+ if (!(f = ast_read(chan))) {
+ ast_debug(1, "Hangup in ADSI\n");
+ return -1;
+ }
+ if (f->frametype == AST_FRAME_DTMF) {
+ if (f->subclass == 'A') {
+ /* Okay, this is an ADSI CPE. Note this for future reference, too */
+ if (!chan->adsicpe)
+ chan->adsicpe = AST_ADSI_AVAILABLE;
+ break;
+ } else {
+ if (f->subclass == 'D')
+ ast_debug(1, "Off-hook capable CPE only, not ADSI\n");
+ else
+ ast_log(LOG_WARNING, "Unknown ADSI response '%c'\n", f->subclass);
+ if (!chan->adsicpe)
+ chan->adsicpe = AST_ADSI_UNAVAILABLE;
+ errno = ENOSYS;
+ ast_frfree(f);
+ return -1;
+ }
+ }
+ ast_frfree(f);
+ }
+
+ ast_debug(1, "ADSI Compatible CPE Detected\n");
+ } else {
+ ast_debug(1, "Already in data mode\n");
+ }
+
+ x = 0;
+ pos = 0;
+#if 1
+ def= ast_channel_defer_dtmf(chan);
+#endif
+ while ((x < 6) && msg[x]) {
+ if ((res = adsi_generate(buf + pos, msgtype[x], msg[x], msglen[x], x+1 - start, (x == 5) || !msg[x+1], AST_FORMAT_ULAW)) < 0) {
+ ast_log(LOG_WARNING, "Failed to generate ADSI message %d on channel %s\n", x + 1, chan->name);
+ return -1;
+ }
+ ast_debug(1, "Message %d, of %d input bytes, %d output bytes\n", x + 1, msglen[x], res);
+ pos += res;
+ x++;
+ }
+
+
+ rem = 0;
+ res = adsi_careful_send(chan, buf, pos, &rem);
+ if (!def)
+ ast_channel_undefer_dtmf(chan);
+ if (res)
+ return -1;
+
+ ast_debug(1, "Sent total spill of %d bytes\n", pos);
+
+ memset(ack, 0, sizeof(ack));
+ /* Get real result and check for hangup */
+ if ((res = ast_readstring(chan, ack, 2, 1000, 1000, "")) < 0)
+ return -1;
+ if (ack[0] == 'D') {
+ ast_debug(1, "Acked up to message %d\n", atoi(ack + 1)); start += atoi(ack + 1);
+ if (start >= x)
+ break;
+ else {
+ retries++;
+ ast_debug(1, "Retransmitting (%d), from %d\n", retries, start + 1);
+ }
+ } else {
+ retries++;
+ ast_log(LOG_WARNING, "Unexpected response to ack: %s (retry %d)\n", ack, retries);
+ }
+ }
+ if (retries >= maxretries) {
+ ast_log(LOG_WARNING, "Maximum ADSI Retries (%d) exceeded\n", maxretries);
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ return 0;
+
+}
+
+static int _ast_adsi_begin_download(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
+{
+ int bytes = 0;
+ unsigned char buf[256];
+ char ack[2];
+
+ /* Setup the resident soft key stuff, a piece at a time */
+ /* Upload what scripts we can for voicemail ahead of time */
+ bytes += ast_adsi_download_connect(buf + bytes, service, fdn, sec, version);
+ if (ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DOWNLOAD, 0))
+ return -1;
+ if (ast_readstring(chan, ack, 1, 10000, 10000, ""))
+ return -1;
+ if (ack[0] == 'B')
+ return 0;
+ ast_debug(1, "Download was denied by CPE\n");
+ return -1;
+}
+
+static int _ast_adsi_end_download(struct ast_channel *chan)
+{
+ int bytes = 0;
+ unsigned char buf[256];
+
+ /* Setup the resident soft key stuff, a piece at a time */
+ /* Upload what scripts we can for voicemail ahead of time */
+ bytes += ast_adsi_download_disconnect(buf + bytes);
+ if (ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DOWNLOAD, 0))
+ return -1;
+ return 0;
+}
+
+static int _ast_adsi_transmit_message_full(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype, int dowait)
+{
+ unsigned char *msgs[5] = { NULL, NULL, NULL, NULL, NULL };
+ int msglens[5], msgtypes[5], newdatamode = (chan->adsicpe & ADSI_FLAG_DATAMODE), res, x, writeformat = chan->writeformat, readformat = chan->readformat, waitforswitch = 0;
+
+ for (x = 0; x < msglen; x += (msg[x+1]+2)) {
+ if (msg[x] == ADSI_SWITCH_TO_DATA) {
+ ast_debug(1, "Switch to data is sent!\n");
+ waitforswitch++;
+ newdatamode = ADSI_FLAG_DATAMODE;
+ }
+
+ if (msg[x] == ADSI_SWITCH_TO_VOICE) {
+ ast_debug(1, "Switch to voice is sent!\n");
+ waitforswitch++;
+ newdatamode = 0;
+ }
+ }
+ msgs[0] = msg;
+
+ msglens[0] = msglen;
+ msgtypes[0] = msgtype;
+
+ if (msglen > 253) {
+ ast_log(LOG_WARNING, "Can't send ADSI message of %d bytes, too large\n", msglen);
+ return -1;
+ }
+
+ ast_stopstream(chan);
+
+ if (ast_set_write_format(chan, AST_FORMAT_ULAW)) {
+ ast_log(LOG_WARNING, "Unable to set write format to ULAW\n");
+ return -1;
+ }
+
+ if (ast_set_read_format(chan, AST_FORMAT_ULAW)) {
+ ast_log(LOG_WARNING, "Unable to set read format to ULAW\n");
+ if (writeformat) {
+ if (ast_set_write_format(chan, writeformat))
+ ast_log(LOG_WARNING, "Unable to restore write format to %d\n", writeformat);
+ }
+ return -1;
+ }
+ res = __adsi_transmit_messages(chan, msgs, msglens, msgtypes);
+
+ if (dowait) {
+ ast_debug(1, "Wait for switch is '%d'\n", waitforswitch);
+ while (waitforswitch-- && ((res = ast_waitfordigit(chan, 1000)) > 0)) {
+ res = 0;
+ ast_debug(1, "Waiting for 'B'...\n");
+ }
+ }
+
+ if (!res)
+ chan->adsicpe = (chan->adsicpe & ~ADSI_FLAG_DATAMODE) | newdatamode;
+
+ if (writeformat)
+ ast_set_write_format(chan, writeformat);
+ if (readformat)
+ ast_set_read_format(chan, readformat);
+
+ if (!res)
+ res = ast_safe_sleep(chan, 100 );
+ return res;
+}
+
+static int _ast_adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
+{
+ return ast_adsi_transmit_message_full(chan, msg, msglen, msgtype, 1);
+}
+
+static inline int ccopy(unsigned char *dst, const unsigned char *src, int max)
+{
+ int x = 0;
+ /* Carefully copy the requested data */
+ while ((x < max) && src[x] && (src[x] != 0xff)) {
+ dst[x] = src[x];
+ x++;
+ }
+ return x;
+}
+
+static int _ast_adsi_load_soft_key(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
+{
+ int bytes = 0;
+
+ /* Abort if invalid key specified */
+ if ((key < 2) || (key > 33))
+ return -1;
+
+ buf[bytes++] = ADSI_LOAD_SOFTKEY;
+ /* Reserve for length */
+ bytes++;
+ /* Which key */
+ buf[bytes++] = key;
+
+ /* Carefully copy long label */
+ bytes += ccopy(buf + bytes, (const unsigned char *)llabel, 18);
+
+ /* Place delimiter */
+ buf[bytes++] = 0xff;
+
+ /* Short label */
+ bytes += ccopy(buf + bytes, (const unsigned char *)slabel, 7);
+
+
+ /* If specified, copy return string */
+ if (ret) {
+ /* Place delimiter */
+ buf[bytes++] = 0xff;
+ if (data)
+ buf[bytes++] = ADSI_SWITCH_TO_DATA2;
+ /* Carefully copy return string */
+ bytes += ccopy(buf + bytes, (const unsigned char *)ret, 20);
+
+ }
+ /* Replace parameter length */
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_connect_session(unsigned char *buf, unsigned char *fdn, int ver)
+{
+ int bytes = 0, x;
+
+ /* Message type */
+ buf[bytes++] = ADSI_CONNECT_SESSION;
+
+ /* Reserve space for length */
+ bytes++;
+
+ if (fdn) {
+ for (x = 0; x < 4; x++)
+ buf[bytes++] = fdn[x];
+ if (ver > -1)
+ buf[bytes++] = ver & 0xff;
+ }
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_download_connect(unsigned char *buf, char *service, unsigned char *fdn, unsigned char *sec, int ver)
+{
+ int bytes = 0, x;
+
+ /* Message type */
+ buf[bytes++] = ADSI_DOWNLOAD_CONNECT;
+
+ /* Reserve space for length */
+ bytes++;
+
+ /* Primary column */
+ bytes+= ccopy(buf + bytes, (unsigned char *)service, 18);
+
+ /* Delimiter */
+ buf[bytes++] = 0xff;
+
+ for (x = 0; x < 4; x++)
+ buf[bytes++] = fdn[x];
+
+ for (x = 0; x < 4; x++)
+ buf[bytes++] = sec[x];
+
+ buf[bytes++] = ver & 0xff;
+
+ buf[1] = bytes - 2;
+
+ return bytes;
+
+}
+
+static int _ast_adsi_disconnect_session(unsigned char *buf)
+{
+ int bytes = 0;
+
+ /* Message type */
+ buf[bytes++] = ADSI_DISC_SESSION;
+
+ /* Reserve space for length */
+ bytes++;
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_query_cpeid(unsigned char *buf)
+{
+ int bytes = 0;
+ buf[bytes++] = ADSI_QUERY_CPEID;
+ /* Reserve space for length */
+ bytes++;
+ buf[1] = bytes - 2;
+ return bytes;
+}
+
+static int _ast_adsi_query_cpeinfo(unsigned char *buf)
+{
+ int bytes = 0;
+ buf[bytes++] = ADSI_QUERY_CONFIG;
+ /* Reserve space for length */
+ bytes++;
+ buf[1] = bytes - 2;
+ return bytes;
+}
+
+static int _ast_adsi_read_encoded_dtmf(struct ast_channel *chan, unsigned char *buf, int maxlen)
+{
+ int bytes = 0, res, gotstar = 0, pos = 0;
+ unsigned char current = 0;
+
+ memset(buf, 0, sizeof(buf));
+
+ while(bytes <= maxlen) {
+ /* Wait up to a second for a digit */
+ if (!(res = ast_waitfordigit(chan, 1000)))
+ break;
+ if (res == '*') {
+ gotstar = 1;
+ continue;
+ }
+ /* Ignore anything other than a digit */
+ if ((res < '0') || (res > '9'))
+ continue;
+ res -= '0';
+ if (gotstar)
+ res += 9;
+ if (pos) {
+ pos = 0;
+ buf[bytes++] = (res << 4) | current;
+ } else {
+ pos = 1;
+ current = res;
+ }
+ gotstar = 0;
+ }
+
+ return bytes;
+}
+
+static int _ast_adsi_get_cpeid(struct ast_channel *chan, unsigned char *cpeid, int voice)
+{
+ unsigned char buf[256] = "";
+ int bytes = 0, res;
+
+ bytes += ast_adsi_data_mode(buf);
+ ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
+
+ bytes = 0;
+ bytes += ast_adsi_query_cpeid(buf);
+ ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
+
+ /* Get response */
+ res = ast_adsi_read_encoded_dtmf(chan, cpeid, 4);
+ if (res != 4) {
+ ast_log(LOG_WARNING, "Got %d bytes back of encoded DTMF, expecting 4\n", res);
+ res = 0;
+ } else {
+ res = 1;
+ }
+
+ if (voice) {
+ bytes = 0;
+ bytes += ast_adsi_voice_mode(buf, 0);
+ ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
+ /* Ignore the resulting DTMF B announcing it's in voice mode */
+ ast_waitfordigit(chan, 1000);
+ }
+ return res;
+}
+
+static int _ast_adsi_get_cpeinfo(struct ast_channel *chan, int *width, int *height, int *buttons, int voice)
+{
+ unsigned char buf[256] = "";
+ int bytes = 0, res;
+
+ bytes += ast_adsi_data_mode(buf);
+ ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
+
+ bytes = 0;
+ bytes += ast_adsi_query_cpeinfo(buf);
+ ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
+
+ /* Get width */
+ if ((res = ast_readstring(chan, (char *)buf, 2, 1000, 500, "")) < 0)
+ return res;
+ if (strlen((char *)buf) != 2) {
+ ast_log(LOG_WARNING, "Got %d bytes of width, expecting 2\n", res);
+ res = 0;
+ } else {
+ res = 1;
+ }
+ if (width)
+ *width = atoi((char *)buf);
+ /* Get height */
+ memset(buf, 0, sizeof(buf));
+ if (res) {
+ if ((res = ast_readstring(chan, (char *)buf, 2, 1000, 500, "")) < 0)
+ return res;
+ if (strlen((char *)buf) != 2) {
+ ast_log(LOG_WARNING, "Got %d bytes of height, expecting 2\n", res);
+ res = 0;
+ } else {
+ res = 1;
+ }
+ if (height)
+ *height= atoi((char *)buf);
+ }
+ /* Get buttons */
+ memset(buf, 0, sizeof(buf));
+ if (res) {
+ if ((res = ast_readstring(chan, (char *)buf, 1, 1000, 500, "")) < 0)
+ return res;
+ if (strlen((char *)buf) != 1) {
+ ast_log(LOG_WARNING, "Got %d bytes of buttons, expecting 1\n", res);
+ res = 0;
+ } else {
+ res = 1;
+ }
+ if (buttons)
+ *buttons = atoi((char *)buf);
+ }
+ if (voice) {
+ bytes = 0;
+ bytes += ast_adsi_voice_mode(buf, 0);
+ ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
+ /* Ignore the resulting DTMF B announcing it's in voice mode */
+ ast_waitfordigit(chan, 1000);
+ }
+ return res;
+}
+
+static int _ast_adsi_data_mode(unsigned char *buf)
+{
+ int bytes = 0;
+
+ /* Message type */
+ buf[bytes++] = ADSI_SWITCH_TO_DATA;
+
+ /* Reserve space for length */
+ bytes++;
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_clear_soft_keys(unsigned char *buf)
+{
+ int bytes = 0;
+
+ /* Message type */
+ buf[bytes++] = ADSI_CLEAR_SOFTKEY;
+
+ /* Reserve space for length */
+ bytes++;
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_clear_screen(unsigned char *buf)
+{
+ int bytes = 0;
+
+ /* Message type */
+ buf[bytes++] = ADSI_CLEAR_SCREEN;
+
+ /* Reserve space for length */
+ bytes++;
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_voice_mode(unsigned char *buf, int when)
+{
+ int bytes = 0;
+
+ /* Message type */
+ buf[bytes++] = ADSI_SWITCH_TO_VOICE;
+
+ /* Reserve space for length */
+ bytes++;
+
+ buf[bytes++] = when & 0x7f;
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_available(struct ast_channel *chan)
+{
+ int cpe = chan->adsicpe & 0xff;
+ if ((cpe == AST_ADSI_AVAILABLE) ||
+ (cpe == AST_ADSI_UNKNOWN))
+ return 1;
+ return 0;
+}
+
+static int _ast_adsi_download_disconnect(unsigned char *buf)
+{
+ int bytes = 0;
+
+ /* Message type */
+ buf[bytes++] = ADSI_DOWNLOAD_DISC;
+
+ /* Reserve space for length */
+ bytes++;
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_display(unsigned char *buf, int page, int line, int just, int wrap,
+ char *col1, char *col2)
+{
+ int bytes = 0;
+
+ /* Sanity check line number */
+
+ if (page) {
+ if (line > 4) return -1;
+ } else {
+ if (line > 33) return -1;
+ }
+
+ if (line < 1)
+ return -1;
+ /* Parameter type */
+ buf[bytes++] = ADSI_LOAD_VIRTUAL_DISP;
+
+ /* Reserve space for size */
+ bytes++;
+
+ /* Page and wrap indicator */
+ buf[bytes++] = ((page & 0x1) << 7) | ((wrap & 0x1) << 6) | (line & 0x3f);
+
+ /* Justification */
+ buf[bytes++] = (just & 0x3) << 5;
+
+ /* Omit highlight mode definition */
+ buf[bytes++] = 0xff;
+
+ /* Primary column */
+ bytes+= ccopy(buf + bytes, (unsigned char *)col1, 20);
+
+ /* Delimiter */
+ buf[bytes++] = 0xff;
+
+ /* Secondary column */
+ bytes += ccopy(buf + bytes, (unsigned char *)col2, 20);
+
+ /* Update length */
+ buf[1] = bytes - 2;
+
+ return bytes;
+
+}
+
+static int _ast_adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
+{
+ int bytes = 0;
+
+ if (page) {
+ if (line > 4) return -1;
+ } else {
+ if (line > 33) return -1;
+ }
+
+ if (line < 1)
+ return -1;
+
+ buf[bytes++] = ADSI_INPUT_CONTROL;
+ bytes++;
+ buf[bytes++] = ((page & 1) << 7) | (line & 0x3f);
+ buf[bytes++] = ((display & 1) << 7) | ((just & 0x3) << 4) | (format & 0x7);
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+}
+
+static int _ast_adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
+{
+ int bytes = 0;
+
+ if (!strlen((char *)format1))
+ return -1;
+
+ buf[bytes++] = ADSI_INPUT_FORMAT;
+ bytes++;
+ buf[bytes++] = ((dir & 1) << 7) | ((wrap & 1) << 6) | (num & 0x7);
+ bytes += ccopy(buf + bytes, (unsigned char *)format1, 20);
+ buf[bytes++] = 0xff;
+ if (format2 && strlen((char *)format2)) {
+ bytes += ccopy(buf + bytes, (unsigned char *)format2, 20);
+ }
+ buf[1] = bytes - 2;
+ return bytes;
+}
+
+static int _ast_adsi_set_keys(unsigned char *buf, unsigned char *keys)
+{
+ int bytes = 0, x;
+
+ /* Message type */
+ buf[bytes++] = ADSI_INIT_SOFTKEY_LINE;
+ /* Space for size */
+ bytes++;
+ /* Key definitions */
+ for (x = 0; x < 6; x++)
+ buf[bytes++] = (keys[x] & 0x3f) ? keys[x] : (keys[x] | 0x1);
+ buf[1] = bytes - 2;
+ return bytes;
+}
+
+static int _ast_adsi_set_line(unsigned char *buf, int page, int line)
+{
+ int bytes = 0;
+
+ /* Sanity check line number */
+
+ if (page) {
+ if (line > 4) return -1;
+ } else {
+ if (line > 33) return -1;
+ }
+
+ if (line < 1)
+ return -1;
+ /* Parameter type */
+ buf[bytes++] = ADSI_LINE_CONTROL;
+
+ /* Reserve space for size */
+ bytes++;
+
+ /* Page and line */
+ buf[bytes++] = ((page & 0x1) << 7) | (line & 0x3f);
+
+ buf[1] = bytes - 2;
+ return bytes;
+
+};
+
+static int total = 0;
+static int speeds = 0;
+
+static int _ast_adsi_channel_restore(struct ast_channel *chan)
+{
+ unsigned char dsp[256] = "", keyd[6] = "";
+ int bytes, x;
+
+ /* Start with initial display setup */
+ bytes = 0;
+ bytes += ast_adsi_set_line(dsp + bytes, ADSI_INFO_PAGE, 1);
+
+ /* Prepare key setup messages */
+
+ if (speeds) {
+ for (x = 0; x < speeds; x++)
+ keyd[x] = ADSI_SPEED_DIAL + x;
+ bytes += ast_adsi_set_keys(dsp + bytes, keyd);
+ }
+ ast_adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0);
+ return 0;
+
+}
+
+static int _ast_adsi_print(struct ast_channel *chan, char **lines, int *aligns, int voice)
+{
+ unsigned char buf[4096];
+ int bytes = 0, res, x;
+
+ for(x = 0; lines[x]; x++)
+ bytes += ast_adsi_display(buf + bytes, ADSI_INFO_PAGE, x+1, aligns[x], 0, lines[x], "");
+ bytes += ast_adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
+ if (voice)
+ bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ res = ast_adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
+ if (voice)
+ /* Ignore the resulting DTMF B announcing it's in voice mode */
+ ast_waitfordigit(chan, 1000);
+ return res;
+}
+
+static int _ast_adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
+{
+ unsigned char dsp[256] = "";
+ int bytes = 0, res;
+ char resp[2];
+
+ /* Connect to session */
+ bytes += ast_adsi_connect_session(dsp + bytes, app, ver);
+
+ if (data)
+ bytes += ast_adsi_data_mode(dsp + bytes);
+
+ /* Prepare key setup messages */
+ if (ast_adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0))
+ return -1;
+ if (app) {
+ if ((res = ast_readstring(chan, resp, 1, 1200, 1200, "")) < 0)
+ return -1;
+ if (res) {
+ ast_debug(1, "No response from CPE about version. Assuming not there.\n");
+ return 0;
+ }
+ if (!strcmp(resp, "B")) {
+ ast_debug(1, "CPE has script '%s' version %d already loaded\n", app, ver);
+ return 1;
+ } else if (!strcmp(resp, "A")) {
+ ast_debug(1, "CPE hasn't script '%s' version %d already loaded\n", app, ver);
+ } else {
+ ast_log(LOG_WARNING, "Unexpected CPE response to script query: %s\n", resp);
+ }
+ } else
+ return 1;
+ return 0;
+
+}
+
+static int _ast_adsi_unload_session(struct ast_channel *chan)
+{
+ unsigned char dsp[256] = "";
+ int bytes = 0;
+
+ /* Connect to session */
+ bytes += ast_adsi_disconnect_session(dsp + bytes);
+ bytes += ast_adsi_voice_mode(dsp + bytes, 0);
+
+ /* Prepare key setup messages */
+ if (ast_adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0))
+ return -1;
+
+ return 0;
+}
+
+static int str2align(const char *s)
+{
+ if (!strncasecmp(s, "l", 1))
+ return ADSI_JUST_LEFT;
+ else if (!strncasecmp(s, "r", 1))
+ return ADSI_JUST_RIGHT;
+ else if (!strncasecmp(s, "i", 1))
+ return ADSI_JUST_IND;
+ else
+ return ADSI_JUST_CENT;
+}
+
+static void init_state(void)
+{
+ int x;
+
+ for (x = 0; x < ADSI_MAX_INTRO; x++)
+ aligns[x] = ADSI_JUST_CENT;
+ ast_copy_string(intro[0], "Welcome to the", sizeof(intro[0]));
+ ast_copy_string(intro[1], "Asterisk", sizeof(intro[1]));
+ ast_copy_string(intro[2], "Open Source PBX", sizeof(intro[2]));
+ total = 3;
+ speeds = 0;
+ for (x = 3; x < ADSI_MAX_INTRO; x++)
+ intro[x][0] = '\0';
+ memset(speeddial, 0, sizeof(speeddial));
+ alignment = ADSI_JUST_CENT;
+}
+
+static void adsi_load(int reload)
+{
+ int x = 0;
+ struct ast_config *conf = NULL;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ char *name, *sname;
+ init_state();
+
+ if (!(conf = ast_config_load("adsi.conf", config_flags)))
+ return;
+ else if (conf == CONFIG_STATUS_FILEUNCHANGED)
+ return;
+ for (v = ast_variable_browse(conf, "intro"); v; v = v->next) {
+ if (!strcasecmp(v->name, "alignment"))
+ alignment = str2align(v->value);
+ else if (!strcasecmp(v->name, "greeting")) {
+ if (x < ADSI_MAX_INTRO) {
+ aligns[x] = alignment;
+ ast_copy_string(intro[x], v->value, sizeof(intro[x]));
+ x++;
+ }
+ } else if (!strcasecmp(v->name, "maxretries")) {
+ if (atoi(v->value) > 0)
+ maxretries = atoi(v->value);
+ }
+ }
+ if (x)
+ total = x;
+
+ x = 0;
+ for (v = ast_variable_browse(conf, "speeddial"); v; v = v->next) {
+ char buf[3 * SPEEDDIAL_MAX_LEN];
+ char *stringp = buf;
+ ast_copy_string(buf, v->value, sizeof(buf));
+ name = strsep(&stringp, ",");
+ sname = strsep(&stringp, ",");
+ if (!sname)
+ sname = name;
+ if (x < ADSI_MAX_SPEED_DIAL) {
+ ast_copy_string(speeddial[x][0], v->name, sizeof(speeddial[x][0]));
+ ast_copy_string(speeddial[x][1], name, 18);
+ ast_copy_string(speeddial[x][2], sname, 7);
+ x++;
+ }
+ }
+ if (x)
+ speeds = x;
+ ast_config_destroy(conf);
+
+ return;
+}
+
+static int reload(void)
+{
+ adsi_load(1);
+ return 0;
+}
+
+static int load_module(void)
+{
+ adsi_load(0);
+
+ ast_adsi_begin_download = _ast_adsi_begin_download;
+ ast_adsi_end_download = _ast_adsi_end_download;
+ ast_adsi_channel_restore = _ast_adsi_channel_restore;
+ ast_adsi_print = _ast_adsi_print;
+ ast_adsi_load_session = _ast_adsi_load_session;
+ ast_adsi_unload_session = _ast_adsi_unload_session;
+ ast_adsi_transmit_message = _ast_adsi_transmit_message;
+ ast_adsi_transmit_message_full = _ast_adsi_transmit_message_full;
+ ast_adsi_read_encoded_dtmf = _ast_adsi_read_encoded_dtmf;
+ ast_adsi_connect_session = _ast_adsi_connect_session;
+ ast_adsi_query_cpeid = _ast_adsi_query_cpeid;
+ ast_adsi_query_cpeinfo = _ast_adsi_query_cpeinfo;
+ ast_adsi_get_cpeid = _ast_adsi_get_cpeid;
+ ast_adsi_get_cpeinfo = _ast_adsi_get_cpeinfo;
+ ast_adsi_download_connect = _ast_adsi_download_connect;
+ ast_adsi_disconnect_session = _ast_adsi_disconnect_session;
+ ast_adsi_download_disconnect = _ast_adsi_download_disconnect;
+ ast_adsi_data_mode = _ast_adsi_data_mode;
+ ast_adsi_clear_soft_keys = _ast_adsi_clear_soft_keys;
+ ast_adsi_clear_screen = _ast_adsi_clear_screen;
+ ast_adsi_voice_mode = _ast_adsi_voice_mode;
+ ast_adsi_available = _ast_adsi_available;
+ ast_adsi_display = _ast_adsi_display;
+ ast_adsi_set_line = _ast_adsi_set_line;
+ ast_adsi_load_soft_key = _ast_adsi_load_soft_key;
+ ast_adsi_set_keys = _ast_adsi_set_keys;
+ ast_adsi_input_control = _ast_adsi_input_control;
+ ast_adsi_input_format = _ast_adsi_input_format;
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ /* Can't unload this once we're loaded */
+ return -1;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ADSI Resource",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_ael_share.c b/trunk/res/res_ael_share.c
new file mode 100644
index 000000000..805dfd9a8
--- /dev/null
+++ b/trunk/res/res_ael_share.c
@@ -0,0 +1,54 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Steve Murphy <murf@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 Shareable AEL code -- mainly between internal and external modules
+ *
+ * \author Steve Murphy <murf@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+static int load_module(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "share-able code for AEL",
+ .load = load_module,
+ .unload = unload_module
+ );
diff --git a/trunk/res/res_agi.c b/trunk/res/res_agi.c
new file mode 100644
index 000000000..75692089b
--- /dev/null
+++ b/trunk/res/res_agi.c
@@ -0,0 +1,2992 @@
+/*
+ * 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 AGI - the Asterisk Gateway Interface
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include "asterisk/paths.h" /* use many ast_config_AST_*_DIR */
+#include "asterisk/network.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/astdb.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cli.h"
+#include "asterisk/image.h"
+#include "asterisk/say.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/strings.h"
+#include "asterisk/agi.h"
+#include "asterisk/manager.h"
+#include "asterisk/version.h"
+#include "asterisk/speech.h"
+#include "asterisk/manager.h"
+
+#define MAX_ARGS 128
+#define AGI_NANDFS_RETRY 3
+#define AGI_BUF_LEN 2048
+
+static char *app = "AGI";
+
+static char *eapp = "EAGI";
+
+static char *deadapp = "DeadAGI";
+
+static char *synopsis = "Executes an AGI compliant application";
+static char *esynopsis = "Executes an EAGI compliant application";
+static char *deadsynopsis = "Executes AGI on a hungup channel";
+
+static char *descrip =
+" [E|Dead]AGI(command,args): Executes an Asterisk Gateway Interface compliant\n"
+"program on a channel. AGI allows Asterisk to launch external programs\n"
+"written in any language to control a telephony channel, play audio,\n"
+"read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
+"and stdout.\n"
+" This channel will stop dialplan execution on hangup inside of this\n"
+"application, except when using DeadAGI. Otherwise, dialplan execution\n"
+"will continue normally.\n"
+" A locally executed AGI script will receive SIGHUP on hangup from the channel\n"
+"except when using DeadAGI. This can be disabled by setting the AGISIGHUP channel\n"
+"variable to \"no\" before executing the AGI application.\n"
+" Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n"
+"on file descriptor 3\n\n"
+" Use the CLI command 'agi show' to list available agi commands\n"
+" This application sets the following channel variable upon completion:\n"
+" AGISTATUS The status of the attempt to the run the AGI script\n"
+" text string, one of SUCCESS | FAILURE | NOTFOUND | HANGUP\n";
+
+static int agidebug = 0;
+
+#define TONE_BLOCK_SIZE 200
+
+/* Max time to connect to an AGI remote host */
+#define MAX_AGI_CONNECT 2000
+
+#define AGI_PORT 4573
+
+enum agi_result {
+ AGI_RESULT_SUCCESS,
+ AGI_RESULT_SUCCESS_FAST,
+ AGI_RESULT_SUCCESS_ASYNC,
+ AGI_RESULT_FAILURE,
+ AGI_RESULT_NOTFOUND,
+ AGI_RESULT_HANGUP,
+};
+
+static agi_command *find_command(char *cmds[], int exact);
+
+AST_THREADSTORAGE(agi_buf);
+#define AGI_BUF_INITSIZE 256
+
+int ast_agi_fdprintf(struct ast_channel *chan, int fd, char *fmt, ...)
+{
+ int res = 0;
+ va_list ap;
+ struct ast_str *buf;
+
+ if (!(buf = ast_str_thread_get(&agi_buf, AGI_BUF_INITSIZE)))
+ return -1;
+
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ if (res == -1) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ if (agidebug) {
+ if (chan) {
+ ast_verbose("<%s>AGI Tx >> %s", chan->name, buf->str);
+ } else {
+ ast_verbose("AGI Tx >> %s", buf->str);
+ }
+ }
+
+ return ast_carefulwrite(fd, buf->str, buf->used, 100);
+}
+
+/* linked list of AGI commands ready to be executed by Async AGI */
+struct agi_cmd {
+ char *cmd_buffer;
+ char *cmd_id;
+ AST_LIST_ENTRY(agi_cmd) entry;
+};
+
+static void free_agi_cmd(struct agi_cmd *cmd)
+{
+ ast_free(cmd->cmd_buffer);
+ ast_free(cmd->cmd_id);
+ ast_free(cmd);
+}
+
+/* AGI datastore destructor */
+static void agi_destroy_commands_cb(void *data)
+{
+ struct agi_cmd *cmd;
+ AST_LIST_HEAD(, agi_cmd) *chan_cmds = data;
+ AST_LIST_LOCK(chan_cmds);
+ while ( (cmd = AST_LIST_REMOVE_HEAD(chan_cmds, entry)) ) {
+ free_agi_cmd(cmd);
+ }
+ AST_LIST_UNLOCK(chan_cmds);
+ AST_LIST_HEAD_DESTROY(chan_cmds);
+ ast_free(chan_cmds);
+}
+
+/* channel datastore to keep the queue of AGI commands in the channel */
+static const struct ast_datastore_info agi_commands_datastore_info = {
+ .type = "AsyncAGI",
+ .destroy = agi_destroy_commands_cb
+};
+
+static const char mandescr_asyncagi[] =
+"Description: Add an AGI command to the execute queue of the channel in Async AGI\n"
+"Variables:\n"
+" *Channel: Channel that is currently in Async AGI\n"
+" *Command: Application to execute\n"
+" CommandID: comand id. This will be sent back in CommandID header of AsyncAGI exec event notification\n"
+"\n";
+
+static struct agi_cmd *get_agi_cmd(struct ast_channel *chan)
+{
+ struct ast_datastore *store;
+ struct agi_cmd *cmd;
+ AST_LIST_HEAD(, agi_cmd) *agi_commands;
+
+ ast_channel_lock(chan);
+ store = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
+ ast_channel_unlock(chan);
+ if (!store) {
+ ast_log(LOG_ERROR, "Hu? datastore disappeared at Async AGI on Channel %s!\n", chan->name);
+ return NULL;
+ }
+ agi_commands = store->data;
+ AST_LIST_LOCK(agi_commands);
+ cmd = AST_LIST_REMOVE_HEAD(agi_commands, entry);
+ AST_LIST_UNLOCK(agi_commands);
+ return cmd;
+}
+
+/* channel is locked when calling this one either from the CLI or manager thread */
+static int add_agi_cmd(struct ast_channel *chan, const char *cmd_buff, const char *cmd_id)
+{
+ struct ast_datastore *store;
+ struct agi_cmd *cmd;
+ AST_LIST_HEAD(, agi_cmd) *agi_commands;
+
+ store = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
+ if (!store) {
+ ast_log(LOG_WARNING, "Channel %s is not at Async AGI.\n", chan->name);
+ return -1;
+ }
+ agi_commands = store->data;
+ cmd = ast_calloc(1, sizeof(*cmd));
+ if (!cmd) {
+ return -1;
+ }
+ cmd->cmd_buffer = ast_strdup(cmd_buff);
+ if (!cmd->cmd_buffer) {
+ ast_free(cmd);
+ return -1;
+ }
+ cmd->cmd_id = ast_strdup(cmd_id);
+ if (!cmd->cmd_id) {
+ ast_free(cmd->cmd_buffer);
+ ast_free(cmd);
+ return -1;
+ }
+ AST_LIST_LOCK(agi_commands);
+ AST_LIST_INSERT_TAIL(agi_commands, cmd, entry);
+ AST_LIST_UNLOCK(agi_commands);
+ return 0;
+}
+
+static int add_to_agi(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+ AST_LIST_HEAD(, agi_cmd) *agi_cmds_list;
+
+ /* check if already on AGI */
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
+ ast_channel_unlock(chan);
+ if (datastore) {
+ /* we already have an AGI datastore, let's just
+ return success */
+ return 0;
+ }
+
+ /* the channel has never been on Async AGI,
+ let's allocate it's datastore */
+ datastore = ast_channel_datastore_alloc(&agi_commands_datastore_info, "AGI");
+ if (!datastore) {
+ return -1;
+ }
+ agi_cmds_list = ast_calloc(1, sizeof(*agi_cmds_list));
+ if (!agi_cmds_list) {
+ ast_log(LOG_ERROR, "Unable to allocate Async AGI commands list.\n");
+ ast_channel_datastore_free(datastore);
+ return -1;
+ }
+ datastore->data = agi_cmds_list;
+ AST_LIST_HEAD_INIT(agi_cmds_list);
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+ return 0;
+}
+
+/*!
+ * \brief CLI command to add applications to execute in Async AGI
+ * \param e
+ * \param cmd
+ * \param a
+ *
+ * \retval CLI_SUCCESS on success
+ * \retval NULL when init or tab completion is used
+*/
+static char *handle_cli_agi_add_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "agi exec";
+ e->usage = "Usage: agi exec <channel name> <app and arguments> [id]\n"
+ " Add AGI command to the execute queue of the specified channel in Async AGI\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos == 2)
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
+ return NULL;
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+ chan = ast_get_channel_by_name_locked(a->argv[2]);
+ if (!chan) {
+ ast_log(LOG_WARNING, "Channel %s does not exists or cannot lock it\n", a->argv[2]);
+ return CLI_FAILURE;
+ }
+ if (add_agi_cmd(chan, a->argv[3], (a->argc > 4 ? a->argv[4] : ""))) {
+ ast_log(LOG_WARNING, "failed to add AGI command to queue of channel %s\n", chan->name);
+ ast_channel_unlock(chan);
+ return CLI_FAILURE;
+ }
+ ast_log(LOG_DEBUG, "Added AGI command to channel %s queue\n", chan->name);
+ ast_channel_unlock(chan);
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Add a new command to execute by the Async AGI application
+ * \param s
+ * \param m
+ *
+ * It will append the application to the specified channel's queue
+ * if the channel is not inside Async AGI application it will return an error
+ * \retval 0 on success or incorrect use
+ * \retval 1 on failure to add the command ( most likely because the channel
+ * is not in Async AGI loop )
+*/
+static int action_add_agi_cmd(struct mansession *s, const struct message *m)
+{
+ const char *channel = astman_get_header(m, "Channel");
+ const char *cmdbuff = astman_get_header(m, "Command");
+ const char *cmdid = astman_get_header(m, "CommandID");
+ struct ast_channel *chan;
+ char buf[256];
+ if (ast_strlen_zero(channel) || ast_strlen_zero(cmdbuff)) {
+ astman_send_error(s, m, "Both, Channel and Command are *required*");
+ return 0;
+ }
+ chan = ast_get_channel_by_name_locked(channel);
+ if (!chan) {
+ snprintf(buf, sizeof(buf), "Channel %s does not exists or cannot get its lock", channel);
+ astman_send_error(s, m, buf);
+ return 1;
+ }
+ if (add_agi_cmd(chan, cmdbuff, cmdid)) {
+ snprintf(buf, sizeof(buf), "Failed to add AGI command to channel %s queue", chan->name);
+ astman_send_error(s, m, buf);
+ ast_channel_unlock(chan);
+ return 1;
+ }
+ astman_send_ack(s, m, "Added AGI command to queue");
+ ast_channel_unlock(chan);
+ return 0;
+}
+
+static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead);
+static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[]);
+static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], int *efd)
+{
+/* This buffer sizes might cause truncation if the AGI command writes more data
+ than AGI_BUF_SIZE as result. But let's be serious, is there an AGI command
+ that writes a response larger than 1024 bytes?, I don't think so, most of
+ them are just result=blah stuff. However probably if GET VARIABLE is called
+ and the variable has large amount of data, that could be a problem. We could
+ make this buffers dynamic, but let's leave that as a second step.
+
+ AMI_BUF_SIZE is twice AGI_BUF_SIZE just for the sake of choosing a safe
+ number. Some characters of AGI buf will be url encoded to be sent to manager
+ clients. An URL encoded character will take 3 bytes, but again, to cause
+ truncation more than about 70% of the AGI buffer should be URL encoded for
+ that to happen. Not likely at all.
+
+ On the other hand. I wonder if read() could eventually return less data than
+ the amount already available in the pipe? If so, how to deal with that?
+ So far, my tests on Linux have not had any problems.
+ */
+#define AGI_BUF_SIZE 1024
+#define AMI_BUF_SIZE 2048
+ struct ast_frame *f;
+ struct agi_cmd *cmd;
+ int res, fds[2];
+ int timeout = 100;
+ char agi_buffer[AGI_BUF_SIZE + 1];
+ char ami_buffer[AMI_BUF_SIZE];
+ enum agi_result returnstatus = AGI_RESULT_SUCCESS_ASYNC;
+ AGI async_agi;
+
+ if (efd) {
+ ast_log(LOG_WARNING, "Async AGI does not support Enhanced AGI yet\n");
+ return AGI_RESULT_FAILURE;
+ }
+
+ /* add AsyncAGI datastore to the channel */
+ if (add_to_agi(chan)) {
+ ast_log(LOG_ERROR, "failed to start Async AGI on channel %s\n", chan->name);
+ return AGI_RESULT_FAILURE;
+ }
+
+ /* this pipe allows us to create a "fake" AGI struct to use
+ the AGI commands */
+ res = pipe(fds);
+ if (res) {
+ ast_log(LOG_ERROR, "failed to create Async AGI pipe\n");
+ /* intentionally do not remove datastore, added with
+ add_to_agi(), from channel. It will be removed when
+ the channel is hung up anyways */
+ return AGI_RESULT_FAILURE;
+ }
+ /* handlers will get the pipe write fd and we read the AGI responses
+ from the pipe read fd */
+ async_agi.fd = fds[1];
+ async_agi.ctrl = fds[1];
+ async_agi.audio = -1; /* no audio support */
+ async_agi.fast = 0;
+
+ /* notify possible manager users of a new channel ready to
+ receive commands */
+ setup_env(chan, "async", fds[1], 0, 0, NULL);
+ /* read the environment */
+ res = read(fds[0], agi_buffer, AGI_BUF_SIZE);
+ if (!res) {
+ ast_log(LOG_ERROR, "failed to read from Async AGI pipe on channel %s\n", chan->name);
+ returnstatus = AGI_RESULT_FAILURE;
+ goto quit;
+ }
+ agi_buffer[res] = '\0';
+ /* encode it and send it thru the manager so whoever is going to take
+ care of AGI commands on this channel can decide which AGI commands
+ to execute based on the setup info */
+ ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, 1);
+ manager_event(EVENT_FLAG_CALL, "AsyncAGI", "SubEvent: Start\r\nChannel: %s\r\nEnv: %s\r\n", chan->name, ami_buffer);
+ while (1) {
+ /* bail out if we need to hangup */
+ if (ast_check_hangup(chan)) {
+ ast_log(LOG_DEBUG, "ast_check_hangup returned true on chan %s\n", chan->name);
+ break;
+ }
+ /* retrieve a command
+ (commands are added via the manager or the cli threads) */
+ cmd = get_agi_cmd(chan);
+ if (cmd) {
+ /* OK, we have a command, let's call the
+ command handler. */
+ res = agi_handle_command(chan, &async_agi, cmd->cmd_buffer, 0);
+ if ((res < 0) || (res == AST_PBX_KEEPALIVE)) {
+ free_agi_cmd(cmd);
+ break;
+ }
+ /* the command handler must have written to our fake
+ AGI struct fd (the pipe), let's read the response */
+ res = read(fds[0], agi_buffer, AGI_BUF_SIZE);
+ if (!res) {
+ returnstatus = AGI_RESULT_FAILURE;
+ ast_log(LOG_ERROR, "failed to read from AsyncAGI pipe on channel %s\n", chan->name);
+ free_agi_cmd(cmd);
+ break;
+ }
+ /* we have a response, let's send the response thru the
+ manager. Include the CommandID if it was specified
+ when the command was added */
+ agi_buffer[res] = '\0';
+ ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, 1);
+ if (ast_strlen_zero(cmd->cmd_id))
+ manager_event(EVENT_FLAG_CALL, "AsyncAGI", "SubEvent: Exec\r\nChannel: %s\r\nResult: %s\r\n", chan->name, ami_buffer);
+ else
+ manager_event(EVENT_FLAG_CALL, "AsyncAGI", "SubEvent: Exec\r\nChannel: %s\r\nCommandID: %s\r\nResult: %s\r\n", chan->name, cmd->cmd_id, ami_buffer);
+ free_agi_cmd(cmd);
+ } else {
+ /* no command so far, wait a bit for a frame to read */
+ res = ast_waitfor(chan, timeout);
+ if (res < 0) {
+ ast_log(LOG_DEBUG, "ast_waitfor returned <= 0 on chan %s\n", chan->name);
+ break;
+ }
+ if (res == 0)
+ continue;
+ f = ast_read(chan);
+ if (!f) {
+ ast_log(LOG_DEBUG, "No frame read on channel %s, going out ...\n", chan->name);
+ returnstatus = AGI_RESULT_HANGUP;
+ break;
+ }
+ /* is there any other frame we should care about
+ besides AST_CONTROL_HANGUP? */
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
+ ast_log(LOG_DEBUG, "Got HANGUP frame on channel %s, going out ...\n", chan->name);
+ ast_frfree(f);
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+quit:
+ /* notify manager users this channel cannot be
+ controlled anymore by Async AGI */
+ manager_event(EVENT_FLAG_CALL, "AsyncAGI", "SubEvent: End\r\nChannel: %s\r\n", chan->name);
+
+ /* close the pipe */
+ close(fds[0]);
+ close(fds[1]);
+
+ /* intentionally don't get rid of the datastore. So commands can be
+ still in the queue in case AsyncAGI gets called again.
+ Datastore destructor will be called on channel destroy anyway */
+
+ return returnstatus;
+
+#undef AGI_BUF_SIZE
+#undef AMI_BUF_SIZE
+}
+
+/* launch_netscript: The fastagi handler.
+ FastAGI defaults to port 4573 */
+static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
+{
+ int s, flags, res, port = AGI_PORT;
+ struct pollfd pfds[1];
+ char *host, *c, *script = "";
+ struct sockaddr_in sin;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+
+ /* agiusl is "agi://host.domain[:port][/script/name]" */
+ host = ast_strdupa(agiurl + 6); /* Remove agi:// */
+ /* Strip off any script name */
+ if ((c = strchr(host, '/'))) {
+ *c = '\0';
+ c++;
+ script = c;
+ }
+ if ((c = strchr(host, ':'))) {
+ *c = '\0';
+ c++;
+ port = atoi(c);
+ }
+ if (efd) {
+ ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
+ return -1;
+ }
+ if (!(hp = ast_gethostbyname(host, &ahp))) {
+ ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
+ return -1;
+ }
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
+ return -1;
+ }
+ if ((flags = fcntl(s, F_GETFL)) < 0) {
+ ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
+ close(s);
+ return -1;
+ }
+ if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
+ ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
+ close(s);
+ return -1;
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
+ ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
+ close(s);
+ return AGI_RESULT_FAILURE;
+ }
+
+ pfds[0].fd = s;
+ pfds[0].events = POLLOUT;
+ while ((res = poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
+ if (errno != EINTR) {
+ if (!res) {
+ ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
+ agiurl, MAX_AGI_CONNECT);
+ } else
+ ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
+ close(s);
+ return AGI_RESULT_FAILURE;
+ }
+ }
+
+ if (ast_agi_fdprintf(NULL, s, "agi_network: yes\n") < 0) {
+ if (errno != EINTR) {
+ ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
+ close(s);
+ return AGI_RESULT_FAILURE;
+ }
+ }
+
+ /* If we have a script parameter, relay it to the fastagi server */
+ /* Script parameters take the form of: AGI(agi://my.example.com/?extension=${EXTEN}) */
+ if (!ast_strlen_zero(script))
+ ast_agi_fdprintf(NULL, s, "agi_network_script: %s\n", script);
+
+ ast_debug(4, "Wow, connected!\n");
+ fds[0] = s;
+ fds[1] = s;
+ *opid = -1;
+ return AGI_RESULT_SUCCESS_FAST;
+}
+
+static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid)
+{
+ char tmp[256];
+ int pid, toast[2], fromast[2], audio[2], x, res;
+ sigset_t signal_set, old_set;
+ struct stat st;
+
+ if (!strncasecmp(script, "agi://", 6))
+ return launch_netscript(script, argv, fds, efd, opid);
+ if (!strncasecmp(script, "agi:async", sizeof("agi:async")-1))
+ return launch_asyncagi(chan, argv, efd);
+
+ if (script[0] != '/') {
+ snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_AGI_DIR, script);
+ script = tmp;
+ }
+
+ /* Before even trying let's see if the file actually exists */
+ if (stat(script, &st)) {
+ ast_log(LOG_WARNING, "Failed to execute '%s': File does not exist.\n", script);
+ return AGI_RESULT_NOTFOUND;
+ }
+
+ if (pipe(toast)) {
+ ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
+ return AGI_RESULT_FAILURE;
+ }
+ if (pipe(fromast)) {
+ ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
+ close(toast[0]);
+ close(toast[1]);
+ return AGI_RESULT_FAILURE;
+ }
+ if (efd) {
+ if (pipe(audio)) {
+ ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
+ close(fromast[0]);
+ close(fromast[1]);
+ close(toast[0]);
+ close(toast[1]);
+ return AGI_RESULT_FAILURE;
+ }
+ res = fcntl(audio[1], F_GETFL);
+ if (res > -1)
+ res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
+ close(fromast[0]);
+ close(fromast[1]);
+ close(toast[0]);
+ close(toast[1]);
+ close(audio[0]);
+ close(audio[1]);
+ return AGI_RESULT_FAILURE;
+ }
+ }
+
+ /* Block SIGHUP during the fork - prevents a race */
+ sigfillset(&signal_set);
+ pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
+ if ((pid = fork()) < 0) {
+ ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
+ pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+ return AGI_RESULT_FAILURE;
+ }
+ if (!pid) {
+ /* Pass paths to AGI via environmental variables */
+ setenv("AST_CONFIG_DIR", ast_config_AST_CONFIG_DIR, 1);
+ setenv("AST_CONFIG_FILE", ast_config_AST_CONFIG_FILE, 1);
+ setenv("AST_MODULE_DIR", ast_config_AST_MODULE_DIR, 1);
+ setenv("AST_SPOOL_DIR", ast_config_AST_SPOOL_DIR, 1);
+ setenv("AST_MONITOR_DIR", ast_config_AST_MONITOR_DIR, 1);
+ setenv("AST_VAR_DIR", ast_config_AST_VAR_DIR, 1);
+ setenv("AST_DATA_DIR", ast_config_AST_DATA_DIR, 1);
+ setenv("AST_LOG_DIR", ast_config_AST_LOG_DIR, 1);
+ setenv("AST_AGI_DIR", ast_config_AST_AGI_DIR, 1);
+ setenv("AST_KEY_DIR", ast_config_AST_KEY_DIR, 1);
+ setenv("AST_RUN_DIR", ast_config_AST_RUN_DIR, 1);
+
+ /* Don't run AGI scripts with realtime priority -- it causes audio stutter */
+ ast_set_priority(0);
+
+ /* Redirect stdin and out, provide enhanced audio channel if desired */
+ dup2(fromast[0], STDIN_FILENO);
+ dup2(toast[1], STDOUT_FILENO);
+ if (efd)
+ dup2(audio[0], STDERR_FILENO + 1);
+ else
+ close(STDERR_FILENO + 1);
+
+ /* Before we unblock our signals, return our trapped signals back to the defaults */
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGURG, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGXFSZ, SIG_DFL);
+
+ /* unblock important signal handlers */
+ if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) {
+ ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno));
+ _exit(1);
+ }
+
+ /* Close everything but stdin/out/error */
+ for (x = STDERR_FILENO + 2; x < 1024; x++)
+ close(x);
+
+ /* Execute script */
+ /* XXX argv should be deprecated in favor of passing agi_argX paramaters */
+ execv(script, argv);
+ /* Can't use ast_log since FD's are closed */
+ fprintf(stdout, "verbose \"Failed to execute '%s': %s\" 2\n", script, strerror(errno));
+ fflush(stdout);
+ _exit(1);
+ }
+ pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+ ast_verb(3, "Launched AGI Script %s\n", script);
+ fds[0] = toast[0];
+ fds[1] = fromast[1];
+ if (efd)
+ *efd = audio[1];
+ /* close what we're not using in the parent */
+ close(toast[1]);
+ close(fromast[0]);
+
+ if (efd)
+ close(audio[0]);
+
+ *opid = pid;
+ return AGI_RESULT_SUCCESS;
+}
+
+static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[])
+{
+ int count;
+
+ /* Print initial environment, with agi_request always being the first
+ thing */
+ ast_agi_fdprintf(chan, fd, "agi_request: %s\n", request);
+ ast_agi_fdprintf(chan, fd, "agi_channel: %s\n", chan->name);
+ ast_agi_fdprintf(chan, fd, "agi_language: %s\n", chan->language);
+ ast_agi_fdprintf(chan, fd, "agi_type: %s\n", chan->tech->type);
+ ast_agi_fdprintf(chan, fd, "agi_uniqueid: %s\n", chan->uniqueid);
+ ast_agi_fdprintf(chan, fd, "agi_version: %s\n", ast_get_version());
+
+ /* ANI/DNIS */
+ ast_agi_fdprintf(chan, fd, "agi_callerid: %s\n", S_OR(chan->cid.cid_num, "unknown"));
+ ast_agi_fdprintf(chan, fd, "agi_calleridname: %s\n", S_OR(chan->cid.cid_name, "unknown"));
+ ast_agi_fdprintf(chan, fd, "agi_callingpres: %d\n", chan->cid.cid_pres);
+ ast_agi_fdprintf(chan, fd, "agi_callingani2: %d\n", chan->cid.cid_ani2);
+ ast_agi_fdprintf(chan, fd, "agi_callington: %d\n", chan->cid.cid_ton);
+ ast_agi_fdprintf(chan, fd, "agi_callingtns: %d\n", chan->cid.cid_tns);
+ ast_agi_fdprintf(chan, fd, "agi_dnid: %s\n", S_OR(chan->cid.cid_dnid, "unknown"));
+ ast_agi_fdprintf(chan, fd, "agi_rdnis: %s\n", S_OR(chan->cid.cid_rdnis, "unknown"));
+
+ /* Context information */
+ ast_agi_fdprintf(chan, fd, "agi_context: %s\n", chan->context);
+ ast_agi_fdprintf(chan, fd, "agi_extension: %s\n", chan->exten);
+ ast_agi_fdprintf(chan, fd, "agi_priority: %d\n", chan->priority);
+ ast_agi_fdprintf(chan, fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
+
+ /* User information */
+ ast_agi_fdprintf(chan, fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
+ ast_agi_fdprintf(chan, fd, "agi_threadid: %ld\n", (long)pthread_self());
+
+ /* Send any parameters to the fastagi server that have been passed via the agi application */
+ /* Agi application paramaters take the form of: AGI(/path/to/example/script|${EXTEN}) */
+ for(count = 1; count < argc; count++)
+ ast_agi_fdprintf(chan, fd, "agi_arg_%d: %s\n", count, argv[count]);
+
+ /* End with empty return */
+ ast_agi_fdprintf(chan, fd, "\n");
+}
+
+static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res = 0;
+
+ /* Answer the channel */
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, to;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[3], "%d", &to) != 1)
+ return RESULT_SHOWUSAGE;
+ res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ /* At the moment, the parser (perhaps broken) returns with
+ the last argument PLUS the newline at the end of the input
+ buffer. This probably needs to be fixed, but I wont do that
+ because other stuff may break as a result. The right way
+ would probably be to strip off the trailing newline before
+ parsing, then here, add a newline at the end of the string
+ before sending it to ast_sendtext --DUDE */
+ res = ast_sendtext(chan, argv[2]);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ res = ast_recvchar(chan,atoi(argv[2]));
+ if (res == 0) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (timeout)\n", res);
+ return RESULT_SUCCESS;
+ }
+ if (res > 0) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return RESULT_SUCCESS;
+ }
+ else {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (hangup)\n", res);
+ return RESULT_FAILURE;
+ }
+}
+
+static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ char *buf;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ buf = ast_recvtext(chan,atoi(argv[2]));
+ if (buf) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1 (%s)\n", buf);
+ ast_free(buf);
+ } else {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=-1\n");
+ }
+ return RESULT_SUCCESS;
+}
+
+static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, x;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ if (!strncasecmp(argv[2],"on",2))
+ x = 1;
+ else
+ x = 0;
+ if (!strncasecmp(argv[2],"mate",4))
+ x = 2;
+ if (!strncasecmp(argv[2],"tdd",3))
+ x = 1;
+ res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
+ if (res != RESULT_SUCCESS)
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ res = ast_send_image(chan, argv[2]);
+ if (!ast_check_hangup(chan))
+ res = 0;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res = 0, skipms = 3000;
+ char *fwd = NULL, *rev = NULL, *pause = NULL, *stop = NULL;
+
+ if (argc < 5 || argc > 9)
+ return RESULT_SHOWUSAGE;
+
+ if (!ast_strlen_zero(argv[4]))
+ stop = argv[4];
+ else
+ stop = NULL;
+
+ if ((argc > 5) && (sscanf(argv[5], "%d", &skipms) != 1))
+ return RESULT_SHOWUSAGE;
+
+ if (argc > 6 && !ast_strlen_zero(argv[6]))
+ fwd = argv[6];
+ else
+ fwd = "#";
+
+ if (argc > 7 && !ast_strlen_zero(argv[7]))
+ rev = argv[7];
+ else
+ rev = "*";
+
+ if (argc > 8 && !ast_strlen_zero(argv[8]))
+ pause = argv[8];
+ else
+ pause = NULL;
+
+ res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, pause, NULL, skipms, NULL);
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, vres;
+ struct ast_filestream *fs, *vfs;
+ long sample_offset = 0, max_length;
+ char *edigits = "";
+
+ if (argc < 4 || argc > 5)
+ return RESULT_SHOWUSAGE;
+
+ if (argv[3])
+ edigits = argv[3];
+
+ if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
+ return RESULT_SHOWUSAGE;
+
+ if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
+ return RESULT_SUCCESS;
+ }
+
+ if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
+ ast_debug(1, "Ooh, found a video stream, too\n");
+
+ ast_verb(3, "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
+
+ ast_seekstream(fs, 0, SEEK_END);
+ max_length = ast_tellstream(fs);
+ ast_seekstream(fs, sample_offset, SEEK_SET);
+ res = ast_applystream(chan, fs);
+ if (vfs)
+ vres = ast_applystream(chan, vfs);
+ ast_playstream(fs);
+ if (vfs)
+ ast_playstream(vfs);
+
+ res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
+ /* this is to check for if ast_waitstream closed the stream, we probably are at
+ * the end of the stream, return that amount, else check for the amount */
+ sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
+ ast_stopstream(chan);
+ if (res == 1) {
+ /* Stop this command, don't print a result line, as there is a new command */
+ return RESULT_SUCCESS;
+ }
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+/* get option - really similar to the handle_streamfile, but with a timeout */
+static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, vres;
+ struct ast_filestream *fs, *vfs;
+ long sample_offset = 0, max_length;
+ int timeout = 0;
+ char *edigits = "";
+
+ if ( argc < 4 || argc > 5 )
+ return RESULT_SHOWUSAGE;
+
+ if ( argv[3] )
+ edigits = argv[3];
+
+ if ( argc == 5 )
+ timeout = atoi(argv[4]);
+ else if (chan->pbx->dtimeout) {
+ /* by default dtimeout is set to 5sec */
+ timeout = chan->pbx->dtimeout * 1000; /* in msec */
+ }
+
+ if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
+ ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
+ return RESULT_SUCCESS;
+ }
+
+ if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
+ ast_debug(1, "Ooh, found a video stream, too\n");
+
+ ast_verb(3, "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
+
+ ast_seekstream(fs, 0, SEEK_END);
+ max_length = ast_tellstream(fs);
+ ast_seekstream(fs, sample_offset, SEEK_SET);
+ res = ast_applystream(chan, fs);
+ if (vfs)
+ vres = ast_applystream(chan, vfs);
+ ast_playstream(fs);
+ if (vfs)
+ ast_playstream(vfs);
+
+ res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
+ /* this is to check for if ast_waitstream closed the stream, we probably are at
+ * the end of the stream, return that amount, else check for the amount */
+ sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
+ ast_stopstream(chan);
+ if (res == 1) {
+ /* Stop this command, don't print a result line, as there is a new command */
+ return RESULT_SUCCESS;
+ }
+
+ /* If the user didnt press a key, wait for digitTimeout*/
+ if (res == 0 ) {
+ res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
+ /* Make sure the new result is in the escape digits of the GET OPTION */
+ if ( !strchr(edigits,res) )
+ res=0;
+ }
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+
+
+
+/*--- handle_saynumber: Say number in various language syntaxes ---*/
+/* While waiting, we're sending a NULL. */
+static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, num;
+
+ if (argc < 4 || argc > 5)
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[2], "%d", &num) != 1)
+ return RESULT_SHOWUSAGE;
+ res = ast_say_number_full(chan, num, argv[3], chan->language, argc > 4 ? argv[4] : NULL, agi->audio, agi->ctrl);
+ if (res == 1)
+ return RESULT_SUCCESS;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, num;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[2], "%d", &num) != 1)
+ return RESULT_SHOWUSAGE;
+
+ res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
+ if (res == 1) /* New command */
+ return RESULT_SUCCESS;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
+ if (res == 1) /* New command */
+ return RESULT_SUCCESS;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, num;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[2], "%d", &num) != 1)
+ return RESULT_SHOWUSAGE;
+ res = ast_say_date(chan, num, argv[3], chan->language);
+ if (res == 1)
+ return RESULT_SUCCESS;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, num;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[2], "%d", &num) != 1)
+ return RESULT_SHOWUSAGE;
+ res = ast_say_time(chan, num, argv[3], chan->language);
+ if (res == 1)
+ return RESULT_SUCCESS;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res = 0;
+ time_t unixtime;
+ char *format, *zone = NULL;
+
+ if (argc < 4)
+ return RESULT_SHOWUSAGE;
+
+ if (argc > 4) {
+ format = argv[4];
+ } else {
+ /* XXX this doesn't belong here, but in the 'say' module */
+ if (!strcasecmp(chan->language, "de")) {
+ format = "A dBY HMS";
+ } else {
+ format = "ABdY 'digits/at' IMp";
+ }
+ }
+
+ if (argc > 5 && !ast_strlen_zero(argv[5]))
+ zone = argv[5];
+
+ if (ast_get_time_t(argv[2], &unixtime, 0, NULL))
+ return RESULT_SHOWUSAGE;
+
+ res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone);
+ if (res == 1)
+ return RESULT_SUCCESS;
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
+ if (res == 1) /* New command */
+ return RESULT_SUCCESS;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+ return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
+}
+
+static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int res, max, timeout;
+ char data[1024];
+
+ if (argc < 3)
+ return RESULT_SHOWUSAGE;
+ if (argc >= 4)
+ timeout = atoi(argv[3]);
+ else
+ timeout = 0;
+ if (argc >= 5)
+ max = atoi(argv[4]);
+ else
+ max = 1024;
+ res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
+ if (res == 2) /* New command */
+ return RESULT_SUCCESS;
+ else if (res == 1)
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%s (timeout)\n", data);
+ else if (res < 0 )
+ ast_agi_fdprintf(chan, agi->fd, "200 result=-1\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%s\n", data);
+ return RESULT_SUCCESS;
+}
+
+static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_copy_string(chan->context, argv[2], sizeof(chan->context));
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ int pri;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ if (sscanf(argv[2], "%d", &pri) != 1) {
+ if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
+ return RESULT_SHOWUSAGE;
+ }
+
+ ast_explicit_goto(chan, NULL, NULL, pri);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ struct ast_filestream *fs;
+ struct ast_frame *f;
+ struct timeval start;
+ long sample_offset = 0;
+ int res = 0;
+ int ms;
+
+ struct ast_dsp *sildet=NULL; /* silence detector dsp */
+ int totalsilence = 0;
+ int dspsilence = 0;
+ int silence = 0; /* amount of silence to allow */
+ int gotsilence = 0; /* did we timeout for silence? */
+ char *silencestr=NULL;
+ int rfmt=0;
+
+
+ /* XXX EAGI FIXME XXX */
+
+ if (argc < 6)
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[5], "%d", &ms) != 1)
+ return RESULT_SHOWUSAGE;
+
+ if (argc > 6)
+ silencestr = strchr(argv[6],'s');
+ if ((argc > 7) && (!silencestr))
+ silencestr = strchr(argv[7],'s');
+ if ((argc > 8) && (!silencestr))
+ silencestr = strchr(argv[8],'s');
+
+ if (silencestr) {
+ if (strlen(silencestr) > 2) {
+ if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
+ silencestr++;
+ silencestr++;
+ if (silencestr)
+ silence = atoi(silencestr);
+ if (silence > 0)
+ silence *= 1000;
+ }
+ }
+ }
+
+ if (silence > 0) {
+ rfmt = chan->readformat;
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+ return -1;
+ }
+ sildet = ast_dsp_new();
+ if (!sildet) {
+ ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+ return -1;
+ }
+ ast_dsp_set_threshold(sildet, 256);
+ }
+
+ /* backward compatibility, if no offset given, arg[6] would have been
+ * caught below and taken to be a beep, else if it is a digit then it is a
+ * offset */
+ if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
+ res = ast_streamfile(chan, "beep", chan->language);
+
+ if ((argc > 7) && (!strchr(argv[7], '=')))
+ res = ast_streamfile(chan, "beep", chan->language);
+
+ if (!res)
+ res = ast_waitstream(chan, argv[4]);
+ if (res) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
+ } else {
+ fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, AST_FILE_MODE);
+ if (!fs) {
+ res = -1;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (writefile)\n", res);
+ if (sildet)
+ ast_dsp_free(sildet);
+ return RESULT_FAILURE;
+ }
+
+ /* Request a video update */
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+
+ chan->stream = fs;
+ ast_applystream(chan,fs);
+ /* really should have checks */
+ ast_seekstream(fs, sample_offset, SEEK_SET);
+ ast_truncstream(fs);
+
+ start = ast_tvnow();
+ while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) {
+ res = ast_waitfor(chan, -1);
+ if (res < 0) {
+ ast_closestream(fs);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
+ if (sildet)
+ ast_dsp_free(sildet);
+ return RESULT_FAILURE;
+ }
+ f = ast_read(chan);
+ if (!f) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
+ ast_closestream(fs);
+ if (sildet)
+ ast_dsp_free(sildet);
+ return RESULT_FAILURE;
+ }
+ switch(f->frametype) {
+ case AST_FRAME_DTMF:
+ if (strchr(argv[4], f->subclass)) {
+ /* This is an interrupting chracter, so rewind to chop off any small
+ amount of DTMF that may have been recorded
+ */
+ ast_stream_rewind(fs, 200);
+ ast_truncstream(fs);
+ sample_offset = ast_tellstream(fs);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
+ ast_closestream(fs);
+ ast_frfree(f);
+ if (sildet)
+ ast_dsp_free(sildet);
+ return RESULT_SUCCESS;
+ }
+ break;
+ case AST_FRAME_VOICE:
+ ast_writestream(fs, f);
+ /* this is a safe place to check progress since we know that fs
+ * is valid after a write, and it will then have our current
+ * location */
+ sample_offset = ast_tellstream(fs);
+ if (silence > 0) {
+ dspsilence = 0;
+ ast_dsp_silence(sildet, f, &dspsilence);
+ if (dspsilence) {
+ totalsilence = dspsilence;
+ } else {
+ totalsilence = 0;
+ }
+ if (totalsilence > silence) {
+ /* Ended happily with silence */
+ gotsilence = 1;
+ break;
+ }
+ }
+ break;
+ case AST_FRAME_VIDEO:
+ ast_writestream(fs, f);
+ default:
+ /* Ignore all other frames */
+ break;
+ }
+ ast_frfree(f);
+ if (gotsilence)
+ break;
+ }
+
+ if (gotsilence) {
+ ast_stream_rewind(fs, silence-1000);
+ ast_truncstream(fs);
+ sample_offset = ast_tellstream(fs);
+ }
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
+ ast_closestream(fs);
+ }
+
+ if (silence > 0) {
+ res = ast_set_read_format(chan, rfmt);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
+ ast_dsp_free(sildet);
+ }
+ return RESULT_SUCCESS;
+}
+
+static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ int timeout;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ if (sscanf(argv[2], "%d", &timeout) != 1)
+ return RESULT_SHOWUSAGE;
+ if (timeout < 0)
+ timeout = 0;
+ if (timeout)
+ chan->whentohangup = time(NULL) + timeout;
+ else
+ chan->whentohangup = 0;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ struct ast_channel *c;
+
+ if (argc == 1) {
+ /* no argument: hangup the current channel */
+ ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+ return RESULT_SUCCESS;
+ } else if (argc == 2) {
+ /* one argument: look for info on the specified channel */
+ c = ast_get_channel_by_name_locked(argv[1]);
+ if (c) {
+ /* we have a matching channel */
+ ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+ ast_channel_unlock(c);
+ return RESULT_SUCCESS;
+ }
+ /* if we get this far no channel name matched the argument given */
+ ast_agi_fdprintf(chan, agi->fd, "200 result=-1\n");
+ return RESULT_SUCCESS;
+ } else {
+ return RESULT_SHOWUSAGE;
+ }
+}
+
+static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ int res;
+ struct ast_app *app;
+
+ if (argc < 2)
+ return RESULT_SHOWUSAGE;
+
+ ast_verb(3, "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
+
+ if ((app = pbx_findapp(argv[1]))) {
+ res = pbx_exec(chan, app, argv[2]);
+ } else {
+ ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
+ res = -2;
+ }
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", res);
+
+ /* Even though this is wrong, users are depending upon this result. */
+ return res;
+}
+
+static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ char tmp[256]="";
+ char *l = NULL, *n = NULL;
+
+ if (argv[2]) {
+ ast_copy_string(tmp, argv[2], sizeof(tmp));
+ ast_callerid_parse(tmp, &n, &l);
+ if (l)
+ ast_shrink_phone_number(l);
+ else
+ l = "";
+ if (!n)
+ n = "";
+ ast_set_callerid(chan, l, n, NULL);
+ }
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ struct ast_channel *c;
+ if (argc == 2) {
+ /* no argument: supply info on the current channel */
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", chan->_state);
+ return RESULT_SUCCESS;
+ } else if (argc == 3) {
+ /* one argument: look for info on the specified channel */
+ c = ast_get_channel_by_name_locked(argv[2]);
+ if (c) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%d\n", c->_state);
+ ast_channel_unlock(c);
+ return RESULT_SUCCESS;
+ }
+ /* if we get this far no channel name matched the argument given */
+ ast_agi_fdprintf(chan, agi->fd, "200 result=-1\n");
+ return RESULT_SUCCESS;
+ } else {
+ return RESULT_SHOWUSAGE;
+ }
+}
+
+static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ if (argv[3])
+ pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ char *ret;
+ char tempstr[1024];
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ /* check if we want to execute an ast_custom_function */
+ if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
+ ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
+ } else {
+ pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
+ }
+
+ if (ret)
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1 (%s)\n", ret);
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ char tmp[4096];
+ struct ast_channel *chan2=NULL;
+
+ if ((argc != 4) && (argc != 5))
+ return RESULT_SHOWUSAGE;
+ if (argc == 5) {
+ chan2 = ast_get_channel_by_name_locked(argv[4]);
+ } else {
+ chan2 = chan;
+ }
+ if (chan2) {
+ pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1 (%s)\n", tmp);
+ } else {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ }
+ if (chan2 && (chan2 != chan))
+ ast_channel_unlock(chan2);
+ return RESULT_SUCCESS;
+}
+
+static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ int level = 0;
+ char *prefix;
+
+ if (argc < 2)
+ return RESULT_SHOWUSAGE;
+
+ if (argv[2])
+ sscanf(argv[2], "%d", &level);
+
+ switch (level) {
+ case 4:
+ prefix = VERBOSE_PREFIX_4;
+ break;
+ case 3:
+ prefix = VERBOSE_PREFIX_3;
+ break;
+ case 2:
+ prefix = VERBOSE_PREFIX_2;
+ break;
+ case 1:
+ default:
+ prefix = VERBOSE_PREFIX_1;
+ break;
+ }
+
+ if (level <= option_verbose)
+ ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ int res;
+ char tmp[256];
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
+ if (res)
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1 (%s)\n", tmp);
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ int res;
+
+ if (argc != 5)
+ return RESULT_SHOWUSAGE;
+ res = ast_db_put(argv[2], argv[3], argv[4]);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%c\n", res ? '0' : '1');
+ return RESULT_SUCCESS;
+}
+
+static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ int res;
+
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ res = ast_db_del(argv[2], argv[3]);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%c\n", res ? '0' : '1');
+ return RESULT_SUCCESS;
+}
+
+static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ int res;
+
+ if ((argc < 3) || (argc > 4))
+ return RESULT_SHOWUSAGE;
+ if (argc == 4)
+ res = ast_db_deltree(argv[2], argv[3]);
+ else
+ res = ast_db_deltree(argv[2], NULL);
+
+ ast_agi_fdprintf(chan, agi->fd, "200 result=%c\n", res ? '0' : '1');
+ return RESULT_SUCCESS;
+}
+
+static char *handle_cli_agi_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "agi debug [off]";
+ e->usage =
+ "Usage: agi debug [off]\n"
+ " Enables/disables dumping of AGI transactions for\n"
+ " debugging purposes.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < e->args - 1 || a->argc > e->args )
+ return CLI_SHOWUSAGE;
+ if (a->argc == e->args - 1) {
+ agidebug = 1;
+ } else {
+ if (strncasecmp(a->argv[e->args - 1], "off", 3) == 0) {
+ agidebug = 0;
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+ }
+ ast_cli(a->fd, "AGI Debugging %sabled\n", agidebug ? "En" : "Dis");
+ return CLI_SUCCESS;
+}
+
+static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
+{
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ if (!strncasecmp(argv[2], "on", 2))
+ ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
+ else if (!strncasecmp(argv[2], "off", 3))
+ ast_moh_stop(chan);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+}
+
+static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ /* If a structure already exists, return an error */
+ if (agi->speech) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ if ((agi->speech = ast_speech_new(argv[2], AST_FORMAT_SLINEAR)))
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_speechset(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ /* Check for minimum arguments */
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ /* Check to make sure speech structure exists */
+ if (!agi->speech) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ ast_speech_change(agi->speech, argv[2], argv[3]);
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_speechdestroy(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ if (agi->speech) {
+ ast_speech_destroy(agi->speech);
+ agi->speech = NULL;
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+ } else {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ }
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_speechloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ if (argc != 5)
+ return RESULT_SHOWUSAGE;
+
+ if (!agi->speech) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ if (ast_speech_grammar_load(agi->speech, argv[3], argv[4]))
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_speechunloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ if (!agi->speech) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ if (ast_speech_grammar_unload(agi->speech, argv[3]))
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_speechactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ if (!agi->speech) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ if (ast_speech_grammar_activate(agi->speech, argv[3]))
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_speechdeactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+
+ if (!agi->speech) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ if (ast_speech_grammar_deactivate(agi->speech, argv[3]))
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ else
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+
+ return RESULT_SUCCESS;
+}
+
+static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang, int offset)
+{
+ struct ast_filestream *fs = NULL;
+
+ if (!(fs = ast_openstream(chan, filename, preflang)))
+ return -1;
+
+ if (offset)
+ ast_seekstream(fs, offset, SEEK_SET);
+
+ if (ast_applystream(chan, fs))
+ return -1;
+
+ if (ast_playstream(fs))
+ return -1;
+
+ return 0;
+}
+
+static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+ struct ast_speech *speech = agi->speech;
+ char *prompt, dtmf = 0, tmp[4096] = "", *buf = tmp;
+ int timeout = 0, offset = 0, old_read_format = 0, res = 0, i = 0;
+ long current_offset = 0;
+ const char *reason = NULL;
+ struct ast_frame *fr = NULL;
+ struct ast_speech_result *result = NULL;
+ size_t left = sizeof(tmp);
+ time_t start = 0, current;
+
+ if (argc < 4)
+ return RESULT_SHOWUSAGE;
+
+ if (!speech) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ prompt = argv[2];
+ timeout = atoi(argv[3]);
+
+ /* If offset is specified then convert from text to integer */
+ if (argc == 5)
+ offset = atoi(argv[4]);
+
+ /* We want frames coming in signed linear */
+ old_read_format = chan->readformat;
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return RESULT_SUCCESS;
+ }
+
+ /* Setup speech structure */
+ if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
+ ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
+ ast_speech_start(speech);
+ }
+
+ /* Start playing prompt */
+ speech_streamfile(chan, prompt, chan->language, offset);
+
+ /* Go into loop reading in frames, passing to speech thingy, checking for hangup, all that jazz */
+ while (ast_strlen_zero(reason)) {
+ /* Run scheduled items */
+ ast_sched_runq(chan->sched);
+
+ /* See maximum time of waiting */
+ if ((res = ast_sched_wait(chan->sched)) < 0)
+ res = 1000;
+
+ /* Wait for frame */
+ if (ast_waitfor(chan, res) > 0) {
+ if (!(fr = ast_read(chan))) {
+ reason = "hangup";
+ break;
+ }
+ }
+
+ /* Perform timeout check */
+ if ((timeout > 0) && (start > 0)) {
+ time(&current);
+ if ((current - start) >= timeout) {
+ reason = "timeout";
+ if (fr)
+ ast_frfree(fr);
+ break;
+ }
+ }
+
+ /* Check the speech structure for any changes */
+ ast_mutex_lock(&speech->lock);
+
+ /* See if we need to quiet the audio stream playback */
+ if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream) {
+ current_offset = ast_tellstream(chan->stream);
+ ast_stopstream(chan);
+ ast_clear_flag(speech, AST_SPEECH_QUIET);
+ }
+
+ /* Check each state */
+ switch (speech->state) {
+ case AST_SPEECH_STATE_READY:
+ /* If the stream is done, start timeout calculation */
+ if ((timeout > 0) && ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL))) {
+ ast_stopstream(chan);
+ time(&start);
+ }
+ /* Write audio frame data into speech engine if possible */
+ if (fr && fr->frametype == AST_FRAME_VOICE)
+ ast_speech_write(speech, fr->data, fr->datalen);
+ break;
+ case AST_SPEECH_STATE_WAIT:
+ /* Cue waiting sound if not already playing */
+ if ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL)) {
+ ast_stopstream(chan);
+ /* If a processing sound exists, or is not none - play it */
+ if (!ast_strlen_zero(speech->processing_sound) && strcasecmp(speech->processing_sound, "none"))
+ speech_streamfile(chan, speech->processing_sound, chan->language, 0);
+ }
+ break;
+ case AST_SPEECH_STATE_DONE:
+ /* Get the results */
+ speech->results = ast_speech_results_get(speech);
+ /* Change state to not ready */
+ ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
+ reason = "speech";
+ break;
+ default:
+ break;
+ }
+ ast_mutex_unlock(&speech->lock);
+
+ /* Check frame for DTMF or hangup */
+ if (fr) {
+ if (fr->frametype == AST_FRAME_DTMF) {
+ reason = "dtmf";
+ dtmf = fr->subclass;
+ } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_HANGUP) {
+ reason = "hangup";
+ }
+ ast_frfree(fr);
+ }
+ }
+
+ if (!strcasecmp(reason, "speech")) {
+ /* Build string containing speech results */
+ for (result = speech->results; result; result = AST_LIST_NEXT(result, list)) {
+ /* Build result string */
+ ast_build_string(&buf, &left, "%sscore%d=%d text%d=\"%s\" grammar%d=%s", (i > 0 ? " " : ""), i, result->score, i, result->text, i, result->grammar);
+ /* Increment result count */
+ i++;
+ }
+ /* Print out */
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1 (speech) endpos=%ld results=%d %s\n", current_offset, i, tmp);
+ } else if (!strcasecmp(reason, "dtmf")) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1 (digit) digit=%c endpos=%ld\n", dtmf, current_offset);
+ } else if (!strcasecmp(reason, "hangup") || !strcasecmp(reason, "timeout")) {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=1 (%s) endpos=%ld\n", reason, current_offset);
+ } else {
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0 endpos=%ld\n", current_offset);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+static int handle_asyncagi_break(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
+{
+ ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+ return AST_PBX_KEEPALIVE;
+}
+
+static char usage_setmusic[] =
+" Usage: SET MUSIC ON <on|off> <class>\n"
+" Enables/Disables the music on hold generator. If <class> is\n"
+" not specified, then the default music on hold class will be used.\n"
+" Always returns 0.\n";
+
+static char usage_dbput[] =
+" Usage: DATABASE PUT <family> <key> <value>\n"
+" Adds or updates an entry in the Asterisk database for a\n"
+" given family, key, and value.\n"
+" Returns 1 if successful, 0 otherwise.\n";
+
+static char usage_dbget[] =
+" Usage: DATABASE GET <family> <key>\n"
+" Retrieves an entry in the Asterisk database for a\n"
+" given family and key.\n"
+" Returns 0 if <key> is not set. Returns 1 if <key>\n"
+" is set and returns the variable in parentheses.\n"
+" Example return code: 200 result=1 (testvariable)\n";
+
+static char usage_dbdel[] =
+" Usage: DATABASE DEL <family> <key>\n"
+" Deletes an entry in the Asterisk database for a\n"
+" given family and key.\n"
+" Returns 1 if successful, 0 otherwise.\n";
+
+static char usage_dbdeltree[] =
+" Usage: DATABASE DELTREE <family> [keytree]\n"
+" Deletes a family or specific keytree within a family\n"
+" in the Asterisk database.\n"
+" Returns 1 if successful, 0 otherwise.\n";
+
+static char usage_verbose[] =
+" Usage: VERBOSE <message> <level>\n"
+" Sends <message> to the console via verbose message system.\n"
+" <level> is the the verbose level (1-4)\n"
+" Always returns 1.\n";
+
+static char usage_getvariable[] =
+" Usage: GET VARIABLE <variablename>\n"
+" Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
+" is set and returns the variable in parentheses.\n"
+" example return code: 200 result=1 (testvariable)\n";
+
+static char usage_getvariablefull[] =
+" Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
+" Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
+"if <variablename> is set and returns the variable in parenthesis. Understands\n"
+"complex variable names and builtin variables, unlike GET VARIABLE.\n"
+" example return code: 200 result=1 (testvariable)\n";
+
+static char usage_setvariable[] =
+" Usage: SET VARIABLE <variablename> <value>\n";
+
+static char usage_channelstatus[] =
+" Usage: CHANNEL STATUS [<channelname>]\n"
+" Returns the status of the specified channel.\n"
+" If no channel name is given the returns the status of the\n"
+" current channel. Return values:\n"
+" 0 Channel is down and available\n"
+" 1 Channel is down, but reserved\n"
+" 2 Channel is off hook\n"
+" 3 Digits (or equivalent) have been dialed\n"
+" 4 Line is ringing\n"
+" 5 Remote end is ringing\n"
+" 6 Line is up\n"
+" 7 Line is busy\n";
+
+static char usage_setcallerid[] =
+" Usage: SET CALLERID <number>\n"
+" Changes the callerid of the current channel.\n";
+
+static char usage_exec[] =
+" Usage: EXEC <application> <options>\n"
+" Executes <application> with given <options>.\n"
+" Returns whatever the application returns, or -2 on failure to find application\n";
+
+static char usage_hangup[] =
+" Usage: HANGUP [<channelname>]\n"
+" Hangs up the specified channel.\n"
+" If no channel name is given, hangs up the current channel\n";
+
+static char usage_answer[] =
+" Usage: ANSWER\n"
+" Answers channel if not already in answer state. Returns -1 on\n"
+" channel failure, or 0 if successful.\n";
+
+static char usage_waitfordigit[] =
+" Usage: WAIT FOR DIGIT <timeout>\n"
+" Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
+" Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
+" the numerical value of the ascii of the digit if one is received. Use -1\n"
+" for the timeout value if you desire the call to block indefinitely.\n";
+
+static char usage_sendtext[] =
+" Usage: SEND TEXT \"<text to send>\"\n"
+" Sends the given text on a channel. Most channels do not support the\n"
+" transmission of text. Returns 0 if text is sent, or if the channel does not\n"
+" support text transmission. Returns -1 only on error/hangup. Text\n"
+" consisting of greater than one word should be placed in quotes since the\n"
+" command only accepts a single argument.\n";
+
+static char usage_recvchar[] =
+" Usage: RECEIVE CHAR <timeout>\n"
+" Receives a character of text on a channel. Specify timeout to be the\n"
+" maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
+" do not support the reception of text. Returns the decimal value of the character\n"
+" if one is received, or 0 if the channel does not support text reception. Returns\n"
+" -1 only on error/hangup.\n";
+
+static char usage_recvtext[] =
+" Usage: RECEIVE TEXT <timeout>\n"
+" Receives a string of text on a channel. Specify timeout to be the\n"
+" maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
+" do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
+
+static char usage_tddmode[] =
+" Usage: TDD MODE <on|off>\n"
+" Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
+" successful, or 0 if channel is not TDD-capable.\n";
+
+static char usage_sendimage[] =
+" Usage: SEND IMAGE <image>\n"
+" Sends the given image on a channel. Most channels do not support the\n"
+" transmission of images. Returns 0 if image is sent, or if the channel does not\n"
+" support image transmission. Returns -1 only on error/hangup. Image names\n"
+" should not include extensions.\n";
+
+static char usage_streamfile[] =
+" Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
+" Send the given file, allowing playback to be interrupted by the given\n"
+" digits, if any. Use double quotes for the digits if you wish none to be\n"
+" permitted. If sample offset is provided then the audio will seek to sample\n"
+" offset before play starts. Returns 0 if playback completes without a digit\n"
+" being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
+" or -1 on error or if the channel was disconnected. Remember, the file\n"
+" extension must not be included in the filename.\n";
+
+static char usage_controlstreamfile[] =
+" Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
+" Send the given file, allowing playback to be controled by the given\n"
+" digits, if any. Use double quotes for the digits if you wish none to be\n"
+" permitted. Returns 0 if playback completes without a digit\n"
+" being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
+" or -1 on error or if the channel was disconnected. Remember, the file\n"
+" extension must not be included in the filename.\n\n"
+" Note: ffchar and rewchar default to * and # respectively.\n";
+
+static char usage_getoption[] =
+" Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
+" Behaves similar to STREAM FILE but used with a timeout option.\n";
+
+static char usage_saynumber[] =
+" Usage: SAY NUMBER <number> <escape digits> [gender]\n"
+" Say a given number, returning early if any of the given DTMF digits\n"
+" are received on the channel. Returns 0 if playback completes without a digit\n"
+" being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
+" -1 on error/hangup.\n";
+
+static char usage_saydigits[] =
+" Usage: SAY DIGITS <number> <escape digits>\n"
+" Say a given digit string, returning early if any of the given DTMF digits\n"
+" are received on the channel. Returns 0 if playback completes without a digit\n"
+" being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
+" -1 on error/hangup.\n";
+
+static char usage_sayalpha[] =
+" Usage: SAY ALPHA <number> <escape digits>\n"
+" Say a given character string, returning early if any of the given DTMF digits\n"
+" are received on the channel. Returns 0 if playback completes without a digit\n"
+" being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
+" -1 on error/hangup.\n";
+
+static char usage_saydate[] =
+" Usage: SAY DATE <date> <escape digits>\n"
+" Say a given date, returning early if any of the given DTMF digits are\n"
+" received on the channel. <date> is number of seconds elapsed since 00:00:00\n"
+" on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
+" completes without a digit being pressed, or the ASCII numerical value of the\n"
+" digit if one was pressed or -1 on error/hangup.\n";
+
+static char usage_saytime[] =
+" Usage: SAY TIME <time> <escape digits>\n"
+" Say a given time, returning early if any of the given DTMF digits are\n"
+" received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
+" on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
+" completes without a digit being pressed, or the ASCII numerical value of the\n"
+" digit if one was pressed or -1 on error/hangup.\n";
+
+static char usage_saydatetime[] =
+" Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
+" Say a given time, returning early if any of the given DTMF digits are\n"
+" received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
+" on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
+" the time should be said in. See voicemail.conf (defaults to \"ABdY\n"
+" 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n"
+" /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback\n"
+" completes without a digit being pressed, or the ASCII numerical value of the\n"
+" digit if one was pressed or -1 on error/hangup.\n";
+
+static char usage_sayphonetic[] =
+" Usage: SAY PHONETIC <string> <escape digits>\n"
+" Say a given character string with phonetics, returning early if any of the\n"
+" given DTMF digits are received on the channel. Returns 0 if playback\n"
+" completes without a digit pressed, the ASCII numerical value of the digit\n"
+" if one was pressed, or -1 on error/hangup.\n";
+
+static char usage_getdata[] =
+" Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
+" Stream the given file, and recieve DTMF data. Returns the digits received\n"
+"from the channel at the other end.\n";
+
+static char usage_setcontext[] =
+" Usage: SET CONTEXT <desired context>\n"
+" Sets the context for continuation upon exiting the application.\n";
+
+static char usage_setextension[] =
+" Usage: SET EXTENSION <new extension>\n"
+" Changes the extension for continuation upon exiting the application.\n";
+
+static char usage_setpriority[] =
+" Usage: SET PRIORITY <priority>\n"
+" Changes the priority for continuation upon exiting the application.\n"
+" The priority must be a valid priority or label.\n";
+
+static char usage_recordfile[] =
+" Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
+" [offset samples] [BEEP] [s=silence]\n"
+" Record to a file until a given dtmf digit in the sequence is received\n"
+" Returns -1 on hangup or error. The format will specify what kind of file\n"
+" will be recorded. The timeout is the maximum record time in milliseconds, or\n"
+" -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
+" to the offset without exceeding the end of the file. \"silence\" is the number\n"
+" of seconds of silence allowed before the function returns despite the\n"
+" lack of dtmf digits or reaching timeout. Silence value must be\n"
+" preceeded by \"s=\" and is also optional.\n";
+
+static char usage_autohangup[] =
+" Usage: SET AUTOHANGUP <time>\n"
+" Cause the channel to automatically hangup at <time> seconds in the\n"
+" future. Of course it can be hungup before then as well. Setting to 0 will\n"
+" cause the autohangup feature to be disabled on this channel.\n";
+
+static char usage_break_aagi[] =
+" Usage: ASYNCAGI BREAK\n"
+" Break the Async AGI loop.\n";
+
+static char usage_noop[] =
+" Usage: NoOp\n"
+" Does nothing.\n";
+
+static char usage_speechcreate[] =
+" Usage: SPEECH CREATE <engine>\n"
+" Create a speech object to be used by the other Speech AGI commands.\n";
+
+static char usage_speechset[] =
+" Usage: SPEECH SET <name> <value>\n"
+" Set an engine-specific setting.\n";
+
+static char usage_speechdestroy[] =
+" Usage: SPEECH DESTROY\n"
+" Destroy the speech object created by SPEECH CREATE.\n";
+
+static char usage_speechloadgrammar[] =
+" Usage: SPEECH LOAD GRAMMAR <grammar name> <path to grammar>\n"
+" Loads the specified grammar as the specified name.\n";
+
+static char usage_speechunloadgrammar[] =
+" Usage: SPEECH UNLOAD GRAMMAR <grammar name>\n"
+" Unloads the specified grammar.\n";
+
+static char usage_speechactivategrammar[] =
+" Usage: SPEECH ACTIVATE GRAMMAR <grammar name>\n"
+" Activates the specified grammar on the speech object.\n";
+
+static char usage_speechdeactivategrammar[] =
+" Usage: SPEECH DEACTIVATE GRAMMAR <grammar name>\n"
+" Deactivates the specified grammar on the speech object.\n";
+
+static char usage_speechrecognize[] =
+" Usage: SPEECH RECOGNIZE <prompt> <timeout> [<offset>]\n"
+" Plays back given prompt while listening for speech and dtmf.\n";
+
+/*!
+ * \brief AGI commands list
+ */
+static struct agi_command commands[] = {
+ { { "answer", NULL }, handle_answer, "Answer channel", usage_answer , 0 },
+ { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus , 0 },
+ { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel , 1 },
+ { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree , 1 },
+ { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget , 1 },
+ { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput , 1 },
+ { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec , 1 },
+ { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata , 0 },
+ { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull , 1 },
+ { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption , 0 },
+ { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable , 1 },
+ { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup , 0 },
+ { { "noop", NULL }, handle_noop, "Does nothing", usage_noop , 1 },
+ { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar , 0 },
+ { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext , 0 },
+ { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile , 0 },
+ { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha , 0 },
+ { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits , 0 },
+ { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber , 0 },
+ { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic , 0 },
+ { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate , 0 },
+ { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime , 0 },
+ { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime , 0 },
+ { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage , 0 },
+ { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext , 0 },
+ { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup , 0 },
+ { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid , 0 },
+ { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext , 0 },
+ { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension , 0 },
+ { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic , 0 },
+ { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority , 0 },
+ { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable , 1 },
+ { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile , 0 },
+ { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile , 0 },
+ { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode , 0 },
+ { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose , 1 },
+ { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit , 0 },
+ { { "speech", "create", NULL }, handle_speechcreate, "Creates a speech object", usage_speechcreate, 0 },
+ { { "speech", "set", NULL }, handle_speechset, "Sets a speech engine setting", usage_speechset, 0 },
+ { { "speech", "destroy", NULL }, handle_speechdestroy, "Destroys a speech object", usage_speechdestroy, 1 },
+ { { "speech", "load", "grammar", NULL }, handle_speechloadgrammar, "Loads a grammar", usage_speechloadgrammar, 0 },
+ { { "speech", "unload", "grammar", NULL }, handle_speechunloadgrammar, "Unloads a grammar", usage_speechunloadgrammar, 1 },
+ { { "speech", "activate", "grammar", NULL }, handle_speechactivategrammar, "Activates a grammar", usage_speechactivategrammar, 0 },
+ { { "speech", "deactivate", "grammar", NULL }, handle_speechdeactivategrammar, "Deactivates a grammar", usage_speechdeactivategrammar, 0 },
+ { { "speech", "recognize", NULL }, handle_speechrecognize, "Recognizes speech", usage_speechrecognize, 0 },
+ { { "asyncagi", "break", NULL }, handle_asyncagi_break, "Break AsyncAGI loop", usage_break_aagi, 0 },
+};
+
+static AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
+
+static char *help_workhorse(int fd, char *match[])
+{
+ char fullcmd[80], matchstr[80];
+ struct agi_command *e;
+
+ if (match)
+ ast_join(matchstr, sizeof(matchstr), match);
+
+ ast_cli(fd, "%5.5s %30.30s %s\n","Dead","Command","Description");
+ AST_RWLIST_RDLOCK(&agi_commands);
+ AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
+ if (!e->cmda[0])
+ break;
+ /* Hide commands that start with '_' */
+ if ((e->cmda[0])[0] == '_')
+ continue;
+ ast_join(fullcmd, sizeof(fullcmd), e->cmda);
+ if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
+ continue;
+ ast_cli(fd, "%5.5s %30.30s %s\n", e->dead ? "Yes" : "No" , fullcmd, e->summary);
+ }
+ AST_RWLIST_UNLOCK(&agi_commands);
+
+ return CLI_SUCCESS;
+}
+
+int ast_agi_register(struct ast_module *mod, agi_command *cmd)
+{
+ char fullcmd[80];
+
+ ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
+
+ if (!find_command(cmd->cmda,1)) {
+ cmd->mod = mod;
+ AST_RWLIST_WRLOCK(&agi_commands);
+ AST_LIST_INSERT_TAIL(&agi_commands, cmd, list);
+ AST_RWLIST_UNLOCK(&agi_commands);
+ if (mod != ast_module_info->self)
+ ast_module_ref(ast_module_info->self);
+ ast_verb(2, "AGI Command '%s' registered\n",fullcmd);
+ return 1;
+ } else {
+ ast_log(LOG_WARNING, "Command already registered!\n");
+ return 0;
+ }
+}
+
+int ast_agi_unregister(struct ast_module *mod, agi_command *cmd)
+{
+ struct agi_command *e;
+ int unregistered = 0;
+ char fullcmd[80];
+
+ ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
+
+ AST_RWLIST_WRLOCK(&agi_commands);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&agi_commands, e, list) {
+ if (cmd == e) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ if (mod != ast_module_info->self)
+ ast_module_unref(ast_module_info->self);
+ unregistered=1;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&agi_commands);
+ if (unregistered)
+ ast_verb(2, "AGI Command '%s' unregistered\n",fullcmd);
+ else
+ ast_log(LOG_WARNING, "Unable to unregister command: '%s'!\n",fullcmd);
+ return unregistered;
+}
+
+void ast_agi_register_multiple(struct ast_module *mod, agi_command *cmd, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ast_agi_register(mod, cmd + i);
+
+}
+
+void ast_agi_unregister_multiple(struct ast_module *mod, agi_command *cmd, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ast_agi_unregister(mod, cmd + i);
+}
+
+static agi_command *find_command(char *cmds[], int exact)
+{
+ int y, match;
+ struct agi_command *e;
+
+ AST_RWLIST_RDLOCK(&agi_commands);
+ AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
+ if (!e->cmda[0])
+ break;
+ /* start optimistic */
+ match = 1;
+ for (y = 0; match && cmds[y]; y++) {
+ /* If there are no more words in the command (and we're looking for
+ an exact match) or there is a difference between the two words,
+ then this is not a match */
+ if (!e->cmda[y] && !exact)
+ break;
+ /* don't segfault if the next part of a command doesn't exist */
+ if (!e->cmda[y])
+ return NULL;
+ if (strcasecmp(e->cmda[y], cmds[y]))
+ match = 0;
+ }
+ /* If more words are needed to complete the command then this is not
+ a candidate (unless we're looking for a really inexact answer */
+ if ((exact > -1) && e->cmda[y])
+ match = 0;
+ if (match)
+ return e;
+ }
+ AST_RWLIST_UNLOCK(&agi_commands);
+ return NULL;
+}
+
+static int parse_args(char *s, int *max, char *argv[])
+{
+ int x = 0, quoted = 0, escaped = 0, whitespace = 1;
+ char *cur;
+
+ cur = s;
+ while(*s) {
+ switch(*s) {
+ case '"':
+ /* If it's escaped, put a literal quote */
+ if (escaped)
+ goto normal;
+ else
+ quoted = !quoted;
+ if (quoted && whitespace) {
+ /* If we're starting a quote, coming off white space start a new word, too */
+ argv[x++] = cur;
+ whitespace=0;
+ }
+ escaped = 0;
+ break;
+ case ' ':
+ case '\t':
+ if (!quoted && !escaped) {
+ /* If we're not quoted, mark this as whitespace, and
+ end the previous argument */
+ whitespace = 1;
+ *(cur++) = '\0';
+ } else
+ /* Otherwise, just treat it as anything else */
+ goto normal;
+ break;
+ case '\\':
+ /* If we're escaped, print a literal, otherwise enable escaping */
+ if (escaped) {
+ goto normal;
+ } else {
+ escaped=1;
+ }
+ break;
+ default:
+normal:
+ if (whitespace) {
+ if (x >= MAX_ARGS -1) {
+ ast_log(LOG_WARNING, "Too many arguments, truncating\n");
+ break;
+ }
+ /* Coming off of whitespace, start the next argument */
+ argv[x++] = cur;
+ whitespace=0;
+ }
+ *(cur++) = *s;
+ escaped=0;
+ }
+ s++;
+ }
+ /* Null terminate */
+ *(cur++) = '\0';
+ argv[x] = NULL;
+ *max = x;
+ return 0;
+}
+
+static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
+{
+ char *argv[MAX_ARGS];
+ int argc = MAX_ARGS, res;
+ agi_command *c;
+ const char *ami_res = "Unknown Result";
+ char *ami_cmd = ast_strdupa(buf);
+ int command_id = ast_random(), resultcode = 200;
+
+ manager_event(EVENT_FLAG_CALL, "AGIExec",
+ "SubEvent: Start\r\n"
+ "Channel: %s\r\n"
+ "CommandId: %d\r\n"
+ "Command: %s\r\n", chan->name, command_id, ami_cmd);
+ parse_args(buf, &argc, argv);
+ if ((c = find_command(argv, 0)) && (!dead || (dead && c->dead))) {
+ /* if this command wasnt registered by res_agi, be sure to usecount
+ the module we are using */
+ if (c->mod != ast_module_info->self)
+ ast_module_ref(c->mod);
+ res = c->handler(chan, agi, argc, argv);
+ if (c->mod != ast_module_info->self)
+ ast_module_unref(c->mod);
+ switch (res) {
+ case RESULT_SHOWUSAGE: ami_res = "Usage"; resultcode = 520; break;
+ case AST_PBX_KEEPALIVE: ami_res = "KeepAlive"; resultcode = 210; break;
+ case RESULT_FAILURE: ami_res = "Failure"; resultcode = -1; break;
+ case RESULT_SUCCESS: ami_res = "Success"; resultcode = 200; break;
+ }
+ manager_event(EVENT_FLAG_CALL, "AGIExec",
+ "SubEvent: End\r\n"
+ "Channel: %s\r\n"
+ "CommandId: %d\r\n"
+ "Command: %s\r\n"
+ "ResultCode: %d\r\n"
+ "Result: %s\r\n", chan->name, command_id, ami_cmd, resultcode, ami_res);
+ switch(res) {
+ case RESULT_SHOWUSAGE:
+ ast_agi_fdprintf(chan, agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
+ ast_agi_fdprintf(chan, agi->fd, c->usage);
+ ast_agi_fdprintf(chan, agi->fd, "520 End of proper usage.\n");
+ break;
+ case AST_PBX_KEEPALIVE:
+ /* We've been asked to keep alive, so do so */
+ return AST_PBX_KEEPALIVE;
+ break;
+ case RESULT_FAILURE:
+ /* They've already given the failure. We've been hung up on so handle this
+ appropriately */
+ return -1;
+ }
+ } else if ((c = find_command(argv, 0))) {
+ ast_agi_fdprintf(chan, agi->fd, "511 Command Not Permitted on a dead channel\n");
+ manager_event(EVENT_FLAG_CALL, "AGIExec",
+ "SubEvent: End\r\n"
+ "Channel: %s\r\n"
+ "CommandId: %d\r\n"
+ "Command: %s\r\n"
+ "ResultCode: 511\r\n"
+ "Result: Command not permitted on a dead channel\r\n", chan->name, command_id, ami_cmd);
+ } else {
+ ast_agi_fdprintf(chan, agi->fd, "510 Invalid or unknown command\n");
+ manager_event(EVENT_FLAG_CALL, "AGIExec",
+ "SubEvent: End\r\n"
+ "Channel: %s\r\n"
+ "CommandId: %d\r\n"
+ "Command: %s\r\n"
+ "ResultCode: 510\r\n"
+ "Result: Invalid or unknown command\r\n", chan->name, command_id, ami_cmd);
+ }
+ return 0;
+}
+static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
+{
+ struct ast_channel *c;
+ int outfd, ms, needhup = 0;
+ enum agi_result returnstatus = AGI_RESULT_SUCCESS;
+ struct ast_frame *f;
+ char buf[AGI_BUF_LEN];
+ char *res = NULL;
+ FILE *readf;
+ /* how many times we'll retry if ast_waitfor_nandfs will return without either
+ channel or file descriptor in case select is interrupted by a system call (EINTR) */
+ int retry = AGI_NANDFS_RETRY;
+
+ if (!(readf = fdopen(agi->ctrl, "r"))) {
+ ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
+ if (pid > -1)
+ kill(pid, SIGHUP);
+ close(agi->ctrl);
+ return AGI_RESULT_FAILURE;
+ }
+ setlinebuf(readf);
+ setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
+ for (;;) {
+ if (needhup) {
+ needhup = 0;
+ dead = 1;
+ if (pid > -1)
+ kill(pid, SIGHUP);
+ }
+ ms = -1;
+ c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
+ if (c) {
+ retry = AGI_NANDFS_RETRY;
+ /* Idle the channel until we get a command */
+ f = ast_read(c);
+ if (!f) {
+ ast_debug(1, "%s hungup\n", chan->name);
+ returnstatus = AGI_RESULT_HANGUP;
+ needhup = 1;
+ continue;
+ } else {
+ /* If it's voice, write it to the audio pipe */
+ if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
+ /* Write, ignoring errors */
+ write(agi->audio, f->data, f->datalen);
+ }
+ ast_frfree(f);
+ }
+ } else if (outfd > -1) {
+ size_t len = sizeof(buf);
+ size_t buflen = 0;
+
+ retry = AGI_NANDFS_RETRY;
+ buf[0] = '\0';
+
+ while (buflen < (len - 1)) {
+ res = fgets(buf + buflen, len, readf);
+ if (feof(readf))
+ break;
+ if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))
+ break;
+ if (res != NULL && !agi->fast)
+ break;
+ buflen = strlen(buf);
+ if (buflen && buf[buflen - 1] == '\n')
+ break;
+ len -= buflen;
+ if (agidebug)
+ ast_verbose( "AGI Rx << temp buffer %s - errno %s\n", buf, strerror(errno));
+ }
+
+ if (!buf[0]) {
+ /* Program terminated */
+ if (returnstatus && returnstatus != AST_PBX_KEEPALIVE)
+ returnstatus = -1;
+ ast_verb(3, "<%s>AGI Script %s completed, returning %d\n", chan->name, request, returnstatus);
+ if (pid > 0)
+ waitpid(pid, status, 0);
+ /* No need to kill the pid anymore, since they closed us */
+ pid = -1;
+ break;
+ }
+
+ /* get rid of trailing newline, if any */
+ if (*buf && buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = 0;
+ if (agidebug)
+ ast_verbose("<%s>AGI Rx << %s\n", chan->name, buf);
+ returnstatus |= agi_handle_command(chan, agi, buf, dead);
+ /* If the handle_command returns -1, we need to stop */
+ if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
+ needhup = 1;
+ continue;
+ }
+ } else {
+ if (--retry <= 0) {
+ ast_log(LOG_WARNING, "No channel, no fd?\n");
+ returnstatus = AGI_RESULT_FAILURE;
+ break;
+ }
+ }
+ }
+ /* Notify process */
+ if (pid > -1) {
+ const char *sighup = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
+ if (ast_strlen_zero(sighup) || !ast_false(sighup)) {
+ if (kill(pid, SIGHUP))
+ ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
+ }
+ }
+ fclose(readf);
+ return returnstatus;
+}
+
+static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct agi_command *command;
+ char fullcmd[80];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "agi show";
+ e->usage =
+ "Usage: agi show [topic]\n"
+ " When called with a topic as an argument, displays usage\n"
+ " information on the given command. If called without a\n"
+ " topic, it provides a list of AGI commands.\n";
+ break;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < e->args)
+ return CLI_SHOWUSAGE;
+ if (a->argc > e->args) {
+ command = find_command(a->argv + e->args, 1);
+ if (command) {
+ ast_cli(a->fd, command->usage);
+ ast_cli(a->fd, " Runs Dead : %s\n", command->dead ? "Yes" : "No");
+ } else {
+ if (find_command(a->argv + e->args, -1)) {
+ return help_workhorse(a->fd, a->argv + e->args);
+ } else {
+ ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
+ ast_cli(a->fd, "No such command '%s'.\n", fullcmd);
+ }
+ }
+ } else {
+ return help_workhorse(a->fd, NULL);
+ }
+ return CLI_SUCCESS;
+}
+
+/*! \brief Convert string to use HTML escaped characters
+ \note Maybe this should be a generic function?
+*/
+static void write_html_escaped(FILE *htmlfile, char *str)
+{
+ char *cur = str;
+
+ while(*cur) {
+ switch (*cur) {
+ case '<':
+ fprintf(htmlfile, "%s", "&lt;");
+ break;
+ case '>':
+ fprintf(htmlfile, "%s", "&gt;");
+ break;
+ case '&':
+ fprintf(htmlfile, "%s", "&amp;");
+ break;
+ case '"':
+ fprintf(htmlfile, "%s", "&quot;");
+ break;
+ default:
+ fprintf(htmlfile, "%c", *cur);
+ break;
+ }
+ cur++;
+ }
+
+ return;
+}
+
+static char *handle_cli_agi_dumphtml(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct agi_command *command;
+ char fullcmd[80];
+ FILE *htmlfile;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "agi dumphtml";
+ e->usage =
+ "Usage: agi dumphtml <filename>\n"
+ " Dumps the AGI command list in HTML format to the given\n"
+ " file.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < e->args + 1)
+ return CLI_SHOWUSAGE;
+
+ if (!(htmlfile = fopen(a->argv[2], "wt"))) {
+ ast_cli(a->fd, "Could not create file '%s'\n", a->argv[2]);
+ return CLI_SHOWUSAGE;
+ }
+
+ fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
+ fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
+ fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
+
+ AST_RWLIST_RDLOCK(&agi_commands);
+ AST_RWLIST_TRAVERSE(&agi_commands, command, list) {
+ char *stringp, *tempstr;
+
+ if (!command->cmda[0]) /* end ? */
+ break;
+ /* Hide commands that start with '_' */
+ if ((command->cmda[0])[0] == '_')
+ continue;
+ ast_join(fullcmd, sizeof(fullcmd), command->cmda);
+
+ fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
+ fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd, command->summary);
+
+ stringp = command->usage;
+ tempstr = strsep(&stringp, "\n");
+
+ fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">");
+ write_html_escaped(htmlfile, tempstr);
+ fprintf(htmlfile, "</TD></TR>\n");
+ fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
+
+ while ((tempstr = strsep(&stringp, "\n")) != NULL) {
+ write_html_escaped(htmlfile, tempstr);
+ fprintf(htmlfile, "<BR>\n");
+ }
+ fprintf(htmlfile, "</TD></TR>\n");
+ fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
+ }
+ AST_RWLIST_UNLOCK(&agi_commands);
+ fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
+ fclose(htmlfile);
+ ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[2]);
+ return CLI_SUCCESS;
+}
+
+static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
+{
+ enum agi_result res;
+ struct ast_module_user *u;
+ char buf[AGI_BUF_LEN] = "", *tmp = buf;
+ int fds[2], efd = -1, pid;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(arg)[MAX_ARGS];
+ );
+ AGI agi;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
+ return -1;
+ }
+ if (dead)
+ ast_log(LOG_NOTICE, "Hungup channel detected, running agi in dead mode.\n");
+ ast_copy_string(buf, data, sizeof(buf));
+ memset(&agi, 0, sizeof(agi));
+ AST_STANDARD_APP_ARGS(args, tmp);
+ args.argv[args.argc] = NULL;
+
+ u = ast_module_user_add(chan);
+#if 0
+ /* Answer if need be */
+ if (chan->_state != AST_STATE_UP) {
+ if (ast_answer(chan)) {
+ ast_module_user_remove(u);
+ return -1;
+ }
+ }
+#endif
+ res = launch_script(chan, args.argv[0], args.argv, fds, enhanced ? &efd : NULL, &pid);
+ /* Async AGI do not require run_agi(), so just proceed if normal AGI
+ or Fast AGI are setup with success. */
+ if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
+ int status = 0;
+ agi.fd = fds[1];
+ agi.ctrl = fds[0];
+ agi.audio = efd;
+ agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
+ res = run_agi(chan, args.argv[0], &agi, pid, &status, dead, args.argc, args.argv);
+ /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
+ if ((res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) && status)
+ res = AGI_RESULT_FAILURE;
+ if (fds[1] != fds[0])
+ close(fds[1]);
+ if (efd > -1)
+ close(efd);
+ ast_unreplace_sigchld();
+ }
+ ast_module_user_remove(u);
+
+ switch (res) {
+ case AGI_RESULT_SUCCESS:
+ case AGI_RESULT_SUCCESS_FAST:
+ case AGI_RESULT_SUCCESS_ASYNC:
+ pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
+ break;
+ case AGI_RESULT_FAILURE:
+ pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
+ break;
+ case AGI_RESULT_NOTFOUND:
+ pbx_builtin_setvar_helper(chan, "AGISTATUS", "NOTFOUND");
+ break;
+ case AGI_RESULT_HANGUP:
+ pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int agi_exec(struct ast_channel *chan, void *data)
+{
+ if (!ast_check_hangup(chan))
+ return agi_exec_full(chan, data, 0, 0);
+ else
+ return agi_exec_full(chan, data, 0, 1);
+}
+
+static int eagi_exec(struct ast_channel *chan, void *data)
+{
+ int readformat, res;
+
+ if (ast_check_hangup(chan)) {
+ ast_log(LOG_ERROR, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
+ return 0;
+ }
+ readformat = chan->readformat;
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
+ return -1;
+ }
+ res = agi_exec_full(chan, data, 1, 0);
+ if (!res) {
+ if (ast_set_read_format(chan, readformat)) {
+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
+ }
+ }
+ return res;
+}
+
+static int deadagi_exec(struct ast_channel *chan, void *data)
+{
+ ast_log(LOG_WARNING, "DeadAGI has been deprecated, please use AGI in all cases!\n");
+ return agi_exec(chan, data);
+}
+
+static struct ast_cli_entry cli_agi[] = {
+ AST_CLI_DEFINE(handle_cli_agi_add_cmd, "Add AGI command to a channel in Async AGI"),
+ AST_CLI_DEFINE(handle_cli_agi_debug, "Enable/Disable AGI debugging"),
+ AST_CLI_DEFINE(handle_cli_agi_show, "List AGI commands or specific help"),
+ AST_CLI_DEFINE(handle_cli_agi_dumphtml, "Dumps a list of AGI commands in HTML format")
+};
+
+static int unload_module(void)
+{
+ ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
+ ast_agi_unregister_multiple(ast_module_info->self, commands, sizeof(commands) / sizeof(struct agi_command));
+ ast_unregister_application(eapp);
+ ast_unregister_application(deadapp);
+ ast_manager_unregister("AGI");
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
+ ast_agi_register_multiple(ast_module_info->self, commands, sizeof(commands) / sizeof(struct agi_command));
+ ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
+ ast_register_application(eapp, eagi_exec, esynopsis, descrip);
+ ast_manager_register2("AGI", EVENT_FLAG_CALL, action_add_agi_cmd, "Add an AGI command to execute by Async AGI", mandescr_asyncagi);
+ return ast_register_application(app, agi_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
+ .load = load_module,
+ .unload = unload_module,
+ );
diff --git a/trunk/res/res_clioriginate.c b/trunk/res/res_clioriginate.c
new file mode 100644
index 000000000..3d20e3ca3
--- /dev/null
+++ b/trunk/res/res_clioriginate.c
@@ -0,0 +1,191 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005 - 2006, Digium, Inc.
+ *
+ * Russell Bryant <russell@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
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \brief Originate calls via the CLI
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/frame.h"
+
+/*! The timeout for originated calls, in seconds */
+#define TIMEOUT 30
+
+/*!
+ * \brief orginate a call from the CLI
+ * \param fd file descriptor for cli
+ * \param chan channel to create type/data
+ * \param app application you want to run
+ * \param appdata data for application
+ * \retval CLI_SUCCESS on success.
+ * \retval CLI_SHOWUSAGE on failure.
+*/
+static char *orig_app(int fd, const char *chan, const char *app, const char *appdata)
+{
+ char *chantech;
+ char *chandata;
+ int reason = 0;
+
+ if (ast_strlen_zero(app))
+ return CLI_SHOWUSAGE;
+
+ chandata = ast_strdupa(chan);
+
+ chantech = strsep(&chandata, "/");
+ if (!chandata) {
+ ast_cli(fd, "*** No data provided after channel type! ***\n");
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_pbx_outgoing_app(chantech, AST_FORMAT_SLINEAR, chandata, TIMEOUT * 1000, app, appdata, &reason, 1, NULL, NULL, NULL, NULL, NULL);
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief orginate from extension
+ * \param fd file descriptor for cli
+ * \param chan channel to create type/data
+ * \param data contains exten\@context
+ * \retval CLI_SUCCESS on success.
+ * \retval CLI_SHOWUSAGE on failure.
+*/
+static char *orig_exten(int fd, const char *chan, const char *data)
+{
+ char *chantech;
+ char *chandata;
+ char *exten = NULL;
+ char *context = NULL;
+ int reason = 0;
+
+ chandata = ast_strdupa(chan);
+
+ chantech = strsep(&chandata, "/");
+ if (!chandata) {
+ ast_cli(fd, "*** No data provided after channel type! ***\n");
+ return CLI_SHOWUSAGE;
+ }
+
+ if (!ast_strlen_zero(data)) {
+ context = ast_strdupa(data);
+ exten = strsep(&context, "@");
+ }
+
+ if (ast_strlen_zero(exten))
+ exten = "s";
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ ast_pbx_outgoing_exten(chantech, AST_FORMAT_SLINEAR, chandata, TIMEOUT * 1000, context, exten, 1, &reason, 1, NULL, NULL, NULL, NULL, NULL);
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief handle for orgination app or exten.
+ * \param e pointer to the CLI structure to initialize
+ * \param cmd operation to execute
+ * \param a structure that contains either application or extension arguments
+ * \retval CLI_SUCCESS on success.
+ * \retval CLI_SHOWUSAGE on failure.
+*/
+static char *handle_orig(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ static char *choices[] = { "application", "extension", NULL };
+ char *res;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "originate";
+ e->usage =
+ " There are two ways to use this command. A call can be originated between a\n"
+ "channel and a specific application, or between a channel and an extension in\n"
+ "the dialplan. This is similar to call files or the manager originate action.\n"
+ "Calls originated with this command are given a timeout of 30 seconds.\n\n"
+
+ "Usage1: originate <tech/data> application <appname> [appdata]\n"
+ " This will originate a call between the specified channel tech/data and the\n"
+ "given application. Arguments to the application are optional. If the given\n"
+ "arguments to the application include spaces, all of the arguments to the\n"
+ "application need to be placed in quotation marks.\n\n"
+
+ "Usage2: originate <tech/data> extension [exten@][context]\n"
+ " This will originate a call between the specified channel tech/data and the\n"
+ "given extension. If no context is specified, the 'default' context will be\n"
+ "used. If no extension is given, the 's' extension will be used.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos != 2)
+ return NULL;
+
+ /* ugly, can be removed when CLI entries have ast_module pointers */
+ ast_module_ref(ast_module_info->self);
+ res = ast_cli_complete(a->word, choices, a->n);
+ ast_module_unref(ast_module_info->self);
+
+ return res;
+ }
+
+ if (ast_strlen_zero(a->argv[1]) || ast_strlen_zero(a->argv[2]))
+ return CLI_SHOWUSAGE;
+
+ /* ugly, can be removed when CLI entries have ast_module pointers */
+ ast_module_ref(ast_module_info->self);
+
+ if (!strcasecmp("application", a->argv[2])) {
+ res = orig_app(a->fd, a->argv[1], a->argv[3], a->argv[4]);
+ } else if (!strcasecmp("extension", a->argv[2])) {
+ res = orig_exten(a->fd, a->argv[1], a->argv[3]);
+ } else
+ res = CLI_SHOWUSAGE;
+
+ ast_module_unref(ast_module_info->self);
+
+ return res;
+}
+
+static struct ast_cli_entry cli_cliorig[] = {
+ AST_CLI_DEFINE(handle_orig, "Originate a call"),
+};
+
+/*! \brief Unload orginate module */
+static int unload_module(void)
+{
+ return ast_cli_unregister_multiple(cli_cliorig, ARRAY_LEN(cli_cliorig));
+}
+
+/*! \brief Load orginate module */
+static int load_module(void)
+{
+ int res;
+ res = ast_cli_register_multiple(cli_cliorig, ARRAY_LEN(cli_cliorig));
+ return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call origination from the CLI");
diff --git a/trunk/res/res_config_curl.c b/trunk/res/res_config_curl.c
new file mode 100644
index 000000000..622b8639f
--- /dev/null
+++ b/trunk/res/res_config_curl.c
@@ -0,0 +1,503 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, Digium, Inc.
+ *
+ * Tilghman Lesher <res_config_curl_v1@the-tilghman.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 curl plugin for portable configuration engine
+ *
+ * \author Tilghman Lesher <res_config_curl_v1@the-tilghman.com>
+ *
+ * \extref Depends on the CURL library - http://curl.haxx.se/
+ *
+ */
+
+/*** MODULEINFO
+ <depend>curl</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <curl/curl.h>
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+
+/*!
+ * \brief Execute a curl query and return ast_variable list
+ * \param url The base URL from which to retrieve data
+ * \param unused Not currently used
+ * \param ap list containing one or more field/operator/value set.
+ *
+ * \retval var on success
+ * \retval NULL on failure
+*/
+static struct ast_variable *realtime_curl(const char *url, const char *unused, va_list ap)
+{
+ struct ast_str *query;
+ char buf1[200], buf2[200];
+ const char *newparam, *newval;
+ char *stringp, *pair, *key;
+ int i;
+ struct ast_variable *var=NULL, *prev=NULL;
+ const int EncodeSpecialChars = 1;
+ char *buffer;
+
+ if (!ast_custom_function_find("CURL")) {
+ ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
+ return NULL;
+ }
+
+ if (!(query = ast_str_create(1000)))
+ return NULL;
+
+ if (!(buffer = ast_malloc(64000))) {
+ ast_free(query);
+ return NULL;
+ }
+
+ ast_str_set(&query, 0, "${CURL(%s,", url);
+
+ for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
+ newval = va_arg(ap, const char *);
+ ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
+ ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
+ }
+ va_end(ap);
+
+ ast_str_append(&query, 0, ")}");
+ pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
+
+ /* Remove any trailing newline characters */
+ if ((stringp = strchr(buffer, '\r')) || (stringp = strchr(buffer, '\n')))
+ *stringp = '\0';
+
+ stringp = buffer;
+ while ((pair = strsep(&stringp, "&"))) {
+ key = strsep(&pair, "=");
+ ast_uri_decode(key);
+ if (pair)
+ ast_uri_decode(pair);
+
+ if (!ast_strlen_zero(key)) {
+ if (prev) {
+ prev->next = ast_variable_new(key, S_OR(pair, ""), "");
+ if (prev->next)
+ prev = prev->next;
+ } else
+ prev = var = ast_variable_new(key, S_OR(pair, ""), "");
+ }
+ }
+
+ ast_free(buffer);
+ ast_free(query);
+ return var;
+}
+
+/*!
+ * \brief Excute an Select query and return ast_config list
+ * \param url
+ * \param unused
+ * \param ap list containing one or more field/operator/value set.
+ *
+ * \retval struct ast_config pointer on success
+ * \retval NULL on failure
+*/
+static struct ast_config *realtime_multi_curl(const char *url, const char *unused, va_list ap)
+{
+ struct ast_str *query;
+ char buf1[200], buf2[200];
+ const char *newparam, *newval;
+ char *stringp, *line, *pair, *key, *initfield = NULL;
+ int i, EncodeSpecialChars = 1;
+ struct ast_variable *var=NULL;
+ struct ast_config *cfg=NULL;
+ struct ast_category *cat=NULL;
+ char *buffer;
+
+ if (!ast_custom_function_find("CURL")) {
+ ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
+ return NULL;
+ }
+
+ if (!(query = ast_str_create(1000)))
+ return NULL;
+
+ if (!(buffer = ast_malloc(256000))) {
+ ast_free(query);
+ return NULL;
+ }
+
+ ast_str_set(&query, 0, "${CURL(%s/multi,", url);
+
+ for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
+ newval = va_arg(ap, const char *);
+ if (i == 0)
+ initfield = ast_strdupa(newparam);
+ ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
+ ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
+ }
+ va_end(ap);
+
+ ast_str_append(&query, 0, ")}");
+
+ /* Do the CURL query */
+ pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
+
+ if (!(cfg = ast_config_new()))
+ goto exit_multi;
+
+ /* Line oriented output */
+ stringp = buffer;
+ while ((line = strsep(&stringp, "\r\n"))) {
+ if (ast_strlen_zero(line))
+ continue;
+
+ if (!(cat = ast_category_new("", "", 99999)))
+ continue;
+
+ while ((pair = strsep(&line, "&"))) {
+ key = strsep(&pair, "=");
+ ast_uri_decode(key);
+ if (pair)
+ ast_uri_decode(pair);
+
+ if (!strcasecmp(key, initfield) && pair)
+ ast_category_rename(cat, pair);
+
+ if (!ast_strlen_zero(key)) {
+ var = ast_variable_new(key, S_OR(pair, ""), "");
+ ast_variable_append(cat, var);
+ }
+ }
+ ast_category_append(cfg, cat);
+ }
+
+exit_multi:
+ ast_free(buffer);
+ ast_free(query);
+ return cfg;
+}
+
+/*!
+ * \brief Execute an UPDATE query
+ * \param url
+ * \param unused
+ * \param keyfield where clause field
+ * \param lookup value of field for where clause
+ * \param ap list containing one or more field/value set(s).
+ *
+ * Update a database table, prepare the sql statement using keyfield and lookup
+ * control the number of records to change. All values to be changed are stored in ap list.
+ * Sub-in the values to the prepared statement and execute it.
+ *
+ * \retval number of rows affected
+ * \retval -1 on failure
+*/
+static int update_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
+{
+ struct ast_str *query;
+ char buf1[200], buf2[200];
+ const char *newparam, *newval;
+ char *stringp;
+ int i, rowcount = -1;
+ const int EncodeSpecialChars = 1;
+ char *buffer;
+
+ if (!ast_custom_function_find("CURL")) {
+ ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
+ return -1;
+ }
+
+ if (!(query = ast_str_create(1000)))
+ return -1;
+
+ if (!(buffer = ast_malloc(100))) {
+ ast_free(query);
+ return -1;
+ }
+
+ ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars);
+ ast_str_set(&query, 0, "${CURL(%s/update?%s=%s,", url, buf1, buf2);
+
+ for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
+ newval = va_arg(ap, const char *);
+ ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
+ ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
+ }
+ va_end(ap);
+
+ ast_str_append(&query, 0, ")}");
+ pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
+
+ /* Line oriented output */
+ stringp = buffer;
+ while (*stringp <= ' ')
+ stringp++;
+ sscanf(stringp, "%d", &rowcount);
+
+ ast_free(buffer);
+ ast_free(query);
+
+ if (rowcount >= 0)
+ return (int)rowcount;
+
+ return -1;
+}
+
+/*!
+ * \brief Execute an INSERT query
+ * \param database
+ * \param table
+ * \param ap list containing one or more field/value set(s)
+ *
+ * Insert a new record into database table, prepare the sql statement.
+ * All values to be changed are stored in ap list.
+ * Sub-in the values to the prepared statement and execute it.
+ *
+ * \retval number of rows affected
+ * \retval -1 on failure
+*/
+static int store_curl(const char *url, const char *unused, va_list ap)
+{
+ struct ast_str *query;
+ char buf1[200], buf2[200];
+ const char *newparam, *newval;
+ char *stringp;
+ int i, rowcount = -1;
+ const int EncodeSpecialChars = 1;
+ char *buffer;
+
+ if (!ast_custom_function_find("CURL")) {
+ ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
+ return -1;
+ }
+
+ if (!(query = ast_str_create(1000)))
+ return -1;
+
+ if (!(buffer = ast_malloc(100))) {
+ ast_free(query);
+ return -1;
+ }
+
+ ast_str_set(&query, 0, "${CURL(%s/store,", url);
+
+ for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
+ newval = va_arg(ap, const char *);
+ ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
+ ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
+ }
+ va_end(ap);
+
+ ast_str_append(&query, 0, ")}");
+ pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
+
+ stringp = buffer;
+ while (*stringp <= ' ')
+ stringp++;
+ sscanf(stringp, "%d", &rowcount);
+
+ ast_free(buffer);
+ ast_free(query);
+
+ if (rowcount >= 0)
+ return (int)rowcount;
+
+ return -1;
+}
+
+/*!
+ * \brief Execute an DELETE query
+ * \param url
+ * \param unused
+ * \param keyfield where clause field
+ * \param lookup value of field for where clause
+ * \param ap list containing one or more field/value set(s)
+ *
+ * Delete a row from a database table, prepare the sql statement using keyfield and lookup
+ * control the number of records to change. Additional params to match rows are stored in ap list.
+ * Sub-in the values to the prepared statement and execute it.
+ *
+ * \retval number of rows affected
+ * \retval -1 on failure
+*/
+static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
+{
+ struct ast_str *query;
+ char buf1[200], buf2[200];
+ const char *newparam, *newval;
+ char *stringp;
+ int i, rowcount = -1;
+ const int EncodeSpecialChars = 1;
+ char *buffer;
+
+ if (!ast_custom_function_find("CURL")) {
+ ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
+ return -1;
+ }
+
+ if (!(query = ast_str_create(1000)))
+ return -1;
+
+ if (!(buffer = ast_malloc(100))) {
+ ast_free(query);
+ return -1;
+ }
+
+ ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars);
+ ast_str_set(&query, 0, "${CURL(%s/destroy,%s=%s&", url, buf1, buf2);
+
+ for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
+ newval = va_arg(ap, const char *);
+ ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
+ ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
+ }
+ va_end(ap);
+
+ ast_str_append(&query, 0, ")}");
+ pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
+
+ /* Line oriented output */
+ stringp = buffer;
+ while (*stringp <= ' ')
+ stringp++;
+ sscanf(stringp, "%d", &rowcount);
+
+ ast_free(buffer);
+ ast_free(query);
+
+ if (rowcount >= 0)
+ return (int)rowcount;
+
+ return -1;
+}
+
+
+static struct ast_config *config_curl(const char *url, const char *unused, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl)
+{
+ struct ast_str *query;
+ char buf1[200];
+ char *stringp, *line, *pair, *key;
+ int EncodeSpecialChars = 1, last_cat_metric = -1, cat_metric = -1;
+ struct ast_category *cat=NULL;
+ char *buffer, *cur_cat = "";
+ char *category = "", *var_name = "", *var_val = "";
+ struct ast_flags loader_flags = { 0 };
+
+ if (!ast_custom_function_find("CURL")) {
+ ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
+ return NULL;
+ }
+
+ if (!(query = ast_str_create(1000)))
+ return NULL;
+
+ if (!(buffer = ast_malloc(256000))) {
+ ast_free(query);
+ return NULL;
+ }
+
+ ast_uri_encode(file, buf1, sizeof(buf1), EncodeSpecialChars);
+ ast_str_set(&query, 0, "${CURL(%s/static?file=%s)}", url, buf1);
+
+ /* Do the CURL query */
+ pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
+
+ /* Line oriented output */
+ stringp = buffer;
+ cat = ast_config_get_current_category(cfg);
+
+ while ((line = strsep(&stringp, "\r\n"))) {
+ if (ast_strlen_zero(line))
+ continue;
+
+ while ((pair = strsep(&line, "&"))) {
+ key = strsep(&pair, "=");
+ ast_uri_decode(key);
+ if (pair)
+ ast_uri_decode(pair);
+
+ if (!strcasecmp(key, "category"))
+ category = S_OR(pair, "");
+ else if (!strcasecmp(key, "var_name"))
+ var_name = S_OR(pair, "");
+ else if (!strcasecmp(key, "var_val"))
+ var_val = S_OR(pair, "");
+ else if (!strcasecmp(key, "cat_metric"))
+ cat_metric = pair ? atoi(pair) : 0;
+ }
+
+ if (!strcmp(var_name, "#include")) {
+ if (!ast_config_internal_load(var_val, cfg, loader_flags, ""))
+ return NULL;
+ }
+
+ if (strcmp(category, cur_cat) || last_cat_metric != cat_metric) {
+ if (!(cat = ast_category_new(category, "", 99999)))
+ break;
+ cur_cat = category;
+ last_cat_metric = cat_metric;
+ ast_category_append(cfg, cat);
+ }
+ ast_variable_append(cat, ast_variable_new(var_name, var_val, ""));
+ }
+
+ ast_free(buffer);
+ ast_free(query);
+ return cfg;
+}
+
+static struct ast_config_engine curl_engine = {
+ .name = "curl",
+ .load_func = config_curl,
+ .realtime_func = realtime_curl,
+ .realtime_multi_func = realtime_multi_curl,
+ .store_func = store_curl,
+ .destroy_func = destroy_curl,
+ .update_func = update_curl
+};
+
+static int unload_module (void)
+{
+ ast_config_engine_deregister(&curl_engine);
+ ast_verb(1, "res_config_curl unloaded.\n");
+ return 0;
+}
+
+static int load_module (void)
+{
+ ast_config_engine_register(&curl_engine);
+ ast_verb(1, "res_config_curl loaded.\n");
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Curl configuration");
diff --git a/trunk/res/res_config_odbc.c b/trunk/res/res_config_odbc.c
new file mode 100644
index 000000000..57b1ba528
--- /dev/null
+++ b/trunk/res/res_config_odbc.c
@@ -0,0 +1,739 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.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 odbc+odbc plugin for portable configuration engine
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * \arg http://www.unixodbc.org
+ */
+
+/*** MODULEINFO
+ <depend>unixodbc</depend>
+ <depend>ltdl</depend>
+ <depend>res_odbc</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/res_odbc.h"
+#include "asterisk/utils.h"
+
+struct custom_prepare_struct {
+ const char *sql;
+ const char *extra;
+ va_list ap;
+};
+
+static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
+{
+ int res, x = 1;
+ struct custom_prepare_struct *cps = data;
+ const char *newparam, *newval;
+ SQLHSTMT stmt;
+ va_list ap;
+
+ va_copy(ap, cps->ap);
+
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+ return NULL;
+ }
+
+ res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+
+ while ((newparam = va_arg(ap, const char *))) {
+ newval = va_arg(ap, const char *);
+ SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
+ }
+ va_end(ap);
+
+ if (!ast_strlen_zero(cps->extra))
+ SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
+ return stmt;
+}
+
+/*!
+ * \brief Excute an SQL query and return ast_variable list
+ * \param database
+ * \param table
+ * \param ap list containing one or more field/operator/value set.
+ *
+ * Select database and preform query on table, prepare the sql statement
+ * Sub-in the values to the prepared statement and execute it. Return results
+ * as a ast_variable list.
+ *
+ * \retval var on success
+ * \retval NULL on failure
+*/
+static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
+{
+ struct odbc_obj *obj;
+ SQLHSTMT stmt;
+ char sql[1024];
+ char coltitle[256];
+ char rowdata[2048];
+ char *op;
+ const char *newparam, *newval;
+ char *stringp;
+ char *chunk;
+ SQLSMALLINT collen;
+ int res;
+ int x;
+ struct ast_variable *var=NULL, *prev=NULL;
+ SQLULEN colsize;
+ SQLSMALLINT colcount=0;
+ SQLSMALLINT datatype;
+ SQLSMALLINT decimaldigits;
+ SQLSMALLINT nullable;
+ SQLLEN indicator;
+ va_list aq;
+ struct custom_prepare_struct cps = { .sql = sql };
+
+ va_copy(cps.ap, ap);
+ va_copy(aq, ap);
+
+ if (!table)
+ return NULL;
+
+ obj = ast_odbc_request_obj(database, 0);
+
+ if (!obj) {
+ ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
+ return NULL;
+ }
+
+ newparam = va_arg(aq, const char *);
+ if (!newparam)
+ return NULL;
+ newval = va_arg(aq, const char *);
+ op = !strchr(newparam, ' ') ? " =" : "";
+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
+ strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+ while((newparam = va_arg(aq, const char *))) {
+ op = !strchr(newparam, ' ') ? " =" : "";
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
+ strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+ newval = va_arg(aq, const char *);
+ }
+ va_end(aq);
+
+ stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+
+ if (!stmt) {
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ res = SQLNumResultCols(stmt, &colcount);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ res = SQLFetch(stmt);
+ if (res == SQL_NO_DATA) {
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+ for (x = 0; x < colcount; x++) {
+ rowdata[0] = '\0';
+ collen = sizeof(coltitle);
+ res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
+ &datatype, &colsize, &decimaldigits, &nullable);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+ if (var)
+ ast_variables_destroy(var);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ indicator = 0;
+ res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
+ if (indicator == SQL_NULL_DATA)
+ rowdata[0] = '\0';
+ else if (ast_strlen_zero(rowdata)) {
+ /* Because we encode the empty string for a NULL, we will encode
+ * actual empty strings as a string containing a single whitespace. */
+ ast_copy_string(rowdata, " ", sizeof(rowdata));
+ }
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ if (var)
+ ast_variables_destroy(var);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+ stringp = rowdata;
+ while(stringp) {
+ chunk = strsep(&stringp, ";");
+ if (!ast_strlen_zero(ast_strip(chunk))) {
+ if (prev) {
+ prev->next = ast_variable_new(coltitle, chunk, "");
+ if (prev->next)
+ prev = prev->next;
+ } else
+ prev = var = ast_variable_new(coltitle, chunk, "");
+ }
+ }
+ }
+
+
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return var;
+}
+
+/*!
+ * \brief Excute an Select query and return ast_config list
+ * \param database
+ * \param table
+ * \param ap list containing one or more field/operator/value set.
+ *
+ * Select database and preform query on table, prepare the sql statement
+ * Sub-in the values to the prepared statement and execute it.
+ * Execute this prepared query against several ODBC connected databases.
+ * Return results as an ast_config variable.
+ *
+ * \retval var on success
+ * \retval NULL on failure
+*/
+static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
+{
+ struct odbc_obj *obj;
+ SQLHSTMT stmt;
+ char sql[1024];
+ char coltitle[256];
+ char rowdata[2048];
+ const char *initfield=NULL;
+ char *op;
+ const char *newparam, *newval;
+ char *stringp;
+ char *chunk;
+ SQLSMALLINT collen;
+ int res;
+ int x;
+ struct ast_variable *var=NULL;
+ struct ast_config *cfg=NULL;
+ struct ast_category *cat=NULL;
+ SQLULEN colsize;
+ SQLSMALLINT colcount=0;
+ SQLSMALLINT datatype;
+ SQLSMALLINT decimaldigits;
+ SQLSMALLINT nullable;
+ SQLLEN indicator;
+ struct custom_prepare_struct cps = { .sql = sql };
+ va_list aq;
+
+ va_copy(cps.ap, ap);
+ va_copy(aq, ap);
+
+ if (!table)
+ return NULL;
+
+ obj = ast_odbc_request_obj(database, 0);
+ if (!obj)
+ return NULL;
+
+ newparam = va_arg(aq, const char *);
+ if (!newparam) {
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+ initfield = ast_strdupa(newparam);
+ if ((op = strchr(initfield, ' ')))
+ *op = '\0';
+ newval = va_arg(aq, const char *);
+ op = !strchr(newparam, ' ') ? " =" : "";
+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
+ strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+ while((newparam = va_arg(aq, const char *))) {
+ op = !strchr(newparam, ' ') ? " =" : "";
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
+ strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+ newval = va_arg(aq, const char *);
+ }
+ if (initfield)
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
+ va_end(aq);
+
+ stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+
+ if (!stmt) {
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ res = SQLNumResultCols(stmt, &colcount);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ cfg = ast_config_new();
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
+ var = NULL;
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ continue;
+ }
+ cat = ast_category_new("","",99999);
+ if (!cat) {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ continue;
+ }
+ for (x=0;x<colcount;x++) {
+ rowdata[0] = '\0';
+ collen = sizeof(coltitle);
+ res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
+ &datatype, &colsize, &decimaldigits, &nullable);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+ ast_category_destroy(cat);
+ continue;
+ }
+
+ indicator = 0;
+ res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
+ if (indicator == SQL_NULL_DATA)
+ continue;
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ ast_category_destroy(cat);
+ continue;
+ }
+ stringp = rowdata;
+ while(stringp) {
+ chunk = strsep(&stringp, ";");
+ if (!ast_strlen_zero(ast_strip(chunk))) {
+ if (initfield && !strcmp(initfield, coltitle))
+ ast_category_rename(cat, chunk);
+ var = ast_variable_new(coltitle, chunk, "");
+ ast_variable_append(cat, var);
+ }
+ }
+ }
+ ast_category_append(cfg, cat);
+ }
+
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return cfg;
+}
+
+/*!
+ * \brief Excute an UPDATE query
+ * \param database
+ * \param table
+ * \param keyfield where clause field
+ * \param lookup value of field for where clause
+ * \param ap list containing one or more field/value set(s).
+ *
+ * Update a database table, prepare the sql statement using keyfield and lookup
+ * control the number of records to change. All values to be changed are stored in ap list.
+ * Sub-in the values to the prepared statement and execute it.
+ *
+ * \retval number of rows affected
+ * \retval -1 on failure
+*/
+static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
+{
+ struct odbc_obj *obj;
+ SQLHSTMT stmt;
+ char sql[256];
+ SQLLEN rowcount=0;
+ const char *newparam, *newval;
+ int res;
+ va_list aq;
+ struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
+
+ va_copy(cps.ap, ap);
+ va_copy(aq, ap);
+
+ if (!table)
+ return -1;
+
+ obj = ast_odbc_request_obj(database, 0);
+ if (!obj)
+ return -1;
+
+ newparam = va_arg(aq, const char *);
+ if (!newparam) {
+ ast_odbc_release_obj(obj);
+ return -1;
+ }
+ newval = va_arg(aq, const char *);
+ snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
+ while((newparam = va_arg(aq, const char *))) {
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
+ newval = va_arg(aq, const char *);
+ }
+ va_end(aq);
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
+
+ stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+
+ if (!stmt) {
+ ast_odbc_release_obj(obj);
+ return -1;
+ }
+
+ res = SQLRowCount(stmt, &rowcount);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+ return -1;
+ }
+
+ if (rowcount >= 0)
+ return (int)rowcount;
+
+ return -1;
+}
+
+/*!
+ * \brief Excute an INSERT query
+ * \param database
+ * \param table
+ * \param ap list containing one or more field/value set(s)
+ *
+ * Insert a new record into database table, prepare the sql statement.
+ * All values to be changed are stored in ap list.
+ * Sub-in the values to the prepared statement and execute it.
+ *
+ * \retval number of rows affected
+ * \retval -1 on failure
+*/
+static int store_odbc(const char *database, const char *table, va_list ap)
+{
+ struct odbc_obj *obj;
+ SQLHSTMT stmt;
+ char sql[256];
+ char keys[256];
+ char vals[256];
+ SQLLEN rowcount=0;
+ const char *newparam, *newval;
+ int res;
+ va_list aq;
+ struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
+
+ va_copy(cps.ap, ap);
+ va_copy(aq, ap);
+
+ if (!table)
+ return -1;
+
+ obj = ast_odbc_request_obj(database, 0);
+ if (!obj)
+ return -1;
+
+ newparam = va_arg(aq, const char *);
+ if (!newparam) {
+ ast_odbc_release_obj(obj);
+ return -1;
+ }
+ newval = va_arg(aq, const char *);
+ snprintf(keys, sizeof(keys), "%s", newparam);
+ ast_copy_string(vals, "?", sizeof(vals));
+ while ((newparam = va_arg(aq, const char *))) {
+ snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
+ snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
+ newval = va_arg(aq, const char *);
+ }
+ va_end(aq);
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
+
+ stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+
+ if (!stmt) {
+ ast_odbc_release_obj(obj);
+ return -1;
+ }
+
+ res = SQLRowCount(stmt, &rowcount);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+ return -1;
+ }
+
+ if (rowcount >= 0)
+ return (int)rowcount;
+
+ return -1;
+}
+
+/*!
+ * \brief Excute an DELETE query
+ * \param database
+ * \param table
+ * \param keyfield where clause field
+ * \param lookup value of field for where clause
+ * \param ap list containing one or more field/value set(s)
+ *
+ * Delete a row from a database table, prepare the sql statement using keyfield and lookup
+ * control the number of records to change. Additional params to match rows are stored in ap list.
+ * Sub-in the values to the prepared statement and execute it.
+ *
+ * \retval number of rows affected
+ * \retval -1 on failure
+*/
+static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
+{
+ struct odbc_obj *obj;
+ SQLHSTMT stmt;
+ char sql[256];
+ SQLLEN rowcount=0;
+ const char *newparam, *newval;
+ int res;
+ va_list aq;
+ struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
+
+ va_copy(cps.ap, ap);
+ va_copy(aq, ap);
+
+ if (!table)
+ return -1;
+
+ obj = ast_odbc_request_obj(database, 0);
+ if (!obj)
+ return -1;
+
+ snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
+ while((newparam = va_arg(aq, const char *))) {
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
+ newval = va_arg(aq, const char *);
+ }
+ va_end(aq);
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
+
+ stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
+
+ if (!stmt) {
+ ast_odbc_release_obj(obj);
+ return -1;
+ }
+
+ res = SQLRowCount(stmt, &rowcount);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
+ return -1;
+ }
+
+ if (rowcount >= 0)
+ return (int)rowcount;
+
+ return -1;
+}
+
+
+struct config_odbc_obj {
+ char *sql;
+ unsigned long cat_metric;
+ char category[128];
+ char var_name[128];
+ char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
+ SQLLEN err;
+};
+
+static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
+{
+ struct config_odbc_obj *q = data;
+ SQLHSTMT sth;
+ int res;
+
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_verb(4, "Failure in AllocStatement %d\n", res);
+ return NULL;
+ }
+
+ res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_verb(4, "Error in PREPARE %d\n", res);
+ SQLFreeHandle(SQL_HANDLE_STMT, sth);
+ return NULL;
+ }
+
+ SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
+ SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
+ SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
+ SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
+
+ return sth;
+}
+
+static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl)
+{
+ struct ast_variable *new_v;
+ struct ast_category *cur_cat;
+ int res = 0;
+ struct odbc_obj *obj;
+ char sqlbuf[1024] = "";
+ char *sql = sqlbuf;
+ size_t sqlleft = sizeof(sqlbuf);
+ unsigned int last_cat_metric = 0;
+ SQLSMALLINT rowcount = 0;
+ SQLHSTMT stmt;
+ char last[128] = "";
+ struct config_odbc_obj q;
+ struct ast_flags loader_flags = { 0 };
+
+ memset(&q, 0, sizeof(q));
+
+ if (!file || !strcmp (file, "res_config_odbc.conf"))
+ return NULL; /* cant configure myself with myself ! */
+
+ obj = ast_odbc_request_obj(database, 0);
+ if (!obj)
+ return NULL;
+
+ ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
+ ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
+ ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
+ q.sql = sqlbuf;
+
+ stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
+
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ res = SQLNumResultCols(stmt, &rowcount);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+
+ if (!rowcount) {
+ ast_log(LOG_NOTICE, "found nothing\n");
+ ast_odbc_release_obj(obj);
+ return cfg;
+ }
+
+ cur_cat = ast_config_get_current_category(cfg);
+
+ while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
+ if (!strcmp (q.var_name, "#include")) {
+ if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "")) {
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return NULL;
+ }
+ continue;
+ }
+ if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
+ cur_cat = ast_category_new(q.category, "", 99999);
+ if (!cur_cat) {
+ ast_log(LOG_WARNING, "Out of memory!\n");
+ break;
+ }
+ strcpy(last, q.category);
+ last_cat_metric = q.cat_metric;
+ ast_category_append(cfg, cur_cat);
+ }
+
+ new_v = ast_variable_new(q.var_name, q.var_val, "");
+ ast_variable_append(cur_cat, new_v);
+ }
+
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ return cfg;
+}
+
+static struct ast_config_engine odbc_engine = {
+ .name = "odbc",
+ .load_func = config_odbc,
+ .realtime_func = realtime_odbc,
+ .realtime_multi_func = realtime_multi_odbc,
+ .store_func = store_odbc,
+ .destroy_func = destroy_odbc,
+ .update_func = update_odbc
+};
+
+static int unload_module (void)
+{
+ ast_config_engine_deregister(&odbc_engine);
+ ast_verb(1, "res_config_odbc unloaded.\n");
+ return 0;
+}
+
+static int load_module (void)
+{
+ ast_config_engine_register(&odbc_engine);
+ ast_verb(1, "res_config_odbc loaded.\n");
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime ODBC configuration",
+ .load = load_module,
+ .unload = unload_module,
+ );
diff --git a/trunk/res/res_config_pgsql.c b/trunk/res/res_config_pgsql.c
new file mode 100644
index 000000000..81da43456
--- /dev/null
+++ b/trunk/res/res_config_pgsql.c
@@ -0,0 +1,1008 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (C) 1999-2005, Digium, Inc.
+ *
+ * Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
+ * Mark Spencer <markster@digium.com> - Asterisk Author
+ * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
+ *
+ * res_config_pgsql.c <PostgreSQL plugin for RealTime configuration engine>
+ *
+ * v1.0 - (07-11-05) - Initial version based on res_config_mysql v2.0
+ */
+
+/*! \file
+ *
+ * \brief PostgreSQL plugin for Asterisk RealTime Architecture
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
+ *
+ * \arg http://www.postgresql.org
+ */
+
+/*** MODULEINFO
+ <depend>pgsql</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <libpq-fe.h> /* PostgreSQL */
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+
+AST_MUTEX_DEFINE_STATIC(pgsql_lock);
+
+#define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
+
+PGconn *pgsqlConn = NULL;
+
+#define MAX_DB_OPTION_SIZE 64
+
+static char dbhost[MAX_DB_OPTION_SIZE] = "";
+static char dbuser[MAX_DB_OPTION_SIZE] = "";
+static char dbpass[MAX_DB_OPTION_SIZE] = "";
+static char dbname[MAX_DB_OPTION_SIZE] = "";
+static char dbsock[MAX_DB_OPTION_SIZE] = "";
+static int dbport = 5432;
+static time_t connect_time = 0;
+
+static int parse_config(int reload);
+static int pgsql_reconnect(const char *database);
+static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry cli_realtime[] = {
+ AST_CLI_DEFINE(handle_cli_realtime_pgsql_status, "Shows connection information for the PostgreSQL RealTime driver"),
+};
+
+static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap)
+{
+ PGresult *result = NULL;
+ int num_rows = 0, pgerror;
+ char sql[256], escapebuf[513];
+ char *stringp;
+ char *chunk;
+ char *op;
+ const char *newparam, *newval;
+ struct ast_variable *var = NULL, *prev = NULL;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
+ return NULL;
+ }
+
+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
+ newparam = va_arg(ap, const char *);
+ newval = va_arg(ap, const char *);
+ if (!newparam || !newval) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ };
+ return NULL;
+ }
+
+ /* Create the first part of the query using the first parameter/value pairs we just extracted
+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
+ op = strchr(newparam, ' ') ? "" : " =";
+
+ PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
+ if (pgerror) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ va_end(ap);
+ return NULL;
+ }
+
+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
+ escapebuf);
+ while ((newparam = va_arg(ap, const char *))) {
+ newval = va_arg(ap, const char *);
+ if (!strchr(newparam, ' '))
+ op = " =";
+ else
+ op = "";
+
+ PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
+ if (pgerror) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ va_end(ap);
+ return NULL;
+ }
+
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
+ op, escapebuf);
+ }
+ va_end(ap);
+
+ /* We now have our complete statement; Lets connect to the server and execute it. */
+ ast_mutex_lock(&pgsql_lock);
+ if (!pgsql_reconnect(database)) {
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ }
+
+ if (!(result = PQexec(pgsqlConn, sql))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ } else {
+ ExecStatusType result_status = PQresultStatus(result);
+ if (result_status != PGRES_COMMAND_OK
+ && result_status != PGRES_TUPLES_OK
+ && result_status != PGRES_NONFATAL_ERROR) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
+ PQresultErrorMessage(result), PQresStatus(result_status));
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ }
+ }
+
+ ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql);
+
+ if ((num_rows = PQntuples(result)) > 0) {
+ int i = 0;
+ int rowIndex = 0;
+ int numFields = PQnfields(result);
+ char **fieldnames = NULL;
+
+ ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
+
+ if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
+ ast_mutex_unlock(&pgsql_lock);
+ PQclear(result);
+ return NULL;
+ }
+ for (i = 0; i < numFields; i++)
+ fieldnames[i] = PQfname(result, i);
+ for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
+ for (i = 0; i < numFields; i++) {
+ stringp = PQgetvalue(result, rowIndex, i);
+ while (stringp) {
+ chunk = strsep(&stringp, ";");
+ if (!ast_strlen_zero(ast_strip(chunk))) {
+ if (prev) {
+ prev->next = ast_variable_new(fieldnames[i], chunk, "");
+ if (prev->next) {
+ prev = prev->next;
+ }
+ } else {
+ prev = var = ast_variable_new(fieldnames[i], chunk, "");
+ }
+ }
+ }
+ }
+ }
+ ast_free(fieldnames);
+ } else {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
+ }
+
+ ast_mutex_unlock(&pgsql_lock);
+ PQclear(result);
+
+ return var;
+}
+
+static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
+{
+ PGresult *result = NULL;
+ int num_rows = 0, pgerror;
+ char sql[256], escapebuf[513];
+ const char *initfield = NULL;
+ char *stringp;
+ char *chunk;
+ char *op;
+ const char *newparam, *newval;
+ struct ast_variable *var = NULL;
+ struct ast_config *cfg = NULL;
+ struct ast_category *cat = NULL;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
+ return NULL;
+ }
+
+ if (!(cfg = ast_config_new()))
+ return NULL;
+
+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
+ newparam = va_arg(ap, const char *);
+ newval = va_arg(ap, const char *);
+ if (!newparam || !newval) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ };
+ return NULL;
+ }
+
+ initfield = ast_strdupa(newparam);
+ if ((op = strchr(initfield, ' '))) {
+ *op = '\0';
+ }
+
+ /* Create the first part of the query using the first parameter/value pairs we just extracted
+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
+
+ if (!strchr(newparam, ' '))
+ op = " =";
+ else
+ op = "";
+
+ PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
+ if (pgerror) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ va_end(ap);
+ return NULL;
+ }
+
+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
+ escapebuf);
+ while ((newparam = va_arg(ap, const char *))) {
+ newval = va_arg(ap, const char *);
+ if (!strchr(newparam, ' '))
+ op = " =";
+ else
+ op = "";
+
+ PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
+ if (pgerror) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ va_end(ap);
+ return NULL;
+ }
+
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
+ op, escapebuf);
+ }
+
+ if (initfield) {
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
+ }
+
+ va_end(ap);
+
+ /* We now have our complete statement; Lets connect to the server and execute it. */
+ ast_mutex_lock(&pgsql_lock);
+ if (!pgsql_reconnect(database)) {
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ }
+
+ if (!(result = PQexec(pgsqlConn, sql))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ } else {
+ ExecStatusType result_status = PQresultStatus(result);
+ if (result_status != PGRES_COMMAND_OK
+ && result_status != PGRES_TUPLES_OK
+ && result_status != PGRES_NONFATAL_ERROR) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
+ PQresultErrorMessage(result), PQresStatus(result_status));
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ }
+ }
+
+ ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql);
+
+ if ((num_rows = PQntuples(result)) > 0) {
+ int numFields = PQnfields(result);
+ int i = 0;
+ int rowIndex = 0;
+ char **fieldnames = NULL;
+
+ ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
+
+ if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
+ ast_mutex_unlock(&pgsql_lock);
+ PQclear(result);
+ return NULL;
+ }
+ for (i = 0; i < numFields; i++)
+ fieldnames[i] = PQfname(result, i);
+
+ for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
+ var = NULL;
+ if (!(cat = ast_category_new("","",99999)))
+ continue;
+ for (i = 0; i < numFields; i++) {
+ stringp = PQgetvalue(result, rowIndex, i);
+ while (stringp) {
+ chunk = strsep(&stringp, ";");
+ if (!ast_strlen_zero(ast_strip(chunk))) {
+ if (initfield && !strcmp(initfield, fieldnames[i])) {
+ ast_category_rename(cat, chunk);
+ }
+ var = ast_variable_new(fieldnames[i], chunk, "");
+ ast_variable_append(cat, var);
+ }
+ }
+ }
+ ast_category_append(cfg, cat);
+ }
+ ast_free(fieldnames);
+ } else {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
+ }
+
+ ast_mutex_unlock(&pgsql_lock);
+ PQclear(result);
+
+ return cfg;
+}
+
+static int update_pgsql(const char *database, const char *table, const char *keyfield,
+ const char *lookup, va_list ap)
+{
+ PGresult *result = NULL;
+ int numrows = 0, pgerror;
+ char sql[256], escapebuf[513];
+ const char *newparam, *newval;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
+ return -1;
+ }
+
+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
+ newparam = va_arg(ap, const char *);
+ newval = va_arg(ap, const char *);
+ if (!newparam || !newval) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ };
+ return -1;
+ }
+
+ /* Create the first part of the query using the first parameter/value pairs we just extracted
+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
+
+ PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
+ if (pgerror) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ va_end(ap);
+ return -1;
+ }
+ snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, escapebuf);
+
+ while ((newparam = va_arg(ap, const char *))) {
+ newval = va_arg(ap, const char *);
+
+ PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror);
+ if (pgerror) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
+ va_end(ap);
+ return -1;
+ }
+
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam,
+ escapebuf);
+ }
+ va_end(ap);
+
+ PQescapeStringConn(pgsqlConn, escapebuf, lookup, (sizeof(escapebuf) - 1) / 2, &pgerror);
+ if (pgerror) {
+ ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
+ va_end(ap);
+ return -1;
+ }
+
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield,
+ escapebuf);
+
+ ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", sql);
+
+ /* We now have our complete statement; Lets connect to the server and execute it. */
+ ast_mutex_lock(&pgsql_lock);
+ if (!pgsql_reconnect(database)) {
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+
+ if (!(result = PQexec(pgsqlConn, sql))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ } else {
+ ExecStatusType result_status = PQresultStatus(result);
+ if (result_status != PGRES_COMMAND_OK
+ && result_status != PGRES_TUPLES_OK
+ && result_status != PGRES_NONFATAL_ERROR) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
+ PQresultErrorMessage(result), PQresStatus(result_status));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+ }
+
+ numrows = atoi(PQcmdTuples(result));
+ ast_mutex_unlock(&pgsql_lock);
+
+ ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, table);
+
+ /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
+ * An integer greater than zero indicates the number of rows affected
+ * Zero indicates that no records were updated
+ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
+ */
+
+ if (numrows >= 0)
+ return (int) numrows;
+
+ return -1;
+}
+
+static int store_pgsql(const char *database, const char *table, va_list ap)
+{
+ PGresult *result = NULL;
+ Oid insertid;
+ char sql[256];
+ char params[256];
+ char vals[256];
+ char buf[256];
+ int pgresult;
+ const char *newparam, *newval;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
+ return -1;
+ }
+
+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
+ newparam = va_arg(ap, const char *);
+ newval = va_arg(ap, const char *);
+ if (!newparam || !newval) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ };
+ return -1;
+ }
+
+ /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
+ ast_mutex_lock(&pgsql_lock);
+ if (!pgsql_reconnect(database)) {
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+
+ /* Create the first part of the query using the first parameter/value pairs we just extracted
+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
+ PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
+ snprintf(params, sizeof(params), "%s", buf);
+ PQescapeStringConn(pgsqlConn, buf, newval, sizeof(newval), &pgresult);
+ snprintf(vals, sizeof(vals), "'%s'", buf);
+ while ((newparam = va_arg(ap, const char *))) {
+ newval = va_arg(ap, const char *);
+ PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
+ snprintf(params + strlen(params), sizeof(params) - strlen(params), ", %s", buf);
+ PQescapeStringConn(pgsqlConn, buf, newval, sizeof(newval), &pgresult);
+ snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", '%s'", buf);
+ }
+ va_end(ap);
+ snprintf(sql, sizeof(sql), "INSERT INTO (%s) VALUES (%s)", params, vals);
+
+ ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", sql);
+
+ if (!(result = PQexec(pgsqlConn, sql))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ } else {
+ ExecStatusType result_status = PQresultStatus(result);
+ if (result_status != PGRES_COMMAND_OK
+ && result_status != PGRES_TUPLES_OK
+ && result_status != PGRES_NONFATAL_ERROR) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
+ PQresultErrorMessage(result), PQresStatus(result_status));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+ }
+
+ insertid = PQoidValue(result);
+ ast_mutex_unlock(&pgsql_lock);
+
+ ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
+
+ /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
+ * An integer greater than zero indicates the number of rows affected
+ * Zero indicates that no records were updated
+ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
+ */
+
+ if (insertid >= 0)
+ return (int) insertid;
+
+ return -1;
+}
+
+static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
+{
+ PGresult *result = NULL;
+ int numrows = 0;
+ int pgresult;
+ char sql[256];
+ char buf[256], buf2[256];
+ const char *newparam, *newval;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
+ return -1;
+ }
+
+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
+ /*newparam = va_arg(ap, const char *);
+ newval = va_arg(ap, const char *);
+ if (!newparam || !newval) {*/
+ if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ };
+ return -1;
+ }
+
+ /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
+ ast_mutex_lock(&pgsql_lock);
+ if (!pgsql_reconnect(database)) {
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+
+
+ /* Create the first part of the query using the first parameter/value pairs we just extracted
+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
+
+ PQescapeStringConn(pgsqlConn, buf, keyfield, sizeof(keyfield), &pgresult);
+ PQescapeStringConn(pgsqlConn, buf2, lookup, sizeof(lookup), &pgresult);
+ snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE %s = '%s'", table, buf, buf2);
+ while ((newparam = va_arg(ap, const char *))) {
+ newval = va_arg(ap, const char *);
+ PQescapeStringConn(pgsqlConn, buf, newparam, sizeof(newparam), &pgresult);
+ PQescapeStringConn(pgsqlConn, buf2, newval, sizeof(newval), &pgresult);
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s = '%s'", buf, buf2);
+ }
+ va_end(ap);
+
+ ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", sql);
+
+ if (!(result = PQexec(pgsqlConn, sql))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ } else {
+ ExecStatusType result_status = PQresultStatus(result);
+ if (result_status != PGRES_COMMAND_OK
+ && result_status != PGRES_TUPLES_OK
+ && result_status != PGRES_NONFATAL_ERROR) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
+ PQresultErrorMessage(result), PQresStatus(result_status));
+ ast_mutex_unlock(&pgsql_lock);
+ return -1;
+ }
+ }
+
+ numrows = atoi(PQcmdTuples(result));
+ ast_mutex_unlock(&pgsql_lock);
+
+ ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
+
+ /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
+ * An integer greater than zero indicates the number of rows affected
+ * Zero indicates that no records were updated
+ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
+ */
+
+ if (numrows >= 0)
+ return (int) numrows;
+
+ return -1;
+}
+
+
+static struct ast_config *config_pgsql(const char *database, const char *table,
+ const char *file, struct ast_config *cfg,
+ struct ast_flags flags, const char *suggested_incl)
+{
+ PGresult *result = NULL;
+ long num_rows;
+ struct ast_variable *new_v;
+ struct ast_category *cur_cat = NULL;
+ char sqlbuf[1024] = "";
+ char *sql = sqlbuf;
+ size_t sqlleft = sizeof(sqlbuf);
+ char last[80] = "";
+ int last_cat_metric = 0;
+
+ last[0] = '\0';
+
+ if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
+ ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
+ return NULL;
+ }
+
+ ast_build_string(&sql, &sqlleft, "SELECT category, var_name, var_val, cat_metric FROM %s ", table);
+ ast_build_string(&sql, &sqlleft, "WHERE filename='%s' and commented=0", file);
+ ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
+
+ ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", sqlbuf);
+
+ /* We now have our complete statement; Lets connect to the server and execute it. */
+ ast_mutex_lock(&pgsql_lock);
+ if (!pgsql_reconnect(database)) {
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ }
+
+ if (!(result = PQexec(pgsqlConn, sqlbuf))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ } else {
+ ExecStatusType result_status = PQresultStatus(result);
+ if (result_status != PGRES_COMMAND_OK
+ && result_status != PGRES_TUPLES_OK
+ && result_status != PGRES_NONFATAL_ERROR) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
+ ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql);
+ ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
+ PQresultErrorMessage(result), PQresStatus(result_status));
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ }
+ }
+
+ if ((num_rows = PQntuples(result)) > 0) {
+ int rowIndex = 0;
+
+ ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
+
+ for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
+ char *field_category = PQgetvalue(result, rowIndex, 0);
+ char *field_var_name = PQgetvalue(result, rowIndex, 1);
+ char *field_var_val = PQgetvalue(result, rowIndex, 2);
+ char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
+ if (!strcmp(field_var_name, "#include")) {
+ if (!ast_config_internal_load(field_var_val, cfg, flags, "")) {
+ PQclear(result);
+ ast_mutex_unlock(&pgsql_lock);
+ return NULL;
+ }
+ continue;
+ }
+
+ if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
+ cur_cat = ast_category_new(field_category, "", 99999);
+ if (!cur_cat)
+ break;
+ strcpy(last, field_category);
+ last_cat_metric = atoi(field_cat_metric);
+ ast_category_append(cfg, cur_cat);
+ }
+ new_v = ast_variable_new(field_var_name, field_var_val, "");
+ ast_variable_append(cur_cat, new_v);
+ }
+ } else {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
+ }
+
+ PQclear(result);
+ ast_mutex_unlock(&pgsql_lock);
+
+ return cfg;
+}
+
+static struct ast_config_engine pgsql_engine = {
+ .name = "pgsql",
+ .load_func = config_pgsql,
+ .realtime_func = realtime_pgsql,
+ .realtime_multi_func = realtime_multi_pgsql,
+ .store_func = store_pgsql,
+ .destroy_func = destroy_pgsql,
+ .update_func = update_pgsql
+};
+
+static int load_module(void)
+{
+ if(!parse_config(0))
+ return AST_MODULE_LOAD_DECLINE;
+
+ ast_config_engine_register(&pgsql_engine);
+ ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
+ ast_cli_register_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ /* Acquire control before doing anything to the module itself. */
+ ast_mutex_lock(&pgsql_lock);
+
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ }
+ ast_cli_unregister_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
+ ast_config_engine_deregister(&pgsql_engine);
+ ast_verb(1, "PostgreSQL RealTime unloaded.\n");
+
+ /* Unlock so something else can destroy the lock. */
+ ast_mutex_unlock(&pgsql_lock);
+
+ return 0;
+}
+
+static int reload(void)
+{
+ parse_config(1);
+
+ return 0;
+}
+
+static int parse_config(int reload)
+{
+ struct ast_config *config;
+ const char *s;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (!config) {
+ ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
+ return 0;
+ }
+
+ ast_mutex_lock(&pgsql_lock);
+
+ if (pgsqlConn) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ }
+
+ if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
+ strcpy(dbuser, "asterisk");
+ } else {
+ ast_copy_string(dbuser, s, sizeof(dbuser));
+ }
+
+ if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
+ strcpy(dbpass, "asterisk");
+ } else {
+ ast_copy_string(dbpass, s, sizeof(dbpass));
+ }
+
+ if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
+ dbhost[0] = '\0';
+ } else {
+ ast_copy_string(dbhost, s, sizeof(dbhost));
+ }
+
+ if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
+ strcpy(dbname, "asterisk");
+ } else {
+ ast_copy_string(dbname, s, sizeof(dbname));
+ }
+
+ if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
+ dbport = 5432;
+ } else {
+ dbport = atoi(s);
+ }
+
+ if (!ast_strlen_zero(dbhost)) {
+ /* No socket needed */
+ } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: No database socket found, using '/tmp/pgsql.sock' as default.\n");
+ strcpy(dbsock, "/tmp/pgsql.sock");
+ } else {
+ ast_copy_string(dbsock, s, sizeof(dbsock));
+ }
+ ast_config_destroy(config);
+
+ if (option_debug) {
+ if (!ast_strlen_zero(dbhost)) {
+ ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
+ ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
+ } else {
+ ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
+ }
+ ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
+ ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
+ ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
+ }
+
+ if (!pgsql_reconnect(NULL)) {
+ ast_log(LOG_WARNING,
+ "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
+ ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
+ }
+
+ ast_verb(2, "PostgreSQL RealTime reloaded.\n");
+
+ /* Done reloading. Release lock so others can now use driver. */
+ ast_mutex_unlock(&pgsql_lock);
+
+ return 1;
+}
+
+static int pgsql_reconnect(const char *database)
+{
+ char my_database[50];
+
+ ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
+
+ /* mutex lock should have been locked before calling this function. */
+
+ if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
+ PQfinish(pgsqlConn);
+ pgsqlConn = NULL;
+ }
+
+ if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(dbpass) && !ast_strlen_zero(my_database)) {
+ char *connInfo = NULL;
+ unsigned int size = 100 + strlen(dbhost)
+ + strlen(dbuser)
+ + strlen(dbpass)
+ + strlen(my_database);
+
+ if (!(connInfo = ast_malloc(size)))
+ return 0;
+
+ sprintf(connInfo, "host=%s port=%d dbname=%s user=%s password=%s",
+ dbhost, dbport, my_database, dbuser, dbpass);
+ ast_debug(1, "%u connInfo=%s\n", size, connInfo);
+ pgsqlConn = PQconnectdb(connInfo);
+ ast_debug(1, "%u connInfo=%s\n", size, connInfo);
+ ast_free(connInfo);
+ connInfo = NULL;
+ ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
+ if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
+ ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
+ connect_time = time(NULL);
+ return 1;
+ } else {
+ ast_log(LOG_ERROR,
+ "PostgreSQL RealTime: Failed to connect database server %s on %s. Check debug for more info.\n",
+ dbname, dbhost);
+ ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQresultErrorMessage(NULL));
+ return 0;
+ }
+ } else {
+ ast_debug(1, "PostgreSQL RealTime: Everything is fine.\n");
+ return 1;
+ }
+}
+
+static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char status[256], status2[100] = "";
+ int ctime = time(NULL) - connect_time;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "realtime pgsql status";
+ e->usage =
+ "Usage: realtime pgsql status\n"
+ " Shows connection information for the PostgreSQL RealTime driver\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
+ if (!ast_strlen_zero(dbhost))
+ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
+ else if (!ast_strlen_zero(dbsock))
+ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
+ else
+ snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
+
+ if (!ast_strlen_zero(dbuser))
+ snprintf(status2, 99, " with username %s", dbuser);
+
+ if (ctime > 31536000)
+ ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
+ status, status2, ctime / 31536000, (ctime % 31536000) / 86400,
+ (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
+ else if (ctime > 86400)
+ ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
+ status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60,
+ ctime % 60);
+ else if (ctime > 3600)
+ ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2,
+ ctime / 3600, (ctime % 3600) / 60, ctime % 60);
+ else if (ctime > 60)
+ ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60,
+ ctime % 60);
+ else
+ ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
+
+ return CLI_SUCCESS;
+ } else {
+ return CLI_FAILURE;
+ }
+}
+
+/* needs usecount semantics defined */
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PostgreSQL RealTime Configuration Driver",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload
+ );
diff --git a/trunk/res/res_config_sqlite.c b/trunk/res/res_config_sqlite.c
new file mode 100644
index 000000000..fb22009b2
--- /dev/null
+++ b/trunk/res/res_config_sqlite.c
@@ -0,0 +1,1534 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Proformatique
+ *
+ * Written by Richard Braun <rbraun@proformatique.com>
+ *
+ * Based on res_sqlite3 by Anthony Minessale II,
+ * and res_config_mysql by Matthew Boehm
+ *
+ * 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.
+ */
+
+/*!
+ * \page res_config_sqlite
+ *
+ * \section intro_sec Presentation
+ *
+ * res_config_sqlite is a module for the Asterisk Open Source PBX to
+ * support SQLite 2 databases. It can be used to fetch configuration
+ * from a database (static configuration files and/or using the Asterisk
+ * RealTime Architecture - ARA).
+ * It can also be used to log CDR entries. Finally, it can be used for simple
+ * queries in the Dialplan. Note that Asterisk already comes with a module
+ * named cdr_sqlite. There are two reasons for including it in res_config_sqlite:
+ * the first is that rewriting it was a training to learn how to write a
+ * simple module for Asterisk, the other is to have the same database open for
+ * all kinds of operations, which improves reliability and performance.
+ *
+ * There is already a module for SQLite 3 (named res_sqlite3) in the Asterisk
+ * addons. res_config_sqlite was developed because we, at Proformatique, are using
+ * PHP 4 in our embedded systems, and PHP 4 has no stable support for SQLite 3
+ * at this time. We also needed RealTime support.
+ *
+ * \section conf_sec Configuration
+ *
+ * The main configuration file is res_config_sqlite.conf. It must be readable or
+ * res_config_sqlite will fail to start. It is suggested to use the sample file
+ * in this package as a starting point. The file has only one section
+ * named <code>general</code>. Here are the supported parameters :
+ *
+ * <dl>
+ * <dt><code>dbfile</code></dt>
+ * <dd>The absolute path to the SQLite database (the file can be non existent,
+ * res_config_sqlite will create it if it has the appropriate rights)</dd>
+ * <dt><code>config_table</code></dt>
+ * <dd>The table used for static configuration</dd>
+ * <dt><code>cdr_table</code></dt>
+ * <dd>The table used to store CDR entries (if ommitted, CDR support is
+ * disabled)</dd>
+ * </dl>
+ *
+ * To use res_config_sqlite for static and/or RealTime configuration, refer to the
+ * Asterisk documentation. The file tables.sql can be used to create the
+ * needed tables.
+ *
+ * \section status_sec Driver status
+ *
+ * The CLI command <code>show sqlite status</code> returns status information
+ * about the running driver.
+ *
+ * \section credits_sec Credits
+ *
+ * res_config_sqlite was developed by Richard Braun at the Proformatique company.
+ */
+
+/*!
+ * \file
+ * \brief res_config_sqlite module.
+ */
+
+/*** MODULEINFO
+ <depend>sqlite</depend>
+ ***/
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sqlite.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/cdr.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/linkedlists.h"
+
+#define MACRO_BEGIN do {
+#define MACRO_END } while (0)
+
+#define RES_CONFIG_SQLITE_NAME "res_config_sqlite"
+#define RES_CONFIG_SQLITE_DRIVER "sqlite"
+#define RES_CONFIG_SQLITE_DESCRIPTION "Resource Module for SQLite 2"
+#define RES_CONFIG_SQLITE_CONF_FILE "res_config_sqlite.conf"
+
+enum {
+ RES_CONFIG_SQLITE_CONFIG_ID,
+ RES_CONFIG_SQLITE_CONFIG_CAT_METRIC,
+ RES_CONFIG_SQLITE_CONFIG_VAR_METRIC,
+ RES_CONFIG_SQLITE_CONFIG_COMMENTED,
+ RES_CONFIG_SQLITE_CONFIG_FILENAME,
+ RES_CONFIG_SQLITE_CONFIG_CATEGORY,
+ RES_CONFIG_SQLITE_CONFIG_VAR_NAME,
+ RES_CONFIG_SQLITE_CONFIG_VAR_VAL,
+ RES_CONFIG_SQLITE_CONFIG_COLUMNS,
+};
+
+#define SET_VAR(config, to, from) \
+MACRO_BEGIN \
+ int __error; \
+ \
+ __error = set_var(&to, #to, from->value); \
+ \
+ if (__error) { \
+ ast_config_destroy(config); \
+ unload_config(); \
+ return 1; \
+ } \
+MACRO_END
+
+/*!
+ * Maximum number of loops before giving up executing a query. Calls to
+ * sqlite_xxx() functions which can return SQLITE_BUSY or SQLITE_LOCKED
+ * are enclosed by RES_CONFIG_SQLITE_BEGIN and RES_CONFIG_SQLITE_END, e.g.
+ * <pre>
+ * char *errormsg;
+ * int error;
+ *
+ * RES_CONFIG_SQLITE_BEGIN
+ * error = sqlite_exec(db, query, NULL, NULL, &errormsg);
+ * RES_CONFIG_SQLITE_END(error)
+ *
+ * if (error)
+ * ...;
+ * </pre>
+ */
+#define RES_CONFIG_SQLITE_MAX_LOOPS 10
+
+/*!
+ * Macro used before executing a query.
+ *
+ * \see RES_CONFIG_SQLITE_MAX_LOOPS.
+ */
+#define RES_CONFIG_SQLITE_BEGIN \
+MACRO_BEGIN \
+ int __i; \
+ \
+ for (__i = 0; __i < RES_CONFIG_SQLITE_MAX_LOOPS; __i++) {
+
+/*!
+ * Macro used after executing a query.
+ *
+ * \see RES_CONFIG_SQLITE_MAX_LOOPS.
+ */
+#define RES_CONFIG_SQLITE_END(error) \
+ if (error != SQLITE_BUSY && error != SQLITE_LOCKED) \
+ break; \
+ usleep(1000); \
+ } \
+MACRO_END;
+
+/*!
+ * Structure sent to the SQLite callback function for static configuration.
+ *
+ * \see add_cfg_entry()
+ */
+struct cfg_entry_args {
+ struct ast_config *cfg;
+ struct ast_category *cat;
+ char *cat_name;
+ struct ast_flags flags;
+};
+
+/*!
+ * Structure sent to the SQLite callback function for RealTime configuration.
+ *
+ * \see add_rt_cfg_entry()
+ */
+struct rt_cfg_entry_args {
+ struct ast_variable *var;
+ struct ast_variable *last;
+};
+
+/*!
+ * Structure sent to the SQLite callback function for RealTime configuration
+ * (realtime_multi_handler()).
+ *
+ * \see add_rt_multi_cfg_entry()
+ */
+struct rt_multi_cfg_entry_args {
+ struct ast_config *cfg;
+ char *initfield;
+};
+
+/*!
+ * \brief Allocate a variable.
+ * \param var the address of the variable to set (it will be allocated)
+ * \param name the name of the variable (for error handling)
+ * \param value the value to store in var
+ * \retval 0 on success
+ * \retval 1 if an allocation error occurred
+ */
+static int set_var(char **var, const char *name, const char *value);
+
+/*!
+ * \brief Load the configuration file.
+ * \see unload_config()
+ *
+ * This function sets dbfile, config_table, and cdr_table. It calls
+ * check_vars() before returning, and unload_config() if an error occurred.
+ *
+ * \retval 0 on success
+ * \retval 1 if an error occurred
+ */
+static int load_config(void);
+
+/*!
+ * \brief Free resources related to configuration.
+ * \see load_config()
+ */
+static void unload_config(void);
+
+/*!
+ * \brief Asterisk callback function for CDR support.
+ * \param cdr the CDR entry Asterisk sends us.
+ *
+ * Asterisk will call this function each time a CDR entry must be logged if
+ * CDR support is enabled.
+ *
+ * \retval 0 on success
+ * \retval 1 if an error occurred
+ */
+static int cdr_handler(struct ast_cdr *cdr);
+
+/*!
+ * \brief SQLite callback function for static configuration.
+ *
+ * This function is passed to the SQLite engine as a callback function to
+ * parse a row and store it in a struct ast_config object. It relies on
+ * resulting rows being sorted by category.
+ *
+ * \param arg a pointer to a struct cfg_entry_args object
+ * \param argc number of columns
+ * \param argv values in the row
+ * \param columnNames names and types of the columns
+ * \retval 0 on success
+ * \retval 1 if an error occurred
+ * \see cfg_entry_args
+ * \see sql_get_config_table
+ * \see config_handler()
+ */
+static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames);
+
+/*!
+ * \brief Asterisk callback function for static configuration.
+ *
+ * Asterisk will call this function when it loads its static configuration,
+ * which usually happens at startup and reload.
+ *
+ * \param database the database to use (ignored)
+ * \param table the table to use
+ * \param file the file to load from the database
+ * \param cfg the struct ast_config object to use when storing variables
+ * \param flags Optional flags. Not used.
+ * \param suggested_incl suggest include.
+ * \retval cfg object
+ * \retval NULL if an error occurred
+ * \see add_cfg_entry()
+ */
+static struct ast_config * config_handler(const char *database, const char *table, const char *file,
+ struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl);
+
+/*!
+ * \brief Helper function to parse a va_list object into 2 dynamic arrays of
+ * strings, parameters and values.
+ *
+ * ap must have the following format : param1 val1 param2 val2 param3 val3 ...
+ * arguments will be extracted to create 2 arrays:
+ *
+ * <ul>
+ * <li>params : param1 param2 param3 ...</li>
+ * <li>vals : val1 val2 val3 ...</li>
+ * </ul>
+ *
+ * The address of these arrays are stored in params_ptr and vals_ptr. It
+ * is the responsibility of the caller to release the memory of these arrays.
+ * It is considered an error that va_list has a null or odd number of strings.
+ *
+ * \param ap the va_list object to parse
+ * \param params_ptr where the address of the params array is stored
+ * \param vals_ptr where the address of the vals array is stored
+ * \retval the number of elements in the arrays (which have the same size).
+ * \retval 0 if an error occurred.
+ */
+static size_t get_params(va_list ap, const char ***params_ptr,
+ const char ***vals_ptr);
+
+/*!
+ * \brief SQLite callback function for RealTime configuration.
+ *
+ * This function is passed to the SQLite engine as a callback function to
+ * parse a row and store it in a linked list of struct ast_variable objects.
+ *
+ * \param arg a pointer to a struct rt_cfg_entry_args object
+ * \param argc number of columns
+ * \param argv values in the row
+ * \param columnNames names and types of the columns
+ * \retval 0 on success.
+ * \retval 1 if an error occurred.
+ * \see rt_cfg_entry_args
+ * \see realtime_handler()
+ */
+static int add_rt_cfg_entry(void *arg, int argc, char **argv,
+ char **columnNames);
+
+/*!
+ * Asterisk callback function for RealTime configuration.
+ *
+ * Asterisk will call this function each time it requires a variable
+ * through the RealTime architecture. ap is a list of parameters and
+ * values used to find a specific row, e.g one parameter "name" and
+ * one value "123" so that the SQL query becomes <code>SELECT * FROM
+ * table WHERE name = '123';</code>.
+ *
+ * \param database the database to use (ignored)
+ * \param table the table to use
+ * \param ap list of parameters and values to match
+ *
+ * \retval a linked list of struct ast_variable objects
+ * \retval NULL if an error occurred
+ * \see add_rt_cfg_entry()
+ */
+static struct ast_variable * realtime_handler(const char *database,
+ const char *table, va_list ap);
+
+/*!
+ * \brief SQLite callback function for RealTime configuration.
+ *
+ * This function performs the same actions as add_rt_cfg_entry() except
+ * that the rt_multi_cfg_entry_args structure is designed to store
+ * categories in addition to variables.
+ *
+ * \param arg a pointer to a struct rt_multi_cfg_entry_args object
+ * \param argc number of columns
+ * \param argv values in the row
+ * \param columnNames names and types of the columns
+ * \retval 0 on success.
+ * \retval 1 if an error occurred.
+ * \see rt_multi_cfg_entry_args
+ * \see realtime_multi_handler()
+ */
+static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv,
+ char **columnNames);
+
+/*!
+ * \brief Asterisk callback function for RealTime configuration.
+ *
+ * This function performs the same actions as realtime_handler() except
+ * that it can store variables per category, and can return several
+ * categories.
+ *
+ * \param database the database to use (ignored)
+ * \param table the table to use
+ * \param ap list of parameters and values to match
+ * \retval a struct ast_config object storing categories and variables.
+ * \retval NULL if an error occurred.
+ *
+ * \see add_rt_multi_cfg_entry()
+ */
+static struct ast_config * realtime_multi_handler(const char *database,
+ const char *table, va_list ap);
+
+/*!
+ * \brief Asterisk callback function for RealTime configuration (variable
+ * update).
+ *
+ * Asterisk will call this function each time a variable has been modified
+ * internally and must be updated in the backend engine. keyfield and entity
+ * are used to find the row to update, e.g. <code>UPDATE table SET ... WHERE
+ * keyfield = 'entity';</code>. ap is a list of parameters and values with the
+ * same format as the other realtime functions.
+ *
+ * \param database the database to use (ignored)
+ * \param table the table to use
+ * \param keyfield the column of the matching cell
+ * \param entity the value of the matching cell
+ * \param ap list of parameters and new values to update in the database
+ * \retval the number of affected rows.
+ * \retval -1 if an error occurred.
+ */
+static int realtime_update_handler(const char *database, const char *table,
+ const char *keyfield, const char *entity, va_list ap);
+
+/*!
+ * \brief Asterisk callback function for RealTime configuration (variable
+ * create/store).
+ *
+ * Asterisk will call this function each time a variable has been created
+ * internally and must be stored in the backend engine.
+ * are used to find the row to update, e.g. ap is a list of parameters and
+ * values with the same format as the other realtime functions.
+ *
+ * \param database the database to use (ignored)
+ * \param table the table to use
+ * \param ap list of parameters and new values to insert into the database
+ * \retval the rowid of inserted row.
+ * \retval -1 if an error occurred.
+ */
+static int realtime_store_handler(const char *database, const char *table,
+ va_list ap);
+
+/*!
+ * \brief Asterisk callback function for RealTime configuration (destroys
+ * variable).
+ *
+ * Asterisk will call this function each time a variable has been destroyed
+ * internally and must be removed from the backend engine. keyfield and entity
+ * are used to find the row to delete, e.g. <code>DELETE FROM table WHERE
+ * keyfield = 'entity';</code>. ap is a list of parameters and values with the
+ * same format as the other realtime functions.
+ *
+ * \param database the database to use (ignored)
+ * \param table the table to use
+ * \param keyfield the column of the matching cell
+ * \param entity the value of the matching cell
+ * \param ap list of additional parameters for cell matching
+ * \retval the number of affected rows.
+ * \retval -1 if an error occurred.
+ */
+static int realtime_destroy_handler(const char *database, const char *table,
+ const char *keyfield, const char *entity, va_list ap);
+
+/*!
+ * \brief Asterisk callback function for the CLI status command.
+ *
+ * \param e CLI command
+ * \param cmd
+ * \param a CLI argument list
+ * \return RESULT_SUCCESS
+ */
+static char *handle_cli_show_sqlite_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+/*! The SQLite database object. */
+static sqlite *db;
+
+/*! Set to 1 if CDR support is enabled. */
+static int use_cdr;
+
+/*! Set to 1 if the CDR callback function was registered. */
+static int cdr_registered;
+
+/*! Set to 1 if the CLI status command callback function was registered. */
+static int cli_status_registered;
+
+/*! The path of the database file. */
+static char *dbfile;
+
+/*! The name of the static configuration table. */
+static char *config_table;
+
+/*! The name of the table used to store CDR entries. */
+static char *cdr_table;
+
+/*!
+ * The structure specifying all callback functions used by Asterisk for static
+ * and RealTime configuration.
+ */
+static struct ast_config_engine sqlite_engine =
+{
+ .name = RES_CONFIG_SQLITE_DRIVER,
+ .load_func = config_handler,
+ .realtime_func = realtime_handler,
+ .realtime_multi_func = realtime_multi_handler,
+ .store_func = realtime_store_handler,
+ .destroy_func = realtime_destroy_handler,
+ .update_func = realtime_update_handler
+};
+
+/*!
+ * The mutex used to prevent simultaneous access to the SQLite database.
+ */
+AST_MUTEX_DEFINE_STATIC(mutex);
+
+/*!
+ * Structure containing details and callback functions for the CLI status
+ * command.
+ */
+static struct ast_cli_entry cli_status[] = {
+ AST_CLI_DEFINE(handle_cli_show_sqlite_status, "Show status information about the SQLite 2 driver"),
+};
+
+/*
+ * Taken from Asterisk 1.2 cdr_sqlite.so.
+ */
+
+/*! SQL query format to create the CDR table if non existent. */
+static char *sql_create_cdr_table =
+"CREATE TABLE '%q' (\n"
+" id INTEGER,\n"
+" clid VARCHAR(80) NOT NULL DEFAULT '',\n"
+" src VARCHAR(80) NOT NULL DEFAULT '',\n"
+" dst VARCHAR(80) NOT NULL DEFAULT '',\n"
+" dcontext VARCHAR(80) NOT NULL DEFAULT '',\n"
+" channel VARCHAR(80) NOT NULL DEFAULT '',\n"
+" dstchannel VARCHAR(80) NOT NULL DEFAULT '',\n"
+" lastapp VARCHAR(80) NOT NULL DEFAULT '',\n"
+" lastdata VARCHAR(80) NOT NULL DEFAULT '',\n"
+" start DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
+" answer DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
+" end DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
+" duration INT(11) NOT NULL DEFAULT 0,\n"
+" billsec INT(11) NOT NULL DEFAULT 0,\n"
+" disposition VARCHAR(45) NOT NULL DEFAULT '',\n"
+" amaflags INT(11) NOT NULL DEFAULT 0,\n"
+" accountcode VARCHAR(20) NOT NULL DEFAULT '',\n"
+" uniqueid VARCHAR(32) NOT NULL DEFAULT '',\n"
+" userfield VARCHAR(255) NOT NULL DEFAULT '',\n"
+" PRIMARY KEY (id)\n"
+");";
+
+/*! SQL query format to insert a CDR entry. */
+static char *sql_add_cdr_entry =
+"INSERT INTO '%q' ("
+" clid,"
+" src,"
+" dst,"
+" dcontext,"
+" channel,"
+" dstchannel,"
+" lastapp,"
+" lastdata,"
+" start,"
+" answer,"
+" end,"
+" duration,"
+" billsec,"
+" disposition,"
+" amaflags,"
+" accountcode,"
+" uniqueid,"
+" userfield"
+") VALUES ("
+" '%q',"
+" '%q',"
+" '%q',"
+" '%q',"
+" '%q',"
+" '%q',"
+" '%q',"
+" '%q',"
+" datetime(%d,'unixepoch','localtime'),"
+" datetime(%d,'unixepoch','localtime'),"
+" datetime(%d,'unixepoch','localtime'),"
+" '%ld',"
+" '%ld',"
+" '%ld',"
+" '%ld',"
+" '%q',"
+" '%q',"
+" '%q'"
+");";
+
+/*!
+ * SQL query format to fetch the static configuration of a file.
+ * Rows must be sorted by category.
+ *
+ * \see add_cfg_entry()
+ */
+static char *sql_get_config_table =
+"SELECT *"
+" FROM '%q'"
+" WHERE filename = '%q' AND commented = 0"
+" ORDER BY cat_metric ASC, var_metric ASC;";
+
+static int set_var(char **var, const char *name, const char *value)
+{
+ if (*var)
+ ast_free(*var);
+
+ *var = ast_strdup(value);
+
+ if (!*var) {
+ ast_log(LOG_WARNING, "Unable to allocate variable %s\n", name);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int check_vars(void)
+{
+ if (!dbfile) {
+ ast_log(LOG_ERROR, "Undefined parameter %s\n", dbfile);
+ return 1;
+ }
+
+ use_cdr = (cdr_table != NULL);
+
+ return 0;
+}
+
+static int load_config(void)
+{
+ struct ast_config *config;
+ struct ast_variable *var;
+ int error;
+ struct ast_flags config_flags = { 0 };
+
+ config = ast_config_load(RES_CONFIG_SQLITE_CONF_FILE, config_flags);
+
+ if (!config) {
+ ast_log(LOG_ERROR, "Unable to load " RES_CONFIG_SQLITE_CONF_FILE "\n");
+ return 1;
+ }
+
+ for (var = ast_variable_browse(config, "general"); var; var = var->next) {
+ if (!strcasecmp(var->name, "dbfile"))
+ SET_VAR(config, dbfile, var);
+ else if (!strcasecmp(var->name, "config_table"))
+ SET_VAR(config, config_table, var);
+ else if (!strcasecmp(var->name, "cdr_table"))
+ SET_VAR(config, cdr_table, var);
+ else
+ ast_log(LOG_WARNING, "Unknown parameter : %s\n", var->name);
+ }
+
+ ast_config_destroy(config);
+ error = check_vars();
+
+ if (error) {
+ unload_config();
+ return 1;
+ }
+
+ return 0;
+}
+
+static void unload_config(void)
+{
+ ast_free(dbfile);
+ dbfile = NULL;
+ ast_free(config_table);
+ config_table = NULL;
+ ast_free(cdr_table);
+ cdr_table = NULL;
+}
+
+static int cdr_handler(struct ast_cdr *cdr)
+{
+ char *query, *errormsg;
+ int error;
+
+ query = sqlite_mprintf(sql_add_cdr_entry, cdr_table, cdr->clid,
+ cdr->src, cdr->dst, cdr->dcontext, cdr->channel,
+ cdr->dstchannel, cdr->lastapp, cdr->lastdata,
+ cdr->start.tv_sec, cdr->answer.tv_sec,
+ cdr->end.tv_sec, cdr->duration, cdr->billsec,
+ cdr->disposition, cdr->amaflags, cdr->accountcode,
+ cdr->uniqueid, cdr->userfield);
+
+ if (!query) {
+ ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
+ return 1;
+ }
+
+ ast_debug(1, "SQL query: %s\n", query);
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, NULL, NULL, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ ast_mutex_unlock(&mutex);
+
+ sqlite_freemem(query);
+
+ if (error) {
+ ast_log(LOG_ERROR, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
+{
+ struct cfg_entry_args *args;
+ struct ast_variable *var;
+
+ if (argc != RES_CONFIG_SQLITE_CONFIG_COLUMNS) {
+ ast_log(LOG_WARNING, "Corrupt table\n");
+ return 1;
+ }
+
+ args = arg;
+
+ if (!strcmp(argv[RES_CONFIG_SQLITE_CONFIG_VAR_NAME], "#include")) {
+ struct ast_config *cfg;
+ char *val;
+
+ val = argv[RES_CONFIG_SQLITE_CONFIG_VAR_VAL];
+ cfg = ast_config_internal_load(val, args->cfg, args->flags, "");
+
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Unable to include %s\n", val);
+ return 1;
+ } else {
+ args->cfg = cfg;
+ return 0;
+ }
+ }
+
+ if (!args->cat_name || strcmp(args->cat_name, argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY])) {
+ args->cat = ast_category_new(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY], "", 99999);
+
+ if (!args->cat) {
+ ast_log(LOG_WARNING, "Unable to allocate category\n");
+ return 1;
+ }
+
+ ast_free(args->cat_name);
+ args->cat_name = ast_strdup(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY]);
+
+ if (!args->cat_name) {
+ ast_category_destroy(args->cat);
+ return 1;
+ }
+
+ ast_category_append(args->cfg, args->cat);
+ }
+
+ var = ast_variable_new(argv[RES_CONFIG_SQLITE_CONFIG_VAR_NAME], argv[RES_CONFIG_SQLITE_CONFIG_VAR_VAL], "");
+
+ if (!var) {
+ ast_log(LOG_WARNING, "Unable to allocate variable");
+ return 1;
+ }
+
+ ast_variable_append(args->cat, var);
+
+ return 0;
+}
+
+static struct ast_config *config_handler(const char *database, const char *table, const char *file,
+ struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl)
+{
+ struct cfg_entry_args args;
+ char *query, *errormsg;
+ int error;
+
+ if (!config_table) {
+ if (!table) {
+ ast_log(LOG_ERROR, "Table name unspecified\n");
+ return NULL;
+ }
+ } else
+ table = config_table;
+
+ query = sqlite_mprintf(sql_get_config_table, table, file);
+
+ if (!query) {
+ ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
+ return NULL;
+ }
+
+ ast_debug(1, "SQL query: %s\n", query);
+ args.cfg = cfg;
+ args.cat = NULL;
+ args.cat_name = NULL;
+ args.flags = flags;
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, add_cfg_entry, &args, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ ast_mutex_unlock(&mutex);
+
+ ast_free(args.cat_name);
+ sqlite_freemem(query);
+
+ if (error) {
+ ast_log(LOG_ERROR, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ return NULL;
+ }
+
+ return cfg;
+}
+
+static size_t get_params(va_list ap, const char ***params_ptr, const char ***vals_ptr)
+{
+ const char **tmp, *param, *val, **params, **vals;
+ size_t params_count;
+
+ params = NULL;
+ vals = NULL;
+ params_count = 0;
+
+ while ((param = va_arg(ap, const char *)) && (val = va_arg(ap, const char *))) {
+ if (!(tmp = ast_realloc(params, (params_count + 1) * sizeof(char *)))) {
+ ast_free(params);
+ ast_free(vals);
+ return 0;
+ }
+ params = tmp;
+
+ if (!(tmp = ast_realloc(vals, (params_count + 1) * sizeof(char *)))) {
+ ast_free(params);
+ ast_free(vals);
+ return 0;
+ }
+ vals = tmp;
+
+ params[params_count] = param;
+ vals[params_count] = val;
+ params_count++;
+ }
+
+ if (params_count > 0) {
+ *params_ptr = params;
+ *vals_ptr = vals;
+ } else
+ ast_log(LOG_WARNING, "1 parameter and 1 value at least required\n");
+
+ return params_count;
+}
+
+static int add_rt_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
+{
+ struct rt_cfg_entry_args *args;
+ struct ast_variable *var;
+ int i;
+
+ args = arg;
+
+ for (i = 0; i < argc; i++) {
+ if (!argv[i])
+ continue;
+
+ if (!(var = ast_variable_new(columnNames[i], argv[i], "")))
+ return 1;
+
+ if (!args->var)
+ args->var = var;
+
+ if (!args->last)
+ args->last = var;
+ else {
+ args->last->next = var;
+ args->last = var;
+ }
+ }
+
+ return 0;
+}
+
+static struct ast_variable * realtime_handler(const char *database, const char *table, va_list ap)
+{
+ char *query, *errormsg, *op, *tmp_str;
+ struct rt_cfg_entry_args args;
+ const char **params, **vals;
+ size_t params_count;
+ int error;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "Table name unspecified\n");
+ return NULL;
+ }
+
+ params_count = get_params(ap, &params, &vals);
+
+ if (params_count == 0)
+ return NULL;
+
+ op = (strchr(params[0], ' ') == NULL) ? " =" : "";
+
+/* \cond DOXYGEN_CAN_PARSE_THIS */
+#undef QUERY
+#define QUERY "SELECT * FROM '%q' WHERE commented = 0 AND %q%s '%q'"
+/* \endcond */
+
+ query = sqlite_mprintf(QUERY, table, params[0], op, vals[0]);
+
+ if (!query) {
+ ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return NULL;
+ }
+
+ if (params_count > 1) {
+ size_t i;
+
+ for (i = 1; i < params_count; i++) {
+ op = (strchr(params[i], ' ') == NULL) ? " =" : "";
+ tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, params[i], op, vals[i]);
+ sqlite_freemem(query);
+
+ if (!tmp_str) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return NULL;
+ }
+
+ query = tmp_str;
+ }
+ }
+
+ ast_free(params);
+ ast_free(vals);
+
+ tmp_str = sqlite_mprintf("%s LIMIT 1;", query);
+ sqlite_freemem(query);
+
+ if (!tmp_str) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ return NULL;
+ }
+
+ query = tmp_str;
+ ast_debug(1, "SQL query: %s\n", query);
+ args.var = NULL;
+ args.last = NULL;
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, add_rt_cfg_entry, &args, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ ast_mutex_unlock(&mutex);
+
+ sqlite_freemem(query);
+
+ if (error) {
+ ast_log(LOG_WARNING, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ ast_variables_destroy(args.var);
+ return NULL;
+ }
+
+ return args.var;
+}
+
+static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
+{
+ struct rt_multi_cfg_entry_args *args;
+ struct ast_category *cat;
+ struct ast_variable *var;
+ char *cat_name;
+ size_t i;
+
+ args = arg;
+ cat_name = NULL;
+
+ /*
+ * cat_name should always be set here, since initfield is forged from
+ * params[0] in realtime_multi_handler(), which is a search parameter
+ * of the SQL query.
+ */
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(args->initfield, columnNames[i]))
+ cat_name = argv[i];
+ }
+
+ if (!cat_name) {
+ ast_log(LOG_ERROR, "Bogus SQL results, cat_name is NULL !\n");
+ return 1;
+ }
+
+ if (!(cat = ast_category_new(cat_name, "", 99999))) {
+ ast_log(LOG_WARNING, "Unable to allocate category\n");
+ return 1;
+ }
+
+ ast_category_append(args->cfg, cat);
+
+ for (i = 0; i < argc; i++) {
+ if (!argv[i] || !strcmp(args->initfield, columnNames[i]))
+ continue;
+
+ if (!(var = ast_variable_new(columnNames[i], argv[i], ""))) {
+ ast_log(LOG_WARNING, "Unable to allocate variable\n");
+ return 1;
+ }
+
+ ast_variable_append(cat, var);
+ }
+
+ return 0;
+}
+
+static struct ast_config *realtime_multi_handler(const char *database,
+ const char *table, va_list ap)
+{
+ char *query, *errormsg, *op, *tmp_str, *initfield;
+ struct rt_multi_cfg_entry_args args;
+ const char **params, **vals;
+ struct ast_config *cfg;
+ size_t params_count;
+ int error;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "Table name unspecified\n");
+ return NULL;
+ }
+
+ if (!(cfg = ast_config_new())) {
+ ast_log(LOG_WARNING, "Unable to allocate configuration structure\n");
+ return NULL;
+ }
+
+ if (!(params_count = get_params(ap, &params, &vals))) {
+ ast_config_destroy(cfg);
+ return NULL;
+ }
+
+ if (!(initfield = ast_strdup(params[0]))) {
+ ast_config_destroy(cfg);
+ ast_free(params);
+ ast_free(vals);
+ return NULL;
+ }
+
+ tmp_str = strchr(initfield, ' ');
+
+ if (tmp_str)
+ *tmp_str = '\0';
+
+ op = (!strchr(params[0], ' ')) ? " =" : "";
+
+ /*
+ * Asterisk sends us an already escaped string when searching for
+ * "exten LIKE" (uh!). Handle it separately.
+ */
+ tmp_str = (!strcmp(vals[0], "\\_%")) ? "_%" : (char *)vals[0];
+
+/* \cond DOXYGEN_CAN_PARSE_THIS */
+#undef QUERY
+#define QUERY "SELECT * FROM '%q' WHERE commented = 0 AND %q%s '%q'"
+/* \endcond */
+
+ if (!(query = sqlite_mprintf(QUERY, table, params[0], op, tmp_str))) {
+ ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
+ ast_config_destroy(cfg);
+ ast_free(params);
+ ast_free(vals);
+ ast_free(initfield);
+ return NULL;
+ }
+
+ if (params_count > 1) {
+ size_t i;
+
+ for (i = 1; i < params_count; i++) {
+ op = (!strchr(params[i], ' ')) ? " =" : "";
+ tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, params[i], op, vals[i]);
+ sqlite_freemem(query);
+
+ if (!tmp_str) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ ast_config_destroy(cfg);
+ ast_free(params);
+ ast_free(vals);
+ ast_free(initfield);
+ return NULL;
+ }
+
+ query = tmp_str;
+ }
+ }
+
+ ast_free(params);
+ ast_free(vals);
+
+ if (!(tmp_str = sqlite_mprintf("%s ORDER BY %q;", query, initfield))) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ ast_config_destroy(cfg);
+ ast_free(initfield);
+ return NULL;
+ }
+
+ sqlite_freemem(query);
+ query = tmp_str;
+ ast_debug(1, "SQL query: %s\n", query);
+ args.cfg = cfg;
+ args.initfield = initfield;
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, add_rt_multi_cfg_entry, &args, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ ast_mutex_unlock(&mutex);
+
+ sqlite_freemem(query);
+ ast_free(initfield);
+
+ if (error) {
+ ast_log(LOG_WARNING, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ ast_config_destroy(cfg);
+ return NULL;
+ }
+
+ return cfg;
+}
+
+static int realtime_update_handler(const char *database, const char *table,
+ const char *keyfield, const char *entity, va_list ap)
+{
+ char *query, *errormsg, *tmp_str;
+ const char **params, **vals;
+ size_t params_count;
+ int error, rows_num;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "Table name unspecified\n");
+ return -1;
+ }
+
+ if (!(params_count = get_params(ap, &params, &vals)))
+ return -1;
+
+/* \cond DOXYGEN_CAN_PARSE_THIS */
+#undef QUERY
+#define QUERY "UPDATE '%q' SET %q = '%q'"
+/* \endcond */
+
+ if (!(query = sqlite_mprintf(QUERY, table, params[0], vals[0]))) {
+ ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return -1;
+ }
+
+ if (params_count > 1) {
+ size_t i;
+
+ for (i = 1; i < params_count; i++) {
+ tmp_str = sqlite_mprintf("%s, %q = '%q'", query, params[i], vals[i]);
+ sqlite_freemem(query);
+
+ if (!tmp_str) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return -1;
+ }
+
+ query = tmp_str;
+ }
+ }
+
+ ast_free(params);
+ ast_free(vals);
+
+ if (!(tmp_str = sqlite_mprintf("%s WHERE %q = '%q';", query, keyfield, entity))) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ return -1;
+ }
+
+ sqlite_freemem(query);
+ query = tmp_str;
+ ast_debug(1, "SQL query: %s\n", query);
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, NULL, NULL, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ if (!error)
+ rows_num = sqlite_changes(db);
+ else
+ rows_num = -1;
+
+ ast_mutex_unlock(&mutex);
+
+ sqlite_freemem(query);
+
+ if (error) {
+ ast_log(LOG_WARNING, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ }
+
+ return rows_num;
+}
+
+static int realtime_store_handler(const char *database, const char *table, va_list ap) {
+ char *errormsg, *tmp_str, *tmp_keys, *tmp_keys2, *tmp_vals, *tmp_vals2;
+ const char **params, **vals;
+ size_t params_count;
+ int error, rows_id;
+ size_t i;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "Table name unspecified\n");
+ return -1;
+ }
+
+ if (!(params_count = get_params(ap, &params, &vals)))
+ return -1;
+
+/* \cond DOXYGEN_CAN_PARSE_THIS */
+#undef QUERY
+#define QUERY "INSERT into '%q' (%s) VALUES (%s);"
+/* \endcond */
+
+ tmp_keys2 = NULL;
+ tmp_vals2 = NULL;
+ for (i = 0; i < params_count; i++) {
+ if ( tmp_keys2 ) {
+ tmp_keys = sqlite_mprintf("%s, %q", tmp_keys2, params[i]);
+ sqlite_freemem(tmp_keys2);
+ } else {
+ tmp_keys = sqlite_mprintf("%q", params[i]);
+ }
+ if (!tmp_keys) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return -1;
+ }
+
+ if ( tmp_vals2 ) {
+ tmp_vals = sqlite_mprintf("%s, '%q'", tmp_vals2, params[i]);
+ sqlite_freemem(tmp_vals2);
+ } else {
+ tmp_vals = sqlite_mprintf("'%q'", params[i]);
+ }
+ if (!tmp_vals) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return -1;
+ }
+
+
+ tmp_keys2 = tmp_keys;
+ tmp_vals2 = tmp_vals;
+ }
+
+ ast_free(params);
+ ast_free(vals);
+
+ if (!(tmp_str = sqlite_mprintf(QUERY, table, tmp_keys, tmp_vals))) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ return -1;
+ }
+
+ sqlite_freemem(tmp_keys);
+ sqlite_freemem(tmp_vals);
+
+ ast_debug(1, "SQL query: %s\n", tmp_str);
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, tmp_str, NULL, NULL, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ if (!error) {
+ rows_id = sqlite_last_insert_rowid(db);
+ } else {
+ rows_id = -1;
+ }
+
+ ast_mutex_unlock(&mutex);
+
+ sqlite_freemem(tmp_str);
+
+ if (error) {
+ ast_log(LOG_WARNING, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ }
+
+ return rows_id;
+}
+
+static int realtime_destroy_handler(const char *database, const char *table,
+ const char *keyfield, const char *entity, va_list ap)
+{
+ char *query, *errormsg, *tmp_str;
+ const char **params, **vals;
+ size_t params_count;
+ int error, rows_num;
+ size_t i;
+
+ if (!table) {
+ ast_log(LOG_WARNING, "Table name unspecified\n");
+ return -1;
+ }
+
+ if (!(params_count = get_params(ap, &params, &vals)))
+ return -1;
+
+/* \cond DOXYGEN_CAN_PARSE_THIS */
+#undef QUERY
+#define QUERY "DELETE FROM '%q' WHERE"
+/* \endcond */
+
+ if (!(query = sqlite_mprintf(QUERY, table))) {
+ ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return -1;
+ }
+
+ for (i = 0; i < params_count; i++) {
+ tmp_str = sqlite_mprintf("%s %q = '%q' AND", query, params[i], vals[i]);
+ sqlite_freemem(query);
+
+ if (!tmp_str) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ ast_free(params);
+ ast_free(vals);
+ return -1;
+ }
+
+ query = tmp_str;
+ }
+
+ ast_free(params);
+ ast_free(vals);
+ if (!(tmp_str = sqlite_mprintf("%s %q = '%q';", query, keyfield, entity))) {
+ ast_log(LOG_WARNING, "Unable to reallocate SQL query\n");
+ return -1;
+ }
+ sqlite_freemem(query);
+ query = tmp_str;
+ ast_debug(1, "SQL query: %s\n", query);
+
+ ast_mutex_lock(&mutex);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, NULL, NULL, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ if (!error)
+ rows_num = sqlite_changes(db);
+ else
+ rows_num = -1;
+
+ ast_mutex_unlock(&mutex);
+
+ sqlite_freemem(query);
+
+ if (error) {
+ ast_log(LOG_WARNING, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ }
+
+ return rows_num;
+}
+
+static char *handle_cli_show_sqlite_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "show sqlite status";
+ e->usage =
+ "Usage: show sqlite status\n"
+ " Show status information about the SQLite 2 driver\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "SQLite database path: %s\n", dbfile);
+ ast_cli(a->fd, "config_table: ");
+
+ if (!config_table)
+ ast_cli(a->fd, "unspecified, must be present in extconfig.conf\n");
+ else
+ ast_cli(a->fd, "%s\n", config_table);
+
+ ast_cli(a->fd, "cdr_table: ");
+
+ if (!cdr_table)
+ ast_cli(a->fd, "unspecified, CDR support disabled\n");
+ else
+ ast_cli(a->fd, "%s\n", cdr_table);
+
+ return CLI_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ if (cli_status_registered)
+ ast_cli_unregister_multiple(cli_status, sizeof(cli_status) / sizeof(struct ast_cli_entry));
+
+ if (cdr_registered)
+ ast_cdr_unregister(RES_CONFIG_SQLITE_NAME);
+
+ ast_config_engine_deregister(&sqlite_engine);
+
+ if (db)
+ sqlite_close(db);
+
+ unload_config();
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ char *errormsg;
+ int error;
+
+ db = NULL;
+ cdr_registered = 0;
+ cli_status_registered = 0;
+ dbfile = NULL;
+ config_table = NULL;
+ cdr_table = NULL;
+ error = load_config();
+
+ if (error)
+ return AST_MODULE_LOAD_DECLINE;
+
+ if (!(db = sqlite_open(dbfile, 0660, &errormsg))) {
+ ast_log(LOG_ERROR, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ unload_module();
+ return 1;
+ }
+
+ ast_config_engine_register(&sqlite_engine);
+
+ if (use_cdr) {
+ char *query;
+
+/* \cond DOXYGEN_CAN_PARSE_THIS */
+#undef QUERY
+#define QUERY "SELECT COUNT(id) FROM %Q;"
+/* \endcond */
+
+ query = sqlite_mprintf(QUERY, cdr_table);
+
+ if (!query) {
+ ast_log(LOG_ERROR, "Unable to allocate SQL query\n");
+ unload_module();
+ return 1;
+ }
+
+ ast_debug(1, "SQL query: %s\n", query);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, NULL, NULL, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ sqlite_freemem(query);
+
+ if (error) {
+ /*
+ * Unexpected error.
+ */
+ if (error != SQLITE_ERROR) {
+ ast_log(LOG_ERROR, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ unload_module();
+ return 1;
+ }
+
+ sqlite_freemem(errormsg);
+ query = sqlite_mprintf(sql_create_cdr_table, cdr_table);
+
+ if (!query) {
+ ast_log(LOG_ERROR, "Unable to allocate SQL query\n");
+ unload_module();
+ return 1;
+ }
+
+ ast_debug(1, "SQL query: %s\n", query);
+
+ RES_CONFIG_SQLITE_BEGIN
+ error = sqlite_exec(db, query, NULL, NULL, &errormsg);
+ RES_CONFIG_SQLITE_END(error)
+
+ sqlite_freemem(query);
+
+ if (error) {
+ ast_log(LOG_ERROR, "%s\n", errormsg);
+ sqlite_freemem(errormsg);
+ unload_module();
+ return 1;
+ }
+ }
+
+ error = ast_cdr_register(RES_CONFIG_SQLITE_NAME, RES_CONFIG_SQLITE_DESCRIPTION, cdr_handler);
+
+ if (error) {
+ unload_module();
+ return 1;
+ }
+
+ cdr_registered = 1;
+ }
+
+ error = ast_cli_register_multiple(cli_status, sizeof(cli_status) / sizeof(struct ast_cli_entry));
+
+ if (error) {
+ unload_module();
+ return 1;
+ }
+
+ cli_status_registered = 1;
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime SQLite configuration",
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/trunk/res/res_convert.c b/trunk/res/res_convert.c
new file mode 100644
index 000000000..0b4d664ff
--- /dev/null
+++ b/trunk/res/res_convert.c
@@ -0,0 +1,160 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, 2006, Digium, Inc.
+ *
+ * redice li <redice_li@yahoo.com>
+ * Russell Bryant <russell@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 file format conversion CLI command using Asterisk formats and translators
+ *
+ * \author redice li <redice_li@yahoo.com>
+ * \author Russell Bryant <russell@digium.com>
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/file.h"
+
+/*! \brief Split the filename to basename and extension */
+static int split_ext(char *filename, char **name, char **ext)
+{
+ *name = *ext = filename;
+
+ if ((*ext = strrchr(filename, '.'))) {
+ **ext = '\0';
+ (*ext)++;
+ }
+
+ if (ast_strlen_zero(*name) || ast_strlen_zero(*ext))
+ return -1;
+
+ return 0;
+}
+
+/*!
+ * \brief Convert a file from one format to another
+ * \param e CLI entry
+ * \param cmd command number
+ * \param a list of cli arguments
+ * \retval CLI_SUCCESS on success.
+ * \retval CLI_SHOWUSAGE or CLI_FAILURE on failure.
+*/
+static char *handle_cli_file_convert(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *ret = CLI_FAILURE;
+ struct ast_filestream *fs_in = NULL, *fs_out = NULL;
+ struct ast_frame *f;
+ struct timeval start;
+ int cost;
+ char *file_in = NULL, *file_out = NULL;
+ char *name_in, *ext_in, *name_out, *ext_out;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "file convert";
+ e->usage =
+ "Usage: file convert <file_in> <file_out>\n"
+ " Convert from file_in to file_out. If an absolute path\n"
+ " is not given, the default Asterisk sounds directory\n"
+ " will be used.\n\n"
+ " Example:\n"
+ " file convert tt-weasels.gsm tt-weasels.ulaw\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ /* ugly, can be removed when CLI entries have ast_module pointers */
+ ast_module_ref(ast_module_info->self);
+
+ if (a->argc != 4 || ast_strlen_zero(a->argv[2]) || ast_strlen_zero(a->argv[3])) {
+ ret = CLI_SHOWUSAGE;
+ goto fail_out;
+ }
+
+ file_in = ast_strdupa(a->argv[2]);
+ file_out = ast_strdupa(a->argv[3]);
+
+ if (split_ext(file_in, &name_in, &ext_in)) {
+ ast_cli(a->fd, "'%s' is an invalid filename!\n", a->argv[2]);
+ goto fail_out;
+ }
+ if (!(fs_in = ast_readfile(name_in, ext_in, NULL, O_RDONLY, 0, 0))) {
+ ast_cli(a->fd, "Unable to open input file: %s\n", a->argv[2]);
+ goto fail_out;
+ }
+
+ if (split_ext(file_out, &name_out, &ext_out)) {
+ ast_cli(a->fd, "'%s' is an invalid filename!\n", a->argv[3]);
+ goto fail_out;
+ }
+ if (!(fs_out = ast_writefile(name_out, ext_out, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
+ ast_cli(a->fd, "Unable to open output file: %s\n", a->argv[3]);
+ goto fail_out;
+ }
+
+ start = ast_tvnow();
+
+ while ((f = ast_readframe(fs_in))) {
+ if (ast_writestream(fs_out, f)) {
+ ast_cli(a->fd, "Failed to convert %s.%s to %s.%s!\n", name_in, ext_in, name_out, ext_out);
+ goto fail_out;
+ }
+ }
+
+ cost = ast_tvdiff_ms(ast_tvnow(), start);
+ ast_cli(a->fd, "Converted %s.%s to %s.%s in %dms\n", name_in, ext_in, name_out, ext_out, cost);
+ ret = CLI_SUCCESS;
+
+fail_out:
+ if (fs_out) {
+ ast_closestream(fs_out);
+ if (ret != CLI_SUCCESS)
+ ast_filedelete(name_out, ext_out);
+ }
+
+ if (fs_in)
+ ast_closestream(fs_in);
+
+ ast_module_unref(ast_module_info->self);
+
+ return ret;
+}
+
+static struct ast_cli_entry cli_convert[] = {
+ AST_CLI_DEFINE(handle_cli_file_convert, "Convert audio file")
+};
+
+static int unload_module(void)
+{
+ ast_cli_unregister_multiple(cli_convert, sizeof(cli_convert) / sizeof(struct ast_cli_entry));
+ return 0;
+}
+
+static int load_module(void)
+{
+ ast_cli_register_multiple(cli_convert, sizeof(cli_convert) / sizeof(struct ast_cli_entry));
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "File format conversion CLI command");
diff --git a/trunk/res/res_crypto.c b/trunk/res/res_crypto.c
new file mode 100644
index 000000000..f1a2234fd
--- /dev/null
+++ b/trunk/res/res_crypto.c
@@ -0,0 +1,624 @@
+/*
+ * 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 Provide Cryptographic Signature capability
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \extref Uses the OpenSSL library, available at
+ * http://www.openssl.org/
+ */
+
+/*** MODULEINFO
+ <depend>ssl</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_KEY_DIR */
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <dirent.h>
+
+#include "asterisk/module.h"
+#include "asterisk/crypto.h"
+#include "asterisk/md5.h"
+#include "asterisk/cli.h"
+#include "asterisk/io.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+
+/*
+ * Asterisk uses RSA keys with SHA-1 message digests for its
+ * digital signatures. The choice of RSA is due to its higher
+ * throughput on verification, and the choice of SHA-1 based
+ * on the recently discovered collisions in MD5's compression
+ * algorithm and recommendations of avoiding MD5 in new schemes
+ * from various industry experts.
+ *
+ * We use OpenSSL to provide our crypto routines, although we never
+ * actually use full-up SSL
+ *
+ */
+
+#define KEY_NEEDS_PASSCODE (1 << 16)
+
+struct ast_key {
+ /*! Name of entity */
+ char name[80];
+ /*! File name */
+ char fn[256];
+ /*! Key type (AST_KEY_PUB or AST_KEY_PRIV, along with flags from above) */
+ int ktype;
+ /*! RSA structure (if successfully loaded) */
+ RSA *rsa;
+ /*! Whether we should be deleted */
+ int delme;
+ /*! FD for input (or -1 if no input allowed, or -2 if we needed input) */
+ int infd;
+ /*! FD for output */
+ int outfd;
+ /*! Last MD5 Digest */
+ unsigned char digest[16];
+ AST_RWLIST_ENTRY(ast_key) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(keys, ast_key);
+
+/*!
+ * \brief setting of priv key
+ * \param buf
+ * \param size
+ * \param rwflag
+ * \param userdata
+ * \return length of string,-1 on failure
+*/
+static int pw_cb(char *buf, int size, int rwflag, void *userdata)
+{
+ struct ast_key *key = (struct ast_key *)userdata;
+ char prompt[256];
+ int res, tmp;
+
+ if (key->infd < 0) {
+ /* Note that we were at least called */
+ key->infd = -2;
+ return -1;
+ }
+
+ snprintf(prompt, sizeof(prompt), ">>>> passcode for %s key '%s': ",
+ key->ktype == AST_KEY_PRIVATE ? "PRIVATE" : "PUBLIC", key->name);
+ write(key->outfd, prompt, strlen(prompt));
+ memset(buf, 0, sizeof(buf));
+ tmp = ast_hide_password(key->infd);
+ memset(buf, 0, size);
+ res = read(key->infd, buf, size);
+ ast_restore_tty(key->infd, tmp);
+ if (buf[strlen(buf) -1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ return strlen(buf);
+}
+
+/*!
+ * \brief return the ast_key structure for name
+ * \see ast_key_get
+*/
+static struct ast_key *__ast_key_get(const char *kname, int ktype)
+{
+ struct ast_key *key;
+
+ AST_RWLIST_RDLOCK(&keys);
+ AST_RWLIST_TRAVERSE(&keys, key, list) {
+ if (!strcmp(kname, key->name) &&
+ (ktype == key->ktype))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&keys);
+
+ return key;
+}
+
+/*!
+ * \brief load RSA key from file
+ * \param dir directory string
+ * \param fname name of file
+ * \param ifd incoming file descriptor
+ * \param ofd outgoing file descriptor
+ * \param not2
+ * \retval key on success.
+ * \retval NULL on failure.
+*/
+static struct ast_key *try_load_key(const char *dir, const char *fname, int ifd, int ofd, int *not2)
+{
+ int ktype = 0, found = 0;
+ char *c = NULL, ffname[256];
+ unsigned char digest[16];
+ FILE *f;
+ struct MD5Context md5;
+ struct ast_key *key;
+ static int notice = 0;
+
+ /* Make sure its name is a public or private key */
+ if ((c = strstr(fname, ".pub")) && !strcmp(c, ".pub"))
+ ktype = AST_KEY_PUBLIC;
+ else if ((c = strstr(fname, ".key")) && !strcmp(c, ".key"))
+ ktype = AST_KEY_PRIVATE;
+ else
+ return NULL;
+
+ /* Get actual filename */
+ snprintf(ffname, sizeof(ffname), "%s/%s", dir, fname);
+
+ /* Open file */
+ if (!(f = fopen(ffname, "r"))) {
+ ast_log(LOG_WARNING, "Unable to open key file %s: %s\n", ffname, strerror(errno));
+ return NULL;
+ }
+
+ MD5Init(&md5);
+ while(!feof(f)) {
+ /* Calculate a "whatever" quality md5sum of the key */
+ char buf[256] = "";
+ fgets(buf, sizeof(buf), f);
+ if (!feof(f))
+ MD5Update(&md5, (unsigned char *) buf, strlen(buf));
+ }
+ MD5Final(digest, &md5);
+
+ /* Look for an existing key */
+ AST_RWLIST_TRAVERSE(&keys, key, list) {
+ if (!strcasecmp(key->fn, ffname))
+ break;
+ }
+
+ if (key) {
+ /* If the MD5 sum is the same, and it isn't awaiting a passcode
+ then this is far enough */
+ if (!memcmp(digest, key->digest, 16) &&
+ !(key->ktype & KEY_NEEDS_PASSCODE)) {
+ fclose(f);
+ key->delme = 0;
+ return NULL;
+ } else {
+ /* Preserve keytype */
+ ktype = key->ktype;
+ /* Recycle the same structure */
+ found++;
+ }
+ }
+
+ /* Make fname just be the normal name now */
+ *c = '\0';
+ if (!key) {
+ if (!(key = ast_calloc(1, sizeof(*key)))) {
+ fclose(f);
+ return NULL;
+ }
+ }
+ /* First the filename */
+ ast_copy_string(key->fn, ffname, sizeof(key->fn));
+ /* Then the name */
+ ast_copy_string(key->name, fname, sizeof(key->name));
+ key->ktype = ktype;
+ /* Yes, assume we're going to be deleted */
+ key->delme = 1;
+ /* Keep the key type */
+ memcpy(key->digest, digest, 16);
+ /* Can I/O takes the FD we're given */
+ key->infd = ifd;
+ key->outfd = ofd;
+ /* Reset the file back to the beginning */
+ rewind(f);
+ /* Now load the key with the right method */
+ if (ktype == AST_KEY_PUBLIC)
+ key->rsa = PEM_read_RSA_PUBKEY(f, NULL, pw_cb, key);
+ else
+ key->rsa = PEM_read_RSAPrivateKey(f, NULL, pw_cb, key);
+ fclose(f);
+ if (key->rsa) {
+ if (RSA_size(key->rsa) == 128) {
+ /* Key loaded okay */
+ key->ktype &= ~KEY_NEEDS_PASSCODE;
+ ast_verb(3, "Loaded %s key '%s'\n", key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name);
+ ast_debug(1, "Key '%s' loaded OK\n", key->name);
+ key->delme = 0;
+ } else
+ ast_log(LOG_NOTICE, "Key '%s' is not expected size.\n", key->name);
+ } else if (key->infd != -2) {
+ ast_log(LOG_WARNING, "Key load %s '%s' failed\n",key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name);
+ if (ofd > -1)
+ ERR_print_errors_fp(stderr);
+ else
+ ERR_print_errors_fp(stderr);
+ } else {
+ ast_log(LOG_NOTICE, "Key '%s' needs passcode.\n", key->name);
+ key->ktype |= KEY_NEEDS_PASSCODE;
+ if (!notice) {
+ if (!ast_opt_init_keys)
+ ast_log(LOG_NOTICE, "Add the '-i' flag to the asterisk command line if you want to automatically initialize passcodes at launch.\n");
+ notice++;
+ }
+ /* Keep it anyway */
+ key->delme = 0;
+ /* Print final notice about "init keys" when done */
+ *not2 = 1;
+ }
+
+ /* If this is a new key add it to the list */
+ if (!found)
+ AST_RWLIST_INSERT_TAIL(&keys, key, list);
+
+ return key;
+}
+
+/*!
+ * \brief signs outgoing message with public key
+ * \see ast_sign_bin
+*/
+static int __ast_sign_bin(struct ast_key *key, const char *msg, int msglen, unsigned char *dsig)
+{
+ unsigned char digest[20];
+ unsigned int siglen = 128;
+ int res;
+
+ if (key->ktype != AST_KEY_PRIVATE) {
+ ast_log(LOG_WARNING, "Cannot sign with a public key\n");
+ return -1;
+ }
+
+ /* Calculate digest of message */
+ SHA1((unsigned char *)msg, msglen, digest);
+
+ /* Verify signature */
+ if (!(res = RSA_sign(NID_sha1, digest, sizeof(digest), dsig, &siglen, key->rsa))) {
+ ast_log(LOG_WARNING, "RSA Signature (key %s) failed\n", key->name);
+ return -1;
+ }
+
+ if (siglen != 128) {
+ ast_log(LOG_WARNING, "Unexpected signature length %d, expecting %d\n", (int)siglen, (int)128);
+ return -1;
+ }
+
+ return 0;
+
+}
+
+/*!
+ * \brief decrypt a message
+ * \see ast_decrypt_bin
+*/
+static int __ast_decrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
+{
+ int res, pos = 0;
+
+ if (key->ktype != AST_KEY_PRIVATE) {
+ ast_log(LOG_WARNING, "Cannot decrypt with a public key\n");
+ return -1;
+ }
+
+ if (srclen % 128) {
+ ast_log(LOG_NOTICE, "Tried to decrypt something not a multiple of 128 bytes\n");
+ return -1;
+ }
+
+ while(srclen) {
+ /* Process chunks 128 bytes at a time */
+ if ((res = RSA_private_decrypt(128, src, dst, key->rsa, RSA_PKCS1_OAEP_PADDING)) < 0)
+ return -1;
+ pos += res;
+ src += 128;
+ srclen -= 128;
+ dst += res;
+ }
+
+ return pos;
+}
+
+/*!
+ * \brief encrypt a message
+ * \see ast_encrypt_bin
+*/
+static int __ast_encrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
+{
+ int res, bytes, pos = 0;
+
+ if (key->ktype != AST_KEY_PUBLIC) {
+ ast_log(LOG_WARNING, "Cannot encrypt with a private key\n");
+ return -1;
+ }
+
+ while(srclen) {
+ bytes = srclen;
+ if (bytes > 128 - 41)
+ bytes = 128 - 41;
+ /* Process chunks 128-41 bytes at a time */
+ if ((res = RSA_public_encrypt(bytes, src, dst, key->rsa, RSA_PKCS1_OAEP_PADDING)) != 128) {
+ ast_log(LOG_NOTICE, "How odd, encrypted size is %d\n", res);
+ return -1;
+ }
+ src += bytes;
+ srclen -= bytes;
+ pos += res;
+ dst += res;
+ }
+ return pos;
+}
+
+/*!
+ * \brief wrapper for __ast_sign_bin then base64 encode it
+ * \see ast_sign
+*/
+static int __ast_sign(struct ast_key *key, char *msg, char *sig)
+{
+ unsigned char dsig[128];
+ int siglen = sizeof(dsig), res;
+
+ if (!(res = ast_sign_bin(key, msg, strlen(msg), dsig)))
+ /* Success -- encode (256 bytes max as documented) */
+ ast_base64encode(sig, dsig, siglen, 256);
+
+ return res;
+}
+
+/*!
+ * \brief check signature of a message
+ * \see ast_check_signature_bin
+*/
+static int __ast_check_signature_bin(struct ast_key *key, const char *msg, int msglen, const unsigned char *dsig)
+{
+ unsigned char digest[20];
+ int res;
+
+ if (key->ktype != AST_KEY_PUBLIC) {
+ /* Okay, so of course you really *can* but for our purposes
+ we're going to say you can't */
+ ast_log(LOG_WARNING, "Cannot check message signature with a private key\n");
+ return -1;
+ }
+
+ /* Calculate digest of message */
+ SHA1((unsigned char *)msg, msglen, digest);
+
+ /* Verify signature */
+ if (!(res = RSA_verify(NID_sha1, digest, sizeof(digest), (unsigned char *)dsig, 128, key->rsa))) {
+ ast_debug(1, "Key failed verification: %s\n", key->name);
+ return -1;
+ }
+
+ /* Pass */
+ return 0;
+}
+
+/*!
+ * \brief base64 decode then sent to __ast_check_signature_bin
+ * \see ast_check_signature
+*/
+static int __ast_check_signature(struct ast_key *key, const char *msg, const char *sig)
+{
+ unsigned char dsig[128];
+ int res;
+
+ /* Decode signature */
+ if ((res = ast_base64decode(dsig, sig, sizeof(dsig))) != sizeof(dsig)) {
+ ast_log(LOG_WARNING, "Signature improper length (expect %d, got %d)\n", (int)sizeof(dsig), (int)res);
+ return -1;
+ }
+
+ res = ast_check_signature_bin(key, msg, strlen(msg), dsig);
+
+ return res;
+}
+
+/*!
+ * \brief refresh RSA keys from file
+ * \param ifd file descriptor
+ * \param ofd file descriptor
+ * \return void
+*/
+static void crypto_load(int ifd, int ofd)
+{
+ struct ast_key *key;
+ DIR *dir = NULL;
+ struct dirent *ent;
+ int note = 0;
+
+ AST_RWLIST_WRLOCK(&keys);
+
+ /* Mark all keys for deletion */
+ AST_RWLIST_TRAVERSE(&keys, key, list) {
+ key->delme = 1;
+ }
+
+ /* Load new keys */
+ if ((dir = opendir(ast_config_AST_KEY_DIR))) {
+ while((ent = readdir(dir))) {
+ try_load_key(ast_config_AST_KEY_DIR, ent->d_name, ifd, ofd, &note);
+ }
+ closedir(dir);
+ } else
+ ast_log(LOG_WARNING, "Unable to open key directory '%s'\n", ast_config_AST_KEY_DIR);
+
+ if (note)
+ ast_log(LOG_NOTICE, "Please run the command 'init keys' to enter the passcodes for the keys\n");
+
+ /* Delete any keys that are no longer present */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&keys, key, list) {
+ if (key->delme) {
+ ast_debug(1, "Deleting key %s type %d\n", key->name, key->ktype);
+ AST_RWLIST_REMOVE_CURRENT(list);
+ if (key->rsa)
+ RSA_free(key->rsa);
+ ast_free(key);
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ AST_RWLIST_UNLOCK(&keys);
+}
+
+static void md52sum(char *sum, unsigned char *md5)
+{
+ int x;
+ for (x = 0; x < 16; x++)
+ sum += sprintf(sum, "%02x", *(md5++));
+}
+
+/*!
+ * \brief show the list of RSA keys
+ * \param e CLI command
+ * \param cmd
+ * \param a list of CLI arguments
+ * \return CLI_SUCCESS
+*/
+static char *handle_cli_keys_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-18s %-8s %-16s %-33s\n"
+
+ struct ast_key *key;
+ char sum[16 * 2 + 1];
+ int count_keys = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "keys show";
+ e->usage =
+ "Usage: keys show\n"
+ " Displays information about RSA keys known by Asterisk\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, FORMAT, "Key Name", "Type", "Status", "Sum");
+ ast_cli(a->fd, FORMAT, "------------------", "--------", "----------------", "--------------------------------");
+
+ AST_RWLIST_RDLOCK(&keys);
+ AST_RWLIST_TRAVERSE(&keys, key, list) {
+ md52sum(sum, key->digest);
+ ast_cli(a->fd, FORMAT, key->name,
+ (key->ktype & 0xf) == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE",
+ key->ktype & KEY_NEEDS_PASSCODE ? "[Needs Passcode]" : "[Loaded]", sum);
+ count_keys++;
+ }
+ AST_RWLIST_UNLOCK(&keys);
+
+ ast_cli(a->fd, "\n%d known RSA keys.\n", count_keys);
+
+ return CLI_SUCCESS;
+
+#undef FORMAT
+}
+
+/*!
+ * \brief initialize all RSA keys
+ * \param e CLI command
+ * \param cmd
+ * \param a list of CLI arguments
+ * \return CLI_SUCCESS
+*/
+static char *handle_cli_keys_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_key *key;
+ int ign;
+ char *kn, tmp[256] = "";
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "keys init";
+ e->usage =
+ "Usage: keys init\n"
+ " Initializes private keys (by reading in pass code from\n"
+ " the user)\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_WRLOCK(&keys);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&keys, key, list) {
+ /* Reload keys that need pass codes now */
+ if (key->ktype & KEY_NEEDS_PASSCODE) {
+ kn = key->fn + strlen(ast_config_AST_KEY_DIR) + 1;
+ ast_copy_string(tmp, kn, sizeof(tmp));
+ try_load_key(ast_config_AST_KEY_DIR, tmp, a->fd, a->fd, &ign);
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+ AST_RWLIST_UNLOCK(&keys);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_crypto[] = {
+ AST_CLI_DEFINE(handle_cli_keys_show, "Displays RSA key information"),
+ AST_CLI_DEFINE(handle_cli_keys_init, "Initialize RSA key passcodes")
+};
+
+/*! \brief initialise the res_crypto module */
+static int crypto_init(void)
+{
+ SSL_library_init();
+ ERR_load_crypto_strings();
+ ast_cli_register_multiple(cli_crypto, sizeof(cli_crypto) / sizeof(struct ast_cli_entry));
+
+ /* Install ourselves into stubs */
+ ast_key_get = __ast_key_get;
+ ast_check_signature = __ast_check_signature;
+ ast_check_signature_bin = __ast_check_signature_bin;
+ ast_sign = __ast_sign;
+ ast_sign_bin = __ast_sign_bin;
+ ast_encrypt_bin = __ast_encrypt_bin;
+ ast_decrypt_bin = __ast_decrypt_bin;
+ return 0;
+}
+
+static int reload(void)
+{
+ crypto_load(-1, -1);
+ return 0;
+}
+
+static int load_module(void)
+{
+ crypto_init();
+ if (ast_opt_init_keys)
+ crypto_load(STDIN_FILENO, STDOUT_FILENO);
+ else
+ crypto_load(-1, -1);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ /* Can't unload this once we're loaded */
+ return -1;
+}
+
+/* needs usecount semantics defined */
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Cryptographic Digital Signatures",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload
+ );
diff --git a/trunk/res/res_features.c b/trunk/res/res_features.c
new file mode 100644
index 000000000..d9e8ef55f
--- /dev/null
+++ b/trunk/res/res_features.c
@@ -0,0 +1,3444 @@
+/*
+ * 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 Routines implementing call features as call pickup, parking and transfer
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/causes.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/app.h"
+#include "asterisk/say.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/utils.h"
+#include "asterisk/adsi.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/monitor.h"
+#include "asterisk/audiohook.h"
+
+#define DEFAULT_PARK_TIME 45000
+#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
+#define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
+#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
+#define DEFAULT_ATXFER_DROP_CALL 0
+#define DEFAULT_ATXFER_LOOP_DELAY 10000
+#define DEFAULT_ATXFER_CALLBACK_RETRIES 2
+
+#define AST_MAX_WATCHERS 256
+
+enum {
+ AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
+ AST_FEATURE_FLAG_ONPEER = (1 << 1),
+ AST_FEATURE_FLAG_ONSELF = (1 << 2),
+ AST_FEATURE_FLAG_BYCALLEE = (1 << 3),
+ AST_FEATURE_FLAG_BYCALLER = (1 << 4),
+ AST_FEATURE_FLAG_BYBOTH = (3 << 3),
+};
+
+struct feature_group_exten {
+ AST_LIST_ENTRY(feature_group_exten) entry;
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(exten);
+ );
+ struct ast_call_feature *feature;
+};
+
+struct feature_group {
+ AST_LIST_ENTRY(feature_group) entry;
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(gname);
+ );
+ AST_LIST_HEAD_NOLOCK(, feature_group_exten) features;
+};
+
+static AST_RWLIST_HEAD_STATIC(feature_groups, feature_group);
+
+static char *parkedcall = "ParkedCall";
+
+static int parkaddhints = 0; /*!< Add parking hints automatically */
+static int parkedcalltransfers = 0; /*!< Enable DTMF based transfers on bridge when picking up parked calls */
+static int parkedcallreparking = 0; /*!< Enable DTMF based parking on bridge when picking up parked calls */
+static int parkingtime = DEFAULT_PARK_TIME; /*!< No more than 45 seconds parked before you do something with them */
+static char parking_con[AST_MAX_EXTENSION]; /*!< Context for which parking is made accessible */
+static char parking_con_dial[AST_MAX_EXTENSION]; /*!< Context for dialback for parking (KLUDGE) */
+static char parking_ext[AST_MAX_EXTENSION]; /*!< Extension you type to park the call */
+static char pickup_ext[AST_MAX_EXTENSION]; /*!< Call pickup extension */
+static char parkmohclass[MAX_MUSICCLASS]; /*!< Music class used for parking */
+static int parking_start; /*!< First available extension for parking */
+static int parking_stop; /*!< Last available extension for parking */
+
+static char courtesytone[256]; /*!< Courtesy tone */
+static int parkedplay = 0; /*!< Who to play the courtesy tone to */
+static char xfersound[256]; /*!< Call transfer sound */
+static char xferfailsound[256]; /*!< Call transfer failure sound */
+
+static int parking_offset;
+static int parkfindnext;
+
+static int adsipark;
+
+static int transferdigittimeout;
+static int featuredigittimeout;
+static int comebacktoorigin = 1;
+
+static int atxfernoanswertimeout;
+static unsigned int atxferdropcall;
+static unsigned int atxferloopdelay;
+static unsigned int atxfercallbackretries;
+
+static char *registrar = "res_features"; /*!< Registrar for operations */
+
+/* module and CLI command definitions */
+static char *synopsis = "Answer a parked call";
+
+static char *descrip = "ParkedCall(exten): "
+"Used to connect to a parked call. This application is always\n"
+"registered internally and does not need to be explicitly added\n"
+"into the dialplan, although you should include the 'parkedcalls'\n"
+"context. If no extension is provided, then the first available\n"
+"parked call will be acquired.\n";
+
+static char *parkcall = "Park";
+
+static char *synopsis2 = "Park yourself";
+
+static char *descrip2 = "Park(): "
+"Used to park yourself (typically in combination with a supervised\n"
+"transfer to know the parking space). This application is always\n"
+"registered internally and does not need to be explicitly added\n"
+"into the dialplan, although you should include the 'parkedcalls'\n"
+"context (or the context specified in features.conf).\n\n"
+"If you set the PARKINGEXTEN variable to an extension in your\n"
+"parking context, Park() will park the call on that extension, unless\n"
+"it already exists. In that case, execution will continue at next\n"
+"priority.\n" ;
+
+static struct ast_app *monitor_app = NULL;
+static int monitor_ok = 1;
+
+static struct ast_app *mixmonitor_app = NULL;
+static int mixmonitor_ok = 1;
+
+static struct ast_app *stopmixmonitor_app = NULL;
+static int stopmixmonitor_ok = 1;
+
+struct parkeduser {
+ struct ast_channel *chan; /*!< Parking channel */
+ struct timeval start; /*!< Time the parking started */
+ int parkingnum; /*!< Parking lot */
+ char parkingexten[AST_MAX_EXTENSION]; /*!< If set beforehand, parking extension used for this call */
+ char context[AST_MAX_CONTEXT]; /*!< Where to go if our parking time expires */
+ char exten[AST_MAX_EXTENSION];
+ int priority;
+ int parkingtime; /*!< Maximum length in parking lot before return */
+ int notquiteyet;
+ char peername[1024];
+ unsigned char moh_trys;
+ AST_LIST_ENTRY(parkeduser) list;
+};
+
+static AST_LIST_HEAD_STATIC(parkinglot, parkeduser);
+
+static pthread_t parking_thread;
+
+const char *ast_parking_ext(void)
+{
+ return parking_ext;
+}
+
+const char *ast_pickup_ext(void)
+{
+ return pickup_ext;
+}
+
+struct ast_bridge_thread_obj
+{
+ struct ast_bridge_config bconfig;
+ struct ast_channel *chan;
+ struct ast_channel *peer;
+ unsigned int return_to_pbx:1;
+};
+
+
+
+/*!
+ * \brief store context, extension and priority
+ * \param chan, context, ext, pri
+*/
+static void set_c_e_p(struct ast_channel *chan, const char *context, const char *ext, int pri)
+{
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ ast_copy_string(chan->exten, ext, sizeof(chan->exten));
+ chan->priority = pri;
+}
+
+/*!
+ * \brief Check goto on transfer
+ * \param chan
+ *
+ * Check if channel has 'GOTO_ON_BLINDXFR' set, if not exit.
+ * When found make sure the types are compatible. Check if channel is valid
+ * if so start the new channel else hangup the call.
+*/
+static void check_goto_on_transfer(struct ast_channel *chan)
+{
+ struct ast_channel *xferchan;
+ const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
+ char *x, *goto_on_transfer;
+ struct ast_frame *f;
+
+ if (ast_strlen_zero(val))
+ return;
+
+ goto_on_transfer = ast_strdupa(val);
+
+ if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, chan->name)))
+ return;
+
+ for (x = goto_on_transfer; x && *x; x++) {
+ if (*x == '^')
+ *x = '|';
+ }
+ /* Make formats okay */
+ xferchan->readformat = chan->readformat;
+ xferchan->writeformat = chan->writeformat;
+ ast_channel_masquerade(xferchan, chan);
+ ast_parseable_goto(xferchan, goto_on_transfer);
+ xferchan->_state = AST_STATE_UP;
+ ast_clear_flag(xferchan, AST_FLAGS_ALL);
+ xferchan->_softhangup = 0;
+ if ((f = ast_read(xferchan))) {
+ ast_frfree(f);
+ f = NULL;
+ ast_pbx_start(xferchan);
+ } else {
+ ast_hangup(xferchan);
+ }
+}
+
+static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, struct ast_channel *transferee, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, int igncallerstate);
+
+/*!
+ * \brief bridge the call
+ * \param data thread bridge.
+ *
+ * Set Last Data for respective channels, reset cdr for channels
+ * bridge call, check if we're going back to dialplan
+ * if not hangup both legs of the call
+*/
+static void *ast_bridge_call_thread(void *data)
+{
+ struct ast_bridge_thread_obj *tobj = data;
+ int res;
+
+ tobj->chan->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
+ tobj->chan->data = tobj->peer->name;
+ tobj->peer->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
+ tobj->peer->data = tobj->chan->name;
+
+ if (tobj->chan->cdr) {
+ ast_cdr_reset(tobj->chan->cdr, NULL);
+ ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
+ }
+ if (tobj->peer->cdr) {
+ ast_cdr_reset(tobj->peer->cdr, NULL);
+ ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
+ }
+
+ ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
+
+ if (tobj->return_to_pbx) {
+ if (!ast_check_hangup(tobj->peer)) {
+ ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", tobj->peer->name);
+ res = ast_pbx_start(tobj->peer);
+ if (res != AST_PBX_SUCCESS)
+ ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", tobj->peer->name);
+ } else
+ ast_hangup(tobj->peer);
+ if (!ast_check_hangup(tobj->chan)) {
+ ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", tobj->chan->name);
+ res = ast_pbx_start(tobj->chan);
+ if (res != AST_PBX_SUCCESS)
+ ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", tobj->chan->name);
+ } else
+ ast_hangup(tobj->chan);
+ } else {
+ ast_hangup(tobj->chan);
+ ast_hangup(tobj->peer);
+ }
+
+ ast_free(tobj);
+
+ return NULL;
+}
+
+/*!
+ * \brief create thread for the parked call
+ * \param data
+ *
+ * Create thread and attributes, call ast_bridge_call_thread
+*/
+static void ast_bridge_call_thread_launch(void *data)
+{
+ pthread_t thread;
+ pthread_attr_t attr;
+ struct sched_param sched;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
+ pthread_attr_destroy(&attr);
+ memset(&sched, 0, sizeof(sched));
+ pthread_setschedparam(thread, SCHED_RR, &sched);
+}
+
+/*!
+ * \brief Announce call parking by ADSI
+ * \param chan .
+ * \param parkingexten .
+ * Create message to show for ADSI, display message.
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+static int adsi_announce_park(struct ast_channel *chan, char *parkingexten)
+{
+ int res;
+ int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
+ char tmp[256];
+ char *message[5] = {NULL, NULL, NULL, NULL, NULL};
+
+ snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
+ message[0] = tmp;
+ res = ast_adsi_load_session(chan, NULL, 0, 1);
+ if (res == -1)
+ return res;
+ return ast_adsi_print(chan, message, justify, 1);
+}
+
+/*! \brief Notify metermaids that we've changed an extension */
+static void notify_metermaids(const char *exten, char *context, enum ast_device_state state)
+{
+ ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'",
+ exten, context, devstate2str(state));
+
+ ast_devstate_changed(state, "park:%s@%s", exten, context);
+}
+
+/*! \brief metermaids callback from devicestate.c */
+static enum ast_device_state metermaidstate(const char *data)
+{
+ char *context;
+ char *exten;
+
+ context = ast_strdupa(data);
+
+ exten = strsep(&context, "@");
+ if (!context)
+ return AST_DEVICE_INVALID;
+
+ ast_debug(4, "Checking state of exten %s in context %s\n", exten, context);
+
+ if (!ast_exists_extension(NULL, context, exten, 1, NULL))
+ return AST_DEVICE_NOT_INUSE;
+
+ return AST_DEVICE_INUSE;
+}
+
+/* Park a call */
+static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, char *orig_chan_name)
+{
+ struct parkeduser *pu, *cur;
+ int i, x = -1, parking_range;
+ struct ast_context *con;
+ const char *parkingexten;
+
+ /* Allocate memory for parking data */
+ if (!(pu = ast_calloc(1, sizeof(*pu))))
+ return -1;
+
+ /* Lock parking lot */
+ AST_LIST_LOCK(&parkinglot);
+ /* Check for channel variable PARKINGEXTEN */
+ parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN");
+ if (!ast_strlen_zero(parkingexten)) {
+ if (ast_exists_extension(NULL, parking_con, parkingexten, 1, NULL)) {
+ AST_LIST_UNLOCK(&parkinglot);
+ ast_free(pu);
+ ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con);
+ return 1; /* Continue execution if possible */
+ }
+ ast_copy_string(pu->parkingexten, parkingexten, sizeof(pu->parkingexten));
+ x = atoi(parkingexten);
+ } else {
+ /* Select parking space within range */
+ parking_range = parking_stop - parking_start+1;
+ for (i = 0; i < parking_range; i++) {
+ x = (i + parking_offset) % parking_range + parking_start;
+ AST_LIST_TRAVERSE(&parkinglot, cur, list) {
+ if (cur->parkingnum == x)
+ break;
+ }
+ if (!cur)
+ break;
+ }
+
+ if (!(i < parking_range)) {
+ ast_log(LOG_WARNING, "No more parking spaces\n");
+ ast_free(pu);
+ AST_LIST_UNLOCK(&parkinglot);
+ return -1;
+ }
+ /* Set pointer for next parking */
+ if (parkfindnext)
+ parking_offset = x - parking_start + 1;
+ }
+
+ chan->appl = "Parked Call";
+ chan->data = NULL;
+
+ pu->chan = chan;
+
+ /* Put the parked channel on hold if we have two different channels */
+ if (chan != peer) {
+ ast_indicate_data(pu->chan, AST_CONTROL_HOLD,
+ S_OR(parkmohclass, NULL),
+ !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
+ }
+
+ pu->start = ast_tvnow();
+ pu->parkingnum = x;
+ pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
+ if (extout)
+ *extout = x;
+
+ if (peer)
+ ast_copy_string(pu->peername, peer->name, sizeof(pu->peername));
+
+ /* Remember what had been dialed, so that if the parking
+ expires, we try to come back to the same place */
+ ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
+ ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
+ pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
+ AST_LIST_INSERT_TAIL(&parkinglot, pu, list);
+
+ /* If parking a channel directly, don't quiet yet get parking running on it */
+ if (peer == chan)
+ pu->notquiteyet = 1;
+ AST_LIST_UNLOCK(&parkinglot);
+ /* Wake up the (presumably select()ing) thread */
+ pthread_kill(parking_thread, SIGURG);
+ ast_verb(2, "Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parking_con, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
+
+ if (pu->parkingnum != -1)
+ snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x);
+ manager_event(EVENT_FLAG_CALL, "ParkedCall",
+ "Exten: %s\r\n"
+ "Channel: %s\r\n"
+ "From: %s\r\n"
+ "Timeout: %ld\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n",
+ pu->parkingexten, pu->chan->name, peer ? peer->name : "",
+ (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
+ S_OR(pu->chan->cid.cid_num, "<unknown>"),
+ S_OR(pu->chan->cid.cid_name, "<unknown>")
+ );
+
+ if (peer && adsipark && ast_adsi_available(peer)) {
+ adsi_announce_park(peer, pu->parkingexten); /* Only supports parking numbers */
+ ast_adsi_unload_session(peer);
+ }
+
+ con = ast_context_find(parking_con);
+ if (!con)
+ con = ast_context_create(NULL, parking_con, registrar);
+ if (!con) /* Still no context? Bad */
+ ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
+ /* Tell the peer channel the number of the parking space */
+ if (peer && ((pu->parkingnum != -1 && ast_strlen_zero(orig_chan_name)) || !strcasecmp(peer->name, orig_chan_name))) { /* Only say number if it's a number and the channel hasn't been masqueraded away */
+ /* Make sure we don't start saying digits to the channel being parked */
+ ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM);
+ ast_say_digits(peer, pu->parkingnum, "", peer->language);
+ ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM);
+ }
+ if (con) {
+ if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, ast_strdup(pu->parkingexten), ast_free_ptr, registrar))
+ notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_INUSE);
+ }
+ if (pu->notquiteyet) {
+ /* Wake up parking thread if we're really done */
+ ast_indicate_data(pu->chan, AST_CONTROL_HOLD,
+ S_OR(parkmohclass, NULL),
+ !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
+ pu->notquiteyet = 0;
+ pthread_kill(parking_thread, SIGURG);
+ }
+ return 0;
+}
+
+/*! \brief Park a call */
+int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
+{
+ return park_call_full(chan, peer, timeout, extout, NULL);
+}
+
+/* Park call via masquraded channel */
+int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
+{
+ struct ast_channel *chan;
+ struct ast_frame *f;
+
+ /* Make a new, fake channel that we'll use to masquerade in the real one */
+ if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) {
+ ast_log(LOG_WARNING, "Unable to create parked channel\n");
+ return -1;
+ }
+
+ /* Make formats okay */
+ chan->readformat = rchan->readformat;
+ chan->writeformat = rchan->writeformat;
+ ast_channel_masquerade(chan, rchan);
+
+ /* Setup the extensions and such */
+ set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
+
+ /* Make the masq execute */
+ f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+
+ ast_park_call(chan, peer, timeout, extout);
+ return 0;
+}
+
+
+#define FEATURE_RETURN_HANGUP -1
+#define FEATURE_RETURN_SUCCESSBREAK 0
+#define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
+#define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
+#define FEATURE_RETURN_PASSDIGITS 21
+#define FEATURE_RETURN_STOREDIGITS 22
+#define FEATURE_RETURN_SUCCESS 23
+#define FEATURE_RETURN_KEEPTRYING 24
+
+#define FEATURE_SENSE_CHAN (1 << 0)
+#define FEATURE_SENSE_PEER (1 << 1)
+
+/*!
+ * \brief set caller and callee according to the direction
+ * \param caller, callee, peer, chan, sense
+ *
+ * Detect who triggered feature and set callee/caller variables accordingly
+*/
+static void set_peers(struct ast_channel **caller, struct ast_channel **callee,
+ struct ast_channel *peer, struct ast_channel *chan, int sense)
+{
+ if (sense == FEATURE_SENSE_PEER) {
+ *caller = peer;
+ *callee = chan;
+ } else {
+ *callee = peer;
+ *caller = chan;
+ }
+}
+
+/*!
+ * \brief support routing for one touch call parking
+ * \param chan channel parking call
+ * \param peer channel to be parked
+ * \param config unsed
+ * \param code unused
+ * \param sense feature options
+ *
+ * \param data
+ * Setup channel, set return exten,priority to 's,1'
+ * answer chan, sleep chan, park call
+*/
+static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+ struct ast_channel *parker;
+ struct ast_channel *parkee;
+ int res = 0;
+ struct ast_module_user *u;
+
+ u = ast_module_user_add(chan);
+
+ set_peers(&parker, &parkee, peer, chan, sense);
+ /* Setup the exten/priority to be s/1 since we don't know
+ where this call should return */
+ strcpy(chan->exten, "s");
+ chan->priority = 1;
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ if (!res)
+ res = ast_park_call(parkee, parker, 0, NULL);
+
+ ast_module_user_remove(u);
+
+ if (!res) {
+ if (sense == FEATURE_SENSE_CHAN)
+ res = AST_PBX_NO_HANGUP_PEER;
+ else
+ res = AST_PBX_KEEPALIVE;
+ }
+ return res;
+
+}
+
+/*!
+ * \brief Monitor a channel by DTMF
+ * \param chan channel requesting monitor
+ * \param peer channel to be monitored
+ * \param config
+ * \param code
+ * \param sense feature options
+ *
+ * \param data
+ * Check monitor app enabled, setup channels, both caller/callee chans not null
+ * get TOUCH_MONITOR variable for filename if exists, exec monitor app.
+ * \retval FEATURE_RETURN_SUCCESS on success.
+ * \retval -1 on error.
+*/
+static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+ char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
+ int x = 0;
+ size_t len;
+ struct ast_channel *caller_chan, *callee_chan;
+
+ if (!monitor_ok) {
+ ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
+ return -1;
+ }
+
+ if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
+ monitor_ok = 0;
+ ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
+ return -1;
+ }
+
+ set_peers(&caller_chan, &callee_chan, peer, chan, sense);
+
+ if (!ast_strlen_zero(courtesytone)) {
+ if (ast_autoservice_start(callee_chan))
+ return -1;
+ if (ast_stream_and_wait(caller_chan, courtesytone, "")) {
+ ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+ ast_autoservice_stop(callee_chan);
+ return -1;
+ }
+ if (ast_autoservice_stop(callee_chan))
+ return -1;
+ }
+
+ if (callee_chan->monitor) {
+ ast_verb(4, "User hit '%s' to stop recording call.\n", code);
+ ast_monitor_stop(callee_chan, 1);
+ return FEATURE_RETURN_SUCCESS;
+ }
+
+ if (caller_chan && callee_chan) {
+ const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
+ const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
+ const char *touch_monitor_prefix = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_PREFIX");
+
+ if (!touch_format)
+ touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
+
+ if (!touch_monitor)
+ touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
+
+ if (!touch_monitor_prefix)
+ touch_monitor_prefix = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_PREFIX");
+
+ if (touch_monitor) {
+ len = strlen(touch_monitor) + 50;
+ args = alloca(len);
+ touch_filename = alloca(len);
+ snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor);
+ snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename);
+ } else {
+ caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
+ callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
+ len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
+ args = alloca(len);
+ touch_filename = alloca(len);
+ snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, callee_chan_id);
+ snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename);
+ }
+
+ for(x = 0; x < strlen(args); x++) {
+ if (args[x] == '/')
+ args[x] = '-';
+ }
+
+ ast_verb(4, "User hit '%s' to record call. filename: %s\n", code, args);
+
+ pbx_exec(callee_chan, monitor_app, args);
+ pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
+ pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
+
+ return FEATURE_RETURN_SUCCESS;
+ }
+
+ ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");
+ return -1;
+}
+
+static int builtin_automixmonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+ char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
+ int x = 0;
+ size_t len;
+ struct ast_channel *caller_chan, *callee_chan;
+ const char *mixmonitor_spy_type = "MixMonitor";
+ int count = 0;
+
+ if (!mixmonitor_ok) {
+ ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
+ return -1;
+ }
+
+ if (!(mixmonitor_app = pbx_findapp("MixMonitor"))) {
+ mixmonitor_ok = 0;
+ ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
+ return -1;
+ }
+
+ set_peers(&caller_chan, &callee_chan, peer, chan, sense);
+
+ if (!ast_strlen_zero(courtesytone)) {
+ if (ast_autoservice_start(callee_chan))
+ return -1;
+ if (ast_stream_and_wait(caller_chan, courtesytone, "")) {
+ ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+ ast_autoservice_stop(callee_chan);
+ return -1;
+ }
+ if (ast_autoservice_stop(callee_chan))
+ return -1;
+ }
+
+ ast_channel_lock(callee_chan);
+ count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
+ ast_channel_unlock(callee_chan);
+
+ // This means a mixmonitor is attached to the channel, running or not is unknown.
+ if (count > 0) {
+
+ ast_verb(3, "User hit '%s' to stop recording call.\n", code);
+
+ //Make sure they are running
+ ast_channel_lock(callee_chan);
+ count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
+ ast_channel_unlock(callee_chan);
+ if (count > 0) {
+ if (!stopmixmonitor_ok) {
+ ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
+ return -1;
+ }
+ if (!(stopmixmonitor_app = pbx_findapp("StopMixMonitor"))) {
+ stopmixmonitor_ok = 0;
+ ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
+ return -1;
+ } else {
+ pbx_exec(callee_chan, stopmixmonitor_app, "");
+ return FEATURE_RETURN_SUCCESS;
+ }
+ }
+
+ ast_log(LOG_WARNING,"Stopped MixMonitors are attached to the channel.\n");
+ }
+
+ if (caller_chan && callee_chan) {
+ const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR_FORMAT");
+ const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR");
+
+ if (!touch_format)
+ touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR_FORMAT");
+
+ if (!touch_monitor)
+ touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR");
+
+ if (touch_monitor) {
+ len = strlen(touch_monitor) + 50;
+ args = alloca(len);
+ touch_filename = alloca(len);
+ snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
+ snprintf(args, len, "%s.%s,b", touch_filename, (touch_format) ? touch_format : "wav");
+ } else {
+ caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
+ callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
+ len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
+ args = alloca(len);
+ touch_filename = alloca(len);
+ snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
+ snprintf(args, len, "%s.%s,b", touch_filename, S_OR(touch_format, "wav"));
+ }
+
+ for( x = 0; x < strlen(args); x++) {
+ if (args[x] == '/')
+ args[x] = '-';
+ }
+
+ ast_verb(3, "User hit '%s' to record call. filename: %s\n", code, touch_filename);
+
+ pbx_exec(callee_chan, mixmonitor_app, args);
+ pbx_builtin_setvar_helper(callee_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
+ pbx_builtin_setvar_helper(caller_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
+ return FEATURE_RETURN_SUCCESS;
+
+ }
+
+ ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");
+ return -1;
+
+}
+
+static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+ ast_verb(4, "User hit '%s' to disconnect call.\n", code);
+ return FEATURE_RETURN_HANGUP;
+}
+
+static int finishup(struct ast_channel *chan)
+{
+ ast_indicate(chan, AST_CONTROL_UNHOLD);
+
+ return ast_autoservice_stop(chan);
+}
+
+/*!
+ * \brief Find the context for the transfer
+ * \param transferer
+ * \param transferee
+ *
+ * Grab the TRANSFER_CONTEXT, if fails try grabbing macrocontext.
+ * \return a context string
+*/
+static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee)
+{
+ const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
+ if (ast_strlen_zero(s))
+ s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
+ if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */
+ s = transferer->macrocontext;
+ if (ast_strlen_zero(s))
+ s = transferer->context;
+ return s;
+}
+
+/*!
+ * \brief Blind transfer user to another extension
+ * \param chan channel to be transfered
+ * \param peer channel initiated blind transfer
+ * \param config
+ * \param code
+ * \param data
+ * \param sense feature options
+ *
+ * Place chan on hold, check if transferred to parkinglot extension,
+ * otherwise check extension exists and transfer caller.
+ * \retval FEATURE_RETURN_SUCCESS.
+ * \retval -1 on failure.
+*/
+static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+ struct ast_channel *transferer;
+ struct ast_channel *transferee;
+ const char *transferer_real_context;
+ char xferto[256];
+ int res;
+
+ set_peers(&transferer, &transferee, peer, chan, sense);
+ transferer_real_context = real_ctx(transferer, transferee);
+ /* Start autoservice on chan while we talk to the originator */
+ ast_autoservice_start(transferee);
+ ast_indicate(transferee, AST_CONTROL_HOLD);
+
+ memset(xferto, 0, sizeof(xferto));
+
+ /* Transfer */
+ res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
+ if (res < 0) {
+ finishup(transferee);
+ return -1; /* error ? */
+ }
+ if (res > 0) /* If they've typed a digit already, handle it */
+ xferto[0] = (char) res;
+
+ ast_stopstream(transferer);
+ res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
+ if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */
+ finishup(transferee);
+ return res;
+ }
+ if (!strcmp(xferto, ast_parking_ext())) {
+ res = finishup(transferee);
+ if (res)
+ res = -1;
+ else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */
+ /* We return non-zero, but tell the PBX not to hang the channel when
+ the thread dies -- We have to be careful now though. We are responsible for
+ hanging up the channel, else it will never be hung up! */
+
+ return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER;
+ } else {
+ ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
+ }
+ /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
+ } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
+ pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", transferee->name);
+ pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
+ res=finishup(transferee);
+ if (!transferer->cdr) {
+ transferer->cdr=ast_cdr_alloc();
+ if (transferer) {
+ ast_cdr_init(transferer->cdr, transferer); /* initilize our channel's cdr */
+ ast_cdr_start(transferer->cdr);
+ }
+ }
+ if (transferer->cdr) {
+ ast_cdr_setdestchan(transferer->cdr, transferee->name);
+ ast_cdr_setapp(transferer->cdr, "BLINDTRANSFER","");
+ }
+ if (!transferee->pbx) {
+ /* Doh! Use our handy async_goto functions */
+ ast_verb(3, "Transferring %s to '%s' (context %s) priority 1\n"
+ ,transferee->name, xferto, transferer_real_context);
+ if (ast_async_goto(transferee, transferer_real_context, xferto, 1))
+ ast_log(LOG_WARNING, "Async goto failed :-(\n");
+ } else {
+ /* Set the channel's new extension, since it exists, using transferer context */
+ set_c_e_p(transferee, transferer_real_context, xferto, 0);
+ }
+ check_goto_on_transfer(transferer);
+ return res;
+ } else {
+ ast_verb(3, "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context);
+ }
+ if (ast_stream_and_wait(transferer, xferfailsound, AST_DIGIT_ANY) < 0) {
+ finishup(transferee);
+ return -1;
+ }
+ ast_stopstream(transferer);
+ res = finishup(transferee);
+ if (res) {
+ ast_verb(2, "Hungup during autoservice stop on '%s'\n", transferee->name);
+ return res;
+ }
+ return FEATURE_RETURN_SUCCESS;
+}
+
+/*!
+ * \brief make channels compatible
+ * \param c
+ * \param newchan
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+static int check_compat(struct ast_channel *c, struct ast_channel *newchan)
+{
+ if (ast_channel_make_compatible(c, newchan) < 0) {
+ ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
+ c->name, newchan->name);
+ ast_hangup(newchan);
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * \brief Attended transfer
+ * \param chan transfered user
+ * \param peer person transfering call
+ * \param config
+ * \param code
+ * \param sense feature options
+ *
+ * \param data
+ * Get extension to transfer to, if you cannot generate channel (or find extension)
+ * return to host channel. After called channel answered wait for hangup of transferer,
+ * bridge call between transfer peer (taking them off hold) to attended transfer channel.
+ *
+ * \return -1 on failure
+*/
+static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+ struct ast_channel *transferer;
+ struct ast_channel *transferee;
+ const char *transferer_real_context;
+ char xferto[256] = "";
+ int res;
+ int outstate=0;
+ struct ast_channel *newchan;
+ struct ast_channel *xferchan;
+ struct ast_bridge_thread_obj *tobj;
+ struct ast_bridge_config bconfig;
+ struct ast_frame *f;
+ int l;
+
+ ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
+ set_peers(&transferer, &transferee, peer, chan, sense);
+ transferer_real_context = real_ctx(transferer, transferee);
+ /* Start autoservice on chan while we talk to the originator */
+ ast_autoservice_start(transferee);
+ ast_indicate(transferee, AST_CONTROL_HOLD);
+
+ /* Transfer */
+ res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
+ if (res < 0) {
+ finishup(transferee);
+ return res;
+ }
+ if (res > 0) /* If they've typed a digit already, handle it */
+ xferto[0] = (char) res;
+
+ /* this is specific of atxfer */
+ res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
+ if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */
+ finishup(transferee);
+ return res;
+ }
+ if (res == 0) {
+ ast_log(LOG_WARNING, "Did not read data.\n");
+ finishup(transferee);
+ if (ast_stream_and_wait(transferer, "beeperr", ""))
+ return -1;
+ return FEATURE_RETURN_SUCCESS;
+ }
+
+ /* valid extension, res == 1 */
+ if (!ast_exists_extension(transferer, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
+ ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
+ finishup(transferee);
+ if (ast_stream_and_wait(transferer, "beeperr", ""))
+ return -1;
+ return FEATURE_RETURN_SUCCESS;
+ }
+
+ l = strlen(xferto);
+ snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* append context */
+ newchan = ast_feature_request_and_dial(transferer, transferee, "Local", ast_best_codec(transferer->nativeformats),
+ xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, 1);
+
+ if (!ast_check_hangup(transferer)) {
+ /* Transferer is up - old behaviour */
+ ast_indicate(transferer, -1);
+ if (!newchan) {
+ finishup(transferee);
+ /* any reason besides user requested cancel and busy triggers the failed sound */
+ if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
+ ast_stream_and_wait(transferer, xferfailsound, ""))
+ return -1;
+ if (ast_stream_and_wait(transferer, xfersound, ""))
+ ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+ return FEATURE_RETURN_SUCCESS;
+ }
+
+ if (check_compat(transferer, newchan)) {
+ /* we do mean transferee here, NOT transferer */
+ finishup(transferee);
+ return -1;
+ }
+ memset(&bconfig,0,sizeof(struct ast_bridge_config));
+ ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
+ ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
+ res = ast_bridge_call(transferer, newchan, &bconfig);
+ if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) {
+ ast_hangup(newchan);
+ if (ast_stream_and_wait(transferer, xfersound, ""))
+ ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+ finishup(transferee);
+ transferer->_softhangup = 0;
+ return FEATURE_RETURN_SUCCESS;
+ }
+ if (check_compat(transferee, newchan)) {
+ finishup(transferee);
+ return -1;
+ }
+ ast_indicate(transferee, AST_CONTROL_UNHOLD);
+
+ if ((ast_autoservice_stop(transferee) < 0)
+ || (ast_waitfordigit(transferee, 100) < 0)
+ || (ast_waitfordigit(newchan, 100) < 0)
+ || ast_check_hangup(transferee)
+ || ast_check_hangup(newchan)) {
+ ast_hangup(newchan);
+ return -1;
+ }
+ xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
+ if (!xferchan) {
+ ast_hangup(newchan);
+ return -1;
+ }
+ /* Make formats okay */
+ xferchan->readformat = transferee->readformat;
+ xferchan->writeformat = transferee->writeformat;
+ ast_channel_masquerade(xferchan, transferee);
+ ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
+ xferchan->_state = AST_STATE_UP;
+ ast_clear_flag(xferchan, AST_FLAGS_ALL);
+ xferchan->_softhangup = 0;
+ if ((f = ast_read(xferchan)))
+ ast_frfree(f);
+ newchan->_state = AST_STATE_UP;
+ ast_clear_flag(newchan, AST_FLAGS_ALL);
+ newchan->_softhangup = 0;
+ if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
+ ast_hangup(xferchan);
+ ast_hangup(newchan);
+ return -1;
+ }
+ tobj->chan = newchan;
+ tobj->peer = xferchan;
+ tobj->bconfig = *config;
+
+ if (ast_stream_and_wait(newchan, xfersound, ""))
+ ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+ ast_bridge_call_thread_launch(tobj);
+ return -1; /* XXX meaning the channel is bridged ? */
+ } else if (!ast_check_hangup(transferee)) {
+ /* act as blind transfer */
+ if (ast_autoservice_stop(transferee) < 0) {
+ ast_hangup(newchan);
+ return -1;
+ }
+
+ if (!newchan) {
+ unsigned int tries = 0;
+ char *transferer_tech, *transferer_name = ast_strdupa(transferer->name);
+
+ transferer_tech = strsep(&transferer_name, "/");
+ transferer_name = strsep(&transferer_name, "-");
+
+ if (ast_strlen_zero(transferer_name) || ast_strlen_zero(transferer_tech)) {
+ ast_log(LOG_WARNING, "Transferer has invalid channel name: '%s'\n", transferer->name);
+ if (ast_stream_and_wait(transferee, "beeperr", ""))
+ return -1;
+ return FEATURE_RETURN_SUCCESS;
+ }
+
+ ast_log(LOG_NOTICE, "We're trying to call %s/%s\n", transferer_tech, transferer_name);
+ newchan = ast_feature_request_and_dial(transferee, NULL, transferer_tech, ast_best_codec(transferee->nativeformats),
+ transferer_name, atxfernoanswertimeout, &outstate, transferee->cid.cid_num, transferee->cid.cid_name, 0);
+ while (!newchan && !atxferdropcall && tries < atxfercallbackretries) {
+ /* Trying to transfer again */
+ ast_autoservice_start(transferee);
+ ast_indicate(transferee, AST_CONTROL_HOLD);
+
+ newchan = ast_feature_request_and_dial(transferer, transferee, "Local", ast_best_codec(transferer->nativeformats),
+ xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, 1);
+ if (ast_autoservice_stop(transferee) < 0) {
+ if (newchan)
+ ast_hangup(newchan);
+ return -1;
+ }
+ if (!newchan) {
+ /* Transfer failed, sleeping */
+ ast_debug(1, "Sleeping for %d ms before callback.\n", atxferloopdelay);
+ ast_safe_sleep(transferee, atxferloopdelay);
+ ast_debug(1, "Trying to callback...\n");
+ newchan = ast_feature_request_and_dial(transferee, NULL, transferer_tech, ast_best_codec(transferee->nativeformats),
+ transferer_name, atxfernoanswertimeout, &outstate, transferee->cid.cid_num, transferee->cid.cid_name, 0);
+ }
+ tries++;
+ }
+ }
+ if (!newchan)
+ return -1;
+
+ /* newchan is up, we should prepare transferee and bridge them */
+ if (check_compat(transferee, newchan)) {
+ finishup(transferee);
+ return -1;
+ }
+ ast_indicate(transferee, AST_CONTROL_UNHOLD);
+
+ if ((ast_waitfordigit(transferee, 100) < 0)
+ || (ast_waitfordigit(newchan, 100) < 0)
+ || ast_check_hangup(transferee)
+ || ast_check_hangup(newchan)) {
+ ast_hangup(newchan);
+ return -1;
+ }
+
+ xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
+ if (!xferchan) {
+ ast_hangup(newchan);
+ return -1;
+ }
+ /* Make formats okay */
+ xferchan->readformat = transferee->readformat;
+ xferchan->writeformat = transferee->writeformat;
+ ast_channel_masquerade(xferchan, transferee);
+ ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
+ xferchan->_state = AST_STATE_UP;
+ ast_clear_flag(xferchan, AST_FLAGS_ALL);
+ xferchan->_softhangup = 0;
+ if ((f = ast_read(xferchan)))
+ ast_frfree(f);
+ newchan->_state = AST_STATE_UP;
+ ast_clear_flag(newchan, AST_FLAGS_ALL);
+ newchan->_softhangup = 0;
+ if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
+ ast_hangup(xferchan);
+ ast_hangup(newchan);
+ return -1;
+ }
+ tobj->chan = newchan;
+ tobj->peer = xferchan;
+ tobj->bconfig = *config;
+
+ if (ast_stream_and_wait(newchan, xfersound, ""))
+ ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
+ ast_bridge_call_thread_launch(tobj);
+ return -1; /* XXX meaning the channel is bridged ? */
+ } else {
+ /* Transferee hung up */
+ finishup(transferee);
+ return -1;
+ }
+}
+
+/* add atxfer and automon as undefined so you can only use em if you configure them */
+#define FEATURES_COUNT ARRAY_LEN(builtin_features)
+
+AST_RWLOCK_DEFINE_STATIC(features_lock);
+
+static struct ast_call_feature builtin_features[] =
+{
+ { AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
+ { AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF, "" },
+ { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" },
+ { AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF, "" },
+ { AST_FEATURE_PARKCALL, "Park Call", "parkcall", "", "", builtin_parkcall, AST_FEATURE_FLAG_NEEDSDTMF, "" },
+ { AST_FEATURE_AUTOMIXMON, "One Touch MixMonitor", "automixmon", "", "", builtin_automixmonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" },
+};
+
+
+static AST_LIST_HEAD_STATIC(feature_list,ast_call_feature);
+
+/*! \brief register new feature into feature_list*/
+void ast_register_feature(struct ast_call_feature *feature)
+{
+ if (!feature) {
+ ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
+ return;
+ }
+
+ AST_LIST_LOCK(&feature_list);
+ AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry);
+ AST_LIST_UNLOCK(&feature_list);
+
+ ast_verb(2, "Registered Feature '%s'\n",feature->sname);
+}
+
+/*!
+ * \brief Add new feature group
+ * \param fgname feature group name.
+ *
+ * Add new feature group to the feature group list insert at head of list.
+ * \note This function MUST be called while feature_groups is locked.
+*/
+static struct feature_group* register_group(const char *fgname)
+{
+ struct feature_group *fg;
+
+ if (!fgname) {
+ ast_log(LOG_NOTICE, "You didn't pass a new group name!\n");
+ return NULL;
+ }
+
+ if (!(fg = ast_calloc(1, sizeof(*fg))))
+ return NULL;
+
+ if (ast_string_field_init(fg, 128)) {
+ ast_free(fg);
+ return NULL;
+ }
+
+ ast_string_field_set(fg, gname, fgname);
+
+ AST_LIST_INSERT_HEAD(&feature_groups, fg, entry);
+
+ ast_verb(2, "Registered group '%s'\n", fg->gname);
+
+ return fg;
+}
+
+/*!
+ * \brief Add feature to group
+ * \param fg feature group
+ * \param exten
+ * \param feature feature to add.
+ *
+ * Check fg and feature specified, add feature to list
+ * \note This function MUST be called while feature_groups is locked.
+*/
+static void register_group_feature(struct feature_group *fg, const char *exten, struct ast_call_feature *feature)
+{
+ struct feature_group_exten *fge;
+
+ if (!(fge = ast_calloc(1, sizeof(*fge))))
+ return;
+
+ if (ast_string_field_init(fge, 128)) {
+ ast_free(fge);
+ return;
+ }
+
+ if (!fg) {
+ ast_log(LOG_NOTICE, "You didn't pass a group!\n");
+ return;
+ }
+
+ if (!feature) {
+ ast_log(LOG_NOTICE, "You didn't pass a feature!\n");
+ return;
+ }
+
+ ast_string_field_set(fge, exten, (ast_strlen_zero(exten) ? feature->exten : exten));
+
+ fge->feature = feature;
+
+ AST_LIST_INSERT_HEAD(&fg->features, fge, entry);
+
+ ast_verb(2, "Registered feature '%s' for group '%s' at exten '%s'\n",
+ feature->sname, fg->gname, exten);
+}
+
+void ast_unregister_feature(struct ast_call_feature *feature)
+{
+ if (!feature)
+ return;
+
+ AST_LIST_LOCK(&feature_list);
+ AST_LIST_REMOVE(&feature_list,feature,feature_entry);
+ AST_LIST_UNLOCK(&feature_list);
+ ast_free(feature);
+}
+
+/*! \brief Remove all features in the list */
+static void ast_unregister_features(void)
+{
+ struct ast_call_feature *feature;
+
+ AST_LIST_LOCK(&feature_list);
+ while ((feature = AST_LIST_REMOVE_HEAD(&feature_list,feature_entry)))
+ ast_free(feature);
+ AST_LIST_UNLOCK(&feature_list);
+}
+
+/*! \brief find a call feature by name */
+static struct ast_call_feature *find_dynamic_feature(const char *name)
+{
+ struct ast_call_feature *tmp;
+
+ AST_LIST_TRAVERSE(&feature_list, tmp, feature_entry) {
+ if (!strcasecmp(tmp->sname, name))
+ break;
+ }
+
+ return tmp;
+}
+
+/*! \brief Remove all feature groups in the list */
+static void ast_unregister_groups(void)
+{
+ struct feature_group *fg;
+ struct feature_group_exten *fge;
+
+ AST_RWLIST_WRLOCK(&feature_groups);
+ while ((fg = AST_LIST_REMOVE_HEAD(&feature_groups, entry))) {
+ while ((fge = AST_LIST_REMOVE_HEAD(&fg->features, entry))) {
+ ast_string_field_free_memory(fge);
+ ast_free(fge);
+ }
+
+ ast_string_field_free_memory(fg);
+ ast_free(fg);
+ }
+ AST_RWLIST_UNLOCK(&feature_groups);
+}
+
+/*!
+ * \brief Find a group by name
+ * \param name feature name
+ * \retval feature group on success.
+ * \retval NULL on failure.
+*/
+static struct feature_group *find_group(const char *name) {
+ struct feature_group *fg = NULL;
+
+ AST_LIST_TRAVERSE(&feature_groups, fg, entry) {
+ if (!strcasecmp(fg->gname, name))
+ break;
+ }
+
+ return fg;
+}
+
+void ast_rdlock_call_features(void)
+{
+ ast_rwlock_rdlock(&features_lock);
+}
+
+void ast_unlock_call_features(void)
+{
+ ast_rwlock_unlock(&features_lock);
+}
+
+struct ast_call_feature *ast_find_call_feature(const char *name)
+{
+ int x;
+ for (x = 0; x < FEATURES_COUNT; x++) {
+ if (!strcasecmp(name, builtin_features[x].sname))
+ return &builtin_features[x];
+ }
+ return NULL;
+}
+
+/*!
+ * \brief exec an app by feature
+ * \param chan,peer,config,code,sense,data
+ *
+ * Find a feature, determine which channel activated
+ * \retval FEATURE_RETURN_PBX_KEEPALIVE,FEATURE_RETURN_NO_HANGUP_PEER
+ * \retval -1 error.
+ * \retval -2 when an application cannot be found.
+*/
+static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
+{
+ struct ast_app *app;
+ struct ast_call_feature *feature = data;
+ struct ast_channel *work, *idle;
+ int res;
+
+ if (!feature) { /* shouldn't ever happen! */
+ ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
+ return -1;
+ }
+
+ if (sense == FEATURE_SENSE_CHAN) {
+ if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
+ return FEATURE_RETURN_KEEPTRYING;
+ if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
+ work = chan;
+ idle = peer;
+ } else {
+ work = peer;
+ idle = chan;
+ }
+ } else {
+ if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
+ return FEATURE_RETURN_KEEPTRYING;
+ if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
+ work = peer;
+ idle = chan;
+ } else {
+ work = chan;
+ idle = peer;
+ }
+ }
+
+ if (!(app = pbx_findapp(feature->app))) {
+ ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
+ return -2;
+ }
+
+ ast_autoservice_start(idle);
+
+ if (!ast_strlen_zero(feature->moh_class))
+ ast_moh_start(idle, feature->moh_class, NULL);
+
+ res = pbx_exec(work, app, feature->app_args);
+
+ if (!ast_strlen_zero(feature->moh_class))
+ ast_moh_stop(idle);
+
+ ast_autoservice_stop(idle);
+
+ if (res == AST_PBX_KEEPALIVE)
+ return FEATURE_RETURN_PBX_KEEPALIVE;
+ else if (res == AST_PBX_NO_HANGUP_PEER)
+ return FEATURE_RETURN_NO_HANGUP_PEER;
+ else if (res)
+ return FEATURE_RETURN_SUCCESSBREAK;
+
+ return FEATURE_RETURN_SUCCESS; /*! \todo XXX should probably return res */
+}
+
+static void unmap_features(void)
+{
+ int x;
+
+ ast_rwlock_wrlock(&features_lock);
+ for (x = 0; x < FEATURES_COUNT; x++)
+ strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
+ ast_rwlock_unlock(&features_lock);
+}
+
+static int remap_feature(const char *name, const char *value)
+{
+ int x, res = -1;
+
+ ast_rwlock_wrlock(&features_lock);
+ for (x = 0; x < FEATURES_COUNT; x++) {
+ if (strcasecmp(builtin_features[x].sname, name))
+ continue;
+
+ ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
+ res = 0;
+ break;
+ }
+ ast_rwlock_unlock(&features_lock);
+
+ return res;
+}
+
+/*!
+ * \brief Check the dynamic features
+ * \param chan,peer,config,code,sense
+ *
+ * Lock features list, browse for code, unlock list
+ * \retval res on success.
+ * \retval -1 on failure.
+*/
+static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
+{
+ int x;
+ struct ast_flags features;
+ int res = FEATURE_RETURN_PASSDIGITS;
+ struct ast_call_feature *feature;
+ struct feature_group *fg = NULL;
+ struct feature_group_exten *fge;
+ const char *dynamic_features;
+ char *tmp, *tok;
+
+ if (sense == FEATURE_SENSE_CHAN) {
+ ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
+ dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
+ }
+ else {
+ ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
+ dynamic_features = pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES");
+ }
+ ast_debug(3, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d, dynamic=%s\n", chan->name, peer->name, sense, features.flags, dynamic_features);
+
+ ast_rwlock_rdlock(&features_lock);
+ for (x = 0; x < FEATURES_COUNT; x++) {
+ if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
+ !ast_strlen_zero(builtin_features[x].exten)) {
+ /* Feature is up for consideration */
+ if (!strcmp(builtin_features[x].exten, code)) {
+ res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
+ break;
+ } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
+ if (res == FEATURE_RETURN_PASSDIGITS)
+ res = FEATURE_RETURN_STOREDIGITS;
+ }
+ }
+ }
+ ast_rwlock_unlock(&features_lock);
+
+ if (ast_strlen_zero(dynamic_features))
+ return res;
+
+ tmp = ast_strdupa(dynamic_features);
+
+ while ((tok = strsep(&tmp, "#"))) {
+ AST_RWLIST_RDLOCK(&feature_groups);
+
+ fg = find_group(tok);
+
+ if (fg) {
+ AST_LIST_TRAVERSE(&fg->features, fge, entry) {
+ if (strcasecmp(fge->exten, code))
+ continue;
+
+ res = fge->feature->operation(chan, peer, config, code, sense, fge->feature);
+ if (res != FEATURE_RETURN_KEEPTRYING) {
+ AST_RWLIST_UNLOCK(&feature_groups);
+ break;
+ }
+ res = FEATURE_RETURN_PASSDIGITS;
+ }
+ if (fge)
+ break;
+ }
+
+ AST_RWLIST_UNLOCK(&feature_groups);
+ AST_LIST_LOCK(&feature_list);
+
+ if(!(feature = find_dynamic_feature(tok))) {
+ AST_LIST_UNLOCK(&feature_list);
+ continue;
+ }
+
+ /* Feature is up for consideration */
+ if (!strcmp(feature->exten, code)) {
+ ast_verb(3, " Feature Found: %s exten: %s\n",feature->sname, tok);
+ res = feature->operation(chan, peer, config, code, sense, feature);
+ if (res != FEATURE_RETURN_KEEPTRYING) {
+ AST_LIST_UNLOCK(&feature_list);
+ break;
+ }
+ res = FEATURE_RETURN_PASSDIGITS;
+ } else if (!strncmp(feature->exten, code, strlen(code)))
+ res = FEATURE_RETURN_STOREDIGITS;
+
+ AST_LIST_UNLOCK(&feature_list);
+ }
+
+ return res;
+}
+
+static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
+{
+ int x;
+
+ ast_clear_flag(config, AST_FLAGS_ALL);
+
+ ast_rwlock_rdlock(&features_lock);
+ for (x = 0; x < FEATURES_COUNT; x++) {
+ if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
+ continue;
+
+ if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
+ ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
+
+ if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
+ ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
+ }
+ ast_rwlock_unlock(&features_lock);
+
+ if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
+ const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
+
+ if (dynamic_features) {
+ char *tmp = ast_strdupa(dynamic_features);
+ char *tok;
+ struct ast_call_feature *feature;
+
+ /* while we have a feature */
+ while ((tok = strsep(&tmp, "#"))) {
+ AST_LIST_LOCK(&feature_list);
+ if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
+ if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
+ ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
+ if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
+ ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
+ }
+ AST_LIST_UNLOCK(&feature_list);
+ }
+ }
+ }
+}
+
+/*!
+ * \brief Get feature and dial
+ * \param caller,transferee,type,format,data,timeout,outstate,cid_num,cid_name,igncallerstate
+ *
+ * Request channel, set channel variables, initiate call,check if they want to disconnect
+ * go into loop, check if timeout has elapsed, check if person to be transfered hung up,
+ * check for answer break loop, set cdr return channel.
+ *
+ * \todo XXX Check - this is very similar to the code in channel.c
+ * \return always a channel
+*/
+static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, struct ast_channel *transferee, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, int igncallerstate)
+{
+ int state = 0;
+ int cause = 0;
+ int to;
+ struct ast_channel *chan;
+ struct ast_channel *monitor_chans[2];
+ struct ast_channel *active_channel;
+ int res = 0, ready = 0;
+
+ if ((chan = ast_request(type, format, data, &cause))) {
+ ast_set_callerid(chan, cid_num, cid_name, cid_num);
+ ast_channel_inherit_variables(caller, chan);
+ pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller->name);
+ if (!chan->cdr) {
+ chan->cdr=ast_cdr_alloc();
+ if (chan->cdr) {
+ ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
+ ast_cdr_start(chan->cdr);
+ }
+ }
+
+ if (!ast_call(chan, data, timeout)) {
+ struct timeval started;
+ int x, len = 0;
+ char *disconnect_code = NULL, *dialed_code = NULL;
+
+ ast_indicate(caller, AST_CONTROL_RINGING);
+ /* support dialing of the featuremap disconnect code while performing an attended tranfer */
+ ast_rwlock_rdlock(&features_lock);
+ for (x = 0; x < FEATURES_COUNT; x++) {
+ if (strcasecmp(builtin_features[x].sname, "disconnect"))
+ continue;
+
+ disconnect_code = builtin_features[x].exten;
+ len = strlen(disconnect_code) + 1;
+ dialed_code = alloca(len);
+ memset(dialed_code, 0, len);
+ break;
+ }
+ ast_rwlock_unlock(&features_lock);
+ x = 0;
+ started = ast_tvnow();
+ to = timeout;
+
+ ast_poll_channel_add(caller, chan);
+
+ while (!((transferee && ast_check_hangup(transferee)) && (!igncallerstate && ast_check_hangup(caller))) && timeout && (chan->_state != AST_STATE_UP)) {
+ struct ast_frame *f = NULL;
+
+ monitor_chans[0] = caller;
+ monitor_chans[1] = chan;
+ active_channel = ast_waitfor_n(monitor_chans, 2, &to);
+
+ /* see if the timeout has been violated */
+ if(ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
+ state = AST_CONTROL_UNHOLD;
+ ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n");
+ break; /*doh! timeout*/
+ }
+
+ if (!active_channel)
+ continue;
+
+ if (chan && (chan == active_channel)){
+ f = ast_read(chan);
+ if (f == NULL) { /*doh! where'd he go?*/
+ state = AST_CONTROL_HANGUP;
+ res = 0;
+ break;
+ }
+
+ if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) {
+ if (f->subclass == AST_CONTROL_RINGING) {
+ state = f->subclass;
+ ast_verb(3, "%s is ringing\n", chan->name);
+ ast_indicate(caller, AST_CONTROL_RINGING);
+ } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
+ state = f->subclass;
+ ast_verb(3, "%s is busy\n", chan->name);
+ ast_indicate(caller, AST_CONTROL_BUSY);
+ ast_frfree(f);
+ f = NULL;
+ break;
+ } else if (f->subclass == AST_CONTROL_ANSWER) {
+ /* This is what we are hoping for */
+ state = f->subclass;
+ ast_frfree(f);
+ f = NULL;
+ ready=1;
+ break;
+ } else if (f->subclass != -1) {
+ ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass);
+ }
+ /* else who cares */
+ }
+
+ } else if (caller && (active_channel == caller)) {
+ f = ast_read(caller);
+ if (f == NULL) { /*doh! where'd he go?*/
+ if (!igncallerstate) {
+ if (ast_check_hangup(caller) && !ast_check_hangup(chan)) {
+ /* make this a blind transfer */
+ ready = 1;
+ break;
+ }
+ state = AST_CONTROL_HANGUP;
+ res = 0;
+ break;
+ }
+ } else {
+
+ if (f->frametype == AST_FRAME_DTMF) {
+ dialed_code[x++] = f->subclass;
+ dialed_code[x] = '\0';
+ if (strlen(dialed_code) == len) {
+ x = 0;
+ } else if (x && strncmp(dialed_code, disconnect_code, x)) {
+ x = 0;
+ dialed_code[x] = '\0';
+ }
+ if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
+ /* Caller Canceled the call */
+ state = AST_CONTROL_UNHOLD;
+ ast_frfree(f);
+ f = NULL;
+ break;
+ }
+ }
+ }
+ }
+ if (f)
+ ast_frfree(f);
+ } /* end while */
+
+ ast_poll_channel_del(caller, chan);
+
+ } else
+ ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
+ } else {
+ ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
+ switch(cause) {
+ case AST_CAUSE_BUSY:
+ state = AST_CONTROL_BUSY;
+ break;
+ case AST_CAUSE_CONGESTION:
+ state = AST_CONTROL_CONGESTION;
+ break;
+ }
+ }
+
+ ast_indicate(caller, -1);
+ if (chan && ready) {
+ if (chan->_state == AST_STATE_UP)
+ state = AST_CONTROL_ANSWER;
+ res = 0;
+ } else if(chan) {
+ res = -1;
+ ast_hangup(chan);
+ chan = NULL;
+ } else {
+ res = -1;
+ }
+
+ if (outstate)
+ *outstate = state;
+
+ if (chan && res <= 0) {
+ if (chan->cdr || (chan->cdr = ast_cdr_alloc())) {
+ char tmp[256];
+ ast_cdr_init(chan->cdr, chan);
+ snprintf(tmp, 256, "%s/%s", type, (char *)data);
+ ast_cdr_setapp(chan->cdr,"Dial",tmp);
+ ast_cdr_update(chan);
+ ast_cdr_start(chan->cdr);
+ ast_cdr_end(chan->cdr);
+ /* If the cause wasn't handled properly */
+ if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ } else {
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ }
+ }
+
+ return chan;
+}
+
+/*!
+ * \brief bridge the call and set CDR
+ * \param chan,peer,config
+ *
+ * Set start time, check for two channels,check if monitor on
+ * check for feature activation, create new CDR
+ * \retval res on success.
+ * \retval -1 on failure to bridge.
+*/
+int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
+{
+ /* Copy voice back and forth between the two channels. Give the peer
+ the ability to transfer calls with '#<extension' syntax. */
+ struct ast_frame *f;
+ struct ast_channel *who;
+ char chan_featurecode[FEATURE_MAX_LEN + 1]="";
+ char peer_featurecode[FEATURE_MAX_LEN + 1]="";
+ int res;
+ int diff;
+ int hasfeatures=0;
+ int hadfeatures=0;
+ struct ast_option_header *aoh;
+ struct ast_bridge_config backup_config;
+ struct ast_cdr *bridge_cdr;
+
+ memset(&backup_config, 0, sizeof(backup_config));
+
+ config->start_time = ast_tvnow();
+
+ if (chan && peer) {
+ pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
+ pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
+ } else if (chan)
+ pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
+
+ if (monitor_ok) {
+ const char *monitor_exec;
+ struct ast_channel *src = NULL;
+ if (!monitor_app) {
+ if (!(monitor_app = pbx_findapp("Monitor")))
+ monitor_ok=0;
+ }
+ if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR")))
+ src = chan;
+ else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
+ src = peer;
+ if (monitor_app && src) {
+ char *tmp = ast_strdupa(monitor_exec);
+ pbx_exec(src, monitor_app, tmp);
+ }
+ }
+
+ set_config_flags(chan, peer, config);
+ config->firstpass = 1;
+
+ /* Answer if need be */
+ if (ast_answer(chan))
+ return -1;
+ peer->appl = "Bridged Call";
+ peer->data = chan->name;
+
+ /* copy the userfield from the B-leg to A-leg if applicable */
+ if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
+ char tmp[256];
+ if (!ast_strlen_zero(chan->cdr->userfield)) {
+ snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield);
+ ast_cdr_appenduserfield(chan, tmp);
+ } else
+ ast_cdr_setuserfield(chan, peer->cdr->userfield);
+ /* free the peer's cdr without ast_cdr_free complaining */
+ ast_free(peer->cdr);
+ peer->cdr = NULL;
+ }
+
+ for (;;) {
+ struct ast_channel *other; /* used later */
+
+ res = ast_channel_bridge(chan, peer, config, &f, &who);
+
+ if (config->feature_timer) {
+ /* Update time limit for next pass */
+ diff = ast_tvdiff_ms(ast_tvnow(), config->start_time);
+ config->feature_timer -= diff;
+ if (hasfeatures) {
+ /* Running on backup config, meaning a feature might be being
+ activated, but that's no excuse to keep things going
+ indefinitely! */
+ if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) {
+ ast_debug(1, "Timed out, realtime this time!\n");
+ config->feature_timer = 0;
+ who = chan;
+ if (f)
+ ast_frfree(f);
+ f = NULL;
+ res = 0;
+ } else if (config->feature_timer <= 0) {
+ /* Not *really* out of time, just out of time for
+ digits to come in for features. */
+ ast_debug(1, "Timed out for feature!\n");
+ if (!ast_strlen_zero(peer_featurecode)) {
+ ast_dtmf_stream(chan, peer, peer_featurecode, 0, 0);
+ memset(peer_featurecode, 0, sizeof(peer_featurecode));
+ }
+ if (!ast_strlen_zero(chan_featurecode)) {
+ ast_dtmf_stream(peer, chan, chan_featurecode, 0, 0);
+ memset(chan_featurecode, 0, sizeof(chan_featurecode));
+ }
+ if (f)
+ ast_frfree(f);
+ hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
+ if (!hasfeatures) {
+ /* Restore original (possibly time modified) bridge config */
+ memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
+ memset(&backup_config, 0, sizeof(backup_config));
+ }
+ hadfeatures = hasfeatures;
+ /* Continue as we were */
+ continue;
+ } else if (!f) {
+ /* The bridge returned without a frame and there is a feature in progress.
+ * However, we don't think the feature has quite yet timed out, so just
+ * go back into the bridge. */
+ continue;
+ }
+ } else {
+ if (config->feature_timer <=0) {
+ /* We ran out of time */
+ config->feature_timer = 0;
+ who = chan;
+ if (f)
+ ast_frfree(f);
+ f = NULL;
+ res = 0;
+ }
+ }
+ }
+ if (res < 0) {
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer))
+ ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
+ return -1;
+ }
+
+ if (!f || (f->frametype == AST_FRAME_CONTROL &&
+ (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY ||
+ f->subclass == AST_CONTROL_CONGESTION))) {
+ res = -1;
+ break;
+ }
+ /* many things should be sent to the 'other' channel */
+ other = (who == chan) ? peer : chan;
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch (f->subclass) {
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_FLASH:
+ case -1:
+ ast_indicate(other, f->subclass);
+ break;
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ ast_indicate_data(other, f->subclass, f->data, f->datalen);
+ break;
+ case AST_CONTROL_OPTION:
+ aoh = f->data;
+ /* Forward option Requests */
+ if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
+ ast_channel_setoption(other, ntohs(aoh->option), aoh->data,
+ f->datalen - sizeof(struct ast_option_header), 0);
+ }
+ break;
+ }
+ } else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
+ /* eat it */
+ } else if (f->frametype == AST_FRAME_DTMF) {
+ char *featurecode;
+ int sense;
+
+ hadfeatures = hasfeatures;
+ /* This cannot overrun because the longest feature is one shorter than our buffer */
+ if (who == chan) {
+ sense = FEATURE_SENSE_CHAN;
+ featurecode = chan_featurecode;
+ } else {
+ sense = FEATURE_SENSE_PEER;
+ featurecode = peer_featurecode;
+ }
+ /*! append the event to featurecode. we rely on the string being zero-filled, and
+ * not overflowing it.
+ * \todo XXX how do we guarantee the latter ?
+ */
+ featurecode[strlen(featurecode)] = f->subclass;
+ /* Get rid of the frame before we start doing "stuff" with the channels */
+ ast_frfree(f);
+ f = NULL;
+ config->feature_timer = backup_config.feature_timer;
+ res = ast_feature_interpret(chan, peer, config, featurecode, sense);
+ switch(res) {
+ case FEATURE_RETURN_PASSDIGITS:
+ ast_dtmf_stream(other, who, featurecode, 0, 0);
+ /* Fall through */
+ case FEATURE_RETURN_SUCCESS:
+ memset(featurecode, 0, sizeof(chan_featurecode));
+ break;
+ }
+ if (res >= FEATURE_RETURN_PASSDIGITS) {
+ res = 0;
+ } else
+ break;
+ hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
+ if (hadfeatures && !hasfeatures) {
+ /* Restore backup */
+ memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
+ memset(&backup_config, 0, sizeof(struct ast_bridge_config));
+ } else if (hasfeatures) {
+ if (!hadfeatures) {
+ /* Backup configuration */
+ memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
+ /* Setup temporary config options */
+ config->play_warning = 0;
+ ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
+ ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
+ config->warning_freq = 0;
+ config->warning_sound = NULL;
+ config->end_sound = NULL;
+ config->start_sound = NULL;
+ config->firstpass = 0;
+ }
+ config->start_time = ast_tvnow();
+ config->feature_timer = featuredigittimeout;
+ ast_debug(1, "Set time limit to %ld\n", config->feature_timer);
+ }
+ }
+ if (f)
+ ast_frfree(f);
+
+ }
+ /* arrange the cdrs */
+ bridge_cdr = ast_cdr_alloc();
+ if (bridge_cdr) {
+ if (chan->cdr && peer->cdr) { /* both of them? merge */
+ ast_cdr_init(bridge_cdr,chan); /* seems more logicaller to use the destination as a base, but, really, it's random */
+ ast_cdr_start(bridge_cdr); /* now is the time to start */
+
+ /* absorb the channel cdr */
+ ast_cdr_merge(bridge_cdr, chan->cdr);
+ if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED))
+ ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
+
+ /* absorb the peer cdr */
+ ast_cdr_merge(bridge_cdr, peer->cdr);
+ if (!ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED))
+ ast_cdr_discard(peer->cdr); /* if locked cdrs are in peer, they are taken over in the merge */
+
+ peer->cdr = NULL;
+ chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
+ } else if (chan->cdr) {
+ /* take the cdr from the channel - literally */
+ ast_cdr_init(bridge_cdr,chan);
+ /* absorb this data */
+ ast_cdr_merge(bridge_cdr, chan->cdr);
+ if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED))
+ ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
+ chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
+ } else if (peer->cdr) {
+ /* take the cdr from the peer - literally */
+ ast_cdr_init(bridge_cdr,peer);
+ /* absorb this data */
+ ast_cdr_merge(bridge_cdr, peer->cdr);
+ if (!ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED))
+ ast_cdr_discard(peer->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
+ peer->cdr = NULL;
+ peer->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
+ } else {
+ /* make up a new cdr */
+ ast_cdr_init(bridge_cdr,chan); /* eh, just pick one of them */
+ chan->cdr = bridge_cdr; /* */
+ }
+ if (ast_strlen_zero(bridge_cdr->dstchannel)) {
+ if (strcmp(bridge_cdr->channel, peer->name) != 0)
+ ast_cdr_setdestchan(bridge_cdr, peer->name);
+ else
+ ast_cdr_setdestchan(bridge_cdr, chan->name);
+ }
+ }
+ return res;
+}
+
+/*! \brief Output parking event to manager */
+static void post_manager_event(const char *s, struct parkeduser *pu)
+{
+ manager_event(EVENT_FLAG_CALL, s,
+ "Exten: %s\r\n"
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n\r\n",
+ pu->parkingexten,
+ pu->chan->name,
+ S_OR(pu->chan->cid.cid_num, "<unknown>"),
+ S_OR(pu->chan->cid.cid_name, "<unknown>")
+ );
+}
+
+/*!
+ * \brief Take care of parked calls and unpark them if needed
+ * \param ignore unused var.
+ *
+ * Start inf loop, lock parking lot, check if any parked channels have gone above timeout
+ * if so, remove channel from parking lot and return it to the extension that parked it.
+ * Check if parked channel decided to hangup, wait until next FD via select().
+*/
+static void *do_parking_thread(void *ignore)
+{
+ char parkingslot[AST_MAX_EXTENSION];
+ fd_set rfds, efds; /* results from previous select, to be preserved across loops. */
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&efds);
+
+ for (;;) {
+ struct parkeduser *pu;
+ int ms = -1; /* select timeout, uninitialized */
+ int max = -1; /* max fd, none there yet */
+ fd_set nrfds, nefds; /* args for the next select */
+ FD_ZERO(&nrfds);
+ FD_ZERO(&nefds);
+
+ AST_LIST_LOCK(&parkinglot);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot, pu, list) {
+ struct ast_channel *chan = pu->chan; /* shorthand */
+ int tms; /* timeout for this item */
+ int x; /* fd index in channel */
+ struct ast_context *con;
+
+ if (pu->notquiteyet) /* Pretend this one isn't here yet */
+ continue;
+ tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
+ if (tms > pu->parkingtime) {
+ ast_indicate(chan, AST_CONTROL_UNHOLD);
+ /* Get chan, exten from derived kludge */
+ if (pu->peername[0]) {
+ char *peername = ast_strdupa(pu->peername);
+ char *cp = strrchr(peername, '-');
+ char peername_flat[AST_MAX_EXTENSION]; /* using something like Zap/52 for an extension name is NOT a good idea */
+ int i;
+
+ if (cp)
+ *cp = 0;
+ ast_copy_string(peername_flat,peername,sizeof(peername_flat));
+ for(i=0; peername_flat[i] && i < AST_MAX_EXTENSION; i++) {
+ if (peername_flat[i] == '/')
+ peername_flat[i]= '0';
+ }
+ con = ast_context_find(parking_con_dial);
+ if (!con) {
+ con = ast_context_create(NULL, parking_con_dial, registrar);
+ if (!con)
+ ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
+ }
+ if (con) {
+ char returnexten[AST_MAX_EXTENSION];
+ snprintf(returnexten, sizeof(returnexten), "%s,,t", peername);
+ ast_add_extension2(con, 1, peername_flat, 1, NULL, NULL, "Dial", ast_strdup(returnexten), ast_free_ptr, registrar);
+ }
+ if (comebacktoorigin) {
+ set_c_e_p(chan, parking_con_dial, peername_flat, 1);
+ } else {
+ ast_log(LOG_WARNING, "now going to parkedcallstimeout,s,1 | ps is %d\n",pu->parkingnum);
+ snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
+ pbx_builtin_setvar_helper(pu->chan, "PARKINGSLOT", parkingslot);
+ set_c_e_p(chan, "parkedcallstimeout", peername_flat, 1);
+ }
+ } else {
+ /* They've been waiting too long, send them back to where they came. Theoretically they
+ should have their original extensions and such, but we copy to be on the safe side */
+ set_c_e_p(chan, pu->context, pu->exten, pu->priority);
+ }
+
+ post_manager_event("ParkedCallTimeOut", pu);
+
+ ast_verb(2, "Timeout for %s parked on %d. Returning to %s,%s,%d\n", chan->name, pu->parkingnum, chan->context, chan->exten, chan->priority);
+ /* Start up the PBX, or hang them up */
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name);
+ ast_hangup(chan);
+ }
+ /* And take them out of the parking lot */
+ AST_LIST_REMOVE_CURRENT(list);
+ con = ast_context_find(parking_con);
+ if (con) {
+ if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
+ ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
+ else
+ notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_NOT_INUSE);
+ } else
+ ast_log(LOG_WARNING, "Whoa, no parking context?\n");
+ ast_free(pu);
+ } else { /* still within parking time, process descriptors */
+ for (x = 0; x < AST_MAX_FDS; x++) {
+ struct ast_frame *f;
+
+ if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds)))
+ continue; /* nothing on this descriptor */
+
+ if (FD_ISSET(chan->fds[x], &efds))
+ ast_set_flag(chan, AST_FLAG_EXCEPTION);
+ else
+ ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+ chan->fdno = x;
+
+ /* See if they need servicing */
+ f = ast_read(chan);
+ if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) {
+ if (f)
+ ast_frfree(f);
+ post_manager_event("ParkedCallGiveUp", pu);
+
+ /* There's a problem, hang them up*/
+ ast_verb(2, "%s got tired of being parked\n", chan->name);
+ ast_hangup(chan);
+ /* And take them out of the parking lot */
+ AST_LIST_REMOVE_CURRENT(list);
+ con = ast_context_find(parking_con);
+ if (con) {
+ if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
+ ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
+ else
+ notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_NOT_INUSE);
+ } else
+ ast_log(LOG_WARNING, "Whoa, no parking context?\n");
+ ast_free(pu);
+ break;
+ } else {
+ /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
+ ast_frfree(f);
+ if (pu->moh_trys < 3 && !chan->generatordata) {
+ ast_debug(1, "MOH on parked call stopped by outside source. Restarting.\n");
+ ast_indicate_data(chan, AST_CONTROL_HOLD,
+ S_OR(parkmohclass, NULL),
+ !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
+ pu->moh_trys++;
+ }
+ goto std; /*! \todo XXX Ick: jumping into an else statement??? XXX */
+ }
+
+ } /* end for */
+ if (x >= AST_MAX_FDS) {
+std: for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */
+ if (chan->fds[x] > -1) {
+ FD_SET(chan->fds[x], &nrfds);
+ FD_SET(chan->fds[x], &nefds);
+ if (chan->fds[x] > max)
+ max = chan->fds[x];
+ }
+ }
+ /* Keep track of our shortest wait */
+ if (tms < ms || ms < 0)
+ ms = tms;
+ }
+ }
+ } /* end while */
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&parkinglot);
+ rfds = nrfds;
+ efds = nefds;
+ {
+ struct timeval tv = ast_samp2tv(ms, 1000);
+ /* Wait for something to happen */
+ ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
+ }
+ pthread_testcancel();
+ }
+ return NULL; /* Never reached */
+}
+
+/*! \brief Park a call */
+static int park_call_exec(struct ast_channel *chan, void *data)
+{
+ /* Cache the original channel name in case we get masqueraded in the middle
+ * of a park--it is still theoretically possible for a transfer to happen before
+ * we get here, but it is _really_ unlikely */
+ char *orig_chan_name = ast_strdupa(chan->name);
+ char orig_exten[AST_MAX_EXTENSION];
+ int orig_priority = chan->priority;
+
+ /* Data is unused at the moment but could contain a parking
+ lot context eventually */
+ int res = 0;
+
+ ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten));
+
+ /* Setup the exten/priority to be s/1 since we don't know
+ where this call should return */
+ strcpy(chan->exten, "s");
+ chan->priority = 1;
+ /* Answer if call is not up */
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+ /* Sleep to allow VoIP streams to settle down */
+ if (!res)
+ res = ast_safe_sleep(chan, 1000);
+ /* Park the call */
+ if (!res) {
+ res = park_call_full(chan, chan, 0, NULL, orig_chan_name);
+ /* Continue on in the dialplan */
+ if (res == 1) {
+ ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
+ chan->priority = orig_priority;
+ res = 0;
+ } else if (!res)
+ res = AST_PBX_KEEPALIVE;
+ }
+
+ return res;
+}
+
+/*! \brief Pickup parked call */
+static int park_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_channel *peer=NULL;
+ struct parkeduser *pu;
+ struct ast_context *con;
+ int park = 0;
+ struct ast_bridge_config config;
+
+ if (data)
+ park = atoi((char *)data);
+
+ AST_LIST_LOCK(&parkinglot);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot, pu, list) {
+ if (!data || pu->parkingnum == park) {
+ AST_LIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&parkinglot);
+
+ if (pu) {
+ peer = pu->chan;
+ con = ast_context_find(parking_con);
+ if (con) {
+ if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
+ ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
+ else
+ notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_NOT_INUSE);
+ } else
+ ast_log(LOG_WARNING, "Whoa, no parking context?\n");
+
+ manager_event(EVENT_FLAG_CALL, "UnParkedCall",
+ "Exten: %s\r\n"
+ "Channel: %s\r\n"
+ "From: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n",
+ pu->parkingexten, pu->chan->name, chan->name,
+ S_OR(pu->chan->cid.cid_num, "<unknown>"),
+ S_OR(pu->chan->cid.cid_name, "<unknown>")
+ );
+
+ ast_free(pu);
+ }
+ /* JK02: it helps to answer the channel if not already up */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ if (peer) {
+ /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
+
+ if (!ast_strlen_zero(courtesytone)) {
+ int error = 0;
+ ast_indicate(peer, AST_CONTROL_UNHOLD);
+ if (parkedplay == 0) {
+ error = ast_stream_and_wait(chan, courtesytone, "");
+ } else if (parkedplay == 1) {
+ error = ast_stream_and_wait(peer, courtesytone, "");
+ } else if (parkedplay == 2) {
+ if (!ast_streamfile(chan, courtesytone, chan->language) &&
+ !ast_streamfile(peer, courtesytone, chan->language)) {
+ /*! \todo XXX we would like to wait on both! */
+ res = ast_waitstream(chan, "");
+ if (res >= 0)
+ res = ast_waitstream(peer, "");
+ if (res < 0)
+ error = 1;
+ }
+ }
+ if (error) {
+ ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+ ast_hangup(peer);
+ return -1;
+ }
+ } else
+ ast_indicate(peer, AST_CONTROL_UNHOLD);
+
+ res = ast_channel_make_compatible(chan, peer);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
+ ast_hangup(peer);
+ return -1;
+ }
+ /* This runs sorta backwards, since we give the incoming channel control, as if it
+ were the person called. */
+ ast_verb(3, "Channel %s connected to parked call %d\n", chan->name, park);
+
+ pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
+ ast_cdr_setdestchan(chan->cdr, peer->name);
+ memset(&config, 0, sizeof(struct ast_bridge_config));
+ if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
+ if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
+ if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
+ if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
+ res = ast_bridge_call(chan, peer, &config);
+
+ pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
+ ast_cdr_setdestchan(chan->cdr, peer->name);
+
+ /* Simulate the PBX hanging up */
+ if (res != AST_PBX_NO_HANGUP_PEER)
+ ast_hangup(peer);
+ return res;
+ } else {
+ /*! \todo XXX Play a message XXX */
+ if (ast_stream_and_wait(chan, "pbx-invalidpark", ""))
+ ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
+ ast_verb(3, "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
+ res = -1;
+ }
+
+ return res;
+}
+
+/*!
+ * \brief CLI command to list configured features
+ * \param e
+ * \param cmd
+ * \param a
+ *
+ * \retval CLI_SUCCESS on success.
+ * \retval NULL when tab completion is used.
+ */
+static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) {
+ int i;
+ struct ast_call_feature *feature;
+ char format[] = "%-25s %-7s %-7s\n";
+
+ switch (cmd) {
+
+ case CLI_INIT:
+ e->command = "features show";
+ e->usage =
+ "Usage: features show\n"
+ " Lists configured features\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, format, "Builtin Feature", "Default", "Current");
+ ast_cli(a->fd, format, "---------------", "-------", "-------");
+
+ ast_cli(a->fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
+
+ ast_rwlock_rdlock(&features_lock);
+ for (i = 0; i < FEATURES_COUNT; i++)
+ ast_cli(a->fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
+ ast_rwlock_unlock(&features_lock);
+
+ ast_cli(a->fd, "\n");
+ ast_cli(a->fd, format, "Dynamic Feature", "Default", "Current");
+ ast_cli(a->fd, format, "---------------", "-------", "-------");
+ if (AST_LIST_EMPTY(&feature_list))
+ ast_cli(a->fd, "(none)\n");
+ else {
+ AST_LIST_LOCK(&feature_list);
+ AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
+ ast_cli(a->fd, format, feature->sname, "no def", feature->exten);
+ AST_LIST_UNLOCK(&feature_list);
+ }
+ ast_cli(a->fd, "\nCall parking\n");
+ ast_cli(a->fd, "------------\n");
+ ast_cli(a->fd,"%-20s: %s\n", "Parking extension", parking_ext);
+ ast_cli(a->fd,"%-20s: %s\n", "Parking context", parking_con);
+ ast_cli(a->fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop);
+ ast_cli(a->fd,"\n");
+
+ return CLI_SUCCESS;
+}
+
+static char mandescr_bridge[] =
+"Description: Bridge together two channels already in the PBX\n"
+"Variables: ( Headers marked with * are required )\n"
+" *Channel1: Channel to Bridge to Channel2\n"
+" *Channel2: Channel to Bridge to Channel1\n"
+" Tone: (Yes|No) Play courtesy tone to Channel 2\n"
+"\n";
+
+/*!
+ * \brief Actual bridge
+ * \param chan
+ * \param tmpchan
+ *
+ * Stop hold music, lock both channels, masq channels,
+ * after bridge return channel to next priority.
+*/
+static void do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan)
+{
+ ast_moh_stop(chan);
+ ast_channel_lock(chan);
+ ast_setstate(tmpchan, chan->_state);
+ tmpchan->readformat = chan->readformat;
+ tmpchan->writeformat = chan->writeformat;
+ ast_channel_masquerade(tmpchan, chan);
+ ast_channel_lock(tmpchan);
+ ast_do_masquerade(tmpchan);
+ /* when returning from bridge, the channel will continue at the next priority */
+ ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1);
+ ast_channel_unlock(tmpchan);
+ ast_channel_unlock(chan);
+}
+
+/*!
+ * \brief Bridge channels together
+ * \param s
+ * \param m
+ *
+ * Make sure valid channels were specified,
+ * send errors if any of the channels could not be found/locked, answer channels if needed,
+ * create the placeholder channels and grab the other channels
+ * make the channels compatible, send error if we fail doing so
+ * setup the bridge thread object and start the bridge.
+ *
+ * \retval 0 on success or on incorrect use.
+ * \retval 1 on failure to bridge channels.
+*/
+static int action_bridge(struct mansession *s, const struct message *m)
+{
+ const char *channela = astman_get_header(m, "Channel1");
+ const char *channelb = astman_get_header(m, "Channel2");
+ const char *playtone = astman_get_header(m, "Tone");
+ struct ast_channel *chana = NULL, *chanb = NULL;
+ struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
+ struct ast_bridge_thread_obj *tobj = NULL;
+
+ /* make sure valid channels were specified */
+ if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) {
+ chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela));
+ chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb));
+ if (chana)
+ ast_channel_unlock(chana);
+ if (chanb)
+ ast_channel_unlock(chanb);
+
+ /* send errors if any of the channels could not be found/locked */
+ if (!chana) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
+ astman_send_error(s, m, buf);
+ return 0;
+ }
+ if (!chanb) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
+ astman_send_error(s, m, buf);
+ return 0;
+ }
+ } else {
+ astman_send_error(s, m, "Missing channel parameter in request");
+ return 0;
+ }
+
+ /* Answer the channels if needed */
+ if (chana->_state != AST_STATE_UP)
+ ast_answer(chana);
+ if (chanb->_state != AST_STATE_UP)
+ ast_answer(chanb);
+
+ /* create the placeholder channels and grab the other channels */
+ if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
+ NULL, NULL, 0, "Bridge/%s", chana->name))) {
+ astman_send_error(s, m, "Unable to create temporary channel!");
+ return 1;
+ }
+
+ if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
+ NULL, NULL, 0, "Bridge/%s", chanb->name))) {
+ astman_send_error(s, m, "Unable to create temporary channels!");
+ ast_channel_free(tmpchana);
+ return 1;
+ }
+
+ do_bridge_masquerade(chana, tmpchana);
+ do_bridge_masquerade(chanb, tmpchanb);
+
+ /* make the channels compatible, send error if we fail doing so */
+ if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
+ ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name);
+ astman_send_error(s, m, "Could not make channels compatible for manager bridge");
+ ast_hangup(tmpchana);
+ ast_hangup(tmpchanb);
+ return 1;
+ }
+
+ /* setup the bridge thread object and start the bridge */
+ if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
+ ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno));
+ astman_send_error(s, m, "Unable to spawn a new bridge thread");
+ ast_hangup(tmpchana);
+ ast_hangup(tmpchanb);
+ return 1;
+ }
+
+ tobj->chan = tmpchana;
+ tobj->peer = tmpchanb;
+ tobj->return_to_pbx = 1;
+
+ if (ast_true(playtone)) {
+ if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) {
+ if (ast_waitstream(tmpchanb, "") < 0)
+ ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name);
+ }
+ }
+
+ ast_bridge_call_thread_launch(tobj);
+
+ astman_send_ack(s, m, "Launched bridge thread with success");
+
+ return 0;
+}
+
+/*!
+ * \brief CLI command to list parked calls
+ * \param e
+ * \param cmd
+ * \param a
+ *
+ * Check right usage, lock parking lot, display parked calls, unlock parking lot list.
+ * \retval CLI_SUCCESS on success.
+ * \retval CLI_SHOWUSAGE on incorrect number of arguments.
+ * \retval NULL when tab completion is used.
+*/
+static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct parkeduser *cur;
+ int numparked = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "parkedcalls show";
+ e->usage =
+ "Usage: parkedcalls show\n"
+ " List currently parked calls\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > e->args)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
+ , "Context", "Extension", "Pri", "Timeout");
+
+ AST_LIST_LOCK(&parkinglot);
+ AST_LIST_TRAVERSE(&parkinglot, cur, list) {
+ ast_cli(a->fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
+ ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
+ ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
+
+ numparked++;
+ }
+ AST_LIST_UNLOCK(&parkinglot);
+ ast_cli(a->fd, "%d parked call%s.\n", numparked, ESS(numparked));
+
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_parkedcalls_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = handle_parkedcalls(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "show parkedcalls";
+ return res;
+}
+
+static struct ast_cli_entry cli_show_parkedcalls_deprecated = AST_CLI_DEFINE(handle_parkedcalls_deprecated, "List currently parked calls.");
+
+static struct ast_cli_entry cli_features[] = {
+ AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
+ AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls", .deprecate_cmd = &cli_show_parkedcalls_deprecated),
+};
+
+/*!
+ * \brief Dump parking lot status
+ * \param s
+ * \param m
+ *
+ * Lock parking lot, iterate list and append parked calls status, unlock parking lot.
+ * \return Always RESULT_SUCCESS
+*/
+static int manager_parking_status(struct mansession *s, const struct message *m)
+{
+ struct parkeduser *cur;
+ const char *id = astman_get_header(m, "ActionID");
+ char idText[256] = "";
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ astman_send_ack(s, m, "Parked calls will follow");
+
+ AST_LIST_LOCK(&parkinglot);
+
+ AST_LIST_TRAVERSE(&parkinglot, cur, list) {
+ astman_append(s, "Event: ParkedCall\r\n"
+ "Exten: %d\r\n"
+ "Channel: %s\r\n"
+ "From: %s\r\n"
+ "Timeout: %ld\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "%s"
+ "\r\n",
+ cur->parkingnum, cur->chan->name, cur->peername,
+ (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
+ S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */
+ S_OR(cur->chan->cid.cid_name, ""),
+ idText);
+ }
+
+ astman_append(s,
+ "Event: ParkedCallsComplete\r\n"
+ "%s"
+ "\r\n",idText);
+
+ AST_LIST_UNLOCK(&parkinglot);
+
+ return RESULT_SUCCESS;
+}
+
+static char mandescr_park[] =
+"Description: Park a channel.\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel name to park\n"
+" *Channel2: Channel to announce park info to (and return to if timeout)\n"
+" Timeout: Number of milliseconds to wait before callback.\n";
+
+/*!
+ * \brief Create manager event for parked calls
+ * \param s
+ * \param m
+ *
+ * Get channels involved in park, create event.
+ * \return Always 0
+*/
+static int manager_park(struct mansession *s, const struct message *m)
+{
+ const char *channel = astman_get_header(m, "Channel");
+ const char *channel2 = astman_get_header(m, "Channel2");
+ const char *timeout = astman_get_header(m, "Timeout");
+ char buf[BUFSIZ];
+ int to = 0;
+ int res = 0;
+ int parkExt = 0;
+ struct ast_channel *ch1, *ch2;
+
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "Channel not specified");
+ return 0;
+ }
+
+ if (ast_strlen_zero(channel2)) {
+ astman_send_error(s, m, "Channel2 not specified");
+ return 0;
+ }
+
+ ch1 = ast_get_channel_by_name_locked(channel);
+ if (!ch1) {
+ snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
+ astman_send_error(s, m, buf);
+ return 0;
+ }
+
+ ch2 = ast_get_channel_by_name_locked(channel2);
+ if (!ch2) {
+ snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
+ astman_send_error(s, m, buf);
+ ast_channel_unlock(ch1);
+ return 0;
+ }
+
+ if (!ast_strlen_zero(timeout)) {
+ sscanf(timeout, "%d", &to);
+ }
+
+ res = ast_masq_park_call(ch1, ch2, to, &parkExt);
+ if (!res) {
+ ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
+ astman_send_ack(s, m, "Park successful");
+ } else {
+ astman_send_error(s, m, "Park failure");
+ }
+
+ ast_channel_unlock(ch1);
+ ast_channel_unlock(ch2);
+
+ return 0;
+}
+
+/*!
+ * \brief Pickup a call
+ * \param chan channel that initiated pickup.
+ *
+ * Walk list of channels, checking it is not itself, channel is pbx one,
+ * check that the callgroup for both channels are the same and the channel is ringing.
+ * Answer calling channel, flag channel as answered on queue, masq channels together.
+*/
+int ast_pickup_call(struct ast_channel *chan)
+{
+ struct ast_channel *cur = NULL;
+ int res = -1;
+
+ while ((cur = ast_channel_walk_locked(cur)) != NULL) {
+ if (!cur->pbx &&
+ (cur != chan) &&
+ (chan->pickupgroup & cur->callgroup) &&
+ ((cur->_state == AST_STATE_RINGING) ||
+ (cur->_state == AST_STATE_RING))) {
+ break;
+ }
+ ast_channel_unlock(cur);
+ }
+ if (cur) {
+ ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
+ res = ast_answer(chan);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
+ res = ast_queue_control(chan, AST_CONTROL_ANSWER);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
+ res = ast_channel_masquerade(cur, chan);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */
+ ast_channel_unlock(cur);
+ } else {
+ ast_debug(1, "No call pickup possible...\n");
+ }
+ return res;
+}
+
+/*!
+ * \brief Add parking hints for all defined parking lots
+ * \param context
+ * \param start starting parkinglot number
+ * \param stop ending parkinglot number
+*/
+static void park_add_hints(char *context, int start, int stop)
+{
+ int numext;
+ char device[AST_MAX_EXTENSION];
+ char exten[10];
+
+ for (numext = start; numext <= stop; numext++) {
+ snprintf(exten, sizeof(exten), "%d", numext);
+ snprintf(device, sizeof(device), "park:%s@%s", exten, context);
+ ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
+ }
+}
+
+
+static int load_config(void)
+{
+ int start = 0, end = 0;
+ int res;
+ int i;
+ struct ast_context *con = NULL;
+ struct ast_config *cfg = NULL;
+ struct ast_variable *var = NULL;
+ struct feature_group *fg = NULL;
+ struct ast_flags config_flags = { 0 };
+ char old_parking_ext[AST_MAX_EXTENSION];
+ char old_parking_con[AST_MAX_EXTENSION] = "";
+ char *ctg;
+ static const char *categories[] = {
+ /* Categories in features.conf that are not
+ * to be parsed as group categories
+ */
+ "general",
+ "featuremap",
+ "applicationmap"
+ };
+
+ if (!ast_strlen_zero(parking_con)) {
+ strcpy(old_parking_ext, parking_ext);
+ strcpy(old_parking_con, parking_con);
+ }
+
+ /* Reset to defaults */
+ strcpy(parking_con, "parkedcalls");
+ strcpy(parking_con_dial, "park-dial");
+ strcpy(parking_ext, "700");
+ strcpy(pickup_ext, "*8");
+ strcpy(parkmohclass, "default");
+ courtesytone[0] = '\0';
+ strcpy(xfersound, "beep");
+ strcpy(xferfailsound, "pbx-invalid");
+ parking_start = 701;
+ parking_stop = 750;
+ parkfindnext = 0;
+ adsipark = 0;
+ comebacktoorigin = 1;
+ parkaddhints = 0;
+ parkedcalltransfers = 0;
+ parkedcallreparking = 0;
+
+ transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
+ featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
+ atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
+ atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
+ atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
+ atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
+
+ cfg = ast_config_load("features.conf", config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING,"Could not load features.conf\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+ if (!strcasecmp(var->name, "parkext")) {
+ ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
+ } else if (!strcasecmp(var->name, "context")) {
+ ast_copy_string(parking_con, var->value, sizeof(parking_con));
+ } else if (!strcasecmp(var->name, "parkingtime")) {
+ if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
+ ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
+ parkingtime = DEFAULT_PARK_TIME;
+ } else
+ parkingtime = parkingtime * 1000;
+ } else if (!strcasecmp(var->name, "parkpos")) {
+ if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
+ ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
+ } else {
+ parking_start = start;
+ parking_stop = end;
+ }
+ } else if (!strcasecmp(var->name, "findslot")) {
+ parkfindnext = (!strcasecmp(var->value, "next"));
+ } else if (!strcasecmp(var->name, "parkinghints")) {
+ parkaddhints = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
+ if (!strcasecmp(var->value, "both"))
+ parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
+ else if (!strcasecmp(var->value, "caller"))
+ parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
+ else if (!strcasecmp(var->value, "callee"))
+ parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
+ } else if (!strcasecmp(var->name, "parkedcallreparking")) {
+ if (!strcasecmp(var->value, "both"))
+ parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
+ else if (!strcasecmp(var->value, "caller"))
+ parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
+ else if (!strcasecmp(var->value, "callee"))
+ parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
+ } else if (!strcasecmp(var->name, "adsipark")) {
+ adsipark = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "transferdigittimeout")) {
+ if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
+ ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
+ transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
+ } else
+ transferdigittimeout = transferdigittimeout * 1000;
+ } else if (!strcasecmp(var->name, "featuredigittimeout")) {
+ if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
+ ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
+ featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
+ }
+ } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
+ if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
+ ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
+ atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
+ } else
+ atxfernoanswertimeout = atxfernoanswertimeout * 1000;
+ } else if (!strcasecmp(var->name, "atxferloopdelay")) {
+ if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
+ ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
+ atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
+ } else
+ atxferloopdelay *= 1000;
+ } else if (!strcasecmp(var->name, "atxferdropcall")) {
+ atxferdropcall = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "atxfercallbackretries")) {
+ if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
+ ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
+ atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
+ }
+ } else if (!strcasecmp(var->name, "courtesytone")) {
+ ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
+ } else if (!strcasecmp(var->name, "parkedplay")) {
+ if (!strcasecmp(var->value, "both"))
+ parkedplay = 2;
+ else if (!strcasecmp(var->value, "parked"))
+ parkedplay = 1;
+ else
+ parkedplay = 0;
+ } else if (!strcasecmp(var->name, "xfersound")) {
+ ast_copy_string(xfersound, var->value, sizeof(xfersound));
+ } else if (!strcasecmp(var->name, "xferfailsound")) {
+ ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
+ } else if (!strcasecmp(var->name, "pickupexten")) {
+ ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
+ } else if (!strcasecmp(var->name, "comebacktoorigin")) {
+ comebacktoorigin = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "parkedmusicclass")) {
+ ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
+ }
+ }
+
+ unmap_features();
+ for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
+ if (remap_feature(var->name, var->value))
+ ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
+ }
+
+ /* Map a key combination to an application*/
+ ast_unregister_features();
+ for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
+ char *tmp_val = ast_strdupa(var->value);
+ char *exten, *activateon, *activatedby, *app, *app_args, *moh_class;
+ struct ast_call_feature *feature;
+
+ /* strsep() sets the argument to NULL if match not found, and it
+ * is safe to use it with a NULL argument, so we don't check
+ * between calls.
+ */
+ exten = strsep(&tmp_val,",");
+ activatedby = strsep(&tmp_val,",");
+ app = strsep(&tmp_val,",");
+ app_args = strsep(&tmp_val,",");
+ moh_class = strsep(&tmp_val,",");
+
+ activateon = strsep(&activatedby, "/");
+
+ /*! \todo XXX var_name or app_args ? */
+ if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
+ ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
+ app, exten, activateon, var->name);
+ continue;
+ }
+
+ AST_LIST_LOCK(&feature_list);
+ if ((feature = find_dynamic_feature(var->name))) {
+ AST_LIST_UNLOCK(&feature_list);
+ ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
+ continue;
+ }
+ AST_LIST_UNLOCK(&feature_list);
+
+ if (!(feature = ast_calloc(1, sizeof(*feature))))
+ continue;
+
+ ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
+ ast_copy_string(feature->app, app, FEATURE_APP_LEN);
+ ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
+
+ if (app_args)
+ ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
+
+ if (moh_class)
+ ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
+
+ ast_copy_string(feature->exten, exten, sizeof(feature->exten));
+ feature->operation = feature_exec_app;
+ ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
+
+ /* Allow caller and calle to be specified for backwards compatability */
+ if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
+ ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
+ else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
+ ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
+ else {
+ ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
+ " must be 'self', or 'peer'\n", var->name);
+ continue;
+ }
+
+ if (ast_strlen_zero(activatedby))
+ ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
+ else if (!strcasecmp(activatedby, "caller"))
+ ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
+ else if (!strcasecmp(activatedby, "callee"))
+ ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
+ else if (!strcasecmp(activatedby, "both"))
+ ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
+ else {
+ ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
+ " must be 'caller', or 'callee', or 'both'\n", var->name);
+ continue;
+ }
+
+ ast_register_feature(feature);
+
+ ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
+ }
+
+ ast_unregister_groups();
+ AST_RWLIST_WRLOCK(&feature_groups);
+
+ ctg = NULL;
+ while ((ctg = ast_category_browse(cfg, ctg))) {
+ for (i = 0; i < ARRAY_LEN(categories); i++) {
+ if (!strcasecmp(categories[i], ctg))
+ break;
+ }
+
+ if (i < ARRAY_LEN(categories))
+ continue;
+
+ if (!(fg = register_group(ctg)))
+ continue;
+
+ for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
+ struct ast_call_feature *feature;
+
+ AST_LIST_LOCK(&feature_list);
+ if(!(feature = find_dynamic_feature(var->name)) &&
+ !(feature = ast_find_call_feature(var->name))) {
+ AST_LIST_UNLOCK(&feature_list);
+ ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
+ continue;
+ }
+ AST_LIST_UNLOCK(&feature_list);
+
+ register_group_feature(fg, var->value, feature);
+ }
+ }
+
+ AST_RWLIST_UNLOCK(&feature_groups);
+
+ ast_config_destroy(cfg);
+
+ /* Remove the old parking extension */
+ if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
+ if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar))
+ notify_metermaids(old_parking_ext, old_parking_con, AST_DEVICE_NOT_INUSE);
+ ast_debug(1, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
+ }
+
+ if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
+ ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
+ return -1;
+ }
+ res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
+ if (parkaddhints)
+ park_add_hints(parking_con, parking_start, parking_stop);
+ if (!res)
+ notify_metermaids(ast_parking_ext(), parking_con, AST_DEVICE_INUSE);
+ return res;
+
+}
+
+static char *app_bridge = "Bridge";
+static char *bridge_synopsis = "Bridge two channels";
+static char *bridge_descrip =
+"Usage: Bridge(channel[,options])\n"
+" Allows the ability to bridge two channels via the dialplan.\n"
+"The current channel is bridged to the specified 'channel'.\n"
+" Options:\n"
+" p - Play a courtesy tone to 'channel'.\n"
+"This application sets the following channel variable upon completion:\n"
+" BRIDGERESULT The result of the bridge attempt as a text string, one of\n"
+" SUCCESS | FAILURE | LOOP | NONEXISTENT | INCOMPATIBLE\n";
+
+enum {
+ BRIDGE_OPT_PLAYTONE = (1 << 0),
+};
+
+AST_APP_OPTIONS(bridge_exec_options, BEGIN_OPTIONS
+ AST_APP_OPTION('p', BRIDGE_OPT_PLAYTONE)
+END_OPTIONS );
+
+/*!
+ * \brief Bridge channels
+ * \param chan
+ * \param data channel to bridge with.
+ *
+ * Split data, check we aren't bridging with ourself, check valid channel,
+ * answer call if not already, check compatible channels, setup bridge config
+ * now bridge call, if transfered party hangs up return to PBX extension.
+*/
+static int bridge_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_channel *current_dest_chan, *final_dest_chan;
+ char *tmp_data = NULL;
+ struct ast_flags opts = { 0, };
+ struct ast_bridge_config bconfig = { { 0, }, };
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(dest_chan);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Bridge require at least 1 argument specifying the other end of the bridge\n");
+ return -1;
+ }
+
+ tmp_data = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, tmp_data);
+ if (!ast_strlen_zero(args.options))
+ ast_app_parse_options(bridge_exec_options, &opts, NULL, args.options);
+
+ /* avoid bridge with ourselves */
+ if (!strncmp(chan->name, args.dest_chan,
+ strlen(chan->name) < strlen(args.dest_chan) ?
+ strlen(chan->name) : strlen(args.dest_chan))) {
+ ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", chan->name);
+ manager_event(EVENT_FLAG_CALL, "BridgeExec",
+ "Response: Failed\r\n"
+ "Reason: Unable to bridge channel to itself\r\n"
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n",
+ chan->name, args.dest_chan);
+ pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP");
+ return 0;
+ }
+
+ /* make sure we have a valid end point */
+ if (!(current_dest_chan = ast_get_channel_by_name_prefix_locked(args.dest_chan,
+ strlen(args.dest_chan)))) {
+ ast_log(LOG_WARNING, "Bridge failed because channel %s does not exists or we "
+ "cannot get its lock\n", args.dest_chan);
+ manager_event(EVENT_FLAG_CALL, "BridgeExec",
+ "Response: Failed\r\n"
+ "Reason: Cannot grab end point\r\n"
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n", chan->name, args.dest_chan);
+ pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT");
+ return 0;
+ }
+ ast_channel_unlock(current_dest_chan);
+
+ /* answer the channel if needed */
+ if (current_dest_chan->_state != AST_STATE_UP)
+ ast_answer(current_dest_chan);
+
+ /* try to allocate a place holder where current_dest_chan will be placed */
+ if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
+ NULL, NULL, 0, "Bridge/%s", current_dest_chan->name))) {
+ ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan);
+ manager_event(EVENT_FLAG_CALL, "BridgeExec",
+ "Response: Failed\r\n"
+ "Reason: cannot create placeholder\r\n"
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n", chan->name, args.dest_chan);
+ }
+ do_bridge_masquerade(current_dest_chan, final_dest_chan);
+
+ /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */
+ /* try to make compatible, send error if we fail */
+ if (ast_channel_make_compatible(chan, final_dest_chan) < 0) {
+ ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, final_dest_chan->name);
+ manager_event(EVENT_FLAG_CALL, "BridgeExec",
+ "Response: Failed\r\n"
+ "Reason: Could not make channels compatible for bridge\r\n"
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n", chan->name, final_dest_chan->name);
+ ast_hangup(final_dest_chan); /* may be we should return this channel to the PBX? */
+ pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
+ return 0;
+ }
+
+ /* Report that the bridge will be successfull */
+ manager_event(EVENT_FLAG_CALL, "BridgeExec",
+ "Response: Success\r\n"
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n", chan->name, final_dest_chan->name);
+
+ /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */
+ if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) {
+ if (!ast_streamfile(final_dest_chan, xfersound, final_dest_chan->language)) {
+ if (ast_waitstream(final_dest_chan, "") < 0)
+ ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", final_dest_chan->name);
+ }
+ }
+
+ /* do the bridge */
+ ast_bridge_call(chan, final_dest_chan, &bconfig);
+
+ /* the bridge has ended, set BRIDGERESULT to SUCCESS. If the other channel has not been hung up, return it to the PBX */
+ pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS");
+ if (!ast_check_hangup(final_dest_chan)) {
+ ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
+ final_dest_chan->context, final_dest_chan->exten,
+ final_dest_chan->priority, final_dest_chan->name);
+
+ if (ast_pbx_start(final_dest_chan) != AST_PBX_SUCCESS) {
+ ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", final_dest_chan->name);
+ ast_hangup(final_dest_chan);
+ } else
+ ast_debug(1, "SUCCESS continuing PBX on chan %s\n", final_dest_chan->name);
+ } else {
+ ast_debug(1, "hangup chan %s since the other endpoint has hung up\n", final_dest_chan->name);
+ ast_hangup(final_dest_chan);
+ }
+
+ return 0;
+}
+
+static int reload(void)
+{
+ return load_config();
+}
+
+static int load_module(void)
+{
+ int res;
+
+ ast_register_application(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip);
+
+ memset(parking_ext, 0, sizeof(parking_ext));
+ memset(parking_con, 0, sizeof(parking_con));
+
+ if ((res = load_config()))
+ return res;
+ ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
+ ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
+ res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
+ if (!res)
+ res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
+ if (!res) {
+ ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls");
+ ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
+ "Park a channel", mandescr_park);
+ ast_manager_register2("Bridge", EVENT_FLAG_CALL, action_bridge, "Bridge two channels already in the PBX", mandescr_bridge);
+ }
+
+ res |= ast_devstate_prov_add("Park", metermaidstate);
+
+ return res;
+}
+
+
+static int unload_module(void)
+{
+ struct ast_context *con;
+ ast_manager_unregister("ParkedCalls");
+ ast_manager_unregister("Bridge");
+ ast_manager_unregister("Park");
+ ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
+ ast_unregister_application(parkcall);
+ ast_unregister_application(app_bridge);
+ ast_devstate_prov_del("Park");
+ con = ast_context_find(parking_con);
+ if (con)
+ ast_context_destroy(con, registrar);
+ con = ast_context_find(parking_con_dial);
+ if (con)
+ ast_context_destroy(con, registrar);
+ return ast_unregister_application(parkedcall);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Features Resource",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_indications.c b/trunk/res/res_indications.c
new file mode 100644
index 000000000..a957b7828
--- /dev/null
+++ b/trunk/res/res_indications.c
@@ -0,0 +1,419 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002, Pauline Middelink
+ *
+ *
+ * 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 res_indications.c
+ *
+ * \brief Load the indications
+ *
+ * \author Pauline Middelink <middelink@polyware.nl>
+ *
+ * Load the country specific dialtones into the asterisk PBX.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/indications.h"
+#include "asterisk/utils.h"
+
+/* Globals */
+static const char config[] = "indications.conf";
+
+char *playtones_desc=
+" PlayTones(arg): Plays a tone list. Execution will continue with the next step immediately,\n"
+"while the tones continue to play.\n"
+"Arg is either the tone name defined in the indications.conf configuration file, or a directly\n"
+"specified list of frequencies and durations.\n"
+"See the sample indications.conf for a description of the specification of a tonelist.\n\n"
+"Use the StopPlayTones application to stop the tones playing. \n";
+
+/*
+ * Implementation of functions provided by this module
+ */
+
+/*!
+ * \brief Add a country to indication
+ * \param e the ast_cli_entry for this CLI command
+ * \param cmd the reason we are being called
+ * \param a the arguments being passed to us
+ */
+static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ind_tone_zone *tz;
+ int created_country = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "indication add";
+ e->usage =
+ "Usage: indication add <country> <indication> \"<tonelist>\"\n"
+ " Add the given indication to the country.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+
+ tz = ast_get_indication_zone(a->argv[2]);
+ if (!tz) {
+ /* country does not exist, create it */
+ ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
+
+ if (!(tz = ast_calloc(1, sizeof(*tz)))) {
+ return CLI_FAILURE;
+ }
+ ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
+ if (ast_register_indication_country(tz)) {
+ ast_log(LOG_WARNING, "Unable to register new country\n");
+ ast_free(tz);
+ return CLI_FAILURE;
+ }
+ created_country = 1;
+ }
+ if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
+ ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
+ if (created_country)
+ ast_unregister_indication_country(a->argv[2]);
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Remove a country from indication
+ * \param e the ast_cli_entry for this CLI command
+ * \param cmd the reason we are being called
+ * \param a the arguments being passed to us
+ */
+static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ind_tone_zone *tz;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "indication remove";
+ e->usage =
+ "Usage: indication remove <country> <indication>\n"
+ " Remove the given indication from the country.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3 && a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 3) {
+ /* remove entiry country */
+ if (ast_unregister_indication_country(a->argv[2])) {
+ ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+ }
+
+ tz = ast_get_indication_zone(a->argv[2]);
+ if (!tz) {
+ ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
+ return CLI_FAILURE;
+ }
+ if (ast_unregister_indication(tz, a->argv[3])) {
+ ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Show the current indications
+ * \param e the ast_cli_entry for this CLI command
+ * \param cmd the reason we are being called
+ * \param a the arguments being passed to us
+ */
+static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ind_tone_zone *tz = NULL;
+ char buf[256];
+ int found_country = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "indication show";
+ e->usage =
+ "Usage: indication show [<country> ...]\n"
+ " Display either a condensed for of all country/indications, or the\n"
+ " indications for the specified countries.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 2) {
+ /* no arguments, show a list of countries */
+ ast_cli(a->fd, "Country Alias Description\n");
+ ast_cli(a->fd, "===========================\n");
+ while ((tz = ast_walk_indications(tz)))
+ ast_cli(a->fd, "%-7.7s %-7.7s %s\n", tz->country, tz->alias, tz->description);
+ return CLI_SUCCESS;
+ }
+ /* there was a request for specific country(ies), lets humor them */
+ while ((tz = ast_walk_indications(tz))) {
+ int i, j;
+ for (i = 2; i < a->argc; i++) {
+ if (strcasecmp(tz->country, a->argv[i]) == 0 && !tz->alias[0]) {
+ struct ind_tone_zone_sound* ts;
+ if (!found_country) {
+ found_country = 1;
+ ast_cli(a->fd, "Country Indication PlayList\n");
+ ast_cli(a->fd, "=====================================\n");
+ }
+ j = snprintf(buf, sizeof(buf), "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
+ for (i = 0; i < tz->nrringcadence; i++) {
+ j += snprintf(buf + j, sizeof(buf) - j, "%d,", tz->ringcadence[i]);
+ }
+ if (tz->nrringcadence)
+ j--;
+ ast_copy_string(buf + j, "\n", sizeof(buf) - j);
+ ast_cli(a->fd, buf);
+ for (ts = tz->tones; ts; ts = ts->next)
+ ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
+ break;
+ }
+ }
+ }
+ if (!found_country)
+ ast_cli(a->fd, "No countries matched your criteria.\n");
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief play tone for indication country
+ * \param chan ast_channel to play the sounds back to
+ * \param data contains tone to play
+ */
+static int handle_playtones(struct ast_channel *chan, void *data)
+{
+ struct ind_tone_zone_sound *ts;
+ int res;
+
+ if (!data || !((char*)data)[0]) {
+ ast_log(LOG_NOTICE,"Nothing to play\n");
+ return -1;
+ }
+ ts = ast_get_indication_tone(chan->zone, (const char*)data);
+ if (ts && ts->data[0])
+ res = ast_playtones_start(chan, 0, ts->data, 0);
+ else
+ res = ast_playtones_start(chan, 0, (const char*)data, 0);
+ if (res)
+ ast_log(LOG_NOTICE,"Unable to start playtones\n");
+ return res;
+}
+
+/*!
+ * \brief Stop tones playing
+ * \param chan
+ * \param data
+ */
+static int handle_stopplaytones(struct ast_channel *chan, void *data)
+{
+ ast_playtones_stop(chan);
+ return 0;
+}
+
+/*! \brief load indications module */
+static int ind_load_module(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ char *cxt;
+ char *c;
+ struct ind_tone_zone *tones;
+ const char *country = NULL;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ /* that the following cast is needed, is yuk! */
+ /* yup, checked it out. It is NOT written to. */
+ cfg = ast_config_load((char *)config, config_flags);
+ if (!cfg)
+ return -1;
+ else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (reload)
+ ast_unregister_indication_country(NULL);
+
+ /* Use existing config to populate the Indication table */
+ cxt = ast_category_browse(cfg, NULL);
+ while(cxt) {
+ /* All categories but "general" are considered countries */
+ if (!strcasecmp(cxt, "general")) {
+ cxt = ast_category_browse(cfg, cxt);
+ continue;
+ }
+ if (!(tones = ast_calloc(1, sizeof(*tones)))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ ast_copy_string(tones->country,cxt,sizeof(tones->country));
+
+ v = ast_variable_browse(cfg, cxt);
+ while(v) {
+ if (!strcasecmp(v->name, "description")) {
+ ast_copy_string(tones->description, v->value, sizeof(tones->description));
+ } else if ((!strcasecmp(v->name,"ringcadence"))||(!strcasecmp(v->name,"ringcadance"))) {
+ char *ring,*rings = ast_strdupa(v->value);
+ c = rings;
+ ring = strsep(&c,",");
+ while (ring) {
+ int *tmp, val;
+ if (!isdigit(ring[0]) || (val=atoi(ring))==-1) {
+ ast_log(LOG_WARNING,"Invalid ringcadence given '%s' at line %d.\n",ring,v->lineno);
+ ring = strsep(&c,",");
+ continue;
+ }
+ if (!(tmp = ast_realloc(tones->ringcadence, (tones->nrringcadence + 1) * sizeof(int)))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ tones->ringcadence = tmp;
+ tmp[tones->nrringcadence] = val;
+ tones->nrringcadence++;
+ /* next item */
+ ring = strsep(&c,",");
+ }
+ } else if (!strcasecmp(v->name,"alias")) {
+ char *countries = ast_strdupa(v->value);
+ c = countries;
+ country = strsep(&c,",");
+ while (country) {
+ struct ind_tone_zone* azone;
+ if (!(azone = ast_calloc(1, sizeof(*azone)))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ ast_copy_string(azone->country, country, sizeof(azone->country));
+ ast_copy_string(azone->alias, cxt, sizeof(azone->alias));
+ if (ast_register_indication_country(azone)) {
+ ast_log(LOG_WARNING, "Unable to register indication alias at line %d.\n",v->lineno);
+ ast_free(tones);
+ }
+ /* next item */
+ country = strsep(&c,",");
+ }
+ } else {
+ /* add tone to country */
+ struct ind_tone_zone_sound *ps,*ts;
+ for (ps=NULL,ts=tones->tones; ts; ps=ts, ts=ts->next) {
+ if (strcasecmp(v->name,ts->name)==0) {
+ /* already there */
+ ast_log(LOG_NOTICE,"Duplicate entry '%s', skipped.\n",v->name);
+ goto out;
+ }
+ }
+ /* not there, add it to the back */
+ if (!(ts = ast_malloc(sizeof(*ts)))) {
+ ast_config_destroy(cfg);
+ return -1;
+ }
+ ts->next = NULL;
+ ts->name = ast_strdup(v->name);
+ ts->data = ast_strdup(v->value);
+ if (ps)
+ ps->next = ts;
+ else
+ tones->tones = ts;
+ }
+out: v = v->next;
+ }
+ if (tones->description[0] || tones->alias[0] || tones->tones) {
+ if (ast_register_indication_country(tones)) {
+ ast_log(LOG_WARNING, "Unable to register indication at line %d.\n",v->lineno);
+ ast_free(tones);
+ }
+ } else ast_free(tones);
+
+ cxt = ast_category_browse(cfg, cxt);
+ }
+
+ /* determine which country is the default */
+ country = ast_variable_retrieve(cfg,"general","country");
+ if (!country || !*country || ast_set_indication_country(country))
+ ast_log(LOG_WARNING,"Unable to set the default country (for indication tones)\n");
+
+ ast_config_destroy(cfg);
+ return 0;
+}
+
+/*! \brief CLI entries for commands provided by this module */
+static struct ast_cli_entry cli_indications[] = {
+ AST_CLI_DEFINE(handle_cli_indication_add, "Add the given indication to the country"),
+ AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
+ AST_CLI_DEFINE(handle_cli_indication_show, "Display a list of all countries/indications")
+};
+
+/*! \brief Unload indicators module */
+static int unload_module(void)
+{
+ /* remove the registed indications... */
+ ast_unregister_indication_country(NULL);
+
+ /* and the functions */
+ ast_cli_unregister_multiple(cli_indications, sizeof(cli_indications) / sizeof(struct ast_cli_entry));
+ ast_unregister_application("PlayTones");
+ ast_unregister_application("StopPlayTones");
+ return 0;
+}
+
+
+/*! \brief Load indications module */
+static int load_module(void)
+{
+ if (ind_load_module(0))
+ return AST_MODULE_LOAD_DECLINE;
+ ast_cli_register_multiple(cli_indications, sizeof(cli_indications) / sizeof(struct ast_cli_entry));
+ ast_register_application("PlayTones", handle_playtones, "Play a tone list", playtones_desc);
+ ast_register_application("StopPlayTones", handle_stopplaytones, "Stop playing a tone list"," StopPlayTones(): Stop playing a tone list");
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+/*! \brief Reload indications module */
+static int reload(void)
+{
+ return ind_load_module(1);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Region-specific tones",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_jabber.c b/trunk/res/res_jabber.c
new file mode 100644
index 000000000..bd12d2977
--- /dev/null
+++ b/trunk/res/res_jabber.c
@@ -0,0 +1,3015 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Matt O'Gorman <mogorman@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 A resource for interfacing asterisk directly as a client
+ * or a component to a jabber compliant server.
+ *
+ * \extref Iksemel http://code.google.com/p/iksemel/
+ *
+ * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
+ *
+ */
+
+/*** MODULEINFO
+ <depend>iksemel</depend>
+ <use>openssl</use>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <iksemel.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/jabber.h"
+#include "asterisk/file.h"
+#include "asterisk/config.h"
+#include "asterisk/callerid.h"
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/md5.h"
+#include "asterisk/acl.h"
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/astobj.h"
+#include "asterisk/astdb.h"
+#include "asterisk/manager.h"
+
+#define JABBER_CONFIG "jabber.conf"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/*-- Forward declarations */
+static void aji_buddy_destroy(struct aji_buddy *obj);
+static void aji_client_destroy(struct aji_client *obj);
+static int aji_send_exec(struct ast_channel *chan, void *data);
+static int aji_status_exec(struct ast_channel *chan, void *data);
+static int aji_is_secure(struct aji_client *client);
+static int aji_start_tls(struct aji_client *client);
+static int aji_tls_handshake(struct aji_client *client);
+static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
+static int aji_recv(struct aji_client *client, int timeout);
+static int aji_send_header(struct aji_client *client, const char *to);
+static int aji_send_raw(struct aji_client *client, const char *xmlstr);
+static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
+static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
+static int aji_act_hook(void *data, int type, iks *node);
+static void aji_handle_iq(struct aji_client *client, iks *node);
+static void aji_handle_message(struct aji_client *client, ikspak *pak);
+static void aji_handle_presence(struct aji_client *client, ikspak *pak);
+static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
+static void *aji_recv_loop(void *data);
+static int aji_initialize(struct aji_client *client);
+static int aji_client_connect(void *data, ikspak *pak);
+static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
+static char *aji_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *aji_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static int aji_create_client(char *label, struct ast_variable *var, int debug);
+static int aji_create_buddy(char *label, struct aji_client *client);
+static int aji_reload(int reload);
+static int aji_load_config(int reload);
+static void aji_pruneregister(struct aji_client *client);
+static int aji_filter_roster(void *data, ikspak *pak);
+static int aji_get_roster(struct aji_client *client);
+static int aji_client_info_handler(void *data, ikspak *pak);
+static int aji_dinfo_handler(void *data, ikspak *pak);
+static int aji_ditems_handler(void *data, ikspak *pak);
+static int aji_register_query_handler(void *data, ikspak *pak);
+static int aji_register_approve_handler(void *data, ikspak *pak);
+static int aji_reconnect(struct aji_client *client);
+static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
+/* No transports in this version */
+/*
+static int aji_create_transport(char *label, struct aji_client *client);
+static int aji_register_transport(void *data, ikspak *pak);
+static int aji_register_transport2(void *data, ikspak *pak);
+*/
+
+static struct ast_cli_entry aji_cli[] = {
+ AST_CLI_DEFINE(aji_do_debug, "Enable jabber debugging"),
+ AST_CLI_DEFINE(aji_no_debug, "Disable Jabber debug"),
+ AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
+ AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
+ AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
+ AST_CLI_DEFINE(aji_test, "Shows roster, but is generally used for mog's debugging."),
+};
+
+static char *app_ajisend = "JabberSend";
+
+static char *ajisend_synopsis = "JabberSend(jabber,screenname,message)";
+
+static char *ajisend_descrip =
+"JabberSend(Jabber,ScreenName,Message)\n"
+" Jabber - Client or transport Asterisk uses to connect to Jabber\n"
+" ScreenName - User Name to message.\n"
+" Message - Message to be sent to the buddy\n";
+
+static char *app_ajistatus = "JabberStatus";
+
+static char *ajistatus_synopsis = "JabberStatus(Jabber,ScreenName,Variable)";
+
+static char *ajistatus_descrip =
+"JabberStatus(Jabber,ScreenName,Variable)\n"
+" Jabber - Client or transport Asterisk uses to connect to Jabber\n"
+" ScreenName - User Name to retrieve status from.\n"
+" Variable - Variable to store presence in will be 1-6.\n"
+" In order, Online, Chatty, Away, XAway, DND, Offline\n"
+" If not in roster variable will = 7\n";
+
+struct aji_client_container clients;
+struct aji_capabilities *capabilities = NULL;
+
+/*! \brief Global flags, initialized to default values */
+static struct ast_flags globalflags = { AJI_AUTOPRUNE | AJI_AUTOREGISTER };
+
+/*!
+ * \brief Deletes the aji_client data structure.
+ * \param obj aji_client The structure we will delete.
+ * \return void.
+ */
+static void aji_client_destroy(struct aji_client *obj)
+{
+ struct aji_message *tmp;
+ ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, aji_buddy_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
+ iks_filter_delete(obj->f);
+ iks_parser_delete(obj->p);
+ iks_stack_delete(obj->stack);
+ AST_LIST_LOCK(&obj->messages);
+ while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
+ if (tmp->from)
+ ast_free(tmp->from);
+ if (tmp->message)
+ ast_free(tmp->message);
+ }
+ AST_LIST_HEAD_DESTROY(&obj->messages);
+ ast_free(obj);
+}
+
+/*!
+ * \brief Deletes the aji_buddy data structure.
+ * \param obj aji_buddy The structure we will delete.
+ * \return void.
+ */
+static void aji_buddy_destroy(struct aji_buddy *obj)
+{
+ struct aji_resource *tmp;
+
+ while ((tmp = obj->resources)) {
+ obj->resources = obj->resources->next;
+ ast_free(tmp->description);
+ ast_free(tmp);
+ }
+
+ ast_free(obj);
+}
+
+/*!
+ * \brief Find version in XML stream and populate our capabilities list
+ * \param node the node attribute in the caps element we'll look for or add to
+ * our list
+ * \param version the version attribute in the caps element we'll look for or
+ * add to our list
+ * \param pak struct The XML stanza we're processing
+ * \return a pointer to the added or found aji_version structure
+ */
+static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
+{
+ struct aji_capabilities *list = NULL;
+ struct aji_version *res = NULL;
+
+ list = capabilities;
+
+ if(!node)
+ node = pak->from->full;
+ if(!version)
+ version = "none supplied.";
+ while(list) {
+ if(!strcasecmp(list->node, node)) {
+ res = list->versions;
+ while(res) {
+ if(!strcasecmp(res->version, version))
+ return res;
+ res = res->next;
+ }
+ /* Specified version not found. Let's add it to
+ this node in our capabilities list */
+ if(!res) {
+ res = ast_malloc(sizeof(*res));
+ if(!res) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ return NULL;
+ }
+ res->jingle = 0;
+ res->parent = list;
+ ast_copy_string(res->version, version, sizeof(res->version));
+ res->next = list->versions;
+ list->versions = res;
+ return res;
+ }
+ }
+ list = list->next;
+ }
+ /* Specified node not found. Let's add it our capabilities list */
+ if(!list) {
+ list = ast_malloc(sizeof(*list));
+ if(!list) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ return NULL;
+ }
+ res = ast_malloc(sizeof(*res));
+ if(!res) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ ast_free(list);
+ return NULL;
+ }
+ ast_copy_string(list->node, node, sizeof(list->node));
+ ast_copy_string(res->version, version, sizeof(res->version));
+ res->jingle = 0;
+ res->parent = list;
+ res->next = NULL;
+ list->versions = res;
+ list->next = capabilities;
+ capabilities = list;
+ }
+ return res;
+}
+/*!
+ * \brief Find the aji_resource we want
+ * \param buddy aji_buddy A buddy
+ * \param name
+ * \return aji_resource object
+*/
+static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
+{
+ struct aji_resource *res = NULL;
+ if (!buddy || !name)
+ return res;
+ res = buddy->resources;
+ while (res) {
+ if (!strcasecmp(res->resource, name)) {
+ break;
+ }
+ res = res->next;
+ }
+ return res;
+}
+
+/*!
+ * \brief Jabber GTalk function
+ * \param node iks
+ * \return 1 on success, 0 on failure.
+*/
+static int gtalk_yuck(iks *node)
+{
+ if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps"))
+ return 1;
+ return 0;
+}
+
+/*!
+ * \brief Setup the authentication struct
+ * \param id iksid
+ * \param pass password
+ * \param sid
+ * \return x iks
+*/
+static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
+{
+ iks *x, *y;
+ x = iks_new("iq");
+ iks_insert_attrib(x, "type", "set");
+ y = iks_insert(x, "query");
+ iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
+ iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
+ iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
+ if (sid) {
+ char buf[41];
+ char sidpass[100];
+ snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
+ ast_sha1_hash(buf, sidpass);
+ iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
+ } else {
+ iks_insert_cdata(iks_insert(y, "password"), pass, 0);
+ }
+ return x;
+}
+
+/*!
+ * \brief Dial plan function status(). puts the status of watched user
+ into a channel variable.
+ * \param chan ast_channel
+ * \param data
+ * \return 0 on success, -1 on error
+ */
+static int aji_status_exec(struct ast_channel *chan, void *data)
+{
+ struct aji_client *client = NULL;
+ struct aji_buddy *buddy = NULL;
+ struct aji_resource *r = NULL;
+ char *s = NULL;
+ int stat = 7;
+ char status[2];
+ static int deprecation_warning = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(sender);
+ AST_APP_ARG(jid);
+ AST_APP_ARG(variable);
+ );
+ AST_DECLARE_APP_ARGS(jid,
+ AST_APP_ARG(screenname);
+ AST_APP_ARG(resource);
+ );
+
+ if (deprecation_warning++ % 10 == 0)
+ ast_log(LOG_WARNING, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<screenname>[/<resource>],<varname>\n");
+ return 0;
+ }
+ s = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, s);
+
+ if (args.argc != 3) {
+ ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
+
+ if (!(client = ast_aji_get_client(args.sender))) {
+ ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
+ return -1;
+ }
+ buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
+ if (!buddy) {
+ ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
+ return -1;
+ }
+ r = aji_find_resource(buddy, jid.resource);
+ if (!r && buddy->resources)
+ r = buddy->resources;
+ if (!r)
+ ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
+ else
+ stat = r->status;
+ snprintf(status, sizeof(status), "%d", stat);
+ pbx_builtin_setvar_helper(chan, args.variable, status);
+ return 0;
+}
+
+static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
+{
+ struct aji_client *client = NULL;
+ struct aji_buddy *buddy = NULL;
+ struct aji_resource *r = NULL;
+ int stat = 7;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(sender);
+ AST_APP_ARG(jid);
+ );
+ AST_DECLARE_APP_ARGS(jid,
+ AST_APP_ARG(screenname);
+ AST_APP_ARG(resource);
+ );
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<screenname>[/<resource>])\n");
+ return 0;
+ }
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.argc != 2) {
+ ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments.\n");
+ return -1;
+ }
+
+ AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
+
+ if (!(client = ast_aji_get_client(args.sender))) {
+ ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
+ return -1;
+ }
+ buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
+ if (!buddy) {
+ ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
+ return -1;
+ }
+ r = aji_find_resource(buddy, jid.resource);
+ if (!r && buddy->resources)
+ r = buddy->resources;
+ if (!r)
+ ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
+ else
+ stat = r->status;
+ snprintf(buf, buflen, "%d", stat);
+ return 0;
+}
+
+static struct ast_custom_function jabberstatus_function = {
+ .name = "JABBER_STATUS",
+ .synopsis = "Retrieve buddy status",
+ .syntax = "JABBER_STATUS(<sender>,<buddy>[/<resource>])",
+ .read = acf_jabberstatus_read,
+ .desc =
+"Retrieves the numeric status associated with the specified buddy. If the\n"
+"buddy does not exist in the buddylist, returns 7.\n",
+};
+
+/*!
+ * \brief Dial plan function to send a message.
+ * \param chan ast_channel
+ * \param data Data is sender|reciever|message.
+ * \return 0 on success,-1 on error.
+ */
+static int aji_send_exec(struct ast_channel *chan, void *data)
+{
+ struct aji_client *client = NULL;
+ char *s;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(sender);
+ AST_APP_ARG(recipient);
+ AST_APP_ARG(message);
+ );
+
+ if (!data) {
+ ast_log(LOG_ERROR, "Usage: JabberSend(<sender>,<recipient>,<message>)\n");
+ return 0;
+ }
+ s = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, s);
+ if (args.argc < 3) {
+ ast_log(LOG_ERROR, "JabberSend requires 3 arguments: '%s'\n", (char *) data);
+ return -1;
+ }
+
+ if (!(client = ast_aji_get_client(args.sender))) {
+ ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
+ return -1;
+ }
+ if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message))
+ ast_aji_send_chat(client, args.recipient, args.message);
+ return 0;
+}
+
+/*!
+ * \brief Tests whether the connection is secured or not
+ * \return 0 if the connection is not secured
+ */
+static int aji_is_secure(struct aji_client *client)
+{
+#ifdef HAVE_OPENSSL
+ return client->stream_flags & SECURE;
+#else
+ return 0;
+#endif
+}
+
+
+/*!
+ * \brief Starts the TLS procedure
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
+ * if OpenSSL is not installed
+ */
+static int aji_start_tls(struct aji_client *client)
+{
+ int ret;
+#ifndef HAVE_OPENSSL
+ return IKS_NET_TLSFAIL;
+#endif
+ /* This is sent not encrypted */
+ ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
+ if (ret)
+ return ret;
+ client->stream_flags |= TRY_SECURE;
+
+ return IKS_OK;
+}
+
+/*!
+ * \brief TLS handshake, OpenSSL initialization
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
+ */
+static int aji_tls_handshake(struct aji_client *client)
+{
+ int ret;
+ int sock;
+
+#ifndef HAVE_OPENSSL
+ return IKS_NET_TLSFAIL;
+#endif
+
+ ast_debug(1, "Starting TLS handshake\n");
+
+ /* Load encryption, hashing algorithms and error strings */
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ /* Choose an SSL/TLS protocol version, create SSL_CTX */
+ client->ssl_method = SSLv3_method();
+ client->ssl_context = SSL_CTX_new(client->ssl_method);
+ if (!client->ssl_context)
+ return IKS_NET_TLSFAIL;
+
+ /* Create new SSL session */
+ client->ssl_session = SSL_new(client->ssl_context);
+ if (!client->ssl_session)
+ return IKS_NET_TLSFAIL;
+
+ /* Enforce TLS on our XMPP connection */
+ sock = iks_fd(client->p);
+ ret = SSL_set_fd(client->ssl_session, sock);
+ if (!ret)
+ return IKS_NET_TLSFAIL;
+
+ /* Perform SSL handshake */
+ ret = SSL_connect(client->ssl_session);
+ if (!ret)
+ return IKS_NET_TLSFAIL;
+
+ client->stream_flags &= (~TRY_SECURE);
+ client->stream_flags |= SECURE;
+
+ /* Sent over the established TLS connection */
+ ret = aji_send_header(client, client->jid->server);
+ if (ret != IKS_OK)
+ return IKS_NET_TLSFAIL;
+
+ ast_debug(1, "TLS started with server\n");
+
+ return IKS_OK;
+}
+
+/*!
+ * \brief Secured or unsecured IO socket receiving function
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param buffer the reception buffer
+ * \param buf_len the size of the buffer
+ * \param timeout the select timer
+ * \return the number of read bytes on success, 0 on timeout expiration,
+ * -1 on error
+ */
+static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
+{
+ int sock;
+ fd_set fds;
+ struct timeval tv, *tvptr = NULL;
+ int len, res;
+
+#ifdef HAVE_OPENSSL
+ if (aji_is_secure(client)) {
+ sock = SSL_get_fd(client->ssl_session);
+ if (sock < 0)
+ return -1;
+ } else
+#endif /* HAVE_OPENSSL */
+ sock = iks_fd(client->p);
+
+ memset(&tv, 0, sizeof(struct timeval));
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ tv.tv_sec = timeout;
+
+ /* NULL value for tvptr makes ast_select wait indefinitely */
+ tvptr = (timeout != -1) ? &tv : NULL;
+
+ /* ast_select emulates linux behaviour in terms of timeout handling */
+ res = ast_select(sock + 1, &fds, NULL, NULL, tvptr);
+ if (res > 0) {
+#ifdef HAVE_OPENSSL
+ if (aji_is_secure(client)) {
+ len = SSL_read(client->ssl_session, buffer, buf_len);
+ } else
+#endif /* HAVE_OPENSSL */
+ len = recv(sock, buffer, buf_len, 0);
+
+ if (len > 0) {
+ return len;
+ } else if (len <= 0) {
+ return -1;
+ }
+ }
+ return res;
+}
+
+/*!
+ * \brief Tries to receive data from the Jabber server
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param timeout the timeout value
+ * This function receives (encrypted or unencrypted) data from the XMPP server,
+ * and passes it to the parser.
+ * \return IKS_OK on success, IKS_NET_RWERR on IO error, IKS_NET_NOCONN, if no
+ * connection available, IKS_NET_EXPIRED on timeout expiration
+ */
+static int aji_recv (struct aji_client *client, int timeout)
+{
+ int len, ret;
+ char buf[NET_IO_BUF_SIZE -1];
+
+ memset(buf, 0, sizeof(buf));
+
+ while (1) {
+ len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 1, timeout);
+ if (len < 0) return IKS_NET_RWERR;
+ if (len == 0) return IKS_NET_EXPIRED;
+ buf[len] = '\0';
+
+ /* Log the message here, because iksemel's logHook is
+ unaccessible */
+ aji_log_hook(client, buf, len, 1);
+
+ ret = iks_parse(client->p, buf, len, 0);
+ if (ret != IKS_OK) {
+ return ret;
+ }
+ }
+ return IKS_OK;
+}
+
+/*!
+ * \brief Sends XMPP header to the server
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param to the target XMPP server
+ * \return IKS_OK on success, any other value on failure
+ */
+static int aji_send_header(struct aji_client *client, const char *to)
+{
+ char *msg;
+ int len, err;
+
+ len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
+ msg = iks_malloc(len);
+ if (!msg)
+ return IKS_NOMEM;
+ sprintf(msg, "<?xml version='1.0'?>"
+ "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
+ "%s' to='%s' version='1.0'>", client->name_space, to);
+ err = aji_send_raw(client, msg);
+ iks_free(msg);
+ if (err != IKS_OK)
+ return err;
+
+ return IKS_OK;
+}
+
+/*!
+ * \brief Wraps raw sending
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param x the XMPP packet to send
+ * \return IKS_OK on success, any other value on failure
+ */
+int ast_aji_send(struct aji_client *client, iks *x)
+{
+ return aji_send_raw(client, iks_string(iks_stack(x), x));
+}
+
+/*!
+ * \brief Sends an XML string over an XMPP connection
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param xmlstr the XML string to send
+ * The XML data is sent whether the connection is secured or not. In the
+ * latter case, we just call iks_send_raw().
+ * \return IKS_OK on success, any other value on failure
+ */
+static int aji_send_raw(struct aji_client *client, const char *xmlstr)
+{
+ int ret;
+#ifdef HAVE_OPENSSL
+ int len = strlen(xmlstr);
+
+ if (aji_is_secure(client)) {
+ ret = SSL_write(client->ssl_session, xmlstr, len);
+ if (ret) {
+ /* Log the message here, because iksemel's logHook is
+ unaccessible */
+ aji_log_hook(client, xmlstr, len, 0);
+ return IKS_OK;
+ }
+ }
+#endif
+ /* If needed, data will be sent unencrypted, and logHook will
+ be called inside iks_send_raw */
+ ret = iks_send_raw(client->p, xmlstr);
+ if (ret != IKS_OK)
+ return ret;
+
+ return IKS_OK;
+}
+
+/*!
+ * \brief the debug loop.
+ * \param data void
+ * \param xmpp xml data as string
+ * \param size size of string
+ * \param is_incoming direction of packet 1 for inbound 0 for outbound.
+ */
+static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+
+ if (!ast_strlen_zero(xmpp))
+ manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
+
+ if (client->debug) {
+ if (is_incoming)
+ ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
+ else {
+ if( strlen(xmpp) == 1) {
+ if(option_debug > 2 && xmpp[0] == ' ')
+ ast_verbose("\nJABBER: Keep alive packet\n");
+ } else
+ ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
+ }
+
+ }
+ ASTOBJ_UNREF(client, aji_client_destroy);
+}
+
+/*!
+ * \brief A wrapper function for iks_start_sasl
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param type the SASL authentication type. Supported types are PLAIN and MD5
+ * \param username
+ * \param pass password.
+ *
+ * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
+ */
+static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
+{
+ iks *x = NULL;
+ int len;
+ char *s;
+ char *base64;
+
+ /* trigger SASL DIGEST-MD5 only over an unsecured connection.
+ iks_start_sasl is an iksemel API function and relies on GnuTLS,
+ whereas we use OpenSSL */
+ if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
+ return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass);
+ if (!(type & IKS_STREAM_SASL_PLAIN)) {
+ ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
+ return IKS_NET_NOTSUPP;
+ }
+
+ x = iks_new("auth");
+ if (!x) {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ return IKS_NET_NOTSUPP;
+ }
+
+ iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
+ len = strlen(username) + strlen(pass) + 3;
+ s = alloca(len);
+ base64 = alloca((len + 2) * 4 / 3);
+ iks_insert_attrib(x, "mechanism", "PLAIN");
+ snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
+
+ /* exclude the NULL training byte from the base64 encoding operation
+ as some XMPP servers will refuse it.
+ The format for authentication is [authzid]\0authcid\0password
+ not [authzid]\0authcid\0password\0 */
+ ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
+ iks_insert_cdata(x, base64, 0);
+ ast_aji_send(client, x);
+ iks_delete(x);
+
+ return IKS_OK;
+}
+
+/*!
+ * \brief The action hook parses the inbound packets, constantly running.
+ * \param data aji client structure
+ * \param type type of packet
+ * \param node the actual packet.
+ * \return IKS_OK or IKS_HOOK .
+ */
+static int aji_act_hook(void *data, int type, iks *node)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ ikspak *pak = NULL;
+ iks *auth = NULL;
+ int features = 0;
+
+ if(!node) {
+ ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+ }
+
+ if (client->state == AJI_DISCONNECTING) {
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+ }
+
+ pak = iks_packet(node);
+
+ if (!client->component) { /*client */
+ switch (type) {
+ case IKS_NODE_START:
+ if (client->usetls && !aji_is_secure(client)) {
+ if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
+ ast_log(LOG_ERROR, "OpenSSL not installed. You need to install OpenSSL on this system\n");
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+ }
+
+ break;
+ }
+ if (!client->usesasl) {
+ iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid, IKS_RULE_DONE);
+ auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
+ if (auth) {
+ iks_insert_attrib(auth, "id", client->mid);
+ iks_insert_attrib(auth, "to", client->jid->server);
+ ast_aji_increment_mid(client->mid);
+ ast_aji_send(client, auth);
+ iks_delete(auth);
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ break;
+
+ case IKS_NODE_NORMAL:
+ if (client->stream_flags & TRY_SECURE) {
+ if (!strcmp("proceed", iks_name(node))) {
+ return aji_tls_handshake(client);
+ }
+ }
+
+ if (!strcmp("stream:features", iks_name(node))) {
+ features = iks_stream_features(node);
+ if (client->usesasl) {
+ if (client->usetls && !aji_is_secure(client))
+ break;
+ if (client->authorized) {
+ if (features & IKS_STREAM_BIND) {
+ iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE);
+ auth = iks_make_resource_bind(client->jid);
+ if (auth) {
+ iks_insert_attrib(auth, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ ast_aji_send(client, auth);
+ iks_delete(auth);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ break;
+ }
+ }
+ if (features & IKS_STREAM_SESSION) {
+ iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE);
+ auth = iks_make_session();
+ if (auth) {
+ iks_insert_attrib(auth, "id", "auth");
+ ast_aji_increment_mid(client->mid);
+ ast_aji_send(client, auth);
+ iks_delete(auth);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ }
+ } else {
+ int ret;
+ if (!client->jid->user) {
+ ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
+ break;
+ }
+
+ ret = aji_start_sasl(client, features, client->jid->user, client->password);
+ if (ret != IKS_OK) {
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+ }
+ break;
+ }
+ }
+ } else if (!strcmp("failure", iks_name(node))) {
+ ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
+ } else if (!strcmp("success", iks_name(node))) {
+ client->authorized = 1;
+ aji_send_header(client, client->jid->server);
+ }
+ break;
+ case IKS_NODE_ERROR:
+ ast_log(LOG_ERROR, "JABBER: Node Error\n");
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+ break;
+ case IKS_NODE_STOP:
+ ast_log(LOG_WARNING, "JABBER: Disconnected\n");
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+ break;
+ }
+ } else if (client->state != AJI_CONNECTED && client->component) {
+ switch (type) {
+ case IKS_NODE_START:
+ if (client->state == AJI_DISCONNECTED) {
+ char secret[160], shasum[320], *handshake;
+
+ sprintf(secret, "%s%s", pak->id, client->password);
+ ast_sha1_hash(shasum, secret);
+ handshake = NULL;
+ asprintf(&handshake, "<handshake>%s</handshake>", shasum);
+ if (handshake) {
+ aji_send_raw(client, handshake);
+ ast_free(handshake);
+ handshake = NULL;
+ }
+ client->state = AJI_CONNECTING;
+ if(aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
+ client->state = AJI_CONNECTED;
+ else
+ ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
+ break;
+ }
+ break;
+
+ case IKS_NODE_NORMAL:
+ break;
+
+ case IKS_NODE_ERROR:
+ ast_log(LOG_ERROR, "JABBER: Node Error\n");
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+
+ case IKS_NODE_STOP:
+ ast_log(LOG_WARNING, "JABBER: Disconnected\n");
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_HOOK;
+ }
+ }
+
+ switch (pak->type) {
+ case IKS_PAK_NONE:
+ ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
+ break;
+ case IKS_PAK_MESSAGE:
+ aji_handle_message(client, pak);
+ ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
+ break;
+ case IKS_PAK_PRESENCE:
+ aji_handle_presence(client, pak);
+ ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
+ break;
+ case IKS_PAK_S10N:
+ aji_handle_subscribe(client, pak);
+ ast_debug(1, "JABBER: Handling paktype S10N\n");
+ break;
+ case IKS_PAK_IQ:
+ ast_debug(1, "JABBER: Handling paktype IQ\n");
+ aji_handle_iq(client, node);
+ break;
+ default:
+ ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
+ break;
+ }
+
+ iks_filter_packet(client->f, pak);
+
+ if (node)
+ iks_delete(node);
+
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_OK;
+}
+/*!
+ * \brief Uknown
+ * \param data void
+ * \param pak ikspak
+ * \return IKS_FILTER_EAT.
+*/
+static int aji_register_approve_handler(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ iks *iq = NULL, *presence = NULL, *x = NULL;
+
+ iq = iks_new("iq");
+ presence = iks_new("presence");
+ x = iks_new("x");
+ if (client && iq && presence && x) {
+ if (!iks_find(pak->query, "remove")) {
+ iks_insert_attrib(iq, "from", client->jid->full);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ ast_aji_send(client, iq);
+
+ iks_insert_attrib(presence, "from", client->jid->full);
+ iks_insert_attrib(presence, "to", pak->from->partial);
+ iks_insert_attrib(presence, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ iks_insert_attrib(presence, "type", "subscribe");
+ iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
+ iks_insert_node(presence, x);
+ ast_aji_send(client, presence);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+
+ if (iq)
+ iks_delete(iq);
+ if(presence)
+ iks_delete(presence);
+ if (x)
+ iks_delete(x);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+}
+/*!
+ * \brief register query
+ * \param data incoming aji_client request
+ * \param pak ikspak
+ * \return IKS_FILTER_EAT.
+*/
+static int aji_register_query_handler(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ struct aji_buddy *buddy = NULL;
+ char *node = NULL;
+
+ client = (struct aji_client *) data;
+
+ buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
+ if (!buddy) {
+ iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL;
+
+ ast_verbose("Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
+ iq = iks_new("iq");
+ query = iks_new("query");
+ error = iks_new("error");
+ notacceptable = iks_new("not-acceptable");
+ if(iq && query && error && notacceptable) {
+ iks_insert_attrib(iq, "type", "error");
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(query, "xmlns", "jabber:iq:register");
+ iks_insert_attrib(error, "code" , "406");
+ iks_insert_attrib(error, "type", "modify");
+ iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
+ iks_insert_node(iq, query);
+ iks_insert_node(iq, error);
+ iks_insert_node(error, notacceptable);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (error)
+ iks_delete(error);
+ if (notacceptable)
+ iks_delete(notacceptable);
+ } else if (!(node = iks_find_attrib(pak->query, "node"))) {
+ iks *iq = NULL, *query = NULL, *instructions = NULL;
+ char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
+ iq = iks_new("iq");
+ query = iks_new("query");
+ instructions = iks_new("instructions");
+ if (iq && query && instructions && client) {
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(query, "xmlns", "jabber:iq:register");
+ iks_insert_cdata(instructions, explain, 0);
+ iks_insert_node(iq, query);
+ iks_insert_node(query, instructions);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (instructions)
+ iks_delete(instructions);
+ }
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+}
+
+/*!
+ * \brief Handles stuff
+ * \param data void
+ * \param pak ikspak
+ * \return IKS_FILTER_EAT.
+*/
+static int aji_ditems_handler(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ char *node = NULL;
+
+ if (!(node = iks_find_attrib(pak->query, "node"))) {
+ iks *iq = NULL, *query = NULL, *item = NULL;
+ iq = iks_new("iq");
+ query = iks_new("query");
+ item = iks_new("item");
+
+ if (iq && query && item) {
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
+ iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
+ iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
+ iks_insert_attrib(item, "jid", client->user);
+
+ iks_insert_node(iq, query);
+ iks_insert_node(query, item);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (item)
+ iks_delete(item);
+
+ } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
+ iks *iq, *query, *confirm;
+ iq = iks_new("iq");
+ query = iks_new("query");
+ confirm = iks_new("item");
+ if (iq && query && confirm && client) {
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
+ iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
+ iks_insert_attrib(confirm, "node", "confirmaccount");
+ iks_insert_attrib(confirm, "name", "Confirm AIM account");
+ iks_insert_attrib(confirm, "jid", "blog.astjab.org");
+
+ iks_insert_node(iq, query);
+ iks_insert_node(query, confirm);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (confirm)
+ iks_delete(confirm);
+
+ } else if (!strcasecmp(node, "confirmaccount")) {
+ iks *iq = NULL, *query = NULL, *feature = NULL;
+
+ iq = iks_new("iq");
+ query = iks_new("query");
+ feature = iks_new("feature");
+
+ if (iq && query && feature && client) {
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
+ iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
+ iks_insert_node(iq, query);
+ iks_insert_node(query, feature);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (feature)
+ iks_delete(feature);
+ }
+
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+
+}
+/*!
+ * \brief Handle add extra info
+ * \param data void
+ * \param pak ikspak
+ * \return IKS_FILTER_EAT
+*/
+static int aji_client_info_handler(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ struct aji_resource *resource = NULL;
+ struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
+
+ resource = aji_find_resource(buddy, pak->from->resource);
+ if (pak->subtype == IKS_TYPE_RESULT) {
+ if (!resource) {
+ ast_log(LOG_NOTICE,"JABBER: Received client info from %s when not requested.\n", pak->from->full);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+ }
+ if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
+ resource->cap->jingle = 1;
+ } else
+ resource->cap->jingle = 0;
+ } else if (pak->subtype == IKS_TYPE_GET) {
+ iks *iq, *disco, *ident, *google, *query;
+ iq = iks_new("iq");
+ query = iks_new("query");
+ ident = iks_new("identity");
+ disco = iks_new("feature");
+ google = iks_new("feature");
+ if (iq && ident && disco && google) {
+ iks_insert_attrib(iq, "from", client->jid->full);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
+ iks_insert_attrib(ident, "category", "client");
+ iks_insert_attrib(ident, "type", "pc");
+ iks_insert_attrib(ident, "name", "asterisk");
+ iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
+ iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
+ iks_insert_node(iq, query);
+ iks_insert_node(query, ident);
+ iks_insert_node(query, google);
+ iks_insert_node(query, disco);
+ ast_aji_send(client, iq);
+ } else
+ ast_log(LOG_ERROR, "Out of Memory.\n");
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (ident)
+ iks_delete(ident);
+ if (google)
+ iks_delete(google);
+ if (disco)
+ iks_delete(disco);
+ } else if (pak->subtype == IKS_TYPE_ERROR) {
+ ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
+ }
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+}
+/*!
+ * \brief Handler of the return info packet
+ * \param data aji_client
+ * \param pak ikspak
+ * \return IKS_FILTER_EAT
+*/
+static int aji_dinfo_handler(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ char *node = NULL;
+ struct aji_resource *resource = NULL;
+ struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
+
+ resource = aji_find_resource(buddy, pak->from->resource);
+ if (pak->subtype == IKS_TYPE_ERROR) {
+ ast_log(LOG_WARNING, "Recieved error from a client, turn on jabber debug!\n");
+ return IKS_FILTER_EAT;
+ }
+ if (pak->subtype == IKS_TYPE_RESULT) {
+ if (!resource) {
+ ast_log(LOG_NOTICE,"JABBER: Received client info from %s when not requested.\n", pak->from->full);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+ }
+ if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
+ resource->cap->jingle = 1;
+ } else
+ resource->cap->jingle = 0;
+ } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
+ iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
+
+ iq = iks_new("iq");
+ query = iks_new("query");
+ identity = iks_new("identity");
+ disco = iks_new("feature");
+ reg = iks_new("feature");
+ commands = iks_new("feature");
+ gateway = iks_new("feature");
+ version = iks_new("feature");
+ vcard = iks_new("feature");
+ search = iks_new("feature");
+
+ if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
+ iks_insert_attrib(identity, "category", "gateway");
+ iks_insert_attrib(identity, "type", "pstn");
+ iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
+ iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
+ iks_insert_attrib(reg, "var", "jabber:iq:register");
+ iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
+ iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
+ iks_insert_attrib(version, "var", "jabber:iq:version");
+ iks_insert_attrib(vcard, "var", "vcard-temp");
+ iks_insert_attrib(search, "var", "jabber:iq:search");
+
+ iks_insert_node(iq, query);
+ iks_insert_node(query, identity);
+ iks_insert_node(query, disco);
+ iks_insert_node(query, reg);
+ iks_insert_node(query, commands);
+ iks_insert_node(query, gateway);
+ iks_insert_node(query, version);
+ iks_insert_node(query, vcard);
+ iks_insert_node(query, search);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (identity)
+ iks_delete(identity);
+ if (disco)
+ iks_delete(disco);
+ if (reg)
+ iks_delete(reg);
+ if (commands)
+ iks_delete(commands);
+ if (gateway)
+ iks_delete(gateway);
+ if (version)
+ iks_delete(version);
+ if (vcard)
+ iks_delete(vcard);
+ if (search)
+ iks_delete(search);
+
+ } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
+ iks *iq, *query, *confirm;
+ iq = iks_new("iq");
+ query = iks_new("query");
+ confirm = iks_new("item");
+
+ if (iq && query && confirm && client) {
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
+ iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
+ iks_insert_attrib(confirm, "node", "confirmaccount");
+ iks_insert_attrib(confirm, "name", "Confirm AIM account");
+ iks_insert_attrib(confirm, "jid", client->user);
+ iks_insert_node(iq, query);
+ iks_insert_node(query, confirm);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (confirm)
+ iks_delete(confirm);
+
+ } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
+ iks *iq, *query, *feature;
+
+ iq = iks_new("iq");
+ query = iks_new("query");
+ feature = iks_new("feature");
+
+ if (iq && query && feature && client) {
+ iks_insert_attrib(iq, "from", client->user);
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq, "id", pak->id);
+ iks_insert_attrib(iq, "type", "result");
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
+ iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
+ iks_insert_node(iq, query);
+ iks_insert_node(query, feature);
+ ast_aji_send(client, iq);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (iq)
+ iks_delete(iq);
+ if (query)
+ iks_delete(query);
+ if (feature)
+ iks_delete(feature);
+ }
+
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+}
+
+/*!
+ * \brief Handles \verbatim <iq> \endverbatim tags.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param node iks
+ * \return void.
+ */
+static void aji_handle_iq(struct aji_client *client, iks *node)
+{
+ /*Nothing to see here */
+}
+
+/*!
+ * \brief Handles presence packets.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param pak ikspak the node
+ */
+static void aji_handle_message(struct aji_client *client, ikspak *pak)
+{
+ struct aji_message *insert, *tmp;
+ int flag = 0;
+
+ if (!(insert = ast_calloc(1, sizeof(*insert))))
+ return;
+ time(&insert->arrived);
+ if (iks_find_cdata(pak->x, "body"))
+ insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
+ if (pak->id)
+ ast_copy_string(insert->id, pak->id, sizeof(insert->message));
+ if (pak->from)
+ insert->from = ast_strdup(pak->from->full);
+ AST_LIST_LOCK(&client->messages);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
+ if (flag) {
+ AST_LIST_REMOVE_CURRENT(list);
+ if (tmp->from)
+ ast_free(tmp->from);
+ if (tmp->message)
+ ast_free(tmp->message);
+ } else if (difftime(time(NULL), tmp->arrived) >= client->message_timeout) {
+ flag = 1;
+ AST_LIST_REMOVE_CURRENT(list);
+ if (tmp->from)
+ ast_free(tmp->from);
+ if (tmp->message)
+ ast_free(tmp->message);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_INSERT_HEAD(&client->messages, insert, list);
+ AST_LIST_UNLOCK(&client->messages);
+}
+/*!
+ * \brief Check the presence info
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param pak ikspak
+*/
+static void aji_handle_presence(struct aji_client *client, ikspak *pak)
+{
+ int status, priority;
+ struct aji_buddy *buddy;
+ struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
+ char *ver, *node, *descrip, *type;
+
+ if(client->state != AJI_CONNECTED)
+ aji_create_buddy(pak->from->partial, client);
+
+ buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
+ if (!buddy && pak->from->partial) {
+ /* allow our jid to be used to log in with another resource */
+ if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
+ aji_create_buddy(pak->from->partial, client);
+ else
+ ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
+ return;
+ }
+ type = iks_find_attrib(pak->x, "type");
+ if(client->component && type &&!strcasecmp("probe", type)) {
+ aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
+ ast_verbose("what i was looking for \n");
+ }
+ ASTOBJ_WRLOCK(buddy);
+ status = (pak->show) ? pak->show : 6;
+ priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
+ tmp = buddy->resources;
+ descrip = ast_strdup(iks_find_cdata(pak->x,"status"));
+
+ while (tmp && pak->from->resource) {
+ if (!strcasecmp(tmp->resource, pak->from->resource)) {
+ tmp->status = status;
+ if (tmp->description) ast_free(tmp->description);
+ tmp->description = descrip;
+ found = tmp;
+ if (status == 6) { /* Sign off Destroy resource */
+ if (last && found->next) {
+ last->next = found->next;
+ } else if (!last) {
+ if (found->next)
+ buddy->resources = found->next;
+ else
+ buddy->resources = NULL;
+ } else if (!found->next) {
+ if (last)
+ last->next = NULL;
+ else
+ buddy->resources = NULL;
+ }
+ ast_free(found);
+ found = NULL;
+ break;
+ }
+ /* resource list is sorted by descending priority */
+ if (tmp->priority != priority) {
+ found->priority = priority;
+ if (!last && !found->next)
+ /* resource was found to be unique,
+ leave loop */
+ break;
+ /* search for resource in our list
+ and take it out for the moment */
+ if (last)
+ last->next = found->next;
+ else
+ buddy->resources = found->next;
+
+ last = NULL;
+ tmp = buddy->resources;
+ if (!buddy->resources)
+ buddy->resources = found;
+ /* priority processing */
+ while (tmp) {
+ /* insert resource back according to
+ its priority value */
+ if (found->priority > tmp->priority) {
+ if (last)
+ /* insert within list */
+ last->next = found;
+ found->next = tmp;
+ if (!last)
+ /* insert on top */
+ buddy->resources = found;
+ break;
+ }
+ if (!tmp->next) {
+ /* insert at the end of the list */
+ tmp->next = found;
+ found->next = NULL;
+ break;
+ }
+ last = tmp;
+ tmp = tmp->next;
+ }
+ }
+ break;
+ }
+ last = tmp;
+ tmp = tmp->next;
+ }
+
+ /* resource not found in our list, create it */
+ if (!found && status != 6 && pak->from->resource) {
+ found = ast_calloc(1, sizeof(*found));
+
+ if (!found) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ return;
+ }
+ ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
+ found->status = status;
+ found->description = descrip;
+ found->priority = priority;
+ found->next = NULL;
+ last = NULL;
+ tmp = buddy->resources;
+ while (tmp) {
+ if (found->priority > tmp->priority) {
+ if (last)
+ last->next = found;
+ found->next = tmp;
+ if (!last)
+ buddy->resources = found;
+ break;
+ }
+ if (!tmp->next) {
+ tmp->next = found;
+ break;
+ }
+ last = tmp;
+ tmp = tmp->next;
+ }
+ if (!tmp)
+ buddy->resources = found;
+ }
+
+ ASTOBJ_UNLOCK(buddy);
+ ASTOBJ_UNREF(buddy, aji_buddy_destroy);
+
+ node = iks_find_attrib(iks_find(pak->x, "c"), "node");
+ ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
+
+ /* handle gmail client's special caps:c tag */
+ if (!node && !ver) {
+ node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
+ ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
+ }
+
+ /* retrieve capabilites of the new resource */
+ if(status !=6 && found && !found->cap) {
+ found->cap = aji_find_version(node, ver, pak);
+ if(gtalk_yuck(pak->x)) /* gtalk should do discover */
+ found->cap->jingle = 1;
+ if(found->cap->jingle && option_debug > 4) {
+ ast_debug(1,"Special case for google till they support discover.\n");
+ }
+ else {
+ iks *iq, *query;
+ iq = iks_new("iq");
+ query = iks_new("query");
+ if(query && iq) {
+ iks_insert_attrib(iq, "type", "get");
+ iks_insert_attrib(iq, "to", pak->from->full);
+ iks_insert_attrib(iq,"from", client->jid->full);
+ iks_insert_attrib(iq, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
+ iks_insert_node(iq, query);
+ ast_aji_send(client, iq);
+
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ if(query)
+ iks_delete(query);
+ if(iq)
+ iks_delete(iq);
+ }
+ }
+ switch (pak->subtype) {
+ case IKS_TYPE_AVAILABLE:
+ ast_verb(5, "JABBER: I am available ^_* %i\n", pak->subtype);
+ break;
+ case IKS_TYPE_UNAVAILABLE:
+ ast_verb(5, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
+ break;
+ default:
+ ast_verb(5, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
+ }
+ switch (pak->show) {
+ case IKS_SHOW_UNAVAILABLE:
+ ast_verb(5, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
+ break;
+ case IKS_SHOW_AVAILABLE:
+ ast_verb(5, "JABBER: type is available\n");
+ break;
+ case IKS_SHOW_CHAT:
+ ast_verb(5, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
+ break;
+ case IKS_SHOW_AWAY:
+ ast_verb(5, "JABBER: type is away\n");
+ break;
+ case IKS_SHOW_XA:
+ ast_verb(5, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
+ break;
+ case IKS_SHOW_DND:
+ ast_verb(5, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
+ break;
+ default:
+ ast_verb(5, "JABBER: Kinky! how did that happen %i\n", pak->show);
+ }
+}
+
+/*!
+ * \brief handles subscription requests.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param pak ikspak iksemel packet.
+ * \return void.
+ */
+static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
+{
+ if (pak->subtype == IKS_TYPE_SUBSCRIBE) {
+ iks *presence = NULL, *status = NULL;
+ presence = iks_new("presence");
+ status = iks_new("status");
+ if (presence && status) {
+ iks_insert_attrib(presence, "type", "subscribed");
+ iks_insert_attrib(presence, "to", pak->from->full);
+ iks_insert_attrib(presence, "from", client->jid->full);
+ if (pak->id)
+ iks_insert_attrib(presence, "id", pak->id);
+ iks_insert_cdata(status, "Asterisk has approved subscription", 0);
+ iks_insert_node(presence, status);
+ ast_aji_send(client, presence);
+ } else
+ ast_log(LOG_ERROR, "Unable to allocate nodes\n");
+ if (presence)
+ iks_delete(presence);
+ if (status)
+ iks_delete(status);
+ if (client->component)
+ aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
+ }
+
+ switch (pak->subtype) {
+ case IKS_TYPE_SUBSCRIBE:
+ ast_verb(5, "JABBER: Subscribe handled.\n");
+ break;
+ case IKS_TYPE_SUBSCRIBED:
+ ast_verb(5, "JABBER: Subscribed (%d) not handled.\n", pak->subtype);
+ break;
+ case IKS_TYPE_UNSUBSCRIBE:
+ ast_verb(5, "JABBER: Unsubscribe (%d) not handled.\n", pak->subtype);
+ break;
+ case IKS_TYPE_UNSUBSCRIBED:
+ ast_verb(5, "JABBER: Unsubscribed (%d) not handled.\n", pak->subtype);
+ break;
+ default: /*IKS_TYPE_ERROR: */
+ ast_verb(5, "JABBER: Unknown pak subtype %d.\n", pak->subtype);
+ break;
+ }
+}
+
+/*!
+ * \brief sends messages.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param address
+ * \param message
+ * \return 1.
+ */
+int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
+{
+ int res = 0;
+ iks *message_packet = NULL;
+ if (client->state == AJI_CONNECTED) {
+ message_packet = iks_make_msg(IKS_TYPE_CHAT, address, message);
+ if (message_packet) {
+ iks_insert_attrib(message_packet, "from", client->jid->full);
+ res = ast_aji_send(client, message_packet);
+ } else {
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ }
+ if (message_packet)
+ iks_delete(message_packet);
+ } else
+ ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
+ return 1;
+}
+
+/*!
+ * \brief create a chatroom.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param room name of room
+ * \param server name of server
+ * \param topic topic for the room.
+ * \return 0.
+ */
+int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
+{
+ int res = 0;
+ iks *iq = NULL;
+ iq = iks_new("iq");
+ if (iq && client) {
+ iks_insert_attrib(iq, "type", "get");
+ iks_insert_attrib(iq, "to", server);
+ iks_insert_attrib(iq, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ ast_aji_send(client, iq);
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ return res;
+}
+
+/*!
+ * \brief join a chatroom.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param room room to join
+ * \return res.
+ */
+int ast_aji_join_chat(struct aji_client *client, char *room)
+{
+ int res = 0;
+ iks *presence = NULL, *priority = NULL;
+ presence = iks_new("presence");
+ priority = iks_new("priority");
+ if (presence && priority && client) {
+ iks_insert_cdata(priority, "0", 1);
+ iks_insert_attrib(presence, "to", room);
+ iks_insert_node(presence, priority);
+ res = ast_aji_send(client, presence);
+ iks_insert_cdata(priority, "5", 1);
+ iks_insert_attrib(presence, "to", room);
+ res = ast_aji_send(client, presence);
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ if (presence)
+ iks_delete(presence);
+ if (priority)
+ iks_delete(priority);
+ return res;
+}
+
+/*!
+ * \brief invite to a chatroom.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param user
+ * \param room
+ * \param message
+ * \return res.
+ */
+int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
+{
+ int res = 0;
+ iks *invite, *body, *namespace;
+
+ invite = iks_new("message");
+ body = iks_new("body");
+ namespace = iks_new("x");
+ if (client && invite && body && namespace) {
+ iks_insert_attrib(invite, "to", user);
+ iks_insert_attrib(invite, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ iks_insert_cdata(body, message, 0);
+ iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
+ iks_insert_attrib(namespace, "jid", room);
+ iks_insert_node(invite, body);
+ iks_insert_node(invite, namespace);
+ res = ast_aji_send(client, invite);
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ if (body)
+ iks_delete(body);
+ if (namespace)
+ iks_delete(namespace);
+ if (invite)
+ iks_delete(invite);
+ return res;
+}
+
+
+/*!
+ * \brief receive message loop.
+ * \param data void
+ * \return void.
+ */
+static void *aji_recv_loop(void *data)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ int res = IKS_HOOK;
+
+ while(res != IKS_OK) {
+ if(option_verbose > 3)
+ ast_verbose("JABBER: Connecting.\n");
+ res = aji_reconnect(client);
+ sleep(4);
+ }
+
+ do {
+ if (res == IKS_NET_RWERR || client->timeout == 0) {
+ while(res != IKS_OK) {
+ ast_verb(4, "JABBER: reconnecting.\n");
+ res = aji_reconnect(client);
+ sleep(4);
+ }
+ }
+
+ res = aji_recv(client, 1);
+
+ if (client->state == AJI_DISCONNECTING) {
+ ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
+ pthread_exit(NULL);
+ }
+
+ /* Decrease timeout if no data received */
+ if (res == IKS_NET_EXPIRED)
+ client->timeout--;
+
+ if (res == IKS_HOOK)
+ ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
+ else if (res == IKS_NET_TLSFAIL)
+ ast_log(LOG_WARNING, "JABBER: Failure in TLS.\n");
+ else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
+ res = aji_send_raw(client, " ");
+ if(res == IKS_OK)
+ client->timeout = 50;
+ else
+ ast_log(LOG_WARNING, "JABBER: Network Timeout\n");
+ } else if (res == IKS_NET_RWERR)
+ ast_log(LOG_WARNING, "JABBER: socket read error\n");
+ } while (client);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return 0;
+}
+
+/*!
+ * \brief increments the mid field for messages and other events.
+ * \param mid char.
+ * \return void.
+ */
+void ast_aji_increment_mid(char *mid)
+{
+ int i = 0;
+
+ for (i = strlen(mid) - 1; i >= 0; i--) {
+ if (mid[i] != 'z') {
+ mid[i] = mid[i] + 1;
+ i = 0;
+ } else
+ mid[i] = 'a';
+ }
+}
+
+#if 0
+/*!
+ * \brief attempts to register to a transport.
+ * \param aji_client struct, and xml packet.
+ * \return IKS_FILTER_EAT.
+ */
+/*allows for registering to transport , was too sketch and is out for now. */
+static int aji_register_transport(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ int res = 0;
+ struct aji_buddy *buddy = NULL;
+ iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
+
+ if (client && send) {
+ ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->btype == AJI_TRANS) {
+ buddy = iterator;
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+ iks_filter_remove_hook(client->f, aji_register_transport);
+ iks_filter_add_rule(client->f, aji_register_transport2, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, IKS_NS_REGISTER, IKS_RULE_DONE);
+ iks_insert_attrib(send, "to", buddy->host);
+ iks_insert_attrib(send, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ iks_insert_attrib(send, "from", client->user);
+ res = ast_aji_send(client, send);
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+
+ if (send)
+ iks_delete(send);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+
+}
+/*!
+ * \brief attempts to register to a transport step 2.
+ * \param aji_client struct, and xml packet.
+ * \return IKS_FILTER_EAT.
+ */
+/* more of the same blob of code, too wonky for now*/
+static int aji_register_transport2(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ int res = 0;
+ struct aji_buddy *buddy = NULL;
+
+ iks *regiq = iks_new("iq");
+ iks *regquery = iks_new("query");
+ iks *reguser = iks_new("username");
+ iks *regpass = iks_new("password");
+
+ if (client && regquery && reguser && regpass && regiq) {
+ ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->btype == AJI_TRANS)
+ buddy = iterator; ASTOBJ_UNLOCK(iterator);
+ });
+ iks_filter_remove_hook(client->f, aji_register_transport2);
+ iks_insert_attrib(regiq, "to", buddy->host);
+ iks_insert_attrib(regiq, "type", "set");
+ iks_insert_attrib(regiq, "id", client->mid);
+ ast_aji_increment_mid(client->mid);
+ iks_insert_attrib(regiq, "from", client->user);
+ iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
+ iks_insert_cdata(reguser, buddy->user, 0);
+ iks_insert_cdata(regpass, buddy->pass, 0);
+ iks_insert_node(regiq, regquery);
+ iks_insert_node(regquery, reguser);
+ iks_insert_node(regquery, regpass);
+ res = ast_aji_send(client, regiq);
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ if (regiq)
+ iks_delete(regiq);
+ if (regquery)
+ iks_delete(regquery);
+ if (reguser)
+ iks_delete(reguser);
+ if (regpass)
+ iks_delete(regpass);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+}
+#endif
+
+/*!
+ * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return void.
+ */
+static void aji_pruneregister(struct aji_client *client)
+{
+ int res = 0;
+ iks *removeiq = iks_new("iq");
+ iks *removequery = iks_new("query");
+ iks *removeitem = iks_new("item");
+ iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
+
+ if (client && removeiq && removequery && removeitem && send) {
+ iks_insert_node(removeiq, removequery);
+ iks_insert_node(removequery, removeitem);
+ ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
+ * be called at the same time */
+ if (ast_test_flag(iterator, AJI_AUTOPRUNE)) {
+ res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
+ "GoodBye your status is no longer needed by Asterisk the Open Source PBX"
+ " so I am no longer subscribing to your presence.\n"));
+ res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
+ "GoodBye you are no longer in the asterisk config file so I am removing"
+ " your access to my presence.\n"));
+ iks_insert_attrib(removeiq, "from", client->jid->full);
+ iks_insert_attrib(removeiq, "type", "set");
+ iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
+ iks_insert_attrib(removeitem, "jid", iterator->name);
+ iks_insert_attrib(removeitem, "subscription", "remove");
+ res = ast_aji_send(client, removeiq);
+ } else if (ast_test_flag(iterator, AJI_AUTOREGISTER)) {
+ res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name,
+ "Greetings I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
+ ast_clear_flag(iterator, AJI_AUTOREGISTER);
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ if (removeiq)
+ iks_delete(removeiq);
+ if (removequery)
+ iks_delete(removequery);
+ if (removeitem)
+ iks_delete(removeitem);
+ if (send)
+ iks_delete(send);
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, aji_buddy_destroy);
+}
+
+/*!
+ * \brief filters the roster packet we get back from server.
+ * \param data void
+ * \param pak ikspak iksemel packet.
+ * \return IKS_FILTER_EAT.
+ */
+static int aji_filter_roster(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ int flag = 0;
+ iks *x = NULL;
+ struct aji_buddy *buddy;
+
+ client->state = AJI_CONNECTED;
+ ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ x = iks_child(pak->query);
+ flag = 0;
+ while (x) {
+ if (!iks_strcmp(iks_name(x), "item")) {
+ if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
+ flag = 1;
+ ast_clear_flag(iterator, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
+ }
+ }
+ x = iks_next(x);
+ }
+ if (!flag)
+ ast_copy_flags(iterator, client, AJI_AUTOREGISTER);
+ if (x)
+ iks_delete(x);
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ x = iks_child(pak->query);
+ while (x) {
+ flag = 0;
+ if (iks_strcmp(iks_name(x), "item") == 0) {
+ ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
+ flag = 1;
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ if (!flag) {
+ buddy = ast_calloc(1, sizeof(*buddy));
+ if (!buddy) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return 0;
+ }
+ ASTOBJ_INIT(buddy);
+ ASTOBJ_WRLOCK(buddy);
+ ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
+ ast_clear_flag(buddy, AST_FLAGS_ALL);
+ if(ast_test_flag(client, AJI_AUTOPRUNE)) {
+ ast_set_flag(buddy, AJI_AUTOPRUNE);
+ buddy->objflags |= ASTOBJ_FLAG_MARKED;
+ } else
+ ast_set_flag(buddy, AJI_AUTOREGISTER);
+ ASTOBJ_UNLOCK(buddy);
+ if (buddy) {
+ ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
+ ASTOBJ_UNREF(buddy, aji_buddy_destroy);
+ }
+ }
+ }
+ x = iks_next(x);
+ }
+ if (x)
+ iks_delete(x);
+ aji_pruneregister(client);
+
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return IKS_FILTER_EAT;
+}
+/*!
+ * \brief reconnect to jabber server
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return res.
+*/
+static int aji_reconnect(struct aji_client *client)
+{
+ int res = 0;
+
+ if (client->state)
+ client->state = AJI_DISCONNECTED;
+ client->timeout=50;
+ if (client->p)
+ iks_parser_reset(client->p);
+ if (client->authorized)
+ client->authorized = 0;
+
+ res = aji_initialize(client);
+
+ return res;
+}
+/*!
+ * \brief Get the roster of jabber users
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return 1.
+*/
+static int aji_get_roster(struct aji_client *client)
+{
+ iks *roster = NULL;
+ roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
+ if(roster) {
+ iks_insert_attrib(roster, "id", "roster");
+ aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
+ ast_aji_send(client, roster);
+ }
+ if (roster)
+ iks_delete(roster);
+ return 1;
+}
+
+/*!
+ * \brief connects as a client to jabber server.
+ * \param data void
+ * \param pak ikspak iksemel packet
+ * \return res.
+ */
+static int aji_client_connect(void *data, ikspak *pak)
+{
+ struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
+ int res = 0;
+
+ if (client) {
+ if (client->state == AJI_DISCONNECTED) {
+ iks_filter_add_rule(client->f, aji_filter_roster, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "roster", IKS_RULE_DONE);
+ client->state = AJI_CONNECTING;
+ client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
+ iks_filter_remove_hook(client->f, aji_client_connect);
+ if(!client->component) /*client*/
+ aji_get_roster(client);
+ }
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return res;
+}
+
+/*!
+ * \brief prepares client for connect.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return 1.
+ */
+static int aji_initialize(struct aji_client *client)
+{
+ int connected = IKS_NET_NOCONN;
+
+ /* reset stream flags */
+ client->stream_flags = 0;
+
+ /* If it's a component, connect to user, otherwise, connect to server */
+ connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
+
+ if (connected == IKS_NET_NOCONN) {
+ ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
+ return IKS_HOOK;
+ } else if (connected == IKS_NET_NODNS) {
+ ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name, S_OR(client->serverhost, client->jid->server));
+ return IKS_HOOK;
+ }
+
+ return IKS_OK;
+}
+
+/*!
+ * \brief disconnect from jabber server.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return 1.
+ */
+int ast_aji_disconnect(struct aji_client *client)
+{
+ if (client) {
+ ast_verb(4, "JABBER: Disconnecting\n");
+#ifdef HAVE_OPENSSL
+ if (client->stream_flags & SECURE) {
+ SSL_shutdown(client->ssl_session);
+ SSL_CTX_free(client->ssl_context);
+ SSL_free(client->ssl_session);
+ }
+#endif
+ iks_disconnect(client->p);
+ iks_parser_delete(client->p);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ }
+
+ return 1;
+}
+
+/*!
+ * \brief set presence of client.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param to user send it to
+ * \param from user it came from
+ * \param level
+ * \param desc
+ * \return void.
+ */
+static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc)
+{
+ int res = 0;
+ iks *presence = iks_make_pres(level, desc);
+ iks *cnode = iks_new("c");
+ iks *priority = iks_new("priority");
+ char priorityS[10];
+
+ if (presence && cnode && client && priority) {
+ if(to)
+ iks_insert_attrib(presence, "to", to);
+ if(from)
+ iks_insert_attrib(presence, "from", from);
+ snprintf(priorityS, sizeof(priorityS), "%d", client->priority);
+ iks_insert_cdata(priority, priorityS, strlen(priorityS));
+ iks_insert_node(presence, priority);
+ iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
+ iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
+ iks_insert_attrib(cnode, "ext", "voice-v1");
+ iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
+ iks_insert_node(presence, cnode);
+ res = ast_aji_send(client, presence);
+ } else
+ ast_log(LOG_ERROR, "Out of memory.\n");
+ if (cnode)
+ iks_delete(cnode);
+ if (presence)
+ iks_delete(presence);
+ if (priority)
+ iks_delete(priority);
+}
+
+/*!
+ * \brief Turn on console debugging.
+ * \return CLI_SUCCESS.
+ */
+static char *aji_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jabber debug";
+ e->usage =
+ "Usage: jabber debug\n"
+ " Enables dumping of Jabber packets for debugging purposes.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ iterator->debug = 1;
+ ASTOBJ_UNLOCK(iterator);
+ });
+ ast_cli(a->fd, "Jabber Debugging Enabled.\n");
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Reload jabber module.
+ * \return CLI_SUCCESS.
+ */
+static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jabber reload";
+ e->usage =
+ "Usage: jabber reload\n"
+ " Reloads the Jabber module.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ aji_reload(1);
+ ast_cli(a->fd, "Jabber Reloaded.\n");
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Turn off console debugging.
+ * \return CLI_SUCCESS.
+ */
+static char *aji_no_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jabber debug off";
+ e->usage =
+ "Usage: jabber debug off\n"
+ " Disables dumping of Jabber packets for debugging purposes.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ iterator->debug = 0;
+ ASTOBJ_UNLOCK(iterator);
+ });
+ ast_cli(a->fd, "Jabber Debugging Disabled.\n");
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Show client status.
+ * \return CLI_SUCCESS.
+ */
+static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *status;
+ int count = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jabber show connected";
+ e->usage =
+ "Usage: jabber show connected\n"
+ " Shows state of clients and components\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "Jabber Users and their status:\n");
+ ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ count++;
+ switch (iterator->state) {
+ case AJI_DISCONNECTED:
+ status = "Disconnected";
+ break;
+ case AJI_CONNECTING:
+ status = "Connecting";
+ break;
+ case AJI_CONNECTED:
+ status = "Connected";
+ break;
+ default:
+ status = "Unknown";
+ }
+ ast_cli(a->fd, " User: %s - %s\n", iterator->user, status);
+ ASTOBJ_UNLOCK(iterator);
+ });
+ ast_cli(a->fd, "----\n");
+ ast_cli(a->fd, " Number of users: %d\n", count);
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Show buddy lists
+ * \return CLI_SUCCESS.
+ */
+static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct aji_resource *resource;
+ struct aji_client *client;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jabber show buddies";
+ e->usage =
+ "Usage: jabber show buddies\n"
+ " Shows buddy lists of our clients\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "Jabber buddy lists\n");
+ ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
+ ast_cli(a->fd,"Client: %s\n", iterator->user);
+ client = iterator;
+ ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ ast_cli(a->fd,"\tBuddy:\t%s\n", iterator->name);
+ if (!iterator->resources)
+ ast_cli(a->fd,"\t\tResource: None\n");
+ for (resource = iterator->resources; resource; resource = resource->next) {
+ ast_cli(a->fd,"\t\tResource: %s\n", resource->resource);
+ if(resource->cap) {
+ ast_cli(a->fd,"\t\t\tnode: %s\n", resource->cap->parent->node);
+ ast_cli(a->fd,"\t\t\tversion: %s\n", resource->cap->version);
+ ast_cli(a->fd,"\t\t\tJingle capable: %s\n", resource->cap->jingle ? "yes" : "no");
+ }
+ ast_cli(a->fd,"\t\tStatus: %d\n", resource->status);
+ ast_cli(a->fd,"\t\tPriority: %d\n", resource->priority);
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+ iterator = client;
+ });
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief Send test message for debugging.
+ * \return CLI_SUCCESS,CLI_FAILURE.
+ */
+static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct aji_client *client;
+ struct aji_resource *resource;
+ const char *name = "asterisk";
+ struct aji_message *tmp;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "jabber test";
+ e->usage =
+ "Usage: jabber test [client]\n"
+ " Sends test message for debugging purposes. A specific client\n"
+ " as configured in jabber.conf can be optionally specified.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 3)
+ return CLI_SHOWUSAGE;
+ else if (a->argc == 3)
+ name = a->argv[2];
+
+ if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
+ ast_cli(a->fd, "Unable to find client '%s'!\n", name);
+ return CLI_FAILURE;
+ }
+
+ /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
+ ast_aji_send_chat(client, "mogorman@astjab.org", "blahblah");
+ ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ ast_verbose("User: %s\n", iterator->name);
+ for (resource = iterator->resources; resource; resource = resource->next) {
+ ast_verbose("Resource: %s\n", resource->resource);
+ if(resource->cap) {
+ ast_verbose(" client: %s\n", resource->cap->parent->node);
+ ast_verbose(" version: %s\n", resource->cap->version);
+ ast_verbose(" Jingle Capable: %d\n", resource->cap->jingle);
+ }
+ ast_verbose(" Priority: %d\n", resource->priority);
+ ast_verbose(" Status: %d\n", resource->status);
+ ast_verbose(" Message: %s\n", S_OR(resource->description,""));
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+ ast_verbose("\nOooh a working message stack!\n");
+ AST_LIST_LOCK(&client->messages);
+ AST_LIST_TRAVERSE(&client->messages, tmp, list) {
+ ast_verbose(" Message from: %s with id %s @ %s %s\n",tmp->from, S_OR(tmp->id,""), ctime(&tmp->arrived), S_OR(tmp->message, ""));
+ }
+ AST_LIST_UNLOCK(&client->messages);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \brief creates aji_client structure.
+ * \param label
+ * \param var ast_variable
+ * \param debug
+ * \return 0.
+ */
+static int aji_create_client(char *label, struct ast_variable *var, int debug)
+{
+ char *resource;
+ struct aji_client *client = NULL;
+ int flag = 0;
+
+ client = ASTOBJ_CONTAINER_FIND(&clients,label);
+ if (!client) {
+ flag = 1;
+ client = ast_calloc(1, sizeof(*client));
+ if (!client) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ return 0;
+ }
+ ASTOBJ_INIT(client);
+ ASTOBJ_WRLOCK(client);
+ ASTOBJ_CONTAINER_INIT(&client->buddies);
+ } else {
+ ASTOBJ_WRLOCK(client);
+ ASTOBJ_UNMARK(client);
+ }
+ ASTOBJ_CONTAINER_MARKALL(&client->buddies);
+ ast_copy_string(client->name, label, sizeof(client->name));
+ ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
+
+ /* Set default values for the client object */
+ client->debug = debug;
+ ast_copy_flags(client, &globalflags, AST_FLAGS_ALL);
+ client->port = 5222;
+ client->usetls = 1;
+ client->usesasl = 1;
+ client->forcessl = 0;
+ client->keepalive = 1;
+ client->timeout = 50;
+ client->message_timeout = 100;
+ AST_LIST_HEAD_INIT(&client->messages);
+ client->component = 0;
+ ast_copy_string(client->statusmessage, "Online and Available", sizeof(client->statusmessage));
+ client->priority = 0;
+ client->status = IKS_SHOW_AVAILABLE;
+
+ if (flag) {
+ client->authorized = 0;
+ client->state = AJI_DISCONNECTED;
+ }
+ while (var) {
+ if (!strcasecmp(var->name, "username"))
+ ast_copy_string(client->user, var->value, sizeof(client->user));
+ else if (!strcasecmp(var->name, "serverhost"))
+ ast_copy_string(client->serverhost, var->value, sizeof(client->serverhost));
+ else if (!strcasecmp(var->name, "secret"))
+ ast_copy_string(client->password, var->value, sizeof(client->password));
+ else if (!strcasecmp(var->name, "statusmessage"))
+ ast_copy_string(client->statusmessage, var->value, sizeof(client->statusmessage));
+ else if (!strcasecmp(var->name, "port"))
+ client->port = atoi(var->value);
+ else if (!strcasecmp(var->name, "timeout"))
+ client->message_timeout = atoi(var->value);
+ else if (!strcasecmp(var->name, "debug"))
+ client->debug = (ast_false(var->value)) ? 0 : 1;
+ else if (!strcasecmp(var->name, "type")) {
+ if (!strcasecmp(var->value, "component"))
+ client->component = 1;
+ } else if (!strcasecmp(var->name, "usetls")) {
+ client->usetls = (ast_false(var->value)) ? 0 : 1;
+ } else if (!strcasecmp(var->name, "usesasl")) {
+ client->usesasl = (ast_false(var->value)) ? 0 : 1;
+ } else if (!strcasecmp(var->name, "forceoldssl"))
+ client->forcessl = (ast_false(var->value)) ? 0 : 1;
+ else if (!strcasecmp(var->name, "keepalive"))
+ client->keepalive = (ast_false(var->value)) ? 0 : 1;
+ else if (!strcasecmp(var->name, "autoprune"))
+ ast_set2_flag(client, ast_true(var->value), AJI_AUTOPRUNE);
+ else if (!strcasecmp(var->name, "autoregister"))
+ ast_set2_flag(client, ast_true(var->value), AJI_AUTOREGISTER);
+ else if (!strcasecmp(var->name, "buddy"))
+ aji_create_buddy((char *)var->value, client);
+ else if (!strcasecmp(var->name, "priority"))
+ client->priority = atoi(var->value);
+ else if (!strcasecmp(var->name, "status")) {
+ if (!strcasecmp(var->value, "unavailable"))
+ client->status = IKS_SHOW_UNAVAILABLE;
+ else
+ if (!strcasecmp(var->value, "available")
+ || !strcasecmp(var->value, "online"))
+ client->status = IKS_SHOW_AVAILABLE;
+ else
+ if (!strcasecmp(var->value, "chat")
+ || !strcasecmp(var->value, "chatty"))
+ client->status = IKS_SHOW_CHAT;
+ else
+ if (!strcasecmp(var->value, "away"))
+ client->status = IKS_SHOW_AWAY;
+ else
+ if (!strcasecmp(var->value, "xa")
+ || !strcasecmp(var->value, "xaway"))
+ client->status = IKS_SHOW_XA;
+ else
+ if (!strcasecmp(var->value, "dnd"))
+ client->status = IKS_SHOW_DND;
+ else
+ if (!strcasecmp(var->value, "invisible"))
+ #ifdef IKS_SHOW_INVISIBLE
+ client->status = IKS_SHOW_INVISIBLE;
+ #else
+ {
+ ast_log(LOG_WARNING, "Your iksemel doesn't support invisible status: falling back to DND\n");
+ client->status = IKS_SHOW_DND;
+ }
+ #endif
+ else
+ ast_log(LOG_WARNING, "Unknown presence status: %s\n", var->value);
+ }
+ /* no transport support in this version */
+ /* else if (!strcasecmp(var->name, "transport"))
+ aji_create_transport(var->value, client);
+ */
+ var = var->next;
+ }
+ if (!flag) {
+ ASTOBJ_UNLOCK(client);
+ ASTOBJ_UNREF(client, aji_client_destroy);
+ return 1;
+ }
+
+ ast_copy_string(client->name_space, (client->component) ? "jabber:component:accept" : "jabber:client", sizeof(client->name_space));
+ client->p = iks_stream_new(client->name_space, client, aji_act_hook);
+ if (!client->p) {
+ ast_log(LOG_ERROR, "Failed to create stream for client '%s'!\n", client->name);
+ return 0;
+ }
+ client->stack = iks_stack_new(8192, 8192);
+ if (!client->stack) {
+ ast_log(LOG_ERROR, "Failed to allocate stack for client '%s'\n", client->name);
+ return 0;
+ }
+ client->f = iks_filter_new();
+ if (!client->f) {
+ ast_log(LOG_ERROR, "Failed to create filter for client '%s'\n", client->name);
+ return 0;
+ }
+ if (!strchr(client->user, '/') && !client->component) { /*client */
+ resource = NULL;
+ asprintf(&resource, "%s/asterisk", client->user);
+ if (resource) {
+ client->jid = iks_id_new(client->stack, resource);
+ ast_free(resource);
+ }
+ } else
+ client->jid = iks_id_new(client->stack, client->user);
+ if (client->component) {
+ iks_filter_add_rule(client->f, aji_dinfo_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
+ iks_filter_add_rule(client->f, aji_ditems_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE);
+ iks_filter_add_rule(client->f, aji_register_query_handler, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
+ iks_filter_add_rule(client->f, aji_register_approve_handler, client, IKS_RULE_SUBTYPE, IKS_TYPE_SET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
+ } else {
+ iks_filter_add_rule(client->f, aji_client_info_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
+ }
+ if (!strchr(client->user, '/') && !client->component) { /*client */
+ resource = NULL;
+ asprintf(&resource, "%s/asterisk", client->user);
+ if (resource) {
+ client->jid = iks_id_new(client->stack, resource);
+ ast_free(resource);
+ }
+ } else
+ client->jid = iks_id_new(client->stack, client->user);
+ iks_set_log_hook(client->p, aji_log_hook);
+ ASTOBJ_UNLOCK(client);
+ ASTOBJ_CONTAINER_LINK(&clients,client);
+ return 1;
+}
+
+#if 0
+/*!
+ * \brief creates transport.
+ * \param label, buddy to dump it into.
+ * \return 0.
+ */
+/* no connecting to transports today */
+static int aji_create_transport(char *label, struct aji_client *client)
+{
+ char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
+ struct aji_buddy *buddy = NULL;
+
+ buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
+ if (!buddy) {
+ buddy = ast_calloc(1, sizeof(*buddy));
+ if(!buddy) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return 0;
+ }
+ ASTOBJ_INIT(buddy);
+ }
+ ASTOBJ_WRLOCK(buddy);
+ server = label;
+ if ((buddyname = strchr(label, ','))) {
+ *buddyname = '\0';
+ buddyname++;
+ if (buddyname && buddyname[0] != '\0') {
+ if ((user = strchr(buddyname, ','))) {
+ *user = '\0';
+ user++;
+ if (user && user[0] != '\0') {
+ if ((pass = strchr(user, ','))) {
+ *pass = '\0';
+ pass++;
+ ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
+ ast_copy_string(buddy->user, user, sizeof(buddy->user));
+ ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
+ ast_copy_string(buddy->server, server, sizeof(buddy->server));
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ ASTOBJ_UNLOCK(buddy);
+ ASTOBJ_UNMARK(buddy);
+ ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
+ return 0;
+}
+#endif
+
+/*!
+ * \brief creates buddy.
+ * \param label char.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return 1 on success, 0 on failure.
+ */
+static int aji_create_buddy(char *label, struct aji_client *client)
+{
+ struct aji_buddy *buddy = NULL;
+ int flag = 0;
+ buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
+ if (!buddy) {
+ flag = 1;
+ buddy = ast_calloc(1, sizeof(*buddy));
+ if(!buddy) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return 0;
+ }
+ ASTOBJ_INIT(buddy);
+ }
+ ASTOBJ_WRLOCK(buddy);
+ ast_copy_string(buddy->name, label, sizeof(buddy->name));
+ ASTOBJ_UNLOCK(buddy);
+ if(flag)
+ ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
+ else {
+ ASTOBJ_UNMARK(buddy);
+ ASTOBJ_UNREF(buddy, aji_buddy_destroy);
+ }
+ return 1;
+}
+
+/*!< load config file. \return 1. */
+static int aji_load_config(int reload)
+{
+ char *cat = NULL;
+ int debug = 1;
+ struct ast_config *cfg = NULL;
+ struct ast_variable *var = NULL;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load(JABBER_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return -1;
+
+ /* Reset flags to default value */
+ ast_set_flag(&globalflags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
+
+ if (!cfg) {
+ ast_log(LOG_WARNING, "No such configuration file %s\n", JABBER_CONFIG);
+ return 0;
+ }
+
+ cat = ast_category_browse(cfg, NULL);
+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+ if (!strcasecmp(var->name, "debug"))
+ debug = (ast_false(ast_variable_retrieve(cfg, "general", "debug"))) ? 0 : 1;
+ else if (!strcasecmp(var->name, "autoprune"))
+ ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOPRUNE);
+ else if (!strcasecmp(var->name, "autoregister"))
+ ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOREGISTER);
+ }
+
+ while (cat) {
+ if (strcasecmp(cat, "general")) {
+ var = ast_variable_browse(cfg, cat);
+ aji_create_client(cat, var, debug);
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_config_destroy(cfg); /* or leak memory */
+ return 1;
+}
+
+/*!
+ * \brief grab a aji_client structure by label name.
+ * \param name label name
+ * \return aji_client.
+ */
+struct aji_client *ast_aji_get_client(const char *name)
+{
+ struct aji_client *client = NULL;
+
+ client = ASTOBJ_CONTAINER_FIND(&clients, name);
+ if (!client && !strchr(name, '@'))
+ client = ASTOBJ_CONTAINER_FIND_FULL(&clients, name, user,,, strcasecmp);
+ return client;
+}
+
+struct aji_client_container *ast_aji_get_clients(void)
+{
+ return &clients;
+}
+
+static char mandescr_jabber_send[] =
+"Description: Sends a message to a Jabber Client.\n"
+"Variables: \n"
+" Jabber: Client or transport Asterisk uses to connect to JABBER.\n"
+" ScreenName: User Name to message.\n"
+" Message: Message to be sent to the buddy\n";
+
+/*!
+ * \brief Send a Jabber Message via call from the Manager
+ * \param s mansession Manager session
+ * \param m message Message to send
+ * \return 0
+*/
+static int manager_jabber_send(struct mansession *s, const struct message *m)
+{
+ struct aji_client *client = NULL;
+ const char *id = astman_get_header(m,"ActionID");
+ const char *jabber = astman_get_header(m,"Jabber");
+ const char *screenname = astman_get_header(m,"ScreenName");
+ const char *message = astman_get_header(m,"Message");
+
+ if (ast_strlen_zero(jabber)) {
+ astman_send_error(s, m, "No transport specified");
+ return 0;
+ }
+ if (ast_strlen_zero(screenname)) {
+ astman_send_error(s, m, "No ScreenName specified");
+ return 0;
+ }
+ if (ast_strlen_zero(message)) {
+ astman_send_error(s, m, "No Message specified");
+ return 0;
+ }
+
+ astman_send_ack(s, m, "Attempting to send Jabber Message");
+ client = ast_aji_get_client(jabber);
+ if (!client) {
+ astman_send_error(s, m, "Could not find Sender");
+ return 0;
+ }
+ if (strchr(screenname, '@') && message){
+ ast_aji_send_chat(client, screenname, message);
+ astman_append(s, "Response: Success\r\n");
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n",id);
+ return 0;
+ }
+ astman_append(s, "Response: Error\r\n");
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n",id);
+ return 0;
+}
+
+/*! \brief Reload the jabber module */
+static int aji_reload(int reload)
+{
+ int res;
+
+ ASTOBJ_CONTAINER_MARKALL(&clients);
+ if (!(res = aji_load_config(reload))) {
+ ast_log(LOG_ERROR, "JABBER: Failed to load config.\n");
+ return 0;
+ } else if (res == -1)
+ return 1;
+
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&clients, aji_client_destroy);
+ ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ if(iterator->state == AJI_DISCONNECTED) {
+ if (!iterator->thread)
+ ast_pthread_create_background(&iterator->thread, NULL, aji_recv_loop, iterator);
+ } else if (iterator->state == AJI_CONNECTING)
+ aji_get_roster(iterator);
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ return 1;
+}
+
+/*! \brief Unload the jabber module */
+static int unload_module(void)
+{
+
+ ast_cli_unregister_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry));
+ ast_unregister_application(app_ajisend);
+ ast_unregister_application(app_ajistatus);
+ ast_manager_unregister("JabberSend");
+ ast_custom_function_unregister(&jabberstatus_function);
+
+ ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ ast_debug(3, "JABBER: Releasing and disconnecting client: %s\n", iterator->name);
+ iterator->state = AJI_DISCONNECTING;
+ ast_aji_disconnect(iterator);
+ pthread_join(iterator->thread, NULL);
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ ASTOBJ_CONTAINER_DESTROYALL(&clients, aji_client_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&clients);
+ return 0;
+}
+
+/*! \brief Unload the jabber module */
+static int load_module(void)
+{
+ ASTOBJ_CONTAINER_INIT(&clients);
+ if(!aji_reload(0))
+ return AST_MODULE_LOAD_DECLINE;
+ ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM, manager_jabber_send,
+ "Sends a message to a Jabber Client", mandescr_jabber_send);
+ ast_register_application(app_ajisend, aji_send_exec, ajisend_synopsis, ajisend_descrip);
+ ast_register_application(app_ajistatus, aji_status_exec, ajistatus_synopsis, ajistatus_descrip);
+ ast_cli_register_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry));
+ ast_custom_function_register(&jabberstatus_function);
+
+ return 0;
+}
+
+/*! \brief Wrapper for aji_reload */
+static int reload(void)
+{
+ aji_reload(1);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "AJI - Asterisk Jabber Interface",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_limit.c b/trunk/res/res_limit.c
new file mode 100644
index 000000000..28a5e1323
--- /dev/null
+++ b/trunk/res/res_limit.c
@@ -0,0 +1,216 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Resource limits
+ *
+ * Copyright (c) 2006 Tilghman Lesher. All rights reserved.
+ *
+ * Tilghman Lesher <res_limit_200607@the-tilghman.com>
+ *
+ * This code is released by the author with no restrictions on usage.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Resource limits
+ *
+ * \author Tilghman Lesher <res_limit_200607@the-tilghman.com>
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#define _XOPEN_SOURCE 600
+
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+
+/* Find proper rlimit for virtual memory */
+#ifdef RLIMIT_AS
+#define VMEM_DEF RLIMIT_AS
+#else
+#ifdef RLIMIT_VMEM
+#define VMEM_DEF RLIMIT_VMEM
+#endif
+#endif
+
+static struct limits {
+ int resource;
+ char limit[3];
+ char desc[40];
+} limits[] = {
+ { RLIMIT_CPU, "-t", "cpu time" },
+ { RLIMIT_FSIZE, "-f", "file size" },
+ { RLIMIT_DATA, "-d", "program data segment" },
+ { RLIMIT_STACK, "-s", "program stack size" },
+ { RLIMIT_CORE, "-c", "core file size" },
+#ifdef RLIMIT_RSS
+ { RLIMIT_RSS, "-m", "resident memory" },
+ { RLIMIT_MEMLOCK, "-l", "amount of memory locked into RAM" },
+#endif
+#ifdef RLIMIT_NPROC
+ { RLIMIT_NPROC, "-u", "number of processes" },
+#endif
+ { RLIMIT_NOFILE, "-n", "number of file descriptors" },
+#ifdef VMEM_DEF
+ { VMEM_DEF, "-v", "virtual memory" },
+#endif
+};
+
+static int str2limit(const char *string)
+{
+ size_t i;
+ for (i = 0; i < sizeof(limits) / sizeof(limits[0]); i++) {
+ if (!strcasecmp(string, limits[i].limit))
+ return limits[i].resource;
+ }
+ return -1;
+}
+
+static const char *str2desc(const char *string)
+{
+ size_t i;
+ for (i = 0; i < sizeof(limits) / sizeof(limits[0]); i++) {
+ if (!strcmp(string, limits[i].limit))
+ return limits[i].desc;
+ }
+ return "<unknown>";
+}
+
+static char *complete_ulimit(struct ast_cli_args *a)
+{
+ int which = 0, i;
+ int wordlen = strlen(a->word);
+
+ if (a->pos > 1)
+ return NULL;
+ for (i = 0; i < sizeof(limits) / sizeof(limits[0]); i++) {
+ if (!strncasecmp(limits[i].limit, a->word, wordlen)) {
+ if (++which > a->n)
+ return ast_strdup(limits[i].limit);
+ }
+ }
+ return NULL;
+}
+
+static char *handle_cli_ulimit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int resource;
+ struct rlimit rlimit = { 0, 0 };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ulimit";
+ e->usage =
+ "Usage: ulimit {-d|"
+#ifdef RLIMIT_RSS
+ "-l|"
+#endif
+ "-f|"
+#ifdef RLIMIT_RSS
+ "-m|"
+#endif
+ "-s|-t|"
+#ifdef RLIMIT_NPROC
+ "-u|"
+#endif
+#ifdef VMEM_DEF
+ "-v|"
+#endif
+ "-c|-n} [<num>]\n"
+ " Shows or sets the corresponding resource limit.\n"
+ " -d Process data segment [readonly]\n"
+#ifdef RLIMIT_RSS
+ " -l Memory lock size [readonly]\n"
+#endif
+ " -f File size\n"
+#ifdef RLIMIT_RSS
+ " -m Process resident memory [readonly]\n"
+#endif
+ " -s Process stack size [readonly]\n"
+ " -t CPU usage [readonly]\n"
+#ifdef RLIMIT_NPROC
+ " -u Child processes\n"
+#endif
+#ifdef VMEM_DEF
+ " -v Process virtual memory [readonly]\n"
+#endif
+ " -c Core dump file size\n"
+ " -n Number of file descriptors\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_ulimit(a);
+ }
+
+ if (a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 1) {
+ char arg2[3];
+ char *newargv[2] = { "ulimit", arg2 };
+ for (resource = 0; resource < sizeof(limits) / sizeof(limits[0]); resource++) {
+ struct ast_cli_args newArgs = { .argv = newargv, .argc = 2 };
+ ast_copy_string(arg2, limits[resource].limit, sizeof(arg2));
+ handle_cli_ulimit(e, CLI_HANDLER, &newArgs);
+ }
+ return CLI_SUCCESS;
+ } else {
+ resource = str2limit(a->argv[1]);
+ if (resource == -1) {
+ ast_cli(a->fd, "Unknown resource\n");
+ return CLI_FAILURE;
+ }
+
+ if (a->argc == 3) {
+ int x;
+#ifdef RLIMIT_NPROC
+ if (resource != RLIMIT_NOFILE && resource != RLIMIT_CORE && resource != RLIMIT_NPROC && resource != RLIMIT_FSIZE) {
+#else
+ if (resource != RLIMIT_NOFILE && resource != RLIMIT_CORE && resource != RLIMIT_FSIZE) {
+#endif
+ ast_cli(a->fd, "Resource not permitted to be set\n");
+ return CLI_FAILURE;
+ }
+
+ sscanf(a->argv[2], "%d", &x);
+ rlimit.rlim_max = rlimit.rlim_cur = x;
+ setrlimit(resource, &rlimit);
+ return CLI_SUCCESS;
+ } else {
+ if (!getrlimit(resource, &rlimit)) {
+ char printlimit[32];
+ const char *desc;
+ if (rlimit.rlim_max == RLIM_INFINITY)
+ ast_copy_string(printlimit, "effectively unlimited", sizeof(printlimit));
+ else
+ snprintf(printlimit, sizeof(printlimit), "limited to %d", (int) rlimit.rlim_cur);
+ desc = str2desc(a->argv[1]);
+ ast_cli(a->fd, "%c%s (%s) is %s.\n", toupper(desc[0]), desc + 1, a->argv[1], printlimit);
+ } else
+ ast_cli(a->fd, "Could not retrieve resource limits for %s: %s\n", str2desc(a->argv[1]), strerror(errno));
+ return CLI_SUCCESS;
+ }
+ }
+}
+
+static struct ast_cli_entry cli_ulimit =
+ AST_CLI_DEFINE(handle_cli_ulimit, "Set or show process resource limits");
+
+static int unload_module(void)
+{
+ return ast_cli_unregister(&cli_ulimit);
+}
+
+static int load_module(void)
+{
+ return ast_cli_register(&cli_ulimit) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Resource limits");
+
diff --git a/trunk/res/res_monitor.c b/trunk/res/res_monitor.c
new file mode 100644
index 000000000..3e4c099f4
--- /dev/null
+++ b/trunk/res/res_monitor.c
@@ -0,0 +1,765 @@
+/*
+ * 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 PBX channel monitoring
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+#include <libgen.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/manager.h"
+#include "asterisk/cli.h"
+#include "asterisk/monitor.h"
+#include "asterisk/app.h"
+#include "asterisk/utils.h"
+#include "asterisk/config.h"
+
+AST_MUTEX_DEFINE_STATIC(monitorlock);
+
+#define LOCK_IF_NEEDED(lock, needed) do { \
+ if (needed) \
+ ast_channel_lock(lock); \
+ } while(0)
+
+#define UNLOCK_IF_NEEDED(lock, needed) do { \
+ if (needed) \
+ ast_channel_unlock(lock); \
+ } while (0)
+
+static unsigned long seq = 0;
+
+static char *monitor_synopsis = "Monitor a channel";
+
+static char *monitor_descrip = " Monitor([file_format[:urlbase],[fname_base],[options]]):\n"
+"Used to start monitoring a channel. The channel's input and output\n"
+"voice packets are logged to files until the channel hangs up or\n"
+"monitoring is stopped by the StopMonitor application.\n"
+" file_format optional, if not set, defaults to \"wav\"\n"
+" fname_base if set, changes the filename used to the one specified.\n"
+" options:\n"
+" m - when the recording ends mix the two leg files into one and\n"
+" delete the two leg files. If the variable MONITOR_EXEC is set, the\n"
+" application referenced in it will be executed instead of\n"
+#ifdef HAVE_SOXMIX
+" soxmix and the raw leg files will NOT be deleted automatically.\n"
+" soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
+#else
+" sox and the raw leg files will NOT be deleted automatically.\n"
+" sox or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
+#endif
+" and a target mixed file name which is the same as the leg file names\n"
+" only without the in/out designator.\n"
+" If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n"
+" additional arguments to MONITOR_EXEC\n"
+" Both MONITOR_EXEC and the Mix flag can be set from the\n"
+" administrator interface\n"
+"\n"
+" b - Don't begin recording unless a call is bridged to another channel\n"
+" i - Skip recording of input stream (disables m option)\n"
+" o - Skip recording of output stream (disables m option)\n"
+"\nReturns -1 if monitor files can't be opened or if the channel is already\n"
+"monitored, otherwise 0.\n"
+;
+
+static char *stopmonitor_synopsis = "Stop monitoring a channel";
+
+static char *stopmonitor_descrip = " StopMonitor():\n"
+ "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
+
+static char *changemonitor_synopsis = "Change monitoring filename of a channel";
+
+static char *changemonitor_descrip = " ChangeMonitor(filename_base):\n"
+ "Changes monitoring filename of a channel. Has no effect if the channel is not monitored.\n"
+ "The argument is the new filename base to use for monitoring this channel.\n";
+
+static char *pausemonitor_synopsis = "Pause monitoring of a channel";
+
+static char *pausemonitor_descrip = " PauseMonitor():\n"
+ "Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.\n";
+
+static char *unpausemonitor_synopsis = "Unpause monitoring of a channel";
+
+static char *unpausemonitor_descrip = " UnpauseMonitor():\n"
+ "Unpauses monitoring of a channel on which monitoring had\n"
+ "previously been paused with PauseMonitor.\n";
+
+/*!
+ * \brief Change state of monitored channel
+ * \param chan
+ * \param state monitor state
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+static int ast_monitor_set_state(struct ast_channel *chan, int state)
+{
+ LOCK_IF_NEEDED(chan, 1);
+ if (!chan->monitor) {
+ UNLOCK_IF_NEEDED(chan, 1);
+ return -1;
+ }
+ chan->monitor->state = state;
+ UNLOCK_IF_NEEDED(chan, 1);
+ return 0;
+}
+
+/*! \brief Start monitoring a channel
+ * \param chan ast_channel struct to record
+ * \param format_spec file format to use for recording
+ * \param fname_base filename base to record to
+ * \param need_lock whether to lock the channel mutex
+ * \param stream_action whether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used
+ * Creates the file to record, if no format is specified it assumes WAV
+ * It also sets channel variable __MONITORED=yes
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_monitor_start( struct ast_channel *chan, const char *format_spec,
+ const char *fname_base, int need_lock, int stream_action)
+{
+ int res = 0;
+
+ LOCK_IF_NEEDED(chan, need_lock);
+
+ if (!(chan->monitor)) {
+ struct ast_channel_monitor *monitor;
+ char *channel_name, *p;
+
+ /* Create monitoring directory if needed */
+ ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);
+
+ if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
+ UNLOCK_IF_NEEDED(chan, need_lock);
+ return -1;
+ }
+
+ /* Determine file names */
+ if (!ast_strlen_zero(fname_base)) {
+ int directory = strchr(fname_base, '/') ? 1 : 0;
+ /* try creating the directory just in case it doesn't exist */
+ if (directory) {
+ char *name = ast_strdupa(fname_base);
+ ast_mkdir(dirname(name), 0777);
+ }
+ snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in",
+ directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
+ snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out",
+ directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
+ ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base));
+ } else {
+ ast_mutex_lock(&monitorlock);
+ snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
+ ast_config_AST_MONITOR_DIR, seq);
+ snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
+ ast_config_AST_MONITOR_DIR, seq);
+ seq++;
+ ast_mutex_unlock(&monitorlock);
+
+ channel_name = ast_strdupa(chan->name);
+ while ((p = strchr(channel_name, '/'))) {
+ *p = '-';
+ }
+ snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
+ ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
+ monitor->filename_changed = 1;
+ }
+
+ monitor->stop = ast_monitor_stop;
+
+ /* Determine file format */
+ if (!ast_strlen_zero(format_spec)) {
+ monitor->format = ast_strdup(format_spec);
+ } else {
+ monitor->format = ast_strdup("wav");
+ }
+
+ /* open files */
+ if (stream_action & X_REC_IN) {
+ if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
+ ast_filedelete(monitor->read_filename, NULL);
+ if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
+ monitor->format, NULL,
+ O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
+ ast_log(LOG_WARNING, "Could not create file %s\n",
+ monitor->read_filename);
+ ast_free(monitor);
+ UNLOCK_IF_NEEDED(chan, need_lock);
+ return -1;
+ }
+ } else
+ monitor->read_stream = NULL;
+
+ if (stream_action & X_REC_OUT) {
+ if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
+ ast_filedelete(monitor->write_filename, NULL);
+ }
+ if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
+ monitor->format, NULL,
+ O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
+ ast_log(LOG_WARNING, "Could not create file %s\n",
+ monitor->write_filename);
+ ast_closestream(monitor->read_stream);
+ ast_free(monitor);
+ UNLOCK_IF_NEEDED(chan, need_lock);
+ return -1;
+ }
+ } else
+ monitor->write_stream = NULL;
+
+ chan->monitor = monitor;
+ ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
+ /* so we know this call has been monitored in case we need to bill for it or something */
+ pbx_builtin_setvar_helper(chan, "__MONITORED","true");
+
+ manager_event(EVENT_FLAG_CALL, "MonitorStart",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan->name,
+ chan->uniqueid
+ );
+ } else {
+ ast_debug(1,"Cannot start monitoring %s, already monitored\n", chan->name);
+ res = -1;
+ }
+
+ UNLOCK_IF_NEEDED(chan, need_lock);
+
+ return res;
+}
+
+/*!
+ * \brief Get audio format.
+ * \param format recording format.
+ * The file format extensions that Asterisk uses are not all the same as that
+ * which soxmix expects. This function ensures that the format used as the
+ * extension on the filename is something soxmix will understand.
+ */
+static const char *get_soxmix_format(const char *format)
+{
+ const char *res = format;
+
+ if (!strcasecmp(format,"ulaw"))
+ res = "ul";
+ if (!strcasecmp(format,"alaw"))
+ res = "al";
+
+ return res;
+}
+
+/*!
+ * \brief Stop monitoring channel
+ * \param chan
+ * \param need_lock
+ * Stop the recording, close any open streams, mix in/out channels if required
+ * \return Always 0
+*/
+int ast_monitor_stop(struct ast_channel *chan, int need_lock)
+{
+ int delfiles = 0;
+
+ LOCK_IF_NEEDED(chan, need_lock);
+
+ if (chan->monitor) {
+ char filename[ FILENAME_MAX ];
+
+ if (chan->monitor->read_stream) {
+ ast_closestream(chan->monitor->read_stream);
+ }
+ if (chan->monitor->write_stream) {
+ ast_closestream(chan->monitor->write_stream);
+ }
+
+ if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
+ if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
+ snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
+ if (ast_fileexists(filename, NULL, NULL) > 0) {
+ ast_filedelete(filename, NULL);
+ }
+ ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
+ } else {
+ ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
+ }
+
+ if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
+ snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
+ if (ast_fileexists(filename, NULL, NULL) > 0) {
+ ast_filedelete(filename, NULL);
+ }
+ ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
+ } else {
+ ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
+ }
+ }
+
+ if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
+ char tmp[1024];
+ char tmp2[1024];
+ const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
+ char *name = chan->monitor->filename_base;
+ int directory = strchr(name, '/') ? 1 : 0;
+ const char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
+ const char *execute, *execute_args;
+
+ /* Set the execute application */
+ execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
+ if (ast_strlen_zero(execute)) {
+#ifdef HAVE_SOXMIX
+ execute = "nice -n 19 soxmix";
+#else
+ execute = "nice -n 19 sox -m";
+#endif
+ format = get_soxmix_format(format);
+ delfiles = 1;
+ }
+ execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
+ if (ast_strlen_zero(execute_args)) {
+ execute_args = "";
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args);
+ if (delfiles) {
+ snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */
+ ast_copy_string(tmp, tmp2, sizeof(tmp));
+ }
+ ast_debug(1,"monitor executing %s\n",tmp);
+ if (ast_safe_system(tmp) == -1)
+ ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
+ }
+
+ ast_free(chan->monitor->format);
+ ast_free(chan->monitor);
+ chan->monitor = NULL;
+
+ manager_event(EVENT_FLAG_CALL, "MonitorStop",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan->name,
+ chan->uniqueid
+ );
+ }
+
+ UNLOCK_IF_NEEDED(chan, need_lock);
+
+ return 0;
+}
+
+
+/*! \brief Pause monitoring of channel */
+int ast_monitor_pause(struct ast_channel *chan)
+{
+ return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
+}
+
+/*! \brief Unpause monitoring of channel */
+int ast_monitor_unpause(struct ast_channel *chan)
+{
+ return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
+}
+
+/*! \brief Wrapper for ast_monitor_pause */
+static int pause_monitor_exec(struct ast_channel *chan, void *data)
+{
+ return ast_monitor_pause(chan);
+}
+
+/*! \brief Wrapper for ast_monitor_unpause */
+static int unpause_monitor_exec(struct ast_channel *chan, void *data)
+{
+ return ast_monitor_unpause(chan);
+}
+
+/*!
+ * \brief Change monitored filename of channel
+ * \param chan
+ * \param fname_base new filename
+ * \param need_lock
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
+{
+ if (ast_strlen_zero(fname_base)) {
+ ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
+ return -1;
+ }
+
+ LOCK_IF_NEEDED(chan, need_lock);
+
+ if (chan->monitor) {
+ int directory = strchr(fname_base, '/') ? 1 : 0;
+ /* try creating the directory just in case it doesn't exist */
+ if (directory) {
+ char *name = ast_strdupa(fname_base);
+ ast_mkdir(dirname(name), 0777);
+ }
+
+ snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
+ chan->monitor->filename_changed = 1;
+ } else {
+ ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
+ }
+
+ UNLOCK_IF_NEEDED(chan, need_lock);
+
+ return 0;
+}
+
+
+/*!
+ * \brief Start monitor
+ * \param chan
+ * \param data arguments passed fname|options
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+static int start_monitor_exec(struct ast_channel *chan, void *data)
+{
+ char *arg = NULL;
+ char *options = NULL;
+ char *delay = NULL;
+ char *urlprefix = NULL;
+ char tmp[256];
+ int stream_action = X_REC_IN | X_REC_OUT;
+ int joinfiles = 0;
+ int waitforbridge = 0;
+ int res = 0;
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(format);
+ AST_APP_ARG(fname_base);
+ AST_APP_ARG(options);
+ );
+
+ /* Parse arguments. */
+ if (ast_strlen_zero((char*)data)) {
+ ast_log(LOG_ERROR, "Monitor requires an argument\n");
+ return 0;
+ }
+
+ parse = ast_strdupa((char*)data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options)) {
+ if (strchr(args.options, 'm'))
+ stream_action |= X_JOIN;
+ if (strchr(args.options, 'b'))
+ waitforbridge = 1;
+ if (strchr(args.options, 'i'))
+ stream_action &= ~X_REC_IN;
+ if (strchr(args.options, 'o'))
+ stream_action &= ~X_REC_OUT;
+ }
+
+ arg = strchr(args.format, ':');
+ if (arg) {
+ *arg++ = 0;
+ urlprefix = arg;
+ }
+
+ if (urlprefix) {
+ snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
+ ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
+ if (!chan->cdr && !(chan->cdr = ast_cdr_alloc()))
+ return -1;
+ ast_cdr_setuserfield(chan, tmp);
+ }
+ if (waitforbridge) {
+ /* We must remove the "b" option if listed. In principle none of
+ the following could give NULL results, but we check just to
+ be pedantic. Reconstructing with checks for 'm' option does not
+ work if we end up adding more options than 'm' in the future. */
+ delay = ast_strdupa(data);
+ options = strrchr(delay, '|');
+ if (options) {
+ arg = strchr(options, 'b');
+ if (arg) {
+ *arg = 'X';
+ pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
+ }
+ }
+ return 0;
+ }
+
+ res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
+ if (res < 0)
+ res = ast_monitor_change_fname(chan, args.fname_base, 1);
+
+ if (stream_action & X_JOIN) {
+ if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
+ joinfiles = 1;
+ else
+ ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
+ }
+ ast_monitor_setjoinfiles(chan, joinfiles);
+
+ return res;
+}
+
+/*! \brief Wrapper function \see ast_monitor_stop */
+static int stop_monitor_exec(struct ast_channel *chan, void *data)
+{
+ return ast_monitor_stop(chan, 1);
+}
+
+/*! \brief Wrapper function \see ast_monitor_change_fname */
+static int change_monitor_exec(struct ast_channel *chan, void *data)
+{
+ return ast_monitor_change_fname(chan, (const char*)data, 1);
+}
+
+static char start_monitor_action_help[] =
+"Description: The 'Monitor' action may be used to record the audio on a\n"
+" specified channel. The following parameters may be used to control\n"
+" this:\n"
+" Channel - Required. Used to specify the channel to record.\n"
+" File - Optional. Is the name of the file created in the\n"
+" monitor spool directory. Defaults to the same name\n"
+" as the channel (with slashes replaced with dashes).\n"
+" Format - Optional. Is the audio recording format. Defaults\n"
+" to \"wav\".\n"
+" Mix - Optional. Boolean parameter as to whether to mix\n"
+" the input and output channels together after the\n"
+" recording is finished.\n";
+
+/*! \brief Start monitoring a channel by manager connection */
+static int start_monitor_action(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *fname = astman_get_header(m, "File");
+ const char *format = astman_get_header(m, "Format");
+ const char *mix = astman_get_header(m, "Mix");
+ char *d;
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+
+ if (ast_strlen_zero(fname)) {
+ /* No filename base specified, default to channel name as per CLI */
+ if (!(fname = ast_strdup(c->name))) {
+ astman_send_error(s, m, "Could not start monitoring channel");
+ ast_channel_unlock(c);
+ return 0;
+ }
+ /* Channels have the format technology/channel_name - have to replace that / */
+ if ((d = strchr(fname, '/')))
+ *d = '-';
+ }
+
+ if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
+ if (ast_monitor_change_fname(c, fname, 1)) {
+ astman_send_error(s, m, "Could not start monitoring channel");
+ ast_channel_unlock(c);
+ return 0;
+ }
+ }
+
+ if (ast_true(mix)) {
+ ast_monitor_setjoinfiles(c, 1);
+ }
+
+ ast_channel_unlock(c);
+ astman_send_ack(s, m, "Started monitoring channel");
+ return 0;
+}
+
+static char stop_monitor_action_help[] =
+"Description: The 'StopMonitor' action may be used to end a previously\n"
+" started 'Monitor' action. The only parameter is 'Channel', the name\n"
+" of the channel monitored.\n";
+
+/*! \brief Stop monitoring a channel by manager connection */
+static int stop_monitor_action(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ int res;
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ res = ast_monitor_stop(c, 1);
+ ast_channel_unlock(c);
+ if (res) {
+ astman_send_error(s, m, "Could not stop monitoring channel");
+ return 0;
+ }
+ astman_send_ack(s, m, "Stopped monitoring channel");
+ return 0;
+}
+
+static char change_monitor_action_help[] =
+"Description: The 'ChangeMonitor' action may be used to change the file\n"
+" started by a previous 'Monitor' action. The following parameters may\n"
+" be used to control this:\n"
+" Channel - Required. Used to specify the channel to record.\n"
+" File - Required. Is the new name of the file created in the\n"
+" monitor spool directory.\n";
+
+/*! \brief Change filename of a monitored channel by manager connection */
+static int change_monitor_action(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *fname = astman_get_header(m, "File");
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ if (ast_strlen_zero(fname)) {
+ astman_send_error(s, m, "No filename specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ if (ast_monitor_change_fname(c, fname, 1)) {
+ astman_send_error(s, m, "Could not change monitored filename of channel");
+ ast_channel_unlock(c);
+ return 0;
+ }
+ ast_channel_unlock(c);
+ astman_send_ack(s, m, "Changed monitor filename");
+ return 0;
+}
+
+void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
+{
+ if (chan->monitor)
+ chan->monitor->joinfiles = turnon;
+}
+
+enum MONITOR_PAUSING_ACTION
+{
+ MONITOR_ACTION_PAUSE,
+ MONITOR_ACTION_UNPAUSE
+};
+
+static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return -1;
+ }
+
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return -1;
+ }
+
+ if (action == MONITOR_ACTION_PAUSE)
+ ast_monitor_pause(c);
+ else
+ ast_monitor_unpause(c);
+
+ ast_channel_unlock(c);
+ astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
+ return 0;
+}
+
+static char pause_monitor_action_help[] =
+ "Description: The 'PauseMonitor' action may be used to temporarily stop the\n"
+ " recording of a channel. The following parameters may\n"
+ " be used to control this:\n"
+ " Channel - Required. Used to specify the channel to record.\n";
+
+static int pause_monitor_action(struct mansession *s, const struct message *m)
+{
+ return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
+}
+
+static char unpause_monitor_action_help[] =
+ "Description: The 'UnpauseMonitor' action may be used to re-enable recording\n"
+ " of a channel after calling PauseMonitor. The following parameters may\n"
+ " be used to control this:\n"
+ " Channel - Required. Used to specify the channel to record.\n";
+
+static int unpause_monitor_action(struct mansession *s, const struct message *m)
+{
+ return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
+}
+
+
+static int load_module(void)
+{
+ ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
+ ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
+ ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
+ ast_register_application("PauseMonitor", pause_monitor_exec, pausemonitor_synopsis, pausemonitor_descrip);
+ ast_register_application("UnpauseMonitor", unpause_monitor_exec, unpausemonitor_synopsis, unpausemonitor_descrip);
+ ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
+ ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
+ ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
+ ast_manager_register2("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action, pausemonitor_synopsis, pause_monitor_action_help);
+ ast_manager_register2("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action, unpausemonitor_synopsis, unpause_monitor_action_help);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_unregister_application("Monitor");
+ ast_unregister_application("StopMonitor");
+ ast_unregister_application("ChangeMonitor");
+ ast_unregister_application("PauseMonitor");
+ ast_unregister_application("UnpauseMonitor");
+ ast_manager_unregister("Monitor");
+ ast_manager_unregister("StopMonitor");
+ ast_manager_unregister("ChangeMonitor");
+ ast_manager_unregister("PauseMonitor");
+ ast_manager_unregister("UnpauseMonitor");
+
+ return 0;
+}
+
+/* usecount semantics need to be defined */
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Monitoring Resource",
+ .load = load_module,
+ .unload = unload_module,
+ );
diff --git a/trunk/res/res_musiconhold.c b/trunk/res/res_musiconhold.c
new file mode 100644
index 000000000..b4c5edfbb
--- /dev/null
+++ b/trunk/res/res_musiconhold.c
@@ -0,0 +1,1566 @@
+/*
+ * 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 Routines implementing music on hold
+ *
+ * \arg See also \ref Config_moh
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+/*** MODULEINFO
+ <conflict>win32</conflict>
+ <use>zaptel</use>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#ifdef SOLARIS
+#include <thread.h>
+#endif
+
+#include "asterisk/zapata.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/say.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/linkedlists.h"
+
+#define INITIAL_NUM_FILES 8
+
+static char *play_moh = "MusicOnHold";
+static char *wait_moh = "WaitMusicOnHold";
+static char *set_moh = "SetMusicOnHold";
+static char *start_moh = "StartMusicOnHold";
+static char *stop_moh = "StopMusicOnHold";
+
+static char *play_moh_syn = "Play Music On Hold indefinitely";
+static char *wait_moh_syn = "Wait, playing Music On Hold";
+static char *set_moh_syn = "Set default Music On Hold class";
+static char *start_moh_syn = "Play Music On Hold";
+static char *stop_moh_syn = "Stop Playing Music On Hold";
+
+static char *play_moh_desc = " MusicOnHold(class):\n"
+"Plays hold music specified by class. If omitted, the default\n"
+"music source for the channel will be used. Set the default \n"
+"class with the SetMusicOnHold() application.\n"
+"Returns -1 on hangup.\n"
+"Never returns otherwise.\n";
+
+static char *wait_moh_desc = " WaitMusicOnHold(delay):\n"
+"Plays hold music specified number of seconds. Returns 0 when\n"
+"done, or -1 on hangup. If no hold music is available, the delay will\n"
+"still occur with no sound.\n";
+
+static char *set_moh_desc = " SetMusicOnHold(class):\n"
+"Sets the default class for music on hold for a given channel. When\n"
+"music on hold is activated, this class will be used to select which\n"
+"music is played.\n";
+
+static char *start_moh_desc = " StartMusicOnHold(class):\n"
+"Starts playing music on hold, uses default music class for channel.\n"
+"Starts playing music specified by class. If omitted, the default\n"
+"music source for the channel will be used. Always returns 0.\n";
+
+static char *stop_moh_desc = " StopMusicOnHold(): "
+"Stops playing music on hold.\n";
+
+static int respawn_time = 20;
+
+struct moh_files_state {
+ struct mohclass *class;
+ int origwfmt;
+ int samples;
+ int sample_queue;
+ int pos;
+ int save_pos;
+};
+
+#define MOH_QUIET (1 << 0)
+#define MOH_SINGLE (1 << 1)
+#define MOH_CUSTOM (1 << 2)
+#define MOH_RANDOMIZE (1 << 3)
+#define MOH_SORTALPHA (1 << 4)
+
+#define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
+
+static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
+
+struct mohclass {
+ char name[MAX_MUSICCLASS];
+ char dir[256];
+ char args[256];
+ char mode[80];
+ char digit;
+ /*! A dynamically sized array to hold the list of filenames in "files" mode */
+ char **filearray;
+ /*! The current size of the filearray */
+ int allowed_files;
+ /*! The current number of files loaded into the filearray */
+ int total_files;
+ unsigned int flags;
+ /*! The format from the MOH source, not applicable to "files" mode */
+ int format;
+ /*! The pid of the external application delivering MOH */
+ int pid;
+ time_t start;
+ pthread_t thread;
+ /*! Source of audio */
+ int srcfd;
+ /*! FD for timing source */
+ int pseudofd;
+ /*! Number of users */
+ int inuse;
+ /*! Created on the fly, from RT engine */
+ int realtime;
+ unsigned int delete:1;
+ AST_LIST_HEAD_NOLOCK(, mohdata) members;
+ AST_LIST_ENTRY(mohclass) list;
+};
+
+struct mohdata {
+ int pipe[2];
+ int origwfmt;
+ struct mohclass *parent;
+ struct ast_frame f;
+ AST_LIST_ENTRY(mohdata) list;
+};
+
+AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
+
+#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
+#define MPG_123 "/usr/bin/mpg123"
+#define MAX_MP3S 256
+
+static int ast_moh_destroy_one(struct mohclass *moh);
+static int reload(void);
+
+static void ast_moh_free_class(struct mohclass **mohclass)
+{
+ struct mohdata *member;
+ struct mohclass *class = *mohclass;
+ int i;
+
+ while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
+ ast_free(member);
+
+ if (class->thread) {
+ pthread_cancel(class->thread);
+ class->thread = 0;
+ }
+
+ if (class->filearray) {
+ for (i = 0; i < class->total_files; i++)
+ ast_free(class->filearray[i]);
+ ast_free(class->filearray);
+ }
+
+ ast_free(class);
+ *mohclass = NULL;
+}
+
+
+static void moh_files_release(struct ast_channel *chan, void *data)
+{
+ struct moh_files_state *state = chan->music_state;
+
+ if (chan && state) {
+ if (chan->stream) {
+ ast_closestream(chan->stream);
+ chan->stream = NULL;
+ }
+ ast_verb(3, "Stopped music on hold on %s\n", chan->name);
+
+ if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
+ }
+ state->save_pos = state->pos;
+ }
+ if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
+ ast_moh_destroy_one(state->class);
+}
+
+
+static int ast_moh_files_next(struct ast_channel *chan)
+{
+ struct moh_files_state *state = chan->music_state;
+ int tries;
+
+ /* Discontinue a stream if it is running already */
+ if (chan->stream) {
+ ast_closestream(chan->stream);
+ chan->stream = NULL;
+ }
+
+ if (!state->class->total_files) {
+ ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
+ return -1;
+ }
+
+ /* If a specific file has been saved, use it */
+ if (state->save_pos >= 0) {
+ state->pos = state->save_pos;
+ state->save_pos = -1;
+ } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
+ /* Get a random file and ensure we can open it */
+ for (tries = 0; tries < 20; tries++) {
+ state->pos = rand() % state->class->total_files;
+ if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
+ break;
+ }
+ state->samples = 0;
+ } else {
+ /* This is easy, just increment our position and make sure we don't exceed the total file count */
+ state->pos++;
+ state->pos %= state->class->total_files;
+ state->samples = 0;
+ }
+
+ if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
+ ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
+ state->pos++;
+ state->pos %= state->class->total_files;
+ return -1;
+ }
+
+ ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
+
+ if (state->samples)
+ ast_seekstream(chan->stream, state->samples, SEEK_SET);
+
+ return 0;
+}
+
+
+static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
+{
+ struct ast_frame *f = NULL;
+
+ if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
+ if (!ast_moh_files_next(chan))
+ f = ast_readframe(chan->stream);
+ }
+
+ return f;
+}
+
+static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct moh_files_state *state = chan->music_state;
+ struct ast_frame *f = NULL;
+ int res = 0;
+
+ state->sample_queue += samples;
+
+ while (state->sample_queue > 0) {
+ if ((f = moh_files_readframe(chan))) {
+ state->samples += f->samples;
+ res = ast_write(chan, f);
+ state->sample_queue -= f->samples;
+ ast_frfree(f);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
+ return -1;
+ }
+ } else
+ return -1;
+ }
+ return res;
+}
+
+
+static void *moh_files_alloc(struct ast_channel *chan, void *params)
+{
+ struct moh_files_state *state;
+ struct mohclass *class = params;
+
+ if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
+ chan->music_state = state;
+ state->class = class;
+ state->save_pos = -1;
+ } else
+ state = chan->music_state;
+
+ if (state) {
+ if (state->class != class) {
+ /* initialize */
+ memset(state, 0, sizeof(*state));
+ state->class = class;
+ if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
+ state->pos = ast_random() % class->total_files;
+ }
+
+ state->origwfmt = chan->writeformat;
+
+ ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
+ }
+
+ return chan->music_state;
+}
+
+/*! \note This function should be called with the mohclasses list locked */
+static struct mohclass *get_mohbydigit(char digit)
+{
+ struct mohclass *moh = NULL;
+
+ AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
+ if (digit == moh->digit)
+ break;
+ }
+
+ return moh;
+}
+
+static void moh_handle_digit(struct ast_channel *chan, char digit)
+{
+ struct mohclass *moh;
+ const char *classname = NULL;
+
+ AST_RWLIST_RDLOCK(&mohclasses);
+ if ((moh = get_mohbydigit(digit)))
+ classname = ast_strdupa(moh->name);
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ if (!moh)
+ return;
+
+ ast_moh_stop(chan);
+ ast_moh_start(chan, classname, NULL);
+}
+
+static struct ast_generator moh_file_stream =
+{
+ alloc: moh_files_alloc,
+ release: moh_files_release,
+ generate: moh_files_generator,
+ digit: moh_handle_digit,
+};
+
+static int spawn_mp3(struct mohclass *class)
+{
+ int fds[2];
+ int files = 0;
+ char fns[MAX_MP3S][80];
+ char *argv[MAX_MP3S + 50];
+ char xargs[256];
+ char *argptr;
+ int argc = 0;
+ DIR *dir = NULL;
+ struct dirent *de;
+ sigset_t signal_set, old_set;
+
+
+ if (!strcasecmp(class->dir, "nodir")) {
+ files = 1;
+ } else {
+ dir = opendir(class->dir);
+ if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
+ ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
+ return -1;
+ }
+ }
+
+ if (!ast_test_flag(class, MOH_CUSTOM)) {
+ argv[argc++] = "mpg123";
+ argv[argc++] = "-q";
+ argv[argc++] = "-s";
+ argv[argc++] = "--mono";
+ argv[argc++] = "-r";
+ argv[argc++] = "8000";
+
+ if (!ast_test_flag(class, MOH_SINGLE)) {
+ argv[argc++] = "-b";
+ argv[argc++] = "2048";
+ }
+
+ argv[argc++] = "-f";
+
+ if (ast_test_flag(class, MOH_QUIET))
+ argv[argc++] = "4096";
+ else
+ argv[argc++] = "8192";
+
+ /* Look for extra arguments and add them to the list */
+ ast_copy_string(xargs, class->args, sizeof(xargs));
+ argptr = xargs;
+ while (!ast_strlen_zero(argptr)) {
+ argv[argc++] = argptr;
+ strsep(&argptr, ",");
+ }
+ } else {
+ /* Format arguments for argv vector */
+ ast_copy_string(xargs, class->args, sizeof(xargs));
+ argptr = xargs;
+ while (!ast_strlen_zero(argptr)) {
+ argv[argc++] = argptr;
+ strsep(&argptr, " ");
+ }
+ }
+
+
+ if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
+ ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
+ argv[argc++] = fns[files];
+ files++;
+ } else if (dir) {
+ while ((de = readdir(dir)) && (files < MAX_MP3S)) {
+ if ((strlen(de->d_name) > 3) &&
+ ((ast_test_flag(class, MOH_CUSTOM) &&
+ (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
+ !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
+ !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
+ ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
+ argv[argc++] = fns[files];
+ files++;
+ }
+ }
+ }
+ argv[argc] = NULL;
+ if (dir) {
+ closedir(dir);
+ }
+ if (pipe(fds)) {
+ ast_log(LOG_WARNING, "Pipe failed\n");
+ return -1;
+ }
+ if (!files) {
+ ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
+ close(fds[0]);
+ close(fds[1]);
+ return -1;
+ }
+ if (time(NULL) - class->start < respawn_time) {
+ sleep(respawn_time - (time(NULL) - class->start));
+ }
+
+ /* Block signals during the fork() */
+ sigfillset(&signal_set);
+ pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
+
+ time(&class->start);
+ class->pid = fork();
+ if (class->pid < 0) {
+ close(fds[0]);
+ close(fds[1]);
+ ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+ return -1;
+ }
+ if (!class->pid) {
+ int x;
+
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+
+ /* Reset ignored signals back to default */
+ signal(SIGPIPE, SIG_DFL);
+ pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
+
+ close(fds[0]);
+ /* Stdout goes to pipe */
+ dup2(fds[1], STDOUT_FILENO);
+ /* Close unused file descriptors */
+ for (x=3;x<8192;x++) {
+ if (-1 != fcntl(x, F_GETFL)) {
+ close(x);
+ }
+ }
+ /* Child */
+ chdir(class->dir);
+ if (ast_test_flag(class, MOH_CUSTOM)) {
+ execv(argv[0], argv);
+ } else {
+ /* Default install is /usr/local/bin */
+ execv(LOCAL_MPG_123, argv);
+ /* Many places have it in /usr/bin */
+ execv(MPG_123, argv);
+ /* Check PATH as a last-ditch effort */
+ execvp("mpg123", argv);
+ }
+ ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
+ close(fds[1]);
+ _exit(1);
+ } else {
+ /* Parent */
+ pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+ close(fds[1]);
+ }
+ return fds[0];
+}
+
+static void *monmp3thread(void *data)
+{
+#define MOH_MS_INTERVAL 100
+
+ struct mohclass *class = data;
+ struct mohdata *moh;
+ char buf[8192];
+ short sbuf[8192];
+ int res, res2;
+ int len;
+ struct timeval tv, tv_tmp;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ for(;/* ever */;) {
+ pthread_testcancel();
+ /* Spawn mp3 player if it's not there */
+ if (class->srcfd < 0) {
+ if ((class->srcfd = spawn_mp3(class)) < 0) {
+ ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
+ /* Try again later */
+ sleep(500);
+ pthread_testcancel();
+ }
+ }
+ if (class->pseudofd > -1) {
+#ifdef SOLARIS
+ thr_yield();
+#endif
+ /* Pause some amount of time */
+ res = read(class->pseudofd, buf, sizeof(buf));
+ pthread_testcancel();
+ } else {
+ long delta;
+ /* Reliable sleep */
+ tv_tmp = ast_tvnow();
+ if (ast_tvzero(tv))
+ tv = tv_tmp;
+ delta = ast_tvdiff_ms(tv_tmp, tv);
+ if (delta < MOH_MS_INTERVAL) { /* too early */
+ tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
+ usleep(1000 * (MOH_MS_INTERVAL - delta));
+ pthread_testcancel();
+ } else {
+ ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
+ tv = tv_tmp;
+ }
+ res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
+ }
+ if (AST_LIST_EMPTY(&class->members))
+ continue;
+ /* Read mp3 audio */
+ len = ast_codec_get_len(class->format, res);
+
+ if ((res2 = read(class->srcfd, sbuf, len)) != len) {
+ if (!res2) {
+ close(class->srcfd);
+ class->srcfd = -1;
+ pthread_testcancel();
+ if (class->pid > 1) {
+ kill(class->pid, SIGHUP);
+ usleep(100000);
+ kill(class->pid, SIGTERM);
+ usleep(100000);
+ kill(class->pid, SIGKILL);
+ class->pid = 0;
+ }
+ } else {
+ ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
+ }
+ continue;
+ }
+ pthread_testcancel();
+ AST_RWLIST_RDLOCK(&mohclasses);
+ AST_RWLIST_TRAVERSE(&class->members, moh, list) {
+ /* Write data */
+ if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
+ ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
+ }
+ }
+ AST_RWLIST_UNLOCK(&mohclasses);
+ }
+ return NULL;
+}
+
+static int play_moh_exec(struct ast_channel *chan, void *data)
+{
+ if (ast_moh_start(chan, data, NULL)) {
+ ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
+ return 0;
+ }
+ while (!ast_safe_sleep(chan, 10000));
+ ast_moh_stop(chan);
+ return -1;
+}
+
+static int wait_moh_exec(struct ast_channel *chan, void *data)
+{
+ int res;
+ if (!data || !atoi(data)) {
+ ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
+ return -1;
+ }
+ if (ast_moh_start(chan, NULL, NULL)) {
+ ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
+ return 0;
+ }
+ res = ast_safe_sleep(chan, atoi(data) * 1000);
+ ast_moh_stop(chan);
+ return res;
+}
+
+static int set_moh_exec(struct ast_channel *chan, void *data)
+{
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
+ return -1;
+ }
+ ast_string_field_set(chan, musicclass, data);
+ return 0;
+}
+
+static int start_moh_exec(struct ast_channel *chan, void *data)
+{
+ char *class = NULL;
+ if (data && strlen(data))
+ class = data;
+ if (ast_moh_start(chan, class, NULL))
+ ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
+
+ return 0;
+}
+
+static int stop_moh_exec(struct ast_channel *chan, void *data)
+{
+ ast_moh_stop(chan);
+
+ return 0;
+}
+
+/*! \note This function should be called with the mohclasses list locked */
+static struct mohclass *get_mohbyname(const char *name, int warn)
+{
+ struct mohclass *moh = NULL;
+
+ AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
+ if (!strcasecmp(name, moh->name))
+ break;
+ }
+
+ if (!moh && warn)
+ ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
+
+ return moh;
+}
+
+static struct mohdata *mohalloc(struct mohclass *cl)
+{
+ struct mohdata *moh;
+ long flags;
+
+ if (!(moh = ast_calloc(1, sizeof(*moh))))
+ return NULL;
+
+ if (pipe(moh->pipe)) {
+ ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
+ ast_free(moh);
+ return NULL;
+ }
+
+ /* Make entirely non-blocking */
+ flags = fcntl(moh->pipe[0], F_GETFL);
+ fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl(moh->pipe[1], F_GETFL);
+ fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
+
+ moh->f.frametype = AST_FRAME_VOICE;
+ moh->f.subclass = cl->format;
+ moh->f.offset = AST_FRIENDLY_OFFSET;
+
+ moh->parent = cl;
+
+ AST_RWLIST_WRLOCK(&mohclasses);
+ AST_LIST_INSERT_HEAD(&cl->members, moh, list);
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ return moh;
+}
+
+static void moh_release(struct ast_channel *chan, void *data)
+{
+ struct mohdata *moh = data;
+ int oldwfmt;
+ struct moh_files_state *state;
+
+ AST_RWLIST_WRLOCK(&mohclasses);
+ AST_RWLIST_REMOVE(&moh->parent->members, moh, list);
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ close(moh->pipe[0]);
+ close(moh->pipe[1]);
+ oldwfmt = moh->origwfmt;
+ state = chan->music_state;
+ if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
+ ast_moh_destroy_one(moh->parent);
+ if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
+ ast_moh_destroy_one(state->class);
+
+ ast_free(moh);
+ if (chan) {
+ if (oldwfmt && ast_set_write_format(chan, oldwfmt))
+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
+ ast_verb(3, "Stopped music on hold on %s\n", chan->name);
+ }
+}
+
+static void *moh_alloc(struct ast_channel *chan, void *params)
+{
+ struct mohdata *res;
+ struct mohclass *class = params;
+ struct moh_files_state *state;
+
+ /* Initiating music_state for current channel. Channel should know name of moh class */
+ if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
+ chan->music_state = state;
+ state->class = class;
+ } else
+ state = chan->music_state;
+ if (state && state->class != class) {
+ memset(state, 0, sizeof(*state));
+ state->class = class;
+ }
+
+ if ((res = mohalloc(class))) {
+ res->origwfmt = chan->writeformat;
+ if (ast_set_write_format(chan, class->format)) {
+ ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
+ moh_release(NULL, res);
+ res = NULL;
+ }
+ ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
+ }
+ return res;
+}
+
+static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct mohdata *moh = data;
+ short buf[1280 + AST_FRIENDLY_OFFSET / 2];
+ int res;
+
+ if (!moh->parent->pid)
+ return -1;
+
+ len = ast_codec_get_len(moh->parent->format, samples);
+
+ if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
+ ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
+ len = sizeof(buf) - AST_FRIENDLY_OFFSET;
+ }
+ res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
+ if (res <= 0)
+ return 0;
+
+ moh->f.datalen = res;
+ moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
+ moh->f.samples = ast_codec_get_samples(&moh->f);
+
+ if (ast_write(chan, &moh->f) < 0) {
+ ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct ast_generator mohgen =
+{
+ alloc: moh_alloc,
+ release: moh_release,
+ generate: moh_generate,
+ digit: moh_handle_digit
+};
+
+static int moh_add_file(struct mohclass *class, const char *filepath)
+{
+ if (!class->allowed_files) {
+ if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
+ return -1;
+ class->allowed_files = INITIAL_NUM_FILES;
+ } else if (class->total_files == class->allowed_files) {
+ if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
+ class->allowed_files = 0;
+ class->total_files = 0;
+ return -1;
+ }
+ class->allowed_files *= 2;
+ }
+
+ if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
+ return -1;
+
+ class->total_files++;
+
+ return 0;
+}
+
+static int moh_sort_compare(const void *i1, const void *i2)
+{
+ char *s1, *s2;
+
+ s1 = ((char **)i1)[0];
+ s2 = ((char **)i2)[0];
+
+ return strcasecmp(s1, s2);
+}
+
+static int moh_scan_files(struct mohclass *class) {
+
+ DIR *files_DIR;
+ struct dirent *files_dirent;
+ char path[PATH_MAX];
+ char filepath[PATH_MAX];
+ char *ext;
+ struct stat statbuf;
+ int dirnamelen;
+ int i;
+
+ files_DIR = opendir(class->dir);
+ if (!files_DIR) {
+ ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
+ return -1;
+ }
+
+ for (i = 0; i < class->total_files; i++)
+ ast_free(class->filearray[i]);
+
+ class->total_files = 0;
+ dirnamelen = strlen(class->dir) + 2;
+ getcwd(path, sizeof(path));
+ chdir(class->dir);
+ while ((files_dirent = readdir(files_DIR))) {
+ /* The file name must be at least long enough to have the file type extension */
+ if ((strlen(files_dirent->d_name) < 4))
+ continue;
+
+ /* Skip files that starts with a dot */
+ if (files_dirent->d_name[0] == '.')
+ continue;
+
+ /* Skip files without extensions... they are not audio */
+ if (!strchr(files_dirent->d_name, '.'))
+ continue;
+
+ snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
+
+ if (stat(filepath, &statbuf))
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode))
+ continue;
+
+ if ((ext = strrchr(filepath, '.')))
+ *ext = '\0';
+
+ /* if the file is present in multiple formats, ensure we only put it into the list once */
+ for (i = 0; i < class->total_files; i++)
+ if (!strcmp(filepath, class->filearray[i]))
+ break;
+
+ if (i == class->total_files) {
+ if (moh_add_file(class, filepath))
+ break;
+ }
+ }
+
+ closedir(files_DIR);
+ chdir(path);
+ if (ast_test_flag(class, MOH_SORTALPHA))
+ qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
+ return class->total_files;
+}
+
+static int moh_diff(struct mohclass *old, struct mohclass *new)
+{
+ if (!old || !new)
+ return -1;
+
+ if (strcmp(old->dir, new->dir))
+ return -1;
+ else if (strcmp(old->mode, new->mode))
+ return -1;
+ else if (strcmp(old->args, new->args))
+ return -1;
+ else if (old->flags != new->flags)
+ return -1;
+
+ return 0;
+}
+
+static int moh_register(struct mohclass *moh, int reload)
+{
+#ifdef HAVE_ZAPTEL
+ int x;
+#endif
+ struct mohclass *mohclass = NULL;
+
+ AST_RWLIST_WRLOCK(&mohclasses);
+ if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
+ mohclass->delete = 0;
+ if (reload) {
+ ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
+ } else {
+ ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
+ }
+ ast_free(moh);
+ AST_RWLIST_UNLOCK(&mohclasses);
+ return -1;
+ }
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ time(&moh->start);
+ moh->start -= respawn_time;
+
+ if (!strcasecmp(moh->mode, "files")) {
+ if (!moh_scan_files(moh)) {
+ ast_moh_free_class(&moh);
+ return -1;
+ }
+ if (strchr(moh->args, 'r'))
+ ast_set_flag(moh, MOH_RANDOMIZE);
+ } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
+
+ if (!strcasecmp(moh->mode, "custom"))
+ ast_set_flag(moh, MOH_CUSTOM);
+ else if (!strcasecmp(moh->mode, "mp3nb"))
+ ast_set_flag(moh, MOH_SINGLE);
+ else if (!strcasecmp(moh->mode, "quietmp3nb"))
+ ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
+ else if (!strcasecmp(moh->mode, "quietmp3"))
+ ast_set_flag(moh, MOH_QUIET);
+
+ moh->srcfd = -1;
+#ifdef HAVE_ZAPTEL
+ /* Open /dev/zap/pseudo for timing... Is
+ there a better, yet reliable way to do this? */
+ moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
+ if (moh->pseudofd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
+ } else {
+ x = 320;
+ ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
+ }
+#else
+ moh->pseudofd = -1;
+#endif
+ if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
+ ast_log(LOG_WARNING, "Unable to create moh...\n");
+ if (moh->pseudofd > -1)
+ close(moh->pseudofd);
+ ast_moh_free_class(&moh);
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
+ ast_moh_free_class(&moh);
+ return -1;
+ }
+
+ AST_RWLIST_WRLOCK(&mohclasses);
+ AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ return 0;
+}
+
+static void local_ast_moh_cleanup(struct ast_channel *chan)
+{
+ struct moh_files_state *state = chan->music_state;
+
+ if (state) {
+ if (state->class->realtime) {
+ if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
+ /* We are cleaning out cached RT class, we should remove it from list, if no one else using it */
+ if (!(state->class->inuse)) {
+ /* Remove this class from list */
+ AST_RWLIST_WRLOCK(&mohclasses);
+ AST_RWLIST_REMOVE(&mohclasses, state->class, list);
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ /* Free some memory */
+ ast_moh_destroy_one(state->class);
+ }
+ } else {
+ ast_moh_destroy_one(state->class);
+ }
+ }
+ ast_free(chan->music_state);
+ chan->music_state = NULL;
+ }
+}
+
+static struct mohclass *moh_class_malloc(void)
+{
+ struct mohclass *class;
+
+ if ((class = ast_calloc(1, sizeof(*class)))) {
+ class->format = AST_FORMAT_SLINEAR;
+ class->realtime = 0;
+ }
+
+ return class;
+}
+
+static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
+{
+ struct mohclass *mohclass = NULL;
+ struct ast_variable *var = NULL;
+ struct ast_variable *tmp = NULL;
+ struct moh_files_state *state = chan->music_state;
+#ifdef HAVE_ZAPTEL
+ int x;
+#endif
+
+ /* The following is the order of preference for which class to use:
+ * 1) The channels explicitly set musicclass, which should *only* be
+ * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
+ * 2) The mclass argument. If a channel is calling ast_moh_start() as the
+ * result of receiving a HOLD control frame, this should be the
+ * payload that came with the frame.
+ * 3) The interpclass argument. This would be from the mohinterpret
+ * option from channel drivers. This is the same as the old musicclass
+ * option.
+ * 4) The default class.
+ */
+
+ /* First, let's check in memory for static and cached RT classes */
+ AST_RWLIST_RDLOCK(&mohclasses);
+ if (!ast_strlen_zero(chan->musicclass))
+ mohclass = get_mohbyname(chan->musicclass, 1);
+ if (!mohclass && !ast_strlen_zero(mclass))
+ mohclass = get_mohbyname(mclass, 1);
+ if (!mohclass && !ast_strlen_zero(interpclass))
+ mohclass = get_mohbyname(interpclass, 1);
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ /* If no moh class found in memory, then check RT */
+ if (!mohclass && ast_check_realtime("musiconhold")) {
+ if (!ast_strlen_zero(chan->musicclass)) {
+ var = ast_load_realtime("musiconhold", "name", chan->musicclass, NULL);
+ }
+ if (!var && !ast_strlen_zero(mclass))
+ var = ast_load_realtime("musiconhold", "name", mclass, NULL);
+ if (!var && !ast_strlen_zero(interpclass))
+ var = ast_load_realtime("musiconhold", "name", interpclass, NULL);
+ if (!var)
+ var = ast_load_realtime("musiconhold", "name", "default", NULL);
+ if (var && (mohclass = moh_class_malloc())) {
+ mohclass->realtime = 1;
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "name"))
+ ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
+ else if (!strcasecmp(tmp->name, "mode"))
+ ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode));
+ else if (!strcasecmp(tmp->name, "directory"))
+ ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
+ else if (!strcasecmp(tmp->name, "application"))
+ ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
+ else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
+ mohclass->digit = *tmp->value;
+ else if (!strcasecmp(tmp->name, "random"))
+ ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
+ else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
+ ast_set_flag(mohclass, MOH_RANDOMIZE);
+ else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha"))
+ ast_set_flag(mohclass, MOH_SORTALPHA);
+ else if (!strcasecmp(tmp->name, "format")) {
+ mohclass->format = ast_getformatbyname(tmp->value);
+ if (!mohclass->format) {
+ ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
+ mohclass->format = AST_FORMAT_SLINEAR;
+ }
+ }
+ }
+ ast_variables_destroy(var);
+ if (ast_strlen_zero(mohclass->dir)) {
+ if (!strcasecmp(mohclass->mode, "custom")) {
+ strcpy(mohclass->dir, "nodir");
+ } else {
+ ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
+ ast_free(mohclass);
+ return -1;
+ }
+ }
+ if (ast_strlen_zero(mohclass->mode)) {
+ ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
+ ast_free(mohclass);
+ return -1;
+ }
+ if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
+ ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
+ ast_free(mohclass);
+ return -1;
+ }
+
+ if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
+ /* CACHERTCLASSES enabled, let's add this class to default tree */
+ if (state && state->class) {
+ /* Class already exist for this channel */
+ ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
+ if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
+ /* we found RT class with the same name, seems like we should continue playing existing one */
+ ast_moh_free_class(&mohclass);
+ mohclass = state->class;
+ }
+ }
+ moh_register(mohclass, 0);
+ } else {
+
+ /* We don't register RT moh class, so let's init it manualy */
+
+ time(&mohclass->start);
+ mohclass->start -= respawn_time;
+
+ if (!strcasecmp(mohclass->mode, "files")) {
+ if (!moh_scan_files(mohclass)) {
+ ast_moh_free_class(&mohclass);
+ return -1;
+ }
+ if (strchr(mohclass->args, 'r'))
+ ast_set_flag(mohclass, MOH_RANDOMIZE);
+ } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {
+
+ if (!strcasecmp(mohclass->mode, "custom"))
+ ast_set_flag(mohclass, MOH_CUSTOM);
+ else if (!strcasecmp(mohclass->mode, "mp3nb"))
+ ast_set_flag(mohclass, MOH_SINGLE);
+ else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
+ ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
+ else if (!strcasecmp(mohclass->mode, "quietmp3"))
+ ast_set_flag(mohclass, MOH_QUIET);
+
+ mohclass->srcfd = -1;
+#ifdef HAVE_ZAPTEL
+ /* Open /dev/zap/pseudo for timing... Is
+ there a better, yet reliable way to do this? */
+ mohclass->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
+ if (mohclass->pseudofd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
+ } else {
+ x = 320;
+ ioctl(mohclass->pseudofd, ZT_SET_BLOCKSIZE, &x);
+ }
+#else
+ mohclass->pseudofd = -1;
+#endif
+ /* Let's check if this channel already had a moh class before */
+ if (state && state->class) {
+ /* Class already exist for this channel */
+ ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
+ if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
+ /* we found RT class with the same name, seems like we should continue playing existing one */
+ ast_moh_free_class(&mohclass);
+ mohclass = state->class;
+
+ }
+ } else {
+ if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
+ ast_log(LOG_WARNING, "Unable to create moh...\n");
+ if (mohclass->pseudofd > -1)
+ close(mohclass->pseudofd);
+ ast_moh_free_class(&mohclass);
+ return -1;
+ }
+ }
+ } else {
+ ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
+ ast_moh_free_class(&mohclass);
+ return -1;
+ }
+
+ }
+
+ } else if (var)
+ ast_variables_destroy(var);
+ }
+
+
+
+ /* Requested MOH class not found, check for 'default' class in musiconhold.conf */
+ if (!mohclass) {
+ AST_RWLIST_RDLOCK(&mohclasses);
+ mohclass = get_mohbyname("default", 1);
+ if (mohclass)
+ ast_atomic_fetchadd_int(&mohclass->inuse, +1);
+ AST_RWLIST_UNLOCK(&mohclasses);
+ } else {
+ AST_RWLIST_RDLOCK(&mohclasses);
+ ast_atomic_fetchadd_int(&mohclass->inuse, +1);
+ AST_RWLIST_UNLOCK(&mohclasses);
+ }
+
+ if (!mohclass)
+ return -1;
+
+ ast_set_flag(chan, AST_FLAG_MOH);
+ if (mohclass->total_files) {
+ return ast_activate_generator(chan, &moh_file_stream, mohclass);
+ } else
+ return ast_activate_generator(chan, &mohgen, mohclass);
+}
+
+static void local_ast_moh_stop(struct ast_channel *chan)
+{
+ struct moh_files_state *state = chan->music_state;
+ ast_clear_flag(chan, AST_FLAG_MOH);
+ ast_deactivate_generator(chan);
+
+ if (state) {
+ if (chan->stream) {
+ ast_closestream(chan->stream);
+ chan->stream = NULL;
+ }
+ }
+}
+
+static int load_moh_classes(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ struct mohclass *class;
+ char *cat;
+ int numclasses = 0;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ cfg = ast_config_load("musiconhold.conf", config_flags);
+
+ if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ if (reload) {
+ AST_RWLIST_WRLOCK(&mohclasses);
+ AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
+ class->delete = 1;
+ }
+ AST_RWLIST_UNLOCK(&mohclasses);
+ }
+
+ ast_clear_flag(global_flags, AST_FLAGS_ALL);
+
+ cat = ast_category_browse(cfg, NULL);
+ for (; cat; cat = ast_category_browse(cfg, cat)) {
+ /* Setup common options from [general] section */
+ if (!strcasecmp(cat, "general")) {
+ var = ast_variable_browse(cfg, cat);
+ while (var) {
+ if (!strcasecmp(var->name, "cachertclasses")) {
+ ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
+ } else {
+ ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
+ }
+ var = var->next;
+ }
+ }
+ /* These names were deprecated in 1.4 and should not be used until after the next major release. */
+ if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
+ if (!(class = moh_class_malloc()))
+ break;
+
+ ast_copy_string(class->name, cat, sizeof(class->name));
+ var = ast_variable_browse(cfg, cat);
+ while (var) {
+ if (!strcasecmp(var->name, "mode"))
+ ast_copy_string(class->mode, var->value, sizeof(class->mode));
+ else if (!strcasecmp(var->name, "directory"))
+ ast_copy_string(class->dir, var->value, sizeof(class->dir));
+ else if (!strcasecmp(var->name, "application"))
+ ast_copy_string(class->args, var->value, sizeof(class->args));
+ else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
+ class->digit = *var->value;
+ else if (!strcasecmp(var->name, "random"))
+ ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
+ else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
+ ast_set_flag(class, MOH_RANDOMIZE);
+ else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha"))
+ ast_set_flag(class, MOH_SORTALPHA);
+ else if (!strcasecmp(var->name, "format")) {
+ class->format = ast_getformatbyname(var->value);
+ if (!class->format) {
+ ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
+ class->format = AST_FORMAT_SLINEAR;
+ }
+ }
+ var = var->next;
+ }
+
+ if (ast_strlen_zero(class->dir)) {
+ if (!strcasecmp(class->mode, "custom")) {
+ strcpy(class->dir, "nodir");
+ } else {
+ ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
+ ast_free(class);
+ continue;
+ }
+ }
+ if (ast_strlen_zero(class->mode)) {
+ ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
+ ast_free(class);
+ continue;
+ }
+ if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
+ ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
+ ast_free(class);
+ continue;
+ }
+
+ /* Don't leak a class when it's already registered */
+ moh_register(class, reload);
+
+ numclasses++;
+ }
+ }
+
+ ast_config_destroy(cfg);
+
+ return numclasses;
+}
+
+static int ast_moh_destroy_one(struct mohclass *moh)
+{
+ char buff[8192];
+ int bytes, tbytes = 0, stime = 0, pid = 0;
+
+ if (moh) {
+ if (moh->pid > 1) {
+ ast_debug(1, "killing %d!\n", moh->pid);
+ stime = time(NULL) + 2;
+ pid = moh->pid;
+ moh->pid = 0;
+ /* Back when this was just mpg123, SIGKILL was fine. Now we need
+ * to give the process a reason and time enough to kill off its
+ * children. */
+ kill(pid, SIGHUP);
+ usleep(100000);
+ kill(pid, SIGTERM);
+ usleep(100000);
+ kill(pid, SIGKILL);
+ while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
+ tbytes = tbytes + bytes;
+ ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
+ close(moh->srcfd);
+ }
+ ast_moh_free_class(&moh);
+ }
+
+ return 0;
+}
+
+static void ast_moh_destroy(void)
+{
+ struct mohclass *moh;
+
+ ast_verb(2, "Destroying musiconhold processes\n");
+
+ AST_RWLIST_WRLOCK(&mohclasses);
+ while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
+ ast_moh_destroy_one(moh);
+ }
+ AST_RWLIST_UNLOCK(&mohclasses);
+}
+
+static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "moh reload";
+ e->usage =
+ "Usage: moh reload\n"
+ " Reloads the MusicOnHold module.\n"
+ " Alias for 'module reload res_musiconhold.so'\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ reload();
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i;
+ struct mohclass *class;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "moh show files";
+ e->usage =
+ "Usage: moh show files\n"
+ " Lists all loaded file-based MusicOnHold classes and their\n"
+ " files.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&mohclasses);
+ AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
+ if (!class->total_files)
+ continue;
+
+ ast_cli(a->fd, "Class: %s\n", class->name);
+ for (i = 0; i < class->total_files; i++)
+ ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
+ }
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct mohclass *class;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "moh show classes";
+ e->usage =
+ "Usage: moh show classes\n"
+ " Lists all MusicOnHold classes.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&mohclasses);
+ AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
+ ast_cli(a->fd, "Class: %s\n", class->name);
+ ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
+ ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
+ ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
+ if (class->digit)
+ ast_cli(a->fd, "\tDigit: %c\n", class->digit);
+ if (ast_test_flag(class, MOH_CUSTOM))
+ ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
+ if (strcasecmp(class->mode, "files"))
+ ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
+ }
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_moh[] = {
+ AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
+ AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
+ AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes")
+};
+
+static int init_classes(int reload)
+{
+ struct mohclass *moh;
+
+ if (!load_moh_classes(reload)) /* Load classes from config */
+ return 0; /* Return if nothing is found */
+
+ AST_RWLIST_WRLOCK(&mohclasses);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
+ if (reload && moh->delete) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ if (!moh->inuse)
+ ast_moh_destroy_one(moh);
+ } else if (moh->total_files) {
+ if (moh_scan_files(moh) <= 0) {
+ ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name);
+ moh->delete = 1;
+ AST_LIST_REMOVE_CURRENT(list);
+ if (!moh->inuse)
+ ast_moh_destroy_one(moh);
+ }
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+ AST_RWLIST_UNLOCK(&mohclasses);
+
+ return 1;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
+ ast_register_atexit(ast_moh_destroy);
+ ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
+ if (!res)
+ res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
+ if (!res)
+ res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
+ if (!res)
+ res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
+ if (!res)
+ res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
+
+ if (!init_classes(0)) { /* No music classes configured, so skip it */
+ ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
+ } else {
+ ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int reload(void)
+{
+ if (init_classes(1))
+ ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return -1;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_odbc.c b/trunk/res/res_odbc.c
new file mode 100644
index 000000000..d030de142
--- /dev/null
+++ b/trunk/res/res_odbc.c
@@ -0,0 +1,778 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * res_odbc.c <ODBC resource manager>
+ * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.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 ODBC resource manager
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * \arg See also: \ref cdr_odbc
+ */
+
+/*** MODULEINFO
+ <depend>unixodbc</depend>
+ <depend>ltdl</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/res_odbc.h"
+
+struct odbc_class
+{
+ AST_LIST_ENTRY(odbc_class) list;
+ char name[80];
+ char dsn[80];
+ char *username;
+ char *password;
+ char sanitysql[256];
+ SQLHENV env;
+ unsigned int haspool:1; /* Boolean - TDS databases need this */
+ unsigned int limit:10; /* Gives a limit of 1023 maximum */
+ unsigned int count:10; /* Running count of pooled connections */
+ unsigned int delme:1; /* Purge the class */
+ unsigned int backslash_is_escape:1; /* On this database, the backslash is a native escape sequence */
+ AST_LIST_HEAD(, odbc_obj) odbc_obj;
+};
+
+AST_LIST_HEAD_STATIC(odbc_list, odbc_class);
+
+static odbc_status odbc_obj_connect(struct odbc_obj *obj);
+static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
+static int odbc_register_class(struct odbc_class *class, int connect);
+
+
+SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
+{
+ int attempt;
+ SQLHSTMT stmt;
+
+ for (attempt = 0; attempt < 2; attempt++) {
+ stmt = exec_cb(obj, data);
+
+ if (stmt) {
+ break;
+ } else {
+ obj->up = 0;
+ ast_log(LOG_WARNING, "SQL Exec Direct failed. Attempting a reconnect...\n");
+
+ odbc_obj_disconnect(obj);
+ odbc_obj_connect(obj);
+ }
+ }
+
+ return stmt;
+}
+
+SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
+{
+ int res = 0, i, attempt;
+ SQLINTEGER nativeerror=0, numfields=0;
+ SQLSMALLINT diagbytes=0;
+ unsigned char state[10], diagnostic[256];
+ SQLHSTMT stmt;
+
+ for (attempt = 0; attempt < 2; attempt++) {
+ /* This prepare callback may do more than just prepare -- it may also
+ * bind parameters, bind results, etc. The real key, here, is that
+ * when we disconnect, all handles become invalid for most databases.
+ * We must therefore redo everything when we establish a new
+ * connection. */
+ stmt = prepare_cb(obj, data);
+
+ if (stmt) {
+ res = SQLExecute(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
+ if (res == SQL_ERROR) {
+ SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
+ for (i = 0; i < numfields; i++) {
+ SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
+ ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
+ if (i > 10) {
+ ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
+ break;
+ }
+ }
+ }
+
+ ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ stmt = NULL;
+
+ obj->up = 0;
+ /*
+ * While this isn't the best way to try to correct an error, this won't automatically
+ * fail when the statement handle invalidates.
+ */
+ ast_odbc_sanity_check(obj);
+ continue;
+ }
+ break;
+ } else if (attempt == 0)
+ ast_odbc_sanity_check(obj);
+ }
+
+ return stmt;
+}
+
+int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
+{
+ int res = 0, i;
+ SQLINTEGER nativeerror=0, numfields=0;
+ SQLSMALLINT diagbytes=0;
+ unsigned char state[10], diagnostic[256];
+
+ res = SQLExecute(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
+ if (res == SQL_ERROR) {
+ SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
+ for (i = 0; i < numfields; i++) {
+ SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
+ ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
+ if (i > 10) {
+ ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
+ break;
+ }
+ }
+ }
+#if 0
+ /* This is a really bad method of trying to correct a dead connection. It
+ * only ever really worked with MySQL. It will not work with any other
+ * database, since most databases prepare their statements on the server,
+ * and if you disconnect, you invalidate the statement handle. Hence, if
+ * you disconnect, you're going to fail anyway, whether you try to execute
+ * a second time or not.
+ */
+ ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
+ ast_mutex_lock(&obj->lock);
+ obj->up = 0;
+ ast_mutex_unlock(&obj->lock);
+ odbc_obj_disconnect(obj);
+ odbc_obj_connect(obj);
+ res = SQLExecute(stmt);
+#endif
+ }
+
+ return res;
+}
+
+
+int ast_odbc_sanity_check(struct odbc_obj *obj)
+{
+ char *test_sql = "select 1";
+ SQLHSTMT stmt;
+ int res = 0;
+
+ if (!ast_strlen_zero(obj->parent->sanitysql))
+ test_sql = obj->parent->sanitysql;
+
+ if (obj->up) {
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ obj->up = 0;
+ } else {
+ res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ obj->up = 0;
+ } else {
+ res = SQLExecute(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ obj->up = 0;
+ }
+ }
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ }
+
+ if (!obj->up) { /* Try to reconnect! */
+ ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
+ odbc_obj_disconnect(obj);
+ odbc_obj_connect(obj);
+ }
+ return obj->up;
+}
+
+static int load_odbc_config(void)
+{
+ static char *cfg = "res_odbc.conf";
+ struct ast_config *config;
+ struct ast_variable *v;
+ char *cat;
+ const char *dsn, *username, *password, *sanitysql;
+ int enabled, pooling, limit, bse;
+ int connect = 0, res = 0;
+ struct ast_flags config_flags = { 0 };
+
+ struct odbc_class *new;
+
+ config = ast_config_load(cfg, config_flags);
+ if (!config) {
+ ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
+ return -1;
+ }
+ for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
+ if (!strcasecmp(cat, "ENV")) {
+ for (v = ast_variable_browse(config, cat); v; v = v->next) {
+ setenv(v->name, v->value, 1);
+ ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
+ }
+ } else {
+ /* Reset all to defaults for each class of odbc connections */
+ dsn = username = password = sanitysql = NULL;
+ enabled = 1;
+ connect = 0;
+ pooling = 0;
+ limit = 0;
+ bse = 1;
+ for (v = ast_variable_browse(config, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "pooling")) {
+ if (ast_true(v->value))
+ pooling = 1;
+ } else if (!strcasecmp(v->name, "limit")) {
+ sscanf(v->value, "%d", &limit);
+ if (ast_true(v->value) && !limit) {
+ ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
+ limit = 1023;
+ } else if (ast_false(v->value)) {
+ ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
+ enabled = 0;
+ break;
+ }
+ } else if (!strcasecmp(v->name, "enabled")) {
+ enabled = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "pre-connect")) {
+ connect = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "dsn")) {
+ dsn = v->value;
+ } else if (!strcasecmp(v->name, "username")) {
+ username = v->value;
+ } else if (!strcasecmp(v->name, "password")) {
+ password = v->value;
+ } else if (!strcasecmp(v->name, "sanitysql")) {
+ sanitysql = v->value;
+ } else if (!strcasecmp(v->name, "backslash_is_escape")) {
+ bse = ast_true(v->value);
+ }
+ }
+
+ if (enabled && !ast_strlen_zero(dsn)) {
+ new = ast_calloc(1, sizeof(*new));
+
+ if (!new) {
+ res = -1;
+ break;
+ }
+
+ if (cat)
+ ast_copy_string(new->name, cat, sizeof(new->name));
+ if (dsn)
+ ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
+ if (username)
+ new->username = ast_strdup(username);
+ if (password)
+ new->password = ast_strdup(password);
+ if (sanitysql)
+ ast_copy_string(new->sanitysql, sanitysql, sizeof(new->sanitysql));
+
+ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
+ res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
+ SQLFreeHandle(SQL_HANDLE_ENV, new->env);
+ return res;
+ }
+
+ if (pooling) {
+ new->haspool = pooling;
+ if (limit) {
+ new->limit = limit;
+ } else {
+ ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
+ new->limit = 5;
+ }
+ }
+
+ new->backslash_is_escape = bse ? 1 : 0;
+
+ odbc_register_class(new, connect);
+ ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
+ }
+ }
+ }
+ ast_config_destroy(config);
+ return res;
+}
+
+static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct odbc_class *class;
+ struct odbc_obj *current;
+ int length = 0;
+ int which = 0;
+ char *ret = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "odbc show";
+ e->usage =
+ "Usage: odbc show [class]\n"
+ " List settings of a particular ODBC class or,\n"
+ " if not specified, all classes.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos != 2)
+ return NULL;
+ length = strlen(a->word);
+ AST_LIST_LOCK(&odbc_list);
+ AST_LIST_TRAVERSE(&odbc_list, class, list) {
+ if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
+ ret = ast_strdup(class->name);
+ break;
+ }
+ }
+ if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
+ ret = ast_strdup("all");
+ }
+ AST_LIST_UNLOCK(&odbc_list);
+ return ret;
+ }
+
+ ast_cli(a->fd, "\nODBC DSN Settings\n");
+ ast_cli(a->fd, "-----------------\n\n");
+ AST_LIST_LOCK(&odbc_list);
+ AST_LIST_TRAVERSE(&odbc_list, class, list) {
+ if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
+ int count = 0;
+ ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
+
+ if (class->haspool) {
+ ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count);
+
+ AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
+ ast_cli(a->fd, " - Connection %d: %s\n", ++count, current->used ? "in use" : current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
+ }
+ } else {
+ /* Should only ever be one of these */
+ AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
+ ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
+ }
+ }
+ ast_cli(a->fd, "\n");
+ }
+ }
+ AST_LIST_UNLOCK(&odbc_list);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_odbc[] = {
+ AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
+};
+
+static int odbc_register_class(struct odbc_class *class, int connect)
+{
+ struct odbc_obj *obj;
+ if (class) {
+ AST_LIST_LOCK(&odbc_list);
+ AST_LIST_INSERT_HEAD(&odbc_list, class, list);
+ AST_LIST_UNLOCK(&odbc_list);
+
+ if (connect) {
+ /* Request and release builds a connection */
+ obj = ast_odbc_request_obj(class->name, 0);
+ if (obj)
+ ast_odbc_release_obj(obj);
+ }
+
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
+ return -1;
+ }
+}
+
+void ast_odbc_release_obj(struct odbc_obj *obj)
+{
+ /* For pooled connections, this frees the connection to be
+ * reused. For non-pooled connections, it does nothing. */
+ obj->used = 0;
+}
+
+int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
+{
+ return obj->parent->backslash_is_escape;
+}
+
+struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
+{
+ struct odbc_obj *obj = NULL;
+ struct odbc_class *class;
+
+ AST_LIST_LOCK(&odbc_list);
+ AST_LIST_TRAVERSE(&odbc_list, class, list) {
+ if (!strcmp(class->name, name))
+ break;
+ }
+ AST_LIST_UNLOCK(&odbc_list);
+
+ if (!class)
+ return NULL;
+
+ AST_LIST_LOCK(&class->odbc_obj);
+ if (class->haspool) {
+ /* Recycle connections before building another */
+ AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
+ if (! obj->used) {
+ obj->used = 1;
+ break;
+ }
+ }
+
+ if (!obj && (class->count < class->limit)) {
+ class->count++;
+ obj = ast_calloc(1, sizeof(*obj));
+ if (!obj) {
+ AST_LIST_UNLOCK(&class->odbc_obj);
+ return NULL;
+ }
+ ast_mutex_init(&obj->lock);
+ obj->parent = class;
+ if (odbc_obj_connect(obj) == ODBC_FAIL) {
+ ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
+ ast_mutex_destroy(&obj->lock);
+ ast_free(obj);
+ obj = NULL;
+ class->count--;
+ } else {
+ obj->used = 1;
+ AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
+ }
+ }
+ } else {
+ /* Non-pooled connection: multiple modules can use the same connection. */
+ AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
+ /* Non-pooled connection: if there is an entry, return it */
+ break;
+ }
+
+ if (!obj) {
+ /* No entry: build one */
+ obj = ast_calloc(1, sizeof(*obj));
+ if (!obj) {
+ AST_LIST_UNLOCK(&class->odbc_obj);
+ return NULL;
+ }
+ ast_mutex_init(&obj->lock);
+ obj->parent = class;
+ if (odbc_obj_connect(obj) == ODBC_FAIL) {
+ ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
+ ast_mutex_destroy(&obj->lock);
+ ast_free(obj);
+ obj = NULL;
+ } else {
+ AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
+ }
+ }
+ }
+ AST_LIST_UNLOCK(&class->odbc_obj);
+
+ if (obj && check) {
+ ast_odbc_sanity_check(obj);
+ }
+ return obj;
+}
+
+static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
+{
+ int res;
+ ast_mutex_lock(&obj->lock);
+
+ res = SQLDisconnect(obj->con);
+
+ if (res == ODBC_SUCCESS) {
+ ast_log(LOG_WARNING, "res_odbc: disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
+ } else {
+ ast_log(LOG_WARNING, "res_odbc: %s [%s] already disconnected\n",
+ obj->parent->name, obj->parent->dsn);
+ }
+ obj->up = 0;
+ ast_mutex_unlock(&obj->lock);
+ return ODBC_SUCCESS;
+}
+
+static odbc_status odbc_obj_connect(struct odbc_obj *obj)
+{
+ int res;
+ SQLINTEGER err;
+ short int mlen;
+ unsigned char msg[200], stat[10];
+#ifdef NEEDTRACE
+ SQLINTEGER enable = 1;
+ char *tracefile = "/tmp/odbc.trace";
+#endif
+ ast_mutex_lock(&obj->lock);
+
+ res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
+ ast_mutex_unlock(&obj->lock);
+ return ODBC_FAIL;
+ }
+ SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
+ SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
+#ifdef NEEDTRACE
+ SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
+ SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
+#endif
+
+ if (obj->up) {
+ odbc_obj_disconnect(obj);
+ ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
+ } else {
+ ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
+ }
+
+ res = SQLConnect(obj->con,
+ (SQLCHAR *) obj->parent->dsn, SQL_NTS,
+ (SQLCHAR *) obj->parent->username, SQL_NTS,
+ (SQLCHAR *) obj->parent->password, SQL_NTS);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
+ ast_mutex_unlock(&obj->lock);
+ ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
+ return ODBC_FAIL;
+ } else {
+ ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
+ obj->up = 1;
+ }
+
+ ast_mutex_unlock(&obj->lock);
+ return ODBC_SUCCESS;
+}
+
+static int reload(void)
+{
+ static char *cfg = "res_odbc.conf";
+ struct ast_config *config;
+ struct ast_variable *v;
+ char *cat;
+ const char *dsn, *username, *password, *sanitysql;
+ int enabled, pooling, limit, bse;
+ int connect = 0, res = 0;
+ struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
+
+ struct odbc_class *new, *class;
+ struct odbc_obj *current;
+
+ /* First, mark all to be purged */
+ AST_LIST_LOCK(&odbc_list);
+ AST_LIST_TRAVERSE(&odbc_list, class, list) {
+ class->delme = 1;
+ }
+
+ config = ast_config_load(cfg, config_flags);
+ if (config != NULL && config != CONFIG_STATUS_FILEUNCHANGED) {
+ for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
+ if (!strcasecmp(cat, "ENV")) {
+ for (v = ast_variable_browse(config, cat); v; v = v->next) {
+ setenv(v->name, v->value, 1);
+ ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
+ }
+ } else {
+ char *freeme = NULL;
+ /* Reset all to defaults for each class of odbc connections */
+ dsn = username = password = sanitysql = NULL;
+ enabled = 1;
+ connect = 0;
+ pooling = 0;
+ limit = 0;
+ bse = 1;
+ for (v = ast_variable_browse(config, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "pooling")) {
+ pooling = 1;
+ } else if (!strcasecmp(v->name, "limit")) {
+ sscanf(v->value, "%d", &limit);
+ if (ast_true(v->value) && !limit) {
+ ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
+ limit = 1023;
+ } else if (ast_false(v->value)) {
+ ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
+ enabled = 0;
+ break;
+ }
+ } else if (!strcasecmp(v->name, "enabled")) {
+ enabled = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "pre-connect")) {
+ connect = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "dsn")) {
+ dsn = v->value;
+ } else if (!strcasecmp(v->name, "username")) {
+ username = v->value;
+ } else if (!strcasecmp(v->name, "password")) {
+ password = v->value;
+ } else if (!strcasecmp(v->name, "sanitysql")) {
+ sanitysql = v->value;
+ } else if (!strcasecmp(v->name, "backslash_is_escape")) {
+ bse = ast_true(v->value);
+ }
+ }
+
+ if (enabled && !ast_strlen_zero(dsn)) {
+ /* First, check the list to see if it already exists */
+ AST_LIST_TRAVERSE(&odbc_list, class, list) {
+ if (!strcmp(class->name, cat)) {
+ class->delme = 0;
+ break;
+ }
+ }
+
+ if (class) {
+ new = class;
+ } else {
+ new = ast_calloc(1, sizeof(*new));
+ }
+
+ if (!new) {
+ res = -1;
+ break;
+ }
+
+ if (cat)
+ ast_copy_string(new->name, cat, sizeof(new->name));
+ if (dsn)
+ ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
+
+ /* Safely replace username */
+ if (class && class->username)
+ freeme = class->username;
+ if (username)
+ new->username = ast_strdup(username);
+ if (freeme) {
+ ast_free(freeme);
+ freeme = NULL;
+ }
+
+ /* Safely replace password */
+ if (class && class->password)
+ freeme = class->password;
+ if (password)
+ new->password = ast_strdup(password);
+ if (freeme) {
+ ast_free(freeme);
+ freeme = NULL;
+ }
+
+ if (sanitysql)
+ ast_copy_string(new->sanitysql, sanitysql, sizeof(new->sanitysql));
+
+ if (!class) {
+ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
+ res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
+
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
+ SQLFreeHandle(SQL_HANDLE_ENV, new->env);
+ AST_LIST_UNLOCK(&odbc_list);
+ return res;
+ }
+ }
+
+ if (pooling) {
+ new->haspool = pooling;
+ if (limit) {
+ new->limit = limit;
+ } else {
+ ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
+ new->limit = 5;
+ }
+ }
+
+ new->backslash_is_escape = bse;
+
+ if (class) {
+ ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
+ } else {
+ odbc_register_class(new, connect);
+ ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
+ }
+ }
+ }
+ }
+ ast_config_destroy(config);
+ }
+
+ /* Purge classes that we know can go away (pooled with 0, only) */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
+ if (class->delme && class->haspool && class->count == 0) {
+ while ((current = AST_LIST_REMOVE_HEAD(&class->odbc_obj, list))) {
+ odbc_obj_disconnect(current);
+ ast_mutex_destroy(&current->lock);
+ ast_free(current);
+ }
+
+ AST_LIST_REMOVE_CURRENT(list);
+ if (class->username)
+ ast_free(class->username);
+ if (class->password)
+ ast_free(class->password);
+ ast_free(class);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&odbc_list);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ /* Prohibit unloading */
+ return -1;
+}
+
+static int load_module(void)
+{
+ if (load_odbc_config() == -1)
+ return AST_MODULE_LOAD_DECLINE;
+ ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
+ ast_log(LOG_NOTICE, "res_odbc loaded.\n");
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC resource",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_phoneprov.c b/trunk/res/res_phoneprov.c
new file mode 100644
index 000000000..bffe1a2df
--- /dev/null
+++ b/trunk/res/res_phoneprov.c
@@ -0,0 +1,1033 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Matthew Brooks <mbrooks@digium.com>
+ * Terry Wilson <twilson@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
+ *
+ * \Phone provisioning application for the asterisk internal http server
+ *
+ * \author Matthew Brooks <mbrooks@digium.com>
+ * \author Terry Wilson <twilson@digium.com>
+ */
+
+#include "asterisk.h"
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 96773 $")
+
+#include "asterisk/file.h"
+#include "asterisk/paths.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cli.h"
+#include "asterisk/module.h"
+#include "asterisk/http.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/strings.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/options.h"
+#include "asterisk/config.h"
+#include "asterisk/acl.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/version.h"
+
+#ifdef LOW_MEMORY
+#define MAX_PROFILE_BUCKETS 1
+#define MAX_ROUTE_BUCKETS 1
+#else
+#define MAX_PROFILE_BUCKETS 17
+#define MAX_ROUTE_BUCKETS 563
+#endif /* LOW_MEMORY */
+
+#define VAR_BUF_SIZE 4096
+
+/*! \brief for use in lookup_iface */
+static struct in_addr __ourip = { .s_addr = 0x00000000, };
+
+/* \note This enum and the pp_variable_list must be in the same order or
+ * bad things happen! */
+enum pp_variables {
+ PP_MACADDRESS,
+ PP_USERNAME,
+ PP_FULLNAME,
+ PP_SECRET,
+ PP_LABEL,
+ PP_CALLERID,
+ PP_TIMEZONE,
+ PP_VAR_LIST_LENGTH, /* This entry must always be the last in the list */
+};
+
+/*! \brief Lookup table to translate between users.conf property names and
+ * variables for use in phoneprov templates */
+static const struct pp_variable_lookup {
+ enum pp_variables id;
+ const char * const user_var;
+ const char * const template_var;
+} pp_variable_list[] = {
+ { PP_MACADDRESS, "macaddress", "MAC" },
+ { PP_USERNAME, "username", "USERNAME" },
+ { PP_FULLNAME, "fullname", "DISPLAY_NAME" },
+ { PP_SECRET, "secret", "SECRET" },
+ { PP_LABEL, "label", "LABEL" },
+ { PP_CALLERID, "cid_number", "CALLERID" },
+ { PP_TIMEZONE, "timezone", "TIMEZONE" },
+};
+
+/*! \brief structure to hold file data */
+struct phoneprov_file {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(format); /*!< After variable substitution, becomes route->uri */
+ AST_STRING_FIELD(template); /*!< Template/physical file location */
+ AST_STRING_FIELD(mime_type);/*!< Mime-type of the file */
+ );
+ AST_LIST_ENTRY(phoneprov_file) entry;
+};
+
+/*! \brief structure to hold phone profiles read from phoneprov.conf */
+struct phone_profile {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name); /*!< Name of phone profile */
+ AST_STRING_FIELD(default_mime_type); /*!< Default mime type if it isn't provided */
+ AST_STRING_FIELD(staticdir); /*!< Subdirectory that static files are stored in */
+ );
+ struct varshead *headp; /*!< List of variables set with 'setvar' in phoneprov.conf */
+ AST_LIST_HEAD_NOLOCK(, phoneprov_file) static_files; /*!< List of static files */
+ AST_LIST_HEAD_NOLOCK(, phoneprov_file) dynamic_files; /*!< List of dynamic files */
+};
+
+/*! \brief structure to hold users read from users.conf */
+struct user {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name); /*!< Name of user */
+ AST_STRING_FIELD(macaddress); /*!< Mac address of user's phone */
+ );
+ struct phone_profile *profile; /*!< Profile the phone belongs to */
+ struct varshead *headp; /*!< List of variables to substitute into templates */
+ AST_LIST_ENTRY(user) entry;
+};
+
+/*! \brief structure to hold http routes (valid URIs, and the files they link to) */
+struct http_route {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(uri); /*!< The URI requested */
+ );
+ struct phoneprov_file *file; /*!< The file that links to the URI */
+ struct user *user; /*!< The user that has variables to substitute into the file
+ * NULL in the case of a static route */
+};
+
+static struct ao2_container *profiles;
+static struct ao2_container *http_routes;
+static AST_RWLIST_HEAD_STATIC(users, user);
+
+/*! \brief Extensions whose mime types we think we know */
+static struct {
+ char *ext;
+ char *mtype;
+} mimetypes[] = {
+ { "png", "image/png" },
+ { "xml", "text/xml" },
+ { "jpg", "image/jpeg" },
+ { "js", "application/x-javascript" },
+ { "wav", "audio/x-wav" },
+ { "mp3", "audio/mpeg" },
+};
+
+char global_server[80] = ""; /*!< Server to substitute into templates */
+char global_serverport[6] = ""; /*!< Server port to substitute into templates */
+char global_default_profile[80] = ""; /*!< Default profile to use if one isn't specified */
+
+/*! \brief List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH */
+struct varshead global_variables;
+
+/*! \brief Return mime type based on extension */
+static char *ftype2mtype(const char *ftype)
+{
+ int x;
+
+ if (ast_strlen_zero(ftype))
+ return NULL;
+
+ for (x = 0;x < ARRAY_LEN(mimetypes);x++) {
+ if (!strcasecmp(ftype, mimetypes[x].ext))
+ return mimetypes[x].mtype;
+ }
+
+ return NULL;
+}
+
+/* iface is the interface (e.g. eth0); address is the return value */
+static int lookup_iface(const char *iface, struct in_addr *address)
+{
+ int mysock, res = 0;
+ struct ifreq ifr;
+ struct sockaddr_in *sin;
+
+ memset(&ifr, 0, sizeof(ifr));
+ ast_copy_string(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+
+ mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (mysock < 0) {
+ ast_log(LOG_ERROR, "Failed to create socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ res = ioctl(mysock, SIOCGIFADDR, &ifr);
+
+ close(mysock);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
+ memcpy(address, &__ourip, sizeof(__ourip));
+ return -1;
+ } else {
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ memcpy(address, &sin->sin_addr, sizeof(*address));
+ return 0;
+ }
+}
+
+static struct phone_profile *unref_profile(struct phone_profile *prof)
+{
+ ao2_ref(prof, -1);
+
+ return NULL;
+}
+
+/*! \brief Return a phone profile looked up by name */
+static struct phone_profile *find_profile(const char *name)
+{
+ struct phone_profile tmp = {
+ .name = name,
+ };
+
+ return ao2_find(profiles, &tmp, OBJ_POINTER);
+}
+
+static int profile_hash_fn(const void *obj, const int flags)
+{
+ const struct phone_profile *profile = obj;
+
+ return ast_str_hash(profile->name);
+}
+
+static int profile_cmp_fn(void *obj, void *arg, int flags)
+{
+ const struct phone_profile *profile1 = obj, *profile2 = arg;
+
+ return !strcasecmp(profile1->name, profile2->name) ? CMP_MATCH : 0;
+}
+
+static void delete_file(struct phoneprov_file *file)
+{
+ ast_string_field_free_memory(file);
+ free(file);
+}
+
+static void profile_destructor(void *obj)
+{
+ struct phone_profile *profile = obj;
+ struct phoneprov_file *file;
+ struct ast_var_t *var;
+
+ while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry)))
+ delete_file(file);
+
+ while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry)))
+ delete_file(file);
+
+ while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries)))
+ ast_var_delete(var);
+
+ free(profile->headp);
+ ast_string_field_free_memory(profile);
+}
+
+static struct http_route *unref_route(struct http_route *route)
+{
+ ao2_ref(route, -1);
+
+ return NULL;
+}
+
+static int routes_hash_fn(const void *obj, const int flags)
+{
+ const struct http_route *route = obj;
+
+ return ast_str_hash(route->uri);
+}
+
+static int routes_cmp_fn(void *obj, void *arg, int flags)
+{
+ const struct http_route *route1 = obj, *route2 = arg;
+
+ return !strcmp(route1->uri, route2->uri) ? CMP_MATCH : 0;
+}
+
+static void route_destructor(void *obj)
+{
+ struct http_route *route = obj;
+
+ ast_string_field_free_memory(route);
+}
+
+/*! \brief Read a TEXT file into a string and return the length */
+static int load_file(const char *filename, char **ret)
+{
+ int len = 0;
+ FILE *f;
+
+ if (!(f = fopen(filename, "r"))) {
+ *ret = NULL;
+ return -1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ if (!(*ret = ast_malloc(len + 1)))
+ return -2;
+
+ if (len != fread(*ret, sizeof(char), len, f)) {
+ free(*ret);
+ *ret = NULL;
+ return -3;
+ }
+
+ fclose(f);
+
+ (*ret)[len] = '\0';
+
+ return len;
+}
+
+/*! \brief Callback that is executed everytime an http request is received by this module */
+static struct ast_str *phoneprov_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+{
+ struct http_route *route;
+ struct http_route search_route = {
+ .uri = uri,
+ };
+ struct ast_str *result = ast_str_create(512);
+ char path[PATH_MAX];
+ char *file = NULL;
+ int len;
+ int fd;
+ char buf[256];
+ struct timeval tv = ast_tvnow();
+ struct ast_tm tm;
+
+ if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER)))
+ goto out404;
+
+ snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
+
+ if (!route->user) { /* Static file */
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto out500;
+
+ len = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
+ close(fd);
+ goto out500;
+ }
+
+ ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
+ fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
+ "Server: Asterisk/%s\r\n"
+ "Date: %s\r\n"
+ "Connection: close\r\n"
+ "Cache-Control: no-cache, no-store\r\n"
+ "Content-Length: %d\r\n"
+ "Content-Type: %s\r\n\r\n",
+ ast_get_version(), buf, len, route->file->mime_type);
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0)
+ fwrite(buf, 1, len, ser->f);
+
+ close(fd);
+ route = unref_route(route);
+ return NULL;
+ } else { /* Dynamic file */
+ int bufsize;
+ char *tmp;
+
+ len = load_file(path, &file);
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
+ if (file)
+ ast_free(file);
+ goto out500;
+ }
+
+ if (!file)
+ goto out500;
+
+ /* XXX This is a hack -- maybe sum length of all variables in route->user->headp and add that? */
+ bufsize = len + VAR_BUF_SIZE;
+
+ /* malloc() instead of alloca() here, just in case the file is bigger than
+ * we have enough stack space for. */
+ if (!(tmp = ast_calloc(1, bufsize))) {
+ if (file)
+ ast_free(file);
+ goto out500;
+ }
+
+ /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to
+ * the IP address we are listening on that the phone contacted for this config file */
+ if (ast_strlen_zero(global_server)) {
+ struct sockaddr name;
+ socklen_t namelen = sizeof(name);
+ int res;
+
+ if ((res = getsockname(ser->fd, &name, &namelen)))
+ ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
+ else {
+ struct ast_var_t *var;
+
+ if ((var = ast_var_assign("SERVER", ast_inet_ntoa(((struct sockaddr_in *)&name)->sin_addr))))
+ AST_LIST_INSERT_TAIL(route->user->headp, var, entries);
+ }
+ }
+
+ pbx_substitute_variables_varshead(route->user->headp, file, tmp, bufsize);
+
+ if (file)
+ ast_free(file);
+
+ ast_str_append(&result, 0,
+ "Content-Type: %s\r\n"
+ "Content-length: %d\r\n"
+ "\r\n"
+ "%s", route->file->mime_type, (int) strlen(tmp), tmp);
+
+ if (tmp)
+ ast_free(tmp);
+
+ route = unref_route(route);
+
+ return result;
+ }
+
+out404:
+ *status = 404;
+ *title = strdup("Not Found");
+ *contentlength = 0;
+ return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
+
+out500:
+ route = unref_route(route);
+ *status = 500;
+ *title = strdup("Internal Server Error");
+ *contentlength = 0;
+ return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
+}
+
+/*! \brief Build a route structure and add it to the list of available http routes
+ \param pp_file File to link to the route
+ \param user User to link to the route (NULL means static route)
+ \param uri URI of the route
+*/
+static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri)
+{
+ struct http_route *route;
+
+ if (!(route = ao2_alloc(sizeof(*route), route_destructor)))
+ return;
+
+ if (ast_string_field_init(route, 32)) {
+ ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
+ route = unref_route(route);
+ return;
+ }
+
+ ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
+ route->user = user;
+ route->file = pp_file;
+
+ ao2_link(http_routes, route);
+
+ route = unref_route(route);
+}
+
+/*! \brief Build a phone profile and add it to the list of phone profiles
+ \param name the name of the profile
+ \param v ast_variable from parsing phoneprov.conf
+*/
+static void build_profile(const char *name, struct ast_variable *v)
+{
+ struct phone_profile *profile;
+ struct ast_var_t *var;
+
+ if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor)))
+ return;
+
+ if (ast_string_field_init(profile, 32)) {
+ profile = unref_profile(profile);
+ return;
+ }
+
+ if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) {
+ profile = unref_profile(profile);
+ return;
+ }
+
+ AST_LIST_HEAD_INIT_NOLOCK(&profile->static_files);
+ AST_LIST_HEAD_INIT_NOLOCK(&profile->dynamic_files);
+
+ ast_string_field_set(profile, name, name);
+ for (; v; v = v->next) {
+ if (!strcasecmp(v->name, "mime_type")) {
+ ast_string_field_set(profile, default_mime_type, v->value);
+ } else if (!strcasecmp(v->name, "setvar")) {
+ struct ast_var_t *var;
+ char *value_copy = ast_strdupa(v->value);
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(varname);
+ AST_APP_ARG(varval);
+ );
+
+ AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
+ do {
+ if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
+ break;
+ args.varname = ast_strip(args.varname);
+ args.varval = ast_strip(args.varval);
+ if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
+ break;
+ if ((var = ast_var_assign(args.varname, args.varval)))
+ AST_LIST_INSERT_TAIL(profile->headp, var, entries);
+ } while (0);
+ } else if (!strcasecmp(v->name, "staticdir")) {
+ ast_string_field_set(profile, staticdir, v->value);
+ } else {
+ struct phoneprov_file *pp_file;
+ char *file_extension;
+ char *value_copy = ast_strdupa(v->value);
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(mimetype);
+ );
+
+ if (!(pp_file = ast_calloc(1, sizeof(*pp_file)))) {
+ profile = unref_profile(profile);
+ return;
+ }
+ if (ast_string_field_init(pp_file, 32)) {
+ ast_free(pp_file);
+ profile = unref_profile(profile);
+ return;
+ }
+
+ if ((file_extension = strrchr(pp_file->format, '.')))
+ file_extension++;
+
+ AST_STANDARD_APP_ARGS(args, value_copy);
+
+ /* Mime type order of preference
+ * 1) Specific mime-type defined for file in profile
+ * 2) Mime determined by extension
+ * 3) Default mime type specified in profile
+ * 4) text/plain
+ */
+ ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype, (S_OR(S_OR(ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
+
+ if (!strcasecmp(v->name, "static_file")) {
+ ast_string_field_set(pp_file, format, args.filename);
+ ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
+ AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
+ /* Add a route for the static files, as their filenames won't change per-user */
+ build_route(pp_file, NULL, NULL);
+ } else {
+ ast_string_field_set(pp_file, format, v->name);
+ ast_string_field_set(pp_file, template, args.filename);
+ AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
+ }
+ }
+ }
+
+ /* Append the global variables to the variables list for this profile.
+ * This is for convenience later, when we need to provide a single
+ * variable list for use in substitution. */
+ AST_LIST_TRAVERSE(&global_variables, var, entries) {
+ struct ast_var_t *new_var;
+ if ((new_var = ast_var_assign(var->name, var->value)))
+ AST_LIST_INSERT_TAIL(profile->headp, new_var, entries);
+ }
+
+ ao2_link(profiles, profile);
+
+ profile = unref_profile(profile);
+}
+
+/*! \brief Free all memory associated with a user */
+static void delete_user(struct user *user)
+{
+ struct ast_var_t *var;
+
+ while ((var = AST_LIST_REMOVE_HEAD(user->headp, entries)))
+ ast_var_delete(var);
+
+ ast_free(user->headp);
+ ast_string_field_free_memory(user);
+ user->profile = unref_profile(user->profile);
+ free(user);
+}
+
+/*! \brief Destroy entire user list */
+static void delete_users(void)
+{
+ struct user *user;
+
+ AST_RWLIST_WRLOCK(&users);
+ while ((user = AST_LIST_REMOVE_HEAD(&users, entry)))
+ delete_user(user);
+ AST_RWLIST_UNLOCK(&users);
+}
+
+/*! \brief Set all timezone-related variables based on a zone (i.e. America/New_York)
+ \param headp pointer to list of user variables
+ \param zone A time zone. NULL sets variables based on timezone of the machine
+*/
+static void set_timezone_variables(struct varshead *headp, const char *zone)
+{
+ time_t utc_time;
+ int dstenable;
+ time_t dststart;
+ time_t dstend;
+ struct ast_tm tm_info;
+ int tzoffset;
+ char buffer[21];
+ struct ast_var_t *var;
+ struct timeval tv;
+
+ time(&utc_time);
+ ast_get_dst_info(&utc_time, &dstenable, &dststart, &dstend, &tzoffset, zone);
+ snprintf(buffer, sizeof(buffer), "%d", tzoffset);
+ var = ast_var_assign("TZOFFSET", buffer);
+ if (var)
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+
+ if (!dstenable)
+ return;
+
+ if ((var = ast_var_assign("DST_ENABLE", "1")))
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+
+ tv.tv_sec = dststart;
+ ast_localtime(&tv, &tm_info, zone);
+
+ snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon+1);
+ if ((var = ast_var_assign("DST_START_MONTH", buffer)))
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+
+ snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
+ if ((var = ast_var_assign("DST_START_MDAY", buffer)))
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+
+ snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
+ if ((var = ast_var_assign("DST_START_HOUR", buffer)))
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+
+ tv.tv_sec = dstend;
+ ast_localtime(&tv, &tm_info, zone);
+
+ snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon + 1);
+ if ((var = ast_var_assign("DST_END_MONTH", buffer)))
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+
+ snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
+ if ((var = ast_var_assign("DST_END_MDAY", buffer)))
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+
+ snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
+ if ((var = ast_var_assign("DST_END_HOUR", buffer)))
+ AST_LIST_INSERT_TAIL(headp, var, entries);
+}
+
+/*! \brief Build and return a user structure based on gathered config data */
+static struct user *build_user(struct ast_config *cfg, const char *name, const char *mac, struct phone_profile *profile)
+{
+ struct user *user;
+ struct ast_var_t *var;
+ const char *tmp;
+ int i;
+
+ if (!(user = ast_calloc(1, sizeof(*user)))) {
+ profile = unref_profile(profile);
+ return NULL;
+ }
+
+ if (!(user->headp = ast_calloc(1, sizeof(*user->headp)))) {
+ profile = unref_profile(profile);
+ ast_free(user);
+ return NULL;
+ }
+
+ if (ast_string_field_init(user, 32)) {
+ profile = unref_profile(profile);
+ delete_user(user);
+ return NULL;
+ }
+
+ ast_string_field_set(user, name, name);
+ ast_string_field_set(user, macaddress, mac);
+ user->profile = profile; /* already ref counted by find_profile */
+
+ for (i = 0; i < PP_VAR_LIST_LENGTH; i++) {
+ tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var);
+
+ if (i == PP_TIMEZONE) {
+ /* perfectly ok if tmp is NULL, will set variables based on server's time zone */
+ set_timezone_variables(user->headp, tmp);
+ }
+
+ if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp)))
+ AST_LIST_INSERT_TAIL(user->headp, var, entries);
+ }
+
+ if (!ast_strlen_zero(global_server)) {
+ if ((var = ast_var_assign("SERVER", global_server)))
+ AST_LIST_INSERT_TAIL(user->headp, var, entries);
+ }
+
+ if (!ast_strlen_zero(global_serverport)) {
+ if ((var = ast_var_assign("SERVER_PORT", global_serverport)))
+ AST_LIST_INSERT_TAIL(user->headp, var, entries);
+ }
+
+ /* Append profile variables here, and substitute variables on profile
+ * setvars, so that we can use user specific variables in them */
+ AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
+ char expand_buf[VAR_BUF_SIZE] = {0,};
+ struct ast_var_t *var2;
+
+ pbx_substitute_variables_varshead(user->headp, var->value, expand_buf, sizeof(expand_buf));
+ if ((var2 = ast_var_assign(var->name, expand_buf)))
+ AST_LIST_INSERT_TAIL(user->headp, var2, entries);
+ }
+
+ return user;
+}
+
+/*! \brief Add an http route for dynamic files attached to the profile of the user */
+static int build_user_routes(struct user *user)
+{
+ struct phoneprov_file *pp_file;
+
+ AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
+ char expand_buf[VAR_BUF_SIZE] = { 0, };
+
+ pbx_substitute_variables_varshead(user->headp, pp_file->format, expand_buf, sizeof(expand_buf));
+ build_route(pp_file, user, expand_buf);
+ }
+
+ return 0;
+}
+
+/* \brief Parse config files and create appropriate structures */
+static int set_config(void)
+{
+ struct ast_config *cfg;
+ char *cat;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { 0 };
+
+ /* Try to grab the port from sip.conf. If we don't get it here, we'll set it
+ * to whatever is set in phoneprov.conf or default to 5060 */
+ if ((cfg = ast_config_load("sip.conf", config_flags))) {
+ ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport));
+ ast_config_destroy(cfg);
+ }
+
+ if (!(cfg = ast_config_load("phoneprov.conf", config_flags))) {
+ ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
+ return -1;
+ }
+
+ cat = NULL;
+ while ((cat = ast_category_browse(cfg, cat))) {
+ if (!strcasecmp(cat, "general")) {
+ for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "serveraddr"))
+ ast_copy_string(global_server, v->value, sizeof(global_server));
+ else if (!strcasecmp(v->name, "serveriface")) {
+ struct in_addr addr;
+ lookup_iface(v->value, &addr);
+ ast_copy_string(global_server, ast_inet_ntoa(addr), sizeof(global_server));
+ } else if (!strcasecmp(v->name, "serverport"))
+ ast_copy_string(global_serverport, v->value, sizeof(global_serverport));
+ else if (!strcasecmp(v->name, "default_profile"))
+ ast_copy_string(global_default_profile, v->value, sizeof(global_default_profile));
+ }
+ } else
+ build_profile(cat, ast_variable_browse(cfg, cat));
+ }
+
+ ast_config_destroy(cfg);
+
+ if (!(cfg = ast_config_load("users.conf", config_flags))) {
+ ast_log(LOG_WARNING, "Unable to load users.cfg\n");
+ return 0;
+ }
+
+ cat = NULL;
+ while ((cat = ast_category_browse(cfg, cat))) {
+ const char *tmp, *mac;
+ struct user *user;
+ struct phone_profile *profile;
+ struct ast_var_t *var;
+
+ if (!strcasecmp(cat, "general")) {
+ for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "vmexten")) {
+ if ((var = ast_var_assign("VOICEMAIL_EXTEN", v->value)))
+ AST_LIST_INSERT_TAIL(&global_variables, var, entries);
+ }
+ if (!strcasecmp(v->name, "localextenlength")) {
+ if ((var = ast_var_assign("EXTENSION_LENGTH", v->value)))
+ AST_LIST_INSERT_TAIL(&global_variables, var, entries);
+ }
+ }
+ }
+
+ if (!strcasecmp(cat, "authentication"))
+ continue;
+
+ if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp)))
+ continue;
+
+ if (!(mac = ast_variable_retrieve(cfg, cat, "macaddress"))) {
+ ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
+ continue;
+ }
+
+ tmp = S_OR(ast_variable_retrieve(cfg, cat, "profile"), global_default_profile);
+ if (ast_strlen_zero(tmp)) {
+ ast_log(LOG_WARNING, "No profile for user [%s] with mac '%s' - skipping\n", cat, mac);
+ continue;
+ }
+
+ if (!(profile = find_profile(tmp))) {
+ ast_log(LOG_WARNING, "Could not look up profile '%s' - skipping.\n", tmp);
+ continue;
+ }
+
+ if (!(user = build_user(cfg, cat, mac, profile))) {
+ ast_log(LOG_WARNING, "Could not create user %s - skipping.\n", cat);
+ continue;
+ }
+
+ if (build_user_routes(user)) {
+ ast_log(LOG_WARNING, "Could not create http routes for %s - skipping\n", user->name);
+ delete_user(user);
+ continue;
+ }
+
+ AST_RWLIST_WRLOCK(&users);
+ AST_RWLIST_INSERT_TAIL(&users, user, entry);
+ AST_RWLIST_UNLOCK(&users);
+ }
+
+ ast_config_destroy(cfg);
+
+ return 0;
+}
+
+/*! \brief Delete all http routes, freeing their memory */
+static void delete_routes(void)
+{
+ struct ao2_iterator i;
+ struct http_route *route;
+
+ i = ao2_iterator_init(http_routes, 0);
+ while ((route = ao2_iterator_next(&i))) {
+ ao2_unlink(http_routes, route);
+ route = unref_route(route);
+ }
+}
+
+/*! \brief Delete all phone profiles, freeing their memory */
+static void delete_profiles(void)
+{
+ struct ao2_iterator i;
+ struct phone_profile *profile;
+
+ i = ao2_iterator_init(profiles, 0);
+ while ((profile = ao2_iterator_next(&i))) {
+ ao2_unlink(profiles, profile);
+ profile = unref_profile(profile);
+ }
+}
+
+/*! \brief A dialplan function that can be used to print a string for each phoneprov user */
+static int pp_each_user_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *tmp, expand_buf[VAR_BUF_SIZE] = {0,};
+ struct user *user;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(string);
+ AST_APP_ARG(exclude_mac);
+ );
+ AST_STANDARD_APP_ARGS(args, data);
+
+ /* Fix data by turning %{ into ${ */
+ while ((tmp = strstr(args.string, "%{")))
+ *tmp = '$';
+
+ AST_RWLIST_RDLOCK(&users);
+ AST_RWLIST_TRAVERSE(&users, user, entry) {
+ if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac))
+ continue;
+ pbx_substitute_variables_varshead(user->headp, args.string, expand_buf, sizeof(expand_buf));
+ ast_build_string(&buf, &len, expand_buf);
+ }
+ AST_RWLIST_UNLOCK(&users);
+
+ return 0;
+}
+
+static struct ast_custom_function pp_each_user_function = {
+ .name = "PP_EACH_USER",
+ .synopsis = "Generate a string for each phoneprov user",
+ .syntax = "PP_EACH_USER(<string>|<exclude_mac>)",
+ .desc =
+ "Pass in a string, with phoneprov variables you want substituted in the format of\n"
+ "%{VARNAME}, and you will get the string rendered for each user in phoneprov\n"
+ "excluding ones with MAC address <exclude_mac>. Probably not useful outside of\n"
+ "res_phoneprov.\n"
+ "\nExample: ${PP_EACH_USER(<item><fn>%{DISPLAY_NAME}</fn></item>|${MAC})",
+ .read = pp_each_user_exec,
+};
+
+/*! \brief CLI command to list static and dynamic routes */
+static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-40.40s %-30.30s\n"
+ struct ao2_iterator i;
+ struct http_route *route;
+
+ switch(cmd) {
+ case CLI_INIT:
+ e->command = "phoneprov show routes";
+ e->usage =
+ "Usage: phoneprov show routes\n"
+ " Lists all registered phoneprov http routes.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ /* This currently iterates over routes twice, but it is the only place I've needed
+ * to really separate static an dynamic routes, so I've just left it this way. */
+ ast_cli(a->fd, "Static routes\n\n");
+ ast_cli(a->fd, FORMAT, "Relative URI", "Physical location");
+ i = ao2_iterator_init(http_routes, 0);
+ while ((route = ao2_iterator_next(&i))) {
+ if (!route->user)
+ ast_cli(a->fd, FORMAT, route->uri, route->file->template);
+ route = unref_route(route);
+ }
+
+ ast_cli(a->fd, "\nDynamic routes\n\n");
+ ast_cli(a->fd, FORMAT, "Relative URI", "Template");
+
+ i = ao2_iterator_init(http_routes, 0);
+ while ((route = ao2_iterator_next(&i))) {
+ if (route->user)
+ ast_cli(a->fd, FORMAT, route->uri, route->file->template);
+ route = unref_route(route);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry pp_cli[] = {
+ AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
+};
+
+static struct ast_http_uri phoneprovuri = {
+ .callback = phoneprov_callback,
+ .description = "Asterisk HTTP Phone Provisioning Tool",
+ .uri = "phoneprov",
+ .has_subtree = 1,
+};
+
+static int load_module(void)
+{
+ profiles = ao2_container_alloc(MAX_PROFILE_BUCKETS, profile_hash_fn, profile_cmp_fn);
+
+ http_routes = ao2_container_alloc(MAX_ROUTE_BUCKETS, routes_hash_fn, routes_cmp_fn);
+
+ AST_LIST_HEAD_INIT_NOLOCK(&global_variables);
+
+ ast_custom_function_register(&pp_each_user_function);
+ ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
+
+ set_config();
+ ast_http_uri_link(&phoneprovuri);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ struct ast_var_t *var;
+
+ ast_http_uri_unlink(&phoneprovuri);
+ ast_custom_function_unregister(&pp_each_user_function);
+ ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
+
+ delete_routes();
+ delete_users();
+ delete_profiles();
+ ao2_ref(profiles, -1);
+ ao2_ref(http_routes, -1);
+
+ while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries)))
+ ast_var_delete(var);
+
+ return 0;
+}
+
+static int reload(void)
+{
+ delete_routes();
+ delete_users();
+ delete_profiles();
+ set_config();
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "HTTP Phone Provisioning",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_realtime.c b/trunk/res/res_realtime.c
new file mode 100644
index 000000000..14ad28c40
--- /dev/null
+++ b/trunk/res/res_realtime.c
@@ -0,0 +1,129 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Anthony Minessale <anthmct@yahoo.com>
+ * 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 RealTime CLI
+ *
+ * \author Anthony Minessale <anthmct@yahoo.com>
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+
+
+static char *cli_realtime_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *header_format = "%30s %-30s\n";
+ struct ast_variable *var=NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "realtime load";
+ e->usage =
+ "Usage: realtime load <family> <colmatch> <value>\n"
+ " Prints out a list of variables using the RealTime driver.\n"
+ " You must supply a family name, a column to match on, and a value to match to.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+
+ if (a->argc < 5)
+ return CLI_SHOWUSAGE;
+
+ var = ast_load_realtime_all(a->argv[2], a->argv[3], a->argv[4], NULL);
+
+ if (var) {
+ ast_cli(a->fd, header_format, "Column Name", "Column Value");
+ ast_cli(a->fd, header_format, "--------------------", "--------------------");
+ while (var) {
+ ast_cli(a->fd, header_format, var->name, var->value);
+ var = var->next;
+ }
+ } else {
+ ast_cli(a->fd, "No rows found matching search criteria.\n");
+ }
+ return CLI_SUCCESS;
+}
+
+static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) {
+ int res = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "realtime update";
+ e->usage =
+ "Usage: realtime update <family> <colupdate> <newvalue> <colmatch> <valuematch>\n"
+ " Update a single variable using the RealTime driver.\n"
+ " You must supply a family name, a column to update on, a new value, column to match, and value to match.\n"
+ " Ex: realtime update sipfriends name bobsphone port 4343\n"
+ " will execute SQL as UPDATE sipfriends SET port = 4343 WHERE name = bobsphone\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+
+ if (a->argc < 7)
+ return CLI_SHOWUSAGE;
+
+ res = ast_update_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], NULL);
+
+ if(res < 0) {
+ ast_cli(a->fd, "Failed to update. Check the debug log for possible SQL related entries.\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Updated %d RealTime record%s.\n", res, ESS(res));
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_realtime[] = {
+ AST_CLI_DEFINE(cli_realtime_load, "Used to print out RealTime variables."),
+ AST_CLI_DEFINE(cli_realtime_update, "Used to update RealTime variables."),
+};
+
+static int unload_module(void)
+{
+ ast_cli_unregister_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
+ return 0;
+}
+
+static int load_module(void)
+{
+ ast_cli_register_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Data Lookup/Rewrite");
diff --git a/trunk/res/res_smdi.c b/trunk/res/res_smdi.c
new file mode 100644
index 000000000..1df720e83
--- /dev/null
+++ b/trunk/res/res_smdi.c
@@ -0,0 +1,746 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (C) 2005-2006, Digium, Inc.
+ *
+ * Matthew A. Nicholson <mnicholson@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 SMDI support for Asterisk.
+ * \author Matthew A. Nicholson <mnicholson@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <termios.h>
+#include <sys/time.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/smdi.h"
+#include "asterisk/config.h"
+#include "asterisk/astobj.h"
+#include "asterisk/io.h"
+
+/* Message expiry time in milliseconds */
+#define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */
+
+static const char config_file[] = "smdi.conf";
+
+static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
+static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
+
+static void *smdi_read(void *iface_p);
+static int smdi_load(int reload);
+
+struct module_symbols *me; /* initialized in load_module() */
+
+/*! \brief SMDI interface container. */
+struct ast_smdi_interface_container {
+ ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
+} smdi_ifaces;
+
+/*!
+ * \internal
+ * \brief Push an SMDI message to the back of an interface's message queue.
+ * \param iface a pointer to the interface to use.
+ * \param md_msg a pointer to the message to use.
+ */
+static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
+{
+ ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
+}
+
+/*!
+ * \internal
+ * \brief Push an SMDI message to the back of an interface's message queue.
+ * \param iface a pointer to the interface to use.
+ * \param mwi_msg a pointer to the message to use.
+ */
+static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
+{
+ ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
+}
+
+/*!
+ * \brief Set the MWI indicator for a mailbox.
+ * \param iface the interface to use.
+ * \param mailbox the mailbox to use.
+ */
+int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
+{
+ FILE *file;
+ int i;
+
+ if (!(file = fopen(iface->name, "w"))) {
+ ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
+ return 1;
+ }
+
+ ASTOBJ_WRLOCK(iface);
+
+ fprintf(file, "OP:MWI ");
+
+ for (i = 0; i < iface->msdstrip; i++)
+ fprintf(file, "0");
+
+ fprintf(file, "%s!\x04", mailbox);
+ fclose(file);
+
+ ASTOBJ_UNLOCK(iface);
+ ast_debug(1, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
+ return 0;
+}
+
+/*!
+ * \brief Unset the MWI indicator for a mailbox.
+ * \param iface the interface to use.
+ * \param mailbox the mailbox to use.
+ */
+int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
+{
+ FILE *file;
+ int i;
+
+ if (!(file = fopen(iface->name, "w"))) {
+ ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
+ return 1;
+ }
+
+ ASTOBJ_WRLOCK(iface);
+
+ fprintf(file, "RMV:MWI ");
+
+ for (i = 0; i < iface->msdstrip; i++)
+ fprintf(file, "0");
+
+ fprintf(file, "%s!\x04", mailbox);
+ fclose(file);
+
+ ASTOBJ_UNLOCK(iface);
+ ast_debug(1, "Sent MWI unset message for %s on %s\n", mailbox, iface->name);
+ return 0;
+}
+
+/*!
+ * \brief Put an SMDI message back in the front of the queue.
+ * \param iface a pointer to the interface to use.
+ * \param md_msg a pointer to the message to use.
+ *
+ * This function puts a message back in the front of the specified queue. It
+ * should be used if a message was popped but is not going to be processed for
+ * some reason, and the message needs to be returned to the queue.
+ */
+void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
+{
+ ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
+}
+
+/*!
+ * \brief Put an SMDI message back in the front of the queue.
+ * \param iface a pointer to the interface to use.
+ * \param mwi_msg a pointer to the message to use.
+ *
+ * This function puts a message back in the front of the specified queue. It
+ * should be used if a message was popped but is not going to be processed for
+ * some reason, and the message needs to be returned to the queue.
+ */
+void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
+{
+ ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
+}
+
+/*!
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ *
+ * This function pulls the first unexpired message from the SMDI message queue
+ * on the specified interface. It will purge all expired SMDI messages before
+ * returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages.
+ */
+struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
+{
+ struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
+ struct timeval now;
+ long elapsed = 0;
+
+ /* purge old messages */
+ now = ast_tvnow();
+ while (md_msg) {
+ elapsed = ast_tvdiff_ms(now, md_msg->timestamp);
+
+ if (elapsed > iface->msg_expiry) {
+ /* found an expired message */
+ ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
+ ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MD message queue. Message was %ld milliseconds too old.\n",
+ iface->name, elapsed - iface->msg_expiry);
+ md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
+ }
+ else {
+ /* good message, return it */
+ break;
+ }
+ }
+
+ return md_msg;
+}
+
+/*!
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ * \param timeout the time to wait before returning in milliseconds.
+ *
+ * This function pulls a message from the SMDI message queue on the specified
+ * interface. If no message is available this function will wait the specified
+ * amount of time before returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages and
+ * the timeout has expired.
+ */
+extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+ struct timeval start = ast_tvnow();
+ long diff = 0;
+ struct ast_smdi_md_message *msg;
+
+ while (diff < timeout) {
+
+ if ((msg = ast_smdi_md_message_pop(iface)))
+ return msg;
+
+ /* check timeout */
+ diff = ast_tvdiff_ms(ast_tvnow(), start);
+ }
+
+ return (ast_smdi_md_message_pop(iface));
+}
+
+/*!
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ *
+ * This function pulls the first unexpired message from the SMDI message queue
+ * on the specified interface. It will purge all expired SMDI messages before
+ * returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages.
+ */
+extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
+{
+ struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
+ struct timeval now = ast_tvnow();
+ long elapsed = 0;
+
+ /* purge old messages */
+ while (mwi_msg) {
+ elapsed = ast_tvdiff_ms(now, mwi_msg->timestamp);
+
+ if (elapsed > iface->msg_expiry) {
+ /* found an expired message */
+ ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+ ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MWI message queue. Message was %ld milliseconds too old.\n",
+ iface->name, elapsed - iface->msg_expiry);
+ mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
+ }
+ else {
+ /* good message, return it */
+ break;
+ }
+ }
+
+ return mwi_msg;
+}
+
+/*!
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ * \param timeout the time to wait before returning in milliseconds.
+ *
+ * This function pulls a message from the SMDI message queue on the specified
+ * interface. If no message is available this function will wait the specified
+ * amount of time before returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages and
+ * the timeout has expired.
+ */
+extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+ struct timeval start = ast_tvnow();
+ long diff = 0;
+ struct ast_smdi_mwi_message *msg;
+
+ while (diff < timeout) {
+
+ if ((msg = ast_smdi_mwi_message_pop(iface)))
+ return msg;
+
+ /* check timeout */
+ diff = ast_tvdiff_ms(ast_tvnow(), start);
+ }
+
+ return (ast_smdi_mwi_message_pop(iface));
+}
+
+/*!
+ * \brief Find an SMDI interface with the specified name.
+ * \param iface_name the name/port of the interface to search for.
+ *
+ * \return a pointer to the interface located or NULL if none was found. This
+ * actually returns an ASTOBJ reference and should be released using
+ * #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).
+ */
+extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
+{
+ return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
+}
+
+/*! \brief Read an SMDI message.
+ *
+ * \param iface_p the SMDI interface to read from.
+ *
+ * This function loops and reads from and SMDI interface. It must be stopped
+ * using pthread_cancel().
+ */
+static void *smdi_read(void *iface_p)
+{
+ struct ast_smdi_interface *iface = iface_p;
+ struct ast_smdi_md_message *md_msg;
+ struct ast_smdi_mwi_message *mwi_msg;
+ char c = '\0';
+ char *cp = NULL;
+ int i;
+ int start = 0;
+
+ /* read an smdi message */
+ while ((c = fgetc(iface->file))) {
+
+ /* check if this is the start of a message */
+ if (!start) {
+ if (c == 'M')
+ start = 1;
+ }
+ else { /* Determine if this is a MD or MWI message */
+ if(c == 'D') { /* MD message */
+ start = 0;
+
+ if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
+ ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
+ return NULL;
+ }
+
+ ASTOBJ_INIT(md_msg);
+
+ /* read the message desk number */
+ for(i = 0; i < SMDI_MESG_DESK_NUM_LEN; i++)
+ md_msg->mesg_desk_num[i] = fgetc(iface->file);
+
+ md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0';
+
+ /* read the message desk terminal number */
+ for(i = 0; i < SMDI_MESG_DESK_TERM_LEN; i++)
+ md_msg->mesg_desk_term[i] = fgetc(iface->file);
+
+ md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0';
+
+ /* read the message type */
+ md_msg->type = fgetc(iface->file);
+
+ /* read the forwarding station number (may be blank) */
+ cp = &md_msg->fwd_st[0];
+ for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
+ if((c = fgetc(iface->file)) == ' ') {
+ *cp = '\0';
+ break;
+ }
+
+ /* store c in md_msg->fwd_st */
+ if( i >= iface->msdstrip)
+ *cp++ = c;
+ }
+
+ /* make sure the value is null terminated, even if this truncates it */
+ md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
+ cp = NULL;
+
+ /* read the calling station number (may be blank) */
+ cp = &md_msg->calling_st[0];
+ for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
+ if (!isdigit((c = fgetc(iface->file)))) {
+ *cp = '\0';
+ break;
+ }
+
+ /* store c in md_msg->calling_st */
+ if (i >= iface->msdstrip)
+ *cp++ = c;
+ }
+
+ /* make sure the value is null terminated, even if this truncates it */
+ md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
+ cp = NULL;
+
+ /* add the message to the message queue */
+ md_msg->timestamp = ast_tvnow();
+ ast_smdi_md_message_push(iface, md_msg);
+ ast_debug(1, "Recieved SMDI MD message on %s\n", iface->name);
+
+ ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
+
+ } else if(c == 'W') { /* MWI message */
+ start = 0;
+
+ if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
+ ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
+ return NULL;
+ }
+
+ ASTOBJ_INIT(mwi_msg);
+
+ /* discard the 'I' (from 'MWI') */
+ fgetc(iface->file);
+
+ /* read the forwarding station number (may be blank) */
+ cp = &mwi_msg->fwd_st[0];
+ for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
+ if ((c = fgetc(iface->file)) == ' ') {
+ *cp = '\0';
+ break;
+ }
+
+ /* store c in md_msg->fwd_st */
+ if (i >= iface->msdstrip)
+ *cp++ = c;
+ }
+
+ /* make sure the station number is null terminated, even if this will truncate it */
+ mwi_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
+ cp = NULL;
+
+ /* read the mwi failure cause */
+ for (i = 0; i < SMDI_MWI_FAIL_CAUSE_LEN; i++)
+ mwi_msg->cause[i] = fgetc(iface->file);
+
+ mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0';
+
+ /* add the message to the message queue */
+ mwi_msg->timestamp = ast_tvnow();
+ ast_smdi_mwi_message_push(iface, mwi_msg);
+ ast_debug(1, "Recieved SMDI MWI message on %s\n", iface->name);
+
+ ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+ } else {
+ ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
+ start = 0;
+ }
+ }
+ }
+
+ ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
+ ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
+ return NULL;
+}
+
+/*! \brief ast_smdi_md_message destructor. */
+void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
+{
+ ast_free(msg);
+}
+
+/*! \brief ast_smdi_mwi_message destructor. */
+void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
+{
+ ast_free(msg);
+}
+
+/*! \brief ast_smdi_interface destructor. */
+void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
+{
+ if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
+ pthread_cancel(iface->thread);
+ pthread_join(iface->thread, NULL);
+ }
+
+ iface->thread = AST_PTHREADT_STOP;
+
+ if(iface->file)
+ fclose(iface->file);
+
+ ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
+ ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
+ ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
+ ast_free(iface);
+
+ ast_module_unref(ast_module_info->self);
+}
+
+/*!
+ * \internal
+ * \brief Load and reload SMDI configuration.
+ * \param reload this should be 1 if we are reloading and 0 if not.
+ *
+ * This function loads/reloads the SMDI configuration and starts and stops
+ * interfaces accordingly.
+ *
+ * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
+ */
+static int smdi_load(int reload)
+{
+ struct ast_config *conf;
+ struct ast_variable *v;
+ struct ast_smdi_interface *iface = NULL;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ int res = 0;
+
+ /* Config options */
+ speed_t baud_rate = B9600; /* 9600 baud rate */
+ tcflag_t paritybit = PARENB; /* even parity checking */
+ tcflag_t charsize = CS7; /* seven bit characters */
+ int stopbits = 0; /* One stop bit */
+
+ int msdstrip = 0; /* strip zero digits */
+ long msg_expiry = SMDI_MSG_EXPIRY_TIME;
+
+ if (!(conf = ast_config_load(config_file, config_flags))) {
+ if (reload)
+ ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
+ else
+ ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
+ return 1;
+ } else if (conf == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ /* Mark all interfaces that we are listening on. We will unmark them
+ * as we find them in the config file, this way we know any interfaces
+ * still marked after we have finished parsing the config file should
+ * be stopped.
+ */
+ if (reload)
+ ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
+
+ for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
+ if (!strcasecmp(v->name, "baudrate")) {
+ if (!strcasecmp(v->value, "9600"))
+ baud_rate = B9600;
+ else if(!strcasecmp(v->value, "4800"))
+ baud_rate = B4800;
+ else if(!strcasecmp(v->value, "2400"))
+ baud_rate = B2400;
+ else if(!strcasecmp(v->value, "1200"))
+ baud_rate = B1200;
+ else {
+ ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
+ baud_rate = B9600;
+ }
+ } else if (!strcasecmp(v->name, "msdstrip")) {
+ if (!sscanf(v->value, "%d", &msdstrip)) {
+ ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
+ msdstrip = 0;
+ } else if (0 > msdstrip || msdstrip > 9) {
+ ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
+ msdstrip = 0;
+ }
+ } else if (!strcasecmp(v->name, "msgexpirytime")) {
+ if (!sscanf(v->value, "%ld", &msg_expiry)) {
+ ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
+ msg_expiry = SMDI_MSG_EXPIRY_TIME;
+ }
+ } else if (!strcasecmp(v->name, "paritybit")) {
+ if (!strcasecmp(v->value, "even"))
+ paritybit = PARENB;
+ else if (!strcasecmp(v->value, "odd"))
+ paritybit = PARENB | PARODD;
+ else if (!strcasecmp(v->value, "none"))
+ paritybit = ~PARENB;
+ else {
+ ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
+ paritybit = PARENB;
+ }
+ } else if (!strcasecmp(v->name, "charsize")) {
+ if (!strcasecmp(v->value, "7"))
+ charsize = CS7;
+ else if (!strcasecmp(v->value, "8"))
+ charsize = CS8;
+ else {
+ ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
+ charsize = CS7;
+ }
+ } else if (!strcasecmp(v->name, "twostopbits")) {
+ stopbits = ast_true(v->name);
+ } else if (!strcasecmp(v->name, "smdiport")) {
+ if (reload) {
+ /* we are reloading, check if we are already
+ * monitoring this interface, if we are we do
+ * not want to start it again. This also has
+ * the side effect of not updating different
+ * setting for the serial port, but it should
+ * be trivial to rewrite this section so that
+ * options on the port are changed without
+ * restarting the interface. Or the interface
+ * could be restarted with out emptying the
+ * queue. */
+ if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
+ ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
+ ASTOBJ_UNMARK(iface);
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ continue;
+ }
+ }
+
+ if (!(iface = ast_calloc(1, sizeof(*iface))))
+ continue;
+
+ ASTOBJ_INIT(iface);
+ ASTOBJ_CONTAINER_INIT(&iface->md_q);
+ ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
+
+ ast_copy_string(iface->name, v->value, sizeof(iface->name));
+
+ iface->thread = AST_PTHREADT_NULL;
+
+ if (!(iface->file = fopen(iface->name, "r"))) {
+ ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ continue;
+ }
+
+ iface->fd = fileno(iface->file);
+
+ /* Set the proper attributes for our serial port. */
+
+ /* get the current attributes from the port */
+ if (tcgetattr(iface->fd, &iface->mode)) {
+ ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ continue;
+ }
+
+ /* set the desired speed */
+ if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
+ ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ continue;
+ }
+
+ /* set the stop bits */
+ if (stopbits)
+ iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB; /* set two stop bits */
+ else
+ iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB; /* set one stop bit */
+
+ /* set the parity */
+ iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
+
+ /* set the character size */
+ iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
+
+ /* commit the desired attributes */
+ if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
+ ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ continue;
+ }
+
+ /* set the msdstrip */
+ iface->msdstrip = msdstrip;
+
+ /* set the message expiry time */
+ iface->msg_expiry = msg_expiry;
+
+ /* start the listner thread */
+ ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
+ if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
+ ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ continue;
+ }
+
+ ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ ast_module_ref(ast_module_info->self);
+ } else {
+ ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
+ }
+ }
+ ast_config_destroy(conf);
+
+ /* Prune any interfaces we should no longer monitor. */
+ if (reload)
+ ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
+
+ ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
+ /* TODO: this is bad, we need an ASTOBJ method for this! */
+ if (!smdi_ifaces.head)
+ res = 1;
+ ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ /* initialize our containers */
+ memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
+ ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
+
+ /* load the config and start the listener threads*/
+ res = smdi_load(0);
+ if (res < 0) {
+ return res;
+ } else if (res == 1) {
+ ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ } else
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ /* this destructor stops any running smdi_read threads */
+ ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
+
+ return 0;
+}
+
+static int reload(void)
+{
+ int res;
+
+ res = smdi_load(1);
+
+ if (res < 0) {
+ return res;
+ } else if (res == 1) {
+ ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
+ return 0;
+ } else
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/trunk/res/res_snmp.c b/trunk/res/res_snmp.c
new file mode 100644
index 000000000..71a79f209
--- /dev/null
+++ b/trunk/res/res_snmp.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2006 Voop as
+ * Thorsten Lockert <tholo@voop.as>
+ *
+ * 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 SNMP Agent / SubAgent support for Asterisk
+ *
+ * \author Thorsten Lockert <tholo@voop.as>
+ *
+ * \extref Uses the Net-SNMP libraries available at
+ * http://net-snmp.sourceforge.net/
+ */
+
+/*** MODULEINFO
+ <depend>netsnmp</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+
+#include "snmp/agent.h"
+
+#define MODULE_DESCRIPTION "SNMP [Sub]Agent for Asterisk"
+
+int res_snmp_agentx_subagent;
+int res_snmp_dont_stop;
+int res_snmp_enabled;
+
+static pthread_t thread = AST_PTHREADT_NULL;
+
+/*!
+ * \brief Load res_snmp.conf config file
+ * \return 1 on load, 0 file does not exist
+*/
+static int load_config(void)
+{
+ struct ast_variable *var;
+ struct ast_config *cfg;
+ struct ast_flags config_flags = { 0 };
+ char *cat;
+
+ res_snmp_enabled = 0;
+ res_snmp_agentx_subagent = 1;
+ cfg = ast_config_load("res_snmp.conf", config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Could not load res_snmp.conf\n");
+ return 0;
+ }
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
+ var = ast_variable_browse(cfg, cat);
+
+ if (strcasecmp(cat, "general") == 0) {
+ while (var) {
+ if (strcasecmp(var->name, "subagent") == 0) {
+ if (ast_true(var->value))
+ res_snmp_agentx_subagent = 1;
+ else if (ast_false(var->value))
+ res_snmp_agentx_subagent = 0;
+ else {
+ ast_log(LOG_ERROR, "Value '%s' does not evaluate to true or false.\n", var->value);
+ ast_config_destroy(cfg);
+ return 1;
+ }
+ } else if (strcasecmp(var->name, "enabled") == 0) {
+ res_snmp_enabled = ast_true(var->value);
+ } else {
+ ast_log(LOG_ERROR, "Unrecognized variable '%s' in category '%s'\n", var->name, cat);
+ ast_config_destroy(cfg);
+ return 1;
+ }
+ var = var->next;
+ }
+ } else {
+ ast_log(LOG_ERROR, "Unrecognized category '%s'\n", cat);
+ ast_config_destroy(cfg);
+ return 1;
+ }
+
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_config_destroy(cfg);
+ return 1;
+}
+
+static int load_module(void)
+{
+ if(!load_config())
+ return AST_MODULE_LOAD_DECLINE;
+
+ ast_verb(1, "Loading [Sub]Agent Module\n");
+
+ res_snmp_dont_stop = 1;
+ if (res_snmp_enabled)
+ return ast_pthread_create_background(&thread, NULL, agent_thread, NULL);
+ else
+ return 0;
+}
+
+static int unload_module(void)
+{
+ ast_verb(1, "Unloading [Sub]Agent Module\n");
+
+ res_snmp_dont_stop = 0;
+ return ((thread != AST_PTHREADT_NULL) ? pthread_join(thread, NULL) : 0);
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "SNMP [Sub]Agent for Asterisk",
+ .load = load_module,
+ .unload = unload_module,
+ );
diff --git a/trunk/res/res_speech.c b/trunk/res/res_speech.c
new file mode 100644
index 000000000..902955da1
--- /dev/null
+++ b/trunk/res/res_speech.c
@@ -0,0 +1,344 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Generic Speech Recognition API
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+#include "asterisk/speech.h"
+
+
+static AST_RWLIST_HEAD_STATIC(engines, ast_speech_engine);
+static struct ast_speech_engine *default_engine = NULL;
+
+/*! \brief Find a speech recognition engine of specified name, if NULL then use the default one */
+static struct ast_speech_engine *find_engine(char *engine_name)
+{
+ struct ast_speech_engine *engine = NULL;
+
+ /* If no name is specified -- use the default engine */
+ if (ast_strlen_zero(engine_name))
+ return default_engine;
+
+ AST_RWLIST_RDLOCK(&engines);
+ AST_RWLIST_TRAVERSE(&engines, engine, list) {
+ if (!strcasecmp(engine->name, engine_name)) {
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&engines);
+
+ return engine;
+}
+
+/*! \brief Activate a loaded (either local or global) grammar */
+int ast_speech_grammar_activate(struct ast_speech *speech, char *grammar_name)
+{
+ return (speech->engine->activate ? speech->engine->activate(speech, grammar_name) : -1);
+}
+
+/*! \brief Deactivate a loaded grammar on a speech structure */
+int ast_speech_grammar_deactivate(struct ast_speech *speech, char *grammar_name)
+{
+ return (speech->engine->deactivate ? speech->engine->deactivate(speech, grammar_name) : -1);
+}
+
+/*! \brief Load a local grammar on a speech structure */
+int ast_speech_grammar_load(struct ast_speech *speech, char *grammar_name, char *grammar)
+{
+ return (speech->engine->load ? speech->engine->load(speech, grammar_name, grammar) : -1);
+}
+
+/*! \brief Unload a local grammar from a speech structure */
+int ast_speech_grammar_unload(struct ast_speech *speech, char *grammar_name)
+{
+ return (speech->engine->unload ? speech->engine->unload(speech, grammar_name) : -1);
+}
+
+/*! \brief Return the results of a recognition from the speech structure */
+struct ast_speech_result *ast_speech_results_get(struct ast_speech *speech)
+{
+ return (speech->engine->get ? speech->engine->get(speech) : NULL);
+}
+
+/*! \brief Free a list of results */
+int ast_speech_results_free(struct ast_speech_result *result)
+{
+ struct ast_speech_result *current_result = result, *prev_result = NULL;
+ int res = 0;
+
+ while (current_result != NULL) {
+ prev_result = current_result;
+ /* Deallocate what we can */
+ if (current_result->text != NULL) {
+ ast_free(current_result->text);
+ current_result->text = NULL;
+ }
+ if (current_result->grammar != NULL) {
+ ast_free(current_result->grammar);
+ current_result->grammar = NULL;
+ }
+ /* Move on and then free ourselves */
+ current_result = AST_LIST_NEXT(current_result, list);
+ ast_free(prev_result);
+ prev_result = NULL;
+ }
+
+ return res;
+}
+
+/*! \brief Start speech recognition on a speech structure */
+void ast_speech_start(struct ast_speech *speech)
+{
+
+ /* Clear any flags that may affect things */
+ ast_clear_flag(speech, AST_SPEECH_SPOKE);
+ ast_clear_flag(speech, AST_SPEECH_QUIET);
+ ast_clear_flag(speech, AST_SPEECH_HAVE_RESULTS);
+
+ /* If results are on the structure, free them since we are starting again */
+ if (speech->results) {
+ ast_speech_results_free(speech->results);
+ speech->results = NULL;
+ }
+
+ /* If the engine needs to start stuff up, do it */
+ if (speech->engine->start)
+ speech->engine->start(speech);
+
+ return;
+}
+
+/*! \brief Write in signed linear audio to be recognized */
+int ast_speech_write(struct ast_speech *speech, void *data, int len)
+{
+ /* Make sure the speech engine is ready to accept audio */
+ if (speech->state != AST_SPEECH_STATE_READY)
+ return -1;
+
+ return speech->engine->write(speech, data, len);
+}
+
+/*! \brief Signal to the engine that DTMF was received */
+int ast_speech_dtmf(struct ast_speech *speech, const char *dtmf)
+{
+ int res = 0;
+
+ if (speech->state != AST_SPEECH_STATE_READY)
+ return -1;
+
+ if (speech->engine->dtmf != NULL) {
+ res = speech->engine->dtmf(speech, dtmf);
+ }
+
+ return res;
+}
+
+/*! \brief Change an engine specific attribute */
+int ast_speech_change(struct ast_speech *speech, char *name, const char *value)
+{
+ return (speech->engine->change ? speech->engine->change(speech, name, value) : -1);
+}
+
+/*! \brief Create a new speech structure using the engine specified */
+struct ast_speech *ast_speech_new(char *engine_name, int formats)
+{
+ struct ast_speech_engine *engine = NULL;
+ struct ast_speech *new_speech = NULL;
+ int format = AST_FORMAT_SLINEAR;
+
+ /* Try to find the speech recognition engine that was requested */
+ if (!(engine = find_engine(engine_name)))
+ return NULL;
+
+ /* Before even allocating the memory below do some codec negotiation, we choose the best codec possible and fall back to signed linear if possible */
+ if ((format = (engine->formats & formats)))
+ format = ast_best_codec(format);
+ else if ((engine->formats & AST_FORMAT_SLINEAR))
+ format = AST_FORMAT_SLINEAR;
+ else
+ return NULL;
+
+ /* Allocate our own speech structure, and try to allocate a structure from the engine too */
+ if (!(new_speech = ast_calloc(1, sizeof(*new_speech))))
+ return NULL;
+
+ /* Initialize the lock */
+ ast_mutex_init(&new_speech->lock);
+
+ /* Make sure no results are present */
+ new_speech->results = NULL;
+
+ /* Copy over our engine pointer */
+ new_speech->engine = engine;
+
+ /* Can't forget the format audio is going to be in */
+ new_speech->format = format;
+
+ /* We are not ready to accept audio yet */
+ ast_speech_change_state(new_speech, AST_SPEECH_STATE_NOT_READY);
+
+ /* Pass ourselves to the engine so they can set us up some more and if they error out then do not create a structure */
+ if (engine->create(new_speech, format)) {
+ ast_mutex_destroy(&new_speech->lock);
+ ast_free(new_speech);
+ new_speech = NULL;
+ }
+
+ return new_speech;
+}
+
+/*! \brief Destroy a speech structure */
+int ast_speech_destroy(struct ast_speech *speech)
+{
+ int res = 0;
+
+ /* Call our engine so we are destroyed properly */
+ speech->engine->destroy(speech);
+
+ /* Deinitialize the lock */
+ ast_mutex_destroy(&speech->lock);
+
+ /* If results exist on the speech structure, destroy them */
+ if (speech->results)
+ ast_speech_results_free(speech->results);
+
+ /* If a processing sound is set - free the memory used by it */
+ if (speech->processing_sound)
+ ast_free(speech->processing_sound);
+
+ /* Aloha we are done */
+ ast_free(speech);
+
+ return res;
+}
+
+/*! \brief Change state of a speech structure */
+int ast_speech_change_state(struct ast_speech *speech, int state)
+{
+ int res = 0;
+
+ switch (state) {
+ case AST_SPEECH_STATE_WAIT:
+ /* The engine heard audio, so they spoke */
+ ast_set_flag(speech, AST_SPEECH_SPOKE);
+ default:
+ speech->state = state;
+ break;
+ }
+
+ return res;
+}
+
+/*! \brief Change the type of results we want */
+int ast_speech_change_results_type(struct ast_speech *speech, enum ast_speech_results_type results_type)
+{
+ speech->results_type = results_type;
+
+ return (speech->engine->change_results_type ? speech->engine->change_results_type(speech, results_type) : 0);
+}
+
+/*! \brief Register a speech recognition engine */
+int ast_speech_register(struct ast_speech_engine *engine)
+{
+ struct ast_speech_engine *existing_engine = NULL;
+ int res = 0;
+
+ /* Confirm the engine meets the minimum API requirements */
+ if (!engine->create || !engine->write || !engine->destroy) {
+ ast_log(LOG_WARNING, "Speech recognition engine '%s' did not meet minimum API requirements.\n", engine->name);
+ return -1;
+ }
+
+ /* If an engine is already loaded with this name, error out */
+ if ((existing_engine = find_engine(engine->name))) {
+ ast_log(LOG_WARNING, "Speech recognition engine '%s' already exists.\n", engine->name);
+ return -1;
+ }
+
+ ast_verb(2, "Registered speech recognition engine '%s'\n", engine->name);
+
+ /* Add to the engine linked list and make default if needed */
+ AST_RWLIST_WRLOCK(&engines);
+ AST_RWLIST_INSERT_HEAD(&engines, engine, list);
+ if (!default_engine) {
+ default_engine = engine;
+ ast_verb(2, "Made '%s' the default speech recognition engine\n", engine->name);
+ }
+ AST_RWLIST_UNLOCK(&engines);
+
+ return res;
+}
+
+/*! \brief Unregister a speech recognition engine */
+int ast_speech_unregister(char *engine_name)
+{
+ struct ast_speech_engine *engine = NULL;
+ int res = -1;
+
+ if (ast_strlen_zero(engine_name))
+ return -1;
+
+ AST_RWLIST_WRLOCK(&engines);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&engines, engine, list) {
+ if (!strcasecmp(engine->name, engine_name)) {
+ /* We have our engine... removed it */
+ AST_RWLIST_REMOVE_CURRENT(list);
+ /* If this was the default engine, we need to pick a new one */
+ if (!default_engine)
+ default_engine = AST_RWLIST_FIRST(&engines);
+ ast_verb(2, "Unregistered speech recognition engine '%s'\n", engine_name);
+ /* All went well */
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&engines);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ /* We can not be unloaded */
+ return -1;
+}
+
+static int load_module(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Generic Speech Recognition API",
+ .load = load_module,
+ .unload = unload_module,
+ );
diff --git a/trunk/res/snmp/agent.c b/trunk/res/snmp/agent.c
new file mode 100644
index 000000000..207acb8a1
--- /dev/null
+++ b/trunk/res/snmp/agent.c
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) 2006 Voop as
+ * Thorsten Lockert <tholo@voop.as>
+ *
+ * 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 SNMP Agent / SubAgent support for Asterisk
+ *
+ * \author Thorsten Lockert <tholo@voop.as>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+/*
+ * There is some collision collision between netsmp and asterisk names,
+ * causing build under AST_DEVMODE to fail.
+ *
+ * The following PACKAGE_* macros are one place.
+ * Also netsnmp has an improper check for HAVE_DMALLOC_H, using
+ * #if HAVE_DMALLOC_H instead of #ifdef HAVE_DMALLOC_H
+ * As a countermeasure we define it to 0, however this will fail
+ * when the proper check is implemented.
+ */
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+#ifdef PACKAGE_BUGREPORT
+#undef PACKAGE_BUGREPORT
+#endif
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+#ifndef HAVE_DMALLOC_H
+#define HAVE_DMALLOC_H 0 /* XXX we shouldn't do this */
+#endif
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include "asterisk/paths.h" /* need ast_config_AST_SOCKET */
+#include "asterisk/channel.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/indications.h"
+#include "asterisk/version.h"
+#include "asterisk/pbx.h"
+
+/* Colission between Net-SNMP and Asterisk */
+#define unload_module ast_unload_module
+#include "asterisk/module.h"
+#undef unload_module
+
+#include "agent.h"
+
+/* Helper functions in Net-SNMP, header file not installed by default */
+int header_generic(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **);
+int header_simple_table(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **, int);
+int register_sysORTable(oid *, size_t, const char *);
+int unregister_sysORTable(oid *, size_t);
+
+/* Forward declaration */
+static void init_asterisk_mib(void);
+
+/*
+ * Anchor for all the Asterisk MIB values
+ */
+static oid asterisk_oid[] = { 1, 3, 6, 1, 4, 1, 22736, 1 };
+
+/*
+ * MIB values -- these correspond to values in the Asterisk MIB,
+ * and MUST be kept in sync with the MIB for things to work as
+ * expected.
+ */
+#define ASTVERSION 1
+#define ASTVERSTRING 1
+#define ASTVERTAG 2
+
+#define ASTCONFIGURATION 2
+#define ASTCONFUPTIME 1
+#define ASTCONFRELOADTIME 2
+#define ASTCONFPID 3
+#define ASTCONFSOCKET 4
+#define ASTCONFACTIVECALLS 5
+#define ASTCONFPROCESSEDCALLS 6
+
+#define ASTMODULES 3
+#define ASTMODCOUNT 1
+
+#define ASTINDICATIONS 4
+#define ASTINDCOUNT 1
+#define ASTINDCURRENT 2
+
+#define ASTINDTABLE 3
+#define ASTINDINDEX 1
+#define ASTINDCOUNTRY 2
+#define ASTINDALIAS 3
+#define ASTINDDESCRIPTION 4
+
+#define ASTCHANNELS 5
+#define ASTCHANCOUNT 1
+
+#define ASTCHANTABLE 2
+#define ASTCHANINDEX 1
+#define ASTCHANNAME 2
+#define ASTCHANLANGUAGE 3
+#define ASTCHANTYPE 4
+#define ASTCHANMUSICCLASS 5
+#define ASTCHANBRIDGE 6
+#define ASTCHANMASQ 7
+#define ASTCHANMASQR 8
+#define ASTCHANWHENHANGUP 9
+#define ASTCHANAPP 10
+#define ASTCHANDATA 11
+#define ASTCHANCONTEXT 12
+#define ASTCHANMACROCONTEXT 13
+#define ASTCHANMACROEXTEN 14
+#define ASTCHANMACROPRI 15
+#define ASTCHANEXTEN 16
+#define ASTCHANPRI 17
+#define ASTCHANACCOUNTCODE 18
+#define ASTCHANFORWARDTO 19
+#define ASTCHANUNIQUEID 20
+#define ASTCHANCALLGROUP 21
+#define ASTCHANPICKUPGROUP 22
+#define ASTCHANSTATE 23
+#define ASTCHANMUTED 24
+#define ASTCHANRINGS 25
+#define ASTCHANCIDDNID 26
+#define ASTCHANCIDNUM 27
+#define ASTCHANCIDNAME 28
+#define ASTCHANCIDANI 29
+#define ASTCHANCIDRDNIS 30
+#define ASTCHANCIDPRES 31
+#define ASTCHANCIDANI2 32
+#define ASTCHANCIDTON 33
+#define ASTCHANCIDTNS 34
+#define ASTCHANAMAFLAGS 35
+#define ASTCHANADSI 36
+#define ASTCHANTONEZONE 37
+#define ASTCHANHANGUPCAUSE 38
+#define ASTCHANVARIABLES 39
+#define ASTCHANFLAGS 40
+#define ASTCHANTRANSFERCAP 41
+
+#define ASTCHANTYPECOUNT 3
+
+#define ASTCHANTYPETABLE 4
+#define ASTCHANTYPEINDEX 1
+#define ASTCHANTYPENAME 2
+#define ASTCHANTYPEDESC 3
+#define ASTCHANTYPEDEVSTATE 4
+#define ASTCHANTYPEINDICATIONS 5
+#define ASTCHANTYPETRANSFER 6
+#define ASTCHANTYPECHANNELS 7
+
+#define ASTCHANSCALARS 5
+#define ASTCHANBRIDGECOUNT 1
+
+void *agent_thread(void *arg)
+{
+ ast_verb(2, "Starting %sAgent\n", res_snmp_agentx_subagent ? "Sub" : "");
+
+ snmp_enable_stderrlog();
+
+ if (res_snmp_agentx_subagent)
+ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_AGENT_ROLE,
+ 1);
+
+ init_agent("asterisk");
+
+ init_asterisk_mib();
+
+ init_snmp("asterisk");
+
+ if (!res_snmp_agentx_subagent)
+ init_master_agent();
+
+ while (res_snmp_dont_stop)
+ agent_check_and_process(1);
+
+ snmp_shutdown("asterisk");
+
+ ast_verb(2, "Terminating %sAgent\n", res_snmp_agentx_subagent ? "Sub" : "");
+
+ return NULL;
+}
+
+static u_char *
+ast_var_channels(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ if (vp->magic != ASTCHANCOUNT)
+ return NULL;
+
+ long_ret = ast_active_channels();
+
+ return (u_char *)&long_ret;
+}
+
+static u_char *ast_var_channels_table(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ static u_char bits_ret[2];
+ static char string_ret[256];
+ struct ast_channel *chan, *bridge;
+ struct timeval tval;
+ u_char *ret = NULL;
+ int i, bit;
+ struct ast_str *out = ast_str_alloca(2048);
+
+ if (header_simple_table(vp, name, length, exact, var_len, write_method, ast_active_channels()))
+ return NULL;
+
+ i = name[*length - 1] - 1;
+ for (chan = ast_channel_walk_locked(NULL);
+ chan && i;
+ chan = ast_channel_walk_locked(chan), i--)
+ ast_channel_unlock(chan);
+ if (chan == NULL)
+ return NULL;
+ *var_len = sizeof(long_ret);
+
+ switch (vp->magic) {
+ case ASTCHANINDEX:
+ long_ret = name[*length - 1];
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANNAME:
+ if (!ast_strlen_zero(chan->name)) {
+ strncpy(string_ret, chan->name, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANLANGUAGE:
+ if (!ast_strlen_zero(chan->language)) {
+ strncpy(string_ret, chan->language, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANTYPE:
+ strncpy(string_ret, chan->tech->type, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ break;
+ case ASTCHANMUSICCLASS:
+ if (!ast_strlen_zero(chan->musicclass)) {
+ strncpy(string_ret, chan->musicclass, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANBRIDGE:
+ if ((bridge = ast_bridged_channel(chan)) != NULL) {
+ strncpy(string_ret, bridge->name, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANMASQ:
+ if (chan->masq && !ast_strlen_zero(chan->masq->name)) {
+ strncpy(string_ret, chan->masq->name, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANMASQR:
+ if (chan->masqr && !ast_strlen_zero(chan->masqr->name)) {
+ strncpy(string_ret, chan->masqr->name, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANWHENHANGUP:
+ if (chan->whentohangup) {
+ gettimeofday(&tval, NULL);
+ long_ret = difftime(chan->whentohangup, tval.tv_sec) * 100 - tval.tv_usec / 10000;
+ ret= (u_char *)&long_ret;
+ }
+ break;
+ case ASTCHANAPP:
+ if (chan->appl) {
+ strncpy(string_ret, chan->appl, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANDATA:
+ if (chan->data) {
+ strncpy(string_ret, chan->data, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANCONTEXT:
+ strncpy(string_ret, chan->context, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ break;
+ case ASTCHANMACROCONTEXT:
+ strncpy(string_ret, chan->macrocontext, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ break;
+ case ASTCHANMACROEXTEN:
+ strncpy(string_ret, chan->macroexten, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ break;
+ case ASTCHANMACROPRI:
+ long_ret = chan->macropriority;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANEXTEN:
+ strncpy(string_ret, chan->exten, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ break;
+ case ASTCHANPRI:
+ long_ret = chan->priority;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANACCOUNTCODE:
+ if (!ast_strlen_zero(chan->accountcode)) {
+ strncpy(string_ret, chan->accountcode, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANFORWARDTO:
+ if (!ast_strlen_zero(chan->call_forward)) {
+ strncpy(string_ret, chan->call_forward, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANUNIQUEID:
+ strncpy(string_ret, chan->uniqueid, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ break;
+ case ASTCHANCALLGROUP:
+ long_ret = chan->callgroup;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANPICKUPGROUP:
+ long_ret = chan->pickupgroup;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANSTATE:
+ long_ret = chan->_state & 0xffff;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANMUTED:
+ long_ret = chan->_state & AST_STATE_MUTE ? 1 : 2;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANRINGS:
+ long_ret = chan->rings;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANCIDDNID:
+ if (chan->cid.cid_dnid) {
+ strncpy(string_ret, chan->cid.cid_dnid, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANCIDNUM:
+ if (chan->cid.cid_num) {
+ strncpy(string_ret, chan->cid.cid_num, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANCIDNAME:
+ if (chan->cid.cid_name) {
+ strncpy(string_ret, chan->cid.cid_name, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANCIDANI:
+ if (chan->cid.cid_ani) {
+ strncpy(string_ret, chan->cid.cid_ani, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANCIDRDNIS:
+ if (chan->cid.cid_rdnis) {
+ strncpy(string_ret, chan->cid.cid_rdnis, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANCIDPRES:
+ long_ret = chan->cid.cid_pres;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANCIDANI2:
+ long_ret = chan->cid.cid_ani2;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANCIDTON:
+ long_ret = chan->cid.cid_ton;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANCIDTNS:
+ long_ret = chan->cid.cid_tns;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANAMAFLAGS:
+ long_ret = chan->amaflags;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANADSI:
+ long_ret = chan->adsicpe;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANTONEZONE:
+ if (chan->zone) {
+ strncpy(string_ret, chan->zone->country, sizeof(string_ret));
+ string_ret[sizeof(string_ret) - 1] = '\0';
+ *var_len = strlen(string_ret);
+ ret = (u_char *)string_ret;
+ }
+ break;
+ case ASTCHANHANGUPCAUSE:
+ long_ret = chan->hangupcause;
+ ret = (u_char *)&long_ret;
+ break;
+ case ASTCHANVARIABLES:
+ if (pbx_builtin_serialize_variables(chan, &out)) {
+ *var_len = strlen(out->str);
+ ret = (u_char *)out->str;
+ }
+ break;
+ case ASTCHANFLAGS:
+ bits_ret[0] = 0;
+ for (bit = 0; bit < 8; bit++)
+ bits_ret[0] |= ((chan->flags & (1 << bit)) >> bit) << (7 - bit);
+ bits_ret[1] = 0;
+ for (bit = 0; bit < 8; bit++)
+ bits_ret[1] |= (((chan->flags >> 8) & (1 << bit)) >> bit) << (7 - bit);
+ *var_len = 2;
+ ret = bits_ret;
+ break;
+ case ASTCHANTRANSFERCAP:
+ long_ret = chan->transfercapability;
+ ret = (u_char *)&long_ret;
+ default:
+ break;
+ }
+ ast_channel_unlock(chan);
+ return ret;
+}
+
+static u_char *ast_var_channel_types(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ struct ast_variable *channel_types, *next;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ if (vp->magic != ASTCHANTYPECOUNT)
+ return NULL;
+
+ for (long_ret = 0, channel_types = next = ast_channeltype_list(); next; next = next->next)
+ long_ret++;
+ ast_variables_destroy(channel_types);
+
+ return (u_char *)&long_ret;
+}
+
+static u_char *ast_var_channel_types_table(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ const struct ast_channel_tech *tech = NULL;
+ struct ast_variable *channel_types, *next;
+ static unsigned long long_ret;
+ struct ast_channel *chan;
+ u_long i;
+
+ if (header_simple_table(vp, name, length, exact, var_len, write_method, -1))
+ return NULL;
+
+ channel_types = ast_channeltype_list();
+ for (i = 1, next = channel_types; next && i != name[*length - 1]; next = next->next, i++)
+ ;
+ if (next != NULL)
+ tech = ast_get_channel_tech(next->name);
+ ast_variables_destroy(channel_types);
+ if (next == NULL || tech == NULL)
+ return NULL;
+
+ switch (vp->magic) {
+ case ASTCHANTYPEINDEX:
+ long_ret = name[*length - 1];
+ return (u_char *)&long_ret;
+ case ASTCHANTYPENAME:
+ *var_len = strlen(tech->type);
+ return (u_char *)tech->type;
+ case ASTCHANTYPEDESC:
+ *var_len = strlen(tech->description);
+ return (u_char *)tech->description;
+ case ASTCHANTYPEDEVSTATE:
+ long_ret = tech->devicestate ? 1 : 2;
+ return (u_char *)&long_ret;
+ case ASTCHANTYPEINDICATIONS:
+ long_ret = tech->indicate ? 1 : 2;
+ return (u_char *)&long_ret;
+ case ASTCHANTYPETRANSFER:
+ long_ret = tech->transfer ? 1 : 2;
+ return (u_char *)&long_ret;
+ case ASTCHANTYPECHANNELS:
+ long_ret = 0;
+ for (chan = ast_channel_walk_locked(NULL); chan; chan = ast_channel_walk_locked(chan)) {
+ ast_channel_unlock(chan);
+ if (chan->tech == tech)
+ long_ret++;
+ }
+ return (u_char *)&long_ret;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char *ast_var_channel_bridge(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ struct ast_channel *chan = NULL;
+
+ long_ret = 0;
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ while ((chan = ast_channel_walk_locked(chan))) {
+ if (ast_bridged_channel(chan))
+ long_ret++;
+ ast_channel_unlock(chan);
+ }
+
+ *var_len = sizeof(long_ret);
+
+ return (vp->magic == ASTCHANBRIDGECOUNT) ? (u_char *) &long_ret : NULL;
+}
+
+static u_char *ast_var_Config(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ struct timeval tval;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ switch (vp->magic) {
+ case ASTCONFUPTIME:
+ gettimeofday(&tval, NULL);
+ long_ret = difftime(tval.tv_sec, ast_startuptime.tv_sec) * 100 + tval.tv_usec / 10000 - ast_startuptime.tv_usec / 10000;
+ return (u_char *)&long_ret;
+ case ASTCONFRELOADTIME:
+ gettimeofday(&tval, NULL);
+ if (ast_lastreloadtime.tv_sec)
+ long_ret = difftime(tval.tv_sec, ast_lastreloadtime.tv_sec) * 100 + tval.tv_usec / 10000 - ast_lastreloadtime.tv_usec / 10000;
+ else
+ long_ret = difftime(tval.tv_sec, ast_startuptime.tv_sec) * 100 + tval.tv_usec / 10000 - ast_startuptime.tv_usec / 10000;
+ return (u_char *)&long_ret;
+ case ASTCONFPID:
+ long_ret = getpid();
+ return (u_char *)&long_ret;
+ case ASTCONFSOCKET:
+ *var_len = strlen(ast_config_AST_SOCKET);
+ return (u_char *)ast_config_AST_SOCKET;
+ case ASTCONFACTIVECALLS:
+ long_ret = ast_active_calls();
+ return (u_char *)&long_ret;
+ case ASTCONFPROCESSEDCALLS:
+ long_ret = ast_processed_calls();
+ return (u_char *)&long_ret;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char *ast_var_indications(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ struct ind_tone_zone *tz = NULL;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ switch (vp->magic) {
+ case ASTINDCOUNT:
+ long_ret = 0;
+ while ( (tz = ast_walk_indications(tz)) )
+ long_ret++;
+
+ return (u_char *)&long_ret;
+ case ASTINDCURRENT:
+ tz = ast_get_indication_zone(NULL);
+ if (tz) {
+ *var_len = strlen(tz->country);
+ return (u_char *)tz->country;
+ }
+ *var_len = 0;
+ return NULL;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static u_char *ast_var_indications_table(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+ struct ind_tone_zone *tz = NULL;
+ int i;
+
+ if (header_simple_table(vp, name, length, exact, var_len, write_method, -1))
+ return NULL;
+
+ i = name[*length - 1] - 1;
+ while ( (tz = ast_walk_indications(tz)) && i )
+ i--;
+ if (tz == NULL)
+ return NULL;
+
+ switch (vp->magic) {
+ case ASTINDINDEX:
+ long_ret = name[*length - 1];
+ return (u_char *)&long_ret;
+ case ASTINDCOUNTRY:
+ *var_len = strlen(tz->country);
+ return (u_char *)tz->country;
+ case ASTINDALIAS:
+ if (tz->alias) {
+ *var_len = strlen(tz->alias);
+ return (u_char *)tz->alias;
+ }
+ return NULL;
+ case ASTINDDESCRIPTION:
+ *var_len = strlen(tz->description);
+ return (u_char *)tz->description;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int countmodule(const char *mod, const char *desc, int use, const char *like)
+{
+ return 1;
+}
+
+static u_char *ast_var_Modules(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ if (vp->magic != ASTMODCOUNT)
+ return NULL;
+
+ long_ret = ast_update_module_list(countmodule, NULL);
+
+ return (u_char *)&long_ret;
+}
+
+static u_char *ast_var_Version(struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ static unsigned long long_ret;
+
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ switch (vp->magic) {
+ case ASTVERSTRING:
+ {
+ const char *version = ast_get_version();
+ *var_len = strlen(version);
+ return (u_char *)version;
+ }
+ case ASTVERTAG:
+ sscanf(ast_get_version_num(), "%lu", &long_ret);
+ return (u_char *)&long_ret;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int term_asterisk_mib(int majorID, int minorID, void *serverarg, void *clientarg)
+{
+ unregister_sysORTable(asterisk_oid, OID_LENGTH(asterisk_oid));
+ return 0;
+}
+
+static void init_asterisk_mib(void)
+{
+ static struct variable4 asterisk_vars[] = {
+ {ASTVERSTRING, ASN_OCTET_STR, RONLY, ast_var_Version, 2, {ASTVERSION, ASTVERSTRING}},
+ {ASTVERTAG, ASN_UNSIGNED, RONLY, ast_var_Version, 2, {ASTVERSION, ASTVERTAG}},
+ {ASTCONFUPTIME, ASN_TIMETICKS, RONLY, ast_var_Config, 2, {ASTCONFIGURATION, ASTCONFUPTIME}},
+ {ASTCONFRELOADTIME, ASN_TIMETICKS, RONLY, ast_var_Config, 2, {ASTCONFIGURATION, ASTCONFRELOADTIME}},
+ {ASTCONFPID, ASN_INTEGER, RONLY, ast_var_Config, 2, {ASTCONFIGURATION, ASTCONFPID}},
+ {ASTCONFSOCKET, ASN_OCTET_STR, RONLY, ast_var_Config, 2, {ASTCONFIGURATION, ASTCONFSOCKET}},
+ {ASTCONFACTIVECALLS, ASN_GAUGE, RONLY, ast_var_Config, 2, {ASTCONFIGURATION, ASTCONFACTIVECALLS}},
+ {ASTCONFPROCESSEDCALLS, ASN_INTEGER, RONLY, ast_var_Config, 2, {ASTCONFIGURATION, ASTCONFPROCESSEDCALLS}},
+ {ASTMODCOUNT, ASN_INTEGER, RONLY, ast_var_Modules , 2, {ASTMODULES, ASTMODCOUNT}},
+ {ASTINDCOUNT, ASN_INTEGER, RONLY, ast_var_indications, 2, {ASTINDICATIONS, ASTINDCOUNT}},
+ {ASTINDCURRENT, ASN_OCTET_STR, RONLY, ast_var_indications, 2, {ASTINDICATIONS, ASTINDCURRENT}},
+ {ASTINDINDEX, ASN_INTEGER, RONLY, ast_var_indications_table, 4, {ASTINDICATIONS, ASTINDTABLE, 1, ASTINDINDEX}},
+ {ASTINDCOUNTRY, ASN_OCTET_STR, RONLY, ast_var_indications_table, 4, {ASTINDICATIONS, ASTINDTABLE, 1, ASTINDCOUNTRY}},
+ {ASTINDALIAS, ASN_OCTET_STR, RONLY, ast_var_indications_table, 4, {ASTINDICATIONS, ASTINDTABLE, 1, ASTINDALIAS}},
+ {ASTINDDESCRIPTION, ASN_OCTET_STR, RONLY, ast_var_indications_table, 4, {ASTINDICATIONS, ASTINDTABLE, 1, ASTINDDESCRIPTION}},
+ {ASTCHANCOUNT, ASN_INTEGER, RONLY, ast_var_channels, 2, {ASTCHANNELS, ASTCHANCOUNT}},
+ {ASTCHANINDEX, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANINDEX}},
+ {ASTCHANNAME, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANNAME}},
+ {ASTCHANLANGUAGE, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANLANGUAGE}},
+ {ASTCHANTYPE, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANTYPE}},
+ {ASTCHANMUSICCLASS, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANMUSICCLASS}},
+ {ASTCHANBRIDGE, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANBRIDGE}},
+ {ASTCHANMASQ, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANMASQ}},
+ {ASTCHANMASQR, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANMASQR}},
+ {ASTCHANWHENHANGUP, ASN_TIMETICKS, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANWHENHANGUP}},
+ {ASTCHANAPP, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANAPP}},
+ {ASTCHANDATA, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANDATA}},
+ {ASTCHANCONTEXT, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCONTEXT}},
+ {ASTCHANMACROCONTEXT, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANMACROCONTEXT}},
+ {ASTCHANMACROEXTEN, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANMACROEXTEN}},
+ {ASTCHANMACROPRI, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANMACROPRI}},
+ {ASTCHANEXTEN, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANEXTEN}},
+ {ASTCHANPRI, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANPRI}},
+ {ASTCHANACCOUNTCODE, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANACCOUNTCODE}},
+ {ASTCHANFORWARDTO, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANFORWARDTO}},
+ {ASTCHANUNIQUEID, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANUNIQUEID}},
+ {ASTCHANCALLGROUP, ASN_UNSIGNED, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCALLGROUP}},
+ {ASTCHANPICKUPGROUP, ASN_UNSIGNED, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANPICKUPGROUP}},
+ {ASTCHANSTATE, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANSTATE}},
+ {ASTCHANMUTED, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANMUTED}},
+ {ASTCHANRINGS, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANRINGS}},
+ {ASTCHANCIDDNID, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDDNID}},
+ {ASTCHANCIDNUM, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDNUM}},
+ {ASTCHANCIDNAME, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDNAME}},
+ {ASTCHANCIDANI, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDANI}},
+ {ASTCHANCIDRDNIS, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDRDNIS}},
+ {ASTCHANCIDPRES, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDPRES}},
+ {ASTCHANCIDANI2, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDANI2}},
+ {ASTCHANCIDTON, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDTON}},
+ {ASTCHANCIDTNS, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANCIDTNS}},
+ {ASTCHANAMAFLAGS, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANAMAFLAGS}},
+ {ASTCHANADSI, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANADSI}},
+ {ASTCHANTONEZONE, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANTONEZONE}},
+ {ASTCHANHANGUPCAUSE, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANHANGUPCAUSE}},
+ {ASTCHANVARIABLES, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANVARIABLES}},
+ {ASTCHANFLAGS, ASN_OCTET_STR, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANFLAGS}},
+ {ASTCHANTRANSFERCAP, ASN_INTEGER, RONLY, ast_var_channels_table, 4, {ASTCHANNELS, ASTCHANTABLE, 1, ASTCHANTRANSFERCAP}},
+ {ASTCHANTYPECOUNT, ASN_INTEGER, RONLY, ast_var_channel_types, 2, {ASTCHANNELS, ASTCHANTYPECOUNT}},
+ {ASTCHANTYPEINDEX, ASN_INTEGER, RONLY, ast_var_channel_types_table, 4, {ASTCHANNELS, ASTCHANTYPETABLE, 1, ASTCHANTYPEINDEX}},
+ {ASTCHANTYPENAME, ASN_OCTET_STR, RONLY, ast_var_channel_types_table, 4, {ASTCHANNELS, ASTCHANTYPETABLE, 1, ASTCHANTYPENAME}},
+ {ASTCHANTYPEDESC, ASN_OCTET_STR, RONLY, ast_var_channel_types_table, 4, {ASTCHANNELS, ASTCHANTYPETABLE, 1, ASTCHANTYPEDESC}},
+ {ASTCHANTYPEDEVSTATE, ASN_INTEGER, RONLY, ast_var_channel_types_table, 4, {ASTCHANNELS, ASTCHANTYPETABLE, 1, ASTCHANTYPEDEVSTATE}},
+ {ASTCHANTYPEINDICATIONS, ASN_INTEGER, RONLY, ast_var_channel_types_table, 4, {ASTCHANNELS, ASTCHANTYPETABLE, 1, ASTCHANTYPEINDICATIONS}},
+ {ASTCHANTYPETRANSFER, ASN_INTEGER, RONLY, ast_var_channel_types_table, 4, {ASTCHANNELS, ASTCHANTYPETABLE, 1, ASTCHANTYPETRANSFER}},
+ {ASTCHANTYPECHANNELS, ASN_GAUGE, RONLY, ast_var_channel_types_table, 4, {ASTCHANNELS, ASTCHANTYPETABLE, 1, ASTCHANTYPECHANNELS}},
+ {ASTCHANBRIDGECOUNT, ASN_GAUGE, RONLY, ast_var_channel_bridge, 3, {ASTCHANNELS, ASTCHANSCALARS, ASTCHANBRIDGECOUNT}},
+ };
+
+ register_sysORTable(asterisk_oid, OID_LENGTH(asterisk_oid),
+ "ASTERISK-MIB implementation for Asterisk.");
+
+ REGISTER_MIB("res_snmp", asterisk_vars, variable4, asterisk_oid);
+
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY,
+ SNMP_CALLBACK_SHUTDOWN,
+ term_asterisk_mib, NULL);
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * c-file-offsets: ((case-label . 0))
+ * tab-width: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/trunk/res/snmp/agent.h b/trunk/res/snmp/agent.h
new file mode 100644
index 000000000..21389d6c9
--- /dev/null
+++ b/trunk/res/snmp/agent.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006 Voop as
+ * Thorsten Lockert <tholo@voop.as>
+ *
+ * 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 SNMP Agent / SubAgent support for Asterisk
+ *
+ * \author Thorsten Lockert <tholo@voop.as>
+ */
+
+/*!
+ * \internal
+ * \brief Thread running the SNMP Agent or Subagent
+ * \param Not used -- required by pthread_create
+ * \return A pointer with return status -- not used
+ *
+ * This represent the main thread of the SNMP [sub]agent, and
+ * will initialize SNMP and loop, processing requests until
+ * termination is requested by resetting the flag in
+ * \ref res_snmp_dontStop.
+ */
+void *agent_thread(void *);
+
+/*!
+ * \internal
+ * Flag saying whether we run as a Subagent or full Agent
+ */
+extern int res_snmp_agentx_subagent;
+
+/*!
+ * \internal
+ * Flag stating the agent thread should not terminate
+ */
+extern int res_snmp_dont_stop;
diff --git a/trunk/sample.call b/trunk/sample.call
new file mode 100644
index 000000000..6549d7914
--- /dev/null
+++ b/trunk/sample.call
@@ -0,0 +1,80 @@
+#
+# This is a sample file that can be dumped in /var/spool/asterisk/outgoing
+# to generate a call.
+#
+# Comments are indicated by a '#' character that begins a line, or follows
+# a space or tab character. To be consistent with the configuration files
+# in Asterisk, comments can also be indicated by a semicolon. However, the
+# multiline comments (;-- --;) used in Asterisk configuration files are not
+# supported. Semicolons can be escaped by a backslash.
+#
+
+# Obviously, you MUST specify at least a channel in the same format as you
+# would for the "Dial" application. Only one channel name is permitted.
+#
+Channel: Zap/1
+#
+# You may also specify a wait time (default is 45 seconds) for how long to
+# wait for the channel to be answered, a retry time (default is 5 mins)
+# for how soon to retry this call, and a maximum number of retries (default
+# is 0) for how many times to retry this call.
+#
+MaxRetries: 2
+RetryTime: 60
+WaitTime: 30
+
+#
+# Once the call is answered, you must provide either an application/data
+# combination, or a context/extension/priority in which to start the PBX.
+#
+Context: default
+Extension: s
+Priority: 1
+
+#
+# Alternatively you can specify just an application
+# and its arguments to be run, instead of a context
+# extension and priority
+#
+#Application: VoiceMailMain
+#Data: 1234
+
+#
+# You can set the callerid that will be used for the outgoing call
+#
+#Callerid: Wakeup Call Service <(555) 555-5555>
+
+#
+# An account code can be specified the following way:
+#
+#Account: mysuperfunaccountcode
+
+#
+# Normally, a call file is always deleted after the call is successful
+# or the maximum number of tries is reached even if the modification
+# time of the call file was changed during the call to be in the
+# future. By Setting AlwaysDelete to No the modification time of the
+# call file will be checked after the call is completed or the maximum
+# number of retries is reached. If the modification time is in the
+# future, the call file will not be deleted.
+#
+#AlwaysDelete: Yes
+
+#
+# You can set channel variables that will be passed to the channel.
+# This includes writable dialplan functions. To set a writable dialplan
+# function, the module containing this function *must* be loaded.
+#
+#Set: file1=/tmp/to
+#Set: file2=/tmp/msg
+#Set: timestamp=20021023104500
+#Set: CDR(userfield|r)=42
+
+#
+# Setting Archive to yes the call file is never deleted, but is moved
+# in the subdir "outgoing_done" of the spool directory. In this case
+# will be appended a line with "Status: value", where value can be
+# Completed, Expired or Failed.
+#
+#Archive: yes
+
diff --git a/trunk/sounds/Makefile b/trunk/sounds/Makefile
new file mode 100644
index 000000000..a31dcb2cf
--- /dev/null
+++ b/trunk/sounds/Makefile
@@ -0,0 +1,157 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for sound files
+#
+# 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
+#
+
+.PHONY: dist-clean all uninstall have_download install
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/makeopts
+
+SOUNDS_DIR:=$(DESTDIR)$(ASTDATADIR)/sounds
+MOH_DIR:=$(DESTDIR)$(ASTDATADIR)/moh
+CORE_SOUNDS_VERSION:=1.4.8
+EXTRA_SOUNDS_VERSION:=1.4.7
+SOUNDS_URL:=http://downloads.digium.com/pub/telephony/sounds/releases
+MCS:=$(subst -EN-,-en-,$(MENUSELECT_CORE_SOUNDS))
+MCS:=$(subst -FR-,-fr-,$(MCS))
+MCS:=$(subst -ES-,-es-,$(MCS))
+MCS:=$(subst -WAV,-wav,$(MCS))
+MCS:=$(subst -ULAW,-ulaw,$(MCS))
+MCS:=$(subst -ALAW,-alaw,$(MCS))
+MCS:=$(subst -GSM,-gsm,$(MCS))
+MCS:=$(subst -G729,-g729,$(MCS))
+MCS:=$(subst -G722,-g722,$(MCS))
+CORE_SOUNDS:=$(MCS:CORE-SOUNDS-%=asterisk-core-sounds-%-$(CORE_SOUNDS_VERSION).tar.gz)
+CORE_SOUND_TAGS:=$(MCS:CORE-SOUNDS-%=$(SOUNDS_DIR)/.asterisk-core-sounds-%-$(CORE_SOUNDS_VERSION))
+MES:=$(subst -EN-,-en-,$(MENUSELECT_EXTRA_SOUNDS))
+MES:=$(subst -FR-,-fr-,$(MES))
+MES:=$(subst -ES-,-es-,$(MES))
+MES:=$(subst -WAV,-wav,$(MES))
+MES:=$(subst -ULAW,-ulaw,$(MES))
+MES:=$(subst -ALAW,-alaw,$(MES))
+MES:=$(subst -GSM,-gsm,$(MES))
+MES:=$(subst -G729,-g729,$(MES))
+MES:=$(subst -G722,-g722,$(MES))
+EXTRA_SOUNDS:=$(MES:EXTRA-SOUNDS-%=asterisk-extra-sounds-%-$(EXTRA_SOUNDS_VERSION).tar.gz)
+EXTRA_SOUND_TAGS:=$(MES:EXTRA-SOUNDS-%=$(SOUNDS_DIR)/.asterisk-extra-sounds-%-$(EXTRA_SOUNDS_VERSION))
+MM:=$(subst -FREEPLAY-,-freeplay-,$(MENUSELECT_MOH))
+MM:=$(subst -WAV,-wav,$(MM))
+MM:=$(subst -ULAW,-ulaw,$(MM))
+MM:=$(subst -ALAW,-alaw,$(MM))
+MM:=$(subst -GSM,-gsm,$(MM))
+MM:=$(subst -G729,-g729,$(MM))
+MM:=$(subst -G722,-g722,$(MM))
+MOH:=$(MM:MOH-%=asterisk-moh-%.tar.gz)
+MOH_TAGS:=$(MM:MOH-%=$(MOH_DIR)/.asterisk-moh-%)
+# If "fetch" is used, --continue is not a valid option.
+ifeq ($(WGET),wget)
+WGET_ARGS:=--continue
+endif
+
+all: $(CORE_SOUNDS) $(EXTRA_SOUNDS) $(MOH)
+
+have_download:
+ @if test "$(DOWNLOAD)" = ":" ; then \
+ echo "**************************************************"; \
+ echo "*** ***"; \
+ echo "*** You must have either wget or fetch to be ***"; \
+ echo "*** able to automatically download and install ***"; \
+ echo "*** the requested sound packages. ***"; \
+ echo "*** ***"; \
+ echo "*** Please install one of these, or remove any ***"; \
+ echo "*** extra sound package selections in ***"; \
+ echo "*** menuselecct before installing Asterisk. ***"; \
+ echo "*** ***"; \
+ echo "**************************************************"; \
+ exit 1; \
+ fi
+
+$(SOUNDS_DIR)/.asterisk-core-sounds-en-%: have_download
+ @PACKAGE=$(subst $(SOUNDS_DIR)/.asterisk,asterisk,$@).tar.gz; \
+ if test ! -f $${PACKAGE}; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$${PACKAGE}; fi; \
+ if test ! -f $${PACKAGE}; then exit 1; fi; \
+ rm -f $(subst -$(CORE_SOUNDS_VERSION),,$@)-* && \
+ (cd $(SOUNDS_DIR)/en; cat $(CURDIR)/$${PACKAGE} | gzip -d | tar xf -) && \
+ touch $@
+
+$(SOUNDS_DIR)/.asterisk-core-sounds-es-%: have_download
+ @PACKAGE=$(subst $(SOUNDS_DIR)/.asterisk,asterisk,$@).tar.gz; \
+ if test ! -f $${PACKAGE}; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$${PACKAGE}; fi; \
+ if test ! -f $${PACKAGE}; then exit 1; fi; \
+ rm -f $(subst -$(CORE_SOUNDS_VERSION),,$@)-* && \
+ (cd $(SOUNDS_DIR)/es; cat $(CURDIR)/$${PACKAGE} | gzip -d | tar xf -) && \
+ touch $@
+
+$(SOUNDS_DIR)/.asterisk-core-sounds-fr-%: have_download
+ @PACKAGE=$(subst $(SOUNDS_DIR)/.asterisk,asterisk,$@).tar.gz; \
+ if test ! -f $${PACKAGE}; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$${PACKAGE}; fi; \
+ if test ! -f $${PACKAGE}; then exit 1; fi; \
+ rm -f $(subst -$(CORE_SOUNDS_VERSION),,$@)-* && \
+ (cd $(SOUNDS_DIR)/fr; cat $(CURDIR)/$${PACKAGE} | gzip -d | tar xf -) && \
+ touch $@
+
+$(SOUNDS_DIR)/.asterisk-extra-sounds-en-%: have_download
+ @PACKAGE=$(subst $(SOUNDS_DIR)/.asterisk,asterisk,$@).tar.gz; \
+ if test ! -f $${PACKAGE}; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$${PACKAGE}; fi; \
+ if test ! -f $${PACKAGE}; then exit 1; fi; \
+ rm -f $(subst -$(EXTRA_SOUNDS_VERSION),,$@)-* && \
+ (cd $(SOUNDS_DIR)/en; cat $(CURDIR)/$${PACKAGE} | gzip -d | tar xf -) && \
+ touch $@
+
+$(SOUNDS_DIR)/.asterisk-extra-sounds-es-%: have_download
+ @PACKAGE=$(subst $(SOUNDS_DIR)/.asterisk,asterisk,$@).tar.gz; \
+ if test ! -f $${PACKAGE}; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$${PACKAGE}; fi; \
+ if test ! -f $${PACKAGE}; then exit 1; fi; \
+ rm -f $(subst -$(EXTRA_SOUNDS_VERSION),,$@)-* && \
+ (cd $(SOUNDS_DIR)/es; cat $(CURDIR)/$${PACKAGE} | gzip -d | tar xf -) && \
+ touch $@
+
+$(SOUNDS_DIR)/.asterisk-extra-sounds-fr-%: have_download
+ @PACKAGE=$(subst $(SOUNDS_DIR)/.asterisk,asterisk,$@).tar.gz; \
+ if test ! -f $${PACKAGE}; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$${PACKAGE}; fi; \
+ if test ! -f $${PACKAGE}; then exit 1; fi; \
+ rm -f $(subst -$(EXTRA_SOUNDS_VERSION),,$@)-* && \
+ (cd $(SOUNDS_DIR)/fr; cat $(CURDIR)/$${PACKAGE} | gzip -d | tar xf -) && \
+ touch $@
+
+$(MOH_DIR)/.asterisk-moh-%: have_download
+ @PACKAGE=$(subst $(MOH_DIR)/.asterisk,asterisk,$@).tar.gz; \
+ if test ! -f $${PACKAGE}; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$${PACKAGE}; fi; \
+ if test ! -f $${PACKAGE}; then exit 1; fi; \
+ (cd $(MOH_DIR); cat $(CURDIR)/$${PACKAGE} | gzip -d | tar xf -) && \
+ touch $@
+
+asterisk-core-%.tar.gz: have_download
+ @if test ! -f $@ && test ! -f $(SOUNDS_DIR)/.$(subst .tar.gz,,$@) ; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$@;fi
+
+asterisk-extra-%.tar.gz: have_download
+ @if test ! -f $@ && test ! -f $(SOUNDS_DIR)/.$(subst .tar.gz,,$@) ; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$@;fi
+
+asterisk-moh-%.tar.gz: have_download
+ @if test ! -f $@ && test ! -f $(MOH_DIR)/.$(subst .tar.gz,,$@) ; then $(DOWNLOAD) $(WGET_ARGS) $(SOUNDS_URL)/$@;fi
+
+dist-clean:
+ rm -f *.tar.gz
+
+$(SOUNDS_DIR)/en $(MOH_DIR) $(SOUNDS_DIR)/es $(SOUNDS_DIR)/fr:
+ mkdir -p $@
+
+install: $(SOUNDS_DIR)/en $(SOUNDS_DIR)/es $(SOUNDS_DIR)/fr $(MOH_DIR) $(CORE_SOUND_TAGS) $(EXTRA_SOUND_TAGS) $(MOH_TAGS)
+
+uninstall:
+ rm -rf $(SOUNDS_DIR)
+ rm -rf $(MOH_DIR)
+
+core_sounds_version:
+ @echo $(CORE_SOUNDS_VERSION)
+
+extra_sounds_version:
+ @echo $(EXTRA_SOUNDS_VERSION)
diff --git a/trunk/sounds/sounds.xml b/trunk/sounds/sounds.xml
new file mode 100644
index 000000000..4ba16e092
--- /dev/null
+++ b/trunk/sounds/sounds.xml
@@ -0,0 +1,68 @@
+ <category name="MENUSELECT_CORE_SOUNDS" displayname="Core Sound Packages" positive_output="yes">
+ <member name="CORE-SOUNDS-EN-WAV" displayname="English, WAV format">
+ </member>
+ <member name="CORE-SOUNDS-EN-ULAW" displayname="English, mu-Law format">
+ </member>
+ <member name="CORE-SOUNDS-EN-ALAW" displayname="English, a-Law format">
+ </member>
+ <member name="CORE-SOUNDS-EN-GSM" displayname="English, GSM format" >
+ <defaultenabled>yes</defaultenabled>
+ </member>
+ <member name="CORE-SOUNDS-EN-G729" displayname="English, G.729 format">
+ </member>
+ <member name="CORE-SOUNDS-EN-G722" displayname="English, G.722 format">
+ </member>
+ <member name="CORE-SOUNDS-ES-WAV" displayname="Spanish, WAV format">
+ </member>
+ <member name="CORE-SOUNDS-ES-ULAW" displayname="Spanish, mu-Law format">
+ </member>
+ <member name="CORE-SOUNDS-ES-ALAW" displayname="Spanish, a-Law format">
+ </member>
+ <member name="CORE-SOUNDS-ES-GSM" displayname="Spanish, GSM format">
+ </member>
+ <member name="CORE-SOUNDS-ES-G729" displayname="Spanish, G.729 format">
+ </member>
+ <member name="CORE-SOUNDS-ES-G722" displayname="Spanish, G.722 format">
+ </member>
+ <member name="CORE-SOUNDS-FR-WAV" displayname="French, WAV format">
+ </member>
+ <member name="CORE-SOUNDS-FR-ULAW" displayname="French, mu-Law format">
+ </member>
+ <member name="CORE-SOUNDS-FR-ALAW" displayname="French, a-Law format">
+ </member>
+ <member name="CORE-SOUNDS-FR-GSM" displayname="French, GSM format">
+ </member>
+ <member name="CORE-SOUNDS-FR-G729" displayname="French, G.729 format">
+ </member>
+ <member name="CORE-SOUNDS-FR-G722" displayname="French, G.722 format">
+ </member>
+ </category>
+ <category name="MENUSELECT_MOH" displayname="Music On Hold File Packages" positive_output="yes">
+ <member name="MOH-FREEPLAY-WAV" displayname="FreePlay Music On Hold Files, WAV format" >
+ <defaultenabled>yes</defaultenabled>
+ </member>
+ <member name="MOH-FREEPLAY-ULAW" displayname="FreePlay Music On Hold Files, mu-Law format" >
+ </member>
+ <member name="MOH-FREEPLAY-ALAW" displayname="FreePlay Music On Hold Files, a-Law format" >
+ </member>
+ <member name="MOH-FREEPLAY-GSM" displayname="FreePlay Music On Hold Files, GSM format" >
+ </member>
+ <member name="MOH-FREEPLAY-G729" displayname="FreePlay Music On Hold Files, G.729 format" >
+ </member>
+ <member name="MOH-FREEPLAY-G722" displayname="FreePlay Music On Hold Files, G.722 format" >
+ </member>
+ </category>
+ <category name="MENUSELECT_EXTRA_SOUNDS" displayname="Extras Sound Packages" positive_output="yes">
+ <member name="EXTRA-SOUNDS-EN-WAV" displayname="English, WAV format">
+ </member>
+ <member name="EXTRA-SOUNDS-EN-ULAW" displayname="English, mu-Law format">
+ </member>
+ <member name="EXTRA-SOUNDS-EN-ALAW" displayname="English, a-Law format">
+ </member>
+ <member name="EXTRA-SOUNDS-EN-GSM" displayname="English, GSM format" >
+ </member>
+ <member name="EXTRA-SOUNDS-EN-G729" displayname="English, G.729 format">
+ </member>
+ <member name="EXTRA-SOUNDS-EN-G722" displayname="English, G.722 format">
+ </member>
+ </category>
diff --git a/trunk/static-http/ajamdemo.html b/trunk/static-http/ajamdemo.html
new file mode 100644
index 000000000..607a2cfbe
--- /dev/null
+++ b/trunk/static-http/ajamdemo.html
@@ -0,0 +1,219 @@
+<script src="prototype.js"></script>
+<script src="astman.js"></script>
+<link href="astman.css" media="all" rel="Stylesheet" type="text/css" />
+
+<script>
+ var logins = new Object;
+ var logoffs = new Object;
+ var channels = new Object;
+ var pongs = new Object;
+ var loggedon = -1;
+ var selectedchan = null;
+ var hungupchan = "";
+ var transferedchan = "";
+
+ var demo = new Object;
+
+ function loggedOn() {
+ if (loggedon == 1)
+ return;
+ loggedon = 1;
+ updateButtons();
+ $('statusbar').innerHTML = "<i>Retrieving channel status...</i>";
+ astmanEngine.pollEvents();
+ astmanEngine.sendRequest('action=status', demo.channels);
+ }
+
+ function clearChannelList() {
+ $('channellist').innerHTML = "<i class='light'>Not connected</i>";
+ }
+
+ function loggedOff() {
+ if (loggedon == 0)
+ return;
+ loggedon = 0;
+ selectedchan = null;
+ updateButtons();
+ astmanEngine.channelClear();
+ clearChannelList();
+ }
+
+ function updateButtons()
+ {
+ if ($(selectedchan)) {
+ $('transfer').disabled = 0;
+ $('hangup').disabled = 0;
+ } else {
+ $('transfer').disabled = 1;
+ $('hangup').disabled = 1;
+ selectedchan = null;
+ }
+ if (loggedon) {
+ $('username').disabled = 1;
+ $('secret').disabled = 1;
+ $('logoff').disabled = 0;
+ $('login').disabled = 1;
+ $('refresh').disabled = 0;
+ } else {
+ $('username').disabled = 0;
+ $('secret').disabled = 0;
+ $('logoff').disabled = 1;
+ $('login').disabled = 0;
+ $('refresh').disabled = 1;
+ }
+ }
+
+ demo.channelCallback = function(target) {
+ selectedchan = target;
+ updateButtons();
+ }
+
+ demo.channels = function(msgs) {
+ resp = msgs[0].headers['response'];
+ if (resp == "Success") {
+ loggedOn();
+ } else
+ loggedOff();
+
+ for (i=1;i<msgs.length - 1;i++)
+ astmanEngine.channelUpdate(msgs[i]);
+ $('channellist').innerHTML = astmanEngine.channelTable(demo.channelCallback);
+ $('statusbar').innerHTML = "Ready";
+ }
+
+ demo.logins = function(msgs) {
+ $('statusbar').innerHTML = msgs[0].headers['message'];
+ resp = msgs[0].headers['response'];
+ if (resp == "Success")
+ loggedOn();
+ else
+ loggedOff();
+ };
+
+
+ demo.logoffs = function(msgs) {
+ $('statusbar').innerHTML = msgs[0].headers['message'];
+ loggedOff();
+ };
+
+ demo.hungup = function(msgs) {
+ $('statusbar').innerHTML = "Hungup " + hungupchan;
+ }
+
+ demo.transferred = function(msgs) {
+ $('statusbar').innerHTML = "Transferred " + transferredchan;
+ }
+
+ function doHangup() {
+ hungupchan = selectedchan;
+ astmanEngine.sendRequest('action=hangup&channel=' + selectedchan, demo.hungup);
+ }
+
+ function doStatus() {
+ $('statusbar').innerHTML = "<i>Updating channel status...</i>";
+ astmanEngine.channelClear();
+ astmanEngine.sendRequest('action=status', demo.channels);
+ }
+
+ function doLogin() {
+ $('statusbar').innerHTML = "<i>Logging in...</i>";
+ astmanEngine.sendRequest('action=login&username=' + $('username').value + "&secret=" + $('secret').value, demo.logins);
+ }
+
+ function doTransfer() {
+ var channel = astmanEngine.channelInfo(selectedchan);
+ var exten = prompt("Enter new extension for " + selectedchan);
+ var altchan;
+ if (exten) {
+ if (channel.link) {
+ if (confirm("Transfer " + channel.link + " too?"))
+ altchan = channel.link;
+ }
+ if (altchan) {
+ transferredchan = selectedchan + " and " + altchan + " to " + exten;
+ astmanEngine.sendRequest('action=redirect&channel=' + selectedchan + "&priority=1&extrachannel=" + altchan + "&exten=" + exten, demo.transferred);
+ } else {
+ transferredchan = selectedchan + " to " + exten;
+ astmanEngine.sendRequest('action=redirect&channel=' + selectedchan + "&priority=1&exten=" + exten, demo.transferred);
+ }
+ }
+ }
+
+ function doLogoff() {
+ $('statusbar').innerHTML = "<i>Logging off...</i>";
+ astmanEngine.sendRequest('action=logoff', demo.logoffs);
+ }
+
+ demo.pongs = function(msgs) {
+ resp = msgs[0].headers['response'];
+ if (resp == "Pong") {
+ $('statusbar').innerHTML = "<i>Already connected...</i>";
+ loggedOn();
+ } else {
+ $('statusbar').innerHTML = "<i>Please login...</i>";
+ loggedOff();
+ }
+ }
+
+ demo.eventcb = function(msgs) {
+ var x;
+ if (loggedon) {
+ for (i=1;i<msgs.length - 1;i++) {
+ astmanEngine.channelUpdate(msgs[i]);
+ }
+ $('channellist').innerHTML = astmanEngine.channelTable(demo.channelCallback);
+ astmanEngine.pollEvents();
+ }
+ updateButtons();
+ }
+
+ function localajaminit() {
+ astmanEngine.setURL('../rawman');
+ astmanEngine.setEventCallback(demo.eventcb);
+ //astmanEngine.setDebug($('ditto'));
+ clearChannelList();
+ astmanEngine.sendRequest('action=ping', demo.pongs);
+ }
+</script>
+
+<title>Asterisk&trade; AJAM Demo</title>
+<body onload="localajaminit()">
+<table align="center" width=600>
+<tr valign="top"><td>
+<table align="left">
+<tr><td colspan="2"><h2>Asterisk&trade; AJAM Demo</h2></td>
+<tr><td>Username:</td><td><input id="username"></td></tr>
+<tr><td>Secret:</td><td><input type="password" id="secret"></td></tr>
+ <tr><td colspan=2 align="center">
+ <div id="statusbar">
+ <span style="margin-left: 4px;font-weight:bold">&nbsp;</span>
+ </div>
+ </td></tr>
+
+ <tr><td><input type="submit" id="login" value="Login" onClick="doLogin()"></td>
+ <td><input type="submit" id="logoff" value="Logoff" disabled=1 onClick="doLogoff()"></td></tr>
+</table>
+</td><td valign='bottom'>
+<table>
+<div style="margin-left:10;margin-right:50;margin-top:10;margin-bottom:20">
+<i>This is a demo of the Asynchronous Javascript Asterisk Manager interface. You can login with a
+valid, appropriately permissioned manager username and secret.</i>
+</div>
+<tr>
+ <td><input type="submit" onClick="doStatus()" id="refresh" value="Refresh"></td>
+ <td><input type="submit" onClick="doTransfer()" id="transfer" value="Transfer..."></td>
+ <td><input type="submit" onClick="doHangup()" id="hangup" value="Hangup"></td>
+</tr>
+</table>
+</td></tr>
+<tr><td colspan=2>
+ <div id="channellist" class="chanlist">
+ </div>
+ </td></tr>
+<tr><td align="center" colspan=2>
+ <font size=-1><i>
+ Copyright (C) 2006 Digium, Inc. Asterisk and Digium are trademarks of Digium, Inc.
+ </i></font>
+</td></tr>
+</table>
+</body>
diff --git a/trunk/static-http/astman.css b/trunk/static-http/astman.css
new file mode 100644
index 000000000..fbf2b2cf9
--- /dev/null
+++ b/trunk/static-http/astman.css
@@ -0,0 +1,34 @@
+.chanlist {
+ border : 1px solid #1f669b;
+ height : 150px;
+ overflow : auto;
+ background-color : #f1f1f1;
+ width : 600;
+}
+
+.chantable {
+ border : 0px;
+ background-color : #f1f1f1;
+ width : 100%;
+}
+
+.labels {
+ background-color : #000000;
+ color : #ffffff;
+}
+
+.chanlisteven {
+ background-color : #fff8e4;
+}
+
+.chanlistodd {
+ background-color : #f0f5ff;
+}
+
+.chanlistselected {
+ background-color : #ffb13d;
+}
+
+.light {
+ color : #717171;
+}
diff --git a/trunk/static-http/astman.js b/trunk/static-http/astman.js
new file mode 100644
index 000000000..81f896c1c
--- /dev/null
+++ b/trunk/static-http/astman.js
@@ -0,0 +1,262 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Javascript routines or accessing manager routines over HTTP.
+ *
+ * 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.
+ *
+ */
+
+
+function Astman() {
+ var me = this;
+ var channels = new Array;
+ var lastselect;
+ var selecttarget;
+ this.setURL = function(url) {
+ this.url = url;
+ };
+ this.setEventCallback = function(callback) {
+ this.eventcallback = callback;
+ };
+ this.setDebug = function(debug) {
+ this.debug = debug;
+ };
+ this.clickChannel = function(ev) {
+ var target = ev.target;
+ // XXX This is icky, we statically use astmanEngine to call the callback XXX
+ if (me.selecttarget)
+ me.restoreTarget(me.selecttarget);
+ while(!target.id || !target.id.length)
+ target=target.parentNode;
+ me.selecttarget = target.id;
+ target.className = "chanlistselected";
+ me.chancallback(target.id);
+ };
+ this.restoreTarget = function(targetname) {
+ var other;
+ target = $(targetname);
+ if (!target)
+ return;
+ if (target.previousSibling) {
+ other = target.previousSibling.previousSibling.className;
+ } else if (target.nextSibling) {
+ other = target.nextSibling.nextSibling.className;
+ }
+ if (other) {
+ if (other == "chanlisteven")
+ target.className = "chanlistodd";
+ else
+ target.className = "chanlisteven";
+ } else
+ target.className = "chanlistodd";
+ };
+ this.channelUpdate = function(msg, channame) {
+ var fields = new Array("callerid", "calleridname", "context", "extension", "priority", "account", "state", "link", "uniqueid" );
+
+ if (!channame || !channame.length)
+ channame = msg.headers['channel'];
+
+ if (!channels[channame])
+ channels[channame] = new Array();
+
+ if (msg.headers.event) {
+ if (msg.headers.event == "Hangup") {
+ delete channels[channame];
+ } else if (msg.headers.event == "Link") {
+ var chan1 = msg.headers.channel1;
+ var chan2 = msg.headers.channel2;
+ if (chan1 && channels[chan1])
+ channels[chan1].link = chan2;
+ if (chan2 && channels[chan2])
+ channels[chan2].link = chan1;
+ } else if (msg.headers.event == "Unlink") {
+ var chan1 = msg.headers.channel1;
+ var chan2 = msg.headers.channel2;
+ if (chan1 && channels[chan1])
+ delete channels[chan1].link;
+ if (chan2 && channels[chan2])
+ delete channels[chan2].link;
+ } else if (msg.headers.event == "Rename") {
+ var oldname = msg.headers.oldname;
+ var newname = msg.headers.newname;
+ if (oldname && channels[oldname]) {
+ channels[newname] = channels[oldname];
+ delete channels[oldname];
+ }
+ } else {
+ channels[channame]['channel'] = channame;
+ for (x=0;x<fields.length;x++) {
+ if (msg.headers[fields[x]])
+ channels[channame][fields[x]] = msg.headers[fields[x]];
+ }
+ }
+ } else {
+ channels[channame]['channel'] = channame;
+ for (x=0;x<fields.length;x++) {
+ if (msg.headers[fields[x]])
+ channels[channame][fields[x]] = msg.headers[fields[x]];
+ }
+ }
+ };
+ this.channelClear = function() {
+ channels = new Array;
+ }
+ this.channelInfo = function(channame) {
+ return channels[channame];
+ };
+ this.channelTable = function(callback) {
+ var s, x;
+ var cclass, count=0;
+ var found = 0;
+ var foundactive = 0;
+ var fieldlist = new Array("channel", "callerid", "calleridname", "context", "extension", "priority");
+
+ me.chancallback = callback;
+ s = "<table class='chantable' align='center'>\n";
+ s = s + "\t<tr class='labels' id='labels'><td>Channel</td><td>State</td><td>Caller</td><td>Location</td><td>Link</td></tr>";
+ count=0;
+ for (x in channels) {
+ if (channels[x].channel) {
+ if (count % 2)
+ cclass = "chanlistodd";
+ else
+ cclass = "chanlisteven";
+ if (me.selecttarget && (me.selecttarget == x)) {
+ cclass = "chanlistselected";
+ foundactive = 1;
+ }
+ count++;
+ s = s + "\t<tr class='" + cclass + "' id='" + channels[x].channel + "' onClick='astmanEngine.clickChannel(event)'>";
+ s = s + "<td>" + channels[x].channel + "</td>";
+ if (channels[x].state)
+ s = s + "<td>" + channels[x].state + "</td>";
+ else
+ s = s + "<td><i class='light'>unknown</i></td>";
+ if (channels[x].calleridname && channels[x].callerid && channels[x].calleridname != "<unknown>") {
+ cid = channels[x].calleridname.escapeHTML() + " &lt;" + channels[x].callerid.escapeHTML() + "&gt;";
+ } else if (channels[x].calleridname && (channels[x].calleridname != "<unknown>")) {
+ cid = channels[x].calleridname.escapeHTML();
+ } else if (channels[x].callerid) {
+ cid = channels[x].callerid.escapeHTML();
+ } else {
+ cid = "<i class='light'>Unknown</i>";
+ }
+ s = s + "<td>" + cid + "</td>";
+ if (channels[x].extension) {
+ s = s + "<td>" + channels[x].extension + "@" + channels[x].context + ":" + channels[x].priority + "</td>";
+ } else {
+ s = s + "<td><i class='light'>None</i></td>";
+ }
+ if (channels[x].link) {
+ s = s + "<td>" + channels[x].link + "</td>";
+ } else {
+ s = s + "<td><i class='light'>None</i></td>";
+ }
+ s = s + "</tr>\n";
+ found++;
+ }
+ }
+ if (!found)
+ s += "<tr><td colspan=" + fieldlist.length + "><i class='light'>No active channels</i></td>\n";
+ s += "</table>\n";
+ if (!foundactive) {
+ me.selecttarget = null;
+ }
+ return s;
+ };
+ this.parseResponse = function(t, callback) {
+ var msgs = new Array();
+ var inmsg = 0;
+ var msgnum = 0;
+ var x,y;
+ var s = t.responseText;
+ var allheaders = s.split('\r\n');
+ if (me.debug)
+ me.debug.value = "\n";
+ for (x=0;x<allheaders.length;x++) {
+ if (allheaders[x].length) {
+ var fields = allheaders[x].split(': ');
+ if (!inmsg) {
+ msgs[msgnum] = new Object();
+ msgs[msgnum].headers = new Array();
+ msgs[msgnum].names = new Array();
+ y=0;
+ }
+ msgs[msgnum].headers[fields[0].toLowerCase()] = fields[1];
+ msgs[msgnum].names[y++] = fields[0].toLowerCase();
+ if (me.debug)
+ me.debug.value = me.debug.value + "field " + fields[0] + "/" + fields[1] + "\n";
+ inmsg=1;
+ } else {
+ if (inmsg) {
+ if (me.debug)
+ me.debug.value = me.debug.value + " new message\n";
+ inmsg = 0;
+ msgnum++;
+ }
+ }
+ }
+ if (me.debug) {
+ me.debug.value = me.debug.value + "msgnum is " + msgnum + " and array length is " + msgs.length + "\n";
+ me.debug.value = me.debug.value + "length is " + msgs.length + "\n";
+ var x, y;
+ for (x=0;x<msgs.length;x++) {
+ for (y=0;y<msgs[x].names.length;y++) {
+ me.debug.value = me.debug.value + "msg "+ (x + 1) + "/" + msgs[x].names[y] + "/" + msgs[x].headers[msgs[x].names[y]] + "\n";
+ }
+ }
+ }
+ callback(msgs);
+ };
+ this.managerResponse = function(t) {
+ me.parseResponse(t, me.callback);
+ };
+ this.doEvents = function(msgs) {
+ me.eventcallback(msgs);
+ };
+ this.eventResponse = function(t) {
+ me.parseResponse(t, me.doEvents);
+ };
+ this.sendRequest = function(request, callback) {
+ var tmp;
+ var opt = {
+ method: 'get',
+ asynchronous: true,
+ onSuccess: this.managerResponse,
+ onFailure: function(t) {
+ alert("Error: " + t.status + ": " + t.statusText);
+ },
+ };
+ me.callback = callback;
+ opt.parameters = request;
+ tmp = new Ajax.Request(this.url, opt);
+ };
+ this.pollEvents = function() {
+ var tmp;
+ var opt = {
+ method: 'get',
+ asynchronous: true,
+ onSuccess: this.eventResponse,
+ onFailure: function(t) {
+ alert("Event Error: " + t.status + ": " + t.statusText);
+ },
+ };
+ opt.parameters="action=waitevent";
+ tmp = new Ajax.Request(this.url, opt);
+ };
+};
+
+astmanEngine = new Astman();
diff --git a/trunk/static-http/prototype.js b/trunk/static-http/prototype.js
new file mode 100644
index 000000000..0e85338ba
--- /dev/null
+++ b/trunk/static-http/prototype.js
@@ -0,0 +1,1781 @@
+/* Prototype JavaScript framework, version 1.4.0
+ * (c) 2005 Sam Stephenson <sam@conio.net>
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.4.0',
+ ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+ emptyFunction: function() {},
+ K: function(x) {return x}
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.inspect = function(object) {
+ try {
+ if (object == undefined) return 'undefined';
+ if (object == null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+}
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ var digits = this.toString(16);
+ if (this < 16) return '0' + digits;
+ return digits;
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ }
+});
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+Object.extend(String.prototype, {
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(eval);
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+ },
+
+ toQueryParams: function() {
+ var pairs = this.match(/^\??(.*)$/)[1].split('&');
+ return pairs.inject({}, function(params, pairString) {
+ var pair = pairString.split('=');
+ params[pair[0]] = pair[1];
+ return params;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ camelize: function() {
+ var oStringList = this.split('-');
+ if (oStringList.length == 1) return oStringList[0];
+
+ var camelizedString = this.indexOf('-') == 0
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+ : oStringList[0];
+
+ for (var i = 1, len = oStringList.length; i < len; i++) {
+ var s = oStringList[i];
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+ }
+
+ return camelizedString;
+ },
+
+ inspect: function() {
+ return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
+ }
+});
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != $continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function (iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value >= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value <= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ iterator(value = collections.pluck(index));
+ return value;
+ });
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0; i < this.length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != undefined || value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0; i < this.length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ shift: function() {
+ var result = this[0];
+ for (var i = 0; i < this.length - 1; i++)
+ this[i] = this[i + 1];
+ this.length--;
+ return result;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+});
+var Hash = {
+ _each: function(iterator) {
+ for (key in this) {
+ var value = this[key];
+ if (typeof value == 'function') continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject($H(this), function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ return pair.map(encodeURIComponent).join('=');
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ }
+}
+
+function $H(object) {
+ var hash = Object.extend({}, object || {});
+ Object.extend(hash, Enumerable);
+ Object.extend(hash, Hash);
+ return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ do {
+ iterator(value);
+ value = value.succ();
+ } while (this.include(value));
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+ function() {return new XMLHttpRequest()}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responderToAdd) {
+ if (!this.include(responderToAdd))
+ this.responders.push(responderToAdd);
+ },
+
+ unregister: function(responderToRemove) {
+ this.responders = this.responders.without(responderToRemove);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (responder[callback] && typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+ },
+
+ responseIsSuccess: function() {
+ return this.transport.status == undefined
+ || this.transport.status == 0
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ responseIsFailure: function() {
+ return !this.responseIsSuccess();
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ var parameters = this.options.parameters || '';
+ if (parameters.length > 0) parameters += '&_=';
+
+ try {
+ this.url = url;
+ if (this.options.method == 'get' && parameters.length > 0)
+ this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.options.method, this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+ }
+
+ this.setRequestHeaders();
+
+ var body = this.options.postBody ? this.options.postBody : parameters;
+ this.transport.send(this.options.method == 'post' ? body : null);
+
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ setRequestHeaders: function() {
+ var requestHeaders =
+ ['X-Requested-With', 'XMLHttpRequest',
+ 'X-Prototype-Version', Prototype.Version];
+
+ if (this.options.method == 'post') {
+ requestHeaders.push('Content-type',
+ 'application/x-www-form-urlencoded');
+
+ /* Force "Connection: close" for Mozilla browsers to work around
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
+ * header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType)
+ requestHeaders.push('Connection', 'close');
+ }
+
+ if (this.options.requestHeaders)
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+ for (var i = 0; i < requestHeaders.length; i += 2)
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState != 1)
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ header: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {}
+ },
+
+ evalJSON: function() {
+ try {
+ return eval(this.header('X-JSON'));
+ } catch (e) {}
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (event == 'Complete') {
+ try {
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.containers = {
+ success: container.success ? $(container.success) : $(container),
+ failure: container.failure ? $(container.failure) :
+ (container.success ? null : $(container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, object) {
+ this.updateContent();
+ onComplete(transport, object);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.responseIsSuccess() ?
+ this.containers.success : this.containers.failure;
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts)
+ response = response.stripScripts();
+
+ if (receiver) {
+ if (this.options.insertion) {
+ new this.options.insertion(receiver, response);
+ } else {
+ Element.update(receiver, response);
+ }
+ }
+
+ if (this.responseIsSuccess()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ return $A(children).inject([], function(elements, child) {
+ if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ elements.push(child);
+ return elements;
+ });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) {
+ var Element = new Object();
+}
+
+Object.extend(Element, {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ }
+ },
+
+ hide: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = 'none';
+ }
+ },
+
+ show: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = '';
+ }
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ },
+
+ update: function(element, html) {
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).include(className);
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).add(className);
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).remove(className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var node = element.childNodes[i];
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ Element.remove(node);
+ }
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.match(/^\s*$/);
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var x = element.x ? element.x : element.offsetLeft,
+ y = element.y ? element.y : element.offsetTop;
+ window.scrollTo(x, y);
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ var value = element.style[style.camelize()];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style.camelize()];
+ }
+ }
+
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+ return value == 'auto' ? null : value;
+ },
+
+ setStyle: function(element, style) {
+ element = $(element);
+ for (name in style)
+ element.style[name.camelize()] = style[name];
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'display') != 'none')
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = '';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = 'none';
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element._overflow = element.style.overflow;
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element.style.overflow = element._overflow;
+ element._overflow = undefined;
+ }
+});
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ if (this.element.tagName.toLowerCase() == 'tbody') {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set(this.toArray().concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set(this.select(function(className) {
+ return className != classNameToRemove;
+ }).join(' '));
+ },
+
+ toString: function() {
+ return this.toArray().join(' ');
+ }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Field = {
+ clear: function() {
+ for (var i = 0; i < arguments.length; i++)
+ $(arguments[i]).value = '';
+ },
+
+ focus: function(element) {
+ $(element).focus();
+ },
+
+ present: function() {
+ for (var i = 0; i < arguments.length; i++)
+ if ($(arguments[i]).value == '') return false;
+ return true;
+ },
+
+ select: function(element) {
+ $(element).select();
+ },
+
+ activate: function(element) {
+ element = $(element);
+ element.focus();
+ if (element.select)
+ element.select();
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+ serialize: function(form) {
+ var elements = Form.getElements($(form));
+ var queryComponents = new Array();
+
+ for (var i = 0; i < elements.length; i++) {
+ var queryComponent = Form.Element.serialize(elements[i]);
+ if (queryComponent)
+ queryComponents.push(queryComponent);
+ }
+
+ return queryComponents.join('&');
+ },
+
+ getElements: function(form) {
+ form = $(form);
+ var elements = new Array();
+
+ for (tagName in Form.Element.Serializers) {
+ var tagElements = form.getElementsByTagName(tagName);
+ for (var j = 0; j < tagElements.length; j++)
+ elements.push(tagElements[j]);
+ }
+ return elements;
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(input);
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.blur();
+ element.disabled = 'true';
+ }
+ },
+
+ enable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.disabled = '';
+ }
+ },
+
+ findFirstElement: function(form) {
+ return Form.getElements(form).find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ Field.activate(Form.findFirstElement(form));
+ },
+
+ reset: function(form) {
+ $(form).reset();
+ }
+}
+
+Form.Element = {
+ serialize: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter) {
+ var key = encodeURIComponent(parameter[0]);
+ if (key.length == 0) return;
+
+ if (parameter[1].constructor != Array)
+ parameter[1] = [parameter[1]];
+
+ return parameter[1].map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ }
+}
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'submit':
+ case 'hidden':
+ case 'password':
+ case 'text':
+ return Form.Element.Serializers.textarea(element);
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ }
+ return false;
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ return Form.Element.Serializers[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var value = '', opt, index = element.selectedIndex;
+ if (index >= 0) {
+ opt = element.options[index];
+ value = opt.value;
+ if (!value && !('value' in opt))
+ value = opt.text;
+ }
+ return [element.name, value];
+ },
+
+ selectMany: function(element) {
+ var value = new Array();
+ for (var i = 0; i < element.length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) {
+ var optValue = opt.value;
+ if (!optValue && !('value' in opt))
+ optValue = opt.text;
+ value.push(optValue);
+ }
+ }
+ return [element.name, value];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ var elements = Form.getElements(this.element);
+ for (var i = 0; i < elements.length; i++)
+ this.registerCallback(elements[i]);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0; i < Event.observers.length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ element.detachEvent('on' + name, observer);
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ clone: function(source, target) {
+ source = $(source);
+ target = $(target);
+ target.style.position = 'absolute';
+ var offsets = this.cumulativeOffset(source);
+ target.style.top = offsets[1] + 'px';
+ target.style.left = offsets[0] + 'px';
+ target.style.width = source.offsetWidth + 'px';
+ target.style.height = source.offsetHeight + 'px';
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+} \ No newline at end of file
diff --git a/trunk/tests/Makefile b/trunk/tests/Makefile
new file mode 100644
index 000000000..83ce0b262
--- /dev/null
+++ b/trunk/tests/Makefile
@@ -0,0 +1,20 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for test modules
+#
+# Copyright (C) 2008, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=test
+MENUSELECT_CATEGORY=TEST
+MENUSELECT_DESCRIPTION=Test Modules
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/trunk/tests/test_skel.c b/trunk/tests/test_skel.c
new file mode 100644
index 000000000..9f8008658
--- /dev/null
+++ b/trunk/tests/test_skel.c
@@ -0,0 +1,54 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) <Year>, <Your Name Here>
+ *
+ * <Your Name Here> <<Your Email Here>>
+ *
+ * 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 Skeleton Test
+ *
+ * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk test module
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+static int load_module(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Test");
diff --git a/trunk/utils/Makefile b/trunk/utils/Makefile
new file mode 100644
index 000000000..4c6bb1114
--- /dev/null
+++ b/trunk/utils/Makefile
@@ -0,0 +1,177 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Various utilities
+#
+# 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
+#
+
+ASTTOPDIR?=..
+-include $(ASTTOPDIR)/menuselect.makeopts
+
+.PHONY: clean all uninstall
+
+# to get check_expr, add it to the ALL_UTILS list
+ALL_UTILS:=astman smsq stereorize streamplayer aelparse muted check_expr conf2ael hashtest2 hashtest astcanary
+UTILS:=$(ALL_UTILS)
+
+LIBS += $(BKTR_LIB) # astobj2 with devmode uses backtrace
+
+include $(ASTTOPDIR)/Makefile.rules
+
+ifeq ($(OSARCH),SunOS)
+ LIBS+=-lsocket -lnsl
+ UTILS:=$(filter-out muted,$(UTILS))
+endif
+
+ifeq ($(OSARCH),OpenBSD)
+ UTILS:=$(filter-out muted,$(UTILS))
+endif
+
+ifeq ($(OSARCH),cygwin)
+ UTILS:=$(filter-out muted,$(UTILS))
+endif
+
+ifeq ($(OSARCH),mingw32)
+ UTILS:=
+endif
+
+ifneq ($(findstring darwin,$(OSARCH)),)
+ AUDIO_LIBS=-framework CoreAudio
+endif
+
+ifeq ($(POPT_LIB),)
+ UTILS:=$(filter-out smsq,$(UTILS))
+endif
+
+ifeq ($(NEWT_LIB),)
+ UTILS:=$(filter-out astman,$(UTILS))
+endif
+
+ifneq ($(filter pbx_ael,$(MENUSELECT_PBX)),)
+ UTILS:=$(filter-out aelparse,$(UTILS))
+ UTILS:=$(filter-out conf2ael,$(UTILS))
+endif
+
+all: $(UTILS)
+
+install:
+ for x in $(UTILS); do \
+ if [ "$$x" != "none" ]; then \
+ $(INSTALL) -m 755 $$x $(DESTDIR)$(ASTSBINDIR)/$$x; \
+ fi; \
+ done
+
+uninstall:
+ for x in $(ALL_UTILS); do rm -f $$x $(DESTDIR)$(ASTSBINDIR)/$$x; done
+
+clean:
+ rm -f *.o $(ALL_UTILS) check_expr
+ rm -f .*.o.d .*.oo.d
+ rm -f *.s *.i
+ rm -f md5.c strcompat.c ast_expr2.c ast_expr2f.c pbx_ael.c pval.c hashtab.c
+ rm -f aelparse.c aelbison.c conf2ael
+ rm -f utils.c threadstorage.c sha1.c astobj2.c hashtest2 hashtest
+
+md5.c: $(ASTTOPDIR)/main/md5.c
+ @cp $< $@
+
+astman: astman.o md5.o
+astman: LIBS+=$(NEWT_LIB)
+
+stereorize: stereorize.o frame.o
+stereorize: LIBS+=-lm
+
+hashtab.c: $(ASTTOPDIR)/main/hashtab.c
+ @cp $< $@
+
+strcompat.c: $(ASTTOPDIR)/main/strcompat.c
+ @cp $< $@
+
+$(ASTTOPDIR)/main/ast_expr2.c:
+ @echo " [BISON] $(ASTTOPDIR)/main/ast_expr2.y -> $@"
+ @bison -o $@ -d --name-prefix=ast_yy $(ASTTOPDIR)/main/ast_expr2.y
+
+$(ASTTOPDIR)/main/ast_expr2f.c:
+ @echo " [FLEX] $(ASTTOPDIR)/main/ast_expr2.fl -> $@"
+ @flex -o $@ --full $(ASTTOPDIR)/main/ast_expr2.fl
+
+pval.c: $(ASTTOPDIR)/res/ael/pval.c
+ @cp $< $@
+
+ast_expr2.c: $(ASTTOPDIR)/main/ast_expr2.c
+ @cp $< $@
+
+ast_expr2f.c: $(ASTTOPDIR)/main/ast_expr2f.c
+ @cp $< $@
+
+ast_expr2f.o: ASTCFLAGS+=-DSTANDALONE_AEL -I$(ASTTOPDIR)/main
+
+pval.o : ASTCFLAGS+=-DSTANDALONE
+
+check_expr: check_expr.o ast_expr2.o ast_expr2f.o strcompat.o threadstorage.o clicompat.o
+
+aelbison.c: $(ASTTOPDIR)/res/ael/ael.tab.c
+ @cp $< $@
+
+aelbison.o: ASTCFLAGS+=-I$(ASTTOPDIR)/res/ael -DYYENABLE_NLS=0
+
+pbx_ael.c: $(ASTTOPDIR)/pbx/pbx_ael.c
+ @cp $< $@
+
+pbx_ael.o: ASTCFLAGS+=-DSTANDALONE_AEL
+
+aelparse.c: $(ASTTOPDIR)/res/ael/ael_lex.c
+ @cp $< $@
+
+aelparse.o: ASTCFLAGS+=-I$(ASTTOPDIR)/res -DSTANDALONE_AEL
+
+aelparse: aelparse.o aelbison.o pbx_ael.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o
+
+astobj2.c: $(ASTTOPDIR)/main/astobj2.c
+ @cp $< $@
+
+utils.c: $(ASTTOPDIR)/main/utils.c
+ @cp $< $@
+
+sha1.c: $(ASTTOPDIR)/main/sha1.c
+ @cp $< $@
+
+threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c
+ @cp $< $@
+
+hashtest2.o: ASTCFLAGS+=-O0
+
+hashtest2: hashtest2.o md5.o utils.o astobj2.o sha1.o strcompat.o threadstorage.o clicompat.o
+
+hashtest: hashtest.o md5.o hashtab.o utils.o sha1.o strcompat.o threadstorage.o clicompat.o
+
+hashtest.o: ASTCFLAGS+=-O0
+
+extconf.o: extconf.c
+
+conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o
+
+testexpr2s: $(ASTTOPDIR)/main/ast_expr2f.c $(ASTTOPDIR)/main/ast_expr2.c $(ASTTOPDIR)/main/ast_expr2.h
+ $(CC) -g -c -I$(ASTTOPDIR)/include -DSTANDALONE_AEL $(ASTTOPDIR)/main/ast_expr2f.c -o ast_expr2f.o
+ $(CC) -g -c -I$(ASTTOPDIR)/include -DSTANDALONE_AEL $(ASTTOPDIR)/main/ast_expr2.c -o ast_expr2.o
+ $(CC) -g -o testexpr2s ast_expr2f.o ast_expr2.o
+ rm ast_expr2.o ast_expr2f.o
+ ./testexpr2s expr2.testinput
+
+smsq: smsq.o strcompat.o
+smsq: LIBS+=$(POPT_LIB)
+
+streamplayer: streamplayer.o
+
+muted: muted.o
+muted: LIBS+=$(AUDIO_LIBS)
+
+ifneq ($(wildcard .*.d),)
+ include .*.d
+endif
diff --git a/trunk/utils/ael_main.c b/trunk/utils/ael_main.c
new file mode 100644
index 000000000..7f20365f3
--- /dev/null
+++ b/trunk/utils/ael_main.c
@@ -0,0 +1,584 @@
+/*
+ * XXX this file probably need a fair amount of cleanup, at the very least:
+ *
+ * - documenting its purpose;
+ * - removing all unnecessary headers and other stuff from the sources
+ * it was copied from;
+ * - fixing the formatting
+ */
+#include "asterisk.h"
+
+#include <locale.h>
+#include <ctype.h>
+#include <regex.h>
+#include <limits.h>
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/extconf.h"
+
+/*** MODULEINFO
+ <depend>res_ael_share</depend>
+ ***/
+
+struct namelist
+{
+ char name[100];
+ char name2[100];
+ struct namelist *next;
+};
+
+struct ast_context
+{
+ int extension_count;
+ char name[100];
+ char registrar[100];
+ struct namelist *includes;
+ struct namelist *ignorepats;
+ struct namelist *switches;
+ struct namelist *eswitches;
+
+ struct namelist *includes_last;
+ struct namelist *ignorepats_last;
+ struct namelist *switches_last;
+ struct namelist *eswitches_last;
+
+ struct ast_context *next;
+};
+
+#define ADD_LAST(headptr,memptr) if(!headptr){ headptr=(memptr); (headptr##_last)=(memptr);} else {(headptr##_last)->next = (memptr); (headptr##_last) = (memptr);}
+
+void destroy_namelist(struct namelist *x);
+void destroy_namelist(struct namelist *x)
+{
+ struct namelist *z,*z2;
+ for(z=x; z; z = z2)
+ {
+ z2 = z->next;
+ z->next = 0;
+ free(z);
+ }
+}
+
+struct namelist *create_name(const char *name);
+struct namelist *create_name(const char *name)
+{
+ struct namelist *x = calloc(1, sizeof(*x));
+ if (!x)
+ return NULL;
+ strncpy(x->name, name, sizeof(x->name) - 1);
+ return x;
+}
+
+struct ast_context *context_list;
+struct ast_context *last_context;
+struct namelist *globalvars;
+struct namelist *globalvars_last;
+
+int conts=0, extens=0, priors=0;
+char last_exten[18000];
+
+static char config_dir[PATH_MAX];
+static char var_dir[PATH_MAX];
+const char *ast_config_AST_CONFIG_DIR = config_dir;
+const char *ast_config_AST_VAR_DIR = var_dir;
+
+void ast_cli_register_multiple(void);
+int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar);
+void pbx_builtin_setvar(void *chan, void *data);
+struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar);
+struct ast_context * ast_context_find_or_create(void **extcontexts, const char *name, const char *registrar);
+void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
+void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar);
+void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar);
+void ast_merge_contexts_and_delete(void);
+void ast_context_verify_includes(void);
+struct ast_context * ast_walk_contexts(void);
+void ast_cli_unregister_multiple(void);
+void ast_context_destroy(void);
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...);
+char *ast_process_quotes_and_slashes(char *start, char find, char replace_with);
+void ast_verbose(const char *fmt, ...);
+struct ast_app *pbx_findapp(const char *app);
+void filter_leading_space_from_exprs(char *str);
+void filter_newlines(char *str);
+static int quiet = 0;
+static int no_comp = 0;
+static int use_curr_dir = 0;
+static int dump_extensions = 0;
+static int FIRST_TIME = 0;
+static FILE *dumpfile;
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action)
+{
+ return localized_find_extension(bypass, q, context, exten, priority, label, callerid, action);
+}
+
+struct ast_app *pbx_findapp(const char *app)
+{
+ return (struct ast_app*)1; /* so as not to trigger an error */
+}
+
+struct ast_custom_function *ast_custom_function_find(const char *name);
+
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ return 0; /* in "standalone" mode, functions are just not avail */
+}
+
+void ast_register_file_version(const char *file, const char *version)
+{
+}
+
+void ast_unregister_file_version(const char *file)
+{
+}
+
+int ast_add_profile(const char *x, uint64_t scale)
+{
+ if (!no_comp)
+ printf("Executed ast_add_profile();\n");
+
+ return 0;
+}
+
+int ast_loader_register(int (*updater)(void))
+{
+ return 1;
+}
+
+int ast_loader_unregister(int (*updater)(void))
+{
+ return 1;
+}
+void ast_module_register(const struct ast_module_info *x)
+{
+}
+
+void ast_module_unregister(const struct ast_module_info *x)
+{
+}
+
+
+void ast_cli_register_multiple(void)
+{
+ if(!no_comp)
+ printf("Executed ast_cli_register_multiple();\n");
+}
+
+void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
+void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count)
+{
+ if (cp1 && *cp1)
+ strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for
+ a possible var substitution on extension names,
+ so....! */
+ else
+ *cp2 = 0;
+}
+
+int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ priors++;
+ con->extension_count++;
+ if (strcmp(extension,last_exten) != 0) {
+ extens++;
+ strcpy(last_exten, extension);
+ }
+ if (!label) {
+ label = "(null)";
+ }
+ if (!callerid) {
+ callerid = "(null)";
+ }
+ if (!application) {
+ application = "(null)";
+ }
+
+ if(!no_comp)
+ printf("Executed ast_add_extension2(context=%s, rep=%d, exten=%s, priority=%d, label=%s, callerid=%s, appl=%s, data=%s, FREE, registrar=%s);\n",
+ con->name, replace, extension, priority, label, callerid, application, (data?(char*)data:"(null)"), registrar);
+
+ if( dump_extensions && dumpfile ) {
+ struct namelist *n;
+ char *data2,*data3=0;
+ int commacount = 0;
+
+ if( FIRST_TIME ) {
+ FIRST_TIME = 0;
+
+ if( globalvars )
+ fprintf(dumpfile,"[globals]\n");
+
+ for(n=globalvars;n;n=n->next) {
+ fprintf(dumpfile, "%s\n", n->name);
+ }
+ }
+
+ /* print out each extension , possibly the context header also */
+ if( con != last_context ) {
+ fprintf(dumpfile,"\n\n[%s]\n", con->name);
+ last_context = con;
+ for(n=con->ignorepats;n;n=n->next) {
+ fprintf(dumpfile, "ignorepat => %s\n", n->name);
+ }
+ for(n=con->includes;n;n=n->next) {
+ fprintf(dumpfile, "include => %s\n", n->name);
+ }
+ for(n=con->switches;n;n=n->next) {
+ fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2);
+ }
+ for(n=con->eswitches;n;n=n->next) {
+ fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2);
+ }
+
+ }
+ if( data ) {
+ filter_newlines((char*)data);
+ filter_leading_space_from_exprs((char*)data);
+
+ /* compiling turns commas into vertical bars in the app data, and also removes the backslash from before escaped commas;
+ we have to restore the escaping backslash in front of any commas; the vertical bars are OK to leave as-is */
+ for (data2 = data; *data2; data2++) {
+ if (*data2 == ',')
+ commacount++; /* we need to know how much bigger the string will grow-- one backslash for each comma */
+ }
+ if (commacount)
+ {
+ char *d3,*d4;
+
+ data2 = (char*)malloc(strlen(data)+commacount+1);
+ data3 = data;
+ d3 = data;
+ d4 = data2;
+ while (*d3) {
+ if (*d3 == ',') {
+ *d4++ = '\\'; /* put a backslash in front of each comma */
+ *d4++ = *d3++;
+ } else
+ *d4++ = *d3++; /* or just copy the char */
+ }
+ *d4++ = 0; /* cap off the new string */
+ data = data2;
+ } else
+ data2 = 0;
+
+ if( strcmp(label,"(null)") != 0 )
+ fprintf(dumpfile,"exten => %s,%d(%s),%s(%s)\n", extension, priority, label, application, (char*)data);
+ else
+ fprintf(dumpfile,"exten => %s,%d,%s(%s)\n", extension, priority, application, (char*)data);
+
+ if (data2) {
+ free(data2);
+ data2 = 0;
+ data = data3; /* restore data to pre-messedup state */
+ }
+
+ } else {
+
+ if( strcmp(label,"(null)") != 0 )
+ fprintf(dumpfile,"exten => %s,%d(%s),%s\n", extension, priority, label, application);
+ else
+ fprintf(dumpfile,"exten => %s,%d,%s\n", extension, priority, application);
+ }
+ }
+
+ /* since add_extension2 is responsible for the malloc'd data stuff */
+ if( data )
+ free(data);
+ return 0;
+}
+
+void pbx_builtin_setvar(void *chan, void *data)
+{
+ struct namelist *x = create_name(data);
+ if(!no_comp)
+ printf("Executed pbx_builtin_setvar(chan, data=%s);\n", (char*)data);
+
+ if( dump_extensions ) {
+ x = create_name(data);
+ ADD_LAST(globalvars,x);
+ }
+}
+
+
+struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar)
+{
+ struct ast_context *x = calloc(1, sizeof(*x));
+ if (!x)
+ return NULL;
+ x->next = context_list;
+ context_list = x;
+ if (!no_comp)
+ printf("Executed ast_context_create(conts, name=%s, registrar=%s);\n", name, registrar);
+ conts++;
+ strncpy(x->name, name, sizeof(x->name) - 1);
+ strncpy(x->registrar, registrar, sizeof(x->registrar) - 1);
+ return x;
+}
+
+struct ast_context * ast_context_find_or_create(void **extcontexts, const char *name, const char *registrar)
+{
+ struct ast_context *x = calloc(1, sizeof(*x));
+ if (!x)
+ return NULL;
+ x->next = context_list;
+ context_list = x;
+ if (!no_comp)
+ printf("Executed ast_context_find_or_create(conts, name=%s, registrar=%s);\n", name, registrar);
+ conts++;
+ strncpy(x->name, name, sizeof(x->name) - 1);
+ strncpy(x->registrar, registrar, sizeof(x->registrar) - 1);
+ return x;
+}
+
+void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ if(!no_comp)
+ printf("Executed ast_context_add_ignorepat2(con, value=%s, registrar=%s);\n", value, registrar);
+ if( dump_extensions ) {
+ struct namelist *x;
+ x = create_name(value);
+ ADD_LAST(con->ignorepats,x);
+ }
+}
+
+void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar)
+{
+ if(!no_comp)
+ printf("Executed ast_context_add_include2(con, value=%s, registrar=%s);\n", value, registrar);
+ if( dump_extensions ) {
+ struct namelist *x;
+ x = create_name((char*)value);
+ ADD_LAST(con->includes,x);
+ }
+}
+
+void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar)
+{
+ if(!no_comp)
+ printf("Executed ast_context_add_switch2(con, value=%s, data=%s, eval=%d, registrar=%s);\n", value, data, eval, registrar);
+ if( dump_extensions ) {
+ struct namelist *x;
+ x = create_name((char*)value);
+ strncpy(x->name2,data,100);
+ if( eval ) {
+
+ ADD_LAST(con->switches,x);
+
+ } else {
+
+ ADD_LAST(con->eswitches,x);
+ }
+ }
+}
+
+void ast_merge_contexts_and_delete(void)
+{
+ if(!no_comp)
+ printf("Executed ast_merge_contexts_and_delete();\n");
+}
+
+void ast_context_verify_includes(void)
+{
+ if(!no_comp)
+ printf("Executed ast_context_verify_includes();\n");
+}
+
+struct ast_context * ast_walk_contexts(void)
+{
+ if(!no_comp)
+ printf("Executed ast_walk_contexts();\n");
+ return 0;
+}
+
+void ast_cli_unregister_multiple(void)
+{
+ if(!no_comp)
+ printf("Executed ast_cli_unregister_multiple();\n");
+}
+
+void ast_context_destroy(void)
+{
+ if( !no_comp)
+ printf("Executed ast_context_destroy();\n");
+}
+
+void filter_leading_space_from_exprs(char *str)
+{
+ /* Mainly for aesthetics */
+ char *t, *v, *u = str;
+
+ while ( u && *u ) {
+
+ if( *u == '$' && *(u+1) == '[' ) {
+ t = u+2;
+ while( *t == '\n' || *t == '\r' || *t == '\t' || *t == ' ' ) {
+ v = t;
+ while ( *v ) {
+ *v = *(v+1);
+ v++;
+ }
+ }
+ }
+
+ u++;
+ }
+}
+
+void filter_newlines(char *str)
+{
+ /* remove all newlines, returns */
+ char *t=str;
+ while( t && *t ) {
+ if( *t == '\n' || *t == '\r' ) {
+ *t = ' '; /* just replace newlines and returns with spaces; they act as
+ token separators, and just blindly removing them could be
+ harmful. */
+ }
+ t++;
+ }
+}
+
+
+extern struct module_symbols mod_data;
+int ael_external_load_module(void);
+
+
+int main(int argc, char **argv)
+{
+ int i;
+ struct namelist *n;
+ struct ast_context *lp,*lp2;
+
+ for(i=1;i<argc;i++) {
+ if( argv[i][0] == '-' && argv[i][1] == 'n' )
+ no_comp =1;
+ if( argv[i][0] == '-' && argv[i][1] == 'q' ) {
+ quiet = 1;
+ no_comp =1;
+ }
+ if( argv[i][0] == '-' && argv[i][1] == 'd' )
+ use_curr_dir =1;
+ if( argv[i][0] == '-' && argv[i][1] == 'w' )
+ dump_extensions =1;
+ }
+
+ if( !quiet ) {
+ printf("\n(If you find progress and other non-error messages irritating, you can use -q to suppress them)\n");
+ if( !no_comp )
+ printf("\n(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)\n\n");
+ if( !use_curr_dir )
+ printf("\n(You can use the -d option if you want to use the current working directory as the CONFIG_DIR. I will look in this dir for extensions.ael* and its included files)\n\n");
+ if( !dump_extensions )
+ printf("\n(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)\n");
+ }
+
+ if( use_curr_dir ) {
+ strcpy(config_dir, ".");
+ localized_use_local_dir();
+ }
+ else {
+ strcpy(config_dir, "/etc/asterisk");
+ localized_use_conf_dir();
+ }
+ strcpy(var_dir, "/var/lib/asterisk");
+
+ if( dump_extensions ) {
+ dumpfile = fopen("extensions.conf.aeldump","w");
+ if( !dumpfile ) {
+ printf("\n\nSorry, cannot open extensions.conf.aeldump for writing! Correct the situation and try again!\n\n");
+ exit(10);
+ }
+
+ }
+
+ FIRST_TIME = 1;
+
+ ael_external_load_module();
+
+ ast_log(4, "ael2_parse", __LINE__, "main", "%d contexts, %d extensions, %d priorities\n", conts, extens, priors);
+
+ if( dump_extensions && dumpfile ) {
+
+ for( lp = context_list; lp; lp = lp->next ) { /* print out any contexts that didn't have any
+ extensions in them */
+ if( lp->extension_count == 0 ) {
+
+ fprintf(dumpfile,"\n\n[%s]\n", lp->name);
+
+ for(n=lp->ignorepats;n;n=n->next) {
+ fprintf(dumpfile, "ignorepat => %s\n", n->name);
+ }
+ for(n=lp->includes;n;n=n->next) {
+ fprintf(dumpfile, "include => %s\n", n->name);
+ }
+ for(n=lp->switches;n;n=n->next) {
+ fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2);
+ }
+ for(n=lp->eswitches;n;n=n->next) {
+ fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2);
+ }
+ }
+ }
+ }
+
+ if( dump_extensions && dumpfile )
+ fclose(dumpfile);
+
+ for( lp = context_list; lp; lp = lp2 ) { /* free the ast_context structs */
+ lp2 = lp->next;
+ lp->next = 0;
+
+ destroy_namelist(lp->includes);
+ destroy_namelist(lp->ignorepats);
+ destroy_namelist(lp->switches);
+ destroy_namelist(lp->eswitches);
+
+ free(lp);
+ }
+
+ return 0;
+}
diff --git a/trunk/utils/astcanary.c b/trunk/utils/astcanary.c
new file mode 100644
index 000000000..eb9f17208
--- /dev/null
+++ b/trunk/utils/astcanary.c
@@ -0,0 +1,84 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Tilghman Lesher <tlesher AT digium DOT 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.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/*!\brief
+ * At one time, canaries were carried along with coal miners down
+ * into a mine. Their purpose was to alert the miners when they
+ * had drilled into a pocket of methane gas or another noxious
+ * substance. The canary, being the most sensitive animal would
+ * immediately fall over. Seeing this, the miners could take
+ * action to escape the mine, seeing an imminent danger.
+ *
+ * This process serves a similar purpose, though with the realtime
+ * priority being the reason. When a thread starts running away
+ * with the processor, it is typically difficult to tell what
+ * thread caused the problem, as the machine acts as if it is
+ * locked up (in fact, what has happened is that Asterisk runs at
+ * a higher priority than even the login shell, so the runaway
+ * thread hogs all available CPU time.
+ *
+ * If that happens, this canary process will cease to get any
+ * process time, which we can monitor with a realtime thread in
+ * Asterisk. Should that happen, that monitoring thread may take
+ * immediate action to slow down Asterisk to regular priority,
+ * thus allowing an administrator to login to the system and
+ * restart Asterisk or perhaps take another course of action
+ * (such as retrieving a backtrace to let the developers know
+ * what precisely went wrong).
+ *
+ * Note that according to POSIX.1, all threads inside a single
+ * process must share the same priority, so when the monitoring
+ * thread deprioritizes itself, it deprioritizes all threads at
+ * the same time. This is also why this canary must exist as a
+ * completely separate process and not simply as a thread within
+ * Asterisk itself.
+ */
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ /* Run at normal priority */
+ setpriority(PRIO_PROCESS, 0, 0);
+ for (;;) {
+ /* Update the modification times (checked from Asterisk) */
+ if (utime(argv[1], NULL)) {
+ /* Recreate the file if it doesn't exist */
+ if ((fd = open(argv[1], O_RDWR | O_TRUNC | O_CREAT)) > -1)
+ close(fd);
+ else
+ exit(1);
+ continue;
+ }
+
+ /* Run occasionally */
+ sleep(5);
+ }
+
+ /* Never reached */
+ return 0;
+}
+
diff --git a/trunk/utils/astman.1 b/trunk/utils/astman.1
new file mode 100644
index 000000000..6a36ca4da
--- /dev/null
+++ b/trunk/utils/astman.1
@@ -0,0 +1,102 @@
+.\" $Header$
+.\"
+.\" transcript compatibility for postscript use.
+.\"
+.\" synopsis: .P! <file.ps>
+.\"
+.de P!
+.fl
+\!!1 setgray
+.fl
+\\&.\"
+.fl
+\!!0 setgray
+.fl \" force out current output buffer
+\!!save /psv exch def currentpoint translate 0 0 moveto
+\!!/showpage{}def
+.fl \" prolog
+.sy sed \-e 's/^/!/' \\$1\" bring in postscript file
+\!!psv restore
+.
+.de pF
+.ie \\*(f1 .ds f1 \\n(.f
+.el .ie \\*(f2 .ds f2 \\n(.f
+.el .ie \\*(f3 .ds f3 \\n(.f
+.el .ie \\*(f4 .ds f4 \\n(.f
+.el .tm ? font overflow
+.ft \\$1
+..
+.de fP
+.ie !\\*(f4 \{\
+. ft \\*(f4
+. ds f4\"
+' br \}
+.el .ie !\\*(f3 \{\
+. ft \\*(f3
+. ds f3\"
+' br \}
+.el .ie !\\*(f2 \{\
+. ft \\*(f2
+. ds f2\"
+' br \}
+.el .ie !\\*(f1 \{\
+. ft \\*(f1
+. ds f1\"
+' br \}
+.el .tm ? font underflow
+..
+.ds f1\"
+.ds f2\"
+.ds f3\"
+.ds f4\"
+'\" t
+.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n
+.TH ASTMAN 1 "Jun 12th, 2005" "astman" "Linux Programmer's Manual"
+.SH NAME
+.B astman
+-- a client to asterisk's manager interface
+.SH SYNOPSIS
+.PP
+.B astman
+.I hostname
+
+.SH DESCRIPTION
+.B astman
+This program is a full-screen (terminal) client for Asterisk's manager
+interface.
+
+.SH OPTIONS
+.B hostname
+
+The host name or IP address to connect to (TCP port 5038). If astman
+fails to connect it will exit immidiately.
+
+.SH USAGE
+If \fBastman\fR has successfully cunnected to the manager port it will
+prompt the user for a username and a secret (password) for the manager
+interface on the remote Asterisk manager interface. It will then be able
+to report existing channels (calls). You will then be able to redirect
+calls to or terminate them.
+
+.SH "SEE ALSO"
+asterisk(8)
+
+http://www.voip-info.org/wiki-Asterisk+astman
+
+.SH BUGS
+The hostname does not default to localhost.
+
+Impossible to use a port other than 5038.
+
+The username and password cannot be defined from the command-line.
+
+I mean, what's the point in a man page if the syntax is so simple?
+
+.SH "AUTHOR"
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/trunk/utils/astman.c b/trunk/utils/astman.c
new file mode 100644
index 000000000..900e6f759
--- /dev/null
+++ b/trunk/utils/astman.c
@@ -0,0 +1,752 @@
+/*
+ * 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.
+ */
+
+/*
+ *
+ * ASTerisk MANager
+ *
+ */
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include <newt.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "asterisk/md5.h"
+#include "asterisk/linkedlists.h"
+
+#undef gethostbyname
+
+#define MAX_HEADERS 80
+#define MAX_LEN 256
+
+/*
+ * 2005.05.27 - different versions of newt define the type of the buffer
+ * for the 5th argument to newtEntry() as char ** or const char ** . To
+ * let the code compile cleanly with -Werror, we cast it to void * through
+ * _NEWT_CAST.
+ */
+#define _NEWT_CAST (void *)
+
+#define DEFAULT_MANAGER_PORT 5038
+
+struct message {
+ unsigned int hdrcount;
+ char headers[MAX_HEADERS][MAX_LEN];
+};
+
+static struct ast_mansession {
+ struct sockaddr_in sin;
+ int fd;
+ char inbuf[MAX_LEN];
+ int inlen;
+} session;
+
+struct ast_chan {
+ char name[80];
+ char exten[20];
+ char context[20];
+ char priority[20];
+ char callerid[40];
+ char state[10];
+ AST_LIST_ENTRY(ast_chan) list;
+};
+
+static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan);
+
+/* dummy functions to be compatible with the Asterisk core for md5.c */
+void ast_register_file_version(const char *file, const char *version);
+void ast_register_file_version(const char *file, const char *version)
+{
+}
+
+void ast_unregister_file_version(const char *file);
+void ast_unregister_file_version(const char *file)
+{
+}
+
+int ast_add_profile(const char *, uint64_t scale);
+int ast_add_profile(const char *s, uint64_t scale)
+{
+ return -1;
+}
+
+int64_t ast_profile(int, int64_t);
+int64_t ast_profile(int key, int64_t val)
+{
+ return 0;
+}
+int64_t ast_mark(int, int start1_stop0);
+int64_t ast_mark(int key, int start1_stop0)
+{
+ return 0;
+}
+
+/* end of dummy functions */
+
+static struct ast_chan *find_chan(char *name)
+{
+ struct ast_chan *chan;
+ AST_LIST_TRAVERSE(&chans, chan, list) {
+ if (!strcmp(name, chan->name))
+ return chan;
+ }
+ chan = malloc(sizeof(struct ast_chan));
+ if (chan) {
+ memset(chan, 0, sizeof(struct ast_chan));
+ strncpy(chan->name, name, sizeof(chan->name) - 1);
+ AST_LIST_INSERT_TAIL(&chans, chan, list);
+ }
+ return chan;
+}
+
+static void del_chan(char *name)
+{
+ struct ast_chan *chan;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) {
+ if (!strcmp(name, chan->name)) {
+ AST_LIST_REMOVE_CURRENT(list);
+ free(chan);
+ return;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+}
+
+static void fdprintf(int fd, char *fmt, ...)
+{
+ char stuff[4096];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(stuff, sizeof(stuff), fmt, ap);
+ va_end(ap);
+ write(fd, stuff, strlen(stuff));
+}
+
+static char *get_header(struct message *m, char *var)
+{
+ char cmp[80];
+ int x;
+ snprintf(cmp, sizeof(cmp), "%s: ", var);
+ for (x=0;x<m->hdrcount;x++)
+ if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
+ return m->headers[x] + strlen(cmp);
+ return "";
+}
+
+static int event_newstate(struct ast_mansession *s, struct message *m)
+{
+ struct ast_chan *chan;
+ chan = find_chan(get_header(m, "Channel"));
+ strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
+ return 0;
+}
+
+static int event_newexten(struct ast_mansession *s, struct message *m)
+{
+ struct ast_chan *chan;
+ chan = find_chan(get_header(m, "Channel"));
+ strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
+ strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
+ strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
+ return 0;
+}
+
+static int event_newchannel(struct ast_mansession *s, struct message *m)
+{
+ struct ast_chan *chan;
+ chan = find_chan(get_header(m, "Channel"));
+ strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
+ strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
+ return 0;
+}
+
+static int event_status(struct ast_mansession *s, struct message *m)
+{
+ struct ast_chan *chan;
+ chan = find_chan(get_header(m, "Channel"));
+ strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
+ strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
+ strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
+ strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
+ strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
+ return 0;
+}
+
+static int event_hangup(struct ast_mansession *s, struct message *m)
+{
+ del_chan(get_header(m, "Channel"));
+ return 0;
+}
+
+static int event_ignore(struct ast_mansession *s, struct message *m)
+{
+ return 0;
+}
+
+static int event_rename(struct ast_mansession *s, struct message *m)
+{
+ struct ast_chan *chan;
+ chan = find_chan(get_header(m, "Oldname"));
+ strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
+ return 0;
+}
+static struct event {
+ char *event;
+ int (*func)(struct ast_mansession *s, struct message *m);
+} events[] = {
+ { "Newstate", event_newstate },
+ { "Newchannel", event_newchannel },
+ { "Newexten", event_newexten },
+ { "Hangup", event_hangup },
+ { "Rename", event_rename },
+ { "Status", event_status },
+ { "Link", event_ignore },
+ { "Unlink", event_ignore },
+ { "StatusComplete", event_ignore },
+ { "Dial", event_ignore },
+ { "PeerStatus", event_ignore },
+ { "MessageWaiting", event_ignore },
+ { "Newcallerid", event_ignore }
+};
+
+static int process_message(struct ast_mansession *s, struct message *m)
+{
+ int x;
+ char event[80] = "";
+ strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
+ if (!strlen(event)) {
+ fprintf(stderr, "Missing event in request");
+ return 0;
+ }
+ for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
+ if (!strcasecmp(event, events[x].event)) {
+ if (events[x].func(s, m))
+ return -1;
+ break;
+ }
+ }
+ if (x >= sizeof(events) / sizeof(events[0]))
+ fprintf(stderr, "Ignoring unknown event '%s'", event);
+#if 0
+ for (x=0;x<m->hdrcount;x++) {
+ printf("Header: %s\n", m->headers[x]);
+ }
+#endif
+ return 0;
+}
+
+static void rebuild_channels(newtComponent c)
+{
+ void *prev = NULL;
+ struct ast_chan *chan;
+ char tmpn[42];
+ char tmp[256];
+ int x=0;
+ prev = newtListboxGetCurrent(c);
+ newtListboxClear(c);
+ AST_LIST_TRAVERSE(&chans, chan, list) {
+ snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
+ if (strlen(chan->exten))
+ snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
+ tmpn, chan->state,
+ chan->exten, chan->context, chan->priority);
+ else
+ snprintf(tmp, sizeof(tmp), "%-30s %8s",
+ tmpn, chan->state);
+ newtListboxAppendEntry(c, tmp, chan);
+ x++;
+ }
+ if (!x)
+ newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
+ newtListboxSetCurrentByKey(c, prev);
+}
+
+static int has_input(struct ast_mansession *s)
+{
+ int x;
+ for (x=1;x<s->inlen;x++)
+ if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
+ return 1;
+ return 0;
+}
+
+static int get_input(struct ast_mansession *s, char *output)
+{
+ /* output must have at least sizeof(s->inbuf) space */
+ int res;
+ int x;
+ struct timeval tv = {0, 0};
+ fd_set fds;
+ for (x=1;x<s->inlen;x++) {
+ if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
+ /* Copy output data up to and including \r\n */
+ memcpy(output, s->inbuf, x + 1);
+ /* Add trailing \0 */
+ output[x+1] = '\0';
+ /* Move remaining data back to the front */
+ memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
+ s->inlen -= (x + 1);
+ return 1;
+ }
+ }
+ if (s->inlen >= sizeof(s->inbuf) - 1) {
+ fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
+ s->inlen = 0;
+ }
+ FD_ZERO(&fds);
+ FD_SET(s->fd, &fds);
+ res = select(s->fd + 1, &fds, NULL, NULL, &tv);
+ if (res < 0) {
+ fprintf(stderr, "Select returned error: %s\n", strerror(errno));
+ } else if (res > 0) {
+ res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
+ if (res < 1)
+ return -1;
+ s->inlen += res;
+ s->inbuf[s->inlen] = '\0';
+ } else {
+ return 2;
+ }
+ return 0;
+}
+
+static int input_check(struct ast_mansession *s, struct message **mout)
+{
+ static struct message m;
+ int res;
+
+ if (mout)
+ *mout = NULL;
+
+ for(;;) {
+ res = get_input(s, m.headers[m.hdrcount]);
+ if (res == 1) {
+#if 0
+ fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
+ fgetc(stdin);
+#endif
+ /* Strip trailing \r\n */
+ if (strlen(m.headers[m.hdrcount]) < 2)
+ continue;
+ m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
+ if (!strlen(m.headers[m.hdrcount])) {
+ if (mout && strlen(get_header(&m, "Response"))) {
+ *mout = &m;
+ return 0;
+ }
+ if (process_message(s, &m))
+ break;
+ memset(&m, 0, sizeof(&m));
+ } else if (m.hdrcount < MAX_HEADERS - 1)
+ m.hdrcount++;
+ } else if (res < 0) {
+ return -1;
+ } else if (res == 2)
+ return 0;
+ }
+ return -1;
+}
+
+static struct message *wait_for_response(int timeout)
+{
+ struct message *m;
+ struct timeval tv;
+ int res;
+ fd_set fds;
+ for (;;) {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ FD_SET(session.fd, &fds);
+ res = select(session.fd + 1, &fds, NULL, NULL, &tv);
+ if (res < 1)
+ break;
+ if (input_check(&session, &m) < 0) {
+ return NULL;
+ }
+ if (m)
+ return m;
+ }
+ return NULL;
+}
+
+static int manager_action(char *action, char *fmt, ...)
+{
+ struct ast_mansession *s;
+ char tmp[4096];
+ va_list ap;
+
+ s = &session;
+ fdprintf(s->fd, "Action: %s\r\n", action);
+ va_start(ap, fmt);
+ vsnprintf(tmp, sizeof(tmp), fmt, ap);
+ va_end(ap);
+ write(s->fd, tmp, strlen(tmp));
+ fdprintf(s->fd, "\r\n");
+ return 0;
+}
+
+static int show_message(char *title, char *msg)
+{
+ newtComponent form;
+ newtComponent label;
+ newtComponent ok;
+ struct newtExitStruct es;
+
+ newtCenteredWindow(60,7, title);
+
+ label = newtLabel(4,1,msg);
+ ok = newtButton(27, 3, "OK");
+ form = newtForm(NULL, NULL, 0);
+ newtFormAddComponents(form, label, ok, NULL);
+ newtFormRun(form, &es);
+ newtPopWindow();
+ newtFormDestroy(form);
+ return 0;
+}
+
+static newtComponent showform;
+static int show_doing(char *title, char *tmp)
+{
+ struct newtExitStruct es;
+ newtComponent label;
+ showform = newtForm(NULL, NULL, 0);
+ newtCenteredWindow(70,4, title);
+ label = newtLabel(3,1,tmp);
+ newtFormAddComponents(showform,label, NULL);
+ newtFormSetTimer(showform, 200);
+ newtFormRun(showform, &es);
+ return 0;
+}
+
+static int hide_doing(void)
+{
+ newtPopWindow();
+ newtFormDestroy(showform);
+ return 0;
+}
+
+static void try_status(void)
+{
+ struct message *m;
+ manager_action("Status", "");
+ m = wait_for_response(10000);
+ if (!m) {
+ show_message("Status Failed", "Timeout waiting for response");
+ } else if (strcasecmp(get_header(m, "Response"), "Success")) {
+ show_message("Status Failed Failed", get_header(m, "Message"));
+ }
+}
+
+
+static void try_hangup(newtComponent c)
+{
+ struct ast_chan *chan;
+ struct message *m;
+
+ chan = newtListboxGetCurrent(c);
+ if (chan) {
+ manager_action("Hangup", "Channel: %s\r\n", chan->name);
+ m = wait_for_response(10000);
+ if (!m) {
+ show_message("Hangup Failed", "Timeout waiting for response");
+ } else if (strcasecmp(get_header(m, "Response"), "Success")) {
+ show_message("Hangup Failed", get_header(m, "Message"));
+ }
+ }
+
+}
+
+static int get_user_input(char *msg, char *buf, int buflen)
+{
+ newtComponent form;
+ newtComponent ok;
+ newtComponent cancel;
+ newtComponent inpfield;
+ const char *input;
+ int res = -1;
+ struct newtExitStruct es;
+
+ newtCenteredWindow(60,7, msg);
+
+ inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
+ ok = newtButton(22, 3, "OK");
+ cancel = newtButton(32, 3, "Cancel");
+ form = newtForm(NULL, NULL, 0);
+ newtFormAddComponents(form, inpfield, ok, cancel, NULL);
+ newtFormRun(form, &es);
+ strncpy(buf, input, buflen - 1);
+ if (es.u.co == ok)
+ res = 0;
+ else
+ res = -1;
+ newtPopWindow();
+ newtFormDestroy(form);
+ return res;
+}
+
+static void try_redirect(newtComponent c)
+{
+ struct ast_chan *chan;
+ char dest[256];
+ struct message *m;
+ char channame[256];
+ char tmp[80];
+ char *context;
+
+ chan = newtListboxGetCurrent(c);
+ if (chan) {
+ strncpy(channame, chan->name, sizeof(channame) - 1);
+ snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
+ if (get_user_input(tmp, dest, sizeof(dest)))
+ return;
+ if ((context = strchr(dest, '@'))) {
+ *context = '\0';
+ context++;
+ manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
+ } else {
+ manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
+ }
+ m = wait_for_response(10000);
+ if (!m) {
+ show_message("Hangup Failed", "Timeout waiting for response");
+ } else if (strcasecmp(get_header(m, "Response"), "Success")) {
+ show_message("Hangup Failed", get_header(m, "Message"));
+ }
+ }
+
+}
+
+static int manage_calls(char *host)
+{
+ newtComponent form;
+ newtComponent quit;
+ newtComponent hangup;
+ newtComponent redirect;
+ newtComponent channels;
+ struct newtExitStruct es;
+ char tmp[80];
+
+ /* Mark: If there's one thing you learn from this code, it is this...
+ Never, ever fly Air France. Their customer service is absolutely
+ the worst. I've never heard the words "That's not my problem" as
+ many times as I have from their staff -- It should, without doubt
+ be their corporate motto if it isn't already. Don't bother giving
+ them business because you're just a pain in their side and they
+ will be sure to let you know the first time you speak to them.
+
+ If you ever want to make me happy just tell me that you, too, will
+ never fly Air France again either (in spite of their excellent
+ cuisine).
+
+ Update by oej: The merger with KLM has transferred this
+ behaviour to KLM as well.
+ Don't bother giving them business either...
+
+ Only if you want to travel randomly without luggage, you
+ might pick either of them.
+
+ */
+ snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
+ newtCenteredWindow(74, 20, tmp);
+ form = newtForm(NULL, NULL, 0);
+ newtFormWatchFd(form, session.fd, NEWT_FD_READ);
+ newtFormSetTimer(form, 100);
+ quit = newtButton(62, 16, "Quit");
+ redirect = newtButton(35, 16, "Redirect");
+ hangup = newtButton(50, 16, "Hangup");
+ channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
+ newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
+ newtListboxSetWidth(channels, 72);
+
+ show_doing("Getting Status", "Retrieving system status...");
+ try_status();
+ hide_doing();
+
+ for(;;) {
+ newtFormRun(form, &es);
+ if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
+ if (input_check(&session, NULL)) {
+ show_message("Disconnected", "Disconnected from remote host");
+ break;
+ }
+ } else if (es.reason == NEWT_EXIT_COMPONENT) {
+ if (es.u.co == quit)
+ break;
+ if (es.u.co == hangup) {
+ try_hangup(channels);
+ } else if (es.u.co == redirect) {
+ try_redirect(channels);
+ }
+ }
+ rebuild_channels(channels);
+ }
+ newtFormDestroy(form);
+ return 0;
+}
+
+static int login(char *hostname)
+{
+ newtComponent form;
+ newtComponent cancel;
+ newtComponent login;
+ newtComponent username;
+ newtComponent password;
+ newtComponent label;
+ newtComponent ulabel;
+ newtComponent plabel;
+ const char *user;
+ const char *pass;
+ struct message *m;
+ struct newtExitStruct es;
+ char tmp[55];
+ struct hostent *hp;
+ int res = -1;
+
+ session.fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (session.fd < 0) {
+ snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
+ show_message("Socket failed", tmp);
+ return -1;
+ }
+
+ snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
+ show_doing("Connecting....", tmp);
+
+
+ hp = gethostbyname(hostname);
+ if (!hp) {
+ snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
+ show_message("Host lookup failed", tmp);
+ return -1;
+ }
+ hide_doing();
+ snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
+ show_doing("Connecting...", tmp);
+
+ session.sin.sin_family = AF_INET;
+ session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
+ memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
+
+ if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
+ snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
+ show_message("Connect Failed", tmp);
+ return -1;
+ }
+
+ hide_doing();
+
+ login = newtButton(5, 6, "Login");
+ cancel = newtButton(25, 6, "Cancel");
+ newtCenteredWindow(40, 10, "Asterisk Manager Login");
+ snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
+ label = newtLabel(4,1, tmp);
+
+ ulabel = newtLabel(4,2,"Username:");
+ plabel = newtLabel(4,3,"Password:");
+
+ username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
+ password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
+
+ form = newtForm(NULL, NULL, 0);
+ newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
+ newtFormRun(form, &es);
+ if (es.reason == NEWT_EXIT_COMPONENT) {
+ if (es.u.co == login) {
+ snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
+ show_doing("Logging in", tmp);
+ /* Check to see if the remote host supports MD5 Authentication */
+ manager_action("Challenge", "AuthType: MD5\r\n");
+ m = wait_for_response(10000);
+ if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
+ char *challenge = get_header(m, "Challenge");
+ int x;
+ int len = 0;
+ char md5key[256] = "";
+ struct MD5Context md5;
+ unsigned char digest[16];
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
+ MD5Update(&md5, (unsigned char *)pass, strlen(pass));
+ MD5Final(digest, &md5);
+ for (x=0; x<16; x++)
+ len += sprintf(md5key + len, "%2.2x", digest[x]);
+ manager_action("Login",
+ "AuthType: MD5\r\n"
+ "Username: %s\r\n"
+ "Key: %s\r\n",
+ user, md5key);
+ m = wait_for_response(10000);
+ hide_doing();
+ if (!strcasecmp(get_header(m, "Response"), "Success")) {
+ res = 0;
+ } else {
+ show_message("Login Failed", get_header(m, "Message"));
+ }
+ } else {
+ memset(m, 0, sizeof(m));
+ manager_action("Login",
+ "Username: %s\r\n"
+ "Secret: %s\r\n",
+ user, pass);
+ m = wait_for_response(10000);
+ hide_doing();
+ if (m) {
+ if (!strcasecmp(get_header(m, "Response"), "Success")) {
+ res = 0;
+ } else {
+ show_message("Login Failed", get_header(m, "Message"));
+ }
+ }
+ }
+ }
+ }
+ newtFormDestroy(form);
+ return res;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ fprintf(stderr, "Usage: astman <host>\n");
+ exit(1);
+ }
+ newtInit();
+ newtCls();
+ newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
+ newtPushHelpLine("Welcome to the Asterisk Manager!");
+ if (login(argv[1])) {
+ newtFinished();
+ exit(1);
+ }
+ manage_calls(argv[1]);
+ newtFinished();
+ return 0;
+}
diff --git a/trunk/utils/build-extensions-conf.lua b/trunk/utils/build-extensions-conf.lua
new file mode 100755
index 000000000..a3f159def
--- /dev/null
+++ b/trunk/utils/build-extensions-conf.lua
@@ -0,0 +1,81 @@
+#!/usr/bin/env lua
+--[[
+
+This utility can be used to generate an extensions.conf file to match an
+existing extensions.lua file. As an argument it takes the patch of the
+extensions.lua file to read from, otherwise it uses
+/etc/asterisk/extensions.lua.
+
+This script can also be used to automatically include extensions.lua in
+extensions.conf via a #exec as well.
+
+#exec /usr/bin/build-extensions-conf.lua -c
+
+--]]
+
+usage = [[
+
+Usage:
+ ]] .. arg[0] .. [[ [options] [extensions.lua path]
+
+This utility can generate an extensions.conf file with all of the contexts in
+your extensions.lua file defined as including the Lua switch. This is useful
+if you want to use your extensions.lua file exclusively. By using this utility
+you dont't have to create each extension in extensions.conf manually.
+
+The resulting extensions.conf file is printed to standard output.
+
+ --contexts-only, -c Don't print the [global] or [general] sections. This
+ is useful for including the generated file into an
+ existing extensions.conf via #include or #exec.
+
+ --help, -h Print this message.
+
+]]
+
+extensions_file = "/etc/asterisk/extensions.lua"
+
+options = {}
+
+for k, v in ipairs(arg) do
+ if v:sub(1, 1) == "-" then
+ if v == "-h" or v == "--help" then
+ print("match")
+ options["help"] = true
+ elseif v == "-c" or v == "--contexts-only" then
+ options["contexts-only"] = true
+ end
+ else
+ options["extensions-file"] = v
+ end
+end
+
+if options["help"] then
+ io.stderr:write(usage)
+ os.exit(0)
+end
+
+if options["extensions-file"] then
+ extensions_file = options["extensions-file"]
+end
+
+result, error_message = pcall(dofile, extensions_file)
+
+if not result then
+ io.stderr:write(error_message .. "\n")
+ os.exit(1)
+end
+
+if not extensions then
+ io.stderr:write("Error: extensions table not found in '" .. extensions_file .. "'\n")
+ os.exit(1)
+end
+
+if not options["contexts-only"] then
+ io.stdout:write("[general]\n\n[globals]\n\n")
+end
+
+for context, extens in pairs(extensions) do
+ io.stdout:write("[" .. tostring(context) .. "]\nswitch => Lua\n\n")
+end
+
diff --git a/trunk/utils/check_expr.c b/trunk/utils/check_expr.c
new file mode 100644
index 000000000..00d00d802
--- /dev/null
+++ b/trunk/utils/check_expr.c
@@ -0,0 +1,459 @@
+/*
+ * 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.
+ */
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/ast_expr.h"
+
+#define AST_API_MODULE 1
+#include "asterisk/inline_api.h"
+
+#define AST_API_MODULE 1
+#include "asterisk/lock.h"
+
+#include "asterisk/strings.h"
+
+/* I included this from utils.c, so as not to have everything in that .c
+ file included */
+/*!
+ * core handler for dynamic strings.
+ * This is not meant to be called directly, but rather through the
+ * various wrapper macros
+ * ast_str_set(...)
+ * ast_str_append(...)
+ * ast_str_set_va(...)
+ * ast_str_append_va(...)
+ */
+int __ast_str_helper(struct ast_str **buf, size_t max_len,
+ int append, const char *fmt, va_list ap)
+{
+ int res, need;
+ int offset = (append && (*buf)->len) ? (*buf)->used : 0;
+
+ if (max_len < 0)
+ max_len = (*buf)->len; /* don't exceed the allocated space */
+ /*
+ * Ask vsnprintf how much space we need. Remember that vsnprintf
+ * does not count the final '\0' so we must add 1.
+ */
+ res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap);
+
+ need = res + offset + 1;
+ /*
+ * If there is not enough space and we are below the max length,
+ * reallocate the buffer and return a message telling to retry.
+ */
+ if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) {
+ if (max_len && max_len < need) /* truncate as needed */
+ need = max_len;
+ else if (max_len == 0) /* if unbounded, give more room for next time */
+ need += 16 + need/4;
+ if (ast_str_make_space(buf, need)) {
+ return AST_DYNSTR_BUILD_FAILED;
+ }
+ (*buf)->str[offset] = '\0'; /* Truncate the partial write. */
+
+ /* va_end() and va_start() must be done before calling
+ * vsnprintf() again. */
+ return AST_DYNSTR_BUILD_RETRY;
+ }
+ /* update space used, keep in mind the truncation */
+ (*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset;
+
+ return res;
+}
+#ifndef DEBUG_THREADS
+enum ast_lock_type {
+ AST_MUTEX,
+ AST_RDLOCK,
+ AST_WRLOCK,
+};
+#endif
+void ast_store_lock_info(enum ast_lock_type type, const char *filename,
+ int line_num, const char *func, const char *lock_name, void *lock_addr);
+void ast_store_lock_info(enum ast_lock_type type, const char *filename,
+ int line_num, const char *func, const char *lock_name, void *lock_addr)
+{
+ /* not a lot to do in a standalone w/o threading! */
+}
+
+void ast_mark_lock_acquired(void);
+void ast_mark_lock_acquired(void)
+{
+ /* not a lot to do in a standalone w/o threading! */
+}
+
+void ast_remove_lock_info(void *lock_addr);
+void ast_remove_lock_info(void *lock_addr)
+{
+ /* not a lot to do in a standalone w/o threading! */
+}
+
+static int global_lineno = 1;
+static int global_expr_count=0;
+static int global_expr_max_size=0;
+static int global_expr_tot_size=0;
+static int global_warn_count=0;
+static int global_OK_count=0;
+
+struct varz
+{
+ char varname[100]; /* a really ultra-simple, space-wasting linked list of var=val data */
+ char varval[1000]; /* if any varname is bigger than 100 chars, or val greater than 1000, then **CRASH** */
+ struct varz *next;
+};
+
+struct varz *global_varlist;
+
+/* Our own version of ast_log, since the expr parser uses it. */
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+//void ast_register_file_version(const char *file, const char *version);
+//void ast_unregister_file_version(const char *file);
+
+char *find_var(const char *varname);
+void set_var(const char *varname, const char *varval);
+unsigned int check_expr(char* buffer, char* error_report);
+int check_eval(char *buffer, char *error_report);
+void parse_file(const char *fname);
+
+void ast_register_file_version(const char *file, const char *version) { }
+int ast_add_profile(const char *x, uint64_t scale) { return 0;}
+
+int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
+{
+ int ret;
+ ret = *p;
+ *p += v;
+ return ret;
+}
+
+void ast_unregister_file_version(const char *file)
+{
+}
+
+char *find_var(const char *varname) /* the list should be pretty short, if there's any list at all */
+{
+ struct varz *t;
+ for (t= global_varlist; t; t = t->next) {
+ if (!strcmp(t->varname, varname)) {
+ return t->varval;
+ }
+ }
+ return 0;
+}
+
+void set_var(const char *varname, const char *varval);
+
+void set_var(const char *varname, const char *varval)
+{
+ struct varz *t = (struct varz*)calloc(1,sizeof(struct varz));
+ if (!t)
+ return;
+ strcpy(t->varname, varname);
+ strcpy(t->varval, varval);
+ t->next = global_varlist;
+ global_varlist = t;
+}
+
+unsigned int check_expr(char* buffer, char* error_report)
+{
+ char* cp;
+ unsigned int warn_found = 0;
+
+ error_report[0] = 0;
+
+ for (cp = buffer; *cp; ++cp)
+ {
+ switch (*cp)
+ {
+ case '"':
+ /* skip to the other end */
+ while (*(++cp) && *cp != '"') ;
+
+ if (*cp == 0)
+ {
+ fprintf(stderr,
+ "Trouble? Unterminated double quote found at line %d\n",
+ global_lineno);
+ }
+ break;
+
+ case '>':
+ case '<':
+ case '!':
+ if ( (*(cp + 1) == '=')
+ && ( ( (cp > buffer) && (*(cp - 1) != ' ') ) || (*(cp + 2) != ' ') ) )
+ {
+ char msg[200];
+ snprintf(msg,
+ sizeof(msg),
+ "WARNING: line %d: '%c%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n",
+ global_lineno, *cp, *(cp + 1));
+ strcat(error_report, msg);
+ ++global_warn_count;
+ ++warn_found;
+ }
+ break;
+
+ case '|':
+ case '&':
+ case '=':
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '?':
+ case ':':
+ if ( ( (cp > buffer) && (*(cp - 1) != ' ') ) || (*(cp + 1) != ' ') )
+ {
+ char msg[200];
+ snprintf(msg,
+ sizeof(msg),
+ "WARNING: line %d: '%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n",
+ global_lineno, *cp );
+ strcat(error_report, msg);
+ ++global_warn_count;
+ ++warn_found;
+ }
+ break;
+ }
+ }
+
+ return warn_found;
+}
+
+int check_eval(char *buffer, char *error_report);
+
+struct ast_custom_function *ast_custom_function_find(const char *name);
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ return 0;
+}
+
+int check_eval(char *buffer, char *error_report)
+{
+ char *cp, *ep;
+ char s[4096];
+ char evalbuf[80000];
+ int result;
+
+ error_report[0] = 0;
+ ep = evalbuf;
+
+ for (cp=buffer;*cp;cp++) {
+ if (*cp == '$' && *(cp+1) == '{') {
+ int brack_lev = 1;
+ char *xp= cp+2;
+
+ while (*xp) {
+ if (*xp == '{')
+ brack_lev++;
+ else if (*xp == '}')
+ brack_lev--;
+
+ if (brack_lev == 0)
+ break;
+ xp++;
+ }
+ if (*xp == '}') {
+ char varname[200];
+ char *val;
+
+ strncpy(varname,cp+2, xp-cp-2);
+ varname[xp-cp-2] = 0;
+ cp = xp;
+ val = find_var(varname);
+ if (val) {
+ char *z = val;
+ while (*z)
+ *ep++ = *z++;
+ }
+ else {
+ *ep++ = '5'; /* why not */
+ *ep++ = '5';
+ *ep++ = '5';
+ }
+ }
+ else {
+ printf("Unterminated variable reference at line %d\n", global_lineno);
+ *ep++ = *cp;
+ }
+ }
+ else if (*cp == '\\') {
+ /* braindead simple elim of backslash */
+ cp++;
+ *ep++ = *cp;
+ }
+ else
+ *ep++ = *cp;
+ }
+ *ep++ = 0;
+
+ /* now, run the test */
+ result = ast_expr(evalbuf, s, sizeof(s),NULL);
+ if (result) {
+ sprintf(error_report,"line %d, evaluation of $[ %s ] result: %s\n", global_lineno, evalbuf, s);
+ return 1;
+ } else {
+ sprintf(error_report,"line %d, evaluation of $[ %s ] result: ****SYNTAX ERROR****\n", global_lineno, evalbuf);
+ return 1;
+ }
+}
+
+
+void parse_file(const char *fname);
+
+void parse_file(const char *fname)
+{
+ FILE *f = fopen(fname,"r");
+ FILE *l = fopen("expr2_log","w");
+ int c1;
+ char last_char= 0;
+ char buffer[30000]; /* I sure hope no expr gets this big! */
+
+ if (!f) {
+ fprintf(stderr,"Couldn't open %s for reading... need an extensions.conf file to parse!\n",fname);
+ exit(20);
+ }
+ if (!l) {
+ fprintf(stderr,"Couldn't open 'expr2_log' file for writing... please fix and re-run!\n");
+ exit(21);
+ }
+
+ global_lineno = 1;
+
+ while ((c1 = fgetc(f)) != EOF) {
+ if (c1 == '\n')
+ global_lineno++;
+ else if (c1 == '[') {
+ if (last_char == '$') {
+ /* bingo, an expr */
+ int bracklev = 1;
+ int bufcount = 0;
+ int retval;
+ char error_report[30000];
+
+ while ((c1 = fgetc(f)) != EOF) {
+ if (c1 == '[')
+ bracklev++;
+ else if (c1 == ']')
+ bracklev--;
+ if (c1 == '\n') {
+ fprintf(l, "ERROR-- A newline in an expression? Weird! ...at line %d\n", global_lineno);
+ fclose(f);
+ fclose(l);
+ printf("--- ERROR --- A newline in the middle of an expression at line %d!\n", global_lineno);
+ }
+
+ if (bracklev == 0)
+ break;
+ buffer[bufcount++] = c1;
+ }
+ if (c1 == EOF) {
+ fprintf(l, "ERROR-- End of File Reached in the middle of an Expr at line %d\n", global_lineno);
+ fclose(f);
+ fclose(l);
+ printf("--- ERROR --- EOF reached in middle of an expression at line %d!\n", global_lineno);
+ exit(22);
+ }
+
+ buffer[bufcount] = 0;
+ /* update stats */
+ global_expr_tot_size += bufcount;
+ global_expr_count++;
+ if (bufcount > global_expr_max_size)
+ global_expr_max_size = bufcount;
+
+ retval = check_expr(buffer, error_report); /* check_expr should bump the warning counter */
+ if (retval != 0) {
+ /* print error report */
+ printf("Warning(s) at line %d, expression: $[%s]; see expr2_log file for details\n",
+ global_lineno, buffer);
+ fprintf(l, "%s", error_report);
+ }
+ else {
+ printf("OK -- $[%s] at line %d\n", buffer, global_lineno);
+ global_OK_count++;
+ }
+ error_report[0] = 0;
+ retval = check_eval(buffer, error_report);
+ fprintf(l, "%s", error_report);
+ }
+ }
+ last_char = c1;
+ }
+ printf("Summary:\n Expressions detected: %d\n Expressions OK: %d\n Total # Warnings: %d\n Longest Expr: %d chars\n Ave expr len: %d chars\n",
+ global_expr_count,
+ global_OK_count,
+ global_warn_count,
+ global_expr_max_size,
+ (global_expr_count) ? global_expr_tot_size/global_expr_count : 0);
+
+ fclose(f);
+ fclose(l);
+}
+
+
+int main(int argc,char **argv)
+{
+ int argc1;
+ char *eq;
+
+ if (argc < 2) {
+ printf("check_expr -- a program to look thru extensions.conf files for $[...] expressions,\n");
+ printf(" and run them thru the parser, looking for problems\n");
+ printf("Hey-- give me a path to an extensions.conf file!\n");
+ printf(" You can also follow the file path with a series of variable decls,\n");
+ printf(" of the form, varname=value, each separated from the next by spaces.\n");
+ printf(" (this might allow you to avoid division by zero messages, check that math\n");
+ printf(" is being done correctly, etc.)\n");
+ printf(" Note that messages about operators not being surrounded by spaces is merely to alert\n");
+ printf(" you to possible problems where you might be expecting those operators as part of a string.\n");
+ printf(" (to include operators in a string, wrap with double quotes!)\n");
+
+ exit(19);
+ }
+ global_varlist = 0;
+ for (argc1=2;argc1 < argc; argc1++) {
+ if ((eq = strchr(argv[argc1],'='))) {
+ *eq = 0;
+ set_var(argv[argc1],eq+1);
+ }
+ }
+
+ /* parse command args for x=y and set varz */
+
+ parse_file(argv[1]);
+ return 0;
+}
diff --git a/trunk/utils/clicompat.c b/trunk/utils/clicompat.c
new file mode 100644
index 000000000..33f90fae1
--- /dev/null
+++ b/trunk/utils/clicompat.c
@@ -0,0 +1,16 @@
+/*
+ * Stubs for some cli functions used by the test routines.
+ * $Revision$
+ */
+void ast_cli(int fd, const char *fmt, ...);
+void ast_cli(int fd, const char *fmt, ...)
+{
+}
+
+struct ast_cli_entry;
+
+int ast_cli_register_multiple(struct ast_cli_entry *e, int len);
+int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
+{
+ return 0;
+}
diff --git a/trunk/utils/conf2ael.c b/trunk/utils/conf2ael.c
new file mode 100644
index 000000000..fbb8aa3b6
--- /dev/null
+++ b/trunk/utils/conf2ael.c
@@ -0,0 +1,692 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Steve Murphy <murf@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.
+ */
+
+/*
+ *
+ * Reverse compile extensions.conf code into prototype AEL code
+ *
+ */
+
+/*** MODULEINFO
+ <depend>res_ael_share</depend>
+ ***/
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* CONFIG_DIR */
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+#include <err.h>
+#endif
+#include <regex.h>
+
+#include "asterisk.h"
+#include "asterisk/pbx.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/channel.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+#include "asterisk/options.h"
+#include "asterisk/callerid.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/pval.h"
+#include "asterisk/extconf.h"
+
+const char *ast_config_AST_CONFIG_DIR = "/etc/asterisk"; /* placeholder */
+
+void get_start_stop(unsigned int *word, int bitsperword, int totalbits, int *start, int *end);
+int all_bits_set(unsigned int *word, int bitsperword, int totalbits);
+extern char *days[];
+extern char *months[];
+
+char *config = "extensions.conf";
+
+/*
+static char *registrar = "conf2ael";
+static char userscontext[AST_MAX_EXTENSION] = "default";
+static int static_config = 0;
+static int write_protect_config = 1;
+static int autofallthrough_config = 0;
+static int clearglobalvars_config = 0;
+char ast_config_AST_SYSTEM_NAME[20] = ""; */
+
+/*! Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK 128
+/* static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function); */
+//extern char ast_config_AST_CONFIG_DIR[PATH_MAX];
+
+void ast_register_file_version(const char *file, const char *version)
+{
+}
+
+void ast_unregister_file_version(const char *file)
+{
+}
+int ast_add_profile(const char *x, uint64_t scale) { return 0;}
+
+/* Our own version of ast_log, since the expr parser uses it. -- stolen from utils/check_expr.c */
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+/* stolen from pbx.c */
+struct ast_context;
+struct ast_app;
+#ifdef LOW_MEMORY
+#define EXT_DATA_SIZE 256
+#else
+#define EXT_DATA_SIZE 8192
+#endif
+
+#define SWITCH_DATA_LENGTH 256
+
+#define VAR_BUF_SIZE 4096
+
+#define VAR_NORMAL 1
+#define VAR_SOFTTRAN 2
+#define VAR_HARDTRAN 3
+
+#define BACKGROUND_SKIP (1 << 0)
+#define BACKGROUND_NOANSWER (1 << 1)
+#define BACKGROUND_MATCHEXTEN (1 << 2)
+#define BACKGROUND_PLAYBACK (1 << 3)
+
+/*!
+ \brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+
+
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+/*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_context {
+ ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
+ struct ast_exten *root; /*!< The root of the list of extensions */
+ struct ast_context *next; /*!< Link them together */
+ struct ast_include *includes; /*!< Include other contexts */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ struct module *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+
+/*! \brief ast_state_cb: An extension state notify register item */
+struct ast_state_cb {
+ int id;
+ void *data;
+ ast_state_cb_type callback;
+ struct ast_state_cb *next;
+};
+
+/*! \brief Structure for dial plan hints
+
+ \note Hints are pointers from an extension in the dialplan to one or
+ more devices (tech/name)
+ - See \ref AstExtState
+*/
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
+};
+
+struct store_hint {
+ char *context;
+ char *exten;
+ struct ast_state_cb *callbacks;
+ int laststate;
+ AST_LIST_ENTRY(store_hint) list;
+ char data[1];
+};
+
+AST_LIST_HEAD(store_hints, store_hint);
+
+static const struct cfextension_states {
+ int extension_state;
+ const char * const text;
+} extension_states[] = {
+ { AST_EXTENSION_NOT_INUSE, "Idle" },
+ { AST_EXTENSION_INUSE, "InUse" },
+ { AST_EXTENSION_BUSY, "Busy" },
+ { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
+ { AST_EXTENSION_RINGING, "Ringing" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
+ { AST_EXTENSION_ONHOLD, "Hold" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
+};
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+extern struct ast_context *local_contexts;
+extern struct ast_context *contexts;
+
+
+struct ast_custom_function *ast_custom_function_find(const char *name);
+
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ return 0; /* in "standalone" mode, functions are just not avail */
+}
+
+
+struct profile_entry {
+ const char *name;
+ uint64_t scale; /* if non-zero, values are scaled by this */
+ int64_t mark;
+ int64_t value;
+ int64_t events;
+};
+
+struct profile_data {
+ int entries;
+ int max_size;
+ struct profile_entry e[0];
+};
+
+static int bit_at(unsigned int *word, int bitsperword, int bitnum)
+{
+ return word[bitnum/bitsperword] & (1 << (bitnum % bitsperword));
+}
+
+void get_start_stop(unsigned int *word, int bitsperword, int totalbits, int *start, int *end)
+{
+ int i;
+ int thisbit, thatbit = bit_at(word, bitsperword, totalbits-1);
+
+ for (i=0; i<totalbits; i++) {
+ thisbit = bit_at(word, bitsperword, i);
+
+ if (thisbit != thatbit ) {
+ if (thisbit) {
+ *start = i;
+ } else {
+ *end = i;
+ }
+ }
+ thatbit = thisbit;
+ }
+}
+
+int all_bits_set(unsigned int *word, int bitsperword, int totalbits )
+{
+
+ int i, total=totalbits/bitsperword,bitmask = 0;
+
+ for (i=0; i<bitsperword; i++)
+ {
+ bitmask |= (1 << i);
+ }
+
+ for (i=0; i<total; i++)
+ {
+ if (word[i] != bitmask)
+ return 0;
+ }
+ return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+ struct ast_context *tmp;
+ struct ast_exten *e, *eroot;
+ pval *tree, *tmptree, *sws;
+ struct ast_include *tmpi;
+ struct ast_sw *sw = 0;
+ struct ast_ignorepat *ipi;
+ pval *incl=0;
+ int localdir = 0, i;
+
+ tree = 0;
+ tmptree = 0;
+
+ /* process the command line args */
+ for (i=1; i<argc; i++)
+ {
+ if (strcmp(argv[i],"-d")==0)
+ localdir =1;
+ }
+
+ /* 3 simple steps: */
+ /* 1. read in the extensions.conf config file
+ * 2. traverse, and build an AEL tree
+ * 3. Output the AEL tree into a file
+ */
+ printf("WARNING: This is an EXTREMELY preliminary version of a program\n");
+ printf(" that will someday hopefully do a thoughful and intelligent\n");
+ printf(" job of transforming your extensions.conf file into an\n");
+ printf(" extensions.ael file.\n");
+ printf(" This version has absolutely no intelligence, and pretty\n");
+ printf(" much just does a direct conversion\n");
+ printf(" The result will most likely need careful attention to\n");
+ printf(" finish the job!!!!!\n");
+
+ if (!localdir)
+ printf(" (You could use -d the use the extensions.conf in the current directory!)\n");
+
+ strcpy((char *)ast_config_AST_CONFIG_DIR,"/etc/asterisk");
+
+ printf("Loading %s/%s...\n", ast_config_AST_CONFIG_DIR, config);
+
+ if (!localdir)
+ localized_use_conf_dir();
+ localized_pbx_load_module();
+
+ printf("... Done!\n");
+
+ tmp = 0;
+ while ((tmp = localized_walk_contexts(tmp)) ) {
+ printf("Context: %s\n", tmp->name);
+ }
+ printf("=========\n");
+ tmp = 0;
+ while ((tmp = localized_walk_contexts(tmp)) ) {
+ /* printf("Context: %s\n", tmp->name); */
+ tmptree = pvalCreateNode(PV_CONTEXT);
+ if (!tree)
+ tree = tmptree;
+ else
+ pvalTopLevAddObject(tree, tmptree);
+
+ pvalContextSetName(tmptree, ast_strdup(tmp->name));
+
+ if (tmp->includes) {
+ incl = pvalCreateNode(PV_INCLUDES);
+ pvalContextAddStatement(tmptree, incl);
+ for (tmpi = tmp->includes; tmpi; ) { /* includes */
+ if (strchr(tmpi->name,'|')==0) {
+ if (tmpi->hastime)
+ {
+ char timerange[15];
+ char dowrange[10];
+ char domrange[10];
+ char monrange[10];
+ int startbit=0, endbit=0;
+
+ if (all_bits_set(tmpi->timing.minmask, 30, 720))
+ strcpy(timerange, "*");
+ else {
+ int hr, min;
+ char tbuf[20];
+ get_start_stop(tmpi->timing.minmask, 30, 720, &startbit, &endbit);
+ hr = startbit/30;
+ min = (startbit % 30) * 2;
+ sprintf(tbuf,"%02d:%02d", hr, min);
+ strcpy(timerange, tbuf);
+ hr = endbit/30;
+ min = (endbit % 30) * 2;
+ sprintf(tbuf,"%02d:%02d", hr, min);
+ strcat(timerange,"-");
+ strcat(timerange,tbuf);
+ }
+
+ if (all_bits_set(&tmpi->timing.dowmask, 7, 7))
+ strcpy(dowrange, "*");
+ else {
+ get_start_stop(&tmpi->timing.dowmask, 7, 7, &startbit, &endbit);
+ strcpy(dowrange, days[startbit]);
+ strcat(dowrange,"-");
+ strcat(dowrange, days[endbit]);
+ }
+
+ if (all_bits_set(&tmpi->timing.monthmask, 12, 12))
+ strcpy(monrange, "*");
+ else {
+ get_start_stop(&tmpi->timing.monthmask, 12, 12, &startbit, &endbit);
+ strcpy(monrange, months[startbit]);
+ strcat(monrange,"-");
+ strcat(monrange, months[endbit]);
+ }
+
+ if (all_bits_set(&tmpi->timing.daymask, 31, 31))
+ strcpy(domrange, "*");
+ else {
+ char tbuf[20];
+ get_start_stop(&tmpi->timing.daymask, 31, 31, &startbit, &endbit);
+ sprintf(tbuf,"%d", startbit);
+ strcpy(domrange, tbuf);
+ strcat(domrange,"-");
+ sprintf(tbuf,"%d", endbit);
+ strcat(domrange, tbuf);
+ }
+ /* now all 4 fields are set; what do we do? */
+ pvalIncludesAddIncludeWithTimeConstraints(incl, strdup(tmpi->name), strdup(timerange), strdup(domrange), strdup(dowrange), strdup(monrange));
+
+ } else {
+ pvalIncludesAddInclude(incl, strdup(tmpi->name));
+ }
+ } else { /* it appears the timing constraint info is tacked onto the name, carve it up and divvy it out */
+ char *dow,*dom,*mon;
+ char *all = strdup(tmpi->name);
+ char *hr = strchr(all,'|');
+ if (hr) {
+ *hr++ = 0;
+ dow = strchr(hr,'|');
+ if (dow) {
+ *dow++ = 0;
+ dom = strchr(dow,'|');
+ if (dom) {
+ *dom++ = 0;
+ mon = strchr(dom,'|');
+ if (mon) {
+ *mon++ = 0;
+ /* now all 4 fields are set; what do we do? */
+ pvalIncludesAddIncludeWithTimeConstraints(incl, all, hr, dow, dom, mon);
+ /* the original data is always best to keep (no 2-min rounding) */
+ } else {
+ ast_log(LOG_ERROR,"No month spec attached to include!\n");
+ }
+ } else {
+ ast_log(LOG_ERROR,"No day of month spec attached to include!\n");
+ }
+ } else {
+ ast_log(LOG_ERROR,"No day of week spec attached to include!\n");
+ }
+ }
+ }
+ tmpi = tmpi->next;
+ }
+ }
+ for (ipi = tmp->ignorepats; ipi; ) { /* ignorepats */
+ incl = pvalCreateNode(PV_IGNOREPAT);
+ pvalIgnorePatSetPattern(incl,(char *)ipi->pattern);
+ pvalContextAddStatement(tmptree, incl);
+ ipi = ipi->next;
+ }
+ eroot=0;
+ while ( (eroot = localized_walk_context_extensions(tmp, eroot)) ) {
+ pval *exten = pvalCreateNode(PV_EXTENSION);
+ pvalContextAddStatement(tmptree, exten);
+ pvalExtenSetName(exten, ast_strdup(eroot->exten));
+
+ if (eroot->peer) {
+ pval *block = pvalCreateNode(PV_STATEMENTBLOCK);
+ pvalExtenSetStatement(exten, block);
+
+ e = 0;
+ while ( (e = localized_walk_extension_priorities(eroot, e)) ) {
+
+ pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL);
+ pval *args = pvalCreateNode(PV_WORD);
+
+ /* printf(" %s(%s)\n", e->app, (char*)e->data); */
+
+ pvalAppCallSetAppName(statemnt, ast_strdup(e->app));
+ pvalWordSetString(args, ast_strdup(e->data));
+ pvalAppCallAddArg(statemnt, args);
+
+ pvalStatementBlockAddStatement(block, statemnt);
+ }
+ } else if (eroot->priority == -1) {
+
+ pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL);
+ pval *args = pvalCreateNode(PV_WORD);
+
+ /* printf("Mike, we have a hint on exten %s with data %s\n", eroot->exten, eroot->app); */
+
+ pvalAppCallSetAppName(statemnt, "NoOp");
+ pvalWordSetString(args, ast_strdup(eroot->app));
+
+
+ pvalExtenSetStatement(exten, statemnt);
+ pvalExtenSetHints(exten, ast_strdup(eroot->app));
+ } else {
+
+ pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL);
+ pval *args = pvalCreateNode(PV_WORD);
+
+ /* printf(" %s (%s)\n", eroot->app, (char *)eroot->data); */
+
+ pvalAppCallSetAppName(statemnt, ast_strdup(eroot->app));
+ pvalWordSetString(args, ast_strdup(eroot->data));
+
+
+ pvalAppCallAddArg(statemnt, args);
+ pvalExtenSetStatement(exten, statemnt);
+ }
+
+ /* printf(" extension: %s\n", eroot->exten); */
+ }
+ if (AST_LIST_FIRST(&tmp->alts)) {
+ sws = pvalCreateNode(PV_SWITCHES);
+ pvalContextAddStatement(tmptree,sws);
+
+ sw = 0;
+ while ((sw = localized_walk_context_switches(tmp,sw)) ) {
+ pvalSwitchesAddSwitch(sws, ast_strdup(sw->name));
+ }
+ }
+ }
+ printf("Generating aelout.ael file...\n");
+
+ ael2_print("aelout.ael", tree);
+
+ printf("...Done!\n");
+ return 0;
+}
+
+
+/* ==================================== for linking internal stuff to external stuff */
+
+int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ return localized_pbx_builtin_setvar(chan, data);
+}
+
+void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
+void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count)
+{
+ if (cp1 && *cp1)
+ strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for
+ a possible var substitution on extension names,
+ so....! */
+ else
+ *cp2 = 0;
+}
+
+int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ return localized_add_extension2(con, replace, extension, priority, label, callerid, application, data, datad, registrar);
+}
+
+int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+
+ return localized_context_add_ignorepat2(con, value, registrar);
+}
+
+int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+
+ return localized_context_add_switch2(con, value, data, eval, registrar);
+}
+
+int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+
+ return localized_context_add_include2(con, value,registrar);
+}
+
+struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ printf("Creating context %s, registrar=%s\n", name, registrar);
+
+ return localized_context_create(extcontexts, name, registrar);
+}
+
+struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ printf("find/Creating context %s, registrar=%s\n", name, registrar);
+
+ return localized_context_create(extcontexts, name, registrar);
+}
+
+void ast_cli_register_multiple(void);
+
+void ast_cli_register_multiple(void)
+{
+}
+
+void ast_module_register(const struct ast_module_info *x)
+{
+}
+
+void ast_module_unregister(const struct ast_module_info *x)
+{
+}
+
+void ast_cli_unregister_multiple(void);
+
+void ast_cli_unregister_multiple(void)
+{
+}
+
+struct ast_context *ast_walk_contexts(struct ast_context *con);
+struct ast_context *ast_walk_contexts(struct ast_context *con)
+{
+ return localized_walk_contexts(con);
+}
+
+void ast_context_destroy(struct ast_context *con, const char *registrar);
+
+void ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ return localized_context_destroy(con, registrar);
+}
+
+int ast_context_verify_includes(struct ast_context *con);
+
+int ast_context_verify_includes(struct ast_context *con)
+{
+ return localized_context_verify_includes(con);
+}
+
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar);
+
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ localized_merge_contexts_and_delete(extcontexts, registrar);
+}
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action)
+{
+ return localized_find_extension(bypass, q, context, exten, priority, label, callerid, action);
+}
+
diff --git a/trunk/utils/expr2.testinput b/trunk/utils/expr2.testinput
new file mode 100644
index 000000000..9e1532b27
--- /dev/null
+++ b/trunk/utils/expr2.testinput
@@ -0,0 +1,126 @@
+2 + 2
+ 2 + 2
+
+2 - 4
+4 - 2
+-4 - -2
+4 + 2 * 8
+(4 + 2) * 8
+4 + (2 * 8)
+4 + (2 * 8) ? 3 :: 6
+4 + 8 / 2
+4 + 8 / 3
+(4+8) / 3
+4 + 8 % 3
+4 + 9 % 3
+(4+9) %3
+(4+8) %3
+(4+9) %3
+(4+8) %3
+(4+9) % 3
+(4+8) % 3
+(4+9) % 3
+(4+8) % 3
+(4+9)% 3
+(4+8)% 3
+(4+9)% 3
+(4+8)% 3
+4 & 4
+0 & 4
+0 & 0
+2 | 0
+2 | 4
+0 | 0
+!0 | 0
+!4 | 0
+4 | !0
+!4 | !0
+3 < 4
+4 < 3
+3 > 4
+4 > 3
+3 = 3
+3 = 4
+3 != 3
+3 != 4
+3 >= 4
+3 >= 3
+4 >= 3
+3 <= 4
+4 <= 3
+4 <= 4
+3 > 4 & 4 < 3
+4 > 3 & 3 < 4
+x = x
+y = x
+x != y
+x != x
+"Something interesting" =~ interesting
+"Something interesting" =~ Something
+"Something interesting" : Something
+"Something interesting" : interesting
+"Something interesting" =~ "interesting"
+"Something interesting" =~ "Something"
+"Something interesting" : "Something"
+"Something interesting" : "interesting"
+"Something interesting" =~ (interesting)
+"Something interesting" =~ (Something)
+"Something interesting" : (Something)
+"Something interesting" : (interesting)
+"Something interesting" =~ "\(interesting\)"
+"Something interesting" =~ "\(Something\)"
+"Something interesting" : "\(Something\)"
+"Something interesting" : "\(interesting\)"
+"011043567857575" : "011\(..\)"
+"9011043567857575" : "011\(..\)"
+"011043567857575" =~ "011\(..\)"
+"9011043567857575" =~ "011\(..\)"
+"Something interesting" =~ (interesting)
+"Something interesting" =~ (Something)
+"Something interesting" : (Something)
+"Something interesting" : (interesting)
+"Something interesting" =~ "(interesting)"
+"Something interesting" =~ "(Something)"
+"Something interesting" : "(Something)"
+"Something interesting" : "(interesting)"
+"011043567857575" : "011(..)"
+"9011043567857575" : "011(..)"
+"011043567857575" =~ "011(..)"
+"9011043567857575" =~ "011(..)"
+3
+something
+043
+2.1+4.2
+1.500003+1.4999999999999898989898989898989898989889898
+1/4
+2.3 + COS(3.141592653)
+REMAINDER(13,3)
+2.3 + SIN(3.1415823)
+TAN(45) + 2.3
+POW(10.0,4.0)
+SQRT(4)
+SQRT(2)
+FLOOR(2.4)
+FLOOR(2.6)
+CEIL(2.4)
+CEIL(2.6)
+ROUND(2.4)
+ROUND(2.5)
+ROUND(2.6)
+RINT(2.4)
+RINT(2.5)
+RINT(2.6)
+TRUNC(2.4)
+TRUNC(2.5)
+TRUNC(2.6)
+EXP(1.0)
+EXP2(1.0)
+LOG(10)
+LOG2(10)
+LOG10(10)
+ATAN2(4,5)
+ACOS(12)
+ASIN(1)
+ATAN(10)
+SQRT(2)*SQRT(2)
+MATH(3*9)
diff --git a/trunk/utils/extconf.c b/trunk/utils/extconf.c
new file mode 100644
index 000000000..c5b9fbdea
--- /dev/null
+++ b/trunk/utils/extconf.c
@@ -0,0 +1,6211 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@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.
+ */
+
+
+/*
+ *
+ * A condensation of the pbx_config stuff, to read into exensions.conf, and provide an interface to the data there,
+ * for operations outside of asterisk. A huge, awful hack.
+ *
+ */
+
+#include "asterisk/compat.h"
+#include "asterisk/paths.h" /* we use AST_CONFIG_DIR */
+
+#include <errno.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+#include <err.h>
+#endif
+#include <regex.h>
+#include <limits.h>
+#include <pthread.h>
+#include <netdb.h>
+#include <sys/param.h>
+#define ASINCLUDE_GLOB 1
+#ifdef AST_INCLUDE_GLOB
+#if defined(__Darwin__) || defined(__CYGWIN__)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+# include <glob.h>
+#endif
+
+#define AST_API_MODULE 1 /* gimme the inline defs! */
+struct ast_channel
+{
+ char x; /* basically empty! */
+};
+
+
+
+#include "asterisk/inline_api.h"
+#include "asterisk/endian.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/ael_structs.h"
+#include "asterisk/pval.h"
+
+/* logger.h */
+#define EVENTLOG "event_log"
+#define QUEUELOG "queue_log"
+
+#define DEBUG_M(a) { \
+ a; \
+}
+
+#define VERBOSE_PREFIX_1 " "
+#define VERBOSE_PREFIX_2 " == "
+#define VERBOSE_PREFIX_3 " -- "
+#define VERBOSE_PREFIX_4 " > "
+
+/* IN CONFLICT: void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+ __attribute__ ((format (printf, 5, 6))); */
+
+static void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+
+
+void ast_backtrace(void);
+
+void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
+ __attribute__ ((format (printf, 5, 6)));
+
+/* IN CONFLICT: void ast_verbose(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2))); */
+
+int ast_register_verbose(void (*verboser)(const char *string));
+int ast_unregister_verbose(void (*verboser)(const char *string));
+
+void ast_console_puts(const char *string);
+
+void ast_console_puts_mutable(const char *string);
+void ast_console_toggle_mute(int fd);
+
+#define _A_ __FILE__, __LINE__, __PRETTY_FUNCTION__
+
+#ifdef LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+#define __LOG_DEBUG 0
+#define LOG_DEBUG __LOG_DEBUG, _A_
+
+#ifdef LOG_EVENT
+#undef LOG_EVENT
+#endif
+#define __LOG_EVENT 1
+#define LOG_EVENT __LOG_EVENT, _A_
+
+#ifdef LOG_NOTICE
+#undef LOG_NOTICE
+#endif
+#define __LOG_NOTICE 2
+#define LOG_NOTICE __LOG_NOTICE, _A_
+
+#ifdef LOG_WARNING
+#undef LOG_WARNING
+#endif
+#define __LOG_WARNING 3
+#define LOG_WARNING __LOG_WARNING, _A_
+
+#ifdef LOG_ERROR
+#undef LOG_ERROR
+#endif
+#define __LOG_ERROR 4
+#define LOG_ERROR __LOG_ERROR, _A_
+
+#ifdef LOG_VERBOSE
+#undef LOG_VERBOSE
+#endif
+#define __LOG_VERBOSE 5
+#define LOG_VERBOSE __LOG_VERBOSE, _A_
+
+#ifdef LOG_DTMF
+#undef LOG_DTMF
+#endif
+#define __LOG_DTMF 6
+#define LOG_DTMF __LOG_DTMF, _A_
+
+/* from utils.h */
+
+static unsigned int __unsigned_int_flags_dummy;
+
+struct ast_flags { /* stolen from utils.h */
+ unsigned int flags;
+};
+#define ast_test_flag(p,flag) ({ \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ ((p)->flags & (flag)); \
+ })
+
+#define ast_set2_flag(p,value,flag) do { \
+ typeof ((p)->flags) __p = (p)->flags; \
+ typeof (__unsigned_int_flags_dummy) __x = 0; \
+ (void) (&__p == &__x); \
+ if (value) \
+ (p)->flags |= (flag); \
+ else \
+ (p)->flags &= ~(flag); \
+ } while (0)
+
+
+#ifdef __AST_DEBUG_MALLOC
+static void ast_free(void *ptr) attribute_unused;
+static void ast_free(void *ptr)
+{
+ free(ptr);
+}
+#else
+#define ast_free free
+#endif
+
+#ifndef __AST_DEBUG_MALLOC
+
+#define MALLOC_FAILURE_MSG \
+ ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file);
+/*!
+ * \brief A wrapper for malloc()
+ *
+ * ast_malloc() is a wrapper for malloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The argument and return value are the same as malloc()
+ */
+#define ast_malloc(len) \
+ _ast_malloc((len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_malloc(size_t len, const char *file, int lineno, const char *func),
+{
+ void *p;
+
+ if (!(p = malloc(len)))
+ MALLOC_FAILURE_MSG;
+
+ return p;
+}
+)
+
+/*!
+ * \brief A wrapper for calloc()
+ *
+ * ast_calloc() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_calloc(size_t num, size_t len, const char *file, int lineno, const char *func),
+{
+ void *p;
+
+ if (!(p = calloc(num, len)))
+ MALLOC_FAILURE_MSG;
+
+ return p;
+}
+)
+
+/*!
+ * \brief A wrapper for calloc() for use in cache pools
+ *
+ * ast_calloc_cache() is a wrapper for calloc() that will generate an Asterisk log
+ * message in the case that the allocation fails. When memory debugging is in use,
+ * the memory allocated by this function will be marked as 'cache' so it can be
+ * distinguished from normal memory allocations.
+ *
+ * The arguments and return value are the same as calloc()
+ */
+#define ast_calloc_cache(num, len) \
+ _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+/*!
+ * \brief A wrapper for realloc()
+ *
+ * ast_realloc() is a wrapper for realloc() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as realloc()
+ */
+#define ast_realloc(p, len) \
+ _ast_realloc((p), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+void * attribute_malloc _ast_realloc(void *p, size_t len, const char *file, int lineno, const char *func),
+{
+ void *newp;
+
+ if (!(newp = realloc(p, len)))
+ MALLOC_FAILURE_MSG;
+
+ return newp;
+}
+)
+
+/*!
+ * \brief A wrapper for strdup()
+ *
+ * ast_strdup() is a wrapper for strdup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strdup(), unlike strdup(), can safely accept a NULL argument. If a NULL
+ * argument is provided, ast_strdup will return NULL without generating any
+ * kind of error log message.
+ *
+ * The argument and return value are the same as strdup()
+ */
+#define ast_strdup(str) \
+ _ast_strdup((str), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+char * attribute_malloc _ast_strdup(const char *str, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
+
+ if (str) {
+ if (!(newstr = strdup(str)))
+ MALLOC_FAILURE_MSG;
+ }
+
+ return newstr;
+}
+)
+
+/*!
+ * \brief A wrapper for strndup()
+ *
+ * ast_strndup() is a wrapper for strndup() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * ast_strndup(), unlike strndup(), can safely accept a NULL argument for the
+ * string to duplicate. If a NULL argument is provided, ast_strdup will return
+ * NULL without generating any kind of error log message.
+ *
+ * The arguments and return value are the same as strndup()
+ */
+#define ast_strndup(str, len) \
+ _ast_strndup((str), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+AST_INLINE_API(
+char * attribute_malloc _ast_strndup(const char *str, size_t len, const char *file, int lineno, const char *func),
+{
+ char *newstr = NULL;
+
+ if (str) {
+ if (!(newstr = strndup(str, len)))
+ MALLOC_FAILURE_MSG;
+ }
+
+ return newstr;
+}
+)
+
+/*!
+ * \brief A wrapper for asprintf()
+ *
+ * ast_asprintf() is a wrapper for asprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as asprintf()
+ */
+#define ast_asprintf(ret, fmt, ...) \
+ _ast_asprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, __VA_ARGS__)
+
+AST_INLINE_API(
+int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...),
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+ va_end(ap);
+
+ return res;
+}
+)
+
+/*!
+ * \brief A wrapper for vasprintf()
+ *
+ * ast_vasprintf() is a wrapper for vasprintf() that will generate an Asterisk log
+ * message in the case that the allocation fails.
+ *
+ * The arguments and return value are the same as vasprintf()
+ */
+#define ast_vasprintf(ret, fmt, ap) \
+ _ast_vasprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, (fmt), (ap))
+
+AST_INLINE_API(
+int _ast_vasprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, va_list ap),
+{
+ int res;
+
+ if ((res = vasprintf(ret, fmt, ap)) == -1)
+ MALLOC_FAILURE_MSG;
+
+ return res;
+}
+)
+
+#else
+
+/* If astmm is in use, let it handle these. Otherwise, it will report that
+ all allocations are coming from this header file */
+
+#define ast_malloc(a) malloc(a)
+#define ast_calloc(a,b) calloc(a,b)
+#define ast_realloc(a,b) realloc(a,b)
+#define ast_strdup(a) strdup(a)
+#define ast_strndup(a,b) strndup(a,b)
+#define ast_asprintf(a,b,c) asprintf(a,b,c)
+#define ast_vasprintf(a,b,c) vasprintf(a,b,c)
+
+#endif /* AST_DEBUG_MALLOC */
+
+#if !defined(ast_strdupa) && defined(__GNUC__)
+/*!
+ \brief duplicate a string in memory from the stack
+ \param s The string to duplicate
+
+ This macro will duplicate the given string. It returns a pointer to the stack
+ allocatted memory for the new string.
+*/
+#define ast_strdupa(s) \
+ (__extension__ \
+ ({ \
+ const char *__old = (s); \
+ size_t __len = strlen(__old) + 1; \
+ char *__new = __builtin_alloca(__len); \
+ memcpy (__new, __old, __len); \
+ __new; \
+ }))
+#endif
+
+
+/* from config.c */
+
+#define MAX_NESTED_COMMENTS 128
+#define COMMENT_START ";--"
+#define COMMENT_END "--;"
+#define COMMENT_META ';'
+#define COMMENT_TAG '-'
+
+static char *extconfig_conf = "extconfig.conf";
+
+/*! Growable string buffer */
+static char *comment_buffer; /*!< this will be a comment collector.*/
+static int comment_buffer_size; /*!< the amount of storage so far alloc'd for the comment_buffer */
+
+static char *lline_buffer; /*!< A buffer for stuff behind the ; */
+static int lline_buffer_size;
+
+#define CB_INCR 250
+
+struct ast_comment {
+ struct ast_comment *next;
+ char cmt[0];
+};
+
+static void CB_INIT(void)
+{
+ if (!comment_buffer) {
+ comment_buffer = ast_malloc(CB_INCR);
+ if (!comment_buffer)
+ return;
+ comment_buffer[0] = 0;
+ comment_buffer_size = CB_INCR;
+ lline_buffer = ast_malloc(CB_INCR);
+ if (!lline_buffer)
+ return;
+ lline_buffer[0] = 0;
+ lline_buffer_size = CB_INCR;
+ } else {
+ comment_buffer[0] = 0;
+ lline_buffer[0] = 0;
+ }
+}
+
+static void CB_ADD(char *str)
+{
+ int rem = comment_buffer_size - strlen(comment_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + siz + 1);
+ if (!comment_buffer)
+ return;
+ comment_buffer_size += CB_INCR+siz+1;
+ }
+ strcat(comment_buffer,str);
+}
+
+static void CB_ADD_LEN(char *str, int len)
+{
+ int cbl = strlen(comment_buffer) + 1;
+ int rem = comment_buffer_size - cbl;
+ if (rem < len+1) {
+ comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + len + 1);
+ if (!comment_buffer)
+ return;
+ comment_buffer_size += CB_INCR+len+1;
+ }
+ strncat(comment_buffer,str,len);
+ comment_buffer[cbl+len-1] = 0;
+}
+
+static void LLB_ADD(char *str)
+{
+ int rem = lline_buffer_size - strlen(lline_buffer) - 1;
+ int siz = strlen(str);
+ if (rem < siz+1) {
+ lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1);
+ if (!lline_buffer)
+ return;
+ lline_buffer_size += CB_INCR + siz + 1;
+ }
+ strcat(lline_buffer,str);
+}
+
+static void CB_RESET(void )
+{
+ comment_buffer[0] = 0;
+ lline_buffer[0] = 0;
+}
+
+/*! \brief Keep track of how many threads are currently trying to wait*() on
+ * a child process */
+static unsigned int safe_system_level = 0;
+static void *safe_system_prev_handler;
+
+/*! \brief NULL handler so we can collect the child exit status */
+static void null_sig_handler(int signal)
+{
+
+}
+
+void ast_replace_sigchld(void);
+
+void ast_replace_sigchld(void)
+{
+ unsigned int level;
+
+ level = safe_system_level++;
+
+ /* only replace the handler if it has not already been done */
+ if (level == 0)
+ safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
+
+}
+
+void ast_unreplace_sigchld(void);
+
+void ast_unreplace_sigchld(void)
+{
+ unsigned int level;
+
+ level = --safe_system_level;
+
+ /* only restore the handler if we are the last one */
+ if (level == 0)
+ signal(SIGCHLD, safe_system_prev_handler);
+
+}
+
+int ast_safe_system(const char *s);
+
+int ast_safe_system(const char *s)
+{
+ pid_t pid;
+#ifdef HAVE_WORKING_FORK
+ int x;
+#endif
+ int res;
+ struct rusage rusage;
+ int status;
+
+#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
+ ast_replace_sigchld();
+
+#ifdef HAVE_WORKING_FORK
+ pid = fork();
+#else
+ pid = vfork();
+#endif
+
+ if (pid == 0) {
+#ifdef HAVE_WORKING_FORK
+ /* Close file descriptors and launch system command */
+ for (x = STDERR_FILENO + 1; x < 4096; x++)
+ close(x);
+#endif
+ execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
+ _exit(1);
+ } else if (pid > 0) {
+ for(;;) {
+ res = wait4(pid, &status, 0, &rusage);
+ if (res > -1) {
+ res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ break;
+ } else if (errno != EINTR)
+ break;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+ res = -1;
+ }
+
+ ast_unreplace_sigchld();
+#else
+ res = -1;
+#endif
+
+ return res;
+}
+
+static struct ast_comment *ALLOC_COMMENT(const char *buffer)
+{
+ struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1);
+ strcpy(x->cmt, buffer);
+ return x;
+}
+
+static struct ast_config_map {
+ struct ast_config_map *next;
+ char *name;
+ char *driver;
+ char *database;
+ char *table;
+ char stuff[0];
+} *config_maps = NULL;
+
+static struct ast_config_engine *config_engine_list;
+
+#define MAX_INCLUDE_LEVEL 10
+
+
+struct ast_category {
+ char name[80];
+ int ignored; /*!< do not let user of the config see this category */
+ int include_level;
+ char *file; /*!< the file name from whence this declaration was read */
+ int lineno;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *root;
+ struct ast_variable *last;
+ struct ast_category *next;
+};
+
+struct ast_config {
+ struct ast_category *root;
+ struct ast_category *last;
+ struct ast_category *current;
+ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
+ int include_level;
+ int max_include_level;
+ struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
+};
+
+struct ast_config_include {
+ char *include_location_file; /*!< file name in which the include occurs */
+ int include_location_lineno; /*!< lineno where include occurred */
+ int exec; /*!< set to non-zero if itsa #exec statement */
+ char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
+ char *included_file; /*!< file name included */
+ int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
+ we explode the instances and will include those-- so all entries will be unique */
+ int output; /*!< a flag to indicate if the inclusion has been output */
+ struct ast_config_include *next; /*!< ptr to next inclusion in the list */
+};
+
+typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, int withcomments, const char *suggested_include_file);
+typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
+typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
+typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+
+/*! \brief Configuration engine structure, used to define realtime drivers */
+struct ast_config_engine {
+ char *name;
+ config_load_func *load_func;
+ realtime_var_get *realtime_func;
+ realtime_multi_get *realtime_multi_func;
+ realtime_update *update_func;
+ struct ast_config_engine *next;
+};
+
+static struct ast_config_engine *config_engine_list;
+
+/* taken from strings.h */
+
+static force_inline int ast_strlen_zero(const char *s)
+{
+ return (!s || (*s == '\0'));
+}
+
+#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b))
+
+AST_INLINE_API(
+void ast_copy_string(char *dst, const char *src, size_t size),
+{
+ while (*src && size) {
+ *dst++ = *src++;
+ size--;
+ }
+ if (__builtin_expect(!size, 0))
+ dst--;
+ *dst = '\0';
+}
+)
+
+AST_INLINE_API(
+char *ast_skip_blanks(const char *str),
+{
+ while (*str && *str < 33)
+ str++;
+ return (char *)str;
+}
+)
+
+/*!
+ \brief Trims trailing whitespace characters from a string.
+ \param ast_trim_blanks function being used
+ \param str the input string
+ \return a pointer to the modified string
+ */
+AST_INLINE_API(
+char *ast_trim_blanks(char *str),
+{
+ char *work = str;
+
+ if (work) {
+ work += strlen(work) - 1;
+ /* It's tempting to only want to erase after we exit this loop,
+ but since ast_trim_blanks *could* receive a constant string
+ (which we presumably wouldn't have to touch), we shouldn't
+ actually set anything unless we must, and it's easier just
+ to set each position to \0 than to keep track of a variable
+ for it */
+ while ((work >= str) && *work < 33)
+ *(work--) = '\0';
+ }
+ return str;
+}
+)
+
+/*!
+ \brief Strip leading/trailing whitespace from a string.
+ \param s The string to be stripped (will be modified).
+ \return The stripped string.
+
+ This functions strips all leading and trailing whitespace
+ characters from the input string, and returns a pointer to
+ the resulting string. The string is modified in place.
+*/
+AST_INLINE_API(
+char *ast_strip(char *s),
+{
+ s = ast_skip_blanks(s);
+ if (s)
+ ast_trim_blanks(s);
+ return s;
+}
+)
+
+
+/* from config.h */
+
+struct ast_variable {
+ char *name;
+ char *value;
+ char *file;
+ int lineno;
+ int object; /*!< 0 for variable, 1 for object */
+ int blanklines; /*!< Number of blanklines following entry */
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_variable *next;
+ char stuff[0];
+};
+
+static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file);
+
+struct ast_config *localized_config_load_with_comments(const char *filename);
+static char *ast_category_browse(struct ast_config *config, const char *prev);
+static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
+static void ast_variables_destroy(struct ast_variable *v);
+static void ast_config_destroy(struct ast_config *cfg);
+static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size);
+static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file);
+void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file);
+
+static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename);
+
+static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
+{
+ struct ast_variable *variable;
+ int name_len = strlen(name) + 1;
+
+ if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
+ variable->name = variable->stuff;
+ variable->value = variable->stuff + name_len;
+ variable->file = variable->value + strlen(value) + 1;
+ strcpy(variable->name,name);
+ strcpy(variable->value,value);
+ strcpy(variable->file,filename);
+ }
+
+ return variable;
+}
+
+static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
+{
+ /* a file should be included ONCE. Otherwise, if one of the instances is changed,
+ then all be changed. -- how do we know to include it? -- Handling modified
+ instances is possible, I'd have
+ to create a new master for each instance. */
+ struct ast_config_include *inc;
+
+ inc = ast_include_find(conf, included_file);
+ if (inc)
+ {
+ inc->inclusion_count++;
+ snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
+ ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
+ } else
+ *real_included_file_name = 0;
+
+ inc = ast_calloc(1,sizeof(struct ast_config_include));
+ inc->include_location_file = ast_strdup(from_file);
+ inc->include_location_lineno = from_lineno;
+ if (!ast_strlen_zero(real_included_file_name))
+ inc->included_file = ast_strdup(real_included_file_name);
+ else
+ inc->included_file = ast_strdup(included_file);
+
+ inc->exec = is_exec;
+ if (is_exec)
+ inc->exec_file = ast_strdup(exec_file);
+
+ /* attach this new struct to the conf struct */
+ inc->next = conf->includes;
+ conf->includes = inc;
+
+ return inc;
+}
+
+void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
+{
+ struct ast_config_include *incl;
+ struct ast_category *cat;
+ struct ast_variable *v;
+
+ int from_len = strlen(from_file);
+ int to_len = strlen(to_file);
+
+ if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
+ return;
+
+ /* the manager code allows you to read in one config file, then
+ write it back out under a different name. But, the new arrangement
+ ties output lines to the file name. So, before you try to write
+ the config file to disk, better riffle thru the data and make sure
+ the file names are changed.
+ */
+ /* file names are on categories, includes (of course), and on variables. So,
+ traverse all this and swap names */
+
+ for (incl = conf->includes; incl; incl=incl->next) {
+ if (strcmp(incl->include_location_file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(incl->include_location_file, to_file);
+ else {
+ free(incl->include_location_file);
+ incl->include_location_file = strdup(to_file);
+ }
+ }
+ }
+ for (cat = conf->root; cat; cat = cat->next) {
+ if (strcmp(cat->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(cat->file, to_file);
+ else {
+ free(cat->file);
+ cat->file = strdup(to_file);
+ }
+ }
+ for (v = cat->root; v; v = v->next) {
+ if (strcmp(v->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(v->file, to_file);
+ else {
+ free(v->file);
+ v->file = strdup(to_file);
+ }
+ }
+ }
+ }
+}
+
+static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
+{
+ struct ast_config_include *x;
+ for (x=conf->includes;x;x=x->next)
+ {
+ if (strcmp(x->included_file,included_file) == 0)
+ return x;
+ }
+ return 0;
+}
+
+
+static void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
+
+static void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
+{
+ if (!variable)
+ return;
+ if (category->last)
+ category->last->next = variable;
+ else
+ category->root = variable;
+ category->last = variable;
+ while (category->last->next)
+ category->last = category->last->next;
+}
+
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored);
+
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
+{
+ struct ast_category *cat;
+
+ /* try exact match first, then case-insensitive match */
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == category_name && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ return NULL;
+}
+
+static struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+{
+ return category_get(config, category_name, 0);
+}
+
+static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
+{
+ struct ast_category *cat = NULL;
+
+ if (category && config->last_browse && (config->last_browse->name == category))
+ cat = config->last_browse;
+ else
+ cat = ast_category_get(config, category);
+
+ return (cat) ? cat->root : NULL;
+}
+
+static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
+{
+ struct ast_variable *v;
+
+ if (category) {
+ for (v = ast_variable_browse(config, category); v; v = v->next) {
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
+ } else {
+ struct ast_category *cat;
+
+ for (cat = config->root; cat; cat = cat->next)
+ for (v = cat->root; v; v = v->next)
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
+
+ return NULL;
+}
+
+static struct ast_variable *variable_clone(const struct ast_variable *old)
+{
+ struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
+
+ if (new) {
+ new->lineno = old->lineno;
+ new->object = old->object;
+ new->blanklines = old->blanklines;
+ /* TODO: clone comments? */
+ }
+
+ return new;
+}
+
+static void ast_variables_destroy(struct ast_variable *v)
+{
+ struct ast_variable *vn;
+
+ while (v) {
+ vn = v;
+ v = v->next;
+ free(vn);
+ }
+}
+
+static void ast_includes_destroy(struct ast_config_include *incls)
+{
+ struct ast_config_include *incl,*inclnext;
+
+ for (incl=incls; incl; incl = inclnext) {
+ inclnext = incl->next;
+ if (incl->include_location_file)
+ free(incl->include_location_file);
+ if (incl->exec_file)
+ free(incl->exec_file);
+ if (incl->included_file)
+ free(incl->included_file);
+ free(incl);
+ }
+}
+
+static void ast_config_destroy(struct ast_config *cfg)
+{
+ struct ast_category *cat, *catn;
+
+ if (!cfg)
+ return;
+
+ ast_includes_destroy(cfg->includes);
+
+ cat = cfg->root;
+ while (cat) {
+ ast_variables_destroy(cat->root);
+ catn = cat;
+ cat = cat->next;
+ free(catn);
+ }
+ free(cfg);
+}
+
+
+/* options.h declars ast_options extern; I need it static? */
+
+#define AST_CACHE_DIR_LEN 512
+#define AST_FILENAME_MAX 80
+
+/*! \ingroup main_options */
+enum ast_option_flags {
+ /*! Allow \#exec in config files */
+ AST_OPT_FLAG_EXEC_INCLUDES = (1 << 0),
+ /*! Do not fork() */
+ AST_OPT_FLAG_NO_FORK = (1 << 1),
+ /*! Keep quiet */
+ AST_OPT_FLAG_QUIET = (1 << 2),
+ /*! Console mode */
+ AST_OPT_FLAG_CONSOLE = (1 << 3),
+ /*! Run in realtime Linux priority */
+ AST_OPT_FLAG_HIGH_PRIORITY = (1 << 4),
+ /*! Initialize keys for RSA authentication */
+ AST_OPT_FLAG_INIT_KEYS = (1 << 5),
+ /*! Remote console */
+ AST_OPT_FLAG_REMOTE = (1 << 6),
+ /*! Execute an asterisk CLI command upon startup */
+ AST_OPT_FLAG_EXEC = (1 << 7),
+ /*! Don't use termcap colors */
+ AST_OPT_FLAG_NO_COLOR = (1 << 8),
+ /*! Are we fully started yet? */
+ AST_OPT_FLAG_FULLY_BOOTED = (1 << 9),
+ /*! Trascode via signed linear */
+ AST_OPT_FLAG_TRANSCODE_VIA_SLIN = (1 << 10),
+ /*! Enable priority jumping in applications */
+ AST_OPT_FLAG_PRIORITY_JUMPING = (1 << 11),
+ /*! Dump core on a seg fault */
+ AST_OPT_FLAG_DUMP_CORE = (1 << 12),
+ /*! Cache sound files */
+ AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13),
+ /*! Display timestamp in CLI verbose output */
+ AST_OPT_FLAG_TIMESTAMP = (1 << 14),
+ /*! Override config */
+ AST_OPT_FLAG_OVERRIDE_CONFIG = (1 << 15),
+ /*! Reconnect */
+ AST_OPT_FLAG_RECONNECT = (1 << 16),
+ /*! Transmit Silence during Record() */
+ AST_OPT_FLAG_TRANSMIT_SILENCE = (1 << 17),
+ /*! Suppress some warnings */
+ AST_OPT_FLAG_DONT_WARN = (1 << 18),
+ /*! End CDRs before the 'h' extension */
+ AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN = (1 << 19),
+ /*! Use Zaptel Timing for generators if available */
+ AST_OPT_FLAG_INTERNAL_TIMING = (1 << 20),
+ /*! Always fork, even if verbose or debug settings are non-zero */
+ AST_OPT_FLAG_ALWAYS_FORK = (1 << 21),
+ /*! Disable log/verbose output to remote consoles */
+ AST_OPT_FLAG_MUTE = (1 << 22)
+};
+
+/*! These are the options that set by default when Asterisk starts */
+#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN
+
+#define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES)
+#define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK)
+#define ast_opt_quiet ast_test_flag(&ast_options, AST_OPT_FLAG_QUIET)
+#define ast_opt_console ast_test_flag(&ast_options, AST_OPT_FLAG_CONSOLE)
+#define ast_opt_high_priority ast_test_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY)
+#define ast_opt_init_keys ast_test_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS)
+#define ast_opt_remote ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)
+#define ast_opt_exec ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC)
+#define ast_opt_no_color ast_test_flag(&ast_options, AST_OPT_FLAG_NO_COLOR)
+#define ast_fully_booted ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)
+#define ast_opt_transcode_via_slin ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN)
+#define ast_opt_priority_jumping ast_test_flag(&ast_options, AST_OPT_FLAG_PRIORITY_JUMPING)
+#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE)
+#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES)
+#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP)
+#define ast_opt_override_config ast_test_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG)
+#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT)
+#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE)
+#define ast_opt_dont_warn ast_test_flag(&ast_options, AST_OPT_FLAG_DONT_WARN)
+#define ast_opt_end_cdr_before_h_exten ast_test_flag(&ast_options, AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN)
+#define ast_opt_internal_timing ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING)
+#define ast_opt_always_fork ast_test_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK)
+#define ast_opt_mute ast_test_flag(&ast_options, AST_OPT_FLAG_MUTE)
+
+/* IN CONFLICT: extern int option_verbose; */
+/* IN CONFLICT: extern int option_debug; */ /*!< Debugging */
+extern int option_maxcalls; /*!< Maximum number of simultaneous channels */
+extern double option_maxload;
+extern char defaultlanguage[];
+
+extern time_t ast_startuptime;
+extern time_t ast_lastreloadtime;
+extern pid_t ast_mainpid;
+
+extern char record_cache_dir[AST_CACHE_DIR_LEN];
+extern char debug_filename[AST_FILENAME_MAX];
+
+extern int ast_language_is_prefix;
+
+
+
+/* lock.h */
+
+#ifndef HAVE_MTX_PROFILE
+#define __MTX_PROF(a) return pthread_mutex_lock((a))
+#else
+#define __MTX_PROF(a) do { \
+ int i; \
+ /* profile only non-blocking events */ \
+ ast_mark(mtx_prof, 1); \
+ i = pthread_mutex_trylock((a)); \
+ ast_mark(mtx_prof, 0); \
+ if (!i) \
+ return i; \
+ else \
+ return pthread_mutex_lock((a)); \
+ } while (0)
+#endif /* HAVE_MTX_PROFILE */
+
+#define AST_PTHREADT_NULL (pthread_t) -1
+#define AST_PTHREADT_STOP (pthread_t) -2
+
+#if defined(SOLARIS) || defined(BSD)
+#define AST_MUTEX_INIT_W_CONSTRUCTORS
+#endif /* SOLARIS || BSD */
+
+/* Asterisk REQUIRES recursive (not error checking) mutexes
+ and will not run without them. */
+#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) && defined(PTHREAD_MUTEX_RECURSIVE_NP)
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE_NP
+#else
+#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_MUTEX_INITIALIZER
+#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE
+#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */
+
+#ifdef DEBUG_THREADS
+
+#define __ast_mutex_logger(...) do { if (canlog) ast_log(LOG_ERROR, __VA_ARGS__); else fprintf(stderr, __VA_ARGS__); } while (0)
+
+#ifdef THREAD_CRASH
+#define DO_THREAD_CRASH do { *((int *)(0)) = 1; } while(0)
+#else
+#define DO_THREAD_CRASH do { } while (0)
+#endif
+
+#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, { NULL }, { 0 }, 0, { NULL }, { 0 } }
+
+#define AST_MAX_REENTRANCY 10
+
+struct ast_mutex_info {
+ pthread_mutex_t mutex;
+ /*! Track which thread holds this lock */
+ unsigned int track:1;
+ const char *file[AST_MAX_REENTRANCY];
+ int lineno[AST_MAX_REENTRANCY];
+ int reentrancy;
+ const char *func[AST_MAX_REENTRANCY];
+ pthread_t thread[AST_MAX_REENTRANCY];
+};
+
+typedef struct ast_mutex_info ast_mutex_t;
+
+typedef pthread_cond_t ast_cond_t;
+
+static pthread_mutex_t empty_mutex;
+
+static void __attribute__((constructor)) init_empty_mutex(void)
+{
+ memset(&empty_mutex, 0, sizeof(empty_mutex));
+}
+
+static inline int __ast_pthread_mutex_init_attr(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t,
+ pthread_mutexattr_t *attr)
+{
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ int canlog = strcmp(filename, "logger.c");
+
+ if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ if ((t->mutex) != (empty_mutex)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is already initialized.\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): Error: previously initialization of mutex '%s'.\n",
+ t->file[0], t->lineno[0], t->func[0], mutex_name);
+ DO_THREAD_CRASH;
+ return 0;
+ }
+ }
+#endif
+
+ t->file[0] = filename;
+ t->lineno[0] = lineno;
+ t->func[0] = func;
+ t->thread[0] = 0;
+ t->reentrancy = 0;
+
+ return pthread_mutex_init(&t->mutex, attr);
+}
+
+static inline int __ast_pthread_mutex_init(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ static pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+
+ return __ast_pthread_mutex_init_attr(filename, lineno, func, mutex_name, t, &attr);
+}
+#define ast_mutex_init(pmutex) __ast_pthread_mutex_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #pmutex, pmutex)
+
+static inline int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ res = pthread_mutex_trylock(&t->mutex);
+ switch (res) {
+ case 0:
+ pthread_mutex_unlock(&t->mutex);
+ break;
+ case EINVAL:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy invalid mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ break;
+ case EBUSY:
+ __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): Error: '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ break;
+ }
+
+ if ((res = pthread_mutex_destroy(&t->mutex)))
+ __ast_mutex_logger("%s line %d (%s): Error destroying mutex: %s\n",
+ filename, lineno, func, strerror(res));
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+ else
+ t->mutex = PTHREAD_MUTEX_INIT_VALUE;
+#endif
+ t->file[0] = filename;
+ t->lineno[0] = lineno;
+ t->func[0] = func;
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ ast_mutex_init(t);
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+#ifdef DETECT_DEADLOCKS
+ {
+ time_t seconds = time(NULL);
+ time_t current;
+ do {
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+#endif
+ res = pthread_mutex_trylock(&t->mutex);
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 0);
+#endif
+ if (res == EBUSY) {
+ current = time(NULL);
+ if ((current - seconds) && (!((current - seconds) % 5))) {
+ __ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for mutex '%s'?\n",
+ filename, lineno, func, (int)(current - seconds), mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1],
+ t->func[t->reentrancy-1], mutex_name);
+ }
+ usleep(200);
+ }
+ } while (res == EBUSY);
+ }
+#else
+#ifdef HAVE_MTX_PROFILE
+ ast_mark(mtx_prof, 1);
+ res = pthread_mutex_trylock(&t->mutex);
+ ast_mark(mtx_prof, 0);
+ if (res)
+#endif
+ res = pthread_mutex_lock(&t->mutex);
+#endif /* DETECT_DEADLOCKS */
+
+ if (!res) {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ } else {
+ __ast_mutex_logger("%s line %d (%s): Error obtaining mutex: %s\n",
+ filename, lineno, func, strerror(errno));
+ DO_THREAD_CRASH;
+ }
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *func,
+ const char* mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ ast_mutex_init(t);
+ }
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+ if (!(res = pthread_mutex_trylock(&t->mutex))) {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ } else {
+ __ast_mutex_logger("%s line %d (%s): Warning: '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ }
+
+ return res;
+}
+
+static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *func,
+ const char *mutex_name, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+
+ if ((res = pthread_mutex_unlock(&t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ }
+
+ return res;
+}
+
+static inline int __ast_cond_init(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr)
+{
+ return pthread_cond_init(cond, cond_attr);
+}
+
+static inline int __ast_cond_signal(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_signal(cond);
+}
+
+static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_broadcast(cond);
+}
+
+static inline int __ast_cond_destroy(const char *filename, int lineno, const char *func,
+ const char *cond_name, ast_cond_t *cond)
+{
+ return pthread_cond_destroy(cond);
+}
+
+static inline int __ast_cond_wait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name,
+ ast_cond_t *cond, ast_mutex_t *t)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+
+ if ((res = pthread_cond_wait(cond, &t->mutex))) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ }
+
+ return res;
+}
+
+static inline int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
+ const char *cond_name, const char *mutex_name, ast_cond_t *cond,
+ ast_mutex_t *t, const struct timespec *abstime)
+{
+ int res;
+ int canlog = strcmp(filename, "logger.c");
+
+#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
+ if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
+ __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
+ filename, lineno, func, mutex_name);
+ }
+#endif
+
+ if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
+ __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+ filename, lineno, func, mutex_name);
+ __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
+ t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
+ DO_THREAD_CRASH;
+ }
+
+ if (--t->reentrancy < 0) {
+ __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ filename, lineno, func, mutex_name);
+ t->reentrancy = 0;
+ }
+
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = NULL;
+ t->lineno[t->reentrancy] = 0;
+ t->func[t->reentrancy] = NULL;
+ t->thread[t->reentrancy] = 0;
+ }
+
+ if ((res = pthread_cond_timedwait(cond, &t->mutex, abstime)) && (res != ETIMEDOUT)) {
+ __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
+ filename, lineno, func, strerror(res));
+ DO_THREAD_CRASH;
+ } else {
+ if (t->reentrancy < AST_MAX_REENTRANCY) {
+ t->file[t->reentrancy] = filename;
+ t->lineno[t->reentrancy] = lineno;
+ t->func[t->reentrancy] = func;
+ t->thread[t->reentrancy] = pthread_self();
+ t->reentrancy++;
+ } else {
+ __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
+ filename, lineno, func, mutex_name);
+ }
+ }
+
+ return res;
+}
+
+#define ast_mutex_destroy(a) __ast_pthread_mutex_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_mutex_trylock(a) __ast_pthread_mutex_trylock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
+#define ast_cond_init(cond, attr) __ast_cond_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond, attr)
+#define ast_cond_destroy(cond) __ast_cond_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_signal(cond) __ast_cond_signal(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_broadcast(cond) __ast_cond_broadcast(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
+#define ast_cond_wait(cond, mutex) __ast_cond_wait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex)
+#define ast_cond_timedwait(cond, mutex, time) __ast_cond_timedwait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex, time)
+
+#else /* !DEBUG_THREADS */
+
+
+typedef pthread_mutex_t ast_mutex_t;
+
+#define AST_MUTEX_INIT_VALUE ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE)
+
+static inline int ast_mutex_init(ast_mutex_t *pmutex)
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
+
+ return pthread_mutex_init(pmutex, &attr);
+}
+
+#define ast_pthread_mutex_init(pmutex,a) pthread_mutex_init(pmutex,a)
+
+static inline int ast_mutex_unlock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_unlock(pmutex);
+}
+
+static inline int ast_mutex_destroy(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_destroy(pmutex);
+}
+
+static inline int ast_mutex_lock(ast_mutex_t *pmutex)
+{
+ __MTX_PROF(pmutex);
+}
+
+static inline int ast_mutex_trylock(ast_mutex_t *pmutex)
+{
+ return pthread_mutex_trylock(pmutex);
+}
+
+typedef pthread_cond_t ast_cond_t;
+
+static inline int ast_cond_init(ast_cond_t *cond, pthread_condattr_t *cond_attr)
+{
+ return pthread_cond_init(cond, cond_attr);
+}
+
+static inline int ast_cond_signal(ast_cond_t *cond)
+{
+ return pthread_cond_signal(cond);
+}
+
+static inline int ast_cond_broadcast(ast_cond_t *cond)
+{
+ return pthread_cond_broadcast(cond);
+}
+
+static inline int ast_cond_destroy(ast_cond_t *cond)
+{
+ return pthread_cond_destroy(cond);
+}
+
+static inline int ast_cond_wait(ast_cond_t *cond, ast_mutex_t *t)
+{
+ return pthread_cond_wait(cond, t);
+}
+
+static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const struct timespec *abstime)
+{
+ return pthread_cond_timedwait(cond, t, abstime);
+}
+
+#endif /* !DEBUG_THREADS */
+
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+/* If AST_MUTEX_INIT_W_CONSTRUCTORS is defined, use file scope
+ constructors/destructors to create/destroy mutexes. */
+#define __AST_MUTEX_DEFINE(scope, mutex) \
+ scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE; \
+static void __attribute__ ((constructor)) init_##mutex(void) \
+{ \
+ ast_mutex_init(&mutex); \
+} \
+static void __attribute__ ((destructor)) fini_##mutex(void) \
+{ \
+ ast_mutex_destroy(&mutex); \
+}
+#else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */
+/* By default, use static initialization of mutexes. */
+#define __AST_MUTEX_DEFINE(scope, mutex) \
+ scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+
+#define pthread_mutex_t use_ast_mutex_t_instead_of_pthread_mutex_t
+#define pthread_mutex_lock use_ast_mutex_lock_instead_of_pthread_mutex_lock
+#define pthread_mutex_unlock use_ast_mutex_unlock_instead_of_pthread_mutex_unlock
+#define pthread_mutex_trylock use_ast_mutex_trylock_instead_of_pthread_mutex_trylock
+#define pthread_mutex_init use_ast_mutex_init_instead_of_pthread_mutex_init
+#define pthread_mutex_destroy use_ast_mutex_destroy_instead_of_pthread_mutex_destroy
+#define pthread_cond_t use_ast_cond_t_instead_of_pthread_cond_t
+#define pthread_cond_init use_ast_cond_init_instead_of_pthread_cond_init
+#define pthread_cond_destroy use_ast_cond_destroy_instead_of_pthread_cond_destroy
+#define pthread_cond_signal use_ast_cond_signal_instead_of_pthread_cond_signal
+#define pthread_cond_broadcast use_ast_cond_broadcast_instead_of_pthread_cond_broadcast
+#define pthread_cond_wait use_ast_cond_wait_instead_of_pthread_cond_wait
+#define pthread_cond_timedwait use_ast_cond_timedwait_instead_of_pthread_cond_timedwait
+
+#define AST_MUTEX_DEFINE_STATIC(mutex) __AST_MUTEX_DEFINE(static, mutex)
+
+#define AST_MUTEX_INITIALIZER __use_AST_MUTEX_DEFINE_STATIC_rather_than_AST_MUTEX_INITIALIZER__
+
+#define gethostbyname __gethostbyname__is__not__reentrant__use__ast_gethostbyname__instead__
+
+#ifndef __linux__
+#define pthread_create __use_ast_pthread_create_instead__
+#endif
+
+typedef pthread_rwlock_t ast_rwlock_t;
+
+static inline int ast_rwlock_init(ast_rwlock_t *prwlock)
+{
+ pthread_rwlockattr_t attr;
+
+ pthread_rwlockattr_init(&attr);
+
+#ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
+ pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
+#endif
+
+ return pthread_rwlock_init(prwlock, &attr);
+}
+
+static inline int ast_rwlock_destroy(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_destroy(prwlock);
+}
+
+static inline int ast_rwlock_unlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_unlock(prwlock);
+}
+
+static inline int ast_rwlock_rdlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_rdlock(prwlock);
+}
+
+static inline int ast_rwlock_tryrdlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_tryrdlock(prwlock);
+}
+
+static inline int ast_rwlock_wrlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_wrlock(prwlock);
+}
+
+static inline int ast_rwlock_trywrlock(ast_rwlock_t *prwlock)
+{
+ return pthread_rwlock_trywrlock(prwlock);
+}
+
+/* Statically declared read/write locks */
+
+#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
+#define __AST_RWLOCK_DEFINE(scope, rwlock) \
+ scope ast_rwlock_t rwlock; \
+static void __attribute__ ((constructor)) init_##rwlock(void) \
+{ \
+ ast_rwlock_init(&rwlock); \
+} \
+static void __attribute__ ((destructor)) fini_##rwlock(void) \
+{ \
+ ast_rwlock_destroy(&rwlock); \
+}
+#else
+#define AST_RWLOCK_INIT_VALUE PTHREAD_RWLOCK_INITIALIZER
+#define __AST_RWLOCK_DEFINE(scope, rwlock) \
+ scope ast_rwlock_t rwlock = AST_RWLOCK_INIT_VALUE
+#endif
+
+#define AST_RWLOCK_DEFINE_STATIC(rwlock) __AST_RWLOCK_DEFINE(static, rwlock)
+
+/*
+ * Initial support for atomic instructions.
+ * For platforms that have it, use the native cpu instruction to
+ * implement them. For other platforms, resort to a 'slow' version
+ * (defined in utils.c) that protects the atomic instruction with
+ * a single lock.
+ * The slow versions is always available, for testing purposes,
+ * as ast_atomic_fetchadd_int_slow()
+ */
+
+#if defined(HAVE_OSX_ATOMICS)
+#include "libkern/OSAtomic.h"
+#endif
+
+/*! \brief Atomically add v to *p and return * the previous value of *p.
+ * This can be used to handle reference counts, and the return value
+ * can be used to generate unique identifiers.
+ */
+
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return __sync_fetch_and_add(p, v);
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd32(v, (int32_t *) p);
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return OSAtomicAdd64(v, (int64_t *) p);
+#elif defined (__i386__) || defined(__x86_64__)
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ __asm __volatile (
+ " lock xaddl %0, %1 ; "
+ : "+r" (v), /* 0 (result) */
+ "=m" (*p) /* 1 */
+ : "m" (*p)); /* 2 */
+ return (v);
+})
+#else
+static int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
+{
+ int ret;
+ ret = *p;
+ *p += v;
+ return ret;
+}
+AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
+{
+ return ast_atomic_fetchadd_int_slow(p, v);
+})
+#endif
+
+/*! \brief decrement *p by 1 and return true if the variable has reached 0.
+ * Useful e.g. to check if a refcount has reached 0.
+ */
+#if defined(HAVE_GCC_ATOMICS)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return __sync_sub_and_fetch(p, 1) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd32( -1, (int32_t *) p) == 0;
+})
+#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8)
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ return OSAtomicAdd64( -1, (int64_t *) p) == 0;
+#else
+AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
+{
+ int a = ast_atomic_fetchadd_int(p, -1);
+ return a == 1; /* true if the value is 0 now (so it was 1 previously) */
+})
+#endif
+
+#ifndef DEBUG_CHANNEL_LOCKS
+/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_lock(x) ast_mutex_lock(&x->lock)
+/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_unlock(x) ast_mutex_unlock(&x->lock)
+/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined
+ in the Makefile, print relevant output for debugging */
+#define ast_channel_trylock(x) ast_mutex_trylock(&x->lock)
+#else
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_lock(struct ast_channel *chan);
+
+/*! \brief Unlock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function
+*/
+int ast_channel_unlock(struct ast_channel *chan);
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_trylock(struct ast_channel *chan);
+#endif
+
+
+/* linkedlists.h */
+
+#define AST_LIST_LOCK(head) \
+ ast_mutex_lock(&(head)->lock)
+
+/*!
+ \brief Write locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_WRLOCK(head) \
+ ast_rwlock_wrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_RDLOCK(head) \
+ ast_rwlock_rdlock(&(head)->lock)
+
+/*!
+ \brief Locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_LIST_TRYLOCK(head) \
+ ast_mutex_trylock(&(head)->lock)
+
+/*!
+ \brief Write locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_TRYWRLOCK(head) \
+ ast_rwlock_trywrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ Returns non-zero on success, 0 on failure
+*/
+#define AST_RWLIST_TRYRDLOCK(head) \
+ ast_rwlock_tryrdlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove an exclusive lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_LIST_UNLOCK(head) \
+ ast_mutex_unlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a read/write based list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove a read or write lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_RWLIST_UNLOCK(head) \
+ ast_rwlock_unlock(&(head)->lock)
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_RWLIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_RWLIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type (with no lock).
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD_NOLOCK(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD_NOLOCK(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+}
+
+/*!
+ \brief Defines initial values for a declaration of AST_LIST_HEAD
+*/
+#define AST_LIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_MUTEX_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_RWLIST_HEAD
+*/
+#define AST_RWLIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_RWLOCK_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK
+*/
+#define AST_LIST_HEAD_NOLOCK_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ }
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_LIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+#define AST_LIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name; \
+static void __attribute__ ((constructor)) init_##name(void) \
+{ \
+ AST_LIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) fini_##name(void) \
+{ \
+ AST_LIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_LIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name = AST_LIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_RWLIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#ifndef AST_RWLOCK_INIT_VALUE
+#define AST_RWLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name; \
+static void __attribute__ ((constructor)) init_##name(void) \
+{ \
+ AST_RWLIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) fini_##name(void) \
+{ \
+ AST_RWLIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_RWLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name = AST_RWLIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+
+ This is the same as AST_LIST_HEAD_STATIC, except without the lock included.
+*/
+#define AST_LIST_HEAD_NOLOCK_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+} name = AST_LIST_HEAD_NOLOCK_INIT_VALUE
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_mutex_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes an rwlist head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_RWLIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_rwlock_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value.
+*/
+#define AST_LIST_HEAD_SET_NOLOCK(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+} while (0)
+
+/*!
+ \brief Declare a forward link structure inside a list entry.
+ \param type This is the type of each list entry.
+
+ This macro declares a structure to be used to link list entries together.
+ It must be used inside the definition of the structure named in
+ \a type, as follows:
+
+ \code
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ \endcode
+
+ The field name \a list here is arbitrary, and can be anything you wish.
+*/
+#define AST_LIST_ENTRY(type) \
+struct { \
+ struct type *next; \
+}
+
+#define AST_RWLIST_ENTRY AST_LIST_ENTRY
+
+/*!
+ \brief Returns the first entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_LIST_FIRST(head) ((head)->first)
+
+#define AST_RWLIST_FIRST AST_LIST_FIRST
+
+/*!
+ \brief Returns the last entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_LIST_LAST(head) ((head)->last)
+
+#define AST_RWLIST_LAST AST_LIST_LAST
+
+/*!
+ \brief Returns the next entry in the list after the given entry.
+ \param elm This is a pointer to the current entry.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+*/
+#define AST_LIST_NEXT(elm, field) ((elm)->field.next)
+
+#define AST_RWLIST_NEXT AST_LIST_NEXT
+
+/*!
+ \brief Checks whether the specified list contains any entries.
+ \param head This is a pointer to the list head structure
+
+ Returns non-zero if the list has entries, zero if not.
+ */
+#define AST_LIST_EMPTY(head) (AST_LIST_FIRST(head) == NULL)
+
+#define AST_RWLIST_EMPTY AST_LIST_EMPTY
+
+/*!
+ \brief Loops over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is use to loop over (traverse) the entries in a list. It uses a
+ \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+ \code
+ static AST_LIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_LIST_TRAVERSE(&entries, current, list) {
+ (do something with current here)
+ }
+ \endcode
+ \warning If you modify the forward-link pointer contained in the \a current entry while
+ inside the loop, the behavior will be unpredictable. At a minimum, the following
+ macros will modify the forward-link pointer, and should not be used inside
+ AST_LIST_TRAVERSE() against the entry pointed to by the \a current pointer without
+ careful consideration of their consequences:
+ \li AST_LIST_NEXT() (when used as an lvalue)
+ \li AST_LIST_INSERT_AFTER()
+ \li AST_LIST_INSERT_HEAD()
+ \li AST_LIST_INSERT_TAIL()
+*/
+#define AST_LIST_TRAVERSE(head,var,field) \
+ for((var) = (head)->first; (var); (var) = (var)->field.next)
+
+#define AST_RWLIST_TRAVERSE AST_LIST_TRAVERSE
+
+/*!
+ \brief Loops safely over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is used to safely loop over (traverse) the entries in a list. It
+ uses a \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+
+ \code
+ static AST_LIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_LIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&entries, current, list) {
+ (do something with current here)
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ \endcode
+
+ It differs from AST_LIST_TRAVERSE() in that the code inside the loop can modify
+ (or even free, after calling AST_LIST_REMOVE_CURRENT()) the entry pointed to by
+ the \a current pointer without affecting the loop traversal.
+*/
+#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field) { \
+ typeof((head)->first) __list_next; \
+ typeof((head)->first) __list_prev = NULL; \
+ typeof((head)->first) __new_prev = NULL; \
+ for ((var) = (head)->first, __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL; \
+ (var); \
+ __list_prev = __new_prev, (var) = __list_next, \
+ __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL \
+ )
+
+#define AST_RWLIST_TRAVERSE_SAFE_BEGIN AST_LIST_TRAVERSE_SAFE_BEGIN
+
+/*!
+ \brief Removes the \a current entry from a list during a traversal.
+ \param head This is a pointer to the list head structure
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+ block; it is used to unlink the current entry from the list without affecting
+ the list traversal (and without having to re-traverse the list to modify the
+ previous entry, if any).
+ */
+#define AST_LIST_REMOVE_CURRENT(head, field) \
+ __new_prev->field.next = NULL; \
+ __new_prev = __list_prev; \
+ if (__list_prev) \
+ __list_prev->field.next = __list_next; \
+ else \
+ (head)->first = __list_next; \
+ if (!__list_next) \
+ (head)->last = __list_prev;
+
+#define AST_RWLIST_REMOVE_CURRENT AST_LIST_REMOVE_CURRENT
+
+/*!
+ \brief Inserts a list entry before the current entry during a traversal.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+ block.
+ */
+#define AST_LIST_INSERT_BEFORE_CURRENT(head, elm, field) do { \
+ if (__list_prev) { \
+ (elm)->field.next = __list_prev->field.next; \
+ __list_prev->field.next = elm; \
+ } else { \
+ (elm)->field.next = (head)->first; \
+ (head)->first = (elm); \
+ } \
+ __new_prev = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_BEFORE_CURRENT AST_LIST_INSERT_BEFORE_CURRENT
+
+/*!
+ \brief Closes a safe loop traversal block.
+ */
+#define AST_LIST_TRAVERSE_SAFE_END }
+
+#define AST_RWLIST_TRAVERSE_SAFE_END AST_LIST_TRAVERSE_SAFE_END
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_init(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_RWLIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_init(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_LIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_RWLIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list). There is no embedded lock handling
+ with this macro.
+*/
+#define AST_LIST_HEAD_INIT_NOLOCK(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+}
+
+/*!
+ \brief Inserts a list entry after a given entry.
+ \param head This is a pointer to the list head structure
+ \param listelm This is a pointer to the entry after which the new entry should
+ be inserted.
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.next = (listelm)->field.next; \
+ (listelm)->field.next = (elm); \
+ if ((head)->last == (listelm)) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_AFTER AST_LIST_INSERT_AFTER
+
+/*!
+ \brief Inserts a list entry at the head of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.next = (head)->first; \
+ (head)->first = (elm); \
+ if (!(head)->last) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWLIST_INSERT_HEAD AST_LIST_INSERT_HEAD
+
+/*!
+ \brief Appends a list entry to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be appended.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ Note: The link field in the appended entry is \b not modified, so if it is
+ actually the head of a list itself, the entire list will be appended
+ temporarily (until the next AST_LIST_INSERT_TAIL is performed).
+ */
+#define AST_LIST_INSERT_TAIL(head, elm, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (elm); \
+ (head)->last = (elm); \
+ } else { \
+ (head)->last->field.next = (elm); \
+ (head)->last = (elm); \
+ } \
+} while (0)
+
+#define AST_RWLIST_INSERT_TAIL AST_LIST_INSERT_TAIL
+
+/*!
+ \brief Appends a whole list to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param list This is a pointer to the list to be appended.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_LIST_APPEND_LIST(head, list, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (list)->first; \
+ (head)->last = (list)->last; \
+ } else { \
+ (head)->last->field.next = (list)->first; \
+ (head)->last = (list)->last; \
+ } \
+} while (0)
+
+#define AST_RWLIST_APPEND_LIST AST_LIST_APPEND_LIST
+
+/*!
+ \brief Removes and returns the head entry from a list.
+ \param head This is a pointer to the list head structure
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+
+ Removes the head entry from the list, and returns a pointer to it.
+ This macro is safe to call on an empty list.
+ */
+#define AST_LIST_REMOVE_HEAD(head, field) ({ \
+ typeof((head)->first) cur = (head)->first; \
+ if (cur) { \
+ (head)->first = cur->field.next; \
+ cur->field.next = NULL; \
+ if ((head)->last == cur) \
+ (head)->last = NULL; \
+ } \
+ cur; \
+ })
+
+#define AST_RWLIST_REMOVE_HEAD AST_LIST_REMOVE_HEAD
+
+/*!
+ \brief Removes a specific entry from a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be removed.
+ \param field This is the name of the field (declared using AST_LIST_ENTRY())
+ used to link entries of this list together.
+ \warning The removed entry is \b not freed nor modified in any way.
+ */
+#define AST_LIST_REMOVE(head, elm, field) do { \
+ if ((head)->first == (elm)) { \
+ (head)->first = (elm)->field.next; \
+ if ((head)->last == (elm)) \
+ (head)->last = NULL; \
+ } else { \
+ typeof(elm) curelm = (head)->first; \
+ while (curelm && (curelm->field.next != (elm))) \
+ curelm = curelm->field.next; \
+ if (curelm) { \
+ curelm->field.next = (elm)->field.next; \
+ if ((head)->last == (elm)) \
+ (head)->last = curelm; \
+ } \
+ } \
+ (elm)->field.next = NULL; \
+} while (0)
+
+#define AST_RWLIST_REMOVE AST_LIST_REMOVE
+
+/* chanvars.h */
+
+struct ast_var_t {
+ AST_LIST_ENTRY(ast_var_t) entries;
+ char *value;
+ char name[0];
+};
+
+AST_LIST_HEAD_NOLOCK(varshead, ast_var_t);
+
+AST_RWLOCK_DEFINE_STATIC(globalslock);
+static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+
+/* IN CONFLICT: struct ast_var_t *ast_var_assign(const char *name, const char *value); */
+
+static struct ast_var_t *ast_var_assign(const char *name, const char *value);
+
+static void ast_var_delete(struct ast_var_t *var);
+
+/*from channel.h */
+#define AST_MAX_EXTENSION 80 /*!< Max length of an extension */
+
+
+/* from pbx.h */
+#define PRIORITY_HINT -1 /*!< Special Priority for a hint */
+
+enum ast_extension_states {
+ AST_EXTENSION_REMOVED = -2, /*!< Extension removed */
+ AST_EXTENSION_DEACTIVATED = -1, /*!< Extension hint removed */
+ AST_EXTENSION_NOT_INUSE = 0, /*!< No device INUSE or BUSY */
+ AST_EXTENSION_INUSE = 1 << 0, /*!< One or more devices INUSE */
+ AST_EXTENSION_BUSY = 1 << 1, /*!< All devices BUSY */
+ AST_EXTENSION_UNAVAILABLE = 1 << 2, /*!< All devices UNAVAILABLE/UNREGISTERED */
+ AST_EXTENSION_RINGING = 1 << 3, /*!< All devices RINGING */
+ AST_EXTENSION_ONHOLD = 1 << 4, /*!< All devices ONHOLD */
+};
+
+struct ast_custom_function {
+ const char *name; /*!< Name */
+ const char *synopsis; /*!< Short description for "show functions" */
+ const char *desc; /*!< Help text that explains it all */
+ const char *syntax; /*!< Syntax description */
+ int (*read)(struct ast_channel *, const char *, char *, char *, size_t); /*!< Read function, if read is supported */
+ int (*write)(struct ast_channel *, const char *, char *, const char *); /*!< Write function, if write is supported */
+ AST_RWLIST_ENTRY(ast_custom_function) acflist;
+};
+
+typedef int (ast_switch_f)(struct ast_channel *chan, const char *context,
+ const char *exten, int priority, const char *callerid, const char *data);
+
+struct ast_switch {
+ AST_LIST_ENTRY(ast_switch) list;
+ const char *name; /*!< Name of the switch */
+ const char *description; /*!< Description of the switch */
+
+ ast_switch_f *exists;
+ ast_switch_f *canmatch;
+ ast_switch_f *exec;
+ ast_switch_f *matchmore;
+};
+
+
+static char *config = "extensions.conf";
+static char *registrar = "conf2ael";
+static char userscontext[AST_MAX_EXTENSION] = "default";
+static int static_config = 0;
+static int write_protect_config = 1;
+static int autofallthrough_config = 0;
+static int clearglobalvars_config = 0;
+/*! Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK 128
+static void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
+
+
+/* stolen from callerid.c */
+
+/*! \brief Clean up phone string
+ * remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets.
+ * Basically, remove anything that could be invalid in a pattern.
+ */
+static void ast_shrink_phone_number(char *n)
+{
+ int x, y=0;
+ int bracketed = 0;
+
+ for (x=0; n[x]; x++) {
+ switch(n[x]) {
+ case '[':
+ bracketed++;
+ n[y++] = n[x];
+ break;
+ case ']':
+ bracketed--;
+ n[y++] = n[x];
+ break;
+ case '-':
+ if (bracketed)
+ n[y++] = n[x];
+ break;
+ case '.':
+ if (!n[x+1])
+ n[y++] = n[x];
+ break;
+ default:
+ if (!strchr("()", n[x]))
+ n[y++] = n[x];
+ }
+ }
+ n[y] = '\0';
+}
+
+
+/* stolen from chanvars.c */
+
+static const char *ast_var_name(const struct ast_var_t *var)
+{
+ const char *name;
+
+ if (var == NULL || (name = var->name) == NULL)
+ return NULL;
+ /* Return the name without the initial underscores */
+ if (name[0] == '_') {
+ name++;
+ if (name[0] == '_')
+ name++;
+ }
+ return name;
+}
+
+
+/* stolen from asterisk.c */
+
+static struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
+static int option_verbose = 0; /*!< Verbosity level */
+static int option_debug = 0; /*!< Debug level */
+
+
+/* experiment 1: see if it's easier just to use existing config code
+ * to read in the extensions.conf file. In this scenario,
+ I have to rip/copy code from other modules, because they
+ are staticly declared as-is. A solution would be to move
+ the ripped code to another location and make them available
+ to other modules and standalones */
+
+/* Our own version of ast_log, since the expr parser uses it. -- stolen from utils/check_expr.c */
+
+static void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+static void ast_verbose(const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("VERBOSE: ");
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+/* stolen from main/utils.c */
+static char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
+{
+ char *dataPut = start;
+ int inEscape = 0;
+ int inQuotes = 0;
+
+ for (; *start; start++) {
+ if (inEscape) {
+ *dataPut++ = *start; /* Always goes verbatim */
+ inEscape = 0;
+ } else {
+ if (*start == '\\') {
+ inEscape = 1; /* Do not copy \ into the data */
+ } else if (*start == '\'') {
+ inQuotes = 1 - inQuotes; /* Do not copy ' into the data */
+ } else {
+ /* Replace , with |, unless in quotes */
+ *dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
+ }
+ }
+ }
+ if (start != dataPut)
+ *dataPut = 0;
+ return dataPut;
+}
+
+static int ast_true(const char *s)
+{
+ if (ast_strlen_zero(s))
+ return 0;
+
+ /* Determine if this is a true value */
+ if (!strcasecmp(s, "yes") ||
+ !strcasecmp(s, "true") ||
+ !strcasecmp(s, "y") ||
+ !strcasecmp(s, "t") ||
+ !strcasecmp(s, "1") ||
+ !strcasecmp(s, "on"))
+ return -1;
+
+ return 0;
+}
+
+/* stolen from pbx.c */
+#define VAR_BUF_SIZE 4096
+
+#define VAR_NORMAL 1
+#define VAR_SOFTTRAN 2
+#define VAR_HARDTRAN 3
+
+#define BACKGROUND_SKIP (1 << 0)
+#define BACKGROUND_NOANSWER (1 << 1)
+#define BACKGROUND_MATCHEXTEN (1 << 2)
+#define BACKGROUND_PLAYBACK (1 << 3)
+
+/*!
+ \brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+/* from pbx.h */
+typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
+struct ast_timing {
+ int hastime; /*!< If time construct exists */
+ unsigned int monthmask; /*!< Mask for month */
+ unsigned int daymask; /*!< Mask for date */
+ unsigned int dowmask; /*!< Mask for day of week (mon-sun) */
+ unsigned int minmask[24]; /*!< Mask for minute */
+};
+/* end of pbx.h */
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+/*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_context {
+ ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
+ struct ast_exten *root; /*!< The root of the list of extensions */
+ struct ast_context *next; /*!< Link them together */
+ struct ast_include *includes; /*!< Include other contexts */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ void *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+
+
+/*! \brief ast_state_cb: An extension state notify register item */
+struct ast_state_cb {
+ int id;
+ void *data;
+ ast_state_cb_type callback;
+ struct ast_state_cb *next;
+};
+
+/*! \brief Structure for dial plan hints
+
+ \note Hints are pointers from an extension in the dialplan to one or
+ more devices (tech/name)
+ - See \ref AstExtState
+*/
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
+};
+
+struct store_hint {
+ char *context;
+ char *exten;
+ struct ast_state_cb *callbacks;
+ int laststate;
+ AST_LIST_ENTRY(store_hint) list;
+ char data[1];
+};
+
+AST_LIST_HEAD(store_hints, store_hint);
+
+static const struct cfextension_states {
+ int extension_state;
+ const char * const text;
+} extension_states[] = {
+ { AST_EXTENSION_NOT_INUSE, "Idle" },
+ { AST_EXTENSION_INUSE, "InUse" },
+ { AST_EXTENSION_BUSY, "Busy" },
+ { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
+ { AST_EXTENSION_RINGING, "Ringing" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
+ { AST_EXTENSION_ONHOLD, "Hold" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
+};
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+
+#if defined ( __i386__) && (defined(__FreeBSD__) || defined(linux))
+#if defined(__FreeBSD__)
+#include <machine/cpufunc.h>
+#elif defined(linux)
+static __inline uint64_t
+rdtsc(void)
+{
+ uint64_t rv;
+
+ __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
+ return (rv);
+}
+#endif
+#else /* supply a dummy function on other platforms */
+static __inline uint64_t
+rdtsc(void)
+{
+ return 0;
+}
+#endif
+
+
+static struct ast_var_t *ast_var_assign(const char *name, const char *value)
+{
+ struct ast_var_t *var;
+ int name_len = strlen(name) + 1;
+ int value_len = strlen(value) + 1;
+
+ if (!(var = ast_calloc(sizeof(*var) + name_len + value_len, sizeof(char)))) {
+ return NULL;
+ }
+
+ ast_copy_string(var->name, name, name_len);
+ var->value = var->name + name_len;
+ ast_copy_string(var->value, value, value_len);
+
+ return var;
+}
+
+static void ast_var_delete(struct ast_var_t *var)
+{
+ if (var)
+ free(var);
+}
+
+
+/* chopped this one off at the knees! */
+static int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+{
+
+ /* ast_log(LOG_ERROR, "Function %s not registered\n", function); we are not interested in the details here */
+
+ return -1;
+}
+
+static unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
+{
+ int argc;
+ char *scan;
+ int paren = 0, quote = 0;
+
+ if (!buf || !array || !arraylen)
+ return 0;
+
+ memset(array, 0, arraylen * sizeof(*array));
+
+ scan = buf;
+
+ for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
+ array[argc] = scan;
+ for (; *scan; scan++) {
+ if (*scan == '(')
+ paren++;
+ else if (*scan == ')') {
+ if (paren)
+ paren--;
+ } else if (*scan == '"' && delim != '"') {
+ quote = quote ? 0 : 1;
+ /* Remove quote character from argument */
+ memmove(scan, scan + 1, strlen(scan));
+ scan--;
+ } else if (*scan == '\\') {
+ /* Literal character, don't parse */
+ memmove(scan, scan + 1, strlen(scan));
+ } else if ((*scan == delim) && !paren && !quote) {
+ *scan++ = '\0';
+ break;
+ }
+ }
+ }
+
+ if (*scan)
+ array[argc++] = scan;
+
+ return argc;
+}
+
+static void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ const char *nametail = name;
+
+ /* XXX may need locking on the channel ? */
+ if (name[strlen(name)-1] == ')') {
+ char *function = ast_strdupa(name);
+
+ ast_func_write(chan, function, value);
+ return;
+ }
+
+ headp = &globals;
+
+ /* For comparison purposes, we have to strip leading underscores */
+ if (*nametail == '_') {
+ nametail++;
+ if (*nametail == '_')
+ nametail++;
+ }
+
+ AST_LIST_TRAVERSE (headp, newvariable, entries) {
+ if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE(headp, newvariable, entries);
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+
+ if (value) {
+ if ((option_verbose > 1) && (headp == &globals))
+ ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+}
+
+static int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ char *name, *value, *mydata;
+ int argc;
+ char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
+ int global = 0;
+ int x;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
+ return 0;
+ }
+
+ mydata = ast_strdupa(data);
+ argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ /* check for a trailing flags argument */
+ if ((argc > 1) && !strchr(argv[argc-1], '=')) {
+ argc--;
+ if (strchr(argv[argc], 'g'))
+ global = 1;
+ }
+
+ for (x = 0; x < argc; x++) {
+ name = argv[x];
+ if ((value = strchr(name, '='))) {
+ *value++ = '\0';
+ pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
+ } else
+ ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
+ }
+
+ return(0);
+}
+
+int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data);
+
+int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ return pbx_builtin_setvar(chan, data);
+}
+
+
+/*! \brief Helper for get_range.
+ * return the index of the matching entry, starting from 1.
+ * If names is not supplied, try numeric values.
+ */
+
+static int lookup_name(const char *s, char *const names[], int max)
+{
+ int i;
+
+ if (names) {
+ for (i = 0; names[i]; i++) {
+ if (!strcasecmp(s, names[i]))
+ return i+1;
+ }
+ } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
+ return i;
+ }
+ return 0; /* error return */
+}
+
+/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
+ * names, if supplied, is an array of names that should be mapped to numbers.
+ */
+static unsigned get_range(char *src, int max, char *const names[], const char *msg)
+{
+ int s, e; /* start and ending position */
+ unsigned int mask = 0;
+
+ /* Check for whole range */
+ if (ast_strlen_zero(src) || !strcmp(src, "*")) {
+ s = 0;
+ e = max - 1;
+ } else {
+ /* Get start and ending position */
+ char *c = strchr(src, '-');
+ if (c)
+ *c++ = '\0';
+ /* Find the start */
+ s = lookup_name(src, names, max);
+ if (!s) {
+ ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
+ return 0;
+ }
+ s--;
+ if (c) { /* find end of range */
+ e = lookup_name(c, names, max);
+ if (!e) {
+ ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
+ return 0;
+ }
+ e--;
+ } else
+ e = s;
+ }
+ /* Fill the mask. Remember that ranges are cyclic */
+ mask = 1 << e; /* initialize with last element */
+ while (s != e) {
+ if (s >= max) {
+ s = 0;
+ mask |= (1 << s);
+ } else {
+ mask |= (1 << s);
+ s++;
+ }
+ }
+ return mask;
+}
+
+/*! \brief store a bitmask of valid times, one bit each 2 minute */
+static void get_timerange(struct ast_timing *i, char *times)
+{
+ char *e;
+ int x;
+ int s1, s2;
+ int e1, e2;
+ /* int cth, ctm; */
+
+ /* start disabling all times, fill the fields with 0's, as they may contain garbage */
+ memset(i->minmask, 0, sizeof(i->minmask));
+
+ /* 2-minutes per bit, since the mask has only 32 bits :( */
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ for (x=0; x<24; x++)
+ i->minmask[x] = 0x3fffffff; /* 30 bits */
+ return;
+ }
+ /* Otherwise expect a range */
+ e = strchr(times, '-');
+ if (!e) {
+ ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
+ return;
+ }
+ *e++ = '\0';
+ /* XXX why skip non digits ? */
+ while (*e && !isdigit(*e))
+ e++;
+ if (!*e) {
+ ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
+ return;
+ }
+ if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
+ return;
+ }
+ if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
+ return;
+ }
+ /* XXX this needs to be optimized */
+#if 1
+ s1 = s1 * 30 + s2/2;
+ if ((s1 < 0) || (s1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
+ return;
+ }
+ e1 = e1 * 30 + e2/2;
+ if ((e1 < 0) || (e1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
+ return;
+ }
+ /* Go through the time and enable each appropriate bit */
+ for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
+ i->minmask[x/30] |= (1 << (x % 30));
+ }
+ /* Do the last one */
+ i->minmask[x/30] |= (1 << (x % 30));
+#else
+ for (cth=0; cth<24; cth++) {
+ /* Initialize masks to blank */
+ i->minmask[cth] = 0;
+ for (ctm=0; ctm<30; ctm++) {
+ if (
+ /* First hour with more than one hour */
+ (((cth == s1) && (ctm >= s2)) &&
+ ((cth < e1)))
+ /* Only one hour */
+ || (((cth == s1) && (ctm >= s2)) &&
+ ((cth == e1) && (ctm <= e2)))
+ /* In between first and last hours (more than 2 hours) */
+ || ((cth > s1) &&
+ (cth < e1))
+ /* Last hour with more than one hour */
+ || ((cth > s1) &&
+ ((cth == e1) && (ctm <= e2)))
+ )
+ i->minmask[cth] |= (1 << (ctm / 2));
+ }
+ }
+#endif
+ /* All done */
+ return;
+}
+
+static void null_datad(void *foo)
+{
+}
+
+/*! \brief Find realtime engine for realtime family */
+static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
+{
+ struct ast_config_engine *eng, *ret = NULL;
+ struct ast_config_map *map;
+
+
+ for (map = config_maps; map; map = map->next) {
+ if (!strcasecmp(family, map->name)) {
+ if (database)
+ ast_copy_string(database, map->database, dbsiz);
+ if (table)
+ ast_copy_string(table, map->table ? map->table : family, tabsiz);
+ break;
+ }
+ }
+
+ /* Check if the required driver (engine) exist */
+ if (map) {
+ for (eng = config_engine_list; !ret && eng; eng = eng->next) {
+ if (!strcasecmp(eng->name, map->driver))
+ ret = eng;
+ }
+ }
+
+
+ /* if we found a mapping, but the engine is not available, then issue a warning */
+ if (map && !ret)
+ ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
+
+ return ret;
+}
+
+struct ast_category *ast_config_get_current_category(const struct ast_config *cfg);
+
+struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
+{
+ return cfg->current;
+}
+
+static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
+
+static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
+{
+ struct ast_category *category;
+
+ if ((category = ast_calloc(1, sizeof(*category))))
+ ast_copy_string(category->name, name, sizeof(category->name));
+ category->file = strdup(in_file);
+ category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
+ return category;
+}
+
+struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name);
+
+struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name)
+{
+ return category_get(config, category_name, 0);
+}
+
+static void move_variables(struct ast_category *old, struct ast_category *new)
+{
+ struct ast_variable *var = old->root;
+ old->root = NULL;
+#if 1
+ /* we can just move the entire list in a single op */
+ ast_variable_append(new, var);
+#else
+ while (var) {
+ struct ast_variable *next = var->next;
+ var->next = NULL;
+ ast_variable_append(new, var);
+ var = next;
+ }
+#endif
+}
+
+static void inherit_category(struct ast_category *new, const struct ast_category *base)
+{
+ struct ast_variable *var;
+
+ for (var = base->root; var; var = var->next)
+ ast_variable_append(new, variable_clone(var));
+}
+
+static void ast_category_append(struct ast_config *config, struct ast_category *category);
+
+static void ast_category_append(struct ast_config *config, struct ast_category *category)
+{
+ if (config->last)
+ config->last->next = category;
+ else
+ config->root = category;
+ config->last = category;
+ config->current = category;
+}
+
+static void ast_category_destroy(struct ast_category *cat);
+
+static void ast_category_destroy(struct ast_category *cat)
+{
+ ast_variables_destroy(cat->root);
+ if (cat->file)
+ free(cat->file);
+
+ free(cat);
+}
+
+static struct ast_config_engine text_file_engine = {
+ .name = "text",
+ .load_func = config_text_file_load,
+};
+
+
+static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file);
+
+static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file)
+{
+ char db[256];
+ char table[256];
+ struct ast_config_engine *loader = &text_file_engine;
+ struct ast_config *result;
+
+ if (cfg->include_level == cfg->max_include_level) {
+ ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
+ return NULL;
+ }
+
+ cfg->include_level++;
+ /* silence is golden!
+ ast_log(LOG_WARNING, "internal loading file %s level=%d\n", filename, cfg->include_level);
+ */
+
+ if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
+ struct ast_config_engine *eng;
+
+ eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
+
+
+ if (eng && eng->load_func) {
+ loader = eng;
+ } else {
+ eng = find_engine("global", db, sizeof(db), table, sizeof(table));
+ if (eng && eng->load_func)
+ loader = eng;
+ }
+ }
+
+ result = loader->load_func(db, table, filename, cfg, withcomments, suggested_incl_file);
+ /* silence is golden
+ ast_log(LOG_WARNING, "finished internal loading file %s level=%d\n", filename, cfg->include_level);
+ */
+
+ if (result)
+ result->include_level--;
+
+ return result;
+}
+
+
+static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments, const char *suggested_include_file)
+{
+ char *c;
+ char *cur = buf;
+ struct ast_variable *v;
+ char cmd[512], exec_file[512];
+ int object, do_exec, do_include;
+
+ /* Actually parse the entry */
+ if (cur[0] == '[') {
+ struct ast_category *newcat = NULL;
+ char *catname;
+
+ /* A category header */
+ c = strchr(cur, ']');
+ if (!c) {
+ ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *c++ = '\0';
+ cur++;
+ if (*c++ != '(')
+ c = NULL;
+ catname = cur;
+ if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
+ return -1;
+ }
+ (*cat)->lineno = lineno;
+
+ /* add comments */
+ if (withcomments && comment_buffer && comment_buffer[0] ) {
+ newcat->precomments = ALLOC_COMMENT(comment_buffer);
+ }
+ if (withcomments && lline_buffer && lline_buffer[0] ) {
+ newcat->sameline = ALLOC_COMMENT(lline_buffer);
+ }
+ if( withcomments )
+ CB_RESET();
+
+ /* If there are options or categories to inherit from, process them now */
+ if (c) {
+ if (!(cur = strchr(c, ')'))) {
+ ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *cur = '\0';
+ while ((cur = strsep(&c, ","))) {
+ if (!strcasecmp(cur, "!")) {
+ (*cat)->ignored = 1;
+ } else if (!strcasecmp(cur, "+")) {
+ *cat = category_get(cfg, catname, 1);
+ if (!*cat) {
+ ast_config_destroy(cfg);
+ if (newcat)
+ ast_category_destroy(newcat);
+ ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
+ return -1;
+ }
+ if (newcat) {
+ move_variables(newcat, *cat);
+ ast_category_destroy(newcat);
+ newcat = NULL;
+ }
+ } else {
+ struct ast_category *base;
+
+ base = category_get(cfg, cur, 1);
+ if (!base) {
+ ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
+ return -1;
+ }
+ inherit_category(*cat, base);
+ }
+ }
+ }
+ if (newcat)
+ ast_category_append(cfg, *cat);
+ } else if (cur[0] == '#') {
+ /* A directive */
+ cur++;
+ c = cur;
+ while(*c && (*c > 32)) c++;
+ if (*c) {
+ *c = '\0';
+ /* Find real argument */
+ c = ast_skip_blanks(c + 1);
+ if (!*c)
+ c = NULL;
+ } else
+ c = NULL;
+ do_include = !strcasecmp(cur, "include");
+ if(!do_include)
+ do_exec = !strcasecmp(cur, "exec");
+ else
+ do_exec = 0;
+ if (do_exec && !ast_opt_exec_includes) {
+ ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
+ do_exec = 0;
+ }
+ if (do_include || do_exec) {
+ if (c) {
+ char *cur2;
+ char real_inclusion_name[256];
+ struct ast_config_include *inclu;
+
+ /* Strip off leading and trailing "'s and <>'s */
+ while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
+ /* Get rid of leading mess */
+ cur = c;
+ cur2 = cur;
+ while (!ast_strlen_zero(cur)) {
+ c = cur + strlen(cur) - 1;
+ if ((*c == '>') || (*c == '<') || (*c == '\"'))
+ *c = '\0';
+ else
+ break;
+ }
+ /* #exec </path/to/executable>
+ We create a tmp file, then we #include it, then we delete it. */
+ if (do_exec) {
+ snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
+ snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
+ ast_safe_system(cmd);
+ cur = exec_file;
+ } else
+ exec_file[0] = '\0';
+ /* A #include */
+ /* ast_log(LOG_WARNING, "Reading in included file %s withcomments=%d\n", cur, withcomments); */
+
+ /* record this inclusion */
+ inclu = ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
+
+ do_include = ast_config_internal_load(cur, cfg, withcomments, real_inclusion_name) ? 1 : 0;
+ if(!ast_strlen_zero(exec_file))
+ unlink(exec_file);
+ if(!do_include)
+ return 0;
+ /* ast_log(LOG_WARNING, "Done reading in included file %s withcomments=%d\n", cur, withcomments); */
+
+ } else {
+ ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
+ do_exec ? "exec" : "include",
+ do_exec ? "/path/to/executable" : "filename",
+ lineno,
+ configfile);
+ }
+ }
+ else
+ ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
+ } else {
+ /* Just a line (variable = value) */
+ if (!*cat) {
+ ast_log(LOG_WARNING,
+ "parse error: No category context for line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ c = strchr(cur, '=');
+ if (c) {
+ *c = 0;
+ c++;
+ /* Ignore > in => */
+ if (*c== '>') {
+ object = 1;
+ c++;
+ } else
+ object = 0;
+ if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), configfile))) {
+ v->lineno = lineno;
+ v->object = object;
+ /* Put and reset comments */
+ v->blanklines = 0;
+ ast_variable_append(*cat, v);
+ /* add comments */
+ if (withcomments && comment_buffer && comment_buffer[0] ) {
+ v->precomments = ALLOC_COMMENT(comment_buffer);
+ }
+ if (withcomments && lline_buffer && lline_buffer[0] ) {
+ v->sameline = ALLOC_COMMENT(lline_buffer);
+ }
+ if( withcomments )
+ CB_RESET();
+
+ } else {
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "EXTENSIONS.CONF: No '=' (equal sign) in line %d of %s\n", lineno, configfile);
+ }
+ }
+ return 0;
+}
+
+static int use_local_dir = 1;
+
+void localized_use_local_dir(void);
+void localized_use_conf_dir(void);
+
+void localized_use_local_dir(void)
+{
+ use_local_dir = 1;
+}
+
+void localized_use_conf_dir(void)
+{
+ use_local_dir = 0;
+}
+
+
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file)
+{
+ char fn[256];
+ char buf[8192];
+ char *new_buf, *comment_p, *process_buf;
+ FILE *f;
+ int lineno=0;
+ int comment = 0, nest[MAX_NESTED_COMMENTS];
+ struct ast_category *cat = NULL;
+ int count = 0;
+ struct stat statbuf;
+
+ cat = ast_config_get_current_category(cfg);
+
+ if (filename[0] == '/') {
+ ast_copy_string(fn, filename, sizeof(fn));
+ } else {
+ if (use_local_dir)
+ snprintf(fn, sizeof(fn), "./%s", filename);
+ else
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
+ }
+
+ if (withcomments && cfg && cfg->include_level < 2 ) {
+ CB_INIT();
+ }
+
+#ifdef AST_INCLUDE_GLOB
+ {
+ int glob_ret;
+ glob_t globbuf;
+
+ globbuf.gl_offs = 0; /* initialize it to silence gcc */
+#ifdef SOLARIS
+ glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf);
+#else
+ glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
+#endif
+ if (glob_ret == GLOB_NOSPACE)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
+ else if (glob_ret == GLOB_ABORTED)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Read error\n", fn);
+ else {
+ /* loop over expanded files */
+ int i;
+ for (i=0; i<globbuf.gl_pathc; i++) {
+ ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
+#endif
+ do {
+ if (stat(fn, &statbuf))
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode)) {
+ ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
+ continue;
+ }
+ if (option_verbose > 1) {
+ ast_verbose(VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
+ fflush(stdout);
+ }
+ if (!(f = fopen(fn, "r"))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose( "Not found (%s)\n", strerror(errno));
+ continue;
+ }
+ count++;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Parsing %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose("Found\n");
+ while(!feof(f)) {
+ lineno++;
+ if (fgets(buf, sizeof(buf), f)) {
+ if ( withcomments ) {
+ CB_ADD(lline_buffer); /* add the current lline buffer to the comment buffer */
+ lline_buffer[0] = 0; /* erase the lline buffer */
+ }
+
+ new_buf = buf;
+ if (comment)
+ process_buf = NULL;
+ else
+ process_buf = buf;
+
+ while ((comment_p = strchr(new_buf, COMMENT_META))) {
+ if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
+ /* Yuck, gotta memmove */
+ memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
+ new_buf = comment_p;
+ } else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
+ /* Meta-Comment start detected ";--" */
+ if (comment < MAX_NESTED_COMMENTS) {
+ *comment_p = '\0';
+ new_buf = comment_p + 3;
+ comment++;
+ nest[comment-1] = lineno;
+ } else {
+ ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
+ }
+ } else if ((comment_p >= new_buf + 2) &&
+ (*(comment_p - 1) == COMMENT_TAG) &&
+ (*(comment_p - 2) == COMMENT_TAG)) {
+ /* Meta-Comment end detected */
+ comment--;
+ new_buf = comment_p + 1;
+ if (!comment) {
+ /* Back to non-comment now */
+ if (process_buf) {
+ /* Actually have to move what's left over the top, then continue */
+ char *oldptr;
+ oldptr = process_buf + strlen(process_buf);
+ if ( withcomments ) {
+ CB_ADD(";");
+ CB_ADD_LEN(oldptr+1,new_buf-oldptr-1);
+ }
+
+ memmove(oldptr, new_buf, strlen(new_buf) + 1);
+ new_buf = oldptr;
+ } else
+ process_buf = new_buf;
+ }
+ } else {
+ if (!comment) {
+ /* If ; is found, and we are not nested in a comment,
+ we immediately stop all comment processing */
+ if ( withcomments ) {
+ LLB_ADD(comment_p);
+ }
+ *comment_p = '\0';
+ new_buf = comment_p;
+ } else
+ new_buf = comment_p + 1;
+ }
+ }
+ if( withcomments && comment && !process_buf )
+ {
+ CB_ADD(buf); /* the whole line is a comment, store it */
+ }
+
+ if (process_buf) {
+ char *buf = ast_strip(process_buf);
+ if (!ast_strlen_zero(buf)) {
+ if (process_text_line(cfg, &cat, buf, lineno, filename, withcomments, suggested_include_file)) {
+ cfg = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ fclose(f);
+ } while(0);
+ if (comment) {
+ ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
+ }
+#ifdef AST_INCLUDE_GLOB
+ if (!cfg)
+ break;
+ }
+ globfree(&globbuf);
+ }
+ }
+#endif
+ if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
+ if (comment_buffer) {
+ free(comment_buffer);
+ free(lline_buffer);
+ comment_buffer=0;
+ lline_buffer=0;
+ comment_buffer_size=0;
+ lline_buffer_size=0;
+ }
+ }
+ if (count == 0)
+ return NULL;
+
+ return cfg;
+}
+
+
+static struct ast_config *ast_config_new(void) ;
+
+static struct ast_config *ast_config_new(void)
+{
+ struct ast_config *config;
+
+ if ((config = ast_calloc(1, sizeof(*config))))
+ config->max_include_level = MAX_INCLUDE_LEVEL;
+ return config;
+}
+
+struct ast_config *localized_config_load(const char *filename);
+
+struct ast_config *localized_config_load(const char *filename)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, 0, "");
+ if (!result)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+struct ast_config *localized_config_load_with_comments(const char *filename);
+
+struct ast_config *localized_config_load_with_comments(const char *filename)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, 1, "");
+ if (!result)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+static struct ast_category *next_available_category(struct ast_category *cat)
+{
+ for (; cat && cat->ignored; cat = cat->next);
+
+ return cat;
+}
+
+static char *ast_category_browse(struct ast_config *config, const char *prev)
+{
+ struct ast_category *cat = NULL;
+
+ if (prev && config->last_browse && (config->last_browse->name == prev))
+ cat = config->last_browse->next;
+ else if (!prev && config->root)
+ cat = config->root;
+ else if (prev) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == prev) {
+ cat = cat->next;
+ break;
+ }
+ }
+ if (!cat) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, prev)) {
+ cat = cat->next;
+ break;
+ }
+ }
+ }
+ }
+
+ if (cat)
+ cat = next_available_category(cat);
+
+ config->last_browse = cat;
+ return (cat) ? cat->name : NULL;
+}
+
+
+
+void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat);
+
+void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
+{
+ /* cast below is just to silence compiler warning about dropping "const" */
+ cfg->current = (struct ast_category *) cat;
+}
+
+/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
+ which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
+ recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
+ be shocked and mystified as to why things are not showing up in the files!
+
+ Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
+ and line number are stored for each include, plus the name of the file included, so that these statements may be
+ included in the output files on a file_save operation.
+
+ The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
+ are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
+ the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
+ and a header gets added.
+
+ vars and category heads are output in the order they are stored in the config file. So, if the software
+ shuffles these at all, then the placement of #include directives might get a little mixed up, because the
+ file/lineno data probably won't get changed.
+
+*/
+
+static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
+{
+ char date[256]="";
+ time_t t;
+ time(&t);
+ ast_copy_string(date, ctime(&t), sizeof(date));
+
+ fprintf(f1, ";!\n");
+ fprintf(f1, ";! Automatically generated configuration file\n");
+ if (strcmp(configfile, fn))
+ fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
+ else
+ fprintf(f1, ";! Filename: %s\n", configfile);
+ fprintf(f1, ";! Generator: %s\n", generator);
+ fprintf(f1, ";! Creation Date: %s", date);
+ fprintf(f1, ";!\n");
+}
+
+static void set_fn(char *fn, int fn_size, const char *file, const char *configfile)
+{
+ if (!file || file[0] == 0) {
+ if (configfile[0] == '/')
+ ast_copy_string(fn, configfile, fn_size);
+ else
+ snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
+ } else if (file[0] == '/')
+ ast_copy_string(fn, file, fn_size);
+ else
+ snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
+}
+
+int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator);
+
+int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
+{
+ FILE *f;
+ char fn[256];
+ struct ast_variable *var;
+ struct ast_category *cat;
+ struct ast_comment *cmt;
+ struct ast_config_include *incl;
+ int blanklines = 0;
+
+ /* reset all the output flags, in case this isn't our first time saving this data */
+
+ for (incl=cfg->includes; incl; incl = incl->next)
+ incl->output = 0;
+
+ /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
+ are all truncated to zero bytes and have that nice header*/
+
+ for (incl=cfg->includes; incl; incl = incl->next)
+ {
+ if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
+ FILE *f1;
+
+ set_fn(fn, sizeof(fn), incl->included_file, configfile); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
+ f1 = fopen(fn,"w");
+ if (f1) {
+ gen_header(f1, configfile, fn, generator);
+ fclose(f1); /* this should zero out the file */
+ } else {
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
+ }
+ }
+ }
+
+ set_fn(fn, sizeof(fn), 0, configfile); /* just set fn to absolute ver of configfile */
+#ifdef __CYGWIN__
+ if ((f = fopen(fn, "w+"))) {
+#else
+ if ((f = fopen(fn, "w"))) {
+#endif
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn);
+
+ gen_header(f, configfile, fn, generator);
+ cat = cfg->root;
+ fclose(f);
+
+ /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
+ /* since each var, cat, and associated comments can come from any file, we have to be
+ mobile, and open each file, print, and close it on an entry-by-entry basis */
+
+ while(cat) {
+ set_fn(fn, sizeof(fn), cat->file, configfile);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
+ return -1;
+ }
+
+ /* dump any includes that happen before this category header */
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (strcmp(incl->include_location_file, cat->file) == 0){
+ if (cat->lineno > incl->include_location_lineno && !incl->output) {
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ incl->output = 1;
+ }
+ }
+ }
+
+ /* Dump section with any appropriate comment */
+ for (cmt = cat->precomments; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (!cat->precomments)
+ fprintf(f,"\n");
+ fprintf(f, "[%s]", cat->name);
+ for(cmt = cat->sameline; cmt; cmt=cmt->next) {
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (!cat->sameline)
+ fprintf(f,"\n");
+ fclose(f);
+
+ var = cat->root;
+ while(var) {
+ set_fn(fn, sizeof(fn), var->file, configfile);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
+ return -1;
+ }
+
+ /* dump any includes that happen before this category header */
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (strcmp(incl->include_location_file, var->file) == 0){
+ if (var->lineno > incl->include_location_lineno && !incl->output) {
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ incl->output = 1;
+ }
+ }
+ }
+
+ for (cmt = var->precomments; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (var->sameline)
+ fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
+ else
+ fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
+ if (var->blanklines) {
+ blanklines = var->blanklines;
+ while (blanklines--)
+ fprintf(f, "\n");
+ }
+
+ fclose(f);
+
+
+ var = var->next;
+ }
+ cat = cat->next;
+ }
+ if ((option_verbose > 1) && !option_debug)
+ ast_verbose("Saved\n");
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Unable to open for writing: %s\n", fn);
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno));
+ return -1;
+ }
+
+ /* Now, for files with trailing #include/#exec statements,
+ we have to make sure every entry is output */
+
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (!incl->output) {
+ /* open the respective file */
+ set_fn(fn, sizeof(fn), incl->include_location_file, configfile);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
+ return -1;
+ }
+
+ /* output the respective include */
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ fclose(f);
+ incl->output = 1;
+ }
+ }
+
+ return 0;
+}
+
+/* ================ the Line ========================================
+ above this line, you have what you need to load a config file,
+ and below it, you have what you need to process the extensions.conf
+ file into the context/exten/prio stuff. They are both in one file
+ to make things simpler */
+
+static struct ast_context *local_contexts = NULL;
+static struct ast_context *contexts = NULL;
+struct ast_context;
+struct ast_app;
+#ifdef LOW_MEMORY
+#define EXT_DATA_SIZE 256
+#else
+#define EXT_DATA_SIZE 8192
+#endif
+/*!
+ * When looking up extensions, we can have different requests
+ * identified by the 'action' argument, as follows.
+ * Note that the coding is such that the low 4 bits are the
+ * third argument to extension_match_core.
+ */
+enum ext_match_t {
+ E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
+ E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
+ E_MATCH = 0x02, /* extension is an exact match */
+ E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
+ E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
+ E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
+};
+
+#ifdef NOT_ANYMORE
+static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
+#endif
+
+#define SWITCH_DATA_LENGTH 256
+
+static const char *ast_get_extension_app(struct ast_exten *e)
+{
+ return e ? e->app : NULL;
+}
+
+static const char *ast_get_extension_name(struct ast_exten *exten)
+{
+ return exten ? exten->exten : NULL;
+}
+
+static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
+
+/*! \brief ast_change_hint: Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{
+ struct ast_hint *hint;
+ int res = -1;
+
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == oe) {
+ hint->exten = ne;
+ res = 0;
+ break;
+ }
+ }
+
+ return res;
+}
+
+/*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
+static int ast_add_hint(struct ast_exten *e)
+{
+ struct ast_hint *hint;
+
+ if (!e)
+ return -1;
+
+
+ /* Search if hint exists, do nothing */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+ return -1;
+ }
+ }
+
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+
+ if (!(hint = ast_calloc(1, sizeof(*hint)))) {
+ return -1;
+ }
+ /* Initialize and insert new item at the top */
+ hint->exten = e;
+ AST_RWLIST_INSERT_HEAD(&hints, hint, list);
+
+ return 0;
+}
+
+/*! \brief add the extension in the priority chain.
+ * returns 0 on success, -1 on failure
+ */
+static int add_pri(struct ast_context *con, struct ast_exten *tmp,
+ struct ast_exten *el, struct ast_exten *e, int replace)
+{
+ struct ast_exten *ep;
+
+ for (ep = NULL; e ; ep = e, e = e->peer) {
+ if (e->priority >= tmp->priority)
+ break;
+ }
+ if (!e) { /* go at the end, and ep is surely set because the list is not empty */
+ ep->peer = tmp;
+ return 0; /* success */
+ }
+ if (e->priority == tmp->priority) {
+ /* Can't have something exactly the same. Is this a
+ replacement? If so, replace, otherwise, bonk. */
+ if (!replace) {
+ ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
+ tmp->datad(tmp->data);
+ free(tmp);
+ return -1;
+ }
+ /* we are replacing e, so copy the link fields and then update
+ * whoever pointed to e to point to us
+ */
+ tmp->next = e->next; /* not meaningful if we are not first in the peer list */
+ tmp->peer = e->peer; /* always meaningful */
+ if (ep) /* We're in the peer list, just insert ourselves */
+ ep->peer = tmp;
+ else if (el) /* We're the first extension. Take over e's functions */
+ el->next = tmp;
+ else /* We're the very first extension. */
+ con->root = tmp;
+ if (tmp->priority == PRIORITY_HINT)
+ ast_change_hint(e,tmp);
+ /* Destroy the old one */
+ e->datad(e->data);
+ free(e);
+ } else { /* Slip ourselves in just before e */
+ tmp->peer = e;
+ tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
+ if (ep) /* Easy enough, we're just in the peer list */
+ ep->peer = tmp;
+ else { /* we are the first in some peer list, so link in the ext list */
+ if (el)
+ el->next = tmp; /* in the middle... */
+ else
+ con->root = tmp; /* ... or at the head */
+ e->next = NULL; /* e is no more at the head, so e->next must be reset */
+ }
+ /* And immediately return success. */
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ return 0;
+}
+
+/*! \brief ast_remove_hint: Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
+{
+ /* Cleanup the Notifys if hint is removed */
+ struct ast_hint *hint;
+ struct ast_state_cb *cblist, *cbprev;
+ int res = -1;
+
+ if (!e)
+ return -1;
+
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
+ if (hint->exten == e) {
+ cbprev = NULL;
+ cblist = hint->callbacks;
+ while (cblist) {
+ /* Notify with -1 and remove all callbacks */
+ cbprev = cblist;
+ cblist = cblist->next;
+ free(cbprev);
+ }
+ hint->callbacks = NULL;
+ AST_RWLIST_REMOVE_CURRENT(&hints, list);
+ free(hint);
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+
+ return res;
+}
+
+static void destroy_exten(struct ast_exten *e)
+{
+ if (e->priority == PRIORITY_HINT)
+ ast_remove_hint(e);
+
+ if (e->datad)
+ e->datad(e->data);
+ free(e);
+}
+
+char *days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+ NULL,
+};
+
+char *months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+ NULL,
+};
+
+static int ast_build_timing(struct ast_timing *i, const char *info_in)
+{
+ char info_save[256];
+ char *info;
+
+ /* Check for empty just in case */
+ if (ast_strlen_zero(info_in))
+ return 0;
+ /* make a copy just in case we were passed a static string */
+ ast_copy_string(info_save, info_in, sizeof(info_save));
+ info = info_save;
+ /* Assume everything except time */
+ i->monthmask = 0xfff; /* 12 bits */
+ i->daymask = 0x7fffffffU; /* 31 bits */
+ i->dowmask = 0x7f; /* 7 bits */
+ /* on each call, use strsep() to move info to the next argument */
+ get_timerange(i, strsep(&info, "|"));
+ if (info)
+ i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
+ if (info)
+ i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
+ if (info)
+ i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
+ return 1;
+}
+
+/*!
+ * \brief helper functions to sort extensions and patterns in the desired way,
+ * so that more specific patterns appear first.
+ *
+ * ext_cmp1 compares individual characters (or sets of), returning
+ * an int where bits 0-7 are the ASCII code of the first char in the set,
+ * while bit 8-15 are the cardinality of the set minus 1.
+ * This way more specific patterns (smaller cardinality) appear first.
+ * Wildcards have a special value, so that we can directly compare them to
+ * sets by subtracting the two values. In particular:
+ * 0x000xx one character, xx
+ * 0x0yyxx yy character set starting with xx
+ * 0x10000 '.' (one or more of anything)
+ * 0x20000 '!' (zero or more of anything)
+ * 0x30000 NUL (end of string)
+ * 0x40000 error in set.
+ * The pointer to the string is advanced according to needs.
+ * NOTES:
+ * 1. the empty set is equivalent to NUL.
+ * 2. given that a full set has always 0 as the first element,
+ * we could encode the special cases as 0xffXX where XX
+ * is 1, 2, 3, 4 as used above.
+ */
+static int ext_cmp1(const char **p)
+{
+ uint32_t chars[8];
+ int c, cmin = 0xff, count = 0;
+ const char *end;
+
+ /* load, sign extend and advance pointer until we find
+ * a valid character.
+ */
+ while ( (c = *(*p)++) && (c == ' ' || c == '-') )
+ ; /* ignore some characters */
+
+ /* always return unless we have a set of chars */
+ switch (c) {
+ default: /* ordinary character */
+ return 0x0000 | (c & 0xff);
+
+ case 'N': /* 2..9 */
+ return 0x0700 | '2' ;
+
+ case 'X': /* 0..9 */
+ return 0x0900 | '0';
+
+ case 'Z': /* 1..9 */
+ return 0x0800 | '1';
+
+ case '.': /* wildcard */
+ return 0x10000;
+
+ case '!': /* earlymatch */
+ return 0x20000; /* less specific than NULL */
+
+ case '\0': /* empty string */
+ *p = NULL;
+ return 0x30000;
+
+ case '[': /* pattern */
+ break;
+ }
+ /* locate end of set */
+ end = strchr(*p, ']');
+
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0x40000; /* XXX make this entry go last... */
+ }
+
+ bzero(chars, sizeof(chars)); /* clear all chars in the set */
+ for (; *p < end ; (*p)++) {
+ unsigned char c1, c2; /* first-last char in range */
+ c1 = (unsigned char)((*p)[0]);
+ if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
+ c2 = (unsigned char)((*p)[2]);
+ *p += 2; /* skip a total of 3 chars */
+ } else /* individual character */
+ c2 = c1;
+ if (c1 < cmin)
+ cmin = c1;
+ for (; c1 <= c2; c1++) {
+ uint32_t mask = 1 << (c1 % 32);
+ if ( (chars[ c1 / 32 ] & mask) == 0)
+ count += 0x100;
+ chars[ c1 / 32 ] |= mask;
+ }
+ }
+ (*p)++;
+ return count == 0 ? 0x30000 : (count | cmin);
+}
+
+/*!
+ * \brief the full routine to compare extensions in rules.
+ */
+static int ext_cmp(const char *a, const char *b)
+{
+ /* make sure non-patterns come first.
+ * If a is not a pattern, it either comes first or
+ * we use strcmp to compare the strings.
+ */
+ int ret = 0;
+
+ if (a[0] != '_')
+ return (b[0] == '_') ? -1 : strcmp(a, b);
+
+ /* Now we know a is a pattern; if b is not, a comes first */
+ if (b[0] != '_')
+ return 1;
+#if 0 /* old mode for ext matching */
+ return strcmp(a, b);
+#endif
+ /* ok we need full pattern sorting routine */
+ while (!ret && a && b)
+ ret = ext_cmp1(&a) - ext_cmp1(&b);
+ if (ret == 0)
+ return 0;
+ else
+ return (ret > 0) ? 1 : -1;
+}
+
+/*! \brief copy a string skipping whitespace */
+static int ext_strncpy(char *dst, const char *src, int len)
+{
+ int count=0;
+
+ while (*src && (count < len - 1)) {
+ switch(*src) {
+ case ' ':
+ /* otherwise exten => [a-b],1,... doesn't work */
+ /* case '-': */
+ /* Ignore */
+ break;
+ default:
+ *dst = *src;
+ dst++;
+ }
+ src++;
+ count++;
+ }
+ *dst = '\0';
+
+ return count;
+}
+
+/*
+ * Wrapper around _extension_match_core() to do performance measurement
+ * using the profiling code.
+ */
+static int ast_check_timing(const struct ast_timing *i)
+{
+ struct tm tm;
+ time_t t = time(NULL);
+
+ localtime_r(&t,&tm);
+
+ /* If it's not the right month, return */
+ if (!(i->monthmask & (1 << tm.tm_mon)))
+ return 0;
+
+ /* If it's not that time of the month.... */
+ /* Warning, tm_mday has range 1..31! */
+ if (!(i->daymask & (1 << (tm.tm_mday-1))))
+ return 0;
+
+ /* If it's not the right day of the week */
+ if (!(i->dowmask & (1 << tm.tm_wday)))
+ return 0;
+
+ /* Sanity check the hour just to be safe */
+ if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
+ ast_log(LOG_WARNING, "Insane time...\n");
+ return 0;
+ }
+
+ /* Now the tough part, we calculate if it fits
+ in the right time based on min/hour */
+ if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
+ return 0;
+
+ /* If we got this far, then we're good */
+ return 1;
+}
+
+#ifdef NOT_ANYMORE
+static struct ast_switch *pbx_findswitch(const char *sw)
+{
+ struct ast_switch *asw;
+
+ AST_RWLIST_TRAVERSE(&switches, asw, list) {
+ if (!strcasecmp(asw->name, sw))
+ break;
+ }
+
+ return asw;
+}
+#endif
+
+
+static struct ast_context *ast_walk_contexts(struct ast_context *con);
+
+static struct ast_context *ast_walk_contexts(struct ast_context *con)
+{
+ return con ? con->next : contexts;
+}
+
+struct ast_context *localized_walk_contexts(struct ast_context *con);
+struct ast_context *localized_walk_contexts(struct ast_context *con)
+{
+ return ast_walk_contexts(con);
+}
+
+
+
+static struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten);
+
+static struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten)
+{
+ if (!exten)
+ return con ? con->root : NULL;
+ else
+ return exten->next;
+}
+
+struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten);
+struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten)
+{
+ return ast_walk_context_extensions(con,exten);
+}
+
+
+static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority);
+
+static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority)
+{
+ return priority ? priority->peer : exten;
+}
+
+struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority);
+struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority)
+{
+ return ast_walk_extension_priorities(exten, priority);
+}
+
+
+
+static struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc);
+
+static struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc)
+{
+ if (!inc)
+ return con ? con->includes : NULL;
+ else
+ return inc->next;
+}
+
+struct ast_include *localized_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc);
+struct ast_include *localized_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc)
+{
+ return ast_walk_context_includes(con, inc);
+}
+
+
+static struct ast_sw *ast_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw);
+
+static struct ast_sw *ast_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw)
+{
+ if (!sw)
+ return con ? AST_LIST_FIRST(&con->alts) : NULL;
+ else
+ return AST_LIST_NEXT(sw, list);
+}
+
+struct ast_sw *localized_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw);
+struct ast_sw *localized_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw)
+{
+ return ast_walk_context_switches(con, sw);
+}
+
+
+static struct ast_context *ast_context_find(const char *name);
+
+static struct ast_context *ast_context_find(const char *name)
+{
+ struct ast_context *tmp = NULL;
+ while ( (tmp = ast_walk_contexts(tmp)) ) {
+ if (!name || !strcasecmp(name, tmp->name))
+ break;
+ }
+ return tmp;
+}
+
+/* request and result for pbx_find_extension */
+struct pbx_find_info {
+#if 0
+ const char *context;
+ const char *exten;
+ int priority;
+#endif
+
+ char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
+ int stacklen; /* modified during the search */
+ int status; /* set on return */
+ struct ast_switch *swo; /* set on return */
+ const char *data; /* set on return */
+ const char *foundcontext; /* set on return */
+};
+
+/*
+ * Internal function for ast_extension_{match|close}
+ * return 0 on no-match, 1 on match, 2 on early match.
+ * mode is as follows:
+ * E_MATCH success only on exact match
+ * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
+ * E_CANMATCH either of the above.
+ */
+
+static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ mode &= E_MATCH_MASK; /* only consider the relevant bits */
+
+ if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
+ return 1;
+
+ if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
+ int ld = strlen(data), lp = strlen(pattern);
+
+ if (lp < ld) /* pattern too short, cannot match */
+ return 0;
+ /* depending on the mode, accept full or partial match or both */
+ if (mode == E_MATCH)
+ return !strcmp(pattern, data); /* 1 on match, 0 on fail */
+ if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
+ return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
+ else
+ return 0;
+ }
+ pattern++; /* skip leading _ */
+ /*
+ * XXX below we stop at '/' which is a separator for the CID info. However we should
+ * not store '/' in the pattern at all. When we insure it, we can remove the checks.
+ */
+ while (*data && *pattern && *pattern != '/') {
+ const char *end;
+
+ if (*data == '-') { /* skip '-' in data (just a separator) */
+ data++;
+ continue;
+ }
+ switch (toupper(*pattern)) {
+ case '[': /* a range */
+ end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0; /* unconditional failure */
+ }
+ for (pattern++; pattern != end; pattern++) {
+ if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
+ if (*data >= pattern[0] && *data <= pattern[2])
+ break; /* match found */
+ else {
+ pattern += 2; /* skip a total of 3 chars */
+ continue;
+ }
+ } else if (*data == pattern[0])
+ break; /* match found */
+ }
+ if (pattern == end)
+ return 0;
+ pattern = end; /* skip and continue */
+ break;
+ case 'N':
+ if (*data < '2' || *data > '9')
+ return 0;
+ break;
+ case 'X':
+ if (*data < '0' || *data > '9')
+ return 0;
+ break;
+ case 'Z':
+ if (*data < '1' || *data > '9')
+ return 0;
+ break;
+ case '.': /* Must match, even with more digits */
+ return 1;
+ case '!': /* Early match */
+ return 2;
+ case ' ':
+ case '-': /* Ignore these in patterns */
+ data--; /* compensate the final data++ */
+ break;
+ default:
+ if (*data != *pattern)
+ return 0;
+ }
+ data++;
+ pattern++;
+ }
+ if (*data) /* data longer than pattern, no match */
+ return 0;
+ /*
+ * match so far, but ran off the end of the data.
+ * Depending on what is next, determine match or not.
+ */
+ if (*pattern == '\0' || *pattern == '/') /* exact match */
+ return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
+ else if (*pattern == '!') /* early match */
+ return 2;
+ else /* partial match */
+ return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
+}
+
+static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ int i;
+ i = _extension_match_core(pattern, data, mode);
+ return i;
+}
+
+static int ast_extension_match(const char *pattern, const char *data);
+
+static int ast_extension_match(const char *pattern, const char *data)
+{
+ return extension_match_core(pattern, data, E_MATCH);
+}
+
+static int matchcid(const char *cidpattern, const char *callerid)
+{
+ /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
+ failing to get a number should count as a match, otherwise not */
+
+ if (ast_strlen_zero(callerid))
+ return ast_strlen_zero(cidpattern) ? 1 : 0;
+
+ return ast_extension_match(cidpattern, callerid);
+}
+
+static inline int include_valid(struct ast_include *i)
+{
+ if (!i->hastime)
+ return 1;
+
+ return ast_check_timing(&(i->timing));
+}
+
+
+
+static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+
+static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action)
+{
+ int x;
+ struct ast_context *tmp;
+ struct ast_exten *e, *eroot;
+ struct ast_include *i;
+
+ /* Initialize status if appropriate */
+ if (q->stacklen == 0) {
+ q->status = STATUS_NO_CONTEXT;
+ q->swo = NULL;
+ q->data = NULL;
+ q->foundcontext = NULL;
+ } else if (q->stacklen >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
+ return NULL;
+ }
+ /* Check first to see if we've already been checked */
+ for (x = 0; x < q->stacklen; x++) {
+ if (!strcasecmp(q->incstack[x], context))
+ return NULL;
+ }
+ if (bypass) /* bypass means we only look there */
+ tmp = bypass;
+ else { /* look in contexts */
+ tmp = NULL;
+ while ((tmp = ast_walk_contexts(tmp)) ) {
+ if (!strcmp(tmp->name, context))
+ break;
+ }
+ if (!tmp)
+ return NULL;
+ }
+ if (q->status < STATUS_NO_EXTENSION)
+ q->status = STATUS_NO_EXTENSION;
+
+ /* scan the list trying to match extension and CID */
+ eroot = NULL;
+ while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
+ int match = extension_match_core(eroot->exten, exten, action);
+ /* 0 on fail, 1 on match, 2 on earlymatch */
+
+ if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
+ continue; /* keep trying */
+ if (match == 2 && action == E_MATCHMORE) {
+ /* We match an extension ending in '!'.
+ * The decision in this case is final and is NULL (no match).
+ */
+ return NULL;
+ }
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
+ /* Match label or priority */
+ if (action == E_FINDLABEL) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ if (label && e->label && !strcmp(label, e->label))
+ break; /* found it */
+ } else if (e->priority == priority) {
+ break; /* found it */
+ } /* else keep searching */
+ }
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+ return e;
+ }
+ }
+#ifdef NOT_RIGHT_NOW
+ /* Check alternative switches??? */
+ AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
+ struct ast_switch *asw = pbx_findswitch(sw->name);
+ ast_switch_f *aswf = NULL;
+ char *datap;
+
+ if (!asw) {
+ ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
+ continue;
+ }
+ /* No need to Substitute variables now; we shouldn't be here if there's any */
+
+ /* equivalent of extension_match_core() at the switch level */
+ if (action == E_CANMATCH)
+ aswf = asw->canmatch;
+ else if (action == E_MATCHMORE)
+ aswf = asw->matchmore;
+ else /* action == E_MATCH */
+ aswf = asw->exists;
+ datap = sw->eval ? sw->tmpdata : sw->data;
+ res = !aswf ? 0 : aswf(chan, context, exten, priority, callerid, datap);
+ if (res) { /* Got a match */
+ q->swo = asw;
+ q->data = datap;
+ q->foundcontext = context;
+ /* XXX keep status = STATUS_NO_CONTEXT ? */
+ return NULL;
+ }
+ }
+#endif
+ q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
+ /* Now try any includes we have in this context */
+ for (i = tmp->includes; i; i = i->next) {
+ if (include_valid(i)) {
+ if ((e = pbx_find_extension(NULL, bypass, q, i->rname, exten, priority, label, callerid, action)))
+ return e;
+ if (q->swo)
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+struct ast_exten *localized_find_extension(struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action);
+
+struct ast_exten *localized_find_extension(struct ast_context *bypass,
+ struct pbx_find_info *q,
+ const char *context,
+ const char *exten,
+ int priority,
+ const char *label,
+ const char *callerid,
+ enum ext_match_t action)
+{
+ return pbx_find_extension(NULL, bypass, q, context, exten, priority, label, callerid, action);
+}
+
+
+static struct ast_context *contexts;
+AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
+
+static const char *ast_get_context_name(struct ast_context *con);
+
+static const char *ast_get_context_name(struct ast_context *con)
+{
+ return con ? con->name : NULL;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+static int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar);
+
+static int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+ struct ast_include *new_include;
+ char *c;
+ struct ast_include *i, *il = NULL; /* include, include_last */
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_include);
+ length += 2 * (strlen(value) + 1);
+
+ /* allocate new include structure ... */
+ if (!(new_include = ast_calloc(1, length)))
+ return -1;
+ /* Fill in this structure. Use 'p' for assignments, as the fields
+ * in the structure are 'const char *'
+ */
+ p = new_include->stuff;
+ new_include->name = p;
+ strcpy(p, value);
+ p += strlen(value) + 1;
+ new_include->rname = p;
+ strcpy(p, value);
+ /* Strip off timing info, and process if it is there */
+ if ( (c = strchr(p, '|')) ) {
+ *c++ = '\0';
+ new_include->hastime = ast_build_timing(&(new_include->timing), c);
+ }
+ new_include->next = NULL;
+ new_include->registrar = registrar;
+
+
+ /* ... go to last include and check if context is already included too... */
+ for (i = con->includes; i; i = i->next) {
+ if (!strcasecmp(i->name, new_include->name)) {
+ free(new_include);
+ errno = EEXIST;
+ return -1;
+ }
+ il = i;
+ }
+
+ /* ... include new context into context list, unlock, return */
+ if (il)
+ il->next = new_include;
+ else
+ con->includes = new_include;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
+
+ return 0;
+}
+
+int localized_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar);
+int localized_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+ return ast_context_add_include2(con, value, registrar);
+}
+
+
+
+static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
+
+static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
+ int length;
+ length = sizeof(struct ast_ignorepat);
+ length += strlen(value) + 1;
+ if (!(ignorepat = ast_calloc(1, length)))
+ return -1;
+ /* The cast to char * is because we need to write the initial value.
+ * The field is not supposed to be modified otherwise
+ */
+ strcpy((char *)ignorepat->pattern, value);
+ ignorepat->next = NULL;
+ ignorepat->registrar = registrar;
+ for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
+ ignorepatl = ignorepatc;
+ if (!strcasecmp(ignorepatc->pattern, value)) {
+ /* Already there */
+ errno = EEXIST;
+ return -1;
+ }
+ }
+ if (ignorepatl)
+ ignorepatl->next = ignorepat;
+ else
+ con->ignorepats = ignorepat;
+ return 0;
+
+}
+
+int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
+
+int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ return ast_context_add_ignorepat2(con, value, registrar);
+}
+
+
+/*
+ * Lock context list functions ...
+ */
+
+static int ast_wrlock_contexts(void)
+{
+ return ast_rwlock_wrlock(&conlock);
+}
+
+static int ast_unlock_contexts(void)
+{
+ return ast_rwlock_unlock(&conlock);
+}
+
+static int ast_wrlock_context(struct ast_context *con)
+{
+ return ast_rwlock_wrlock(&con->lock);
+}
+
+static int ast_unlock_context(struct ast_context *con)
+{
+ return ast_rwlock_unlock(&con->lock);
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+static int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar);
+
+static int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+ struct ast_sw *new_sw;
+ struct ast_sw *i;
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_sw);
+ length += strlen(value) + 1;
+ if (data)
+ length += strlen(data);
+ length++;
+ if (eval) {
+ /* Create buffer for evaluation of variables */
+ length += SWITCH_DATA_LENGTH;
+ length++;
+ }
+
+ /* allocate new sw structure ... */
+ if (!(new_sw = ast_calloc(1, length)))
+ return -1;
+ /* ... fill in this structure ... */
+ p = new_sw->stuff;
+ new_sw->name = p;
+ strcpy(new_sw->name, value);
+ p += strlen(value) + 1;
+ new_sw->data = p;
+ if (data) {
+ strcpy(new_sw->data, data);
+ p += strlen(data) + 1;
+ } else {
+ strcpy(new_sw->data, "");
+ p++;
+ }
+ if (eval)
+ new_sw->tmpdata = p;
+ new_sw->eval = eval;
+ new_sw->registrar = registrar;
+
+ /* ... go to last sw and check if context is already swd too... */
+ AST_LIST_TRAVERSE(&con->alts, i, list) {
+ if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
+ free(new_sw);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ /* ... sw new context into context list, unlock, return */
+ AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
+
+ return 0;
+}
+
+int localized_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar);
+
+int localized_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+ return ast_context_add_switch2(con, value, data, eval, registrar);
+}
+
+static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
+{
+ struct ast_context *tmp, **local_contexts;
+ int length = sizeof(struct ast_context) + strlen(name) + 1;
+
+ if (!extcontexts) {
+ ast_wrlock_contexts();
+ local_contexts = &contexts;
+ } else
+ local_contexts = extcontexts;
+
+ for (tmp = *local_contexts; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, name)) {
+ if (!existsokay) {
+ ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+ tmp = NULL;
+ }
+ if (!extcontexts)
+ ast_unlock_contexts();
+ return tmp;
+ }
+ }
+ if ((tmp = ast_calloc(1, length))) {
+ ast_rwlock_init(&tmp->lock);
+ ast_mutex_init(&tmp->macrolock);
+ strcpy(tmp->name, name);
+ tmp->root = NULL;
+ tmp->registrar = registrar;
+ tmp->next = *local_contexts;
+ tmp->includes = NULL;
+ tmp->ignorepats = NULL;
+ *local_contexts = tmp;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
+ }
+
+ if (!extcontexts)
+ ast_unlock_contexts();
+ return tmp;
+}
+
+/*! \brief
+ * Main interface to add extensions to the list for out context.
+ *
+ * We sort extensions in order of matching preference, so that we can
+ * stop the search as soon as we find a suitable match.
+ * This ordering also takes care of wildcards such as '.' (meaning
+ * "one or more of any character") and '!' (which is 'earlymatch',
+ * meaning "zero or more of any character" but also impacts the
+ * return value from CANMATCH and EARLYMATCH.
+ *
+ * The extension match rules defined in the devmeeting 2006.05.05 are
+ * quite simple: WE SELECT THE LONGEST MATCH.
+ * In detail, "longest" means the number of matched characters in
+ * the extension. In case of ties (e.g. _XXX and 333) in the length
+ * of a pattern, we give priority to entries with the smallest cardinality
+ * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
+ * while the latter has 7, etc.
+ * In case of same cardinality, the first element in the range counts.
+ * If we still have a tie, any final '!' will make this as a possibly
+ * less specific pattern.
+ *
+ * EBUSY - can't lock
+ * EEXIST - extension with the same priority exist and no replace is set
+ *
+ */
+static int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ /*
+ * Sort extensions (or patterns) according to the rules indicated above.
+ * These are implemented by the function ext_cmp()).
+ * All priorities for the same ext/pattern/cid are kept in a list,
+ * using the 'peer' field as a link field..
+ */
+ struct ast_exten *tmp, *e, *el = NULL;
+ int res;
+ int length;
+ char *p;
+
+ /* if we are adding a hint, and there are global variables, and the hint
+ contains variable references, then expand them --- NOT In this situation!!!
+ */
+
+ length = sizeof(struct ast_exten);
+ length += strlen(extension) + 1;
+ length += strlen(application) + 1;
+ if (label)
+ length += strlen(label) + 1;
+ if (callerid)
+ length += strlen(callerid) + 1;
+ else
+ length ++; /* just the '\0' */
+
+ /* Be optimistic: Build the extension structure first */
+ if (datad == NULL)
+ datad = null_datad;
+ if (!(tmp = ast_calloc(1, length)))
+ return -1;
+
+ /* use p as dst in assignments, as the fields are const char * */
+ p = tmp->stuff;
+ if (label) {
+ tmp->label = p;
+ strcpy(p, label);
+ p += strlen(label) + 1;
+ }
+ tmp->exten = p;
+ p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
+ tmp->priority = priority;
+ tmp->cidmatch = p; /* but use p for assignments below */
+ if (callerid) {
+ p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
+ tmp->matchcid = 1;
+ } else {
+ *p++ = '\0';
+ tmp->matchcid = 0;
+ }
+ tmp->app = p;
+ strcpy(p, application);
+ tmp->parent = con;
+ tmp->data = data;
+ tmp->datad = datad;
+ tmp->registrar = registrar;
+
+ res = 0; /* some compilers will think it is uninitialized otherwise */
+ for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
+ res = ext_cmp(e->exten, extension);
+ if (res == 0) { /* extension match, now look at cidmatch */
+ if (!e->matchcid && !tmp->matchcid)
+ res = 0;
+ else if (tmp->matchcid && !e->matchcid)
+ res = 1;
+ else if (e->matchcid && !tmp->matchcid)
+ res = -1;
+ else
+ res = strcasecmp(e->cidmatch, tmp->cidmatch);
+ }
+ if (res >= 0)
+ break;
+ }
+ if (e && res == 0) { /* exact match, insert in the pri chain */
+ res = add_pri(con, tmp, el, e, replace);
+ if (res < 0) {
+ errno = EEXIST; /* XXX do we care ? */
+ return 0; /* XXX should we return -1 maybe ? */
+ }
+ } else {
+ /*
+ * not an exact match, this is the first entry with this pattern,
+ * so insert in the main list right before 'e' (if any)
+ */
+ tmp->next = e;
+ if (el)
+ el->next = tmp;
+ else
+ con->root = tmp;
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ if (option_debug) {
+ if (tmp->matchcid) {
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+ if (option_verbose > 2) {
+ if (tmp->matchcid) {
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+ return 0;
+}
+
+int localized_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar);
+
+int localized_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+ return ast_add_extension2(con, replace, extension, priority, label, callerid, application, data, datad, registrar);
+}
+
+
+
+/*! \brief The return value depends on the action:
+ *
+ * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
+ * and return 0 on failure, -1 on match;
+ * E_FINDLABEL maps the label to a priority, and returns
+ * the priority on success, ... XXX
+ * E_SPAWN, spawn an application,
+ * and return 0 on success, -1 on failure.
+ */
+static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action)
+{
+ struct ast_exten *e;
+ int res;
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+
+ int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
+
+ e = pbx_find_extension(NULL, con, &q, context, exten, priority, label, callerid, action);
+ if (e) {
+ if (matching_action) {
+ return -1; /* success, we found it */
+ } else if (action == E_FINDLABEL) { /* map the label to a priority */
+ res = e->priority;
+ return res; /* the priority we were looking for */
+ } else { /* spawn */
+
+ /* NOT!!!!! */
+ return 0;
+ }
+ } else if (q.swo) { /* not found here, but in another switch */
+ if (matching_action)
+ return -1;
+ else {
+ if (!q.swo->exec) {
+ ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
+ res = -1;
+ }
+ return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
+ }
+ } else { /* not found anywhere, see what happened */
+ switch (q.status) {
+ case STATUS_NO_CONTEXT:
+ if (!matching_action)
+ ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
+ break;
+ case STATUS_NO_EXTENSION:
+ if (!matching_action)
+ ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
+ break;
+ case STATUS_NO_PRIORITY:
+ if (!matching_action)
+ ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
+ break;
+ case STATUS_NO_LABEL:
+ if (context)
+ ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
+ break;
+ default:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Shouldn't happen!\n");
+ }
+
+ return (matching_action) ? 0 : -1;
+ }
+}
+
+static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid);
+
+static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
+}
+
+static struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 1);
+}
+
+struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar);
+
+struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 0);
+}
+
+
+
+/* chopped this one off at the knees */
+static int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
+{
+ ast_log(LOG_ERROR, "Function %s not registered\n", function);
+ return -1;
+}
+
+/*! \brief extract offset:length from variable name.
+ * Returns 1 if there is a offset:length part, which is
+ * trimmed off (values go into variables)
+ */
+static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
+{
+ int parens=0;
+
+ *offset = 0;
+ *length = INT_MAX;
+ *isfunc = 0;
+ for (; *var; var++) {
+ if (*var == '(') {
+ (*isfunc)++;
+ parens++;
+ } else if (*var == ')') {
+ parens--;
+ } else if (*var == ':' && parens == 0) {
+ *var++ = '\0';
+ sscanf(var, "%d:%d", offset, length);
+ return 1; /* offset:length valid */
+ }
+ }
+ return 0;
+}
+
+static const char *ast_var_value(const struct ast_var_t *var)
+{
+ return (var ? var->value : NULL);
+}
+
+/*! \brief takes a substring. It is ok to call with value == workspace.
+ *
+ * offset < 0 means start from the end of the string and set the beginning
+ * to be that many characters back.
+ * length is the length of the substring. A value less than 0 means to leave
+ * that many off the end.
+ * Always return a copy in workspace.
+ */
+static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
+{
+ char *ret = workspace;
+ int lr; /* length of the input string after the copy */
+
+ ast_copy_string(workspace, value, workspace_len); /* always make a copy */
+
+ lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
+
+ /* Quick check if no need to do anything */
+ if (offset == 0 && length >= lr) /* take the whole string */
+ return ret;
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr)
+ return ret + lr; /* the final '\0' */
+
+ ret += offset; /* move to the start position */
+ if (length >= 0 && length < lr - offset) /* truncate if necessary */
+ ret[length] = '\0';
+ else if (length < 0) {
+ if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
+ ret[lr + length - offset] = '\0';
+ else
+ ret[0] = '\0';
+ }
+
+ return ret;
+}
+
+/*! \brief Support for Asterisk built-in variables in the dialplan
+\note See also
+ - \ref AstVar Channel variables
+ - \ref AstCauses The HANGUPCAUSE variable
+ */
+static void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
+{
+ const char not_found = '\0';
+ char *tmpvar;
+ const char *s; /* the result */
+ int offset, length;
+ int i, need_substring;
+ struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
+
+ /*
+ * Make a copy of var because parse_variable_name() modifies the string.
+ * Then if called directly, we might need to run substring() on the result;
+ * remember this for later in 'need_substring', 'offset' and 'length'
+ */
+ tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
+ need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
+
+ /*
+ * Look first into predefined variables, then into variable lists.
+ * Variable 's' points to the result, according to the following rules:
+ * s == &not_found (set at the beginning) means that we did not find a
+ * matching variable and need to look into more places.
+ * If s != &not_found, s is a valid result string as follows:
+ * s = NULL if the variable does not have a value;
+ * you typically do this when looking for an unset predefined variable.
+ * s = workspace if the result has been assembled there;
+ * typically done when the result is built e.g. with an snprintf(),
+ * so we don't need to do an additional copy.
+ * s != workspace in case we have a string, that needs to be copied
+ * (the ast_copy_string is done once for all at the end).
+ * Typically done when the result is already available in some string.
+ */
+ s = &not_found; /* default value */
+ if (s == &not_found) { /* look for more */
+ if (!strcmp(var, "EPOCH")) {
+ snprintf(workspace, workspacelen, "%u",(int)time(NULL));
+ }
+
+ s = workspace;
+ }
+ /* if not found, look into chanvars or global vars */
+ for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
+ struct ast_var_t *variables;
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (strcasecmp(ast_var_name(variables), var)==0) {
+ s = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ }
+ if (s == &not_found || s == NULL)
+ *ret = NULL;
+ else {
+ if (s != workspace)
+ ast_copy_string(workspace, s, workspacelen);
+ *ret = workspace;
+ if (need_substring)
+ *ret = substring(*ret, offset, length, workspace, workspacelen);
+ }
+}
+
+static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
+ zero-filled */
+ char *cp4;
+ const char *tmp, *whereweare;
+ int length, offset, offset2, isfunction;
+ char *workspace = NULL;
+ char *ltmp = NULL, *var = NULL;
+ char *nextvar, *nextexp, *nextthing;
+ char *vars, *vare;
+ int pos, brackets, needsub, len;
+
+ *cp2 = 0; /* just in case there's nothing to do */
+ whereweare=tmp=cp1;
+ while (!ast_strlen_zero(whereweare) && count) {
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch (nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ }
+ }
+
+ if (pos) {
+ /* Can't copy more than 'count' bytes */
+ if (pos > count)
+ pos = count;
+
+ /* Copy that many bytes */
+ memcpy(cp2, whereweare, pos);
+
+ count -= pos;
+ cp2 += pos;
+ whereweare += pos;
+ *cp2 = 0;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ } else if (vare[0] == '{') {
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing '}' in '%s')\n", cp1);
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ memset(ltmp, 0, VAR_BUF_SIZE);
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ if (!workspace)
+ workspace = alloca(VAR_BUF_SIZE);
+
+ workspace[0] = '\0';
+
+ parse_variable_name(vars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+ }
+ if (cp4) {
+ cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
+
+ length = strlen(cp4);
+ if (length > count)
+ length = count;
+ memcpy(cp2, cp4, length);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ memset(ltmp, 0, VAR_BUF_SIZE);
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ length = ast_expr(vars, cp2, count, NULL);
+
+ if (length) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ } else
+ break;
+ }
+}
+
+static void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(c, NULL, cp1, cp2, count);
+}
+
+
+static int pbx_load_config(const char *config_file);
+
+static int pbx_load_config(const char *config_file)
+{
+ struct ast_config *cfg;
+ char *end;
+ char *label;
+ char realvalue[256];
+ int lastpri = -2;
+ struct ast_context *con;
+ struct ast_variable *v;
+ const char *cxt;
+ const char *aft;
+
+ cfg = localized_config_load(config_file);
+ if (!cfg)
+ return 0;
+
+ /* Use existing config to populate the PBX table */
+ static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
+ write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
+ if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
+ autofallthrough_config = ast_true(aft);
+ clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
+ ast_set2_flag(&ast_options, ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING);
+
+ if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext")))
+ ast_copy_string(userscontext, cxt, sizeof(userscontext));
+ else
+ ast_copy_string(userscontext, "default", sizeof(userscontext));
+
+ for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
+ memset(realvalue, 0, sizeof(realvalue));
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ pbx_builtin_setvar_helper(NULL, v->name, realvalue);
+ }
+ for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
+ /* All categories but "general" or "globals" are considered contexts */
+ if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
+ continue;
+ con=ast_context_find_or_create(&local_contexts,cxt, registrar);
+ if (con == NULL)
+ continue;
+
+ for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
+ if (!strcasecmp(v->name, "exten")) {
+ char *tc = ast_strdup(v->value);
+ if (tc) {
+ int ipri = -2;
+ char realext[256]="";
+ char *plus, *firstp, *firstc;
+ char *pri, *appl, *data, *cidmatch;
+ char *stringp = tc;
+ char *ext = strsep(&stringp, ",");
+ if (!ext)
+ ext="";
+ pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
+ cidmatch = strchr(realext, '/');
+ if (cidmatch) {
+ *cidmatch++ = '\0';
+ ast_shrink_phone_number(cidmatch);
+ }
+ pri = strsep(&stringp, ",");
+ if (!pri)
+ pri="";
+ label = strchr(pri, '(');
+ if (label) {
+ *label++ = '\0';
+ end = strchr(label, ')');
+ if (end)
+ *end = '\0';
+ else
+ ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
+ }
+ plus = strchr(pri, '+');
+ if (plus)
+ *plus++ = '\0';
+ if (!strcmp(pri,"hint"))
+ ipri=PRIORITY_HINT;
+ else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
+ if (lastpri > -2)
+ ipri = lastpri + 1;
+ else
+ ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
+ } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
+ if (lastpri > -2)
+ ipri = lastpri;
+ else
+ ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
+ } else if (sscanf(pri, "%d", &ipri) != 1 &&
+ (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
+ ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
+ ipri = 0;
+ }
+ appl = S_OR(stringp, "");
+ /* Find the first occurrence of either '(' or ',' */
+ firstc = strchr(appl, ',');
+ firstp = strchr(appl, '(');
+ if (firstc && (!firstp || firstc < firstp)) {
+ /* comma found, no parenthesis */
+ /* or both found, but comma found first */
+ appl = strsep(&stringp, ",");
+ data = stringp;
+ } else if (!firstc && !firstp) {
+ /* Neither found */
+ data = "";
+ } else {
+ /* Final remaining case is parenthesis found first */
+ appl = strsep(&stringp, "(");
+ data = stringp;
+ end = strrchr(data, ')');
+ if ((end = strrchr(data, ')'))) {
+ *end = '\0';
+ } else {
+ ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
+ }
+ ast_process_quotes_and_slashes(data, ',', '|');
+ }
+
+ if (!data)
+ data="";
+ appl = ast_skip_blanks(appl);
+ if (ipri) {
+ if (plus)
+ ipri += atoi(plus);
+ lastpri = ipri;
+ if (!ast_opt_dont_warn && !strcmp(realext, "_."))
+ ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v->lineno);
+ if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) {
+ ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
+ }
+ }
+ free(tc);
+ }
+ } else if (!strcasecmp(v->name, "include")) {
+ memset(realvalue, 0, sizeof(realvalue));
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ if (ast_context_add_include2(con, realvalue, registrar))
+ ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
+ } else if (!strcasecmp(v->name, "ignorepat")) {
+ memset(realvalue, 0, sizeof(realvalue));
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ if (ast_context_add_ignorepat2(con, realvalue, registrar))
+ ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
+ } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
+ char *stringp= realvalue;
+ char *appl, *data;
+
+ memset(realvalue, 0, sizeof(realvalue));
+ if (!strcasecmp(v->name, "switch"))
+ pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
+ else
+ ast_copy_string(realvalue, v->value, sizeof(realvalue));
+ appl = strsep(&stringp, "/");
+ data = strsep(&stringp, ""); /* XXX what for ? */
+ if (!data)
+ data = "";
+ if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
+ ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
+ } else {
+ ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+ return 1;
+}
+
+static void __ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ struct ast_context *tmp, *tmpl=NULL;
+ struct ast_include *tmpi;
+ struct ast_sw *sw;
+ struct ast_exten *e, *el, *en;
+ struct ast_ignorepat *ipi;
+
+ for (tmp = contexts; tmp; ) {
+ struct ast_context *next; /* next starting point */
+ for (; tmp; tmpl = tmp, tmp = tmp->next) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
+ if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
+ (!con || !strcasecmp(tmp->name, con->name)) )
+ break; /* found it */
+ }
+ if (!tmp) /* not found, we are done */
+ break;
+ ast_wrlock_context(tmp);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
+ next = tmp->next;
+ if (tmpl)
+ tmpl->next = next;
+ else
+ contexts = next;
+ /* Okay, now we're safe to let it go -- in a sense, we were
+ ready to let it go as soon as we locked it. */
+ ast_unlock_context(tmp);
+ for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
+ struct ast_include *tmpil = tmpi;
+ tmpi = tmpi->next;
+ free(tmpil);
+ }
+ for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
+ struct ast_ignorepat *ipl = ipi;
+ ipi = ipi->next;
+ free(ipl);
+ }
+ while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
+ free(sw);
+ for (e = tmp->root; e;) {
+ for (en = e->peer; en;) {
+ el = en;
+ en = en->peer;
+ destroy_exten(el);
+ }
+ el = e;
+ e = e->next;
+ destroy_exten(el);
+ }
+ ast_rwlock_destroy(&tmp->lock);
+ free(tmp);
+ /* if we have a specific match, we are done, otherwise continue */
+ tmp = con ? NULL : next;
+ }
+}
+
+void localized_context_destroy(struct ast_context *con, const char *registrar);
+
+void localized_context_destroy(struct ast_context *con, const char *registrar)
+{
+ ast_wrlock_contexts();
+ __ast_context_destroy(con,registrar);
+ ast_unlock_contexts();
+}
+
+
+static void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ struct ast_context *tmp, *lasttmp = NULL;
+
+ /* it is very important that this function hold the hint list lock _and_ the conlock
+ during its operation; not only do we need to ensure that the list of contexts
+ and extensions does not change, but also that no hint callbacks (watchers) are
+ added or removed during the merge/delete process
+
+ in addition, the locks _must_ be taken in this order, because there are already
+ other code paths that use this order
+ */
+ ast_wrlock_contexts();
+
+ tmp = *extcontexts;
+ if (registrar) {
+ /* XXX remove previous contexts from same registrar */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
+ __ast_context_destroy(NULL,registrar);
+ while (tmp) {
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ } else {
+ /* XXX remove contexts with the same name */
+ while (tmp) {
+ ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
+ __ast_context_destroy(tmp,tmp->registrar);
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ }
+ if (lasttmp) {
+ lasttmp->next = contexts;
+ contexts = *extcontexts;
+ *extcontexts = NULL;
+ } else
+ ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
+
+ ast_unlock_contexts();
+
+ return;
+}
+
+void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar);
+
+void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ ast_merge_contexts_and_delete(extcontexts, registrar);
+}
+
+static int ast_context_verify_includes(struct ast_context *con)
+{
+ struct ast_include *inc = NULL;
+ int res = 0;
+
+ while ( (inc = ast_walk_context_includes(con, inc)) )
+ if (!ast_context_find(inc->rname)) {
+ res = -1;
+ if (strcasecmp(inc->rname,"parkedcalls")!=0)
+ ast_log(LOG_WARNING, "Context '%s' tries to include the nonexistent context '%s'\n",
+ ast_get_context_name(con), inc->rname);
+ }
+ return res;
+}
+
+int localized_context_verify_includes(struct ast_context *con);
+
+int localized_context_verify_includes(struct ast_context *con)
+{
+ return ast_context_verify_includes(con);
+}
+
+int localized_pbx_load_module(void);
+
+int localized_pbx_load_module(void)
+{
+ struct ast_context *con;
+
+ if(!pbx_load_config(config))
+ return -1 /* AST_MODULE_LOAD_DECLINE*/;
+
+ /* pbx_load_users(); */ /* does this affect the dialplan? */
+
+ ast_merge_contexts_and_delete(&local_contexts, registrar);
+
+ for (con = NULL; (con = ast_walk_contexts(con));)
+ ast_context_verify_includes(con);
+
+ printf("=== Loading extensions.conf ===\n");
+ con = 0;
+ while ((con = ast_walk_contexts(con)) ) {
+ printf("Context: %s\n", con->name);
+ }
+ printf("=========\n");
+
+ return 0;
+}
+
diff --git a/trunk/utils/frame.c b/trunk/utils/frame.c
new file mode 100644
index 000000000..7fdb1637d
--- /dev/null
+++ b/trunk/utils/frame.c
@@ -0,0 +1,1034 @@
+/****************************************************************************
+ *
+ * Programs for processing sound files in raw- or WAV-format.
+ * -- Useful functions for parsing command line options and
+ * issuing errors, warnings, and chit chat.
+ *
+ * Name: frame.c
+ * Version: see static char *standardversion, below.
+ * Author: Mark Roberts <mark@manumark.de>
+ * Michael Labuschke <michael@labuschke.de> sys_errlist fixes
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * These are useful functions that all DSP programs might find handy
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h> /* for exit and malloc */
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+#include "frame.h"
+
+time_t stopwatch; /* will hold time at start of calculation */
+int samplefrequency;
+unsigned short samplewidth;
+unsigned short channels;
+int wavout; /* TRUE iff out file should be a .WAV file */
+int iswav; /* TRUE iff in file was found to be a .WAV file */
+FILE *in, *out;
+char *infilename, *outfilename;
+int verboselevel;
+char *version = "";
+char *usage = "";
+static int test_usage;
+
+static char *standardversion = "frame version 1.3, June 13th 2001";
+static char *standardusage =
+"\nOptions common to all mark-dsp programs:\n"
+
+"-h \t\t create a WAV-header on output files.\n"
+"-c#\t\t set number of channels to # (1 or 2). Default: like input.\n"
+"-w#\t\t set number of bits per sample (width) to # (only 16)\n"
+"-f#\t\t set sample frequency to #. Default: like input.\n"
+"-V \t\t verbose: talk a lot.\n"
+"-Q \t\t quiet: talk as little as possible.\n\n"
+"In most cases, a filename of '-' means stdin or stdout.\n\n"
+"Bug-reports: mark@manumark.de\n"
+;
+
+/* -----------------------------------------------------------------------
+ Writes the number of samples to result that are yet to be read from anyin.
+ Return values are TRUE on success, FALSE on failure.
+ -----------------------------------------------------------------------*/
+int getremainingfilelength( FILE *anyin, long *result)
+{
+ long i;
+
+ i = ftell (anyin);
+ if (i == -1) return FALSE;
+ if (fseek (anyin, 0, SEEK_END) == -1) return FALSE;
+ *result = ftell (anyin);
+ if (*result == -1) return FALSE;
+ (*result) -= i;
+ (*result) /= samplewidth;
+ if (fseek (anyin, i, SEEK_SET) == -1) return FALSE;
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------
+ Read a .pk-header from 'anyin'.
+ -----------------------------------------------------------------------*/
+void readpkheader( FILE *anyin)
+{
+ unsigned short tempushort;
+ int tempint, i, x;
+ unsigned char blood[8];
+
+ for (i = 0; i < 11; i++)
+ {
+ fread( &tempint, 4, 1, anyin);
+ printf( "%d: %d, ", i, tempint);
+ }
+ printf( "\n");
+ fread( blood, 1, 8, anyin);
+ for (i = 0; i < 8; i++)
+ printf( "%d ", blood[i]);
+ printf( "\n");
+ for (i = 0; i < 8; i++)
+ {
+ for (x = 128; x > 0; x /= 2)
+ printf((blood[i] & x) == 0? "0 ":"1 ");
+ printf(i%4==3? "\n":"| ");
+ }
+ printf( "\n");
+ for (i = 0; i < 2; i++)
+ {
+ fread( &tempint, 4, 1, anyin);
+ printf( "%d: %d, ", i, tempint);
+ }
+ printf( "\n");
+ for (i = 0; i < 2; i++)
+ {
+ fread( &tempushort, 2, 1, anyin);
+ printf( "%d: %d, ", i, tempushort);
+ }
+ printf( "\n");
+}
+
+
+
+/* -----------------------------------------------------------------------
+ Read a .WAV header from 'anyin'. See header for details.
+ -----------------------------------------------------------------------*/
+void readwavheader( FILE *anyin)
+{
+ unsigned int tempuint, sf;
+ unsigned short tempushort, cn;
+ char str[9];
+ int nowav = FALSE;
+
+ iswav = FALSE;
+
+ if (ftell(anyin) == -1) /* If we cannot seek this file */
+ {
+ nowav = TRUE; /* -> Pretend this is no wav-file */
+ chat("File not seekable: not checking for WAV-header.\n");
+ }
+ else
+ {
+ /* Expect four bytes "RIFF" and four bytes filelength */
+ fread (str, 1, 8, anyin); /* 0 */
+ str[4] = '\0';
+ if (strcmp(str, "RIFF") != 0) nowav = TRUE;
+ /* Expect eight bytes "WAVEfmt " */
+ fread (str, 1, 8, anyin); /* 8 */
+ str[8] = '\0';
+ if (strcmp(str, "WAVEfmt ") != 0) nowav = TRUE;
+ /* Expect length of fmt data, which should be 16 */
+ fread (&tempuint, 4, 1, anyin); /* 16 */
+ if (tempuint != 16) nowav = TRUE;
+ /* Expect format tag, which should be 1 for pcm */
+ fread (&tempushort, 2, 1, anyin); /* 20 */
+ if (tempushort != 1)
+ nowav = TRUE;
+ /* Expect number of channels */
+ fread (&cn, 2, 1, anyin); /* 20 */
+ if (cn != 1 && cn != 2) nowav = TRUE;
+ /* Read samplefrequency */
+ fread (&sf, 4, 1, anyin); /* 24 */
+ /* Read bytes per second: Should be samplefreq * channels * 2 */
+ fread (&tempuint, 4, 1, anyin); /* 28 */
+ if (tempuint != sf * cn * 2) nowav = TRUE;
+ /* read bytes per frame: Should be channels * 2 */
+ fread (&tempushort, 2, 1, anyin); /* 32 */
+ if (tempushort != cn * 2) nowav = TRUE;
+ /* Read bits per sample: Should be 16 */
+ fread (&tempushort, 2, 1, anyin); /* 34 */
+ if (tempushort != 16) nowav = TRUE;
+ fread (str, 4, 1, anyin); /* 36 */
+ str[4] = '\0';
+ if (strcmp(str, "data") != 0) nowav = TRUE;
+ fread (&tempuint, 4, 1, anyin); /* 40 */
+ if (nowav)
+ {
+ fseek (anyin, 0, SEEK_SET); /* Back to beginning of file */
+ chat("File has no WAV header.\n");
+ }
+ else
+ {
+ samplefrequency = sf;
+ channels = cn;
+ chat ("Read WAV header: %d channels, samplefrequency %d.\n",
+ channels, samplefrequency);
+ iswav = TRUE;
+ }
+ }
+ return;
+}
+
+
+
+/* -----------------------------------------------------------------------
+ Write a .WAV header to 'out'. See header for details.
+ -----------------------------------------------------------------------*/
+void makewavheader( void)
+{
+ unsigned int tempuint, filelength;
+ unsigned short tempushort;
+
+ /* If fseek fails, don't create the header. */
+ if (fseek (out, 0, SEEK_END) != -1)
+ {
+ filelength = ftell (out);
+ chat ("filelength %d, ", filelength);
+ fseek (out, 0, SEEK_SET);
+ fwrite ("RIFF", 1, 4, out); /* 0 */
+ tempuint = filelength - 8; fwrite (&tempuint, 4, 1, out); /* 4 */
+ fwrite ("WAVEfmt ", 1, 8, out); /* 8 */
+ /* length of fmt data 16 bytes */
+ tempuint = 16;
+ fwrite (&tempuint, 4, 1, out); /* 16 */
+ /* Format tag: 1 for pcm */
+ tempushort = 1;
+ fwrite (&tempushort, 2, 1, out); /* 20 */
+ chat ("%d channels\n", channels);
+ fwrite (&channels, 2, 1, out);
+ chat ("samplefrequency %d\n", samplefrequency);
+ fwrite (&samplefrequency, 4, 1, out); /* 24 */
+ /* Bytes per second */
+ tempuint = channels * samplefrequency * 2;
+ fwrite (&tempuint, 4, 1, out); /* 28 */
+ /* Block align */
+ tempushort = 2 * channels;
+ fwrite (&tempushort, 2, 1, out); /* 32 */
+ /* Bits per sample */
+ tempushort = 16;
+ fwrite (&tempushort, 2, 1, out); /* 34 */
+ fwrite ("data", 4, 1, out); /* 36 */
+ tempuint = filelength - 44; fwrite (&tempuint, 4, 1, out); /* 40 */
+ }
+ return;
+}
+
+/* -----------------------------------------------------------------------
+ After all is read and done, inform the inclined user of the elapsed time
+ -----------------------------------------------------------------------*/
+static void statistics( void)
+{
+ int temp;
+
+ temp = time(NULL) - stopwatch;
+ if (temp != 1)
+ {
+ inform ("\nTime: %d seconds\n", temp);
+ }
+ else
+ {
+ inform ("\nTime: 1 second\n");
+ }
+ return;
+}
+
+
+/* -----------------------------------------------------------------------
+ Start the stopwatch and make sure the user is informed at end of program.
+ -----------------------------------------------------------------------*/
+void startstopwatch(void)
+{
+ stopwatch = time(NULL); /* Remember time 'now' */
+ atexit(statistics); /* Call function statistics() at exit. */
+
+ return;
+}
+
+/* --------------------------------------------------------------------
+ Tests the character 'coal' for being a command line option character,
+ momentarrily '-'.
+ -------------------------------------------------------------------- */
+int isoptionchar (char coal)
+{
+ return (coal =='-');
+}
+
+/* -----------------------------------------------------------------------
+ Reads through the arguments on the lookout for an option starting
+ with 'string'. The rest of the option is read as a time and passed
+ to *result, where the result is meant to mean 'number of samples' in
+ that time.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -----------------------------------------------------------------------*/
+int parsetimearg( int argcount, char *args[], char *string, int *result)
+{
+ int i;
+
+ if ((i = findoption( argcount, args, string)) > 0)
+ {
+ if (parsetime(args[i] + 1 + strlen( string), result))
+ return TRUE;
+ argerrornum(args[i]+1, ME_NOTIME);
+ }
+ return FALSE;
+}
+
+/* -----------------------------------------------------------------------
+ The string argument is read as a time and passed
+ to *result, where the result is meant to mean 'number of samples' in
+ that time.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -----------------------------------------------------------------------*/
+int parsetime(char *string, int *result)
+{
+ int k;
+ double temp;
+ char m, s, end;
+
+ k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end);
+ switch (k)
+ {
+ case 0: case EOF: case 4:
+ return FALSE;
+ case 1:
+ *result = temp;
+ break;
+ case 2:
+ if (m == 's')
+ *result = temp * samplefrequency;
+ else
+ return FALSE;
+ break;
+ case 3:
+ if (m == 'm' && s == 's')
+ *result = temp * samplefrequency / 1000;
+ else if (m == 'H' && s == 'z')
+ *result = samplefrequency / temp;
+ else
+ return FALSE;
+ break;
+ default:
+ argerrornum(NULL, ME_THISCANTHAPPEN);
+ }
+ return TRUE;
+}
+
+/* -----------------------------------------------------------------------
+ The string argument is read as a frequency and passed
+ to *result, where the result is meant to mean 'number of samples' in
+ one cycle of that frequency.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -----------------------------------------------------------------------*/
+int parsefreq(char *string, double *result)
+{
+ int k;
+ double temp;
+ char m, s, end;
+
+ k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end);
+ switch (k)
+ {
+ case 0: case EOF: case 2: case 4:
+ return FALSE;
+ case 1:
+ *result = temp;
+ break;
+ case 3:
+ if (m == 'H' && s == 'z')
+ *result = samplefrequency / temp;
+ else
+ return FALSE;
+ break;
+ default:
+ argerrornum(NULL, ME_THISCANTHAPPEN);
+ }
+ return TRUE;
+}
+
+char *parsefilearg( int argcount, char *args[])
+{
+ int i;
+ char *result = NULL;
+
+ for (i = 1; i < argcount; i++)
+ {
+ if (args[i][0] != '\0' &&
+ (!isoptionchar (args[i][0]) || args[i][1] == '\0' ))
+ {
+ /*---------------------------------------------*
+ * The argument is a filename: *
+ * it is either no dash followed by something, *
+ * or it is a dash following by nothing. *
+ *---------------------------------------------*/
+ result = malloc( strlen( args[i]) + 1);
+ if (result == NULL)
+ fatalperror( "Couldn't allocate memory for filename\n");
+ strcpy( result, args[i]);
+ args[i][0] = '\0'; /* Mark as used up */
+ break;
+ }
+ }
+ return result;
+}
+
+int parseswitch( char *found, char *wanted)
+{
+ if (strncmp( found, wanted, strlen( wanted)) == 0)
+ {
+ if (found[strlen( wanted)] == '\0')
+ return TRUE;
+ else
+ argerrornum( found, ME_NOSWITCH);
+ }
+ return FALSE;
+}
+
+int parseswitcharg( int argcount, char *args[], char *string)
+{
+ int i;
+
+ if ((i = findoption( argcount, args, string)) > 0)
+ {
+ if (args[i][strlen( string) + 1] == '\0')
+ return TRUE;
+ else
+ argerrornum( args[i] + 1, ME_NOSWITCH);
+ }
+ return FALSE;
+}
+
+int parseintarg( int argcount, char *args[], char *string, int *result)
+{
+ int i, temp;
+ char c;
+
+ if ((i = findoption( argcount, args, string)) > 0)
+ {
+ switch (sscanf(args[i] + 1 + strlen( string),
+ "%d%c", &temp, &c))
+ {
+ case 0: case EOF: case 2:
+ argerrornum(args[i]+1, ME_NOINT);
+ return FALSE;
+ case 1:
+ *result = temp;
+ break;
+ default:
+ say("frame.c: This can't happen\n");
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for an option starting
+ with 'string'. The rest of the option is read as a double and
+ passed to *result.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -------------------------------------------------------------------- */
+int parsedoublearg( int argcount, char *args[], char *string, double *result)
+{
+ int i;
+ double temp;
+ char end;
+
+ if ((i = findoption( argcount, args, string)) > 0)
+ {
+ switch (sscanf(args[i] + 1 + strlen( string), "%lf%c", &temp, &end))
+ {
+ case 0: case EOF: case 2:
+ argerrornum(args[i]+1, ME_NODOUBLE);
+ return FALSE;
+ case 1:
+ *result = temp;
+ break;
+ default:
+ say("frame.c: This can't happen\n");
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for an option starting
+ with 'string'. The rest of the option is read as a volume, i.e.
+ absolute, percent or db. The result is passed to *result.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -------------------------------------------------------------------- */
+int parsevolarg( int argcount, char *args[], char *string, double *result)
+{
+ double vol = 1.0;
+ char sbd, sbb, end;
+ int i, weird = FALSE;
+
+ if ((i = findoption( argcount, args, string)) > 0)
+ {
+ switch (sscanf(args[i] + 1 + strlen( string),
+ "%lf%c%c%c", &vol, &sbd, &sbb, &end))
+ {
+ case 0: case EOF: case 4:
+ weird = TRUE;
+ break; /* No number: error */
+ case 1:
+ *result = vol;
+ break;
+ case 2:
+ if (sbd == '%')
+ *result = vol / 100;
+ else
+ weird = TRUE; /* One char but no percent: error */
+ break;
+ case 3:
+ if (sbd =='d' && sbb == 'b')
+ *result = pow(2, vol / 6.02);
+ else
+ weird = TRUE; /* Two chars but not db: error */
+ break;
+ default:
+ say("frame.c: This can't happen.\n");
+ }
+ if (weird)
+ argerrornum( args[i] + 1, ME_NOVOL);
+ /* ("Weird option: couldn't parse volume '%s'\n", args[i]+2); */
+ return !weird;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+/* --------------------------------------------------------------------
+ Reads the specified string 's' and interprets it as a volume. The string
+ would be of the form 1.8 or 180% or 5db.
+ On success, the return value TRUE and *result is given result
+ (i.e. the relative volume, i.e. 1.8). On failure, FALSE is returned and
+ result is given value 1.0.
+ -------------------------------------------------------------------- */
+int parsevolume(char *s, double *result)
+{
+ int k;
+ char sbd, sbb, end;
+
+ *result = 1.0;
+ k = sscanf(s, "%lf%c%c%c", result, &sbd, &sbb, &end);
+ switch (k)
+ {
+ case 0:
+ case EOF:
+ case 4:
+ return FALSE;
+ case 1:
+ break;
+ case 2:
+ if (sbd != '%')
+ return FALSE;
+ (*result) /=100;
+ break;
+ case 3:
+ if (sbd !='d' || sbb != 'b')
+ return FALSE;
+ (*result) = pow(2, (*result) / 6.02);
+ break;
+ default:
+ say("parsevolume: This can't happen (%d).\n", k);
+ }
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------
+ Reports an error due to parsing the string 's' encountered on the
+ command line.
+ -------------------------------------------------------------------- */
+void argerror(char *s)
+{
+ error ("Error parsing command line. Unrecognized option:\n\t-%s\n", s);
+ fatalerror("\nTry --help for help.\n");
+}
+
+/* --------------------------------------------------------------------
+ Reports an error due to parsing the string 's' encountered on the
+ command line. 'code' indicates the type of error.
+ -------------------------------------------------------------------- */
+void argerrornum(char *s, Errornum code)
+{
+ char *message;
+
+ if (code == ME_TOOMANYFILES)
+ {
+ error("Too many files on command line: '%s'.\n", s);
+ }
+ else
+ {
+ if (s != NULL)
+ error ("Error parsing option -%s:\n\t", s);
+ switch( code)
+ {
+ case ME_NOINT:
+ message = "Integer expected";
+ break;
+ case ME_NODOUBLE:
+ message = "Floating point number expected";
+ break;
+ case ME_NOTIME:
+ message = "Time argument expected";
+ break;
+ case ME_NOVOL:
+ message = "Volume argument expected";
+ break;
+ case ME_NOSWITCH:
+ message = "Garbage after switch-type option";
+ break;
+ case ME_HEADERONTEXTFILE:
+ message = "Option -h is not useful for text-output";
+ break;
+ case ME_NOINFILE:
+ message = "No input file specified";
+ break;
+ case ME_NOOUTFILE:
+ message = "No output file specified";
+ break;
+ case ME_NOIOFILE:
+ message = "No input/output file specified";
+ break;
+ case ME_NOSTDIN:
+ message = "Standard in not supported here";
+ break;
+ case ME_NOSTDOUT:
+ message = "Standard out not supported here";
+ break;
+ case ME_NOSTDIO:
+ message = "Standard in/out not supported here";
+ break;
+ case ME_NOTENOUGHFILES:
+ message = "Not enough files specified";
+ break;
+ case ME_THISCANTHAPPEN:
+ fatalerror("\nThis can't happen. Report this as a bug\n");
+ /* fatalerror does not return */
+ default:
+ error("Error code %d not implemented. Fix me!\n", code);
+ message = "Error message not implemented. Fix me!";
+ }
+ error("%s\n", message);
+ }
+ fatalerror("\nTry --help for help.\n");
+}
+
+/* --------------------------------------------------------------------
+ Reports an error due to parsing the string 's' encountered on the
+ command line. 'message' explains the type of error.
+ -------------------------------------------------------------------- */
+void argerrortxt(char *s, char *message)
+{
+ if (s != NULL)
+ error ("Error parsing option -%s:\n\t", s);
+ else
+ error ("Error parsing command line:\n\t");
+ error ("%s\n", message);
+ fatalerror("\nTry --help for help.\n");
+}
+
+/* --------------------------------------------------------------------
+ Check for any remaining arguments and complain about their existence
+ -------------------------------------------------------------------- */
+void checknoargs( int argcount, char *args[])
+{
+ int i, errorcount = 0;
+
+ for (i = 1; i < argcount; i++)
+ {
+ if (args[i][0] != '\0') /* An unused argument! */
+ {
+ errorcount++;
+ if (errorcount == 1)
+ error("The following arguments were not recognized:\n");
+ error("\t%s\n", args[i]);
+ }
+ }
+ if (errorcount > 0) /* Errors are fatal */
+ fatalerror("\nTry --help for help.\n");
+
+ return; /* No errors? Return. */
+}
+
+/* --------------------------------------------------------------------
+ Parses the command line arguments as represented by the function
+ arguments. Sets the global variables 'in', 'out', 'samplefrequency'
+ and 'samplewidth' accordingly. Also verboselevel.
+ The files 'in' and 'out' are even opened according to 'fileswitch'.
+ See headerfile for details
+ -------------------------------------------------------------------- */
+void parseargs( int argcount, char *args[], int fileswitch)
+{
+ char *filename;
+ int tempint;
+
+ if ((fileswitch & 1) != 0) /* If getting infile */
+ in = NULL;
+ if ((fileswitch & 4) != 0) /* If getting outfile */
+ out = NULL;
+ wavout = FALSE;
+ verboselevel = 5;
+ samplefrequency = DEFAULTFREQ;
+ samplewidth = 2;
+ channels = 1;
+
+ /*-----------------------------------------------*
+ * First first check testcase, usage and version *
+ *-----------------------------------------------*/
+ test_usage = parseswitcharg( argcount, args, "-test-usage");
+ if (parseswitcharg( argcount, args, "-help"))
+ {
+ printf("%s%s", usage, standardusage);
+ exit(0);
+ }
+ if (parseswitcharg( argcount, args, "-version"))
+ {
+ printf("%s\n(%s)\n", version, standardversion);
+ exit(0);
+ }
+ /*--------------------------------------*
+ * Set verboselevel *
+ *--------------------------------------*/
+ while (parseswitcharg( argcount, args, "V"))
+ verboselevel = 10;
+ while (parseswitcharg( argcount, args, "Q"))
+ verboselevel = 1;
+ /*-------------------------------------------------*
+ * Get filenames and open files *
+ *-------------------------------------------------*/
+ if ((fileswitch & 1) != 0) /* Infile wanted */
+ {
+ infilename = parsefilearg( argcount, args);
+ if (infilename == NULL)
+ argerrornum( NULL, ME_NOINFILE);
+ if (strcmp( infilename, "-") == 0)
+ {
+ infilename = "<stdin>";
+ in = stdin;
+ if ((fileswitch & 2) != 0) /* Binfile wanted */
+ readwavheader( in);
+ }
+ else
+ {
+ if ((fileswitch & 2) == 0) /* Textfile wanted */
+ in = fopen(infilename, "rt");
+ else /* Binfile wanted */
+ if ((in = fopen(infilename, "rb")) != NULL)
+ readwavheader( in);
+ }
+ if (in == NULL)
+ fatalerror("Error opening input file '%s': %s\n", infilename,strerror(errno));
+ else
+ inform("Using file '%s' as input\n", infilename);
+ }
+ if ((fileswitch & 4) != 0) /* Outfile wanted */
+ {
+ outfilename = parsefilearg( argcount, args);
+ if (outfilename == NULL)
+ argerrornum( NULL, ME_NOOUTFILE);
+ if (strcmp( outfilename, "-") == 0)
+ {
+ outfilename = "<stdout>";
+ out = stdout;
+ }
+ else
+ {
+
+ if ((fileswitch & 8) == 0) /* Textfile wanted */
+ out = fopen(outfilename, "wt");
+ else /* Binfile wanted */
+ out = fopen(outfilename, "wb");
+ }
+ if (out == NULL)
+ fatalerror("Error opening output file '%s': %s\n", outfilename,strerror(errno));
+ else
+ inform("Using file '%s' as output\n", outfilename);
+ }
+ if ((fileswitch & 32) != 0) /* In-/Outfile wanted */
+ {
+ assert (in == NULL && out == NULL);
+ infilename = outfilename = parsefilearg( argcount, args);
+ if (outfilename == NULL)
+ argerrornum( NULL, ME_NOIOFILE);
+ if (strcmp( infilename, "-") == 0)
+ argerrornum( infilename, ME_NOSTDIN);
+ inform("Using file '%s' as input/output\n", outfilename);
+ in = out = fopen(outfilename, "r+");
+ if (out == NULL)
+ fatalerror("Error opening input/output file '%s': %s\n", outfilename,strerror(errno));
+
+ readwavheader( in);
+ }
+ if ((fileswitch & 16) == 0) /* No additional files wanted */
+ {
+ if ((filename = parsefilearg( argcount, args)) != NULL)
+ argerrornum( filename, ME_TOOMANYFILES);
+ }
+
+ /*-------------------------------------------------*
+ * Set samplefrequency, width, wavout,
+ *-------------------------------------------------*/
+ parseintarg( argcount, args, "f", &samplefrequency);
+ wavout = parseswitcharg( argcount, args, "h");
+ if (parseintarg( argcount, args, "w", &tempint))
+ {
+ if (tempint != 16)
+ argerrortxt(NULL, "Option -w is only valid "
+ "with value 16. Sorry.");
+ else
+ samplewidth = tempint;
+ }
+ if (parseintarg( argcount, args, "c", &tempint))
+ {
+ if (tempint != 1 && tempint != 2)
+ argerrortxt(NULL, "Option -c is only valid "
+ "with values 1 or 2. Sorry.");
+ else
+ channels = tempint;
+ }
+ /*-------------------------------------------------*
+ * Create WAV-header on output if wanted. *
+ *-------------------------------------------------*/
+ if (wavout)
+ switch (fileswitch & (12))
+ {
+ case 4: /* User wants header on textfile */
+ argerrornum( NULL, ME_HEADERONTEXTFILE);
+ case 12: /* User wants header on binfile */
+ makewavheader();
+ break;
+ case 0: /* User wants header, but there is no outfile */
+ /* Problem: what about i/o-file, 32? You might want a header
+ on that? Better ignore this case. */
+ break;
+ case 8: /* An application musn't ask for this */
+ default: /* This can't happen */
+ assert( FALSE);
+ }
+ return;
+}
+
+/* --------------------------------------------------------------------
+ Returns the index 'i' of the first argument that IS an option, and
+ which begins with the label 's'. If there is none, -1.
+ We also mark that option as done with, i.e. we cross it out.
+ -------------------------------------------------------------------- */
+int findoption( int argcount, char *args[], char *s)
+{
+ int i;
+
+ if (test_usage)
+ printf("Checking for option -%s\n", s);
+
+ for (i=1; i<argcount; i++)
+ {
+ if (isoptionchar (args[i][0]) &&
+ strncmp( args[i] + 1, s, strlen( s)) == 0)
+ {
+ args[i][0] = '\0';
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* --------------------------------------------------------------------
+ Finishes off the .WAV header (if any) and exits correctly and formerly.
+ -------------------------------------------------------------------- */
+int myexit (int value)
+{
+ switch (value)
+ {
+ case 0:
+ if (wavout)
+ makewavheader(); /* Writes a fully informed .WAV header */
+ chat ("Success!\n");
+ break;
+ default:
+ chat ("Failure.\n");
+ break;
+ }
+ exit (value);
+}
+
+/* --------------------------------------------------------------------
+ Reads the stated input file bufferwise, calls the function 'work'
+ with the proper values, and writes the result to the stated output file.
+ Return value: TRUE on success, FALSE otherwise.
+ -------------------------------------------------------------------- */
+int workloop( FILE *theinfile, FILE *theoutfile,
+ int (*work)( short *buffer, int length) )
+{
+ short *buffer;
+ int length, nowlength;
+
+ length = BUFFSIZE;
+ if ((buffer = malloc( sizeof(short) * length)) == NULL)
+ fatalperror ("");
+ while (TRUE)
+ {
+ nowlength = fread(buffer, sizeof(short), length, theinfile);
+ if (ferror( theinfile) != 0)
+ fatalperror("Error reading input file");
+ if (nowlength == 0) /* Reached end of input file */
+ break;
+ /* Call the routine that does the work */
+ if (!work (buffer, nowlength)) /* On error, stop. */
+ return FALSE;
+ fwrite(buffer, sizeof(short), nowlength, theoutfile);
+ if (ferror( theoutfile) != 0)
+ fatalperror("Error writing to output file");
+ }
+ return TRUE; /* Input file done with, no errors. */
+}
+
+int chat( const char *format, ...)
+{
+ va_list ap;
+ int result = 0;
+
+ if (verboselevel > 5)
+ {
+ va_start( ap, format);
+ result = vfprintf( stderr, format, ap);
+ va_end( ap);
+ }
+ return result;
+}
+
+
+int inform( const char *format, ...)
+{
+ va_list ap;
+ int result = 0;
+
+ if (verboselevel > 1)
+ {
+ va_start( ap, format);
+ result = vfprintf( stderr, format, ap);
+ va_end( ap);
+ }
+ return result;
+}
+
+int error( const char *format, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start( ap, format);
+ result = vfprintf( stderr, format, ap);
+ va_end( ap);
+ return result;
+}
+
+void fatalerror( const char *format, ...)
+{
+ va_list ap;
+
+ va_start( ap, format);
+ vfprintf( stderr, format, ap);
+ va_end( ap);
+ myexit(1);
+}
+
+void fatalperror( const char *string)
+{
+ perror( string);
+ myexit( 1);
+}
+
+int say( const char *format, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start( ap, format);
+ result = vfprintf( stdout, format, ap);
+ va_end( ap);
+ return result;
+}
+
+
+char *malloccopy( char *string)
+{
+ char *result;
+
+ result = malloc( strlen( string) + 1);
+ if (result != NULL)
+ strcpy( result, string);
+ return result;
+}
+
+
+char *mallocconcat( char *one, char *two)
+{
+ char *result;
+
+ result = malloc( strlen( one) + strlen( two) + 1);
+ if (result != NULL)
+ {
+ strcpy( result, one);
+ strcat( result, two);
+ }
+ return result;
+}
+
+double double2db( double value)
+{
+ if (value < 0)
+ value = -value;
+ return 6.0 * log( value / 32767) / log( 2);
+}
+
+void readawaysamples( FILE *in, size_t size)
+{
+ short *buffer;
+ int samplesread, count;
+
+ buffer = malloc( sizeof( *buffer) * BUFFSIZE);
+ if (buffer == NULL) fatalperror("Couldn't allocate buffer");
+
+ while (size > 0)
+ {
+ if (size > BUFFSIZE)
+ count = BUFFSIZE;
+ else
+ count = size;
+
+ samplesread = fread( buffer, sizeof(*buffer), count, in);
+ if (ferror( in) != 0)
+ fatalperror("Error reading input file");
+ size -= samplesread;
+ }
+ free( buffer);
+}
+
diff --git a/trunk/utils/frame.h b/trunk/utils/frame.h
new file mode 100644
index 000000000..a07c605ec
--- /dev/null
+++ b/trunk/utils/frame.h
@@ -0,0 +1,300 @@
+/****************************************************************************
+ *
+ * Programs for processing sound files in raw- or WAV-format.
+ * -- Useful functions for parsing command line options and
+ * issuing errors, warnings, and chit chat.
+ *
+ * Name: frame.h
+ * Version: see frame.c
+ * Author: Mark Roberts <mark@manumark.de>
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * These are useful functions that all DSP programs might find handy
+ ****************************************************************************/
+
+/* fileswitch for parseargs:
+
+ The following are masks for several different ways of opening files.
+ --------------------------------------------------------------------
+ Bit 0: Open infile?
+ Bit 1: Open infile as binary (as opposed to text)
+ Bit 2: Open outfile?
+ Bit 3: Open outfile as binary (as opposed to text)
+ Bit 4: Do not complain about too many file arguments
+ Bit 5: Open one file for input AND output, binary.
+*/
+#define INTEXT (1+0)
+#define INBIN (1+2)
+#define OUTTEXT (4)
+#define OUTBIN (4+8)
+#define NOFILES (0)
+#define NOCOMPLAIN (16)
+#define IOBIN (32)
+
+#ifndef FALSE
+ #define FALSE (0==1)
+ #define TRUE (0==0)
+#endif
+
+extern int samplefrequency;
+extern unsigned short samplewidth;
+extern unsigned short channels;
+extern int wavout; /* TRUE iff out file is .WAV file */
+extern int iswav; /* TRUE iff in file was found to be a .WAV file */
+extern FILE *in, *out;
+extern char *infilename, *outfilename;
+extern int verboselevel;
+extern char *version; /* String to be issued as version string. Should
+ be set by application. */
+extern char *usage; /* String to be issued as usage string. Should be
+ set by application. */
+
+#define DEFAULTFREQ 44100
+#define BUFFSIZE 50000 /* How many samples to read in one go (preferred) */
+#define MINBUFFSIZE 5000 /* How many samples to read in one go (minimum) */
+
+/*************************************************
+ * Types of errors handled by argerrornum() *
+ *************************************************/
+typedef enum
+{
+ ME_NOINT,
+ ME_NODOUBLE,
+ ME_NOTIME,
+ ME_NOVOL,
+ ME_NOSWITCH,
+ ME_TOOMANYFILES,
+ ME_HEADERONTEXTFILE,
+ ME_NOINFILE,
+ ME_NOOUTFILE,
+ ME_NOIOFILE,
+ ME_NOSTDIN,
+ ME_NOSTDOUT,
+ ME_NOSTDIO,
+ ME_NOTENOUGHFILES,
+ ME_THISCANTHAPPEN
+} Errornum;
+
+
+/* -----------------------------------------------------------------------
+ Create memory and copy 'string', returning a pointer to the copy.
+ NULL is returned if malloc fails.
+ -----------------------------------------------------------------------*/
+extern char *malloccopy( char *string);
+
+/* -----------------------------------------------------------------------
+ Start the stopwatch and make sure the user is informed at end of program.
+ -----------------------------------------------------------------------*/
+extern void startstopwatch(void);
+
+/* -----------------------------------------------------------------------
+ Writes the number of samples to result that are yet to be read from anyin.
+ I.e. the number of remaining bytes is divided by the number of bytes per
+ sample value, but not by the number of channels.
+ Return values are TRUE on success, FALSE on failure.
+ -----------------------------------------------------------------------*/
+extern int getremainingfilelength( FILE *anyin, long *result);
+
+/* -----------------------------------------------------------------------
+ Read a .pk-header from 'anyin' and printf the entries.
+ -----------------------------------------------------------------------*/
+void readpkheader( FILE *anyin);
+
+/* -----------------------------------------------------------------------
+ Read a .WAV header from 'anyin'.
+ If it is recognised, the data is used.
+ Otherwise, we assume it's PCM-data and ignore the header.
+ The global variable 'iswav' is set on success, otherwise cleared.
+ -----------------------------------------------------------------------*/
+extern void readwavheader( FILE *anyin);
+
+/* -----------------------------------------------------------------------
+ Write a .WAV header to 'out'.
+ The filepointer is placed at the end of 'out' before operation.
+ This should be called before any data is
+ written, and again, when ALL the data has been written.
+ First time, this positions the file pointer correctly; second time, the
+ missing data can be inserted that wasn't known the first time round.
+ -----------------------------------------------------------------------*/
+extern void makewavheader( void);
+
+/* --------------------------------------------------------------------
+ Tests the character 'coal' for being a command line option character,
+ momentarrily '/' or '-'.
+ -------------------------------------------------------------------- */
+extern int isoptionchar (char coal);
+
+/* -----------------------------------------------------------------------
+ Reads through the arguments on the lookout for an option starting
+ with 'string'. The rest of the option is read as a time and passed
+ to *result, where the result is meant to mean 'number of samples' in
+ that time.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -----------------------------------------------------------------------*/
+extern int parsetimearg( int argcount, char *args[], char *string,
+ int *result);
+
+/* -----------------------------------------------------------------------
+ The string argument is read as a time and passed to *result, where
+ the result is meant to mean 'number of samples' in that time. On
+ failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -----------------------------------------------------------------------*/
+int parsetime(char *string, int *result);
+
+/* -----------------------------------------------------------------------
+ The string argument is read as a frequency and passed
+ to *result, where the result is meant to mean 'number of samples' in
+ one cycle of that frequency.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -----------------------------------------------------------------------*/
+int parsefreq(char *string, double *result);
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for a switch -'string'.
+ return value is TRUE if one exists, FALSE otherwise.
+ If characters remain after the switch, a fatal error is issued.
+ -------------------------------------------------------------------- */
+extern int parseswitcharg( int argcount, char *args[], char *string);
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for an option starting
+ with 'string'. The rest of the option is read as an integer and
+ passed to &result.
+ On failure, &result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -------------------------------------------------------------------- */
+extern int parseintarg( int argcount, char *args[], char *string,
+ int *result);
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for a filename, i.e. anything
+ that does not start with the optionchar. The filename is copied to
+ newly allocated memory, a pointer to which is returned.
+ The argument is marked as used. Therefore repeated use of this function
+ will yield a complete list of filenames on the commandline.
+ If malloc() fails, the function does not return.
+ -------------------------------------------------------------------- */
+extern char *parsefilearg( int argcount, char *args[]);
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for an option starting
+ with 'string'. The rest of the option is read as a double and
+ passed to *result.
+ On failure, *result is unchanged.
+ return value is TRUE on success, FALSE otherwise.
+ -------------------------------------------------------------------- */
+extern int parsedoublearg( int argcount, char *args[], char *string,
+ double *result);
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for an option starting
+ with 'string'. The rest of the option is read as a volume, i.e.
+ absolute, percent or db. The result is passed to *result.
+ On failure, *result is unchanged.
+ -------------------------------------------------------------------- */
+extern int parsevolarg( int argcount, char *args[], char *string,
+ double *result);
+
+/* --------------------------------------------------------------------
+ Reads the specified string and interprets it as a volume. The string
+ would be of the form 1.8 or 180% or 5db.
+ On success, the return value is the relative volume, i.e. 1.8
+ On failure, -1 is returned.
+ -------------------------------------------------------------------- */
+extern int parsevolume(char *s, double *result);
+
+/* --------------------------------------------------------------------
+ Reads through the arguments on the lookout for a switch -'string'.
+ return value is TRUE if one exists, FALSE otherwise.
+ If characters remain after the switch, a fatal error is issued.
+ -------------------------------------------------------------------- */
+extern int parseswitch( char *found, char *wanted);
+
+/* --------------------------------------------------------------------
+ Reports an error due to parsing the string 's' encountered on the
+ command line.
+ -------------------------------------------------------------------- */
+extern void argerror(char *s);
+
+/* --------------------------------------------------------------------
+ Reports an error due to parsing the string 's' encountered on the
+ command line. 'code' indicates the type of error.
+ -------------------------------------------------------------------- */
+extern void argerrornum(char *s, Errornum code);
+
+/* --------------------------------------------------------------------
+ Reports an error due to parsing the string 's' encountered on the
+ command line. 'message' explains the type of error.
+ -------------------------------------------------------------------- */
+extern void argerrortxt(char *s, char *message);
+
+/* --------------------------------------------------------------------
+ Check for any remaining arguments and complain about their existence.
+ If arguments are found, this function does not return.
+ -------------------------------------------------------------------- */
+extern void checknoargs( int argcount, char *args[]);
+
+/* --------------------------------------------------------------------
+ Parses the command line arguments as represented by the function
+ arguments. Sets the global variables 'in', 'out', 'samplefrequency'
+ and 'samplewidth' accordingly.
+ According to 'fileswitch', in and out files are opened or not. See
+ above for an explanation of 'fileswitch'.
+ -------------------------------------------------------------------- */
+extern void parseargs( int argcount, char *args[], int fileswitch);
+
+/* --------------------------------------------------------------------
+ Returns the index 'i' of the first argument that IS an option, and
+ which begins with the label 's'. If there is none, -1.
+ We also mark that option as done with, i.e. we cross it out.
+ -------------------------------------------------------------------- */
+extern int findoption( int argcount, char *args[], char *s);
+
+/* --------------------------------------------------------------------
+ Finishes off the .WAV header (if any) and exits correctly and formerly.
+ -------------------------------------------------------------------- */
+extern int myexit (int value);
+
+/* --------------------------------------------------------------------
+ Reads the stated input file bufferwise, calls the function 'work'
+ with the proper values, and writes the result to the stated output file.
+ Return value: TRUE on success, FALSE otherwise.
+ -------------------------------------------------------------------- */
+extern int workloop( FILE *theinfile, FILE *theoutfile,
+ int (*work)( short *buffer, int length) );
+
+/* --------------------------------------------------------------------
+ Five functions for printing to stderr. Depending on the level of verbose,
+ output may be supressed. fatalerror() is like error() but does not return.
+ fatalperror() is like the standard function perror() but does not return.
+ -------------------------------------------------------------------- */
+extern int chat( const char *format, ...);
+extern int inform( const char *format, ...);
+extern int error( const char *format, ...);
+extern void fatalerror( const char *format, ...);
+extern void fatalperror( const char *string);
+
+/* --------------------------------------------------------------------
+ And one functions for printing to stdout.
+ -------------------------------------------------------------------- */
+extern int say( const char *format, ...);
+
+/* --------------------------------------------------------------------
+ Allocate memory for it and return a pointer to a string made up of
+ the two argument strings.
+ -------------------------------------------------------------------- */
+extern char *mallocconcat( char *one, char *two);
+
+/* --------------------------------------------------------------------
+ Convert a sample value to decibel.
+ -------------------------------------------------------------------- */
+extern double double2db( double value);
+
+/* --------------------------------------------------------------------
+ Read 'size' samples from file 'in' and lose them.
+ -------------------------------------------------------------------- */
+extern void readawaysamples( FILE *in, size_t size);
diff --git a/trunk/utils/hashtest.c b/trunk/utils/hashtest.c
new file mode 100644
index 000000000..e1780791f
--- /dev/null
+++ b/trunk/utils/hashtest.c
@@ -0,0 +1,364 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Steve Murphy
+ *
+ * Steve Murphy <murf@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 A program to thoroughly thrash a hash table, testing
+ * out locking safety, and making sure all functionality
+ * is functioning. Run with 5 or more threads to get that
+ * fully intense firestorm of activity. If your
+ * hash tables don't crash, lock up, or go weird, it must
+ * be good code! Even features some global counters
+ * that will get slightly behind because they aren't lock-protected.
+ *
+ * \author Steve Murphy <murf@digium.com>
+ */
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pthread.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include "asterisk/lock.h"
+#include "asterisk/hashtab.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+int testno = 1;
+
+/* stuff we need to make this work with the hashtab stuff */
+
+int64_t ast_mark(int prof_id, int x)
+{
+ return 0;
+}
+
+struct ht_element
+{
+ char *key;
+ char *val;
+};
+
+static int hashtab_compare_strings_nocase(const void *a, const void *b)
+{
+ const struct ht_element *ae = a, *be = b;
+ return ast_hashtab_compare_strings_nocase(ae->key, be->key);
+}
+
+#if 0
+static int hashtab_compare_strings(const void *a, const void *b)
+{
+ const struct ht_element *ae = a, *be = b;
+ return ast_hashtab_compare_strings(ae->key, be->key);
+}
+
+static unsigned int hashtab_hash_string(const void *obj)
+{
+ const struct ht_element *o = obj;
+ return ast_hashtab_hash_string((const void *)o->key);
+}
+#endif
+
+static unsigned int hashtab_hash_string_nocase(const void *obj)
+{
+ const struct ht_element *o = obj;
+ return ast_hashtab_hash_string_nocase((const void*)o->key);
+}
+
+/* random numbers */
+
+static int my_rand(int incl_low, int incl_high, unsigned int *seedp)
+{
+ if (incl_high == 0)
+ return 0;
+
+ return incl_low + (rand_r(seedp) % incl_high);
+}
+
+
+
+
+/* the testing routines */
+
+static int glob_highwater = 0;
+struct ast_hashtab *glob_hashtab = 0;
+unsigned int glob_seed = 0;
+int els_removed = 0;
+int els_added = 0;
+int els_lookedup = 0;
+int els_found = 0;
+int els_traversals = 0;
+
+/* all the operations to perform on the hashtab */
+
+static void add_element(void)
+{
+ char keybuf[100];
+ struct ht_element *x = malloc(sizeof(struct ht_element));
+ sprintf(keybuf,"key%08d", glob_highwater++);
+ x->key = strdup(keybuf);
+ x->val = strdup("interesting data");
+ ast_hashtab_insert_immediate(glob_hashtab, x);
+ els_added++;
+}
+
+static void traverse_elements(void)
+{
+ struct ht_element *el;
+ int c=0;
+ struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(glob_hashtab);
+#ifdef DEBUG
+ printf("Traverse hashtab\n");
+#endif
+ while ((el = ast_hashtab_next(it))) {
+ c++;
+ }
+ ast_hashtab_end_traversal(it);
+ els_traversals++; /* unprotected, sometimes off, but, not really important, either */
+}
+
+static void * del_element(unsigned int *seedp)
+{
+ char keybuf[100];
+ struct ht_element *el, lookup;
+ int x;
+
+ /* pick a random element from 0 to highwater-1 */
+ x = my_rand(0,glob_highwater-1,seedp);
+ sprintf(keybuf, "key%08d", x);
+#ifdef DEBUG
+ printf("Removing %s", keybuf);
+#endif
+ lookup.key = keybuf;
+ el = ast_hashtab_remove_object_via_lookup(glob_hashtab, &lookup);
+
+ if (el) {
+#ifdef DEBUG
+ printf("...YES (el=%x)\n", (unsigned long)el);
+#endif
+ free(el->key);
+ free(el->val);
+ free(el);
+ els_removed++;
+ } else {
+#ifdef DEBUG
+ printf("...NO.\n");
+#endif
+ return 0;
+ }
+
+
+ return el;
+}
+
+static int lookup_element(unsigned int *seedp)
+{
+ char keybuf[100];
+ struct ht_element *el, lookup;
+ int x;
+
+ /* pick a random element from 0 to highwater-1 */
+ x = my_rand(0,glob_highwater-1,seedp);
+ sprintf(keybuf, "key%08d", x);
+ lookup.key = keybuf;
+ el = ast_hashtab_lookup(glob_hashtab, &lookup);
+ els_lookedup++;
+ if (el)
+ els_found++;
+ if (el)
+ return 1;
+ return 0;
+}
+
+
+static void *hashtest(void *data)
+{
+ int my_els_removed = 0;
+ int my_els_added = 0;
+ int my_els_lookedup = 0;
+ int my_els_found = 0;
+ int my_els_traversals = 0;
+ int my_testno = testno++;
+ int its;
+
+ /* data will be a random number == use as a seed for random numbers */
+ unsigned long seed = (unsigned long)data;
+
+ printf("hashtest thread created... test beginning\n");
+
+ /* main test routine-- a global hashtab exists, pound it like crazy */
+ for(its=0;its<100000;its++)
+ {
+ void *seed2 = &seed;
+ int op = my_rand(0,100, seed2);
+ if (op<60) {
+ my_els_lookedup++;
+#ifdef DEBUG
+ printf("%d[%d]: LOOKUP\n", my_testno, its);
+#endif
+ if ((my_els_lookedup%1000)==0) {
+ printf(".");
+ fflush(stdout);
+ }
+ if (lookup_element(seed2))
+ my_els_found++;
+ } else if (op < 61) { /* make this 61 and it'll take 15 minutes to run */
+#ifdef DEBUG
+ printf("%d[%d]: TRAVERSE\n", my_testno, its);
+#endif
+ traverse_elements();
+ my_els_traversals++;
+
+ } else if (op < 80) {
+#ifdef DEBUG
+ printf("%d[%d]: REMOVE\n", my_testno, its);
+#endif
+ if (del_element(seed2))
+ my_els_removed++;
+ } else {
+ my_els_added++;
+#ifdef DEBUG
+ printf("%d[%d]: ADD\n", my_testno, its);
+#endif
+ add_element();
+ }
+ }
+ printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
+ my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals);
+ printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
+ els_found, els_lookedup, els_added, els_removed,els_traversals);
+ pthread_exit(0);
+}
+
+static void run_hashtest(int numthr)
+{
+ pthread_t thr[numthr];
+ void *thrres[numthr];
+ int i, biggest, resize_cnt, numobjs, numbuckets;
+
+ /* init a single global hashtab, then... */
+ glob_hashtab = ast_hashtab_create(180000, hashtab_compare_strings_nocase, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_string_nocase, 1);
+ printf("starting with %d elements in the hashtable...\n", ast_hashtab_capacity(glob_hashtab));
+ /* set a random seed */
+ glob_seed = (unsigned int)time(0);
+ srand(glob_seed);
+
+ /* create threads, each running hashtest */
+ for(i=0;i<numthr;i++)
+ {
+ unsigned long z = rand();
+
+ printf("starting hashtest thread %d....\n",i+1);
+ if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) {
+ printf("Sorry, couldn't create thread #%d\n", i+1);
+ }
+ printf("hashtest thread spawned.... \n");
+ }
+ /* collect threads, each running hashtest */
+ for(i=0;i<numthr;i++)
+ {
+ printf("waiting for thread %d....\n", i+1);
+ if (pthread_join(thr[i], &thrres[i])) {
+ printf("Sorry, couldn't join thread #%d\n", i+1);
+ }
+ printf("hashtest thread %d done.... \n",i+1);
+ }
+ /* user has to kill/intr the process to stop the test? */
+ ast_hashtab_get_stats(glob_hashtab, &biggest, &resize_cnt, &numobjs, &numbuckets);
+ printf("Some stats: longest bucket chain: %d; number of resizes: %d; number of objects: %d; capacity: %d\n",
+ biggest, resize_cnt, numobjs, numbuckets);
+}
+
+
+int main(int argc,char **argv)
+{
+ if (argc < 2 || argc > 2 || atoi(argv[1]) < 1)
+ {
+ printf("Usage: hashtest <number of threads>\n");
+ exit(1);
+ }
+
+ /* one arg == number of threads to create */
+ run_hashtest(atoi(argv[1]));
+
+ return 0;
+}
+
+int ast_add_profile(const char *x, uint64_t scale)
+{
+ return 0;
+}
+
+int ast_loader_register(int (*updater)(void))
+{
+ return 1;
+}
+
+int ast_loader_unregister(int (*updater)(void))
+{
+ return 1;
+}
+void ast_module_register(const struct ast_module_info *x)
+{
+}
+
+void ast_module_unregister(const struct ast_module_info *x)
+{
+}
+
+
+void ast_register_file_version(const char *file, const char *version)
+{
+}
+
+void ast_unregister_file_version(const char *file)
+{
+
+}
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+void ast_verbose(const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("VERBOSE: ");
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+void ast_register_thread(char *name)
+{
+
+}
+
+void ast_unregister_thread(void *id)
+{
+}
diff --git a/trunk/utils/hashtest2.c b/trunk/utils/hashtest2.c
new file mode 100644
index 000000000..0fe10ab75
--- /dev/null
+++ b/trunk/utils/hashtest2.c
@@ -0,0 +1,376 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Steve Murphy
+ *
+ * Steve Murphy <murf@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 A program to thoroughly thrash a hash table, testing
+ * out locking safety, and making sure all functionality
+ * is functioning. Run with 5 or more threads to get that
+ * fully intense firestorm of activity. If your
+ * hash tables don't crash, lock up, or go weird, it must
+ * be good code! Even features some global counters
+ * that will get slightly behind because they aren't lock-protected.
+ *
+ * \author Steve Murphy <murf@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pthread.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+
+int testno = 2;
+
+/* stuff we need to make this work with the astobj2 stuff */
+
+int64_t ast_mark(int prof_id, int x)
+{
+ return 0;
+}
+
+/* my OBJECT */
+struct ht_element
+{
+ char *key;
+ char *val;
+};
+
+
+static int hash_string(const void *obj, const int flags)
+{
+ char *str = ((struct ht_element*)obj)->key;
+ int total;
+
+ for (total=0; *str; str++)
+ {
+ unsigned int tmp = total;
+ total <<= 1; /* multiply by 2 */
+ total += tmp; /* multiply by 3 */
+ total <<= 2; /* multiply by 12 */
+ total += tmp; /* multiply by 13 */
+
+ total += ((unsigned int)(*str));
+ }
+ if (total < 0)
+ total = -total;
+ return total;
+}
+
+static int hashtab_compare_strings(void *a, void *b, int flags)
+{
+ const struct ht_element *ae = a, *be = b;
+ return !strcmp(ae->key, be->key) ? CMP_MATCH : 0;
+}
+
+/* random numbers */
+
+static int my_rand(int incl_low, int incl_high, unsigned int *seedp)
+{
+ if (incl_high == 0)
+ return 0;
+
+ return incl_low + (rand_r(seedp) % incl_high);
+}
+
+
+
+
+/* the testing routines */
+
+static int glob_highwater = 0;
+struct ao2_container *glob_hashtab = 0;
+unsigned int glob_seed = 0;
+int els_removed = 0;
+int els_added = 0;
+int els_lookedup = 0;
+int els_found = 0;
+int els_traversals = 0;
+
+/* all the operations to perform on the hashtab */
+
+static void ht_destroy(void *obj)
+{
+ const struct ht_element *o = obj;
+ if (o->key)
+ free(o->key);
+ if (o->val)
+ free(o->val);
+}
+
+
+static void add_element(void)
+{
+ char keybuf[100];
+ struct ht_element *x = ao2_alloc(sizeof(struct ht_element), ht_destroy);
+ sprintf(keybuf,"key%08d", glob_highwater++);
+ x->key = strdup(keybuf);
+ x->val = strdup("interesting data");
+#ifdef DEBUG
+ printf("+ %s\n", keybuf);
+#endif
+ ao2_link(glob_hashtab, x);
+
+ els_added++; /* unprotected, sometimes off, but, not really important, either */
+}
+
+static int do_nothing_cb(void *obj, void *arg, int flags)
+{
+ return 0;
+}
+
+static void traverse_elements(void)
+{
+#ifdef DEBUG
+ printf("Traverse hashtab\n");
+#endif
+ ao2_callback(glob_hashtab, OBJ_NODATA, do_nothing_cb, NULL);
+ els_traversals++; /* unprotected, sometimes off, but, not really important, either */
+}
+
+static void * del_element(unsigned int *seedp)
+{
+ char keybuf[100];
+ struct ht_element *el, lookup;
+ int x;
+
+ /* pick a random element from 0 to highwater-1 */
+ x = my_rand(0,glob_highwater-1,seedp);
+ sprintf(keybuf, "key%08d", x);
+#ifdef DEBUG
+ printf("- %s", keybuf);
+#endif
+ lookup.key = keybuf;
+ el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER);
+ if (el) {
+#ifdef DEBUG
+ printf("...YES (el=%x)\n", (unsigned long)el);
+#endif
+ ao2_unlink(glob_hashtab, el); /* mistakenly tried to use ao2_ref(c,-2) here to unlink. Bad Boy! */
+ els_removed++;
+ } else {
+#ifdef DEBUG
+ printf("...NO.\n");
+#endif
+ return 0;
+ }
+ return el;
+}
+
+static int lookup_element(unsigned int *seedp)
+{
+ char keybuf[100];
+ struct ht_element *el, lookup;
+ int x;
+
+ /* pick a random element from 0 to highwater-1 */
+ x = my_rand(0,glob_highwater-1,seedp);
+ sprintf(keybuf, "key%08d", x);
+ lookup.key = keybuf;
+ el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER);
+ els_lookedup++;
+ if (el) {
+ els_found++;
+ ao2_ref(el, -1); /* toss out this ref, no longer needed */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+static void *hashtest(void *data)
+{
+ int my_els_removed = 0;
+ int my_els_added = 0;
+ int my_els_lookedup = 0;
+ int my_els_found = 0;
+ int my_els_traversals = 0;
+ int my_testno = testno++;
+ int its;
+
+ /* data will be a random number == use as a seed for random numbers */
+ unsigned long seed = (unsigned long)data;
+ printf("hashtest thread created... test beginning\n");
+
+ /* main test routine-- a global hashtab exists, pound it like crazy */
+ for(its=0;its<100000;its++)
+ {
+ void *seed2 = &seed;
+ int op = my_rand(0,100, seed2);
+ if (op<60) {
+ my_els_lookedup++;
+#ifdef DEBUG
+ printf("%d[%d]: LOOKUP\n", my_testno, its);
+#endif
+ if ((my_els_lookedup%1000)==0) {
+ printf(".");
+ fflush(stdout);
+ }
+ if (lookup_element(seed2))
+ my_els_found++;
+ } else if (op < 61) { /* make this 61 and it'll take 16 minutes to run */
+#ifdef DEBUG
+ printf("%d[%d]: TRAVERSE\n", my_testno, its);
+#endif
+ traverse_elements();
+ my_els_traversals++;
+
+ } else if (op < 80) {
+#ifdef DEBUG
+ printf("%d[%d]: REMOVE\n", my_testno, its);
+#endif
+ if (del_element(seed2))
+ my_els_removed++;
+ } else {
+ my_els_added++;
+#ifdef DEBUG
+ printf("%d[%d]: ADD\n", my_testno, its);
+#endif
+ add_element();
+ }
+ }
+ printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
+ my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals);
+ printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d; traversals=%d\n",
+ els_found, els_lookedup, els_added, els_removed, els_traversals);
+ pthread_exit(0);
+ return NULL;
+}
+
+static void run_hashtest(int numthr)
+{
+ pthread_t thr[numthr];
+ void *thrres[numthr];
+ int i;
+
+ /* init a single global hashtab, then... */
+ glob_hashtab = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
+
+ /* set a random seed */
+ glob_seed = (unsigned int)time(0);
+ srand(glob_seed);
+
+ /* create threads, each running hashtest */
+ for(i=0;i<numthr;i++)
+ {
+ unsigned long z = rand();
+
+ printf("starting hashtest thread %d....\n",i+1);
+ if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) {
+ printf("Sorry, couldn't create thread #%d\n", i+1);
+ }
+ printf("hashtest thread spawned.... \n");
+ }
+ /* collect threads, each running hashtest */
+ for(i=0;i<numthr;i++)
+ {
+ printf("waiting for thread %d....\n", i+1);
+ if (pthread_join(thr[i], &thrres[i])) {
+ printf("Sorry, couldn't join thread #%d\n", i+1);
+ }
+ printf("hashtest thread %d done.... \n",i+1);
+ }
+ /* user has to kill/intr the process to stop the test? */
+}
+
+
+int main(int argc,char **argv)
+{
+ if (argc < 2 || argc > 2 || atoi(argv[1]) < 1)
+ {
+ printf("Usage: hashtest <number of threads>\n");
+ exit(1);
+ }
+
+ /* one arg == number of threads to create */
+ run_hashtest(atoi(argv[1]));
+
+ return 0;
+}
+
+
+int ast_add_profile(const char *x, uint64_t scale)
+{
+ return 0;
+}
+
+int ast_loader_register(int (*updater)(void))
+{
+ return 1;
+}
+
+int ast_loader_unregister(int (*updater)(void))
+{
+ return 1;
+}
+void ast_module_register(const struct ast_module_info *x)
+{
+}
+
+void ast_module_unregister(const struct ast_module_info *x)
+{
+}
+
+
+void ast_register_file_version(const char *file, const char *version)
+{
+}
+
+void ast_unregister_file_version(const char *file)
+{
+
+}
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+ printf("LOG: lev:%d file:%s line:%d func: %s ",
+ level, file, line, function);
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+void ast_verbose(const char *fmt, ...)
+{
+ va_list vars;
+ va_start(vars,fmt);
+
+ printf("VERBOSE: ");
+ vprintf(fmt, vars);
+ fflush(stdout);
+ va_end(vars);
+}
+
+void ast_register_thread(char *name)
+{
+
+}
+
+void ast_unregister_thread(void *id)
+{
+}
diff --git a/trunk/utils/muted.c b/trunk/utils/muted.c
new file mode 100644
index 000000000..6a32b6d99
--- /dev/null
+++ b/trunk/utils/muted.c
@@ -0,0 +1,698 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Updated for Mac OSX CoreAudio
+ * by Josh Roberson <josh@asteriasgi.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 Mute Daemon
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * Updated for Mac OSX CoreAudio
+ * \arg Josh Roberson <josh@asteriasgi.com>
+ *
+ * \note Specially written for Malcolm Davenport, but I think I'll use it too
+ * Connects to the Asterisk Manager Interface, AMI, and listens for events
+ * on certain devices. If a phone call is connected to one of the devices (phones)
+ * the local sound is muted to a lower volume during the call.
+ *
+ */
+
+#ifdef __Darwin__
+#include <CoreAudio/AudioHardware.h>
+#elif defined(__linux__) || defined(__FreeBSD__)
+#include <sys/soundcard.h>
+#endif
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+static char *config = "/etc/muted.conf";
+
+static char host[256] = "";
+static char user[256] = "";
+static char pass[256] = "";
+static int smoothfade = 0;
+static int mutelevel = 20;
+static int muted = 0;
+static int needfork = 1;
+static int debug = 0;
+static int stepsize = 3;
+#ifndef __Darwin__
+static int mixchan = SOUND_MIXER_VOLUME;
+#endif
+
+struct subchannel {
+ char *name;
+ struct subchannel *next;
+};
+
+static struct channel {
+ char *tech;
+ char *location;
+ struct channel *next;
+ struct subchannel *subs;
+} *channels;
+
+static void add_channel(char *tech, char *location)
+{
+ struct channel *chan;
+ chan = malloc(sizeof(struct channel));
+ if (chan) {
+ memset(chan, 0, sizeof(struct channel));
+ if (!(chan->tech = strdup(tech))) {
+ free(chan);
+ return;
+ }
+ if (!(chan->location = strdup(location))) {
+ free(chan->tech);
+ free(chan);
+ return;
+ }
+ chan->next = channels;
+ channels = chan;
+ }
+
+}
+
+static int load_config(void)
+{
+ FILE *f;
+ char buf[1024];
+ char *val;
+ char *val2;
+ int lineno=0;
+ int x;
+ f = fopen(config, "r");
+ if (!f) {
+ fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno));
+ return -1;
+ }
+ while(!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!feof(f)) {
+ lineno++;
+ val = strchr(buf, '#');
+ if (val) *val = '\0';
+ while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
+ buf[strlen(buf) - 1] = '\0';
+ if (!strlen(buf))
+ continue;
+ val = buf;
+ while(*val) {
+ if (*val < 33)
+ break;
+ val++;
+ }
+ if (*val) {
+ *val = '\0';
+ val++;
+ while(*val && (*val < 33)) val++;
+ }
+ if (!strcasecmp(buf, "host")) {
+ if (val && strlen(val))
+ strncpy(host, val, sizeof(host) - 1);
+ else
+ fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno);
+ } else if (!strcasecmp(buf, "user")) {
+ if (val && strlen(val))
+ strncpy(user, val, sizeof(user) - 1);
+ else
+ fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno);
+ } else if (!strcasecmp(buf, "pass")) {
+ if (val && strlen(val))
+ strncpy(pass, val, sizeof(pass) - 1);
+ else
+ fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno);
+ } else if (!strcasecmp(buf, "smoothfade")) {
+ smoothfade = 1;
+ } else if (!strcasecmp(buf, "mutelevel")) {
+ if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) {
+ mutelevel = x;
+ } else
+ fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
+ } else if (!strcasecmp(buf, "channel")) {
+ if (val && strlen(val)) {
+ val2 = strchr(val, '/');
+ if (val2) {
+ *val2 = '\0';
+ val2++;
+ add_channel(val, val2);
+ } else
+ fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno);
+ } else
+ fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno);
+ } else {
+ fprintf(stderr, "ignoring unknown keyword '%s'\n", buf);
+ }
+ }
+ }
+ fclose(f);
+ if (!strlen(host))
+ fprintf(stderr, "no 'host' specification in config file\n");
+ else if (!strlen(user))
+ fprintf(stderr, "no 'user' specification in config file\n");
+ else if (!channels)
+ fprintf(stderr, "no 'channel' specifications in config file\n");
+ else
+ return 0;
+ return -1;
+}
+
+static FILE *astf;
+#ifndef __Darwin__
+static int mixfd;
+
+static int open_mixer(void)
+{
+ mixfd = open("/dev/mixer", O_RDWR);
+ if (mixfd < 0) {
+ fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+#endif /* !__Darwin */
+
+/*! Connect to the asterisk manager interface */
+static int connect_asterisk(void)
+{
+ int sock;
+ struct hostent *hp;
+ char *ports;
+ int port = 5038;
+ struct sockaddr_in sin;
+
+ ports = strchr(host, ':');
+ if (ports) {
+ *ports = '\0';
+ ports++;
+ if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) {
+ fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports);
+ return -1;
+ }
+ }
+ hp = gethostbyname(host);
+ if (!hp) {
+ fprintf(stderr, "Can't find host '%s'\n", host);
+ return -1;
+ }
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
+ return -1;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ if (connect(sock, (struct sockaddr *)&sin, sizeof(sin))) {
+ fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno));
+ close(sock);
+ return -1;
+ }
+ astf = fdopen(sock, "r+");
+ if (!astf) {
+ fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+ return 0;
+}
+
+static char *get_line(void)
+{
+ static char buf[1024];
+ if (fgets(buf, sizeof(buf), astf)) {
+ while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
+ buf[strlen(buf) - 1] = '\0';
+ return buf;
+ } else
+ return NULL;
+}
+
+/*! Login to the asterisk manager interface */
+static int login_asterisk(void)
+{
+ char *welcome;
+ char *resp;
+ if (!(welcome = get_line())) {
+ fprintf(stderr, "disconnected (1)\n");
+ return -1;
+ }
+ fprintf(astf,
+ "Action: Login\r\n"
+ "Username: %s\r\n"
+ "Secret: %s\r\n\r\n", user, pass);
+ if (!(welcome = get_line())) {
+ fprintf(stderr, "disconnected (2)\n");
+ return -1;
+ }
+ if (strcasecmp(welcome, "Response: Success")) {
+ fprintf(stderr, "login failed ('%s')\n", welcome);
+ return -1;
+ }
+ /* Eat the rest of the event */
+ while((resp = get_line()) && strlen(resp));
+ if (!resp) {
+ fprintf(stderr, "disconnected (3)\n");
+ return -1;
+ }
+ fprintf(astf,
+ "Action: Status\r\n\r\n");
+ if (!(welcome = get_line())) {
+ fprintf(stderr, "disconnected (4)\n");
+ return -1;
+ }
+ if (strcasecmp(welcome, "Response: Success")) {
+ fprintf(stderr, "status failed ('%s')\n", welcome);
+ return -1;
+ }
+ /* Eat the rest of the event */
+ while((resp = get_line()) && strlen(resp));
+ if (!resp) {
+ fprintf(stderr, "disconnected (5)\n");
+ return -1;
+ }
+ return 0;
+}
+
+static struct channel *find_channel(char *channel)
+{
+ char tmp[256] = "";
+ char *s, *t;
+ struct channel *chan;
+ strncpy(tmp, channel, sizeof(tmp) - 1);
+ s = strchr(tmp, '/');
+ if (s) {
+ *s = '\0';
+ s++;
+ t = strrchr(s, '-');
+ if (t) {
+ *t = '\0';
+ }
+ if (debug)
+ printf("Searching for '%s' tech, '%s' location\n", tmp, s);
+ chan = channels;
+ while(chan) {
+ if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) {
+ if (debug)
+ printf("Found '%s'/'%s'\n", chan->tech, chan->location);
+ break;
+ }
+ chan = chan->next;
+ }
+ } else
+ chan = NULL;
+ return chan;
+}
+
+#ifndef __Darwin__
+static int getvol(void)
+{
+ int vol;
+
+ if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
+#else
+static float getvol(void)
+{
+ float volumeL, volumeR, vol;
+ OSStatus err;
+ AudioDeviceID device;
+ UInt32 size;
+ UInt32 channels[2];
+
+ size = sizeof(device);
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
+ size = sizeof(channels);
+ if (!err)
+ err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
+ size = sizeof(vol);
+ if (!err)
+ err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL);
+ if (!err)
+ err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR);
+ if (!err)
+ vol = (volumeL < volumeR) ? volumeR : volumeL;
+ else {
+#endif
+ fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno));
+ return -1;
+ }
+ return vol;
+}
+
+#ifndef __Darwin__
+static int setvol(int vol)
+#else
+static int setvol(float vol)
+#endif
+{
+#ifndef __Darwin__
+ if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
+#else
+ float volumeL = vol;
+ float volumeR = vol;
+ OSStatus err;
+ AudioDeviceID device;
+ UInt32 size;
+ UInt32 channels[2];
+
+ size = sizeof(device);
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
+ size = sizeof(channels);
+ err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
+ size = sizeof(vol);
+ if (!err)
+ err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL);
+ if (!err)
+ err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR);
+ if (err) {
+#endif
+
+ fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
+ return -1;
+
+ }
+ return 0;
+}
+
+#ifndef __Darwin__
+static int oldvol = 0;
+static int mutevol = 0;
+#else
+static float oldvol = 0;
+static float mutevol = 0;
+#endif
+
+#ifndef __Darwin__
+static int mutedlevel(int orig, int mutelevel)
+{
+ int l = orig >> 8;
+ int r = orig & 0xff;
+ l = (float)(mutelevel) * (float)(l) / 100.0;
+ r = (float)(mutelevel) * (float)(r) / 100.0;
+
+ return (l << 8) | r;
+#else
+static float mutedlevel(float orig, float mutelevel)
+{
+ float master = orig;
+ master = mutelevel * master / 100.0;
+ return master;
+#endif
+
+}
+
+static void mute(void)
+{
+#ifndef __Darwin__
+ int vol;
+ int start;
+ int x;
+#else
+ float vol;
+ float start = 1.0;
+ float x;
+#endif
+ vol = getvol();
+ oldvol = vol;
+ if (smoothfade)
+#ifdef __Darwin__
+ start = mutelevel;
+#else
+ start = 100;
+ else
+ start = mutelevel;
+#endif
+ for (x=start;x>=mutelevel;x-=stepsize) {
+ mutevol = mutedlevel(vol, x);
+ setvol(mutevol);
+ /* Wait 0.01 sec */
+ usleep(10000);
+ }
+ mutevol = mutedlevel(vol, mutelevel);
+ setvol(mutevol);
+ if (debug)
+#ifdef __Darwin__
+ printf("Mute from '%f' to '%f'!\n", oldvol, mutevol);
+#else
+ printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol);
+#endif
+ muted = 1;
+}
+
+static void unmute(void)
+{
+#ifdef __Darwin__
+ float vol;
+ float start;
+ float x;
+#else
+ int vol;
+ int start;
+ int x;
+#endif
+ vol = getvol();
+ if (debug)
+#ifdef __Darwin__
+ printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol);
+ mutevol = vol;
+ if (vol == mutevol) {
+#else
+ printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol);
+ if ((int)vol == mutevol) {
+#endif
+ if (smoothfade)
+ start = mutelevel;
+ else
+#ifdef __Darwin__
+ start = 1.0;
+#else
+ start = 100;
+#endif
+ for (x=start;x<100;x+=stepsize) {
+ mutevol = mutedlevel(oldvol, x);
+ setvol(mutevol);
+ /* Wait 0.01 sec */
+ usleep(10000);
+ }
+ setvol(oldvol);
+ } else
+ printf("Whoops, it's already been changed!\n");
+ muted = 0;
+}
+
+static void check_mute(void)
+{
+ int offhook = 0;
+ struct channel *chan;
+ chan = channels;
+ while(chan) {
+ if (chan->subs) {
+ offhook++;
+ break;
+ }
+ chan = chan->next;
+ }
+ if (offhook && !muted)
+ mute();
+ else if (!offhook && muted)
+ unmute();
+}
+
+static void delete_sub(struct channel *chan, char *name)
+{
+ struct subchannel *sub, *prev;
+ prev = NULL;
+ sub = chan->subs;
+ while(sub) {
+ if (!strcasecmp(sub->name, name)) {
+ if (prev)
+ prev->next = sub->next;
+ else
+ chan->subs = sub->next;
+ free(sub->name);
+ free(sub);
+ return;
+ }
+ prev = sub;
+ sub = sub->next;
+ }
+}
+
+static void append_sub(struct channel *chan, char *name)
+{
+ struct subchannel *sub;
+ sub = chan->subs;
+ while(sub) {
+ if (!strcasecmp(sub->name, name))
+ return;
+ sub = sub->next;
+ }
+ sub = malloc(sizeof(struct subchannel));
+ if (sub) {
+ memset(sub, 0, sizeof(struct subchannel));
+ if (!(sub->name = strdup(name))) {
+ free(sub);
+ return;
+ }
+ sub->next = chan->subs;
+ chan->subs = sub;
+ }
+}
+
+static void hangup_chan(char *channel)
+{
+ struct channel *chan;
+ if (debug)
+ printf("Hangup '%s'\n", channel);
+ chan = find_channel(channel);
+ if (chan)
+ delete_sub(chan, channel);
+ check_mute();
+}
+
+static void offhook_chan(char *channel)
+{
+ struct channel *chan;
+ if (debug)
+ printf("Offhook '%s'\n", channel);
+ chan = find_channel(channel);
+ if (chan)
+ append_sub(chan, channel);
+ check_mute();
+}
+
+static int wait_event(void)
+{
+ char *resp;
+ char event[120]="";
+ char channel[120]="";
+ char oldname[120]="";
+ char newname[120]="";
+
+ resp = get_line();
+ if (!resp) {
+ fprintf(stderr, "disconnected (6)\n");
+ return -1;
+ }
+ if (!strncasecmp(resp, "Event: ", strlen("Event: "))) {
+ strncpy(event, resp + strlen("Event: "), sizeof(event) - 1);
+ /* Consume the rest of the non-event */
+ while((resp = get_line()) && strlen(resp)) {
+ if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
+ strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
+ if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
+ strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
+ if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
+ strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
+ }
+ if (strlen(channel)) {
+ if (!strcasecmp(event, "Hangup"))
+ hangup_chan(channel);
+ else
+ offhook_chan(channel);
+ }
+ if (strlen(newname) && strlen(oldname)) {
+ if (!strcasecmp(event, "Rename")) {
+ hangup_chan(oldname);
+ offhook_chan(newname);
+ }
+ }
+ } else {
+ /* Consume the rest of the non-event */
+ while((resp = get_line()) && strlen(resp));
+ }
+ if (!resp) {
+ fprintf(stderr, "disconnected (7)\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("Usage: muted [-f] [-d]\n"
+ " -f : Do not fork\n"
+ " -d : Debug (implies -f)\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int x;
+ while((x = getopt(argc, argv, "fhd")) > 0) {
+ switch(x) {
+ case 'd':
+ debug = 1;
+ needfork = 0;
+ break;
+ case 'f':
+ needfork = 0;
+ break;
+ case 'h':
+ /* Fall through */
+ default:
+ usage();
+ exit(1);
+ }
+ }
+ if (load_config())
+ exit(1);
+#ifndef __Darwin__
+ if (open_mixer())
+ exit(1);
+#endif
+ if (connect_asterisk()) {
+#ifndef __Darwin__
+ close(mixfd);
+#endif
+ exit(1);
+ }
+ if (login_asterisk()) {
+#ifndef __Darwin__
+ close(mixfd);
+#endif
+ fclose(astf);
+ exit(1);
+ }
+ if (needfork)
+ daemon(0,0);
+ for(;;) {
+ if (wait_event()) {
+ fclose(astf);
+ while(connect_asterisk()) {
+ sleep(5);
+ }
+ if (login_asterisk()) {
+ fclose(astf);
+ exit(1);
+ }
+ }
+ }
+ exit(0);
+}
diff --git a/trunk/utils/smsq.c b/trunk/utils/smsq.c
new file mode 100644
index 000000000..af8238c84
--- /dev/null
+++ b/trunk/utils/smsq.c
@@ -0,0 +1,771 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005
+ *
+ * SMS queuing application for use with asterisk app_sms
+ * by Adrian Kennard
+ *
+ * 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.
+ */
+
+#include "asterisk.h"
+
+#include <popt.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <time.h>
+
+#ifdef SOLARIS
+#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000
+#endif
+#if !defined(POPT_ARGFLAG_SHOW_DEFAULT)
+#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000
+#endif
+
+
+/*!
+ * \brief reads next USC character from null terminated UTF-8 string and advanced pointer
+ * for non valid UTF-8 sequences.
+ * \return character as is Does \b NOT advance pointer for null termination
+*/
+static int utf8decode (unsigned char **pp)
+{
+ unsigned char *p = *pp;
+ if (!*p)
+ return 0; /* null termination of string */
+ (*pp)++;
+ if (*p < 0xC0)
+ return *p; /* ascii or continuation character */
+ if (*p < 0xE0)
+ {
+ if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp)++;
+ return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
+ }
+ if (*p < 0xF0)
+ {
+ if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 2;
+ return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
+ }
+ if (*p < 0xF8)
+ {
+ if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 3;
+ return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
+ }
+ if (*p < 0xFC)
+ {
+ if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
+ || (p[4] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 4;
+ return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
+ }
+ if (*p < 0xFE)
+ {
+ if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
+ || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
+ return *p; /* not valid UTF-8 */
+ (*pp) += 5;
+ return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) +
+ (p[5] & 0x3F);
+ }
+ return *p; /* not sensible */
+}
+
+/*!
+ * \brief check for any queued messages in specific queue (queue="" means any queue)
+ * \param dir,queue,subaddress,channel,callerid,wait,delay,retries,concurrent
+ * \retval 0 if nothing queued
+ * \retval 1 if queued and outgoing set up OK
+ * \retval 2 of outgoing exists
+*/
+static char txqcheck (char *dir, char *queue, char subaddress, char *channel, char *callerid, int wait, int delay, int retries, int concurrent)
+{
+ char ogname[100],
+ temp[100],
+ dirname[100],
+ *p=NULL;
+ FILE *f;
+ DIR *d;
+ int ql = strlen (queue), qfl = ql;
+ struct dirent *fn;
+ snprintf (dirname, sizeof(dirname), "sms/%s", dir);
+ d = opendir (dirname);
+ if (!d)
+ return 0;
+ while ((fn = readdir (d))
+ && !(*fn->d_name != '.'
+ && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))));
+ if (!fn)
+ {
+ closedir (d);
+ return 0;
+ }
+ if (!ql)
+ { /* not searching any specific queue, so use whatr we found as the queue */
+ queue = fn->d_name;
+ qfl = ql = p - queue;
+ }
+ p = strchr (queue, '-');
+ if (p && p < queue + ql)
+ {
+ ql = p - queue;
+ subaddress = p[1];
+ }
+ snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
+ f = fopen (temp, "w");
+ if (!f)
+ {
+ perror (temp);
+ closedir (d);
+ return 0;
+ }
+ fprintf (f, "Channel: ");
+ if (!channel)
+ fprintf (f, "Local/%.*s\n", ql, queue);
+ else
+ {
+ p = strchr (channel, '/');
+ if (!p)
+ p = channel;
+ p = strchr (p, 'X');
+ if (p)
+ fprintf (f, "%.*s%c%s\n", (int)(p - channel), channel, subaddress, p + 1);
+ else
+ fprintf (f, "%s\n", channel);
+ }
+ fprintf (f, "Callerid: SMS <");
+ if (!callerid)
+ fprintf (f, "%.*s", ql, queue);
+ else
+ {
+ p = strchr (callerid, 'X');
+ if (p)
+ fprintf (f, "%.*s%c%s", (int)(p - callerid), callerid, subaddress, p + 1);
+ else
+ fprintf (f, "%s", callerid);
+ }
+ fprintf (f, ">\n");
+ fprintf (f, "Application: SMS\n");
+ fprintf (f, "Data: %.*s", qfl, queue);
+ if (dir[1] == 't')
+ fprintf (f, "|s");
+ fprintf (f, "\nMaxRetries: %d\n", retries);
+ fprintf (f, "RetryTime: %d\n", delay);
+ fprintf (f, "WaitTime: %d\n", wait);
+ fclose (f);
+ closedir (d);
+ {
+ int try = 0;
+ while (try < concurrent)
+ {
+ try++;
+ snprintf(ogname, sizeof(ogname), "outgoing/smsq.%s.%s.%d", dir, queue, try);
+ if (!link (temp, ogname))
+ { /* queued OK */
+ unlink (temp);
+ return 1;
+ }
+ }
+ }
+ /* failed to create call queue */
+ unlink (temp);
+ return 2;
+}
+
+/*!
+ * \brief Process received queue entries
+ * Run through a process, setting environment variables
+*/
+static void rxqcheck (char *dir, char *queue, char *process)
+{
+ char *p;
+ void *pp = &p;
+ char dirname[100],
+ temp[100];
+ DIR *d;
+ int ql = strlen (queue);
+ struct dirent *fn;
+ snprintf(temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
+ snprintf(dirname, sizeof(dirname), "sms/%s", dir);
+ d = opendir (dirname);
+ if (!d)
+ return;
+ while ((fn = readdir (d)))
+ if ((*fn->d_name != '.'
+ && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))))
+ { /* process file */
+ char filename[1010];
+ char line[1000];
+ unsigned short ud[160];
+ unsigned char udl = 0;
+ FILE *f;
+ snprintf (filename, sizeof(filename), "sms/%s/%s", dir, fn->d_name);
+ if (rename (filename, temp))
+ continue; /* cannot access file */
+ f = fopen (temp, "r");
+ unlink (temp);
+ if (!f)
+ {
+ perror (temp);
+ continue;
+ }
+ unsetenv ("oa");
+ unsetenv ("da");
+ unsetenv ("scts");
+ unsetenv ("pid");
+ unsetenv ("dcs");
+ unsetenv ("mr");
+ unsetenv ("srr");
+ unsetenv ("rp");
+ unsetenv ("vp");
+ unsetenv ("udh");
+ unsetenv ("ud");
+ unsetenv ("ude");
+ unsetenv ("ud8");
+ unsetenv ("ud16");
+ unsetenv ("morx");
+ unsetenv ("motx");
+ unsetenv ("queue");
+ if (*queue)
+ setenv ("queue", queue, 1);
+ setenv (dir, "", 1);
+ while (fgets (line, sizeof (line), f))
+ {
+ for (p = line; *p && *p != '\n' && *p != '\r'; p++);
+ *p = 0; /* strip eoln */
+ p = line;
+ if (!*p || *p == ';')
+ continue; /* blank line or comment, ignore */
+ while (isalnum (*p))
+ {
+ *p = tolower (*p);
+ p++;
+ }
+ while (isspace (*p))
+ *p++ = 0;
+ if (*p == '=')
+ { /* = */
+ *p++ = 0;
+ if (!strcmp (line, "oa") || !strcmp (line, "da") || !strcmp (line, "scts") || !strcmp (line, "pid")
+ || !strcmp (line, "dcs") || !strcmp (line, "mr") || !strcmp (line, "vp"))
+ setenv (line, p, 1);
+ else if ((!strcmp (line, "srr") || !strcmp (line, "rp")) && atoi (p))
+ setenv (line, "", 1);
+ else if (!strcmp (line, "ud"))
+ { /* read the user data as UTF-8 */
+ long v;
+ udl = 0;
+ while ((v = utf8decode (pp)) && udl < 160)
+ if (v && v <= 0xFFFF)
+ ud[udl++] = v;
+ }
+ } else if (*p == '#')
+ {
+ *p++ = 0;
+ if (*p == '#')
+ { /* ## */
+ p++;
+ if (!strcmp (line, "udh"))
+ setenv (line, p, 1);
+ else if (!strcmp (line, "ud"))
+ { /* read user data UCS-2 */
+ udl = 0;
+ while (*p && udl < 160)
+ {
+ if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3]))
+ {
+ ud[udl++] =
+ (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) +
+ (((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
+ (((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF));
+ p += 4;
+ } else
+ break;
+ }
+ }
+ } else
+ { /* # */
+ if (!strcmp (line, "ud"))
+ { /* read user data UCS-1 */
+ udl = 0;
+ while (*p && udl < 160)
+ {
+ if (isxdigit (*p) && isxdigit (p[1]))
+ {
+ ud[udl++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
+ p += 2;
+ } else
+ break;
+ }
+ }
+ }
+ }
+ }
+ fclose (f);
+ /* set up user data variables */
+ {
+ char temp[481];
+ int n,
+ p;
+ for (n = 0, p = 0; p < udl; p++)
+ {
+ unsigned short v = ud[p];
+ if (v)
+ {
+ if (v < 0x80)
+ temp[n++] = v;
+ else if (v < 0x800)
+ {
+ temp[n++] = (0xC0 + (v >> 6));
+ temp[n++] = (0x80 + (v & 0x3F));
+ } else
+ {
+ temp[n++] = (0xE0 + (v >> 12));
+ temp[n++] = (0x80 + ((v >> 6) & 0x3F));
+ temp[n++] = (0x80 + (v & 0x3F));
+ }
+ }
+ }
+ temp[n] = 0;
+ setenv ("ud", temp, 1);
+ for (n = 0, p = 0; p < udl; p++)
+ {
+ unsigned short v = ud[p];
+ if (v < ' ' || v == '\\')
+ {
+ temp[n++] = '\\';
+ if (v == '\\')
+ temp[n++] = '\\';
+ else if (v == '\n')
+ temp[n++] = 'n';
+ else if (v == '\r')
+ temp[n++] = 'r';
+ else if (v == '\t')
+ temp[n++] = 't';
+ else if (v == '\f')
+ temp[n++] = 'f';
+ else
+ {
+ temp[n++] = '0' + (v >> 6);
+ temp[n++] = '0' + ((v >> 3) & 7);
+ temp[n++] = '0' + (v & 7);
+ }
+ } else if (v < 0x80)
+ temp[n++] = v;
+ else if (v < 0x800)
+ {
+ temp[n++] = (0xC0 + (v >> 6));
+ temp[n++] = (0x80 + (v & 0x3F));
+ } else
+ {
+ temp[n++] = (0xE0 + (v >> 12));
+ temp[n++] = (0x80 + ((v >> 6) & 0x3F));
+ temp[n++] = (0x80 + (v & 0x3F));
+ }
+ }
+ temp[n] = 0;
+ setenv ("ude", temp, 1);
+ for (p = 0; p < udl && ud[p] < 0x100; p++);
+ if (p == udl)
+ {
+ for (n = 0, p = 0; p < udl; p++)
+ {
+ sprintf (temp + n, "%02X", ud[p]);
+ n += 2;
+ }
+ setenv ("ud8", temp, 1);
+ }
+ for (n = 0, p = 0; p < udl; p++)
+ {
+ sprintf (temp + n, "%04X", ud[p]);
+ n += 4;
+ }
+ setenv ("ud16", temp, 1);
+ }
+ /* run the command */
+ system (process);
+ }
+ closedir (d);
+}
+
+/* Main app */
+int
+main (int argc, const char *argv[])
+{
+ char c;
+ int mt = 0,
+ mo = 0,
+ tx = 0,
+ rx = 0,
+ nodial = 0,
+ nowait = 0,
+ concurrent = 1,
+ motxwait = 10,
+ motxdelay = 1,
+ motxretries = 10,
+ mttxwait = 10,
+ mttxdelay = 30,
+ mttxretries = 100,
+ mr = -1,
+ pid = -1,
+ dcs = -1,
+ srr = 0,
+ rp = 0,
+ vp = 0,
+ udl = 0,
+ utf8 = 0,
+ ucs1 = 0,
+ ucs2 = 0;
+ unsigned short ud[160];
+ unsigned char *uds = 0,
+ *udh = 0;
+ char *da = 0,
+ *oa = 0,
+ *queue = "",
+ *udfile = 0,
+ *process = 0,
+ *spooldir = "/var/spool/asterisk",
+ *motxchannel = "Local/1709400X",
+ *motxcallerid = 0,
+ *mttxchannel = 0,
+ *mttxcallerid = "080058752X0",
+ *defaultsubaddress = "9",
+ subaddress = 0,
+ *scts = 0;
+ poptContext optCon; /* context for parsing command-line options */
+ const struct poptOption optionsTable[] = {
+ {"queue", 'q', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &queue, 0, "Queue [inc sub address]", "number[-X]"},
+ {"da", 'd', POPT_ARG_STRING, &da, 0, "Destination address", "number"},
+ {"oa", 'o', POPT_ARG_STRING, &oa, 0, "Origination address", "number"},
+ {"ud", 'm', POPT_ARG_STRING, &uds, 0, "Message", "text"},
+ {"ud-file", 'f', POPT_ARG_STRING, &udfile, 0, "Message file", "filename"},
+ {"UTF-8", 0, POPT_ARG_NONE, &utf8, 0, "File treated as null terminated UTF-8 (default)", 0},
+ {"UCS-1", 0, POPT_ARG_NONE, &ucs1, 0, "File treated as UCS-1", 0},
+ {"UCS-2", 0, POPT_ARG_NONE, &ucs2, 0, "File treated as UCS-2", 0},
+ {"mt", 't', POPT_ARG_NONE, &mt, 0, "Mobile Terminated", 0},
+ {"mo", 0, POPT_ARG_NONE, &mo, 0, "Mobile Originated", 0},
+ {"tx", 0, POPT_ARG_NONE, &tx, 0, "Send message", 0},
+ {"rx", 'r', POPT_ARG_NONE, &rx, 0, "Queue for receipt", 0},
+ {"process", 'e', POPT_ARG_STRING, &process, 0, "Rx queue process command", "command"},
+ {"no-dial", 'x', POPT_ARG_NONE, &nodial, 0, "Do not dial", 0},
+ {"no-wait", 0, POPT_ARG_NONE, &nowait, 0, "Do not wait if already calling", 0},
+ {"concurrent", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &concurrent, 0, "Number of concurrent calls to allow", "n"},
+ {"motx-channel", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &motxchannel, 0, "Channel for motx calls", "channel"},
+ {"motx-callerid", 0, POPT_ARG_STRING, &motxcallerid, 0,
+ "Caller ID for motx calls (default is queue name without sub address)", "number"},
+ {"motx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxwait, 0, "Time to wait for motx call to answer",
+ "seconds"},
+ {"motx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxdelay, 0, "Time between motx call retries", "seconds"},
+ {"motx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxretries, 0, "Number of retries for motx call", "n"},
+ {"mttx-channel", 0, POPT_ARG_STRING, &mttxchannel, 0,
+ "Channel for mttx calls (default is Local/ and queue name without sub address)", "channel"},
+ {"mttx-callerid", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &mttxcallerid, 0,
+ "Caller ID for mttx calls (default is queue name without sub address)", "number"},
+ {"mttx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxwait, 0, "Time to wait for mttx call to answer",
+ "seconds"},
+ {"mttx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxdelay, 0, "Time between mttx call retries", "seconds"},
+ {"mttx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxretries, 0, "Number of retries for mttx call", "n"},
+ {"mr", 'n', POPT_ARG_INT, &mr, 0, "Message reference", "n"},
+ {"pid", 'p', POPT_ARG_INT, &pid, 0, "Protocol ID", "n"},
+ {"dcs", 'c', POPT_ARG_INT, &dcs, 0, "Data Coding Scheme", "n"},
+ {"udh", 0, POPT_ARG_STRING, &udh, 0, "User data header", "hex"},
+ {"srr", 0, POPT_ARG_NONE, &srr, 0, "Status Report Request", 0},
+ {"rp", 0, POPT_ARG_NONE, &rp, 0, "Return Path request", 0},
+ {"v", 0, POPT_ARG_INT, &vp, 0, "Validity Period", "seconds"},
+ {"scts", 0, POPT_ARG_STRING, &scts, 0, "Timestamp", "YYYY-MM-SSTHH:MM:SS"},
+ {"default-sub-address", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &defaultsubaddress, 0, "Default sub address", "X"},
+ {"spool-dir", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &spooldir, 0, "Asterisk spool dir", "dirname"},
+ POPT_AUTOHELP {NULL, 0, 0, NULL, 0}
+ };
+
+ optCon = poptGetContext (NULL, argc, argv, optionsTable, 0);
+ poptSetOtherOptionHelp (optCon, "<oa/da> <message>");
+
+ /* Now do options processing, get portname */
+ if ((c = poptGetNextOpt (optCon)) < -1)
+ {
+ /* an error occurred during option processing */
+ fprintf (stderr, "%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c));
+ return 1;
+ }
+ if (!ucs1 && !ucs2)
+ utf8 = 1;
+ if (utf8 + ucs1 + ucs2 > 1)
+ {
+ fprintf (stderr, "Pick one of UTF-8, UCS-1 or UCS-2 only\n");
+ return 1;
+ }
+ if (!udfile && (ucs1 || ucs2))
+ {
+ fprintf (stderr, "Command line arguments always treated as UTF-8\n");
+ return 1;
+ }
+ /* if (!where && poptPeekArg (optCon)) where = (char *) poptGetArg (optCon); */
+ if (!mt && !mo && process)
+ mt = 1;
+ if (!mt && !mo && oa)
+ mt = 1;
+ if (!mt)
+ mo = 1;
+ if (mt && mo)
+ {
+ fprintf (stderr, "Cannot be --mt and --mo\n");
+ return 1;
+ }
+ if (!rx && !tx && process)
+ rx = 1;
+ if (!rx)
+ tx = 1;
+ if (tx && rx)
+ {
+ fprintf (stderr, "Cannot be --tx and --rx\n");
+ return 1;
+ }
+ if (rx)
+ nodial = 1;
+ if (uds && udfile)
+ {
+ fprintf (stderr, "Cannot have --ud and --ud-file\n");
+ return 1;
+ }
+ if (mo && !da && poptPeekArg (optCon))
+ da = (char *) poptGetArg (optCon);
+ if (mt && !oa && poptPeekArg (optCon))
+ oa = (char *) poptGetArg (optCon);
+ if (tx && oa && mo)
+ {
+ fprintf (stderr, "--oa makes no sense with --mo as CLI is used (i.e. queue name)\n");
+ return 1;
+ }
+ if (tx && da && mt)
+ {
+ fprintf (stderr, "--da makes no sense with --mt as called number is used (i.e. queue name)\n");
+ return 1;
+ }
+ if (da && strlen (da) > 20)
+ {
+ fprintf (stderr, "--da too long\n");
+ return 1;
+ }
+ if (oa && strlen (oa) > 20)
+ {
+ fprintf (stderr, "--oa too long\n");
+ return 1;
+ }
+ if (queue && strlen (queue) > 20)
+ {
+ fprintf (stderr, "--queue name too long\n");
+ return 1;
+ }
+ if (mo && scts)
+ {
+ fprintf (stderr, "scts is set my service centre\n");
+ return 1;
+ }
+ if (uds)
+ { /* simple user data command line option in \UTF-8 */
+ while (udl < 160 && *uds)
+ {
+ int v = utf8decode (&uds);
+ if (v > 0xFFFF)
+ {
+ fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
+ return 1;
+ }
+ ud[udl++] = v;
+ }
+ }
+ if (!uds && !udfile && poptPeekArg (optCon))
+ { /* multiple command line arguments in UTF-8 */
+ while (poptPeekArg (optCon) && udl < 160)
+ {
+ unsigned char *a = (unsigned char *) poptGetArg (optCon);
+ if (udl && udl < 160)
+ ud[udl++] = ' '; /* space between arguments */
+ while (udl < 160 && *a)
+ {
+ int v = utf8decode (&a);
+ if (v > 0xFFFF)
+ {
+ fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
+ return 1;
+ }
+ ud[udl++] = v;
+ }
+ }
+ }
+ if (poptPeekArg (optCon))
+ {
+ fprintf (stderr, "Unknown argument %s\n", poptGetArg (optCon));
+ return 1;
+ }
+ if (udfile)
+ { /* get message from file */
+ unsigned char dat[1204],
+ *p = dat,
+ *e;
+ int f,
+ n;
+ if (*udfile)
+ f = open (udfile, O_RDONLY);
+ else
+ f = fileno (stdin);
+ if (f < 0)
+ {
+ perror (udfile);
+ return 1;
+ }
+ n = read (f, dat, sizeof (dat));
+ if (n < 0)
+ {
+ perror (udfile);
+ return 1;
+ }
+ if (*udfile)
+ close (f);
+ e = dat + n;
+ if (utf8)
+ { /* UTF-8 */
+ while (p < e && udl < 160 && *p)
+ ud[udl++] = utf8decode (&p);
+ } else if (ucs1)
+ { /* UCS-1 */
+ while (p < e && udl < 160)
+ ud[udl++] = *p++;
+ } else
+ { /* UCS-2 */
+ while (p + 1 < e && udl < 160)
+ {
+ ud[udl++] = (*p << 8) + p[1];
+ p += 2;
+ }
+ }
+ }
+ if (queue)
+ {
+ char *d = strrchr (queue, '-');
+ if (d && d[1])
+ subaddress = d[1];
+ else
+ subaddress = *defaultsubaddress;
+ }
+
+ if (chdir (spooldir))
+ {
+ perror (spooldir);
+ return 1;
+ }
+
+ if (oa || da)
+ { /* send message */
+ char temp[100],
+ queuename[100],
+ *dir = (mo ? rx ? "sms/morx" : "sms/motx" : rx ? "sms/mtrx" : "sms/mttx");
+ FILE *f;
+ snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ());
+ mkdir ("sms", 0777); /* ensure directory exists */
+ mkdir (dir, 0777); /* ensure directory exists */
+ snprintf (queuename, sizeof(queuename), "%s/%s.%ld-%d", dir, *queue ? queue : "0", (long)time (0), (int)getpid ());
+ f = fopen (temp, "w");
+ if (!f)
+ {
+ perror (temp);
+ return 1;
+ }
+ if (oa)
+ fprintf (f, "oa=%s\n", oa);
+ if (da)
+ fprintf (f, "da=%s\n", da);
+ if (scts)
+ fprintf (f, "scts=%s\n", scts);
+ if (pid >= 0)
+ fprintf (f, "pid=%d\n", pid);
+ if (dcs >= 0)
+ fprintf (f, "dcs=%d\n", dcs);
+ if (mr >= 0)
+ fprintf (f, "mr=%d\n", mr);
+ if (srr)
+ fprintf (f, "srr=1\n");
+ if (rp)
+ fprintf (f, "rp=1\n");
+ if (udh)
+ fprintf (f, "udh#%s\n", udh);
+ if (vp > 0)
+ fprintf (f, "vp=%d\n", vp);
+ if (udl)
+ {
+ int p;
+ for (p = 0; p < udl && ud[p] < 0x100; p++);
+ if (p == udl)
+ {
+ for (p = 0; p < udl && ud[p] < 0x80 && ud[p] >= 0x20; p++);
+ if (p == udl)
+ { /* use text */
+ fprintf (f, "ud=");
+ for (p = 0; p < udl; p++)
+ fputc (ud[p], f);
+ } else
+ { /* use one byte hex */
+ fprintf (f, "ud#");
+ for (p = 0; p < udl; p++)
+ fprintf (f, "%02X", ud[p]);
+ }
+ } else
+ { /* use two byte hex */
+ fprintf (f, "ud##");
+ for (p = 0; p < udl; p++)
+ fprintf (f, "%04X", ud[p]);
+ }
+ fprintf (f, "\n");
+ }
+ fclose (f);
+ if (rename (temp, queuename))
+ {
+ perror (queuename);
+ unlink (temp);
+ return 1;
+ }
+ }
+
+ if (!nodial && tx && !process)
+ { /* dial to send messages */
+ char ret=0,
+ try = 3;
+ if (nowait)
+ try = 1;
+ while (try--)
+ {
+ if (mo)
+ ret = txqcheck ("motx", queue, subaddress, motxchannel, motxcallerid, motxwait, motxdelay, motxretries, concurrent);
+ else
+ ret = txqcheck ("mttx", queue, subaddress, mttxchannel, mttxcallerid, mttxwait, mttxdelay, mttxretries, concurrent);
+ if (ret < 2)
+ break; /* sent, or queued OK */
+ if (try)
+ sleep (1);
+ }
+ if (ret == 2 && !nowait)
+ fprintf (stderr, "No call scheduled as already sending\n");
+ }
+ if (process)
+ rxqcheck (mo ? rx ? "morx" : "motx" : rx ? "mtrx" : "mttx", queue, process);
+
+ return 0;
+}
diff --git a/trunk/utils/stereorize.c b/trunk/utils/stereorize.c
new file mode 100644
index 000000000..7d72cbdbf
--- /dev/null
+++ b/trunk/utils/stereorize.c
@@ -0,0 +1,159 @@
+/****************************************************************************
+ *
+ * Programs for processing sound files in raw- or WAV-format.
+ * -- Merge two mono WAV-files to one stereo WAV-file.
+ *
+ * Name: stereorize.c
+ * Version: 1.1
+ * Author: Mark Roberts <mark@manumark.de>
+ * Michael Labuschke <michael@labuschke.de>
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include "frame.h"
+
+static char *Version = "stereorize 1.1, November 5th 2000";
+static char *Usage =
+"Usage: stereorize [options] infile-left infile-right outfile\n\n"
+
+"Example:\n"
+" stereorize left.wav right.wav stereo.wav -h\n\n"
+
+"Creates stereo.wav (with WAV-header, option -h) from data in mono files\n"
+"left.wav and right.wav.\n"
+;
+
+int main( int argcount, char *args[])
+{
+ int i, k[2], maxk, stdin_in_use=FALSE;
+ short *leftsample, *rightsample, *stereosample;
+ FILE *channel[2];
+ char *filename[2], *tempname;
+
+ version = Version;
+ usage = Usage;
+
+ channel[0] = NULL;
+ channel[1] = NULL;
+
+ parseargs( argcount, args, NOFILES | NOCOMPLAIN);
+
+ for (i = 0; i < 2; i++)
+ {
+ filename[i] = parsefilearg( argcount, args);
+ if (filename[i] == NULL)
+ argerrornum( NULL, ME_NOTENOUGHFILES);
+ if (strcmp (filename[i], "-") == 0)
+ {
+ if (stdin_in_use)
+ argerrortxt( filename[i] + 1,
+ "Cannot use <stdin> for both input files");
+ filename[i] = "<stdin>";
+ channel[i] = stdin;
+ stdin_in_use = TRUE;
+ }
+ else
+ {
+ channel[i] = fopen(filename[i], "rb");
+ }
+ if (channel[i] == NULL)
+ fatalerror( "Error opening input file '%s': %s\n", filename[i],strerror(errno));
+ else
+ inform("Using file '%s' as input\n", filename[i]);
+ }
+ for (i = 0; i < 2; i++)
+ {
+ assert ( channel[i] != NULL);
+ readwavheader( channel[i]);
+ if (iswav && channels != 1)
+ inform("Warning: '%s' is no mono file\n", filename[i]);
+ }
+
+ outfilename = parsefilearg( argcount, args);
+ if (outfilename == NULL) argerrornum( NULL, ME_NOOUTFILE);
+ if (strcmp (outfilename, "-") == 0)
+ {
+ outfilename = "<stdout>";
+ out = stdout;
+ }
+ else
+ {
+ out = fopen(outfilename, "wb");
+ }
+ if (out == NULL)
+ fatalerror( "Error opening output file '%s': %s\n", outfilename,strerror(errno));
+ else
+ inform("Using file '%s' as output\n", outfilename);
+
+ if ((tempname = parsefilearg( argcount, args)) != NULL)
+ argerrornum( tempname, ME_TOOMANYFILES);
+
+ checknoargs(argcount, args); /* Check that no arguments are left */
+
+ leftsample = malloc( sizeof(*leftsample) * BUFFSIZE);
+ rightsample = malloc( sizeof(*leftsample) * BUFFSIZE);
+ stereosample = malloc( sizeof(*leftsample) * 2 * BUFFSIZE);
+ if (leftsample == NULL || rightsample == NULL || stereosample == NULL)
+ fatalperror ("");
+
+ channels = 2; /* Output files are stereo */
+ if (wavout)
+ {
+ if ((strcmp(outfilename,"<stdout>")!=0) && (fseek( out, 0, SEEK_SET) != 0))
+ fatalerror("Couldn't navigate output file '%s': %s\n",outfilename, strerror(errno));
+ makewavheader();
+ }
+
+ startstopwatch();
+ while (TRUE)
+ {
+ maxk = 0;
+ for (i = 0; i < 2; i++)
+ {
+ k[i] = fread(i==0? leftsample : rightsample,
+ sizeof(*leftsample),
+ BUFFSIZE,
+ channel[i]);
+ if (k[i] == -1)
+ fatalerror("Error reading file '%s': %s\n", filename[i],strerror(errno));
+ if (k[i] > maxk)
+ maxk = k[i];
+ }
+ if (maxk == 0)
+ myexit (0);
+
+ /*-------------------------------------------------*
+ * First the left channel as far as it goes ... *
+ *-------------------------------------------------*/
+ for (i = 0; i < k[0]; i++)
+ stereosample[2 * i] = leftsample[i];
+ /*-------------------------------------------------*
+ * ... and fill up till the end of this buffer. *
+ *-------------------------------------------------*/
+ for (; i < maxk; i++)
+ stereosample[2 * i] = 0;
+
+ /*-------------------------------------------------*
+ * Next the right channel as far as it goes ... *
+ *-------------------------------------------------*/
+ for (i = 0; i < k[1]; i++)
+ stereosample[2 * i + 1] = rightsample[i];
+ /*-------------------------------------------------*
+ * ... and fill up till the end of this buffer. *
+ *-------------------------------------------------*/
+ for (; i < maxk; i++)
+ stereosample[2 * i + 1] = 0;
+
+ fwrite(stereosample, sizeof(*leftsample), 2 * maxk, out);
+ if (ferror( out) != 0)
+ fatalerror("Error writing to file '%s': %s\n",
+ outfilename, strerror(errno));
+ }
+ /* That was an endless loop. This point is never reached. */
+}
diff --git a/trunk/utils/streamplayer.c b/trunk/utils/streamplayer.c
new file mode 100644
index 000000000..fb0d055a2
--- /dev/null
+++ b/trunk/utils/streamplayer.c
@@ -0,0 +1,121 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Russell Bryant <russell@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
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \brief A utility for reading from a raw TCP stream
+ *
+ * This application is intended for use when a raw TCP stream is desired to be
+ * used as a music on hold source for Asterisk. Some devices are capable of
+ * taking some kind of audio input and provide it as a raw TCP stream over the
+ * network, which is what inspired someone to fund this to be written.
+ * However, it would certainly be possible to write your own server application
+ * to provide music over a TCP stream from a centralized location.
+ *
+ * This application is quite simple. It just reads the data from the TCP
+ * stream and dumps it straight to stdout. Due to the way Asterisk handles
+ * music on hold sources, this application checks to make sure writing
+ * to stdout will not be a blocking operation before doing so. If so, the data
+ * is just thrown away. This ensures that the stream will continue to be
+ * serviced, even if Asterisk is not currently using the source.
+ *
+ * \todo Update this application to be able to connect to a stream via HTTP,
+ * since that is the #1 most requested feature, and it would be quite useful.
+ * A lot of people think that is what this is for and email me when it does
+ * not work. :)
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__) || defined(__CYGWIN__)
+#include <netinet/in.h>
+#endif
+#include <sys/time.h>
+
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in sin;
+ struct hostent *hp;
+ int s;
+ int res;
+ char buf[2048];
+ fd_set wfds;
+ struct timeval tv;
+
+ if (argc != 3) {
+ fprintf(stderr, "streamplayer -- A utility for reading from a raw TCP stream.\n");
+ fprintf(stderr, "Written for use with Asterisk (http://www.asterisk.org)\n");
+ fprintf(stderr, "Copyright (C) 2005 -- Russell Bryant -- Digium, Inc.\n\n");
+ fprintf(stderr, "Usage: ./streamplayer <ip> <port>\n");
+ exit(1);
+ }
+
+ hp = gethostbyname(argv[1]);
+ if (!hp) {
+ fprintf(stderr, "Unable to lookup IP for host '%s'\n", argv[1]);
+ exit(1);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(atoi(argv[2]));
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (s < 0) {
+ fprintf(stderr, "Unable to allocate socket!\n");
+ exit(1);
+ }
+
+ res = connect(s, (struct sockaddr *)&sin, sizeof(sin));
+
+ if (res) {
+ fprintf(stderr, "Unable to connect to host!\n");
+ close(s);
+ exit(1);
+ }
+
+ while (1) {
+ res = read(s, buf, sizeof(buf));
+
+ if (res < 1)
+ break;
+
+ memset(&tv, 0, sizeof(tv));
+ FD_ZERO(&wfds);
+ FD_SET(1, &wfds);
+
+ select(2, NULL, &wfds, NULL, &tv);
+
+ if (FD_ISSET(1, &wfds))
+ write(1, buf, res);
+ }
+
+ close(s);
+ exit(res);
+}